diff --git a/CHANGELOG.md b/CHANGELOG.md index 44102e11f6..ca3325f523 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -92,31 +92,31 @@ ## [3.8.51] — TBD -_Living section — reconciled 2026-09-07 from all cycle commits (`release/v3.8.50` tip `091589089c` → `release/v3.8.51` tip `d6f315018a`, 696 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._ +_Living section — reconciled 2026-09-15 from all cycle commits (`091589089c` → `c0f92ec98a`, 916 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 | **120** | -| 📝 Commits in the cycle | **696** | -| 🔀 Pull requests referenced | **672** | -| 📋 Changelog entries | **685** | -| 🙌 Contributors credited in entries | **118** | +| 👥 People who contributed | **123** | +| 📝 Commits in the cycle | **916** | +| 🔀 Pull requests referenced | **892** | +| 📋 Changelog entries | **919** | +| 🙌 Contributors credited in entries | **121** | | 🤖 Automated dependency commits | 18 | **Entries by type** | Type | Count | | --- | ---: | -| 🐛 Fixes | 403 | -| ✨ Features | 118 | -| 🧹 Chore | 72 | -| 📚 Docs | 36 | -| 🧪 Tests | 29 | +| 🐛 Fixes | 592 | +| ✨ Features | 131 | +| 🧹 Chore | 93 | +| 📚 Docs | 42 | +| 🧪 Tests | 32 | +| ♻️ Refactor | 8 | +| 🏗️ Build | 7 | | ⚡ Performance | 7 | -| ♻️ Refactor | 7 | -| 🏗️ Build | 6 | | ⚙️ CI | 2 | | ⏪ Reverts | 2 | | 📦 Dependencies | 2 | @@ -124,35 +124,35 @@ _Living section — reconciled 2026-09-07 from all cycle commits (`release/v3.8. ### 🏆 Top 25 contributors this cycle -_By commits in `release/v3.8.50..release/v3.8.51` (`091589089c..d6f315018a`), author identities consolidated via `.mailmap` and the merged PR's GitHub login. Bots excluded._ +_By commits in `091589089c..c0f92ec98a`, author identities consolidated via `.mailmap` and the merged PR's GitHub login. Bots excluded._ | # | Contributor | Commits | | ---: | --- | ---: | -| 🥇 | diegosouzapw | 240 | -| 🥈 | Paco Cartones (@pacocartones) | 58 | -| 🥉 | Bob.Hou (@HouMinXi) | 57 | -| 4 | Dizzle (@maxmad64bis) | 27 | -| 5 | Webman (@jonlwheat2-gif) | 22 | -| 6 | Ravi Tharuma (@RaviTharuma) | 21 | -| 7 | backryun | 17 | -| 8 | Markus Hartung (@hartmark) | 17 | -| 9 | Nguyen Thanh Dat (@ntdatt812) | 16 | -| 10 | Rafa Martins (@rafacpti23) | 10 | -| 11 | Syed Raheemuddin (@raheemuddin786) | 10 | -| 12 | Koosha Paridehpour (@KooshaPari) | 9 | -| 13 | Nguyễn Viết Tuấn (@TheDemonTuan) | 8 | -| 14 | MumuTW | 7 | -| 15 | Paijo (@oyi77) | 7 | -| 16 | Tobias Andersen (@turbolego) | 6 | -| 17 | Bl0ck (@Bl0ck154) | 5 | -| 18 | SHANMUGAPRIYAN (@geek007git) | 5 | -| 19 | KaspaPulse | 5 | -| 20 | Abhishek Sharma (@abhisheksharma2411) | 4 | -| 21 | Abhishek Divekar (@adivekar-utexas) | 4 | -| 22 | Andrew B. (@AndrianBalanescu) | 4 | -| 23 | NoxzRCW | 4 | -| 24 | opensource-elearning | 4 | -| 25 | Alvin T. Veroy (@alvinveroy) | 3 | +| 🥇 | diegosouzapw | 323 | +| 🥈 | Bob.Hou (@HouMinXi) | 79 | +| 🥉 | Paco Cartones (@pacocartones) | 77 | +| 4 | Dizzle (@maxmad64bis) | 49 | +| 5 | Koosha Paridehpour (@KooshaPari) | 43 | +| 6 | Nguyen Thanh Dat (@ntdatt812) | 31 | +| 7 | Ravi Tharuma (@RaviTharuma) | 26 | +| 8 | Markus Hartung (@hartmark) | 22 | +| 9 | Webman (@jonlwheat2-gif) | 22 | +| 10 | backryun | 17 | +| 11 | anhtahaylove | 15 | +| 12 | Rafa Martins (@rafacpti23) | 10 | +| 13 | Syed Raheemuddin (@raheemuddin786) | 10 | +| 14 | Nguyễn Viết Tuấn (@TheDemonTuan) | 8 | +| 15 | MumuTW | 7 | +| 16 | Paijo (@oyi77) | 7 | +| 17 | Tobias Andersen (@turbolego) | 6 | +| 18 | Bl0ck (@Bl0ck154) | 5 | +| 19 | SHANMUGAPRIYAN (@geek007git) | 5 | +| 20 | KaspaPulse | 5 | +| 21 | Abhishek Sharma (@abhisheksharma2411) | 4 | +| 22 | Abhishek Divekar (@adivekar-utexas) | 4 | +| 23 | Andrew B. (@AndrianBalanescu) | 4 | +| 24 | NoxzRCW | 4 | +| 25 | opensource-elearning | 4 | ### ✨ New Features @@ -278,6 +278,19 @@ _By commits in `release/v3.8.50..release/v3.8.51` (`091589089c..d6f315018a`), au - **feat(dashboard):** link a log entry's Conversation Context to its owning conversation ([#12646](https://github.com/diegosouzapw/OmniRoute/pull/12646)) — thanks @hartmark - **feat(catalog):** eligibility-gated free-tier bucket ([#12669](https://github.com/diegosouzapw/OmniRoute/pull/12669)) - **feat(db):** fail-closed previous_response_id continuation for redacted video turns (#12150 P2b) ([#12707](https://github.com/diegosouzapw/OmniRoute/pull/12707)) +- **feat(gamification): pay the documented `streak_bonus` and `badge_unlock` XP rewards.** `XP_REWARDS` listed both rewards but the award pipeline never paid them: the private reward table in `events.ts` omitted them, `updateStreak()` did not report when a streak extended, and badge unlocks carried no XP. Every request that extends a daily streak now pays `streak_bonus × streak length` once per UTC day (guarded by a same-day `xp_audit_log` check), and every badge unlocked through the pipeline pays `badge_unlock` once per badge (guarded by the `user_badges` primary key; `unlockBadge()` now reports whether it inserted). Bonus XP flows through the same `addXp` + level sync + global/weekly/monthly leaderboard path as action XP, so level-ups and rankings include it. The Radar supporter recognition unlock stays XP-free. (#12522 — thanks @pacocartones) +- **feat(providers):** advertise a `free-tier` capability in the provider plugin manifest for every provider with documented free models, so sidecars and dashboards can filter free-capable providers without reading the quota catalog ([#12786](https://github.com/diegosouzapw/OmniRoute/pull/12786)) — thanks @maxmad64bis +- **feat(providers):** Added EURouter as an OpenAI-compatible API-key gateway (`https://api.eurouter.ai/v1`), with live model discovery via `passthroughModels`. Its copy states that models are served by third-party upstreams listed per model, so an EU-based router is not read as EU data residency for inference. ([#13025](https://github.com/diegosouzapw/OmniRoute/pull/13025)) — thanks @ntdatt812 +- **feat(providers):** Added GreenPT as an OpenAI-compatible API-key provider (`https://api.greenpt.ai/v1`), with live model discovery via `passthroughModels`. No free-inference badge: the published docs describe a free API subscription billed per token, not a free tier. ([#13024](https://github.com/diegosouzapw/OmniRoute/pull/13024)) — thanks @ntdatt812 +- **feat(i18n):** 9 new locales — Greek (`el`), Croatian (`hr`), Serbian (`sr`), Lithuanian (`lt`), Estonian (`et`), Latvian (`lv`), Slovenian (`sl`), Maltese (`mt`), Irish (`ga`) — across the dashboard, docs mirrors, CLI, README and the site; OmniRoute now ships every official EU language (51 locales). (#13044) +- **feat(i18n):** 8 new locales — Kannada (`kn`), Malayalam (`ml`), Odia (`or`), Punjabi (`pa`), Nepali (`ne`), Sinhala (`si`), Burmese (`my`), Khmer (`km`) — across the dashboard, docs mirrors, CLI, README and the site (59 locales). The translator now restores the ICU literal escape around angle placeholders and splits oversized docs sections before translating. (#0000) ([#13660](https://github.com/diegosouzapw/OmniRoute/pull/13660)) +- feat(providers): **list Agnes 3.0 Flash as the current free chat model, drop retired 1.5 Flash, add Image 2.0/2.5 Flash plus Video 2.5/2.5 Flash, and discover the live `/v1/models` catalog (including the CN host `api.agnes-ai.cn`).** `agnes-1.5-flash` now forwards to `agnes-3.0-flash`. Video 2.5 polls `GET /v1/videos/{id}` (not the V2.0 `/agnesapi` contract). Live `/v1/models` (2026-09-09) no longer serves 1.5; the wiki marks it deprecated. 3.0 Flash is 512K context / 65,536 max output, same window as 2.5. CN-region keys use the existing per-connection base-URL field, default stays `apihub.agnes-ai.com`. ([#13120](https://github.com/diegosouzapw/OmniRoute/pull/13120)) — thanks @HouMinXi +- **feat(sse):** Codex and OpenAI catalogs list GPT-6 Astra with effort aliases (`-low` through `-ultra`); Codex CLI identity pins `@openai/codex@0.153.4` in lockstep with the image ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **feat(dashboard):** the orchestration History tab gained a "Compare runs" mode — toggling it turns each grid cell into a 2-item selection queue (a 3rd click drops the oldest pick), and picking two cells opens a side-by-side comparison panel instead of the usual detail drawer. The panel fetches both runs' detail the same way the drawer does (falling back to persisted history once a run leaves the live TTL window) and shows, per side: identity/source/state, start time, a signed `right - left` delta for duration/cost/event count, the event timeline aligned by index, and any memory hits. One side's fetch failing never blocks the other, and a delta is only ever computed when both sides have a finite value — otherwise it renders "—", never `NaN`. Comparing two runs from different sources or skills still works; a banner marks the deltas as informational rather than hiding them, since the two runs aren't a strict apples-to-apples pair. ([#12677](https://github.com/diegosouzapw/OmniRoute/pull/12677)) +- **feat(dashboard):** badge a conversation that never reached a clean stop ([#12717](https://github.com/diegosouzapw/OmniRoute/pull/12717)) — thanks @hartmark +- **feat(release):** reconcile-changelog tool + version-anchored fragment aggregation ([#12987](https://github.com/diegosouzapw/OmniRoute/pull/12987)) +- **feat(mcp):** carry context_length through MCP list_models_catalog projection (#12776) ([#13406](https://github.com/diegosouzapw/OmniRoute/pull/13406)) — thanks @KooshaPari +- **feat(cli):** make startup readiness budget configurable (#13369) ([#13433](https://github.com/diegosouzapw/OmniRoute/pull/13433)) — thanks @KooshaPari ### 🐛 Bug Fixes @@ -694,6 +707,194 @@ _By commits in `release/v3.8.50..release/v3.8.51` (`091589089c..d6f315018a`), au - **fix(i18n):** quote placeholder in OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES description (#12505) ([#12769](https://github.com/diegosouzapw/OmniRoute/pull/12769)) — thanks @KooshaPari - **fix(api/pricing):** surface validation error message as string, not raw object (#12494) ([#12771](https://github.com/diegosouzapw/OmniRoute/pull/12771)) — thanks @KooshaPari - **fix(sse):** re-enable prompt compression for native Codex passthrough ([#12834](https://github.com/diegosouzapw/OmniRoute/pull/12834)) — thanks @thomasmaerz +- **fix(dashboard):** model health tests for a provider node set to the Responses API now call `/v1/responses` with a Responses-shaped body instead of `/v1/chat/completions` — those models were reported as `Provider returned HTTP 200 but no text content` even though the same model answered normally through `/v1/responses` ([#13070](https://github.com/diegosouzapw/OmniRoute/issues/13070)) ([#13087](https://github.com/diegosouzapw/OmniRoute/pull/13087)) — thanks @ntdatt812 +- fix(routing): stop a round-robin combo's "opencode" targets from collapsing onto the opencode-zen connection (#11912) ([#13283](https://github.com/diegosouzapw/OmniRoute/pull/13283)) +- fix(routing): stop the reactive-compaction debug log from lying when compression is globally disabled (#11977) ([#13276](https://github.com/diegosouzapw/OmniRoute/pull/13276)) +- fix(electron): relativize standalone-bundle symlink targets so Stage 8 manifest verification stops failing on Windows (#11979) ([#13251](https://github.com/diegosouzapw/OmniRoute/pull/13251)) +- fix(dashboard): surface a visible error when Compression Studio's combined preview run fails (#12061) ([#13288](https://github.com/diegosouzapw/OmniRoute/pull/13288)) +- fix(dashboard): make the compression "Effective pipeline" preview honor the active profile and warn when the master switch is off (#12063) ([#13287](https://github.com/diegosouzapw/OmniRoute/pull/13287)) +- fix(providers): scope TinyCMS Web signer's DOM shims to each call instead of leaking them for the process lifetime, and surface a clean HTTP status on a non-JSON interception-toggles error (#12072) ([#13254](https://github.com/diegosouzapw/OmniRoute/pull/13254)) +- fix(guardrails): stop Vision Bridge from re-selecting a model locked after a 404 (#12111) ([#13259](https://github.com/diegosouzapw/OmniRoute/pull/13259)) +- fix(sse): require Responses-shaped body before native OpenAI-compatible passthrough (#12129) ([#13278](https://github.com/diegosouzapw/OmniRoute/pull/13278)) +- fix(providers): minimax-m3 now collapses manual thinking.type:"enabled" to adaptive, preventing upstream 400 (2013) (#12132) ([#13249](https://github.com/diegosouzapw/OmniRoute/pull/13249)) +- fix(db): scope model visibility overrides by modality so hiding a Chat model no longer hides an identically-ID'd Image/Embeddings/etc. model (#12172) ([#13258](https://github.com/diegosouzapw/OmniRoute/pull/13258)) +- fix(db): scope local-provider apiKey dedup to matching base URL so LM Studio/Ollama-style connections support multiple accounts (#12173) ([#13250](https://github.com/diegosouzapw/OmniRoute/pull/13250)) +- fix(providers): refresh Trae's stale Referer/Origin and forward user timezone so imported connections stop failing with 401 (#12190) ([#13255](https://github.com/diegosouzapw/OmniRoute/pull/13255)) +- fix(providers): route opencode-go/gpt-5.6-luna to /responses instead of /chat/completions (#12196) ([#13275](https://github.com/diegosouzapw/OmniRoute/pull/13275)) +- fix(dashboard): allow deleting the last extra-upstream-header row even when invalid (#12251) ([#13252](https://github.com/diegosouzapw/OmniRoute/pull/13252)) +- **fix(i18n):** translate pre-existing `__MISSING__:` keys for `combo.sort`, `requestLogger.detail` expand/collapse, `common.profile`, and `settings.resilienceCredentialHealth*` across 39 locales ([#12272](https://github.com/diegosouzapw/OmniRoute/issues/12272)) ([#13034](https://github.com/diegosouzapw/OmniRoute/pull/13034)) — thanks @HouMinXi +- fix(cli): run the Node.js runtime compatibility guard before the heavy `tsx/esm` + Commander import chain so an unsupported runtime gets a clear message instead of a raw `Invalid regular expression flags` crash (#12296) ([#13253](https://github.com/diegosouzapw/OmniRoute/pull/13253)) +- fix(dashboard): refresh the providers list after deleting a compatible provider node (#12298) ([#13256](https://github.com/diegosouzapw/OmniRoute/pull/13256)) +- fix(usage): fail closed on API-key budget enforcement when a provider's `auto` routing alias has no pricing row, instead of silently counting it as $0 (#12341) ([#13257](https://github.com/diegosouzapw/OmniRoute/pull/13257)) +- **fix(providers):** include the submitted Agnes video model when polling by `video_id` ([#12356](https://github.com/diegosouzapw/OmniRoute/pull/12356)) — thanks @pacocartones +- **fix(routing):** custom OpenAI-compatible nodes now honor the saved Chat/Responses API type after edits instead of letting the node's original ID prefix override the live connection setting ([#11884](https://github.com/diegosouzapw/OmniRoute/issues/11884)). ([#12358](https://github.com/diegosouzapw/OmniRoute/pull/12358)) — thanks @pacocartones +- fix(sse): surface an error instead of a silent empty 200 when a Claude stream closes with zero bytes (#12398) ([#13285](https://github.com/diegosouzapw/OmniRoute/pull/13285)) +- fix(oauth): warn before the dead localhost:8080 redirect in antigravity/gemini `oauth start` (#12413) ([#13265](https://github.com/diegosouzapw/OmniRoute/pull/13265)) +- fix(providers): stop devin-cli spawn error from double-closing the SSE controller (#12517) ([#13261](https://github.com/diegosouzapw/OmniRoute/pull/13261)) +- **fix(rerank):** clamp Voyage `top_k` to the documents actually sent after empty-string filtering, and honor `return_documents: false` in the NVIDIA response adapter (#12523 — thanks @pacocartones) +- **fix(audio):** `/v1/audio/translations` now resolves combo names the way `/v1/audio/transcriptions` already does, so a combo that `GET /v1/models` advertises is fanned out to its targets instead of being rejected with `400 Invalid translation model: . Use format: provider/model`; literal `provider/model` ids and unknown bare names behave as before (#12536 — thanks @pacocartones) +- **fix(gemini):** strip the JSON-Schema-2020-12 `prefixItems` keyword from Gemini tool schemas at every nesting level, so Claude Code tool definitions no longer fail with `400 Unknown name "prefixItems"` on Gemini models (#12540 — thanks @pacocartones) +- **fix(api-manager):** Expose an accessible loading status while API keys are fetched instead of an empty accessibility tree (#12541 — thanks @pacocartones) +- **fix(video):** Clamp `estimateJpegFrameBytes` at zero for padding-only payloads and build the three encode-side frame data URIs from `JPEG_FRAME_DATA_URI_PREFIX` instead of a repeated literal (#12543 — thanks @pacocartones) +- **fix(devin):** accept Windows `DEVIN_AGENTIC_HOME` sandbox paths (`C:\...\.sandbox\...`) in the isolated-home check so the Devin Claude Bridge no longer fails closed on Windows ([#12405](https://github.com/diegosouzapw/OmniRoute/issues/12405)) (#12545 — thanks @pacocartones) +- **fix(db):** Add `conversation_turn_nodes` and orphaned `agentic_conversations` to the auto-cleanup cycle under the existing `retention.callLogs` window, so identity nodes whose call-log content has already been purged no longer accumulate without bound in `storage.sqlite` (#12548 — thanks @pacocartones) +- **fix(i18n):** Wrap the `~/.claude/profiles//settings.json` placeholder in ICU single quotes in the `OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES` feature-flag description across all 42 locales and the TypeScript default, so next-intl no longer fails with `INVALID_MESSAGE: UNCLOSED_TAG` and the Feature Flags card shows the description instead of the raw key (#12549 — thanks @pacocartones) +- **fix(orchestration):** `updateCloudAgentTask` now publishes the task's real `status` on `agent.task.updated` when an update only touches `result`, `activities` or `error`, instead of the fabricated `"updated"` state, and stays silent when no row matched the id (#12550 — thanks @pacocartones) +- **fix(i18n):** the home "Recent Requests" panel and the Provider Topology legend are now translated instead of rendering English copies on non-English dashboards; the legend reads its own `home.topologyLegend*` labels with consistent casing rather than borrowing the memory-settings "Recent" and analytics "Error" strings (#12551 — thanks @pacocartones). +- **docs(reference):** bring the `FEATURE_FLAGS.md` catalog back to 1:1 with `featureFlagDefinitions.ts` — 20 missing flags added, the two `*_BLOCK_THRESHOLD` env-only knobs moved out of the flag tables, category/total counts and the Live WS port corrected, guarded by a static test (#12552 — thanks @pacocartones) +- **fix(i18n):** backfill missing `usage.kiloPass*` strings in 39 locales and restore `featureFlagOmnirouteDisableThinkingLevelVariantsDescription` in `pt.json` ([#12561](https://github.com/diegosouzapw/OmniRoute/issues/12561)) ([#13027](https://github.com/diegosouzapw/OmniRoute/pull/13027)) — thanks @HouMinXi +- fix(docker): default docker-compose app ports (dashboard/API/live-WS) to loopback instead of `0.0.0.0`, closing the anonymous `/v1` LAN/WAN exposure gap left open by `REQUIRE_API_KEY=false` (#12568) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): close DNS-rebinding SSRF gap in webhook outbound-URL guard (#12569) ([#13243](https://github.com/diegosouzapw/OmniRoute/pull/13243)) +- fix(docker): require a per-session token on the VNC browser CDP bridge and isolate it on a dedicated Docker network (#12571) ([#13241](https://github.com/diegosouzapw/OmniRoute/pull/13241)) +- fix(open-sse): write Adobe Firefly session tokens and cookie jars with 0700/0600 permissions instead of the process umask (#12572) ([#13242](https://github.com/diegosouzapw/OmniRoute/pull/13242)) +- fix(api): remove hardcoded wildcard CORS in openai-to-gemini-sse.ts so the centralized fail-closed CORS gate is the sole source of `Access-Control-Allow-Origin` (#12573) ([#13239](https://github.com/diegosouzapw/OmniRoute/pull/13239)) +- fix(api): enforce API key policy (budget/rate-limit/schedule/endpoint scoping) on the ElevenLabs speech-to-text, text-to-speech and voices proxy routes (#12574) ([#13244](https://github.com/diegosouzapw/OmniRoute/pull/13244)) +- fix(sse): cap HuggingChat NDJSON body size and bound the read loop with the fetch timeout so a stalled or hostile upstream cannot buffer unbounded memory (#12577) ([#13282](https://github.com/diegosouzapw/OmniRoute/pull/13282)) +- fix(docker): scope the cliproxyapi/qdrant/bifrost sidecars to loopback by default and forward `CLIPROXYAPI_MANAGEMENT_KEY` into the cliproxyapi container so its management API is not left both unauthenticated and LAN-published (#12578) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): create DB export temp paths with `fs.mkdtempSync` instead of predictable timestamps (#12579) ([#13237](https://github.com/diegosouzapw/OmniRoute/pull/13237)) +- Cline 401 bodies that say "re-authenticate your Cline account" classify as a refreshable OAuth token, not a terminal expired key. The cooling panel no longer labels every cooldown as a 429; it shows the recorded last error instead. (#12594) ([#13060](https://github.com/diegosouzapw/OmniRoute/pull/13060)) — thanks @HouMinXi +- **fix(catalog):** degrade unknown combo targets instead of dropping LCD modalities, and persist OpenRouter `architecture.input_modalities` into the capability snapshot ([#12613](https://github.com/diegosouzapw/OmniRoute/issues/12613)) ([#13042](https://github.com/diegosouzapw/OmniRoute/pull/13042)) — thanks @HouMinXi +- fix(providers): send `x-api-key` instead of `Authorization: Bearer` for OpenCode Zen's `/v1/responses` endpoint (Muse Spark Contributor models), fixing a 401 on OmniRoute's auth header (#12633) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(sse): surface the actionable "Auggie CLI not found" message when the shell reports a missing `auggie` binary via exit code instead of a spawn error (#12645) ([#13263](https://github.com/diegosouzapw/OmniRoute/pull/13263)) +- **fix(config):** Persist deletions of built-in background-degradation entries — when a stored settings record exists its `degradationMap` is now authoritative instead of being merged under the defaults, so an entry the user removed in the dashboard no longer reappears on the next apply or restart ([#12424](https://github.com/diegosouzapw/OmniRoute/issues/12424)) ([#12647](https://github.com/diegosouzapw/OmniRoute/pull/12647)) — thanks @pacocartones +- **fix(gamification):** action-count badge milestones (First Token, Token Consumer, Token Machine, Token Whale, and the token-sharing tier) are now backed by a durable `xp_action_counts` counter incremented in `addXp()`, instead of a live `COUNT(*)` over `xp_audit_log`. The audit log is pruned by `retention.xpAuditLog` (default 30 days), so on a default install those "lifetime" milestones were really "actions in the last 30 days" and unlocked badges could stop unlocking once old rows aged out. `getActionCount()` and `checkActionCountBadges()` now read the same durable source, and a migration backfills existing totals from the surviving audit rows ([#12546](https://github.com/diegosouzapw/OmniRoute/issues/12546)) ([#12651](https://github.com/diegosouzapw/OmniRoute/pull/12651)) — thanks @pacocartones +- **fix(images):** `/v1/images/edits` now iterates a combo's targets the same way `/v1/images/generations` does (#9239) instead of flattening a bare combo to its first target. A combo whose first target is not edit-capable — or lacks credentials — now falls through to a later edit-capable target rather than hard-erroring, and missing credentials are skipped (not a hard `401`) to match the generations path. The per-target skip/terminal classification is extracted into a shared `runImageComboTargets` loop, so generations behavior is unchanged ([#12547](https://github.com/diegosouzapw/OmniRoute/issues/12547)). ([#12653](https://github.com/diegosouzapw/OmniRoute/pull/12653)) — thanks @pacocartones +- fix(sse): add first-byte watchdog to the TLS-fingerprint transport so a stalled wreq body falls back instead of hanging for minutes (#12656) ([#13272](https://github.com/diegosouzapw/OmniRoute/pull/13272)) +- fix(sse): exempt tiny-budget reasoning probes from combo quality failure and surface persisted-cooldown skips in ALL_TARGETS_SKIPPED diagnostics (#12659) ([#13269](https://github.com/diegosouzapw/OmniRoute/pull/13269)) +- fix(models): declare the real ~1M contextLength for OpenCode Zen's Muse Spark 1.2 models instead of falling back to the 200000 provider default (#12681) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(oauth): align codebuddy-cn OAuth User-Agent with the chat/usage CLI version to avoid WAF false positives (#12702) ([#13264](https://github.com/diegosouzapw/OmniRoute/pull/13264)) +- fix(dashboard): surface an authentication-required banner instead of silently blanking database settings for a guest session (#12709) ([#13270](https://github.com/diegosouzapw/OmniRoute/pull/13270)) +- **fix(resilience):** stop unbounded queue that hangs 6min until Aborted — gate, provider slot, and Bottleneck queue now share a per-connection `maxWaitMs` budget; `fail-closed` on exhaust (503); execution backstop `executionMaxWaitMs` overridable per connection with upstream clamp ([#12715](https://github.com/diegosouzapw/OmniRoute/pull/12715)) — thanks @maxmad64bis (with thanks to @Tushar49 for surfacing the slow-provider need in #12635) +- **fix(auto-combo):** every mode pack now carries `quality` and `reliability` weights (`quality` 0.02, 0.03 in `quality-first`; `reliability` 0.03, 0.04 in `reliability-first`), so selecting a pack no longer silences either signal; `DEFAULT_WEIGHTS` is unchanged ([#12731](https://github.com/diegosouzapw/OmniRoute/pull/12731)) — thanks @maxmad64bis +- **fix(sse):** the provider execution pipeline keeps the upstream error `code`/`type` again — both were lost when the non-streaming leg was extracted, so a config-class `422` (Antigravity missing project) degraded into a generic account cooldown instead of reaching the gate that recognises it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(db):** a legacy `call_logs` table no longer aborts startup — the provider-stats index is created after the column healing runs, not before it, so an install predating `request_type` opens instead of failing with `no such column` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(catalog):** a malformed row in the operator-writable custom-models blob no longer kills every `auto/*` pool ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(tests):** realign the guards that the same day's merges left asserting the old behavior — the provider-count total after GreenPT and EURouter, the GLM stream buffer slot that `#12925` turned into a declared parameter, and the injection scan bound that `#13104` reshaped from a head-only window into head-plus-tail ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- fix(cache): fold tool_choice/tools/response_format into the semantic cache signature so a cached tool_calls response can no longer be replayed for a request whose tool policy forbids it (#12734) ([#13267](https://github.com/diegosouzapw/OmniRoute/pull/13267)) +- **fix(models):** stop treating provider-supplied `isFree:true`/`:free`/`0/0` flags as trusted unless the provider has a documented free tier — only the shipped free-tier catalog decides otherwise; fetched `isFree:true` trusted only for a free-tier provider, custom `isFree:true` via a trusted path ([#12744](https://github.com/diegosouzapw/OmniRoute/pull/12744)) — thanks @maxmad64bis +- fix(memory): authenticate the internal /v1/rerank loopback call so memory reranking no longer silently degrades to unranked order when REQUIRE_API_KEY=true (#12745) ([#13268](https://github.com/diegosouzapw/OmniRoute/pull/13268)) +- fix(sse): parse Ollama Cloud's current usage markup (`$X of $Y used` aria-label, nested width style) (#12749) ([#13260](https://github.com/diegosouzapw/OmniRoute/pull/13260)) +- fix(cli): setup-opencode no longer sends an active context's management token to `/v1/models` when `--api-key`/`OMNIROUTE_API_KEY` is supplied — an explicit flag or the env var now always outranks the context's token, and the flag itself is no longer swallowed by the parent program's global `--api-key` option (#12783) ([#13246](https://github.com/diegosouzapw/OmniRoute/pull/13246)) +- fix(sse): register Arcee AI in the executor provider registry so requests reach api.arcee.ai instead of silently falling back to OpenAI (#12784) ([#13277](https://github.com/diegosouzapw/OmniRoute/pull/13277)) +- **fix(quota):** share one upstream quota read when identical saturation checks arrive at the same time, so traffic bursts don't multiply provider API calls ([#12787](https://github.com/diegosouzapw/OmniRoute/pull/12787)) — thanks @maxmad64bis +- **fix(db):** ignore expired or invalid rate-limit cooldown writes so a stale timestamp can't lock a connection that should be usable — clearing still works as before ([#12788](https://github.com/diegosouzapw/OmniRoute/pull/12788)) — thanks @maxmad64bis +- **fix(providers):** single source for provider order with `xao` ranking alongside `xai-oauth` ([#12790](https://github.com/diegosouzapw/OmniRoute/pull/12790)) — thanks @maxmad64bis +- **fix(routing):** off-table models check the free-model catalog before inheriting premium prices, latency bootstraps from the observed pool median, async tiers read live database pricing with a 90-day freshness gate, and the tier cache invalidates on every pricing write ([#12792](https://github.com/diegosouzapw/OmniRoute/pull/12792)) — thanks @maxmad64bis +- **fix(auto-combo):** snapshot scoring uses the observed per-connection account tier and per-model quality instead of neutral constants, and circuit-open providers rank lower at build time ([#12794](https://github.com/diegosouzapw/OmniRoute/pull/12794)) — thanks @maxmad64bis +- **fix(pool):** empty `auto/*` pools now say why they are empty, and the models listing uses the same paid check as routing ([#12795](https://github.com/diegosouzapw/OmniRoute/pull/12795)) — thanks @maxmad64bis +- fix(routing): recognize CLIProxyAPI's 'unknown provider for model' 400 as fallback-worthy (#12800) ([#13284](https://github.com/diegosouzapw/OmniRoute/pull/13284)) +- **fix(sse):** translate-mode streams now emit the estimated token counts as a trailing usage-only chunk before `[DONE]` when the upstream stays silent, so metered chat clients see totals instead of nothing ([#12828](https://github.com/diegosouzapw/OmniRoute/pull/12828)) — thanks @maxmad64bis +- **fix(db):** provider stats stay truthful on empty and legacy databases: fallback counts default to `0` instead of `null`, latency averages read `null` (not `0`) when no durations were recorded, failures logged before the error-type column existed group under `pre_migration` instead of `unclassified`, and per-provider queries use two new composite indexes ([#12832](https://github.com/diegosouzapw/OmniRoute/pull/12832)) — thanks @maxmad64bis +- fix(nvidia): fail open when a synced model catalog goes stale instead of gating forever (#12849) ([#13248](https://github.com/diegosouzapw/OmniRoute/pull/13248)) +- **fix(db):** WAL maintenance lives in its own module: a `TRUNCATE` checkpoint that hits a busy database now warns with its streak and retries once via `PASSIVE` instead of logging success, and closing-time checkpoints no longer report success on builds without a database file, with the busy totals visible in the authenticated monitoring health payload ([#12853](https://github.com/diegosouzapw/OmniRoute/pull/12853)) — thanks @maxmad64bis +- **fix(health):** quota with no snapshots reads empty instead of a contradicting 0% ([#12857](https://github.com/diegosouzapw/OmniRoute/pull/12857)) — thanks @maxmad64bis +- fix(a2a): accept the dashboard's own session cookie on /a2a so "Run message/send" no longer fails with "Unauthorized: missing or invalid API key" (#12888) ([#13271](https://github.com/diegosouzapw/OmniRoute/pull/13271)) +- **fix(logs):** the Logs grid's in-memory filter pass no longer discards rows the SQL query already matched — selecting an API key from the dropdown (which sends the key's id) returns its calls again, the Combo tab shows every combo instead of only those whose name contains a "1", and the model filter and search cover the same columns as the query ([#12896](https://github.com/diegosouzapw/OmniRoute/pull/12896)) — fixes [#12873](https://github.com/diegosouzapw/OmniRoute/issues/12873) — thanks @ntdatt812 +- **fix(a2a):** `/api/a2a/status` now builds the agent card from the request that asked for it, so a gateway reached at a non-localhost host no longer advertises `http://localhost:20128` as its A2A URL ([#12918](https://github.com/diegosouzapw/OmniRoute/pull/12918)). — thanks @ntdatt812 +- **fix(compression):** progressive aging now appends its `[COMPRESSED:aging:…]` annotation after a turn's `tool_result` blocks instead of in front of them, so Anthropic no longer rejects aged conversations with "`tool_use` ids were found without `tool_result` blocks immediately after" ([#12920](https://github.com/diegosouzapw/OmniRoute/pull/12920)). — thanks @ntdatt812 +- **fix(bedrock):** model import now resolves context limits for every vendor prefix instead of only `anthropic.*`, so `global.openai.gpt-5.6-*` no longer imports with a null `inputTokenLimit` and gets rejected pre-flight at the 200k default ([#12921](https://github.com/diegosouzapw/OmniRoute/pull/12921)). — thanks @ntdatt812 +- **fix(stream):** the 64 KB stream buffer GLM asks for is honoured instead of dropped, and the type error it caused no longer fails the API Route Typecheck gate on every open PR ([#12925](https://github.com/diegosouzapw/OmniRoute/pull/12925)) — thanks @ntdatt812 +- **fix(guardrails):** mask PII inside a `tool_result`'s nested content array, which the masker walked past while redacting its sibling block ([#12930](https://github.com/diegosouzapw/OmniRoute/pull/12930)) — thanks @ntdatt812 +- **fix(dashboard):** the Radar catalog table no longer leaves absent data unexplained — empty limits, unknown context windows, and unreported capabilities each explain themselves on hover, and a new check keeps it that way ([#12937](https://github.com/diegosouzapw/OmniRoute/pull/12937)) — thanks @maxmad64bis +- **fix(sse):** geo-blocked opencode requests rotate to the next account proxy instead of failing, so one refused egress no longer aborts the whole chain ([#12941](https://github.com/diegosouzapw/OmniRoute/pull/12941)) — thanks @maxmad64bis +- **fix(codex):** dashboard "clear cooldown" and the CAS recovery path now drop nested `codexScopeRateLimitedUntil` maps in the same write that nulls `rate_limited_until`, and fresh quota snapshots with headroom lift fallback-sourced scope cooldowns parked by quota preflight ([#12817](https://github.com/diegosouzapw/OmniRoute/issues/12817), [#12860](https://github.com/diegosouzapw/OmniRoute/issues/12860), [#12951](https://github.com/diegosouzapw/OmniRoute/pull/12951)) The dashboard reset-credit button (`consumeCodexResetCredit`) now exercises that same snapshot path after a successful redeem, so a filled quota bar is enough to unpark a leftover fallback Codex child without another manual clear. — thanks @HouMinXi +- **fix(db):** resolve `sql-wasm.wasm` across global npm install and hoisted layouts, ensuring OmniRoute can boot cleanly on Node 24 when native `better-sqlite3` is uncompiled. ([#13035](https://github.com/diegosouzapw/OmniRoute/pull/13035)) — thanks @HouMinXi +- **fix(api):** `DELETE /v1/batches/delete-completed` now sweeps only the calling API key's own completed batches (batches with no owner stay out of a key-scoped sweep on purpose), with an explicit instance-wide mode reserved for authenticated dashboard sessions, a 401 for a presented key that is unknown, revoked, deactivated, banned or expired (never falling through to the session branch), audit logging of every sweep, a sanitized 500 on failure, an owner-scoped file half (a key-scoped sweep never nulls a file another tenant owns) and an atomic sweep — chunked in 200-batch transactions in instance mode — so a mid-way error never leaves a batch pointing at a nulled file (GHSA-wvxc-jp3v-5mg5) ([#12969](https://github.com/diegosouzapw/OmniRoute/pull/12969)) +- **fix(combo):** quota-weighted routing stops drawing on an out-of-credit connection — a 402 now invalidates the stored quota snapshot instead of leaving its stale remaining percentage in place, and a snapshot older than 10 minutes no longer counts as confident headroom for the primary pool ([#12972](https://github.com/diegosouzapw/OmniRoute/pull/12972)) ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- **fix(vision):** Custom Models with "Vision capable" checked no longer have image requests swapped to `glm/glm-4.6v` when the client sends the advertised alias (`vllm/path/...`) or the bare path-shaped id — Vision Bridge now matches the stored override for all three id forms ([#12758](https://github.com/diegosouzapw/OmniRoute/issues/12758)) ([#12974](https://github.com/diegosouzapw/OmniRoute/pull/12974)) — thanks @HouMinXi +- **fix(sse):** transient opencode upstream failures rotate to the next account proxy instead of failing, so one flapping egress no longer aborts the whole chain ([#12975](https://github.com/diegosouzapw/OmniRoute/pull/12975)) — thanks @maxmad64bis +- **fix(azure):** Deployments from GPT-6 onward now send `max_completion_tokens` instead of `max_tokens`, which Azure rejects with HTTP 400. The rule matched a literal `gpt-5`, so each new generation arrived broken; it now matches the generation range, while `gpt-35-turbo` still keeps `max_tokens`. ([#13007](https://github.com/diegosouzapw/OmniRoute/pull/13007)) — thanks @ntdatt812 +- **fix(db):** add an opt-in self-restart circuit for sustained critical memory pressure, gate post-cleanup VACUUM behind a minimum freed-rows threshold, and checkpoint the SQLite WAL every 5 minutes with a size guard that escalates to TRUNCATE, so a growing WAL can no longer stall the event loop into a full outage. ([#13011](https://github.com/diegosouzapw/OmniRoute/pull/13011)) — thanks @HouMinXi +- **fix(auth):** an explicit connection pin may probe a quota-disabled row once and re-enable it on success ([#12874](https://github.com/diegosouzapw/OmniRoute/issues/12874)) ([#13017](https://github.com/diegosouzapw/OmniRoute/pull/13017)) — thanks @HouMinXi +- **fix(api):** thread `X-OmniRoute-Fallback-Attempts` through combo chat completions so streaming and non-streaming responses report how many prior legs were attempted ([#13038](https://github.com/diegosouzapw/OmniRoute/pull/13038)) — thanks @HouMinXi +- Restore provider failure classification and credential refresh on non-streaming requests: classify non-2xx failures to lock models on per-model quota exhaustion, update connection rate limits from headers and body, and pass credential refresh handlers to pipeline execution so 401 tokens can be refreshed and retried (#13043). ([#13069](https://github.com/diegosouzapw/OmniRoute/pull/13069)) — thanks @HouMinXi +- **fix(responses):** wrap forced-non-streaming web_search fallback JSON as Responses SSE so Codex still sees `response.completed` ([#13050](https://github.com/diegosouzapw/OmniRoute/pull/13050)) — thanks @HouMinXi +- **fix(antigravity):** Preserve upstream thought-token usage in normalized completion and reasoning token counts ([#13055](https://github.com/diegosouzapw/OmniRoute/pull/13055)) — thanks @pacocartones +- Fix a concurrency-slot leak in the DeepSeek PoW solver: a worker that failed to spawn (for example a missing worker script) never released its slot, so `MAX_CONCURRENT_WORKERS` failures disabled the solver until restart. ([#13097](https://github.com/diegosouzapw/OmniRoute/pull/13097)) — thanks @anhtahaylove +- **fix(acp):** bound the ACP session output buffers — `stdoutBuffer` and `stderrBuffer` now cap at 1 MiB keeping the most recent output behind a visible `[...output truncated...]` marker, and `stderrBuffer` is reset per prompt instead of accumulating for the lifetime of the session. ([#13100](https://github.com/diegosouzapw/OmniRoute/pull/13100)) — thanks @anhtahaylove +- **fix(acp):** release the `stdout`/`exit` listeners and the idle timer that a `sendPrompt` timeout used to leave attached to the `acpManager` singleton, and drop sessions that exited on their own from the session map instead of keeping them forever. ([#13096](https://github.com/diegosouzapw/OmniRoute/pull/13096)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection and PII scanners now read the text a `tool_result` block carries on `content` (string or nested block list), in messages and in system blocks, so tool output is judged by the same rules as user text ([#13101](https://github.com/diegosouzapw/OmniRoute/pull/13101)) — thanks @ntdatt812 +- **fix(gamification):** close the badge notification SSE stream when the request signal is already aborted before the stream starts — a client that disconnects while the route is still awaiting auth used to leave both the 2s unlock poll and the 15s heartbeat running for the lifetime of the process. ([#13106](https://github.com/diegosouzapw/OmniRoute/pull/13106)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection scan now spends its 16 KB budget on both ends of the request instead of the first 16 KB only, so `system`, `instructions`, `query`, `documents` and the newest turns are no longer hidden behind one long message ([#13104](https://github.com/diegosouzapw/OmniRoute/pull/13104)) — thanks @ntdatt812 +- **fix(dashboard):** expose the Volcano Ark console cookie on quota scraping and unwrap connect-error objects so the dashboard shows the upstream message ([#13107](https://github.com/diegosouzapw/OmniRoute/pull/13107)) — thanks @HouMinXi +- **fix(db):** release the `beforeExit`/`SIGINT`/`SIGTERM` handlers when a `node:sqlite` adapter closes, so a closed adapter and its database handle are no longer pinned to `process` for the lifetime of the run — the same treatment #7494 gave the sql.js adapter. ([#13109](https://github.com/diegosouzapw/OmniRoute/pull/13109)) — thanks @anhtahaylove +- **fix(translator):** `contentSchema` and `unevaluatedItems` are now treated as subschema positions by the tool-schema sanitizer, so a truncation placeholder in either is replaced with a permissive schema instead of being forwarded as a string ([#13110](https://github.com/diegosouzapw/OmniRoute/pull/13110)) — thanks @ntdatt812 +- **fix(cli-helper):** clear the `createLogStream` timeout on the abort path — `stop()` aborts the in-flight fetch and returned through the `signal.aborted` branch, which skipped `clearTimeout` and left an armed timer per stopped stream. The stream reader is now also cancelled when the read loop exits early. ([#13114](https://github.com/diegosouzapw/OmniRoute/pull/13114)) — thanks @anhtahaylove +- **fix(docs):** drop TLS-impersonation, MITM-decrypt, supply-chain attestation, and XOR-mask writeups from the public `/docs` catalog and Docker image. Files stay in git for engineers; operators who need them open the repo, not the website. ([#13136](https://github.com/diegosouzapw/OmniRoute/pull/13136)) — thanks @HouMinXi +- **fix(combo):** parse numeric-epoch `rate_limited_until` in the combo cooldown read path ([#13141](https://github.com/diegosouzapw/OmniRoute/pull/13141)) — thanks @maxmad64bis +- **fix(opencode):** both OpenCode plugins now reject a gateway address typed without `http://` at configuration time, instead of publishing every model with an api url no client can call, and the v2 plugin no longer publishes a model card whose api url is blank or relative ([#13142](https://github.com/diegosouzapw/OmniRoute/pull/13142)) — thanks @maxmad64bis +- **fix(providers):** lock opencode model on upstream 400 model-unavailable ([#13146](https://github.com/diegosouzapw/OmniRoute/pull/13146)) — thanks @maxmad64bis +- **fix(logging):** keep the provider exchange rather than the raw client bodies when a call log exceeds its size budget, and show that recovered payload in the request-detail panel instead of replacing it with the stored response body ([#13147](https://github.com/diegosouzapw/OmniRoute/pull/13147)) — thanks @maxmad64bis +- **fix(traffic-inspector):** the WebSocket route no longer leaks a traffic-buffer subscriber and a 30s ping timer when the client socket is already closed at handler time — listeners are attached before any resource is acquired, a destroyed socket bails out early, and the ping interval stops on a dead socket where `write()` never throws ([#13155](https://github.com/diegosouzapw/OmniRoute/pull/13155)) — thanks @anhtahaylove +- **fix(telegram):** bound the per-user API key cache in the Telegram chat proxy so a burst of distinct chat ids can no longer grow the process heap without limit ([#13165](https://github.com/diegosouzapw/OmniRoute/issues/13165)) ([#13166](https://github.com/diegosouzapw/OmniRoute/pull/13166)) — thanks @anhtahaylove +- **fix(stream):** cancel the upstream response body when the JSON-to-SSE sniff unwinds on a body timeout, so a stalled upstream no longer pins the connection ([#13169](https://github.com/diegosouzapw/OmniRoute/issues/13169)) ([#13171](https://github.com/diegosouzapw/OmniRoute/pull/13171)) — thanks @anhtahaylove +- **fix(telegram):** authenticate webhook deliveries with Telegram's `secret_token` so an unauthenticated caller can no longer mint API keys or spend upstream quota ([#13172](https://github.com/diegosouzapw/OmniRoute/issues/13172)) ([#13175](https://github.com/diegosouzapw/OmniRoute/pull/13175)) — thanks @anhtahaylove +- **fix(models):** give discoverable Gemini 3.8 Flash ids their own 65536 output spec so Antigravity no longer clamps them to 16384 ([#13195](https://github.com/diegosouzapw/OmniRoute/pull/13195)) — thanks @HouMinXi +- **fix(dashboard):** leftover catalog-removed provider rows (gemini-cli) can be listed and purged from the providers page ([#13067](https://github.com/diegosouzapw/OmniRoute/issues/13067)) ([#13197](https://github.com/diegosouzapw/OmniRoute/pull/13197)) — thanks @HouMinXi +- **fix(auth):** a dashboard session now requires the `authenticated: true` claim that login, OIDC and the session refresh already emit — a JWT merely signed with `JWT_SECRET` (for example the Cursor CLI passthrough token, which any API-key holder can obtain) no longer verifies as the `auth_token` cookie on any route, the WebSocket handshake or the live server; existing sessions keep working ([#13298](https://github.com/diegosouzapw/OmniRoute/issues/13298)) ([#13375](https://github.com/diegosouzapw/OmniRoute/pull/13375)) +- **fix(api):** `DELETE /v1/batches/delete-completed` now applies the caller's API-key policy (endpoint allowlist, schedule, usage cap, rate limit) like every other `/v1` route, commits the key-scoped sweep per 200-batch chunk instead of holding one write lock for the whole sweep, refuses to loop without progress, and rejects a scope that names both a key and `allTenants` ([#13684](https://github.com/diegosouzapw/OmniRoute/pull/13684)) +- Restore the API-route typecheck gate: the non-streaming leg result lost its discriminated-union narrowing after the server-owned tool loop reassignment, producing 13 new TS2339 diagnostics in `chatCore.ts`. ([#12963](https://github.com/diegosouzapw/OmniRoute/pull/12963)) +- Restore four non-streaming behaviours the server-owned tool loop refactor dropped: client aborts map to 499 with the fixed `Request aborted` message, an aborted request no longer logs a synthetic `clientResponse`, Claude prompt-cache telemetry is recorded again, and a body that cannot be canonicalized no longer throws when the tool loop is off. ([#12990](https://github.com/diegosouzapw/OmniRoute/pull/12990)) +- **fix(skills):** The CLI registry parser now reads positionals declared with `.addArgument()`, not only those written inline in `.command()`. `tunnel create [type]` was being published as `tunnel create`, so the agent-skills sync gate reported drift on every branch and regenerating would have deleted the argument. ([#13009](https://github.com/diegosouzapw/OmniRoute/pull/13009)) — thanks @ntdatt812 +- **fix(combos):** dashboard combo test uses a short prompt, serial probes, and a 60s timeout so reasoning models and rate-limited free pools do not fail the health check ([#13001](https://github.com/diegosouzapw/OmniRoute/pull/13001)) — thanks @HouMinXi +- fix(compression): terminate idle worker threads on eviction so long-running instances stop leaking OS threads and MessagePorts ([#13091](https://github.com/diegosouzapw/OmniRoute/pull/13091)) — thanks @anhtahaylove +- Redact Google API keys of any length in error bodies: the pattern required exactly 39 characters, so shorter or longer `AIza…` credentials passed through unredacted. ([#12964](https://github.com/diegosouzapw/OmniRoute/pull/12964)) +- **fix(grok-cli):** a 402 "Grok Build usage balance exhausted" parks that Grok login as out of credit (Grok Build CLI, grok.com cookie, and xAI OAuth share the weekly pool). Combo routing then tries the next login instead of locking the model for every account in the pool ([#13061](https://github.com/diegosouzapw/OmniRoute/pull/13061)) — thanks @HouMinXi +- fix(compression): spawn the LLMLingua worker with a file URL object so compression actually runs instead of silently failing open on Node ([#13093](https://github.com/diegosouzapw/OmniRoute/pull/13093)) — thanks @anhtahaylove +- **fix(test):** run the local `test` and `test:unit` scripts at concurrency 4 so a full-suite run no longer exhausts the machine's commit charge and kills unrelated processes ([#13187](https://github.com/diegosouzapw/OmniRoute/pull/13187)) — thanks @anhtahaylove +- **fix(monitoring):** `GET /api/monitoring/health` `credentialHealth` now includes a bounded `failedConnections` list (`connectionId`, `status`, sanitized `lastError`) when the probe-cache gauge `failed>0`, plus `source: probe-cache` and a cheap `staleDbNonOkCount` for sticky SQLite `test_status` on active rows. Documents that the live gauge is not `provider_connections.test_status`. ([#12876](https://github.com/diegosouzapw/OmniRoute/pull/12876)) — thanks @RaviTharuma +- **fix(resilience):** an openai-compatible multi-upstream gateway no longer marks the whole connection `credits_exhausted` when a single upstream (or the `/models` / representative-model health probe) returns 402. Quota failures stay model-scoped; true connection-wide auth failures are unchanged. Single-credential 402 key disable ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239)) is preserved. ([#12875](https://github.com/diegosouzapw/OmniRoute/pull/12875)) — thanks @RaviTharuma +- **fix(dashboard):** nine fixes on the Orchestration Canvas, all diagnosed in the Phase 2 reviews. A source that fails now keeps the timestamp of its FIRST failure instead of being re-stamped every poll — the stale line said "since the last poll" no matter how long the source had been down, and the churn also defeated the snapshot's stable identity (it serializes the source list), so the canvas re-rendered on every tick while anything was broken. A source that HAD data and only then started failing is flagged too: previously only a source with no node at all got the warning, so a source that went down mid-session kept a healthy-looking node forever. The rest are pointwise: clicking a filter chip cancels the pending search debounce (left armed, it fired ~300ms later and silently reverted the chip); the search input carries an accessible name; `?state=running, failed` parses like the unpadded form instead of dropping the padded value; the CSV toggle helper is defined once in `model/urlParams.ts` rather than twice; the agents tab tells "nothing running" apart from "the filter matched nothing", offering a clear-filters button instead of setup links that would be wrong advice there; edges stop emitting SMIL particles above 40 simultaneously active edges, keeping the colored stroke; and the drawer's error banner clears when a retried action succeeds. ([#12983](https://github.com/diegosouzapw/OmniRoute/pull/12983)) +- **fix(i18n):** the nine locales added with the EU-language batch (Greek, Estonian, Irish, Croatian, Lithuanian, Latvian, Maltese, Slovenian, Serbian) were missing the eleven Orchestration Canvas keys that Phase 3 introduced, so the compare-runs panel and the "no runs match these filters" empty state fell back to English in those languages (`deepMergeFallback` substitutes English for an absent key, so nothing rendered blank — it rendered untranslated). The coverage gate does not catch this: it enforces an 80% floor per locale, and eleven missing keys out of ~13,000 leaves coverage at 99.9%. Translated for real in each language, calibrated against the wording each file already uses for "run", "filter" and "skill". ([#13294](https://github.com/diegosouzapw/OmniRoute/pull/13294)) +- **fix(dashboard):** five follow-ups from the Phase 2 Orchestration Canvas review. The History drawer now shows the memory section for runs that already left the live TTL window: the persisted `memory_hits` event is parsed into `metadata.memoryHits` with the same defensive validation the drawer applies, and — because that event is observability rather than a state transition — it no longer leaks into the timeline, where it had been inheriting the task's state and rendering as a duplicate transition. The A2A memory recall runs against its own 1.5s deadline instead of inheriting the memory backend's 30s one; overshooting degrades exactly like any other recall failure (no hits, task proceeds), and the timer is cleared on both paths. Repeating a Conductor run carries its `requirements.cli`/`requirements.model` forward, so the new run is pinned to the same runner profile and model rather than drifting to whatever the fleet picks. A successful repeat from the Agents tab now focuses the run it created, instead of leaving the operator on the finished one. And the auth test for `POST /api/conductor/tasks` moved into the shared `ROUTES` array rather than restating the pattern. ([#12988](https://github.com/diegosouzapw/OmniRoute/pull/12988)) +- fix(plugins): stop leaking an exit listener per plugin hook timeout, which triggered MaxListenersExceededWarning on plugins that ignore SIGTERM ([#13092](https://github.com/diegosouzapw/OmniRoute/pull/13092)) — thanks @anhtahaylove +- **fix(validation):** Provider node edits no longer fail with a generic "Invalid request" when the optional daily-quota reset fields are left blank. The dashboard sends `dailyQuotaResetTimezone` and `dailyQuotaResetHour` as `null`, and only the hour accepted it. ([#13066](https://github.com/diegosouzapw/OmniRoute/issues/13066)) ([#13083](https://github.com/diegosouzapw/OmniRoute/pull/13083)) — thanks @ntdatt812 +- **fix(combo):** quota-aware expansion drops banned, inactive, missing, and wrong-provider connections before quota fetch or model dispatch; pins and allowlists stay selectors, not a bypass. Antigravity automatic exhaustion now requires a reported zero remaining, so a positive balance below 1% stays eligible. ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- Fix an intermittent failure in the quota-weighted routing test suite: scores are a function of `Date.now()`, so peers with identical quota scored microseconds apart never tied and swapped order. ([#12946](https://github.com/diegosouzapw/OmniRoute/pull/12946)) +- fix(volcengine): resolve quota window mapping gaps, safe multi-connection console binding, and response cookie sanitization ([#12950](https://github.com/diegosouzapw/OmniRoute/pull/12950)) — thanks @HouMinXi +- **fix(test):** resolve the WebDAV handler path with `fileURLToPath` so the suite's 37 WebDAV tests run on Windows instead of failing with a doubled `C:\C:\` drive prefix ([#13196](https://github.com/diegosouzapw/OmniRoute/pull/13196)) — thanks @anhtahaylove +- **fix(ui):** fall back from invalid AnySearch icon alias (direct commit `9587e07b69`) +- **fix(sse):** stop rebuilding a truncated summary from the collector's cap-dropped event array ([#12718](https://github.com/diegosouzapw/OmniRoute/pull/12718)) — thanks @hartmark +- **fix(conversations):** show a pending spinner for unresolved tool nodes instead of "(empty)" ([#12727](https://github.com/diegosouzapw/OmniRoute/pull/12727)) — thanks @hartmark +- **fix(streaming):** fail fast when an upstream stream produces only lifecycle/heartbeat events, never real content ([#12741](https://github.com/diegosouzapw/OmniRoute/pull/12741)) — thanks @hartmark +- **fix(responses-continuation):** bridge the write-in-flight window with an in-memory pending store ([#12854](https://github.com/diegosouzapw/OmniRoute/pull/12854)) — thanks @hartmark +- **fix(translator):** drop or map agent_message on Chat Completions fallback ([#12880](https://github.com/diegosouzapw/OmniRoute/pull/12880)) — thanks @RaviTharuma +- **fix(api):** page and stream-complete GET /v1/models for large catalogs ([#12882](https://github.com/diegosouzapw/OmniRoute/pull/12882)) — thanks @RaviTharuma +- **fix(quota):** align AUTH window labels with usage API and clarify 503 ([#12884](https://github.com/diegosouzapw/OmniRoute/pull/12884)) — thanks @RaviTharuma +- **fix(ci):** clear two base-reds on release/v3.8.51 — mutation-coverage gate + image-only-model guard ([#12945](https://github.com/diegosouzapw/OmniRoute/pull/12945)) +- **fix(api):** scope batch bulk-delete to the calling API key ([#13211](https://github.com/diegosouzapw/OmniRoute/pull/13211)) +- **fix(tests):** align model-sync log assertion to shipped path redaction (base-red) ([#13227](https://github.com/diegosouzapw/OmniRoute/pull/13227)) +- **fix(test):** make the ToS heading guard actually require the parentheses ([#13228](https://github.com/diegosouzapw/OmniRoute/pull/13228)) +- **fix(quality):** register 4 drifted covering tests in stryker tap.testFiles ([#13229](https://github.com/diegosouzapw/OmniRoute/pull/13229)) +- **fix(logging):** mask *-api-key headers in request-log pipeline (#13273) ([#13401](https://github.com/diegosouzapw/OmniRoute/pull/13401)) — thanks @KooshaPari +- **fix(tests):** correct stale Jina catalog prefix in custom-models test ([#13403](https://github.com/diegosouzapw/OmniRoute/pull/13403)) — thanks @KooshaPari +- **fix(backend):** prune old backups after health-check-repair snapshots (#13308) ([#13404](https://github.com/diegosouzapw/OmniRoute/pull/13404)) — thanks @KooshaPari +- **fix(backend):** guard memory_fts_au trigger with WHEN clause to stop FTS bloat (#13326) ([#13405](https://github.com/diegosouzapw/OmniRoute/pull/13405)) — thanks @KooshaPari +- **fix(free-tiers):** remove obsolete Together $25 signup credit (#12526) ([#13407](https://github.com/diegosouzapw/OmniRoute/pull/13407)) — thanks @KooshaPari +- **fix:** skip non-chat generation models in Test all models ([#13409](https://github.com/diegosouzapw/OmniRoute/pull/13409)) — thanks @KooshaPari +- **fix(providers):** de-duplicate agy and antigravity model catalogs ([#13410](https://github.com/diegosouzapw/OmniRoute/pull/13410)) — thanks @KooshaPari +- **fix(dashboard):** send provider in body for mini-playground web search ([#13411](https://github.com/diegosouzapw/OmniRoute/pull/13411)) — thanks @KooshaPari +- **fix(resilience):** add CJK quota-exhaustion patterns to 429 classifier ([#13412](https://github.com/diegosouzapw/OmniRoute/pull/13412)) — thanks @KooshaPari +- **fix(backend):** eval runner: failed calls no longer score as passed + dotAll regex ([#13413](https://github.com/diegosouzapw/OmniRoute/pull/13413)) — thanks @KooshaPari +- **fix(backend):** eval runner opts out of output-style and memory injection ([#13414](https://github.com/diegosouzapw/OmniRoute/pull/13414)) — thanks @KooshaPari +- **fix:** chain serial quarantine step in npm test script (fixes #13286) ([#13418](https://github.com/diegosouzapw/OmniRoute/pull/13418)) — thanks @KooshaPari +- **fix(antigravity):** derive stable session ID from credentials for prompt-cache reuse (#13305) ([#13424](https://github.com/diegosouzapw/OmniRoute/pull/13424)) — thanks @KooshaPari +- **fix(memory):** merge memory into top-level system field instead of unshifting at messages[0] (#13425) ([#13427](https://github.com/diegosouzapw/OmniRoute/pull/13427)) — thanks @KooshaPari +- **fix(compression):** preserve instruction blocks from lossy engines ([#13523](https://github.com/diegosouzapw/OmniRoute/pull/13523)) — thanks @KooshaPari +- **fix(compression):** preserve polarity words and newlines in ultra heuristic ([#13524](https://github.com/diegosouzapw/OmniRoute/pull/13524)) — thanks @KooshaPari +- **fix(compression):** run user preservePatterns before built-in patterns (#13457) ([#13525](https://github.com/diegosouzapw/OmniRoute/pull/13525)) — thanks @KooshaPari +- **fix(reasoning):** expand deepseek-v4 regex to match v4.1+ and add command-code provider (#13430) ([#13528](https://github.com/diegosouzapw/OmniRoute/pull/13528)) — thanks @KooshaPari +- **fix(providers):** add alwaysReasons metadata for reka-flash-3 ([#13531](https://github.com/diegosouzapw/OmniRoute/pull/13531)) — thanks @KooshaPari +- **fix(playground):** allow Compare columns to scroll independently ([#13532](https://github.com/diegosouzapw/OmniRoute/pull/13532)) — thanks @KooshaPari +- **fix(security):** empty whitelist allows through to prevent admin lockout ([#13534](https://github.com/diegosouzapw/OmniRoute/pull/13534)) — thanks @KooshaPari +- **fix(tests):** add DISABLE_IOREG_STRATEGY env var for macOS test compatibility (fixes #13467) ([#13539](https://github.com/diegosouzapw/OmniRoute/pull/13539)) — thanks @KooshaPari +- **fix(zed-hosted):** map developer role and Gemini enums for Zed proxy ([#13543](https://github.com/diegosouzapw/OmniRoute/pull/13543)) — thanks @KooshaPari +- **fix(analytics):** normalize provider aliases to canonical id in usage_history ([#13545](https://github.com/diegosouzapw/OmniRoute/pull/13545)) — thanks @KooshaPari +- **fix(combo):** use traceId for call log id to prevent UNIQUE constraint failure ([#13546](https://github.com/diegosouzapw/OmniRoute/pull/13546)) — thanks @KooshaPari +- **fix(auth):** check model lockout before returning synthetic noauth connection ([#13547](https://github.com/diegosouzapw/OmniRoute/pull/13547)) — thanks @KooshaPari +- **fix(guardrails):** skip credential redaction for base64 image data URLs ([#13550](https://github.com/diegosouzapw/OmniRoute/pull/13550)) — thanks @KooshaPari +- **fix(resilience):** increase requestQueue.maxWaitMs default from 15s to 30s ([#13553](https://github.com/diegosouzapw/OmniRoute/pull/13553)) — thanks @KooshaPari +- **fix(docs):** document OMNIROUTE_READY_TIMEOUT_MS and allowlist the test-only DISABLE_IOREG_STRATEGY ([#13692](https://github.com/diegosouzapw/OmniRoute/pull/13692)) ### 📝 Maintenance @@ -849,6 +1050,39 @@ _By commits in `release/v3.8.50..release/v3.8.51` (`091589089c..d6f315018a`), au - **docs(security):** document socket.yml scanner config + CI workflow link (#12575) ([#12764](https://github.com/diegosouzapw/OmniRoute/pull/12764)) — thanks @KooshaPari - **chore(ci):** guard commit identity in pre-commit to stop author misattribution ([#12772](https://github.com/diegosouzapw/OmniRoute/pull/12772)) - **deps:** 17 Dependabot bumps — bump electron from 43.4.0 to 43.4.1 in /electron ([#11426](https://github.com/diegosouzapw/OmniRoute/pull/11426)); bump the development group across 1 directory with 8 updates ([#11428](https://github.com/diegosouzapw/OmniRoute/pull/11428)); bump the production group across 1 directory with 11 updates ([#11440](https://github.com/diegosouzapw/OmniRoute/pull/11440)); chore(deps): bump github/codeql-action/init from 4.37.7 to 4.37.8 ([#11925](https://github.com/diegosouzapw/OmniRoute/pull/11925)); chore(deps): bump github/codeql-action from 4.37.7 to 4.37.8 ([#11926](https://github.com/diegosouzapw/OmniRoute/pull/11926)); chore(deps): bump github/codeql-action/analyze from 4.37.7 to 4.37.8 ([#11927](https://github.com/diegosouzapw/OmniRoute/pull/11927)); build(deps): bump github/codeql-action/init from 4.37.8 to 4.37.9 ([#12345](https://github.com/diegosouzapw/OmniRoute/pull/12345)); build(deps): bump github/codeql-action/analyze from 4.37.8 to 4.37.9 ([#12346](https://github.com/diegosouzapw/OmniRoute/pull/12346)); bump the development group across 1 directory with 2 updates ([#12347](https://github.com/diegosouzapw/OmniRoute/pull/12347)); build(deps): bump github/codeql-action from 4.37.8 to 4.37.9 ([#12349](https://github.com/diegosouzapw/OmniRoute/pull/12349)); bump browserslist from 4.28.2 to 4.28.8 ([#12396](https://github.com/diegosouzapw/OmniRoute/pull/12396)); bump the production group across 1 directory with 4 updates ([#12399](https://github.com/diegosouzapw/OmniRoute/pull/12399)); bump @xmldom/xmldom ([#12500](https://github.com/diegosouzapw/OmniRoute/pull/12500)); bump qs from 6.15.2 to 6.16.0 ([#12512](https://github.com/diegosouzapw/OmniRoute/pull/12512)); bump @xmldom/xmldom from 0.9.10 to 0.9.12 ([#12513](https://github.com/diegosouzapw/OmniRoute/pull/12513)); bump fast-uri from 3.1.5 to 3.1.7 ([#12514](https://github.com/diegosouzapw/OmniRoute/pull/12514)); bump @humanfs/node from 0.16.7 to 0.16.8 ([#12515](https://github.com/diegosouzapw/OmniRoute/pull/12515)) +- Clear the `release/v3.8.51` `check:agent-skills-sync` base-red: regenerate `skills/cli-tunnel/SKILL.md` so the `tunnel create [type]` positional that #13009 taught the generator to read is reflected in the committed skill. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` typecheck base-red from #12731: the new `Mode pack ranking gates` candidates in `open-sse/services/autoCombo/__tests__/autoCombo.test.ts` omitted the required `provider`, `model` and `errorRate` fields of `ProviderCandidate`, failing `check:open-sse-typecheck` (and with it `Fast Quality Gates`) on every open PR. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` docs-sync base-red: EURouter (#13025) and GreenPT (#13024) took the live provider count to 358, leaving 7 STRICT drifts (`PROVIDER_REFERENCE.md`, 4 diagrams, 2 tier-flow images) plus the `AGENTS.md` / `llm.txt` / `package.json` count claims stale. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- **test(models):** Pin `CATALOG_BUILD_TIMEOUT_MS` in the #12058 canonical-catalog regression so a cold catalog build racing the 8s default bound no longer flakes the suite with a 500 ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Allow `@omniroute/opencode-plugin-v2/` in the npm pack policy — [#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870) shipped the v2 plugin beside its v1 sibling without widening the allowlist, so every packed file under it read as an unexpected artifact and failed `check:pack-policy` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Point the release-green validator's pack gate at the tree it is validating — the build-provenance guard defaulted to `origin/main`, which a release branch tip can never be an ancestor of mid-cycle, so `Package artifact` reported a false red on every run and the tarball boot-smoke cascaded off it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **docs(checks):** keep doc counts honest — headings, rankings, catalog, weights, quality gate and scoring diagram now covered ([#12507](https://github.com/diegosouzapw/OmniRoute/pull/12507)) — thanks @maxmad64bis +- **chore(lifecycle):** `check:model-lifecycle` now also diffs `DEFAULT_DEGRADATION_MAP` (the background-task redirect table) against the vendor lifecycle snapshot, refusing a retired id as source or target, with a table-driven unit test beside it. Three rows whose source the vendor had retired — `claude-sonnet-4-20250514`, `gemini-3-pro-preview` and `gpt-5.1-codex` (whose target `gpt-5.1-codex-mini` is retired too) — were dead code, since `checkLifecycle` answers 410 before the redirect runs; they are dropped (#12535 — thanks @pacocartones) +- **test(compression):** cover idle worker eviction at the resource level — the pool must call `terminate()` and must not retain the worker's `MessagePort`, complementing the `exit`-event assertion added with the fix ([#13371](https://github.com/diegosouzapw/OmniRoute/pull/13371)) +- **chore(quality):** raise the `imageGeneration.ts` file-size ceiling for the image-only-model guard that clears the #12945 base-red ([#13392](https://github.com/diegosouzapw/OmniRoute/pull/13392)) +- **chore(quality):** type the combo-test route's JSON response bodies instead of casting them to `any`, drop the now-empty suppression entry, and remove the `hasPerModelQuota` import `chatCore.ts` stopped using when the failure-classification helper was extracted ([#13379](https://github.com/diegosouzapw/OmniRoute/pull/13379)) +- **chore(electron):** the desktop bundle no longer ships the root-level files of every translated docs mirror (`README.md`, `llm.txt`, `CLAUDE.md`, `GEMINI.md`, `CONTRIBUTING.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`) — the packaged app only reads `docs/i18n//docs/**`, which stays. Saves ~11 MB on top of the translated CHANGELOGs already pruned. (#0000) ([#13235](https://github.com/diegosouzapw/OmniRoute/pull/13235)) +- **chore(quality):** retighten the `src/sse/handlers/chat.ts` unused-vars suppression count to the 8 violations that actually remain ([#13385](https://github.com/diegosouzapw/OmniRoute/pull/13385)) +- Bring the documented counts back in line with the code: 171 migrations (was 169) and 20 routing strategies (was 19), plus the regenerated `cli-tunnel` skill reference. ([#12970](https://github.com/diegosouzapw/OmniRoute/pull/12970)) +- Update the documented migration count to 172 after the call-logs provider-stats indexes landed. ([#13209](https://github.com/diegosouzapw/OmniRoute/pull/13209)) +- **chore(ci):** two gates that close the blind spots behind the exclusions above. `check:vitest-exclusions` requires every Vitest exclusion to name a tracking issue and to appear in `config/quality/vitest-exclusions.json` — the previous list grew to 62 files behind a comment pointing at an issue that had been closed for a month. `check-new-key-coverage` requires a key newly added to `en.json` to reach every locale; the existing coverage gate is a percentage floor per locale, so eleven absent keys out of ~13,000 left it at 99.9% while a whole feature shipped untranslated in nine languages. Both are diff-aware, so pre-existing debt stays frozen and neither needed a migration to turn on. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **build:** root layout no longer loads Inter from `next/font/google`, so a production image build does not need fonts.googleapis.com ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **docs(i18n):** bring the eight new `llm.txt` mirrors (km, kn, ml, my, ne, or, pa, si) up to the current provider and migration counts, so `check-docs-sync` stops rejecting every commit ([#13674](https://github.com/diegosouzapw/OmniRoute/pull/13674)) +- **refactor(ui):** flow surfaces (home topology, combo live studio, compression cockpit/waterfall/nodes, token-health badge) express state through the theme-aware `--orch-status-*` tokens instead of fixed dark-mode hex, so green/red/amber/grey stay legible in the light theme; dark mode is byte-identical. Categorical palettes (routing-strategy hues, compression-layer pills, provider brand colors) deliberately stay hex ([#12378](https://github.com/diegosouzapw/OmniRoute/issues/12378)) ([#13203](https://github.com/diegosouzapw/OmniRoute/pull/13203)) +- **chore(tests):** 51 test files that had been excluded from Vitest are running again, restoring roughly 350 assertions to the blocking suite. Each was measured individually first: of the 62 files parked behind the `// #8618 — pre-existing failure` comment, 51 pass against the current tree with no source change, so the exclusions had outlived the failures they were added for. The 11 that genuinely still fail stay excluded, but now point at a live tracker (#13204) rather than at #8618, which was closed in August while the list it tracked kept growing. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **chore(quality):** drop two ESLint suppression entries whose violations no longer exist, so `eslint --suppressions-location` stops rejecting every commit that touches the surrounding files ([#13384](https://github.com/diegosouzapw/OmniRoute/pull/13384)) +- **chore(release):** bring main's post-tag fixes into release/v3.8.51 — sync-back step 2/2 (direct commit `21c488f210`) +- **chore(deps):** patch toml and esbuild in the opencode plugin lockfiles ([#12965](https://github.com/diegosouzapw/OmniRoute/pull/12965)) +- **docs(changelog):** reconcile the v3.8.51 living section — fold 366 fragments, cover every cycle commit, credit every contributor ([#12971](https://github.com/diegosouzapw/OmniRoute/pull/12971)) +- **test(plugin):** wait for the v2 lazy-refresh tier instead of sleeping past it ([#12984](https://github.com/diegosouzapw/OmniRoute/pull/12984)) +- **chore(quality):** rebaseline chatCore.ts after the non-streaming regression fixes ([#13045](https://github.com/diegosouzapw/OmniRoute/pull/13045)) +- **docs(changelog):** add the missing bullet marker to the DeepSeek PoW fragment ([#13200](https://github.com/diegosouzapw/OmniRoute/pull/13200)) +- **chore(deps):** drain the Dependabot queue — 10 of 13 alerts ([#13213](https://github.com/diegosouzapw/OmniRoute/pull/13213)) +- **chore(ci):** make worktrees carry the hooks that enforce the identity gate ([#13236](https://github.com/diegosouzapw/OmniRoute/pull/13236)) +- **chore(deps):** pin csv-parse to ^7.0.2 via overrides (#13117) ([#13300](https://github.com/diegosouzapw/OmniRoute/pull/13300)) — thanks @KooshaPari +- **chore(deps):** bump hono from ^4.12.34 to ^4.13.7 (#13148) ([#13301](https://github.com/diegosouzapw/OmniRoute/pull/13301)) — thanks @KooshaPari +- **chore(deps):** pin joi to ^18.2.8 via overrides (#13085) ([#13302](https://github.com/diegosouzapw/OmniRoute/pull/13302)) — thanks @KooshaPari +- **docs(providers):** add Microsoft 365 Copilot (BizChat) provider guide (#12779) ([#13340](https://github.com/diegosouzapw/OmniRoute/pull/13340)) — thanks @KooshaPari ### 🙌 Contributors @@ -866,6 +1100,7 @@ Thanks to everyone whose work landed in v3.8.51: | [@amaleta](https://github.com/amaleta) | #11906, #11934 | | [@amartinawi](https://github.com/amartinawi) | #11575 | | [@AndrianBalanescu](https://github.com/AndrianBalanescu) | #11083, #11830, #11834, #11844 | +| [@anhtahaylove](https://github.com/anhtahaylove) | #13091, #13092, #13093, #13096, #13097, #13100, #13106, #13109, #13114, #13155, #13166, #13171, #13175, #13187, #13196 | | [@aniruddhaadak80](https://github.com/aniruddhaadak80) | #11760 | | [@app](https://github.com/app) | #12554 | | [@arminanton](https://github.com/arminanton) | #11461, #11513 | @@ -895,10 +1130,10 @@ Thanks to everyone whose work landed in v3.8.51: | [@gonisulaimann](https://github.com/gonisulaimann) | #12368, #12369, #12371 | | [@Gorillaz322](https://github.com/Gorillaz322) | #12207 | | [@groovecityJO](https://github.com/groovecityJO) | #12682 | -| [@hartmark](https://github.com/hartmark) | #11434, #11452, #11473, #11499, #11703, #11983, #11984, #11985, #11986, #11988, #11989, #11990, #11994, #12221, #12293, #12445, #12446, #12447, #12448, #12460, #12461, #12623, #12646, #12650, #12680 | +| [@hartmark](https://github.com/hartmark) | #11434, #11452, #11473, #11499, #11703, #11983, #11984, #11985, #11986, #11988, #11989, #11990, #11994, #12221, #12293, #12445, #12446, #12447, #12448, #12460, #12461, #12623, #12646, #12650, #12680, #12717, #12718, #12727, #12741, #12854 | | [@hizzt](https://github.com/hizzt) | #11894 | | [@hongnoul](https://github.com/hongnoul) | #11484 | -| [@HouMinXi](https://github.com/HouMinXi) | #11411, #11414, #11512, #11518, #11520, #11641, #11642, #11643, #11687, #11779, #11849, #11850, #11851, #11915, #11916, #11918, #11919, #11920, #12013, #12017, #12033, #12042, #12106, #12139, #12166, #12169, #12171, #12205, #12213, #12312, #12325, #12487, #12488, #12495, #12504, #12557, #12566, #12590, #12591, #12624, #12626, #12632, #12637, #12678, #12696, #12697, #12711, #12733, #12746, #12767, #12770, #12789, #12803, #12805, #12811, #12866, #12868, #12899, #12926, #12934 | +| [@HouMinXi](https://github.com/HouMinXi) | #11411, #11414, #11512, #11518, #11520, #11641, #11642, #11643, #11687, #11779, #11849, #11850, #11851, #11915, #11916, #11918, #11919, #11920, #12013, #12017, #12033, #12042, #12106, #12139, #12166, #12169, #12171, #12205, #12213, #12312, #12325, #12487, #12488, #12495, #12504, #12557, #12566, #12590, #12591, #12624, #12626, #12632, #12637, #12678, #12696, #12697, #12711, #12733, #12746, #12767, #12770, #12789, #12803, #12805, #12811, #12866, #12868, #12899, #12926, #12934, #12950, #12951, #12974, #13001, #13006, #13011, #13017, #13026, #13027, #13034, #13035, #13038, #13042, #13050, #13060, #13061, #13069, #13107, #13120, #13136, #13195, #13197 | | [@Hsia97](https://github.com/Hsia97) | #11624 | | [@jacobsparts](https://github.com/jacobsparts) | #11854, #12155, #12167 | | [@joglomedia](https://github.com/joglomedia) | #11980 | @@ -908,11 +1143,11 @@ Thanks to everyone whose work landed in v3.8.51: | [@KaspaPulse](https://github.com/KaspaPulse) | #11389, #11469, #11628, #11666, #11910 | | [@keeltrace](https://github.com/keeltrace) | #12080, #12223 | | [@killer30001000](https://github.com/killer30001000) | #12178 | -| [@KooshaPari](https://github.com/KooshaPari) | #12470, #12592, #12667, #12699, #12703, #12706, #12764, #12769, #12771 | +| [@KooshaPari](https://github.com/KooshaPari) | #12470, #12592, #12667, #12699, #12703, #12706, #12764, #12769, #12771, #13300, #13301, #13302, #13340, #13401, #13403, #13404, #13405, #13406, #13407, #13409, #13410, #13411, #13412, #13413, #13414, #13418, #13424, #13427, #13433, #13523, #13524, #13525, #13528, #13531, #13532, #13534, #13539, #13543, #13545, #13546, #13547, #13550, #13553 | | [@kriptoburak](https://github.com/kriptoburak) | #11370 | | [@KrzysiekSko](https://github.com/KrzysiekSko) | #12425, #12432, #12673 | | [@marcelokarval](https://github.com/marcelokarval) | direct commit / report | -| [@maxmad64bis](https://github.com/maxmad64bis) | #1622, #11435, #11437, #11441, #11537, #11550, #11553, #11555, #11557, #11812, #11842, #11843, #11903, #12151, #12214, #12215, #12218, #12226, #12314, #12316, #12317, #12318, #12319, #12320, #12321, #12870 | +| [@maxmad64bis](https://github.com/maxmad64bis) | #1622, #11435, #11437, #11441, #11537, #11550, #11553, #11555, #11557, #11812, #11842, #11843, #11903, #12151, #12214, #12215, #12218, #12226, #12314, #12316, #12317, #12318, #12319, #12320, #12321, #12507, #12715, #12731, #12744, #12786, #12787, #12788, #12790, #12792, #12794, #12795, #12828, #12832, #12853, #12857, #12870, #12937, #12941, #12975, #13141, #13142, #13146, #13147 | | [@mdigitalbh81](https://github.com/mdigitalbh81) | #12240 | | [@Meet6338-X](https://github.com/Meet6338-X) | #11598, #11609, #12110 | | [@morpheus9393](https://github.com/morpheus9393) | #11943 | @@ -921,11 +1156,11 @@ Thanks to everyone whose work landed in v3.8.51: | [@NightStalker-87](https://github.com/NightStalker-87) | #12183 | | [@NoxzRCW](https://github.com/NoxzRCW) | #11879, #11880, #11881, #11882, #11883, #11935 | | [@ntdat812](https://github.com/ntdat812) | #11585, #12095 | -| [@ntdatt812](https://github.com/ntdatt812) | #11368, #11573, #11574, #11576, #11577, #11580, #11582, #11583, #11588, #11589, #11590, #11591, #11592, #11593, #11672, #12177, #12180 | +| [@ntdatt812](https://github.com/ntdatt812) | #11368, #11573, #11574, #11576, #11577, #11580, #11582, #11583, #11588, #11589, #11590, #11591, #11592, #11593, #11672, #12177, #12180, #12873, #12918, #12920, #12921, #12925, #12930, #13007, #13009, #13024, #13025, #13083, #13087, #13101, #13104, #13110 | | [@official-burak](https://github.com/official-burak) | #11542 | | [@opensource-elearning](https://github.com/opensource-elearning) | #12179, #12189, #12278, #12286 | | [@oyi77](https://github.com/oyi77) | #11408, #11409, #11421, #11505, #11677, #12036, #12110 | -| [@pacocartones](https://github.com/pacocartones) | #11521, #11522, #11527, #11528, #11529, #11530, #11531, #11532, #11533, #11534, #11595, #11599, #11603, #11604, #11605, #11607, #11610, #11676, #11714, #11716, #11718, #11767, #11838, #11860, #11862, #11869, #11871, #11872, #11873, #11903, #11906, #11921, #11934, #12359, #12360, #12361, #12362, #12364, #12365, #12373, #12374, #12375, #12376, #12377, #12379, #12380, #12381, #12386, #12387, #12389, #12390, #12394, #12395, #12397, #12401, #12403, #12404 | +| [@pacocartones](https://github.com/pacocartones) | #11521, #11522, #11527, #11528, #11529, #11530, #11531, #11532, #11533, #11534, #11595, #11599, #11603, #11604, #11605, #11607, #11610, #11676, #11714, #11716, #11718, #11767, #11838, #11860, #11862, #11869, #11871, #11872, #11873, #11903, #11906, #11921, #11934, #12356, #12358, #12359, #12360, #12361, #12362, #12364, #12365, #12373, #12374, #12375, #12376, #12377, #12379, #12380, #12381, #12386, #12387, #12389, #12390, #12394, #12395, #12397, #12401, #12403, #12404, #12522, #12523, #12535, #12536, #12540, #12541, #12543, #12545, #12548, #12549, #12550, #12551, #12552, #12647, #12651, #12653, #13055 | | [@patrykkopycinski](https://github.com/patrykkopycinski) | #11936, #11937, #12224 | | [@PauloFH](https://github.com/PauloFH) | #11509 | | [@PauloHSOliveira](https://github.com/PauloHSOliveira) | #12241 | @@ -937,7 +1172,7 @@ Thanks to everyone whose work landed in v3.8.51: | [@raheemuddin786](https://github.com/raheemuddin786) | #11491, #11839, #11840, #11841, #12003, #12230, #12231, #12232, #12233, #12234 | | [@rahilmavani](https://github.com/rahilmavani) | #11761 | | [@Rahulsharma0810](https://github.com/Rahulsharma0810) | #11771 | -| [@RaviTharuma](https://github.com/RaviTharuma) | #11710, #11727, #11797, #11798, #11802, #11805, #11806, #11811, #12098, #12099, #12101, #12449, #12452, #12472, #12473, #12493, #12533, #12607, #12628, #12631, #12636 | +| [@RaviTharuma](https://github.com/RaviTharuma) | #11710, #11727, #11797, #11798, #11802, #11805, #11806, #11811, #12098, #12099, #12101, #12449, #12452, #12472, #12473, #12493, #12533, #12607, #12628, #12631, #12636, #12875, #12876, #12880, #12882, #12884 | | [@rezjalibd](https://github.com/rezjalibd) | #12186 | | [@RhianB14](https://github.com/RhianB14) | #11692 | | [@rifqiawl](https://github.com/rifqiawl) | #11517, #11519, #11969 | diff --git a/changelog.d/features/12522-gamification-streak-badge-xp.md b/changelog.d/features/12522-gamification-streak-badge-xp.md deleted file mode 100644 index d13d0ad942..0000000000 --- a/changelog.d/features/12522-gamification-streak-badge-xp.md +++ /dev/null @@ -1 +0,0 @@ -- **feat(gamification): pay the documented `streak_bonus` and `badge_unlock` XP rewards.** `XP_REWARDS` listed both rewards but the award pipeline never paid them: the private reward table in `events.ts` omitted them, `updateStreak()` did not report when a streak extended, and badge unlocks carried no XP. Every request that extends a daily streak now pays `streak_bonus × streak length` once per UTC day (guarded by a same-day `xp_audit_log` check), and every badge unlocked through the pipeline pays `badge_unlock` once per badge (guarded by the `user_badges` primary key; `unlockBadge()` now reports whether it inserted). Bonus XP flows through the same `addXp` + level sync + global/weekly/monthly leaderboard path as action XP, so level-ups and rankings include it. The Radar supporter recognition unlock stays XP-free. (#12522 — thanks @pacocartones) diff --git a/changelog.d/features/12786-manifest-free-tier-capability.md b/changelog.d/features/12786-manifest-free-tier-capability.md deleted file mode 100644 index c075841a94..0000000000 --- a/changelog.d/features/12786-manifest-free-tier-capability.md +++ /dev/null @@ -1 +0,0 @@ -- **feat(providers):** advertise a `free-tier` capability in the provider plugin manifest for every provider with documented free models, so sidecars and dashboards can filter free-capable providers without reading the quota catalog ([#12786](https://github.com/diegosouzapw/OmniRoute/pull/12786)) — thanks @maxmad64bis diff --git a/changelog.d/features/12985-eurouter-provider.md b/changelog.d/features/12985-eurouter-provider.md deleted file mode 100644 index 9fe6ddb2ef..0000000000 --- a/changelog.d/features/12985-eurouter-provider.md +++ /dev/null @@ -1 +0,0 @@ -- **feat(providers):** Added EURouter as an OpenAI-compatible API-key gateway (`https://api.eurouter.ai/v1`), with live model discovery via `passthroughModels`. Its copy states that models are served by third-party upstreams listed per model, so an EU-based router is not read as EU data residency for inference. diff --git a/changelog.d/features/12986-greenpt-provider.md b/changelog.d/features/12986-greenpt-provider.md deleted file mode 100644 index 47a030fe75..0000000000 --- a/changelog.d/features/12986-greenpt-provider.md +++ /dev/null @@ -1 +0,0 @@ -- **feat(providers):** Added GreenPT as an OpenAI-compatible API-key provider (`https://api.greenpt.ai/v1`), with live model discovery via `passthroughModels`. No free-inference badge: the published docs describe a free API subscription billed per token, not a free tier. diff --git a/changelog.d/features/13044-i18n-batch-eu.md b/changelog.d/features/13044-i18n-batch-eu.md deleted file mode 100644 index c200f988ef..0000000000 --- a/changelog.d/features/13044-i18n-batch-eu.md +++ /dev/null @@ -1 +0,0 @@ -- **feat(i18n):** 9 new locales — Greek (`el`), Croatian (`hr`), Serbian (`sr`), Lithuanian (`lt`), Estonian (`et`), Latvian (`lv`), Slovenian (`sl`), Maltese (`mt`), Irish (`ga`) — across the dashboard, docs mirrors, CLI, README and the site; OmniRoute now ships every official EU language (51 locales). (#13044) diff --git a/changelog.d/features/13660-i18n-batch-sa.md b/changelog.d/features/13660-i18n-batch-sa.md deleted file mode 100644 index f545cc841a..0000000000 --- a/changelog.d/features/13660-i18n-batch-sa.md +++ /dev/null @@ -1 +0,0 @@ -- **feat(i18n):** 8 new locales — Kannada (`kn`), Malayalam (`ml`), Odia (`or`), Punjabi (`pa`), Nepali (`ne`), Sinhala (`si`), Burmese (`my`), Khmer (`km`) — across the dashboard, docs mirrors, CLI, README and the site (59 locales). The translator now restores the ICU literal escape around angle placeholders and splits oversized docs sections before translating. (#0000) diff --git a/changelog.d/features/agnes-30-flash-catalog.md b/changelog.d/features/agnes-30-flash-catalog.md deleted file mode 100644 index 0e5de18d1a..0000000000 --- a/changelog.d/features/agnes-30-flash-catalog.md +++ /dev/null @@ -1 +0,0 @@ -- feat(providers): **list Agnes 3.0 Flash as the current free chat model, drop retired 1.5 Flash, add Image 2.0/2.5 Flash plus Video 2.5/2.5 Flash, and discover the live `/v1/models` catalog (including the CN host `api.agnes-ai.cn`).** `agnes-1.5-flash` now forwards to `agnes-3.0-flash`. Video 2.5 polls `GET /v1/videos/{id}` (not the V2.0 `/agnesapi` contract). Live `/v1/models` (2026-09-09) no longer serves 1.5; the wiki marks it deprecated. 3.0 Flash is 512K context / 65,536 max output, same window as 2.5. CN-region keys use the existing per-connection base-URL field, default stays `apihub.agnes-ai.com`. diff --git a/changelog.d/features/codex-gpt-6-astra.md b/changelog.d/features/codex-gpt-6-astra.md deleted file mode 100644 index faec8f2369..0000000000 --- a/changelog.d/features/codex-gpt-6-astra.md +++ /dev/null @@ -1 +0,0 @@ -- **feat(sse):** Codex and OpenAI catalogs list GPT-6 Astra with effort aliases (`-low` through `-ultra`); Codex CLI identity pins `@openai/codex@0.153.4` in lockstep with the image ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) diff --git a/changelog.d/features/orchestration-compare-runs.md b/changelog.d/features/orchestration-compare-runs.md deleted file mode 100644 index e0f1f56030..0000000000 --- a/changelog.d/features/orchestration-compare-runs.md +++ /dev/null @@ -1,11 +0,0 @@ -- **feat(dashboard):** the orchestration History tab gained a "Compare runs" mode — toggling it - turns each grid cell into a 2-item selection queue (a 3rd click drops the oldest pick), and - picking two cells opens a side-by-side comparison panel instead of the usual detail drawer. - The panel fetches both runs' detail the same way the drawer does (falling back to persisted - history once a run leaves the live TTL window) and shows, per side: identity/source/state, - start time, a signed `right - left` delta for duration/cost/event count, the event timeline - aligned by index, and any memory hits. One side's fetch failing never blocks the other, and a - delta is only ever computed when both sides have a finite value — otherwise it renders "—", - never `NaN`. Comparing two runs from different sources or skills still works; a banner marks - the deltas as informational rather than hiding them, since the two runs aren't a strict - apples-to-apples pair. diff --git a/changelog.d/fixes/0000-responses-node-model-test.md b/changelog.d/fixes/0000-responses-node-model-test.md deleted file mode 100644 index c3191fc953..0000000000 --- a/changelog.d/fixes/0000-responses-node-model-test.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(dashboard):** model health tests for a provider node set to the Responses API now call `/v1/responses` with a Responses-shaped body instead of `/v1/chat/completions` — those models were reported as `Provider returned HTTP 200 but no text content` even though the same model answered normally through `/v1/responses` ([#13070](https://github.com/diegosouzapw/OmniRoute/issues/13070)) diff --git a/changelog.d/fixes/11912-roundrobin-opencode-zen-collision.md b/changelog.d/fixes/11912-roundrobin-opencode-zen-collision.md deleted file mode 100644 index beb24e4a89..0000000000 --- a/changelog.d/fixes/11912-roundrobin-opencode-zen-collision.md +++ /dev/null @@ -1 +0,0 @@ -- fix(routing): stop a round-robin combo's "opencode" targets from collapsing onto the opencode-zen connection (#11912) diff --git a/changelog.d/fixes/11977-antigravity-stale-compaction-log.md b/changelog.d/fixes/11977-antigravity-stale-compaction-log.md deleted file mode 100644 index decd6ea604..0000000000 --- a/changelog.d/fixes/11977-antigravity-stale-compaction-log.md +++ /dev/null @@ -1 +0,0 @@ -- fix(routing): stop the reactive-compaction debug log from lying when compression is globally disabled (#11977) diff --git a/changelog.d/fixes/11979-standalone-manifest-symlink-portability.md b/changelog.d/fixes/11979-standalone-manifest-symlink-portability.md deleted file mode 100644 index 2425114cdd..0000000000 --- a/changelog.d/fixes/11979-standalone-manifest-symlink-portability.md +++ /dev/null @@ -1 +0,0 @@ -- fix(electron): relativize standalone-bundle symlink targets so Stage 8 manifest verification stops failing on Windows (#11979) diff --git a/changelog.d/fixes/12061-compression-studio-run-error.md b/changelog.d/fixes/12061-compression-studio-run-error.md deleted file mode 100644 index c58ee351d5..0000000000 --- a/changelog.d/fixes/12061-compression-studio-run-error.md +++ /dev/null @@ -1 +0,0 @@ -- fix(dashboard): surface a visible error when Compression Studio's combined preview run fails (#12061) diff --git a/changelog.d/fixes/12063-compression-profile-header.md b/changelog.d/fixes/12063-compression-profile-header.md deleted file mode 100644 index b98e306f32..0000000000 --- a/changelog.d/fixes/12063-compression-profile-header.md +++ /dev/null @@ -1 +0,0 @@ -- fix(dashboard): make the compression "Effective pipeline" preview honor the active profile and warn when the master switch is off (#12063) diff --git a/changelog.d/fixes/12072-tinycms-dom-shim-leak.md b/changelog.d/fixes/12072-tinycms-dom-shim-leak.md deleted file mode 100644 index 60a4a8676a..0000000000 --- a/changelog.d/fixes/12072-tinycms-dom-shim-leak.md +++ /dev/null @@ -1 +0,0 @@ -- fix(providers): scope TinyCMS Web signer's DOM shims to each call instead of leaking them for the process lifetime, and surface a clean HTTP status on a non-JSON interception-toggles error (#12072) diff --git a/changelog.d/fixes/12111-vision-bridge-model-lockout.md b/changelog.d/fixes/12111-vision-bridge-model-lockout.md deleted file mode 100644 index 11e833e8d5..0000000000 --- a/changelog.d/fixes/12111-vision-bridge-model-lockout.md +++ /dev/null @@ -1 +0,0 @@ -- fix(guardrails): stop Vision Bridge from re-selecting a model locked after a 404 (#12111) diff --git a/changelog.d/fixes/12129-context-handoff-native-passthrough-shape.md b/changelog.d/fixes/12129-context-handoff-native-passthrough-shape.md deleted file mode 100644 index f2668facbd..0000000000 --- a/changelog.d/fixes/12129-context-handoff-native-passthrough-shape.md +++ /dev/null @@ -1 +0,0 @@ -- fix(sse): require Responses-shaped body before native OpenAI-compatible passthrough (#12129) diff --git a/changelog.d/fixes/12132-minimax-m3-adaptive-thinking.md b/changelog.d/fixes/12132-minimax-m3-adaptive-thinking.md deleted file mode 100644 index 496332561c..0000000000 --- a/changelog.d/fixes/12132-minimax-m3-adaptive-thinking.md +++ /dev/null @@ -1 +0,0 @@ -- fix(providers): minimax-m3 now collapses manual thinking.type:"enabled" to adaptive, preventing upstream 400 (2013) (#12132) diff --git a/changelog.d/fixes/12172-model-id-collision-chat-image.md b/changelog.d/fixes/12172-model-id-collision-chat-image.md deleted file mode 100644 index ef06abfd79..0000000000 --- a/changelog.d/fixes/12172-model-id-collision-chat-image.md +++ /dev/null @@ -1 +0,0 @@ -- fix(db): scope model visibility overrides by modality so hiding a Chat model no longer hides an identically-ID'd Image/Embeddings/etc. model (#12172) diff --git a/changelog.d/fixes/12173-lmstudio-multi-account.md b/changelog.d/fixes/12173-lmstudio-multi-account.md deleted file mode 100644 index 848be79448..0000000000 --- a/changelog.d/fixes/12173-lmstudio-multi-account.md +++ /dev/null @@ -1 +0,0 @@ -- fix(db): scope local-provider apiKey dedup to matching base URL so LM Studio/Ollama-style connections support multiple accounts (#12173) diff --git a/changelog.d/fixes/12190-trae-referer-401.md b/changelog.d/fixes/12190-trae-referer-401.md deleted file mode 100644 index 5b5959a49f..0000000000 --- a/changelog.d/fixes/12190-trae-referer-401.md +++ /dev/null @@ -1 +0,0 @@ -- fix(providers): refresh Trae's stale Referer/Origin and forward user timezone so imported connections stop failing with 401 (#12190) diff --git a/changelog.d/fixes/12196-opencode-go-gpt56luna.md b/changelog.d/fixes/12196-opencode-go-gpt56luna.md deleted file mode 100644 index 26fa9396a0..0000000000 --- a/changelog.d/fixes/12196-opencode-go-gpt56luna.md +++ /dev/null @@ -1 +0,0 @@ -- fix(providers): route opencode-go/gpt-5.6-luna to /responses instead of /chat/completions (#12196) diff --git a/changelog.d/fixes/12251-extra-upstream-headers-delete.md b/changelog.d/fixes/12251-extra-upstream-headers-delete.md deleted file mode 100644 index 7c6edfb5e5..0000000000 --- a/changelog.d/fixes/12251-extra-upstream-headers-delete.md +++ /dev/null @@ -1 +0,0 @@ -- fix(dashboard): allow deleting the last extra-upstream-header row even when invalid (#12251) diff --git a/changelog.d/fixes/12272-missing-i18n.md b/changelog.d/fixes/12272-missing-i18n.md deleted file mode 100644 index c1ef774beb..0000000000 --- a/changelog.d/fixes/12272-missing-i18n.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(i18n):** translate pre-existing `__MISSING__:` keys for `combo.sort`, `requestLogger.detail` expand/collapse, `common.profile`, and `settings.resilienceCredentialHealth*` across 39 locales ([#12272](https://github.com/diegosouzapw/OmniRoute/issues/12272)) diff --git a/changelog.d/fixes/12296-node-runtime-guard-early.md b/changelog.d/fixes/12296-node-runtime-guard-early.md deleted file mode 100644 index 1c2439f287..0000000000 --- a/changelog.d/fixes/12296-node-runtime-guard-early.md +++ /dev/null @@ -1 +0,0 @@ -- fix(cli): run the Node.js runtime compatibility guard before the heavy `tsx/esm` + Commander import chain so an unsupported runtime gets a clear message instead of a raw `Invalid regular expression flags` crash (#12296) diff --git a/changelog.d/fixes/12298-provider-node-delete-refresh.md b/changelog.d/fixes/12298-provider-node-delete-refresh.md deleted file mode 100644 index c902e6cc7b..0000000000 --- a/changelog.d/fixes/12298-provider-node-delete-refresh.md +++ /dev/null @@ -1 +0,0 @@ -- fix(dashboard): refresh the providers list after deleting a compatible provider node (#12298) diff --git a/changelog.d/fixes/12341-budget-alias-auto.md b/changelog.d/fixes/12341-budget-alias-auto.md deleted file mode 100644 index 3abc4c0b25..0000000000 --- a/changelog.d/fixes/12341-budget-alias-auto.md +++ /dev/null @@ -1 +0,0 @@ -- fix(usage): fail closed on API-key budget enforcement when a provider's `auto` routing alias has no pricing row, instead of silently counting it as $0 (#12341) diff --git a/changelog.d/fixes/12356-agnes-video-poll-model-name.md b/changelog.d/fixes/12356-agnes-video-poll-model-name.md deleted file mode 100644 index e756da7686..0000000000 --- a/changelog.d/fixes/12356-agnes-video-poll-model-name.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(providers):** include the submitted Agnes video model when polling by `video_id` diff --git a/changelog.d/fixes/12358-custom-node-api-type-precedence.md b/changelog.d/fixes/12358-custom-node-api-type-precedence.md deleted file mode 100644 index 739017ad26..0000000000 --- a/changelog.d/fixes/12358-custom-node-api-type-precedence.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(routing):** custom OpenAI-compatible nodes now honor the saved Chat/Responses API type after edits instead of letting the node's original ID prefix override the live connection setting ([#11884](https://github.com/diegosouzapw/OmniRoute/issues/11884)). diff --git a/changelog.d/fixes/12398-claude-truly-empty-stream.md b/changelog.d/fixes/12398-claude-truly-empty-stream.md deleted file mode 100644 index 39a86ec94f..0000000000 --- a/changelog.d/fixes/12398-claude-truly-empty-stream.md +++ /dev/null @@ -1 +0,0 @@ -- fix(sse): surface an error instead of a silent empty 200 when a Claude stream closes with zero bytes (#12398) diff --git a/changelog.d/fixes/12413-antigravity-oauth-redirect-hint.md b/changelog.d/fixes/12413-antigravity-oauth-redirect-hint.md deleted file mode 100644 index 96b6358d47..0000000000 --- a/changelog.d/fixes/12413-antigravity-oauth-redirect-hint.md +++ /dev/null @@ -1 +0,0 @@ -- fix(oauth): warn before the dead localhost:8080 redirect in antigravity/gemini `oauth start` (#12413) diff --git a/changelog.d/fixes/12517-devin-cli-sse-double-close.md b/changelog.d/fixes/12517-devin-cli-sse-double-close.md deleted file mode 100644 index 7663b8c90e..0000000000 --- a/changelog.d/fixes/12517-devin-cli-sse-double-close.md +++ /dev/null @@ -1 +0,0 @@ -- fix(providers): stop devin-cli spawn error from double-closing the SSE controller (#12517) diff --git a/changelog.d/fixes/12523-rerank-topk-and-return-documents.md b/changelog.d/fixes/12523-rerank-topk-and-return-documents.md deleted file mode 100644 index 88b1a23726..0000000000 --- a/changelog.d/fixes/12523-rerank-topk-and-return-documents.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(rerank):** clamp Voyage `top_k` to the documents actually sent after empty-string filtering, and honor `return_documents: false` in the NVIDIA response adapter (#12523 — thanks @pacocartones) diff --git a/changelog.d/fixes/12536-audio-translations-combo-resolution.md b/changelog.d/fixes/12536-audio-translations-combo-resolution.md deleted file mode 100644 index 0f16b8f32e..0000000000 --- a/changelog.d/fixes/12536-audio-translations-combo-resolution.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(audio):** `/v1/audio/translations` now resolves combo names the way `/v1/audio/transcriptions` already does, so a combo that `GET /v1/models` advertises is fanned out to its targets instead of being rejected with `400 Invalid translation model: . Use format: provider/model`; literal `provider/model` ids and unknown bare names behave as before (#12536 — thanks @pacocartones) diff --git a/changelog.d/fixes/12540-gemini-strip-prefixitems-nested.md b/changelog.d/fixes/12540-gemini-strip-prefixitems-nested.md deleted file mode 100644 index d1b24b21ab..0000000000 --- a/changelog.d/fixes/12540-gemini-strip-prefixitems-nested.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(gemini):** strip the JSON-Schema-2020-12 `prefixItems` keyword from Gemini tool schemas at every nesting level, so Claude Code tool definitions no longer fail with `400 Unknown name "prefixItems"` on Gemini models (#12540 — thanks @pacocartones) diff --git a/changelog.d/fixes/12541-api-manager-skeleton-a11y-status.md b/changelog.d/fixes/12541-api-manager-skeleton-a11y-status.md deleted file mode 100644 index 2896aef4da..0000000000 --- a/changelog.d/fixes/12541-api-manager-skeleton-a11y-status.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(api-manager):** Expose an accessible loading status while API keys are fetched instead of an empty accessibility tree (#12541 — thanks @pacocartones) diff --git a/changelog.d/fixes/12543-video-frame-estimate-clamp.md b/changelog.d/fixes/12543-video-frame-estimate-clamp.md deleted file mode 100644 index 767c8fb587..0000000000 --- a/changelog.d/fixes/12543-video-frame-estimate-clamp.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(video):** Clamp `estimateJpegFrameBytes` at zero for padding-only payloads and build the three encode-side frame data URIs from `JPEG_FRAME_DATA_URI_PREFIX` instead of a repeated literal (#12543 — thanks @pacocartones) diff --git a/changelog.d/fixes/12545-devin-windows-agentic-home-check.md b/changelog.d/fixes/12545-devin-windows-agentic-home-check.md deleted file mode 100644 index fd59162f4d..0000000000 --- a/changelog.d/fixes/12545-devin-windows-agentic-home-check.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(devin):** accept Windows `DEVIN_AGENTIC_HOME` sandbox paths (`C:\...\.sandbox\...`) in the isolated-home check so the Devin Claude Bridge no longer fails closed on Windows ([#12405](https://github.com/diegosouzapw/OmniRoute/issues/12405)) (#12545 — thanks @pacocartones) diff --git a/changelog.d/fixes/12548-db-cleanup-orphaned-conversation-nodes.md b/changelog.d/fixes/12548-db-cleanup-orphaned-conversation-nodes.md deleted file mode 100644 index 1a5a015518..0000000000 --- a/changelog.d/fixes/12548-db-cleanup-orphaned-conversation-nodes.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(db):** Add `conversation_turn_nodes` and orphaned `agentic_conversations` to the auto-cleanup cycle under the existing `retention.callLogs` window, so identity nodes whose call-log content has already been purged no longer accumulate without bound in `storage.sqlite` (#12548 — thanks @pacocartones) diff --git a/changelog.d/fixes/12549-i18n-escape-raw-name-tag-12505.md b/changelog.d/fixes/12549-i18n-escape-raw-name-tag-12505.md deleted file mode 100644 index 6de3771893..0000000000 --- a/changelog.d/fixes/12549-i18n-escape-raw-name-tag-12505.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(i18n):** Wrap the `~/.claude/profiles//settings.json` placeholder in ICU single quotes in the `OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES` feature-flag description across all 42 locales and the TypeScript default, so next-intl no longer fails with `INVALID_MESSAGE: UNCLOSED_TAG` and the Feature Flags card shows the description instead of the raw key (#12549 — thanks @pacocartones) diff --git a/changelog.d/fixes/12550-orchestration-emit-real-status.md b/changelog.d/fixes/12550-orchestration-emit-real-status.md deleted file mode 100644 index a0567e31f0..0000000000 --- a/changelog.d/fixes/12550-orchestration-emit-real-status.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(orchestration):** `updateCloudAgentTask` now publishes the task's real `status` on `agent.task.updated` when an update only touches `result`, `activities` or `error`, instead of the fabricated `"updated"` state, and stays silent when no row matched the id (#12550 — thanks @pacocartones) diff --git a/changelog.d/fixes/12551-i18n-home-recent-requests-topology.md b/changelog.d/fixes/12551-i18n-home-recent-requests-topology.md deleted file mode 100644 index 87dbbcf0b7..0000000000 --- a/changelog.d/fixes/12551-i18n-home-recent-requests-topology.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(i18n):** the home "Recent Requests" panel and the Provider Topology legend are now translated instead of rendering English copies on non-English dashboards; the legend reads its own `home.topologyLegend*` labels with consistent casing rather than borrowing the memory-settings "Recent" and analytics "Error" strings (#12551 — thanks @pacocartones). diff --git a/changelog.d/fixes/12552-feature-flags-reference-sync.md b/changelog.d/fixes/12552-feature-flags-reference-sync.md deleted file mode 100644 index 62b599ac6d..0000000000 --- a/changelog.d/fixes/12552-feature-flags-reference-sync.md +++ /dev/null @@ -1 +0,0 @@ -- **docs(reference):** bring the `FEATURE_FLAGS.md` catalog back to 1:1 with `featureFlagDefinitions.ts` — 20 missing flags added, the two `*_BLOCK_THRESHOLD` env-only knobs moved out of the flag tables, category/total counts and the Live WS port corrected, guarded by a static test (#12552 — thanks @pacocartones) diff --git a/changelog.d/fixes/12561-kilo-pass-i18n.md b/changelog.d/fixes/12561-kilo-pass-i18n.md deleted file mode 100644 index c303682e68..0000000000 --- a/changelog.d/fixes/12561-kilo-pass-i18n.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(i18n):** backfill missing `usage.kiloPass*` strings in 39 locales and restore `featureFlagOmnirouteDisableThinkingLevelVariantsDescription` in `pt.json` ([#12561](https://github.com/diegosouzapw/OmniRoute/issues/12561)) — thanks @HouMinXi diff --git a/changelog.d/fixes/12568-compose-loopback-bind.md b/changelog.d/fixes/12568-compose-loopback-bind.md deleted file mode 100644 index ab5c513796..0000000000 --- a/changelog.d/fixes/12568-compose-loopback-bind.md +++ /dev/null @@ -1 +0,0 @@ -- fix(docker): default docker-compose app ports (dashboard/API/live-WS) to loopback instead of `0.0.0.0`, closing the anonymous `/v1` LAN/WAN exposure gap left open by `REQUIRE_API_KEY=false` (#12568) diff --git a/changelog.d/fixes/12569-webhook-dns-rebinding-ssrf.md b/changelog.d/fixes/12569-webhook-dns-rebinding-ssrf.md deleted file mode 100644 index 56ed320269..0000000000 --- a/changelog.d/fixes/12569-webhook-dns-rebinding-ssrf.md +++ /dev/null @@ -1 +0,0 @@ -- fix(api): close DNS-rebinding SSRF gap in webhook outbound-URL guard (#12569) diff --git a/changelog.d/fixes/12571-vnc-cdp-bridge-auth.md b/changelog.d/fixes/12571-vnc-cdp-bridge-auth.md deleted file mode 100644 index 83cbb6cf8e..0000000000 --- a/changelog.d/fixes/12571-vnc-cdp-bridge-auth.md +++ /dev/null @@ -1 +0,0 @@ -- fix(docker): require a per-session token on the VNC browser CDP bridge and isolate it on a dedicated Docker network (#12571) diff --git a/changelog.d/fixes/12572-adobe-firefly-session-file-perms.md b/changelog.d/fixes/12572-adobe-firefly-session-file-perms.md deleted file mode 100644 index 8030ed697c..0000000000 --- a/changelog.d/fixes/12572-adobe-firefly-session-file-perms.md +++ /dev/null @@ -1 +0,0 @@ -- fix(open-sse): write Adobe Firefly session tokens and cookie jars with 0700/0600 permissions instead of the process umask (#12572) diff --git a/changelog.d/fixes/12573-gemini-cors-wildcard.md b/changelog.d/fixes/12573-gemini-cors-wildcard.md deleted file mode 100644 index 4ed9e9804c..0000000000 --- a/changelog.d/fixes/12573-gemini-cors-wildcard.md +++ /dev/null @@ -1 +0,0 @@ -- fix(api): remove hardcoded wildcard CORS in openai-to-gemini-sse.ts so the centralized fail-closed CORS gate is the sole source of `Access-Control-Allow-Origin` (#12573) diff --git a/changelog.d/fixes/12574-elevenlabs-policy-enforcement.md b/changelog.d/fixes/12574-elevenlabs-policy-enforcement.md deleted file mode 100644 index 930c6ed52c..0000000000 --- a/changelog.d/fixes/12574-elevenlabs-policy-enforcement.md +++ /dev/null @@ -1 +0,0 @@ -- fix(api): enforce API key policy (budget/rate-limit/schedule/endpoint scoping) on the ElevenLabs speech-to-text, text-to-speech and voices proxy routes (#12574) diff --git a/changelog.d/fixes/12577-huggingchat-buffer-cap.md b/changelog.d/fixes/12577-huggingchat-buffer-cap.md deleted file mode 100644 index a50c56acad..0000000000 --- a/changelog.d/fixes/12577-huggingchat-buffer-cap.md +++ /dev/null @@ -1 +0,0 @@ -- fix(sse): cap HuggingChat NDJSON body size and bound the read loop with the fetch timeout so a stalled or hostile upstream cannot buffer unbounded memory (#12577) diff --git a/changelog.d/fixes/12578-cliproxyapi-loopback-bind.md b/changelog.d/fixes/12578-cliproxyapi-loopback-bind.md deleted file mode 100644 index 38eec82fc8..0000000000 --- a/changelog.d/fixes/12578-cliproxyapi-loopback-bind.md +++ /dev/null @@ -1 +0,0 @@ -- fix(docker): scope the cliproxyapi/qdrant/bifrost sidecars to loopback by default and forward `CLIPROXYAPI_MANAGEMENT_KEY` into the cliproxyapi container so its management API is not left both unauthenticated and LAN-published (#12578) diff --git a/changelog.d/fixes/12579-db-export-tempdir-mkdtemp.md b/changelog.d/fixes/12579-db-export-tempdir-mkdtemp.md deleted file mode 100644 index 304757ea01..0000000000 --- a/changelog.d/fixes/12579-db-export-tempdir-mkdtemp.md +++ /dev/null @@ -1 +0,0 @@ -- fix(api): create DB export temp paths with `fs.mkdtempSync` instead of predictable timestamps (#12579) diff --git a/changelog.d/fixes/12594-cline-401-oauth.md b/changelog.d/fixes/12594-cline-401-oauth.md deleted file mode 100644 index 02643a94ae..0000000000 --- a/changelog.d/fixes/12594-cline-401-oauth.md +++ /dev/null @@ -1 +0,0 @@ -- Cline 401 bodies that say "re-authenticate your Cline account" classify as a refreshable OAuth token, not a terminal expired key. The cooling panel no longer labels every cooldown as a 429; it shows the recorded last error instead. (#12594) diff --git a/changelog.d/fixes/12613-combo-openrouter-modalities.md b/changelog.d/fixes/12613-combo-openrouter-modalities.md deleted file mode 100644 index aaa4782dcb..0000000000 --- a/changelog.d/fixes/12613-combo-openrouter-modalities.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(catalog):** degrade unknown combo targets instead of dropping LCD modalities, and persist OpenRouter `architecture.input_modalities` into the capability snapshot ([#12613](https://github.com/diegosouzapw/OmniRoute/issues/12613)) diff --git a/changelog.d/fixes/12633-opencode-zen-responses-auth-header.md b/changelog.d/fixes/12633-opencode-zen-responses-auth-header.md deleted file mode 100644 index 9877207035..0000000000 --- a/changelog.d/fixes/12633-opencode-zen-responses-auth-header.md +++ /dev/null @@ -1 +0,0 @@ -- fix(providers): send `x-api-key` instead of `Authorization: Bearer` for OpenCode Zen's `/v1/responses` endpoint (Muse Spark Contributor models), fixing a 401 on OmniRoute's auth header (#12633) diff --git a/changelog.d/fixes/12645-auggie-cli-not-found-shell-exit.md b/changelog.d/fixes/12645-auggie-cli-not-found-shell-exit.md deleted file mode 100644 index f322ac43d9..0000000000 --- a/changelog.d/fixes/12645-auggie-cli-not-found-shell-exit.md +++ /dev/null @@ -1 +0,0 @@ -- fix(sse): surface the actionable "Auggie CLI not found" message when the shell reports a missing `auggie` binary via exit code instead of a spawn error (#12645) diff --git a/changelog.d/fixes/12647-background-degradation-deletions.md b/changelog.d/fixes/12647-background-degradation-deletions.md deleted file mode 100644 index 5339d07594..0000000000 --- a/changelog.d/fixes/12647-background-degradation-deletions.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(config):** Persist deletions of built-in background-degradation entries — when a stored settings record exists its `degradationMap` is now authoritative instead of being merged under the defaults, so an entry the user removed in the dashboard no longer reappears on the next apply or restart ([#12424](https://github.com/diegosouzapw/OmniRoute/issues/12424)) diff --git a/changelog.d/fixes/12651-action-count-durable-counters.md b/changelog.d/fixes/12651-action-count-durable-counters.md deleted file mode 100644 index 26e2f24728..0000000000 --- a/changelog.d/fixes/12651-action-count-durable-counters.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(gamification):** action-count badge milestones (First Token, Token Consumer, Token Machine, Token Whale, and the token-sharing tier) are now backed by a durable `xp_action_counts` counter incremented in `addXp()`, instead of a live `COUNT(*)` over `xp_audit_log`. The audit log is pruned by `retention.xpAuditLog` (default 30 days), so on a default install those "lifetime" milestones were really "actions in the last 30 days" and unlocked badges could stop unlocking once old rows aged out. `getActionCount()` and `checkActionCountBadges()` now read the same durable source, and a migration backfills existing totals from the surviving audit rows ([#12546](https://github.com/diegosouzapw/OmniRoute/issues/12546)) diff --git a/changelog.d/fixes/12653-image-combo-edits-fallback.md b/changelog.d/fixes/12653-image-combo-edits-fallback.md deleted file mode 100644 index 4d143b37c1..0000000000 --- a/changelog.d/fixes/12653-image-combo-edits-fallback.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(images):** `/v1/images/edits` now iterates a combo's targets the same way `/v1/images/generations` does (#9239) instead of flattening a bare combo to its first target. A combo whose first target is not edit-capable — or lacks credentials — now falls through to a later edit-capable target rather than hard-erroring, and missing credentials are skipped (not a hard `401`) to match the generations path. The per-target skip/terminal classification is extracted into a shared `runImageComboTargets` loop, so generations behavior is unchanged ([#12547](https://github.com/diegosouzapw/OmniRoute/issues/12547)). diff --git a/changelog.d/fixes/12656-tls-wreq-first-byte-watchdog.md b/changelog.d/fixes/12656-tls-wreq-first-byte-watchdog.md deleted file mode 100644 index 6adb30b64f..0000000000 --- a/changelog.d/fixes/12656-tls-wreq-first-byte-watchdog.md +++ /dev/null @@ -1 +0,0 @@ -- fix(sse): add first-byte watchdog to the TLS-fingerprint transport so a stalled wreq body falls back instead of hanging for minutes (#12656) diff --git a/changelog.d/fixes/12659-combo-skip-reasons-tiny-budget-probe.md b/changelog.d/fixes/12659-combo-skip-reasons-tiny-budget-probe.md deleted file mode 100644 index fb6fa47eda..0000000000 --- a/changelog.d/fixes/12659-combo-skip-reasons-tiny-budget-probe.md +++ /dev/null @@ -1 +0,0 @@ -- fix(sse): exempt tiny-budget reasoning probes from combo quality failure and surface persisted-cooldown skips in ALL_TARGETS_SKIPPED diagnostics (#12659) diff --git a/changelog.d/fixes/12681-opencode-muse-spark-context-length.md b/changelog.d/fixes/12681-opencode-muse-spark-context-length.md deleted file mode 100644 index aaec199255..0000000000 --- a/changelog.d/fixes/12681-opencode-muse-spark-context-length.md +++ /dev/null @@ -1 +0,0 @@ -- fix(models): declare the real ~1M contextLength for OpenCode Zen's Muse Spark 1.2 models instead of falling back to the 200000 provider default (#12681) diff --git a/changelog.d/fixes/12702-codebuddy-cn-useragent-consistency.md b/changelog.d/fixes/12702-codebuddy-cn-useragent-consistency.md deleted file mode 100644 index 10beb4efdf..0000000000 --- a/changelog.d/fixes/12702-codebuddy-cn-useragent-consistency.md +++ /dev/null @@ -1 +0,0 @@ -- fix(oauth): align codebuddy-cn OAuth User-Agent with the chat/usage CLI version to avoid WAF false positives (#12702) diff --git a/changelog.d/fixes/12709-guest-import-settings.md b/changelog.d/fixes/12709-guest-import-settings.md deleted file mode 100644 index 0542d75ac3..0000000000 --- a/changelog.d/fixes/12709-guest-import-settings.md +++ /dev/null @@ -1 +0,0 @@ -- fix(dashboard): surface an authentication-required banner instead of silently blanking database settings for a guest session (#12709) diff --git a/changelog.d/fixes/12715-cascade-pr1-queue-bound.md b/changelog.d/fixes/12715-cascade-pr1-queue-bound.md deleted file mode 100644 index a311e13997..0000000000 --- a/changelog.d/fixes/12715-cascade-pr1-queue-bound.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(resilience):** stop unbounded queue that hangs 6min until Aborted — gate, provider slot, and Bottleneck queue now share a per-connection `maxWaitMs` budget; `fail-closed` on exhaust (503); execution backstop `executionMaxWaitMs` overridable per connection with upstream clamp ([#12715](https://github.com/diegosouzapw/OmniRoute/pull/12715)) — thanks @maxmad64bis (with thanks to @Tushar49 for surfacing the slow-provider need in #12635) diff --git a/changelog.d/fixes/12731-scoring-packs-quality-reliability.md b/changelog.d/fixes/12731-scoring-packs-quality-reliability.md deleted file mode 100644 index 32033d4456..0000000000 --- a/changelog.d/fixes/12731-scoring-packs-quality-reliability.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(auto-combo):** every mode pack now carries `quality` and `reliability` weights (`quality` 0.02, 0.03 in `quality-first`; `reliability` 0.03, 0.04 in `reliability-first`), so selecting a pack no longer silences either signal; `DEFAULT_WEIGHTS` is unchanged ([#12731](https://github.com/diegosouzapw/OmniRoute/pull/12731)) — thanks @maxmad64bis diff --git a/changelog.d/fixes/12732-basereds-orphans-v3851.md b/changelog.d/fixes/12732-basereds-orphans-v3851.md deleted file mode 100644 index 35c230cd1b..0000000000 --- a/changelog.d/fixes/12732-basereds-orphans-v3851.md +++ /dev/null @@ -1,4 +0,0 @@ -- **fix(sse):** the provider execution pipeline keeps the upstream error `code`/`type` again — both were lost when the non-streaming leg was extracted, so a config-class `422` (Antigravity missing project) degraded into a generic account cooldown instead of reaching the gate that recognises it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) -- **fix(db):** a legacy `call_logs` table no longer aborts startup — the provider-stats index is created after the column healing runs, not before it, so an install predating `request_type` opens instead of failing with `no such column` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) -- **fix(catalog):** a malformed row in the operator-writable custom-models blob no longer kills every `auto/*` pool ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) -- **fix(tests):** realign the guards that the same day's merges left asserting the old behavior — the provider-count total after GreenPT and EURouter, the GLM stream buffer slot that `#12925` turned into a declared parameter, and the injection scan bound that `#13104` reshaped from a head-only window into head-plus-tail ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) diff --git a/changelog.d/fixes/12734-semantic-cache-tool-choice.md b/changelog.d/fixes/12734-semantic-cache-tool-choice.md deleted file mode 100644 index e3dc7045f9..0000000000 --- a/changelog.d/fixes/12734-semantic-cache-tool-choice.md +++ /dev/null @@ -1 +0,0 @@ -- fix(cache): fold tool_choice/tools/response_format into the semantic cache signature so a cached tool_calls response can no longer be replayed for a request whose tool policy forbids it (#12734) diff --git a/changelog.d/fixes/12744-free-predicate-guard.md b/changelog.d/fixes/12744-free-predicate-guard.md deleted file mode 100644 index a0f979a149..0000000000 --- a/changelog.d/fixes/12744-free-predicate-guard.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(models):** stop treating provider-supplied `isFree:true`/`:free`/`0/0` flags as trusted unless the provider has a documented free tier — only the shipped free-tier catalog decides otherwise; fetched `isFree:true` trusted only for a free-tier provider, custom `isFree:true` via a trusted path ([#12744](https://github.com/diegosouzapw/OmniRoute/pull/12744)) — thanks @maxmad64bis diff --git a/changelog.d/fixes/12745-memory-rerank-loopback-auth.md b/changelog.d/fixes/12745-memory-rerank-loopback-auth.md deleted file mode 100644 index 402ddd0988..0000000000 --- a/changelog.d/fixes/12745-memory-rerank-loopback-auth.md +++ /dev/null @@ -1 +0,0 @@ -- fix(memory): authenticate the internal /v1/rerank loopback call so memory reranking no longer silently degrades to unranked order when REQUIRE_API_KEY=true (#12745) diff --git a/changelog.d/fixes/12749-ollama-cloud-usage-cookie.md b/changelog.d/fixes/12749-ollama-cloud-usage-cookie.md deleted file mode 100644 index 244aec13c5..0000000000 --- a/changelog.d/fixes/12749-ollama-cloud-usage-cookie.md +++ /dev/null @@ -1 +0,0 @@ -- fix(sse): parse Ollama Cloud's current usage markup (`$X of $Y used` aria-label, nested width style) (#12749) diff --git a/changelog.d/fixes/12783-setup-opencode-api-key-precedence.md b/changelog.d/fixes/12783-setup-opencode-api-key-precedence.md deleted file mode 100644 index 0cd25a8908..0000000000 --- a/changelog.d/fixes/12783-setup-opencode-api-key-precedence.md +++ /dev/null @@ -1 +0,0 @@ -- fix(cli): setup-opencode no longer sends an active context's management token to `/v1/models` when `--api-key`/`OMNIROUTE_API_KEY` is supplied — an explicit flag or the env var now always outranks the context's token, and the flag itself is no longer swallowed by the parent program's global `--api-key` option (#12783) diff --git a/changelog.d/fixes/12784-arcee-ai-provider-registry.md b/changelog.d/fixes/12784-arcee-ai-provider-registry.md deleted file mode 100644 index f211b561b8..0000000000 --- a/changelog.d/fixes/12784-arcee-ai-provider-registry.md +++ /dev/null @@ -1 +0,0 @@ -- fix(sse): register Arcee AI in the executor provider registry so requests reach api.arcee.ai instead of silently falling back to OpenAI (#12784) diff --git a/changelog.d/fixes/12787-saturation-singleflight.md b/changelog.d/fixes/12787-saturation-singleflight.md deleted file mode 100644 index 51ee5c7ac4..0000000000 --- a/changelog.d/fixes/12787-saturation-singleflight.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(quota):** share one upstream quota read when identical saturation checks arrive at the same time, so traffic bursts don't multiply provider API calls ([#12787](https://github.com/diegosouzapw/OmniRoute/pull/12787)) — thanks @maxmad64bis diff --git a/changelog.d/fixes/12788-ratelimit-write-guard.md b/changelog.d/fixes/12788-ratelimit-write-guard.md deleted file mode 100644 index 1662233abf..0000000000 --- a/changelog.d/fixes/12788-ratelimit-write-guard.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(db):** ignore expired or invalid rate-limit cooldown writes so a stale timestamp can't lock a connection that should be usable — clearing still works as before ([#12788](https://github.com/diegosouzapw/OmniRoute/pull/12788)) — thanks @maxmad64bis diff --git a/changelog.d/fixes/12790-provider-order-single-source.md b/changelog.d/fixes/12790-provider-order-single-source.md deleted file mode 100644 index 6a2c587375..0000000000 --- a/changelog.d/fixes/12790-provider-order-single-source.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(providers):** single source for provider order with `xao` ranking alongside `xai-oauth` ([#12790](https://github.com/diegosouzapw/OmniRoute/pull/12790)) — thanks @maxmad64bis diff --git a/changelog.d/fixes/12792-pricing-valuation.md b/changelog.d/fixes/12792-pricing-valuation.md deleted file mode 100644 index 7eea070726..0000000000 --- a/changelog.d/fixes/12792-pricing-valuation.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(routing):** off-table models check the free-model catalog before inheriting premium prices, latency bootstraps from the observed pool median, async tiers read live database pricing with a 90-day freshness gate, and the tier cache invalidates on every pricing write ([#12792](https://github.com/diegosouzapw/OmniRoute/pull/12792)) — thanks @maxmad64bis diff --git a/changelog.d/fixes/12794-snapshot-scoring.md b/changelog.d/fixes/12794-snapshot-scoring.md deleted file mode 100644 index 18e0b55f22..0000000000 --- a/changelog.d/fixes/12794-snapshot-scoring.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(auto-combo):** snapshot scoring uses the observed per-connection account tier and per-model quality instead of neutral constants, and circuit-open providers rank lower at build time ([#12794](https://github.com/diegosouzapw/OmniRoute/pull/12794)) — thanks @maxmad64bis diff --git a/changelog.d/fixes/12795-pool-diagnosis.md b/changelog.d/fixes/12795-pool-diagnosis.md deleted file mode 100644 index 4dd1a55b51..0000000000 --- a/changelog.d/fixes/12795-pool-diagnosis.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(pool):** empty `auto/*` pools now say why they are empty, and the models listing uses the same paid check as routing ([#12795](https://github.com/diegosouzapw/OmniRoute/pull/12795)) — thanks @maxmad64bis diff --git a/changelog.d/fixes/12800-cliproxyapi-unknown-provider.md b/changelog.d/fixes/12800-cliproxyapi-unknown-provider.md deleted file mode 100644 index cc0a6e2f21..0000000000 --- a/changelog.d/fixes/12800-cliproxyapi-unknown-provider.md +++ /dev/null @@ -1 +0,0 @@ -- fix(routing): recognize CLIProxyAPI's 'unknown provider for model' 400 as fallback-worthy (#12800) diff --git a/changelog.d/fixes/12828-translate-usage-trailing-chunk.md b/changelog.d/fixes/12828-translate-usage-trailing-chunk.md deleted file mode 100644 index 748e325c12..0000000000 --- a/changelog.d/fixes/12828-translate-usage-trailing-chunk.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(sse):** translate-mode streams now emit the estimated token counts as a trailing usage-only chunk before `[DONE]` when the upstream stays silent, so metered chat clients see totals instead of nothing ([#12828](https://github.com/diegosouzapw/OmniRoute/pull/12828)) — thanks @maxmad64bis diff --git a/changelog.d/fixes/12832-call-logs-provider-stats.md b/changelog.d/fixes/12832-call-logs-provider-stats.md deleted file mode 100644 index 9e4ef7c433..0000000000 --- a/changelog.d/fixes/12832-call-logs-provider-stats.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(db):** provider stats stay truthful on empty and legacy databases: fallback counts default to `0` instead of `null`, latency averages read `null` (not `0`) when no durations were recorded, failures logged before the error-type column existed group under `pre_migration` instead of `unclassified`, and per-provider queries use two new composite indexes ([#12832](https://github.com/diegosouzapw/OmniRoute/pull/12832)) — thanks @maxmad64bis diff --git a/changelog.d/fixes/12849-nvidia-stale-synced-catalog.md b/changelog.d/fixes/12849-nvidia-stale-synced-catalog.md deleted file mode 100644 index 9987f0a391..0000000000 --- a/changelog.d/fixes/12849-nvidia-stale-synced-catalog.md +++ /dev/null @@ -1 +0,0 @@ -- fix(nvidia): fail open when a synced model catalog goes stale instead of gating forever (#12849) diff --git a/changelog.d/fixes/12853-wal-maintenance-extraction.md b/changelog.d/fixes/12853-wal-maintenance-extraction.md deleted file mode 100644 index eabeb66866..0000000000 --- a/changelog.d/fixes/12853-wal-maintenance-extraction.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(db):** WAL maintenance lives in its own module: a `TRUNCATE` checkpoint that hits a busy database now warns with its streak and retries once via `PASSIVE` instead of logging success, and closing-time checkpoints no longer report success on builds without a database file, with the busy totals visible in the authenticated monitoring health payload ([#12853](https://github.com/diegosouzapw/OmniRoute/pull/12853)) diff --git a/changelog.d/fixes/12857-empty-quota-unknown.md b/changelog.d/fixes/12857-empty-quota-unknown.md deleted file mode 100644 index 4cbace07ff..0000000000 --- a/changelog.d/fixes/12857-empty-quota-unknown.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(health):** quota with no snapshots reads empty instead of a contradicting 0% ([#12857](https://github.com/diegosouzapw/OmniRoute/pull/12857)) — thanks @maxmad64bis diff --git a/changelog.d/fixes/12888-a2a-dashboard-auth.md b/changelog.d/fixes/12888-a2a-dashboard-auth.md deleted file mode 100644 index 8e6b45fa81..0000000000 --- a/changelog.d/fixes/12888-a2a-dashboard-auth.md +++ /dev/null @@ -1 +0,0 @@ -- fix(a2a): accept the dashboard's own session cookie on /a2a so "Run message/send" no longer fails with "Unauthorized: missing or invalid API key" (#12888) diff --git a/changelog.d/fixes/12896-call-logs-filter-parity.md b/changelog.d/fixes/12896-call-logs-filter-parity.md deleted file mode 100644 index 3b46f54763..0000000000 --- a/changelog.d/fixes/12896-call-logs-filter-parity.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(logs):** the Logs grid's in-memory filter pass no longer discards rows the SQL query already matched — selecting an API key from the dropdown (which sends the key's id) returns its calls again, the Combo tab shows every combo instead of only those whose name contains a "1", and the model filter and search cover the same columns as the query ([#12896](https://github.com/diegosouzapw/OmniRoute/pull/12896)) — fixes [#12873](https://github.com/diegosouzapw/OmniRoute/issues/12873) diff --git a/changelog.d/fixes/12918-a2a-status-agent-card-base-url.md b/changelog.d/fixes/12918-a2a-status-agent-card-base-url.md deleted file mode 100644 index 11080aa856..0000000000 --- a/changelog.d/fixes/12918-a2a-status-agent-card-base-url.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(a2a):** `/api/a2a/status` now builds the agent card from the request that asked for it, so a gateway reached at a non-localhost host no longer advertises `http://localhost:20128` as its A2A URL ([#12918](https://github.com/diegosouzapw/OmniRoute/pull/12918)). diff --git a/changelog.d/fixes/12920-aging-tool-result-block-order.md b/changelog.d/fixes/12920-aging-tool-result-block-order.md deleted file mode 100644 index 6f53f0e187..0000000000 --- a/changelog.d/fixes/12920-aging-tool-result-block-order.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(compression):** progressive aging now appends its `[COMPRESSED:aging:…]` annotation after a turn's `tool_result` blocks instead of in front of them, so Anthropic no longer rejects aged conversations with "`tool_use` ids were found without `tool_result` blocks immediately after" ([#12920](https://github.com/diegosouzapw/OmniRoute/pull/12920)). diff --git a/changelog.d/fixes/12921-bedrock-vendor-context-limits.md b/changelog.d/fixes/12921-bedrock-vendor-context-limits.md deleted file mode 100644 index 6eb84d5967..0000000000 --- a/changelog.d/fixes/12921-bedrock-vendor-context-limits.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(bedrock):** model import now resolves context limits for every vendor prefix instead of only `anthropic.*`, so `global.openai.gpt-5.6-*` no longer imports with a null `inputTokenLimit` and gets rejected pre-flight at the 200k default ([#12921](https://github.com/diegosouzapw/OmniRoute/pull/12921)). diff --git a/changelog.d/fixes/12925-glm-stream-buffer-arity.md b/changelog.d/fixes/12925-glm-stream-buffer-arity.md deleted file mode 100644 index 3267f4f7cb..0000000000 --- a/changelog.d/fixes/12925-glm-stream-buffer-arity.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(stream):** the 64 KB stream buffer GLM asks for is honoured instead of dropped, and the type error it caused no longer fails the API Route Typecheck gate on every open PR ([#12925](https://github.com/diegosouzapw/OmniRoute/pull/12925)) diff --git a/changelog.d/fixes/12930-pii-nested-tool-result.md b/changelog.d/fixes/12930-pii-nested-tool-result.md deleted file mode 100644 index 2309f2e9f1..0000000000 --- a/changelog.d/fixes/12930-pii-nested-tool-result.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(guardrails):** mask PII inside a `tool_result`'s nested content array, which the masker walked past while redacting its sibling block ([#12930](https://github.com/diegosouzapw/OmniRoute/pull/12930)) diff --git a/changelog.d/fixes/12937-radar-silent-cells.md b/changelog.d/fixes/12937-radar-silent-cells.md deleted file mode 100644 index 64072b9eb8..0000000000 --- a/changelog.d/fixes/12937-radar-silent-cells.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(dashboard):** the Radar catalog table no longer leaves absent data unexplained — empty limits, unknown context windows, and unreported capabilities each explain themselves on hover, and a new check keeps it that way ([#12937](https://github.com/diegosouzapw/OmniRoute/pull/12937)) — thanks @maxmad64bis diff --git a/changelog.d/fixes/12941-opencode-geo-block-rotation.md b/changelog.d/fixes/12941-opencode-geo-block-rotation.md deleted file mode 100644 index e07fa5c7da..0000000000 --- a/changelog.d/fixes/12941-opencode-geo-block-rotation.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(sse):** geo-blocked opencode requests rotate to the next account proxy instead of failing, so one refused egress no longer aborts the whole chain ([#12941](https://github.com/diegosouzapw/OmniRoute/pull/12941)) — thanks @maxmad64bis diff --git a/changelog.d/fixes/12951-codex-scope-cooldown-clear.md b/changelog.d/fixes/12951-codex-scope-cooldown-clear.md deleted file mode 100644 index edc1d4a64c..0000000000 --- a/changelog.d/fixes/12951-codex-scope-cooldown-clear.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(codex):** dashboard "clear cooldown" and the CAS recovery path now drop nested `codexScopeRateLimitedUntil` maps in the same write that nulls `rate_limited_until`, and fresh quota snapshots with headroom lift fallback-sourced scope cooldowns parked by quota preflight ([#12817](https://github.com/diegosouzapw/OmniRoute/issues/12817), [#12860](https://github.com/diegosouzapw/OmniRoute/issues/12860), [#12951](https://github.com/diegosouzapw/OmniRoute/pull/12951)) The dashboard reset-credit button (`consumeCodexResetCredit`) now exercises that same snapshot path after a successful redeem, so a filled quota bar is enough to unpark a leftover fallback Codex child without another manual clear. diff --git a/changelog.d/fixes/12960-sqljs-wasm-global-path.md b/changelog.d/fixes/12960-sqljs-wasm-global-path.md deleted file mode 100644 index a77ef9a9bf..0000000000 --- a/changelog.d/fixes/12960-sqljs-wasm-global-path.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(db):** resolve `sql-wasm.wasm` across global npm install and hoisted layouts, ensuring OmniRoute can boot cleanly on Node 24 when native `better-sqlite3` is uncompiled. diff --git a/changelog.d/fixes/12969-batches-delete-completed-ownership.md b/changelog.d/fixes/12969-batches-delete-completed-ownership.md deleted file mode 100644 index e602446524..0000000000 --- a/changelog.d/fixes/12969-batches-delete-completed-ownership.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(api):** `DELETE /v1/batches/delete-completed` now sweeps only the calling API key's own completed batches (batches with no owner stay out of a key-scoped sweep on purpose), with an explicit instance-wide mode reserved for authenticated dashboard sessions, a 401 for a presented key that is unknown, revoked, deactivated, banned or expired (never falling through to the session branch), audit logging of every sweep, a sanitized 500 on failure, an owner-scoped file half (a key-scoped sweep never nulls a file another tenant owns) and an atomic sweep — chunked in 200-batch transactions in instance mode — so a mid-way error never leaves a batch pointing at a nulled file (GHSA-wvxc-jp3v-5mg5) ([#12969](https://github.com/diegosouzapw/OmniRoute/pull/12969)) diff --git a/changelog.d/fixes/12972-quota-weighted-credit-exhaustion.md b/changelog.d/fixes/12972-quota-weighted-credit-exhaustion.md deleted file mode 100644 index 094a9cd7ee..0000000000 --- a/changelog.d/fixes/12972-quota-weighted-credit-exhaustion.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(combo):** quota-weighted routing stops drawing on an out-of-credit connection — a 402 now invalidates the stored quota snapshot instead of leaving its stale remaining percentage in place, and a snapshot older than 10 minutes no longer counts as confident headroom for the primary pool ([#12972](https://github.com/diegosouzapw/OmniRoute/pull/12972)) — thanks @HouMinXi diff --git a/changelog.d/fixes/12974-custom-vision-advertised-alias.md b/changelog.d/fixes/12974-custom-vision-advertised-alias.md deleted file mode 100644 index 7d86bc2937..0000000000 --- a/changelog.d/fixes/12974-custom-vision-advertised-alias.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(vision):** Custom Models with "Vision capable" checked no longer have image requests swapped to `glm/glm-4.6v` when the client sends the advertised alias (`vllm/path/...`) or the bare path-shaped id — Vision Bridge now matches the stored override for all three id forms ([#12758](https://github.com/diegosouzapw/OmniRoute/issues/12758)) diff --git a/changelog.d/fixes/12975-opencode-transient-5xx-rotation.md b/changelog.d/fixes/12975-opencode-transient-5xx-rotation.md deleted file mode 100644 index 36c2b21012..0000000000 --- a/changelog.d/fixes/12975-opencode-transient-5xx-rotation.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(sse):** transient opencode upstream failures rotate to the next account proxy instead of failing, so one flapping egress no longer aborts the whole chain ([#12975](https://github.com/diegosouzapw/OmniRoute/pull/12975)) — thanks @maxmad64bis diff --git a/changelog.d/fixes/12981-azure-generation-range.md b/changelog.d/fixes/12981-azure-generation-range.md deleted file mode 100644 index 2aca35be74..0000000000 --- a/changelog.d/fixes/12981-azure-generation-range.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(azure):** Deployments from GPT-6 onward now send `max_completion_tokens` instead of `max_tokens`, which Azure rejects with HTTP 400. The rule matched a literal `gpt-5`, so each new generation arrived broken; it now matches the generation range, while `gpt-35-turbo` still keeps `max_tokens`. diff --git a/changelog.d/fixes/13011-memory-pressure-self-heal.md b/changelog.d/fixes/13011-memory-pressure-self-heal.md deleted file mode 100644 index 00300f6750..0000000000 --- a/changelog.d/fixes/13011-memory-pressure-self-heal.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(db):** add an opt-in self-restart circuit for sustained critical memory pressure, gate post-cleanup VACUUM behind a minimum freed-rows threshold, and checkpoint the SQLite WAL every 5 minutes with a size guard that escalates to TRUNCATE, so a growing WAL can no longer stall the event loop into a full outage. diff --git a/changelog.d/fixes/13017-explicit-inactive-probe.md b/changelog.d/fixes/13017-explicit-inactive-probe.md deleted file mode 100644 index f3179991bd..0000000000 --- a/changelog.d/fixes/13017-explicit-inactive-probe.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(auth):** an explicit connection pin may probe a quota-disabled row once and re-enable it on success ([#12874](https://github.com/diegosouzapw/OmniRoute/issues/12874)) ([#13017](https://github.com/diegosouzapw/OmniRoute/pull/13017)) diff --git a/changelog.d/fixes/13038-fallback-attempts-chat.md b/changelog.d/fixes/13038-fallback-attempts-chat.md deleted file mode 100644 index 2b93d009b5..0000000000 --- a/changelog.d/fixes/13038-fallback-attempts-chat.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(api):** thread `X-OmniRoute-Fallback-Attempts` through combo chat completions so streaming and non-streaming responses report how many prior legs were attempted ([#13038](https://github.com/diegosouzapw/OmniRoute/pull/13038)) diff --git a/changelog.d/fixes/13043-non-streaming-failure-classification.md b/changelog.d/fixes/13043-non-streaming-failure-classification.md deleted file mode 100644 index e6774f9825..0000000000 --- a/changelog.d/fixes/13043-non-streaming-failure-classification.md +++ /dev/null @@ -1 +0,0 @@ -- Restore provider failure classification and credential refresh on non-streaming requests: classify non-2xx failures to lock models on per-model quota exhaustion, update connection rate limits from headers and body, and pass credential refresh handlers to pipeline execution so 401 tokens can be refreshed and retried (#13043). diff --git a/changelog.d/fixes/13050-responses-websearch-sse.md b/changelog.d/fixes/13050-responses-websearch-sse.md deleted file mode 100644 index 884f25b5ef..0000000000 --- a/changelog.d/fixes/13050-responses-websearch-sse.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(responses):** wrap forced-non-streaming web_search fallback JSON as Responses SSE so Codex still sees `response.completed` ([#13050](https://github.com/diegosouzapw/OmniRoute/pull/13050)) diff --git a/changelog.d/fixes/13055-antigravity-thought-token-usage.md b/changelog.d/fixes/13055-antigravity-thought-token-usage.md deleted file mode 100644 index f19f31a0a5..0000000000 --- a/changelog.d/fixes/13055-antigravity-thought-token-usage.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(antigravity):** Preserve upstream thought-token usage in normalized completion and reasoning token counts ([#13055](https://github.com/diegosouzapw/OmniRoute/pull/13055)) — thanks @pacocartones diff --git a/changelog.d/fixes/13094-deepseek-pow-slot-leak.md b/changelog.d/fixes/13094-deepseek-pow-slot-leak.md deleted file mode 100644 index ab26d28c6f..0000000000 --- a/changelog.d/fixes/13094-deepseek-pow-slot-leak.md +++ /dev/null @@ -1 +0,0 @@ -- Fix a concurrency-slot leak in the DeepSeek PoW solver: a worker that failed to spawn (for example a missing worker script) never released its slot, so `MAX_CONCURRENT_WORKERS` failures disabled the solver until restart. diff --git a/changelog.d/fixes/13095-acp-buffer-cap.md b/changelog.d/fixes/13095-acp-buffer-cap.md deleted file mode 100644 index 61473dca6d..0000000000 --- a/changelog.d/fixes/13095-acp-buffer-cap.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(acp):** bound the ACP session output buffers — `stdoutBuffer` and `stderrBuffer` now cap at 1 MiB keeping the most recent output behind a visible `[...output truncated...]` marker, and `stderrBuffer` is reset per prompt instead of accumulating for the lifetime of the session. diff --git a/changelog.d/fixes/13095-acp-sendprompt-listener-leak.md b/changelog.d/fixes/13095-acp-sendprompt-listener-leak.md deleted file mode 100644 index 56473cc8c2..0000000000 --- a/changelog.d/fixes/13095-acp-sendprompt-listener-leak.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(acp):** release the `stdout`/`exit` listeners and the idle timer that a `sendPrompt` timeout used to leave attached to the `acpManager` singleton, and drop sessions that exited on their own from the session map instead of keeping them forever. diff --git a/changelog.d/fixes/13101-sanitizer-tool-result-carrier.md b/changelog.d/fixes/13101-sanitizer-tool-result-carrier.md deleted file mode 100644 index 9ee2cde406..0000000000 --- a/changelog.d/fixes/13101-sanitizer-tool-result-carrier.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(security):** the prompt-injection and PII scanners now read the text a `tool_result` block carries on `content` (string or nested block list), in messages and in system blocks, so tool output is judged by the same rules as user text ([#13101](https://github.com/diegosouzapw/OmniRoute/pull/13101)) diff --git a/changelog.d/fixes/13103-badge-sse-aborted-signal.md b/changelog.d/fixes/13103-badge-sse-aborted-signal.md deleted file mode 100644 index 000e3ddb45..0000000000 --- a/changelog.d/fixes/13103-badge-sse-aborted-signal.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(gamification):** close the badge notification SSE stream when the request signal is already aborted before the stream starts — a client that disconnects while the route is still awaiting auth used to leave both the 2s unlock poll and the 15s heartbeat running for the lifetime of the process. diff --git a/changelog.d/fixes/13104-injection-scan-window.md b/changelog.d/fixes/13104-injection-scan-window.md deleted file mode 100644 index 00ad889c51..0000000000 --- a/changelog.d/fixes/13104-injection-scan-window.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(security):** the prompt-injection scan now spends its 16 KB budget on both ends of the request instead of the first 16 KB only, so `system`, `instructions`, `query`, `documents` and the newest turns are no longer hidden behind one long message ([#13104](https://github.com/diegosouzapw/OmniRoute/pull/13104)) diff --git a/changelog.d/fixes/13107-volcengine-console-cookie.md b/changelog.d/fixes/13107-volcengine-console-cookie.md deleted file mode 100644 index c9a24dcbe8..0000000000 --- a/changelog.d/fixes/13107-volcengine-console-cookie.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(dashboard):** expose the Volcano Ark console cookie on quota scraping and unwrap connect-error objects so the dashboard shows the upstream message diff --git a/changelog.d/fixes/13108-nodesqlite-process-listener-leak.md b/changelog.d/fixes/13108-nodesqlite-process-listener-leak.md deleted file mode 100644 index 93875c856f..0000000000 --- a/changelog.d/fixes/13108-nodesqlite-process-listener-leak.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(db):** release the `beforeExit`/`SIGINT`/`SIGTERM` handlers when a `node:sqlite` adapter closes, so a closed adapter and its database handle are no longer pinned to `process` for the lifetime of the run — the same treatment #7494 gave the sql.js adapter. diff --git a/changelog.d/fixes/13110-schema-slot-keys.md b/changelog.d/fixes/13110-schema-slot-keys.md deleted file mode 100644 index fb91255e94..0000000000 --- a/changelog.d/fixes/13110-schema-slot-keys.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(translator):** `contentSchema` and `unevaluatedItems` are now treated as subschema positions by the tool-schema sanitizer, so a truncation placeholder in either is replaced with a permissive schema instead of being forwarded as a string ([#13110](https://github.com/diegosouzapw/OmniRoute/pull/13110)) diff --git a/changelog.d/fixes/13113-logstream-timer-leak.md b/changelog.d/fixes/13113-logstream-timer-leak.md deleted file mode 100644 index 6bd6ced117..0000000000 --- a/changelog.d/fixes/13113-logstream-timer-leak.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(cli-helper):** clear the `createLogStream` timeout on the abort path — `stop()` aborts the in-flight fetch and returned through the `signal.aborted` branch, which skipped `clearTimeout` and left an armed timer per stopped stream. The stream reader is now also cancelled when the read loop exits early. diff --git a/changelog.d/fixes/13136-docs-sensitive-catalog.md b/changelog.d/fixes/13136-docs-sensitive-catalog.md deleted file mode 100644 index 573d471a3a..0000000000 --- a/changelog.d/fixes/13136-docs-sensitive-catalog.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(docs):** drop TLS-impersonation, MITM-decrypt, supply-chain attestation, and XOR-mask writeups from the public `/docs` catalog and Docker image. Files stay in git for engineers; operators who need them open the repo, not the website. diff --git a/changelog.d/fixes/13141-breaker-epoch-cooldown.md b/changelog.d/fixes/13141-breaker-epoch-cooldown.md deleted file mode 100644 index d66876f65e..0000000000 --- a/changelog.d/fixes/13141-breaker-epoch-cooldown.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(combo):** parse numeric-epoch `rate_limited_until` in the combo cooldown read path ([#13141](https://github.com/diegosouzapw/OmniRoute/pull/13141)) — thanks @maxmad64bis diff --git a/changelog.d/fixes/13142-plugin-v2-model-api-url.md b/changelog.d/fixes/13142-plugin-v2-model-api-url.md deleted file mode 100644 index 34f1c9a172..0000000000 --- a/changelog.d/fixes/13142-plugin-v2-model-api-url.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(opencode):** both OpenCode plugins now reject a gateway address typed without `http://` at configuration time, instead of publishing every model with an api url no client can call, and the v2 plugin no longer publishes a model card whose api url is blank or relative ([#13142](https://github.com/diegosouzapw/OmniRoute/pull/13142)) — thanks @maxmad64bis diff --git a/changelog.d/fixes/13146-opencode-400-model-lock.md b/changelog.d/fixes/13146-opencode-400-model-lock.md deleted file mode 100644 index 3666287873..0000000000 --- a/changelog.d/fixes/13146-opencode-400-model-lock.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(providers):** lock opencode model on upstream 400 model-unavailable ([#13146](https://github.com/diegosouzapw/OmniRoute/pull/13146)) — thanks @maxmad64bis diff --git a/changelog.d/fixes/13147-bodies-first-artifact.md b/changelog.d/fixes/13147-bodies-first-artifact.md deleted file mode 100644 index f197b0f657..0000000000 --- a/changelog.d/fixes/13147-bodies-first-artifact.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(logging):** keep the provider exchange rather than the raw client bodies when a call log exceeds its size budget, and show that recovered payload in the request-detail panel instead of replacing it with the stored response body ([#13147](https://github.com/diegosouzapw/OmniRoute/pull/13147)) — thanks @maxmad64bis diff --git a/changelog.d/fixes/13152-traffic-inspector-ws-subscriber-leak.md b/changelog.d/fixes/13152-traffic-inspector-ws-subscriber-leak.md deleted file mode 100644 index 4c5249f4fd..0000000000 --- a/changelog.d/fixes/13152-traffic-inspector-ws-subscriber-leak.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(traffic-inspector):** the WebSocket route no longer leaks a traffic-buffer subscriber and a 30s ping timer when the client socket is already closed at handler time — listeners are attached before any resource is acquired, a destroyed socket bails out early, and the ping interval stops on a dead socket where `write()` never throws ([#13155](https://github.com/diegosouzapw/OmniRoute/pull/13155)) diff --git a/changelog.d/fixes/13165-telegram-keycache-unbounded.md b/changelog.d/fixes/13165-telegram-keycache-unbounded.md deleted file mode 100644 index 2672fb93be..0000000000 --- a/changelog.d/fixes/13165-telegram-keycache-unbounded.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(telegram):** bound the per-user API key cache in the Telegram chat proxy so a burst of distinct chat ids can no longer grow the process heap without limit ([#13165](https://github.com/diegosouzapw/OmniRoute/issues/13165)) diff --git a/changelog.d/fixes/13169-jsonbody-sniff-reader-leak.md b/changelog.d/fixes/13169-jsonbody-sniff-reader-leak.md deleted file mode 100644 index b0ad5629c3..0000000000 --- a/changelog.d/fixes/13169-jsonbody-sniff-reader-leak.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(stream):** cancel the upstream response body when the JSON-to-SSE sniff unwinds on a body timeout, so a stalled upstream no longer pins the connection ([#13169](https://github.com/diegosouzapw/OmniRoute/issues/13169)) diff --git a/changelog.d/fixes/13172-telegram-webhook-secret.md b/changelog.d/fixes/13172-telegram-webhook-secret.md deleted file mode 100644 index 49f14b55fb..0000000000 --- a/changelog.d/fixes/13172-telegram-webhook-secret.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(telegram):** authenticate webhook deliveries with Telegram's `secret_token` so an unauthenticated caller can no longer mint API keys or spend upstream quota ([#13172](https://github.com/diegosouzapw/OmniRoute/issues/13172)) diff --git a/changelog.d/fixes/13195-gemini-38-output-spec.md b/changelog.d/fixes/13195-gemini-38-output-spec.md deleted file mode 100644 index ee4772c47e..0000000000 --- a/changelog.d/fixes/13195-gemini-38-output-spec.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(models):** give discoverable Gemini 3.8 Flash ids their own 65536 output spec so Antigravity no longer clamps them to 16384 ([#13195](https://github.com/diegosouzapw/OmniRoute/pull/13195)) — thanks @HouMinXi diff --git a/changelog.d/fixes/13197-deprecated-provider-purge.md b/changelog.d/fixes/13197-deprecated-provider-purge.md deleted file mode 100644 index aa7f960b32..0000000000 --- a/changelog.d/fixes/13197-deprecated-provider-purge.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(dashboard):** leftover catalog-removed provider rows (gemini-cli) can be listed and purged from the providers page ([#13067](https://github.com/diegosouzapw/OmniRoute/issues/13067)) ([#13197](https://github.com/diegosouzapw/OmniRoute/pull/13197)) diff --git a/changelog.d/fixes/13298-dashboard-session-authenticated-claim.md b/changelog.d/fixes/13298-dashboard-session-authenticated-claim.md deleted file mode 100644 index c84635053a..0000000000 --- a/changelog.d/fixes/13298-dashboard-session-authenticated-claim.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(auth):** a dashboard session now requires the `authenticated: true` claim that login, OIDC and the session refresh already emit — a JWT merely signed with `JWT_SECRET` (for example the Cursor CLI passthrough token, which any API-key holder can obtain) no longer verifies as the `auth_token` cookie on any route, the WebSocket handshake or the live server; existing sessions keep working ([#13298](https://github.com/diegosouzapw/OmniRoute/issues/13298)) diff --git a/changelog.d/fixes/13684-batches-sweep-policy-and-lock.md b/changelog.d/fixes/13684-batches-sweep-policy-and-lock.md deleted file mode 100644 index 746eb6db91..0000000000 --- a/changelog.d/fixes/13684-batches-sweep-policy-and-lock.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(api):** `DELETE /v1/batches/delete-completed` now applies the caller's API-key policy (endpoint allowlist, schedule, usage cap, rate limit) like every other `/v1` route, commits the key-scoped sweep per 200-batch chunk instead of holding one write lock for the whole sweep, refuses to loop without progress, and rejects a scope that names both a key and `allTenants` ([#13684](https://github.com/diegosouzapw/OmniRoute/pull/13684)) diff --git a/changelog.d/fixes/chatcore-legresult-narrowing.md b/changelog.d/fixes/chatcore-legresult-narrowing.md deleted file mode 100644 index ae8c7f084d..0000000000 --- a/changelog.d/fixes/chatcore-legresult-narrowing.md +++ /dev/null @@ -1 +0,0 @@ -- Restore the API-route typecheck gate: the non-streaming leg result lost its discriminated-union narrowing after the server-owned tool loop reassignment, producing 13 new TS2339 diagnostics in `chatCore.ts`. diff --git a/changelog.d/fixes/chatcore-nonstreaming-regressions.md b/changelog.d/fixes/chatcore-nonstreaming-regressions.md deleted file mode 100644 index b11fdc89e8..0000000000 --- a/changelog.d/fixes/chatcore-nonstreaming-regressions.md +++ /dev/null @@ -1 +0,0 @@ -- Restore four non-streaming behaviours the server-owned tool loop refactor dropped: client aborts map to 499 with the fixed `Request aborted` message, an aborted request no longer logs a synthetic `clientResponse`, Claude prompt-cache telemetry is recorded again, and a body that cannot be canonicalized no longer throws when the tool loop is off. diff --git a/changelog.d/fixes/cli-skill-parser-addargument.md b/changelog.d/fixes/cli-skill-parser-addargument.md deleted file mode 100644 index cd99c08422..0000000000 --- a/changelog.d/fixes/cli-skill-parser-addargument.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(skills):** The CLI registry parser now reads positionals declared with `.addArgument()`, not only those written inline in `.command()`. `tunnel create [type]` was being published as `tunnel create`, so the agent-skills sync gate reported drift on every branch and regenerating would have deleted the argument. diff --git a/changelog.d/fixes/combo-test-probe-timeout.md b/changelog.d/fixes/combo-test-probe-timeout.md deleted file mode 100644 index b77c01a881..0000000000 --- a/changelog.d/fixes/combo-test-probe-timeout.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(combos):** dashboard combo test uses a short prompt, serial probes, and a 60s timeout so reasoning models and rate-limited free pools do not fail the health check diff --git a/changelog.d/fixes/compression-pool-idle-terminate.md b/changelog.d/fixes/compression-pool-idle-terminate.md deleted file mode 100644 index d2a280f1ba..0000000000 --- a/changelog.d/fixes/compression-pool-idle-terminate.md +++ /dev/null @@ -1 +0,0 @@ -- fix(compression): terminate idle worker threads on eviction so long-running instances stop leaking OS threads and MessagePorts diff --git a/changelog.d/fixes/google-key-redaction-length.md b/changelog.d/fixes/google-key-redaction-length.md deleted file mode 100644 index e212604119..0000000000 --- a/changelog.d/fixes/google-key-redaction-length.md +++ /dev/null @@ -1 +0,0 @@ -- Redact Google API keys of any length in error bodies: the pattern required exactly 39 characters, so shorter or longer `AIza…` credentials passed through unredacted. diff --git a/changelog.d/fixes/grok-cli-shared-wallet-402.md b/changelog.d/fixes/grok-cli-shared-wallet-402.md deleted file mode 100644 index 5559902c6b..0000000000 --- a/changelog.d/fixes/grok-cli-shared-wallet-402.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(grok-cli):** a 402 "Grok Build usage balance exhausted" parks that Grok login as out of credit (Grok Build CLI, grok.com cookie, and xAI OAuth share the weekly pool). Combo routing then tries the next login instead of locking the model for every account in the pool diff --git a/changelog.d/fixes/llmlingua-worker-spawn.md b/changelog.d/fixes/llmlingua-worker-spawn.md deleted file mode 100644 index 865f19a32e..0000000000 --- a/changelog.d/fixes/llmlingua-worker-spawn.md +++ /dev/null @@ -1 +0,0 @@ -- fix(compression): spawn the LLMLingua worker with a file URL object so compression actually runs instead of silently failing open on Node diff --git a/changelog.d/fixes/local-test-concurrency.md b/changelog.d/fixes/local-test-concurrency.md deleted file mode 100644 index 70a46e3984..0000000000 --- a/changelog.d/fixes/local-test-concurrency.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(test):** run the local `test` and `test:unit` scripts at concurrency 4 so a full-suite run no longer exhausts the machine's commit charge and kills unrelated processes diff --git a/changelog.d/fixes/monitoring-health-failed-connection-ids.md b/changelog.d/fixes/monitoring-health-failed-connection-ids.md deleted file mode 100644 index eaa7ff3cc8..0000000000 --- a/changelog.d/fixes/monitoring-health-failed-connection-ids.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(monitoring):** `GET /api/monitoring/health` `credentialHealth` now includes a bounded `failedConnections` list (`connectionId`, `status`, sanitized `lastError`) when the probe-cache gauge `failed>0`, plus `source: probe-cache` and a cheap `staleDbNonOkCount` for sticky SQLite `test_status` on active rows. Documents that the live gauge is not `provider_connections.test_status`. diff --git a/changelog.d/fixes/openai-compatible-per-upstream-402-health.md b/changelog.d/fixes/openai-compatible-per-upstream-402-health.md deleted file mode 100644 index 6500007c91..0000000000 --- a/changelog.d/fixes/openai-compatible-per-upstream-402-health.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(resilience):** an openai-compatible multi-upstream gateway no longer marks the whole connection `credits_exhausted` when a single upstream (or the `/models` / representative-model health probe) returns 402. Quota failures stay model-scoped; true connection-wide auth failures are unchanged. Single-credential 402 key disable ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239)) is preserved. diff --git a/changelog.d/fixes/orchestration-canvas-polish.md b/changelog.d/fixes/orchestration-canvas-polish.md deleted file mode 100644 index 516f53ee5c..0000000000 --- a/changelog.d/fixes/orchestration-canvas-polish.md +++ /dev/null @@ -1,17 +0,0 @@ -- **fix(dashboard):** nine fixes on the Orchestration Canvas, all diagnosed in the Phase 2 reviews. - A source that fails now keeps the timestamp of its FIRST failure instead of being re-stamped - every poll — the stale line said "since the last poll" no matter how long the source had been - down, and the churn also defeated the snapshot's stable identity (it serializes the source list), - so the canvas re-rendered on every tick while anything was broken. A source that HAD data and - only then started failing is flagged too: previously only a source with no node at all got the - warning, so a source that went down mid-session kept a healthy-looking node forever. The rest are - pointwise: clicking a filter chip cancels the pending search debounce (left armed, it fired - ~300ms later and silently reverted the chip); the search input carries an accessible name; - `?state=running, failed` parses like the unpadded form instead of dropping the padded value; the - CSV toggle helper is defined once in `model/urlParams.ts` rather than twice; the agents tab tells - "nothing running" apart from "the filter matched nothing", offering a clear-filters button - instead of setup links that would be wrong advice there; edges stop emitting SMIL particles above - 40 simultaneously active edges, keeping the colored stroke; and the drawer's error banner clears - when a retried action succeeds. - - Closes #12392 diff --git a/changelog.d/fixes/orchestration-fase3-eu-locales.md b/changelog.d/fixes/orchestration-fase3-eu-locales.md deleted file mode 100644 index 8d740f60cc..0000000000 --- a/changelog.d/fixes/orchestration-fase3-eu-locales.md +++ /dev/null @@ -1,9 +0,0 @@ -- **fix(i18n):** the nine locales added with the EU-language batch (Greek, Estonian, Irish, - Croatian, Lithuanian, Latvian, Maltese, Slovenian, Serbian) were missing the eleven - Orchestration Canvas keys that Phase 3 introduced, so the compare-runs panel and the - "no runs match these filters" empty state fell back to English in those languages - (`deepMergeFallback` substitutes English for an absent key, so nothing rendered blank — - it rendered untranslated). The coverage gate does not catch this: it enforces an 80% floor - per locale, and eleven missing keys out of ~13,000 leaves coverage at 99.9%. Translated for - real in each language, calibrated against the wording each file already uses for "run", - "filter" and "skill". diff --git a/changelog.d/fixes/orchestration-followups.md b/changelog.d/fixes/orchestration-followups.md deleted file mode 100644 index 6502c7f40b..0000000000 --- a/changelog.d/fixes/orchestration-followups.md +++ /dev/null @@ -1,15 +0,0 @@ -- **fix(dashboard):** five follow-ups from the Phase 2 Orchestration Canvas review. The History - drawer now shows the memory section for runs that already left the live TTL window: the - persisted `memory_hits` event is parsed into `metadata.memoryHits` with the same defensive - validation the drawer applies, and — because that event is observability rather than a state - transition — it no longer leaks into the timeline, where it had been inheriting the task's - state and rendering as a duplicate transition. The A2A memory recall runs against its own 1.5s - deadline instead of inheriting the memory backend's 30s one; overshooting degrades exactly like - any other recall failure (no hits, task proceeds), and the timer is cleared on both paths. - Repeating a Conductor run carries its `requirements.cli`/`requirements.model` forward, so the - new run is pinned to the same runner profile and model rather than drifting to whatever the - fleet picks. A successful repeat from the Agents tab now focuses the run it created, instead of - leaving the operator on the finished one. And the auth test for `POST /api/conductor/tasks` - moved into the shared `ROUTES` array rather than restating the pattern. - - Closes #12639 diff --git a/changelog.d/fixes/plugin-sigkill-listener-leak.md b/changelog.d/fixes/plugin-sigkill-listener-leak.md deleted file mode 100644 index a4baa47066..0000000000 --- a/changelog.d/fixes/plugin-sigkill-listener-leak.md +++ /dev/null @@ -1 +0,0 @@ -- fix(plugins): stop leaking an exit listener per plugin hook timeout, which triggered MaxListenersExceededWarning on plugins that ignore SIGTERM diff --git a/changelog.d/fixes/provider-node-null-quota-reset.md b/changelog.d/fixes/provider-node-null-quota-reset.md deleted file mode 100644 index a8bdd9aeae..0000000000 --- a/changelog.d/fixes/provider-node-null-quota-reset.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(validation):** Provider node edits no longer fail with a generic "Invalid request" when the optional daily-quota reset fields are left blank. The dashboard sends `dailyQuotaResetTimezone` and `dailyQuotaResetHour` as `null`, and only the hour accepted it. ([#13066](https://github.com/diegosouzapw/OmniRoute/issues/13066)) diff --git a/changelog.d/fixes/quota-routing-eligibility-and-agy-threshold.md b/changelog.d/fixes/quota-routing-eligibility-and-agy-threshold.md deleted file mode 100644 index bc664be676..0000000000 --- a/changelog.d/fixes/quota-routing-eligibility-and-agy-threshold.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(combo):** quota-aware expansion drops banned, inactive, missing, and wrong-provider connections before quota fetch or model dispatch; pins and allowlists stay selectors, not a bypass. Antigravity automatic exhaustion now requires a reported zero remaining, so a positive balance below 1% stays eligible. diff --git a/changelog.d/fixes/quota-weighted-test-clock.md b/changelog.d/fixes/quota-weighted-test-clock.md deleted file mode 100644 index eb33e1ba05..0000000000 --- a/changelog.d/fixes/quota-weighted-test-clock.md +++ /dev/null @@ -1 +0,0 @@ -- Fix an intermittent failure in the quota-weighted routing test suite: scores are a function of `Date.now()`, so peers with identical quota scored microseconds apart never tied and swapped order. diff --git a/changelog.d/fixes/volcengine-quota-binding.md b/changelog.d/fixes/volcengine-quota-binding.md deleted file mode 100644 index edd9e7484e..0000000000 --- a/changelog.d/fixes/volcengine-quota-binding.md +++ /dev/null @@ -1 +0,0 @@ -- fix(volcengine): resolve quota window mapping gaps, safe multi-connection console binding, and response cookie sanitization diff --git a/changelog.d/fixes/webdav-test-windows-path.md b/changelog.d/fixes/webdav-test-windows-path.md deleted file mode 100644 index 2ba7400611..0000000000 --- a/changelog.d/fixes/webdav-test-windows-path.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(test):** resolve the WebDAV handler path with `fileURLToPath` so the suite's 37 WebDAV tests run on Windows instead of failing with a doubled `C:\C:\` drive prefix diff --git a/changelog.d/maintenance/0000-basered-agent-skills-cli-tunnel.md b/changelog.d/maintenance/0000-basered-agent-skills-cli-tunnel.md deleted file mode 100644 index 48d064e67a..0000000000 --- a/changelog.d/maintenance/0000-basered-agent-skills-cli-tunnel.md +++ /dev/null @@ -1 +0,0 @@ -- Clear the `release/v3.8.51` `check:agent-skills-sync` base-red: regenerate `skills/cli-tunnel/SKILL.md` so the `tunnel create [type]` positional that #13009 taught the generator to read is reflected in the committed skill. diff --git a/changelog.d/maintenance/0000-basered-autocombo-providercandidate.md b/changelog.d/maintenance/0000-basered-autocombo-providercandidate.md deleted file mode 100644 index 11a9ad3b6d..0000000000 --- a/changelog.d/maintenance/0000-basered-autocombo-providercandidate.md +++ /dev/null @@ -1 +0,0 @@ -- Clear the `release/v3.8.51` typecheck base-red from #12731: the new `Mode pack ranking gates` candidates in `open-sse/services/autoCombo/__tests__/autoCombo.test.ts` omitted the required `provider`, `model` and `errorRate` fields of `ProviderCandidate`, failing `check:open-sse-typecheck` (and with it `Fast Quality Gates`) on every open PR. diff --git a/changelog.d/maintenance/0000-basered-docs-provider-count-358.md b/changelog.d/maintenance/0000-basered-docs-provider-count-358.md deleted file mode 100644 index 178355f99d..0000000000 --- a/changelog.d/maintenance/0000-basered-docs-provider-count-358.md +++ /dev/null @@ -1 +0,0 @@ -- Clear the `release/v3.8.51` docs-sync base-red: EURouter (#13025) and GreenPT (#13024) took the live provider count to 358, leaving 7 STRICT drifts (`PROVIDER_REFERENCE.md`, 4 diagrams, 2 tier-flow images) plus the `AGENTS.md` / `llm.txt` / `package.json` count claims stale. diff --git a/changelog.d/maintenance/0000-catalog-12058-test-timeout.md b/changelog.d/maintenance/0000-catalog-12058-test-timeout.md deleted file mode 100644 index 67fd01c495..0000000000 --- a/changelog.d/maintenance/0000-catalog-12058-test-timeout.md +++ /dev/null @@ -1 +0,0 @@ -- **test(models):** Pin `CATALOG_BUILD_TIMEOUT_MS` in the #12058 canonical-catalog regression so a cold catalog build racing the 8s default bound no longer flakes the suite with a 500 ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) diff --git a/changelog.d/maintenance/0000-pack-policy-opencode-v2.md b/changelog.d/maintenance/0000-pack-policy-opencode-v2.md deleted file mode 100644 index 69a2210391..0000000000 --- a/changelog.d/maintenance/0000-pack-policy-opencode-v2.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(ci):** Allow `@omniroute/opencode-plugin-v2/` in the npm pack policy — [#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870) shipped the v2 plugin beside its v1 sibling without widening the allowlist, so every packed file under it read as an unexpected artifact and failed `check:pack-policy` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) diff --git a/changelog.d/maintenance/0000-release-green-pack-provenance.md b/changelog.d/maintenance/0000-release-green-pack-provenance.md deleted file mode 100644 index 78399bf0b1..0000000000 --- a/changelog.d/maintenance/0000-release-green-pack-provenance.md +++ /dev/null @@ -1 +0,0 @@ -- **fix(ci):** Point the release-green validator's pack gate at the tree it is validating — the build-provenance guard defaulted to `origin/main`, which a release branch tip can never be an ancestor of mid-cycle, so `Package artifact` reported a false red on every run and the tarball boot-smoke cascaded off it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) diff --git a/changelog.d/maintenance/12507-docs-counts-gates.md b/changelog.d/maintenance/12507-docs-counts-gates.md deleted file mode 100644 index fde6496d25..0000000000 --- a/changelog.d/maintenance/12507-docs-counts-gates.md +++ /dev/null @@ -1 +0,0 @@ -- **docs(checks):** keep doc counts honest — headings, rankings, catalog, weights, quality gate and scoring diagram now covered ([#12507](https://github.com/diegosouzapw/OmniRoute/pull/12507)) — thanks @maxmad64bis diff --git a/changelog.d/maintenance/12535-lifecycle-gate-degradation-map.md b/changelog.d/maintenance/12535-lifecycle-gate-degradation-map.md deleted file mode 100644 index f667999ac9..0000000000 --- a/changelog.d/maintenance/12535-lifecycle-gate-degradation-map.md +++ /dev/null @@ -1,7 +0,0 @@ -- **chore(lifecycle):** `check:model-lifecycle` now also diffs `DEFAULT_DEGRADATION_MAP` - (the background-task redirect table) against the vendor lifecycle snapshot, refusing a - retired id as source or target, with a table-driven unit test beside it. Three rows - whose source the vendor had retired — `claude-sonnet-4-20250514`, `gemini-3-pro-preview` - and `gpt-5.1-codex` (whose target `gpt-5.1-codex-mini` is retired too) — were dead code, - since `checkLifecycle` answers 410 before the redirect runs; they are dropped - (#12535 — thanks @pacocartones) diff --git a/changelog.d/maintenance/12542-compression-idle-terminate-test.md b/changelog.d/maintenance/12542-compression-idle-terminate-test.md deleted file mode 100644 index 744e38f854..0000000000 --- a/changelog.d/maintenance/12542-compression-idle-terminate-test.md +++ /dev/null @@ -1 +0,0 @@ -- **test(compression):** cover idle worker eviction at the resource level — the pool must call `terminate()` and must not retain the worker's `MessagePort`, complementing the `exit`-event assertion added with the fix diff --git a/changelog.d/maintenance/12945-imagegeneration-rebaseline.md b/changelog.d/maintenance/12945-imagegeneration-rebaseline.md deleted file mode 100644 index 1901370576..0000000000 --- a/changelog.d/maintenance/12945-imagegeneration-rebaseline.md +++ /dev/null @@ -1 +0,0 @@ -- **chore(quality):** raise the `imageGeneration.ts` file-size ceiling for the image-only-model guard that clears the #12945 base-red diff --git a/changelog.d/maintenance/13001-13069-eslint-regressions.md b/changelog.d/maintenance/13001-13069-eslint-regressions.md deleted file mode 100644 index edbe641c52..0000000000 --- a/changelog.d/maintenance/13001-13069-eslint-regressions.md +++ /dev/null @@ -1 +0,0 @@ -- **chore(quality):** type the combo-test route's JSON response bodies instead of casting them to `any`, drop the now-empty suppression entry, and remove the `hasPerModelQuota` import `chatCore.ts` stopped using when the failure-classification helper was extracted diff --git a/changelog.d/maintenance/13235-electron-prune-locale-root-mirrors.md b/changelog.d/maintenance/13235-electron-prune-locale-root-mirrors.md deleted file mode 100644 index c8bd46e47b..0000000000 --- a/changelog.d/maintenance/13235-electron-prune-locale-root-mirrors.md +++ /dev/null @@ -1 +0,0 @@ -- **chore(electron):** the desktop bundle no longer ships the root-level files of every translated docs mirror (`README.md`, `llm.txt`, `CLAUDE.md`, `GEMINI.md`, `CONTRIBUTING.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`) — the packaged app only reads `docs/i18n//docs/**`, which stays. Saves ~11 MB on top of the translated CHANGELOGs already pruned. (#0000) diff --git a/changelog.d/maintenance/chat-unused-vars-count-20260911.md b/changelog.d/maintenance/chat-unused-vars-count-20260911.md deleted file mode 100644 index 8f8726bf0f..0000000000 --- a/changelog.d/maintenance/chat-unused-vars-count-20260911.md +++ /dev/null @@ -1 +0,0 @@ -- **chore(quality):** retighten the `src/sse/handlers/chat.ts` unused-vars suppression count to the 8 violations that actually remain diff --git a/changelog.d/maintenance/doc-counts-171-migrations-20-strategies.md b/changelog.d/maintenance/doc-counts-171-migrations-20-strategies.md deleted file mode 100644 index d5a857d295..0000000000 --- a/changelog.d/maintenance/doc-counts-171-migrations-20-strategies.md +++ /dev/null @@ -1 +0,0 @@ -- Bring the documented counts back in line with the code: 171 migrations (was 169) and 20 routing strategies (was 19), plus the regenerated `cli-tunnel` skill reference. diff --git a/changelog.d/maintenance/doc-counts-172-migrations.md b/changelog.d/maintenance/doc-counts-172-migrations.md deleted file mode 100644 index 4d6d36e6cc..0000000000 --- a/changelog.d/maintenance/doc-counts-172-migrations.md +++ /dev/null @@ -1 +0,0 @@ -- Update the documented migration count to 172 after the call-logs provider-stats indexes landed. diff --git a/changelog.d/maintenance/gates-for-silent-debt.md b/changelog.d/maintenance/gates-for-silent-debt.md deleted file mode 100644 index 6deaa73e99..0000000000 --- a/changelog.d/maintenance/gates-for-silent-debt.md +++ /dev/null @@ -1,8 +0,0 @@ -- **chore(ci):** two gates that close the blind spots behind the exclusions above. - `check:vitest-exclusions` requires every Vitest exclusion to name a tracking issue and to - appear in `config/quality/vitest-exclusions.json` — the previous list grew to 62 files behind - a comment pointing at an issue that had been closed for a month. `check-new-key-coverage` - requires a key newly added to `en.json` to reach every locale; the existing coverage gate is a - percentage floor per locale, so eleven absent keys out of ~13,000 left it at 99.9% while a - whole feature shipped untranslated in nine languages. Both are diff-aware, so pre-existing - debt stays frozen and neither needed a migration to turn on. diff --git a/changelog.d/maintenance/layout-no-google-fonts.md b/changelog.d/maintenance/layout-no-google-fonts.md deleted file mode 100644 index cff6b2b9ef..0000000000 --- a/changelog.d/maintenance/layout-no-google-fonts.md +++ /dev/null @@ -1 +0,0 @@ -- **build:** root layout no longer loads Inter from `next/font/google`, so a production image build does not need fonts.googleapis.com ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) diff --git a/changelog.d/maintenance/llm-mirrors-new-locales-counts.md b/changelog.d/maintenance/llm-mirrors-new-locales-counts.md deleted file mode 100644 index 8441f33d4c..0000000000 --- a/changelog.d/maintenance/llm-mirrors-new-locales-counts.md +++ /dev/null @@ -1 +0,0 @@ -- **docs(i18n):** bring the eight new `llm.txt` mirrors (km, kn, ml, my, ne, or, pa, si) up to the current provider and migration counts, so `check-docs-sync` stops rejecting every commit diff --git a/changelog.d/maintenance/orchestration-status-tokens.md b/changelog.d/maintenance/orchestration-status-tokens.md deleted file mode 100644 index f5c9197d5e..0000000000 --- a/changelog.d/maintenance/orchestration-status-tokens.md +++ /dev/null @@ -1 +0,0 @@ -- **refactor(ui):** flow surfaces (home topology, combo live studio, compression cockpit/waterfall/nodes, token-health badge) express state through the theme-aware `--orch-status-*` tokens instead of fixed dark-mode hex, so green/red/amber/grey stay legible in the light theme; dark mode is byte-identical. Categorical palettes (routing-strategy hues, compression-layer pills, provider brand colors) deliberately stay hex ([#12378](https://github.com/diegosouzapw/OmniRoute/issues/12378)) diff --git a/changelog.d/maintenance/revive-excluded-vitest-tests.md b/changelog.d/maintenance/revive-excluded-vitest-tests.md deleted file mode 100644 index 5e3fa4ded4..0000000000 --- a/changelog.d/maintenance/revive-excluded-vitest-tests.md +++ /dev/null @@ -1,6 +0,0 @@ -- **chore(tests):** 51 test files that had been excluded from Vitest are running again, restoring - roughly 350 assertions to the blocking suite. Each was measured individually first: of the 62 - files parked behind the `// #8618 — pre-existing failure` comment, 51 pass against the current - tree with no source change, so the exclusions had outlived the failures they were added for. - The 11 that genuinely still fail stay excluded, but now point at a live tracker (#13204) rather - than at #8618, which was closed in August while the list it tracked kept growing. diff --git a/changelog.d/maintenance/stale-eslint-suppressions-20260911.md b/changelog.d/maintenance/stale-eslint-suppressions-20260911.md deleted file mode 100644 index 3c1c6e0903..0000000000 --- a/changelog.d/maintenance/stale-eslint-suppressions-20260911.md +++ /dev/null @@ -1 +0,0 @@ -- **chore(quality):** drop two ESLint suppression entries whose violations no longer exist, so `eslint --suppressions-location` stops rejecting every commit that touches the surrounding files diff --git a/docs/i18n/ar/CHANGELOG.md b/docs/i18n/ar/CHANGELOG.md index eebf796285..2b779acc1b 100644 --- a/docs/i18n/ar/CHANGELOG.md +++ b/docs/i18n/ar/CHANGELOG.md @@ -8,16 +8,1119 @@ ## [3.8.51] — TBD -_Living section — cycle opened at the v3.8.50 freeze (parallel-cycle model). Bullets are aggregated from `changelog.d/` fragments at each `/generate-release` phase._ +_Living section — reconciled 2026-09-15 from all cycle commits (`091589089c` → `c0f92ec98a`, 916 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 | **123** | +| 📝 Commits in the cycle | **916** | +| 🔀 Pull requests referenced | **892** | +| 📋 Changelog entries | **919** | +| 🙌 Contributors credited in entries | **121** | +| 🤖 Automated dependency commits | 18 | + +**Entries by type** + +| Type | Count | +| --- | ---: | +| 🐛 Fixes | 592 | +| ✨ Features | 131 | +| 🧹 Chore | 93 | +| 📚 Docs | 42 | +| 🧪 Tests | 32 | +| ♻️ Refactor | 8 | +| 🏗️ Build | 7 | +| ⚡ Performance | 7 | +| ⚙️ CI | 2 | +| ⏪ Reverts | 2 | +| 📦 Dependencies | 2 | +| 🔒 Security | 1 | + +### 🏆 Top 25 contributors this cycle + +_By commits in `091589089c..c0f92ec98a`, author identities consolidated via `.mailmap` and the merged PR's GitHub login. Bots excluded._ + +| # | Contributor | Commits | +| ---: | --- | ---: | +| 🥇 | diegosouzapw | 323 | +| 🥈 | Bob.Hou (@HouMinXi) | 79 | +| 🥉 | Paco Cartones (@pacocartones) | 77 | +| 4 | Dizzle (@maxmad64bis) | 49 | +| 5 | Koosha Paridehpour (@KooshaPari) | 43 | +| 6 | Nguyen Thanh Dat (@ntdatt812) | 31 | +| 7 | Ravi Tharuma (@RaviTharuma) | 26 | +| 8 | Markus Hartung (@hartmark) | 22 | +| 9 | Webman (@jonlwheat2-gif) | 22 | +| 10 | backryun | 17 | +| 11 | anhtahaylove | 15 | +| 12 | Rafa Martins (@rafacpti23) | 10 | +| 13 | Syed Raheemuddin (@raheemuddin786) | 10 | +| 14 | Nguyễn Viết Tuấn (@TheDemonTuan) | 8 | +| 15 | MumuTW | 7 | +| 16 | Paijo (@oyi77) | 7 | +| 17 | Tobias Andersen (@turbolego) | 6 | +| 18 | Bl0ck (@Bl0ck154) | 5 | +| 19 | SHANMUGAPRIYAN (@geek007git) | 5 | +| 20 | KaspaPulse | 5 | +| 21 | Abhishek Sharma (@abhisheksharma2411) | 4 | +| 22 | Abhishek Divekar (@adivekar-utexas) | 4 | +| 23 | Andrew B. (@AndrianBalanescu) | 4 | +| 24 | NoxzRCW | 4 | +| 25 | opensource-elearning | 4 | ### ✨ New Features +- **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). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- Added Google AI Studio Gemini batch text-to-speech support through `POST /v1/audio/speech`. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- 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. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(routing):** subscription-first auto groupings — `auto/subscription` routes only through plan-included connections with a documented hard-stop overage and fails closed on exhaustion, while `auto/thrifty` orders the pool `subscription → keyless → free → cheap → premium` and steps up one rung at a time as each is exhausted. Billing class comes from a curated per-connection catalog (uncurated is treated as metered, never plan-included), both reuse STRICT_ZERO_COST's per-connection verification, and a quota reading whose `resetAt` has passed is now refreshed regardless of TTL so routing returns to plan capacity as soon as it resets ([#11146](https://github.com/diegosouzapw/OmniRoute/pull/11146)) — thanks @yourspraveen +- **feat(providers):** publish a management-authenticated versioned web-session credential contract from OmniRoute's canonical browser credential metadata ([#11340](https://github.com/diegosouzapw/OmniRoute/pull/11340)) — thanks @Zartharas +- **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)) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add Xquik X search with typed results, credential validation, REST routing, and MCP selection ([#11370](https://github.com/diegosouzapw/OmniRoute/pull/11370)) — thanks @kriptoburak +- **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)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(dashboard):** surface durable exclusive managed leases in the existing Sessions view, keeping leased clients visible across idle gaps while marking connections with in-flight work as active ([#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389)) — thanks @KaspaPulse +- **build(bun):** allow Turbopack bundler flag on Bun 1.4+ with configurable Webpack fallback ([#11471](https://github.com/diegosouzapw/OmniRoute/pull/11471)) — thanks @TheDemonTuan +- feat(api): add an opt-in `modelVisibilityAllowlist`/`modelVisibilityDenylist` settings pair to curate exactly which models `/v1/models` advertises, mirrored into every `auto/*` combo candidate pool so a denied model cannot be routed to via combo selection either (#11481) ([#11997](https://github.com/diegosouzapw/OmniRoute/pull/11997)) +- **feat(rankings):** the Free Provider Rankings page shows what each provider actually served over the last 24 h. It ranked by ELO alone, which left a provider that answers every call with an error in first place; the usage data was already served by the API but never requested. A provider with too small a sample shows a dash, not a number ([#11546](https://github.com/diegosouzapw/OmniRoute/pull/11546)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **feat(guardrails):** enforce a bounded, deterministic contract for Video Bridge transcripts — 256 cues, 4096 input code units and 4 KiB UTF-8 per cue, 64 KiB total text, malformed-Unicode rejection, focus-window scoping, cross-source reconciliation with contributing-source metadata, and a structural provenance trust boundary so caller JSON can never self-assert `embedded`/`audio-bridge` provenance ([#11652](https://github.com/diegosouzapw/OmniRoute/issues/11652)) ([#12009](https://github.com/diegosouzapw/OmniRoute/pull/12009)) +- **feat(video):** orchestrate optional Video Bridge audio extraction and Audio Bridge STT behind a dual opt-in (operator setting AND per-request signal) — a new loopback-only broker `mode=audio` operation shares the frame path's exact process queue, deadline, AbortSignal, and byte budgets to extract a bounded mono 16 kHz PCM WAV from the same already-downloaded video, then reuses the existing Audio Bridge transcription boundary; provider segment timing is preserved when available and marked coarse otherwise, and every failure degrades to a visual-only-safe partial instead of throwing (#11654). ([#12012](https://github.com/diegosouzapw/OmniRoute/pull/12012)) +- Add a tenant-bound Video Bridge drill-down lifecycle on top of the existing secure cache substrate: opaque hashed handles (never raw session/video identifiers), preview/standard/detail multiresolution variants resampled on read, response pagination capped at 8 frames and 32 MiB, and a new authenticated `/api/v1/video-bridge/drilldown` consumer route that stays disabled for remote access by default and denies cross-key access with the same response as a nonexistent handle (no existence oracle). ([#12006](https://github.com/diegosouzapw/OmniRoute/pull/12006)) +- **test(video):** Add the Video Bridge FU-07/FU-09 promotion-evidence harness (#11656) — a frozen Zod manifest schema covering the 8 required scenario kinds (static scenes, rapid cuts, late facts, fades, blur, small text, close events, visual prompt injection) with a minimum of 3 repetitions per case, deterministic declarative fixture recipes (`videoBridgePromotionFixtures.ts`), a pure medians/p95 metrics aggregator, a pure FU-07/FU-09 promotion-verdict evaluator applying the ticket's exact thresholds (missing token usage always holds), a digest-only persistence layer that never retains raw media or raw model responses, and a versioned per-model promotion allowlist shipped empty with every model defaulting to `hold`. The FU-07/FU-09 promotion verdicts themselves remain HOLD — they require a real evidence run against real models on VPS 192.168.0.15. ([#12008](https://github.com/diegosouzapw/OmniRoute/pull/12008)) +- **feat(video bridge):** "embedded" transcript provenance can now be legitimately earned instead of merely asserted — a bounded, allowlisted (`mov_text`/`subrip`/`webvtt`) subtitle probe runs through the loopback-only Video Bridge broker (at most 2 streams, 10s subdeadline bounded by the request deadline, 256 KiB output, 4096-code-unit lines), normalized through a bounded, ReDoS-safe WebVTT parser and Zod-validated end to end. The adapter always resolves to an explicit `success`/`absent`/`transient_failure` outcome — a subtitle failure never breaks the visual description path, and only a fingerprint-verified broker response (never a caller-declared label) can produce embedded cues (#11659). ([#12011](https://github.com/diegosouzapw/OmniRoute/pull/12011)) +- Default new Antigravity-family connections (agy CLI imports and Antigravity OAuth connects) to model auto-sync, so live model discovery lands in the synced catalog and `/v1/models` picks up freshly released upstream models (e.g. Gemini 3.7 Flash tiers) without code changes. Existing connections keep their current setting; the per-connection dashboard toggle remains the opt-out. (#11685 — thanks @MumuTW) ([#11887](https://github.com/diegosouzapw/OmniRoute/pull/11887)) +- **feat(ui):** enable React Compiler (`reactCompiler: true` + `babel-plugin-react-compiler`) for automatic memoization at build time ([#11783](https://github.com/diegosouzapw/OmniRoute/pull/11783)) — thanks @jonlwheat2-gif +- **feat(providers):** add SeekAi (`seekai.cc`) as an OpenAI-compatible New-API gateway — catalog id `seekai` (alias `ska`), `https://seekai.cc/v1`, live `/v1/models` via `passthroughModels`, aggregator-list membership so New-API balance detection can opt in. No referral/aff codes. ([#11786](https://github.com/diegosouzapw/OmniRoute/issues/11786)) ([#12557](https://github.com/diegosouzapw/OmniRoute/pull/12557)) — thanks @HouMinXi +- **feat(zai):** add GLM-5.3-Flash Coding Plan support (1M context, 128K output, vision, `low|high|max` reasoning) and route `zai` GLM-5.3-family API-key traffic through the OpenAI-compatible Coding Plan endpoint with native thinking defaults ([#11801](https://github.com/diegosouzapw/OmniRoute/pull/11801)) — thanks @Neuron-Mr-White +- **feat(combo):** choose how combo models are ordered — manual, provider, score, or name — via a sort control in the dashboard builder, persisted in `config.modelSort` and re-applied on load and after add ([#11812](https://github.com/diegosouzapw/OmniRoute/pull/11812)) — thanks @maxmad64bis +- **feat(free):** custom models can be marked free-tier via `customModels[].isFree`; `isFreeModel()` is the first door and `hidePaidModels` respects it even for providers outside the free budget ([#11843](https://github.com/diegosouzapw/OmniRoute/pull/11843)) — thanks @maxmad64bis +- **feat(nodejs):** add `5dive` as a `configure` target — `omniroute configure 5dive` / `omniroute setup-5dive` write a 5dive auth profile that points an agent fleet's `claude` seats at OmniRoute, with the root-only write, the loopback-vs-`https` endpoint rule and the per-seat model pin handled explicitly ([#11852](https://github.com/diegosouzapw/OmniRoute/pull/11852)) — thanks @5dive-bot +- **feat(sse):** treat `max` as a first-class reasoning-effort tier and clamp per model family (GLM 5.1+/DeepSeek V4+/Kimi K3+ keep native `max`; o1/MiniMax/Grok/Muse Spark clamp to their upstream ceiling) ([#11875](https://github.com/diegosouzapw/OmniRoute/pull/11875)) — thanks @Chewji9875 +- **feat(providers):** the provider plugin manifest now advertises a `usage-fetch` capability for the 40 providers that have a wired usage/quota fetcher, so external dashboards can read it from `GET /api/v1/provider-plugin-manifest` instead of parsing `open-sse/services/usage.ts` after every release. Discovery only — no new fetcher, no quota change, and the Dashboard quota widget stays gated by `USAGE_SUPPORTED_PROVIDERS`. `USAGE_FETCHER_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/fetcherProviders.ts`) and is re-exported from `services/usage.ts`, keeping the manifest module a light leaf instead of pulling the ~490-module usage dispatcher into the manifest route. ([#11903](https://github.com/diegosouzapw/OmniRoute/pull/11903)) — thanks @maxmad64bis / @pacocartones +- **feat(plugins):** `OMNIROUTE_PLUGINS_DIR` sets the directory the runtime plugin scanner reads — and the root the plugin manager installs into — overriding the `HOME`-derived default, so a Docker/K8s deployment can point straight at its bind-mounted plugin tree instead of moving `HOME` just to relocate the scan path. An image that exports no home no longer scans `/tmp/.omniroute/plugins` in silence: the resolved directory is logged once at startup as `scanner.dir_resolved`, naming the input that won. Unset, behaviour is unchanged. Distinct from the CLI-only `OMNIROUTE_PLUGIN_PATH`, which finds `omniroute-cmd-*` command packages and never reached this scanner ([#11906](https://github.com/diegosouzapw/OmniRoute/pull/11906)) — thanks @amaleta / @pacocartones +- **feat(leases):** add an explicit owner-authenticated status action that returns only the active lease's privacy-safe configured connection and provider labels, with generation fencing and no credential or internal-id disclosure ([#11910](https://github.com/diegosouzapw/OmniRoute/pull/11910)) — thanks @KaspaPulse +- **feat(providers):** import-from-file modal shows per-row API errors and ships a downloadable CSV template ([#12071](https://github.com/diegosouzapw/OmniRoute/issues/12071)) ([#12504](https://github.com/diegosouzapw/OmniRoute/pull/12504)) — thanks @HouMinXi +- **feat(providers):** dashboard search matches connection name and `baseUrl` so imported OpenAI-compat nodes surface on the provider card ([#12108](https://github.com/diegosouzapw/OmniRoute/issues/12108)) ([#12495](https://github.com/diegosouzapw/OmniRoute/pull/12495)) — thanks @HouMinXi +- **feat(routing):** add a `score` Auto router strategy that selects the highest configured weighted score and reuses `explorationRate`. ([#12155](https://github.com/diegosouzapw/OmniRoute/pull/12155)) — thanks @jacobsparts +- Add a runtime feature flag to disable universal context handoffs globally without changing the default behavior. ([#12167](https://github.com/diegosouzapw/OmniRoute/pull/12167)) — thanks @jacobsparts +- **feat(providers):** the provider plugin manifest now also advertises a `usage-supported` capability for the 46 providers whose usage API is accepted by the server and Dashboard routes, so integrators can distinguish "the server will serve quota for this provider" from "a fetcher is wired" without reading TypeScript. Discovery only — no fetcher or quota change. `usage-fetch` resolves on id or alias (the usage dispatcher accepts both); `usage-supported` resolves on id alone, matching the runtime guard `USAGE_SUPPORTED_PROVIDERS.includes(providerId)`. `USAGE_SUPPORTED_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/supportedProviders.ts`) and is re-exported from `providers.ts`, mirroring the `fetcherProviders` leaf from #11903 and keeping the manifest a light module. ([#12214](https://github.com/diegosouzapw/OmniRoute/pull/12214)) — thanks @maxmad64bis +- **feat(rankings):** order Free Provider Rankings by what each provider actually served — `GET /api/free-provider-rankings?sortBy=reliability` and a "Most reliable first" toggle on the page. Providers with too few calls to state a success rate keep their score order below the measured ones; the default order is unchanged ([#12218](https://github.com/diegosouzapw/OmniRoute/pull/12218)). — thanks @maxmad64bis +- **perf(sse):** defer `cloneLogPayload()` in the structured SSE collector until after the `maxEvents`/`maxBytes` cap check, eliminating ~9,800 wasted `structuredClone` calls per streaming response (65–71% faster `push()`). Reducer snapshot isolation restored for OpenAI and Responses summaries ([#12241](https://github.com/diegosouzapw/OmniRoute/pull/12241)) — thanks @PauloHSOliveira ([#12243](https://github.com/diegosouzapw/OmniRoute/pull/12243)) +- **feat(usage):** Devin CLI agentic quota (Codeium seat-management GetUserStatus) and OpenRouter key limits plus account credits now surface in Provider Limits ([#12256](https://github.com/diegosouzapw/OmniRoute/pull/12256) — thanks @Neuron-Mr-White) +- **feat(settings):** persist `headroomUrl` through Settings so status/start use the operator URL instead of only `HEADROOM_URL` ([#12306](https://github.com/diegosouzapw/OmniRoute/issues/12306)) ([#12487](https://github.com/diegosouzapw/OmniRoute/pull/12487)) — thanks @HouMinXi +- **feat(auto-combo):** Auto-Combo scoring can now weigh how often a provider/model has actually succeeded. The engine already carried that number on every candidate — 24 hours of usage history behind a ten-sample floor, real-time metrics otherwise — and the scoring function never read it, while the weight table described `stability` as if it did. `reliability` (`1 - failureRate`, with the same field precedence and the same rate-bounding the speed ranking already uses, so a corrupt reading means "nothing observed" rather than "fails every call") is now a declared factor shipping at weight `0`, so routing is unchanged until an operator gives it one, and the `stability` description now matches what that factor computes ([#12317](https://github.com/diegosouzapw/OmniRoute/pull/12317)) — thanks @maxmad64bis +- **feat(routing):** With `freeAccessPolicy: "strict"`, the read-only candidate listing (`GET /v1/auto-combo/{channel}/candidates`) no longer hides the candidates the zero-cost guard excludes — the same read-only transparency the resilience filter already honours (#9133). Each candidate now carries `freeAccessExclusion` saying why it would be kept out, and it tells an exhausted allowance apart from a quota reading that never arrived or went stale, which used to look identical from the outside. Routing is unchanged: the listing reports, it never enforces. The separate `excludeTosAvoid` guard still drops its candidates without a reason; that gap is now documented rather than closed ([#12319](https://github.com/diegosouzapw/OmniRoute/pull/12319)) — thanks @maxmad64bis +- **feat(radar):** The Radar catalog table now shows two facts it was already receiving from the feed and dropping on the floor: the per-model rate limits (requests and tokens, per minute and per day) in a new column, and a badge when a provider's terms state it may train on the prompts you send. A limit of zero renders as zero rather than "rate-only" — for a ceiling those are opposite facts — and a model with no training statement gets no badge, because an absent statement is not a guarantee ([#12320](https://github.com/diegosouzapw/OmniRoute/pull/12320)) — thanks @maxmad64bis +- **feat(radar):** explain Community, single-use, contributor, supporter, recovery, abuse, offers, and privacy rules before either Radar activation action, and remove the superseded fixed-PR grant promise from every UI locale ([#12342](https://github.com/diegosouzapw/OmniRoute/pull/12342)) +- **feat(gamification):** the dashboard Profile page now shows the real daily streak — `/api/gamification/level` returns `streak: { current, longest }` (per key with `apiKeyId`, operator-wide maximum otherwise) and the streak card reads it instead of a hard-coded 0 (#2403) ([#12377](https://github.com/diegosouzapw/OmniRoute/pull/12377)) — thanks @pacocartones +- **feat(gamification):** the dashboard leaderboard now shows each API key's display name under the Name column instead of a truncated key id; `GET /api/gamification/leaderboard` attaches `name` per entry (name only — no key material), while the shared ranking helper and the federation leaderboard stay id-only — thanks @pacocartones ([#12385](https://github.com/diegosouzapw/OmniRoute/pull/12385)) +- **feat(gamification):** enforce the documented 1000 XP/min per-API-key anti-cheat rate limit on the XP award path; over-limit awards are logged and skipped instead of persisted, and the sliding window now matches the timestamp format stored in `xp_audit_log` ([#2403](https://github.com/diegosouzapw/OmniRoute/issues/2403)) ([#12390](https://github.com/diegosouzapw/OmniRoute/pull/12390)) — thanks @pacocartones +- **feat(admin):** localize the gamification anomalies page — the loading state, the Status column and the Suspicious badge now come from the `common` catalog (new `common.suspicious` key propagated to every locale) — add it to the Gamification sidebar group as `gamification-admin` (`/dashboard/gamification/admin`), and expose the loading and empty states as polite `role="status"` live regions (#12401 — thanks @pacocartones) +- **feat(providers):** skip GitHub combo members missing from the live synced catalog, and drop Copilot models that are policy-disabled or hidden from the model picker ([#12473](https://github.com/diegosouzapw/OmniRoute/pull/12473)) — thanks @RaviTharuma +- **feat(i18n):** locale-expansion tooling — `npm run i18n:add-locale` adds a language to config, dashboard, docs mirrors, CLI, README/indexes and site in one command; browser, CLI and cookie detection resolve `uk`, `fil`/`tl`, `zh-Hant` (and the retired legacy `in` → `id`) via config aliases; `config/i18n.json` now ships in the npm package so the published CLI can read it; new real-translation ratio gate (`npm run i18n:check-ratio`, advisory) with a per-locale ratchet baseline; `run-translation --adopt` restores `.i18n-state.json`; `sync-language-bars` generates 🌐 bars from config; `validate_translation.py` loads its allowlist again. Retires the duplicate `in` locale (Indonesian mislabelled as Hindi) — 42 honest locales; saved `NEXT_LOCALE=in` / `OMNIROUTE_LANG=in` keep working. (#12496) +- **feat(api):** Emit gateway-measured `tokens_per_second` (TTFT excluded) on streaming usage and `X-OmniRoute-Tokens-Per-Second` when first-token latency is known ([#12616](https://github.com/diegosouzapw/OmniRoute/issues/12616)) ([#12631](https://github.com/diegosouzapw/OmniRoute/pull/12631)) — thanks @RaviTharuma +- **feat(opencode-plugin): map gateway cost/usage/tok/s onto OpenCode inference payloads** — the official plugin copies `X-OmniRoute-Response-Cost`, token counts, `X-OmniRoute-Tokens-Per-Second` / `usage.tokens_per_second`, TTFT, and the winning `X-OmniRoute-Model` onto the JSON/SSE body OpenCode already consumes. Missing tok/s is left unset (never `tokens / latency`). (#12636) — thanks @RaviTharuma +- **feat(combo):** add combo strategy `quota-weighted`: skip exhausted accounts, then weighted-draw by leftover / in-flight load; existing conversations stay pinned until the account is empty ([#12789](https://github.com/diegosouzapw/OmniRoute/pull/12789)) — thanks @HouMinXi +- **feat(models):** Account live listings become the chat catalog source for Claude, Codex, Copilot, and AGY; public metadata only fills prices on IDs those accounts already list. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **fix(models):** Union `agy` and `antigravity` live catalogs so `agy/gemini-3.8-flash-high` is not rejected after the prefix folds to `antigravity`. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **feat(opencode):** Add `@omniroute/opencode-plugin-v2`, an opencode v2 plugin that gives a v2 install a route to an OmniRoute gateway: it publishes models, combos and auto-combos into the host catalog, refreshes them lazily behind a 300s TTL, and keeps serving the last known catalog from an on-disk snapshot when the gateway is unreachable. The v1 plugin (`@omniroute/opencode-plugin`) is untouched and keeps working as before. The plugin adapts to the host instead of assuming one shape: it reads the shape the host seeds into the catalog draft and publishes the `api` block, the pre-`api` top-level fields, or both. The gateway key comes from the host credential store when one is connected, so no secret needs to sit in a config file; a source the gateway refuses is named in a warning instead of silently degrading the catalog to raw model ids; and tool schemas bound for a Gemini model are stripped of the JSON-Schema keywords Gemini rejects, so tool calling survives that route ([#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870)) — thanks @maxmad64bis +- feat(services): show sanitized CLIProxyAPI account health from its authenticated management API without exposing credentials, file paths, or raw account metadata (#6342) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add AnySearch free web search + URL extract (webFetch) with typed results, credential validation, REST routing, and MCP selection - fallback-only ([#11690](https://github.com/diegosouzapw/OmniRoute/pull/11690)) — thanks @Xxx91n +- **feat(dashboard):** display clamped `[0, 100]%` cached input token ratio in request logs table ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) — thanks @b3nw +- **feat(catalog):** add `OMNIROUTE_DISABLE_THINKING_LEVEL_VARIANTS` feature flag to optionally filter out thinking level variants from model catalog ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) — thanks @b3nw +- **feat(dashboard):** continuously export call logs to external analytics stores. A pluggable destination registry ships the full Logs-tab record set on an hourly `JobRegistry` cron, with a persisted per-destination cursor, batched inserts, a config UI rendered from each destination's own field descriptors, and a REST layer (`/api/log-export/*`) for CRUD, a connection test, and an on-demand run. A destination can opt into `includeBodies` to also ship the request and response payloads shown in the Logs detail pane, including the client and provider views of each call; this is off by default, and payloads inherit the dashboard's PII sanitisation, secret redaction and `noLog` handling. Google BigQuery is the first destination, using a service-account key stored encrypted at rest and streaming inserts keyed by call-log id, into a table that is day-partitioned on `timestamp` and clustered on `api_key_name`, `provider`, `model` and `status`. ([#11945](https://github.com/diegosouzapw/OmniRoute/pull/11945)) — thanks @dpozimski +- **feat(providers):** add MaxAI as a signed, OpenAI-compatible provider serving its 13 paid chat models (GPT-5.6 / Luna / Thinking, Claude 5 Sonnet, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite, Grok 4.1-fast / 4.5, DeepSeek V3.2 / R1, Llama 3.3 70B) through OmniRoute's `/v1` endpoint, with per-request HMAC-SHA1→SM3→AES request signing, live model + context-window discovery from `/models/get_config`, and prompted tool-calling translated to OpenAI `tool_calls` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI vision input — image_url content parts are forwarded inline in `message_content` to the 6 vision-capable models (GPT-5.6 / Luna / Thinking, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite) ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI image generation — 6 image models (gpt-image-1, dall-e-3, flux-1-schnell/dev/pro, sd3-medium) exposed through `POST /v1/images/generations` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI document RAG — inline base64 file/document attachments are uploaded to MaxAI (content-addressed `doc_id`) and attached to the chat via `doc_list` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** browserless MaxAI onboarding — email device-pair login (`/api/providers/[id]/login`) and signed access-token refresh, so a connection can be created and kept fresh without a real browser or Google OAuth ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** per-provider TLS impersonation profile (MaxAI presents a Windows Firefox-150 client fingerprint) so its bot-sensitive endpoints accept OmniRoute traffic ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** add **Nimble** as a web-search and web-fetch provider (`nimble-search`) — `/v1/search` routes to Nimble's search API at `lite` depth (locale, freshness and include/exclude domain filters mapped onto the shared request contract), and `/v1/web/fetch` routes to Nimble Extract, which covers all four fetch formats (`markdown`, `html`, `links`, `screenshot`) from a single call. One API key serves both surfaces. ([#11620](https://github.com/diegosouzapw/OmniRoute/pull/11620)) — thanks @wildcard +- The npm publish is automatic again, through npm Trusted Publishing (OIDC): the hosted `stage-npm` job publishes with a short-lived credential minted from GitHub's id-token — no `NPM_TOKEN`, no 2FA prompt, provenance attached. `publish_mode=staged` (owner approves with 2FA) and `direct` (token) remain available on `workflow_dispatch`. ([#11931](https://github.com/diegosouzapw/OmniRoute/pull/11931)) +- **feat(providers):** Add **Opper** as an API-key gateway provider — EU-hosted AI gateway with 700+ models from 30+ providers behind one OpenAI-compatible API and one key (`OPPER_API_KEY`); model ids use `provider/model` format (e.g. `anthropic/claude-sonnet-4-6`, `openai/gpt-5`); live model catalog at `https://api.opper.ai/v3/compat/models`; entry mirrors `requesty` (same shape, `passthroughModels: true`, no static seed) ([#11629](https://github.com/diegosouzapw/OmniRoute/pull/11629)) +- **feat(dashboard):** the `/dashboard/orchestration` snapshot hook now subscribes to the `agents` WebSocket channel (`agent.task.updated`) instead of `requests` as its refetch trigger, and relaxes its background poll from 5s to 30s while that WS connection is up — falling back to the tighter 5s cadence, reprogrammed live on any connect/disconnect transition, whenever the socket is down. ([#12409](https://github.com/diegosouzapw/OmniRoute/pull/12409)) +- **feat(dashboard):** Orchestration canvas quick wins — search box plus state/source/provider filter chips with a one-click clear, and per-source collapse/expand, all reflected in the URL so a filtered/collapsed view is shareable and survives a refresh; the detail drawer gained a "copy trace JSON" action and hardened error/empty-state and accessibility handling; the Agents-tab edges now animate traveling particles along active (running) connections; and the canvas node/edge status colors moved off fixed hex values onto theme-aware `--orch-status-*` CSS custom properties, so they adapt correctly to light/dark mode. ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- **chore(dashboard):** Orchestration UI hardening pass and the missing component/model test coverage it called for — `OrchestratorNode`/`ActivityNode`/`OverflowNode` rendering, the `?node=`/overflow-click page routing, the Agents-tab orchestrator-click no-op and `showCompleted` toggle, and the overview kanban's done-column sort order (#12270, #12271). ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- New `/dashboard/orchestration` page: live unified view of everything running — Cloud Agent, A2A and Conductor as a real-time graph (Agents tab), the combo cascade (Routing tab, reusing the Combo Live Studio) and a state kanban (Overview tab), with a detail drawer (trace, cost, approve/cancel). Read-only over existing APIs — no new backend. Canvas concept credit: PR #11815 design ([#12261](https://github.com/diegosouzapw/OmniRoute/pull/12261)) +- **feat(dashboard):** new "History" tab on `/dashboard/orchestration` — an Airflow-style grid of finished runs over a 24h/7d/30d preset window, one row per (source, identity), clicking a cell opens the existing detail drawer. It is backed by real persistence: A2A task lifecycle transitions are now written to the `a2a_tasks` table (purged after 30 days, configurable via `OMNIROUTE_A2A_HISTORY_RETENTION_DAYS`) and served by the new `GET /api/a2a/tasks/history` listing endpoint, with the task-detail route falling back to persisted history once a run leaves the in-memory snapshot. Conductor runs stay remote and are not persisted locally — the tab says so instead of silently omitting them. ([#12479](https://github.com/diegosouzapw/OmniRoute/pull/12479)) +- **feat(dashboard):** the orchestration detail drawer gained a "Repeat" action for Cloud Agent, A2A and Conductor tasks — a two-click confirm (click once to arm, click again within the confirm window to fire) re-submits the original prompt/input as a new run. The button is disabled with an explanatory tooltip whenever the original input can't be recovered from the loaded task detail (e.g. it never carried a prompt, or the detail failed to load). Two limitations of the A2A variant, by design: it targets the `/a2a` JSON-RPC endpoint, which authenticates with an API key only (`REQUIRE_API_KEY=true` or a configured `OMNIROUTE_API_KEY` makes a dashboard-session repeat answer `HTTP 400` — surfaced verbatim in the drawer's error line, never as a success), and the `message/send` call is SYNCHRONOUS: the POST blocks for the whole skill run, so the success confirmation only appears once the repeated task finishes. A dashboard-authenticated A2A creation path is deliberately left to a follow-up — widening the endpoint's auth posture is an operator decision, not a side effect of this feature. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(a2a):** A2A task execution now records which memories were consulted for the task's last user message as `metadata.memoryHits` (id/key/type/content-snippet) plus a `memory_hits` history event, purely for observability — the retrieved memory is never injected into a skill's prompt or behavior. Gated by the `OMNIROUTE_A2A_MEMORY_HITS` kill-switch (default enabled; set to `0` to skip the recall lookup entirely). The drawer's new "Memory used" section lists these hits for a2a tasks and is omitted whenever there are none. Known limitation: recall only resolves under the keyless posture — a keyed caller's task owner is a SHA-256 prefix of the API key, while memory rows are keyed by the database api-key id, and no hash→id lookup exists today, so the hit list stays empty for keyed callers. The recorded hits are also kept out of the task's own `input` (and therefore out of the persisted input and of the "Repeat" request body), so repeating a task never re-sends the previous run's memory snippets. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(providers):** add a Perplexity Agent API provider (`perplexity-agent` / `pplx-agent`) for Perplexity `/v1/responses`, including the documented Anthropic, OpenAI, Google, xAI, DeepSeek, Z.AI, Moonshot/Kimi, NVIDIA, and Perplexity model IDs plus Anthropic-model `max_output_tokens` compatibility. ([#12103](https://github.com/diegosouzapw/OmniRoute/pull/12103)) — thanks @AIB1TAL0S +- **feat(providers):** add RPD (Requests Per Day) limit to provider rate limit overrides across UI, schemas, DB, and i18n ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) — thanks @b3nw +- **feat(providers): add UC Direct (uncensored.com Developer API), the metered OpenAI-compatible surface.** A standard OpenAI-compatible passthrough (default executor) for uncensored.com's official REST API at `https://api.uncensored.com/api/v1`: `X-api-key` auth (never-expiring `uai_sk_live_` key), `POST /chat/completions` with streaming SSE and native tool-calling, and the full live metered catalog (82 models across 15 providers, discovered from the public `GET /v1/models`). Registered as provider `uc-direct` (alias `ucd`). Complements the un-metered `uc` persona provider — same models, metered credits and a plain API key instead of a subscription session. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(providers): add UC (uncensored.com), the un-metered subscription "persona" chat as an OpenAI-compatible provider.** A WebSocket web-app port: a durable Clerk credential mints a short-lived session token per connect (browserless — no API key), driving UC's persona socket. Ships the browserless email-code login (request → verify → harvest), the 19 verified persona models (Claude Opus, Gemini, Grok, GLM, Kimi, DeepSeek, MiniMax, incl. the uncensored variants), prompted `` tool-calling with a per-model code-style dialect + auto-cure retry for guardrailed models, live ``/reasoning split, streaming + non-streaming OpenAI responses, and full quota/auth error surfacing (paywall / message-limit / rate-limit → 429, invalid session → 401 re-login). Full multimodal parity via the persona blob-upload layer: **vision** (image input, 15 vision-capable models), **document RAG** (PDF/doc upload, server-side extraction), **image generation** (22 models), **video generation** (14 models, async signed-url → poll), and **TTS** (streaming MP3). Registered as provider `uc` (alias `ucn`). The metered OpenAI-compatible Developer API is a separate `uc-direct` provider. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(logs):** add copy-all button to request detail modal ([#11083](https://github.com/diegosouzapw/OmniRoute/pull/11083)) — thanks @AndrianBalanescu +- **feat(check):** make serviceKinds required and add the reverse-walk provider consistency gate ([#11392](https://github.com/diegosouzapw/OmniRoute/pull/11392)) — thanks @Tushar49 +- **feat(providers):** only advertise verified-working connections by default ([#11448](https://github.com/diegosouzapw/OmniRoute/pull/11448)) — thanks @alltomatos +- **feat(resilience):** add hierarchical concurrency admission ([#11493](https://github.com/diegosouzapw/OmniRoute/pull/11493)) +- **feat(providers):** derive + surface expiry for JWT-bearing web cookies (#11497) ([#11505](https://github.com/diegosouzapw/OmniRoute/pull/11505)) — thanks @oyi77 +- **feat(tunnels):** support named cloudflared tunnels via CLOUDFLARED_CONFIG ([#11561](https://github.com/diegosouzapw/OmniRoute/pull/11561)) — thanks @ggiak +- **feat(compression):** make proactive context-compression threshold a live setting ([#11564](https://github.com/diegosouzapw/OmniRoute/pull/11564)) — thanks @ragnar-claude +- **feat(resilience):** add provider peak-hour protection ([#11622](https://github.com/diegosouzapw/OmniRoute/pull/11622)) — thanks @Neuron-Mr-White +- **feat(providers):** add 1min.ai provider ([#11631](https://github.com/diegosouzapw/OmniRoute/pull/11631)) — thanks @zero-executioner +- **feat(dashboard):** collapsible JSON tree viewer for request/response payloads ([#11703](https://github.com/diegosouzapw/OmniRoute/pull/11703)) — thanks @hartmark +- **feat(routing):** add LiquidAI LFM2.5-2.6B free tier via OpenRouter ([#11752](https://github.com/diegosouzapw/OmniRoute/pull/11752)) — thanks @brick30llc-ctrl +- **feat(pricing):** add GLM-5.3-Flash pricing, model specs, and catalog registration ([#11830](https://github.com/diegosouzapw/OmniRoute/pull/11830)) — thanks @AndrianBalanescu +- **feat(routing):** add relayMode for schema-locked context handoffs ([#11839](https://github.com/diegosouzapw/OmniRoute/pull/11839)) — thanks @raheemuddin786 +- **feat(config):** add support for runtime system prompt configuration and hot-reloading ([#11841](https://github.com/diegosouzapw/OmniRoute/pull/11841)) — thanks @raheemuddin786 +- **feat(routing):** expand connection-aware quota prefilter across combo strategies (#11682) ([#11850](https://github.com/diegosouzapw/OmniRoute/pull/11850)) — thanks @HouMinXi +- **feat(models):** reactive discovery sync after Antigravity model-not-found 404 ([#11888](https://github.com/diegosouzapw/OmniRoute/pull/11888)) — thanks @MumuTW +- **feat(models):** auto-aliases and token limits from synced Antigravity discovery ([#11889](https://github.com/diegosouzapw/OmniRoute/pull/11889)) — thanks @MumuTW +- **feat(settings):** raise sticky round-robin limit caps to 1000 ([#12015](https://github.com/diegosouzapw/OmniRoute/pull/12015)) — thanks @adivekar-utexas +- **feat(resilience):** operator-configurable global credential health check interval ([#12043](https://github.com/diegosouzapw/OmniRoute/pull/12043)) — thanks @adivekar-utexas +- **feat(quota):** use official OpenCode Go usage API ([#12124](https://github.com/diegosouzapw/OmniRoute/pull/12124)) — thanks @ddarkr +- **feat(resilience):** default the credential health check sweep to 60 minutes ([#12138](https://github.com/diegosouzapw/OmniRoute/pull/12138)) — thanks @adivekar-utexas +- **feat(quality):** new-code mode for the complexity and dead-code ratchets (Clean as You Code) ([#12142](https://github.com/diegosouzapw/OmniRoute/pull/12142)) +- **feat(dashboard):** orchestration canvas — unified model + snapshot hook (part 1/2) ([#12156](https://github.com/diegosouzapw/OmniRoute/pull/12156)) +- **feat(usage):** add Kilo Code balance and Kilo Pass quotas ([#12178](https://github.com/diegosouzapw/OmniRoute/pull/12178)) — thanks @killer30001000 +- **feat(providers):** refresh vendored ChatGPT Web connector to v4.0.7 ([#12181](https://github.com/diegosouzapw/OmniRoute/pull/12181)) — thanks @backryun +- **feat(providers):** manual "Clear cooldown" action in the cooling panel ([#12224](https://github.com/diegosouzapw/OmniRoute/pull/12224)) — thanks @patrykkopycinski +- **feat(providers):** restore ChatGPT Web via clean-room browser transport ([#12239](https://github.com/diegosouzapw/OmniRoute/pull/12239)) — thanks @backryun +- **feat(sse):** wire the PROVIDER_PROFILES window gate into the global provider cooldown ([#12247](https://github.com/diegosouzapw/OmniRoute/pull/12247)) +- **feat(providers):** modernize CLOVA Studio chat and embeddings ([#12277](https://github.com/diegosouzapw/OmniRoute/pull/12277)) — thanks @backryun +- **feat(browser):** adopt Obscura as primary headless browser engine with Chromium fallback ([#12286](https://github.com/diegosouzapw/OmniRoute/pull/12286)) — thanks @opensource-elearning +- **feat(sse):** full language parity for output styles (es/de/fr/it/ru/zh + autoDetect) ([#12289](https://github.com/diegosouzapw/OmniRoute/pull/12289)) +- **feat(quality):** complete test:scoped — --full map rebuild, stdin selection, CI loader parity (#8084 D1) ([#12353](https://github.com/diegosouzapw/OmniRoute/pull/12353)) +- **feat(providers):** refresh Fable, Cursor, and Devin catalogs ([#12367](https://github.com/diegosouzapw/OmniRoute/pull/12367)) — thanks @backryun +- **feat(video):** redact transcript text from logs and durable memory (#12150 P1) ([#12427](https://github.com/diegosouzapw/OmniRoute/pull/12427)) +- **feat(dashboard):** parent-link, genuine-continuation badge, and modal perf fixes ([#12448](https://github.com/diegosouzapw/OmniRoute/pull/12448)) — thanks @hartmark +- **feat(dashboard):** adaptive context-budget dial on compression panel ([#12488](https://github.com/diegosouzapw/OmniRoute/pull/12488)) — thanks @HouMinXi +- **feat(providers):** refresh Z.ai Web models and browser transport ([#12524](https://github.com/diegosouzapw/OmniRoute/pull/12524)) — thanks @backryun +- **feat(video):** redact raw client-snapshot transcript fields in the detailed log (#12150 P2) ([#12528](https://github.com/diegosouzapw/OmniRoute/pull/12528)) +- **feat(providers):** refresh NVIDIA hosted models ([#12538](https://github.com/diegosouzapw/OmniRoute/pull/12538)) — thanks @backryun +- **feat(quota):** Moonshot Open Platform balance and TPD lock for custom nodes ([#12590](https://github.com/diegosouzapw/OmniRoute/pull/12590)) — thanks @HouMinXi +- **feat(video):** redact transcript in the in-memory pending-request snapshot (#12430 item 6) ([#12596](https://github.com/diegosouzapw/OmniRoute/pull/12596)) +- **feat(dashboard):** link a log entry's Conversation Context to its owning conversation ([#12646](https://github.com/diegosouzapw/OmniRoute/pull/12646)) — thanks @hartmark +- **feat(catalog):** eligibility-gated free-tier bucket ([#12669](https://github.com/diegosouzapw/OmniRoute/pull/12669)) +- **feat(db):** fail-closed previous_response_id continuation for redacted video turns (#12150 P2b) ([#12707](https://github.com/diegosouzapw/OmniRoute/pull/12707)) +- **feat(gamification): pay the documented `streak_bonus` and `badge_unlock` XP rewards.** `XP_REWARDS` listed both rewards but the award pipeline never paid them: the private reward table in `events.ts` omitted them, `updateStreak()` did not report when a streak extended, and badge unlocks carried no XP. Every request that extends a daily streak now pays `streak_bonus × streak length` once per UTC day (guarded by a same-day `xp_audit_log` check), and every badge unlocked through the pipeline pays `badge_unlock` once per badge (guarded by the `user_badges` primary key; `unlockBadge()` now reports whether it inserted). Bonus XP flows through the same `addXp` + level sync + global/weekly/monthly leaderboard path as action XP, so level-ups and rankings include it. The Radar supporter recognition unlock stays XP-free. (#12522 — thanks @pacocartones) +- **feat(providers):** advertise a `free-tier` capability in the provider plugin manifest for every provider with documented free models, so sidecars and dashboards can filter free-capable providers without reading the quota catalog ([#12786](https://github.com/diegosouzapw/OmniRoute/pull/12786)) — thanks @maxmad64bis +- **feat(providers):** Added EURouter as an OpenAI-compatible API-key gateway (`https://api.eurouter.ai/v1`), with live model discovery via `passthroughModels`. Its copy states that models are served by third-party upstreams listed per model, so an EU-based router is not read as EU data residency for inference. ([#13025](https://github.com/diegosouzapw/OmniRoute/pull/13025)) — thanks @ntdatt812 +- **feat(providers):** Added GreenPT as an OpenAI-compatible API-key provider (`https://api.greenpt.ai/v1`), with live model discovery via `passthroughModels`. No free-inference badge: the published docs describe a free API subscription billed per token, not a free tier. ([#13024](https://github.com/diegosouzapw/OmniRoute/pull/13024)) — thanks @ntdatt812 +- **feat(i18n):** 9 new locales — Greek (`el`), Croatian (`hr`), Serbian (`sr`), Lithuanian (`lt`), Estonian (`et`), Latvian (`lv`), Slovenian (`sl`), Maltese (`mt`), Irish (`ga`) — across the dashboard, docs mirrors, CLI, README and the site; OmniRoute now ships every official EU language (51 locales). (#13044) +- **feat(i18n):** 8 new locales — Kannada (`kn`), Malayalam (`ml`), Odia (`or`), Punjabi (`pa`), Nepali (`ne`), Sinhala (`si`), Burmese (`my`), Khmer (`km`) — across the dashboard, docs mirrors, CLI, README and the site (59 locales). The translator now restores the ICU literal escape around angle placeholders and splits oversized docs sections before translating. (#0000) ([#13660](https://github.com/diegosouzapw/OmniRoute/pull/13660)) +- feat(providers): **list Agnes 3.0 Flash as the current free chat model, drop retired 1.5 Flash, add Image 2.0/2.5 Flash plus Video 2.5/2.5 Flash, and discover the live `/v1/models` catalog (including the CN host `api.agnes-ai.cn`).** `agnes-1.5-flash` now forwards to `agnes-3.0-flash`. Video 2.5 polls `GET /v1/videos/{id}` (not the V2.0 `/agnesapi` contract). Live `/v1/models` (2026-09-09) no longer serves 1.5; the wiki marks it deprecated. 3.0 Flash is 512K context / 65,536 max output, same window as 2.5. CN-region keys use the existing per-connection base-URL field, default stays `apihub.agnes-ai.com`. ([#13120](https://github.com/diegosouzapw/OmniRoute/pull/13120)) — thanks @HouMinXi +- **feat(sse):** Codex and OpenAI catalogs list GPT-6 Astra with effort aliases (`-low` through `-ultra`); Codex CLI identity pins `@openai/codex@0.153.4` in lockstep with the image ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **feat(dashboard):** the orchestration History tab gained a "Compare runs" mode — toggling it turns each grid cell into a 2-item selection queue (a 3rd click drops the oldest pick), and picking two cells opens a side-by-side comparison panel instead of the usual detail drawer. The panel fetches both runs' detail the same way the drawer does (falling back to persisted history once a run leaves the live TTL window) and shows, per side: identity/source/state, start time, a signed `right - left` delta for duration/cost/event count, the event timeline aligned by index, and any memory hits. One side's fetch failing never blocks the other, and a delta is only ever computed when both sides have a finite value — otherwise it renders "—", never `NaN`. Comparing two runs from different sources or skills still works; a banner marks the deltas as informational rather than hiding them, since the two runs aren't a strict apples-to-apples pair. ([#12677](https://github.com/diegosouzapw/OmniRoute/pull/12677)) +- **feat(dashboard):** badge a conversation that never reached a clean stop ([#12717](https://github.com/diegosouzapw/OmniRoute/pull/12717)) — thanks @hartmark +- **feat(release):** reconcile-changelog tool + version-anchored fragment aggregation ([#12987](https://github.com/diegosouzapw/OmniRoute/pull/12987)) +- **feat(mcp):** carry context_length through MCP list_models_catalog projection (#12776) ([#13406](https://github.com/diegosouzapw/OmniRoute/pull/13406)) — thanks @KooshaPari +- **feat(cli):** make startup readiness budget configurable (#13369) ([#13433](https://github.com/diegosouzapw/OmniRoute/pull/13433)) — thanks @KooshaPari + ### 🐛 Bug Fixes +- **security(streaming):** sanitize generic mid-stream error messages before emitting OpenAI, + Responses, or Claude SSE failure frames and before diagnostic logging, while preserving raw + failures for internal classification and keeping client disconnects out of provider failure state. ([#12457](https://github.com/diegosouzapw/OmniRoute/pull/12457)) +- **fix(claude):** `blockExtraUsage: false` no longer lets 5h quota preflight skip the account; extra usage is billed after the session bar is gone, so the request must reach Anthropic ([#12803](https://github.com/diegosouzapw/OmniRoute/pull/12803)) — thanks @HouMinXi +- Sanitize HuggingChat conversation-creation and message-send transport failures before they reach client error bodies or provider logs. ([#12467](https://github.com/diegosouzapw/OmniRoute/pull/12467)) +- **fix(dashboard):** Keep local and theme-aware provider SVG icons at a definite layout size so Chromium does not collapse them to 0×0 after the v3.8.50 image-rendering change ([#12054](https://github.com/diegosouzapw/OmniRoute/pull/12054)) — thanks @ponkcore +- **fix(security):** Sanitize provider and runtime failures before public API, SSE and MCP responses and before persistent request, proxy and usage logs, preventing credentials, stack traces and host filesystem paths from crossing those boundaries while preserving stable error codes and useful diagnostics. ([#12506](https://github.com/diegosouzapw/OmniRoute/pull/12506)) +- **fix(cli):** `omniroute tunnel create` no longer crashes with `Cannot read properties of undefined (reading optsWithGlobals)` — removed the duplicate positional argument that caused Commander.js to misalign the action callback parameters ([#12295](https://github.com/diegosouzapw/OmniRoute/issues/12295)) ([#12368](https://github.com/diegosouzapw/OmniRoute/pull/12368)) — thanks @gonisulaimann +- Stop advertising Gemini Live-only models as supported audio endpoints until OmniRoute proxies the bidirectional Live protocol. ([#11710](https://github.com/diegosouzapw/OmniRoute/pull/11710)) — thanks @RaviTharuma +- **fix(kie):** reroute `flux/kontext` off the KIE Market `createTask` flow — it is catalogued with `isMarket: true` but has no Market catalog page, so KIE rejected it with "model name not supported"; it now hits the dedicated `POST /api/v1/flux/kontext/generate` / `GET /api/v1/flux/kontext/record-info` endpoints instead (#11296). ([#11985](https://github.com/diegosouzapw/OmniRoute/pull/11985)) — thanks @hartmark +- **fix(kie):** correct 12 more KIE Market catalog ids that were sent to `createTask` unchanged but diverge from KIE's documented upstream `model` values — GPT Image 2 T2I/I2I (drops the `gpt/` prefix), GPT Image 1.5 T2I/I2I (`gpt-image/` namespace), Seedream 5.0 Lite T2I/I2I (drops the `.0`), all 4 Flux 2 variants (`flux-2/` namespace, generic variant renamed `flex`), and Wan 2.7 Image / Image Pro (dash instead of dot) — each verified individually against the literal example request published on docs.kie.ai. `#11326`'s "everything else already matches" claim was wrong a second time (#11296); `z-image/4.0-*`/`z-image/4.5-*` and `flux/kontext` remain open, documented as unresolved in `KIE_MARKET_UPSTREAM_MODEL_IDS`'s comment pending further verification. ([#11667](https://github.com/diegosouzapw/OmniRoute/pull/11667)) +- **fix(dashboard):** `useApiKeySave.handleSaveApiKey` no longer forces a full upstream `/models` catalog sync on every non-curated provider connection save — callers can now pass `skipModelSync: true` to opt out, so a workflow that only wants to add one manual model no longer floods the provider's available-models list with hundreds/thousands of synced entries. The flag is a client-side intent signal only and is stripped before the connection payload is POSTed to `/api/providers`; default behavior (full sync on save) is unchanged when the flag is omitted (#11324) ([#11664](https://github.com/diegosouzapw/OmniRoute/pull/11664)) +- **Provider connections:** keep `tokenExpiresAt` when a connection is created. The create-path allowlist omitted it, so every insert stored NULL and the dashboard token badge could read a fresh connection as expired until its first background refresh ([#11368](https://github.com/diegosouzapw/OmniRoute/pull/11368)). — thanks @ntdatt812 +- **fix(combos):** the combo builder's precision-select, global-model-search, and manual-entry flows now serialize a model step's `model` string using the provider's already-computed routing-alias prefix (e.g. `oc/`) instead of rebuilding it from the raw canonical `providerId`, fixing the no-auth "OpenCode Free" provider (`opencode`) being routed to the unrelated paid "OpenCode Zen" provider (`opencode-zen`) because `opencode` doubles as a manual routing-prefix override ([#11433](https://github.com/diegosouzapw/OmniRoute/issues/11433)). ([#11670](https://github.com/diegosouzapw/OmniRoute/pull/11670)) +- **fix(radar):** the catalog feed cache now keeps `generatedAt`, the date the feed's data was built, next to `fetchedAt`, the date this install downloaded it (#11435). The feed schema requires that date and the sync path validates it, but the cache dropped it — so a feed fetched minutes ago and one carrying weeks-old figures looked identical to everything downstream, including the dashboard's "Last fetched" line. `getRadarCatalog().meta` and `GET /api/radar/status` now report both dates, the latter as its own field rather than folded into `version` — and omitted entirely for the offers and intel caches, which keep no build date, where a `null` would read as "unknown" rather than "never stored". The dashboard still shows only the fetch time; surfacing the build date there needs a new translated label and is left to a follow-up. Rows cached before migration 163 read back as `null`: unknown stays unknown instead of borrowing the fetch time. The referrals cache has persisted the same date since migration 142. — thanks @maxmad64bis +- **fix(cli):** postinstall no longer fills `JWT_SECRET` and `API_KEY_SECRET` in the installed package's `.env` (#11436). `.env.example` ships both blank on purpose: the server restores them from its durable store, or generates and persists them there, in `ensureSecrets()`. Pre-filling them defeated that — the file lives inside the package directory, so `npm i -g` replaced it and postinstall wrote _different_ values, while `ensureSecrets()` (which only acts on an empty variable) never got to restore the real ones. Both secrets rotated silently on every update, invalidating dashboard sessions and API-key CRCs. `STORAGE_ENCRYPTION_KEY` left the same list for the same reason in #1622; its comment pointed at a function that no longer exists and now names the real provisioning path. — thanks @maxmad64bis +- **fix(cli):** the CLI now says when a loaded `.env` lives inside the installed package directory (#11437). It already announces every env file it reads, without distinguishing the ones that survive an update from the one that does not: `npm i -g` replaces the package directory wholesale, so values set there are gone at the next update, silently. The warning names the durable path to move them to, and fires only when that file actually supplied a value — a file entirely shadowed by a durable one supplied nothing. A development checkout stays silent: there the same path is stable and documented in `SETUP_GUIDE.md`. — thanks @maxmad64bis +- **fix(free-models):** the shared `isFreeModel()` predicate no longer reports catalog entries marked `freeType: "discontinued"` as free, so `hidePaidModels` can't route to Pollinations' seven premium models that now require a paid key ([#11441](https://github.com/diegosouzapw/OmniRoute/pull/11441)) — thanks @maxmad64bis +- fix(ui): let AnySearch use the normal provider-icon fallback when LobeHub has no matching icon (#11449) +- Fixed the v3.8.50 Costs and Analytics dashboards so flat-rate Claude Code usage can be shown as an explicitly requested token-price estimate without changing default billed-cost semantics. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed archived usage retention so each request is priced individually instead of pricing a day's summed tokens once, which understated archived cost whenever a day mixed cache-heavy and ordinary requests. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed the Costs dashboard so it discloses when displayed figures include flat-rate token-price estimates instead of labelling them as billed spend, using the flag the analytics API already returns; the month-end projection and the CSV/JSON exports carry the same marker, and billed-cost mode is unchanged. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- **fix(combo):** attach the same combo-diagnostics trace (`poolSize`/`attemptOrder`/`excluded`/`terminalReason`, plus `x-omniroute-combo-*` headers) to the round-robin strategy's and the nested pipeline/fusion runtime-unit loop's "Maximum combo retry limit reached" 503 that the priority-strategy path already attaches for the identical terminal condition — previously those two paths returned a bare, contextless 503 ([#11462](https://github.com/diegosouzapw/OmniRoute/issues/11462)). ([#11665](https://github.com/diegosouzapw/OmniRoute/pull/11665)) +- **fix(bun):** use native `bun:sqlite` in `bootstrap-env` and `sync-env` to avoid loading `better-sqlite3` N-API addon during Bun startup ([#11468](https://github.com/diegosouzapw/OmniRoute/pull/11468)) — thanks @TheDemonTuan +- **fix(docker-bun):** make `Dockerfile.bun` install reliable with pre-install script helpers and native `bun:sqlite` smoke check ([#11470](https://github.com/diegosouzapw/OmniRoute/pull/11470)) — thanks @TheDemonTuan +- **fix(docker-bun):** remove every vendored `better-sqlite3` native addon from the Bun runtime image so startup cannot abort before the `bun:sqlite` fallback ([#11482](https://github.com/diegosouzapw/OmniRoute/pull/11482)) — thanks @TheDemonTuan +- **fix(autoCombo):** effort/alias model variants (`gpt-5.6-sol-xhigh`, `gpt-5.6`, cursor's `claude-4.6-opus-high`) inherit their base model's task fitness instead of falling to the wildcard 0.5 ([#11492](https://github.com/diegosouzapw/OmniRoute/pull/11492)) — thanks @MumuTW +- fix(db): rate-limit repeated Arena ELO leaderboard fetch-failure warnings instead of logging one per sync attempt (#11500) ([#11989](https://github.com/diegosouzapw/OmniRoute/pull/11989)) — thanks @hartmark +- **fix(db):** dedupe the raw `[Encryption] Decryption failed...` log line emitted by the lazy-decrypt views (`createLazyRowProxy`/`createLazyConnectionView`), which power `getProviderConnections()` and were re-triggering that line on every CredentialHealth/model-sync cycle for the same corrupt or stale-key credential — a fresh Proxy over a fresh row on every cycle meant the per-proxy memoization never suppressed it, unlike the dedup `decryptConnectionFields()` already had since [#9927](https://github.com/diegosouzapw/OmniRoute/issues/9927). Now shares that dedupe tracking so the line logs at most once per credential ([#11500](https://github.com/diegosouzapw/OmniRoute/issues/11500)). ([#11678](https://github.com/diegosouzapw/OmniRoute/pull/11678)) +- **fix(intelligence):** Arena ELO sync stores a synthesized base-model row for every effort/harness variant (`claude-opus-5-max` → `claude-opus-5`, `gpt-5.6-sol-xhigh (codex-harness)` → `gpt-5.6-sol`), so bare model requests reach the synced intelligence layer instead of the static fallback table; the hand-maintained `MODEL_ALIAS_MAP` — which collapsed generations (`gpt-5.5` → `gpt-5`) — is gone ([#11506](https://github.com/diegosouzapw/OmniRoute/pull/11506)) — thanks @MumuTW +- **fix(autoCombo,sse):** catalog hygiene — the static task-fitness table no longer ranks retired models above live ones (dead rows dropped, patterns matched on segment boundaries), the 7 deprecation aliases that forwarded to retired or non-existent models now point at the vendors' published replacements, an alias is no longer rewritten when the serving provider offers the id as-is, and a new `check:model-lifecycle` gate diffs both tables against a vendor lifecycle snapshot ([#11507](https://github.com/diegosouzapw/OmniRoute/pull/11507)) — thanks @MumuTW +- **fix(sse):** stop dropping resolved `thoughtSignature` values on parallel (multi tool-call) turns sent to Gemini 3.x — the claude→gemini and openai→gemini translators previously kept the signature only on the *first* function call of a message, causing Gemini to reject subsequent calls in the same turn with HTTP 400 "Function call is missing a thought_signature"; each function call now keeps its own resolved signature ([#11510](https://github.com/diegosouzapw/OmniRoute/issues/11510)). ([#11669](https://github.com/diegosouzapw/OmniRoute/pull/11669)) +- Treat Docker publishing from a non-default release branch as an expected skip instead of a failed workflow, while preserving the guard that prevents it from overwriting the `next` channel. ([#11527](https://github.com/diegosouzapw/OmniRoute/pull/11527)) — thanks @pacocartones +- fix(search): prefer credentialed providers over duckduckgo-free fallback (#11524) — the fallback-only loop ran before the credentialed-providers loop in `executeWebSearch`, making configured providers unreachable when `duckduckgo-free` was available. ([#11565](https://github.com/diegosouzapw/OmniRoute/pull/11565)) — thanks @vsd2807 +- **fix(sse):** cap the upstream headers-wait phase for STREAMING requests to a client-realistic ceiling (110s, under Codex's own ~120s hard client-abort window) instead of the flat 10-minute `FETCH_TIMEOUT_MS` default — that default was 5x longer than the body-phase readiness watchdog's own adaptive bound, so a request whose upstream never returned any response at all (not even headers, e.g. a stalled NVIDIA target behind a tool-heavy Responses→Chat translation) kept the client connection alive on keepalives only, guaranteeing the client's own patience ran out first with an opaque 499 instead of OmniRoute detecting and failing the stall fast. Non-streaming requests are unaffected — they keep the existing flat default (`open-sse/utils/fetchStartTimeoutPolicy.ts`) (#11526) ([#11668](https://github.com/diegosouzapw/OmniRoute/pull/11668)) +- **fix(ci):** protocol-clients E2E harness boots the peer-stamped custom server (`run-next.mjs dev`) instead of the bare `next` CLI, so `LOCAL_ONLY` locality resolves from the real TCP peer stamp and `/api/mcp/audit` answers 200 (open bootstrap preserved via an `OMNIROUTE_E2E_BOOTSTRAP_MODE=open` credential clear in `run-next.mjs`, pinned to loopback `HOST`) instead of a deterministic 403 (#11535) ([#11549](https://github.com/diegosouzapw/OmniRoute/pull/11549)) — thanks @jonlwheat2-gif +- **fix(docs):** the Auto-Combo engine is described with the number of factors it actually declares. Four different counts were in circulation — 6, 9, 13 and 14 across `AGENTS.md`, four guides and six code comments — while `DEFAULT_WEIGHTS` declares 15. `check:docs-counts` now reads that number from the source, so adding a factor without updating the prose fails CI ([#11545](https://github.com/diegosouzapw/OmniRoute/pull/11545)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **fix(quota-share):** in-flight leases are per request, so an aborted request's slot ages out instead of being kept alive by later traffic on the same connection ([#11547](https://github.com/diegosouzapw/OmniRoute/pull/11547)) — thanks @abhisheksharma2411 +- The free-tier summary route now serves the refreshed Radar catalog when the feed is active — the same numbers the dashboard shows — and states which catalog answered plus its real build date, instead of always reporting release-frozen figures with a stale curation date. The supporter-key live feed stays reserved to authenticated callers of the instance. ([#11550](https://github.com/diegosouzapw/OmniRoute/pull/11550)) — thanks @maxmad64bis +- **sse:** fix LiveWS/embed-WS servers crashing at startup under the Node/tsx runtime — `liveServer.ts`, `embedWsProxy.ts` and `apiBridgeServer.ts` imported `@/shared/utils/httpClientAbortGuard` without the `.mjs` extension, so the client-abort crash guard added by [#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556) was unreachable and every dependent test failed with `ERR_MODULE_NOT_FOUND` ([#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556)). ([#11671](https://github.com/diegosouzapw/OmniRoute/pull/11671)) — thanks @jonlwheat2-gif / @yxyxy +- fix(chat): shed any of the matched provider-node's routing identifiers (public prefix or internal id) before model lookup, so composites like `//` no longer reach upstream verbatim (#11557) — thanks @maxmad64bis +- **fix(cache):** a semantic-cache entry now expires on its TTL instead of surviving until the next UTC midnight, and `dbEntries` stops counting expired rows ([#11573](https://github.com/diegosouzapw/OmniRoute/pull/11573)) — thanks @ntdatt812 +- **fix(api):** `/v1/models` schedules its stale-while-revalidate rebuild through Next's `after()` again, so a stale catalog reaches the client before the rebuild blocks the event loop ([#11574](https://github.com/diegosouzapw/OmniRoute/pull/11574)) — thanks @ntdatt812 +- **fix(check):** the `known-symbols` executor conformance gate awaits the now-async `getExecutor()`, so it stops reporting all 142 registered aliases as dead symbols and can detect a lazy import that fails ([#11582](https://github.com/diegosouzapw/OmniRoute/pull/11582)) — thanks @ntdatt812 +- **fix(pack):** register `bin/cli/utils/volatileEnvPath.mjs` in `PACK_ARTIFACT_REQUIRED_PATHS`, so the prune can no longer silently drop a module the published CLI imports on every boot ([#11588](https://github.com/diegosouzapw/OmniRoute/pull/11588)) — thanks @ntdatt812 +- **i18n(vi):** translate the five reliability-column strings on the free-provider rankings page, which rendered as `__MISSING__:` markers in the Vietnamese UI ([#11589](https://github.com/diegosouzapw/OmniRoute/pull/11589)) — thanks @ntdatt812 +- **resilience:** restore the expired-connection retry-budget probe in the token-health sweep — the `!isGitHubAccessTokenOnlyConnection` carve-out reintroduced by #11608 contradicted the boundary pinned by #11592, so a GitHub connection parked at `expired` with retry budget remaining was never probed and could never self-heal ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)). ([#11672](https://github.com/diegosouzapw/OmniRoute/pull/11672)) — thanks @jonlwheat2-gif / @ntdatt812 +- **fix(dashboard):** Provider Stats now displays combo success rates on their native 0–100 percentage scale instead of multiplying them twice ([#11595](https://github.com/diegosouzapw/OmniRoute/pull/11595)) — thanks @pacocartones +- **fix(providers):** route DuckDuckGo Web Chat through the verified `duck.ai` endpoint, refresh its free model catalog, and avoid forwarding unsolved anti-abuse challenges ([#11597](https://github.com/diegosouzapw/OmniRoute/pull/11597)) — thanks @solstxce +- **fix(ui):** Console log Refresh and Copy controls now expose localized accessible names, keep copy actions visible on keyboard focus, and announce copy completion safely ([#11599](https://github.com/diegosouzapw/OmniRoute/pull/11599)) — thanks @pacocartones +- Dropped the stale-`.eslintcache` `restore-keys` fallback from both "Restore ESLint file cache" steps in `ci.yml`, so the blocking `Lint` job can no longer be served per-file verdicts computed under a different lint config, suppressions file or lockfile. `quality.yml` had already dropped it in #11963; `ci.yml` — the workflow that actually gates PRs — had not (#11600). ([#11996](https://github.com/diegosouzapw/OmniRoute/pull/11996)) +- fix(ci): pass `--pass-on-unpruned-suppressions` in `run-eslint-json.mjs` so the CI Lint job no longer fails when a suppression is merely orphaned by a genuine fix, mirroring the identical fix already in `validate-release-green.mjs` (#11600) ([#11983](https://github.com/diegosouzapw/OmniRoute/pull/11983)) — thanks @hartmark +- fix(handoff): enforce provider allowlist for universal handoff (#11602) — universal handoff now skips summarization when the selected summary model's provider is not included in the configured provider allowlist. ([#11619](https://github.com/diegosouzapw/OmniRoute/pull/11619)) — thanks @vsd2807 +- **fix(dashboard):** Show a stable error state when Search Analytics returns an HTTP error or malformed data ([#11603](https://github.com/diegosouzapw/OmniRoute/pull/11603)) — thanks @pacocartones +- **test(gamification):** pin the aggregate profile level to the XP-derived semantics of #11604 — `getAggregateXp()` now derives `currentLevel` from the summed XP (`calculateLevel(sum)`), not `MAX(stored current_level)`, and the #3484 fixture levels are aligned with the XP curve ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)). ([#11676](https://github.com/diegosouzapw/OmniRoute/pull/11676)) — thanks @jonlwheat2-gif / @pacocartones +- **fix(dashboard):** Keep the Profile level and progress aligned with aggregate XP, including bounded handling for invalid totals ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)) — thanks @pacocartones +- **fix(dashboard):** Prevent locked hidden badges from revealing their icon or opening private badge details before they are earned ([#11605](https://github.com/diegosouzapw/OmniRoute/pull/11605)) — thanks @pacocartones +- **fix(dashboard):** Restore keyboard focus after shared modals close and cancel delayed autofocus during cleanup ([#11607](https://github.com/diegosouzapw/OmniRoute/pull/11607)) — thanks @pacocartones +- **fix(dashboard):** Enable Enter and Space activation for clickable data-table rows without hijacking nested controls ([#11610](https://github.com/diegosouzapw/OmniRoute/pull/11610)) — thanks @pacocartones +- **fix(autoCombo,sse):** vendor-retired catalog ids are dropped from the auto-combo candidate pool and no longer win on leftover `arena_elo` / `user_override` rows; `getModelLifecycleDecision` consults `model-lifecycle.json` (prefix-stripped) so aggregator traffic is not `untracked` for ids the snapshot already knows ([#11625](https://github.com/diegosouzapw/OmniRoute/issues/11625)) ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)) — thanks @MumuTW +- **test(chatcore):** move Codex/Claude combo fixtures off models the lifecycle guard now rejects — `gpt-5.1-codex`/`gpt-5-codex` are vendor-retired (snapshot, #11626) and `claude-3-5-sonnet-20241022` is shut down, so native-passthrough and combo-fallback tests switched to `gpt-5.6-sol` and `claude-sonnet-4.6` ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)). ([#11675](https://github.com/diegosouzapw/OmniRoute/pull/11675)) — thanks @jonlwheat2-gif / @MumuTW +- fix(opencode-plugin): stop a free-tier budget that rounds up across a magnitude from rendering as `1000K`/`1000M` in the model picker — `fmtTokens` chose its unit from the raw token count and then rounded with `toFixed(1)`, so 999,950–999,999 printed as `1000K` rather than `1M` and just under a billion printed as `1000M` rather than `1B` ([#11684](https://github.com/diegosouzapw/OmniRoute/pull/11684)) — thanks @abhisheksharma2411 +- Cap prose-derived Antigravity quota resets at the configured model cooldown maximum while preserving authoritative Retry-After headers and Google RetryInfo hints. (#11695) Thanks @pacocartones. ([#11823](https://github.com/diegosouzapw/OmniRoute/pull/11823)) +- Compare `_rebaseline_*` notes structurally in the ratchet-bank verifier so an object-valued note no longer reports itself as rewritten on an unmodified tree — the nightly `bank-ratchet-shrinks` lane had been aborting on every run since 2026-08-11, leaving 41,672 lines of earned file-size shrinks unbanked (#11702). ([#11892](https://github.com/diegosouzapw/OmniRoute/pull/11892)) — thanks @MumuTW +- **fix(build):** `prepublish.ts` bundles the ChatGPT Web (Codex) MCP bridge through `runBuildTool()` instead of spawning `npx.cmd` raw, fixing the build crash on Node ≥ 20/Windows where `.cmd` shims cannot be spawned without a shell (EINVAL) ([#11704](https://github.com/diegosouzapw/OmniRoute/issues/11704)) ([#11706](https://github.com/diegosouzapw/OmniRoute/pull/11706)) — thanks @jonlwheat2-gif +- fix(codex): keep `parallel_tool_calls:false` on the translated Codex Responses Lite path (#11707) ([#11984](https://github.com/diegosouzapw/OmniRoute/pull/11984)) — thanks @hartmark +- **fix(tests):** the new Gemini Web Images retirement test (#11708) called the now-async `getExecutor()` synchronously, throwing `TypeError: getExecutor(...).getProvider is not a function` instead of asserting Gemini Web chat stays available. Awaited to match the R0.3 executor-registry refactor (#11220). ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(search):** Allow REST and MCP web-fetch requests to select the advertised `anysearch-search` provider explicitly ([#11716](https://github.com/diegosouzapw/OmniRoute/pull/11716)) — thanks @pacocartones +- **fix(providers):** Keep overnight peak-hour protection active after midnight according to the window's configured start day ([#11718](https://github.com/diegosouzapw/OmniRoute/pull/11718)) — thanks @pacocartones +- **fix(providers):** correct all five g4f.space gateway descriptions to document proof-of-work credits or member API keys, variable limits, and the remote prompt boundary instead of promising free keyless access at five requests per minute ([#11738](https://github.com/diegosouzapw/OmniRoute/pull/11738)). +- **fix(db):** `model_capabilities` is created by a migration instead of lazily on the first models.dev sync, so a clean install and an upgraded install converge on the same schema regardless of which features have run ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(ci):** `check:install-upgrade` now fails on an `npm` install truncated by ENOSPC (npm reports it as a warning and still exits 0), authenticates its health probe so the version assertion works against the hardened health payload, frees the clean-install tree before the upgrade phase, and no longer reports a schema divergence computed from a boot that never served ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(routing):** harden session-affinity key derivation against oversized explicit IDs and empty structural-payload collisions, while recognizing Gemini and common text request shapes ([#11746](https://github.com/diegosouzapw/OmniRoute/pull/11746)) — thanks @MumuTW +- **fix(packages/browser-pool):** regenerate `package-lock.json` so the `packages/browser-pool` workspace's locked `playwright`/`@types/node` (and transitives) match its `package.json` specs, fixing cache-only/offline installs (`npm ci --offline`, Nix `buildNpmPackage`) that previously failed with `ENOTCACHED` ([#11747](https://github.com/diegosouzapw/OmniRoute/issues/11747)) — thanks @benjaminkitt ([#11784](https://github.com/diegosouzapw/OmniRoute/pull/11784)) — thanks @geekyNads +- **fix(assets):** Stop distributing 79 provider and CLI logos without verified source/license provenance, keep every provider/tool available through the internal generic icon, and prevent those IDs from falling through to an external icon CDN. ([#11750](https://github.com/diegosouzapw/OmniRoute/pull/11750)) +- **fix(providers):** retire the common `chatgpt-web` / `cgpt-web` provider because its pre-key and proof-of-work provenance could not be cleared ([#11754](https://github.com/diegosouzapw/OmniRoute/pull/11754)) — explicit chat and image requests, slash-prefixed models, persisted aliases, proxy overrides, and combo targets now fail closed with a sanitized HTTP `410` / `PROVIDER_RETIRED` before upstream dispatch. Migration 163 tombstones matching connections and invalidates their active leases without deleting history or widening API-key allowlists. The independent `chatgpt-web-codex` / `cgpt-codex` provider and its MIT-noticed vendored implementation remain available. +- **fix(usage):** quota and usage refresh no longer 409 when an exclusive lease reserves the connection ([#11758](https://github.com/diegosouzapw/OmniRoute/pull/11758)) — thanks @TheDemonTuan +- Keep the embedding registry's vector width and `embedding` type on models when a synced model exists for the same id, so `/v1/models` no longer reports registry-described embedding models widthless or untyped (#11761) — thanks @rahilmavani +- Correct `google/gemini-embedding-001` on the OpenRouter route to 3072 dimensions, the width it returns when `dimensions` is not sent (#11761) — thanks @rahilmavani +- **fix(vision-bridge):** Restrict automatic vision routing and fallbacks to models available in authoritative live provider catalogs, including aliases and effort variants ([#11767](https://github.com/diegosouzapw/OmniRoute/issues/11767)) — thanks @pacocartones ([#11813](https://github.com/diegosouzapw/OmniRoute/pull/11813)) +- **fix(providers):** reclassify Cerebras as a one-time $5 signup credit (payment method required, 30-day validity), not a recurring no-card 1M tokens/day trial ([#11773](https://github.com/diegosouzapw/OmniRoute/issues/11773)) ([#12591](https://github.com/diegosouzapw/OmniRoute/pull/12591)) — thanks @HouMinXi +- fix(cli): stop prepublish from re-rebuilding the already-built ESM-only opencode-plugin dist (#11787) ([#11990](https://github.com/diegosouzapw/OmniRoute/pull/11990)) — thanks @hartmark +- **fix(db):** Fresh installs no longer log a non-fatal `no such table: compression_run_telemetry` warning when retention cleanup runs before the lazily-created telemetry table exists ([#11802](https://github.com/diegosouzapw/OmniRoute/pull/11802)) — thanks @RaviTharuma +- fix(sse): set X-OmniRoute-Selected-Connection-Id on successful combo dispatches so downstream consumers stop falling back to an empty connection id (#11810) ([#11986](https://github.com/diegosouzapw/OmniRoute/pull/11986)) — thanks @hartmark +- **fix(services):** embedded CLIProxyAPI lifecycle checks now use public `/healthz`, while model discovery uses the configured dedicated data-plane API key instead of the management password ([#11811](https://github.com/diegosouzapw/OmniRoute/pull/11811)) — thanks @RaviTharuma +- Fix persisted model steps returning an error in the playground route simulator, and warn when structural steps cannot be simulated. ([#11862](https://github.com/diegosouzapw/OmniRoute/pull/11862)) — thanks @pacocartones +- **fix(providers):** Antigravity's dynamic mitmAlias table no longer routes `gemini-3.7-flash-{high,medium,low}` to a literal tier-suffixed upstream id just because one connected account's own discovery listed it directly — those display ids always resolve through the safe `gemini-3.7-flash-tiered` static alias, so one account's Google-provisioned access no longer 404s every sibling account of the provider ([#11824](https://github.com/diegosouzapw/OmniRoute/issues/11824), [#11651](https://github.com/diegosouzapw/OmniRoute/issues/11651)) ([#11988](https://github.com/diegosouzapw/OmniRoute/pull/11988)) — thanks @hartmark +- **fix(dashboard):** Expose Profile loading, errors, page structure, and XP progress to assistive technologies ([#11838](https://github.com/diegosouzapw/OmniRoute/pull/11838)) — thanks @pacocartones +- **fix(socks):** `Agent.connectTimeout` now bounds both SOCKS handshake and TLS connect, and `family === null` no longer falls back to `fetch-socks` ([#11842](https://github.com/diegosouzapw/OmniRoute/pull/11842)) — thanks @maxmad64bis +- **perf(compression):** OOM mitigations for large payload hashing, memoization, and token estimation ([#11844](https://github.com/diegosouzapw/OmniRoute/pull/11844) — thanks @AndrianBalanescu) +- Excluded Next.js Node File Trace manifests (`*.nft.json`) from the published npm tarball. They are build-time metadata and are never read while serving, but had grown to 668.7 MB — 61% of the package — which pushed the upload past the registry limit and made `npm publish` fail with `413 Payload Too Large`. ([#11864](https://github.com/diegosouzapw/OmniRoute/pull/11864)) +- **fix(cli):** keep the global OmniRoute server `--base-url` separate from provider-node `--endpoint` values in add, update, and validate commands ([#11860](https://github.com/diegosouzapw/OmniRoute/pull/11860)) — thanks @pacocartones +- **fix(config):** Nous Research's `Hermes-4-405B` model now displays as "Hermes 4 405B (Nous Research)" in both the provider registry and the free-model catalog, instead of the mislabelled "Hermes 4 7B" ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#11993](https://github.com/diegosouzapw/OmniRoute/pull/11993)) +- **fix(provider/nous):** inject required user= tag into Nous Research inference requests to resolve upstream 400 "missing tags" error ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#12044](https://github.com/diegosouzapw/OmniRoute/pull/12044)) +- **fix(build):** `npm run build` now fails in one second with a named package and a copy-pasteable fix when npm silently drops an externalised optional native dependency, instead of dying four minutes in with `Module not found: Can't resolve 'better-sqlite3'` ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- **fix(install):** `postinstall` no longer throws `ReferenceError: isAndroid is not defined` — failing the whole `npm install` — when the `better-sqlite3` rebuild fallback times out; the manual-fix guidance is reachable again ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- Fixed the Alibaba free-tier allowlist test that went red on its own once the shipped catalog's `validUntil` (2026-08-27) passed, leaving every PR and `main` with a failing `Unit Tests (1/8)`. The test now builds its own packs with dates it controls, and covers the expired-pack fallback that production has actually been serving. ([#11867](https://github.com/diegosouzapw/OmniRoute/pull/11867)) +- Split the npm registry upload into its own GitHub-hosted job. npm refuses `--provenance` from a self-hosted runner (`422 ... Only "github-hosted" runners are supported`), which blocked the v3.8.50 publish; the heavy verification cannot move to a hosted runner, so it now hands the proven tarball over instead. ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(ui):** the shared `DataTable` loading state no longer reads its decorative ⏳ glyph out to assistive technology, and now carries the same `role="status"` / `aria-live="polite"` / `aria-busy="true"` semantics as `PageLoading` ([#11869](https://github.com/diegosouzapw/OmniRoute/pull/11869)) — thanks @pacocartones +- **fix(api):** Bound the `timeout` accepted by `POST /api/providers/[id]/login` to the same 15s–600s window the Adobe Firefly and Conol browser-login paths already enforce, so a request can no longer pin a headful browser and the single active-login slot with an arbitrarily large poll budget. ([#11871](https://github.com/diegosouzapw/OmniRoute/pull/11871)) — thanks @pacocartones +- **fix(api):** Stop the tunnel and MITM routes from returning raw child-process error text, which disclosed host paths, binary install locations and Tailscale `tskey-*` credentials in a body some of these routes serve to non-loopback callers, and return a real 400 instead of a framework 500 when a tunnel request body fails validation. ([#11872](https://github.com/diegosouzapw/OmniRoute/pull/11872)) — thanks @pacocartones +- **fix(open-sse):** stream TTFT/ITL are sampled from a monotonic clock (`performance.now`) instead of `Date.now`, so an NTP correction or wall-clock jump can no longer inflate a genuine TTFT or produce a negative one that the `ttft >= 0` guard silently drops — protecting the router's OTel/EWMA/`usage_history`/speed-ranking signals ([#11873](https://github.com/diegosouzapw/OmniRoute/pull/11873)) — thanks @pacocartones +- **fix(providers):** xAI `reasoning_effort: "xhigh"` now reaches grok-4.6+ instead of being silently clamped to `"high"` ([#11879](https://github.com/diegosouzapw/OmniRoute/pull/11879)) — thanks @NoxzRCW +- **fix(dashboard):** the providers page no longer crashes into the error boundary when a provider id collides with an `Object.prototype` member (`constructor`, `__proto__`); icon lookups are own-property guarded ([#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)) — thanks @NoxzRCW +- **fix(skills):** injected skill tools declared in shorthand (`{"content": "string"}`) now forward valid JSON Schema, unblocking providers that validate tool schemas strictly such as Zhipu GLM on the Console Go tier ([#11881](https://github.com/diegosouzapw/OmniRoute/pull/11881)) — thanks @NoxzRCW +- **fix(api):** `POST /api/playground/simulate-route` now surfaces `combo-ref` and `provider-wildcard` persisted combo steps with a specific warning (naming the referenced combo, or the unresolved `provider/model` wildcard) instead of folding them into a generic "unsupported step" count; a `provider-wildcard` step is also now included as an unresolved target so the operator can see it is in the route (ported from [#11882](https://github.com/diegosouzapw/OmniRoute/pull/11882) — thanks @NoxzRCW). ([#11930](https://github.com/diegosouzapw/OmniRoute/pull/11930)) +- **fix(translator):** the streaming OpenAI→Claude translator keeps upstream usage, including prompt-cache tokens, when it arrives on a trailing `choices: []` chunk (Fireworks and any upstream using `stream_options.include_usage`) ([#11883](https://github.com/diegosouzapw/OmniRoute/pull/11883)) — thanks @NoxzRCW +- **fix(cli):** `omniroute update --apply` now tells you explicitly whether a running server was detected and, if so, that you must run `omniroute restart` to apply the update — it never restarted anything and previously implied the update was already live once files were installed. The dashboard's npm-mode Update flow (`/api/system/version`) now tries OmniRoute's own PID-file-managed supervisor before falling back to pm2, and reports an honest "restart required" step instead of a silent pm2-only "skipped" that read like a completed update. The server-side latest-version lookup backing the dashboard's update banner also gained `--prefer-online`, closing the same stale-npm-cache class already fixed in the CLI's own copy for #4376 ([#11885](https://github.com/diegosouzapw/OmniRoute/issues/11885)). ([#12005](https://github.com/diegosouzapw/OmniRoute/pull/12005)) +- **fix(resilience):** clear persisted LKGP pins when a target suffers connection/provider exhaustion or is skipped before dispatch due to cooldown/exhaustion/unavailability, preventing subsequent requests from repeatedly prioritizing known-dead providers ([#11911](https://github.com/diegosouzapw/OmniRoute/issues/11911)). ([#12013](https://github.com/diegosouzapw/OmniRoute/pull/12013)) — thanks @HouMinXi +- **fix(sse):** the OpenAI→Claude stream translator now defers the terminal `message_delta`/`message_stop` emission until the real usage block has arrived (or a genuine end-of-stream flush forces it) instead of emitting it immediately on `finish_reason` — previously, when the trailing usage-only chunk (`{"choices":[],"usage":{...}}`) arrived *after* the `finish_reason` chunk (the normal order for Fireworks/vLLM/Together and other `stream_options.include_usage` upstreams), the client-visible `message_delta` still carried stale/zero usage even though `state.usage` was internally corrected too late to matter (ported from [#11915](https://github.com/diegosouzapw/OmniRoute/pull/11915) — thanks @HouMinXi). ([#11933](https://github.com/diegosouzapw/OmniRoute/pull/11933)) +- **fix(sse):** `stripStore()` now forces `store=false` for stateless OpenAI-compatible Responses-API targets (unless the connection has explicitly opted in via `providerSpecificData.openaiStoreEnabled`), instead of only handling the `openai`/`agentrouter` cases — a client-supplied `store` value previously passed through untouched to backends that don't actually persist responses server-side ([#11916](https://github.com/diegosouzapw/OmniRoute/pull/11916) — thanks @HouMinXi). +- **fix(models):** custom provider-node models (synced, custom, and alias-backed) now appear under their configured prefix in the unified catalog when the operator's model-id prefix mode is canonical, instead of being dropped whenever alias-inclusion was otherwise disabled ([#11918](https://github.com/diegosouzapw/OmniRoute/pull/11918) — thanks @HouMinXi). +- **fix(models):** the unified model catalog now suppresses stale static registry models (including effort-tier variants) for any provider whose active connection has an authoritative live synced catalog, not only providers already using exclusive-synced-listing — a connection with `providerUsesAuthoritativeLiveCatalog` previously kept serving both the live-synced models and the stale static rows side by side ([#11919](https://github.com/diegosouzapw/OmniRoute/pull/11919) — thanks @HouMinXi). +- **fix(ui):** `ProviderIcon`'s three remaining unguarded lookups (`PROVIDER_ICON_ALIASES`, `LOCAL_SVG_ALIASES`, `THEMED_SVGS`) now use `Object.hasOwn()` own-property checks — a provider id such as `constructor` or `__proto__` previously resolved through the prototype chain instead of falling through to the unknown-provider CDN fallback (`getLobeProviderIcon()` itself was already guarded by [#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)); `ProviderPageHeader`'s `color` field is also now optional, matching the rest of the component's defensive typing (ported from [#11920](https://github.com/diegosouzapw/OmniRoute/pull/11920) — thanks @HouMinXi). ([#11935](https://github.com/diegosouzapw/OmniRoute/pull/11935)) — thanks @NoxzRCW +- fix(ollama): preserve multi-byte UTF-8 content split across stream chunks in the Ollama NDJSON transform, which previously corrupted CJK/emoji into U+FFFD (#11921) — thanks @pacocartones +- **fix(providers):** OrcaRouter chat requests now target `/v1/chat/completions` instead of the bare `/v1` API root, fixing the upstream `404 Invalid URL (POST /v1)` ([#11923](https://github.com/diegosouzapw/OmniRoute/pull/11923)). — thanks @echel0nn +- **fix(plugins):** deliver the `onStreamComplete` event to disk-installed plugins. The event shipped in v3.8.50 (#9669) was emitted internally but had no plugin-facing wiring, so no plugin could ever subscribe: the manifest schema silently dropped `hooks.onStreamComplete`, and the loader/manager only knew the seven legacy hooks. `onStreamComplete` is now a declarable manifest hook, wired through the loader and registered by the manager like the other hooks, and its payload carries a `requestId` so consumers can correlate the stream-completion event with the originating request ([#11934](https://github.com/diegosouzapw/OmniRoute/pull/11934)) — thanks @amaleta / @pacocartones +- **fix(db):** the Qdrant embedding-model dropdown now lists local/self-hosted providers (Ollama, LM Studio, vLLM, etc.) — an active connection is treated as "configured" when the provider allows an optional API key, not only when it has a real key or OAuth, so a running local embedding provider is no longer hidden from the picker ([#11949](https://github.com/diegosouzapw/OmniRoute/issues/11949)) ([#11995](https://github.com/diegosouzapw/OmniRoute/pull/11995)) +- **fix(providers):** Vertex AI Anthropic partner-model discovery now calls the Model Garden `v1beta1` publisher list (`/v1beta1/publishers/anthropic/models`, global) and parses its `publisherModels` envelope, so Claude models auto-synced from Vertex populate the active live catalog and route at request time instead of returning `Model '' is not available in the active live catalog` ([#11991](https://github.com/diegosouzapw/OmniRoute/issues/11991)) — thanks @fabioluissilva ([#11998](https://github.com/diegosouzapw/OmniRoute/pull/11998)) +- **fix(cli):** support `--base-url` alongside `--endpoint` in `omniroute nodes add`, `update`, and `validate` subcommands to prevent global `--base-url` shadowing issues ([#11999](https://github.com/diegosouzapw/OmniRoute/issues/11999)). ([#12033](https://github.com/diegosouzapw/OmniRoute/pull/12033)) — thanks @HouMinXi +- **fix(providers):** `cloudflare-ai` no longer refuses image content parts for every Workers AI model ([#12002](https://github.com/diegosouzapw/OmniRoute/pull/12002)) — the plain-string `content` requirement behind #2539 is carried by the _model_ schema, not by the `/ai/v1/chat/completions` endpoint (measured: an all-text part array returns 200 on `@cf/mistralai/mistral-small-3.1-24b-instruct`, `@cf/meta/llama-4-scout-17b-16e-instruct` and `@cf/meta/llama-3.3-70b-instruct-fp8-fast`, and 400 on the text-only `@cf/qwen/qwen2.5-coder-32b-instruct`). `transformRequest()` flattened every array and threw on the first non-text part (#6390), so image input was refused for vision-capable Cloudflare models that accept it. All-text arrays are still flattened — the one shape every model accepts — while an array carrying a non-text part is passed through untouched, so the attachment is still never silently dropped. Regression guards: `tests/unit/cloudflare-ai-image-parts-6390.test.ts`. — thanks @davidlinfr +- **fix(resilience):** decouple the limiter-managed execution backstop from the queue-wait budget — new `requestQueue.executionMaxWaitMs` (env `RATE_LIMIT_EXECUTION_MAX_WAIT_MS`, default 600000 = 10 min) now feeds Bottleneck's post-dispatch `expiration`, while `requestQueue.maxWaitMs` keeps its documented queue-wait semantics. Previously the queue-wait budget doubled as the execution expiration, so legitimate long-running calls on non-incremental gateways (whole generation buffered before the first upstream byte, e.g. Console Go / Command Code tiers serving GLM models) were killed mid-flight at the queue budget with a false 504 `RATE_LIMIT_EXECUTION_TIMEOUT` — the local limiter undercut the provider-aware upstream fetch-start timeouts. The surfaced 504 message now names `requestQueue.executionMaxWaitMs`; the error keeps the #4165 guarantees (disclaims an upstream timeout, preserves the Bottleneck error as `cause`, branded code + trusted provenance, classified request-scoped so combo falls back). A real queue-wait bound (the `Promise.race` around `limiter.schedule()` sketched in #9533) remains future work. (#12025) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **Call logs:** keep the `error` field when an artifact exceeds the storage cap, instead of replacing it with the omission marker. The error is the only field that says *why* a request failed and is typically ~90 bytes next to the multi-hundred-KB bodies that trip the cap, so dropping it left a size-limited row undiagnosable — a provider outage, a local timeout and an upstream 400 all rendered identically. It is now preserved at every fallback stage, truncated to 4KB if it is itself large ([#12026](https://github.com/diegosouzapw/OmniRoute/issues/12026)). ([#12095](https://github.com/diegosouzapw/OmniRoute/pull/12095)) — thanks @ntdat812 +- **fix(diagnostics):** preserve the error field (truncated to 4KB with a `[truncated: …]` suffix) in every call-log artifact size-limit fallback stage. Previously the minimal fallback replaced the error with `[omitted: call log artifact size limit exceeded]`, so an oversized artifact row showed nothing about WHY the request failed — e.g. 91 of 847 opencode-go 504 rows on one production instance were undiagnosable from the dashboard. Oversized request/response bodies are still omitted exactly as before; the error cap is independent of the payload sizes that tripped the fallback. (#12026) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **fix(sse):** OpenAI Responses clients that declare the native `web_search` tool now receive a spec-shaped `web_search_call` output item with `action.sources` alongside the preserved function-call round-trip, so search results executed through OmniRoute's own search backend are consumable by standard Responses clients (Codex, pi-web-access, …). ([#12031](https://github.com/diegosouzapw/OmniRoute/pull/12031)) — thanks @watchingdogs +- **fix(cli):** use in-thread alias resolver hooks on modern runtimes to avoid deprecation noise and improve Node.js forward compatibility ([#12073](https://github.com/diegosouzapw/OmniRoute/issues/12073)). ([#12083](https://github.com/diegosouzapw/OmniRoute/pull/12083)) — thanks @xiaoyaner0201 +- **fix(combo):** an operator-set **Agent Features → Context length** on a combo is now honored at request time. The value was persisted and advertised through `/v1/models`, but `resolveComboContextLimit()` never consulted it — so a multi-target combo whose members carry no per-model window fell through to the provider's generic `defaultContextLength` (openrouter 128000, command-code 200000) and rejected large requests with `Input exceeds context window … limit 128000` despite the combo being explicitly sized much larger. An identical single-target combo worked, because it collapses to its concrete target before the guard runs. Invalid values (0/negative/NaN/Infinity) are ignored, so the existing target → combo-min → fallback order is unchanged. ([#12090](https://github.com/diegosouzapw/OmniRoute/pull/12090)) — thanks @adivekar-utexas +- **fix(sse):** passthrough streams now estimate usage on finish when upstream closes without usage even with `stream_options.include_usage` — avoids `0 tokens / 0%` for providers that stay silent (and correctly handles trailing empty-choices usage) ([#12151](https://github.com/diegosouzapw/OmniRoute/pull/12151)) — thanks @maxmad64bis +- **fix(combos):** clearing an agent feature in the combos editor now persists — unchecking context cache protection, or emptying the system message or tool filter, sends an explicit `null` instead of dropping the field from the `PUT` body, which the update merge read as "leave unchanged" ([#12177](https://github.com/diegosouzapw/OmniRoute/pull/12177)) — thanks @foreveryh / @ntdatt812 +- **fix(memory):** self-hosted embedding endpoints now vectorize — the vector width is measured from the first embedding that comes back instead of being read from a registry that cannot describe them, so `vec_memories` is created and memories stop piling up unvectorized behind a green health check ([#12180](https://github.com/diegosouzapw/OmniRoute/pull/12180)) — thanks @kanade-hoshino / @ntdatt812 +- **fix(translator):** the leading `system` message now reaches Responses-API upstreams when its `content` is a content-part array — it was read as `typeof content === "string" ? content : ""`, so a prompt-caching client (Anthropic `cache_control`, the shape LiteLLM and the Anthropic SDK emit) had its entire system prompt replaced by an empty `instructions`. The request was still accepted with a normal `prompt_tokens` count, so the model answered with no instructions and nothing in the response said they were missing. Mid-conversation system turns already handled the array shape ([#7056](https://github.com/diegosouzapw/OmniRoute/pull/7056)); only the first one did not ([#12206](https://github.com/diegosouzapw/OmniRoute/issues/12206)). Regression guard: `tests/unit/translator-openai-responses-system-content-parts.test.ts`. ([#12207](https://github.com/diegosouzapw/OmniRoute/pull/12207)) — thanks @Gorillaz322 +- **fix(free-tier):** `/api/free-tier/summary` no longer computes its totals from a Radar feed built before the catalog the running release ships. When the cached feed is older — or carries no build date at all — the route answers from the shipped catalog, resolved through the operator's local model state so disabled and tombstoned models stay out of the numbers ([#12215](https://github.com/diegosouzapw/OmniRoute/pull/12215)). — thanks @maxmad64bis +- **fix(oauth):** Keep a Claude personal workspace and a Team organization as separate connections — they share the same email and `accountUUID`, so the email-only OAuth dedup let the second login overwrite the first account's tokens; `organizationUUID` now disambiguates them, the way `workspaceId` does for Codex ([#12222](https://github.com/diegosouzapw/OmniRoute/pull/12222)) — thanks @davidebaraldo +- **fix(resilience):** a 402 on a single paid model of a passthrough/gateway provider (e.g. `kilo-gateway`, `ollama-cloud`) no longer terminalizes the whole connection with a never-auto-recovered `credits_exhausted` status — only the paid model is locked out, so free models on the same key keep serving. 402 variant of [#3027](https://github.com/diegosouzapw/OmniRoute/issues/3027). Single-credential providers are unaffected — a 402 there is still treated as the key being genuinely out of credit ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239) / [#10616](https://github.com/diegosouzapw/OmniRoute/issues/10616)) ([#12242](https://github.com/diegosouzapw/OmniRoute/issues/12242)) — thanks @brick30llc-ctrl ([#12266](https://github.com/diegosouzapw/OmniRoute/pull/12266)) — thanks @geekyNads +- **fix(sse):** trust `finish_reason: "length"`/`"max_tokens"` over the reasoning-consumed-token ratio in response quality validation, so a reasoning model truncated below the old 90% threshold correctly fails and retries instead of returning empty content as a silent "success" ([#12262](https://github.com/diegosouzapw/OmniRoute/pull/12262)) — thanks @brick30llc-ctrl +- **fix(settings):** `PUT /api/settings/cache-config` now persists `alwaysPreserveClientCache` to the flat general settings the runtime cache-control policy actually reads; previously the value landed in the databaseSettings "cache" section and was silently ignored, so the endpoint had no effect on `cache_control` passthrough ([#12304](https://github.com/diegosouzapw/OmniRoute/pull/12304)) — thanks @davidebaraldo +- **fix(grok-cli):** treat omitted SuperGrokPro `creditUsagePercent` as 0% used so Provider Limits still renders a weekly bar (proto3 zero-elision) ([#12312](https://github.com/diegosouzapw/OmniRoute/pull/12312)) — thanks @HouMinXi +- **fix(combo):** Expose the two Auto-Combo scoring factors nobody could set — the combo validation schema and the dashboard weight sliders both declared 13 of the scorer's 15 factors, so `connectionDensity` (spreads load across a provider's connections) and `quality` were dropped on save and offered nowhere. The sliders also shipped their own default table that differed from the engine's on every non-zero factor and summed to 1.05, so the percentages shown next to them added up to 105%. Both lists now match `DEFAULT_WEIGHTS`, and a test keeps them there. Note that a combo whose stored `weights` omitted the two keys was effectively running with them at zero and the other thirteen renormalized upward; it now runs with the engine's intended distribution, so its routing does shift ([#12314](https://github.com/diegosouzapw/OmniRoute/pull/12314)) — thanks @maxmad64bis +- **fix(docs):** The free-tier reference no longer says its numbers come "confidence tagged per row" — no catalog entry carries a confidence tag and the API serves none, so every figure on that page is an estimate of the same, unstated quality. The page now states what an entry does vouch for: an independently documented hard stop (set by hand with the source in a comment, never defaulted to `true`) and a prompt-training disclosure, both with live counts the `check:docs-counts` gate keeps honest ([#12318](https://github.com/diegosouzapw/OmniRoute/pull/12318)) — thanks @maxmad64bis +- **fix(usage):** `adobe-firefly` and `firefly` have had a working usage fetcher since Adobe Firefly landed, but neither was ever added to the registration list, so the provider-plugin manifest, `genericQuotaFetcher` and the free-access quota cache all reported them as having no usage support — while `USAGE_SUPPORTED_PROVIDERS` said the opposite. Both are now declared, which also means their credit balance is fetched like any other declared provider's: `registerGenericQuotaFetchers` now registers a generic quota fetcher for them, and `resolveFreeAccessState` no longer returns early. A test holds the registration list to the dispatcher's switch in both directions, which is what the module's own docstring already asked for in prose ([#12321](https://github.com/diegosouzapw/OmniRoute/pull/12321)) — thanks @maxmad64bis +- **fix(quota):** drop the generic quota cache (agy / Antigravity / Claude OAuth) on an upstream 429 so reset-aware scoring does not keep a 60s stale snapshot, and force-refresh the next usage fetch so inner provider caches cannot recache the same window ([#12325](https://github.com/diegosouzapw/OmniRoute/pull/12325)) — thanks @HouMinXi +- **fix(combos):** deleting a combo now clears its persisted LKGP pins instead of leaving unreachable `key_value` rows behind ([#12326](https://github.com/diegosouzapw/OmniRoute/issues/12326)) ([#12425](https://github.com/diegosouzapw/OmniRoute/pull/12425)) — thanks @KrzysiekSko +- **fix(sse):** Keep ZWNJ (U+200C) and ZWJ (U+200D) in assistant text, reasoning and tool-call arguments — Persian/Kurdish half-space (`ارائه‌دهنده`), Arabic/Indic shaping and emoji sequences no longer lose them; the response de-obfuscation now removes joiners only between ASCII word characters, where the request side inserts them ([#12186](https://github.com/diegosouzapw/OmniRoute/issues/12186)) — thanks @rezjalibd ([#12359](https://github.com/diegosouzapw/OmniRoute/pull/12359)) — thanks @pacocartones +- **fix(resilience):** count resolved upstream 5xx results against the provider circuit breaker on the chat path — `CircuitBreaker.execute()` no longer reads a resolved `{ success: false, status: 5xx }` as a success that cancels the call-site failure, so a provider answering 503s now trips its breaker instead of staying `CLOSED` at `failureCount: 1`; single-model and combo dispatches are each accounted exactly once ([#12254](https://github.com/diegosouzapw/OmniRoute/issues/12254)) ([#12360](https://github.com/diegosouzapw/OmniRoute/pull/12360)) — thanks @pacocartones +- **fix(providers):** resolve the Codex quota auto-ping model from the live provider catalog and lifecycle registry instead of the retired `gpt-5.1-codex-mini`, and pause the ping with one actionable warning when no selectable Codex model exists rather than retrying a shut-down id every cooldown window ([#11905](https://github.com/diegosouzapw/OmniRoute/issues/11905)) ([#12361](https://github.com/diegosouzapw/OmniRoute/pull/12361)) — thanks @pacocartones +- **fix(api):** keep the `{created, data}` wrapper on combo-routed `/v1/images/generations` responses and default Codex image results to `b64_json` on both `/v1/images/generations` and `/v1/images/edits` so Codex CLI's built-in `image_gen` can decode them ([#12268](https://github.com/diegosouzapw/OmniRoute/issues/12268)) ([#12362](https://github.com/diegosouzapw/OmniRoute/pull/12362)) — thanks @pacocartones +- **fix(sse):** Name the shadowed custom provider node when a built-in provider id/alias (e.g. `openference` → `of`) reserves the prefix of an existing OpenAI/Anthropic-compatible node, so the runtime `No active credentials for provider: ` error explains that the prefix routed to the built-in and never reached the node's healthy connections, instead of contradicting the dashboard ([#11943](https://github.com/diegosouzapw/OmniRoute/issues/11943)) — thanks @morpheus9393 ([#12365](https://github.com/diegosouzapw/OmniRoute/pull/12365)) — thanks @pacocartones +- **fix(i18n):** wrap `ccOnboardingKeyPlaceholder` in ICU single quotes across all 43 locales so angle brackets render literally instead of being parsed as rich-text tags, which crashed the Claude Code onboarding block with `INVALID_MESSAGE: INVALID_TAG` ([#12302](https://github.com/diegosouzapw/OmniRoute/issues/12302)) ([#12369](https://github.com/diegosouzapw/OmniRoute/pull/12369)) — thanks @gonisulaimann +- **fix(models):** publish `effort_tiers` on Kimi K3's synced base-model entries (`kmca/k3`, `kmca/k3-256k`) so catalog-only clients (OpenCode, plain SDK pickers) can see and select the reasoning tiers (`low`/`high`/`max`) the synced metadata already carried — the `isSkippedEffortProvider` gate no longer suppresses tier visibility on those base entries, while synthetic `-` variant generation stays prevented and Codex/GLM base models remain excluded unchanged ([#12299](https://github.com/diegosouzapw/OmniRoute/issues/12299)) ([#12371](https://github.com/diegosouzapw/OmniRoute/pull/12371)) — thanks @gonisulaimann +- **fix(guardrails):** keep `auto`/`auto/*` virtual combos exempt from the Vision Bridge `fixedModel` credential guard so a combo target is passed through instead of silently falling back to global auto-selection ([#12237](https://github.com/diegosouzapw/OmniRoute/issues/12237)) ([#12373](https://github.com/diegosouzapw/OmniRoute/pull/12373)) — thanks @pacocartones +- **fix(combo):** capability-filter exhaustion caused by `max_tokens` above every target's known output limit now reports that reason (requested `max_tokens` vs the pool's highest known ceiling) instead of the unrelated "supports structured output" message ([#12229](https://github.com/diegosouzapw/OmniRoute/issues/12229)) — thanks @DW-MediaLab ([#12374](https://github.com/diegosouzapw/OmniRoute/pull/12374)) — thanks @pacocartones +- **fix(auth):** the `least-used` account strategy now prefers accounts without backoff before falling back to oldest `lastUsedAt`, the same tie-break `round-robin` already applies, so a failover no longer lands on a just-rate-limited account for a single request ([#12279](https://github.com/diegosouzapw/OmniRoute/issues/12279)) — thanks @tenshiak ([#12375](https://github.com/diegosouzapw/OmniRoute/pull/12375)) — thanks @pacocartones +- **fix(docker):** the `chatgpt-web-codex-browser` image now finds the Chrome binary under `chrome-linux64/` (Chrome for Testing layout in `playwright:v1.62.0-noble`) as well as the legacy `chrome-linux/`, so the container no longer crash-loops with `exec: --headless=new: not found` ([#12024](https://github.com/diegosouzapw/OmniRoute/issues/12024)) ([#12376](https://github.com/diegosouzapw/OmniRoute/pull/12376)) — thanks @pacocartones +- **fix(providers):** declare `groq/compound` and `allam-2-7b` as non-reasoning models in the curated Groq registry so `reasoning_effort` / `output_config.effort` / `thinking` from Claude Code are stripped instead of forwarded, which Groq rejected with HTTP 400 ([#12134](https://github.com/diegosouzapw/OmniRoute/issues/12134)) ([#12379](https://github.com/diegosouzapw/OmniRoute/pull/12379)) — thanks @pacocartones +- **fix(executors):** `OpencodeExecutor` no longer forces a direct connection when the connection has a proxy assigned in Proxy Management but no per-account proxies: the single-account fast path used to wrap the upstream dispatch in the direct-egress sentinel, discarding the ambient proxy context the chat handler had pinned from `proxy_assignments`, so API-key `opencode`/`opencode-go` connections egressed from the host IP (and hit geoblocks) despite the assignment. The direct pin is now applied only when no ambient proxy context exists ([#11894](https://github.com/diegosouzapw/OmniRoute/issues/11894) — thanks @hizzt) ([#12380](https://github.com/diegosouzapw/OmniRoute/pull/12380)) — thanks @pacocartones +- **fix(api):** `GET /v1/models` with `MODELS_CATALOG_PREFIX_MODE=canonical` (or `?prefix=canonical`) now lists providers whose registry alias is undefined or equal to their own id (Antigravity, Antigravity CLI and other self-aliased built-ins) — their single `provider/model` id was dropped by the alias/canonical duplicate guard in the static, synced, custom and alias-backed catalog loops ([#12058](https://github.com/diegosouzapw/OmniRoute/issues/12058)) — thanks @cheynetom ([#12381](https://github.com/diegosouzapw/OmniRoute/pull/12381)) — thanks @pacocartones +- **fix(translator):** Drop replayed `thinking` blocks that carry no signature (the shape produced from cross-provider `reasoning_content`) instead of stamping the default Claude signature on them, which Anthropic rejected with `400 Invalid signature in thinking block` on the next turn served by an Anthropic rung ([#12105](https://github.com/diegosouzapw/OmniRoute/issues/12105)) — thanks @atescivitci-cmd ([#12386](https://github.com/diegosouzapw/OmniRoute/pull/12386)) — thanks @pacocartones +- **fix(cli):** Resolve Bun's `--preload` polyfill path against the package root instead of `dist/`, so `omniroute` installed with `bun install -g` no longer crashes at startup with `error: preload not found …/dist/open-sse/utils/setupPolyfill.ts` ([#11980](https://github.com/diegosouzapw/OmniRoute/issues/11980)) — thanks @joglomedia ([#12387](https://github.com/diegosouzapw/OmniRoute/pull/12387)) — thanks @pacocartones +- **fix(providers):** `gemini-business` now publishes its model catalog — `/v1/models` and `/v1/providers/gemini-business/models` list the 12 enterprise Gemini ids the executor understands instead of returning an empty list (#12107) ([#12389](https://github.com/diegosouzapw/OmniRoute/pull/12389)) — thanks @pacocartones +- **fix(db):** install `busy_timeout` before the SQLite connection's first statement so a process opening the database while another one closes its WAL connection waits out the transient EXCLUSIVE lock instead of dying with `database is locked`, and recognise the drivers' real BUSY/PROTOCOL/IOERR errors as transient in the corruption probe so the same lock no longer renames the database away as corrupt; deflakes `cross-process contenders never both acquire the same connection` (#12394 — thanks @pacocartones) +- **fix(chat-admission):** derive the `chat_admission_busy` 503 `Retry-After` from observed heavyweight-lease occupancy — the larger of the exhausted `OMNIROUTE_CHAT_ADMISSION_QUEUE_MS` window and the time since capacity last turned over, capped at 60 s — instead of a fixed 1 s (structural) / 2 s (byte-stage) hint that invited Codex/agent fan-out clients to re-send ~1 MiB `/v1/responses` bodies every second into a gate held for the whole SSE lifetime; an idle gate keeps the historical floors ([#12135](https://github.com/diegosouzapw/OmniRoute/issues/12135)) (#12395 — thanks @pacocartones) +- **fix(api-manager):** the API key permissions modal no longer silently drops `allowedCombos` entries its Combo picker cannot render — routing-rule names such as `rt-*`, which the backend already honours — when "All" is clicked and the key is switched back to "Restrict"; those entries now survive the toggle, are listed read-only under the combo list so the count and the list agree, and are saved back verbatim instead of persisting `[]` (deny-all) (#12397 — thanks @pacocartones) +- **fix(catalog):** write the NUL separator of the catalog connection memo key, the provider serviceKind memo key, the Video Bridge promotion group key and a JSON-exactness test fixture as the `\u0000` escape instead of a raw byte — same runtime value, but the raw byte made git, GitHub and ripgrep treat those files as binary (hidden PR diffs, silently skipped searches); a guard test now keeps raw NUL bytes out of `src/`, `open-sse/` and `tests/` (#12403 — thanks @pacocartones) +- **fix(providers):** add `CLAUDE_CODE_CLIENT_VERSION` and `GITHUB_COPILOT_CLI_VERSION` env overrides so Anthropic/Copilot client-version gates can be unblocked without a rebuild ([#12417](https://github.com/diegosouzapw/OmniRoute/issues/12417)) ([#12632](https://github.com/diegosouzapw/OmniRoute/pull/12632)) — thanks @HouMinXi +- **fix(db):** back-fill `last_ping_at` and `last_pinged_reset_key` on `provider_connections` during schema reconciliation so divergent lineages that skipped `123_quota_auto_ping` still accept quota auto-ping writes ([#12470](https://github.com/diegosouzapw/OmniRoute/pull/12470) — thanks @KooshaPari) +- **fix(ci):** document MIT exceptions for `@eloqnt/{config,format-json,format-po}` (next-intl transitive; locked tarballs omit `license`) and keep the A2A lifecycle vitest off the real SQLite persistence seam ([#12581](https://github.com/diegosouzapw/OmniRoute/issues/12581)) ([#12595](https://github.com/diegosouzapw/OmniRoute/pull/12595)) +- OpenCode plugin `/v1/models` catalog fetch now waits 30s by default and attaches HTTP `statusCode` on 401/5xx so host fallback plugins can hop instead of seeing an untyped AbortError/UnknownError. ([#12607](https://github.com/diegosouzapw/OmniRoute/pull/12607)) — thanks @RaviTharuma +- **CI:** the OpenAPI security-tier gate now mirrors `isAlwaysProtectedPath()` in full — it also reads `ALWAYS_PROTECTED_API_PATTERNS`, so the pattern-gated credential routes (`/api/providers/{id}/{claude,codex}-auth/{export,apply-local}`, GHSA-5926-2w35-7h4q) no longer report as unannotated. (#12605) +- **fix(api):** GET /v1/models no longer waits forever on a hung coalesced catalog rebuild; cold-path waits are bounded (`CATALOG_BUILD_TIMEOUT_MS`, default 8s) and a last-good 200 is served when the rebuild times out ([#12627](https://github.com/diegosouzapw/OmniRoute/issues/12627)). ([#12628](https://github.com/diegosouzapw/OmniRoute/pull/12628)) — thanks @RaviTharuma +- **fix(cursor):** a non-array `tool_calls` on an assistant message no longer crashes the cursor request translator with a `TypeError`; both loops now require an array ([#12689](https://github.com/diegosouzapw/OmniRoute/issues/12689)) ([#12691](https://github.com/diegosouzapw/OmniRoute/pull/12691)) — thanks @soroush5 +- **fix(chat):** Chat Completions no longer return empty `content` after a server-owned memory or skills tool; the first provider send and account/model recovery now share one pipeline so a follow-up round-trip can fill the reply ([#12696](https://github.com/diegosouzapw/OmniRoute/issues/12696)) — thanks @HouMinXi ([#12867](https://github.com/diegosouzapw/OmniRoute/pull/12867)) +- **fix(dashboard):** batch-deleting provider keys no longer toasts failure after a successful delete when the confirm button's click event is forwarded as `onAfter` ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) — thanks @HouMinXi +- **fix(glm):** drop the extra 16th argument to `createSSETransformStreamWithLogger` that TypeScript rejected (TS2554) and that never reached the TransformStream ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) ([#12770](https://github.com/diegosouzapw/OmniRoute/pull/12770)) — thanks @HouMinXi +- **fix(dashboard):** pass `nodeMap` into Runtime `QuotaGroup` so a quota monitor in error/exhausted/alerting no longer throws `ReferenceError: nodeMap is not defined`. ([#12868](https://github.com/diegosouzapw/OmniRoute/pull/12868)) — thanks @HouMinXi +- Restricted API keys whose `allowedModels` lists a combo name no longer skip every combo member at pre-dispatch (`ALL_TARGETS_SKIPPED`). Inner-target filtering still applies when the allow-list is a provider prefix or `disableNonPublicModels` is on ([#12899](https://github.com/diegosouzapw/OmniRoute/pull/12899)). — thanks @HouMinXi +- **fix(combos):** Effort-suffixed combo members inherit the base model's `model_context_overrides` row so priority order is not inverted on large requests ([#12926](https://github.com/diegosouzapw/OmniRoute/pull/12926)) — thanks @HouMinXi +- **fix(catalog):** Picker-added `customModels` enter the dispatch-time live catalog so combo members and bare inference no longer 400 ([#12934](https://github.com/diegosouzapw/OmniRoute/pull/12934)) — thanks @HouMinXi +- **fix(docker):** re-derive the Docker build's worker-pool memory budget from the MEASURED ~4.5 GB per-process RSS (the issue owner's own VPS dmesg OOM-killer reproduction) instead of the stale 2560 MB/worker inference #11419 shipped, and lower `OMNIROUTE_BUILD_WORKERS` 3 → 2 so 1 parent + 1 page-data worker (2 processes × 4.5 GB = 9 GB) fits the 12.288 GB (75%) budget on a 16 GB GitHub Actions runner — the previous default (1 parent + 2 workers = 13.5 GB) still overcommitted the runner and kept "Publish to Docker Hub" failing with `cannot allocate memory` after #11419 merged (#7518). ([#11663](https://github.com/diegosouzapw/OmniRoute/pull/11663)) +- **fix(electron):** the packaged Windows desktop build now passes the #7592 cold-restart smoke end to end. Five defects were found and fixed on the way: (1) optional-pack staging failed on any machine whose `tar` is GNU tar — it reads the drive letter in an absolute `-f C:\...` archive path as a remote rsh host (`Cannot connect to C:`), so staging now passes a bare filename with `cwd` at the tarball directory and surfaces tar stderr on failure; (2) the Electron `files` allowlist was missing `lib/loginHeaderCapture.js`, so the packaged main process crashed at startup with `Cannot find module './lib/loginHeaderCapture'`; (3) electron-builder ≥26 injects an `!**/node_modules/**` ignore into every extraResources pattern list that no positive filter can override, which silently dropped the staged runtime `node_modules` (including the better-sqlite3 N-API prebuild) from `resources/app` — a fresh v3.8.50 desktop build would have shipped with no native SQLite driver and reproduced the #7592 sql.js fallback on every machine; an `afterPack` hook now restores the staged `node_modules` after packing; (4) the packaged-app smoke harness redirected `USERPROFILE` into its temp DATA_DIR, but Electron resolves the Roaming profile from `%USERPROFILE%\AppData\Roaming\` (USERPROFILE takes precedence over the APPDATA env var) and the path service throws instead of creating it, so `requestSingleInstanceLock()` returned false and the app exited(0) silently before `app.whenReady()` — the harness now pre-creates the derived tree, and `ensureSmokeEnvDirs` is exported and unit-tested; (5) the #7592 driver assertion parsed a `[DB] Driver: ...` line that the server's primary DB path never printed (only the unused `openDatabaseAsync()` did) — `getDbInstance()` now emits the same line on open so the guard can actually assert the native driver. Also: the smoke env-allowlist unit test hardcoded POSIX paths and could only pass on Linux/macOS; expectations are now host-agnostic, with new regression tests for the USERPROFILE-derived tree and `tarPack` under absolute Windows-style paths ([#11443](https://github.com/diegosouzapw/OmniRoute/pull/11443)) — thanks @jonlwheat2-gif +- fix(sse): stop the auto-combo candidates inspector from silently dropping model-locked/cooled-down rows (#9133) ([#11994](https://github.com/diegosouzapw/OmniRoute/pull/11994)) — thanks @hartmark +- **fix(providers):** Perplexity Web no longer turns upstream stream failures into successful assistant text; pre-content failures remain eligible for fallback, partial output ends with a structured sanitized error, and failed sessions are not persisted ([#12465](https://github.com/diegosouzapw/OmniRoute/pull/12465)) +- **fix(video-bridge):** preserve chronological video and transcript fusion, source-frame timestamps, transcript provenance, full contact-sheet intervals, and pre-abort callback isolation ([#11681](https://github.com/diegosouzapw/OmniRoute/pull/11681)) +- **Z.ai Web:** HTTP 200 streams carrying an upstream error now terminate with a structured failure instead of assistant text plus a normal stop, preserving partial output while allowing pre-content combo fallback. ([#12454](https://github.com/diegosouzapw/OmniRoute/pull/12454)) +- Absorb `Error [AbortError]: request_signal_aborted` and DOMException AbortError shapes in the process-level client-abort crash guard so routine client disconnects no longer kill the server (exit code 7). ([#12165](https://github.com/diegosouzapw/OmniRoute/pull/12165)) — thanks @alvinveroy +- **fix(sse):** Treat Adapta Web `type:error` SSE events as sanitized non-stream failures instead of empty HTTP 200 completions. ([#12459](https://github.com/diegosouzapw/OmniRoute/pull/12459)) +- **fix(autoCombo):** layer 3 (`models_dev_tier`) no longer scores retired models as premium, merge cross-provider capability rows deterministically, and inherit tier scores through variant ids ([#11508](https://github.com/diegosouzapw/OmniRoute/issues/11508)). `getModelsDevTierFitness` now vetoes ids present in `config/quality/model-lifecycle.json` with status `retired` before any other signal; `loadModelCapabilities()` aggregates per `model_id` (any-non-null-true for booleans, max for `limit_context`) instead of last-write-wins over SQLite's undefined row order; a capabilities miss retries through `resolveScoresAs()`'s base id and reports `models_dev_tier:inherited`, matching layers 1–2 (#11492). Adds `getModelsDevTierFitnessWithSource()` and an `invalidateCapabilitiesCache()` test/ops hook. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) — thanks @Meet6338-X +- fix(sse): accept Claude `content:[]` + `stop_reason: max_tokens`/`tool_use` in `detectMalformedNonStream` (match `isEmptyContentResponse`) so Claude Code `/model` probes with `max_tokens: 1` no longer become a false 502 ([#11568](https://github.com/diegosouzapw/OmniRoute/pull/11568)) — thanks @Seramicx +- **fix(security):** sanitize `request.failed` diagnostics before publishing them to live dashboard listeners and replay history, while keeping status, model, provider, latency, and internal call-log diagnostics intact. ([#12469](https://github.com/diegosouzapw/OmniRoute/pull/12469)) +- **fix(pwa):** do not intercept dashboard navigations so Chrome can fall back from a stale HTTP/3 Alt-Svc advertisement (UDP :20128 is unpublished; F5 on a long-lived tab hung until a new tab opened a fresh TCP connection). ([#12767](https://github.com/diegosouzapw/OmniRoute/pull/12767)) — thanks @HouMinXi +- **fix(executors):** handle DuckDuckGo ERR_BN_LIMIT (418) without retrying — when the upstream returns `418 ERR_BN_LIMIT` (rate-limit/ban), the executor now returns the error immediately instead of burning another VQD acquisition that would only count against the IP limit. The retry logic for `418 ERR_CHALLENGE` (unsolved challenge) remains unchanged. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) ([#12110](https://github.com/diegosouzapw/OmniRoute/pull/12110)) — thanks @oyi77 / @Meet6338-X +- Fixed DuckDuckGo streaming responses losing JSON lines and UTF-8 characters split across network chunks. ([#11528](https://github.com/diegosouzapw/OmniRoute/pull/11528)) — thanks @pacocartones +- Harden generated Cloudflare, Vercel, and Deno relays by stripping hop-by-hop, framing, and proxy authentication headers before forwarding requests upstream. ([#11533](https://github.com/diegosouzapw/OmniRoute/pull/11533)) — thanks @pacocartones +- **feat(grok-cli):** Provider Limits shows grok-cli banked reset credits from `GetRemainingResets` (including a real zero; a failed RPC omits the row) and the existing View credits button now calls `ConsumerUiSvc/RedeemReset` for grok-cli. Live tokens use nested fields 10/20/30 (id + Timestamp), not compact 1/2/3. ([#12805](https://github.com/diegosouzapw/OmniRoute/pull/12805)) — thanks @HouMinXi +- **fix(memory):** Embedding Model Quick select, Embedding Source remote dropdown, and Rerank selector now list every configured provider with embedding/rerank support instead of only chat-catalog text matches plus OpenRouter live discovery; a generic OpenAI-compatible `/embeddings` + Cohere-compatible `/rerank` runtime fallback resolves any configured chat provider's embedding/rerank endpoint, so unlisted providers no longer fail with "Unknown embedding provider"; both memory selectors gained a free-text model override ([#11390](https://github.com/diegosouzapw/OmniRoute/pull/11390)) — thanks @rqzbeh +- Harden SQLite upgrades around the historical migration-074 version collision: missing discovery and inspector tables are replayed atomically, pre-existing databases (including setup-created skeletons) receive reusable content-addressed safety snapshots, and Node test/eval probes without `DATA_DIR` are isolated from the operator database. ([#12435](https://github.com/diegosouzapw/OmniRoute/pull/12435)) +- **fix(dashboard):** Moonshot/Kimi Open Platform voucher and cash leftover percentages follow the bucket balance, so an empty wallet no longer paints those rows as 100% while Available is 0% ([#12733](https://github.com/diegosouzapw/OmniRoute/pull/12733)) — thanks @HouMinXi +- **fix(combo):** return non-retryable HTTP 400 when all candidates for a pinned native Codex turn are unavailable due to model-scoped lockout, terminating the turn cleanly while preserving turn continuity and enabling standard Combo routing on subsequent turns ([#12240](https://github.com/diegosouzapw/OmniRoute/pull/12240)) — thanks @mdigitalbh81 +- Fixed auto-router strategies preserving the selected connection through ranking and dispatch when multiple connections share the same provider and model. ([#11530](https://github.com/diegosouzapw/OmniRoute/pull/11530)) — thanks @pacocartones +- Preserve Chat Completions JSON-object response formats when translating requests to the Responses API. ([#11529](https://github.com/diegosouzapw/OmniRoute/pull/11529)) — thanks @pacocartones +- Fix idempotency fingerprints so Responses API requests include `input` and other semantic generation fields using stable serialization, while excluding credentials and request metadata. ([#11532](https://github.com/diegosouzapw/OmniRoute/pull/11532)) — thanks @pacocartones +- **fix(api):** Generated API CLI commands now enforce required OpenAPI request bodies; Combo test commands forward the required `comboName` body, while API keys created by older writers after migration 149 preserve legacy allow-all Combo access without widening explicit empty allowlists — thanks @marcelokarval ([#12070](https://github.com/diegosouzapw/OmniRoute/pull/12070)) +- **fix(grok-web):** treat upstream streaming failures as failures instead of successful assistant text: error-only streams now fail readiness with HTTP 502, while failures after legitimate content preserve that partial output and terminate through the sanitized stream failure path without a normal `stop` completion. ([#12458](https://github.com/diegosouzapw/OmniRoute/pull/12458)) +- HuggingChat now turns HTTP 200 JSONL generation failures into a sanitized 502 before content, or a fixed public stream failure after partial output, so fallback and request persistence no longer record a false successful stop. ([#12456](https://github.com/diegosouzapw/OmniRoute/pull/12456)) +- **fix(providers):** keep 1min.ai HTTP 200 stream errors out of assistant content, preserve partial output, and expose sanitized terminal errors so pre-content failures can fall back. ([#12466](https://github.com/diegosouzapw/OmniRoute/pull/12466)) +- **fix(sessions):** preserve legacy Sessions data when the additive exclusive-session projection is unavailable, returning an empty projection and warning only once per contiguous outage instead of failing the endpoint; the Sessions badge intentionally reflects the merged legacy and exclusive row count introduced by [#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389) ([#11469](https://github.com/diegosouzapw/OmniRoute/pull/11469)) — thanks @KaspaPulse +- Render Nimble Search with the generic provider icon and serve Opper's proven logo locally. ([#12415](https://github.com/diegosouzapw/OmniRoute/pull/12415)) +- **fix(quality):** `config/quality/provider-assets-provenance.jsonl` was stale against the physical `public/providers/` tree (still listing 225 assets and 5 duplicate-content groups after several provider-retirement PRs removed 85 icon files), which would have made `check:provider-asset-provenance` fail on the next full run. Reconciled the manifest to the real 142-file snapshot (142 asset records, 1 duplicate-content group, 2 newly-registered files marked `unresolved` pending provenance research), repinned `auditedCommit`, and updated the regression test's hardcoded counts to match. ([#11876](https://github.com/diegosouzapw/OmniRoute/pull/11876)) +- fix(ui): wrap quota card footer action buttons to prevent clipping Refresh button on narrow cards — when extra action buttons such as `View credits` are present on Codex cards, allow the footer row to flex-wrap with `shrink-0` buttons so `Refresh now` remains visible and clickable across all responsive card widths ([#11464](https://github.com/diegosouzapw/OmniRoute/pull/11464)) — thanks @adevwithpurpose +- Keep Antigravity Gemini usable when the same connection's Claude weekly quota is empty; generic quota cache stays per-connection for every other provider. ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) — thanks @HouMinXi +- Fixed Tailscale tunnel setup to reject ports outside the valid 1-65535 range and safely install Windows MSI packages from paths containing apostrophes. ([#11534](https://github.com/diegosouzapw/OmniRoute/pull/11534)) — thanks @pacocartones +- Classify Cloudflared and Tailscale process-management routes as local-only and spawn-capable while preserving remote access to read-only tunnel status endpoints. ([#11531](https://github.com/diegosouzapw/OmniRoute/pull/11531)) — thanks @pacocartones +- Electron release workflow: the `publish-npm` job now grants `actions: read` to the reusable `npm-publish.yml` it calls (its `publish` job requests it), which is what made GitHub refuse the whole v3.8.50 run at startup and ship the release with zero desktop assets; a `workflow_dispatch` now builds the requested tag instead of the dispatching branch and can skip the npm leg (`publish_npm=false`) when only re-attaching assets ([#11974](https://github.com/diegosouzapw/OmniRoute/pull/11974)) +- **fix(dashboard):** The Combos page usage guide now reads its dismissal through `useSyncExternalStore` instead of correcting SSR state inside an effect, removing an extra commit of the page tree on every load (and the `react-hooks/set-state-in-effect` error it raised). ([#12671](https://github.com/diegosouzapw/OmniRoute/pull/12671)) +- Electron release: `electron/package-lock.json` regained the optional `electron-builder-squirrel-windows` subtree (13 entries) that `npm ci` had been refusing as out of sync, `electron-release.yml` gained a `build_ref` dispatch input and stops regenerating release notes on a re-attach dispatch, and the npm publish workflow attaches the SBOM to the GitHub Release on dispatch publishes too — so the v3.8.51 tag ships every desktop asset and the SBOM like v3.8.49 did ([#12022](https://github.com/diegosouzapw/OmniRoute/pull/12022)) +- Drained the reds every PR against `release/v3.8.51` was born with: documented the three service-worker build-id variables, registered the six retirement/tunnel tests with the mutation gate, approved `eslint-plugin-react-hooks` in the dependency allowlist, added the six `combo.sort.*` strings to `vi` and `pt-BR`, pointed the ChatGPT Web doc at the real migration-168 test, worded the g4f hint around the member key, and realigned four tests to the retired-provider catalog and the legacy-schema fixtures the retirement migrations touch. ([#11940](https://github.com/diegosouzapw/OmniRoute/pull/11940)) +- **fix(providers):** Claude, Grok, LMArena, Notion, and Perplexity web-cookie transports now use pooled `wreq-js` 3.2 instead of the native sidecar, with all nine supported bindings pinned and audited, and the applicable platform binding plus native-license evidence included in each release artifact ([#12429](https://github.com/diegosouzapw/OmniRoute/pull/12429), supersedes [#11753](https://github.com/diegosouzapw/OmniRoute/pull/11753)). +- **fix(providers):** Zed Hosted streaming failures now trigger fallback before content and end partial streams with a sanitized structured error instead of fake assistant text and a normal-success stop. ([#12455](https://github.com/diegosouzapw/OmniRoute/pull/12455)) +- **fix(test):** capture hook result after render (direct commit `b85d0abb0b`) +- **fix(sync):** repair the two regressions the v3.8.50 sync-back left on release/v3.8.51 (direct commit `5ade9e0851`) +- **revert(models):** drop the memo reset hook ported from #10055 (direct commit `282de841ae`) +- **fix(typecheck):** guard the optional connectionId before the reactive model sync (direct commit `f2825bad75`) +- **fix(quota-share):** release the winner's reserved in-flight slot (#11371) ([#11408](https://github.com/diegosouzapw/OmniRoute/pull/11408)) — thanks @oyi77 +- **fix(providers):** stop silently dropping reasoning effort on opencode families (#10788) ([#11409](https://github.com/diegosouzapw/OmniRoute/pull/11409)) — thanks @oyi77 +- **fix(combo):** enable genuine quota-aware routing for generic providers (antigravity, claude, etc.) ([#11411](https://github.com/diegosouzapw/OmniRoute/pull/11411)) — thanks @HouMinXi +- **fix(oauth):** allow expired token retry before deactivation and activate expired retry loop (#P0) ([#11414](https://github.com/diegosouzapw/OmniRoute/pull/11414)) — thanks @HouMinXi +- **perf(executors):** lazy-load the executor registry — defer class imports + construction to first use (#11220) ([#11421](https://github.com/diegosouzapw/OmniRoute/pull/11421)) — thanks @oyi77 +- **fix:** make release/v3.8.50 CI gates green (run 32786966560 root causes) ([#11450](https://github.com/diegosouzapw/OmniRoute/pull/11450)) — thanks @jonlwheat2-gif +- **fix(sse):** exact-domain cookie match and origin-equality URL assertions ([#11452](https://github.com/diegosouzapw/OmniRoute/pull/11452)) — thanks @hartmark +- **fix(providers):** send Responses API payload during OpenAI-compatible validation ([#11454](https://github.com/diegosouzapw/OmniRoute/pull/11454)) — thanks @tuandinh0801 +- **fix(command-code):** fallback to /alpha/generate for Go plan without Provider API access ([#11455](https://github.com/diegosouzapw/OmniRoute/pull/11455)) — thanks @Chewji9875 +- **fix(responses-continuation):** fail closed on a log-truncated stored input/output array ([#11473](https://github.com/diegosouzapw/OmniRoute/pull/11473)) — thanks @hartmark +- **fix(cli):** write jcode settings as [providers.omniroute] in config.toml ([#11484](https://github.com/diegosouzapw/OmniRoute/pull/11484)) — thanks @hongnoul +- **fix(logging):** raise chat-log array truncation cap to 1000 and unify duplicate implementations ([#11499](https://github.com/diegosouzapw/OmniRoute/pull/11499)) — thanks @hartmark +- **fix:** restore live websocket URL helpers ([#11509](https://github.com/diegosouzapw/OmniRoute/pull/11509)) — thanks @PauloFH +- **perf(core):** eliminate async waterfalls across home, cache, and db operations (#11396) ([#11512](https://github.com/diegosouzapw/OmniRoute/pull/11512)) — thanks @HouMinXi +- **fix(kiro):** add runtime.us-east-1.kiro.dev as first-attempt endpoint ([#11517](https://github.com/diegosouzapw/OmniRoute/pull/11517)) — thanks @rifqiawl +- **fix(providers):** remove duplicate 128-tool truncation in OpencodeExecutor (#11444) ([#11518](https://github.com/diegosouzapw/OmniRoute/pull/11518)) — thanks @HouMinXi +- **fix(providers):** poll onboardUser LRO before classifying BYOP ([#11519](https://github.com/diegosouzapw/OmniRoute/pull/11519)) — thanks @rifqiawl +- **fix(translator):** preserve exact tool name casing for Claude tool_use payloads (#11487) ([#11520](https://github.com/diegosouzapw/OmniRoute/pull/11520)) — thanks @HouMinXi +- **fix(kimi-web):** align health probe with executor domain ([#11521](https://github.com/diegosouzapw/OmniRoute/pull/11521)) — thanks @pacocartones +- **fix(cli):** preserve existing admin password during setup ([#11522](https://github.com/diegosouzapw/OmniRoute/pull/11522)) — thanks @pacocartones +- **fix(mitm):** keep Windows DNS elevation off the POSIX sudo path (#11430) ([#11542](https://github.com/diegosouzapw/OmniRoute/pull/11542)) — thanks @official-burak +- **fix(sse):** scale chat admission by ingest byte budget instead of a fixed request count ([#11548](https://github.com/diegosouzapw/OmniRoute/pull/11548)) — thanks @TheDemonTuan +- **fix:** accept Gemini Web cookie exports ([#11554](https://github.com/diegosouzapw/OmniRoute/pull/11554)) — thanks @rafacpti23 +- **fix(i18n):** complete Brazilian Portuguese translations ([#11558](https://github.com/diegosouzapw/OmniRoute/pull/11558)) — thanks @rafacpti23 +- **fix(sse):** explicit types for openai-responses pureHelpers — clears last failing core typecheck gate ([#11567](https://github.com/diegosouzapw/OmniRoute/pull/11567)) — thanks @jonlwheat2-gif +- **fix(opencode):** hydrate Proxy Pool references for no-auth connections ([#11584](https://github.com/diegosouzapw/OmniRoute/pull/11584)) — thanks @AStupidBear +- **fix(ci):** resolve inherited release/v3.8.51 base-red CI failures ([#11608](https://github.com/diegosouzapw/OmniRoute/pull/11608)) — thanks @jonlwheat2-gif +- **fix(db):** fallback load STORAGE_ENCRYPTION_KEY from env files ([#11614](https://github.com/diegosouzapw/OmniRoute/pull/11614)) — thanks @benzntech +- **fix(providers):** mark Brave Search as no longer having a free tier ([#11621](https://github.com/diegosouzapw/OmniRoute/pull/11621)) — thanks @turbolego +- **fix:** pass tool result content through as string in OpenAI/Claude -> Gemini translation ([#11624](https://github.com/diegosouzapw/OmniRoute/pull/11624)) — thanks @Hsia97 +- **fix(sse):** scope credential health to exact key ([#11628](https://github.com/diegosouzapw/OmniRoute/pull/11628)) — thanks @KaspaPulse +- **fix(install):** guard prepare script against missing husky ([#11634](https://github.com/diegosouzapw/OmniRoute/pull/11634)) — thanks @Prajeeth-12 +- **fix(security):** redact NVIDIA_API_KEY at the log sink in the ad-hoc diag script ([#11639](https://github.com/diegosouzapw/OmniRoute/pull/11639)) +- **fix(types):** clear the two type regressions red on release/v3.8.51 ([#11640](https://github.com/diegosouzapw/OmniRoute/pull/11640)) — thanks @jonlwheat2-gif +- **fix(build):** resolve workspace: protocol dependencies to concrete semver versions during prepublish (#11540) ([#11641](https://github.com/diegosouzapw/OmniRoute/pull/11641)) — thanks @HouMinXi +- **fix(search):** properly resolve configured search connection in /v1/responses pipeline (#11524) ([#11642](https://github.com/diegosouzapw/OmniRoute/pull/11642)) — thanks @HouMinXi +- **fix(authz):** trust cf-connecting-ip for via-proxy detection when peer is Cloudflare edge (#11514) ([#11643](https://github.com/diegosouzapw/OmniRoute/pull/11643)) — thanks @HouMinXi +- **fix(security):** compare the OIDC state cookie in constant time ([#11646](https://github.com/diegosouzapw/OmniRoute/pull/11646)) +- **fix(security):** namespace the dedup hash by the calling API key ([#11649](https://github.com/diegosouzapw/OmniRoute/pull/11649)) +- **fix(models):** gate the Codex-native catalog loop on prefix mode (#11632) ([#11662](https://github.com/diegosouzapw/OmniRoute/pull/11662)) — thanks @xiaoyaner0201 +- **fix(dashboard):** project Codex routing quota in account pools ([#11666](https://github.com/diegosouzapw/OmniRoute/pull/11666)) — thanks @KaspaPulse +- **fix(ddgw):** normaliza system no upstream da release v3.8.51 ([#11692](https://github.com/diegosouzapw/OmniRoute/pull/11692)) — thanks @RhianB14 +- **fix(memory):** honor category filter in GET /api/memory ([#11699](https://github.com/diegosouzapw/OmniRoute/pull/11699)) — thanks @Sabeekhann +- **fix(ci):** detect stale workspace lockfile entries ([#11714](https://github.com/diegosouzapw/OmniRoute/pull/11714)) — thanks @pacocartones +- **fix(ci):** stamp dist/BUILD_SHA before the npm publish provenance gate ([#11721](https://github.com/diegosouzapw/OmniRoute/pull/11721)) +- **fix(docker):** decouple the best-effort Bun image from the release manifest ([#11724](https://github.com/diegosouzapw/OmniRoute/pull/11724)) +- **perf(compression):** avoid cloning memo misses twice ([#11727](https://github.com/diegosouzapw/OmniRoute/pull/11727)) — thanks @RaviTharuma +- **fix(docker):** let the bun digest artifact be absent, not fatal ([#11740](https://github.com/diegosouzapw/OmniRoute/pull/11740)) +- **fix(shared):** block cloud-metadata hosts under default remote-image guard ([#11755](https://github.com/diegosouzapw/OmniRoute/pull/11755)) — thanks @santosraju99-hub +- **fix(api):** preserve caller-provided X-Correlation-Id on chat completions ([#11760](https://github.com/diegosouzapw/OmniRoute/pull/11760)) — thanks @aniruddhaadak80 +- **fix(resilience):** lock permanently retired models instead of short backoff (Gemini ban prevention) ([#11762](https://github.com/diegosouzapw/OmniRoute/pull/11762)) — thanks @turbolego +- **fix:** mark Vercel AI Gateway as passthroughModels ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **fix(resilience):** stop hammering permanently-moved endpoints and billing-suspended accounts ([#11774](https://github.com/diegosouzapw/OmniRoute/pull/11774)) — thanks @turbolego +- **fix(lease):** remove global static reservation and gate routing on live active lease occupancy ([#11775](https://github.com/diegosouzapw/OmniRoute/pull/11775)) — thanks @TheDemonTuan +- **fix(ci):** size the install-upgrade gate to a measured run, and log the pack cost ([#11776](https://github.com/diegosouzapw/OmniRoute/pull/11776)) +- **fix(pwa):** stop serving the stale shell after deploys ([#11779](https://github.com/diegosouzapw/OmniRoute/pull/11779)) — thanks @HouMinXi +- **fix(combo):** lock GitHub models rejected as "not supported" for future requests ([#11781](https://github.com/diegosouzapw/OmniRoute/pull/11781)) — thanks @turbolego +- **fix(cli):** probe both IPv4 and IPv6 loopback for server readiness (#11766) ([#11794](https://github.com/diegosouzapw/OmniRoute/pull/11794)) — thanks @vermasomesh835 +- **fix(sse):** preserve CLIProxyAPI wrappers per connection ([#11797](https://github.com/diegosouzapw/OmniRoute/pull/11797)) — thanks @RaviTharuma +- **fix(providers):** honor model auto-fetch opt-in on create ([#11798](https://github.com/diegosouzapw/OmniRoute/pull/11798)) — thanks @RaviTharuma +- **fix(dashboard):** make model auto-fetch order independent ([#11805](https://github.com/diegosouzapw/OmniRoute/pull/11805)) — thanks @RaviTharuma +- **fix(docker):** make GHA cache exports best effort ([#11806](https://github.com/diegosouzapw/OmniRoute/pull/11806)) — thanks @RaviTharuma +- **fix(kiro):** do not permanently ban on 'User is not authorized to make this call' ([#11809](https://github.com/diegosouzapw/OmniRoute/pull/11809)) — thanks @Deftera186 +- **fix(sse):** keep cache-write tokens in OpenAI-shaped usage ([#11814](https://github.com/diegosouzapw/OmniRoute/pull/11814)) — thanks @TheDemonTuan +- **fix(docs):** resolve relative markdown and wiki links across Fumadocs and GitHub wiki ([#11834](https://github.com/diegosouzapw/OmniRoute/pull/11834)) — thanks @AndrianBalanescu +- **fix(chat):** preserve unstripped model string for passthrough provider routing ([#11840](https://github.com/diegosouzapw/OmniRoute/pull/11840)) — thanks @raheemuddin786 +- **fix(mcp):** avoid async initialization inside sync esm wrappers (#11569) ([#11849](https://github.com/diegosouzapw/OmniRoute/pull/11849)) — thanks @HouMinXi +- **fix(sse):** preserve coherent markdown token boundaries across stream translation chunks (#11606) ([#11851](https://github.com/diegosouzapw/OmniRoute/pull/11851)) — thanks @HouMinXi +- **fix(ci):** run the install-upgrade gate on disk, not on the /tmp tmpfs ([#11855](https://github.com/diegosouzapw/OmniRoute/pull/11855)) +- **fix(skills):** expand shorthand property types in injected tool schemas ([#11857](https://github.com/diegosouzapw/OmniRoute/pull/11857)) — thanks @alvinveroy +- **fix(ci):** publish npm from a hosted runner so provenance is accepted ([#11877](https://github.com/diegosouzapw/OmniRoute/pull/11877)) +- **fix(lint):** pin react hooks plugin to 7.0.1 ([#11886](https://github.com/diegosouzapw/OmniRoute/pull/11886)) +- **fix(ci):** surface and clear release-green ESLint failure ([#11890](https://github.com/diegosouzapw/OmniRoute/pull/11890)) — thanks @MumuTW +- **fix(guardrails):** prevent duplicate prompt-injection-guard log output ([#11936](https://github.com/diegosouzapw/OmniRoute/pull/11936)) — thanks @patrykkopycinski +- **fix(auth):** downgrade expected transient states from warn to debug ([#11937](https://github.com/diegosouzapw/OmniRoute/pull/11937)) — thanks @patrykkopycinski +- **fix(codex):** fail over image generation for imported free plans ([#11948](https://github.com/diegosouzapw/OmniRoute/pull/11948)) — thanks @Bl0ck154 +- **fix(codex):** normalize non-stream responses ([#11951](https://github.com/diegosouzapw/OmniRoute/pull/11951)) — thanks @Bl0ck154 +- **fix(images):** forward Antigravity image size ([#11952](https://github.com/diegosouzapw/OmniRoute/pull/11952)) — thanks @Bl0ck154 +- **fix(db):** invalidate connection cache after upsert ([#11953](https://github.com/diegosouzapw/OmniRoute/pull/11953)) — thanks @Bl0ck154 +- **fix(codex):** restore imported account state ([#11954](https://github.com/diegosouzapw/OmniRoute/pull/11954)) — thanks @Bl0ck154 +- **fix(sse):** give extended-thinking targets the reasoning readiness budget ([#11959](https://github.com/diegosouzapw/OmniRoute/pull/11959)) — thanks @abhisheksharma2411 +- **fix(dashboard):** use opaque background and readable text color on cost chart tooltips ([#11960](https://github.com/diegosouzapw/OmniRoute/pull/11960)) — thanks @ZaimMarzuki +- **fix(antigravity):** send complete loadCodeAssist metadata (ideType/platform/pluginType as numeric enums) ([#11969](https://github.com/diegosouzapw/OmniRoute/pull/11969)) — thanks @rifqiawl +- **fix(core):** resolve DB init race condition and reasoning translation ([#12003](https://github.com/diegosouzapw/OmniRoute/pull/12003)) — thanks @raheemuddin786 +- **fix(admission):** exclude reclaimable page cache from the cgroup pressure ratio ([#12017](https://github.com/diegosouzapw/OmniRoute/pull/12017)) — thanks @HouMinXi +- **fix(build):** prune non-production trees in NFT trace excludes and tsconfig ([#12028](https://github.com/diegosouzapw/OmniRoute/pull/12028)) — thanks @Chewji9875 +- **fix(migrations):** add renamed migration compatibility for 056/073/077/101 ([#12036](https://github.com/diegosouzapw/OmniRoute/pull/12036)) — thanks @oyi77 +- **fix(sse):** default crash-guard logger to console.warn, not console ([#12042](https://github.com/diegosouzapw/OmniRoute/pull/12042)) — thanks @HouMinXi +- **fix(catalog):** derive vision/modalities for built-in auto combos from effective target pool ([#12046](https://github.com/diegosouzapw/OmniRoute/pull/12046)) — thanks @Prajeeth-12 +- **fix(release):** the packaged-app smoke verifies the database opened, not a driver line the primary path never prints (twin of #12032) ([#12047](https://github.com/diegosouzapw/OmniRoute/pull/12047)) +- **fix(barrel):** migrate src/app/ off the @/lib/localDb barrel import (#11795 Phase 2) ([#12051](https://github.com/diegosouzapw/OmniRoute/pull/12051)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate src/lib/ off the localDb barrel to direct db imports (#59) ([#12052](https://github.com/diegosouzapw/OmniRoute/pull/12052)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate open-sse, src/shared, src/sse, src/models, src/domain off the @/lib/localDb barrel import (#11795 Phase 4) ([#12053](https://github.com/diegosouzapw/OmniRoute/pull/12053)) — thanks @jonlwheat2-gif +- **fix(barrel):** delete the @/lib/localDb barrel — every consumer migrated (#11795 Phase 5) ([#12055](https://github.com/diegosouzapw/OmniRoute/pull/12055)) — thanks @jonlwheat2-gif +- **fix(radar):** restore D12 public boundary ([#12057](https://github.com/diegosouzapw/OmniRoute/pull/12057)) +- **fix(dev):** bound webpack and Tailwind scans ([#12075](https://github.com/diegosouzapw/OmniRoute/pull/12075)) — thanks @backryun +- **fix(dev):** isolate root layout settings reads ([#12076](https://github.com/diegosouzapw/OmniRoute/pull/12076)) — thanks @backryun +- **fix(dev):** reduce instrumentation executor fan-out (phase 3) ([#12078](https://github.com/diegosouzapw/OmniRoute/pull/12078)) — thanks @backryun +- **fix(dev):** make logging resources HMR-singleton ([#12079](https://github.com/diegosouzapw/OmniRoute/pull/12079)) — thanks @backryun +- **fix(sse):** keep unavailable forced connections scoped ([#12080](https://github.com/diegosouzapw/OmniRoute/pull/12080)) — thanks @keeltrace +- **fix(dev):** isolate batch dispatch from instrumentation ([#12081](https://github.com/diegosouzapw/OmniRoute/pull/12081)) — thanks @backryun +- **fix(ci):** reconcile release test contract drift ([#12082](https://github.com/diegosouzapw/OmniRoute/pull/12082)) — thanks @backryun +- **fix(ci):** accept CVE-2025-68121 in the prebuilt tls-client .so, auto-close base-red issues, guard Scorecard on the default branch ([#12085](https://github.com/diegosouzapw/OmniRoute/pull/12085)) +- **fix(db):** drop three consumer-less 1proxy exports — dead-code base-red on release/v3.8.51 after the barrel deletion (#12055) ([#12087](https://github.com/diegosouzapw/OmniRoute/pull/12087)) +- **fix(api):** clear the six API-route TypeScript regressions the new gate landed red on ([#12094](https://github.com/diegosouzapw/OmniRoute/pull/12094)) +- **fix(release):** never let the tag-push Create Release append auto notes to the curated body ([#12096](https://github.com/diegosouzapw/OmniRoute/pull/12096)) +- **fix(sse):** honor CLIProxyAPI environment API key ([#12099](https://github.com/diegosouzapw/OmniRoute/pull/12099)) — thanks @RaviTharuma +- **fix(oauth):** bind Google refresh to the client that issued the token ([#12106](https://github.com/diegosouzapw/OmniRoute/pull/12106)) — thanks @HouMinXi +- **fix(dashboard):** make RequestLoggerDetail loadable outside Next — CSS via globals.css + CJS/ESM interop (#11703 base-reds) ([#12114](https://github.com/diegosouzapw/OmniRoute/pull/12114)) +- **fix(leases):** project status lease row to lease columns so joined connection PII never escapes ([#12115](https://github.com/diegosouzapw/OmniRoute/pull/12115)) — thanks @geek007git +- **fix(plugins):** do not kill the plugin process when a fire-and-forget hook times out ([#12116](https://github.com/diegosouzapw/OmniRoute/pull/12116)) — thanks @geek007git +- **fix(guardrails):** restore injection-guard logging on middleware-only routes ([#12117](https://github.com/diegosouzapw/OmniRoute/pull/12117)) — thanks @geek007git +- **fix(plugins):** refresh stored manifest from disk on activate so new hook fields reach existing installs ([#12120](https://github.com/diegosouzapw/OmniRoute/pull/12120)) — thanks @geek007git +- **fix(codex):** preserve existing provider state when bulk-import upserts a matching connection ([#12122](https://github.com/diegosouzapw/OmniRoute/pull/12122)) — thanks @geek007git +- **fix(ci):** clear the two base-reds the 2026-08-30 merge batch left on release/v3.8.51 (round 3) ([#12123](https://github.com/diegosouzapw/OmniRoute/pull/12123)) +- **fix(sse):** strip type:'custom' from Claude tools on agentrouter dispatch ([#12126](https://github.com/diegosouzapw/OmniRoute/pull/12126)) — thanks @wahidsadik371-coder +- **fix(sse):** stop advertising video providers the dispatcher cannot run ([#12131](https://github.com/diegosouzapw/OmniRoute/pull/12131)) — thanks @quiterunner-commits +- **fix(search):** treat HTTP 432 and plan limit errors as transient cooldown ([#12139](https://github.com/diegosouzapw/OmniRoute/pull/12139)) — thanks @HouMinXi +- **fix(sse):** refuse an AI Horde queue that cannot fit the request budget ([#12143](https://github.com/diegosouzapw/OmniRoute/pull/12143)) — thanks @quiterunner-commits +- **fix(ci):** clear the base-reds the afternoon merge batch left on release/v3.8.51 (round 5: provider count 352, TS2554/TS2677) ([#12144](https://github.com/diegosouzapw/OmniRoute/pull/12144)) +- **fix(translator):** strip neutral tool_choice when tools absent in Responses-to-Chat (#12141) ([#12166](https://github.com/diegosouzapw/OmniRoute/pull/12166)) — thanks @HouMinXi +- **fix(guardrails):** pass providerId to getResolvedModelCapabilities in checkComboVision (#12112) ([#12169](https://github.com/diegosouzapw/OmniRoute/pull/12169)) — thanks @HouMinXi +- **fix(translator):** strip plaintext reasoning content for opaque responses backends (#12128) ([#12171](https://github.com/diegosouzapw/OmniRoute/pull/12171)) — thanks @HouMinXi +- **perf(stream):** compile hot-path regexes once, bound token caches, fix quadratic buffering ([#12179](https://github.com/diegosouzapw/OmniRoute/pull/12179)) — thanks @opensource-elearning +- **fix:** resolve compression worker file using runtime anchors instead of… ([#12183](https://github.com/diegosouzapw/OmniRoute/pull/12183)) — thanks @NightStalker-87 +- **fix:** prevent Claude Code session kills via liveness-aware readiness + auto model echo ([#12189](https://github.com/diegosouzapw/OmniRoute/pull/12189)) — thanks @opensource-elearning +- **fix(translator):** preserve falsy primitive values in Gemini and Antigravity function response results ([#12191](https://github.com/diegosouzapw/OmniRoute/pull/12191)) — thanks @Siva010 +- **fix(oauth):** mark empty Antigravity projectId as degraded and clear stale errors (#11284) ([#12205](https://github.com/diegosouzapw/OmniRoute/pull/12205)) — thanks @HouMinXi +- **fix(providers):** gate the Codex auto-ping usage read on the shared quota throttle ([#12209](https://github.com/diegosouzapw/OmniRoute/pull/12209)) — thanks @abhisheksharma2411 +- **fix(combos):** prioritize SQLite row id over inner JSON id and notify delete errors ([#12213](https://github.com/diegosouzapw/OmniRoute/pull/12213)) — thanks @HouMinXi +- **fix(providers):** mark featherless.ai as no longer having a free tier (`hasFree: false`) ([#12216](https://github.com/diegosouzapw/OmniRoute/pull/12216)) — thanks @turbolego +- **fix(deepseek-web):** stop Turbopack dev panic in the PoW worker path resolver ([#12221](https://github.com/diegosouzapw/OmniRoute/pull/12221)) — thanks @hartmark +- **fix(chat-admission):** clarify local 503 source ([#12223](https://github.com/diegosouzapw/OmniRoute/pull/12223)) — thanks @keeltrace +- **fix(dev):** silence webpack runtime module warnings ([#12228](https://github.com/diegosouzapw/OmniRoute/pull/12228)) — thanks @backryun +- **fix(db):** use module.require for CommonJS runtime driver loading ([#12230](https://github.com/diegosouzapw/OmniRoute/pull/12230)) — thanks @raheemuddin786 +- **fix(memory):** honest probe-driven FTS5 keyword status + memory_id rowid sync ([#12231](https://github.com/diegosouzapw/OmniRoute/pull/12231)) — thanks @raheemuddin786 +- **fix(system):** propagate abort signal to stream reader in HTTP version checks ([#12232](https://github.com/diegosouzapw/OmniRoute/pull/12232)) — thanks @raheemuddin786 +- **fix(resilience):** isolate local host execution errors from provider circuit breakers ([#12233](https://github.com/diegosouzapw/OmniRoute/pull/12233)) — thanks @raheemuddin786 +- **fix(sse):** sort injected tools deterministically for prompt caching ([#12234](https://github.com/diegosouzapw/OmniRoute/pull/12234)) — thanks @raheemuddin786 +- **fix(combo):** always clear the loop-safety timer, not just on the happy path (#11804) ([#12245](https://github.com/diegosouzapw/OmniRoute/pull/12245)) +- **fix(quality):** base-red pair — stryker tap registration + turn-pin suites aligned to the window gate ([#12255](https://github.com/diegosouzapw/OmniRoute/pull/12255)) — thanks @backryun +- **fix(dev):** qualify Turbopack runtime boundaries ([#12258](https://github.com/diegosouzapw/OmniRoute/pull/12258)) — thanks @backryun +- **fix(combo):** fall back to full pool when collapsed sole survivor is context-too-small ([#12278](https://github.com/diegosouzapw/OmniRoute/pull/12278)) — thanks @opensource-elearning +- **fix(combo):** bound the pre-dispatch unavailable skip so a stale label cannot dark a pool (#12168) ([#12285](https://github.com/diegosouzapw/OmniRoute/pull/12285)) +- **fix(security):** strip Qwen/Alibaba console-session cookies from provider API responses ([#12287](https://github.com/diegosouzapw/OmniRoute/pull/12287)) +- **fix(usage):** console-aware Token Plan guidance + subscription hint on bailian 401 ([#12288](https://github.com/diegosouzapw/OmniRoute/pull/12288)) +- **fix(resourcePressure):** log numeric detail on every rejection, recover faster ([#12293](https://github.com/diegosouzapw/OmniRoute/pull/12293)) — thanks @hartmark +- **fix(release):** drain the 2026-09-01 base-red window — passthrough usage regression + radarPage i18n keys ([#12327](https://github.com/diegosouzapw/OmniRoute/pull/12327)) +- **fix(ci):** openapi-security-tiers checker must honor routeGuard patterns + imported prefixes ([#12350](https://github.com/diegosouzapw/OmniRoute/pull/12350)) — thanks @ggiak +- **perf(ci):** cache node_modules in the npm-ci-retry composite (#8084 D3) ([#12408](https://github.com/diegosouzapw/OmniRoute/pull/12408)) +- **revert(ui):** point CTAs back at their real destinations ([#12410](https://github.com/diegosouzapw/OmniRoute/pull/12410)) +- **fix(release):** drain the 2026-09-02 base-red — rerank-providers import + api-typecheck baseline ratchet ([#12414](https://github.com/diegosouzapw/OmniRoute/pull/12414)) +- **fix(types):** make system prompt injection noImplicitAny-safe ([#12416](https://github.com/diegosouzapw/OmniRoute/pull/12416)) +- **fix(quality):** validate typecheck baseline schema ([#12419](https://github.com/diegosouzapw/OmniRoute/pull/12419)) +- **fix(memory):** resolve rerank provider node cache import ([#12421](https://github.com/diegosouzapw/OmniRoute/pull/12421)) +- **fix(ci):** repair release-root regressions — pack dedup, web-session syntax, uc-image ids ([#12423](https://github.com/diegosouzapw/OmniRoute/pull/12423)) — thanks @backryun +- **fix(providers):** separate MaxAI and UC credential contracts ([#12431](https://github.com/diegosouzapw/OmniRoute/pull/12431)) +- **fix(cli):** preserve Claude settings on config set ([#12432](https://github.com/diegosouzapw/OmniRoute/pull/12432)) — thanks @KrzysiekSko +- **fix(providers):** repair the maxai credential block truncated by merge auto-resolve ([#12433](https://github.com/diegosouzapw/OmniRoute/pull/12433)) +- **fix(quality):** record the 2026-09-02 merged growth in the file-size baseline ([#12434](https://github.com/diegosouzapw/OmniRoute/pull/12434)) +- **fix(uc):** route UC error strings through sanitizeErrorMessage; allowlist the retired codex id ([#12437](https://github.com/diegosouzapw/OmniRoute/pull/12437)) +- **fix(adapta):** redact streamed upstream errors ([#12438](https://github.com/diegosouzapw/OmniRoute/pull/12438)) +- **fix(sse):** remap non-contiguous upstream tool_calls index to a gap-free output_index ([#12445](https://github.com/diegosouzapw/OmniRoute/pull/12445)) — thanks @hartmark +- **fix(quality):** detect duplicate tool_calls entries in one response ([#12446](https://github.com/diegosouzapw/OmniRoute/pull/12446)) — thanks @hartmark +- **fix(conversations):** resolve turn content OmniRoute never sends back to the client ([#12447](https://github.com/diegosouzapw/OmniRoute/pull/12447)) — thanks @hartmark +- **fix(combo):** do not treat credits-exhausted 401 as auth skip ([#12449](https://github.com/diegosouzapw/OmniRoute/pull/12449)) — thanks @RaviTharuma +- **fix(auth):** do not park healthy quota accounts as expired ([#12452](https://github.com/diegosouzapw/OmniRoute/pull/12452)) — thanks @RaviTharuma +- **fix(responses-continuation):** fail closed on a collector-truncated, empty output array ([#12460](https://github.com/diegosouzapw/OmniRoute/pull/12460)) — thanks @hartmark +- **fix(logging):** raise the SSE payload collector's default cap ([#12461](https://github.com/diegosouzapw/OmniRoute/pull/12461)) — thanks @hartmark +- **fix(resilience):** surface Responses failed.error.message in 502s ([#12472](https://github.com/diegosouzapw/OmniRoute/pull/12472)) — thanks @RaviTharuma +- **fix(security):** unbiased maxai X-Random nonce + stricter URL/regex assertions ([#12502](https://github.com/diegosouzapw/OmniRoute/pull/12502)) +- **fix(video):** re-anchor transcript log-redaction so PII/credential maskers can't reopen the leak (#12150 P1 follow-up) ([#12503](https://github.com/diegosouzapw/OmniRoute/pull/12503)) +- **fix(monitoring):** serve cached credentialHealth off the request path ([#12533](https://github.com/diegosouzapw/OmniRoute/pull/12533)) — thanks @RaviTharuma +- **fix(quota):** keep Antigravity Gemini usable when Claude weekly is empty ([#12566](https://github.com/diegosouzapw/OmniRoute/pull/12566)) — thanks @HouMinXi +- **fix(authz):** hard-gate every credential export and CLI-config write (GHSA-5926-2w35-7h4q) ([#12600](https://github.com/diegosouzapw/OmniRoute/pull/12600)) +- **fix(docs):** restore the Next build — REMOVED_PROVIDERS.md had no frontmatter (base-red #12581) ([#12610](https://github.com/diegosouzapw/OmniRoute/pull/12610)) +- **fix(quality):** re-point the zcodeProtocol public-creds allowlist to line 313 ([#12615](https://github.com/diegosouzapw/OmniRoute/pull/12615)) +- **fix(security):** close 3 advisories — search baseUrl exfil, sk- in the error sanitizer, bifrost relay header leak ([#12620](https://github.com/diegosouzapw/OmniRoute/pull/12620)) +- **fix(providers):** sanitize boolean required and nested bare maps for Gemini (#12269) ([#12624](https://github.com/diegosouzapw/OmniRoute/pull/12624)) — thanks @HouMinXi +- **fix(resilience):** keep Overloaded STREAM_EARLY_EOF off the provider breaker ([#12626](https://github.com/diegosouzapw/OmniRoute/pull/12626)) — thanks @HouMinXi +- **fix(catalog):** re-audit free-tier quotas against official pages ([#12649](https://github.com/diegosouzapw/OmniRoute/pull/12649)) +- **fix(dashboard):** keep a request's pending-tracking id stable across combo target retries ([#12650](https://github.com/diegosouzapw/OmniRoute/pull/12650)) — thanks @hartmark +- **fix(tests):** retire dead model ids from the chat-pipeline integration suite (base-red #12581) ([#12670](https://github.com/diegosouzapw/OmniRoute/pull/12670)) +- **fix:** resolve SqliteError no such table compression_run_telemetry during cleanup ([#12682](https://github.com/diegosouzapw/OmniRoute/pull/12682)) — thanks @groovecityJO +- **fix(combo):** treat a pin-only step as implicit connection allowlist ([#12697](https://github.com/diegosouzapw/OmniRoute/pull/12697)) — thanks @HouMinXi +- **fix(ci):** ship .npmrc in published package so legacy-peer-deps applies to consumers (#11544) ([#12699](https://github.com/diegosouzapw/OmniRoute/pull/12699)) — thanks @KooshaPari +- **fix(docker):** pin 4 CLI tools to exact versions (#12576) ([#12703](https://github.com/diegosouzapw/OmniRoute/pull/12703)) — thanks @KooshaPari +- **fix(docker):** document and harden cli profile trust boundary (#12570) ([#12706](https://github.com/diegosouzapw/OmniRoute/pull/12706)) — thanks @KooshaPari +- **fix(sse):** redact video transcript in pre-guardrail rejected-request logs (#12150 P2 item 7) ([#12710](https://github.com/diegosouzapw/OmniRoute/pull/12710)) +- **fix(i18n):** quote placeholder in OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES description (#12505) ([#12769](https://github.com/diegosouzapw/OmniRoute/pull/12769)) — thanks @KooshaPari +- **fix(api/pricing):** surface validation error message as string, not raw object (#12494) ([#12771](https://github.com/diegosouzapw/OmniRoute/pull/12771)) — thanks @KooshaPari +- **fix(sse):** re-enable prompt compression for native Codex passthrough ([#12834](https://github.com/diegosouzapw/OmniRoute/pull/12834)) — thanks @thomasmaerz +- **fix(dashboard):** model health tests for a provider node set to the Responses API now call `/v1/responses` with a Responses-shaped body instead of `/v1/chat/completions` — those models were reported as `Provider returned HTTP 200 but no text content` even though the same model answered normally through `/v1/responses` ([#13070](https://github.com/diegosouzapw/OmniRoute/issues/13070)) ([#13087](https://github.com/diegosouzapw/OmniRoute/pull/13087)) — thanks @ntdatt812 +- fix(routing): stop a round-robin combo's "opencode" targets from collapsing onto the opencode-zen connection (#11912) ([#13283](https://github.com/diegosouzapw/OmniRoute/pull/13283)) +- fix(routing): stop the reactive-compaction debug log from lying when compression is globally disabled (#11977) ([#13276](https://github.com/diegosouzapw/OmniRoute/pull/13276)) +- fix(electron): relativize standalone-bundle symlink targets so Stage 8 manifest verification stops failing on Windows (#11979) ([#13251](https://github.com/diegosouzapw/OmniRoute/pull/13251)) +- fix(dashboard): surface a visible error when Compression Studio's combined preview run fails (#12061) ([#13288](https://github.com/diegosouzapw/OmniRoute/pull/13288)) +- fix(dashboard): make the compression "Effective pipeline" preview honor the active profile and warn when the master switch is off (#12063) ([#13287](https://github.com/diegosouzapw/OmniRoute/pull/13287)) +- fix(providers): scope TinyCMS Web signer's DOM shims to each call instead of leaking them for the process lifetime, and surface a clean HTTP status on a non-JSON interception-toggles error (#12072) ([#13254](https://github.com/diegosouzapw/OmniRoute/pull/13254)) +- fix(guardrails): stop Vision Bridge from re-selecting a model locked after a 404 (#12111) ([#13259](https://github.com/diegosouzapw/OmniRoute/pull/13259)) +- fix(sse): require Responses-shaped body before native OpenAI-compatible passthrough (#12129) ([#13278](https://github.com/diegosouzapw/OmniRoute/pull/13278)) +- fix(providers): minimax-m3 now collapses manual thinking.type:"enabled" to adaptive, preventing upstream 400 (2013) (#12132) ([#13249](https://github.com/diegosouzapw/OmniRoute/pull/13249)) +- fix(db): scope model visibility overrides by modality so hiding a Chat model no longer hides an identically-ID'd Image/Embeddings/etc. model (#12172) ([#13258](https://github.com/diegosouzapw/OmniRoute/pull/13258)) +- fix(db): scope local-provider apiKey dedup to matching base URL so LM Studio/Ollama-style connections support multiple accounts (#12173) ([#13250](https://github.com/diegosouzapw/OmniRoute/pull/13250)) +- fix(providers): refresh Trae's stale Referer/Origin and forward user timezone so imported connections stop failing with 401 (#12190) ([#13255](https://github.com/diegosouzapw/OmniRoute/pull/13255)) +- fix(providers): route opencode-go/gpt-5.6-luna to /responses instead of /chat/completions (#12196) ([#13275](https://github.com/diegosouzapw/OmniRoute/pull/13275)) +- fix(dashboard): allow deleting the last extra-upstream-header row even when invalid (#12251) ([#13252](https://github.com/diegosouzapw/OmniRoute/pull/13252)) +- **fix(i18n):** translate pre-existing `__MISSING__:` keys for `combo.sort`, `requestLogger.detail` expand/collapse, `common.profile`, and `settings.resilienceCredentialHealth*` across 39 locales ([#12272](https://github.com/diegosouzapw/OmniRoute/issues/12272)) ([#13034](https://github.com/diegosouzapw/OmniRoute/pull/13034)) — thanks @HouMinXi +- fix(cli): run the Node.js runtime compatibility guard before the heavy `tsx/esm` + Commander import chain so an unsupported runtime gets a clear message instead of a raw `Invalid regular expression flags` crash (#12296) ([#13253](https://github.com/diegosouzapw/OmniRoute/pull/13253)) +- fix(dashboard): refresh the providers list after deleting a compatible provider node (#12298) ([#13256](https://github.com/diegosouzapw/OmniRoute/pull/13256)) +- fix(usage): fail closed on API-key budget enforcement when a provider's `auto` routing alias has no pricing row, instead of silently counting it as $0 (#12341) ([#13257](https://github.com/diegosouzapw/OmniRoute/pull/13257)) +- **fix(providers):** include the submitted Agnes video model when polling by `video_id` ([#12356](https://github.com/diegosouzapw/OmniRoute/pull/12356)) — thanks @pacocartones +- **fix(routing):** custom OpenAI-compatible nodes now honor the saved Chat/Responses API type after edits instead of letting the node's original ID prefix override the live connection setting ([#11884](https://github.com/diegosouzapw/OmniRoute/issues/11884)). ([#12358](https://github.com/diegosouzapw/OmniRoute/pull/12358)) — thanks @pacocartones +- fix(sse): surface an error instead of a silent empty 200 when a Claude stream closes with zero bytes (#12398) ([#13285](https://github.com/diegosouzapw/OmniRoute/pull/13285)) +- fix(oauth): warn before the dead localhost:8080 redirect in antigravity/gemini `oauth start` (#12413) ([#13265](https://github.com/diegosouzapw/OmniRoute/pull/13265)) +- fix(providers): stop devin-cli spawn error from double-closing the SSE controller (#12517) ([#13261](https://github.com/diegosouzapw/OmniRoute/pull/13261)) +- **fix(rerank):** clamp Voyage `top_k` to the documents actually sent after empty-string filtering, and honor `return_documents: false` in the NVIDIA response adapter (#12523 — thanks @pacocartones) +- **fix(audio):** `/v1/audio/translations` now resolves combo names the way `/v1/audio/transcriptions` already does, so a combo that `GET /v1/models` advertises is fanned out to its targets instead of being rejected with `400 Invalid translation model: . Use format: provider/model`; literal `provider/model` ids and unknown bare names behave as before (#12536 — thanks @pacocartones) +- **fix(gemini):** strip the JSON-Schema-2020-12 `prefixItems` keyword from Gemini tool schemas at every nesting level, so Claude Code tool definitions no longer fail with `400 Unknown name "prefixItems"` on Gemini models (#12540 — thanks @pacocartones) +- **fix(api-manager):** Expose an accessible loading status while API keys are fetched instead of an empty accessibility tree (#12541 — thanks @pacocartones) +- **fix(video):** Clamp `estimateJpegFrameBytes` at zero for padding-only payloads and build the three encode-side frame data URIs from `JPEG_FRAME_DATA_URI_PREFIX` instead of a repeated literal (#12543 — thanks @pacocartones) +- **fix(devin):** accept Windows `DEVIN_AGENTIC_HOME` sandbox paths (`C:\...\.sandbox\...`) in the isolated-home check so the Devin Claude Bridge no longer fails closed on Windows ([#12405](https://github.com/diegosouzapw/OmniRoute/issues/12405)) (#12545 — thanks @pacocartones) +- **fix(db):** Add `conversation_turn_nodes` and orphaned `agentic_conversations` to the auto-cleanup cycle under the existing `retention.callLogs` window, so identity nodes whose call-log content has already been purged no longer accumulate without bound in `storage.sqlite` (#12548 — thanks @pacocartones) +- **fix(i18n):** Wrap the `~/.claude/profiles//settings.json` placeholder in ICU single quotes in the `OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES` feature-flag description across all 42 locales and the TypeScript default, so next-intl no longer fails with `INVALID_MESSAGE: UNCLOSED_TAG` and the Feature Flags card shows the description instead of the raw key (#12549 — thanks @pacocartones) +- **fix(orchestration):** `updateCloudAgentTask` now publishes the task's real `status` on `agent.task.updated` when an update only touches `result`, `activities` or `error`, instead of the fabricated `"updated"` state, and stays silent when no row matched the id (#12550 — thanks @pacocartones) +- **fix(i18n):** the home "Recent Requests" panel and the Provider Topology legend are now translated instead of rendering English copies on non-English dashboards; the legend reads its own `home.topologyLegend*` labels with consistent casing rather than borrowing the memory-settings "Recent" and analytics "Error" strings (#12551 — thanks @pacocartones). +- **docs(reference):** bring the `FEATURE_FLAGS.md` catalog back to 1:1 with `featureFlagDefinitions.ts` — 20 missing flags added, the two `*_BLOCK_THRESHOLD` env-only knobs moved out of the flag tables, category/total counts and the Live WS port corrected, guarded by a static test (#12552 — thanks @pacocartones) +- **fix(i18n):** backfill missing `usage.kiloPass*` strings in 39 locales and restore `featureFlagOmnirouteDisableThinkingLevelVariantsDescription` in `pt.json` ([#12561](https://github.com/diegosouzapw/OmniRoute/issues/12561)) ([#13027](https://github.com/diegosouzapw/OmniRoute/pull/13027)) — thanks @HouMinXi +- fix(docker): default docker-compose app ports (dashboard/API/live-WS) to loopback instead of `0.0.0.0`, closing the anonymous `/v1` LAN/WAN exposure gap left open by `REQUIRE_API_KEY=false` (#12568) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): close DNS-rebinding SSRF gap in webhook outbound-URL guard (#12569) ([#13243](https://github.com/diegosouzapw/OmniRoute/pull/13243)) +- fix(docker): require a per-session token on the VNC browser CDP bridge and isolate it on a dedicated Docker network (#12571) ([#13241](https://github.com/diegosouzapw/OmniRoute/pull/13241)) +- fix(open-sse): write Adobe Firefly session tokens and cookie jars with 0700/0600 permissions instead of the process umask (#12572) ([#13242](https://github.com/diegosouzapw/OmniRoute/pull/13242)) +- fix(api): remove hardcoded wildcard CORS in openai-to-gemini-sse.ts so the centralized fail-closed CORS gate is the sole source of `Access-Control-Allow-Origin` (#12573) ([#13239](https://github.com/diegosouzapw/OmniRoute/pull/13239)) +- fix(api): enforce API key policy (budget/rate-limit/schedule/endpoint scoping) on the ElevenLabs speech-to-text, text-to-speech and voices proxy routes (#12574) ([#13244](https://github.com/diegosouzapw/OmniRoute/pull/13244)) +- fix(sse): cap HuggingChat NDJSON body size and bound the read loop with the fetch timeout so a stalled or hostile upstream cannot buffer unbounded memory (#12577) ([#13282](https://github.com/diegosouzapw/OmniRoute/pull/13282)) +- fix(docker): scope the cliproxyapi/qdrant/bifrost sidecars to loopback by default and forward `CLIPROXYAPI_MANAGEMENT_KEY` into the cliproxyapi container so its management API is not left both unauthenticated and LAN-published (#12578) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): create DB export temp paths with `fs.mkdtempSync` instead of predictable timestamps (#12579) ([#13237](https://github.com/diegosouzapw/OmniRoute/pull/13237)) +- Cline 401 bodies that say "re-authenticate your Cline account" classify as a refreshable OAuth token, not a terminal expired key. The cooling panel no longer labels every cooldown as a 429; it shows the recorded last error instead. (#12594) ([#13060](https://github.com/diegosouzapw/OmniRoute/pull/13060)) — thanks @HouMinXi +- **fix(catalog):** degrade unknown combo targets instead of dropping LCD modalities, and persist OpenRouter `architecture.input_modalities` into the capability snapshot ([#12613](https://github.com/diegosouzapw/OmniRoute/issues/12613)) ([#13042](https://github.com/diegosouzapw/OmniRoute/pull/13042)) — thanks @HouMinXi +- fix(providers): send `x-api-key` instead of `Authorization: Bearer` for OpenCode Zen's `/v1/responses` endpoint (Muse Spark Contributor models), fixing a 401 on OmniRoute's auth header (#12633) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(sse): surface the actionable "Auggie CLI not found" message when the shell reports a missing `auggie` binary via exit code instead of a spawn error (#12645) ([#13263](https://github.com/diegosouzapw/OmniRoute/pull/13263)) +- **fix(config):** Persist deletions of built-in background-degradation entries — when a stored settings record exists its `degradationMap` is now authoritative instead of being merged under the defaults, so an entry the user removed in the dashboard no longer reappears on the next apply or restart ([#12424](https://github.com/diegosouzapw/OmniRoute/issues/12424)) ([#12647](https://github.com/diegosouzapw/OmniRoute/pull/12647)) — thanks @pacocartones +- **fix(gamification):** action-count badge milestones (First Token, Token Consumer, Token Machine, Token Whale, and the token-sharing tier) are now backed by a durable `xp_action_counts` counter incremented in `addXp()`, instead of a live `COUNT(*)` over `xp_audit_log`. The audit log is pruned by `retention.xpAuditLog` (default 30 days), so on a default install those "lifetime" milestones were really "actions in the last 30 days" and unlocked badges could stop unlocking once old rows aged out. `getActionCount()` and `checkActionCountBadges()` now read the same durable source, and a migration backfills existing totals from the surviving audit rows ([#12546](https://github.com/diegosouzapw/OmniRoute/issues/12546)) ([#12651](https://github.com/diegosouzapw/OmniRoute/pull/12651)) — thanks @pacocartones +- **fix(images):** `/v1/images/edits` now iterates a combo's targets the same way `/v1/images/generations` does (#9239) instead of flattening a bare combo to its first target. A combo whose first target is not edit-capable — or lacks credentials — now falls through to a later edit-capable target rather than hard-erroring, and missing credentials are skipped (not a hard `401`) to match the generations path. The per-target skip/terminal classification is extracted into a shared `runImageComboTargets` loop, so generations behavior is unchanged ([#12547](https://github.com/diegosouzapw/OmniRoute/issues/12547)). ([#12653](https://github.com/diegosouzapw/OmniRoute/pull/12653)) — thanks @pacocartones +- fix(sse): add first-byte watchdog to the TLS-fingerprint transport so a stalled wreq body falls back instead of hanging for minutes (#12656) ([#13272](https://github.com/diegosouzapw/OmniRoute/pull/13272)) +- fix(sse): exempt tiny-budget reasoning probes from combo quality failure and surface persisted-cooldown skips in ALL_TARGETS_SKIPPED diagnostics (#12659) ([#13269](https://github.com/diegosouzapw/OmniRoute/pull/13269)) +- fix(models): declare the real ~1M contextLength for OpenCode Zen's Muse Spark 1.2 models instead of falling back to the 200000 provider default (#12681) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(oauth): align codebuddy-cn OAuth User-Agent with the chat/usage CLI version to avoid WAF false positives (#12702) ([#13264](https://github.com/diegosouzapw/OmniRoute/pull/13264)) +- fix(dashboard): surface an authentication-required banner instead of silently blanking database settings for a guest session (#12709) ([#13270](https://github.com/diegosouzapw/OmniRoute/pull/13270)) +- **fix(resilience):** stop unbounded queue that hangs 6min until Aborted — gate, provider slot, and Bottleneck queue now share a per-connection `maxWaitMs` budget; `fail-closed` on exhaust (503); execution backstop `executionMaxWaitMs` overridable per connection with upstream clamp ([#12715](https://github.com/diegosouzapw/OmniRoute/pull/12715)) — thanks @maxmad64bis (with thanks to @Tushar49 for surfacing the slow-provider need in #12635) +- **fix(auto-combo):** every mode pack now carries `quality` and `reliability` weights (`quality` 0.02, 0.03 in `quality-first`; `reliability` 0.03, 0.04 in `reliability-first`), so selecting a pack no longer silences either signal; `DEFAULT_WEIGHTS` is unchanged ([#12731](https://github.com/diegosouzapw/OmniRoute/pull/12731)) — thanks @maxmad64bis +- **fix(sse):** the provider execution pipeline keeps the upstream error `code`/`type` again — both were lost when the non-streaming leg was extracted, so a config-class `422` (Antigravity missing project) degraded into a generic account cooldown instead of reaching the gate that recognises it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(db):** a legacy `call_logs` table no longer aborts startup — the provider-stats index is created after the column healing runs, not before it, so an install predating `request_type` opens instead of failing with `no such column` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(catalog):** a malformed row in the operator-writable custom-models blob no longer kills every `auto/*` pool ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(tests):** realign the guards that the same day's merges left asserting the old behavior — the provider-count total after GreenPT and EURouter, the GLM stream buffer slot that `#12925` turned into a declared parameter, and the injection scan bound that `#13104` reshaped from a head-only window into head-plus-tail ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- fix(cache): fold tool_choice/tools/response_format into the semantic cache signature so a cached tool_calls response can no longer be replayed for a request whose tool policy forbids it (#12734) ([#13267](https://github.com/diegosouzapw/OmniRoute/pull/13267)) +- **fix(models):** stop treating provider-supplied `isFree:true`/`:free`/`0/0` flags as trusted unless the provider has a documented free tier — only the shipped free-tier catalog decides otherwise; fetched `isFree:true` trusted only for a free-tier provider, custom `isFree:true` via a trusted path ([#12744](https://github.com/diegosouzapw/OmniRoute/pull/12744)) — thanks @maxmad64bis +- fix(memory): authenticate the internal /v1/rerank loopback call so memory reranking no longer silently degrades to unranked order when REQUIRE_API_KEY=true (#12745) ([#13268](https://github.com/diegosouzapw/OmniRoute/pull/13268)) +- fix(sse): parse Ollama Cloud's current usage markup (`$X of $Y used` aria-label, nested width style) (#12749) ([#13260](https://github.com/diegosouzapw/OmniRoute/pull/13260)) +- fix(cli): setup-opencode no longer sends an active context's management token to `/v1/models` when `--api-key`/`OMNIROUTE_API_KEY` is supplied — an explicit flag or the env var now always outranks the context's token, and the flag itself is no longer swallowed by the parent program's global `--api-key` option (#12783) ([#13246](https://github.com/diegosouzapw/OmniRoute/pull/13246)) +- fix(sse): register Arcee AI in the executor provider registry so requests reach api.arcee.ai instead of silently falling back to OpenAI (#12784) ([#13277](https://github.com/diegosouzapw/OmniRoute/pull/13277)) +- **fix(quota):** share one upstream quota read when identical saturation checks arrive at the same time, so traffic bursts don't multiply provider API calls ([#12787](https://github.com/diegosouzapw/OmniRoute/pull/12787)) — thanks @maxmad64bis +- **fix(db):** ignore expired or invalid rate-limit cooldown writes so a stale timestamp can't lock a connection that should be usable — clearing still works as before ([#12788](https://github.com/diegosouzapw/OmniRoute/pull/12788)) — thanks @maxmad64bis +- **fix(providers):** single source for provider order with `xao` ranking alongside `xai-oauth` ([#12790](https://github.com/diegosouzapw/OmniRoute/pull/12790)) — thanks @maxmad64bis +- **fix(routing):** off-table models check the free-model catalog before inheriting premium prices, latency bootstraps from the observed pool median, async tiers read live database pricing with a 90-day freshness gate, and the tier cache invalidates on every pricing write ([#12792](https://github.com/diegosouzapw/OmniRoute/pull/12792)) — thanks @maxmad64bis +- **fix(auto-combo):** snapshot scoring uses the observed per-connection account tier and per-model quality instead of neutral constants, and circuit-open providers rank lower at build time ([#12794](https://github.com/diegosouzapw/OmniRoute/pull/12794)) — thanks @maxmad64bis +- **fix(pool):** empty `auto/*` pools now say why they are empty, and the models listing uses the same paid check as routing ([#12795](https://github.com/diegosouzapw/OmniRoute/pull/12795)) — thanks @maxmad64bis +- fix(routing): recognize CLIProxyAPI's 'unknown provider for model' 400 as fallback-worthy (#12800) ([#13284](https://github.com/diegosouzapw/OmniRoute/pull/13284)) +- **fix(sse):** translate-mode streams now emit the estimated token counts as a trailing usage-only chunk before `[DONE]` when the upstream stays silent, so metered chat clients see totals instead of nothing ([#12828](https://github.com/diegosouzapw/OmniRoute/pull/12828)) — thanks @maxmad64bis +- **fix(db):** provider stats stay truthful on empty and legacy databases: fallback counts default to `0` instead of `null`, latency averages read `null` (not `0`) when no durations were recorded, failures logged before the error-type column existed group under `pre_migration` instead of `unclassified`, and per-provider queries use two new composite indexes ([#12832](https://github.com/diegosouzapw/OmniRoute/pull/12832)) — thanks @maxmad64bis +- fix(nvidia): fail open when a synced model catalog goes stale instead of gating forever (#12849) ([#13248](https://github.com/diegosouzapw/OmniRoute/pull/13248)) +- **fix(db):** WAL maintenance lives in its own module: a `TRUNCATE` checkpoint that hits a busy database now warns with its streak and retries once via `PASSIVE` instead of logging success, and closing-time checkpoints no longer report success on builds without a database file, with the busy totals visible in the authenticated monitoring health payload ([#12853](https://github.com/diegosouzapw/OmniRoute/pull/12853)) — thanks @maxmad64bis +- **fix(health):** quota with no snapshots reads empty instead of a contradicting 0% ([#12857](https://github.com/diegosouzapw/OmniRoute/pull/12857)) — thanks @maxmad64bis +- fix(a2a): accept the dashboard's own session cookie on /a2a so "Run message/send" no longer fails with "Unauthorized: missing or invalid API key" (#12888) ([#13271](https://github.com/diegosouzapw/OmniRoute/pull/13271)) +- **fix(logs):** the Logs grid's in-memory filter pass no longer discards rows the SQL query already matched — selecting an API key from the dropdown (which sends the key's id) returns its calls again, the Combo tab shows every combo instead of only those whose name contains a "1", and the model filter and search cover the same columns as the query ([#12896](https://github.com/diegosouzapw/OmniRoute/pull/12896)) — fixes [#12873](https://github.com/diegosouzapw/OmniRoute/issues/12873) — thanks @ntdatt812 +- **fix(a2a):** `/api/a2a/status` now builds the agent card from the request that asked for it, so a gateway reached at a non-localhost host no longer advertises `http://localhost:20128` as its A2A URL ([#12918](https://github.com/diegosouzapw/OmniRoute/pull/12918)). — thanks @ntdatt812 +- **fix(compression):** progressive aging now appends its `[COMPRESSED:aging:…]` annotation after a turn's `tool_result` blocks instead of in front of them, so Anthropic no longer rejects aged conversations with "`tool_use` ids were found without `tool_result` blocks immediately after" ([#12920](https://github.com/diegosouzapw/OmniRoute/pull/12920)). — thanks @ntdatt812 +- **fix(bedrock):** model import now resolves context limits for every vendor prefix instead of only `anthropic.*`, so `global.openai.gpt-5.6-*` no longer imports with a null `inputTokenLimit` and gets rejected pre-flight at the 200k default ([#12921](https://github.com/diegosouzapw/OmniRoute/pull/12921)). — thanks @ntdatt812 +- **fix(stream):** the 64 KB stream buffer GLM asks for is honoured instead of dropped, and the type error it caused no longer fails the API Route Typecheck gate on every open PR ([#12925](https://github.com/diegosouzapw/OmniRoute/pull/12925)) — thanks @ntdatt812 +- **fix(guardrails):** mask PII inside a `tool_result`'s nested content array, which the masker walked past while redacting its sibling block ([#12930](https://github.com/diegosouzapw/OmniRoute/pull/12930)) — thanks @ntdatt812 +- **fix(dashboard):** the Radar catalog table no longer leaves absent data unexplained — empty limits, unknown context windows, and unreported capabilities each explain themselves on hover, and a new check keeps it that way ([#12937](https://github.com/diegosouzapw/OmniRoute/pull/12937)) — thanks @maxmad64bis +- **fix(sse):** geo-blocked opencode requests rotate to the next account proxy instead of failing, so one refused egress no longer aborts the whole chain ([#12941](https://github.com/diegosouzapw/OmniRoute/pull/12941)) — thanks @maxmad64bis +- **fix(codex):** dashboard "clear cooldown" and the CAS recovery path now drop nested `codexScopeRateLimitedUntil` maps in the same write that nulls `rate_limited_until`, and fresh quota snapshots with headroom lift fallback-sourced scope cooldowns parked by quota preflight ([#12817](https://github.com/diegosouzapw/OmniRoute/issues/12817), [#12860](https://github.com/diegosouzapw/OmniRoute/issues/12860), [#12951](https://github.com/diegosouzapw/OmniRoute/pull/12951)) The dashboard reset-credit button (`consumeCodexResetCredit`) now exercises that same snapshot path after a successful redeem, so a filled quota bar is enough to unpark a leftover fallback Codex child without another manual clear. — thanks @HouMinXi +- **fix(db):** resolve `sql-wasm.wasm` across global npm install and hoisted layouts, ensuring OmniRoute can boot cleanly on Node 24 when native `better-sqlite3` is uncompiled. ([#13035](https://github.com/diegosouzapw/OmniRoute/pull/13035)) — thanks @HouMinXi +- **fix(api):** `DELETE /v1/batches/delete-completed` now sweeps only the calling API key's own completed batches (batches with no owner stay out of a key-scoped sweep on purpose), with an explicit instance-wide mode reserved for authenticated dashboard sessions, a 401 for a presented key that is unknown, revoked, deactivated, banned or expired (never falling through to the session branch), audit logging of every sweep, a sanitized 500 on failure, an owner-scoped file half (a key-scoped sweep never nulls a file another tenant owns) and an atomic sweep — chunked in 200-batch transactions in instance mode — so a mid-way error never leaves a batch pointing at a nulled file (GHSA-wvxc-jp3v-5mg5) ([#12969](https://github.com/diegosouzapw/OmniRoute/pull/12969)) +- **fix(combo):** quota-weighted routing stops drawing on an out-of-credit connection — a 402 now invalidates the stored quota snapshot instead of leaving its stale remaining percentage in place, and a snapshot older than 10 minutes no longer counts as confident headroom for the primary pool ([#12972](https://github.com/diegosouzapw/OmniRoute/pull/12972)) ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- **fix(vision):** Custom Models with "Vision capable" checked no longer have image requests swapped to `glm/glm-4.6v` when the client sends the advertised alias (`vllm/path/...`) or the bare path-shaped id — Vision Bridge now matches the stored override for all three id forms ([#12758](https://github.com/diegosouzapw/OmniRoute/issues/12758)) ([#12974](https://github.com/diegosouzapw/OmniRoute/pull/12974)) — thanks @HouMinXi +- **fix(sse):** transient opencode upstream failures rotate to the next account proxy instead of failing, so one flapping egress no longer aborts the whole chain ([#12975](https://github.com/diegosouzapw/OmniRoute/pull/12975)) — thanks @maxmad64bis +- **fix(azure):** Deployments from GPT-6 onward now send `max_completion_tokens` instead of `max_tokens`, which Azure rejects with HTTP 400. The rule matched a literal `gpt-5`, so each new generation arrived broken; it now matches the generation range, while `gpt-35-turbo` still keeps `max_tokens`. ([#13007](https://github.com/diegosouzapw/OmniRoute/pull/13007)) — thanks @ntdatt812 +- **fix(db):** add an opt-in self-restart circuit for sustained critical memory pressure, gate post-cleanup VACUUM behind a minimum freed-rows threshold, and checkpoint the SQLite WAL every 5 minutes with a size guard that escalates to TRUNCATE, so a growing WAL can no longer stall the event loop into a full outage. ([#13011](https://github.com/diegosouzapw/OmniRoute/pull/13011)) — thanks @HouMinXi +- **fix(auth):** an explicit connection pin may probe a quota-disabled row once and re-enable it on success ([#12874](https://github.com/diegosouzapw/OmniRoute/issues/12874)) ([#13017](https://github.com/diegosouzapw/OmniRoute/pull/13017)) — thanks @HouMinXi +- **fix(api):** thread `X-OmniRoute-Fallback-Attempts` through combo chat completions so streaming and non-streaming responses report how many prior legs were attempted ([#13038](https://github.com/diegosouzapw/OmniRoute/pull/13038)) — thanks @HouMinXi +- Restore provider failure classification and credential refresh on non-streaming requests: classify non-2xx failures to lock models on per-model quota exhaustion, update connection rate limits from headers and body, and pass credential refresh handlers to pipeline execution so 401 tokens can be refreshed and retried (#13043). ([#13069](https://github.com/diegosouzapw/OmniRoute/pull/13069)) — thanks @HouMinXi +- **fix(responses):** wrap forced-non-streaming web_search fallback JSON as Responses SSE so Codex still sees `response.completed` ([#13050](https://github.com/diegosouzapw/OmniRoute/pull/13050)) — thanks @HouMinXi +- **fix(antigravity):** Preserve upstream thought-token usage in normalized completion and reasoning token counts ([#13055](https://github.com/diegosouzapw/OmniRoute/pull/13055)) — thanks @pacocartones +- Fix a concurrency-slot leak in the DeepSeek PoW solver: a worker that failed to spawn (for example a missing worker script) never released its slot, so `MAX_CONCURRENT_WORKERS` failures disabled the solver until restart. ([#13097](https://github.com/diegosouzapw/OmniRoute/pull/13097)) — thanks @anhtahaylove +- **fix(acp):** bound the ACP session output buffers — `stdoutBuffer` and `stderrBuffer` now cap at 1 MiB keeping the most recent output behind a visible `[...output truncated...]` marker, and `stderrBuffer` is reset per prompt instead of accumulating for the lifetime of the session. ([#13100](https://github.com/diegosouzapw/OmniRoute/pull/13100)) — thanks @anhtahaylove +- **fix(acp):** release the `stdout`/`exit` listeners and the idle timer that a `sendPrompt` timeout used to leave attached to the `acpManager` singleton, and drop sessions that exited on their own from the session map instead of keeping them forever. ([#13096](https://github.com/diegosouzapw/OmniRoute/pull/13096)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection and PII scanners now read the text a `tool_result` block carries on `content` (string or nested block list), in messages and in system blocks, so tool output is judged by the same rules as user text ([#13101](https://github.com/diegosouzapw/OmniRoute/pull/13101)) — thanks @ntdatt812 +- **fix(gamification):** close the badge notification SSE stream when the request signal is already aborted before the stream starts — a client that disconnects while the route is still awaiting auth used to leave both the 2s unlock poll and the 15s heartbeat running for the lifetime of the process. ([#13106](https://github.com/diegosouzapw/OmniRoute/pull/13106)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection scan now spends its 16 KB budget on both ends of the request instead of the first 16 KB only, so `system`, `instructions`, `query`, `documents` and the newest turns are no longer hidden behind one long message ([#13104](https://github.com/diegosouzapw/OmniRoute/pull/13104)) — thanks @ntdatt812 +- **fix(dashboard):** expose the Volcano Ark console cookie on quota scraping and unwrap connect-error objects so the dashboard shows the upstream message ([#13107](https://github.com/diegosouzapw/OmniRoute/pull/13107)) — thanks @HouMinXi +- **fix(db):** release the `beforeExit`/`SIGINT`/`SIGTERM` handlers when a `node:sqlite` adapter closes, so a closed adapter and its database handle are no longer pinned to `process` for the lifetime of the run — the same treatment #7494 gave the sql.js adapter. ([#13109](https://github.com/diegosouzapw/OmniRoute/pull/13109)) — thanks @anhtahaylove +- **fix(translator):** `contentSchema` and `unevaluatedItems` are now treated as subschema positions by the tool-schema sanitizer, so a truncation placeholder in either is replaced with a permissive schema instead of being forwarded as a string ([#13110](https://github.com/diegosouzapw/OmniRoute/pull/13110)) — thanks @ntdatt812 +- **fix(cli-helper):** clear the `createLogStream` timeout on the abort path — `stop()` aborts the in-flight fetch and returned through the `signal.aborted` branch, which skipped `clearTimeout` and left an armed timer per stopped stream. The stream reader is now also cancelled when the read loop exits early. ([#13114](https://github.com/diegosouzapw/OmniRoute/pull/13114)) — thanks @anhtahaylove +- **fix(docs):** drop TLS-impersonation, MITM-decrypt, supply-chain attestation, and XOR-mask writeups from the public `/docs` catalog and Docker image. Files stay in git for engineers; operators who need them open the repo, not the website. ([#13136](https://github.com/diegosouzapw/OmniRoute/pull/13136)) — thanks @HouMinXi +- **fix(combo):** parse numeric-epoch `rate_limited_until` in the combo cooldown read path ([#13141](https://github.com/diegosouzapw/OmniRoute/pull/13141)) — thanks @maxmad64bis +- **fix(opencode):** both OpenCode plugins now reject a gateway address typed without `http://` at configuration time, instead of publishing every model with an api url no client can call, and the v2 plugin no longer publishes a model card whose api url is blank or relative ([#13142](https://github.com/diegosouzapw/OmniRoute/pull/13142)) — thanks @maxmad64bis +- **fix(providers):** lock opencode model on upstream 400 model-unavailable ([#13146](https://github.com/diegosouzapw/OmniRoute/pull/13146)) — thanks @maxmad64bis +- **fix(logging):** keep the provider exchange rather than the raw client bodies when a call log exceeds its size budget, and show that recovered payload in the request-detail panel instead of replacing it with the stored response body ([#13147](https://github.com/diegosouzapw/OmniRoute/pull/13147)) — thanks @maxmad64bis +- **fix(traffic-inspector):** the WebSocket route no longer leaks a traffic-buffer subscriber and a 30s ping timer when the client socket is already closed at handler time — listeners are attached before any resource is acquired, a destroyed socket bails out early, and the ping interval stops on a dead socket where `write()` never throws ([#13155](https://github.com/diegosouzapw/OmniRoute/pull/13155)) — thanks @anhtahaylove +- **fix(telegram):** bound the per-user API key cache in the Telegram chat proxy so a burst of distinct chat ids can no longer grow the process heap without limit ([#13165](https://github.com/diegosouzapw/OmniRoute/issues/13165)) ([#13166](https://github.com/diegosouzapw/OmniRoute/pull/13166)) — thanks @anhtahaylove +- **fix(stream):** cancel the upstream response body when the JSON-to-SSE sniff unwinds on a body timeout, so a stalled upstream no longer pins the connection ([#13169](https://github.com/diegosouzapw/OmniRoute/issues/13169)) ([#13171](https://github.com/diegosouzapw/OmniRoute/pull/13171)) — thanks @anhtahaylove +- **fix(telegram):** authenticate webhook deliveries with Telegram's `secret_token` so an unauthenticated caller can no longer mint API keys or spend upstream quota ([#13172](https://github.com/diegosouzapw/OmniRoute/issues/13172)) ([#13175](https://github.com/diegosouzapw/OmniRoute/pull/13175)) — thanks @anhtahaylove +- **fix(models):** give discoverable Gemini 3.8 Flash ids their own 65536 output spec so Antigravity no longer clamps them to 16384 ([#13195](https://github.com/diegosouzapw/OmniRoute/pull/13195)) — thanks @HouMinXi +- **fix(dashboard):** leftover catalog-removed provider rows (gemini-cli) can be listed and purged from the providers page ([#13067](https://github.com/diegosouzapw/OmniRoute/issues/13067)) ([#13197](https://github.com/diegosouzapw/OmniRoute/pull/13197)) — thanks @HouMinXi +- **fix(auth):** a dashboard session now requires the `authenticated: true` claim that login, OIDC and the session refresh already emit — a JWT merely signed with `JWT_SECRET` (for example the Cursor CLI passthrough token, which any API-key holder can obtain) no longer verifies as the `auth_token` cookie on any route, the WebSocket handshake or the live server; existing sessions keep working ([#13298](https://github.com/diegosouzapw/OmniRoute/issues/13298)) ([#13375](https://github.com/diegosouzapw/OmniRoute/pull/13375)) +- **fix(api):** `DELETE /v1/batches/delete-completed` now applies the caller's API-key policy (endpoint allowlist, schedule, usage cap, rate limit) like every other `/v1` route, commits the key-scoped sweep per 200-batch chunk instead of holding one write lock for the whole sweep, refuses to loop without progress, and rejects a scope that names both a key and `allTenants` ([#13684](https://github.com/diegosouzapw/OmniRoute/pull/13684)) +- Restore the API-route typecheck gate: the non-streaming leg result lost its discriminated-union narrowing after the server-owned tool loop reassignment, producing 13 new TS2339 diagnostics in `chatCore.ts`. ([#12963](https://github.com/diegosouzapw/OmniRoute/pull/12963)) +- Restore four non-streaming behaviours the server-owned tool loop refactor dropped: client aborts map to 499 with the fixed `Request aborted` message, an aborted request no longer logs a synthetic `clientResponse`, Claude prompt-cache telemetry is recorded again, and a body that cannot be canonicalized no longer throws when the tool loop is off. ([#12990](https://github.com/diegosouzapw/OmniRoute/pull/12990)) +- **fix(skills):** The CLI registry parser now reads positionals declared with `.addArgument()`, not only those written inline in `.command()`. `tunnel create [type]` was being published as `tunnel create`, so the agent-skills sync gate reported drift on every branch and regenerating would have deleted the argument. ([#13009](https://github.com/diegosouzapw/OmniRoute/pull/13009)) — thanks @ntdatt812 +- **fix(combos):** dashboard combo test uses a short prompt, serial probes, and a 60s timeout so reasoning models and rate-limited free pools do not fail the health check ([#13001](https://github.com/diegosouzapw/OmniRoute/pull/13001)) — thanks @HouMinXi +- fix(compression): terminate idle worker threads on eviction so long-running instances stop leaking OS threads and MessagePorts ([#13091](https://github.com/diegosouzapw/OmniRoute/pull/13091)) — thanks @anhtahaylove +- Redact Google API keys of any length in error bodies: the pattern required exactly 39 characters, so shorter or longer `AIza…` credentials passed through unredacted. ([#12964](https://github.com/diegosouzapw/OmniRoute/pull/12964)) +- **fix(grok-cli):** a 402 "Grok Build usage balance exhausted" parks that Grok login as out of credit (Grok Build CLI, grok.com cookie, and xAI OAuth share the weekly pool). Combo routing then tries the next login instead of locking the model for every account in the pool ([#13061](https://github.com/diegosouzapw/OmniRoute/pull/13061)) — thanks @HouMinXi +- fix(compression): spawn the LLMLingua worker with a file URL object so compression actually runs instead of silently failing open on Node ([#13093](https://github.com/diegosouzapw/OmniRoute/pull/13093)) — thanks @anhtahaylove +- **fix(test):** run the local `test` and `test:unit` scripts at concurrency 4 so a full-suite run no longer exhausts the machine's commit charge and kills unrelated processes ([#13187](https://github.com/diegosouzapw/OmniRoute/pull/13187)) — thanks @anhtahaylove +- **fix(monitoring):** `GET /api/monitoring/health` `credentialHealth` now includes a bounded `failedConnections` list (`connectionId`, `status`, sanitized `lastError`) when the probe-cache gauge `failed>0`, plus `source: probe-cache` and a cheap `staleDbNonOkCount` for sticky SQLite `test_status` on active rows. Documents that the live gauge is not `provider_connections.test_status`. ([#12876](https://github.com/diegosouzapw/OmniRoute/pull/12876)) — thanks @RaviTharuma +- **fix(resilience):** an openai-compatible multi-upstream gateway no longer marks the whole connection `credits_exhausted` when a single upstream (or the `/models` / representative-model health probe) returns 402. Quota failures stay model-scoped; true connection-wide auth failures are unchanged. Single-credential 402 key disable ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239)) is preserved. ([#12875](https://github.com/diegosouzapw/OmniRoute/pull/12875)) — thanks @RaviTharuma +- **fix(dashboard):** nine fixes on the Orchestration Canvas, all diagnosed in the Phase 2 reviews. A source that fails now keeps the timestamp of its FIRST failure instead of being re-stamped every poll — the stale line said "since the last poll" no matter how long the source had been down, and the churn also defeated the snapshot's stable identity (it serializes the source list), so the canvas re-rendered on every tick while anything was broken. A source that HAD data and only then started failing is flagged too: previously only a source with no node at all got the warning, so a source that went down mid-session kept a healthy-looking node forever. The rest are pointwise: clicking a filter chip cancels the pending search debounce (left armed, it fired ~300ms later and silently reverted the chip); the search input carries an accessible name; `?state=running, failed` parses like the unpadded form instead of dropping the padded value; the CSV toggle helper is defined once in `model/urlParams.ts` rather than twice; the agents tab tells "nothing running" apart from "the filter matched nothing", offering a clear-filters button instead of setup links that would be wrong advice there; edges stop emitting SMIL particles above 40 simultaneously active edges, keeping the colored stroke; and the drawer's error banner clears when a retried action succeeds. ([#12983](https://github.com/diegosouzapw/OmniRoute/pull/12983)) +- **fix(i18n):** the nine locales added with the EU-language batch (Greek, Estonian, Irish, Croatian, Lithuanian, Latvian, Maltese, Slovenian, Serbian) were missing the eleven Orchestration Canvas keys that Phase 3 introduced, so the compare-runs panel and the "no runs match these filters" empty state fell back to English in those languages (`deepMergeFallback` substitutes English for an absent key, so nothing rendered blank — it rendered untranslated). The coverage gate does not catch this: it enforces an 80% floor per locale, and eleven missing keys out of ~13,000 leaves coverage at 99.9%. Translated for real in each language, calibrated against the wording each file already uses for "run", "filter" and "skill". ([#13294](https://github.com/diegosouzapw/OmniRoute/pull/13294)) +- **fix(dashboard):** five follow-ups from the Phase 2 Orchestration Canvas review. The History drawer now shows the memory section for runs that already left the live TTL window: the persisted `memory_hits` event is parsed into `metadata.memoryHits` with the same defensive validation the drawer applies, and — because that event is observability rather than a state transition — it no longer leaks into the timeline, where it had been inheriting the task's state and rendering as a duplicate transition. The A2A memory recall runs against its own 1.5s deadline instead of inheriting the memory backend's 30s one; overshooting degrades exactly like any other recall failure (no hits, task proceeds), and the timer is cleared on both paths. Repeating a Conductor run carries its `requirements.cli`/`requirements.model` forward, so the new run is pinned to the same runner profile and model rather than drifting to whatever the fleet picks. A successful repeat from the Agents tab now focuses the run it created, instead of leaving the operator on the finished one. And the auth test for `POST /api/conductor/tasks` moved into the shared `ROUTES` array rather than restating the pattern. ([#12988](https://github.com/diegosouzapw/OmniRoute/pull/12988)) +- fix(plugins): stop leaking an exit listener per plugin hook timeout, which triggered MaxListenersExceededWarning on plugins that ignore SIGTERM ([#13092](https://github.com/diegosouzapw/OmniRoute/pull/13092)) — thanks @anhtahaylove +- **fix(validation):** Provider node edits no longer fail with a generic "Invalid request" when the optional daily-quota reset fields are left blank. The dashboard sends `dailyQuotaResetTimezone` and `dailyQuotaResetHour` as `null`, and only the hour accepted it. ([#13066](https://github.com/diegosouzapw/OmniRoute/issues/13066)) ([#13083](https://github.com/diegosouzapw/OmniRoute/pull/13083)) — thanks @ntdatt812 +- **fix(combo):** quota-aware expansion drops banned, inactive, missing, and wrong-provider connections before quota fetch or model dispatch; pins and allowlists stay selectors, not a bypass. Antigravity automatic exhaustion now requires a reported zero remaining, so a positive balance below 1% stays eligible. ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- Fix an intermittent failure in the quota-weighted routing test suite: scores are a function of `Date.now()`, so peers with identical quota scored microseconds apart never tied and swapped order. ([#12946](https://github.com/diegosouzapw/OmniRoute/pull/12946)) +- fix(volcengine): resolve quota window mapping gaps, safe multi-connection console binding, and response cookie sanitization ([#12950](https://github.com/diegosouzapw/OmniRoute/pull/12950)) — thanks @HouMinXi +- **fix(test):** resolve the WebDAV handler path with `fileURLToPath` so the suite's 37 WebDAV tests run on Windows instead of failing with a doubled `C:\C:\` drive prefix ([#13196](https://github.com/diegosouzapw/OmniRoute/pull/13196)) — thanks @anhtahaylove +- **fix(ui):** fall back from invalid AnySearch icon alias (direct commit `9587e07b69`) +- **fix(sse):** stop rebuilding a truncated summary from the collector's cap-dropped event array ([#12718](https://github.com/diegosouzapw/OmniRoute/pull/12718)) — thanks @hartmark +- **fix(conversations):** show a pending spinner for unresolved tool nodes instead of "(empty)" ([#12727](https://github.com/diegosouzapw/OmniRoute/pull/12727)) — thanks @hartmark +- **fix(streaming):** fail fast when an upstream stream produces only lifecycle/heartbeat events, never real content ([#12741](https://github.com/diegosouzapw/OmniRoute/pull/12741)) — thanks @hartmark +- **fix(responses-continuation):** bridge the write-in-flight window with an in-memory pending store ([#12854](https://github.com/diegosouzapw/OmniRoute/pull/12854)) — thanks @hartmark +- **fix(translator):** drop or map agent_message on Chat Completions fallback ([#12880](https://github.com/diegosouzapw/OmniRoute/pull/12880)) — thanks @RaviTharuma +- **fix(api):** page and stream-complete GET /v1/models for large catalogs ([#12882](https://github.com/diegosouzapw/OmniRoute/pull/12882)) — thanks @RaviTharuma +- **fix(quota):** align AUTH window labels with usage API and clarify 503 ([#12884](https://github.com/diegosouzapw/OmniRoute/pull/12884)) — thanks @RaviTharuma +- **fix(ci):** clear two base-reds on release/v3.8.51 — mutation-coverage gate + image-only-model guard ([#12945](https://github.com/diegosouzapw/OmniRoute/pull/12945)) +- **fix(api):** scope batch bulk-delete to the calling API key ([#13211](https://github.com/diegosouzapw/OmniRoute/pull/13211)) +- **fix(tests):** align model-sync log assertion to shipped path redaction (base-red) ([#13227](https://github.com/diegosouzapw/OmniRoute/pull/13227)) +- **fix(test):** make the ToS heading guard actually require the parentheses ([#13228](https://github.com/diegosouzapw/OmniRoute/pull/13228)) +- **fix(quality):** register 4 drifted covering tests in stryker tap.testFiles ([#13229](https://github.com/diegosouzapw/OmniRoute/pull/13229)) +- **fix(logging):** mask *-api-key headers in request-log pipeline (#13273) ([#13401](https://github.com/diegosouzapw/OmniRoute/pull/13401)) — thanks @KooshaPari +- **fix(tests):** correct stale Jina catalog prefix in custom-models test ([#13403](https://github.com/diegosouzapw/OmniRoute/pull/13403)) — thanks @KooshaPari +- **fix(backend):** prune old backups after health-check-repair snapshots (#13308) ([#13404](https://github.com/diegosouzapw/OmniRoute/pull/13404)) — thanks @KooshaPari +- **fix(backend):** guard memory_fts_au trigger with WHEN clause to stop FTS bloat (#13326) ([#13405](https://github.com/diegosouzapw/OmniRoute/pull/13405)) — thanks @KooshaPari +- **fix(free-tiers):** remove obsolete Together $25 signup credit (#12526) ([#13407](https://github.com/diegosouzapw/OmniRoute/pull/13407)) — thanks @KooshaPari +- **fix:** skip non-chat generation models in Test all models ([#13409](https://github.com/diegosouzapw/OmniRoute/pull/13409)) — thanks @KooshaPari +- **fix(providers):** de-duplicate agy and antigravity model catalogs ([#13410](https://github.com/diegosouzapw/OmniRoute/pull/13410)) — thanks @KooshaPari +- **fix(dashboard):** send provider in body for mini-playground web search ([#13411](https://github.com/diegosouzapw/OmniRoute/pull/13411)) — thanks @KooshaPari +- **fix(resilience):** add CJK quota-exhaustion patterns to 429 classifier ([#13412](https://github.com/diegosouzapw/OmniRoute/pull/13412)) — thanks @KooshaPari +- **fix(backend):** eval runner: failed calls no longer score as passed + dotAll regex ([#13413](https://github.com/diegosouzapw/OmniRoute/pull/13413)) — thanks @KooshaPari +- **fix(backend):** eval runner opts out of output-style and memory injection ([#13414](https://github.com/diegosouzapw/OmniRoute/pull/13414)) — thanks @KooshaPari +- **fix:** chain serial quarantine step in npm test script (fixes #13286) ([#13418](https://github.com/diegosouzapw/OmniRoute/pull/13418)) — thanks @KooshaPari +- **fix(antigravity):** derive stable session ID from credentials for prompt-cache reuse (#13305) ([#13424](https://github.com/diegosouzapw/OmniRoute/pull/13424)) — thanks @KooshaPari +- **fix(memory):** merge memory into top-level system field instead of unshifting at messages[0] (#13425) ([#13427](https://github.com/diegosouzapw/OmniRoute/pull/13427)) — thanks @KooshaPari +- **fix(compression):** preserve instruction blocks from lossy engines ([#13523](https://github.com/diegosouzapw/OmniRoute/pull/13523)) — thanks @KooshaPari +- **fix(compression):** preserve polarity words and newlines in ultra heuristic ([#13524](https://github.com/diegosouzapw/OmniRoute/pull/13524)) — thanks @KooshaPari +- **fix(compression):** run user preservePatterns before built-in patterns (#13457) ([#13525](https://github.com/diegosouzapw/OmniRoute/pull/13525)) — thanks @KooshaPari +- **fix(reasoning):** expand deepseek-v4 regex to match v4.1+ and add command-code provider (#13430) ([#13528](https://github.com/diegosouzapw/OmniRoute/pull/13528)) — thanks @KooshaPari +- **fix(providers):** add alwaysReasons metadata for reka-flash-3 ([#13531](https://github.com/diegosouzapw/OmniRoute/pull/13531)) — thanks @KooshaPari +- **fix(playground):** allow Compare columns to scroll independently ([#13532](https://github.com/diegosouzapw/OmniRoute/pull/13532)) — thanks @KooshaPari +- **fix(security):** empty whitelist allows through to prevent admin lockout ([#13534](https://github.com/diegosouzapw/OmniRoute/pull/13534)) — thanks @KooshaPari +- **fix(tests):** add DISABLE_IOREG_STRATEGY env var for macOS test compatibility (fixes #13467) ([#13539](https://github.com/diegosouzapw/OmniRoute/pull/13539)) — thanks @KooshaPari +- **fix(zed-hosted):** map developer role and Gemini enums for Zed proxy ([#13543](https://github.com/diegosouzapw/OmniRoute/pull/13543)) — thanks @KooshaPari +- **fix(analytics):** normalize provider aliases to canonical id in usage_history ([#13545](https://github.com/diegosouzapw/OmniRoute/pull/13545)) — thanks @KooshaPari +- **fix(combo):** use traceId for call log id to prevent UNIQUE constraint failure ([#13546](https://github.com/diegosouzapw/OmniRoute/pull/13546)) — thanks @KooshaPari +- **fix(auth):** check model lockout before returning synthetic noauth connection ([#13547](https://github.com/diegosouzapw/OmniRoute/pull/13547)) — thanks @KooshaPari +- **fix(guardrails):** skip credential redaction for base64 image data URLs ([#13550](https://github.com/diegosouzapw/OmniRoute/pull/13550)) — thanks @KooshaPari +- **fix(resilience):** increase requestQueue.maxWaitMs default from 15s to 30s ([#13553](https://github.com/diegosouzapw/OmniRoute/pull/13553)) — thanks @KooshaPari +- **fix(docs):** document OMNIROUTE_READY_TIMEOUT_MS and allowlist the test-only DISABLE_IOREG_STRATEGY ([#13692](https://github.com/diegosouzapw/OmniRoute/pull/13692)) + ### 📝 Maintenance ---- +- **docs(readme):** reconcile live v3.8.50 provider, free-tier, CLI, routing, test, community, sponsor, acknowledgment, and SVG metrics with their audited source denominators, including a deduplicated OmniRoute-in-Action snapshot and distinct contributor rankings for merged pull requests, GitHub-attributed commits, and Git history ([#11356](https://github.com/diegosouzapw/OmniRoute/pull/11356)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **docs:** sync the canonical provider count 354 → 356 across `README.md`, `AGENTS.md`, `llm.txt` (+ 42 i18n mirrors), the four README SVG diagrams, `docs/reference/PROVIDER_REFERENCE.md` (regenerated) and the `package.json` description after Opper (#11629) and 1min.ai (#11631) boarded the catalog — closes the `check:docs-counts-sync` strict drifts that kept `release/v3.8.51` red ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11673](https://github.com/diegosouzapw/OmniRoute/pull/11673)) — thanks @jonlwheat2-gif +- **test(providers):** update count-derived assertions after the v3.8.51 provider additions — `APIKEY_PROVIDERS` 233 → 235 (Opper #11629 + 1min.ai #11631), reserved-prefix REGISTRY walk 395 → 398, `WEB_FETCH_PROVIDERS` now includes `nimble-search` (#11620), and the provider translate-path golden snapshot regenerated ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11674](https://github.com/diegosouzapw/OmniRoute/pull/11674)) — thanks @jonlwheat2-gif +- **test(sse):** bump the hard-lease connection-query inventory for `src/lib/tokenHealthCheck.ts` to 2 — the verify-only web-cookie sweep added by #11495 split the single `getProviderConnections` call into oauth + cookie variants, which the frozen inventory had not tracked ([#11495](https://github.com/diegosouzapw/OmniRoute/pull/11495)). ([#11677](https://github.com/diegosouzapw/OmniRoute/pull/11677)) — thanks @jonlwheat2-gif / @oyi77 +- **chore(release):** clear the `release/v3.8.51` base-red gates — docs migration count (+38 i18n mirrors), stryker `tap.testFiles`, lockfile registry host, stale ESLint suppressions, the 7 lint regressions they were hiding, the stale `APIKEY_PROVIDERS` partition count (231 → 233), the `glm-5.3-max` tier missing from the GLM test table, a dead `modelType === "chat"` comparison in the models catalog, and the `resolveLiveWsUrl` / `sanitizeLiveWsPort` helpers from #11388 that a later merge dropped from `wsPath.ts` (dashboard live WebSocket URL resolution) ([#11502](https://github.com/diegosouzapw/OmniRoute/pull/11502)) — thanks @MumuTW +- **chore(free-models):** the free-tier regime table now declares which totals figure each regime feeds and whether it may take the no-auth shortcut, so the sets that used to repeat those answers by hand are derived from it — a new `freeType` no longer compiles until it has answered every question, instead of silently contributing to no total ([#11537](https://github.com/diegosouzapw/OmniRoute/pull/11537)) — thanks @maxmad64bis +- **docs(routing):** LKGP strategy copy now states provider-level stickiness and the explicit rules fallback instead of promising per-target pinning ([#11555](https://github.com/diegosouzapw/OmniRoute/pull/11555)) — thanks @maxmad64bis +- **test(chatcore):** pin the response tool-name alias resolution against the ordering hazard that broke every Gemini/Antigravity MCP tool call in v3.8.49. `extractRequestToolIdentityMap` deletes `translatedBody._toolNameMap`, so the later read is always undefined and the ledger survives only through the `requestToolIdentityMap` fallback — removing that fallback previously left the entire tool-name suite green. The resolution moves into `resolveResponseToolNameMap()` next to the map it depends on, with a regression guard that fails without the recovery. No behaviour change. ([#11575](https://github.com/diegosouzapw/OmniRoute/pull/11575)) — thanks @amartinawi +- **docs(env):** document `OMNIROUTE_CORPUS_CACHE_SIZE` in `.env.example` and `ENVIRONMENT.md`, so the env/docs contract check stops failing Docs Gates on every branch ([#11576](https://github.com/diegosouzapw/OmniRoute/pull/11576)) — thanks @ntdatt812 +- **chore(stryker):** register `route-guard-tunnel-processes-local-only` and `universal-quota-aware-routing` in `tap.testFiles`, so their mutant kills count for the three modules they cover ([#11577](https://github.com/diegosouzapw/OmniRoute/pull/11577)) — thanks @ntdatt812 +- **test(authz):** pin `LOCAL_ONLY_API_GET_EXEMPTIONS` by exact membership instead of by entry count, so the guard names the offending path and also catches a substitution ([#11580](https://github.com/diegosouzapw/OmniRoute/pull/11580)) — thanks @ntdatt812 +- **test(build):** pin `resolveNextBuildBundlerFlag` to the `OMNIROUTE_USE_TURBOPACK` contract it implements, across runtimes, instead of asserting an unimplemented Bun override ([#11583](https://github.com/diegosouzapw/OmniRoute/pull/11583)) — thanks @ntdatt812 +- **test(claude-web):** the Claude Web transport test's barrel anchors no longer pin the exact construction spelling. #11421 made the executor map lazy, so `/"claude-web": new ClaudeWebExecutor\(\)/` stopped matching and the test went red on `release/v3.8.51` itself — a base-red gate every open PR inherited. The anchors now match the alias key and the class name and tolerate either spelling in between, and were checked to still fail when an alias is genuinely repointed at another executor ([#11585](https://github.com/diegosouzapw/OmniRoute/pull/11585)) — thanks @ntdat812 +- **test(models):** pin `qwen3.8-max` resolution per provider — two of the four catalogs now serve the bare id and no longer carry `-preview` ([#11590](https://github.com/diegosouzapw/OmniRoute/pull/11590)) — thanks @ntdatt812 +- **test(executors):** drop the stale 128-tool truncation assertion that contradicted the #11444 pass-through contract ([#11591](https://github.com/diegosouzapw/OmniRoute/pull/11591)) — thanks @ntdatt812 +- **test(token-health):** pin the current terminal-skip boundary — expired with retry budget is probed, `account_deactivated` stays skipped ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)) — thanks @ntdatt812 +- **test(models):** follow #11503's retarget of the `gemini-3-pro-high` alias to the hyphenated catalog id ([#11593](https://github.com/diegosouzapw/OmniRoute/pull/11593)) — thanks @ntdatt812 +- **refactor(video bridge):** extract per-video acquisition, whole-result caching, description, and metrics/abort/cleanup out of `VideoBridgeGuardrail.preCall` into a `processVideoPart` seam in a new `videoBridgePipeline.ts`, behind explicit `VideoMediaBrokerPort`, `VideoAudioTranscriptionPort`, and `VideoDrilldownPort` boundaries; `preCall` now only handles request traversal, policy, and response aggregation. The Video tab's FFmpeg/ffprobe runtime status is now an explicit `unknown` / `restricted` / `unavailable` / `available` state instead of a nullable boolean pair, fixing a case where an in-flight or failed probe was mislabeled as "install FFmpeg" ([#11657](https://github.com/diegosouzapw/OmniRoute/issues/11657)). ([#12016](https://github.com/diegosouzapw/OmniRoute/pull/12016)) +- **test(opencode-plugin):** add unit test coverage for `formatFreeBudget()` naming helper ([#11660](https://github.com/diegosouzapw/OmniRoute/pull/11660)) — thanks @f9td56dbgh-hub +- **docs(video):** clarify that the Video Bridge transcript `source` field (`client`, `embedded`, `audio-bridge`) is presently caller-declared and not yet server-verified — OmniRoute enforces the enum shape but does not cryptographically confirm that an `embedded`/`audio-bridge` label came from a server-owned extraction ([#11661](https://github.com/diegosouzapw/OmniRoute/issues/11661)). ([#12001](https://github.com/diegosouzapw/OmniRoute/pull/12001)) +- **chore(providers):** retire the distributed Raycast Relay and Hailuo Web integrations after a provenance audit confirmed substantial derivation from GPL-3.0 sources; the official GitHub and MiniMax API providers remain available. Migration 163 disables legacy connections and invalidates their active leases while preserving connection IDs, audit/history rows, quota snapshots, usage records, and fail-closed API-key connection policies. ([#11691](https://github.com/diegosouzapw/OmniRoute/pull/11691)) +- **chore(providers):** retire the Felo Web provider while its GPL-derived provenance remains on licensing HOLD ([#11698](https://github.com/diegosouzapw/OmniRoute/pull/11698)) — stale runtime and database references now fail closed while existing connection, lease, and usage history remains preserved for auditability. +- **fix(sse):** retire the Gemini Web image-generation bridge while provenance and licensing remain on HOLD, preserving Gemini Web chat/auth/tools and legitimate Gemini/Nano Banana image providers ([#11708](https://github.com/diegosouzapw/OmniRoute/pull/11708)) +- **chore(audio):** retire the built-in EdgeTTS provider and its 11-voice catalog while provenance/licensing review remains on HOLD; the generic `/v1/audio/speech` endpoint and the other speech providers remain available ([#11711](https://github.com/diegosouzapw/OmniRoute/pull/11711)) +- **fix(providers):** retire the unofficial Qwen Web integration and its `qwen-web`/`qw` identities while third-party provenance remains on HOLD ([#11713](https://github.com/diegosouzapw/OmniRoute/pull/11713)) — direct requests now fail closed with a sanitized HTTP 410 and combos skip the retired target, while migration guards preserve connection IDs, allowlists, usage/audit history and invalidate active leases. Supported Qwen API, Qwen Code, cloud, token-plan, audio, local and model-name surfaces remain available; restoring Qwen Web requires a qualified provenance/license decision. +- **fix(providers):** retire `microsoft-designer-web` and its `msdesigner` alias from runtime and catalogs while preserving stored credentials, provider history and allowlists as inactive records — source dependency on gpt4free's [`MicrosoftDesigner.py`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/g4f/Provider/needs_auth/MicrosoftDesigner.py) is proven at snapshot `0943d16eef757a44deb60651c07bbe5fa958cafe`, whose [`LICENSE`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/LICENSE) is GPL-3.0; expressive adaptation is probable, which records provenance/licensing risk without asserting a legal conclusion ([#11720](https://github.com/diegosouzapw/OmniRoute/pull/11720)). +- **docs(legal):** centralize pinned MIT notices for the vendored GCF codec and local flag assets ([#11726](https://github.com/diegosouzapw/OmniRoute/pull/11726)) +- Split the 2,952-line `adobeFireflyClient.ts` into nine focused modules (catalog, credentials, payload, ARP session, upload, responses, IMS, account, poll) behind an unchanged barrel — no consumer edits and no public API change (#11728). — thanks @MumuTW +- **fix(dashboard):** replaced the Model Lockout card's unprovenanced notification MP3 with a short, locally synthesized Web Audio chime ([#11731](https://github.com/diegosouzapw/OmniRoute/pull/11731)) — both toggles retain optional audible feedback without shipping replacement media or third-party code, and unsupported, suspended, or rejected audio contexts fail open without blocking the setting change. +- **chore(deepseek-web):** remove the provenance-unresolved DeepSeek PoW WASM binary and its runtime loader/tracing while retaining the existing JavaScript solver (slower at high difficulty) ([#11732](https://github.com/diegosouzapw/OmniRoute/pull/11732)). +- **chore(assets):** remove four unused provider assets whose redistribution provenance could not be verified while retaining the active Freebuff SVG ([#11735](https://github.com/diegosouzapw/OmniRoute/pull/11735)) +- **docs(assets):** retired two raster images whose redistribution provenance could not be verified ([#11736](https://github.com/diegosouzapw/OmniRoute/pull/11736)) — PicoClaw documentation now reuses the existing SVG, while Zoo Code remains a text link without an image. +- **docs(legal):** add pinned notices for six LobeHub-derived provider SVGs and 65 byte-exact theSVG assets, retaining registry claims as unverified metadata with explicit trademark and HOLD boundaries ([#11737](https://github.com/diegosouzapw/OmniRoute/pull/11737)). +- **chore(quality):** added a versioned 225/225 provider-asset provenance manifest and blocking drift gate ([#11743](https://github.com/diegosouzapw/OmniRoute/pull/11743)) — the inventory pins magic MIME and SHA-256 for every physical file, records immutable source matches for 71 assets, keeps 69 probable and 85 unresolved entries explicit but non-blocking, and treats provenance as source evidence rather than copyright or trademark clearance. +- **refactor(mitm):** replace four previously derived Traffic Inspector utilities — conversation normalization, SSE merging, secret masking, and header sanitization — with independent clean-room implementations based on public provider schemas, WHATWG SSE, and RFC 6750, while keeping the two independently classified passthrough surfaces unchanged ([#11748](https://github.com/diegosouzapw/OmniRoute/pull/11748)). +- Added a unit test that fails seven days before any dated pack under `config/` (`validUntil` and sibling keys) lapses, naming the file and key. The Alibaba free-tier pack expired on 2026-08-27 and turned every PR red the next morning with no commit involved; renewal now happens on someone's terms, not the clock's. ([#11891](https://github.com/diegosouzapw/OmniRoute/pull/11891)) +- `check:workflows` now fails (under `--strict`/`--ratchet`) when any job routed to a self-hosted runner publishes with `--provenance` — npm rejects that with `422` at the registry, which in v3.8.50 only surfaced after the tag and Docker images were public. ([#11895](https://github.com/diegosouzapw/OmniRoute/pull/11895)) +- `scripts/ops/runner-janitor.sh` now proves a path is idle with one `lsof` snapshot and removes stale leftovers itself (tmpfs after 3 h — it is RAM — disk after 24 h), kills orphan `next-build` processes, prunes checkouts of stopped runners, and alerts on memory pressure; `--dry-run` shows exactly what it would do. `docs/ops/RUNNER_BOX.md` reconciled to the measured box (31 GB, 10 listeners). ([#11893](https://github.com/diegosouzapw/OmniRoute/pull/11893)) +- The `next-build` artefact (1.3 GB) is now written and read under `$RUNNER_TEMP` (per-runner, on disk) instead of `/tmp`, which on the self-hosted pool is a 12 GB tmpfs in RAM. Landing it there took 27–32 of the publish job's 76 minutes, and the fixed `/tmp/e2e-build.tar.gz` name let E2E jobs on different runners overwrite each other's download. ([#11896](https://github.com/diegosouzapw/OmniRoute/pull/11896)) +- The CI `build` job now runs in two concurrency lanes — `main` and pull requests — so a release build is never queued behind (or OOM-killed beside) PR builds on the self-hosted pool, which holds one `next-build` comfortably and two at the edge. ([#11901](https://github.com/diegosouzapw/OmniRoute/pull/11901)) +- Re-freeze the ESLint suppressions on `release/v3.8.51` from a clean-room measurement (2 stale file entries pruned, 55 pre-existing `no-explicit-any` in six new files frozen under #11924) and drop the dead `GPT_SIZE_MAP` constant orphaned by the Adobe Firefly client split, so `No new ESLint warnings` stops failing every PR with exit 2 (Refs #11924) ([#11955](https://github.com/diegosouzapw/OmniRoute/pull/11955)) +- Type the 55 `no-explicit-any` sites that had been frozen under #11924 — four redundant casts in `socksConnectorWithFamily.ts` (undici/socks types already accept them) and the mocks/fixtures of the socks-timeout and isFree suites — and drop their suppression entries; the ESLint ratchet shrinks from 5487 to 5432 (Closes #11924) ([#11975](https://github.com/diegosouzapw/OmniRoute/pull/11975)) +- Take the two hosted-runner builds off the PR rail: `Build App` (`build.yml`) is `workflow_dispatch`-only and `DAST smoke (PR)` runs only for PRs into `main` — the 7 GB hosted VM cannot build `release/v3.8.51` in any profile (19/30 red, VM shutdown ~8 min into `next build`) and both checks had turned into permanent noise on every release PR; the bundle stays validated by `ci.yml` on `main` and by `nightly-release-green` on `release/**` (Closes #11946) ([#11962](https://github.com/diegosouzapw/OmniRoute/pull/11962)) +- Move the four nightly jobs that build the backend (`nightly-schemathesis`, `nightly-llm-security` promptfoo + garak, `nightly-resilience` axe-a11y) off the hosted 7 GB runner — where they died on `release/v3.8.51` unseen — onto the box's new `omni-light` pool (two listeners), and document the reshaped fleet (4 active OmniRoute listeners: 2 `omni-build` + 2 `omni-light`, janitor ceiling 4) (Closes #11965) ([#11967](https://github.com/diegosouzapw/OmniRoute/pull/11967)) +- Stop painting every fork PR into `release/**` red: `quality.yml` `Build (advisory)` is skipped (GitHub still reports `continue-on-error` failures as check FAILURE). Hosted `ubuntu-latest` cannot finish `npm run build` on this tree — same class as #11962 taking `build.yml` off the PR rail. `docker-publish.yml` amd64 now runs on the `.113` `omni-build` pool (31 GB / 32 cores, two listeners) with Turbopack, shares the `heavy-build-main` lane with `ci.yml` `Build` so it queues instead of becoming a third heavy, and keeps per-ref concurrency (a merge storm was starting 8 concurrent OOM builds). arm64 stays on `ubuntu-24.04-arm` with webpack — there is no ARM box. Fallback when `USE_VPS_RUNNER` is off: hosted amd64 + webpack (#11976). ([#12021](https://github.com/diegosouzapw/OmniRoute/pull/12021)) +- `docker-publish.yml` builds with webpack (`OMNIROUTE_USE_TURBOPACK=0`) on **both** arches, including amd64 on the `.113` `omni-build` pool. Turbopack had the RAM (31 GB, `omniroute-113-6`) and still panicked (`TurbopackInternalError: there must be a path to a root`, run 33253576569, 26 min in). The same tree's arm64 webpack build on hosted `ubuntu-24.04-arm` succeeded (run 33264823398). Dockerfile already documents webpack as the Docker escape hatch (#11976). ([#12050](https://github.com/diegosouzapw/OmniRoute/pull/12050)) +- **docs(free-tier):** declare the counting vs deciding regimes for "is it free?" and guard the deciding path from DB-backed catalog resolution ([#12226](https://github.com/diegosouzapw/OmniRoute/pull/12226)) — thanks @maxmad64bis +- **docs(auto-combo):** The mode pack table in `docs/routing/AUTO-COMBO.md` now lists all six shipped packs with every weight each one sets, replacing a four-pack table whose numbers had also drifted from the source. It states plainly that no pack sets `quality`, so selecting any pack silences the observed-quality signal. Six more documents that quote the scoring factor count joined the `check:docs-counts` gate, which caught five stale claims — including one naming nine factors that do not exist — and two stale mode pack counts. The dashboard routing panel, which offered four of the six packs and labelled the default strategy "6-Factor Scoring", is now covered by a test; the two packs it was missing are `reliability-first` and `chaos-mode`, the latter labelled as the fault-injection profile it is rather than as one more routing preference ([#12316](https://github.com/diegosouzapw/OmniRoute/pull/12316)) — thanks @maxmad64bis +- **chore(quality):** rebaseline `src/lib/db/apiKeys.ts` for the ACL the key-creation path now preserves ([#12352](https://github.com/diegosouzapw/OmniRoute/pull/12352)) ([#12673](https://github.com/diegosouzapw/OmniRoute/pull/12673)) — thanks @KrzysiekSko +- **chore(providers):** bump the Claude Code wire identity and the Devin bridge image pin from `2.1.220` to `2.1.258` ([#12402](https://github.com/diegosouzapw/OmniRoute/pull/12402)) — thanks @ggiak ([#12604](https://github.com/diegosouzapw/OmniRoute/pull/12604)) +- **docs(env):** align `.env.example`, the README Bun section, and the troubleshooting guide with the code: `OMNIROUTE_USE_TURBOPACK` also governs `npm run build` (not dev-only), `bun run build` follows that flag instead of auto-selecting Webpack, `OMNIROUTE_CHAT_MAX_HEAVY_IN_FLIGHT` is unset by default (no request-count cap), and the structural `503 chat_admission_busy` message matches `chatAdmissionResponses.ts` (#12404 — thanks @pacocartones) +- **chore(electron):** upgrade the desktop app to Electron 44 (Chromium 152, Node 24.18.1) ([#12217](https://github.com/diegosouzapw/OmniRoute/pull/12217)). **Requires macOS 13 (Ventura) or later** — Chromium dropped macOS 12 (Monterey), so Monterey users must stay on an earlier OmniRoute desktop build. Windows and Linux are unaffected; the app already shipped only x64/arm64, so Electron 44 dropping 32-bit builds changes nothing. Removes the `openAsHidden`/`wasOpenedAsHidden` login-item fields deleted in Electron 44 — hidden autostart continues to work through the `--hidden` argument registered with the login item ([#12554](https://github.com/diegosouzapw/OmniRoute/pull/12554)) — thanks @app/dependabot +- **chore(quality):** rebaseline `src/sse/handlers/chat.ts` for the effective-input persistence the continuation fix needs ([#12641](https://github.com/diegosouzapw/OmniRoute/pull/12641)) ([#12680](https://github.com/diegosouzapw/OmniRoute/pull/12680)) — thanks @hartmark +- `Coverage` job on `ci.yml`: the informational Codecov upload gets its own 5-minute ceiling and `continue-on-error`, and the job budget grows from 20 to 30 minutes (the 8-shard c8 merge alone takes ~10) — a stalled upload no longer ends the job `cancelled` and drags a fully green `main` run's conclusion down with it ([#11972](https://github.com/diegosouzapw/OmniRoute/pull/11972)) +- CI hardening on the PR rail: the four `Unit Tests fast-path` shards get `timeout-minutes: 30` (a hung shard held a PR for 64 min instead of GitHub's 6 h default) and both ESLint file caches lose their `restore-keys` fallback, so a cache built under another suppressions file or lint config can no longer report stale verdicts (Refs #11600, #11924) ([#11963](https://github.com/diegosouzapw/OmniRoute/pull/11963)) +- Every CI job that runs a `next build` (`build`, the npm `publish`, both release-green validations) now targets the `omni-build` runner label, which only two of the eight self-hosted runners carry. The box holds one build comfortably and two at the edge; a third now queues on GitHub instead of being OOM-killed by the kernel. ([#11932](https://github.com/diegosouzapw/OmniRoute/pull/11932)) +- **refactor(combo):** move `handleRoundRobinCombo` (and `resolveTargetTokenLimit`) into `open-sse/services/combo/roundRobinCombo.ts`. `combo.ts` drops from 2164 to 1014 lines (`split("\n").length`); the leaf is 1199 (under the 1200 new-file cap). The round-robin call site uses a dynamic `import()` so `releaseStickyPinOnFailure` / `clearStaleLKGP` can stay exported from `combo.ts` without a static cycle. Skip / sticky / semaphore / safety-timer behavior is unchanged. ([#12811](https://github.com/diegosouzapw/OmniRoute/pull/12811)) — thanks @HouMinXi +- **chore(quality):** rebaseline the file-size caps the error-boundary campaign grew past (`open-sse/executors/codex.ts`, `open-sse/vendor/codex-chatgpt-web/bridge.ts`, both via [#12444](https://github.com/diegosouzapw/OmniRoute/pull/12444)) ([#12654](https://github.com/diegosouzapw/OmniRoute/pull/12654)) +- **chore(quality):** rebaseline the file-size caps the hartmark batch grew past (`combos/page.tsx` via [#12355](https://github.com/diegosouzapw/OmniRoute/pull/12355), `open-sse/services/combo.ts` via [#12338](https://github.com/diegosouzapw/OmniRoute/pull/12338)) ([#12623](https://github.com/diegosouzapw/OmniRoute/pull/12623)) — thanks @hartmark +- **chore(quality):** rebaseline the file-size caps the HouMinXi batch grew past when its PRs stacked (`providers/page.tsx`, `chatCore.ts`, `accountFallback.ts`) — each PR measured correctly in isolation, none saw the stacking ([#12619](https://github.com/diegosouzapw/OmniRoute/pull/12619)) +- **chore(quality):** rebaseline `open-sse/services/combo.ts` for the reset-aware scoring the HouMinXi batch stacked ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) ([#12678](https://github.com/diegosouzapw/OmniRoute/pull/12678)) — thanks @HouMinXi +- **docs:** sync provider catalog count (direct commit `4f11b2ae3d`) +- **docs(skills):** sync omni-inference search fallback (direct commit `b4e76a7ed9`) +- **chore(lint):** prune two stale base suppressions (direct commit `1325047d56`) +- **test(auto-combo):** tolerate full-suite rotation load (direct commit `ad3e293f9f`) +- **test:** drain inherited quality snapshot reds (direct commit `21ed68d8ac`) +- **chore(quality):** rebaseline measured CLI bundle growth (direct commit `d35b3f9779`) +- **test(ui):** reproduce invalid AnySearch icon alias (direct commit `12cc6ca834`) +- **test(translator):** fix the relative imports of the relocated deferred-finish test (direct commit `777d9d1629`) +- **docs(counts):** bring the documented provider and migration counts to the measured values (direct commit `e70bea30e9`) +- **test(security):** drop the guard-target set left unused by the sync (direct commit `a32ba0e362`) +- **chore(db):** renumber model_capabilities migration 163 → 169 (cross-branch collision with 163_radar_feed_cache_generated_at) (direct commit `70f5d4cbf9`) +- **docs(cli):** document CLI_PRIME_AGENT_BIN and correct the CLI Agents count (direct commit `9058e39b61`) +- **chore(deps):** refresh runtimes and adopt ESLint 10 ([#11259](https://github.com/diegosouzapw/OmniRoute/pull/11259)) — thanks @backryun +- **refactor(local-corpus):** implement dynamic root resolution and LRU cache ([#11491](https://github.com/diegosouzapw/OmniRoute/pull/11491)) — thanks @raheemuddin786 +- **chore(quality):** prune stale eslint-suppressions entries ([#11596](https://github.com/diegosouzapw/OmniRoute/pull/11596)) +- **chore(ci):** unblock quality gates - env-doc sync + stryker tap.testFiles registration (#9282) ([#11609](https://github.com/diegosouzapw/OmniRoute/pull/11609)) — thanks @Meet6338-X +- **chore(ci):** point the circular-deps gate at dpdm's real JS entrypoint ([#11615](https://github.com/diegosouzapw/OmniRoute/pull/11615)) — thanks @benzntech +- **chore(quality):** rebaseline file-size for #11448/#11495/#11561 growth ([#11630](https://github.com/diegosouzapw/OmniRoute/pull/11630)) +- **deps:** realign onnxruntime-node pin to @huggingface/transformers' 1.24.3 lockstep ([#11633](https://github.com/diegosouzapw/OmniRoute/pull/11633)) — thanks @jonlwheat2-gif +- **test(autoCombo):** port models_dev_tier #11508 guard from node:test to vitest ([#11635](https://github.com/diegosouzapw/OmniRoute/pull/11635)) — thanks @jonlwheat2-gif +- **chore(quality):** rebaseline file-size for #11083/#11631/#11628 growth ([#11636](https://github.com/diegosouzapw/OmniRoute/pull/11636)) +- **chore(lint):** prune stale ESLint suppressions (base-red #11449) ([#11644](https://github.com/diegosouzapw/OmniRoute/pull/11644)) — thanks @jonlwheat2-gif +- **docs(changelog):** aggregate the ten v3.8.50 fragments into CHANGELOG.md ([#11683](https://github.com/diegosouzapw/OmniRoute/pull/11683)) +- **chore(search):** drop unreachable credential sweep after #11642 ([#11687](https://github.com/diegosouzapw/OmniRoute/pull/11687)) — thanks @HouMinXi +- **ci:** add API route TypeScript regression gate ([#11705](https://github.com/diegosouzapw/OmniRoute/pull/11705)) — thanks @Sabeekhann +- **docs(dedup):** correct the CodeQL claim on the tenant-namespace comment ([#11712](https://github.com/diegosouzapw/OmniRoute/pull/11712)) +- **docs(changelog):** add consolidated v3.8.50 stats, top-25 ranking and a .mailmap ([#11715](https://github.com/diegosouzapw/OmniRoute/pull/11715)) +- **docs:** recommend gstack for AI-assisted workflows ([#11770](https://github.com/diegosouzapw/OmniRoute/pull/11770)) — thanks @santosraju99-hub +- **chore(quality):** tighten complexity/cognitive-complexity ratchets to the current tip; land the missed gateways.ts rebaseline ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **docs(i18n):** finish Freepik → Magnific rebrand in locale strings and README ([#11772](https://github.com/diegosouzapw/OmniRoute/pull/11772)) — thanks @turbolego +- **refactor(auto-combo):** fix divergent scoring in combo health reporting ([#11854](https://github.com/diegosouzapw/OmniRoute/pull/11854)) — thanks @jacobsparts +- **chore(changelog):** add missing fragments for #11919/#11918/#11916 ([#11938](https://github.com/diegosouzapw/OmniRoute/pull/11938)) +- **test(check):** escape the runs-on fixture with JSON.stringify, not a quote-only replace ([#11942](https://github.com/diegosouzapw/OmniRoute/pull/11942)) (direct commit `fb7445eaa3`) +- **test(release):** align five suites with the contracts #11933, #11919 and #11876 shipped on release/v3.8.51 ([#11944](https://github.com/diegosouzapw/OmniRoute/pull/11944)) +- **chore(quality):** remove unreachable code and restore test discovery ([#11950](https://github.com/diegosouzapw/OmniRoute/pull/11950)) — thanks @backryun +- **docs(agents):** sync-back landings are fast-forward, never squash ([#11964](https://github.com/diegosouzapw/OmniRoute/pull/11964)) +- **test(infra):** retry recursive temp-dir removal instead of failing a shard on ENOTEMPTY (#11966) ([#11968](https://github.com/diegosouzapw/OmniRoute/pull/11968)) +- **docs(ops):** the .113 heavy-build ceiling is one runner, not two ([#12048](https://github.com/diegosouzapw/OmniRoute/pull/12048)) +- **test(ui):** correct inactive auto-fetch expectation ([#12098](https://github.com/diegosouzapw/OmniRoute/pull/12098)) — thanks @RaviTharuma +- **docs:** document native dependency check escape hatch ([#12101](https://github.com/diegosouzapw/OmniRoute/pull/12101)) — thanks @RaviTharuma +- **test(providers):** regenerate the translate-path golden for OrcaRouter (#11923) ([#12118](https://github.com/diegosouzapw/OmniRoute/pull/12118)) +- **chore(quality):** velocity phase — loosen every numeric baseline by 20% until v4.0, monitor headroom nightly ([#12125](https://github.com/diegosouzapw/OmniRoute/pull/12125)) +- **chore(lint):** batch 0 of #12146 — type the call-log-cap sqlite rows instead of 45 as-any casts ([#12157](https://github.com/diegosouzapw/OmniRoute/pull/12157)) +- **chore(lint):** batch 4 of #12146 — shared/components react-hooks violations resolved ([#12159](https://github.com/diegosouzapw/OmniRoute/pull/12159)) +- **chore(lint):** batch 1 of #12146 — dashboard/cli-code react-hooks violations resolved ([#12160](https://github.com/diegosouzapw/OmniRoute/pull/12160)) +- **chore(lint):** batch 3 of #12146 — dashboard/settings react-hooks violations resolved ([#12162](https://github.com/diegosouzapw/OmniRoute/pull/12162)) +- **chore(lint):** batch 2 of #12146 — resolve the react-hooks compiler violations in dashboard/providers ([#12163](https://github.com/diegosouzapw/OmniRoute/pull/12163)) +- **chore(quality):** register search-432 cooldown test in stryker tap.testFiles ([#12170](https://github.com/diegosouzapw/OmniRoute/pull/12170)) +- **chore(lint):** batch 5 of #12146 — combos, endpoint, provider-stats, api-manager and costs react-hooks violations resolved ([#12174](https://github.com/diegosouzapw/OmniRoute/pull/12174)) +- **build:** add contributor fast profile ([#12192](https://github.com/diegosouzapw/OmniRoute/pull/12192)) — thanks @rafacpti23 +- **docs(readme):** README touch-ups ([#12193](https://github.com/diegosouzapw/OmniRoute/pull/12193)) ([#12194](https://github.com/diegosouzapw/OmniRoute/pull/12194)) ([#12202](https://github.com/diegosouzapw/OmniRoute/pull/12202)) — thanks @rafacpti23 +- **build:** force the webpack bundler in the contributor build profile ([#12197](https://github.com/diegosouzapw/OmniRoute/pull/12197)) — thanks @rafacpti23 +- **build:** skip standalone packaging in the contributor profile ([#12198](https://github.com/diegosouzapw/OmniRoute/pull/12198)) — thanks @rafacpti23 +- **docs(audit):** align every published number with the code and harden check:docs-counts ([#12200](https://github.com/diegosouzapw/OmniRoute/pull/12200)) +- **build:** stub the instrumentation entrypoints in the contributor profile ([#12203](https://github.com/diegosouzapw/OmniRoute/pull/12203)) — thanks @rafacpti23 +- **build:** omit the standalone output target for contributor builds ([#12204](https://github.com/diegosouzapw/OmniRoute/pull/12204)) — thanks @rafacpti23 +- **chore(lint):** batch 6 of #12146 — memory, radar, audit, analytics, cache, usage, activity, home and RequestLoggerV2 react-hooks violations resolved ([#12208](https://github.com/diegosouzapw/OmniRoute/pull/12208)) +- **docs(diagrams):** rename number-carrying diagram files to stable names ([#12210](https://github.com/diegosouzapw/OmniRoute/pull/12210)) +- **docs(dashboard):** redraw onboarding tier-flow SVGs for the real 4-tier model ([#12211](https://github.com/diegosouzapw/OmniRoute/pull/12211)) +- **docs(api):** document every implemented route in openapi.yaml (276 → 692 paths) ([#12212](https://github.com/diegosouzapw/OmniRoute/pull/12212)) +- **chore(lint):** batch 7 of #12146 — final src tail: 37 react-hooks violations across 33 files resolved ([#12244](https://github.com/diegosouzapw/OmniRoute/pull/12244)) +- Revert "docs: recommend gstack for AI-assisted workflows (#11770)" ([#12248](https://github.com/diegosouzapw/OmniRoute/pull/12248)) ([#12249](https://github.com/diegosouzapw/OmniRoute/pull/12249)) +- **docs:** Chaos Mode setup guide + weighted strategy semantics ([#12250](https://github.com/diegosouzapw/OmniRoute/pull/12250)) +- **docs(agents):** protected-surface merge rule — operator approval for agent-instruction files ([#12253](https://github.com/diegosouzapw/OmniRoute/pull/12253)) +- **chore(quality):** register native-codex turn-pin tests in stryker tap.testFiles ([#12263](https://github.com/diegosouzapw/OmniRoute/pull/12263)) +- **chore(quality):** dedupe tap.testFiles entries added by racing base-red fixes ([#12265](https://github.com/diegosouzapw/OmniRoute/pull/12265)) +- **docs(readme):** introduce OmniRouteTray — the macOS menu-bar companion ([#12276](https://github.com/diegosouzapw/OmniRoute/pull/12276)) — thanks @ggiak +- **chore(proxy):** purge the legacy 1proxy residue — sync/rotator modules, dead DB exports, dead settings tab and flag, docs (#12091) ([#12290](https://github.com/diegosouzapw/OmniRoute/pull/12290)) +- **chore(quality):** baseline-headroom skips generated and vendored files in the fileSize worst-file signal ([#12291](https://github.com/diegosouzapw/OmniRoute/pull/12291)) +- **refactor(video):** unify the JPEG frame data-URI contract ([#12322](https://github.com/diegosouzapw/OmniRoute/pull/12322)) +- **chore(deps):** freeze onnxruntime-node and eslint-plugin-react-hooks out of dependabot groups ([#12329](https://github.com/diegosouzapw/OmniRoute/pull/12329)) +- **test(executors):** restore the #10986 reasoning-only fallback guards ([#12364](https://github.com/diegosouzapw/OmniRoute/pull/12364)) — thanks @pacocartones +- **chore(quality):** re-tighten the file-size ratchet to the real LOC (plan 3.8.52 task 0) ([#12411](https://github.com/diegosouzapw/OmniRoute/pull/12411)) +- **test(grok):** format web executor suite ([#12412](https://github.com/diegosouzapw/OmniRoute/pull/12412)) +- **chore(quality):** ratchet open-sse typecheck baseline to zero ([#12418](https://github.com/diegosouzapw/OmniRoute/pull/12418)) +- **test(build):** guard the artifact path policy arrays against duplicates ([#12422](https://github.com/diegosouzapw/OmniRoute/pull/12422)) +- **chore(lint):** adopt eslint-plugin-react-hooks 7.1.1 ([#12428](https://github.com/diegosouzapw/OmniRoute/pull/12428)) +- **chore(providers):** remove a keyless provider integration at its operator's request ([#12440](https://github.com/diegosouzapw/OmniRoute/pull/12440)) +- **docs(providers):** register providers removed at their operator's request and guard against reintroduction ([#12478](https://github.com/diegosouzapw/OmniRoute/pull/12478)) +- **chore(quality):** base fixes — stryker tap.testFiles + node_modules cache key ([#12482](https://github.com/diegosouzapw/OmniRoute/pull/12482)) +- **docs(arch):** one-process recipe for tens of long /v1/responses ([#12493](https://github.com/diegosouzapw/OmniRoute/pull/12493)) — thanks @RaviTharuma +- **chore(quality):** tighten the CodeQL ratchet baseline from 11 to 6 ([#12530](https://github.com/diegosouzapw/OmniRoute/pull/12530)) +- **chore(deps):** pin browserslist override to ^4.28.8 ([#12592](https://github.com/diegosouzapw/OmniRoute/pull/12592)) — thanks @KooshaPari +- **chore(deps):** bump fast-uri to 3.1.7 in the electron lockfile ([#12601](https://github.com/diegosouzapw/OmniRoute/pull/12601)) +- **docs(quality):** document how the CodeQL ratchet refreshes and how to tighten it ([#12611](https://github.com/diegosouzapw/OmniRoute/pull/12611)) +- **test(ci):** pin the openapi-security-tiers two-arm contract with an executing gate test (#12581) ([#12652](https://github.com/diegosouzapw/OmniRoute/pull/12652)) +- **docs(gamification):** align XP Rewards table with code (#12501) ([#12667](https://github.com/diegosouzapw/OmniRoute/pull/12667)) — thanks @KooshaPari +- **refactor(combo):** split executeTarget into gates, attempt, and loop ([#12746](https://github.com/diegosouzapw/OmniRoute/pull/12746)) — thanks @HouMinXi +- **docs(security):** document socket.yml scanner config + CI workflow link (#12575) ([#12764](https://github.com/diegosouzapw/OmniRoute/pull/12764)) — thanks @KooshaPari +- **chore(ci):** guard commit identity in pre-commit to stop author misattribution ([#12772](https://github.com/diegosouzapw/OmniRoute/pull/12772)) +- **deps:** 17 Dependabot bumps — bump electron from 43.4.0 to 43.4.1 in /electron ([#11426](https://github.com/diegosouzapw/OmniRoute/pull/11426)); bump the development group across 1 directory with 8 updates ([#11428](https://github.com/diegosouzapw/OmniRoute/pull/11428)); bump the production group across 1 directory with 11 updates ([#11440](https://github.com/diegosouzapw/OmniRoute/pull/11440)); chore(deps): bump github/codeql-action/init from 4.37.7 to 4.37.8 ([#11925](https://github.com/diegosouzapw/OmniRoute/pull/11925)); chore(deps): bump github/codeql-action from 4.37.7 to 4.37.8 ([#11926](https://github.com/diegosouzapw/OmniRoute/pull/11926)); chore(deps): bump github/codeql-action/analyze from 4.37.7 to 4.37.8 ([#11927](https://github.com/diegosouzapw/OmniRoute/pull/11927)); build(deps): bump github/codeql-action/init from 4.37.8 to 4.37.9 ([#12345](https://github.com/diegosouzapw/OmniRoute/pull/12345)); build(deps): bump github/codeql-action/analyze from 4.37.8 to 4.37.9 ([#12346](https://github.com/diegosouzapw/OmniRoute/pull/12346)); bump the development group across 1 directory with 2 updates ([#12347](https://github.com/diegosouzapw/OmniRoute/pull/12347)); build(deps): bump github/codeql-action from 4.37.8 to 4.37.9 ([#12349](https://github.com/diegosouzapw/OmniRoute/pull/12349)); bump browserslist from 4.28.2 to 4.28.8 ([#12396](https://github.com/diegosouzapw/OmniRoute/pull/12396)); bump the production group across 1 directory with 4 updates ([#12399](https://github.com/diegosouzapw/OmniRoute/pull/12399)); bump @xmldom/xmldom ([#12500](https://github.com/diegosouzapw/OmniRoute/pull/12500)); bump qs from 6.15.2 to 6.16.0 ([#12512](https://github.com/diegosouzapw/OmniRoute/pull/12512)); bump @xmldom/xmldom from 0.9.10 to 0.9.12 ([#12513](https://github.com/diegosouzapw/OmniRoute/pull/12513)); bump fast-uri from 3.1.5 to 3.1.7 ([#12514](https://github.com/diegosouzapw/OmniRoute/pull/12514)); bump @humanfs/node from 0.16.7 to 0.16.8 ([#12515](https://github.com/diegosouzapw/OmniRoute/pull/12515)) +- Clear the `release/v3.8.51` `check:agent-skills-sync` base-red: regenerate `skills/cli-tunnel/SKILL.md` so the `tunnel create [type]` positional that #13009 taught the generator to read is reflected in the committed skill. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` typecheck base-red from #12731: the new `Mode pack ranking gates` candidates in `open-sse/services/autoCombo/__tests__/autoCombo.test.ts` omitted the required `provider`, `model` and `errorRate` fields of `ProviderCandidate`, failing `check:open-sse-typecheck` (and with it `Fast Quality Gates`) on every open PR. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` docs-sync base-red: EURouter (#13025) and GreenPT (#13024) took the live provider count to 358, leaving 7 STRICT drifts (`PROVIDER_REFERENCE.md`, 4 diagrams, 2 tier-flow images) plus the `AGENTS.md` / `llm.txt` / `package.json` count claims stale. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- **test(models):** Pin `CATALOG_BUILD_TIMEOUT_MS` in the #12058 canonical-catalog regression so a cold catalog build racing the 8s default bound no longer flakes the suite with a 500 ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Allow `@omniroute/opencode-plugin-v2/` in the npm pack policy — [#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870) shipped the v2 plugin beside its v1 sibling without widening the allowlist, so every packed file under it read as an unexpected artifact and failed `check:pack-policy` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Point the release-green validator's pack gate at the tree it is validating — the build-provenance guard defaulted to `origin/main`, which a release branch tip can never be an ancestor of mid-cycle, so `Package artifact` reported a false red on every run and the tarball boot-smoke cascaded off it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **docs(checks):** keep doc counts honest — headings, rankings, catalog, weights, quality gate and scoring diagram now covered ([#12507](https://github.com/diegosouzapw/OmniRoute/pull/12507)) — thanks @maxmad64bis +- **chore(lifecycle):** `check:model-lifecycle` now also diffs `DEFAULT_DEGRADATION_MAP` (the background-task redirect table) against the vendor lifecycle snapshot, refusing a retired id as source or target, with a table-driven unit test beside it. Three rows whose source the vendor had retired — `claude-sonnet-4-20250514`, `gemini-3-pro-preview` and `gpt-5.1-codex` (whose target `gpt-5.1-codex-mini` is retired too) — were dead code, since `checkLifecycle` answers 410 before the redirect runs; they are dropped (#12535 — thanks @pacocartones) +- **test(compression):** cover idle worker eviction at the resource level — the pool must call `terminate()` and must not retain the worker's `MessagePort`, complementing the `exit`-event assertion added with the fix ([#13371](https://github.com/diegosouzapw/OmniRoute/pull/13371)) +- **chore(quality):** raise the `imageGeneration.ts` file-size ceiling for the image-only-model guard that clears the #12945 base-red ([#13392](https://github.com/diegosouzapw/OmniRoute/pull/13392)) +- **chore(quality):** type the combo-test route's JSON response bodies instead of casting them to `any`, drop the now-empty suppression entry, and remove the `hasPerModelQuota` import `chatCore.ts` stopped using when the failure-classification helper was extracted ([#13379](https://github.com/diegosouzapw/OmniRoute/pull/13379)) +- **chore(electron):** the desktop bundle no longer ships the root-level files of every translated docs mirror (`README.md`, `llm.txt`, `CLAUDE.md`, `GEMINI.md`, `CONTRIBUTING.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`) — the packaged app only reads `docs/i18n//docs/**`, which stays. Saves ~11 MB on top of the translated CHANGELOGs already pruned. (#0000) ([#13235](https://github.com/diegosouzapw/OmniRoute/pull/13235)) +- **chore(quality):** retighten the `src/sse/handlers/chat.ts` unused-vars suppression count to the 8 violations that actually remain ([#13385](https://github.com/diegosouzapw/OmniRoute/pull/13385)) +- Bring the documented counts back in line with the code: 171 migrations (was 169) and 20 routing strategies (was 19), plus the regenerated `cli-tunnel` skill reference. ([#12970](https://github.com/diegosouzapw/OmniRoute/pull/12970)) +- Update the documented migration count to 172 after the call-logs provider-stats indexes landed. ([#13209](https://github.com/diegosouzapw/OmniRoute/pull/13209)) +- **chore(ci):** two gates that close the blind spots behind the exclusions above. `check:vitest-exclusions` requires every Vitest exclusion to name a tracking issue and to appear in `config/quality/vitest-exclusions.json` — the previous list grew to 62 files behind a comment pointing at an issue that had been closed for a month. `check-new-key-coverage` requires a key newly added to `en.json` to reach every locale; the existing coverage gate is a percentage floor per locale, so eleven absent keys out of ~13,000 left it at 99.9% while a whole feature shipped untranslated in nine languages. Both are diff-aware, so pre-existing debt stays frozen and neither needed a migration to turn on. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **build:** root layout no longer loads Inter from `next/font/google`, so a production image build does not need fonts.googleapis.com ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **docs(i18n):** bring the eight new `llm.txt` mirrors (km, kn, ml, my, ne, or, pa, si) up to the current provider and migration counts, so `check-docs-sync` stops rejecting every commit ([#13674](https://github.com/diegosouzapw/OmniRoute/pull/13674)) +- **refactor(ui):** flow surfaces (home topology, combo live studio, compression cockpit/waterfall/nodes, token-health badge) express state through the theme-aware `--orch-status-*` tokens instead of fixed dark-mode hex, so green/red/amber/grey stay legible in the light theme; dark mode is byte-identical. Categorical palettes (routing-strategy hues, compression-layer pills, provider brand colors) deliberately stay hex ([#12378](https://github.com/diegosouzapw/OmniRoute/issues/12378)) ([#13203](https://github.com/diegosouzapw/OmniRoute/pull/13203)) +- **chore(tests):** 51 test files that had been excluded from Vitest are running again, restoring roughly 350 assertions to the blocking suite. Each was measured individually first: of the 62 files parked behind the `// #8618 — pre-existing failure` comment, 51 pass against the current tree with no source change, so the exclusions had outlived the failures they were added for. The 11 that genuinely still fail stay excluded, but now point at a live tracker (#13204) rather than at #8618, which was closed in August while the list it tracked kept growing. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **chore(quality):** drop two ESLint suppression entries whose violations no longer exist, so `eslint --suppressions-location` stops rejecting every commit that touches the surrounding files ([#13384](https://github.com/diegosouzapw/OmniRoute/pull/13384)) +- **chore(release):** bring main's post-tag fixes into release/v3.8.51 — sync-back step 2/2 (direct commit `21c488f210`) +- **chore(deps):** patch toml and esbuild in the opencode plugin lockfiles ([#12965](https://github.com/diegosouzapw/OmniRoute/pull/12965)) +- **docs(changelog):** reconcile the v3.8.51 living section — fold 366 fragments, cover every cycle commit, credit every contributor ([#12971](https://github.com/diegosouzapw/OmniRoute/pull/12971)) +- **test(plugin):** wait for the v2 lazy-refresh tier instead of sleeping past it ([#12984](https://github.com/diegosouzapw/OmniRoute/pull/12984)) +- **chore(quality):** rebaseline chatCore.ts after the non-streaming regression fixes ([#13045](https://github.com/diegosouzapw/OmniRoute/pull/13045)) +- **docs(changelog):** add the missing bullet marker to the DeepSeek PoW fragment ([#13200](https://github.com/diegosouzapw/OmniRoute/pull/13200)) +- **chore(deps):** drain the Dependabot queue — 10 of 13 alerts ([#13213](https://github.com/diegosouzapw/OmniRoute/pull/13213)) +- **chore(ci):** make worktrees carry the hooks that enforce the identity gate ([#13236](https://github.com/diegosouzapw/OmniRoute/pull/13236)) +- **chore(deps):** pin csv-parse to ^7.0.2 via overrides (#13117) ([#13300](https://github.com/diegosouzapw/OmniRoute/pull/13300)) — thanks @KooshaPari +- **chore(deps):** bump hono from ^4.12.34 to ^4.13.7 (#13148) ([#13301](https://github.com/diegosouzapw/OmniRoute/pull/13301)) — thanks @KooshaPari +- **chore(deps):** pin joi to ^18.2.8 via overrides (#13085) ([#13302](https://github.com/diegosouzapw/OmniRoute/pull/13302)) — thanks @KooshaPari +- **docs(providers):** add Microsoft 365 Copilot (BizChat) provider guide (#12779) ([#13340](https://github.com/diegosouzapw/OmniRoute/pull/13340)) — thanks @KooshaPari +### 🙌 Contributors + +Thanks to everyone whose work landed in v3.8.51: + +| Contributor | PRs / Issues | +| --- | --- | +| [@5dive-bot](https://github.com/5dive-bot) | #11852 | +| [@abhisheksharma2411](https://github.com/abhisheksharma2411) | #11547, #11684, #11959, #12209 | +| [@adevwithpurpose](https://github.com/adevwithpurpose) | #11464 | +| [@adivekar-utexas](https://github.com/adivekar-utexas) | #12015, #12043, #12090, #12138 | +| [@AIB1TAL0S](https://github.com/AIB1TAL0S) | #12103 | +| [@alltomatos](https://github.com/alltomatos) | #11448 | +| [@alvinveroy](https://github.com/alvinveroy) | #11857, #12027, #12165 | +| [@amaleta](https://github.com/amaleta) | #11906, #11934 | +| [@amartinawi](https://github.com/amartinawi) | #11575 | +| [@AndrianBalanescu](https://github.com/AndrianBalanescu) | #11083, #11830, #11834, #11844 | +| [@anhtahaylove](https://github.com/anhtahaylove) | #13091, #13092, #13093, #13096, #13097, #13100, #13106, #13109, #13114, #13155, #13166, #13171, #13175, #13187, #13196 | +| [@aniruddhaadak80](https://github.com/aniruddhaadak80) | #11760 | +| [@app](https://github.com/app) | #12554 | +| [@arminanton](https://github.com/arminanton) | #11461, #11513 | +| [@AStupidBear](https://github.com/AStupidBear) | #11584 | +| [@atescivitci-cmd](https://github.com/atescivitci-cmd) | #12105 | +| [@b3nw](https://github.com/b3nw) | #11970, #11971, #12147 | +| [@backryun](https://github.com/backryun) | #11259, #11950, #12075, #12076, #12078, #12079, #12081, #12082, #12181, #12228, #12239, #12255, #12258, #12277, #12367, #12423, #12524, #12538 | +| [@benjaminkitt](https://github.com/benjaminkitt) | #11747 | +| [@benzntech](https://github.com/benzntech) | #11614, #11615 | +| [@Bl0ck154](https://github.com/Bl0ck154) | #11948, #11951, #11952, #11953, #11954 | +| [@brick30llc-ctrl](https://github.com/brick30llc-ctrl) | #11752, #12242, #12262 | +| [@Chewji9875](https://github.com/Chewji9875) | #11455, #11875, #12028 | +| [@cheynetom](https://github.com/cheynetom) | #12058 | +| [@davidebaraldo](https://github.com/davidebaraldo) | #12222, #12304 | +| [@davidlinfr](https://github.com/davidlinfr) | #6390 | +| [@ddarkr](https://github.com/ddarkr) | #12124 | +| [@Deftera186](https://github.com/Deftera186) | #11809 | +| [@dpozimski](https://github.com/dpozimski) | #11945 | +| [@DW-MediaLab](https://github.com/DW-MediaLab) | #12229 | +| [@echel0nn](https://github.com/echel0nn) | #11923 | +| [@f9td56dbgh-hub](https://github.com/f9td56dbgh-hub) | #11660 | +| [@fabioluissilva](https://github.com/fabioluissilva) | #11991 | +| [@foreveryh](https://github.com/foreveryh) | #12177 | +| [@geek007git](https://github.com/geek007git) | #12115, #12116, #12117, #12120, #12122 | +| [@geekyNads](https://github.com/geekyNads) | #11784, #12266 | +| [@ggiak](https://github.com/ggiak) | #11561, #12276, #12350, #12402 | +| [@gonisulaimann](https://github.com/gonisulaimann) | #12368, #12369, #12371 | +| [@Gorillaz322](https://github.com/Gorillaz322) | #12207 | +| [@groovecityJO](https://github.com/groovecityJO) | #12682 | +| [@hartmark](https://github.com/hartmark) | #11434, #11452, #11473, #11499, #11703, #11983, #11984, #11985, #11986, #11988, #11989, #11990, #11994, #12221, #12293, #12445, #12446, #12447, #12448, #12460, #12461, #12623, #12646, #12650, #12680, #12717, #12718, #12727, #12741, #12854 | +| [@hizzt](https://github.com/hizzt) | #11894 | +| [@hongnoul](https://github.com/hongnoul) | #11484 | +| [@HouMinXi](https://github.com/HouMinXi) | #11411, #11414, #11512, #11518, #11520, #11641, #11642, #11643, #11687, #11779, #11849, #11850, #11851, #11915, #11916, #11918, #11919, #11920, #12013, #12017, #12033, #12042, #12106, #12139, #12166, #12169, #12171, #12205, #12213, #12312, #12325, #12487, #12488, #12495, #12504, #12557, #12566, #12590, #12591, #12624, #12626, #12632, #12637, #12678, #12696, #12697, #12711, #12733, #12746, #12767, #12770, #12789, #12803, #12805, #12811, #12866, #12868, #12899, #12926, #12934, #12950, #12951, #12974, #13001, #13006, #13011, #13017, #13026, #13027, #13034, #13035, #13038, #13042, #13050, #13060, #13061, #13069, #13107, #13120, #13136, #13195, #13197 | +| [@Hsia97](https://github.com/Hsia97) | #11624 | +| [@jacobsparts](https://github.com/jacobsparts) | #11854, #12155, #12167 | +| [@joglomedia](https://github.com/joglomedia) | #11980 | +| [@jonlwheat2-gif](https://github.com/jonlwheat2-gif) | #11443, #11450, #11549, #11567, #11608, #11633, #11635, #11640, #11644, #11671, #11672, #11673, #11674, #11675, #11676, #11677, #11706, #11783, #12051, #12052, #12053, #12055 | +| [@kanade-hoshino](https://github.com/kanade-hoshino) | #12180 | +| [@Karan825](https://github.com/Karan825) | #11861 | +| [@KaspaPulse](https://github.com/KaspaPulse) | #11389, #11469, #11628, #11666, #11910 | +| [@keeltrace](https://github.com/keeltrace) | #12080, #12223 | +| [@killer30001000](https://github.com/killer30001000) | #12178 | +| [@KooshaPari](https://github.com/KooshaPari) | #12470, #12592, #12667, #12699, #12703, #12706, #12764, #12769, #12771, #13300, #13301, #13302, #13340, #13401, #13403, #13404, #13405, #13406, #13407, #13409, #13410, #13411, #13412, #13413, #13414, #13418, #13424, #13427, #13433, #13523, #13524, #13525, #13528, #13531, #13532, #13534, #13539, #13543, #13545, #13546, #13547, #13550, #13553 | +| [@kriptoburak](https://github.com/kriptoburak) | #11370 | +| [@KrzysiekSko](https://github.com/KrzysiekSko) | #12425, #12432, #12673 | +| [@marcelokarval](https://github.com/marcelokarval) | direct commit / report | +| [@maxmad64bis](https://github.com/maxmad64bis) | #1622, #11435, #11437, #11441, #11537, #11550, #11553, #11555, #11557, #11812, #11842, #11843, #11903, #12151, #12214, #12215, #12218, #12226, #12314, #12316, #12317, #12318, #12319, #12320, #12321, #12507, #12715, #12731, #12744, #12786, #12787, #12788, #12790, #12792, #12794, #12795, #12828, #12832, #12853, #12857, #12870, #12937, #12941, #12975, #13141, #13142, #13146, #13147 | +| [@mdigitalbh81](https://github.com/mdigitalbh81) | #12240 | +| [@Meet6338-X](https://github.com/Meet6338-X) | #11598, #11609, #12110 | +| [@morpheus9393](https://github.com/morpheus9393) | #11943 | +| [@MumuTW](https://github.com/MumuTW) | #11492, #11502, #11506, #11507, #11626, #11675, #11685, #11728, #11746, #11888, #11889, #11890, #11892 | +| [@Neuron-Mr-White](https://github.com/Neuron-Mr-White) | #11622, #11801, #12256 | +| [@NightStalker-87](https://github.com/NightStalker-87) | #12183 | +| [@NoxzRCW](https://github.com/NoxzRCW) | #11879, #11880, #11881, #11882, #11883, #11935 | +| [@ntdat812](https://github.com/ntdat812) | #11585, #12095 | +| [@ntdatt812](https://github.com/ntdatt812) | #11368, #11573, #11574, #11576, #11577, #11580, #11582, #11583, #11588, #11589, #11590, #11591, #11592, #11593, #11672, #12177, #12180, #12873, #12918, #12920, #12921, #12925, #12930, #13007, #13009, #13024, #13025, #13083, #13087, #13101, #13104, #13110 | +| [@official-burak](https://github.com/official-burak) | #11542 | +| [@opensource-elearning](https://github.com/opensource-elearning) | #12179, #12189, #12278, #12286 | +| [@oyi77](https://github.com/oyi77) | #11408, #11409, #11421, #11505, #11677, #12036, #12110 | +| [@pacocartones](https://github.com/pacocartones) | #11521, #11522, #11527, #11528, #11529, #11530, #11531, #11532, #11533, #11534, #11595, #11599, #11603, #11604, #11605, #11607, #11610, #11676, #11714, #11716, #11718, #11767, #11838, #11860, #11862, #11869, #11871, #11872, #11873, #11903, #11906, #11921, #11934, #12356, #12358, #12359, #12360, #12361, #12362, #12364, #12365, #12373, #12374, #12375, #12376, #12377, #12379, #12380, #12381, #12386, #12387, #12389, #12390, #12394, #12395, #12397, #12401, #12403, #12404, #12522, #12523, #12535, #12536, #12540, #12541, #12543, #12545, #12548, #12549, #12550, #12551, #12552, #12647, #12651, #12653, #13055 | +| [@patrykkopycinski](https://github.com/patrykkopycinski) | #11936, #11937, #12224 | +| [@PauloFH](https://github.com/PauloFH) | #11509 | +| [@PauloHSOliveira](https://github.com/PauloHSOliveira) | #12241 | +| [@ponkcore](https://github.com/ponkcore) | #12054 | +| [@Prajeeth-12](https://github.com/Prajeeth-12) | #11634, #12046 | +| [@quiterunner-commits](https://github.com/quiterunner-commits) | #12131, #12143 | +| [@rafacpti23](https://github.com/rafacpti23) | #11554, #11558, #12192, #12197, #12198, #12202, #12203, #12204 | +| [@ragnar-claude](https://github.com/ragnar-claude) | #11564 | +| [@raheemuddin786](https://github.com/raheemuddin786) | #11491, #11839, #11840, #11841, #12003, #12230, #12231, #12232, #12233, #12234 | +| [@rahilmavani](https://github.com/rahilmavani) | #11761 | +| [@Rahulsharma0810](https://github.com/Rahulsharma0810) | #11771 | +| [@RaviTharuma](https://github.com/RaviTharuma) | #11710, #11727, #11797, #11798, #11802, #11805, #11806, #11811, #12098, #12099, #12101, #12449, #12452, #12472, #12473, #12493, #12533, #12607, #12628, #12631, #12636, #12875, #12876, #12880, #12882, #12884 | +| [@rezjalibd](https://github.com/rezjalibd) | #12186 | +| [@RhianB14](https://github.com/RhianB14) | #11692 | +| [@rifqiawl](https://github.com/rifqiawl) | #11517, #11519, #11969 | +| [@rqzbeh](https://github.com/rqzbeh) | #11390 | +| [@Sabeekhann](https://github.com/Sabeekhann) | #11699, #11705 | +| [@santosraju99-hub](https://github.com/santosraju99-hub) | #11755, #11770 | +| [@Seramicx](https://github.com/Seramicx) | #11568 | +| [@Siva010](https://github.com/Siva010) | #12191 | +| [@solstxce](https://github.com/solstxce) | #11597 | +| [@soroush5](https://github.com/soroush5) | #12691 | +| [@tenshiak](https://github.com/tenshiak) | #12279 | +| [@TheDemonTuan](https://github.com/TheDemonTuan) | #11468, #11470, #11471, #11482, #11548, #11758, #11775, #11814 | +| [@thomasmaerz](https://github.com/thomasmaerz) | #12834 | +| [@tuandinh0801](https://github.com/tuandinh0801) | #11454 | +| [@turbolego](https://github.com/turbolego) | #11621, #11762, #11772, #11774, #11781, #12216 | +| [@Tushar49](https://github.com/Tushar49) | #11392 | +| [@ujjawalkaushik1110](https://github.com/ujjawalkaushik1110) | #11863 | +| [@vermasomesh835](https://github.com/vermasomesh835) | #11794 | +| [@vsd2807](https://github.com/vsd2807) | #11565, #11619 | +| [@wahidsadik371-coder](https://github.com/wahidsadik371-coder) | #12126 | +| [@watchingdogs](https://github.com/watchingdogs) | #12031 | +| [@wildcard](https://github.com/wildcard) | #11620 | +| [@xiaoyaner0201](https://github.com/xiaoyaner0201) | #11460, #11662, #12083 | +| [@Xxx91n](https://github.com/Xxx91n) | #11690 | +| [@yourspraveen](https://github.com/yourspraveen) | #11146 | +| [@yxyxy](https://github.com/yxyxy) | #11671 | +| [@ZaimMarzuki](https://github.com/ZaimMarzuki) | #11960 | +| [@Zartharas](https://github.com/Zartharas) | #11340 | +| [@zero-executioner](https://github.com/zero-executioner) | #11631 | +| [@diegosouzapw](https://github.com/diegosouzapw) | maintainer | + +--- ## [3.8.50] — 2026-08-25 _Living section — regenerated 2026-08-12 from all cycle commits (cycle open `ed2db6cb19` → tip). Bullets carry the merged PR and its author; direct pushes listed separately._ diff --git a/docs/i18n/az/CHANGELOG.md b/docs/i18n/az/CHANGELOG.md index d5460f3fd2..3ca3e21293 100644 --- a/docs/i18n/az/CHANGELOG.md +++ b/docs/i18n/az/CHANGELOG.md @@ -8,16 +8,1119 @@ ## [3.8.51] — TBD -_Living section — cycle opened at the v3.8.50 freeze (parallel-cycle model). Bullets are aggregated from `changelog.d/` fragments at each `/generate-release` phase._ +_Living section — reconciled 2026-09-15 from all cycle commits (`091589089c` → `c0f92ec98a`, 916 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 | **123** | +| 📝 Commits in the cycle | **916** | +| 🔀 Pull requests referenced | **892** | +| 📋 Changelog entries | **919** | +| 🙌 Contributors credited in entries | **121** | +| 🤖 Automated dependency commits | 18 | + +**Entries by type** + +| Type | Count | +| --- | ---: | +| 🐛 Fixes | 592 | +| ✨ Features | 131 | +| 🧹 Chore | 93 | +| 📚 Docs | 42 | +| 🧪 Tests | 32 | +| ♻️ Refactor | 8 | +| 🏗️ Build | 7 | +| ⚡ Performance | 7 | +| ⚙️ CI | 2 | +| ⏪ Reverts | 2 | +| 📦 Dependencies | 2 | +| 🔒 Security | 1 | + +### 🏆 Top 25 contributors this cycle + +_By commits in `091589089c..c0f92ec98a`, author identities consolidated via `.mailmap` and the merged PR's GitHub login. Bots excluded._ + +| # | Contributor | Commits | +| ---: | --- | ---: | +| 🥇 | diegosouzapw | 323 | +| 🥈 | Bob.Hou (@HouMinXi) | 79 | +| 🥉 | Paco Cartones (@pacocartones) | 77 | +| 4 | Dizzle (@maxmad64bis) | 49 | +| 5 | Koosha Paridehpour (@KooshaPari) | 43 | +| 6 | Nguyen Thanh Dat (@ntdatt812) | 31 | +| 7 | Ravi Tharuma (@RaviTharuma) | 26 | +| 8 | Markus Hartung (@hartmark) | 22 | +| 9 | Webman (@jonlwheat2-gif) | 22 | +| 10 | backryun | 17 | +| 11 | anhtahaylove | 15 | +| 12 | Rafa Martins (@rafacpti23) | 10 | +| 13 | Syed Raheemuddin (@raheemuddin786) | 10 | +| 14 | Nguyễn Viết Tuấn (@TheDemonTuan) | 8 | +| 15 | MumuTW | 7 | +| 16 | Paijo (@oyi77) | 7 | +| 17 | Tobias Andersen (@turbolego) | 6 | +| 18 | Bl0ck (@Bl0ck154) | 5 | +| 19 | SHANMUGAPRIYAN (@geek007git) | 5 | +| 20 | KaspaPulse | 5 | +| 21 | Abhishek Sharma (@abhisheksharma2411) | 4 | +| 22 | Abhishek Divekar (@adivekar-utexas) | 4 | +| 23 | Andrew B. (@AndrianBalanescu) | 4 | +| 24 | NoxzRCW | 4 | +| 25 | opensource-elearning | 4 | ### ✨ New Features +- **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). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- Added Google AI Studio Gemini batch text-to-speech support through `POST /v1/audio/speech`. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- 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. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(routing):** subscription-first auto groupings — `auto/subscription` routes only through plan-included connections with a documented hard-stop overage and fails closed on exhaustion, while `auto/thrifty` orders the pool `subscription → keyless → free → cheap → premium` and steps up one rung at a time as each is exhausted. Billing class comes from a curated per-connection catalog (uncurated is treated as metered, never plan-included), both reuse STRICT_ZERO_COST's per-connection verification, and a quota reading whose `resetAt` has passed is now refreshed regardless of TTL so routing returns to plan capacity as soon as it resets ([#11146](https://github.com/diegosouzapw/OmniRoute/pull/11146)) — thanks @yourspraveen +- **feat(providers):** publish a management-authenticated versioned web-session credential contract from OmniRoute's canonical browser credential metadata ([#11340](https://github.com/diegosouzapw/OmniRoute/pull/11340)) — thanks @Zartharas +- **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)) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add Xquik X search with typed results, credential validation, REST routing, and MCP selection ([#11370](https://github.com/diegosouzapw/OmniRoute/pull/11370)) — thanks @kriptoburak +- **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)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(dashboard):** surface durable exclusive managed leases in the existing Sessions view, keeping leased clients visible across idle gaps while marking connections with in-flight work as active ([#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389)) — thanks @KaspaPulse +- **build(bun):** allow Turbopack bundler flag on Bun 1.4+ with configurable Webpack fallback ([#11471](https://github.com/diegosouzapw/OmniRoute/pull/11471)) — thanks @TheDemonTuan +- feat(api): add an opt-in `modelVisibilityAllowlist`/`modelVisibilityDenylist` settings pair to curate exactly which models `/v1/models` advertises, mirrored into every `auto/*` combo candidate pool so a denied model cannot be routed to via combo selection either (#11481) ([#11997](https://github.com/diegosouzapw/OmniRoute/pull/11997)) +- **feat(rankings):** the Free Provider Rankings page shows what each provider actually served over the last 24 h. It ranked by ELO alone, which left a provider that answers every call with an error in first place; the usage data was already served by the API but never requested. A provider with too small a sample shows a dash, not a number ([#11546](https://github.com/diegosouzapw/OmniRoute/pull/11546)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **feat(guardrails):** enforce a bounded, deterministic contract for Video Bridge transcripts — 256 cues, 4096 input code units and 4 KiB UTF-8 per cue, 64 KiB total text, malformed-Unicode rejection, focus-window scoping, cross-source reconciliation with contributing-source metadata, and a structural provenance trust boundary so caller JSON can never self-assert `embedded`/`audio-bridge` provenance ([#11652](https://github.com/diegosouzapw/OmniRoute/issues/11652)) ([#12009](https://github.com/diegosouzapw/OmniRoute/pull/12009)) +- **feat(video):** orchestrate optional Video Bridge audio extraction and Audio Bridge STT behind a dual opt-in (operator setting AND per-request signal) — a new loopback-only broker `mode=audio` operation shares the frame path's exact process queue, deadline, AbortSignal, and byte budgets to extract a bounded mono 16 kHz PCM WAV from the same already-downloaded video, then reuses the existing Audio Bridge transcription boundary; provider segment timing is preserved when available and marked coarse otherwise, and every failure degrades to a visual-only-safe partial instead of throwing (#11654). ([#12012](https://github.com/diegosouzapw/OmniRoute/pull/12012)) +- Add a tenant-bound Video Bridge drill-down lifecycle on top of the existing secure cache substrate: opaque hashed handles (never raw session/video identifiers), preview/standard/detail multiresolution variants resampled on read, response pagination capped at 8 frames and 32 MiB, and a new authenticated `/api/v1/video-bridge/drilldown` consumer route that stays disabled for remote access by default and denies cross-key access with the same response as a nonexistent handle (no existence oracle). ([#12006](https://github.com/diegosouzapw/OmniRoute/pull/12006)) +- **test(video):** Add the Video Bridge FU-07/FU-09 promotion-evidence harness (#11656) — a frozen Zod manifest schema covering the 8 required scenario kinds (static scenes, rapid cuts, late facts, fades, blur, small text, close events, visual prompt injection) with a minimum of 3 repetitions per case, deterministic declarative fixture recipes (`videoBridgePromotionFixtures.ts`), a pure medians/p95 metrics aggregator, a pure FU-07/FU-09 promotion-verdict evaluator applying the ticket's exact thresholds (missing token usage always holds), a digest-only persistence layer that never retains raw media or raw model responses, and a versioned per-model promotion allowlist shipped empty with every model defaulting to `hold`. The FU-07/FU-09 promotion verdicts themselves remain HOLD — they require a real evidence run against real models on VPS 192.168.0.15. ([#12008](https://github.com/diegosouzapw/OmniRoute/pull/12008)) +- **feat(video bridge):** "embedded" transcript provenance can now be legitimately earned instead of merely asserted — a bounded, allowlisted (`mov_text`/`subrip`/`webvtt`) subtitle probe runs through the loopback-only Video Bridge broker (at most 2 streams, 10s subdeadline bounded by the request deadline, 256 KiB output, 4096-code-unit lines), normalized through a bounded, ReDoS-safe WebVTT parser and Zod-validated end to end. The adapter always resolves to an explicit `success`/`absent`/`transient_failure` outcome — a subtitle failure never breaks the visual description path, and only a fingerprint-verified broker response (never a caller-declared label) can produce embedded cues (#11659). ([#12011](https://github.com/diegosouzapw/OmniRoute/pull/12011)) +- Default new Antigravity-family connections (agy CLI imports and Antigravity OAuth connects) to model auto-sync, so live model discovery lands in the synced catalog and `/v1/models` picks up freshly released upstream models (e.g. Gemini 3.7 Flash tiers) without code changes. Existing connections keep their current setting; the per-connection dashboard toggle remains the opt-out. (#11685 — thanks @MumuTW) ([#11887](https://github.com/diegosouzapw/OmniRoute/pull/11887)) +- **feat(ui):** enable React Compiler (`reactCompiler: true` + `babel-plugin-react-compiler`) for automatic memoization at build time ([#11783](https://github.com/diegosouzapw/OmniRoute/pull/11783)) — thanks @jonlwheat2-gif +- **feat(providers):** add SeekAi (`seekai.cc`) as an OpenAI-compatible New-API gateway — catalog id `seekai` (alias `ska`), `https://seekai.cc/v1`, live `/v1/models` via `passthroughModels`, aggregator-list membership so New-API balance detection can opt in. No referral/aff codes. ([#11786](https://github.com/diegosouzapw/OmniRoute/issues/11786)) ([#12557](https://github.com/diegosouzapw/OmniRoute/pull/12557)) — thanks @HouMinXi +- **feat(zai):** add GLM-5.3-Flash Coding Plan support (1M context, 128K output, vision, `low|high|max` reasoning) and route `zai` GLM-5.3-family API-key traffic through the OpenAI-compatible Coding Plan endpoint with native thinking defaults ([#11801](https://github.com/diegosouzapw/OmniRoute/pull/11801)) — thanks @Neuron-Mr-White +- **feat(combo):** choose how combo models are ordered — manual, provider, score, or name — via a sort control in the dashboard builder, persisted in `config.modelSort` and re-applied on load and after add ([#11812](https://github.com/diegosouzapw/OmniRoute/pull/11812)) — thanks @maxmad64bis +- **feat(free):** custom models can be marked free-tier via `customModels[].isFree`; `isFreeModel()` is the first door and `hidePaidModels` respects it even for providers outside the free budget ([#11843](https://github.com/diegosouzapw/OmniRoute/pull/11843)) — thanks @maxmad64bis +- **feat(nodejs):** add `5dive` as a `configure` target — `omniroute configure 5dive` / `omniroute setup-5dive` write a 5dive auth profile that points an agent fleet's `claude` seats at OmniRoute, with the root-only write, the loopback-vs-`https` endpoint rule and the per-seat model pin handled explicitly ([#11852](https://github.com/diegosouzapw/OmniRoute/pull/11852)) — thanks @5dive-bot +- **feat(sse):** treat `max` as a first-class reasoning-effort tier and clamp per model family (GLM 5.1+/DeepSeek V4+/Kimi K3+ keep native `max`; o1/MiniMax/Grok/Muse Spark clamp to their upstream ceiling) ([#11875](https://github.com/diegosouzapw/OmniRoute/pull/11875)) — thanks @Chewji9875 +- **feat(providers):** the provider plugin manifest now advertises a `usage-fetch` capability for the 40 providers that have a wired usage/quota fetcher, so external dashboards can read it from `GET /api/v1/provider-plugin-manifest` instead of parsing `open-sse/services/usage.ts` after every release. Discovery only — no new fetcher, no quota change, and the Dashboard quota widget stays gated by `USAGE_SUPPORTED_PROVIDERS`. `USAGE_FETCHER_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/fetcherProviders.ts`) and is re-exported from `services/usage.ts`, keeping the manifest module a light leaf instead of pulling the ~490-module usage dispatcher into the manifest route. ([#11903](https://github.com/diegosouzapw/OmniRoute/pull/11903)) — thanks @maxmad64bis / @pacocartones +- **feat(plugins):** `OMNIROUTE_PLUGINS_DIR` sets the directory the runtime plugin scanner reads — and the root the plugin manager installs into — overriding the `HOME`-derived default, so a Docker/K8s deployment can point straight at its bind-mounted plugin tree instead of moving `HOME` just to relocate the scan path. An image that exports no home no longer scans `/tmp/.omniroute/plugins` in silence: the resolved directory is logged once at startup as `scanner.dir_resolved`, naming the input that won. Unset, behaviour is unchanged. Distinct from the CLI-only `OMNIROUTE_PLUGIN_PATH`, which finds `omniroute-cmd-*` command packages and never reached this scanner ([#11906](https://github.com/diegosouzapw/OmniRoute/pull/11906)) — thanks @amaleta / @pacocartones +- **feat(leases):** add an explicit owner-authenticated status action that returns only the active lease's privacy-safe configured connection and provider labels, with generation fencing and no credential or internal-id disclosure ([#11910](https://github.com/diegosouzapw/OmniRoute/pull/11910)) — thanks @KaspaPulse +- **feat(providers):** import-from-file modal shows per-row API errors and ships a downloadable CSV template ([#12071](https://github.com/diegosouzapw/OmniRoute/issues/12071)) ([#12504](https://github.com/diegosouzapw/OmniRoute/pull/12504)) — thanks @HouMinXi +- **feat(providers):** dashboard search matches connection name and `baseUrl` so imported OpenAI-compat nodes surface on the provider card ([#12108](https://github.com/diegosouzapw/OmniRoute/issues/12108)) ([#12495](https://github.com/diegosouzapw/OmniRoute/pull/12495)) — thanks @HouMinXi +- **feat(routing):** add a `score` Auto router strategy that selects the highest configured weighted score and reuses `explorationRate`. ([#12155](https://github.com/diegosouzapw/OmniRoute/pull/12155)) — thanks @jacobsparts +- Add a runtime feature flag to disable universal context handoffs globally without changing the default behavior. ([#12167](https://github.com/diegosouzapw/OmniRoute/pull/12167)) — thanks @jacobsparts +- **feat(providers):** the provider plugin manifest now also advertises a `usage-supported` capability for the 46 providers whose usage API is accepted by the server and Dashboard routes, so integrators can distinguish "the server will serve quota for this provider" from "a fetcher is wired" without reading TypeScript. Discovery only — no fetcher or quota change. `usage-fetch` resolves on id or alias (the usage dispatcher accepts both); `usage-supported` resolves on id alone, matching the runtime guard `USAGE_SUPPORTED_PROVIDERS.includes(providerId)`. `USAGE_SUPPORTED_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/supportedProviders.ts`) and is re-exported from `providers.ts`, mirroring the `fetcherProviders` leaf from #11903 and keeping the manifest a light module. ([#12214](https://github.com/diegosouzapw/OmniRoute/pull/12214)) — thanks @maxmad64bis +- **feat(rankings):** order Free Provider Rankings by what each provider actually served — `GET /api/free-provider-rankings?sortBy=reliability` and a "Most reliable first" toggle on the page. Providers with too few calls to state a success rate keep their score order below the measured ones; the default order is unchanged ([#12218](https://github.com/diegosouzapw/OmniRoute/pull/12218)). — thanks @maxmad64bis +- **perf(sse):** defer `cloneLogPayload()` in the structured SSE collector until after the `maxEvents`/`maxBytes` cap check, eliminating ~9,800 wasted `structuredClone` calls per streaming response (65–71% faster `push()`). Reducer snapshot isolation restored for OpenAI and Responses summaries ([#12241](https://github.com/diegosouzapw/OmniRoute/pull/12241)) — thanks @PauloHSOliveira ([#12243](https://github.com/diegosouzapw/OmniRoute/pull/12243)) +- **feat(usage):** Devin CLI agentic quota (Codeium seat-management GetUserStatus) and OpenRouter key limits plus account credits now surface in Provider Limits ([#12256](https://github.com/diegosouzapw/OmniRoute/pull/12256) — thanks @Neuron-Mr-White) +- **feat(settings):** persist `headroomUrl` through Settings so status/start use the operator URL instead of only `HEADROOM_URL` ([#12306](https://github.com/diegosouzapw/OmniRoute/issues/12306)) ([#12487](https://github.com/diegosouzapw/OmniRoute/pull/12487)) — thanks @HouMinXi +- **feat(auto-combo):** Auto-Combo scoring can now weigh how often a provider/model has actually succeeded. The engine already carried that number on every candidate — 24 hours of usage history behind a ten-sample floor, real-time metrics otherwise — and the scoring function never read it, while the weight table described `stability` as if it did. `reliability` (`1 - failureRate`, with the same field precedence and the same rate-bounding the speed ranking already uses, so a corrupt reading means "nothing observed" rather than "fails every call") is now a declared factor shipping at weight `0`, so routing is unchanged until an operator gives it one, and the `stability` description now matches what that factor computes ([#12317](https://github.com/diegosouzapw/OmniRoute/pull/12317)) — thanks @maxmad64bis +- **feat(routing):** With `freeAccessPolicy: "strict"`, the read-only candidate listing (`GET /v1/auto-combo/{channel}/candidates`) no longer hides the candidates the zero-cost guard excludes — the same read-only transparency the resilience filter already honours (#9133). Each candidate now carries `freeAccessExclusion` saying why it would be kept out, and it tells an exhausted allowance apart from a quota reading that never arrived or went stale, which used to look identical from the outside. Routing is unchanged: the listing reports, it never enforces. The separate `excludeTosAvoid` guard still drops its candidates without a reason; that gap is now documented rather than closed ([#12319](https://github.com/diegosouzapw/OmniRoute/pull/12319)) — thanks @maxmad64bis +- **feat(radar):** The Radar catalog table now shows two facts it was already receiving from the feed and dropping on the floor: the per-model rate limits (requests and tokens, per minute and per day) in a new column, and a badge when a provider's terms state it may train on the prompts you send. A limit of zero renders as zero rather than "rate-only" — for a ceiling those are opposite facts — and a model with no training statement gets no badge, because an absent statement is not a guarantee ([#12320](https://github.com/diegosouzapw/OmniRoute/pull/12320)) — thanks @maxmad64bis +- **feat(radar):** explain Community, single-use, contributor, supporter, recovery, abuse, offers, and privacy rules before either Radar activation action, and remove the superseded fixed-PR grant promise from every UI locale ([#12342](https://github.com/diegosouzapw/OmniRoute/pull/12342)) +- **feat(gamification):** the dashboard Profile page now shows the real daily streak — `/api/gamification/level` returns `streak: { current, longest }` (per key with `apiKeyId`, operator-wide maximum otherwise) and the streak card reads it instead of a hard-coded 0 (#2403) ([#12377](https://github.com/diegosouzapw/OmniRoute/pull/12377)) — thanks @pacocartones +- **feat(gamification):** the dashboard leaderboard now shows each API key's display name under the Name column instead of a truncated key id; `GET /api/gamification/leaderboard` attaches `name` per entry (name only — no key material), while the shared ranking helper and the federation leaderboard stay id-only — thanks @pacocartones ([#12385](https://github.com/diegosouzapw/OmniRoute/pull/12385)) +- **feat(gamification):** enforce the documented 1000 XP/min per-API-key anti-cheat rate limit on the XP award path; over-limit awards are logged and skipped instead of persisted, and the sliding window now matches the timestamp format stored in `xp_audit_log` ([#2403](https://github.com/diegosouzapw/OmniRoute/issues/2403)) ([#12390](https://github.com/diegosouzapw/OmniRoute/pull/12390)) — thanks @pacocartones +- **feat(admin):** localize the gamification anomalies page — the loading state, the Status column and the Suspicious badge now come from the `common` catalog (new `common.suspicious` key propagated to every locale) — add it to the Gamification sidebar group as `gamification-admin` (`/dashboard/gamification/admin`), and expose the loading and empty states as polite `role="status"` live regions (#12401 — thanks @pacocartones) +- **feat(providers):** skip GitHub combo members missing from the live synced catalog, and drop Copilot models that are policy-disabled or hidden from the model picker ([#12473](https://github.com/diegosouzapw/OmniRoute/pull/12473)) — thanks @RaviTharuma +- **feat(i18n):** locale-expansion tooling — `npm run i18n:add-locale` adds a language to config, dashboard, docs mirrors, CLI, README/indexes and site in one command; browser, CLI and cookie detection resolve `uk`, `fil`/`tl`, `zh-Hant` (and the retired legacy `in` → `id`) via config aliases; `config/i18n.json` now ships in the npm package so the published CLI can read it; new real-translation ratio gate (`npm run i18n:check-ratio`, advisory) with a per-locale ratchet baseline; `run-translation --adopt` restores `.i18n-state.json`; `sync-language-bars` generates 🌐 bars from config; `validate_translation.py` loads its allowlist again. Retires the duplicate `in` locale (Indonesian mislabelled as Hindi) — 42 honest locales; saved `NEXT_LOCALE=in` / `OMNIROUTE_LANG=in` keep working. (#12496) +- **feat(api):** Emit gateway-measured `tokens_per_second` (TTFT excluded) on streaming usage and `X-OmniRoute-Tokens-Per-Second` when first-token latency is known ([#12616](https://github.com/diegosouzapw/OmniRoute/issues/12616)) ([#12631](https://github.com/diegosouzapw/OmniRoute/pull/12631)) — thanks @RaviTharuma +- **feat(opencode-plugin): map gateway cost/usage/tok/s onto OpenCode inference payloads** — the official plugin copies `X-OmniRoute-Response-Cost`, token counts, `X-OmniRoute-Tokens-Per-Second` / `usage.tokens_per_second`, TTFT, and the winning `X-OmniRoute-Model` onto the JSON/SSE body OpenCode already consumes. Missing tok/s is left unset (never `tokens / latency`). (#12636) — thanks @RaviTharuma +- **feat(combo):** add combo strategy `quota-weighted`: skip exhausted accounts, then weighted-draw by leftover / in-flight load; existing conversations stay pinned until the account is empty ([#12789](https://github.com/diegosouzapw/OmniRoute/pull/12789)) — thanks @HouMinXi +- **feat(models):** Account live listings become the chat catalog source for Claude, Codex, Copilot, and AGY; public metadata only fills prices on IDs those accounts already list. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **fix(models):** Union `agy` and `antigravity` live catalogs so `agy/gemini-3.8-flash-high` is not rejected after the prefix folds to `antigravity`. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **feat(opencode):** Add `@omniroute/opencode-plugin-v2`, an opencode v2 plugin that gives a v2 install a route to an OmniRoute gateway: it publishes models, combos and auto-combos into the host catalog, refreshes them lazily behind a 300s TTL, and keeps serving the last known catalog from an on-disk snapshot when the gateway is unreachable. The v1 plugin (`@omniroute/opencode-plugin`) is untouched and keeps working as before. The plugin adapts to the host instead of assuming one shape: it reads the shape the host seeds into the catalog draft and publishes the `api` block, the pre-`api` top-level fields, or both. The gateway key comes from the host credential store when one is connected, so no secret needs to sit in a config file; a source the gateway refuses is named in a warning instead of silently degrading the catalog to raw model ids; and tool schemas bound for a Gemini model are stripped of the JSON-Schema keywords Gemini rejects, so tool calling survives that route ([#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870)) — thanks @maxmad64bis +- feat(services): show sanitized CLIProxyAPI account health from its authenticated management API without exposing credentials, file paths, or raw account metadata (#6342) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add AnySearch free web search + URL extract (webFetch) with typed results, credential validation, REST routing, and MCP selection - fallback-only ([#11690](https://github.com/diegosouzapw/OmniRoute/pull/11690)) — thanks @Xxx91n +- **feat(dashboard):** display clamped `[0, 100]%` cached input token ratio in request logs table ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) — thanks @b3nw +- **feat(catalog):** add `OMNIROUTE_DISABLE_THINKING_LEVEL_VARIANTS` feature flag to optionally filter out thinking level variants from model catalog ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) — thanks @b3nw +- **feat(dashboard):** continuously export call logs to external analytics stores. A pluggable destination registry ships the full Logs-tab record set on an hourly `JobRegistry` cron, with a persisted per-destination cursor, batched inserts, a config UI rendered from each destination's own field descriptors, and a REST layer (`/api/log-export/*`) for CRUD, a connection test, and an on-demand run. A destination can opt into `includeBodies` to also ship the request and response payloads shown in the Logs detail pane, including the client and provider views of each call; this is off by default, and payloads inherit the dashboard's PII sanitisation, secret redaction and `noLog` handling. Google BigQuery is the first destination, using a service-account key stored encrypted at rest and streaming inserts keyed by call-log id, into a table that is day-partitioned on `timestamp` and clustered on `api_key_name`, `provider`, `model` and `status`. ([#11945](https://github.com/diegosouzapw/OmniRoute/pull/11945)) — thanks @dpozimski +- **feat(providers):** add MaxAI as a signed, OpenAI-compatible provider serving its 13 paid chat models (GPT-5.6 / Luna / Thinking, Claude 5 Sonnet, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite, Grok 4.1-fast / 4.5, DeepSeek V3.2 / R1, Llama 3.3 70B) through OmniRoute's `/v1` endpoint, with per-request HMAC-SHA1→SM3→AES request signing, live model + context-window discovery from `/models/get_config`, and prompted tool-calling translated to OpenAI `tool_calls` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI vision input — image_url content parts are forwarded inline in `message_content` to the 6 vision-capable models (GPT-5.6 / Luna / Thinking, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite) ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI image generation — 6 image models (gpt-image-1, dall-e-3, flux-1-schnell/dev/pro, sd3-medium) exposed through `POST /v1/images/generations` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI document RAG — inline base64 file/document attachments are uploaded to MaxAI (content-addressed `doc_id`) and attached to the chat via `doc_list` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** browserless MaxAI onboarding — email device-pair login (`/api/providers/[id]/login`) and signed access-token refresh, so a connection can be created and kept fresh without a real browser or Google OAuth ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** per-provider TLS impersonation profile (MaxAI presents a Windows Firefox-150 client fingerprint) so its bot-sensitive endpoints accept OmniRoute traffic ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** add **Nimble** as a web-search and web-fetch provider (`nimble-search`) — `/v1/search` routes to Nimble's search API at `lite` depth (locale, freshness and include/exclude domain filters mapped onto the shared request contract), and `/v1/web/fetch` routes to Nimble Extract, which covers all four fetch formats (`markdown`, `html`, `links`, `screenshot`) from a single call. One API key serves both surfaces. ([#11620](https://github.com/diegosouzapw/OmniRoute/pull/11620)) — thanks @wildcard +- The npm publish is automatic again, through npm Trusted Publishing (OIDC): the hosted `stage-npm` job publishes with a short-lived credential minted from GitHub's id-token — no `NPM_TOKEN`, no 2FA prompt, provenance attached. `publish_mode=staged` (owner approves with 2FA) and `direct` (token) remain available on `workflow_dispatch`. ([#11931](https://github.com/diegosouzapw/OmniRoute/pull/11931)) +- **feat(providers):** Add **Opper** as an API-key gateway provider — EU-hosted AI gateway with 700+ models from 30+ providers behind one OpenAI-compatible API and one key (`OPPER_API_KEY`); model ids use `provider/model` format (e.g. `anthropic/claude-sonnet-4-6`, `openai/gpt-5`); live model catalog at `https://api.opper.ai/v3/compat/models`; entry mirrors `requesty` (same shape, `passthroughModels: true`, no static seed) ([#11629](https://github.com/diegosouzapw/OmniRoute/pull/11629)) +- **feat(dashboard):** the `/dashboard/orchestration` snapshot hook now subscribes to the `agents` WebSocket channel (`agent.task.updated`) instead of `requests` as its refetch trigger, and relaxes its background poll from 5s to 30s while that WS connection is up — falling back to the tighter 5s cadence, reprogrammed live on any connect/disconnect transition, whenever the socket is down. ([#12409](https://github.com/diegosouzapw/OmniRoute/pull/12409)) +- **feat(dashboard):** Orchestration canvas quick wins — search box plus state/source/provider filter chips with a one-click clear, and per-source collapse/expand, all reflected in the URL so a filtered/collapsed view is shareable and survives a refresh; the detail drawer gained a "copy trace JSON" action and hardened error/empty-state and accessibility handling; the Agents-tab edges now animate traveling particles along active (running) connections; and the canvas node/edge status colors moved off fixed hex values onto theme-aware `--orch-status-*` CSS custom properties, so they adapt correctly to light/dark mode. ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- **chore(dashboard):** Orchestration UI hardening pass and the missing component/model test coverage it called for — `OrchestratorNode`/`ActivityNode`/`OverflowNode` rendering, the `?node=`/overflow-click page routing, the Agents-tab orchestrator-click no-op and `showCompleted` toggle, and the overview kanban's done-column sort order (#12270, #12271). ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- New `/dashboard/orchestration` page: live unified view of everything running — Cloud Agent, A2A and Conductor as a real-time graph (Agents tab), the combo cascade (Routing tab, reusing the Combo Live Studio) and a state kanban (Overview tab), with a detail drawer (trace, cost, approve/cancel). Read-only over existing APIs — no new backend. Canvas concept credit: PR #11815 design ([#12261](https://github.com/diegosouzapw/OmniRoute/pull/12261)) +- **feat(dashboard):** new "History" tab on `/dashboard/orchestration` — an Airflow-style grid of finished runs over a 24h/7d/30d preset window, one row per (source, identity), clicking a cell opens the existing detail drawer. It is backed by real persistence: A2A task lifecycle transitions are now written to the `a2a_tasks` table (purged after 30 days, configurable via `OMNIROUTE_A2A_HISTORY_RETENTION_DAYS`) and served by the new `GET /api/a2a/tasks/history` listing endpoint, with the task-detail route falling back to persisted history once a run leaves the in-memory snapshot. Conductor runs stay remote and are not persisted locally — the tab says so instead of silently omitting them. ([#12479](https://github.com/diegosouzapw/OmniRoute/pull/12479)) +- **feat(dashboard):** the orchestration detail drawer gained a "Repeat" action for Cloud Agent, A2A and Conductor tasks — a two-click confirm (click once to arm, click again within the confirm window to fire) re-submits the original prompt/input as a new run. The button is disabled with an explanatory tooltip whenever the original input can't be recovered from the loaded task detail (e.g. it never carried a prompt, or the detail failed to load). Two limitations of the A2A variant, by design: it targets the `/a2a` JSON-RPC endpoint, which authenticates with an API key only (`REQUIRE_API_KEY=true` or a configured `OMNIROUTE_API_KEY` makes a dashboard-session repeat answer `HTTP 400` — surfaced verbatim in the drawer's error line, never as a success), and the `message/send` call is SYNCHRONOUS: the POST blocks for the whole skill run, so the success confirmation only appears once the repeated task finishes. A dashboard-authenticated A2A creation path is deliberately left to a follow-up — widening the endpoint's auth posture is an operator decision, not a side effect of this feature. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(a2a):** A2A task execution now records which memories were consulted for the task's last user message as `metadata.memoryHits` (id/key/type/content-snippet) plus a `memory_hits` history event, purely for observability — the retrieved memory is never injected into a skill's prompt or behavior. Gated by the `OMNIROUTE_A2A_MEMORY_HITS` kill-switch (default enabled; set to `0` to skip the recall lookup entirely). The drawer's new "Memory used" section lists these hits for a2a tasks and is omitted whenever there are none. Known limitation: recall only resolves under the keyless posture — a keyed caller's task owner is a SHA-256 prefix of the API key, while memory rows are keyed by the database api-key id, and no hash→id lookup exists today, so the hit list stays empty for keyed callers. The recorded hits are also kept out of the task's own `input` (and therefore out of the persisted input and of the "Repeat" request body), so repeating a task never re-sends the previous run's memory snippets. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(providers):** add a Perplexity Agent API provider (`perplexity-agent` / `pplx-agent`) for Perplexity `/v1/responses`, including the documented Anthropic, OpenAI, Google, xAI, DeepSeek, Z.AI, Moonshot/Kimi, NVIDIA, and Perplexity model IDs plus Anthropic-model `max_output_tokens` compatibility. ([#12103](https://github.com/diegosouzapw/OmniRoute/pull/12103)) — thanks @AIB1TAL0S +- **feat(providers):** add RPD (Requests Per Day) limit to provider rate limit overrides across UI, schemas, DB, and i18n ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) — thanks @b3nw +- **feat(providers): add UC Direct (uncensored.com Developer API), the metered OpenAI-compatible surface.** A standard OpenAI-compatible passthrough (default executor) for uncensored.com's official REST API at `https://api.uncensored.com/api/v1`: `X-api-key` auth (never-expiring `uai_sk_live_` key), `POST /chat/completions` with streaming SSE and native tool-calling, and the full live metered catalog (82 models across 15 providers, discovered from the public `GET /v1/models`). Registered as provider `uc-direct` (alias `ucd`). Complements the un-metered `uc` persona provider — same models, metered credits and a plain API key instead of a subscription session. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(providers): add UC (uncensored.com), the un-metered subscription "persona" chat as an OpenAI-compatible provider.** A WebSocket web-app port: a durable Clerk credential mints a short-lived session token per connect (browserless — no API key), driving UC's persona socket. Ships the browserless email-code login (request → verify → harvest), the 19 verified persona models (Claude Opus, Gemini, Grok, GLM, Kimi, DeepSeek, MiniMax, incl. the uncensored variants), prompted `` tool-calling with a per-model code-style dialect + auto-cure retry for guardrailed models, live ``/reasoning split, streaming + non-streaming OpenAI responses, and full quota/auth error surfacing (paywall / message-limit / rate-limit → 429, invalid session → 401 re-login). Full multimodal parity via the persona blob-upload layer: **vision** (image input, 15 vision-capable models), **document RAG** (PDF/doc upload, server-side extraction), **image generation** (22 models), **video generation** (14 models, async signed-url → poll), and **TTS** (streaming MP3). Registered as provider `uc` (alias `ucn`). The metered OpenAI-compatible Developer API is a separate `uc-direct` provider. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(logs):** add copy-all button to request detail modal ([#11083](https://github.com/diegosouzapw/OmniRoute/pull/11083)) — thanks @AndrianBalanescu +- **feat(check):** make serviceKinds required and add the reverse-walk provider consistency gate ([#11392](https://github.com/diegosouzapw/OmniRoute/pull/11392)) — thanks @Tushar49 +- **feat(providers):** only advertise verified-working connections by default ([#11448](https://github.com/diegosouzapw/OmniRoute/pull/11448)) — thanks @alltomatos +- **feat(resilience):** add hierarchical concurrency admission ([#11493](https://github.com/diegosouzapw/OmniRoute/pull/11493)) +- **feat(providers):** derive + surface expiry for JWT-bearing web cookies (#11497) ([#11505](https://github.com/diegosouzapw/OmniRoute/pull/11505)) — thanks @oyi77 +- **feat(tunnels):** support named cloudflared tunnels via CLOUDFLARED_CONFIG ([#11561](https://github.com/diegosouzapw/OmniRoute/pull/11561)) — thanks @ggiak +- **feat(compression):** make proactive context-compression threshold a live setting ([#11564](https://github.com/diegosouzapw/OmniRoute/pull/11564)) — thanks @ragnar-claude +- **feat(resilience):** add provider peak-hour protection ([#11622](https://github.com/diegosouzapw/OmniRoute/pull/11622)) — thanks @Neuron-Mr-White +- **feat(providers):** add 1min.ai provider ([#11631](https://github.com/diegosouzapw/OmniRoute/pull/11631)) — thanks @zero-executioner +- **feat(dashboard):** collapsible JSON tree viewer for request/response payloads ([#11703](https://github.com/diegosouzapw/OmniRoute/pull/11703)) — thanks @hartmark +- **feat(routing):** add LiquidAI LFM2.5-2.6B free tier via OpenRouter ([#11752](https://github.com/diegosouzapw/OmniRoute/pull/11752)) — thanks @brick30llc-ctrl +- **feat(pricing):** add GLM-5.3-Flash pricing, model specs, and catalog registration ([#11830](https://github.com/diegosouzapw/OmniRoute/pull/11830)) — thanks @AndrianBalanescu +- **feat(routing):** add relayMode for schema-locked context handoffs ([#11839](https://github.com/diegosouzapw/OmniRoute/pull/11839)) — thanks @raheemuddin786 +- **feat(config):** add support for runtime system prompt configuration and hot-reloading ([#11841](https://github.com/diegosouzapw/OmniRoute/pull/11841)) — thanks @raheemuddin786 +- **feat(routing):** expand connection-aware quota prefilter across combo strategies (#11682) ([#11850](https://github.com/diegosouzapw/OmniRoute/pull/11850)) — thanks @HouMinXi +- **feat(models):** reactive discovery sync after Antigravity model-not-found 404 ([#11888](https://github.com/diegosouzapw/OmniRoute/pull/11888)) — thanks @MumuTW +- **feat(models):** auto-aliases and token limits from synced Antigravity discovery ([#11889](https://github.com/diegosouzapw/OmniRoute/pull/11889)) — thanks @MumuTW +- **feat(settings):** raise sticky round-robin limit caps to 1000 ([#12015](https://github.com/diegosouzapw/OmniRoute/pull/12015)) — thanks @adivekar-utexas +- **feat(resilience):** operator-configurable global credential health check interval ([#12043](https://github.com/diegosouzapw/OmniRoute/pull/12043)) — thanks @adivekar-utexas +- **feat(quota):** use official OpenCode Go usage API ([#12124](https://github.com/diegosouzapw/OmniRoute/pull/12124)) — thanks @ddarkr +- **feat(resilience):** default the credential health check sweep to 60 minutes ([#12138](https://github.com/diegosouzapw/OmniRoute/pull/12138)) — thanks @adivekar-utexas +- **feat(quality):** new-code mode for the complexity and dead-code ratchets (Clean as You Code) ([#12142](https://github.com/diegosouzapw/OmniRoute/pull/12142)) +- **feat(dashboard):** orchestration canvas — unified model + snapshot hook (part 1/2) ([#12156](https://github.com/diegosouzapw/OmniRoute/pull/12156)) +- **feat(usage):** add Kilo Code balance and Kilo Pass quotas ([#12178](https://github.com/diegosouzapw/OmniRoute/pull/12178)) — thanks @killer30001000 +- **feat(providers):** refresh vendored ChatGPT Web connector to v4.0.7 ([#12181](https://github.com/diegosouzapw/OmniRoute/pull/12181)) — thanks @backryun +- **feat(providers):** manual "Clear cooldown" action in the cooling panel ([#12224](https://github.com/diegosouzapw/OmniRoute/pull/12224)) — thanks @patrykkopycinski +- **feat(providers):** restore ChatGPT Web via clean-room browser transport ([#12239](https://github.com/diegosouzapw/OmniRoute/pull/12239)) — thanks @backryun +- **feat(sse):** wire the PROVIDER_PROFILES window gate into the global provider cooldown ([#12247](https://github.com/diegosouzapw/OmniRoute/pull/12247)) +- **feat(providers):** modernize CLOVA Studio chat and embeddings ([#12277](https://github.com/diegosouzapw/OmniRoute/pull/12277)) — thanks @backryun +- **feat(browser):** adopt Obscura as primary headless browser engine with Chromium fallback ([#12286](https://github.com/diegosouzapw/OmniRoute/pull/12286)) — thanks @opensource-elearning +- **feat(sse):** full language parity for output styles (es/de/fr/it/ru/zh + autoDetect) ([#12289](https://github.com/diegosouzapw/OmniRoute/pull/12289)) +- **feat(quality):** complete test:scoped — --full map rebuild, stdin selection, CI loader parity (#8084 D1) ([#12353](https://github.com/diegosouzapw/OmniRoute/pull/12353)) +- **feat(providers):** refresh Fable, Cursor, and Devin catalogs ([#12367](https://github.com/diegosouzapw/OmniRoute/pull/12367)) — thanks @backryun +- **feat(video):** redact transcript text from logs and durable memory (#12150 P1) ([#12427](https://github.com/diegosouzapw/OmniRoute/pull/12427)) +- **feat(dashboard):** parent-link, genuine-continuation badge, and modal perf fixes ([#12448](https://github.com/diegosouzapw/OmniRoute/pull/12448)) — thanks @hartmark +- **feat(dashboard):** adaptive context-budget dial on compression panel ([#12488](https://github.com/diegosouzapw/OmniRoute/pull/12488)) — thanks @HouMinXi +- **feat(providers):** refresh Z.ai Web models and browser transport ([#12524](https://github.com/diegosouzapw/OmniRoute/pull/12524)) — thanks @backryun +- **feat(video):** redact raw client-snapshot transcript fields in the detailed log (#12150 P2) ([#12528](https://github.com/diegosouzapw/OmniRoute/pull/12528)) +- **feat(providers):** refresh NVIDIA hosted models ([#12538](https://github.com/diegosouzapw/OmniRoute/pull/12538)) — thanks @backryun +- **feat(quota):** Moonshot Open Platform balance and TPD lock for custom nodes ([#12590](https://github.com/diegosouzapw/OmniRoute/pull/12590)) — thanks @HouMinXi +- **feat(video):** redact transcript in the in-memory pending-request snapshot (#12430 item 6) ([#12596](https://github.com/diegosouzapw/OmniRoute/pull/12596)) +- **feat(dashboard):** link a log entry's Conversation Context to its owning conversation ([#12646](https://github.com/diegosouzapw/OmniRoute/pull/12646)) — thanks @hartmark +- **feat(catalog):** eligibility-gated free-tier bucket ([#12669](https://github.com/diegosouzapw/OmniRoute/pull/12669)) +- **feat(db):** fail-closed previous_response_id continuation for redacted video turns (#12150 P2b) ([#12707](https://github.com/diegosouzapw/OmniRoute/pull/12707)) +- **feat(gamification): pay the documented `streak_bonus` and `badge_unlock` XP rewards.** `XP_REWARDS` listed both rewards but the award pipeline never paid them: the private reward table in `events.ts` omitted them, `updateStreak()` did not report when a streak extended, and badge unlocks carried no XP. Every request that extends a daily streak now pays `streak_bonus × streak length` once per UTC day (guarded by a same-day `xp_audit_log` check), and every badge unlocked through the pipeline pays `badge_unlock` once per badge (guarded by the `user_badges` primary key; `unlockBadge()` now reports whether it inserted). Bonus XP flows through the same `addXp` + level sync + global/weekly/monthly leaderboard path as action XP, so level-ups and rankings include it. The Radar supporter recognition unlock stays XP-free. (#12522 — thanks @pacocartones) +- **feat(providers):** advertise a `free-tier` capability in the provider plugin manifest for every provider with documented free models, so sidecars and dashboards can filter free-capable providers without reading the quota catalog ([#12786](https://github.com/diegosouzapw/OmniRoute/pull/12786)) — thanks @maxmad64bis +- **feat(providers):** Added EURouter as an OpenAI-compatible API-key gateway (`https://api.eurouter.ai/v1`), with live model discovery via `passthroughModels`. Its copy states that models are served by third-party upstreams listed per model, so an EU-based router is not read as EU data residency for inference. ([#13025](https://github.com/diegosouzapw/OmniRoute/pull/13025)) — thanks @ntdatt812 +- **feat(providers):** Added GreenPT as an OpenAI-compatible API-key provider (`https://api.greenpt.ai/v1`), with live model discovery via `passthroughModels`. No free-inference badge: the published docs describe a free API subscription billed per token, not a free tier. ([#13024](https://github.com/diegosouzapw/OmniRoute/pull/13024)) — thanks @ntdatt812 +- **feat(i18n):** 9 new locales — Greek (`el`), Croatian (`hr`), Serbian (`sr`), Lithuanian (`lt`), Estonian (`et`), Latvian (`lv`), Slovenian (`sl`), Maltese (`mt`), Irish (`ga`) — across the dashboard, docs mirrors, CLI, README and the site; OmniRoute now ships every official EU language (51 locales). (#13044) +- **feat(i18n):** 8 new locales — Kannada (`kn`), Malayalam (`ml`), Odia (`or`), Punjabi (`pa`), Nepali (`ne`), Sinhala (`si`), Burmese (`my`), Khmer (`km`) — across the dashboard, docs mirrors, CLI, README and the site (59 locales). The translator now restores the ICU literal escape around angle placeholders and splits oversized docs sections before translating. (#0000) ([#13660](https://github.com/diegosouzapw/OmniRoute/pull/13660)) +- feat(providers): **list Agnes 3.0 Flash as the current free chat model, drop retired 1.5 Flash, add Image 2.0/2.5 Flash plus Video 2.5/2.5 Flash, and discover the live `/v1/models` catalog (including the CN host `api.agnes-ai.cn`).** `agnes-1.5-flash` now forwards to `agnes-3.0-flash`. Video 2.5 polls `GET /v1/videos/{id}` (not the V2.0 `/agnesapi` contract). Live `/v1/models` (2026-09-09) no longer serves 1.5; the wiki marks it deprecated. 3.0 Flash is 512K context / 65,536 max output, same window as 2.5. CN-region keys use the existing per-connection base-URL field, default stays `apihub.agnes-ai.com`. ([#13120](https://github.com/diegosouzapw/OmniRoute/pull/13120)) — thanks @HouMinXi +- **feat(sse):** Codex and OpenAI catalogs list GPT-6 Astra with effort aliases (`-low` through `-ultra`); Codex CLI identity pins `@openai/codex@0.153.4` in lockstep with the image ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **feat(dashboard):** the orchestration History tab gained a "Compare runs" mode — toggling it turns each grid cell into a 2-item selection queue (a 3rd click drops the oldest pick), and picking two cells opens a side-by-side comparison panel instead of the usual detail drawer. The panel fetches both runs' detail the same way the drawer does (falling back to persisted history once a run leaves the live TTL window) and shows, per side: identity/source/state, start time, a signed `right - left` delta for duration/cost/event count, the event timeline aligned by index, and any memory hits. One side's fetch failing never blocks the other, and a delta is only ever computed when both sides have a finite value — otherwise it renders "—", never `NaN`. Comparing two runs from different sources or skills still works; a banner marks the deltas as informational rather than hiding them, since the two runs aren't a strict apples-to-apples pair. ([#12677](https://github.com/diegosouzapw/OmniRoute/pull/12677)) +- **feat(dashboard):** badge a conversation that never reached a clean stop ([#12717](https://github.com/diegosouzapw/OmniRoute/pull/12717)) — thanks @hartmark +- **feat(release):** reconcile-changelog tool + version-anchored fragment aggregation ([#12987](https://github.com/diegosouzapw/OmniRoute/pull/12987)) +- **feat(mcp):** carry context_length through MCP list_models_catalog projection (#12776) ([#13406](https://github.com/diegosouzapw/OmniRoute/pull/13406)) — thanks @KooshaPari +- **feat(cli):** make startup readiness budget configurable (#13369) ([#13433](https://github.com/diegosouzapw/OmniRoute/pull/13433)) — thanks @KooshaPari + ### 🐛 Bug Fixes +- **security(streaming):** sanitize generic mid-stream error messages before emitting OpenAI, + Responses, or Claude SSE failure frames and before diagnostic logging, while preserving raw + failures for internal classification and keeping client disconnects out of provider failure state. ([#12457](https://github.com/diegosouzapw/OmniRoute/pull/12457)) +- **fix(claude):** `blockExtraUsage: false` no longer lets 5h quota preflight skip the account; extra usage is billed after the session bar is gone, so the request must reach Anthropic ([#12803](https://github.com/diegosouzapw/OmniRoute/pull/12803)) — thanks @HouMinXi +- Sanitize HuggingChat conversation-creation and message-send transport failures before they reach client error bodies or provider logs. ([#12467](https://github.com/diegosouzapw/OmniRoute/pull/12467)) +- **fix(dashboard):** Keep local and theme-aware provider SVG icons at a definite layout size so Chromium does not collapse them to 0×0 after the v3.8.50 image-rendering change ([#12054](https://github.com/diegosouzapw/OmniRoute/pull/12054)) — thanks @ponkcore +- **fix(security):** Sanitize provider and runtime failures before public API, SSE and MCP responses and before persistent request, proxy and usage logs, preventing credentials, stack traces and host filesystem paths from crossing those boundaries while preserving stable error codes and useful diagnostics. ([#12506](https://github.com/diegosouzapw/OmniRoute/pull/12506)) +- **fix(cli):** `omniroute tunnel create` no longer crashes with `Cannot read properties of undefined (reading optsWithGlobals)` — removed the duplicate positional argument that caused Commander.js to misalign the action callback parameters ([#12295](https://github.com/diegosouzapw/OmniRoute/issues/12295)) ([#12368](https://github.com/diegosouzapw/OmniRoute/pull/12368)) — thanks @gonisulaimann +- Stop advertising Gemini Live-only models as supported audio endpoints until OmniRoute proxies the bidirectional Live protocol. ([#11710](https://github.com/diegosouzapw/OmniRoute/pull/11710)) — thanks @RaviTharuma +- **fix(kie):** reroute `flux/kontext` off the KIE Market `createTask` flow — it is catalogued with `isMarket: true` but has no Market catalog page, so KIE rejected it with "model name not supported"; it now hits the dedicated `POST /api/v1/flux/kontext/generate` / `GET /api/v1/flux/kontext/record-info` endpoints instead (#11296). ([#11985](https://github.com/diegosouzapw/OmniRoute/pull/11985)) — thanks @hartmark +- **fix(kie):** correct 12 more KIE Market catalog ids that were sent to `createTask` unchanged but diverge from KIE's documented upstream `model` values — GPT Image 2 T2I/I2I (drops the `gpt/` prefix), GPT Image 1.5 T2I/I2I (`gpt-image/` namespace), Seedream 5.0 Lite T2I/I2I (drops the `.0`), all 4 Flux 2 variants (`flux-2/` namespace, generic variant renamed `flex`), and Wan 2.7 Image / Image Pro (dash instead of dot) — each verified individually against the literal example request published on docs.kie.ai. `#11326`'s "everything else already matches" claim was wrong a second time (#11296); `z-image/4.0-*`/`z-image/4.5-*` and `flux/kontext` remain open, documented as unresolved in `KIE_MARKET_UPSTREAM_MODEL_IDS`'s comment pending further verification. ([#11667](https://github.com/diegosouzapw/OmniRoute/pull/11667)) +- **fix(dashboard):** `useApiKeySave.handleSaveApiKey` no longer forces a full upstream `/models` catalog sync on every non-curated provider connection save — callers can now pass `skipModelSync: true` to opt out, so a workflow that only wants to add one manual model no longer floods the provider's available-models list with hundreds/thousands of synced entries. The flag is a client-side intent signal only and is stripped before the connection payload is POSTed to `/api/providers`; default behavior (full sync on save) is unchanged when the flag is omitted (#11324) ([#11664](https://github.com/diegosouzapw/OmniRoute/pull/11664)) +- **Provider connections:** keep `tokenExpiresAt` when a connection is created. The create-path allowlist omitted it, so every insert stored NULL and the dashboard token badge could read a fresh connection as expired until its first background refresh ([#11368](https://github.com/diegosouzapw/OmniRoute/pull/11368)). — thanks @ntdatt812 +- **fix(combos):** the combo builder's precision-select, global-model-search, and manual-entry flows now serialize a model step's `model` string using the provider's already-computed routing-alias prefix (e.g. `oc/`) instead of rebuilding it from the raw canonical `providerId`, fixing the no-auth "OpenCode Free" provider (`opencode`) being routed to the unrelated paid "OpenCode Zen" provider (`opencode-zen`) because `opencode` doubles as a manual routing-prefix override ([#11433](https://github.com/diegosouzapw/OmniRoute/issues/11433)). ([#11670](https://github.com/diegosouzapw/OmniRoute/pull/11670)) +- **fix(radar):** the catalog feed cache now keeps `generatedAt`, the date the feed's data was built, next to `fetchedAt`, the date this install downloaded it (#11435). The feed schema requires that date and the sync path validates it, but the cache dropped it — so a feed fetched minutes ago and one carrying weeks-old figures looked identical to everything downstream, including the dashboard's "Last fetched" line. `getRadarCatalog().meta` and `GET /api/radar/status` now report both dates, the latter as its own field rather than folded into `version` — and omitted entirely for the offers and intel caches, which keep no build date, where a `null` would read as "unknown" rather than "never stored". The dashboard still shows only the fetch time; surfacing the build date there needs a new translated label and is left to a follow-up. Rows cached before migration 163 read back as `null`: unknown stays unknown instead of borrowing the fetch time. The referrals cache has persisted the same date since migration 142. — thanks @maxmad64bis +- **fix(cli):** postinstall no longer fills `JWT_SECRET` and `API_KEY_SECRET` in the installed package's `.env` (#11436). `.env.example` ships both blank on purpose: the server restores them from its durable store, or generates and persists them there, in `ensureSecrets()`. Pre-filling them defeated that — the file lives inside the package directory, so `npm i -g` replaced it and postinstall wrote _different_ values, while `ensureSecrets()` (which only acts on an empty variable) never got to restore the real ones. Both secrets rotated silently on every update, invalidating dashboard sessions and API-key CRCs. `STORAGE_ENCRYPTION_KEY` left the same list for the same reason in #1622; its comment pointed at a function that no longer exists and now names the real provisioning path. — thanks @maxmad64bis +- **fix(cli):** the CLI now says when a loaded `.env` lives inside the installed package directory (#11437). It already announces every env file it reads, without distinguishing the ones that survive an update from the one that does not: `npm i -g` replaces the package directory wholesale, so values set there are gone at the next update, silently. The warning names the durable path to move them to, and fires only when that file actually supplied a value — a file entirely shadowed by a durable one supplied nothing. A development checkout stays silent: there the same path is stable and documented in `SETUP_GUIDE.md`. — thanks @maxmad64bis +- **fix(free-models):** the shared `isFreeModel()` predicate no longer reports catalog entries marked `freeType: "discontinued"` as free, so `hidePaidModels` can't route to Pollinations' seven premium models that now require a paid key ([#11441](https://github.com/diegosouzapw/OmniRoute/pull/11441)) — thanks @maxmad64bis +- fix(ui): let AnySearch use the normal provider-icon fallback when LobeHub has no matching icon (#11449) +- Fixed the v3.8.50 Costs and Analytics dashboards so flat-rate Claude Code usage can be shown as an explicitly requested token-price estimate without changing default billed-cost semantics. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed archived usage retention so each request is priced individually instead of pricing a day's summed tokens once, which understated archived cost whenever a day mixed cache-heavy and ordinary requests. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed the Costs dashboard so it discloses when displayed figures include flat-rate token-price estimates instead of labelling them as billed spend, using the flag the analytics API already returns; the month-end projection and the CSV/JSON exports carry the same marker, and billed-cost mode is unchanged. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- **fix(combo):** attach the same combo-diagnostics trace (`poolSize`/`attemptOrder`/`excluded`/`terminalReason`, plus `x-omniroute-combo-*` headers) to the round-robin strategy's and the nested pipeline/fusion runtime-unit loop's "Maximum combo retry limit reached" 503 that the priority-strategy path already attaches for the identical terminal condition — previously those two paths returned a bare, contextless 503 ([#11462](https://github.com/diegosouzapw/OmniRoute/issues/11462)). ([#11665](https://github.com/diegosouzapw/OmniRoute/pull/11665)) +- **fix(bun):** use native `bun:sqlite` in `bootstrap-env` and `sync-env` to avoid loading `better-sqlite3` N-API addon during Bun startup ([#11468](https://github.com/diegosouzapw/OmniRoute/pull/11468)) — thanks @TheDemonTuan +- **fix(docker-bun):** make `Dockerfile.bun` install reliable with pre-install script helpers and native `bun:sqlite` smoke check ([#11470](https://github.com/diegosouzapw/OmniRoute/pull/11470)) — thanks @TheDemonTuan +- **fix(docker-bun):** remove every vendored `better-sqlite3` native addon from the Bun runtime image so startup cannot abort before the `bun:sqlite` fallback ([#11482](https://github.com/diegosouzapw/OmniRoute/pull/11482)) — thanks @TheDemonTuan +- **fix(autoCombo):** effort/alias model variants (`gpt-5.6-sol-xhigh`, `gpt-5.6`, cursor's `claude-4.6-opus-high`) inherit their base model's task fitness instead of falling to the wildcard 0.5 ([#11492](https://github.com/diegosouzapw/OmniRoute/pull/11492)) — thanks @MumuTW +- fix(db): rate-limit repeated Arena ELO leaderboard fetch-failure warnings instead of logging one per sync attempt (#11500) ([#11989](https://github.com/diegosouzapw/OmniRoute/pull/11989)) — thanks @hartmark +- **fix(db):** dedupe the raw `[Encryption] Decryption failed...` log line emitted by the lazy-decrypt views (`createLazyRowProxy`/`createLazyConnectionView`), which power `getProviderConnections()` and were re-triggering that line on every CredentialHealth/model-sync cycle for the same corrupt or stale-key credential — a fresh Proxy over a fresh row on every cycle meant the per-proxy memoization never suppressed it, unlike the dedup `decryptConnectionFields()` already had since [#9927](https://github.com/diegosouzapw/OmniRoute/issues/9927). Now shares that dedupe tracking so the line logs at most once per credential ([#11500](https://github.com/diegosouzapw/OmniRoute/issues/11500)). ([#11678](https://github.com/diegosouzapw/OmniRoute/pull/11678)) +- **fix(intelligence):** Arena ELO sync stores a synthesized base-model row for every effort/harness variant (`claude-opus-5-max` → `claude-opus-5`, `gpt-5.6-sol-xhigh (codex-harness)` → `gpt-5.6-sol`), so bare model requests reach the synced intelligence layer instead of the static fallback table; the hand-maintained `MODEL_ALIAS_MAP` — which collapsed generations (`gpt-5.5` → `gpt-5`) — is gone ([#11506](https://github.com/diegosouzapw/OmniRoute/pull/11506)) — thanks @MumuTW +- **fix(autoCombo,sse):** catalog hygiene — the static task-fitness table no longer ranks retired models above live ones (dead rows dropped, patterns matched on segment boundaries), the 7 deprecation aliases that forwarded to retired or non-existent models now point at the vendors' published replacements, an alias is no longer rewritten when the serving provider offers the id as-is, and a new `check:model-lifecycle` gate diffs both tables against a vendor lifecycle snapshot ([#11507](https://github.com/diegosouzapw/OmniRoute/pull/11507)) — thanks @MumuTW +- **fix(sse):** stop dropping resolved `thoughtSignature` values on parallel (multi tool-call) turns sent to Gemini 3.x — the claude→gemini and openai→gemini translators previously kept the signature only on the *first* function call of a message, causing Gemini to reject subsequent calls in the same turn with HTTP 400 "Function call is missing a thought_signature"; each function call now keeps its own resolved signature ([#11510](https://github.com/diegosouzapw/OmniRoute/issues/11510)). ([#11669](https://github.com/diegosouzapw/OmniRoute/pull/11669)) +- Treat Docker publishing from a non-default release branch as an expected skip instead of a failed workflow, while preserving the guard that prevents it from overwriting the `next` channel. ([#11527](https://github.com/diegosouzapw/OmniRoute/pull/11527)) — thanks @pacocartones +- fix(search): prefer credentialed providers over duckduckgo-free fallback (#11524) — the fallback-only loop ran before the credentialed-providers loop in `executeWebSearch`, making configured providers unreachable when `duckduckgo-free` was available. ([#11565](https://github.com/diegosouzapw/OmniRoute/pull/11565)) — thanks @vsd2807 +- **fix(sse):** cap the upstream headers-wait phase for STREAMING requests to a client-realistic ceiling (110s, under Codex's own ~120s hard client-abort window) instead of the flat 10-minute `FETCH_TIMEOUT_MS` default — that default was 5x longer than the body-phase readiness watchdog's own adaptive bound, so a request whose upstream never returned any response at all (not even headers, e.g. a stalled NVIDIA target behind a tool-heavy Responses→Chat translation) kept the client connection alive on keepalives only, guaranteeing the client's own patience ran out first with an opaque 499 instead of OmniRoute detecting and failing the stall fast. Non-streaming requests are unaffected — they keep the existing flat default (`open-sse/utils/fetchStartTimeoutPolicy.ts`) (#11526) ([#11668](https://github.com/diegosouzapw/OmniRoute/pull/11668)) +- **fix(ci):** protocol-clients E2E harness boots the peer-stamped custom server (`run-next.mjs dev`) instead of the bare `next` CLI, so `LOCAL_ONLY` locality resolves from the real TCP peer stamp and `/api/mcp/audit` answers 200 (open bootstrap preserved via an `OMNIROUTE_E2E_BOOTSTRAP_MODE=open` credential clear in `run-next.mjs`, pinned to loopback `HOST`) instead of a deterministic 403 (#11535) ([#11549](https://github.com/diegosouzapw/OmniRoute/pull/11549)) — thanks @jonlwheat2-gif +- **fix(docs):** the Auto-Combo engine is described with the number of factors it actually declares. Four different counts were in circulation — 6, 9, 13 and 14 across `AGENTS.md`, four guides and six code comments — while `DEFAULT_WEIGHTS` declares 15. `check:docs-counts` now reads that number from the source, so adding a factor without updating the prose fails CI ([#11545](https://github.com/diegosouzapw/OmniRoute/pull/11545)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **fix(quota-share):** in-flight leases are per request, so an aborted request's slot ages out instead of being kept alive by later traffic on the same connection ([#11547](https://github.com/diegosouzapw/OmniRoute/pull/11547)) — thanks @abhisheksharma2411 +- The free-tier summary route now serves the refreshed Radar catalog when the feed is active — the same numbers the dashboard shows — and states which catalog answered plus its real build date, instead of always reporting release-frozen figures with a stale curation date. The supporter-key live feed stays reserved to authenticated callers of the instance. ([#11550](https://github.com/diegosouzapw/OmniRoute/pull/11550)) — thanks @maxmad64bis +- **sse:** fix LiveWS/embed-WS servers crashing at startup under the Node/tsx runtime — `liveServer.ts`, `embedWsProxy.ts` and `apiBridgeServer.ts` imported `@/shared/utils/httpClientAbortGuard` without the `.mjs` extension, so the client-abort crash guard added by [#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556) was unreachable and every dependent test failed with `ERR_MODULE_NOT_FOUND` ([#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556)). ([#11671](https://github.com/diegosouzapw/OmniRoute/pull/11671)) — thanks @jonlwheat2-gif / @yxyxy +- fix(chat): shed any of the matched provider-node's routing identifiers (public prefix or internal id) before model lookup, so composites like `//` no longer reach upstream verbatim (#11557) — thanks @maxmad64bis +- **fix(cache):** a semantic-cache entry now expires on its TTL instead of surviving until the next UTC midnight, and `dbEntries` stops counting expired rows ([#11573](https://github.com/diegosouzapw/OmniRoute/pull/11573)) — thanks @ntdatt812 +- **fix(api):** `/v1/models` schedules its stale-while-revalidate rebuild through Next's `after()` again, so a stale catalog reaches the client before the rebuild blocks the event loop ([#11574](https://github.com/diegosouzapw/OmniRoute/pull/11574)) — thanks @ntdatt812 +- **fix(check):** the `known-symbols` executor conformance gate awaits the now-async `getExecutor()`, so it stops reporting all 142 registered aliases as dead symbols and can detect a lazy import that fails ([#11582](https://github.com/diegosouzapw/OmniRoute/pull/11582)) — thanks @ntdatt812 +- **fix(pack):** register `bin/cli/utils/volatileEnvPath.mjs` in `PACK_ARTIFACT_REQUIRED_PATHS`, so the prune can no longer silently drop a module the published CLI imports on every boot ([#11588](https://github.com/diegosouzapw/OmniRoute/pull/11588)) — thanks @ntdatt812 +- **i18n(vi):** translate the five reliability-column strings on the free-provider rankings page, which rendered as `__MISSING__:` markers in the Vietnamese UI ([#11589](https://github.com/diegosouzapw/OmniRoute/pull/11589)) — thanks @ntdatt812 +- **resilience:** restore the expired-connection retry-budget probe in the token-health sweep — the `!isGitHubAccessTokenOnlyConnection` carve-out reintroduced by #11608 contradicted the boundary pinned by #11592, so a GitHub connection parked at `expired` with retry budget remaining was never probed and could never self-heal ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)). ([#11672](https://github.com/diegosouzapw/OmniRoute/pull/11672)) — thanks @jonlwheat2-gif / @ntdatt812 +- **fix(dashboard):** Provider Stats now displays combo success rates on their native 0–100 percentage scale instead of multiplying them twice ([#11595](https://github.com/diegosouzapw/OmniRoute/pull/11595)) — thanks @pacocartones +- **fix(providers):** route DuckDuckGo Web Chat through the verified `duck.ai` endpoint, refresh its free model catalog, and avoid forwarding unsolved anti-abuse challenges ([#11597](https://github.com/diegosouzapw/OmniRoute/pull/11597)) — thanks @solstxce +- **fix(ui):** Console log Refresh and Copy controls now expose localized accessible names, keep copy actions visible on keyboard focus, and announce copy completion safely ([#11599](https://github.com/diegosouzapw/OmniRoute/pull/11599)) — thanks @pacocartones +- Dropped the stale-`.eslintcache` `restore-keys` fallback from both "Restore ESLint file cache" steps in `ci.yml`, so the blocking `Lint` job can no longer be served per-file verdicts computed under a different lint config, suppressions file or lockfile. `quality.yml` had already dropped it in #11963; `ci.yml` — the workflow that actually gates PRs — had not (#11600). ([#11996](https://github.com/diegosouzapw/OmniRoute/pull/11996)) +- fix(ci): pass `--pass-on-unpruned-suppressions` in `run-eslint-json.mjs` so the CI Lint job no longer fails when a suppression is merely orphaned by a genuine fix, mirroring the identical fix already in `validate-release-green.mjs` (#11600) ([#11983](https://github.com/diegosouzapw/OmniRoute/pull/11983)) — thanks @hartmark +- fix(handoff): enforce provider allowlist for universal handoff (#11602) — universal handoff now skips summarization when the selected summary model's provider is not included in the configured provider allowlist. ([#11619](https://github.com/diegosouzapw/OmniRoute/pull/11619)) — thanks @vsd2807 +- **fix(dashboard):** Show a stable error state when Search Analytics returns an HTTP error or malformed data ([#11603](https://github.com/diegosouzapw/OmniRoute/pull/11603)) — thanks @pacocartones +- **test(gamification):** pin the aggregate profile level to the XP-derived semantics of #11604 — `getAggregateXp()` now derives `currentLevel` from the summed XP (`calculateLevel(sum)`), not `MAX(stored current_level)`, and the #3484 fixture levels are aligned with the XP curve ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)). ([#11676](https://github.com/diegosouzapw/OmniRoute/pull/11676)) — thanks @jonlwheat2-gif / @pacocartones +- **fix(dashboard):** Keep the Profile level and progress aligned with aggregate XP, including bounded handling for invalid totals ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)) — thanks @pacocartones +- **fix(dashboard):** Prevent locked hidden badges from revealing their icon or opening private badge details before they are earned ([#11605](https://github.com/diegosouzapw/OmniRoute/pull/11605)) — thanks @pacocartones +- **fix(dashboard):** Restore keyboard focus after shared modals close and cancel delayed autofocus during cleanup ([#11607](https://github.com/diegosouzapw/OmniRoute/pull/11607)) — thanks @pacocartones +- **fix(dashboard):** Enable Enter and Space activation for clickable data-table rows without hijacking nested controls ([#11610](https://github.com/diegosouzapw/OmniRoute/pull/11610)) — thanks @pacocartones +- **fix(autoCombo,sse):** vendor-retired catalog ids are dropped from the auto-combo candidate pool and no longer win on leftover `arena_elo` / `user_override` rows; `getModelLifecycleDecision` consults `model-lifecycle.json` (prefix-stripped) so aggregator traffic is not `untracked` for ids the snapshot already knows ([#11625](https://github.com/diegosouzapw/OmniRoute/issues/11625)) ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)) — thanks @MumuTW +- **test(chatcore):** move Codex/Claude combo fixtures off models the lifecycle guard now rejects — `gpt-5.1-codex`/`gpt-5-codex` are vendor-retired (snapshot, #11626) and `claude-3-5-sonnet-20241022` is shut down, so native-passthrough and combo-fallback tests switched to `gpt-5.6-sol` and `claude-sonnet-4.6` ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)). ([#11675](https://github.com/diegosouzapw/OmniRoute/pull/11675)) — thanks @jonlwheat2-gif / @MumuTW +- fix(opencode-plugin): stop a free-tier budget that rounds up across a magnitude from rendering as `1000K`/`1000M` in the model picker — `fmtTokens` chose its unit from the raw token count and then rounded with `toFixed(1)`, so 999,950–999,999 printed as `1000K` rather than `1M` and just under a billion printed as `1000M` rather than `1B` ([#11684](https://github.com/diegosouzapw/OmniRoute/pull/11684)) — thanks @abhisheksharma2411 +- Cap prose-derived Antigravity quota resets at the configured model cooldown maximum while preserving authoritative Retry-After headers and Google RetryInfo hints. (#11695) Thanks @pacocartones. ([#11823](https://github.com/diegosouzapw/OmniRoute/pull/11823)) +- Compare `_rebaseline_*` notes structurally in the ratchet-bank verifier so an object-valued note no longer reports itself as rewritten on an unmodified tree — the nightly `bank-ratchet-shrinks` lane had been aborting on every run since 2026-08-11, leaving 41,672 lines of earned file-size shrinks unbanked (#11702). ([#11892](https://github.com/diegosouzapw/OmniRoute/pull/11892)) — thanks @MumuTW +- **fix(build):** `prepublish.ts` bundles the ChatGPT Web (Codex) MCP bridge through `runBuildTool()` instead of spawning `npx.cmd` raw, fixing the build crash on Node ≥ 20/Windows where `.cmd` shims cannot be spawned without a shell (EINVAL) ([#11704](https://github.com/diegosouzapw/OmniRoute/issues/11704)) ([#11706](https://github.com/diegosouzapw/OmniRoute/pull/11706)) — thanks @jonlwheat2-gif +- fix(codex): keep `parallel_tool_calls:false` on the translated Codex Responses Lite path (#11707) ([#11984](https://github.com/diegosouzapw/OmniRoute/pull/11984)) — thanks @hartmark +- **fix(tests):** the new Gemini Web Images retirement test (#11708) called the now-async `getExecutor()` synchronously, throwing `TypeError: getExecutor(...).getProvider is not a function` instead of asserting Gemini Web chat stays available. Awaited to match the R0.3 executor-registry refactor (#11220). ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(search):** Allow REST and MCP web-fetch requests to select the advertised `anysearch-search` provider explicitly ([#11716](https://github.com/diegosouzapw/OmniRoute/pull/11716)) — thanks @pacocartones +- **fix(providers):** Keep overnight peak-hour protection active after midnight according to the window's configured start day ([#11718](https://github.com/diegosouzapw/OmniRoute/pull/11718)) — thanks @pacocartones +- **fix(providers):** correct all five g4f.space gateway descriptions to document proof-of-work credits or member API keys, variable limits, and the remote prompt boundary instead of promising free keyless access at five requests per minute ([#11738](https://github.com/diegosouzapw/OmniRoute/pull/11738)). +- **fix(db):** `model_capabilities` is created by a migration instead of lazily on the first models.dev sync, so a clean install and an upgraded install converge on the same schema regardless of which features have run ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(ci):** `check:install-upgrade` now fails on an `npm` install truncated by ENOSPC (npm reports it as a warning and still exits 0), authenticates its health probe so the version assertion works against the hardened health payload, frees the clean-install tree before the upgrade phase, and no longer reports a schema divergence computed from a boot that never served ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(routing):** harden session-affinity key derivation against oversized explicit IDs and empty structural-payload collisions, while recognizing Gemini and common text request shapes ([#11746](https://github.com/diegosouzapw/OmniRoute/pull/11746)) — thanks @MumuTW +- **fix(packages/browser-pool):** regenerate `package-lock.json` so the `packages/browser-pool` workspace's locked `playwright`/`@types/node` (and transitives) match its `package.json` specs, fixing cache-only/offline installs (`npm ci --offline`, Nix `buildNpmPackage`) that previously failed with `ENOTCACHED` ([#11747](https://github.com/diegosouzapw/OmniRoute/issues/11747)) — thanks @benjaminkitt ([#11784](https://github.com/diegosouzapw/OmniRoute/pull/11784)) — thanks @geekyNads +- **fix(assets):** Stop distributing 79 provider and CLI logos without verified source/license provenance, keep every provider/tool available through the internal generic icon, and prevent those IDs from falling through to an external icon CDN. ([#11750](https://github.com/diegosouzapw/OmniRoute/pull/11750)) +- **fix(providers):** retire the common `chatgpt-web` / `cgpt-web` provider because its pre-key and proof-of-work provenance could not be cleared ([#11754](https://github.com/diegosouzapw/OmniRoute/pull/11754)) — explicit chat and image requests, slash-prefixed models, persisted aliases, proxy overrides, and combo targets now fail closed with a sanitized HTTP `410` / `PROVIDER_RETIRED` before upstream dispatch. Migration 163 tombstones matching connections and invalidates their active leases without deleting history or widening API-key allowlists. The independent `chatgpt-web-codex` / `cgpt-codex` provider and its MIT-noticed vendored implementation remain available. +- **fix(usage):** quota and usage refresh no longer 409 when an exclusive lease reserves the connection ([#11758](https://github.com/diegosouzapw/OmniRoute/pull/11758)) — thanks @TheDemonTuan +- Keep the embedding registry's vector width and `embedding` type on models when a synced model exists for the same id, so `/v1/models` no longer reports registry-described embedding models widthless or untyped (#11761) — thanks @rahilmavani +- Correct `google/gemini-embedding-001` on the OpenRouter route to 3072 dimensions, the width it returns when `dimensions` is not sent (#11761) — thanks @rahilmavani +- **fix(vision-bridge):** Restrict automatic vision routing and fallbacks to models available in authoritative live provider catalogs, including aliases and effort variants ([#11767](https://github.com/diegosouzapw/OmniRoute/issues/11767)) — thanks @pacocartones ([#11813](https://github.com/diegosouzapw/OmniRoute/pull/11813)) +- **fix(providers):** reclassify Cerebras as a one-time $5 signup credit (payment method required, 30-day validity), not a recurring no-card 1M tokens/day trial ([#11773](https://github.com/diegosouzapw/OmniRoute/issues/11773)) ([#12591](https://github.com/diegosouzapw/OmniRoute/pull/12591)) — thanks @HouMinXi +- fix(cli): stop prepublish from re-rebuilding the already-built ESM-only opencode-plugin dist (#11787) ([#11990](https://github.com/diegosouzapw/OmniRoute/pull/11990)) — thanks @hartmark +- **fix(db):** Fresh installs no longer log a non-fatal `no such table: compression_run_telemetry` warning when retention cleanup runs before the lazily-created telemetry table exists ([#11802](https://github.com/diegosouzapw/OmniRoute/pull/11802)) — thanks @RaviTharuma +- fix(sse): set X-OmniRoute-Selected-Connection-Id on successful combo dispatches so downstream consumers stop falling back to an empty connection id (#11810) ([#11986](https://github.com/diegosouzapw/OmniRoute/pull/11986)) — thanks @hartmark +- **fix(services):** embedded CLIProxyAPI lifecycle checks now use public `/healthz`, while model discovery uses the configured dedicated data-plane API key instead of the management password ([#11811](https://github.com/diegosouzapw/OmniRoute/pull/11811)) — thanks @RaviTharuma +- Fix persisted model steps returning an error in the playground route simulator, and warn when structural steps cannot be simulated. ([#11862](https://github.com/diegosouzapw/OmniRoute/pull/11862)) — thanks @pacocartones +- **fix(providers):** Antigravity's dynamic mitmAlias table no longer routes `gemini-3.7-flash-{high,medium,low}` to a literal tier-suffixed upstream id just because one connected account's own discovery listed it directly — those display ids always resolve through the safe `gemini-3.7-flash-tiered` static alias, so one account's Google-provisioned access no longer 404s every sibling account of the provider ([#11824](https://github.com/diegosouzapw/OmniRoute/issues/11824), [#11651](https://github.com/diegosouzapw/OmniRoute/issues/11651)) ([#11988](https://github.com/diegosouzapw/OmniRoute/pull/11988)) — thanks @hartmark +- **fix(dashboard):** Expose Profile loading, errors, page structure, and XP progress to assistive technologies ([#11838](https://github.com/diegosouzapw/OmniRoute/pull/11838)) — thanks @pacocartones +- **fix(socks):** `Agent.connectTimeout` now bounds both SOCKS handshake and TLS connect, and `family === null` no longer falls back to `fetch-socks` ([#11842](https://github.com/diegosouzapw/OmniRoute/pull/11842)) — thanks @maxmad64bis +- **perf(compression):** OOM mitigations for large payload hashing, memoization, and token estimation ([#11844](https://github.com/diegosouzapw/OmniRoute/pull/11844) — thanks @AndrianBalanescu) +- Excluded Next.js Node File Trace manifests (`*.nft.json`) from the published npm tarball. They are build-time metadata and are never read while serving, but had grown to 668.7 MB — 61% of the package — which pushed the upload past the registry limit and made `npm publish` fail with `413 Payload Too Large`. ([#11864](https://github.com/diegosouzapw/OmniRoute/pull/11864)) +- **fix(cli):** keep the global OmniRoute server `--base-url` separate from provider-node `--endpoint` values in add, update, and validate commands ([#11860](https://github.com/diegosouzapw/OmniRoute/pull/11860)) — thanks @pacocartones +- **fix(config):** Nous Research's `Hermes-4-405B` model now displays as "Hermes 4 405B (Nous Research)" in both the provider registry and the free-model catalog, instead of the mislabelled "Hermes 4 7B" ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#11993](https://github.com/diegosouzapw/OmniRoute/pull/11993)) +- **fix(provider/nous):** inject required user= tag into Nous Research inference requests to resolve upstream 400 "missing tags" error ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#12044](https://github.com/diegosouzapw/OmniRoute/pull/12044)) +- **fix(build):** `npm run build` now fails in one second with a named package and a copy-pasteable fix when npm silently drops an externalised optional native dependency, instead of dying four minutes in with `Module not found: Can't resolve 'better-sqlite3'` ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- **fix(install):** `postinstall` no longer throws `ReferenceError: isAndroid is not defined` — failing the whole `npm install` — when the `better-sqlite3` rebuild fallback times out; the manual-fix guidance is reachable again ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- Fixed the Alibaba free-tier allowlist test that went red on its own once the shipped catalog's `validUntil` (2026-08-27) passed, leaving every PR and `main` with a failing `Unit Tests (1/8)`. The test now builds its own packs with dates it controls, and covers the expired-pack fallback that production has actually been serving. ([#11867](https://github.com/diegosouzapw/OmniRoute/pull/11867)) +- Split the npm registry upload into its own GitHub-hosted job. npm refuses `--provenance` from a self-hosted runner (`422 ... Only "github-hosted" runners are supported`), which blocked the v3.8.50 publish; the heavy verification cannot move to a hosted runner, so it now hands the proven tarball over instead. ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(ui):** the shared `DataTable` loading state no longer reads its decorative ⏳ glyph out to assistive technology, and now carries the same `role="status"` / `aria-live="polite"` / `aria-busy="true"` semantics as `PageLoading` ([#11869](https://github.com/diegosouzapw/OmniRoute/pull/11869)) — thanks @pacocartones +- **fix(api):** Bound the `timeout` accepted by `POST /api/providers/[id]/login` to the same 15s–600s window the Adobe Firefly and Conol browser-login paths already enforce, so a request can no longer pin a headful browser and the single active-login slot with an arbitrarily large poll budget. ([#11871](https://github.com/diegosouzapw/OmniRoute/pull/11871)) — thanks @pacocartones +- **fix(api):** Stop the tunnel and MITM routes from returning raw child-process error text, which disclosed host paths, binary install locations and Tailscale `tskey-*` credentials in a body some of these routes serve to non-loopback callers, and return a real 400 instead of a framework 500 when a tunnel request body fails validation. ([#11872](https://github.com/diegosouzapw/OmniRoute/pull/11872)) — thanks @pacocartones +- **fix(open-sse):** stream TTFT/ITL are sampled from a monotonic clock (`performance.now`) instead of `Date.now`, so an NTP correction or wall-clock jump can no longer inflate a genuine TTFT or produce a negative one that the `ttft >= 0` guard silently drops — protecting the router's OTel/EWMA/`usage_history`/speed-ranking signals ([#11873](https://github.com/diegosouzapw/OmniRoute/pull/11873)) — thanks @pacocartones +- **fix(providers):** xAI `reasoning_effort: "xhigh"` now reaches grok-4.6+ instead of being silently clamped to `"high"` ([#11879](https://github.com/diegosouzapw/OmniRoute/pull/11879)) — thanks @NoxzRCW +- **fix(dashboard):** the providers page no longer crashes into the error boundary when a provider id collides with an `Object.prototype` member (`constructor`, `__proto__`); icon lookups are own-property guarded ([#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)) — thanks @NoxzRCW +- **fix(skills):** injected skill tools declared in shorthand (`{"content": "string"}`) now forward valid JSON Schema, unblocking providers that validate tool schemas strictly such as Zhipu GLM on the Console Go tier ([#11881](https://github.com/diegosouzapw/OmniRoute/pull/11881)) — thanks @NoxzRCW +- **fix(api):** `POST /api/playground/simulate-route` now surfaces `combo-ref` and `provider-wildcard` persisted combo steps with a specific warning (naming the referenced combo, or the unresolved `provider/model` wildcard) instead of folding them into a generic "unsupported step" count; a `provider-wildcard` step is also now included as an unresolved target so the operator can see it is in the route (ported from [#11882](https://github.com/diegosouzapw/OmniRoute/pull/11882) — thanks @NoxzRCW). ([#11930](https://github.com/diegosouzapw/OmniRoute/pull/11930)) +- **fix(translator):** the streaming OpenAI→Claude translator keeps upstream usage, including prompt-cache tokens, when it arrives on a trailing `choices: []` chunk (Fireworks and any upstream using `stream_options.include_usage`) ([#11883](https://github.com/diegosouzapw/OmniRoute/pull/11883)) — thanks @NoxzRCW +- **fix(cli):** `omniroute update --apply` now tells you explicitly whether a running server was detected and, if so, that you must run `omniroute restart` to apply the update — it never restarted anything and previously implied the update was already live once files were installed. The dashboard's npm-mode Update flow (`/api/system/version`) now tries OmniRoute's own PID-file-managed supervisor before falling back to pm2, and reports an honest "restart required" step instead of a silent pm2-only "skipped" that read like a completed update. The server-side latest-version lookup backing the dashboard's update banner also gained `--prefer-online`, closing the same stale-npm-cache class already fixed in the CLI's own copy for #4376 ([#11885](https://github.com/diegosouzapw/OmniRoute/issues/11885)). ([#12005](https://github.com/diegosouzapw/OmniRoute/pull/12005)) +- **fix(resilience):** clear persisted LKGP pins when a target suffers connection/provider exhaustion or is skipped before dispatch due to cooldown/exhaustion/unavailability, preventing subsequent requests from repeatedly prioritizing known-dead providers ([#11911](https://github.com/diegosouzapw/OmniRoute/issues/11911)). ([#12013](https://github.com/diegosouzapw/OmniRoute/pull/12013)) — thanks @HouMinXi +- **fix(sse):** the OpenAI→Claude stream translator now defers the terminal `message_delta`/`message_stop` emission until the real usage block has arrived (or a genuine end-of-stream flush forces it) instead of emitting it immediately on `finish_reason` — previously, when the trailing usage-only chunk (`{"choices":[],"usage":{...}}`) arrived *after* the `finish_reason` chunk (the normal order for Fireworks/vLLM/Together and other `stream_options.include_usage` upstreams), the client-visible `message_delta` still carried stale/zero usage even though `state.usage` was internally corrected too late to matter (ported from [#11915](https://github.com/diegosouzapw/OmniRoute/pull/11915) — thanks @HouMinXi). ([#11933](https://github.com/diegosouzapw/OmniRoute/pull/11933)) +- **fix(sse):** `stripStore()` now forces `store=false` for stateless OpenAI-compatible Responses-API targets (unless the connection has explicitly opted in via `providerSpecificData.openaiStoreEnabled`), instead of only handling the `openai`/`agentrouter` cases — a client-supplied `store` value previously passed through untouched to backends that don't actually persist responses server-side ([#11916](https://github.com/diegosouzapw/OmniRoute/pull/11916) — thanks @HouMinXi). +- **fix(models):** custom provider-node models (synced, custom, and alias-backed) now appear under their configured prefix in the unified catalog when the operator's model-id prefix mode is canonical, instead of being dropped whenever alias-inclusion was otherwise disabled ([#11918](https://github.com/diegosouzapw/OmniRoute/pull/11918) — thanks @HouMinXi). +- **fix(models):** the unified model catalog now suppresses stale static registry models (including effort-tier variants) for any provider whose active connection has an authoritative live synced catalog, not only providers already using exclusive-synced-listing — a connection with `providerUsesAuthoritativeLiveCatalog` previously kept serving both the live-synced models and the stale static rows side by side ([#11919](https://github.com/diegosouzapw/OmniRoute/pull/11919) — thanks @HouMinXi). +- **fix(ui):** `ProviderIcon`'s three remaining unguarded lookups (`PROVIDER_ICON_ALIASES`, `LOCAL_SVG_ALIASES`, `THEMED_SVGS`) now use `Object.hasOwn()` own-property checks — a provider id such as `constructor` or `__proto__` previously resolved through the prototype chain instead of falling through to the unknown-provider CDN fallback (`getLobeProviderIcon()` itself was already guarded by [#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)); `ProviderPageHeader`'s `color` field is also now optional, matching the rest of the component's defensive typing (ported from [#11920](https://github.com/diegosouzapw/OmniRoute/pull/11920) — thanks @HouMinXi). ([#11935](https://github.com/diegosouzapw/OmniRoute/pull/11935)) — thanks @NoxzRCW +- fix(ollama): preserve multi-byte UTF-8 content split across stream chunks in the Ollama NDJSON transform, which previously corrupted CJK/emoji into U+FFFD (#11921) — thanks @pacocartones +- **fix(providers):** OrcaRouter chat requests now target `/v1/chat/completions` instead of the bare `/v1` API root, fixing the upstream `404 Invalid URL (POST /v1)` ([#11923](https://github.com/diegosouzapw/OmniRoute/pull/11923)). — thanks @echel0nn +- **fix(plugins):** deliver the `onStreamComplete` event to disk-installed plugins. The event shipped in v3.8.50 (#9669) was emitted internally but had no plugin-facing wiring, so no plugin could ever subscribe: the manifest schema silently dropped `hooks.onStreamComplete`, and the loader/manager only knew the seven legacy hooks. `onStreamComplete` is now a declarable manifest hook, wired through the loader and registered by the manager like the other hooks, and its payload carries a `requestId` so consumers can correlate the stream-completion event with the originating request ([#11934](https://github.com/diegosouzapw/OmniRoute/pull/11934)) — thanks @amaleta / @pacocartones +- **fix(db):** the Qdrant embedding-model dropdown now lists local/self-hosted providers (Ollama, LM Studio, vLLM, etc.) — an active connection is treated as "configured" when the provider allows an optional API key, not only when it has a real key or OAuth, so a running local embedding provider is no longer hidden from the picker ([#11949](https://github.com/diegosouzapw/OmniRoute/issues/11949)) ([#11995](https://github.com/diegosouzapw/OmniRoute/pull/11995)) +- **fix(providers):** Vertex AI Anthropic partner-model discovery now calls the Model Garden `v1beta1` publisher list (`/v1beta1/publishers/anthropic/models`, global) and parses its `publisherModels` envelope, so Claude models auto-synced from Vertex populate the active live catalog and route at request time instead of returning `Model '' is not available in the active live catalog` ([#11991](https://github.com/diegosouzapw/OmniRoute/issues/11991)) — thanks @fabioluissilva ([#11998](https://github.com/diegosouzapw/OmniRoute/pull/11998)) +- **fix(cli):** support `--base-url` alongside `--endpoint` in `omniroute nodes add`, `update`, and `validate` subcommands to prevent global `--base-url` shadowing issues ([#11999](https://github.com/diegosouzapw/OmniRoute/issues/11999)). ([#12033](https://github.com/diegosouzapw/OmniRoute/pull/12033)) — thanks @HouMinXi +- **fix(providers):** `cloudflare-ai` no longer refuses image content parts for every Workers AI model ([#12002](https://github.com/diegosouzapw/OmniRoute/pull/12002)) — the plain-string `content` requirement behind #2539 is carried by the _model_ schema, not by the `/ai/v1/chat/completions` endpoint (measured: an all-text part array returns 200 on `@cf/mistralai/mistral-small-3.1-24b-instruct`, `@cf/meta/llama-4-scout-17b-16e-instruct` and `@cf/meta/llama-3.3-70b-instruct-fp8-fast`, and 400 on the text-only `@cf/qwen/qwen2.5-coder-32b-instruct`). `transformRequest()` flattened every array and threw on the first non-text part (#6390), so image input was refused for vision-capable Cloudflare models that accept it. All-text arrays are still flattened — the one shape every model accepts — while an array carrying a non-text part is passed through untouched, so the attachment is still never silently dropped. Regression guards: `tests/unit/cloudflare-ai-image-parts-6390.test.ts`. — thanks @davidlinfr +- **fix(resilience):** decouple the limiter-managed execution backstop from the queue-wait budget — new `requestQueue.executionMaxWaitMs` (env `RATE_LIMIT_EXECUTION_MAX_WAIT_MS`, default 600000 = 10 min) now feeds Bottleneck's post-dispatch `expiration`, while `requestQueue.maxWaitMs` keeps its documented queue-wait semantics. Previously the queue-wait budget doubled as the execution expiration, so legitimate long-running calls on non-incremental gateways (whole generation buffered before the first upstream byte, e.g. Console Go / Command Code tiers serving GLM models) were killed mid-flight at the queue budget with a false 504 `RATE_LIMIT_EXECUTION_TIMEOUT` — the local limiter undercut the provider-aware upstream fetch-start timeouts. The surfaced 504 message now names `requestQueue.executionMaxWaitMs`; the error keeps the #4165 guarantees (disclaims an upstream timeout, preserves the Bottleneck error as `cause`, branded code + trusted provenance, classified request-scoped so combo falls back). A real queue-wait bound (the `Promise.race` around `limiter.schedule()` sketched in #9533) remains future work. (#12025) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **Call logs:** keep the `error` field when an artifact exceeds the storage cap, instead of replacing it with the omission marker. The error is the only field that says *why* a request failed and is typically ~90 bytes next to the multi-hundred-KB bodies that trip the cap, so dropping it left a size-limited row undiagnosable — a provider outage, a local timeout and an upstream 400 all rendered identically. It is now preserved at every fallback stage, truncated to 4KB if it is itself large ([#12026](https://github.com/diegosouzapw/OmniRoute/issues/12026)). ([#12095](https://github.com/diegosouzapw/OmniRoute/pull/12095)) — thanks @ntdat812 +- **fix(diagnostics):** preserve the error field (truncated to 4KB with a `[truncated: …]` suffix) in every call-log artifact size-limit fallback stage. Previously the minimal fallback replaced the error with `[omitted: call log artifact size limit exceeded]`, so an oversized artifact row showed nothing about WHY the request failed — e.g. 91 of 847 opencode-go 504 rows on one production instance were undiagnosable from the dashboard. Oversized request/response bodies are still omitted exactly as before; the error cap is independent of the payload sizes that tripped the fallback. (#12026) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **fix(sse):** OpenAI Responses clients that declare the native `web_search` tool now receive a spec-shaped `web_search_call` output item with `action.sources` alongside the preserved function-call round-trip, so search results executed through OmniRoute's own search backend are consumable by standard Responses clients (Codex, pi-web-access, …). ([#12031](https://github.com/diegosouzapw/OmniRoute/pull/12031)) — thanks @watchingdogs +- **fix(cli):** use in-thread alias resolver hooks on modern runtimes to avoid deprecation noise and improve Node.js forward compatibility ([#12073](https://github.com/diegosouzapw/OmniRoute/issues/12073)). ([#12083](https://github.com/diegosouzapw/OmniRoute/pull/12083)) — thanks @xiaoyaner0201 +- **fix(combo):** an operator-set **Agent Features → Context length** on a combo is now honored at request time. The value was persisted and advertised through `/v1/models`, but `resolveComboContextLimit()` never consulted it — so a multi-target combo whose members carry no per-model window fell through to the provider's generic `defaultContextLength` (openrouter 128000, command-code 200000) and rejected large requests with `Input exceeds context window … limit 128000` despite the combo being explicitly sized much larger. An identical single-target combo worked, because it collapses to its concrete target before the guard runs. Invalid values (0/negative/NaN/Infinity) are ignored, so the existing target → combo-min → fallback order is unchanged. ([#12090](https://github.com/diegosouzapw/OmniRoute/pull/12090)) — thanks @adivekar-utexas +- **fix(sse):** passthrough streams now estimate usage on finish when upstream closes without usage even with `stream_options.include_usage` — avoids `0 tokens / 0%` for providers that stay silent (and correctly handles trailing empty-choices usage) ([#12151](https://github.com/diegosouzapw/OmniRoute/pull/12151)) — thanks @maxmad64bis +- **fix(combos):** clearing an agent feature in the combos editor now persists — unchecking context cache protection, or emptying the system message or tool filter, sends an explicit `null` instead of dropping the field from the `PUT` body, which the update merge read as "leave unchanged" ([#12177](https://github.com/diegosouzapw/OmniRoute/pull/12177)) — thanks @foreveryh / @ntdatt812 +- **fix(memory):** self-hosted embedding endpoints now vectorize — the vector width is measured from the first embedding that comes back instead of being read from a registry that cannot describe them, so `vec_memories` is created and memories stop piling up unvectorized behind a green health check ([#12180](https://github.com/diegosouzapw/OmniRoute/pull/12180)) — thanks @kanade-hoshino / @ntdatt812 +- **fix(translator):** the leading `system` message now reaches Responses-API upstreams when its `content` is a content-part array — it was read as `typeof content === "string" ? content : ""`, so a prompt-caching client (Anthropic `cache_control`, the shape LiteLLM and the Anthropic SDK emit) had its entire system prompt replaced by an empty `instructions`. The request was still accepted with a normal `prompt_tokens` count, so the model answered with no instructions and nothing in the response said they were missing. Mid-conversation system turns already handled the array shape ([#7056](https://github.com/diegosouzapw/OmniRoute/pull/7056)); only the first one did not ([#12206](https://github.com/diegosouzapw/OmniRoute/issues/12206)). Regression guard: `tests/unit/translator-openai-responses-system-content-parts.test.ts`. ([#12207](https://github.com/diegosouzapw/OmniRoute/pull/12207)) — thanks @Gorillaz322 +- **fix(free-tier):** `/api/free-tier/summary` no longer computes its totals from a Radar feed built before the catalog the running release ships. When the cached feed is older — or carries no build date at all — the route answers from the shipped catalog, resolved through the operator's local model state so disabled and tombstoned models stay out of the numbers ([#12215](https://github.com/diegosouzapw/OmniRoute/pull/12215)). — thanks @maxmad64bis +- **fix(oauth):** Keep a Claude personal workspace and a Team organization as separate connections — they share the same email and `accountUUID`, so the email-only OAuth dedup let the second login overwrite the first account's tokens; `organizationUUID` now disambiguates them, the way `workspaceId` does for Codex ([#12222](https://github.com/diegosouzapw/OmniRoute/pull/12222)) — thanks @davidebaraldo +- **fix(resilience):** a 402 on a single paid model of a passthrough/gateway provider (e.g. `kilo-gateway`, `ollama-cloud`) no longer terminalizes the whole connection with a never-auto-recovered `credits_exhausted` status — only the paid model is locked out, so free models on the same key keep serving. 402 variant of [#3027](https://github.com/diegosouzapw/OmniRoute/issues/3027). Single-credential providers are unaffected — a 402 there is still treated as the key being genuinely out of credit ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239) / [#10616](https://github.com/diegosouzapw/OmniRoute/issues/10616)) ([#12242](https://github.com/diegosouzapw/OmniRoute/issues/12242)) — thanks @brick30llc-ctrl ([#12266](https://github.com/diegosouzapw/OmniRoute/pull/12266)) — thanks @geekyNads +- **fix(sse):** trust `finish_reason: "length"`/`"max_tokens"` over the reasoning-consumed-token ratio in response quality validation, so a reasoning model truncated below the old 90% threshold correctly fails and retries instead of returning empty content as a silent "success" ([#12262](https://github.com/diegosouzapw/OmniRoute/pull/12262)) — thanks @brick30llc-ctrl +- **fix(settings):** `PUT /api/settings/cache-config` now persists `alwaysPreserveClientCache` to the flat general settings the runtime cache-control policy actually reads; previously the value landed in the databaseSettings "cache" section and was silently ignored, so the endpoint had no effect on `cache_control` passthrough ([#12304](https://github.com/diegosouzapw/OmniRoute/pull/12304)) — thanks @davidebaraldo +- **fix(grok-cli):** treat omitted SuperGrokPro `creditUsagePercent` as 0% used so Provider Limits still renders a weekly bar (proto3 zero-elision) ([#12312](https://github.com/diegosouzapw/OmniRoute/pull/12312)) — thanks @HouMinXi +- **fix(combo):** Expose the two Auto-Combo scoring factors nobody could set — the combo validation schema and the dashboard weight sliders both declared 13 of the scorer's 15 factors, so `connectionDensity` (spreads load across a provider's connections) and `quality` were dropped on save and offered nowhere. The sliders also shipped their own default table that differed from the engine's on every non-zero factor and summed to 1.05, so the percentages shown next to them added up to 105%. Both lists now match `DEFAULT_WEIGHTS`, and a test keeps them there. Note that a combo whose stored `weights` omitted the two keys was effectively running with them at zero and the other thirteen renormalized upward; it now runs with the engine's intended distribution, so its routing does shift ([#12314](https://github.com/diegosouzapw/OmniRoute/pull/12314)) — thanks @maxmad64bis +- **fix(docs):** The free-tier reference no longer says its numbers come "confidence tagged per row" — no catalog entry carries a confidence tag and the API serves none, so every figure on that page is an estimate of the same, unstated quality. The page now states what an entry does vouch for: an independently documented hard stop (set by hand with the source in a comment, never defaulted to `true`) and a prompt-training disclosure, both with live counts the `check:docs-counts` gate keeps honest ([#12318](https://github.com/diegosouzapw/OmniRoute/pull/12318)) — thanks @maxmad64bis +- **fix(usage):** `adobe-firefly` and `firefly` have had a working usage fetcher since Adobe Firefly landed, but neither was ever added to the registration list, so the provider-plugin manifest, `genericQuotaFetcher` and the free-access quota cache all reported them as having no usage support — while `USAGE_SUPPORTED_PROVIDERS` said the opposite. Both are now declared, which also means their credit balance is fetched like any other declared provider's: `registerGenericQuotaFetchers` now registers a generic quota fetcher for them, and `resolveFreeAccessState` no longer returns early. A test holds the registration list to the dispatcher's switch in both directions, which is what the module's own docstring already asked for in prose ([#12321](https://github.com/diegosouzapw/OmniRoute/pull/12321)) — thanks @maxmad64bis +- **fix(quota):** drop the generic quota cache (agy / Antigravity / Claude OAuth) on an upstream 429 so reset-aware scoring does not keep a 60s stale snapshot, and force-refresh the next usage fetch so inner provider caches cannot recache the same window ([#12325](https://github.com/diegosouzapw/OmniRoute/pull/12325)) — thanks @HouMinXi +- **fix(combos):** deleting a combo now clears its persisted LKGP pins instead of leaving unreachable `key_value` rows behind ([#12326](https://github.com/diegosouzapw/OmniRoute/issues/12326)) ([#12425](https://github.com/diegosouzapw/OmniRoute/pull/12425)) — thanks @KrzysiekSko +- **fix(sse):** Keep ZWNJ (U+200C) and ZWJ (U+200D) in assistant text, reasoning and tool-call arguments — Persian/Kurdish half-space (`ارائه‌دهنده`), Arabic/Indic shaping and emoji sequences no longer lose them; the response de-obfuscation now removes joiners only between ASCII word characters, where the request side inserts them ([#12186](https://github.com/diegosouzapw/OmniRoute/issues/12186)) — thanks @rezjalibd ([#12359](https://github.com/diegosouzapw/OmniRoute/pull/12359)) — thanks @pacocartones +- **fix(resilience):** count resolved upstream 5xx results against the provider circuit breaker on the chat path — `CircuitBreaker.execute()` no longer reads a resolved `{ success: false, status: 5xx }` as a success that cancels the call-site failure, so a provider answering 503s now trips its breaker instead of staying `CLOSED` at `failureCount: 1`; single-model and combo dispatches are each accounted exactly once ([#12254](https://github.com/diegosouzapw/OmniRoute/issues/12254)) ([#12360](https://github.com/diegosouzapw/OmniRoute/pull/12360)) — thanks @pacocartones +- **fix(providers):** resolve the Codex quota auto-ping model from the live provider catalog and lifecycle registry instead of the retired `gpt-5.1-codex-mini`, and pause the ping with one actionable warning when no selectable Codex model exists rather than retrying a shut-down id every cooldown window ([#11905](https://github.com/diegosouzapw/OmniRoute/issues/11905)) ([#12361](https://github.com/diegosouzapw/OmniRoute/pull/12361)) — thanks @pacocartones +- **fix(api):** keep the `{created, data}` wrapper on combo-routed `/v1/images/generations` responses and default Codex image results to `b64_json` on both `/v1/images/generations` and `/v1/images/edits` so Codex CLI's built-in `image_gen` can decode them ([#12268](https://github.com/diegosouzapw/OmniRoute/issues/12268)) ([#12362](https://github.com/diegosouzapw/OmniRoute/pull/12362)) — thanks @pacocartones +- **fix(sse):** Name the shadowed custom provider node when a built-in provider id/alias (e.g. `openference` → `of`) reserves the prefix of an existing OpenAI/Anthropic-compatible node, so the runtime `No active credentials for provider: ` error explains that the prefix routed to the built-in and never reached the node's healthy connections, instead of contradicting the dashboard ([#11943](https://github.com/diegosouzapw/OmniRoute/issues/11943)) — thanks @morpheus9393 ([#12365](https://github.com/diegosouzapw/OmniRoute/pull/12365)) — thanks @pacocartones +- **fix(i18n):** wrap `ccOnboardingKeyPlaceholder` in ICU single quotes across all 43 locales so angle brackets render literally instead of being parsed as rich-text tags, which crashed the Claude Code onboarding block with `INVALID_MESSAGE: INVALID_TAG` ([#12302](https://github.com/diegosouzapw/OmniRoute/issues/12302)) ([#12369](https://github.com/diegosouzapw/OmniRoute/pull/12369)) — thanks @gonisulaimann +- **fix(models):** publish `effort_tiers` on Kimi K3's synced base-model entries (`kmca/k3`, `kmca/k3-256k`) so catalog-only clients (OpenCode, plain SDK pickers) can see and select the reasoning tiers (`low`/`high`/`max`) the synced metadata already carried — the `isSkippedEffortProvider` gate no longer suppresses tier visibility on those base entries, while synthetic `-` variant generation stays prevented and Codex/GLM base models remain excluded unchanged ([#12299](https://github.com/diegosouzapw/OmniRoute/issues/12299)) ([#12371](https://github.com/diegosouzapw/OmniRoute/pull/12371)) — thanks @gonisulaimann +- **fix(guardrails):** keep `auto`/`auto/*` virtual combos exempt from the Vision Bridge `fixedModel` credential guard so a combo target is passed through instead of silently falling back to global auto-selection ([#12237](https://github.com/diegosouzapw/OmniRoute/issues/12237)) ([#12373](https://github.com/diegosouzapw/OmniRoute/pull/12373)) — thanks @pacocartones +- **fix(combo):** capability-filter exhaustion caused by `max_tokens` above every target's known output limit now reports that reason (requested `max_tokens` vs the pool's highest known ceiling) instead of the unrelated "supports structured output" message ([#12229](https://github.com/diegosouzapw/OmniRoute/issues/12229)) — thanks @DW-MediaLab ([#12374](https://github.com/diegosouzapw/OmniRoute/pull/12374)) — thanks @pacocartones +- **fix(auth):** the `least-used` account strategy now prefers accounts without backoff before falling back to oldest `lastUsedAt`, the same tie-break `round-robin` already applies, so a failover no longer lands on a just-rate-limited account for a single request ([#12279](https://github.com/diegosouzapw/OmniRoute/issues/12279)) — thanks @tenshiak ([#12375](https://github.com/diegosouzapw/OmniRoute/pull/12375)) — thanks @pacocartones +- **fix(docker):** the `chatgpt-web-codex-browser` image now finds the Chrome binary under `chrome-linux64/` (Chrome for Testing layout in `playwright:v1.62.0-noble`) as well as the legacy `chrome-linux/`, so the container no longer crash-loops with `exec: --headless=new: not found` ([#12024](https://github.com/diegosouzapw/OmniRoute/issues/12024)) ([#12376](https://github.com/diegosouzapw/OmniRoute/pull/12376)) — thanks @pacocartones +- **fix(providers):** declare `groq/compound` and `allam-2-7b` as non-reasoning models in the curated Groq registry so `reasoning_effort` / `output_config.effort` / `thinking` from Claude Code are stripped instead of forwarded, which Groq rejected with HTTP 400 ([#12134](https://github.com/diegosouzapw/OmniRoute/issues/12134)) ([#12379](https://github.com/diegosouzapw/OmniRoute/pull/12379)) — thanks @pacocartones +- **fix(executors):** `OpencodeExecutor` no longer forces a direct connection when the connection has a proxy assigned in Proxy Management but no per-account proxies: the single-account fast path used to wrap the upstream dispatch in the direct-egress sentinel, discarding the ambient proxy context the chat handler had pinned from `proxy_assignments`, so API-key `opencode`/`opencode-go` connections egressed from the host IP (and hit geoblocks) despite the assignment. The direct pin is now applied only when no ambient proxy context exists ([#11894](https://github.com/diegosouzapw/OmniRoute/issues/11894) — thanks @hizzt) ([#12380](https://github.com/diegosouzapw/OmniRoute/pull/12380)) — thanks @pacocartones +- **fix(api):** `GET /v1/models` with `MODELS_CATALOG_PREFIX_MODE=canonical` (or `?prefix=canonical`) now lists providers whose registry alias is undefined or equal to their own id (Antigravity, Antigravity CLI and other self-aliased built-ins) — their single `provider/model` id was dropped by the alias/canonical duplicate guard in the static, synced, custom and alias-backed catalog loops ([#12058](https://github.com/diegosouzapw/OmniRoute/issues/12058)) — thanks @cheynetom ([#12381](https://github.com/diegosouzapw/OmniRoute/pull/12381)) — thanks @pacocartones +- **fix(translator):** Drop replayed `thinking` blocks that carry no signature (the shape produced from cross-provider `reasoning_content`) instead of stamping the default Claude signature on them, which Anthropic rejected with `400 Invalid signature in thinking block` on the next turn served by an Anthropic rung ([#12105](https://github.com/diegosouzapw/OmniRoute/issues/12105)) — thanks @atescivitci-cmd ([#12386](https://github.com/diegosouzapw/OmniRoute/pull/12386)) — thanks @pacocartones +- **fix(cli):** Resolve Bun's `--preload` polyfill path against the package root instead of `dist/`, so `omniroute` installed with `bun install -g` no longer crashes at startup with `error: preload not found …/dist/open-sse/utils/setupPolyfill.ts` ([#11980](https://github.com/diegosouzapw/OmniRoute/issues/11980)) — thanks @joglomedia ([#12387](https://github.com/diegosouzapw/OmniRoute/pull/12387)) — thanks @pacocartones +- **fix(providers):** `gemini-business` now publishes its model catalog — `/v1/models` and `/v1/providers/gemini-business/models` list the 12 enterprise Gemini ids the executor understands instead of returning an empty list (#12107) ([#12389](https://github.com/diegosouzapw/OmniRoute/pull/12389)) — thanks @pacocartones +- **fix(db):** install `busy_timeout` before the SQLite connection's first statement so a process opening the database while another one closes its WAL connection waits out the transient EXCLUSIVE lock instead of dying with `database is locked`, and recognise the drivers' real BUSY/PROTOCOL/IOERR errors as transient in the corruption probe so the same lock no longer renames the database away as corrupt; deflakes `cross-process contenders never both acquire the same connection` (#12394 — thanks @pacocartones) +- **fix(chat-admission):** derive the `chat_admission_busy` 503 `Retry-After` from observed heavyweight-lease occupancy — the larger of the exhausted `OMNIROUTE_CHAT_ADMISSION_QUEUE_MS` window and the time since capacity last turned over, capped at 60 s — instead of a fixed 1 s (structural) / 2 s (byte-stage) hint that invited Codex/agent fan-out clients to re-send ~1 MiB `/v1/responses` bodies every second into a gate held for the whole SSE lifetime; an idle gate keeps the historical floors ([#12135](https://github.com/diegosouzapw/OmniRoute/issues/12135)) (#12395 — thanks @pacocartones) +- **fix(api-manager):** the API key permissions modal no longer silently drops `allowedCombos` entries its Combo picker cannot render — routing-rule names such as `rt-*`, which the backend already honours — when "All" is clicked and the key is switched back to "Restrict"; those entries now survive the toggle, are listed read-only under the combo list so the count and the list agree, and are saved back verbatim instead of persisting `[]` (deny-all) (#12397 — thanks @pacocartones) +- **fix(catalog):** write the NUL separator of the catalog connection memo key, the provider serviceKind memo key, the Video Bridge promotion group key and a JSON-exactness test fixture as the `\u0000` escape instead of a raw byte — same runtime value, but the raw byte made git, GitHub and ripgrep treat those files as binary (hidden PR diffs, silently skipped searches); a guard test now keeps raw NUL bytes out of `src/`, `open-sse/` and `tests/` (#12403 — thanks @pacocartones) +- **fix(providers):** add `CLAUDE_CODE_CLIENT_VERSION` and `GITHUB_COPILOT_CLI_VERSION` env overrides so Anthropic/Copilot client-version gates can be unblocked without a rebuild ([#12417](https://github.com/diegosouzapw/OmniRoute/issues/12417)) ([#12632](https://github.com/diegosouzapw/OmniRoute/pull/12632)) — thanks @HouMinXi +- **fix(db):** back-fill `last_ping_at` and `last_pinged_reset_key` on `provider_connections` during schema reconciliation so divergent lineages that skipped `123_quota_auto_ping` still accept quota auto-ping writes ([#12470](https://github.com/diegosouzapw/OmniRoute/pull/12470) — thanks @KooshaPari) +- **fix(ci):** document MIT exceptions for `@eloqnt/{config,format-json,format-po}` (next-intl transitive; locked tarballs omit `license`) and keep the A2A lifecycle vitest off the real SQLite persistence seam ([#12581](https://github.com/diegosouzapw/OmniRoute/issues/12581)) ([#12595](https://github.com/diegosouzapw/OmniRoute/pull/12595)) +- OpenCode plugin `/v1/models` catalog fetch now waits 30s by default and attaches HTTP `statusCode` on 401/5xx so host fallback plugins can hop instead of seeing an untyped AbortError/UnknownError. ([#12607](https://github.com/diegosouzapw/OmniRoute/pull/12607)) — thanks @RaviTharuma +- **CI:** the OpenAPI security-tier gate now mirrors `isAlwaysProtectedPath()` in full — it also reads `ALWAYS_PROTECTED_API_PATTERNS`, so the pattern-gated credential routes (`/api/providers/{id}/{claude,codex}-auth/{export,apply-local}`, GHSA-5926-2w35-7h4q) no longer report as unannotated. (#12605) +- **fix(api):** GET /v1/models no longer waits forever on a hung coalesced catalog rebuild; cold-path waits are bounded (`CATALOG_BUILD_TIMEOUT_MS`, default 8s) and a last-good 200 is served when the rebuild times out ([#12627](https://github.com/diegosouzapw/OmniRoute/issues/12627)). ([#12628](https://github.com/diegosouzapw/OmniRoute/pull/12628)) — thanks @RaviTharuma +- **fix(cursor):** a non-array `tool_calls` on an assistant message no longer crashes the cursor request translator with a `TypeError`; both loops now require an array ([#12689](https://github.com/diegosouzapw/OmniRoute/issues/12689)) ([#12691](https://github.com/diegosouzapw/OmniRoute/pull/12691)) — thanks @soroush5 +- **fix(chat):** Chat Completions no longer return empty `content` after a server-owned memory or skills tool; the first provider send and account/model recovery now share one pipeline so a follow-up round-trip can fill the reply ([#12696](https://github.com/diegosouzapw/OmniRoute/issues/12696)) — thanks @HouMinXi ([#12867](https://github.com/diegosouzapw/OmniRoute/pull/12867)) +- **fix(dashboard):** batch-deleting provider keys no longer toasts failure after a successful delete when the confirm button's click event is forwarded as `onAfter` ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) — thanks @HouMinXi +- **fix(glm):** drop the extra 16th argument to `createSSETransformStreamWithLogger` that TypeScript rejected (TS2554) and that never reached the TransformStream ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) ([#12770](https://github.com/diegosouzapw/OmniRoute/pull/12770)) — thanks @HouMinXi +- **fix(dashboard):** pass `nodeMap` into Runtime `QuotaGroup` so a quota monitor in error/exhausted/alerting no longer throws `ReferenceError: nodeMap is not defined`. ([#12868](https://github.com/diegosouzapw/OmniRoute/pull/12868)) — thanks @HouMinXi +- Restricted API keys whose `allowedModels` lists a combo name no longer skip every combo member at pre-dispatch (`ALL_TARGETS_SKIPPED`). Inner-target filtering still applies when the allow-list is a provider prefix or `disableNonPublicModels` is on ([#12899](https://github.com/diegosouzapw/OmniRoute/pull/12899)). — thanks @HouMinXi +- **fix(combos):** Effort-suffixed combo members inherit the base model's `model_context_overrides` row so priority order is not inverted on large requests ([#12926](https://github.com/diegosouzapw/OmniRoute/pull/12926)) — thanks @HouMinXi +- **fix(catalog):** Picker-added `customModels` enter the dispatch-time live catalog so combo members and bare inference no longer 400 ([#12934](https://github.com/diegosouzapw/OmniRoute/pull/12934)) — thanks @HouMinXi +- **fix(docker):** re-derive the Docker build's worker-pool memory budget from the MEASURED ~4.5 GB per-process RSS (the issue owner's own VPS dmesg OOM-killer reproduction) instead of the stale 2560 MB/worker inference #11419 shipped, and lower `OMNIROUTE_BUILD_WORKERS` 3 → 2 so 1 parent + 1 page-data worker (2 processes × 4.5 GB = 9 GB) fits the 12.288 GB (75%) budget on a 16 GB GitHub Actions runner — the previous default (1 parent + 2 workers = 13.5 GB) still overcommitted the runner and kept "Publish to Docker Hub" failing with `cannot allocate memory` after #11419 merged (#7518). ([#11663](https://github.com/diegosouzapw/OmniRoute/pull/11663)) +- **fix(electron):** the packaged Windows desktop build now passes the #7592 cold-restart smoke end to end. Five defects were found and fixed on the way: (1) optional-pack staging failed on any machine whose `tar` is GNU tar — it reads the drive letter in an absolute `-f C:\...` archive path as a remote rsh host (`Cannot connect to C:`), so staging now passes a bare filename with `cwd` at the tarball directory and surfaces tar stderr on failure; (2) the Electron `files` allowlist was missing `lib/loginHeaderCapture.js`, so the packaged main process crashed at startup with `Cannot find module './lib/loginHeaderCapture'`; (3) electron-builder ≥26 injects an `!**/node_modules/**` ignore into every extraResources pattern list that no positive filter can override, which silently dropped the staged runtime `node_modules` (including the better-sqlite3 N-API prebuild) from `resources/app` — a fresh v3.8.50 desktop build would have shipped with no native SQLite driver and reproduced the #7592 sql.js fallback on every machine; an `afterPack` hook now restores the staged `node_modules` after packing; (4) the packaged-app smoke harness redirected `USERPROFILE` into its temp DATA_DIR, but Electron resolves the Roaming profile from `%USERPROFILE%\AppData\Roaming\` (USERPROFILE takes precedence over the APPDATA env var) and the path service throws instead of creating it, so `requestSingleInstanceLock()` returned false and the app exited(0) silently before `app.whenReady()` — the harness now pre-creates the derived tree, and `ensureSmokeEnvDirs` is exported and unit-tested; (5) the #7592 driver assertion parsed a `[DB] Driver: ...` line that the server's primary DB path never printed (only the unused `openDatabaseAsync()` did) — `getDbInstance()` now emits the same line on open so the guard can actually assert the native driver. Also: the smoke env-allowlist unit test hardcoded POSIX paths and could only pass on Linux/macOS; expectations are now host-agnostic, with new regression tests for the USERPROFILE-derived tree and `tarPack` under absolute Windows-style paths ([#11443](https://github.com/diegosouzapw/OmniRoute/pull/11443)) — thanks @jonlwheat2-gif +- fix(sse): stop the auto-combo candidates inspector from silently dropping model-locked/cooled-down rows (#9133) ([#11994](https://github.com/diegosouzapw/OmniRoute/pull/11994)) — thanks @hartmark +- **fix(providers):** Perplexity Web no longer turns upstream stream failures into successful assistant text; pre-content failures remain eligible for fallback, partial output ends with a structured sanitized error, and failed sessions are not persisted ([#12465](https://github.com/diegosouzapw/OmniRoute/pull/12465)) +- **fix(video-bridge):** preserve chronological video and transcript fusion, source-frame timestamps, transcript provenance, full contact-sheet intervals, and pre-abort callback isolation ([#11681](https://github.com/diegosouzapw/OmniRoute/pull/11681)) +- **Z.ai Web:** HTTP 200 streams carrying an upstream error now terminate with a structured failure instead of assistant text plus a normal stop, preserving partial output while allowing pre-content combo fallback. ([#12454](https://github.com/diegosouzapw/OmniRoute/pull/12454)) +- Absorb `Error [AbortError]: request_signal_aborted` and DOMException AbortError shapes in the process-level client-abort crash guard so routine client disconnects no longer kill the server (exit code 7). ([#12165](https://github.com/diegosouzapw/OmniRoute/pull/12165)) — thanks @alvinveroy +- **fix(sse):** Treat Adapta Web `type:error` SSE events as sanitized non-stream failures instead of empty HTTP 200 completions. ([#12459](https://github.com/diegosouzapw/OmniRoute/pull/12459)) +- **fix(autoCombo):** layer 3 (`models_dev_tier`) no longer scores retired models as premium, merge cross-provider capability rows deterministically, and inherit tier scores through variant ids ([#11508](https://github.com/diegosouzapw/OmniRoute/issues/11508)). `getModelsDevTierFitness` now vetoes ids present in `config/quality/model-lifecycle.json` with status `retired` before any other signal; `loadModelCapabilities()` aggregates per `model_id` (any-non-null-true for booleans, max for `limit_context`) instead of last-write-wins over SQLite's undefined row order; a capabilities miss retries through `resolveScoresAs()`'s base id and reports `models_dev_tier:inherited`, matching layers 1–2 (#11492). Adds `getModelsDevTierFitnessWithSource()` and an `invalidateCapabilitiesCache()` test/ops hook. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) — thanks @Meet6338-X +- fix(sse): accept Claude `content:[]` + `stop_reason: max_tokens`/`tool_use` in `detectMalformedNonStream` (match `isEmptyContentResponse`) so Claude Code `/model` probes with `max_tokens: 1` no longer become a false 502 ([#11568](https://github.com/diegosouzapw/OmniRoute/pull/11568)) — thanks @Seramicx +- **fix(security):** sanitize `request.failed` diagnostics before publishing them to live dashboard listeners and replay history, while keeping status, model, provider, latency, and internal call-log diagnostics intact. ([#12469](https://github.com/diegosouzapw/OmniRoute/pull/12469)) +- **fix(pwa):** do not intercept dashboard navigations so Chrome can fall back from a stale HTTP/3 Alt-Svc advertisement (UDP :20128 is unpublished; F5 on a long-lived tab hung until a new tab opened a fresh TCP connection). ([#12767](https://github.com/diegosouzapw/OmniRoute/pull/12767)) — thanks @HouMinXi +- **fix(executors):** handle DuckDuckGo ERR_BN_LIMIT (418) without retrying — when the upstream returns `418 ERR_BN_LIMIT` (rate-limit/ban), the executor now returns the error immediately instead of burning another VQD acquisition that would only count against the IP limit. The retry logic for `418 ERR_CHALLENGE` (unsolved challenge) remains unchanged. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) ([#12110](https://github.com/diegosouzapw/OmniRoute/pull/12110)) — thanks @oyi77 / @Meet6338-X +- Fixed DuckDuckGo streaming responses losing JSON lines and UTF-8 characters split across network chunks. ([#11528](https://github.com/diegosouzapw/OmniRoute/pull/11528)) — thanks @pacocartones +- Harden generated Cloudflare, Vercel, and Deno relays by stripping hop-by-hop, framing, and proxy authentication headers before forwarding requests upstream. ([#11533](https://github.com/diegosouzapw/OmniRoute/pull/11533)) — thanks @pacocartones +- **feat(grok-cli):** Provider Limits shows grok-cli banked reset credits from `GetRemainingResets` (including a real zero; a failed RPC omits the row) and the existing View credits button now calls `ConsumerUiSvc/RedeemReset` for grok-cli. Live tokens use nested fields 10/20/30 (id + Timestamp), not compact 1/2/3. ([#12805](https://github.com/diegosouzapw/OmniRoute/pull/12805)) — thanks @HouMinXi +- **fix(memory):** Embedding Model Quick select, Embedding Source remote dropdown, and Rerank selector now list every configured provider with embedding/rerank support instead of only chat-catalog text matches plus OpenRouter live discovery; a generic OpenAI-compatible `/embeddings` + Cohere-compatible `/rerank` runtime fallback resolves any configured chat provider's embedding/rerank endpoint, so unlisted providers no longer fail with "Unknown embedding provider"; both memory selectors gained a free-text model override ([#11390](https://github.com/diegosouzapw/OmniRoute/pull/11390)) — thanks @rqzbeh +- Harden SQLite upgrades around the historical migration-074 version collision: missing discovery and inspector tables are replayed atomically, pre-existing databases (including setup-created skeletons) receive reusable content-addressed safety snapshots, and Node test/eval probes without `DATA_DIR` are isolated from the operator database. ([#12435](https://github.com/diegosouzapw/OmniRoute/pull/12435)) +- **fix(dashboard):** Moonshot/Kimi Open Platform voucher and cash leftover percentages follow the bucket balance, so an empty wallet no longer paints those rows as 100% while Available is 0% ([#12733](https://github.com/diegosouzapw/OmniRoute/pull/12733)) — thanks @HouMinXi +- **fix(combo):** return non-retryable HTTP 400 when all candidates for a pinned native Codex turn are unavailable due to model-scoped lockout, terminating the turn cleanly while preserving turn continuity and enabling standard Combo routing on subsequent turns ([#12240](https://github.com/diegosouzapw/OmniRoute/pull/12240)) — thanks @mdigitalbh81 +- Fixed auto-router strategies preserving the selected connection through ranking and dispatch when multiple connections share the same provider and model. ([#11530](https://github.com/diegosouzapw/OmniRoute/pull/11530)) — thanks @pacocartones +- Preserve Chat Completions JSON-object response formats when translating requests to the Responses API. ([#11529](https://github.com/diegosouzapw/OmniRoute/pull/11529)) — thanks @pacocartones +- Fix idempotency fingerprints so Responses API requests include `input` and other semantic generation fields using stable serialization, while excluding credentials and request metadata. ([#11532](https://github.com/diegosouzapw/OmniRoute/pull/11532)) — thanks @pacocartones +- **fix(api):** Generated API CLI commands now enforce required OpenAPI request bodies; Combo test commands forward the required `comboName` body, while API keys created by older writers after migration 149 preserve legacy allow-all Combo access without widening explicit empty allowlists — thanks @marcelokarval ([#12070](https://github.com/diegosouzapw/OmniRoute/pull/12070)) +- **fix(grok-web):** treat upstream streaming failures as failures instead of successful assistant text: error-only streams now fail readiness with HTTP 502, while failures after legitimate content preserve that partial output and terminate through the sanitized stream failure path without a normal `stop` completion. ([#12458](https://github.com/diegosouzapw/OmniRoute/pull/12458)) +- HuggingChat now turns HTTP 200 JSONL generation failures into a sanitized 502 before content, or a fixed public stream failure after partial output, so fallback and request persistence no longer record a false successful stop. ([#12456](https://github.com/diegosouzapw/OmniRoute/pull/12456)) +- **fix(providers):** keep 1min.ai HTTP 200 stream errors out of assistant content, preserve partial output, and expose sanitized terminal errors so pre-content failures can fall back. ([#12466](https://github.com/diegosouzapw/OmniRoute/pull/12466)) +- **fix(sessions):** preserve legacy Sessions data when the additive exclusive-session projection is unavailable, returning an empty projection and warning only once per contiguous outage instead of failing the endpoint; the Sessions badge intentionally reflects the merged legacy and exclusive row count introduced by [#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389) ([#11469](https://github.com/diegosouzapw/OmniRoute/pull/11469)) — thanks @KaspaPulse +- Render Nimble Search with the generic provider icon and serve Opper's proven logo locally. ([#12415](https://github.com/diegosouzapw/OmniRoute/pull/12415)) +- **fix(quality):** `config/quality/provider-assets-provenance.jsonl` was stale against the physical `public/providers/` tree (still listing 225 assets and 5 duplicate-content groups after several provider-retirement PRs removed 85 icon files), which would have made `check:provider-asset-provenance` fail on the next full run. Reconciled the manifest to the real 142-file snapshot (142 asset records, 1 duplicate-content group, 2 newly-registered files marked `unresolved` pending provenance research), repinned `auditedCommit`, and updated the regression test's hardcoded counts to match. ([#11876](https://github.com/diegosouzapw/OmniRoute/pull/11876)) +- fix(ui): wrap quota card footer action buttons to prevent clipping Refresh button on narrow cards — when extra action buttons such as `View credits` are present on Codex cards, allow the footer row to flex-wrap with `shrink-0` buttons so `Refresh now` remains visible and clickable across all responsive card widths ([#11464](https://github.com/diegosouzapw/OmniRoute/pull/11464)) — thanks @adevwithpurpose +- Keep Antigravity Gemini usable when the same connection's Claude weekly quota is empty; generic quota cache stays per-connection for every other provider. ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) — thanks @HouMinXi +- Fixed Tailscale tunnel setup to reject ports outside the valid 1-65535 range and safely install Windows MSI packages from paths containing apostrophes. ([#11534](https://github.com/diegosouzapw/OmniRoute/pull/11534)) — thanks @pacocartones +- Classify Cloudflared and Tailscale process-management routes as local-only and spawn-capable while preserving remote access to read-only tunnel status endpoints. ([#11531](https://github.com/diegosouzapw/OmniRoute/pull/11531)) — thanks @pacocartones +- Electron release workflow: the `publish-npm` job now grants `actions: read` to the reusable `npm-publish.yml` it calls (its `publish` job requests it), which is what made GitHub refuse the whole v3.8.50 run at startup and ship the release with zero desktop assets; a `workflow_dispatch` now builds the requested tag instead of the dispatching branch and can skip the npm leg (`publish_npm=false`) when only re-attaching assets ([#11974](https://github.com/diegosouzapw/OmniRoute/pull/11974)) +- **fix(dashboard):** The Combos page usage guide now reads its dismissal through `useSyncExternalStore` instead of correcting SSR state inside an effect, removing an extra commit of the page tree on every load (and the `react-hooks/set-state-in-effect` error it raised). ([#12671](https://github.com/diegosouzapw/OmniRoute/pull/12671)) +- Electron release: `electron/package-lock.json` regained the optional `electron-builder-squirrel-windows` subtree (13 entries) that `npm ci` had been refusing as out of sync, `electron-release.yml` gained a `build_ref` dispatch input and stops regenerating release notes on a re-attach dispatch, and the npm publish workflow attaches the SBOM to the GitHub Release on dispatch publishes too — so the v3.8.51 tag ships every desktop asset and the SBOM like v3.8.49 did ([#12022](https://github.com/diegosouzapw/OmniRoute/pull/12022)) +- Drained the reds every PR against `release/v3.8.51` was born with: documented the three service-worker build-id variables, registered the six retirement/tunnel tests with the mutation gate, approved `eslint-plugin-react-hooks` in the dependency allowlist, added the six `combo.sort.*` strings to `vi` and `pt-BR`, pointed the ChatGPT Web doc at the real migration-168 test, worded the g4f hint around the member key, and realigned four tests to the retired-provider catalog and the legacy-schema fixtures the retirement migrations touch. ([#11940](https://github.com/diegosouzapw/OmniRoute/pull/11940)) +- **fix(providers):** Claude, Grok, LMArena, Notion, and Perplexity web-cookie transports now use pooled `wreq-js` 3.2 instead of the native sidecar, with all nine supported bindings pinned and audited, and the applicable platform binding plus native-license evidence included in each release artifact ([#12429](https://github.com/diegosouzapw/OmniRoute/pull/12429), supersedes [#11753](https://github.com/diegosouzapw/OmniRoute/pull/11753)). +- **fix(providers):** Zed Hosted streaming failures now trigger fallback before content and end partial streams with a sanitized structured error instead of fake assistant text and a normal-success stop. ([#12455](https://github.com/diegosouzapw/OmniRoute/pull/12455)) +- **fix(test):** capture hook result after render (direct commit `b85d0abb0b`) +- **fix(sync):** repair the two regressions the v3.8.50 sync-back left on release/v3.8.51 (direct commit `5ade9e0851`) +- **revert(models):** drop the memo reset hook ported from #10055 (direct commit `282de841ae`) +- **fix(typecheck):** guard the optional connectionId before the reactive model sync (direct commit `f2825bad75`) +- **fix(quota-share):** release the winner's reserved in-flight slot (#11371) ([#11408](https://github.com/diegosouzapw/OmniRoute/pull/11408)) — thanks @oyi77 +- **fix(providers):** stop silently dropping reasoning effort on opencode families (#10788) ([#11409](https://github.com/diegosouzapw/OmniRoute/pull/11409)) — thanks @oyi77 +- **fix(combo):** enable genuine quota-aware routing for generic providers (antigravity, claude, etc.) ([#11411](https://github.com/diegosouzapw/OmniRoute/pull/11411)) — thanks @HouMinXi +- **fix(oauth):** allow expired token retry before deactivation and activate expired retry loop (#P0) ([#11414](https://github.com/diegosouzapw/OmniRoute/pull/11414)) — thanks @HouMinXi +- **perf(executors):** lazy-load the executor registry — defer class imports + construction to first use (#11220) ([#11421](https://github.com/diegosouzapw/OmniRoute/pull/11421)) — thanks @oyi77 +- **fix:** make release/v3.8.50 CI gates green (run 32786966560 root causes) ([#11450](https://github.com/diegosouzapw/OmniRoute/pull/11450)) — thanks @jonlwheat2-gif +- **fix(sse):** exact-domain cookie match and origin-equality URL assertions ([#11452](https://github.com/diegosouzapw/OmniRoute/pull/11452)) — thanks @hartmark +- **fix(providers):** send Responses API payload during OpenAI-compatible validation ([#11454](https://github.com/diegosouzapw/OmniRoute/pull/11454)) — thanks @tuandinh0801 +- **fix(command-code):** fallback to /alpha/generate for Go plan without Provider API access ([#11455](https://github.com/diegosouzapw/OmniRoute/pull/11455)) — thanks @Chewji9875 +- **fix(responses-continuation):** fail closed on a log-truncated stored input/output array ([#11473](https://github.com/diegosouzapw/OmniRoute/pull/11473)) — thanks @hartmark +- **fix(cli):** write jcode settings as [providers.omniroute] in config.toml ([#11484](https://github.com/diegosouzapw/OmniRoute/pull/11484)) — thanks @hongnoul +- **fix(logging):** raise chat-log array truncation cap to 1000 and unify duplicate implementations ([#11499](https://github.com/diegosouzapw/OmniRoute/pull/11499)) — thanks @hartmark +- **fix:** restore live websocket URL helpers ([#11509](https://github.com/diegosouzapw/OmniRoute/pull/11509)) — thanks @PauloFH +- **perf(core):** eliminate async waterfalls across home, cache, and db operations (#11396) ([#11512](https://github.com/diegosouzapw/OmniRoute/pull/11512)) — thanks @HouMinXi +- **fix(kiro):** add runtime.us-east-1.kiro.dev as first-attempt endpoint ([#11517](https://github.com/diegosouzapw/OmniRoute/pull/11517)) — thanks @rifqiawl +- **fix(providers):** remove duplicate 128-tool truncation in OpencodeExecutor (#11444) ([#11518](https://github.com/diegosouzapw/OmniRoute/pull/11518)) — thanks @HouMinXi +- **fix(providers):** poll onboardUser LRO before classifying BYOP ([#11519](https://github.com/diegosouzapw/OmniRoute/pull/11519)) — thanks @rifqiawl +- **fix(translator):** preserve exact tool name casing for Claude tool_use payloads (#11487) ([#11520](https://github.com/diegosouzapw/OmniRoute/pull/11520)) — thanks @HouMinXi +- **fix(kimi-web):** align health probe with executor domain ([#11521](https://github.com/diegosouzapw/OmniRoute/pull/11521)) — thanks @pacocartones +- **fix(cli):** preserve existing admin password during setup ([#11522](https://github.com/diegosouzapw/OmniRoute/pull/11522)) — thanks @pacocartones +- **fix(mitm):** keep Windows DNS elevation off the POSIX sudo path (#11430) ([#11542](https://github.com/diegosouzapw/OmniRoute/pull/11542)) — thanks @official-burak +- **fix(sse):** scale chat admission by ingest byte budget instead of a fixed request count ([#11548](https://github.com/diegosouzapw/OmniRoute/pull/11548)) — thanks @TheDemonTuan +- **fix:** accept Gemini Web cookie exports ([#11554](https://github.com/diegosouzapw/OmniRoute/pull/11554)) — thanks @rafacpti23 +- **fix(i18n):** complete Brazilian Portuguese translations ([#11558](https://github.com/diegosouzapw/OmniRoute/pull/11558)) — thanks @rafacpti23 +- **fix(sse):** explicit types for openai-responses pureHelpers — clears last failing core typecheck gate ([#11567](https://github.com/diegosouzapw/OmniRoute/pull/11567)) — thanks @jonlwheat2-gif +- **fix(opencode):** hydrate Proxy Pool references for no-auth connections ([#11584](https://github.com/diegosouzapw/OmniRoute/pull/11584)) — thanks @AStupidBear +- **fix(ci):** resolve inherited release/v3.8.51 base-red CI failures ([#11608](https://github.com/diegosouzapw/OmniRoute/pull/11608)) — thanks @jonlwheat2-gif +- **fix(db):** fallback load STORAGE_ENCRYPTION_KEY from env files ([#11614](https://github.com/diegosouzapw/OmniRoute/pull/11614)) — thanks @benzntech +- **fix(providers):** mark Brave Search as no longer having a free tier ([#11621](https://github.com/diegosouzapw/OmniRoute/pull/11621)) — thanks @turbolego +- **fix:** pass tool result content through as string in OpenAI/Claude -> Gemini translation ([#11624](https://github.com/diegosouzapw/OmniRoute/pull/11624)) — thanks @Hsia97 +- **fix(sse):** scope credential health to exact key ([#11628](https://github.com/diegosouzapw/OmniRoute/pull/11628)) — thanks @KaspaPulse +- **fix(install):** guard prepare script against missing husky ([#11634](https://github.com/diegosouzapw/OmniRoute/pull/11634)) — thanks @Prajeeth-12 +- **fix(security):** redact NVIDIA_API_KEY at the log sink in the ad-hoc diag script ([#11639](https://github.com/diegosouzapw/OmniRoute/pull/11639)) +- **fix(types):** clear the two type regressions red on release/v3.8.51 ([#11640](https://github.com/diegosouzapw/OmniRoute/pull/11640)) — thanks @jonlwheat2-gif +- **fix(build):** resolve workspace: protocol dependencies to concrete semver versions during prepublish (#11540) ([#11641](https://github.com/diegosouzapw/OmniRoute/pull/11641)) — thanks @HouMinXi +- **fix(search):** properly resolve configured search connection in /v1/responses pipeline (#11524) ([#11642](https://github.com/diegosouzapw/OmniRoute/pull/11642)) — thanks @HouMinXi +- **fix(authz):** trust cf-connecting-ip for via-proxy detection when peer is Cloudflare edge (#11514) ([#11643](https://github.com/diegosouzapw/OmniRoute/pull/11643)) — thanks @HouMinXi +- **fix(security):** compare the OIDC state cookie in constant time ([#11646](https://github.com/diegosouzapw/OmniRoute/pull/11646)) +- **fix(security):** namespace the dedup hash by the calling API key ([#11649](https://github.com/diegosouzapw/OmniRoute/pull/11649)) +- **fix(models):** gate the Codex-native catalog loop on prefix mode (#11632) ([#11662](https://github.com/diegosouzapw/OmniRoute/pull/11662)) — thanks @xiaoyaner0201 +- **fix(dashboard):** project Codex routing quota in account pools ([#11666](https://github.com/diegosouzapw/OmniRoute/pull/11666)) — thanks @KaspaPulse +- **fix(ddgw):** normaliza system no upstream da release v3.8.51 ([#11692](https://github.com/diegosouzapw/OmniRoute/pull/11692)) — thanks @RhianB14 +- **fix(memory):** honor category filter in GET /api/memory ([#11699](https://github.com/diegosouzapw/OmniRoute/pull/11699)) — thanks @Sabeekhann +- **fix(ci):** detect stale workspace lockfile entries ([#11714](https://github.com/diegosouzapw/OmniRoute/pull/11714)) — thanks @pacocartones +- **fix(ci):** stamp dist/BUILD_SHA before the npm publish provenance gate ([#11721](https://github.com/diegosouzapw/OmniRoute/pull/11721)) +- **fix(docker):** decouple the best-effort Bun image from the release manifest ([#11724](https://github.com/diegosouzapw/OmniRoute/pull/11724)) +- **perf(compression):** avoid cloning memo misses twice ([#11727](https://github.com/diegosouzapw/OmniRoute/pull/11727)) — thanks @RaviTharuma +- **fix(docker):** let the bun digest artifact be absent, not fatal ([#11740](https://github.com/diegosouzapw/OmniRoute/pull/11740)) +- **fix(shared):** block cloud-metadata hosts under default remote-image guard ([#11755](https://github.com/diegosouzapw/OmniRoute/pull/11755)) — thanks @santosraju99-hub +- **fix(api):** preserve caller-provided X-Correlation-Id on chat completions ([#11760](https://github.com/diegosouzapw/OmniRoute/pull/11760)) — thanks @aniruddhaadak80 +- **fix(resilience):** lock permanently retired models instead of short backoff (Gemini ban prevention) ([#11762](https://github.com/diegosouzapw/OmniRoute/pull/11762)) — thanks @turbolego +- **fix:** mark Vercel AI Gateway as passthroughModels ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **fix(resilience):** stop hammering permanently-moved endpoints and billing-suspended accounts ([#11774](https://github.com/diegosouzapw/OmniRoute/pull/11774)) — thanks @turbolego +- **fix(lease):** remove global static reservation and gate routing on live active lease occupancy ([#11775](https://github.com/diegosouzapw/OmniRoute/pull/11775)) — thanks @TheDemonTuan +- **fix(ci):** size the install-upgrade gate to a measured run, and log the pack cost ([#11776](https://github.com/diegosouzapw/OmniRoute/pull/11776)) +- **fix(pwa):** stop serving the stale shell after deploys ([#11779](https://github.com/diegosouzapw/OmniRoute/pull/11779)) — thanks @HouMinXi +- **fix(combo):** lock GitHub models rejected as "not supported" for future requests ([#11781](https://github.com/diegosouzapw/OmniRoute/pull/11781)) — thanks @turbolego +- **fix(cli):** probe both IPv4 and IPv6 loopback for server readiness (#11766) ([#11794](https://github.com/diegosouzapw/OmniRoute/pull/11794)) — thanks @vermasomesh835 +- **fix(sse):** preserve CLIProxyAPI wrappers per connection ([#11797](https://github.com/diegosouzapw/OmniRoute/pull/11797)) — thanks @RaviTharuma +- **fix(providers):** honor model auto-fetch opt-in on create ([#11798](https://github.com/diegosouzapw/OmniRoute/pull/11798)) — thanks @RaviTharuma +- **fix(dashboard):** make model auto-fetch order independent ([#11805](https://github.com/diegosouzapw/OmniRoute/pull/11805)) — thanks @RaviTharuma +- **fix(docker):** make GHA cache exports best effort ([#11806](https://github.com/diegosouzapw/OmniRoute/pull/11806)) — thanks @RaviTharuma +- **fix(kiro):** do not permanently ban on 'User is not authorized to make this call' ([#11809](https://github.com/diegosouzapw/OmniRoute/pull/11809)) — thanks @Deftera186 +- **fix(sse):** keep cache-write tokens in OpenAI-shaped usage ([#11814](https://github.com/diegosouzapw/OmniRoute/pull/11814)) — thanks @TheDemonTuan +- **fix(docs):** resolve relative markdown and wiki links across Fumadocs and GitHub wiki ([#11834](https://github.com/diegosouzapw/OmniRoute/pull/11834)) — thanks @AndrianBalanescu +- **fix(chat):** preserve unstripped model string for passthrough provider routing ([#11840](https://github.com/diegosouzapw/OmniRoute/pull/11840)) — thanks @raheemuddin786 +- **fix(mcp):** avoid async initialization inside sync esm wrappers (#11569) ([#11849](https://github.com/diegosouzapw/OmniRoute/pull/11849)) — thanks @HouMinXi +- **fix(sse):** preserve coherent markdown token boundaries across stream translation chunks (#11606) ([#11851](https://github.com/diegosouzapw/OmniRoute/pull/11851)) — thanks @HouMinXi +- **fix(ci):** run the install-upgrade gate on disk, not on the /tmp tmpfs ([#11855](https://github.com/diegosouzapw/OmniRoute/pull/11855)) +- **fix(skills):** expand shorthand property types in injected tool schemas ([#11857](https://github.com/diegosouzapw/OmniRoute/pull/11857)) — thanks @alvinveroy +- **fix(ci):** publish npm from a hosted runner so provenance is accepted ([#11877](https://github.com/diegosouzapw/OmniRoute/pull/11877)) +- **fix(lint):** pin react hooks plugin to 7.0.1 ([#11886](https://github.com/diegosouzapw/OmniRoute/pull/11886)) +- **fix(ci):** surface and clear release-green ESLint failure ([#11890](https://github.com/diegosouzapw/OmniRoute/pull/11890)) — thanks @MumuTW +- **fix(guardrails):** prevent duplicate prompt-injection-guard log output ([#11936](https://github.com/diegosouzapw/OmniRoute/pull/11936)) — thanks @patrykkopycinski +- **fix(auth):** downgrade expected transient states from warn to debug ([#11937](https://github.com/diegosouzapw/OmniRoute/pull/11937)) — thanks @patrykkopycinski +- **fix(codex):** fail over image generation for imported free plans ([#11948](https://github.com/diegosouzapw/OmniRoute/pull/11948)) — thanks @Bl0ck154 +- **fix(codex):** normalize non-stream responses ([#11951](https://github.com/diegosouzapw/OmniRoute/pull/11951)) — thanks @Bl0ck154 +- **fix(images):** forward Antigravity image size ([#11952](https://github.com/diegosouzapw/OmniRoute/pull/11952)) — thanks @Bl0ck154 +- **fix(db):** invalidate connection cache after upsert ([#11953](https://github.com/diegosouzapw/OmniRoute/pull/11953)) — thanks @Bl0ck154 +- **fix(codex):** restore imported account state ([#11954](https://github.com/diegosouzapw/OmniRoute/pull/11954)) — thanks @Bl0ck154 +- **fix(sse):** give extended-thinking targets the reasoning readiness budget ([#11959](https://github.com/diegosouzapw/OmniRoute/pull/11959)) — thanks @abhisheksharma2411 +- **fix(dashboard):** use opaque background and readable text color on cost chart tooltips ([#11960](https://github.com/diegosouzapw/OmniRoute/pull/11960)) — thanks @ZaimMarzuki +- **fix(antigravity):** send complete loadCodeAssist metadata (ideType/platform/pluginType as numeric enums) ([#11969](https://github.com/diegosouzapw/OmniRoute/pull/11969)) — thanks @rifqiawl +- **fix(core):** resolve DB init race condition and reasoning translation ([#12003](https://github.com/diegosouzapw/OmniRoute/pull/12003)) — thanks @raheemuddin786 +- **fix(admission):** exclude reclaimable page cache from the cgroup pressure ratio ([#12017](https://github.com/diegosouzapw/OmniRoute/pull/12017)) — thanks @HouMinXi +- **fix(build):** prune non-production trees in NFT trace excludes and tsconfig ([#12028](https://github.com/diegosouzapw/OmniRoute/pull/12028)) — thanks @Chewji9875 +- **fix(migrations):** add renamed migration compatibility for 056/073/077/101 ([#12036](https://github.com/diegosouzapw/OmniRoute/pull/12036)) — thanks @oyi77 +- **fix(sse):** default crash-guard logger to console.warn, not console ([#12042](https://github.com/diegosouzapw/OmniRoute/pull/12042)) — thanks @HouMinXi +- **fix(catalog):** derive vision/modalities for built-in auto combos from effective target pool ([#12046](https://github.com/diegosouzapw/OmniRoute/pull/12046)) — thanks @Prajeeth-12 +- **fix(release):** the packaged-app smoke verifies the database opened, not a driver line the primary path never prints (twin of #12032) ([#12047](https://github.com/diegosouzapw/OmniRoute/pull/12047)) +- **fix(barrel):** migrate src/app/ off the @/lib/localDb barrel import (#11795 Phase 2) ([#12051](https://github.com/diegosouzapw/OmniRoute/pull/12051)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate src/lib/ off the localDb barrel to direct db imports (#59) ([#12052](https://github.com/diegosouzapw/OmniRoute/pull/12052)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate open-sse, src/shared, src/sse, src/models, src/domain off the @/lib/localDb barrel import (#11795 Phase 4) ([#12053](https://github.com/diegosouzapw/OmniRoute/pull/12053)) — thanks @jonlwheat2-gif +- **fix(barrel):** delete the @/lib/localDb barrel — every consumer migrated (#11795 Phase 5) ([#12055](https://github.com/diegosouzapw/OmniRoute/pull/12055)) — thanks @jonlwheat2-gif +- **fix(radar):** restore D12 public boundary ([#12057](https://github.com/diegosouzapw/OmniRoute/pull/12057)) +- **fix(dev):** bound webpack and Tailwind scans ([#12075](https://github.com/diegosouzapw/OmniRoute/pull/12075)) — thanks @backryun +- **fix(dev):** isolate root layout settings reads ([#12076](https://github.com/diegosouzapw/OmniRoute/pull/12076)) — thanks @backryun +- **fix(dev):** reduce instrumentation executor fan-out (phase 3) ([#12078](https://github.com/diegosouzapw/OmniRoute/pull/12078)) — thanks @backryun +- **fix(dev):** make logging resources HMR-singleton ([#12079](https://github.com/diegosouzapw/OmniRoute/pull/12079)) — thanks @backryun +- **fix(sse):** keep unavailable forced connections scoped ([#12080](https://github.com/diegosouzapw/OmniRoute/pull/12080)) — thanks @keeltrace +- **fix(dev):** isolate batch dispatch from instrumentation ([#12081](https://github.com/diegosouzapw/OmniRoute/pull/12081)) — thanks @backryun +- **fix(ci):** reconcile release test contract drift ([#12082](https://github.com/diegosouzapw/OmniRoute/pull/12082)) — thanks @backryun +- **fix(ci):** accept CVE-2025-68121 in the prebuilt tls-client .so, auto-close base-red issues, guard Scorecard on the default branch ([#12085](https://github.com/diegosouzapw/OmniRoute/pull/12085)) +- **fix(db):** drop three consumer-less 1proxy exports — dead-code base-red on release/v3.8.51 after the barrel deletion (#12055) ([#12087](https://github.com/diegosouzapw/OmniRoute/pull/12087)) +- **fix(api):** clear the six API-route TypeScript regressions the new gate landed red on ([#12094](https://github.com/diegosouzapw/OmniRoute/pull/12094)) +- **fix(release):** never let the tag-push Create Release append auto notes to the curated body ([#12096](https://github.com/diegosouzapw/OmniRoute/pull/12096)) +- **fix(sse):** honor CLIProxyAPI environment API key ([#12099](https://github.com/diegosouzapw/OmniRoute/pull/12099)) — thanks @RaviTharuma +- **fix(oauth):** bind Google refresh to the client that issued the token ([#12106](https://github.com/diegosouzapw/OmniRoute/pull/12106)) — thanks @HouMinXi +- **fix(dashboard):** make RequestLoggerDetail loadable outside Next — CSS via globals.css + CJS/ESM interop (#11703 base-reds) ([#12114](https://github.com/diegosouzapw/OmniRoute/pull/12114)) +- **fix(leases):** project status lease row to lease columns so joined connection PII never escapes ([#12115](https://github.com/diegosouzapw/OmniRoute/pull/12115)) — thanks @geek007git +- **fix(plugins):** do not kill the plugin process when a fire-and-forget hook times out ([#12116](https://github.com/diegosouzapw/OmniRoute/pull/12116)) — thanks @geek007git +- **fix(guardrails):** restore injection-guard logging on middleware-only routes ([#12117](https://github.com/diegosouzapw/OmniRoute/pull/12117)) — thanks @geek007git +- **fix(plugins):** refresh stored manifest from disk on activate so new hook fields reach existing installs ([#12120](https://github.com/diegosouzapw/OmniRoute/pull/12120)) — thanks @geek007git +- **fix(codex):** preserve existing provider state when bulk-import upserts a matching connection ([#12122](https://github.com/diegosouzapw/OmniRoute/pull/12122)) — thanks @geek007git +- **fix(ci):** clear the two base-reds the 2026-08-30 merge batch left on release/v3.8.51 (round 3) ([#12123](https://github.com/diegosouzapw/OmniRoute/pull/12123)) +- **fix(sse):** strip type:'custom' from Claude tools on agentrouter dispatch ([#12126](https://github.com/diegosouzapw/OmniRoute/pull/12126)) — thanks @wahidsadik371-coder +- **fix(sse):** stop advertising video providers the dispatcher cannot run ([#12131](https://github.com/diegosouzapw/OmniRoute/pull/12131)) — thanks @quiterunner-commits +- **fix(search):** treat HTTP 432 and plan limit errors as transient cooldown ([#12139](https://github.com/diegosouzapw/OmniRoute/pull/12139)) — thanks @HouMinXi +- **fix(sse):** refuse an AI Horde queue that cannot fit the request budget ([#12143](https://github.com/diegosouzapw/OmniRoute/pull/12143)) — thanks @quiterunner-commits +- **fix(ci):** clear the base-reds the afternoon merge batch left on release/v3.8.51 (round 5: provider count 352, TS2554/TS2677) ([#12144](https://github.com/diegosouzapw/OmniRoute/pull/12144)) +- **fix(translator):** strip neutral tool_choice when tools absent in Responses-to-Chat (#12141) ([#12166](https://github.com/diegosouzapw/OmniRoute/pull/12166)) — thanks @HouMinXi +- **fix(guardrails):** pass providerId to getResolvedModelCapabilities in checkComboVision (#12112) ([#12169](https://github.com/diegosouzapw/OmniRoute/pull/12169)) — thanks @HouMinXi +- **fix(translator):** strip plaintext reasoning content for opaque responses backends (#12128) ([#12171](https://github.com/diegosouzapw/OmniRoute/pull/12171)) — thanks @HouMinXi +- **perf(stream):** compile hot-path regexes once, bound token caches, fix quadratic buffering ([#12179](https://github.com/diegosouzapw/OmniRoute/pull/12179)) — thanks @opensource-elearning +- **fix:** resolve compression worker file using runtime anchors instead of… ([#12183](https://github.com/diegosouzapw/OmniRoute/pull/12183)) — thanks @NightStalker-87 +- **fix:** prevent Claude Code session kills via liveness-aware readiness + auto model echo ([#12189](https://github.com/diegosouzapw/OmniRoute/pull/12189)) — thanks @opensource-elearning +- **fix(translator):** preserve falsy primitive values in Gemini and Antigravity function response results ([#12191](https://github.com/diegosouzapw/OmniRoute/pull/12191)) — thanks @Siva010 +- **fix(oauth):** mark empty Antigravity projectId as degraded and clear stale errors (#11284) ([#12205](https://github.com/diegosouzapw/OmniRoute/pull/12205)) — thanks @HouMinXi +- **fix(providers):** gate the Codex auto-ping usage read on the shared quota throttle ([#12209](https://github.com/diegosouzapw/OmniRoute/pull/12209)) — thanks @abhisheksharma2411 +- **fix(combos):** prioritize SQLite row id over inner JSON id and notify delete errors ([#12213](https://github.com/diegosouzapw/OmniRoute/pull/12213)) — thanks @HouMinXi +- **fix(providers):** mark featherless.ai as no longer having a free tier (`hasFree: false`) ([#12216](https://github.com/diegosouzapw/OmniRoute/pull/12216)) — thanks @turbolego +- **fix(deepseek-web):** stop Turbopack dev panic in the PoW worker path resolver ([#12221](https://github.com/diegosouzapw/OmniRoute/pull/12221)) — thanks @hartmark +- **fix(chat-admission):** clarify local 503 source ([#12223](https://github.com/diegosouzapw/OmniRoute/pull/12223)) — thanks @keeltrace +- **fix(dev):** silence webpack runtime module warnings ([#12228](https://github.com/diegosouzapw/OmniRoute/pull/12228)) — thanks @backryun +- **fix(db):** use module.require for CommonJS runtime driver loading ([#12230](https://github.com/diegosouzapw/OmniRoute/pull/12230)) — thanks @raheemuddin786 +- **fix(memory):** honest probe-driven FTS5 keyword status + memory_id rowid sync ([#12231](https://github.com/diegosouzapw/OmniRoute/pull/12231)) — thanks @raheemuddin786 +- **fix(system):** propagate abort signal to stream reader in HTTP version checks ([#12232](https://github.com/diegosouzapw/OmniRoute/pull/12232)) — thanks @raheemuddin786 +- **fix(resilience):** isolate local host execution errors from provider circuit breakers ([#12233](https://github.com/diegosouzapw/OmniRoute/pull/12233)) — thanks @raheemuddin786 +- **fix(sse):** sort injected tools deterministically for prompt caching ([#12234](https://github.com/diegosouzapw/OmniRoute/pull/12234)) — thanks @raheemuddin786 +- **fix(combo):** always clear the loop-safety timer, not just on the happy path (#11804) ([#12245](https://github.com/diegosouzapw/OmniRoute/pull/12245)) +- **fix(quality):** base-red pair — stryker tap registration + turn-pin suites aligned to the window gate ([#12255](https://github.com/diegosouzapw/OmniRoute/pull/12255)) — thanks @backryun +- **fix(dev):** qualify Turbopack runtime boundaries ([#12258](https://github.com/diegosouzapw/OmniRoute/pull/12258)) — thanks @backryun +- **fix(combo):** fall back to full pool when collapsed sole survivor is context-too-small ([#12278](https://github.com/diegosouzapw/OmniRoute/pull/12278)) — thanks @opensource-elearning +- **fix(combo):** bound the pre-dispatch unavailable skip so a stale label cannot dark a pool (#12168) ([#12285](https://github.com/diegosouzapw/OmniRoute/pull/12285)) +- **fix(security):** strip Qwen/Alibaba console-session cookies from provider API responses ([#12287](https://github.com/diegosouzapw/OmniRoute/pull/12287)) +- **fix(usage):** console-aware Token Plan guidance + subscription hint on bailian 401 ([#12288](https://github.com/diegosouzapw/OmniRoute/pull/12288)) +- **fix(resourcePressure):** log numeric detail on every rejection, recover faster ([#12293](https://github.com/diegosouzapw/OmniRoute/pull/12293)) — thanks @hartmark +- **fix(release):** drain the 2026-09-01 base-red window — passthrough usage regression + radarPage i18n keys ([#12327](https://github.com/diegosouzapw/OmniRoute/pull/12327)) +- **fix(ci):** openapi-security-tiers checker must honor routeGuard patterns + imported prefixes ([#12350](https://github.com/diegosouzapw/OmniRoute/pull/12350)) — thanks @ggiak +- **perf(ci):** cache node_modules in the npm-ci-retry composite (#8084 D3) ([#12408](https://github.com/diegosouzapw/OmniRoute/pull/12408)) +- **revert(ui):** point CTAs back at their real destinations ([#12410](https://github.com/diegosouzapw/OmniRoute/pull/12410)) +- **fix(release):** drain the 2026-09-02 base-red — rerank-providers import + api-typecheck baseline ratchet ([#12414](https://github.com/diegosouzapw/OmniRoute/pull/12414)) +- **fix(types):** make system prompt injection noImplicitAny-safe ([#12416](https://github.com/diegosouzapw/OmniRoute/pull/12416)) +- **fix(quality):** validate typecheck baseline schema ([#12419](https://github.com/diegosouzapw/OmniRoute/pull/12419)) +- **fix(memory):** resolve rerank provider node cache import ([#12421](https://github.com/diegosouzapw/OmniRoute/pull/12421)) +- **fix(ci):** repair release-root regressions — pack dedup, web-session syntax, uc-image ids ([#12423](https://github.com/diegosouzapw/OmniRoute/pull/12423)) — thanks @backryun +- **fix(providers):** separate MaxAI and UC credential contracts ([#12431](https://github.com/diegosouzapw/OmniRoute/pull/12431)) +- **fix(cli):** preserve Claude settings on config set ([#12432](https://github.com/diegosouzapw/OmniRoute/pull/12432)) — thanks @KrzysiekSko +- **fix(providers):** repair the maxai credential block truncated by merge auto-resolve ([#12433](https://github.com/diegosouzapw/OmniRoute/pull/12433)) +- **fix(quality):** record the 2026-09-02 merged growth in the file-size baseline ([#12434](https://github.com/diegosouzapw/OmniRoute/pull/12434)) +- **fix(uc):** route UC error strings through sanitizeErrorMessage; allowlist the retired codex id ([#12437](https://github.com/diegosouzapw/OmniRoute/pull/12437)) +- **fix(adapta):** redact streamed upstream errors ([#12438](https://github.com/diegosouzapw/OmniRoute/pull/12438)) +- **fix(sse):** remap non-contiguous upstream tool_calls index to a gap-free output_index ([#12445](https://github.com/diegosouzapw/OmniRoute/pull/12445)) — thanks @hartmark +- **fix(quality):** detect duplicate tool_calls entries in one response ([#12446](https://github.com/diegosouzapw/OmniRoute/pull/12446)) — thanks @hartmark +- **fix(conversations):** resolve turn content OmniRoute never sends back to the client ([#12447](https://github.com/diegosouzapw/OmniRoute/pull/12447)) — thanks @hartmark +- **fix(combo):** do not treat credits-exhausted 401 as auth skip ([#12449](https://github.com/diegosouzapw/OmniRoute/pull/12449)) — thanks @RaviTharuma +- **fix(auth):** do not park healthy quota accounts as expired ([#12452](https://github.com/diegosouzapw/OmniRoute/pull/12452)) — thanks @RaviTharuma +- **fix(responses-continuation):** fail closed on a collector-truncated, empty output array ([#12460](https://github.com/diegosouzapw/OmniRoute/pull/12460)) — thanks @hartmark +- **fix(logging):** raise the SSE payload collector's default cap ([#12461](https://github.com/diegosouzapw/OmniRoute/pull/12461)) — thanks @hartmark +- **fix(resilience):** surface Responses failed.error.message in 502s ([#12472](https://github.com/diegosouzapw/OmniRoute/pull/12472)) — thanks @RaviTharuma +- **fix(security):** unbiased maxai X-Random nonce + stricter URL/regex assertions ([#12502](https://github.com/diegosouzapw/OmniRoute/pull/12502)) +- **fix(video):** re-anchor transcript log-redaction so PII/credential maskers can't reopen the leak (#12150 P1 follow-up) ([#12503](https://github.com/diegosouzapw/OmniRoute/pull/12503)) +- **fix(monitoring):** serve cached credentialHealth off the request path ([#12533](https://github.com/diegosouzapw/OmniRoute/pull/12533)) — thanks @RaviTharuma +- **fix(quota):** keep Antigravity Gemini usable when Claude weekly is empty ([#12566](https://github.com/diegosouzapw/OmniRoute/pull/12566)) — thanks @HouMinXi +- **fix(authz):** hard-gate every credential export and CLI-config write (GHSA-5926-2w35-7h4q) ([#12600](https://github.com/diegosouzapw/OmniRoute/pull/12600)) +- **fix(docs):** restore the Next build — REMOVED_PROVIDERS.md had no frontmatter (base-red #12581) ([#12610](https://github.com/diegosouzapw/OmniRoute/pull/12610)) +- **fix(quality):** re-point the zcodeProtocol public-creds allowlist to line 313 ([#12615](https://github.com/diegosouzapw/OmniRoute/pull/12615)) +- **fix(security):** close 3 advisories — search baseUrl exfil, sk- in the error sanitizer, bifrost relay header leak ([#12620](https://github.com/diegosouzapw/OmniRoute/pull/12620)) +- **fix(providers):** sanitize boolean required and nested bare maps for Gemini (#12269) ([#12624](https://github.com/diegosouzapw/OmniRoute/pull/12624)) — thanks @HouMinXi +- **fix(resilience):** keep Overloaded STREAM_EARLY_EOF off the provider breaker ([#12626](https://github.com/diegosouzapw/OmniRoute/pull/12626)) — thanks @HouMinXi +- **fix(catalog):** re-audit free-tier quotas against official pages ([#12649](https://github.com/diegosouzapw/OmniRoute/pull/12649)) +- **fix(dashboard):** keep a request's pending-tracking id stable across combo target retries ([#12650](https://github.com/diegosouzapw/OmniRoute/pull/12650)) — thanks @hartmark +- **fix(tests):** retire dead model ids from the chat-pipeline integration suite (base-red #12581) ([#12670](https://github.com/diegosouzapw/OmniRoute/pull/12670)) +- **fix:** resolve SqliteError no such table compression_run_telemetry during cleanup ([#12682](https://github.com/diegosouzapw/OmniRoute/pull/12682)) — thanks @groovecityJO +- **fix(combo):** treat a pin-only step as implicit connection allowlist ([#12697](https://github.com/diegosouzapw/OmniRoute/pull/12697)) — thanks @HouMinXi +- **fix(ci):** ship .npmrc in published package so legacy-peer-deps applies to consumers (#11544) ([#12699](https://github.com/diegosouzapw/OmniRoute/pull/12699)) — thanks @KooshaPari +- **fix(docker):** pin 4 CLI tools to exact versions (#12576) ([#12703](https://github.com/diegosouzapw/OmniRoute/pull/12703)) — thanks @KooshaPari +- **fix(docker):** document and harden cli profile trust boundary (#12570) ([#12706](https://github.com/diegosouzapw/OmniRoute/pull/12706)) — thanks @KooshaPari +- **fix(sse):** redact video transcript in pre-guardrail rejected-request logs (#12150 P2 item 7) ([#12710](https://github.com/diegosouzapw/OmniRoute/pull/12710)) +- **fix(i18n):** quote placeholder in OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES description (#12505) ([#12769](https://github.com/diegosouzapw/OmniRoute/pull/12769)) — thanks @KooshaPari +- **fix(api/pricing):** surface validation error message as string, not raw object (#12494) ([#12771](https://github.com/diegosouzapw/OmniRoute/pull/12771)) — thanks @KooshaPari +- **fix(sse):** re-enable prompt compression for native Codex passthrough ([#12834](https://github.com/diegosouzapw/OmniRoute/pull/12834)) — thanks @thomasmaerz +- **fix(dashboard):** model health tests for a provider node set to the Responses API now call `/v1/responses` with a Responses-shaped body instead of `/v1/chat/completions` — those models were reported as `Provider returned HTTP 200 but no text content` even though the same model answered normally through `/v1/responses` ([#13070](https://github.com/diegosouzapw/OmniRoute/issues/13070)) ([#13087](https://github.com/diegosouzapw/OmniRoute/pull/13087)) — thanks @ntdatt812 +- fix(routing): stop a round-robin combo's "opencode" targets from collapsing onto the opencode-zen connection (#11912) ([#13283](https://github.com/diegosouzapw/OmniRoute/pull/13283)) +- fix(routing): stop the reactive-compaction debug log from lying when compression is globally disabled (#11977) ([#13276](https://github.com/diegosouzapw/OmniRoute/pull/13276)) +- fix(electron): relativize standalone-bundle symlink targets so Stage 8 manifest verification stops failing on Windows (#11979) ([#13251](https://github.com/diegosouzapw/OmniRoute/pull/13251)) +- fix(dashboard): surface a visible error when Compression Studio's combined preview run fails (#12061) ([#13288](https://github.com/diegosouzapw/OmniRoute/pull/13288)) +- fix(dashboard): make the compression "Effective pipeline" preview honor the active profile and warn when the master switch is off (#12063) ([#13287](https://github.com/diegosouzapw/OmniRoute/pull/13287)) +- fix(providers): scope TinyCMS Web signer's DOM shims to each call instead of leaking them for the process lifetime, and surface a clean HTTP status on a non-JSON interception-toggles error (#12072) ([#13254](https://github.com/diegosouzapw/OmniRoute/pull/13254)) +- fix(guardrails): stop Vision Bridge from re-selecting a model locked after a 404 (#12111) ([#13259](https://github.com/diegosouzapw/OmniRoute/pull/13259)) +- fix(sse): require Responses-shaped body before native OpenAI-compatible passthrough (#12129) ([#13278](https://github.com/diegosouzapw/OmniRoute/pull/13278)) +- fix(providers): minimax-m3 now collapses manual thinking.type:"enabled" to adaptive, preventing upstream 400 (2013) (#12132) ([#13249](https://github.com/diegosouzapw/OmniRoute/pull/13249)) +- fix(db): scope model visibility overrides by modality so hiding a Chat model no longer hides an identically-ID'd Image/Embeddings/etc. model (#12172) ([#13258](https://github.com/diegosouzapw/OmniRoute/pull/13258)) +- fix(db): scope local-provider apiKey dedup to matching base URL so LM Studio/Ollama-style connections support multiple accounts (#12173) ([#13250](https://github.com/diegosouzapw/OmniRoute/pull/13250)) +- fix(providers): refresh Trae's stale Referer/Origin and forward user timezone so imported connections stop failing with 401 (#12190) ([#13255](https://github.com/diegosouzapw/OmniRoute/pull/13255)) +- fix(providers): route opencode-go/gpt-5.6-luna to /responses instead of /chat/completions (#12196) ([#13275](https://github.com/diegosouzapw/OmniRoute/pull/13275)) +- fix(dashboard): allow deleting the last extra-upstream-header row even when invalid (#12251) ([#13252](https://github.com/diegosouzapw/OmniRoute/pull/13252)) +- **fix(i18n):** translate pre-existing `__MISSING__:` keys for `combo.sort`, `requestLogger.detail` expand/collapse, `common.profile`, and `settings.resilienceCredentialHealth*` across 39 locales ([#12272](https://github.com/diegosouzapw/OmniRoute/issues/12272)) ([#13034](https://github.com/diegosouzapw/OmniRoute/pull/13034)) — thanks @HouMinXi +- fix(cli): run the Node.js runtime compatibility guard before the heavy `tsx/esm` + Commander import chain so an unsupported runtime gets a clear message instead of a raw `Invalid regular expression flags` crash (#12296) ([#13253](https://github.com/diegosouzapw/OmniRoute/pull/13253)) +- fix(dashboard): refresh the providers list after deleting a compatible provider node (#12298) ([#13256](https://github.com/diegosouzapw/OmniRoute/pull/13256)) +- fix(usage): fail closed on API-key budget enforcement when a provider's `auto` routing alias has no pricing row, instead of silently counting it as $0 (#12341) ([#13257](https://github.com/diegosouzapw/OmniRoute/pull/13257)) +- **fix(providers):** include the submitted Agnes video model when polling by `video_id` ([#12356](https://github.com/diegosouzapw/OmniRoute/pull/12356)) — thanks @pacocartones +- **fix(routing):** custom OpenAI-compatible nodes now honor the saved Chat/Responses API type after edits instead of letting the node's original ID prefix override the live connection setting ([#11884](https://github.com/diegosouzapw/OmniRoute/issues/11884)). ([#12358](https://github.com/diegosouzapw/OmniRoute/pull/12358)) — thanks @pacocartones +- fix(sse): surface an error instead of a silent empty 200 when a Claude stream closes with zero bytes (#12398) ([#13285](https://github.com/diegosouzapw/OmniRoute/pull/13285)) +- fix(oauth): warn before the dead localhost:8080 redirect in antigravity/gemini `oauth start` (#12413) ([#13265](https://github.com/diegosouzapw/OmniRoute/pull/13265)) +- fix(providers): stop devin-cli spawn error from double-closing the SSE controller (#12517) ([#13261](https://github.com/diegosouzapw/OmniRoute/pull/13261)) +- **fix(rerank):** clamp Voyage `top_k` to the documents actually sent after empty-string filtering, and honor `return_documents: false` in the NVIDIA response adapter (#12523 — thanks @pacocartones) +- **fix(audio):** `/v1/audio/translations` now resolves combo names the way `/v1/audio/transcriptions` already does, so a combo that `GET /v1/models` advertises is fanned out to its targets instead of being rejected with `400 Invalid translation model: . Use format: provider/model`; literal `provider/model` ids and unknown bare names behave as before (#12536 — thanks @pacocartones) +- **fix(gemini):** strip the JSON-Schema-2020-12 `prefixItems` keyword from Gemini tool schemas at every nesting level, so Claude Code tool definitions no longer fail with `400 Unknown name "prefixItems"` on Gemini models (#12540 — thanks @pacocartones) +- **fix(api-manager):** Expose an accessible loading status while API keys are fetched instead of an empty accessibility tree (#12541 — thanks @pacocartones) +- **fix(video):** Clamp `estimateJpegFrameBytes` at zero for padding-only payloads and build the three encode-side frame data URIs from `JPEG_FRAME_DATA_URI_PREFIX` instead of a repeated literal (#12543 — thanks @pacocartones) +- **fix(devin):** accept Windows `DEVIN_AGENTIC_HOME` sandbox paths (`C:\...\.sandbox\...`) in the isolated-home check so the Devin Claude Bridge no longer fails closed on Windows ([#12405](https://github.com/diegosouzapw/OmniRoute/issues/12405)) (#12545 — thanks @pacocartones) +- **fix(db):** Add `conversation_turn_nodes` and orphaned `agentic_conversations` to the auto-cleanup cycle under the existing `retention.callLogs` window, so identity nodes whose call-log content has already been purged no longer accumulate without bound in `storage.sqlite` (#12548 — thanks @pacocartones) +- **fix(i18n):** Wrap the `~/.claude/profiles//settings.json` placeholder in ICU single quotes in the `OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES` feature-flag description across all 42 locales and the TypeScript default, so next-intl no longer fails with `INVALID_MESSAGE: UNCLOSED_TAG` and the Feature Flags card shows the description instead of the raw key (#12549 — thanks @pacocartones) +- **fix(orchestration):** `updateCloudAgentTask` now publishes the task's real `status` on `agent.task.updated` when an update only touches `result`, `activities` or `error`, instead of the fabricated `"updated"` state, and stays silent when no row matched the id (#12550 — thanks @pacocartones) +- **fix(i18n):** the home "Recent Requests" panel and the Provider Topology legend are now translated instead of rendering English copies on non-English dashboards; the legend reads its own `home.topologyLegend*` labels with consistent casing rather than borrowing the memory-settings "Recent" and analytics "Error" strings (#12551 — thanks @pacocartones). +- **docs(reference):** bring the `FEATURE_FLAGS.md` catalog back to 1:1 with `featureFlagDefinitions.ts` — 20 missing flags added, the two `*_BLOCK_THRESHOLD` env-only knobs moved out of the flag tables, category/total counts and the Live WS port corrected, guarded by a static test (#12552 — thanks @pacocartones) +- **fix(i18n):** backfill missing `usage.kiloPass*` strings in 39 locales and restore `featureFlagOmnirouteDisableThinkingLevelVariantsDescription` in `pt.json` ([#12561](https://github.com/diegosouzapw/OmniRoute/issues/12561)) ([#13027](https://github.com/diegosouzapw/OmniRoute/pull/13027)) — thanks @HouMinXi +- fix(docker): default docker-compose app ports (dashboard/API/live-WS) to loopback instead of `0.0.0.0`, closing the anonymous `/v1` LAN/WAN exposure gap left open by `REQUIRE_API_KEY=false` (#12568) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): close DNS-rebinding SSRF gap in webhook outbound-URL guard (#12569) ([#13243](https://github.com/diegosouzapw/OmniRoute/pull/13243)) +- fix(docker): require a per-session token on the VNC browser CDP bridge and isolate it on a dedicated Docker network (#12571) ([#13241](https://github.com/diegosouzapw/OmniRoute/pull/13241)) +- fix(open-sse): write Adobe Firefly session tokens and cookie jars with 0700/0600 permissions instead of the process umask (#12572) ([#13242](https://github.com/diegosouzapw/OmniRoute/pull/13242)) +- fix(api): remove hardcoded wildcard CORS in openai-to-gemini-sse.ts so the centralized fail-closed CORS gate is the sole source of `Access-Control-Allow-Origin` (#12573) ([#13239](https://github.com/diegosouzapw/OmniRoute/pull/13239)) +- fix(api): enforce API key policy (budget/rate-limit/schedule/endpoint scoping) on the ElevenLabs speech-to-text, text-to-speech and voices proxy routes (#12574) ([#13244](https://github.com/diegosouzapw/OmniRoute/pull/13244)) +- fix(sse): cap HuggingChat NDJSON body size and bound the read loop with the fetch timeout so a stalled or hostile upstream cannot buffer unbounded memory (#12577) ([#13282](https://github.com/diegosouzapw/OmniRoute/pull/13282)) +- fix(docker): scope the cliproxyapi/qdrant/bifrost sidecars to loopback by default and forward `CLIPROXYAPI_MANAGEMENT_KEY` into the cliproxyapi container so its management API is not left both unauthenticated and LAN-published (#12578) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): create DB export temp paths with `fs.mkdtempSync` instead of predictable timestamps (#12579) ([#13237](https://github.com/diegosouzapw/OmniRoute/pull/13237)) +- Cline 401 bodies that say "re-authenticate your Cline account" classify as a refreshable OAuth token, not a terminal expired key. The cooling panel no longer labels every cooldown as a 429; it shows the recorded last error instead. (#12594) ([#13060](https://github.com/diegosouzapw/OmniRoute/pull/13060)) — thanks @HouMinXi +- **fix(catalog):** degrade unknown combo targets instead of dropping LCD modalities, and persist OpenRouter `architecture.input_modalities` into the capability snapshot ([#12613](https://github.com/diegosouzapw/OmniRoute/issues/12613)) ([#13042](https://github.com/diegosouzapw/OmniRoute/pull/13042)) — thanks @HouMinXi +- fix(providers): send `x-api-key` instead of `Authorization: Bearer` for OpenCode Zen's `/v1/responses` endpoint (Muse Spark Contributor models), fixing a 401 on OmniRoute's auth header (#12633) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(sse): surface the actionable "Auggie CLI not found" message when the shell reports a missing `auggie` binary via exit code instead of a spawn error (#12645) ([#13263](https://github.com/diegosouzapw/OmniRoute/pull/13263)) +- **fix(config):** Persist deletions of built-in background-degradation entries — when a stored settings record exists its `degradationMap` is now authoritative instead of being merged under the defaults, so an entry the user removed in the dashboard no longer reappears on the next apply or restart ([#12424](https://github.com/diegosouzapw/OmniRoute/issues/12424)) ([#12647](https://github.com/diegosouzapw/OmniRoute/pull/12647)) — thanks @pacocartones +- **fix(gamification):** action-count badge milestones (First Token, Token Consumer, Token Machine, Token Whale, and the token-sharing tier) are now backed by a durable `xp_action_counts` counter incremented in `addXp()`, instead of a live `COUNT(*)` over `xp_audit_log`. The audit log is pruned by `retention.xpAuditLog` (default 30 days), so on a default install those "lifetime" milestones were really "actions in the last 30 days" and unlocked badges could stop unlocking once old rows aged out. `getActionCount()` and `checkActionCountBadges()` now read the same durable source, and a migration backfills existing totals from the surviving audit rows ([#12546](https://github.com/diegosouzapw/OmniRoute/issues/12546)) ([#12651](https://github.com/diegosouzapw/OmniRoute/pull/12651)) — thanks @pacocartones +- **fix(images):** `/v1/images/edits` now iterates a combo's targets the same way `/v1/images/generations` does (#9239) instead of flattening a bare combo to its first target. A combo whose first target is not edit-capable — or lacks credentials — now falls through to a later edit-capable target rather than hard-erroring, and missing credentials are skipped (not a hard `401`) to match the generations path. The per-target skip/terminal classification is extracted into a shared `runImageComboTargets` loop, so generations behavior is unchanged ([#12547](https://github.com/diegosouzapw/OmniRoute/issues/12547)). ([#12653](https://github.com/diegosouzapw/OmniRoute/pull/12653)) — thanks @pacocartones +- fix(sse): add first-byte watchdog to the TLS-fingerprint transport so a stalled wreq body falls back instead of hanging for minutes (#12656) ([#13272](https://github.com/diegosouzapw/OmniRoute/pull/13272)) +- fix(sse): exempt tiny-budget reasoning probes from combo quality failure and surface persisted-cooldown skips in ALL_TARGETS_SKIPPED diagnostics (#12659) ([#13269](https://github.com/diegosouzapw/OmniRoute/pull/13269)) +- fix(models): declare the real ~1M contextLength for OpenCode Zen's Muse Spark 1.2 models instead of falling back to the 200000 provider default (#12681) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(oauth): align codebuddy-cn OAuth User-Agent with the chat/usage CLI version to avoid WAF false positives (#12702) ([#13264](https://github.com/diegosouzapw/OmniRoute/pull/13264)) +- fix(dashboard): surface an authentication-required banner instead of silently blanking database settings for a guest session (#12709) ([#13270](https://github.com/diegosouzapw/OmniRoute/pull/13270)) +- **fix(resilience):** stop unbounded queue that hangs 6min until Aborted — gate, provider slot, and Bottleneck queue now share a per-connection `maxWaitMs` budget; `fail-closed` on exhaust (503); execution backstop `executionMaxWaitMs` overridable per connection with upstream clamp ([#12715](https://github.com/diegosouzapw/OmniRoute/pull/12715)) — thanks @maxmad64bis (with thanks to @Tushar49 for surfacing the slow-provider need in #12635) +- **fix(auto-combo):** every mode pack now carries `quality` and `reliability` weights (`quality` 0.02, 0.03 in `quality-first`; `reliability` 0.03, 0.04 in `reliability-first`), so selecting a pack no longer silences either signal; `DEFAULT_WEIGHTS` is unchanged ([#12731](https://github.com/diegosouzapw/OmniRoute/pull/12731)) — thanks @maxmad64bis +- **fix(sse):** the provider execution pipeline keeps the upstream error `code`/`type` again — both were lost when the non-streaming leg was extracted, so a config-class `422` (Antigravity missing project) degraded into a generic account cooldown instead of reaching the gate that recognises it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(db):** a legacy `call_logs` table no longer aborts startup — the provider-stats index is created after the column healing runs, not before it, so an install predating `request_type` opens instead of failing with `no such column` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(catalog):** a malformed row in the operator-writable custom-models blob no longer kills every `auto/*` pool ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(tests):** realign the guards that the same day's merges left asserting the old behavior — the provider-count total after GreenPT and EURouter, the GLM stream buffer slot that `#12925` turned into a declared parameter, and the injection scan bound that `#13104` reshaped from a head-only window into head-plus-tail ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- fix(cache): fold tool_choice/tools/response_format into the semantic cache signature so a cached tool_calls response can no longer be replayed for a request whose tool policy forbids it (#12734) ([#13267](https://github.com/diegosouzapw/OmniRoute/pull/13267)) +- **fix(models):** stop treating provider-supplied `isFree:true`/`:free`/`0/0` flags as trusted unless the provider has a documented free tier — only the shipped free-tier catalog decides otherwise; fetched `isFree:true` trusted only for a free-tier provider, custom `isFree:true` via a trusted path ([#12744](https://github.com/diegosouzapw/OmniRoute/pull/12744)) — thanks @maxmad64bis +- fix(memory): authenticate the internal /v1/rerank loopback call so memory reranking no longer silently degrades to unranked order when REQUIRE_API_KEY=true (#12745) ([#13268](https://github.com/diegosouzapw/OmniRoute/pull/13268)) +- fix(sse): parse Ollama Cloud's current usage markup (`$X of $Y used` aria-label, nested width style) (#12749) ([#13260](https://github.com/diegosouzapw/OmniRoute/pull/13260)) +- fix(cli): setup-opencode no longer sends an active context's management token to `/v1/models` when `--api-key`/`OMNIROUTE_API_KEY` is supplied — an explicit flag or the env var now always outranks the context's token, and the flag itself is no longer swallowed by the parent program's global `--api-key` option (#12783) ([#13246](https://github.com/diegosouzapw/OmniRoute/pull/13246)) +- fix(sse): register Arcee AI in the executor provider registry so requests reach api.arcee.ai instead of silently falling back to OpenAI (#12784) ([#13277](https://github.com/diegosouzapw/OmniRoute/pull/13277)) +- **fix(quota):** share one upstream quota read when identical saturation checks arrive at the same time, so traffic bursts don't multiply provider API calls ([#12787](https://github.com/diegosouzapw/OmniRoute/pull/12787)) — thanks @maxmad64bis +- **fix(db):** ignore expired or invalid rate-limit cooldown writes so a stale timestamp can't lock a connection that should be usable — clearing still works as before ([#12788](https://github.com/diegosouzapw/OmniRoute/pull/12788)) — thanks @maxmad64bis +- **fix(providers):** single source for provider order with `xao` ranking alongside `xai-oauth` ([#12790](https://github.com/diegosouzapw/OmniRoute/pull/12790)) — thanks @maxmad64bis +- **fix(routing):** off-table models check the free-model catalog before inheriting premium prices, latency bootstraps from the observed pool median, async tiers read live database pricing with a 90-day freshness gate, and the tier cache invalidates on every pricing write ([#12792](https://github.com/diegosouzapw/OmniRoute/pull/12792)) — thanks @maxmad64bis +- **fix(auto-combo):** snapshot scoring uses the observed per-connection account tier and per-model quality instead of neutral constants, and circuit-open providers rank lower at build time ([#12794](https://github.com/diegosouzapw/OmniRoute/pull/12794)) — thanks @maxmad64bis +- **fix(pool):** empty `auto/*` pools now say why they are empty, and the models listing uses the same paid check as routing ([#12795](https://github.com/diegosouzapw/OmniRoute/pull/12795)) — thanks @maxmad64bis +- fix(routing): recognize CLIProxyAPI's 'unknown provider for model' 400 as fallback-worthy (#12800) ([#13284](https://github.com/diegosouzapw/OmniRoute/pull/13284)) +- **fix(sse):** translate-mode streams now emit the estimated token counts as a trailing usage-only chunk before `[DONE]` when the upstream stays silent, so metered chat clients see totals instead of nothing ([#12828](https://github.com/diegosouzapw/OmniRoute/pull/12828)) — thanks @maxmad64bis +- **fix(db):** provider stats stay truthful on empty and legacy databases: fallback counts default to `0` instead of `null`, latency averages read `null` (not `0`) when no durations were recorded, failures logged before the error-type column existed group under `pre_migration` instead of `unclassified`, and per-provider queries use two new composite indexes ([#12832](https://github.com/diegosouzapw/OmniRoute/pull/12832)) — thanks @maxmad64bis +- fix(nvidia): fail open when a synced model catalog goes stale instead of gating forever (#12849) ([#13248](https://github.com/diegosouzapw/OmniRoute/pull/13248)) +- **fix(db):** WAL maintenance lives in its own module: a `TRUNCATE` checkpoint that hits a busy database now warns with its streak and retries once via `PASSIVE` instead of logging success, and closing-time checkpoints no longer report success on builds without a database file, with the busy totals visible in the authenticated monitoring health payload ([#12853](https://github.com/diegosouzapw/OmniRoute/pull/12853)) — thanks @maxmad64bis +- **fix(health):** quota with no snapshots reads empty instead of a contradicting 0% ([#12857](https://github.com/diegosouzapw/OmniRoute/pull/12857)) — thanks @maxmad64bis +- fix(a2a): accept the dashboard's own session cookie on /a2a so "Run message/send" no longer fails with "Unauthorized: missing or invalid API key" (#12888) ([#13271](https://github.com/diegosouzapw/OmniRoute/pull/13271)) +- **fix(logs):** the Logs grid's in-memory filter pass no longer discards rows the SQL query already matched — selecting an API key from the dropdown (which sends the key's id) returns its calls again, the Combo tab shows every combo instead of only those whose name contains a "1", and the model filter and search cover the same columns as the query ([#12896](https://github.com/diegosouzapw/OmniRoute/pull/12896)) — fixes [#12873](https://github.com/diegosouzapw/OmniRoute/issues/12873) — thanks @ntdatt812 +- **fix(a2a):** `/api/a2a/status` now builds the agent card from the request that asked for it, so a gateway reached at a non-localhost host no longer advertises `http://localhost:20128` as its A2A URL ([#12918](https://github.com/diegosouzapw/OmniRoute/pull/12918)). — thanks @ntdatt812 +- **fix(compression):** progressive aging now appends its `[COMPRESSED:aging:…]` annotation after a turn's `tool_result` blocks instead of in front of them, so Anthropic no longer rejects aged conversations with "`tool_use` ids were found without `tool_result` blocks immediately after" ([#12920](https://github.com/diegosouzapw/OmniRoute/pull/12920)). — thanks @ntdatt812 +- **fix(bedrock):** model import now resolves context limits for every vendor prefix instead of only `anthropic.*`, so `global.openai.gpt-5.6-*` no longer imports with a null `inputTokenLimit` and gets rejected pre-flight at the 200k default ([#12921](https://github.com/diegosouzapw/OmniRoute/pull/12921)). — thanks @ntdatt812 +- **fix(stream):** the 64 KB stream buffer GLM asks for is honoured instead of dropped, and the type error it caused no longer fails the API Route Typecheck gate on every open PR ([#12925](https://github.com/diegosouzapw/OmniRoute/pull/12925)) — thanks @ntdatt812 +- **fix(guardrails):** mask PII inside a `tool_result`'s nested content array, which the masker walked past while redacting its sibling block ([#12930](https://github.com/diegosouzapw/OmniRoute/pull/12930)) — thanks @ntdatt812 +- **fix(dashboard):** the Radar catalog table no longer leaves absent data unexplained — empty limits, unknown context windows, and unreported capabilities each explain themselves on hover, and a new check keeps it that way ([#12937](https://github.com/diegosouzapw/OmniRoute/pull/12937)) — thanks @maxmad64bis +- **fix(sse):** geo-blocked opencode requests rotate to the next account proxy instead of failing, so one refused egress no longer aborts the whole chain ([#12941](https://github.com/diegosouzapw/OmniRoute/pull/12941)) — thanks @maxmad64bis +- **fix(codex):** dashboard "clear cooldown" and the CAS recovery path now drop nested `codexScopeRateLimitedUntil` maps in the same write that nulls `rate_limited_until`, and fresh quota snapshots with headroom lift fallback-sourced scope cooldowns parked by quota preflight ([#12817](https://github.com/diegosouzapw/OmniRoute/issues/12817), [#12860](https://github.com/diegosouzapw/OmniRoute/issues/12860), [#12951](https://github.com/diegosouzapw/OmniRoute/pull/12951)) The dashboard reset-credit button (`consumeCodexResetCredit`) now exercises that same snapshot path after a successful redeem, so a filled quota bar is enough to unpark a leftover fallback Codex child without another manual clear. — thanks @HouMinXi +- **fix(db):** resolve `sql-wasm.wasm` across global npm install and hoisted layouts, ensuring OmniRoute can boot cleanly on Node 24 when native `better-sqlite3` is uncompiled. ([#13035](https://github.com/diegosouzapw/OmniRoute/pull/13035)) — thanks @HouMinXi +- **fix(api):** `DELETE /v1/batches/delete-completed` now sweeps only the calling API key's own completed batches (batches with no owner stay out of a key-scoped sweep on purpose), with an explicit instance-wide mode reserved for authenticated dashboard sessions, a 401 for a presented key that is unknown, revoked, deactivated, banned or expired (never falling through to the session branch), audit logging of every sweep, a sanitized 500 on failure, an owner-scoped file half (a key-scoped sweep never nulls a file another tenant owns) and an atomic sweep — chunked in 200-batch transactions in instance mode — so a mid-way error never leaves a batch pointing at a nulled file (GHSA-wvxc-jp3v-5mg5) ([#12969](https://github.com/diegosouzapw/OmniRoute/pull/12969)) +- **fix(combo):** quota-weighted routing stops drawing on an out-of-credit connection — a 402 now invalidates the stored quota snapshot instead of leaving its stale remaining percentage in place, and a snapshot older than 10 minutes no longer counts as confident headroom for the primary pool ([#12972](https://github.com/diegosouzapw/OmniRoute/pull/12972)) ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- **fix(vision):** Custom Models with "Vision capable" checked no longer have image requests swapped to `glm/glm-4.6v` when the client sends the advertised alias (`vllm/path/...`) or the bare path-shaped id — Vision Bridge now matches the stored override for all three id forms ([#12758](https://github.com/diegosouzapw/OmniRoute/issues/12758)) ([#12974](https://github.com/diegosouzapw/OmniRoute/pull/12974)) — thanks @HouMinXi +- **fix(sse):** transient opencode upstream failures rotate to the next account proxy instead of failing, so one flapping egress no longer aborts the whole chain ([#12975](https://github.com/diegosouzapw/OmniRoute/pull/12975)) — thanks @maxmad64bis +- **fix(azure):** Deployments from GPT-6 onward now send `max_completion_tokens` instead of `max_tokens`, which Azure rejects with HTTP 400. The rule matched a literal `gpt-5`, so each new generation arrived broken; it now matches the generation range, while `gpt-35-turbo` still keeps `max_tokens`. ([#13007](https://github.com/diegosouzapw/OmniRoute/pull/13007)) — thanks @ntdatt812 +- **fix(db):** add an opt-in self-restart circuit for sustained critical memory pressure, gate post-cleanup VACUUM behind a minimum freed-rows threshold, and checkpoint the SQLite WAL every 5 minutes with a size guard that escalates to TRUNCATE, so a growing WAL can no longer stall the event loop into a full outage. ([#13011](https://github.com/diegosouzapw/OmniRoute/pull/13011)) — thanks @HouMinXi +- **fix(auth):** an explicit connection pin may probe a quota-disabled row once and re-enable it on success ([#12874](https://github.com/diegosouzapw/OmniRoute/issues/12874)) ([#13017](https://github.com/diegosouzapw/OmniRoute/pull/13017)) — thanks @HouMinXi +- **fix(api):** thread `X-OmniRoute-Fallback-Attempts` through combo chat completions so streaming and non-streaming responses report how many prior legs were attempted ([#13038](https://github.com/diegosouzapw/OmniRoute/pull/13038)) — thanks @HouMinXi +- Restore provider failure classification and credential refresh on non-streaming requests: classify non-2xx failures to lock models on per-model quota exhaustion, update connection rate limits from headers and body, and pass credential refresh handlers to pipeline execution so 401 tokens can be refreshed and retried (#13043). ([#13069](https://github.com/diegosouzapw/OmniRoute/pull/13069)) — thanks @HouMinXi +- **fix(responses):** wrap forced-non-streaming web_search fallback JSON as Responses SSE so Codex still sees `response.completed` ([#13050](https://github.com/diegosouzapw/OmniRoute/pull/13050)) — thanks @HouMinXi +- **fix(antigravity):** Preserve upstream thought-token usage in normalized completion and reasoning token counts ([#13055](https://github.com/diegosouzapw/OmniRoute/pull/13055)) — thanks @pacocartones +- Fix a concurrency-slot leak in the DeepSeek PoW solver: a worker that failed to spawn (for example a missing worker script) never released its slot, so `MAX_CONCURRENT_WORKERS` failures disabled the solver until restart. ([#13097](https://github.com/diegosouzapw/OmniRoute/pull/13097)) — thanks @anhtahaylove +- **fix(acp):** bound the ACP session output buffers — `stdoutBuffer` and `stderrBuffer` now cap at 1 MiB keeping the most recent output behind a visible `[...output truncated...]` marker, and `stderrBuffer` is reset per prompt instead of accumulating for the lifetime of the session. ([#13100](https://github.com/diegosouzapw/OmniRoute/pull/13100)) — thanks @anhtahaylove +- **fix(acp):** release the `stdout`/`exit` listeners and the idle timer that a `sendPrompt` timeout used to leave attached to the `acpManager` singleton, and drop sessions that exited on their own from the session map instead of keeping them forever. ([#13096](https://github.com/diegosouzapw/OmniRoute/pull/13096)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection and PII scanners now read the text a `tool_result` block carries on `content` (string or nested block list), in messages and in system blocks, so tool output is judged by the same rules as user text ([#13101](https://github.com/diegosouzapw/OmniRoute/pull/13101)) — thanks @ntdatt812 +- **fix(gamification):** close the badge notification SSE stream when the request signal is already aborted before the stream starts — a client that disconnects while the route is still awaiting auth used to leave both the 2s unlock poll and the 15s heartbeat running for the lifetime of the process. ([#13106](https://github.com/diegosouzapw/OmniRoute/pull/13106)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection scan now spends its 16 KB budget on both ends of the request instead of the first 16 KB only, so `system`, `instructions`, `query`, `documents` and the newest turns are no longer hidden behind one long message ([#13104](https://github.com/diegosouzapw/OmniRoute/pull/13104)) — thanks @ntdatt812 +- **fix(dashboard):** expose the Volcano Ark console cookie on quota scraping and unwrap connect-error objects so the dashboard shows the upstream message ([#13107](https://github.com/diegosouzapw/OmniRoute/pull/13107)) — thanks @HouMinXi +- **fix(db):** release the `beforeExit`/`SIGINT`/`SIGTERM` handlers when a `node:sqlite` adapter closes, so a closed adapter and its database handle are no longer pinned to `process` for the lifetime of the run — the same treatment #7494 gave the sql.js adapter. ([#13109](https://github.com/diegosouzapw/OmniRoute/pull/13109)) — thanks @anhtahaylove +- **fix(translator):** `contentSchema` and `unevaluatedItems` are now treated as subschema positions by the tool-schema sanitizer, so a truncation placeholder in either is replaced with a permissive schema instead of being forwarded as a string ([#13110](https://github.com/diegosouzapw/OmniRoute/pull/13110)) — thanks @ntdatt812 +- **fix(cli-helper):** clear the `createLogStream` timeout on the abort path — `stop()` aborts the in-flight fetch and returned through the `signal.aborted` branch, which skipped `clearTimeout` and left an armed timer per stopped stream. The stream reader is now also cancelled when the read loop exits early. ([#13114](https://github.com/diegosouzapw/OmniRoute/pull/13114)) — thanks @anhtahaylove +- **fix(docs):** drop TLS-impersonation, MITM-decrypt, supply-chain attestation, and XOR-mask writeups from the public `/docs` catalog and Docker image. Files stay in git for engineers; operators who need them open the repo, not the website. ([#13136](https://github.com/diegosouzapw/OmniRoute/pull/13136)) — thanks @HouMinXi +- **fix(combo):** parse numeric-epoch `rate_limited_until` in the combo cooldown read path ([#13141](https://github.com/diegosouzapw/OmniRoute/pull/13141)) — thanks @maxmad64bis +- **fix(opencode):** both OpenCode plugins now reject a gateway address typed without `http://` at configuration time, instead of publishing every model with an api url no client can call, and the v2 plugin no longer publishes a model card whose api url is blank or relative ([#13142](https://github.com/diegosouzapw/OmniRoute/pull/13142)) — thanks @maxmad64bis +- **fix(providers):** lock opencode model on upstream 400 model-unavailable ([#13146](https://github.com/diegosouzapw/OmniRoute/pull/13146)) — thanks @maxmad64bis +- **fix(logging):** keep the provider exchange rather than the raw client bodies when a call log exceeds its size budget, and show that recovered payload in the request-detail panel instead of replacing it with the stored response body ([#13147](https://github.com/diegosouzapw/OmniRoute/pull/13147)) — thanks @maxmad64bis +- **fix(traffic-inspector):** the WebSocket route no longer leaks a traffic-buffer subscriber and a 30s ping timer when the client socket is already closed at handler time — listeners are attached before any resource is acquired, a destroyed socket bails out early, and the ping interval stops on a dead socket where `write()` never throws ([#13155](https://github.com/diegosouzapw/OmniRoute/pull/13155)) — thanks @anhtahaylove +- **fix(telegram):** bound the per-user API key cache in the Telegram chat proxy so a burst of distinct chat ids can no longer grow the process heap without limit ([#13165](https://github.com/diegosouzapw/OmniRoute/issues/13165)) ([#13166](https://github.com/diegosouzapw/OmniRoute/pull/13166)) — thanks @anhtahaylove +- **fix(stream):** cancel the upstream response body when the JSON-to-SSE sniff unwinds on a body timeout, so a stalled upstream no longer pins the connection ([#13169](https://github.com/diegosouzapw/OmniRoute/issues/13169)) ([#13171](https://github.com/diegosouzapw/OmniRoute/pull/13171)) — thanks @anhtahaylove +- **fix(telegram):** authenticate webhook deliveries with Telegram's `secret_token` so an unauthenticated caller can no longer mint API keys or spend upstream quota ([#13172](https://github.com/diegosouzapw/OmniRoute/issues/13172)) ([#13175](https://github.com/diegosouzapw/OmniRoute/pull/13175)) — thanks @anhtahaylove +- **fix(models):** give discoverable Gemini 3.8 Flash ids their own 65536 output spec so Antigravity no longer clamps them to 16384 ([#13195](https://github.com/diegosouzapw/OmniRoute/pull/13195)) — thanks @HouMinXi +- **fix(dashboard):** leftover catalog-removed provider rows (gemini-cli) can be listed and purged from the providers page ([#13067](https://github.com/diegosouzapw/OmniRoute/issues/13067)) ([#13197](https://github.com/diegosouzapw/OmniRoute/pull/13197)) — thanks @HouMinXi +- **fix(auth):** a dashboard session now requires the `authenticated: true` claim that login, OIDC and the session refresh already emit — a JWT merely signed with `JWT_SECRET` (for example the Cursor CLI passthrough token, which any API-key holder can obtain) no longer verifies as the `auth_token` cookie on any route, the WebSocket handshake or the live server; existing sessions keep working ([#13298](https://github.com/diegosouzapw/OmniRoute/issues/13298)) ([#13375](https://github.com/diegosouzapw/OmniRoute/pull/13375)) +- **fix(api):** `DELETE /v1/batches/delete-completed` now applies the caller's API-key policy (endpoint allowlist, schedule, usage cap, rate limit) like every other `/v1` route, commits the key-scoped sweep per 200-batch chunk instead of holding one write lock for the whole sweep, refuses to loop without progress, and rejects a scope that names both a key and `allTenants` ([#13684](https://github.com/diegosouzapw/OmniRoute/pull/13684)) +- Restore the API-route typecheck gate: the non-streaming leg result lost its discriminated-union narrowing after the server-owned tool loop reassignment, producing 13 new TS2339 diagnostics in `chatCore.ts`. ([#12963](https://github.com/diegosouzapw/OmniRoute/pull/12963)) +- Restore four non-streaming behaviours the server-owned tool loop refactor dropped: client aborts map to 499 with the fixed `Request aborted` message, an aborted request no longer logs a synthetic `clientResponse`, Claude prompt-cache telemetry is recorded again, and a body that cannot be canonicalized no longer throws when the tool loop is off. ([#12990](https://github.com/diegosouzapw/OmniRoute/pull/12990)) +- **fix(skills):** The CLI registry parser now reads positionals declared with `.addArgument()`, not only those written inline in `.command()`. `tunnel create [type]` was being published as `tunnel create`, so the agent-skills sync gate reported drift on every branch and regenerating would have deleted the argument. ([#13009](https://github.com/diegosouzapw/OmniRoute/pull/13009)) — thanks @ntdatt812 +- **fix(combos):** dashboard combo test uses a short prompt, serial probes, and a 60s timeout so reasoning models and rate-limited free pools do not fail the health check ([#13001](https://github.com/diegosouzapw/OmniRoute/pull/13001)) — thanks @HouMinXi +- fix(compression): terminate idle worker threads on eviction so long-running instances stop leaking OS threads and MessagePorts ([#13091](https://github.com/diegosouzapw/OmniRoute/pull/13091)) — thanks @anhtahaylove +- Redact Google API keys of any length in error bodies: the pattern required exactly 39 characters, so shorter or longer `AIza…` credentials passed through unredacted. ([#12964](https://github.com/diegosouzapw/OmniRoute/pull/12964)) +- **fix(grok-cli):** a 402 "Grok Build usage balance exhausted" parks that Grok login as out of credit (Grok Build CLI, grok.com cookie, and xAI OAuth share the weekly pool). Combo routing then tries the next login instead of locking the model for every account in the pool ([#13061](https://github.com/diegosouzapw/OmniRoute/pull/13061)) — thanks @HouMinXi +- fix(compression): spawn the LLMLingua worker with a file URL object so compression actually runs instead of silently failing open on Node ([#13093](https://github.com/diegosouzapw/OmniRoute/pull/13093)) — thanks @anhtahaylove +- **fix(test):** run the local `test` and `test:unit` scripts at concurrency 4 so a full-suite run no longer exhausts the machine's commit charge and kills unrelated processes ([#13187](https://github.com/diegosouzapw/OmniRoute/pull/13187)) — thanks @anhtahaylove +- **fix(monitoring):** `GET /api/monitoring/health` `credentialHealth` now includes a bounded `failedConnections` list (`connectionId`, `status`, sanitized `lastError`) when the probe-cache gauge `failed>0`, plus `source: probe-cache` and a cheap `staleDbNonOkCount` for sticky SQLite `test_status` on active rows. Documents that the live gauge is not `provider_connections.test_status`. ([#12876](https://github.com/diegosouzapw/OmniRoute/pull/12876)) — thanks @RaviTharuma +- **fix(resilience):** an openai-compatible multi-upstream gateway no longer marks the whole connection `credits_exhausted` when a single upstream (or the `/models` / representative-model health probe) returns 402. Quota failures stay model-scoped; true connection-wide auth failures are unchanged. Single-credential 402 key disable ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239)) is preserved. ([#12875](https://github.com/diegosouzapw/OmniRoute/pull/12875)) — thanks @RaviTharuma +- **fix(dashboard):** nine fixes on the Orchestration Canvas, all diagnosed in the Phase 2 reviews. A source that fails now keeps the timestamp of its FIRST failure instead of being re-stamped every poll — the stale line said "since the last poll" no matter how long the source had been down, and the churn also defeated the snapshot's stable identity (it serializes the source list), so the canvas re-rendered on every tick while anything was broken. A source that HAD data and only then started failing is flagged too: previously only a source with no node at all got the warning, so a source that went down mid-session kept a healthy-looking node forever. The rest are pointwise: clicking a filter chip cancels the pending search debounce (left armed, it fired ~300ms later and silently reverted the chip); the search input carries an accessible name; `?state=running, failed` parses like the unpadded form instead of dropping the padded value; the CSV toggle helper is defined once in `model/urlParams.ts` rather than twice; the agents tab tells "nothing running" apart from "the filter matched nothing", offering a clear-filters button instead of setup links that would be wrong advice there; edges stop emitting SMIL particles above 40 simultaneously active edges, keeping the colored stroke; and the drawer's error banner clears when a retried action succeeds. ([#12983](https://github.com/diegosouzapw/OmniRoute/pull/12983)) +- **fix(i18n):** the nine locales added with the EU-language batch (Greek, Estonian, Irish, Croatian, Lithuanian, Latvian, Maltese, Slovenian, Serbian) were missing the eleven Orchestration Canvas keys that Phase 3 introduced, so the compare-runs panel and the "no runs match these filters" empty state fell back to English in those languages (`deepMergeFallback` substitutes English for an absent key, so nothing rendered blank — it rendered untranslated). The coverage gate does not catch this: it enforces an 80% floor per locale, and eleven missing keys out of ~13,000 leaves coverage at 99.9%. Translated for real in each language, calibrated against the wording each file already uses for "run", "filter" and "skill". ([#13294](https://github.com/diegosouzapw/OmniRoute/pull/13294)) +- **fix(dashboard):** five follow-ups from the Phase 2 Orchestration Canvas review. The History drawer now shows the memory section for runs that already left the live TTL window: the persisted `memory_hits` event is parsed into `metadata.memoryHits` with the same defensive validation the drawer applies, and — because that event is observability rather than a state transition — it no longer leaks into the timeline, where it had been inheriting the task's state and rendering as a duplicate transition. The A2A memory recall runs against its own 1.5s deadline instead of inheriting the memory backend's 30s one; overshooting degrades exactly like any other recall failure (no hits, task proceeds), and the timer is cleared on both paths. Repeating a Conductor run carries its `requirements.cli`/`requirements.model` forward, so the new run is pinned to the same runner profile and model rather than drifting to whatever the fleet picks. A successful repeat from the Agents tab now focuses the run it created, instead of leaving the operator on the finished one. And the auth test for `POST /api/conductor/tasks` moved into the shared `ROUTES` array rather than restating the pattern. ([#12988](https://github.com/diegosouzapw/OmniRoute/pull/12988)) +- fix(plugins): stop leaking an exit listener per plugin hook timeout, which triggered MaxListenersExceededWarning on plugins that ignore SIGTERM ([#13092](https://github.com/diegosouzapw/OmniRoute/pull/13092)) — thanks @anhtahaylove +- **fix(validation):** Provider node edits no longer fail with a generic "Invalid request" when the optional daily-quota reset fields are left blank. The dashboard sends `dailyQuotaResetTimezone` and `dailyQuotaResetHour` as `null`, and only the hour accepted it. ([#13066](https://github.com/diegosouzapw/OmniRoute/issues/13066)) ([#13083](https://github.com/diegosouzapw/OmniRoute/pull/13083)) — thanks @ntdatt812 +- **fix(combo):** quota-aware expansion drops banned, inactive, missing, and wrong-provider connections before quota fetch or model dispatch; pins and allowlists stay selectors, not a bypass. Antigravity automatic exhaustion now requires a reported zero remaining, so a positive balance below 1% stays eligible. ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- Fix an intermittent failure in the quota-weighted routing test suite: scores are a function of `Date.now()`, so peers with identical quota scored microseconds apart never tied and swapped order. ([#12946](https://github.com/diegosouzapw/OmniRoute/pull/12946)) +- fix(volcengine): resolve quota window mapping gaps, safe multi-connection console binding, and response cookie sanitization ([#12950](https://github.com/diegosouzapw/OmniRoute/pull/12950)) — thanks @HouMinXi +- **fix(test):** resolve the WebDAV handler path with `fileURLToPath` so the suite's 37 WebDAV tests run on Windows instead of failing with a doubled `C:\C:\` drive prefix ([#13196](https://github.com/diegosouzapw/OmniRoute/pull/13196)) — thanks @anhtahaylove +- **fix(ui):** fall back from invalid AnySearch icon alias (direct commit `9587e07b69`) +- **fix(sse):** stop rebuilding a truncated summary from the collector's cap-dropped event array ([#12718](https://github.com/diegosouzapw/OmniRoute/pull/12718)) — thanks @hartmark +- **fix(conversations):** show a pending spinner for unresolved tool nodes instead of "(empty)" ([#12727](https://github.com/diegosouzapw/OmniRoute/pull/12727)) — thanks @hartmark +- **fix(streaming):** fail fast when an upstream stream produces only lifecycle/heartbeat events, never real content ([#12741](https://github.com/diegosouzapw/OmniRoute/pull/12741)) — thanks @hartmark +- **fix(responses-continuation):** bridge the write-in-flight window with an in-memory pending store ([#12854](https://github.com/diegosouzapw/OmniRoute/pull/12854)) — thanks @hartmark +- **fix(translator):** drop or map agent_message on Chat Completions fallback ([#12880](https://github.com/diegosouzapw/OmniRoute/pull/12880)) — thanks @RaviTharuma +- **fix(api):** page and stream-complete GET /v1/models for large catalogs ([#12882](https://github.com/diegosouzapw/OmniRoute/pull/12882)) — thanks @RaviTharuma +- **fix(quota):** align AUTH window labels with usage API and clarify 503 ([#12884](https://github.com/diegosouzapw/OmniRoute/pull/12884)) — thanks @RaviTharuma +- **fix(ci):** clear two base-reds on release/v3.8.51 — mutation-coverage gate + image-only-model guard ([#12945](https://github.com/diegosouzapw/OmniRoute/pull/12945)) +- **fix(api):** scope batch bulk-delete to the calling API key ([#13211](https://github.com/diegosouzapw/OmniRoute/pull/13211)) +- **fix(tests):** align model-sync log assertion to shipped path redaction (base-red) ([#13227](https://github.com/diegosouzapw/OmniRoute/pull/13227)) +- **fix(test):** make the ToS heading guard actually require the parentheses ([#13228](https://github.com/diegosouzapw/OmniRoute/pull/13228)) +- **fix(quality):** register 4 drifted covering tests in stryker tap.testFiles ([#13229](https://github.com/diegosouzapw/OmniRoute/pull/13229)) +- **fix(logging):** mask *-api-key headers in request-log pipeline (#13273) ([#13401](https://github.com/diegosouzapw/OmniRoute/pull/13401)) — thanks @KooshaPari +- **fix(tests):** correct stale Jina catalog prefix in custom-models test ([#13403](https://github.com/diegosouzapw/OmniRoute/pull/13403)) — thanks @KooshaPari +- **fix(backend):** prune old backups after health-check-repair snapshots (#13308) ([#13404](https://github.com/diegosouzapw/OmniRoute/pull/13404)) — thanks @KooshaPari +- **fix(backend):** guard memory_fts_au trigger with WHEN clause to stop FTS bloat (#13326) ([#13405](https://github.com/diegosouzapw/OmniRoute/pull/13405)) — thanks @KooshaPari +- **fix(free-tiers):** remove obsolete Together $25 signup credit (#12526) ([#13407](https://github.com/diegosouzapw/OmniRoute/pull/13407)) — thanks @KooshaPari +- **fix:** skip non-chat generation models in Test all models ([#13409](https://github.com/diegosouzapw/OmniRoute/pull/13409)) — thanks @KooshaPari +- **fix(providers):** de-duplicate agy and antigravity model catalogs ([#13410](https://github.com/diegosouzapw/OmniRoute/pull/13410)) — thanks @KooshaPari +- **fix(dashboard):** send provider in body for mini-playground web search ([#13411](https://github.com/diegosouzapw/OmniRoute/pull/13411)) — thanks @KooshaPari +- **fix(resilience):** add CJK quota-exhaustion patterns to 429 classifier ([#13412](https://github.com/diegosouzapw/OmniRoute/pull/13412)) — thanks @KooshaPari +- **fix(backend):** eval runner: failed calls no longer score as passed + dotAll regex ([#13413](https://github.com/diegosouzapw/OmniRoute/pull/13413)) — thanks @KooshaPari +- **fix(backend):** eval runner opts out of output-style and memory injection ([#13414](https://github.com/diegosouzapw/OmniRoute/pull/13414)) — thanks @KooshaPari +- **fix:** chain serial quarantine step in npm test script (fixes #13286) ([#13418](https://github.com/diegosouzapw/OmniRoute/pull/13418)) — thanks @KooshaPari +- **fix(antigravity):** derive stable session ID from credentials for prompt-cache reuse (#13305) ([#13424](https://github.com/diegosouzapw/OmniRoute/pull/13424)) — thanks @KooshaPari +- **fix(memory):** merge memory into top-level system field instead of unshifting at messages[0] (#13425) ([#13427](https://github.com/diegosouzapw/OmniRoute/pull/13427)) — thanks @KooshaPari +- **fix(compression):** preserve instruction blocks from lossy engines ([#13523](https://github.com/diegosouzapw/OmniRoute/pull/13523)) — thanks @KooshaPari +- **fix(compression):** preserve polarity words and newlines in ultra heuristic ([#13524](https://github.com/diegosouzapw/OmniRoute/pull/13524)) — thanks @KooshaPari +- **fix(compression):** run user preservePatterns before built-in patterns (#13457) ([#13525](https://github.com/diegosouzapw/OmniRoute/pull/13525)) — thanks @KooshaPari +- **fix(reasoning):** expand deepseek-v4 regex to match v4.1+ and add command-code provider (#13430) ([#13528](https://github.com/diegosouzapw/OmniRoute/pull/13528)) — thanks @KooshaPari +- **fix(providers):** add alwaysReasons metadata for reka-flash-3 ([#13531](https://github.com/diegosouzapw/OmniRoute/pull/13531)) — thanks @KooshaPari +- **fix(playground):** allow Compare columns to scroll independently ([#13532](https://github.com/diegosouzapw/OmniRoute/pull/13532)) — thanks @KooshaPari +- **fix(security):** empty whitelist allows through to prevent admin lockout ([#13534](https://github.com/diegosouzapw/OmniRoute/pull/13534)) — thanks @KooshaPari +- **fix(tests):** add DISABLE_IOREG_STRATEGY env var for macOS test compatibility (fixes #13467) ([#13539](https://github.com/diegosouzapw/OmniRoute/pull/13539)) — thanks @KooshaPari +- **fix(zed-hosted):** map developer role and Gemini enums for Zed proxy ([#13543](https://github.com/diegosouzapw/OmniRoute/pull/13543)) — thanks @KooshaPari +- **fix(analytics):** normalize provider aliases to canonical id in usage_history ([#13545](https://github.com/diegosouzapw/OmniRoute/pull/13545)) — thanks @KooshaPari +- **fix(combo):** use traceId for call log id to prevent UNIQUE constraint failure ([#13546](https://github.com/diegosouzapw/OmniRoute/pull/13546)) — thanks @KooshaPari +- **fix(auth):** check model lockout before returning synthetic noauth connection ([#13547](https://github.com/diegosouzapw/OmniRoute/pull/13547)) — thanks @KooshaPari +- **fix(guardrails):** skip credential redaction for base64 image data URLs ([#13550](https://github.com/diegosouzapw/OmniRoute/pull/13550)) — thanks @KooshaPari +- **fix(resilience):** increase requestQueue.maxWaitMs default from 15s to 30s ([#13553](https://github.com/diegosouzapw/OmniRoute/pull/13553)) — thanks @KooshaPari +- **fix(docs):** document OMNIROUTE_READY_TIMEOUT_MS and allowlist the test-only DISABLE_IOREG_STRATEGY ([#13692](https://github.com/diegosouzapw/OmniRoute/pull/13692)) + ### 📝 Maintenance ---- +- **docs(readme):** reconcile live v3.8.50 provider, free-tier, CLI, routing, test, community, sponsor, acknowledgment, and SVG metrics with their audited source denominators, including a deduplicated OmniRoute-in-Action snapshot and distinct contributor rankings for merged pull requests, GitHub-attributed commits, and Git history ([#11356](https://github.com/diegosouzapw/OmniRoute/pull/11356)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **docs:** sync the canonical provider count 354 → 356 across `README.md`, `AGENTS.md`, `llm.txt` (+ 42 i18n mirrors), the four README SVG diagrams, `docs/reference/PROVIDER_REFERENCE.md` (regenerated) and the `package.json` description after Opper (#11629) and 1min.ai (#11631) boarded the catalog — closes the `check:docs-counts-sync` strict drifts that kept `release/v3.8.51` red ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11673](https://github.com/diegosouzapw/OmniRoute/pull/11673)) — thanks @jonlwheat2-gif +- **test(providers):** update count-derived assertions after the v3.8.51 provider additions — `APIKEY_PROVIDERS` 233 → 235 (Opper #11629 + 1min.ai #11631), reserved-prefix REGISTRY walk 395 → 398, `WEB_FETCH_PROVIDERS` now includes `nimble-search` (#11620), and the provider translate-path golden snapshot regenerated ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11674](https://github.com/diegosouzapw/OmniRoute/pull/11674)) — thanks @jonlwheat2-gif +- **test(sse):** bump the hard-lease connection-query inventory for `src/lib/tokenHealthCheck.ts` to 2 — the verify-only web-cookie sweep added by #11495 split the single `getProviderConnections` call into oauth + cookie variants, which the frozen inventory had not tracked ([#11495](https://github.com/diegosouzapw/OmniRoute/pull/11495)). ([#11677](https://github.com/diegosouzapw/OmniRoute/pull/11677)) — thanks @jonlwheat2-gif / @oyi77 +- **chore(release):** clear the `release/v3.8.51` base-red gates — docs migration count (+38 i18n mirrors), stryker `tap.testFiles`, lockfile registry host, stale ESLint suppressions, the 7 lint regressions they were hiding, the stale `APIKEY_PROVIDERS` partition count (231 → 233), the `glm-5.3-max` tier missing from the GLM test table, a dead `modelType === "chat"` comparison in the models catalog, and the `resolveLiveWsUrl` / `sanitizeLiveWsPort` helpers from #11388 that a later merge dropped from `wsPath.ts` (dashboard live WebSocket URL resolution) ([#11502](https://github.com/diegosouzapw/OmniRoute/pull/11502)) — thanks @MumuTW +- **chore(free-models):** the free-tier regime table now declares which totals figure each regime feeds and whether it may take the no-auth shortcut, so the sets that used to repeat those answers by hand are derived from it — a new `freeType` no longer compiles until it has answered every question, instead of silently contributing to no total ([#11537](https://github.com/diegosouzapw/OmniRoute/pull/11537)) — thanks @maxmad64bis +- **docs(routing):** LKGP strategy copy now states provider-level stickiness and the explicit rules fallback instead of promising per-target pinning ([#11555](https://github.com/diegosouzapw/OmniRoute/pull/11555)) — thanks @maxmad64bis +- **test(chatcore):** pin the response tool-name alias resolution against the ordering hazard that broke every Gemini/Antigravity MCP tool call in v3.8.49. `extractRequestToolIdentityMap` deletes `translatedBody._toolNameMap`, so the later read is always undefined and the ledger survives only through the `requestToolIdentityMap` fallback — removing that fallback previously left the entire tool-name suite green. The resolution moves into `resolveResponseToolNameMap()` next to the map it depends on, with a regression guard that fails without the recovery. No behaviour change. ([#11575](https://github.com/diegosouzapw/OmniRoute/pull/11575)) — thanks @amartinawi +- **docs(env):** document `OMNIROUTE_CORPUS_CACHE_SIZE` in `.env.example` and `ENVIRONMENT.md`, so the env/docs contract check stops failing Docs Gates on every branch ([#11576](https://github.com/diegosouzapw/OmniRoute/pull/11576)) — thanks @ntdatt812 +- **chore(stryker):** register `route-guard-tunnel-processes-local-only` and `universal-quota-aware-routing` in `tap.testFiles`, so their mutant kills count for the three modules they cover ([#11577](https://github.com/diegosouzapw/OmniRoute/pull/11577)) — thanks @ntdatt812 +- **test(authz):** pin `LOCAL_ONLY_API_GET_EXEMPTIONS` by exact membership instead of by entry count, so the guard names the offending path and also catches a substitution ([#11580](https://github.com/diegosouzapw/OmniRoute/pull/11580)) — thanks @ntdatt812 +- **test(build):** pin `resolveNextBuildBundlerFlag` to the `OMNIROUTE_USE_TURBOPACK` contract it implements, across runtimes, instead of asserting an unimplemented Bun override ([#11583](https://github.com/diegosouzapw/OmniRoute/pull/11583)) — thanks @ntdatt812 +- **test(claude-web):** the Claude Web transport test's barrel anchors no longer pin the exact construction spelling. #11421 made the executor map lazy, so `/"claude-web": new ClaudeWebExecutor\(\)/` stopped matching and the test went red on `release/v3.8.51` itself — a base-red gate every open PR inherited. The anchors now match the alias key and the class name and tolerate either spelling in between, and were checked to still fail when an alias is genuinely repointed at another executor ([#11585](https://github.com/diegosouzapw/OmniRoute/pull/11585)) — thanks @ntdat812 +- **test(models):** pin `qwen3.8-max` resolution per provider — two of the four catalogs now serve the bare id and no longer carry `-preview` ([#11590](https://github.com/diegosouzapw/OmniRoute/pull/11590)) — thanks @ntdatt812 +- **test(executors):** drop the stale 128-tool truncation assertion that contradicted the #11444 pass-through contract ([#11591](https://github.com/diegosouzapw/OmniRoute/pull/11591)) — thanks @ntdatt812 +- **test(token-health):** pin the current terminal-skip boundary — expired with retry budget is probed, `account_deactivated` stays skipped ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)) — thanks @ntdatt812 +- **test(models):** follow #11503's retarget of the `gemini-3-pro-high` alias to the hyphenated catalog id ([#11593](https://github.com/diegosouzapw/OmniRoute/pull/11593)) — thanks @ntdatt812 +- **refactor(video bridge):** extract per-video acquisition, whole-result caching, description, and metrics/abort/cleanup out of `VideoBridgeGuardrail.preCall` into a `processVideoPart` seam in a new `videoBridgePipeline.ts`, behind explicit `VideoMediaBrokerPort`, `VideoAudioTranscriptionPort`, and `VideoDrilldownPort` boundaries; `preCall` now only handles request traversal, policy, and response aggregation. The Video tab's FFmpeg/ffprobe runtime status is now an explicit `unknown` / `restricted` / `unavailable` / `available` state instead of a nullable boolean pair, fixing a case where an in-flight or failed probe was mislabeled as "install FFmpeg" ([#11657](https://github.com/diegosouzapw/OmniRoute/issues/11657)). ([#12016](https://github.com/diegosouzapw/OmniRoute/pull/12016)) +- **test(opencode-plugin):** add unit test coverage for `formatFreeBudget()` naming helper ([#11660](https://github.com/diegosouzapw/OmniRoute/pull/11660)) — thanks @f9td56dbgh-hub +- **docs(video):** clarify that the Video Bridge transcript `source` field (`client`, `embedded`, `audio-bridge`) is presently caller-declared and not yet server-verified — OmniRoute enforces the enum shape but does not cryptographically confirm that an `embedded`/`audio-bridge` label came from a server-owned extraction ([#11661](https://github.com/diegosouzapw/OmniRoute/issues/11661)). ([#12001](https://github.com/diegosouzapw/OmniRoute/pull/12001)) +- **chore(providers):** retire the distributed Raycast Relay and Hailuo Web integrations after a provenance audit confirmed substantial derivation from GPL-3.0 sources; the official GitHub and MiniMax API providers remain available. Migration 163 disables legacy connections and invalidates their active leases while preserving connection IDs, audit/history rows, quota snapshots, usage records, and fail-closed API-key connection policies. ([#11691](https://github.com/diegosouzapw/OmniRoute/pull/11691)) +- **chore(providers):** retire the Felo Web provider while its GPL-derived provenance remains on licensing HOLD ([#11698](https://github.com/diegosouzapw/OmniRoute/pull/11698)) — stale runtime and database references now fail closed while existing connection, lease, and usage history remains preserved for auditability. +- **fix(sse):** retire the Gemini Web image-generation bridge while provenance and licensing remain on HOLD, preserving Gemini Web chat/auth/tools and legitimate Gemini/Nano Banana image providers ([#11708](https://github.com/diegosouzapw/OmniRoute/pull/11708)) +- **chore(audio):** retire the built-in EdgeTTS provider and its 11-voice catalog while provenance/licensing review remains on HOLD; the generic `/v1/audio/speech` endpoint and the other speech providers remain available ([#11711](https://github.com/diegosouzapw/OmniRoute/pull/11711)) +- **fix(providers):** retire the unofficial Qwen Web integration and its `qwen-web`/`qw` identities while third-party provenance remains on HOLD ([#11713](https://github.com/diegosouzapw/OmniRoute/pull/11713)) — direct requests now fail closed with a sanitized HTTP 410 and combos skip the retired target, while migration guards preserve connection IDs, allowlists, usage/audit history and invalidate active leases. Supported Qwen API, Qwen Code, cloud, token-plan, audio, local and model-name surfaces remain available; restoring Qwen Web requires a qualified provenance/license decision. +- **fix(providers):** retire `microsoft-designer-web` and its `msdesigner` alias from runtime and catalogs while preserving stored credentials, provider history and allowlists as inactive records — source dependency on gpt4free's [`MicrosoftDesigner.py`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/g4f/Provider/needs_auth/MicrosoftDesigner.py) is proven at snapshot `0943d16eef757a44deb60651c07bbe5fa958cafe`, whose [`LICENSE`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/LICENSE) is GPL-3.0; expressive adaptation is probable, which records provenance/licensing risk without asserting a legal conclusion ([#11720](https://github.com/diegosouzapw/OmniRoute/pull/11720)). +- **docs(legal):** centralize pinned MIT notices for the vendored GCF codec and local flag assets ([#11726](https://github.com/diegosouzapw/OmniRoute/pull/11726)) +- Split the 2,952-line `adobeFireflyClient.ts` into nine focused modules (catalog, credentials, payload, ARP session, upload, responses, IMS, account, poll) behind an unchanged barrel — no consumer edits and no public API change (#11728). — thanks @MumuTW +- **fix(dashboard):** replaced the Model Lockout card's unprovenanced notification MP3 with a short, locally synthesized Web Audio chime ([#11731](https://github.com/diegosouzapw/OmniRoute/pull/11731)) — both toggles retain optional audible feedback without shipping replacement media or third-party code, and unsupported, suspended, or rejected audio contexts fail open without blocking the setting change. +- **chore(deepseek-web):** remove the provenance-unresolved DeepSeek PoW WASM binary and its runtime loader/tracing while retaining the existing JavaScript solver (slower at high difficulty) ([#11732](https://github.com/diegosouzapw/OmniRoute/pull/11732)). +- **chore(assets):** remove four unused provider assets whose redistribution provenance could not be verified while retaining the active Freebuff SVG ([#11735](https://github.com/diegosouzapw/OmniRoute/pull/11735)) +- **docs(assets):** retired two raster images whose redistribution provenance could not be verified ([#11736](https://github.com/diegosouzapw/OmniRoute/pull/11736)) — PicoClaw documentation now reuses the existing SVG, while Zoo Code remains a text link without an image. +- **docs(legal):** add pinned notices for six LobeHub-derived provider SVGs and 65 byte-exact theSVG assets, retaining registry claims as unverified metadata with explicit trademark and HOLD boundaries ([#11737](https://github.com/diegosouzapw/OmniRoute/pull/11737)). +- **chore(quality):** added a versioned 225/225 provider-asset provenance manifest and blocking drift gate ([#11743](https://github.com/diegosouzapw/OmniRoute/pull/11743)) — the inventory pins magic MIME and SHA-256 for every physical file, records immutable source matches for 71 assets, keeps 69 probable and 85 unresolved entries explicit but non-blocking, and treats provenance as source evidence rather than copyright or trademark clearance. +- **refactor(mitm):** replace four previously derived Traffic Inspector utilities — conversation normalization, SSE merging, secret masking, and header sanitization — with independent clean-room implementations based on public provider schemas, WHATWG SSE, and RFC 6750, while keeping the two independently classified passthrough surfaces unchanged ([#11748](https://github.com/diegosouzapw/OmniRoute/pull/11748)). +- Added a unit test that fails seven days before any dated pack under `config/` (`validUntil` and sibling keys) lapses, naming the file and key. The Alibaba free-tier pack expired on 2026-08-27 and turned every PR red the next morning with no commit involved; renewal now happens on someone's terms, not the clock's. ([#11891](https://github.com/diegosouzapw/OmniRoute/pull/11891)) +- `check:workflows` now fails (under `--strict`/`--ratchet`) when any job routed to a self-hosted runner publishes with `--provenance` — npm rejects that with `422` at the registry, which in v3.8.50 only surfaced after the tag and Docker images were public. ([#11895](https://github.com/diegosouzapw/OmniRoute/pull/11895)) +- `scripts/ops/runner-janitor.sh` now proves a path is idle with one `lsof` snapshot and removes stale leftovers itself (tmpfs after 3 h — it is RAM — disk after 24 h), kills orphan `next-build` processes, prunes checkouts of stopped runners, and alerts on memory pressure; `--dry-run` shows exactly what it would do. `docs/ops/RUNNER_BOX.md` reconciled to the measured box (31 GB, 10 listeners). ([#11893](https://github.com/diegosouzapw/OmniRoute/pull/11893)) +- The `next-build` artefact (1.3 GB) is now written and read under `$RUNNER_TEMP` (per-runner, on disk) instead of `/tmp`, which on the self-hosted pool is a 12 GB tmpfs in RAM. Landing it there took 27–32 of the publish job's 76 minutes, and the fixed `/tmp/e2e-build.tar.gz` name let E2E jobs on different runners overwrite each other's download. ([#11896](https://github.com/diegosouzapw/OmniRoute/pull/11896)) +- The CI `build` job now runs in two concurrency lanes — `main` and pull requests — so a release build is never queued behind (or OOM-killed beside) PR builds on the self-hosted pool, which holds one `next-build` comfortably and two at the edge. ([#11901](https://github.com/diegosouzapw/OmniRoute/pull/11901)) +- Re-freeze the ESLint suppressions on `release/v3.8.51` from a clean-room measurement (2 stale file entries pruned, 55 pre-existing `no-explicit-any` in six new files frozen under #11924) and drop the dead `GPT_SIZE_MAP` constant orphaned by the Adobe Firefly client split, so `No new ESLint warnings` stops failing every PR with exit 2 (Refs #11924) ([#11955](https://github.com/diegosouzapw/OmniRoute/pull/11955)) +- Type the 55 `no-explicit-any` sites that had been frozen under #11924 — four redundant casts in `socksConnectorWithFamily.ts` (undici/socks types already accept them) and the mocks/fixtures of the socks-timeout and isFree suites — and drop their suppression entries; the ESLint ratchet shrinks from 5487 to 5432 (Closes #11924) ([#11975](https://github.com/diegosouzapw/OmniRoute/pull/11975)) +- Take the two hosted-runner builds off the PR rail: `Build App` (`build.yml`) is `workflow_dispatch`-only and `DAST smoke (PR)` runs only for PRs into `main` — the 7 GB hosted VM cannot build `release/v3.8.51` in any profile (19/30 red, VM shutdown ~8 min into `next build`) and both checks had turned into permanent noise on every release PR; the bundle stays validated by `ci.yml` on `main` and by `nightly-release-green` on `release/**` (Closes #11946) ([#11962](https://github.com/diegosouzapw/OmniRoute/pull/11962)) +- Move the four nightly jobs that build the backend (`nightly-schemathesis`, `nightly-llm-security` promptfoo + garak, `nightly-resilience` axe-a11y) off the hosted 7 GB runner — where they died on `release/v3.8.51` unseen — onto the box's new `omni-light` pool (two listeners), and document the reshaped fleet (4 active OmniRoute listeners: 2 `omni-build` + 2 `omni-light`, janitor ceiling 4) (Closes #11965) ([#11967](https://github.com/diegosouzapw/OmniRoute/pull/11967)) +- Stop painting every fork PR into `release/**` red: `quality.yml` `Build (advisory)` is skipped (GitHub still reports `continue-on-error` failures as check FAILURE). Hosted `ubuntu-latest` cannot finish `npm run build` on this tree — same class as #11962 taking `build.yml` off the PR rail. `docker-publish.yml` amd64 now runs on the `.113` `omni-build` pool (31 GB / 32 cores, two listeners) with Turbopack, shares the `heavy-build-main` lane with `ci.yml` `Build` so it queues instead of becoming a third heavy, and keeps per-ref concurrency (a merge storm was starting 8 concurrent OOM builds). arm64 stays on `ubuntu-24.04-arm` with webpack — there is no ARM box. Fallback when `USE_VPS_RUNNER` is off: hosted amd64 + webpack (#11976). ([#12021](https://github.com/diegosouzapw/OmniRoute/pull/12021)) +- `docker-publish.yml` builds with webpack (`OMNIROUTE_USE_TURBOPACK=0`) on **both** arches, including amd64 on the `.113` `omni-build` pool. Turbopack had the RAM (31 GB, `omniroute-113-6`) and still panicked (`TurbopackInternalError: there must be a path to a root`, run 33253576569, 26 min in). The same tree's arm64 webpack build on hosted `ubuntu-24.04-arm` succeeded (run 33264823398). Dockerfile already documents webpack as the Docker escape hatch (#11976). ([#12050](https://github.com/diegosouzapw/OmniRoute/pull/12050)) +- **docs(free-tier):** declare the counting vs deciding regimes for "is it free?" and guard the deciding path from DB-backed catalog resolution ([#12226](https://github.com/diegosouzapw/OmniRoute/pull/12226)) — thanks @maxmad64bis +- **docs(auto-combo):** The mode pack table in `docs/routing/AUTO-COMBO.md` now lists all six shipped packs with every weight each one sets, replacing a four-pack table whose numbers had also drifted from the source. It states plainly that no pack sets `quality`, so selecting any pack silences the observed-quality signal. Six more documents that quote the scoring factor count joined the `check:docs-counts` gate, which caught five stale claims — including one naming nine factors that do not exist — and two stale mode pack counts. The dashboard routing panel, which offered four of the six packs and labelled the default strategy "6-Factor Scoring", is now covered by a test; the two packs it was missing are `reliability-first` and `chaos-mode`, the latter labelled as the fault-injection profile it is rather than as one more routing preference ([#12316](https://github.com/diegosouzapw/OmniRoute/pull/12316)) — thanks @maxmad64bis +- **chore(quality):** rebaseline `src/lib/db/apiKeys.ts` for the ACL the key-creation path now preserves ([#12352](https://github.com/diegosouzapw/OmniRoute/pull/12352)) ([#12673](https://github.com/diegosouzapw/OmniRoute/pull/12673)) — thanks @KrzysiekSko +- **chore(providers):** bump the Claude Code wire identity and the Devin bridge image pin from `2.1.220` to `2.1.258` ([#12402](https://github.com/diegosouzapw/OmniRoute/pull/12402)) — thanks @ggiak ([#12604](https://github.com/diegosouzapw/OmniRoute/pull/12604)) +- **docs(env):** align `.env.example`, the README Bun section, and the troubleshooting guide with the code: `OMNIROUTE_USE_TURBOPACK` also governs `npm run build` (not dev-only), `bun run build` follows that flag instead of auto-selecting Webpack, `OMNIROUTE_CHAT_MAX_HEAVY_IN_FLIGHT` is unset by default (no request-count cap), and the structural `503 chat_admission_busy` message matches `chatAdmissionResponses.ts` (#12404 — thanks @pacocartones) +- **chore(electron):** upgrade the desktop app to Electron 44 (Chromium 152, Node 24.18.1) ([#12217](https://github.com/diegosouzapw/OmniRoute/pull/12217)). **Requires macOS 13 (Ventura) or later** — Chromium dropped macOS 12 (Monterey), so Monterey users must stay on an earlier OmniRoute desktop build. Windows and Linux are unaffected; the app already shipped only x64/arm64, so Electron 44 dropping 32-bit builds changes nothing. Removes the `openAsHidden`/`wasOpenedAsHidden` login-item fields deleted in Electron 44 — hidden autostart continues to work through the `--hidden` argument registered with the login item ([#12554](https://github.com/diegosouzapw/OmniRoute/pull/12554)) — thanks @app/dependabot +- **chore(quality):** rebaseline `src/sse/handlers/chat.ts` for the effective-input persistence the continuation fix needs ([#12641](https://github.com/diegosouzapw/OmniRoute/pull/12641)) ([#12680](https://github.com/diegosouzapw/OmniRoute/pull/12680)) — thanks @hartmark +- `Coverage` job on `ci.yml`: the informational Codecov upload gets its own 5-minute ceiling and `continue-on-error`, and the job budget grows from 20 to 30 minutes (the 8-shard c8 merge alone takes ~10) — a stalled upload no longer ends the job `cancelled` and drags a fully green `main` run's conclusion down with it ([#11972](https://github.com/diegosouzapw/OmniRoute/pull/11972)) +- CI hardening on the PR rail: the four `Unit Tests fast-path` shards get `timeout-minutes: 30` (a hung shard held a PR for 64 min instead of GitHub's 6 h default) and both ESLint file caches lose their `restore-keys` fallback, so a cache built under another suppressions file or lint config can no longer report stale verdicts (Refs #11600, #11924) ([#11963](https://github.com/diegosouzapw/OmniRoute/pull/11963)) +- Every CI job that runs a `next build` (`build`, the npm `publish`, both release-green validations) now targets the `omni-build` runner label, which only two of the eight self-hosted runners carry. The box holds one build comfortably and two at the edge; a third now queues on GitHub instead of being OOM-killed by the kernel. ([#11932](https://github.com/diegosouzapw/OmniRoute/pull/11932)) +- **refactor(combo):** move `handleRoundRobinCombo` (and `resolveTargetTokenLimit`) into `open-sse/services/combo/roundRobinCombo.ts`. `combo.ts` drops from 2164 to 1014 lines (`split("\n").length`); the leaf is 1199 (under the 1200 new-file cap). The round-robin call site uses a dynamic `import()` so `releaseStickyPinOnFailure` / `clearStaleLKGP` can stay exported from `combo.ts` without a static cycle. Skip / sticky / semaphore / safety-timer behavior is unchanged. ([#12811](https://github.com/diegosouzapw/OmniRoute/pull/12811)) — thanks @HouMinXi +- **chore(quality):** rebaseline the file-size caps the error-boundary campaign grew past (`open-sse/executors/codex.ts`, `open-sse/vendor/codex-chatgpt-web/bridge.ts`, both via [#12444](https://github.com/diegosouzapw/OmniRoute/pull/12444)) ([#12654](https://github.com/diegosouzapw/OmniRoute/pull/12654)) +- **chore(quality):** rebaseline the file-size caps the hartmark batch grew past (`combos/page.tsx` via [#12355](https://github.com/diegosouzapw/OmniRoute/pull/12355), `open-sse/services/combo.ts` via [#12338](https://github.com/diegosouzapw/OmniRoute/pull/12338)) ([#12623](https://github.com/diegosouzapw/OmniRoute/pull/12623)) — thanks @hartmark +- **chore(quality):** rebaseline the file-size caps the HouMinXi batch grew past when its PRs stacked (`providers/page.tsx`, `chatCore.ts`, `accountFallback.ts`) — each PR measured correctly in isolation, none saw the stacking ([#12619](https://github.com/diegosouzapw/OmniRoute/pull/12619)) +- **chore(quality):** rebaseline `open-sse/services/combo.ts` for the reset-aware scoring the HouMinXi batch stacked ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) ([#12678](https://github.com/diegosouzapw/OmniRoute/pull/12678)) — thanks @HouMinXi +- **docs:** sync provider catalog count (direct commit `4f11b2ae3d`) +- **docs(skills):** sync omni-inference search fallback (direct commit `b4e76a7ed9`) +- **chore(lint):** prune two stale base suppressions (direct commit `1325047d56`) +- **test(auto-combo):** tolerate full-suite rotation load (direct commit `ad3e293f9f`) +- **test:** drain inherited quality snapshot reds (direct commit `21ed68d8ac`) +- **chore(quality):** rebaseline measured CLI bundle growth (direct commit `d35b3f9779`) +- **test(ui):** reproduce invalid AnySearch icon alias (direct commit `12cc6ca834`) +- **test(translator):** fix the relative imports of the relocated deferred-finish test (direct commit `777d9d1629`) +- **docs(counts):** bring the documented provider and migration counts to the measured values (direct commit `e70bea30e9`) +- **test(security):** drop the guard-target set left unused by the sync (direct commit `a32ba0e362`) +- **chore(db):** renumber model_capabilities migration 163 → 169 (cross-branch collision with 163_radar_feed_cache_generated_at) (direct commit `70f5d4cbf9`) +- **docs(cli):** document CLI_PRIME_AGENT_BIN and correct the CLI Agents count (direct commit `9058e39b61`) +- **chore(deps):** refresh runtimes and adopt ESLint 10 ([#11259](https://github.com/diegosouzapw/OmniRoute/pull/11259)) — thanks @backryun +- **refactor(local-corpus):** implement dynamic root resolution and LRU cache ([#11491](https://github.com/diegosouzapw/OmniRoute/pull/11491)) — thanks @raheemuddin786 +- **chore(quality):** prune stale eslint-suppressions entries ([#11596](https://github.com/diegosouzapw/OmniRoute/pull/11596)) +- **chore(ci):** unblock quality gates - env-doc sync + stryker tap.testFiles registration (#9282) ([#11609](https://github.com/diegosouzapw/OmniRoute/pull/11609)) — thanks @Meet6338-X +- **chore(ci):** point the circular-deps gate at dpdm's real JS entrypoint ([#11615](https://github.com/diegosouzapw/OmniRoute/pull/11615)) — thanks @benzntech +- **chore(quality):** rebaseline file-size for #11448/#11495/#11561 growth ([#11630](https://github.com/diegosouzapw/OmniRoute/pull/11630)) +- **deps:** realign onnxruntime-node pin to @huggingface/transformers' 1.24.3 lockstep ([#11633](https://github.com/diegosouzapw/OmniRoute/pull/11633)) — thanks @jonlwheat2-gif +- **test(autoCombo):** port models_dev_tier #11508 guard from node:test to vitest ([#11635](https://github.com/diegosouzapw/OmniRoute/pull/11635)) — thanks @jonlwheat2-gif +- **chore(quality):** rebaseline file-size for #11083/#11631/#11628 growth ([#11636](https://github.com/diegosouzapw/OmniRoute/pull/11636)) +- **chore(lint):** prune stale ESLint suppressions (base-red #11449) ([#11644](https://github.com/diegosouzapw/OmniRoute/pull/11644)) — thanks @jonlwheat2-gif +- **docs(changelog):** aggregate the ten v3.8.50 fragments into CHANGELOG.md ([#11683](https://github.com/diegosouzapw/OmniRoute/pull/11683)) +- **chore(search):** drop unreachable credential sweep after #11642 ([#11687](https://github.com/diegosouzapw/OmniRoute/pull/11687)) — thanks @HouMinXi +- **ci:** add API route TypeScript regression gate ([#11705](https://github.com/diegosouzapw/OmniRoute/pull/11705)) — thanks @Sabeekhann +- **docs(dedup):** correct the CodeQL claim on the tenant-namespace comment ([#11712](https://github.com/diegosouzapw/OmniRoute/pull/11712)) +- **docs(changelog):** add consolidated v3.8.50 stats, top-25 ranking and a .mailmap ([#11715](https://github.com/diegosouzapw/OmniRoute/pull/11715)) +- **docs:** recommend gstack for AI-assisted workflows ([#11770](https://github.com/diegosouzapw/OmniRoute/pull/11770)) — thanks @santosraju99-hub +- **chore(quality):** tighten complexity/cognitive-complexity ratchets to the current tip; land the missed gateways.ts rebaseline ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **docs(i18n):** finish Freepik → Magnific rebrand in locale strings and README ([#11772](https://github.com/diegosouzapw/OmniRoute/pull/11772)) — thanks @turbolego +- **refactor(auto-combo):** fix divergent scoring in combo health reporting ([#11854](https://github.com/diegosouzapw/OmniRoute/pull/11854)) — thanks @jacobsparts +- **chore(changelog):** add missing fragments for #11919/#11918/#11916 ([#11938](https://github.com/diegosouzapw/OmniRoute/pull/11938)) +- **test(check):** escape the runs-on fixture with JSON.stringify, not a quote-only replace ([#11942](https://github.com/diegosouzapw/OmniRoute/pull/11942)) (direct commit `fb7445eaa3`) +- **test(release):** align five suites with the contracts #11933, #11919 and #11876 shipped on release/v3.8.51 ([#11944](https://github.com/diegosouzapw/OmniRoute/pull/11944)) +- **chore(quality):** remove unreachable code and restore test discovery ([#11950](https://github.com/diegosouzapw/OmniRoute/pull/11950)) — thanks @backryun +- **docs(agents):** sync-back landings are fast-forward, never squash ([#11964](https://github.com/diegosouzapw/OmniRoute/pull/11964)) +- **test(infra):** retry recursive temp-dir removal instead of failing a shard on ENOTEMPTY (#11966) ([#11968](https://github.com/diegosouzapw/OmniRoute/pull/11968)) +- **docs(ops):** the .113 heavy-build ceiling is one runner, not two ([#12048](https://github.com/diegosouzapw/OmniRoute/pull/12048)) +- **test(ui):** correct inactive auto-fetch expectation ([#12098](https://github.com/diegosouzapw/OmniRoute/pull/12098)) — thanks @RaviTharuma +- **docs:** document native dependency check escape hatch ([#12101](https://github.com/diegosouzapw/OmniRoute/pull/12101)) — thanks @RaviTharuma +- **test(providers):** regenerate the translate-path golden for OrcaRouter (#11923) ([#12118](https://github.com/diegosouzapw/OmniRoute/pull/12118)) +- **chore(quality):** velocity phase — loosen every numeric baseline by 20% until v4.0, monitor headroom nightly ([#12125](https://github.com/diegosouzapw/OmniRoute/pull/12125)) +- **chore(lint):** batch 0 of #12146 — type the call-log-cap sqlite rows instead of 45 as-any casts ([#12157](https://github.com/diegosouzapw/OmniRoute/pull/12157)) +- **chore(lint):** batch 4 of #12146 — shared/components react-hooks violations resolved ([#12159](https://github.com/diegosouzapw/OmniRoute/pull/12159)) +- **chore(lint):** batch 1 of #12146 — dashboard/cli-code react-hooks violations resolved ([#12160](https://github.com/diegosouzapw/OmniRoute/pull/12160)) +- **chore(lint):** batch 3 of #12146 — dashboard/settings react-hooks violations resolved ([#12162](https://github.com/diegosouzapw/OmniRoute/pull/12162)) +- **chore(lint):** batch 2 of #12146 — resolve the react-hooks compiler violations in dashboard/providers ([#12163](https://github.com/diegosouzapw/OmniRoute/pull/12163)) +- **chore(quality):** register search-432 cooldown test in stryker tap.testFiles ([#12170](https://github.com/diegosouzapw/OmniRoute/pull/12170)) +- **chore(lint):** batch 5 of #12146 — combos, endpoint, provider-stats, api-manager and costs react-hooks violations resolved ([#12174](https://github.com/diegosouzapw/OmniRoute/pull/12174)) +- **build:** add contributor fast profile ([#12192](https://github.com/diegosouzapw/OmniRoute/pull/12192)) — thanks @rafacpti23 +- **docs(readme):** README touch-ups ([#12193](https://github.com/diegosouzapw/OmniRoute/pull/12193)) ([#12194](https://github.com/diegosouzapw/OmniRoute/pull/12194)) ([#12202](https://github.com/diegosouzapw/OmniRoute/pull/12202)) — thanks @rafacpti23 +- **build:** force the webpack bundler in the contributor build profile ([#12197](https://github.com/diegosouzapw/OmniRoute/pull/12197)) — thanks @rafacpti23 +- **build:** skip standalone packaging in the contributor profile ([#12198](https://github.com/diegosouzapw/OmniRoute/pull/12198)) — thanks @rafacpti23 +- **docs(audit):** align every published number with the code and harden check:docs-counts ([#12200](https://github.com/diegosouzapw/OmniRoute/pull/12200)) +- **build:** stub the instrumentation entrypoints in the contributor profile ([#12203](https://github.com/diegosouzapw/OmniRoute/pull/12203)) — thanks @rafacpti23 +- **build:** omit the standalone output target for contributor builds ([#12204](https://github.com/diegosouzapw/OmniRoute/pull/12204)) — thanks @rafacpti23 +- **chore(lint):** batch 6 of #12146 — memory, radar, audit, analytics, cache, usage, activity, home and RequestLoggerV2 react-hooks violations resolved ([#12208](https://github.com/diegosouzapw/OmniRoute/pull/12208)) +- **docs(diagrams):** rename number-carrying diagram files to stable names ([#12210](https://github.com/diegosouzapw/OmniRoute/pull/12210)) +- **docs(dashboard):** redraw onboarding tier-flow SVGs for the real 4-tier model ([#12211](https://github.com/diegosouzapw/OmniRoute/pull/12211)) +- **docs(api):** document every implemented route in openapi.yaml (276 → 692 paths) ([#12212](https://github.com/diegosouzapw/OmniRoute/pull/12212)) +- **chore(lint):** batch 7 of #12146 — final src tail: 37 react-hooks violations across 33 files resolved ([#12244](https://github.com/diegosouzapw/OmniRoute/pull/12244)) +- Revert "docs: recommend gstack for AI-assisted workflows (#11770)" ([#12248](https://github.com/diegosouzapw/OmniRoute/pull/12248)) ([#12249](https://github.com/diegosouzapw/OmniRoute/pull/12249)) +- **docs:** Chaos Mode setup guide + weighted strategy semantics ([#12250](https://github.com/diegosouzapw/OmniRoute/pull/12250)) +- **docs(agents):** protected-surface merge rule — operator approval for agent-instruction files ([#12253](https://github.com/diegosouzapw/OmniRoute/pull/12253)) +- **chore(quality):** register native-codex turn-pin tests in stryker tap.testFiles ([#12263](https://github.com/diegosouzapw/OmniRoute/pull/12263)) +- **chore(quality):** dedupe tap.testFiles entries added by racing base-red fixes ([#12265](https://github.com/diegosouzapw/OmniRoute/pull/12265)) +- **docs(readme):** introduce OmniRouteTray — the macOS menu-bar companion ([#12276](https://github.com/diegosouzapw/OmniRoute/pull/12276)) — thanks @ggiak +- **chore(proxy):** purge the legacy 1proxy residue — sync/rotator modules, dead DB exports, dead settings tab and flag, docs (#12091) ([#12290](https://github.com/diegosouzapw/OmniRoute/pull/12290)) +- **chore(quality):** baseline-headroom skips generated and vendored files in the fileSize worst-file signal ([#12291](https://github.com/diegosouzapw/OmniRoute/pull/12291)) +- **refactor(video):** unify the JPEG frame data-URI contract ([#12322](https://github.com/diegosouzapw/OmniRoute/pull/12322)) +- **chore(deps):** freeze onnxruntime-node and eslint-plugin-react-hooks out of dependabot groups ([#12329](https://github.com/diegosouzapw/OmniRoute/pull/12329)) +- **test(executors):** restore the #10986 reasoning-only fallback guards ([#12364](https://github.com/diegosouzapw/OmniRoute/pull/12364)) — thanks @pacocartones +- **chore(quality):** re-tighten the file-size ratchet to the real LOC (plan 3.8.52 task 0) ([#12411](https://github.com/diegosouzapw/OmniRoute/pull/12411)) +- **test(grok):** format web executor suite ([#12412](https://github.com/diegosouzapw/OmniRoute/pull/12412)) +- **chore(quality):** ratchet open-sse typecheck baseline to zero ([#12418](https://github.com/diegosouzapw/OmniRoute/pull/12418)) +- **test(build):** guard the artifact path policy arrays against duplicates ([#12422](https://github.com/diegosouzapw/OmniRoute/pull/12422)) +- **chore(lint):** adopt eslint-plugin-react-hooks 7.1.1 ([#12428](https://github.com/diegosouzapw/OmniRoute/pull/12428)) +- **chore(providers):** remove a keyless provider integration at its operator's request ([#12440](https://github.com/diegosouzapw/OmniRoute/pull/12440)) +- **docs(providers):** register providers removed at their operator's request and guard against reintroduction ([#12478](https://github.com/diegosouzapw/OmniRoute/pull/12478)) +- **chore(quality):** base fixes — stryker tap.testFiles + node_modules cache key ([#12482](https://github.com/diegosouzapw/OmniRoute/pull/12482)) +- **docs(arch):** one-process recipe for tens of long /v1/responses ([#12493](https://github.com/diegosouzapw/OmniRoute/pull/12493)) — thanks @RaviTharuma +- **chore(quality):** tighten the CodeQL ratchet baseline from 11 to 6 ([#12530](https://github.com/diegosouzapw/OmniRoute/pull/12530)) +- **chore(deps):** pin browserslist override to ^4.28.8 ([#12592](https://github.com/diegosouzapw/OmniRoute/pull/12592)) — thanks @KooshaPari +- **chore(deps):** bump fast-uri to 3.1.7 in the electron lockfile ([#12601](https://github.com/diegosouzapw/OmniRoute/pull/12601)) +- **docs(quality):** document how the CodeQL ratchet refreshes and how to tighten it ([#12611](https://github.com/diegosouzapw/OmniRoute/pull/12611)) +- **test(ci):** pin the openapi-security-tiers two-arm contract with an executing gate test (#12581) ([#12652](https://github.com/diegosouzapw/OmniRoute/pull/12652)) +- **docs(gamification):** align XP Rewards table with code (#12501) ([#12667](https://github.com/diegosouzapw/OmniRoute/pull/12667)) — thanks @KooshaPari +- **refactor(combo):** split executeTarget into gates, attempt, and loop ([#12746](https://github.com/diegosouzapw/OmniRoute/pull/12746)) — thanks @HouMinXi +- **docs(security):** document socket.yml scanner config + CI workflow link (#12575) ([#12764](https://github.com/diegosouzapw/OmniRoute/pull/12764)) — thanks @KooshaPari +- **chore(ci):** guard commit identity in pre-commit to stop author misattribution ([#12772](https://github.com/diegosouzapw/OmniRoute/pull/12772)) +- **deps:** 17 Dependabot bumps — bump electron from 43.4.0 to 43.4.1 in /electron ([#11426](https://github.com/diegosouzapw/OmniRoute/pull/11426)); bump the development group across 1 directory with 8 updates ([#11428](https://github.com/diegosouzapw/OmniRoute/pull/11428)); bump the production group across 1 directory with 11 updates ([#11440](https://github.com/diegosouzapw/OmniRoute/pull/11440)); chore(deps): bump github/codeql-action/init from 4.37.7 to 4.37.8 ([#11925](https://github.com/diegosouzapw/OmniRoute/pull/11925)); chore(deps): bump github/codeql-action from 4.37.7 to 4.37.8 ([#11926](https://github.com/diegosouzapw/OmniRoute/pull/11926)); chore(deps): bump github/codeql-action/analyze from 4.37.7 to 4.37.8 ([#11927](https://github.com/diegosouzapw/OmniRoute/pull/11927)); build(deps): bump github/codeql-action/init from 4.37.8 to 4.37.9 ([#12345](https://github.com/diegosouzapw/OmniRoute/pull/12345)); build(deps): bump github/codeql-action/analyze from 4.37.8 to 4.37.9 ([#12346](https://github.com/diegosouzapw/OmniRoute/pull/12346)); bump the development group across 1 directory with 2 updates ([#12347](https://github.com/diegosouzapw/OmniRoute/pull/12347)); build(deps): bump github/codeql-action from 4.37.8 to 4.37.9 ([#12349](https://github.com/diegosouzapw/OmniRoute/pull/12349)); bump browserslist from 4.28.2 to 4.28.8 ([#12396](https://github.com/diegosouzapw/OmniRoute/pull/12396)); bump the production group across 1 directory with 4 updates ([#12399](https://github.com/diegosouzapw/OmniRoute/pull/12399)); bump @xmldom/xmldom ([#12500](https://github.com/diegosouzapw/OmniRoute/pull/12500)); bump qs from 6.15.2 to 6.16.0 ([#12512](https://github.com/diegosouzapw/OmniRoute/pull/12512)); bump @xmldom/xmldom from 0.9.10 to 0.9.12 ([#12513](https://github.com/diegosouzapw/OmniRoute/pull/12513)); bump fast-uri from 3.1.5 to 3.1.7 ([#12514](https://github.com/diegosouzapw/OmniRoute/pull/12514)); bump @humanfs/node from 0.16.7 to 0.16.8 ([#12515](https://github.com/diegosouzapw/OmniRoute/pull/12515)) +- Clear the `release/v3.8.51` `check:agent-skills-sync` base-red: regenerate `skills/cli-tunnel/SKILL.md` so the `tunnel create [type]` positional that #13009 taught the generator to read is reflected in the committed skill. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` typecheck base-red from #12731: the new `Mode pack ranking gates` candidates in `open-sse/services/autoCombo/__tests__/autoCombo.test.ts` omitted the required `provider`, `model` and `errorRate` fields of `ProviderCandidate`, failing `check:open-sse-typecheck` (and with it `Fast Quality Gates`) on every open PR. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` docs-sync base-red: EURouter (#13025) and GreenPT (#13024) took the live provider count to 358, leaving 7 STRICT drifts (`PROVIDER_REFERENCE.md`, 4 diagrams, 2 tier-flow images) plus the `AGENTS.md` / `llm.txt` / `package.json` count claims stale. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- **test(models):** Pin `CATALOG_BUILD_TIMEOUT_MS` in the #12058 canonical-catalog regression so a cold catalog build racing the 8s default bound no longer flakes the suite with a 500 ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Allow `@omniroute/opencode-plugin-v2/` in the npm pack policy — [#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870) shipped the v2 plugin beside its v1 sibling without widening the allowlist, so every packed file under it read as an unexpected artifact and failed `check:pack-policy` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Point the release-green validator's pack gate at the tree it is validating — the build-provenance guard defaulted to `origin/main`, which a release branch tip can never be an ancestor of mid-cycle, so `Package artifact` reported a false red on every run and the tarball boot-smoke cascaded off it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **docs(checks):** keep doc counts honest — headings, rankings, catalog, weights, quality gate and scoring diagram now covered ([#12507](https://github.com/diegosouzapw/OmniRoute/pull/12507)) — thanks @maxmad64bis +- **chore(lifecycle):** `check:model-lifecycle` now also diffs `DEFAULT_DEGRADATION_MAP` (the background-task redirect table) against the vendor lifecycle snapshot, refusing a retired id as source or target, with a table-driven unit test beside it. Three rows whose source the vendor had retired — `claude-sonnet-4-20250514`, `gemini-3-pro-preview` and `gpt-5.1-codex` (whose target `gpt-5.1-codex-mini` is retired too) — were dead code, since `checkLifecycle` answers 410 before the redirect runs; they are dropped (#12535 — thanks @pacocartones) +- **test(compression):** cover idle worker eviction at the resource level — the pool must call `terminate()` and must not retain the worker's `MessagePort`, complementing the `exit`-event assertion added with the fix ([#13371](https://github.com/diegosouzapw/OmniRoute/pull/13371)) +- **chore(quality):** raise the `imageGeneration.ts` file-size ceiling for the image-only-model guard that clears the #12945 base-red ([#13392](https://github.com/diegosouzapw/OmniRoute/pull/13392)) +- **chore(quality):** type the combo-test route's JSON response bodies instead of casting them to `any`, drop the now-empty suppression entry, and remove the `hasPerModelQuota` import `chatCore.ts` stopped using when the failure-classification helper was extracted ([#13379](https://github.com/diegosouzapw/OmniRoute/pull/13379)) +- **chore(electron):** the desktop bundle no longer ships the root-level files of every translated docs mirror (`README.md`, `llm.txt`, `CLAUDE.md`, `GEMINI.md`, `CONTRIBUTING.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`) — the packaged app only reads `docs/i18n//docs/**`, which stays. Saves ~11 MB on top of the translated CHANGELOGs already pruned. (#0000) ([#13235](https://github.com/diegosouzapw/OmniRoute/pull/13235)) +- **chore(quality):** retighten the `src/sse/handlers/chat.ts` unused-vars suppression count to the 8 violations that actually remain ([#13385](https://github.com/diegosouzapw/OmniRoute/pull/13385)) +- Bring the documented counts back in line with the code: 171 migrations (was 169) and 20 routing strategies (was 19), plus the regenerated `cli-tunnel` skill reference. ([#12970](https://github.com/diegosouzapw/OmniRoute/pull/12970)) +- Update the documented migration count to 172 after the call-logs provider-stats indexes landed. ([#13209](https://github.com/diegosouzapw/OmniRoute/pull/13209)) +- **chore(ci):** two gates that close the blind spots behind the exclusions above. `check:vitest-exclusions` requires every Vitest exclusion to name a tracking issue and to appear in `config/quality/vitest-exclusions.json` — the previous list grew to 62 files behind a comment pointing at an issue that had been closed for a month. `check-new-key-coverage` requires a key newly added to `en.json` to reach every locale; the existing coverage gate is a percentage floor per locale, so eleven absent keys out of ~13,000 left it at 99.9% while a whole feature shipped untranslated in nine languages. Both are diff-aware, so pre-existing debt stays frozen and neither needed a migration to turn on. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **build:** root layout no longer loads Inter from `next/font/google`, so a production image build does not need fonts.googleapis.com ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **docs(i18n):** bring the eight new `llm.txt` mirrors (km, kn, ml, my, ne, or, pa, si) up to the current provider and migration counts, so `check-docs-sync` stops rejecting every commit ([#13674](https://github.com/diegosouzapw/OmniRoute/pull/13674)) +- **refactor(ui):** flow surfaces (home topology, combo live studio, compression cockpit/waterfall/nodes, token-health badge) express state through the theme-aware `--orch-status-*` tokens instead of fixed dark-mode hex, so green/red/amber/grey stay legible in the light theme; dark mode is byte-identical. Categorical palettes (routing-strategy hues, compression-layer pills, provider brand colors) deliberately stay hex ([#12378](https://github.com/diegosouzapw/OmniRoute/issues/12378)) ([#13203](https://github.com/diegosouzapw/OmniRoute/pull/13203)) +- **chore(tests):** 51 test files that had been excluded from Vitest are running again, restoring roughly 350 assertions to the blocking suite. Each was measured individually first: of the 62 files parked behind the `// #8618 — pre-existing failure` comment, 51 pass against the current tree with no source change, so the exclusions had outlived the failures they were added for. The 11 that genuinely still fail stay excluded, but now point at a live tracker (#13204) rather than at #8618, which was closed in August while the list it tracked kept growing. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **chore(quality):** drop two ESLint suppression entries whose violations no longer exist, so `eslint --suppressions-location` stops rejecting every commit that touches the surrounding files ([#13384](https://github.com/diegosouzapw/OmniRoute/pull/13384)) +- **chore(release):** bring main's post-tag fixes into release/v3.8.51 — sync-back step 2/2 (direct commit `21c488f210`) +- **chore(deps):** patch toml and esbuild in the opencode plugin lockfiles ([#12965](https://github.com/diegosouzapw/OmniRoute/pull/12965)) +- **docs(changelog):** reconcile the v3.8.51 living section — fold 366 fragments, cover every cycle commit, credit every contributor ([#12971](https://github.com/diegosouzapw/OmniRoute/pull/12971)) +- **test(plugin):** wait for the v2 lazy-refresh tier instead of sleeping past it ([#12984](https://github.com/diegosouzapw/OmniRoute/pull/12984)) +- **chore(quality):** rebaseline chatCore.ts after the non-streaming regression fixes ([#13045](https://github.com/diegosouzapw/OmniRoute/pull/13045)) +- **docs(changelog):** add the missing bullet marker to the DeepSeek PoW fragment ([#13200](https://github.com/diegosouzapw/OmniRoute/pull/13200)) +- **chore(deps):** drain the Dependabot queue — 10 of 13 alerts ([#13213](https://github.com/diegosouzapw/OmniRoute/pull/13213)) +- **chore(ci):** make worktrees carry the hooks that enforce the identity gate ([#13236](https://github.com/diegosouzapw/OmniRoute/pull/13236)) +- **chore(deps):** pin csv-parse to ^7.0.2 via overrides (#13117) ([#13300](https://github.com/diegosouzapw/OmniRoute/pull/13300)) — thanks @KooshaPari +- **chore(deps):** bump hono from ^4.12.34 to ^4.13.7 (#13148) ([#13301](https://github.com/diegosouzapw/OmniRoute/pull/13301)) — thanks @KooshaPari +- **chore(deps):** pin joi to ^18.2.8 via overrides (#13085) ([#13302](https://github.com/diegosouzapw/OmniRoute/pull/13302)) — thanks @KooshaPari +- **docs(providers):** add Microsoft 365 Copilot (BizChat) provider guide (#12779) ([#13340](https://github.com/diegosouzapw/OmniRoute/pull/13340)) — thanks @KooshaPari +### 🙌 Contributors + +Thanks to everyone whose work landed in v3.8.51: + +| Contributor | PRs / Issues | +| --- | --- | +| [@5dive-bot](https://github.com/5dive-bot) | #11852 | +| [@abhisheksharma2411](https://github.com/abhisheksharma2411) | #11547, #11684, #11959, #12209 | +| [@adevwithpurpose](https://github.com/adevwithpurpose) | #11464 | +| [@adivekar-utexas](https://github.com/adivekar-utexas) | #12015, #12043, #12090, #12138 | +| [@AIB1TAL0S](https://github.com/AIB1TAL0S) | #12103 | +| [@alltomatos](https://github.com/alltomatos) | #11448 | +| [@alvinveroy](https://github.com/alvinveroy) | #11857, #12027, #12165 | +| [@amaleta](https://github.com/amaleta) | #11906, #11934 | +| [@amartinawi](https://github.com/amartinawi) | #11575 | +| [@AndrianBalanescu](https://github.com/AndrianBalanescu) | #11083, #11830, #11834, #11844 | +| [@anhtahaylove](https://github.com/anhtahaylove) | #13091, #13092, #13093, #13096, #13097, #13100, #13106, #13109, #13114, #13155, #13166, #13171, #13175, #13187, #13196 | +| [@aniruddhaadak80](https://github.com/aniruddhaadak80) | #11760 | +| [@app](https://github.com/app) | #12554 | +| [@arminanton](https://github.com/arminanton) | #11461, #11513 | +| [@AStupidBear](https://github.com/AStupidBear) | #11584 | +| [@atescivitci-cmd](https://github.com/atescivitci-cmd) | #12105 | +| [@b3nw](https://github.com/b3nw) | #11970, #11971, #12147 | +| [@backryun](https://github.com/backryun) | #11259, #11950, #12075, #12076, #12078, #12079, #12081, #12082, #12181, #12228, #12239, #12255, #12258, #12277, #12367, #12423, #12524, #12538 | +| [@benjaminkitt](https://github.com/benjaminkitt) | #11747 | +| [@benzntech](https://github.com/benzntech) | #11614, #11615 | +| [@Bl0ck154](https://github.com/Bl0ck154) | #11948, #11951, #11952, #11953, #11954 | +| [@brick30llc-ctrl](https://github.com/brick30llc-ctrl) | #11752, #12242, #12262 | +| [@Chewji9875](https://github.com/Chewji9875) | #11455, #11875, #12028 | +| [@cheynetom](https://github.com/cheynetom) | #12058 | +| [@davidebaraldo](https://github.com/davidebaraldo) | #12222, #12304 | +| [@davidlinfr](https://github.com/davidlinfr) | #6390 | +| [@ddarkr](https://github.com/ddarkr) | #12124 | +| [@Deftera186](https://github.com/Deftera186) | #11809 | +| [@dpozimski](https://github.com/dpozimski) | #11945 | +| [@DW-MediaLab](https://github.com/DW-MediaLab) | #12229 | +| [@echel0nn](https://github.com/echel0nn) | #11923 | +| [@f9td56dbgh-hub](https://github.com/f9td56dbgh-hub) | #11660 | +| [@fabioluissilva](https://github.com/fabioluissilva) | #11991 | +| [@foreveryh](https://github.com/foreveryh) | #12177 | +| [@geek007git](https://github.com/geek007git) | #12115, #12116, #12117, #12120, #12122 | +| [@geekyNads](https://github.com/geekyNads) | #11784, #12266 | +| [@ggiak](https://github.com/ggiak) | #11561, #12276, #12350, #12402 | +| [@gonisulaimann](https://github.com/gonisulaimann) | #12368, #12369, #12371 | +| [@Gorillaz322](https://github.com/Gorillaz322) | #12207 | +| [@groovecityJO](https://github.com/groovecityJO) | #12682 | +| [@hartmark](https://github.com/hartmark) | #11434, #11452, #11473, #11499, #11703, #11983, #11984, #11985, #11986, #11988, #11989, #11990, #11994, #12221, #12293, #12445, #12446, #12447, #12448, #12460, #12461, #12623, #12646, #12650, #12680, #12717, #12718, #12727, #12741, #12854 | +| [@hizzt](https://github.com/hizzt) | #11894 | +| [@hongnoul](https://github.com/hongnoul) | #11484 | +| [@HouMinXi](https://github.com/HouMinXi) | #11411, #11414, #11512, #11518, #11520, #11641, #11642, #11643, #11687, #11779, #11849, #11850, #11851, #11915, #11916, #11918, #11919, #11920, #12013, #12017, #12033, #12042, #12106, #12139, #12166, #12169, #12171, #12205, #12213, #12312, #12325, #12487, #12488, #12495, #12504, #12557, #12566, #12590, #12591, #12624, #12626, #12632, #12637, #12678, #12696, #12697, #12711, #12733, #12746, #12767, #12770, #12789, #12803, #12805, #12811, #12866, #12868, #12899, #12926, #12934, #12950, #12951, #12974, #13001, #13006, #13011, #13017, #13026, #13027, #13034, #13035, #13038, #13042, #13050, #13060, #13061, #13069, #13107, #13120, #13136, #13195, #13197 | +| [@Hsia97](https://github.com/Hsia97) | #11624 | +| [@jacobsparts](https://github.com/jacobsparts) | #11854, #12155, #12167 | +| [@joglomedia](https://github.com/joglomedia) | #11980 | +| [@jonlwheat2-gif](https://github.com/jonlwheat2-gif) | #11443, #11450, #11549, #11567, #11608, #11633, #11635, #11640, #11644, #11671, #11672, #11673, #11674, #11675, #11676, #11677, #11706, #11783, #12051, #12052, #12053, #12055 | +| [@kanade-hoshino](https://github.com/kanade-hoshino) | #12180 | +| [@Karan825](https://github.com/Karan825) | #11861 | +| [@KaspaPulse](https://github.com/KaspaPulse) | #11389, #11469, #11628, #11666, #11910 | +| [@keeltrace](https://github.com/keeltrace) | #12080, #12223 | +| [@killer30001000](https://github.com/killer30001000) | #12178 | +| [@KooshaPari](https://github.com/KooshaPari) | #12470, #12592, #12667, #12699, #12703, #12706, #12764, #12769, #12771, #13300, #13301, #13302, #13340, #13401, #13403, #13404, #13405, #13406, #13407, #13409, #13410, #13411, #13412, #13413, #13414, #13418, #13424, #13427, #13433, #13523, #13524, #13525, #13528, #13531, #13532, #13534, #13539, #13543, #13545, #13546, #13547, #13550, #13553 | +| [@kriptoburak](https://github.com/kriptoburak) | #11370 | +| [@KrzysiekSko](https://github.com/KrzysiekSko) | #12425, #12432, #12673 | +| [@marcelokarval](https://github.com/marcelokarval) | direct commit / report | +| [@maxmad64bis](https://github.com/maxmad64bis) | #1622, #11435, #11437, #11441, #11537, #11550, #11553, #11555, #11557, #11812, #11842, #11843, #11903, #12151, #12214, #12215, #12218, #12226, #12314, #12316, #12317, #12318, #12319, #12320, #12321, #12507, #12715, #12731, #12744, #12786, #12787, #12788, #12790, #12792, #12794, #12795, #12828, #12832, #12853, #12857, #12870, #12937, #12941, #12975, #13141, #13142, #13146, #13147 | +| [@mdigitalbh81](https://github.com/mdigitalbh81) | #12240 | +| [@Meet6338-X](https://github.com/Meet6338-X) | #11598, #11609, #12110 | +| [@morpheus9393](https://github.com/morpheus9393) | #11943 | +| [@MumuTW](https://github.com/MumuTW) | #11492, #11502, #11506, #11507, #11626, #11675, #11685, #11728, #11746, #11888, #11889, #11890, #11892 | +| [@Neuron-Mr-White](https://github.com/Neuron-Mr-White) | #11622, #11801, #12256 | +| [@NightStalker-87](https://github.com/NightStalker-87) | #12183 | +| [@NoxzRCW](https://github.com/NoxzRCW) | #11879, #11880, #11881, #11882, #11883, #11935 | +| [@ntdat812](https://github.com/ntdat812) | #11585, #12095 | +| [@ntdatt812](https://github.com/ntdatt812) | #11368, #11573, #11574, #11576, #11577, #11580, #11582, #11583, #11588, #11589, #11590, #11591, #11592, #11593, #11672, #12177, #12180, #12873, #12918, #12920, #12921, #12925, #12930, #13007, #13009, #13024, #13025, #13083, #13087, #13101, #13104, #13110 | +| [@official-burak](https://github.com/official-burak) | #11542 | +| [@opensource-elearning](https://github.com/opensource-elearning) | #12179, #12189, #12278, #12286 | +| [@oyi77](https://github.com/oyi77) | #11408, #11409, #11421, #11505, #11677, #12036, #12110 | +| [@pacocartones](https://github.com/pacocartones) | #11521, #11522, #11527, #11528, #11529, #11530, #11531, #11532, #11533, #11534, #11595, #11599, #11603, #11604, #11605, #11607, #11610, #11676, #11714, #11716, #11718, #11767, #11838, #11860, #11862, #11869, #11871, #11872, #11873, #11903, #11906, #11921, #11934, #12356, #12358, #12359, #12360, #12361, #12362, #12364, #12365, #12373, #12374, #12375, #12376, #12377, #12379, #12380, #12381, #12386, #12387, #12389, #12390, #12394, #12395, #12397, #12401, #12403, #12404, #12522, #12523, #12535, #12536, #12540, #12541, #12543, #12545, #12548, #12549, #12550, #12551, #12552, #12647, #12651, #12653, #13055 | +| [@patrykkopycinski](https://github.com/patrykkopycinski) | #11936, #11937, #12224 | +| [@PauloFH](https://github.com/PauloFH) | #11509 | +| [@PauloHSOliveira](https://github.com/PauloHSOliveira) | #12241 | +| [@ponkcore](https://github.com/ponkcore) | #12054 | +| [@Prajeeth-12](https://github.com/Prajeeth-12) | #11634, #12046 | +| [@quiterunner-commits](https://github.com/quiterunner-commits) | #12131, #12143 | +| [@rafacpti23](https://github.com/rafacpti23) | #11554, #11558, #12192, #12197, #12198, #12202, #12203, #12204 | +| [@ragnar-claude](https://github.com/ragnar-claude) | #11564 | +| [@raheemuddin786](https://github.com/raheemuddin786) | #11491, #11839, #11840, #11841, #12003, #12230, #12231, #12232, #12233, #12234 | +| [@rahilmavani](https://github.com/rahilmavani) | #11761 | +| [@Rahulsharma0810](https://github.com/Rahulsharma0810) | #11771 | +| [@RaviTharuma](https://github.com/RaviTharuma) | #11710, #11727, #11797, #11798, #11802, #11805, #11806, #11811, #12098, #12099, #12101, #12449, #12452, #12472, #12473, #12493, #12533, #12607, #12628, #12631, #12636, #12875, #12876, #12880, #12882, #12884 | +| [@rezjalibd](https://github.com/rezjalibd) | #12186 | +| [@RhianB14](https://github.com/RhianB14) | #11692 | +| [@rifqiawl](https://github.com/rifqiawl) | #11517, #11519, #11969 | +| [@rqzbeh](https://github.com/rqzbeh) | #11390 | +| [@Sabeekhann](https://github.com/Sabeekhann) | #11699, #11705 | +| [@santosraju99-hub](https://github.com/santosraju99-hub) | #11755, #11770 | +| [@Seramicx](https://github.com/Seramicx) | #11568 | +| [@Siva010](https://github.com/Siva010) | #12191 | +| [@solstxce](https://github.com/solstxce) | #11597 | +| [@soroush5](https://github.com/soroush5) | #12691 | +| [@tenshiak](https://github.com/tenshiak) | #12279 | +| [@TheDemonTuan](https://github.com/TheDemonTuan) | #11468, #11470, #11471, #11482, #11548, #11758, #11775, #11814 | +| [@thomasmaerz](https://github.com/thomasmaerz) | #12834 | +| [@tuandinh0801](https://github.com/tuandinh0801) | #11454 | +| [@turbolego](https://github.com/turbolego) | #11621, #11762, #11772, #11774, #11781, #12216 | +| [@Tushar49](https://github.com/Tushar49) | #11392 | +| [@ujjawalkaushik1110](https://github.com/ujjawalkaushik1110) | #11863 | +| [@vermasomesh835](https://github.com/vermasomesh835) | #11794 | +| [@vsd2807](https://github.com/vsd2807) | #11565, #11619 | +| [@wahidsadik371-coder](https://github.com/wahidsadik371-coder) | #12126 | +| [@watchingdogs](https://github.com/watchingdogs) | #12031 | +| [@wildcard](https://github.com/wildcard) | #11620 | +| [@xiaoyaner0201](https://github.com/xiaoyaner0201) | #11460, #11662, #12083 | +| [@Xxx91n](https://github.com/Xxx91n) | #11690 | +| [@yourspraveen](https://github.com/yourspraveen) | #11146 | +| [@yxyxy](https://github.com/yxyxy) | #11671 | +| [@ZaimMarzuki](https://github.com/ZaimMarzuki) | #11960 | +| [@Zartharas](https://github.com/Zartharas) | #11340 | +| [@zero-executioner](https://github.com/zero-executioner) | #11631 | +| [@diegosouzapw](https://github.com/diegosouzapw) | maintainer | + +--- ## [3.8.50] — 2026-08-25 _Living section — regenerated 2026-08-12 from all cycle commits (cycle open `ed2db6cb19` → tip). Bullets carry the merged PR and its author; direct pushes listed separately._ diff --git a/docs/i18n/bg/CHANGELOG.md b/docs/i18n/bg/CHANGELOG.md index d64ce7212b..99fdb9c3a2 100644 --- a/docs/i18n/bg/CHANGELOG.md +++ b/docs/i18n/bg/CHANGELOG.md @@ -8,16 +8,1119 @@ ## [3.8.51] — TBD -_Living section — cycle opened at the v3.8.50 freeze (parallel-cycle model). Bullets are aggregated from `changelog.d/` fragments at each `/generate-release` phase._ +_Living section — reconciled 2026-09-15 from all cycle commits (`091589089c` → `c0f92ec98a`, 916 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 | **123** | +| 📝 Commits in the cycle | **916** | +| 🔀 Pull requests referenced | **892** | +| 📋 Changelog entries | **919** | +| 🙌 Contributors credited in entries | **121** | +| 🤖 Automated dependency commits | 18 | + +**Entries by type** + +| Type | Count | +| --- | ---: | +| 🐛 Fixes | 592 | +| ✨ Features | 131 | +| 🧹 Chore | 93 | +| 📚 Docs | 42 | +| 🧪 Tests | 32 | +| ♻️ Refactor | 8 | +| 🏗️ Build | 7 | +| ⚡ Performance | 7 | +| ⚙️ CI | 2 | +| ⏪ Reverts | 2 | +| 📦 Dependencies | 2 | +| 🔒 Security | 1 | + +### 🏆 Top 25 contributors this cycle + +_By commits in `091589089c..c0f92ec98a`, author identities consolidated via `.mailmap` and the merged PR's GitHub login. Bots excluded._ + +| # | Contributor | Commits | +| ---: | --- | ---: | +| 🥇 | diegosouzapw | 323 | +| 🥈 | Bob.Hou (@HouMinXi) | 79 | +| 🥉 | Paco Cartones (@pacocartones) | 77 | +| 4 | Dizzle (@maxmad64bis) | 49 | +| 5 | Koosha Paridehpour (@KooshaPari) | 43 | +| 6 | Nguyen Thanh Dat (@ntdatt812) | 31 | +| 7 | Ravi Tharuma (@RaviTharuma) | 26 | +| 8 | Markus Hartung (@hartmark) | 22 | +| 9 | Webman (@jonlwheat2-gif) | 22 | +| 10 | backryun | 17 | +| 11 | anhtahaylove | 15 | +| 12 | Rafa Martins (@rafacpti23) | 10 | +| 13 | Syed Raheemuddin (@raheemuddin786) | 10 | +| 14 | Nguyễn Viết Tuấn (@TheDemonTuan) | 8 | +| 15 | MumuTW | 7 | +| 16 | Paijo (@oyi77) | 7 | +| 17 | Tobias Andersen (@turbolego) | 6 | +| 18 | Bl0ck (@Bl0ck154) | 5 | +| 19 | SHANMUGAPRIYAN (@geek007git) | 5 | +| 20 | KaspaPulse | 5 | +| 21 | Abhishek Sharma (@abhisheksharma2411) | 4 | +| 22 | Abhishek Divekar (@adivekar-utexas) | 4 | +| 23 | Andrew B. (@AndrianBalanescu) | 4 | +| 24 | NoxzRCW | 4 | +| 25 | opensource-elearning | 4 | ### ✨ New Features +- **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). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- Added Google AI Studio Gemini batch text-to-speech support through `POST /v1/audio/speech`. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- 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. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(routing):** subscription-first auto groupings — `auto/subscription` routes only through plan-included connections with a documented hard-stop overage and fails closed on exhaustion, while `auto/thrifty` orders the pool `subscription → keyless → free → cheap → premium` and steps up one rung at a time as each is exhausted. Billing class comes from a curated per-connection catalog (uncurated is treated as metered, never plan-included), both reuse STRICT_ZERO_COST's per-connection verification, and a quota reading whose `resetAt` has passed is now refreshed regardless of TTL so routing returns to plan capacity as soon as it resets ([#11146](https://github.com/diegosouzapw/OmniRoute/pull/11146)) — thanks @yourspraveen +- **feat(providers):** publish a management-authenticated versioned web-session credential contract from OmniRoute's canonical browser credential metadata ([#11340](https://github.com/diegosouzapw/OmniRoute/pull/11340)) — thanks @Zartharas +- **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)) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add Xquik X search with typed results, credential validation, REST routing, and MCP selection ([#11370](https://github.com/diegosouzapw/OmniRoute/pull/11370)) — thanks @kriptoburak +- **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)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(dashboard):** surface durable exclusive managed leases in the existing Sessions view, keeping leased clients visible across idle gaps while marking connections with in-flight work as active ([#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389)) — thanks @KaspaPulse +- **build(bun):** allow Turbopack bundler flag on Bun 1.4+ with configurable Webpack fallback ([#11471](https://github.com/diegosouzapw/OmniRoute/pull/11471)) — thanks @TheDemonTuan +- feat(api): add an opt-in `modelVisibilityAllowlist`/`modelVisibilityDenylist` settings pair to curate exactly which models `/v1/models` advertises, mirrored into every `auto/*` combo candidate pool so a denied model cannot be routed to via combo selection either (#11481) ([#11997](https://github.com/diegosouzapw/OmniRoute/pull/11997)) +- **feat(rankings):** the Free Provider Rankings page shows what each provider actually served over the last 24 h. It ranked by ELO alone, which left a provider that answers every call with an error in first place; the usage data was already served by the API but never requested. A provider with too small a sample shows a dash, not a number ([#11546](https://github.com/diegosouzapw/OmniRoute/pull/11546)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **feat(guardrails):** enforce a bounded, deterministic contract for Video Bridge transcripts — 256 cues, 4096 input code units and 4 KiB UTF-8 per cue, 64 KiB total text, malformed-Unicode rejection, focus-window scoping, cross-source reconciliation with contributing-source metadata, and a structural provenance trust boundary so caller JSON can never self-assert `embedded`/`audio-bridge` provenance ([#11652](https://github.com/diegosouzapw/OmniRoute/issues/11652)) ([#12009](https://github.com/diegosouzapw/OmniRoute/pull/12009)) +- **feat(video):** orchestrate optional Video Bridge audio extraction and Audio Bridge STT behind a dual opt-in (operator setting AND per-request signal) — a new loopback-only broker `mode=audio` operation shares the frame path's exact process queue, deadline, AbortSignal, and byte budgets to extract a bounded mono 16 kHz PCM WAV from the same already-downloaded video, then reuses the existing Audio Bridge transcription boundary; provider segment timing is preserved when available and marked coarse otherwise, and every failure degrades to a visual-only-safe partial instead of throwing (#11654). ([#12012](https://github.com/diegosouzapw/OmniRoute/pull/12012)) +- Add a tenant-bound Video Bridge drill-down lifecycle on top of the existing secure cache substrate: opaque hashed handles (never raw session/video identifiers), preview/standard/detail multiresolution variants resampled on read, response pagination capped at 8 frames and 32 MiB, and a new authenticated `/api/v1/video-bridge/drilldown` consumer route that stays disabled for remote access by default and denies cross-key access with the same response as a nonexistent handle (no existence oracle). ([#12006](https://github.com/diegosouzapw/OmniRoute/pull/12006)) +- **test(video):** Add the Video Bridge FU-07/FU-09 promotion-evidence harness (#11656) — a frozen Zod manifest schema covering the 8 required scenario kinds (static scenes, rapid cuts, late facts, fades, blur, small text, close events, visual prompt injection) with a minimum of 3 repetitions per case, deterministic declarative fixture recipes (`videoBridgePromotionFixtures.ts`), a pure medians/p95 metrics aggregator, a pure FU-07/FU-09 promotion-verdict evaluator applying the ticket's exact thresholds (missing token usage always holds), a digest-only persistence layer that never retains raw media or raw model responses, and a versioned per-model promotion allowlist shipped empty with every model defaulting to `hold`. The FU-07/FU-09 promotion verdicts themselves remain HOLD — they require a real evidence run against real models on VPS 192.168.0.15. ([#12008](https://github.com/diegosouzapw/OmniRoute/pull/12008)) +- **feat(video bridge):** "embedded" transcript provenance can now be legitimately earned instead of merely asserted — a bounded, allowlisted (`mov_text`/`subrip`/`webvtt`) subtitle probe runs through the loopback-only Video Bridge broker (at most 2 streams, 10s subdeadline bounded by the request deadline, 256 KiB output, 4096-code-unit lines), normalized through a bounded, ReDoS-safe WebVTT parser and Zod-validated end to end. The adapter always resolves to an explicit `success`/`absent`/`transient_failure` outcome — a subtitle failure never breaks the visual description path, and only a fingerprint-verified broker response (never a caller-declared label) can produce embedded cues (#11659). ([#12011](https://github.com/diegosouzapw/OmniRoute/pull/12011)) +- Default new Antigravity-family connections (agy CLI imports and Antigravity OAuth connects) to model auto-sync, so live model discovery lands in the synced catalog and `/v1/models` picks up freshly released upstream models (e.g. Gemini 3.7 Flash tiers) without code changes. Existing connections keep their current setting; the per-connection dashboard toggle remains the opt-out. (#11685 — thanks @MumuTW) ([#11887](https://github.com/diegosouzapw/OmniRoute/pull/11887)) +- **feat(ui):** enable React Compiler (`reactCompiler: true` + `babel-plugin-react-compiler`) for automatic memoization at build time ([#11783](https://github.com/diegosouzapw/OmniRoute/pull/11783)) — thanks @jonlwheat2-gif +- **feat(providers):** add SeekAi (`seekai.cc`) as an OpenAI-compatible New-API gateway — catalog id `seekai` (alias `ska`), `https://seekai.cc/v1`, live `/v1/models` via `passthroughModels`, aggregator-list membership so New-API balance detection can opt in. No referral/aff codes. ([#11786](https://github.com/diegosouzapw/OmniRoute/issues/11786)) ([#12557](https://github.com/diegosouzapw/OmniRoute/pull/12557)) — thanks @HouMinXi +- **feat(zai):** add GLM-5.3-Flash Coding Plan support (1M context, 128K output, vision, `low|high|max` reasoning) and route `zai` GLM-5.3-family API-key traffic through the OpenAI-compatible Coding Plan endpoint with native thinking defaults ([#11801](https://github.com/diegosouzapw/OmniRoute/pull/11801)) — thanks @Neuron-Mr-White +- **feat(combo):** choose how combo models are ordered — manual, provider, score, or name — via a sort control in the dashboard builder, persisted in `config.modelSort` and re-applied on load and after add ([#11812](https://github.com/diegosouzapw/OmniRoute/pull/11812)) — thanks @maxmad64bis +- **feat(free):** custom models can be marked free-tier via `customModels[].isFree`; `isFreeModel()` is the first door and `hidePaidModels` respects it even for providers outside the free budget ([#11843](https://github.com/diegosouzapw/OmniRoute/pull/11843)) — thanks @maxmad64bis +- **feat(nodejs):** add `5dive` as a `configure` target — `omniroute configure 5dive` / `omniroute setup-5dive` write a 5dive auth profile that points an agent fleet's `claude` seats at OmniRoute, with the root-only write, the loopback-vs-`https` endpoint rule and the per-seat model pin handled explicitly ([#11852](https://github.com/diegosouzapw/OmniRoute/pull/11852)) — thanks @5dive-bot +- **feat(sse):** treat `max` as a first-class reasoning-effort tier and clamp per model family (GLM 5.1+/DeepSeek V4+/Kimi K3+ keep native `max`; o1/MiniMax/Grok/Muse Spark clamp to their upstream ceiling) ([#11875](https://github.com/diegosouzapw/OmniRoute/pull/11875)) — thanks @Chewji9875 +- **feat(providers):** the provider plugin manifest now advertises a `usage-fetch` capability for the 40 providers that have a wired usage/quota fetcher, so external dashboards can read it from `GET /api/v1/provider-plugin-manifest` instead of parsing `open-sse/services/usage.ts` after every release. Discovery only — no new fetcher, no quota change, and the Dashboard quota widget stays gated by `USAGE_SUPPORTED_PROVIDERS`. `USAGE_FETCHER_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/fetcherProviders.ts`) and is re-exported from `services/usage.ts`, keeping the manifest module a light leaf instead of pulling the ~490-module usage dispatcher into the manifest route. ([#11903](https://github.com/diegosouzapw/OmniRoute/pull/11903)) — thanks @maxmad64bis / @pacocartones +- **feat(plugins):** `OMNIROUTE_PLUGINS_DIR` sets the directory the runtime plugin scanner reads — and the root the plugin manager installs into — overriding the `HOME`-derived default, so a Docker/K8s deployment can point straight at its bind-mounted plugin tree instead of moving `HOME` just to relocate the scan path. An image that exports no home no longer scans `/tmp/.omniroute/plugins` in silence: the resolved directory is logged once at startup as `scanner.dir_resolved`, naming the input that won. Unset, behaviour is unchanged. Distinct from the CLI-only `OMNIROUTE_PLUGIN_PATH`, which finds `omniroute-cmd-*` command packages and never reached this scanner ([#11906](https://github.com/diegosouzapw/OmniRoute/pull/11906)) — thanks @amaleta / @pacocartones +- **feat(leases):** add an explicit owner-authenticated status action that returns only the active lease's privacy-safe configured connection and provider labels, with generation fencing and no credential or internal-id disclosure ([#11910](https://github.com/diegosouzapw/OmniRoute/pull/11910)) — thanks @KaspaPulse +- **feat(providers):** import-from-file modal shows per-row API errors and ships a downloadable CSV template ([#12071](https://github.com/diegosouzapw/OmniRoute/issues/12071)) ([#12504](https://github.com/diegosouzapw/OmniRoute/pull/12504)) — thanks @HouMinXi +- **feat(providers):** dashboard search matches connection name and `baseUrl` so imported OpenAI-compat nodes surface on the provider card ([#12108](https://github.com/diegosouzapw/OmniRoute/issues/12108)) ([#12495](https://github.com/diegosouzapw/OmniRoute/pull/12495)) — thanks @HouMinXi +- **feat(routing):** add a `score` Auto router strategy that selects the highest configured weighted score and reuses `explorationRate`. ([#12155](https://github.com/diegosouzapw/OmniRoute/pull/12155)) — thanks @jacobsparts +- Add a runtime feature flag to disable universal context handoffs globally without changing the default behavior. ([#12167](https://github.com/diegosouzapw/OmniRoute/pull/12167)) — thanks @jacobsparts +- **feat(providers):** the provider plugin manifest now also advertises a `usage-supported` capability for the 46 providers whose usage API is accepted by the server and Dashboard routes, so integrators can distinguish "the server will serve quota for this provider" from "a fetcher is wired" without reading TypeScript. Discovery only — no fetcher or quota change. `usage-fetch` resolves on id or alias (the usage dispatcher accepts both); `usage-supported` resolves on id alone, matching the runtime guard `USAGE_SUPPORTED_PROVIDERS.includes(providerId)`. `USAGE_SUPPORTED_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/supportedProviders.ts`) and is re-exported from `providers.ts`, mirroring the `fetcherProviders` leaf from #11903 and keeping the manifest a light module. ([#12214](https://github.com/diegosouzapw/OmniRoute/pull/12214)) — thanks @maxmad64bis +- **feat(rankings):** order Free Provider Rankings by what each provider actually served — `GET /api/free-provider-rankings?sortBy=reliability` and a "Most reliable first" toggle on the page. Providers with too few calls to state a success rate keep their score order below the measured ones; the default order is unchanged ([#12218](https://github.com/diegosouzapw/OmniRoute/pull/12218)). — thanks @maxmad64bis +- **perf(sse):** defer `cloneLogPayload()` in the structured SSE collector until after the `maxEvents`/`maxBytes` cap check, eliminating ~9,800 wasted `structuredClone` calls per streaming response (65–71% faster `push()`). Reducer snapshot isolation restored for OpenAI and Responses summaries ([#12241](https://github.com/diegosouzapw/OmniRoute/pull/12241)) — thanks @PauloHSOliveira ([#12243](https://github.com/diegosouzapw/OmniRoute/pull/12243)) +- **feat(usage):** Devin CLI agentic quota (Codeium seat-management GetUserStatus) and OpenRouter key limits plus account credits now surface in Provider Limits ([#12256](https://github.com/diegosouzapw/OmniRoute/pull/12256) — thanks @Neuron-Mr-White) +- **feat(settings):** persist `headroomUrl` through Settings so status/start use the operator URL instead of only `HEADROOM_URL` ([#12306](https://github.com/diegosouzapw/OmniRoute/issues/12306)) ([#12487](https://github.com/diegosouzapw/OmniRoute/pull/12487)) — thanks @HouMinXi +- **feat(auto-combo):** Auto-Combo scoring can now weigh how often a provider/model has actually succeeded. The engine already carried that number on every candidate — 24 hours of usage history behind a ten-sample floor, real-time metrics otherwise — and the scoring function never read it, while the weight table described `stability` as if it did. `reliability` (`1 - failureRate`, with the same field precedence and the same rate-bounding the speed ranking already uses, so a corrupt reading means "nothing observed" rather than "fails every call") is now a declared factor shipping at weight `0`, so routing is unchanged until an operator gives it one, and the `stability` description now matches what that factor computes ([#12317](https://github.com/diegosouzapw/OmniRoute/pull/12317)) — thanks @maxmad64bis +- **feat(routing):** With `freeAccessPolicy: "strict"`, the read-only candidate listing (`GET /v1/auto-combo/{channel}/candidates`) no longer hides the candidates the zero-cost guard excludes — the same read-only transparency the resilience filter already honours (#9133). Each candidate now carries `freeAccessExclusion` saying why it would be kept out, and it tells an exhausted allowance apart from a quota reading that never arrived or went stale, which used to look identical from the outside. Routing is unchanged: the listing reports, it never enforces. The separate `excludeTosAvoid` guard still drops its candidates without a reason; that gap is now documented rather than closed ([#12319](https://github.com/diegosouzapw/OmniRoute/pull/12319)) — thanks @maxmad64bis +- **feat(radar):** The Radar catalog table now shows two facts it was already receiving from the feed and dropping on the floor: the per-model rate limits (requests and tokens, per minute and per day) in a new column, and a badge when a provider's terms state it may train on the prompts you send. A limit of zero renders as zero rather than "rate-only" — for a ceiling those are opposite facts — and a model with no training statement gets no badge, because an absent statement is not a guarantee ([#12320](https://github.com/diegosouzapw/OmniRoute/pull/12320)) — thanks @maxmad64bis +- **feat(radar):** explain Community, single-use, contributor, supporter, recovery, abuse, offers, and privacy rules before either Radar activation action, and remove the superseded fixed-PR grant promise from every UI locale ([#12342](https://github.com/diegosouzapw/OmniRoute/pull/12342)) +- **feat(gamification):** the dashboard Profile page now shows the real daily streak — `/api/gamification/level` returns `streak: { current, longest }` (per key with `apiKeyId`, operator-wide maximum otherwise) and the streak card reads it instead of a hard-coded 0 (#2403) ([#12377](https://github.com/diegosouzapw/OmniRoute/pull/12377)) — thanks @pacocartones +- **feat(gamification):** the dashboard leaderboard now shows each API key's display name under the Name column instead of a truncated key id; `GET /api/gamification/leaderboard` attaches `name` per entry (name only — no key material), while the shared ranking helper and the federation leaderboard stay id-only — thanks @pacocartones ([#12385](https://github.com/diegosouzapw/OmniRoute/pull/12385)) +- **feat(gamification):** enforce the documented 1000 XP/min per-API-key anti-cheat rate limit on the XP award path; over-limit awards are logged and skipped instead of persisted, and the sliding window now matches the timestamp format stored in `xp_audit_log` ([#2403](https://github.com/diegosouzapw/OmniRoute/issues/2403)) ([#12390](https://github.com/diegosouzapw/OmniRoute/pull/12390)) — thanks @pacocartones +- **feat(admin):** localize the gamification anomalies page — the loading state, the Status column and the Suspicious badge now come from the `common` catalog (new `common.suspicious` key propagated to every locale) — add it to the Gamification sidebar group as `gamification-admin` (`/dashboard/gamification/admin`), and expose the loading and empty states as polite `role="status"` live regions (#12401 — thanks @pacocartones) +- **feat(providers):** skip GitHub combo members missing from the live synced catalog, and drop Copilot models that are policy-disabled or hidden from the model picker ([#12473](https://github.com/diegosouzapw/OmniRoute/pull/12473)) — thanks @RaviTharuma +- **feat(i18n):** locale-expansion tooling — `npm run i18n:add-locale` adds a language to config, dashboard, docs mirrors, CLI, README/indexes and site in one command; browser, CLI and cookie detection resolve `uk`, `fil`/`tl`, `zh-Hant` (and the retired legacy `in` → `id`) via config aliases; `config/i18n.json` now ships in the npm package so the published CLI can read it; new real-translation ratio gate (`npm run i18n:check-ratio`, advisory) with a per-locale ratchet baseline; `run-translation --adopt` restores `.i18n-state.json`; `sync-language-bars` generates 🌐 bars from config; `validate_translation.py` loads its allowlist again. Retires the duplicate `in` locale (Indonesian mislabelled as Hindi) — 42 honest locales; saved `NEXT_LOCALE=in` / `OMNIROUTE_LANG=in` keep working. (#12496) +- **feat(api):** Emit gateway-measured `tokens_per_second` (TTFT excluded) on streaming usage and `X-OmniRoute-Tokens-Per-Second` when first-token latency is known ([#12616](https://github.com/diegosouzapw/OmniRoute/issues/12616)) ([#12631](https://github.com/diegosouzapw/OmniRoute/pull/12631)) — thanks @RaviTharuma +- **feat(opencode-plugin): map gateway cost/usage/tok/s onto OpenCode inference payloads** — the official plugin copies `X-OmniRoute-Response-Cost`, token counts, `X-OmniRoute-Tokens-Per-Second` / `usage.tokens_per_second`, TTFT, and the winning `X-OmniRoute-Model` onto the JSON/SSE body OpenCode already consumes. Missing tok/s is left unset (never `tokens / latency`). (#12636) — thanks @RaviTharuma +- **feat(combo):** add combo strategy `quota-weighted`: skip exhausted accounts, then weighted-draw by leftover / in-flight load; existing conversations stay pinned until the account is empty ([#12789](https://github.com/diegosouzapw/OmniRoute/pull/12789)) — thanks @HouMinXi +- **feat(models):** Account live listings become the chat catalog source for Claude, Codex, Copilot, and AGY; public metadata only fills prices on IDs those accounts already list. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **fix(models):** Union `agy` and `antigravity` live catalogs so `agy/gemini-3.8-flash-high` is not rejected after the prefix folds to `antigravity`. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **feat(opencode):** Add `@omniroute/opencode-plugin-v2`, an opencode v2 plugin that gives a v2 install a route to an OmniRoute gateway: it publishes models, combos and auto-combos into the host catalog, refreshes them lazily behind a 300s TTL, and keeps serving the last known catalog from an on-disk snapshot when the gateway is unreachable. The v1 plugin (`@omniroute/opencode-plugin`) is untouched and keeps working as before. The plugin adapts to the host instead of assuming one shape: it reads the shape the host seeds into the catalog draft and publishes the `api` block, the pre-`api` top-level fields, or both. The gateway key comes from the host credential store when one is connected, so no secret needs to sit in a config file; a source the gateway refuses is named in a warning instead of silently degrading the catalog to raw model ids; and tool schemas bound for a Gemini model are stripped of the JSON-Schema keywords Gemini rejects, so tool calling survives that route ([#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870)) — thanks @maxmad64bis +- feat(services): show sanitized CLIProxyAPI account health from its authenticated management API without exposing credentials, file paths, or raw account metadata (#6342) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add AnySearch free web search + URL extract (webFetch) with typed results, credential validation, REST routing, and MCP selection - fallback-only ([#11690](https://github.com/diegosouzapw/OmniRoute/pull/11690)) — thanks @Xxx91n +- **feat(dashboard):** display clamped `[0, 100]%` cached input token ratio in request logs table ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) — thanks @b3nw +- **feat(catalog):** add `OMNIROUTE_DISABLE_THINKING_LEVEL_VARIANTS` feature flag to optionally filter out thinking level variants from model catalog ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) — thanks @b3nw +- **feat(dashboard):** continuously export call logs to external analytics stores. A pluggable destination registry ships the full Logs-tab record set on an hourly `JobRegistry` cron, with a persisted per-destination cursor, batched inserts, a config UI rendered from each destination's own field descriptors, and a REST layer (`/api/log-export/*`) for CRUD, a connection test, and an on-demand run. A destination can opt into `includeBodies` to also ship the request and response payloads shown in the Logs detail pane, including the client and provider views of each call; this is off by default, and payloads inherit the dashboard's PII sanitisation, secret redaction and `noLog` handling. Google BigQuery is the first destination, using a service-account key stored encrypted at rest and streaming inserts keyed by call-log id, into a table that is day-partitioned on `timestamp` and clustered on `api_key_name`, `provider`, `model` and `status`. ([#11945](https://github.com/diegosouzapw/OmniRoute/pull/11945)) — thanks @dpozimski +- **feat(providers):** add MaxAI as a signed, OpenAI-compatible provider serving its 13 paid chat models (GPT-5.6 / Luna / Thinking, Claude 5 Sonnet, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite, Grok 4.1-fast / 4.5, DeepSeek V3.2 / R1, Llama 3.3 70B) through OmniRoute's `/v1` endpoint, with per-request HMAC-SHA1→SM3→AES request signing, live model + context-window discovery from `/models/get_config`, and prompted tool-calling translated to OpenAI `tool_calls` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI vision input — image_url content parts are forwarded inline in `message_content` to the 6 vision-capable models (GPT-5.6 / Luna / Thinking, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite) ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI image generation — 6 image models (gpt-image-1, dall-e-3, flux-1-schnell/dev/pro, sd3-medium) exposed through `POST /v1/images/generations` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI document RAG — inline base64 file/document attachments are uploaded to MaxAI (content-addressed `doc_id`) and attached to the chat via `doc_list` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** browserless MaxAI onboarding — email device-pair login (`/api/providers/[id]/login`) and signed access-token refresh, so a connection can be created and kept fresh without a real browser or Google OAuth ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** per-provider TLS impersonation profile (MaxAI presents a Windows Firefox-150 client fingerprint) so its bot-sensitive endpoints accept OmniRoute traffic ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** add **Nimble** as a web-search and web-fetch provider (`nimble-search`) — `/v1/search` routes to Nimble's search API at `lite` depth (locale, freshness and include/exclude domain filters mapped onto the shared request contract), and `/v1/web/fetch` routes to Nimble Extract, which covers all four fetch formats (`markdown`, `html`, `links`, `screenshot`) from a single call. One API key serves both surfaces. ([#11620](https://github.com/diegosouzapw/OmniRoute/pull/11620)) — thanks @wildcard +- The npm publish is automatic again, through npm Trusted Publishing (OIDC): the hosted `stage-npm` job publishes with a short-lived credential minted from GitHub's id-token — no `NPM_TOKEN`, no 2FA prompt, provenance attached. `publish_mode=staged` (owner approves with 2FA) and `direct` (token) remain available on `workflow_dispatch`. ([#11931](https://github.com/diegosouzapw/OmniRoute/pull/11931)) +- **feat(providers):** Add **Opper** as an API-key gateway provider — EU-hosted AI gateway with 700+ models from 30+ providers behind one OpenAI-compatible API and one key (`OPPER_API_KEY`); model ids use `provider/model` format (e.g. `anthropic/claude-sonnet-4-6`, `openai/gpt-5`); live model catalog at `https://api.opper.ai/v3/compat/models`; entry mirrors `requesty` (same shape, `passthroughModels: true`, no static seed) ([#11629](https://github.com/diegosouzapw/OmniRoute/pull/11629)) +- **feat(dashboard):** the `/dashboard/orchestration` snapshot hook now subscribes to the `agents` WebSocket channel (`agent.task.updated`) instead of `requests` as its refetch trigger, and relaxes its background poll from 5s to 30s while that WS connection is up — falling back to the tighter 5s cadence, reprogrammed live on any connect/disconnect transition, whenever the socket is down. ([#12409](https://github.com/diegosouzapw/OmniRoute/pull/12409)) +- **feat(dashboard):** Orchestration canvas quick wins — search box plus state/source/provider filter chips with a one-click clear, and per-source collapse/expand, all reflected in the URL so a filtered/collapsed view is shareable and survives a refresh; the detail drawer gained a "copy trace JSON" action and hardened error/empty-state and accessibility handling; the Agents-tab edges now animate traveling particles along active (running) connections; and the canvas node/edge status colors moved off fixed hex values onto theme-aware `--orch-status-*` CSS custom properties, so they adapt correctly to light/dark mode. ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- **chore(dashboard):** Orchestration UI hardening pass and the missing component/model test coverage it called for — `OrchestratorNode`/`ActivityNode`/`OverflowNode` rendering, the `?node=`/overflow-click page routing, the Agents-tab orchestrator-click no-op and `showCompleted` toggle, and the overview kanban's done-column sort order (#12270, #12271). ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- New `/dashboard/orchestration` page: live unified view of everything running — Cloud Agent, A2A and Conductor as a real-time graph (Agents tab), the combo cascade (Routing tab, reusing the Combo Live Studio) and a state kanban (Overview tab), with a detail drawer (trace, cost, approve/cancel). Read-only over existing APIs — no new backend. Canvas concept credit: PR #11815 design ([#12261](https://github.com/diegosouzapw/OmniRoute/pull/12261)) +- **feat(dashboard):** new "History" tab on `/dashboard/orchestration` — an Airflow-style grid of finished runs over a 24h/7d/30d preset window, one row per (source, identity), clicking a cell opens the existing detail drawer. It is backed by real persistence: A2A task lifecycle transitions are now written to the `a2a_tasks` table (purged after 30 days, configurable via `OMNIROUTE_A2A_HISTORY_RETENTION_DAYS`) and served by the new `GET /api/a2a/tasks/history` listing endpoint, with the task-detail route falling back to persisted history once a run leaves the in-memory snapshot. Conductor runs stay remote and are not persisted locally — the tab says so instead of silently omitting them. ([#12479](https://github.com/diegosouzapw/OmniRoute/pull/12479)) +- **feat(dashboard):** the orchestration detail drawer gained a "Repeat" action for Cloud Agent, A2A and Conductor tasks — a two-click confirm (click once to arm, click again within the confirm window to fire) re-submits the original prompt/input as a new run. The button is disabled with an explanatory tooltip whenever the original input can't be recovered from the loaded task detail (e.g. it never carried a prompt, or the detail failed to load). Two limitations of the A2A variant, by design: it targets the `/a2a` JSON-RPC endpoint, which authenticates with an API key only (`REQUIRE_API_KEY=true` or a configured `OMNIROUTE_API_KEY` makes a dashboard-session repeat answer `HTTP 400` — surfaced verbatim in the drawer's error line, never as a success), and the `message/send` call is SYNCHRONOUS: the POST blocks for the whole skill run, so the success confirmation only appears once the repeated task finishes. A dashboard-authenticated A2A creation path is deliberately left to a follow-up — widening the endpoint's auth posture is an operator decision, not a side effect of this feature. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(a2a):** A2A task execution now records which memories were consulted for the task's last user message as `metadata.memoryHits` (id/key/type/content-snippet) plus a `memory_hits` history event, purely for observability — the retrieved memory is never injected into a skill's prompt or behavior. Gated by the `OMNIROUTE_A2A_MEMORY_HITS` kill-switch (default enabled; set to `0` to skip the recall lookup entirely). The drawer's new "Memory used" section lists these hits for a2a tasks and is omitted whenever there are none. Known limitation: recall only resolves under the keyless posture — a keyed caller's task owner is a SHA-256 prefix of the API key, while memory rows are keyed by the database api-key id, and no hash→id lookup exists today, so the hit list stays empty for keyed callers. The recorded hits are also kept out of the task's own `input` (and therefore out of the persisted input and of the "Repeat" request body), so repeating a task never re-sends the previous run's memory snippets. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(providers):** add a Perplexity Agent API provider (`perplexity-agent` / `pplx-agent`) for Perplexity `/v1/responses`, including the documented Anthropic, OpenAI, Google, xAI, DeepSeek, Z.AI, Moonshot/Kimi, NVIDIA, and Perplexity model IDs plus Anthropic-model `max_output_tokens` compatibility. ([#12103](https://github.com/diegosouzapw/OmniRoute/pull/12103)) — thanks @AIB1TAL0S +- **feat(providers):** add RPD (Requests Per Day) limit to provider rate limit overrides across UI, schemas, DB, and i18n ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) — thanks @b3nw +- **feat(providers): add UC Direct (uncensored.com Developer API), the metered OpenAI-compatible surface.** A standard OpenAI-compatible passthrough (default executor) for uncensored.com's official REST API at `https://api.uncensored.com/api/v1`: `X-api-key` auth (never-expiring `uai_sk_live_` key), `POST /chat/completions` with streaming SSE and native tool-calling, and the full live metered catalog (82 models across 15 providers, discovered from the public `GET /v1/models`). Registered as provider `uc-direct` (alias `ucd`). Complements the un-metered `uc` persona provider — same models, metered credits and a plain API key instead of a subscription session. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(providers): add UC (uncensored.com), the un-metered subscription "persona" chat as an OpenAI-compatible provider.** A WebSocket web-app port: a durable Clerk credential mints a short-lived session token per connect (browserless — no API key), driving UC's persona socket. Ships the browserless email-code login (request → verify → harvest), the 19 verified persona models (Claude Opus, Gemini, Grok, GLM, Kimi, DeepSeek, MiniMax, incl. the uncensored variants), prompted `` tool-calling with a per-model code-style dialect + auto-cure retry for guardrailed models, live ``/reasoning split, streaming + non-streaming OpenAI responses, and full quota/auth error surfacing (paywall / message-limit / rate-limit → 429, invalid session → 401 re-login). Full multimodal parity via the persona blob-upload layer: **vision** (image input, 15 vision-capable models), **document RAG** (PDF/doc upload, server-side extraction), **image generation** (22 models), **video generation** (14 models, async signed-url → poll), and **TTS** (streaming MP3). Registered as provider `uc` (alias `ucn`). The metered OpenAI-compatible Developer API is a separate `uc-direct` provider. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(logs):** add copy-all button to request detail modal ([#11083](https://github.com/diegosouzapw/OmniRoute/pull/11083)) — thanks @AndrianBalanescu +- **feat(check):** make serviceKinds required and add the reverse-walk provider consistency gate ([#11392](https://github.com/diegosouzapw/OmniRoute/pull/11392)) — thanks @Tushar49 +- **feat(providers):** only advertise verified-working connections by default ([#11448](https://github.com/diegosouzapw/OmniRoute/pull/11448)) — thanks @alltomatos +- **feat(resilience):** add hierarchical concurrency admission ([#11493](https://github.com/diegosouzapw/OmniRoute/pull/11493)) +- **feat(providers):** derive + surface expiry for JWT-bearing web cookies (#11497) ([#11505](https://github.com/diegosouzapw/OmniRoute/pull/11505)) — thanks @oyi77 +- **feat(tunnels):** support named cloudflared tunnels via CLOUDFLARED_CONFIG ([#11561](https://github.com/diegosouzapw/OmniRoute/pull/11561)) — thanks @ggiak +- **feat(compression):** make proactive context-compression threshold a live setting ([#11564](https://github.com/diegosouzapw/OmniRoute/pull/11564)) — thanks @ragnar-claude +- **feat(resilience):** add provider peak-hour protection ([#11622](https://github.com/diegosouzapw/OmniRoute/pull/11622)) — thanks @Neuron-Mr-White +- **feat(providers):** add 1min.ai provider ([#11631](https://github.com/diegosouzapw/OmniRoute/pull/11631)) — thanks @zero-executioner +- **feat(dashboard):** collapsible JSON tree viewer for request/response payloads ([#11703](https://github.com/diegosouzapw/OmniRoute/pull/11703)) — thanks @hartmark +- **feat(routing):** add LiquidAI LFM2.5-2.6B free tier via OpenRouter ([#11752](https://github.com/diegosouzapw/OmniRoute/pull/11752)) — thanks @brick30llc-ctrl +- **feat(pricing):** add GLM-5.3-Flash pricing, model specs, and catalog registration ([#11830](https://github.com/diegosouzapw/OmniRoute/pull/11830)) — thanks @AndrianBalanescu +- **feat(routing):** add relayMode for schema-locked context handoffs ([#11839](https://github.com/diegosouzapw/OmniRoute/pull/11839)) — thanks @raheemuddin786 +- **feat(config):** add support for runtime system prompt configuration and hot-reloading ([#11841](https://github.com/diegosouzapw/OmniRoute/pull/11841)) — thanks @raheemuddin786 +- **feat(routing):** expand connection-aware quota prefilter across combo strategies (#11682) ([#11850](https://github.com/diegosouzapw/OmniRoute/pull/11850)) — thanks @HouMinXi +- **feat(models):** reactive discovery sync after Antigravity model-not-found 404 ([#11888](https://github.com/diegosouzapw/OmniRoute/pull/11888)) — thanks @MumuTW +- **feat(models):** auto-aliases and token limits from synced Antigravity discovery ([#11889](https://github.com/diegosouzapw/OmniRoute/pull/11889)) — thanks @MumuTW +- **feat(settings):** raise sticky round-robin limit caps to 1000 ([#12015](https://github.com/diegosouzapw/OmniRoute/pull/12015)) — thanks @adivekar-utexas +- **feat(resilience):** operator-configurable global credential health check interval ([#12043](https://github.com/diegosouzapw/OmniRoute/pull/12043)) — thanks @adivekar-utexas +- **feat(quota):** use official OpenCode Go usage API ([#12124](https://github.com/diegosouzapw/OmniRoute/pull/12124)) — thanks @ddarkr +- **feat(resilience):** default the credential health check sweep to 60 minutes ([#12138](https://github.com/diegosouzapw/OmniRoute/pull/12138)) — thanks @adivekar-utexas +- **feat(quality):** new-code mode for the complexity and dead-code ratchets (Clean as You Code) ([#12142](https://github.com/diegosouzapw/OmniRoute/pull/12142)) +- **feat(dashboard):** orchestration canvas — unified model + snapshot hook (part 1/2) ([#12156](https://github.com/diegosouzapw/OmniRoute/pull/12156)) +- **feat(usage):** add Kilo Code balance and Kilo Pass quotas ([#12178](https://github.com/diegosouzapw/OmniRoute/pull/12178)) — thanks @killer30001000 +- **feat(providers):** refresh vendored ChatGPT Web connector to v4.0.7 ([#12181](https://github.com/diegosouzapw/OmniRoute/pull/12181)) — thanks @backryun +- **feat(providers):** manual "Clear cooldown" action in the cooling panel ([#12224](https://github.com/diegosouzapw/OmniRoute/pull/12224)) — thanks @patrykkopycinski +- **feat(providers):** restore ChatGPT Web via clean-room browser transport ([#12239](https://github.com/diegosouzapw/OmniRoute/pull/12239)) — thanks @backryun +- **feat(sse):** wire the PROVIDER_PROFILES window gate into the global provider cooldown ([#12247](https://github.com/diegosouzapw/OmniRoute/pull/12247)) +- **feat(providers):** modernize CLOVA Studio chat and embeddings ([#12277](https://github.com/diegosouzapw/OmniRoute/pull/12277)) — thanks @backryun +- **feat(browser):** adopt Obscura as primary headless browser engine with Chromium fallback ([#12286](https://github.com/diegosouzapw/OmniRoute/pull/12286)) — thanks @opensource-elearning +- **feat(sse):** full language parity for output styles (es/de/fr/it/ru/zh + autoDetect) ([#12289](https://github.com/diegosouzapw/OmniRoute/pull/12289)) +- **feat(quality):** complete test:scoped — --full map rebuild, stdin selection, CI loader parity (#8084 D1) ([#12353](https://github.com/diegosouzapw/OmniRoute/pull/12353)) +- **feat(providers):** refresh Fable, Cursor, and Devin catalogs ([#12367](https://github.com/diegosouzapw/OmniRoute/pull/12367)) — thanks @backryun +- **feat(video):** redact transcript text from logs and durable memory (#12150 P1) ([#12427](https://github.com/diegosouzapw/OmniRoute/pull/12427)) +- **feat(dashboard):** parent-link, genuine-continuation badge, and modal perf fixes ([#12448](https://github.com/diegosouzapw/OmniRoute/pull/12448)) — thanks @hartmark +- **feat(dashboard):** adaptive context-budget dial on compression panel ([#12488](https://github.com/diegosouzapw/OmniRoute/pull/12488)) — thanks @HouMinXi +- **feat(providers):** refresh Z.ai Web models and browser transport ([#12524](https://github.com/diegosouzapw/OmniRoute/pull/12524)) — thanks @backryun +- **feat(video):** redact raw client-snapshot transcript fields in the detailed log (#12150 P2) ([#12528](https://github.com/diegosouzapw/OmniRoute/pull/12528)) +- **feat(providers):** refresh NVIDIA hosted models ([#12538](https://github.com/diegosouzapw/OmniRoute/pull/12538)) — thanks @backryun +- **feat(quota):** Moonshot Open Platform balance and TPD lock for custom nodes ([#12590](https://github.com/diegosouzapw/OmniRoute/pull/12590)) — thanks @HouMinXi +- **feat(video):** redact transcript in the in-memory pending-request snapshot (#12430 item 6) ([#12596](https://github.com/diegosouzapw/OmniRoute/pull/12596)) +- **feat(dashboard):** link a log entry's Conversation Context to its owning conversation ([#12646](https://github.com/diegosouzapw/OmniRoute/pull/12646)) — thanks @hartmark +- **feat(catalog):** eligibility-gated free-tier bucket ([#12669](https://github.com/diegosouzapw/OmniRoute/pull/12669)) +- **feat(db):** fail-closed previous_response_id continuation for redacted video turns (#12150 P2b) ([#12707](https://github.com/diegosouzapw/OmniRoute/pull/12707)) +- **feat(gamification): pay the documented `streak_bonus` and `badge_unlock` XP rewards.** `XP_REWARDS` listed both rewards but the award pipeline never paid them: the private reward table in `events.ts` omitted them, `updateStreak()` did not report when a streak extended, and badge unlocks carried no XP. Every request that extends a daily streak now pays `streak_bonus × streak length` once per UTC day (guarded by a same-day `xp_audit_log` check), and every badge unlocked through the pipeline pays `badge_unlock` once per badge (guarded by the `user_badges` primary key; `unlockBadge()` now reports whether it inserted). Bonus XP flows through the same `addXp` + level sync + global/weekly/monthly leaderboard path as action XP, so level-ups and rankings include it. The Radar supporter recognition unlock stays XP-free. (#12522 — thanks @pacocartones) +- **feat(providers):** advertise a `free-tier` capability in the provider plugin manifest for every provider with documented free models, so sidecars and dashboards can filter free-capable providers without reading the quota catalog ([#12786](https://github.com/diegosouzapw/OmniRoute/pull/12786)) — thanks @maxmad64bis +- **feat(providers):** Added EURouter as an OpenAI-compatible API-key gateway (`https://api.eurouter.ai/v1`), with live model discovery via `passthroughModels`. Its copy states that models are served by third-party upstreams listed per model, so an EU-based router is not read as EU data residency for inference. ([#13025](https://github.com/diegosouzapw/OmniRoute/pull/13025)) — thanks @ntdatt812 +- **feat(providers):** Added GreenPT as an OpenAI-compatible API-key provider (`https://api.greenpt.ai/v1`), with live model discovery via `passthroughModels`. No free-inference badge: the published docs describe a free API subscription billed per token, not a free tier. ([#13024](https://github.com/diegosouzapw/OmniRoute/pull/13024)) — thanks @ntdatt812 +- **feat(i18n):** 9 new locales — Greek (`el`), Croatian (`hr`), Serbian (`sr`), Lithuanian (`lt`), Estonian (`et`), Latvian (`lv`), Slovenian (`sl`), Maltese (`mt`), Irish (`ga`) — across the dashboard, docs mirrors, CLI, README and the site; OmniRoute now ships every official EU language (51 locales). (#13044) +- **feat(i18n):** 8 new locales — Kannada (`kn`), Malayalam (`ml`), Odia (`or`), Punjabi (`pa`), Nepali (`ne`), Sinhala (`si`), Burmese (`my`), Khmer (`km`) — across the dashboard, docs mirrors, CLI, README and the site (59 locales). The translator now restores the ICU literal escape around angle placeholders and splits oversized docs sections before translating. (#0000) ([#13660](https://github.com/diegosouzapw/OmniRoute/pull/13660)) +- feat(providers): **list Agnes 3.0 Flash as the current free chat model, drop retired 1.5 Flash, add Image 2.0/2.5 Flash plus Video 2.5/2.5 Flash, and discover the live `/v1/models` catalog (including the CN host `api.agnes-ai.cn`).** `agnes-1.5-flash` now forwards to `agnes-3.0-flash`. Video 2.5 polls `GET /v1/videos/{id}` (not the V2.0 `/agnesapi` contract). Live `/v1/models` (2026-09-09) no longer serves 1.5; the wiki marks it deprecated. 3.0 Flash is 512K context / 65,536 max output, same window as 2.5. CN-region keys use the existing per-connection base-URL field, default stays `apihub.agnes-ai.com`. ([#13120](https://github.com/diegosouzapw/OmniRoute/pull/13120)) — thanks @HouMinXi +- **feat(sse):** Codex and OpenAI catalogs list GPT-6 Astra with effort aliases (`-low` through `-ultra`); Codex CLI identity pins `@openai/codex@0.153.4` in lockstep with the image ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **feat(dashboard):** the orchestration History tab gained a "Compare runs" mode — toggling it turns each grid cell into a 2-item selection queue (a 3rd click drops the oldest pick), and picking two cells opens a side-by-side comparison panel instead of the usual detail drawer. The panel fetches both runs' detail the same way the drawer does (falling back to persisted history once a run leaves the live TTL window) and shows, per side: identity/source/state, start time, a signed `right - left` delta for duration/cost/event count, the event timeline aligned by index, and any memory hits. One side's fetch failing never blocks the other, and a delta is only ever computed when both sides have a finite value — otherwise it renders "—", never `NaN`. Comparing two runs from different sources or skills still works; a banner marks the deltas as informational rather than hiding them, since the two runs aren't a strict apples-to-apples pair. ([#12677](https://github.com/diegosouzapw/OmniRoute/pull/12677)) +- **feat(dashboard):** badge a conversation that never reached a clean stop ([#12717](https://github.com/diegosouzapw/OmniRoute/pull/12717)) — thanks @hartmark +- **feat(release):** reconcile-changelog tool + version-anchored fragment aggregation ([#12987](https://github.com/diegosouzapw/OmniRoute/pull/12987)) +- **feat(mcp):** carry context_length through MCP list_models_catalog projection (#12776) ([#13406](https://github.com/diegosouzapw/OmniRoute/pull/13406)) — thanks @KooshaPari +- **feat(cli):** make startup readiness budget configurable (#13369) ([#13433](https://github.com/diegosouzapw/OmniRoute/pull/13433)) — thanks @KooshaPari + ### 🐛 Bug Fixes +- **security(streaming):** sanitize generic mid-stream error messages before emitting OpenAI, + Responses, or Claude SSE failure frames and before diagnostic logging, while preserving raw + failures for internal classification and keeping client disconnects out of provider failure state. ([#12457](https://github.com/diegosouzapw/OmniRoute/pull/12457)) +- **fix(claude):** `blockExtraUsage: false` no longer lets 5h quota preflight skip the account; extra usage is billed after the session bar is gone, so the request must reach Anthropic ([#12803](https://github.com/diegosouzapw/OmniRoute/pull/12803)) — thanks @HouMinXi +- Sanitize HuggingChat conversation-creation and message-send transport failures before they reach client error bodies or provider logs. ([#12467](https://github.com/diegosouzapw/OmniRoute/pull/12467)) +- **fix(dashboard):** Keep local and theme-aware provider SVG icons at a definite layout size so Chromium does not collapse them to 0×0 after the v3.8.50 image-rendering change ([#12054](https://github.com/diegosouzapw/OmniRoute/pull/12054)) — thanks @ponkcore +- **fix(security):** Sanitize provider and runtime failures before public API, SSE and MCP responses and before persistent request, proxy and usage logs, preventing credentials, stack traces and host filesystem paths from crossing those boundaries while preserving stable error codes and useful diagnostics. ([#12506](https://github.com/diegosouzapw/OmniRoute/pull/12506)) +- **fix(cli):** `omniroute tunnel create` no longer crashes with `Cannot read properties of undefined (reading optsWithGlobals)` — removed the duplicate positional argument that caused Commander.js to misalign the action callback parameters ([#12295](https://github.com/diegosouzapw/OmniRoute/issues/12295)) ([#12368](https://github.com/diegosouzapw/OmniRoute/pull/12368)) — thanks @gonisulaimann +- Stop advertising Gemini Live-only models as supported audio endpoints until OmniRoute proxies the bidirectional Live protocol. ([#11710](https://github.com/diegosouzapw/OmniRoute/pull/11710)) — thanks @RaviTharuma +- **fix(kie):** reroute `flux/kontext` off the KIE Market `createTask` flow — it is catalogued with `isMarket: true` but has no Market catalog page, so KIE rejected it with "model name not supported"; it now hits the dedicated `POST /api/v1/flux/kontext/generate` / `GET /api/v1/flux/kontext/record-info` endpoints instead (#11296). ([#11985](https://github.com/diegosouzapw/OmniRoute/pull/11985)) — thanks @hartmark +- **fix(kie):** correct 12 more KIE Market catalog ids that were sent to `createTask` unchanged but diverge from KIE's documented upstream `model` values — GPT Image 2 T2I/I2I (drops the `gpt/` prefix), GPT Image 1.5 T2I/I2I (`gpt-image/` namespace), Seedream 5.0 Lite T2I/I2I (drops the `.0`), all 4 Flux 2 variants (`flux-2/` namespace, generic variant renamed `flex`), and Wan 2.7 Image / Image Pro (dash instead of dot) — each verified individually against the literal example request published on docs.kie.ai. `#11326`'s "everything else already matches" claim was wrong a second time (#11296); `z-image/4.0-*`/`z-image/4.5-*` and `flux/kontext` remain open, documented as unresolved in `KIE_MARKET_UPSTREAM_MODEL_IDS`'s comment pending further verification. ([#11667](https://github.com/diegosouzapw/OmniRoute/pull/11667)) +- **fix(dashboard):** `useApiKeySave.handleSaveApiKey` no longer forces a full upstream `/models` catalog sync on every non-curated provider connection save — callers can now pass `skipModelSync: true` to opt out, so a workflow that only wants to add one manual model no longer floods the provider's available-models list with hundreds/thousands of synced entries. The flag is a client-side intent signal only and is stripped before the connection payload is POSTed to `/api/providers`; default behavior (full sync on save) is unchanged when the flag is omitted (#11324) ([#11664](https://github.com/diegosouzapw/OmniRoute/pull/11664)) +- **Provider connections:** keep `tokenExpiresAt` when a connection is created. The create-path allowlist omitted it, so every insert stored NULL and the dashboard token badge could read a fresh connection as expired until its first background refresh ([#11368](https://github.com/diegosouzapw/OmniRoute/pull/11368)). — thanks @ntdatt812 +- **fix(combos):** the combo builder's precision-select, global-model-search, and manual-entry flows now serialize a model step's `model` string using the provider's already-computed routing-alias prefix (e.g. `oc/`) instead of rebuilding it from the raw canonical `providerId`, fixing the no-auth "OpenCode Free" provider (`opencode`) being routed to the unrelated paid "OpenCode Zen" provider (`opencode-zen`) because `opencode` doubles as a manual routing-prefix override ([#11433](https://github.com/diegosouzapw/OmniRoute/issues/11433)). ([#11670](https://github.com/diegosouzapw/OmniRoute/pull/11670)) +- **fix(radar):** the catalog feed cache now keeps `generatedAt`, the date the feed's data was built, next to `fetchedAt`, the date this install downloaded it (#11435). The feed schema requires that date and the sync path validates it, but the cache dropped it — so a feed fetched minutes ago and one carrying weeks-old figures looked identical to everything downstream, including the dashboard's "Last fetched" line. `getRadarCatalog().meta` and `GET /api/radar/status` now report both dates, the latter as its own field rather than folded into `version` — and omitted entirely for the offers and intel caches, which keep no build date, where a `null` would read as "unknown" rather than "never stored". The dashboard still shows only the fetch time; surfacing the build date there needs a new translated label and is left to a follow-up. Rows cached before migration 163 read back as `null`: unknown stays unknown instead of borrowing the fetch time. The referrals cache has persisted the same date since migration 142. — thanks @maxmad64bis +- **fix(cli):** postinstall no longer fills `JWT_SECRET` and `API_KEY_SECRET` in the installed package's `.env` (#11436). `.env.example` ships both blank on purpose: the server restores them from its durable store, or generates and persists them there, in `ensureSecrets()`. Pre-filling them defeated that — the file lives inside the package directory, so `npm i -g` replaced it and postinstall wrote _different_ values, while `ensureSecrets()` (which only acts on an empty variable) never got to restore the real ones. Both secrets rotated silently on every update, invalidating dashboard sessions and API-key CRCs. `STORAGE_ENCRYPTION_KEY` left the same list for the same reason in #1622; its comment pointed at a function that no longer exists and now names the real provisioning path. — thanks @maxmad64bis +- **fix(cli):** the CLI now says when a loaded `.env` lives inside the installed package directory (#11437). It already announces every env file it reads, without distinguishing the ones that survive an update from the one that does not: `npm i -g` replaces the package directory wholesale, so values set there are gone at the next update, silently. The warning names the durable path to move them to, and fires only when that file actually supplied a value — a file entirely shadowed by a durable one supplied nothing. A development checkout stays silent: there the same path is stable and documented in `SETUP_GUIDE.md`. — thanks @maxmad64bis +- **fix(free-models):** the shared `isFreeModel()` predicate no longer reports catalog entries marked `freeType: "discontinued"` as free, so `hidePaidModels` can't route to Pollinations' seven premium models that now require a paid key ([#11441](https://github.com/diegosouzapw/OmniRoute/pull/11441)) — thanks @maxmad64bis +- fix(ui): let AnySearch use the normal provider-icon fallback when LobeHub has no matching icon (#11449) +- Fixed the v3.8.50 Costs and Analytics dashboards so flat-rate Claude Code usage can be shown as an explicitly requested token-price estimate without changing default billed-cost semantics. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed archived usage retention so each request is priced individually instead of pricing a day's summed tokens once, which understated archived cost whenever a day mixed cache-heavy and ordinary requests. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed the Costs dashboard so it discloses when displayed figures include flat-rate token-price estimates instead of labelling them as billed spend, using the flag the analytics API already returns; the month-end projection and the CSV/JSON exports carry the same marker, and billed-cost mode is unchanged. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- **fix(combo):** attach the same combo-diagnostics trace (`poolSize`/`attemptOrder`/`excluded`/`terminalReason`, plus `x-omniroute-combo-*` headers) to the round-robin strategy's and the nested pipeline/fusion runtime-unit loop's "Maximum combo retry limit reached" 503 that the priority-strategy path already attaches for the identical terminal condition — previously those two paths returned a bare, contextless 503 ([#11462](https://github.com/diegosouzapw/OmniRoute/issues/11462)). ([#11665](https://github.com/diegosouzapw/OmniRoute/pull/11665)) +- **fix(bun):** use native `bun:sqlite` in `bootstrap-env` and `sync-env` to avoid loading `better-sqlite3` N-API addon during Bun startup ([#11468](https://github.com/diegosouzapw/OmniRoute/pull/11468)) — thanks @TheDemonTuan +- **fix(docker-bun):** make `Dockerfile.bun` install reliable with pre-install script helpers and native `bun:sqlite` smoke check ([#11470](https://github.com/diegosouzapw/OmniRoute/pull/11470)) — thanks @TheDemonTuan +- **fix(docker-bun):** remove every vendored `better-sqlite3` native addon from the Bun runtime image so startup cannot abort before the `bun:sqlite` fallback ([#11482](https://github.com/diegosouzapw/OmniRoute/pull/11482)) — thanks @TheDemonTuan +- **fix(autoCombo):** effort/alias model variants (`gpt-5.6-sol-xhigh`, `gpt-5.6`, cursor's `claude-4.6-opus-high`) inherit their base model's task fitness instead of falling to the wildcard 0.5 ([#11492](https://github.com/diegosouzapw/OmniRoute/pull/11492)) — thanks @MumuTW +- fix(db): rate-limit repeated Arena ELO leaderboard fetch-failure warnings instead of logging one per sync attempt (#11500) ([#11989](https://github.com/diegosouzapw/OmniRoute/pull/11989)) — thanks @hartmark +- **fix(db):** dedupe the raw `[Encryption] Decryption failed...` log line emitted by the lazy-decrypt views (`createLazyRowProxy`/`createLazyConnectionView`), which power `getProviderConnections()` and were re-triggering that line on every CredentialHealth/model-sync cycle for the same corrupt or stale-key credential — a fresh Proxy over a fresh row on every cycle meant the per-proxy memoization never suppressed it, unlike the dedup `decryptConnectionFields()` already had since [#9927](https://github.com/diegosouzapw/OmniRoute/issues/9927). Now shares that dedupe tracking so the line logs at most once per credential ([#11500](https://github.com/diegosouzapw/OmniRoute/issues/11500)). ([#11678](https://github.com/diegosouzapw/OmniRoute/pull/11678)) +- **fix(intelligence):** Arena ELO sync stores a synthesized base-model row for every effort/harness variant (`claude-opus-5-max` → `claude-opus-5`, `gpt-5.6-sol-xhigh (codex-harness)` → `gpt-5.6-sol`), so bare model requests reach the synced intelligence layer instead of the static fallback table; the hand-maintained `MODEL_ALIAS_MAP` — which collapsed generations (`gpt-5.5` → `gpt-5`) — is gone ([#11506](https://github.com/diegosouzapw/OmniRoute/pull/11506)) — thanks @MumuTW +- **fix(autoCombo,sse):** catalog hygiene — the static task-fitness table no longer ranks retired models above live ones (dead rows dropped, patterns matched on segment boundaries), the 7 deprecation aliases that forwarded to retired or non-existent models now point at the vendors' published replacements, an alias is no longer rewritten when the serving provider offers the id as-is, and a new `check:model-lifecycle` gate diffs both tables against a vendor lifecycle snapshot ([#11507](https://github.com/diegosouzapw/OmniRoute/pull/11507)) — thanks @MumuTW +- **fix(sse):** stop dropping resolved `thoughtSignature` values on parallel (multi tool-call) turns sent to Gemini 3.x — the claude→gemini and openai→gemini translators previously kept the signature only on the *first* function call of a message, causing Gemini to reject subsequent calls in the same turn with HTTP 400 "Function call is missing a thought_signature"; each function call now keeps its own resolved signature ([#11510](https://github.com/diegosouzapw/OmniRoute/issues/11510)). ([#11669](https://github.com/diegosouzapw/OmniRoute/pull/11669)) +- Treat Docker publishing from a non-default release branch as an expected skip instead of a failed workflow, while preserving the guard that prevents it from overwriting the `next` channel. ([#11527](https://github.com/diegosouzapw/OmniRoute/pull/11527)) — thanks @pacocartones +- fix(search): prefer credentialed providers over duckduckgo-free fallback (#11524) — the fallback-only loop ran before the credentialed-providers loop in `executeWebSearch`, making configured providers unreachable when `duckduckgo-free` was available. ([#11565](https://github.com/diegosouzapw/OmniRoute/pull/11565)) — thanks @vsd2807 +- **fix(sse):** cap the upstream headers-wait phase for STREAMING requests to a client-realistic ceiling (110s, under Codex's own ~120s hard client-abort window) instead of the flat 10-minute `FETCH_TIMEOUT_MS` default — that default was 5x longer than the body-phase readiness watchdog's own adaptive bound, so a request whose upstream never returned any response at all (not even headers, e.g. a stalled NVIDIA target behind a tool-heavy Responses→Chat translation) kept the client connection alive on keepalives only, guaranteeing the client's own patience ran out first with an opaque 499 instead of OmniRoute detecting and failing the stall fast. Non-streaming requests are unaffected — they keep the existing flat default (`open-sse/utils/fetchStartTimeoutPolicy.ts`) (#11526) ([#11668](https://github.com/diegosouzapw/OmniRoute/pull/11668)) +- **fix(ci):** protocol-clients E2E harness boots the peer-stamped custom server (`run-next.mjs dev`) instead of the bare `next` CLI, so `LOCAL_ONLY` locality resolves from the real TCP peer stamp and `/api/mcp/audit` answers 200 (open bootstrap preserved via an `OMNIROUTE_E2E_BOOTSTRAP_MODE=open` credential clear in `run-next.mjs`, pinned to loopback `HOST`) instead of a deterministic 403 (#11535) ([#11549](https://github.com/diegosouzapw/OmniRoute/pull/11549)) — thanks @jonlwheat2-gif +- **fix(docs):** the Auto-Combo engine is described with the number of factors it actually declares. Four different counts were in circulation — 6, 9, 13 and 14 across `AGENTS.md`, four guides and six code comments — while `DEFAULT_WEIGHTS` declares 15. `check:docs-counts` now reads that number from the source, so adding a factor without updating the prose fails CI ([#11545](https://github.com/diegosouzapw/OmniRoute/pull/11545)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **fix(quota-share):** in-flight leases are per request, so an aborted request's slot ages out instead of being kept alive by later traffic on the same connection ([#11547](https://github.com/diegosouzapw/OmniRoute/pull/11547)) — thanks @abhisheksharma2411 +- The free-tier summary route now serves the refreshed Radar catalog when the feed is active — the same numbers the dashboard shows — and states which catalog answered plus its real build date, instead of always reporting release-frozen figures with a stale curation date. The supporter-key live feed stays reserved to authenticated callers of the instance. ([#11550](https://github.com/diegosouzapw/OmniRoute/pull/11550)) — thanks @maxmad64bis +- **sse:** fix LiveWS/embed-WS servers crashing at startup under the Node/tsx runtime — `liveServer.ts`, `embedWsProxy.ts` and `apiBridgeServer.ts` imported `@/shared/utils/httpClientAbortGuard` without the `.mjs` extension, so the client-abort crash guard added by [#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556) was unreachable and every dependent test failed with `ERR_MODULE_NOT_FOUND` ([#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556)). ([#11671](https://github.com/diegosouzapw/OmniRoute/pull/11671)) — thanks @jonlwheat2-gif / @yxyxy +- fix(chat): shed any of the matched provider-node's routing identifiers (public prefix or internal id) before model lookup, so composites like `//` no longer reach upstream verbatim (#11557) — thanks @maxmad64bis +- **fix(cache):** a semantic-cache entry now expires on its TTL instead of surviving until the next UTC midnight, and `dbEntries` stops counting expired rows ([#11573](https://github.com/diegosouzapw/OmniRoute/pull/11573)) — thanks @ntdatt812 +- **fix(api):** `/v1/models` schedules its stale-while-revalidate rebuild through Next's `after()` again, so a stale catalog reaches the client before the rebuild blocks the event loop ([#11574](https://github.com/diegosouzapw/OmniRoute/pull/11574)) — thanks @ntdatt812 +- **fix(check):** the `known-symbols` executor conformance gate awaits the now-async `getExecutor()`, so it stops reporting all 142 registered aliases as dead symbols and can detect a lazy import that fails ([#11582](https://github.com/diegosouzapw/OmniRoute/pull/11582)) — thanks @ntdatt812 +- **fix(pack):** register `bin/cli/utils/volatileEnvPath.mjs` in `PACK_ARTIFACT_REQUIRED_PATHS`, so the prune can no longer silently drop a module the published CLI imports on every boot ([#11588](https://github.com/diegosouzapw/OmniRoute/pull/11588)) — thanks @ntdatt812 +- **i18n(vi):** translate the five reliability-column strings on the free-provider rankings page, which rendered as `__MISSING__:` markers in the Vietnamese UI ([#11589](https://github.com/diegosouzapw/OmniRoute/pull/11589)) — thanks @ntdatt812 +- **resilience:** restore the expired-connection retry-budget probe in the token-health sweep — the `!isGitHubAccessTokenOnlyConnection` carve-out reintroduced by #11608 contradicted the boundary pinned by #11592, so a GitHub connection parked at `expired` with retry budget remaining was never probed and could never self-heal ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)). ([#11672](https://github.com/diegosouzapw/OmniRoute/pull/11672)) — thanks @jonlwheat2-gif / @ntdatt812 +- **fix(dashboard):** Provider Stats now displays combo success rates on their native 0–100 percentage scale instead of multiplying them twice ([#11595](https://github.com/diegosouzapw/OmniRoute/pull/11595)) — thanks @pacocartones +- **fix(providers):** route DuckDuckGo Web Chat through the verified `duck.ai` endpoint, refresh its free model catalog, and avoid forwarding unsolved anti-abuse challenges ([#11597](https://github.com/diegosouzapw/OmniRoute/pull/11597)) — thanks @solstxce +- **fix(ui):** Console log Refresh and Copy controls now expose localized accessible names, keep copy actions visible on keyboard focus, and announce copy completion safely ([#11599](https://github.com/diegosouzapw/OmniRoute/pull/11599)) — thanks @pacocartones +- Dropped the stale-`.eslintcache` `restore-keys` fallback from both "Restore ESLint file cache" steps in `ci.yml`, so the blocking `Lint` job can no longer be served per-file verdicts computed under a different lint config, suppressions file or lockfile. `quality.yml` had already dropped it in #11963; `ci.yml` — the workflow that actually gates PRs — had not (#11600). ([#11996](https://github.com/diegosouzapw/OmniRoute/pull/11996)) +- fix(ci): pass `--pass-on-unpruned-suppressions` in `run-eslint-json.mjs` so the CI Lint job no longer fails when a suppression is merely orphaned by a genuine fix, mirroring the identical fix already in `validate-release-green.mjs` (#11600) ([#11983](https://github.com/diegosouzapw/OmniRoute/pull/11983)) — thanks @hartmark +- fix(handoff): enforce provider allowlist for universal handoff (#11602) — universal handoff now skips summarization when the selected summary model's provider is not included in the configured provider allowlist. ([#11619](https://github.com/diegosouzapw/OmniRoute/pull/11619)) — thanks @vsd2807 +- **fix(dashboard):** Show a stable error state when Search Analytics returns an HTTP error or malformed data ([#11603](https://github.com/diegosouzapw/OmniRoute/pull/11603)) — thanks @pacocartones +- **test(gamification):** pin the aggregate profile level to the XP-derived semantics of #11604 — `getAggregateXp()` now derives `currentLevel` from the summed XP (`calculateLevel(sum)`), not `MAX(stored current_level)`, and the #3484 fixture levels are aligned with the XP curve ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)). ([#11676](https://github.com/diegosouzapw/OmniRoute/pull/11676)) — thanks @jonlwheat2-gif / @pacocartones +- **fix(dashboard):** Keep the Profile level and progress aligned with aggregate XP, including bounded handling for invalid totals ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)) — thanks @pacocartones +- **fix(dashboard):** Prevent locked hidden badges from revealing their icon or opening private badge details before they are earned ([#11605](https://github.com/diegosouzapw/OmniRoute/pull/11605)) — thanks @pacocartones +- **fix(dashboard):** Restore keyboard focus after shared modals close and cancel delayed autofocus during cleanup ([#11607](https://github.com/diegosouzapw/OmniRoute/pull/11607)) — thanks @pacocartones +- **fix(dashboard):** Enable Enter and Space activation for clickable data-table rows without hijacking nested controls ([#11610](https://github.com/diegosouzapw/OmniRoute/pull/11610)) — thanks @pacocartones +- **fix(autoCombo,sse):** vendor-retired catalog ids are dropped from the auto-combo candidate pool and no longer win on leftover `arena_elo` / `user_override` rows; `getModelLifecycleDecision` consults `model-lifecycle.json` (prefix-stripped) so aggregator traffic is not `untracked` for ids the snapshot already knows ([#11625](https://github.com/diegosouzapw/OmniRoute/issues/11625)) ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)) — thanks @MumuTW +- **test(chatcore):** move Codex/Claude combo fixtures off models the lifecycle guard now rejects — `gpt-5.1-codex`/`gpt-5-codex` are vendor-retired (snapshot, #11626) and `claude-3-5-sonnet-20241022` is shut down, so native-passthrough and combo-fallback tests switched to `gpt-5.6-sol` and `claude-sonnet-4.6` ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)). ([#11675](https://github.com/diegosouzapw/OmniRoute/pull/11675)) — thanks @jonlwheat2-gif / @MumuTW +- fix(opencode-plugin): stop a free-tier budget that rounds up across a magnitude from rendering as `1000K`/`1000M` in the model picker — `fmtTokens` chose its unit from the raw token count and then rounded with `toFixed(1)`, so 999,950–999,999 printed as `1000K` rather than `1M` and just under a billion printed as `1000M` rather than `1B` ([#11684](https://github.com/diegosouzapw/OmniRoute/pull/11684)) — thanks @abhisheksharma2411 +- Cap prose-derived Antigravity quota resets at the configured model cooldown maximum while preserving authoritative Retry-After headers and Google RetryInfo hints. (#11695) Thanks @pacocartones. ([#11823](https://github.com/diegosouzapw/OmniRoute/pull/11823)) +- Compare `_rebaseline_*` notes structurally in the ratchet-bank verifier so an object-valued note no longer reports itself as rewritten on an unmodified tree — the nightly `bank-ratchet-shrinks` lane had been aborting on every run since 2026-08-11, leaving 41,672 lines of earned file-size shrinks unbanked (#11702). ([#11892](https://github.com/diegosouzapw/OmniRoute/pull/11892)) — thanks @MumuTW +- **fix(build):** `prepublish.ts` bundles the ChatGPT Web (Codex) MCP bridge through `runBuildTool()` instead of spawning `npx.cmd` raw, fixing the build crash on Node ≥ 20/Windows where `.cmd` shims cannot be spawned without a shell (EINVAL) ([#11704](https://github.com/diegosouzapw/OmniRoute/issues/11704)) ([#11706](https://github.com/diegosouzapw/OmniRoute/pull/11706)) — thanks @jonlwheat2-gif +- fix(codex): keep `parallel_tool_calls:false` on the translated Codex Responses Lite path (#11707) ([#11984](https://github.com/diegosouzapw/OmniRoute/pull/11984)) — thanks @hartmark +- **fix(tests):** the new Gemini Web Images retirement test (#11708) called the now-async `getExecutor()` synchronously, throwing `TypeError: getExecutor(...).getProvider is not a function` instead of asserting Gemini Web chat stays available. Awaited to match the R0.3 executor-registry refactor (#11220). ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(search):** Allow REST and MCP web-fetch requests to select the advertised `anysearch-search` provider explicitly ([#11716](https://github.com/diegosouzapw/OmniRoute/pull/11716)) — thanks @pacocartones +- **fix(providers):** Keep overnight peak-hour protection active after midnight according to the window's configured start day ([#11718](https://github.com/diegosouzapw/OmniRoute/pull/11718)) — thanks @pacocartones +- **fix(providers):** correct all five g4f.space gateway descriptions to document proof-of-work credits or member API keys, variable limits, and the remote prompt boundary instead of promising free keyless access at five requests per minute ([#11738](https://github.com/diegosouzapw/OmniRoute/pull/11738)). +- **fix(db):** `model_capabilities` is created by a migration instead of lazily on the first models.dev sync, so a clean install and an upgraded install converge on the same schema regardless of which features have run ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(ci):** `check:install-upgrade` now fails on an `npm` install truncated by ENOSPC (npm reports it as a warning and still exits 0), authenticates its health probe so the version assertion works against the hardened health payload, frees the clean-install tree before the upgrade phase, and no longer reports a schema divergence computed from a boot that never served ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(routing):** harden session-affinity key derivation against oversized explicit IDs and empty structural-payload collisions, while recognizing Gemini and common text request shapes ([#11746](https://github.com/diegosouzapw/OmniRoute/pull/11746)) — thanks @MumuTW +- **fix(packages/browser-pool):** regenerate `package-lock.json` so the `packages/browser-pool` workspace's locked `playwright`/`@types/node` (and transitives) match its `package.json` specs, fixing cache-only/offline installs (`npm ci --offline`, Nix `buildNpmPackage`) that previously failed with `ENOTCACHED` ([#11747](https://github.com/diegosouzapw/OmniRoute/issues/11747)) — thanks @benjaminkitt ([#11784](https://github.com/diegosouzapw/OmniRoute/pull/11784)) — thanks @geekyNads +- **fix(assets):** Stop distributing 79 provider and CLI logos without verified source/license provenance, keep every provider/tool available through the internal generic icon, and prevent those IDs from falling through to an external icon CDN. ([#11750](https://github.com/diegosouzapw/OmniRoute/pull/11750)) +- **fix(providers):** retire the common `chatgpt-web` / `cgpt-web` provider because its pre-key and proof-of-work provenance could not be cleared ([#11754](https://github.com/diegosouzapw/OmniRoute/pull/11754)) — explicit chat and image requests, slash-prefixed models, persisted aliases, proxy overrides, and combo targets now fail closed with a sanitized HTTP `410` / `PROVIDER_RETIRED` before upstream dispatch. Migration 163 tombstones matching connections and invalidates their active leases without deleting history or widening API-key allowlists. The independent `chatgpt-web-codex` / `cgpt-codex` provider and its MIT-noticed vendored implementation remain available. +- **fix(usage):** quota and usage refresh no longer 409 when an exclusive lease reserves the connection ([#11758](https://github.com/diegosouzapw/OmniRoute/pull/11758)) — thanks @TheDemonTuan +- Keep the embedding registry's vector width and `embedding` type on models when a synced model exists for the same id, so `/v1/models` no longer reports registry-described embedding models widthless or untyped (#11761) — thanks @rahilmavani +- Correct `google/gemini-embedding-001` on the OpenRouter route to 3072 dimensions, the width it returns when `dimensions` is not sent (#11761) — thanks @rahilmavani +- **fix(vision-bridge):** Restrict automatic vision routing and fallbacks to models available in authoritative live provider catalogs, including aliases and effort variants ([#11767](https://github.com/diegosouzapw/OmniRoute/issues/11767)) — thanks @pacocartones ([#11813](https://github.com/diegosouzapw/OmniRoute/pull/11813)) +- **fix(providers):** reclassify Cerebras as a one-time $5 signup credit (payment method required, 30-day validity), not a recurring no-card 1M tokens/day trial ([#11773](https://github.com/diegosouzapw/OmniRoute/issues/11773)) ([#12591](https://github.com/diegosouzapw/OmniRoute/pull/12591)) — thanks @HouMinXi +- fix(cli): stop prepublish from re-rebuilding the already-built ESM-only opencode-plugin dist (#11787) ([#11990](https://github.com/diegosouzapw/OmniRoute/pull/11990)) — thanks @hartmark +- **fix(db):** Fresh installs no longer log a non-fatal `no such table: compression_run_telemetry` warning when retention cleanup runs before the lazily-created telemetry table exists ([#11802](https://github.com/diegosouzapw/OmniRoute/pull/11802)) — thanks @RaviTharuma +- fix(sse): set X-OmniRoute-Selected-Connection-Id on successful combo dispatches so downstream consumers stop falling back to an empty connection id (#11810) ([#11986](https://github.com/diegosouzapw/OmniRoute/pull/11986)) — thanks @hartmark +- **fix(services):** embedded CLIProxyAPI lifecycle checks now use public `/healthz`, while model discovery uses the configured dedicated data-plane API key instead of the management password ([#11811](https://github.com/diegosouzapw/OmniRoute/pull/11811)) — thanks @RaviTharuma +- Fix persisted model steps returning an error in the playground route simulator, and warn when structural steps cannot be simulated. ([#11862](https://github.com/diegosouzapw/OmniRoute/pull/11862)) — thanks @pacocartones +- **fix(providers):** Antigravity's dynamic mitmAlias table no longer routes `gemini-3.7-flash-{high,medium,low}` to a literal tier-suffixed upstream id just because one connected account's own discovery listed it directly — those display ids always resolve through the safe `gemini-3.7-flash-tiered` static alias, so one account's Google-provisioned access no longer 404s every sibling account of the provider ([#11824](https://github.com/diegosouzapw/OmniRoute/issues/11824), [#11651](https://github.com/diegosouzapw/OmniRoute/issues/11651)) ([#11988](https://github.com/diegosouzapw/OmniRoute/pull/11988)) — thanks @hartmark +- **fix(dashboard):** Expose Profile loading, errors, page structure, and XP progress to assistive technologies ([#11838](https://github.com/diegosouzapw/OmniRoute/pull/11838)) — thanks @pacocartones +- **fix(socks):** `Agent.connectTimeout` now bounds both SOCKS handshake and TLS connect, and `family === null` no longer falls back to `fetch-socks` ([#11842](https://github.com/diegosouzapw/OmniRoute/pull/11842)) — thanks @maxmad64bis +- **perf(compression):** OOM mitigations for large payload hashing, memoization, and token estimation ([#11844](https://github.com/diegosouzapw/OmniRoute/pull/11844) — thanks @AndrianBalanescu) +- Excluded Next.js Node File Trace manifests (`*.nft.json`) from the published npm tarball. They are build-time metadata and are never read while serving, but had grown to 668.7 MB — 61% of the package — which pushed the upload past the registry limit and made `npm publish` fail with `413 Payload Too Large`. ([#11864](https://github.com/diegosouzapw/OmniRoute/pull/11864)) +- **fix(cli):** keep the global OmniRoute server `--base-url` separate from provider-node `--endpoint` values in add, update, and validate commands ([#11860](https://github.com/diegosouzapw/OmniRoute/pull/11860)) — thanks @pacocartones +- **fix(config):** Nous Research's `Hermes-4-405B` model now displays as "Hermes 4 405B (Nous Research)" in both the provider registry and the free-model catalog, instead of the mislabelled "Hermes 4 7B" ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#11993](https://github.com/diegosouzapw/OmniRoute/pull/11993)) +- **fix(provider/nous):** inject required user= tag into Nous Research inference requests to resolve upstream 400 "missing tags" error ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#12044](https://github.com/diegosouzapw/OmniRoute/pull/12044)) +- **fix(build):** `npm run build` now fails in one second with a named package and a copy-pasteable fix when npm silently drops an externalised optional native dependency, instead of dying four minutes in with `Module not found: Can't resolve 'better-sqlite3'` ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- **fix(install):** `postinstall` no longer throws `ReferenceError: isAndroid is not defined` — failing the whole `npm install` — when the `better-sqlite3` rebuild fallback times out; the manual-fix guidance is reachable again ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- Fixed the Alibaba free-tier allowlist test that went red on its own once the shipped catalog's `validUntil` (2026-08-27) passed, leaving every PR and `main` with a failing `Unit Tests (1/8)`. The test now builds its own packs with dates it controls, and covers the expired-pack fallback that production has actually been serving. ([#11867](https://github.com/diegosouzapw/OmniRoute/pull/11867)) +- Split the npm registry upload into its own GitHub-hosted job. npm refuses `--provenance` from a self-hosted runner (`422 ... Only "github-hosted" runners are supported`), which blocked the v3.8.50 publish; the heavy verification cannot move to a hosted runner, so it now hands the proven tarball over instead. ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(ui):** the shared `DataTable` loading state no longer reads its decorative ⏳ glyph out to assistive technology, and now carries the same `role="status"` / `aria-live="polite"` / `aria-busy="true"` semantics as `PageLoading` ([#11869](https://github.com/diegosouzapw/OmniRoute/pull/11869)) — thanks @pacocartones +- **fix(api):** Bound the `timeout` accepted by `POST /api/providers/[id]/login` to the same 15s–600s window the Adobe Firefly and Conol browser-login paths already enforce, so a request can no longer pin a headful browser and the single active-login slot with an arbitrarily large poll budget. ([#11871](https://github.com/diegosouzapw/OmniRoute/pull/11871)) — thanks @pacocartones +- **fix(api):** Stop the tunnel and MITM routes from returning raw child-process error text, which disclosed host paths, binary install locations and Tailscale `tskey-*` credentials in a body some of these routes serve to non-loopback callers, and return a real 400 instead of a framework 500 when a tunnel request body fails validation. ([#11872](https://github.com/diegosouzapw/OmniRoute/pull/11872)) — thanks @pacocartones +- **fix(open-sse):** stream TTFT/ITL are sampled from a monotonic clock (`performance.now`) instead of `Date.now`, so an NTP correction or wall-clock jump can no longer inflate a genuine TTFT or produce a negative one that the `ttft >= 0` guard silently drops — protecting the router's OTel/EWMA/`usage_history`/speed-ranking signals ([#11873](https://github.com/diegosouzapw/OmniRoute/pull/11873)) — thanks @pacocartones +- **fix(providers):** xAI `reasoning_effort: "xhigh"` now reaches grok-4.6+ instead of being silently clamped to `"high"` ([#11879](https://github.com/diegosouzapw/OmniRoute/pull/11879)) — thanks @NoxzRCW +- **fix(dashboard):** the providers page no longer crashes into the error boundary when a provider id collides with an `Object.prototype` member (`constructor`, `__proto__`); icon lookups are own-property guarded ([#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)) — thanks @NoxzRCW +- **fix(skills):** injected skill tools declared in shorthand (`{"content": "string"}`) now forward valid JSON Schema, unblocking providers that validate tool schemas strictly such as Zhipu GLM on the Console Go tier ([#11881](https://github.com/diegosouzapw/OmniRoute/pull/11881)) — thanks @NoxzRCW +- **fix(api):** `POST /api/playground/simulate-route` now surfaces `combo-ref` and `provider-wildcard` persisted combo steps with a specific warning (naming the referenced combo, or the unresolved `provider/model` wildcard) instead of folding them into a generic "unsupported step" count; a `provider-wildcard` step is also now included as an unresolved target so the operator can see it is in the route (ported from [#11882](https://github.com/diegosouzapw/OmniRoute/pull/11882) — thanks @NoxzRCW). ([#11930](https://github.com/diegosouzapw/OmniRoute/pull/11930)) +- **fix(translator):** the streaming OpenAI→Claude translator keeps upstream usage, including prompt-cache tokens, when it arrives on a trailing `choices: []` chunk (Fireworks and any upstream using `stream_options.include_usage`) ([#11883](https://github.com/diegosouzapw/OmniRoute/pull/11883)) — thanks @NoxzRCW +- **fix(cli):** `omniroute update --apply` now tells you explicitly whether a running server was detected and, if so, that you must run `omniroute restart` to apply the update — it never restarted anything and previously implied the update was already live once files were installed. The dashboard's npm-mode Update flow (`/api/system/version`) now tries OmniRoute's own PID-file-managed supervisor before falling back to pm2, and reports an honest "restart required" step instead of a silent pm2-only "skipped" that read like a completed update. The server-side latest-version lookup backing the dashboard's update banner also gained `--prefer-online`, closing the same stale-npm-cache class already fixed in the CLI's own copy for #4376 ([#11885](https://github.com/diegosouzapw/OmniRoute/issues/11885)). ([#12005](https://github.com/diegosouzapw/OmniRoute/pull/12005)) +- **fix(resilience):** clear persisted LKGP pins when a target suffers connection/provider exhaustion or is skipped before dispatch due to cooldown/exhaustion/unavailability, preventing subsequent requests from repeatedly prioritizing known-dead providers ([#11911](https://github.com/diegosouzapw/OmniRoute/issues/11911)). ([#12013](https://github.com/diegosouzapw/OmniRoute/pull/12013)) — thanks @HouMinXi +- **fix(sse):** the OpenAI→Claude stream translator now defers the terminal `message_delta`/`message_stop` emission until the real usage block has arrived (or a genuine end-of-stream flush forces it) instead of emitting it immediately on `finish_reason` — previously, when the trailing usage-only chunk (`{"choices":[],"usage":{...}}`) arrived *after* the `finish_reason` chunk (the normal order for Fireworks/vLLM/Together and other `stream_options.include_usage` upstreams), the client-visible `message_delta` still carried stale/zero usage even though `state.usage` was internally corrected too late to matter (ported from [#11915](https://github.com/diegosouzapw/OmniRoute/pull/11915) — thanks @HouMinXi). ([#11933](https://github.com/diegosouzapw/OmniRoute/pull/11933)) +- **fix(sse):** `stripStore()` now forces `store=false` for stateless OpenAI-compatible Responses-API targets (unless the connection has explicitly opted in via `providerSpecificData.openaiStoreEnabled`), instead of only handling the `openai`/`agentrouter` cases — a client-supplied `store` value previously passed through untouched to backends that don't actually persist responses server-side ([#11916](https://github.com/diegosouzapw/OmniRoute/pull/11916) — thanks @HouMinXi). +- **fix(models):** custom provider-node models (synced, custom, and alias-backed) now appear under their configured prefix in the unified catalog when the operator's model-id prefix mode is canonical, instead of being dropped whenever alias-inclusion was otherwise disabled ([#11918](https://github.com/diegosouzapw/OmniRoute/pull/11918) — thanks @HouMinXi). +- **fix(models):** the unified model catalog now suppresses stale static registry models (including effort-tier variants) for any provider whose active connection has an authoritative live synced catalog, not only providers already using exclusive-synced-listing — a connection with `providerUsesAuthoritativeLiveCatalog` previously kept serving both the live-synced models and the stale static rows side by side ([#11919](https://github.com/diegosouzapw/OmniRoute/pull/11919) — thanks @HouMinXi). +- **fix(ui):** `ProviderIcon`'s three remaining unguarded lookups (`PROVIDER_ICON_ALIASES`, `LOCAL_SVG_ALIASES`, `THEMED_SVGS`) now use `Object.hasOwn()` own-property checks — a provider id such as `constructor` or `__proto__` previously resolved through the prototype chain instead of falling through to the unknown-provider CDN fallback (`getLobeProviderIcon()` itself was already guarded by [#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)); `ProviderPageHeader`'s `color` field is also now optional, matching the rest of the component's defensive typing (ported from [#11920](https://github.com/diegosouzapw/OmniRoute/pull/11920) — thanks @HouMinXi). ([#11935](https://github.com/diegosouzapw/OmniRoute/pull/11935)) — thanks @NoxzRCW +- fix(ollama): preserve multi-byte UTF-8 content split across stream chunks in the Ollama NDJSON transform, which previously corrupted CJK/emoji into U+FFFD (#11921) — thanks @pacocartones +- **fix(providers):** OrcaRouter chat requests now target `/v1/chat/completions` instead of the bare `/v1` API root, fixing the upstream `404 Invalid URL (POST /v1)` ([#11923](https://github.com/diegosouzapw/OmniRoute/pull/11923)). — thanks @echel0nn +- **fix(plugins):** deliver the `onStreamComplete` event to disk-installed plugins. The event shipped in v3.8.50 (#9669) was emitted internally but had no plugin-facing wiring, so no plugin could ever subscribe: the manifest schema silently dropped `hooks.onStreamComplete`, and the loader/manager only knew the seven legacy hooks. `onStreamComplete` is now a declarable manifest hook, wired through the loader and registered by the manager like the other hooks, and its payload carries a `requestId` so consumers can correlate the stream-completion event with the originating request ([#11934](https://github.com/diegosouzapw/OmniRoute/pull/11934)) — thanks @amaleta / @pacocartones +- **fix(db):** the Qdrant embedding-model dropdown now lists local/self-hosted providers (Ollama, LM Studio, vLLM, etc.) — an active connection is treated as "configured" when the provider allows an optional API key, not only when it has a real key or OAuth, so a running local embedding provider is no longer hidden from the picker ([#11949](https://github.com/diegosouzapw/OmniRoute/issues/11949)) ([#11995](https://github.com/diegosouzapw/OmniRoute/pull/11995)) +- **fix(providers):** Vertex AI Anthropic partner-model discovery now calls the Model Garden `v1beta1` publisher list (`/v1beta1/publishers/anthropic/models`, global) and parses its `publisherModels` envelope, so Claude models auto-synced from Vertex populate the active live catalog and route at request time instead of returning `Model '' is not available in the active live catalog` ([#11991](https://github.com/diegosouzapw/OmniRoute/issues/11991)) — thanks @fabioluissilva ([#11998](https://github.com/diegosouzapw/OmniRoute/pull/11998)) +- **fix(cli):** support `--base-url` alongside `--endpoint` in `omniroute nodes add`, `update`, and `validate` subcommands to prevent global `--base-url` shadowing issues ([#11999](https://github.com/diegosouzapw/OmniRoute/issues/11999)). ([#12033](https://github.com/diegosouzapw/OmniRoute/pull/12033)) — thanks @HouMinXi +- **fix(providers):** `cloudflare-ai` no longer refuses image content parts for every Workers AI model ([#12002](https://github.com/diegosouzapw/OmniRoute/pull/12002)) — the plain-string `content` requirement behind #2539 is carried by the _model_ schema, not by the `/ai/v1/chat/completions` endpoint (measured: an all-text part array returns 200 on `@cf/mistralai/mistral-small-3.1-24b-instruct`, `@cf/meta/llama-4-scout-17b-16e-instruct` and `@cf/meta/llama-3.3-70b-instruct-fp8-fast`, and 400 on the text-only `@cf/qwen/qwen2.5-coder-32b-instruct`). `transformRequest()` flattened every array and threw on the first non-text part (#6390), so image input was refused for vision-capable Cloudflare models that accept it. All-text arrays are still flattened — the one shape every model accepts — while an array carrying a non-text part is passed through untouched, so the attachment is still never silently dropped. Regression guards: `tests/unit/cloudflare-ai-image-parts-6390.test.ts`. — thanks @davidlinfr +- **fix(resilience):** decouple the limiter-managed execution backstop from the queue-wait budget — new `requestQueue.executionMaxWaitMs` (env `RATE_LIMIT_EXECUTION_MAX_WAIT_MS`, default 600000 = 10 min) now feeds Bottleneck's post-dispatch `expiration`, while `requestQueue.maxWaitMs` keeps its documented queue-wait semantics. Previously the queue-wait budget doubled as the execution expiration, so legitimate long-running calls on non-incremental gateways (whole generation buffered before the first upstream byte, e.g. Console Go / Command Code tiers serving GLM models) were killed mid-flight at the queue budget with a false 504 `RATE_LIMIT_EXECUTION_TIMEOUT` — the local limiter undercut the provider-aware upstream fetch-start timeouts. The surfaced 504 message now names `requestQueue.executionMaxWaitMs`; the error keeps the #4165 guarantees (disclaims an upstream timeout, preserves the Bottleneck error as `cause`, branded code + trusted provenance, classified request-scoped so combo falls back). A real queue-wait bound (the `Promise.race` around `limiter.schedule()` sketched in #9533) remains future work. (#12025) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **Call logs:** keep the `error` field when an artifact exceeds the storage cap, instead of replacing it with the omission marker. The error is the only field that says *why* a request failed and is typically ~90 bytes next to the multi-hundred-KB bodies that trip the cap, so dropping it left a size-limited row undiagnosable — a provider outage, a local timeout and an upstream 400 all rendered identically. It is now preserved at every fallback stage, truncated to 4KB if it is itself large ([#12026](https://github.com/diegosouzapw/OmniRoute/issues/12026)). ([#12095](https://github.com/diegosouzapw/OmniRoute/pull/12095)) — thanks @ntdat812 +- **fix(diagnostics):** preserve the error field (truncated to 4KB with a `[truncated: …]` suffix) in every call-log artifact size-limit fallback stage. Previously the minimal fallback replaced the error with `[omitted: call log artifact size limit exceeded]`, so an oversized artifact row showed nothing about WHY the request failed — e.g. 91 of 847 opencode-go 504 rows on one production instance were undiagnosable from the dashboard. Oversized request/response bodies are still omitted exactly as before; the error cap is independent of the payload sizes that tripped the fallback. (#12026) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **fix(sse):** OpenAI Responses clients that declare the native `web_search` tool now receive a spec-shaped `web_search_call` output item with `action.sources` alongside the preserved function-call round-trip, so search results executed through OmniRoute's own search backend are consumable by standard Responses clients (Codex, pi-web-access, …). ([#12031](https://github.com/diegosouzapw/OmniRoute/pull/12031)) — thanks @watchingdogs +- **fix(cli):** use in-thread alias resolver hooks on modern runtimes to avoid deprecation noise and improve Node.js forward compatibility ([#12073](https://github.com/diegosouzapw/OmniRoute/issues/12073)). ([#12083](https://github.com/diegosouzapw/OmniRoute/pull/12083)) — thanks @xiaoyaner0201 +- **fix(combo):** an operator-set **Agent Features → Context length** on a combo is now honored at request time. The value was persisted and advertised through `/v1/models`, but `resolveComboContextLimit()` never consulted it — so a multi-target combo whose members carry no per-model window fell through to the provider's generic `defaultContextLength` (openrouter 128000, command-code 200000) and rejected large requests with `Input exceeds context window … limit 128000` despite the combo being explicitly sized much larger. An identical single-target combo worked, because it collapses to its concrete target before the guard runs. Invalid values (0/negative/NaN/Infinity) are ignored, so the existing target → combo-min → fallback order is unchanged. ([#12090](https://github.com/diegosouzapw/OmniRoute/pull/12090)) — thanks @adivekar-utexas +- **fix(sse):** passthrough streams now estimate usage on finish when upstream closes without usage even with `stream_options.include_usage` — avoids `0 tokens / 0%` for providers that stay silent (and correctly handles trailing empty-choices usage) ([#12151](https://github.com/diegosouzapw/OmniRoute/pull/12151)) — thanks @maxmad64bis +- **fix(combos):** clearing an agent feature in the combos editor now persists — unchecking context cache protection, or emptying the system message or tool filter, sends an explicit `null` instead of dropping the field from the `PUT` body, which the update merge read as "leave unchanged" ([#12177](https://github.com/diegosouzapw/OmniRoute/pull/12177)) — thanks @foreveryh / @ntdatt812 +- **fix(memory):** self-hosted embedding endpoints now vectorize — the vector width is measured from the first embedding that comes back instead of being read from a registry that cannot describe them, so `vec_memories` is created and memories stop piling up unvectorized behind a green health check ([#12180](https://github.com/diegosouzapw/OmniRoute/pull/12180)) — thanks @kanade-hoshino / @ntdatt812 +- **fix(translator):** the leading `system` message now reaches Responses-API upstreams when its `content` is a content-part array — it was read as `typeof content === "string" ? content : ""`, so a prompt-caching client (Anthropic `cache_control`, the shape LiteLLM and the Anthropic SDK emit) had its entire system prompt replaced by an empty `instructions`. The request was still accepted with a normal `prompt_tokens` count, so the model answered with no instructions and nothing in the response said they were missing. Mid-conversation system turns already handled the array shape ([#7056](https://github.com/diegosouzapw/OmniRoute/pull/7056)); only the first one did not ([#12206](https://github.com/diegosouzapw/OmniRoute/issues/12206)). Regression guard: `tests/unit/translator-openai-responses-system-content-parts.test.ts`. ([#12207](https://github.com/diegosouzapw/OmniRoute/pull/12207)) — thanks @Gorillaz322 +- **fix(free-tier):** `/api/free-tier/summary` no longer computes its totals from a Radar feed built before the catalog the running release ships. When the cached feed is older — or carries no build date at all — the route answers from the shipped catalog, resolved through the operator's local model state so disabled and tombstoned models stay out of the numbers ([#12215](https://github.com/diegosouzapw/OmniRoute/pull/12215)). — thanks @maxmad64bis +- **fix(oauth):** Keep a Claude personal workspace and a Team organization as separate connections — they share the same email and `accountUUID`, so the email-only OAuth dedup let the second login overwrite the first account's tokens; `organizationUUID` now disambiguates them, the way `workspaceId` does for Codex ([#12222](https://github.com/diegosouzapw/OmniRoute/pull/12222)) — thanks @davidebaraldo +- **fix(resilience):** a 402 on a single paid model of a passthrough/gateway provider (e.g. `kilo-gateway`, `ollama-cloud`) no longer terminalizes the whole connection with a never-auto-recovered `credits_exhausted` status — only the paid model is locked out, so free models on the same key keep serving. 402 variant of [#3027](https://github.com/diegosouzapw/OmniRoute/issues/3027). Single-credential providers are unaffected — a 402 there is still treated as the key being genuinely out of credit ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239) / [#10616](https://github.com/diegosouzapw/OmniRoute/issues/10616)) ([#12242](https://github.com/diegosouzapw/OmniRoute/issues/12242)) — thanks @brick30llc-ctrl ([#12266](https://github.com/diegosouzapw/OmniRoute/pull/12266)) — thanks @geekyNads +- **fix(sse):** trust `finish_reason: "length"`/`"max_tokens"` over the reasoning-consumed-token ratio in response quality validation, so a reasoning model truncated below the old 90% threshold correctly fails and retries instead of returning empty content as a silent "success" ([#12262](https://github.com/diegosouzapw/OmniRoute/pull/12262)) — thanks @brick30llc-ctrl +- **fix(settings):** `PUT /api/settings/cache-config` now persists `alwaysPreserveClientCache` to the flat general settings the runtime cache-control policy actually reads; previously the value landed in the databaseSettings "cache" section and was silently ignored, so the endpoint had no effect on `cache_control` passthrough ([#12304](https://github.com/diegosouzapw/OmniRoute/pull/12304)) — thanks @davidebaraldo +- **fix(grok-cli):** treat omitted SuperGrokPro `creditUsagePercent` as 0% used so Provider Limits still renders a weekly bar (proto3 zero-elision) ([#12312](https://github.com/diegosouzapw/OmniRoute/pull/12312)) — thanks @HouMinXi +- **fix(combo):** Expose the two Auto-Combo scoring factors nobody could set — the combo validation schema and the dashboard weight sliders both declared 13 of the scorer's 15 factors, so `connectionDensity` (spreads load across a provider's connections) and `quality` were dropped on save and offered nowhere. The sliders also shipped their own default table that differed from the engine's on every non-zero factor and summed to 1.05, so the percentages shown next to them added up to 105%. Both lists now match `DEFAULT_WEIGHTS`, and a test keeps them there. Note that a combo whose stored `weights` omitted the two keys was effectively running with them at zero and the other thirteen renormalized upward; it now runs with the engine's intended distribution, so its routing does shift ([#12314](https://github.com/diegosouzapw/OmniRoute/pull/12314)) — thanks @maxmad64bis +- **fix(docs):** The free-tier reference no longer says its numbers come "confidence tagged per row" — no catalog entry carries a confidence tag and the API serves none, so every figure on that page is an estimate of the same, unstated quality. The page now states what an entry does vouch for: an independently documented hard stop (set by hand with the source in a comment, never defaulted to `true`) and a prompt-training disclosure, both with live counts the `check:docs-counts` gate keeps honest ([#12318](https://github.com/diegosouzapw/OmniRoute/pull/12318)) — thanks @maxmad64bis +- **fix(usage):** `adobe-firefly` and `firefly` have had a working usage fetcher since Adobe Firefly landed, but neither was ever added to the registration list, so the provider-plugin manifest, `genericQuotaFetcher` and the free-access quota cache all reported them as having no usage support — while `USAGE_SUPPORTED_PROVIDERS` said the opposite. Both are now declared, which also means their credit balance is fetched like any other declared provider's: `registerGenericQuotaFetchers` now registers a generic quota fetcher for them, and `resolveFreeAccessState` no longer returns early. A test holds the registration list to the dispatcher's switch in both directions, which is what the module's own docstring already asked for in prose ([#12321](https://github.com/diegosouzapw/OmniRoute/pull/12321)) — thanks @maxmad64bis +- **fix(quota):** drop the generic quota cache (agy / Antigravity / Claude OAuth) on an upstream 429 so reset-aware scoring does not keep a 60s stale snapshot, and force-refresh the next usage fetch so inner provider caches cannot recache the same window ([#12325](https://github.com/diegosouzapw/OmniRoute/pull/12325)) — thanks @HouMinXi +- **fix(combos):** deleting a combo now clears its persisted LKGP pins instead of leaving unreachable `key_value` rows behind ([#12326](https://github.com/diegosouzapw/OmniRoute/issues/12326)) ([#12425](https://github.com/diegosouzapw/OmniRoute/pull/12425)) — thanks @KrzysiekSko +- **fix(sse):** Keep ZWNJ (U+200C) and ZWJ (U+200D) in assistant text, reasoning and tool-call arguments — Persian/Kurdish half-space (`ارائه‌دهنده`), Arabic/Indic shaping and emoji sequences no longer lose them; the response de-obfuscation now removes joiners only between ASCII word characters, where the request side inserts them ([#12186](https://github.com/diegosouzapw/OmniRoute/issues/12186)) — thanks @rezjalibd ([#12359](https://github.com/diegosouzapw/OmniRoute/pull/12359)) — thanks @pacocartones +- **fix(resilience):** count resolved upstream 5xx results against the provider circuit breaker on the chat path — `CircuitBreaker.execute()` no longer reads a resolved `{ success: false, status: 5xx }` as a success that cancels the call-site failure, so a provider answering 503s now trips its breaker instead of staying `CLOSED` at `failureCount: 1`; single-model and combo dispatches are each accounted exactly once ([#12254](https://github.com/diegosouzapw/OmniRoute/issues/12254)) ([#12360](https://github.com/diegosouzapw/OmniRoute/pull/12360)) — thanks @pacocartones +- **fix(providers):** resolve the Codex quota auto-ping model from the live provider catalog and lifecycle registry instead of the retired `gpt-5.1-codex-mini`, and pause the ping with one actionable warning when no selectable Codex model exists rather than retrying a shut-down id every cooldown window ([#11905](https://github.com/diegosouzapw/OmniRoute/issues/11905)) ([#12361](https://github.com/diegosouzapw/OmniRoute/pull/12361)) — thanks @pacocartones +- **fix(api):** keep the `{created, data}` wrapper on combo-routed `/v1/images/generations` responses and default Codex image results to `b64_json` on both `/v1/images/generations` and `/v1/images/edits` so Codex CLI's built-in `image_gen` can decode them ([#12268](https://github.com/diegosouzapw/OmniRoute/issues/12268)) ([#12362](https://github.com/diegosouzapw/OmniRoute/pull/12362)) — thanks @pacocartones +- **fix(sse):** Name the shadowed custom provider node when a built-in provider id/alias (e.g. `openference` → `of`) reserves the prefix of an existing OpenAI/Anthropic-compatible node, so the runtime `No active credentials for provider: ` error explains that the prefix routed to the built-in and never reached the node's healthy connections, instead of contradicting the dashboard ([#11943](https://github.com/diegosouzapw/OmniRoute/issues/11943)) — thanks @morpheus9393 ([#12365](https://github.com/diegosouzapw/OmniRoute/pull/12365)) — thanks @pacocartones +- **fix(i18n):** wrap `ccOnboardingKeyPlaceholder` in ICU single quotes across all 43 locales so angle brackets render literally instead of being parsed as rich-text tags, which crashed the Claude Code onboarding block with `INVALID_MESSAGE: INVALID_TAG` ([#12302](https://github.com/diegosouzapw/OmniRoute/issues/12302)) ([#12369](https://github.com/diegosouzapw/OmniRoute/pull/12369)) — thanks @gonisulaimann +- **fix(models):** publish `effort_tiers` on Kimi K3's synced base-model entries (`kmca/k3`, `kmca/k3-256k`) so catalog-only clients (OpenCode, plain SDK pickers) can see and select the reasoning tiers (`low`/`high`/`max`) the synced metadata already carried — the `isSkippedEffortProvider` gate no longer suppresses tier visibility on those base entries, while synthetic `-` variant generation stays prevented and Codex/GLM base models remain excluded unchanged ([#12299](https://github.com/diegosouzapw/OmniRoute/issues/12299)) ([#12371](https://github.com/diegosouzapw/OmniRoute/pull/12371)) — thanks @gonisulaimann +- **fix(guardrails):** keep `auto`/`auto/*` virtual combos exempt from the Vision Bridge `fixedModel` credential guard so a combo target is passed through instead of silently falling back to global auto-selection ([#12237](https://github.com/diegosouzapw/OmniRoute/issues/12237)) ([#12373](https://github.com/diegosouzapw/OmniRoute/pull/12373)) — thanks @pacocartones +- **fix(combo):** capability-filter exhaustion caused by `max_tokens` above every target's known output limit now reports that reason (requested `max_tokens` vs the pool's highest known ceiling) instead of the unrelated "supports structured output" message ([#12229](https://github.com/diegosouzapw/OmniRoute/issues/12229)) — thanks @DW-MediaLab ([#12374](https://github.com/diegosouzapw/OmniRoute/pull/12374)) — thanks @pacocartones +- **fix(auth):** the `least-used` account strategy now prefers accounts without backoff before falling back to oldest `lastUsedAt`, the same tie-break `round-robin` already applies, so a failover no longer lands on a just-rate-limited account for a single request ([#12279](https://github.com/diegosouzapw/OmniRoute/issues/12279)) — thanks @tenshiak ([#12375](https://github.com/diegosouzapw/OmniRoute/pull/12375)) — thanks @pacocartones +- **fix(docker):** the `chatgpt-web-codex-browser` image now finds the Chrome binary under `chrome-linux64/` (Chrome for Testing layout in `playwright:v1.62.0-noble`) as well as the legacy `chrome-linux/`, so the container no longer crash-loops with `exec: --headless=new: not found` ([#12024](https://github.com/diegosouzapw/OmniRoute/issues/12024)) ([#12376](https://github.com/diegosouzapw/OmniRoute/pull/12376)) — thanks @pacocartones +- **fix(providers):** declare `groq/compound` and `allam-2-7b` as non-reasoning models in the curated Groq registry so `reasoning_effort` / `output_config.effort` / `thinking` from Claude Code are stripped instead of forwarded, which Groq rejected with HTTP 400 ([#12134](https://github.com/diegosouzapw/OmniRoute/issues/12134)) ([#12379](https://github.com/diegosouzapw/OmniRoute/pull/12379)) — thanks @pacocartones +- **fix(executors):** `OpencodeExecutor` no longer forces a direct connection when the connection has a proxy assigned in Proxy Management but no per-account proxies: the single-account fast path used to wrap the upstream dispatch in the direct-egress sentinel, discarding the ambient proxy context the chat handler had pinned from `proxy_assignments`, so API-key `opencode`/`opencode-go` connections egressed from the host IP (and hit geoblocks) despite the assignment. The direct pin is now applied only when no ambient proxy context exists ([#11894](https://github.com/diegosouzapw/OmniRoute/issues/11894) — thanks @hizzt) ([#12380](https://github.com/diegosouzapw/OmniRoute/pull/12380)) — thanks @pacocartones +- **fix(api):** `GET /v1/models` with `MODELS_CATALOG_PREFIX_MODE=canonical` (or `?prefix=canonical`) now lists providers whose registry alias is undefined or equal to their own id (Antigravity, Antigravity CLI and other self-aliased built-ins) — their single `provider/model` id was dropped by the alias/canonical duplicate guard in the static, synced, custom and alias-backed catalog loops ([#12058](https://github.com/diegosouzapw/OmniRoute/issues/12058)) — thanks @cheynetom ([#12381](https://github.com/diegosouzapw/OmniRoute/pull/12381)) — thanks @pacocartones +- **fix(translator):** Drop replayed `thinking` blocks that carry no signature (the shape produced from cross-provider `reasoning_content`) instead of stamping the default Claude signature on them, which Anthropic rejected with `400 Invalid signature in thinking block` on the next turn served by an Anthropic rung ([#12105](https://github.com/diegosouzapw/OmniRoute/issues/12105)) — thanks @atescivitci-cmd ([#12386](https://github.com/diegosouzapw/OmniRoute/pull/12386)) — thanks @pacocartones +- **fix(cli):** Resolve Bun's `--preload` polyfill path against the package root instead of `dist/`, so `omniroute` installed with `bun install -g` no longer crashes at startup with `error: preload not found …/dist/open-sse/utils/setupPolyfill.ts` ([#11980](https://github.com/diegosouzapw/OmniRoute/issues/11980)) — thanks @joglomedia ([#12387](https://github.com/diegosouzapw/OmniRoute/pull/12387)) — thanks @pacocartones +- **fix(providers):** `gemini-business` now publishes its model catalog — `/v1/models` and `/v1/providers/gemini-business/models` list the 12 enterprise Gemini ids the executor understands instead of returning an empty list (#12107) ([#12389](https://github.com/diegosouzapw/OmniRoute/pull/12389)) — thanks @pacocartones +- **fix(db):** install `busy_timeout` before the SQLite connection's first statement so a process opening the database while another one closes its WAL connection waits out the transient EXCLUSIVE lock instead of dying with `database is locked`, and recognise the drivers' real BUSY/PROTOCOL/IOERR errors as transient in the corruption probe so the same lock no longer renames the database away as corrupt; deflakes `cross-process contenders never both acquire the same connection` (#12394 — thanks @pacocartones) +- **fix(chat-admission):** derive the `chat_admission_busy` 503 `Retry-After` from observed heavyweight-lease occupancy — the larger of the exhausted `OMNIROUTE_CHAT_ADMISSION_QUEUE_MS` window and the time since capacity last turned over, capped at 60 s — instead of a fixed 1 s (structural) / 2 s (byte-stage) hint that invited Codex/agent fan-out clients to re-send ~1 MiB `/v1/responses` bodies every second into a gate held for the whole SSE lifetime; an idle gate keeps the historical floors ([#12135](https://github.com/diegosouzapw/OmniRoute/issues/12135)) (#12395 — thanks @pacocartones) +- **fix(api-manager):** the API key permissions modal no longer silently drops `allowedCombos` entries its Combo picker cannot render — routing-rule names such as `rt-*`, which the backend already honours — when "All" is clicked and the key is switched back to "Restrict"; those entries now survive the toggle, are listed read-only under the combo list so the count and the list agree, and are saved back verbatim instead of persisting `[]` (deny-all) (#12397 — thanks @pacocartones) +- **fix(catalog):** write the NUL separator of the catalog connection memo key, the provider serviceKind memo key, the Video Bridge promotion group key and a JSON-exactness test fixture as the `\u0000` escape instead of a raw byte — same runtime value, but the raw byte made git, GitHub and ripgrep treat those files as binary (hidden PR diffs, silently skipped searches); a guard test now keeps raw NUL bytes out of `src/`, `open-sse/` and `tests/` (#12403 — thanks @pacocartones) +- **fix(providers):** add `CLAUDE_CODE_CLIENT_VERSION` and `GITHUB_COPILOT_CLI_VERSION` env overrides so Anthropic/Copilot client-version gates can be unblocked without a rebuild ([#12417](https://github.com/diegosouzapw/OmniRoute/issues/12417)) ([#12632](https://github.com/diegosouzapw/OmniRoute/pull/12632)) — thanks @HouMinXi +- **fix(db):** back-fill `last_ping_at` and `last_pinged_reset_key` on `provider_connections` during schema reconciliation so divergent lineages that skipped `123_quota_auto_ping` still accept quota auto-ping writes ([#12470](https://github.com/diegosouzapw/OmniRoute/pull/12470) — thanks @KooshaPari) +- **fix(ci):** document MIT exceptions for `@eloqnt/{config,format-json,format-po}` (next-intl transitive; locked tarballs omit `license`) and keep the A2A lifecycle vitest off the real SQLite persistence seam ([#12581](https://github.com/diegosouzapw/OmniRoute/issues/12581)) ([#12595](https://github.com/diegosouzapw/OmniRoute/pull/12595)) +- OpenCode plugin `/v1/models` catalog fetch now waits 30s by default and attaches HTTP `statusCode` on 401/5xx so host fallback plugins can hop instead of seeing an untyped AbortError/UnknownError. ([#12607](https://github.com/diegosouzapw/OmniRoute/pull/12607)) — thanks @RaviTharuma +- **CI:** the OpenAPI security-tier gate now mirrors `isAlwaysProtectedPath()` in full — it also reads `ALWAYS_PROTECTED_API_PATTERNS`, so the pattern-gated credential routes (`/api/providers/{id}/{claude,codex}-auth/{export,apply-local}`, GHSA-5926-2w35-7h4q) no longer report as unannotated. (#12605) +- **fix(api):** GET /v1/models no longer waits forever on a hung coalesced catalog rebuild; cold-path waits are bounded (`CATALOG_BUILD_TIMEOUT_MS`, default 8s) and a last-good 200 is served when the rebuild times out ([#12627](https://github.com/diegosouzapw/OmniRoute/issues/12627)). ([#12628](https://github.com/diegosouzapw/OmniRoute/pull/12628)) — thanks @RaviTharuma +- **fix(cursor):** a non-array `tool_calls` on an assistant message no longer crashes the cursor request translator with a `TypeError`; both loops now require an array ([#12689](https://github.com/diegosouzapw/OmniRoute/issues/12689)) ([#12691](https://github.com/diegosouzapw/OmniRoute/pull/12691)) — thanks @soroush5 +- **fix(chat):** Chat Completions no longer return empty `content` after a server-owned memory or skills tool; the first provider send and account/model recovery now share one pipeline so a follow-up round-trip can fill the reply ([#12696](https://github.com/diegosouzapw/OmniRoute/issues/12696)) — thanks @HouMinXi ([#12867](https://github.com/diegosouzapw/OmniRoute/pull/12867)) +- **fix(dashboard):** batch-deleting provider keys no longer toasts failure after a successful delete when the confirm button's click event is forwarded as `onAfter` ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) — thanks @HouMinXi +- **fix(glm):** drop the extra 16th argument to `createSSETransformStreamWithLogger` that TypeScript rejected (TS2554) and that never reached the TransformStream ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) ([#12770](https://github.com/diegosouzapw/OmniRoute/pull/12770)) — thanks @HouMinXi +- **fix(dashboard):** pass `nodeMap` into Runtime `QuotaGroup` so a quota monitor in error/exhausted/alerting no longer throws `ReferenceError: nodeMap is not defined`. ([#12868](https://github.com/diegosouzapw/OmniRoute/pull/12868)) — thanks @HouMinXi +- Restricted API keys whose `allowedModels` lists a combo name no longer skip every combo member at pre-dispatch (`ALL_TARGETS_SKIPPED`). Inner-target filtering still applies when the allow-list is a provider prefix or `disableNonPublicModels` is on ([#12899](https://github.com/diegosouzapw/OmniRoute/pull/12899)). — thanks @HouMinXi +- **fix(combos):** Effort-suffixed combo members inherit the base model's `model_context_overrides` row so priority order is not inverted on large requests ([#12926](https://github.com/diegosouzapw/OmniRoute/pull/12926)) — thanks @HouMinXi +- **fix(catalog):** Picker-added `customModels` enter the dispatch-time live catalog so combo members and bare inference no longer 400 ([#12934](https://github.com/diegosouzapw/OmniRoute/pull/12934)) — thanks @HouMinXi +- **fix(docker):** re-derive the Docker build's worker-pool memory budget from the MEASURED ~4.5 GB per-process RSS (the issue owner's own VPS dmesg OOM-killer reproduction) instead of the stale 2560 MB/worker inference #11419 shipped, and lower `OMNIROUTE_BUILD_WORKERS` 3 → 2 so 1 parent + 1 page-data worker (2 processes × 4.5 GB = 9 GB) fits the 12.288 GB (75%) budget on a 16 GB GitHub Actions runner — the previous default (1 parent + 2 workers = 13.5 GB) still overcommitted the runner and kept "Publish to Docker Hub" failing with `cannot allocate memory` after #11419 merged (#7518). ([#11663](https://github.com/diegosouzapw/OmniRoute/pull/11663)) +- **fix(electron):** the packaged Windows desktop build now passes the #7592 cold-restart smoke end to end. Five defects were found and fixed on the way: (1) optional-pack staging failed on any machine whose `tar` is GNU tar — it reads the drive letter in an absolute `-f C:\...` archive path as a remote rsh host (`Cannot connect to C:`), so staging now passes a bare filename with `cwd` at the tarball directory and surfaces tar stderr on failure; (2) the Electron `files` allowlist was missing `lib/loginHeaderCapture.js`, so the packaged main process crashed at startup with `Cannot find module './lib/loginHeaderCapture'`; (3) electron-builder ≥26 injects an `!**/node_modules/**` ignore into every extraResources pattern list that no positive filter can override, which silently dropped the staged runtime `node_modules` (including the better-sqlite3 N-API prebuild) from `resources/app` — a fresh v3.8.50 desktop build would have shipped with no native SQLite driver and reproduced the #7592 sql.js fallback on every machine; an `afterPack` hook now restores the staged `node_modules` after packing; (4) the packaged-app smoke harness redirected `USERPROFILE` into its temp DATA_DIR, but Electron resolves the Roaming profile from `%USERPROFILE%\AppData\Roaming\` (USERPROFILE takes precedence over the APPDATA env var) and the path service throws instead of creating it, so `requestSingleInstanceLock()` returned false and the app exited(0) silently before `app.whenReady()` — the harness now pre-creates the derived tree, and `ensureSmokeEnvDirs` is exported and unit-tested; (5) the #7592 driver assertion parsed a `[DB] Driver: ...` line that the server's primary DB path never printed (only the unused `openDatabaseAsync()` did) — `getDbInstance()` now emits the same line on open so the guard can actually assert the native driver. Also: the smoke env-allowlist unit test hardcoded POSIX paths and could only pass on Linux/macOS; expectations are now host-agnostic, with new regression tests for the USERPROFILE-derived tree and `tarPack` under absolute Windows-style paths ([#11443](https://github.com/diegosouzapw/OmniRoute/pull/11443)) — thanks @jonlwheat2-gif +- fix(sse): stop the auto-combo candidates inspector from silently dropping model-locked/cooled-down rows (#9133) ([#11994](https://github.com/diegosouzapw/OmniRoute/pull/11994)) — thanks @hartmark +- **fix(providers):** Perplexity Web no longer turns upstream stream failures into successful assistant text; pre-content failures remain eligible for fallback, partial output ends with a structured sanitized error, and failed sessions are not persisted ([#12465](https://github.com/diegosouzapw/OmniRoute/pull/12465)) +- **fix(video-bridge):** preserve chronological video and transcript fusion, source-frame timestamps, transcript provenance, full contact-sheet intervals, and pre-abort callback isolation ([#11681](https://github.com/diegosouzapw/OmniRoute/pull/11681)) +- **Z.ai Web:** HTTP 200 streams carrying an upstream error now terminate with a structured failure instead of assistant text plus a normal stop, preserving partial output while allowing pre-content combo fallback. ([#12454](https://github.com/diegosouzapw/OmniRoute/pull/12454)) +- Absorb `Error [AbortError]: request_signal_aborted` and DOMException AbortError shapes in the process-level client-abort crash guard so routine client disconnects no longer kill the server (exit code 7). ([#12165](https://github.com/diegosouzapw/OmniRoute/pull/12165)) — thanks @alvinveroy +- **fix(sse):** Treat Adapta Web `type:error` SSE events as sanitized non-stream failures instead of empty HTTP 200 completions. ([#12459](https://github.com/diegosouzapw/OmniRoute/pull/12459)) +- **fix(autoCombo):** layer 3 (`models_dev_tier`) no longer scores retired models as premium, merge cross-provider capability rows deterministically, and inherit tier scores through variant ids ([#11508](https://github.com/diegosouzapw/OmniRoute/issues/11508)). `getModelsDevTierFitness` now vetoes ids present in `config/quality/model-lifecycle.json` with status `retired` before any other signal; `loadModelCapabilities()` aggregates per `model_id` (any-non-null-true for booleans, max for `limit_context`) instead of last-write-wins over SQLite's undefined row order; a capabilities miss retries through `resolveScoresAs()`'s base id and reports `models_dev_tier:inherited`, matching layers 1–2 (#11492). Adds `getModelsDevTierFitnessWithSource()` and an `invalidateCapabilitiesCache()` test/ops hook. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) — thanks @Meet6338-X +- fix(sse): accept Claude `content:[]` + `stop_reason: max_tokens`/`tool_use` in `detectMalformedNonStream` (match `isEmptyContentResponse`) so Claude Code `/model` probes with `max_tokens: 1` no longer become a false 502 ([#11568](https://github.com/diegosouzapw/OmniRoute/pull/11568)) — thanks @Seramicx +- **fix(security):** sanitize `request.failed` diagnostics before publishing them to live dashboard listeners and replay history, while keeping status, model, provider, latency, and internal call-log diagnostics intact. ([#12469](https://github.com/diegosouzapw/OmniRoute/pull/12469)) +- **fix(pwa):** do not intercept dashboard navigations so Chrome can fall back from a stale HTTP/3 Alt-Svc advertisement (UDP :20128 is unpublished; F5 on a long-lived tab hung until a new tab opened a fresh TCP connection). ([#12767](https://github.com/diegosouzapw/OmniRoute/pull/12767)) — thanks @HouMinXi +- **fix(executors):** handle DuckDuckGo ERR_BN_LIMIT (418) without retrying — when the upstream returns `418 ERR_BN_LIMIT` (rate-limit/ban), the executor now returns the error immediately instead of burning another VQD acquisition that would only count against the IP limit. The retry logic for `418 ERR_CHALLENGE` (unsolved challenge) remains unchanged. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) ([#12110](https://github.com/diegosouzapw/OmniRoute/pull/12110)) — thanks @oyi77 / @Meet6338-X +- Fixed DuckDuckGo streaming responses losing JSON lines and UTF-8 characters split across network chunks. ([#11528](https://github.com/diegosouzapw/OmniRoute/pull/11528)) — thanks @pacocartones +- Harden generated Cloudflare, Vercel, and Deno relays by stripping hop-by-hop, framing, and proxy authentication headers before forwarding requests upstream. ([#11533](https://github.com/diegosouzapw/OmniRoute/pull/11533)) — thanks @pacocartones +- **feat(grok-cli):** Provider Limits shows grok-cli banked reset credits from `GetRemainingResets` (including a real zero; a failed RPC omits the row) and the existing View credits button now calls `ConsumerUiSvc/RedeemReset` for grok-cli. Live tokens use nested fields 10/20/30 (id + Timestamp), not compact 1/2/3. ([#12805](https://github.com/diegosouzapw/OmniRoute/pull/12805)) — thanks @HouMinXi +- **fix(memory):** Embedding Model Quick select, Embedding Source remote dropdown, and Rerank selector now list every configured provider with embedding/rerank support instead of only chat-catalog text matches plus OpenRouter live discovery; a generic OpenAI-compatible `/embeddings` + Cohere-compatible `/rerank` runtime fallback resolves any configured chat provider's embedding/rerank endpoint, so unlisted providers no longer fail with "Unknown embedding provider"; both memory selectors gained a free-text model override ([#11390](https://github.com/diegosouzapw/OmniRoute/pull/11390)) — thanks @rqzbeh +- Harden SQLite upgrades around the historical migration-074 version collision: missing discovery and inspector tables are replayed atomically, pre-existing databases (including setup-created skeletons) receive reusable content-addressed safety snapshots, and Node test/eval probes without `DATA_DIR` are isolated from the operator database. ([#12435](https://github.com/diegosouzapw/OmniRoute/pull/12435)) +- **fix(dashboard):** Moonshot/Kimi Open Platform voucher and cash leftover percentages follow the bucket balance, so an empty wallet no longer paints those rows as 100% while Available is 0% ([#12733](https://github.com/diegosouzapw/OmniRoute/pull/12733)) — thanks @HouMinXi +- **fix(combo):** return non-retryable HTTP 400 when all candidates for a pinned native Codex turn are unavailable due to model-scoped lockout, terminating the turn cleanly while preserving turn continuity and enabling standard Combo routing on subsequent turns ([#12240](https://github.com/diegosouzapw/OmniRoute/pull/12240)) — thanks @mdigitalbh81 +- Fixed auto-router strategies preserving the selected connection through ranking and dispatch when multiple connections share the same provider and model. ([#11530](https://github.com/diegosouzapw/OmniRoute/pull/11530)) — thanks @pacocartones +- Preserve Chat Completions JSON-object response formats when translating requests to the Responses API. ([#11529](https://github.com/diegosouzapw/OmniRoute/pull/11529)) — thanks @pacocartones +- Fix idempotency fingerprints so Responses API requests include `input` and other semantic generation fields using stable serialization, while excluding credentials and request metadata. ([#11532](https://github.com/diegosouzapw/OmniRoute/pull/11532)) — thanks @pacocartones +- **fix(api):** Generated API CLI commands now enforce required OpenAPI request bodies; Combo test commands forward the required `comboName` body, while API keys created by older writers after migration 149 preserve legacy allow-all Combo access without widening explicit empty allowlists — thanks @marcelokarval ([#12070](https://github.com/diegosouzapw/OmniRoute/pull/12070)) +- **fix(grok-web):** treat upstream streaming failures as failures instead of successful assistant text: error-only streams now fail readiness with HTTP 502, while failures after legitimate content preserve that partial output and terminate through the sanitized stream failure path without a normal `stop` completion. ([#12458](https://github.com/diegosouzapw/OmniRoute/pull/12458)) +- HuggingChat now turns HTTP 200 JSONL generation failures into a sanitized 502 before content, or a fixed public stream failure after partial output, so fallback and request persistence no longer record a false successful stop. ([#12456](https://github.com/diegosouzapw/OmniRoute/pull/12456)) +- **fix(providers):** keep 1min.ai HTTP 200 stream errors out of assistant content, preserve partial output, and expose sanitized terminal errors so pre-content failures can fall back. ([#12466](https://github.com/diegosouzapw/OmniRoute/pull/12466)) +- **fix(sessions):** preserve legacy Sessions data when the additive exclusive-session projection is unavailable, returning an empty projection and warning only once per contiguous outage instead of failing the endpoint; the Sessions badge intentionally reflects the merged legacy and exclusive row count introduced by [#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389) ([#11469](https://github.com/diegosouzapw/OmniRoute/pull/11469)) — thanks @KaspaPulse +- Render Nimble Search with the generic provider icon and serve Opper's proven logo locally. ([#12415](https://github.com/diegosouzapw/OmniRoute/pull/12415)) +- **fix(quality):** `config/quality/provider-assets-provenance.jsonl` was stale against the physical `public/providers/` tree (still listing 225 assets and 5 duplicate-content groups after several provider-retirement PRs removed 85 icon files), which would have made `check:provider-asset-provenance` fail on the next full run. Reconciled the manifest to the real 142-file snapshot (142 asset records, 1 duplicate-content group, 2 newly-registered files marked `unresolved` pending provenance research), repinned `auditedCommit`, and updated the regression test's hardcoded counts to match. ([#11876](https://github.com/diegosouzapw/OmniRoute/pull/11876)) +- fix(ui): wrap quota card footer action buttons to prevent clipping Refresh button on narrow cards — when extra action buttons such as `View credits` are present on Codex cards, allow the footer row to flex-wrap with `shrink-0` buttons so `Refresh now` remains visible and clickable across all responsive card widths ([#11464](https://github.com/diegosouzapw/OmniRoute/pull/11464)) — thanks @adevwithpurpose +- Keep Antigravity Gemini usable when the same connection's Claude weekly quota is empty; generic quota cache stays per-connection for every other provider. ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) — thanks @HouMinXi +- Fixed Tailscale tunnel setup to reject ports outside the valid 1-65535 range and safely install Windows MSI packages from paths containing apostrophes. ([#11534](https://github.com/diegosouzapw/OmniRoute/pull/11534)) — thanks @pacocartones +- Classify Cloudflared and Tailscale process-management routes as local-only and spawn-capable while preserving remote access to read-only tunnel status endpoints. ([#11531](https://github.com/diegosouzapw/OmniRoute/pull/11531)) — thanks @pacocartones +- Electron release workflow: the `publish-npm` job now grants `actions: read` to the reusable `npm-publish.yml` it calls (its `publish` job requests it), which is what made GitHub refuse the whole v3.8.50 run at startup and ship the release with zero desktop assets; a `workflow_dispatch` now builds the requested tag instead of the dispatching branch and can skip the npm leg (`publish_npm=false`) when only re-attaching assets ([#11974](https://github.com/diegosouzapw/OmniRoute/pull/11974)) +- **fix(dashboard):** The Combos page usage guide now reads its dismissal through `useSyncExternalStore` instead of correcting SSR state inside an effect, removing an extra commit of the page tree on every load (and the `react-hooks/set-state-in-effect` error it raised). ([#12671](https://github.com/diegosouzapw/OmniRoute/pull/12671)) +- Electron release: `electron/package-lock.json` regained the optional `electron-builder-squirrel-windows` subtree (13 entries) that `npm ci` had been refusing as out of sync, `electron-release.yml` gained a `build_ref` dispatch input and stops regenerating release notes on a re-attach dispatch, and the npm publish workflow attaches the SBOM to the GitHub Release on dispatch publishes too — so the v3.8.51 tag ships every desktop asset and the SBOM like v3.8.49 did ([#12022](https://github.com/diegosouzapw/OmniRoute/pull/12022)) +- Drained the reds every PR against `release/v3.8.51` was born with: documented the three service-worker build-id variables, registered the six retirement/tunnel tests with the mutation gate, approved `eslint-plugin-react-hooks` in the dependency allowlist, added the six `combo.sort.*` strings to `vi` and `pt-BR`, pointed the ChatGPT Web doc at the real migration-168 test, worded the g4f hint around the member key, and realigned four tests to the retired-provider catalog and the legacy-schema fixtures the retirement migrations touch. ([#11940](https://github.com/diegosouzapw/OmniRoute/pull/11940)) +- **fix(providers):** Claude, Grok, LMArena, Notion, and Perplexity web-cookie transports now use pooled `wreq-js` 3.2 instead of the native sidecar, with all nine supported bindings pinned and audited, and the applicable platform binding plus native-license evidence included in each release artifact ([#12429](https://github.com/diegosouzapw/OmniRoute/pull/12429), supersedes [#11753](https://github.com/diegosouzapw/OmniRoute/pull/11753)). +- **fix(providers):** Zed Hosted streaming failures now trigger fallback before content and end partial streams with a sanitized structured error instead of fake assistant text and a normal-success stop. ([#12455](https://github.com/diegosouzapw/OmniRoute/pull/12455)) +- **fix(test):** capture hook result after render (direct commit `b85d0abb0b`) +- **fix(sync):** repair the two regressions the v3.8.50 sync-back left on release/v3.8.51 (direct commit `5ade9e0851`) +- **revert(models):** drop the memo reset hook ported from #10055 (direct commit `282de841ae`) +- **fix(typecheck):** guard the optional connectionId before the reactive model sync (direct commit `f2825bad75`) +- **fix(quota-share):** release the winner's reserved in-flight slot (#11371) ([#11408](https://github.com/diegosouzapw/OmniRoute/pull/11408)) — thanks @oyi77 +- **fix(providers):** stop silently dropping reasoning effort on opencode families (#10788) ([#11409](https://github.com/diegosouzapw/OmniRoute/pull/11409)) — thanks @oyi77 +- **fix(combo):** enable genuine quota-aware routing for generic providers (antigravity, claude, etc.) ([#11411](https://github.com/diegosouzapw/OmniRoute/pull/11411)) — thanks @HouMinXi +- **fix(oauth):** allow expired token retry before deactivation and activate expired retry loop (#P0) ([#11414](https://github.com/diegosouzapw/OmniRoute/pull/11414)) — thanks @HouMinXi +- **perf(executors):** lazy-load the executor registry — defer class imports + construction to first use (#11220) ([#11421](https://github.com/diegosouzapw/OmniRoute/pull/11421)) — thanks @oyi77 +- **fix:** make release/v3.8.50 CI gates green (run 32786966560 root causes) ([#11450](https://github.com/diegosouzapw/OmniRoute/pull/11450)) — thanks @jonlwheat2-gif +- **fix(sse):** exact-domain cookie match and origin-equality URL assertions ([#11452](https://github.com/diegosouzapw/OmniRoute/pull/11452)) — thanks @hartmark +- **fix(providers):** send Responses API payload during OpenAI-compatible validation ([#11454](https://github.com/diegosouzapw/OmniRoute/pull/11454)) — thanks @tuandinh0801 +- **fix(command-code):** fallback to /alpha/generate for Go plan without Provider API access ([#11455](https://github.com/diegosouzapw/OmniRoute/pull/11455)) — thanks @Chewji9875 +- **fix(responses-continuation):** fail closed on a log-truncated stored input/output array ([#11473](https://github.com/diegosouzapw/OmniRoute/pull/11473)) — thanks @hartmark +- **fix(cli):** write jcode settings as [providers.omniroute] in config.toml ([#11484](https://github.com/diegosouzapw/OmniRoute/pull/11484)) — thanks @hongnoul +- **fix(logging):** raise chat-log array truncation cap to 1000 and unify duplicate implementations ([#11499](https://github.com/diegosouzapw/OmniRoute/pull/11499)) — thanks @hartmark +- **fix:** restore live websocket URL helpers ([#11509](https://github.com/diegosouzapw/OmniRoute/pull/11509)) — thanks @PauloFH +- **perf(core):** eliminate async waterfalls across home, cache, and db operations (#11396) ([#11512](https://github.com/diegosouzapw/OmniRoute/pull/11512)) — thanks @HouMinXi +- **fix(kiro):** add runtime.us-east-1.kiro.dev as first-attempt endpoint ([#11517](https://github.com/diegosouzapw/OmniRoute/pull/11517)) — thanks @rifqiawl +- **fix(providers):** remove duplicate 128-tool truncation in OpencodeExecutor (#11444) ([#11518](https://github.com/diegosouzapw/OmniRoute/pull/11518)) — thanks @HouMinXi +- **fix(providers):** poll onboardUser LRO before classifying BYOP ([#11519](https://github.com/diegosouzapw/OmniRoute/pull/11519)) — thanks @rifqiawl +- **fix(translator):** preserve exact tool name casing for Claude tool_use payloads (#11487) ([#11520](https://github.com/diegosouzapw/OmniRoute/pull/11520)) — thanks @HouMinXi +- **fix(kimi-web):** align health probe with executor domain ([#11521](https://github.com/diegosouzapw/OmniRoute/pull/11521)) — thanks @pacocartones +- **fix(cli):** preserve existing admin password during setup ([#11522](https://github.com/diegosouzapw/OmniRoute/pull/11522)) — thanks @pacocartones +- **fix(mitm):** keep Windows DNS elevation off the POSIX sudo path (#11430) ([#11542](https://github.com/diegosouzapw/OmniRoute/pull/11542)) — thanks @official-burak +- **fix(sse):** scale chat admission by ingest byte budget instead of a fixed request count ([#11548](https://github.com/diegosouzapw/OmniRoute/pull/11548)) — thanks @TheDemonTuan +- **fix:** accept Gemini Web cookie exports ([#11554](https://github.com/diegosouzapw/OmniRoute/pull/11554)) — thanks @rafacpti23 +- **fix(i18n):** complete Brazilian Portuguese translations ([#11558](https://github.com/diegosouzapw/OmniRoute/pull/11558)) — thanks @rafacpti23 +- **fix(sse):** explicit types for openai-responses pureHelpers — clears last failing core typecheck gate ([#11567](https://github.com/diegosouzapw/OmniRoute/pull/11567)) — thanks @jonlwheat2-gif +- **fix(opencode):** hydrate Proxy Pool references for no-auth connections ([#11584](https://github.com/diegosouzapw/OmniRoute/pull/11584)) — thanks @AStupidBear +- **fix(ci):** resolve inherited release/v3.8.51 base-red CI failures ([#11608](https://github.com/diegosouzapw/OmniRoute/pull/11608)) — thanks @jonlwheat2-gif +- **fix(db):** fallback load STORAGE_ENCRYPTION_KEY from env files ([#11614](https://github.com/diegosouzapw/OmniRoute/pull/11614)) — thanks @benzntech +- **fix(providers):** mark Brave Search as no longer having a free tier ([#11621](https://github.com/diegosouzapw/OmniRoute/pull/11621)) — thanks @turbolego +- **fix:** pass tool result content through as string in OpenAI/Claude -> Gemini translation ([#11624](https://github.com/diegosouzapw/OmniRoute/pull/11624)) — thanks @Hsia97 +- **fix(sse):** scope credential health to exact key ([#11628](https://github.com/diegosouzapw/OmniRoute/pull/11628)) — thanks @KaspaPulse +- **fix(install):** guard prepare script against missing husky ([#11634](https://github.com/diegosouzapw/OmniRoute/pull/11634)) — thanks @Prajeeth-12 +- **fix(security):** redact NVIDIA_API_KEY at the log sink in the ad-hoc diag script ([#11639](https://github.com/diegosouzapw/OmniRoute/pull/11639)) +- **fix(types):** clear the two type regressions red on release/v3.8.51 ([#11640](https://github.com/diegosouzapw/OmniRoute/pull/11640)) — thanks @jonlwheat2-gif +- **fix(build):** resolve workspace: protocol dependencies to concrete semver versions during prepublish (#11540) ([#11641](https://github.com/diegosouzapw/OmniRoute/pull/11641)) — thanks @HouMinXi +- **fix(search):** properly resolve configured search connection in /v1/responses pipeline (#11524) ([#11642](https://github.com/diegosouzapw/OmniRoute/pull/11642)) — thanks @HouMinXi +- **fix(authz):** trust cf-connecting-ip for via-proxy detection when peer is Cloudflare edge (#11514) ([#11643](https://github.com/diegosouzapw/OmniRoute/pull/11643)) — thanks @HouMinXi +- **fix(security):** compare the OIDC state cookie in constant time ([#11646](https://github.com/diegosouzapw/OmniRoute/pull/11646)) +- **fix(security):** namespace the dedup hash by the calling API key ([#11649](https://github.com/diegosouzapw/OmniRoute/pull/11649)) +- **fix(models):** gate the Codex-native catalog loop on prefix mode (#11632) ([#11662](https://github.com/diegosouzapw/OmniRoute/pull/11662)) — thanks @xiaoyaner0201 +- **fix(dashboard):** project Codex routing quota in account pools ([#11666](https://github.com/diegosouzapw/OmniRoute/pull/11666)) — thanks @KaspaPulse +- **fix(ddgw):** normaliza system no upstream da release v3.8.51 ([#11692](https://github.com/diegosouzapw/OmniRoute/pull/11692)) — thanks @RhianB14 +- **fix(memory):** honor category filter in GET /api/memory ([#11699](https://github.com/diegosouzapw/OmniRoute/pull/11699)) — thanks @Sabeekhann +- **fix(ci):** detect stale workspace lockfile entries ([#11714](https://github.com/diegosouzapw/OmniRoute/pull/11714)) — thanks @pacocartones +- **fix(ci):** stamp dist/BUILD_SHA before the npm publish provenance gate ([#11721](https://github.com/diegosouzapw/OmniRoute/pull/11721)) +- **fix(docker):** decouple the best-effort Bun image from the release manifest ([#11724](https://github.com/diegosouzapw/OmniRoute/pull/11724)) +- **perf(compression):** avoid cloning memo misses twice ([#11727](https://github.com/diegosouzapw/OmniRoute/pull/11727)) — thanks @RaviTharuma +- **fix(docker):** let the bun digest artifact be absent, not fatal ([#11740](https://github.com/diegosouzapw/OmniRoute/pull/11740)) +- **fix(shared):** block cloud-metadata hosts under default remote-image guard ([#11755](https://github.com/diegosouzapw/OmniRoute/pull/11755)) — thanks @santosraju99-hub +- **fix(api):** preserve caller-provided X-Correlation-Id on chat completions ([#11760](https://github.com/diegosouzapw/OmniRoute/pull/11760)) — thanks @aniruddhaadak80 +- **fix(resilience):** lock permanently retired models instead of short backoff (Gemini ban prevention) ([#11762](https://github.com/diegosouzapw/OmniRoute/pull/11762)) — thanks @turbolego +- **fix:** mark Vercel AI Gateway as passthroughModels ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **fix(resilience):** stop hammering permanently-moved endpoints and billing-suspended accounts ([#11774](https://github.com/diegosouzapw/OmniRoute/pull/11774)) — thanks @turbolego +- **fix(lease):** remove global static reservation and gate routing on live active lease occupancy ([#11775](https://github.com/diegosouzapw/OmniRoute/pull/11775)) — thanks @TheDemonTuan +- **fix(ci):** size the install-upgrade gate to a measured run, and log the pack cost ([#11776](https://github.com/diegosouzapw/OmniRoute/pull/11776)) +- **fix(pwa):** stop serving the stale shell after deploys ([#11779](https://github.com/diegosouzapw/OmniRoute/pull/11779)) — thanks @HouMinXi +- **fix(combo):** lock GitHub models rejected as "not supported" for future requests ([#11781](https://github.com/diegosouzapw/OmniRoute/pull/11781)) — thanks @turbolego +- **fix(cli):** probe both IPv4 and IPv6 loopback for server readiness (#11766) ([#11794](https://github.com/diegosouzapw/OmniRoute/pull/11794)) — thanks @vermasomesh835 +- **fix(sse):** preserve CLIProxyAPI wrappers per connection ([#11797](https://github.com/diegosouzapw/OmniRoute/pull/11797)) — thanks @RaviTharuma +- **fix(providers):** honor model auto-fetch opt-in on create ([#11798](https://github.com/diegosouzapw/OmniRoute/pull/11798)) — thanks @RaviTharuma +- **fix(dashboard):** make model auto-fetch order independent ([#11805](https://github.com/diegosouzapw/OmniRoute/pull/11805)) — thanks @RaviTharuma +- **fix(docker):** make GHA cache exports best effort ([#11806](https://github.com/diegosouzapw/OmniRoute/pull/11806)) — thanks @RaviTharuma +- **fix(kiro):** do not permanently ban on 'User is not authorized to make this call' ([#11809](https://github.com/diegosouzapw/OmniRoute/pull/11809)) — thanks @Deftera186 +- **fix(sse):** keep cache-write tokens in OpenAI-shaped usage ([#11814](https://github.com/diegosouzapw/OmniRoute/pull/11814)) — thanks @TheDemonTuan +- **fix(docs):** resolve relative markdown and wiki links across Fumadocs and GitHub wiki ([#11834](https://github.com/diegosouzapw/OmniRoute/pull/11834)) — thanks @AndrianBalanescu +- **fix(chat):** preserve unstripped model string for passthrough provider routing ([#11840](https://github.com/diegosouzapw/OmniRoute/pull/11840)) — thanks @raheemuddin786 +- **fix(mcp):** avoid async initialization inside sync esm wrappers (#11569) ([#11849](https://github.com/diegosouzapw/OmniRoute/pull/11849)) — thanks @HouMinXi +- **fix(sse):** preserve coherent markdown token boundaries across stream translation chunks (#11606) ([#11851](https://github.com/diegosouzapw/OmniRoute/pull/11851)) — thanks @HouMinXi +- **fix(ci):** run the install-upgrade gate on disk, not on the /tmp tmpfs ([#11855](https://github.com/diegosouzapw/OmniRoute/pull/11855)) +- **fix(skills):** expand shorthand property types in injected tool schemas ([#11857](https://github.com/diegosouzapw/OmniRoute/pull/11857)) — thanks @alvinveroy +- **fix(ci):** publish npm from a hosted runner so provenance is accepted ([#11877](https://github.com/diegosouzapw/OmniRoute/pull/11877)) +- **fix(lint):** pin react hooks plugin to 7.0.1 ([#11886](https://github.com/diegosouzapw/OmniRoute/pull/11886)) +- **fix(ci):** surface and clear release-green ESLint failure ([#11890](https://github.com/diegosouzapw/OmniRoute/pull/11890)) — thanks @MumuTW +- **fix(guardrails):** prevent duplicate prompt-injection-guard log output ([#11936](https://github.com/diegosouzapw/OmniRoute/pull/11936)) — thanks @patrykkopycinski +- **fix(auth):** downgrade expected transient states from warn to debug ([#11937](https://github.com/diegosouzapw/OmniRoute/pull/11937)) — thanks @patrykkopycinski +- **fix(codex):** fail over image generation for imported free plans ([#11948](https://github.com/diegosouzapw/OmniRoute/pull/11948)) — thanks @Bl0ck154 +- **fix(codex):** normalize non-stream responses ([#11951](https://github.com/diegosouzapw/OmniRoute/pull/11951)) — thanks @Bl0ck154 +- **fix(images):** forward Antigravity image size ([#11952](https://github.com/diegosouzapw/OmniRoute/pull/11952)) — thanks @Bl0ck154 +- **fix(db):** invalidate connection cache after upsert ([#11953](https://github.com/diegosouzapw/OmniRoute/pull/11953)) — thanks @Bl0ck154 +- **fix(codex):** restore imported account state ([#11954](https://github.com/diegosouzapw/OmniRoute/pull/11954)) — thanks @Bl0ck154 +- **fix(sse):** give extended-thinking targets the reasoning readiness budget ([#11959](https://github.com/diegosouzapw/OmniRoute/pull/11959)) — thanks @abhisheksharma2411 +- **fix(dashboard):** use opaque background and readable text color on cost chart tooltips ([#11960](https://github.com/diegosouzapw/OmniRoute/pull/11960)) — thanks @ZaimMarzuki +- **fix(antigravity):** send complete loadCodeAssist metadata (ideType/platform/pluginType as numeric enums) ([#11969](https://github.com/diegosouzapw/OmniRoute/pull/11969)) — thanks @rifqiawl +- **fix(core):** resolve DB init race condition and reasoning translation ([#12003](https://github.com/diegosouzapw/OmniRoute/pull/12003)) — thanks @raheemuddin786 +- **fix(admission):** exclude reclaimable page cache from the cgroup pressure ratio ([#12017](https://github.com/diegosouzapw/OmniRoute/pull/12017)) — thanks @HouMinXi +- **fix(build):** prune non-production trees in NFT trace excludes and tsconfig ([#12028](https://github.com/diegosouzapw/OmniRoute/pull/12028)) — thanks @Chewji9875 +- **fix(migrations):** add renamed migration compatibility for 056/073/077/101 ([#12036](https://github.com/diegosouzapw/OmniRoute/pull/12036)) — thanks @oyi77 +- **fix(sse):** default crash-guard logger to console.warn, not console ([#12042](https://github.com/diegosouzapw/OmniRoute/pull/12042)) — thanks @HouMinXi +- **fix(catalog):** derive vision/modalities for built-in auto combos from effective target pool ([#12046](https://github.com/diegosouzapw/OmniRoute/pull/12046)) — thanks @Prajeeth-12 +- **fix(release):** the packaged-app smoke verifies the database opened, not a driver line the primary path never prints (twin of #12032) ([#12047](https://github.com/diegosouzapw/OmniRoute/pull/12047)) +- **fix(barrel):** migrate src/app/ off the @/lib/localDb barrel import (#11795 Phase 2) ([#12051](https://github.com/diegosouzapw/OmniRoute/pull/12051)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate src/lib/ off the localDb barrel to direct db imports (#59) ([#12052](https://github.com/diegosouzapw/OmniRoute/pull/12052)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate open-sse, src/shared, src/sse, src/models, src/domain off the @/lib/localDb barrel import (#11795 Phase 4) ([#12053](https://github.com/diegosouzapw/OmniRoute/pull/12053)) — thanks @jonlwheat2-gif +- **fix(barrel):** delete the @/lib/localDb barrel — every consumer migrated (#11795 Phase 5) ([#12055](https://github.com/diegosouzapw/OmniRoute/pull/12055)) — thanks @jonlwheat2-gif +- **fix(radar):** restore D12 public boundary ([#12057](https://github.com/diegosouzapw/OmniRoute/pull/12057)) +- **fix(dev):** bound webpack and Tailwind scans ([#12075](https://github.com/diegosouzapw/OmniRoute/pull/12075)) — thanks @backryun +- **fix(dev):** isolate root layout settings reads ([#12076](https://github.com/diegosouzapw/OmniRoute/pull/12076)) — thanks @backryun +- **fix(dev):** reduce instrumentation executor fan-out (phase 3) ([#12078](https://github.com/diegosouzapw/OmniRoute/pull/12078)) — thanks @backryun +- **fix(dev):** make logging resources HMR-singleton ([#12079](https://github.com/diegosouzapw/OmniRoute/pull/12079)) — thanks @backryun +- **fix(sse):** keep unavailable forced connections scoped ([#12080](https://github.com/diegosouzapw/OmniRoute/pull/12080)) — thanks @keeltrace +- **fix(dev):** isolate batch dispatch from instrumentation ([#12081](https://github.com/diegosouzapw/OmniRoute/pull/12081)) — thanks @backryun +- **fix(ci):** reconcile release test contract drift ([#12082](https://github.com/diegosouzapw/OmniRoute/pull/12082)) — thanks @backryun +- **fix(ci):** accept CVE-2025-68121 in the prebuilt tls-client .so, auto-close base-red issues, guard Scorecard on the default branch ([#12085](https://github.com/diegosouzapw/OmniRoute/pull/12085)) +- **fix(db):** drop three consumer-less 1proxy exports — dead-code base-red on release/v3.8.51 after the barrel deletion (#12055) ([#12087](https://github.com/diegosouzapw/OmniRoute/pull/12087)) +- **fix(api):** clear the six API-route TypeScript regressions the new gate landed red on ([#12094](https://github.com/diegosouzapw/OmniRoute/pull/12094)) +- **fix(release):** never let the tag-push Create Release append auto notes to the curated body ([#12096](https://github.com/diegosouzapw/OmniRoute/pull/12096)) +- **fix(sse):** honor CLIProxyAPI environment API key ([#12099](https://github.com/diegosouzapw/OmniRoute/pull/12099)) — thanks @RaviTharuma +- **fix(oauth):** bind Google refresh to the client that issued the token ([#12106](https://github.com/diegosouzapw/OmniRoute/pull/12106)) — thanks @HouMinXi +- **fix(dashboard):** make RequestLoggerDetail loadable outside Next — CSS via globals.css + CJS/ESM interop (#11703 base-reds) ([#12114](https://github.com/diegosouzapw/OmniRoute/pull/12114)) +- **fix(leases):** project status lease row to lease columns so joined connection PII never escapes ([#12115](https://github.com/diegosouzapw/OmniRoute/pull/12115)) — thanks @geek007git +- **fix(plugins):** do not kill the plugin process when a fire-and-forget hook times out ([#12116](https://github.com/diegosouzapw/OmniRoute/pull/12116)) — thanks @geek007git +- **fix(guardrails):** restore injection-guard logging on middleware-only routes ([#12117](https://github.com/diegosouzapw/OmniRoute/pull/12117)) — thanks @geek007git +- **fix(plugins):** refresh stored manifest from disk on activate so new hook fields reach existing installs ([#12120](https://github.com/diegosouzapw/OmniRoute/pull/12120)) — thanks @geek007git +- **fix(codex):** preserve existing provider state when bulk-import upserts a matching connection ([#12122](https://github.com/diegosouzapw/OmniRoute/pull/12122)) — thanks @geek007git +- **fix(ci):** clear the two base-reds the 2026-08-30 merge batch left on release/v3.8.51 (round 3) ([#12123](https://github.com/diegosouzapw/OmniRoute/pull/12123)) +- **fix(sse):** strip type:'custom' from Claude tools on agentrouter dispatch ([#12126](https://github.com/diegosouzapw/OmniRoute/pull/12126)) — thanks @wahidsadik371-coder +- **fix(sse):** stop advertising video providers the dispatcher cannot run ([#12131](https://github.com/diegosouzapw/OmniRoute/pull/12131)) — thanks @quiterunner-commits +- **fix(search):** treat HTTP 432 and plan limit errors as transient cooldown ([#12139](https://github.com/diegosouzapw/OmniRoute/pull/12139)) — thanks @HouMinXi +- **fix(sse):** refuse an AI Horde queue that cannot fit the request budget ([#12143](https://github.com/diegosouzapw/OmniRoute/pull/12143)) — thanks @quiterunner-commits +- **fix(ci):** clear the base-reds the afternoon merge batch left on release/v3.8.51 (round 5: provider count 352, TS2554/TS2677) ([#12144](https://github.com/diegosouzapw/OmniRoute/pull/12144)) +- **fix(translator):** strip neutral tool_choice when tools absent in Responses-to-Chat (#12141) ([#12166](https://github.com/diegosouzapw/OmniRoute/pull/12166)) — thanks @HouMinXi +- **fix(guardrails):** pass providerId to getResolvedModelCapabilities in checkComboVision (#12112) ([#12169](https://github.com/diegosouzapw/OmniRoute/pull/12169)) — thanks @HouMinXi +- **fix(translator):** strip plaintext reasoning content for opaque responses backends (#12128) ([#12171](https://github.com/diegosouzapw/OmniRoute/pull/12171)) — thanks @HouMinXi +- **perf(stream):** compile hot-path regexes once, bound token caches, fix quadratic buffering ([#12179](https://github.com/diegosouzapw/OmniRoute/pull/12179)) — thanks @opensource-elearning +- **fix:** resolve compression worker file using runtime anchors instead of… ([#12183](https://github.com/diegosouzapw/OmniRoute/pull/12183)) — thanks @NightStalker-87 +- **fix:** prevent Claude Code session kills via liveness-aware readiness + auto model echo ([#12189](https://github.com/diegosouzapw/OmniRoute/pull/12189)) — thanks @opensource-elearning +- **fix(translator):** preserve falsy primitive values in Gemini and Antigravity function response results ([#12191](https://github.com/diegosouzapw/OmniRoute/pull/12191)) — thanks @Siva010 +- **fix(oauth):** mark empty Antigravity projectId as degraded and clear stale errors (#11284) ([#12205](https://github.com/diegosouzapw/OmniRoute/pull/12205)) — thanks @HouMinXi +- **fix(providers):** gate the Codex auto-ping usage read on the shared quota throttle ([#12209](https://github.com/diegosouzapw/OmniRoute/pull/12209)) — thanks @abhisheksharma2411 +- **fix(combos):** prioritize SQLite row id over inner JSON id and notify delete errors ([#12213](https://github.com/diegosouzapw/OmniRoute/pull/12213)) — thanks @HouMinXi +- **fix(providers):** mark featherless.ai as no longer having a free tier (`hasFree: false`) ([#12216](https://github.com/diegosouzapw/OmniRoute/pull/12216)) — thanks @turbolego +- **fix(deepseek-web):** stop Turbopack dev panic in the PoW worker path resolver ([#12221](https://github.com/diegosouzapw/OmniRoute/pull/12221)) — thanks @hartmark +- **fix(chat-admission):** clarify local 503 source ([#12223](https://github.com/diegosouzapw/OmniRoute/pull/12223)) — thanks @keeltrace +- **fix(dev):** silence webpack runtime module warnings ([#12228](https://github.com/diegosouzapw/OmniRoute/pull/12228)) — thanks @backryun +- **fix(db):** use module.require for CommonJS runtime driver loading ([#12230](https://github.com/diegosouzapw/OmniRoute/pull/12230)) — thanks @raheemuddin786 +- **fix(memory):** honest probe-driven FTS5 keyword status + memory_id rowid sync ([#12231](https://github.com/diegosouzapw/OmniRoute/pull/12231)) — thanks @raheemuddin786 +- **fix(system):** propagate abort signal to stream reader in HTTP version checks ([#12232](https://github.com/diegosouzapw/OmniRoute/pull/12232)) — thanks @raheemuddin786 +- **fix(resilience):** isolate local host execution errors from provider circuit breakers ([#12233](https://github.com/diegosouzapw/OmniRoute/pull/12233)) — thanks @raheemuddin786 +- **fix(sse):** sort injected tools deterministically for prompt caching ([#12234](https://github.com/diegosouzapw/OmniRoute/pull/12234)) — thanks @raheemuddin786 +- **fix(combo):** always clear the loop-safety timer, not just on the happy path (#11804) ([#12245](https://github.com/diegosouzapw/OmniRoute/pull/12245)) +- **fix(quality):** base-red pair — stryker tap registration + turn-pin suites aligned to the window gate ([#12255](https://github.com/diegosouzapw/OmniRoute/pull/12255)) — thanks @backryun +- **fix(dev):** qualify Turbopack runtime boundaries ([#12258](https://github.com/diegosouzapw/OmniRoute/pull/12258)) — thanks @backryun +- **fix(combo):** fall back to full pool when collapsed sole survivor is context-too-small ([#12278](https://github.com/diegosouzapw/OmniRoute/pull/12278)) — thanks @opensource-elearning +- **fix(combo):** bound the pre-dispatch unavailable skip so a stale label cannot dark a pool (#12168) ([#12285](https://github.com/diegosouzapw/OmniRoute/pull/12285)) +- **fix(security):** strip Qwen/Alibaba console-session cookies from provider API responses ([#12287](https://github.com/diegosouzapw/OmniRoute/pull/12287)) +- **fix(usage):** console-aware Token Plan guidance + subscription hint on bailian 401 ([#12288](https://github.com/diegosouzapw/OmniRoute/pull/12288)) +- **fix(resourcePressure):** log numeric detail on every rejection, recover faster ([#12293](https://github.com/diegosouzapw/OmniRoute/pull/12293)) — thanks @hartmark +- **fix(release):** drain the 2026-09-01 base-red window — passthrough usage regression + radarPage i18n keys ([#12327](https://github.com/diegosouzapw/OmniRoute/pull/12327)) +- **fix(ci):** openapi-security-tiers checker must honor routeGuard patterns + imported prefixes ([#12350](https://github.com/diegosouzapw/OmniRoute/pull/12350)) — thanks @ggiak +- **perf(ci):** cache node_modules in the npm-ci-retry composite (#8084 D3) ([#12408](https://github.com/diegosouzapw/OmniRoute/pull/12408)) +- **revert(ui):** point CTAs back at their real destinations ([#12410](https://github.com/diegosouzapw/OmniRoute/pull/12410)) +- **fix(release):** drain the 2026-09-02 base-red — rerank-providers import + api-typecheck baseline ratchet ([#12414](https://github.com/diegosouzapw/OmniRoute/pull/12414)) +- **fix(types):** make system prompt injection noImplicitAny-safe ([#12416](https://github.com/diegosouzapw/OmniRoute/pull/12416)) +- **fix(quality):** validate typecheck baseline schema ([#12419](https://github.com/diegosouzapw/OmniRoute/pull/12419)) +- **fix(memory):** resolve rerank provider node cache import ([#12421](https://github.com/diegosouzapw/OmniRoute/pull/12421)) +- **fix(ci):** repair release-root regressions — pack dedup, web-session syntax, uc-image ids ([#12423](https://github.com/diegosouzapw/OmniRoute/pull/12423)) — thanks @backryun +- **fix(providers):** separate MaxAI and UC credential contracts ([#12431](https://github.com/diegosouzapw/OmniRoute/pull/12431)) +- **fix(cli):** preserve Claude settings on config set ([#12432](https://github.com/diegosouzapw/OmniRoute/pull/12432)) — thanks @KrzysiekSko +- **fix(providers):** repair the maxai credential block truncated by merge auto-resolve ([#12433](https://github.com/diegosouzapw/OmniRoute/pull/12433)) +- **fix(quality):** record the 2026-09-02 merged growth in the file-size baseline ([#12434](https://github.com/diegosouzapw/OmniRoute/pull/12434)) +- **fix(uc):** route UC error strings through sanitizeErrorMessage; allowlist the retired codex id ([#12437](https://github.com/diegosouzapw/OmniRoute/pull/12437)) +- **fix(adapta):** redact streamed upstream errors ([#12438](https://github.com/diegosouzapw/OmniRoute/pull/12438)) +- **fix(sse):** remap non-contiguous upstream tool_calls index to a gap-free output_index ([#12445](https://github.com/diegosouzapw/OmniRoute/pull/12445)) — thanks @hartmark +- **fix(quality):** detect duplicate tool_calls entries in one response ([#12446](https://github.com/diegosouzapw/OmniRoute/pull/12446)) — thanks @hartmark +- **fix(conversations):** resolve turn content OmniRoute never sends back to the client ([#12447](https://github.com/diegosouzapw/OmniRoute/pull/12447)) — thanks @hartmark +- **fix(combo):** do not treat credits-exhausted 401 as auth skip ([#12449](https://github.com/diegosouzapw/OmniRoute/pull/12449)) — thanks @RaviTharuma +- **fix(auth):** do not park healthy quota accounts as expired ([#12452](https://github.com/diegosouzapw/OmniRoute/pull/12452)) — thanks @RaviTharuma +- **fix(responses-continuation):** fail closed on a collector-truncated, empty output array ([#12460](https://github.com/diegosouzapw/OmniRoute/pull/12460)) — thanks @hartmark +- **fix(logging):** raise the SSE payload collector's default cap ([#12461](https://github.com/diegosouzapw/OmniRoute/pull/12461)) — thanks @hartmark +- **fix(resilience):** surface Responses failed.error.message in 502s ([#12472](https://github.com/diegosouzapw/OmniRoute/pull/12472)) — thanks @RaviTharuma +- **fix(security):** unbiased maxai X-Random nonce + stricter URL/regex assertions ([#12502](https://github.com/diegosouzapw/OmniRoute/pull/12502)) +- **fix(video):** re-anchor transcript log-redaction so PII/credential maskers can't reopen the leak (#12150 P1 follow-up) ([#12503](https://github.com/diegosouzapw/OmniRoute/pull/12503)) +- **fix(monitoring):** serve cached credentialHealth off the request path ([#12533](https://github.com/diegosouzapw/OmniRoute/pull/12533)) — thanks @RaviTharuma +- **fix(quota):** keep Antigravity Gemini usable when Claude weekly is empty ([#12566](https://github.com/diegosouzapw/OmniRoute/pull/12566)) — thanks @HouMinXi +- **fix(authz):** hard-gate every credential export and CLI-config write (GHSA-5926-2w35-7h4q) ([#12600](https://github.com/diegosouzapw/OmniRoute/pull/12600)) +- **fix(docs):** restore the Next build — REMOVED_PROVIDERS.md had no frontmatter (base-red #12581) ([#12610](https://github.com/diegosouzapw/OmniRoute/pull/12610)) +- **fix(quality):** re-point the zcodeProtocol public-creds allowlist to line 313 ([#12615](https://github.com/diegosouzapw/OmniRoute/pull/12615)) +- **fix(security):** close 3 advisories — search baseUrl exfil, sk- in the error sanitizer, bifrost relay header leak ([#12620](https://github.com/diegosouzapw/OmniRoute/pull/12620)) +- **fix(providers):** sanitize boolean required and nested bare maps for Gemini (#12269) ([#12624](https://github.com/diegosouzapw/OmniRoute/pull/12624)) — thanks @HouMinXi +- **fix(resilience):** keep Overloaded STREAM_EARLY_EOF off the provider breaker ([#12626](https://github.com/diegosouzapw/OmniRoute/pull/12626)) — thanks @HouMinXi +- **fix(catalog):** re-audit free-tier quotas against official pages ([#12649](https://github.com/diegosouzapw/OmniRoute/pull/12649)) +- **fix(dashboard):** keep a request's pending-tracking id stable across combo target retries ([#12650](https://github.com/diegosouzapw/OmniRoute/pull/12650)) — thanks @hartmark +- **fix(tests):** retire dead model ids from the chat-pipeline integration suite (base-red #12581) ([#12670](https://github.com/diegosouzapw/OmniRoute/pull/12670)) +- **fix:** resolve SqliteError no such table compression_run_telemetry during cleanup ([#12682](https://github.com/diegosouzapw/OmniRoute/pull/12682)) — thanks @groovecityJO +- **fix(combo):** treat a pin-only step as implicit connection allowlist ([#12697](https://github.com/diegosouzapw/OmniRoute/pull/12697)) — thanks @HouMinXi +- **fix(ci):** ship .npmrc in published package so legacy-peer-deps applies to consumers (#11544) ([#12699](https://github.com/diegosouzapw/OmniRoute/pull/12699)) — thanks @KooshaPari +- **fix(docker):** pin 4 CLI tools to exact versions (#12576) ([#12703](https://github.com/diegosouzapw/OmniRoute/pull/12703)) — thanks @KooshaPari +- **fix(docker):** document and harden cli profile trust boundary (#12570) ([#12706](https://github.com/diegosouzapw/OmniRoute/pull/12706)) — thanks @KooshaPari +- **fix(sse):** redact video transcript in pre-guardrail rejected-request logs (#12150 P2 item 7) ([#12710](https://github.com/diegosouzapw/OmniRoute/pull/12710)) +- **fix(i18n):** quote placeholder in OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES description (#12505) ([#12769](https://github.com/diegosouzapw/OmniRoute/pull/12769)) — thanks @KooshaPari +- **fix(api/pricing):** surface validation error message as string, not raw object (#12494) ([#12771](https://github.com/diegosouzapw/OmniRoute/pull/12771)) — thanks @KooshaPari +- **fix(sse):** re-enable prompt compression for native Codex passthrough ([#12834](https://github.com/diegosouzapw/OmniRoute/pull/12834)) — thanks @thomasmaerz +- **fix(dashboard):** model health tests for a provider node set to the Responses API now call `/v1/responses` with a Responses-shaped body instead of `/v1/chat/completions` — those models were reported as `Provider returned HTTP 200 but no text content` even though the same model answered normally through `/v1/responses` ([#13070](https://github.com/diegosouzapw/OmniRoute/issues/13070)) ([#13087](https://github.com/diegosouzapw/OmniRoute/pull/13087)) — thanks @ntdatt812 +- fix(routing): stop a round-robin combo's "opencode" targets from collapsing onto the opencode-zen connection (#11912) ([#13283](https://github.com/diegosouzapw/OmniRoute/pull/13283)) +- fix(routing): stop the reactive-compaction debug log from lying when compression is globally disabled (#11977) ([#13276](https://github.com/diegosouzapw/OmniRoute/pull/13276)) +- fix(electron): relativize standalone-bundle symlink targets so Stage 8 manifest verification stops failing on Windows (#11979) ([#13251](https://github.com/diegosouzapw/OmniRoute/pull/13251)) +- fix(dashboard): surface a visible error when Compression Studio's combined preview run fails (#12061) ([#13288](https://github.com/diegosouzapw/OmniRoute/pull/13288)) +- fix(dashboard): make the compression "Effective pipeline" preview honor the active profile and warn when the master switch is off (#12063) ([#13287](https://github.com/diegosouzapw/OmniRoute/pull/13287)) +- fix(providers): scope TinyCMS Web signer's DOM shims to each call instead of leaking them for the process lifetime, and surface a clean HTTP status on a non-JSON interception-toggles error (#12072) ([#13254](https://github.com/diegosouzapw/OmniRoute/pull/13254)) +- fix(guardrails): stop Vision Bridge from re-selecting a model locked after a 404 (#12111) ([#13259](https://github.com/diegosouzapw/OmniRoute/pull/13259)) +- fix(sse): require Responses-shaped body before native OpenAI-compatible passthrough (#12129) ([#13278](https://github.com/diegosouzapw/OmniRoute/pull/13278)) +- fix(providers): minimax-m3 now collapses manual thinking.type:"enabled" to adaptive, preventing upstream 400 (2013) (#12132) ([#13249](https://github.com/diegosouzapw/OmniRoute/pull/13249)) +- fix(db): scope model visibility overrides by modality so hiding a Chat model no longer hides an identically-ID'd Image/Embeddings/etc. model (#12172) ([#13258](https://github.com/diegosouzapw/OmniRoute/pull/13258)) +- fix(db): scope local-provider apiKey dedup to matching base URL so LM Studio/Ollama-style connections support multiple accounts (#12173) ([#13250](https://github.com/diegosouzapw/OmniRoute/pull/13250)) +- fix(providers): refresh Trae's stale Referer/Origin and forward user timezone so imported connections stop failing with 401 (#12190) ([#13255](https://github.com/diegosouzapw/OmniRoute/pull/13255)) +- fix(providers): route opencode-go/gpt-5.6-luna to /responses instead of /chat/completions (#12196) ([#13275](https://github.com/diegosouzapw/OmniRoute/pull/13275)) +- fix(dashboard): allow deleting the last extra-upstream-header row even when invalid (#12251) ([#13252](https://github.com/diegosouzapw/OmniRoute/pull/13252)) +- **fix(i18n):** translate pre-existing `__MISSING__:` keys for `combo.sort`, `requestLogger.detail` expand/collapse, `common.profile`, and `settings.resilienceCredentialHealth*` across 39 locales ([#12272](https://github.com/diegosouzapw/OmniRoute/issues/12272)) ([#13034](https://github.com/diegosouzapw/OmniRoute/pull/13034)) — thanks @HouMinXi +- fix(cli): run the Node.js runtime compatibility guard before the heavy `tsx/esm` + Commander import chain so an unsupported runtime gets a clear message instead of a raw `Invalid regular expression flags` crash (#12296) ([#13253](https://github.com/diegosouzapw/OmniRoute/pull/13253)) +- fix(dashboard): refresh the providers list after deleting a compatible provider node (#12298) ([#13256](https://github.com/diegosouzapw/OmniRoute/pull/13256)) +- fix(usage): fail closed on API-key budget enforcement when a provider's `auto` routing alias has no pricing row, instead of silently counting it as $0 (#12341) ([#13257](https://github.com/diegosouzapw/OmniRoute/pull/13257)) +- **fix(providers):** include the submitted Agnes video model when polling by `video_id` ([#12356](https://github.com/diegosouzapw/OmniRoute/pull/12356)) — thanks @pacocartones +- **fix(routing):** custom OpenAI-compatible nodes now honor the saved Chat/Responses API type after edits instead of letting the node's original ID prefix override the live connection setting ([#11884](https://github.com/diegosouzapw/OmniRoute/issues/11884)). ([#12358](https://github.com/diegosouzapw/OmniRoute/pull/12358)) — thanks @pacocartones +- fix(sse): surface an error instead of a silent empty 200 when a Claude stream closes with zero bytes (#12398) ([#13285](https://github.com/diegosouzapw/OmniRoute/pull/13285)) +- fix(oauth): warn before the dead localhost:8080 redirect in antigravity/gemini `oauth start` (#12413) ([#13265](https://github.com/diegosouzapw/OmniRoute/pull/13265)) +- fix(providers): stop devin-cli spawn error from double-closing the SSE controller (#12517) ([#13261](https://github.com/diegosouzapw/OmniRoute/pull/13261)) +- **fix(rerank):** clamp Voyage `top_k` to the documents actually sent after empty-string filtering, and honor `return_documents: false` in the NVIDIA response adapter (#12523 — thanks @pacocartones) +- **fix(audio):** `/v1/audio/translations` now resolves combo names the way `/v1/audio/transcriptions` already does, so a combo that `GET /v1/models` advertises is fanned out to its targets instead of being rejected with `400 Invalid translation model: . Use format: provider/model`; literal `provider/model` ids and unknown bare names behave as before (#12536 — thanks @pacocartones) +- **fix(gemini):** strip the JSON-Schema-2020-12 `prefixItems` keyword from Gemini tool schemas at every nesting level, so Claude Code tool definitions no longer fail with `400 Unknown name "prefixItems"` on Gemini models (#12540 — thanks @pacocartones) +- **fix(api-manager):** Expose an accessible loading status while API keys are fetched instead of an empty accessibility tree (#12541 — thanks @pacocartones) +- **fix(video):** Clamp `estimateJpegFrameBytes` at zero for padding-only payloads and build the three encode-side frame data URIs from `JPEG_FRAME_DATA_URI_PREFIX` instead of a repeated literal (#12543 — thanks @pacocartones) +- **fix(devin):** accept Windows `DEVIN_AGENTIC_HOME` sandbox paths (`C:\...\.sandbox\...`) in the isolated-home check so the Devin Claude Bridge no longer fails closed on Windows ([#12405](https://github.com/diegosouzapw/OmniRoute/issues/12405)) (#12545 — thanks @pacocartones) +- **fix(db):** Add `conversation_turn_nodes` and orphaned `agentic_conversations` to the auto-cleanup cycle under the existing `retention.callLogs` window, so identity nodes whose call-log content has already been purged no longer accumulate without bound in `storage.sqlite` (#12548 — thanks @pacocartones) +- **fix(i18n):** Wrap the `~/.claude/profiles//settings.json` placeholder in ICU single quotes in the `OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES` feature-flag description across all 42 locales and the TypeScript default, so next-intl no longer fails with `INVALID_MESSAGE: UNCLOSED_TAG` and the Feature Flags card shows the description instead of the raw key (#12549 — thanks @pacocartones) +- **fix(orchestration):** `updateCloudAgentTask` now publishes the task's real `status` on `agent.task.updated` when an update only touches `result`, `activities` or `error`, instead of the fabricated `"updated"` state, and stays silent when no row matched the id (#12550 — thanks @pacocartones) +- **fix(i18n):** the home "Recent Requests" panel and the Provider Topology legend are now translated instead of rendering English copies on non-English dashboards; the legend reads its own `home.topologyLegend*` labels with consistent casing rather than borrowing the memory-settings "Recent" and analytics "Error" strings (#12551 — thanks @pacocartones). +- **docs(reference):** bring the `FEATURE_FLAGS.md` catalog back to 1:1 with `featureFlagDefinitions.ts` — 20 missing flags added, the two `*_BLOCK_THRESHOLD` env-only knobs moved out of the flag tables, category/total counts and the Live WS port corrected, guarded by a static test (#12552 — thanks @pacocartones) +- **fix(i18n):** backfill missing `usage.kiloPass*` strings in 39 locales and restore `featureFlagOmnirouteDisableThinkingLevelVariantsDescription` in `pt.json` ([#12561](https://github.com/diegosouzapw/OmniRoute/issues/12561)) ([#13027](https://github.com/diegosouzapw/OmniRoute/pull/13027)) — thanks @HouMinXi +- fix(docker): default docker-compose app ports (dashboard/API/live-WS) to loopback instead of `0.0.0.0`, closing the anonymous `/v1` LAN/WAN exposure gap left open by `REQUIRE_API_KEY=false` (#12568) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): close DNS-rebinding SSRF gap in webhook outbound-URL guard (#12569) ([#13243](https://github.com/diegosouzapw/OmniRoute/pull/13243)) +- fix(docker): require a per-session token on the VNC browser CDP bridge and isolate it on a dedicated Docker network (#12571) ([#13241](https://github.com/diegosouzapw/OmniRoute/pull/13241)) +- fix(open-sse): write Adobe Firefly session tokens and cookie jars with 0700/0600 permissions instead of the process umask (#12572) ([#13242](https://github.com/diegosouzapw/OmniRoute/pull/13242)) +- fix(api): remove hardcoded wildcard CORS in openai-to-gemini-sse.ts so the centralized fail-closed CORS gate is the sole source of `Access-Control-Allow-Origin` (#12573) ([#13239](https://github.com/diegosouzapw/OmniRoute/pull/13239)) +- fix(api): enforce API key policy (budget/rate-limit/schedule/endpoint scoping) on the ElevenLabs speech-to-text, text-to-speech and voices proxy routes (#12574) ([#13244](https://github.com/diegosouzapw/OmniRoute/pull/13244)) +- fix(sse): cap HuggingChat NDJSON body size and bound the read loop with the fetch timeout so a stalled or hostile upstream cannot buffer unbounded memory (#12577) ([#13282](https://github.com/diegosouzapw/OmniRoute/pull/13282)) +- fix(docker): scope the cliproxyapi/qdrant/bifrost sidecars to loopback by default and forward `CLIPROXYAPI_MANAGEMENT_KEY` into the cliproxyapi container so its management API is not left both unauthenticated and LAN-published (#12578) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): create DB export temp paths with `fs.mkdtempSync` instead of predictable timestamps (#12579) ([#13237](https://github.com/diegosouzapw/OmniRoute/pull/13237)) +- Cline 401 bodies that say "re-authenticate your Cline account" classify as a refreshable OAuth token, not a terminal expired key. The cooling panel no longer labels every cooldown as a 429; it shows the recorded last error instead. (#12594) ([#13060](https://github.com/diegosouzapw/OmniRoute/pull/13060)) — thanks @HouMinXi +- **fix(catalog):** degrade unknown combo targets instead of dropping LCD modalities, and persist OpenRouter `architecture.input_modalities` into the capability snapshot ([#12613](https://github.com/diegosouzapw/OmniRoute/issues/12613)) ([#13042](https://github.com/diegosouzapw/OmniRoute/pull/13042)) — thanks @HouMinXi +- fix(providers): send `x-api-key` instead of `Authorization: Bearer` for OpenCode Zen's `/v1/responses` endpoint (Muse Spark Contributor models), fixing a 401 on OmniRoute's auth header (#12633) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(sse): surface the actionable "Auggie CLI not found" message when the shell reports a missing `auggie` binary via exit code instead of a spawn error (#12645) ([#13263](https://github.com/diegosouzapw/OmniRoute/pull/13263)) +- **fix(config):** Persist deletions of built-in background-degradation entries — when a stored settings record exists its `degradationMap` is now authoritative instead of being merged under the defaults, so an entry the user removed in the dashboard no longer reappears on the next apply or restart ([#12424](https://github.com/diegosouzapw/OmniRoute/issues/12424)) ([#12647](https://github.com/diegosouzapw/OmniRoute/pull/12647)) — thanks @pacocartones +- **fix(gamification):** action-count badge milestones (First Token, Token Consumer, Token Machine, Token Whale, and the token-sharing tier) are now backed by a durable `xp_action_counts` counter incremented in `addXp()`, instead of a live `COUNT(*)` over `xp_audit_log`. The audit log is pruned by `retention.xpAuditLog` (default 30 days), so on a default install those "lifetime" milestones were really "actions in the last 30 days" and unlocked badges could stop unlocking once old rows aged out. `getActionCount()` and `checkActionCountBadges()` now read the same durable source, and a migration backfills existing totals from the surviving audit rows ([#12546](https://github.com/diegosouzapw/OmniRoute/issues/12546)) ([#12651](https://github.com/diegosouzapw/OmniRoute/pull/12651)) — thanks @pacocartones +- **fix(images):** `/v1/images/edits` now iterates a combo's targets the same way `/v1/images/generations` does (#9239) instead of flattening a bare combo to its first target. A combo whose first target is not edit-capable — or lacks credentials — now falls through to a later edit-capable target rather than hard-erroring, and missing credentials are skipped (not a hard `401`) to match the generations path. The per-target skip/terminal classification is extracted into a shared `runImageComboTargets` loop, so generations behavior is unchanged ([#12547](https://github.com/diegosouzapw/OmniRoute/issues/12547)). ([#12653](https://github.com/diegosouzapw/OmniRoute/pull/12653)) — thanks @pacocartones +- fix(sse): add first-byte watchdog to the TLS-fingerprint transport so a stalled wreq body falls back instead of hanging for minutes (#12656) ([#13272](https://github.com/diegosouzapw/OmniRoute/pull/13272)) +- fix(sse): exempt tiny-budget reasoning probes from combo quality failure and surface persisted-cooldown skips in ALL_TARGETS_SKIPPED diagnostics (#12659) ([#13269](https://github.com/diegosouzapw/OmniRoute/pull/13269)) +- fix(models): declare the real ~1M contextLength for OpenCode Zen's Muse Spark 1.2 models instead of falling back to the 200000 provider default (#12681) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(oauth): align codebuddy-cn OAuth User-Agent with the chat/usage CLI version to avoid WAF false positives (#12702) ([#13264](https://github.com/diegosouzapw/OmniRoute/pull/13264)) +- fix(dashboard): surface an authentication-required banner instead of silently blanking database settings for a guest session (#12709) ([#13270](https://github.com/diegosouzapw/OmniRoute/pull/13270)) +- **fix(resilience):** stop unbounded queue that hangs 6min until Aborted — gate, provider slot, and Bottleneck queue now share a per-connection `maxWaitMs` budget; `fail-closed` on exhaust (503); execution backstop `executionMaxWaitMs` overridable per connection with upstream clamp ([#12715](https://github.com/diegosouzapw/OmniRoute/pull/12715)) — thanks @maxmad64bis (with thanks to @Tushar49 for surfacing the slow-provider need in #12635) +- **fix(auto-combo):** every mode pack now carries `quality` and `reliability` weights (`quality` 0.02, 0.03 in `quality-first`; `reliability` 0.03, 0.04 in `reliability-first`), so selecting a pack no longer silences either signal; `DEFAULT_WEIGHTS` is unchanged ([#12731](https://github.com/diegosouzapw/OmniRoute/pull/12731)) — thanks @maxmad64bis +- **fix(sse):** the provider execution pipeline keeps the upstream error `code`/`type` again — both were lost when the non-streaming leg was extracted, so a config-class `422` (Antigravity missing project) degraded into a generic account cooldown instead of reaching the gate that recognises it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(db):** a legacy `call_logs` table no longer aborts startup — the provider-stats index is created after the column healing runs, not before it, so an install predating `request_type` opens instead of failing with `no such column` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(catalog):** a malformed row in the operator-writable custom-models blob no longer kills every `auto/*` pool ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(tests):** realign the guards that the same day's merges left asserting the old behavior — the provider-count total after GreenPT and EURouter, the GLM stream buffer slot that `#12925` turned into a declared parameter, and the injection scan bound that `#13104` reshaped from a head-only window into head-plus-tail ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- fix(cache): fold tool_choice/tools/response_format into the semantic cache signature so a cached tool_calls response can no longer be replayed for a request whose tool policy forbids it (#12734) ([#13267](https://github.com/diegosouzapw/OmniRoute/pull/13267)) +- **fix(models):** stop treating provider-supplied `isFree:true`/`:free`/`0/0` flags as trusted unless the provider has a documented free tier — only the shipped free-tier catalog decides otherwise; fetched `isFree:true` trusted only for a free-tier provider, custom `isFree:true` via a trusted path ([#12744](https://github.com/diegosouzapw/OmniRoute/pull/12744)) — thanks @maxmad64bis +- fix(memory): authenticate the internal /v1/rerank loopback call so memory reranking no longer silently degrades to unranked order when REQUIRE_API_KEY=true (#12745) ([#13268](https://github.com/diegosouzapw/OmniRoute/pull/13268)) +- fix(sse): parse Ollama Cloud's current usage markup (`$X of $Y used` aria-label, nested width style) (#12749) ([#13260](https://github.com/diegosouzapw/OmniRoute/pull/13260)) +- fix(cli): setup-opencode no longer sends an active context's management token to `/v1/models` when `--api-key`/`OMNIROUTE_API_KEY` is supplied — an explicit flag or the env var now always outranks the context's token, and the flag itself is no longer swallowed by the parent program's global `--api-key` option (#12783) ([#13246](https://github.com/diegosouzapw/OmniRoute/pull/13246)) +- fix(sse): register Arcee AI in the executor provider registry so requests reach api.arcee.ai instead of silently falling back to OpenAI (#12784) ([#13277](https://github.com/diegosouzapw/OmniRoute/pull/13277)) +- **fix(quota):** share one upstream quota read when identical saturation checks arrive at the same time, so traffic bursts don't multiply provider API calls ([#12787](https://github.com/diegosouzapw/OmniRoute/pull/12787)) — thanks @maxmad64bis +- **fix(db):** ignore expired or invalid rate-limit cooldown writes so a stale timestamp can't lock a connection that should be usable — clearing still works as before ([#12788](https://github.com/diegosouzapw/OmniRoute/pull/12788)) — thanks @maxmad64bis +- **fix(providers):** single source for provider order with `xao` ranking alongside `xai-oauth` ([#12790](https://github.com/diegosouzapw/OmniRoute/pull/12790)) — thanks @maxmad64bis +- **fix(routing):** off-table models check the free-model catalog before inheriting premium prices, latency bootstraps from the observed pool median, async tiers read live database pricing with a 90-day freshness gate, and the tier cache invalidates on every pricing write ([#12792](https://github.com/diegosouzapw/OmniRoute/pull/12792)) — thanks @maxmad64bis +- **fix(auto-combo):** snapshot scoring uses the observed per-connection account tier and per-model quality instead of neutral constants, and circuit-open providers rank lower at build time ([#12794](https://github.com/diegosouzapw/OmniRoute/pull/12794)) — thanks @maxmad64bis +- **fix(pool):** empty `auto/*` pools now say why they are empty, and the models listing uses the same paid check as routing ([#12795](https://github.com/diegosouzapw/OmniRoute/pull/12795)) — thanks @maxmad64bis +- fix(routing): recognize CLIProxyAPI's 'unknown provider for model' 400 as fallback-worthy (#12800) ([#13284](https://github.com/diegosouzapw/OmniRoute/pull/13284)) +- **fix(sse):** translate-mode streams now emit the estimated token counts as a trailing usage-only chunk before `[DONE]` when the upstream stays silent, so metered chat clients see totals instead of nothing ([#12828](https://github.com/diegosouzapw/OmniRoute/pull/12828)) — thanks @maxmad64bis +- **fix(db):** provider stats stay truthful on empty and legacy databases: fallback counts default to `0` instead of `null`, latency averages read `null` (not `0`) when no durations were recorded, failures logged before the error-type column existed group under `pre_migration` instead of `unclassified`, and per-provider queries use two new composite indexes ([#12832](https://github.com/diegosouzapw/OmniRoute/pull/12832)) — thanks @maxmad64bis +- fix(nvidia): fail open when a synced model catalog goes stale instead of gating forever (#12849) ([#13248](https://github.com/diegosouzapw/OmniRoute/pull/13248)) +- **fix(db):** WAL maintenance lives in its own module: a `TRUNCATE` checkpoint that hits a busy database now warns with its streak and retries once via `PASSIVE` instead of logging success, and closing-time checkpoints no longer report success on builds without a database file, with the busy totals visible in the authenticated monitoring health payload ([#12853](https://github.com/diegosouzapw/OmniRoute/pull/12853)) — thanks @maxmad64bis +- **fix(health):** quota with no snapshots reads empty instead of a contradicting 0% ([#12857](https://github.com/diegosouzapw/OmniRoute/pull/12857)) — thanks @maxmad64bis +- fix(a2a): accept the dashboard's own session cookie on /a2a so "Run message/send" no longer fails with "Unauthorized: missing or invalid API key" (#12888) ([#13271](https://github.com/diegosouzapw/OmniRoute/pull/13271)) +- **fix(logs):** the Logs grid's in-memory filter pass no longer discards rows the SQL query already matched — selecting an API key from the dropdown (which sends the key's id) returns its calls again, the Combo tab shows every combo instead of only those whose name contains a "1", and the model filter and search cover the same columns as the query ([#12896](https://github.com/diegosouzapw/OmniRoute/pull/12896)) — fixes [#12873](https://github.com/diegosouzapw/OmniRoute/issues/12873) — thanks @ntdatt812 +- **fix(a2a):** `/api/a2a/status` now builds the agent card from the request that asked for it, so a gateway reached at a non-localhost host no longer advertises `http://localhost:20128` as its A2A URL ([#12918](https://github.com/diegosouzapw/OmniRoute/pull/12918)). — thanks @ntdatt812 +- **fix(compression):** progressive aging now appends its `[COMPRESSED:aging:…]` annotation after a turn's `tool_result` blocks instead of in front of them, so Anthropic no longer rejects aged conversations with "`tool_use` ids were found without `tool_result` blocks immediately after" ([#12920](https://github.com/diegosouzapw/OmniRoute/pull/12920)). — thanks @ntdatt812 +- **fix(bedrock):** model import now resolves context limits for every vendor prefix instead of only `anthropic.*`, so `global.openai.gpt-5.6-*` no longer imports with a null `inputTokenLimit` and gets rejected pre-flight at the 200k default ([#12921](https://github.com/diegosouzapw/OmniRoute/pull/12921)). — thanks @ntdatt812 +- **fix(stream):** the 64 KB stream buffer GLM asks for is honoured instead of dropped, and the type error it caused no longer fails the API Route Typecheck gate on every open PR ([#12925](https://github.com/diegosouzapw/OmniRoute/pull/12925)) — thanks @ntdatt812 +- **fix(guardrails):** mask PII inside a `tool_result`'s nested content array, which the masker walked past while redacting its sibling block ([#12930](https://github.com/diegosouzapw/OmniRoute/pull/12930)) — thanks @ntdatt812 +- **fix(dashboard):** the Radar catalog table no longer leaves absent data unexplained — empty limits, unknown context windows, and unreported capabilities each explain themselves on hover, and a new check keeps it that way ([#12937](https://github.com/diegosouzapw/OmniRoute/pull/12937)) — thanks @maxmad64bis +- **fix(sse):** geo-blocked opencode requests rotate to the next account proxy instead of failing, so one refused egress no longer aborts the whole chain ([#12941](https://github.com/diegosouzapw/OmniRoute/pull/12941)) — thanks @maxmad64bis +- **fix(codex):** dashboard "clear cooldown" and the CAS recovery path now drop nested `codexScopeRateLimitedUntil` maps in the same write that nulls `rate_limited_until`, and fresh quota snapshots with headroom lift fallback-sourced scope cooldowns parked by quota preflight ([#12817](https://github.com/diegosouzapw/OmniRoute/issues/12817), [#12860](https://github.com/diegosouzapw/OmniRoute/issues/12860), [#12951](https://github.com/diegosouzapw/OmniRoute/pull/12951)) The dashboard reset-credit button (`consumeCodexResetCredit`) now exercises that same snapshot path after a successful redeem, so a filled quota bar is enough to unpark a leftover fallback Codex child without another manual clear. — thanks @HouMinXi +- **fix(db):** resolve `sql-wasm.wasm` across global npm install and hoisted layouts, ensuring OmniRoute can boot cleanly on Node 24 when native `better-sqlite3` is uncompiled. ([#13035](https://github.com/diegosouzapw/OmniRoute/pull/13035)) — thanks @HouMinXi +- **fix(api):** `DELETE /v1/batches/delete-completed` now sweeps only the calling API key's own completed batches (batches with no owner stay out of a key-scoped sweep on purpose), with an explicit instance-wide mode reserved for authenticated dashboard sessions, a 401 for a presented key that is unknown, revoked, deactivated, banned or expired (never falling through to the session branch), audit logging of every sweep, a sanitized 500 on failure, an owner-scoped file half (a key-scoped sweep never nulls a file another tenant owns) and an atomic sweep — chunked in 200-batch transactions in instance mode — so a mid-way error never leaves a batch pointing at a nulled file (GHSA-wvxc-jp3v-5mg5) ([#12969](https://github.com/diegosouzapw/OmniRoute/pull/12969)) +- **fix(combo):** quota-weighted routing stops drawing on an out-of-credit connection — a 402 now invalidates the stored quota snapshot instead of leaving its stale remaining percentage in place, and a snapshot older than 10 minutes no longer counts as confident headroom for the primary pool ([#12972](https://github.com/diegosouzapw/OmniRoute/pull/12972)) ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- **fix(vision):** Custom Models with "Vision capable" checked no longer have image requests swapped to `glm/glm-4.6v` when the client sends the advertised alias (`vllm/path/...`) or the bare path-shaped id — Vision Bridge now matches the stored override for all three id forms ([#12758](https://github.com/diegosouzapw/OmniRoute/issues/12758)) ([#12974](https://github.com/diegosouzapw/OmniRoute/pull/12974)) — thanks @HouMinXi +- **fix(sse):** transient opencode upstream failures rotate to the next account proxy instead of failing, so one flapping egress no longer aborts the whole chain ([#12975](https://github.com/diegosouzapw/OmniRoute/pull/12975)) — thanks @maxmad64bis +- **fix(azure):** Deployments from GPT-6 onward now send `max_completion_tokens` instead of `max_tokens`, which Azure rejects with HTTP 400. The rule matched a literal `gpt-5`, so each new generation arrived broken; it now matches the generation range, while `gpt-35-turbo` still keeps `max_tokens`. ([#13007](https://github.com/diegosouzapw/OmniRoute/pull/13007)) — thanks @ntdatt812 +- **fix(db):** add an opt-in self-restart circuit for sustained critical memory pressure, gate post-cleanup VACUUM behind a minimum freed-rows threshold, and checkpoint the SQLite WAL every 5 minutes with a size guard that escalates to TRUNCATE, so a growing WAL can no longer stall the event loop into a full outage. ([#13011](https://github.com/diegosouzapw/OmniRoute/pull/13011)) — thanks @HouMinXi +- **fix(auth):** an explicit connection pin may probe a quota-disabled row once and re-enable it on success ([#12874](https://github.com/diegosouzapw/OmniRoute/issues/12874)) ([#13017](https://github.com/diegosouzapw/OmniRoute/pull/13017)) — thanks @HouMinXi +- **fix(api):** thread `X-OmniRoute-Fallback-Attempts` through combo chat completions so streaming and non-streaming responses report how many prior legs were attempted ([#13038](https://github.com/diegosouzapw/OmniRoute/pull/13038)) — thanks @HouMinXi +- Restore provider failure classification and credential refresh on non-streaming requests: classify non-2xx failures to lock models on per-model quota exhaustion, update connection rate limits from headers and body, and pass credential refresh handlers to pipeline execution so 401 tokens can be refreshed and retried (#13043). ([#13069](https://github.com/diegosouzapw/OmniRoute/pull/13069)) — thanks @HouMinXi +- **fix(responses):** wrap forced-non-streaming web_search fallback JSON as Responses SSE so Codex still sees `response.completed` ([#13050](https://github.com/diegosouzapw/OmniRoute/pull/13050)) — thanks @HouMinXi +- **fix(antigravity):** Preserve upstream thought-token usage in normalized completion and reasoning token counts ([#13055](https://github.com/diegosouzapw/OmniRoute/pull/13055)) — thanks @pacocartones +- Fix a concurrency-slot leak in the DeepSeek PoW solver: a worker that failed to spawn (for example a missing worker script) never released its slot, so `MAX_CONCURRENT_WORKERS` failures disabled the solver until restart. ([#13097](https://github.com/diegosouzapw/OmniRoute/pull/13097)) — thanks @anhtahaylove +- **fix(acp):** bound the ACP session output buffers — `stdoutBuffer` and `stderrBuffer` now cap at 1 MiB keeping the most recent output behind a visible `[...output truncated...]` marker, and `stderrBuffer` is reset per prompt instead of accumulating for the lifetime of the session. ([#13100](https://github.com/diegosouzapw/OmniRoute/pull/13100)) — thanks @anhtahaylove +- **fix(acp):** release the `stdout`/`exit` listeners and the idle timer that a `sendPrompt` timeout used to leave attached to the `acpManager` singleton, and drop sessions that exited on their own from the session map instead of keeping them forever. ([#13096](https://github.com/diegosouzapw/OmniRoute/pull/13096)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection and PII scanners now read the text a `tool_result` block carries on `content` (string or nested block list), in messages and in system blocks, so tool output is judged by the same rules as user text ([#13101](https://github.com/diegosouzapw/OmniRoute/pull/13101)) — thanks @ntdatt812 +- **fix(gamification):** close the badge notification SSE stream when the request signal is already aborted before the stream starts — a client that disconnects while the route is still awaiting auth used to leave both the 2s unlock poll and the 15s heartbeat running for the lifetime of the process. ([#13106](https://github.com/diegosouzapw/OmniRoute/pull/13106)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection scan now spends its 16 KB budget on both ends of the request instead of the first 16 KB only, so `system`, `instructions`, `query`, `documents` and the newest turns are no longer hidden behind one long message ([#13104](https://github.com/diegosouzapw/OmniRoute/pull/13104)) — thanks @ntdatt812 +- **fix(dashboard):** expose the Volcano Ark console cookie on quota scraping and unwrap connect-error objects so the dashboard shows the upstream message ([#13107](https://github.com/diegosouzapw/OmniRoute/pull/13107)) — thanks @HouMinXi +- **fix(db):** release the `beforeExit`/`SIGINT`/`SIGTERM` handlers when a `node:sqlite` adapter closes, so a closed adapter and its database handle are no longer pinned to `process` for the lifetime of the run — the same treatment #7494 gave the sql.js adapter. ([#13109](https://github.com/diegosouzapw/OmniRoute/pull/13109)) — thanks @anhtahaylove +- **fix(translator):** `contentSchema` and `unevaluatedItems` are now treated as subschema positions by the tool-schema sanitizer, so a truncation placeholder in either is replaced with a permissive schema instead of being forwarded as a string ([#13110](https://github.com/diegosouzapw/OmniRoute/pull/13110)) — thanks @ntdatt812 +- **fix(cli-helper):** clear the `createLogStream` timeout on the abort path — `stop()` aborts the in-flight fetch and returned through the `signal.aborted` branch, which skipped `clearTimeout` and left an armed timer per stopped stream. The stream reader is now also cancelled when the read loop exits early. ([#13114](https://github.com/diegosouzapw/OmniRoute/pull/13114)) — thanks @anhtahaylove +- **fix(docs):** drop TLS-impersonation, MITM-decrypt, supply-chain attestation, and XOR-mask writeups from the public `/docs` catalog and Docker image. Files stay in git for engineers; operators who need them open the repo, not the website. ([#13136](https://github.com/diegosouzapw/OmniRoute/pull/13136)) — thanks @HouMinXi +- **fix(combo):** parse numeric-epoch `rate_limited_until` in the combo cooldown read path ([#13141](https://github.com/diegosouzapw/OmniRoute/pull/13141)) — thanks @maxmad64bis +- **fix(opencode):** both OpenCode plugins now reject a gateway address typed without `http://` at configuration time, instead of publishing every model with an api url no client can call, and the v2 plugin no longer publishes a model card whose api url is blank or relative ([#13142](https://github.com/diegosouzapw/OmniRoute/pull/13142)) — thanks @maxmad64bis +- **fix(providers):** lock opencode model on upstream 400 model-unavailable ([#13146](https://github.com/diegosouzapw/OmniRoute/pull/13146)) — thanks @maxmad64bis +- **fix(logging):** keep the provider exchange rather than the raw client bodies when a call log exceeds its size budget, and show that recovered payload in the request-detail panel instead of replacing it with the stored response body ([#13147](https://github.com/diegosouzapw/OmniRoute/pull/13147)) — thanks @maxmad64bis +- **fix(traffic-inspector):** the WebSocket route no longer leaks a traffic-buffer subscriber and a 30s ping timer when the client socket is already closed at handler time — listeners are attached before any resource is acquired, a destroyed socket bails out early, and the ping interval stops on a dead socket where `write()` never throws ([#13155](https://github.com/diegosouzapw/OmniRoute/pull/13155)) — thanks @anhtahaylove +- **fix(telegram):** bound the per-user API key cache in the Telegram chat proxy so a burst of distinct chat ids can no longer grow the process heap without limit ([#13165](https://github.com/diegosouzapw/OmniRoute/issues/13165)) ([#13166](https://github.com/diegosouzapw/OmniRoute/pull/13166)) — thanks @anhtahaylove +- **fix(stream):** cancel the upstream response body when the JSON-to-SSE sniff unwinds on a body timeout, so a stalled upstream no longer pins the connection ([#13169](https://github.com/diegosouzapw/OmniRoute/issues/13169)) ([#13171](https://github.com/diegosouzapw/OmniRoute/pull/13171)) — thanks @anhtahaylove +- **fix(telegram):** authenticate webhook deliveries with Telegram's `secret_token` so an unauthenticated caller can no longer mint API keys or spend upstream quota ([#13172](https://github.com/diegosouzapw/OmniRoute/issues/13172)) ([#13175](https://github.com/diegosouzapw/OmniRoute/pull/13175)) — thanks @anhtahaylove +- **fix(models):** give discoverable Gemini 3.8 Flash ids their own 65536 output spec so Antigravity no longer clamps them to 16384 ([#13195](https://github.com/diegosouzapw/OmniRoute/pull/13195)) — thanks @HouMinXi +- **fix(dashboard):** leftover catalog-removed provider rows (gemini-cli) can be listed and purged from the providers page ([#13067](https://github.com/diegosouzapw/OmniRoute/issues/13067)) ([#13197](https://github.com/diegosouzapw/OmniRoute/pull/13197)) — thanks @HouMinXi +- **fix(auth):** a dashboard session now requires the `authenticated: true` claim that login, OIDC and the session refresh already emit — a JWT merely signed with `JWT_SECRET` (for example the Cursor CLI passthrough token, which any API-key holder can obtain) no longer verifies as the `auth_token` cookie on any route, the WebSocket handshake or the live server; existing sessions keep working ([#13298](https://github.com/diegosouzapw/OmniRoute/issues/13298)) ([#13375](https://github.com/diegosouzapw/OmniRoute/pull/13375)) +- **fix(api):** `DELETE /v1/batches/delete-completed` now applies the caller's API-key policy (endpoint allowlist, schedule, usage cap, rate limit) like every other `/v1` route, commits the key-scoped sweep per 200-batch chunk instead of holding one write lock for the whole sweep, refuses to loop without progress, and rejects a scope that names both a key and `allTenants` ([#13684](https://github.com/diegosouzapw/OmniRoute/pull/13684)) +- Restore the API-route typecheck gate: the non-streaming leg result lost its discriminated-union narrowing after the server-owned tool loop reassignment, producing 13 new TS2339 diagnostics in `chatCore.ts`. ([#12963](https://github.com/diegosouzapw/OmniRoute/pull/12963)) +- Restore four non-streaming behaviours the server-owned tool loop refactor dropped: client aborts map to 499 with the fixed `Request aborted` message, an aborted request no longer logs a synthetic `clientResponse`, Claude prompt-cache telemetry is recorded again, and a body that cannot be canonicalized no longer throws when the tool loop is off. ([#12990](https://github.com/diegosouzapw/OmniRoute/pull/12990)) +- **fix(skills):** The CLI registry parser now reads positionals declared with `.addArgument()`, not only those written inline in `.command()`. `tunnel create [type]` was being published as `tunnel create`, so the agent-skills sync gate reported drift on every branch and regenerating would have deleted the argument. ([#13009](https://github.com/diegosouzapw/OmniRoute/pull/13009)) — thanks @ntdatt812 +- **fix(combos):** dashboard combo test uses a short prompt, serial probes, and a 60s timeout so reasoning models and rate-limited free pools do not fail the health check ([#13001](https://github.com/diegosouzapw/OmniRoute/pull/13001)) — thanks @HouMinXi +- fix(compression): terminate idle worker threads on eviction so long-running instances stop leaking OS threads and MessagePorts ([#13091](https://github.com/diegosouzapw/OmniRoute/pull/13091)) — thanks @anhtahaylove +- Redact Google API keys of any length in error bodies: the pattern required exactly 39 characters, so shorter or longer `AIza…` credentials passed through unredacted. ([#12964](https://github.com/diegosouzapw/OmniRoute/pull/12964)) +- **fix(grok-cli):** a 402 "Grok Build usage balance exhausted" parks that Grok login as out of credit (Grok Build CLI, grok.com cookie, and xAI OAuth share the weekly pool). Combo routing then tries the next login instead of locking the model for every account in the pool ([#13061](https://github.com/diegosouzapw/OmniRoute/pull/13061)) — thanks @HouMinXi +- fix(compression): spawn the LLMLingua worker with a file URL object so compression actually runs instead of silently failing open on Node ([#13093](https://github.com/diegosouzapw/OmniRoute/pull/13093)) — thanks @anhtahaylove +- **fix(test):** run the local `test` and `test:unit` scripts at concurrency 4 so a full-suite run no longer exhausts the machine's commit charge and kills unrelated processes ([#13187](https://github.com/diegosouzapw/OmniRoute/pull/13187)) — thanks @anhtahaylove +- **fix(monitoring):** `GET /api/monitoring/health` `credentialHealth` now includes a bounded `failedConnections` list (`connectionId`, `status`, sanitized `lastError`) when the probe-cache gauge `failed>0`, plus `source: probe-cache` and a cheap `staleDbNonOkCount` for sticky SQLite `test_status` on active rows. Documents that the live gauge is not `provider_connections.test_status`. ([#12876](https://github.com/diegosouzapw/OmniRoute/pull/12876)) — thanks @RaviTharuma +- **fix(resilience):** an openai-compatible multi-upstream gateway no longer marks the whole connection `credits_exhausted` when a single upstream (or the `/models` / representative-model health probe) returns 402. Quota failures stay model-scoped; true connection-wide auth failures are unchanged. Single-credential 402 key disable ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239)) is preserved. ([#12875](https://github.com/diegosouzapw/OmniRoute/pull/12875)) — thanks @RaviTharuma +- **fix(dashboard):** nine fixes on the Orchestration Canvas, all diagnosed in the Phase 2 reviews. A source that fails now keeps the timestamp of its FIRST failure instead of being re-stamped every poll — the stale line said "since the last poll" no matter how long the source had been down, and the churn also defeated the snapshot's stable identity (it serializes the source list), so the canvas re-rendered on every tick while anything was broken. A source that HAD data and only then started failing is flagged too: previously only a source with no node at all got the warning, so a source that went down mid-session kept a healthy-looking node forever. The rest are pointwise: clicking a filter chip cancels the pending search debounce (left armed, it fired ~300ms later and silently reverted the chip); the search input carries an accessible name; `?state=running, failed` parses like the unpadded form instead of dropping the padded value; the CSV toggle helper is defined once in `model/urlParams.ts` rather than twice; the agents tab tells "nothing running" apart from "the filter matched nothing", offering a clear-filters button instead of setup links that would be wrong advice there; edges stop emitting SMIL particles above 40 simultaneously active edges, keeping the colored stroke; and the drawer's error banner clears when a retried action succeeds. ([#12983](https://github.com/diegosouzapw/OmniRoute/pull/12983)) +- **fix(i18n):** the nine locales added with the EU-language batch (Greek, Estonian, Irish, Croatian, Lithuanian, Latvian, Maltese, Slovenian, Serbian) were missing the eleven Orchestration Canvas keys that Phase 3 introduced, so the compare-runs panel and the "no runs match these filters" empty state fell back to English in those languages (`deepMergeFallback` substitutes English for an absent key, so nothing rendered blank — it rendered untranslated). The coverage gate does not catch this: it enforces an 80% floor per locale, and eleven missing keys out of ~13,000 leaves coverage at 99.9%. Translated for real in each language, calibrated against the wording each file already uses for "run", "filter" and "skill". ([#13294](https://github.com/diegosouzapw/OmniRoute/pull/13294)) +- **fix(dashboard):** five follow-ups from the Phase 2 Orchestration Canvas review. The History drawer now shows the memory section for runs that already left the live TTL window: the persisted `memory_hits` event is parsed into `metadata.memoryHits` with the same defensive validation the drawer applies, and — because that event is observability rather than a state transition — it no longer leaks into the timeline, where it had been inheriting the task's state and rendering as a duplicate transition. The A2A memory recall runs against its own 1.5s deadline instead of inheriting the memory backend's 30s one; overshooting degrades exactly like any other recall failure (no hits, task proceeds), and the timer is cleared on both paths. Repeating a Conductor run carries its `requirements.cli`/`requirements.model` forward, so the new run is pinned to the same runner profile and model rather than drifting to whatever the fleet picks. A successful repeat from the Agents tab now focuses the run it created, instead of leaving the operator on the finished one. And the auth test for `POST /api/conductor/tasks` moved into the shared `ROUTES` array rather than restating the pattern. ([#12988](https://github.com/diegosouzapw/OmniRoute/pull/12988)) +- fix(plugins): stop leaking an exit listener per plugin hook timeout, which triggered MaxListenersExceededWarning on plugins that ignore SIGTERM ([#13092](https://github.com/diegosouzapw/OmniRoute/pull/13092)) — thanks @anhtahaylove +- **fix(validation):** Provider node edits no longer fail with a generic "Invalid request" when the optional daily-quota reset fields are left blank. The dashboard sends `dailyQuotaResetTimezone` and `dailyQuotaResetHour` as `null`, and only the hour accepted it. ([#13066](https://github.com/diegosouzapw/OmniRoute/issues/13066)) ([#13083](https://github.com/diegosouzapw/OmniRoute/pull/13083)) — thanks @ntdatt812 +- **fix(combo):** quota-aware expansion drops banned, inactive, missing, and wrong-provider connections before quota fetch or model dispatch; pins and allowlists stay selectors, not a bypass. Antigravity automatic exhaustion now requires a reported zero remaining, so a positive balance below 1% stays eligible. ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- Fix an intermittent failure in the quota-weighted routing test suite: scores are a function of `Date.now()`, so peers with identical quota scored microseconds apart never tied and swapped order. ([#12946](https://github.com/diegosouzapw/OmniRoute/pull/12946)) +- fix(volcengine): resolve quota window mapping gaps, safe multi-connection console binding, and response cookie sanitization ([#12950](https://github.com/diegosouzapw/OmniRoute/pull/12950)) — thanks @HouMinXi +- **fix(test):** resolve the WebDAV handler path with `fileURLToPath` so the suite's 37 WebDAV tests run on Windows instead of failing with a doubled `C:\C:\` drive prefix ([#13196](https://github.com/diegosouzapw/OmniRoute/pull/13196)) — thanks @anhtahaylove +- **fix(ui):** fall back from invalid AnySearch icon alias (direct commit `9587e07b69`) +- **fix(sse):** stop rebuilding a truncated summary from the collector's cap-dropped event array ([#12718](https://github.com/diegosouzapw/OmniRoute/pull/12718)) — thanks @hartmark +- **fix(conversations):** show a pending spinner for unresolved tool nodes instead of "(empty)" ([#12727](https://github.com/diegosouzapw/OmniRoute/pull/12727)) — thanks @hartmark +- **fix(streaming):** fail fast when an upstream stream produces only lifecycle/heartbeat events, never real content ([#12741](https://github.com/diegosouzapw/OmniRoute/pull/12741)) — thanks @hartmark +- **fix(responses-continuation):** bridge the write-in-flight window with an in-memory pending store ([#12854](https://github.com/diegosouzapw/OmniRoute/pull/12854)) — thanks @hartmark +- **fix(translator):** drop or map agent_message on Chat Completions fallback ([#12880](https://github.com/diegosouzapw/OmniRoute/pull/12880)) — thanks @RaviTharuma +- **fix(api):** page and stream-complete GET /v1/models for large catalogs ([#12882](https://github.com/diegosouzapw/OmniRoute/pull/12882)) — thanks @RaviTharuma +- **fix(quota):** align AUTH window labels with usage API and clarify 503 ([#12884](https://github.com/diegosouzapw/OmniRoute/pull/12884)) — thanks @RaviTharuma +- **fix(ci):** clear two base-reds on release/v3.8.51 — mutation-coverage gate + image-only-model guard ([#12945](https://github.com/diegosouzapw/OmniRoute/pull/12945)) +- **fix(api):** scope batch bulk-delete to the calling API key ([#13211](https://github.com/diegosouzapw/OmniRoute/pull/13211)) +- **fix(tests):** align model-sync log assertion to shipped path redaction (base-red) ([#13227](https://github.com/diegosouzapw/OmniRoute/pull/13227)) +- **fix(test):** make the ToS heading guard actually require the parentheses ([#13228](https://github.com/diegosouzapw/OmniRoute/pull/13228)) +- **fix(quality):** register 4 drifted covering tests in stryker tap.testFiles ([#13229](https://github.com/diegosouzapw/OmniRoute/pull/13229)) +- **fix(logging):** mask *-api-key headers in request-log pipeline (#13273) ([#13401](https://github.com/diegosouzapw/OmniRoute/pull/13401)) — thanks @KooshaPari +- **fix(tests):** correct stale Jina catalog prefix in custom-models test ([#13403](https://github.com/diegosouzapw/OmniRoute/pull/13403)) — thanks @KooshaPari +- **fix(backend):** prune old backups after health-check-repair snapshots (#13308) ([#13404](https://github.com/diegosouzapw/OmniRoute/pull/13404)) — thanks @KooshaPari +- **fix(backend):** guard memory_fts_au trigger with WHEN clause to stop FTS bloat (#13326) ([#13405](https://github.com/diegosouzapw/OmniRoute/pull/13405)) — thanks @KooshaPari +- **fix(free-tiers):** remove obsolete Together $25 signup credit (#12526) ([#13407](https://github.com/diegosouzapw/OmniRoute/pull/13407)) — thanks @KooshaPari +- **fix:** skip non-chat generation models in Test all models ([#13409](https://github.com/diegosouzapw/OmniRoute/pull/13409)) — thanks @KooshaPari +- **fix(providers):** de-duplicate agy and antigravity model catalogs ([#13410](https://github.com/diegosouzapw/OmniRoute/pull/13410)) — thanks @KooshaPari +- **fix(dashboard):** send provider in body for mini-playground web search ([#13411](https://github.com/diegosouzapw/OmniRoute/pull/13411)) — thanks @KooshaPari +- **fix(resilience):** add CJK quota-exhaustion patterns to 429 classifier ([#13412](https://github.com/diegosouzapw/OmniRoute/pull/13412)) — thanks @KooshaPari +- **fix(backend):** eval runner: failed calls no longer score as passed + dotAll regex ([#13413](https://github.com/diegosouzapw/OmniRoute/pull/13413)) — thanks @KooshaPari +- **fix(backend):** eval runner opts out of output-style and memory injection ([#13414](https://github.com/diegosouzapw/OmniRoute/pull/13414)) — thanks @KooshaPari +- **fix:** chain serial quarantine step in npm test script (fixes #13286) ([#13418](https://github.com/diegosouzapw/OmniRoute/pull/13418)) — thanks @KooshaPari +- **fix(antigravity):** derive stable session ID from credentials for prompt-cache reuse (#13305) ([#13424](https://github.com/diegosouzapw/OmniRoute/pull/13424)) — thanks @KooshaPari +- **fix(memory):** merge memory into top-level system field instead of unshifting at messages[0] (#13425) ([#13427](https://github.com/diegosouzapw/OmniRoute/pull/13427)) — thanks @KooshaPari +- **fix(compression):** preserve instruction blocks from lossy engines ([#13523](https://github.com/diegosouzapw/OmniRoute/pull/13523)) — thanks @KooshaPari +- **fix(compression):** preserve polarity words and newlines in ultra heuristic ([#13524](https://github.com/diegosouzapw/OmniRoute/pull/13524)) — thanks @KooshaPari +- **fix(compression):** run user preservePatterns before built-in patterns (#13457) ([#13525](https://github.com/diegosouzapw/OmniRoute/pull/13525)) — thanks @KooshaPari +- **fix(reasoning):** expand deepseek-v4 regex to match v4.1+ and add command-code provider (#13430) ([#13528](https://github.com/diegosouzapw/OmniRoute/pull/13528)) — thanks @KooshaPari +- **fix(providers):** add alwaysReasons metadata for reka-flash-3 ([#13531](https://github.com/diegosouzapw/OmniRoute/pull/13531)) — thanks @KooshaPari +- **fix(playground):** allow Compare columns to scroll independently ([#13532](https://github.com/diegosouzapw/OmniRoute/pull/13532)) — thanks @KooshaPari +- **fix(security):** empty whitelist allows through to prevent admin lockout ([#13534](https://github.com/diegosouzapw/OmniRoute/pull/13534)) — thanks @KooshaPari +- **fix(tests):** add DISABLE_IOREG_STRATEGY env var for macOS test compatibility (fixes #13467) ([#13539](https://github.com/diegosouzapw/OmniRoute/pull/13539)) — thanks @KooshaPari +- **fix(zed-hosted):** map developer role and Gemini enums for Zed proxy ([#13543](https://github.com/diegosouzapw/OmniRoute/pull/13543)) — thanks @KooshaPari +- **fix(analytics):** normalize provider aliases to canonical id in usage_history ([#13545](https://github.com/diegosouzapw/OmniRoute/pull/13545)) — thanks @KooshaPari +- **fix(combo):** use traceId for call log id to prevent UNIQUE constraint failure ([#13546](https://github.com/diegosouzapw/OmniRoute/pull/13546)) — thanks @KooshaPari +- **fix(auth):** check model lockout before returning synthetic noauth connection ([#13547](https://github.com/diegosouzapw/OmniRoute/pull/13547)) — thanks @KooshaPari +- **fix(guardrails):** skip credential redaction for base64 image data URLs ([#13550](https://github.com/diegosouzapw/OmniRoute/pull/13550)) — thanks @KooshaPari +- **fix(resilience):** increase requestQueue.maxWaitMs default from 15s to 30s ([#13553](https://github.com/diegosouzapw/OmniRoute/pull/13553)) — thanks @KooshaPari +- **fix(docs):** document OMNIROUTE_READY_TIMEOUT_MS and allowlist the test-only DISABLE_IOREG_STRATEGY ([#13692](https://github.com/diegosouzapw/OmniRoute/pull/13692)) + ### 📝 Maintenance ---- +- **docs(readme):** reconcile live v3.8.50 provider, free-tier, CLI, routing, test, community, sponsor, acknowledgment, and SVG metrics with their audited source denominators, including a deduplicated OmniRoute-in-Action snapshot and distinct contributor rankings for merged pull requests, GitHub-attributed commits, and Git history ([#11356](https://github.com/diegosouzapw/OmniRoute/pull/11356)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **docs:** sync the canonical provider count 354 → 356 across `README.md`, `AGENTS.md`, `llm.txt` (+ 42 i18n mirrors), the four README SVG diagrams, `docs/reference/PROVIDER_REFERENCE.md` (regenerated) and the `package.json` description after Opper (#11629) and 1min.ai (#11631) boarded the catalog — closes the `check:docs-counts-sync` strict drifts that kept `release/v3.8.51` red ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11673](https://github.com/diegosouzapw/OmniRoute/pull/11673)) — thanks @jonlwheat2-gif +- **test(providers):** update count-derived assertions after the v3.8.51 provider additions — `APIKEY_PROVIDERS` 233 → 235 (Opper #11629 + 1min.ai #11631), reserved-prefix REGISTRY walk 395 → 398, `WEB_FETCH_PROVIDERS` now includes `nimble-search` (#11620), and the provider translate-path golden snapshot regenerated ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11674](https://github.com/diegosouzapw/OmniRoute/pull/11674)) — thanks @jonlwheat2-gif +- **test(sse):** bump the hard-lease connection-query inventory for `src/lib/tokenHealthCheck.ts` to 2 — the verify-only web-cookie sweep added by #11495 split the single `getProviderConnections` call into oauth + cookie variants, which the frozen inventory had not tracked ([#11495](https://github.com/diegosouzapw/OmniRoute/pull/11495)). ([#11677](https://github.com/diegosouzapw/OmniRoute/pull/11677)) — thanks @jonlwheat2-gif / @oyi77 +- **chore(release):** clear the `release/v3.8.51` base-red gates — docs migration count (+38 i18n mirrors), stryker `tap.testFiles`, lockfile registry host, stale ESLint suppressions, the 7 lint regressions they were hiding, the stale `APIKEY_PROVIDERS` partition count (231 → 233), the `glm-5.3-max` tier missing from the GLM test table, a dead `modelType === "chat"` comparison in the models catalog, and the `resolveLiveWsUrl` / `sanitizeLiveWsPort` helpers from #11388 that a later merge dropped from `wsPath.ts` (dashboard live WebSocket URL resolution) ([#11502](https://github.com/diegosouzapw/OmniRoute/pull/11502)) — thanks @MumuTW +- **chore(free-models):** the free-tier regime table now declares which totals figure each regime feeds and whether it may take the no-auth shortcut, so the sets that used to repeat those answers by hand are derived from it — a new `freeType` no longer compiles until it has answered every question, instead of silently contributing to no total ([#11537](https://github.com/diegosouzapw/OmniRoute/pull/11537)) — thanks @maxmad64bis +- **docs(routing):** LKGP strategy copy now states provider-level stickiness and the explicit rules fallback instead of promising per-target pinning ([#11555](https://github.com/diegosouzapw/OmniRoute/pull/11555)) — thanks @maxmad64bis +- **test(chatcore):** pin the response tool-name alias resolution against the ordering hazard that broke every Gemini/Antigravity MCP tool call in v3.8.49. `extractRequestToolIdentityMap` deletes `translatedBody._toolNameMap`, so the later read is always undefined and the ledger survives only through the `requestToolIdentityMap` fallback — removing that fallback previously left the entire tool-name suite green. The resolution moves into `resolveResponseToolNameMap()` next to the map it depends on, with a regression guard that fails without the recovery. No behaviour change. ([#11575](https://github.com/diegosouzapw/OmniRoute/pull/11575)) — thanks @amartinawi +- **docs(env):** document `OMNIROUTE_CORPUS_CACHE_SIZE` in `.env.example` and `ENVIRONMENT.md`, so the env/docs contract check stops failing Docs Gates on every branch ([#11576](https://github.com/diegosouzapw/OmniRoute/pull/11576)) — thanks @ntdatt812 +- **chore(stryker):** register `route-guard-tunnel-processes-local-only` and `universal-quota-aware-routing` in `tap.testFiles`, so their mutant kills count for the three modules they cover ([#11577](https://github.com/diegosouzapw/OmniRoute/pull/11577)) — thanks @ntdatt812 +- **test(authz):** pin `LOCAL_ONLY_API_GET_EXEMPTIONS` by exact membership instead of by entry count, so the guard names the offending path and also catches a substitution ([#11580](https://github.com/diegosouzapw/OmniRoute/pull/11580)) — thanks @ntdatt812 +- **test(build):** pin `resolveNextBuildBundlerFlag` to the `OMNIROUTE_USE_TURBOPACK` contract it implements, across runtimes, instead of asserting an unimplemented Bun override ([#11583](https://github.com/diegosouzapw/OmniRoute/pull/11583)) — thanks @ntdatt812 +- **test(claude-web):** the Claude Web transport test's barrel anchors no longer pin the exact construction spelling. #11421 made the executor map lazy, so `/"claude-web": new ClaudeWebExecutor\(\)/` stopped matching and the test went red on `release/v3.8.51` itself — a base-red gate every open PR inherited. The anchors now match the alias key and the class name and tolerate either spelling in between, and were checked to still fail when an alias is genuinely repointed at another executor ([#11585](https://github.com/diegosouzapw/OmniRoute/pull/11585)) — thanks @ntdat812 +- **test(models):** pin `qwen3.8-max` resolution per provider — two of the four catalogs now serve the bare id and no longer carry `-preview` ([#11590](https://github.com/diegosouzapw/OmniRoute/pull/11590)) — thanks @ntdatt812 +- **test(executors):** drop the stale 128-tool truncation assertion that contradicted the #11444 pass-through contract ([#11591](https://github.com/diegosouzapw/OmniRoute/pull/11591)) — thanks @ntdatt812 +- **test(token-health):** pin the current terminal-skip boundary — expired with retry budget is probed, `account_deactivated` stays skipped ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)) — thanks @ntdatt812 +- **test(models):** follow #11503's retarget of the `gemini-3-pro-high` alias to the hyphenated catalog id ([#11593](https://github.com/diegosouzapw/OmniRoute/pull/11593)) — thanks @ntdatt812 +- **refactor(video bridge):** extract per-video acquisition, whole-result caching, description, and metrics/abort/cleanup out of `VideoBridgeGuardrail.preCall` into a `processVideoPart` seam in a new `videoBridgePipeline.ts`, behind explicit `VideoMediaBrokerPort`, `VideoAudioTranscriptionPort`, and `VideoDrilldownPort` boundaries; `preCall` now only handles request traversal, policy, and response aggregation. The Video tab's FFmpeg/ffprobe runtime status is now an explicit `unknown` / `restricted` / `unavailable` / `available` state instead of a nullable boolean pair, fixing a case where an in-flight or failed probe was mislabeled as "install FFmpeg" ([#11657](https://github.com/diegosouzapw/OmniRoute/issues/11657)). ([#12016](https://github.com/diegosouzapw/OmniRoute/pull/12016)) +- **test(opencode-plugin):** add unit test coverage for `formatFreeBudget()` naming helper ([#11660](https://github.com/diegosouzapw/OmniRoute/pull/11660)) — thanks @f9td56dbgh-hub +- **docs(video):** clarify that the Video Bridge transcript `source` field (`client`, `embedded`, `audio-bridge`) is presently caller-declared and not yet server-verified — OmniRoute enforces the enum shape but does not cryptographically confirm that an `embedded`/`audio-bridge` label came from a server-owned extraction ([#11661](https://github.com/diegosouzapw/OmniRoute/issues/11661)). ([#12001](https://github.com/diegosouzapw/OmniRoute/pull/12001)) +- **chore(providers):** retire the distributed Raycast Relay and Hailuo Web integrations after a provenance audit confirmed substantial derivation from GPL-3.0 sources; the official GitHub and MiniMax API providers remain available. Migration 163 disables legacy connections and invalidates their active leases while preserving connection IDs, audit/history rows, quota snapshots, usage records, and fail-closed API-key connection policies. ([#11691](https://github.com/diegosouzapw/OmniRoute/pull/11691)) +- **chore(providers):** retire the Felo Web provider while its GPL-derived provenance remains on licensing HOLD ([#11698](https://github.com/diegosouzapw/OmniRoute/pull/11698)) — stale runtime and database references now fail closed while existing connection, lease, and usage history remains preserved for auditability. +- **fix(sse):** retire the Gemini Web image-generation bridge while provenance and licensing remain on HOLD, preserving Gemini Web chat/auth/tools and legitimate Gemini/Nano Banana image providers ([#11708](https://github.com/diegosouzapw/OmniRoute/pull/11708)) +- **chore(audio):** retire the built-in EdgeTTS provider and its 11-voice catalog while provenance/licensing review remains on HOLD; the generic `/v1/audio/speech` endpoint and the other speech providers remain available ([#11711](https://github.com/diegosouzapw/OmniRoute/pull/11711)) +- **fix(providers):** retire the unofficial Qwen Web integration and its `qwen-web`/`qw` identities while third-party provenance remains on HOLD ([#11713](https://github.com/diegosouzapw/OmniRoute/pull/11713)) — direct requests now fail closed with a sanitized HTTP 410 and combos skip the retired target, while migration guards preserve connection IDs, allowlists, usage/audit history and invalidate active leases. Supported Qwen API, Qwen Code, cloud, token-plan, audio, local and model-name surfaces remain available; restoring Qwen Web requires a qualified provenance/license decision. +- **fix(providers):** retire `microsoft-designer-web` and its `msdesigner` alias from runtime and catalogs while preserving stored credentials, provider history and allowlists as inactive records — source dependency on gpt4free's [`MicrosoftDesigner.py`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/g4f/Provider/needs_auth/MicrosoftDesigner.py) is proven at snapshot `0943d16eef757a44deb60651c07bbe5fa958cafe`, whose [`LICENSE`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/LICENSE) is GPL-3.0; expressive adaptation is probable, which records provenance/licensing risk without asserting a legal conclusion ([#11720](https://github.com/diegosouzapw/OmniRoute/pull/11720)). +- **docs(legal):** centralize pinned MIT notices for the vendored GCF codec and local flag assets ([#11726](https://github.com/diegosouzapw/OmniRoute/pull/11726)) +- Split the 2,952-line `adobeFireflyClient.ts` into nine focused modules (catalog, credentials, payload, ARP session, upload, responses, IMS, account, poll) behind an unchanged barrel — no consumer edits and no public API change (#11728). — thanks @MumuTW +- **fix(dashboard):** replaced the Model Lockout card's unprovenanced notification MP3 with a short, locally synthesized Web Audio chime ([#11731](https://github.com/diegosouzapw/OmniRoute/pull/11731)) — both toggles retain optional audible feedback without shipping replacement media or third-party code, and unsupported, suspended, or rejected audio contexts fail open without blocking the setting change. +- **chore(deepseek-web):** remove the provenance-unresolved DeepSeek PoW WASM binary and its runtime loader/tracing while retaining the existing JavaScript solver (slower at high difficulty) ([#11732](https://github.com/diegosouzapw/OmniRoute/pull/11732)). +- **chore(assets):** remove four unused provider assets whose redistribution provenance could not be verified while retaining the active Freebuff SVG ([#11735](https://github.com/diegosouzapw/OmniRoute/pull/11735)) +- **docs(assets):** retired two raster images whose redistribution provenance could not be verified ([#11736](https://github.com/diegosouzapw/OmniRoute/pull/11736)) — PicoClaw documentation now reuses the existing SVG, while Zoo Code remains a text link without an image. +- **docs(legal):** add pinned notices for six LobeHub-derived provider SVGs and 65 byte-exact theSVG assets, retaining registry claims as unverified metadata with explicit trademark and HOLD boundaries ([#11737](https://github.com/diegosouzapw/OmniRoute/pull/11737)). +- **chore(quality):** added a versioned 225/225 provider-asset provenance manifest and blocking drift gate ([#11743](https://github.com/diegosouzapw/OmniRoute/pull/11743)) — the inventory pins magic MIME and SHA-256 for every physical file, records immutable source matches for 71 assets, keeps 69 probable and 85 unresolved entries explicit but non-blocking, and treats provenance as source evidence rather than copyright or trademark clearance. +- **refactor(mitm):** replace four previously derived Traffic Inspector utilities — conversation normalization, SSE merging, secret masking, and header sanitization — with independent clean-room implementations based on public provider schemas, WHATWG SSE, and RFC 6750, while keeping the two independently classified passthrough surfaces unchanged ([#11748](https://github.com/diegosouzapw/OmniRoute/pull/11748)). +- Added a unit test that fails seven days before any dated pack under `config/` (`validUntil` and sibling keys) lapses, naming the file and key. The Alibaba free-tier pack expired on 2026-08-27 and turned every PR red the next morning with no commit involved; renewal now happens on someone's terms, not the clock's. ([#11891](https://github.com/diegosouzapw/OmniRoute/pull/11891)) +- `check:workflows` now fails (under `--strict`/`--ratchet`) when any job routed to a self-hosted runner publishes with `--provenance` — npm rejects that with `422` at the registry, which in v3.8.50 only surfaced after the tag and Docker images were public. ([#11895](https://github.com/diegosouzapw/OmniRoute/pull/11895)) +- `scripts/ops/runner-janitor.sh` now proves a path is idle with one `lsof` snapshot and removes stale leftovers itself (tmpfs after 3 h — it is RAM — disk after 24 h), kills orphan `next-build` processes, prunes checkouts of stopped runners, and alerts on memory pressure; `--dry-run` shows exactly what it would do. `docs/ops/RUNNER_BOX.md` reconciled to the measured box (31 GB, 10 listeners). ([#11893](https://github.com/diegosouzapw/OmniRoute/pull/11893)) +- The `next-build` artefact (1.3 GB) is now written and read under `$RUNNER_TEMP` (per-runner, on disk) instead of `/tmp`, which on the self-hosted pool is a 12 GB tmpfs in RAM. Landing it there took 27–32 of the publish job's 76 minutes, and the fixed `/tmp/e2e-build.tar.gz` name let E2E jobs on different runners overwrite each other's download. ([#11896](https://github.com/diegosouzapw/OmniRoute/pull/11896)) +- The CI `build` job now runs in two concurrency lanes — `main` and pull requests — so a release build is never queued behind (or OOM-killed beside) PR builds on the self-hosted pool, which holds one `next-build` comfortably and two at the edge. ([#11901](https://github.com/diegosouzapw/OmniRoute/pull/11901)) +- Re-freeze the ESLint suppressions on `release/v3.8.51` from a clean-room measurement (2 stale file entries pruned, 55 pre-existing `no-explicit-any` in six new files frozen under #11924) and drop the dead `GPT_SIZE_MAP` constant orphaned by the Adobe Firefly client split, so `No new ESLint warnings` stops failing every PR with exit 2 (Refs #11924) ([#11955](https://github.com/diegosouzapw/OmniRoute/pull/11955)) +- Type the 55 `no-explicit-any` sites that had been frozen under #11924 — four redundant casts in `socksConnectorWithFamily.ts` (undici/socks types already accept them) and the mocks/fixtures of the socks-timeout and isFree suites — and drop their suppression entries; the ESLint ratchet shrinks from 5487 to 5432 (Closes #11924) ([#11975](https://github.com/diegosouzapw/OmniRoute/pull/11975)) +- Take the two hosted-runner builds off the PR rail: `Build App` (`build.yml`) is `workflow_dispatch`-only and `DAST smoke (PR)` runs only for PRs into `main` — the 7 GB hosted VM cannot build `release/v3.8.51` in any profile (19/30 red, VM shutdown ~8 min into `next build`) and both checks had turned into permanent noise on every release PR; the bundle stays validated by `ci.yml` on `main` and by `nightly-release-green` on `release/**` (Closes #11946) ([#11962](https://github.com/diegosouzapw/OmniRoute/pull/11962)) +- Move the four nightly jobs that build the backend (`nightly-schemathesis`, `nightly-llm-security` promptfoo + garak, `nightly-resilience` axe-a11y) off the hosted 7 GB runner — where they died on `release/v3.8.51` unseen — onto the box's new `omni-light` pool (two listeners), and document the reshaped fleet (4 active OmniRoute listeners: 2 `omni-build` + 2 `omni-light`, janitor ceiling 4) (Closes #11965) ([#11967](https://github.com/diegosouzapw/OmniRoute/pull/11967)) +- Stop painting every fork PR into `release/**` red: `quality.yml` `Build (advisory)` is skipped (GitHub still reports `continue-on-error` failures as check FAILURE). Hosted `ubuntu-latest` cannot finish `npm run build` on this tree — same class as #11962 taking `build.yml` off the PR rail. `docker-publish.yml` amd64 now runs on the `.113` `omni-build` pool (31 GB / 32 cores, two listeners) with Turbopack, shares the `heavy-build-main` lane with `ci.yml` `Build` so it queues instead of becoming a third heavy, and keeps per-ref concurrency (a merge storm was starting 8 concurrent OOM builds). arm64 stays on `ubuntu-24.04-arm` with webpack — there is no ARM box. Fallback when `USE_VPS_RUNNER` is off: hosted amd64 + webpack (#11976). ([#12021](https://github.com/diegosouzapw/OmniRoute/pull/12021)) +- `docker-publish.yml` builds with webpack (`OMNIROUTE_USE_TURBOPACK=0`) on **both** arches, including amd64 on the `.113` `omni-build` pool. Turbopack had the RAM (31 GB, `omniroute-113-6`) and still panicked (`TurbopackInternalError: there must be a path to a root`, run 33253576569, 26 min in). The same tree's arm64 webpack build on hosted `ubuntu-24.04-arm` succeeded (run 33264823398). Dockerfile already documents webpack as the Docker escape hatch (#11976). ([#12050](https://github.com/diegosouzapw/OmniRoute/pull/12050)) +- **docs(free-tier):** declare the counting vs deciding regimes for "is it free?" and guard the deciding path from DB-backed catalog resolution ([#12226](https://github.com/diegosouzapw/OmniRoute/pull/12226)) — thanks @maxmad64bis +- **docs(auto-combo):** The mode pack table in `docs/routing/AUTO-COMBO.md` now lists all six shipped packs with every weight each one sets, replacing a four-pack table whose numbers had also drifted from the source. It states plainly that no pack sets `quality`, so selecting any pack silences the observed-quality signal. Six more documents that quote the scoring factor count joined the `check:docs-counts` gate, which caught five stale claims — including one naming nine factors that do not exist — and two stale mode pack counts. The dashboard routing panel, which offered four of the six packs and labelled the default strategy "6-Factor Scoring", is now covered by a test; the two packs it was missing are `reliability-first` and `chaos-mode`, the latter labelled as the fault-injection profile it is rather than as one more routing preference ([#12316](https://github.com/diegosouzapw/OmniRoute/pull/12316)) — thanks @maxmad64bis +- **chore(quality):** rebaseline `src/lib/db/apiKeys.ts` for the ACL the key-creation path now preserves ([#12352](https://github.com/diegosouzapw/OmniRoute/pull/12352)) ([#12673](https://github.com/diegosouzapw/OmniRoute/pull/12673)) — thanks @KrzysiekSko +- **chore(providers):** bump the Claude Code wire identity and the Devin bridge image pin from `2.1.220` to `2.1.258` ([#12402](https://github.com/diegosouzapw/OmniRoute/pull/12402)) — thanks @ggiak ([#12604](https://github.com/diegosouzapw/OmniRoute/pull/12604)) +- **docs(env):** align `.env.example`, the README Bun section, and the troubleshooting guide with the code: `OMNIROUTE_USE_TURBOPACK` also governs `npm run build` (not dev-only), `bun run build` follows that flag instead of auto-selecting Webpack, `OMNIROUTE_CHAT_MAX_HEAVY_IN_FLIGHT` is unset by default (no request-count cap), and the structural `503 chat_admission_busy` message matches `chatAdmissionResponses.ts` (#12404 — thanks @pacocartones) +- **chore(electron):** upgrade the desktop app to Electron 44 (Chromium 152, Node 24.18.1) ([#12217](https://github.com/diegosouzapw/OmniRoute/pull/12217)). **Requires macOS 13 (Ventura) or later** — Chromium dropped macOS 12 (Monterey), so Monterey users must stay on an earlier OmniRoute desktop build. Windows and Linux are unaffected; the app already shipped only x64/arm64, so Electron 44 dropping 32-bit builds changes nothing. Removes the `openAsHidden`/`wasOpenedAsHidden` login-item fields deleted in Electron 44 — hidden autostart continues to work through the `--hidden` argument registered with the login item ([#12554](https://github.com/diegosouzapw/OmniRoute/pull/12554)) — thanks @app/dependabot +- **chore(quality):** rebaseline `src/sse/handlers/chat.ts` for the effective-input persistence the continuation fix needs ([#12641](https://github.com/diegosouzapw/OmniRoute/pull/12641)) ([#12680](https://github.com/diegosouzapw/OmniRoute/pull/12680)) — thanks @hartmark +- `Coverage` job on `ci.yml`: the informational Codecov upload gets its own 5-minute ceiling and `continue-on-error`, and the job budget grows from 20 to 30 minutes (the 8-shard c8 merge alone takes ~10) — a stalled upload no longer ends the job `cancelled` and drags a fully green `main` run's conclusion down with it ([#11972](https://github.com/diegosouzapw/OmniRoute/pull/11972)) +- CI hardening on the PR rail: the four `Unit Tests fast-path` shards get `timeout-minutes: 30` (a hung shard held a PR for 64 min instead of GitHub's 6 h default) and both ESLint file caches lose their `restore-keys` fallback, so a cache built under another suppressions file or lint config can no longer report stale verdicts (Refs #11600, #11924) ([#11963](https://github.com/diegosouzapw/OmniRoute/pull/11963)) +- Every CI job that runs a `next build` (`build`, the npm `publish`, both release-green validations) now targets the `omni-build` runner label, which only two of the eight self-hosted runners carry. The box holds one build comfortably and two at the edge; a third now queues on GitHub instead of being OOM-killed by the kernel. ([#11932](https://github.com/diegosouzapw/OmniRoute/pull/11932)) +- **refactor(combo):** move `handleRoundRobinCombo` (and `resolveTargetTokenLimit`) into `open-sse/services/combo/roundRobinCombo.ts`. `combo.ts` drops from 2164 to 1014 lines (`split("\n").length`); the leaf is 1199 (under the 1200 new-file cap). The round-robin call site uses a dynamic `import()` so `releaseStickyPinOnFailure` / `clearStaleLKGP` can stay exported from `combo.ts` without a static cycle. Skip / sticky / semaphore / safety-timer behavior is unchanged. ([#12811](https://github.com/diegosouzapw/OmniRoute/pull/12811)) — thanks @HouMinXi +- **chore(quality):** rebaseline the file-size caps the error-boundary campaign grew past (`open-sse/executors/codex.ts`, `open-sse/vendor/codex-chatgpt-web/bridge.ts`, both via [#12444](https://github.com/diegosouzapw/OmniRoute/pull/12444)) ([#12654](https://github.com/diegosouzapw/OmniRoute/pull/12654)) +- **chore(quality):** rebaseline the file-size caps the hartmark batch grew past (`combos/page.tsx` via [#12355](https://github.com/diegosouzapw/OmniRoute/pull/12355), `open-sse/services/combo.ts` via [#12338](https://github.com/diegosouzapw/OmniRoute/pull/12338)) ([#12623](https://github.com/diegosouzapw/OmniRoute/pull/12623)) — thanks @hartmark +- **chore(quality):** rebaseline the file-size caps the HouMinXi batch grew past when its PRs stacked (`providers/page.tsx`, `chatCore.ts`, `accountFallback.ts`) — each PR measured correctly in isolation, none saw the stacking ([#12619](https://github.com/diegosouzapw/OmniRoute/pull/12619)) +- **chore(quality):** rebaseline `open-sse/services/combo.ts` for the reset-aware scoring the HouMinXi batch stacked ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) ([#12678](https://github.com/diegosouzapw/OmniRoute/pull/12678)) — thanks @HouMinXi +- **docs:** sync provider catalog count (direct commit `4f11b2ae3d`) +- **docs(skills):** sync omni-inference search fallback (direct commit `b4e76a7ed9`) +- **chore(lint):** prune two stale base suppressions (direct commit `1325047d56`) +- **test(auto-combo):** tolerate full-suite rotation load (direct commit `ad3e293f9f`) +- **test:** drain inherited quality snapshot reds (direct commit `21ed68d8ac`) +- **chore(quality):** rebaseline measured CLI bundle growth (direct commit `d35b3f9779`) +- **test(ui):** reproduce invalid AnySearch icon alias (direct commit `12cc6ca834`) +- **test(translator):** fix the relative imports of the relocated deferred-finish test (direct commit `777d9d1629`) +- **docs(counts):** bring the documented provider and migration counts to the measured values (direct commit `e70bea30e9`) +- **test(security):** drop the guard-target set left unused by the sync (direct commit `a32ba0e362`) +- **chore(db):** renumber model_capabilities migration 163 → 169 (cross-branch collision with 163_radar_feed_cache_generated_at) (direct commit `70f5d4cbf9`) +- **docs(cli):** document CLI_PRIME_AGENT_BIN and correct the CLI Agents count (direct commit `9058e39b61`) +- **chore(deps):** refresh runtimes and adopt ESLint 10 ([#11259](https://github.com/diegosouzapw/OmniRoute/pull/11259)) — thanks @backryun +- **refactor(local-corpus):** implement dynamic root resolution and LRU cache ([#11491](https://github.com/diegosouzapw/OmniRoute/pull/11491)) — thanks @raheemuddin786 +- **chore(quality):** prune stale eslint-suppressions entries ([#11596](https://github.com/diegosouzapw/OmniRoute/pull/11596)) +- **chore(ci):** unblock quality gates - env-doc sync + stryker tap.testFiles registration (#9282) ([#11609](https://github.com/diegosouzapw/OmniRoute/pull/11609)) — thanks @Meet6338-X +- **chore(ci):** point the circular-deps gate at dpdm's real JS entrypoint ([#11615](https://github.com/diegosouzapw/OmniRoute/pull/11615)) — thanks @benzntech +- **chore(quality):** rebaseline file-size for #11448/#11495/#11561 growth ([#11630](https://github.com/diegosouzapw/OmniRoute/pull/11630)) +- **deps:** realign onnxruntime-node pin to @huggingface/transformers' 1.24.3 lockstep ([#11633](https://github.com/diegosouzapw/OmniRoute/pull/11633)) — thanks @jonlwheat2-gif +- **test(autoCombo):** port models_dev_tier #11508 guard from node:test to vitest ([#11635](https://github.com/diegosouzapw/OmniRoute/pull/11635)) — thanks @jonlwheat2-gif +- **chore(quality):** rebaseline file-size for #11083/#11631/#11628 growth ([#11636](https://github.com/diegosouzapw/OmniRoute/pull/11636)) +- **chore(lint):** prune stale ESLint suppressions (base-red #11449) ([#11644](https://github.com/diegosouzapw/OmniRoute/pull/11644)) — thanks @jonlwheat2-gif +- **docs(changelog):** aggregate the ten v3.8.50 fragments into CHANGELOG.md ([#11683](https://github.com/diegosouzapw/OmniRoute/pull/11683)) +- **chore(search):** drop unreachable credential sweep after #11642 ([#11687](https://github.com/diegosouzapw/OmniRoute/pull/11687)) — thanks @HouMinXi +- **ci:** add API route TypeScript regression gate ([#11705](https://github.com/diegosouzapw/OmniRoute/pull/11705)) — thanks @Sabeekhann +- **docs(dedup):** correct the CodeQL claim on the tenant-namespace comment ([#11712](https://github.com/diegosouzapw/OmniRoute/pull/11712)) +- **docs(changelog):** add consolidated v3.8.50 stats, top-25 ranking and a .mailmap ([#11715](https://github.com/diegosouzapw/OmniRoute/pull/11715)) +- **docs:** recommend gstack for AI-assisted workflows ([#11770](https://github.com/diegosouzapw/OmniRoute/pull/11770)) — thanks @santosraju99-hub +- **chore(quality):** tighten complexity/cognitive-complexity ratchets to the current tip; land the missed gateways.ts rebaseline ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **docs(i18n):** finish Freepik → Magnific rebrand in locale strings and README ([#11772](https://github.com/diegosouzapw/OmniRoute/pull/11772)) — thanks @turbolego +- **refactor(auto-combo):** fix divergent scoring in combo health reporting ([#11854](https://github.com/diegosouzapw/OmniRoute/pull/11854)) — thanks @jacobsparts +- **chore(changelog):** add missing fragments for #11919/#11918/#11916 ([#11938](https://github.com/diegosouzapw/OmniRoute/pull/11938)) +- **test(check):** escape the runs-on fixture with JSON.stringify, not a quote-only replace ([#11942](https://github.com/diegosouzapw/OmniRoute/pull/11942)) (direct commit `fb7445eaa3`) +- **test(release):** align five suites with the contracts #11933, #11919 and #11876 shipped on release/v3.8.51 ([#11944](https://github.com/diegosouzapw/OmniRoute/pull/11944)) +- **chore(quality):** remove unreachable code and restore test discovery ([#11950](https://github.com/diegosouzapw/OmniRoute/pull/11950)) — thanks @backryun +- **docs(agents):** sync-back landings are fast-forward, never squash ([#11964](https://github.com/diegosouzapw/OmniRoute/pull/11964)) +- **test(infra):** retry recursive temp-dir removal instead of failing a shard on ENOTEMPTY (#11966) ([#11968](https://github.com/diegosouzapw/OmniRoute/pull/11968)) +- **docs(ops):** the .113 heavy-build ceiling is one runner, not two ([#12048](https://github.com/diegosouzapw/OmniRoute/pull/12048)) +- **test(ui):** correct inactive auto-fetch expectation ([#12098](https://github.com/diegosouzapw/OmniRoute/pull/12098)) — thanks @RaviTharuma +- **docs:** document native dependency check escape hatch ([#12101](https://github.com/diegosouzapw/OmniRoute/pull/12101)) — thanks @RaviTharuma +- **test(providers):** regenerate the translate-path golden for OrcaRouter (#11923) ([#12118](https://github.com/diegosouzapw/OmniRoute/pull/12118)) +- **chore(quality):** velocity phase — loosen every numeric baseline by 20% until v4.0, monitor headroom nightly ([#12125](https://github.com/diegosouzapw/OmniRoute/pull/12125)) +- **chore(lint):** batch 0 of #12146 — type the call-log-cap sqlite rows instead of 45 as-any casts ([#12157](https://github.com/diegosouzapw/OmniRoute/pull/12157)) +- **chore(lint):** batch 4 of #12146 — shared/components react-hooks violations resolved ([#12159](https://github.com/diegosouzapw/OmniRoute/pull/12159)) +- **chore(lint):** batch 1 of #12146 — dashboard/cli-code react-hooks violations resolved ([#12160](https://github.com/diegosouzapw/OmniRoute/pull/12160)) +- **chore(lint):** batch 3 of #12146 — dashboard/settings react-hooks violations resolved ([#12162](https://github.com/diegosouzapw/OmniRoute/pull/12162)) +- **chore(lint):** batch 2 of #12146 — resolve the react-hooks compiler violations in dashboard/providers ([#12163](https://github.com/diegosouzapw/OmniRoute/pull/12163)) +- **chore(quality):** register search-432 cooldown test in stryker tap.testFiles ([#12170](https://github.com/diegosouzapw/OmniRoute/pull/12170)) +- **chore(lint):** batch 5 of #12146 — combos, endpoint, provider-stats, api-manager and costs react-hooks violations resolved ([#12174](https://github.com/diegosouzapw/OmniRoute/pull/12174)) +- **build:** add contributor fast profile ([#12192](https://github.com/diegosouzapw/OmniRoute/pull/12192)) — thanks @rafacpti23 +- **docs(readme):** README touch-ups ([#12193](https://github.com/diegosouzapw/OmniRoute/pull/12193)) ([#12194](https://github.com/diegosouzapw/OmniRoute/pull/12194)) ([#12202](https://github.com/diegosouzapw/OmniRoute/pull/12202)) — thanks @rafacpti23 +- **build:** force the webpack bundler in the contributor build profile ([#12197](https://github.com/diegosouzapw/OmniRoute/pull/12197)) — thanks @rafacpti23 +- **build:** skip standalone packaging in the contributor profile ([#12198](https://github.com/diegosouzapw/OmniRoute/pull/12198)) — thanks @rafacpti23 +- **docs(audit):** align every published number with the code and harden check:docs-counts ([#12200](https://github.com/diegosouzapw/OmniRoute/pull/12200)) +- **build:** stub the instrumentation entrypoints in the contributor profile ([#12203](https://github.com/diegosouzapw/OmniRoute/pull/12203)) — thanks @rafacpti23 +- **build:** omit the standalone output target for contributor builds ([#12204](https://github.com/diegosouzapw/OmniRoute/pull/12204)) — thanks @rafacpti23 +- **chore(lint):** batch 6 of #12146 — memory, radar, audit, analytics, cache, usage, activity, home and RequestLoggerV2 react-hooks violations resolved ([#12208](https://github.com/diegosouzapw/OmniRoute/pull/12208)) +- **docs(diagrams):** rename number-carrying diagram files to stable names ([#12210](https://github.com/diegosouzapw/OmniRoute/pull/12210)) +- **docs(dashboard):** redraw onboarding tier-flow SVGs for the real 4-tier model ([#12211](https://github.com/diegosouzapw/OmniRoute/pull/12211)) +- **docs(api):** document every implemented route in openapi.yaml (276 → 692 paths) ([#12212](https://github.com/diegosouzapw/OmniRoute/pull/12212)) +- **chore(lint):** batch 7 of #12146 — final src tail: 37 react-hooks violations across 33 files resolved ([#12244](https://github.com/diegosouzapw/OmniRoute/pull/12244)) +- Revert "docs: recommend gstack for AI-assisted workflows (#11770)" ([#12248](https://github.com/diegosouzapw/OmniRoute/pull/12248)) ([#12249](https://github.com/diegosouzapw/OmniRoute/pull/12249)) +- **docs:** Chaos Mode setup guide + weighted strategy semantics ([#12250](https://github.com/diegosouzapw/OmniRoute/pull/12250)) +- **docs(agents):** protected-surface merge rule — operator approval for agent-instruction files ([#12253](https://github.com/diegosouzapw/OmniRoute/pull/12253)) +- **chore(quality):** register native-codex turn-pin tests in stryker tap.testFiles ([#12263](https://github.com/diegosouzapw/OmniRoute/pull/12263)) +- **chore(quality):** dedupe tap.testFiles entries added by racing base-red fixes ([#12265](https://github.com/diegosouzapw/OmniRoute/pull/12265)) +- **docs(readme):** introduce OmniRouteTray — the macOS menu-bar companion ([#12276](https://github.com/diegosouzapw/OmniRoute/pull/12276)) — thanks @ggiak +- **chore(proxy):** purge the legacy 1proxy residue — sync/rotator modules, dead DB exports, dead settings tab and flag, docs (#12091) ([#12290](https://github.com/diegosouzapw/OmniRoute/pull/12290)) +- **chore(quality):** baseline-headroom skips generated and vendored files in the fileSize worst-file signal ([#12291](https://github.com/diegosouzapw/OmniRoute/pull/12291)) +- **refactor(video):** unify the JPEG frame data-URI contract ([#12322](https://github.com/diegosouzapw/OmniRoute/pull/12322)) +- **chore(deps):** freeze onnxruntime-node and eslint-plugin-react-hooks out of dependabot groups ([#12329](https://github.com/diegosouzapw/OmniRoute/pull/12329)) +- **test(executors):** restore the #10986 reasoning-only fallback guards ([#12364](https://github.com/diegosouzapw/OmniRoute/pull/12364)) — thanks @pacocartones +- **chore(quality):** re-tighten the file-size ratchet to the real LOC (plan 3.8.52 task 0) ([#12411](https://github.com/diegosouzapw/OmniRoute/pull/12411)) +- **test(grok):** format web executor suite ([#12412](https://github.com/diegosouzapw/OmniRoute/pull/12412)) +- **chore(quality):** ratchet open-sse typecheck baseline to zero ([#12418](https://github.com/diegosouzapw/OmniRoute/pull/12418)) +- **test(build):** guard the artifact path policy arrays against duplicates ([#12422](https://github.com/diegosouzapw/OmniRoute/pull/12422)) +- **chore(lint):** adopt eslint-plugin-react-hooks 7.1.1 ([#12428](https://github.com/diegosouzapw/OmniRoute/pull/12428)) +- **chore(providers):** remove a keyless provider integration at its operator's request ([#12440](https://github.com/diegosouzapw/OmniRoute/pull/12440)) +- **docs(providers):** register providers removed at their operator's request and guard against reintroduction ([#12478](https://github.com/diegosouzapw/OmniRoute/pull/12478)) +- **chore(quality):** base fixes — stryker tap.testFiles + node_modules cache key ([#12482](https://github.com/diegosouzapw/OmniRoute/pull/12482)) +- **docs(arch):** one-process recipe for tens of long /v1/responses ([#12493](https://github.com/diegosouzapw/OmniRoute/pull/12493)) — thanks @RaviTharuma +- **chore(quality):** tighten the CodeQL ratchet baseline from 11 to 6 ([#12530](https://github.com/diegosouzapw/OmniRoute/pull/12530)) +- **chore(deps):** pin browserslist override to ^4.28.8 ([#12592](https://github.com/diegosouzapw/OmniRoute/pull/12592)) — thanks @KooshaPari +- **chore(deps):** bump fast-uri to 3.1.7 in the electron lockfile ([#12601](https://github.com/diegosouzapw/OmniRoute/pull/12601)) +- **docs(quality):** document how the CodeQL ratchet refreshes and how to tighten it ([#12611](https://github.com/diegosouzapw/OmniRoute/pull/12611)) +- **test(ci):** pin the openapi-security-tiers two-arm contract with an executing gate test (#12581) ([#12652](https://github.com/diegosouzapw/OmniRoute/pull/12652)) +- **docs(gamification):** align XP Rewards table with code (#12501) ([#12667](https://github.com/diegosouzapw/OmniRoute/pull/12667)) — thanks @KooshaPari +- **refactor(combo):** split executeTarget into gates, attempt, and loop ([#12746](https://github.com/diegosouzapw/OmniRoute/pull/12746)) — thanks @HouMinXi +- **docs(security):** document socket.yml scanner config + CI workflow link (#12575) ([#12764](https://github.com/diegosouzapw/OmniRoute/pull/12764)) — thanks @KooshaPari +- **chore(ci):** guard commit identity in pre-commit to stop author misattribution ([#12772](https://github.com/diegosouzapw/OmniRoute/pull/12772)) +- **deps:** 17 Dependabot bumps — bump electron from 43.4.0 to 43.4.1 in /electron ([#11426](https://github.com/diegosouzapw/OmniRoute/pull/11426)); bump the development group across 1 directory with 8 updates ([#11428](https://github.com/diegosouzapw/OmniRoute/pull/11428)); bump the production group across 1 directory with 11 updates ([#11440](https://github.com/diegosouzapw/OmniRoute/pull/11440)); chore(deps): bump github/codeql-action/init from 4.37.7 to 4.37.8 ([#11925](https://github.com/diegosouzapw/OmniRoute/pull/11925)); chore(deps): bump github/codeql-action from 4.37.7 to 4.37.8 ([#11926](https://github.com/diegosouzapw/OmniRoute/pull/11926)); chore(deps): bump github/codeql-action/analyze from 4.37.7 to 4.37.8 ([#11927](https://github.com/diegosouzapw/OmniRoute/pull/11927)); build(deps): bump github/codeql-action/init from 4.37.8 to 4.37.9 ([#12345](https://github.com/diegosouzapw/OmniRoute/pull/12345)); build(deps): bump github/codeql-action/analyze from 4.37.8 to 4.37.9 ([#12346](https://github.com/diegosouzapw/OmniRoute/pull/12346)); bump the development group across 1 directory with 2 updates ([#12347](https://github.com/diegosouzapw/OmniRoute/pull/12347)); build(deps): bump github/codeql-action from 4.37.8 to 4.37.9 ([#12349](https://github.com/diegosouzapw/OmniRoute/pull/12349)); bump browserslist from 4.28.2 to 4.28.8 ([#12396](https://github.com/diegosouzapw/OmniRoute/pull/12396)); bump the production group across 1 directory with 4 updates ([#12399](https://github.com/diegosouzapw/OmniRoute/pull/12399)); bump @xmldom/xmldom ([#12500](https://github.com/diegosouzapw/OmniRoute/pull/12500)); bump qs from 6.15.2 to 6.16.0 ([#12512](https://github.com/diegosouzapw/OmniRoute/pull/12512)); bump @xmldom/xmldom from 0.9.10 to 0.9.12 ([#12513](https://github.com/diegosouzapw/OmniRoute/pull/12513)); bump fast-uri from 3.1.5 to 3.1.7 ([#12514](https://github.com/diegosouzapw/OmniRoute/pull/12514)); bump @humanfs/node from 0.16.7 to 0.16.8 ([#12515](https://github.com/diegosouzapw/OmniRoute/pull/12515)) +- Clear the `release/v3.8.51` `check:agent-skills-sync` base-red: regenerate `skills/cli-tunnel/SKILL.md` so the `tunnel create [type]` positional that #13009 taught the generator to read is reflected in the committed skill. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` typecheck base-red from #12731: the new `Mode pack ranking gates` candidates in `open-sse/services/autoCombo/__tests__/autoCombo.test.ts` omitted the required `provider`, `model` and `errorRate` fields of `ProviderCandidate`, failing `check:open-sse-typecheck` (and with it `Fast Quality Gates`) on every open PR. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` docs-sync base-red: EURouter (#13025) and GreenPT (#13024) took the live provider count to 358, leaving 7 STRICT drifts (`PROVIDER_REFERENCE.md`, 4 diagrams, 2 tier-flow images) plus the `AGENTS.md` / `llm.txt` / `package.json` count claims stale. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- **test(models):** Pin `CATALOG_BUILD_TIMEOUT_MS` in the #12058 canonical-catalog regression so a cold catalog build racing the 8s default bound no longer flakes the suite with a 500 ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Allow `@omniroute/opencode-plugin-v2/` in the npm pack policy — [#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870) shipped the v2 plugin beside its v1 sibling without widening the allowlist, so every packed file under it read as an unexpected artifact and failed `check:pack-policy` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Point the release-green validator's pack gate at the tree it is validating — the build-provenance guard defaulted to `origin/main`, which a release branch tip can never be an ancestor of mid-cycle, so `Package artifact` reported a false red on every run and the tarball boot-smoke cascaded off it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **docs(checks):** keep doc counts honest — headings, rankings, catalog, weights, quality gate and scoring diagram now covered ([#12507](https://github.com/diegosouzapw/OmniRoute/pull/12507)) — thanks @maxmad64bis +- **chore(lifecycle):** `check:model-lifecycle` now also diffs `DEFAULT_DEGRADATION_MAP` (the background-task redirect table) against the vendor lifecycle snapshot, refusing a retired id as source or target, with a table-driven unit test beside it. Three rows whose source the vendor had retired — `claude-sonnet-4-20250514`, `gemini-3-pro-preview` and `gpt-5.1-codex` (whose target `gpt-5.1-codex-mini` is retired too) — were dead code, since `checkLifecycle` answers 410 before the redirect runs; they are dropped (#12535 — thanks @pacocartones) +- **test(compression):** cover idle worker eviction at the resource level — the pool must call `terminate()` and must not retain the worker's `MessagePort`, complementing the `exit`-event assertion added with the fix ([#13371](https://github.com/diegosouzapw/OmniRoute/pull/13371)) +- **chore(quality):** raise the `imageGeneration.ts` file-size ceiling for the image-only-model guard that clears the #12945 base-red ([#13392](https://github.com/diegosouzapw/OmniRoute/pull/13392)) +- **chore(quality):** type the combo-test route's JSON response bodies instead of casting them to `any`, drop the now-empty suppression entry, and remove the `hasPerModelQuota` import `chatCore.ts` stopped using when the failure-classification helper was extracted ([#13379](https://github.com/diegosouzapw/OmniRoute/pull/13379)) +- **chore(electron):** the desktop bundle no longer ships the root-level files of every translated docs mirror (`README.md`, `llm.txt`, `CLAUDE.md`, `GEMINI.md`, `CONTRIBUTING.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`) — the packaged app only reads `docs/i18n//docs/**`, which stays. Saves ~11 MB on top of the translated CHANGELOGs already pruned. (#0000) ([#13235](https://github.com/diegosouzapw/OmniRoute/pull/13235)) +- **chore(quality):** retighten the `src/sse/handlers/chat.ts` unused-vars suppression count to the 8 violations that actually remain ([#13385](https://github.com/diegosouzapw/OmniRoute/pull/13385)) +- Bring the documented counts back in line with the code: 171 migrations (was 169) and 20 routing strategies (was 19), plus the regenerated `cli-tunnel` skill reference. ([#12970](https://github.com/diegosouzapw/OmniRoute/pull/12970)) +- Update the documented migration count to 172 after the call-logs provider-stats indexes landed. ([#13209](https://github.com/diegosouzapw/OmniRoute/pull/13209)) +- **chore(ci):** two gates that close the blind spots behind the exclusions above. `check:vitest-exclusions` requires every Vitest exclusion to name a tracking issue and to appear in `config/quality/vitest-exclusions.json` — the previous list grew to 62 files behind a comment pointing at an issue that had been closed for a month. `check-new-key-coverage` requires a key newly added to `en.json` to reach every locale; the existing coverage gate is a percentage floor per locale, so eleven absent keys out of ~13,000 left it at 99.9% while a whole feature shipped untranslated in nine languages. Both are diff-aware, so pre-existing debt stays frozen and neither needed a migration to turn on. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **build:** root layout no longer loads Inter from `next/font/google`, so a production image build does not need fonts.googleapis.com ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **docs(i18n):** bring the eight new `llm.txt` mirrors (km, kn, ml, my, ne, or, pa, si) up to the current provider and migration counts, so `check-docs-sync` stops rejecting every commit ([#13674](https://github.com/diegosouzapw/OmniRoute/pull/13674)) +- **refactor(ui):** flow surfaces (home topology, combo live studio, compression cockpit/waterfall/nodes, token-health badge) express state through the theme-aware `--orch-status-*` tokens instead of fixed dark-mode hex, so green/red/amber/grey stay legible in the light theme; dark mode is byte-identical. Categorical palettes (routing-strategy hues, compression-layer pills, provider brand colors) deliberately stay hex ([#12378](https://github.com/diegosouzapw/OmniRoute/issues/12378)) ([#13203](https://github.com/diegosouzapw/OmniRoute/pull/13203)) +- **chore(tests):** 51 test files that had been excluded from Vitest are running again, restoring roughly 350 assertions to the blocking suite. Each was measured individually first: of the 62 files parked behind the `// #8618 — pre-existing failure` comment, 51 pass against the current tree with no source change, so the exclusions had outlived the failures they were added for. The 11 that genuinely still fail stay excluded, but now point at a live tracker (#13204) rather than at #8618, which was closed in August while the list it tracked kept growing. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **chore(quality):** drop two ESLint suppression entries whose violations no longer exist, so `eslint --suppressions-location` stops rejecting every commit that touches the surrounding files ([#13384](https://github.com/diegosouzapw/OmniRoute/pull/13384)) +- **chore(release):** bring main's post-tag fixes into release/v3.8.51 — sync-back step 2/2 (direct commit `21c488f210`) +- **chore(deps):** patch toml and esbuild in the opencode plugin lockfiles ([#12965](https://github.com/diegosouzapw/OmniRoute/pull/12965)) +- **docs(changelog):** reconcile the v3.8.51 living section — fold 366 fragments, cover every cycle commit, credit every contributor ([#12971](https://github.com/diegosouzapw/OmniRoute/pull/12971)) +- **test(plugin):** wait for the v2 lazy-refresh tier instead of sleeping past it ([#12984](https://github.com/diegosouzapw/OmniRoute/pull/12984)) +- **chore(quality):** rebaseline chatCore.ts after the non-streaming regression fixes ([#13045](https://github.com/diegosouzapw/OmniRoute/pull/13045)) +- **docs(changelog):** add the missing bullet marker to the DeepSeek PoW fragment ([#13200](https://github.com/diegosouzapw/OmniRoute/pull/13200)) +- **chore(deps):** drain the Dependabot queue — 10 of 13 alerts ([#13213](https://github.com/diegosouzapw/OmniRoute/pull/13213)) +- **chore(ci):** make worktrees carry the hooks that enforce the identity gate ([#13236](https://github.com/diegosouzapw/OmniRoute/pull/13236)) +- **chore(deps):** pin csv-parse to ^7.0.2 via overrides (#13117) ([#13300](https://github.com/diegosouzapw/OmniRoute/pull/13300)) — thanks @KooshaPari +- **chore(deps):** bump hono from ^4.12.34 to ^4.13.7 (#13148) ([#13301](https://github.com/diegosouzapw/OmniRoute/pull/13301)) — thanks @KooshaPari +- **chore(deps):** pin joi to ^18.2.8 via overrides (#13085) ([#13302](https://github.com/diegosouzapw/OmniRoute/pull/13302)) — thanks @KooshaPari +- **docs(providers):** add Microsoft 365 Copilot (BizChat) provider guide (#12779) ([#13340](https://github.com/diegosouzapw/OmniRoute/pull/13340)) — thanks @KooshaPari +### 🙌 Contributors + +Thanks to everyone whose work landed in v3.8.51: + +| Contributor | PRs / Issues | +| --- | --- | +| [@5dive-bot](https://github.com/5dive-bot) | #11852 | +| [@abhisheksharma2411](https://github.com/abhisheksharma2411) | #11547, #11684, #11959, #12209 | +| [@adevwithpurpose](https://github.com/adevwithpurpose) | #11464 | +| [@adivekar-utexas](https://github.com/adivekar-utexas) | #12015, #12043, #12090, #12138 | +| [@AIB1TAL0S](https://github.com/AIB1TAL0S) | #12103 | +| [@alltomatos](https://github.com/alltomatos) | #11448 | +| [@alvinveroy](https://github.com/alvinveroy) | #11857, #12027, #12165 | +| [@amaleta](https://github.com/amaleta) | #11906, #11934 | +| [@amartinawi](https://github.com/amartinawi) | #11575 | +| [@AndrianBalanescu](https://github.com/AndrianBalanescu) | #11083, #11830, #11834, #11844 | +| [@anhtahaylove](https://github.com/anhtahaylove) | #13091, #13092, #13093, #13096, #13097, #13100, #13106, #13109, #13114, #13155, #13166, #13171, #13175, #13187, #13196 | +| [@aniruddhaadak80](https://github.com/aniruddhaadak80) | #11760 | +| [@app](https://github.com/app) | #12554 | +| [@arminanton](https://github.com/arminanton) | #11461, #11513 | +| [@AStupidBear](https://github.com/AStupidBear) | #11584 | +| [@atescivitci-cmd](https://github.com/atescivitci-cmd) | #12105 | +| [@b3nw](https://github.com/b3nw) | #11970, #11971, #12147 | +| [@backryun](https://github.com/backryun) | #11259, #11950, #12075, #12076, #12078, #12079, #12081, #12082, #12181, #12228, #12239, #12255, #12258, #12277, #12367, #12423, #12524, #12538 | +| [@benjaminkitt](https://github.com/benjaminkitt) | #11747 | +| [@benzntech](https://github.com/benzntech) | #11614, #11615 | +| [@Bl0ck154](https://github.com/Bl0ck154) | #11948, #11951, #11952, #11953, #11954 | +| [@brick30llc-ctrl](https://github.com/brick30llc-ctrl) | #11752, #12242, #12262 | +| [@Chewji9875](https://github.com/Chewji9875) | #11455, #11875, #12028 | +| [@cheynetom](https://github.com/cheynetom) | #12058 | +| [@davidebaraldo](https://github.com/davidebaraldo) | #12222, #12304 | +| [@davidlinfr](https://github.com/davidlinfr) | #6390 | +| [@ddarkr](https://github.com/ddarkr) | #12124 | +| [@Deftera186](https://github.com/Deftera186) | #11809 | +| [@dpozimski](https://github.com/dpozimski) | #11945 | +| [@DW-MediaLab](https://github.com/DW-MediaLab) | #12229 | +| [@echel0nn](https://github.com/echel0nn) | #11923 | +| [@f9td56dbgh-hub](https://github.com/f9td56dbgh-hub) | #11660 | +| [@fabioluissilva](https://github.com/fabioluissilva) | #11991 | +| [@foreveryh](https://github.com/foreveryh) | #12177 | +| [@geek007git](https://github.com/geek007git) | #12115, #12116, #12117, #12120, #12122 | +| [@geekyNads](https://github.com/geekyNads) | #11784, #12266 | +| [@ggiak](https://github.com/ggiak) | #11561, #12276, #12350, #12402 | +| [@gonisulaimann](https://github.com/gonisulaimann) | #12368, #12369, #12371 | +| [@Gorillaz322](https://github.com/Gorillaz322) | #12207 | +| [@groovecityJO](https://github.com/groovecityJO) | #12682 | +| [@hartmark](https://github.com/hartmark) | #11434, #11452, #11473, #11499, #11703, #11983, #11984, #11985, #11986, #11988, #11989, #11990, #11994, #12221, #12293, #12445, #12446, #12447, #12448, #12460, #12461, #12623, #12646, #12650, #12680, #12717, #12718, #12727, #12741, #12854 | +| [@hizzt](https://github.com/hizzt) | #11894 | +| [@hongnoul](https://github.com/hongnoul) | #11484 | +| [@HouMinXi](https://github.com/HouMinXi) | #11411, #11414, #11512, #11518, #11520, #11641, #11642, #11643, #11687, #11779, #11849, #11850, #11851, #11915, #11916, #11918, #11919, #11920, #12013, #12017, #12033, #12042, #12106, #12139, #12166, #12169, #12171, #12205, #12213, #12312, #12325, #12487, #12488, #12495, #12504, #12557, #12566, #12590, #12591, #12624, #12626, #12632, #12637, #12678, #12696, #12697, #12711, #12733, #12746, #12767, #12770, #12789, #12803, #12805, #12811, #12866, #12868, #12899, #12926, #12934, #12950, #12951, #12974, #13001, #13006, #13011, #13017, #13026, #13027, #13034, #13035, #13038, #13042, #13050, #13060, #13061, #13069, #13107, #13120, #13136, #13195, #13197 | +| [@Hsia97](https://github.com/Hsia97) | #11624 | +| [@jacobsparts](https://github.com/jacobsparts) | #11854, #12155, #12167 | +| [@joglomedia](https://github.com/joglomedia) | #11980 | +| [@jonlwheat2-gif](https://github.com/jonlwheat2-gif) | #11443, #11450, #11549, #11567, #11608, #11633, #11635, #11640, #11644, #11671, #11672, #11673, #11674, #11675, #11676, #11677, #11706, #11783, #12051, #12052, #12053, #12055 | +| [@kanade-hoshino](https://github.com/kanade-hoshino) | #12180 | +| [@Karan825](https://github.com/Karan825) | #11861 | +| [@KaspaPulse](https://github.com/KaspaPulse) | #11389, #11469, #11628, #11666, #11910 | +| [@keeltrace](https://github.com/keeltrace) | #12080, #12223 | +| [@killer30001000](https://github.com/killer30001000) | #12178 | +| [@KooshaPari](https://github.com/KooshaPari) | #12470, #12592, #12667, #12699, #12703, #12706, #12764, #12769, #12771, #13300, #13301, #13302, #13340, #13401, #13403, #13404, #13405, #13406, #13407, #13409, #13410, #13411, #13412, #13413, #13414, #13418, #13424, #13427, #13433, #13523, #13524, #13525, #13528, #13531, #13532, #13534, #13539, #13543, #13545, #13546, #13547, #13550, #13553 | +| [@kriptoburak](https://github.com/kriptoburak) | #11370 | +| [@KrzysiekSko](https://github.com/KrzysiekSko) | #12425, #12432, #12673 | +| [@marcelokarval](https://github.com/marcelokarval) | direct commit / report | +| [@maxmad64bis](https://github.com/maxmad64bis) | #1622, #11435, #11437, #11441, #11537, #11550, #11553, #11555, #11557, #11812, #11842, #11843, #11903, #12151, #12214, #12215, #12218, #12226, #12314, #12316, #12317, #12318, #12319, #12320, #12321, #12507, #12715, #12731, #12744, #12786, #12787, #12788, #12790, #12792, #12794, #12795, #12828, #12832, #12853, #12857, #12870, #12937, #12941, #12975, #13141, #13142, #13146, #13147 | +| [@mdigitalbh81](https://github.com/mdigitalbh81) | #12240 | +| [@Meet6338-X](https://github.com/Meet6338-X) | #11598, #11609, #12110 | +| [@morpheus9393](https://github.com/morpheus9393) | #11943 | +| [@MumuTW](https://github.com/MumuTW) | #11492, #11502, #11506, #11507, #11626, #11675, #11685, #11728, #11746, #11888, #11889, #11890, #11892 | +| [@Neuron-Mr-White](https://github.com/Neuron-Mr-White) | #11622, #11801, #12256 | +| [@NightStalker-87](https://github.com/NightStalker-87) | #12183 | +| [@NoxzRCW](https://github.com/NoxzRCW) | #11879, #11880, #11881, #11882, #11883, #11935 | +| [@ntdat812](https://github.com/ntdat812) | #11585, #12095 | +| [@ntdatt812](https://github.com/ntdatt812) | #11368, #11573, #11574, #11576, #11577, #11580, #11582, #11583, #11588, #11589, #11590, #11591, #11592, #11593, #11672, #12177, #12180, #12873, #12918, #12920, #12921, #12925, #12930, #13007, #13009, #13024, #13025, #13083, #13087, #13101, #13104, #13110 | +| [@official-burak](https://github.com/official-burak) | #11542 | +| [@opensource-elearning](https://github.com/opensource-elearning) | #12179, #12189, #12278, #12286 | +| [@oyi77](https://github.com/oyi77) | #11408, #11409, #11421, #11505, #11677, #12036, #12110 | +| [@pacocartones](https://github.com/pacocartones) | #11521, #11522, #11527, #11528, #11529, #11530, #11531, #11532, #11533, #11534, #11595, #11599, #11603, #11604, #11605, #11607, #11610, #11676, #11714, #11716, #11718, #11767, #11838, #11860, #11862, #11869, #11871, #11872, #11873, #11903, #11906, #11921, #11934, #12356, #12358, #12359, #12360, #12361, #12362, #12364, #12365, #12373, #12374, #12375, #12376, #12377, #12379, #12380, #12381, #12386, #12387, #12389, #12390, #12394, #12395, #12397, #12401, #12403, #12404, #12522, #12523, #12535, #12536, #12540, #12541, #12543, #12545, #12548, #12549, #12550, #12551, #12552, #12647, #12651, #12653, #13055 | +| [@patrykkopycinski](https://github.com/patrykkopycinski) | #11936, #11937, #12224 | +| [@PauloFH](https://github.com/PauloFH) | #11509 | +| [@PauloHSOliveira](https://github.com/PauloHSOliveira) | #12241 | +| [@ponkcore](https://github.com/ponkcore) | #12054 | +| [@Prajeeth-12](https://github.com/Prajeeth-12) | #11634, #12046 | +| [@quiterunner-commits](https://github.com/quiterunner-commits) | #12131, #12143 | +| [@rafacpti23](https://github.com/rafacpti23) | #11554, #11558, #12192, #12197, #12198, #12202, #12203, #12204 | +| [@ragnar-claude](https://github.com/ragnar-claude) | #11564 | +| [@raheemuddin786](https://github.com/raheemuddin786) | #11491, #11839, #11840, #11841, #12003, #12230, #12231, #12232, #12233, #12234 | +| [@rahilmavani](https://github.com/rahilmavani) | #11761 | +| [@Rahulsharma0810](https://github.com/Rahulsharma0810) | #11771 | +| [@RaviTharuma](https://github.com/RaviTharuma) | #11710, #11727, #11797, #11798, #11802, #11805, #11806, #11811, #12098, #12099, #12101, #12449, #12452, #12472, #12473, #12493, #12533, #12607, #12628, #12631, #12636, #12875, #12876, #12880, #12882, #12884 | +| [@rezjalibd](https://github.com/rezjalibd) | #12186 | +| [@RhianB14](https://github.com/RhianB14) | #11692 | +| [@rifqiawl](https://github.com/rifqiawl) | #11517, #11519, #11969 | +| [@rqzbeh](https://github.com/rqzbeh) | #11390 | +| [@Sabeekhann](https://github.com/Sabeekhann) | #11699, #11705 | +| [@santosraju99-hub](https://github.com/santosraju99-hub) | #11755, #11770 | +| [@Seramicx](https://github.com/Seramicx) | #11568 | +| [@Siva010](https://github.com/Siva010) | #12191 | +| [@solstxce](https://github.com/solstxce) | #11597 | +| [@soroush5](https://github.com/soroush5) | #12691 | +| [@tenshiak](https://github.com/tenshiak) | #12279 | +| [@TheDemonTuan](https://github.com/TheDemonTuan) | #11468, #11470, #11471, #11482, #11548, #11758, #11775, #11814 | +| [@thomasmaerz](https://github.com/thomasmaerz) | #12834 | +| [@tuandinh0801](https://github.com/tuandinh0801) | #11454 | +| [@turbolego](https://github.com/turbolego) | #11621, #11762, #11772, #11774, #11781, #12216 | +| [@Tushar49](https://github.com/Tushar49) | #11392 | +| [@ujjawalkaushik1110](https://github.com/ujjawalkaushik1110) | #11863 | +| [@vermasomesh835](https://github.com/vermasomesh835) | #11794 | +| [@vsd2807](https://github.com/vsd2807) | #11565, #11619 | +| [@wahidsadik371-coder](https://github.com/wahidsadik371-coder) | #12126 | +| [@watchingdogs](https://github.com/watchingdogs) | #12031 | +| [@wildcard](https://github.com/wildcard) | #11620 | +| [@xiaoyaner0201](https://github.com/xiaoyaner0201) | #11460, #11662, #12083 | +| [@Xxx91n](https://github.com/Xxx91n) | #11690 | +| [@yourspraveen](https://github.com/yourspraveen) | #11146 | +| [@yxyxy](https://github.com/yxyxy) | #11671 | +| [@ZaimMarzuki](https://github.com/ZaimMarzuki) | #11960 | +| [@Zartharas](https://github.com/Zartharas) | #11340 | +| [@zero-executioner](https://github.com/zero-executioner) | #11631 | +| [@diegosouzapw](https://github.com/diegosouzapw) | maintainer | + +--- ## [3.8.50] — 2026-08-25 _Living section — regenerated 2026-08-12 from all cycle commits (cycle open `ed2db6cb19` → tip). Bullets carry the merged PR and its author; direct pushes listed separately._ diff --git a/docs/i18n/bn/CHANGELOG.md b/docs/i18n/bn/CHANGELOG.md index 2a4e9ebae3..ccc1fe3768 100644 --- a/docs/i18n/bn/CHANGELOG.md +++ b/docs/i18n/bn/CHANGELOG.md @@ -8,16 +8,1119 @@ ## [3.8.51] — TBD -_Living section — cycle opened at the v3.8.50 freeze (parallel-cycle model). Bullets are aggregated from `changelog.d/` fragments at each `/generate-release` phase._ +_Living section — reconciled 2026-09-15 from all cycle commits (`091589089c` → `c0f92ec98a`, 916 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 | **123** | +| 📝 Commits in the cycle | **916** | +| 🔀 Pull requests referenced | **892** | +| 📋 Changelog entries | **919** | +| 🙌 Contributors credited in entries | **121** | +| 🤖 Automated dependency commits | 18 | + +**Entries by type** + +| Type | Count | +| --- | ---: | +| 🐛 Fixes | 592 | +| ✨ Features | 131 | +| 🧹 Chore | 93 | +| 📚 Docs | 42 | +| 🧪 Tests | 32 | +| ♻️ Refactor | 8 | +| 🏗️ Build | 7 | +| ⚡ Performance | 7 | +| ⚙️ CI | 2 | +| ⏪ Reverts | 2 | +| 📦 Dependencies | 2 | +| 🔒 Security | 1 | + +### 🏆 Top 25 contributors this cycle + +_By commits in `091589089c..c0f92ec98a`, author identities consolidated via `.mailmap` and the merged PR's GitHub login. Bots excluded._ + +| # | Contributor | Commits | +| ---: | --- | ---: | +| 🥇 | diegosouzapw | 323 | +| 🥈 | Bob.Hou (@HouMinXi) | 79 | +| 🥉 | Paco Cartones (@pacocartones) | 77 | +| 4 | Dizzle (@maxmad64bis) | 49 | +| 5 | Koosha Paridehpour (@KooshaPari) | 43 | +| 6 | Nguyen Thanh Dat (@ntdatt812) | 31 | +| 7 | Ravi Tharuma (@RaviTharuma) | 26 | +| 8 | Markus Hartung (@hartmark) | 22 | +| 9 | Webman (@jonlwheat2-gif) | 22 | +| 10 | backryun | 17 | +| 11 | anhtahaylove | 15 | +| 12 | Rafa Martins (@rafacpti23) | 10 | +| 13 | Syed Raheemuddin (@raheemuddin786) | 10 | +| 14 | Nguyễn Viết Tuấn (@TheDemonTuan) | 8 | +| 15 | MumuTW | 7 | +| 16 | Paijo (@oyi77) | 7 | +| 17 | Tobias Andersen (@turbolego) | 6 | +| 18 | Bl0ck (@Bl0ck154) | 5 | +| 19 | SHANMUGAPRIYAN (@geek007git) | 5 | +| 20 | KaspaPulse | 5 | +| 21 | Abhishek Sharma (@abhisheksharma2411) | 4 | +| 22 | Abhishek Divekar (@adivekar-utexas) | 4 | +| 23 | Andrew B. (@AndrianBalanescu) | 4 | +| 24 | NoxzRCW | 4 | +| 25 | opensource-elearning | 4 | ### ✨ New Features +- **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). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- Added Google AI Studio Gemini batch text-to-speech support through `POST /v1/audio/speech`. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- 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. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(routing):** subscription-first auto groupings — `auto/subscription` routes only through plan-included connections with a documented hard-stop overage and fails closed on exhaustion, while `auto/thrifty` orders the pool `subscription → keyless → free → cheap → premium` and steps up one rung at a time as each is exhausted. Billing class comes from a curated per-connection catalog (uncurated is treated as metered, never plan-included), both reuse STRICT_ZERO_COST's per-connection verification, and a quota reading whose `resetAt` has passed is now refreshed regardless of TTL so routing returns to plan capacity as soon as it resets ([#11146](https://github.com/diegosouzapw/OmniRoute/pull/11146)) — thanks @yourspraveen +- **feat(providers):** publish a management-authenticated versioned web-session credential contract from OmniRoute's canonical browser credential metadata ([#11340](https://github.com/diegosouzapw/OmniRoute/pull/11340)) — thanks @Zartharas +- **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)) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add Xquik X search with typed results, credential validation, REST routing, and MCP selection ([#11370](https://github.com/diegosouzapw/OmniRoute/pull/11370)) — thanks @kriptoburak +- **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)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(dashboard):** surface durable exclusive managed leases in the existing Sessions view, keeping leased clients visible across idle gaps while marking connections with in-flight work as active ([#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389)) — thanks @KaspaPulse +- **build(bun):** allow Turbopack bundler flag on Bun 1.4+ with configurable Webpack fallback ([#11471](https://github.com/diegosouzapw/OmniRoute/pull/11471)) — thanks @TheDemonTuan +- feat(api): add an opt-in `modelVisibilityAllowlist`/`modelVisibilityDenylist` settings pair to curate exactly which models `/v1/models` advertises, mirrored into every `auto/*` combo candidate pool so a denied model cannot be routed to via combo selection either (#11481) ([#11997](https://github.com/diegosouzapw/OmniRoute/pull/11997)) +- **feat(rankings):** the Free Provider Rankings page shows what each provider actually served over the last 24 h. It ranked by ELO alone, which left a provider that answers every call with an error in first place; the usage data was already served by the API but never requested. A provider with too small a sample shows a dash, not a number ([#11546](https://github.com/diegosouzapw/OmniRoute/pull/11546)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **feat(guardrails):** enforce a bounded, deterministic contract for Video Bridge transcripts — 256 cues, 4096 input code units and 4 KiB UTF-8 per cue, 64 KiB total text, malformed-Unicode rejection, focus-window scoping, cross-source reconciliation with contributing-source metadata, and a structural provenance trust boundary so caller JSON can never self-assert `embedded`/`audio-bridge` provenance ([#11652](https://github.com/diegosouzapw/OmniRoute/issues/11652)) ([#12009](https://github.com/diegosouzapw/OmniRoute/pull/12009)) +- **feat(video):** orchestrate optional Video Bridge audio extraction and Audio Bridge STT behind a dual opt-in (operator setting AND per-request signal) — a new loopback-only broker `mode=audio` operation shares the frame path's exact process queue, deadline, AbortSignal, and byte budgets to extract a bounded mono 16 kHz PCM WAV from the same already-downloaded video, then reuses the existing Audio Bridge transcription boundary; provider segment timing is preserved when available and marked coarse otherwise, and every failure degrades to a visual-only-safe partial instead of throwing (#11654). ([#12012](https://github.com/diegosouzapw/OmniRoute/pull/12012)) +- Add a tenant-bound Video Bridge drill-down lifecycle on top of the existing secure cache substrate: opaque hashed handles (never raw session/video identifiers), preview/standard/detail multiresolution variants resampled on read, response pagination capped at 8 frames and 32 MiB, and a new authenticated `/api/v1/video-bridge/drilldown` consumer route that stays disabled for remote access by default and denies cross-key access with the same response as a nonexistent handle (no existence oracle). ([#12006](https://github.com/diegosouzapw/OmniRoute/pull/12006)) +- **test(video):** Add the Video Bridge FU-07/FU-09 promotion-evidence harness (#11656) — a frozen Zod manifest schema covering the 8 required scenario kinds (static scenes, rapid cuts, late facts, fades, blur, small text, close events, visual prompt injection) with a minimum of 3 repetitions per case, deterministic declarative fixture recipes (`videoBridgePromotionFixtures.ts`), a pure medians/p95 metrics aggregator, a pure FU-07/FU-09 promotion-verdict evaluator applying the ticket's exact thresholds (missing token usage always holds), a digest-only persistence layer that never retains raw media or raw model responses, and a versioned per-model promotion allowlist shipped empty with every model defaulting to `hold`. The FU-07/FU-09 promotion verdicts themselves remain HOLD — they require a real evidence run against real models on VPS 192.168.0.15. ([#12008](https://github.com/diegosouzapw/OmniRoute/pull/12008)) +- **feat(video bridge):** "embedded" transcript provenance can now be legitimately earned instead of merely asserted — a bounded, allowlisted (`mov_text`/`subrip`/`webvtt`) subtitle probe runs through the loopback-only Video Bridge broker (at most 2 streams, 10s subdeadline bounded by the request deadline, 256 KiB output, 4096-code-unit lines), normalized through a bounded, ReDoS-safe WebVTT parser and Zod-validated end to end. The adapter always resolves to an explicit `success`/`absent`/`transient_failure` outcome — a subtitle failure never breaks the visual description path, and only a fingerprint-verified broker response (never a caller-declared label) can produce embedded cues (#11659). ([#12011](https://github.com/diegosouzapw/OmniRoute/pull/12011)) +- Default new Antigravity-family connections (agy CLI imports and Antigravity OAuth connects) to model auto-sync, so live model discovery lands in the synced catalog and `/v1/models` picks up freshly released upstream models (e.g. Gemini 3.7 Flash tiers) without code changes. Existing connections keep their current setting; the per-connection dashboard toggle remains the opt-out. (#11685 — thanks @MumuTW) ([#11887](https://github.com/diegosouzapw/OmniRoute/pull/11887)) +- **feat(ui):** enable React Compiler (`reactCompiler: true` + `babel-plugin-react-compiler`) for automatic memoization at build time ([#11783](https://github.com/diegosouzapw/OmniRoute/pull/11783)) — thanks @jonlwheat2-gif +- **feat(providers):** add SeekAi (`seekai.cc`) as an OpenAI-compatible New-API gateway — catalog id `seekai` (alias `ska`), `https://seekai.cc/v1`, live `/v1/models` via `passthroughModels`, aggregator-list membership so New-API balance detection can opt in. No referral/aff codes. ([#11786](https://github.com/diegosouzapw/OmniRoute/issues/11786)) ([#12557](https://github.com/diegosouzapw/OmniRoute/pull/12557)) — thanks @HouMinXi +- **feat(zai):** add GLM-5.3-Flash Coding Plan support (1M context, 128K output, vision, `low|high|max` reasoning) and route `zai` GLM-5.3-family API-key traffic through the OpenAI-compatible Coding Plan endpoint with native thinking defaults ([#11801](https://github.com/diegosouzapw/OmniRoute/pull/11801)) — thanks @Neuron-Mr-White +- **feat(combo):** choose how combo models are ordered — manual, provider, score, or name — via a sort control in the dashboard builder, persisted in `config.modelSort` and re-applied on load and after add ([#11812](https://github.com/diegosouzapw/OmniRoute/pull/11812)) — thanks @maxmad64bis +- **feat(free):** custom models can be marked free-tier via `customModels[].isFree`; `isFreeModel()` is the first door and `hidePaidModels` respects it even for providers outside the free budget ([#11843](https://github.com/diegosouzapw/OmniRoute/pull/11843)) — thanks @maxmad64bis +- **feat(nodejs):** add `5dive` as a `configure` target — `omniroute configure 5dive` / `omniroute setup-5dive` write a 5dive auth profile that points an agent fleet's `claude` seats at OmniRoute, with the root-only write, the loopback-vs-`https` endpoint rule and the per-seat model pin handled explicitly ([#11852](https://github.com/diegosouzapw/OmniRoute/pull/11852)) — thanks @5dive-bot +- **feat(sse):** treat `max` as a first-class reasoning-effort tier and clamp per model family (GLM 5.1+/DeepSeek V4+/Kimi K3+ keep native `max`; o1/MiniMax/Grok/Muse Spark clamp to their upstream ceiling) ([#11875](https://github.com/diegosouzapw/OmniRoute/pull/11875)) — thanks @Chewji9875 +- **feat(providers):** the provider plugin manifest now advertises a `usage-fetch` capability for the 40 providers that have a wired usage/quota fetcher, so external dashboards can read it from `GET /api/v1/provider-plugin-manifest` instead of parsing `open-sse/services/usage.ts` after every release. Discovery only — no new fetcher, no quota change, and the Dashboard quota widget stays gated by `USAGE_SUPPORTED_PROVIDERS`. `USAGE_FETCHER_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/fetcherProviders.ts`) and is re-exported from `services/usage.ts`, keeping the manifest module a light leaf instead of pulling the ~490-module usage dispatcher into the manifest route. ([#11903](https://github.com/diegosouzapw/OmniRoute/pull/11903)) — thanks @maxmad64bis / @pacocartones +- **feat(plugins):** `OMNIROUTE_PLUGINS_DIR` sets the directory the runtime plugin scanner reads — and the root the plugin manager installs into — overriding the `HOME`-derived default, so a Docker/K8s deployment can point straight at its bind-mounted plugin tree instead of moving `HOME` just to relocate the scan path. An image that exports no home no longer scans `/tmp/.omniroute/plugins` in silence: the resolved directory is logged once at startup as `scanner.dir_resolved`, naming the input that won. Unset, behaviour is unchanged. Distinct from the CLI-only `OMNIROUTE_PLUGIN_PATH`, which finds `omniroute-cmd-*` command packages and never reached this scanner ([#11906](https://github.com/diegosouzapw/OmniRoute/pull/11906)) — thanks @amaleta / @pacocartones +- **feat(leases):** add an explicit owner-authenticated status action that returns only the active lease's privacy-safe configured connection and provider labels, with generation fencing and no credential or internal-id disclosure ([#11910](https://github.com/diegosouzapw/OmniRoute/pull/11910)) — thanks @KaspaPulse +- **feat(providers):** import-from-file modal shows per-row API errors and ships a downloadable CSV template ([#12071](https://github.com/diegosouzapw/OmniRoute/issues/12071)) ([#12504](https://github.com/diegosouzapw/OmniRoute/pull/12504)) — thanks @HouMinXi +- **feat(providers):** dashboard search matches connection name and `baseUrl` so imported OpenAI-compat nodes surface on the provider card ([#12108](https://github.com/diegosouzapw/OmniRoute/issues/12108)) ([#12495](https://github.com/diegosouzapw/OmniRoute/pull/12495)) — thanks @HouMinXi +- **feat(routing):** add a `score` Auto router strategy that selects the highest configured weighted score and reuses `explorationRate`. ([#12155](https://github.com/diegosouzapw/OmniRoute/pull/12155)) — thanks @jacobsparts +- Add a runtime feature flag to disable universal context handoffs globally without changing the default behavior. ([#12167](https://github.com/diegosouzapw/OmniRoute/pull/12167)) — thanks @jacobsparts +- **feat(providers):** the provider plugin manifest now also advertises a `usage-supported` capability for the 46 providers whose usage API is accepted by the server and Dashboard routes, so integrators can distinguish "the server will serve quota for this provider" from "a fetcher is wired" without reading TypeScript. Discovery only — no fetcher or quota change. `usage-fetch` resolves on id or alias (the usage dispatcher accepts both); `usage-supported` resolves on id alone, matching the runtime guard `USAGE_SUPPORTED_PROVIDERS.includes(providerId)`. `USAGE_SUPPORTED_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/supportedProviders.ts`) and is re-exported from `providers.ts`, mirroring the `fetcherProviders` leaf from #11903 and keeping the manifest a light module. ([#12214](https://github.com/diegosouzapw/OmniRoute/pull/12214)) — thanks @maxmad64bis +- **feat(rankings):** order Free Provider Rankings by what each provider actually served — `GET /api/free-provider-rankings?sortBy=reliability` and a "Most reliable first" toggle on the page. Providers with too few calls to state a success rate keep their score order below the measured ones; the default order is unchanged ([#12218](https://github.com/diegosouzapw/OmniRoute/pull/12218)). — thanks @maxmad64bis +- **perf(sse):** defer `cloneLogPayload()` in the structured SSE collector until after the `maxEvents`/`maxBytes` cap check, eliminating ~9,800 wasted `structuredClone` calls per streaming response (65–71% faster `push()`). Reducer snapshot isolation restored for OpenAI and Responses summaries ([#12241](https://github.com/diegosouzapw/OmniRoute/pull/12241)) — thanks @PauloHSOliveira ([#12243](https://github.com/diegosouzapw/OmniRoute/pull/12243)) +- **feat(usage):** Devin CLI agentic quota (Codeium seat-management GetUserStatus) and OpenRouter key limits plus account credits now surface in Provider Limits ([#12256](https://github.com/diegosouzapw/OmniRoute/pull/12256) — thanks @Neuron-Mr-White) +- **feat(settings):** persist `headroomUrl` through Settings so status/start use the operator URL instead of only `HEADROOM_URL` ([#12306](https://github.com/diegosouzapw/OmniRoute/issues/12306)) ([#12487](https://github.com/diegosouzapw/OmniRoute/pull/12487)) — thanks @HouMinXi +- **feat(auto-combo):** Auto-Combo scoring can now weigh how often a provider/model has actually succeeded. The engine already carried that number on every candidate — 24 hours of usage history behind a ten-sample floor, real-time metrics otherwise — and the scoring function never read it, while the weight table described `stability` as if it did. `reliability` (`1 - failureRate`, with the same field precedence and the same rate-bounding the speed ranking already uses, so a corrupt reading means "nothing observed" rather than "fails every call") is now a declared factor shipping at weight `0`, so routing is unchanged until an operator gives it one, and the `stability` description now matches what that factor computes ([#12317](https://github.com/diegosouzapw/OmniRoute/pull/12317)) — thanks @maxmad64bis +- **feat(routing):** With `freeAccessPolicy: "strict"`, the read-only candidate listing (`GET /v1/auto-combo/{channel}/candidates`) no longer hides the candidates the zero-cost guard excludes — the same read-only transparency the resilience filter already honours (#9133). Each candidate now carries `freeAccessExclusion` saying why it would be kept out, and it tells an exhausted allowance apart from a quota reading that never arrived or went stale, which used to look identical from the outside. Routing is unchanged: the listing reports, it never enforces. The separate `excludeTosAvoid` guard still drops its candidates without a reason; that gap is now documented rather than closed ([#12319](https://github.com/diegosouzapw/OmniRoute/pull/12319)) — thanks @maxmad64bis +- **feat(radar):** The Radar catalog table now shows two facts it was already receiving from the feed and dropping on the floor: the per-model rate limits (requests and tokens, per minute and per day) in a new column, and a badge when a provider's terms state it may train on the prompts you send. A limit of zero renders as zero rather than "rate-only" — for a ceiling those are opposite facts — and a model with no training statement gets no badge, because an absent statement is not a guarantee ([#12320](https://github.com/diegosouzapw/OmniRoute/pull/12320)) — thanks @maxmad64bis +- **feat(radar):** explain Community, single-use, contributor, supporter, recovery, abuse, offers, and privacy rules before either Radar activation action, and remove the superseded fixed-PR grant promise from every UI locale ([#12342](https://github.com/diegosouzapw/OmniRoute/pull/12342)) +- **feat(gamification):** the dashboard Profile page now shows the real daily streak — `/api/gamification/level` returns `streak: { current, longest }` (per key with `apiKeyId`, operator-wide maximum otherwise) and the streak card reads it instead of a hard-coded 0 (#2403) ([#12377](https://github.com/diegosouzapw/OmniRoute/pull/12377)) — thanks @pacocartones +- **feat(gamification):** the dashboard leaderboard now shows each API key's display name under the Name column instead of a truncated key id; `GET /api/gamification/leaderboard` attaches `name` per entry (name only — no key material), while the shared ranking helper and the federation leaderboard stay id-only — thanks @pacocartones ([#12385](https://github.com/diegosouzapw/OmniRoute/pull/12385)) +- **feat(gamification):** enforce the documented 1000 XP/min per-API-key anti-cheat rate limit on the XP award path; over-limit awards are logged and skipped instead of persisted, and the sliding window now matches the timestamp format stored in `xp_audit_log` ([#2403](https://github.com/diegosouzapw/OmniRoute/issues/2403)) ([#12390](https://github.com/diegosouzapw/OmniRoute/pull/12390)) — thanks @pacocartones +- **feat(admin):** localize the gamification anomalies page — the loading state, the Status column and the Suspicious badge now come from the `common` catalog (new `common.suspicious` key propagated to every locale) — add it to the Gamification sidebar group as `gamification-admin` (`/dashboard/gamification/admin`), and expose the loading and empty states as polite `role="status"` live regions (#12401 — thanks @pacocartones) +- **feat(providers):** skip GitHub combo members missing from the live synced catalog, and drop Copilot models that are policy-disabled or hidden from the model picker ([#12473](https://github.com/diegosouzapw/OmniRoute/pull/12473)) — thanks @RaviTharuma +- **feat(i18n):** locale-expansion tooling — `npm run i18n:add-locale` adds a language to config, dashboard, docs mirrors, CLI, README/indexes and site in one command; browser, CLI and cookie detection resolve `uk`, `fil`/`tl`, `zh-Hant` (and the retired legacy `in` → `id`) via config aliases; `config/i18n.json` now ships in the npm package so the published CLI can read it; new real-translation ratio gate (`npm run i18n:check-ratio`, advisory) with a per-locale ratchet baseline; `run-translation --adopt` restores `.i18n-state.json`; `sync-language-bars` generates 🌐 bars from config; `validate_translation.py` loads its allowlist again. Retires the duplicate `in` locale (Indonesian mislabelled as Hindi) — 42 honest locales; saved `NEXT_LOCALE=in` / `OMNIROUTE_LANG=in` keep working. (#12496) +- **feat(api):** Emit gateway-measured `tokens_per_second` (TTFT excluded) on streaming usage and `X-OmniRoute-Tokens-Per-Second` when first-token latency is known ([#12616](https://github.com/diegosouzapw/OmniRoute/issues/12616)) ([#12631](https://github.com/diegosouzapw/OmniRoute/pull/12631)) — thanks @RaviTharuma +- **feat(opencode-plugin): map gateway cost/usage/tok/s onto OpenCode inference payloads** — the official plugin copies `X-OmniRoute-Response-Cost`, token counts, `X-OmniRoute-Tokens-Per-Second` / `usage.tokens_per_second`, TTFT, and the winning `X-OmniRoute-Model` onto the JSON/SSE body OpenCode already consumes. Missing tok/s is left unset (never `tokens / latency`). (#12636) — thanks @RaviTharuma +- **feat(combo):** add combo strategy `quota-weighted`: skip exhausted accounts, then weighted-draw by leftover / in-flight load; existing conversations stay pinned until the account is empty ([#12789](https://github.com/diegosouzapw/OmniRoute/pull/12789)) — thanks @HouMinXi +- **feat(models):** Account live listings become the chat catalog source for Claude, Codex, Copilot, and AGY; public metadata only fills prices on IDs those accounts already list. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **fix(models):** Union `agy` and `antigravity` live catalogs so `agy/gemini-3.8-flash-high` is not rejected after the prefix folds to `antigravity`. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **feat(opencode):** Add `@omniroute/opencode-plugin-v2`, an opencode v2 plugin that gives a v2 install a route to an OmniRoute gateway: it publishes models, combos and auto-combos into the host catalog, refreshes them lazily behind a 300s TTL, and keeps serving the last known catalog from an on-disk snapshot when the gateway is unreachable. The v1 plugin (`@omniroute/opencode-plugin`) is untouched and keeps working as before. The plugin adapts to the host instead of assuming one shape: it reads the shape the host seeds into the catalog draft and publishes the `api` block, the pre-`api` top-level fields, or both. The gateway key comes from the host credential store when one is connected, so no secret needs to sit in a config file; a source the gateway refuses is named in a warning instead of silently degrading the catalog to raw model ids; and tool schemas bound for a Gemini model are stripped of the JSON-Schema keywords Gemini rejects, so tool calling survives that route ([#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870)) — thanks @maxmad64bis +- feat(services): show sanitized CLIProxyAPI account health from its authenticated management API without exposing credentials, file paths, or raw account metadata (#6342) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add AnySearch free web search + URL extract (webFetch) with typed results, credential validation, REST routing, and MCP selection - fallback-only ([#11690](https://github.com/diegosouzapw/OmniRoute/pull/11690)) — thanks @Xxx91n +- **feat(dashboard):** display clamped `[0, 100]%` cached input token ratio in request logs table ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) — thanks @b3nw +- **feat(catalog):** add `OMNIROUTE_DISABLE_THINKING_LEVEL_VARIANTS` feature flag to optionally filter out thinking level variants from model catalog ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) — thanks @b3nw +- **feat(dashboard):** continuously export call logs to external analytics stores. A pluggable destination registry ships the full Logs-tab record set on an hourly `JobRegistry` cron, with a persisted per-destination cursor, batched inserts, a config UI rendered from each destination's own field descriptors, and a REST layer (`/api/log-export/*`) for CRUD, a connection test, and an on-demand run. A destination can opt into `includeBodies` to also ship the request and response payloads shown in the Logs detail pane, including the client and provider views of each call; this is off by default, and payloads inherit the dashboard's PII sanitisation, secret redaction and `noLog` handling. Google BigQuery is the first destination, using a service-account key stored encrypted at rest and streaming inserts keyed by call-log id, into a table that is day-partitioned on `timestamp` and clustered on `api_key_name`, `provider`, `model` and `status`. ([#11945](https://github.com/diegosouzapw/OmniRoute/pull/11945)) — thanks @dpozimski +- **feat(providers):** add MaxAI as a signed, OpenAI-compatible provider serving its 13 paid chat models (GPT-5.6 / Luna / Thinking, Claude 5 Sonnet, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite, Grok 4.1-fast / 4.5, DeepSeek V3.2 / R1, Llama 3.3 70B) through OmniRoute's `/v1` endpoint, with per-request HMAC-SHA1→SM3→AES request signing, live model + context-window discovery from `/models/get_config`, and prompted tool-calling translated to OpenAI `tool_calls` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI vision input — image_url content parts are forwarded inline in `message_content` to the 6 vision-capable models (GPT-5.6 / Luna / Thinking, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite) ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI image generation — 6 image models (gpt-image-1, dall-e-3, flux-1-schnell/dev/pro, sd3-medium) exposed through `POST /v1/images/generations` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI document RAG — inline base64 file/document attachments are uploaded to MaxAI (content-addressed `doc_id`) and attached to the chat via `doc_list` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** browserless MaxAI onboarding — email device-pair login (`/api/providers/[id]/login`) and signed access-token refresh, so a connection can be created and kept fresh without a real browser or Google OAuth ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** per-provider TLS impersonation profile (MaxAI presents a Windows Firefox-150 client fingerprint) so its bot-sensitive endpoints accept OmniRoute traffic ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** add **Nimble** as a web-search and web-fetch provider (`nimble-search`) — `/v1/search` routes to Nimble's search API at `lite` depth (locale, freshness and include/exclude domain filters mapped onto the shared request contract), and `/v1/web/fetch` routes to Nimble Extract, which covers all four fetch formats (`markdown`, `html`, `links`, `screenshot`) from a single call. One API key serves both surfaces. ([#11620](https://github.com/diegosouzapw/OmniRoute/pull/11620)) — thanks @wildcard +- The npm publish is automatic again, through npm Trusted Publishing (OIDC): the hosted `stage-npm` job publishes with a short-lived credential minted from GitHub's id-token — no `NPM_TOKEN`, no 2FA prompt, provenance attached. `publish_mode=staged` (owner approves with 2FA) and `direct` (token) remain available on `workflow_dispatch`. ([#11931](https://github.com/diegosouzapw/OmniRoute/pull/11931)) +- **feat(providers):** Add **Opper** as an API-key gateway provider — EU-hosted AI gateway with 700+ models from 30+ providers behind one OpenAI-compatible API and one key (`OPPER_API_KEY`); model ids use `provider/model` format (e.g. `anthropic/claude-sonnet-4-6`, `openai/gpt-5`); live model catalog at `https://api.opper.ai/v3/compat/models`; entry mirrors `requesty` (same shape, `passthroughModels: true`, no static seed) ([#11629](https://github.com/diegosouzapw/OmniRoute/pull/11629)) +- **feat(dashboard):** the `/dashboard/orchestration` snapshot hook now subscribes to the `agents` WebSocket channel (`agent.task.updated`) instead of `requests` as its refetch trigger, and relaxes its background poll from 5s to 30s while that WS connection is up — falling back to the tighter 5s cadence, reprogrammed live on any connect/disconnect transition, whenever the socket is down. ([#12409](https://github.com/diegosouzapw/OmniRoute/pull/12409)) +- **feat(dashboard):** Orchestration canvas quick wins — search box plus state/source/provider filter chips with a one-click clear, and per-source collapse/expand, all reflected in the URL so a filtered/collapsed view is shareable and survives a refresh; the detail drawer gained a "copy trace JSON" action and hardened error/empty-state and accessibility handling; the Agents-tab edges now animate traveling particles along active (running) connections; and the canvas node/edge status colors moved off fixed hex values onto theme-aware `--orch-status-*` CSS custom properties, so they adapt correctly to light/dark mode. ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- **chore(dashboard):** Orchestration UI hardening pass and the missing component/model test coverage it called for — `OrchestratorNode`/`ActivityNode`/`OverflowNode` rendering, the `?node=`/overflow-click page routing, the Agents-tab orchestrator-click no-op and `showCompleted` toggle, and the overview kanban's done-column sort order (#12270, #12271). ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- New `/dashboard/orchestration` page: live unified view of everything running — Cloud Agent, A2A and Conductor as a real-time graph (Agents tab), the combo cascade (Routing tab, reusing the Combo Live Studio) and a state kanban (Overview tab), with a detail drawer (trace, cost, approve/cancel). Read-only over existing APIs — no new backend. Canvas concept credit: PR #11815 design ([#12261](https://github.com/diegosouzapw/OmniRoute/pull/12261)) +- **feat(dashboard):** new "History" tab on `/dashboard/orchestration` — an Airflow-style grid of finished runs over a 24h/7d/30d preset window, one row per (source, identity), clicking a cell opens the existing detail drawer. It is backed by real persistence: A2A task lifecycle transitions are now written to the `a2a_tasks` table (purged after 30 days, configurable via `OMNIROUTE_A2A_HISTORY_RETENTION_DAYS`) and served by the new `GET /api/a2a/tasks/history` listing endpoint, with the task-detail route falling back to persisted history once a run leaves the in-memory snapshot. Conductor runs stay remote and are not persisted locally — the tab says so instead of silently omitting them. ([#12479](https://github.com/diegosouzapw/OmniRoute/pull/12479)) +- **feat(dashboard):** the orchestration detail drawer gained a "Repeat" action for Cloud Agent, A2A and Conductor tasks — a two-click confirm (click once to arm, click again within the confirm window to fire) re-submits the original prompt/input as a new run. The button is disabled with an explanatory tooltip whenever the original input can't be recovered from the loaded task detail (e.g. it never carried a prompt, or the detail failed to load). Two limitations of the A2A variant, by design: it targets the `/a2a` JSON-RPC endpoint, which authenticates with an API key only (`REQUIRE_API_KEY=true` or a configured `OMNIROUTE_API_KEY` makes a dashboard-session repeat answer `HTTP 400` — surfaced verbatim in the drawer's error line, never as a success), and the `message/send` call is SYNCHRONOUS: the POST blocks for the whole skill run, so the success confirmation only appears once the repeated task finishes. A dashboard-authenticated A2A creation path is deliberately left to a follow-up — widening the endpoint's auth posture is an operator decision, not a side effect of this feature. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(a2a):** A2A task execution now records which memories were consulted for the task's last user message as `metadata.memoryHits` (id/key/type/content-snippet) plus a `memory_hits` history event, purely for observability — the retrieved memory is never injected into a skill's prompt or behavior. Gated by the `OMNIROUTE_A2A_MEMORY_HITS` kill-switch (default enabled; set to `0` to skip the recall lookup entirely). The drawer's new "Memory used" section lists these hits for a2a tasks and is omitted whenever there are none. Known limitation: recall only resolves under the keyless posture — a keyed caller's task owner is a SHA-256 prefix of the API key, while memory rows are keyed by the database api-key id, and no hash→id lookup exists today, so the hit list stays empty for keyed callers. The recorded hits are also kept out of the task's own `input` (and therefore out of the persisted input and of the "Repeat" request body), so repeating a task never re-sends the previous run's memory snippets. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(providers):** add a Perplexity Agent API provider (`perplexity-agent` / `pplx-agent`) for Perplexity `/v1/responses`, including the documented Anthropic, OpenAI, Google, xAI, DeepSeek, Z.AI, Moonshot/Kimi, NVIDIA, and Perplexity model IDs plus Anthropic-model `max_output_tokens` compatibility. ([#12103](https://github.com/diegosouzapw/OmniRoute/pull/12103)) — thanks @AIB1TAL0S +- **feat(providers):** add RPD (Requests Per Day) limit to provider rate limit overrides across UI, schemas, DB, and i18n ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) — thanks @b3nw +- **feat(providers): add UC Direct (uncensored.com Developer API), the metered OpenAI-compatible surface.** A standard OpenAI-compatible passthrough (default executor) for uncensored.com's official REST API at `https://api.uncensored.com/api/v1`: `X-api-key` auth (never-expiring `uai_sk_live_` key), `POST /chat/completions` with streaming SSE and native tool-calling, and the full live metered catalog (82 models across 15 providers, discovered from the public `GET /v1/models`). Registered as provider `uc-direct` (alias `ucd`). Complements the un-metered `uc` persona provider — same models, metered credits and a plain API key instead of a subscription session. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(providers): add UC (uncensored.com), the un-metered subscription "persona" chat as an OpenAI-compatible provider.** A WebSocket web-app port: a durable Clerk credential mints a short-lived session token per connect (browserless — no API key), driving UC's persona socket. Ships the browserless email-code login (request → verify → harvest), the 19 verified persona models (Claude Opus, Gemini, Grok, GLM, Kimi, DeepSeek, MiniMax, incl. the uncensored variants), prompted `` tool-calling with a per-model code-style dialect + auto-cure retry for guardrailed models, live ``/reasoning split, streaming + non-streaming OpenAI responses, and full quota/auth error surfacing (paywall / message-limit / rate-limit → 429, invalid session → 401 re-login). Full multimodal parity via the persona blob-upload layer: **vision** (image input, 15 vision-capable models), **document RAG** (PDF/doc upload, server-side extraction), **image generation** (22 models), **video generation** (14 models, async signed-url → poll), and **TTS** (streaming MP3). Registered as provider `uc` (alias `ucn`). The metered OpenAI-compatible Developer API is a separate `uc-direct` provider. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(logs):** add copy-all button to request detail modal ([#11083](https://github.com/diegosouzapw/OmniRoute/pull/11083)) — thanks @AndrianBalanescu +- **feat(check):** make serviceKinds required and add the reverse-walk provider consistency gate ([#11392](https://github.com/diegosouzapw/OmniRoute/pull/11392)) — thanks @Tushar49 +- **feat(providers):** only advertise verified-working connections by default ([#11448](https://github.com/diegosouzapw/OmniRoute/pull/11448)) — thanks @alltomatos +- **feat(resilience):** add hierarchical concurrency admission ([#11493](https://github.com/diegosouzapw/OmniRoute/pull/11493)) +- **feat(providers):** derive + surface expiry for JWT-bearing web cookies (#11497) ([#11505](https://github.com/diegosouzapw/OmniRoute/pull/11505)) — thanks @oyi77 +- **feat(tunnels):** support named cloudflared tunnels via CLOUDFLARED_CONFIG ([#11561](https://github.com/diegosouzapw/OmniRoute/pull/11561)) — thanks @ggiak +- **feat(compression):** make proactive context-compression threshold a live setting ([#11564](https://github.com/diegosouzapw/OmniRoute/pull/11564)) — thanks @ragnar-claude +- **feat(resilience):** add provider peak-hour protection ([#11622](https://github.com/diegosouzapw/OmniRoute/pull/11622)) — thanks @Neuron-Mr-White +- **feat(providers):** add 1min.ai provider ([#11631](https://github.com/diegosouzapw/OmniRoute/pull/11631)) — thanks @zero-executioner +- **feat(dashboard):** collapsible JSON tree viewer for request/response payloads ([#11703](https://github.com/diegosouzapw/OmniRoute/pull/11703)) — thanks @hartmark +- **feat(routing):** add LiquidAI LFM2.5-2.6B free tier via OpenRouter ([#11752](https://github.com/diegosouzapw/OmniRoute/pull/11752)) — thanks @brick30llc-ctrl +- **feat(pricing):** add GLM-5.3-Flash pricing, model specs, and catalog registration ([#11830](https://github.com/diegosouzapw/OmniRoute/pull/11830)) — thanks @AndrianBalanescu +- **feat(routing):** add relayMode for schema-locked context handoffs ([#11839](https://github.com/diegosouzapw/OmniRoute/pull/11839)) — thanks @raheemuddin786 +- **feat(config):** add support for runtime system prompt configuration and hot-reloading ([#11841](https://github.com/diegosouzapw/OmniRoute/pull/11841)) — thanks @raheemuddin786 +- **feat(routing):** expand connection-aware quota prefilter across combo strategies (#11682) ([#11850](https://github.com/diegosouzapw/OmniRoute/pull/11850)) — thanks @HouMinXi +- **feat(models):** reactive discovery sync after Antigravity model-not-found 404 ([#11888](https://github.com/diegosouzapw/OmniRoute/pull/11888)) — thanks @MumuTW +- **feat(models):** auto-aliases and token limits from synced Antigravity discovery ([#11889](https://github.com/diegosouzapw/OmniRoute/pull/11889)) — thanks @MumuTW +- **feat(settings):** raise sticky round-robin limit caps to 1000 ([#12015](https://github.com/diegosouzapw/OmniRoute/pull/12015)) — thanks @adivekar-utexas +- **feat(resilience):** operator-configurable global credential health check interval ([#12043](https://github.com/diegosouzapw/OmniRoute/pull/12043)) — thanks @adivekar-utexas +- **feat(quota):** use official OpenCode Go usage API ([#12124](https://github.com/diegosouzapw/OmniRoute/pull/12124)) — thanks @ddarkr +- **feat(resilience):** default the credential health check sweep to 60 minutes ([#12138](https://github.com/diegosouzapw/OmniRoute/pull/12138)) — thanks @adivekar-utexas +- **feat(quality):** new-code mode for the complexity and dead-code ratchets (Clean as You Code) ([#12142](https://github.com/diegosouzapw/OmniRoute/pull/12142)) +- **feat(dashboard):** orchestration canvas — unified model + snapshot hook (part 1/2) ([#12156](https://github.com/diegosouzapw/OmniRoute/pull/12156)) +- **feat(usage):** add Kilo Code balance and Kilo Pass quotas ([#12178](https://github.com/diegosouzapw/OmniRoute/pull/12178)) — thanks @killer30001000 +- **feat(providers):** refresh vendored ChatGPT Web connector to v4.0.7 ([#12181](https://github.com/diegosouzapw/OmniRoute/pull/12181)) — thanks @backryun +- **feat(providers):** manual "Clear cooldown" action in the cooling panel ([#12224](https://github.com/diegosouzapw/OmniRoute/pull/12224)) — thanks @patrykkopycinski +- **feat(providers):** restore ChatGPT Web via clean-room browser transport ([#12239](https://github.com/diegosouzapw/OmniRoute/pull/12239)) — thanks @backryun +- **feat(sse):** wire the PROVIDER_PROFILES window gate into the global provider cooldown ([#12247](https://github.com/diegosouzapw/OmniRoute/pull/12247)) +- **feat(providers):** modernize CLOVA Studio chat and embeddings ([#12277](https://github.com/diegosouzapw/OmniRoute/pull/12277)) — thanks @backryun +- **feat(browser):** adopt Obscura as primary headless browser engine with Chromium fallback ([#12286](https://github.com/diegosouzapw/OmniRoute/pull/12286)) — thanks @opensource-elearning +- **feat(sse):** full language parity for output styles (es/de/fr/it/ru/zh + autoDetect) ([#12289](https://github.com/diegosouzapw/OmniRoute/pull/12289)) +- **feat(quality):** complete test:scoped — --full map rebuild, stdin selection, CI loader parity (#8084 D1) ([#12353](https://github.com/diegosouzapw/OmniRoute/pull/12353)) +- **feat(providers):** refresh Fable, Cursor, and Devin catalogs ([#12367](https://github.com/diegosouzapw/OmniRoute/pull/12367)) — thanks @backryun +- **feat(video):** redact transcript text from logs and durable memory (#12150 P1) ([#12427](https://github.com/diegosouzapw/OmniRoute/pull/12427)) +- **feat(dashboard):** parent-link, genuine-continuation badge, and modal perf fixes ([#12448](https://github.com/diegosouzapw/OmniRoute/pull/12448)) — thanks @hartmark +- **feat(dashboard):** adaptive context-budget dial on compression panel ([#12488](https://github.com/diegosouzapw/OmniRoute/pull/12488)) — thanks @HouMinXi +- **feat(providers):** refresh Z.ai Web models and browser transport ([#12524](https://github.com/diegosouzapw/OmniRoute/pull/12524)) — thanks @backryun +- **feat(video):** redact raw client-snapshot transcript fields in the detailed log (#12150 P2) ([#12528](https://github.com/diegosouzapw/OmniRoute/pull/12528)) +- **feat(providers):** refresh NVIDIA hosted models ([#12538](https://github.com/diegosouzapw/OmniRoute/pull/12538)) — thanks @backryun +- **feat(quota):** Moonshot Open Platform balance and TPD lock for custom nodes ([#12590](https://github.com/diegosouzapw/OmniRoute/pull/12590)) — thanks @HouMinXi +- **feat(video):** redact transcript in the in-memory pending-request snapshot (#12430 item 6) ([#12596](https://github.com/diegosouzapw/OmniRoute/pull/12596)) +- **feat(dashboard):** link a log entry's Conversation Context to its owning conversation ([#12646](https://github.com/diegosouzapw/OmniRoute/pull/12646)) — thanks @hartmark +- **feat(catalog):** eligibility-gated free-tier bucket ([#12669](https://github.com/diegosouzapw/OmniRoute/pull/12669)) +- **feat(db):** fail-closed previous_response_id continuation for redacted video turns (#12150 P2b) ([#12707](https://github.com/diegosouzapw/OmniRoute/pull/12707)) +- **feat(gamification): pay the documented `streak_bonus` and `badge_unlock` XP rewards.** `XP_REWARDS` listed both rewards but the award pipeline never paid them: the private reward table in `events.ts` omitted them, `updateStreak()` did not report when a streak extended, and badge unlocks carried no XP. Every request that extends a daily streak now pays `streak_bonus × streak length` once per UTC day (guarded by a same-day `xp_audit_log` check), and every badge unlocked through the pipeline pays `badge_unlock` once per badge (guarded by the `user_badges` primary key; `unlockBadge()` now reports whether it inserted). Bonus XP flows through the same `addXp` + level sync + global/weekly/monthly leaderboard path as action XP, so level-ups and rankings include it. The Radar supporter recognition unlock stays XP-free. (#12522 — thanks @pacocartones) +- **feat(providers):** advertise a `free-tier` capability in the provider plugin manifest for every provider with documented free models, so sidecars and dashboards can filter free-capable providers without reading the quota catalog ([#12786](https://github.com/diegosouzapw/OmniRoute/pull/12786)) — thanks @maxmad64bis +- **feat(providers):** Added EURouter as an OpenAI-compatible API-key gateway (`https://api.eurouter.ai/v1`), with live model discovery via `passthroughModels`. Its copy states that models are served by third-party upstreams listed per model, so an EU-based router is not read as EU data residency for inference. ([#13025](https://github.com/diegosouzapw/OmniRoute/pull/13025)) — thanks @ntdatt812 +- **feat(providers):** Added GreenPT as an OpenAI-compatible API-key provider (`https://api.greenpt.ai/v1`), with live model discovery via `passthroughModels`. No free-inference badge: the published docs describe a free API subscription billed per token, not a free tier. ([#13024](https://github.com/diegosouzapw/OmniRoute/pull/13024)) — thanks @ntdatt812 +- **feat(i18n):** 9 new locales — Greek (`el`), Croatian (`hr`), Serbian (`sr`), Lithuanian (`lt`), Estonian (`et`), Latvian (`lv`), Slovenian (`sl`), Maltese (`mt`), Irish (`ga`) — across the dashboard, docs mirrors, CLI, README and the site; OmniRoute now ships every official EU language (51 locales). (#13044) +- **feat(i18n):** 8 new locales — Kannada (`kn`), Malayalam (`ml`), Odia (`or`), Punjabi (`pa`), Nepali (`ne`), Sinhala (`si`), Burmese (`my`), Khmer (`km`) — across the dashboard, docs mirrors, CLI, README and the site (59 locales). The translator now restores the ICU literal escape around angle placeholders and splits oversized docs sections before translating. (#0000) ([#13660](https://github.com/diegosouzapw/OmniRoute/pull/13660)) +- feat(providers): **list Agnes 3.0 Flash as the current free chat model, drop retired 1.5 Flash, add Image 2.0/2.5 Flash plus Video 2.5/2.5 Flash, and discover the live `/v1/models` catalog (including the CN host `api.agnes-ai.cn`).** `agnes-1.5-flash` now forwards to `agnes-3.0-flash`. Video 2.5 polls `GET /v1/videos/{id}` (not the V2.0 `/agnesapi` contract). Live `/v1/models` (2026-09-09) no longer serves 1.5; the wiki marks it deprecated. 3.0 Flash is 512K context / 65,536 max output, same window as 2.5. CN-region keys use the existing per-connection base-URL field, default stays `apihub.agnes-ai.com`. ([#13120](https://github.com/diegosouzapw/OmniRoute/pull/13120)) — thanks @HouMinXi +- **feat(sse):** Codex and OpenAI catalogs list GPT-6 Astra with effort aliases (`-low` through `-ultra`); Codex CLI identity pins `@openai/codex@0.153.4` in lockstep with the image ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **feat(dashboard):** the orchestration History tab gained a "Compare runs" mode — toggling it turns each grid cell into a 2-item selection queue (a 3rd click drops the oldest pick), and picking two cells opens a side-by-side comparison panel instead of the usual detail drawer. The panel fetches both runs' detail the same way the drawer does (falling back to persisted history once a run leaves the live TTL window) and shows, per side: identity/source/state, start time, a signed `right - left` delta for duration/cost/event count, the event timeline aligned by index, and any memory hits. One side's fetch failing never blocks the other, and a delta is only ever computed when both sides have a finite value — otherwise it renders "—", never `NaN`. Comparing two runs from different sources or skills still works; a banner marks the deltas as informational rather than hiding them, since the two runs aren't a strict apples-to-apples pair. ([#12677](https://github.com/diegosouzapw/OmniRoute/pull/12677)) +- **feat(dashboard):** badge a conversation that never reached a clean stop ([#12717](https://github.com/diegosouzapw/OmniRoute/pull/12717)) — thanks @hartmark +- **feat(release):** reconcile-changelog tool + version-anchored fragment aggregation ([#12987](https://github.com/diegosouzapw/OmniRoute/pull/12987)) +- **feat(mcp):** carry context_length through MCP list_models_catalog projection (#12776) ([#13406](https://github.com/diegosouzapw/OmniRoute/pull/13406)) — thanks @KooshaPari +- **feat(cli):** make startup readiness budget configurable (#13369) ([#13433](https://github.com/diegosouzapw/OmniRoute/pull/13433)) — thanks @KooshaPari + ### 🐛 Bug Fixes +- **security(streaming):** sanitize generic mid-stream error messages before emitting OpenAI, + Responses, or Claude SSE failure frames and before diagnostic logging, while preserving raw + failures for internal classification and keeping client disconnects out of provider failure state. ([#12457](https://github.com/diegosouzapw/OmniRoute/pull/12457)) +- **fix(claude):** `blockExtraUsage: false` no longer lets 5h quota preflight skip the account; extra usage is billed after the session bar is gone, so the request must reach Anthropic ([#12803](https://github.com/diegosouzapw/OmniRoute/pull/12803)) — thanks @HouMinXi +- Sanitize HuggingChat conversation-creation and message-send transport failures before they reach client error bodies or provider logs. ([#12467](https://github.com/diegosouzapw/OmniRoute/pull/12467)) +- **fix(dashboard):** Keep local and theme-aware provider SVG icons at a definite layout size so Chromium does not collapse them to 0×0 after the v3.8.50 image-rendering change ([#12054](https://github.com/diegosouzapw/OmniRoute/pull/12054)) — thanks @ponkcore +- **fix(security):** Sanitize provider and runtime failures before public API, SSE and MCP responses and before persistent request, proxy and usage logs, preventing credentials, stack traces and host filesystem paths from crossing those boundaries while preserving stable error codes and useful diagnostics. ([#12506](https://github.com/diegosouzapw/OmniRoute/pull/12506)) +- **fix(cli):** `omniroute tunnel create` no longer crashes with `Cannot read properties of undefined (reading optsWithGlobals)` — removed the duplicate positional argument that caused Commander.js to misalign the action callback parameters ([#12295](https://github.com/diegosouzapw/OmniRoute/issues/12295)) ([#12368](https://github.com/diegosouzapw/OmniRoute/pull/12368)) — thanks @gonisulaimann +- Stop advertising Gemini Live-only models as supported audio endpoints until OmniRoute proxies the bidirectional Live protocol. ([#11710](https://github.com/diegosouzapw/OmniRoute/pull/11710)) — thanks @RaviTharuma +- **fix(kie):** reroute `flux/kontext` off the KIE Market `createTask` flow — it is catalogued with `isMarket: true` but has no Market catalog page, so KIE rejected it with "model name not supported"; it now hits the dedicated `POST /api/v1/flux/kontext/generate` / `GET /api/v1/flux/kontext/record-info` endpoints instead (#11296). ([#11985](https://github.com/diegosouzapw/OmniRoute/pull/11985)) — thanks @hartmark +- **fix(kie):** correct 12 more KIE Market catalog ids that were sent to `createTask` unchanged but diverge from KIE's documented upstream `model` values — GPT Image 2 T2I/I2I (drops the `gpt/` prefix), GPT Image 1.5 T2I/I2I (`gpt-image/` namespace), Seedream 5.0 Lite T2I/I2I (drops the `.0`), all 4 Flux 2 variants (`flux-2/` namespace, generic variant renamed `flex`), and Wan 2.7 Image / Image Pro (dash instead of dot) — each verified individually against the literal example request published on docs.kie.ai. `#11326`'s "everything else already matches" claim was wrong a second time (#11296); `z-image/4.0-*`/`z-image/4.5-*` and `flux/kontext` remain open, documented as unresolved in `KIE_MARKET_UPSTREAM_MODEL_IDS`'s comment pending further verification. ([#11667](https://github.com/diegosouzapw/OmniRoute/pull/11667)) +- **fix(dashboard):** `useApiKeySave.handleSaveApiKey` no longer forces a full upstream `/models` catalog sync on every non-curated provider connection save — callers can now pass `skipModelSync: true` to opt out, so a workflow that only wants to add one manual model no longer floods the provider's available-models list with hundreds/thousands of synced entries. The flag is a client-side intent signal only and is stripped before the connection payload is POSTed to `/api/providers`; default behavior (full sync on save) is unchanged when the flag is omitted (#11324) ([#11664](https://github.com/diegosouzapw/OmniRoute/pull/11664)) +- **Provider connections:** keep `tokenExpiresAt` when a connection is created. The create-path allowlist omitted it, so every insert stored NULL and the dashboard token badge could read a fresh connection as expired until its first background refresh ([#11368](https://github.com/diegosouzapw/OmniRoute/pull/11368)). — thanks @ntdatt812 +- **fix(combos):** the combo builder's precision-select, global-model-search, and manual-entry flows now serialize a model step's `model` string using the provider's already-computed routing-alias prefix (e.g. `oc/`) instead of rebuilding it from the raw canonical `providerId`, fixing the no-auth "OpenCode Free" provider (`opencode`) being routed to the unrelated paid "OpenCode Zen" provider (`opencode-zen`) because `opencode` doubles as a manual routing-prefix override ([#11433](https://github.com/diegosouzapw/OmniRoute/issues/11433)). ([#11670](https://github.com/diegosouzapw/OmniRoute/pull/11670)) +- **fix(radar):** the catalog feed cache now keeps `generatedAt`, the date the feed's data was built, next to `fetchedAt`, the date this install downloaded it (#11435). The feed schema requires that date and the sync path validates it, but the cache dropped it — so a feed fetched minutes ago and one carrying weeks-old figures looked identical to everything downstream, including the dashboard's "Last fetched" line. `getRadarCatalog().meta` and `GET /api/radar/status` now report both dates, the latter as its own field rather than folded into `version` — and omitted entirely for the offers and intel caches, which keep no build date, where a `null` would read as "unknown" rather than "never stored". The dashboard still shows only the fetch time; surfacing the build date there needs a new translated label and is left to a follow-up. Rows cached before migration 163 read back as `null`: unknown stays unknown instead of borrowing the fetch time. The referrals cache has persisted the same date since migration 142. — thanks @maxmad64bis +- **fix(cli):** postinstall no longer fills `JWT_SECRET` and `API_KEY_SECRET` in the installed package's `.env` (#11436). `.env.example` ships both blank on purpose: the server restores them from its durable store, or generates and persists them there, in `ensureSecrets()`. Pre-filling them defeated that — the file lives inside the package directory, so `npm i -g` replaced it and postinstall wrote _different_ values, while `ensureSecrets()` (which only acts on an empty variable) never got to restore the real ones. Both secrets rotated silently on every update, invalidating dashboard sessions and API-key CRCs. `STORAGE_ENCRYPTION_KEY` left the same list for the same reason in #1622; its comment pointed at a function that no longer exists and now names the real provisioning path. — thanks @maxmad64bis +- **fix(cli):** the CLI now says when a loaded `.env` lives inside the installed package directory (#11437). It already announces every env file it reads, without distinguishing the ones that survive an update from the one that does not: `npm i -g` replaces the package directory wholesale, so values set there are gone at the next update, silently. The warning names the durable path to move them to, and fires only when that file actually supplied a value — a file entirely shadowed by a durable one supplied nothing. A development checkout stays silent: there the same path is stable and documented in `SETUP_GUIDE.md`. — thanks @maxmad64bis +- **fix(free-models):** the shared `isFreeModel()` predicate no longer reports catalog entries marked `freeType: "discontinued"` as free, so `hidePaidModels` can't route to Pollinations' seven premium models that now require a paid key ([#11441](https://github.com/diegosouzapw/OmniRoute/pull/11441)) — thanks @maxmad64bis +- fix(ui): let AnySearch use the normal provider-icon fallback when LobeHub has no matching icon (#11449) +- Fixed the v3.8.50 Costs and Analytics dashboards so flat-rate Claude Code usage can be shown as an explicitly requested token-price estimate without changing default billed-cost semantics. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed archived usage retention so each request is priced individually instead of pricing a day's summed tokens once, which understated archived cost whenever a day mixed cache-heavy and ordinary requests. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed the Costs dashboard so it discloses when displayed figures include flat-rate token-price estimates instead of labelling them as billed spend, using the flag the analytics API already returns; the month-end projection and the CSV/JSON exports carry the same marker, and billed-cost mode is unchanged. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- **fix(combo):** attach the same combo-diagnostics trace (`poolSize`/`attemptOrder`/`excluded`/`terminalReason`, plus `x-omniroute-combo-*` headers) to the round-robin strategy's and the nested pipeline/fusion runtime-unit loop's "Maximum combo retry limit reached" 503 that the priority-strategy path already attaches for the identical terminal condition — previously those two paths returned a bare, contextless 503 ([#11462](https://github.com/diegosouzapw/OmniRoute/issues/11462)). ([#11665](https://github.com/diegosouzapw/OmniRoute/pull/11665)) +- **fix(bun):** use native `bun:sqlite` in `bootstrap-env` and `sync-env` to avoid loading `better-sqlite3` N-API addon during Bun startup ([#11468](https://github.com/diegosouzapw/OmniRoute/pull/11468)) — thanks @TheDemonTuan +- **fix(docker-bun):** make `Dockerfile.bun` install reliable with pre-install script helpers and native `bun:sqlite` smoke check ([#11470](https://github.com/diegosouzapw/OmniRoute/pull/11470)) — thanks @TheDemonTuan +- **fix(docker-bun):** remove every vendored `better-sqlite3` native addon from the Bun runtime image so startup cannot abort before the `bun:sqlite` fallback ([#11482](https://github.com/diegosouzapw/OmniRoute/pull/11482)) — thanks @TheDemonTuan +- **fix(autoCombo):** effort/alias model variants (`gpt-5.6-sol-xhigh`, `gpt-5.6`, cursor's `claude-4.6-opus-high`) inherit their base model's task fitness instead of falling to the wildcard 0.5 ([#11492](https://github.com/diegosouzapw/OmniRoute/pull/11492)) — thanks @MumuTW +- fix(db): rate-limit repeated Arena ELO leaderboard fetch-failure warnings instead of logging one per sync attempt (#11500) ([#11989](https://github.com/diegosouzapw/OmniRoute/pull/11989)) — thanks @hartmark +- **fix(db):** dedupe the raw `[Encryption] Decryption failed...` log line emitted by the lazy-decrypt views (`createLazyRowProxy`/`createLazyConnectionView`), which power `getProviderConnections()` and were re-triggering that line on every CredentialHealth/model-sync cycle for the same corrupt or stale-key credential — a fresh Proxy over a fresh row on every cycle meant the per-proxy memoization never suppressed it, unlike the dedup `decryptConnectionFields()` already had since [#9927](https://github.com/diegosouzapw/OmniRoute/issues/9927). Now shares that dedupe tracking so the line logs at most once per credential ([#11500](https://github.com/diegosouzapw/OmniRoute/issues/11500)). ([#11678](https://github.com/diegosouzapw/OmniRoute/pull/11678)) +- **fix(intelligence):** Arena ELO sync stores a synthesized base-model row for every effort/harness variant (`claude-opus-5-max` → `claude-opus-5`, `gpt-5.6-sol-xhigh (codex-harness)` → `gpt-5.6-sol`), so bare model requests reach the synced intelligence layer instead of the static fallback table; the hand-maintained `MODEL_ALIAS_MAP` — which collapsed generations (`gpt-5.5` → `gpt-5`) — is gone ([#11506](https://github.com/diegosouzapw/OmniRoute/pull/11506)) — thanks @MumuTW +- **fix(autoCombo,sse):** catalog hygiene — the static task-fitness table no longer ranks retired models above live ones (dead rows dropped, patterns matched on segment boundaries), the 7 deprecation aliases that forwarded to retired or non-existent models now point at the vendors' published replacements, an alias is no longer rewritten when the serving provider offers the id as-is, and a new `check:model-lifecycle` gate diffs both tables against a vendor lifecycle snapshot ([#11507](https://github.com/diegosouzapw/OmniRoute/pull/11507)) — thanks @MumuTW +- **fix(sse):** stop dropping resolved `thoughtSignature` values on parallel (multi tool-call) turns sent to Gemini 3.x — the claude→gemini and openai→gemini translators previously kept the signature only on the *first* function call of a message, causing Gemini to reject subsequent calls in the same turn with HTTP 400 "Function call is missing a thought_signature"; each function call now keeps its own resolved signature ([#11510](https://github.com/diegosouzapw/OmniRoute/issues/11510)). ([#11669](https://github.com/diegosouzapw/OmniRoute/pull/11669)) +- Treat Docker publishing from a non-default release branch as an expected skip instead of a failed workflow, while preserving the guard that prevents it from overwriting the `next` channel. ([#11527](https://github.com/diegosouzapw/OmniRoute/pull/11527)) — thanks @pacocartones +- fix(search): prefer credentialed providers over duckduckgo-free fallback (#11524) — the fallback-only loop ran before the credentialed-providers loop in `executeWebSearch`, making configured providers unreachable when `duckduckgo-free` was available. ([#11565](https://github.com/diegosouzapw/OmniRoute/pull/11565)) — thanks @vsd2807 +- **fix(sse):** cap the upstream headers-wait phase for STREAMING requests to a client-realistic ceiling (110s, under Codex's own ~120s hard client-abort window) instead of the flat 10-minute `FETCH_TIMEOUT_MS` default — that default was 5x longer than the body-phase readiness watchdog's own adaptive bound, so a request whose upstream never returned any response at all (not even headers, e.g. a stalled NVIDIA target behind a tool-heavy Responses→Chat translation) kept the client connection alive on keepalives only, guaranteeing the client's own patience ran out first with an opaque 499 instead of OmniRoute detecting and failing the stall fast. Non-streaming requests are unaffected — they keep the existing flat default (`open-sse/utils/fetchStartTimeoutPolicy.ts`) (#11526) ([#11668](https://github.com/diegosouzapw/OmniRoute/pull/11668)) +- **fix(ci):** protocol-clients E2E harness boots the peer-stamped custom server (`run-next.mjs dev`) instead of the bare `next` CLI, so `LOCAL_ONLY` locality resolves from the real TCP peer stamp and `/api/mcp/audit` answers 200 (open bootstrap preserved via an `OMNIROUTE_E2E_BOOTSTRAP_MODE=open` credential clear in `run-next.mjs`, pinned to loopback `HOST`) instead of a deterministic 403 (#11535) ([#11549](https://github.com/diegosouzapw/OmniRoute/pull/11549)) — thanks @jonlwheat2-gif +- **fix(docs):** the Auto-Combo engine is described with the number of factors it actually declares. Four different counts were in circulation — 6, 9, 13 and 14 across `AGENTS.md`, four guides and six code comments — while `DEFAULT_WEIGHTS` declares 15. `check:docs-counts` now reads that number from the source, so adding a factor without updating the prose fails CI ([#11545](https://github.com/diegosouzapw/OmniRoute/pull/11545)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **fix(quota-share):** in-flight leases are per request, so an aborted request's slot ages out instead of being kept alive by later traffic on the same connection ([#11547](https://github.com/diegosouzapw/OmniRoute/pull/11547)) — thanks @abhisheksharma2411 +- The free-tier summary route now serves the refreshed Radar catalog when the feed is active — the same numbers the dashboard shows — and states which catalog answered plus its real build date, instead of always reporting release-frozen figures with a stale curation date. The supporter-key live feed stays reserved to authenticated callers of the instance. ([#11550](https://github.com/diegosouzapw/OmniRoute/pull/11550)) — thanks @maxmad64bis +- **sse:** fix LiveWS/embed-WS servers crashing at startup under the Node/tsx runtime — `liveServer.ts`, `embedWsProxy.ts` and `apiBridgeServer.ts` imported `@/shared/utils/httpClientAbortGuard` without the `.mjs` extension, so the client-abort crash guard added by [#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556) was unreachable and every dependent test failed with `ERR_MODULE_NOT_FOUND` ([#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556)). ([#11671](https://github.com/diegosouzapw/OmniRoute/pull/11671)) — thanks @jonlwheat2-gif / @yxyxy +- fix(chat): shed any of the matched provider-node's routing identifiers (public prefix or internal id) before model lookup, so composites like `//` no longer reach upstream verbatim (#11557) — thanks @maxmad64bis +- **fix(cache):** a semantic-cache entry now expires on its TTL instead of surviving until the next UTC midnight, and `dbEntries` stops counting expired rows ([#11573](https://github.com/diegosouzapw/OmniRoute/pull/11573)) — thanks @ntdatt812 +- **fix(api):** `/v1/models` schedules its stale-while-revalidate rebuild through Next's `after()` again, so a stale catalog reaches the client before the rebuild blocks the event loop ([#11574](https://github.com/diegosouzapw/OmniRoute/pull/11574)) — thanks @ntdatt812 +- **fix(check):** the `known-symbols` executor conformance gate awaits the now-async `getExecutor()`, so it stops reporting all 142 registered aliases as dead symbols and can detect a lazy import that fails ([#11582](https://github.com/diegosouzapw/OmniRoute/pull/11582)) — thanks @ntdatt812 +- **fix(pack):** register `bin/cli/utils/volatileEnvPath.mjs` in `PACK_ARTIFACT_REQUIRED_PATHS`, so the prune can no longer silently drop a module the published CLI imports on every boot ([#11588](https://github.com/diegosouzapw/OmniRoute/pull/11588)) — thanks @ntdatt812 +- **i18n(vi):** translate the five reliability-column strings on the free-provider rankings page, which rendered as `__MISSING__:` markers in the Vietnamese UI ([#11589](https://github.com/diegosouzapw/OmniRoute/pull/11589)) — thanks @ntdatt812 +- **resilience:** restore the expired-connection retry-budget probe in the token-health sweep — the `!isGitHubAccessTokenOnlyConnection` carve-out reintroduced by #11608 contradicted the boundary pinned by #11592, so a GitHub connection parked at `expired` with retry budget remaining was never probed and could never self-heal ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)). ([#11672](https://github.com/diegosouzapw/OmniRoute/pull/11672)) — thanks @jonlwheat2-gif / @ntdatt812 +- **fix(dashboard):** Provider Stats now displays combo success rates on their native 0–100 percentage scale instead of multiplying them twice ([#11595](https://github.com/diegosouzapw/OmniRoute/pull/11595)) — thanks @pacocartones +- **fix(providers):** route DuckDuckGo Web Chat through the verified `duck.ai` endpoint, refresh its free model catalog, and avoid forwarding unsolved anti-abuse challenges ([#11597](https://github.com/diegosouzapw/OmniRoute/pull/11597)) — thanks @solstxce +- **fix(ui):** Console log Refresh and Copy controls now expose localized accessible names, keep copy actions visible on keyboard focus, and announce copy completion safely ([#11599](https://github.com/diegosouzapw/OmniRoute/pull/11599)) — thanks @pacocartones +- Dropped the stale-`.eslintcache` `restore-keys` fallback from both "Restore ESLint file cache" steps in `ci.yml`, so the blocking `Lint` job can no longer be served per-file verdicts computed under a different lint config, suppressions file or lockfile. `quality.yml` had already dropped it in #11963; `ci.yml` — the workflow that actually gates PRs — had not (#11600). ([#11996](https://github.com/diegosouzapw/OmniRoute/pull/11996)) +- fix(ci): pass `--pass-on-unpruned-suppressions` in `run-eslint-json.mjs` so the CI Lint job no longer fails when a suppression is merely orphaned by a genuine fix, mirroring the identical fix already in `validate-release-green.mjs` (#11600) ([#11983](https://github.com/diegosouzapw/OmniRoute/pull/11983)) — thanks @hartmark +- fix(handoff): enforce provider allowlist for universal handoff (#11602) — universal handoff now skips summarization when the selected summary model's provider is not included in the configured provider allowlist. ([#11619](https://github.com/diegosouzapw/OmniRoute/pull/11619)) — thanks @vsd2807 +- **fix(dashboard):** Show a stable error state when Search Analytics returns an HTTP error or malformed data ([#11603](https://github.com/diegosouzapw/OmniRoute/pull/11603)) — thanks @pacocartones +- **test(gamification):** pin the aggregate profile level to the XP-derived semantics of #11604 — `getAggregateXp()` now derives `currentLevel` from the summed XP (`calculateLevel(sum)`), not `MAX(stored current_level)`, and the #3484 fixture levels are aligned with the XP curve ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)). ([#11676](https://github.com/diegosouzapw/OmniRoute/pull/11676)) — thanks @jonlwheat2-gif / @pacocartones +- **fix(dashboard):** Keep the Profile level and progress aligned with aggregate XP, including bounded handling for invalid totals ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)) — thanks @pacocartones +- **fix(dashboard):** Prevent locked hidden badges from revealing their icon or opening private badge details before they are earned ([#11605](https://github.com/diegosouzapw/OmniRoute/pull/11605)) — thanks @pacocartones +- **fix(dashboard):** Restore keyboard focus after shared modals close and cancel delayed autofocus during cleanup ([#11607](https://github.com/diegosouzapw/OmniRoute/pull/11607)) — thanks @pacocartones +- **fix(dashboard):** Enable Enter and Space activation for clickable data-table rows without hijacking nested controls ([#11610](https://github.com/diegosouzapw/OmniRoute/pull/11610)) — thanks @pacocartones +- **fix(autoCombo,sse):** vendor-retired catalog ids are dropped from the auto-combo candidate pool and no longer win on leftover `arena_elo` / `user_override` rows; `getModelLifecycleDecision` consults `model-lifecycle.json` (prefix-stripped) so aggregator traffic is not `untracked` for ids the snapshot already knows ([#11625](https://github.com/diegosouzapw/OmniRoute/issues/11625)) ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)) — thanks @MumuTW +- **test(chatcore):** move Codex/Claude combo fixtures off models the lifecycle guard now rejects — `gpt-5.1-codex`/`gpt-5-codex` are vendor-retired (snapshot, #11626) and `claude-3-5-sonnet-20241022` is shut down, so native-passthrough and combo-fallback tests switched to `gpt-5.6-sol` and `claude-sonnet-4.6` ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)). ([#11675](https://github.com/diegosouzapw/OmniRoute/pull/11675)) — thanks @jonlwheat2-gif / @MumuTW +- fix(opencode-plugin): stop a free-tier budget that rounds up across a magnitude from rendering as `1000K`/`1000M` in the model picker — `fmtTokens` chose its unit from the raw token count and then rounded with `toFixed(1)`, so 999,950–999,999 printed as `1000K` rather than `1M` and just under a billion printed as `1000M` rather than `1B` ([#11684](https://github.com/diegosouzapw/OmniRoute/pull/11684)) — thanks @abhisheksharma2411 +- Cap prose-derived Antigravity quota resets at the configured model cooldown maximum while preserving authoritative Retry-After headers and Google RetryInfo hints. (#11695) Thanks @pacocartones. ([#11823](https://github.com/diegosouzapw/OmniRoute/pull/11823)) +- Compare `_rebaseline_*` notes structurally in the ratchet-bank verifier so an object-valued note no longer reports itself as rewritten on an unmodified tree — the nightly `bank-ratchet-shrinks` lane had been aborting on every run since 2026-08-11, leaving 41,672 lines of earned file-size shrinks unbanked (#11702). ([#11892](https://github.com/diegosouzapw/OmniRoute/pull/11892)) — thanks @MumuTW +- **fix(build):** `prepublish.ts` bundles the ChatGPT Web (Codex) MCP bridge through `runBuildTool()` instead of spawning `npx.cmd` raw, fixing the build crash on Node ≥ 20/Windows where `.cmd` shims cannot be spawned without a shell (EINVAL) ([#11704](https://github.com/diegosouzapw/OmniRoute/issues/11704)) ([#11706](https://github.com/diegosouzapw/OmniRoute/pull/11706)) — thanks @jonlwheat2-gif +- fix(codex): keep `parallel_tool_calls:false` on the translated Codex Responses Lite path (#11707) ([#11984](https://github.com/diegosouzapw/OmniRoute/pull/11984)) — thanks @hartmark +- **fix(tests):** the new Gemini Web Images retirement test (#11708) called the now-async `getExecutor()` synchronously, throwing `TypeError: getExecutor(...).getProvider is not a function` instead of asserting Gemini Web chat stays available. Awaited to match the R0.3 executor-registry refactor (#11220). ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(search):** Allow REST and MCP web-fetch requests to select the advertised `anysearch-search` provider explicitly ([#11716](https://github.com/diegosouzapw/OmniRoute/pull/11716)) — thanks @pacocartones +- **fix(providers):** Keep overnight peak-hour protection active after midnight according to the window's configured start day ([#11718](https://github.com/diegosouzapw/OmniRoute/pull/11718)) — thanks @pacocartones +- **fix(providers):** correct all five g4f.space gateway descriptions to document proof-of-work credits or member API keys, variable limits, and the remote prompt boundary instead of promising free keyless access at five requests per minute ([#11738](https://github.com/diegosouzapw/OmniRoute/pull/11738)). +- **fix(db):** `model_capabilities` is created by a migration instead of lazily on the first models.dev sync, so a clean install and an upgraded install converge on the same schema regardless of which features have run ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(ci):** `check:install-upgrade` now fails on an `npm` install truncated by ENOSPC (npm reports it as a warning and still exits 0), authenticates its health probe so the version assertion works against the hardened health payload, frees the clean-install tree before the upgrade phase, and no longer reports a schema divergence computed from a boot that never served ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(routing):** harden session-affinity key derivation against oversized explicit IDs and empty structural-payload collisions, while recognizing Gemini and common text request shapes ([#11746](https://github.com/diegosouzapw/OmniRoute/pull/11746)) — thanks @MumuTW +- **fix(packages/browser-pool):** regenerate `package-lock.json` so the `packages/browser-pool` workspace's locked `playwright`/`@types/node` (and transitives) match its `package.json` specs, fixing cache-only/offline installs (`npm ci --offline`, Nix `buildNpmPackage`) that previously failed with `ENOTCACHED` ([#11747](https://github.com/diegosouzapw/OmniRoute/issues/11747)) — thanks @benjaminkitt ([#11784](https://github.com/diegosouzapw/OmniRoute/pull/11784)) — thanks @geekyNads +- **fix(assets):** Stop distributing 79 provider and CLI logos without verified source/license provenance, keep every provider/tool available through the internal generic icon, and prevent those IDs from falling through to an external icon CDN. ([#11750](https://github.com/diegosouzapw/OmniRoute/pull/11750)) +- **fix(providers):** retire the common `chatgpt-web` / `cgpt-web` provider because its pre-key and proof-of-work provenance could not be cleared ([#11754](https://github.com/diegosouzapw/OmniRoute/pull/11754)) — explicit chat and image requests, slash-prefixed models, persisted aliases, proxy overrides, and combo targets now fail closed with a sanitized HTTP `410` / `PROVIDER_RETIRED` before upstream dispatch. Migration 163 tombstones matching connections and invalidates their active leases without deleting history or widening API-key allowlists. The independent `chatgpt-web-codex` / `cgpt-codex` provider and its MIT-noticed vendored implementation remain available. +- **fix(usage):** quota and usage refresh no longer 409 when an exclusive lease reserves the connection ([#11758](https://github.com/diegosouzapw/OmniRoute/pull/11758)) — thanks @TheDemonTuan +- Keep the embedding registry's vector width and `embedding` type on models when a synced model exists for the same id, so `/v1/models` no longer reports registry-described embedding models widthless or untyped (#11761) — thanks @rahilmavani +- Correct `google/gemini-embedding-001` on the OpenRouter route to 3072 dimensions, the width it returns when `dimensions` is not sent (#11761) — thanks @rahilmavani +- **fix(vision-bridge):** Restrict automatic vision routing and fallbacks to models available in authoritative live provider catalogs, including aliases and effort variants ([#11767](https://github.com/diegosouzapw/OmniRoute/issues/11767)) — thanks @pacocartones ([#11813](https://github.com/diegosouzapw/OmniRoute/pull/11813)) +- **fix(providers):** reclassify Cerebras as a one-time $5 signup credit (payment method required, 30-day validity), not a recurring no-card 1M tokens/day trial ([#11773](https://github.com/diegosouzapw/OmniRoute/issues/11773)) ([#12591](https://github.com/diegosouzapw/OmniRoute/pull/12591)) — thanks @HouMinXi +- fix(cli): stop prepublish from re-rebuilding the already-built ESM-only opencode-plugin dist (#11787) ([#11990](https://github.com/diegosouzapw/OmniRoute/pull/11990)) — thanks @hartmark +- **fix(db):** Fresh installs no longer log a non-fatal `no such table: compression_run_telemetry` warning when retention cleanup runs before the lazily-created telemetry table exists ([#11802](https://github.com/diegosouzapw/OmniRoute/pull/11802)) — thanks @RaviTharuma +- fix(sse): set X-OmniRoute-Selected-Connection-Id on successful combo dispatches so downstream consumers stop falling back to an empty connection id (#11810) ([#11986](https://github.com/diegosouzapw/OmniRoute/pull/11986)) — thanks @hartmark +- **fix(services):** embedded CLIProxyAPI lifecycle checks now use public `/healthz`, while model discovery uses the configured dedicated data-plane API key instead of the management password ([#11811](https://github.com/diegosouzapw/OmniRoute/pull/11811)) — thanks @RaviTharuma +- Fix persisted model steps returning an error in the playground route simulator, and warn when structural steps cannot be simulated. ([#11862](https://github.com/diegosouzapw/OmniRoute/pull/11862)) — thanks @pacocartones +- **fix(providers):** Antigravity's dynamic mitmAlias table no longer routes `gemini-3.7-flash-{high,medium,low}` to a literal tier-suffixed upstream id just because one connected account's own discovery listed it directly — those display ids always resolve through the safe `gemini-3.7-flash-tiered` static alias, so one account's Google-provisioned access no longer 404s every sibling account of the provider ([#11824](https://github.com/diegosouzapw/OmniRoute/issues/11824), [#11651](https://github.com/diegosouzapw/OmniRoute/issues/11651)) ([#11988](https://github.com/diegosouzapw/OmniRoute/pull/11988)) — thanks @hartmark +- **fix(dashboard):** Expose Profile loading, errors, page structure, and XP progress to assistive technologies ([#11838](https://github.com/diegosouzapw/OmniRoute/pull/11838)) — thanks @pacocartones +- **fix(socks):** `Agent.connectTimeout` now bounds both SOCKS handshake and TLS connect, and `family === null` no longer falls back to `fetch-socks` ([#11842](https://github.com/diegosouzapw/OmniRoute/pull/11842)) — thanks @maxmad64bis +- **perf(compression):** OOM mitigations for large payload hashing, memoization, and token estimation ([#11844](https://github.com/diegosouzapw/OmniRoute/pull/11844) — thanks @AndrianBalanescu) +- Excluded Next.js Node File Trace manifests (`*.nft.json`) from the published npm tarball. They are build-time metadata and are never read while serving, but had grown to 668.7 MB — 61% of the package — which pushed the upload past the registry limit and made `npm publish` fail with `413 Payload Too Large`. ([#11864](https://github.com/diegosouzapw/OmniRoute/pull/11864)) +- **fix(cli):** keep the global OmniRoute server `--base-url` separate from provider-node `--endpoint` values in add, update, and validate commands ([#11860](https://github.com/diegosouzapw/OmniRoute/pull/11860)) — thanks @pacocartones +- **fix(config):** Nous Research's `Hermes-4-405B` model now displays as "Hermes 4 405B (Nous Research)" in both the provider registry and the free-model catalog, instead of the mislabelled "Hermes 4 7B" ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#11993](https://github.com/diegosouzapw/OmniRoute/pull/11993)) +- **fix(provider/nous):** inject required user= tag into Nous Research inference requests to resolve upstream 400 "missing tags" error ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#12044](https://github.com/diegosouzapw/OmniRoute/pull/12044)) +- **fix(build):** `npm run build` now fails in one second with a named package and a copy-pasteable fix when npm silently drops an externalised optional native dependency, instead of dying four minutes in with `Module not found: Can't resolve 'better-sqlite3'` ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- **fix(install):** `postinstall` no longer throws `ReferenceError: isAndroid is not defined` — failing the whole `npm install` — when the `better-sqlite3` rebuild fallback times out; the manual-fix guidance is reachable again ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- Fixed the Alibaba free-tier allowlist test that went red on its own once the shipped catalog's `validUntil` (2026-08-27) passed, leaving every PR and `main` with a failing `Unit Tests (1/8)`. The test now builds its own packs with dates it controls, and covers the expired-pack fallback that production has actually been serving. ([#11867](https://github.com/diegosouzapw/OmniRoute/pull/11867)) +- Split the npm registry upload into its own GitHub-hosted job. npm refuses `--provenance` from a self-hosted runner (`422 ... Only "github-hosted" runners are supported`), which blocked the v3.8.50 publish; the heavy verification cannot move to a hosted runner, so it now hands the proven tarball over instead. ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(ui):** the shared `DataTable` loading state no longer reads its decorative ⏳ glyph out to assistive technology, and now carries the same `role="status"` / `aria-live="polite"` / `aria-busy="true"` semantics as `PageLoading` ([#11869](https://github.com/diegosouzapw/OmniRoute/pull/11869)) — thanks @pacocartones +- **fix(api):** Bound the `timeout` accepted by `POST /api/providers/[id]/login` to the same 15s–600s window the Adobe Firefly and Conol browser-login paths already enforce, so a request can no longer pin a headful browser and the single active-login slot with an arbitrarily large poll budget. ([#11871](https://github.com/diegosouzapw/OmniRoute/pull/11871)) — thanks @pacocartones +- **fix(api):** Stop the tunnel and MITM routes from returning raw child-process error text, which disclosed host paths, binary install locations and Tailscale `tskey-*` credentials in a body some of these routes serve to non-loopback callers, and return a real 400 instead of a framework 500 when a tunnel request body fails validation. ([#11872](https://github.com/diegosouzapw/OmniRoute/pull/11872)) — thanks @pacocartones +- **fix(open-sse):** stream TTFT/ITL are sampled from a monotonic clock (`performance.now`) instead of `Date.now`, so an NTP correction or wall-clock jump can no longer inflate a genuine TTFT or produce a negative one that the `ttft >= 0` guard silently drops — protecting the router's OTel/EWMA/`usage_history`/speed-ranking signals ([#11873](https://github.com/diegosouzapw/OmniRoute/pull/11873)) — thanks @pacocartones +- **fix(providers):** xAI `reasoning_effort: "xhigh"` now reaches grok-4.6+ instead of being silently clamped to `"high"` ([#11879](https://github.com/diegosouzapw/OmniRoute/pull/11879)) — thanks @NoxzRCW +- **fix(dashboard):** the providers page no longer crashes into the error boundary when a provider id collides with an `Object.prototype` member (`constructor`, `__proto__`); icon lookups are own-property guarded ([#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)) — thanks @NoxzRCW +- **fix(skills):** injected skill tools declared in shorthand (`{"content": "string"}`) now forward valid JSON Schema, unblocking providers that validate tool schemas strictly such as Zhipu GLM on the Console Go tier ([#11881](https://github.com/diegosouzapw/OmniRoute/pull/11881)) — thanks @NoxzRCW +- **fix(api):** `POST /api/playground/simulate-route` now surfaces `combo-ref` and `provider-wildcard` persisted combo steps with a specific warning (naming the referenced combo, or the unresolved `provider/model` wildcard) instead of folding them into a generic "unsupported step" count; a `provider-wildcard` step is also now included as an unresolved target so the operator can see it is in the route (ported from [#11882](https://github.com/diegosouzapw/OmniRoute/pull/11882) — thanks @NoxzRCW). ([#11930](https://github.com/diegosouzapw/OmniRoute/pull/11930)) +- **fix(translator):** the streaming OpenAI→Claude translator keeps upstream usage, including prompt-cache tokens, when it arrives on a trailing `choices: []` chunk (Fireworks and any upstream using `stream_options.include_usage`) ([#11883](https://github.com/diegosouzapw/OmniRoute/pull/11883)) — thanks @NoxzRCW +- **fix(cli):** `omniroute update --apply` now tells you explicitly whether a running server was detected and, if so, that you must run `omniroute restart` to apply the update — it never restarted anything and previously implied the update was already live once files were installed. The dashboard's npm-mode Update flow (`/api/system/version`) now tries OmniRoute's own PID-file-managed supervisor before falling back to pm2, and reports an honest "restart required" step instead of a silent pm2-only "skipped" that read like a completed update. The server-side latest-version lookup backing the dashboard's update banner also gained `--prefer-online`, closing the same stale-npm-cache class already fixed in the CLI's own copy for #4376 ([#11885](https://github.com/diegosouzapw/OmniRoute/issues/11885)). ([#12005](https://github.com/diegosouzapw/OmniRoute/pull/12005)) +- **fix(resilience):** clear persisted LKGP pins when a target suffers connection/provider exhaustion or is skipped before dispatch due to cooldown/exhaustion/unavailability, preventing subsequent requests from repeatedly prioritizing known-dead providers ([#11911](https://github.com/diegosouzapw/OmniRoute/issues/11911)). ([#12013](https://github.com/diegosouzapw/OmniRoute/pull/12013)) — thanks @HouMinXi +- **fix(sse):** the OpenAI→Claude stream translator now defers the terminal `message_delta`/`message_stop` emission until the real usage block has arrived (or a genuine end-of-stream flush forces it) instead of emitting it immediately on `finish_reason` — previously, when the trailing usage-only chunk (`{"choices":[],"usage":{...}}`) arrived *after* the `finish_reason` chunk (the normal order for Fireworks/vLLM/Together and other `stream_options.include_usage` upstreams), the client-visible `message_delta` still carried stale/zero usage even though `state.usage` was internally corrected too late to matter (ported from [#11915](https://github.com/diegosouzapw/OmniRoute/pull/11915) — thanks @HouMinXi). ([#11933](https://github.com/diegosouzapw/OmniRoute/pull/11933)) +- **fix(sse):** `stripStore()` now forces `store=false` for stateless OpenAI-compatible Responses-API targets (unless the connection has explicitly opted in via `providerSpecificData.openaiStoreEnabled`), instead of only handling the `openai`/`agentrouter` cases — a client-supplied `store` value previously passed through untouched to backends that don't actually persist responses server-side ([#11916](https://github.com/diegosouzapw/OmniRoute/pull/11916) — thanks @HouMinXi). +- **fix(models):** custom provider-node models (synced, custom, and alias-backed) now appear under their configured prefix in the unified catalog when the operator's model-id prefix mode is canonical, instead of being dropped whenever alias-inclusion was otherwise disabled ([#11918](https://github.com/diegosouzapw/OmniRoute/pull/11918) — thanks @HouMinXi). +- **fix(models):** the unified model catalog now suppresses stale static registry models (including effort-tier variants) for any provider whose active connection has an authoritative live synced catalog, not only providers already using exclusive-synced-listing — a connection with `providerUsesAuthoritativeLiveCatalog` previously kept serving both the live-synced models and the stale static rows side by side ([#11919](https://github.com/diegosouzapw/OmniRoute/pull/11919) — thanks @HouMinXi). +- **fix(ui):** `ProviderIcon`'s three remaining unguarded lookups (`PROVIDER_ICON_ALIASES`, `LOCAL_SVG_ALIASES`, `THEMED_SVGS`) now use `Object.hasOwn()` own-property checks — a provider id such as `constructor` or `__proto__` previously resolved through the prototype chain instead of falling through to the unknown-provider CDN fallback (`getLobeProviderIcon()` itself was already guarded by [#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)); `ProviderPageHeader`'s `color` field is also now optional, matching the rest of the component's defensive typing (ported from [#11920](https://github.com/diegosouzapw/OmniRoute/pull/11920) — thanks @HouMinXi). ([#11935](https://github.com/diegosouzapw/OmniRoute/pull/11935)) — thanks @NoxzRCW +- fix(ollama): preserve multi-byte UTF-8 content split across stream chunks in the Ollama NDJSON transform, which previously corrupted CJK/emoji into U+FFFD (#11921) — thanks @pacocartones +- **fix(providers):** OrcaRouter chat requests now target `/v1/chat/completions` instead of the bare `/v1` API root, fixing the upstream `404 Invalid URL (POST /v1)` ([#11923](https://github.com/diegosouzapw/OmniRoute/pull/11923)). — thanks @echel0nn +- **fix(plugins):** deliver the `onStreamComplete` event to disk-installed plugins. The event shipped in v3.8.50 (#9669) was emitted internally but had no plugin-facing wiring, so no plugin could ever subscribe: the manifest schema silently dropped `hooks.onStreamComplete`, and the loader/manager only knew the seven legacy hooks. `onStreamComplete` is now a declarable manifest hook, wired through the loader and registered by the manager like the other hooks, and its payload carries a `requestId` so consumers can correlate the stream-completion event with the originating request ([#11934](https://github.com/diegosouzapw/OmniRoute/pull/11934)) — thanks @amaleta / @pacocartones +- **fix(db):** the Qdrant embedding-model dropdown now lists local/self-hosted providers (Ollama, LM Studio, vLLM, etc.) — an active connection is treated as "configured" when the provider allows an optional API key, not only when it has a real key or OAuth, so a running local embedding provider is no longer hidden from the picker ([#11949](https://github.com/diegosouzapw/OmniRoute/issues/11949)) ([#11995](https://github.com/diegosouzapw/OmniRoute/pull/11995)) +- **fix(providers):** Vertex AI Anthropic partner-model discovery now calls the Model Garden `v1beta1` publisher list (`/v1beta1/publishers/anthropic/models`, global) and parses its `publisherModels` envelope, so Claude models auto-synced from Vertex populate the active live catalog and route at request time instead of returning `Model '' is not available in the active live catalog` ([#11991](https://github.com/diegosouzapw/OmniRoute/issues/11991)) — thanks @fabioluissilva ([#11998](https://github.com/diegosouzapw/OmniRoute/pull/11998)) +- **fix(cli):** support `--base-url` alongside `--endpoint` in `omniroute nodes add`, `update`, and `validate` subcommands to prevent global `--base-url` shadowing issues ([#11999](https://github.com/diegosouzapw/OmniRoute/issues/11999)). ([#12033](https://github.com/diegosouzapw/OmniRoute/pull/12033)) — thanks @HouMinXi +- **fix(providers):** `cloudflare-ai` no longer refuses image content parts for every Workers AI model ([#12002](https://github.com/diegosouzapw/OmniRoute/pull/12002)) — the plain-string `content` requirement behind #2539 is carried by the _model_ schema, not by the `/ai/v1/chat/completions` endpoint (measured: an all-text part array returns 200 on `@cf/mistralai/mistral-small-3.1-24b-instruct`, `@cf/meta/llama-4-scout-17b-16e-instruct` and `@cf/meta/llama-3.3-70b-instruct-fp8-fast`, and 400 on the text-only `@cf/qwen/qwen2.5-coder-32b-instruct`). `transformRequest()` flattened every array and threw on the first non-text part (#6390), so image input was refused for vision-capable Cloudflare models that accept it. All-text arrays are still flattened — the one shape every model accepts — while an array carrying a non-text part is passed through untouched, so the attachment is still never silently dropped. Regression guards: `tests/unit/cloudflare-ai-image-parts-6390.test.ts`. — thanks @davidlinfr +- **fix(resilience):** decouple the limiter-managed execution backstop from the queue-wait budget — new `requestQueue.executionMaxWaitMs` (env `RATE_LIMIT_EXECUTION_MAX_WAIT_MS`, default 600000 = 10 min) now feeds Bottleneck's post-dispatch `expiration`, while `requestQueue.maxWaitMs` keeps its documented queue-wait semantics. Previously the queue-wait budget doubled as the execution expiration, so legitimate long-running calls on non-incremental gateways (whole generation buffered before the first upstream byte, e.g. Console Go / Command Code tiers serving GLM models) were killed mid-flight at the queue budget with a false 504 `RATE_LIMIT_EXECUTION_TIMEOUT` — the local limiter undercut the provider-aware upstream fetch-start timeouts. The surfaced 504 message now names `requestQueue.executionMaxWaitMs`; the error keeps the #4165 guarantees (disclaims an upstream timeout, preserves the Bottleneck error as `cause`, branded code + trusted provenance, classified request-scoped so combo falls back). A real queue-wait bound (the `Promise.race` around `limiter.schedule()` sketched in #9533) remains future work. (#12025) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **Call logs:** keep the `error` field when an artifact exceeds the storage cap, instead of replacing it with the omission marker. The error is the only field that says *why* a request failed and is typically ~90 bytes next to the multi-hundred-KB bodies that trip the cap, so dropping it left a size-limited row undiagnosable — a provider outage, a local timeout and an upstream 400 all rendered identically. It is now preserved at every fallback stage, truncated to 4KB if it is itself large ([#12026](https://github.com/diegosouzapw/OmniRoute/issues/12026)). ([#12095](https://github.com/diegosouzapw/OmniRoute/pull/12095)) — thanks @ntdat812 +- **fix(diagnostics):** preserve the error field (truncated to 4KB with a `[truncated: …]` suffix) in every call-log artifact size-limit fallback stage. Previously the minimal fallback replaced the error with `[omitted: call log artifact size limit exceeded]`, so an oversized artifact row showed nothing about WHY the request failed — e.g. 91 of 847 opencode-go 504 rows on one production instance were undiagnosable from the dashboard. Oversized request/response bodies are still omitted exactly as before; the error cap is independent of the payload sizes that tripped the fallback. (#12026) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **fix(sse):** OpenAI Responses clients that declare the native `web_search` tool now receive a spec-shaped `web_search_call` output item with `action.sources` alongside the preserved function-call round-trip, so search results executed through OmniRoute's own search backend are consumable by standard Responses clients (Codex, pi-web-access, …). ([#12031](https://github.com/diegosouzapw/OmniRoute/pull/12031)) — thanks @watchingdogs +- **fix(cli):** use in-thread alias resolver hooks on modern runtimes to avoid deprecation noise and improve Node.js forward compatibility ([#12073](https://github.com/diegosouzapw/OmniRoute/issues/12073)). ([#12083](https://github.com/diegosouzapw/OmniRoute/pull/12083)) — thanks @xiaoyaner0201 +- **fix(combo):** an operator-set **Agent Features → Context length** on a combo is now honored at request time. The value was persisted and advertised through `/v1/models`, but `resolveComboContextLimit()` never consulted it — so a multi-target combo whose members carry no per-model window fell through to the provider's generic `defaultContextLength` (openrouter 128000, command-code 200000) and rejected large requests with `Input exceeds context window … limit 128000` despite the combo being explicitly sized much larger. An identical single-target combo worked, because it collapses to its concrete target before the guard runs. Invalid values (0/negative/NaN/Infinity) are ignored, so the existing target → combo-min → fallback order is unchanged. ([#12090](https://github.com/diegosouzapw/OmniRoute/pull/12090)) — thanks @adivekar-utexas +- **fix(sse):** passthrough streams now estimate usage on finish when upstream closes without usage even with `stream_options.include_usage` — avoids `0 tokens / 0%` for providers that stay silent (and correctly handles trailing empty-choices usage) ([#12151](https://github.com/diegosouzapw/OmniRoute/pull/12151)) — thanks @maxmad64bis +- **fix(combos):** clearing an agent feature in the combos editor now persists — unchecking context cache protection, or emptying the system message or tool filter, sends an explicit `null` instead of dropping the field from the `PUT` body, which the update merge read as "leave unchanged" ([#12177](https://github.com/diegosouzapw/OmniRoute/pull/12177)) — thanks @foreveryh / @ntdatt812 +- **fix(memory):** self-hosted embedding endpoints now vectorize — the vector width is measured from the first embedding that comes back instead of being read from a registry that cannot describe them, so `vec_memories` is created and memories stop piling up unvectorized behind a green health check ([#12180](https://github.com/diegosouzapw/OmniRoute/pull/12180)) — thanks @kanade-hoshino / @ntdatt812 +- **fix(translator):** the leading `system` message now reaches Responses-API upstreams when its `content` is a content-part array — it was read as `typeof content === "string" ? content : ""`, so a prompt-caching client (Anthropic `cache_control`, the shape LiteLLM and the Anthropic SDK emit) had its entire system prompt replaced by an empty `instructions`. The request was still accepted with a normal `prompt_tokens` count, so the model answered with no instructions and nothing in the response said they were missing. Mid-conversation system turns already handled the array shape ([#7056](https://github.com/diegosouzapw/OmniRoute/pull/7056)); only the first one did not ([#12206](https://github.com/diegosouzapw/OmniRoute/issues/12206)). Regression guard: `tests/unit/translator-openai-responses-system-content-parts.test.ts`. ([#12207](https://github.com/diegosouzapw/OmniRoute/pull/12207)) — thanks @Gorillaz322 +- **fix(free-tier):** `/api/free-tier/summary` no longer computes its totals from a Radar feed built before the catalog the running release ships. When the cached feed is older — or carries no build date at all — the route answers from the shipped catalog, resolved through the operator's local model state so disabled and tombstoned models stay out of the numbers ([#12215](https://github.com/diegosouzapw/OmniRoute/pull/12215)). — thanks @maxmad64bis +- **fix(oauth):** Keep a Claude personal workspace and a Team organization as separate connections — they share the same email and `accountUUID`, so the email-only OAuth dedup let the second login overwrite the first account's tokens; `organizationUUID` now disambiguates them, the way `workspaceId` does for Codex ([#12222](https://github.com/diegosouzapw/OmniRoute/pull/12222)) — thanks @davidebaraldo +- **fix(resilience):** a 402 on a single paid model of a passthrough/gateway provider (e.g. `kilo-gateway`, `ollama-cloud`) no longer terminalizes the whole connection with a never-auto-recovered `credits_exhausted` status — only the paid model is locked out, so free models on the same key keep serving. 402 variant of [#3027](https://github.com/diegosouzapw/OmniRoute/issues/3027). Single-credential providers are unaffected — a 402 there is still treated as the key being genuinely out of credit ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239) / [#10616](https://github.com/diegosouzapw/OmniRoute/issues/10616)) ([#12242](https://github.com/diegosouzapw/OmniRoute/issues/12242)) — thanks @brick30llc-ctrl ([#12266](https://github.com/diegosouzapw/OmniRoute/pull/12266)) — thanks @geekyNads +- **fix(sse):** trust `finish_reason: "length"`/`"max_tokens"` over the reasoning-consumed-token ratio in response quality validation, so a reasoning model truncated below the old 90% threshold correctly fails and retries instead of returning empty content as a silent "success" ([#12262](https://github.com/diegosouzapw/OmniRoute/pull/12262)) — thanks @brick30llc-ctrl +- **fix(settings):** `PUT /api/settings/cache-config` now persists `alwaysPreserveClientCache` to the flat general settings the runtime cache-control policy actually reads; previously the value landed in the databaseSettings "cache" section and was silently ignored, so the endpoint had no effect on `cache_control` passthrough ([#12304](https://github.com/diegosouzapw/OmniRoute/pull/12304)) — thanks @davidebaraldo +- **fix(grok-cli):** treat omitted SuperGrokPro `creditUsagePercent` as 0% used so Provider Limits still renders a weekly bar (proto3 zero-elision) ([#12312](https://github.com/diegosouzapw/OmniRoute/pull/12312)) — thanks @HouMinXi +- **fix(combo):** Expose the two Auto-Combo scoring factors nobody could set — the combo validation schema and the dashboard weight sliders both declared 13 of the scorer's 15 factors, so `connectionDensity` (spreads load across a provider's connections) and `quality` were dropped on save and offered nowhere. The sliders also shipped their own default table that differed from the engine's on every non-zero factor and summed to 1.05, so the percentages shown next to them added up to 105%. Both lists now match `DEFAULT_WEIGHTS`, and a test keeps them there. Note that a combo whose stored `weights` omitted the two keys was effectively running with them at zero and the other thirteen renormalized upward; it now runs with the engine's intended distribution, so its routing does shift ([#12314](https://github.com/diegosouzapw/OmniRoute/pull/12314)) — thanks @maxmad64bis +- **fix(docs):** The free-tier reference no longer says its numbers come "confidence tagged per row" — no catalog entry carries a confidence tag and the API serves none, so every figure on that page is an estimate of the same, unstated quality. The page now states what an entry does vouch for: an independently documented hard stop (set by hand with the source in a comment, never defaulted to `true`) and a prompt-training disclosure, both with live counts the `check:docs-counts` gate keeps honest ([#12318](https://github.com/diegosouzapw/OmniRoute/pull/12318)) — thanks @maxmad64bis +- **fix(usage):** `adobe-firefly` and `firefly` have had a working usage fetcher since Adobe Firefly landed, but neither was ever added to the registration list, so the provider-plugin manifest, `genericQuotaFetcher` and the free-access quota cache all reported them as having no usage support — while `USAGE_SUPPORTED_PROVIDERS` said the opposite. Both are now declared, which also means their credit balance is fetched like any other declared provider's: `registerGenericQuotaFetchers` now registers a generic quota fetcher for them, and `resolveFreeAccessState` no longer returns early. A test holds the registration list to the dispatcher's switch in both directions, which is what the module's own docstring already asked for in prose ([#12321](https://github.com/diegosouzapw/OmniRoute/pull/12321)) — thanks @maxmad64bis +- **fix(quota):** drop the generic quota cache (agy / Antigravity / Claude OAuth) on an upstream 429 so reset-aware scoring does not keep a 60s stale snapshot, and force-refresh the next usage fetch so inner provider caches cannot recache the same window ([#12325](https://github.com/diegosouzapw/OmniRoute/pull/12325)) — thanks @HouMinXi +- **fix(combos):** deleting a combo now clears its persisted LKGP pins instead of leaving unreachable `key_value` rows behind ([#12326](https://github.com/diegosouzapw/OmniRoute/issues/12326)) ([#12425](https://github.com/diegosouzapw/OmniRoute/pull/12425)) — thanks @KrzysiekSko +- **fix(sse):** Keep ZWNJ (U+200C) and ZWJ (U+200D) in assistant text, reasoning and tool-call arguments — Persian/Kurdish half-space (`ارائه‌دهنده`), Arabic/Indic shaping and emoji sequences no longer lose them; the response de-obfuscation now removes joiners only between ASCII word characters, where the request side inserts them ([#12186](https://github.com/diegosouzapw/OmniRoute/issues/12186)) — thanks @rezjalibd ([#12359](https://github.com/diegosouzapw/OmniRoute/pull/12359)) — thanks @pacocartones +- **fix(resilience):** count resolved upstream 5xx results against the provider circuit breaker on the chat path — `CircuitBreaker.execute()` no longer reads a resolved `{ success: false, status: 5xx }` as a success that cancels the call-site failure, so a provider answering 503s now trips its breaker instead of staying `CLOSED` at `failureCount: 1`; single-model and combo dispatches are each accounted exactly once ([#12254](https://github.com/diegosouzapw/OmniRoute/issues/12254)) ([#12360](https://github.com/diegosouzapw/OmniRoute/pull/12360)) — thanks @pacocartones +- **fix(providers):** resolve the Codex quota auto-ping model from the live provider catalog and lifecycle registry instead of the retired `gpt-5.1-codex-mini`, and pause the ping with one actionable warning when no selectable Codex model exists rather than retrying a shut-down id every cooldown window ([#11905](https://github.com/diegosouzapw/OmniRoute/issues/11905)) ([#12361](https://github.com/diegosouzapw/OmniRoute/pull/12361)) — thanks @pacocartones +- **fix(api):** keep the `{created, data}` wrapper on combo-routed `/v1/images/generations` responses and default Codex image results to `b64_json` on both `/v1/images/generations` and `/v1/images/edits` so Codex CLI's built-in `image_gen` can decode them ([#12268](https://github.com/diegosouzapw/OmniRoute/issues/12268)) ([#12362](https://github.com/diegosouzapw/OmniRoute/pull/12362)) — thanks @pacocartones +- **fix(sse):** Name the shadowed custom provider node when a built-in provider id/alias (e.g. `openference` → `of`) reserves the prefix of an existing OpenAI/Anthropic-compatible node, so the runtime `No active credentials for provider: ` error explains that the prefix routed to the built-in and never reached the node's healthy connections, instead of contradicting the dashboard ([#11943](https://github.com/diegosouzapw/OmniRoute/issues/11943)) — thanks @morpheus9393 ([#12365](https://github.com/diegosouzapw/OmniRoute/pull/12365)) — thanks @pacocartones +- **fix(i18n):** wrap `ccOnboardingKeyPlaceholder` in ICU single quotes across all 43 locales so angle brackets render literally instead of being parsed as rich-text tags, which crashed the Claude Code onboarding block with `INVALID_MESSAGE: INVALID_TAG` ([#12302](https://github.com/diegosouzapw/OmniRoute/issues/12302)) ([#12369](https://github.com/diegosouzapw/OmniRoute/pull/12369)) — thanks @gonisulaimann +- **fix(models):** publish `effort_tiers` on Kimi K3's synced base-model entries (`kmca/k3`, `kmca/k3-256k`) so catalog-only clients (OpenCode, plain SDK pickers) can see and select the reasoning tiers (`low`/`high`/`max`) the synced metadata already carried — the `isSkippedEffortProvider` gate no longer suppresses tier visibility on those base entries, while synthetic `-` variant generation stays prevented and Codex/GLM base models remain excluded unchanged ([#12299](https://github.com/diegosouzapw/OmniRoute/issues/12299)) ([#12371](https://github.com/diegosouzapw/OmniRoute/pull/12371)) — thanks @gonisulaimann +- **fix(guardrails):** keep `auto`/`auto/*` virtual combos exempt from the Vision Bridge `fixedModel` credential guard so a combo target is passed through instead of silently falling back to global auto-selection ([#12237](https://github.com/diegosouzapw/OmniRoute/issues/12237)) ([#12373](https://github.com/diegosouzapw/OmniRoute/pull/12373)) — thanks @pacocartones +- **fix(combo):** capability-filter exhaustion caused by `max_tokens` above every target's known output limit now reports that reason (requested `max_tokens` vs the pool's highest known ceiling) instead of the unrelated "supports structured output" message ([#12229](https://github.com/diegosouzapw/OmniRoute/issues/12229)) — thanks @DW-MediaLab ([#12374](https://github.com/diegosouzapw/OmniRoute/pull/12374)) — thanks @pacocartones +- **fix(auth):** the `least-used` account strategy now prefers accounts without backoff before falling back to oldest `lastUsedAt`, the same tie-break `round-robin` already applies, so a failover no longer lands on a just-rate-limited account for a single request ([#12279](https://github.com/diegosouzapw/OmniRoute/issues/12279)) — thanks @tenshiak ([#12375](https://github.com/diegosouzapw/OmniRoute/pull/12375)) — thanks @pacocartones +- **fix(docker):** the `chatgpt-web-codex-browser` image now finds the Chrome binary under `chrome-linux64/` (Chrome for Testing layout in `playwright:v1.62.0-noble`) as well as the legacy `chrome-linux/`, so the container no longer crash-loops with `exec: --headless=new: not found` ([#12024](https://github.com/diegosouzapw/OmniRoute/issues/12024)) ([#12376](https://github.com/diegosouzapw/OmniRoute/pull/12376)) — thanks @pacocartones +- **fix(providers):** declare `groq/compound` and `allam-2-7b` as non-reasoning models in the curated Groq registry so `reasoning_effort` / `output_config.effort` / `thinking` from Claude Code are stripped instead of forwarded, which Groq rejected with HTTP 400 ([#12134](https://github.com/diegosouzapw/OmniRoute/issues/12134)) ([#12379](https://github.com/diegosouzapw/OmniRoute/pull/12379)) — thanks @pacocartones +- **fix(executors):** `OpencodeExecutor` no longer forces a direct connection when the connection has a proxy assigned in Proxy Management but no per-account proxies: the single-account fast path used to wrap the upstream dispatch in the direct-egress sentinel, discarding the ambient proxy context the chat handler had pinned from `proxy_assignments`, so API-key `opencode`/`opencode-go` connections egressed from the host IP (and hit geoblocks) despite the assignment. The direct pin is now applied only when no ambient proxy context exists ([#11894](https://github.com/diegosouzapw/OmniRoute/issues/11894) — thanks @hizzt) ([#12380](https://github.com/diegosouzapw/OmniRoute/pull/12380)) — thanks @pacocartones +- **fix(api):** `GET /v1/models` with `MODELS_CATALOG_PREFIX_MODE=canonical` (or `?prefix=canonical`) now lists providers whose registry alias is undefined or equal to their own id (Antigravity, Antigravity CLI and other self-aliased built-ins) — their single `provider/model` id was dropped by the alias/canonical duplicate guard in the static, synced, custom and alias-backed catalog loops ([#12058](https://github.com/diegosouzapw/OmniRoute/issues/12058)) — thanks @cheynetom ([#12381](https://github.com/diegosouzapw/OmniRoute/pull/12381)) — thanks @pacocartones +- **fix(translator):** Drop replayed `thinking` blocks that carry no signature (the shape produced from cross-provider `reasoning_content`) instead of stamping the default Claude signature on them, which Anthropic rejected with `400 Invalid signature in thinking block` on the next turn served by an Anthropic rung ([#12105](https://github.com/diegosouzapw/OmniRoute/issues/12105)) — thanks @atescivitci-cmd ([#12386](https://github.com/diegosouzapw/OmniRoute/pull/12386)) — thanks @pacocartones +- **fix(cli):** Resolve Bun's `--preload` polyfill path against the package root instead of `dist/`, so `omniroute` installed with `bun install -g` no longer crashes at startup with `error: preload not found …/dist/open-sse/utils/setupPolyfill.ts` ([#11980](https://github.com/diegosouzapw/OmniRoute/issues/11980)) — thanks @joglomedia ([#12387](https://github.com/diegosouzapw/OmniRoute/pull/12387)) — thanks @pacocartones +- **fix(providers):** `gemini-business` now publishes its model catalog — `/v1/models` and `/v1/providers/gemini-business/models` list the 12 enterprise Gemini ids the executor understands instead of returning an empty list (#12107) ([#12389](https://github.com/diegosouzapw/OmniRoute/pull/12389)) — thanks @pacocartones +- **fix(db):** install `busy_timeout` before the SQLite connection's first statement so a process opening the database while another one closes its WAL connection waits out the transient EXCLUSIVE lock instead of dying with `database is locked`, and recognise the drivers' real BUSY/PROTOCOL/IOERR errors as transient in the corruption probe so the same lock no longer renames the database away as corrupt; deflakes `cross-process contenders never both acquire the same connection` (#12394 — thanks @pacocartones) +- **fix(chat-admission):** derive the `chat_admission_busy` 503 `Retry-After` from observed heavyweight-lease occupancy — the larger of the exhausted `OMNIROUTE_CHAT_ADMISSION_QUEUE_MS` window and the time since capacity last turned over, capped at 60 s — instead of a fixed 1 s (structural) / 2 s (byte-stage) hint that invited Codex/agent fan-out clients to re-send ~1 MiB `/v1/responses` bodies every second into a gate held for the whole SSE lifetime; an idle gate keeps the historical floors ([#12135](https://github.com/diegosouzapw/OmniRoute/issues/12135)) (#12395 — thanks @pacocartones) +- **fix(api-manager):** the API key permissions modal no longer silently drops `allowedCombos` entries its Combo picker cannot render — routing-rule names such as `rt-*`, which the backend already honours — when "All" is clicked and the key is switched back to "Restrict"; those entries now survive the toggle, are listed read-only under the combo list so the count and the list agree, and are saved back verbatim instead of persisting `[]` (deny-all) (#12397 — thanks @pacocartones) +- **fix(catalog):** write the NUL separator of the catalog connection memo key, the provider serviceKind memo key, the Video Bridge promotion group key and a JSON-exactness test fixture as the `\u0000` escape instead of a raw byte — same runtime value, but the raw byte made git, GitHub and ripgrep treat those files as binary (hidden PR diffs, silently skipped searches); a guard test now keeps raw NUL bytes out of `src/`, `open-sse/` and `tests/` (#12403 — thanks @pacocartones) +- **fix(providers):** add `CLAUDE_CODE_CLIENT_VERSION` and `GITHUB_COPILOT_CLI_VERSION` env overrides so Anthropic/Copilot client-version gates can be unblocked without a rebuild ([#12417](https://github.com/diegosouzapw/OmniRoute/issues/12417)) ([#12632](https://github.com/diegosouzapw/OmniRoute/pull/12632)) — thanks @HouMinXi +- **fix(db):** back-fill `last_ping_at` and `last_pinged_reset_key` on `provider_connections` during schema reconciliation so divergent lineages that skipped `123_quota_auto_ping` still accept quota auto-ping writes ([#12470](https://github.com/diegosouzapw/OmniRoute/pull/12470) — thanks @KooshaPari) +- **fix(ci):** document MIT exceptions for `@eloqnt/{config,format-json,format-po}` (next-intl transitive; locked tarballs omit `license`) and keep the A2A lifecycle vitest off the real SQLite persistence seam ([#12581](https://github.com/diegosouzapw/OmniRoute/issues/12581)) ([#12595](https://github.com/diegosouzapw/OmniRoute/pull/12595)) +- OpenCode plugin `/v1/models` catalog fetch now waits 30s by default and attaches HTTP `statusCode` on 401/5xx so host fallback plugins can hop instead of seeing an untyped AbortError/UnknownError. ([#12607](https://github.com/diegosouzapw/OmniRoute/pull/12607)) — thanks @RaviTharuma +- **CI:** the OpenAPI security-tier gate now mirrors `isAlwaysProtectedPath()` in full — it also reads `ALWAYS_PROTECTED_API_PATTERNS`, so the pattern-gated credential routes (`/api/providers/{id}/{claude,codex}-auth/{export,apply-local}`, GHSA-5926-2w35-7h4q) no longer report as unannotated. (#12605) +- **fix(api):** GET /v1/models no longer waits forever on a hung coalesced catalog rebuild; cold-path waits are bounded (`CATALOG_BUILD_TIMEOUT_MS`, default 8s) and a last-good 200 is served when the rebuild times out ([#12627](https://github.com/diegosouzapw/OmniRoute/issues/12627)). ([#12628](https://github.com/diegosouzapw/OmniRoute/pull/12628)) — thanks @RaviTharuma +- **fix(cursor):** a non-array `tool_calls` on an assistant message no longer crashes the cursor request translator with a `TypeError`; both loops now require an array ([#12689](https://github.com/diegosouzapw/OmniRoute/issues/12689)) ([#12691](https://github.com/diegosouzapw/OmniRoute/pull/12691)) — thanks @soroush5 +- **fix(chat):** Chat Completions no longer return empty `content` after a server-owned memory or skills tool; the first provider send and account/model recovery now share one pipeline so a follow-up round-trip can fill the reply ([#12696](https://github.com/diegosouzapw/OmniRoute/issues/12696)) — thanks @HouMinXi ([#12867](https://github.com/diegosouzapw/OmniRoute/pull/12867)) +- **fix(dashboard):** batch-deleting provider keys no longer toasts failure after a successful delete when the confirm button's click event is forwarded as `onAfter` ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) — thanks @HouMinXi +- **fix(glm):** drop the extra 16th argument to `createSSETransformStreamWithLogger` that TypeScript rejected (TS2554) and that never reached the TransformStream ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) ([#12770](https://github.com/diegosouzapw/OmniRoute/pull/12770)) — thanks @HouMinXi +- **fix(dashboard):** pass `nodeMap` into Runtime `QuotaGroup` so a quota monitor in error/exhausted/alerting no longer throws `ReferenceError: nodeMap is not defined`. ([#12868](https://github.com/diegosouzapw/OmniRoute/pull/12868)) — thanks @HouMinXi +- Restricted API keys whose `allowedModels` lists a combo name no longer skip every combo member at pre-dispatch (`ALL_TARGETS_SKIPPED`). Inner-target filtering still applies when the allow-list is a provider prefix or `disableNonPublicModels` is on ([#12899](https://github.com/diegosouzapw/OmniRoute/pull/12899)). — thanks @HouMinXi +- **fix(combos):** Effort-suffixed combo members inherit the base model's `model_context_overrides` row so priority order is not inverted on large requests ([#12926](https://github.com/diegosouzapw/OmniRoute/pull/12926)) — thanks @HouMinXi +- **fix(catalog):** Picker-added `customModels` enter the dispatch-time live catalog so combo members and bare inference no longer 400 ([#12934](https://github.com/diegosouzapw/OmniRoute/pull/12934)) — thanks @HouMinXi +- **fix(docker):** re-derive the Docker build's worker-pool memory budget from the MEASURED ~4.5 GB per-process RSS (the issue owner's own VPS dmesg OOM-killer reproduction) instead of the stale 2560 MB/worker inference #11419 shipped, and lower `OMNIROUTE_BUILD_WORKERS` 3 → 2 so 1 parent + 1 page-data worker (2 processes × 4.5 GB = 9 GB) fits the 12.288 GB (75%) budget on a 16 GB GitHub Actions runner — the previous default (1 parent + 2 workers = 13.5 GB) still overcommitted the runner and kept "Publish to Docker Hub" failing with `cannot allocate memory` after #11419 merged (#7518). ([#11663](https://github.com/diegosouzapw/OmniRoute/pull/11663)) +- **fix(electron):** the packaged Windows desktop build now passes the #7592 cold-restart smoke end to end. Five defects were found and fixed on the way: (1) optional-pack staging failed on any machine whose `tar` is GNU tar — it reads the drive letter in an absolute `-f C:\...` archive path as a remote rsh host (`Cannot connect to C:`), so staging now passes a bare filename with `cwd` at the tarball directory and surfaces tar stderr on failure; (2) the Electron `files` allowlist was missing `lib/loginHeaderCapture.js`, so the packaged main process crashed at startup with `Cannot find module './lib/loginHeaderCapture'`; (3) electron-builder ≥26 injects an `!**/node_modules/**` ignore into every extraResources pattern list that no positive filter can override, which silently dropped the staged runtime `node_modules` (including the better-sqlite3 N-API prebuild) from `resources/app` — a fresh v3.8.50 desktop build would have shipped with no native SQLite driver and reproduced the #7592 sql.js fallback on every machine; an `afterPack` hook now restores the staged `node_modules` after packing; (4) the packaged-app smoke harness redirected `USERPROFILE` into its temp DATA_DIR, but Electron resolves the Roaming profile from `%USERPROFILE%\AppData\Roaming\` (USERPROFILE takes precedence over the APPDATA env var) and the path service throws instead of creating it, so `requestSingleInstanceLock()` returned false and the app exited(0) silently before `app.whenReady()` — the harness now pre-creates the derived tree, and `ensureSmokeEnvDirs` is exported and unit-tested; (5) the #7592 driver assertion parsed a `[DB] Driver: ...` line that the server's primary DB path never printed (only the unused `openDatabaseAsync()` did) — `getDbInstance()` now emits the same line on open so the guard can actually assert the native driver. Also: the smoke env-allowlist unit test hardcoded POSIX paths and could only pass on Linux/macOS; expectations are now host-agnostic, with new regression tests for the USERPROFILE-derived tree and `tarPack` under absolute Windows-style paths ([#11443](https://github.com/diegosouzapw/OmniRoute/pull/11443)) — thanks @jonlwheat2-gif +- fix(sse): stop the auto-combo candidates inspector from silently dropping model-locked/cooled-down rows (#9133) ([#11994](https://github.com/diegosouzapw/OmniRoute/pull/11994)) — thanks @hartmark +- **fix(providers):** Perplexity Web no longer turns upstream stream failures into successful assistant text; pre-content failures remain eligible for fallback, partial output ends with a structured sanitized error, and failed sessions are not persisted ([#12465](https://github.com/diegosouzapw/OmniRoute/pull/12465)) +- **fix(video-bridge):** preserve chronological video and transcript fusion, source-frame timestamps, transcript provenance, full contact-sheet intervals, and pre-abort callback isolation ([#11681](https://github.com/diegosouzapw/OmniRoute/pull/11681)) +- **Z.ai Web:** HTTP 200 streams carrying an upstream error now terminate with a structured failure instead of assistant text plus a normal stop, preserving partial output while allowing pre-content combo fallback. ([#12454](https://github.com/diegosouzapw/OmniRoute/pull/12454)) +- Absorb `Error [AbortError]: request_signal_aborted` and DOMException AbortError shapes in the process-level client-abort crash guard so routine client disconnects no longer kill the server (exit code 7). ([#12165](https://github.com/diegosouzapw/OmniRoute/pull/12165)) — thanks @alvinveroy +- **fix(sse):** Treat Adapta Web `type:error` SSE events as sanitized non-stream failures instead of empty HTTP 200 completions. ([#12459](https://github.com/diegosouzapw/OmniRoute/pull/12459)) +- **fix(autoCombo):** layer 3 (`models_dev_tier`) no longer scores retired models as premium, merge cross-provider capability rows deterministically, and inherit tier scores through variant ids ([#11508](https://github.com/diegosouzapw/OmniRoute/issues/11508)). `getModelsDevTierFitness` now vetoes ids present in `config/quality/model-lifecycle.json` with status `retired` before any other signal; `loadModelCapabilities()` aggregates per `model_id` (any-non-null-true for booleans, max for `limit_context`) instead of last-write-wins over SQLite's undefined row order; a capabilities miss retries through `resolveScoresAs()`'s base id and reports `models_dev_tier:inherited`, matching layers 1–2 (#11492). Adds `getModelsDevTierFitnessWithSource()` and an `invalidateCapabilitiesCache()` test/ops hook. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) — thanks @Meet6338-X +- fix(sse): accept Claude `content:[]` + `stop_reason: max_tokens`/`tool_use` in `detectMalformedNonStream` (match `isEmptyContentResponse`) so Claude Code `/model` probes with `max_tokens: 1` no longer become a false 502 ([#11568](https://github.com/diegosouzapw/OmniRoute/pull/11568)) — thanks @Seramicx +- **fix(security):** sanitize `request.failed` diagnostics before publishing them to live dashboard listeners and replay history, while keeping status, model, provider, latency, and internal call-log diagnostics intact. ([#12469](https://github.com/diegosouzapw/OmniRoute/pull/12469)) +- **fix(pwa):** do not intercept dashboard navigations so Chrome can fall back from a stale HTTP/3 Alt-Svc advertisement (UDP :20128 is unpublished; F5 on a long-lived tab hung until a new tab opened a fresh TCP connection). ([#12767](https://github.com/diegosouzapw/OmniRoute/pull/12767)) — thanks @HouMinXi +- **fix(executors):** handle DuckDuckGo ERR_BN_LIMIT (418) without retrying — when the upstream returns `418 ERR_BN_LIMIT` (rate-limit/ban), the executor now returns the error immediately instead of burning another VQD acquisition that would only count against the IP limit. The retry logic for `418 ERR_CHALLENGE` (unsolved challenge) remains unchanged. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) ([#12110](https://github.com/diegosouzapw/OmniRoute/pull/12110)) — thanks @oyi77 / @Meet6338-X +- Fixed DuckDuckGo streaming responses losing JSON lines and UTF-8 characters split across network chunks. ([#11528](https://github.com/diegosouzapw/OmniRoute/pull/11528)) — thanks @pacocartones +- Harden generated Cloudflare, Vercel, and Deno relays by stripping hop-by-hop, framing, and proxy authentication headers before forwarding requests upstream. ([#11533](https://github.com/diegosouzapw/OmniRoute/pull/11533)) — thanks @pacocartones +- **feat(grok-cli):** Provider Limits shows grok-cli banked reset credits from `GetRemainingResets` (including a real zero; a failed RPC omits the row) and the existing View credits button now calls `ConsumerUiSvc/RedeemReset` for grok-cli. Live tokens use nested fields 10/20/30 (id + Timestamp), not compact 1/2/3. ([#12805](https://github.com/diegosouzapw/OmniRoute/pull/12805)) — thanks @HouMinXi +- **fix(memory):** Embedding Model Quick select, Embedding Source remote dropdown, and Rerank selector now list every configured provider with embedding/rerank support instead of only chat-catalog text matches plus OpenRouter live discovery; a generic OpenAI-compatible `/embeddings` + Cohere-compatible `/rerank` runtime fallback resolves any configured chat provider's embedding/rerank endpoint, so unlisted providers no longer fail with "Unknown embedding provider"; both memory selectors gained a free-text model override ([#11390](https://github.com/diegosouzapw/OmniRoute/pull/11390)) — thanks @rqzbeh +- Harden SQLite upgrades around the historical migration-074 version collision: missing discovery and inspector tables are replayed atomically, pre-existing databases (including setup-created skeletons) receive reusable content-addressed safety snapshots, and Node test/eval probes without `DATA_DIR` are isolated from the operator database. ([#12435](https://github.com/diegosouzapw/OmniRoute/pull/12435)) +- **fix(dashboard):** Moonshot/Kimi Open Platform voucher and cash leftover percentages follow the bucket balance, so an empty wallet no longer paints those rows as 100% while Available is 0% ([#12733](https://github.com/diegosouzapw/OmniRoute/pull/12733)) — thanks @HouMinXi +- **fix(combo):** return non-retryable HTTP 400 when all candidates for a pinned native Codex turn are unavailable due to model-scoped lockout, terminating the turn cleanly while preserving turn continuity and enabling standard Combo routing on subsequent turns ([#12240](https://github.com/diegosouzapw/OmniRoute/pull/12240)) — thanks @mdigitalbh81 +- Fixed auto-router strategies preserving the selected connection through ranking and dispatch when multiple connections share the same provider and model. ([#11530](https://github.com/diegosouzapw/OmniRoute/pull/11530)) — thanks @pacocartones +- Preserve Chat Completions JSON-object response formats when translating requests to the Responses API. ([#11529](https://github.com/diegosouzapw/OmniRoute/pull/11529)) — thanks @pacocartones +- Fix idempotency fingerprints so Responses API requests include `input` and other semantic generation fields using stable serialization, while excluding credentials and request metadata. ([#11532](https://github.com/diegosouzapw/OmniRoute/pull/11532)) — thanks @pacocartones +- **fix(api):** Generated API CLI commands now enforce required OpenAPI request bodies; Combo test commands forward the required `comboName` body, while API keys created by older writers after migration 149 preserve legacy allow-all Combo access without widening explicit empty allowlists — thanks @marcelokarval ([#12070](https://github.com/diegosouzapw/OmniRoute/pull/12070)) +- **fix(grok-web):** treat upstream streaming failures as failures instead of successful assistant text: error-only streams now fail readiness with HTTP 502, while failures after legitimate content preserve that partial output and terminate through the sanitized stream failure path without a normal `stop` completion. ([#12458](https://github.com/diegosouzapw/OmniRoute/pull/12458)) +- HuggingChat now turns HTTP 200 JSONL generation failures into a sanitized 502 before content, or a fixed public stream failure after partial output, so fallback and request persistence no longer record a false successful stop. ([#12456](https://github.com/diegosouzapw/OmniRoute/pull/12456)) +- **fix(providers):** keep 1min.ai HTTP 200 stream errors out of assistant content, preserve partial output, and expose sanitized terminal errors so pre-content failures can fall back. ([#12466](https://github.com/diegosouzapw/OmniRoute/pull/12466)) +- **fix(sessions):** preserve legacy Sessions data when the additive exclusive-session projection is unavailable, returning an empty projection and warning only once per contiguous outage instead of failing the endpoint; the Sessions badge intentionally reflects the merged legacy and exclusive row count introduced by [#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389) ([#11469](https://github.com/diegosouzapw/OmniRoute/pull/11469)) — thanks @KaspaPulse +- Render Nimble Search with the generic provider icon and serve Opper's proven logo locally. ([#12415](https://github.com/diegosouzapw/OmniRoute/pull/12415)) +- **fix(quality):** `config/quality/provider-assets-provenance.jsonl` was stale against the physical `public/providers/` tree (still listing 225 assets and 5 duplicate-content groups after several provider-retirement PRs removed 85 icon files), which would have made `check:provider-asset-provenance` fail on the next full run. Reconciled the manifest to the real 142-file snapshot (142 asset records, 1 duplicate-content group, 2 newly-registered files marked `unresolved` pending provenance research), repinned `auditedCommit`, and updated the regression test's hardcoded counts to match. ([#11876](https://github.com/diegosouzapw/OmniRoute/pull/11876)) +- fix(ui): wrap quota card footer action buttons to prevent clipping Refresh button on narrow cards — when extra action buttons such as `View credits` are present on Codex cards, allow the footer row to flex-wrap with `shrink-0` buttons so `Refresh now` remains visible and clickable across all responsive card widths ([#11464](https://github.com/diegosouzapw/OmniRoute/pull/11464)) — thanks @adevwithpurpose +- Keep Antigravity Gemini usable when the same connection's Claude weekly quota is empty; generic quota cache stays per-connection for every other provider. ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) — thanks @HouMinXi +- Fixed Tailscale tunnel setup to reject ports outside the valid 1-65535 range and safely install Windows MSI packages from paths containing apostrophes. ([#11534](https://github.com/diegosouzapw/OmniRoute/pull/11534)) — thanks @pacocartones +- Classify Cloudflared and Tailscale process-management routes as local-only and spawn-capable while preserving remote access to read-only tunnel status endpoints. ([#11531](https://github.com/diegosouzapw/OmniRoute/pull/11531)) — thanks @pacocartones +- Electron release workflow: the `publish-npm` job now grants `actions: read` to the reusable `npm-publish.yml` it calls (its `publish` job requests it), which is what made GitHub refuse the whole v3.8.50 run at startup and ship the release with zero desktop assets; a `workflow_dispatch` now builds the requested tag instead of the dispatching branch and can skip the npm leg (`publish_npm=false`) when only re-attaching assets ([#11974](https://github.com/diegosouzapw/OmniRoute/pull/11974)) +- **fix(dashboard):** The Combos page usage guide now reads its dismissal through `useSyncExternalStore` instead of correcting SSR state inside an effect, removing an extra commit of the page tree on every load (and the `react-hooks/set-state-in-effect` error it raised). ([#12671](https://github.com/diegosouzapw/OmniRoute/pull/12671)) +- Electron release: `electron/package-lock.json` regained the optional `electron-builder-squirrel-windows` subtree (13 entries) that `npm ci` had been refusing as out of sync, `electron-release.yml` gained a `build_ref` dispatch input and stops regenerating release notes on a re-attach dispatch, and the npm publish workflow attaches the SBOM to the GitHub Release on dispatch publishes too — so the v3.8.51 tag ships every desktop asset and the SBOM like v3.8.49 did ([#12022](https://github.com/diegosouzapw/OmniRoute/pull/12022)) +- Drained the reds every PR against `release/v3.8.51` was born with: documented the three service-worker build-id variables, registered the six retirement/tunnel tests with the mutation gate, approved `eslint-plugin-react-hooks` in the dependency allowlist, added the six `combo.sort.*` strings to `vi` and `pt-BR`, pointed the ChatGPT Web doc at the real migration-168 test, worded the g4f hint around the member key, and realigned four tests to the retired-provider catalog and the legacy-schema fixtures the retirement migrations touch. ([#11940](https://github.com/diegosouzapw/OmniRoute/pull/11940)) +- **fix(providers):** Claude, Grok, LMArena, Notion, and Perplexity web-cookie transports now use pooled `wreq-js` 3.2 instead of the native sidecar, with all nine supported bindings pinned and audited, and the applicable platform binding plus native-license evidence included in each release artifact ([#12429](https://github.com/diegosouzapw/OmniRoute/pull/12429), supersedes [#11753](https://github.com/diegosouzapw/OmniRoute/pull/11753)). +- **fix(providers):** Zed Hosted streaming failures now trigger fallback before content and end partial streams with a sanitized structured error instead of fake assistant text and a normal-success stop. ([#12455](https://github.com/diegosouzapw/OmniRoute/pull/12455)) +- **fix(test):** capture hook result after render (direct commit `b85d0abb0b`) +- **fix(sync):** repair the two regressions the v3.8.50 sync-back left on release/v3.8.51 (direct commit `5ade9e0851`) +- **revert(models):** drop the memo reset hook ported from #10055 (direct commit `282de841ae`) +- **fix(typecheck):** guard the optional connectionId before the reactive model sync (direct commit `f2825bad75`) +- **fix(quota-share):** release the winner's reserved in-flight slot (#11371) ([#11408](https://github.com/diegosouzapw/OmniRoute/pull/11408)) — thanks @oyi77 +- **fix(providers):** stop silently dropping reasoning effort on opencode families (#10788) ([#11409](https://github.com/diegosouzapw/OmniRoute/pull/11409)) — thanks @oyi77 +- **fix(combo):** enable genuine quota-aware routing for generic providers (antigravity, claude, etc.) ([#11411](https://github.com/diegosouzapw/OmniRoute/pull/11411)) — thanks @HouMinXi +- **fix(oauth):** allow expired token retry before deactivation and activate expired retry loop (#P0) ([#11414](https://github.com/diegosouzapw/OmniRoute/pull/11414)) — thanks @HouMinXi +- **perf(executors):** lazy-load the executor registry — defer class imports + construction to first use (#11220) ([#11421](https://github.com/diegosouzapw/OmniRoute/pull/11421)) — thanks @oyi77 +- **fix:** make release/v3.8.50 CI gates green (run 32786966560 root causes) ([#11450](https://github.com/diegosouzapw/OmniRoute/pull/11450)) — thanks @jonlwheat2-gif +- **fix(sse):** exact-domain cookie match and origin-equality URL assertions ([#11452](https://github.com/diegosouzapw/OmniRoute/pull/11452)) — thanks @hartmark +- **fix(providers):** send Responses API payload during OpenAI-compatible validation ([#11454](https://github.com/diegosouzapw/OmniRoute/pull/11454)) — thanks @tuandinh0801 +- **fix(command-code):** fallback to /alpha/generate for Go plan without Provider API access ([#11455](https://github.com/diegosouzapw/OmniRoute/pull/11455)) — thanks @Chewji9875 +- **fix(responses-continuation):** fail closed on a log-truncated stored input/output array ([#11473](https://github.com/diegosouzapw/OmniRoute/pull/11473)) — thanks @hartmark +- **fix(cli):** write jcode settings as [providers.omniroute] in config.toml ([#11484](https://github.com/diegosouzapw/OmniRoute/pull/11484)) — thanks @hongnoul +- **fix(logging):** raise chat-log array truncation cap to 1000 and unify duplicate implementations ([#11499](https://github.com/diegosouzapw/OmniRoute/pull/11499)) — thanks @hartmark +- **fix:** restore live websocket URL helpers ([#11509](https://github.com/diegosouzapw/OmniRoute/pull/11509)) — thanks @PauloFH +- **perf(core):** eliminate async waterfalls across home, cache, and db operations (#11396) ([#11512](https://github.com/diegosouzapw/OmniRoute/pull/11512)) — thanks @HouMinXi +- **fix(kiro):** add runtime.us-east-1.kiro.dev as first-attempt endpoint ([#11517](https://github.com/diegosouzapw/OmniRoute/pull/11517)) — thanks @rifqiawl +- **fix(providers):** remove duplicate 128-tool truncation in OpencodeExecutor (#11444) ([#11518](https://github.com/diegosouzapw/OmniRoute/pull/11518)) — thanks @HouMinXi +- **fix(providers):** poll onboardUser LRO before classifying BYOP ([#11519](https://github.com/diegosouzapw/OmniRoute/pull/11519)) — thanks @rifqiawl +- **fix(translator):** preserve exact tool name casing for Claude tool_use payloads (#11487) ([#11520](https://github.com/diegosouzapw/OmniRoute/pull/11520)) — thanks @HouMinXi +- **fix(kimi-web):** align health probe with executor domain ([#11521](https://github.com/diegosouzapw/OmniRoute/pull/11521)) — thanks @pacocartones +- **fix(cli):** preserve existing admin password during setup ([#11522](https://github.com/diegosouzapw/OmniRoute/pull/11522)) — thanks @pacocartones +- **fix(mitm):** keep Windows DNS elevation off the POSIX sudo path (#11430) ([#11542](https://github.com/diegosouzapw/OmniRoute/pull/11542)) — thanks @official-burak +- **fix(sse):** scale chat admission by ingest byte budget instead of a fixed request count ([#11548](https://github.com/diegosouzapw/OmniRoute/pull/11548)) — thanks @TheDemonTuan +- **fix:** accept Gemini Web cookie exports ([#11554](https://github.com/diegosouzapw/OmniRoute/pull/11554)) — thanks @rafacpti23 +- **fix(i18n):** complete Brazilian Portuguese translations ([#11558](https://github.com/diegosouzapw/OmniRoute/pull/11558)) — thanks @rafacpti23 +- **fix(sse):** explicit types for openai-responses pureHelpers — clears last failing core typecheck gate ([#11567](https://github.com/diegosouzapw/OmniRoute/pull/11567)) — thanks @jonlwheat2-gif +- **fix(opencode):** hydrate Proxy Pool references for no-auth connections ([#11584](https://github.com/diegosouzapw/OmniRoute/pull/11584)) — thanks @AStupidBear +- **fix(ci):** resolve inherited release/v3.8.51 base-red CI failures ([#11608](https://github.com/diegosouzapw/OmniRoute/pull/11608)) — thanks @jonlwheat2-gif +- **fix(db):** fallback load STORAGE_ENCRYPTION_KEY from env files ([#11614](https://github.com/diegosouzapw/OmniRoute/pull/11614)) — thanks @benzntech +- **fix(providers):** mark Brave Search as no longer having a free tier ([#11621](https://github.com/diegosouzapw/OmniRoute/pull/11621)) — thanks @turbolego +- **fix:** pass tool result content through as string in OpenAI/Claude -> Gemini translation ([#11624](https://github.com/diegosouzapw/OmniRoute/pull/11624)) — thanks @Hsia97 +- **fix(sse):** scope credential health to exact key ([#11628](https://github.com/diegosouzapw/OmniRoute/pull/11628)) — thanks @KaspaPulse +- **fix(install):** guard prepare script against missing husky ([#11634](https://github.com/diegosouzapw/OmniRoute/pull/11634)) — thanks @Prajeeth-12 +- **fix(security):** redact NVIDIA_API_KEY at the log sink in the ad-hoc diag script ([#11639](https://github.com/diegosouzapw/OmniRoute/pull/11639)) +- **fix(types):** clear the two type regressions red on release/v3.8.51 ([#11640](https://github.com/diegosouzapw/OmniRoute/pull/11640)) — thanks @jonlwheat2-gif +- **fix(build):** resolve workspace: protocol dependencies to concrete semver versions during prepublish (#11540) ([#11641](https://github.com/diegosouzapw/OmniRoute/pull/11641)) — thanks @HouMinXi +- **fix(search):** properly resolve configured search connection in /v1/responses pipeline (#11524) ([#11642](https://github.com/diegosouzapw/OmniRoute/pull/11642)) — thanks @HouMinXi +- **fix(authz):** trust cf-connecting-ip for via-proxy detection when peer is Cloudflare edge (#11514) ([#11643](https://github.com/diegosouzapw/OmniRoute/pull/11643)) — thanks @HouMinXi +- **fix(security):** compare the OIDC state cookie in constant time ([#11646](https://github.com/diegosouzapw/OmniRoute/pull/11646)) +- **fix(security):** namespace the dedup hash by the calling API key ([#11649](https://github.com/diegosouzapw/OmniRoute/pull/11649)) +- **fix(models):** gate the Codex-native catalog loop on prefix mode (#11632) ([#11662](https://github.com/diegosouzapw/OmniRoute/pull/11662)) — thanks @xiaoyaner0201 +- **fix(dashboard):** project Codex routing quota in account pools ([#11666](https://github.com/diegosouzapw/OmniRoute/pull/11666)) — thanks @KaspaPulse +- **fix(ddgw):** normaliza system no upstream da release v3.8.51 ([#11692](https://github.com/diegosouzapw/OmniRoute/pull/11692)) — thanks @RhianB14 +- **fix(memory):** honor category filter in GET /api/memory ([#11699](https://github.com/diegosouzapw/OmniRoute/pull/11699)) — thanks @Sabeekhann +- **fix(ci):** detect stale workspace lockfile entries ([#11714](https://github.com/diegosouzapw/OmniRoute/pull/11714)) — thanks @pacocartones +- **fix(ci):** stamp dist/BUILD_SHA before the npm publish provenance gate ([#11721](https://github.com/diegosouzapw/OmniRoute/pull/11721)) +- **fix(docker):** decouple the best-effort Bun image from the release manifest ([#11724](https://github.com/diegosouzapw/OmniRoute/pull/11724)) +- **perf(compression):** avoid cloning memo misses twice ([#11727](https://github.com/diegosouzapw/OmniRoute/pull/11727)) — thanks @RaviTharuma +- **fix(docker):** let the bun digest artifact be absent, not fatal ([#11740](https://github.com/diegosouzapw/OmniRoute/pull/11740)) +- **fix(shared):** block cloud-metadata hosts under default remote-image guard ([#11755](https://github.com/diegosouzapw/OmniRoute/pull/11755)) — thanks @santosraju99-hub +- **fix(api):** preserve caller-provided X-Correlation-Id on chat completions ([#11760](https://github.com/diegosouzapw/OmniRoute/pull/11760)) — thanks @aniruddhaadak80 +- **fix(resilience):** lock permanently retired models instead of short backoff (Gemini ban prevention) ([#11762](https://github.com/diegosouzapw/OmniRoute/pull/11762)) — thanks @turbolego +- **fix:** mark Vercel AI Gateway as passthroughModels ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **fix(resilience):** stop hammering permanently-moved endpoints and billing-suspended accounts ([#11774](https://github.com/diegosouzapw/OmniRoute/pull/11774)) — thanks @turbolego +- **fix(lease):** remove global static reservation and gate routing on live active lease occupancy ([#11775](https://github.com/diegosouzapw/OmniRoute/pull/11775)) — thanks @TheDemonTuan +- **fix(ci):** size the install-upgrade gate to a measured run, and log the pack cost ([#11776](https://github.com/diegosouzapw/OmniRoute/pull/11776)) +- **fix(pwa):** stop serving the stale shell after deploys ([#11779](https://github.com/diegosouzapw/OmniRoute/pull/11779)) — thanks @HouMinXi +- **fix(combo):** lock GitHub models rejected as "not supported" for future requests ([#11781](https://github.com/diegosouzapw/OmniRoute/pull/11781)) — thanks @turbolego +- **fix(cli):** probe both IPv4 and IPv6 loopback for server readiness (#11766) ([#11794](https://github.com/diegosouzapw/OmniRoute/pull/11794)) — thanks @vermasomesh835 +- **fix(sse):** preserve CLIProxyAPI wrappers per connection ([#11797](https://github.com/diegosouzapw/OmniRoute/pull/11797)) — thanks @RaviTharuma +- **fix(providers):** honor model auto-fetch opt-in on create ([#11798](https://github.com/diegosouzapw/OmniRoute/pull/11798)) — thanks @RaviTharuma +- **fix(dashboard):** make model auto-fetch order independent ([#11805](https://github.com/diegosouzapw/OmniRoute/pull/11805)) — thanks @RaviTharuma +- **fix(docker):** make GHA cache exports best effort ([#11806](https://github.com/diegosouzapw/OmniRoute/pull/11806)) — thanks @RaviTharuma +- **fix(kiro):** do not permanently ban on 'User is not authorized to make this call' ([#11809](https://github.com/diegosouzapw/OmniRoute/pull/11809)) — thanks @Deftera186 +- **fix(sse):** keep cache-write tokens in OpenAI-shaped usage ([#11814](https://github.com/diegosouzapw/OmniRoute/pull/11814)) — thanks @TheDemonTuan +- **fix(docs):** resolve relative markdown and wiki links across Fumadocs and GitHub wiki ([#11834](https://github.com/diegosouzapw/OmniRoute/pull/11834)) — thanks @AndrianBalanescu +- **fix(chat):** preserve unstripped model string for passthrough provider routing ([#11840](https://github.com/diegosouzapw/OmniRoute/pull/11840)) — thanks @raheemuddin786 +- **fix(mcp):** avoid async initialization inside sync esm wrappers (#11569) ([#11849](https://github.com/diegosouzapw/OmniRoute/pull/11849)) — thanks @HouMinXi +- **fix(sse):** preserve coherent markdown token boundaries across stream translation chunks (#11606) ([#11851](https://github.com/diegosouzapw/OmniRoute/pull/11851)) — thanks @HouMinXi +- **fix(ci):** run the install-upgrade gate on disk, not on the /tmp tmpfs ([#11855](https://github.com/diegosouzapw/OmniRoute/pull/11855)) +- **fix(skills):** expand shorthand property types in injected tool schemas ([#11857](https://github.com/diegosouzapw/OmniRoute/pull/11857)) — thanks @alvinveroy +- **fix(ci):** publish npm from a hosted runner so provenance is accepted ([#11877](https://github.com/diegosouzapw/OmniRoute/pull/11877)) +- **fix(lint):** pin react hooks plugin to 7.0.1 ([#11886](https://github.com/diegosouzapw/OmniRoute/pull/11886)) +- **fix(ci):** surface and clear release-green ESLint failure ([#11890](https://github.com/diegosouzapw/OmniRoute/pull/11890)) — thanks @MumuTW +- **fix(guardrails):** prevent duplicate prompt-injection-guard log output ([#11936](https://github.com/diegosouzapw/OmniRoute/pull/11936)) — thanks @patrykkopycinski +- **fix(auth):** downgrade expected transient states from warn to debug ([#11937](https://github.com/diegosouzapw/OmniRoute/pull/11937)) — thanks @patrykkopycinski +- **fix(codex):** fail over image generation for imported free plans ([#11948](https://github.com/diegosouzapw/OmniRoute/pull/11948)) — thanks @Bl0ck154 +- **fix(codex):** normalize non-stream responses ([#11951](https://github.com/diegosouzapw/OmniRoute/pull/11951)) — thanks @Bl0ck154 +- **fix(images):** forward Antigravity image size ([#11952](https://github.com/diegosouzapw/OmniRoute/pull/11952)) — thanks @Bl0ck154 +- **fix(db):** invalidate connection cache after upsert ([#11953](https://github.com/diegosouzapw/OmniRoute/pull/11953)) — thanks @Bl0ck154 +- **fix(codex):** restore imported account state ([#11954](https://github.com/diegosouzapw/OmniRoute/pull/11954)) — thanks @Bl0ck154 +- **fix(sse):** give extended-thinking targets the reasoning readiness budget ([#11959](https://github.com/diegosouzapw/OmniRoute/pull/11959)) — thanks @abhisheksharma2411 +- **fix(dashboard):** use opaque background and readable text color on cost chart tooltips ([#11960](https://github.com/diegosouzapw/OmniRoute/pull/11960)) — thanks @ZaimMarzuki +- **fix(antigravity):** send complete loadCodeAssist metadata (ideType/platform/pluginType as numeric enums) ([#11969](https://github.com/diegosouzapw/OmniRoute/pull/11969)) — thanks @rifqiawl +- **fix(core):** resolve DB init race condition and reasoning translation ([#12003](https://github.com/diegosouzapw/OmniRoute/pull/12003)) — thanks @raheemuddin786 +- **fix(admission):** exclude reclaimable page cache from the cgroup pressure ratio ([#12017](https://github.com/diegosouzapw/OmniRoute/pull/12017)) — thanks @HouMinXi +- **fix(build):** prune non-production trees in NFT trace excludes and tsconfig ([#12028](https://github.com/diegosouzapw/OmniRoute/pull/12028)) — thanks @Chewji9875 +- **fix(migrations):** add renamed migration compatibility for 056/073/077/101 ([#12036](https://github.com/diegosouzapw/OmniRoute/pull/12036)) — thanks @oyi77 +- **fix(sse):** default crash-guard logger to console.warn, not console ([#12042](https://github.com/diegosouzapw/OmniRoute/pull/12042)) — thanks @HouMinXi +- **fix(catalog):** derive vision/modalities for built-in auto combos from effective target pool ([#12046](https://github.com/diegosouzapw/OmniRoute/pull/12046)) — thanks @Prajeeth-12 +- **fix(release):** the packaged-app smoke verifies the database opened, not a driver line the primary path never prints (twin of #12032) ([#12047](https://github.com/diegosouzapw/OmniRoute/pull/12047)) +- **fix(barrel):** migrate src/app/ off the @/lib/localDb barrel import (#11795 Phase 2) ([#12051](https://github.com/diegosouzapw/OmniRoute/pull/12051)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate src/lib/ off the localDb barrel to direct db imports (#59) ([#12052](https://github.com/diegosouzapw/OmniRoute/pull/12052)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate open-sse, src/shared, src/sse, src/models, src/domain off the @/lib/localDb barrel import (#11795 Phase 4) ([#12053](https://github.com/diegosouzapw/OmniRoute/pull/12053)) — thanks @jonlwheat2-gif +- **fix(barrel):** delete the @/lib/localDb barrel — every consumer migrated (#11795 Phase 5) ([#12055](https://github.com/diegosouzapw/OmniRoute/pull/12055)) — thanks @jonlwheat2-gif +- **fix(radar):** restore D12 public boundary ([#12057](https://github.com/diegosouzapw/OmniRoute/pull/12057)) +- **fix(dev):** bound webpack and Tailwind scans ([#12075](https://github.com/diegosouzapw/OmniRoute/pull/12075)) — thanks @backryun +- **fix(dev):** isolate root layout settings reads ([#12076](https://github.com/diegosouzapw/OmniRoute/pull/12076)) — thanks @backryun +- **fix(dev):** reduce instrumentation executor fan-out (phase 3) ([#12078](https://github.com/diegosouzapw/OmniRoute/pull/12078)) — thanks @backryun +- **fix(dev):** make logging resources HMR-singleton ([#12079](https://github.com/diegosouzapw/OmniRoute/pull/12079)) — thanks @backryun +- **fix(sse):** keep unavailable forced connections scoped ([#12080](https://github.com/diegosouzapw/OmniRoute/pull/12080)) — thanks @keeltrace +- **fix(dev):** isolate batch dispatch from instrumentation ([#12081](https://github.com/diegosouzapw/OmniRoute/pull/12081)) — thanks @backryun +- **fix(ci):** reconcile release test contract drift ([#12082](https://github.com/diegosouzapw/OmniRoute/pull/12082)) — thanks @backryun +- **fix(ci):** accept CVE-2025-68121 in the prebuilt tls-client .so, auto-close base-red issues, guard Scorecard on the default branch ([#12085](https://github.com/diegosouzapw/OmniRoute/pull/12085)) +- **fix(db):** drop three consumer-less 1proxy exports — dead-code base-red on release/v3.8.51 after the barrel deletion (#12055) ([#12087](https://github.com/diegosouzapw/OmniRoute/pull/12087)) +- **fix(api):** clear the six API-route TypeScript regressions the new gate landed red on ([#12094](https://github.com/diegosouzapw/OmniRoute/pull/12094)) +- **fix(release):** never let the tag-push Create Release append auto notes to the curated body ([#12096](https://github.com/diegosouzapw/OmniRoute/pull/12096)) +- **fix(sse):** honor CLIProxyAPI environment API key ([#12099](https://github.com/diegosouzapw/OmniRoute/pull/12099)) — thanks @RaviTharuma +- **fix(oauth):** bind Google refresh to the client that issued the token ([#12106](https://github.com/diegosouzapw/OmniRoute/pull/12106)) — thanks @HouMinXi +- **fix(dashboard):** make RequestLoggerDetail loadable outside Next — CSS via globals.css + CJS/ESM interop (#11703 base-reds) ([#12114](https://github.com/diegosouzapw/OmniRoute/pull/12114)) +- **fix(leases):** project status lease row to lease columns so joined connection PII never escapes ([#12115](https://github.com/diegosouzapw/OmniRoute/pull/12115)) — thanks @geek007git +- **fix(plugins):** do not kill the plugin process when a fire-and-forget hook times out ([#12116](https://github.com/diegosouzapw/OmniRoute/pull/12116)) — thanks @geek007git +- **fix(guardrails):** restore injection-guard logging on middleware-only routes ([#12117](https://github.com/diegosouzapw/OmniRoute/pull/12117)) — thanks @geek007git +- **fix(plugins):** refresh stored manifest from disk on activate so new hook fields reach existing installs ([#12120](https://github.com/diegosouzapw/OmniRoute/pull/12120)) — thanks @geek007git +- **fix(codex):** preserve existing provider state when bulk-import upserts a matching connection ([#12122](https://github.com/diegosouzapw/OmniRoute/pull/12122)) — thanks @geek007git +- **fix(ci):** clear the two base-reds the 2026-08-30 merge batch left on release/v3.8.51 (round 3) ([#12123](https://github.com/diegosouzapw/OmniRoute/pull/12123)) +- **fix(sse):** strip type:'custom' from Claude tools on agentrouter dispatch ([#12126](https://github.com/diegosouzapw/OmniRoute/pull/12126)) — thanks @wahidsadik371-coder +- **fix(sse):** stop advertising video providers the dispatcher cannot run ([#12131](https://github.com/diegosouzapw/OmniRoute/pull/12131)) — thanks @quiterunner-commits +- **fix(search):** treat HTTP 432 and plan limit errors as transient cooldown ([#12139](https://github.com/diegosouzapw/OmniRoute/pull/12139)) — thanks @HouMinXi +- **fix(sse):** refuse an AI Horde queue that cannot fit the request budget ([#12143](https://github.com/diegosouzapw/OmniRoute/pull/12143)) — thanks @quiterunner-commits +- **fix(ci):** clear the base-reds the afternoon merge batch left on release/v3.8.51 (round 5: provider count 352, TS2554/TS2677) ([#12144](https://github.com/diegosouzapw/OmniRoute/pull/12144)) +- **fix(translator):** strip neutral tool_choice when tools absent in Responses-to-Chat (#12141) ([#12166](https://github.com/diegosouzapw/OmniRoute/pull/12166)) — thanks @HouMinXi +- **fix(guardrails):** pass providerId to getResolvedModelCapabilities in checkComboVision (#12112) ([#12169](https://github.com/diegosouzapw/OmniRoute/pull/12169)) — thanks @HouMinXi +- **fix(translator):** strip plaintext reasoning content for opaque responses backends (#12128) ([#12171](https://github.com/diegosouzapw/OmniRoute/pull/12171)) — thanks @HouMinXi +- **perf(stream):** compile hot-path regexes once, bound token caches, fix quadratic buffering ([#12179](https://github.com/diegosouzapw/OmniRoute/pull/12179)) — thanks @opensource-elearning +- **fix:** resolve compression worker file using runtime anchors instead of… ([#12183](https://github.com/diegosouzapw/OmniRoute/pull/12183)) — thanks @NightStalker-87 +- **fix:** prevent Claude Code session kills via liveness-aware readiness + auto model echo ([#12189](https://github.com/diegosouzapw/OmniRoute/pull/12189)) — thanks @opensource-elearning +- **fix(translator):** preserve falsy primitive values in Gemini and Antigravity function response results ([#12191](https://github.com/diegosouzapw/OmniRoute/pull/12191)) — thanks @Siva010 +- **fix(oauth):** mark empty Antigravity projectId as degraded and clear stale errors (#11284) ([#12205](https://github.com/diegosouzapw/OmniRoute/pull/12205)) — thanks @HouMinXi +- **fix(providers):** gate the Codex auto-ping usage read on the shared quota throttle ([#12209](https://github.com/diegosouzapw/OmniRoute/pull/12209)) — thanks @abhisheksharma2411 +- **fix(combos):** prioritize SQLite row id over inner JSON id and notify delete errors ([#12213](https://github.com/diegosouzapw/OmniRoute/pull/12213)) — thanks @HouMinXi +- **fix(providers):** mark featherless.ai as no longer having a free tier (`hasFree: false`) ([#12216](https://github.com/diegosouzapw/OmniRoute/pull/12216)) — thanks @turbolego +- **fix(deepseek-web):** stop Turbopack dev panic in the PoW worker path resolver ([#12221](https://github.com/diegosouzapw/OmniRoute/pull/12221)) — thanks @hartmark +- **fix(chat-admission):** clarify local 503 source ([#12223](https://github.com/diegosouzapw/OmniRoute/pull/12223)) — thanks @keeltrace +- **fix(dev):** silence webpack runtime module warnings ([#12228](https://github.com/diegosouzapw/OmniRoute/pull/12228)) — thanks @backryun +- **fix(db):** use module.require for CommonJS runtime driver loading ([#12230](https://github.com/diegosouzapw/OmniRoute/pull/12230)) — thanks @raheemuddin786 +- **fix(memory):** honest probe-driven FTS5 keyword status + memory_id rowid sync ([#12231](https://github.com/diegosouzapw/OmniRoute/pull/12231)) — thanks @raheemuddin786 +- **fix(system):** propagate abort signal to stream reader in HTTP version checks ([#12232](https://github.com/diegosouzapw/OmniRoute/pull/12232)) — thanks @raheemuddin786 +- **fix(resilience):** isolate local host execution errors from provider circuit breakers ([#12233](https://github.com/diegosouzapw/OmniRoute/pull/12233)) — thanks @raheemuddin786 +- **fix(sse):** sort injected tools deterministically for prompt caching ([#12234](https://github.com/diegosouzapw/OmniRoute/pull/12234)) — thanks @raheemuddin786 +- **fix(combo):** always clear the loop-safety timer, not just on the happy path (#11804) ([#12245](https://github.com/diegosouzapw/OmniRoute/pull/12245)) +- **fix(quality):** base-red pair — stryker tap registration + turn-pin suites aligned to the window gate ([#12255](https://github.com/diegosouzapw/OmniRoute/pull/12255)) — thanks @backryun +- **fix(dev):** qualify Turbopack runtime boundaries ([#12258](https://github.com/diegosouzapw/OmniRoute/pull/12258)) — thanks @backryun +- **fix(combo):** fall back to full pool when collapsed sole survivor is context-too-small ([#12278](https://github.com/diegosouzapw/OmniRoute/pull/12278)) — thanks @opensource-elearning +- **fix(combo):** bound the pre-dispatch unavailable skip so a stale label cannot dark a pool (#12168) ([#12285](https://github.com/diegosouzapw/OmniRoute/pull/12285)) +- **fix(security):** strip Qwen/Alibaba console-session cookies from provider API responses ([#12287](https://github.com/diegosouzapw/OmniRoute/pull/12287)) +- **fix(usage):** console-aware Token Plan guidance + subscription hint on bailian 401 ([#12288](https://github.com/diegosouzapw/OmniRoute/pull/12288)) +- **fix(resourcePressure):** log numeric detail on every rejection, recover faster ([#12293](https://github.com/diegosouzapw/OmniRoute/pull/12293)) — thanks @hartmark +- **fix(release):** drain the 2026-09-01 base-red window — passthrough usage regression + radarPage i18n keys ([#12327](https://github.com/diegosouzapw/OmniRoute/pull/12327)) +- **fix(ci):** openapi-security-tiers checker must honor routeGuard patterns + imported prefixes ([#12350](https://github.com/diegosouzapw/OmniRoute/pull/12350)) — thanks @ggiak +- **perf(ci):** cache node_modules in the npm-ci-retry composite (#8084 D3) ([#12408](https://github.com/diegosouzapw/OmniRoute/pull/12408)) +- **revert(ui):** point CTAs back at their real destinations ([#12410](https://github.com/diegosouzapw/OmniRoute/pull/12410)) +- **fix(release):** drain the 2026-09-02 base-red — rerank-providers import + api-typecheck baseline ratchet ([#12414](https://github.com/diegosouzapw/OmniRoute/pull/12414)) +- **fix(types):** make system prompt injection noImplicitAny-safe ([#12416](https://github.com/diegosouzapw/OmniRoute/pull/12416)) +- **fix(quality):** validate typecheck baseline schema ([#12419](https://github.com/diegosouzapw/OmniRoute/pull/12419)) +- **fix(memory):** resolve rerank provider node cache import ([#12421](https://github.com/diegosouzapw/OmniRoute/pull/12421)) +- **fix(ci):** repair release-root regressions — pack dedup, web-session syntax, uc-image ids ([#12423](https://github.com/diegosouzapw/OmniRoute/pull/12423)) — thanks @backryun +- **fix(providers):** separate MaxAI and UC credential contracts ([#12431](https://github.com/diegosouzapw/OmniRoute/pull/12431)) +- **fix(cli):** preserve Claude settings on config set ([#12432](https://github.com/diegosouzapw/OmniRoute/pull/12432)) — thanks @KrzysiekSko +- **fix(providers):** repair the maxai credential block truncated by merge auto-resolve ([#12433](https://github.com/diegosouzapw/OmniRoute/pull/12433)) +- **fix(quality):** record the 2026-09-02 merged growth in the file-size baseline ([#12434](https://github.com/diegosouzapw/OmniRoute/pull/12434)) +- **fix(uc):** route UC error strings through sanitizeErrorMessage; allowlist the retired codex id ([#12437](https://github.com/diegosouzapw/OmniRoute/pull/12437)) +- **fix(adapta):** redact streamed upstream errors ([#12438](https://github.com/diegosouzapw/OmniRoute/pull/12438)) +- **fix(sse):** remap non-contiguous upstream tool_calls index to a gap-free output_index ([#12445](https://github.com/diegosouzapw/OmniRoute/pull/12445)) — thanks @hartmark +- **fix(quality):** detect duplicate tool_calls entries in one response ([#12446](https://github.com/diegosouzapw/OmniRoute/pull/12446)) — thanks @hartmark +- **fix(conversations):** resolve turn content OmniRoute never sends back to the client ([#12447](https://github.com/diegosouzapw/OmniRoute/pull/12447)) — thanks @hartmark +- **fix(combo):** do not treat credits-exhausted 401 as auth skip ([#12449](https://github.com/diegosouzapw/OmniRoute/pull/12449)) — thanks @RaviTharuma +- **fix(auth):** do not park healthy quota accounts as expired ([#12452](https://github.com/diegosouzapw/OmniRoute/pull/12452)) — thanks @RaviTharuma +- **fix(responses-continuation):** fail closed on a collector-truncated, empty output array ([#12460](https://github.com/diegosouzapw/OmniRoute/pull/12460)) — thanks @hartmark +- **fix(logging):** raise the SSE payload collector's default cap ([#12461](https://github.com/diegosouzapw/OmniRoute/pull/12461)) — thanks @hartmark +- **fix(resilience):** surface Responses failed.error.message in 502s ([#12472](https://github.com/diegosouzapw/OmniRoute/pull/12472)) — thanks @RaviTharuma +- **fix(security):** unbiased maxai X-Random nonce + stricter URL/regex assertions ([#12502](https://github.com/diegosouzapw/OmniRoute/pull/12502)) +- **fix(video):** re-anchor transcript log-redaction so PII/credential maskers can't reopen the leak (#12150 P1 follow-up) ([#12503](https://github.com/diegosouzapw/OmniRoute/pull/12503)) +- **fix(monitoring):** serve cached credentialHealth off the request path ([#12533](https://github.com/diegosouzapw/OmniRoute/pull/12533)) — thanks @RaviTharuma +- **fix(quota):** keep Antigravity Gemini usable when Claude weekly is empty ([#12566](https://github.com/diegosouzapw/OmniRoute/pull/12566)) — thanks @HouMinXi +- **fix(authz):** hard-gate every credential export and CLI-config write (GHSA-5926-2w35-7h4q) ([#12600](https://github.com/diegosouzapw/OmniRoute/pull/12600)) +- **fix(docs):** restore the Next build — REMOVED_PROVIDERS.md had no frontmatter (base-red #12581) ([#12610](https://github.com/diegosouzapw/OmniRoute/pull/12610)) +- **fix(quality):** re-point the zcodeProtocol public-creds allowlist to line 313 ([#12615](https://github.com/diegosouzapw/OmniRoute/pull/12615)) +- **fix(security):** close 3 advisories — search baseUrl exfil, sk- in the error sanitizer, bifrost relay header leak ([#12620](https://github.com/diegosouzapw/OmniRoute/pull/12620)) +- **fix(providers):** sanitize boolean required and nested bare maps for Gemini (#12269) ([#12624](https://github.com/diegosouzapw/OmniRoute/pull/12624)) — thanks @HouMinXi +- **fix(resilience):** keep Overloaded STREAM_EARLY_EOF off the provider breaker ([#12626](https://github.com/diegosouzapw/OmniRoute/pull/12626)) — thanks @HouMinXi +- **fix(catalog):** re-audit free-tier quotas against official pages ([#12649](https://github.com/diegosouzapw/OmniRoute/pull/12649)) +- **fix(dashboard):** keep a request's pending-tracking id stable across combo target retries ([#12650](https://github.com/diegosouzapw/OmniRoute/pull/12650)) — thanks @hartmark +- **fix(tests):** retire dead model ids from the chat-pipeline integration suite (base-red #12581) ([#12670](https://github.com/diegosouzapw/OmniRoute/pull/12670)) +- **fix:** resolve SqliteError no such table compression_run_telemetry during cleanup ([#12682](https://github.com/diegosouzapw/OmniRoute/pull/12682)) — thanks @groovecityJO +- **fix(combo):** treat a pin-only step as implicit connection allowlist ([#12697](https://github.com/diegosouzapw/OmniRoute/pull/12697)) — thanks @HouMinXi +- **fix(ci):** ship .npmrc in published package so legacy-peer-deps applies to consumers (#11544) ([#12699](https://github.com/diegosouzapw/OmniRoute/pull/12699)) — thanks @KooshaPari +- **fix(docker):** pin 4 CLI tools to exact versions (#12576) ([#12703](https://github.com/diegosouzapw/OmniRoute/pull/12703)) — thanks @KooshaPari +- **fix(docker):** document and harden cli profile trust boundary (#12570) ([#12706](https://github.com/diegosouzapw/OmniRoute/pull/12706)) — thanks @KooshaPari +- **fix(sse):** redact video transcript in pre-guardrail rejected-request logs (#12150 P2 item 7) ([#12710](https://github.com/diegosouzapw/OmniRoute/pull/12710)) +- **fix(i18n):** quote placeholder in OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES description (#12505) ([#12769](https://github.com/diegosouzapw/OmniRoute/pull/12769)) — thanks @KooshaPari +- **fix(api/pricing):** surface validation error message as string, not raw object (#12494) ([#12771](https://github.com/diegosouzapw/OmniRoute/pull/12771)) — thanks @KooshaPari +- **fix(sse):** re-enable prompt compression for native Codex passthrough ([#12834](https://github.com/diegosouzapw/OmniRoute/pull/12834)) — thanks @thomasmaerz +- **fix(dashboard):** model health tests for a provider node set to the Responses API now call `/v1/responses` with a Responses-shaped body instead of `/v1/chat/completions` — those models were reported as `Provider returned HTTP 200 but no text content` even though the same model answered normally through `/v1/responses` ([#13070](https://github.com/diegosouzapw/OmniRoute/issues/13070)) ([#13087](https://github.com/diegosouzapw/OmniRoute/pull/13087)) — thanks @ntdatt812 +- fix(routing): stop a round-robin combo's "opencode" targets from collapsing onto the opencode-zen connection (#11912) ([#13283](https://github.com/diegosouzapw/OmniRoute/pull/13283)) +- fix(routing): stop the reactive-compaction debug log from lying when compression is globally disabled (#11977) ([#13276](https://github.com/diegosouzapw/OmniRoute/pull/13276)) +- fix(electron): relativize standalone-bundle symlink targets so Stage 8 manifest verification stops failing on Windows (#11979) ([#13251](https://github.com/diegosouzapw/OmniRoute/pull/13251)) +- fix(dashboard): surface a visible error when Compression Studio's combined preview run fails (#12061) ([#13288](https://github.com/diegosouzapw/OmniRoute/pull/13288)) +- fix(dashboard): make the compression "Effective pipeline" preview honor the active profile and warn when the master switch is off (#12063) ([#13287](https://github.com/diegosouzapw/OmniRoute/pull/13287)) +- fix(providers): scope TinyCMS Web signer's DOM shims to each call instead of leaking them for the process lifetime, and surface a clean HTTP status on a non-JSON interception-toggles error (#12072) ([#13254](https://github.com/diegosouzapw/OmniRoute/pull/13254)) +- fix(guardrails): stop Vision Bridge from re-selecting a model locked after a 404 (#12111) ([#13259](https://github.com/diegosouzapw/OmniRoute/pull/13259)) +- fix(sse): require Responses-shaped body before native OpenAI-compatible passthrough (#12129) ([#13278](https://github.com/diegosouzapw/OmniRoute/pull/13278)) +- fix(providers): minimax-m3 now collapses manual thinking.type:"enabled" to adaptive, preventing upstream 400 (2013) (#12132) ([#13249](https://github.com/diegosouzapw/OmniRoute/pull/13249)) +- fix(db): scope model visibility overrides by modality so hiding a Chat model no longer hides an identically-ID'd Image/Embeddings/etc. model (#12172) ([#13258](https://github.com/diegosouzapw/OmniRoute/pull/13258)) +- fix(db): scope local-provider apiKey dedup to matching base URL so LM Studio/Ollama-style connections support multiple accounts (#12173) ([#13250](https://github.com/diegosouzapw/OmniRoute/pull/13250)) +- fix(providers): refresh Trae's stale Referer/Origin and forward user timezone so imported connections stop failing with 401 (#12190) ([#13255](https://github.com/diegosouzapw/OmniRoute/pull/13255)) +- fix(providers): route opencode-go/gpt-5.6-luna to /responses instead of /chat/completions (#12196) ([#13275](https://github.com/diegosouzapw/OmniRoute/pull/13275)) +- fix(dashboard): allow deleting the last extra-upstream-header row even when invalid (#12251) ([#13252](https://github.com/diegosouzapw/OmniRoute/pull/13252)) +- **fix(i18n):** translate pre-existing `__MISSING__:` keys for `combo.sort`, `requestLogger.detail` expand/collapse, `common.profile`, and `settings.resilienceCredentialHealth*` across 39 locales ([#12272](https://github.com/diegosouzapw/OmniRoute/issues/12272)) ([#13034](https://github.com/diegosouzapw/OmniRoute/pull/13034)) — thanks @HouMinXi +- fix(cli): run the Node.js runtime compatibility guard before the heavy `tsx/esm` + Commander import chain so an unsupported runtime gets a clear message instead of a raw `Invalid regular expression flags` crash (#12296) ([#13253](https://github.com/diegosouzapw/OmniRoute/pull/13253)) +- fix(dashboard): refresh the providers list after deleting a compatible provider node (#12298) ([#13256](https://github.com/diegosouzapw/OmniRoute/pull/13256)) +- fix(usage): fail closed on API-key budget enforcement when a provider's `auto` routing alias has no pricing row, instead of silently counting it as $0 (#12341) ([#13257](https://github.com/diegosouzapw/OmniRoute/pull/13257)) +- **fix(providers):** include the submitted Agnes video model when polling by `video_id` ([#12356](https://github.com/diegosouzapw/OmniRoute/pull/12356)) — thanks @pacocartones +- **fix(routing):** custom OpenAI-compatible nodes now honor the saved Chat/Responses API type after edits instead of letting the node's original ID prefix override the live connection setting ([#11884](https://github.com/diegosouzapw/OmniRoute/issues/11884)). ([#12358](https://github.com/diegosouzapw/OmniRoute/pull/12358)) — thanks @pacocartones +- fix(sse): surface an error instead of a silent empty 200 when a Claude stream closes with zero bytes (#12398) ([#13285](https://github.com/diegosouzapw/OmniRoute/pull/13285)) +- fix(oauth): warn before the dead localhost:8080 redirect in antigravity/gemini `oauth start` (#12413) ([#13265](https://github.com/diegosouzapw/OmniRoute/pull/13265)) +- fix(providers): stop devin-cli spawn error from double-closing the SSE controller (#12517) ([#13261](https://github.com/diegosouzapw/OmniRoute/pull/13261)) +- **fix(rerank):** clamp Voyage `top_k` to the documents actually sent after empty-string filtering, and honor `return_documents: false` in the NVIDIA response adapter (#12523 — thanks @pacocartones) +- **fix(audio):** `/v1/audio/translations` now resolves combo names the way `/v1/audio/transcriptions` already does, so a combo that `GET /v1/models` advertises is fanned out to its targets instead of being rejected with `400 Invalid translation model: . Use format: provider/model`; literal `provider/model` ids and unknown bare names behave as before (#12536 — thanks @pacocartones) +- **fix(gemini):** strip the JSON-Schema-2020-12 `prefixItems` keyword from Gemini tool schemas at every nesting level, so Claude Code tool definitions no longer fail with `400 Unknown name "prefixItems"` on Gemini models (#12540 — thanks @pacocartones) +- **fix(api-manager):** Expose an accessible loading status while API keys are fetched instead of an empty accessibility tree (#12541 — thanks @pacocartones) +- **fix(video):** Clamp `estimateJpegFrameBytes` at zero for padding-only payloads and build the three encode-side frame data URIs from `JPEG_FRAME_DATA_URI_PREFIX` instead of a repeated literal (#12543 — thanks @pacocartones) +- **fix(devin):** accept Windows `DEVIN_AGENTIC_HOME` sandbox paths (`C:\...\.sandbox\...`) in the isolated-home check so the Devin Claude Bridge no longer fails closed on Windows ([#12405](https://github.com/diegosouzapw/OmniRoute/issues/12405)) (#12545 — thanks @pacocartones) +- **fix(db):** Add `conversation_turn_nodes` and orphaned `agentic_conversations` to the auto-cleanup cycle under the existing `retention.callLogs` window, so identity nodes whose call-log content has already been purged no longer accumulate without bound in `storage.sqlite` (#12548 — thanks @pacocartones) +- **fix(i18n):** Wrap the `~/.claude/profiles//settings.json` placeholder in ICU single quotes in the `OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES` feature-flag description across all 42 locales and the TypeScript default, so next-intl no longer fails with `INVALID_MESSAGE: UNCLOSED_TAG` and the Feature Flags card shows the description instead of the raw key (#12549 — thanks @pacocartones) +- **fix(orchestration):** `updateCloudAgentTask` now publishes the task's real `status` on `agent.task.updated` when an update only touches `result`, `activities` or `error`, instead of the fabricated `"updated"` state, and stays silent when no row matched the id (#12550 — thanks @pacocartones) +- **fix(i18n):** the home "Recent Requests" panel and the Provider Topology legend are now translated instead of rendering English copies on non-English dashboards; the legend reads its own `home.topologyLegend*` labels with consistent casing rather than borrowing the memory-settings "Recent" and analytics "Error" strings (#12551 — thanks @pacocartones). +- **docs(reference):** bring the `FEATURE_FLAGS.md` catalog back to 1:1 with `featureFlagDefinitions.ts` — 20 missing flags added, the two `*_BLOCK_THRESHOLD` env-only knobs moved out of the flag tables, category/total counts and the Live WS port corrected, guarded by a static test (#12552 — thanks @pacocartones) +- **fix(i18n):** backfill missing `usage.kiloPass*` strings in 39 locales and restore `featureFlagOmnirouteDisableThinkingLevelVariantsDescription` in `pt.json` ([#12561](https://github.com/diegosouzapw/OmniRoute/issues/12561)) ([#13027](https://github.com/diegosouzapw/OmniRoute/pull/13027)) — thanks @HouMinXi +- fix(docker): default docker-compose app ports (dashboard/API/live-WS) to loopback instead of `0.0.0.0`, closing the anonymous `/v1` LAN/WAN exposure gap left open by `REQUIRE_API_KEY=false` (#12568) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): close DNS-rebinding SSRF gap in webhook outbound-URL guard (#12569) ([#13243](https://github.com/diegosouzapw/OmniRoute/pull/13243)) +- fix(docker): require a per-session token on the VNC browser CDP bridge and isolate it on a dedicated Docker network (#12571) ([#13241](https://github.com/diegosouzapw/OmniRoute/pull/13241)) +- fix(open-sse): write Adobe Firefly session tokens and cookie jars with 0700/0600 permissions instead of the process umask (#12572) ([#13242](https://github.com/diegosouzapw/OmniRoute/pull/13242)) +- fix(api): remove hardcoded wildcard CORS in openai-to-gemini-sse.ts so the centralized fail-closed CORS gate is the sole source of `Access-Control-Allow-Origin` (#12573) ([#13239](https://github.com/diegosouzapw/OmniRoute/pull/13239)) +- fix(api): enforce API key policy (budget/rate-limit/schedule/endpoint scoping) on the ElevenLabs speech-to-text, text-to-speech and voices proxy routes (#12574) ([#13244](https://github.com/diegosouzapw/OmniRoute/pull/13244)) +- fix(sse): cap HuggingChat NDJSON body size and bound the read loop with the fetch timeout so a stalled or hostile upstream cannot buffer unbounded memory (#12577) ([#13282](https://github.com/diegosouzapw/OmniRoute/pull/13282)) +- fix(docker): scope the cliproxyapi/qdrant/bifrost sidecars to loopback by default and forward `CLIPROXYAPI_MANAGEMENT_KEY` into the cliproxyapi container so its management API is not left both unauthenticated and LAN-published (#12578) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): create DB export temp paths with `fs.mkdtempSync` instead of predictable timestamps (#12579) ([#13237](https://github.com/diegosouzapw/OmniRoute/pull/13237)) +- Cline 401 bodies that say "re-authenticate your Cline account" classify as a refreshable OAuth token, not a terminal expired key. The cooling panel no longer labels every cooldown as a 429; it shows the recorded last error instead. (#12594) ([#13060](https://github.com/diegosouzapw/OmniRoute/pull/13060)) — thanks @HouMinXi +- **fix(catalog):** degrade unknown combo targets instead of dropping LCD modalities, and persist OpenRouter `architecture.input_modalities` into the capability snapshot ([#12613](https://github.com/diegosouzapw/OmniRoute/issues/12613)) ([#13042](https://github.com/diegosouzapw/OmniRoute/pull/13042)) — thanks @HouMinXi +- fix(providers): send `x-api-key` instead of `Authorization: Bearer` for OpenCode Zen's `/v1/responses` endpoint (Muse Spark Contributor models), fixing a 401 on OmniRoute's auth header (#12633) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(sse): surface the actionable "Auggie CLI not found" message when the shell reports a missing `auggie` binary via exit code instead of a spawn error (#12645) ([#13263](https://github.com/diegosouzapw/OmniRoute/pull/13263)) +- **fix(config):** Persist deletions of built-in background-degradation entries — when a stored settings record exists its `degradationMap` is now authoritative instead of being merged under the defaults, so an entry the user removed in the dashboard no longer reappears on the next apply or restart ([#12424](https://github.com/diegosouzapw/OmniRoute/issues/12424)) ([#12647](https://github.com/diegosouzapw/OmniRoute/pull/12647)) — thanks @pacocartones +- **fix(gamification):** action-count badge milestones (First Token, Token Consumer, Token Machine, Token Whale, and the token-sharing tier) are now backed by a durable `xp_action_counts` counter incremented in `addXp()`, instead of a live `COUNT(*)` over `xp_audit_log`. The audit log is pruned by `retention.xpAuditLog` (default 30 days), so on a default install those "lifetime" milestones were really "actions in the last 30 days" and unlocked badges could stop unlocking once old rows aged out. `getActionCount()` and `checkActionCountBadges()` now read the same durable source, and a migration backfills existing totals from the surviving audit rows ([#12546](https://github.com/diegosouzapw/OmniRoute/issues/12546)) ([#12651](https://github.com/diegosouzapw/OmniRoute/pull/12651)) — thanks @pacocartones +- **fix(images):** `/v1/images/edits` now iterates a combo's targets the same way `/v1/images/generations` does (#9239) instead of flattening a bare combo to its first target. A combo whose first target is not edit-capable — or lacks credentials — now falls through to a later edit-capable target rather than hard-erroring, and missing credentials are skipped (not a hard `401`) to match the generations path. The per-target skip/terminal classification is extracted into a shared `runImageComboTargets` loop, so generations behavior is unchanged ([#12547](https://github.com/diegosouzapw/OmniRoute/issues/12547)). ([#12653](https://github.com/diegosouzapw/OmniRoute/pull/12653)) — thanks @pacocartones +- fix(sse): add first-byte watchdog to the TLS-fingerprint transport so a stalled wreq body falls back instead of hanging for minutes (#12656) ([#13272](https://github.com/diegosouzapw/OmniRoute/pull/13272)) +- fix(sse): exempt tiny-budget reasoning probes from combo quality failure and surface persisted-cooldown skips in ALL_TARGETS_SKIPPED diagnostics (#12659) ([#13269](https://github.com/diegosouzapw/OmniRoute/pull/13269)) +- fix(models): declare the real ~1M contextLength for OpenCode Zen's Muse Spark 1.2 models instead of falling back to the 200000 provider default (#12681) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(oauth): align codebuddy-cn OAuth User-Agent with the chat/usage CLI version to avoid WAF false positives (#12702) ([#13264](https://github.com/diegosouzapw/OmniRoute/pull/13264)) +- fix(dashboard): surface an authentication-required banner instead of silently blanking database settings for a guest session (#12709) ([#13270](https://github.com/diegosouzapw/OmniRoute/pull/13270)) +- **fix(resilience):** stop unbounded queue that hangs 6min until Aborted — gate, provider slot, and Bottleneck queue now share a per-connection `maxWaitMs` budget; `fail-closed` on exhaust (503); execution backstop `executionMaxWaitMs` overridable per connection with upstream clamp ([#12715](https://github.com/diegosouzapw/OmniRoute/pull/12715)) — thanks @maxmad64bis (with thanks to @Tushar49 for surfacing the slow-provider need in #12635) +- **fix(auto-combo):** every mode pack now carries `quality` and `reliability` weights (`quality` 0.02, 0.03 in `quality-first`; `reliability` 0.03, 0.04 in `reliability-first`), so selecting a pack no longer silences either signal; `DEFAULT_WEIGHTS` is unchanged ([#12731](https://github.com/diegosouzapw/OmniRoute/pull/12731)) — thanks @maxmad64bis +- **fix(sse):** the provider execution pipeline keeps the upstream error `code`/`type` again — both were lost when the non-streaming leg was extracted, so a config-class `422` (Antigravity missing project) degraded into a generic account cooldown instead of reaching the gate that recognises it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(db):** a legacy `call_logs` table no longer aborts startup — the provider-stats index is created after the column healing runs, not before it, so an install predating `request_type` opens instead of failing with `no such column` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(catalog):** a malformed row in the operator-writable custom-models blob no longer kills every `auto/*` pool ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(tests):** realign the guards that the same day's merges left asserting the old behavior — the provider-count total after GreenPT and EURouter, the GLM stream buffer slot that `#12925` turned into a declared parameter, and the injection scan bound that `#13104` reshaped from a head-only window into head-plus-tail ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- fix(cache): fold tool_choice/tools/response_format into the semantic cache signature so a cached tool_calls response can no longer be replayed for a request whose tool policy forbids it (#12734) ([#13267](https://github.com/diegosouzapw/OmniRoute/pull/13267)) +- **fix(models):** stop treating provider-supplied `isFree:true`/`:free`/`0/0` flags as trusted unless the provider has a documented free tier — only the shipped free-tier catalog decides otherwise; fetched `isFree:true` trusted only for a free-tier provider, custom `isFree:true` via a trusted path ([#12744](https://github.com/diegosouzapw/OmniRoute/pull/12744)) — thanks @maxmad64bis +- fix(memory): authenticate the internal /v1/rerank loopback call so memory reranking no longer silently degrades to unranked order when REQUIRE_API_KEY=true (#12745) ([#13268](https://github.com/diegosouzapw/OmniRoute/pull/13268)) +- fix(sse): parse Ollama Cloud's current usage markup (`$X of $Y used` aria-label, nested width style) (#12749) ([#13260](https://github.com/diegosouzapw/OmniRoute/pull/13260)) +- fix(cli): setup-opencode no longer sends an active context's management token to `/v1/models` when `--api-key`/`OMNIROUTE_API_KEY` is supplied — an explicit flag or the env var now always outranks the context's token, and the flag itself is no longer swallowed by the parent program's global `--api-key` option (#12783) ([#13246](https://github.com/diegosouzapw/OmniRoute/pull/13246)) +- fix(sse): register Arcee AI in the executor provider registry so requests reach api.arcee.ai instead of silently falling back to OpenAI (#12784) ([#13277](https://github.com/diegosouzapw/OmniRoute/pull/13277)) +- **fix(quota):** share one upstream quota read when identical saturation checks arrive at the same time, so traffic bursts don't multiply provider API calls ([#12787](https://github.com/diegosouzapw/OmniRoute/pull/12787)) — thanks @maxmad64bis +- **fix(db):** ignore expired or invalid rate-limit cooldown writes so a stale timestamp can't lock a connection that should be usable — clearing still works as before ([#12788](https://github.com/diegosouzapw/OmniRoute/pull/12788)) — thanks @maxmad64bis +- **fix(providers):** single source for provider order with `xao` ranking alongside `xai-oauth` ([#12790](https://github.com/diegosouzapw/OmniRoute/pull/12790)) — thanks @maxmad64bis +- **fix(routing):** off-table models check the free-model catalog before inheriting premium prices, latency bootstraps from the observed pool median, async tiers read live database pricing with a 90-day freshness gate, and the tier cache invalidates on every pricing write ([#12792](https://github.com/diegosouzapw/OmniRoute/pull/12792)) — thanks @maxmad64bis +- **fix(auto-combo):** snapshot scoring uses the observed per-connection account tier and per-model quality instead of neutral constants, and circuit-open providers rank lower at build time ([#12794](https://github.com/diegosouzapw/OmniRoute/pull/12794)) — thanks @maxmad64bis +- **fix(pool):** empty `auto/*` pools now say why they are empty, and the models listing uses the same paid check as routing ([#12795](https://github.com/diegosouzapw/OmniRoute/pull/12795)) — thanks @maxmad64bis +- fix(routing): recognize CLIProxyAPI's 'unknown provider for model' 400 as fallback-worthy (#12800) ([#13284](https://github.com/diegosouzapw/OmniRoute/pull/13284)) +- **fix(sse):** translate-mode streams now emit the estimated token counts as a trailing usage-only chunk before `[DONE]` when the upstream stays silent, so metered chat clients see totals instead of nothing ([#12828](https://github.com/diegosouzapw/OmniRoute/pull/12828)) — thanks @maxmad64bis +- **fix(db):** provider stats stay truthful on empty and legacy databases: fallback counts default to `0` instead of `null`, latency averages read `null` (not `0`) when no durations were recorded, failures logged before the error-type column existed group under `pre_migration` instead of `unclassified`, and per-provider queries use two new composite indexes ([#12832](https://github.com/diegosouzapw/OmniRoute/pull/12832)) — thanks @maxmad64bis +- fix(nvidia): fail open when a synced model catalog goes stale instead of gating forever (#12849) ([#13248](https://github.com/diegosouzapw/OmniRoute/pull/13248)) +- **fix(db):** WAL maintenance lives in its own module: a `TRUNCATE` checkpoint that hits a busy database now warns with its streak and retries once via `PASSIVE` instead of logging success, and closing-time checkpoints no longer report success on builds without a database file, with the busy totals visible in the authenticated monitoring health payload ([#12853](https://github.com/diegosouzapw/OmniRoute/pull/12853)) — thanks @maxmad64bis +- **fix(health):** quota with no snapshots reads empty instead of a contradicting 0% ([#12857](https://github.com/diegosouzapw/OmniRoute/pull/12857)) — thanks @maxmad64bis +- fix(a2a): accept the dashboard's own session cookie on /a2a so "Run message/send" no longer fails with "Unauthorized: missing or invalid API key" (#12888) ([#13271](https://github.com/diegosouzapw/OmniRoute/pull/13271)) +- **fix(logs):** the Logs grid's in-memory filter pass no longer discards rows the SQL query already matched — selecting an API key from the dropdown (which sends the key's id) returns its calls again, the Combo tab shows every combo instead of only those whose name contains a "1", and the model filter and search cover the same columns as the query ([#12896](https://github.com/diegosouzapw/OmniRoute/pull/12896)) — fixes [#12873](https://github.com/diegosouzapw/OmniRoute/issues/12873) — thanks @ntdatt812 +- **fix(a2a):** `/api/a2a/status` now builds the agent card from the request that asked for it, so a gateway reached at a non-localhost host no longer advertises `http://localhost:20128` as its A2A URL ([#12918](https://github.com/diegosouzapw/OmniRoute/pull/12918)). — thanks @ntdatt812 +- **fix(compression):** progressive aging now appends its `[COMPRESSED:aging:…]` annotation after a turn's `tool_result` blocks instead of in front of them, so Anthropic no longer rejects aged conversations with "`tool_use` ids were found without `tool_result` blocks immediately after" ([#12920](https://github.com/diegosouzapw/OmniRoute/pull/12920)). — thanks @ntdatt812 +- **fix(bedrock):** model import now resolves context limits for every vendor prefix instead of only `anthropic.*`, so `global.openai.gpt-5.6-*` no longer imports with a null `inputTokenLimit` and gets rejected pre-flight at the 200k default ([#12921](https://github.com/diegosouzapw/OmniRoute/pull/12921)). — thanks @ntdatt812 +- **fix(stream):** the 64 KB stream buffer GLM asks for is honoured instead of dropped, and the type error it caused no longer fails the API Route Typecheck gate on every open PR ([#12925](https://github.com/diegosouzapw/OmniRoute/pull/12925)) — thanks @ntdatt812 +- **fix(guardrails):** mask PII inside a `tool_result`'s nested content array, which the masker walked past while redacting its sibling block ([#12930](https://github.com/diegosouzapw/OmniRoute/pull/12930)) — thanks @ntdatt812 +- **fix(dashboard):** the Radar catalog table no longer leaves absent data unexplained — empty limits, unknown context windows, and unreported capabilities each explain themselves on hover, and a new check keeps it that way ([#12937](https://github.com/diegosouzapw/OmniRoute/pull/12937)) — thanks @maxmad64bis +- **fix(sse):** geo-blocked opencode requests rotate to the next account proxy instead of failing, so one refused egress no longer aborts the whole chain ([#12941](https://github.com/diegosouzapw/OmniRoute/pull/12941)) — thanks @maxmad64bis +- **fix(codex):** dashboard "clear cooldown" and the CAS recovery path now drop nested `codexScopeRateLimitedUntil` maps in the same write that nulls `rate_limited_until`, and fresh quota snapshots with headroom lift fallback-sourced scope cooldowns parked by quota preflight ([#12817](https://github.com/diegosouzapw/OmniRoute/issues/12817), [#12860](https://github.com/diegosouzapw/OmniRoute/issues/12860), [#12951](https://github.com/diegosouzapw/OmniRoute/pull/12951)) The dashboard reset-credit button (`consumeCodexResetCredit`) now exercises that same snapshot path after a successful redeem, so a filled quota bar is enough to unpark a leftover fallback Codex child without another manual clear. — thanks @HouMinXi +- **fix(db):** resolve `sql-wasm.wasm` across global npm install and hoisted layouts, ensuring OmniRoute can boot cleanly on Node 24 when native `better-sqlite3` is uncompiled. ([#13035](https://github.com/diegosouzapw/OmniRoute/pull/13035)) — thanks @HouMinXi +- **fix(api):** `DELETE /v1/batches/delete-completed` now sweeps only the calling API key's own completed batches (batches with no owner stay out of a key-scoped sweep on purpose), with an explicit instance-wide mode reserved for authenticated dashboard sessions, a 401 for a presented key that is unknown, revoked, deactivated, banned or expired (never falling through to the session branch), audit logging of every sweep, a sanitized 500 on failure, an owner-scoped file half (a key-scoped sweep never nulls a file another tenant owns) and an atomic sweep — chunked in 200-batch transactions in instance mode — so a mid-way error never leaves a batch pointing at a nulled file (GHSA-wvxc-jp3v-5mg5) ([#12969](https://github.com/diegosouzapw/OmniRoute/pull/12969)) +- **fix(combo):** quota-weighted routing stops drawing on an out-of-credit connection — a 402 now invalidates the stored quota snapshot instead of leaving its stale remaining percentage in place, and a snapshot older than 10 minutes no longer counts as confident headroom for the primary pool ([#12972](https://github.com/diegosouzapw/OmniRoute/pull/12972)) ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- **fix(vision):** Custom Models with "Vision capable" checked no longer have image requests swapped to `glm/glm-4.6v` when the client sends the advertised alias (`vllm/path/...`) or the bare path-shaped id — Vision Bridge now matches the stored override for all three id forms ([#12758](https://github.com/diegosouzapw/OmniRoute/issues/12758)) ([#12974](https://github.com/diegosouzapw/OmniRoute/pull/12974)) — thanks @HouMinXi +- **fix(sse):** transient opencode upstream failures rotate to the next account proxy instead of failing, so one flapping egress no longer aborts the whole chain ([#12975](https://github.com/diegosouzapw/OmniRoute/pull/12975)) — thanks @maxmad64bis +- **fix(azure):** Deployments from GPT-6 onward now send `max_completion_tokens` instead of `max_tokens`, which Azure rejects with HTTP 400. The rule matched a literal `gpt-5`, so each new generation arrived broken; it now matches the generation range, while `gpt-35-turbo` still keeps `max_tokens`. ([#13007](https://github.com/diegosouzapw/OmniRoute/pull/13007)) — thanks @ntdatt812 +- **fix(db):** add an opt-in self-restart circuit for sustained critical memory pressure, gate post-cleanup VACUUM behind a minimum freed-rows threshold, and checkpoint the SQLite WAL every 5 minutes with a size guard that escalates to TRUNCATE, so a growing WAL can no longer stall the event loop into a full outage. ([#13011](https://github.com/diegosouzapw/OmniRoute/pull/13011)) — thanks @HouMinXi +- **fix(auth):** an explicit connection pin may probe a quota-disabled row once and re-enable it on success ([#12874](https://github.com/diegosouzapw/OmniRoute/issues/12874)) ([#13017](https://github.com/diegosouzapw/OmniRoute/pull/13017)) — thanks @HouMinXi +- **fix(api):** thread `X-OmniRoute-Fallback-Attempts` through combo chat completions so streaming and non-streaming responses report how many prior legs were attempted ([#13038](https://github.com/diegosouzapw/OmniRoute/pull/13038)) — thanks @HouMinXi +- Restore provider failure classification and credential refresh on non-streaming requests: classify non-2xx failures to lock models on per-model quota exhaustion, update connection rate limits from headers and body, and pass credential refresh handlers to pipeline execution so 401 tokens can be refreshed and retried (#13043). ([#13069](https://github.com/diegosouzapw/OmniRoute/pull/13069)) — thanks @HouMinXi +- **fix(responses):** wrap forced-non-streaming web_search fallback JSON as Responses SSE so Codex still sees `response.completed` ([#13050](https://github.com/diegosouzapw/OmniRoute/pull/13050)) — thanks @HouMinXi +- **fix(antigravity):** Preserve upstream thought-token usage in normalized completion and reasoning token counts ([#13055](https://github.com/diegosouzapw/OmniRoute/pull/13055)) — thanks @pacocartones +- Fix a concurrency-slot leak in the DeepSeek PoW solver: a worker that failed to spawn (for example a missing worker script) never released its slot, so `MAX_CONCURRENT_WORKERS` failures disabled the solver until restart. ([#13097](https://github.com/diegosouzapw/OmniRoute/pull/13097)) — thanks @anhtahaylove +- **fix(acp):** bound the ACP session output buffers — `stdoutBuffer` and `stderrBuffer` now cap at 1 MiB keeping the most recent output behind a visible `[...output truncated...]` marker, and `stderrBuffer` is reset per prompt instead of accumulating for the lifetime of the session. ([#13100](https://github.com/diegosouzapw/OmniRoute/pull/13100)) — thanks @anhtahaylove +- **fix(acp):** release the `stdout`/`exit` listeners and the idle timer that a `sendPrompt` timeout used to leave attached to the `acpManager` singleton, and drop sessions that exited on their own from the session map instead of keeping them forever. ([#13096](https://github.com/diegosouzapw/OmniRoute/pull/13096)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection and PII scanners now read the text a `tool_result` block carries on `content` (string or nested block list), in messages and in system blocks, so tool output is judged by the same rules as user text ([#13101](https://github.com/diegosouzapw/OmniRoute/pull/13101)) — thanks @ntdatt812 +- **fix(gamification):** close the badge notification SSE stream when the request signal is already aborted before the stream starts — a client that disconnects while the route is still awaiting auth used to leave both the 2s unlock poll and the 15s heartbeat running for the lifetime of the process. ([#13106](https://github.com/diegosouzapw/OmniRoute/pull/13106)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection scan now spends its 16 KB budget on both ends of the request instead of the first 16 KB only, so `system`, `instructions`, `query`, `documents` and the newest turns are no longer hidden behind one long message ([#13104](https://github.com/diegosouzapw/OmniRoute/pull/13104)) — thanks @ntdatt812 +- **fix(dashboard):** expose the Volcano Ark console cookie on quota scraping and unwrap connect-error objects so the dashboard shows the upstream message ([#13107](https://github.com/diegosouzapw/OmniRoute/pull/13107)) — thanks @HouMinXi +- **fix(db):** release the `beforeExit`/`SIGINT`/`SIGTERM` handlers when a `node:sqlite` adapter closes, so a closed adapter and its database handle are no longer pinned to `process` for the lifetime of the run — the same treatment #7494 gave the sql.js adapter. ([#13109](https://github.com/diegosouzapw/OmniRoute/pull/13109)) — thanks @anhtahaylove +- **fix(translator):** `contentSchema` and `unevaluatedItems` are now treated as subschema positions by the tool-schema sanitizer, so a truncation placeholder in either is replaced with a permissive schema instead of being forwarded as a string ([#13110](https://github.com/diegosouzapw/OmniRoute/pull/13110)) — thanks @ntdatt812 +- **fix(cli-helper):** clear the `createLogStream` timeout on the abort path — `stop()` aborts the in-flight fetch and returned through the `signal.aborted` branch, which skipped `clearTimeout` and left an armed timer per stopped stream. The stream reader is now also cancelled when the read loop exits early. ([#13114](https://github.com/diegosouzapw/OmniRoute/pull/13114)) — thanks @anhtahaylove +- **fix(docs):** drop TLS-impersonation, MITM-decrypt, supply-chain attestation, and XOR-mask writeups from the public `/docs` catalog and Docker image. Files stay in git for engineers; operators who need them open the repo, not the website. ([#13136](https://github.com/diegosouzapw/OmniRoute/pull/13136)) — thanks @HouMinXi +- **fix(combo):** parse numeric-epoch `rate_limited_until` in the combo cooldown read path ([#13141](https://github.com/diegosouzapw/OmniRoute/pull/13141)) — thanks @maxmad64bis +- **fix(opencode):** both OpenCode plugins now reject a gateway address typed without `http://` at configuration time, instead of publishing every model with an api url no client can call, and the v2 plugin no longer publishes a model card whose api url is blank or relative ([#13142](https://github.com/diegosouzapw/OmniRoute/pull/13142)) — thanks @maxmad64bis +- **fix(providers):** lock opencode model on upstream 400 model-unavailable ([#13146](https://github.com/diegosouzapw/OmniRoute/pull/13146)) — thanks @maxmad64bis +- **fix(logging):** keep the provider exchange rather than the raw client bodies when a call log exceeds its size budget, and show that recovered payload in the request-detail panel instead of replacing it with the stored response body ([#13147](https://github.com/diegosouzapw/OmniRoute/pull/13147)) — thanks @maxmad64bis +- **fix(traffic-inspector):** the WebSocket route no longer leaks a traffic-buffer subscriber and a 30s ping timer when the client socket is already closed at handler time — listeners are attached before any resource is acquired, a destroyed socket bails out early, and the ping interval stops on a dead socket where `write()` never throws ([#13155](https://github.com/diegosouzapw/OmniRoute/pull/13155)) — thanks @anhtahaylove +- **fix(telegram):** bound the per-user API key cache in the Telegram chat proxy so a burst of distinct chat ids can no longer grow the process heap without limit ([#13165](https://github.com/diegosouzapw/OmniRoute/issues/13165)) ([#13166](https://github.com/diegosouzapw/OmniRoute/pull/13166)) — thanks @anhtahaylove +- **fix(stream):** cancel the upstream response body when the JSON-to-SSE sniff unwinds on a body timeout, so a stalled upstream no longer pins the connection ([#13169](https://github.com/diegosouzapw/OmniRoute/issues/13169)) ([#13171](https://github.com/diegosouzapw/OmniRoute/pull/13171)) — thanks @anhtahaylove +- **fix(telegram):** authenticate webhook deliveries with Telegram's `secret_token` so an unauthenticated caller can no longer mint API keys or spend upstream quota ([#13172](https://github.com/diegosouzapw/OmniRoute/issues/13172)) ([#13175](https://github.com/diegosouzapw/OmniRoute/pull/13175)) — thanks @anhtahaylove +- **fix(models):** give discoverable Gemini 3.8 Flash ids their own 65536 output spec so Antigravity no longer clamps them to 16384 ([#13195](https://github.com/diegosouzapw/OmniRoute/pull/13195)) — thanks @HouMinXi +- **fix(dashboard):** leftover catalog-removed provider rows (gemini-cli) can be listed and purged from the providers page ([#13067](https://github.com/diegosouzapw/OmniRoute/issues/13067)) ([#13197](https://github.com/diegosouzapw/OmniRoute/pull/13197)) — thanks @HouMinXi +- **fix(auth):** a dashboard session now requires the `authenticated: true` claim that login, OIDC and the session refresh already emit — a JWT merely signed with `JWT_SECRET` (for example the Cursor CLI passthrough token, which any API-key holder can obtain) no longer verifies as the `auth_token` cookie on any route, the WebSocket handshake or the live server; existing sessions keep working ([#13298](https://github.com/diegosouzapw/OmniRoute/issues/13298)) ([#13375](https://github.com/diegosouzapw/OmniRoute/pull/13375)) +- **fix(api):** `DELETE /v1/batches/delete-completed` now applies the caller's API-key policy (endpoint allowlist, schedule, usage cap, rate limit) like every other `/v1` route, commits the key-scoped sweep per 200-batch chunk instead of holding one write lock for the whole sweep, refuses to loop without progress, and rejects a scope that names both a key and `allTenants` ([#13684](https://github.com/diegosouzapw/OmniRoute/pull/13684)) +- Restore the API-route typecheck gate: the non-streaming leg result lost its discriminated-union narrowing after the server-owned tool loop reassignment, producing 13 new TS2339 diagnostics in `chatCore.ts`. ([#12963](https://github.com/diegosouzapw/OmniRoute/pull/12963)) +- Restore four non-streaming behaviours the server-owned tool loop refactor dropped: client aborts map to 499 with the fixed `Request aborted` message, an aborted request no longer logs a synthetic `clientResponse`, Claude prompt-cache telemetry is recorded again, and a body that cannot be canonicalized no longer throws when the tool loop is off. ([#12990](https://github.com/diegosouzapw/OmniRoute/pull/12990)) +- **fix(skills):** The CLI registry parser now reads positionals declared with `.addArgument()`, not only those written inline in `.command()`. `tunnel create [type]` was being published as `tunnel create`, so the agent-skills sync gate reported drift on every branch and regenerating would have deleted the argument. ([#13009](https://github.com/diegosouzapw/OmniRoute/pull/13009)) — thanks @ntdatt812 +- **fix(combos):** dashboard combo test uses a short prompt, serial probes, and a 60s timeout so reasoning models and rate-limited free pools do not fail the health check ([#13001](https://github.com/diegosouzapw/OmniRoute/pull/13001)) — thanks @HouMinXi +- fix(compression): terminate idle worker threads on eviction so long-running instances stop leaking OS threads and MessagePorts ([#13091](https://github.com/diegosouzapw/OmniRoute/pull/13091)) — thanks @anhtahaylove +- Redact Google API keys of any length in error bodies: the pattern required exactly 39 characters, so shorter or longer `AIza…` credentials passed through unredacted. ([#12964](https://github.com/diegosouzapw/OmniRoute/pull/12964)) +- **fix(grok-cli):** a 402 "Grok Build usage balance exhausted" parks that Grok login as out of credit (Grok Build CLI, grok.com cookie, and xAI OAuth share the weekly pool). Combo routing then tries the next login instead of locking the model for every account in the pool ([#13061](https://github.com/diegosouzapw/OmniRoute/pull/13061)) — thanks @HouMinXi +- fix(compression): spawn the LLMLingua worker with a file URL object so compression actually runs instead of silently failing open on Node ([#13093](https://github.com/diegosouzapw/OmniRoute/pull/13093)) — thanks @anhtahaylove +- **fix(test):** run the local `test` and `test:unit` scripts at concurrency 4 so a full-suite run no longer exhausts the machine's commit charge and kills unrelated processes ([#13187](https://github.com/diegosouzapw/OmniRoute/pull/13187)) — thanks @anhtahaylove +- **fix(monitoring):** `GET /api/monitoring/health` `credentialHealth` now includes a bounded `failedConnections` list (`connectionId`, `status`, sanitized `lastError`) when the probe-cache gauge `failed>0`, plus `source: probe-cache` and a cheap `staleDbNonOkCount` for sticky SQLite `test_status` on active rows. Documents that the live gauge is not `provider_connections.test_status`. ([#12876](https://github.com/diegosouzapw/OmniRoute/pull/12876)) — thanks @RaviTharuma +- **fix(resilience):** an openai-compatible multi-upstream gateway no longer marks the whole connection `credits_exhausted` when a single upstream (or the `/models` / representative-model health probe) returns 402. Quota failures stay model-scoped; true connection-wide auth failures are unchanged. Single-credential 402 key disable ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239)) is preserved. ([#12875](https://github.com/diegosouzapw/OmniRoute/pull/12875)) — thanks @RaviTharuma +- **fix(dashboard):** nine fixes on the Orchestration Canvas, all diagnosed in the Phase 2 reviews. A source that fails now keeps the timestamp of its FIRST failure instead of being re-stamped every poll — the stale line said "since the last poll" no matter how long the source had been down, and the churn also defeated the snapshot's stable identity (it serializes the source list), so the canvas re-rendered on every tick while anything was broken. A source that HAD data and only then started failing is flagged too: previously only a source with no node at all got the warning, so a source that went down mid-session kept a healthy-looking node forever. The rest are pointwise: clicking a filter chip cancels the pending search debounce (left armed, it fired ~300ms later and silently reverted the chip); the search input carries an accessible name; `?state=running, failed` parses like the unpadded form instead of dropping the padded value; the CSV toggle helper is defined once in `model/urlParams.ts` rather than twice; the agents tab tells "nothing running" apart from "the filter matched nothing", offering a clear-filters button instead of setup links that would be wrong advice there; edges stop emitting SMIL particles above 40 simultaneously active edges, keeping the colored stroke; and the drawer's error banner clears when a retried action succeeds. ([#12983](https://github.com/diegosouzapw/OmniRoute/pull/12983)) +- **fix(i18n):** the nine locales added with the EU-language batch (Greek, Estonian, Irish, Croatian, Lithuanian, Latvian, Maltese, Slovenian, Serbian) were missing the eleven Orchestration Canvas keys that Phase 3 introduced, so the compare-runs panel and the "no runs match these filters" empty state fell back to English in those languages (`deepMergeFallback` substitutes English for an absent key, so nothing rendered blank — it rendered untranslated). The coverage gate does not catch this: it enforces an 80% floor per locale, and eleven missing keys out of ~13,000 leaves coverage at 99.9%. Translated for real in each language, calibrated against the wording each file already uses for "run", "filter" and "skill". ([#13294](https://github.com/diegosouzapw/OmniRoute/pull/13294)) +- **fix(dashboard):** five follow-ups from the Phase 2 Orchestration Canvas review. The History drawer now shows the memory section for runs that already left the live TTL window: the persisted `memory_hits` event is parsed into `metadata.memoryHits` with the same defensive validation the drawer applies, and — because that event is observability rather than a state transition — it no longer leaks into the timeline, where it had been inheriting the task's state and rendering as a duplicate transition. The A2A memory recall runs against its own 1.5s deadline instead of inheriting the memory backend's 30s one; overshooting degrades exactly like any other recall failure (no hits, task proceeds), and the timer is cleared on both paths. Repeating a Conductor run carries its `requirements.cli`/`requirements.model` forward, so the new run is pinned to the same runner profile and model rather than drifting to whatever the fleet picks. A successful repeat from the Agents tab now focuses the run it created, instead of leaving the operator on the finished one. And the auth test for `POST /api/conductor/tasks` moved into the shared `ROUTES` array rather than restating the pattern. ([#12988](https://github.com/diegosouzapw/OmniRoute/pull/12988)) +- fix(plugins): stop leaking an exit listener per plugin hook timeout, which triggered MaxListenersExceededWarning on plugins that ignore SIGTERM ([#13092](https://github.com/diegosouzapw/OmniRoute/pull/13092)) — thanks @anhtahaylove +- **fix(validation):** Provider node edits no longer fail with a generic "Invalid request" when the optional daily-quota reset fields are left blank. The dashboard sends `dailyQuotaResetTimezone` and `dailyQuotaResetHour` as `null`, and only the hour accepted it. ([#13066](https://github.com/diegosouzapw/OmniRoute/issues/13066)) ([#13083](https://github.com/diegosouzapw/OmniRoute/pull/13083)) — thanks @ntdatt812 +- **fix(combo):** quota-aware expansion drops banned, inactive, missing, and wrong-provider connections before quota fetch or model dispatch; pins and allowlists stay selectors, not a bypass. Antigravity automatic exhaustion now requires a reported zero remaining, so a positive balance below 1% stays eligible. ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- Fix an intermittent failure in the quota-weighted routing test suite: scores are a function of `Date.now()`, so peers with identical quota scored microseconds apart never tied and swapped order. ([#12946](https://github.com/diegosouzapw/OmniRoute/pull/12946)) +- fix(volcengine): resolve quota window mapping gaps, safe multi-connection console binding, and response cookie sanitization ([#12950](https://github.com/diegosouzapw/OmniRoute/pull/12950)) — thanks @HouMinXi +- **fix(test):** resolve the WebDAV handler path with `fileURLToPath` so the suite's 37 WebDAV tests run on Windows instead of failing with a doubled `C:\C:\` drive prefix ([#13196](https://github.com/diegosouzapw/OmniRoute/pull/13196)) — thanks @anhtahaylove +- **fix(ui):** fall back from invalid AnySearch icon alias (direct commit `9587e07b69`) +- **fix(sse):** stop rebuilding a truncated summary from the collector's cap-dropped event array ([#12718](https://github.com/diegosouzapw/OmniRoute/pull/12718)) — thanks @hartmark +- **fix(conversations):** show a pending spinner for unresolved tool nodes instead of "(empty)" ([#12727](https://github.com/diegosouzapw/OmniRoute/pull/12727)) — thanks @hartmark +- **fix(streaming):** fail fast when an upstream stream produces only lifecycle/heartbeat events, never real content ([#12741](https://github.com/diegosouzapw/OmniRoute/pull/12741)) — thanks @hartmark +- **fix(responses-continuation):** bridge the write-in-flight window with an in-memory pending store ([#12854](https://github.com/diegosouzapw/OmniRoute/pull/12854)) — thanks @hartmark +- **fix(translator):** drop or map agent_message on Chat Completions fallback ([#12880](https://github.com/diegosouzapw/OmniRoute/pull/12880)) — thanks @RaviTharuma +- **fix(api):** page and stream-complete GET /v1/models for large catalogs ([#12882](https://github.com/diegosouzapw/OmniRoute/pull/12882)) — thanks @RaviTharuma +- **fix(quota):** align AUTH window labels with usage API and clarify 503 ([#12884](https://github.com/diegosouzapw/OmniRoute/pull/12884)) — thanks @RaviTharuma +- **fix(ci):** clear two base-reds on release/v3.8.51 — mutation-coverage gate + image-only-model guard ([#12945](https://github.com/diegosouzapw/OmniRoute/pull/12945)) +- **fix(api):** scope batch bulk-delete to the calling API key ([#13211](https://github.com/diegosouzapw/OmniRoute/pull/13211)) +- **fix(tests):** align model-sync log assertion to shipped path redaction (base-red) ([#13227](https://github.com/diegosouzapw/OmniRoute/pull/13227)) +- **fix(test):** make the ToS heading guard actually require the parentheses ([#13228](https://github.com/diegosouzapw/OmniRoute/pull/13228)) +- **fix(quality):** register 4 drifted covering tests in stryker tap.testFiles ([#13229](https://github.com/diegosouzapw/OmniRoute/pull/13229)) +- **fix(logging):** mask *-api-key headers in request-log pipeline (#13273) ([#13401](https://github.com/diegosouzapw/OmniRoute/pull/13401)) — thanks @KooshaPari +- **fix(tests):** correct stale Jina catalog prefix in custom-models test ([#13403](https://github.com/diegosouzapw/OmniRoute/pull/13403)) — thanks @KooshaPari +- **fix(backend):** prune old backups after health-check-repair snapshots (#13308) ([#13404](https://github.com/diegosouzapw/OmniRoute/pull/13404)) — thanks @KooshaPari +- **fix(backend):** guard memory_fts_au trigger with WHEN clause to stop FTS bloat (#13326) ([#13405](https://github.com/diegosouzapw/OmniRoute/pull/13405)) — thanks @KooshaPari +- **fix(free-tiers):** remove obsolete Together $25 signup credit (#12526) ([#13407](https://github.com/diegosouzapw/OmniRoute/pull/13407)) — thanks @KooshaPari +- **fix:** skip non-chat generation models in Test all models ([#13409](https://github.com/diegosouzapw/OmniRoute/pull/13409)) — thanks @KooshaPari +- **fix(providers):** de-duplicate agy and antigravity model catalogs ([#13410](https://github.com/diegosouzapw/OmniRoute/pull/13410)) — thanks @KooshaPari +- **fix(dashboard):** send provider in body for mini-playground web search ([#13411](https://github.com/diegosouzapw/OmniRoute/pull/13411)) — thanks @KooshaPari +- **fix(resilience):** add CJK quota-exhaustion patterns to 429 classifier ([#13412](https://github.com/diegosouzapw/OmniRoute/pull/13412)) — thanks @KooshaPari +- **fix(backend):** eval runner: failed calls no longer score as passed + dotAll regex ([#13413](https://github.com/diegosouzapw/OmniRoute/pull/13413)) — thanks @KooshaPari +- **fix(backend):** eval runner opts out of output-style and memory injection ([#13414](https://github.com/diegosouzapw/OmniRoute/pull/13414)) — thanks @KooshaPari +- **fix:** chain serial quarantine step in npm test script (fixes #13286) ([#13418](https://github.com/diegosouzapw/OmniRoute/pull/13418)) — thanks @KooshaPari +- **fix(antigravity):** derive stable session ID from credentials for prompt-cache reuse (#13305) ([#13424](https://github.com/diegosouzapw/OmniRoute/pull/13424)) — thanks @KooshaPari +- **fix(memory):** merge memory into top-level system field instead of unshifting at messages[0] (#13425) ([#13427](https://github.com/diegosouzapw/OmniRoute/pull/13427)) — thanks @KooshaPari +- **fix(compression):** preserve instruction blocks from lossy engines ([#13523](https://github.com/diegosouzapw/OmniRoute/pull/13523)) — thanks @KooshaPari +- **fix(compression):** preserve polarity words and newlines in ultra heuristic ([#13524](https://github.com/diegosouzapw/OmniRoute/pull/13524)) — thanks @KooshaPari +- **fix(compression):** run user preservePatterns before built-in patterns (#13457) ([#13525](https://github.com/diegosouzapw/OmniRoute/pull/13525)) — thanks @KooshaPari +- **fix(reasoning):** expand deepseek-v4 regex to match v4.1+ and add command-code provider (#13430) ([#13528](https://github.com/diegosouzapw/OmniRoute/pull/13528)) — thanks @KooshaPari +- **fix(providers):** add alwaysReasons metadata for reka-flash-3 ([#13531](https://github.com/diegosouzapw/OmniRoute/pull/13531)) — thanks @KooshaPari +- **fix(playground):** allow Compare columns to scroll independently ([#13532](https://github.com/diegosouzapw/OmniRoute/pull/13532)) — thanks @KooshaPari +- **fix(security):** empty whitelist allows through to prevent admin lockout ([#13534](https://github.com/diegosouzapw/OmniRoute/pull/13534)) — thanks @KooshaPari +- **fix(tests):** add DISABLE_IOREG_STRATEGY env var for macOS test compatibility (fixes #13467) ([#13539](https://github.com/diegosouzapw/OmniRoute/pull/13539)) — thanks @KooshaPari +- **fix(zed-hosted):** map developer role and Gemini enums for Zed proxy ([#13543](https://github.com/diegosouzapw/OmniRoute/pull/13543)) — thanks @KooshaPari +- **fix(analytics):** normalize provider aliases to canonical id in usage_history ([#13545](https://github.com/diegosouzapw/OmniRoute/pull/13545)) — thanks @KooshaPari +- **fix(combo):** use traceId for call log id to prevent UNIQUE constraint failure ([#13546](https://github.com/diegosouzapw/OmniRoute/pull/13546)) — thanks @KooshaPari +- **fix(auth):** check model lockout before returning synthetic noauth connection ([#13547](https://github.com/diegosouzapw/OmniRoute/pull/13547)) — thanks @KooshaPari +- **fix(guardrails):** skip credential redaction for base64 image data URLs ([#13550](https://github.com/diegosouzapw/OmniRoute/pull/13550)) — thanks @KooshaPari +- **fix(resilience):** increase requestQueue.maxWaitMs default from 15s to 30s ([#13553](https://github.com/diegosouzapw/OmniRoute/pull/13553)) — thanks @KooshaPari +- **fix(docs):** document OMNIROUTE_READY_TIMEOUT_MS and allowlist the test-only DISABLE_IOREG_STRATEGY ([#13692](https://github.com/diegosouzapw/OmniRoute/pull/13692)) + ### 📝 Maintenance ---- +- **docs(readme):** reconcile live v3.8.50 provider, free-tier, CLI, routing, test, community, sponsor, acknowledgment, and SVG metrics with their audited source denominators, including a deduplicated OmniRoute-in-Action snapshot and distinct contributor rankings for merged pull requests, GitHub-attributed commits, and Git history ([#11356](https://github.com/diegosouzapw/OmniRoute/pull/11356)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **docs:** sync the canonical provider count 354 → 356 across `README.md`, `AGENTS.md`, `llm.txt` (+ 42 i18n mirrors), the four README SVG diagrams, `docs/reference/PROVIDER_REFERENCE.md` (regenerated) and the `package.json` description after Opper (#11629) and 1min.ai (#11631) boarded the catalog — closes the `check:docs-counts-sync` strict drifts that kept `release/v3.8.51` red ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11673](https://github.com/diegosouzapw/OmniRoute/pull/11673)) — thanks @jonlwheat2-gif +- **test(providers):** update count-derived assertions after the v3.8.51 provider additions — `APIKEY_PROVIDERS` 233 → 235 (Opper #11629 + 1min.ai #11631), reserved-prefix REGISTRY walk 395 → 398, `WEB_FETCH_PROVIDERS` now includes `nimble-search` (#11620), and the provider translate-path golden snapshot regenerated ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11674](https://github.com/diegosouzapw/OmniRoute/pull/11674)) — thanks @jonlwheat2-gif +- **test(sse):** bump the hard-lease connection-query inventory for `src/lib/tokenHealthCheck.ts` to 2 — the verify-only web-cookie sweep added by #11495 split the single `getProviderConnections` call into oauth + cookie variants, which the frozen inventory had not tracked ([#11495](https://github.com/diegosouzapw/OmniRoute/pull/11495)). ([#11677](https://github.com/diegosouzapw/OmniRoute/pull/11677)) — thanks @jonlwheat2-gif / @oyi77 +- **chore(release):** clear the `release/v3.8.51` base-red gates — docs migration count (+38 i18n mirrors), stryker `tap.testFiles`, lockfile registry host, stale ESLint suppressions, the 7 lint regressions they were hiding, the stale `APIKEY_PROVIDERS` partition count (231 → 233), the `glm-5.3-max` tier missing from the GLM test table, a dead `modelType === "chat"` comparison in the models catalog, and the `resolveLiveWsUrl` / `sanitizeLiveWsPort` helpers from #11388 that a later merge dropped from `wsPath.ts` (dashboard live WebSocket URL resolution) ([#11502](https://github.com/diegosouzapw/OmniRoute/pull/11502)) — thanks @MumuTW +- **chore(free-models):** the free-tier regime table now declares which totals figure each regime feeds and whether it may take the no-auth shortcut, so the sets that used to repeat those answers by hand are derived from it — a new `freeType` no longer compiles until it has answered every question, instead of silently contributing to no total ([#11537](https://github.com/diegosouzapw/OmniRoute/pull/11537)) — thanks @maxmad64bis +- **docs(routing):** LKGP strategy copy now states provider-level stickiness and the explicit rules fallback instead of promising per-target pinning ([#11555](https://github.com/diegosouzapw/OmniRoute/pull/11555)) — thanks @maxmad64bis +- **test(chatcore):** pin the response tool-name alias resolution against the ordering hazard that broke every Gemini/Antigravity MCP tool call in v3.8.49. `extractRequestToolIdentityMap` deletes `translatedBody._toolNameMap`, so the later read is always undefined and the ledger survives only through the `requestToolIdentityMap` fallback — removing that fallback previously left the entire tool-name suite green. The resolution moves into `resolveResponseToolNameMap()` next to the map it depends on, with a regression guard that fails without the recovery. No behaviour change. ([#11575](https://github.com/diegosouzapw/OmniRoute/pull/11575)) — thanks @amartinawi +- **docs(env):** document `OMNIROUTE_CORPUS_CACHE_SIZE` in `.env.example` and `ENVIRONMENT.md`, so the env/docs contract check stops failing Docs Gates on every branch ([#11576](https://github.com/diegosouzapw/OmniRoute/pull/11576)) — thanks @ntdatt812 +- **chore(stryker):** register `route-guard-tunnel-processes-local-only` and `universal-quota-aware-routing` in `tap.testFiles`, so their mutant kills count for the three modules they cover ([#11577](https://github.com/diegosouzapw/OmniRoute/pull/11577)) — thanks @ntdatt812 +- **test(authz):** pin `LOCAL_ONLY_API_GET_EXEMPTIONS` by exact membership instead of by entry count, so the guard names the offending path and also catches a substitution ([#11580](https://github.com/diegosouzapw/OmniRoute/pull/11580)) — thanks @ntdatt812 +- **test(build):** pin `resolveNextBuildBundlerFlag` to the `OMNIROUTE_USE_TURBOPACK` contract it implements, across runtimes, instead of asserting an unimplemented Bun override ([#11583](https://github.com/diegosouzapw/OmniRoute/pull/11583)) — thanks @ntdatt812 +- **test(claude-web):** the Claude Web transport test's barrel anchors no longer pin the exact construction spelling. #11421 made the executor map lazy, so `/"claude-web": new ClaudeWebExecutor\(\)/` stopped matching and the test went red on `release/v3.8.51` itself — a base-red gate every open PR inherited. The anchors now match the alias key and the class name and tolerate either spelling in between, and were checked to still fail when an alias is genuinely repointed at another executor ([#11585](https://github.com/diegosouzapw/OmniRoute/pull/11585)) — thanks @ntdat812 +- **test(models):** pin `qwen3.8-max` resolution per provider — two of the four catalogs now serve the bare id and no longer carry `-preview` ([#11590](https://github.com/diegosouzapw/OmniRoute/pull/11590)) — thanks @ntdatt812 +- **test(executors):** drop the stale 128-tool truncation assertion that contradicted the #11444 pass-through contract ([#11591](https://github.com/diegosouzapw/OmniRoute/pull/11591)) — thanks @ntdatt812 +- **test(token-health):** pin the current terminal-skip boundary — expired with retry budget is probed, `account_deactivated` stays skipped ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)) — thanks @ntdatt812 +- **test(models):** follow #11503's retarget of the `gemini-3-pro-high` alias to the hyphenated catalog id ([#11593](https://github.com/diegosouzapw/OmniRoute/pull/11593)) — thanks @ntdatt812 +- **refactor(video bridge):** extract per-video acquisition, whole-result caching, description, and metrics/abort/cleanup out of `VideoBridgeGuardrail.preCall` into a `processVideoPart` seam in a new `videoBridgePipeline.ts`, behind explicit `VideoMediaBrokerPort`, `VideoAudioTranscriptionPort`, and `VideoDrilldownPort` boundaries; `preCall` now only handles request traversal, policy, and response aggregation. The Video tab's FFmpeg/ffprobe runtime status is now an explicit `unknown` / `restricted` / `unavailable` / `available` state instead of a nullable boolean pair, fixing a case where an in-flight or failed probe was mislabeled as "install FFmpeg" ([#11657](https://github.com/diegosouzapw/OmniRoute/issues/11657)). ([#12016](https://github.com/diegosouzapw/OmniRoute/pull/12016)) +- **test(opencode-plugin):** add unit test coverage for `formatFreeBudget()` naming helper ([#11660](https://github.com/diegosouzapw/OmniRoute/pull/11660)) — thanks @f9td56dbgh-hub +- **docs(video):** clarify that the Video Bridge transcript `source` field (`client`, `embedded`, `audio-bridge`) is presently caller-declared and not yet server-verified — OmniRoute enforces the enum shape but does not cryptographically confirm that an `embedded`/`audio-bridge` label came from a server-owned extraction ([#11661](https://github.com/diegosouzapw/OmniRoute/issues/11661)). ([#12001](https://github.com/diegosouzapw/OmniRoute/pull/12001)) +- **chore(providers):** retire the distributed Raycast Relay and Hailuo Web integrations after a provenance audit confirmed substantial derivation from GPL-3.0 sources; the official GitHub and MiniMax API providers remain available. Migration 163 disables legacy connections and invalidates their active leases while preserving connection IDs, audit/history rows, quota snapshots, usage records, and fail-closed API-key connection policies. ([#11691](https://github.com/diegosouzapw/OmniRoute/pull/11691)) +- **chore(providers):** retire the Felo Web provider while its GPL-derived provenance remains on licensing HOLD ([#11698](https://github.com/diegosouzapw/OmniRoute/pull/11698)) — stale runtime and database references now fail closed while existing connection, lease, and usage history remains preserved for auditability. +- **fix(sse):** retire the Gemini Web image-generation bridge while provenance and licensing remain on HOLD, preserving Gemini Web chat/auth/tools and legitimate Gemini/Nano Banana image providers ([#11708](https://github.com/diegosouzapw/OmniRoute/pull/11708)) +- **chore(audio):** retire the built-in EdgeTTS provider and its 11-voice catalog while provenance/licensing review remains on HOLD; the generic `/v1/audio/speech` endpoint and the other speech providers remain available ([#11711](https://github.com/diegosouzapw/OmniRoute/pull/11711)) +- **fix(providers):** retire the unofficial Qwen Web integration and its `qwen-web`/`qw` identities while third-party provenance remains on HOLD ([#11713](https://github.com/diegosouzapw/OmniRoute/pull/11713)) — direct requests now fail closed with a sanitized HTTP 410 and combos skip the retired target, while migration guards preserve connection IDs, allowlists, usage/audit history and invalidate active leases. Supported Qwen API, Qwen Code, cloud, token-plan, audio, local and model-name surfaces remain available; restoring Qwen Web requires a qualified provenance/license decision. +- **fix(providers):** retire `microsoft-designer-web` and its `msdesigner` alias from runtime and catalogs while preserving stored credentials, provider history and allowlists as inactive records — source dependency on gpt4free's [`MicrosoftDesigner.py`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/g4f/Provider/needs_auth/MicrosoftDesigner.py) is proven at snapshot `0943d16eef757a44deb60651c07bbe5fa958cafe`, whose [`LICENSE`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/LICENSE) is GPL-3.0; expressive adaptation is probable, which records provenance/licensing risk without asserting a legal conclusion ([#11720](https://github.com/diegosouzapw/OmniRoute/pull/11720)). +- **docs(legal):** centralize pinned MIT notices for the vendored GCF codec and local flag assets ([#11726](https://github.com/diegosouzapw/OmniRoute/pull/11726)) +- Split the 2,952-line `adobeFireflyClient.ts` into nine focused modules (catalog, credentials, payload, ARP session, upload, responses, IMS, account, poll) behind an unchanged barrel — no consumer edits and no public API change (#11728). — thanks @MumuTW +- **fix(dashboard):** replaced the Model Lockout card's unprovenanced notification MP3 with a short, locally synthesized Web Audio chime ([#11731](https://github.com/diegosouzapw/OmniRoute/pull/11731)) — both toggles retain optional audible feedback without shipping replacement media or third-party code, and unsupported, suspended, or rejected audio contexts fail open without blocking the setting change. +- **chore(deepseek-web):** remove the provenance-unresolved DeepSeek PoW WASM binary and its runtime loader/tracing while retaining the existing JavaScript solver (slower at high difficulty) ([#11732](https://github.com/diegosouzapw/OmniRoute/pull/11732)). +- **chore(assets):** remove four unused provider assets whose redistribution provenance could not be verified while retaining the active Freebuff SVG ([#11735](https://github.com/diegosouzapw/OmniRoute/pull/11735)) +- **docs(assets):** retired two raster images whose redistribution provenance could not be verified ([#11736](https://github.com/diegosouzapw/OmniRoute/pull/11736)) — PicoClaw documentation now reuses the existing SVG, while Zoo Code remains a text link without an image. +- **docs(legal):** add pinned notices for six LobeHub-derived provider SVGs and 65 byte-exact theSVG assets, retaining registry claims as unverified metadata with explicit trademark and HOLD boundaries ([#11737](https://github.com/diegosouzapw/OmniRoute/pull/11737)). +- **chore(quality):** added a versioned 225/225 provider-asset provenance manifest and blocking drift gate ([#11743](https://github.com/diegosouzapw/OmniRoute/pull/11743)) — the inventory pins magic MIME and SHA-256 for every physical file, records immutable source matches for 71 assets, keeps 69 probable and 85 unresolved entries explicit but non-blocking, and treats provenance as source evidence rather than copyright or trademark clearance. +- **refactor(mitm):** replace four previously derived Traffic Inspector utilities — conversation normalization, SSE merging, secret masking, and header sanitization — with independent clean-room implementations based on public provider schemas, WHATWG SSE, and RFC 6750, while keeping the two independently classified passthrough surfaces unchanged ([#11748](https://github.com/diegosouzapw/OmniRoute/pull/11748)). +- Added a unit test that fails seven days before any dated pack under `config/` (`validUntil` and sibling keys) lapses, naming the file and key. The Alibaba free-tier pack expired on 2026-08-27 and turned every PR red the next morning with no commit involved; renewal now happens on someone's terms, not the clock's. ([#11891](https://github.com/diegosouzapw/OmniRoute/pull/11891)) +- `check:workflows` now fails (under `--strict`/`--ratchet`) when any job routed to a self-hosted runner publishes with `--provenance` — npm rejects that with `422` at the registry, which in v3.8.50 only surfaced after the tag and Docker images were public. ([#11895](https://github.com/diegosouzapw/OmniRoute/pull/11895)) +- `scripts/ops/runner-janitor.sh` now proves a path is idle with one `lsof` snapshot and removes stale leftovers itself (tmpfs after 3 h — it is RAM — disk after 24 h), kills orphan `next-build` processes, prunes checkouts of stopped runners, and alerts on memory pressure; `--dry-run` shows exactly what it would do. `docs/ops/RUNNER_BOX.md` reconciled to the measured box (31 GB, 10 listeners). ([#11893](https://github.com/diegosouzapw/OmniRoute/pull/11893)) +- The `next-build` artefact (1.3 GB) is now written and read under `$RUNNER_TEMP` (per-runner, on disk) instead of `/tmp`, which on the self-hosted pool is a 12 GB tmpfs in RAM. Landing it there took 27–32 of the publish job's 76 minutes, and the fixed `/tmp/e2e-build.tar.gz` name let E2E jobs on different runners overwrite each other's download. ([#11896](https://github.com/diegosouzapw/OmniRoute/pull/11896)) +- The CI `build` job now runs in two concurrency lanes — `main` and pull requests — so a release build is never queued behind (or OOM-killed beside) PR builds on the self-hosted pool, which holds one `next-build` comfortably and two at the edge. ([#11901](https://github.com/diegosouzapw/OmniRoute/pull/11901)) +- Re-freeze the ESLint suppressions on `release/v3.8.51` from a clean-room measurement (2 stale file entries pruned, 55 pre-existing `no-explicit-any` in six new files frozen under #11924) and drop the dead `GPT_SIZE_MAP` constant orphaned by the Adobe Firefly client split, so `No new ESLint warnings` stops failing every PR with exit 2 (Refs #11924) ([#11955](https://github.com/diegosouzapw/OmniRoute/pull/11955)) +- Type the 55 `no-explicit-any` sites that had been frozen under #11924 — four redundant casts in `socksConnectorWithFamily.ts` (undici/socks types already accept them) and the mocks/fixtures of the socks-timeout and isFree suites — and drop their suppression entries; the ESLint ratchet shrinks from 5487 to 5432 (Closes #11924) ([#11975](https://github.com/diegosouzapw/OmniRoute/pull/11975)) +- Take the two hosted-runner builds off the PR rail: `Build App` (`build.yml`) is `workflow_dispatch`-only and `DAST smoke (PR)` runs only for PRs into `main` — the 7 GB hosted VM cannot build `release/v3.8.51` in any profile (19/30 red, VM shutdown ~8 min into `next build`) and both checks had turned into permanent noise on every release PR; the bundle stays validated by `ci.yml` on `main` and by `nightly-release-green` on `release/**` (Closes #11946) ([#11962](https://github.com/diegosouzapw/OmniRoute/pull/11962)) +- Move the four nightly jobs that build the backend (`nightly-schemathesis`, `nightly-llm-security` promptfoo + garak, `nightly-resilience` axe-a11y) off the hosted 7 GB runner — where they died on `release/v3.8.51` unseen — onto the box's new `omni-light` pool (two listeners), and document the reshaped fleet (4 active OmniRoute listeners: 2 `omni-build` + 2 `omni-light`, janitor ceiling 4) (Closes #11965) ([#11967](https://github.com/diegosouzapw/OmniRoute/pull/11967)) +- Stop painting every fork PR into `release/**` red: `quality.yml` `Build (advisory)` is skipped (GitHub still reports `continue-on-error` failures as check FAILURE). Hosted `ubuntu-latest` cannot finish `npm run build` on this tree — same class as #11962 taking `build.yml` off the PR rail. `docker-publish.yml` amd64 now runs on the `.113` `omni-build` pool (31 GB / 32 cores, two listeners) with Turbopack, shares the `heavy-build-main` lane with `ci.yml` `Build` so it queues instead of becoming a third heavy, and keeps per-ref concurrency (a merge storm was starting 8 concurrent OOM builds). arm64 stays on `ubuntu-24.04-arm` with webpack — there is no ARM box. Fallback when `USE_VPS_RUNNER` is off: hosted amd64 + webpack (#11976). ([#12021](https://github.com/diegosouzapw/OmniRoute/pull/12021)) +- `docker-publish.yml` builds with webpack (`OMNIROUTE_USE_TURBOPACK=0`) on **both** arches, including amd64 on the `.113` `omni-build` pool. Turbopack had the RAM (31 GB, `omniroute-113-6`) and still panicked (`TurbopackInternalError: there must be a path to a root`, run 33253576569, 26 min in). The same tree's arm64 webpack build on hosted `ubuntu-24.04-arm` succeeded (run 33264823398). Dockerfile already documents webpack as the Docker escape hatch (#11976). ([#12050](https://github.com/diegosouzapw/OmniRoute/pull/12050)) +- **docs(free-tier):** declare the counting vs deciding regimes for "is it free?" and guard the deciding path from DB-backed catalog resolution ([#12226](https://github.com/diegosouzapw/OmniRoute/pull/12226)) — thanks @maxmad64bis +- **docs(auto-combo):** The mode pack table in `docs/routing/AUTO-COMBO.md` now lists all six shipped packs with every weight each one sets, replacing a four-pack table whose numbers had also drifted from the source. It states plainly that no pack sets `quality`, so selecting any pack silences the observed-quality signal. Six more documents that quote the scoring factor count joined the `check:docs-counts` gate, which caught five stale claims — including one naming nine factors that do not exist — and two stale mode pack counts. The dashboard routing panel, which offered four of the six packs and labelled the default strategy "6-Factor Scoring", is now covered by a test; the two packs it was missing are `reliability-first` and `chaos-mode`, the latter labelled as the fault-injection profile it is rather than as one more routing preference ([#12316](https://github.com/diegosouzapw/OmniRoute/pull/12316)) — thanks @maxmad64bis +- **chore(quality):** rebaseline `src/lib/db/apiKeys.ts` for the ACL the key-creation path now preserves ([#12352](https://github.com/diegosouzapw/OmniRoute/pull/12352)) ([#12673](https://github.com/diegosouzapw/OmniRoute/pull/12673)) — thanks @KrzysiekSko +- **chore(providers):** bump the Claude Code wire identity and the Devin bridge image pin from `2.1.220` to `2.1.258` ([#12402](https://github.com/diegosouzapw/OmniRoute/pull/12402)) — thanks @ggiak ([#12604](https://github.com/diegosouzapw/OmniRoute/pull/12604)) +- **docs(env):** align `.env.example`, the README Bun section, and the troubleshooting guide with the code: `OMNIROUTE_USE_TURBOPACK` also governs `npm run build` (not dev-only), `bun run build` follows that flag instead of auto-selecting Webpack, `OMNIROUTE_CHAT_MAX_HEAVY_IN_FLIGHT` is unset by default (no request-count cap), and the structural `503 chat_admission_busy` message matches `chatAdmissionResponses.ts` (#12404 — thanks @pacocartones) +- **chore(electron):** upgrade the desktop app to Electron 44 (Chromium 152, Node 24.18.1) ([#12217](https://github.com/diegosouzapw/OmniRoute/pull/12217)). **Requires macOS 13 (Ventura) or later** — Chromium dropped macOS 12 (Monterey), so Monterey users must stay on an earlier OmniRoute desktop build. Windows and Linux are unaffected; the app already shipped only x64/arm64, so Electron 44 dropping 32-bit builds changes nothing. Removes the `openAsHidden`/`wasOpenedAsHidden` login-item fields deleted in Electron 44 — hidden autostart continues to work through the `--hidden` argument registered with the login item ([#12554](https://github.com/diegosouzapw/OmniRoute/pull/12554)) — thanks @app/dependabot +- **chore(quality):** rebaseline `src/sse/handlers/chat.ts` for the effective-input persistence the continuation fix needs ([#12641](https://github.com/diegosouzapw/OmniRoute/pull/12641)) ([#12680](https://github.com/diegosouzapw/OmniRoute/pull/12680)) — thanks @hartmark +- `Coverage` job on `ci.yml`: the informational Codecov upload gets its own 5-minute ceiling and `continue-on-error`, and the job budget grows from 20 to 30 minutes (the 8-shard c8 merge alone takes ~10) — a stalled upload no longer ends the job `cancelled` and drags a fully green `main` run's conclusion down with it ([#11972](https://github.com/diegosouzapw/OmniRoute/pull/11972)) +- CI hardening on the PR rail: the four `Unit Tests fast-path` shards get `timeout-minutes: 30` (a hung shard held a PR for 64 min instead of GitHub's 6 h default) and both ESLint file caches lose their `restore-keys` fallback, so a cache built under another suppressions file or lint config can no longer report stale verdicts (Refs #11600, #11924) ([#11963](https://github.com/diegosouzapw/OmniRoute/pull/11963)) +- Every CI job that runs a `next build` (`build`, the npm `publish`, both release-green validations) now targets the `omni-build` runner label, which only two of the eight self-hosted runners carry. The box holds one build comfortably and two at the edge; a third now queues on GitHub instead of being OOM-killed by the kernel. ([#11932](https://github.com/diegosouzapw/OmniRoute/pull/11932)) +- **refactor(combo):** move `handleRoundRobinCombo` (and `resolveTargetTokenLimit`) into `open-sse/services/combo/roundRobinCombo.ts`. `combo.ts` drops from 2164 to 1014 lines (`split("\n").length`); the leaf is 1199 (under the 1200 new-file cap). The round-robin call site uses a dynamic `import()` so `releaseStickyPinOnFailure` / `clearStaleLKGP` can stay exported from `combo.ts` without a static cycle. Skip / sticky / semaphore / safety-timer behavior is unchanged. ([#12811](https://github.com/diegosouzapw/OmniRoute/pull/12811)) — thanks @HouMinXi +- **chore(quality):** rebaseline the file-size caps the error-boundary campaign grew past (`open-sse/executors/codex.ts`, `open-sse/vendor/codex-chatgpt-web/bridge.ts`, both via [#12444](https://github.com/diegosouzapw/OmniRoute/pull/12444)) ([#12654](https://github.com/diegosouzapw/OmniRoute/pull/12654)) +- **chore(quality):** rebaseline the file-size caps the hartmark batch grew past (`combos/page.tsx` via [#12355](https://github.com/diegosouzapw/OmniRoute/pull/12355), `open-sse/services/combo.ts` via [#12338](https://github.com/diegosouzapw/OmniRoute/pull/12338)) ([#12623](https://github.com/diegosouzapw/OmniRoute/pull/12623)) — thanks @hartmark +- **chore(quality):** rebaseline the file-size caps the HouMinXi batch grew past when its PRs stacked (`providers/page.tsx`, `chatCore.ts`, `accountFallback.ts`) — each PR measured correctly in isolation, none saw the stacking ([#12619](https://github.com/diegosouzapw/OmniRoute/pull/12619)) +- **chore(quality):** rebaseline `open-sse/services/combo.ts` for the reset-aware scoring the HouMinXi batch stacked ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) ([#12678](https://github.com/diegosouzapw/OmniRoute/pull/12678)) — thanks @HouMinXi +- **docs:** sync provider catalog count (direct commit `4f11b2ae3d`) +- **docs(skills):** sync omni-inference search fallback (direct commit `b4e76a7ed9`) +- **chore(lint):** prune two stale base suppressions (direct commit `1325047d56`) +- **test(auto-combo):** tolerate full-suite rotation load (direct commit `ad3e293f9f`) +- **test:** drain inherited quality snapshot reds (direct commit `21ed68d8ac`) +- **chore(quality):** rebaseline measured CLI bundle growth (direct commit `d35b3f9779`) +- **test(ui):** reproduce invalid AnySearch icon alias (direct commit `12cc6ca834`) +- **test(translator):** fix the relative imports of the relocated deferred-finish test (direct commit `777d9d1629`) +- **docs(counts):** bring the documented provider and migration counts to the measured values (direct commit `e70bea30e9`) +- **test(security):** drop the guard-target set left unused by the sync (direct commit `a32ba0e362`) +- **chore(db):** renumber model_capabilities migration 163 → 169 (cross-branch collision with 163_radar_feed_cache_generated_at) (direct commit `70f5d4cbf9`) +- **docs(cli):** document CLI_PRIME_AGENT_BIN and correct the CLI Agents count (direct commit `9058e39b61`) +- **chore(deps):** refresh runtimes and adopt ESLint 10 ([#11259](https://github.com/diegosouzapw/OmniRoute/pull/11259)) — thanks @backryun +- **refactor(local-corpus):** implement dynamic root resolution and LRU cache ([#11491](https://github.com/diegosouzapw/OmniRoute/pull/11491)) — thanks @raheemuddin786 +- **chore(quality):** prune stale eslint-suppressions entries ([#11596](https://github.com/diegosouzapw/OmniRoute/pull/11596)) +- **chore(ci):** unblock quality gates - env-doc sync + stryker tap.testFiles registration (#9282) ([#11609](https://github.com/diegosouzapw/OmniRoute/pull/11609)) — thanks @Meet6338-X +- **chore(ci):** point the circular-deps gate at dpdm's real JS entrypoint ([#11615](https://github.com/diegosouzapw/OmniRoute/pull/11615)) — thanks @benzntech +- **chore(quality):** rebaseline file-size for #11448/#11495/#11561 growth ([#11630](https://github.com/diegosouzapw/OmniRoute/pull/11630)) +- **deps:** realign onnxruntime-node pin to @huggingface/transformers' 1.24.3 lockstep ([#11633](https://github.com/diegosouzapw/OmniRoute/pull/11633)) — thanks @jonlwheat2-gif +- **test(autoCombo):** port models_dev_tier #11508 guard from node:test to vitest ([#11635](https://github.com/diegosouzapw/OmniRoute/pull/11635)) — thanks @jonlwheat2-gif +- **chore(quality):** rebaseline file-size for #11083/#11631/#11628 growth ([#11636](https://github.com/diegosouzapw/OmniRoute/pull/11636)) +- **chore(lint):** prune stale ESLint suppressions (base-red #11449) ([#11644](https://github.com/diegosouzapw/OmniRoute/pull/11644)) — thanks @jonlwheat2-gif +- **docs(changelog):** aggregate the ten v3.8.50 fragments into CHANGELOG.md ([#11683](https://github.com/diegosouzapw/OmniRoute/pull/11683)) +- **chore(search):** drop unreachable credential sweep after #11642 ([#11687](https://github.com/diegosouzapw/OmniRoute/pull/11687)) — thanks @HouMinXi +- **ci:** add API route TypeScript regression gate ([#11705](https://github.com/diegosouzapw/OmniRoute/pull/11705)) — thanks @Sabeekhann +- **docs(dedup):** correct the CodeQL claim on the tenant-namespace comment ([#11712](https://github.com/diegosouzapw/OmniRoute/pull/11712)) +- **docs(changelog):** add consolidated v3.8.50 stats, top-25 ranking and a .mailmap ([#11715](https://github.com/diegosouzapw/OmniRoute/pull/11715)) +- **docs:** recommend gstack for AI-assisted workflows ([#11770](https://github.com/diegosouzapw/OmniRoute/pull/11770)) — thanks @santosraju99-hub +- **chore(quality):** tighten complexity/cognitive-complexity ratchets to the current tip; land the missed gateways.ts rebaseline ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **docs(i18n):** finish Freepik → Magnific rebrand in locale strings and README ([#11772](https://github.com/diegosouzapw/OmniRoute/pull/11772)) — thanks @turbolego +- **refactor(auto-combo):** fix divergent scoring in combo health reporting ([#11854](https://github.com/diegosouzapw/OmniRoute/pull/11854)) — thanks @jacobsparts +- **chore(changelog):** add missing fragments for #11919/#11918/#11916 ([#11938](https://github.com/diegosouzapw/OmniRoute/pull/11938)) +- **test(check):** escape the runs-on fixture with JSON.stringify, not a quote-only replace ([#11942](https://github.com/diegosouzapw/OmniRoute/pull/11942)) (direct commit `fb7445eaa3`) +- **test(release):** align five suites with the contracts #11933, #11919 and #11876 shipped on release/v3.8.51 ([#11944](https://github.com/diegosouzapw/OmniRoute/pull/11944)) +- **chore(quality):** remove unreachable code and restore test discovery ([#11950](https://github.com/diegosouzapw/OmniRoute/pull/11950)) — thanks @backryun +- **docs(agents):** sync-back landings are fast-forward, never squash ([#11964](https://github.com/diegosouzapw/OmniRoute/pull/11964)) +- **test(infra):** retry recursive temp-dir removal instead of failing a shard on ENOTEMPTY (#11966) ([#11968](https://github.com/diegosouzapw/OmniRoute/pull/11968)) +- **docs(ops):** the .113 heavy-build ceiling is one runner, not two ([#12048](https://github.com/diegosouzapw/OmniRoute/pull/12048)) +- **test(ui):** correct inactive auto-fetch expectation ([#12098](https://github.com/diegosouzapw/OmniRoute/pull/12098)) — thanks @RaviTharuma +- **docs:** document native dependency check escape hatch ([#12101](https://github.com/diegosouzapw/OmniRoute/pull/12101)) — thanks @RaviTharuma +- **test(providers):** regenerate the translate-path golden for OrcaRouter (#11923) ([#12118](https://github.com/diegosouzapw/OmniRoute/pull/12118)) +- **chore(quality):** velocity phase — loosen every numeric baseline by 20% until v4.0, monitor headroom nightly ([#12125](https://github.com/diegosouzapw/OmniRoute/pull/12125)) +- **chore(lint):** batch 0 of #12146 — type the call-log-cap sqlite rows instead of 45 as-any casts ([#12157](https://github.com/diegosouzapw/OmniRoute/pull/12157)) +- **chore(lint):** batch 4 of #12146 — shared/components react-hooks violations resolved ([#12159](https://github.com/diegosouzapw/OmniRoute/pull/12159)) +- **chore(lint):** batch 1 of #12146 — dashboard/cli-code react-hooks violations resolved ([#12160](https://github.com/diegosouzapw/OmniRoute/pull/12160)) +- **chore(lint):** batch 3 of #12146 — dashboard/settings react-hooks violations resolved ([#12162](https://github.com/diegosouzapw/OmniRoute/pull/12162)) +- **chore(lint):** batch 2 of #12146 — resolve the react-hooks compiler violations in dashboard/providers ([#12163](https://github.com/diegosouzapw/OmniRoute/pull/12163)) +- **chore(quality):** register search-432 cooldown test in stryker tap.testFiles ([#12170](https://github.com/diegosouzapw/OmniRoute/pull/12170)) +- **chore(lint):** batch 5 of #12146 — combos, endpoint, provider-stats, api-manager and costs react-hooks violations resolved ([#12174](https://github.com/diegosouzapw/OmniRoute/pull/12174)) +- **build:** add contributor fast profile ([#12192](https://github.com/diegosouzapw/OmniRoute/pull/12192)) — thanks @rafacpti23 +- **docs(readme):** README touch-ups ([#12193](https://github.com/diegosouzapw/OmniRoute/pull/12193)) ([#12194](https://github.com/diegosouzapw/OmniRoute/pull/12194)) ([#12202](https://github.com/diegosouzapw/OmniRoute/pull/12202)) — thanks @rafacpti23 +- **build:** force the webpack bundler in the contributor build profile ([#12197](https://github.com/diegosouzapw/OmniRoute/pull/12197)) — thanks @rafacpti23 +- **build:** skip standalone packaging in the contributor profile ([#12198](https://github.com/diegosouzapw/OmniRoute/pull/12198)) — thanks @rafacpti23 +- **docs(audit):** align every published number with the code and harden check:docs-counts ([#12200](https://github.com/diegosouzapw/OmniRoute/pull/12200)) +- **build:** stub the instrumentation entrypoints in the contributor profile ([#12203](https://github.com/diegosouzapw/OmniRoute/pull/12203)) — thanks @rafacpti23 +- **build:** omit the standalone output target for contributor builds ([#12204](https://github.com/diegosouzapw/OmniRoute/pull/12204)) — thanks @rafacpti23 +- **chore(lint):** batch 6 of #12146 — memory, radar, audit, analytics, cache, usage, activity, home and RequestLoggerV2 react-hooks violations resolved ([#12208](https://github.com/diegosouzapw/OmniRoute/pull/12208)) +- **docs(diagrams):** rename number-carrying diagram files to stable names ([#12210](https://github.com/diegosouzapw/OmniRoute/pull/12210)) +- **docs(dashboard):** redraw onboarding tier-flow SVGs for the real 4-tier model ([#12211](https://github.com/diegosouzapw/OmniRoute/pull/12211)) +- **docs(api):** document every implemented route in openapi.yaml (276 → 692 paths) ([#12212](https://github.com/diegosouzapw/OmniRoute/pull/12212)) +- **chore(lint):** batch 7 of #12146 — final src tail: 37 react-hooks violations across 33 files resolved ([#12244](https://github.com/diegosouzapw/OmniRoute/pull/12244)) +- Revert "docs: recommend gstack for AI-assisted workflows (#11770)" ([#12248](https://github.com/diegosouzapw/OmniRoute/pull/12248)) ([#12249](https://github.com/diegosouzapw/OmniRoute/pull/12249)) +- **docs:** Chaos Mode setup guide + weighted strategy semantics ([#12250](https://github.com/diegosouzapw/OmniRoute/pull/12250)) +- **docs(agents):** protected-surface merge rule — operator approval for agent-instruction files ([#12253](https://github.com/diegosouzapw/OmniRoute/pull/12253)) +- **chore(quality):** register native-codex turn-pin tests in stryker tap.testFiles ([#12263](https://github.com/diegosouzapw/OmniRoute/pull/12263)) +- **chore(quality):** dedupe tap.testFiles entries added by racing base-red fixes ([#12265](https://github.com/diegosouzapw/OmniRoute/pull/12265)) +- **docs(readme):** introduce OmniRouteTray — the macOS menu-bar companion ([#12276](https://github.com/diegosouzapw/OmniRoute/pull/12276)) — thanks @ggiak +- **chore(proxy):** purge the legacy 1proxy residue — sync/rotator modules, dead DB exports, dead settings tab and flag, docs (#12091) ([#12290](https://github.com/diegosouzapw/OmniRoute/pull/12290)) +- **chore(quality):** baseline-headroom skips generated and vendored files in the fileSize worst-file signal ([#12291](https://github.com/diegosouzapw/OmniRoute/pull/12291)) +- **refactor(video):** unify the JPEG frame data-URI contract ([#12322](https://github.com/diegosouzapw/OmniRoute/pull/12322)) +- **chore(deps):** freeze onnxruntime-node and eslint-plugin-react-hooks out of dependabot groups ([#12329](https://github.com/diegosouzapw/OmniRoute/pull/12329)) +- **test(executors):** restore the #10986 reasoning-only fallback guards ([#12364](https://github.com/diegosouzapw/OmniRoute/pull/12364)) — thanks @pacocartones +- **chore(quality):** re-tighten the file-size ratchet to the real LOC (plan 3.8.52 task 0) ([#12411](https://github.com/diegosouzapw/OmniRoute/pull/12411)) +- **test(grok):** format web executor suite ([#12412](https://github.com/diegosouzapw/OmniRoute/pull/12412)) +- **chore(quality):** ratchet open-sse typecheck baseline to zero ([#12418](https://github.com/diegosouzapw/OmniRoute/pull/12418)) +- **test(build):** guard the artifact path policy arrays against duplicates ([#12422](https://github.com/diegosouzapw/OmniRoute/pull/12422)) +- **chore(lint):** adopt eslint-plugin-react-hooks 7.1.1 ([#12428](https://github.com/diegosouzapw/OmniRoute/pull/12428)) +- **chore(providers):** remove a keyless provider integration at its operator's request ([#12440](https://github.com/diegosouzapw/OmniRoute/pull/12440)) +- **docs(providers):** register providers removed at their operator's request and guard against reintroduction ([#12478](https://github.com/diegosouzapw/OmniRoute/pull/12478)) +- **chore(quality):** base fixes — stryker tap.testFiles + node_modules cache key ([#12482](https://github.com/diegosouzapw/OmniRoute/pull/12482)) +- **docs(arch):** one-process recipe for tens of long /v1/responses ([#12493](https://github.com/diegosouzapw/OmniRoute/pull/12493)) — thanks @RaviTharuma +- **chore(quality):** tighten the CodeQL ratchet baseline from 11 to 6 ([#12530](https://github.com/diegosouzapw/OmniRoute/pull/12530)) +- **chore(deps):** pin browserslist override to ^4.28.8 ([#12592](https://github.com/diegosouzapw/OmniRoute/pull/12592)) — thanks @KooshaPari +- **chore(deps):** bump fast-uri to 3.1.7 in the electron lockfile ([#12601](https://github.com/diegosouzapw/OmniRoute/pull/12601)) +- **docs(quality):** document how the CodeQL ratchet refreshes and how to tighten it ([#12611](https://github.com/diegosouzapw/OmniRoute/pull/12611)) +- **test(ci):** pin the openapi-security-tiers two-arm contract with an executing gate test (#12581) ([#12652](https://github.com/diegosouzapw/OmniRoute/pull/12652)) +- **docs(gamification):** align XP Rewards table with code (#12501) ([#12667](https://github.com/diegosouzapw/OmniRoute/pull/12667)) — thanks @KooshaPari +- **refactor(combo):** split executeTarget into gates, attempt, and loop ([#12746](https://github.com/diegosouzapw/OmniRoute/pull/12746)) — thanks @HouMinXi +- **docs(security):** document socket.yml scanner config + CI workflow link (#12575) ([#12764](https://github.com/diegosouzapw/OmniRoute/pull/12764)) — thanks @KooshaPari +- **chore(ci):** guard commit identity in pre-commit to stop author misattribution ([#12772](https://github.com/diegosouzapw/OmniRoute/pull/12772)) +- **deps:** 17 Dependabot bumps — bump electron from 43.4.0 to 43.4.1 in /electron ([#11426](https://github.com/diegosouzapw/OmniRoute/pull/11426)); bump the development group across 1 directory with 8 updates ([#11428](https://github.com/diegosouzapw/OmniRoute/pull/11428)); bump the production group across 1 directory with 11 updates ([#11440](https://github.com/diegosouzapw/OmniRoute/pull/11440)); chore(deps): bump github/codeql-action/init from 4.37.7 to 4.37.8 ([#11925](https://github.com/diegosouzapw/OmniRoute/pull/11925)); chore(deps): bump github/codeql-action from 4.37.7 to 4.37.8 ([#11926](https://github.com/diegosouzapw/OmniRoute/pull/11926)); chore(deps): bump github/codeql-action/analyze from 4.37.7 to 4.37.8 ([#11927](https://github.com/diegosouzapw/OmniRoute/pull/11927)); build(deps): bump github/codeql-action/init from 4.37.8 to 4.37.9 ([#12345](https://github.com/diegosouzapw/OmniRoute/pull/12345)); build(deps): bump github/codeql-action/analyze from 4.37.8 to 4.37.9 ([#12346](https://github.com/diegosouzapw/OmniRoute/pull/12346)); bump the development group across 1 directory with 2 updates ([#12347](https://github.com/diegosouzapw/OmniRoute/pull/12347)); build(deps): bump github/codeql-action from 4.37.8 to 4.37.9 ([#12349](https://github.com/diegosouzapw/OmniRoute/pull/12349)); bump browserslist from 4.28.2 to 4.28.8 ([#12396](https://github.com/diegosouzapw/OmniRoute/pull/12396)); bump the production group across 1 directory with 4 updates ([#12399](https://github.com/diegosouzapw/OmniRoute/pull/12399)); bump @xmldom/xmldom ([#12500](https://github.com/diegosouzapw/OmniRoute/pull/12500)); bump qs from 6.15.2 to 6.16.0 ([#12512](https://github.com/diegosouzapw/OmniRoute/pull/12512)); bump @xmldom/xmldom from 0.9.10 to 0.9.12 ([#12513](https://github.com/diegosouzapw/OmniRoute/pull/12513)); bump fast-uri from 3.1.5 to 3.1.7 ([#12514](https://github.com/diegosouzapw/OmniRoute/pull/12514)); bump @humanfs/node from 0.16.7 to 0.16.8 ([#12515](https://github.com/diegosouzapw/OmniRoute/pull/12515)) +- Clear the `release/v3.8.51` `check:agent-skills-sync` base-red: regenerate `skills/cli-tunnel/SKILL.md` so the `tunnel create [type]` positional that #13009 taught the generator to read is reflected in the committed skill. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` typecheck base-red from #12731: the new `Mode pack ranking gates` candidates in `open-sse/services/autoCombo/__tests__/autoCombo.test.ts` omitted the required `provider`, `model` and `errorRate` fields of `ProviderCandidate`, failing `check:open-sse-typecheck` (and with it `Fast Quality Gates`) on every open PR. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` docs-sync base-red: EURouter (#13025) and GreenPT (#13024) took the live provider count to 358, leaving 7 STRICT drifts (`PROVIDER_REFERENCE.md`, 4 diagrams, 2 tier-flow images) plus the `AGENTS.md` / `llm.txt` / `package.json` count claims stale. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- **test(models):** Pin `CATALOG_BUILD_TIMEOUT_MS` in the #12058 canonical-catalog regression so a cold catalog build racing the 8s default bound no longer flakes the suite with a 500 ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Allow `@omniroute/opencode-plugin-v2/` in the npm pack policy — [#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870) shipped the v2 plugin beside its v1 sibling without widening the allowlist, so every packed file under it read as an unexpected artifact and failed `check:pack-policy` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Point the release-green validator's pack gate at the tree it is validating — the build-provenance guard defaulted to `origin/main`, which a release branch tip can never be an ancestor of mid-cycle, so `Package artifact` reported a false red on every run and the tarball boot-smoke cascaded off it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **docs(checks):** keep doc counts honest — headings, rankings, catalog, weights, quality gate and scoring diagram now covered ([#12507](https://github.com/diegosouzapw/OmniRoute/pull/12507)) — thanks @maxmad64bis +- **chore(lifecycle):** `check:model-lifecycle` now also diffs `DEFAULT_DEGRADATION_MAP` (the background-task redirect table) against the vendor lifecycle snapshot, refusing a retired id as source or target, with a table-driven unit test beside it. Three rows whose source the vendor had retired — `claude-sonnet-4-20250514`, `gemini-3-pro-preview` and `gpt-5.1-codex` (whose target `gpt-5.1-codex-mini` is retired too) — were dead code, since `checkLifecycle` answers 410 before the redirect runs; they are dropped (#12535 — thanks @pacocartones) +- **test(compression):** cover idle worker eviction at the resource level — the pool must call `terminate()` and must not retain the worker's `MessagePort`, complementing the `exit`-event assertion added with the fix ([#13371](https://github.com/diegosouzapw/OmniRoute/pull/13371)) +- **chore(quality):** raise the `imageGeneration.ts` file-size ceiling for the image-only-model guard that clears the #12945 base-red ([#13392](https://github.com/diegosouzapw/OmniRoute/pull/13392)) +- **chore(quality):** type the combo-test route's JSON response bodies instead of casting them to `any`, drop the now-empty suppression entry, and remove the `hasPerModelQuota` import `chatCore.ts` stopped using when the failure-classification helper was extracted ([#13379](https://github.com/diegosouzapw/OmniRoute/pull/13379)) +- **chore(electron):** the desktop bundle no longer ships the root-level files of every translated docs mirror (`README.md`, `llm.txt`, `CLAUDE.md`, `GEMINI.md`, `CONTRIBUTING.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`) — the packaged app only reads `docs/i18n//docs/**`, which stays. Saves ~11 MB on top of the translated CHANGELOGs already pruned. (#0000) ([#13235](https://github.com/diegosouzapw/OmniRoute/pull/13235)) +- **chore(quality):** retighten the `src/sse/handlers/chat.ts` unused-vars suppression count to the 8 violations that actually remain ([#13385](https://github.com/diegosouzapw/OmniRoute/pull/13385)) +- Bring the documented counts back in line with the code: 171 migrations (was 169) and 20 routing strategies (was 19), plus the regenerated `cli-tunnel` skill reference. ([#12970](https://github.com/diegosouzapw/OmniRoute/pull/12970)) +- Update the documented migration count to 172 after the call-logs provider-stats indexes landed. ([#13209](https://github.com/diegosouzapw/OmniRoute/pull/13209)) +- **chore(ci):** two gates that close the blind spots behind the exclusions above. `check:vitest-exclusions` requires every Vitest exclusion to name a tracking issue and to appear in `config/quality/vitest-exclusions.json` — the previous list grew to 62 files behind a comment pointing at an issue that had been closed for a month. `check-new-key-coverage` requires a key newly added to `en.json` to reach every locale; the existing coverage gate is a percentage floor per locale, so eleven absent keys out of ~13,000 left it at 99.9% while a whole feature shipped untranslated in nine languages. Both are diff-aware, so pre-existing debt stays frozen and neither needed a migration to turn on. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **build:** root layout no longer loads Inter from `next/font/google`, so a production image build does not need fonts.googleapis.com ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **docs(i18n):** bring the eight new `llm.txt` mirrors (km, kn, ml, my, ne, or, pa, si) up to the current provider and migration counts, so `check-docs-sync` stops rejecting every commit ([#13674](https://github.com/diegosouzapw/OmniRoute/pull/13674)) +- **refactor(ui):** flow surfaces (home topology, combo live studio, compression cockpit/waterfall/nodes, token-health badge) express state through the theme-aware `--orch-status-*` tokens instead of fixed dark-mode hex, so green/red/amber/grey stay legible in the light theme; dark mode is byte-identical. Categorical palettes (routing-strategy hues, compression-layer pills, provider brand colors) deliberately stay hex ([#12378](https://github.com/diegosouzapw/OmniRoute/issues/12378)) ([#13203](https://github.com/diegosouzapw/OmniRoute/pull/13203)) +- **chore(tests):** 51 test files that had been excluded from Vitest are running again, restoring roughly 350 assertions to the blocking suite. Each was measured individually first: of the 62 files parked behind the `// #8618 — pre-existing failure` comment, 51 pass against the current tree with no source change, so the exclusions had outlived the failures they were added for. The 11 that genuinely still fail stay excluded, but now point at a live tracker (#13204) rather than at #8618, which was closed in August while the list it tracked kept growing. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **chore(quality):** drop two ESLint suppression entries whose violations no longer exist, so `eslint --suppressions-location` stops rejecting every commit that touches the surrounding files ([#13384](https://github.com/diegosouzapw/OmniRoute/pull/13384)) +- **chore(release):** bring main's post-tag fixes into release/v3.8.51 — sync-back step 2/2 (direct commit `21c488f210`) +- **chore(deps):** patch toml and esbuild in the opencode plugin lockfiles ([#12965](https://github.com/diegosouzapw/OmniRoute/pull/12965)) +- **docs(changelog):** reconcile the v3.8.51 living section — fold 366 fragments, cover every cycle commit, credit every contributor ([#12971](https://github.com/diegosouzapw/OmniRoute/pull/12971)) +- **test(plugin):** wait for the v2 lazy-refresh tier instead of sleeping past it ([#12984](https://github.com/diegosouzapw/OmniRoute/pull/12984)) +- **chore(quality):** rebaseline chatCore.ts after the non-streaming regression fixes ([#13045](https://github.com/diegosouzapw/OmniRoute/pull/13045)) +- **docs(changelog):** add the missing bullet marker to the DeepSeek PoW fragment ([#13200](https://github.com/diegosouzapw/OmniRoute/pull/13200)) +- **chore(deps):** drain the Dependabot queue — 10 of 13 alerts ([#13213](https://github.com/diegosouzapw/OmniRoute/pull/13213)) +- **chore(ci):** make worktrees carry the hooks that enforce the identity gate ([#13236](https://github.com/diegosouzapw/OmniRoute/pull/13236)) +- **chore(deps):** pin csv-parse to ^7.0.2 via overrides (#13117) ([#13300](https://github.com/diegosouzapw/OmniRoute/pull/13300)) — thanks @KooshaPari +- **chore(deps):** bump hono from ^4.12.34 to ^4.13.7 (#13148) ([#13301](https://github.com/diegosouzapw/OmniRoute/pull/13301)) — thanks @KooshaPari +- **chore(deps):** pin joi to ^18.2.8 via overrides (#13085) ([#13302](https://github.com/diegosouzapw/OmniRoute/pull/13302)) — thanks @KooshaPari +- **docs(providers):** add Microsoft 365 Copilot (BizChat) provider guide (#12779) ([#13340](https://github.com/diegosouzapw/OmniRoute/pull/13340)) — thanks @KooshaPari +### 🙌 Contributors + +Thanks to everyone whose work landed in v3.8.51: + +| Contributor | PRs / Issues | +| --- | --- | +| [@5dive-bot](https://github.com/5dive-bot) | #11852 | +| [@abhisheksharma2411](https://github.com/abhisheksharma2411) | #11547, #11684, #11959, #12209 | +| [@adevwithpurpose](https://github.com/adevwithpurpose) | #11464 | +| [@adivekar-utexas](https://github.com/adivekar-utexas) | #12015, #12043, #12090, #12138 | +| [@AIB1TAL0S](https://github.com/AIB1TAL0S) | #12103 | +| [@alltomatos](https://github.com/alltomatos) | #11448 | +| [@alvinveroy](https://github.com/alvinveroy) | #11857, #12027, #12165 | +| [@amaleta](https://github.com/amaleta) | #11906, #11934 | +| [@amartinawi](https://github.com/amartinawi) | #11575 | +| [@AndrianBalanescu](https://github.com/AndrianBalanescu) | #11083, #11830, #11834, #11844 | +| [@anhtahaylove](https://github.com/anhtahaylove) | #13091, #13092, #13093, #13096, #13097, #13100, #13106, #13109, #13114, #13155, #13166, #13171, #13175, #13187, #13196 | +| [@aniruddhaadak80](https://github.com/aniruddhaadak80) | #11760 | +| [@app](https://github.com/app) | #12554 | +| [@arminanton](https://github.com/arminanton) | #11461, #11513 | +| [@AStupidBear](https://github.com/AStupidBear) | #11584 | +| [@atescivitci-cmd](https://github.com/atescivitci-cmd) | #12105 | +| [@b3nw](https://github.com/b3nw) | #11970, #11971, #12147 | +| [@backryun](https://github.com/backryun) | #11259, #11950, #12075, #12076, #12078, #12079, #12081, #12082, #12181, #12228, #12239, #12255, #12258, #12277, #12367, #12423, #12524, #12538 | +| [@benjaminkitt](https://github.com/benjaminkitt) | #11747 | +| [@benzntech](https://github.com/benzntech) | #11614, #11615 | +| [@Bl0ck154](https://github.com/Bl0ck154) | #11948, #11951, #11952, #11953, #11954 | +| [@brick30llc-ctrl](https://github.com/brick30llc-ctrl) | #11752, #12242, #12262 | +| [@Chewji9875](https://github.com/Chewji9875) | #11455, #11875, #12028 | +| [@cheynetom](https://github.com/cheynetom) | #12058 | +| [@davidebaraldo](https://github.com/davidebaraldo) | #12222, #12304 | +| [@davidlinfr](https://github.com/davidlinfr) | #6390 | +| [@ddarkr](https://github.com/ddarkr) | #12124 | +| [@Deftera186](https://github.com/Deftera186) | #11809 | +| [@dpozimski](https://github.com/dpozimski) | #11945 | +| [@DW-MediaLab](https://github.com/DW-MediaLab) | #12229 | +| [@echel0nn](https://github.com/echel0nn) | #11923 | +| [@f9td56dbgh-hub](https://github.com/f9td56dbgh-hub) | #11660 | +| [@fabioluissilva](https://github.com/fabioluissilva) | #11991 | +| [@foreveryh](https://github.com/foreveryh) | #12177 | +| [@geek007git](https://github.com/geek007git) | #12115, #12116, #12117, #12120, #12122 | +| [@geekyNads](https://github.com/geekyNads) | #11784, #12266 | +| [@ggiak](https://github.com/ggiak) | #11561, #12276, #12350, #12402 | +| [@gonisulaimann](https://github.com/gonisulaimann) | #12368, #12369, #12371 | +| [@Gorillaz322](https://github.com/Gorillaz322) | #12207 | +| [@groovecityJO](https://github.com/groovecityJO) | #12682 | +| [@hartmark](https://github.com/hartmark) | #11434, #11452, #11473, #11499, #11703, #11983, #11984, #11985, #11986, #11988, #11989, #11990, #11994, #12221, #12293, #12445, #12446, #12447, #12448, #12460, #12461, #12623, #12646, #12650, #12680, #12717, #12718, #12727, #12741, #12854 | +| [@hizzt](https://github.com/hizzt) | #11894 | +| [@hongnoul](https://github.com/hongnoul) | #11484 | +| [@HouMinXi](https://github.com/HouMinXi) | #11411, #11414, #11512, #11518, #11520, #11641, #11642, #11643, #11687, #11779, #11849, #11850, #11851, #11915, #11916, #11918, #11919, #11920, #12013, #12017, #12033, #12042, #12106, #12139, #12166, #12169, #12171, #12205, #12213, #12312, #12325, #12487, #12488, #12495, #12504, #12557, #12566, #12590, #12591, #12624, #12626, #12632, #12637, #12678, #12696, #12697, #12711, #12733, #12746, #12767, #12770, #12789, #12803, #12805, #12811, #12866, #12868, #12899, #12926, #12934, #12950, #12951, #12974, #13001, #13006, #13011, #13017, #13026, #13027, #13034, #13035, #13038, #13042, #13050, #13060, #13061, #13069, #13107, #13120, #13136, #13195, #13197 | +| [@Hsia97](https://github.com/Hsia97) | #11624 | +| [@jacobsparts](https://github.com/jacobsparts) | #11854, #12155, #12167 | +| [@joglomedia](https://github.com/joglomedia) | #11980 | +| [@jonlwheat2-gif](https://github.com/jonlwheat2-gif) | #11443, #11450, #11549, #11567, #11608, #11633, #11635, #11640, #11644, #11671, #11672, #11673, #11674, #11675, #11676, #11677, #11706, #11783, #12051, #12052, #12053, #12055 | +| [@kanade-hoshino](https://github.com/kanade-hoshino) | #12180 | +| [@Karan825](https://github.com/Karan825) | #11861 | +| [@KaspaPulse](https://github.com/KaspaPulse) | #11389, #11469, #11628, #11666, #11910 | +| [@keeltrace](https://github.com/keeltrace) | #12080, #12223 | +| [@killer30001000](https://github.com/killer30001000) | #12178 | +| [@KooshaPari](https://github.com/KooshaPari) | #12470, #12592, #12667, #12699, #12703, #12706, #12764, #12769, #12771, #13300, #13301, #13302, #13340, #13401, #13403, #13404, #13405, #13406, #13407, #13409, #13410, #13411, #13412, #13413, #13414, #13418, #13424, #13427, #13433, #13523, #13524, #13525, #13528, #13531, #13532, #13534, #13539, #13543, #13545, #13546, #13547, #13550, #13553 | +| [@kriptoburak](https://github.com/kriptoburak) | #11370 | +| [@KrzysiekSko](https://github.com/KrzysiekSko) | #12425, #12432, #12673 | +| [@marcelokarval](https://github.com/marcelokarval) | direct commit / report | +| [@maxmad64bis](https://github.com/maxmad64bis) | #1622, #11435, #11437, #11441, #11537, #11550, #11553, #11555, #11557, #11812, #11842, #11843, #11903, #12151, #12214, #12215, #12218, #12226, #12314, #12316, #12317, #12318, #12319, #12320, #12321, #12507, #12715, #12731, #12744, #12786, #12787, #12788, #12790, #12792, #12794, #12795, #12828, #12832, #12853, #12857, #12870, #12937, #12941, #12975, #13141, #13142, #13146, #13147 | +| [@mdigitalbh81](https://github.com/mdigitalbh81) | #12240 | +| [@Meet6338-X](https://github.com/Meet6338-X) | #11598, #11609, #12110 | +| [@morpheus9393](https://github.com/morpheus9393) | #11943 | +| [@MumuTW](https://github.com/MumuTW) | #11492, #11502, #11506, #11507, #11626, #11675, #11685, #11728, #11746, #11888, #11889, #11890, #11892 | +| [@Neuron-Mr-White](https://github.com/Neuron-Mr-White) | #11622, #11801, #12256 | +| [@NightStalker-87](https://github.com/NightStalker-87) | #12183 | +| [@NoxzRCW](https://github.com/NoxzRCW) | #11879, #11880, #11881, #11882, #11883, #11935 | +| [@ntdat812](https://github.com/ntdat812) | #11585, #12095 | +| [@ntdatt812](https://github.com/ntdatt812) | #11368, #11573, #11574, #11576, #11577, #11580, #11582, #11583, #11588, #11589, #11590, #11591, #11592, #11593, #11672, #12177, #12180, #12873, #12918, #12920, #12921, #12925, #12930, #13007, #13009, #13024, #13025, #13083, #13087, #13101, #13104, #13110 | +| [@official-burak](https://github.com/official-burak) | #11542 | +| [@opensource-elearning](https://github.com/opensource-elearning) | #12179, #12189, #12278, #12286 | +| [@oyi77](https://github.com/oyi77) | #11408, #11409, #11421, #11505, #11677, #12036, #12110 | +| [@pacocartones](https://github.com/pacocartones) | #11521, #11522, #11527, #11528, #11529, #11530, #11531, #11532, #11533, #11534, #11595, #11599, #11603, #11604, #11605, #11607, #11610, #11676, #11714, #11716, #11718, #11767, #11838, #11860, #11862, #11869, #11871, #11872, #11873, #11903, #11906, #11921, #11934, #12356, #12358, #12359, #12360, #12361, #12362, #12364, #12365, #12373, #12374, #12375, #12376, #12377, #12379, #12380, #12381, #12386, #12387, #12389, #12390, #12394, #12395, #12397, #12401, #12403, #12404, #12522, #12523, #12535, #12536, #12540, #12541, #12543, #12545, #12548, #12549, #12550, #12551, #12552, #12647, #12651, #12653, #13055 | +| [@patrykkopycinski](https://github.com/patrykkopycinski) | #11936, #11937, #12224 | +| [@PauloFH](https://github.com/PauloFH) | #11509 | +| [@PauloHSOliveira](https://github.com/PauloHSOliveira) | #12241 | +| [@ponkcore](https://github.com/ponkcore) | #12054 | +| [@Prajeeth-12](https://github.com/Prajeeth-12) | #11634, #12046 | +| [@quiterunner-commits](https://github.com/quiterunner-commits) | #12131, #12143 | +| [@rafacpti23](https://github.com/rafacpti23) | #11554, #11558, #12192, #12197, #12198, #12202, #12203, #12204 | +| [@ragnar-claude](https://github.com/ragnar-claude) | #11564 | +| [@raheemuddin786](https://github.com/raheemuddin786) | #11491, #11839, #11840, #11841, #12003, #12230, #12231, #12232, #12233, #12234 | +| [@rahilmavani](https://github.com/rahilmavani) | #11761 | +| [@Rahulsharma0810](https://github.com/Rahulsharma0810) | #11771 | +| [@RaviTharuma](https://github.com/RaviTharuma) | #11710, #11727, #11797, #11798, #11802, #11805, #11806, #11811, #12098, #12099, #12101, #12449, #12452, #12472, #12473, #12493, #12533, #12607, #12628, #12631, #12636, #12875, #12876, #12880, #12882, #12884 | +| [@rezjalibd](https://github.com/rezjalibd) | #12186 | +| [@RhianB14](https://github.com/RhianB14) | #11692 | +| [@rifqiawl](https://github.com/rifqiawl) | #11517, #11519, #11969 | +| [@rqzbeh](https://github.com/rqzbeh) | #11390 | +| [@Sabeekhann](https://github.com/Sabeekhann) | #11699, #11705 | +| [@santosraju99-hub](https://github.com/santosraju99-hub) | #11755, #11770 | +| [@Seramicx](https://github.com/Seramicx) | #11568 | +| [@Siva010](https://github.com/Siva010) | #12191 | +| [@solstxce](https://github.com/solstxce) | #11597 | +| [@soroush5](https://github.com/soroush5) | #12691 | +| [@tenshiak](https://github.com/tenshiak) | #12279 | +| [@TheDemonTuan](https://github.com/TheDemonTuan) | #11468, #11470, #11471, #11482, #11548, #11758, #11775, #11814 | +| [@thomasmaerz](https://github.com/thomasmaerz) | #12834 | +| [@tuandinh0801](https://github.com/tuandinh0801) | #11454 | +| [@turbolego](https://github.com/turbolego) | #11621, #11762, #11772, #11774, #11781, #12216 | +| [@Tushar49](https://github.com/Tushar49) | #11392 | +| [@ujjawalkaushik1110](https://github.com/ujjawalkaushik1110) | #11863 | +| [@vermasomesh835](https://github.com/vermasomesh835) | #11794 | +| [@vsd2807](https://github.com/vsd2807) | #11565, #11619 | +| [@wahidsadik371-coder](https://github.com/wahidsadik371-coder) | #12126 | +| [@watchingdogs](https://github.com/watchingdogs) | #12031 | +| [@wildcard](https://github.com/wildcard) | #11620 | +| [@xiaoyaner0201](https://github.com/xiaoyaner0201) | #11460, #11662, #12083 | +| [@Xxx91n](https://github.com/Xxx91n) | #11690 | +| [@yourspraveen](https://github.com/yourspraveen) | #11146 | +| [@yxyxy](https://github.com/yxyxy) | #11671 | +| [@ZaimMarzuki](https://github.com/ZaimMarzuki) | #11960 | +| [@Zartharas](https://github.com/Zartharas) | #11340 | +| [@zero-executioner](https://github.com/zero-executioner) | #11631 | +| [@diegosouzapw](https://github.com/diegosouzapw) | maintainer | + +--- ## [3.8.50] — 2026-08-25 _Living section — regenerated 2026-08-12 from all cycle commits (cycle open `ed2db6cb19` → tip). Bullets carry the merged PR and its author; direct pushes listed separately._ diff --git a/docs/i18n/cs/CHANGELOG.md b/docs/i18n/cs/CHANGELOG.md index 2b2fdd0409..c8dba6372a 100644 --- a/docs/i18n/cs/CHANGELOG.md +++ b/docs/i18n/cs/CHANGELOG.md @@ -8,16 +8,1119 @@ ## [3.8.51] — TBD -_Living section — cycle opened at the v3.8.50 freeze (parallel-cycle model). Bullets are aggregated from `changelog.d/` fragments at each `/generate-release` phase._ +_Living section — reconciled 2026-09-15 from all cycle commits (`091589089c` → `c0f92ec98a`, 916 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 | **123** | +| 📝 Commits in the cycle | **916** | +| 🔀 Pull requests referenced | **892** | +| 📋 Changelog entries | **919** | +| 🙌 Contributors credited in entries | **121** | +| 🤖 Automated dependency commits | 18 | + +**Entries by type** + +| Type | Count | +| --- | ---: | +| 🐛 Fixes | 592 | +| ✨ Features | 131 | +| 🧹 Chore | 93 | +| 📚 Docs | 42 | +| 🧪 Tests | 32 | +| ♻️ Refactor | 8 | +| 🏗️ Build | 7 | +| ⚡ Performance | 7 | +| ⚙️ CI | 2 | +| ⏪ Reverts | 2 | +| 📦 Dependencies | 2 | +| 🔒 Security | 1 | + +### 🏆 Top 25 contributors this cycle + +_By commits in `091589089c..c0f92ec98a`, author identities consolidated via `.mailmap` and the merged PR's GitHub login. Bots excluded._ + +| # | Contributor | Commits | +| ---: | --- | ---: | +| 🥇 | diegosouzapw | 323 | +| 🥈 | Bob.Hou (@HouMinXi) | 79 | +| 🥉 | Paco Cartones (@pacocartones) | 77 | +| 4 | Dizzle (@maxmad64bis) | 49 | +| 5 | Koosha Paridehpour (@KooshaPari) | 43 | +| 6 | Nguyen Thanh Dat (@ntdatt812) | 31 | +| 7 | Ravi Tharuma (@RaviTharuma) | 26 | +| 8 | Markus Hartung (@hartmark) | 22 | +| 9 | Webman (@jonlwheat2-gif) | 22 | +| 10 | backryun | 17 | +| 11 | anhtahaylove | 15 | +| 12 | Rafa Martins (@rafacpti23) | 10 | +| 13 | Syed Raheemuddin (@raheemuddin786) | 10 | +| 14 | Nguyễn Viết Tuấn (@TheDemonTuan) | 8 | +| 15 | MumuTW | 7 | +| 16 | Paijo (@oyi77) | 7 | +| 17 | Tobias Andersen (@turbolego) | 6 | +| 18 | Bl0ck (@Bl0ck154) | 5 | +| 19 | SHANMUGAPRIYAN (@geek007git) | 5 | +| 20 | KaspaPulse | 5 | +| 21 | Abhishek Sharma (@abhisheksharma2411) | 4 | +| 22 | Abhishek Divekar (@adivekar-utexas) | 4 | +| 23 | Andrew B. (@AndrianBalanescu) | 4 | +| 24 | NoxzRCW | 4 | +| 25 | opensource-elearning | 4 | ### ✨ New Features +- **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). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- Added Google AI Studio Gemini batch text-to-speech support through `POST /v1/audio/speech`. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- 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. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(routing):** subscription-first auto groupings — `auto/subscription` routes only through plan-included connections with a documented hard-stop overage and fails closed on exhaustion, while `auto/thrifty` orders the pool `subscription → keyless → free → cheap → premium` and steps up one rung at a time as each is exhausted. Billing class comes from a curated per-connection catalog (uncurated is treated as metered, never plan-included), both reuse STRICT_ZERO_COST's per-connection verification, and a quota reading whose `resetAt` has passed is now refreshed regardless of TTL so routing returns to plan capacity as soon as it resets ([#11146](https://github.com/diegosouzapw/OmniRoute/pull/11146)) — thanks @yourspraveen +- **feat(providers):** publish a management-authenticated versioned web-session credential contract from OmniRoute's canonical browser credential metadata ([#11340](https://github.com/diegosouzapw/OmniRoute/pull/11340)) — thanks @Zartharas +- **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)) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add Xquik X search with typed results, credential validation, REST routing, and MCP selection ([#11370](https://github.com/diegosouzapw/OmniRoute/pull/11370)) — thanks @kriptoburak +- **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)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(dashboard):** surface durable exclusive managed leases in the existing Sessions view, keeping leased clients visible across idle gaps while marking connections with in-flight work as active ([#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389)) — thanks @KaspaPulse +- **build(bun):** allow Turbopack bundler flag on Bun 1.4+ with configurable Webpack fallback ([#11471](https://github.com/diegosouzapw/OmniRoute/pull/11471)) — thanks @TheDemonTuan +- feat(api): add an opt-in `modelVisibilityAllowlist`/`modelVisibilityDenylist` settings pair to curate exactly which models `/v1/models` advertises, mirrored into every `auto/*` combo candidate pool so a denied model cannot be routed to via combo selection either (#11481) ([#11997](https://github.com/diegosouzapw/OmniRoute/pull/11997)) +- **feat(rankings):** the Free Provider Rankings page shows what each provider actually served over the last 24 h. It ranked by ELO alone, which left a provider that answers every call with an error in first place; the usage data was already served by the API but never requested. A provider with too small a sample shows a dash, not a number ([#11546](https://github.com/diegosouzapw/OmniRoute/pull/11546)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **feat(guardrails):** enforce a bounded, deterministic contract for Video Bridge transcripts — 256 cues, 4096 input code units and 4 KiB UTF-8 per cue, 64 KiB total text, malformed-Unicode rejection, focus-window scoping, cross-source reconciliation with contributing-source metadata, and a structural provenance trust boundary so caller JSON can never self-assert `embedded`/`audio-bridge` provenance ([#11652](https://github.com/diegosouzapw/OmniRoute/issues/11652)) ([#12009](https://github.com/diegosouzapw/OmniRoute/pull/12009)) +- **feat(video):** orchestrate optional Video Bridge audio extraction and Audio Bridge STT behind a dual opt-in (operator setting AND per-request signal) — a new loopback-only broker `mode=audio` operation shares the frame path's exact process queue, deadline, AbortSignal, and byte budgets to extract a bounded mono 16 kHz PCM WAV from the same already-downloaded video, then reuses the existing Audio Bridge transcription boundary; provider segment timing is preserved when available and marked coarse otherwise, and every failure degrades to a visual-only-safe partial instead of throwing (#11654). ([#12012](https://github.com/diegosouzapw/OmniRoute/pull/12012)) +- Add a tenant-bound Video Bridge drill-down lifecycle on top of the existing secure cache substrate: opaque hashed handles (never raw session/video identifiers), preview/standard/detail multiresolution variants resampled on read, response pagination capped at 8 frames and 32 MiB, and a new authenticated `/api/v1/video-bridge/drilldown` consumer route that stays disabled for remote access by default and denies cross-key access with the same response as a nonexistent handle (no existence oracle). ([#12006](https://github.com/diegosouzapw/OmniRoute/pull/12006)) +- **test(video):** Add the Video Bridge FU-07/FU-09 promotion-evidence harness (#11656) — a frozen Zod manifest schema covering the 8 required scenario kinds (static scenes, rapid cuts, late facts, fades, blur, small text, close events, visual prompt injection) with a minimum of 3 repetitions per case, deterministic declarative fixture recipes (`videoBridgePromotionFixtures.ts`), a pure medians/p95 metrics aggregator, a pure FU-07/FU-09 promotion-verdict evaluator applying the ticket's exact thresholds (missing token usage always holds), a digest-only persistence layer that never retains raw media or raw model responses, and a versioned per-model promotion allowlist shipped empty with every model defaulting to `hold`. The FU-07/FU-09 promotion verdicts themselves remain HOLD — they require a real evidence run against real models on VPS 192.168.0.15. ([#12008](https://github.com/diegosouzapw/OmniRoute/pull/12008)) +- **feat(video bridge):** "embedded" transcript provenance can now be legitimately earned instead of merely asserted — a bounded, allowlisted (`mov_text`/`subrip`/`webvtt`) subtitle probe runs through the loopback-only Video Bridge broker (at most 2 streams, 10s subdeadline bounded by the request deadline, 256 KiB output, 4096-code-unit lines), normalized through a bounded, ReDoS-safe WebVTT parser and Zod-validated end to end. The adapter always resolves to an explicit `success`/`absent`/`transient_failure` outcome — a subtitle failure never breaks the visual description path, and only a fingerprint-verified broker response (never a caller-declared label) can produce embedded cues (#11659). ([#12011](https://github.com/diegosouzapw/OmniRoute/pull/12011)) +- Default new Antigravity-family connections (agy CLI imports and Antigravity OAuth connects) to model auto-sync, so live model discovery lands in the synced catalog and `/v1/models` picks up freshly released upstream models (e.g. Gemini 3.7 Flash tiers) without code changes. Existing connections keep their current setting; the per-connection dashboard toggle remains the opt-out. (#11685 — thanks @MumuTW) ([#11887](https://github.com/diegosouzapw/OmniRoute/pull/11887)) +- **feat(ui):** enable React Compiler (`reactCompiler: true` + `babel-plugin-react-compiler`) for automatic memoization at build time ([#11783](https://github.com/diegosouzapw/OmniRoute/pull/11783)) — thanks @jonlwheat2-gif +- **feat(providers):** add SeekAi (`seekai.cc`) as an OpenAI-compatible New-API gateway — catalog id `seekai` (alias `ska`), `https://seekai.cc/v1`, live `/v1/models` via `passthroughModels`, aggregator-list membership so New-API balance detection can opt in. No referral/aff codes. ([#11786](https://github.com/diegosouzapw/OmniRoute/issues/11786)) ([#12557](https://github.com/diegosouzapw/OmniRoute/pull/12557)) — thanks @HouMinXi +- **feat(zai):** add GLM-5.3-Flash Coding Plan support (1M context, 128K output, vision, `low|high|max` reasoning) and route `zai` GLM-5.3-family API-key traffic through the OpenAI-compatible Coding Plan endpoint with native thinking defaults ([#11801](https://github.com/diegosouzapw/OmniRoute/pull/11801)) — thanks @Neuron-Mr-White +- **feat(combo):** choose how combo models are ordered — manual, provider, score, or name — via a sort control in the dashboard builder, persisted in `config.modelSort` and re-applied on load and after add ([#11812](https://github.com/diegosouzapw/OmniRoute/pull/11812)) — thanks @maxmad64bis +- **feat(free):** custom models can be marked free-tier via `customModels[].isFree`; `isFreeModel()` is the first door and `hidePaidModels` respects it even for providers outside the free budget ([#11843](https://github.com/diegosouzapw/OmniRoute/pull/11843)) — thanks @maxmad64bis +- **feat(nodejs):** add `5dive` as a `configure` target — `omniroute configure 5dive` / `omniroute setup-5dive` write a 5dive auth profile that points an agent fleet's `claude` seats at OmniRoute, with the root-only write, the loopback-vs-`https` endpoint rule and the per-seat model pin handled explicitly ([#11852](https://github.com/diegosouzapw/OmniRoute/pull/11852)) — thanks @5dive-bot +- **feat(sse):** treat `max` as a first-class reasoning-effort tier and clamp per model family (GLM 5.1+/DeepSeek V4+/Kimi K3+ keep native `max`; o1/MiniMax/Grok/Muse Spark clamp to their upstream ceiling) ([#11875](https://github.com/diegosouzapw/OmniRoute/pull/11875)) — thanks @Chewji9875 +- **feat(providers):** the provider plugin manifest now advertises a `usage-fetch` capability for the 40 providers that have a wired usage/quota fetcher, so external dashboards can read it from `GET /api/v1/provider-plugin-manifest` instead of parsing `open-sse/services/usage.ts` after every release. Discovery only — no new fetcher, no quota change, and the Dashboard quota widget stays gated by `USAGE_SUPPORTED_PROVIDERS`. `USAGE_FETCHER_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/fetcherProviders.ts`) and is re-exported from `services/usage.ts`, keeping the manifest module a light leaf instead of pulling the ~490-module usage dispatcher into the manifest route. ([#11903](https://github.com/diegosouzapw/OmniRoute/pull/11903)) — thanks @maxmad64bis / @pacocartones +- **feat(plugins):** `OMNIROUTE_PLUGINS_DIR` sets the directory the runtime plugin scanner reads — and the root the plugin manager installs into — overriding the `HOME`-derived default, so a Docker/K8s deployment can point straight at its bind-mounted plugin tree instead of moving `HOME` just to relocate the scan path. An image that exports no home no longer scans `/tmp/.omniroute/plugins` in silence: the resolved directory is logged once at startup as `scanner.dir_resolved`, naming the input that won. Unset, behaviour is unchanged. Distinct from the CLI-only `OMNIROUTE_PLUGIN_PATH`, which finds `omniroute-cmd-*` command packages and never reached this scanner ([#11906](https://github.com/diegosouzapw/OmniRoute/pull/11906)) — thanks @amaleta / @pacocartones +- **feat(leases):** add an explicit owner-authenticated status action that returns only the active lease's privacy-safe configured connection and provider labels, with generation fencing and no credential or internal-id disclosure ([#11910](https://github.com/diegosouzapw/OmniRoute/pull/11910)) — thanks @KaspaPulse +- **feat(providers):** import-from-file modal shows per-row API errors and ships a downloadable CSV template ([#12071](https://github.com/diegosouzapw/OmniRoute/issues/12071)) ([#12504](https://github.com/diegosouzapw/OmniRoute/pull/12504)) — thanks @HouMinXi +- **feat(providers):** dashboard search matches connection name and `baseUrl` so imported OpenAI-compat nodes surface on the provider card ([#12108](https://github.com/diegosouzapw/OmniRoute/issues/12108)) ([#12495](https://github.com/diegosouzapw/OmniRoute/pull/12495)) — thanks @HouMinXi +- **feat(routing):** add a `score` Auto router strategy that selects the highest configured weighted score and reuses `explorationRate`. ([#12155](https://github.com/diegosouzapw/OmniRoute/pull/12155)) — thanks @jacobsparts +- Add a runtime feature flag to disable universal context handoffs globally without changing the default behavior. ([#12167](https://github.com/diegosouzapw/OmniRoute/pull/12167)) — thanks @jacobsparts +- **feat(providers):** the provider plugin manifest now also advertises a `usage-supported` capability for the 46 providers whose usage API is accepted by the server and Dashboard routes, so integrators can distinguish "the server will serve quota for this provider" from "a fetcher is wired" without reading TypeScript. Discovery only — no fetcher or quota change. `usage-fetch` resolves on id or alias (the usage dispatcher accepts both); `usage-supported` resolves on id alone, matching the runtime guard `USAGE_SUPPORTED_PROVIDERS.includes(providerId)`. `USAGE_SUPPORTED_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/supportedProviders.ts`) and is re-exported from `providers.ts`, mirroring the `fetcherProviders` leaf from #11903 and keeping the manifest a light module. ([#12214](https://github.com/diegosouzapw/OmniRoute/pull/12214)) — thanks @maxmad64bis +- **feat(rankings):** order Free Provider Rankings by what each provider actually served — `GET /api/free-provider-rankings?sortBy=reliability` and a "Most reliable first" toggle on the page. Providers with too few calls to state a success rate keep their score order below the measured ones; the default order is unchanged ([#12218](https://github.com/diegosouzapw/OmniRoute/pull/12218)). — thanks @maxmad64bis +- **perf(sse):** defer `cloneLogPayload()` in the structured SSE collector until after the `maxEvents`/`maxBytes` cap check, eliminating ~9,800 wasted `structuredClone` calls per streaming response (65–71% faster `push()`). Reducer snapshot isolation restored for OpenAI and Responses summaries ([#12241](https://github.com/diegosouzapw/OmniRoute/pull/12241)) — thanks @PauloHSOliveira ([#12243](https://github.com/diegosouzapw/OmniRoute/pull/12243)) +- **feat(usage):** Devin CLI agentic quota (Codeium seat-management GetUserStatus) and OpenRouter key limits plus account credits now surface in Provider Limits ([#12256](https://github.com/diegosouzapw/OmniRoute/pull/12256) — thanks @Neuron-Mr-White) +- **feat(settings):** persist `headroomUrl` through Settings so status/start use the operator URL instead of only `HEADROOM_URL` ([#12306](https://github.com/diegosouzapw/OmniRoute/issues/12306)) ([#12487](https://github.com/diegosouzapw/OmniRoute/pull/12487)) — thanks @HouMinXi +- **feat(auto-combo):** Auto-Combo scoring can now weigh how often a provider/model has actually succeeded. The engine already carried that number on every candidate — 24 hours of usage history behind a ten-sample floor, real-time metrics otherwise — and the scoring function never read it, while the weight table described `stability` as if it did. `reliability` (`1 - failureRate`, with the same field precedence and the same rate-bounding the speed ranking already uses, so a corrupt reading means "nothing observed" rather than "fails every call") is now a declared factor shipping at weight `0`, so routing is unchanged until an operator gives it one, and the `stability` description now matches what that factor computes ([#12317](https://github.com/diegosouzapw/OmniRoute/pull/12317)) — thanks @maxmad64bis +- **feat(routing):** With `freeAccessPolicy: "strict"`, the read-only candidate listing (`GET /v1/auto-combo/{channel}/candidates`) no longer hides the candidates the zero-cost guard excludes — the same read-only transparency the resilience filter already honours (#9133). Each candidate now carries `freeAccessExclusion` saying why it would be kept out, and it tells an exhausted allowance apart from a quota reading that never arrived or went stale, which used to look identical from the outside. Routing is unchanged: the listing reports, it never enforces. The separate `excludeTosAvoid` guard still drops its candidates without a reason; that gap is now documented rather than closed ([#12319](https://github.com/diegosouzapw/OmniRoute/pull/12319)) — thanks @maxmad64bis +- **feat(radar):** The Radar catalog table now shows two facts it was already receiving from the feed and dropping on the floor: the per-model rate limits (requests and tokens, per minute and per day) in a new column, and a badge when a provider's terms state it may train on the prompts you send. A limit of zero renders as zero rather than "rate-only" — for a ceiling those are opposite facts — and a model with no training statement gets no badge, because an absent statement is not a guarantee ([#12320](https://github.com/diegosouzapw/OmniRoute/pull/12320)) — thanks @maxmad64bis +- **feat(radar):** explain Community, single-use, contributor, supporter, recovery, abuse, offers, and privacy rules before either Radar activation action, and remove the superseded fixed-PR grant promise from every UI locale ([#12342](https://github.com/diegosouzapw/OmniRoute/pull/12342)) +- **feat(gamification):** the dashboard Profile page now shows the real daily streak — `/api/gamification/level` returns `streak: { current, longest }` (per key with `apiKeyId`, operator-wide maximum otherwise) and the streak card reads it instead of a hard-coded 0 (#2403) ([#12377](https://github.com/diegosouzapw/OmniRoute/pull/12377)) — thanks @pacocartones +- **feat(gamification):** the dashboard leaderboard now shows each API key's display name under the Name column instead of a truncated key id; `GET /api/gamification/leaderboard` attaches `name` per entry (name only — no key material), while the shared ranking helper and the federation leaderboard stay id-only — thanks @pacocartones ([#12385](https://github.com/diegosouzapw/OmniRoute/pull/12385)) +- **feat(gamification):** enforce the documented 1000 XP/min per-API-key anti-cheat rate limit on the XP award path; over-limit awards are logged and skipped instead of persisted, and the sliding window now matches the timestamp format stored in `xp_audit_log` ([#2403](https://github.com/diegosouzapw/OmniRoute/issues/2403)) ([#12390](https://github.com/diegosouzapw/OmniRoute/pull/12390)) — thanks @pacocartones +- **feat(admin):** localize the gamification anomalies page — the loading state, the Status column and the Suspicious badge now come from the `common` catalog (new `common.suspicious` key propagated to every locale) — add it to the Gamification sidebar group as `gamification-admin` (`/dashboard/gamification/admin`), and expose the loading and empty states as polite `role="status"` live regions (#12401 — thanks @pacocartones) +- **feat(providers):** skip GitHub combo members missing from the live synced catalog, and drop Copilot models that are policy-disabled or hidden from the model picker ([#12473](https://github.com/diegosouzapw/OmniRoute/pull/12473)) — thanks @RaviTharuma +- **feat(i18n):** locale-expansion tooling — `npm run i18n:add-locale` adds a language to config, dashboard, docs mirrors, CLI, README/indexes and site in one command; browser, CLI and cookie detection resolve `uk`, `fil`/`tl`, `zh-Hant` (and the retired legacy `in` → `id`) via config aliases; `config/i18n.json` now ships in the npm package so the published CLI can read it; new real-translation ratio gate (`npm run i18n:check-ratio`, advisory) with a per-locale ratchet baseline; `run-translation --adopt` restores `.i18n-state.json`; `sync-language-bars` generates 🌐 bars from config; `validate_translation.py` loads its allowlist again. Retires the duplicate `in` locale (Indonesian mislabelled as Hindi) — 42 honest locales; saved `NEXT_LOCALE=in` / `OMNIROUTE_LANG=in` keep working. (#12496) +- **feat(api):** Emit gateway-measured `tokens_per_second` (TTFT excluded) on streaming usage and `X-OmniRoute-Tokens-Per-Second` when first-token latency is known ([#12616](https://github.com/diegosouzapw/OmniRoute/issues/12616)) ([#12631](https://github.com/diegosouzapw/OmniRoute/pull/12631)) — thanks @RaviTharuma +- **feat(opencode-plugin): map gateway cost/usage/tok/s onto OpenCode inference payloads** — the official plugin copies `X-OmniRoute-Response-Cost`, token counts, `X-OmniRoute-Tokens-Per-Second` / `usage.tokens_per_second`, TTFT, and the winning `X-OmniRoute-Model` onto the JSON/SSE body OpenCode already consumes. Missing tok/s is left unset (never `tokens / latency`). (#12636) — thanks @RaviTharuma +- **feat(combo):** add combo strategy `quota-weighted`: skip exhausted accounts, then weighted-draw by leftover / in-flight load; existing conversations stay pinned until the account is empty ([#12789](https://github.com/diegosouzapw/OmniRoute/pull/12789)) — thanks @HouMinXi +- **feat(models):** Account live listings become the chat catalog source for Claude, Codex, Copilot, and AGY; public metadata only fills prices on IDs those accounts already list. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **fix(models):** Union `agy` and `antigravity` live catalogs so `agy/gemini-3.8-flash-high` is not rejected after the prefix folds to `antigravity`. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **feat(opencode):** Add `@omniroute/opencode-plugin-v2`, an opencode v2 plugin that gives a v2 install a route to an OmniRoute gateway: it publishes models, combos and auto-combos into the host catalog, refreshes them lazily behind a 300s TTL, and keeps serving the last known catalog from an on-disk snapshot when the gateway is unreachable. The v1 plugin (`@omniroute/opencode-plugin`) is untouched and keeps working as before. The plugin adapts to the host instead of assuming one shape: it reads the shape the host seeds into the catalog draft and publishes the `api` block, the pre-`api` top-level fields, or both. The gateway key comes from the host credential store when one is connected, so no secret needs to sit in a config file; a source the gateway refuses is named in a warning instead of silently degrading the catalog to raw model ids; and tool schemas bound for a Gemini model are stripped of the JSON-Schema keywords Gemini rejects, so tool calling survives that route ([#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870)) — thanks @maxmad64bis +- feat(services): show sanitized CLIProxyAPI account health from its authenticated management API without exposing credentials, file paths, or raw account metadata (#6342) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add AnySearch free web search + URL extract (webFetch) with typed results, credential validation, REST routing, and MCP selection - fallback-only ([#11690](https://github.com/diegosouzapw/OmniRoute/pull/11690)) — thanks @Xxx91n +- **feat(dashboard):** display clamped `[0, 100]%` cached input token ratio in request logs table ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) — thanks @b3nw +- **feat(catalog):** add `OMNIROUTE_DISABLE_THINKING_LEVEL_VARIANTS` feature flag to optionally filter out thinking level variants from model catalog ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) — thanks @b3nw +- **feat(dashboard):** continuously export call logs to external analytics stores. A pluggable destination registry ships the full Logs-tab record set on an hourly `JobRegistry` cron, with a persisted per-destination cursor, batched inserts, a config UI rendered from each destination's own field descriptors, and a REST layer (`/api/log-export/*`) for CRUD, a connection test, and an on-demand run. A destination can opt into `includeBodies` to also ship the request and response payloads shown in the Logs detail pane, including the client and provider views of each call; this is off by default, and payloads inherit the dashboard's PII sanitisation, secret redaction and `noLog` handling. Google BigQuery is the first destination, using a service-account key stored encrypted at rest and streaming inserts keyed by call-log id, into a table that is day-partitioned on `timestamp` and clustered on `api_key_name`, `provider`, `model` and `status`. ([#11945](https://github.com/diegosouzapw/OmniRoute/pull/11945)) — thanks @dpozimski +- **feat(providers):** add MaxAI as a signed, OpenAI-compatible provider serving its 13 paid chat models (GPT-5.6 / Luna / Thinking, Claude 5 Sonnet, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite, Grok 4.1-fast / 4.5, DeepSeek V3.2 / R1, Llama 3.3 70B) through OmniRoute's `/v1` endpoint, with per-request HMAC-SHA1→SM3→AES request signing, live model + context-window discovery from `/models/get_config`, and prompted tool-calling translated to OpenAI `tool_calls` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI vision input — image_url content parts are forwarded inline in `message_content` to the 6 vision-capable models (GPT-5.6 / Luna / Thinking, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite) ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI image generation — 6 image models (gpt-image-1, dall-e-3, flux-1-schnell/dev/pro, sd3-medium) exposed through `POST /v1/images/generations` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI document RAG — inline base64 file/document attachments are uploaded to MaxAI (content-addressed `doc_id`) and attached to the chat via `doc_list` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** browserless MaxAI onboarding — email device-pair login (`/api/providers/[id]/login`) and signed access-token refresh, so a connection can be created and kept fresh without a real browser or Google OAuth ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** per-provider TLS impersonation profile (MaxAI presents a Windows Firefox-150 client fingerprint) so its bot-sensitive endpoints accept OmniRoute traffic ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** add **Nimble** as a web-search and web-fetch provider (`nimble-search`) — `/v1/search` routes to Nimble's search API at `lite` depth (locale, freshness and include/exclude domain filters mapped onto the shared request contract), and `/v1/web/fetch` routes to Nimble Extract, which covers all four fetch formats (`markdown`, `html`, `links`, `screenshot`) from a single call. One API key serves both surfaces. ([#11620](https://github.com/diegosouzapw/OmniRoute/pull/11620)) — thanks @wildcard +- The npm publish is automatic again, through npm Trusted Publishing (OIDC): the hosted `stage-npm` job publishes with a short-lived credential minted from GitHub's id-token — no `NPM_TOKEN`, no 2FA prompt, provenance attached. `publish_mode=staged` (owner approves with 2FA) and `direct` (token) remain available on `workflow_dispatch`. ([#11931](https://github.com/diegosouzapw/OmniRoute/pull/11931)) +- **feat(providers):** Add **Opper** as an API-key gateway provider — EU-hosted AI gateway with 700+ models from 30+ providers behind one OpenAI-compatible API and one key (`OPPER_API_KEY`); model ids use `provider/model` format (e.g. `anthropic/claude-sonnet-4-6`, `openai/gpt-5`); live model catalog at `https://api.opper.ai/v3/compat/models`; entry mirrors `requesty` (same shape, `passthroughModels: true`, no static seed) ([#11629](https://github.com/diegosouzapw/OmniRoute/pull/11629)) +- **feat(dashboard):** the `/dashboard/orchestration` snapshot hook now subscribes to the `agents` WebSocket channel (`agent.task.updated`) instead of `requests` as its refetch trigger, and relaxes its background poll from 5s to 30s while that WS connection is up — falling back to the tighter 5s cadence, reprogrammed live on any connect/disconnect transition, whenever the socket is down. ([#12409](https://github.com/diegosouzapw/OmniRoute/pull/12409)) +- **feat(dashboard):** Orchestration canvas quick wins — search box plus state/source/provider filter chips with a one-click clear, and per-source collapse/expand, all reflected in the URL so a filtered/collapsed view is shareable and survives a refresh; the detail drawer gained a "copy trace JSON" action and hardened error/empty-state and accessibility handling; the Agents-tab edges now animate traveling particles along active (running) connections; and the canvas node/edge status colors moved off fixed hex values onto theme-aware `--orch-status-*` CSS custom properties, so they adapt correctly to light/dark mode. ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- **chore(dashboard):** Orchestration UI hardening pass and the missing component/model test coverage it called for — `OrchestratorNode`/`ActivityNode`/`OverflowNode` rendering, the `?node=`/overflow-click page routing, the Agents-tab orchestrator-click no-op and `showCompleted` toggle, and the overview kanban's done-column sort order (#12270, #12271). ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- New `/dashboard/orchestration` page: live unified view of everything running — Cloud Agent, A2A and Conductor as a real-time graph (Agents tab), the combo cascade (Routing tab, reusing the Combo Live Studio) and a state kanban (Overview tab), with a detail drawer (trace, cost, approve/cancel). Read-only over existing APIs — no new backend. Canvas concept credit: PR #11815 design ([#12261](https://github.com/diegosouzapw/OmniRoute/pull/12261)) +- **feat(dashboard):** new "History" tab on `/dashboard/orchestration` — an Airflow-style grid of finished runs over a 24h/7d/30d preset window, one row per (source, identity), clicking a cell opens the existing detail drawer. It is backed by real persistence: A2A task lifecycle transitions are now written to the `a2a_tasks` table (purged after 30 days, configurable via `OMNIROUTE_A2A_HISTORY_RETENTION_DAYS`) and served by the new `GET /api/a2a/tasks/history` listing endpoint, with the task-detail route falling back to persisted history once a run leaves the in-memory snapshot. Conductor runs stay remote and are not persisted locally — the tab says so instead of silently omitting them. ([#12479](https://github.com/diegosouzapw/OmniRoute/pull/12479)) +- **feat(dashboard):** the orchestration detail drawer gained a "Repeat" action for Cloud Agent, A2A and Conductor tasks — a two-click confirm (click once to arm, click again within the confirm window to fire) re-submits the original prompt/input as a new run. The button is disabled with an explanatory tooltip whenever the original input can't be recovered from the loaded task detail (e.g. it never carried a prompt, or the detail failed to load). Two limitations of the A2A variant, by design: it targets the `/a2a` JSON-RPC endpoint, which authenticates with an API key only (`REQUIRE_API_KEY=true` or a configured `OMNIROUTE_API_KEY` makes a dashboard-session repeat answer `HTTP 400` — surfaced verbatim in the drawer's error line, never as a success), and the `message/send` call is SYNCHRONOUS: the POST blocks for the whole skill run, so the success confirmation only appears once the repeated task finishes. A dashboard-authenticated A2A creation path is deliberately left to a follow-up — widening the endpoint's auth posture is an operator decision, not a side effect of this feature. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(a2a):** A2A task execution now records which memories were consulted for the task's last user message as `metadata.memoryHits` (id/key/type/content-snippet) plus a `memory_hits` history event, purely for observability — the retrieved memory is never injected into a skill's prompt or behavior. Gated by the `OMNIROUTE_A2A_MEMORY_HITS` kill-switch (default enabled; set to `0` to skip the recall lookup entirely). The drawer's new "Memory used" section lists these hits for a2a tasks and is omitted whenever there are none. Known limitation: recall only resolves under the keyless posture — a keyed caller's task owner is a SHA-256 prefix of the API key, while memory rows are keyed by the database api-key id, and no hash→id lookup exists today, so the hit list stays empty for keyed callers. The recorded hits are also kept out of the task's own `input` (and therefore out of the persisted input and of the "Repeat" request body), so repeating a task never re-sends the previous run's memory snippets. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(providers):** add a Perplexity Agent API provider (`perplexity-agent` / `pplx-agent`) for Perplexity `/v1/responses`, including the documented Anthropic, OpenAI, Google, xAI, DeepSeek, Z.AI, Moonshot/Kimi, NVIDIA, and Perplexity model IDs plus Anthropic-model `max_output_tokens` compatibility. ([#12103](https://github.com/diegosouzapw/OmniRoute/pull/12103)) — thanks @AIB1TAL0S +- **feat(providers):** add RPD (Requests Per Day) limit to provider rate limit overrides across UI, schemas, DB, and i18n ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) — thanks @b3nw +- **feat(providers): add UC Direct (uncensored.com Developer API), the metered OpenAI-compatible surface.** A standard OpenAI-compatible passthrough (default executor) for uncensored.com's official REST API at `https://api.uncensored.com/api/v1`: `X-api-key` auth (never-expiring `uai_sk_live_` key), `POST /chat/completions` with streaming SSE and native tool-calling, and the full live metered catalog (82 models across 15 providers, discovered from the public `GET /v1/models`). Registered as provider `uc-direct` (alias `ucd`). Complements the un-metered `uc` persona provider — same models, metered credits and a plain API key instead of a subscription session. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(providers): add UC (uncensored.com), the un-metered subscription "persona" chat as an OpenAI-compatible provider.** A WebSocket web-app port: a durable Clerk credential mints a short-lived session token per connect (browserless — no API key), driving UC's persona socket. Ships the browserless email-code login (request → verify → harvest), the 19 verified persona models (Claude Opus, Gemini, Grok, GLM, Kimi, DeepSeek, MiniMax, incl. the uncensored variants), prompted `` tool-calling with a per-model code-style dialect + auto-cure retry for guardrailed models, live ``/reasoning split, streaming + non-streaming OpenAI responses, and full quota/auth error surfacing (paywall / message-limit / rate-limit → 429, invalid session → 401 re-login). Full multimodal parity via the persona blob-upload layer: **vision** (image input, 15 vision-capable models), **document RAG** (PDF/doc upload, server-side extraction), **image generation** (22 models), **video generation** (14 models, async signed-url → poll), and **TTS** (streaming MP3). Registered as provider `uc` (alias `ucn`). The metered OpenAI-compatible Developer API is a separate `uc-direct` provider. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(logs):** add copy-all button to request detail modal ([#11083](https://github.com/diegosouzapw/OmniRoute/pull/11083)) — thanks @AndrianBalanescu +- **feat(check):** make serviceKinds required and add the reverse-walk provider consistency gate ([#11392](https://github.com/diegosouzapw/OmniRoute/pull/11392)) — thanks @Tushar49 +- **feat(providers):** only advertise verified-working connections by default ([#11448](https://github.com/diegosouzapw/OmniRoute/pull/11448)) — thanks @alltomatos +- **feat(resilience):** add hierarchical concurrency admission ([#11493](https://github.com/diegosouzapw/OmniRoute/pull/11493)) +- **feat(providers):** derive + surface expiry for JWT-bearing web cookies (#11497) ([#11505](https://github.com/diegosouzapw/OmniRoute/pull/11505)) — thanks @oyi77 +- **feat(tunnels):** support named cloudflared tunnels via CLOUDFLARED_CONFIG ([#11561](https://github.com/diegosouzapw/OmniRoute/pull/11561)) — thanks @ggiak +- **feat(compression):** make proactive context-compression threshold a live setting ([#11564](https://github.com/diegosouzapw/OmniRoute/pull/11564)) — thanks @ragnar-claude +- **feat(resilience):** add provider peak-hour protection ([#11622](https://github.com/diegosouzapw/OmniRoute/pull/11622)) — thanks @Neuron-Mr-White +- **feat(providers):** add 1min.ai provider ([#11631](https://github.com/diegosouzapw/OmniRoute/pull/11631)) — thanks @zero-executioner +- **feat(dashboard):** collapsible JSON tree viewer for request/response payloads ([#11703](https://github.com/diegosouzapw/OmniRoute/pull/11703)) — thanks @hartmark +- **feat(routing):** add LiquidAI LFM2.5-2.6B free tier via OpenRouter ([#11752](https://github.com/diegosouzapw/OmniRoute/pull/11752)) — thanks @brick30llc-ctrl +- **feat(pricing):** add GLM-5.3-Flash pricing, model specs, and catalog registration ([#11830](https://github.com/diegosouzapw/OmniRoute/pull/11830)) — thanks @AndrianBalanescu +- **feat(routing):** add relayMode for schema-locked context handoffs ([#11839](https://github.com/diegosouzapw/OmniRoute/pull/11839)) — thanks @raheemuddin786 +- **feat(config):** add support for runtime system prompt configuration and hot-reloading ([#11841](https://github.com/diegosouzapw/OmniRoute/pull/11841)) — thanks @raheemuddin786 +- **feat(routing):** expand connection-aware quota prefilter across combo strategies (#11682) ([#11850](https://github.com/diegosouzapw/OmniRoute/pull/11850)) — thanks @HouMinXi +- **feat(models):** reactive discovery sync after Antigravity model-not-found 404 ([#11888](https://github.com/diegosouzapw/OmniRoute/pull/11888)) — thanks @MumuTW +- **feat(models):** auto-aliases and token limits from synced Antigravity discovery ([#11889](https://github.com/diegosouzapw/OmniRoute/pull/11889)) — thanks @MumuTW +- **feat(settings):** raise sticky round-robin limit caps to 1000 ([#12015](https://github.com/diegosouzapw/OmniRoute/pull/12015)) — thanks @adivekar-utexas +- **feat(resilience):** operator-configurable global credential health check interval ([#12043](https://github.com/diegosouzapw/OmniRoute/pull/12043)) — thanks @adivekar-utexas +- **feat(quota):** use official OpenCode Go usage API ([#12124](https://github.com/diegosouzapw/OmniRoute/pull/12124)) — thanks @ddarkr +- **feat(resilience):** default the credential health check sweep to 60 minutes ([#12138](https://github.com/diegosouzapw/OmniRoute/pull/12138)) — thanks @adivekar-utexas +- **feat(quality):** new-code mode for the complexity and dead-code ratchets (Clean as You Code) ([#12142](https://github.com/diegosouzapw/OmniRoute/pull/12142)) +- **feat(dashboard):** orchestration canvas — unified model + snapshot hook (part 1/2) ([#12156](https://github.com/diegosouzapw/OmniRoute/pull/12156)) +- **feat(usage):** add Kilo Code balance and Kilo Pass quotas ([#12178](https://github.com/diegosouzapw/OmniRoute/pull/12178)) — thanks @killer30001000 +- **feat(providers):** refresh vendored ChatGPT Web connector to v4.0.7 ([#12181](https://github.com/diegosouzapw/OmniRoute/pull/12181)) — thanks @backryun +- **feat(providers):** manual "Clear cooldown" action in the cooling panel ([#12224](https://github.com/diegosouzapw/OmniRoute/pull/12224)) — thanks @patrykkopycinski +- **feat(providers):** restore ChatGPT Web via clean-room browser transport ([#12239](https://github.com/diegosouzapw/OmniRoute/pull/12239)) — thanks @backryun +- **feat(sse):** wire the PROVIDER_PROFILES window gate into the global provider cooldown ([#12247](https://github.com/diegosouzapw/OmniRoute/pull/12247)) +- **feat(providers):** modernize CLOVA Studio chat and embeddings ([#12277](https://github.com/diegosouzapw/OmniRoute/pull/12277)) — thanks @backryun +- **feat(browser):** adopt Obscura as primary headless browser engine with Chromium fallback ([#12286](https://github.com/diegosouzapw/OmniRoute/pull/12286)) — thanks @opensource-elearning +- **feat(sse):** full language parity for output styles (es/de/fr/it/ru/zh + autoDetect) ([#12289](https://github.com/diegosouzapw/OmniRoute/pull/12289)) +- **feat(quality):** complete test:scoped — --full map rebuild, stdin selection, CI loader parity (#8084 D1) ([#12353](https://github.com/diegosouzapw/OmniRoute/pull/12353)) +- **feat(providers):** refresh Fable, Cursor, and Devin catalogs ([#12367](https://github.com/diegosouzapw/OmniRoute/pull/12367)) — thanks @backryun +- **feat(video):** redact transcript text from logs and durable memory (#12150 P1) ([#12427](https://github.com/diegosouzapw/OmniRoute/pull/12427)) +- **feat(dashboard):** parent-link, genuine-continuation badge, and modal perf fixes ([#12448](https://github.com/diegosouzapw/OmniRoute/pull/12448)) — thanks @hartmark +- **feat(dashboard):** adaptive context-budget dial on compression panel ([#12488](https://github.com/diegosouzapw/OmniRoute/pull/12488)) — thanks @HouMinXi +- **feat(providers):** refresh Z.ai Web models and browser transport ([#12524](https://github.com/diegosouzapw/OmniRoute/pull/12524)) — thanks @backryun +- **feat(video):** redact raw client-snapshot transcript fields in the detailed log (#12150 P2) ([#12528](https://github.com/diegosouzapw/OmniRoute/pull/12528)) +- **feat(providers):** refresh NVIDIA hosted models ([#12538](https://github.com/diegosouzapw/OmniRoute/pull/12538)) — thanks @backryun +- **feat(quota):** Moonshot Open Platform balance and TPD lock for custom nodes ([#12590](https://github.com/diegosouzapw/OmniRoute/pull/12590)) — thanks @HouMinXi +- **feat(video):** redact transcript in the in-memory pending-request snapshot (#12430 item 6) ([#12596](https://github.com/diegosouzapw/OmniRoute/pull/12596)) +- **feat(dashboard):** link a log entry's Conversation Context to its owning conversation ([#12646](https://github.com/diegosouzapw/OmniRoute/pull/12646)) — thanks @hartmark +- **feat(catalog):** eligibility-gated free-tier bucket ([#12669](https://github.com/diegosouzapw/OmniRoute/pull/12669)) +- **feat(db):** fail-closed previous_response_id continuation for redacted video turns (#12150 P2b) ([#12707](https://github.com/diegosouzapw/OmniRoute/pull/12707)) +- **feat(gamification): pay the documented `streak_bonus` and `badge_unlock` XP rewards.** `XP_REWARDS` listed both rewards but the award pipeline never paid them: the private reward table in `events.ts` omitted them, `updateStreak()` did not report when a streak extended, and badge unlocks carried no XP. Every request that extends a daily streak now pays `streak_bonus × streak length` once per UTC day (guarded by a same-day `xp_audit_log` check), and every badge unlocked through the pipeline pays `badge_unlock` once per badge (guarded by the `user_badges` primary key; `unlockBadge()` now reports whether it inserted). Bonus XP flows through the same `addXp` + level sync + global/weekly/monthly leaderboard path as action XP, so level-ups and rankings include it. The Radar supporter recognition unlock stays XP-free. (#12522 — thanks @pacocartones) +- **feat(providers):** advertise a `free-tier` capability in the provider plugin manifest for every provider with documented free models, so sidecars and dashboards can filter free-capable providers without reading the quota catalog ([#12786](https://github.com/diegosouzapw/OmniRoute/pull/12786)) — thanks @maxmad64bis +- **feat(providers):** Added EURouter as an OpenAI-compatible API-key gateway (`https://api.eurouter.ai/v1`), with live model discovery via `passthroughModels`. Its copy states that models are served by third-party upstreams listed per model, so an EU-based router is not read as EU data residency for inference. ([#13025](https://github.com/diegosouzapw/OmniRoute/pull/13025)) — thanks @ntdatt812 +- **feat(providers):** Added GreenPT as an OpenAI-compatible API-key provider (`https://api.greenpt.ai/v1`), with live model discovery via `passthroughModels`. No free-inference badge: the published docs describe a free API subscription billed per token, not a free tier. ([#13024](https://github.com/diegosouzapw/OmniRoute/pull/13024)) — thanks @ntdatt812 +- **feat(i18n):** 9 new locales — Greek (`el`), Croatian (`hr`), Serbian (`sr`), Lithuanian (`lt`), Estonian (`et`), Latvian (`lv`), Slovenian (`sl`), Maltese (`mt`), Irish (`ga`) — across the dashboard, docs mirrors, CLI, README and the site; OmniRoute now ships every official EU language (51 locales). (#13044) +- **feat(i18n):** 8 new locales — Kannada (`kn`), Malayalam (`ml`), Odia (`or`), Punjabi (`pa`), Nepali (`ne`), Sinhala (`si`), Burmese (`my`), Khmer (`km`) — across the dashboard, docs mirrors, CLI, README and the site (59 locales). The translator now restores the ICU literal escape around angle placeholders and splits oversized docs sections before translating. (#0000) ([#13660](https://github.com/diegosouzapw/OmniRoute/pull/13660)) +- feat(providers): **list Agnes 3.0 Flash as the current free chat model, drop retired 1.5 Flash, add Image 2.0/2.5 Flash plus Video 2.5/2.5 Flash, and discover the live `/v1/models` catalog (including the CN host `api.agnes-ai.cn`).** `agnes-1.5-flash` now forwards to `agnes-3.0-flash`. Video 2.5 polls `GET /v1/videos/{id}` (not the V2.0 `/agnesapi` contract). Live `/v1/models` (2026-09-09) no longer serves 1.5; the wiki marks it deprecated. 3.0 Flash is 512K context / 65,536 max output, same window as 2.5. CN-region keys use the existing per-connection base-URL field, default stays `apihub.agnes-ai.com`. ([#13120](https://github.com/diegosouzapw/OmniRoute/pull/13120)) — thanks @HouMinXi +- **feat(sse):** Codex and OpenAI catalogs list GPT-6 Astra with effort aliases (`-low` through `-ultra`); Codex CLI identity pins `@openai/codex@0.153.4` in lockstep with the image ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **feat(dashboard):** the orchestration History tab gained a "Compare runs" mode — toggling it turns each grid cell into a 2-item selection queue (a 3rd click drops the oldest pick), and picking two cells opens a side-by-side comparison panel instead of the usual detail drawer. The panel fetches both runs' detail the same way the drawer does (falling back to persisted history once a run leaves the live TTL window) and shows, per side: identity/source/state, start time, a signed `right - left` delta for duration/cost/event count, the event timeline aligned by index, and any memory hits. One side's fetch failing never blocks the other, and a delta is only ever computed when both sides have a finite value — otherwise it renders "—", never `NaN`. Comparing two runs from different sources or skills still works; a banner marks the deltas as informational rather than hiding them, since the two runs aren't a strict apples-to-apples pair. ([#12677](https://github.com/diegosouzapw/OmniRoute/pull/12677)) +- **feat(dashboard):** badge a conversation that never reached a clean stop ([#12717](https://github.com/diegosouzapw/OmniRoute/pull/12717)) — thanks @hartmark +- **feat(release):** reconcile-changelog tool + version-anchored fragment aggregation ([#12987](https://github.com/diegosouzapw/OmniRoute/pull/12987)) +- **feat(mcp):** carry context_length through MCP list_models_catalog projection (#12776) ([#13406](https://github.com/diegosouzapw/OmniRoute/pull/13406)) — thanks @KooshaPari +- **feat(cli):** make startup readiness budget configurable (#13369) ([#13433](https://github.com/diegosouzapw/OmniRoute/pull/13433)) — thanks @KooshaPari + ### 🐛 Bug Fixes +- **security(streaming):** sanitize generic mid-stream error messages before emitting OpenAI, + Responses, or Claude SSE failure frames and before diagnostic logging, while preserving raw + failures for internal classification and keeping client disconnects out of provider failure state. ([#12457](https://github.com/diegosouzapw/OmniRoute/pull/12457)) +- **fix(claude):** `blockExtraUsage: false` no longer lets 5h quota preflight skip the account; extra usage is billed after the session bar is gone, so the request must reach Anthropic ([#12803](https://github.com/diegosouzapw/OmniRoute/pull/12803)) — thanks @HouMinXi +- Sanitize HuggingChat conversation-creation and message-send transport failures before they reach client error bodies or provider logs. ([#12467](https://github.com/diegosouzapw/OmniRoute/pull/12467)) +- **fix(dashboard):** Keep local and theme-aware provider SVG icons at a definite layout size so Chromium does not collapse them to 0×0 after the v3.8.50 image-rendering change ([#12054](https://github.com/diegosouzapw/OmniRoute/pull/12054)) — thanks @ponkcore +- **fix(security):** Sanitize provider and runtime failures before public API, SSE and MCP responses and before persistent request, proxy and usage logs, preventing credentials, stack traces and host filesystem paths from crossing those boundaries while preserving stable error codes and useful diagnostics. ([#12506](https://github.com/diegosouzapw/OmniRoute/pull/12506)) +- **fix(cli):** `omniroute tunnel create` no longer crashes with `Cannot read properties of undefined (reading optsWithGlobals)` — removed the duplicate positional argument that caused Commander.js to misalign the action callback parameters ([#12295](https://github.com/diegosouzapw/OmniRoute/issues/12295)) ([#12368](https://github.com/diegosouzapw/OmniRoute/pull/12368)) — thanks @gonisulaimann +- Stop advertising Gemini Live-only models as supported audio endpoints until OmniRoute proxies the bidirectional Live protocol. ([#11710](https://github.com/diegosouzapw/OmniRoute/pull/11710)) — thanks @RaviTharuma +- **fix(kie):** reroute `flux/kontext` off the KIE Market `createTask` flow — it is catalogued with `isMarket: true` but has no Market catalog page, so KIE rejected it with "model name not supported"; it now hits the dedicated `POST /api/v1/flux/kontext/generate` / `GET /api/v1/flux/kontext/record-info` endpoints instead (#11296). ([#11985](https://github.com/diegosouzapw/OmniRoute/pull/11985)) — thanks @hartmark +- **fix(kie):** correct 12 more KIE Market catalog ids that were sent to `createTask` unchanged but diverge from KIE's documented upstream `model` values — GPT Image 2 T2I/I2I (drops the `gpt/` prefix), GPT Image 1.5 T2I/I2I (`gpt-image/` namespace), Seedream 5.0 Lite T2I/I2I (drops the `.0`), all 4 Flux 2 variants (`flux-2/` namespace, generic variant renamed `flex`), and Wan 2.7 Image / Image Pro (dash instead of dot) — each verified individually against the literal example request published on docs.kie.ai. `#11326`'s "everything else already matches" claim was wrong a second time (#11296); `z-image/4.0-*`/`z-image/4.5-*` and `flux/kontext` remain open, documented as unresolved in `KIE_MARKET_UPSTREAM_MODEL_IDS`'s comment pending further verification. ([#11667](https://github.com/diegosouzapw/OmniRoute/pull/11667)) +- **fix(dashboard):** `useApiKeySave.handleSaveApiKey` no longer forces a full upstream `/models` catalog sync on every non-curated provider connection save — callers can now pass `skipModelSync: true` to opt out, so a workflow that only wants to add one manual model no longer floods the provider's available-models list with hundreds/thousands of synced entries. The flag is a client-side intent signal only and is stripped before the connection payload is POSTed to `/api/providers`; default behavior (full sync on save) is unchanged when the flag is omitted (#11324) ([#11664](https://github.com/diegosouzapw/OmniRoute/pull/11664)) +- **Provider connections:** keep `tokenExpiresAt` when a connection is created. The create-path allowlist omitted it, so every insert stored NULL and the dashboard token badge could read a fresh connection as expired until its first background refresh ([#11368](https://github.com/diegosouzapw/OmniRoute/pull/11368)). — thanks @ntdatt812 +- **fix(combos):** the combo builder's precision-select, global-model-search, and manual-entry flows now serialize a model step's `model` string using the provider's already-computed routing-alias prefix (e.g. `oc/`) instead of rebuilding it from the raw canonical `providerId`, fixing the no-auth "OpenCode Free" provider (`opencode`) being routed to the unrelated paid "OpenCode Zen" provider (`opencode-zen`) because `opencode` doubles as a manual routing-prefix override ([#11433](https://github.com/diegosouzapw/OmniRoute/issues/11433)). ([#11670](https://github.com/diegosouzapw/OmniRoute/pull/11670)) +- **fix(radar):** the catalog feed cache now keeps `generatedAt`, the date the feed's data was built, next to `fetchedAt`, the date this install downloaded it (#11435). The feed schema requires that date and the sync path validates it, but the cache dropped it — so a feed fetched minutes ago and one carrying weeks-old figures looked identical to everything downstream, including the dashboard's "Last fetched" line. `getRadarCatalog().meta` and `GET /api/radar/status` now report both dates, the latter as its own field rather than folded into `version` — and omitted entirely for the offers and intel caches, which keep no build date, where a `null` would read as "unknown" rather than "never stored". The dashboard still shows only the fetch time; surfacing the build date there needs a new translated label and is left to a follow-up. Rows cached before migration 163 read back as `null`: unknown stays unknown instead of borrowing the fetch time. The referrals cache has persisted the same date since migration 142. — thanks @maxmad64bis +- **fix(cli):** postinstall no longer fills `JWT_SECRET` and `API_KEY_SECRET` in the installed package's `.env` (#11436). `.env.example` ships both blank on purpose: the server restores them from its durable store, or generates and persists them there, in `ensureSecrets()`. Pre-filling them defeated that — the file lives inside the package directory, so `npm i -g` replaced it and postinstall wrote _different_ values, while `ensureSecrets()` (which only acts on an empty variable) never got to restore the real ones. Both secrets rotated silently on every update, invalidating dashboard sessions and API-key CRCs. `STORAGE_ENCRYPTION_KEY` left the same list for the same reason in #1622; its comment pointed at a function that no longer exists and now names the real provisioning path. — thanks @maxmad64bis +- **fix(cli):** the CLI now says when a loaded `.env` lives inside the installed package directory (#11437). It already announces every env file it reads, without distinguishing the ones that survive an update from the one that does not: `npm i -g` replaces the package directory wholesale, so values set there are gone at the next update, silently. The warning names the durable path to move them to, and fires only when that file actually supplied a value — a file entirely shadowed by a durable one supplied nothing. A development checkout stays silent: there the same path is stable and documented in `SETUP_GUIDE.md`. — thanks @maxmad64bis +- **fix(free-models):** the shared `isFreeModel()` predicate no longer reports catalog entries marked `freeType: "discontinued"` as free, so `hidePaidModels` can't route to Pollinations' seven premium models that now require a paid key ([#11441](https://github.com/diegosouzapw/OmniRoute/pull/11441)) — thanks @maxmad64bis +- fix(ui): let AnySearch use the normal provider-icon fallback when LobeHub has no matching icon (#11449) +- Fixed the v3.8.50 Costs and Analytics dashboards so flat-rate Claude Code usage can be shown as an explicitly requested token-price estimate without changing default billed-cost semantics. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed archived usage retention so each request is priced individually instead of pricing a day's summed tokens once, which understated archived cost whenever a day mixed cache-heavy and ordinary requests. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed the Costs dashboard so it discloses when displayed figures include flat-rate token-price estimates instead of labelling them as billed spend, using the flag the analytics API already returns; the month-end projection and the CSV/JSON exports carry the same marker, and billed-cost mode is unchanged. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- **fix(combo):** attach the same combo-diagnostics trace (`poolSize`/`attemptOrder`/`excluded`/`terminalReason`, plus `x-omniroute-combo-*` headers) to the round-robin strategy's and the nested pipeline/fusion runtime-unit loop's "Maximum combo retry limit reached" 503 that the priority-strategy path already attaches for the identical terminal condition — previously those two paths returned a bare, contextless 503 ([#11462](https://github.com/diegosouzapw/OmniRoute/issues/11462)). ([#11665](https://github.com/diegosouzapw/OmniRoute/pull/11665)) +- **fix(bun):** use native `bun:sqlite` in `bootstrap-env` and `sync-env` to avoid loading `better-sqlite3` N-API addon during Bun startup ([#11468](https://github.com/diegosouzapw/OmniRoute/pull/11468)) — thanks @TheDemonTuan +- **fix(docker-bun):** make `Dockerfile.bun` install reliable with pre-install script helpers and native `bun:sqlite` smoke check ([#11470](https://github.com/diegosouzapw/OmniRoute/pull/11470)) — thanks @TheDemonTuan +- **fix(docker-bun):** remove every vendored `better-sqlite3` native addon from the Bun runtime image so startup cannot abort before the `bun:sqlite` fallback ([#11482](https://github.com/diegosouzapw/OmniRoute/pull/11482)) — thanks @TheDemonTuan +- **fix(autoCombo):** effort/alias model variants (`gpt-5.6-sol-xhigh`, `gpt-5.6`, cursor's `claude-4.6-opus-high`) inherit their base model's task fitness instead of falling to the wildcard 0.5 ([#11492](https://github.com/diegosouzapw/OmniRoute/pull/11492)) — thanks @MumuTW +- fix(db): rate-limit repeated Arena ELO leaderboard fetch-failure warnings instead of logging one per sync attempt (#11500) ([#11989](https://github.com/diegosouzapw/OmniRoute/pull/11989)) — thanks @hartmark +- **fix(db):** dedupe the raw `[Encryption] Decryption failed...` log line emitted by the lazy-decrypt views (`createLazyRowProxy`/`createLazyConnectionView`), which power `getProviderConnections()` and were re-triggering that line on every CredentialHealth/model-sync cycle for the same corrupt or stale-key credential — a fresh Proxy over a fresh row on every cycle meant the per-proxy memoization never suppressed it, unlike the dedup `decryptConnectionFields()` already had since [#9927](https://github.com/diegosouzapw/OmniRoute/issues/9927). Now shares that dedupe tracking so the line logs at most once per credential ([#11500](https://github.com/diegosouzapw/OmniRoute/issues/11500)). ([#11678](https://github.com/diegosouzapw/OmniRoute/pull/11678)) +- **fix(intelligence):** Arena ELO sync stores a synthesized base-model row for every effort/harness variant (`claude-opus-5-max` → `claude-opus-5`, `gpt-5.6-sol-xhigh (codex-harness)` → `gpt-5.6-sol`), so bare model requests reach the synced intelligence layer instead of the static fallback table; the hand-maintained `MODEL_ALIAS_MAP` — which collapsed generations (`gpt-5.5` → `gpt-5`) — is gone ([#11506](https://github.com/diegosouzapw/OmniRoute/pull/11506)) — thanks @MumuTW +- **fix(autoCombo,sse):** catalog hygiene — the static task-fitness table no longer ranks retired models above live ones (dead rows dropped, patterns matched on segment boundaries), the 7 deprecation aliases that forwarded to retired or non-existent models now point at the vendors' published replacements, an alias is no longer rewritten when the serving provider offers the id as-is, and a new `check:model-lifecycle` gate diffs both tables against a vendor lifecycle snapshot ([#11507](https://github.com/diegosouzapw/OmniRoute/pull/11507)) — thanks @MumuTW +- **fix(sse):** stop dropping resolved `thoughtSignature` values on parallel (multi tool-call) turns sent to Gemini 3.x — the claude→gemini and openai→gemini translators previously kept the signature only on the *first* function call of a message, causing Gemini to reject subsequent calls in the same turn with HTTP 400 "Function call is missing a thought_signature"; each function call now keeps its own resolved signature ([#11510](https://github.com/diegosouzapw/OmniRoute/issues/11510)). ([#11669](https://github.com/diegosouzapw/OmniRoute/pull/11669)) +- Treat Docker publishing from a non-default release branch as an expected skip instead of a failed workflow, while preserving the guard that prevents it from overwriting the `next` channel. ([#11527](https://github.com/diegosouzapw/OmniRoute/pull/11527)) — thanks @pacocartones +- fix(search): prefer credentialed providers over duckduckgo-free fallback (#11524) — the fallback-only loop ran before the credentialed-providers loop in `executeWebSearch`, making configured providers unreachable when `duckduckgo-free` was available. ([#11565](https://github.com/diegosouzapw/OmniRoute/pull/11565)) — thanks @vsd2807 +- **fix(sse):** cap the upstream headers-wait phase for STREAMING requests to a client-realistic ceiling (110s, under Codex's own ~120s hard client-abort window) instead of the flat 10-minute `FETCH_TIMEOUT_MS` default — that default was 5x longer than the body-phase readiness watchdog's own adaptive bound, so a request whose upstream never returned any response at all (not even headers, e.g. a stalled NVIDIA target behind a tool-heavy Responses→Chat translation) kept the client connection alive on keepalives only, guaranteeing the client's own patience ran out first with an opaque 499 instead of OmniRoute detecting and failing the stall fast. Non-streaming requests are unaffected — they keep the existing flat default (`open-sse/utils/fetchStartTimeoutPolicy.ts`) (#11526) ([#11668](https://github.com/diegosouzapw/OmniRoute/pull/11668)) +- **fix(ci):** protocol-clients E2E harness boots the peer-stamped custom server (`run-next.mjs dev`) instead of the bare `next` CLI, so `LOCAL_ONLY` locality resolves from the real TCP peer stamp and `/api/mcp/audit` answers 200 (open bootstrap preserved via an `OMNIROUTE_E2E_BOOTSTRAP_MODE=open` credential clear in `run-next.mjs`, pinned to loopback `HOST`) instead of a deterministic 403 (#11535) ([#11549](https://github.com/diegosouzapw/OmniRoute/pull/11549)) — thanks @jonlwheat2-gif +- **fix(docs):** the Auto-Combo engine is described with the number of factors it actually declares. Four different counts were in circulation — 6, 9, 13 and 14 across `AGENTS.md`, four guides and six code comments — while `DEFAULT_WEIGHTS` declares 15. `check:docs-counts` now reads that number from the source, so adding a factor without updating the prose fails CI ([#11545](https://github.com/diegosouzapw/OmniRoute/pull/11545)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **fix(quota-share):** in-flight leases are per request, so an aborted request's slot ages out instead of being kept alive by later traffic on the same connection ([#11547](https://github.com/diegosouzapw/OmniRoute/pull/11547)) — thanks @abhisheksharma2411 +- The free-tier summary route now serves the refreshed Radar catalog when the feed is active — the same numbers the dashboard shows — and states which catalog answered plus its real build date, instead of always reporting release-frozen figures with a stale curation date. The supporter-key live feed stays reserved to authenticated callers of the instance. ([#11550](https://github.com/diegosouzapw/OmniRoute/pull/11550)) — thanks @maxmad64bis +- **sse:** fix LiveWS/embed-WS servers crashing at startup under the Node/tsx runtime — `liveServer.ts`, `embedWsProxy.ts` and `apiBridgeServer.ts` imported `@/shared/utils/httpClientAbortGuard` without the `.mjs` extension, so the client-abort crash guard added by [#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556) was unreachable and every dependent test failed with `ERR_MODULE_NOT_FOUND` ([#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556)). ([#11671](https://github.com/diegosouzapw/OmniRoute/pull/11671)) — thanks @jonlwheat2-gif / @yxyxy +- fix(chat): shed any of the matched provider-node's routing identifiers (public prefix or internal id) before model lookup, so composites like `//` no longer reach upstream verbatim (#11557) — thanks @maxmad64bis +- **fix(cache):** a semantic-cache entry now expires on its TTL instead of surviving until the next UTC midnight, and `dbEntries` stops counting expired rows ([#11573](https://github.com/diegosouzapw/OmniRoute/pull/11573)) — thanks @ntdatt812 +- **fix(api):** `/v1/models` schedules its stale-while-revalidate rebuild through Next's `after()` again, so a stale catalog reaches the client before the rebuild blocks the event loop ([#11574](https://github.com/diegosouzapw/OmniRoute/pull/11574)) — thanks @ntdatt812 +- **fix(check):** the `known-symbols` executor conformance gate awaits the now-async `getExecutor()`, so it stops reporting all 142 registered aliases as dead symbols and can detect a lazy import that fails ([#11582](https://github.com/diegosouzapw/OmniRoute/pull/11582)) — thanks @ntdatt812 +- **fix(pack):** register `bin/cli/utils/volatileEnvPath.mjs` in `PACK_ARTIFACT_REQUIRED_PATHS`, so the prune can no longer silently drop a module the published CLI imports on every boot ([#11588](https://github.com/diegosouzapw/OmniRoute/pull/11588)) — thanks @ntdatt812 +- **i18n(vi):** translate the five reliability-column strings on the free-provider rankings page, which rendered as `__MISSING__:` markers in the Vietnamese UI ([#11589](https://github.com/diegosouzapw/OmniRoute/pull/11589)) — thanks @ntdatt812 +- **resilience:** restore the expired-connection retry-budget probe in the token-health sweep — the `!isGitHubAccessTokenOnlyConnection` carve-out reintroduced by #11608 contradicted the boundary pinned by #11592, so a GitHub connection parked at `expired` with retry budget remaining was never probed and could never self-heal ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)). ([#11672](https://github.com/diegosouzapw/OmniRoute/pull/11672)) — thanks @jonlwheat2-gif / @ntdatt812 +- **fix(dashboard):** Provider Stats now displays combo success rates on their native 0–100 percentage scale instead of multiplying them twice ([#11595](https://github.com/diegosouzapw/OmniRoute/pull/11595)) — thanks @pacocartones +- **fix(providers):** route DuckDuckGo Web Chat through the verified `duck.ai` endpoint, refresh its free model catalog, and avoid forwarding unsolved anti-abuse challenges ([#11597](https://github.com/diegosouzapw/OmniRoute/pull/11597)) — thanks @solstxce +- **fix(ui):** Console log Refresh and Copy controls now expose localized accessible names, keep copy actions visible on keyboard focus, and announce copy completion safely ([#11599](https://github.com/diegosouzapw/OmniRoute/pull/11599)) — thanks @pacocartones +- Dropped the stale-`.eslintcache` `restore-keys` fallback from both "Restore ESLint file cache" steps in `ci.yml`, so the blocking `Lint` job can no longer be served per-file verdicts computed under a different lint config, suppressions file or lockfile. `quality.yml` had already dropped it in #11963; `ci.yml` — the workflow that actually gates PRs — had not (#11600). ([#11996](https://github.com/diegosouzapw/OmniRoute/pull/11996)) +- fix(ci): pass `--pass-on-unpruned-suppressions` in `run-eslint-json.mjs` so the CI Lint job no longer fails when a suppression is merely orphaned by a genuine fix, mirroring the identical fix already in `validate-release-green.mjs` (#11600) ([#11983](https://github.com/diegosouzapw/OmniRoute/pull/11983)) — thanks @hartmark +- fix(handoff): enforce provider allowlist for universal handoff (#11602) — universal handoff now skips summarization when the selected summary model's provider is not included in the configured provider allowlist. ([#11619](https://github.com/diegosouzapw/OmniRoute/pull/11619)) — thanks @vsd2807 +- **fix(dashboard):** Show a stable error state when Search Analytics returns an HTTP error or malformed data ([#11603](https://github.com/diegosouzapw/OmniRoute/pull/11603)) — thanks @pacocartones +- **test(gamification):** pin the aggregate profile level to the XP-derived semantics of #11604 — `getAggregateXp()` now derives `currentLevel` from the summed XP (`calculateLevel(sum)`), not `MAX(stored current_level)`, and the #3484 fixture levels are aligned with the XP curve ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)). ([#11676](https://github.com/diegosouzapw/OmniRoute/pull/11676)) — thanks @jonlwheat2-gif / @pacocartones +- **fix(dashboard):** Keep the Profile level and progress aligned with aggregate XP, including bounded handling for invalid totals ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)) — thanks @pacocartones +- **fix(dashboard):** Prevent locked hidden badges from revealing their icon or opening private badge details before they are earned ([#11605](https://github.com/diegosouzapw/OmniRoute/pull/11605)) — thanks @pacocartones +- **fix(dashboard):** Restore keyboard focus after shared modals close and cancel delayed autofocus during cleanup ([#11607](https://github.com/diegosouzapw/OmniRoute/pull/11607)) — thanks @pacocartones +- **fix(dashboard):** Enable Enter and Space activation for clickable data-table rows without hijacking nested controls ([#11610](https://github.com/diegosouzapw/OmniRoute/pull/11610)) — thanks @pacocartones +- **fix(autoCombo,sse):** vendor-retired catalog ids are dropped from the auto-combo candidate pool and no longer win on leftover `arena_elo` / `user_override` rows; `getModelLifecycleDecision` consults `model-lifecycle.json` (prefix-stripped) so aggregator traffic is not `untracked` for ids the snapshot already knows ([#11625](https://github.com/diegosouzapw/OmniRoute/issues/11625)) ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)) — thanks @MumuTW +- **test(chatcore):** move Codex/Claude combo fixtures off models the lifecycle guard now rejects — `gpt-5.1-codex`/`gpt-5-codex` are vendor-retired (snapshot, #11626) and `claude-3-5-sonnet-20241022` is shut down, so native-passthrough and combo-fallback tests switched to `gpt-5.6-sol` and `claude-sonnet-4.6` ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)). ([#11675](https://github.com/diegosouzapw/OmniRoute/pull/11675)) — thanks @jonlwheat2-gif / @MumuTW +- fix(opencode-plugin): stop a free-tier budget that rounds up across a magnitude from rendering as `1000K`/`1000M` in the model picker — `fmtTokens` chose its unit from the raw token count and then rounded with `toFixed(1)`, so 999,950–999,999 printed as `1000K` rather than `1M` and just under a billion printed as `1000M` rather than `1B` ([#11684](https://github.com/diegosouzapw/OmniRoute/pull/11684)) — thanks @abhisheksharma2411 +- Cap prose-derived Antigravity quota resets at the configured model cooldown maximum while preserving authoritative Retry-After headers and Google RetryInfo hints. (#11695) Thanks @pacocartones. ([#11823](https://github.com/diegosouzapw/OmniRoute/pull/11823)) +- Compare `_rebaseline_*` notes structurally in the ratchet-bank verifier so an object-valued note no longer reports itself as rewritten on an unmodified tree — the nightly `bank-ratchet-shrinks` lane had been aborting on every run since 2026-08-11, leaving 41,672 lines of earned file-size shrinks unbanked (#11702). ([#11892](https://github.com/diegosouzapw/OmniRoute/pull/11892)) — thanks @MumuTW +- **fix(build):** `prepublish.ts` bundles the ChatGPT Web (Codex) MCP bridge through `runBuildTool()` instead of spawning `npx.cmd` raw, fixing the build crash on Node ≥ 20/Windows where `.cmd` shims cannot be spawned without a shell (EINVAL) ([#11704](https://github.com/diegosouzapw/OmniRoute/issues/11704)) ([#11706](https://github.com/diegosouzapw/OmniRoute/pull/11706)) — thanks @jonlwheat2-gif +- fix(codex): keep `parallel_tool_calls:false` on the translated Codex Responses Lite path (#11707) ([#11984](https://github.com/diegosouzapw/OmniRoute/pull/11984)) — thanks @hartmark +- **fix(tests):** the new Gemini Web Images retirement test (#11708) called the now-async `getExecutor()` synchronously, throwing `TypeError: getExecutor(...).getProvider is not a function` instead of asserting Gemini Web chat stays available. Awaited to match the R0.3 executor-registry refactor (#11220). ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(search):** Allow REST and MCP web-fetch requests to select the advertised `anysearch-search` provider explicitly ([#11716](https://github.com/diegosouzapw/OmniRoute/pull/11716)) — thanks @pacocartones +- **fix(providers):** Keep overnight peak-hour protection active after midnight according to the window's configured start day ([#11718](https://github.com/diegosouzapw/OmniRoute/pull/11718)) — thanks @pacocartones +- **fix(providers):** correct all five g4f.space gateway descriptions to document proof-of-work credits or member API keys, variable limits, and the remote prompt boundary instead of promising free keyless access at five requests per minute ([#11738](https://github.com/diegosouzapw/OmniRoute/pull/11738)). +- **fix(db):** `model_capabilities` is created by a migration instead of lazily on the first models.dev sync, so a clean install and an upgraded install converge on the same schema regardless of which features have run ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(ci):** `check:install-upgrade` now fails on an `npm` install truncated by ENOSPC (npm reports it as a warning and still exits 0), authenticates its health probe so the version assertion works against the hardened health payload, frees the clean-install tree before the upgrade phase, and no longer reports a schema divergence computed from a boot that never served ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(routing):** harden session-affinity key derivation against oversized explicit IDs and empty structural-payload collisions, while recognizing Gemini and common text request shapes ([#11746](https://github.com/diegosouzapw/OmniRoute/pull/11746)) — thanks @MumuTW +- **fix(packages/browser-pool):** regenerate `package-lock.json` so the `packages/browser-pool` workspace's locked `playwright`/`@types/node` (and transitives) match its `package.json` specs, fixing cache-only/offline installs (`npm ci --offline`, Nix `buildNpmPackage`) that previously failed with `ENOTCACHED` ([#11747](https://github.com/diegosouzapw/OmniRoute/issues/11747)) — thanks @benjaminkitt ([#11784](https://github.com/diegosouzapw/OmniRoute/pull/11784)) — thanks @geekyNads +- **fix(assets):** Stop distributing 79 provider and CLI logos without verified source/license provenance, keep every provider/tool available through the internal generic icon, and prevent those IDs from falling through to an external icon CDN. ([#11750](https://github.com/diegosouzapw/OmniRoute/pull/11750)) +- **fix(providers):** retire the common `chatgpt-web` / `cgpt-web` provider because its pre-key and proof-of-work provenance could not be cleared ([#11754](https://github.com/diegosouzapw/OmniRoute/pull/11754)) — explicit chat and image requests, slash-prefixed models, persisted aliases, proxy overrides, and combo targets now fail closed with a sanitized HTTP `410` / `PROVIDER_RETIRED` before upstream dispatch. Migration 163 tombstones matching connections and invalidates their active leases without deleting history or widening API-key allowlists. The independent `chatgpt-web-codex` / `cgpt-codex` provider and its MIT-noticed vendored implementation remain available. +- **fix(usage):** quota and usage refresh no longer 409 when an exclusive lease reserves the connection ([#11758](https://github.com/diegosouzapw/OmniRoute/pull/11758)) — thanks @TheDemonTuan +- Keep the embedding registry's vector width and `embedding` type on models when a synced model exists for the same id, so `/v1/models` no longer reports registry-described embedding models widthless or untyped (#11761) — thanks @rahilmavani +- Correct `google/gemini-embedding-001` on the OpenRouter route to 3072 dimensions, the width it returns when `dimensions` is not sent (#11761) — thanks @rahilmavani +- **fix(vision-bridge):** Restrict automatic vision routing and fallbacks to models available in authoritative live provider catalogs, including aliases and effort variants ([#11767](https://github.com/diegosouzapw/OmniRoute/issues/11767)) — thanks @pacocartones ([#11813](https://github.com/diegosouzapw/OmniRoute/pull/11813)) +- **fix(providers):** reclassify Cerebras as a one-time $5 signup credit (payment method required, 30-day validity), not a recurring no-card 1M tokens/day trial ([#11773](https://github.com/diegosouzapw/OmniRoute/issues/11773)) ([#12591](https://github.com/diegosouzapw/OmniRoute/pull/12591)) — thanks @HouMinXi +- fix(cli): stop prepublish from re-rebuilding the already-built ESM-only opencode-plugin dist (#11787) ([#11990](https://github.com/diegosouzapw/OmniRoute/pull/11990)) — thanks @hartmark +- **fix(db):** Fresh installs no longer log a non-fatal `no such table: compression_run_telemetry` warning when retention cleanup runs before the lazily-created telemetry table exists ([#11802](https://github.com/diegosouzapw/OmniRoute/pull/11802)) — thanks @RaviTharuma +- fix(sse): set X-OmniRoute-Selected-Connection-Id on successful combo dispatches so downstream consumers stop falling back to an empty connection id (#11810) ([#11986](https://github.com/diegosouzapw/OmniRoute/pull/11986)) — thanks @hartmark +- **fix(services):** embedded CLIProxyAPI lifecycle checks now use public `/healthz`, while model discovery uses the configured dedicated data-plane API key instead of the management password ([#11811](https://github.com/diegosouzapw/OmniRoute/pull/11811)) — thanks @RaviTharuma +- Fix persisted model steps returning an error in the playground route simulator, and warn when structural steps cannot be simulated. ([#11862](https://github.com/diegosouzapw/OmniRoute/pull/11862)) — thanks @pacocartones +- **fix(providers):** Antigravity's dynamic mitmAlias table no longer routes `gemini-3.7-flash-{high,medium,low}` to a literal tier-suffixed upstream id just because one connected account's own discovery listed it directly — those display ids always resolve through the safe `gemini-3.7-flash-tiered` static alias, so one account's Google-provisioned access no longer 404s every sibling account of the provider ([#11824](https://github.com/diegosouzapw/OmniRoute/issues/11824), [#11651](https://github.com/diegosouzapw/OmniRoute/issues/11651)) ([#11988](https://github.com/diegosouzapw/OmniRoute/pull/11988)) — thanks @hartmark +- **fix(dashboard):** Expose Profile loading, errors, page structure, and XP progress to assistive technologies ([#11838](https://github.com/diegosouzapw/OmniRoute/pull/11838)) — thanks @pacocartones +- **fix(socks):** `Agent.connectTimeout` now bounds both SOCKS handshake and TLS connect, and `family === null` no longer falls back to `fetch-socks` ([#11842](https://github.com/diegosouzapw/OmniRoute/pull/11842)) — thanks @maxmad64bis +- **perf(compression):** OOM mitigations for large payload hashing, memoization, and token estimation ([#11844](https://github.com/diegosouzapw/OmniRoute/pull/11844) — thanks @AndrianBalanescu) +- Excluded Next.js Node File Trace manifests (`*.nft.json`) from the published npm tarball. They are build-time metadata and are never read while serving, but had grown to 668.7 MB — 61% of the package — which pushed the upload past the registry limit and made `npm publish` fail with `413 Payload Too Large`. ([#11864](https://github.com/diegosouzapw/OmniRoute/pull/11864)) +- **fix(cli):** keep the global OmniRoute server `--base-url` separate from provider-node `--endpoint` values in add, update, and validate commands ([#11860](https://github.com/diegosouzapw/OmniRoute/pull/11860)) — thanks @pacocartones +- **fix(config):** Nous Research's `Hermes-4-405B` model now displays as "Hermes 4 405B (Nous Research)" in both the provider registry and the free-model catalog, instead of the mislabelled "Hermes 4 7B" ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#11993](https://github.com/diegosouzapw/OmniRoute/pull/11993)) +- **fix(provider/nous):** inject required user= tag into Nous Research inference requests to resolve upstream 400 "missing tags" error ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#12044](https://github.com/diegosouzapw/OmniRoute/pull/12044)) +- **fix(build):** `npm run build` now fails in one second with a named package and a copy-pasteable fix when npm silently drops an externalised optional native dependency, instead of dying four minutes in with `Module not found: Can't resolve 'better-sqlite3'` ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- **fix(install):** `postinstall` no longer throws `ReferenceError: isAndroid is not defined` — failing the whole `npm install` — when the `better-sqlite3` rebuild fallback times out; the manual-fix guidance is reachable again ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- Fixed the Alibaba free-tier allowlist test that went red on its own once the shipped catalog's `validUntil` (2026-08-27) passed, leaving every PR and `main` with a failing `Unit Tests (1/8)`. The test now builds its own packs with dates it controls, and covers the expired-pack fallback that production has actually been serving. ([#11867](https://github.com/diegosouzapw/OmniRoute/pull/11867)) +- Split the npm registry upload into its own GitHub-hosted job. npm refuses `--provenance` from a self-hosted runner (`422 ... Only "github-hosted" runners are supported`), which blocked the v3.8.50 publish; the heavy verification cannot move to a hosted runner, so it now hands the proven tarball over instead. ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(ui):** the shared `DataTable` loading state no longer reads its decorative ⏳ glyph out to assistive technology, and now carries the same `role="status"` / `aria-live="polite"` / `aria-busy="true"` semantics as `PageLoading` ([#11869](https://github.com/diegosouzapw/OmniRoute/pull/11869)) — thanks @pacocartones +- **fix(api):** Bound the `timeout` accepted by `POST /api/providers/[id]/login` to the same 15s–600s window the Adobe Firefly and Conol browser-login paths already enforce, so a request can no longer pin a headful browser and the single active-login slot with an arbitrarily large poll budget. ([#11871](https://github.com/diegosouzapw/OmniRoute/pull/11871)) — thanks @pacocartones +- **fix(api):** Stop the tunnel and MITM routes from returning raw child-process error text, which disclosed host paths, binary install locations and Tailscale `tskey-*` credentials in a body some of these routes serve to non-loopback callers, and return a real 400 instead of a framework 500 when a tunnel request body fails validation. ([#11872](https://github.com/diegosouzapw/OmniRoute/pull/11872)) — thanks @pacocartones +- **fix(open-sse):** stream TTFT/ITL are sampled from a monotonic clock (`performance.now`) instead of `Date.now`, so an NTP correction or wall-clock jump can no longer inflate a genuine TTFT or produce a negative one that the `ttft >= 0` guard silently drops — protecting the router's OTel/EWMA/`usage_history`/speed-ranking signals ([#11873](https://github.com/diegosouzapw/OmniRoute/pull/11873)) — thanks @pacocartones +- **fix(providers):** xAI `reasoning_effort: "xhigh"` now reaches grok-4.6+ instead of being silently clamped to `"high"` ([#11879](https://github.com/diegosouzapw/OmniRoute/pull/11879)) — thanks @NoxzRCW +- **fix(dashboard):** the providers page no longer crashes into the error boundary when a provider id collides with an `Object.prototype` member (`constructor`, `__proto__`); icon lookups are own-property guarded ([#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)) — thanks @NoxzRCW +- **fix(skills):** injected skill tools declared in shorthand (`{"content": "string"}`) now forward valid JSON Schema, unblocking providers that validate tool schemas strictly such as Zhipu GLM on the Console Go tier ([#11881](https://github.com/diegosouzapw/OmniRoute/pull/11881)) — thanks @NoxzRCW +- **fix(api):** `POST /api/playground/simulate-route` now surfaces `combo-ref` and `provider-wildcard` persisted combo steps with a specific warning (naming the referenced combo, or the unresolved `provider/model` wildcard) instead of folding them into a generic "unsupported step" count; a `provider-wildcard` step is also now included as an unresolved target so the operator can see it is in the route (ported from [#11882](https://github.com/diegosouzapw/OmniRoute/pull/11882) — thanks @NoxzRCW). ([#11930](https://github.com/diegosouzapw/OmniRoute/pull/11930)) +- **fix(translator):** the streaming OpenAI→Claude translator keeps upstream usage, including prompt-cache tokens, when it arrives on a trailing `choices: []` chunk (Fireworks and any upstream using `stream_options.include_usage`) ([#11883](https://github.com/diegosouzapw/OmniRoute/pull/11883)) — thanks @NoxzRCW +- **fix(cli):** `omniroute update --apply` now tells you explicitly whether a running server was detected and, if so, that you must run `omniroute restart` to apply the update — it never restarted anything and previously implied the update was already live once files were installed. The dashboard's npm-mode Update flow (`/api/system/version`) now tries OmniRoute's own PID-file-managed supervisor before falling back to pm2, and reports an honest "restart required" step instead of a silent pm2-only "skipped" that read like a completed update. The server-side latest-version lookup backing the dashboard's update banner also gained `--prefer-online`, closing the same stale-npm-cache class already fixed in the CLI's own copy for #4376 ([#11885](https://github.com/diegosouzapw/OmniRoute/issues/11885)). ([#12005](https://github.com/diegosouzapw/OmniRoute/pull/12005)) +- **fix(resilience):** clear persisted LKGP pins when a target suffers connection/provider exhaustion or is skipped before dispatch due to cooldown/exhaustion/unavailability, preventing subsequent requests from repeatedly prioritizing known-dead providers ([#11911](https://github.com/diegosouzapw/OmniRoute/issues/11911)). ([#12013](https://github.com/diegosouzapw/OmniRoute/pull/12013)) — thanks @HouMinXi +- **fix(sse):** the OpenAI→Claude stream translator now defers the terminal `message_delta`/`message_stop` emission until the real usage block has arrived (or a genuine end-of-stream flush forces it) instead of emitting it immediately on `finish_reason` — previously, when the trailing usage-only chunk (`{"choices":[],"usage":{...}}`) arrived *after* the `finish_reason` chunk (the normal order for Fireworks/vLLM/Together and other `stream_options.include_usage` upstreams), the client-visible `message_delta` still carried stale/zero usage even though `state.usage` was internally corrected too late to matter (ported from [#11915](https://github.com/diegosouzapw/OmniRoute/pull/11915) — thanks @HouMinXi). ([#11933](https://github.com/diegosouzapw/OmniRoute/pull/11933)) +- **fix(sse):** `stripStore()` now forces `store=false` for stateless OpenAI-compatible Responses-API targets (unless the connection has explicitly opted in via `providerSpecificData.openaiStoreEnabled`), instead of only handling the `openai`/`agentrouter` cases — a client-supplied `store` value previously passed through untouched to backends that don't actually persist responses server-side ([#11916](https://github.com/diegosouzapw/OmniRoute/pull/11916) — thanks @HouMinXi). +- **fix(models):** custom provider-node models (synced, custom, and alias-backed) now appear under their configured prefix in the unified catalog when the operator's model-id prefix mode is canonical, instead of being dropped whenever alias-inclusion was otherwise disabled ([#11918](https://github.com/diegosouzapw/OmniRoute/pull/11918) — thanks @HouMinXi). +- **fix(models):** the unified model catalog now suppresses stale static registry models (including effort-tier variants) for any provider whose active connection has an authoritative live synced catalog, not only providers already using exclusive-synced-listing — a connection with `providerUsesAuthoritativeLiveCatalog` previously kept serving both the live-synced models and the stale static rows side by side ([#11919](https://github.com/diegosouzapw/OmniRoute/pull/11919) — thanks @HouMinXi). +- **fix(ui):** `ProviderIcon`'s three remaining unguarded lookups (`PROVIDER_ICON_ALIASES`, `LOCAL_SVG_ALIASES`, `THEMED_SVGS`) now use `Object.hasOwn()` own-property checks — a provider id such as `constructor` or `__proto__` previously resolved through the prototype chain instead of falling through to the unknown-provider CDN fallback (`getLobeProviderIcon()` itself was already guarded by [#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)); `ProviderPageHeader`'s `color` field is also now optional, matching the rest of the component's defensive typing (ported from [#11920](https://github.com/diegosouzapw/OmniRoute/pull/11920) — thanks @HouMinXi). ([#11935](https://github.com/diegosouzapw/OmniRoute/pull/11935)) — thanks @NoxzRCW +- fix(ollama): preserve multi-byte UTF-8 content split across stream chunks in the Ollama NDJSON transform, which previously corrupted CJK/emoji into U+FFFD (#11921) — thanks @pacocartones +- **fix(providers):** OrcaRouter chat requests now target `/v1/chat/completions` instead of the bare `/v1` API root, fixing the upstream `404 Invalid URL (POST /v1)` ([#11923](https://github.com/diegosouzapw/OmniRoute/pull/11923)). — thanks @echel0nn +- **fix(plugins):** deliver the `onStreamComplete` event to disk-installed plugins. The event shipped in v3.8.50 (#9669) was emitted internally but had no plugin-facing wiring, so no plugin could ever subscribe: the manifest schema silently dropped `hooks.onStreamComplete`, and the loader/manager only knew the seven legacy hooks. `onStreamComplete` is now a declarable manifest hook, wired through the loader and registered by the manager like the other hooks, and its payload carries a `requestId` so consumers can correlate the stream-completion event with the originating request ([#11934](https://github.com/diegosouzapw/OmniRoute/pull/11934)) — thanks @amaleta / @pacocartones +- **fix(db):** the Qdrant embedding-model dropdown now lists local/self-hosted providers (Ollama, LM Studio, vLLM, etc.) — an active connection is treated as "configured" when the provider allows an optional API key, not only when it has a real key or OAuth, so a running local embedding provider is no longer hidden from the picker ([#11949](https://github.com/diegosouzapw/OmniRoute/issues/11949)) ([#11995](https://github.com/diegosouzapw/OmniRoute/pull/11995)) +- **fix(providers):** Vertex AI Anthropic partner-model discovery now calls the Model Garden `v1beta1` publisher list (`/v1beta1/publishers/anthropic/models`, global) and parses its `publisherModels` envelope, so Claude models auto-synced from Vertex populate the active live catalog and route at request time instead of returning `Model '' is not available in the active live catalog` ([#11991](https://github.com/diegosouzapw/OmniRoute/issues/11991)) — thanks @fabioluissilva ([#11998](https://github.com/diegosouzapw/OmniRoute/pull/11998)) +- **fix(cli):** support `--base-url` alongside `--endpoint` in `omniroute nodes add`, `update`, and `validate` subcommands to prevent global `--base-url` shadowing issues ([#11999](https://github.com/diegosouzapw/OmniRoute/issues/11999)). ([#12033](https://github.com/diegosouzapw/OmniRoute/pull/12033)) — thanks @HouMinXi +- **fix(providers):** `cloudflare-ai` no longer refuses image content parts for every Workers AI model ([#12002](https://github.com/diegosouzapw/OmniRoute/pull/12002)) — the plain-string `content` requirement behind #2539 is carried by the _model_ schema, not by the `/ai/v1/chat/completions` endpoint (measured: an all-text part array returns 200 on `@cf/mistralai/mistral-small-3.1-24b-instruct`, `@cf/meta/llama-4-scout-17b-16e-instruct` and `@cf/meta/llama-3.3-70b-instruct-fp8-fast`, and 400 on the text-only `@cf/qwen/qwen2.5-coder-32b-instruct`). `transformRequest()` flattened every array and threw on the first non-text part (#6390), so image input was refused for vision-capable Cloudflare models that accept it. All-text arrays are still flattened — the one shape every model accepts — while an array carrying a non-text part is passed through untouched, so the attachment is still never silently dropped. Regression guards: `tests/unit/cloudflare-ai-image-parts-6390.test.ts`. — thanks @davidlinfr +- **fix(resilience):** decouple the limiter-managed execution backstop from the queue-wait budget — new `requestQueue.executionMaxWaitMs` (env `RATE_LIMIT_EXECUTION_MAX_WAIT_MS`, default 600000 = 10 min) now feeds Bottleneck's post-dispatch `expiration`, while `requestQueue.maxWaitMs` keeps its documented queue-wait semantics. Previously the queue-wait budget doubled as the execution expiration, so legitimate long-running calls on non-incremental gateways (whole generation buffered before the first upstream byte, e.g. Console Go / Command Code tiers serving GLM models) were killed mid-flight at the queue budget with a false 504 `RATE_LIMIT_EXECUTION_TIMEOUT` — the local limiter undercut the provider-aware upstream fetch-start timeouts. The surfaced 504 message now names `requestQueue.executionMaxWaitMs`; the error keeps the #4165 guarantees (disclaims an upstream timeout, preserves the Bottleneck error as `cause`, branded code + trusted provenance, classified request-scoped so combo falls back). A real queue-wait bound (the `Promise.race` around `limiter.schedule()` sketched in #9533) remains future work. (#12025) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **Call logs:** keep the `error` field when an artifact exceeds the storage cap, instead of replacing it with the omission marker. The error is the only field that says *why* a request failed and is typically ~90 bytes next to the multi-hundred-KB bodies that trip the cap, so dropping it left a size-limited row undiagnosable — a provider outage, a local timeout and an upstream 400 all rendered identically. It is now preserved at every fallback stage, truncated to 4KB if it is itself large ([#12026](https://github.com/diegosouzapw/OmniRoute/issues/12026)). ([#12095](https://github.com/diegosouzapw/OmniRoute/pull/12095)) — thanks @ntdat812 +- **fix(diagnostics):** preserve the error field (truncated to 4KB with a `[truncated: …]` suffix) in every call-log artifact size-limit fallback stage. Previously the minimal fallback replaced the error with `[omitted: call log artifact size limit exceeded]`, so an oversized artifact row showed nothing about WHY the request failed — e.g. 91 of 847 opencode-go 504 rows on one production instance were undiagnosable from the dashboard. Oversized request/response bodies are still omitted exactly as before; the error cap is independent of the payload sizes that tripped the fallback. (#12026) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **fix(sse):** OpenAI Responses clients that declare the native `web_search` tool now receive a spec-shaped `web_search_call` output item with `action.sources` alongside the preserved function-call round-trip, so search results executed through OmniRoute's own search backend are consumable by standard Responses clients (Codex, pi-web-access, …). ([#12031](https://github.com/diegosouzapw/OmniRoute/pull/12031)) — thanks @watchingdogs +- **fix(cli):** use in-thread alias resolver hooks on modern runtimes to avoid deprecation noise and improve Node.js forward compatibility ([#12073](https://github.com/diegosouzapw/OmniRoute/issues/12073)). ([#12083](https://github.com/diegosouzapw/OmniRoute/pull/12083)) — thanks @xiaoyaner0201 +- **fix(combo):** an operator-set **Agent Features → Context length** on a combo is now honored at request time. The value was persisted and advertised through `/v1/models`, but `resolveComboContextLimit()` never consulted it — so a multi-target combo whose members carry no per-model window fell through to the provider's generic `defaultContextLength` (openrouter 128000, command-code 200000) and rejected large requests with `Input exceeds context window … limit 128000` despite the combo being explicitly sized much larger. An identical single-target combo worked, because it collapses to its concrete target before the guard runs. Invalid values (0/negative/NaN/Infinity) are ignored, so the existing target → combo-min → fallback order is unchanged. ([#12090](https://github.com/diegosouzapw/OmniRoute/pull/12090)) — thanks @adivekar-utexas +- **fix(sse):** passthrough streams now estimate usage on finish when upstream closes without usage even with `stream_options.include_usage` — avoids `0 tokens / 0%` for providers that stay silent (and correctly handles trailing empty-choices usage) ([#12151](https://github.com/diegosouzapw/OmniRoute/pull/12151)) — thanks @maxmad64bis +- **fix(combos):** clearing an agent feature in the combos editor now persists — unchecking context cache protection, or emptying the system message or tool filter, sends an explicit `null` instead of dropping the field from the `PUT` body, which the update merge read as "leave unchanged" ([#12177](https://github.com/diegosouzapw/OmniRoute/pull/12177)) — thanks @foreveryh / @ntdatt812 +- **fix(memory):** self-hosted embedding endpoints now vectorize — the vector width is measured from the first embedding that comes back instead of being read from a registry that cannot describe them, so `vec_memories` is created and memories stop piling up unvectorized behind a green health check ([#12180](https://github.com/diegosouzapw/OmniRoute/pull/12180)) — thanks @kanade-hoshino / @ntdatt812 +- **fix(translator):** the leading `system` message now reaches Responses-API upstreams when its `content` is a content-part array — it was read as `typeof content === "string" ? content : ""`, so a prompt-caching client (Anthropic `cache_control`, the shape LiteLLM and the Anthropic SDK emit) had its entire system prompt replaced by an empty `instructions`. The request was still accepted with a normal `prompt_tokens` count, so the model answered with no instructions and nothing in the response said they were missing. Mid-conversation system turns already handled the array shape ([#7056](https://github.com/diegosouzapw/OmniRoute/pull/7056)); only the first one did not ([#12206](https://github.com/diegosouzapw/OmniRoute/issues/12206)). Regression guard: `tests/unit/translator-openai-responses-system-content-parts.test.ts`. ([#12207](https://github.com/diegosouzapw/OmniRoute/pull/12207)) — thanks @Gorillaz322 +- **fix(free-tier):** `/api/free-tier/summary` no longer computes its totals from a Radar feed built before the catalog the running release ships. When the cached feed is older — or carries no build date at all — the route answers from the shipped catalog, resolved through the operator's local model state so disabled and tombstoned models stay out of the numbers ([#12215](https://github.com/diegosouzapw/OmniRoute/pull/12215)). — thanks @maxmad64bis +- **fix(oauth):** Keep a Claude personal workspace and a Team organization as separate connections — they share the same email and `accountUUID`, so the email-only OAuth dedup let the second login overwrite the first account's tokens; `organizationUUID` now disambiguates them, the way `workspaceId` does for Codex ([#12222](https://github.com/diegosouzapw/OmniRoute/pull/12222)) — thanks @davidebaraldo +- **fix(resilience):** a 402 on a single paid model of a passthrough/gateway provider (e.g. `kilo-gateway`, `ollama-cloud`) no longer terminalizes the whole connection with a never-auto-recovered `credits_exhausted` status — only the paid model is locked out, so free models on the same key keep serving. 402 variant of [#3027](https://github.com/diegosouzapw/OmniRoute/issues/3027). Single-credential providers are unaffected — a 402 there is still treated as the key being genuinely out of credit ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239) / [#10616](https://github.com/diegosouzapw/OmniRoute/issues/10616)) ([#12242](https://github.com/diegosouzapw/OmniRoute/issues/12242)) — thanks @brick30llc-ctrl ([#12266](https://github.com/diegosouzapw/OmniRoute/pull/12266)) — thanks @geekyNads +- **fix(sse):** trust `finish_reason: "length"`/`"max_tokens"` over the reasoning-consumed-token ratio in response quality validation, so a reasoning model truncated below the old 90% threshold correctly fails and retries instead of returning empty content as a silent "success" ([#12262](https://github.com/diegosouzapw/OmniRoute/pull/12262)) — thanks @brick30llc-ctrl +- **fix(settings):** `PUT /api/settings/cache-config` now persists `alwaysPreserveClientCache` to the flat general settings the runtime cache-control policy actually reads; previously the value landed in the databaseSettings "cache" section and was silently ignored, so the endpoint had no effect on `cache_control` passthrough ([#12304](https://github.com/diegosouzapw/OmniRoute/pull/12304)) — thanks @davidebaraldo +- **fix(grok-cli):** treat omitted SuperGrokPro `creditUsagePercent` as 0% used so Provider Limits still renders a weekly bar (proto3 zero-elision) ([#12312](https://github.com/diegosouzapw/OmniRoute/pull/12312)) — thanks @HouMinXi +- **fix(combo):** Expose the two Auto-Combo scoring factors nobody could set — the combo validation schema and the dashboard weight sliders both declared 13 of the scorer's 15 factors, so `connectionDensity` (spreads load across a provider's connections) and `quality` were dropped on save and offered nowhere. The sliders also shipped their own default table that differed from the engine's on every non-zero factor and summed to 1.05, so the percentages shown next to them added up to 105%. Both lists now match `DEFAULT_WEIGHTS`, and a test keeps them there. Note that a combo whose stored `weights` omitted the two keys was effectively running with them at zero and the other thirteen renormalized upward; it now runs with the engine's intended distribution, so its routing does shift ([#12314](https://github.com/diegosouzapw/OmniRoute/pull/12314)) — thanks @maxmad64bis +- **fix(docs):** The free-tier reference no longer says its numbers come "confidence tagged per row" — no catalog entry carries a confidence tag and the API serves none, so every figure on that page is an estimate of the same, unstated quality. The page now states what an entry does vouch for: an independently documented hard stop (set by hand with the source in a comment, never defaulted to `true`) and a prompt-training disclosure, both with live counts the `check:docs-counts` gate keeps honest ([#12318](https://github.com/diegosouzapw/OmniRoute/pull/12318)) — thanks @maxmad64bis +- **fix(usage):** `adobe-firefly` and `firefly` have had a working usage fetcher since Adobe Firefly landed, but neither was ever added to the registration list, so the provider-plugin manifest, `genericQuotaFetcher` and the free-access quota cache all reported them as having no usage support — while `USAGE_SUPPORTED_PROVIDERS` said the opposite. Both are now declared, which also means their credit balance is fetched like any other declared provider's: `registerGenericQuotaFetchers` now registers a generic quota fetcher for them, and `resolveFreeAccessState` no longer returns early. A test holds the registration list to the dispatcher's switch in both directions, which is what the module's own docstring already asked for in prose ([#12321](https://github.com/diegosouzapw/OmniRoute/pull/12321)) — thanks @maxmad64bis +- **fix(quota):** drop the generic quota cache (agy / Antigravity / Claude OAuth) on an upstream 429 so reset-aware scoring does not keep a 60s stale snapshot, and force-refresh the next usage fetch so inner provider caches cannot recache the same window ([#12325](https://github.com/diegosouzapw/OmniRoute/pull/12325)) — thanks @HouMinXi +- **fix(combos):** deleting a combo now clears its persisted LKGP pins instead of leaving unreachable `key_value` rows behind ([#12326](https://github.com/diegosouzapw/OmniRoute/issues/12326)) ([#12425](https://github.com/diegosouzapw/OmniRoute/pull/12425)) — thanks @KrzysiekSko +- **fix(sse):** Keep ZWNJ (U+200C) and ZWJ (U+200D) in assistant text, reasoning and tool-call arguments — Persian/Kurdish half-space (`ارائه‌دهنده`), Arabic/Indic shaping and emoji sequences no longer lose them; the response de-obfuscation now removes joiners only between ASCII word characters, where the request side inserts them ([#12186](https://github.com/diegosouzapw/OmniRoute/issues/12186)) — thanks @rezjalibd ([#12359](https://github.com/diegosouzapw/OmniRoute/pull/12359)) — thanks @pacocartones +- **fix(resilience):** count resolved upstream 5xx results against the provider circuit breaker on the chat path — `CircuitBreaker.execute()` no longer reads a resolved `{ success: false, status: 5xx }` as a success that cancels the call-site failure, so a provider answering 503s now trips its breaker instead of staying `CLOSED` at `failureCount: 1`; single-model and combo dispatches are each accounted exactly once ([#12254](https://github.com/diegosouzapw/OmniRoute/issues/12254)) ([#12360](https://github.com/diegosouzapw/OmniRoute/pull/12360)) — thanks @pacocartones +- **fix(providers):** resolve the Codex quota auto-ping model from the live provider catalog and lifecycle registry instead of the retired `gpt-5.1-codex-mini`, and pause the ping with one actionable warning when no selectable Codex model exists rather than retrying a shut-down id every cooldown window ([#11905](https://github.com/diegosouzapw/OmniRoute/issues/11905)) ([#12361](https://github.com/diegosouzapw/OmniRoute/pull/12361)) — thanks @pacocartones +- **fix(api):** keep the `{created, data}` wrapper on combo-routed `/v1/images/generations` responses and default Codex image results to `b64_json` on both `/v1/images/generations` and `/v1/images/edits` so Codex CLI's built-in `image_gen` can decode them ([#12268](https://github.com/diegosouzapw/OmniRoute/issues/12268)) ([#12362](https://github.com/diegosouzapw/OmniRoute/pull/12362)) — thanks @pacocartones +- **fix(sse):** Name the shadowed custom provider node when a built-in provider id/alias (e.g. `openference` → `of`) reserves the prefix of an existing OpenAI/Anthropic-compatible node, so the runtime `No active credentials for provider: ` error explains that the prefix routed to the built-in and never reached the node's healthy connections, instead of contradicting the dashboard ([#11943](https://github.com/diegosouzapw/OmniRoute/issues/11943)) — thanks @morpheus9393 ([#12365](https://github.com/diegosouzapw/OmniRoute/pull/12365)) — thanks @pacocartones +- **fix(i18n):** wrap `ccOnboardingKeyPlaceholder` in ICU single quotes across all 43 locales so angle brackets render literally instead of being parsed as rich-text tags, which crashed the Claude Code onboarding block with `INVALID_MESSAGE: INVALID_TAG` ([#12302](https://github.com/diegosouzapw/OmniRoute/issues/12302)) ([#12369](https://github.com/diegosouzapw/OmniRoute/pull/12369)) — thanks @gonisulaimann +- **fix(models):** publish `effort_tiers` on Kimi K3's synced base-model entries (`kmca/k3`, `kmca/k3-256k`) so catalog-only clients (OpenCode, plain SDK pickers) can see and select the reasoning tiers (`low`/`high`/`max`) the synced metadata already carried — the `isSkippedEffortProvider` gate no longer suppresses tier visibility on those base entries, while synthetic `-` variant generation stays prevented and Codex/GLM base models remain excluded unchanged ([#12299](https://github.com/diegosouzapw/OmniRoute/issues/12299)) ([#12371](https://github.com/diegosouzapw/OmniRoute/pull/12371)) — thanks @gonisulaimann +- **fix(guardrails):** keep `auto`/`auto/*` virtual combos exempt from the Vision Bridge `fixedModel` credential guard so a combo target is passed through instead of silently falling back to global auto-selection ([#12237](https://github.com/diegosouzapw/OmniRoute/issues/12237)) ([#12373](https://github.com/diegosouzapw/OmniRoute/pull/12373)) — thanks @pacocartones +- **fix(combo):** capability-filter exhaustion caused by `max_tokens` above every target's known output limit now reports that reason (requested `max_tokens` vs the pool's highest known ceiling) instead of the unrelated "supports structured output" message ([#12229](https://github.com/diegosouzapw/OmniRoute/issues/12229)) — thanks @DW-MediaLab ([#12374](https://github.com/diegosouzapw/OmniRoute/pull/12374)) — thanks @pacocartones +- **fix(auth):** the `least-used` account strategy now prefers accounts without backoff before falling back to oldest `lastUsedAt`, the same tie-break `round-robin` already applies, so a failover no longer lands on a just-rate-limited account for a single request ([#12279](https://github.com/diegosouzapw/OmniRoute/issues/12279)) — thanks @tenshiak ([#12375](https://github.com/diegosouzapw/OmniRoute/pull/12375)) — thanks @pacocartones +- **fix(docker):** the `chatgpt-web-codex-browser` image now finds the Chrome binary under `chrome-linux64/` (Chrome for Testing layout in `playwright:v1.62.0-noble`) as well as the legacy `chrome-linux/`, so the container no longer crash-loops with `exec: --headless=new: not found` ([#12024](https://github.com/diegosouzapw/OmniRoute/issues/12024)) ([#12376](https://github.com/diegosouzapw/OmniRoute/pull/12376)) — thanks @pacocartones +- **fix(providers):** declare `groq/compound` and `allam-2-7b` as non-reasoning models in the curated Groq registry so `reasoning_effort` / `output_config.effort` / `thinking` from Claude Code are stripped instead of forwarded, which Groq rejected with HTTP 400 ([#12134](https://github.com/diegosouzapw/OmniRoute/issues/12134)) ([#12379](https://github.com/diegosouzapw/OmniRoute/pull/12379)) — thanks @pacocartones +- **fix(executors):** `OpencodeExecutor` no longer forces a direct connection when the connection has a proxy assigned in Proxy Management but no per-account proxies: the single-account fast path used to wrap the upstream dispatch in the direct-egress sentinel, discarding the ambient proxy context the chat handler had pinned from `proxy_assignments`, so API-key `opencode`/`opencode-go` connections egressed from the host IP (and hit geoblocks) despite the assignment. The direct pin is now applied only when no ambient proxy context exists ([#11894](https://github.com/diegosouzapw/OmniRoute/issues/11894) — thanks @hizzt) ([#12380](https://github.com/diegosouzapw/OmniRoute/pull/12380)) — thanks @pacocartones +- **fix(api):** `GET /v1/models` with `MODELS_CATALOG_PREFIX_MODE=canonical` (or `?prefix=canonical`) now lists providers whose registry alias is undefined or equal to their own id (Antigravity, Antigravity CLI and other self-aliased built-ins) — their single `provider/model` id was dropped by the alias/canonical duplicate guard in the static, synced, custom and alias-backed catalog loops ([#12058](https://github.com/diegosouzapw/OmniRoute/issues/12058)) — thanks @cheynetom ([#12381](https://github.com/diegosouzapw/OmniRoute/pull/12381)) — thanks @pacocartones +- **fix(translator):** Drop replayed `thinking` blocks that carry no signature (the shape produced from cross-provider `reasoning_content`) instead of stamping the default Claude signature on them, which Anthropic rejected with `400 Invalid signature in thinking block` on the next turn served by an Anthropic rung ([#12105](https://github.com/diegosouzapw/OmniRoute/issues/12105)) — thanks @atescivitci-cmd ([#12386](https://github.com/diegosouzapw/OmniRoute/pull/12386)) — thanks @pacocartones +- **fix(cli):** Resolve Bun's `--preload` polyfill path against the package root instead of `dist/`, so `omniroute` installed with `bun install -g` no longer crashes at startup with `error: preload not found …/dist/open-sse/utils/setupPolyfill.ts` ([#11980](https://github.com/diegosouzapw/OmniRoute/issues/11980)) — thanks @joglomedia ([#12387](https://github.com/diegosouzapw/OmniRoute/pull/12387)) — thanks @pacocartones +- **fix(providers):** `gemini-business` now publishes its model catalog — `/v1/models` and `/v1/providers/gemini-business/models` list the 12 enterprise Gemini ids the executor understands instead of returning an empty list (#12107) ([#12389](https://github.com/diegosouzapw/OmniRoute/pull/12389)) — thanks @pacocartones +- **fix(db):** install `busy_timeout` before the SQLite connection's first statement so a process opening the database while another one closes its WAL connection waits out the transient EXCLUSIVE lock instead of dying with `database is locked`, and recognise the drivers' real BUSY/PROTOCOL/IOERR errors as transient in the corruption probe so the same lock no longer renames the database away as corrupt; deflakes `cross-process contenders never both acquire the same connection` (#12394 — thanks @pacocartones) +- **fix(chat-admission):** derive the `chat_admission_busy` 503 `Retry-After` from observed heavyweight-lease occupancy — the larger of the exhausted `OMNIROUTE_CHAT_ADMISSION_QUEUE_MS` window and the time since capacity last turned over, capped at 60 s — instead of a fixed 1 s (structural) / 2 s (byte-stage) hint that invited Codex/agent fan-out clients to re-send ~1 MiB `/v1/responses` bodies every second into a gate held for the whole SSE lifetime; an idle gate keeps the historical floors ([#12135](https://github.com/diegosouzapw/OmniRoute/issues/12135)) (#12395 — thanks @pacocartones) +- **fix(api-manager):** the API key permissions modal no longer silently drops `allowedCombos` entries its Combo picker cannot render — routing-rule names such as `rt-*`, which the backend already honours — when "All" is clicked and the key is switched back to "Restrict"; those entries now survive the toggle, are listed read-only under the combo list so the count and the list agree, and are saved back verbatim instead of persisting `[]` (deny-all) (#12397 — thanks @pacocartones) +- **fix(catalog):** write the NUL separator of the catalog connection memo key, the provider serviceKind memo key, the Video Bridge promotion group key and a JSON-exactness test fixture as the `\u0000` escape instead of a raw byte — same runtime value, but the raw byte made git, GitHub and ripgrep treat those files as binary (hidden PR diffs, silently skipped searches); a guard test now keeps raw NUL bytes out of `src/`, `open-sse/` and `tests/` (#12403 — thanks @pacocartones) +- **fix(providers):** add `CLAUDE_CODE_CLIENT_VERSION` and `GITHUB_COPILOT_CLI_VERSION` env overrides so Anthropic/Copilot client-version gates can be unblocked without a rebuild ([#12417](https://github.com/diegosouzapw/OmniRoute/issues/12417)) ([#12632](https://github.com/diegosouzapw/OmniRoute/pull/12632)) — thanks @HouMinXi +- **fix(db):** back-fill `last_ping_at` and `last_pinged_reset_key` on `provider_connections` during schema reconciliation so divergent lineages that skipped `123_quota_auto_ping` still accept quota auto-ping writes ([#12470](https://github.com/diegosouzapw/OmniRoute/pull/12470) — thanks @KooshaPari) +- **fix(ci):** document MIT exceptions for `@eloqnt/{config,format-json,format-po}` (next-intl transitive; locked tarballs omit `license`) and keep the A2A lifecycle vitest off the real SQLite persistence seam ([#12581](https://github.com/diegosouzapw/OmniRoute/issues/12581)) ([#12595](https://github.com/diegosouzapw/OmniRoute/pull/12595)) +- OpenCode plugin `/v1/models` catalog fetch now waits 30s by default and attaches HTTP `statusCode` on 401/5xx so host fallback plugins can hop instead of seeing an untyped AbortError/UnknownError. ([#12607](https://github.com/diegosouzapw/OmniRoute/pull/12607)) — thanks @RaviTharuma +- **CI:** the OpenAPI security-tier gate now mirrors `isAlwaysProtectedPath()` in full — it also reads `ALWAYS_PROTECTED_API_PATTERNS`, so the pattern-gated credential routes (`/api/providers/{id}/{claude,codex}-auth/{export,apply-local}`, GHSA-5926-2w35-7h4q) no longer report as unannotated. (#12605) +- **fix(api):** GET /v1/models no longer waits forever on a hung coalesced catalog rebuild; cold-path waits are bounded (`CATALOG_BUILD_TIMEOUT_MS`, default 8s) and a last-good 200 is served when the rebuild times out ([#12627](https://github.com/diegosouzapw/OmniRoute/issues/12627)). ([#12628](https://github.com/diegosouzapw/OmniRoute/pull/12628)) — thanks @RaviTharuma +- **fix(cursor):** a non-array `tool_calls` on an assistant message no longer crashes the cursor request translator with a `TypeError`; both loops now require an array ([#12689](https://github.com/diegosouzapw/OmniRoute/issues/12689)) ([#12691](https://github.com/diegosouzapw/OmniRoute/pull/12691)) — thanks @soroush5 +- **fix(chat):** Chat Completions no longer return empty `content` after a server-owned memory or skills tool; the first provider send and account/model recovery now share one pipeline so a follow-up round-trip can fill the reply ([#12696](https://github.com/diegosouzapw/OmniRoute/issues/12696)) — thanks @HouMinXi ([#12867](https://github.com/diegosouzapw/OmniRoute/pull/12867)) +- **fix(dashboard):** batch-deleting provider keys no longer toasts failure after a successful delete when the confirm button's click event is forwarded as `onAfter` ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) — thanks @HouMinXi +- **fix(glm):** drop the extra 16th argument to `createSSETransformStreamWithLogger` that TypeScript rejected (TS2554) and that never reached the TransformStream ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) ([#12770](https://github.com/diegosouzapw/OmniRoute/pull/12770)) — thanks @HouMinXi +- **fix(dashboard):** pass `nodeMap` into Runtime `QuotaGroup` so a quota monitor in error/exhausted/alerting no longer throws `ReferenceError: nodeMap is not defined`. ([#12868](https://github.com/diegosouzapw/OmniRoute/pull/12868)) — thanks @HouMinXi +- Restricted API keys whose `allowedModels` lists a combo name no longer skip every combo member at pre-dispatch (`ALL_TARGETS_SKIPPED`). Inner-target filtering still applies when the allow-list is a provider prefix or `disableNonPublicModels` is on ([#12899](https://github.com/diegosouzapw/OmniRoute/pull/12899)). — thanks @HouMinXi +- **fix(combos):** Effort-suffixed combo members inherit the base model's `model_context_overrides` row so priority order is not inverted on large requests ([#12926](https://github.com/diegosouzapw/OmniRoute/pull/12926)) — thanks @HouMinXi +- **fix(catalog):** Picker-added `customModels` enter the dispatch-time live catalog so combo members and bare inference no longer 400 ([#12934](https://github.com/diegosouzapw/OmniRoute/pull/12934)) — thanks @HouMinXi +- **fix(docker):** re-derive the Docker build's worker-pool memory budget from the MEASURED ~4.5 GB per-process RSS (the issue owner's own VPS dmesg OOM-killer reproduction) instead of the stale 2560 MB/worker inference #11419 shipped, and lower `OMNIROUTE_BUILD_WORKERS` 3 → 2 so 1 parent + 1 page-data worker (2 processes × 4.5 GB = 9 GB) fits the 12.288 GB (75%) budget on a 16 GB GitHub Actions runner — the previous default (1 parent + 2 workers = 13.5 GB) still overcommitted the runner and kept "Publish to Docker Hub" failing with `cannot allocate memory` after #11419 merged (#7518). ([#11663](https://github.com/diegosouzapw/OmniRoute/pull/11663)) +- **fix(electron):** the packaged Windows desktop build now passes the #7592 cold-restart smoke end to end. Five defects were found and fixed on the way: (1) optional-pack staging failed on any machine whose `tar` is GNU tar — it reads the drive letter in an absolute `-f C:\...` archive path as a remote rsh host (`Cannot connect to C:`), so staging now passes a bare filename with `cwd` at the tarball directory and surfaces tar stderr on failure; (2) the Electron `files` allowlist was missing `lib/loginHeaderCapture.js`, so the packaged main process crashed at startup with `Cannot find module './lib/loginHeaderCapture'`; (3) electron-builder ≥26 injects an `!**/node_modules/**` ignore into every extraResources pattern list that no positive filter can override, which silently dropped the staged runtime `node_modules` (including the better-sqlite3 N-API prebuild) from `resources/app` — a fresh v3.8.50 desktop build would have shipped with no native SQLite driver and reproduced the #7592 sql.js fallback on every machine; an `afterPack` hook now restores the staged `node_modules` after packing; (4) the packaged-app smoke harness redirected `USERPROFILE` into its temp DATA_DIR, but Electron resolves the Roaming profile from `%USERPROFILE%\AppData\Roaming\` (USERPROFILE takes precedence over the APPDATA env var) and the path service throws instead of creating it, so `requestSingleInstanceLock()` returned false and the app exited(0) silently before `app.whenReady()` — the harness now pre-creates the derived tree, and `ensureSmokeEnvDirs` is exported and unit-tested; (5) the #7592 driver assertion parsed a `[DB] Driver: ...` line that the server's primary DB path never printed (only the unused `openDatabaseAsync()` did) — `getDbInstance()` now emits the same line on open so the guard can actually assert the native driver. Also: the smoke env-allowlist unit test hardcoded POSIX paths and could only pass on Linux/macOS; expectations are now host-agnostic, with new regression tests for the USERPROFILE-derived tree and `tarPack` under absolute Windows-style paths ([#11443](https://github.com/diegosouzapw/OmniRoute/pull/11443)) — thanks @jonlwheat2-gif +- fix(sse): stop the auto-combo candidates inspector from silently dropping model-locked/cooled-down rows (#9133) ([#11994](https://github.com/diegosouzapw/OmniRoute/pull/11994)) — thanks @hartmark +- **fix(providers):** Perplexity Web no longer turns upstream stream failures into successful assistant text; pre-content failures remain eligible for fallback, partial output ends with a structured sanitized error, and failed sessions are not persisted ([#12465](https://github.com/diegosouzapw/OmniRoute/pull/12465)) +- **fix(video-bridge):** preserve chronological video and transcript fusion, source-frame timestamps, transcript provenance, full contact-sheet intervals, and pre-abort callback isolation ([#11681](https://github.com/diegosouzapw/OmniRoute/pull/11681)) +- **Z.ai Web:** HTTP 200 streams carrying an upstream error now terminate with a structured failure instead of assistant text plus a normal stop, preserving partial output while allowing pre-content combo fallback. ([#12454](https://github.com/diegosouzapw/OmniRoute/pull/12454)) +- Absorb `Error [AbortError]: request_signal_aborted` and DOMException AbortError shapes in the process-level client-abort crash guard so routine client disconnects no longer kill the server (exit code 7). ([#12165](https://github.com/diegosouzapw/OmniRoute/pull/12165)) — thanks @alvinveroy +- **fix(sse):** Treat Adapta Web `type:error` SSE events as sanitized non-stream failures instead of empty HTTP 200 completions. ([#12459](https://github.com/diegosouzapw/OmniRoute/pull/12459)) +- **fix(autoCombo):** layer 3 (`models_dev_tier`) no longer scores retired models as premium, merge cross-provider capability rows deterministically, and inherit tier scores through variant ids ([#11508](https://github.com/diegosouzapw/OmniRoute/issues/11508)). `getModelsDevTierFitness` now vetoes ids present in `config/quality/model-lifecycle.json` with status `retired` before any other signal; `loadModelCapabilities()` aggregates per `model_id` (any-non-null-true for booleans, max for `limit_context`) instead of last-write-wins over SQLite's undefined row order; a capabilities miss retries through `resolveScoresAs()`'s base id and reports `models_dev_tier:inherited`, matching layers 1–2 (#11492). Adds `getModelsDevTierFitnessWithSource()` and an `invalidateCapabilitiesCache()` test/ops hook. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) — thanks @Meet6338-X +- fix(sse): accept Claude `content:[]` + `stop_reason: max_tokens`/`tool_use` in `detectMalformedNonStream` (match `isEmptyContentResponse`) so Claude Code `/model` probes with `max_tokens: 1` no longer become a false 502 ([#11568](https://github.com/diegosouzapw/OmniRoute/pull/11568)) — thanks @Seramicx +- **fix(security):** sanitize `request.failed` diagnostics before publishing them to live dashboard listeners and replay history, while keeping status, model, provider, latency, and internal call-log diagnostics intact. ([#12469](https://github.com/diegosouzapw/OmniRoute/pull/12469)) +- **fix(pwa):** do not intercept dashboard navigations so Chrome can fall back from a stale HTTP/3 Alt-Svc advertisement (UDP :20128 is unpublished; F5 on a long-lived tab hung until a new tab opened a fresh TCP connection). ([#12767](https://github.com/diegosouzapw/OmniRoute/pull/12767)) — thanks @HouMinXi +- **fix(executors):** handle DuckDuckGo ERR_BN_LIMIT (418) without retrying — when the upstream returns `418 ERR_BN_LIMIT` (rate-limit/ban), the executor now returns the error immediately instead of burning another VQD acquisition that would only count against the IP limit. The retry logic for `418 ERR_CHALLENGE` (unsolved challenge) remains unchanged. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) ([#12110](https://github.com/diegosouzapw/OmniRoute/pull/12110)) — thanks @oyi77 / @Meet6338-X +- Fixed DuckDuckGo streaming responses losing JSON lines and UTF-8 characters split across network chunks. ([#11528](https://github.com/diegosouzapw/OmniRoute/pull/11528)) — thanks @pacocartones +- Harden generated Cloudflare, Vercel, and Deno relays by stripping hop-by-hop, framing, and proxy authentication headers before forwarding requests upstream. ([#11533](https://github.com/diegosouzapw/OmniRoute/pull/11533)) — thanks @pacocartones +- **feat(grok-cli):** Provider Limits shows grok-cli banked reset credits from `GetRemainingResets` (including a real zero; a failed RPC omits the row) and the existing View credits button now calls `ConsumerUiSvc/RedeemReset` for grok-cli. Live tokens use nested fields 10/20/30 (id + Timestamp), not compact 1/2/3. ([#12805](https://github.com/diegosouzapw/OmniRoute/pull/12805)) — thanks @HouMinXi +- **fix(memory):** Embedding Model Quick select, Embedding Source remote dropdown, and Rerank selector now list every configured provider with embedding/rerank support instead of only chat-catalog text matches plus OpenRouter live discovery; a generic OpenAI-compatible `/embeddings` + Cohere-compatible `/rerank` runtime fallback resolves any configured chat provider's embedding/rerank endpoint, so unlisted providers no longer fail with "Unknown embedding provider"; both memory selectors gained a free-text model override ([#11390](https://github.com/diegosouzapw/OmniRoute/pull/11390)) — thanks @rqzbeh +- Harden SQLite upgrades around the historical migration-074 version collision: missing discovery and inspector tables are replayed atomically, pre-existing databases (including setup-created skeletons) receive reusable content-addressed safety snapshots, and Node test/eval probes without `DATA_DIR` are isolated from the operator database. ([#12435](https://github.com/diegosouzapw/OmniRoute/pull/12435)) +- **fix(dashboard):** Moonshot/Kimi Open Platform voucher and cash leftover percentages follow the bucket balance, so an empty wallet no longer paints those rows as 100% while Available is 0% ([#12733](https://github.com/diegosouzapw/OmniRoute/pull/12733)) — thanks @HouMinXi +- **fix(combo):** return non-retryable HTTP 400 when all candidates for a pinned native Codex turn are unavailable due to model-scoped lockout, terminating the turn cleanly while preserving turn continuity and enabling standard Combo routing on subsequent turns ([#12240](https://github.com/diegosouzapw/OmniRoute/pull/12240)) — thanks @mdigitalbh81 +- Fixed auto-router strategies preserving the selected connection through ranking and dispatch when multiple connections share the same provider and model. ([#11530](https://github.com/diegosouzapw/OmniRoute/pull/11530)) — thanks @pacocartones +- Preserve Chat Completions JSON-object response formats when translating requests to the Responses API. ([#11529](https://github.com/diegosouzapw/OmniRoute/pull/11529)) — thanks @pacocartones +- Fix idempotency fingerprints so Responses API requests include `input` and other semantic generation fields using stable serialization, while excluding credentials and request metadata. ([#11532](https://github.com/diegosouzapw/OmniRoute/pull/11532)) — thanks @pacocartones +- **fix(api):** Generated API CLI commands now enforce required OpenAPI request bodies; Combo test commands forward the required `comboName` body, while API keys created by older writers after migration 149 preserve legacy allow-all Combo access without widening explicit empty allowlists — thanks @marcelokarval ([#12070](https://github.com/diegosouzapw/OmniRoute/pull/12070)) +- **fix(grok-web):** treat upstream streaming failures as failures instead of successful assistant text: error-only streams now fail readiness with HTTP 502, while failures after legitimate content preserve that partial output and terminate through the sanitized stream failure path without a normal `stop` completion. ([#12458](https://github.com/diegosouzapw/OmniRoute/pull/12458)) +- HuggingChat now turns HTTP 200 JSONL generation failures into a sanitized 502 before content, or a fixed public stream failure after partial output, so fallback and request persistence no longer record a false successful stop. ([#12456](https://github.com/diegosouzapw/OmniRoute/pull/12456)) +- **fix(providers):** keep 1min.ai HTTP 200 stream errors out of assistant content, preserve partial output, and expose sanitized terminal errors so pre-content failures can fall back. ([#12466](https://github.com/diegosouzapw/OmniRoute/pull/12466)) +- **fix(sessions):** preserve legacy Sessions data when the additive exclusive-session projection is unavailable, returning an empty projection and warning only once per contiguous outage instead of failing the endpoint; the Sessions badge intentionally reflects the merged legacy and exclusive row count introduced by [#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389) ([#11469](https://github.com/diegosouzapw/OmniRoute/pull/11469)) — thanks @KaspaPulse +- Render Nimble Search with the generic provider icon and serve Opper's proven logo locally. ([#12415](https://github.com/diegosouzapw/OmniRoute/pull/12415)) +- **fix(quality):** `config/quality/provider-assets-provenance.jsonl` was stale against the physical `public/providers/` tree (still listing 225 assets and 5 duplicate-content groups after several provider-retirement PRs removed 85 icon files), which would have made `check:provider-asset-provenance` fail on the next full run. Reconciled the manifest to the real 142-file snapshot (142 asset records, 1 duplicate-content group, 2 newly-registered files marked `unresolved` pending provenance research), repinned `auditedCommit`, and updated the regression test's hardcoded counts to match. ([#11876](https://github.com/diegosouzapw/OmniRoute/pull/11876)) +- fix(ui): wrap quota card footer action buttons to prevent clipping Refresh button on narrow cards — when extra action buttons such as `View credits` are present on Codex cards, allow the footer row to flex-wrap with `shrink-0` buttons so `Refresh now` remains visible and clickable across all responsive card widths ([#11464](https://github.com/diegosouzapw/OmniRoute/pull/11464)) — thanks @adevwithpurpose +- Keep Antigravity Gemini usable when the same connection's Claude weekly quota is empty; generic quota cache stays per-connection for every other provider. ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) — thanks @HouMinXi +- Fixed Tailscale tunnel setup to reject ports outside the valid 1-65535 range and safely install Windows MSI packages from paths containing apostrophes. ([#11534](https://github.com/diegosouzapw/OmniRoute/pull/11534)) — thanks @pacocartones +- Classify Cloudflared and Tailscale process-management routes as local-only and spawn-capable while preserving remote access to read-only tunnel status endpoints. ([#11531](https://github.com/diegosouzapw/OmniRoute/pull/11531)) — thanks @pacocartones +- Electron release workflow: the `publish-npm` job now grants `actions: read` to the reusable `npm-publish.yml` it calls (its `publish` job requests it), which is what made GitHub refuse the whole v3.8.50 run at startup and ship the release with zero desktop assets; a `workflow_dispatch` now builds the requested tag instead of the dispatching branch and can skip the npm leg (`publish_npm=false`) when only re-attaching assets ([#11974](https://github.com/diegosouzapw/OmniRoute/pull/11974)) +- **fix(dashboard):** The Combos page usage guide now reads its dismissal through `useSyncExternalStore` instead of correcting SSR state inside an effect, removing an extra commit of the page tree on every load (and the `react-hooks/set-state-in-effect` error it raised). ([#12671](https://github.com/diegosouzapw/OmniRoute/pull/12671)) +- Electron release: `electron/package-lock.json` regained the optional `electron-builder-squirrel-windows` subtree (13 entries) that `npm ci` had been refusing as out of sync, `electron-release.yml` gained a `build_ref` dispatch input and stops regenerating release notes on a re-attach dispatch, and the npm publish workflow attaches the SBOM to the GitHub Release on dispatch publishes too — so the v3.8.51 tag ships every desktop asset and the SBOM like v3.8.49 did ([#12022](https://github.com/diegosouzapw/OmniRoute/pull/12022)) +- Drained the reds every PR against `release/v3.8.51` was born with: documented the three service-worker build-id variables, registered the six retirement/tunnel tests with the mutation gate, approved `eslint-plugin-react-hooks` in the dependency allowlist, added the six `combo.sort.*` strings to `vi` and `pt-BR`, pointed the ChatGPT Web doc at the real migration-168 test, worded the g4f hint around the member key, and realigned four tests to the retired-provider catalog and the legacy-schema fixtures the retirement migrations touch. ([#11940](https://github.com/diegosouzapw/OmniRoute/pull/11940)) +- **fix(providers):** Claude, Grok, LMArena, Notion, and Perplexity web-cookie transports now use pooled `wreq-js` 3.2 instead of the native sidecar, with all nine supported bindings pinned and audited, and the applicable platform binding plus native-license evidence included in each release artifact ([#12429](https://github.com/diegosouzapw/OmniRoute/pull/12429), supersedes [#11753](https://github.com/diegosouzapw/OmniRoute/pull/11753)). +- **fix(providers):** Zed Hosted streaming failures now trigger fallback before content and end partial streams with a sanitized structured error instead of fake assistant text and a normal-success stop. ([#12455](https://github.com/diegosouzapw/OmniRoute/pull/12455)) +- **fix(test):** capture hook result after render (direct commit `b85d0abb0b`) +- **fix(sync):** repair the two regressions the v3.8.50 sync-back left on release/v3.8.51 (direct commit `5ade9e0851`) +- **revert(models):** drop the memo reset hook ported from #10055 (direct commit `282de841ae`) +- **fix(typecheck):** guard the optional connectionId before the reactive model sync (direct commit `f2825bad75`) +- **fix(quota-share):** release the winner's reserved in-flight slot (#11371) ([#11408](https://github.com/diegosouzapw/OmniRoute/pull/11408)) — thanks @oyi77 +- **fix(providers):** stop silently dropping reasoning effort on opencode families (#10788) ([#11409](https://github.com/diegosouzapw/OmniRoute/pull/11409)) — thanks @oyi77 +- **fix(combo):** enable genuine quota-aware routing for generic providers (antigravity, claude, etc.) ([#11411](https://github.com/diegosouzapw/OmniRoute/pull/11411)) — thanks @HouMinXi +- **fix(oauth):** allow expired token retry before deactivation and activate expired retry loop (#P0) ([#11414](https://github.com/diegosouzapw/OmniRoute/pull/11414)) — thanks @HouMinXi +- **perf(executors):** lazy-load the executor registry — defer class imports + construction to first use (#11220) ([#11421](https://github.com/diegosouzapw/OmniRoute/pull/11421)) — thanks @oyi77 +- **fix:** make release/v3.8.50 CI gates green (run 32786966560 root causes) ([#11450](https://github.com/diegosouzapw/OmniRoute/pull/11450)) — thanks @jonlwheat2-gif +- **fix(sse):** exact-domain cookie match and origin-equality URL assertions ([#11452](https://github.com/diegosouzapw/OmniRoute/pull/11452)) — thanks @hartmark +- **fix(providers):** send Responses API payload during OpenAI-compatible validation ([#11454](https://github.com/diegosouzapw/OmniRoute/pull/11454)) — thanks @tuandinh0801 +- **fix(command-code):** fallback to /alpha/generate for Go plan without Provider API access ([#11455](https://github.com/diegosouzapw/OmniRoute/pull/11455)) — thanks @Chewji9875 +- **fix(responses-continuation):** fail closed on a log-truncated stored input/output array ([#11473](https://github.com/diegosouzapw/OmniRoute/pull/11473)) — thanks @hartmark +- **fix(cli):** write jcode settings as [providers.omniroute] in config.toml ([#11484](https://github.com/diegosouzapw/OmniRoute/pull/11484)) — thanks @hongnoul +- **fix(logging):** raise chat-log array truncation cap to 1000 and unify duplicate implementations ([#11499](https://github.com/diegosouzapw/OmniRoute/pull/11499)) — thanks @hartmark +- **fix:** restore live websocket URL helpers ([#11509](https://github.com/diegosouzapw/OmniRoute/pull/11509)) — thanks @PauloFH +- **perf(core):** eliminate async waterfalls across home, cache, and db operations (#11396) ([#11512](https://github.com/diegosouzapw/OmniRoute/pull/11512)) — thanks @HouMinXi +- **fix(kiro):** add runtime.us-east-1.kiro.dev as first-attempt endpoint ([#11517](https://github.com/diegosouzapw/OmniRoute/pull/11517)) — thanks @rifqiawl +- **fix(providers):** remove duplicate 128-tool truncation in OpencodeExecutor (#11444) ([#11518](https://github.com/diegosouzapw/OmniRoute/pull/11518)) — thanks @HouMinXi +- **fix(providers):** poll onboardUser LRO before classifying BYOP ([#11519](https://github.com/diegosouzapw/OmniRoute/pull/11519)) — thanks @rifqiawl +- **fix(translator):** preserve exact tool name casing for Claude tool_use payloads (#11487) ([#11520](https://github.com/diegosouzapw/OmniRoute/pull/11520)) — thanks @HouMinXi +- **fix(kimi-web):** align health probe with executor domain ([#11521](https://github.com/diegosouzapw/OmniRoute/pull/11521)) — thanks @pacocartones +- **fix(cli):** preserve existing admin password during setup ([#11522](https://github.com/diegosouzapw/OmniRoute/pull/11522)) — thanks @pacocartones +- **fix(mitm):** keep Windows DNS elevation off the POSIX sudo path (#11430) ([#11542](https://github.com/diegosouzapw/OmniRoute/pull/11542)) — thanks @official-burak +- **fix(sse):** scale chat admission by ingest byte budget instead of a fixed request count ([#11548](https://github.com/diegosouzapw/OmniRoute/pull/11548)) — thanks @TheDemonTuan +- **fix:** accept Gemini Web cookie exports ([#11554](https://github.com/diegosouzapw/OmniRoute/pull/11554)) — thanks @rafacpti23 +- **fix(i18n):** complete Brazilian Portuguese translations ([#11558](https://github.com/diegosouzapw/OmniRoute/pull/11558)) — thanks @rafacpti23 +- **fix(sse):** explicit types for openai-responses pureHelpers — clears last failing core typecheck gate ([#11567](https://github.com/diegosouzapw/OmniRoute/pull/11567)) — thanks @jonlwheat2-gif +- **fix(opencode):** hydrate Proxy Pool references for no-auth connections ([#11584](https://github.com/diegosouzapw/OmniRoute/pull/11584)) — thanks @AStupidBear +- **fix(ci):** resolve inherited release/v3.8.51 base-red CI failures ([#11608](https://github.com/diegosouzapw/OmniRoute/pull/11608)) — thanks @jonlwheat2-gif +- **fix(db):** fallback load STORAGE_ENCRYPTION_KEY from env files ([#11614](https://github.com/diegosouzapw/OmniRoute/pull/11614)) — thanks @benzntech +- **fix(providers):** mark Brave Search as no longer having a free tier ([#11621](https://github.com/diegosouzapw/OmniRoute/pull/11621)) — thanks @turbolego +- **fix:** pass tool result content through as string in OpenAI/Claude -> Gemini translation ([#11624](https://github.com/diegosouzapw/OmniRoute/pull/11624)) — thanks @Hsia97 +- **fix(sse):** scope credential health to exact key ([#11628](https://github.com/diegosouzapw/OmniRoute/pull/11628)) — thanks @KaspaPulse +- **fix(install):** guard prepare script against missing husky ([#11634](https://github.com/diegosouzapw/OmniRoute/pull/11634)) — thanks @Prajeeth-12 +- **fix(security):** redact NVIDIA_API_KEY at the log sink in the ad-hoc diag script ([#11639](https://github.com/diegosouzapw/OmniRoute/pull/11639)) +- **fix(types):** clear the two type regressions red on release/v3.8.51 ([#11640](https://github.com/diegosouzapw/OmniRoute/pull/11640)) — thanks @jonlwheat2-gif +- **fix(build):** resolve workspace: protocol dependencies to concrete semver versions during prepublish (#11540) ([#11641](https://github.com/diegosouzapw/OmniRoute/pull/11641)) — thanks @HouMinXi +- **fix(search):** properly resolve configured search connection in /v1/responses pipeline (#11524) ([#11642](https://github.com/diegosouzapw/OmniRoute/pull/11642)) — thanks @HouMinXi +- **fix(authz):** trust cf-connecting-ip for via-proxy detection when peer is Cloudflare edge (#11514) ([#11643](https://github.com/diegosouzapw/OmniRoute/pull/11643)) — thanks @HouMinXi +- **fix(security):** compare the OIDC state cookie in constant time ([#11646](https://github.com/diegosouzapw/OmniRoute/pull/11646)) +- **fix(security):** namespace the dedup hash by the calling API key ([#11649](https://github.com/diegosouzapw/OmniRoute/pull/11649)) +- **fix(models):** gate the Codex-native catalog loop on prefix mode (#11632) ([#11662](https://github.com/diegosouzapw/OmniRoute/pull/11662)) — thanks @xiaoyaner0201 +- **fix(dashboard):** project Codex routing quota in account pools ([#11666](https://github.com/diegosouzapw/OmniRoute/pull/11666)) — thanks @KaspaPulse +- **fix(ddgw):** normaliza system no upstream da release v3.8.51 ([#11692](https://github.com/diegosouzapw/OmniRoute/pull/11692)) — thanks @RhianB14 +- **fix(memory):** honor category filter in GET /api/memory ([#11699](https://github.com/diegosouzapw/OmniRoute/pull/11699)) — thanks @Sabeekhann +- **fix(ci):** detect stale workspace lockfile entries ([#11714](https://github.com/diegosouzapw/OmniRoute/pull/11714)) — thanks @pacocartones +- **fix(ci):** stamp dist/BUILD_SHA before the npm publish provenance gate ([#11721](https://github.com/diegosouzapw/OmniRoute/pull/11721)) +- **fix(docker):** decouple the best-effort Bun image from the release manifest ([#11724](https://github.com/diegosouzapw/OmniRoute/pull/11724)) +- **perf(compression):** avoid cloning memo misses twice ([#11727](https://github.com/diegosouzapw/OmniRoute/pull/11727)) — thanks @RaviTharuma +- **fix(docker):** let the bun digest artifact be absent, not fatal ([#11740](https://github.com/diegosouzapw/OmniRoute/pull/11740)) +- **fix(shared):** block cloud-metadata hosts under default remote-image guard ([#11755](https://github.com/diegosouzapw/OmniRoute/pull/11755)) — thanks @santosraju99-hub +- **fix(api):** preserve caller-provided X-Correlation-Id on chat completions ([#11760](https://github.com/diegosouzapw/OmniRoute/pull/11760)) — thanks @aniruddhaadak80 +- **fix(resilience):** lock permanently retired models instead of short backoff (Gemini ban prevention) ([#11762](https://github.com/diegosouzapw/OmniRoute/pull/11762)) — thanks @turbolego +- **fix:** mark Vercel AI Gateway as passthroughModels ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **fix(resilience):** stop hammering permanently-moved endpoints and billing-suspended accounts ([#11774](https://github.com/diegosouzapw/OmniRoute/pull/11774)) — thanks @turbolego +- **fix(lease):** remove global static reservation and gate routing on live active lease occupancy ([#11775](https://github.com/diegosouzapw/OmniRoute/pull/11775)) — thanks @TheDemonTuan +- **fix(ci):** size the install-upgrade gate to a measured run, and log the pack cost ([#11776](https://github.com/diegosouzapw/OmniRoute/pull/11776)) +- **fix(pwa):** stop serving the stale shell after deploys ([#11779](https://github.com/diegosouzapw/OmniRoute/pull/11779)) — thanks @HouMinXi +- **fix(combo):** lock GitHub models rejected as "not supported" for future requests ([#11781](https://github.com/diegosouzapw/OmniRoute/pull/11781)) — thanks @turbolego +- **fix(cli):** probe both IPv4 and IPv6 loopback for server readiness (#11766) ([#11794](https://github.com/diegosouzapw/OmniRoute/pull/11794)) — thanks @vermasomesh835 +- **fix(sse):** preserve CLIProxyAPI wrappers per connection ([#11797](https://github.com/diegosouzapw/OmniRoute/pull/11797)) — thanks @RaviTharuma +- **fix(providers):** honor model auto-fetch opt-in on create ([#11798](https://github.com/diegosouzapw/OmniRoute/pull/11798)) — thanks @RaviTharuma +- **fix(dashboard):** make model auto-fetch order independent ([#11805](https://github.com/diegosouzapw/OmniRoute/pull/11805)) — thanks @RaviTharuma +- **fix(docker):** make GHA cache exports best effort ([#11806](https://github.com/diegosouzapw/OmniRoute/pull/11806)) — thanks @RaviTharuma +- **fix(kiro):** do not permanently ban on 'User is not authorized to make this call' ([#11809](https://github.com/diegosouzapw/OmniRoute/pull/11809)) — thanks @Deftera186 +- **fix(sse):** keep cache-write tokens in OpenAI-shaped usage ([#11814](https://github.com/diegosouzapw/OmniRoute/pull/11814)) — thanks @TheDemonTuan +- **fix(docs):** resolve relative markdown and wiki links across Fumadocs and GitHub wiki ([#11834](https://github.com/diegosouzapw/OmniRoute/pull/11834)) — thanks @AndrianBalanescu +- **fix(chat):** preserve unstripped model string for passthrough provider routing ([#11840](https://github.com/diegosouzapw/OmniRoute/pull/11840)) — thanks @raheemuddin786 +- **fix(mcp):** avoid async initialization inside sync esm wrappers (#11569) ([#11849](https://github.com/diegosouzapw/OmniRoute/pull/11849)) — thanks @HouMinXi +- **fix(sse):** preserve coherent markdown token boundaries across stream translation chunks (#11606) ([#11851](https://github.com/diegosouzapw/OmniRoute/pull/11851)) — thanks @HouMinXi +- **fix(ci):** run the install-upgrade gate on disk, not on the /tmp tmpfs ([#11855](https://github.com/diegosouzapw/OmniRoute/pull/11855)) +- **fix(skills):** expand shorthand property types in injected tool schemas ([#11857](https://github.com/diegosouzapw/OmniRoute/pull/11857)) — thanks @alvinveroy +- **fix(ci):** publish npm from a hosted runner so provenance is accepted ([#11877](https://github.com/diegosouzapw/OmniRoute/pull/11877)) +- **fix(lint):** pin react hooks plugin to 7.0.1 ([#11886](https://github.com/diegosouzapw/OmniRoute/pull/11886)) +- **fix(ci):** surface and clear release-green ESLint failure ([#11890](https://github.com/diegosouzapw/OmniRoute/pull/11890)) — thanks @MumuTW +- **fix(guardrails):** prevent duplicate prompt-injection-guard log output ([#11936](https://github.com/diegosouzapw/OmniRoute/pull/11936)) — thanks @patrykkopycinski +- **fix(auth):** downgrade expected transient states from warn to debug ([#11937](https://github.com/diegosouzapw/OmniRoute/pull/11937)) — thanks @patrykkopycinski +- **fix(codex):** fail over image generation for imported free plans ([#11948](https://github.com/diegosouzapw/OmniRoute/pull/11948)) — thanks @Bl0ck154 +- **fix(codex):** normalize non-stream responses ([#11951](https://github.com/diegosouzapw/OmniRoute/pull/11951)) — thanks @Bl0ck154 +- **fix(images):** forward Antigravity image size ([#11952](https://github.com/diegosouzapw/OmniRoute/pull/11952)) — thanks @Bl0ck154 +- **fix(db):** invalidate connection cache after upsert ([#11953](https://github.com/diegosouzapw/OmniRoute/pull/11953)) — thanks @Bl0ck154 +- **fix(codex):** restore imported account state ([#11954](https://github.com/diegosouzapw/OmniRoute/pull/11954)) — thanks @Bl0ck154 +- **fix(sse):** give extended-thinking targets the reasoning readiness budget ([#11959](https://github.com/diegosouzapw/OmniRoute/pull/11959)) — thanks @abhisheksharma2411 +- **fix(dashboard):** use opaque background and readable text color on cost chart tooltips ([#11960](https://github.com/diegosouzapw/OmniRoute/pull/11960)) — thanks @ZaimMarzuki +- **fix(antigravity):** send complete loadCodeAssist metadata (ideType/platform/pluginType as numeric enums) ([#11969](https://github.com/diegosouzapw/OmniRoute/pull/11969)) — thanks @rifqiawl +- **fix(core):** resolve DB init race condition and reasoning translation ([#12003](https://github.com/diegosouzapw/OmniRoute/pull/12003)) — thanks @raheemuddin786 +- **fix(admission):** exclude reclaimable page cache from the cgroup pressure ratio ([#12017](https://github.com/diegosouzapw/OmniRoute/pull/12017)) — thanks @HouMinXi +- **fix(build):** prune non-production trees in NFT trace excludes and tsconfig ([#12028](https://github.com/diegosouzapw/OmniRoute/pull/12028)) — thanks @Chewji9875 +- **fix(migrations):** add renamed migration compatibility for 056/073/077/101 ([#12036](https://github.com/diegosouzapw/OmniRoute/pull/12036)) — thanks @oyi77 +- **fix(sse):** default crash-guard logger to console.warn, not console ([#12042](https://github.com/diegosouzapw/OmniRoute/pull/12042)) — thanks @HouMinXi +- **fix(catalog):** derive vision/modalities for built-in auto combos from effective target pool ([#12046](https://github.com/diegosouzapw/OmniRoute/pull/12046)) — thanks @Prajeeth-12 +- **fix(release):** the packaged-app smoke verifies the database opened, not a driver line the primary path never prints (twin of #12032) ([#12047](https://github.com/diegosouzapw/OmniRoute/pull/12047)) +- **fix(barrel):** migrate src/app/ off the @/lib/localDb barrel import (#11795 Phase 2) ([#12051](https://github.com/diegosouzapw/OmniRoute/pull/12051)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate src/lib/ off the localDb barrel to direct db imports (#59) ([#12052](https://github.com/diegosouzapw/OmniRoute/pull/12052)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate open-sse, src/shared, src/sse, src/models, src/domain off the @/lib/localDb barrel import (#11795 Phase 4) ([#12053](https://github.com/diegosouzapw/OmniRoute/pull/12053)) — thanks @jonlwheat2-gif +- **fix(barrel):** delete the @/lib/localDb barrel — every consumer migrated (#11795 Phase 5) ([#12055](https://github.com/diegosouzapw/OmniRoute/pull/12055)) — thanks @jonlwheat2-gif +- **fix(radar):** restore D12 public boundary ([#12057](https://github.com/diegosouzapw/OmniRoute/pull/12057)) +- **fix(dev):** bound webpack and Tailwind scans ([#12075](https://github.com/diegosouzapw/OmniRoute/pull/12075)) — thanks @backryun +- **fix(dev):** isolate root layout settings reads ([#12076](https://github.com/diegosouzapw/OmniRoute/pull/12076)) — thanks @backryun +- **fix(dev):** reduce instrumentation executor fan-out (phase 3) ([#12078](https://github.com/diegosouzapw/OmniRoute/pull/12078)) — thanks @backryun +- **fix(dev):** make logging resources HMR-singleton ([#12079](https://github.com/diegosouzapw/OmniRoute/pull/12079)) — thanks @backryun +- **fix(sse):** keep unavailable forced connections scoped ([#12080](https://github.com/diegosouzapw/OmniRoute/pull/12080)) — thanks @keeltrace +- **fix(dev):** isolate batch dispatch from instrumentation ([#12081](https://github.com/diegosouzapw/OmniRoute/pull/12081)) — thanks @backryun +- **fix(ci):** reconcile release test contract drift ([#12082](https://github.com/diegosouzapw/OmniRoute/pull/12082)) — thanks @backryun +- **fix(ci):** accept CVE-2025-68121 in the prebuilt tls-client .so, auto-close base-red issues, guard Scorecard on the default branch ([#12085](https://github.com/diegosouzapw/OmniRoute/pull/12085)) +- **fix(db):** drop three consumer-less 1proxy exports — dead-code base-red on release/v3.8.51 after the barrel deletion (#12055) ([#12087](https://github.com/diegosouzapw/OmniRoute/pull/12087)) +- **fix(api):** clear the six API-route TypeScript regressions the new gate landed red on ([#12094](https://github.com/diegosouzapw/OmniRoute/pull/12094)) +- **fix(release):** never let the tag-push Create Release append auto notes to the curated body ([#12096](https://github.com/diegosouzapw/OmniRoute/pull/12096)) +- **fix(sse):** honor CLIProxyAPI environment API key ([#12099](https://github.com/diegosouzapw/OmniRoute/pull/12099)) — thanks @RaviTharuma +- **fix(oauth):** bind Google refresh to the client that issued the token ([#12106](https://github.com/diegosouzapw/OmniRoute/pull/12106)) — thanks @HouMinXi +- **fix(dashboard):** make RequestLoggerDetail loadable outside Next — CSS via globals.css + CJS/ESM interop (#11703 base-reds) ([#12114](https://github.com/diegosouzapw/OmniRoute/pull/12114)) +- **fix(leases):** project status lease row to lease columns so joined connection PII never escapes ([#12115](https://github.com/diegosouzapw/OmniRoute/pull/12115)) — thanks @geek007git +- **fix(plugins):** do not kill the plugin process when a fire-and-forget hook times out ([#12116](https://github.com/diegosouzapw/OmniRoute/pull/12116)) — thanks @geek007git +- **fix(guardrails):** restore injection-guard logging on middleware-only routes ([#12117](https://github.com/diegosouzapw/OmniRoute/pull/12117)) — thanks @geek007git +- **fix(plugins):** refresh stored manifest from disk on activate so new hook fields reach existing installs ([#12120](https://github.com/diegosouzapw/OmniRoute/pull/12120)) — thanks @geek007git +- **fix(codex):** preserve existing provider state when bulk-import upserts a matching connection ([#12122](https://github.com/diegosouzapw/OmniRoute/pull/12122)) — thanks @geek007git +- **fix(ci):** clear the two base-reds the 2026-08-30 merge batch left on release/v3.8.51 (round 3) ([#12123](https://github.com/diegosouzapw/OmniRoute/pull/12123)) +- **fix(sse):** strip type:'custom' from Claude tools on agentrouter dispatch ([#12126](https://github.com/diegosouzapw/OmniRoute/pull/12126)) — thanks @wahidsadik371-coder +- **fix(sse):** stop advertising video providers the dispatcher cannot run ([#12131](https://github.com/diegosouzapw/OmniRoute/pull/12131)) — thanks @quiterunner-commits +- **fix(search):** treat HTTP 432 and plan limit errors as transient cooldown ([#12139](https://github.com/diegosouzapw/OmniRoute/pull/12139)) — thanks @HouMinXi +- **fix(sse):** refuse an AI Horde queue that cannot fit the request budget ([#12143](https://github.com/diegosouzapw/OmniRoute/pull/12143)) — thanks @quiterunner-commits +- **fix(ci):** clear the base-reds the afternoon merge batch left on release/v3.8.51 (round 5: provider count 352, TS2554/TS2677) ([#12144](https://github.com/diegosouzapw/OmniRoute/pull/12144)) +- **fix(translator):** strip neutral tool_choice when tools absent in Responses-to-Chat (#12141) ([#12166](https://github.com/diegosouzapw/OmniRoute/pull/12166)) — thanks @HouMinXi +- **fix(guardrails):** pass providerId to getResolvedModelCapabilities in checkComboVision (#12112) ([#12169](https://github.com/diegosouzapw/OmniRoute/pull/12169)) — thanks @HouMinXi +- **fix(translator):** strip plaintext reasoning content for opaque responses backends (#12128) ([#12171](https://github.com/diegosouzapw/OmniRoute/pull/12171)) — thanks @HouMinXi +- **perf(stream):** compile hot-path regexes once, bound token caches, fix quadratic buffering ([#12179](https://github.com/diegosouzapw/OmniRoute/pull/12179)) — thanks @opensource-elearning +- **fix:** resolve compression worker file using runtime anchors instead of… ([#12183](https://github.com/diegosouzapw/OmniRoute/pull/12183)) — thanks @NightStalker-87 +- **fix:** prevent Claude Code session kills via liveness-aware readiness + auto model echo ([#12189](https://github.com/diegosouzapw/OmniRoute/pull/12189)) — thanks @opensource-elearning +- **fix(translator):** preserve falsy primitive values in Gemini and Antigravity function response results ([#12191](https://github.com/diegosouzapw/OmniRoute/pull/12191)) — thanks @Siva010 +- **fix(oauth):** mark empty Antigravity projectId as degraded and clear stale errors (#11284) ([#12205](https://github.com/diegosouzapw/OmniRoute/pull/12205)) — thanks @HouMinXi +- **fix(providers):** gate the Codex auto-ping usage read on the shared quota throttle ([#12209](https://github.com/diegosouzapw/OmniRoute/pull/12209)) — thanks @abhisheksharma2411 +- **fix(combos):** prioritize SQLite row id over inner JSON id and notify delete errors ([#12213](https://github.com/diegosouzapw/OmniRoute/pull/12213)) — thanks @HouMinXi +- **fix(providers):** mark featherless.ai as no longer having a free tier (`hasFree: false`) ([#12216](https://github.com/diegosouzapw/OmniRoute/pull/12216)) — thanks @turbolego +- **fix(deepseek-web):** stop Turbopack dev panic in the PoW worker path resolver ([#12221](https://github.com/diegosouzapw/OmniRoute/pull/12221)) — thanks @hartmark +- **fix(chat-admission):** clarify local 503 source ([#12223](https://github.com/diegosouzapw/OmniRoute/pull/12223)) — thanks @keeltrace +- **fix(dev):** silence webpack runtime module warnings ([#12228](https://github.com/diegosouzapw/OmniRoute/pull/12228)) — thanks @backryun +- **fix(db):** use module.require for CommonJS runtime driver loading ([#12230](https://github.com/diegosouzapw/OmniRoute/pull/12230)) — thanks @raheemuddin786 +- **fix(memory):** honest probe-driven FTS5 keyword status + memory_id rowid sync ([#12231](https://github.com/diegosouzapw/OmniRoute/pull/12231)) — thanks @raheemuddin786 +- **fix(system):** propagate abort signal to stream reader in HTTP version checks ([#12232](https://github.com/diegosouzapw/OmniRoute/pull/12232)) — thanks @raheemuddin786 +- **fix(resilience):** isolate local host execution errors from provider circuit breakers ([#12233](https://github.com/diegosouzapw/OmniRoute/pull/12233)) — thanks @raheemuddin786 +- **fix(sse):** sort injected tools deterministically for prompt caching ([#12234](https://github.com/diegosouzapw/OmniRoute/pull/12234)) — thanks @raheemuddin786 +- **fix(combo):** always clear the loop-safety timer, not just on the happy path (#11804) ([#12245](https://github.com/diegosouzapw/OmniRoute/pull/12245)) +- **fix(quality):** base-red pair — stryker tap registration + turn-pin suites aligned to the window gate ([#12255](https://github.com/diegosouzapw/OmniRoute/pull/12255)) — thanks @backryun +- **fix(dev):** qualify Turbopack runtime boundaries ([#12258](https://github.com/diegosouzapw/OmniRoute/pull/12258)) — thanks @backryun +- **fix(combo):** fall back to full pool when collapsed sole survivor is context-too-small ([#12278](https://github.com/diegosouzapw/OmniRoute/pull/12278)) — thanks @opensource-elearning +- **fix(combo):** bound the pre-dispatch unavailable skip so a stale label cannot dark a pool (#12168) ([#12285](https://github.com/diegosouzapw/OmniRoute/pull/12285)) +- **fix(security):** strip Qwen/Alibaba console-session cookies from provider API responses ([#12287](https://github.com/diegosouzapw/OmniRoute/pull/12287)) +- **fix(usage):** console-aware Token Plan guidance + subscription hint on bailian 401 ([#12288](https://github.com/diegosouzapw/OmniRoute/pull/12288)) +- **fix(resourcePressure):** log numeric detail on every rejection, recover faster ([#12293](https://github.com/diegosouzapw/OmniRoute/pull/12293)) — thanks @hartmark +- **fix(release):** drain the 2026-09-01 base-red window — passthrough usage regression + radarPage i18n keys ([#12327](https://github.com/diegosouzapw/OmniRoute/pull/12327)) +- **fix(ci):** openapi-security-tiers checker must honor routeGuard patterns + imported prefixes ([#12350](https://github.com/diegosouzapw/OmniRoute/pull/12350)) — thanks @ggiak +- **perf(ci):** cache node_modules in the npm-ci-retry composite (#8084 D3) ([#12408](https://github.com/diegosouzapw/OmniRoute/pull/12408)) +- **revert(ui):** point CTAs back at their real destinations ([#12410](https://github.com/diegosouzapw/OmniRoute/pull/12410)) +- **fix(release):** drain the 2026-09-02 base-red — rerank-providers import + api-typecheck baseline ratchet ([#12414](https://github.com/diegosouzapw/OmniRoute/pull/12414)) +- **fix(types):** make system prompt injection noImplicitAny-safe ([#12416](https://github.com/diegosouzapw/OmniRoute/pull/12416)) +- **fix(quality):** validate typecheck baseline schema ([#12419](https://github.com/diegosouzapw/OmniRoute/pull/12419)) +- **fix(memory):** resolve rerank provider node cache import ([#12421](https://github.com/diegosouzapw/OmniRoute/pull/12421)) +- **fix(ci):** repair release-root regressions — pack dedup, web-session syntax, uc-image ids ([#12423](https://github.com/diegosouzapw/OmniRoute/pull/12423)) — thanks @backryun +- **fix(providers):** separate MaxAI and UC credential contracts ([#12431](https://github.com/diegosouzapw/OmniRoute/pull/12431)) +- **fix(cli):** preserve Claude settings on config set ([#12432](https://github.com/diegosouzapw/OmniRoute/pull/12432)) — thanks @KrzysiekSko +- **fix(providers):** repair the maxai credential block truncated by merge auto-resolve ([#12433](https://github.com/diegosouzapw/OmniRoute/pull/12433)) +- **fix(quality):** record the 2026-09-02 merged growth in the file-size baseline ([#12434](https://github.com/diegosouzapw/OmniRoute/pull/12434)) +- **fix(uc):** route UC error strings through sanitizeErrorMessage; allowlist the retired codex id ([#12437](https://github.com/diegosouzapw/OmniRoute/pull/12437)) +- **fix(adapta):** redact streamed upstream errors ([#12438](https://github.com/diegosouzapw/OmniRoute/pull/12438)) +- **fix(sse):** remap non-contiguous upstream tool_calls index to a gap-free output_index ([#12445](https://github.com/diegosouzapw/OmniRoute/pull/12445)) — thanks @hartmark +- **fix(quality):** detect duplicate tool_calls entries in one response ([#12446](https://github.com/diegosouzapw/OmniRoute/pull/12446)) — thanks @hartmark +- **fix(conversations):** resolve turn content OmniRoute never sends back to the client ([#12447](https://github.com/diegosouzapw/OmniRoute/pull/12447)) — thanks @hartmark +- **fix(combo):** do not treat credits-exhausted 401 as auth skip ([#12449](https://github.com/diegosouzapw/OmniRoute/pull/12449)) — thanks @RaviTharuma +- **fix(auth):** do not park healthy quota accounts as expired ([#12452](https://github.com/diegosouzapw/OmniRoute/pull/12452)) — thanks @RaviTharuma +- **fix(responses-continuation):** fail closed on a collector-truncated, empty output array ([#12460](https://github.com/diegosouzapw/OmniRoute/pull/12460)) — thanks @hartmark +- **fix(logging):** raise the SSE payload collector's default cap ([#12461](https://github.com/diegosouzapw/OmniRoute/pull/12461)) — thanks @hartmark +- **fix(resilience):** surface Responses failed.error.message in 502s ([#12472](https://github.com/diegosouzapw/OmniRoute/pull/12472)) — thanks @RaviTharuma +- **fix(security):** unbiased maxai X-Random nonce + stricter URL/regex assertions ([#12502](https://github.com/diegosouzapw/OmniRoute/pull/12502)) +- **fix(video):** re-anchor transcript log-redaction so PII/credential maskers can't reopen the leak (#12150 P1 follow-up) ([#12503](https://github.com/diegosouzapw/OmniRoute/pull/12503)) +- **fix(monitoring):** serve cached credentialHealth off the request path ([#12533](https://github.com/diegosouzapw/OmniRoute/pull/12533)) — thanks @RaviTharuma +- **fix(quota):** keep Antigravity Gemini usable when Claude weekly is empty ([#12566](https://github.com/diegosouzapw/OmniRoute/pull/12566)) — thanks @HouMinXi +- **fix(authz):** hard-gate every credential export and CLI-config write (GHSA-5926-2w35-7h4q) ([#12600](https://github.com/diegosouzapw/OmniRoute/pull/12600)) +- **fix(docs):** restore the Next build — REMOVED_PROVIDERS.md had no frontmatter (base-red #12581) ([#12610](https://github.com/diegosouzapw/OmniRoute/pull/12610)) +- **fix(quality):** re-point the zcodeProtocol public-creds allowlist to line 313 ([#12615](https://github.com/diegosouzapw/OmniRoute/pull/12615)) +- **fix(security):** close 3 advisories — search baseUrl exfil, sk- in the error sanitizer, bifrost relay header leak ([#12620](https://github.com/diegosouzapw/OmniRoute/pull/12620)) +- **fix(providers):** sanitize boolean required and nested bare maps for Gemini (#12269) ([#12624](https://github.com/diegosouzapw/OmniRoute/pull/12624)) — thanks @HouMinXi +- **fix(resilience):** keep Overloaded STREAM_EARLY_EOF off the provider breaker ([#12626](https://github.com/diegosouzapw/OmniRoute/pull/12626)) — thanks @HouMinXi +- **fix(catalog):** re-audit free-tier quotas against official pages ([#12649](https://github.com/diegosouzapw/OmniRoute/pull/12649)) +- **fix(dashboard):** keep a request's pending-tracking id stable across combo target retries ([#12650](https://github.com/diegosouzapw/OmniRoute/pull/12650)) — thanks @hartmark +- **fix(tests):** retire dead model ids from the chat-pipeline integration suite (base-red #12581) ([#12670](https://github.com/diegosouzapw/OmniRoute/pull/12670)) +- **fix:** resolve SqliteError no such table compression_run_telemetry during cleanup ([#12682](https://github.com/diegosouzapw/OmniRoute/pull/12682)) — thanks @groovecityJO +- **fix(combo):** treat a pin-only step as implicit connection allowlist ([#12697](https://github.com/diegosouzapw/OmniRoute/pull/12697)) — thanks @HouMinXi +- **fix(ci):** ship .npmrc in published package so legacy-peer-deps applies to consumers (#11544) ([#12699](https://github.com/diegosouzapw/OmniRoute/pull/12699)) — thanks @KooshaPari +- **fix(docker):** pin 4 CLI tools to exact versions (#12576) ([#12703](https://github.com/diegosouzapw/OmniRoute/pull/12703)) — thanks @KooshaPari +- **fix(docker):** document and harden cli profile trust boundary (#12570) ([#12706](https://github.com/diegosouzapw/OmniRoute/pull/12706)) — thanks @KooshaPari +- **fix(sse):** redact video transcript in pre-guardrail rejected-request logs (#12150 P2 item 7) ([#12710](https://github.com/diegosouzapw/OmniRoute/pull/12710)) +- **fix(i18n):** quote placeholder in OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES description (#12505) ([#12769](https://github.com/diegosouzapw/OmniRoute/pull/12769)) — thanks @KooshaPari +- **fix(api/pricing):** surface validation error message as string, not raw object (#12494) ([#12771](https://github.com/diegosouzapw/OmniRoute/pull/12771)) — thanks @KooshaPari +- **fix(sse):** re-enable prompt compression for native Codex passthrough ([#12834](https://github.com/diegosouzapw/OmniRoute/pull/12834)) — thanks @thomasmaerz +- **fix(dashboard):** model health tests for a provider node set to the Responses API now call `/v1/responses` with a Responses-shaped body instead of `/v1/chat/completions` — those models were reported as `Provider returned HTTP 200 but no text content` even though the same model answered normally through `/v1/responses` ([#13070](https://github.com/diegosouzapw/OmniRoute/issues/13070)) ([#13087](https://github.com/diegosouzapw/OmniRoute/pull/13087)) — thanks @ntdatt812 +- fix(routing): stop a round-robin combo's "opencode" targets from collapsing onto the opencode-zen connection (#11912) ([#13283](https://github.com/diegosouzapw/OmniRoute/pull/13283)) +- fix(routing): stop the reactive-compaction debug log from lying when compression is globally disabled (#11977) ([#13276](https://github.com/diegosouzapw/OmniRoute/pull/13276)) +- fix(electron): relativize standalone-bundle symlink targets so Stage 8 manifest verification stops failing on Windows (#11979) ([#13251](https://github.com/diegosouzapw/OmniRoute/pull/13251)) +- fix(dashboard): surface a visible error when Compression Studio's combined preview run fails (#12061) ([#13288](https://github.com/diegosouzapw/OmniRoute/pull/13288)) +- fix(dashboard): make the compression "Effective pipeline" preview honor the active profile and warn when the master switch is off (#12063) ([#13287](https://github.com/diegosouzapw/OmniRoute/pull/13287)) +- fix(providers): scope TinyCMS Web signer's DOM shims to each call instead of leaking them for the process lifetime, and surface a clean HTTP status on a non-JSON interception-toggles error (#12072) ([#13254](https://github.com/diegosouzapw/OmniRoute/pull/13254)) +- fix(guardrails): stop Vision Bridge from re-selecting a model locked after a 404 (#12111) ([#13259](https://github.com/diegosouzapw/OmniRoute/pull/13259)) +- fix(sse): require Responses-shaped body before native OpenAI-compatible passthrough (#12129) ([#13278](https://github.com/diegosouzapw/OmniRoute/pull/13278)) +- fix(providers): minimax-m3 now collapses manual thinking.type:"enabled" to adaptive, preventing upstream 400 (2013) (#12132) ([#13249](https://github.com/diegosouzapw/OmniRoute/pull/13249)) +- fix(db): scope model visibility overrides by modality so hiding a Chat model no longer hides an identically-ID'd Image/Embeddings/etc. model (#12172) ([#13258](https://github.com/diegosouzapw/OmniRoute/pull/13258)) +- fix(db): scope local-provider apiKey dedup to matching base URL so LM Studio/Ollama-style connections support multiple accounts (#12173) ([#13250](https://github.com/diegosouzapw/OmniRoute/pull/13250)) +- fix(providers): refresh Trae's stale Referer/Origin and forward user timezone so imported connections stop failing with 401 (#12190) ([#13255](https://github.com/diegosouzapw/OmniRoute/pull/13255)) +- fix(providers): route opencode-go/gpt-5.6-luna to /responses instead of /chat/completions (#12196) ([#13275](https://github.com/diegosouzapw/OmniRoute/pull/13275)) +- fix(dashboard): allow deleting the last extra-upstream-header row even when invalid (#12251) ([#13252](https://github.com/diegosouzapw/OmniRoute/pull/13252)) +- **fix(i18n):** translate pre-existing `__MISSING__:` keys for `combo.sort`, `requestLogger.detail` expand/collapse, `common.profile`, and `settings.resilienceCredentialHealth*` across 39 locales ([#12272](https://github.com/diegosouzapw/OmniRoute/issues/12272)) ([#13034](https://github.com/diegosouzapw/OmniRoute/pull/13034)) — thanks @HouMinXi +- fix(cli): run the Node.js runtime compatibility guard before the heavy `tsx/esm` + Commander import chain so an unsupported runtime gets a clear message instead of a raw `Invalid regular expression flags` crash (#12296) ([#13253](https://github.com/diegosouzapw/OmniRoute/pull/13253)) +- fix(dashboard): refresh the providers list after deleting a compatible provider node (#12298) ([#13256](https://github.com/diegosouzapw/OmniRoute/pull/13256)) +- fix(usage): fail closed on API-key budget enforcement when a provider's `auto` routing alias has no pricing row, instead of silently counting it as $0 (#12341) ([#13257](https://github.com/diegosouzapw/OmniRoute/pull/13257)) +- **fix(providers):** include the submitted Agnes video model when polling by `video_id` ([#12356](https://github.com/diegosouzapw/OmniRoute/pull/12356)) — thanks @pacocartones +- **fix(routing):** custom OpenAI-compatible nodes now honor the saved Chat/Responses API type after edits instead of letting the node's original ID prefix override the live connection setting ([#11884](https://github.com/diegosouzapw/OmniRoute/issues/11884)). ([#12358](https://github.com/diegosouzapw/OmniRoute/pull/12358)) — thanks @pacocartones +- fix(sse): surface an error instead of a silent empty 200 when a Claude stream closes with zero bytes (#12398) ([#13285](https://github.com/diegosouzapw/OmniRoute/pull/13285)) +- fix(oauth): warn before the dead localhost:8080 redirect in antigravity/gemini `oauth start` (#12413) ([#13265](https://github.com/diegosouzapw/OmniRoute/pull/13265)) +- fix(providers): stop devin-cli spawn error from double-closing the SSE controller (#12517) ([#13261](https://github.com/diegosouzapw/OmniRoute/pull/13261)) +- **fix(rerank):** clamp Voyage `top_k` to the documents actually sent after empty-string filtering, and honor `return_documents: false` in the NVIDIA response adapter (#12523 — thanks @pacocartones) +- **fix(audio):** `/v1/audio/translations` now resolves combo names the way `/v1/audio/transcriptions` already does, so a combo that `GET /v1/models` advertises is fanned out to its targets instead of being rejected with `400 Invalid translation model: . Use format: provider/model`; literal `provider/model` ids and unknown bare names behave as before (#12536 — thanks @pacocartones) +- **fix(gemini):** strip the JSON-Schema-2020-12 `prefixItems` keyword from Gemini tool schemas at every nesting level, so Claude Code tool definitions no longer fail with `400 Unknown name "prefixItems"` on Gemini models (#12540 — thanks @pacocartones) +- **fix(api-manager):** Expose an accessible loading status while API keys are fetched instead of an empty accessibility tree (#12541 — thanks @pacocartones) +- **fix(video):** Clamp `estimateJpegFrameBytes` at zero for padding-only payloads and build the three encode-side frame data URIs from `JPEG_FRAME_DATA_URI_PREFIX` instead of a repeated literal (#12543 — thanks @pacocartones) +- **fix(devin):** accept Windows `DEVIN_AGENTIC_HOME` sandbox paths (`C:\...\.sandbox\...`) in the isolated-home check so the Devin Claude Bridge no longer fails closed on Windows ([#12405](https://github.com/diegosouzapw/OmniRoute/issues/12405)) (#12545 — thanks @pacocartones) +- **fix(db):** Add `conversation_turn_nodes` and orphaned `agentic_conversations` to the auto-cleanup cycle under the existing `retention.callLogs` window, so identity nodes whose call-log content has already been purged no longer accumulate without bound in `storage.sqlite` (#12548 — thanks @pacocartones) +- **fix(i18n):** Wrap the `~/.claude/profiles//settings.json` placeholder in ICU single quotes in the `OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES` feature-flag description across all 42 locales and the TypeScript default, so next-intl no longer fails with `INVALID_MESSAGE: UNCLOSED_TAG` and the Feature Flags card shows the description instead of the raw key (#12549 — thanks @pacocartones) +- **fix(orchestration):** `updateCloudAgentTask` now publishes the task's real `status` on `agent.task.updated` when an update only touches `result`, `activities` or `error`, instead of the fabricated `"updated"` state, and stays silent when no row matched the id (#12550 — thanks @pacocartones) +- **fix(i18n):** the home "Recent Requests" panel and the Provider Topology legend are now translated instead of rendering English copies on non-English dashboards; the legend reads its own `home.topologyLegend*` labels with consistent casing rather than borrowing the memory-settings "Recent" and analytics "Error" strings (#12551 — thanks @pacocartones). +- **docs(reference):** bring the `FEATURE_FLAGS.md` catalog back to 1:1 with `featureFlagDefinitions.ts` — 20 missing flags added, the two `*_BLOCK_THRESHOLD` env-only knobs moved out of the flag tables, category/total counts and the Live WS port corrected, guarded by a static test (#12552 — thanks @pacocartones) +- **fix(i18n):** backfill missing `usage.kiloPass*` strings in 39 locales and restore `featureFlagOmnirouteDisableThinkingLevelVariantsDescription` in `pt.json` ([#12561](https://github.com/diegosouzapw/OmniRoute/issues/12561)) ([#13027](https://github.com/diegosouzapw/OmniRoute/pull/13027)) — thanks @HouMinXi +- fix(docker): default docker-compose app ports (dashboard/API/live-WS) to loopback instead of `0.0.0.0`, closing the anonymous `/v1` LAN/WAN exposure gap left open by `REQUIRE_API_KEY=false` (#12568) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): close DNS-rebinding SSRF gap in webhook outbound-URL guard (#12569) ([#13243](https://github.com/diegosouzapw/OmniRoute/pull/13243)) +- fix(docker): require a per-session token on the VNC browser CDP bridge and isolate it on a dedicated Docker network (#12571) ([#13241](https://github.com/diegosouzapw/OmniRoute/pull/13241)) +- fix(open-sse): write Adobe Firefly session tokens and cookie jars with 0700/0600 permissions instead of the process umask (#12572) ([#13242](https://github.com/diegosouzapw/OmniRoute/pull/13242)) +- fix(api): remove hardcoded wildcard CORS in openai-to-gemini-sse.ts so the centralized fail-closed CORS gate is the sole source of `Access-Control-Allow-Origin` (#12573) ([#13239](https://github.com/diegosouzapw/OmniRoute/pull/13239)) +- fix(api): enforce API key policy (budget/rate-limit/schedule/endpoint scoping) on the ElevenLabs speech-to-text, text-to-speech and voices proxy routes (#12574) ([#13244](https://github.com/diegosouzapw/OmniRoute/pull/13244)) +- fix(sse): cap HuggingChat NDJSON body size and bound the read loop with the fetch timeout so a stalled or hostile upstream cannot buffer unbounded memory (#12577) ([#13282](https://github.com/diegosouzapw/OmniRoute/pull/13282)) +- fix(docker): scope the cliproxyapi/qdrant/bifrost sidecars to loopback by default and forward `CLIPROXYAPI_MANAGEMENT_KEY` into the cliproxyapi container so its management API is not left both unauthenticated and LAN-published (#12578) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): create DB export temp paths with `fs.mkdtempSync` instead of predictable timestamps (#12579) ([#13237](https://github.com/diegosouzapw/OmniRoute/pull/13237)) +- Cline 401 bodies that say "re-authenticate your Cline account" classify as a refreshable OAuth token, not a terminal expired key. The cooling panel no longer labels every cooldown as a 429; it shows the recorded last error instead. (#12594) ([#13060](https://github.com/diegosouzapw/OmniRoute/pull/13060)) — thanks @HouMinXi +- **fix(catalog):** degrade unknown combo targets instead of dropping LCD modalities, and persist OpenRouter `architecture.input_modalities` into the capability snapshot ([#12613](https://github.com/diegosouzapw/OmniRoute/issues/12613)) ([#13042](https://github.com/diegosouzapw/OmniRoute/pull/13042)) — thanks @HouMinXi +- fix(providers): send `x-api-key` instead of `Authorization: Bearer` for OpenCode Zen's `/v1/responses` endpoint (Muse Spark Contributor models), fixing a 401 on OmniRoute's auth header (#12633) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(sse): surface the actionable "Auggie CLI not found" message when the shell reports a missing `auggie` binary via exit code instead of a spawn error (#12645) ([#13263](https://github.com/diegosouzapw/OmniRoute/pull/13263)) +- **fix(config):** Persist deletions of built-in background-degradation entries — when a stored settings record exists its `degradationMap` is now authoritative instead of being merged under the defaults, so an entry the user removed in the dashboard no longer reappears on the next apply or restart ([#12424](https://github.com/diegosouzapw/OmniRoute/issues/12424)) ([#12647](https://github.com/diegosouzapw/OmniRoute/pull/12647)) — thanks @pacocartones +- **fix(gamification):** action-count badge milestones (First Token, Token Consumer, Token Machine, Token Whale, and the token-sharing tier) are now backed by a durable `xp_action_counts` counter incremented in `addXp()`, instead of a live `COUNT(*)` over `xp_audit_log`. The audit log is pruned by `retention.xpAuditLog` (default 30 days), so on a default install those "lifetime" milestones were really "actions in the last 30 days" and unlocked badges could stop unlocking once old rows aged out. `getActionCount()` and `checkActionCountBadges()` now read the same durable source, and a migration backfills existing totals from the surviving audit rows ([#12546](https://github.com/diegosouzapw/OmniRoute/issues/12546)) ([#12651](https://github.com/diegosouzapw/OmniRoute/pull/12651)) — thanks @pacocartones +- **fix(images):** `/v1/images/edits` now iterates a combo's targets the same way `/v1/images/generations` does (#9239) instead of flattening a bare combo to its first target. A combo whose first target is not edit-capable — or lacks credentials — now falls through to a later edit-capable target rather than hard-erroring, and missing credentials are skipped (not a hard `401`) to match the generations path. The per-target skip/terminal classification is extracted into a shared `runImageComboTargets` loop, so generations behavior is unchanged ([#12547](https://github.com/diegosouzapw/OmniRoute/issues/12547)). ([#12653](https://github.com/diegosouzapw/OmniRoute/pull/12653)) — thanks @pacocartones +- fix(sse): add first-byte watchdog to the TLS-fingerprint transport so a stalled wreq body falls back instead of hanging for minutes (#12656) ([#13272](https://github.com/diegosouzapw/OmniRoute/pull/13272)) +- fix(sse): exempt tiny-budget reasoning probes from combo quality failure and surface persisted-cooldown skips in ALL_TARGETS_SKIPPED diagnostics (#12659) ([#13269](https://github.com/diegosouzapw/OmniRoute/pull/13269)) +- fix(models): declare the real ~1M contextLength for OpenCode Zen's Muse Spark 1.2 models instead of falling back to the 200000 provider default (#12681) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(oauth): align codebuddy-cn OAuth User-Agent with the chat/usage CLI version to avoid WAF false positives (#12702) ([#13264](https://github.com/diegosouzapw/OmniRoute/pull/13264)) +- fix(dashboard): surface an authentication-required banner instead of silently blanking database settings for a guest session (#12709) ([#13270](https://github.com/diegosouzapw/OmniRoute/pull/13270)) +- **fix(resilience):** stop unbounded queue that hangs 6min until Aborted — gate, provider slot, and Bottleneck queue now share a per-connection `maxWaitMs` budget; `fail-closed` on exhaust (503); execution backstop `executionMaxWaitMs` overridable per connection with upstream clamp ([#12715](https://github.com/diegosouzapw/OmniRoute/pull/12715)) — thanks @maxmad64bis (with thanks to @Tushar49 for surfacing the slow-provider need in #12635) +- **fix(auto-combo):** every mode pack now carries `quality` and `reliability` weights (`quality` 0.02, 0.03 in `quality-first`; `reliability` 0.03, 0.04 in `reliability-first`), so selecting a pack no longer silences either signal; `DEFAULT_WEIGHTS` is unchanged ([#12731](https://github.com/diegosouzapw/OmniRoute/pull/12731)) — thanks @maxmad64bis +- **fix(sse):** the provider execution pipeline keeps the upstream error `code`/`type` again — both were lost when the non-streaming leg was extracted, so a config-class `422` (Antigravity missing project) degraded into a generic account cooldown instead of reaching the gate that recognises it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(db):** a legacy `call_logs` table no longer aborts startup — the provider-stats index is created after the column healing runs, not before it, so an install predating `request_type` opens instead of failing with `no such column` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(catalog):** a malformed row in the operator-writable custom-models blob no longer kills every `auto/*` pool ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(tests):** realign the guards that the same day's merges left asserting the old behavior — the provider-count total after GreenPT and EURouter, the GLM stream buffer slot that `#12925` turned into a declared parameter, and the injection scan bound that `#13104` reshaped from a head-only window into head-plus-tail ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- fix(cache): fold tool_choice/tools/response_format into the semantic cache signature so a cached tool_calls response can no longer be replayed for a request whose tool policy forbids it (#12734) ([#13267](https://github.com/diegosouzapw/OmniRoute/pull/13267)) +- **fix(models):** stop treating provider-supplied `isFree:true`/`:free`/`0/0` flags as trusted unless the provider has a documented free tier — only the shipped free-tier catalog decides otherwise; fetched `isFree:true` trusted only for a free-tier provider, custom `isFree:true` via a trusted path ([#12744](https://github.com/diegosouzapw/OmniRoute/pull/12744)) — thanks @maxmad64bis +- fix(memory): authenticate the internal /v1/rerank loopback call so memory reranking no longer silently degrades to unranked order when REQUIRE_API_KEY=true (#12745) ([#13268](https://github.com/diegosouzapw/OmniRoute/pull/13268)) +- fix(sse): parse Ollama Cloud's current usage markup (`$X of $Y used` aria-label, nested width style) (#12749) ([#13260](https://github.com/diegosouzapw/OmniRoute/pull/13260)) +- fix(cli): setup-opencode no longer sends an active context's management token to `/v1/models` when `--api-key`/`OMNIROUTE_API_KEY` is supplied — an explicit flag or the env var now always outranks the context's token, and the flag itself is no longer swallowed by the parent program's global `--api-key` option (#12783) ([#13246](https://github.com/diegosouzapw/OmniRoute/pull/13246)) +- fix(sse): register Arcee AI in the executor provider registry so requests reach api.arcee.ai instead of silently falling back to OpenAI (#12784) ([#13277](https://github.com/diegosouzapw/OmniRoute/pull/13277)) +- **fix(quota):** share one upstream quota read when identical saturation checks arrive at the same time, so traffic bursts don't multiply provider API calls ([#12787](https://github.com/diegosouzapw/OmniRoute/pull/12787)) — thanks @maxmad64bis +- **fix(db):** ignore expired or invalid rate-limit cooldown writes so a stale timestamp can't lock a connection that should be usable — clearing still works as before ([#12788](https://github.com/diegosouzapw/OmniRoute/pull/12788)) — thanks @maxmad64bis +- **fix(providers):** single source for provider order with `xao` ranking alongside `xai-oauth` ([#12790](https://github.com/diegosouzapw/OmniRoute/pull/12790)) — thanks @maxmad64bis +- **fix(routing):** off-table models check the free-model catalog before inheriting premium prices, latency bootstraps from the observed pool median, async tiers read live database pricing with a 90-day freshness gate, and the tier cache invalidates on every pricing write ([#12792](https://github.com/diegosouzapw/OmniRoute/pull/12792)) — thanks @maxmad64bis +- **fix(auto-combo):** snapshot scoring uses the observed per-connection account tier and per-model quality instead of neutral constants, and circuit-open providers rank lower at build time ([#12794](https://github.com/diegosouzapw/OmniRoute/pull/12794)) — thanks @maxmad64bis +- **fix(pool):** empty `auto/*` pools now say why they are empty, and the models listing uses the same paid check as routing ([#12795](https://github.com/diegosouzapw/OmniRoute/pull/12795)) — thanks @maxmad64bis +- fix(routing): recognize CLIProxyAPI's 'unknown provider for model' 400 as fallback-worthy (#12800) ([#13284](https://github.com/diegosouzapw/OmniRoute/pull/13284)) +- **fix(sse):** translate-mode streams now emit the estimated token counts as a trailing usage-only chunk before `[DONE]` when the upstream stays silent, so metered chat clients see totals instead of nothing ([#12828](https://github.com/diegosouzapw/OmniRoute/pull/12828)) — thanks @maxmad64bis +- **fix(db):** provider stats stay truthful on empty and legacy databases: fallback counts default to `0` instead of `null`, latency averages read `null` (not `0`) when no durations were recorded, failures logged before the error-type column existed group under `pre_migration` instead of `unclassified`, and per-provider queries use two new composite indexes ([#12832](https://github.com/diegosouzapw/OmniRoute/pull/12832)) — thanks @maxmad64bis +- fix(nvidia): fail open when a synced model catalog goes stale instead of gating forever (#12849) ([#13248](https://github.com/diegosouzapw/OmniRoute/pull/13248)) +- **fix(db):** WAL maintenance lives in its own module: a `TRUNCATE` checkpoint that hits a busy database now warns with its streak and retries once via `PASSIVE` instead of logging success, and closing-time checkpoints no longer report success on builds without a database file, with the busy totals visible in the authenticated monitoring health payload ([#12853](https://github.com/diegosouzapw/OmniRoute/pull/12853)) — thanks @maxmad64bis +- **fix(health):** quota with no snapshots reads empty instead of a contradicting 0% ([#12857](https://github.com/diegosouzapw/OmniRoute/pull/12857)) — thanks @maxmad64bis +- fix(a2a): accept the dashboard's own session cookie on /a2a so "Run message/send" no longer fails with "Unauthorized: missing or invalid API key" (#12888) ([#13271](https://github.com/diegosouzapw/OmniRoute/pull/13271)) +- **fix(logs):** the Logs grid's in-memory filter pass no longer discards rows the SQL query already matched — selecting an API key from the dropdown (which sends the key's id) returns its calls again, the Combo tab shows every combo instead of only those whose name contains a "1", and the model filter and search cover the same columns as the query ([#12896](https://github.com/diegosouzapw/OmniRoute/pull/12896)) — fixes [#12873](https://github.com/diegosouzapw/OmniRoute/issues/12873) — thanks @ntdatt812 +- **fix(a2a):** `/api/a2a/status` now builds the agent card from the request that asked for it, so a gateway reached at a non-localhost host no longer advertises `http://localhost:20128` as its A2A URL ([#12918](https://github.com/diegosouzapw/OmniRoute/pull/12918)). — thanks @ntdatt812 +- **fix(compression):** progressive aging now appends its `[COMPRESSED:aging:…]` annotation after a turn's `tool_result` blocks instead of in front of them, so Anthropic no longer rejects aged conversations with "`tool_use` ids were found without `tool_result` blocks immediately after" ([#12920](https://github.com/diegosouzapw/OmniRoute/pull/12920)). — thanks @ntdatt812 +- **fix(bedrock):** model import now resolves context limits for every vendor prefix instead of only `anthropic.*`, so `global.openai.gpt-5.6-*` no longer imports with a null `inputTokenLimit` and gets rejected pre-flight at the 200k default ([#12921](https://github.com/diegosouzapw/OmniRoute/pull/12921)). — thanks @ntdatt812 +- **fix(stream):** the 64 KB stream buffer GLM asks for is honoured instead of dropped, and the type error it caused no longer fails the API Route Typecheck gate on every open PR ([#12925](https://github.com/diegosouzapw/OmniRoute/pull/12925)) — thanks @ntdatt812 +- **fix(guardrails):** mask PII inside a `tool_result`'s nested content array, which the masker walked past while redacting its sibling block ([#12930](https://github.com/diegosouzapw/OmniRoute/pull/12930)) — thanks @ntdatt812 +- **fix(dashboard):** the Radar catalog table no longer leaves absent data unexplained — empty limits, unknown context windows, and unreported capabilities each explain themselves on hover, and a new check keeps it that way ([#12937](https://github.com/diegosouzapw/OmniRoute/pull/12937)) — thanks @maxmad64bis +- **fix(sse):** geo-blocked opencode requests rotate to the next account proxy instead of failing, so one refused egress no longer aborts the whole chain ([#12941](https://github.com/diegosouzapw/OmniRoute/pull/12941)) — thanks @maxmad64bis +- **fix(codex):** dashboard "clear cooldown" and the CAS recovery path now drop nested `codexScopeRateLimitedUntil` maps in the same write that nulls `rate_limited_until`, and fresh quota snapshots with headroom lift fallback-sourced scope cooldowns parked by quota preflight ([#12817](https://github.com/diegosouzapw/OmniRoute/issues/12817), [#12860](https://github.com/diegosouzapw/OmniRoute/issues/12860), [#12951](https://github.com/diegosouzapw/OmniRoute/pull/12951)) The dashboard reset-credit button (`consumeCodexResetCredit`) now exercises that same snapshot path after a successful redeem, so a filled quota bar is enough to unpark a leftover fallback Codex child without another manual clear. — thanks @HouMinXi +- **fix(db):** resolve `sql-wasm.wasm` across global npm install and hoisted layouts, ensuring OmniRoute can boot cleanly on Node 24 when native `better-sqlite3` is uncompiled. ([#13035](https://github.com/diegosouzapw/OmniRoute/pull/13035)) — thanks @HouMinXi +- **fix(api):** `DELETE /v1/batches/delete-completed` now sweeps only the calling API key's own completed batches (batches with no owner stay out of a key-scoped sweep on purpose), with an explicit instance-wide mode reserved for authenticated dashboard sessions, a 401 for a presented key that is unknown, revoked, deactivated, banned or expired (never falling through to the session branch), audit logging of every sweep, a sanitized 500 on failure, an owner-scoped file half (a key-scoped sweep never nulls a file another tenant owns) and an atomic sweep — chunked in 200-batch transactions in instance mode — so a mid-way error never leaves a batch pointing at a nulled file (GHSA-wvxc-jp3v-5mg5) ([#12969](https://github.com/diegosouzapw/OmniRoute/pull/12969)) +- **fix(combo):** quota-weighted routing stops drawing on an out-of-credit connection — a 402 now invalidates the stored quota snapshot instead of leaving its stale remaining percentage in place, and a snapshot older than 10 minutes no longer counts as confident headroom for the primary pool ([#12972](https://github.com/diegosouzapw/OmniRoute/pull/12972)) ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- **fix(vision):** Custom Models with "Vision capable" checked no longer have image requests swapped to `glm/glm-4.6v` when the client sends the advertised alias (`vllm/path/...`) or the bare path-shaped id — Vision Bridge now matches the stored override for all three id forms ([#12758](https://github.com/diegosouzapw/OmniRoute/issues/12758)) ([#12974](https://github.com/diegosouzapw/OmniRoute/pull/12974)) — thanks @HouMinXi +- **fix(sse):** transient opencode upstream failures rotate to the next account proxy instead of failing, so one flapping egress no longer aborts the whole chain ([#12975](https://github.com/diegosouzapw/OmniRoute/pull/12975)) — thanks @maxmad64bis +- **fix(azure):** Deployments from GPT-6 onward now send `max_completion_tokens` instead of `max_tokens`, which Azure rejects with HTTP 400. The rule matched a literal `gpt-5`, so each new generation arrived broken; it now matches the generation range, while `gpt-35-turbo` still keeps `max_tokens`. ([#13007](https://github.com/diegosouzapw/OmniRoute/pull/13007)) — thanks @ntdatt812 +- **fix(db):** add an opt-in self-restart circuit for sustained critical memory pressure, gate post-cleanup VACUUM behind a minimum freed-rows threshold, and checkpoint the SQLite WAL every 5 minutes with a size guard that escalates to TRUNCATE, so a growing WAL can no longer stall the event loop into a full outage. ([#13011](https://github.com/diegosouzapw/OmniRoute/pull/13011)) — thanks @HouMinXi +- **fix(auth):** an explicit connection pin may probe a quota-disabled row once and re-enable it on success ([#12874](https://github.com/diegosouzapw/OmniRoute/issues/12874)) ([#13017](https://github.com/diegosouzapw/OmniRoute/pull/13017)) — thanks @HouMinXi +- **fix(api):** thread `X-OmniRoute-Fallback-Attempts` through combo chat completions so streaming and non-streaming responses report how many prior legs were attempted ([#13038](https://github.com/diegosouzapw/OmniRoute/pull/13038)) — thanks @HouMinXi +- Restore provider failure classification and credential refresh on non-streaming requests: classify non-2xx failures to lock models on per-model quota exhaustion, update connection rate limits from headers and body, and pass credential refresh handlers to pipeline execution so 401 tokens can be refreshed and retried (#13043). ([#13069](https://github.com/diegosouzapw/OmniRoute/pull/13069)) — thanks @HouMinXi +- **fix(responses):** wrap forced-non-streaming web_search fallback JSON as Responses SSE so Codex still sees `response.completed` ([#13050](https://github.com/diegosouzapw/OmniRoute/pull/13050)) — thanks @HouMinXi +- **fix(antigravity):** Preserve upstream thought-token usage in normalized completion and reasoning token counts ([#13055](https://github.com/diegosouzapw/OmniRoute/pull/13055)) — thanks @pacocartones +- Fix a concurrency-slot leak in the DeepSeek PoW solver: a worker that failed to spawn (for example a missing worker script) never released its slot, so `MAX_CONCURRENT_WORKERS` failures disabled the solver until restart. ([#13097](https://github.com/diegosouzapw/OmniRoute/pull/13097)) — thanks @anhtahaylove +- **fix(acp):** bound the ACP session output buffers — `stdoutBuffer` and `stderrBuffer` now cap at 1 MiB keeping the most recent output behind a visible `[...output truncated...]` marker, and `stderrBuffer` is reset per prompt instead of accumulating for the lifetime of the session. ([#13100](https://github.com/diegosouzapw/OmniRoute/pull/13100)) — thanks @anhtahaylove +- **fix(acp):** release the `stdout`/`exit` listeners and the idle timer that a `sendPrompt` timeout used to leave attached to the `acpManager` singleton, and drop sessions that exited on their own from the session map instead of keeping them forever. ([#13096](https://github.com/diegosouzapw/OmniRoute/pull/13096)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection and PII scanners now read the text a `tool_result` block carries on `content` (string or nested block list), in messages and in system blocks, so tool output is judged by the same rules as user text ([#13101](https://github.com/diegosouzapw/OmniRoute/pull/13101)) — thanks @ntdatt812 +- **fix(gamification):** close the badge notification SSE stream when the request signal is already aborted before the stream starts — a client that disconnects while the route is still awaiting auth used to leave both the 2s unlock poll and the 15s heartbeat running for the lifetime of the process. ([#13106](https://github.com/diegosouzapw/OmniRoute/pull/13106)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection scan now spends its 16 KB budget on both ends of the request instead of the first 16 KB only, so `system`, `instructions`, `query`, `documents` and the newest turns are no longer hidden behind one long message ([#13104](https://github.com/diegosouzapw/OmniRoute/pull/13104)) — thanks @ntdatt812 +- **fix(dashboard):** expose the Volcano Ark console cookie on quota scraping and unwrap connect-error objects so the dashboard shows the upstream message ([#13107](https://github.com/diegosouzapw/OmniRoute/pull/13107)) — thanks @HouMinXi +- **fix(db):** release the `beforeExit`/`SIGINT`/`SIGTERM` handlers when a `node:sqlite` adapter closes, so a closed adapter and its database handle are no longer pinned to `process` for the lifetime of the run — the same treatment #7494 gave the sql.js adapter. ([#13109](https://github.com/diegosouzapw/OmniRoute/pull/13109)) — thanks @anhtahaylove +- **fix(translator):** `contentSchema` and `unevaluatedItems` are now treated as subschema positions by the tool-schema sanitizer, so a truncation placeholder in either is replaced with a permissive schema instead of being forwarded as a string ([#13110](https://github.com/diegosouzapw/OmniRoute/pull/13110)) — thanks @ntdatt812 +- **fix(cli-helper):** clear the `createLogStream` timeout on the abort path — `stop()` aborts the in-flight fetch and returned through the `signal.aborted` branch, which skipped `clearTimeout` and left an armed timer per stopped stream. The stream reader is now also cancelled when the read loop exits early. ([#13114](https://github.com/diegosouzapw/OmniRoute/pull/13114)) — thanks @anhtahaylove +- **fix(docs):** drop TLS-impersonation, MITM-decrypt, supply-chain attestation, and XOR-mask writeups from the public `/docs` catalog and Docker image. Files stay in git for engineers; operators who need them open the repo, not the website. ([#13136](https://github.com/diegosouzapw/OmniRoute/pull/13136)) — thanks @HouMinXi +- **fix(combo):** parse numeric-epoch `rate_limited_until` in the combo cooldown read path ([#13141](https://github.com/diegosouzapw/OmniRoute/pull/13141)) — thanks @maxmad64bis +- **fix(opencode):** both OpenCode plugins now reject a gateway address typed without `http://` at configuration time, instead of publishing every model with an api url no client can call, and the v2 plugin no longer publishes a model card whose api url is blank or relative ([#13142](https://github.com/diegosouzapw/OmniRoute/pull/13142)) — thanks @maxmad64bis +- **fix(providers):** lock opencode model on upstream 400 model-unavailable ([#13146](https://github.com/diegosouzapw/OmniRoute/pull/13146)) — thanks @maxmad64bis +- **fix(logging):** keep the provider exchange rather than the raw client bodies when a call log exceeds its size budget, and show that recovered payload in the request-detail panel instead of replacing it with the stored response body ([#13147](https://github.com/diegosouzapw/OmniRoute/pull/13147)) — thanks @maxmad64bis +- **fix(traffic-inspector):** the WebSocket route no longer leaks a traffic-buffer subscriber and a 30s ping timer when the client socket is already closed at handler time — listeners are attached before any resource is acquired, a destroyed socket bails out early, and the ping interval stops on a dead socket where `write()` never throws ([#13155](https://github.com/diegosouzapw/OmniRoute/pull/13155)) — thanks @anhtahaylove +- **fix(telegram):** bound the per-user API key cache in the Telegram chat proxy so a burst of distinct chat ids can no longer grow the process heap without limit ([#13165](https://github.com/diegosouzapw/OmniRoute/issues/13165)) ([#13166](https://github.com/diegosouzapw/OmniRoute/pull/13166)) — thanks @anhtahaylove +- **fix(stream):** cancel the upstream response body when the JSON-to-SSE sniff unwinds on a body timeout, so a stalled upstream no longer pins the connection ([#13169](https://github.com/diegosouzapw/OmniRoute/issues/13169)) ([#13171](https://github.com/diegosouzapw/OmniRoute/pull/13171)) — thanks @anhtahaylove +- **fix(telegram):** authenticate webhook deliveries with Telegram's `secret_token` so an unauthenticated caller can no longer mint API keys or spend upstream quota ([#13172](https://github.com/diegosouzapw/OmniRoute/issues/13172)) ([#13175](https://github.com/diegosouzapw/OmniRoute/pull/13175)) — thanks @anhtahaylove +- **fix(models):** give discoverable Gemini 3.8 Flash ids their own 65536 output spec so Antigravity no longer clamps them to 16384 ([#13195](https://github.com/diegosouzapw/OmniRoute/pull/13195)) — thanks @HouMinXi +- **fix(dashboard):** leftover catalog-removed provider rows (gemini-cli) can be listed and purged from the providers page ([#13067](https://github.com/diegosouzapw/OmniRoute/issues/13067)) ([#13197](https://github.com/diegosouzapw/OmniRoute/pull/13197)) — thanks @HouMinXi +- **fix(auth):** a dashboard session now requires the `authenticated: true` claim that login, OIDC and the session refresh already emit — a JWT merely signed with `JWT_SECRET` (for example the Cursor CLI passthrough token, which any API-key holder can obtain) no longer verifies as the `auth_token` cookie on any route, the WebSocket handshake or the live server; existing sessions keep working ([#13298](https://github.com/diegosouzapw/OmniRoute/issues/13298)) ([#13375](https://github.com/diegosouzapw/OmniRoute/pull/13375)) +- **fix(api):** `DELETE /v1/batches/delete-completed` now applies the caller's API-key policy (endpoint allowlist, schedule, usage cap, rate limit) like every other `/v1` route, commits the key-scoped sweep per 200-batch chunk instead of holding one write lock for the whole sweep, refuses to loop without progress, and rejects a scope that names both a key and `allTenants` ([#13684](https://github.com/diegosouzapw/OmniRoute/pull/13684)) +- Restore the API-route typecheck gate: the non-streaming leg result lost its discriminated-union narrowing after the server-owned tool loop reassignment, producing 13 new TS2339 diagnostics in `chatCore.ts`. ([#12963](https://github.com/diegosouzapw/OmniRoute/pull/12963)) +- Restore four non-streaming behaviours the server-owned tool loop refactor dropped: client aborts map to 499 with the fixed `Request aborted` message, an aborted request no longer logs a synthetic `clientResponse`, Claude prompt-cache telemetry is recorded again, and a body that cannot be canonicalized no longer throws when the tool loop is off. ([#12990](https://github.com/diegosouzapw/OmniRoute/pull/12990)) +- **fix(skills):** The CLI registry parser now reads positionals declared with `.addArgument()`, not only those written inline in `.command()`. `tunnel create [type]` was being published as `tunnel create`, so the agent-skills sync gate reported drift on every branch and regenerating would have deleted the argument. ([#13009](https://github.com/diegosouzapw/OmniRoute/pull/13009)) — thanks @ntdatt812 +- **fix(combos):** dashboard combo test uses a short prompt, serial probes, and a 60s timeout so reasoning models and rate-limited free pools do not fail the health check ([#13001](https://github.com/diegosouzapw/OmniRoute/pull/13001)) — thanks @HouMinXi +- fix(compression): terminate idle worker threads on eviction so long-running instances stop leaking OS threads and MessagePorts ([#13091](https://github.com/diegosouzapw/OmniRoute/pull/13091)) — thanks @anhtahaylove +- Redact Google API keys of any length in error bodies: the pattern required exactly 39 characters, so shorter or longer `AIza…` credentials passed through unredacted. ([#12964](https://github.com/diegosouzapw/OmniRoute/pull/12964)) +- **fix(grok-cli):** a 402 "Grok Build usage balance exhausted" parks that Grok login as out of credit (Grok Build CLI, grok.com cookie, and xAI OAuth share the weekly pool). Combo routing then tries the next login instead of locking the model for every account in the pool ([#13061](https://github.com/diegosouzapw/OmniRoute/pull/13061)) — thanks @HouMinXi +- fix(compression): spawn the LLMLingua worker with a file URL object so compression actually runs instead of silently failing open on Node ([#13093](https://github.com/diegosouzapw/OmniRoute/pull/13093)) — thanks @anhtahaylove +- **fix(test):** run the local `test` and `test:unit` scripts at concurrency 4 so a full-suite run no longer exhausts the machine's commit charge and kills unrelated processes ([#13187](https://github.com/diegosouzapw/OmniRoute/pull/13187)) — thanks @anhtahaylove +- **fix(monitoring):** `GET /api/monitoring/health` `credentialHealth` now includes a bounded `failedConnections` list (`connectionId`, `status`, sanitized `lastError`) when the probe-cache gauge `failed>0`, plus `source: probe-cache` and a cheap `staleDbNonOkCount` for sticky SQLite `test_status` on active rows. Documents that the live gauge is not `provider_connections.test_status`. ([#12876](https://github.com/diegosouzapw/OmniRoute/pull/12876)) — thanks @RaviTharuma +- **fix(resilience):** an openai-compatible multi-upstream gateway no longer marks the whole connection `credits_exhausted` when a single upstream (or the `/models` / representative-model health probe) returns 402. Quota failures stay model-scoped; true connection-wide auth failures are unchanged. Single-credential 402 key disable ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239)) is preserved. ([#12875](https://github.com/diegosouzapw/OmniRoute/pull/12875)) — thanks @RaviTharuma +- **fix(dashboard):** nine fixes on the Orchestration Canvas, all diagnosed in the Phase 2 reviews. A source that fails now keeps the timestamp of its FIRST failure instead of being re-stamped every poll — the stale line said "since the last poll" no matter how long the source had been down, and the churn also defeated the snapshot's stable identity (it serializes the source list), so the canvas re-rendered on every tick while anything was broken. A source that HAD data and only then started failing is flagged too: previously only a source with no node at all got the warning, so a source that went down mid-session kept a healthy-looking node forever. The rest are pointwise: clicking a filter chip cancels the pending search debounce (left armed, it fired ~300ms later and silently reverted the chip); the search input carries an accessible name; `?state=running, failed` parses like the unpadded form instead of dropping the padded value; the CSV toggle helper is defined once in `model/urlParams.ts` rather than twice; the agents tab tells "nothing running" apart from "the filter matched nothing", offering a clear-filters button instead of setup links that would be wrong advice there; edges stop emitting SMIL particles above 40 simultaneously active edges, keeping the colored stroke; and the drawer's error banner clears when a retried action succeeds. ([#12983](https://github.com/diegosouzapw/OmniRoute/pull/12983)) +- **fix(i18n):** the nine locales added with the EU-language batch (Greek, Estonian, Irish, Croatian, Lithuanian, Latvian, Maltese, Slovenian, Serbian) were missing the eleven Orchestration Canvas keys that Phase 3 introduced, so the compare-runs panel and the "no runs match these filters" empty state fell back to English in those languages (`deepMergeFallback` substitutes English for an absent key, so nothing rendered blank — it rendered untranslated). The coverage gate does not catch this: it enforces an 80% floor per locale, and eleven missing keys out of ~13,000 leaves coverage at 99.9%. Translated for real in each language, calibrated against the wording each file already uses for "run", "filter" and "skill". ([#13294](https://github.com/diegosouzapw/OmniRoute/pull/13294)) +- **fix(dashboard):** five follow-ups from the Phase 2 Orchestration Canvas review. The History drawer now shows the memory section for runs that already left the live TTL window: the persisted `memory_hits` event is parsed into `metadata.memoryHits` with the same defensive validation the drawer applies, and — because that event is observability rather than a state transition — it no longer leaks into the timeline, where it had been inheriting the task's state and rendering as a duplicate transition. The A2A memory recall runs against its own 1.5s deadline instead of inheriting the memory backend's 30s one; overshooting degrades exactly like any other recall failure (no hits, task proceeds), and the timer is cleared on both paths. Repeating a Conductor run carries its `requirements.cli`/`requirements.model` forward, so the new run is pinned to the same runner profile and model rather than drifting to whatever the fleet picks. A successful repeat from the Agents tab now focuses the run it created, instead of leaving the operator on the finished one. And the auth test for `POST /api/conductor/tasks` moved into the shared `ROUTES` array rather than restating the pattern. ([#12988](https://github.com/diegosouzapw/OmniRoute/pull/12988)) +- fix(plugins): stop leaking an exit listener per plugin hook timeout, which triggered MaxListenersExceededWarning on plugins that ignore SIGTERM ([#13092](https://github.com/diegosouzapw/OmniRoute/pull/13092)) — thanks @anhtahaylove +- **fix(validation):** Provider node edits no longer fail with a generic "Invalid request" when the optional daily-quota reset fields are left blank. The dashboard sends `dailyQuotaResetTimezone` and `dailyQuotaResetHour` as `null`, and only the hour accepted it. ([#13066](https://github.com/diegosouzapw/OmniRoute/issues/13066)) ([#13083](https://github.com/diegosouzapw/OmniRoute/pull/13083)) — thanks @ntdatt812 +- **fix(combo):** quota-aware expansion drops banned, inactive, missing, and wrong-provider connections before quota fetch or model dispatch; pins and allowlists stay selectors, not a bypass. Antigravity automatic exhaustion now requires a reported zero remaining, so a positive balance below 1% stays eligible. ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- Fix an intermittent failure in the quota-weighted routing test suite: scores are a function of `Date.now()`, so peers with identical quota scored microseconds apart never tied and swapped order. ([#12946](https://github.com/diegosouzapw/OmniRoute/pull/12946)) +- fix(volcengine): resolve quota window mapping gaps, safe multi-connection console binding, and response cookie sanitization ([#12950](https://github.com/diegosouzapw/OmniRoute/pull/12950)) — thanks @HouMinXi +- **fix(test):** resolve the WebDAV handler path with `fileURLToPath` so the suite's 37 WebDAV tests run on Windows instead of failing with a doubled `C:\C:\` drive prefix ([#13196](https://github.com/diegosouzapw/OmniRoute/pull/13196)) — thanks @anhtahaylove +- **fix(ui):** fall back from invalid AnySearch icon alias (direct commit `9587e07b69`) +- **fix(sse):** stop rebuilding a truncated summary from the collector's cap-dropped event array ([#12718](https://github.com/diegosouzapw/OmniRoute/pull/12718)) — thanks @hartmark +- **fix(conversations):** show a pending spinner for unresolved tool nodes instead of "(empty)" ([#12727](https://github.com/diegosouzapw/OmniRoute/pull/12727)) — thanks @hartmark +- **fix(streaming):** fail fast when an upstream stream produces only lifecycle/heartbeat events, never real content ([#12741](https://github.com/diegosouzapw/OmniRoute/pull/12741)) — thanks @hartmark +- **fix(responses-continuation):** bridge the write-in-flight window with an in-memory pending store ([#12854](https://github.com/diegosouzapw/OmniRoute/pull/12854)) — thanks @hartmark +- **fix(translator):** drop or map agent_message on Chat Completions fallback ([#12880](https://github.com/diegosouzapw/OmniRoute/pull/12880)) — thanks @RaviTharuma +- **fix(api):** page and stream-complete GET /v1/models for large catalogs ([#12882](https://github.com/diegosouzapw/OmniRoute/pull/12882)) — thanks @RaviTharuma +- **fix(quota):** align AUTH window labels with usage API and clarify 503 ([#12884](https://github.com/diegosouzapw/OmniRoute/pull/12884)) — thanks @RaviTharuma +- **fix(ci):** clear two base-reds on release/v3.8.51 — mutation-coverage gate + image-only-model guard ([#12945](https://github.com/diegosouzapw/OmniRoute/pull/12945)) +- **fix(api):** scope batch bulk-delete to the calling API key ([#13211](https://github.com/diegosouzapw/OmniRoute/pull/13211)) +- **fix(tests):** align model-sync log assertion to shipped path redaction (base-red) ([#13227](https://github.com/diegosouzapw/OmniRoute/pull/13227)) +- **fix(test):** make the ToS heading guard actually require the parentheses ([#13228](https://github.com/diegosouzapw/OmniRoute/pull/13228)) +- **fix(quality):** register 4 drifted covering tests in stryker tap.testFiles ([#13229](https://github.com/diegosouzapw/OmniRoute/pull/13229)) +- **fix(logging):** mask *-api-key headers in request-log pipeline (#13273) ([#13401](https://github.com/diegosouzapw/OmniRoute/pull/13401)) — thanks @KooshaPari +- **fix(tests):** correct stale Jina catalog prefix in custom-models test ([#13403](https://github.com/diegosouzapw/OmniRoute/pull/13403)) — thanks @KooshaPari +- **fix(backend):** prune old backups after health-check-repair snapshots (#13308) ([#13404](https://github.com/diegosouzapw/OmniRoute/pull/13404)) — thanks @KooshaPari +- **fix(backend):** guard memory_fts_au trigger with WHEN clause to stop FTS bloat (#13326) ([#13405](https://github.com/diegosouzapw/OmniRoute/pull/13405)) — thanks @KooshaPari +- **fix(free-tiers):** remove obsolete Together $25 signup credit (#12526) ([#13407](https://github.com/diegosouzapw/OmniRoute/pull/13407)) — thanks @KooshaPari +- **fix:** skip non-chat generation models in Test all models ([#13409](https://github.com/diegosouzapw/OmniRoute/pull/13409)) — thanks @KooshaPari +- **fix(providers):** de-duplicate agy and antigravity model catalogs ([#13410](https://github.com/diegosouzapw/OmniRoute/pull/13410)) — thanks @KooshaPari +- **fix(dashboard):** send provider in body for mini-playground web search ([#13411](https://github.com/diegosouzapw/OmniRoute/pull/13411)) — thanks @KooshaPari +- **fix(resilience):** add CJK quota-exhaustion patterns to 429 classifier ([#13412](https://github.com/diegosouzapw/OmniRoute/pull/13412)) — thanks @KooshaPari +- **fix(backend):** eval runner: failed calls no longer score as passed + dotAll regex ([#13413](https://github.com/diegosouzapw/OmniRoute/pull/13413)) — thanks @KooshaPari +- **fix(backend):** eval runner opts out of output-style and memory injection ([#13414](https://github.com/diegosouzapw/OmniRoute/pull/13414)) — thanks @KooshaPari +- **fix:** chain serial quarantine step in npm test script (fixes #13286) ([#13418](https://github.com/diegosouzapw/OmniRoute/pull/13418)) — thanks @KooshaPari +- **fix(antigravity):** derive stable session ID from credentials for prompt-cache reuse (#13305) ([#13424](https://github.com/diegosouzapw/OmniRoute/pull/13424)) — thanks @KooshaPari +- **fix(memory):** merge memory into top-level system field instead of unshifting at messages[0] (#13425) ([#13427](https://github.com/diegosouzapw/OmniRoute/pull/13427)) — thanks @KooshaPari +- **fix(compression):** preserve instruction blocks from lossy engines ([#13523](https://github.com/diegosouzapw/OmniRoute/pull/13523)) — thanks @KooshaPari +- **fix(compression):** preserve polarity words and newlines in ultra heuristic ([#13524](https://github.com/diegosouzapw/OmniRoute/pull/13524)) — thanks @KooshaPari +- **fix(compression):** run user preservePatterns before built-in patterns (#13457) ([#13525](https://github.com/diegosouzapw/OmniRoute/pull/13525)) — thanks @KooshaPari +- **fix(reasoning):** expand deepseek-v4 regex to match v4.1+ and add command-code provider (#13430) ([#13528](https://github.com/diegosouzapw/OmniRoute/pull/13528)) — thanks @KooshaPari +- **fix(providers):** add alwaysReasons metadata for reka-flash-3 ([#13531](https://github.com/diegosouzapw/OmniRoute/pull/13531)) — thanks @KooshaPari +- **fix(playground):** allow Compare columns to scroll independently ([#13532](https://github.com/diegosouzapw/OmniRoute/pull/13532)) — thanks @KooshaPari +- **fix(security):** empty whitelist allows through to prevent admin lockout ([#13534](https://github.com/diegosouzapw/OmniRoute/pull/13534)) — thanks @KooshaPari +- **fix(tests):** add DISABLE_IOREG_STRATEGY env var for macOS test compatibility (fixes #13467) ([#13539](https://github.com/diegosouzapw/OmniRoute/pull/13539)) — thanks @KooshaPari +- **fix(zed-hosted):** map developer role and Gemini enums for Zed proxy ([#13543](https://github.com/diegosouzapw/OmniRoute/pull/13543)) — thanks @KooshaPari +- **fix(analytics):** normalize provider aliases to canonical id in usage_history ([#13545](https://github.com/diegosouzapw/OmniRoute/pull/13545)) — thanks @KooshaPari +- **fix(combo):** use traceId for call log id to prevent UNIQUE constraint failure ([#13546](https://github.com/diegosouzapw/OmniRoute/pull/13546)) — thanks @KooshaPari +- **fix(auth):** check model lockout before returning synthetic noauth connection ([#13547](https://github.com/diegosouzapw/OmniRoute/pull/13547)) — thanks @KooshaPari +- **fix(guardrails):** skip credential redaction for base64 image data URLs ([#13550](https://github.com/diegosouzapw/OmniRoute/pull/13550)) — thanks @KooshaPari +- **fix(resilience):** increase requestQueue.maxWaitMs default from 15s to 30s ([#13553](https://github.com/diegosouzapw/OmniRoute/pull/13553)) — thanks @KooshaPari +- **fix(docs):** document OMNIROUTE_READY_TIMEOUT_MS and allowlist the test-only DISABLE_IOREG_STRATEGY ([#13692](https://github.com/diegosouzapw/OmniRoute/pull/13692)) + ### 📝 Maintenance ---- +- **docs(readme):** reconcile live v3.8.50 provider, free-tier, CLI, routing, test, community, sponsor, acknowledgment, and SVG metrics with their audited source denominators, including a deduplicated OmniRoute-in-Action snapshot and distinct contributor rankings for merged pull requests, GitHub-attributed commits, and Git history ([#11356](https://github.com/diegosouzapw/OmniRoute/pull/11356)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **docs:** sync the canonical provider count 354 → 356 across `README.md`, `AGENTS.md`, `llm.txt` (+ 42 i18n mirrors), the four README SVG diagrams, `docs/reference/PROVIDER_REFERENCE.md` (regenerated) and the `package.json` description after Opper (#11629) and 1min.ai (#11631) boarded the catalog — closes the `check:docs-counts-sync` strict drifts that kept `release/v3.8.51` red ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11673](https://github.com/diegosouzapw/OmniRoute/pull/11673)) — thanks @jonlwheat2-gif +- **test(providers):** update count-derived assertions after the v3.8.51 provider additions — `APIKEY_PROVIDERS` 233 → 235 (Opper #11629 + 1min.ai #11631), reserved-prefix REGISTRY walk 395 → 398, `WEB_FETCH_PROVIDERS` now includes `nimble-search` (#11620), and the provider translate-path golden snapshot regenerated ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11674](https://github.com/diegosouzapw/OmniRoute/pull/11674)) — thanks @jonlwheat2-gif +- **test(sse):** bump the hard-lease connection-query inventory for `src/lib/tokenHealthCheck.ts` to 2 — the verify-only web-cookie sweep added by #11495 split the single `getProviderConnections` call into oauth + cookie variants, which the frozen inventory had not tracked ([#11495](https://github.com/diegosouzapw/OmniRoute/pull/11495)). ([#11677](https://github.com/diegosouzapw/OmniRoute/pull/11677)) — thanks @jonlwheat2-gif / @oyi77 +- **chore(release):** clear the `release/v3.8.51` base-red gates — docs migration count (+38 i18n mirrors), stryker `tap.testFiles`, lockfile registry host, stale ESLint suppressions, the 7 lint regressions they were hiding, the stale `APIKEY_PROVIDERS` partition count (231 → 233), the `glm-5.3-max` tier missing from the GLM test table, a dead `modelType === "chat"` comparison in the models catalog, and the `resolveLiveWsUrl` / `sanitizeLiveWsPort` helpers from #11388 that a later merge dropped from `wsPath.ts` (dashboard live WebSocket URL resolution) ([#11502](https://github.com/diegosouzapw/OmniRoute/pull/11502)) — thanks @MumuTW +- **chore(free-models):** the free-tier regime table now declares which totals figure each regime feeds and whether it may take the no-auth shortcut, so the sets that used to repeat those answers by hand are derived from it — a new `freeType` no longer compiles until it has answered every question, instead of silently contributing to no total ([#11537](https://github.com/diegosouzapw/OmniRoute/pull/11537)) — thanks @maxmad64bis +- **docs(routing):** LKGP strategy copy now states provider-level stickiness and the explicit rules fallback instead of promising per-target pinning ([#11555](https://github.com/diegosouzapw/OmniRoute/pull/11555)) — thanks @maxmad64bis +- **test(chatcore):** pin the response tool-name alias resolution against the ordering hazard that broke every Gemini/Antigravity MCP tool call in v3.8.49. `extractRequestToolIdentityMap` deletes `translatedBody._toolNameMap`, so the later read is always undefined and the ledger survives only through the `requestToolIdentityMap` fallback — removing that fallback previously left the entire tool-name suite green. The resolution moves into `resolveResponseToolNameMap()` next to the map it depends on, with a regression guard that fails without the recovery. No behaviour change. ([#11575](https://github.com/diegosouzapw/OmniRoute/pull/11575)) — thanks @amartinawi +- **docs(env):** document `OMNIROUTE_CORPUS_CACHE_SIZE` in `.env.example` and `ENVIRONMENT.md`, so the env/docs contract check stops failing Docs Gates on every branch ([#11576](https://github.com/diegosouzapw/OmniRoute/pull/11576)) — thanks @ntdatt812 +- **chore(stryker):** register `route-guard-tunnel-processes-local-only` and `universal-quota-aware-routing` in `tap.testFiles`, so their mutant kills count for the three modules they cover ([#11577](https://github.com/diegosouzapw/OmniRoute/pull/11577)) — thanks @ntdatt812 +- **test(authz):** pin `LOCAL_ONLY_API_GET_EXEMPTIONS` by exact membership instead of by entry count, so the guard names the offending path and also catches a substitution ([#11580](https://github.com/diegosouzapw/OmniRoute/pull/11580)) — thanks @ntdatt812 +- **test(build):** pin `resolveNextBuildBundlerFlag` to the `OMNIROUTE_USE_TURBOPACK` contract it implements, across runtimes, instead of asserting an unimplemented Bun override ([#11583](https://github.com/diegosouzapw/OmniRoute/pull/11583)) — thanks @ntdatt812 +- **test(claude-web):** the Claude Web transport test's barrel anchors no longer pin the exact construction spelling. #11421 made the executor map lazy, so `/"claude-web": new ClaudeWebExecutor\(\)/` stopped matching and the test went red on `release/v3.8.51` itself — a base-red gate every open PR inherited. The anchors now match the alias key and the class name and tolerate either spelling in between, and were checked to still fail when an alias is genuinely repointed at another executor ([#11585](https://github.com/diegosouzapw/OmniRoute/pull/11585)) — thanks @ntdat812 +- **test(models):** pin `qwen3.8-max` resolution per provider — two of the four catalogs now serve the bare id and no longer carry `-preview` ([#11590](https://github.com/diegosouzapw/OmniRoute/pull/11590)) — thanks @ntdatt812 +- **test(executors):** drop the stale 128-tool truncation assertion that contradicted the #11444 pass-through contract ([#11591](https://github.com/diegosouzapw/OmniRoute/pull/11591)) — thanks @ntdatt812 +- **test(token-health):** pin the current terminal-skip boundary — expired with retry budget is probed, `account_deactivated` stays skipped ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)) — thanks @ntdatt812 +- **test(models):** follow #11503's retarget of the `gemini-3-pro-high` alias to the hyphenated catalog id ([#11593](https://github.com/diegosouzapw/OmniRoute/pull/11593)) — thanks @ntdatt812 +- **refactor(video bridge):** extract per-video acquisition, whole-result caching, description, and metrics/abort/cleanup out of `VideoBridgeGuardrail.preCall` into a `processVideoPart` seam in a new `videoBridgePipeline.ts`, behind explicit `VideoMediaBrokerPort`, `VideoAudioTranscriptionPort`, and `VideoDrilldownPort` boundaries; `preCall` now only handles request traversal, policy, and response aggregation. The Video tab's FFmpeg/ffprobe runtime status is now an explicit `unknown` / `restricted` / `unavailable` / `available` state instead of a nullable boolean pair, fixing a case where an in-flight or failed probe was mislabeled as "install FFmpeg" ([#11657](https://github.com/diegosouzapw/OmniRoute/issues/11657)). ([#12016](https://github.com/diegosouzapw/OmniRoute/pull/12016)) +- **test(opencode-plugin):** add unit test coverage for `formatFreeBudget()` naming helper ([#11660](https://github.com/diegosouzapw/OmniRoute/pull/11660)) — thanks @f9td56dbgh-hub +- **docs(video):** clarify that the Video Bridge transcript `source` field (`client`, `embedded`, `audio-bridge`) is presently caller-declared and not yet server-verified — OmniRoute enforces the enum shape but does not cryptographically confirm that an `embedded`/`audio-bridge` label came from a server-owned extraction ([#11661](https://github.com/diegosouzapw/OmniRoute/issues/11661)). ([#12001](https://github.com/diegosouzapw/OmniRoute/pull/12001)) +- **chore(providers):** retire the distributed Raycast Relay and Hailuo Web integrations after a provenance audit confirmed substantial derivation from GPL-3.0 sources; the official GitHub and MiniMax API providers remain available. Migration 163 disables legacy connections and invalidates their active leases while preserving connection IDs, audit/history rows, quota snapshots, usage records, and fail-closed API-key connection policies. ([#11691](https://github.com/diegosouzapw/OmniRoute/pull/11691)) +- **chore(providers):** retire the Felo Web provider while its GPL-derived provenance remains on licensing HOLD ([#11698](https://github.com/diegosouzapw/OmniRoute/pull/11698)) — stale runtime and database references now fail closed while existing connection, lease, and usage history remains preserved for auditability. +- **fix(sse):** retire the Gemini Web image-generation bridge while provenance and licensing remain on HOLD, preserving Gemini Web chat/auth/tools and legitimate Gemini/Nano Banana image providers ([#11708](https://github.com/diegosouzapw/OmniRoute/pull/11708)) +- **chore(audio):** retire the built-in EdgeTTS provider and its 11-voice catalog while provenance/licensing review remains on HOLD; the generic `/v1/audio/speech` endpoint and the other speech providers remain available ([#11711](https://github.com/diegosouzapw/OmniRoute/pull/11711)) +- **fix(providers):** retire the unofficial Qwen Web integration and its `qwen-web`/`qw` identities while third-party provenance remains on HOLD ([#11713](https://github.com/diegosouzapw/OmniRoute/pull/11713)) — direct requests now fail closed with a sanitized HTTP 410 and combos skip the retired target, while migration guards preserve connection IDs, allowlists, usage/audit history and invalidate active leases. Supported Qwen API, Qwen Code, cloud, token-plan, audio, local and model-name surfaces remain available; restoring Qwen Web requires a qualified provenance/license decision. +- **fix(providers):** retire `microsoft-designer-web` and its `msdesigner` alias from runtime and catalogs while preserving stored credentials, provider history and allowlists as inactive records — source dependency on gpt4free's [`MicrosoftDesigner.py`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/g4f/Provider/needs_auth/MicrosoftDesigner.py) is proven at snapshot `0943d16eef757a44deb60651c07bbe5fa958cafe`, whose [`LICENSE`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/LICENSE) is GPL-3.0; expressive adaptation is probable, which records provenance/licensing risk without asserting a legal conclusion ([#11720](https://github.com/diegosouzapw/OmniRoute/pull/11720)). +- **docs(legal):** centralize pinned MIT notices for the vendored GCF codec and local flag assets ([#11726](https://github.com/diegosouzapw/OmniRoute/pull/11726)) +- Split the 2,952-line `adobeFireflyClient.ts` into nine focused modules (catalog, credentials, payload, ARP session, upload, responses, IMS, account, poll) behind an unchanged barrel — no consumer edits and no public API change (#11728). — thanks @MumuTW +- **fix(dashboard):** replaced the Model Lockout card's unprovenanced notification MP3 with a short, locally synthesized Web Audio chime ([#11731](https://github.com/diegosouzapw/OmniRoute/pull/11731)) — both toggles retain optional audible feedback without shipping replacement media or third-party code, and unsupported, suspended, or rejected audio contexts fail open without blocking the setting change. +- **chore(deepseek-web):** remove the provenance-unresolved DeepSeek PoW WASM binary and its runtime loader/tracing while retaining the existing JavaScript solver (slower at high difficulty) ([#11732](https://github.com/diegosouzapw/OmniRoute/pull/11732)). +- **chore(assets):** remove four unused provider assets whose redistribution provenance could not be verified while retaining the active Freebuff SVG ([#11735](https://github.com/diegosouzapw/OmniRoute/pull/11735)) +- **docs(assets):** retired two raster images whose redistribution provenance could not be verified ([#11736](https://github.com/diegosouzapw/OmniRoute/pull/11736)) — PicoClaw documentation now reuses the existing SVG, while Zoo Code remains a text link without an image. +- **docs(legal):** add pinned notices for six LobeHub-derived provider SVGs and 65 byte-exact theSVG assets, retaining registry claims as unverified metadata with explicit trademark and HOLD boundaries ([#11737](https://github.com/diegosouzapw/OmniRoute/pull/11737)). +- **chore(quality):** added a versioned 225/225 provider-asset provenance manifest and blocking drift gate ([#11743](https://github.com/diegosouzapw/OmniRoute/pull/11743)) — the inventory pins magic MIME and SHA-256 for every physical file, records immutable source matches for 71 assets, keeps 69 probable and 85 unresolved entries explicit but non-blocking, and treats provenance as source evidence rather than copyright or trademark clearance. +- **refactor(mitm):** replace four previously derived Traffic Inspector utilities — conversation normalization, SSE merging, secret masking, and header sanitization — with independent clean-room implementations based on public provider schemas, WHATWG SSE, and RFC 6750, while keeping the two independently classified passthrough surfaces unchanged ([#11748](https://github.com/diegosouzapw/OmniRoute/pull/11748)). +- Added a unit test that fails seven days before any dated pack under `config/` (`validUntil` and sibling keys) lapses, naming the file and key. The Alibaba free-tier pack expired on 2026-08-27 and turned every PR red the next morning with no commit involved; renewal now happens on someone's terms, not the clock's. ([#11891](https://github.com/diegosouzapw/OmniRoute/pull/11891)) +- `check:workflows` now fails (under `--strict`/`--ratchet`) when any job routed to a self-hosted runner publishes with `--provenance` — npm rejects that with `422` at the registry, which in v3.8.50 only surfaced after the tag and Docker images were public. ([#11895](https://github.com/diegosouzapw/OmniRoute/pull/11895)) +- `scripts/ops/runner-janitor.sh` now proves a path is idle with one `lsof` snapshot and removes stale leftovers itself (tmpfs after 3 h — it is RAM — disk after 24 h), kills orphan `next-build` processes, prunes checkouts of stopped runners, and alerts on memory pressure; `--dry-run` shows exactly what it would do. `docs/ops/RUNNER_BOX.md` reconciled to the measured box (31 GB, 10 listeners). ([#11893](https://github.com/diegosouzapw/OmniRoute/pull/11893)) +- The `next-build` artefact (1.3 GB) is now written and read under `$RUNNER_TEMP` (per-runner, on disk) instead of `/tmp`, which on the self-hosted pool is a 12 GB tmpfs in RAM. Landing it there took 27–32 of the publish job's 76 minutes, and the fixed `/tmp/e2e-build.tar.gz` name let E2E jobs on different runners overwrite each other's download. ([#11896](https://github.com/diegosouzapw/OmniRoute/pull/11896)) +- The CI `build` job now runs in two concurrency lanes — `main` and pull requests — so a release build is never queued behind (or OOM-killed beside) PR builds on the self-hosted pool, which holds one `next-build` comfortably and two at the edge. ([#11901](https://github.com/diegosouzapw/OmniRoute/pull/11901)) +- Re-freeze the ESLint suppressions on `release/v3.8.51` from a clean-room measurement (2 stale file entries pruned, 55 pre-existing `no-explicit-any` in six new files frozen under #11924) and drop the dead `GPT_SIZE_MAP` constant orphaned by the Adobe Firefly client split, so `No new ESLint warnings` stops failing every PR with exit 2 (Refs #11924) ([#11955](https://github.com/diegosouzapw/OmniRoute/pull/11955)) +- Type the 55 `no-explicit-any` sites that had been frozen under #11924 — four redundant casts in `socksConnectorWithFamily.ts` (undici/socks types already accept them) and the mocks/fixtures of the socks-timeout and isFree suites — and drop their suppression entries; the ESLint ratchet shrinks from 5487 to 5432 (Closes #11924) ([#11975](https://github.com/diegosouzapw/OmniRoute/pull/11975)) +- Take the two hosted-runner builds off the PR rail: `Build App` (`build.yml`) is `workflow_dispatch`-only and `DAST smoke (PR)` runs only for PRs into `main` — the 7 GB hosted VM cannot build `release/v3.8.51` in any profile (19/30 red, VM shutdown ~8 min into `next build`) and both checks had turned into permanent noise on every release PR; the bundle stays validated by `ci.yml` on `main` and by `nightly-release-green` on `release/**` (Closes #11946) ([#11962](https://github.com/diegosouzapw/OmniRoute/pull/11962)) +- Move the four nightly jobs that build the backend (`nightly-schemathesis`, `nightly-llm-security` promptfoo + garak, `nightly-resilience` axe-a11y) off the hosted 7 GB runner — where they died on `release/v3.8.51` unseen — onto the box's new `omni-light` pool (two listeners), and document the reshaped fleet (4 active OmniRoute listeners: 2 `omni-build` + 2 `omni-light`, janitor ceiling 4) (Closes #11965) ([#11967](https://github.com/diegosouzapw/OmniRoute/pull/11967)) +- Stop painting every fork PR into `release/**` red: `quality.yml` `Build (advisory)` is skipped (GitHub still reports `continue-on-error` failures as check FAILURE). Hosted `ubuntu-latest` cannot finish `npm run build` on this tree — same class as #11962 taking `build.yml` off the PR rail. `docker-publish.yml` amd64 now runs on the `.113` `omni-build` pool (31 GB / 32 cores, two listeners) with Turbopack, shares the `heavy-build-main` lane with `ci.yml` `Build` so it queues instead of becoming a third heavy, and keeps per-ref concurrency (a merge storm was starting 8 concurrent OOM builds). arm64 stays on `ubuntu-24.04-arm` with webpack — there is no ARM box. Fallback when `USE_VPS_RUNNER` is off: hosted amd64 + webpack (#11976). ([#12021](https://github.com/diegosouzapw/OmniRoute/pull/12021)) +- `docker-publish.yml` builds with webpack (`OMNIROUTE_USE_TURBOPACK=0`) on **both** arches, including amd64 on the `.113` `omni-build` pool. Turbopack had the RAM (31 GB, `omniroute-113-6`) and still panicked (`TurbopackInternalError: there must be a path to a root`, run 33253576569, 26 min in). The same tree's arm64 webpack build on hosted `ubuntu-24.04-arm` succeeded (run 33264823398). Dockerfile already documents webpack as the Docker escape hatch (#11976). ([#12050](https://github.com/diegosouzapw/OmniRoute/pull/12050)) +- **docs(free-tier):** declare the counting vs deciding regimes for "is it free?" and guard the deciding path from DB-backed catalog resolution ([#12226](https://github.com/diegosouzapw/OmniRoute/pull/12226)) — thanks @maxmad64bis +- **docs(auto-combo):** The mode pack table in `docs/routing/AUTO-COMBO.md` now lists all six shipped packs with every weight each one sets, replacing a four-pack table whose numbers had also drifted from the source. It states plainly that no pack sets `quality`, so selecting any pack silences the observed-quality signal. Six more documents that quote the scoring factor count joined the `check:docs-counts` gate, which caught five stale claims — including one naming nine factors that do not exist — and two stale mode pack counts. The dashboard routing panel, which offered four of the six packs and labelled the default strategy "6-Factor Scoring", is now covered by a test; the two packs it was missing are `reliability-first` and `chaos-mode`, the latter labelled as the fault-injection profile it is rather than as one more routing preference ([#12316](https://github.com/diegosouzapw/OmniRoute/pull/12316)) — thanks @maxmad64bis +- **chore(quality):** rebaseline `src/lib/db/apiKeys.ts` for the ACL the key-creation path now preserves ([#12352](https://github.com/diegosouzapw/OmniRoute/pull/12352)) ([#12673](https://github.com/diegosouzapw/OmniRoute/pull/12673)) — thanks @KrzysiekSko +- **chore(providers):** bump the Claude Code wire identity and the Devin bridge image pin from `2.1.220` to `2.1.258` ([#12402](https://github.com/diegosouzapw/OmniRoute/pull/12402)) — thanks @ggiak ([#12604](https://github.com/diegosouzapw/OmniRoute/pull/12604)) +- **docs(env):** align `.env.example`, the README Bun section, and the troubleshooting guide with the code: `OMNIROUTE_USE_TURBOPACK` also governs `npm run build` (not dev-only), `bun run build` follows that flag instead of auto-selecting Webpack, `OMNIROUTE_CHAT_MAX_HEAVY_IN_FLIGHT` is unset by default (no request-count cap), and the structural `503 chat_admission_busy` message matches `chatAdmissionResponses.ts` (#12404 — thanks @pacocartones) +- **chore(electron):** upgrade the desktop app to Electron 44 (Chromium 152, Node 24.18.1) ([#12217](https://github.com/diegosouzapw/OmniRoute/pull/12217)). **Requires macOS 13 (Ventura) or later** — Chromium dropped macOS 12 (Monterey), so Monterey users must stay on an earlier OmniRoute desktop build. Windows and Linux are unaffected; the app already shipped only x64/arm64, so Electron 44 dropping 32-bit builds changes nothing. Removes the `openAsHidden`/`wasOpenedAsHidden` login-item fields deleted in Electron 44 — hidden autostart continues to work through the `--hidden` argument registered with the login item ([#12554](https://github.com/diegosouzapw/OmniRoute/pull/12554)) — thanks @app/dependabot +- **chore(quality):** rebaseline `src/sse/handlers/chat.ts` for the effective-input persistence the continuation fix needs ([#12641](https://github.com/diegosouzapw/OmniRoute/pull/12641)) ([#12680](https://github.com/diegosouzapw/OmniRoute/pull/12680)) — thanks @hartmark +- `Coverage` job on `ci.yml`: the informational Codecov upload gets its own 5-minute ceiling and `continue-on-error`, and the job budget grows from 20 to 30 minutes (the 8-shard c8 merge alone takes ~10) — a stalled upload no longer ends the job `cancelled` and drags a fully green `main` run's conclusion down with it ([#11972](https://github.com/diegosouzapw/OmniRoute/pull/11972)) +- CI hardening on the PR rail: the four `Unit Tests fast-path` shards get `timeout-minutes: 30` (a hung shard held a PR for 64 min instead of GitHub's 6 h default) and both ESLint file caches lose their `restore-keys` fallback, so a cache built under another suppressions file or lint config can no longer report stale verdicts (Refs #11600, #11924) ([#11963](https://github.com/diegosouzapw/OmniRoute/pull/11963)) +- Every CI job that runs a `next build` (`build`, the npm `publish`, both release-green validations) now targets the `omni-build` runner label, which only two of the eight self-hosted runners carry. The box holds one build comfortably and two at the edge; a third now queues on GitHub instead of being OOM-killed by the kernel. ([#11932](https://github.com/diegosouzapw/OmniRoute/pull/11932)) +- **refactor(combo):** move `handleRoundRobinCombo` (and `resolveTargetTokenLimit`) into `open-sse/services/combo/roundRobinCombo.ts`. `combo.ts` drops from 2164 to 1014 lines (`split("\n").length`); the leaf is 1199 (under the 1200 new-file cap). The round-robin call site uses a dynamic `import()` so `releaseStickyPinOnFailure` / `clearStaleLKGP` can stay exported from `combo.ts` without a static cycle. Skip / sticky / semaphore / safety-timer behavior is unchanged. ([#12811](https://github.com/diegosouzapw/OmniRoute/pull/12811)) — thanks @HouMinXi +- **chore(quality):** rebaseline the file-size caps the error-boundary campaign grew past (`open-sse/executors/codex.ts`, `open-sse/vendor/codex-chatgpt-web/bridge.ts`, both via [#12444](https://github.com/diegosouzapw/OmniRoute/pull/12444)) ([#12654](https://github.com/diegosouzapw/OmniRoute/pull/12654)) +- **chore(quality):** rebaseline the file-size caps the hartmark batch grew past (`combos/page.tsx` via [#12355](https://github.com/diegosouzapw/OmniRoute/pull/12355), `open-sse/services/combo.ts` via [#12338](https://github.com/diegosouzapw/OmniRoute/pull/12338)) ([#12623](https://github.com/diegosouzapw/OmniRoute/pull/12623)) — thanks @hartmark +- **chore(quality):** rebaseline the file-size caps the HouMinXi batch grew past when its PRs stacked (`providers/page.tsx`, `chatCore.ts`, `accountFallback.ts`) — each PR measured correctly in isolation, none saw the stacking ([#12619](https://github.com/diegosouzapw/OmniRoute/pull/12619)) +- **chore(quality):** rebaseline `open-sse/services/combo.ts` for the reset-aware scoring the HouMinXi batch stacked ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) ([#12678](https://github.com/diegosouzapw/OmniRoute/pull/12678)) — thanks @HouMinXi +- **docs:** sync provider catalog count (direct commit `4f11b2ae3d`) +- **docs(skills):** sync omni-inference search fallback (direct commit `b4e76a7ed9`) +- **chore(lint):** prune two stale base suppressions (direct commit `1325047d56`) +- **test(auto-combo):** tolerate full-suite rotation load (direct commit `ad3e293f9f`) +- **test:** drain inherited quality snapshot reds (direct commit `21ed68d8ac`) +- **chore(quality):** rebaseline measured CLI bundle growth (direct commit `d35b3f9779`) +- **test(ui):** reproduce invalid AnySearch icon alias (direct commit `12cc6ca834`) +- **test(translator):** fix the relative imports of the relocated deferred-finish test (direct commit `777d9d1629`) +- **docs(counts):** bring the documented provider and migration counts to the measured values (direct commit `e70bea30e9`) +- **test(security):** drop the guard-target set left unused by the sync (direct commit `a32ba0e362`) +- **chore(db):** renumber model_capabilities migration 163 → 169 (cross-branch collision with 163_radar_feed_cache_generated_at) (direct commit `70f5d4cbf9`) +- **docs(cli):** document CLI_PRIME_AGENT_BIN and correct the CLI Agents count (direct commit `9058e39b61`) +- **chore(deps):** refresh runtimes and adopt ESLint 10 ([#11259](https://github.com/diegosouzapw/OmniRoute/pull/11259)) — thanks @backryun +- **refactor(local-corpus):** implement dynamic root resolution and LRU cache ([#11491](https://github.com/diegosouzapw/OmniRoute/pull/11491)) — thanks @raheemuddin786 +- **chore(quality):** prune stale eslint-suppressions entries ([#11596](https://github.com/diegosouzapw/OmniRoute/pull/11596)) +- **chore(ci):** unblock quality gates - env-doc sync + stryker tap.testFiles registration (#9282) ([#11609](https://github.com/diegosouzapw/OmniRoute/pull/11609)) — thanks @Meet6338-X +- **chore(ci):** point the circular-deps gate at dpdm's real JS entrypoint ([#11615](https://github.com/diegosouzapw/OmniRoute/pull/11615)) — thanks @benzntech +- **chore(quality):** rebaseline file-size for #11448/#11495/#11561 growth ([#11630](https://github.com/diegosouzapw/OmniRoute/pull/11630)) +- **deps:** realign onnxruntime-node pin to @huggingface/transformers' 1.24.3 lockstep ([#11633](https://github.com/diegosouzapw/OmniRoute/pull/11633)) — thanks @jonlwheat2-gif +- **test(autoCombo):** port models_dev_tier #11508 guard from node:test to vitest ([#11635](https://github.com/diegosouzapw/OmniRoute/pull/11635)) — thanks @jonlwheat2-gif +- **chore(quality):** rebaseline file-size for #11083/#11631/#11628 growth ([#11636](https://github.com/diegosouzapw/OmniRoute/pull/11636)) +- **chore(lint):** prune stale ESLint suppressions (base-red #11449) ([#11644](https://github.com/diegosouzapw/OmniRoute/pull/11644)) — thanks @jonlwheat2-gif +- **docs(changelog):** aggregate the ten v3.8.50 fragments into CHANGELOG.md ([#11683](https://github.com/diegosouzapw/OmniRoute/pull/11683)) +- **chore(search):** drop unreachable credential sweep after #11642 ([#11687](https://github.com/diegosouzapw/OmniRoute/pull/11687)) — thanks @HouMinXi +- **ci:** add API route TypeScript regression gate ([#11705](https://github.com/diegosouzapw/OmniRoute/pull/11705)) — thanks @Sabeekhann +- **docs(dedup):** correct the CodeQL claim on the tenant-namespace comment ([#11712](https://github.com/diegosouzapw/OmniRoute/pull/11712)) +- **docs(changelog):** add consolidated v3.8.50 stats, top-25 ranking and a .mailmap ([#11715](https://github.com/diegosouzapw/OmniRoute/pull/11715)) +- **docs:** recommend gstack for AI-assisted workflows ([#11770](https://github.com/diegosouzapw/OmniRoute/pull/11770)) — thanks @santosraju99-hub +- **chore(quality):** tighten complexity/cognitive-complexity ratchets to the current tip; land the missed gateways.ts rebaseline ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **docs(i18n):** finish Freepik → Magnific rebrand in locale strings and README ([#11772](https://github.com/diegosouzapw/OmniRoute/pull/11772)) — thanks @turbolego +- **refactor(auto-combo):** fix divergent scoring in combo health reporting ([#11854](https://github.com/diegosouzapw/OmniRoute/pull/11854)) — thanks @jacobsparts +- **chore(changelog):** add missing fragments for #11919/#11918/#11916 ([#11938](https://github.com/diegosouzapw/OmniRoute/pull/11938)) +- **test(check):** escape the runs-on fixture with JSON.stringify, not a quote-only replace ([#11942](https://github.com/diegosouzapw/OmniRoute/pull/11942)) (direct commit `fb7445eaa3`) +- **test(release):** align five suites with the contracts #11933, #11919 and #11876 shipped on release/v3.8.51 ([#11944](https://github.com/diegosouzapw/OmniRoute/pull/11944)) +- **chore(quality):** remove unreachable code and restore test discovery ([#11950](https://github.com/diegosouzapw/OmniRoute/pull/11950)) — thanks @backryun +- **docs(agents):** sync-back landings are fast-forward, never squash ([#11964](https://github.com/diegosouzapw/OmniRoute/pull/11964)) +- **test(infra):** retry recursive temp-dir removal instead of failing a shard on ENOTEMPTY (#11966) ([#11968](https://github.com/diegosouzapw/OmniRoute/pull/11968)) +- **docs(ops):** the .113 heavy-build ceiling is one runner, not two ([#12048](https://github.com/diegosouzapw/OmniRoute/pull/12048)) +- **test(ui):** correct inactive auto-fetch expectation ([#12098](https://github.com/diegosouzapw/OmniRoute/pull/12098)) — thanks @RaviTharuma +- **docs:** document native dependency check escape hatch ([#12101](https://github.com/diegosouzapw/OmniRoute/pull/12101)) — thanks @RaviTharuma +- **test(providers):** regenerate the translate-path golden for OrcaRouter (#11923) ([#12118](https://github.com/diegosouzapw/OmniRoute/pull/12118)) +- **chore(quality):** velocity phase — loosen every numeric baseline by 20% until v4.0, monitor headroom nightly ([#12125](https://github.com/diegosouzapw/OmniRoute/pull/12125)) +- **chore(lint):** batch 0 of #12146 — type the call-log-cap sqlite rows instead of 45 as-any casts ([#12157](https://github.com/diegosouzapw/OmniRoute/pull/12157)) +- **chore(lint):** batch 4 of #12146 — shared/components react-hooks violations resolved ([#12159](https://github.com/diegosouzapw/OmniRoute/pull/12159)) +- **chore(lint):** batch 1 of #12146 — dashboard/cli-code react-hooks violations resolved ([#12160](https://github.com/diegosouzapw/OmniRoute/pull/12160)) +- **chore(lint):** batch 3 of #12146 — dashboard/settings react-hooks violations resolved ([#12162](https://github.com/diegosouzapw/OmniRoute/pull/12162)) +- **chore(lint):** batch 2 of #12146 — resolve the react-hooks compiler violations in dashboard/providers ([#12163](https://github.com/diegosouzapw/OmniRoute/pull/12163)) +- **chore(quality):** register search-432 cooldown test in stryker tap.testFiles ([#12170](https://github.com/diegosouzapw/OmniRoute/pull/12170)) +- **chore(lint):** batch 5 of #12146 — combos, endpoint, provider-stats, api-manager and costs react-hooks violations resolved ([#12174](https://github.com/diegosouzapw/OmniRoute/pull/12174)) +- **build:** add contributor fast profile ([#12192](https://github.com/diegosouzapw/OmniRoute/pull/12192)) — thanks @rafacpti23 +- **docs(readme):** README touch-ups ([#12193](https://github.com/diegosouzapw/OmniRoute/pull/12193)) ([#12194](https://github.com/diegosouzapw/OmniRoute/pull/12194)) ([#12202](https://github.com/diegosouzapw/OmniRoute/pull/12202)) — thanks @rafacpti23 +- **build:** force the webpack bundler in the contributor build profile ([#12197](https://github.com/diegosouzapw/OmniRoute/pull/12197)) — thanks @rafacpti23 +- **build:** skip standalone packaging in the contributor profile ([#12198](https://github.com/diegosouzapw/OmniRoute/pull/12198)) — thanks @rafacpti23 +- **docs(audit):** align every published number with the code and harden check:docs-counts ([#12200](https://github.com/diegosouzapw/OmniRoute/pull/12200)) +- **build:** stub the instrumentation entrypoints in the contributor profile ([#12203](https://github.com/diegosouzapw/OmniRoute/pull/12203)) — thanks @rafacpti23 +- **build:** omit the standalone output target for contributor builds ([#12204](https://github.com/diegosouzapw/OmniRoute/pull/12204)) — thanks @rafacpti23 +- **chore(lint):** batch 6 of #12146 — memory, radar, audit, analytics, cache, usage, activity, home and RequestLoggerV2 react-hooks violations resolved ([#12208](https://github.com/diegosouzapw/OmniRoute/pull/12208)) +- **docs(diagrams):** rename number-carrying diagram files to stable names ([#12210](https://github.com/diegosouzapw/OmniRoute/pull/12210)) +- **docs(dashboard):** redraw onboarding tier-flow SVGs for the real 4-tier model ([#12211](https://github.com/diegosouzapw/OmniRoute/pull/12211)) +- **docs(api):** document every implemented route in openapi.yaml (276 → 692 paths) ([#12212](https://github.com/diegosouzapw/OmniRoute/pull/12212)) +- **chore(lint):** batch 7 of #12146 — final src tail: 37 react-hooks violations across 33 files resolved ([#12244](https://github.com/diegosouzapw/OmniRoute/pull/12244)) +- Revert "docs: recommend gstack for AI-assisted workflows (#11770)" ([#12248](https://github.com/diegosouzapw/OmniRoute/pull/12248)) ([#12249](https://github.com/diegosouzapw/OmniRoute/pull/12249)) +- **docs:** Chaos Mode setup guide + weighted strategy semantics ([#12250](https://github.com/diegosouzapw/OmniRoute/pull/12250)) +- **docs(agents):** protected-surface merge rule — operator approval for agent-instruction files ([#12253](https://github.com/diegosouzapw/OmniRoute/pull/12253)) +- **chore(quality):** register native-codex turn-pin tests in stryker tap.testFiles ([#12263](https://github.com/diegosouzapw/OmniRoute/pull/12263)) +- **chore(quality):** dedupe tap.testFiles entries added by racing base-red fixes ([#12265](https://github.com/diegosouzapw/OmniRoute/pull/12265)) +- **docs(readme):** introduce OmniRouteTray — the macOS menu-bar companion ([#12276](https://github.com/diegosouzapw/OmniRoute/pull/12276)) — thanks @ggiak +- **chore(proxy):** purge the legacy 1proxy residue — sync/rotator modules, dead DB exports, dead settings tab and flag, docs (#12091) ([#12290](https://github.com/diegosouzapw/OmniRoute/pull/12290)) +- **chore(quality):** baseline-headroom skips generated and vendored files in the fileSize worst-file signal ([#12291](https://github.com/diegosouzapw/OmniRoute/pull/12291)) +- **refactor(video):** unify the JPEG frame data-URI contract ([#12322](https://github.com/diegosouzapw/OmniRoute/pull/12322)) +- **chore(deps):** freeze onnxruntime-node and eslint-plugin-react-hooks out of dependabot groups ([#12329](https://github.com/diegosouzapw/OmniRoute/pull/12329)) +- **test(executors):** restore the #10986 reasoning-only fallback guards ([#12364](https://github.com/diegosouzapw/OmniRoute/pull/12364)) — thanks @pacocartones +- **chore(quality):** re-tighten the file-size ratchet to the real LOC (plan 3.8.52 task 0) ([#12411](https://github.com/diegosouzapw/OmniRoute/pull/12411)) +- **test(grok):** format web executor suite ([#12412](https://github.com/diegosouzapw/OmniRoute/pull/12412)) +- **chore(quality):** ratchet open-sse typecheck baseline to zero ([#12418](https://github.com/diegosouzapw/OmniRoute/pull/12418)) +- **test(build):** guard the artifact path policy arrays against duplicates ([#12422](https://github.com/diegosouzapw/OmniRoute/pull/12422)) +- **chore(lint):** adopt eslint-plugin-react-hooks 7.1.1 ([#12428](https://github.com/diegosouzapw/OmniRoute/pull/12428)) +- **chore(providers):** remove a keyless provider integration at its operator's request ([#12440](https://github.com/diegosouzapw/OmniRoute/pull/12440)) +- **docs(providers):** register providers removed at their operator's request and guard against reintroduction ([#12478](https://github.com/diegosouzapw/OmniRoute/pull/12478)) +- **chore(quality):** base fixes — stryker tap.testFiles + node_modules cache key ([#12482](https://github.com/diegosouzapw/OmniRoute/pull/12482)) +- **docs(arch):** one-process recipe for tens of long /v1/responses ([#12493](https://github.com/diegosouzapw/OmniRoute/pull/12493)) — thanks @RaviTharuma +- **chore(quality):** tighten the CodeQL ratchet baseline from 11 to 6 ([#12530](https://github.com/diegosouzapw/OmniRoute/pull/12530)) +- **chore(deps):** pin browserslist override to ^4.28.8 ([#12592](https://github.com/diegosouzapw/OmniRoute/pull/12592)) — thanks @KooshaPari +- **chore(deps):** bump fast-uri to 3.1.7 in the electron lockfile ([#12601](https://github.com/diegosouzapw/OmniRoute/pull/12601)) +- **docs(quality):** document how the CodeQL ratchet refreshes and how to tighten it ([#12611](https://github.com/diegosouzapw/OmniRoute/pull/12611)) +- **test(ci):** pin the openapi-security-tiers two-arm contract with an executing gate test (#12581) ([#12652](https://github.com/diegosouzapw/OmniRoute/pull/12652)) +- **docs(gamification):** align XP Rewards table with code (#12501) ([#12667](https://github.com/diegosouzapw/OmniRoute/pull/12667)) — thanks @KooshaPari +- **refactor(combo):** split executeTarget into gates, attempt, and loop ([#12746](https://github.com/diegosouzapw/OmniRoute/pull/12746)) — thanks @HouMinXi +- **docs(security):** document socket.yml scanner config + CI workflow link (#12575) ([#12764](https://github.com/diegosouzapw/OmniRoute/pull/12764)) — thanks @KooshaPari +- **chore(ci):** guard commit identity in pre-commit to stop author misattribution ([#12772](https://github.com/diegosouzapw/OmniRoute/pull/12772)) +- **deps:** 17 Dependabot bumps — bump electron from 43.4.0 to 43.4.1 in /electron ([#11426](https://github.com/diegosouzapw/OmniRoute/pull/11426)); bump the development group across 1 directory with 8 updates ([#11428](https://github.com/diegosouzapw/OmniRoute/pull/11428)); bump the production group across 1 directory with 11 updates ([#11440](https://github.com/diegosouzapw/OmniRoute/pull/11440)); chore(deps): bump github/codeql-action/init from 4.37.7 to 4.37.8 ([#11925](https://github.com/diegosouzapw/OmniRoute/pull/11925)); chore(deps): bump github/codeql-action from 4.37.7 to 4.37.8 ([#11926](https://github.com/diegosouzapw/OmniRoute/pull/11926)); chore(deps): bump github/codeql-action/analyze from 4.37.7 to 4.37.8 ([#11927](https://github.com/diegosouzapw/OmniRoute/pull/11927)); build(deps): bump github/codeql-action/init from 4.37.8 to 4.37.9 ([#12345](https://github.com/diegosouzapw/OmniRoute/pull/12345)); build(deps): bump github/codeql-action/analyze from 4.37.8 to 4.37.9 ([#12346](https://github.com/diegosouzapw/OmniRoute/pull/12346)); bump the development group across 1 directory with 2 updates ([#12347](https://github.com/diegosouzapw/OmniRoute/pull/12347)); build(deps): bump github/codeql-action from 4.37.8 to 4.37.9 ([#12349](https://github.com/diegosouzapw/OmniRoute/pull/12349)); bump browserslist from 4.28.2 to 4.28.8 ([#12396](https://github.com/diegosouzapw/OmniRoute/pull/12396)); bump the production group across 1 directory with 4 updates ([#12399](https://github.com/diegosouzapw/OmniRoute/pull/12399)); bump @xmldom/xmldom ([#12500](https://github.com/diegosouzapw/OmniRoute/pull/12500)); bump qs from 6.15.2 to 6.16.0 ([#12512](https://github.com/diegosouzapw/OmniRoute/pull/12512)); bump @xmldom/xmldom from 0.9.10 to 0.9.12 ([#12513](https://github.com/diegosouzapw/OmniRoute/pull/12513)); bump fast-uri from 3.1.5 to 3.1.7 ([#12514](https://github.com/diegosouzapw/OmniRoute/pull/12514)); bump @humanfs/node from 0.16.7 to 0.16.8 ([#12515](https://github.com/diegosouzapw/OmniRoute/pull/12515)) +- Clear the `release/v3.8.51` `check:agent-skills-sync` base-red: regenerate `skills/cli-tunnel/SKILL.md` so the `tunnel create [type]` positional that #13009 taught the generator to read is reflected in the committed skill. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` typecheck base-red from #12731: the new `Mode pack ranking gates` candidates in `open-sse/services/autoCombo/__tests__/autoCombo.test.ts` omitted the required `provider`, `model` and `errorRate` fields of `ProviderCandidate`, failing `check:open-sse-typecheck` (and with it `Fast Quality Gates`) on every open PR. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` docs-sync base-red: EURouter (#13025) and GreenPT (#13024) took the live provider count to 358, leaving 7 STRICT drifts (`PROVIDER_REFERENCE.md`, 4 diagrams, 2 tier-flow images) plus the `AGENTS.md` / `llm.txt` / `package.json` count claims stale. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- **test(models):** Pin `CATALOG_BUILD_TIMEOUT_MS` in the #12058 canonical-catalog regression so a cold catalog build racing the 8s default bound no longer flakes the suite with a 500 ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Allow `@omniroute/opencode-plugin-v2/` in the npm pack policy — [#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870) shipped the v2 plugin beside its v1 sibling without widening the allowlist, so every packed file under it read as an unexpected artifact and failed `check:pack-policy` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Point the release-green validator's pack gate at the tree it is validating — the build-provenance guard defaulted to `origin/main`, which a release branch tip can never be an ancestor of mid-cycle, so `Package artifact` reported a false red on every run and the tarball boot-smoke cascaded off it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **docs(checks):** keep doc counts honest — headings, rankings, catalog, weights, quality gate and scoring diagram now covered ([#12507](https://github.com/diegosouzapw/OmniRoute/pull/12507)) — thanks @maxmad64bis +- **chore(lifecycle):** `check:model-lifecycle` now also diffs `DEFAULT_DEGRADATION_MAP` (the background-task redirect table) against the vendor lifecycle snapshot, refusing a retired id as source or target, with a table-driven unit test beside it. Three rows whose source the vendor had retired — `claude-sonnet-4-20250514`, `gemini-3-pro-preview` and `gpt-5.1-codex` (whose target `gpt-5.1-codex-mini` is retired too) — were dead code, since `checkLifecycle` answers 410 before the redirect runs; they are dropped (#12535 — thanks @pacocartones) +- **test(compression):** cover idle worker eviction at the resource level — the pool must call `terminate()` and must not retain the worker's `MessagePort`, complementing the `exit`-event assertion added with the fix ([#13371](https://github.com/diegosouzapw/OmniRoute/pull/13371)) +- **chore(quality):** raise the `imageGeneration.ts` file-size ceiling for the image-only-model guard that clears the #12945 base-red ([#13392](https://github.com/diegosouzapw/OmniRoute/pull/13392)) +- **chore(quality):** type the combo-test route's JSON response bodies instead of casting them to `any`, drop the now-empty suppression entry, and remove the `hasPerModelQuota` import `chatCore.ts` stopped using when the failure-classification helper was extracted ([#13379](https://github.com/diegosouzapw/OmniRoute/pull/13379)) +- **chore(electron):** the desktop bundle no longer ships the root-level files of every translated docs mirror (`README.md`, `llm.txt`, `CLAUDE.md`, `GEMINI.md`, `CONTRIBUTING.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`) — the packaged app only reads `docs/i18n//docs/**`, which stays. Saves ~11 MB on top of the translated CHANGELOGs already pruned. (#0000) ([#13235](https://github.com/diegosouzapw/OmniRoute/pull/13235)) +- **chore(quality):** retighten the `src/sse/handlers/chat.ts` unused-vars suppression count to the 8 violations that actually remain ([#13385](https://github.com/diegosouzapw/OmniRoute/pull/13385)) +- Bring the documented counts back in line with the code: 171 migrations (was 169) and 20 routing strategies (was 19), plus the regenerated `cli-tunnel` skill reference. ([#12970](https://github.com/diegosouzapw/OmniRoute/pull/12970)) +- Update the documented migration count to 172 after the call-logs provider-stats indexes landed. ([#13209](https://github.com/diegosouzapw/OmniRoute/pull/13209)) +- **chore(ci):** two gates that close the blind spots behind the exclusions above. `check:vitest-exclusions` requires every Vitest exclusion to name a tracking issue and to appear in `config/quality/vitest-exclusions.json` — the previous list grew to 62 files behind a comment pointing at an issue that had been closed for a month. `check-new-key-coverage` requires a key newly added to `en.json` to reach every locale; the existing coverage gate is a percentage floor per locale, so eleven absent keys out of ~13,000 left it at 99.9% while a whole feature shipped untranslated in nine languages. Both are diff-aware, so pre-existing debt stays frozen and neither needed a migration to turn on. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **build:** root layout no longer loads Inter from `next/font/google`, so a production image build does not need fonts.googleapis.com ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **docs(i18n):** bring the eight new `llm.txt` mirrors (km, kn, ml, my, ne, or, pa, si) up to the current provider and migration counts, so `check-docs-sync` stops rejecting every commit ([#13674](https://github.com/diegosouzapw/OmniRoute/pull/13674)) +- **refactor(ui):** flow surfaces (home topology, combo live studio, compression cockpit/waterfall/nodes, token-health badge) express state through the theme-aware `--orch-status-*` tokens instead of fixed dark-mode hex, so green/red/amber/grey stay legible in the light theme; dark mode is byte-identical. Categorical palettes (routing-strategy hues, compression-layer pills, provider brand colors) deliberately stay hex ([#12378](https://github.com/diegosouzapw/OmniRoute/issues/12378)) ([#13203](https://github.com/diegosouzapw/OmniRoute/pull/13203)) +- **chore(tests):** 51 test files that had been excluded from Vitest are running again, restoring roughly 350 assertions to the blocking suite. Each was measured individually first: of the 62 files parked behind the `// #8618 — pre-existing failure` comment, 51 pass against the current tree with no source change, so the exclusions had outlived the failures they were added for. The 11 that genuinely still fail stay excluded, but now point at a live tracker (#13204) rather than at #8618, which was closed in August while the list it tracked kept growing. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **chore(quality):** drop two ESLint suppression entries whose violations no longer exist, so `eslint --suppressions-location` stops rejecting every commit that touches the surrounding files ([#13384](https://github.com/diegosouzapw/OmniRoute/pull/13384)) +- **chore(release):** bring main's post-tag fixes into release/v3.8.51 — sync-back step 2/2 (direct commit `21c488f210`) +- **chore(deps):** patch toml and esbuild in the opencode plugin lockfiles ([#12965](https://github.com/diegosouzapw/OmniRoute/pull/12965)) +- **docs(changelog):** reconcile the v3.8.51 living section — fold 366 fragments, cover every cycle commit, credit every contributor ([#12971](https://github.com/diegosouzapw/OmniRoute/pull/12971)) +- **test(plugin):** wait for the v2 lazy-refresh tier instead of sleeping past it ([#12984](https://github.com/diegosouzapw/OmniRoute/pull/12984)) +- **chore(quality):** rebaseline chatCore.ts after the non-streaming regression fixes ([#13045](https://github.com/diegosouzapw/OmniRoute/pull/13045)) +- **docs(changelog):** add the missing bullet marker to the DeepSeek PoW fragment ([#13200](https://github.com/diegosouzapw/OmniRoute/pull/13200)) +- **chore(deps):** drain the Dependabot queue — 10 of 13 alerts ([#13213](https://github.com/diegosouzapw/OmniRoute/pull/13213)) +- **chore(ci):** make worktrees carry the hooks that enforce the identity gate ([#13236](https://github.com/diegosouzapw/OmniRoute/pull/13236)) +- **chore(deps):** pin csv-parse to ^7.0.2 via overrides (#13117) ([#13300](https://github.com/diegosouzapw/OmniRoute/pull/13300)) — thanks @KooshaPari +- **chore(deps):** bump hono from ^4.12.34 to ^4.13.7 (#13148) ([#13301](https://github.com/diegosouzapw/OmniRoute/pull/13301)) — thanks @KooshaPari +- **chore(deps):** pin joi to ^18.2.8 via overrides (#13085) ([#13302](https://github.com/diegosouzapw/OmniRoute/pull/13302)) — thanks @KooshaPari +- **docs(providers):** add Microsoft 365 Copilot (BizChat) provider guide (#12779) ([#13340](https://github.com/diegosouzapw/OmniRoute/pull/13340)) — thanks @KooshaPari +### 🙌 Contributors + +Thanks to everyone whose work landed in v3.8.51: + +| Contributor | PRs / Issues | +| --- | --- | +| [@5dive-bot](https://github.com/5dive-bot) | #11852 | +| [@abhisheksharma2411](https://github.com/abhisheksharma2411) | #11547, #11684, #11959, #12209 | +| [@adevwithpurpose](https://github.com/adevwithpurpose) | #11464 | +| [@adivekar-utexas](https://github.com/adivekar-utexas) | #12015, #12043, #12090, #12138 | +| [@AIB1TAL0S](https://github.com/AIB1TAL0S) | #12103 | +| [@alltomatos](https://github.com/alltomatos) | #11448 | +| [@alvinveroy](https://github.com/alvinveroy) | #11857, #12027, #12165 | +| [@amaleta](https://github.com/amaleta) | #11906, #11934 | +| [@amartinawi](https://github.com/amartinawi) | #11575 | +| [@AndrianBalanescu](https://github.com/AndrianBalanescu) | #11083, #11830, #11834, #11844 | +| [@anhtahaylove](https://github.com/anhtahaylove) | #13091, #13092, #13093, #13096, #13097, #13100, #13106, #13109, #13114, #13155, #13166, #13171, #13175, #13187, #13196 | +| [@aniruddhaadak80](https://github.com/aniruddhaadak80) | #11760 | +| [@app](https://github.com/app) | #12554 | +| [@arminanton](https://github.com/arminanton) | #11461, #11513 | +| [@AStupidBear](https://github.com/AStupidBear) | #11584 | +| [@atescivitci-cmd](https://github.com/atescivitci-cmd) | #12105 | +| [@b3nw](https://github.com/b3nw) | #11970, #11971, #12147 | +| [@backryun](https://github.com/backryun) | #11259, #11950, #12075, #12076, #12078, #12079, #12081, #12082, #12181, #12228, #12239, #12255, #12258, #12277, #12367, #12423, #12524, #12538 | +| [@benjaminkitt](https://github.com/benjaminkitt) | #11747 | +| [@benzntech](https://github.com/benzntech) | #11614, #11615 | +| [@Bl0ck154](https://github.com/Bl0ck154) | #11948, #11951, #11952, #11953, #11954 | +| [@brick30llc-ctrl](https://github.com/brick30llc-ctrl) | #11752, #12242, #12262 | +| [@Chewji9875](https://github.com/Chewji9875) | #11455, #11875, #12028 | +| [@cheynetom](https://github.com/cheynetom) | #12058 | +| [@davidebaraldo](https://github.com/davidebaraldo) | #12222, #12304 | +| [@davidlinfr](https://github.com/davidlinfr) | #6390 | +| [@ddarkr](https://github.com/ddarkr) | #12124 | +| [@Deftera186](https://github.com/Deftera186) | #11809 | +| [@dpozimski](https://github.com/dpozimski) | #11945 | +| [@DW-MediaLab](https://github.com/DW-MediaLab) | #12229 | +| [@echel0nn](https://github.com/echel0nn) | #11923 | +| [@f9td56dbgh-hub](https://github.com/f9td56dbgh-hub) | #11660 | +| [@fabioluissilva](https://github.com/fabioluissilva) | #11991 | +| [@foreveryh](https://github.com/foreveryh) | #12177 | +| [@geek007git](https://github.com/geek007git) | #12115, #12116, #12117, #12120, #12122 | +| [@geekyNads](https://github.com/geekyNads) | #11784, #12266 | +| [@ggiak](https://github.com/ggiak) | #11561, #12276, #12350, #12402 | +| [@gonisulaimann](https://github.com/gonisulaimann) | #12368, #12369, #12371 | +| [@Gorillaz322](https://github.com/Gorillaz322) | #12207 | +| [@groovecityJO](https://github.com/groovecityJO) | #12682 | +| [@hartmark](https://github.com/hartmark) | #11434, #11452, #11473, #11499, #11703, #11983, #11984, #11985, #11986, #11988, #11989, #11990, #11994, #12221, #12293, #12445, #12446, #12447, #12448, #12460, #12461, #12623, #12646, #12650, #12680, #12717, #12718, #12727, #12741, #12854 | +| [@hizzt](https://github.com/hizzt) | #11894 | +| [@hongnoul](https://github.com/hongnoul) | #11484 | +| [@HouMinXi](https://github.com/HouMinXi) | #11411, #11414, #11512, #11518, #11520, #11641, #11642, #11643, #11687, #11779, #11849, #11850, #11851, #11915, #11916, #11918, #11919, #11920, #12013, #12017, #12033, #12042, #12106, #12139, #12166, #12169, #12171, #12205, #12213, #12312, #12325, #12487, #12488, #12495, #12504, #12557, #12566, #12590, #12591, #12624, #12626, #12632, #12637, #12678, #12696, #12697, #12711, #12733, #12746, #12767, #12770, #12789, #12803, #12805, #12811, #12866, #12868, #12899, #12926, #12934, #12950, #12951, #12974, #13001, #13006, #13011, #13017, #13026, #13027, #13034, #13035, #13038, #13042, #13050, #13060, #13061, #13069, #13107, #13120, #13136, #13195, #13197 | +| [@Hsia97](https://github.com/Hsia97) | #11624 | +| [@jacobsparts](https://github.com/jacobsparts) | #11854, #12155, #12167 | +| [@joglomedia](https://github.com/joglomedia) | #11980 | +| [@jonlwheat2-gif](https://github.com/jonlwheat2-gif) | #11443, #11450, #11549, #11567, #11608, #11633, #11635, #11640, #11644, #11671, #11672, #11673, #11674, #11675, #11676, #11677, #11706, #11783, #12051, #12052, #12053, #12055 | +| [@kanade-hoshino](https://github.com/kanade-hoshino) | #12180 | +| [@Karan825](https://github.com/Karan825) | #11861 | +| [@KaspaPulse](https://github.com/KaspaPulse) | #11389, #11469, #11628, #11666, #11910 | +| [@keeltrace](https://github.com/keeltrace) | #12080, #12223 | +| [@killer30001000](https://github.com/killer30001000) | #12178 | +| [@KooshaPari](https://github.com/KooshaPari) | #12470, #12592, #12667, #12699, #12703, #12706, #12764, #12769, #12771, #13300, #13301, #13302, #13340, #13401, #13403, #13404, #13405, #13406, #13407, #13409, #13410, #13411, #13412, #13413, #13414, #13418, #13424, #13427, #13433, #13523, #13524, #13525, #13528, #13531, #13532, #13534, #13539, #13543, #13545, #13546, #13547, #13550, #13553 | +| [@kriptoburak](https://github.com/kriptoburak) | #11370 | +| [@KrzysiekSko](https://github.com/KrzysiekSko) | #12425, #12432, #12673 | +| [@marcelokarval](https://github.com/marcelokarval) | direct commit / report | +| [@maxmad64bis](https://github.com/maxmad64bis) | #1622, #11435, #11437, #11441, #11537, #11550, #11553, #11555, #11557, #11812, #11842, #11843, #11903, #12151, #12214, #12215, #12218, #12226, #12314, #12316, #12317, #12318, #12319, #12320, #12321, #12507, #12715, #12731, #12744, #12786, #12787, #12788, #12790, #12792, #12794, #12795, #12828, #12832, #12853, #12857, #12870, #12937, #12941, #12975, #13141, #13142, #13146, #13147 | +| [@mdigitalbh81](https://github.com/mdigitalbh81) | #12240 | +| [@Meet6338-X](https://github.com/Meet6338-X) | #11598, #11609, #12110 | +| [@morpheus9393](https://github.com/morpheus9393) | #11943 | +| [@MumuTW](https://github.com/MumuTW) | #11492, #11502, #11506, #11507, #11626, #11675, #11685, #11728, #11746, #11888, #11889, #11890, #11892 | +| [@Neuron-Mr-White](https://github.com/Neuron-Mr-White) | #11622, #11801, #12256 | +| [@NightStalker-87](https://github.com/NightStalker-87) | #12183 | +| [@NoxzRCW](https://github.com/NoxzRCW) | #11879, #11880, #11881, #11882, #11883, #11935 | +| [@ntdat812](https://github.com/ntdat812) | #11585, #12095 | +| [@ntdatt812](https://github.com/ntdatt812) | #11368, #11573, #11574, #11576, #11577, #11580, #11582, #11583, #11588, #11589, #11590, #11591, #11592, #11593, #11672, #12177, #12180, #12873, #12918, #12920, #12921, #12925, #12930, #13007, #13009, #13024, #13025, #13083, #13087, #13101, #13104, #13110 | +| [@official-burak](https://github.com/official-burak) | #11542 | +| [@opensource-elearning](https://github.com/opensource-elearning) | #12179, #12189, #12278, #12286 | +| [@oyi77](https://github.com/oyi77) | #11408, #11409, #11421, #11505, #11677, #12036, #12110 | +| [@pacocartones](https://github.com/pacocartones) | #11521, #11522, #11527, #11528, #11529, #11530, #11531, #11532, #11533, #11534, #11595, #11599, #11603, #11604, #11605, #11607, #11610, #11676, #11714, #11716, #11718, #11767, #11838, #11860, #11862, #11869, #11871, #11872, #11873, #11903, #11906, #11921, #11934, #12356, #12358, #12359, #12360, #12361, #12362, #12364, #12365, #12373, #12374, #12375, #12376, #12377, #12379, #12380, #12381, #12386, #12387, #12389, #12390, #12394, #12395, #12397, #12401, #12403, #12404, #12522, #12523, #12535, #12536, #12540, #12541, #12543, #12545, #12548, #12549, #12550, #12551, #12552, #12647, #12651, #12653, #13055 | +| [@patrykkopycinski](https://github.com/patrykkopycinski) | #11936, #11937, #12224 | +| [@PauloFH](https://github.com/PauloFH) | #11509 | +| [@PauloHSOliveira](https://github.com/PauloHSOliveira) | #12241 | +| [@ponkcore](https://github.com/ponkcore) | #12054 | +| [@Prajeeth-12](https://github.com/Prajeeth-12) | #11634, #12046 | +| [@quiterunner-commits](https://github.com/quiterunner-commits) | #12131, #12143 | +| [@rafacpti23](https://github.com/rafacpti23) | #11554, #11558, #12192, #12197, #12198, #12202, #12203, #12204 | +| [@ragnar-claude](https://github.com/ragnar-claude) | #11564 | +| [@raheemuddin786](https://github.com/raheemuddin786) | #11491, #11839, #11840, #11841, #12003, #12230, #12231, #12232, #12233, #12234 | +| [@rahilmavani](https://github.com/rahilmavani) | #11761 | +| [@Rahulsharma0810](https://github.com/Rahulsharma0810) | #11771 | +| [@RaviTharuma](https://github.com/RaviTharuma) | #11710, #11727, #11797, #11798, #11802, #11805, #11806, #11811, #12098, #12099, #12101, #12449, #12452, #12472, #12473, #12493, #12533, #12607, #12628, #12631, #12636, #12875, #12876, #12880, #12882, #12884 | +| [@rezjalibd](https://github.com/rezjalibd) | #12186 | +| [@RhianB14](https://github.com/RhianB14) | #11692 | +| [@rifqiawl](https://github.com/rifqiawl) | #11517, #11519, #11969 | +| [@rqzbeh](https://github.com/rqzbeh) | #11390 | +| [@Sabeekhann](https://github.com/Sabeekhann) | #11699, #11705 | +| [@santosraju99-hub](https://github.com/santosraju99-hub) | #11755, #11770 | +| [@Seramicx](https://github.com/Seramicx) | #11568 | +| [@Siva010](https://github.com/Siva010) | #12191 | +| [@solstxce](https://github.com/solstxce) | #11597 | +| [@soroush5](https://github.com/soroush5) | #12691 | +| [@tenshiak](https://github.com/tenshiak) | #12279 | +| [@TheDemonTuan](https://github.com/TheDemonTuan) | #11468, #11470, #11471, #11482, #11548, #11758, #11775, #11814 | +| [@thomasmaerz](https://github.com/thomasmaerz) | #12834 | +| [@tuandinh0801](https://github.com/tuandinh0801) | #11454 | +| [@turbolego](https://github.com/turbolego) | #11621, #11762, #11772, #11774, #11781, #12216 | +| [@Tushar49](https://github.com/Tushar49) | #11392 | +| [@ujjawalkaushik1110](https://github.com/ujjawalkaushik1110) | #11863 | +| [@vermasomesh835](https://github.com/vermasomesh835) | #11794 | +| [@vsd2807](https://github.com/vsd2807) | #11565, #11619 | +| [@wahidsadik371-coder](https://github.com/wahidsadik371-coder) | #12126 | +| [@watchingdogs](https://github.com/watchingdogs) | #12031 | +| [@wildcard](https://github.com/wildcard) | #11620 | +| [@xiaoyaner0201](https://github.com/xiaoyaner0201) | #11460, #11662, #12083 | +| [@Xxx91n](https://github.com/Xxx91n) | #11690 | +| [@yourspraveen](https://github.com/yourspraveen) | #11146 | +| [@yxyxy](https://github.com/yxyxy) | #11671 | +| [@ZaimMarzuki](https://github.com/ZaimMarzuki) | #11960 | +| [@Zartharas](https://github.com/Zartharas) | #11340 | +| [@zero-executioner](https://github.com/zero-executioner) | #11631 | +| [@diegosouzapw](https://github.com/diegosouzapw) | maintainer | + +--- ## [3.8.50] — 2026-08-25 _Living section — regenerated 2026-08-12 from all cycle commits (cycle open `ed2db6cb19` → tip). Bullets carry the merged PR and its author; direct pushes listed separately._ diff --git a/docs/i18n/da/CHANGELOG.md b/docs/i18n/da/CHANGELOG.md index afec960bb1..d737c8bf2d 100644 --- a/docs/i18n/da/CHANGELOG.md +++ b/docs/i18n/da/CHANGELOG.md @@ -8,16 +8,1119 @@ ## [3.8.51] — TBD -_Living section — cycle opened at the v3.8.50 freeze (parallel-cycle model). Bullets are aggregated from `changelog.d/` fragments at each `/generate-release` phase._ +_Living section — reconciled 2026-09-15 from all cycle commits (`091589089c` → `c0f92ec98a`, 916 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 | **123** | +| 📝 Commits in the cycle | **916** | +| 🔀 Pull requests referenced | **892** | +| 📋 Changelog entries | **919** | +| 🙌 Contributors credited in entries | **121** | +| 🤖 Automated dependency commits | 18 | + +**Entries by type** + +| Type | Count | +| --- | ---: | +| 🐛 Fixes | 592 | +| ✨ Features | 131 | +| 🧹 Chore | 93 | +| 📚 Docs | 42 | +| 🧪 Tests | 32 | +| ♻️ Refactor | 8 | +| 🏗️ Build | 7 | +| ⚡ Performance | 7 | +| ⚙️ CI | 2 | +| ⏪ Reverts | 2 | +| 📦 Dependencies | 2 | +| 🔒 Security | 1 | + +### 🏆 Top 25 contributors this cycle + +_By commits in `091589089c..c0f92ec98a`, author identities consolidated via `.mailmap` and the merged PR's GitHub login. Bots excluded._ + +| # | Contributor | Commits | +| ---: | --- | ---: | +| 🥇 | diegosouzapw | 323 | +| 🥈 | Bob.Hou (@HouMinXi) | 79 | +| 🥉 | Paco Cartones (@pacocartones) | 77 | +| 4 | Dizzle (@maxmad64bis) | 49 | +| 5 | Koosha Paridehpour (@KooshaPari) | 43 | +| 6 | Nguyen Thanh Dat (@ntdatt812) | 31 | +| 7 | Ravi Tharuma (@RaviTharuma) | 26 | +| 8 | Markus Hartung (@hartmark) | 22 | +| 9 | Webman (@jonlwheat2-gif) | 22 | +| 10 | backryun | 17 | +| 11 | anhtahaylove | 15 | +| 12 | Rafa Martins (@rafacpti23) | 10 | +| 13 | Syed Raheemuddin (@raheemuddin786) | 10 | +| 14 | Nguyễn Viết Tuấn (@TheDemonTuan) | 8 | +| 15 | MumuTW | 7 | +| 16 | Paijo (@oyi77) | 7 | +| 17 | Tobias Andersen (@turbolego) | 6 | +| 18 | Bl0ck (@Bl0ck154) | 5 | +| 19 | SHANMUGAPRIYAN (@geek007git) | 5 | +| 20 | KaspaPulse | 5 | +| 21 | Abhishek Sharma (@abhisheksharma2411) | 4 | +| 22 | Abhishek Divekar (@adivekar-utexas) | 4 | +| 23 | Andrew B. (@AndrianBalanescu) | 4 | +| 24 | NoxzRCW | 4 | +| 25 | opensource-elearning | 4 | ### ✨ New Features +- **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). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- Added Google AI Studio Gemini batch text-to-speech support through `POST /v1/audio/speech`. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- 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. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(routing):** subscription-first auto groupings — `auto/subscription` routes only through plan-included connections with a documented hard-stop overage and fails closed on exhaustion, while `auto/thrifty` orders the pool `subscription → keyless → free → cheap → premium` and steps up one rung at a time as each is exhausted. Billing class comes from a curated per-connection catalog (uncurated is treated as metered, never plan-included), both reuse STRICT_ZERO_COST's per-connection verification, and a quota reading whose `resetAt` has passed is now refreshed regardless of TTL so routing returns to plan capacity as soon as it resets ([#11146](https://github.com/diegosouzapw/OmniRoute/pull/11146)) — thanks @yourspraveen +- **feat(providers):** publish a management-authenticated versioned web-session credential contract from OmniRoute's canonical browser credential metadata ([#11340](https://github.com/diegosouzapw/OmniRoute/pull/11340)) — thanks @Zartharas +- **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)) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add Xquik X search with typed results, credential validation, REST routing, and MCP selection ([#11370](https://github.com/diegosouzapw/OmniRoute/pull/11370)) — thanks @kriptoburak +- **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)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(dashboard):** surface durable exclusive managed leases in the existing Sessions view, keeping leased clients visible across idle gaps while marking connections with in-flight work as active ([#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389)) — thanks @KaspaPulse +- **build(bun):** allow Turbopack bundler flag on Bun 1.4+ with configurable Webpack fallback ([#11471](https://github.com/diegosouzapw/OmniRoute/pull/11471)) — thanks @TheDemonTuan +- feat(api): add an opt-in `modelVisibilityAllowlist`/`modelVisibilityDenylist` settings pair to curate exactly which models `/v1/models` advertises, mirrored into every `auto/*` combo candidate pool so a denied model cannot be routed to via combo selection either (#11481) ([#11997](https://github.com/diegosouzapw/OmniRoute/pull/11997)) +- **feat(rankings):** the Free Provider Rankings page shows what each provider actually served over the last 24 h. It ranked by ELO alone, which left a provider that answers every call with an error in first place; the usage data was already served by the API but never requested. A provider with too small a sample shows a dash, not a number ([#11546](https://github.com/diegosouzapw/OmniRoute/pull/11546)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **feat(guardrails):** enforce a bounded, deterministic contract for Video Bridge transcripts — 256 cues, 4096 input code units and 4 KiB UTF-8 per cue, 64 KiB total text, malformed-Unicode rejection, focus-window scoping, cross-source reconciliation with contributing-source metadata, and a structural provenance trust boundary so caller JSON can never self-assert `embedded`/`audio-bridge` provenance ([#11652](https://github.com/diegosouzapw/OmniRoute/issues/11652)) ([#12009](https://github.com/diegosouzapw/OmniRoute/pull/12009)) +- **feat(video):** orchestrate optional Video Bridge audio extraction and Audio Bridge STT behind a dual opt-in (operator setting AND per-request signal) — a new loopback-only broker `mode=audio` operation shares the frame path's exact process queue, deadline, AbortSignal, and byte budgets to extract a bounded mono 16 kHz PCM WAV from the same already-downloaded video, then reuses the existing Audio Bridge transcription boundary; provider segment timing is preserved when available and marked coarse otherwise, and every failure degrades to a visual-only-safe partial instead of throwing (#11654). ([#12012](https://github.com/diegosouzapw/OmniRoute/pull/12012)) +- Add a tenant-bound Video Bridge drill-down lifecycle on top of the existing secure cache substrate: opaque hashed handles (never raw session/video identifiers), preview/standard/detail multiresolution variants resampled on read, response pagination capped at 8 frames and 32 MiB, and a new authenticated `/api/v1/video-bridge/drilldown` consumer route that stays disabled for remote access by default and denies cross-key access with the same response as a nonexistent handle (no existence oracle). ([#12006](https://github.com/diegosouzapw/OmniRoute/pull/12006)) +- **test(video):** Add the Video Bridge FU-07/FU-09 promotion-evidence harness (#11656) — a frozen Zod manifest schema covering the 8 required scenario kinds (static scenes, rapid cuts, late facts, fades, blur, small text, close events, visual prompt injection) with a minimum of 3 repetitions per case, deterministic declarative fixture recipes (`videoBridgePromotionFixtures.ts`), a pure medians/p95 metrics aggregator, a pure FU-07/FU-09 promotion-verdict evaluator applying the ticket's exact thresholds (missing token usage always holds), a digest-only persistence layer that never retains raw media or raw model responses, and a versioned per-model promotion allowlist shipped empty with every model defaulting to `hold`. The FU-07/FU-09 promotion verdicts themselves remain HOLD — they require a real evidence run against real models on VPS 192.168.0.15. ([#12008](https://github.com/diegosouzapw/OmniRoute/pull/12008)) +- **feat(video bridge):** "embedded" transcript provenance can now be legitimately earned instead of merely asserted — a bounded, allowlisted (`mov_text`/`subrip`/`webvtt`) subtitle probe runs through the loopback-only Video Bridge broker (at most 2 streams, 10s subdeadline bounded by the request deadline, 256 KiB output, 4096-code-unit lines), normalized through a bounded, ReDoS-safe WebVTT parser and Zod-validated end to end. The adapter always resolves to an explicit `success`/`absent`/`transient_failure` outcome — a subtitle failure never breaks the visual description path, and only a fingerprint-verified broker response (never a caller-declared label) can produce embedded cues (#11659). ([#12011](https://github.com/diegosouzapw/OmniRoute/pull/12011)) +- Default new Antigravity-family connections (agy CLI imports and Antigravity OAuth connects) to model auto-sync, so live model discovery lands in the synced catalog and `/v1/models` picks up freshly released upstream models (e.g. Gemini 3.7 Flash tiers) without code changes. Existing connections keep their current setting; the per-connection dashboard toggle remains the opt-out. (#11685 — thanks @MumuTW) ([#11887](https://github.com/diegosouzapw/OmniRoute/pull/11887)) +- **feat(ui):** enable React Compiler (`reactCompiler: true` + `babel-plugin-react-compiler`) for automatic memoization at build time ([#11783](https://github.com/diegosouzapw/OmniRoute/pull/11783)) — thanks @jonlwheat2-gif +- **feat(providers):** add SeekAi (`seekai.cc`) as an OpenAI-compatible New-API gateway — catalog id `seekai` (alias `ska`), `https://seekai.cc/v1`, live `/v1/models` via `passthroughModels`, aggregator-list membership so New-API balance detection can opt in. No referral/aff codes. ([#11786](https://github.com/diegosouzapw/OmniRoute/issues/11786)) ([#12557](https://github.com/diegosouzapw/OmniRoute/pull/12557)) — thanks @HouMinXi +- **feat(zai):** add GLM-5.3-Flash Coding Plan support (1M context, 128K output, vision, `low|high|max` reasoning) and route `zai` GLM-5.3-family API-key traffic through the OpenAI-compatible Coding Plan endpoint with native thinking defaults ([#11801](https://github.com/diegosouzapw/OmniRoute/pull/11801)) — thanks @Neuron-Mr-White +- **feat(combo):** choose how combo models are ordered — manual, provider, score, or name — via a sort control in the dashboard builder, persisted in `config.modelSort` and re-applied on load and after add ([#11812](https://github.com/diegosouzapw/OmniRoute/pull/11812)) — thanks @maxmad64bis +- **feat(free):** custom models can be marked free-tier via `customModels[].isFree`; `isFreeModel()` is the first door and `hidePaidModels` respects it even for providers outside the free budget ([#11843](https://github.com/diegosouzapw/OmniRoute/pull/11843)) — thanks @maxmad64bis +- **feat(nodejs):** add `5dive` as a `configure` target — `omniroute configure 5dive` / `omniroute setup-5dive` write a 5dive auth profile that points an agent fleet's `claude` seats at OmniRoute, with the root-only write, the loopback-vs-`https` endpoint rule and the per-seat model pin handled explicitly ([#11852](https://github.com/diegosouzapw/OmniRoute/pull/11852)) — thanks @5dive-bot +- **feat(sse):** treat `max` as a first-class reasoning-effort tier and clamp per model family (GLM 5.1+/DeepSeek V4+/Kimi K3+ keep native `max`; o1/MiniMax/Grok/Muse Spark clamp to their upstream ceiling) ([#11875](https://github.com/diegosouzapw/OmniRoute/pull/11875)) — thanks @Chewji9875 +- **feat(providers):** the provider plugin manifest now advertises a `usage-fetch` capability for the 40 providers that have a wired usage/quota fetcher, so external dashboards can read it from `GET /api/v1/provider-plugin-manifest` instead of parsing `open-sse/services/usage.ts` after every release. Discovery only — no new fetcher, no quota change, and the Dashboard quota widget stays gated by `USAGE_SUPPORTED_PROVIDERS`. `USAGE_FETCHER_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/fetcherProviders.ts`) and is re-exported from `services/usage.ts`, keeping the manifest module a light leaf instead of pulling the ~490-module usage dispatcher into the manifest route. ([#11903](https://github.com/diegosouzapw/OmniRoute/pull/11903)) — thanks @maxmad64bis / @pacocartones +- **feat(plugins):** `OMNIROUTE_PLUGINS_DIR` sets the directory the runtime plugin scanner reads — and the root the plugin manager installs into — overriding the `HOME`-derived default, so a Docker/K8s deployment can point straight at its bind-mounted plugin tree instead of moving `HOME` just to relocate the scan path. An image that exports no home no longer scans `/tmp/.omniroute/plugins` in silence: the resolved directory is logged once at startup as `scanner.dir_resolved`, naming the input that won. Unset, behaviour is unchanged. Distinct from the CLI-only `OMNIROUTE_PLUGIN_PATH`, which finds `omniroute-cmd-*` command packages and never reached this scanner ([#11906](https://github.com/diegosouzapw/OmniRoute/pull/11906)) — thanks @amaleta / @pacocartones +- **feat(leases):** add an explicit owner-authenticated status action that returns only the active lease's privacy-safe configured connection and provider labels, with generation fencing and no credential or internal-id disclosure ([#11910](https://github.com/diegosouzapw/OmniRoute/pull/11910)) — thanks @KaspaPulse +- **feat(providers):** import-from-file modal shows per-row API errors and ships a downloadable CSV template ([#12071](https://github.com/diegosouzapw/OmniRoute/issues/12071)) ([#12504](https://github.com/diegosouzapw/OmniRoute/pull/12504)) — thanks @HouMinXi +- **feat(providers):** dashboard search matches connection name and `baseUrl` so imported OpenAI-compat nodes surface on the provider card ([#12108](https://github.com/diegosouzapw/OmniRoute/issues/12108)) ([#12495](https://github.com/diegosouzapw/OmniRoute/pull/12495)) — thanks @HouMinXi +- **feat(routing):** add a `score` Auto router strategy that selects the highest configured weighted score and reuses `explorationRate`. ([#12155](https://github.com/diegosouzapw/OmniRoute/pull/12155)) — thanks @jacobsparts +- Add a runtime feature flag to disable universal context handoffs globally without changing the default behavior. ([#12167](https://github.com/diegosouzapw/OmniRoute/pull/12167)) — thanks @jacobsparts +- **feat(providers):** the provider plugin manifest now also advertises a `usage-supported` capability for the 46 providers whose usage API is accepted by the server and Dashboard routes, so integrators can distinguish "the server will serve quota for this provider" from "a fetcher is wired" without reading TypeScript. Discovery only — no fetcher or quota change. `usage-fetch` resolves on id or alias (the usage dispatcher accepts both); `usage-supported` resolves on id alone, matching the runtime guard `USAGE_SUPPORTED_PROVIDERS.includes(providerId)`. `USAGE_SUPPORTED_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/supportedProviders.ts`) and is re-exported from `providers.ts`, mirroring the `fetcherProviders` leaf from #11903 and keeping the manifest a light module. ([#12214](https://github.com/diegosouzapw/OmniRoute/pull/12214)) — thanks @maxmad64bis +- **feat(rankings):** order Free Provider Rankings by what each provider actually served — `GET /api/free-provider-rankings?sortBy=reliability` and a "Most reliable first" toggle on the page. Providers with too few calls to state a success rate keep their score order below the measured ones; the default order is unchanged ([#12218](https://github.com/diegosouzapw/OmniRoute/pull/12218)). — thanks @maxmad64bis +- **perf(sse):** defer `cloneLogPayload()` in the structured SSE collector until after the `maxEvents`/`maxBytes` cap check, eliminating ~9,800 wasted `structuredClone` calls per streaming response (65–71% faster `push()`). Reducer snapshot isolation restored for OpenAI and Responses summaries ([#12241](https://github.com/diegosouzapw/OmniRoute/pull/12241)) — thanks @PauloHSOliveira ([#12243](https://github.com/diegosouzapw/OmniRoute/pull/12243)) +- **feat(usage):** Devin CLI agentic quota (Codeium seat-management GetUserStatus) and OpenRouter key limits plus account credits now surface in Provider Limits ([#12256](https://github.com/diegosouzapw/OmniRoute/pull/12256) — thanks @Neuron-Mr-White) +- **feat(settings):** persist `headroomUrl` through Settings so status/start use the operator URL instead of only `HEADROOM_URL` ([#12306](https://github.com/diegosouzapw/OmniRoute/issues/12306)) ([#12487](https://github.com/diegosouzapw/OmniRoute/pull/12487)) — thanks @HouMinXi +- **feat(auto-combo):** Auto-Combo scoring can now weigh how often a provider/model has actually succeeded. The engine already carried that number on every candidate — 24 hours of usage history behind a ten-sample floor, real-time metrics otherwise — and the scoring function never read it, while the weight table described `stability` as if it did. `reliability` (`1 - failureRate`, with the same field precedence and the same rate-bounding the speed ranking already uses, so a corrupt reading means "nothing observed" rather than "fails every call") is now a declared factor shipping at weight `0`, so routing is unchanged until an operator gives it one, and the `stability` description now matches what that factor computes ([#12317](https://github.com/diegosouzapw/OmniRoute/pull/12317)) — thanks @maxmad64bis +- **feat(routing):** With `freeAccessPolicy: "strict"`, the read-only candidate listing (`GET /v1/auto-combo/{channel}/candidates`) no longer hides the candidates the zero-cost guard excludes — the same read-only transparency the resilience filter already honours (#9133). Each candidate now carries `freeAccessExclusion` saying why it would be kept out, and it tells an exhausted allowance apart from a quota reading that never arrived or went stale, which used to look identical from the outside. Routing is unchanged: the listing reports, it never enforces. The separate `excludeTosAvoid` guard still drops its candidates without a reason; that gap is now documented rather than closed ([#12319](https://github.com/diegosouzapw/OmniRoute/pull/12319)) — thanks @maxmad64bis +- **feat(radar):** The Radar catalog table now shows two facts it was already receiving from the feed and dropping on the floor: the per-model rate limits (requests and tokens, per minute and per day) in a new column, and a badge when a provider's terms state it may train on the prompts you send. A limit of zero renders as zero rather than "rate-only" — for a ceiling those are opposite facts — and a model with no training statement gets no badge, because an absent statement is not a guarantee ([#12320](https://github.com/diegosouzapw/OmniRoute/pull/12320)) — thanks @maxmad64bis +- **feat(radar):** explain Community, single-use, contributor, supporter, recovery, abuse, offers, and privacy rules before either Radar activation action, and remove the superseded fixed-PR grant promise from every UI locale ([#12342](https://github.com/diegosouzapw/OmniRoute/pull/12342)) +- **feat(gamification):** the dashboard Profile page now shows the real daily streak — `/api/gamification/level` returns `streak: { current, longest }` (per key with `apiKeyId`, operator-wide maximum otherwise) and the streak card reads it instead of a hard-coded 0 (#2403) ([#12377](https://github.com/diegosouzapw/OmniRoute/pull/12377)) — thanks @pacocartones +- **feat(gamification):** the dashboard leaderboard now shows each API key's display name under the Name column instead of a truncated key id; `GET /api/gamification/leaderboard` attaches `name` per entry (name only — no key material), while the shared ranking helper and the federation leaderboard stay id-only — thanks @pacocartones ([#12385](https://github.com/diegosouzapw/OmniRoute/pull/12385)) +- **feat(gamification):** enforce the documented 1000 XP/min per-API-key anti-cheat rate limit on the XP award path; over-limit awards are logged and skipped instead of persisted, and the sliding window now matches the timestamp format stored in `xp_audit_log` ([#2403](https://github.com/diegosouzapw/OmniRoute/issues/2403)) ([#12390](https://github.com/diegosouzapw/OmniRoute/pull/12390)) — thanks @pacocartones +- **feat(admin):** localize the gamification anomalies page — the loading state, the Status column and the Suspicious badge now come from the `common` catalog (new `common.suspicious` key propagated to every locale) — add it to the Gamification sidebar group as `gamification-admin` (`/dashboard/gamification/admin`), and expose the loading and empty states as polite `role="status"` live regions (#12401 — thanks @pacocartones) +- **feat(providers):** skip GitHub combo members missing from the live synced catalog, and drop Copilot models that are policy-disabled or hidden from the model picker ([#12473](https://github.com/diegosouzapw/OmniRoute/pull/12473)) — thanks @RaviTharuma +- **feat(i18n):** locale-expansion tooling — `npm run i18n:add-locale` adds a language to config, dashboard, docs mirrors, CLI, README/indexes and site in one command; browser, CLI and cookie detection resolve `uk`, `fil`/`tl`, `zh-Hant` (and the retired legacy `in` → `id`) via config aliases; `config/i18n.json` now ships in the npm package so the published CLI can read it; new real-translation ratio gate (`npm run i18n:check-ratio`, advisory) with a per-locale ratchet baseline; `run-translation --adopt` restores `.i18n-state.json`; `sync-language-bars` generates 🌐 bars from config; `validate_translation.py` loads its allowlist again. Retires the duplicate `in` locale (Indonesian mislabelled as Hindi) — 42 honest locales; saved `NEXT_LOCALE=in` / `OMNIROUTE_LANG=in` keep working. (#12496) +- **feat(api):** Emit gateway-measured `tokens_per_second` (TTFT excluded) on streaming usage and `X-OmniRoute-Tokens-Per-Second` when first-token latency is known ([#12616](https://github.com/diegosouzapw/OmniRoute/issues/12616)) ([#12631](https://github.com/diegosouzapw/OmniRoute/pull/12631)) — thanks @RaviTharuma +- **feat(opencode-plugin): map gateway cost/usage/tok/s onto OpenCode inference payloads** — the official plugin copies `X-OmniRoute-Response-Cost`, token counts, `X-OmniRoute-Tokens-Per-Second` / `usage.tokens_per_second`, TTFT, and the winning `X-OmniRoute-Model` onto the JSON/SSE body OpenCode already consumes. Missing tok/s is left unset (never `tokens / latency`). (#12636) — thanks @RaviTharuma +- **feat(combo):** add combo strategy `quota-weighted`: skip exhausted accounts, then weighted-draw by leftover / in-flight load; existing conversations stay pinned until the account is empty ([#12789](https://github.com/diegosouzapw/OmniRoute/pull/12789)) — thanks @HouMinXi +- **feat(models):** Account live listings become the chat catalog source for Claude, Codex, Copilot, and AGY; public metadata only fills prices on IDs those accounts already list. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **fix(models):** Union `agy` and `antigravity` live catalogs so `agy/gemini-3.8-flash-high` is not rejected after the prefix folds to `antigravity`. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **feat(opencode):** Add `@omniroute/opencode-plugin-v2`, an opencode v2 plugin that gives a v2 install a route to an OmniRoute gateway: it publishes models, combos and auto-combos into the host catalog, refreshes them lazily behind a 300s TTL, and keeps serving the last known catalog from an on-disk snapshot when the gateway is unreachable. The v1 plugin (`@omniroute/opencode-plugin`) is untouched and keeps working as before. The plugin adapts to the host instead of assuming one shape: it reads the shape the host seeds into the catalog draft and publishes the `api` block, the pre-`api` top-level fields, or both. The gateway key comes from the host credential store when one is connected, so no secret needs to sit in a config file; a source the gateway refuses is named in a warning instead of silently degrading the catalog to raw model ids; and tool schemas bound for a Gemini model are stripped of the JSON-Schema keywords Gemini rejects, so tool calling survives that route ([#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870)) — thanks @maxmad64bis +- feat(services): show sanitized CLIProxyAPI account health from its authenticated management API without exposing credentials, file paths, or raw account metadata (#6342) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add AnySearch free web search + URL extract (webFetch) with typed results, credential validation, REST routing, and MCP selection - fallback-only ([#11690](https://github.com/diegosouzapw/OmniRoute/pull/11690)) — thanks @Xxx91n +- **feat(dashboard):** display clamped `[0, 100]%` cached input token ratio in request logs table ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) — thanks @b3nw +- **feat(catalog):** add `OMNIROUTE_DISABLE_THINKING_LEVEL_VARIANTS` feature flag to optionally filter out thinking level variants from model catalog ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) — thanks @b3nw +- **feat(dashboard):** continuously export call logs to external analytics stores. A pluggable destination registry ships the full Logs-tab record set on an hourly `JobRegistry` cron, with a persisted per-destination cursor, batched inserts, a config UI rendered from each destination's own field descriptors, and a REST layer (`/api/log-export/*`) for CRUD, a connection test, and an on-demand run. A destination can opt into `includeBodies` to also ship the request and response payloads shown in the Logs detail pane, including the client and provider views of each call; this is off by default, and payloads inherit the dashboard's PII sanitisation, secret redaction and `noLog` handling. Google BigQuery is the first destination, using a service-account key stored encrypted at rest and streaming inserts keyed by call-log id, into a table that is day-partitioned on `timestamp` and clustered on `api_key_name`, `provider`, `model` and `status`. ([#11945](https://github.com/diegosouzapw/OmniRoute/pull/11945)) — thanks @dpozimski +- **feat(providers):** add MaxAI as a signed, OpenAI-compatible provider serving its 13 paid chat models (GPT-5.6 / Luna / Thinking, Claude 5 Sonnet, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite, Grok 4.1-fast / 4.5, DeepSeek V3.2 / R1, Llama 3.3 70B) through OmniRoute's `/v1` endpoint, with per-request HMAC-SHA1→SM3→AES request signing, live model + context-window discovery from `/models/get_config`, and prompted tool-calling translated to OpenAI `tool_calls` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI vision input — image_url content parts are forwarded inline in `message_content` to the 6 vision-capable models (GPT-5.6 / Luna / Thinking, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite) ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI image generation — 6 image models (gpt-image-1, dall-e-3, flux-1-schnell/dev/pro, sd3-medium) exposed through `POST /v1/images/generations` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI document RAG — inline base64 file/document attachments are uploaded to MaxAI (content-addressed `doc_id`) and attached to the chat via `doc_list` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** browserless MaxAI onboarding — email device-pair login (`/api/providers/[id]/login`) and signed access-token refresh, so a connection can be created and kept fresh without a real browser or Google OAuth ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** per-provider TLS impersonation profile (MaxAI presents a Windows Firefox-150 client fingerprint) so its bot-sensitive endpoints accept OmniRoute traffic ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** add **Nimble** as a web-search and web-fetch provider (`nimble-search`) — `/v1/search` routes to Nimble's search API at `lite` depth (locale, freshness and include/exclude domain filters mapped onto the shared request contract), and `/v1/web/fetch` routes to Nimble Extract, which covers all four fetch formats (`markdown`, `html`, `links`, `screenshot`) from a single call. One API key serves both surfaces. ([#11620](https://github.com/diegosouzapw/OmniRoute/pull/11620)) — thanks @wildcard +- The npm publish is automatic again, through npm Trusted Publishing (OIDC): the hosted `stage-npm` job publishes with a short-lived credential minted from GitHub's id-token — no `NPM_TOKEN`, no 2FA prompt, provenance attached. `publish_mode=staged` (owner approves with 2FA) and `direct` (token) remain available on `workflow_dispatch`. ([#11931](https://github.com/diegosouzapw/OmniRoute/pull/11931)) +- **feat(providers):** Add **Opper** as an API-key gateway provider — EU-hosted AI gateway with 700+ models from 30+ providers behind one OpenAI-compatible API and one key (`OPPER_API_KEY`); model ids use `provider/model` format (e.g. `anthropic/claude-sonnet-4-6`, `openai/gpt-5`); live model catalog at `https://api.opper.ai/v3/compat/models`; entry mirrors `requesty` (same shape, `passthroughModels: true`, no static seed) ([#11629](https://github.com/diegosouzapw/OmniRoute/pull/11629)) +- **feat(dashboard):** the `/dashboard/orchestration` snapshot hook now subscribes to the `agents` WebSocket channel (`agent.task.updated`) instead of `requests` as its refetch trigger, and relaxes its background poll from 5s to 30s while that WS connection is up — falling back to the tighter 5s cadence, reprogrammed live on any connect/disconnect transition, whenever the socket is down. ([#12409](https://github.com/diegosouzapw/OmniRoute/pull/12409)) +- **feat(dashboard):** Orchestration canvas quick wins — search box plus state/source/provider filter chips with a one-click clear, and per-source collapse/expand, all reflected in the URL so a filtered/collapsed view is shareable and survives a refresh; the detail drawer gained a "copy trace JSON" action and hardened error/empty-state and accessibility handling; the Agents-tab edges now animate traveling particles along active (running) connections; and the canvas node/edge status colors moved off fixed hex values onto theme-aware `--orch-status-*` CSS custom properties, so they adapt correctly to light/dark mode. ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- **chore(dashboard):** Orchestration UI hardening pass and the missing component/model test coverage it called for — `OrchestratorNode`/`ActivityNode`/`OverflowNode` rendering, the `?node=`/overflow-click page routing, the Agents-tab orchestrator-click no-op and `showCompleted` toggle, and the overview kanban's done-column sort order (#12270, #12271). ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- New `/dashboard/orchestration` page: live unified view of everything running — Cloud Agent, A2A and Conductor as a real-time graph (Agents tab), the combo cascade (Routing tab, reusing the Combo Live Studio) and a state kanban (Overview tab), with a detail drawer (trace, cost, approve/cancel). Read-only over existing APIs — no new backend. Canvas concept credit: PR #11815 design ([#12261](https://github.com/diegosouzapw/OmniRoute/pull/12261)) +- **feat(dashboard):** new "History" tab on `/dashboard/orchestration` — an Airflow-style grid of finished runs over a 24h/7d/30d preset window, one row per (source, identity), clicking a cell opens the existing detail drawer. It is backed by real persistence: A2A task lifecycle transitions are now written to the `a2a_tasks` table (purged after 30 days, configurable via `OMNIROUTE_A2A_HISTORY_RETENTION_DAYS`) and served by the new `GET /api/a2a/tasks/history` listing endpoint, with the task-detail route falling back to persisted history once a run leaves the in-memory snapshot. Conductor runs stay remote and are not persisted locally — the tab says so instead of silently omitting them. ([#12479](https://github.com/diegosouzapw/OmniRoute/pull/12479)) +- **feat(dashboard):** the orchestration detail drawer gained a "Repeat" action for Cloud Agent, A2A and Conductor tasks — a two-click confirm (click once to arm, click again within the confirm window to fire) re-submits the original prompt/input as a new run. The button is disabled with an explanatory tooltip whenever the original input can't be recovered from the loaded task detail (e.g. it never carried a prompt, or the detail failed to load). Two limitations of the A2A variant, by design: it targets the `/a2a` JSON-RPC endpoint, which authenticates with an API key only (`REQUIRE_API_KEY=true` or a configured `OMNIROUTE_API_KEY` makes a dashboard-session repeat answer `HTTP 400` — surfaced verbatim in the drawer's error line, never as a success), and the `message/send` call is SYNCHRONOUS: the POST blocks for the whole skill run, so the success confirmation only appears once the repeated task finishes. A dashboard-authenticated A2A creation path is deliberately left to a follow-up — widening the endpoint's auth posture is an operator decision, not a side effect of this feature. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(a2a):** A2A task execution now records which memories were consulted for the task's last user message as `metadata.memoryHits` (id/key/type/content-snippet) plus a `memory_hits` history event, purely for observability — the retrieved memory is never injected into a skill's prompt or behavior. Gated by the `OMNIROUTE_A2A_MEMORY_HITS` kill-switch (default enabled; set to `0` to skip the recall lookup entirely). The drawer's new "Memory used" section lists these hits for a2a tasks and is omitted whenever there are none. Known limitation: recall only resolves under the keyless posture — a keyed caller's task owner is a SHA-256 prefix of the API key, while memory rows are keyed by the database api-key id, and no hash→id lookup exists today, so the hit list stays empty for keyed callers. The recorded hits are also kept out of the task's own `input` (and therefore out of the persisted input and of the "Repeat" request body), so repeating a task never re-sends the previous run's memory snippets. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(providers):** add a Perplexity Agent API provider (`perplexity-agent` / `pplx-agent`) for Perplexity `/v1/responses`, including the documented Anthropic, OpenAI, Google, xAI, DeepSeek, Z.AI, Moonshot/Kimi, NVIDIA, and Perplexity model IDs plus Anthropic-model `max_output_tokens` compatibility. ([#12103](https://github.com/diegosouzapw/OmniRoute/pull/12103)) — thanks @AIB1TAL0S +- **feat(providers):** add RPD (Requests Per Day) limit to provider rate limit overrides across UI, schemas, DB, and i18n ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) — thanks @b3nw +- **feat(providers): add UC Direct (uncensored.com Developer API), the metered OpenAI-compatible surface.** A standard OpenAI-compatible passthrough (default executor) for uncensored.com's official REST API at `https://api.uncensored.com/api/v1`: `X-api-key` auth (never-expiring `uai_sk_live_` key), `POST /chat/completions` with streaming SSE and native tool-calling, and the full live metered catalog (82 models across 15 providers, discovered from the public `GET /v1/models`). Registered as provider `uc-direct` (alias `ucd`). Complements the un-metered `uc` persona provider — same models, metered credits and a plain API key instead of a subscription session. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(providers): add UC (uncensored.com), the un-metered subscription "persona" chat as an OpenAI-compatible provider.** A WebSocket web-app port: a durable Clerk credential mints a short-lived session token per connect (browserless — no API key), driving UC's persona socket. Ships the browserless email-code login (request → verify → harvest), the 19 verified persona models (Claude Opus, Gemini, Grok, GLM, Kimi, DeepSeek, MiniMax, incl. the uncensored variants), prompted `` tool-calling with a per-model code-style dialect + auto-cure retry for guardrailed models, live ``/reasoning split, streaming + non-streaming OpenAI responses, and full quota/auth error surfacing (paywall / message-limit / rate-limit → 429, invalid session → 401 re-login). Full multimodal parity via the persona blob-upload layer: **vision** (image input, 15 vision-capable models), **document RAG** (PDF/doc upload, server-side extraction), **image generation** (22 models), **video generation** (14 models, async signed-url → poll), and **TTS** (streaming MP3). Registered as provider `uc` (alias `ucn`). The metered OpenAI-compatible Developer API is a separate `uc-direct` provider. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(logs):** add copy-all button to request detail modal ([#11083](https://github.com/diegosouzapw/OmniRoute/pull/11083)) — thanks @AndrianBalanescu +- **feat(check):** make serviceKinds required and add the reverse-walk provider consistency gate ([#11392](https://github.com/diegosouzapw/OmniRoute/pull/11392)) — thanks @Tushar49 +- **feat(providers):** only advertise verified-working connections by default ([#11448](https://github.com/diegosouzapw/OmniRoute/pull/11448)) — thanks @alltomatos +- **feat(resilience):** add hierarchical concurrency admission ([#11493](https://github.com/diegosouzapw/OmniRoute/pull/11493)) +- **feat(providers):** derive + surface expiry for JWT-bearing web cookies (#11497) ([#11505](https://github.com/diegosouzapw/OmniRoute/pull/11505)) — thanks @oyi77 +- **feat(tunnels):** support named cloudflared tunnels via CLOUDFLARED_CONFIG ([#11561](https://github.com/diegosouzapw/OmniRoute/pull/11561)) — thanks @ggiak +- **feat(compression):** make proactive context-compression threshold a live setting ([#11564](https://github.com/diegosouzapw/OmniRoute/pull/11564)) — thanks @ragnar-claude +- **feat(resilience):** add provider peak-hour protection ([#11622](https://github.com/diegosouzapw/OmniRoute/pull/11622)) — thanks @Neuron-Mr-White +- **feat(providers):** add 1min.ai provider ([#11631](https://github.com/diegosouzapw/OmniRoute/pull/11631)) — thanks @zero-executioner +- **feat(dashboard):** collapsible JSON tree viewer for request/response payloads ([#11703](https://github.com/diegosouzapw/OmniRoute/pull/11703)) — thanks @hartmark +- **feat(routing):** add LiquidAI LFM2.5-2.6B free tier via OpenRouter ([#11752](https://github.com/diegosouzapw/OmniRoute/pull/11752)) — thanks @brick30llc-ctrl +- **feat(pricing):** add GLM-5.3-Flash pricing, model specs, and catalog registration ([#11830](https://github.com/diegosouzapw/OmniRoute/pull/11830)) — thanks @AndrianBalanescu +- **feat(routing):** add relayMode for schema-locked context handoffs ([#11839](https://github.com/diegosouzapw/OmniRoute/pull/11839)) — thanks @raheemuddin786 +- **feat(config):** add support for runtime system prompt configuration and hot-reloading ([#11841](https://github.com/diegosouzapw/OmniRoute/pull/11841)) — thanks @raheemuddin786 +- **feat(routing):** expand connection-aware quota prefilter across combo strategies (#11682) ([#11850](https://github.com/diegosouzapw/OmniRoute/pull/11850)) — thanks @HouMinXi +- **feat(models):** reactive discovery sync after Antigravity model-not-found 404 ([#11888](https://github.com/diegosouzapw/OmniRoute/pull/11888)) — thanks @MumuTW +- **feat(models):** auto-aliases and token limits from synced Antigravity discovery ([#11889](https://github.com/diegosouzapw/OmniRoute/pull/11889)) — thanks @MumuTW +- **feat(settings):** raise sticky round-robin limit caps to 1000 ([#12015](https://github.com/diegosouzapw/OmniRoute/pull/12015)) — thanks @adivekar-utexas +- **feat(resilience):** operator-configurable global credential health check interval ([#12043](https://github.com/diegosouzapw/OmniRoute/pull/12043)) — thanks @adivekar-utexas +- **feat(quota):** use official OpenCode Go usage API ([#12124](https://github.com/diegosouzapw/OmniRoute/pull/12124)) — thanks @ddarkr +- **feat(resilience):** default the credential health check sweep to 60 minutes ([#12138](https://github.com/diegosouzapw/OmniRoute/pull/12138)) — thanks @adivekar-utexas +- **feat(quality):** new-code mode for the complexity and dead-code ratchets (Clean as You Code) ([#12142](https://github.com/diegosouzapw/OmniRoute/pull/12142)) +- **feat(dashboard):** orchestration canvas — unified model + snapshot hook (part 1/2) ([#12156](https://github.com/diegosouzapw/OmniRoute/pull/12156)) +- **feat(usage):** add Kilo Code balance and Kilo Pass quotas ([#12178](https://github.com/diegosouzapw/OmniRoute/pull/12178)) — thanks @killer30001000 +- **feat(providers):** refresh vendored ChatGPT Web connector to v4.0.7 ([#12181](https://github.com/diegosouzapw/OmniRoute/pull/12181)) — thanks @backryun +- **feat(providers):** manual "Clear cooldown" action in the cooling panel ([#12224](https://github.com/diegosouzapw/OmniRoute/pull/12224)) — thanks @patrykkopycinski +- **feat(providers):** restore ChatGPT Web via clean-room browser transport ([#12239](https://github.com/diegosouzapw/OmniRoute/pull/12239)) — thanks @backryun +- **feat(sse):** wire the PROVIDER_PROFILES window gate into the global provider cooldown ([#12247](https://github.com/diegosouzapw/OmniRoute/pull/12247)) +- **feat(providers):** modernize CLOVA Studio chat and embeddings ([#12277](https://github.com/diegosouzapw/OmniRoute/pull/12277)) — thanks @backryun +- **feat(browser):** adopt Obscura as primary headless browser engine with Chromium fallback ([#12286](https://github.com/diegosouzapw/OmniRoute/pull/12286)) — thanks @opensource-elearning +- **feat(sse):** full language parity for output styles (es/de/fr/it/ru/zh + autoDetect) ([#12289](https://github.com/diegosouzapw/OmniRoute/pull/12289)) +- **feat(quality):** complete test:scoped — --full map rebuild, stdin selection, CI loader parity (#8084 D1) ([#12353](https://github.com/diegosouzapw/OmniRoute/pull/12353)) +- **feat(providers):** refresh Fable, Cursor, and Devin catalogs ([#12367](https://github.com/diegosouzapw/OmniRoute/pull/12367)) — thanks @backryun +- **feat(video):** redact transcript text from logs and durable memory (#12150 P1) ([#12427](https://github.com/diegosouzapw/OmniRoute/pull/12427)) +- **feat(dashboard):** parent-link, genuine-continuation badge, and modal perf fixes ([#12448](https://github.com/diegosouzapw/OmniRoute/pull/12448)) — thanks @hartmark +- **feat(dashboard):** adaptive context-budget dial on compression panel ([#12488](https://github.com/diegosouzapw/OmniRoute/pull/12488)) — thanks @HouMinXi +- **feat(providers):** refresh Z.ai Web models and browser transport ([#12524](https://github.com/diegosouzapw/OmniRoute/pull/12524)) — thanks @backryun +- **feat(video):** redact raw client-snapshot transcript fields in the detailed log (#12150 P2) ([#12528](https://github.com/diegosouzapw/OmniRoute/pull/12528)) +- **feat(providers):** refresh NVIDIA hosted models ([#12538](https://github.com/diegosouzapw/OmniRoute/pull/12538)) — thanks @backryun +- **feat(quota):** Moonshot Open Platform balance and TPD lock for custom nodes ([#12590](https://github.com/diegosouzapw/OmniRoute/pull/12590)) — thanks @HouMinXi +- **feat(video):** redact transcript in the in-memory pending-request snapshot (#12430 item 6) ([#12596](https://github.com/diegosouzapw/OmniRoute/pull/12596)) +- **feat(dashboard):** link a log entry's Conversation Context to its owning conversation ([#12646](https://github.com/diegosouzapw/OmniRoute/pull/12646)) — thanks @hartmark +- **feat(catalog):** eligibility-gated free-tier bucket ([#12669](https://github.com/diegosouzapw/OmniRoute/pull/12669)) +- **feat(db):** fail-closed previous_response_id continuation for redacted video turns (#12150 P2b) ([#12707](https://github.com/diegosouzapw/OmniRoute/pull/12707)) +- **feat(gamification): pay the documented `streak_bonus` and `badge_unlock` XP rewards.** `XP_REWARDS` listed both rewards but the award pipeline never paid them: the private reward table in `events.ts` omitted them, `updateStreak()` did not report when a streak extended, and badge unlocks carried no XP. Every request that extends a daily streak now pays `streak_bonus × streak length` once per UTC day (guarded by a same-day `xp_audit_log` check), and every badge unlocked through the pipeline pays `badge_unlock` once per badge (guarded by the `user_badges` primary key; `unlockBadge()` now reports whether it inserted). Bonus XP flows through the same `addXp` + level sync + global/weekly/monthly leaderboard path as action XP, so level-ups and rankings include it. The Radar supporter recognition unlock stays XP-free. (#12522 — thanks @pacocartones) +- **feat(providers):** advertise a `free-tier` capability in the provider plugin manifest for every provider with documented free models, so sidecars and dashboards can filter free-capable providers without reading the quota catalog ([#12786](https://github.com/diegosouzapw/OmniRoute/pull/12786)) — thanks @maxmad64bis +- **feat(providers):** Added EURouter as an OpenAI-compatible API-key gateway (`https://api.eurouter.ai/v1`), with live model discovery via `passthroughModels`. Its copy states that models are served by third-party upstreams listed per model, so an EU-based router is not read as EU data residency for inference. ([#13025](https://github.com/diegosouzapw/OmniRoute/pull/13025)) — thanks @ntdatt812 +- **feat(providers):** Added GreenPT as an OpenAI-compatible API-key provider (`https://api.greenpt.ai/v1`), with live model discovery via `passthroughModels`. No free-inference badge: the published docs describe a free API subscription billed per token, not a free tier. ([#13024](https://github.com/diegosouzapw/OmniRoute/pull/13024)) — thanks @ntdatt812 +- **feat(i18n):** 9 new locales — Greek (`el`), Croatian (`hr`), Serbian (`sr`), Lithuanian (`lt`), Estonian (`et`), Latvian (`lv`), Slovenian (`sl`), Maltese (`mt`), Irish (`ga`) — across the dashboard, docs mirrors, CLI, README and the site; OmniRoute now ships every official EU language (51 locales). (#13044) +- **feat(i18n):** 8 new locales — Kannada (`kn`), Malayalam (`ml`), Odia (`or`), Punjabi (`pa`), Nepali (`ne`), Sinhala (`si`), Burmese (`my`), Khmer (`km`) — across the dashboard, docs mirrors, CLI, README and the site (59 locales). The translator now restores the ICU literal escape around angle placeholders and splits oversized docs sections before translating. (#0000) ([#13660](https://github.com/diegosouzapw/OmniRoute/pull/13660)) +- feat(providers): **list Agnes 3.0 Flash as the current free chat model, drop retired 1.5 Flash, add Image 2.0/2.5 Flash plus Video 2.5/2.5 Flash, and discover the live `/v1/models` catalog (including the CN host `api.agnes-ai.cn`).** `agnes-1.5-flash` now forwards to `agnes-3.0-flash`. Video 2.5 polls `GET /v1/videos/{id}` (not the V2.0 `/agnesapi` contract). Live `/v1/models` (2026-09-09) no longer serves 1.5; the wiki marks it deprecated. 3.0 Flash is 512K context / 65,536 max output, same window as 2.5. CN-region keys use the existing per-connection base-URL field, default stays `apihub.agnes-ai.com`. ([#13120](https://github.com/diegosouzapw/OmniRoute/pull/13120)) — thanks @HouMinXi +- **feat(sse):** Codex and OpenAI catalogs list GPT-6 Astra with effort aliases (`-low` through `-ultra`); Codex CLI identity pins `@openai/codex@0.153.4` in lockstep with the image ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **feat(dashboard):** the orchestration History tab gained a "Compare runs" mode — toggling it turns each grid cell into a 2-item selection queue (a 3rd click drops the oldest pick), and picking two cells opens a side-by-side comparison panel instead of the usual detail drawer. The panel fetches both runs' detail the same way the drawer does (falling back to persisted history once a run leaves the live TTL window) and shows, per side: identity/source/state, start time, a signed `right - left` delta for duration/cost/event count, the event timeline aligned by index, and any memory hits. One side's fetch failing never blocks the other, and a delta is only ever computed when both sides have a finite value — otherwise it renders "—", never `NaN`. Comparing two runs from different sources or skills still works; a banner marks the deltas as informational rather than hiding them, since the two runs aren't a strict apples-to-apples pair. ([#12677](https://github.com/diegosouzapw/OmniRoute/pull/12677)) +- **feat(dashboard):** badge a conversation that never reached a clean stop ([#12717](https://github.com/diegosouzapw/OmniRoute/pull/12717)) — thanks @hartmark +- **feat(release):** reconcile-changelog tool + version-anchored fragment aggregation ([#12987](https://github.com/diegosouzapw/OmniRoute/pull/12987)) +- **feat(mcp):** carry context_length through MCP list_models_catalog projection (#12776) ([#13406](https://github.com/diegosouzapw/OmniRoute/pull/13406)) — thanks @KooshaPari +- **feat(cli):** make startup readiness budget configurable (#13369) ([#13433](https://github.com/diegosouzapw/OmniRoute/pull/13433)) — thanks @KooshaPari + ### 🐛 Bug Fixes +- **security(streaming):** sanitize generic mid-stream error messages before emitting OpenAI, + Responses, or Claude SSE failure frames and before diagnostic logging, while preserving raw + failures for internal classification and keeping client disconnects out of provider failure state. ([#12457](https://github.com/diegosouzapw/OmniRoute/pull/12457)) +- **fix(claude):** `blockExtraUsage: false` no longer lets 5h quota preflight skip the account; extra usage is billed after the session bar is gone, so the request must reach Anthropic ([#12803](https://github.com/diegosouzapw/OmniRoute/pull/12803)) — thanks @HouMinXi +- Sanitize HuggingChat conversation-creation and message-send transport failures before they reach client error bodies or provider logs. ([#12467](https://github.com/diegosouzapw/OmniRoute/pull/12467)) +- **fix(dashboard):** Keep local and theme-aware provider SVG icons at a definite layout size so Chromium does not collapse them to 0×0 after the v3.8.50 image-rendering change ([#12054](https://github.com/diegosouzapw/OmniRoute/pull/12054)) — thanks @ponkcore +- **fix(security):** Sanitize provider and runtime failures before public API, SSE and MCP responses and before persistent request, proxy and usage logs, preventing credentials, stack traces and host filesystem paths from crossing those boundaries while preserving stable error codes and useful diagnostics. ([#12506](https://github.com/diegosouzapw/OmniRoute/pull/12506)) +- **fix(cli):** `omniroute tunnel create` no longer crashes with `Cannot read properties of undefined (reading optsWithGlobals)` — removed the duplicate positional argument that caused Commander.js to misalign the action callback parameters ([#12295](https://github.com/diegosouzapw/OmniRoute/issues/12295)) ([#12368](https://github.com/diegosouzapw/OmniRoute/pull/12368)) — thanks @gonisulaimann +- Stop advertising Gemini Live-only models as supported audio endpoints until OmniRoute proxies the bidirectional Live protocol. ([#11710](https://github.com/diegosouzapw/OmniRoute/pull/11710)) — thanks @RaviTharuma +- **fix(kie):** reroute `flux/kontext` off the KIE Market `createTask` flow — it is catalogued with `isMarket: true` but has no Market catalog page, so KIE rejected it with "model name not supported"; it now hits the dedicated `POST /api/v1/flux/kontext/generate` / `GET /api/v1/flux/kontext/record-info` endpoints instead (#11296). ([#11985](https://github.com/diegosouzapw/OmniRoute/pull/11985)) — thanks @hartmark +- **fix(kie):** correct 12 more KIE Market catalog ids that were sent to `createTask` unchanged but diverge from KIE's documented upstream `model` values — GPT Image 2 T2I/I2I (drops the `gpt/` prefix), GPT Image 1.5 T2I/I2I (`gpt-image/` namespace), Seedream 5.0 Lite T2I/I2I (drops the `.0`), all 4 Flux 2 variants (`flux-2/` namespace, generic variant renamed `flex`), and Wan 2.7 Image / Image Pro (dash instead of dot) — each verified individually against the literal example request published on docs.kie.ai. `#11326`'s "everything else already matches" claim was wrong a second time (#11296); `z-image/4.0-*`/`z-image/4.5-*` and `flux/kontext` remain open, documented as unresolved in `KIE_MARKET_UPSTREAM_MODEL_IDS`'s comment pending further verification. ([#11667](https://github.com/diegosouzapw/OmniRoute/pull/11667)) +- **fix(dashboard):** `useApiKeySave.handleSaveApiKey` no longer forces a full upstream `/models` catalog sync on every non-curated provider connection save — callers can now pass `skipModelSync: true` to opt out, so a workflow that only wants to add one manual model no longer floods the provider's available-models list with hundreds/thousands of synced entries. The flag is a client-side intent signal only and is stripped before the connection payload is POSTed to `/api/providers`; default behavior (full sync on save) is unchanged when the flag is omitted (#11324) ([#11664](https://github.com/diegosouzapw/OmniRoute/pull/11664)) +- **Provider connections:** keep `tokenExpiresAt` when a connection is created. The create-path allowlist omitted it, so every insert stored NULL and the dashboard token badge could read a fresh connection as expired until its first background refresh ([#11368](https://github.com/diegosouzapw/OmniRoute/pull/11368)). — thanks @ntdatt812 +- **fix(combos):** the combo builder's precision-select, global-model-search, and manual-entry flows now serialize a model step's `model` string using the provider's already-computed routing-alias prefix (e.g. `oc/`) instead of rebuilding it from the raw canonical `providerId`, fixing the no-auth "OpenCode Free" provider (`opencode`) being routed to the unrelated paid "OpenCode Zen" provider (`opencode-zen`) because `opencode` doubles as a manual routing-prefix override ([#11433](https://github.com/diegosouzapw/OmniRoute/issues/11433)). ([#11670](https://github.com/diegosouzapw/OmniRoute/pull/11670)) +- **fix(radar):** the catalog feed cache now keeps `generatedAt`, the date the feed's data was built, next to `fetchedAt`, the date this install downloaded it (#11435). The feed schema requires that date and the sync path validates it, but the cache dropped it — so a feed fetched minutes ago and one carrying weeks-old figures looked identical to everything downstream, including the dashboard's "Last fetched" line. `getRadarCatalog().meta` and `GET /api/radar/status` now report both dates, the latter as its own field rather than folded into `version` — and omitted entirely for the offers and intel caches, which keep no build date, where a `null` would read as "unknown" rather than "never stored". The dashboard still shows only the fetch time; surfacing the build date there needs a new translated label and is left to a follow-up. Rows cached before migration 163 read back as `null`: unknown stays unknown instead of borrowing the fetch time. The referrals cache has persisted the same date since migration 142. — thanks @maxmad64bis +- **fix(cli):** postinstall no longer fills `JWT_SECRET` and `API_KEY_SECRET` in the installed package's `.env` (#11436). `.env.example` ships both blank on purpose: the server restores them from its durable store, or generates and persists them there, in `ensureSecrets()`. Pre-filling them defeated that — the file lives inside the package directory, so `npm i -g` replaced it and postinstall wrote _different_ values, while `ensureSecrets()` (which only acts on an empty variable) never got to restore the real ones. Both secrets rotated silently on every update, invalidating dashboard sessions and API-key CRCs. `STORAGE_ENCRYPTION_KEY` left the same list for the same reason in #1622; its comment pointed at a function that no longer exists and now names the real provisioning path. — thanks @maxmad64bis +- **fix(cli):** the CLI now says when a loaded `.env` lives inside the installed package directory (#11437). It already announces every env file it reads, without distinguishing the ones that survive an update from the one that does not: `npm i -g` replaces the package directory wholesale, so values set there are gone at the next update, silently. The warning names the durable path to move them to, and fires only when that file actually supplied a value — a file entirely shadowed by a durable one supplied nothing. A development checkout stays silent: there the same path is stable and documented in `SETUP_GUIDE.md`. — thanks @maxmad64bis +- **fix(free-models):** the shared `isFreeModel()` predicate no longer reports catalog entries marked `freeType: "discontinued"` as free, so `hidePaidModels` can't route to Pollinations' seven premium models that now require a paid key ([#11441](https://github.com/diegosouzapw/OmniRoute/pull/11441)) — thanks @maxmad64bis +- fix(ui): let AnySearch use the normal provider-icon fallback when LobeHub has no matching icon (#11449) +- Fixed the v3.8.50 Costs and Analytics dashboards so flat-rate Claude Code usage can be shown as an explicitly requested token-price estimate without changing default billed-cost semantics. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed archived usage retention so each request is priced individually instead of pricing a day's summed tokens once, which understated archived cost whenever a day mixed cache-heavy and ordinary requests. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed the Costs dashboard so it discloses when displayed figures include flat-rate token-price estimates instead of labelling them as billed spend, using the flag the analytics API already returns; the month-end projection and the CSV/JSON exports carry the same marker, and billed-cost mode is unchanged. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- **fix(combo):** attach the same combo-diagnostics trace (`poolSize`/`attemptOrder`/`excluded`/`terminalReason`, plus `x-omniroute-combo-*` headers) to the round-robin strategy's and the nested pipeline/fusion runtime-unit loop's "Maximum combo retry limit reached" 503 that the priority-strategy path already attaches for the identical terminal condition — previously those two paths returned a bare, contextless 503 ([#11462](https://github.com/diegosouzapw/OmniRoute/issues/11462)). ([#11665](https://github.com/diegosouzapw/OmniRoute/pull/11665)) +- **fix(bun):** use native `bun:sqlite` in `bootstrap-env` and `sync-env` to avoid loading `better-sqlite3` N-API addon during Bun startup ([#11468](https://github.com/diegosouzapw/OmniRoute/pull/11468)) — thanks @TheDemonTuan +- **fix(docker-bun):** make `Dockerfile.bun` install reliable with pre-install script helpers and native `bun:sqlite` smoke check ([#11470](https://github.com/diegosouzapw/OmniRoute/pull/11470)) — thanks @TheDemonTuan +- **fix(docker-bun):** remove every vendored `better-sqlite3` native addon from the Bun runtime image so startup cannot abort before the `bun:sqlite` fallback ([#11482](https://github.com/diegosouzapw/OmniRoute/pull/11482)) — thanks @TheDemonTuan +- **fix(autoCombo):** effort/alias model variants (`gpt-5.6-sol-xhigh`, `gpt-5.6`, cursor's `claude-4.6-opus-high`) inherit their base model's task fitness instead of falling to the wildcard 0.5 ([#11492](https://github.com/diegosouzapw/OmniRoute/pull/11492)) — thanks @MumuTW +- fix(db): rate-limit repeated Arena ELO leaderboard fetch-failure warnings instead of logging one per sync attempt (#11500) ([#11989](https://github.com/diegosouzapw/OmniRoute/pull/11989)) — thanks @hartmark +- **fix(db):** dedupe the raw `[Encryption] Decryption failed...` log line emitted by the lazy-decrypt views (`createLazyRowProxy`/`createLazyConnectionView`), which power `getProviderConnections()` and were re-triggering that line on every CredentialHealth/model-sync cycle for the same corrupt or stale-key credential — a fresh Proxy over a fresh row on every cycle meant the per-proxy memoization never suppressed it, unlike the dedup `decryptConnectionFields()` already had since [#9927](https://github.com/diegosouzapw/OmniRoute/issues/9927). Now shares that dedupe tracking so the line logs at most once per credential ([#11500](https://github.com/diegosouzapw/OmniRoute/issues/11500)). ([#11678](https://github.com/diegosouzapw/OmniRoute/pull/11678)) +- **fix(intelligence):** Arena ELO sync stores a synthesized base-model row for every effort/harness variant (`claude-opus-5-max` → `claude-opus-5`, `gpt-5.6-sol-xhigh (codex-harness)` → `gpt-5.6-sol`), so bare model requests reach the synced intelligence layer instead of the static fallback table; the hand-maintained `MODEL_ALIAS_MAP` — which collapsed generations (`gpt-5.5` → `gpt-5`) — is gone ([#11506](https://github.com/diegosouzapw/OmniRoute/pull/11506)) — thanks @MumuTW +- **fix(autoCombo,sse):** catalog hygiene — the static task-fitness table no longer ranks retired models above live ones (dead rows dropped, patterns matched on segment boundaries), the 7 deprecation aliases that forwarded to retired or non-existent models now point at the vendors' published replacements, an alias is no longer rewritten when the serving provider offers the id as-is, and a new `check:model-lifecycle` gate diffs both tables against a vendor lifecycle snapshot ([#11507](https://github.com/diegosouzapw/OmniRoute/pull/11507)) — thanks @MumuTW +- **fix(sse):** stop dropping resolved `thoughtSignature` values on parallel (multi tool-call) turns sent to Gemini 3.x — the claude→gemini and openai→gemini translators previously kept the signature only on the *first* function call of a message, causing Gemini to reject subsequent calls in the same turn with HTTP 400 "Function call is missing a thought_signature"; each function call now keeps its own resolved signature ([#11510](https://github.com/diegosouzapw/OmniRoute/issues/11510)). ([#11669](https://github.com/diegosouzapw/OmniRoute/pull/11669)) +- Treat Docker publishing from a non-default release branch as an expected skip instead of a failed workflow, while preserving the guard that prevents it from overwriting the `next` channel. ([#11527](https://github.com/diegosouzapw/OmniRoute/pull/11527)) — thanks @pacocartones +- fix(search): prefer credentialed providers over duckduckgo-free fallback (#11524) — the fallback-only loop ran before the credentialed-providers loop in `executeWebSearch`, making configured providers unreachable when `duckduckgo-free` was available. ([#11565](https://github.com/diegosouzapw/OmniRoute/pull/11565)) — thanks @vsd2807 +- **fix(sse):** cap the upstream headers-wait phase for STREAMING requests to a client-realistic ceiling (110s, under Codex's own ~120s hard client-abort window) instead of the flat 10-minute `FETCH_TIMEOUT_MS` default — that default was 5x longer than the body-phase readiness watchdog's own adaptive bound, so a request whose upstream never returned any response at all (not even headers, e.g. a stalled NVIDIA target behind a tool-heavy Responses→Chat translation) kept the client connection alive on keepalives only, guaranteeing the client's own patience ran out first with an opaque 499 instead of OmniRoute detecting and failing the stall fast. Non-streaming requests are unaffected — they keep the existing flat default (`open-sse/utils/fetchStartTimeoutPolicy.ts`) (#11526) ([#11668](https://github.com/diegosouzapw/OmniRoute/pull/11668)) +- **fix(ci):** protocol-clients E2E harness boots the peer-stamped custom server (`run-next.mjs dev`) instead of the bare `next` CLI, so `LOCAL_ONLY` locality resolves from the real TCP peer stamp and `/api/mcp/audit` answers 200 (open bootstrap preserved via an `OMNIROUTE_E2E_BOOTSTRAP_MODE=open` credential clear in `run-next.mjs`, pinned to loopback `HOST`) instead of a deterministic 403 (#11535) ([#11549](https://github.com/diegosouzapw/OmniRoute/pull/11549)) — thanks @jonlwheat2-gif +- **fix(docs):** the Auto-Combo engine is described with the number of factors it actually declares. Four different counts were in circulation — 6, 9, 13 and 14 across `AGENTS.md`, four guides and six code comments — while `DEFAULT_WEIGHTS` declares 15. `check:docs-counts` now reads that number from the source, so adding a factor without updating the prose fails CI ([#11545](https://github.com/diegosouzapw/OmniRoute/pull/11545)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **fix(quota-share):** in-flight leases are per request, so an aborted request's slot ages out instead of being kept alive by later traffic on the same connection ([#11547](https://github.com/diegosouzapw/OmniRoute/pull/11547)) — thanks @abhisheksharma2411 +- The free-tier summary route now serves the refreshed Radar catalog when the feed is active — the same numbers the dashboard shows — and states which catalog answered plus its real build date, instead of always reporting release-frozen figures with a stale curation date. The supporter-key live feed stays reserved to authenticated callers of the instance. ([#11550](https://github.com/diegosouzapw/OmniRoute/pull/11550)) — thanks @maxmad64bis +- **sse:** fix LiveWS/embed-WS servers crashing at startup under the Node/tsx runtime — `liveServer.ts`, `embedWsProxy.ts` and `apiBridgeServer.ts` imported `@/shared/utils/httpClientAbortGuard` without the `.mjs` extension, so the client-abort crash guard added by [#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556) was unreachable and every dependent test failed with `ERR_MODULE_NOT_FOUND` ([#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556)). ([#11671](https://github.com/diegosouzapw/OmniRoute/pull/11671)) — thanks @jonlwheat2-gif / @yxyxy +- fix(chat): shed any of the matched provider-node's routing identifiers (public prefix or internal id) before model lookup, so composites like `//` no longer reach upstream verbatim (#11557) — thanks @maxmad64bis +- **fix(cache):** a semantic-cache entry now expires on its TTL instead of surviving until the next UTC midnight, and `dbEntries` stops counting expired rows ([#11573](https://github.com/diegosouzapw/OmniRoute/pull/11573)) — thanks @ntdatt812 +- **fix(api):** `/v1/models` schedules its stale-while-revalidate rebuild through Next's `after()` again, so a stale catalog reaches the client before the rebuild blocks the event loop ([#11574](https://github.com/diegosouzapw/OmniRoute/pull/11574)) — thanks @ntdatt812 +- **fix(check):** the `known-symbols` executor conformance gate awaits the now-async `getExecutor()`, so it stops reporting all 142 registered aliases as dead symbols and can detect a lazy import that fails ([#11582](https://github.com/diegosouzapw/OmniRoute/pull/11582)) — thanks @ntdatt812 +- **fix(pack):** register `bin/cli/utils/volatileEnvPath.mjs` in `PACK_ARTIFACT_REQUIRED_PATHS`, so the prune can no longer silently drop a module the published CLI imports on every boot ([#11588](https://github.com/diegosouzapw/OmniRoute/pull/11588)) — thanks @ntdatt812 +- **i18n(vi):** translate the five reliability-column strings on the free-provider rankings page, which rendered as `__MISSING__:` markers in the Vietnamese UI ([#11589](https://github.com/diegosouzapw/OmniRoute/pull/11589)) — thanks @ntdatt812 +- **resilience:** restore the expired-connection retry-budget probe in the token-health sweep — the `!isGitHubAccessTokenOnlyConnection` carve-out reintroduced by #11608 contradicted the boundary pinned by #11592, so a GitHub connection parked at `expired` with retry budget remaining was never probed and could never self-heal ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)). ([#11672](https://github.com/diegosouzapw/OmniRoute/pull/11672)) — thanks @jonlwheat2-gif / @ntdatt812 +- **fix(dashboard):** Provider Stats now displays combo success rates on their native 0–100 percentage scale instead of multiplying them twice ([#11595](https://github.com/diegosouzapw/OmniRoute/pull/11595)) — thanks @pacocartones +- **fix(providers):** route DuckDuckGo Web Chat through the verified `duck.ai` endpoint, refresh its free model catalog, and avoid forwarding unsolved anti-abuse challenges ([#11597](https://github.com/diegosouzapw/OmniRoute/pull/11597)) — thanks @solstxce +- **fix(ui):** Console log Refresh and Copy controls now expose localized accessible names, keep copy actions visible on keyboard focus, and announce copy completion safely ([#11599](https://github.com/diegosouzapw/OmniRoute/pull/11599)) — thanks @pacocartones +- Dropped the stale-`.eslintcache` `restore-keys` fallback from both "Restore ESLint file cache" steps in `ci.yml`, so the blocking `Lint` job can no longer be served per-file verdicts computed under a different lint config, suppressions file or lockfile. `quality.yml` had already dropped it in #11963; `ci.yml` — the workflow that actually gates PRs — had not (#11600). ([#11996](https://github.com/diegosouzapw/OmniRoute/pull/11996)) +- fix(ci): pass `--pass-on-unpruned-suppressions` in `run-eslint-json.mjs` so the CI Lint job no longer fails when a suppression is merely orphaned by a genuine fix, mirroring the identical fix already in `validate-release-green.mjs` (#11600) ([#11983](https://github.com/diegosouzapw/OmniRoute/pull/11983)) — thanks @hartmark +- fix(handoff): enforce provider allowlist for universal handoff (#11602) — universal handoff now skips summarization when the selected summary model's provider is not included in the configured provider allowlist. ([#11619](https://github.com/diegosouzapw/OmniRoute/pull/11619)) — thanks @vsd2807 +- **fix(dashboard):** Show a stable error state when Search Analytics returns an HTTP error or malformed data ([#11603](https://github.com/diegosouzapw/OmniRoute/pull/11603)) — thanks @pacocartones +- **test(gamification):** pin the aggregate profile level to the XP-derived semantics of #11604 — `getAggregateXp()` now derives `currentLevel` from the summed XP (`calculateLevel(sum)`), not `MAX(stored current_level)`, and the #3484 fixture levels are aligned with the XP curve ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)). ([#11676](https://github.com/diegosouzapw/OmniRoute/pull/11676)) — thanks @jonlwheat2-gif / @pacocartones +- **fix(dashboard):** Keep the Profile level and progress aligned with aggregate XP, including bounded handling for invalid totals ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)) — thanks @pacocartones +- **fix(dashboard):** Prevent locked hidden badges from revealing their icon or opening private badge details before they are earned ([#11605](https://github.com/diegosouzapw/OmniRoute/pull/11605)) — thanks @pacocartones +- **fix(dashboard):** Restore keyboard focus after shared modals close and cancel delayed autofocus during cleanup ([#11607](https://github.com/diegosouzapw/OmniRoute/pull/11607)) — thanks @pacocartones +- **fix(dashboard):** Enable Enter and Space activation for clickable data-table rows without hijacking nested controls ([#11610](https://github.com/diegosouzapw/OmniRoute/pull/11610)) — thanks @pacocartones +- **fix(autoCombo,sse):** vendor-retired catalog ids are dropped from the auto-combo candidate pool and no longer win on leftover `arena_elo` / `user_override` rows; `getModelLifecycleDecision` consults `model-lifecycle.json` (prefix-stripped) so aggregator traffic is not `untracked` for ids the snapshot already knows ([#11625](https://github.com/diegosouzapw/OmniRoute/issues/11625)) ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)) — thanks @MumuTW +- **test(chatcore):** move Codex/Claude combo fixtures off models the lifecycle guard now rejects — `gpt-5.1-codex`/`gpt-5-codex` are vendor-retired (snapshot, #11626) and `claude-3-5-sonnet-20241022` is shut down, so native-passthrough and combo-fallback tests switched to `gpt-5.6-sol` and `claude-sonnet-4.6` ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)). ([#11675](https://github.com/diegosouzapw/OmniRoute/pull/11675)) — thanks @jonlwheat2-gif / @MumuTW +- fix(opencode-plugin): stop a free-tier budget that rounds up across a magnitude from rendering as `1000K`/`1000M` in the model picker — `fmtTokens` chose its unit from the raw token count and then rounded with `toFixed(1)`, so 999,950–999,999 printed as `1000K` rather than `1M` and just under a billion printed as `1000M` rather than `1B` ([#11684](https://github.com/diegosouzapw/OmniRoute/pull/11684)) — thanks @abhisheksharma2411 +- Cap prose-derived Antigravity quota resets at the configured model cooldown maximum while preserving authoritative Retry-After headers and Google RetryInfo hints. (#11695) Thanks @pacocartones. ([#11823](https://github.com/diegosouzapw/OmniRoute/pull/11823)) +- Compare `_rebaseline_*` notes structurally in the ratchet-bank verifier so an object-valued note no longer reports itself as rewritten on an unmodified tree — the nightly `bank-ratchet-shrinks` lane had been aborting on every run since 2026-08-11, leaving 41,672 lines of earned file-size shrinks unbanked (#11702). ([#11892](https://github.com/diegosouzapw/OmniRoute/pull/11892)) — thanks @MumuTW +- **fix(build):** `prepublish.ts` bundles the ChatGPT Web (Codex) MCP bridge through `runBuildTool()` instead of spawning `npx.cmd` raw, fixing the build crash on Node ≥ 20/Windows where `.cmd` shims cannot be spawned without a shell (EINVAL) ([#11704](https://github.com/diegosouzapw/OmniRoute/issues/11704)) ([#11706](https://github.com/diegosouzapw/OmniRoute/pull/11706)) — thanks @jonlwheat2-gif +- fix(codex): keep `parallel_tool_calls:false` on the translated Codex Responses Lite path (#11707) ([#11984](https://github.com/diegosouzapw/OmniRoute/pull/11984)) — thanks @hartmark +- **fix(tests):** the new Gemini Web Images retirement test (#11708) called the now-async `getExecutor()` synchronously, throwing `TypeError: getExecutor(...).getProvider is not a function` instead of asserting Gemini Web chat stays available. Awaited to match the R0.3 executor-registry refactor (#11220). ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(search):** Allow REST and MCP web-fetch requests to select the advertised `anysearch-search` provider explicitly ([#11716](https://github.com/diegosouzapw/OmniRoute/pull/11716)) — thanks @pacocartones +- **fix(providers):** Keep overnight peak-hour protection active after midnight according to the window's configured start day ([#11718](https://github.com/diegosouzapw/OmniRoute/pull/11718)) — thanks @pacocartones +- **fix(providers):** correct all five g4f.space gateway descriptions to document proof-of-work credits or member API keys, variable limits, and the remote prompt boundary instead of promising free keyless access at five requests per minute ([#11738](https://github.com/diegosouzapw/OmniRoute/pull/11738)). +- **fix(db):** `model_capabilities` is created by a migration instead of lazily on the first models.dev sync, so a clean install and an upgraded install converge on the same schema regardless of which features have run ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(ci):** `check:install-upgrade` now fails on an `npm` install truncated by ENOSPC (npm reports it as a warning and still exits 0), authenticates its health probe so the version assertion works against the hardened health payload, frees the clean-install tree before the upgrade phase, and no longer reports a schema divergence computed from a boot that never served ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(routing):** harden session-affinity key derivation against oversized explicit IDs and empty structural-payload collisions, while recognizing Gemini and common text request shapes ([#11746](https://github.com/diegosouzapw/OmniRoute/pull/11746)) — thanks @MumuTW +- **fix(packages/browser-pool):** regenerate `package-lock.json` so the `packages/browser-pool` workspace's locked `playwright`/`@types/node` (and transitives) match its `package.json` specs, fixing cache-only/offline installs (`npm ci --offline`, Nix `buildNpmPackage`) that previously failed with `ENOTCACHED` ([#11747](https://github.com/diegosouzapw/OmniRoute/issues/11747)) — thanks @benjaminkitt ([#11784](https://github.com/diegosouzapw/OmniRoute/pull/11784)) — thanks @geekyNads +- **fix(assets):** Stop distributing 79 provider and CLI logos without verified source/license provenance, keep every provider/tool available through the internal generic icon, and prevent those IDs from falling through to an external icon CDN. ([#11750](https://github.com/diegosouzapw/OmniRoute/pull/11750)) +- **fix(providers):** retire the common `chatgpt-web` / `cgpt-web` provider because its pre-key and proof-of-work provenance could not be cleared ([#11754](https://github.com/diegosouzapw/OmniRoute/pull/11754)) — explicit chat and image requests, slash-prefixed models, persisted aliases, proxy overrides, and combo targets now fail closed with a sanitized HTTP `410` / `PROVIDER_RETIRED` before upstream dispatch. Migration 163 tombstones matching connections and invalidates their active leases without deleting history or widening API-key allowlists. The independent `chatgpt-web-codex` / `cgpt-codex` provider and its MIT-noticed vendored implementation remain available. +- **fix(usage):** quota and usage refresh no longer 409 when an exclusive lease reserves the connection ([#11758](https://github.com/diegosouzapw/OmniRoute/pull/11758)) — thanks @TheDemonTuan +- Keep the embedding registry's vector width and `embedding` type on models when a synced model exists for the same id, so `/v1/models` no longer reports registry-described embedding models widthless or untyped (#11761) — thanks @rahilmavani +- Correct `google/gemini-embedding-001` on the OpenRouter route to 3072 dimensions, the width it returns when `dimensions` is not sent (#11761) — thanks @rahilmavani +- **fix(vision-bridge):** Restrict automatic vision routing and fallbacks to models available in authoritative live provider catalogs, including aliases and effort variants ([#11767](https://github.com/diegosouzapw/OmniRoute/issues/11767)) — thanks @pacocartones ([#11813](https://github.com/diegosouzapw/OmniRoute/pull/11813)) +- **fix(providers):** reclassify Cerebras as a one-time $5 signup credit (payment method required, 30-day validity), not a recurring no-card 1M tokens/day trial ([#11773](https://github.com/diegosouzapw/OmniRoute/issues/11773)) ([#12591](https://github.com/diegosouzapw/OmniRoute/pull/12591)) — thanks @HouMinXi +- fix(cli): stop prepublish from re-rebuilding the already-built ESM-only opencode-plugin dist (#11787) ([#11990](https://github.com/diegosouzapw/OmniRoute/pull/11990)) — thanks @hartmark +- **fix(db):** Fresh installs no longer log a non-fatal `no such table: compression_run_telemetry` warning when retention cleanup runs before the lazily-created telemetry table exists ([#11802](https://github.com/diegosouzapw/OmniRoute/pull/11802)) — thanks @RaviTharuma +- fix(sse): set X-OmniRoute-Selected-Connection-Id on successful combo dispatches so downstream consumers stop falling back to an empty connection id (#11810) ([#11986](https://github.com/diegosouzapw/OmniRoute/pull/11986)) — thanks @hartmark +- **fix(services):** embedded CLIProxyAPI lifecycle checks now use public `/healthz`, while model discovery uses the configured dedicated data-plane API key instead of the management password ([#11811](https://github.com/diegosouzapw/OmniRoute/pull/11811)) — thanks @RaviTharuma +- Fix persisted model steps returning an error in the playground route simulator, and warn when structural steps cannot be simulated. ([#11862](https://github.com/diegosouzapw/OmniRoute/pull/11862)) — thanks @pacocartones +- **fix(providers):** Antigravity's dynamic mitmAlias table no longer routes `gemini-3.7-flash-{high,medium,low}` to a literal tier-suffixed upstream id just because one connected account's own discovery listed it directly — those display ids always resolve through the safe `gemini-3.7-flash-tiered` static alias, so one account's Google-provisioned access no longer 404s every sibling account of the provider ([#11824](https://github.com/diegosouzapw/OmniRoute/issues/11824), [#11651](https://github.com/diegosouzapw/OmniRoute/issues/11651)) ([#11988](https://github.com/diegosouzapw/OmniRoute/pull/11988)) — thanks @hartmark +- **fix(dashboard):** Expose Profile loading, errors, page structure, and XP progress to assistive technologies ([#11838](https://github.com/diegosouzapw/OmniRoute/pull/11838)) — thanks @pacocartones +- **fix(socks):** `Agent.connectTimeout` now bounds both SOCKS handshake and TLS connect, and `family === null` no longer falls back to `fetch-socks` ([#11842](https://github.com/diegosouzapw/OmniRoute/pull/11842)) — thanks @maxmad64bis +- **perf(compression):** OOM mitigations for large payload hashing, memoization, and token estimation ([#11844](https://github.com/diegosouzapw/OmniRoute/pull/11844) — thanks @AndrianBalanescu) +- Excluded Next.js Node File Trace manifests (`*.nft.json`) from the published npm tarball. They are build-time metadata and are never read while serving, but had grown to 668.7 MB — 61% of the package — which pushed the upload past the registry limit and made `npm publish` fail with `413 Payload Too Large`. ([#11864](https://github.com/diegosouzapw/OmniRoute/pull/11864)) +- **fix(cli):** keep the global OmniRoute server `--base-url` separate from provider-node `--endpoint` values in add, update, and validate commands ([#11860](https://github.com/diegosouzapw/OmniRoute/pull/11860)) — thanks @pacocartones +- **fix(config):** Nous Research's `Hermes-4-405B` model now displays as "Hermes 4 405B (Nous Research)" in both the provider registry and the free-model catalog, instead of the mislabelled "Hermes 4 7B" ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#11993](https://github.com/diegosouzapw/OmniRoute/pull/11993)) +- **fix(provider/nous):** inject required user= tag into Nous Research inference requests to resolve upstream 400 "missing tags" error ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#12044](https://github.com/diegosouzapw/OmniRoute/pull/12044)) +- **fix(build):** `npm run build` now fails in one second with a named package and a copy-pasteable fix when npm silently drops an externalised optional native dependency, instead of dying four minutes in with `Module not found: Can't resolve 'better-sqlite3'` ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- **fix(install):** `postinstall` no longer throws `ReferenceError: isAndroid is not defined` — failing the whole `npm install` — when the `better-sqlite3` rebuild fallback times out; the manual-fix guidance is reachable again ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- Fixed the Alibaba free-tier allowlist test that went red on its own once the shipped catalog's `validUntil` (2026-08-27) passed, leaving every PR and `main` with a failing `Unit Tests (1/8)`. The test now builds its own packs with dates it controls, and covers the expired-pack fallback that production has actually been serving. ([#11867](https://github.com/diegosouzapw/OmniRoute/pull/11867)) +- Split the npm registry upload into its own GitHub-hosted job. npm refuses `--provenance` from a self-hosted runner (`422 ... Only "github-hosted" runners are supported`), which blocked the v3.8.50 publish; the heavy verification cannot move to a hosted runner, so it now hands the proven tarball over instead. ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(ui):** the shared `DataTable` loading state no longer reads its decorative ⏳ glyph out to assistive technology, and now carries the same `role="status"` / `aria-live="polite"` / `aria-busy="true"` semantics as `PageLoading` ([#11869](https://github.com/diegosouzapw/OmniRoute/pull/11869)) — thanks @pacocartones +- **fix(api):** Bound the `timeout` accepted by `POST /api/providers/[id]/login` to the same 15s–600s window the Adobe Firefly and Conol browser-login paths already enforce, so a request can no longer pin a headful browser and the single active-login slot with an arbitrarily large poll budget. ([#11871](https://github.com/diegosouzapw/OmniRoute/pull/11871)) — thanks @pacocartones +- **fix(api):** Stop the tunnel and MITM routes from returning raw child-process error text, which disclosed host paths, binary install locations and Tailscale `tskey-*` credentials in a body some of these routes serve to non-loopback callers, and return a real 400 instead of a framework 500 when a tunnel request body fails validation. ([#11872](https://github.com/diegosouzapw/OmniRoute/pull/11872)) — thanks @pacocartones +- **fix(open-sse):** stream TTFT/ITL are sampled from a monotonic clock (`performance.now`) instead of `Date.now`, so an NTP correction or wall-clock jump can no longer inflate a genuine TTFT or produce a negative one that the `ttft >= 0` guard silently drops — protecting the router's OTel/EWMA/`usage_history`/speed-ranking signals ([#11873](https://github.com/diegosouzapw/OmniRoute/pull/11873)) — thanks @pacocartones +- **fix(providers):** xAI `reasoning_effort: "xhigh"` now reaches grok-4.6+ instead of being silently clamped to `"high"` ([#11879](https://github.com/diegosouzapw/OmniRoute/pull/11879)) — thanks @NoxzRCW +- **fix(dashboard):** the providers page no longer crashes into the error boundary when a provider id collides with an `Object.prototype` member (`constructor`, `__proto__`); icon lookups are own-property guarded ([#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)) — thanks @NoxzRCW +- **fix(skills):** injected skill tools declared in shorthand (`{"content": "string"}`) now forward valid JSON Schema, unblocking providers that validate tool schemas strictly such as Zhipu GLM on the Console Go tier ([#11881](https://github.com/diegosouzapw/OmniRoute/pull/11881)) — thanks @NoxzRCW +- **fix(api):** `POST /api/playground/simulate-route` now surfaces `combo-ref` and `provider-wildcard` persisted combo steps with a specific warning (naming the referenced combo, or the unresolved `provider/model` wildcard) instead of folding them into a generic "unsupported step" count; a `provider-wildcard` step is also now included as an unresolved target so the operator can see it is in the route (ported from [#11882](https://github.com/diegosouzapw/OmniRoute/pull/11882) — thanks @NoxzRCW). ([#11930](https://github.com/diegosouzapw/OmniRoute/pull/11930)) +- **fix(translator):** the streaming OpenAI→Claude translator keeps upstream usage, including prompt-cache tokens, when it arrives on a trailing `choices: []` chunk (Fireworks and any upstream using `stream_options.include_usage`) ([#11883](https://github.com/diegosouzapw/OmniRoute/pull/11883)) — thanks @NoxzRCW +- **fix(cli):** `omniroute update --apply` now tells you explicitly whether a running server was detected and, if so, that you must run `omniroute restart` to apply the update — it never restarted anything and previously implied the update was already live once files were installed. The dashboard's npm-mode Update flow (`/api/system/version`) now tries OmniRoute's own PID-file-managed supervisor before falling back to pm2, and reports an honest "restart required" step instead of a silent pm2-only "skipped" that read like a completed update. The server-side latest-version lookup backing the dashboard's update banner also gained `--prefer-online`, closing the same stale-npm-cache class already fixed in the CLI's own copy for #4376 ([#11885](https://github.com/diegosouzapw/OmniRoute/issues/11885)). ([#12005](https://github.com/diegosouzapw/OmniRoute/pull/12005)) +- **fix(resilience):** clear persisted LKGP pins when a target suffers connection/provider exhaustion or is skipped before dispatch due to cooldown/exhaustion/unavailability, preventing subsequent requests from repeatedly prioritizing known-dead providers ([#11911](https://github.com/diegosouzapw/OmniRoute/issues/11911)). ([#12013](https://github.com/diegosouzapw/OmniRoute/pull/12013)) — thanks @HouMinXi +- **fix(sse):** the OpenAI→Claude stream translator now defers the terminal `message_delta`/`message_stop` emission until the real usage block has arrived (or a genuine end-of-stream flush forces it) instead of emitting it immediately on `finish_reason` — previously, when the trailing usage-only chunk (`{"choices":[],"usage":{...}}`) arrived *after* the `finish_reason` chunk (the normal order for Fireworks/vLLM/Together and other `stream_options.include_usage` upstreams), the client-visible `message_delta` still carried stale/zero usage even though `state.usage` was internally corrected too late to matter (ported from [#11915](https://github.com/diegosouzapw/OmniRoute/pull/11915) — thanks @HouMinXi). ([#11933](https://github.com/diegosouzapw/OmniRoute/pull/11933)) +- **fix(sse):** `stripStore()` now forces `store=false` for stateless OpenAI-compatible Responses-API targets (unless the connection has explicitly opted in via `providerSpecificData.openaiStoreEnabled`), instead of only handling the `openai`/`agentrouter` cases — a client-supplied `store` value previously passed through untouched to backends that don't actually persist responses server-side ([#11916](https://github.com/diegosouzapw/OmniRoute/pull/11916) — thanks @HouMinXi). +- **fix(models):** custom provider-node models (synced, custom, and alias-backed) now appear under their configured prefix in the unified catalog when the operator's model-id prefix mode is canonical, instead of being dropped whenever alias-inclusion was otherwise disabled ([#11918](https://github.com/diegosouzapw/OmniRoute/pull/11918) — thanks @HouMinXi). +- **fix(models):** the unified model catalog now suppresses stale static registry models (including effort-tier variants) for any provider whose active connection has an authoritative live synced catalog, not only providers already using exclusive-synced-listing — a connection with `providerUsesAuthoritativeLiveCatalog` previously kept serving both the live-synced models and the stale static rows side by side ([#11919](https://github.com/diegosouzapw/OmniRoute/pull/11919) — thanks @HouMinXi). +- **fix(ui):** `ProviderIcon`'s three remaining unguarded lookups (`PROVIDER_ICON_ALIASES`, `LOCAL_SVG_ALIASES`, `THEMED_SVGS`) now use `Object.hasOwn()` own-property checks — a provider id such as `constructor` or `__proto__` previously resolved through the prototype chain instead of falling through to the unknown-provider CDN fallback (`getLobeProviderIcon()` itself was already guarded by [#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)); `ProviderPageHeader`'s `color` field is also now optional, matching the rest of the component's defensive typing (ported from [#11920](https://github.com/diegosouzapw/OmniRoute/pull/11920) — thanks @HouMinXi). ([#11935](https://github.com/diegosouzapw/OmniRoute/pull/11935)) — thanks @NoxzRCW +- fix(ollama): preserve multi-byte UTF-8 content split across stream chunks in the Ollama NDJSON transform, which previously corrupted CJK/emoji into U+FFFD (#11921) — thanks @pacocartones +- **fix(providers):** OrcaRouter chat requests now target `/v1/chat/completions` instead of the bare `/v1` API root, fixing the upstream `404 Invalid URL (POST /v1)` ([#11923](https://github.com/diegosouzapw/OmniRoute/pull/11923)). — thanks @echel0nn +- **fix(plugins):** deliver the `onStreamComplete` event to disk-installed plugins. The event shipped in v3.8.50 (#9669) was emitted internally but had no plugin-facing wiring, so no plugin could ever subscribe: the manifest schema silently dropped `hooks.onStreamComplete`, and the loader/manager only knew the seven legacy hooks. `onStreamComplete` is now a declarable manifest hook, wired through the loader and registered by the manager like the other hooks, and its payload carries a `requestId` so consumers can correlate the stream-completion event with the originating request ([#11934](https://github.com/diegosouzapw/OmniRoute/pull/11934)) — thanks @amaleta / @pacocartones +- **fix(db):** the Qdrant embedding-model dropdown now lists local/self-hosted providers (Ollama, LM Studio, vLLM, etc.) — an active connection is treated as "configured" when the provider allows an optional API key, not only when it has a real key or OAuth, so a running local embedding provider is no longer hidden from the picker ([#11949](https://github.com/diegosouzapw/OmniRoute/issues/11949)) ([#11995](https://github.com/diegosouzapw/OmniRoute/pull/11995)) +- **fix(providers):** Vertex AI Anthropic partner-model discovery now calls the Model Garden `v1beta1` publisher list (`/v1beta1/publishers/anthropic/models`, global) and parses its `publisherModels` envelope, so Claude models auto-synced from Vertex populate the active live catalog and route at request time instead of returning `Model '' is not available in the active live catalog` ([#11991](https://github.com/diegosouzapw/OmniRoute/issues/11991)) — thanks @fabioluissilva ([#11998](https://github.com/diegosouzapw/OmniRoute/pull/11998)) +- **fix(cli):** support `--base-url` alongside `--endpoint` in `omniroute nodes add`, `update`, and `validate` subcommands to prevent global `--base-url` shadowing issues ([#11999](https://github.com/diegosouzapw/OmniRoute/issues/11999)). ([#12033](https://github.com/diegosouzapw/OmniRoute/pull/12033)) — thanks @HouMinXi +- **fix(providers):** `cloudflare-ai` no longer refuses image content parts for every Workers AI model ([#12002](https://github.com/diegosouzapw/OmniRoute/pull/12002)) — the plain-string `content` requirement behind #2539 is carried by the _model_ schema, not by the `/ai/v1/chat/completions` endpoint (measured: an all-text part array returns 200 on `@cf/mistralai/mistral-small-3.1-24b-instruct`, `@cf/meta/llama-4-scout-17b-16e-instruct` and `@cf/meta/llama-3.3-70b-instruct-fp8-fast`, and 400 on the text-only `@cf/qwen/qwen2.5-coder-32b-instruct`). `transformRequest()` flattened every array and threw on the first non-text part (#6390), so image input was refused for vision-capable Cloudflare models that accept it. All-text arrays are still flattened — the one shape every model accepts — while an array carrying a non-text part is passed through untouched, so the attachment is still never silently dropped. Regression guards: `tests/unit/cloudflare-ai-image-parts-6390.test.ts`. — thanks @davidlinfr +- **fix(resilience):** decouple the limiter-managed execution backstop from the queue-wait budget — new `requestQueue.executionMaxWaitMs` (env `RATE_LIMIT_EXECUTION_MAX_WAIT_MS`, default 600000 = 10 min) now feeds Bottleneck's post-dispatch `expiration`, while `requestQueue.maxWaitMs` keeps its documented queue-wait semantics. Previously the queue-wait budget doubled as the execution expiration, so legitimate long-running calls on non-incremental gateways (whole generation buffered before the first upstream byte, e.g. Console Go / Command Code tiers serving GLM models) were killed mid-flight at the queue budget with a false 504 `RATE_LIMIT_EXECUTION_TIMEOUT` — the local limiter undercut the provider-aware upstream fetch-start timeouts. The surfaced 504 message now names `requestQueue.executionMaxWaitMs`; the error keeps the #4165 guarantees (disclaims an upstream timeout, preserves the Bottleneck error as `cause`, branded code + trusted provenance, classified request-scoped so combo falls back). A real queue-wait bound (the `Promise.race` around `limiter.schedule()` sketched in #9533) remains future work. (#12025) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **Call logs:** keep the `error` field when an artifact exceeds the storage cap, instead of replacing it with the omission marker. The error is the only field that says *why* a request failed and is typically ~90 bytes next to the multi-hundred-KB bodies that trip the cap, so dropping it left a size-limited row undiagnosable — a provider outage, a local timeout and an upstream 400 all rendered identically. It is now preserved at every fallback stage, truncated to 4KB if it is itself large ([#12026](https://github.com/diegosouzapw/OmniRoute/issues/12026)). ([#12095](https://github.com/diegosouzapw/OmniRoute/pull/12095)) — thanks @ntdat812 +- **fix(diagnostics):** preserve the error field (truncated to 4KB with a `[truncated: …]` suffix) in every call-log artifact size-limit fallback stage. Previously the minimal fallback replaced the error with `[omitted: call log artifact size limit exceeded]`, so an oversized artifact row showed nothing about WHY the request failed — e.g. 91 of 847 opencode-go 504 rows on one production instance were undiagnosable from the dashboard. Oversized request/response bodies are still omitted exactly as before; the error cap is independent of the payload sizes that tripped the fallback. (#12026) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **fix(sse):** OpenAI Responses clients that declare the native `web_search` tool now receive a spec-shaped `web_search_call` output item with `action.sources` alongside the preserved function-call round-trip, so search results executed through OmniRoute's own search backend are consumable by standard Responses clients (Codex, pi-web-access, …). ([#12031](https://github.com/diegosouzapw/OmniRoute/pull/12031)) — thanks @watchingdogs +- **fix(cli):** use in-thread alias resolver hooks on modern runtimes to avoid deprecation noise and improve Node.js forward compatibility ([#12073](https://github.com/diegosouzapw/OmniRoute/issues/12073)). ([#12083](https://github.com/diegosouzapw/OmniRoute/pull/12083)) — thanks @xiaoyaner0201 +- **fix(combo):** an operator-set **Agent Features → Context length** on a combo is now honored at request time. The value was persisted and advertised through `/v1/models`, but `resolveComboContextLimit()` never consulted it — so a multi-target combo whose members carry no per-model window fell through to the provider's generic `defaultContextLength` (openrouter 128000, command-code 200000) and rejected large requests with `Input exceeds context window … limit 128000` despite the combo being explicitly sized much larger. An identical single-target combo worked, because it collapses to its concrete target before the guard runs. Invalid values (0/negative/NaN/Infinity) are ignored, so the existing target → combo-min → fallback order is unchanged. ([#12090](https://github.com/diegosouzapw/OmniRoute/pull/12090)) — thanks @adivekar-utexas +- **fix(sse):** passthrough streams now estimate usage on finish when upstream closes without usage even with `stream_options.include_usage` — avoids `0 tokens / 0%` for providers that stay silent (and correctly handles trailing empty-choices usage) ([#12151](https://github.com/diegosouzapw/OmniRoute/pull/12151)) — thanks @maxmad64bis +- **fix(combos):** clearing an agent feature in the combos editor now persists — unchecking context cache protection, or emptying the system message or tool filter, sends an explicit `null` instead of dropping the field from the `PUT` body, which the update merge read as "leave unchanged" ([#12177](https://github.com/diegosouzapw/OmniRoute/pull/12177)) — thanks @foreveryh / @ntdatt812 +- **fix(memory):** self-hosted embedding endpoints now vectorize — the vector width is measured from the first embedding that comes back instead of being read from a registry that cannot describe them, so `vec_memories` is created and memories stop piling up unvectorized behind a green health check ([#12180](https://github.com/diegosouzapw/OmniRoute/pull/12180)) — thanks @kanade-hoshino / @ntdatt812 +- **fix(translator):** the leading `system` message now reaches Responses-API upstreams when its `content` is a content-part array — it was read as `typeof content === "string" ? content : ""`, so a prompt-caching client (Anthropic `cache_control`, the shape LiteLLM and the Anthropic SDK emit) had its entire system prompt replaced by an empty `instructions`. The request was still accepted with a normal `prompt_tokens` count, so the model answered with no instructions and nothing in the response said they were missing. Mid-conversation system turns already handled the array shape ([#7056](https://github.com/diegosouzapw/OmniRoute/pull/7056)); only the first one did not ([#12206](https://github.com/diegosouzapw/OmniRoute/issues/12206)). Regression guard: `tests/unit/translator-openai-responses-system-content-parts.test.ts`. ([#12207](https://github.com/diegosouzapw/OmniRoute/pull/12207)) — thanks @Gorillaz322 +- **fix(free-tier):** `/api/free-tier/summary` no longer computes its totals from a Radar feed built before the catalog the running release ships. When the cached feed is older — or carries no build date at all — the route answers from the shipped catalog, resolved through the operator's local model state so disabled and tombstoned models stay out of the numbers ([#12215](https://github.com/diegosouzapw/OmniRoute/pull/12215)). — thanks @maxmad64bis +- **fix(oauth):** Keep a Claude personal workspace and a Team organization as separate connections — they share the same email and `accountUUID`, so the email-only OAuth dedup let the second login overwrite the first account's tokens; `organizationUUID` now disambiguates them, the way `workspaceId` does for Codex ([#12222](https://github.com/diegosouzapw/OmniRoute/pull/12222)) — thanks @davidebaraldo +- **fix(resilience):** a 402 on a single paid model of a passthrough/gateway provider (e.g. `kilo-gateway`, `ollama-cloud`) no longer terminalizes the whole connection with a never-auto-recovered `credits_exhausted` status — only the paid model is locked out, so free models on the same key keep serving. 402 variant of [#3027](https://github.com/diegosouzapw/OmniRoute/issues/3027). Single-credential providers are unaffected — a 402 there is still treated as the key being genuinely out of credit ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239) / [#10616](https://github.com/diegosouzapw/OmniRoute/issues/10616)) ([#12242](https://github.com/diegosouzapw/OmniRoute/issues/12242)) — thanks @brick30llc-ctrl ([#12266](https://github.com/diegosouzapw/OmniRoute/pull/12266)) — thanks @geekyNads +- **fix(sse):** trust `finish_reason: "length"`/`"max_tokens"` over the reasoning-consumed-token ratio in response quality validation, so a reasoning model truncated below the old 90% threshold correctly fails and retries instead of returning empty content as a silent "success" ([#12262](https://github.com/diegosouzapw/OmniRoute/pull/12262)) — thanks @brick30llc-ctrl +- **fix(settings):** `PUT /api/settings/cache-config` now persists `alwaysPreserveClientCache` to the flat general settings the runtime cache-control policy actually reads; previously the value landed in the databaseSettings "cache" section and was silently ignored, so the endpoint had no effect on `cache_control` passthrough ([#12304](https://github.com/diegosouzapw/OmniRoute/pull/12304)) — thanks @davidebaraldo +- **fix(grok-cli):** treat omitted SuperGrokPro `creditUsagePercent` as 0% used so Provider Limits still renders a weekly bar (proto3 zero-elision) ([#12312](https://github.com/diegosouzapw/OmniRoute/pull/12312)) — thanks @HouMinXi +- **fix(combo):** Expose the two Auto-Combo scoring factors nobody could set — the combo validation schema and the dashboard weight sliders both declared 13 of the scorer's 15 factors, so `connectionDensity` (spreads load across a provider's connections) and `quality` were dropped on save and offered nowhere. The sliders also shipped their own default table that differed from the engine's on every non-zero factor and summed to 1.05, so the percentages shown next to them added up to 105%. Both lists now match `DEFAULT_WEIGHTS`, and a test keeps them there. Note that a combo whose stored `weights` omitted the two keys was effectively running with them at zero and the other thirteen renormalized upward; it now runs with the engine's intended distribution, so its routing does shift ([#12314](https://github.com/diegosouzapw/OmniRoute/pull/12314)) — thanks @maxmad64bis +- **fix(docs):** The free-tier reference no longer says its numbers come "confidence tagged per row" — no catalog entry carries a confidence tag and the API serves none, so every figure on that page is an estimate of the same, unstated quality. The page now states what an entry does vouch for: an independently documented hard stop (set by hand with the source in a comment, never defaulted to `true`) and a prompt-training disclosure, both with live counts the `check:docs-counts` gate keeps honest ([#12318](https://github.com/diegosouzapw/OmniRoute/pull/12318)) — thanks @maxmad64bis +- **fix(usage):** `adobe-firefly` and `firefly` have had a working usage fetcher since Adobe Firefly landed, but neither was ever added to the registration list, so the provider-plugin manifest, `genericQuotaFetcher` and the free-access quota cache all reported them as having no usage support — while `USAGE_SUPPORTED_PROVIDERS` said the opposite. Both are now declared, which also means their credit balance is fetched like any other declared provider's: `registerGenericQuotaFetchers` now registers a generic quota fetcher for them, and `resolveFreeAccessState` no longer returns early. A test holds the registration list to the dispatcher's switch in both directions, which is what the module's own docstring already asked for in prose ([#12321](https://github.com/diegosouzapw/OmniRoute/pull/12321)) — thanks @maxmad64bis +- **fix(quota):** drop the generic quota cache (agy / Antigravity / Claude OAuth) on an upstream 429 so reset-aware scoring does not keep a 60s stale snapshot, and force-refresh the next usage fetch so inner provider caches cannot recache the same window ([#12325](https://github.com/diegosouzapw/OmniRoute/pull/12325)) — thanks @HouMinXi +- **fix(combos):** deleting a combo now clears its persisted LKGP pins instead of leaving unreachable `key_value` rows behind ([#12326](https://github.com/diegosouzapw/OmniRoute/issues/12326)) ([#12425](https://github.com/diegosouzapw/OmniRoute/pull/12425)) — thanks @KrzysiekSko +- **fix(sse):** Keep ZWNJ (U+200C) and ZWJ (U+200D) in assistant text, reasoning and tool-call arguments — Persian/Kurdish half-space (`ارائه‌دهنده`), Arabic/Indic shaping and emoji sequences no longer lose them; the response de-obfuscation now removes joiners only between ASCII word characters, where the request side inserts them ([#12186](https://github.com/diegosouzapw/OmniRoute/issues/12186)) — thanks @rezjalibd ([#12359](https://github.com/diegosouzapw/OmniRoute/pull/12359)) — thanks @pacocartones +- **fix(resilience):** count resolved upstream 5xx results against the provider circuit breaker on the chat path — `CircuitBreaker.execute()` no longer reads a resolved `{ success: false, status: 5xx }` as a success that cancels the call-site failure, so a provider answering 503s now trips its breaker instead of staying `CLOSED` at `failureCount: 1`; single-model and combo dispatches are each accounted exactly once ([#12254](https://github.com/diegosouzapw/OmniRoute/issues/12254)) ([#12360](https://github.com/diegosouzapw/OmniRoute/pull/12360)) — thanks @pacocartones +- **fix(providers):** resolve the Codex quota auto-ping model from the live provider catalog and lifecycle registry instead of the retired `gpt-5.1-codex-mini`, and pause the ping with one actionable warning when no selectable Codex model exists rather than retrying a shut-down id every cooldown window ([#11905](https://github.com/diegosouzapw/OmniRoute/issues/11905)) ([#12361](https://github.com/diegosouzapw/OmniRoute/pull/12361)) — thanks @pacocartones +- **fix(api):** keep the `{created, data}` wrapper on combo-routed `/v1/images/generations` responses and default Codex image results to `b64_json` on both `/v1/images/generations` and `/v1/images/edits` so Codex CLI's built-in `image_gen` can decode them ([#12268](https://github.com/diegosouzapw/OmniRoute/issues/12268)) ([#12362](https://github.com/diegosouzapw/OmniRoute/pull/12362)) — thanks @pacocartones +- **fix(sse):** Name the shadowed custom provider node when a built-in provider id/alias (e.g. `openference` → `of`) reserves the prefix of an existing OpenAI/Anthropic-compatible node, so the runtime `No active credentials for provider: ` error explains that the prefix routed to the built-in and never reached the node's healthy connections, instead of contradicting the dashboard ([#11943](https://github.com/diegosouzapw/OmniRoute/issues/11943)) — thanks @morpheus9393 ([#12365](https://github.com/diegosouzapw/OmniRoute/pull/12365)) — thanks @pacocartones +- **fix(i18n):** wrap `ccOnboardingKeyPlaceholder` in ICU single quotes across all 43 locales so angle brackets render literally instead of being parsed as rich-text tags, which crashed the Claude Code onboarding block with `INVALID_MESSAGE: INVALID_TAG` ([#12302](https://github.com/diegosouzapw/OmniRoute/issues/12302)) ([#12369](https://github.com/diegosouzapw/OmniRoute/pull/12369)) — thanks @gonisulaimann +- **fix(models):** publish `effort_tiers` on Kimi K3's synced base-model entries (`kmca/k3`, `kmca/k3-256k`) so catalog-only clients (OpenCode, plain SDK pickers) can see and select the reasoning tiers (`low`/`high`/`max`) the synced metadata already carried — the `isSkippedEffortProvider` gate no longer suppresses tier visibility on those base entries, while synthetic `-` variant generation stays prevented and Codex/GLM base models remain excluded unchanged ([#12299](https://github.com/diegosouzapw/OmniRoute/issues/12299)) ([#12371](https://github.com/diegosouzapw/OmniRoute/pull/12371)) — thanks @gonisulaimann +- **fix(guardrails):** keep `auto`/`auto/*` virtual combos exempt from the Vision Bridge `fixedModel` credential guard so a combo target is passed through instead of silently falling back to global auto-selection ([#12237](https://github.com/diegosouzapw/OmniRoute/issues/12237)) ([#12373](https://github.com/diegosouzapw/OmniRoute/pull/12373)) — thanks @pacocartones +- **fix(combo):** capability-filter exhaustion caused by `max_tokens` above every target's known output limit now reports that reason (requested `max_tokens` vs the pool's highest known ceiling) instead of the unrelated "supports structured output" message ([#12229](https://github.com/diegosouzapw/OmniRoute/issues/12229)) — thanks @DW-MediaLab ([#12374](https://github.com/diegosouzapw/OmniRoute/pull/12374)) — thanks @pacocartones +- **fix(auth):** the `least-used` account strategy now prefers accounts without backoff before falling back to oldest `lastUsedAt`, the same tie-break `round-robin` already applies, so a failover no longer lands on a just-rate-limited account for a single request ([#12279](https://github.com/diegosouzapw/OmniRoute/issues/12279)) — thanks @tenshiak ([#12375](https://github.com/diegosouzapw/OmniRoute/pull/12375)) — thanks @pacocartones +- **fix(docker):** the `chatgpt-web-codex-browser` image now finds the Chrome binary under `chrome-linux64/` (Chrome for Testing layout in `playwright:v1.62.0-noble`) as well as the legacy `chrome-linux/`, so the container no longer crash-loops with `exec: --headless=new: not found` ([#12024](https://github.com/diegosouzapw/OmniRoute/issues/12024)) ([#12376](https://github.com/diegosouzapw/OmniRoute/pull/12376)) — thanks @pacocartones +- **fix(providers):** declare `groq/compound` and `allam-2-7b` as non-reasoning models in the curated Groq registry so `reasoning_effort` / `output_config.effort` / `thinking` from Claude Code are stripped instead of forwarded, which Groq rejected with HTTP 400 ([#12134](https://github.com/diegosouzapw/OmniRoute/issues/12134)) ([#12379](https://github.com/diegosouzapw/OmniRoute/pull/12379)) — thanks @pacocartones +- **fix(executors):** `OpencodeExecutor` no longer forces a direct connection when the connection has a proxy assigned in Proxy Management but no per-account proxies: the single-account fast path used to wrap the upstream dispatch in the direct-egress sentinel, discarding the ambient proxy context the chat handler had pinned from `proxy_assignments`, so API-key `opencode`/`opencode-go` connections egressed from the host IP (and hit geoblocks) despite the assignment. The direct pin is now applied only when no ambient proxy context exists ([#11894](https://github.com/diegosouzapw/OmniRoute/issues/11894) — thanks @hizzt) ([#12380](https://github.com/diegosouzapw/OmniRoute/pull/12380)) — thanks @pacocartones +- **fix(api):** `GET /v1/models` with `MODELS_CATALOG_PREFIX_MODE=canonical` (or `?prefix=canonical`) now lists providers whose registry alias is undefined or equal to their own id (Antigravity, Antigravity CLI and other self-aliased built-ins) — their single `provider/model` id was dropped by the alias/canonical duplicate guard in the static, synced, custom and alias-backed catalog loops ([#12058](https://github.com/diegosouzapw/OmniRoute/issues/12058)) — thanks @cheynetom ([#12381](https://github.com/diegosouzapw/OmniRoute/pull/12381)) — thanks @pacocartones +- **fix(translator):** Drop replayed `thinking` blocks that carry no signature (the shape produced from cross-provider `reasoning_content`) instead of stamping the default Claude signature on them, which Anthropic rejected with `400 Invalid signature in thinking block` on the next turn served by an Anthropic rung ([#12105](https://github.com/diegosouzapw/OmniRoute/issues/12105)) — thanks @atescivitci-cmd ([#12386](https://github.com/diegosouzapw/OmniRoute/pull/12386)) — thanks @pacocartones +- **fix(cli):** Resolve Bun's `--preload` polyfill path against the package root instead of `dist/`, so `omniroute` installed with `bun install -g` no longer crashes at startup with `error: preload not found …/dist/open-sse/utils/setupPolyfill.ts` ([#11980](https://github.com/diegosouzapw/OmniRoute/issues/11980)) — thanks @joglomedia ([#12387](https://github.com/diegosouzapw/OmniRoute/pull/12387)) — thanks @pacocartones +- **fix(providers):** `gemini-business` now publishes its model catalog — `/v1/models` and `/v1/providers/gemini-business/models` list the 12 enterprise Gemini ids the executor understands instead of returning an empty list (#12107) ([#12389](https://github.com/diegosouzapw/OmniRoute/pull/12389)) — thanks @pacocartones +- **fix(db):** install `busy_timeout` before the SQLite connection's first statement so a process opening the database while another one closes its WAL connection waits out the transient EXCLUSIVE lock instead of dying with `database is locked`, and recognise the drivers' real BUSY/PROTOCOL/IOERR errors as transient in the corruption probe so the same lock no longer renames the database away as corrupt; deflakes `cross-process contenders never both acquire the same connection` (#12394 — thanks @pacocartones) +- **fix(chat-admission):** derive the `chat_admission_busy` 503 `Retry-After` from observed heavyweight-lease occupancy — the larger of the exhausted `OMNIROUTE_CHAT_ADMISSION_QUEUE_MS` window and the time since capacity last turned over, capped at 60 s — instead of a fixed 1 s (structural) / 2 s (byte-stage) hint that invited Codex/agent fan-out clients to re-send ~1 MiB `/v1/responses` bodies every second into a gate held for the whole SSE lifetime; an idle gate keeps the historical floors ([#12135](https://github.com/diegosouzapw/OmniRoute/issues/12135)) (#12395 — thanks @pacocartones) +- **fix(api-manager):** the API key permissions modal no longer silently drops `allowedCombos` entries its Combo picker cannot render — routing-rule names such as `rt-*`, which the backend already honours — when "All" is clicked and the key is switched back to "Restrict"; those entries now survive the toggle, are listed read-only under the combo list so the count and the list agree, and are saved back verbatim instead of persisting `[]` (deny-all) (#12397 — thanks @pacocartones) +- **fix(catalog):** write the NUL separator of the catalog connection memo key, the provider serviceKind memo key, the Video Bridge promotion group key and a JSON-exactness test fixture as the `\u0000` escape instead of a raw byte — same runtime value, but the raw byte made git, GitHub and ripgrep treat those files as binary (hidden PR diffs, silently skipped searches); a guard test now keeps raw NUL bytes out of `src/`, `open-sse/` and `tests/` (#12403 — thanks @pacocartones) +- **fix(providers):** add `CLAUDE_CODE_CLIENT_VERSION` and `GITHUB_COPILOT_CLI_VERSION` env overrides so Anthropic/Copilot client-version gates can be unblocked without a rebuild ([#12417](https://github.com/diegosouzapw/OmniRoute/issues/12417)) ([#12632](https://github.com/diegosouzapw/OmniRoute/pull/12632)) — thanks @HouMinXi +- **fix(db):** back-fill `last_ping_at` and `last_pinged_reset_key` on `provider_connections` during schema reconciliation so divergent lineages that skipped `123_quota_auto_ping` still accept quota auto-ping writes ([#12470](https://github.com/diegosouzapw/OmniRoute/pull/12470) — thanks @KooshaPari) +- **fix(ci):** document MIT exceptions for `@eloqnt/{config,format-json,format-po}` (next-intl transitive; locked tarballs omit `license`) and keep the A2A lifecycle vitest off the real SQLite persistence seam ([#12581](https://github.com/diegosouzapw/OmniRoute/issues/12581)) ([#12595](https://github.com/diegosouzapw/OmniRoute/pull/12595)) +- OpenCode plugin `/v1/models` catalog fetch now waits 30s by default and attaches HTTP `statusCode` on 401/5xx so host fallback plugins can hop instead of seeing an untyped AbortError/UnknownError. ([#12607](https://github.com/diegosouzapw/OmniRoute/pull/12607)) — thanks @RaviTharuma +- **CI:** the OpenAPI security-tier gate now mirrors `isAlwaysProtectedPath()` in full — it also reads `ALWAYS_PROTECTED_API_PATTERNS`, so the pattern-gated credential routes (`/api/providers/{id}/{claude,codex}-auth/{export,apply-local}`, GHSA-5926-2w35-7h4q) no longer report as unannotated. (#12605) +- **fix(api):** GET /v1/models no longer waits forever on a hung coalesced catalog rebuild; cold-path waits are bounded (`CATALOG_BUILD_TIMEOUT_MS`, default 8s) and a last-good 200 is served when the rebuild times out ([#12627](https://github.com/diegosouzapw/OmniRoute/issues/12627)). ([#12628](https://github.com/diegosouzapw/OmniRoute/pull/12628)) — thanks @RaviTharuma +- **fix(cursor):** a non-array `tool_calls` on an assistant message no longer crashes the cursor request translator with a `TypeError`; both loops now require an array ([#12689](https://github.com/diegosouzapw/OmniRoute/issues/12689)) ([#12691](https://github.com/diegosouzapw/OmniRoute/pull/12691)) — thanks @soroush5 +- **fix(chat):** Chat Completions no longer return empty `content` after a server-owned memory or skills tool; the first provider send and account/model recovery now share one pipeline so a follow-up round-trip can fill the reply ([#12696](https://github.com/diegosouzapw/OmniRoute/issues/12696)) — thanks @HouMinXi ([#12867](https://github.com/diegosouzapw/OmniRoute/pull/12867)) +- **fix(dashboard):** batch-deleting provider keys no longer toasts failure after a successful delete when the confirm button's click event is forwarded as `onAfter` ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) — thanks @HouMinXi +- **fix(glm):** drop the extra 16th argument to `createSSETransformStreamWithLogger` that TypeScript rejected (TS2554) and that never reached the TransformStream ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) ([#12770](https://github.com/diegosouzapw/OmniRoute/pull/12770)) — thanks @HouMinXi +- **fix(dashboard):** pass `nodeMap` into Runtime `QuotaGroup` so a quota monitor in error/exhausted/alerting no longer throws `ReferenceError: nodeMap is not defined`. ([#12868](https://github.com/diegosouzapw/OmniRoute/pull/12868)) — thanks @HouMinXi +- Restricted API keys whose `allowedModels` lists a combo name no longer skip every combo member at pre-dispatch (`ALL_TARGETS_SKIPPED`). Inner-target filtering still applies when the allow-list is a provider prefix or `disableNonPublicModels` is on ([#12899](https://github.com/diegosouzapw/OmniRoute/pull/12899)). — thanks @HouMinXi +- **fix(combos):** Effort-suffixed combo members inherit the base model's `model_context_overrides` row so priority order is not inverted on large requests ([#12926](https://github.com/diegosouzapw/OmniRoute/pull/12926)) — thanks @HouMinXi +- **fix(catalog):** Picker-added `customModels` enter the dispatch-time live catalog so combo members and bare inference no longer 400 ([#12934](https://github.com/diegosouzapw/OmniRoute/pull/12934)) — thanks @HouMinXi +- **fix(docker):** re-derive the Docker build's worker-pool memory budget from the MEASURED ~4.5 GB per-process RSS (the issue owner's own VPS dmesg OOM-killer reproduction) instead of the stale 2560 MB/worker inference #11419 shipped, and lower `OMNIROUTE_BUILD_WORKERS` 3 → 2 so 1 parent + 1 page-data worker (2 processes × 4.5 GB = 9 GB) fits the 12.288 GB (75%) budget on a 16 GB GitHub Actions runner — the previous default (1 parent + 2 workers = 13.5 GB) still overcommitted the runner and kept "Publish to Docker Hub" failing with `cannot allocate memory` after #11419 merged (#7518). ([#11663](https://github.com/diegosouzapw/OmniRoute/pull/11663)) +- **fix(electron):** the packaged Windows desktop build now passes the #7592 cold-restart smoke end to end. Five defects were found and fixed on the way: (1) optional-pack staging failed on any machine whose `tar` is GNU tar — it reads the drive letter in an absolute `-f C:\...` archive path as a remote rsh host (`Cannot connect to C:`), so staging now passes a bare filename with `cwd` at the tarball directory and surfaces tar stderr on failure; (2) the Electron `files` allowlist was missing `lib/loginHeaderCapture.js`, so the packaged main process crashed at startup with `Cannot find module './lib/loginHeaderCapture'`; (3) electron-builder ≥26 injects an `!**/node_modules/**` ignore into every extraResources pattern list that no positive filter can override, which silently dropped the staged runtime `node_modules` (including the better-sqlite3 N-API prebuild) from `resources/app` — a fresh v3.8.50 desktop build would have shipped with no native SQLite driver and reproduced the #7592 sql.js fallback on every machine; an `afterPack` hook now restores the staged `node_modules` after packing; (4) the packaged-app smoke harness redirected `USERPROFILE` into its temp DATA_DIR, but Electron resolves the Roaming profile from `%USERPROFILE%\AppData\Roaming\` (USERPROFILE takes precedence over the APPDATA env var) and the path service throws instead of creating it, so `requestSingleInstanceLock()` returned false and the app exited(0) silently before `app.whenReady()` — the harness now pre-creates the derived tree, and `ensureSmokeEnvDirs` is exported and unit-tested; (5) the #7592 driver assertion parsed a `[DB] Driver: ...` line that the server's primary DB path never printed (only the unused `openDatabaseAsync()` did) — `getDbInstance()` now emits the same line on open so the guard can actually assert the native driver. Also: the smoke env-allowlist unit test hardcoded POSIX paths and could only pass on Linux/macOS; expectations are now host-agnostic, with new regression tests for the USERPROFILE-derived tree and `tarPack` under absolute Windows-style paths ([#11443](https://github.com/diegosouzapw/OmniRoute/pull/11443)) — thanks @jonlwheat2-gif +- fix(sse): stop the auto-combo candidates inspector from silently dropping model-locked/cooled-down rows (#9133) ([#11994](https://github.com/diegosouzapw/OmniRoute/pull/11994)) — thanks @hartmark +- **fix(providers):** Perplexity Web no longer turns upstream stream failures into successful assistant text; pre-content failures remain eligible for fallback, partial output ends with a structured sanitized error, and failed sessions are not persisted ([#12465](https://github.com/diegosouzapw/OmniRoute/pull/12465)) +- **fix(video-bridge):** preserve chronological video and transcript fusion, source-frame timestamps, transcript provenance, full contact-sheet intervals, and pre-abort callback isolation ([#11681](https://github.com/diegosouzapw/OmniRoute/pull/11681)) +- **Z.ai Web:** HTTP 200 streams carrying an upstream error now terminate with a structured failure instead of assistant text plus a normal stop, preserving partial output while allowing pre-content combo fallback. ([#12454](https://github.com/diegosouzapw/OmniRoute/pull/12454)) +- Absorb `Error [AbortError]: request_signal_aborted` and DOMException AbortError shapes in the process-level client-abort crash guard so routine client disconnects no longer kill the server (exit code 7). ([#12165](https://github.com/diegosouzapw/OmniRoute/pull/12165)) — thanks @alvinveroy +- **fix(sse):** Treat Adapta Web `type:error` SSE events as sanitized non-stream failures instead of empty HTTP 200 completions. ([#12459](https://github.com/diegosouzapw/OmniRoute/pull/12459)) +- **fix(autoCombo):** layer 3 (`models_dev_tier`) no longer scores retired models as premium, merge cross-provider capability rows deterministically, and inherit tier scores through variant ids ([#11508](https://github.com/diegosouzapw/OmniRoute/issues/11508)). `getModelsDevTierFitness` now vetoes ids present in `config/quality/model-lifecycle.json` with status `retired` before any other signal; `loadModelCapabilities()` aggregates per `model_id` (any-non-null-true for booleans, max for `limit_context`) instead of last-write-wins over SQLite's undefined row order; a capabilities miss retries through `resolveScoresAs()`'s base id and reports `models_dev_tier:inherited`, matching layers 1–2 (#11492). Adds `getModelsDevTierFitnessWithSource()` and an `invalidateCapabilitiesCache()` test/ops hook. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) — thanks @Meet6338-X +- fix(sse): accept Claude `content:[]` + `stop_reason: max_tokens`/`tool_use` in `detectMalformedNonStream` (match `isEmptyContentResponse`) so Claude Code `/model` probes with `max_tokens: 1` no longer become a false 502 ([#11568](https://github.com/diegosouzapw/OmniRoute/pull/11568)) — thanks @Seramicx +- **fix(security):** sanitize `request.failed` diagnostics before publishing them to live dashboard listeners and replay history, while keeping status, model, provider, latency, and internal call-log diagnostics intact. ([#12469](https://github.com/diegosouzapw/OmniRoute/pull/12469)) +- **fix(pwa):** do not intercept dashboard navigations so Chrome can fall back from a stale HTTP/3 Alt-Svc advertisement (UDP :20128 is unpublished; F5 on a long-lived tab hung until a new tab opened a fresh TCP connection). ([#12767](https://github.com/diegosouzapw/OmniRoute/pull/12767)) — thanks @HouMinXi +- **fix(executors):** handle DuckDuckGo ERR_BN_LIMIT (418) without retrying — when the upstream returns `418 ERR_BN_LIMIT` (rate-limit/ban), the executor now returns the error immediately instead of burning another VQD acquisition that would only count against the IP limit. The retry logic for `418 ERR_CHALLENGE` (unsolved challenge) remains unchanged. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) ([#12110](https://github.com/diegosouzapw/OmniRoute/pull/12110)) — thanks @oyi77 / @Meet6338-X +- Fixed DuckDuckGo streaming responses losing JSON lines and UTF-8 characters split across network chunks. ([#11528](https://github.com/diegosouzapw/OmniRoute/pull/11528)) — thanks @pacocartones +- Harden generated Cloudflare, Vercel, and Deno relays by stripping hop-by-hop, framing, and proxy authentication headers before forwarding requests upstream. ([#11533](https://github.com/diegosouzapw/OmniRoute/pull/11533)) — thanks @pacocartones +- **feat(grok-cli):** Provider Limits shows grok-cli banked reset credits from `GetRemainingResets` (including a real zero; a failed RPC omits the row) and the existing View credits button now calls `ConsumerUiSvc/RedeemReset` for grok-cli. Live tokens use nested fields 10/20/30 (id + Timestamp), not compact 1/2/3. ([#12805](https://github.com/diegosouzapw/OmniRoute/pull/12805)) — thanks @HouMinXi +- **fix(memory):** Embedding Model Quick select, Embedding Source remote dropdown, and Rerank selector now list every configured provider with embedding/rerank support instead of only chat-catalog text matches plus OpenRouter live discovery; a generic OpenAI-compatible `/embeddings` + Cohere-compatible `/rerank` runtime fallback resolves any configured chat provider's embedding/rerank endpoint, so unlisted providers no longer fail with "Unknown embedding provider"; both memory selectors gained a free-text model override ([#11390](https://github.com/diegosouzapw/OmniRoute/pull/11390)) — thanks @rqzbeh +- Harden SQLite upgrades around the historical migration-074 version collision: missing discovery and inspector tables are replayed atomically, pre-existing databases (including setup-created skeletons) receive reusable content-addressed safety snapshots, and Node test/eval probes without `DATA_DIR` are isolated from the operator database. ([#12435](https://github.com/diegosouzapw/OmniRoute/pull/12435)) +- **fix(dashboard):** Moonshot/Kimi Open Platform voucher and cash leftover percentages follow the bucket balance, so an empty wallet no longer paints those rows as 100% while Available is 0% ([#12733](https://github.com/diegosouzapw/OmniRoute/pull/12733)) — thanks @HouMinXi +- **fix(combo):** return non-retryable HTTP 400 when all candidates for a pinned native Codex turn are unavailable due to model-scoped lockout, terminating the turn cleanly while preserving turn continuity and enabling standard Combo routing on subsequent turns ([#12240](https://github.com/diegosouzapw/OmniRoute/pull/12240)) — thanks @mdigitalbh81 +- Fixed auto-router strategies preserving the selected connection through ranking and dispatch when multiple connections share the same provider and model. ([#11530](https://github.com/diegosouzapw/OmniRoute/pull/11530)) — thanks @pacocartones +- Preserve Chat Completions JSON-object response formats when translating requests to the Responses API. ([#11529](https://github.com/diegosouzapw/OmniRoute/pull/11529)) — thanks @pacocartones +- Fix idempotency fingerprints so Responses API requests include `input` and other semantic generation fields using stable serialization, while excluding credentials and request metadata. ([#11532](https://github.com/diegosouzapw/OmniRoute/pull/11532)) — thanks @pacocartones +- **fix(api):** Generated API CLI commands now enforce required OpenAPI request bodies; Combo test commands forward the required `comboName` body, while API keys created by older writers after migration 149 preserve legacy allow-all Combo access without widening explicit empty allowlists — thanks @marcelokarval ([#12070](https://github.com/diegosouzapw/OmniRoute/pull/12070)) +- **fix(grok-web):** treat upstream streaming failures as failures instead of successful assistant text: error-only streams now fail readiness with HTTP 502, while failures after legitimate content preserve that partial output and terminate through the sanitized stream failure path without a normal `stop` completion. ([#12458](https://github.com/diegosouzapw/OmniRoute/pull/12458)) +- HuggingChat now turns HTTP 200 JSONL generation failures into a sanitized 502 before content, or a fixed public stream failure after partial output, so fallback and request persistence no longer record a false successful stop. ([#12456](https://github.com/diegosouzapw/OmniRoute/pull/12456)) +- **fix(providers):** keep 1min.ai HTTP 200 stream errors out of assistant content, preserve partial output, and expose sanitized terminal errors so pre-content failures can fall back. ([#12466](https://github.com/diegosouzapw/OmniRoute/pull/12466)) +- **fix(sessions):** preserve legacy Sessions data when the additive exclusive-session projection is unavailable, returning an empty projection and warning only once per contiguous outage instead of failing the endpoint; the Sessions badge intentionally reflects the merged legacy and exclusive row count introduced by [#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389) ([#11469](https://github.com/diegosouzapw/OmniRoute/pull/11469)) — thanks @KaspaPulse +- Render Nimble Search with the generic provider icon and serve Opper's proven logo locally. ([#12415](https://github.com/diegosouzapw/OmniRoute/pull/12415)) +- **fix(quality):** `config/quality/provider-assets-provenance.jsonl` was stale against the physical `public/providers/` tree (still listing 225 assets and 5 duplicate-content groups after several provider-retirement PRs removed 85 icon files), which would have made `check:provider-asset-provenance` fail on the next full run. Reconciled the manifest to the real 142-file snapshot (142 asset records, 1 duplicate-content group, 2 newly-registered files marked `unresolved` pending provenance research), repinned `auditedCommit`, and updated the regression test's hardcoded counts to match. ([#11876](https://github.com/diegosouzapw/OmniRoute/pull/11876)) +- fix(ui): wrap quota card footer action buttons to prevent clipping Refresh button on narrow cards — when extra action buttons such as `View credits` are present on Codex cards, allow the footer row to flex-wrap with `shrink-0` buttons so `Refresh now` remains visible and clickable across all responsive card widths ([#11464](https://github.com/diegosouzapw/OmniRoute/pull/11464)) — thanks @adevwithpurpose +- Keep Antigravity Gemini usable when the same connection's Claude weekly quota is empty; generic quota cache stays per-connection for every other provider. ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) — thanks @HouMinXi +- Fixed Tailscale tunnel setup to reject ports outside the valid 1-65535 range and safely install Windows MSI packages from paths containing apostrophes. ([#11534](https://github.com/diegosouzapw/OmniRoute/pull/11534)) — thanks @pacocartones +- Classify Cloudflared and Tailscale process-management routes as local-only and spawn-capable while preserving remote access to read-only tunnel status endpoints. ([#11531](https://github.com/diegosouzapw/OmniRoute/pull/11531)) — thanks @pacocartones +- Electron release workflow: the `publish-npm` job now grants `actions: read` to the reusable `npm-publish.yml` it calls (its `publish` job requests it), which is what made GitHub refuse the whole v3.8.50 run at startup and ship the release with zero desktop assets; a `workflow_dispatch` now builds the requested tag instead of the dispatching branch and can skip the npm leg (`publish_npm=false`) when only re-attaching assets ([#11974](https://github.com/diegosouzapw/OmniRoute/pull/11974)) +- **fix(dashboard):** The Combos page usage guide now reads its dismissal through `useSyncExternalStore` instead of correcting SSR state inside an effect, removing an extra commit of the page tree on every load (and the `react-hooks/set-state-in-effect` error it raised). ([#12671](https://github.com/diegosouzapw/OmniRoute/pull/12671)) +- Electron release: `electron/package-lock.json` regained the optional `electron-builder-squirrel-windows` subtree (13 entries) that `npm ci` had been refusing as out of sync, `electron-release.yml` gained a `build_ref` dispatch input and stops regenerating release notes on a re-attach dispatch, and the npm publish workflow attaches the SBOM to the GitHub Release on dispatch publishes too — so the v3.8.51 tag ships every desktop asset and the SBOM like v3.8.49 did ([#12022](https://github.com/diegosouzapw/OmniRoute/pull/12022)) +- Drained the reds every PR against `release/v3.8.51` was born with: documented the three service-worker build-id variables, registered the six retirement/tunnel tests with the mutation gate, approved `eslint-plugin-react-hooks` in the dependency allowlist, added the six `combo.sort.*` strings to `vi` and `pt-BR`, pointed the ChatGPT Web doc at the real migration-168 test, worded the g4f hint around the member key, and realigned four tests to the retired-provider catalog and the legacy-schema fixtures the retirement migrations touch. ([#11940](https://github.com/diegosouzapw/OmniRoute/pull/11940)) +- **fix(providers):** Claude, Grok, LMArena, Notion, and Perplexity web-cookie transports now use pooled `wreq-js` 3.2 instead of the native sidecar, with all nine supported bindings pinned and audited, and the applicable platform binding plus native-license evidence included in each release artifact ([#12429](https://github.com/diegosouzapw/OmniRoute/pull/12429), supersedes [#11753](https://github.com/diegosouzapw/OmniRoute/pull/11753)). +- **fix(providers):** Zed Hosted streaming failures now trigger fallback before content and end partial streams with a sanitized structured error instead of fake assistant text and a normal-success stop. ([#12455](https://github.com/diegosouzapw/OmniRoute/pull/12455)) +- **fix(test):** capture hook result after render (direct commit `b85d0abb0b`) +- **fix(sync):** repair the two regressions the v3.8.50 sync-back left on release/v3.8.51 (direct commit `5ade9e0851`) +- **revert(models):** drop the memo reset hook ported from #10055 (direct commit `282de841ae`) +- **fix(typecheck):** guard the optional connectionId before the reactive model sync (direct commit `f2825bad75`) +- **fix(quota-share):** release the winner's reserved in-flight slot (#11371) ([#11408](https://github.com/diegosouzapw/OmniRoute/pull/11408)) — thanks @oyi77 +- **fix(providers):** stop silently dropping reasoning effort on opencode families (#10788) ([#11409](https://github.com/diegosouzapw/OmniRoute/pull/11409)) — thanks @oyi77 +- **fix(combo):** enable genuine quota-aware routing for generic providers (antigravity, claude, etc.) ([#11411](https://github.com/diegosouzapw/OmniRoute/pull/11411)) — thanks @HouMinXi +- **fix(oauth):** allow expired token retry before deactivation and activate expired retry loop (#P0) ([#11414](https://github.com/diegosouzapw/OmniRoute/pull/11414)) — thanks @HouMinXi +- **perf(executors):** lazy-load the executor registry — defer class imports + construction to first use (#11220) ([#11421](https://github.com/diegosouzapw/OmniRoute/pull/11421)) — thanks @oyi77 +- **fix:** make release/v3.8.50 CI gates green (run 32786966560 root causes) ([#11450](https://github.com/diegosouzapw/OmniRoute/pull/11450)) — thanks @jonlwheat2-gif +- **fix(sse):** exact-domain cookie match and origin-equality URL assertions ([#11452](https://github.com/diegosouzapw/OmniRoute/pull/11452)) — thanks @hartmark +- **fix(providers):** send Responses API payload during OpenAI-compatible validation ([#11454](https://github.com/diegosouzapw/OmniRoute/pull/11454)) — thanks @tuandinh0801 +- **fix(command-code):** fallback to /alpha/generate for Go plan without Provider API access ([#11455](https://github.com/diegosouzapw/OmniRoute/pull/11455)) — thanks @Chewji9875 +- **fix(responses-continuation):** fail closed on a log-truncated stored input/output array ([#11473](https://github.com/diegosouzapw/OmniRoute/pull/11473)) — thanks @hartmark +- **fix(cli):** write jcode settings as [providers.omniroute] in config.toml ([#11484](https://github.com/diegosouzapw/OmniRoute/pull/11484)) — thanks @hongnoul +- **fix(logging):** raise chat-log array truncation cap to 1000 and unify duplicate implementations ([#11499](https://github.com/diegosouzapw/OmniRoute/pull/11499)) — thanks @hartmark +- **fix:** restore live websocket URL helpers ([#11509](https://github.com/diegosouzapw/OmniRoute/pull/11509)) — thanks @PauloFH +- **perf(core):** eliminate async waterfalls across home, cache, and db operations (#11396) ([#11512](https://github.com/diegosouzapw/OmniRoute/pull/11512)) — thanks @HouMinXi +- **fix(kiro):** add runtime.us-east-1.kiro.dev as first-attempt endpoint ([#11517](https://github.com/diegosouzapw/OmniRoute/pull/11517)) — thanks @rifqiawl +- **fix(providers):** remove duplicate 128-tool truncation in OpencodeExecutor (#11444) ([#11518](https://github.com/diegosouzapw/OmniRoute/pull/11518)) — thanks @HouMinXi +- **fix(providers):** poll onboardUser LRO before classifying BYOP ([#11519](https://github.com/diegosouzapw/OmniRoute/pull/11519)) — thanks @rifqiawl +- **fix(translator):** preserve exact tool name casing for Claude tool_use payloads (#11487) ([#11520](https://github.com/diegosouzapw/OmniRoute/pull/11520)) — thanks @HouMinXi +- **fix(kimi-web):** align health probe with executor domain ([#11521](https://github.com/diegosouzapw/OmniRoute/pull/11521)) — thanks @pacocartones +- **fix(cli):** preserve existing admin password during setup ([#11522](https://github.com/diegosouzapw/OmniRoute/pull/11522)) — thanks @pacocartones +- **fix(mitm):** keep Windows DNS elevation off the POSIX sudo path (#11430) ([#11542](https://github.com/diegosouzapw/OmniRoute/pull/11542)) — thanks @official-burak +- **fix(sse):** scale chat admission by ingest byte budget instead of a fixed request count ([#11548](https://github.com/diegosouzapw/OmniRoute/pull/11548)) — thanks @TheDemonTuan +- **fix:** accept Gemini Web cookie exports ([#11554](https://github.com/diegosouzapw/OmniRoute/pull/11554)) — thanks @rafacpti23 +- **fix(i18n):** complete Brazilian Portuguese translations ([#11558](https://github.com/diegosouzapw/OmniRoute/pull/11558)) — thanks @rafacpti23 +- **fix(sse):** explicit types for openai-responses pureHelpers — clears last failing core typecheck gate ([#11567](https://github.com/diegosouzapw/OmniRoute/pull/11567)) — thanks @jonlwheat2-gif +- **fix(opencode):** hydrate Proxy Pool references for no-auth connections ([#11584](https://github.com/diegosouzapw/OmniRoute/pull/11584)) — thanks @AStupidBear +- **fix(ci):** resolve inherited release/v3.8.51 base-red CI failures ([#11608](https://github.com/diegosouzapw/OmniRoute/pull/11608)) — thanks @jonlwheat2-gif +- **fix(db):** fallback load STORAGE_ENCRYPTION_KEY from env files ([#11614](https://github.com/diegosouzapw/OmniRoute/pull/11614)) — thanks @benzntech +- **fix(providers):** mark Brave Search as no longer having a free tier ([#11621](https://github.com/diegosouzapw/OmniRoute/pull/11621)) — thanks @turbolego +- **fix:** pass tool result content through as string in OpenAI/Claude -> Gemini translation ([#11624](https://github.com/diegosouzapw/OmniRoute/pull/11624)) — thanks @Hsia97 +- **fix(sse):** scope credential health to exact key ([#11628](https://github.com/diegosouzapw/OmniRoute/pull/11628)) — thanks @KaspaPulse +- **fix(install):** guard prepare script against missing husky ([#11634](https://github.com/diegosouzapw/OmniRoute/pull/11634)) — thanks @Prajeeth-12 +- **fix(security):** redact NVIDIA_API_KEY at the log sink in the ad-hoc diag script ([#11639](https://github.com/diegosouzapw/OmniRoute/pull/11639)) +- **fix(types):** clear the two type regressions red on release/v3.8.51 ([#11640](https://github.com/diegosouzapw/OmniRoute/pull/11640)) — thanks @jonlwheat2-gif +- **fix(build):** resolve workspace: protocol dependencies to concrete semver versions during prepublish (#11540) ([#11641](https://github.com/diegosouzapw/OmniRoute/pull/11641)) — thanks @HouMinXi +- **fix(search):** properly resolve configured search connection in /v1/responses pipeline (#11524) ([#11642](https://github.com/diegosouzapw/OmniRoute/pull/11642)) — thanks @HouMinXi +- **fix(authz):** trust cf-connecting-ip for via-proxy detection when peer is Cloudflare edge (#11514) ([#11643](https://github.com/diegosouzapw/OmniRoute/pull/11643)) — thanks @HouMinXi +- **fix(security):** compare the OIDC state cookie in constant time ([#11646](https://github.com/diegosouzapw/OmniRoute/pull/11646)) +- **fix(security):** namespace the dedup hash by the calling API key ([#11649](https://github.com/diegosouzapw/OmniRoute/pull/11649)) +- **fix(models):** gate the Codex-native catalog loop on prefix mode (#11632) ([#11662](https://github.com/diegosouzapw/OmniRoute/pull/11662)) — thanks @xiaoyaner0201 +- **fix(dashboard):** project Codex routing quota in account pools ([#11666](https://github.com/diegosouzapw/OmniRoute/pull/11666)) — thanks @KaspaPulse +- **fix(ddgw):** normaliza system no upstream da release v3.8.51 ([#11692](https://github.com/diegosouzapw/OmniRoute/pull/11692)) — thanks @RhianB14 +- **fix(memory):** honor category filter in GET /api/memory ([#11699](https://github.com/diegosouzapw/OmniRoute/pull/11699)) — thanks @Sabeekhann +- **fix(ci):** detect stale workspace lockfile entries ([#11714](https://github.com/diegosouzapw/OmniRoute/pull/11714)) — thanks @pacocartones +- **fix(ci):** stamp dist/BUILD_SHA before the npm publish provenance gate ([#11721](https://github.com/diegosouzapw/OmniRoute/pull/11721)) +- **fix(docker):** decouple the best-effort Bun image from the release manifest ([#11724](https://github.com/diegosouzapw/OmniRoute/pull/11724)) +- **perf(compression):** avoid cloning memo misses twice ([#11727](https://github.com/diegosouzapw/OmniRoute/pull/11727)) — thanks @RaviTharuma +- **fix(docker):** let the bun digest artifact be absent, not fatal ([#11740](https://github.com/diegosouzapw/OmniRoute/pull/11740)) +- **fix(shared):** block cloud-metadata hosts under default remote-image guard ([#11755](https://github.com/diegosouzapw/OmniRoute/pull/11755)) — thanks @santosraju99-hub +- **fix(api):** preserve caller-provided X-Correlation-Id on chat completions ([#11760](https://github.com/diegosouzapw/OmniRoute/pull/11760)) — thanks @aniruddhaadak80 +- **fix(resilience):** lock permanently retired models instead of short backoff (Gemini ban prevention) ([#11762](https://github.com/diegosouzapw/OmniRoute/pull/11762)) — thanks @turbolego +- **fix:** mark Vercel AI Gateway as passthroughModels ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **fix(resilience):** stop hammering permanently-moved endpoints and billing-suspended accounts ([#11774](https://github.com/diegosouzapw/OmniRoute/pull/11774)) — thanks @turbolego +- **fix(lease):** remove global static reservation and gate routing on live active lease occupancy ([#11775](https://github.com/diegosouzapw/OmniRoute/pull/11775)) — thanks @TheDemonTuan +- **fix(ci):** size the install-upgrade gate to a measured run, and log the pack cost ([#11776](https://github.com/diegosouzapw/OmniRoute/pull/11776)) +- **fix(pwa):** stop serving the stale shell after deploys ([#11779](https://github.com/diegosouzapw/OmniRoute/pull/11779)) — thanks @HouMinXi +- **fix(combo):** lock GitHub models rejected as "not supported" for future requests ([#11781](https://github.com/diegosouzapw/OmniRoute/pull/11781)) — thanks @turbolego +- **fix(cli):** probe both IPv4 and IPv6 loopback for server readiness (#11766) ([#11794](https://github.com/diegosouzapw/OmniRoute/pull/11794)) — thanks @vermasomesh835 +- **fix(sse):** preserve CLIProxyAPI wrappers per connection ([#11797](https://github.com/diegosouzapw/OmniRoute/pull/11797)) — thanks @RaviTharuma +- **fix(providers):** honor model auto-fetch opt-in on create ([#11798](https://github.com/diegosouzapw/OmniRoute/pull/11798)) — thanks @RaviTharuma +- **fix(dashboard):** make model auto-fetch order independent ([#11805](https://github.com/diegosouzapw/OmniRoute/pull/11805)) — thanks @RaviTharuma +- **fix(docker):** make GHA cache exports best effort ([#11806](https://github.com/diegosouzapw/OmniRoute/pull/11806)) — thanks @RaviTharuma +- **fix(kiro):** do not permanently ban on 'User is not authorized to make this call' ([#11809](https://github.com/diegosouzapw/OmniRoute/pull/11809)) — thanks @Deftera186 +- **fix(sse):** keep cache-write tokens in OpenAI-shaped usage ([#11814](https://github.com/diegosouzapw/OmniRoute/pull/11814)) — thanks @TheDemonTuan +- **fix(docs):** resolve relative markdown and wiki links across Fumadocs and GitHub wiki ([#11834](https://github.com/diegosouzapw/OmniRoute/pull/11834)) — thanks @AndrianBalanescu +- **fix(chat):** preserve unstripped model string for passthrough provider routing ([#11840](https://github.com/diegosouzapw/OmniRoute/pull/11840)) — thanks @raheemuddin786 +- **fix(mcp):** avoid async initialization inside sync esm wrappers (#11569) ([#11849](https://github.com/diegosouzapw/OmniRoute/pull/11849)) — thanks @HouMinXi +- **fix(sse):** preserve coherent markdown token boundaries across stream translation chunks (#11606) ([#11851](https://github.com/diegosouzapw/OmniRoute/pull/11851)) — thanks @HouMinXi +- **fix(ci):** run the install-upgrade gate on disk, not on the /tmp tmpfs ([#11855](https://github.com/diegosouzapw/OmniRoute/pull/11855)) +- **fix(skills):** expand shorthand property types in injected tool schemas ([#11857](https://github.com/diegosouzapw/OmniRoute/pull/11857)) — thanks @alvinveroy +- **fix(ci):** publish npm from a hosted runner so provenance is accepted ([#11877](https://github.com/diegosouzapw/OmniRoute/pull/11877)) +- **fix(lint):** pin react hooks plugin to 7.0.1 ([#11886](https://github.com/diegosouzapw/OmniRoute/pull/11886)) +- **fix(ci):** surface and clear release-green ESLint failure ([#11890](https://github.com/diegosouzapw/OmniRoute/pull/11890)) — thanks @MumuTW +- **fix(guardrails):** prevent duplicate prompt-injection-guard log output ([#11936](https://github.com/diegosouzapw/OmniRoute/pull/11936)) — thanks @patrykkopycinski +- **fix(auth):** downgrade expected transient states from warn to debug ([#11937](https://github.com/diegosouzapw/OmniRoute/pull/11937)) — thanks @patrykkopycinski +- **fix(codex):** fail over image generation for imported free plans ([#11948](https://github.com/diegosouzapw/OmniRoute/pull/11948)) — thanks @Bl0ck154 +- **fix(codex):** normalize non-stream responses ([#11951](https://github.com/diegosouzapw/OmniRoute/pull/11951)) — thanks @Bl0ck154 +- **fix(images):** forward Antigravity image size ([#11952](https://github.com/diegosouzapw/OmniRoute/pull/11952)) — thanks @Bl0ck154 +- **fix(db):** invalidate connection cache after upsert ([#11953](https://github.com/diegosouzapw/OmniRoute/pull/11953)) — thanks @Bl0ck154 +- **fix(codex):** restore imported account state ([#11954](https://github.com/diegosouzapw/OmniRoute/pull/11954)) — thanks @Bl0ck154 +- **fix(sse):** give extended-thinking targets the reasoning readiness budget ([#11959](https://github.com/diegosouzapw/OmniRoute/pull/11959)) — thanks @abhisheksharma2411 +- **fix(dashboard):** use opaque background and readable text color on cost chart tooltips ([#11960](https://github.com/diegosouzapw/OmniRoute/pull/11960)) — thanks @ZaimMarzuki +- **fix(antigravity):** send complete loadCodeAssist metadata (ideType/platform/pluginType as numeric enums) ([#11969](https://github.com/diegosouzapw/OmniRoute/pull/11969)) — thanks @rifqiawl +- **fix(core):** resolve DB init race condition and reasoning translation ([#12003](https://github.com/diegosouzapw/OmniRoute/pull/12003)) — thanks @raheemuddin786 +- **fix(admission):** exclude reclaimable page cache from the cgroup pressure ratio ([#12017](https://github.com/diegosouzapw/OmniRoute/pull/12017)) — thanks @HouMinXi +- **fix(build):** prune non-production trees in NFT trace excludes and tsconfig ([#12028](https://github.com/diegosouzapw/OmniRoute/pull/12028)) — thanks @Chewji9875 +- **fix(migrations):** add renamed migration compatibility for 056/073/077/101 ([#12036](https://github.com/diegosouzapw/OmniRoute/pull/12036)) — thanks @oyi77 +- **fix(sse):** default crash-guard logger to console.warn, not console ([#12042](https://github.com/diegosouzapw/OmniRoute/pull/12042)) — thanks @HouMinXi +- **fix(catalog):** derive vision/modalities for built-in auto combos from effective target pool ([#12046](https://github.com/diegosouzapw/OmniRoute/pull/12046)) — thanks @Prajeeth-12 +- **fix(release):** the packaged-app smoke verifies the database opened, not a driver line the primary path never prints (twin of #12032) ([#12047](https://github.com/diegosouzapw/OmniRoute/pull/12047)) +- **fix(barrel):** migrate src/app/ off the @/lib/localDb barrel import (#11795 Phase 2) ([#12051](https://github.com/diegosouzapw/OmniRoute/pull/12051)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate src/lib/ off the localDb barrel to direct db imports (#59) ([#12052](https://github.com/diegosouzapw/OmniRoute/pull/12052)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate open-sse, src/shared, src/sse, src/models, src/domain off the @/lib/localDb barrel import (#11795 Phase 4) ([#12053](https://github.com/diegosouzapw/OmniRoute/pull/12053)) — thanks @jonlwheat2-gif +- **fix(barrel):** delete the @/lib/localDb barrel — every consumer migrated (#11795 Phase 5) ([#12055](https://github.com/diegosouzapw/OmniRoute/pull/12055)) — thanks @jonlwheat2-gif +- **fix(radar):** restore D12 public boundary ([#12057](https://github.com/diegosouzapw/OmniRoute/pull/12057)) +- **fix(dev):** bound webpack and Tailwind scans ([#12075](https://github.com/diegosouzapw/OmniRoute/pull/12075)) — thanks @backryun +- **fix(dev):** isolate root layout settings reads ([#12076](https://github.com/diegosouzapw/OmniRoute/pull/12076)) — thanks @backryun +- **fix(dev):** reduce instrumentation executor fan-out (phase 3) ([#12078](https://github.com/diegosouzapw/OmniRoute/pull/12078)) — thanks @backryun +- **fix(dev):** make logging resources HMR-singleton ([#12079](https://github.com/diegosouzapw/OmniRoute/pull/12079)) — thanks @backryun +- **fix(sse):** keep unavailable forced connections scoped ([#12080](https://github.com/diegosouzapw/OmniRoute/pull/12080)) — thanks @keeltrace +- **fix(dev):** isolate batch dispatch from instrumentation ([#12081](https://github.com/diegosouzapw/OmniRoute/pull/12081)) — thanks @backryun +- **fix(ci):** reconcile release test contract drift ([#12082](https://github.com/diegosouzapw/OmniRoute/pull/12082)) — thanks @backryun +- **fix(ci):** accept CVE-2025-68121 in the prebuilt tls-client .so, auto-close base-red issues, guard Scorecard on the default branch ([#12085](https://github.com/diegosouzapw/OmniRoute/pull/12085)) +- **fix(db):** drop three consumer-less 1proxy exports — dead-code base-red on release/v3.8.51 after the barrel deletion (#12055) ([#12087](https://github.com/diegosouzapw/OmniRoute/pull/12087)) +- **fix(api):** clear the six API-route TypeScript regressions the new gate landed red on ([#12094](https://github.com/diegosouzapw/OmniRoute/pull/12094)) +- **fix(release):** never let the tag-push Create Release append auto notes to the curated body ([#12096](https://github.com/diegosouzapw/OmniRoute/pull/12096)) +- **fix(sse):** honor CLIProxyAPI environment API key ([#12099](https://github.com/diegosouzapw/OmniRoute/pull/12099)) — thanks @RaviTharuma +- **fix(oauth):** bind Google refresh to the client that issued the token ([#12106](https://github.com/diegosouzapw/OmniRoute/pull/12106)) — thanks @HouMinXi +- **fix(dashboard):** make RequestLoggerDetail loadable outside Next — CSS via globals.css + CJS/ESM interop (#11703 base-reds) ([#12114](https://github.com/diegosouzapw/OmniRoute/pull/12114)) +- **fix(leases):** project status lease row to lease columns so joined connection PII never escapes ([#12115](https://github.com/diegosouzapw/OmniRoute/pull/12115)) — thanks @geek007git +- **fix(plugins):** do not kill the plugin process when a fire-and-forget hook times out ([#12116](https://github.com/diegosouzapw/OmniRoute/pull/12116)) — thanks @geek007git +- **fix(guardrails):** restore injection-guard logging on middleware-only routes ([#12117](https://github.com/diegosouzapw/OmniRoute/pull/12117)) — thanks @geek007git +- **fix(plugins):** refresh stored manifest from disk on activate so new hook fields reach existing installs ([#12120](https://github.com/diegosouzapw/OmniRoute/pull/12120)) — thanks @geek007git +- **fix(codex):** preserve existing provider state when bulk-import upserts a matching connection ([#12122](https://github.com/diegosouzapw/OmniRoute/pull/12122)) — thanks @geek007git +- **fix(ci):** clear the two base-reds the 2026-08-30 merge batch left on release/v3.8.51 (round 3) ([#12123](https://github.com/diegosouzapw/OmniRoute/pull/12123)) +- **fix(sse):** strip type:'custom' from Claude tools on agentrouter dispatch ([#12126](https://github.com/diegosouzapw/OmniRoute/pull/12126)) — thanks @wahidsadik371-coder +- **fix(sse):** stop advertising video providers the dispatcher cannot run ([#12131](https://github.com/diegosouzapw/OmniRoute/pull/12131)) — thanks @quiterunner-commits +- **fix(search):** treat HTTP 432 and plan limit errors as transient cooldown ([#12139](https://github.com/diegosouzapw/OmniRoute/pull/12139)) — thanks @HouMinXi +- **fix(sse):** refuse an AI Horde queue that cannot fit the request budget ([#12143](https://github.com/diegosouzapw/OmniRoute/pull/12143)) — thanks @quiterunner-commits +- **fix(ci):** clear the base-reds the afternoon merge batch left on release/v3.8.51 (round 5: provider count 352, TS2554/TS2677) ([#12144](https://github.com/diegosouzapw/OmniRoute/pull/12144)) +- **fix(translator):** strip neutral tool_choice when tools absent in Responses-to-Chat (#12141) ([#12166](https://github.com/diegosouzapw/OmniRoute/pull/12166)) — thanks @HouMinXi +- **fix(guardrails):** pass providerId to getResolvedModelCapabilities in checkComboVision (#12112) ([#12169](https://github.com/diegosouzapw/OmniRoute/pull/12169)) — thanks @HouMinXi +- **fix(translator):** strip plaintext reasoning content for opaque responses backends (#12128) ([#12171](https://github.com/diegosouzapw/OmniRoute/pull/12171)) — thanks @HouMinXi +- **perf(stream):** compile hot-path regexes once, bound token caches, fix quadratic buffering ([#12179](https://github.com/diegosouzapw/OmniRoute/pull/12179)) — thanks @opensource-elearning +- **fix:** resolve compression worker file using runtime anchors instead of… ([#12183](https://github.com/diegosouzapw/OmniRoute/pull/12183)) — thanks @NightStalker-87 +- **fix:** prevent Claude Code session kills via liveness-aware readiness + auto model echo ([#12189](https://github.com/diegosouzapw/OmniRoute/pull/12189)) — thanks @opensource-elearning +- **fix(translator):** preserve falsy primitive values in Gemini and Antigravity function response results ([#12191](https://github.com/diegosouzapw/OmniRoute/pull/12191)) — thanks @Siva010 +- **fix(oauth):** mark empty Antigravity projectId as degraded and clear stale errors (#11284) ([#12205](https://github.com/diegosouzapw/OmniRoute/pull/12205)) — thanks @HouMinXi +- **fix(providers):** gate the Codex auto-ping usage read on the shared quota throttle ([#12209](https://github.com/diegosouzapw/OmniRoute/pull/12209)) — thanks @abhisheksharma2411 +- **fix(combos):** prioritize SQLite row id over inner JSON id and notify delete errors ([#12213](https://github.com/diegosouzapw/OmniRoute/pull/12213)) — thanks @HouMinXi +- **fix(providers):** mark featherless.ai as no longer having a free tier (`hasFree: false`) ([#12216](https://github.com/diegosouzapw/OmniRoute/pull/12216)) — thanks @turbolego +- **fix(deepseek-web):** stop Turbopack dev panic in the PoW worker path resolver ([#12221](https://github.com/diegosouzapw/OmniRoute/pull/12221)) — thanks @hartmark +- **fix(chat-admission):** clarify local 503 source ([#12223](https://github.com/diegosouzapw/OmniRoute/pull/12223)) — thanks @keeltrace +- **fix(dev):** silence webpack runtime module warnings ([#12228](https://github.com/diegosouzapw/OmniRoute/pull/12228)) — thanks @backryun +- **fix(db):** use module.require for CommonJS runtime driver loading ([#12230](https://github.com/diegosouzapw/OmniRoute/pull/12230)) — thanks @raheemuddin786 +- **fix(memory):** honest probe-driven FTS5 keyword status + memory_id rowid sync ([#12231](https://github.com/diegosouzapw/OmniRoute/pull/12231)) — thanks @raheemuddin786 +- **fix(system):** propagate abort signal to stream reader in HTTP version checks ([#12232](https://github.com/diegosouzapw/OmniRoute/pull/12232)) — thanks @raheemuddin786 +- **fix(resilience):** isolate local host execution errors from provider circuit breakers ([#12233](https://github.com/diegosouzapw/OmniRoute/pull/12233)) — thanks @raheemuddin786 +- **fix(sse):** sort injected tools deterministically for prompt caching ([#12234](https://github.com/diegosouzapw/OmniRoute/pull/12234)) — thanks @raheemuddin786 +- **fix(combo):** always clear the loop-safety timer, not just on the happy path (#11804) ([#12245](https://github.com/diegosouzapw/OmniRoute/pull/12245)) +- **fix(quality):** base-red pair — stryker tap registration + turn-pin suites aligned to the window gate ([#12255](https://github.com/diegosouzapw/OmniRoute/pull/12255)) — thanks @backryun +- **fix(dev):** qualify Turbopack runtime boundaries ([#12258](https://github.com/diegosouzapw/OmniRoute/pull/12258)) — thanks @backryun +- **fix(combo):** fall back to full pool when collapsed sole survivor is context-too-small ([#12278](https://github.com/diegosouzapw/OmniRoute/pull/12278)) — thanks @opensource-elearning +- **fix(combo):** bound the pre-dispatch unavailable skip so a stale label cannot dark a pool (#12168) ([#12285](https://github.com/diegosouzapw/OmniRoute/pull/12285)) +- **fix(security):** strip Qwen/Alibaba console-session cookies from provider API responses ([#12287](https://github.com/diegosouzapw/OmniRoute/pull/12287)) +- **fix(usage):** console-aware Token Plan guidance + subscription hint on bailian 401 ([#12288](https://github.com/diegosouzapw/OmniRoute/pull/12288)) +- **fix(resourcePressure):** log numeric detail on every rejection, recover faster ([#12293](https://github.com/diegosouzapw/OmniRoute/pull/12293)) — thanks @hartmark +- **fix(release):** drain the 2026-09-01 base-red window — passthrough usage regression + radarPage i18n keys ([#12327](https://github.com/diegosouzapw/OmniRoute/pull/12327)) +- **fix(ci):** openapi-security-tiers checker must honor routeGuard patterns + imported prefixes ([#12350](https://github.com/diegosouzapw/OmniRoute/pull/12350)) — thanks @ggiak +- **perf(ci):** cache node_modules in the npm-ci-retry composite (#8084 D3) ([#12408](https://github.com/diegosouzapw/OmniRoute/pull/12408)) +- **revert(ui):** point CTAs back at their real destinations ([#12410](https://github.com/diegosouzapw/OmniRoute/pull/12410)) +- **fix(release):** drain the 2026-09-02 base-red — rerank-providers import + api-typecheck baseline ratchet ([#12414](https://github.com/diegosouzapw/OmniRoute/pull/12414)) +- **fix(types):** make system prompt injection noImplicitAny-safe ([#12416](https://github.com/diegosouzapw/OmniRoute/pull/12416)) +- **fix(quality):** validate typecheck baseline schema ([#12419](https://github.com/diegosouzapw/OmniRoute/pull/12419)) +- **fix(memory):** resolve rerank provider node cache import ([#12421](https://github.com/diegosouzapw/OmniRoute/pull/12421)) +- **fix(ci):** repair release-root regressions — pack dedup, web-session syntax, uc-image ids ([#12423](https://github.com/diegosouzapw/OmniRoute/pull/12423)) — thanks @backryun +- **fix(providers):** separate MaxAI and UC credential contracts ([#12431](https://github.com/diegosouzapw/OmniRoute/pull/12431)) +- **fix(cli):** preserve Claude settings on config set ([#12432](https://github.com/diegosouzapw/OmniRoute/pull/12432)) — thanks @KrzysiekSko +- **fix(providers):** repair the maxai credential block truncated by merge auto-resolve ([#12433](https://github.com/diegosouzapw/OmniRoute/pull/12433)) +- **fix(quality):** record the 2026-09-02 merged growth in the file-size baseline ([#12434](https://github.com/diegosouzapw/OmniRoute/pull/12434)) +- **fix(uc):** route UC error strings through sanitizeErrorMessage; allowlist the retired codex id ([#12437](https://github.com/diegosouzapw/OmniRoute/pull/12437)) +- **fix(adapta):** redact streamed upstream errors ([#12438](https://github.com/diegosouzapw/OmniRoute/pull/12438)) +- **fix(sse):** remap non-contiguous upstream tool_calls index to a gap-free output_index ([#12445](https://github.com/diegosouzapw/OmniRoute/pull/12445)) — thanks @hartmark +- **fix(quality):** detect duplicate tool_calls entries in one response ([#12446](https://github.com/diegosouzapw/OmniRoute/pull/12446)) — thanks @hartmark +- **fix(conversations):** resolve turn content OmniRoute never sends back to the client ([#12447](https://github.com/diegosouzapw/OmniRoute/pull/12447)) — thanks @hartmark +- **fix(combo):** do not treat credits-exhausted 401 as auth skip ([#12449](https://github.com/diegosouzapw/OmniRoute/pull/12449)) — thanks @RaviTharuma +- **fix(auth):** do not park healthy quota accounts as expired ([#12452](https://github.com/diegosouzapw/OmniRoute/pull/12452)) — thanks @RaviTharuma +- **fix(responses-continuation):** fail closed on a collector-truncated, empty output array ([#12460](https://github.com/diegosouzapw/OmniRoute/pull/12460)) — thanks @hartmark +- **fix(logging):** raise the SSE payload collector's default cap ([#12461](https://github.com/diegosouzapw/OmniRoute/pull/12461)) — thanks @hartmark +- **fix(resilience):** surface Responses failed.error.message in 502s ([#12472](https://github.com/diegosouzapw/OmniRoute/pull/12472)) — thanks @RaviTharuma +- **fix(security):** unbiased maxai X-Random nonce + stricter URL/regex assertions ([#12502](https://github.com/diegosouzapw/OmniRoute/pull/12502)) +- **fix(video):** re-anchor transcript log-redaction so PII/credential maskers can't reopen the leak (#12150 P1 follow-up) ([#12503](https://github.com/diegosouzapw/OmniRoute/pull/12503)) +- **fix(monitoring):** serve cached credentialHealth off the request path ([#12533](https://github.com/diegosouzapw/OmniRoute/pull/12533)) — thanks @RaviTharuma +- **fix(quota):** keep Antigravity Gemini usable when Claude weekly is empty ([#12566](https://github.com/diegosouzapw/OmniRoute/pull/12566)) — thanks @HouMinXi +- **fix(authz):** hard-gate every credential export and CLI-config write (GHSA-5926-2w35-7h4q) ([#12600](https://github.com/diegosouzapw/OmniRoute/pull/12600)) +- **fix(docs):** restore the Next build — REMOVED_PROVIDERS.md had no frontmatter (base-red #12581) ([#12610](https://github.com/diegosouzapw/OmniRoute/pull/12610)) +- **fix(quality):** re-point the zcodeProtocol public-creds allowlist to line 313 ([#12615](https://github.com/diegosouzapw/OmniRoute/pull/12615)) +- **fix(security):** close 3 advisories — search baseUrl exfil, sk- in the error sanitizer, bifrost relay header leak ([#12620](https://github.com/diegosouzapw/OmniRoute/pull/12620)) +- **fix(providers):** sanitize boolean required and nested bare maps for Gemini (#12269) ([#12624](https://github.com/diegosouzapw/OmniRoute/pull/12624)) — thanks @HouMinXi +- **fix(resilience):** keep Overloaded STREAM_EARLY_EOF off the provider breaker ([#12626](https://github.com/diegosouzapw/OmniRoute/pull/12626)) — thanks @HouMinXi +- **fix(catalog):** re-audit free-tier quotas against official pages ([#12649](https://github.com/diegosouzapw/OmniRoute/pull/12649)) +- **fix(dashboard):** keep a request's pending-tracking id stable across combo target retries ([#12650](https://github.com/diegosouzapw/OmniRoute/pull/12650)) — thanks @hartmark +- **fix(tests):** retire dead model ids from the chat-pipeline integration suite (base-red #12581) ([#12670](https://github.com/diegosouzapw/OmniRoute/pull/12670)) +- **fix:** resolve SqliteError no such table compression_run_telemetry during cleanup ([#12682](https://github.com/diegosouzapw/OmniRoute/pull/12682)) — thanks @groovecityJO +- **fix(combo):** treat a pin-only step as implicit connection allowlist ([#12697](https://github.com/diegosouzapw/OmniRoute/pull/12697)) — thanks @HouMinXi +- **fix(ci):** ship .npmrc in published package so legacy-peer-deps applies to consumers (#11544) ([#12699](https://github.com/diegosouzapw/OmniRoute/pull/12699)) — thanks @KooshaPari +- **fix(docker):** pin 4 CLI tools to exact versions (#12576) ([#12703](https://github.com/diegosouzapw/OmniRoute/pull/12703)) — thanks @KooshaPari +- **fix(docker):** document and harden cli profile trust boundary (#12570) ([#12706](https://github.com/diegosouzapw/OmniRoute/pull/12706)) — thanks @KooshaPari +- **fix(sse):** redact video transcript in pre-guardrail rejected-request logs (#12150 P2 item 7) ([#12710](https://github.com/diegosouzapw/OmniRoute/pull/12710)) +- **fix(i18n):** quote placeholder in OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES description (#12505) ([#12769](https://github.com/diegosouzapw/OmniRoute/pull/12769)) — thanks @KooshaPari +- **fix(api/pricing):** surface validation error message as string, not raw object (#12494) ([#12771](https://github.com/diegosouzapw/OmniRoute/pull/12771)) — thanks @KooshaPari +- **fix(sse):** re-enable prompt compression for native Codex passthrough ([#12834](https://github.com/diegosouzapw/OmniRoute/pull/12834)) — thanks @thomasmaerz +- **fix(dashboard):** model health tests for a provider node set to the Responses API now call `/v1/responses` with a Responses-shaped body instead of `/v1/chat/completions` — those models were reported as `Provider returned HTTP 200 but no text content` even though the same model answered normally through `/v1/responses` ([#13070](https://github.com/diegosouzapw/OmniRoute/issues/13070)) ([#13087](https://github.com/diegosouzapw/OmniRoute/pull/13087)) — thanks @ntdatt812 +- fix(routing): stop a round-robin combo's "opencode" targets from collapsing onto the opencode-zen connection (#11912) ([#13283](https://github.com/diegosouzapw/OmniRoute/pull/13283)) +- fix(routing): stop the reactive-compaction debug log from lying when compression is globally disabled (#11977) ([#13276](https://github.com/diegosouzapw/OmniRoute/pull/13276)) +- fix(electron): relativize standalone-bundle symlink targets so Stage 8 manifest verification stops failing on Windows (#11979) ([#13251](https://github.com/diegosouzapw/OmniRoute/pull/13251)) +- fix(dashboard): surface a visible error when Compression Studio's combined preview run fails (#12061) ([#13288](https://github.com/diegosouzapw/OmniRoute/pull/13288)) +- fix(dashboard): make the compression "Effective pipeline" preview honor the active profile and warn when the master switch is off (#12063) ([#13287](https://github.com/diegosouzapw/OmniRoute/pull/13287)) +- fix(providers): scope TinyCMS Web signer's DOM shims to each call instead of leaking them for the process lifetime, and surface a clean HTTP status on a non-JSON interception-toggles error (#12072) ([#13254](https://github.com/diegosouzapw/OmniRoute/pull/13254)) +- fix(guardrails): stop Vision Bridge from re-selecting a model locked after a 404 (#12111) ([#13259](https://github.com/diegosouzapw/OmniRoute/pull/13259)) +- fix(sse): require Responses-shaped body before native OpenAI-compatible passthrough (#12129) ([#13278](https://github.com/diegosouzapw/OmniRoute/pull/13278)) +- fix(providers): minimax-m3 now collapses manual thinking.type:"enabled" to adaptive, preventing upstream 400 (2013) (#12132) ([#13249](https://github.com/diegosouzapw/OmniRoute/pull/13249)) +- fix(db): scope model visibility overrides by modality so hiding a Chat model no longer hides an identically-ID'd Image/Embeddings/etc. model (#12172) ([#13258](https://github.com/diegosouzapw/OmniRoute/pull/13258)) +- fix(db): scope local-provider apiKey dedup to matching base URL so LM Studio/Ollama-style connections support multiple accounts (#12173) ([#13250](https://github.com/diegosouzapw/OmniRoute/pull/13250)) +- fix(providers): refresh Trae's stale Referer/Origin and forward user timezone so imported connections stop failing with 401 (#12190) ([#13255](https://github.com/diegosouzapw/OmniRoute/pull/13255)) +- fix(providers): route opencode-go/gpt-5.6-luna to /responses instead of /chat/completions (#12196) ([#13275](https://github.com/diegosouzapw/OmniRoute/pull/13275)) +- fix(dashboard): allow deleting the last extra-upstream-header row even when invalid (#12251) ([#13252](https://github.com/diegosouzapw/OmniRoute/pull/13252)) +- **fix(i18n):** translate pre-existing `__MISSING__:` keys for `combo.sort`, `requestLogger.detail` expand/collapse, `common.profile`, and `settings.resilienceCredentialHealth*` across 39 locales ([#12272](https://github.com/diegosouzapw/OmniRoute/issues/12272)) ([#13034](https://github.com/diegosouzapw/OmniRoute/pull/13034)) — thanks @HouMinXi +- fix(cli): run the Node.js runtime compatibility guard before the heavy `tsx/esm` + Commander import chain so an unsupported runtime gets a clear message instead of a raw `Invalid regular expression flags` crash (#12296) ([#13253](https://github.com/diegosouzapw/OmniRoute/pull/13253)) +- fix(dashboard): refresh the providers list after deleting a compatible provider node (#12298) ([#13256](https://github.com/diegosouzapw/OmniRoute/pull/13256)) +- fix(usage): fail closed on API-key budget enforcement when a provider's `auto` routing alias has no pricing row, instead of silently counting it as $0 (#12341) ([#13257](https://github.com/diegosouzapw/OmniRoute/pull/13257)) +- **fix(providers):** include the submitted Agnes video model when polling by `video_id` ([#12356](https://github.com/diegosouzapw/OmniRoute/pull/12356)) — thanks @pacocartones +- **fix(routing):** custom OpenAI-compatible nodes now honor the saved Chat/Responses API type after edits instead of letting the node's original ID prefix override the live connection setting ([#11884](https://github.com/diegosouzapw/OmniRoute/issues/11884)). ([#12358](https://github.com/diegosouzapw/OmniRoute/pull/12358)) — thanks @pacocartones +- fix(sse): surface an error instead of a silent empty 200 when a Claude stream closes with zero bytes (#12398) ([#13285](https://github.com/diegosouzapw/OmniRoute/pull/13285)) +- fix(oauth): warn before the dead localhost:8080 redirect in antigravity/gemini `oauth start` (#12413) ([#13265](https://github.com/diegosouzapw/OmniRoute/pull/13265)) +- fix(providers): stop devin-cli spawn error from double-closing the SSE controller (#12517) ([#13261](https://github.com/diegosouzapw/OmniRoute/pull/13261)) +- **fix(rerank):** clamp Voyage `top_k` to the documents actually sent after empty-string filtering, and honor `return_documents: false` in the NVIDIA response adapter (#12523 — thanks @pacocartones) +- **fix(audio):** `/v1/audio/translations` now resolves combo names the way `/v1/audio/transcriptions` already does, so a combo that `GET /v1/models` advertises is fanned out to its targets instead of being rejected with `400 Invalid translation model: . Use format: provider/model`; literal `provider/model` ids and unknown bare names behave as before (#12536 — thanks @pacocartones) +- **fix(gemini):** strip the JSON-Schema-2020-12 `prefixItems` keyword from Gemini tool schemas at every nesting level, so Claude Code tool definitions no longer fail with `400 Unknown name "prefixItems"` on Gemini models (#12540 — thanks @pacocartones) +- **fix(api-manager):** Expose an accessible loading status while API keys are fetched instead of an empty accessibility tree (#12541 — thanks @pacocartones) +- **fix(video):** Clamp `estimateJpegFrameBytes` at zero for padding-only payloads and build the three encode-side frame data URIs from `JPEG_FRAME_DATA_URI_PREFIX` instead of a repeated literal (#12543 — thanks @pacocartones) +- **fix(devin):** accept Windows `DEVIN_AGENTIC_HOME` sandbox paths (`C:\...\.sandbox\...`) in the isolated-home check so the Devin Claude Bridge no longer fails closed on Windows ([#12405](https://github.com/diegosouzapw/OmniRoute/issues/12405)) (#12545 — thanks @pacocartones) +- **fix(db):** Add `conversation_turn_nodes` and orphaned `agentic_conversations` to the auto-cleanup cycle under the existing `retention.callLogs` window, so identity nodes whose call-log content has already been purged no longer accumulate without bound in `storage.sqlite` (#12548 — thanks @pacocartones) +- **fix(i18n):** Wrap the `~/.claude/profiles//settings.json` placeholder in ICU single quotes in the `OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES` feature-flag description across all 42 locales and the TypeScript default, so next-intl no longer fails with `INVALID_MESSAGE: UNCLOSED_TAG` and the Feature Flags card shows the description instead of the raw key (#12549 — thanks @pacocartones) +- **fix(orchestration):** `updateCloudAgentTask` now publishes the task's real `status` on `agent.task.updated` when an update only touches `result`, `activities` or `error`, instead of the fabricated `"updated"` state, and stays silent when no row matched the id (#12550 — thanks @pacocartones) +- **fix(i18n):** the home "Recent Requests" panel and the Provider Topology legend are now translated instead of rendering English copies on non-English dashboards; the legend reads its own `home.topologyLegend*` labels with consistent casing rather than borrowing the memory-settings "Recent" and analytics "Error" strings (#12551 — thanks @pacocartones). +- **docs(reference):** bring the `FEATURE_FLAGS.md` catalog back to 1:1 with `featureFlagDefinitions.ts` — 20 missing flags added, the two `*_BLOCK_THRESHOLD` env-only knobs moved out of the flag tables, category/total counts and the Live WS port corrected, guarded by a static test (#12552 — thanks @pacocartones) +- **fix(i18n):** backfill missing `usage.kiloPass*` strings in 39 locales and restore `featureFlagOmnirouteDisableThinkingLevelVariantsDescription` in `pt.json` ([#12561](https://github.com/diegosouzapw/OmniRoute/issues/12561)) ([#13027](https://github.com/diegosouzapw/OmniRoute/pull/13027)) — thanks @HouMinXi +- fix(docker): default docker-compose app ports (dashboard/API/live-WS) to loopback instead of `0.0.0.0`, closing the anonymous `/v1` LAN/WAN exposure gap left open by `REQUIRE_API_KEY=false` (#12568) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): close DNS-rebinding SSRF gap in webhook outbound-URL guard (#12569) ([#13243](https://github.com/diegosouzapw/OmniRoute/pull/13243)) +- fix(docker): require a per-session token on the VNC browser CDP bridge and isolate it on a dedicated Docker network (#12571) ([#13241](https://github.com/diegosouzapw/OmniRoute/pull/13241)) +- fix(open-sse): write Adobe Firefly session tokens and cookie jars with 0700/0600 permissions instead of the process umask (#12572) ([#13242](https://github.com/diegosouzapw/OmniRoute/pull/13242)) +- fix(api): remove hardcoded wildcard CORS in openai-to-gemini-sse.ts so the centralized fail-closed CORS gate is the sole source of `Access-Control-Allow-Origin` (#12573) ([#13239](https://github.com/diegosouzapw/OmniRoute/pull/13239)) +- fix(api): enforce API key policy (budget/rate-limit/schedule/endpoint scoping) on the ElevenLabs speech-to-text, text-to-speech and voices proxy routes (#12574) ([#13244](https://github.com/diegosouzapw/OmniRoute/pull/13244)) +- fix(sse): cap HuggingChat NDJSON body size and bound the read loop with the fetch timeout so a stalled or hostile upstream cannot buffer unbounded memory (#12577) ([#13282](https://github.com/diegosouzapw/OmniRoute/pull/13282)) +- fix(docker): scope the cliproxyapi/qdrant/bifrost sidecars to loopback by default and forward `CLIPROXYAPI_MANAGEMENT_KEY` into the cliproxyapi container so its management API is not left both unauthenticated and LAN-published (#12578) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): create DB export temp paths with `fs.mkdtempSync` instead of predictable timestamps (#12579) ([#13237](https://github.com/diegosouzapw/OmniRoute/pull/13237)) +- Cline 401 bodies that say "re-authenticate your Cline account" classify as a refreshable OAuth token, not a terminal expired key. The cooling panel no longer labels every cooldown as a 429; it shows the recorded last error instead. (#12594) ([#13060](https://github.com/diegosouzapw/OmniRoute/pull/13060)) — thanks @HouMinXi +- **fix(catalog):** degrade unknown combo targets instead of dropping LCD modalities, and persist OpenRouter `architecture.input_modalities` into the capability snapshot ([#12613](https://github.com/diegosouzapw/OmniRoute/issues/12613)) ([#13042](https://github.com/diegosouzapw/OmniRoute/pull/13042)) — thanks @HouMinXi +- fix(providers): send `x-api-key` instead of `Authorization: Bearer` for OpenCode Zen's `/v1/responses` endpoint (Muse Spark Contributor models), fixing a 401 on OmniRoute's auth header (#12633) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(sse): surface the actionable "Auggie CLI not found" message when the shell reports a missing `auggie` binary via exit code instead of a spawn error (#12645) ([#13263](https://github.com/diegosouzapw/OmniRoute/pull/13263)) +- **fix(config):** Persist deletions of built-in background-degradation entries — when a stored settings record exists its `degradationMap` is now authoritative instead of being merged under the defaults, so an entry the user removed in the dashboard no longer reappears on the next apply or restart ([#12424](https://github.com/diegosouzapw/OmniRoute/issues/12424)) ([#12647](https://github.com/diegosouzapw/OmniRoute/pull/12647)) — thanks @pacocartones +- **fix(gamification):** action-count badge milestones (First Token, Token Consumer, Token Machine, Token Whale, and the token-sharing tier) are now backed by a durable `xp_action_counts` counter incremented in `addXp()`, instead of a live `COUNT(*)` over `xp_audit_log`. The audit log is pruned by `retention.xpAuditLog` (default 30 days), so on a default install those "lifetime" milestones were really "actions in the last 30 days" and unlocked badges could stop unlocking once old rows aged out. `getActionCount()` and `checkActionCountBadges()` now read the same durable source, and a migration backfills existing totals from the surviving audit rows ([#12546](https://github.com/diegosouzapw/OmniRoute/issues/12546)) ([#12651](https://github.com/diegosouzapw/OmniRoute/pull/12651)) — thanks @pacocartones +- **fix(images):** `/v1/images/edits` now iterates a combo's targets the same way `/v1/images/generations` does (#9239) instead of flattening a bare combo to its first target. A combo whose first target is not edit-capable — or lacks credentials — now falls through to a later edit-capable target rather than hard-erroring, and missing credentials are skipped (not a hard `401`) to match the generations path. The per-target skip/terminal classification is extracted into a shared `runImageComboTargets` loop, so generations behavior is unchanged ([#12547](https://github.com/diegosouzapw/OmniRoute/issues/12547)). ([#12653](https://github.com/diegosouzapw/OmniRoute/pull/12653)) — thanks @pacocartones +- fix(sse): add first-byte watchdog to the TLS-fingerprint transport so a stalled wreq body falls back instead of hanging for minutes (#12656) ([#13272](https://github.com/diegosouzapw/OmniRoute/pull/13272)) +- fix(sse): exempt tiny-budget reasoning probes from combo quality failure and surface persisted-cooldown skips in ALL_TARGETS_SKIPPED diagnostics (#12659) ([#13269](https://github.com/diegosouzapw/OmniRoute/pull/13269)) +- fix(models): declare the real ~1M contextLength for OpenCode Zen's Muse Spark 1.2 models instead of falling back to the 200000 provider default (#12681) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(oauth): align codebuddy-cn OAuth User-Agent with the chat/usage CLI version to avoid WAF false positives (#12702) ([#13264](https://github.com/diegosouzapw/OmniRoute/pull/13264)) +- fix(dashboard): surface an authentication-required banner instead of silently blanking database settings for a guest session (#12709) ([#13270](https://github.com/diegosouzapw/OmniRoute/pull/13270)) +- **fix(resilience):** stop unbounded queue that hangs 6min until Aborted — gate, provider slot, and Bottleneck queue now share a per-connection `maxWaitMs` budget; `fail-closed` on exhaust (503); execution backstop `executionMaxWaitMs` overridable per connection with upstream clamp ([#12715](https://github.com/diegosouzapw/OmniRoute/pull/12715)) — thanks @maxmad64bis (with thanks to @Tushar49 for surfacing the slow-provider need in #12635) +- **fix(auto-combo):** every mode pack now carries `quality` and `reliability` weights (`quality` 0.02, 0.03 in `quality-first`; `reliability` 0.03, 0.04 in `reliability-first`), so selecting a pack no longer silences either signal; `DEFAULT_WEIGHTS` is unchanged ([#12731](https://github.com/diegosouzapw/OmniRoute/pull/12731)) — thanks @maxmad64bis +- **fix(sse):** the provider execution pipeline keeps the upstream error `code`/`type` again — both were lost when the non-streaming leg was extracted, so a config-class `422` (Antigravity missing project) degraded into a generic account cooldown instead of reaching the gate that recognises it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(db):** a legacy `call_logs` table no longer aborts startup — the provider-stats index is created after the column healing runs, not before it, so an install predating `request_type` opens instead of failing with `no such column` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(catalog):** a malformed row in the operator-writable custom-models blob no longer kills every `auto/*` pool ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(tests):** realign the guards that the same day's merges left asserting the old behavior — the provider-count total after GreenPT and EURouter, the GLM stream buffer slot that `#12925` turned into a declared parameter, and the injection scan bound that `#13104` reshaped from a head-only window into head-plus-tail ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- fix(cache): fold tool_choice/tools/response_format into the semantic cache signature so a cached tool_calls response can no longer be replayed for a request whose tool policy forbids it (#12734) ([#13267](https://github.com/diegosouzapw/OmniRoute/pull/13267)) +- **fix(models):** stop treating provider-supplied `isFree:true`/`:free`/`0/0` flags as trusted unless the provider has a documented free tier — only the shipped free-tier catalog decides otherwise; fetched `isFree:true` trusted only for a free-tier provider, custom `isFree:true` via a trusted path ([#12744](https://github.com/diegosouzapw/OmniRoute/pull/12744)) — thanks @maxmad64bis +- fix(memory): authenticate the internal /v1/rerank loopback call so memory reranking no longer silently degrades to unranked order when REQUIRE_API_KEY=true (#12745) ([#13268](https://github.com/diegosouzapw/OmniRoute/pull/13268)) +- fix(sse): parse Ollama Cloud's current usage markup (`$X of $Y used` aria-label, nested width style) (#12749) ([#13260](https://github.com/diegosouzapw/OmniRoute/pull/13260)) +- fix(cli): setup-opencode no longer sends an active context's management token to `/v1/models` when `--api-key`/`OMNIROUTE_API_KEY` is supplied — an explicit flag or the env var now always outranks the context's token, and the flag itself is no longer swallowed by the parent program's global `--api-key` option (#12783) ([#13246](https://github.com/diegosouzapw/OmniRoute/pull/13246)) +- fix(sse): register Arcee AI in the executor provider registry so requests reach api.arcee.ai instead of silently falling back to OpenAI (#12784) ([#13277](https://github.com/diegosouzapw/OmniRoute/pull/13277)) +- **fix(quota):** share one upstream quota read when identical saturation checks arrive at the same time, so traffic bursts don't multiply provider API calls ([#12787](https://github.com/diegosouzapw/OmniRoute/pull/12787)) — thanks @maxmad64bis +- **fix(db):** ignore expired or invalid rate-limit cooldown writes so a stale timestamp can't lock a connection that should be usable — clearing still works as before ([#12788](https://github.com/diegosouzapw/OmniRoute/pull/12788)) — thanks @maxmad64bis +- **fix(providers):** single source for provider order with `xao` ranking alongside `xai-oauth` ([#12790](https://github.com/diegosouzapw/OmniRoute/pull/12790)) — thanks @maxmad64bis +- **fix(routing):** off-table models check the free-model catalog before inheriting premium prices, latency bootstraps from the observed pool median, async tiers read live database pricing with a 90-day freshness gate, and the tier cache invalidates on every pricing write ([#12792](https://github.com/diegosouzapw/OmniRoute/pull/12792)) — thanks @maxmad64bis +- **fix(auto-combo):** snapshot scoring uses the observed per-connection account tier and per-model quality instead of neutral constants, and circuit-open providers rank lower at build time ([#12794](https://github.com/diegosouzapw/OmniRoute/pull/12794)) — thanks @maxmad64bis +- **fix(pool):** empty `auto/*` pools now say why they are empty, and the models listing uses the same paid check as routing ([#12795](https://github.com/diegosouzapw/OmniRoute/pull/12795)) — thanks @maxmad64bis +- fix(routing): recognize CLIProxyAPI's 'unknown provider for model' 400 as fallback-worthy (#12800) ([#13284](https://github.com/diegosouzapw/OmniRoute/pull/13284)) +- **fix(sse):** translate-mode streams now emit the estimated token counts as a trailing usage-only chunk before `[DONE]` when the upstream stays silent, so metered chat clients see totals instead of nothing ([#12828](https://github.com/diegosouzapw/OmniRoute/pull/12828)) — thanks @maxmad64bis +- **fix(db):** provider stats stay truthful on empty and legacy databases: fallback counts default to `0` instead of `null`, latency averages read `null` (not `0`) when no durations were recorded, failures logged before the error-type column existed group under `pre_migration` instead of `unclassified`, and per-provider queries use two new composite indexes ([#12832](https://github.com/diegosouzapw/OmniRoute/pull/12832)) — thanks @maxmad64bis +- fix(nvidia): fail open when a synced model catalog goes stale instead of gating forever (#12849) ([#13248](https://github.com/diegosouzapw/OmniRoute/pull/13248)) +- **fix(db):** WAL maintenance lives in its own module: a `TRUNCATE` checkpoint that hits a busy database now warns with its streak and retries once via `PASSIVE` instead of logging success, and closing-time checkpoints no longer report success on builds without a database file, with the busy totals visible in the authenticated monitoring health payload ([#12853](https://github.com/diegosouzapw/OmniRoute/pull/12853)) — thanks @maxmad64bis +- **fix(health):** quota with no snapshots reads empty instead of a contradicting 0% ([#12857](https://github.com/diegosouzapw/OmniRoute/pull/12857)) — thanks @maxmad64bis +- fix(a2a): accept the dashboard's own session cookie on /a2a so "Run message/send" no longer fails with "Unauthorized: missing or invalid API key" (#12888) ([#13271](https://github.com/diegosouzapw/OmniRoute/pull/13271)) +- **fix(logs):** the Logs grid's in-memory filter pass no longer discards rows the SQL query already matched — selecting an API key from the dropdown (which sends the key's id) returns its calls again, the Combo tab shows every combo instead of only those whose name contains a "1", and the model filter and search cover the same columns as the query ([#12896](https://github.com/diegosouzapw/OmniRoute/pull/12896)) — fixes [#12873](https://github.com/diegosouzapw/OmniRoute/issues/12873) — thanks @ntdatt812 +- **fix(a2a):** `/api/a2a/status` now builds the agent card from the request that asked for it, so a gateway reached at a non-localhost host no longer advertises `http://localhost:20128` as its A2A URL ([#12918](https://github.com/diegosouzapw/OmniRoute/pull/12918)). — thanks @ntdatt812 +- **fix(compression):** progressive aging now appends its `[COMPRESSED:aging:…]` annotation after a turn's `tool_result` blocks instead of in front of them, so Anthropic no longer rejects aged conversations with "`tool_use` ids were found without `tool_result` blocks immediately after" ([#12920](https://github.com/diegosouzapw/OmniRoute/pull/12920)). — thanks @ntdatt812 +- **fix(bedrock):** model import now resolves context limits for every vendor prefix instead of only `anthropic.*`, so `global.openai.gpt-5.6-*` no longer imports with a null `inputTokenLimit` and gets rejected pre-flight at the 200k default ([#12921](https://github.com/diegosouzapw/OmniRoute/pull/12921)). — thanks @ntdatt812 +- **fix(stream):** the 64 KB stream buffer GLM asks for is honoured instead of dropped, and the type error it caused no longer fails the API Route Typecheck gate on every open PR ([#12925](https://github.com/diegosouzapw/OmniRoute/pull/12925)) — thanks @ntdatt812 +- **fix(guardrails):** mask PII inside a `tool_result`'s nested content array, which the masker walked past while redacting its sibling block ([#12930](https://github.com/diegosouzapw/OmniRoute/pull/12930)) — thanks @ntdatt812 +- **fix(dashboard):** the Radar catalog table no longer leaves absent data unexplained — empty limits, unknown context windows, and unreported capabilities each explain themselves on hover, and a new check keeps it that way ([#12937](https://github.com/diegosouzapw/OmniRoute/pull/12937)) — thanks @maxmad64bis +- **fix(sse):** geo-blocked opencode requests rotate to the next account proxy instead of failing, so one refused egress no longer aborts the whole chain ([#12941](https://github.com/diegosouzapw/OmniRoute/pull/12941)) — thanks @maxmad64bis +- **fix(codex):** dashboard "clear cooldown" and the CAS recovery path now drop nested `codexScopeRateLimitedUntil` maps in the same write that nulls `rate_limited_until`, and fresh quota snapshots with headroom lift fallback-sourced scope cooldowns parked by quota preflight ([#12817](https://github.com/diegosouzapw/OmniRoute/issues/12817), [#12860](https://github.com/diegosouzapw/OmniRoute/issues/12860), [#12951](https://github.com/diegosouzapw/OmniRoute/pull/12951)) The dashboard reset-credit button (`consumeCodexResetCredit`) now exercises that same snapshot path after a successful redeem, so a filled quota bar is enough to unpark a leftover fallback Codex child without another manual clear. — thanks @HouMinXi +- **fix(db):** resolve `sql-wasm.wasm` across global npm install and hoisted layouts, ensuring OmniRoute can boot cleanly on Node 24 when native `better-sqlite3` is uncompiled. ([#13035](https://github.com/diegosouzapw/OmniRoute/pull/13035)) — thanks @HouMinXi +- **fix(api):** `DELETE /v1/batches/delete-completed` now sweeps only the calling API key's own completed batches (batches with no owner stay out of a key-scoped sweep on purpose), with an explicit instance-wide mode reserved for authenticated dashboard sessions, a 401 for a presented key that is unknown, revoked, deactivated, banned or expired (never falling through to the session branch), audit logging of every sweep, a sanitized 500 on failure, an owner-scoped file half (a key-scoped sweep never nulls a file another tenant owns) and an atomic sweep — chunked in 200-batch transactions in instance mode — so a mid-way error never leaves a batch pointing at a nulled file (GHSA-wvxc-jp3v-5mg5) ([#12969](https://github.com/diegosouzapw/OmniRoute/pull/12969)) +- **fix(combo):** quota-weighted routing stops drawing on an out-of-credit connection — a 402 now invalidates the stored quota snapshot instead of leaving its stale remaining percentage in place, and a snapshot older than 10 minutes no longer counts as confident headroom for the primary pool ([#12972](https://github.com/diegosouzapw/OmniRoute/pull/12972)) ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- **fix(vision):** Custom Models with "Vision capable" checked no longer have image requests swapped to `glm/glm-4.6v` when the client sends the advertised alias (`vllm/path/...`) or the bare path-shaped id — Vision Bridge now matches the stored override for all three id forms ([#12758](https://github.com/diegosouzapw/OmniRoute/issues/12758)) ([#12974](https://github.com/diegosouzapw/OmniRoute/pull/12974)) — thanks @HouMinXi +- **fix(sse):** transient opencode upstream failures rotate to the next account proxy instead of failing, so one flapping egress no longer aborts the whole chain ([#12975](https://github.com/diegosouzapw/OmniRoute/pull/12975)) — thanks @maxmad64bis +- **fix(azure):** Deployments from GPT-6 onward now send `max_completion_tokens` instead of `max_tokens`, which Azure rejects with HTTP 400. The rule matched a literal `gpt-5`, so each new generation arrived broken; it now matches the generation range, while `gpt-35-turbo` still keeps `max_tokens`. ([#13007](https://github.com/diegosouzapw/OmniRoute/pull/13007)) — thanks @ntdatt812 +- **fix(db):** add an opt-in self-restart circuit for sustained critical memory pressure, gate post-cleanup VACUUM behind a minimum freed-rows threshold, and checkpoint the SQLite WAL every 5 minutes with a size guard that escalates to TRUNCATE, so a growing WAL can no longer stall the event loop into a full outage. ([#13011](https://github.com/diegosouzapw/OmniRoute/pull/13011)) — thanks @HouMinXi +- **fix(auth):** an explicit connection pin may probe a quota-disabled row once and re-enable it on success ([#12874](https://github.com/diegosouzapw/OmniRoute/issues/12874)) ([#13017](https://github.com/diegosouzapw/OmniRoute/pull/13017)) — thanks @HouMinXi +- **fix(api):** thread `X-OmniRoute-Fallback-Attempts` through combo chat completions so streaming and non-streaming responses report how many prior legs were attempted ([#13038](https://github.com/diegosouzapw/OmniRoute/pull/13038)) — thanks @HouMinXi +- Restore provider failure classification and credential refresh on non-streaming requests: classify non-2xx failures to lock models on per-model quota exhaustion, update connection rate limits from headers and body, and pass credential refresh handlers to pipeline execution so 401 tokens can be refreshed and retried (#13043). ([#13069](https://github.com/diegosouzapw/OmniRoute/pull/13069)) — thanks @HouMinXi +- **fix(responses):** wrap forced-non-streaming web_search fallback JSON as Responses SSE so Codex still sees `response.completed` ([#13050](https://github.com/diegosouzapw/OmniRoute/pull/13050)) — thanks @HouMinXi +- **fix(antigravity):** Preserve upstream thought-token usage in normalized completion and reasoning token counts ([#13055](https://github.com/diegosouzapw/OmniRoute/pull/13055)) — thanks @pacocartones +- Fix a concurrency-slot leak in the DeepSeek PoW solver: a worker that failed to spawn (for example a missing worker script) never released its slot, so `MAX_CONCURRENT_WORKERS` failures disabled the solver until restart. ([#13097](https://github.com/diegosouzapw/OmniRoute/pull/13097)) — thanks @anhtahaylove +- **fix(acp):** bound the ACP session output buffers — `stdoutBuffer` and `stderrBuffer` now cap at 1 MiB keeping the most recent output behind a visible `[...output truncated...]` marker, and `stderrBuffer` is reset per prompt instead of accumulating for the lifetime of the session. ([#13100](https://github.com/diegosouzapw/OmniRoute/pull/13100)) — thanks @anhtahaylove +- **fix(acp):** release the `stdout`/`exit` listeners and the idle timer that a `sendPrompt` timeout used to leave attached to the `acpManager` singleton, and drop sessions that exited on their own from the session map instead of keeping them forever. ([#13096](https://github.com/diegosouzapw/OmniRoute/pull/13096)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection and PII scanners now read the text a `tool_result` block carries on `content` (string or nested block list), in messages and in system blocks, so tool output is judged by the same rules as user text ([#13101](https://github.com/diegosouzapw/OmniRoute/pull/13101)) — thanks @ntdatt812 +- **fix(gamification):** close the badge notification SSE stream when the request signal is already aborted before the stream starts — a client that disconnects while the route is still awaiting auth used to leave both the 2s unlock poll and the 15s heartbeat running for the lifetime of the process. ([#13106](https://github.com/diegosouzapw/OmniRoute/pull/13106)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection scan now spends its 16 KB budget on both ends of the request instead of the first 16 KB only, so `system`, `instructions`, `query`, `documents` and the newest turns are no longer hidden behind one long message ([#13104](https://github.com/diegosouzapw/OmniRoute/pull/13104)) — thanks @ntdatt812 +- **fix(dashboard):** expose the Volcano Ark console cookie on quota scraping and unwrap connect-error objects so the dashboard shows the upstream message ([#13107](https://github.com/diegosouzapw/OmniRoute/pull/13107)) — thanks @HouMinXi +- **fix(db):** release the `beforeExit`/`SIGINT`/`SIGTERM` handlers when a `node:sqlite` adapter closes, so a closed adapter and its database handle are no longer pinned to `process` for the lifetime of the run — the same treatment #7494 gave the sql.js adapter. ([#13109](https://github.com/diegosouzapw/OmniRoute/pull/13109)) — thanks @anhtahaylove +- **fix(translator):** `contentSchema` and `unevaluatedItems` are now treated as subschema positions by the tool-schema sanitizer, so a truncation placeholder in either is replaced with a permissive schema instead of being forwarded as a string ([#13110](https://github.com/diegosouzapw/OmniRoute/pull/13110)) — thanks @ntdatt812 +- **fix(cli-helper):** clear the `createLogStream` timeout on the abort path — `stop()` aborts the in-flight fetch and returned through the `signal.aborted` branch, which skipped `clearTimeout` and left an armed timer per stopped stream. The stream reader is now also cancelled when the read loop exits early. ([#13114](https://github.com/diegosouzapw/OmniRoute/pull/13114)) — thanks @anhtahaylove +- **fix(docs):** drop TLS-impersonation, MITM-decrypt, supply-chain attestation, and XOR-mask writeups from the public `/docs` catalog and Docker image. Files stay in git for engineers; operators who need them open the repo, not the website. ([#13136](https://github.com/diegosouzapw/OmniRoute/pull/13136)) — thanks @HouMinXi +- **fix(combo):** parse numeric-epoch `rate_limited_until` in the combo cooldown read path ([#13141](https://github.com/diegosouzapw/OmniRoute/pull/13141)) — thanks @maxmad64bis +- **fix(opencode):** both OpenCode plugins now reject a gateway address typed without `http://` at configuration time, instead of publishing every model with an api url no client can call, and the v2 plugin no longer publishes a model card whose api url is blank or relative ([#13142](https://github.com/diegosouzapw/OmniRoute/pull/13142)) — thanks @maxmad64bis +- **fix(providers):** lock opencode model on upstream 400 model-unavailable ([#13146](https://github.com/diegosouzapw/OmniRoute/pull/13146)) — thanks @maxmad64bis +- **fix(logging):** keep the provider exchange rather than the raw client bodies when a call log exceeds its size budget, and show that recovered payload in the request-detail panel instead of replacing it with the stored response body ([#13147](https://github.com/diegosouzapw/OmniRoute/pull/13147)) — thanks @maxmad64bis +- **fix(traffic-inspector):** the WebSocket route no longer leaks a traffic-buffer subscriber and a 30s ping timer when the client socket is already closed at handler time — listeners are attached before any resource is acquired, a destroyed socket bails out early, and the ping interval stops on a dead socket where `write()` never throws ([#13155](https://github.com/diegosouzapw/OmniRoute/pull/13155)) — thanks @anhtahaylove +- **fix(telegram):** bound the per-user API key cache in the Telegram chat proxy so a burst of distinct chat ids can no longer grow the process heap without limit ([#13165](https://github.com/diegosouzapw/OmniRoute/issues/13165)) ([#13166](https://github.com/diegosouzapw/OmniRoute/pull/13166)) — thanks @anhtahaylove +- **fix(stream):** cancel the upstream response body when the JSON-to-SSE sniff unwinds on a body timeout, so a stalled upstream no longer pins the connection ([#13169](https://github.com/diegosouzapw/OmniRoute/issues/13169)) ([#13171](https://github.com/diegosouzapw/OmniRoute/pull/13171)) — thanks @anhtahaylove +- **fix(telegram):** authenticate webhook deliveries with Telegram's `secret_token` so an unauthenticated caller can no longer mint API keys or spend upstream quota ([#13172](https://github.com/diegosouzapw/OmniRoute/issues/13172)) ([#13175](https://github.com/diegosouzapw/OmniRoute/pull/13175)) — thanks @anhtahaylove +- **fix(models):** give discoverable Gemini 3.8 Flash ids their own 65536 output spec so Antigravity no longer clamps them to 16384 ([#13195](https://github.com/diegosouzapw/OmniRoute/pull/13195)) — thanks @HouMinXi +- **fix(dashboard):** leftover catalog-removed provider rows (gemini-cli) can be listed and purged from the providers page ([#13067](https://github.com/diegosouzapw/OmniRoute/issues/13067)) ([#13197](https://github.com/diegosouzapw/OmniRoute/pull/13197)) — thanks @HouMinXi +- **fix(auth):** a dashboard session now requires the `authenticated: true` claim that login, OIDC and the session refresh already emit — a JWT merely signed with `JWT_SECRET` (for example the Cursor CLI passthrough token, which any API-key holder can obtain) no longer verifies as the `auth_token` cookie on any route, the WebSocket handshake or the live server; existing sessions keep working ([#13298](https://github.com/diegosouzapw/OmniRoute/issues/13298)) ([#13375](https://github.com/diegosouzapw/OmniRoute/pull/13375)) +- **fix(api):** `DELETE /v1/batches/delete-completed` now applies the caller's API-key policy (endpoint allowlist, schedule, usage cap, rate limit) like every other `/v1` route, commits the key-scoped sweep per 200-batch chunk instead of holding one write lock for the whole sweep, refuses to loop without progress, and rejects a scope that names both a key and `allTenants` ([#13684](https://github.com/diegosouzapw/OmniRoute/pull/13684)) +- Restore the API-route typecheck gate: the non-streaming leg result lost its discriminated-union narrowing after the server-owned tool loop reassignment, producing 13 new TS2339 diagnostics in `chatCore.ts`. ([#12963](https://github.com/diegosouzapw/OmniRoute/pull/12963)) +- Restore four non-streaming behaviours the server-owned tool loop refactor dropped: client aborts map to 499 with the fixed `Request aborted` message, an aborted request no longer logs a synthetic `clientResponse`, Claude prompt-cache telemetry is recorded again, and a body that cannot be canonicalized no longer throws when the tool loop is off. ([#12990](https://github.com/diegosouzapw/OmniRoute/pull/12990)) +- **fix(skills):** The CLI registry parser now reads positionals declared with `.addArgument()`, not only those written inline in `.command()`. `tunnel create [type]` was being published as `tunnel create`, so the agent-skills sync gate reported drift on every branch and regenerating would have deleted the argument. ([#13009](https://github.com/diegosouzapw/OmniRoute/pull/13009)) — thanks @ntdatt812 +- **fix(combos):** dashboard combo test uses a short prompt, serial probes, and a 60s timeout so reasoning models and rate-limited free pools do not fail the health check ([#13001](https://github.com/diegosouzapw/OmniRoute/pull/13001)) — thanks @HouMinXi +- fix(compression): terminate idle worker threads on eviction so long-running instances stop leaking OS threads and MessagePorts ([#13091](https://github.com/diegosouzapw/OmniRoute/pull/13091)) — thanks @anhtahaylove +- Redact Google API keys of any length in error bodies: the pattern required exactly 39 characters, so shorter or longer `AIza…` credentials passed through unredacted. ([#12964](https://github.com/diegosouzapw/OmniRoute/pull/12964)) +- **fix(grok-cli):** a 402 "Grok Build usage balance exhausted" parks that Grok login as out of credit (Grok Build CLI, grok.com cookie, and xAI OAuth share the weekly pool). Combo routing then tries the next login instead of locking the model for every account in the pool ([#13061](https://github.com/diegosouzapw/OmniRoute/pull/13061)) — thanks @HouMinXi +- fix(compression): spawn the LLMLingua worker with a file URL object so compression actually runs instead of silently failing open on Node ([#13093](https://github.com/diegosouzapw/OmniRoute/pull/13093)) — thanks @anhtahaylove +- **fix(test):** run the local `test` and `test:unit` scripts at concurrency 4 so a full-suite run no longer exhausts the machine's commit charge and kills unrelated processes ([#13187](https://github.com/diegosouzapw/OmniRoute/pull/13187)) — thanks @anhtahaylove +- **fix(monitoring):** `GET /api/monitoring/health` `credentialHealth` now includes a bounded `failedConnections` list (`connectionId`, `status`, sanitized `lastError`) when the probe-cache gauge `failed>0`, plus `source: probe-cache` and a cheap `staleDbNonOkCount` for sticky SQLite `test_status` on active rows. Documents that the live gauge is not `provider_connections.test_status`. ([#12876](https://github.com/diegosouzapw/OmniRoute/pull/12876)) — thanks @RaviTharuma +- **fix(resilience):** an openai-compatible multi-upstream gateway no longer marks the whole connection `credits_exhausted` when a single upstream (or the `/models` / representative-model health probe) returns 402. Quota failures stay model-scoped; true connection-wide auth failures are unchanged. Single-credential 402 key disable ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239)) is preserved. ([#12875](https://github.com/diegosouzapw/OmniRoute/pull/12875)) — thanks @RaviTharuma +- **fix(dashboard):** nine fixes on the Orchestration Canvas, all diagnosed in the Phase 2 reviews. A source that fails now keeps the timestamp of its FIRST failure instead of being re-stamped every poll — the stale line said "since the last poll" no matter how long the source had been down, and the churn also defeated the snapshot's stable identity (it serializes the source list), so the canvas re-rendered on every tick while anything was broken. A source that HAD data and only then started failing is flagged too: previously only a source with no node at all got the warning, so a source that went down mid-session kept a healthy-looking node forever. The rest are pointwise: clicking a filter chip cancels the pending search debounce (left armed, it fired ~300ms later and silently reverted the chip); the search input carries an accessible name; `?state=running, failed` parses like the unpadded form instead of dropping the padded value; the CSV toggle helper is defined once in `model/urlParams.ts` rather than twice; the agents tab tells "nothing running" apart from "the filter matched nothing", offering a clear-filters button instead of setup links that would be wrong advice there; edges stop emitting SMIL particles above 40 simultaneously active edges, keeping the colored stroke; and the drawer's error banner clears when a retried action succeeds. ([#12983](https://github.com/diegosouzapw/OmniRoute/pull/12983)) +- **fix(i18n):** the nine locales added with the EU-language batch (Greek, Estonian, Irish, Croatian, Lithuanian, Latvian, Maltese, Slovenian, Serbian) were missing the eleven Orchestration Canvas keys that Phase 3 introduced, so the compare-runs panel and the "no runs match these filters" empty state fell back to English in those languages (`deepMergeFallback` substitutes English for an absent key, so nothing rendered blank — it rendered untranslated). The coverage gate does not catch this: it enforces an 80% floor per locale, and eleven missing keys out of ~13,000 leaves coverage at 99.9%. Translated for real in each language, calibrated against the wording each file already uses for "run", "filter" and "skill". ([#13294](https://github.com/diegosouzapw/OmniRoute/pull/13294)) +- **fix(dashboard):** five follow-ups from the Phase 2 Orchestration Canvas review. The History drawer now shows the memory section for runs that already left the live TTL window: the persisted `memory_hits` event is parsed into `metadata.memoryHits` with the same defensive validation the drawer applies, and — because that event is observability rather than a state transition — it no longer leaks into the timeline, where it had been inheriting the task's state and rendering as a duplicate transition. The A2A memory recall runs against its own 1.5s deadline instead of inheriting the memory backend's 30s one; overshooting degrades exactly like any other recall failure (no hits, task proceeds), and the timer is cleared on both paths. Repeating a Conductor run carries its `requirements.cli`/`requirements.model` forward, so the new run is pinned to the same runner profile and model rather than drifting to whatever the fleet picks. A successful repeat from the Agents tab now focuses the run it created, instead of leaving the operator on the finished one. And the auth test for `POST /api/conductor/tasks` moved into the shared `ROUTES` array rather than restating the pattern. ([#12988](https://github.com/diegosouzapw/OmniRoute/pull/12988)) +- fix(plugins): stop leaking an exit listener per plugin hook timeout, which triggered MaxListenersExceededWarning on plugins that ignore SIGTERM ([#13092](https://github.com/diegosouzapw/OmniRoute/pull/13092)) — thanks @anhtahaylove +- **fix(validation):** Provider node edits no longer fail with a generic "Invalid request" when the optional daily-quota reset fields are left blank. The dashboard sends `dailyQuotaResetTimezone` and `dailyQuotaResetHour` as `null`, and only the hour accepted it. ([#13066](https://github.com/diegosouzapw/OmniRoute/issues/13066)) ([#13083](https://github.com/diegosouzapw/OmniRoute/pull/13083)) — thanks @ntdatt812 +- **fix(combo):** quota-aware expansion drops banned, inactive, missing, and wrong-provider connections before quota fetch or model dispatch; pins and allowlists stay selectors, not a bypass. Antigravity automatic exhaustion now requires a reported zero remaining, so a positive balance below 1% stays eligible. ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- Fix an intermittent failure in the quota-weighted routing test suite: scores are a function of `Date.now()`, so peers with identical quota scored microseconds apart never tied and swapped order. ([#12946](https://github.com/diegosouzapw/OmniRoute/pull/12946)) +- fix(volcengine): resolve quota window mapping gaps, safe multi-connection console binding, and response cookie sanitization ([#12950](https://github.com/diegosouzapw/OmniRoute/pull/12950)) — thanks @HouMinXi +- **fix(test):** resolve the WebDAV handler path with `fileURLToPath` so the suite's 37 WebDAV tests run on Windows instead of failing with a doubled `C:\C:\` drive prefix ([#13196](https://github.com/diegosouzapw/OmniRoute/pull/13196)) — thanks @anhtahaylove +- **fix(ui):** fall back from invalid AnySearch icon alias (direct commit `9587e07b69`) +- **fix(sse):** stop rebuilding a truncated summary from the collector's cap-dropped event array ([#12718](https://github.com/diegosouzapw/OmniRoute/pull/12718)) — thanks @hartmark +- **fix(conversations):** show a pending spinner for unresolved tool nodes instead of "(empty)" ([#12727](https://github.com/diegosouzapw/OmniRoute/pull/12727)) — thanks @hartmark +- **fix(streaming):** fail fast when an upstream stream produces only lifecycle/heartbeat events, never real content ([#12741](https://github.com/diegosouzapw/OmniRoute/pull/12741)) — thanks @hartmark +- **fix(responses-continuation):** bridge the write-in-flight window with an in-memory pending store ([#12854](https://github.com/diegosouzapw/OmniRoute/pull/12854)) — thanks @hartmark +- **fix(translator):** drop or map agent_message on Chat Completions fallback ([#12880](https://github.com/diegosouzapw/OmniRoute/pull/12880)) — thanks @RaviTharuma +- **fix(api):** page and stream-complete GET /v1/models for large catalogs ([#12882](https://github.com/diegosouzapw/OmniRoute/pull/12882)) — thanks @RaviTharuma +- **fix(quota):** align AUTH window labels with usage API and clarify 503 ([#12884](https://github.com/diegosouzapw/OmniRoute/pull/12884)) — thanks @RaviTharuma +- **fix(ci):** clear two base-reds on release/v3.8.51 — mutation-coverage gate + image-only-model guard ([#12945](https://github.com/diegosouzapw/OmniRoute/pull/12945)) +- **fix(api):** scope batch bulk-delete to the calling API key ([#13211](https://github.com/diegosouzapw/OmniRoute/pull/13211)) +- **fix(tests):** align model-sync log assertion to shipped path redaction (base-red) ([#13227](https://github.com/diegosouzapw/OmniRoute/pull/13227)) +- **fix(test):** make the ToS heading guard actually require the parentheses ([#13228](https://github.com/diegosouzapw/OmniRoute/pull/13228)) +- **fix(quality):** register 4 drifted covering tests in stryker tap.testFiles ([#13229](https://github.com/diegosouzapw/OmniRoute/pull/13229)) +- **fix(logging):** mask *-api-key headers in request-log pipeline (#13273) ([#13401](https://github.com/diegosouzapw/OmniRoute/pull/13401)) — thanks @KooshaPari +- **fix(tests):** correct stale Jina catalog prefix in custom-models test ([#13403](https://github.com/diegosouzapw/OmniRoute/pull/13403)) — thanks @KooshaPari +- **fix(backend):** prune old backups after health-check-repair snapshots (#13308) ([#13404](https://github.com/diegosouzapw/OmniRoute/pull/13404)) — thanks @KooshaPari +- **fix(backend):** guard memory_fts_au trigger with WHEN clause to stop FTS bloat (#13326) ([#13405](https://github.com/diegosouzapw/OmniRoute/pull/13405)) — thanks @KooshaPari +- **fix(free-tiers):** remove obsolete Together $25 signup credit (#12526) ([#13407](https://github.com/diegosouzapw/OmniRoute/pull/13407)) — thanks @KooshaPari +- **fix:** skip non-chat generation models in Test all models ([#13409](https://github.com/diegosouzapw/OmniRoute/pull/13409)) — thanks @KooshaPari +- **fix(providers):** de-duplicate agy and antigravity model catalogs ([#13410](https://github.com/diegosouzapw/OmniRoute/pull/13410)) — thanks @KooshaPari +- **fix(dashboard):** send provider in body for mini-playground web search ([#13411](https://github.com/diegosouzapw/OmniRoute/pull/13411)) — thanks @KooshaPari +- **fix(resilience):** add CJK quota-exhaustion patterns to 429 classifier ([#13412](https://github.com/diegosouzapw/OmniRoute/pull/13412)) — thanks @KooshaPari +- **fix(backend):** eval runner: failed calls no longer score as passed + dotAll regex ([#13413](https://github.com/diegosouzapw/OmniRoute/pull/13413)) — thanks @KooshaPari +- **fix(backend):** eval runner opts out of output-style and memory injection ([#13414](https://github.com/diegosouzapw/OmniRoute/pull/13414)) — thanks @KooshaPari +- **fix:** chain serial quarantine step in npm test script (fixes #13286) ([#13418](https://github.com/diegosouzapw/OmniRoute/pull/13418)) — thanks @KooshaPari +- **fix(antigravity):** derive stable session ID from credentials for prompt-cache reuse (#13305) ([#13424](https://github.com/diegosouzapw/OmniRoute/pull/13424)) — thanks @KooshaPari +- **fix(memory):** merge memory into top-level system field instead of unshifting at messages[0] (#13425) ([#13427](https://github.com/diegosouzapw/OmniRoute/pull/13427)) — thanks @KooshaPari +- **fix(compression):** preserve instruction blocks from lossy engines ([#13523](https://github.com/diegosouzapw/OmniRoute/pull/13523)) — thanks @KooshaPari +- **fix(compression):** preserve polarity words and newlines in ultra heuristic ([#13524](https://github.com/diegosouzapw/OmniRoute/pull/13524)) — thanks @KooshaPari +- **fix(compression):** run user preservePatterns before built-in patterns (#13457) ([#13525](https://github.com/diegosouzapw/OmniRoute/pull/13525)) — thanks @KooshaPari +- **fix(reasoning):** expand deepseek-v4 regex to match v4.1+ and add command-code provider (#13430) ([#13528](https://github.com/diegosouzapw/OmniRoute/pull/13528)) — thanks @KooshaPari +- **fix(providers):** add alwaysReasons metadata for reka-flash-3 ([#13531](https://github.com/diegosouzapw/OmniRoute/pull/13531)) — thanks @KooshaPari +- **fix(playground):** allow Compare columns to scroll independently ([#13532](https://github.com/diegosouzapw/OmniRoute/pull/13532)) — thanks @KooshaPari +- **fix(security):** empty whitelist allows through to prevent admin lockout ([#13534](https://github.com/diegosouzapw/OmniRoute/pull/13534)) — thanks @KooshaPari +- **fix(tests):** add DISABLE_IOREG_STRATEGY env var for macOS test compatibility (fixes #13467) ([#13539](https://github.com/diegosouzapw/OmniRoute/pull/13539)) — thanks @KooshaPari +- **fix(zed-hosted):** map developer role and Gemini enums for Zed proxy ([#13543](https://github.com/diegosouzapw/OmniRoute/pull/13543)) — thanks @KooshaPari +- **fix(analytics):** normalize provider aliases to canonical id in usage_history ([#13545](https://github.com/diegosouzapw/OmniRoute/pull/13545)) — thanks @KooshaPari +- **fix(combo):** use traceId for call log id to prevent UNIQUE constraint failure ([#13546](https://github.com/diegosouzapw/OmniRoute/pull/13546)) — thanks @KooshaPari +- **fix(auth):** check model lockout before returning synthetic noauth connection ([#13547](https://github.com/diegosouzapw/OmniRoute/pull/13547)) — thanks @KooshaPari +- **fix(guardrails):** skip credential redaction for base64 image data URLs ([#13550](https://github.com/diegosouzapw/OmniRoute/pull/13550)) — thanks @KooshaPari +- **fix(resilience):** increase requestQueue.maxWaitMs default from 15s to 30s ([#13553](https://github.com/diegosouzapw/OmniRoute/pull/13553)) — thanks @KooshaPari +- **fix(docs):** document OMNIROUTE_READY_TIMEOUT_MS and allowlist the test-only DISABLE_IOREG_STRATEGY ([#13692](https://github.com/diegosouzapw/OmniRoute/pull/13692)) + ### 📝 Maintenance ---- +- **docs(readme):** reconcile live v3.8.50 provider, free-tier, CLI, routing, test, community, sponsor, acknowledgment, and SVG metrics with their audited source denominators, including a deduplicated OmniRoute-in-Action snapshot and distinct contributor rankings for merged pull requests, GitHub-attributed commits, and Git history ([#11356](https://github.com/diegosouzapw/OmniRoute/pull/11356)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **docs:** sync the canonical provider count 354 → 356 across `README.md`, `AGENTS.md`, `llm.txt` (+ 42 i18n mirrors), the four README SVG diagrams, `docs/reference/PROVIDER_REFERENCE.md` (regenerated) and the `package.json` description after Opper (#11629) and 1min.ai (#11631) boarded the catalog — closes the `check:docs-counts-sync` strict drifts that kept `release/v3.8.51` red ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11673](https://github.com/diegosouzapw/OmniRoute/pull/11673)) — thanks @jonlwheat2-gif +- **test(providers):** update count-derived assertions after the v3.8.51 provider additions — `APIKEY_PROVIDERS` 233 → 235 (Opper #11629 + 1min.ai #11631), reserved-prefix REGISTRY walk 395 → 398, `WEB_FETCH_PROVIDERS` now includes `nimble-search` (#11620), and the provider translate-path golden snapshot regenerated ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11674](https://github.com/diegosouzapw/OmniRoute/pull/11674)) — thanks @jonlwheat2-gif +- **test(sse):** bump the hard-lease connection-query inventory for `src/lib/tokenHealthCheck.ts` to 2 — the verify-only web-cookie sweep added by #11495 split the single `getProviderConnections` call into oauth + cookie variants, which the frozen inventory had not tracked ([#11495](https://github.com/diegosouzapw/OmniRoute/pull/11495)). ([#11677](https://github.com/diegosouzapw/OmniRoute/pull/11677)) — thanks @jonlwheat2-gif / @oyi77 +- **chore(release):** clear the `release/v3.8.51` base-red gates — docs migration count (+38 i18n mirrors), stryker `tap.testFiles`, lockfile registry host, stale ESLint suppressions, the 7 lint regressions they were hiding, the stale `APIKEY_PROVIDERS` partition count (231 → 233), the `glm-5.3-max` tier missing from the GLM test table, a dead `modelType === "chat"` comparison in the models catalog, and the `resolveLiveWsUrl` / `sanitizeLiveWsPort` helpers from #11388 that a later merge dropped from `wsPath.ts` (dashboard live WebSocket URL resolution) ([#11502](https://github.com/diegosouzapw/OmniRoute/pull/11502)) — thanks @MumuTW +- **chore(free-models):** the free-tier regime table now declares which totals figure each regime feeds and whether it may take the no-auth shortcut, so the sets that used to repeat those answers by hand are derived from it — a new `freeType` no longer compiles until it has answered every question, instead of silently contributing to no total ([#11537](https://github.com/diegosouzapw/OmniRoute/pull/11537)) — thanks @maxmad64bis +- **docs(routing):** LKGP strategy copy now states provider-level stickiness and the explicit rules fallback instead of promising per-target pinning ([#11555](https://github.com/diegosouzapw/OmniRoute/pull/11555)) — thanks @maxmad64bis +- **test(chatcore):** pin the response tool-name alias resolution against the ordering hazard that broke every Gemini/Antigravity MCP tool call in v3.8.49. `extractRequestToolIdentityMap` deletes `translatedBody._toolNameMap`, so the later read is always undefined and the ledger survives only through the `requestToolIdentityMap` fallback — removing that fallback previously left the entire tool-name suite green. The resolution moves into `resolveResponseToolNameMap()` next to the map it depends on, with a regression guard that fails without the recovery. No behaviour change. ([#11575](https://github.com/diegosouzapw/OmniRoute/pull/11575)) — thanks @amartinawi +- **docs(env):** document `OMNIROUTE_CORPUS_CACHE_SIZE` in `.env.example` and `ENVIRONMENT.md`, so the env/docs contract check stops failing Docs Gates on every branch ([#11576](https://github.com/diegosouzapw/OmniRoute/pull/11576)) — thanks @ntdatt812 +- **chore(stryker):** register `route-guard-tunnel-processes-local-only` and `universal-quota-aware-routing` in `tap.testFiles`, so their mutant kills count for the three modules they cover ([#11577](https://github.com/diegosouzapw/OmniRoute/pull/11577)) — thanks @ntdatt812 +- **test(authz):** pin `LOCAL_ONLY_API_GET_EXEMPTIONS` by exact membership instead of by entry count, so the guard names the offending path and also catches a substitution ([#11580](https://github.com/diegosouzapw/OmniRoute/pull/11580)) — thanks @ntdatt812 +- **test(build):** pin `resolveNextBuildBundlerFlag` to the `OMNIROUTE_USE_TURBOPACK` contract it implements, across runtimes, instead of asserting an unimplemented Bun override ([#11583](https://github.com/diegosouzapw/OmniRoute/pull/11583)) — thanks @ntdatt812 +- **test(claude-web):** the Claude Web transport test's barrel anchors no longer pin the exact construction spelling. #11421 made the executor map lazy, so `/"claude-web": new ClaudeWebExecutor\(\)/` stopped matching and the test went red on `release/v3.8.51` itself — a base-red gate every open PR inherited. The anchors now match the alias key and the class name and tolerate either spelling in between, and were checked to still fail when an alias is genuinely repointed at another executor ([#11585](https://github.com/diegosouzapw/OmniRoute/pull/11585)) — thanks @ntdat812 +- **test(models):** pin `qwen3.8-max` resolution per provider — two of the four catalogs now serve the bare id and no longer carry `-preview` ([#11590](https://github.com/diegosouzapw/OmniRoute/pull/11590)) — thanks @ntdatt812 +- **test(executors):** drop the stale 128-tool truncation assertion that contradicted the #11444 pass-through contract ([#11591](https://github.com/diegosouzapw/OmniRoute/pull/11591)) — thanks @ntdatt812 +- **test(token-health):** pin the current terminal-skip boundary — expired with retry budget is probed, `account_deactivated` stays skipped ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)) — thanks @ntdatt812 +- **test(models):** follow #11503's retarget of the `gemini-3-pro-high` alias to the hyphenated catalog id ([#11593](https://github.com/diegosouzapw/OmniRoute/pull/11593)) — thanks @ntdatt812 +- **refactor(video bridge):** extract per-video acquisition, whole-result caching, description, and metrics/abort/cleanup out of `VideoBridgeGuardrail.preCall` into a `processVideoPart` seam in a new `videoBridgePipeline.ts`, behind explicit `VideoMediaBrokerPort`, `VideoAudioTranscriptionPort`, and `VideoDrilldownPort` boundaries; `preCall` now only handles request traversal, policy, and response aggregation. The Video tab's FFmpeg/ffprobe runtime status is now an explicit `unknown` / `restricted` / `unavailable` / `available` state instead of a nullable boolean pair, fixing a case where an in-flight or failed probe was mislabeled as "install FFmpeg" ([#11657](https://github.com/diegosouzapw/OmniRoute/issues/11657)). ([#12016](https://github.com/diegosouzapw/OmniRoute/pull/12016)) +- **test(opencode-plugin):** add unit test coverage for `formatFreeBudget()` naming helper ([#11660](https://github.com/diegosouzapw/OmniRoute/pull/11660)) — thanks @f9td56dbgh-hub +- **docs(video):** clarify that the Video Bridge transcript `source` field (`client`, `embedded`, `audio-bridge`) is presently caller-declared and not yet server-verified — OmniRoute enforces the enum shape but does not cryptographically confirm that an `embedded`/`audio-bridge` label came from a server-owned extraction ([#11661](https://github.com/diegosouzapw/OmniRoute/issues/11661)). ([#12001](https://github.com/diegosouzapw/OmniRoute/pull/12001)) +- **chore(providers):** retire the distributed Raycast Relay and Hailuo Web integrations after a provenance audit confirmed substantial derivation from GPL-3.0 sources; the official GitHub and MiniMax API providers remain available. Migration 163 disables legacy connections and invalidates their active leases while preserving connection IDs, audit/history rows, quota snapshots, usage records, and fail-closed API-key connection policies. ([#11691](https://github.com/diegosouzapw/OmniRoute/pull/11691)) +- **chore(providers):** retire the Felo Web provider while its GPL-derived provenance remains on licensing HOLD ([#11698](https://github.com/diegosouzapw/OmniRoute/pull/11698)) — stale runtime and database references now fail closed while existing connection, lease, and usage history remains preserved for auditability. +- **fix(sse):** retire the Gemini Web image-generation bridge while provenance and licensing remain on HOLD, preserving Gemini Web chat/auth/tools and legitimate Gemini/Nano Banana image providers ([#11708](https://github.com/diegosouzapw/OmniRoute/pull/11708)) +- **chore(audio):** retire the built-in EdgeTTS provider and its 11-voice catalog while provenance/licensing review remains on HOLD; the generic `/v1/audio/speech` endpoint and the other speech providers remain available ([#11711](https://github.com/diegosouzapw/OmniRoute/pull/11711)) +- **fix(providers):** retire the unofficial Qwen Web integration and its `qwen-web`/`qw` identities while third-party provenance remains on HOLD ([#11713](https://github.com/diegosouzapw/OmniRoute/pull/11713)) — direct requests now fail closed with a sanitized HTTP 410 and combos skip the retired target, while migration guards preserve connection IDs, allowlists, usage/audit history and invalidate active leases. Supported Qwen API, Qwen Code, cloud, token-plan, audio, local and model-name surfaces remain available; restoring Qwen Web requires a qualified provenance/license decision. +- **fix(providers):** retire `microsoft-designer-web` and its `msdesigner` alias from runtime and catalogs while preserving stored credentials, provider history and allowlists as inactive records — source dependency on gpt4free's [`MicrosoftDesigner.py`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/g4f/Provider/needs_auth/MicrosoftDesigner.py) is proven at snapshot `0943d16eef757a44deb60651c07bbe5fa958cafe`, whose [`LICENSE`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/LICENSE) is GPL-3.0; expressive adaptation is probable, which records provenance/licensing risk without asserting a legal conclusion ([#11720](https://github.com/diegosouzapw/OmniRoute/pull/11720)). +- **docs(legal):** centralize pinned MIT notices for the vendored GCF codec and local flag assets ([#11726](https://github.com/diegosouzapw/OmniRoute/pull/11726)) +- Split the 2,952-line `adobeFireflyClient.ts` into nine focused modules (catalog, credentials, payload, ARP session, upload, responses, IMS, account, poll) behind an unchanged barrel — no consumer edits and no public API change (#11728). — thanks @MumuTW +- **fix(dashboard):** replaced the Model Lockout card's unprovenanced notification MP3 with a short, locally synthesized Web Audio chime ([#11731](https://github.com/diegosouzapw/OmniRoute/pull/11731)) — both toggles retain optional audible feedback without shipping replacement media or third-party code, and unsupported, suspended, or rejected audio contexts fail open without blocking the setting change. +- **chore(deepseek-web):** remove the provenance-unresolved DeepSeek PoW WASM binary and its runtime loader/tracing while retaining the existing JavaScript solver (slower at high difficulty) ([#11732](https://github.com/diegosouzapw/OmniRoute/pull/11732)). +- **chore(assets):** remove four unused provider assets whose redistribution provenance could not be verified while retaining the active Freebuff SVG ([#11735](https://github.com/diegosouzapw/OmniRoute/pull/11735)) +- **docs(assets):** retired two raster images whose redistribution provenance could not be verified ([#11736](https://github.com/diegosouzapw/OmniRoute/pull/11736)) — PicoClaw documentation now reuses the existing SVG, while Zoo Code remains a text link without an image. +- **docs(legal):** add pinned notices for six LobeHub-derived provider SVGs and 65 byte-exact theSVG assets, retaining registry claims as unverified metadata with explicit trademark and HOLD boundaries ([#11737](https://github.com/diegosouzapw/OmniRoute/pull/11737)). +- **chore(quality):** added a versioned 225/225 provider-asset provenance manifest and blocking drift gate ([#11743](https://github.com/diegosouzapw/OmniRoute/pull/11743)) — the inventory pins magic MIME and SHA-256 for every physical file, records immutable source matches for 71 assets, keeps 69 probable and 85 unresolved entries explicit but non-blocking, and treats provenance as source evidence rather than copyright or trademark clearance. +- **refactor(mitm):** replace four previously derived Traffic Inspector utilities — conversation normalization, SSE merging, secret masking, and header sanitization — with independent clean-room implementations based on public provider schemas, WHATWG SSE, and RFC 6750, while keeping the two independently classified passthrough surfaces unchanged ([#11748](https://github.com/diegosouzapw/OmniRoute/pull/11748)). +- Added a unit test that fails seven days before any dated pack under `config/` (`validUntil` and sibling keys) lapses, naming the file and key. The Alibaba free-tier pack expired on 2026-08-27 and turned every PR red the next morning with no commit involved; renewal now happens on someone's terms, not the clock's. ([#11891](https://github.com/diegosouzapw/OmniRoute/pull/11891)) +- `check:workflows` now fails (under `--strict`/`--ratchet`) when any job routed to a self-hosted runner publishes with `--provenance` — npm rejects that with `422` at the registry, which in v3.8.50 only surfaced after the tag and Docker images were public. ([#11895](https://github.com/diegosouzapw/OmniRoute/pull/11895)) +- `scripts/ops/runner-janitor.sh` now proves a path is idle with one `lsof` snapshot and removes stale leftovers itself (tmpfs after 3 h — it is RAM — disk after 24 h), kills orphan `next-build` processes, prunes checkouts of stopped runners, and alerts on memory pressure; `--dry-run` shows exactly what it would do. `docs/ops/RUNNER_BOX.md` reconciled to the measured box (31 GB, 10 listeners). ([#11893](https://github.com/diegosouzapw/OmniRoute/pull/11893)) +- The `next-build` artefact (1.3 GB) is now written and read under `$RUNNER_TEMP` (per-runner, on disk) instead of `/tmp`, which on the self-hosted pool is a 12 GB tmpfs in RAM. Landing it there took 27–32 of the publish job's 76 minutes, and the fixed `/tmp/e2e-build.tar.gz` name let E2E jobs on different runners overwrite each other's download. ([#11896](https://github.com/diegosouzapw/OmniRoute/pull/11896)) +- The CI `build` job now runs in two concurrency lanes — `main` and pull requests — so a release build is never queued behind (or OOM-killed beside) PR builds on the self-hosted pool, which holds one `next-build` comfortably and two at the edge. ([#11901](https://github.com/diegosouzapw/OmniRoute/pull/11901)) +- Re-freeze the ESLint suppressions on `release/v3.8.51` from a clean-room measurement (2 stale file entries pruned, 55 pre-existing `no-explicit-any` in six new files frozen under #11924) and drop the dead `GPT_SIZE_MAP` constant orphaned by the Adobe Firefly client split, so `No new ESLint warnings` stops failing every PR with exit 2 (Refs #11924) ([#11955](https://github.com/diegosouzapw/OmniRoute/pull/11955)) +- Type the 55 `no-explicit-any` sites that had been frozen under #11924 — four redundant casts in `socksConnectorWithFamily.ts` (undici/socks types already accept them) and the mocks/fixtures of the socks-timeout and isFree suites — and drop their suppression entries; the ESLint ratchet shrinks from 5487 to 5432 (Closes #11924) ([#11975](https://github.com/diegosouzapw/OmniRoute/pull/11975)) +- Take the two hosted-runner builds off the PR rail: `Build App` (`build.yml`) is `workflow_dispatch`-only and `DAST smoke (PR)` runs only for PRs into `main` — the 7 GB hosted VM cannot build `release/v3.8.51` in any profile (19/30 red, VM shutdown ~8 min into `next build`) and both checks had turned into permanent noise on every release PR; the bundle stays validated by `ci.yml` on `main` and by `nightly-release-green` on `release/**` (Closes #11946) ([#11962](https://github.com/diegosouzapw/OmniRoute/pull/11962)) +- Move the four nightly jobs that build the backend (`nightly-schemathesis`, `nightly-llm-security` promptfoo + garak, `nightly-resilience` axe-a11y) off the hosted 7 GB runner — where they died on `release/v3.8.51` unseen — onto the box's new `omni-light` pool (two listeners), and document the reshaped fleet (4 active OmniRoute listeners: 2 `omni-build` + 2 `omni-light`, janitor ceiling 4) (Closes #11965) ([#11967](https://github.com/diegosouzapw/OmniRoute/pull/11967)) +- Stop painting every fork PR into `release/**` red: `quality.yml` `Build (advisory)` is skipped (GitHub still reports `continue-on-error` failures as check FAILURE). Hosted `ubuntu-latest` cannot finish `npm run build` on this tree — same class as #11962 taking `build.yml` off the PR rail. `docker-publish.yml` amd64 now runs on the `.113` `omni-build` pool (31 GB / 32 cores, two listeners) with Turbopack, shares the `heavy-build-main` lane with `ci.yml` `Build` so it queues instead of becoming a third heavy, and keeps per-ref concurrency (a merge storm was starting 8 concurrent OOM builds). arm64 stays on `ubuntu-24.04-arm` with webpack — there is no ARM box. Fallback when `USE_VPS_RUNNER` is off: hosted amd64 + webpack (#11976). ([#12021](https://github.com/diegosouzapw/OmniRoute/pull/12021)) +- `docker-publish.yml` builds with webpack (`OMNIROUTE_USE_TURBOPACK=0`) on **both** arches, including amd64 on the `.113` `omni-build` pool. Turbopack had the RAM (31 GB, `omniroute-113-6`) and still panicked (`TurbopackInternalError: there must be a path to a root`, run 33253576569, 26 min in). The same tree's arm64 webpack build on hosted `ubuntu-24.04-arm` succeeded (run 33264823398). Dockerfile already documents webpack as the Docker escape hatch (#11976). ([#12050](https://github.com/diegosouzapw/OmniRoute/pull/12050)) +- **docs(free-tier):** declare the counting vs deciding regimes for "is it free?" and guard the deciding path from DB-backed catalog resolution ([#12226](https://github.com/diegosouzapw/OmniRoute/pull/12226)) — thanks @maxmad64bis +- **docs(auto-combo):** The mode pack table in `docs/routing/AUTO-COMBO.md` now lists all six shipped packs with every weight each one sets, replacing a four-pack table whose numbers had also drifted from the source. It states plainly that no pack sets `quality`, so selecting any pack silences the observed-quality signal. Six more documents that quote the scoring factor count joined the `check:docs-counts` gate, which caught five stale claims — including one naming nine factors that do not exist — and two stale mode pack counts. The dashboard routing panel, which offered four of the six packs and labelled the default strategy "6-Factor Scoring", is now covered by a test; the two packs it was missing are `reliability-first` and `chaos-mode`, the latter labelled as the fault-injection profile it is rather than as one more routing preference ([#12316](https://github.com/diegosouzapw/OmniRoute/pull/12316)) — thanks @maxmad64bis +- **chore(quality):** rebaseline `src/lib/db/apiKeys.ts` for the ACL the key-creation path now preserves ([#12352](https://github.com/diegosouzapw/OmniRoute/pull/12352)) ([#12673](https://github.com/diegosouzapw/OmniRoute/pull/12673)) — thanks @KrzysiekSko +- **chore(providers):** bump the Claude Code wire identity and the Devin bridge image pin from `2.1.220` to `2.1.258` ([#12402](https://github.com/diegosouzapw/OmniRoute/pull/12402)) — thanks @ggiak ([#12604](https://github.com/diegosouzapw/OmniRoute/pull/12604)) +- **docs(env):** align `.env.example`, the README Bun section, and the troubleshooting guide with the code: `OMNIROUTE_USE_TURBOPACK` also governs `npm run build` (not dev-only), `bun run build` follows that flag instead of auto-selecting Webpack, `OMNIROUTE_CHAT_MAX_HEAVY_IN_FLIGHT` is unset by default (no request-count cap), and the structural `503 chat_admission_busy` message matches `chatAdmissionResponses.ts` (#12404 — thanks @pacocartones) +- **chore(electron):** upgrade the desktop app to Electron 44 (Chromium 152, Node 24.18.1) ([#12217](https://github.com/diegosouzapw/OmniRoute/pull/12217)). **Requires macOS 13 (Ventura) or later** — Chromium dropped macOS 12 (Monterey), so Monterey users must stay on an earlier OmniRoute desktop build. Windows and Linux are unaffected; the app already shipped only x64/arm64, so Electron 44 dropping 32-bit builds changes nothing. Removes the `openAsHidden`/`wasOpenedAsHidden` login-item fields deleted in Electron 44 — hidden autostart continues to work through the `--hidden` argument registered with the login item ([#12554](https://github.com/diegosouzapw/OmniRoute/pull/12554)) — thanks @app/dependabot +- **chore(quality):** rebaseline `src/sse/handlers/chat.ts` for the effective-input persistence the continuation fix needs ([#12641](https://github.com/diegosouzapw/OmniRoute/pull/12641)) ([#12680](https://github.com/diegosouzapw/OmniRoute/pull/12680)) — thanks @hartmark +- `Coverage` job on `ci.yml`: the informational Codecov upload gets its own 5-minute ceiling and `continue-on-error`, and the job budget grows from 20 to 30 minutes (the 8-shard c8 merge alone takes ~10) — a stalled upload no longer ends the job `cancelled` and drags a fully green `main` run's conclusion down with it ([#11972](https://github.com/diegosouzapw/OmniRoute/pull/11972)) +- CI hardening on the PR rail: the four `Unit Tests fast-path` shards get `timeout-minutes: 30` (a hung shard held a PR for 64 min instead of GitHub's 6 h default) and both ESLint file caches lose their `restore-keys` fallback, so a cache built under another suppressions file or lint config can no longer report stale verdicts (Refs #11600, #11924) ([#11963](https://github.com/diegosouzapw/OmniRoute/pull/11963)) +- Every CI job that runs a `next build` (`build`, the npm `publish`, both release-green validations) now targets the `omni-build` runner label, which only two of the eight self-hosted runners carry. The box holds one build comfortably and two at the edge; a third now queues on GitHub instead of being OOM-killed by the kernel. ([#11932](https://github.com/diegosouzapw/OmniRoute/pull/11932)) +- **refactor(combo):** move `handleRoundRobinCombo` (and `resolveTargetTokenLimit`) into `open-sse/services/combo/roundRobinCombo.ts`. `combo.ts` drops from 2164 to 1014 lines (`split("\n").length`); the leaf is 1199 (under the 1200 new-file cap). The round-robin call site uses a dynamic `import()` so `releaseStickyPinOnFailure` / `clearStaleLKGP` can stay exported from `combo.ts` without a static cycle. Skip / sticky / semaphore / safety-timer behavior is unchanged. ([#12811](https://github.com/diegosouzapw/OmniRoute/pull/12811)) — thanks @HouMinXi +- **chore(quality):** rebaseline the file-size caps the error-boundary campaign grew past (`open-sse/executors/codex.ts`, `open-sse/vendor/codex-chatgpt-web/bridge.ts`, both via [#12444](https://github.com/diegosouzapw/OmniRoute/pull/12444)) ([#12654](https://github.com/diegosouzapw/OmniRoute/pull/12654)) +- **chore(quality):** rebaseline the file-size caps the hartmark batch grew past (`combos/page.tsx` via [#12355](https://github.com/diegosouzapw/OmniRoute/pull/12355), `open-sse/services/combo.ts` via [#12338](https://github.com/diegosouzapw/OmniRoute/pull/12338)) ([#12623](https://github.com/diegosouzapw/OmniRoute/pull/12623)) — thanks @hartmark +- **chore(quality):** rebaseline the file-size caps the HouMinXi batch grew past when its PRs stacked (`providers/page.tsx`, `chatCore.ts`, `accountFallback.ts`) — each PR measured correctly in isolation, none saw the stacking ([#12619](https://github.com/diegosouzapw/OmniRoute/pull/12619)) +- **chore(quality):** rebaseline `open-sse/services/combo.ts` for the reset-aware scoring the HouMinXi batch stacked ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) ([#12678](https://github.com/diegosouzapw/OmniRoute/pull/12678)) — thanks @HouMinXi +- **docs:** sync provider catalog count (direct commit `4f11b2ae3d`) +- **docs(skills):** sync omni-inference search fallback (direct commit `b4e76a7ed9`) +- **chore(lint):** prune two stale base suppressions (direct commit `1325047d56`) +- **test(auto-combo):** tolerate full-suite rotation load (direct commit `ad3e293f9f`) +- **test:** drain inherited quality snapshot reds (direct commit `21ed68d8ac`) +- **chore(quality):** rebaseline measured CLI bundle growth (direct commit `d35b3f9779`) +- **test(ui):** reproduce invalid AnySearch icon alias (direct commit `12cc6ca834`) +- **test(translator):** fix the relative imports of the relocated deferred-finish test (direct commit `777d9d1629`) +- **docs(counts):** bring the documented provider and migration counts to the measured values (direct commit `e70bea30e9`) +- **test(security):** drop the guard-target set left unused by the sync (direct commit `a32ba0e362`) +- **chore(db):** renumber model_capabilities migration 163 → 169 (cross-branch collision with 163_radar_feed_cache_generated_at) (direct commit `70f5d4cbf9`) +- **docs(cli):** document CLI_PRIME_AGENT_BIN and correct the CLI Agents count (direct commit `9058e39b61`) +- **chore(deps):** refresh runtimes and adopt ESLint 10 ([#11259](https://github.com/diegosouzapw/OmniRoute/pull/11259)) — thanks @backryun +- **refactor(local-corpus):** implement dynamic root resolution and LRU cache ([#11491](https://github.com/diegosouzapw/OmniRoute/pull/11491)) — thanks @raheemuddin786 +- **chore(quality):** prune stale eslint-suppressions entries ([#11596](https://github.com/diegosouzapw/OmniRoute/pull/11596)) +- **chore(ci):** unblock quality gates - env-doc sync + stryker tap.testFiles registration (#9282) ([#11609](https://github.com/diegosouzapw/OmniRoute/pull/11609)) — thanks @Meet6338-X +- **chore(ci):** point the circular-deps gate at dpdm's real JS entrypoint ([#11615](https://github.com/diegosouzapw/OmniRoute/pull/11615)) — thanks @benzntech +- **chore(quality):** rebaseline file-size for #11448/#11495/#11561 growth ([#11630](https://github.com/diegosouzapw/OmniRoute/pull/11630)) +- **deps:** realign onnxruntime-node pin to @huggingface/transformers' 1.24.3 lockstep ([#11633](https://github.com/diegosouzapw/OmniRoute/pull/11633)) — thanks @jonlwheat2-gif +- **test(autoCombo):** port models_dev_tier #11508 guard from node:test to vitest ([#11635](https://github.com/diegosouzapw/OmniRoute/pull/11635)) — thanks @jonlwheat2-gif +- **chore(quality):** rebaseline file-size for #11083/#11631/#11628 growth ([#11636](https://github.com/diegosouzapw/OmniRoute/pull/11636)) +- **chore(lint):** prune stale ESLint suppressions (base-red #11449) ([#11644](https://github.com/diegosouzapw/OmniRoute/pull/11644)) — thanks @jonlwheat2-gif +- **docs(changelog):** aggregate the ten v3.8.50 fragments into CHANGELOG.md ([#11683](https://github.com/diegosouzapw/OmniRoute/pull/11683)) +- **chore(search):** drop unreachable credential sweep after #11642 ([#11687](https://github.com/diegosouzapw/OmniRoute/pull/11687)) — thanks @HouMinXi +- **ci:** add API route TypeScript regression gate ([#11705](https://github.com/diegosouzapw/OmniRoute/pull/11705)) — thanks @Sabeekhann +- **docs(dedup):** correct the CodeQL claim on the tenant-namespace comment ([#11712](https://github.com/diegosouzapw/OmniRoute/pull/11712)) +- **docs(changelog):** add consolidated v3.8.50 stats, top-25 ranking and a .mailmap ([#11715](https://github.com/diegosouzapw/OmniRoute/pull/11715)) +- **docs:** recommend gstack for AI-assisted workflows ([#11770](https://github.com/diegosouzapw/OmniRoute/pull/11770)) — thanks @santosraju99-hub +- **chore(quality):** tighten complexity/cognitive-complexity ratchets to the current tip; land the missed gateways.ts rebaseline ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **docs(i18n):** finish Freepik → Magnific rebrand in locale strings and README ([#11772](https://github.com/diegosouzapw/OmniRoute/pull/11772)) — thanks @turbolego +- **refactor(auto-combo):** fix divergent scoring in combo health reporting ([#11854](https://github.com/diegosouzapw/OmniRoute/pull/11854)) — thanks @jacobsparts +- **chore(changelog):** add missing fragments for #11919/#11918/#11916 ([#11938](https://github.com/diegosouzapw/OmniRoute/pull/11938)) +- **test(check):** escape the runs-on fixture with JSON.stringify, not a quote-only replace ([#11942](https://github.com/diegosouzapw/OmniRoute/pull/11942)) (direct commit `fb7445eaa3`) +- **test(release):** align five suites with the contracts #11933, #11919 and #11876 shipped on release/v3.8.51 ([#11944](https://github.com/diegosouzapw/OmniRoute/pull/11944)) +- **chore(quality):** remove unreachable code and restore test discovery ([#11950](https://github.com/diegosouzapw/OmniRoute/pull/11950)) — thanks @backryun +- **docs(agents):** sync-back landings are fast-forward, never squash ([#11964](https://github.com/diegosouzapw/OmniRoute/pull/11964)) +- **test(infra):** retry recursive temp-dir removal instead of failing a shard on ENOTEMPTY (#11966) ([#11968](https://github.com/diegosouzapw/OmniRoute/pull/11968)) +- **docs(ops):** the .113 heavy-build ceiling is one runner, not two ([#12048](https://github.com/diegosouzapw/OmniRoute/pull/12048)) +- **test(ui):** correct inactive auto-fetch expectation ([#12098](https://github.com/diegosouzapw/OmniRoute/pull/12098)) — thanks @RaviTharuma +- **docs:** document native dependency check escape hatch ([#12101](https://github.com/diegosouzapw/OmniRoute/pull/12101)) — thanks @RaviTharuma +- **test(providers):** regenerate the translate-path golden for OrcaRouter (#11923) ([#12118](https://github.com/diegosouzapw/OmniRoute/pull/12118)) +- **chore(quality):** velocity phase — loosen every numeric baseline by 20% until v4.0, monitor headroom nightly ([#12125](https://github.com/diegosouzapw/OmniRoute/pull/12125)) +- **chore(lint):** batch 0 of #12146 — type the call-log-cap sqlite rows instead of 45 as-any casts ([#12157](https://github.com/diegosouzapw/OmniRoute/pull/12157)) +- **chore(lint):** batch 4 of #12146 — shared/components react-hooks violations resolved ([#12159](https://github.com/diegosouzapw/OmniRoute/pull/12159)) +- **chore(lint):** batch 1 of #12146 — dashboard/cli-code react-hooks violations resolved ([#12160](https://github.com/diegosouzapw/OmniRoute/pull/12160)) +- **chore(lint):** batch 3 of #12146 — dashboard/settings react-hooks violations resolved ([#12162](https://github.com/diegosouzapw/OmniRoute/pull/12162)) +- **chore(lint):** batch 2 of #12146 — resolve the react-hooks compiler violations in dashboard/providers ([#12163](https://github.com/diegosouzapw/OmniRoute/pull/12163)) +- **chore(quality):** register search-432 cooldown test in stryker tap.testFiles ([#12170](https://github.com/diegosouzapw/OmniRoute/pull/12170)) +- **chore(lint):** batch 5 of #12146 — combos, endpoint, provider-stats, api-manager and costs react-hooks violations resolved ([#12174](https://github.com/diegosouzapw/OmniRoute/pull/12174)) +- **build:** add contributor fast profile ([#12192](https://github.com/diegosouzapw/OmniRoute/pull/12192)) — thanks @rafacpti23 +- **docs(readme):** README touch-ups ([#12193](https://github.com/diegosouzapw/OmniRoute/pull/12193)) ([#12194](https://github.com/diegosouzapw/OmniRoute/pull/12194)) ([#12202](https://github.com/diegosouzapw/OmniRoute/pull/12202)) — thanks @rafacpti23 +- **build:** force the webpack bundler in the contributor build profile ([#12197](https://github.com/diegosouzapw/OmniRoute/pull/12197)) — thanks @rafacpti23 +- **build:** skip standalone packaging in the contributor profile ([#12198](https://github.com/diegosouzapw/OmniRoute/pull/12198)) — thanks @rafacpti23 +- **docs(audit):** align every published number with the code and harden check:docs-counts ([#12200](https://github.com/diegosouzapw/OmniRoute/pull/12200)) +- **build:** stub the instrumentation entrypoints in the contributor profile ([#12203](https://github.com/diegosouzapw/OmniRoute/pull/12203)) — thanks @rafacpti23 +- **build:** omit the standalone output target for contributor builds ([#12204](https://github.com/diegosouzapw/OmniRoute/pull/12204)) — thanks @rafacpti23 +- **chore(lint):** batch 6 of #12146 — memory, radar, audit, analytics, cache, usage, activity, home and RequestLoggerV2 react-hooks violations resolved ([#12208](https://github.com/diegosouzapw/OmniRoute/pull/12208)) +- **docs(diagrams):** rename number-carrying diagram files to stable names ([#12210](https://github.com/diegosouzapw/OmniRoute/pull/12210)) +- **docs(dashboard):** redraw onboarding tier-flow SVGs for the real 4-tier model ([#12211](https://github.com/diegosouzapw/OmniRoute/pull/12211)) +- **docs(api):** document every implemented route in openapi.yaml (276 → 692 paths) ([#12212](https://github.com/diegosouzapw/OmniRoute/pull/12212)) +- **chore(lint):** batch 7 of #12146 — final src tail: 37 react-hooks violations across 33 files resolved ([#12244](https://github.com/diegosouzapw/OmniRoute/pull/12244)) +- Revert "docs: recommend gstack for AI-assisted workflows (#11770)" ([#12248](https://github.com/diegosouzapw/OmniRoute/pull/12248)) ([#12249](https://github.com/diegosouzapw/OmniRoute/pull/12249)) +- **docs:** Chaos Mode setup guide + weighted strategy semantics ([#12250](https://github.com/diegosouzapw/OmniRoute/pull/12250)) +- **docs(agents):** protected-surface merge rule — operator approval for agent-instruction files ([#12253](https://github.com/diegosouzapw/OmniRoute/pull/12253)) +- **chore(quality):** register native-codex turn-pin tests in stryker tap.testFiles ([#12263](https://github.com/diegosouzapw/OmniRoute/pull/12263)) +- **chore(quality):** dedupe tap.testFiles entries added by racing base-red fixes ([#12265](https://github.com/diegosouzapw/OmniRoute/pull/12265)) +- **docs(readme):** introduce OmniRouteTray — the macOS menu-bar companion ([#12276](https://github.com/diegosouzapw/OmniRoute/pull/12276)) — thanks @ggiak +- **chore(proxy):** purge the legacy 1proxy residue — sync/rotator modules, dead DB exports, dead settings tab and flag, docs (#12091) ([#12290](https://github.com/diegosouzapw/OmniRoute/pull/12290)) +- **chore(quality):** baseline-headroom skips generated and vendored files in the fileSize worst-file signal ([#12291](https://github.com/diegosouzapw/OmniRoute/pull/12291)) +- **refactor(video):** unify the JPEG frame data-URI contract ([#12322](https://github.com/diegosouzapw/OmniRoute/pull/12322)) +- **chore(deps):** freeze onnxruntime-node and eslint-plugin-react-hooks out of dependabot groups ([#12329](https://github.com/diegosouzapw/OmniRoute/pull/12329)) +- **test(executors):** restore the #10986 reasoning-only fallback guards ([#12364](https://github.com/diegosouzapw/OmniRoute/pull/12364)) — thanks @pacocartones +- **chore(quality):** re-tighten the file-size ratchet to the real LOC (plan 3.8.52 task 0) ([#12411](https://github.com/diegosouzapw/OmniRoute/pull/12411)) +- **test(grok):** format web executor suite ([#12412](https://github.com/diegosouzapw/OmniRoute/pull/12412)) +- **chore(quality):** ratchet open-sse typecheck baseline to zero ([#12418](https://github.com/diegosouzapw/OmniRoute/pull/12418)) +- **test(build):** guard the artifact path policy arrays against duplicates ([#12422](https://github.com/diegosouzapw/OmniRoute/pull/12422)) +- **chore(lint):** adopt eslint-plugin-react-hooks 7.1.1 ([#12428](https://github.com/diegosouzapw/OmniRoute/pull/12428)) +- **chore(providers):** remove a keyless provider integration at its operator's request ([#12440](https://github.com/diegosouzapw/OmniRoute/pull/12440)) +- **docs(providers):** register providers removed at their operator's request and guard against reintroduction ([#12478](https://github.com/diegosouzapw/OmniRoute/pull/12478)) +- **chore(quality):** base fixes — stryker tap.testFiles + node_modules cache key ([#12482](https://github.com/diegosouzapw/OmniRoute/pull/12482)) +- **docs(arch):** one-process recipe for tens of long /v1/responses ([#12493](https://github.com/diegosouzapw/OmniRoute/pull/12493)) — thanks @RaviTharuma +- **chore(quality):** tighten the CodeQL ratchet baseline from 11 to 6 ([#12530](https://github.com/diegosouzapw/OmniRoute/pull/12530)) +- **chore(deps):** pin browserslist override to ^4.28.8 ([#12592](https://github.com/diegosouzapw/OmniRoute/pull/12592)) — thanks @KooshaPari +- **chore(deps):** bump fast-uri to 3.1.7 in the electron lockfile ([#12601](https://github.com/diegosouzapw/OmniRoute/pull/12601)) +- **docs(quality):** document how the CodeQL ratchet refreshes and how to tighten it ([#12611](https://github.com/diegosouzapw/OmniRoute/pull/12611)) +- **test(ci):** pin the openapi-security-tiers two-arm contract with an executing gate test (#12581) ([#12652](https://github.com/diegosouzapw/OmniRoute/pull/12652)) +- **docs(gamification):** align XP Rewards table with code (#12501) ([#12667](https://github.com/diegosouzapw/OmniRoute/pull/12667)) — thanks @KooshaPari +- **refactor(combo):** split executeTarget into gates, attempt, and loop ([#12746](https://github.com/diegosouzapw/OmniRoute/pull/12746)) — thanks @HouMinXi +- **docs(security):** document socket.yml scanner config + CI workflow link (#12575) ([#12764](https://github.com/diegosouzapw/OmniRoute/pull/12764)) — thanks @KooshaPari +- **chore(ci):** guard commit identity in pre-commit to stop author misattribution ([#12772](https://github.com/diegosouzapw/OmniRoute/pull/12772)) +- **deps:** 17 Dependabot bumps — bump electron from 43.4.0 to 43.4.1 in /electron ([#11426](https://github.com/diegosouzapw/OmniRoute/pull/11426)); bump the development group across 1 directory with 8 updates ([#11428](https://github.com/diegosouzapw/OmniRoute/pull/11428)); bump the production group across 1 directory with 11 updates ([#11440](https://github.com/diegosouzapw/OmniRoute/pull/11440)); chore(deps): bump github/codeql-action/init from 4.37.7 to 4.37.8 ([#11925](https://github.com/diegosouzapw/OmniRoute/pull/11925)); chore(deps): bump github/codeql-action from 4.37.7 to 4.37.8 ([#11926](https://github.com/diegosouzapw/OmniRoute/pull/11926)); chore(deps): bump github/codeql-action/analyze from 4.37.7 to 4.37.8 ([#11927](https://github.com/diegosouzapw/OmniRoute/pull/11927)); build(deps): bump github/codeql-action/init from 4.37.8 to 4.37.9 ([#12345](https://github.com/diegosouzapw/OmniRoute/pull/12345)); build(deps): bump github/codeql-action/analyze from 4.37.8 to 4.37.9 ([#12346](https://github.com/diegosouzapw/OmniRoute/pull/12346)); bump the development group across 1 directory with 2 updates ([#12347](https://github.com/diegosouzapw/OmniRoute/pull/12347)); build(deps): bump github/codeql-action from 4.37.8 to 4.37.9 ([#12349](https://github.com/diegosouzapw/OmniRoute/pull/12349)); bump browserslist from 4.28.2 to 4.28.8 ([#12396](https://github.com/diegosouzapw/OmniRoute/pull/12396)); bump the production group across 1 directory with 4 updates ([#12399](https://github.com/diegosouzapw/OmniRoute/pull/12399)); bump @xmldom/xmldom ([#12500](https://github.com/diegosouzapw/OmniRoute/pull/12500)); bump qs from 6.15.2 to 6.16.0 ([#12512](https://github.com/diegosouzapw/OmniRoute/pull/12512)); bump @xmldom/xmldom from 0.9.10 to 0.9.12 ([#12513](https://github.com/diegosouzapw/OmniRoute/pull/12513)); bump fast-uri from 3.1.5 to 3.1.7 ([#12514](https://github.com/diegosouzapw/OmniRoute/pull/12514)); bump @humanfs/node from 0.16.7 to 0.16.8 ([#12515](https://github.com/diegosouzapw/OmniRoute/pull/12515)) +- Clear the `release/v3.8.51` `check:agent-skills-sync` base-red: regenerate `skills/cli-tunnel/SKILL.md` so the `tunnel create [type]` positional that #13009 taught the generator to read is reflected in the committed skill. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` typecheck base-red from #12731: the new `Mode pack ranking gates` candidates in `open-sse/services/autoCombo/__tests__/autoCombo.test.ts` omitted the required `provider`, `model` and `errorRate` fields of `ProviderCandidate`, failing `check:open-sse-typecheck` (and with it `Fast Quality Gates`) on every open PR. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` docs-sync base-red: EURouter (#13025) and GreenPT (#13024) took the live provider count to 358, leaving 7 STRICT drifts (`PROVIDER_REFERENCE.md`, 4 diagrams, 2 tier-flow images) plus the `AGENTS.md` / `llm.txt` / `package.json` count claims stale. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- **test(models):** Pin `CATALOG_BUILD_TIMEOUT_MS` in the #12058 canonical-catalog regression so a cold catalog build racing the 8s default bound no longer flakes the suite with a 500 ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Allow `@omniroute/opencode-plugin-v2/` in the npm pack policy — [#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870) shipped the v2 plugin beside its v1 sibling without widening the allowlist, so every packed file under it read as an unexpected artifact and failed `check:pack-policy` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Point the release-green validator's pack gate at the tree it is validating — the build-provenance guard defaulted to `origin/main`, which a release branch tip can never be an ancestor of mid-cycle, so `Package artifact` reported a false red on every run and the tarball boot-smoke cascaded off it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **docs(checks):** keep doc counts honest — headings, rankings, catalog, weights, quality gate and scoring diagram now covered ([#12507](https://github.com/diegosouzapw/OmniRoute/pull/12507)) — thanks @maxmad64bis +- **chore(lifecycle):** `check:model-lifecycle` now also diffs `DEFAULT_DEGRADATION_MAP` (the background-task redirect table) against the vendor lifecycle snapshot, refusing a retired id as source or target, with a table-driven unit test beside it. Three rows whose source the vendor had retired — `claude-sonnet-4-20250514`, `gemini-3-pro-preview` and `gpt-5.1-codex` (whose target `gpt-5.1-codex-mini` is retired too) — were dead code, since `checkLifecycle` answers 410 before the redirect runs; they are dropped (#12535 — thanks @pacocartones) +- **test(compression):** cover idle worker eviction at the resource level — the pool must call `terminate()` and must not retain the worker's `MessagePort`, complementing the `exit`-event assertion added with the fix ([#13371](https://github.com/diegosouzapw/OmniRoute/pull/13371)) +- **chore(quality):** raise the `imageGeneration.ts` file-size ceiling for the image-only-model guard that clears the #12945 base-red ([#13392](https://github.com/diegosouzapw/OmniRoute/pull/13392)) +- **chore(quality):** type the combo-test route's JSON response bodies instead of casting them to `any`, drop the now-empty suppression entry, and remove the `hasPerModelQuota` import `chatCore.ts` stopped using when the failure-classification helper was extracted ([#13379](https://github.com/diegosouzapw/OmniRoute/pull/13379)) +- **chore(electron):** the desktop bundle no longer ships the root-level files of every translated docs mirror (`README.md`, `llm.txt`, `CLAUDE.md`, `GEMINI.md`, `CONTRIBUTING.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`) — the packaged app only reads `docs/i18n//docs/**`, which stays. Saves ~11 MB on top of the translated CHANGELOGs already pruned. (#0000) ([#13235](https://github.com/diegosouzapw/OmniRoute/pull/13235)) +- **chore(quality):** retighten the `src/sse/handlers/chat.ts` unused-vars suppression count to the 8 violations that actually remain ([#13385](https://github.com/diegosouzapw/OmniRoute/pull/13385)) +- Bring the documented counts back in line with the code: 171 migrations (was 169) and 20 routing strategies (was 19), plus the regenerated `cli-tunnel` skill reference. ([#12970](https://github.com/diegosouzapw/OmniRoute/pull/12970)) +- Update the documented migration count to 172 after the call-logs provider-stats indexes landed. ([#13209](https://github.com/diegosouzapw/OmniRoute/pull/13209)) +- **chore(ci):** two gates that close the blind spots behind the exclusions above. `check:vitest-exclusions` requires every Vitest exclusion to name a tracking issue and to appear in `config/quality/vitest-exclusions.json` — the previous list grew to 62 files behind a comment pointing at an issue that had been closed for a month. `check-new-key-coverage` requires a key newly added to `en.json` to reach every locale; the existing coverage gate is a percentage floor per locale, so eleven absent keys out of ~13,000 left it at 99.9% while a whole feature shipped untranslated in nine languages. Both are diff-aware, so pre-existing debt stays frozen and neither needed a migration to turn on. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **build:** root layout no longer loads Inter from `next/font/google`, so a production image build does not need fonts.googleapis.com ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **docs(i18n):** bring the eight new `llm.txt` mirrors (km, kn, ml, my, ne, or, pa, si) up to the current provider and migration counts, so `check-docs-sync` stops rejecting every commit ([#13674](https://github.com/diegosouzapw/OmniRoute/pull/13674)) +- **refactor(ui):** flow surfaces (home topology, combo live studio, compression cockpit/waterfall/nodes, token-health badge) express state through the theme-aware `--orch-status-*` tokens instead of fixed dark-mode hex, so green/red/amber/grey stay legible in the light theme; dark mode is byte-identical. Categorical palettes (routing-strategy hues, compression-layer pills, provider brand colors) deliberately stay hex ([#12378](https://github.com/diegosouzapw/OmniRoute/issues/12378)) ([#13203](https://github.com/diegosouzapw/OmniRoute/pull/13203)) +- **chore(tests):** 51 test files that had been excluded from Vitest are running again, restoring roughly 350 assertions to the blocking suite. Each was measured individually first: of the 62 files parked behind the `// #8618 — pre-existing failure` comment, 51 pass against the current tree with no source change, so the exclusions had outlived the failures they were added for. The 11 that genuinely still fail stay excluded, but now point at a live tracker (#13204) rather than at #8618, which was closed in August while the list it tracked kept growing. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **chore(quality):** drop two ESLint suppression entries whose violations no longer exist, so `eslint --suppressions-location` stops rejecting every commit that touches the surrounding files ([#13384](https://github.com/diegosouzapw/OmniRoute/pull/13384)) +- **chore(release):** bring main's post-tag fixes into release/v3.8.51 — sync-back step 2/2 (direct commit `21c488f210`) +- **chore(deps):** patch toml and esbuild in the opencode plugin lockfiles ([#12965](https://github.com/diegosouzapw/OmniRoute/pull/12965)) +- **docs(changelog):** reconcile the v3.8.51 living section — fold 366 fragments, cover every cycle commit, credit every contributor ([#12971](https://github.com/diegosouzapw/OmniRoute/pull/12971)) +- **test(plugin):** wait for the v2 lazy-refresh tier instead of sleeping past it ([#12984](https://github.com/diegosouzapw/OmniRoute/pull/12984)) +- **chore(quality):** rebaseline chatCore.ts after the non-streaming regression fixes ([#13045](https://github.com/diegosouzapw/OmniRoute/pull/13045)) +- **docs(changelog):** add the missing bullet marker to the DeepSeek PoW fragment ([#13200](https://github.com/diegosouzapw/OmniRoute/pull/13200)) +- **chore(deps):** drain the Dependabot queue — 10 of 13 alerts ([#13213](https://github.com/diegosouzapw/OmniRoute/pull/13213)) +- **chore(ci):** make worktrees carry the hooks that enforce the identity gate ([#13236](https://github.com/diegosouzapw/OmniRoute/pull/13236)) +- **chore(deps):** pin csv-parse to ^7.0.2 via overrides (#13117) ([#13300](https://github.com/diegosouzapw/OmniRoute/pull/13300)) — thanks @KooshaPari +- **chore(deps):** bump hono from ^4.12.34 to ^4.13.7 (#13148) ([#13301](https://github.com/diegosouzapw/OmniRoute/pull/13301)) — thanks @KooshaPari +- **chore(deps):** pin joi to ^18.2.8 via overrides (#13085) ([#13302](https://github.com/diegosouzapw/OmniRoute/pull/13302)) — thanks @KooshaPari +- **docs(providers):** add Microsoft 365 Copilot (BizChat) provider guide (#12779) ([#13340](https://github.com/diegosouzapw/OmniRoute/pull/13340)) — thanks @KooshaPari +### 🙌 Contributors + +Thanks to everyone whose work landed in v3.8.51: + +| Contributor | PRs / Issues | +| --- | --- | +| [@5dive-bot](https://github.com/5dive-bot) | #11852 | +| [@abhisheksharma2411](https://github.com/abhisheksharma2411) | #11547, #11684, #11959, #12209 | +| [@adevwithpurpose](https://github.com/adevwithpurpose) | #11464 | +| [@adivekar-utexas](https://github.com/adivekar-utexas) | #12015, #12043, #12090, #12138 | +| [@AIB1TAL0S](https://github.com/AIB1TAL0S) | #12103 | +| [@alltomatos](https://github.com/alltomatos) | #11448 | +| [@alvinveroy](https://github.com/alvinveroy) | #11857, #12027, #12165 | +| [@amaleta](https://github.com/amaleta) | #11906, #11934 | +| [@amartinawi](https://github.com/amartinawi) | #11575 | +| [@AndrianBalanescu](https://github.com/AndrianBalanescu) | #11083, #11830, #11834, #11844 | +| [@anhtahaylove](https://github.com/anhtahaylove) | #13091, #13092, #13093, #13096, #13097, #13100, #13106, #13109, #13114, #13155, #13166, #13171, #13175, #13187, #13196 | +| [@aniruddhaadak80](https://github.com/aniruddhaadak80) | #11760 | +| [@app](https://github.com/app) | #12554 | +| [@arminanton](https://github.com/arminanton) | #11461, #11513 | +| [@AStupidBear](https://github.com/AStupidBear) | #11584 | +| [@atescivitci-cmd](https://github.com/atescivitci-cmd) | #12105 | +| [@b3nw](https://github.com/b3nw) | #11970, #11971, #12147 | +| [@backryun](https://github.com/backryun) | #11259, #11950, #12075, #12076, #12078, #12079, #12081, #12082, #12181, #12228, #12239, #12255, #12258, #12277, #12367, #12423, #12524, #12538 | +| [@benjaminkitt](https://github.com/benjaminkitt) | #11747 | +| [@benzntech](https://github.com/benzntech) | #11614, #11615 | +| [@Bl0ck154](https://github.com/Bl0ck154) | #11948, #11951, #11952, #11953, #11954 | +| [@brick30llc-ctrl](https://github.com/brick30llc-ctrl) | #11752, #12242, #12262 | +| [@Chewji9875](https://github.com/Chewji9875) | #11455, #11875, #12028 | +| [@cheynetom](https://github.com/cheynetom) | #12058 | +| [@davidebaraldo](https://github.com/davidebaraldo) | #12222, #12304 | +| [@davidlinfr](https://github.com/davidlinfr) | #6390 | +| [@ddarkr](https://github.com/ddarkr) | #12124 | +| [@Deftera186](https://github.com/Deftera186) | #11809 | +| [@dpozimski](https://github.com/dpozimski) | #11945 | +| [@DW-MediaLab](https://github.com/DW-MediaLab) | #12229 | +| [@echel0nn](https://github.com/echel0nn) | #11923 | +| [@f9td56dbgh-hub](https://github.com/f9td56dbgh-hub) | #11660 | +| [@fabioluissilva](https://github.com/fabioluissilva) | #11991 | +| [@foreveryh](https://github.com/foreveryh) | #12177 | +| [@geek007git](https://github.com/geek007git) | #12115, #12116, #12117, #12120, #12122 | +| [@geekyNads](https://github.com/geekyNads) | #11784, #12266 | +| [@ggiak](https://github.com/ggiak) | #11561, #12276, #12350, #12402 | +| [@gonisulaimann](https://github.com/gonisulaimann) | #12368, #12369, #12371 | +| [@Gorillaz322](https://github.com/Gorillaz322) | #12207 | +| [@groovecityJO](https://github.com/groovecityJO) | #12682 | +| [@hartmark](https://github.com/hartmark) | #11434, #11452, #11473, #11499, #11703, #11983, #11984, #11985, #11986, #11988, #11989, #11990, #11994, #12221, #12293, #12445, #12446, #12447, #12448, #12460, #12461, #12623, #12646, #12650, #12680, #12717, #12718, #12727, #12741, #12854 | +| [@hizzt](https://github.com/hizzt) | #11894 | +| [@hongnoul](https://github.com/hongnoul) | #11484 | +| [@HouMinXi](https://github.com/HouMinXi) | #11411, #11414, #11512, #11518, #11520, #11641, #11642, #11643, #11687, #11779, #11849, #11850, #11851, #11915, #11916, #11918, #11919, #11920, #12013, #12017, #12033, #12042, #12106, #12139, #12166, #12169, #12171, #12205, #12213, #12312, #12325, #12487, #12488, #12495, #12504, #12557, #12566, #12590, #12591, #12624, #12626, #12632, #12637, #12678, #12696, #12697, #12711, #12733, #12746, #12767, #12770, #12789, #12803, #12805, #12811, #12866, #12868, #12899, #12926, #12934, #12950, #12951, #12974, #13001, #13006, #13011, #13017, #13026, #13027, #13034, #13035, #13038, #13042, #13050, #13060, #13061, #13069, #13107, #13120, #13136, #13195, #13197 | +| [@Hsia97](https://github.com/Hsia97) | #11624 | +| [@jacobsparts](https://github.com/jacobsparts) | #11854, #12155, #12167 | +| [@joglomedia](https://github.com/joglomedia) | #11980 | +| [@jonlwheat2-gif](https://github.com/jonlwheat2-gif) | #11443, #11450, #11549, #11567, #11608, #11633, #11635, #11640, #11644, #11671, #11672, #11673, #11674, #11675, #11676, #11677, #11706, #11783, #12051, #12052, #12053, #12055 | +| [@kanade-hoshino](https://github.com/kanade-hoshino) | #12180 | +| [@Karan825](https://github.com/Karan825) | #11861 | +| [@KaspaPulse](https://github.com/KaspaPulse) | #11389, #11469, #11628, #11666, #11910 | +| [@keeltrace](https://github.com/keeltrace) | #12080, #12223 | +| [@killer30001000](https://github.com/killer30001000) | #12178 | +| [@KooshaPari](https://github.com/KooshaPari) | #12470, #12592, #12667, #12699, #12703, #12706, #12764, #12769, #12771, #13300, #13301, #13302, #13340, #13401, #13403, #13404, #13405, #13406, #13407, #13409, #13410, #13411, #13412, #13413, #13414, #13418, #13424, #13427, #13433, #13523, #13524, #13525, #13528, #13531, #13532, #13534, #13539, #13543, #13545, #13546, #13547, #13550, #13553 | +| [@kriptoburak](https://github.com/kriptoburak) | #11370 | +| [@KrzysiekSko](https://github.com/KrzysiekSko) | #12425, #12432, #12673 | +| [@marcelokarval](https://github.com/marcelokarval) | direct commit / report | +| [@maxmad64bis](https://github.com/maxmad64bis) | #1622, #11435, #11437, #11441, #11537, #11550, #11553, #11555, #11557, #11812, #11842, #11843, #11903, #12151, #12214, #12215, #12218, #12226, #12314, #12316, #12317, #12318, #12319, #12320, #12321, #12507, #12715, #12731, #12744, #12786, #12787, #12788, #12790, #12792, #12794, #12795, #12828, #12832, #12853, #12857, #12870, #12937, #12941, #12975, #13141, #13142, #13146, #13147 | +| [@mdigitalbh81](https://github.com/mdigitalbh81) | #12240 | +| [@Meet6338-X](https://github.com/Meet6338-X) | #11598, #11609, #12110 | +| [@morpheus9393](https://github.com/morpheus9393) | #11943 | +| [@MumuTW](https://github.com/MumuTW) | #11492, #11502, #11506, #11507, #11626, #11675, #11685, #11728, #11746, #11888, #11889, #11890, #11892 | +| [@Neuron-Mr-White](https://github.com/Neuron-Mr-White) | #11622, #11801, #12256 | +| [@NightStalker-87](https://github.com/NightStalker-87) | #12183 | +| [@NoxzRCW](https://github.com/NoxzRCW) | #11879, #11880, #11881, #11882, #11883, #11935 | +| [@ntdat812](https://github.com/ntdat812) | #11585, #12095 | +| [@ntdatt812](https://github.com/ntdatt812) | #11368, #11573, #11574, #11576, #11577, #11580, #11582, #11583, #11588, #11589, #11590, #11591, #11592, #11593, #11672, #12177, #12180, #12873, #12918, #12920, #12921, #12925, #12930, #13007, #13009, #13024, #13025, #13083, #13087, #13101, #13104, #13110 | +| [@official-burak](https://github.com/official-burak) | #11542 | +| [@opensource-elearning](https://github.com/opensource-elearning) | #12179, #12189, #12278, #12286 | +| [@oyi77](https://github.com/oyi77) | #11408, #11409, #11421, #11505, #11677, #12036, #12110 | +| [@pacocartones](https://github.com/pacocartones) | #11521, #11522, #11527, #11528, #11529, #11530, #11531, #11532, #11533, #11534, #11595, #11599, #11603, #11604, #11605, #11607, #11610, #11676, #11714, #11716, #11718, #11767, #11838, #11860, #11862, #11869, #11871, #11872, #11873, #11903, #11906, #11921, #11934, #12356, #12358, #12359, #12360, #12361, #12362, #12364, #12365, #12373, #12374, #12375, #12376, #12377, #12379, #12380, #12381, #12386, #12387, #12389, #12390, #12394, #12395, #12397, #12401, #12403, #12404, #12522, #12523, #12535, #12536, #12540, #12541, #12543, #12545, #12548, #12549, #12550, #12551, #12552, #12647, #12651, #12653, #13055 | +| [@patrykkopycinski](https://github.com/patrykkopycinski) | #11936, #11937, #12224 | +| [@PauloFH](https://github.com/PauloFH) | #11509 | +| [@PauloHSOliveira](https://github.com/PauloHSOliveira) | #12241 | +| [@ponkcore](https://github.com/ponkcore) | #12054 | +| [@Prajeeth-12](https://github.com/Prajeeth-12) | #11634, #12046 | +| [@quiterunner-commits](https://github.com/quiterunner-commits) | #12131, #12143 | +| [@rafacpti23](https://github.com/rafacpti23) | #11554, #11558, #12192, #12197, #12198, #12202, #12203, #12204 | +| [@ragnar-claude](https://github.com/ragnar-claude) | #11564 | +| [@raheemuddin786](https://github.com/raheemuddin786) | #11491, #11839, #11840, #11841, #12003, #12230, #12231, #12232, #12233, #12234 | +| [@rahilmavani](https://github.com/rahilmavani) | #11761 | +| [@Rahulsharma0810](https://github.com/Rahulsharma0810) | #11771 | +| [@RaviTharuma](https://github.com/RaviTharuma) | #11710, #11727, #11797, #11798, #11802, #11805, #11806, #11811, #12098, #12099, #12101, #12449, #12452, #12472, #12473, #12493, #12533, #12607, #12628, #12631, #12636, #12875, #12876, #12880, #12882, #12884 | +| [@rezjalibd](https://github.com/rezjalibd) | #12186 | +| [@RhianB14](https://github.com/RhianB14) | #11692 | +| [@rifqiawl](https://github.com/rifqiawl) | #11517, #11519, #11969 | +| [@rqzbeh](https://github.com/rqzbeh) | #11390 | +| [@Sabeekhann](https://github.com/Sabeekhann) | #11699, #11705 | +| [@santosraju99-hub](https://github.com/santosraju99-hub) | #11755, #11770 | +| [@Seramicx](https://github.com/Seramicx) | #11568 | +| [@Siva010](https://github.com/Siva010) | #12191 | +| [@solstxce](https://github.com/solstxce) | #11597 | +| [@soroush5](https://github.com/soroush5) | #12691 | +| [@tenshiak](https://github.com/tenshiak) | #12279 | +| [@TheDemonTuan](https://github.com/TheDemonTuan) | #11468, #11470, #11471, #11482, #11548, #11758, #11775, #11814 | +| [@thomasmaerz](https://github.com/thomasmaerz) | #12834 | +| [@tuandinh0801](https://github.com/tuandinh0801) | #11454 | +| [@turbolego](https://github.com/turbolego) | #11621, #11762, #11772, #11774, #11781, #12216 | +| [@Tushar49](https://github.com/Tushar49) | #11392 | +| [@ujjawalkaushik1110](https://github.com/ujjawalkaushik1110) | #11863 | +| [@vermasomesh835](https://github.com/vermasomesh835) | #11794 | +| [@vsd2807](https://github.com/vsd2807) | #11565, #11619 | +| [@wahidsadik371-coder](https://github.com/wahidsadik371-coder) | #12126 | +| [@watchingdogs](https://github.com/watchingdogs) | #12031 | +| [@wildcard](https://github.com/wildcard) | #11620 | +| [@xiaoyaner0201](https://github.com/xiaoyaner0201) | #11460, #11662, #12083 | +| [@Xxx91n](https://github.com/Xxx91n) | #11690 | +| [@yourspraveen](https://github.com/yourspraveen) | #11146 | +| [@yxyxy](https://github.com/yxyxy) | #11671 | +| [@ZaimMarzuki](https://github.com/ZaimMarzuki) | #11960 | +| [@Zartharas](https://github.com/Zartharas) | #11340 | +| [@zero-executioner](https://github.com/zero-executioner) | #11631 | +| [@diegosouzapw](https://github.com/diegosouzapw) | maintainer | + +--- ## [3.8.50] — 2026-08-25 _Living section — regenerated 2026-08-12 from all cycle commits (cycle open `ed2db6cb19` → tip). Bullets carry the merged PR and its author; direct pushes listed separately._ diff --git a/docs/i18n/de/CHANGELOG.md b/docs/i18n/de/CHANGELOG.md index c955135b1b..c9b1b61073 100644 --- a/docs/i18n/de/CHANGELOG.md +++ b/docs/i18n/de/CHANGELOG.md @@ -8,16 +8,1119 @@ ## [3.8.51] — TBD -_Living section — cycle opened at the v3.8.50 freeze (parallel-cycle model). Bullets are aggregated from `changelog.d/` fragments at each `/generate-release` phase._ +_Living section — reconciled 2026-09-15 from all cycle commits (`091589089c` → `c0f92ec98a`, 916 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 | **123** | +| 📝 Commits in the cycle | **916** | +| 🔀 Pull requests referenced | **892** | +| 📋 Changelog entries | **919** | +| 🙌 Contributors credited in entries | **121** | +| 🤖 Automated dependency commits | 18 | + +**Entries by type** + +| Type | Count | +| --- | ---: | +| 🐛 Fixes | 592 | +| ✨ Features | 131 | +| 🧹 Chore | 93 | +| 📚 Docs | 42 | +| 🧪 Tests | 32 | +| ♻️ Refactor | 8 | +| 🏗️ Build | 7 | +| ⚡ Performance | 7 | +| ⚙️ CI | 2 | +| ⏪ Reverts | 2 | +| 📦 Dependencies | 2 | +| 🔒 Security | 1 | + +### 🏆 Top 25 contributors this cycle + +_By commits in `091589089c..c0f92ec98a`, author identities consolidated via `.mailmap` and the merged PR's GitHub login. Bots excluded._ + +| # | Contributor | Commits | +| ---: | --- | ---: | +| 🥇 | diegosouzapw | 323 | +| 🥈 | Bob.Hou (@HouMinXi) | 79 | +| 🥉 | Paco Cartones (@pacocartones) | 77 | +| 4 | Dizzle (@maxmad64bis) | 49 | +| 5 | Koosha Paridehpour (@KooshaPari) | 43 | +| 6 | Nguyen Thanh Dat (@ntdatt812) | 31 | +| 7 | Ravi Tharuma (@RaviTharuma) | 26 | +| 8 | Markus Hartung (@hartmark) | 22 | +| 9 | Webman (@jonlwheat2-gif) | 22 | +| 10 | backryun | 17 | +| 11 | anhtahaylove | 15 | +| 12 | Rafa Martins (@rafacpti23) | 10 | +| 13 | Syed Raheemuddin (@raheemuddin786) | 10 | +| 14 | Nguyễn Viết Tuấn (@TheDemonTuan) | 8 | +| 15 | MumuTW | 7 | +| 16 | Paijo (@oyi77) | 7 | +| 17 | Tobias Andersen (@turbolego) | 6 | +| 18 | Bl0ck (@Bl0ck154) | 5 | +| 19 | SHANMUGAPRIYAN (@geek007git) | 5 | +| 20 | KaspaPulse | 5 | +| 21 | Abhishek Sharma (@abhisheksharma2411) | 4 | +| 22 | Abhishek Divekar (@adivekar-utexas) | 4 | +| 23 | Andrew B. (@AndrianBalanescu) | 4 | +| 24 | NoxzRCW | 4 | +| 25 | opensource-elearning | 4 | ### ✨ New Features +- **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). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- Added Google AI Studio Gemini batch text-to-speech support through `POST /v1/audio/speech`. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- 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. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(routing):** subscription-first auto groupings — `auto/subscription` routes only through plan-included connections with a documented hard-stop overage and fails closed on exhaustion, while `auto/thrifty` orders the pool `subscription → keyless → free → cheap → premium` and steps up one rung at a time as each is exhausted. Billing class comes from a curated per-connection catalog (uncurated is treated as metered, never plan-included), both reuse STRICT_ZERO_COST's per-connection verification, and a quota reading whose `resetAt` has passed is now refreshed regardless of TTL so routing returns to plan capacity as soon as it resets ([#11146](https://github.com/diegosouzapw/OmniRoute/pull/11146)) — thanks @yourspraveen +- **feat(providers):** publish a management-authenticated versioned web-session credential contract from OmniRoute's canonical browser credential metadata ([#11340](https://github.com/diegosouzapw/OmniRoute/pull/11340)) — thanks @Zartharas +- **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)) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add Xquik X search with typed results, credential validation, REST routing, and MCP selection ([#11370](https://github.com/diegosouzapw/OmniRoute/pull/11370)) — thanks @kriptoburak +- **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)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(dashboard):** surface durable exclusive managed leases in the existing Sessions view, keeping leased clients visible across idle gaps while marking connections with in-flight work as active ([#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389)) — thanks @KaspaPulse +- **build(bun):** allow Turbopack bundler flag on Bun 1.4+ with configurable Webpack fallback ([#11471](https://github.com/diegosouzapw/OmniRoute/pull/11471)) — thanks @TheDemonTuan +- feat(api): add an opt-in `modelVisibilityAllowlist`/`modelVisibilityDenylist` settings pair to curate exactly which models `/v1/models` advertises, mirrored into every `auto/*` combo candidate pool so a denied model cannot be routed to via combo selection either (#11481) ([#11997](https://github.com/diegosouzapw/OmniRoute/pull/11997)) +- **feat(rankings):** the Free Provider Rankings page shows what each provider actually served over the last 24 h. It ranked by ELO alone, which left a provider that answers every call with an error in first place; the usage data was already served by the API but never requested. A provider with too small a sample shows a dash, not a number ([#11546](https://github.com/diegosouzapw/OmniRoute/pull/11546)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **feat(guardrails):** enforce a bounded, deterministic contract for Video Bridge transcripts — 256 cues, 4096 input code units and 4 KiB UTF-8 per cue, 64 KiB total text, malformed-Unicode rejection, focus-window scoping, cross-source reconciliation with contributing-source metadata, and a structural provenance trust boundary so caller JSON can never self-assert `embedded`/`audio-bridge` provenance ([#11652](https://github.com/diegosouzapw/OmniRoute/issues/11652)) ([#12009](https://github.com/diegosouzapw/OmniRoute/pull/12009)) +- **feat(video):** orchestrate optional Video Bridge audio extraction and Audio Bridge STT behind a dual opt-in (operator setting AND per-request signal) — a new loopback-only broker `mode=audio` operation shares the frame path's exact process queue, deadline, AbortSignal, and byte budgets to extract a bounded mono 16 kHz PCM WAV from the same already-downloaded video, then reuses the existing Audio Bridge transcription boundary; provider segment timing is preserved when available and marked coarse otherwise, and every failure degrades to a visual-only-safe partial instead of throwing (#11654). ([#12012](https://github.com/diegosouzapw/OmniRoute/pull/12012)) +- Add a tenant-bound Video Bridge drill-down lifecycle on top of the existing secure cache substrate: opaque hashed handles (never raw session/video identifiers), preview/standard/detail multiresolution variants resampled on read, response pagination capped at 8 frames and 32 MiB, and a new authenticated `/api/v1/video-bridge/drilldown` consumer route that stays disabled for remote access by default and denies cross-key access with the same response as a nonexistent handle (no existence oracle). ([#12006](https://github.com/diegosouzapw/OmniRoute/pull/12006)) +- **test(video):** Add the Video Bridge FU-07/FU-09 promotion-evidence harness (#11656) — a frozen Zod manifest schema covering the 8 required scenario kinds (static scenes, rapid cuts, late facts, fades, blur, small text, close events, visual prompt injection) with a minimum of 3 repetitions per case, deterministic declarative fixture recipes (`videoBridgePromotionFixtures.ts`), a pure medians/p95 metrics aggregator, a pure FU-07/FU-09 promotion-verdict evaluator applying the ticket's exact thresholds (missing token usage always holds), a digest-only persistence layer that never retains raw media or raw model responses, and a versioned per-model promotion allowlist shipped empty with every model defaulting to `hold`. The FU-07/FU-09 promotion verdicts themselves remain HOLD — they require a real evidence run against real models on VPS 192.168.0.15. ([#12008](https://github.com/diegosouzapw/OmniRoute/pull/12008)) +- **feat(video bridge):** "embedded" transcript provenance can now be legitimately earned instead of merely asserted — a bounded, allowlisted (`mov_text`/`subrip`/`webvtt`) subtitle probe runs through the loopback-only Video Bridge broker (at most 2 streams, 10s subdeadline bounded by the request deadline, 256 KiB output, 4096-code-unit lines), normalized through a bounded, ReDoS-safe WebVTT parser and Zod-validated end to end. The adapter always resolves to an explicit `success`/`absent`/`transient_failure` outcome — a subtitle failure never breaks the visual description path, and only a fingerprint-verified broker response (never a caller-declared label) can produce embedded cues (#11659). ([#12011](https://github.com/diegosouzapw/OmniRoute/pull/12011)) +- Default new Antigravity-family connections (agy CLI imports and Antigravity OAuth connects) to model auto-sync, so live model discovery lands in the synced catalog and `/v1/models` picks up freshly released upstream models (e.g. Gemini 3.7 Flash tiers) without code changes. Existing connections keep their current setting; the per-connection dashboard toggle remains the opt-out. (#11685 — thanks @MumuTW) ([#11887](https://github.com/diegosouzapw/OmniRoute/pull/11887)) +- **feat(ui):** enable React Compiler (`reactCompiler: true` + `babel-plugin-react-compiler`) for automatic memoization at build time ([#11783](https://github.com/diegosouzapw/OmniRoute/pull/11783)) — thanks @jonlwheat2-gif +- **feat(providers):** add SeekAi (`seekai.cc`) as an OpenAI-compatible New-API gateway — catalog id `seekai` (alias `ska`), `https://seekai.cc/v1`, live `/v1/models` via `passthroughModels`, aggregator-list membership so New-API balance detection can opt in. No referral/aff codes. ([#11786](https://github.com/diegosouzapw/OmniRoute/issues/11786)) ([#12557](https://github.com/diegosouzapw/OmniRoute/pull/12557)) — thanks @HouMinXi +- **feat(zai):** add GLM-5.3-Flash Coding Plan support (1M context, 128K output, vision, `low|high|max` reasoning) and route `zai` GLM-5.3-family API-key traffic through the OpenAI-compatible Coding Plan endpoint with native thinking defaults ([#11801](https://github.com/diegosouzapw/OmniRoute/pull/11801)) — thanks @Neuron-Mr-White +- **feat(combo):** choose how combo models are ordered — manual, provider, score, or name — via a sort control in the dashboard builder, persisted in `config.modelSort` and re-applied on load and after add ([#11812](https://github.com/diegosouzapw/OmniRoute/pull/11812)) — thanks @maxmad64bis +- **feat(free):** custom models can be marked free-tier via `customModels[].isFree`; `isFreeModel()` is the first door and `hidePaidModels` respects it even for providers outside the free budget ([#11843](https://github.com/diegosouzapw/OmniRoute/pull/11843)) — thanks @maxmad64bis +- **feat(nodejs):** add `5dive` as a `configure` target — `omniroute configure 5dive` / `omniroute setup-5dive` write a 5dive auth profile that points an agent fleet's `claude` seats at OmniRoute, with the root-only write, the loopback-vs-`https` endpoint rule and the per-seat model pin handled explicitly ([#11852](https://github.com/diegosouzapw/OmniRoute/pull/11852)) — thanks @5dive-bot +- **feat(sse):** treat `max` as a first-class reasoning-effort tier and clamp per model family (GLM 5.1+/DeepSeek V4+/Kimi K3+ keep native `max`; o1/MiniMax/Grok/Muse Spark clamp to their upstream ceiling) ([#11875](https://github.com/diegosouzapw/OmniRoute/pull/11875)) — thanks @Chewji9875 +- **feat(providers):** the provider plugin manifest now advertises a `usage-fetch` capability for the 40 providers that have a wired usage/quota fetcher, so external dashboards can read it from `GET /api/v1/provider-plugin-manifest` instead of parsing `open-sse/services/usage.ts` after every release. Discovery only — no new fetcher, no quota change, and the Dashboard quota widget stays gated by `USAGE_SUPPORTED_PROVIDERS`. `USAGE_FETCHER_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/fetcherProviders.ts`) and is re-exported from `services/usage.ts`, keeping the manifest module a light leaf instead of pulling the ~490-module usage dispatcher into the manifest route. ([#11903](https://github.com/diegosouzapw/OmniRoute/pull/11903)) — thanks @maxmad64bis / @pacocartones +- **feat(plugins):** `OMNIROUTE_PLUGINS_DIR` sets the directory the runtime plugin scanner reads — and the root the plugin manager installs into — overriding the `HOME`-derived default, so a Docker/K8s deployment can point straight at its bind-mounted plugin tree instead of moving `HOME` just to relocate the scan path. An image that exports no home no longer scans `/tmp/.omniroute/plugins` in silence: the resolved directory is logged once at startup as `scanner.dir_resolved`, naming the input that won. Unset, behaviour is unchanged. Distinct from the CLI-only `OMNIROUTE_PLUGIN_PATH`, which finds `omniroute-cmd-*` command packages and never reached this scanner ([#11906](https://github.com/diegosouzapw/OmniRoute/pull/11906)) — thanks @amaleta / @pacocartones +- **feat(leases):** add an explicit owner-authenticated status action that returns only the active lease's privacy-safe configured connection and provider labels, with generation fencing and no credential or internal-id disclosure ([#11910](https://github.com/diegosouzapw/OmniRoute/pull/11910)) — thanks @KaspaPulse +- **feat(providers):** import-from-file modal shows per-row API errors and ships a downloadable CSV template ([#12071](https://github.com/diegosouzapw/OmniRoute/issues/12071)) ([#12504](https://github.com/diegosouzapw/OmniRoute/pull/12504)) — thanks @HouMinXi +- **feat(providers):** dashboard search matches connection name and `baseUrl` so imported OpenAI-compat nodes surface on the provider card ([#12108](https://github.com/diegosouzapw/OmniRoute/issues/12108)) ([#12495](https://github.com/diegosouzapw/OmniRoute/pull/12495)) — thanks @HouMinXi +- **feat(routing):** add a `score` Auto router strategy that selects the highest configured weighted score and reuses `explorationRate`. ([#12155](https://github.com/diegosouzapw/OmniRoute/pull/12155)) — thanks @jacobsparts +- Add a runtime feature flag to disable universal context handoffs globally without changing the default behavior. ([#12167](https://github.com/diegosouzapw/OmniRoute/pull/12167)) — thanks @jacobsparts +- **feat(providers):** the provider plugin manifest now also advertises a `usage-supported` capability for the 46 providers whose usage API is accepted by the server and Dashboard routes, so integrators can distinguish "the server will serve quota for this provider" from "a fetcher is wired" without reading TypeScript. Discovery only — no fetcher or quota change. `usage-fetch` resolves on id or alias (the usage dispatcher accepts both); `usage-supported` resolves on id alone, matching the runtime guard `USAGE_SUPPORTED_PROVIDERS.includes(providerId)`. `USAGE_SUPPORTED_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/supportedProviders.ts`) and is re-exported from `providers.ts`, mirroring the `fetcherProviders` leaf from #11903 and keeping the manifest a light module. ([#12214](https://github.com/diegosouzapw/OmniRoute/pull/12214)) — thanks @maxmad64bis +- **feat(rankings):** order Free Provider Rankings by what each provider actually served — `GET /api/free-provider-rankings?sortBy=reliability` and a "Most reliable first" toggle on the page. Providers with too few calls to state a success rate keep their score order below the measured ones; the default order is unchanged ([#12218](https://github.com/diegosouzapw/OmniRoute/pull/12218)). — thanks @maxmad64bis +- **perf(sse):** defer `cloneLogPayload()` in the structured SSE collector until after the `maxEvents`/`maxBytes` cap check, eliminating ~9,800 wasted `structuredClone` calls per streaming response (65–71% faster `push()`). Reducer snapshot isolation restored for OpenAI and Responses summaries ([#12241](https://github.com/diegosouzapw/OmniRoute/pull/12241)) — thanks @PauloHSOliveira ([#12243](https://github.com/diegosouzapw/OmniRoute/pull/12243)) +- **feat(usage):** Devin CLI agentic quota (Codeium seat-management GetUserStatus) and OpenRouter key limits plus account credits now surface in Provider Limits ([#12256](https://github.com/diegosouzapw/OmniRoute/pull/12256) — thanks @Neuron-Mr-White) +- **feat(settings):** persist `headroomUrl` through Settings so status/start use the operator URL instead of only `HEADROOM_URL` ([#12306](https://github.com/diegosouzapw/OmniRoute/issues/12306)) ([#12487](https://github.com/diegosouzapw/OmniRoute/pull/12487)) — thanks @HouMinXi +- **feat(auto-combo):** Auto-Combo scoring can now weigh how often a provider/model has actually succeeded. The engine already carried that number on every candidate — 24 hours of usage history behind a ten-sample floor, real-time metrics otherwise — and the scoring function never read it, while the weight table described `stability` as if it did. `reliability` (`1 - failureRate`, with the same field precedence and the same rate-bounding the speed ranking already uses, so a corrupt reading means "nothing observed" rather than "fails every call") is now a declared factor shipping at weight `0`, so routing is unchanged until an operator gives it one, and the `stability` description now matches what that factor computes ([#12317](https://github.com/diegosouzapw/OmniRoute/pull/12317)) — thanks @maxmad64bis +- **feat(routing):** With `freeAccessPolicy: "strict"`, the read-only candidate listing (`GET /v1/auto-combo/{channel}/candidates`) no longer hides the candidates the zero-cost guard excludes — the same read-only transparency the resilience filter already honours (#9133). Each candidate now carries `freeAccessExclusion` saying why it would be kept out, and it tells an exhausted allowance apart from a quota reading that never arrived or went stale, which used to look identical from the outside. Routing is unchanged: the listing reports, it never enforces. The separate `excludeTosAvoid` guard still drops its candidates without a reason; that gap is now documented rather than closed ([#12319](https://github.com/diegosouzapw/OmniRoute/pull/12319)) — thanks @maxmad64bis +- **feat(radar):** The Radar catalog table now shows two facts it was already receiving from the feed and dropping on the floor: the per-model rate limits (requests and tokens, per minute and per day) in a new column, and a badge when a provider's terms state it may train on the prompts you send. A limit of zero renders as zero rather than "rate-only" — for a ceiling those are opposite facts — and a model with no training statement gets no badge, because an absent statement is not a guarantee ([#12320](https://github.com/diegosouzapw/OmniRoute/pull/12320)) — thanks @maxmad64bis +- **feat(radar):** explain Community, single-use, contributor, supporter, recovery, abuse, offers, and privacy rules before either Radar activation action, and remove the superseded fixed-PR grant promise from every UI locale ([#12342](https://github.com/diegosouzapw/OmniRoute/pull/12342)) +- **feat(gamification):** the dashboard Profile page now shows the real daily streak — `/api/gamification/level` returns `streak: { current, longest }` (per key with `apiKeyId`, operator-wide maximum otherwise) and the streak card reads it instead of a hard-coded 0 (#2403) ([#12377](https://github.com/diegosouzapw/OmniRoute/pull/12377)) — thanks @pacocartones +- **feat(gamification):** the dashboard leaderboard now shows each API key's display name under the Name column instead of a truncated key id; `GET /api/gamification/leaderboard` attaches `name` per entry (name only — no key material), while the shared ranking helper and the federation leaderboard stay id-only — thanks @pacocartones ([#12385](https://github.com/diegosouzapw/OmniRoute/pull/12385)) +- **feat(gamification):** enforce the documented 1000 XP/min per-API-key anti-cheat rate limit on the XP award path; over-limit awards are logged and skipped instead of persisted, and the sliding window now matches the timestamp format stored in `xp_audit_log` ([#2403](https://github.com/diegosouzapw/OmniRoute/issues/2403)) ([#12390](https://github.com/diegosouzapw/OmniRoute/pull/12390)) — thanks @pacocartones +- **feat(admin):** localize the gamification anomalies page — the loading state, the Status column and the Suspicious badge now come from the `common` catalog (new `common.suspicious` key propagated to every locale) — add it to the Gamification sidebar group as `gamification-admin` (`/dashboard/gamification/admin`), and expose the loading and empty states as polite `role="status"` live regions (#12401 — thanks @pacocartones) +- **feat(providers):** skip GitHub combo members missing from the live synced catalog, and drop Copilot models that are policy-disabled or hidden from the model picker ([#12473](https://github.com/diegosouzapw/OmniRoute/pull/12473)) — thanks @RaviTharuma +- **feat(i18n):** locale-expansion tooling — `npm run i18n:add-locale` adds a language to config, dashboard, docs mirrors, CLI, README/indexes and site in one command; browser, CLI and cookie detection resolve `uk`, `fil`/`tl`, `zh-Hant` (and the retired legacy `in` → `id`) via config aliases; `config/i18n.json` now ships in the npm package so the published CLI can read it; new real-translation ratio gate (`npm run i18n:check-ratio`, advisory) with a per-locale ratchet baseline; `run-translation --adopt` restores `.i18n-state.json`; `sync-language-bars` generates 🌐 bars from config; `validate_translation.py` loads its allowlist again. Retires the duplicate `in` locale (Indonesian mislabelled as Hindi) — 42 honest locales; saved `NEXT_LOCALE=in` / `OMNIROUTE_LANG=in` keep working. (#12496) +- **feat(api):** Emit gateway-measured `tokens_per_second` (TTFT excluded) on streaming usage and `X-OmniRoute-Tokens-Per-Second` when first-token latency is known ([#12616](https://github.com/diegosouzapw/OmniRoute/issues/12616)) ([#12631](https://github.com/diegosouzapw/OmniRoute/pull/12631)) — thanks @RaviTharuma +- **feat(opencode-plugin): map gateway cost/usage/tok/s onto OpenCode inference payloads** — the official plugin copies `X-OmniRoute-Response-Cost`, token counts, `X-OmniRoute-Tokens-Per-Second` / `usage.tokens_per_second`, TTFT, and the winning `X-OmniRoute-Model` onto the JSON/SSE body OpenCode already consumes. Missing tok/s is left unset (never `tokens / latency`). (#12636) — thanks @RaviTharuma +- **feat(combo):** add combo strategy `quota-weighted`: skip exhausted accounts, then weighted-draw by leftover / in-flight load; existing conversations stay pinned until the account is empty ([#12789](https://github.com/diegosouzapw/OmniRoute/pull/12789)) — thanks @HouMinXi +- **feat(models):** Account live listings become the chat catalog source for Claude, Codex, Copilot, and AGY; public metadata only fills prices on IDs those accounts already list. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **fix(models):** Union `agy` and `antigravity` live catalogs so `agy/gemini-3.8-flash-high` is not rejected after the prefix folds to `antigravity`. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **feat(opencode):** Add `@omniroute/opencode-plugin-v2`, an opencode v2 plugin that gives a v2 install a route to an OmniRoute gateway: it publishes models, combos and auto-combos into the host catalog, refreshes them lazily behind a 300s TTL, and keeps serving the last known catalog from an on-disk snapshot when the gateway is unreachable. The v1 plugin (`@omniroute/opencode-plugin`) is untouched and keeps working as before. The plugin adapts to the host instead of assuming one shape: it reads the shape the host seeds into the catalog draft and publishes the `api` block, the pre-`api` top-level fields, or both. The gateway key comes from the host credential store when one is connected, so no secret needs to sit in a config file; a source the gateway refuses is named in a warning instead of silently degrading the catalog to raw model ids; and tool schemas bound for a Gemini model are stripped of the JSON-Schema keywords Gemini rejects, so tool calling survives that route ([#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870)) — thanks @maxmad64bis +- feat(services): show sanitized CLIProxyAPI account health from its authenticated management API without exposing credentials, file paths, or raw account metadata (#6342) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add AnySearch free web search + URL extract (webFetch) with typed results, credential validation, REST routing, and MCP selection - fallback-only ([#11690](https://github.com/diegosouzapw/OmniRoute/pull/11690)) — thanks @Xxx91n +- **feat(dashboard):** display clamped `[0, 100]%` cached input token ratio in request logs table ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) — thanks @b3nw +- **feat(catalog):** add `OMNIROUTE_DISABLE_THINKING_LEVEL_VARIANTS` feature flag to optionally filter out thinking level variants from model catalog ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) — thanks @b3nw +- **feat(dashboard):** continuously export call logs to external analytics stores. A pluggable destination registry ships the full Logs-tab record set on an hourly `JobRegistry` cron, with a persisted per-destination cursor, batched inserts, a config UI rendered from each destination's own field descriptors, and a REST layer (`/api/log-export/*`) for CRUD, a connection test, and an on-demand run. A destination can opt into `includeBodies` to also ship the request and response payloads shown in the Logs detail pane, including the client and provider views of each call; this is off by default, and payloads inherit the dashboard's PII sanitisation, secret redaction and `noLog` handling. Google BigQuery is the first destination, using a service-account key stored encrypted at rest and streaming inserts keyed by call-log id, into a table that is day-partitioned on `timestamp` and clustered on `api_key_name`, `provider`, `model` and `status`. ([#11945](https://github.com/diegosouzapw/OmniRoute/pull/11945)) — thanks @dpozimski +- **feat(providers):** add MaxAI as a signed, OpenAI-compatible provider serving its 13 paid chat models (GPT-5.6 / Luna / Thinking, Claude 5 Sonnet, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite, Grok 4.1-fast / 4.5, DeepSeek V3.2 / R1, Llama 3.3 70B) through OmniRoute's `/v1` endpoint, with per-request HMAC-SHA1→SM3→AES request signing, live model + context-window discovery from `/models/get_config`, and prompted tool-calling translated to OpenAI `tool_calls` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI vision input — image_url content parts are forwarded inline in `message_content` to the 6 vision-capable models (GPT-5.6 / Luna / Thinking, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite) ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI image generation — 6 image models (gpt-image-1, dall-e-3, flux-1-schnell/dev/pro, sd3-medium) exposed through `POST /v1/images/generations` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI document RAG — inline base64 file/document attachments are uploaded to MaxAI (content-addressed `doc_id`) and attached to the chat via `doc_list` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** browserless MaxAI onboarding — email device-pair login (`/api/providers/[id]/login`) and signed access-token refresh, so a connection can be created and kept fresh without a real browser or Google OAuth ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** per-provider TLS impersonation profile (MaxAI presents a Windows Firefox-150 client fingerprint) so its bot-sensitive endpoints accept OmniRoute traffic ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** add **Nimble** as a web-search and web-fetch provider (`nimble-search`) — `/v1/search` routes to Nimble's search API at `lite` depth (locale, freshness and include/exclude domain filters mapped onto the shared request contract), and `/v1/web/fetch` routes to Nimble Extract, which covers all four fetch formats (`markdown`, `html`, `links`, `screenshot`) from a single call. One API key serves both surfaces. ([#11620](https://github.com/diegosouzapw/OmniRoute/pull/11620)) — thanks @wildcard +- The npm publish is automatic again, through npm Trusted Publishing (OIDC): the hosted `stage-npm` job publishes with a short-lived credential minted from GitHub's id-token — no `NPM_TOKEN`, no 2FA prompt, provenance attached. `publish_mode=staged` (owner approves with 2FA) and `direct` (token) remain available on `workflow_dispatch`. ([#11931](https://github.com/diegosouzapw/OmniRoute/pull/11931)) +- **feat(providers):** Add **Opper** as an API-key gateway provider — EU-hosted AI gateway with 700+ models from 30+ providers behind one OpenAI-compatible API and one key (`OPPER_API_KEY`); model ids use `provider/model` format (e.g. `anthropic/claude-sonnet-4-6`, `openai/gpt-5`); live model catalog at `https://api.opper.ai/v3/compat/models`; entry mirrors `requesty` (same shape, `passthroughModels: true`, no static seed) ([#11629](https://github.com/diegosouzapw/OmniRoute/pull/11629)) +- **feat(dashboard):** the `/dashboard/orchestration` snapshot hook now subscribes to the `agents` WebSocket channel (`agent.task.updated`) instead of `requests` as its refetch trigger, and relaxes its background poll from 5s to 30s while that WS connection is up — falling back to the tighter 5s cadence, reprogrammed live on any connect/disconnect transition, whenever the socket is down. ([#12409](https://github.com/diegosouzapw/OmniRoute/pull/12409)) +- **feat(dashboard):** Orchestration canvas quick wins — search box plus state/source/provider filter chips with a one-click clear, and per-source collapse/expand, all reflected in the URL so a filtered/collapsed view is shareable and survives a refresh; the detail drawer gained a "copy trace JSON" action and hardened error/empty-state and accessibility handling; the Agents-tab edges now animate traveling particles along active (running) connections; and the canvas node/edge status colors moved off fixed hex values onto theme-aware `--orch-status-*` CSS custom properties, so they adapt correctly to light/dark mode. ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- **chore(dashboard):** Orchestration UI hardening pass and the missing component/model test coverage it called for — `OrchestratorNode`/`ActivityNode`/`OverflowNode` rendering, the `?node=`/overflow-click page routing, the Agents-tab orchestrator-click no-op and `showCompleted` toggle, and the overview kanban's done-column sort order (#12270, #12271). ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- New `/dashboard/orchestration` page: live unified view of everything running — Cloud Agent, A2A and Conductor as a real-time graph (Agents tab), the combo cascade (Routing tab, reusing the Combo Live Studio) and a state kanban (Overview tab), with a detail drawer (trace, cost, approve/cancel). Read-only over existing APIs — no new backend. Canvas concept credit: PR #11815 design ([#12261](https://github.com/diegosouzapw/OmniRoute/pull/12261)) +- **feat(dashboard):** new "History" tab on `/dashboard/orchestration` — an Airflow-style grid of finished runs over a 24h/7d/30d preset window, one row per (source, identity), clicking a cell opens the existing detail drawer. It is backed by real persistence: A2A task lifecycle transitions are now written to the `a2a_tasks` table (purged after 30 days, configurable via `OMNIROUTE_A2A_HISTORY_RETENTION_DAYS`) and served by the new `GET /api/a2a/tasks/history` listing endpoint, with the task-detail route falling back to persisted history once a run leaves the in-memory snapshot. Conductor runs stay remote and are not persisted locally — the tab says so instead of silently omitting them. ([#12479](https://github.com/diegosouzapw/OmniRoute/pull/12479)) +- **feat(dashboard):** the orchestration detail drawer gained a "Repeat" action for Cloud Agent, A2A and Conductor tasks — a two-click confirm (click once to arm, click again within the confirm window to fire) re-submits the original prompt/input as a new run. The button is disabled with an explanatory tooltip whenever the original input can't be recovered from the loaded task detail (e.g. it never carried a prompt, or the detail failed to load). Two limitations of the A2A variant, by design: it targets the `/a2a` JSON-RPC endpoint, which authenticates with an API key only (`REQUIRE_API_KEY=true` or a configured `OMNIROUTE_API_KEY` makes a dashboard-session repeat answer `HTTP 400` — surfaced verbatim in the drawer's error line, never as a success), and the `message/send` call is SYNCHRONOUS: the POST blocks for the whole skill run, so the success confirmation only appears once the repeated task finishes. A dashboard-authenticated A2A creation path is deliberately left to a follow-up — widening the endpoint's auth posture is an operator decision, not a side effect of this feature. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(a2a):** A2A task execution now records which memories were consulted for the task's last user message as `metadata.memoryHits` (id/key/type/content-snippet) plus a `memory_hits` history event, purely for observability — the retrieved memory is never injected into a skill's prompt or behavior. Gated by the `OMNIROUTE_A2A_MEMORY_HITS` kill-switch (default enabled; set to `0` to skip the recall lookup entirely). The drawer's new "Memory used" section lists these hits for a2a tasks and is omitted whenever there are none. Known limitation: recall only resolves under the keyless posture — a keyed caller's task owner is a SHA-256 prefix of the API key, while memory rows are keyed by the database api-key id, and no hash→id lookup exists today, so the hit list stays empty for keyed callers. The recorded hits are also kept out of the task's own `input` (and therefore out of the persisted input and of the "Repeat" request body), so repeating a task never re-sends the previous run's memory snippets. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(providers):** add a Perplexity Agent API provider (`perplexity-agent` / `pplx-agent`) for Perplexity `/v1/responses`, including the documented Anthropic, OpenAI, Google, xAI, DeepSeek, Z.AI, Moonshot/Kimi, NVIDIA, and Perplexity model IDs plus Anthropic-model `max_output_tokens` compatibility. ([#12103](https://github.com/diegosouzapw/OmniRoute/pull/12103)) — thanks @AIB1TAL0S +- **feat(providers):** add RPD (Requests Per Day) limit to provider rate limit overrides across UI, schemas, DB, and i18n ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) — thanks @b3nw +- **feat(providers): add UC Direct (uncensored.com Developer API), the metered OpenAI-compatible surface.** A standard OpenAI-compatible passthrough (default executor) for uncensored.com's official REST API at `https://api.uncensored.com/api/v1`: `X-api-key` auth (never-expiring `uai_sk_live_` key), `POST /chat/completions` with streaming SSE and native tool-calling, and the full live metered catalog (82 models across 15 providers, discovered from the public `GET /v1/models`). Registered as provider `uc-direct` (alias `ucd`). Complements the un-metered `uc` persona provider — same models, metered credits and a plain API key instead of a subscription session. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(providers): add UC (uncensored.com), the un-metered subscription "persona" chat as an OpenAI-compatible provider.** A WebSocket web-app port: a durable Clerk credential mints a short-lived session token per connect (browserless — no API key), driving UC's persona socket. Ships the browserless email-code login (request → verify → harvest), the 19 verified persona models (Claude Opus, Gemini, Grok, GLM, Kimi, DeepSeek, MiniMax, incl. the uncensored variants), prompted `` tool-calling with a per-model code-style dialect + auto-cure retry for guardrailed models, live ``/reasoning split, streaming + non-streaming OpenAI responses, and full quota/auth error surfacing (paywall / message-limit / rate-limit → 429, invalid session → 401 re-login). Full multimodal parity via the persona blob-upload layer: **vision** (image input, 15 vision-capable models), **document RAG** (PDF/doc upload, server-side extraction), **image generation** (22 models), **video generation** (14 models, async signed-url → poll), and **TTS** (streaming MP3). Registered as provider `uc` (alias `ucn`). The metered OpenAI-compatible Developer API is a separate `uc-direct` provider. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(logs):** add copy-all button to request detail modal ([#11083](https://github.com/diegosouzapw/OmniRoute/pull/11083)) — thanks @AndrianBalanescu +- **feat(check):** make serviceKinds required and add the reverse-walk provider consistency gate ([#11392](https://github.com/diegosouzapw/OmniRoute/pull/11392)) — thanks @Tushar49 +- **feat(providers):** only advertise verified-working connections by default ([#11448](https://github.com/diegosouzapw/OmniRoute/pull/11448)) — thanks @alltomatos +- **feat(resilience):** add hierarchical concurrency admission ([#11493](https://github.com/diegosouzapw/OmniRoute/pull/11493)) +- **feat(providers):** derive + surface expiry for JWT-bearing web cookies (#11497) ([#11505](https://github.com/diegosouzapw/OmniRoute/pull/11505)) — thanks @oyi77 +- **feat(tunnels):** support named cloudflared tunnels via CLOUDFLARED_CONFIG ([#11561](https://github.com/diegosouzapw/OmniRoute/pull/11561)) — thanks @ggiak +- **feat(compression):** make proactive context-compression threshold a live setting ([#11564](https://github.com/diegosouzapw/OmniRoute/pull/11564)) — thanks @ragnar-claude +- **feat(resilience):** add provider peak-hour protection ([#11622](https://github.com/diegosouzapw/OmniRoute/pull/11622)) — thanks @Neuron-Mr-White +- **feat(providers):** add 1min.ai provider ([#11631](https://github.com/diegosouzapw/OmniRoute/pull/11631)) — thanks @zero-executioner +- **feat(dashboard):** collapsible JSON tree viewer for request/response payloads ([#11703](https://github.com/diegosouzapw/OmniRoute/pull/11703)) — thanks @hartmark +- **feat(routing):** add LiquidAI LFM2.5-2.6B free tier via OpenRouter ([#11752](https://github.com/diegosouzapw/OmniRoute/pull/11752)) — thanks @brick30llc-ctrl +- **feat(pricing):** add GLM-5.3-Flash pricing, model specs, and catalog registration ([#11830](https://github.com/diegosouzapw/OmniRoute/pull/11830)) — thanks @AndrianBalanescu +- **feat(routing):** add relayMode for schema-locked context handoffs ([#11839](https://github.com/diegosouzapw/OmniRoute/pull/11839)) — thanks @raheemuddin786 +- **feat(config):** add support for runtime system prompt configuration and hot-reloading ([#11841](https://github.com/diegosouzapw/OmniRoute/pull/11841)) — thanks @raheemuddin786 +- **feat(routing):** expand connection-aware quota prefilter across combo strategies (#11682) ([#11850](https://github.com/diegosouzapw/OmniRoute/pull/11850)) — thanks @HouMinXi +- **feat(models):** reactive discovery sync after Antigravity model-not-found 404 ([#11888](https://github.com/diegosouzapw/OmniRoute/pull/11888)) — thanks @MumuTW +- **feat(models):** auto-aliases and token limits from synced Antigravity discovery ([#11889](https://github.com/diegosouzapw/OmniRoute/pull/11889)) — thanks @MumuTW +- **feat(settings):** raise sticky round-robin limit caps to 1000 ([#12015](https://github.com/diegosouzapw/OmniRoute/pull/12015)) — thanks @adivekar-utexas +- **feat(resilience):** operator-configurable global credential health check interval ([#12043](https://github.com/diegosouzapw/OmniRoute/pull/12043)) — thanks @adivekar-utexas +- **feat(quota):** use official OpenCode Go usage API ([#12124](https://github.com/diegosouzapw/OmniRoute/pull/12124)) — thanks @ddarkr +- **feat(resilience):** default the credential health check sweep to 60 minutes ([#12138](https://github.com/diegosouzapw/OmniRoute/pull/12138)) — thanks @adivekar-utexas +- **feat(quality):** new-code mode for the complexity and dead-code ratchets (Clean as You Code) ([#12142](https://github.com/diegosouzapw/OmniRoute/pull/12142)) +- **feat(dashboard):** orchestration canvas — unified model + snapshot hook (part 1/2) ([#12156](https://github.com/diegosouzapw/OmniRoute/pull/12156)) +- **feat(usage):** add Kilo Code balance and Kilo Pass quotas ([#12178](https://github.com/diegosouzapw/OmniRoute/pull/12178)) — thanks @killer30001000 +- **feat(providers):** refresh vendored ChatGPT Web connector to v4.0.7 ([#12181](https://github.com/diegosouzapw/OmniRoute/pull/12181)) — thanks @backryun +- **feat(providers):** manual "Clear cooldown" action in the cooling panel ([#12224](https://github.com/diegosouzapw/OmniRoute/pull/12224)) — thanks @patrykkopycinski +- **feat(providers):** restore ChatGPT Web via clean-room browser transport ([#12239](https://github.com/diegosouzapw/OmniRoute/pull/12239)) — thanks @backryun +- **feat(sse):** wire the PROVIDER_PROFILES window gate into the global provider cooldown ([#12247](https://github.com/diegosouzapw/OmniRoute/pull/12247)) +- **feat(providers):** modernize CLOVA Studio chat and embeddings ([#12277](https://github.com/diegosouzapw/OmniRoute/pull/12277)) — thanks @backryun +- **feat(browser):** adopt Obscura as primary headless browser engine with Chromium fallback ([#12286](https://github.com/diegosouzapw/OmniRoute/pull/12286)) — thanks @opensource-elearning +- **feat(sse):** full language parity for output styles (es/de/fr/it/ru/zh + autoDetect) ([#12289](https://github.com/diegosouzapw/OmniRoute/pull/12289)) +- **feat(quality):** complete test:scoped — --full map rebuild, stdin selection, CI loader parity (#8084 D1) ([#12353](https://github.com/diegosouzapw/OmniRoute/pull/12353)) +- **feat(providers):** refresh Fable, Cursor, and Devin catalogs ([#12367](https://github.com/diegosouzapw/OmniRoute/pull/12367)) — thanks @backryun +- **feat(video):** redact transcript text from logs and durable memory (#12150 P1) ([#12427](https://github.com/diegosouzapw/OmniRoute/pull/12427)) +- **feat(dashboard):** parent-link, genuine-continuation badge, and modal perf fixes ([#12448](https://github.com/diegosouzapw/OmniRoute/pull/12448)) — thanks @hartmark +- **feat(dashboard):** adaptive context-budget dial on compression panel ([#12488](https://github.com/diegosouzapw/OmniRoute/pull/12488)) — thanks @HouMinXi +- **feat(providers):** refresh Z.ai Web models and browser transport ([#12524](https://github.com/diegosouzapw/OmniRoute/pull/12524)) — thanks @backryun +- **feat(video):** redact raw client-snapshot transcript fields in the detailed log (#12150 P2) ([#12528](https://github.com/diegosouzapw/OmniRoute/pull/12528)) +- **feat(providers):** refresh NVIDIA hosted models ([#12538](https://github.com/diegosouzapw/OmniRoute/pull/12538)) — thanks @backryun +- **feat(quota):** Moonshot Open Platform balance and TPD lock for custom nodes ([#12590](https://github.com/diegosouzapw/OmniRoute/pull/12590)) — thanks @HouMinXi +- **feat(video):** redact transcript in the in-memory pending-request snapshot (#12430 item 6) ([#12596](https://github.com/diegosouzapw/OmniRoute/pull/12596)) +- **feat(dashboard):** link a log entry's Conversation Context to its owning conversation ([#12646](https://github.com/diegosouzapw/OmniRoute/pull/12646)) — thanks @hartmark +- **feat(catalog):** eligibility-gated free-tier bucket ([#12669](https://github.com/diegosouzapw/OmniRoute/pull/12669)) +- **feat(db):** fail-closed previous_response_id continuation for redacted video turns (#12150 P2b) ([#12707](https://github.com/diegosouzapw/OmniRoute/pull/12707)) +- **feat(gamification): pay the documented `streak_bonus` and `badge_unlock` XP rewards.** `XP_REWARDS` listed both rewards but the award pipeline never paid them: the private reward table in `events.ts` omitted them, `updateStreak()` did not report when a streak extended, and badge unlocks carried no XP. Every request that extends a daily streak now pays `streak_bonus × streak length` once per UTC day (guarded by a same-day `xp_audit_log` check), and every badge unlocked through the pipeline pays `badge_unlock` once per badge (guarded by the `user_badges` primary key; `unlockBadge()` now reports whether it inserted). Bonus XP flows through the same `addXp` + level sync + global/weekly/monthly leaderboard path as action XP, so level-ups and rankings include it. The Radar supporter recognition unlock stays XP-free. (#12522 — thanks @pacocartones) +- **feat(providers):** advertise a `free-tier` capability in the provider plugin manifest for every provider with documented free models, so sidecars and dashboards can filter free-capable providers without reading the quota catalog ([#12786](https://github.com/diegosouzapw/OmniRoute/pull/12786)) — thanks @maxmad64bis +- **feat(providers):** Added EURouter as an OpenAI-compatible API-key gateway (`https://api.eurouter.ai/v1`), with live model discovery via `passthroughModels`. Its copy states that models are served by third-party upstreams listed per model, so an EU-based router is not read as EU data residency for inference. ([#13025](https://github.com/diegosouzapw/OmniRoute/pull/13025)) — thanks @ntdatt812 +- **feat(providers):** Added GreenPT as an OpenAI-compatible API-key provider (`https://api.greenpt.ai/v1`), with live model discovery via `passthroughModels`. No free-inference badge: the published docs describe a free API subscription billed per token, not a free tier. ([#13024](https://github.com/diegosouzapw/OmniRoute/pull/13024)) — thanks @ntdatt812 +- **feat(i18n):** 9 new locales — Greek (`el`), Croatian (`hr`), Serbian (`sr`), Lithuanian (`lt`), Estonian (`et`), Latvian (`lv`), Slovenian (`sl`), Maltese (`mt`), Irish (`ga`) — across the dashboard, docs mirrors, CLI, README and the site; OmniRoute now ships every official EU language (51 locales). (#13044) +- **feat(i18n):** 8 new locales — Kannada (`kn`), Malayalam (`ml`), Odia (`or`), Punjabi (`pa`), Nepali (`ne`), Sinhala (`si`), Burmese (`my`), Khmer (`km`) — across the dashboard, docs mirrors, CLI, README and the site (59 locales). The translator now restores the ICU literal escape around angle placeholders and splits oversized docs sections before translating. (#0000) ([#13660](https://github.com/diegosouzapw/OmniRoute/pull/13660)) +- feat(providers): **list Agnes 3.0 Flash as the current free chat model, drop retired 1.5 Flash, add Image 2.0/2.5 Flash plus Video 2.5/2.5 Flash, and discover the live `/v1/models` catalog (including the CN host `api.agnes-ai.cn`).** `agnes-1.5-flash` now forwards to `agnes-3.0-flash`. Video 2.5 polls `GET /v1/videos/{id}` (not the V2.0 `/agnesapi` contract). Live `/v1/models` (2026-09-09) no longer serves 1.5; the wiki marks it deprecated. 3.0 Flash is 512K context / 65,536 max output, same window as 2.5. CN-region keys use the existing per-connection base-URL field, default stays `apihub.agnes-ai.com`. ([#13120](https://github.com/diegosouzapw/OmniRoute/pull/13120)) — thanks @HouMinXi +- **feat(sse):** Codex and OpenAI catalogs list GPT-6 Astra with effort aliases (`-low` through `-ultra`); Codex CLI identity pins `@openai/codex@0.153.4` in lockstep with the image ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **feat(dashboard):** the orchestration History tab gained a "Compare runs" mode — toggling it turns each grid cell into a 2-item selection queue (a 3rd click drops the oldest pick), and picking two cells opens a side-by-side comparison panel instead of the usual detail drawer. The panel fetches both runs' detail the same way the drawer does (falling back to persisted history once a run leaves the live TTL window) and shows, per side: identity/source/state, start time, a signed `right - left` delta for duration/cost/event count, the event timeline aligned by index, and any memory hits. One side's fetch failing never blocks the other, and a delta is only ever computed when both sides have a finite value — otherwise it renders "—", never `NaN`. Comparing two runs from different sources or skills still works; a banner marks the deltas as informational rather than hiding them, since the two runs aren't a strict apples-to-apples pair. ([#12677](https://github.com/diegosouzapw/OmniRoute/pull/12677)) +- **feat(dashboard):** badge a conversation that never reached a clean stop ([#12717](https://github.com/diegosouzapw/OmniRoute/pull/12717)) — thanks @hartmark +- **feat(release):** reconcile-changelog tool + version-anchored fragment aggregation ([#12987](https://github.com/diegosouzapw/OmniRoute/pull/12987)) +- **feat(mcp):** carry context_length through MCP list_models_catalog projection (#12776) ([#13406](https://github.com/diegosouzapw/OmniRoute/pull/13406)) — thanks @KooshaPari +- **feat(cli):** make startup readiness budget configurable (#13369) ([#13433](https://github.com/diegosouzapw/OmniRoute/pull/13433)) — thanks @KooshaPari + ### 🐛 Bug Fixes +- **security(streaming):** sanitize generic mid-stream error messages before emitting OpenAI, + Responses, or Claude SSE failure frames and before diagnostic logging, while preserving raw + failures for internal classification and keeping client disconnects out of provider failure state. ([#12457](https://github.com/diegosouzapw/OmniRoute/pull/12457)) +- **fix(claude):** `blockExtraUsage: false` no longer lets 5h quota preflight skip the account; extra usage is billed after the session bar is gone, so the request must reach Anthropic ([#12803](https://github.com/diegosouzapw/OmniRoute/pull/12803)) — thanks @HouMinXi +- Sanitize HuggingChat conversation-creation and message-send transport failures before they reach client error bodies or provider logs. ([#12467](https://github.com/diegosouzapw/OmniRoute/pull/12467)) +- **fix(dashboard):** Keep local and theme-aware provider SVG icons at a definite layout size so Chromium does not collapse them to 0×0 after the v3.8.50 image-rendering change ([#12054](https://github.com/diegosouzapw/OmniRoute/pull/12054)) — thanks @ponkcore +- **fix(security):** Sanitize provider and runtime failures before public API, SSE and MCP responses and before persistent request, proxy and usage logs, preventing credentials, stack traces and host filesystem paths from crossing those boundaries while preserving stable error codes and useful diagnostics. ([#12506](https://github.com/diegosouzapw/OmniRoute/pull/12506)) +- **fix(cli):** `omniroute tunnel create` no longer crashes with `Cannot read properties of undefined (reading optsWithGlobals)` — removed the duplicate positional argument that caused Commander.js to misalign the action callback parameters ([#12295](https://github.com/diegosouzapw/OmniRoute/issues/12295)) ([#12368](https://github.com/diegosouzapw/OmniRoute/pull/12368)) — thanks @gonisulaimann +- Stop advertising Gemini Live-only models as supported audio endpoints until OmniRoute proxies the bidirectional Live protocol. ([#11710](https://github.com/diegosouzapw/OmniRoute/pull/11710)) — thanks @RaviTharuma +- **fix(kie):** reroute `flux/kontext` off the KIE Market `createTask` flow — it is catalogued with `isMarket: true` but has no Market catalog page, so KIE rejected it with "model name not supported"; it now hits the dedicated `POST /api/v1/flux/kontext/generate` / `GET /api/v1/flux/kontext/record-info` endpoints instead (#11296). ([#11985](https://github.com/diegosouzapw/OmniRoute/pull/11985)) — thanks @hartmark +- **fix(kie):** correct 12 more KIE Market catalog ids that were sent to `createTask` unchanged but diverge from KIE's documented upstream `model` values — GPT Image 2 T2I/I2I (drops the `gpt/` prefix), GPT Image 1.5 T2I/I2I (`gpt-image/` namespace), Seedream 5.0 Lite T2I/I2I (drops the `.0`), all 4 Flux 2 variants (`flux-2/` namespace, generic variant renamed `flex`), and Wan 2.7 Image / Image Pro (dash instead of dot) — each verified individually against the literal example request published on docs.kie.ai. `#11326`'s "everything else already matches" claim was wrong a second time (#11296); `z-image/4.0-*`/`z-image/4.5-*` and `flux/kontext` remain open, documented as unresolved in `KIE_MARKET_UPSTREAM_MODEL_IDS`'s comment pending further verification. ([#11667](https://github.com/diegosouzapw/OmniRoute/pull/11667)) +- **fix(dashboard):** `useApiKeySave.handleSaveApiKey` no longer forces a full upstream `/models` catalog sync on every non-curated provider connection save — callers can now pass `skipModelSync: true` to opt out, so a workflow that only wants to add one manual model no longer floods the provider's available-models list with hundreds/thousands of synced entries. The flag is a client-side intent signal only and is stripped before the connection payload is POSTed to `/api/providers`; default behavior (full sync on save) is unchanged when the flag is omitted (#11324) ([#11664](https://github.com/diegosouzapw/OmniRoute/pull/11664)) +- **Provider connections:** keep `tokenExpiresAt` when a connection is created. The create-path allowlist omitted it, so every insert stored NULL and the dashboard token badge could read a fresh connection as expired until its first background refresh ([#11368](https://github.com/diegosouzapw/OmniRoute/pull/11368)). — thanks @ntdatt812 +- **fix(combos):** the combo builder's precision-select, global-model-search, and manual-entry flows now serialize a model step's `model` string using the provider's already-computed routing-alias prefix (e.g. `oc/`) instead of rebuilding it from the raw canonical `providerId`, fixing the no-auth "OpenCode Free" provider (`opencode`) being routed to the unrelated paid "OpenCode Zen" provider (`opencode-zen`) because `opencode` doubles as a manual routing-prefix override ([#11433](https://github.com/diegosouzapw/OmniRoute/issues/11433)). ([#11670](https://github.com/diegosouzapw/OmniRoute/pull/11670)) +- **fix(radar):** the catalog feed cache now keeps `generatedAt`, the date the feed's data was built, next to `fetchedAt`, the date this install downloaded it (#11435). The feed schema requires that date and the sync path validates it, but the cache dropped it — so a feed fetched minutes ago and one carrying weeks-old figures looked identical to everything downstream, including the dashboard's "Last fetched" line. `getRadarCatalog().meta` and `GET /api/radar/status` now report both dates, the latter as its own field rather than folded into `version` — and omitted entirely for the offers and intel caches, which keep no build date, where a `null` would read as "unknown" rather than "never stored". The dashboard still shows only the fetch time; surfacing the build date there needs a new translated label and is left to a follow-up. Rows cached before migration 163 read back as `null`: unknown stays unknown instead of borrowing the fetch time. The referrals cache has persisted the same date since migration 142. — thanks @maxmad64bis +- **fix(cli):** postinstall no longer fills `JWT_SECRET` and `API_KEY_SECRET` in the installed package's `.env` (#11436). `.env.example` ships both blank on purpose: the server restores them from its durable store, or generates and persists them there, in `ensureSecrets()`. Pre-filling them defeated that — the file lives inside the package directory, so `npm i -g` replaced it and postinstall wrote _different_ values, while `ensureSecrets()` (which only acts on an empty variable) never got to restore the real ones. Both secrets rotated silently on every update, invalidating dashboard sessions and API-key CRCs. `STORAGE_ENCRYPTION_KEY` left the same list for the same reason in #1622; its comment pointed at a function that no longer exists and now names the real provisioning path. — thanks @maxmad64bis +- **fix(cli):** the CLI now says when a loaded `.env` lives inside the installed package directory (#11437). It already announces every env file it reads, without distinguishing the ones that survive an update from the one that does not: `npm i -g` replaces the package directory wholesale, so values set there are gone at the next update, silently. The warning names the durable path to move them to, and fires only when that file actually supplied a value — a file entirely shadowed by a durable one supplied nothing. A development checkout stays silent: there the same path is stable and documented in `SETUP_GUIDE.md`. — thanks @maxmad64bis +- **fix(free-models):** the shared `isFreeModel()` predicate no longer reports catalog entries marked `freeType: "discontinued"` as free, so `hidePaidModels` can't route to Pollinations' seven premium models that now require a paid key ([#11441](https://github.com/diegosouzapw/OmniRoute/pull/11441)) — thanks @maxmad64bis +- fix(ui): let AnySearch use the normal provider-icon fallback when LobeHub has no matching icon (#11449) +- Fixed the v3.8.50 Costs and Analytics dashboards so flat-rate Claude Code usage can be shown as an explicitly requested token-price estimate without changing default billed-cost semantics. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed archived usage retention so each request is priced individually instead of pricing a day's summed tokens once, which understated archived cost whenever a day mixed cache-heavy and ordinary requests. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed the Costs dashboard so it discloses when displayed figures include flat-rate token-price estimates instead of labelling them as billed spend, using the flag the analytics API already returns; the month-end projection and the CSV/JSON exports carry the same marker, and billed-cost mode is unchanged. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- **fix(combo):** attach the same combo-diagnostics trace (`poolSize`/`attemptOrder`/`excluded`/`terminalReason`, plus `x-omniroute-combo-*` headers) to the round-robin strategy's and the nested pipeline/fusion runtime-unit loop's "Maximum combo retry limit reached" 503 that the priority-strategy path already attaches for the identical terminal condition — previously those two paths returned a bare, contextless 503 ([#11462](https://github.com/diegosouzapw/OmniRoute/issues/11462)). ([#11665](https://github.com/diegosouzapw/OmniRoute/pull/11665)) +- **fix(bun):** use native `bun:sqlite` in `bootstrap-env` and `sync-env` to avoid loading `better-sqlite3` N-API addon during Bun startup ([#11468](https://github.com/diegosouzapw/OmniRoute/pull/11468)) — thanks @TheDemonTuan +- **fix(docker-bun):** make `Dockerfile.bun` install reliable with pre-install script helpers and native `bun:sqlite` smoke check ([#11470](https://github.com/diegosouzapw/OmniRoute/pull/11470)) — thanks @TheDemonTuan +- **fix(docker-bun):** remove every vendored `better-sqlite3` native addon from the Bun runtime image so startup cannot abort before the `bun:sqlite` fallback ([#11482](https://github.com/diegosouzapw/OmniRoute/pull/11482)) — thanks @TheDemonTuan +- **fix(autoCombo):** effort/alias model variants (`gpt-5.6-sol-xhigh`, `gpt-5.6`, cursor's `claude-4.6-opus-high`) inherit their base model's task fitness instead of falling to the wildcard 0.5 ([#11492](https://github.com/diegosouzapw/OmniRoute/pull/11492)) — thanks @MumuTW +- fix(db): rate-limit repeated Arena ELO leaderboard fetch-failure warnings instead of logging one per sync attempt (#11500) ([#11989](https://github.com/diegosouzapw/OmniRoute/pull/11989)) — thanks @hartmark +- **fix(db):** dedupe the raw `[Encryption] Decryption failed...` log line emitted by the lazy-decrypt views (`createLazyRowProxy`/`createLazyConnectionView`), which power `getProviderConnections()` and were re-triggering that line on every CredentialHealth/model-sync cycle for the same corrupt or stale-key credential — a fresh Proxy over a fresh row on every cycle meant the per-proxy memoization never suppressed it, unlike the dedup `decryptConnectionFields()` already had since [#9927](https://github.com/diegosouzapw/OmniRoute/issues/9927). Now shares that dedupe tracking so the line logs at most once per credential ([#11500](https://github.com/diegosouzapw/OmniRoute/issues/11500)). ([#11678](https://github.com/diegosouzapw/OmniRoute/pull/11678)) +- **fix(intelligence):** Arena ELO sync stores a synthesized base-model row for every effort/harness variant (`claude-opus-5-max` → `claude-opus-5`, `gpt-5.6-sol-xhigh (codex-harness)` → `gpt-5.6-sol`), so bare model requests reach the synced intelligence layer instead of the static fallback table; the hand-maintained `MODEL_ALIAS_MAP` — which collapsed generations (`gpt-5.5` → `gpt-5`) — is gone ([#11506](https://github.com/diegosouzapw/OmniRoute/pull/11506)) — thanks @MumuTW +- **fix(autoCombo,sse):** catalog hygiene — the static task-fitness table no longer ranks retired models above live ones (dead rows dropped, patterns matched on segment boundaries), the 7 deprecation aliases that forwarded to retired or non-existent models now point at the vendors' published replacements, an alias is no longer rewritten when the serving provider offers the id as-is, and a new `check:model-lifecycle` gate diffs both tables against a vendor lifecycle snapshot ([#11507](https://github.com/diegosouzapw/OmniRoute/pull/11507)) — thanks @MumuTW +- **fix(sse):** stop dropping resolved `thoughtSignature` values on parallel (multi tool-call) turns sent to Gemini 3.x — the claude→gemini and openai→gemini translators previously kept the signature only on the *first* function call of a message, causing Gemini to reject subsequent calls in the same turn with HTTP 400 "Function call is missing a thought_signature"; each function call now keeps its own resolved signature ([#11510](https://github.com/diegosouzapw/OmniRoute/issues/11510)). ([#11669](https://github.com/diegosouzapw/OmniRoute/pull/11669)) +- Treat Docker publishing from a non-default release branch as an expected skip instead of a failed workflow, while preserving the guard that prevents it from overwriting the `next` channel. ([#11527](https://github.com/diegosouzapw/OmniRoute/pull/11527)) — thanks @pacocartones +- fix(search): prefer credentialed providers over duckduckgo-free fallback (#11524) — the fallback-only loop ran before the credentialed-providers loop in `executeWebSearch`, making configured providers unreachable when `duckduckgo-free` was available. ([#11565](https://github.com/diegosouzapw/OmniRoute/pull/11565)) — thanks @vsd2807 +- **fix(sse):** cap the upstream headers-wait phase for STREAMING requests to a client-realistic ceiling (110s, under Codex's own ~120s hard client-abort window) instead of the flat 10-minute `FETCH_TIMEOUT_MS` default — that default was 5x longer than the body-phase readiness watchdog's own adaptive bound, so a request whose upstream never returned any response at all (not even headers, e.g. a stalled NVIDIA target behind a tool-heavy Responses→Chat translation) kept the client connection alive on keepalives only, guaranteeing the client's own patience ran out first with an opaque 499 instead of OmniRoute detecting and failing the stall fast. Non-streaming requests are unaffected — they keep the existing flat default (`open-sse/utils/fetchStartTimeoutPolicy.ts`) (#11526) ([#11668](https://github.com/diegosouzapw/OmniRoute/pull/11668)) +- **fix(ci):** protocol-clients E2E harness boots the peer-stamped custom server (`run-next.mjs dev`) instead of the bare `next` CLI, so `LOCAL_ONLY` locality resolves from the real TCP peer stamp and `/api/mcp/audit` answers 200 (open bootstrap preserved via an `OMNIROUTE_E2E_BOOTSTRAP_MODE=open` credential clear in `run-next.mjs`, pinned to loopback `HOST`) instead of a deterministic 403 (#11535) ([#11549](https://github.com/diegosouzapw/OmniRoute/pull/11549)) — thanks @jonlwheat2-gif +- **fix(docs):** the Auto-Combo engine is described with the number of factors it actually declares. Four different counts were in circulation — 6, 9, 13 and 14 across `AGENTS.md`, four guides and six code comments — while `DEFAULT_WEIGHTS` declares 15. `check:docs-counts` now reads that number from the source, so adding a factor without updating the prose fails CI ([#11545](https://github.com/diegosouzapw/OmniRoute/pull/11545)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **fix(quota-share):** in-flight leases are per request, so an aborted request's slot ages out instead of being kept alive by later traffic on the same connection ([#11547](https://github.com/diegosouzapw/OmniRoute/pull/11547)) — thanks @abhisheksharma2411 +- The free-tier summary route now serves the refreshed Radar catalog when the feed is active — the same numbers the dashboard shows — and states which catalog answered plus its real build date, instead of always reporting release-frozen figures with a stale curation date. The supporter-key live feed stays reserved to authenticated callers of the instance. ([#11550](https://github.com/diegosouzapw/OmniRoute/pull/11550)) — thanks @maxmad64bis +- **sse:** fix LiveWS/embed-WS servers crashing at startup under the Node/tsx runtime — `liveServer.ts`, `embedWsProxy.ts` and `apiBridgeServer.ts` imported `@/shared/utils/httpClientAbortGuard` without the `.mjs` extension, so the client-abort crash guard added by [#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556) was unreachable and every dependent test failed with `ERR_MODULE_NOT_FOUND` ([#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556)). ([#11671](https://github.com/diegosouzapw/OmniRoute/pull/11671)) — thanks @jonlwheat2-gif / @yxyxy +- fix(chat): shed any of the matched provider-node's routing identifiers (public prefix or internal id) before model lookup, so composites like `//` no longer reach upstream verbatim (#11557) — thanks @maxmad64bis +- **fix(cache):** a semantic-cache entry now expires on its TTL instead of surviving until the next UTC midnight, and `dbEntries` stops counting expired rows ([#11573](https://github.com/diegosouzapw/OmniRoute/pull/11573)) — thanks @ntdatt812 +- **fix(api):** `/v1/models` schedules its stale-while-revalidate rebuild through Next's `after()` again, so a stale catalog reaches the client before the rebuild blocks the event loop ([#11574](https://github.com/diegosouzapw/OmniRoute/pull/11574)) — thanks @ntdatt812 +- **fix(check):** the `known-symbols` executor conformance gate awaits the now-async `getExecutor()`, so it stops reporting all 142 registered aliases as dead symbols and can detect a lazy import that fails ([#11582](https://github.com/diegosouzapw/OmniRoute/pull/11582)) — thanks @ntdatt812 +- **fix(pack):** register `bin/cli/utils/volatileEnvPath.mjs` in `PACK_ARTIFACT_REQUIRED_PATHS`, so the prune can no longer silently drop a module the published CLI imports on every boot ([#11588](https://github.com/diegosouzapw/OmniRoute/pull/11588)) — thanks @ntdatt812 +- **i18n(vi):** translate the five reliability-column strings on the free-provider rankings page, which rendered as `__MISSING__:` markers in the Vietnamese UI ([#11589](https://github.com/diegosouzapw/OmniRoute/pull/11589)) — thanks @ntdatt812 +- **resilience:** restore the expired-connection retry-budget probe in the token-health sweep — the `!isGitHubAccessTokenOnlyConnection` carve-out reintroduced by #11608 contradicted the boundary pinned by #11592, so a GitHub connection parked at `expired` with retry budget remaining was never probed and could never self-heal ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)). ([#11672](https://github.com/diegosouzapw/OmniRoute/pull/11672)) — thanks @jonlwheat2-gif / @ntdatt812 +- **fix(dashboard):** Provider Stats now displays combo success rates on their native 0–100 percentage scale instead of multiplying them twice ([#11595](https://github.com/diegosouzapw/OmniRoute/pull/11595)) — thanks @pacocartones +- **fix(providers):** route DuckDuckGo Web Chat through the verified `duck.ai` endpoint, refresh its free model catalog, and avoid forwarding unsolved anti-abuse challenges ([#11597](https://github.com/diegosouzapw/OmniRoute/pull/11597)) — thanks @solstxce +- **fix(ui):** Console log Refresh and Copy controls now expose localized accessible names, keep copy actions visible on keyboard focus, and announce copy completion safely ([#11599](https://github.com/diegosouzapw/OmniRoute/pull/11599)) — thanks @pacocartones +- Dropped the stale-`.eslintcache` `restore-keys` fallback from both "Restore ESLint file cache" steps in `ci.yml`, so the blocking `Lint` job can no longer be served per-file verdicts computed under a different lint config, suppressions file or lockfile. `quality.yml` had already dropped it in #11963; `ci.yml` — the workflow that actually gates PRs — had not (#11600). ([#11996](https://github.com/diegosouzapw/OmniRoute/pull/11996)) +- fix(ci): pass `--pass-on-unpruned-suppressions` in `run-eslint-json.mjs` so the CI Lint job no longer fails when a suppression is merely orphaned by a genuine fix, mirroring the identical fix already in `validate-release-green.mjs` (#11600) ([#11983](https://github.com/diegosouzapw/OmniRoute/pull/11983)) — thanks @hartmark +- fix(handoff): enforce provider allowlist for universal handoff (#11602) — universal handoff now skips summarization when the selected summary model's provider is not included in the configured provider allowlist. ([#11619](https://github.com/diegosouzapw/OmniRoute/pull/11619)) — thanks @vsd2807 +- **fix(dashboard):** Show a stable error state when Search Analytics returns an HTTP error or malformed data ([#11603](https://github.com/diegosouzapw/OmniRoute/pull/11603)) — thanks @pacocartones +- **test(gamification):** pin the aggregate profile level to the XP-derived semantics of #11604 — `getAggregateXp()` now derives `currentLevel` from the summed XP (`calculateLevel(sum)`), not `MAX(stored current_level)`, and the #3484 fixture levels are aligned with the XP curve ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)). ([#11676](https://github.com/diegosouzapw/OmniRoute/pull/11676)) — thanks @jonlwheat2-gif / @pacocartones +- **fix(dashboard):** Keep the Profile level and progress aligned with aggregate XP, including bounded handling for invalid totals ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)) — thanks @pacocartones +- **fix(dashboard):** Prevent locked hidden badges from revealing their icon or opening private badge details before they are earned ([#11605](https://github.com/diegosouzapw/OmniRoute/pull/11605)) — thanks @pacocartones +- **fix(dashboard):** Restore keyboard focus after shared modals close and cancel delayed autofocus during cleanup ([#11607](https://github.com/diegosouzapw/OmniRoute/pull/11607)) — thanks @pacocartones +- **fix(dashboard):** Enable Enter and Space activation for clickable data-table rows without hijacking nested controls ([#11610](https://github.com/diegosouzapw/OmniRoute/pull/11610)) — thanks @pacocartones +- **fix(autoCombo,sse):** vendor-retired catalog ids are dropped from the auto-combo candidate pool and no longer win on leftover `arena_elo` / `user_override` rows; `getModelLifecycleDecision` consults `model-lifecycle.json` (prefix-stripped) so aggregator traffic is not `untracked` for ids the snapshot already knows ([#11625](https://github.com/diegosouzapw/OmniRoute/issues/11625)) ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)) — thanks @MumuTW +- **test(chatcore):** move Codex/Claude combo fixtures off models the lifecycle guard now rejects — `gpt-5.1-codex`/`gpt-5-codex` are vendor-retired (snapshot, #11626) and `claude-3-5-sonnet-20241022` is shut down, so native-passthrough and combo-fallback tests switched to `gpt-5.6-sol` and `claude-sonnet-4.6` ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)). ([#11675](https://github.com/diegosouzapw/OmniRoute/pull/11675)) — thanks @jonlwheat2-gif / @MumuTW +- fix(opencode-plugin): stop a free-tier budget that rounds up across a magnitude from rendering as `1000K`/`1000M` in the model picker — `fmtTokens` chose its unit from the raw token count and then rounded with `toFixed(1)`, so 999,950–999,999 printed as `1000K` rather than `1M` and just under a billion printed as `1000M` rather than `1B` ([#11684](https://github.com/diegosouzapw/OmniRoute/pull/11684)) — thanks @abhisheksharma2411 +- Cap prose-derived Antigravity quota resets at the configured model cooldown maximum while preserving authoritative Retry-After headers and Google RetryInfo hints. (#11695) Thanks @pacocartones. ([#11823](https://github.com/diegosouzapw/OmniRoute/pull/11823)) +- Compare `_rebaseline_*` notes structurally in the ratchet-bank verifier so an object-valued note no longer reports itself as rewritten on an unmodified tree — the nightly `bank-ratchet-shrinks` lane had been aborting on every run since 2026-08-11, leaving 41,672 lines of earned file-size shrinks unbanked (#11702). ([#11892](https://github.com/diegosouzapw/OmniRoute/pull/11892)) — thanks @MumuTW +- **fix(build):** `prepublish.ts` bundles the ChatGPT Web (Codex) MCP bridge through `runBuildTool()` instead of spawning `npx.cmd` raw, fixing the build crash on Node ≥ 20/Windows where `.cmd` shims cannot be spawned without a shell (EINVAL) ([#11704](https://github.com/diegosouzapw/OmniRoute/issues/11704)) ([#11706](https://github.com/diegosouzapw/OmniRoute/pull/11706)) — thanks @jonlwheat2-gif +- fix(codex): keep `parallel_tool_calls:false` on the translated Codex Responses Lite path (#11707) ([#11984](https://github.com/diegosouzapw/OmniRoute/pull/11984)) — thanks @hartmark +- **fix(tests):** the new Gemini Web Images retirement test (#11708) called the now-async `getExecutor()` synchronously, throwing `TypeError: getExecutor(...).getProvider is not a function` instead of asserting Gemini Web chat stays available. Awaited to match the R0.3 executor-registry refactor (#11220). ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(search):** Allow REST and MCP web-fetch requests to select the advertised `anysearch-search` provider explicitly ([#11716](https://github.com/diegosouzapw/OmniRoute/pull/11716)) — thanks @pacocartones +- **fix(providers):** Keep overnight peak-hour protection active after midnight according to the window's configured start day ([#11718](https://github.com/diegosouzapw/OmniRoute/pull/11718)) — thanks @pacocartones +- **fix(providers):** correct all five g4f.space gateway descriptions to document proof-of-work credits or member API keys, variable limits, and the remote prompt boundary instead of promising free keyless access at five requests per minute ([#11738](https://github.com/diegosouzapw/OmniRoute/pull/11738)). +- **fix(db):** `model_capabilities` is created by a migration instead of lazily on the first models.dev sync, so a clean install and an upgraded install converge on the same schema regardless of which features have run ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(ci):** `check:install-upgrade` now fails on an `npm` install truncated by ENOSPC (npm reports it as a warning and still exits 0), authenticates its health probe so the version assertion works against the hardened health payload, frees the clean-install tree before the upgrade phase, and no longer reports a schema divergence computed from a boot that never served ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(routing):** harden session-affinity key derivation against oversized explicit IDs and empty structural-payload collisions, while recognizing Gemini and common text request shapes ([#11746](https://github.com/diegosouzapw/OmniRoute/pull/11746)) — thanks @MumuTW +- **fix(packages/browser-pool):** regenerate `package-lock.json` so the `packages/browser-pool` workspace's locked `playwright`/`@types/node` (and transitives) match its `package.json` specs, fixing cache-only/offline installs (`npm ci --offline`, Nix `buildNpmPackage`) that previously failed with `ENOTCACHED` ([#11747](https://github.com/diegosouzapw/OmniRoute/issues/11747)) — thanks @benjaminkitt ([#11784](https://github.com/diegosouzapw/OmniRoute/pull/11784)) — thanks @geekyNads +- **fix(assets):** Stop distributing 79 provider and CLI logos without verified source/license provenance, keep every provider/tool available through the internal generic icon, and prevent those IDs from falling through to an external icon CDN. ([#11750](https://github.com/diegosouzapw/OmniRoute/pull/11750)) +- **fix(providers):** retire the common `chatgpt-web` / `cgpt-web` provider because its pre-key and proof-of-work provenance could not be cleared ([#11754](https://github.com/diegosouzapw/OmniRoute/pull/11754)) — explicit chat and image requests, slash-prefixed models, persisted aliases, proxy overrides, and combo targets now fail closed with a sanitized HTTP `410` / `PROVIDER_RETIRED` before upstream dispatch. Migration 163 tombstones matching connections and invalidates their active leases without deleting history or widening API-key allowlists. The independent `chatgpt-web-codex` / `cgpt-codex` provider and its MIT-noticed vendored implementation remain available. +- **fix(usage):** quota and usage refresh no longer 409 when an exclusive lease reserves the connection ([#11758](https://github.com/diegosouzapw/OmniRoute/pull/11758)) — thanks @TheDemonTuan +- Keep the embedding registry's vector width and `embedding` type on models when a synced model exists for the same id, so `/v1/models` no longer reports registry-described embedding models widthless or untyped (#11761) — thanks @rahilmavani +- Correct `google/gemini-embedding-001` on the OpenRouter route to 3072 dimensions, the width it returns when `dimensions` is not sent (#11761) — thanks @rahilmavani +- **fix(vision-bridge):** Restrict automatic vision routing and fallbacks to models available in authoritative live provider catalogs, including aliases and effort variants ([#11767](https://github.com/diegosouzapw/OmniRoute/issues/11767)) — thanks @pacocartones ([#11813](https://github.com/diegosouzapw/OmniRoute/pull/11813)) +- **fix(providers):** reclassify Cerebras as a one-time $5 signup credit (payment method required, 30-day validity), not a recurring no-card 1M tokens/day trial ([#11773](https://github.com/diegosouzapw/OmniRoute/issues/11773)) ([#12591](https://github.com/diegosouzapw/OmniRoute/pull/12591)) — thanks @HouMinXi +- fix(cli): stop prepublish from re-rebuilding the already-built ESM-only opencode-plugin dist (#11787) ([#11990](https://github.com/diegosouzapw/OmniRoute/pull/11990)) — thanks @hartmark +- **fix(db):** Fresh installs no longer log a non-fatal `no such table: compression_run_telemetry` warning when retention cleanup runs before the lazily-created telemetry table exists ([#11802](https://github.com/diegosouzapw/OmniRoute/pull/11802)) — thanks @RaviTharuma +- fix(sse): set X-OmniRoute-Selected-Connection-Id on successful combo dispatches so downstream consumers stop falling back to an empty connection id (#11810) ([#11986](https://github.com/diegosouzapw/OmniRoute/pull/11986)) — thanks @hartmark +- **fix(services):** embedded CLIProxyAPI lifecycle checks now use public `/healthz`, while model discovery uses the configured dedicated data-plane API key instead of the management password ([#11811](https://github.com/diegosouzapw/OmniRoute/pull/11811)) — thanks @RaviTharuma +- Fix persisted model steps returning an error in the playground route simulator, and warn when structural steps cannot be simulated. ([#11862](https://github.com/diegosouzapw/OmniRoute/pull/11862)) — thanks @pacocartones +- **fix(providers):** Antigravity's dynamic mitmAlias table no longer routes `gemini-3.7-flash-{high,medium,low}` to a literal tier-suffixed upstream id just because one connected account's own discovery listed it directly — those display ids always resolve through the safe `gemini-3.7-flash-tiered` static alias, so one account's Google-provisioned access no longer 404s every sibling account of the provider ([#11824](https://github.com/diegosouzapw/OmniRoute/issues/11824), [#11651](https://github.com/diegosouzapw/OmniRoute/issues/11651)) ([#11988](https://github.com/diegosouzapw/OmniRoute/pull/11988)) — thanks @hartmark +- **fix(dashboard):** Expose Profile loading, errors, page structure, and XP progress to assistive technologies ([#11838](https://github.com/diegosouzapw/OmniRoute/pull/11838)) — thanks @pacocartones +- **fix(socks):** `Agent.connectTimeout` now bounds both SOCKS handshake and TLS connect, and `family === null` no longer falls back to `fetch-socks` ([#11842](https://github.com/diegosouzapw/OmniRoute/pull/11842)) — thanks @maxmad64bis +- **perf(compression):** OOM mitigations for large payload hashing, memoization, and token estimation ([#11844](https://github.com/diegosouzapw/OmniRoute/pull/11844) — thanks @AndrianBalanescu) +- Excluded Next.js Node File Trace manifests (`*.nft.json`) from the published npm tarball. They are build-time metadata and are never read while serving, but had grown to 668.7 MB — 61% of the package — which pushed the upload past the registry limit and made `npm publish` fail with `413 Payload Too Large`. ([#11864](https://github.com/diegosouzapw/OmniRoute/pull/11864)) +- **fix(cli):** keep the global OmniRoute server `--base-url` separate from provider-node `--endpoint` values in add, update, and validate commands ([#11860](https://github.com/diegosouzapw/OmniRoute/pull/11860)) — thanks @pacocartones +- **fix(config):** Nous Research's `Hermes-4-405B` model now displays as "Hermes 4 405B (Nous Research)" in both the provider registry and the free-model catalog, instead of the mislabelled "Hermes 4 7B" ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#11993](https://github.com/diegosouzapw/OmniRoute/pull/11993)) +- **fix(provider/nous):** inject required user= tag into Nous Research inference requests to resolve upstream 400 "missing tags" error ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#12044](https://github.com/diegosouzapw/OmniRoute/pull/12044)) +- **fix(build):** `npm run build` now fails in one second with a named package and a copy-pasteable fix when npm silently drops an externalised optional native dependency, instead of dying four minutes in with `Module not found: Can't resolve 'better-sqlite3'` ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- **fix(install):** `postinstall` no longer throws `ReferenceError: isAndroid is not defined` — failing the whole `npm install` — when the `better-sqlite3` rebuild fallback times out; the manual-fix guidance is reachable again ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- Fixed the Alibaba free-tier allowlist test that went red on its own once the shipped catalog's `validUntil` (2026-08-27) passed, leaving every PR and `main` with a failing `Unit Tests (1/8)`. The test now builds its own packs with dates it controls, and covers the expired-pack fallback that production has actually been serving. ([#11867](https://github.com/diegosouzapw/OmniRoute/pull/11867)) +- Split the npm registry upload into its own GitHub-hosted job. npm refuses `--provenance` from a self-hosted runner (`422 ... Only "github-hosted" runners are supported`), which blocked the v3.8.50 publish; the heavy verification cannot move to a hosted runner, so it now hands the proven tarball over instead. ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(ui):** the shared `DataTable` loading state no longer reads its decorative ⏳ glyph out to assistive technology, and now carries the same `role="status"` / `aria-live="polite"` / `aria-busy="true"` semantics as `PageLoading` ([#11869](https://github.com/diegosouzapw/OmniRoute/pull/11869)) — thanks @pacocartones +- **fix(api):** Bound the `timeout` accepted by `POST /api/providers/[id]/login` to the same 15s–600s window the Adobe Firefly and Conol browser-login paths already enforce, so a request can no longer pin a headful browser and the single active-login slot with an arbitrarily large poll budget. ([#11871](https://github.com/diegosouzapw/OmniRoute/pull/11871)) — thanks @pacocartones +- **fix(api):** Stop the tunnel and MITM routes from returning raw child-process error text, which disclosed host paths, binary install locations and Tailscale `tskey-*` credentials in a body some of these routes serve to non-loopback callers, and return a real 400 instead of a framework 500 when a tunnel request body fails validation. ([#11872](https://github.com/diegosouzapw/OmniRoute/pull/11872)) — thanks @pacocartones +- **fix(open-sse):** stream TTFT/ITL are sampled from a monotonic clock (`performance.now`) instead of `Date.now`, so an NTP correction or wall-clock jump can no longer inflate a genuine TTFT or produce a negative one that the `ttft >= 0` guard silently drops — protecting the router's OTel/EWMA/`usage_history`/speed-ranking signals ([#11873](https://github.com/diegosouzapw/OmniRoute/pull/11873)) — thanks @pacocartones +- **fix(providers):** xAI `reasoning_effort: "xhigh"` now reaches grok-4.6+ instead of being silently clamped to `"high"` ([#11879](https://github.com/diegosouzapw/OmniRoute/pull/11879)) — thanks @NoxzRCW +- **fix(dashboard):** the providers page no longer crashes into the error boundary when a provider id collides with an `Object.prototype` member (`constructor`, `__proto__`); icon lookups are own-property guarded ([#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)) — thanks @NoxzRCW +- **fix(skills):** injected skill tools declared in shorthand (`{"content": "string"}`) now forward valid JSON Schema, unblocking providers that validate tool schemas strictly such as Zhipu GLM on the Console Go tier ([#11881](https://github.com/diegosouzapw/OmniRoute/pull/11881)) — thanks @NoxzRCW +- **fix(api):** `POST /api/playground/simulate-route` now surfaces `combo-ref` and `provider-wildcard` persisted combo steps with a specific warning (naming the referenced combo, or the unresolved `provider/model` wildcard) instead of folding them into a generic "unsupported step" count; a `provider-wildcard` step is also now included as an unresolved target so the operator can see it is in the route (ported from [#11882](https://github.com/diegosouzapw/OmniRoute/pull/11882) — thanks @NoxzRCW). ([#11930](https://github.com/diegosouzapw/OmniRoute/pull/11930)) +- **fix(translator):** the streaming OpenAI→Claude translator keeps upstream usage, including prompt-cache tokens, when it arrives on a trailing `choices: []` chunk (Fireworks and any upstream using `stream_options.include_usage`) ([#11883](https://github.com/diegosouzapw/OmniRoute/pull/11883)) — thanks @NoxzRCW +- **fix(cli):** `omniroute update --apply` now tells you explicitly whether a running server was detected and, if so, that you must run `omniroute restart` to apply the update — it never restarted anything and previously implied the update was already live once files were installed. The dashboard's npm-mode Update flow (`/api/system/version`) now tries OmniRoute's own PID-file-managed supervisor before falling back to pm2, and reports an honest "restart required" step instead of a silent pm2-only "skipped" that read like a completed update. The server-side latest-version lookup backing the dashboard's update banner also gained `--prefer-online`, closing the same stale-npm-cache class already fixed in the CLI's own copy for #4376 ([#11885](https://github.com/diegosouzapw/OmniRoute/issues/11885)). ([#12005](https://github.com/diegosouzapw/OmniRoute/pull/12005)) +- **fix(resilience):** clear persisted LKGP pins when a target suffers connection/provider exhaustion or is skipped before dispatch due to cooldown/exhaustion/unavailability, preventing subsequent requests from repeatedly prioritizing known-dead providers ([#11911](https://github.com/diegosouzapw/OmniRoute/issues/11911)). ([#12013](https://github.com/diegosouzapw/OmniRoute/pull/12013)) — thanks @HouMinXi +- **fix(sse):** the OpenAI→Claude stream translator now defers the terminal `message_delta`/`message_stop` emission until the real usage block has arrived (or a genuine end-of-stream flush forces it) instead of emitting it immediately on `finish_reason` — previously, when the trailing usage-only chunk (`{"choices":[],"usage":{...}}`) arrived *after* the `finish_reason` chunk (the normal order for Fireworks/vLLM/Together and other `stream_options.include_usage` upstreams), the client-visible `message_delta` still carried stale/zero usage even though `state.usage` was internally corrected too late to matter (ported from [#11915](https://github.com/diegosouzapw/OmniRoute/pull/11915) — thanks @HouMinXi). ([#11933](https://github.com/diegosouzapw/OmniRoute/pull/11933)) +- **fix(sse):** `stripStore()` now forces `store=false` for stateless OpenAI-compatible Responses-API targets (unless the connection has explicitly opted in via `providerSpecificData.openaiStoreEnabled`), instead of only handling the `openai`/`agentrouter` cases — a client-supplied `store` value previously passed through untouched to backends that don't actually persist responses server-side ([#11916](https://github.com/diegosouzapw/OmniRoute/pull/11916) — thanks @HouMinXi). +- **fix(models):** custom provider-node models (synced, custom, and alias-backed) now appear under their configured prefix in the unified catalog when the operator's model-id prefix mode is canonical, instead of being dropped whenever alias-inclusion was otherwise disabled ([#11918](https://github.com/diegosouzapw/OmniRoute/pull/11918) — thanks @HouMinXi). +- **fix(models):** the unified model catalog now suppresses stale static registry models (including effort-tier variants) for any provider whose active connection has an authoritative live synced catalog, not only providers already using exclusive-synced-listing — a connection with `providerUsesAuthoritativeLiveCatalog` previously kept serving both the live-synced models and the stale static rows side by side ([#11919](https://github.com/diegosouzapw/OmniRoute/pull/11919) — thanks @HouMinXi). +- **fix(ui):** `ProviderIcon`'s three remaining unguarded lookups (`PROVIDER_ICON_ALIASES`, `LOCAL_SVG_ALIASES`, `THEMED_SVGS`) now use `Object.hasOwn()` own-property checks — a provider id such as `constructor` or `__proto__` previously resolved through the prototype chain instead of falling through to the unknown-provider CDN fallback (`getLobeProviderIcon()` itself was already guarded by [#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)); `ProviderPageHeader`'s `color` field is also now optional, matching the rest of the component's defensive typing (ported from [#11920](https://github.com/diegosouzapw/OmniRoute/pull/11920) — thanks @HouMinXi). ([#11935](https://github.com/diegosouzapw/OmniRoute/pull/11935)) — thanks @NoxzRCW +- fix(ollama): preserve multi-byte UTF-8 content split across stream chunks in the Ollama NDJSON transform, which previously corrupted CJK/emoji into U+FFFD (#11921) — thanks @pacocartones +- **fix(providers):** OrcaRouter chat requests now target `/v1/chat/completions` instead of the bare `/v1` API root, fixing the upstream `404 Invalid URL (POST /v1)` ([#11923](https://github.com/diegosouzapw/OmniRoute/pull/11923)). — thanks @echel0nn +- **fix(plugins):** deliver the `onStreamComplete` event to disk-installed plugins. The event shipped in v3.8.50 (#9669) was emitted internally but had no plugin-facing wiring, so no plugin could ever subscribe: the manifest schema silently dropped `hooks.onStreamComplete`, and the loader/manager only knew the seven legacy hooks. `onStreamComplete` is now a declarable manifest hook, wired through the loader and registered by the manager like the other hooks, and its payload carries a `requestId` so consumers can correlate the stream-completion event with the originating request ([#11934](https://github.com/diegosouzapw/OmniRoute/pull/11934)) — thanks @amaleta / @pacocartones +- **fix(db):** the Qdrant embedding-model dropdown now lists local/self-hosted providers (Ollama, LM Studio, vLLM, etc.) — an active connection is treated as "configured" when the provider allows an optional API key, not only when it has a real key or OAuth, so a running local embedding provider is no longer hidden from the picker ([#11949](https://github.com/diegosouzapw/OmniRoute/issues/11949)) ([#11995](https://github.com/diegosouzapw/OmniRoute/pull/11995)) +- **fix(providers):** Vertex AI Anthropic partner-model discovery now calls the Model Garden `v1beta1` publisher list (`/v1beta1/publishers/anthropic/models`, global) and parses its `publisherModels` envelope, so Claude models auto-synced from Vertex populate the active live catalog and route at request time instead of returning `Model '' is not available in the active live catalog` ([#11991](https://github.com/diegosouzapw/OmniRoute/issues/11991)) — thanks @fabioluissilva ([#11998](https://github.com/diegosouzapw/OmniRoute/pull/11998)) +- **fix(cli):** support `--base-url` alongside `--endpoint` in `omniroute nodes add`, `update`, and `validate` subcommands to prevent global `--base-url` shadowing issues ([#11999](https://github.com/diegosouzapw/OmniRoute/issues/11999)). ([#12033](https://github.com/diegosouzapw/OmniRoute/pull/12033)) — thanks @HouMinXi +- **fix(providers):** `cloudflare-ai` no longer refuses image content parts for every Workers AI model ([#12002](https://github.com/diegosouzapw/OmniRoute/pull/12002)) — the plain-string `content` requirement behind #2539 is carried by the _model_ schema, not by the `/ai/v1/chat/completions` endpoint (measured: an all-text part array returns 200 on `@cf/mistralai/mistral-small-3.1-24b-instruct`, `@cf/meta/llama-4-scout-17b-16e-instruct` and `@cf/meta/llama-3.3-70b-instruct-fp8-fast`, and 400 on the text-only `@cf/qwen/qwen2.5-coder-32b-instruct`). `transformRequest()` flattened every array and threw on the first non-text part (#6390), so image input was refused for vision-capable Cloudflare models that accept it. All-text arrays are still flattened — the one shape every model accepts — while an array carrying a non-text part is passed through untouched, so the attachment is still never silently dropped. Regression guards: `tests/unit/cloudflare-ai-image-parts-6390.test.ts`. — thanks @davidlinfr +- **fix(resilience):** decouple the limiter-managed execution backstop from the queue-wait budget — new `requestQueue.executionMaxWaitMs` (env `RATE_LIMIT_EXECUTION_MAX_WAIT_MS`, default 600000 = 10 min) now feeds Bottleneck's post-dispatch `expiration`, while `requestQueue.maxWaitMs` keeps its documented queue-wait semantics. Previously the queue-wait budget doubled as the execution expiration, so legitimate long-running calls on non-incremental gateways (whole generation buffered before the first upstream byte, e.g. Console Go / Command Code tiers serving GLM models) were killed mid-flight at the queue budget with a false 504 `RATE_LIMIT_EXECUTION_TIMEOUT` — the local limiter undercut the provider-aware upstream fetch-start timeouts. The surfaced 504 message now names `requestQueue.executionMaxWaitMs`; the error keeps the #4165 guarantees (disclaims an upstream timeout, preserves the Bottleneck error as `cause`, branded code + trusted provenance, classified request-scoped so combo falls back). A real queue-wait bound (the `Promise.race` around `limiter.schedule()` sketched in #9533) remains future work. (#12025) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **Call logs:** keep the `error` field when an artifact exceeds the storage cap, instead of replacing it with the omission marker. The error is the only field that says *why* a request failed and is typically ~90 bytes next to the multi-hundred-KB bodies that trip the cap, so dropping it left a size-limited row undiagnosable — a provider outage, a local timeout and an upstream 400 all rendered identically. It is now preserved at every fallback stage, truncated to 4KB if it is itself large ([#12026](https://github.com/diegosouzapw/OmniRoute/issues/12026)). ([#12095](https://github.com/diegosouzapw/OmniRoute/pull/12095)) — thanks @ntdat812 +- **fix(diagnostics):** preserve the error field (truncated to 4KB with a `[truncated: …]` suffix) in every call-log artifact size-limit fallback stage. Previously the minimal fallback replaced the error with `[omitted: call log artifact size limit exceeded]`, so an oversized artifact row showed nothing about WHY the request failed — e.g. 91 of 847 opencode-go 504 rows on one production instance were undiagnosable from the dashboard. Oversized request/response bodies are still omitted exactly as before; the error cap is independent of the payload sizes that tripped the fallback. (#12026) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **fix(sse):** OpenAI Responses clients that declare the native `web_search` tool now receive a spec-shaped `web_search_call` output item with `action.sources` alongside the preserved function-call round-trip, so search results executed through OmniRoute's own search backend are consumable by standard Responses clients (Codex, pi-web-access, …). ([#12031](https://github.com/diegosouzapw/OmniRoute/pull/12031)) — thanks @watchingdogs +- **fix(cli):** use in-thread alias resolver hooks on modern runtimes to avoid deprecation noise and improve Node.js forward compatibility ([#12073](https://github.com/diegosouzapw/OmniRoute/issues/12073)). ([#12083](https://github.com/diegosouzapw/OmniRoute/pull/12083)) — thanks @xiaoyaner0201 +- **fix(combo):** an operator-set **Agent Features → Context length** on a combo is now honored at request time. The value was persisted and advertised through `/v1/models`, but `resolveComboContextLimit()` never consulted it — so a multi-target combo whose members carry no per-model window fell through to the provider's generic `defaultContextLength` (openrouter 128000, command-code 200000) and rejected large requests with `Input exceeds context window … limit 128000` despite the combo being explicitly sized much larger. An identical single-target combo worked, because it collapses to its concrete target before the guard runs. Invalid values (0/negative/NaN/Infinity) are ignored, so the existing target → combo-min → fallback order is unchanged. ([#12090](https://github.com/diegosouzapw/OmniRoute/pull/12090)) — thanks @adivekar-utexas +- **fix(sse):** passthrough streams now estimate usage on finish when upstream closes without usage even with `stream_options.include_usage` — avoids `0 tokens / 0%` for providers that stay silent (and correctly handles trailing empty-choices usage) ([#12151](https://github.com/diegosouzapw/OmniRoute/pull/12151)) — thanks @maxmad64bis +- **fix(combos):** clearing an agent feature in the combos editor now persists — unchecking context cache protection, or emptying the system message or tool filter, sends an explicit `null` instead of dropping the field from the `PUT` body, which the update merge read as "leave unchanged" ([#12177](https://github.com/diegosouzapw/OmniRoute/pull/12177)) — thanks @foreveryh / @ntdatt812 +- **fix(memory):** self-hosted embedding endpoints now vectorize — the vector width is measured from the first embedding that comes back instead of being read from a registry that cannot describe them, so `vec_memories` is created and memories stop piling up unvectorized behind a green health check ([#12180](https://github.com/diegosouzapw/OmniRoute/pull/12180)) — thanks @kanade-hoshino / @ntdatt812 +- **fix(translator):** the leading `system` message now reaches Responses-API upstreams when its `content` is a content-part array — it was read as `typeof content === "string" ? content : ""`, so a prompt-caching client (Anthropic `cache_control`, the shape LiteLLM and the Anthropic SDK emit) had its entire system prompt replaced by an empty `instructions`. The request was still accepted with a normal `prompt_tokens` count, so the model answered with no instructions and nothing in the response said they were missing. Mid-conversation system turns already handled the array shape ([#7056](https://github.com/diegosouzapw/OmniRoute/pull/7056)); only the first one did not ([#12206](https://github.com/diegosouzapw/OmniRoute/issues/12206)). Regression guard: `tests/unit/translator-openai-responses-system-content-parts.test.ts`. ([#12207](https://github.com/diegosouzapw/OmniRoute/pull/12207)) — thanks @Gorillaz322 +- **fix(free-tier):** `/api/free-tier/summary` no longer computes its totals from a Radar feed built before the catalog the running release ships. When the cached feed is older — or carries no build date at all — the route answers from the shipped catalog, resolved through the operator's local model state so disabled and tombstoned models stay out of the numbers ([#12215](https://github.com/diegosouzapw/OmniRoute/pull/12215)). — thanks @maxmad64bis +- **fix(oauth):** Keep a Claude personal workspace and a Team organization as separate connections — they share the same email and `accountUUID`, so the email-only OAuth dedup let the second login overwrite the first account's tokens; `organizationUUID` now disambiguates them, the way `workspaceId` does for Codex ([#12222](https://github.com/diegosouzapw/OmniRoute/pull/12222)) — thanks @davidebaraldo +- **fix(resilience):** a 402 on a single paid model of a passthrough/gateway provider (e.g. `kilo-gateway`, `ollama-cloud`) no longer terminalizes the whole connection with a never-auto-recovered `credits_exhausted` status — only the paid model is locked out, so free models on the same key keep serving. 402 variant of [#3027](https://github.com/diegosouzapw/OmniRoute/issues/3027). Single-credential providers are unaffected — a 402 there is still treated as the key being genuinely out of credit ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239) / [#10616](https://github.com/diegosouzapw/OmniRoute/issues/10616)) ([#12242](https://github.com/diegosouzapw/OmniRoute/issues/12242)) — thanks @brick30llc-ctrl ([#12266](https://github.com/diegosouzapw/OmniRoute/pull/12266)) — thanks @geekyNads +- **fix(sse):** trust `finish_reason: "length"`/`"max_tokens"` over the reasoning-consumed-token ratio in response quality validation, so a reasoning model truncated below the old 90% threshold correctly fails and retries instead of returning empty content as a silent "success" ([#12262](https://github.com/diegosouzapw/OmniRoute/pull/12262)) — thanks @brick30llc-ctrl +- **fix(settings):** `PUT /api/settings/cache-config` now persists `alwaysPreserveClientCache` to the flat general settings the runtime cache-control policy actually reads; previously the value landed in the databaseSettings "cache" section and was silently ignored, so the endpoint had no effect on `cache_control` passthrough ([#12304](https://github.com/diegosouzapw/OmniRoute/pull/12304)) — thanks @davidebaraldo +- **fix(grok-cli):** treat omitted SuperGrokPro `creditUsagePercent` as 0% used so Provider Limits still renders a weekly bar (proto3 zero-elision) ([#12312](https://github.com/diegosouzapw/OmniRoute/pull/12312)) — thanks @HouMinXi +- **fix(combo):** Expose the two Auto-Combo scoring factors nobody could set — the combo validation schema and the dashboard weight sliders both declared 13 of the scorer's 15 factors, so `connectionDensity` (spreads load across a provider's connections) and `quality` were dropped on save and offered nowhere. The sliders also shipped their own default table that differed from the engine's on every non-zero factor and summed to 1.05, so the percentages shown next to them added up to 105%. Both lists now match `DEFAULT_WEIGHTS`, and a test keeps them there. Note that a combo whose stored `weights` omitted the two keys was effectively running with them at zero and the other thirteen renormalized upward; it now runs with the engine's intended distribution, so its routing does shift ([#12314](https://github.com/diegosouzapw/OmniRoute/pull/12314)) — thanks @maxmad64bis +- **fix(docs):** The free-tier reference no longer says its numbers come "confidence tagged per row" — no catalog entry carries a confidence tag and the API serves none, so every figure on that page is an estimate of the same, unstated quality. The page now states what an entry does vouch for: an independently documented hard stop (set by hand with the source in a comment, never defaulted to `true`) and a prompt-training disclosure, both with live counts the `check:docs-counts` gate keeps honest ([#12318](https://github.com/diegosouzapw/OmniRoute/pull/12318)) — thanks @maxmad64bis +- **fix(usage):** `adobe-firefly` and `firefly` have had a working usage fetcher since Adobe Firefly landed, but neither was ever added to the registration list, so the provider-plugin manifest, `genericQuotaFetcher` and the free-access quota cache all reported them as having no usage support — while `USAGE_SUPPORTED_PROVIDERS` said the opposite. Both are now declared, which also means their credit balance is fetched like any other declared provider's: `registerGenericQuotaFetchers` now registers a generic quota fetcher for them, and `resolveFreeAccessState` no longer returns early. A test holds the registration list to the dispatcher's switch in both directions, which is what the module's own docstring already asked for in prose ([#12321](https://github.com/diegosouzapw/OmniRoute/pull/12321)) — thanks @maxmad64bis +- **fix(quota):** drop the generic quota cache (agy / Antigravity / Claude OAuth) on an upstream 429 so reset-aware scoring does not keep a 60s stale snapshot, and force-refresh the next usage fetch so inner provider caches cannot recache the same window ([#12325](https://github.com/diegosouzapw/OmniRoute/pull/12325)) — thanks @HouMinXi +- **fix(combos):** deleting a combo now clears its persisted LKGP pins instead of leaving unreachable `key_value` rows behind ([#12326](https://github.com/diegosouzapw/OmniRoute/issues/12326)) ([#12425](https://github.com/diegosouzapw/OmniRoute/pull/12425)) — thanks @KrzysiekSko +- **fix(sse):** Keep ZWNJ (U+200C) and ZWJ (U+200D) in assistant text, reasoning and tool-call arguments — Persian/Kurdish half-space (`ارائه‌دهنده`), Arabic/Indic shaping and emoji sequences no longer lose them; the response de-obfuscation now removes joiners only between ASCII word characters, where the request side inserts them ([#12186](https://github.com/diegosouzapw/OmniRoute/issues/12186)) — thanks @rezjalibd ([#12359](https://github.com/diegosouzapw/OmniRoute/pull/12359)) — thanks @pacocartones +- **fix(resilience):** count resolved upstream 5xx results against the provider circuit breaker on the chat path — `CircuitBreaker.execute()` no longer reads a resolved `{ success: false, status: 5xx }` as a success that cancels the call-site failure, so a provider answering 503s now trips its breaker instead of staying `CLOSED` at `failureCount: 1`; single-model and combo dispatches are each accounted exactly once ([#12254](https://github.com/diegosouzapw/OmniRoute/issues/12254)) ([#12360](https://github.com/diegosouzapw/OmniRoute/pull/12360)) — thanks @pacocartones +- **fix(providers):** resolve the Codex quota auto-ping model from the live provider catalog and lifecycle registry instead of the retired `gpt-5.1-codex-mini`, and pause the ping with one actionable warning when no selectable Codex model exists rather than retrying a shut-down id every cooldown window ([#11905](https://github.com/diegosouzapw/OmniRoute/issues/11905)) ([#12361](https://github.com/diegosouzapw/OmniRoute/pull/12361)) — thanks @pacocartones +- **fix(api):** keep the `{created, data}` wrapper on combo-routed `/v1/images/generations` responses and default Codex image results to `b64_json` on both `/v1/images/generations` and `/v1/images/edits` so Codex CLI's built-in `image_gen` can decode them ([#12268](https://github.com/diegosouzapw/OmniRoute/issues/12268)) ([#12362](https://github.com/diegosouzapw/OmniRoute/pull/12362)) — thanks @pacocartones +- **fix(sse):** Name the shadowed custom provider node when a built-in provider id/alias (e.g. `openference` → `of`) reserves the prefix of an existing OpenAI/Anthropic-compatible node, so the runtime `No active credentials for provider: ` error explains that the prefix routed to the built-in and never reached the node's healthy connections, instead of contradicting the dashboard ([#11943](https://github.com/diegosouzapw/OmniRoute/issues/11943)) — thanks @morpheus9393 ([#12365](https://github.com/diegosouzapw/OmniRoute/pull/12365)) — thanks @pacocartones +- **fix(i18n):** wrap `ccOnboardingKeyPlaceholder` in ICU single quotes across all 43 locales so angle brackets render literally instead of being parsed as rich-text tags, which crashed the Claude Code onboarding block with `INVALID_MESSAGE: INVALID_TAG` ([#12302](https://github.com/diegosouzapw/OmniRoute/issues/12302)) ([#12369](https://github.com/diegosouzapw/OmniRoute/pull/12369)) — thanks @gonisulaimann +- **fix(models):** publish `effort_tiers` on Kimi K3's synced base-model entries (`kmca/k3`, `kmca/k3-256k`) so catalog-only clients (OpenCode, plain SDK pickers) can see and select the reasoning tiers (`low`/`high`/`max`) the synced metadata already carried — the `isSkippedEffortProvider` gate no longer suppresses tier visibility on those base entries, while synthetic `-` variant generation stays prevented and Codex/GLM base models remain excluded unchanged ([#12299](https://github.com/diegosouzapw/OmniRoute/issues/12299)) ([#12371](https://github.com/diegosouzapw/OmniRoute/pull/12371)) — thanks @gonisulaimann +- **fix(guardrails):** keep `auto`/`auto/*` virtual combos exempt from the Vision Bridge `fixedModel` credential guard so a combo target is passed through instead of silently falling back to global auto-selection ([#12237](https://github.com/diegosouzapw/OmniRoute/issues/12237)) ([#12373](https://github.com/diegosouzapw/OmniRoute/pull/12373)) — thanks @pacocartones +- **fix(combo):** capability-filter exhaustion caused by `max_tokens` above every target's known output limit now reports that reason (requested `max_tokens` vs the pool's highest known ceiling) instead of the unrelated "supports structured output" message ([#12229](https://github.com/diegosouzapw/OmniRoute/issues/12229)) — thanks @DW-MediaLab ([#12374](https://github.com/diegosouzapw/OmniRoute/pull/12374)) — thanks @pacocartones +- **fix(auth):** the `least-used` account strategy now prefers accounts without backoff before falling back to oldest `lastUsedAt`, the same tie-break `round-robin` already applies, so a failover no longer lands on a just-rate-limited account for a single request ([#12279](https://github.com/diegosouzapw/OmniRoute/issues/12279)) — thanks @tenshiak ([#12375](https://github.com/diegosouzapw/OmniRoute/pull/12375)) — thanks @pacocartones +- **fix(docker):** the `chatgpt-web-codex-browser` image now finds the Chrome binary under `chrome-linux64/` (Chrome for Testing layout in `playwright:v1.62.0-noble`) as well as the legacy `chrome-linux/`, so the container no longer crash-loops with `exec: --headless=new: not found` ([#12024](https://github.com/diegosouzapw/OmniRoute/issues/12024)) ([#12376](https://github.com/diegosouzapw/OmniRoute/pull/12376)) — thanks @pacocartones +- **fix(providers):** declare `groq/compound` and `allam-2-7b` as non-reasoning models in the curated Groq registry so `reasoning_effort` / `output_config.effort` / `thinking` from Claude Code are stripped instead of forwarded, which Groq rejected with HTTP 400 ([#12134](https://github.com/diegosouzapw/OmniRoute/issues/12134)) ([#12379](https://github.com/diegosouzapw/OmniRoute/pull/12379)) — thanks @pacocartones +- **fix(executors):** `OpencodeExecutor` no longer forces a direct connection when the connection has a proxy assigned in Proxy Management but no per-account proxies: the single-account fast path used to wrap the upstream dispatch in the direct-egress sentinel, discarding the ambient proxy context the chat handler had pinned from `proxy_assignments`, so API-key `opencode`/`opencode-go` connections egressed from the host IP (and hit geoblocks) despite the assignment. The direct pin is now applied only when no ambient proxy context exists ([#11894](https://github.com/diegosouzapw/OmniRoute/issues/11894) — thanks @hizzt) ([#12380](https://github.com/diegosouzapw/OmniRoute/pull/12380)) — thanks @pacocartones +- **fix(api):** `GET /v1/models` with `MODELS_CATALOG_PREFIX_MODE=canonical` (or `?prefix=canonical`) now lists providers whose registry alias is undefined or equal to their own id (Antigravity, Antigravity CLI and other self-aliased built-ins) — their single `provider/model` id was dropped by the alias/canonical duplicate guard in the static, synced, custom and alias-backed catalog loops ([#12058](https://github.com/diegosouzapw/OmniRoute/issues/12058)) — thanks @cheynetom ([#12381](https://github.com/diegosouzapw/OmniRoute/pull/12381)) — thanks @pacocartones +- **fix(translator):** Drop replayed `thinking` blocks that carry no signature (the shape produced from cross-provider `reasoning_content`) instead of stamping the default Claude signature on them, which Anthropic rejected with `400 Invalid signature in thinking block` on the next turn served by an Anthropic rung ([#12105](https://github.com/diegosouzapw/OmniRoute/issues/12105)) — thanks @atescivitci-cmd ([#12386](https://github.com/diegosouzapw/OmniRoute/pull/12386)) — thanks @pacocartones +- **fix(cli):** Resolve Bun's `--preload` polyfill path against the package root instead of `dist/`, so `omniroute` installed with `bun install -g` no longer crashes at startup with `error: preload not found …/dist/open-sse/utils/setupPolyfill.ts` ([#11980](https://github.com/diegosouzapw/OmniRoute/issues/11980)) — thanks @joglomedia ([#12387](https://github.com/diegosouzapw/OmniRoute/pull/12387)) — thanks @pacocartones +- **fix(providers):** `gemini-business` now publishes its model catalog — `/v1/models` and `/v1/providers/gemini-business/models` list the 12 enterprise Gemini ids the executor understands instead of returning an empty list (#12107) ([#12389](https://github.com/diegosouzapw/OmniRoute/pull/12389)) — thanks @pacocartones +- **fix(db):** install `busy_timeout` before the SQLite connection's first statement so a process opening the database while another one closes its WAL connection waits out the transient EXCLUSIVE lock instead of dying with `database is locked`, and recognise the drivers' real BUSY/PROTOCOL/IOERR errors as transient in the corruption probe so the same lock no longer renames the database away as corrupt; deflakes `cross-process contenders never both acquire the same connection` (#12394 — thanks @pacocartones) +- **fix(chat-admission):** derive the `chat_admission_busy` 503 `Retry-After` from observed heavyweight-lease occupancy — the larger of the exhausted `OMNIROUTE_CHAT_ADMISSION_QUEUE_MS` window and the time since capacity last turned over, capped at 60 s — instead of a fixed 1 s (structural) / 2 s (byte-stage) hint that invited Codex/agent fan-out clients to re-send ~1 MiB `/v1/responses` bodies every second into a gate held for the whole SSE lifetime; an idle gate keeps the historical floors ([#12135](https://github.com/diegosouzapw/OmniRoute/issues/12135)) (#12395 — thanks @pacocartones) +- **fix(api-manager):** the API key permissions modal no longer silently drops `allowedCombos` entries its Combo picker cannot render — routing-rule names such as `rt-*`, which the backend already honours — when "All" is clicked and the key is switched back to "Restrict"; those entries now survive the toggle, are listed read-only under the combo list so the count and the list agree, and are saved back verbatim instead of persisting `[]` (deny-all) (#12397 — thanks @pacocartones) +- **fix(catalog):** write the NUL separator of the catalog connection memo key, the provider serviceKind memo key, the Video Bridge promotion group key and a JSON-exactness test fixture as the `\u0000` escape instead of a raw byte — same runtime value, but the raw byte made git, GitHub and ripgrep treat those files as binary (hidden PR diffs, silently skipped searches); a guard test now keeps raw NUL bytes out of `src/`, `open-sse/` and `tests/` (#12403 — thanks @pacocartones) +- **fix(providers):** add `CLAUDE_CODE_CLIENT_VERSION` and `GITHUB_COPILOT_CLI_VERSION` env overrides so Anthropic/Copilot client-version gates can be unblocked without a rebuild ([#12417](https://github.com/diegosouzapw/OmniRoute/issues/12417)) ([#12632](https://github.com/diegosouzapw/OmniRoute/pull/12632)) — thanks @HouMinXi +- **fix(db):** back-fill `last_ping_at` and `last_pinged_reset_key` on `provider_connections` during schema reconciliation so divergent lineages that skipped `123_quota_auto_ping` still accept quota auto-ping writes ([#12470](https://github.com/diegosouzapw/OmniRoute/pull/12470) — thanks @KooshaPari) +- **fix(ci):** document MIT exceptions for `@eloqnt/{config,format-json,format-po}` (next-intl transitive; locked tarballs omit `license`) and keep the A2A lifecycle vitest off the real SQLite persistence seam ([#12581](https://github.com/diegosouzapw/OmniRoute/issues/12581)) ([#12595](https://github.com/diegosouzapw/OmniRoute/pull/12595)) +- OpenCode plugin `/v1/models` catalog fetch now waits 30s by default and attaches HTTP `statusCode` on 401/5xx so host fallback plugins can hop instead of seeing an untyped AbortError/UnknownError. ([#12607](https://github.com/diegosouzapw/OmniRoute/pull/12607)) — thanks @RaviTharuma +- **CI:** the OpenAPI security-tier gate now mirrors `isAlwaysProtectedPath()` in full — it also reads `ALWAYS_PROTECTED_API_PATTERNS`, so the pattern-gated credential routes (`/api/providers/{id}/{claude,codex}-auth/{export,apply-local}`, GHSA-5926-2w35-7h4q) no longer report as unannotated. (#12605) +- **fix(api):** GET /v1/models no longer waits forever on a hung coalesced catalog rebuild; cold-path waits are bounded (`CATALOG_BUILD_TIMEOUT_MS`, default 8s) and a last-good 200 is served when the rebuild times out ([#12627](https://github.com/diegosouzapw/OmniRoute/issues/12627)). ([#12628](https://github.com/diegosouzapw/OmniRoute/pull/12628)) — thanks @RaviTharuma +- **fix(cursor):** a non-array `tool_calls` on an assistant message no longer crashes the cursor request translator with a `TypeError`; both loops now require an array ([#12689](https://github.com/diegosouzapw/OmniRoute/issues/12689)) ([#12691](https://github.com/diegosouzapw/OmniRoute/pull/12691)) — thanks @soroush5 +- **fix(chat):** Chat Completions no longer return empty `content` after a server-owned memory or skills tool; the first provider send and account/model recovery now share one pipeline so a follow-up round-trip can fill the reply ([#12696](https://github.com/diegosouzapw/OmniRoute/issues/12696)) — thanks @HouMinXi ([#12867](https://github.com/diegosouzapw/OmniRoute/pull/12867)) +- **fix(dashboard):** batch-deleting provider keys no longer toasts failure after a successful delete when the confirm button's click event is forwarded as `onAfter` ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) — thanks @HouMinXi +- **fix(glm):** drop the extra 16th argument to `createSSETransformStreamWithLogger` that TypeScript rejected (TS2554) and that never reached the TransformStream ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) ([#12770](https://github.com/diegosouzapw/OmniRoute/pull/12770)) — thanks @HouMinXi +- **fix(dashboard):** pass `nodeMap` into Runtime `QuotaGroup` so a quota monitor in error/exhausted/alerting no longer throws `ReferenceError: nodeMap is not defined`. ([#12868](https://github.com/diegosouzapw/OmniRoute/pull/12868)) — thanks @HouMinXi +- Restricted API keys whose `allowedModels` lists a combo name no longer skip every combo member at pre-dispatch (`ALL_TARGETS_SKIPPED`). Inner-target filtering still applies when the allow-list is a provider prefix or `disableNonPublicModels` is on ([#12899](https://github.com/diegosouzapw/OmniRoute/pull/12899)). — thanks @HouMinXi +- **fix(combos):** Effort-suffixed combo members inherit the base model's `model_context_overrides` row so priority order is not inverted on large requests ([#12926](https://github.com/diegosouzapw/OmniRoute/pull/12926)) — thanks @HouMinXi +- **fix(catalog):** Picker-added `customModels` enter the dispatch-time live catalog so combo members and bare inference no longer 400 ([#12934](https://github.com/diegosouzapw/OmniRoute/pull/12934)) — thanks @HouMinXi +- **fix(docker):** re-derive the Docker build's worker-pool memory budget from the MEASURED ~4.5 GB per-process RSS (the issue owner's own VPS dmesg OOM-killer reproduction) instead of the stale 2560 MB/worker inference #11419 shipped, and lower `OMNIROUTE_BUILD_WORKERS` 3 → 2 so 1 parent + 1 page-data worker (2 processes × 4.5 GB = 9 GB) fits the 12.288 GB (75%) budget on a 16 GB GitHub Actions runner — the previous default (1 parent + 2 workers = 13.5 GB) still overcommitted the runner and kept "Publish to Docker Hub" failing with `cannot allocate memory` after #11419 merged (#7518). ([#11663](https://github.com/diegosouzapw/OmniRoute/pull/11663)) +- **fix(electron):** the packaged Windows desktop build now passes the #7592 cold-restart smoke end to end. Five defects were found and fixed on the way: (1) optional-pack staging failed on any machine whose `tar` is GNU tar — it reads the drive letter in an absolute `-f C:\...` archive path as a remote rsh host (`Cannot connect to C:`), so staging now passes a bare filename with `cwd` at the tarball directory and surfaces tar stderr on failure; (2) the Electron `files` allowlist was missing `lib/loginHeaderCapture.js`, so the packaged main process crashed at startup with `Cannot find module './lib/loginHeaderCapture'`; (3) electron-builder ≥26 injects an `!**/node_modules/**` ignore into every extraResources pattern list that no positive filter can override, which silently dropped the staged runtime `node_modules` (including the better-sqlite3 N-API prebuild) from `resources/app` — a fresh v3.8.50 desktop build would have shipped with no native SQLite driver and reproduced the #7592 sql.js fallback on every machine; an `afterPack` hook now restores the staged `node_modules` after packing; (4) the packaged-app smoke harness redirected `USERPROFILE` into its temp DATA_DIR, but Electron resolves the Roaming profile from `%USERPROFILE%\AppData\Roaming\` (USERPROFILE takes precedence over the APPDATA env var) and the path service throws instead of creating it, so `requestSingleInstanceLock()` returned false and the app exited(0) silently before `app.whenReady()` — the harness now pre-creates the derived tree, and `ensureSmokeEnvDirs` is exported and unit-tested; (5) the #7592 driver assertion parsed a `[DB] Driver: ...` line that the server's primary DB path never printed (only the unused `openDatabaseAsync()` did) — `getDbInstance()` now emits the same line on open so the guard can actually assert the native driver. Also: the smoke env-allowlist unit test hardcoded POSIX paths and could only pass on Linux/macOS; expectations are now host-agnostic, with new regression tests for the USERPROFILE-derived tree and `tarPack` under absolute Windows-style paths ([#11443](https://github.com/diegosouzapw/OmniRoute/pull/11443)) — thanks @jonlwheat2-gif +- fix(sse): stop the auto-combo candidates inspector from silently dropping model-locked/cooled-down rows (#9133) ([#11994](https://github.com/diegosouzapw/OmniRoute/pull/11994)) — thanks @hartmark +- **fix(providers):** Perplexity Web no longer turns upstream stream failures into successful assistant text; pre-content failures remain eligible for fallback, partial output ends with a structured sanitized error, and failed sessions are not persisted ([#12465](https://github.com/diegosouzapw/OmniRoute/pull/12465)) +- **fix(video-bridge):** preserve chronological video and transcript fusion, source-frame timestamps, transcript provenance, full contact-sheet intervals, and pre-abort callback isolation ([#11681](https://github.com/diegosouzapw/OmniRoute/pull/11681)) +- **Z.ai Web:** HTTP 200 streams carrying an upstream error now terminate with a structured failure instead of assistant text plus a normal stop, preserving partial output while allowing pre-content combo fallback. ([#12454](https://github.com/diegosouzapw/OmniRoute/pull/12454)) +- Absorb `Error [AbortError]: request_signal_aborted` and DOMException AbortError shapes in the process-level client-abort crash guard so routine client disconnects no longer kill the server (exit code 7). ([#12165](https://github.com/diegosouzapw/OmniRoute/pull/12165)) — thanks @alvinveroy +- **fix(sse):** Treat Adapta Web `type:error` SSE events as sanitized non-stream failures instead of empty HTTP 200 completions. ([#12459](https://github.com/diegosouzapw/OmniRoute/pull/12459)) +- **fix(autoCombo):** layer 3 (`models_dev_tier`) no longer scores retired models as premium, merge cross-provider capability rows deterministically, and inherit tier scores through variant ids ([#11508](https://github.com/diegosouzapw/OmniRoute/issues/11508)). `getModelsDevTierFitness` now vetoes ids present in `config/quality/model-lifecycle.json` with status `retired` before any other signal; `loadModelCapabilities()` aggregates per `model_id` (any-non-null-true for booleans, max for `limit_context`) instead of last-write-wins over SQLite's undefined row order; a capabilities miss retries through `resolveScoresAs()`'s base id and reports `models_dev_tier:inherited`, matching layers 1–2 (#11492). Adds `getModelsDevTierFitnessWithSource()` and an `invalidateCapabilitiesCache()` test/ops hook. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) — thanks @Meet6338-X +- fix(sse): accept Claude `content:[]` + `stop_reason: max_tokens`/`tool_use` in `detectMalformedNonStream` (match `isEmptyContentResponse`) so Claude Code `/model` probes with `max_tokens: 1` no longer become a false 502 ([#11568](https://github.com/diegosouzapw/OmniRoute/pull/11568)) — thanks @Seramicx +- **fix(security):** sanitize `request.failed` diagnostics before publishing them to live dashboard listeners and replay history, while keeping status, model, provider, latency, and internal call-log diagnostics intact. ([#12469](https://github.com/diegosouzapw/OmniRoute/pull/12469)) +- **fix(pwa):** do not intercept dashboard navigations so Chrome can fall back from a stale HTTP/3 Alt-Svc advertisement (UDP :20128 is unpublished; F5 on a long-lived tab hung until a new tab opened a fresh TCP connection). ([#12767](https://github.com/diegosouzapw/OmniRoute/pull/12767)) — thanks @HouMinXi +- **fix(executors):** handle DuckDuckGo ERR_BN_LIMIT (418) without retrying — when the upstream returns `418 ERR_BN_LIMIT` (rate-limit/ban), the executor now returns the error immediately instead of burning another VQD acquisition that would only count against the IP limit. The retry logic for `418 ERR_CHALLENGE` (unsolved challenge) remains unchanged. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) ([#12110](https://github.com/diegosouzapw/OmniRoute/pull/12110)) — thanks @oyi77 / @Meet6338-X +- Fixed DuckDuckGo streaming responses losing JSON lines and UTF-8 characters split across network chunks. ([#11528](https://github.com/diegosouzapw/OmniRoute/pull/11528)) — thanks @pacocartones +- Harden generated Cloudflare, Vercel, and Deno relays by stripping hop-by-hop, framing, and proxy authentication headers before forwarding requests upstream. ([#11533](https://github.com/diegosouzapw/OmniRoute/pull/11533)) — thanks @pacocartones +- **feat(grok-cli):** Provider Limits shows grok-cli banked reset credits from `GetRemainingResets` (including a real zero; a failed RPC omits the row) and the existing View credits button now calls `ConsumerUiSvc/RedeemReset` for grok-cli. Live tokens use nested fields 10/20/30 (id + Timestamp), not compact 1/2/3. ([#12805](https://github.com/diegosouzapw/OmniRoute/pull/12805)) — thanks @HouMinXi +- **fix(memory):** Embedding Model Quick select, Embedding Source remote dropdown, and Rerank selector now list every configured provider with embedding/rerank support instead of only chat-catalog text matches plus OpenRouter live discovery; a generic OpenAI-compatible `/embeddings` + Cohere-compatible `/rerank` runtime fallback resolves any configured chat provider's embedding/rerank endpoint, so unlisted providers no longer fail with "Unknown embedding provider"; both memory selectors gained a free-text model override ([#11390](https://github.com/diegosouzapw/OmniRoute/pull/11390)) — thanks @rqzbeh +- Harden SQLite upgrades around the historical migration-074 version collision: missing discovery and inspector tables are replayed atomically, pre-existing databases (including setup-created skeletons) receive reusable content-addressed safety snapshots, and Node test/eval probes without `DATA_DIR` are isolated from the operator database. ([#12435](https://github.com/diegosouzapw/OmniRoute/pull/12435)) +- **fix(dashboard):** Moonshot/Kimi Open Platform voucher and cash leftover percentages follow the bucket balance, so an empty wallet no longer paints those rows as 100% while Available is 0% ([#12733](https://github.com/diegosouzapw/OmniRoute/pull/12733)) — thanks @HouMinXi +- **fix(combo):** return non-retryable HTTP 400 when all candidates for a pinned native Codex turn are unavailable due to model-scoped lockout, terminating the turn cleanly while preserving turn continuity and enabling standard Combo routing on subsequent turns ([#12240](https://github.com/diegosouzapw/OmniRoute/pull/12240)) — thanks @mdigitalbh81 +- Fixed auto-router strategies preserving the selected connection through ranking and dispatch when multiple connections share the same provider and model. ([#11530](https://github.com/diegosouzapw/OmniRoute/pull/11530)) — thanks @pacocartones +- Preserve Chat Completions JSON-object response formats when translating requests to the Responses API. ([#11529](https://github.com/diegosouzapw/OmniRoute/pull/11529)) — thanks @pacocartones +- Fix idempotency fingerprints so Responses API requests include `input` and other semantic generation fields using stable serialization, while excluding credentials and request metadata. ([#11532](https://github.com/diegosouzapw/OmniRoute/pull/11532)) — thanks @pacocartones +- **fix(api):** Generated API CLI commands now enforce required OpenAPI request bodies; Combo test commands forward the required `comboName` body, while API keys created by older writers after migration 149 preserve legacy allow-all Combo access without widening explicit empty allowlists — thanks @marcelokarval ([#12070](https://github.com/diegosouzapw/OmniRoute/pull/12070)) +- **fix(grok-web):** treat upstream streaming failures as failures instead of successful assistant text: error-only streams now fail readiness with HTTP 502, while failures after legitimate content preserve that partial output and terminate through the sanitized stream failure path without a normal `stop` completion. ([#12458](https://github.com/diegosouzapw/OmniRoute/pull/12458)) +- HuggingChat now turns HTTP 200 JSONL generation failures into a sanitized 502 before content, or a fixed public stream failure after partial output, so fallback and request persistence no longer record a false successful stop. ([#12456](https://github.com/diegosouzapw/OmniRoute/pull/12456)) +- **fix(providers):** keep 1min.ai HTTP 200 stream errors out of assistant content, preserve partial output, and expose sanitized terminal errors so pre-content failures can fall back. ([#12466](https://github.com/diegosouzapw/OmniRoute/pull/12466)) +- **fix(sessions):** preserve legacy Sessions data when the additive exclusive-session projection is unavailable, returning an empty projection and warning only once per contiguous outage instead of failing the endpoint; the Sessions badge intentionally reflects the merged legacy and exclusive row count introduced by [#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389) ([#11469](https://github.com/diegosouzapw/OmniRoute/pull/11469)) — thanks @KaspaPulse +- Render Nimble Search with the generic provider icon and serve Opper's proven logo locally. ([#12415](https://github.com/diegosouzapw/OmniRoute/pull/12415)) +- **fix(quality):** `config/quality/provider-assets-provenance.jsonl` was stale against the physical `public/providers/` tree (still listing 225 assets and 5 duplicate-content groups after several provider-retirement PRs removed 85 icon files), which would have made `check:provider-asset-provenance` fail on the next full run. Reconciled the manifest to the real 142-file snapshot (142 asset records, 1 duplicate-content group, 2 newly-registered files marked `unresolved` pending provenance research), repinned `auditedCommit`, and updated the regression test's hardcoded counts to match. ([#11876](https://github.com/diegosouzapw/OmniRoute/pull/11876)) +- fix(ui): wrap quota card footer action buttons to prevent clipping Refresh button on narrow cards — when extra action buttons such as `View credits` are present on Codex cards, allow the footer row to flex-wrap with `shrink-0` buttons so `Refresh now` remains visible and clickable across all responsive card widths ([#11464](https://github.com/diegosouzapw/OmniRoute/pull/11464)) — thanks @adevwithpurpose +- Keep Antigravity Gemini usable when the same connection's Claude weekly quota is empty; generic quota cache stays per-connection for every other provider. ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) — thanks @HouMinXi +- Fixed Tailscale tunnel setup to reject ports outside the valid 1-65535 range and safely install Windows MSI packages from paths containing apostrophes. ([#11534](https://github.com/diegosouzapw/OmniRoute/pull/11534)) — thanks @pacocartones +- Classify Cloudflared and Tailscale process-management routes as local-only and spawn-capable while preserving remote access to read-only tunnel status endpoints. ([#11531](https://github.com/diegosouzapw/OmniRoute/pull/11531)) — thanks @pacocartones +- Electron release workflow: the `publish-npm` job now grants `actions: read` to the reusable `npm-publish.yml` it calls (its `publish` job requests it), which is what made GitHub refuse the whole v3.8.50 run at startup and ship the release with zero desktop assets; a `workflow_dispatch` now builds the requested tag instead of the dispatching branch and can skip the npm leg (`publish_npm=false`) when only re-attaching assets ([#11974](https://github.com/diegosouzapw/OmniRoute/pull/11974)) +- **fix(dashboard):** The Combos page usage guide now reads its dismissal through `useSyncExternalStore` instead of correcting SSR state inside an effect, removing an extra commit of the page tree on every load (and the `react-hooks/set-state-in-effect` error it raised). ([#12671](https://github.com/diegosouzapw/OmniRoute/pull/12671)) +- Electron release: `electron/package-lock.json` regained the optional `electron-builder-squirrel-windows` subtree (13 entries) that `npm ci` had been refusing as out of sync, `electron-release.yml` gained a `build_ref` dispatch input and stops regenerating release notes on a re-attach dispatch, and the npm publish workflow attaches the SBOM to the GitHub Release on dispatch publishes too — so the v3.8.51 tag ships every desktop asset and the SBOM like v3.8.49 did ([#12022](https://github.com/diegosouzapw/OmniRoute/pull/12022)) +- Drained the reds every PR against `release/v3.8.51` was born with: documented the three service-worker build-id variables, registered the six retirement/tunnel tests with the mutation gate, approved `eslint-plugin-react-hooks` in the dependency allowlist, added the six `combo.sort.*` strings to `vi` and `pt-BR`, pointed the ChatGPT Web doc at the real migration-168 test, worded the g4f hint around the member key, and realigned four tests to the retired-provider catalog and the legacy-schema fixtures the retirement migrations touch. ([#11940](https://github.com/diegosouzapw/OmniRoute/pull/11940)) +- **fix(providers):** Claude, Grok, LMArena, Notion, and Perplexity web-cookie transports now use pooled `wreq-js` 3.2 instead of the native sidecar, with all nine supported bindings pinned and audited, and the applicable platform binding plus native-license evidence included in each release artifact ([#12429](https://github.com/diegosouzapw/OmniRoute/pull/12429), supersedes [#11753](https://github.com/diegosouzapw/OmniRoute/pull/11753)). +- **fix(providers):** Zed Hosted streaming failures now trigger fallback before content and end partial streams with a sanitized structured error instead of fake assistant text and a normal-success stop. ([#12455](https://github.com/diegosouzapw/OmniRoute/pull/12455)) +- **fix(test):** capture hook result after render (direct commit `b85d0abb0b`) +- **fix(sync):** repair the two regressions the v3.8.50 sync-back left on release/v3.8.51 (direct commit `5ade9e0851`) +- **revert(models):** drop the memo reset hook ported from #10055 (direct commit `282de841ae`) +- **fix(typecheck):** guard the optional connectionId before the reactive model sync (direct commit `f2825bad75`) +- **fix(quota-share):** release the winner's reserved in-flight slot (#11371) ([#11408](https://github.com/diegosouzapw/OmniRoute/pull/11408)) — thanks @oyi77 +- **fix(providers):** stop silently dropping reasoning effort on opencode families (#10788) ([#11409](https://github.com/diegosouzapw/OmniRoute/pull/11409)) — thanks @oyi77 +- **fix(combo):** enable genuine quota-aware routing for generic providers (antigravity, claude, etc.) ([#11411](https://github.com/diegosouzapw/OmniRoute/pull/11411)) — thanks @HouMinXi +- **fix(oauth):** allow expired token retry before deactivation and activate expired retry loop (#P0) ([#11414](https://github.com/diegosouzapw/OmniRoute/pull/11414)) — thanks @HouMinXi +- **perf(executors):** lazy-load the executor registry — defer class imports + construction to first use (#11220) ([#11421](https://github.com/diegosouzapw/OmniRoute/pull/11421)) — thanks @oyi77 +- **fix:** make release/v3.8.50 CI gates green (run 32786966560 root causes) ([#11450](https://github.com/diegosouzapw/OmniRoute/pull/11450)) — thanks @jonlwheat2-gif +- **fix(sse):** exact-domain cookie match and origin-equality URL assertions ([#11452](https://github.com/diegosouzapw/OmniRoute/pull/11452)) — thanks @hartmark +- **fix(providers):** send Responses API payload during OpenAI-compatible validation ([#11454](https://github.com/diegosouzapw/OmniRoute/pull/11454)) — thanks @tuandinh0801 +- **fix(command-code):** fallback to /alpha/generate for Go plan without Provider API access ([#11455](https://github.com/diegosouzapw/OmniRoute/pull/11455)) — thanks @Chewji9875 +- **fix(responses-continuation):** fail closed on a log-truncated stored input/output array ([#11473](https://github.com/diegosouzapw/OmniRoute/pull/11473)) — thanks @hartmark +- **fix(cli):** write jcode settings as [providers.omniroute] in config.toml ([#11484](https://github.com/diegosouzapw/OmniRoute/pull/11484)) — thanks @hongnoul +- **fix(logging):** raise chat-log array truncation cap to 1000 and unify duplicate implementations ([#11499](https://github.com/diegosouzapw/OmniRoute/pull/11499)) — thanks @hartmark +- **fix:** restore live websocket URL helpers ([#11509](https://github.com/diegosouzapw/OmniRoute/pull/11509)) — thanks @PauloFH +- **perf(core):** eliminate async waterfalls across home, cache, and db operations (#11396) ([#11512](https://github.com/diegosouzapw/OmniRoute/pull/11512)) — thanks @HouMinXi +- **fix(kiro):** add runtime.us-east-1.kiro.dev as first-attempt endpoint ([#11517](https://github.com/diegosouzapw/OmniRoute/pull/11517)) — thanks @rifqiawl +- **fix(providers):** remove duplicate 128-tool truncation in OpencodeExecutor (#11444) ([#11518](https://github.com/diegosouzapw/OmniRoute/pull/11518)) — thanks @HouMinXi +- **fix(providers):** poll onboardUser LRO before classifying BYOP ([#11519](https://github.com/diegosouzapw/OmniRoute/pull/11519)) — thanks @rifqiawl +- **fix(translator):** preserve exact tool name casing for Claude tool_use payloads (#11487) ([#11520](https://github.com/diegosouzapw/OmniRoute/pull/11520)) — thanks @HouMinXi +- **fix(kimi-web):** align health probe with executor domain ([#11521](https://github.com/diegosouzapw/OmniRoute/pull/11521)) — thanks @pacocartones +- **fix(cli):** preserve existing admin password during setup ([#11522](https://github.com/diegosouzapw/OmniRoute/pull/11522)) — thanks @pacocartones +- **fix(mitm):** keep Windows DNS elevation off the POSIX sudo path (#11430) ([#11542](https://github.com/diegosouzapw/OmniRoute/pull/11542)) — thanks @official-burak +- **fix(sse):** scale chat admission by ingest byte budget instead of a fixed request count ([#11548](https://github.com/diegosouzapw/OmniRoute/pull/11548)) — thanks @TheDemonTuan +- **fix:** accept Gemini Web cookie exports ([#11554](https://github.com/diegosouzapw/OmniRoute/pull/11554)) — thanks @rafacpti23 +- **fix(i18n):** complete Brazilian Portuguese translations ([#11558](https://github.com/diegosouzapw/OmniRoute/pull/11558)) — thanks @rafacpti23 +- **fix(sse):** explicit types for openai-responses pureHelpers — clears last failing core typecheck gate ([#11567](https://github.com/diegosouzapw/OmniRoute/pull/11567)) — thanks @jonlwheat2-gif +- **fix(opencode):** hydrate Proxy Pool references for no-auth connections ([#11584](https://github.com/diegosouzapw/OmniRoute/pull/11584)) — thanks @AStupidBear +- **fix(ci):** resolve inherited release/v3.8.51 base-red CI failures ([#11608](https://github.com/diegosouzapw/OmniRoute/pull/11608)) — thanks @jonlwheat2-gif +- **fix(db):** fallback load STORAGE_ENCRYPTION_KEY from env files ([#11614](https://github.com/diegosouzapw/OmniRoute/pull/11614)) — thanks @benzntech +- **fix(providers):** mark Brave Search as no longer having a free tier ([#11621](https://github.com/diegosouzapw/OmniRoute/pull/11621)) — thanks @turbolego +- **fix:** pass tool result content through as string in OpenAI/Claude -> Gemini translation ([#11624](https://github.com/diegosouzapw/OmniRoute/pull/11624)) — thanks @Hsia97 +- **fix(sse):** scope credential health to exact key ([#11628](https://github.com/diegosouzapw/OmniRoute/pull/11628)) — thanks @KaspaPulse +- **fix(install):** guard prepare script against missing husky ([#11634](https://github.com/diegosouzapw/OmniRoute/pull/11634)) — thanks @Prajeeth-12 +- **fix(security):** redact NVIDIA_API_KEY at the log sink in the ad-hoc diag script ([#11639](https://github.com/diegosouzapw/OmniRoute/pull/11639)) +- **fix(types):** clear the two type regressions red on release/v3.8.51 ([#11640](https://github.com/diegosouzapw/OmniRoute/pull/11640)) — thanks @jonlwheat2-gif +- **fix(build):** resolve workspace: protocol dependencies to concrete semver versions during prepublish (#11540) ([#11641](https://github.com/diegosouzapw/OmniRoute/pull/11641)) — thanks @HouMinXi +- **fix(search):** properly resolve configured search connection in /v1/responses pipeline (#11524) ([#11642](https://github.com/diegosouzapw/OmniRoute/pull/11642)) — thanks @HouMinXi +- **fix(authz):** trust cf-connecting-ip for via-proxy detection when peer is Cloudflare edge (#11514) ([#11643](https://github.com/diegosouzapw/OmniRoute/pull/11643)) — thanks @HouMinXi +- **fix(security):** compare the OIDC state cookie in constant time ([#11646](https://github.com/diegosouzapw/OmniRoute/pull/11646)) +- **fix(security):** namespace the dedup hash by the calling API key ([#11649](https://github.com/diegosouzapw/OmniRoute/pull/11649)) +- **fix(models):** gate the Codex-native catalog loop on prefix mode (#11632) ([#11662](https://github.com/diegosouzapw/OmniRoute/pull/11662)) — thanks @xiaoyaner0201 +- **fix(dashboard):** project Codex routing quota in account pools ([#11666](https://github.com/diegosouzapw/OmniRoute/pull/11666)) — thanks @KaspaPulse +- **fix(ddgw):** normaliza system no upstream da release v3.8.51 ([#11692](https://github.com/diegosouzapw/OmniRoute/pull/11692)) — thanks @RhianB14 +- **fix(memory):** honor category filter in GET /api/memory ([#11699](https://github.com/diegosouzapw/OmniRoute/pull/11699)) — thanks @Sabeekhann +- **fix(ci):** detect stale workspace lockfile entries ([#11714](https://github.com/diegosouzapw/OmniRoute/pull/11714)) — thanks @pacocartones +- **fix(ci):** stamp dist/BUILD_SHA before the npm publish provenance gate ([#11721](https://github.com/diegosouzapw/OmniRoute/pull/11721)) +- **fix(docker):** decouple the best-effort Bun image from the release manifest ([#11724](https://github.com/diegosouzapw/OmniRoute/pull/11724)) +- **perf(compression):** avoid cloning memo misses twice ([#11727](https://github.com/diegosouzapw/OmniRoute/pull/11727)) — thanks @RaviTharuma +- **fix(docker):** let the bun digest artifact be absent, not fatal ([#11740](https://github.com/diegosouzapw/OmniRoute/pull/11740)) +- **fix(shared):** block cloud-metadata hosts under default remote-image guard ([#11755](https://github.com/diegosouzapw/OmniRoute/pull/11755)) — thanks @santosraju99-hub +- **fix(api):** preserve caller-provided X-Correlation-Id on chat completions ([#11760](https://github.com/diegosouzapw/OmniRoute/pull/11760)) — thanks @aniruddhaadak80 +- **fix(resilience):** lock permanently retired models instead of short backoff (Gemini ban prevention) ([#11762](https://github.com/diegosouzapw/OmniRoute/pull/11762)) — thanks @turbolego +- **fix:** mark Vercel AI Gateway as passthroughModels ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **fix(resilience):** stop hammering permanently-moved endpoints and billing-suspended accounts ([#11774](https://github.com/diegosouzapw/OmniRoute/pull/11774)) — thanks @turbolego +- **fix(lease):** remove global static reservation and gate routing on live active lease occupancy ([#11775](https://github.com/diegosouzapw/OmniRoute/pull/11775)) — thanks @TheDemonTuan +- **fix(ci):** size the install-upgrade gate to a measured run, and log the pack cost ([#11776](https://github.com/diegosouzapw/OmniRoute/pull/11776)) +- **fix(pwa):** stop serving the stale shell after deploys ([#11779](https://github.com/diegosouzapw/OmniRoute/pull/11779)) — thanks @HouMinXi +- **fix(combo):** lock GitHub models rejected as "not supported" for future requests ([#11781](https://github.com/diegosouzapw/OmniRoute/pull/11781)) — thanks @turbolego +- **fix(cli):** probe both IPv4 and IPv6 loopback for server readiness (#11766) ([#11794](https://github.com/diegosouzapw/OmniRoute/pull/11794)) — thanks @vermasomesh835 +- **fix(sse):** preserve CLIProxyAPI wrappers per connection ([#11797](https://github.com/diegosouzapw/OmniRoute/pull/11797)) — thanks @RaviTharuma +- **fix(providers):** honor model auto-fetch opt-in on create ([#11798](https://github.com/diegosouzapw/OmniRoute/pull/11798)) — thanks @RaviTharuma +- **fix(dashboard):** make model auto-fetch order independent ([#11805](https://github.com/diegosouzapw/OmniRoute/pull/11805)) — thanks @RaviTharuma +- **fix(docker):** make GHA cache exports best effort ([#11806](https://github.com/diegosouzapw/OmniRoute/pull/11806)) — thanks @RaviTharuma +- **fix(kiro):** do not permanently ban on 'User is not authorized to make this call' ([#11809](https://github.com/diegosouzapw/OmniRoute/pull/11809)) — thanks @Deftera186 +- **fix(sse):** keep cache-write tokens in OpenAI-shaped usage ([#11814](https://github.com/diegosouzapw/OmniRoute/pull/11814)) — thanks @TheDemonTuan +- **fix(docs):** resolve relative markdown and wiki links across Fumadocs and GitHub wiki ([#11834](https://github.com/diegosouzapw/OmniRoute/pull/11834)) — thanks @AndrianBalanescu +- **fix(chat):** preserve unstripped model string for passthrough provider routing ([#11840](https://github.com/diegosouzapw/OmniRoute/pull/11840)) — thanks @raheemuddin786 +- **fix(mcp):** avoid async initialization inside sync esm wrappers (#11569) ([#11849](https://github.com/diegosouzapw/OmniRoute/pull/11849)) — thanks @HouMinXi +- **fix(sse):** preserve coherent markdown token boundaries across stream translation chunks (#11606) ([#11851](https://github.com/diegosouzapw/OmniRoute/pull/11851)) — thanks @HouMinXi +- **fix(ci):** run the install-upgrade gate on disk, not on the /tmp tmpfs ([#11855](https://github.com/diegosouzapw/OmniRoute/pull/11855)) +- **fix(skills):** expand shorthand property types in injected tool schemas ([#11857](https://github.com/diegosouzapw/OmniRoute/pull/11857)) — thanks @alvinveroy +- **fix(ci):** publish npm from a hosted runner so provenance is accepted ([#11877](https://github.com/diegosouzapw/OmniRoute/pull/11877)) +- **fix(lint):** pin react hooks plugin to 7.0.1 ([#11886](https://github.com/diegosouzapw/OmniRoute/pull/11886)) +- **fix(ci):** surface and clear release-green ESLint failure ([#11890](https://github.com/diegosouzapw/OmniRoute/pull/11890)) — thanks @MumuTW +- **fix(guardrails):** prevent duplicate prompt-injection-guard log output ([#11936](https://github.com/diegosouzapw/OmniRoute/pull/11936)) — thanks @patrykkopycinski +- **fix(auth):** downgrade expected transient states from warn to debug ([#11937](https://github.com/diegosouzapw/OmniRoute/pull/11937)) — thanks @patrykkopycinski +- **fix(codex):** fail over image generation for imported free plans ([#11948](https://github.com/diegosouzapw/OmniRoute/pull/11948)) — thanks @Bl0ck154 +- **fix(codex):** normalize non-stream responses ([#11951](https://github.com/diegosouzapw/OmniRoute/pull/11951)) — thanks @Bl0ck154 +- **fix(images):** forward Antigravity image size ([#11952](https://github.com/diegosouzapw/OmniRoute/pull/11952)) — thanks @Bl0ck154 +- **fix(db):** invalidate connection cache after upsert ([#11953](https://github.com/diegosouzapw/OmniRoute/pull/11953)) — thanks @Bl0ck154 +- **fix(codex):** restore imported account state ([#11954](https://github.com/diegosouzapw/OmniRoute/pull/11954)) — thanks @Bl0ck154 +- **fix(sse):** give extended-thinking targets the reasoning readiness budget ([#11959](https://github.com/diegosouzapw/OmniRoute/pull/11959)) — thanks @abhisheksharma2411 +- **fix(dashboard):** use opaque background and readable text color on cost chart tooltips ([#11960](https://github.com/diegosouzapw/OmniRoute/pull/11960)) — thanks @ZaimMarzuki +- **fix(antigravity):** send complete loadCodeAssist metadata (ideType/platform/pluginType as numeric enums) ([#11969](https://github.com/diegosouzapw/OmniRoute/pull/11969)) — thanks @rifqiawl +- **fix(core):** resolve DB init race condition and reasoning translation ([#12003](https://github.com/diegosouzapw/OmniRoute/pull/12003)) — thanks @raheemuddin786 +- **fix(admission):** exclude reclaimable page cache from the cgroup pressure ratio ([#12017](https://github.com/diegosouzapw/OmniRoute/pull/12017)) — thanks @HouMinXi +- **fix(build):** prune non-production trees in NFT trace excludes and tsconfig ([#12028](https://github.com/diegosouzapw/OmniRoute/pull/12028)) — thanks @Chewji9875 +- **fix(migrations):** add renamed migration compatibility for 056/073/077/101 ([#12036](https://github.com/diegosouzapw/OmniRoute/pull/12036)) — thanks @oyi77 +- **fix(sse):** default crash-guard logger to console.warn, not console ([#12042](https://github.com/diegosouzapw/OmniRoute/pull/12042)) — thanks @HouMinXi +- **fix(catalog):** derive vision/modalities for built-in auto combos from effective target pool ([#12046](https://github.com/diegosouzapw/OmniRoute/pull/12046)) — thanks @Prajeeth-12 +- **fix(release):** the packaged-app smoke verifies the database opened, not a driver line the primary path never prints (twin of #12032) ([#12047](https://github.com/diegosouzapw/OmniRoute/pull/12047)) +- **fix(barrel):** migrate src/app/ off the @/lib/localDb barrel import (#11795 Phase 2) ([#12051](https://github.com/diegosouzapw/OmniRoute/pull/12051)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate src/lib/ off the localDb barrel to direct db imports (#59) ([#12052](https://github.com/diegosouzapw/OmniRoute/pull/12052)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate open-sse, src/shared, src/sse, src/models, src/domain off the @/lib/localDb barrel import (#11795 Phase 4) ([#12053](https://github.com/diegosouzapw/OmniRoute/pull/12053)) — thanks @jonlwheat2-gif +- **fix(barrel):** delete the @/lib/localDb barrel — every consumer migrated (#11795 Phase 5) ([#12055](https://github.com/diegosouzapw/OmniRoute/pull/12055)) — thanks @jonlwheat2-gif +- **fix(radar):** restore D12 public boundary ([#12057](https://github.com/diegosouzapw/OmniRoute/pull/12057)) +- **fix(dev):** bound webpack and Tailwind scans ([#12075](https://github.com/diegosouzapw/OmniRoute/pull/12075)) — thanks @backryun +- **fix(dev):** isolate root layout settings reads ([#12076](https://github.com/diegosouzapw/OmniRoute/pull/12076)) — thanks @backryun +- **fix(dev):** reduce instrumentation executor fan-out (phase 3) ([#12078](https://github.com/diegosouzapw/OmniRoute/pull/12078)) — thanks @backryun +- **fix(dev):** make logging resources HMR-singleton ([#12079](https://github.com/diegosouzapw/OmniRoute/pull/12079)) — thanks @backryun +- **fix(sse):** keep unavailable forced connections scoped ([#12080](https://github.com/diegosouzapw/OmniRoute/pull/12080)) — thanks @keeltrace +- **fix(dev):** isolate batch dispatch from instrumentation ([#12081](https://github.com/diegosouzapw/OmniRoute/pull/12081)) — thanks @backryun +- **fix(ci):** reconcile release test contract drift ([#12082](https://github.com/diegosouzapw/OmniRoute/pull/12082)) — thanks @backryun +- **fix(ci):** accept CVE-2025-68121 in the prebuilt tls-client .so, auto-close base-red issues, guard Scorecard on the default branch ([#12085](https://github.com/diegosouzapw/OmniRoute/pull/12085)) +- **fix(db):** drop three consumer-less 1proxy exports — dead-code base-red on release/v3.8.51 after the barrel deletion (#12055) ([#12087](https://github.com/diegosouzapw/OmniRoute/pull/12087)) +- **fix(api):** clear the six API-route TypeScript regressions the new gate landed red on ([#12094](https://github.com/diegosouzapw/OmniRoute/pull/12094)) +- **fix(release):** never let the tag-push Create Release append auto notes to the curated body ([#12096](https://github.com/diegosouzapw/OmniRoute/pull/12096)) +- **fix(sse):** honor CLIProxyAPI environment API key ([#12099](https://github.com/diegosouzapw/OmniRoute/pull/12099)) — thanks @RaviTharuma +- **fix(oauth):** bind Google refresh to the client that issued the token ([#12106](https://github.com/diegosouzapw/OmniRoute/pull/12106)) — thanks @HouMinXi +- **fix(dashboard):** make RequestLoggerDetail loadable outside Next — CSS via globals.css + CJS/ESM interop (#11703 base-reds) ([#12114](https://github.com/diegosouzapw/OmniRoute/pull/12114)) +- **fix(leases):** project status lease row to lease columns so joined connection PII never escapes ([#12115](https://github.com/diegosouzapw/OmniRoute/pull/12115)) — thanks @geek007git +- **fix(plugins):** do not kill the plugin process when a fire-and-forget hook times out ([#12116](https://github.com/diegosouzapw/OmniRoute/pull/12116)) — thanks @geek007git +- **fix(guardrails):** restore injection-guard logging on middleware-only routes ([#12117](https://github.com/diegosouzapw/OmniRoute/pull/12117)) — thanks @geek007git +- **fix(plugins):** refresh stored manifest from disk on activate so new hook fields reach existing installs ([#12120](https://github.com/diegosouzapw/OmniRoute/pull/12120)) — thanks @geek007git +- **fix(codex):** preserve existing provider state when bulk-import upserts a matching connection ([#12122](https://github.com/diegosouzapw/OmniRoute/pull/12122)) — thanks @geek007git +- **fix(ci):** clear the two base-reds the 2026-08-30 merge batch left on release/v3.8.51 (round 3) ([#12123](https://github.com/diegosouzapw/OmniRoute/pull/12123)) +- **fix(sse):** strip type:'custom' from Claude tools on agentrouter dispatch ([#12126](https://github.com/diegosouzapw/OmniRoute/pull/12126)) — thanks @wahidsadik371-coder +- **fix(sse):** stop advertising video providers the dispatcher cannot run ([#12131](https://github.com/diegosouzapw/OmniRoute/pull/12131)) — thanks @quiterunner-commits +- **fix(search):** treat HTTP 432 and plan limit errors as transient cooldown ([#12139](https://github.com/diegosouzapw/OmniRoute/pull/12139)) — thanks @HouMinXi +- **fix(sse):** refuse an AI Horde queue that cannot fit the request budget ([#12143](https://github.com/diegosouzapw/OmniRoute/pull/12143)) — thanks @quiterunner-commits +- **fix(ci):** clear the base-reds the afternoon merge batch left on release/v3.8.51 (round 5: provider count 352, TS2554/TS2677) ([#12144](https://github.com/diegosouzapw/OmniRoute/pull/12144)) +- **fix(translator):** strip neutral tool_choice when tools absent in Responses-to-Chat (#12141) ([#12166](https://github.com/diegosouzapw/OmniRoute/pull/12166)) — thanks @HouMinXi +- **fix(guardrails):** pass providerId to getResolvedModelCapabilities in checkComboVision (#12112) ([#12169](https://github.com/diegosouzapw/OmniRoute/pull/12169)) — thanks @HouMinXi +- **fix(translator):** strip plaintext reasoning content for opaque responses backends (#12128) ([#12171](https://github.com/diegosouzapw/OmniRoute/pull/12171)) — thanks @HouMinXi +- **perf(stream):** compile hot-path regexes once, bound token caches, fix quadratic buffering ([#12179](https://github.com/diegosouzapw/OmniRoute/pull/12179)) — thanks @opensource-elearning +- **fix:** resolve compression worker file using runtime anchors instead of… ([#12183](https://github.com/diegosouzapw/OmniRoute/pull/12183)) — thanks @NightStalker-87 +- **fix:** prevent Claude Code session kills via liveness-aware readiness + auto model echo ([#12189](https://github.com/diegosouzapw/OmniRoute/pull/12189)) — thanks @opensource-elearning +- **fix(translator):** preserve falsy primitive values in Gemini and Antigravity function response results ([#12191](https://github.com/diegosouzapw/OmniRoute/pull/12191)) — thanks @Siva010 +- **fix(oauth):** mark empty Antigravity projectId as degraded and clear stale errors (#11284) ([#12205](https://github.com/diegosouzapw/OmniRoute/pull/12205)) — thanks @HouMinXi +- **fix(providers):** gate the Codex auto-ping usage read on the shared quota throttle ([#12209](https://github.com/diegosouzapw/OmniRoute/pull/12209)) — thanks @abhisheksharma2411 +- **fix(combos):** prioritize SQLite row id over inner JSON id and notify delete errors ([#12213](https://github.com/diegosouzapw/OmniRoute/pull/12213)) — thanks @HouMinXi +- **fix(providers):** mark featherless.ai as no longer having a free tier (`hasFree: false`) ([#12216](https://github.com/diegosouzapw/OmniRoute/pull/12216)) — thanks @turbolego +- **fix(deepseek-web):** stop Turbopack dev panic in the PoW worker path resolver ([#12221](https://github.com/diegosouzapw/OmniRoute/pull/12221)) — thanks @hartmark +- **fix(chat-admission):** clarify local 503 source ([#12223](https://github.com/diegosouzapw/OmniRoute/pull/12223)) — thanks @keeltrace +- **fix(dev):** silence webpack runtime module warnings ([#12228](https://github.com/diegosouzapw/OmniRoute/pull/12228)) — thanks @backryun +- **fix(db):** use module.require for CommonJS runtime driver loading ([#12230](https://github.com/diegosouzapw/OmniRoute/pull/12230)) — thanks @raheemuddin786 +- **fix(memory):** honest probe-driven FTS5 keyword status + memory_id rowid sync ([#12231](https://github.com/diegosouzapw/OmniRoute/pull/12231)) — thanks @raheemuddin786 +- **fix(system):** propagate abort signal to stream reader in HTTP version checks ([#12232](https://github.com/diegosouzapw/OmniRoute/pull/12232)) — thanks @raheemuddin786 +- **fix(resilience):** isolate local host execution errors from provider circuit breakers ([#12233](https://github.com/diegosouzapw/OmniRoute/pull/12233)) — thanks @raheemuddin786 +- **fix(sse):** sort injected tools deterministically for prompt caching ([#12234](https://github.com/diegosouzapw/OmniRoute/pull/12234)) — thanks @raheemuddin786 +- **fix(combo):** always clear the loop-safety timer, not just on the happy path (#11804) ([#12245](https://github.com/diegosouzapw/OmniRoute/pull/12245)) +- **fix(quality):** base-red pair — stryker tap registration + turn-pin suites aligned to the window gate ([#12255](https://github.com/diegosouzapw/OmniRoute/pull/12255)) — thanks @backryun +- **fix(dev):** qualify Turbopack runtime boundaries ([#12258](https://github.com/diegosouzapw/OmniRoute/pull/12258)) — thanks @backryun +- **fix(combo):** fall back to full pool when collapsed sole survivor is context-too-small ([#12278](https://github.com/diegosouzapw/OmniRoute/pull/12278)) — thanks @opensource-elearning +- **fix(combo):** bound the pre-dispatch unavailable skip so a stale label cannot dark a pool (#12168) ([#12285](https://github.com/diegosouzapw/OmniRoute/pull/12285)) +- **fix(security):** strip Qwen/Alibaba console-session cookies from provider API responses ([#12287](https://github.com/diegosouzapw/OmniRoute/pull/12287)) +- **fix(usage):** console-aware Token Plan guidance + subscription hint on bailian 401 ([#12288](https://github.com/diegosouzapw/OmniRoute/pull/12288)) +- **fix(resourcePressure):** log numeric detail on every rejection, recover faster ([#12293](https://github.com/diegosouzapw/OmniRoute/pull/12293)) — thanks @hartmark +- **fix(release):** drain the 2026-09-01 base-red window — passthrough usage regression + radarPage i18n keys ([#12327](https://github.com/diegosouzapw/OmniRoute/pull/12327)) +- **fix(ci):** openapi-security-tiers checker must honor routeGuard patterns + imported prefixes ([#12350](https://github.com/diegosouzapw/OmniRoute/pull/12350)) — thanks @ggiak +- **perf(ci):** cache node_modules in the npm-ci-retry composite (#8084 D3) ([#12408](https://github.com/diegosouzapw/OmniRoute/pull/12408)) +- **revert(ui):** point CTAs back at their real destinations ([#12410](https://github.com/diegosouzapw/OmniRoute/pull/12410)) +- **fix(release):** drain the 2026-09-02 base-red — rerank-providers import + api-typecheck baseline ratchet ([#12414](https://github.com/diegosouzapw/OmniRoute/pull/12414)) +- **fix(types):** make system prompt injection noImplicitAny-safe ([#12416](https://github.com/diegosouzapw/OmniRoute/pull/12416)) +- **fix(quality):** validate typecheck baseline schema ([#12419](https://github.com/diegosouzapw/OmniRoute/pull/12419)) +- **fix(memory):** resolve rerank provider node cache import ([#12421](https://github.com/diegosouzapw/OmniRoute/pull/12421)) +- **fix(ci):** repair release-root regressions — pack dedup, web-session syntax, uc-image ids ([#12423](https://github.com/diegosouzapw/OmniRoute/pull/12423)) — thanks @backryun +- **fix(providers):** separate MaxAI and UC credential contracts ([#12431](https://github.com/diegosouzapw/OmniRoute/pull/12431)) +- **fix(cli):** preserve Claude settings on config set ([#12432](https://github.com/diegosouzapw/OmniRoute/pull/12432)) — thanks @KrzysiekSko +- **fix(providers):** repair the maxai credential block truncated by merge auto-resolve ([#12433](https://github.com/diegosouzapw/OmniRoute/pull/12433)) +- **fix(quality):** record the 2026-09-02 merged growth in the file-size baseline ([#12434](https://github.com/diegosouzapw/OmniRoute/pull/12434)) +- **fix(uc):** route UC error strings through sanitizeErrorMessage; allowlist the retired codex id ([#12437](https://github.com/diegosouzapw/OmniRoute/pull/12437)) +- **fix(adapta):** redact streamed upstream errors ([#12438](https://github.com/diegosouzapw/OmniRoute/pull/12438)) +- **fix(sse):** remap non-contiguous upstream tool_calls index to a gap-free output_index ([#12445](https://github.com/diegosouzapw/OmniRoute/pull/12445)) — thanks @hartmark +- **fix(quality):** detect duplicate tool_calls entries in one response ([#12446](https://github.com/diegosouzapw/OmniRoute/pull/12446)) — thanks @hartmark +- **fix(conversations):** resolve turn content OmniRoute never sends back to the client ([#12447](https://github.com/diegosouzapw/OmniRoute/pull/12447)) — thanks @hartmark +- **fix(combo):** do not treat credits-exhausted 401 as auth skip ([#12449](https://github.com/diegosouzapw/OmniRoute/pull/12449)) — thanks @RaviTharuma +- **fix(auth):** do not park healthy quota accounts as expired ([#12452](https://github.com/diegosouzapw/OmniRoute/pull/12452)) — thanks @RaviTharuma +- **fix(responses-continuation):** fail closed on a collector-truncated, empty output array ([#12460](https://github.com/diegosouzapw/OmniRoute/pull/12460)) — thanks @hartmark +- **fix(logging):** raise the SSE payload collector's default cap ([#12461](https://github.com/diegosouzapw/OmniRoute/pull/12461)) — thanks @hartmark +- **fix(resilience):** surface Responses failed.error.message in 502s ([#12472](https://github.com/diegosouzapw/OmniRoute/pull/12472)) — thanks @RaviTharuma +- **fix(security):** unbiased maxai X-Random nonce + stricter URL/regex assertions ([#12502](https://github.com/diegosouzapw/OmniRoute/pull/12502)) +- **fix(video):** re-anchor transcript log-redaction so PII/credential maskers can't reopen the leak (#12150 P1 follow-up) ([#12503](https://github.com/diegosouzapw/OmniRoute/pull/12503)) +- **fix(monitoring):** serve cached credentialHealth off the request path ([#12533](https://github.com/diegosouzapw/OmniRoute/pull/12533)) — thanks @RaviTharuma +- **fix(quota):** keep Antigravity Gemini usable when Claude weekly is empty ([#12566](https://github.com/diegosouzapw/OmniRoute/pull/12566)) — thanks @HouMinXi +- **fix(authz):** hard-gate every credential export and CLI-config write (GHSA-5926-2w35-7h4q) ([#12600](https://github.com/diegosouzapw/OmniRoute/pull/12600)) +- **fix(docs):** restore the Next build — REMOVED_PROVIDERS.md had no frontmatter (base-red #12581) ([#12610](https://github.com/diegosouzapw/OmniRoute/pull/12610)) +- **fix(quality):** re-point the zcodeProtocol public-creds allowlist to line 313 ([#12615](https://github.com/diegosouzapw/OmniRoute/pull/12615)) +- **fix(security):** close 3 advisories — search baseUrl exfil, sk- in the error sanitizer, bifrost relay header leak ([#12620](https://github.com/diegosouzapw/OmniRoute/pull/12620)) +- **fix(providers):** sanitize boolean required and nested bare maps for Gemini (#12269) ([#12624](https://github.com/diegosouzapw/OmniRoute/pull/12624)) — thanks @HouMinXi +- **fix(resilience):** keep Overloaded STREAM_EARLY_EOF off the provider breaker ([#12626](https://github.com/diegosouzapw/OmniRoute/pull/12626)) — thanks @HouMinXi +- **fix(catalog):** re-audit free-tier quotas against official pages ([#12649](https://github.com/diegosouzapw/OmniRoute/pull/12649)) +- **fix(dashboard):** keep a request's pending-tracking id stable across combo target retries ([#12650](https://github.com/diegosouzapw/OmniRoute/pull/12650)) — thanks @hartmark +- **fix(tests):** retire dead model ids from the chat-pipeline integration suite (base-red #12581) ([#12670](https://github.com/diegosouzapw/OmniRoute/pull/12670)) +- **fix:** resolve SqliteError no such table compression_run_telemetry during cleanup ([#12682](https://github.com/diegosouzapw/OmniRoute/pull/12682)) — thanks @groovecityJO +- **fix(combo):** treat a pin-only step as implicit connection allowlist ([#12697](https://github.com/diegosouzapw/OmniRoute/pull/12697)) — thanks @HouMinXi +- **fix(ci):** ship .npmrc in published package so legacy-peer-deps applies to consumers (#11544) ([#12699](https://github.com/diegosouzapw/OmniRoute/pull/12699)) — thanks @KooshaPari +- **fix(docker):** pin 4 CLI tools to exact versions (#12576) ([#12703](https://github.com/diegosouzapw/OmniRoute/pull/12703)) — thanks @KooshaPari +- **fix(docker):** document and harden cli profile trust boundary (#12570) ([#12706](https://github.com/diegosouzapw/OmniRoute/pull/12706)) — thanks @KooshaPari +- **fix(sse):** redact video transcript in pre-guardrail rejected-request logs (#12150 P2 item 7) ([#12710](https://github.com/diegosouzapw/OmniRoute/pull/12710)) +- **fix(i18n):** quote placeholder in OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES description (#12505) ([#12769](https://github.com/diegosouzapw/OmniRoute/pull/12769)) — thanks @KooshaPari +- **fix(api/pricing):** surface validation error message as string, not raw object (#12494) ([#12771](https://github.com/diegosouzapw/OmniRoute/pull/12771)) — thanks @KooshaPari +- **fix(sse):** re-enable prompt compression for native Codex passthrough ([#12834](https://github.com/diegosouzapw/OmniRoute/pull/12834)) — thanks @thomasmaerz +- **fix(dashboard):** model health tests for a provider node set to the Responses API now call `/v1/responses` with a Responses-shaped body instead of `/v1/chat/completions` — those models were reported as `Provider returned HTTP 200 but no text content` even though the same model answered normally through `/v1/responses` ([#13070](https://github.com/diegosouzapw/OmniRoute/issues/13070)) ([#13087](https://github.com/diegosouzapw/OmniRoute/pull/13087)) — thanks @ntdatt812 +- fix(routing): stop a round-robin combo's "opencode" targets from collapsing onto the opencode-zen connection (#11912) ([#13283](https://github.com/diegosouzapw/OmniRoute/pull/13283)) +- fix(routing): stop the reactive-compaction debug log from lying when compression is globally disabled (#11977) ([#13276](https://github.com/diegosouzapw/OmniRoute/pull/13276)) +- fix(electron): relativize standalone-bundle symlink targets so Stage 8 manifest verification stops failing on Windows (#11979) ([#13251](https://github.com/diegosouzapw/OmniRoute/pull/13251)) +- fix(dashboard): surface a visible error when Compression Studio's combined preview run fails (#12061) ([#13288](https://github.com/diegosouzapw/OmniRoute/pull/13288)) +- fix(dashboard): make the compression "Effective pipeline" preview honor the active profile and warn when the master switch is off (#12063) ([#13287](https://github.com/diegosouzapw/OmniRoute/pull/13287)) +- fix(providers): scope TinyCMS Web signer's DOM shims to each call instead of leaking them for the process lifetime, and surface a clean HTTP status on a non-JSON interception-toggles error (#12072) ([#13254](https://github.com/diegosouzapw/OmniRoute/pull/13254)) +- fix(guardrails): stop Vision Bridge from re-selecting a model locked after a 404 (#12111) ([#13259](https://github.com/diegosouzapw/OmniRoute/pull/13259)) +- fix(sse): require Responses-shaped body before native OpenAI-compatible passthrough (#12129) ([#13278](https://github.com/diegosouzapw/OmniRoute/pull/13278)) +- fix(providers): minimax-m3 now collapses manual thinking.type:"enabled" to adaptive, preventing upstream 400 (2013) (#12132) ([#13249](https://github.com/diegosouzapw/OmniRoute/pull/13249)) +- fix(db): scope model visibility overrides by modality so hiding a Chat model no longer hides an identically-ID'd Image/Embeddings/etc. model (#12172) ([#13258](https://github.com/diegosouzapw/OmniRoute/pull/13258)) +- fix(db): scope local-provider apiKey dedup to matching base URL so LM Studio/Ollama-style connections support multiple accounts (#12173) ([#13250](https://github.com/diegosouzapw/OmniRoute/pull/13250)) +- fix(providers): refresh Trae's stale Referer/Origin and forward user timezone so imported connections stop failing with 401 (#12190) ([#13255](https://github.com/diegosouzapw/OmniRoute/pull/13255)) +- fix(providers): route opencode-go/gpt-5.6-luna to /responses instead of /chat/completions (#12196) ([#13275](https://github.com/diegosouzapw/OmniRoute/pull/13275)) +- fix(dashboard): allow deleting the last extra-upstream-header row even when invalid (#12251) ([#13252](https://github.com/diegosouzapw/OmniRoute/pull/13252)) +- **fix(i18n):** translate pre-existing `__MISSING__:` keys for `combo.sort`, `requestLogger.detail` expand/collapse, `common.profile`, and `settings.resilienceCredentialHealth*` across 39 locales ([#12272](https://github.com/diegosouzapw/OmniRoute/issues/12272)) ([#13034](https://github.com/diegosouzapw/OmniRoute/pull/13034)) — thanks @HouMinXi +- fix(cli): run the Node.js runtime compatibility guard before the heavy `tsx/esm` + Commander import chain so an unsupported runtime gets a clear message instead of a raw `Invalid regular expression flags` crash (#12296) ([#13253](https://github.com/diegosouzapw/OmniRoute/pull/13253)) +- fix(dashboard): refresh the providers list after deleting a compatible provider node (#12298) ([#13256](https://github.com/diegosouzapw/OmniRoute/pull/13256)) +- fix(usage): fail closed on API-key budget enforcement when a provider's `auto` routing alias has no pricing row, instead of silently counting it as $0 (#12341) ([#13257](https://github.com/diegosouzapw/OmniRoute/pull/13257)) +- **fix(providers):** include the submitted Agnes video model when polling by `video_id` ([#12356](https://github.com/diegosouzapw/OmniRoute/pull/12356)) — thanks @pacocartones +- **fix(routing):** custom OpenAI-compatible nodes now honor the saved Chat/Responses API type after edits instead of letting the node's original ID prefix override the live connection setting ([#11884](https://github.com/diegosouzapw/OmniRoute/issues/11884)). ([#12358](https://github.com/diegosouzapw/OmniRoute/pull/12358)) — thanks @pacocartones +- fix(sse): surface an error instead of a silent empty 200 when a Claude stream closes with zero bytes (#12398) ([#13285](https://github.com/diegosouzapw/OmniRoute/pull/13285)) +- fix(oauth): warn before the dead localhost:8080 redirect in antigravity/gemini `oauth start` (#12413) ([#13265](https://github.com/diegosouzapw/OmniRoute/pull/13265)) +- fix(providers): stop devin-cli spawn error from double-closing the SSE controller (#12517) ([#13261](https://github.com/diegosouzapw/OmniRoute/pull/13261)) +- **fix(rerank):** clamp Voyage `top_k` to the documents actually sent after empty-string filtering, and honor `return_documents: false` in the NVIDIA response adapter (#12523 — thanks @pacocartones) +- **fix(audio):** `/v1/audio/translations` now resolves combo names the way `/v1/audio/transcriptions` already does, so a combo that `GET /v1/models` advertises is fanned out to its targets instead of being rejected with `400 Invalid translation model: . Use format: provider/model`; literal `provider/model` ids and unknown bare names behave as before (#12536 — thanks @pacocartones) +- **fix(gemini):** strip the JSON-Schema-2020-12 `prefixItems` keyword from Gemini tool schemas at every nesting level, so Claude Code tool definitions no longer fail with `400 Unknown name "prefixItems"` on Gemini models (#12540 — thanks @pacocartones) +- **fix(api-manager):** Expose an accessible loading status while API keys are fetched instead of an empty accessibility tree (#12541 — thanks @pacocartones) +- **fix(video):** Clamp `estimateJpegFrameBytes` at zero for padding-only payloads and build the three encode-side frame data URIs from `JPEG_FRAME_DATA_URI_PREFIX` instead of a repeated literal (#12543 — thanks @pacocartones) +- **fix(devin):** accept Windows `DEVIN_AGENTIC_HOME` sandbox paths (`C:\...\.sandbox\...`) in the isolated-home check so the Devin Claude Bridge no longer fails closed on Windows ([#12405](https://github.com/diegosouzapw/OmniRoute/issues/12405)) (#12545 — thanks @pacocartones) +- **fix(db):** Add `conversation_turn_nodes` and orphaned `agentic_conversations` to the auto-cleanup cycle under the existing `retention.callLogs` window, so identity nodes whose call-log content has already been purged no longer accumulate without bound in `storage.sqlite` (#12548 — thanks @pacocartones) +- **fix(i18n):** Wrap the `~/.claude/profiles//settings.json` placeholder in ICU single quotes in the `OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES` feature-flag description across all 42 locales and the TypeScript default, so next-intl no longer fails with `INVALID_MESSAGE: UNCLOSED_TAG` and the Feature Flags card shows the description instead of the raw key (#12549 — thanks @pacocartones) +- **fix(orchestration):** `updateCloudAgentTask` now publishes the task's real `status` on `agent.task.updated` when an update only touches `result`, `activities` or `error`, instead of the fabricated `"updated"` state, and stays silent when no row matched the id (#12550 — thanks @pacocartones) +- **fix(i18n):** the home "Recent Requests" panel and the Provider Topology legend are now translated instead of rendering English copies on non-English dashboards; the legend reads its own `home.topologyLegend*` labels with consistent casing rather than borrowing the memory-settings "Recent" and analytics "Error" strings (#12551 — thanks @pacocartones). +- **docs(reference):** bring the `FEATURE_FLAGS.md` catalog back to 1:1 with `featureFlagDefinitions.ts` — 20 missing flags added, the two `*_BLOCK_THRESHOLD` env-only knobs moved out of the flag tables, category/total counts and the Live WS port corrected, guarded by a static test (#12552 — thanks @pacocartones) +- **fix(i18n):** backfill missing `usage.kiloPass*` strings in 39 locales and restore `featureFlagOmnirouteDisableThinkingLevelVariantsDescription` in `pt.json` ([#12561](https://github.com/diegosouzapw/OmniRoute/issues/12561)) ([#13027](https://github.com/diegosouzapw/OmniRoute/pull/13027)) — thanks @HouMinXi +- fix(docker): default docker-compose app ports (dashboard/API/live-WS) to loopback instead of `0.0.0.0`, closing the anonymous `/v1` LAN/WAN exposure gap left open by `REQUIRE_API_KEY=false` (#12568) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): close DNS-rebinding SSRF gap in webhook outbound-URL guard (#12569) ([#13243](https://github.com/diegosouzapw/OmniRoute/pull/13243)) +- fix(docker): require a per-session token on the VNC browser CDP bridge and isolate it on a dedicated Docker network (#12571) ([#13241](https://github.com/diegosouzapw/OmniRoute/pull/13241)) +- fix(open-sse): write Adobe Firefly session tokens and cookie jars with 0700/0600 permissions instead of the process umask (#12572) ([#13242](https://github.com/diegosouzapw/OmniRoute/pull/13242)) +- fix(api): remove hardcoded wildcard CORS in openai-to-gemini-sse.ts so the centralized fail-closed CORS gate is the sole source of `Access-Control-Allow-Origin` (#12573) ([#13239](https://github.com/diegosouzapw/OmniRoute/pull/13239)) +- fix(api): enforce API key policy (budget/rate-limit/schedule/endpoint scoping) on the ElevenLabs speech-to-text, text-to-speech and voices proxy routes (#12574) ([#13244](https://github.com/diegosouzapw/OmniRoute/pull/13244)) +- fix(sse): cap HuggingChat NDJSON body size and bound the read loop with the fetch timeout so a stalled or hostile upstream cannot buffer unbounded memory (#12577) ([#13282](https://github.com/diegosouzapw/OmniRoute/pull/13282)) +- fix(docker): scope the cliproxyapi/qdrant/bifrost sidecars to loopback by default and forward `CLIPROXYAPI_MANAGEMENT_KEY` into the cliproxyapi container so its management API is not left both unauthenticated and LAN-published (#12578) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): create DB export temp paths with `fs.mkdtempSync` instead of predictable timestamps (#12579) ([#13237](https://github.com/diegosouzapw/OmniRoute/pull/13237)) +- Cline 401 bodies that say "re-authenticate your Cline account" classify as a refreshable OAuth token, not a terminal expired key. The cooling panel no longer labels every cooldown as a 429; it shows the recorded last error instead. (#12594) ([#13060](https://github.com/diegosouzapw/OmniRoute/pull/13060)) — thanks @HouMinXi +- **fix(catalog):** degrade unknown combo targets instead of dropping LCD modalities, and persist OpenRouter `architecture.input_modalities` into the capability snapshot ([#12613](https://github.com/diegosouzapw/OmniRoute/issues/12613)) ([#13042](https://github.com/diegosouzapw/OmniRoute/pull/13042)) — thanks @HouMinXi +- fix(providers): send `x-api-key` instead of `Authorization: Bearer` for OpenCode Zen's `/v1/responses` endpoint (Muse Spark Contributor models), fixing a 401 on OmniRoute's auth header (#12633) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(sse): surface the actionable "Auggie CLI not found" message when the shell reports a missing `auggie` binary via exit code instead of a spawn error (#12645) ([#13263](https://github.com/diegosouzapw/OmniRoute/pull/13263)) +- **fix(config):** Persist deletions of built-in background-degradation entries — when a stored settings record exists its `degradationMap` is now authoritative instead of being merged under the defaults, so an entry the user removed in the dashboard no longer reappears on the next apply or restart ([#12424](https://github.com/diegosouzapw/OmniRoute/issues/12424)) ([#12647](https://github.com/diegosouzapw/OmniRoute/pull/12647)) — thanks @pacocartones +- **fix(gamification):** action-count badge milestones (First Token, Token Consumer, Token Machine, Token Whale, and the token-sharing tier) are now backed by a durable `xp_action_counts` counter incremented in `addXp()`, instead of a live `COUNT(*)` over `xp_audit_log`. The audit log is pruned by `retention.xpAuditLog` (default 30 days), so on a default install those "lifetime" milestones were really "actions in the last 30 days" and unlocked badges could stop unlocking once old rows aged out. `getActionCount()` and `checkActionCountBadges()` now read the same durable source, and a migration backfills existing totals from the surviving audit rows ([#12546](https://github.com/diegosouzapw/OmniRoute/issues/12546)) ([#12651](https://github.com/diegosouzapw/OmniRoute/pull/12651)) — thanks @pacocartones +- **fix(images):** `/v1/images/edits` now iterates a combo's targets the same way `/v1/images/generations` does (#9239) instead of flattening a bare combo to its first target. A combo whose first target is not edit-capable — or lacks credentials — now falls through to a later edit-capable target rather than hard-erroring, and missing credentials are skipped (not a hard `401`) to match the generations path. The per-target skip/terminal classification is extracted into a shared `runImageComboTargets` loop, so generations behavior is unchanged ([#12547](https://github.com/diegosouzapw/OmniRoute/issues/12547)). ([#12653](https://github.com/diegosouzapw/OmniRoute/pull/12653)) — thanks @pacocartones +- fix(sse): add first-byte watchdog to the TLS-fingerprint transport so a stalled wreq body falls back instead of hanging for minutes (#12656) ([#13272](https://github.com/diegosouzapw/OmniRoute/pull/13272)) +- fix(sse): exempt tiny-budget reasoning probes from combo quality failure and surface persisted-cooldown skips in ALL_TARGETS_SKIPPED diagnostics (#12659) ([#13269](https://github.com/diegosouzapw/OmniRoute/pull/13269)) +- fix(models): declare the real ~1M contextLength for OpenCode Zen's Muse Spark 1.2 models instead of falling back to the 200000 provider default (#12681) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(oauth): align codebuddy-cn OAuth User-Agent with the chat/usage CLI version to avoid WAF false positives (#12702) ([#13264](https://github.com/diegosouzapw/OmniRoute/pull/13264)) +- fix(dashboard): surface an authentication-required banner instead of silently blanking database settings for a guest session (#12709) ([#13270](https://github.com/diegosouzapw/OmniRoute/pull/13270)) +- **fix(resilience):** stop unbounded queue that hangs 6min until Aborted — gate, provider slot, and Bottleneck queue now share a per-connection `maxWaitMs` budget; `fail-closed` on exhaust (503); execution backstop `executionMaxWaitMs` overridable per connection with upstream clamp ([#12715](https://github.com/diegosouzapw/OmniRoute/pull/12715)) — thanks @maxmad64bis (with thanks to @Tushar49 for surfacing the slow-provider need in #12635) +- **fix(auto-combo):** every mode pack now carries `quality` and `reliability` weights (`quality` 0.02, 0.03 in `quality-first`; `reliability` 0.03, 0.04 in `reliability-first`), so selecting a pack no longer silences either signal; `DEFAULT_WEIGHTS` is unchanged ([#12731](https://github.com/diegosouzapw/OmniRoute/pull/12731)) — thanks @maxmad64bis +- **fix(sse):** the provider execution pipeline keeps the upstream error `code`/`type` again — both were lost when the non-streaming leg was extracted, so a config-class `422` (Antigravity missing project) degraded into a generic account cooldown instead of reaching the gate that recognises it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(db):** a legacy `call_logs` table no longer aborts startup — the provider-stats index is created after the column healing runs, not before it, so an install predating `request_type` opens instead of failing with `no such column` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(catalog):** a malformed row in the operator-writable custom-models blob no longer kills every `auto/*` pool ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(tests):** realign the guards that the same day's merges left asserting the old behavior — the provider-count total after GreenPT and EURouter, the GLM stream buffer slot that `#12925` turned into a declared parameter, and the injection scan bound that `#13104` reshaped from a head-only window into head-plus-tail ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- fix(cache): fold tool_choice/tools/response_format into the semantic cache signature so a cached tool_calls response can no longer be replayed for a request whose tool policy forbids it (#12734) ([#13267](https://github.com/diegosouzapw/OmniRoute/pull/13267)) +- **fix(models):** stop treating provider-supplied `isFree:true`/`:free`/`0/0` flags as trusted unless the provider has a documented free tier — only the shipped free-tier catalog decides otherwise; fetched `isFree:true` trusted only for a free-tier provider, custom `isFree:true` via a trusted path ([#12744](https://github.com/diegosouzapw/OmniRoute/pull/12744)) — thanks @maxmad64bis +- fix(memory): authenticate the internal /v1/rerank loopback call so memory reranking no longer silently degrades to unranked order when REQUIRE_API_KEY=true (#12745) ([#13268](https://github.com/diegosouzapw/OmniRoute/pull/13268)) +- fix(sse): parse Ollama Cloud's current usage markup (`$X of $Y used` aria-label, nested width style) (#12749) ([#13260](https://github.com/diegosouzapw/OmniRoute/pull/13260)) +- fix(cli): setup-opencode no longer sends an active context's management token to `/v1/models` when `--api-key`/`OMNIROUTE_API_KEY` is supplied — an explicit flag or the env var now always outranks the context's token, and the flag itself is no longer swallowed by the parent program's global `--api-key` option (#12783) ([#13246](https://github.com/diegosouzapw/OmniRoute/pull/13246)) +- fix(sse): register Arcee AI in the executor provider registry so requests reach api.arcee.ai instead of silently falling back to OpenAI (#12784) ([#13277](https://github.com/diegosouzapw/OmniRoute/pull/13277)) +- **fix(quota):** share one upstream quota read when identical saturation checks arrive at the same time, so traffic bursts don't multiply provider API calls ([#12787](https://github.com/diegosouzapw/OmniRoute/pull/12787)) — thanks @maxmad64bis +- **fix(db):** ignore expired or invalid rate-limit cooldown writes so a stale timestamp can't lock a connection that should be usable — clearing still works as before ([#12788](https://github.com/diegosouzapw/OmniRoute/pull/12788)) — thanks @maxmad64bis +- **fix(providers):** single source for provider order with `xao` ranking alongside `xai-oauth` ([#12790](https://github.com/diegosouzapw/OmniRoute/pull/12790)) — thanks @maxmad64bis +- **fix(routing):** off-table models check the free-model catalog before inheriting premium prices, latency bootstraps from the observed pool median, async tiers read live database pricing with a 90-day freshness gate, and the tier cache invalidates on every pricing write ([#12792](https://github.com/diegosouzapw/OmniRoute/pull/12792)) — thanks @maxmad64bis +- **fix(auto-combo):** snapshot scoring uses the observed per-connection account tier and per-model quality instead of neutral constants, and circuit-open providers rank lower at build time ([#12794](https://github.com/diegosouzapw/OmniRoute/pull/12794)) — thanks @maxmad64bis +- **fix(pool):** empty `auto/*` pools now say why they are empty, and the models listing uses the same paid check as routing ([#12795](https://github.com/diegosouzapw/OmniRoute/pull/12795)) — thanks @maxmad64bis +- fix(routing): recognize CLIProxyAPI's 'unknown provider for model' 400 as fallback-worthy (#12800) ([#13284](https://github.com/diegosouzapw/OmniRoute/pull/13284)) +- **fix(sse):** translate-mode streams now emit the estimated token counts as a trailing usage-only chunk before `[DONE]` when the upstream stays silent, so metered chat clients see totals instead of nothing ([#12828](https://github.com/diegosouzapw/OmniRoute/pull/12828)) — thanks @maxmad64bis +- **fix(db):** provider stats stay truthful on empty and legacy databases: fallback counts default to `0` instead of `null`, latency averages read `null` (not `0`) when no durations were recorded, failures logged before the error-type column existed group under `pre_migration` instead of `unclassified`, and per-provider queries use two new composite indexes ([#12832](https://github.com/diegosouzapw/OmniRoute/pull/12832)) — thanks @maxmad64bis +- fix(nvidia): fail open when a synced model catalog goes stale instead of gating forever (#12849) ([#13248](https://github.com/diegosouzapw/OmniRoute/pull/13248)) +- **fix(db):** WAL maintenance lives in its own module: a `TRUNCATE` checkpoint that hits a busy database now warns with its streak and retries once via `PASSIVE` instead of logging success, and closing-time checkpoints no longer report success on builds without a database file, with the busy totals visible in the authenticated monitoring health payload ([#12853](https://github.com/diegosouzapw/OmniRoute/pull/12853)) — thanks @maxmad64bis +- **fix(health):** quota with no snapshots reads empty instead of a contradicting 0% ([#12857](https://github.com/diegosouzapw/OmniRoute/pull/12857)) — thanks @maxmad64bis +- fix(a2a): accept the dashboard's own session cookie on /a2a so "Run message/send" no longer fails with "Unauthorized: missing or invalid API key" (#12888) ([#13271](https://github.com/diegosouzapw/OmniRoute/pull/13271)) +- **fix(logs):** the Logs grid's in-memory filter pass no longer discards rows the SQL query already matched — selecting an API key from the dropdown (which sends the key's id) returns its calls again, the Combo tab shows every combo instead of only those whose name contains a "1", and the model filter and search cover the same columns as the query ([#12896](https://github.com/diegosouzapw/OmniRoute/pull/12896)) — fixes [#12873](https://github.com/diegosouzapw/OmniRoute/issues/12873) — thanks @ntdatt812 +- **fix(a2a):** `/api/a2a/status` now builds the agent card from the request that asked for it, so a gateway reached at a non-localhost host no longer advertises `http://localhost:20128` as its A2A URL ([#12918](https://github.com/diegosouzapw/OmniRoute/pull/12918)). — thanks @ntdatt812 +- **fix(compression):** progressive aging now appends its `[COMPRESSED:aging:…]` annotation after a turn's `tool_result` blocks instead of in front of them, so Anthropic no longer rejects aged conversations with "`tool_use` ids were found without `tool_result` blocks immediately after" ([#12920](https://github.com/diegosouzapw/OmniRoute/pull/12920)). — thanks @ntdatt812 +- **fix(bedrock):** model import now resolves context limits for every vendor prefix instead of only `anthropic.*`, so `global.openai.gpt-5.6-*` no longer imports with a null `inputTokenLimit` and gets rejected pre-flight at the 200k default ([#12921](https://github.com/diegosouzapw/OmniRoute/pull/12921)). — thanks @ntdatt812 +- **fix(stream):** the 64 KB stream buffer GLM asks for is honoured instead of dropped, and the type error it caused no longer fails the API Route Typecheck gate on every open PR ([#12925](https://github.com/diegosouzapw/OmniRoute/pull/12925)) — thanks @ntdatt812 +- **fix(guardrails):** mask PII inside a `tool_result`'s nested content array, which the masker walked past while redacting its sibling block ([#12930](https://github.com/diegosouzapw/OmniRoute/pull/12930)) — thanks @ntdatt812 +- **fix(dashboard):** the Radar catalog table no longer leaves absent data unexplained — empty limits, unknown context windows, and unreported capabilities each explain themselves on hover, and a new check keeps it that way ([#12937](https://github.com/diegosouzapw/OmniRoute/pull/12937)) — thanks @maxmad64bis +- **fix(sse):** geo-blocked opencode requests rotate to the next account proxy instead of failing, so one refused egress no longer aborts the whole chain ([#12941](https://github.com/diegosouzapw/OmniRoute/pull/12941)) — thanks @maxmad64bis +- **fix(codex):** dashboard "clear cooldown" and the CAS recovery path now drop nested `codexScopeRateLimitedUntil` maps in the same write that nulls `rate_limited_until`, and fresh quota snapshots with headroom lift fallback-sourced scope cooldowns parked by quota preflight ([#12817](https://github.com/diegosouzapw/OmniRoute/issues/12817), [#12860](https://github.com/diegosouzapw/OmniRoute/issues/12860), [#12951](https://github.com/diegosouzapw/OmniRoute/pull/12951)) The dashboard reset-credit button (`consumeCodexResetCredit`) now exercises that same snapshot path after a successful redeem, so a filled quota bar is enough to unpark a leftover fallback Codex child without another manual clear. — thanks @HouMinXi +- **fix(db):** resolve `sql-wasm.wasm` across global npm install and hoisted layouts, ensuring OmniRoute can boot cleanly on Node 24 when native `better-sqlite3` is uncompiled. ([#13035](https://github.com/diegosouzapw/OmniRoute/pull/13035)) — thanks @HouMinXi +- **fix(api):** `DELETE /v1/batches/delete-completed` now sweeps only the calling API key's own completed batches (batches with no owner stay out of a key-scoped sweep on purpose), with an explicit instance-wide mode reserved for authenticated dashboard sessions, a 401 for a presented key that is unknown, revoked, deactivated, banned or expired (never falling through to the session branch), audit logging of every sweep, a sanitized 500 on failure, an owner-scoped file half (a key-scoped sweep never nulls a file another tenant owns) and an atomic sweep — chunked in 200-batch transactions in instance mode — so a mid-way error never leaves a batch pointing at a nulled file (GHSA-wvxc-jp3v-5mg5) ([#12969](https://github.com/diegosouzapw/OmniRoute/pull/12969)) +- **fix(combo):** quota-weighted routing stops drawing on an out-of-credit connection — a 402 now invalidates the stored quota snapshot instead of leaving its stale remaining percentage in place, and a snapshot older than 10 minutes no longer counts as confident headroom for the primary pool ([#12972](https://github.com/diegosouzapw/OmniRoute/pull/12972)) ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- **fix(vision):** Custom Models with "Vision capable" checked no longer have image requests swapped to `glm/glm-4.6v` when the client sends the advertised alias (`vllm/path/...`) or the bare path-shaped id — Vision Bridge now matches the stored override for all three id forms ([#12758](https://github.com/diegosouzapw/OmniRoute/issues/12758)) ([#12974](https://github.com/diegosouzapw/OmniRoute/pull/12974)) — thanks @HouMinXi +- **fix(sse):** transient opencode upstream failures rotate to the next account proxy instead of failing, so one flapping egress no longer aborts the whole chain ([#12975](https://github.com/diegosouzapw/OmniRoute/pull/12975)) — thanks @maxmad64bis +- **fix(azure):** Deployments from GPT-6 onward now send `max_completion_tokens` instead of `max_tokens`, which Azure rejects with HTTP 400. The rule matched a literal `gpt-5`, so each new generation arrived broken; it now matches the generation range, while `gpt-35-turbo` still keeps `max_tokens`. ([#13007](https://github.com/diegosouzapw/OmniRoute/pull/13007)) — thanks @ntdatt812 +- **fix(db):** add an opt-in self-restart circuit for sustained critical memory pressure, gate post-cleanup VACUUM behind a minimum freed-rows threshold, and checkpoint the SQLite WAL every 5 minutes with a size guard that escalates to TRUNCATE, so a growing WAL can no longer stall the event loop into a full outage. ([#13011](https://github.com/diegosouzapw/OmniRoute/pull/13011)) — thanks @HouMinXi +- **fix(auth):** an explicit connection pin may probe a quota-disabled row once and re-enable it on success ([#12874](https://github.com/diegosouzapw/OmniRoute/issues/12874)) ([#13017](https://github.com/diegosouzapw/OmniRoute/pull/13017)) — thanks @HouMinXi +- **fix(api):** thread `X-OmniRoute-Fallback-Attempts` through combo chat completions so streaming and non-streaming responses report how many prior legs were attempted ([#13038](https://github.com/diegosouzapw/OmniRoute/pull/13038)) — thanks @HouMinXi +- Restore provider failure classification and credential refresh on non-streaming requests: classify non-2xx failures to lock models on per-model quota exhaustion, update connection rate limits from headers and body, and pass credential refresh handlers to pipeline execution so 401 tokens can be refreshed and retried (#13043). ([#13069](https://github.com/diegosouzapw/OmniRoute/pull/13069)) — thanks @HouMinXi +- **fix(responses):** wrap forced-non-streaming web_search fallback JSON as Responses SSE so Codex still sees `response.completed` ([#13050](https://github.com/diegosouzapw/OmniRoute/pull/13050)) — thanks @HouMinXi +- **fix(antigravity):** Preserve upstream thought-token usage in normalized completion and reasoning token counts ([#13055](https://github.com/diegosouzapw/OmniRoute/pull/13055)) — thanks @pacocartones +- Fix a concurrency-slot leak in the DeepSeek PoW solver: a worker that failed to spawn (for example a missing worker script) never released its slot, so `MAX_CONCURRENT_WORKERS` failures disabled the solver until restart. ([#13097](https://github.com/diegosouzapw/OmniRoute/pull/13097)) — thanks @anhtahaylove +- **fix(acp):** bound the ACP session output buffers — `stdoutBuffer` and `stderrBuffer` now cap at 1 MiB keeping the most recent output behind a visible `[...output truncated...]` marker, and `stderrBuffer` is reset per prompt instead of accumulating for the lifetime of the session. ([#13100](https://github.com/diegosouzapw/OmniRoute/pull/13100)) — thanks @anhtahaylove +- **fix(acp):** release the `stdout`/`exit` listeners and the idle timer that a `sendPrompt` timeout used to leave attached to the `acpManager` singleton, and drop sessions that exited on their own from the session map instead of keeping them forever. ([#13096](https://github.com/diegosouzapw/OmniRoute/pull/13096)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection and PII scanners now read the text a `tool_result` block carries on `content` (string or nested block list), in messages and in system blocks, so tool output is judged by the same rules as user text ([#13101](https://github.com/diegosouzapw/OmniRoute/pull/13101)) — thanks @ntdatt812 +- **fix(gamification):** close the badge notification SSE stream when the request signal is already aborted before the stream starts — a client that disconnects while the route is still awaiting auth used to leave both the 2s unlock poll and the 15s heartbeat running for the lifetime of the process. ([#13106](https://github.com/diegosouzapw/OmniRoute/pull/13106)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection scan now spends its 16 KB budget on both ends of the request instead of the first 16 KB only, so `system`, `instructions`, `query`, `documents` and the newest turns are no longer hidden behind one long message ([#13104](https://github.com/diegosouzapw/OmniRoute/pull/13104)) — thanks @ntdatt812 +- **fix(dashboard):** expose the Volcano Ark console cookie on quota scraping and unwrap connect-error objects so the dashboard shows the upstream message ([#13107](https://github.com/diegosouzapw/OmniRoute/pull/13107)) — thanks @HouMinXi +- **fix(db):** release the `beforeExit`/`SIGINT`/`SIGTERM` handlers when a `node:sqlite` adapter closes, so a closed adapter and its database handle are no longer pinned to `process` for the lifetime of the run — the same treatment #7494 gave the sql.js adapter. ([#13109](https://github.com/diegosouzapw/OmniRoute/pull/13109)) — thanks @anhtahaylove +- **fix(translator):** `contentSchema` and `unevaluatedItems` are now treated as subschema positions by the tool-schema sanitizer, so a truncation placeholder in either is replaced with a permissive schema instead of being forwarded as a string ([#13110](https://github.com/diegosouzapw/OmniRoute/pull/13110)) — thanks @ntdatt812 +- **fix(cli-helper):** clear the `createLogStream` timeout on the abort path — `stop()` aborts the in-flight fetch and returned through the `signal.aborted` branch, which skipped `clearTimeout` and left an armed timer per stopped stream. The stream reader is now also cancelled when the read loop exits early. ([#13114](https://github.com/diegosouzapw/OmniRoute/pull/13114)) — thanks @anhtahaylove +- **fix(docs):** drop TLS-impersonation, MITM-decrypt, supply-chain attestation, and XOR-mask writeups from the public `/docs` catalog and Docker image. Files stay in git for engineers; operators who need them open the repo, not the website. ([#13136](https://github.com/diegosouzapw/OmniRoute/pull/13136)) — thanks @HouMinXi +- **fix(combo):** parse numeric-epoch `rate_limited_until` in the combo cooldown read path ([#13141](https://github.com/diegosouzapw/OmniRoute/pull/13141)) — thanks @maxmad64bis +- **fix(opencode):** both OpenCode plugins now reject a gateway address typed without `http://` at configuration time, instead of publishing every model with an api url no client can call, and the v2 plugin no longer publishes a model card whose api url is blank or relative ([#13142](https://github.com/diegosouzapw/OmniRoute/pull/13142)) — thanks @maxmad64bis +- **fix(providers):** lock opencode model on upstream 400 model-unavailable ([#13146](https://github.com/diegosouzapw/OmniRoute/pull/13146)) — thanks @maxmad64bis +- **fix(logging):** keep the provider exchange rather than the raw client bodies when a call log exceeds its size budget, and show that recovered payload in the request-detail panel instead of replacing it with the stored response body ([#13147](https://github.com/diegosouzapw/OmniRoute/pull/13147)) — thanks @maxmad64bis +- **fix(traffic-inspector):** the WebSocket route no longer leaks a traffic-buffer subscriber and a 30s ping timer when the client socket is already closed at handler time — listeners are attached before any resource is acquired, a destroyed socket bails out early, and the ping interval stops on a dead socket where `write()` never throws ([#13155](https://github.com/diegosouzapw/OmniRoute/pull/13155)) — thanks @anhtahaylove +- **fix(telegram):** bound the per-user API key cache in the Telegram chat proxy so a burst of distinct chat ids can no longer grow the process heap without limit ([#13165](https://github.com/diegosouzapw/OmniRoute/issues/13165)) ([#13166](https://github.com/diegosouzapw/OmniRoute/pull/13166)) — thanks @anhtahaylove +- **fix(stream):** cancel the upstream response body when the JSON-to-SSE sniff unwinds on a body timeout, so a stalled upstream no longer pins the connection ([#13169](https://github.com/diegosouzapw/OmniRoute/issues/13169)) ([#13171](https://github.com/diegosouzapw/OmniRoute/pull/13171)) — thanks @anhtahaylove +- **fix(telegram):** authenticate webhook deliveries with Telegram's `secret_token` so an unauthenticated caller can no longer mint API keys or spend upstream quota ([#13172](https://github.com/diegosouzapw/OmniRoute/issues/13172)) ([#13175](https://github.com/diegosouzapw/OmniRoute/pull/13175)) — thanks @anhtahaylove +- **fix(models):** give discoverable Gemini 3.8 Flash ids their own 65536 output spec so Antigravity no longer clamps them to 16384 ([#13195](https://github.com/diegosouzapw/OmniRoute/pull/13195)) — thanks @HouMinXi +- **fix(dashboard):** leftover catalog-removed provider rows (gemini-cli) can be listed and purged from the providers page ([#13067](https://github.com/diegosouzapw/OmniRoute/issues/13067)) ([#13197](https://github.com/diegosouzapw/OmniRoute/pull/13197)) — thanks @HouMinXi +- **fix(auth):** a dashboard session now requires the `authenticated: true` claim that login, OIDC and the session refresh already emit — a JWT merely signed with `JWT_SECRET` (for example the Cursor CLI passthrough token, which any API-key holder can obtain) no longer verifies as the `auth_token` cookie on any route, the WebSocket handshake or the live server; existing sessions keep working ([#13298](https://github.com/diegosouzapw/OmniRoute/issues/13298)) ([#13375](https://github.com/diegosouzapw/OmniRoute/pull/13375)) +- **fix(api):** `DELETE /v1/batches/delete-completed` now applies the caller's API-key policy (endpoint allowlist, schedule, usage cap, rate limit) like every other `/v1` route, commits the key-scoped sweep per 200-batch chunk instead of holding one write lock for the whole sweep, refuses to loop without progress, and rejects a scope that names both a key and `allTenants` ([#13684](https://github.com/diegosouzapw/OmniRoute/pull/13684)) +- Restore the API-route typecheck gate: the non-streaming leg result lost its discriminated-union narrowing after the server-owned tool loop reassignment, producing 13 new TS2339 diagnostics in `chatCore.ts`. ([#12963](https://github.com/diegosouzapw/OmniRoute/pull/12963)) +- Restore four non-streaming behaviours the server-owned tool loop refactor dropped: client aborts map to 499 with the fixed `Request aborted` message, an aborted request no longer logs a synthetic `clientResponse`, Claude prompt-cache telemetry is recorded again, and a body that cannot be canonicalized no longer throws when the tool loop is off. ([#12990](https://github.com/diegosouzapw/OmniRoute/pull/12990)) +- **fix(skills):** The CLI registry parser now reads positionals declared with `.addArgument()`, not only those written inline in `.command()`. `tunnel create [type]` was being published as `tunnel create`, so the agent-skills sync gate reported drift on every branch and regenerating would have deleted the argument. ([#13009](https://github.com/diegosouzapw/OmniRoute/pull/13009)) — thanks @ntdatt812 +- **fix(combos):** dashboard combo test uses a short prompt, serial probes, and a 60s timeout so reasoning models and rate-limited free pools do not fail the health check ([#13001](https://github.com/diegosouzapw/OmniRoute/pull/13001)) — thanks @HouMinXi +- fix(compression): terminate idle worker threads on eviction so long-running instances stop leaking OS threads and MessagePorts ([#13091](https://github.com/diegosouzapw/OmniRoute/pull/13091)) — thanks @anhtahaylove +- Redact Google API keys of any length in error bodies: the pattern required exactly 39 characters, so shorter or longer `AIza…` credentials passed through unredacted. ([#12964](https://github.com/diegosouzapw/OmniRoute/pull/12964)) +- **fix(grok-cli):** a 402 "Grok Build usage balance exhausted" parks that Grok login as out of credit (Grok Build CLI, grok.com cookie, and xAI OAuth share the weekly pool). Combo routing then tries the next login instead of locking the model for every account in the pool ([#13061](https://github.com/diegosouzapw/OmniRoute/pull/13061)) — thanks @HouMinXi +- fix(compression): spawn the LLMLingua worker with a file URL object so compression actually runs instead of silently failing open on Node ([#13093](https://github.com/diegosouzapw/OmniRoute/pull/13093)) — thanks @anhtahaylove +- **fix(test):** run the local `test` and `test:unit` scripts at concurrency 4 so a full-suite run no longer exhausts the machine's commit charge and kills unrelated processes ([#13187](https://github.com/diegosouzapw/OmniRoute/pull/13187)) — thanks @anhtahaylove +- **fix(monitoring):** `GET /api/monitoring/health` `credentialHealth` now includes a bounded `failedConnections` list (`connectionId`, `status`, sanitized `lastError`) when the probe-cache gauge `failed>0`, plus `source: probe-cache` and a cheap `staleDbNonOkCount` for sticky SQLite `test_status` on active rows. Documents that the live gauge is not `provider_connections.test_status`. ([#12876](https://github.com/diegosouzapw/OmniRoute/pull/12876)) — thanks @RaviTharuma +- **fix(resilience):** an openai-compatible multi-upstream gateway no longer marks the whole connection `credits_exhausted` when a single upstream (or the `/models` / representative-model health probe) returns 402. Quota failures stay model-scoped; true connection-wide auth failures are unchanged. Single-credential 402 key disable ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239)) is preserved. ([#12875](https://github.com/diegosouzapw/OmniRoute/pull/12875)) — thanks @RaviTharuma +- **fix(dashboard):** nine fixes on the Orchestration Canvas, all diagnosed in the Phase 2 reviews. A source that fails now keeps the timestamp of its FIRST failure instead of being re-stamped every poll — the stale line said "since the last poll" no matter how long the source had been down, and the churn also defeated the snapshot's stable identity (it serializes the source list), so the canvas re-rendered on every tick while anything was broken. A source that HAD data and only then started failing is flagged too: previously only a source with no node at all got the warning, so a source that went down mid-session kept a healthy-looking node forever. The rest are pointwise: clicking a filter chip cancels the pending search debounce (left armed, it fired ~300ms later and silently reverted the chip); the search input carries an accessible name; `?state=running, failed` parses like the unpadded form instead of dropping the padded value; the CSV toggle helper is defined once in `model/urlParams.ts` rather than twice; the agents tab tells "nothing running" apart from "the filter matched nothing", offering a clear-filters button instead of setup links that would be wrong advice there; edges stop emitting SMIL particles above 40 simultaneously active edges, keeping the colored stroke; and the drawer's error banner clears when a retried action succeeds. ([#12983](https://github.com/diegosouzapw/OmniRoute/pull/12983)) +- **fix(i18n):** the nine locales added with the EU-language batch (Greek, Estonian, Irish, Croatian, Lithuanian, Latvian, Maltese, Slovenian, Serbian) were missing the eleven Orchestration Canvas keys that Phase 3 introduced, so the compare-runs panel and the "no runs match these filters" empty state fell back to English in those languages (`deepMergeFallback` substitutes English for an absent key, so nothing rendered blank — it rendered untranslated). The coverage gate does not catch this: it enforces an 80% floor per locale, and eleven missing keys out of ~13,000 leaves coverage at 99.9%. Translated for real in each language, calibrated against the wording each file already uses for "run", "filter" and "skill". ([#13294](https://github.com/diegosouzapw/OmniRoute/pull/13294)) +- **fix(dashboard):** five follow-ups from the Phase 2 Orchestration Canvas review. The History drawer now shows the memory section for runs that already left the live TTL window: the persisted `memory_hits` event is parsed into `metadata.memoryHits` with the same defensive validation the drawer applies, and — because that event is observability rather than a state transition — it no longer leaks into the timeline, where it had been inheriting the task's state and rendering as a duplicate transition. The A2A memory recall runs against its own 1.5s deadline instead of inheriting the memory backend's 30s one; overshooting degrades exactly like any other recall failure (no hits, task proceeds), and the timer is cleared on both paths. Repeating a Conductor run carries its `requirements.cli`/`requirements.model` forward, so the new run is pinned to the same runner profile and model rather than drifting to whatever the fleet picks. A successful repeat from the Agents tab now focuses the run it created, instead of leaving the operator on the finished one. And the auth test for `POST /api/conductor/tasks` moved into the shared `ROUTES` array rather than restating the pattern. ([#12988](https://github.com/diegosouzapw/OmniRoute/pull/12988)) +- fix(plugins): stop leaking an exit listener per plugin hook timeout, which triggered MaxListenersExceededWarning on plugins that ignore SIGTERM ([#13092](https://github.com/diegosouzapw/OmniRoute/pull/13092)) — thanks @anhtahaylove +- **fix(validation):** Provider node edits no longer fail with a generic "Invalid request" when the optional daily-quota reset fields are left blank. The dashboard sends `dailyQuotaResetTimezone` and `dailyQuotaResetHour` as `null`, and only the hour accepted it. ([#13066](https://github.com/diegosouzapw/OmniRoute/issues/13066)) ([#13083](https://github.com/diegosouzapw/OmniRoute/pull/13083)) — thanks @ntdatt812 +- **fix(combo):** quota-aware expansion drops banned, inactive, missing, and wrong-provider connections before quota fetch or model dispatch; pins and allowlists stay selectors, not a bypass. Antigravity automatic exhaustion now requires a reported zero remaining, so a positive balance below 1% stays eligible. ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- Fix an intermittent failure in the quota-weighted routing test suite: scores are a function of `Date.now()`, so peers with identical quota scored microseconds apart never tied and swapped order. ([#12946](https://github.com/diegosouzapw/OmniRoute/pull/12946)) +- fix(volcengine): resolve quota window mapping gaps, safe multi-connection console binding, and response cookie sanitization ([#12950](https://github.com/diegosouzapw/OmniRoute/pull/12950)) — thanks @HouMinXi +- **fix(test):** resolve the WebDAV handler path with `fileURLToPath` so the suite's 37 WebDAV tests run on Windows instead of failing with a doubled `C:\C:\` drive prefix ([#13196](https://github.com/diegosouzapw/OmniRoute/pull/13196)) — thanks @anhtahaylove +- **fix(ui):** fall back from invalid AnySearch icon alias (direct commit `9587e07b69`) +- **fix(sse):** stop rebuilding a truncated summary from the collector's cap-dropped event array ([#12718](https://github.com/diegosouzapw/OmniRoute/pull/12718)) — thanks @hartmark +- **fix(conversations):** show a pending spinner for unresolved tool nodes instead of "(empty)" ([#12727](https://github.com/diegosouzapw/OmniRoute/pull/12727)) — thanks @hartmark +- **fix(streaming):** fail fast when an upstream stream produces only lifecycle/heartbeat events, never real content ([#12741](https://github.com/diegosouzapw/OmniRoute/pull/12741)) — thanks @hartmark +- **fix(responses-continuation):** bridge the write-in-flight window with an in-memory pending store ([#12854](https://github.com/diegosouzapw/OmniRoute/pull/12854)) — thanks @hartmark +- **fix(translator):** drop or map agent_message on Chat Completions fallback ([#12880](https://github.com/diegosouzapw/OmniRoute/pull/12880)) — thanks @RaviTharuma +- **fix(api):** page and stream-complete GET /v1/models for large catalogs ([#12882](https://github.com/diegosouzapw/OmniRoute/pull/12882)) — thanks @RaviTharuma +- **fix(quota):** align AUTH window labels with usage API and clarify 503 ([#12884](https://github.com/diegosouzapw/OmniRoute/pull/12884)) — thanks @RaviTharuma +- **fix(ci):** clear two base-reds on release/v3.8.51 — mutation-coverage gate + image-only-model guard ([#12945](https://github.com/diegosouzapw/OmniRoute/pull/12945)) +- **fix(api):** scope batch bulk-delete to the calling API key ([#13211](https://github.com/diegosouzapw/OmniRoute/pull/13211)) +- **fix(tests):** align model-sync log assertion to shipped path redaction (base-red) ([#13227](https://github.com/diegosouzapw/OmniRoute/pull/13227)) +- **fix(test):** make the ToS heading guard actually require the parentheses ([#13228](https://github.com/diegosouzapw/OmniRoute/pull/13228)) +- **fix(quality):** register 4 drifted covering tests in stryker tap.testFiles ([#13229](https://github.com/diegosouzapw/OmniRoute/pull/13229)) +- **fix(logging):** mask *-api-key headers in request-log pipeline (#13273) ([#13401](https://github.com/diegosouzapw/OmniRoute/pull/13401)) — thanks @KooshaPari +- **fix(tests):** correct stale Jina catalog prefix in custom-models test ([#13403](https://github.com/diegosouzapw/OmniRoute/pull/13403)) — thanks @KooshaPari +- **fix(backend):** prune old backups after health-check-repair snapshots (#13308) ([#13404](https://github.com/diegosouzapw/OmniRoute/pull/13404)) — thanks @KooshaPari +- **fix(backend):** guard memory_fts_au trigger with WHEN clause to stop FTS bloat (#13326) ([#13405](https://github.com/diegosouzapw/OmniRoute/pull/13405)) — thanks @KooshaPari +- **fix(free-tiers):** remove obsolete Together $25 signup credit (#12526) ([#13407](https://github.com/diegosouzapw/OmniRoute/pull/13407)) — thanks @KooshaPari +- **fix:** skip non-chat generation models in Test all models ([#13409](https://github.com/diegosouzapw/OmniRoute/pull/13409)) — thanks @KooshaPari +- **fix(providers):** de-duplicate agy and antigravity model catalogs ([#13410](https://github.com/diegosouzapw/OmniRoute/pull/13410)) — thanks @KooshaPari +- **fix(dashboard):** send provider in body for mini-playground web search ([#13411](https://github.com/diegosouzapw/OmniRoute/pull/13411)) — thanks @KooshaPari +- **fix(resilience):** add CJK quota-exhaustion patterns to 429 classifier ([#13412](https://github.com/diegosouzapw/OmniRoute/pull/13412)) — thanks @KooshaPari +- **fix(backend):** eval runner: failed calls no longer score as passed + dotAll regex ([#13413](https://github.com/diegosouzapw/OmniRoute/pull/13413)) — thanks @KooshaPari +- **fix(backend):** eval runner opts out of output-style and memory injection ([#13414](https://github.com/diegosouzapw/OmniRoute/pull/13414)) — thanks @KooshaPari +- **fix:** chain serial quarantine step in npm test script (fixes #13286) ([#13418](https://github.com/diegosouzapw/OmniRoute/pull/13418)) — thanks @KooshaPari +- **fix(antigravity):** derive stable session ID from credentials for prompt-cache reuse (#13305) ([#13424](https://github.com/diegosouzapw/OmniRoute/pull/13424)) — thanks @KooshaPari +- **fix(memory):** merge memory into top-level system field instead of unshifting at messages[0] (#13425) ([#13427](https://github.com/diegosouzapw/OmniRoute/pull/13427)) — thanks @KooshaPari +- **fix(compression):** preserve instruction blocks from lossy engines ([#13523](https://github.com/diegosouzapw/OmniRoute/pull/13523)) — thanks @KooshaPari +- **fix(compression):** preserve polarity words and newlines in ultra heuristic ([#13524](https://github.com/diegosouzapw/OmniRoute/pull/13524)) — thanks @KooshaPari +- **fix(compression):** run user preservePatterns before built-in patterns (#13457) ([#13525](https://github.com/diegosouzapw/OmniRoute/pull/13525)) — thanks @KooshaPari +- **fix(reasoning):** expand deepseek-v4 regex to match v4.1+ and add command-code provider (#13430) ([#13528](https://github.com/diegosouzapw/OmniRoute/pull/13528)) — thanks @KooshaPari +- **fix(providers):** add alwaysReasons metadata for reka-flash-3 ([#13531](https://github.com/diegosouzapw/OmniRoute/pull/13531)) — thanks @KooshaPari +- **fix(playground):** allow Compare columns to scroll independently ([#13532](https://github.com/diegosouzapw/OmniRoute/pull/13532)) — thanks @KooshaPari +- **fix(security):** empty whitelist allows through to prevent admin lockout ([#13534](https://github.com/diegosouzapw/OmniRoute/pull/13534)) — thanks @KooshaPari +- **fix(tests):** add DISABLE_IOREG_STRATEGY env var for macOS test compatibility (fixes #13467) ([#13539](https://github.com/diegosouzapw/OmniRoute/pull/13539)) — thanks @KooshaPari +- **fix(zed-hosted):** map developer role and Gemini enums for Zed proxy ([#13543](https://github.com/diegosouzapw/OmniRoute/pull/13543)) — thanks @KooshaPari +- **fix(analytics):** normalize provider aliases to canonical id in usage_history ([#13545](https://github.com/diegosouzapw/OmniRoute/pull/13545)) — thanks @KooshaPari +- **fix(combo):** use traceId for call log id to prevent UNIQUE constraint failure ([#13546](https://github.com/diegosouzapw/OmniRoute/pull/13546)) — thanks @KooshaPari +- **fix(auth):** check model lockout before returning synthetic noauth connection ([#13547](https://github.com/diegosouzapw/OmniRoute/pull/13547)) — thanks @KooshaPari +- **fix(guardrails):** skip credential redaction for base64 image data URLs ([#13550](https://github.com/diegosouzapw/OmniRoute/pull/13550)) — thanks @KooshaPari +- **fix(resilience):** increase requestQueue.maxWaitMs default from 15s to 30s ([#13553](https://github.com/diegosouzapw/OmniRoute/pull/13553)) — thanks @KooshaPari +- **fix(docs):** document OMNIROUTE_READY_TIMEOUT_MS and allowlist the test-only DISABLE_IOREG_STRATEGY ([#13692](https://github.com/diegosouzapw/OmniRoute/pull/13692)) + ### 📝 Maintenance ---- +- **docs(readme):** reconcile live v3.8.50 provider, free-tier, CLI, routing, test, community, sponsor, acknowledgment, and SVG metrics with their audited source denominators, including a deduplicated OmniRoute-in-Action snapshot and distinct contributor rankings for merged pull requests, GitHub-attributed commits, and Git history ([#11356](https://github.com/diegosouzapw/OmniRoute/pull/11356)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **docs:** sync the canonical provider count 354 → 356 across `README.md`, `AGENTS.md`, `llm.txt` (+ 42 i18n mirrors), the four README SVG diagrams, `docs/reference/PROVIDER_REFERENCE.md` (regenerated) and the `package.json` description after Opper (#11629) and 1min.ai (#11631) boarded the catalog — closes the `check:docs-counts-sync` strict drifts that kept `release/v3.8.51` red ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11673](https://github.com/diegosouzapw/OmniRoute/pull/11673)) — thanks @jonlwheat2-gif +- **test(providers):** update count-derived assertions after the v3.8.51 provider additions — `APIKEY_PROVIDERS` 233 → 235 (Opper #11629 + 1min.ai #11631), reserved-prefix REGISTRY walk 395 → 398, `WEB_FETCH_PROVIDERS` now includes `nimble-search` (#11620), and the provider translate-path golden snapshot regenerated ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11674](https://github.com/diegosouzapw/OmniRoute/pull/11674)) — thanks @jonlwheat2-gif +- **test(sse):** bump the hard-lease connection-query inventory for `src/lib/tokenHealthCheck.ts` to 2 — the verify-only web-cookie sweep added by #11495 split the single `getProviderConnections` call into oauth + cookie variants, which the frozen inventory had not tracked ([#11495](https://github.com/diegosouzapw/OmniRoute/pull/11495)). ([#11677](https://github.com/diegosouzapw/OmniRoute/pull/11677)) — thanks @jonlwheat2-gif / @oyi77 +- **chore(release):** clear the `release/v3.8.51` base-red gates — docs migration count (+38 i18n mirrors), stryker `tap.testFiles`, lockfile registry host, stale ESLint suppressions, the 7 lint regressions they were hiding, the stale `APIKEY_PROVIDERS` partition count (231 → 233), the `glm-5.3-max` tier missing from the GLM test table, a dead `modelType === "chat"` comparison in the models catalog, and the `resolveLiveWsUrl` / `sanitizeLiveWsPort` helpers from #11388 that a later merge dropped from `wsPath.ts` (dashboard live WebSocket URL resolution) ([#11502](https://github.com/diegosouzapw/OmniRoute/pull/11502)) — thanks @MumuTW +- **chore(free-models):** the free-tier regime table now declares which totals figure each regime feeds and whether it may take the no-auth shortcut, so the sets that used to repeat those answers by hand are derived from it — a new `freeType` no longer compiles until it has answered every question, instead of silently contributing to no total ([#11537](https://github.com/diegosouzapw/OmniRoute/pull/11537)) — thanks @maxmad64bis +- **docs(routing):** LKGP strategy copy now states provider-level stickiness and the explicit rules fallback instead of promising per-target pinning ([#11555](https://github.com/diegosouzapw/OmniRoute/pull/11555)) — thanks @maxmad64bis +- **test(chatcore):** pin the response tool-name alias resolution against the ordering hazard that broke every Gemini/Antigravity MCP tool call in v3.8.49. `extractRequestToolIdentityMap` deletes `translatedBody._toolNameMap`, so the later read is always undefined and the ledger survives only through the `requestToolIdentityMap` fallback — removing that fallback previously left the entire tool-name suite green. The resolution moves into `resolveResponseToolNameMap()` next to the map it depends on, with a regression guard that fails without the recovery. No behaviour change. ([#11575](https://github.com/diegosouzapw/OmniRoute/pull/11575)) — thanks @amartinawi +- **docs(env):** document `OMNIROUTE_CORPUS_CACHE_SIZE` in `.env.example` and `ENVIRONMENT.md`, so the env/docs contract check stops failing Docs Gates on every branch ([#11576](https://github.com/diegosouzapw/OmniRoute/pull/11576)) — thanks @ntdatt812 +- **chore(stryker):** register `route-guard-tunnel-processes-local-only` and `universal-quota-aware-routing` in `tap.testFiles`, so their mutant kills count for the three modules they cover ([#11577](https://github.com/diegosouzapw/OmniRoute/pull/11577)) — thanks @ntdatt812 +- **test(authz):** pin `LOCAL_ONLY_API_GET_EXEMPTIONS` by exact membership instead of by entry count, so the guard names the offending path and also catches a substitution ([#11580](https://github.com/diegosouzapw/OmniRoute/pull/11580)) — thanks @ntdatt812 +- **test(build):** pin `resolveNextBuildBundlerFlag` to the `OMNIROUTE_USE_TURBOPACK` contract it implements, across runtimes, instead of asserting an unimplemented Bun override ([#11583](https://github.com/diegosouzapw/OmniRoute/pull/11583)) — thanks @ntdatt812 +- **test(claude-web):** the Claude Web transport test's barrel anchors no longer pin the exact construction spelling. #11421 made the executor map lazy, so `/"claude-web": new ClaudeWebExecutor\(\)/` stopped matching and the test went red on `release/v3.8.51` itself — a base-red gate every open PR inherited. The anchors now match the alias key and the class name and tolerate either spelling in between, and were checked to still fail when an alias is genuinely repointed at another executor ([#11585](https://github.com/diegosouzapw/OmniRoute/pull/11585)) — thanks @ntdat812 +- **test(models):** pin `qwen3.8-max` resolution per provider — two of the four catalogs now serve the bare id and no longer carry `-preview` ([#11590](https://github.com/diegosouzapw/OmniRoute/pull/11590)) — thanks @ntdatt812 +- **test(executors):** drop the stale 128-tool truncation assertion that contradicted the #11444 pass-through contract ([#11591](https://github.com/diegosouzapw/OmniRoute/pull/11591)) — thanks @ntdatt812 +- **test(token-health):** pin the current terminal-skip boundary — expired with retry budget is probed, `account_deactivated` stays skipped ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)) — thanks @ntdatt812 +- **test(models):** follow #11503's retarget of the `gemini-3-pro-high` alias to the hyphenated catalog id ([#11593](https://github.com/diegosouzapw/OmniRoute/pull/11593)) — thanks @ntdatt812 +- **refactor(video bridge):** extract per-video acquisition, whole-result caching, description, and metrics/abort/cleanup out of `VideoBridgeGuardrail.preCall` into a `processVideoPart` seam in a new `videoBridgePipeline.ts`, behind explicit `VideoMediaBrokerPort`, `VideoAudioTranscriptionPort`, and `VideoDrilldownPort` boundaries; `preCall` now only handles request traversal, policy, and response aggregation. The Video tab's FFmpeg/ffprobe runtime status is now an explicit `unknown` / `restricted` / `unavailable` / `available` state instead of a nullable boolean pair, fixing a case where an in-flight or failed probe was mislabeled as "install FFmpeg" ([#11657](https://github.com/diegosouzapw/OmniRoute/issues/11657)). ([#12016](https://github.com/diegosouzapw/OmniRoute/pull/12016)) +- **test(opencode-plugin):** add unit test coverage for `formatFreeBudget()` naming helper ([#11660](https://github.com/diegosouzapw/OmniRoute/pull/11660)) — thanks @f9td56dbgh-hub +- **docs(video):** clarify that the Video Bridge transcript `source` field (`client`, `embedded`, `audio-bridge`) is presently caller-declared and not yet server-verified — OmniRoute enforces the enum shape but does not cryptographically confirm that an `embedded`/`audio-bridge` label came from a server-owned extraction ([#11661](https://github.com/diegosouzapw/OmniRoute/issues/11661)). ([#12001](https://github.com/diegosouzapw/OmniRoute/pull/12001)) +- **chore(providers):** retire the distributed Raycast Relay and Hailuo Web integrations after a provenance audit confirmed substantial derivation from GPL-3.0 sources; the official GitHub and MiniMax API providers remain available. Migration 163 disables legacy connections and invalidates their active leases while preserving connection IDs, audit/history rows, quota snapshots, usage records, and fail-closed API-key connection policies. ([#11691](https://github.com/diegosouzapw/OmniRoute/pull/11691)) +- **chore(providers):** retire the Felo Web provider while its GPL-derived provenance remains on licensing HOLD ([#11698](https://github.com/diegosouzapw/OmniRoute/pull/11698)) — stale runtime and database references now fail closed while existing connection, lease, and usage history remains preserved for auditability. +- **fix(sse):** retire the Gemini Web image-generation bridge while provenance and licensing remain on HOLD, preserving Gemini Web chat/auth/tools and legitimate Gemini/Nano Banana image providers ([#11708](https://github.com/diegosouzapw/OmniRoute/pull/11708)) +- **chore(audio):** retire the built-in EdgeTTS provider and its 11-voice catalog while provenance/licensing review remains on HOLD; the generic `/v1/audio/speech` endpoint and the other speech providers remain available ([#11711](https://github.com/diegosouzapw/OmniRoute/pull/11711)) +- **fix(providers):** retire the unofficial Qwen Web integration and its `qwen-web`/`qw` identities while third-party provenance remains on HOLD ([#11713](https://github.com/diegosouzapw/OmniRoute/pull/11713)) — direct requests now fail closed with a sanitized HTTP 410 and combos skip the retired target, while migration guards preserve connection IDs, allowlists, usage/audit history and invalidate active leases. Supported Qwen API, Qwen Code, cloud, token-plan, audio, local and model-name surfaces remain available; restoring Qwen Web requires a qualified provenance/license decision. +- **fix(providers):** retire `microsoft-designer-web` and its `msdesigner` alias from runtime and catalogs while preserving stored credentials, provider history and allowlists as inactive records — source dependency on gpt4free's [`MicrosoftDesigner.py`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/g4f/Provider/needs_auth/MicrosoftDesigner.py) is proven at snapshot `0943d16eef757a44deb60651c07bbe5fa958cafe`, whose [`LICENSE`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/LICENSE) is GPL-3.0; expressive adaptation is probable, which records provenance/licensing risk without asserting a legal conclusion ([#11720](https://github.com/diegosouzapw/OmniRoute/pull/11720)). +- **docs(legal):** centralize pinned MIT notices for the vendored GCF codec and local flag assets ([#11726](https://github.com/diegosouzapw/OmniRoute/pull/11726)) +- Split the 2,952-line `adobeFireflyClient.ts` into nine focused modules (catalog, credentials, payload, ARP session, upload, responses, IMS, account, poll) behind an unchanged barrel — no consumer edits and no public API change (#11728). — thanks @MumuTW +- **fix(dashboard):** replaced the Model Lockout card's unprovenanced notification MP3 with a short, locally synthesized Web Audio chime ([#11731](https://github.com/diegosouzapw/OmniRoute/pull/11731)) — both toggles retain optional audible feedback without shipping replacement media or third-party code, and unsupported, suspended, or rejected audio contexts fail open without blocking the setting change. +- **chore(deepseek-web):** remove the provenance-unresolved DeepSeek PoW WASM binary and its runtime loader/tracing while retaining the existing JavaScript solver (slower at high difficulty) ([#11732](https://github.com/diegosouzapw/OmniRoute/pull/11732)). +- **chore(assets):** remove four unused provider assets whose redistribution provenance could not be verified while retaining the active Freebuff SVG ([#11735](https://github.com/diegosouzapw/OmniRoute/pull/11735)) +- **docs(assets):** retired two raster images whose redistribution provenance could not be verified ([#11736](https://github.com/diegosouzapw/OmniRoute/pull/11736)) — PicoClaw documentation now reuses the existing SVG, while Zoo Code remains a text link without an image. +- **docs(legal):** add pinned notices for six LobeHub-derived provider SVGs and 65 byte-exact theSVG assets, retaining registry claims as unverified metadata with explicit trademark and HOLD boundaries ([#11737](https://github.com/diegosouzapw/OmniRoute/pull/11737)). +- **chore(quality):** added a versioned 225/225 provider-asset provenance manifest and blocking drift gate ([#11743](https://github.com/diegosouzapw/OmniRoute/pull/11743)) — the inventory pins magic MIME and SHA-256 for every physical file, records immutable source matches for 71 assets, keeps 69 probable and 85 unresolved entries explicit but non-blocking, and treats provenance as source evidence rather than copyright or trademark clearance. +- **refactor(mitm):** replace four previously derived Traffic Inspector utilities — conversation normalization, SSE merging, secret masking, and header sanitization — with independent clean-room implementations based on public provider schemas, WHATWG SSE, and RFC 6750, while keeping the two independently classified passthrough surfaces unchanged ([#11748](https://github.com/diegosouzapw/OmniRoute/pull/11748)). +- Added a unit test that fails seven days before any dated pack under `config/` (`validUntil` and sibling keys) lapses, naming the file and key. The Alibaba free-tier pack expired on 2026-08-27 and turned every PR red the next morning with no commit involved; renewal now happens on someone's terms, not the clock's. ([#11891](https://github.com/diegosouzapw/OmniRoute/pull/11891)) +- `check:workflows` now fails (under `--strict`/`--ratchet`) when any job routed to a self-hosted runner publishes with `--provenance` — npm rejects that with `422` at the registry, which in v3.8.50 only surfaced after the tag and Docker images were public. ([#11895](https://github.com/diegosouzapw/OmniRoute/pull/11895)) +- `scripts/ops/runner-janitor.sh` now proves a path is idle with one `lsof` snapshot and removes stale leftovers itself (tmpfs after 3 h — it is RAM — disk after 24 h), kills orphan `next-build` processes, prunes checkouts of stopped runners, and alerts on memory pressure; `--dry-run` shows exactly what it would do. `docs/ops/RUNNER_BOX.md` reconciled to the measured box (31 GB, 10 listeners). ([#11893](https://github.com/diegosouzapw/OmniRoute/pull/11893)) +- The `next-build` artefact (1.3 GB) is now written and read under `$RUNNER_TEMP` (per-runner, on disk) instead of `/tmp`, which on the self-hosted pool is a 12 GB tmpfs in RAM. Landing it there took 27–32 of the publish job's 76 minutes, and the fixed `/tmp/e2e-build.tar.gz` name let E2E jobs on different runners overwrite each other's download. ([#11896](https://github.com/diegosouzapw/OmniRoute/pull/11896)) +- The CI `build` job now runs in two concurrency lanes — `main` and pull requests — so a release build is never queued behind (or OOM-killed beside) PR builds on the self-hosted pool, which holds one `next-build` comfortably and two at the edge. ([#11901](https://github.com/diegosouzapw/OmniRoute/pull/11901)) +- Re-freeze the ESLint suppressions on `release/v3.8.51` from a clean-room measurement (2 stale file entries pruned, 55 pre-existing `no-explicit-any` in six new files frozen under #11924) and drop the dead `GPT_SIZE_MAP` constant orphaned by the Adobe Firefly client split, so `No new ESLint warnings` stops failing every PR with exit 2 (Refs #11924) ([#11955](https://github.com/diegosouzapw/OmniRoute/pull/11955)) +- Type the 55 `no-explicit-any` sites that had been frozen under #11924 — four redundant casts in `socksConnectorWithFamily.ts` (undici/socks types already accept them) and the mocks/fixtures of the socks-timeout and isFree suites — and drop their suppression entries; the ESLint ratchet shrinks from 5487 to 5432 (Closes #11924) ([#11975](https://github.com/diegosouzapw/OmniRoute/pull/11975)) +- Take the two hosted-runner builds off the PR rail: `Build App` (`build.yml`) is `workflow_dispatch`-only and `DAST smoke (PR)` runs only for PRs into `main` — the 7 GB hosted VM cannot build `release/v3.8.51` in any profile (19/30 red, VM shutdown ~8 min into `next build`) and both checks had turned into permanent noise on every release PR; the bundle stays validated by `ci.yml` on `main` and by `nightly-release-green` on `release/**` (Closes #11946) ([#11962](https://github.com/diegosouzapw/OmniRoute/pull/11962)) +- Move the four nightly jobs that build the backend (`nightly-schemathesis`, `nightly-llm-security` promptfoo + garak, `nightly-resilience` axe-a11y) off the hosted 7 GB runner — where they died on `release/v3.8.51` unseen — onto the box's new `omni-light` pool (two listeners), and document the reshaped fleet (4 active OmniRoute listeners: 2 `omni-build` + 2 `omni-light`, janitor ceiling 4) (Closes #11965) ([#11967](https://github.com/diegosouzapw/OmniRoute/pull/11967)) +- Stop painting every fork PR into `release/**` red: `quality.yml` `Build (advisory)` is skipped (GitHub still reports `continue-on-error` failures as check FAILURE). Hosted `ubuntu-latest` cannot finish `npm run build` on this tree — same class as #11962 taking `build.yml` off the PR rail. `docker-publish.yml` amd64 now runs on the `.113` `omni-build` pool (31 GB / 32 cores, two listeners) with Turbopack, shares the `heavy-build-main` lane with `ci.yml` `Build` so it queues instead of becoming a third heavy, and keeps per-ref concurrency (a merge storm was starting 8 concurrent OOM builds). arm64 stays on `ubuntu-24.04-arm` with webpack — there is no ARM box. Fallback when `USE_VPS_RUNNER` is off: hosted amd64 + webpack (#11976). ([#12021](https://github.com/diegosouzapw/OmniRoute/pull/12021)) +- `docker-publish.yml` builds with webpack (`OMNIROUTE_USE_TURBOPACK=0`) on **both** arches, including amd64 on the `.113` `omni-build` pool. Turbopack had the RAM (31 GB, `omniroute-113-6`) and still panicked (`TurbopackInternalError: there must be a path to a root`, run 33253576569, 26 min in). The same tree's arm64 webpack build on hosted `ubuntu-24.04-arm` succeeded (run 33264823398). Dockerfile already documents webpack as the Docker escape hatch (#11976). ([#12050](https://github.com/diegosouzapw/OmniRoute/pull/12050)) +- **docs(free-tier):** declare the counting vs deciding regimes for "is it free?" and guard the deciding path from DB-backed catalog resolution ([#12226](https://github.com/diegosouzapw/OmniRoute/pull/12226)) — thanks @maxmad64bis +- **docs(auto-combo):** The mode pack table in `docs/routing/AUTO-COMBO.md` now lists all six shipped packs with every weight each one sets, replacing a four-pack table whose numbers had also drifted from the source. It states plainly that no pack sets `quality`, so selecting any pack silences the observed-quality signal. Six more documents that quote the scoring factor count joined the `check:docs-counts` gate, which caught five stale claims — including one naming nine factors that do not exist — and two stale mode pack counts. The dashboard routing panel, which offered four of the six packs and labelled the default strategy "6-Factor Scoring", is now covered by a test; the two packs it was missing are `reliability-first` and `chaos-mode`, the latter labelled as the fault-injection profile it is rather than as one more routing preference ([#12316](https://github.com/diegosouzapw/OmniRoute/pull/12316)) — thanks @maxmad64bis +- **chore(quality):** rebaseline `src/lib/db/apiKeys.ts` for the ACL the key-creation path now preserves ([#12352](https://github.com/diegosouzapw/OmniRoute/pull/12352)) ([#12673](https://github.com/diegosouzapw/OmniRoute/pull/12673)) — thanks @KrzysiekSko +- **chore(providers):** bump the Claude Code wire identity and the Devin bridge image pin from `2.1.220` to `2.1.258` ([#12402](https://github.com/diegosouzapw/OmniRoute/pull/12402)) — thanks @ggiak ([#12604](https://github.com/diegosouzapw/OmniRoute/pull/12604)) +- **docs(env):** align `.env.example`, the README Bun section, and the troubleshooting guide with the code: `OMNIROUTE_USE_TURBOPACK` also governs `npm run build` (not dev-only), `bun run build` follows that flag instead of auto-selecting Webpack, `OMNIROUTE_CHAT_MAX_HEAVY_IN_FLIGHT` is unset by default (no request-count cap), and the structural `503 chat_admission_busy` message matches `chatAdmissionResponses.ts` (#12404 — thanks @pacocartones) +- **chore(electron):** upgrade the desktop app to Electron 44 (Chromium 152, Node 24.18.1) ([#12217](https://github.com/diegosouzapw/OmniRoute/pull/12217)). **Requires macOS 13 (Ventura) or later** — Chromium dropped macOS 12 (Monterey), so Monterey users must stay on an earlier OmniRoute desktop build. Windows and Linux are unaffected; the app already shipped only x64/arm64, so Electron 44 dropping 32-bit builds changes nothing. Removes the `openAsHidden`/`wasOpenedAsHidden` login-item fields deleted in Electron 44 — hidden autostart continues to work through the `--hidden` argument registered with the login item ([#12554](https://github.com/diegosouzapw/OmniRoute/pull/12554)) — thanks @app/dependabot +- **chore(quality):** rebaseline `src/sse/handlers/chat.ts` for the effective-input persistence the continuation fix needs ([#12641](https://github.com/diegosouzapw/OmniRoute/pull/12641)) ([#12680](https://github.com/diegosouzapw/OmniRoute/pull/12680)) — thanks @hartmark +- `Coverage` job on `ci.yml`: the informational Codecov upload gets its own 5-minute ceiling and `continue-on-error`, and the job budget grows from 20 to 30 minutes (the 8-shard c8 merge alone takes ~10) — a stalled upload no longer ends the job `cancelled` and drags a fully green `main` run's conclusion down with it ([#11972](https://github.com/diegosouzapw/OmniRoute/pull/11972)) +- CI hardening on the PR rail: the four `Unit Tests fast-path` shards get `timeout-minutes: 30` (a hung shard held a PR for 64 min instead of GitHub's 6 h default) and both ESLint file caches lose their `restore-keys` fallback, so a cache built under another suppressions file or lint config can no longer report stale verdicts (Refs #11600, #11924) ([#11963](https://github.com/diegosouzapw/OmniRoute/pull/11963)) +- Every CI job that runs a `next build` (`build`, the npm `publish`, both release-green validations) now targets the `omni-build` runner label, which only two of the eight self-hosted runners carry. The box holds one build comfortably and two at the edge; a third now queues on GitHub instead of being OOM-killed by the kernel. ([#11932](https://github.com/diegosouzapw/OmniRoute/pull/11932)) +- **refactor(combo):** move `handleRoundRobinCombo` (and `resolveTargetTokenLimit`) into `open-sse/services/combo/roundRobinCombo.ts`. `combo.ts` drops from 2164 to 1014 lines (`split("\n").length`); the leaf is 1199 (under the 1200 new-file cap). The round-robin call site uses a dynamic `import()` so `releaseStickyPinOnFailure` / `clearStaleLKGP` can stay exported from `combo.ts` without a static cycle. Skip / sticky / semaphore / safety-timer behavior is unchanged. ([#12811](https://github.com/diegosouzapw/OmniRoute/pull/12811)) — thanks @HouMinXi +- **chore(quality):** rebaseline the file-size caps the error-boundary campaign grew past (`open-sse/executors/codex.ts`, `open-sse/vendor/codex-chatgpt-web/bridge.ts`, both via [#12444](https://github.com/diegosouzapw/OmniRoute/pull/12444)) ([#12654](https://github.com/diegosouzapw/OmniRoute/pull/12654)) +- **chore(quality):** rebaseline the file-size caps the hartmark batch grew past (`combos/page.tsx` via [#12355](https://github.com/diegosouzapw/OmniRoute/pull/12355), `open-sse/services/combo.ts` via [#12338](https://github.com/diegosouzapw/OmniRoute/pull/12338)) ([#12623](https://github.com/diegosouzapw/OmniRoute/pull/12623)) — thanks @hartmark +- **chore(quality):** rebaseline the file-size caps the HouMinXi batch grew past when its PRs stacked (`providers/page.tsx`, `chatCore.ts`, `accountFallback.ts`) — each PR measured correctly in isolation, none saw the stacking ([#12619](https://github.com/diegosouzapw/OmniRoute/pull/12619)) +- **chore(quality):** rebaseline `open-sse/services/combo.ts` for the reset-aware scoring the HouMinXi batch stacked ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) ([#12678](https://github.com/diegosouzapw/OmniRoute/pull/12678)) — thanks @HouMinXi +- **docs:** sync provider catalog count (direct commit `4f11b2ae3d`) +- **docs(skills):** sync omni-inference search fallback (direct commit `b4e76a7ed9`) +- **chore(lint):** prune two stale base suppressions (direct commit `1325047d56`) +- **test(auto-combo):** tolerate full-suite rotation load (direct commit `ad3e293f9f`) +- **test:** drain inherited quality snapshot reds (direct commit `21ed68d8ac`) +- **chore(quality):** rebaseline measured CLI bundle growth (direct commit `d35b3f9779`) +- **test(ui):** reproduce invalid AnySearch icon alias (direct commit `12cc6ca834`) +- **test(translator):** fix the relative imports of the relocated deferred-finish test (direct commit `777d9d1629`) +- **docs(counts):** bring the documented provider and migration counts to the measured values (direct commit `e70bea30e9`) +- **test(security):** drop the guard-target set left unused by the sync (direct commit `a32ba0e362`) +- **chore(db):** renumber model_capabilities migration 163 → 169 (cross-branch collision with 163_radar_feed_cache_generated_at) (direct commit `70f5d4cbf9`) +- **docs(cli):** document CLI_PRIME_AGENT_BIN and correct the CLI Agents count (direct commit `9058e39b61`) +- **chore(deps):** refresh runtimes and adopt ESLint 10 ([#11259](https://github.com/diegosouzapw/OmniRoute/pull/11259)) — thanks @backryun +- **refactor(local-corpus):** implement dynamic root resolution and LRU cache ([#11491](https://github.com/diegosouzapw/OmniRoute/pull/11491)) — thanks @raheemuddin786 +- **chore(quality):** prune stale eslint-suppressions entries ([#11596](https://github.com/diegosouzapw/OmniRoute/pull/11596)) +- **chore(ci):** unblock quality gates - env-doc sync + stryker tap.testFiles registration (#9282) ([#11609](https://github.com/diegosouzapw/OmniRoute/pull/11609)) — thanks @Meet6338-X +- **chore(ci):** point the circular-deps gate at dpdm's real JS entrypoint ([#11615](https://github.com/diegosouzapw/OmniRoute/pull/11615)) — thanks @benzntech +- **chore(quality):** rebaseline file-size for #11448/#11495/#11561 growth ([#11630](https://github.com/diegosouzapw/OmniRoute/pull/11630)) +- **deps:** realign onnxruntime-node pin to @huggingface/transformers' 1.24.3 lockstep ([#11633](https://github.com/diegosouzapw/OmniRoute/pull/11633)) — thanks @jonlwheat2-gif +- **test(autoCombo):** port models_dev_tier #11508 guard from node:test to vitest ([#11635](https://github.com/diegosouzapw/OmniRoute/pull/11635)) — thanks @jonlwheat2-gif +- **chore(quality):** rebaseline file-size for #11083/#11631/#11628 growth ([#11636](https://github.com/diegosouzapw/OmniRoute/pull/11636)) +- **chore(lint):** prune stale ESLint suppressions (base-red #11449) ([#11644](https://github.com/diegosouzapw/OmniRoute/pull/11644)) — thanks @jonlwheat2-gif +- **docs(changelog):** aggregate the ten v3.8.50 fragments into CHANGELOG.md ([#11683](https://github.com/diegosouzapw/OmniRoute/pull/11683)) +- **chore(search):** drop unreachable credential sweep after #11642 ([#11687](https://github.com/diegosouzapw/OmniRoute/pull/11687)) — thanks @HouMinXi +- **ci:** add API route TypeScript regression gate ([#11705](https://github.com/diegosouzapw/OmniRoute/pull/11705)) — thanks @Sabeekhann +- **docs(dedup):** correct the CodeQL claim on the tenant-namespace comment ([#11712](https://github.com/diegosouzapw/OmniRoute/pull/11712)) +- **docs(changelog):** add consolidated v3.8.50 stats, top-25 ranking and a .mailmap ([#11715](https://github.com/diegosouzapw/OmniRoute/pull/11715)) +- **docs:** recommend gstack for AI-assisted workflows ([#11770](https://github.com/diegosouzapw/OmniRoute/pull/11770)) — thanks @santosraju99-hub +- **chore(quality):** tighten complexity/cognitive-complexity ratchets to the current tip; land the missed gateways.ts rebaseline ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **docs(i18n):** finish Freepik → Magnific rebrand in locale strings and README ([#11772](https://github.com/diegosouzapw/OmniRoute/pull/11772)) — thanks @turbolego +- **refactor(auto-combo):** fix divergent scoring in combo health reporting ([#11854](https://github.com/diegosouzapw/OmniRoute/pull/11854)) — thanks @jacobsparts +- **chore(changelog):** add missing fragments for #11919/#11918/#11916 ([#11938](https://github.com/diegosouzapw/OmniRoute/pull/11938)) +- **test(check):** escape the runs-on fixture with JSON.stringify, not a quote-only replace ([#11942](https://github.com/diegosouzapw/OmniRoute/pull/11942)) (direct commit `fb7445eaa3`) +- **test(release):** align five suites with the contracts #11933, #11919 and #11876 shipped on release/v3.8.51 ([#11944](https://github.com/diegosouzapw/OmniRoute/pull/11944)) +- **chore(quality):** remove unreachable code and restore test discovery ([#11950](https://github.com/diegosouzapw/OmniRoute/pull/11950)) — thanks @backryun +- **docs(agents):** sync-back landings are fast-forward, never squash ([#11964](https://github.com/diegosouzapw/OmniRoute/pull/11964)) +- **test(infra):** retry recursive temp-dir removal instead of failing a shard on ENOTEMPTY (#11966) ([#11968](https://github.com/diegosouzapw/OmniRoute/pull/11968)) +- **docs(ops):** the .113 heavy-build ceiling is one runner, not two ([#12048](https://github.com/diegosouzapw/OmniRoute/pull/12048)) +- **test(ui):** correct inactive auto-fetch expectation ([#12098](https://github.com/diegosouzapw/OmniRoute/pull/12098)) — thanks @RaviTharuma +- **docs:** document native dependency check escape hatch ([#12101](https://github.com/diegosouzapw/OmniRoute/pull/12101)) — thanks @RaviTharuma +- **test(providers):** regenerate the translate-path golden for OrcaRouter (#11923) ([#12118](https://github.com/diegosouzapw/OmniRoute/pull/12118)) +- **chore(quality):** velocity phase — loosen every numeric baseline by 20% until v4.0, monitor headroom nightly ([#12125](https://github.com/diegosouzapw/OmniRoute/pull/12125)) +- **chore(lint):** batch 0 of #12146 — type the call-log-cap sqlite rows instead of 45 as-any casts ([#12157](https://github.com/diegosouzapw/OmniRoute/pull/12157)) +- **chore(lint):** batch 4 of #12146 — shared/components react-hooks violations resolved ([#12159](https://github.com/diegosouzapw/OmniRoute/pull/12159)) +- **chore(lint):** batch 1 of #12146 — dashboard/cli-code react-hooks violations resolved ([#12160](https://github.com/diegosouzapw/OmniRoute/pull/12160)) +- **chore(lint):** batch 3 of #12146 — dashboard/settings react-hooks violations resolved ([#12162](https://github.com/diegosouzapw/OmniRoute/pull/12162)) +- **chore(lint):** batch 2 of #12146 — resolve the react-hooks compiler violations in dashboard/providers ([#12163](https://github.com/diegosouzapw/OmniRoute/pull/12163)) +- **chore(quality):** register search-432 cooldown test in stryker tap.testFiles ([#12170](https://github.com/diegosouzapw/OmniRoute/pull/12170)) +- **chore(lint):** batch 5 of #12146 — combos, endpoint, provider-stats, api-manager and costs react-hooks violations resolved ([#12174](https://github.com/diegosouzapw/OmniRoute/pull/12174)) +- **build:** add contributor fast profile ([#12192](https://github.com/diegosouzapw/OmniRoute/pull/12192)) — thanks @rafacpti23 +- **docs(readme):** README touch-ups ([#12193](https://github.com/diegosouzapw/OmniRoute/pull/12193)) ([#12194](https://github.com/diegosouzapw/OmniRoute/pull/12194)) ([#12202](https://github.com/diegosouzapw/OmniRoute/pull/12202)) — thanks @rafacpti23 +- **build:** force the webpack bundler in the contributor build profile ([#12197](https://github.com/diegosouzapw/OmniRoute/pull/12197)) — thanks @rafacpti23 +- **build:** skip standalone packaging in the contributor profile ([#12198](https://github.com/diegosouzapw/OmniRoute/pull/12198)) — thanks @rafacpti23 +- **docs(audit):** align every published number with the code and harden check:docs-counts ([#12200](https://github.com/diegosouzapw/OmniRoute/pull/12200)) +- **build:** stub the instrumentation entrypoints in the contributor profile ([#12203](https://github.com/diegosouzapw/OmniRoute/pull/12203)) — thanks @rafacpti23 +- **build:** omit the standalone output target for contributor builds ([#12204](https://github.com/diegosouzapw/OmniRoute/pull/12204)) — thanks @rafacpti23 +- **chore(lint):** batch 6 of #12146 — memory, radar, audit, analytics, cache, usage, activity, home and RequestLoggerV2 react-hooks violations resolved ([#12208](https://github.com/diegosouzapw/OmniRoute/pull/12208)) +- **docs(diagrams):** rename number-carrying diagram files to stable names ([#12210](https://github.com/diegosouzapw/OmniRoute/pull/12210)) +- **docs(dashboard):** redraw onboarding tier-flow SVGs for the real 4-tier model ([#12211](https://github.com/diegosouzapw/OmniRoute/pull/12211)) +- **docs(api):** document every implemented route in openapi.yaml (276 → 692 paths) ([#12212](https://github.com/diegosouzapw/OmniRoute/pull/12212)) +- **chore(lint):** batch 7 of #12146 — final src tail: 37 react-hooks violations across 33 files resolved ([#12244](https://github.com/diegosouzapw/OmniRoute/pull/12244)) +- Revert "docs: recommend gstack for AI-assisted workflows (#11770)" ([#12248](https://github.com/diegosouzapw/OmniRoute/pull/12248)) ([#12249](https://github.com/diegosouzapw/OmniRoute/pull/12249)) +- **docs:** Chaos Mode setup guide + weighted strategy semantics ([#12250](https://github.com/diegosouzapw/OmniRoute/pull/12250)) +- **docs(agents):** protected-surface merge rule — operator approval for agent-instruction files ([#12253](https://github.com/diegosouzapw/OmniRoute/pull/12253)) +- **chore(quality):** register native-codex turn-pin tests in stryker tap.testFiles ([#12263](https://github.com/diegosouzapw/OmniRoute/pull/12263)) +- **chore(quality):** dedupe tap.testFiles entries added by racing base-red fixes ([#12265](https://github.com/diegosouzapw/OmniRoute/pull/12265)) +- **docs(readme):** introduce OmniRouteTray — the macOS menu-bar companion ([#12276](https://github.com/diegosouzapw/OmniRoute/pull/12276)) — thanks @ggiak +- **chore(proxy):** purge the legacy 1proxy residue — sync/rotator modules, dead DB exports, dead settings tab and flag, docs (#12091) ([#12290](https://github.com/diegosouzapw/OmniRoute/pull/12290)) +- **chore(quality):** baseline-headroom skips generated and vendored files in the fileSize worst-file signal ([#12291](https://github.com/diegosouzapw/OmniRoute/pull/12291)) +- **refactor(video):** unify the JPEG frame data-URI contract ([#12322](https://github.com/diegosouzapw/OmniRoute/pull/12322)) +- **chore(deps):** freeze onnxruntime-node and eslint-plugin-react-hooks out of dependabot groups ([#12329](https://github.com/diegosouzapw/OmniRoute/pull/12329)) +- **test(executors):** restore the #10986 reasoning-only fallback guards ([#12364](https://github.com/diegosouzapw/OmniRoute/pull/12364)) — thanks @pacocartones +- **chore(quality):** re-tighten the file-size ratchet to the real LOC (plan 3.8.52 task 0) ([#12411](https://github.com/diegosouzapw/OmniRoute/pull/12411)) +- **test(grok):** format web executor suite ([#12412](https://github.com/diegosouzapw/OmniRoute/pull/12412)) +- **chore(quality):** ratchet open-sse typecheck baseline to zero ([#12418](https://github.com/diegosouzapw/OmniRoute/pull/12418)) +- **test(build):** guard the artifact path policy arrays against duplicates ([#12422](https://github.com/diegosouzapw/OmniRoute/pull/12422)) +- **chore(lint):** adopt eslint-plugin-react-hooks 7.1.1 ([#12428](https://github.com/diegosouzapw/OmniRoute/pull/12428)) +- **chore(providers):** remove a keyless provider integration at its operator's request ([#12440](https://github.com/diegosouzapw/OmniRoute/pull/12440)) +- **docs(providers):** register providers removed at their operator's request and guard against reintroduction ([#12478](https://github.com/diegosouzapw/OmniRoute/pull/12478)) +- **chore(quality):** base fixes — stryker tap.testFiles + node_modules cache key ([#12482](https://github.com/diegosouzapw/OmniRoute/pull/12482)) +- **docs(arch):** one-process recipe for tens of long /v1/responses ([#12493](https://github.com/diegosouzapw/OmniRoute/pull/12493)) — thanks @RaviTharuma +- **chore(quality):** tighten the CodeQL ratchet baseline from 11 to 6 ([#12530](https://github.com/diegosouzapw/OmniRoute/pull/12530)) +- **chore(deps):** pin browserslist override to ^4.28.8 ([#12592](https://github.com/diegosouzapw/OmniRoute/pull/12592)) — thanks @KooshaPari +- **chore(deps):** bump fast-uri to 3.1.7 in the electron lockfile ([#12601](https://github.com/diegosouzapw/OmniRoute/pull/12601)) +- **docs(quality):** document how the CodeQL ratchet refreshes and how to tighten it ([#12611](https://github.com/diegosouzapw/OmniRoute/pull/12611)) +- **test(ci):** pin the openapi-security-tiers two-arm contract with an executing gate test (#12581) ([#12652](https://github.com/diegosouzapw/OmniRoute/pull/12652)) +- **docs(gamification):** align XP Rewards table with code (#12501) ([#12667](https://github.com/diegosouzapw/OmniRoute/pull/12667)) — thanks @KooshaPari +- **refactor(combo):** split executeTarget into gates, attempt, and loop ([#12746](https://github.com/diegosouzapw/OmniRoute/pull/12746)) — thanks @HouMinXi +- **docs(security):** document socket.yml scanner config + CI workflow link (#12575) ([#12764](https://github.com/diegosouzapw/OmniRoute/pull/12764)) — thanks @KooshaPari +- **chore(ci):** guard commit identity in pre-commit to stop author misattribution ([#12772](https://github.com/diegosouzapw/OmniRoute/pull/12772)) +- **deps:** 17 Dependabot bumps — bump electron from 43.4.0 to 43.4.1 in /electron ([#11426](https://github.com/diegosouzapw/OmniRoute/pull/11426)); bump the development group across 1 directory with 8 updates ([#11428](https://github.com/diegosouzapw/OmniRoute/pull/11428)); bump the production group across 1 directory with 11 updates ([#11440](https://github.com/diegosouzapw/OmniRoute/pull/11440)); chore(deps): bump github/codeql-action/init from 4.37.7 to 4.37.8 ([#11925](https://github.com/diegosouzapw/OmniRoute/pull/11925)); chore(deps): bump github/codeql-action from 4.37.7 to 4.37.8 ([#11926](https://github.com/diegosouzapw/OmniRoute/pull/11926)); chore(deps): bump github/codeql-action/analyze from 4.37.7 to 4.37.8 ([#11927](https://github.com/diegosouzapw/OmniRoute/pull/11927)); build(deps): bump github/codeql-action/init from 4.37.8 to 4.37.9 ([#12345](https://github.com/diegosouzapw/OmniRoute/pull/12345)); build(deps): bump github/codeql-action/analyze from 4.37.8 to 4.37.9 ([#12346](https://github.com/diegosouzapw/OmniRoute/pull/12346)); bump the development group across 1 directory with 2 updates ([#12347](https://github.com/diegosouzapw/OmniRoute/pull/12347)); build(deps): bump github/codeql-action from 4.37.8 to 4.37.9 ([#12349](https://github.com/diegosouzapw/OmniRoute/pull/12349)); bump browserslist from 4.28.2 to 4.28.8 ([#12396](https://github.com/diegosouzapw/OmniRoute/pull/12396)); bump the production group across 1 directory with 4 updates ([#12399](https://github.com/diegosouzapw/OmniRoute/pull/12399)); bump @xmldom/xmldom ([#12500](https://github.com/diegosouzapw/OmniRoute/pull/12500)); bump qs from 6.15.2 to 6.16.0 ([#12512](https://github.com/diegosouzapw/OmniRoute/pull/12512)); bump @xmldom/xmldom from 0.9.10 to 0.9.12 ([#12513](https://github.com/diegosouzapw/OmniRoute/pull/12513)); bump fast-uri from 3.1.5 to 3.1.7 ([#12514](https://github.com/diegosouzapw/OmniRoute/pull/12514)); bump @humanfs/node from 0.16.7 to 0.16.8 ([#12515](https://github.com/diegosouzapw/OmniRoute/pull/12515)) +- Clear the `release/v3.8.51` `check:agent-skills-sync` base-red: regenerate `skills/cli-tunnel/SKILL.md` so the `tunnel create [type]` positional that #13009 taught the generator to read is reflected in the committed skill. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` typecheck base-red from #12731: the new `Mode pack ranking gates` candidates in `open-sse/services/autoCombo/__tests__/autoCombo.test.ts` omitted the required `provider`, `model` and `errorRate` fields of `ProviderCandidate`, failing `check:open-sse-typecheck` (and with it `Fast Quality Gates`) on every open PR. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` docs-sync base-red: EURouter (#13025) and GreenPT (#13024) took the live provider count to 358, leaving 7 STRICT drifts (`PROVIDER_REFERENCE.md`, 4 diagrams, 2 tier-flow images) plus the `AGENTS.md` / `llm.txt` / `package.json` count claims stale. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- **test(models):** Pin `CATALOG_BUILD_TIMEOUT_MS` in the #12058 canonical-catalog regression so a cold catalog build racing the 8s default bound no longer flakes the suite with a 500 ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Allow `@omniroute/opencode-plugin-v2/` in the npm pack policy — [#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870) shipped the v2 plugin beside its v1 sibling without widening the allowlist, so every packed file under it read as an unexpected artifact and failed `check:pack-policy` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Point the release-green validator's pack gate at the tree it is validating — the build-provenance guard defaulted to `origin/main`, which a release branch tip can never be an ancestor of mid-cycle, so `Package artifact` reported a false red on every run and the tarball boot-smoke cascaded off it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **docs(checks):** keep doc counts honest — headings, rankings, catalog, weights, quality gate and scoring diagram now covered ([#12507](https://github.com/diegosouzapw/OmniRoute/pull/12507)) — thanks @maxmad64bis +- **chore(lifecycle):** `check:model-lifecycle` now also diffs `DEFAULT_DEGRADATION_MAP` (the background-task redirect table) against the vendor lifecycle snapshot, refusing a retired id as source or target, with a table-driven unit test beside it. Three rows whose source the vendor had retired — `claude-sonnet-4-20250514`, `gemini-3-pro-preview` and `gpt-5.1-codex` (whose target `gpt-5.1-codex-mini` is retired too) — were dead code, since `checkLifecycle` answers 410 before the redirect runs; they are dropped (#12535 — thanks @pacocartones) +- **test(compression):** cover idle worker eviction at the resource level — the pool must call `terminate()` and must not retain the worker's `MessagePort`, complementing the `exit`-event assertion added with the fix ([#13371](https://github.com/diegosouzapw/OmniRoute/pull/13371)) +- **chore(quality):** raise the `imageGeneration.ts` file-size ceiling for the image-only-model guard that clears the #12945 base-red ([#13392](https://github.com/diegosouzapw/OmniRoute/pull/13392)) +- **chore(quality):** type the combo-test route's JSON response bodies instead of casting them to `any`, drop the now-empty suppression entry, and remove the `hasPerModelQuota` import `chatCore.ts` stopped using when the failure-classification helper was extracted ([#13379](https://github.com/diegosouzapw/OmniRoute/pull/13379)) +- **chore(electron):** the desktop bundle no longer ships the root-level files of every translated docs mirror (`README.md`, `llm.txt`, `CLAUDE.md`, `GEMINI.md`, `CONTRIBUTING.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`) — the packaged app only reads `docs/i18n//docs/**`, which stays. Saves ~11 MB on top of the translated CHANGELOGs already pruned. (#0000) ([#13235](https://github.com/diegosouzapw/OmniRoute/pull/13235)) +- **chore(quality):** retighten the `src/sse/handlers/chat.ts` unused-vars suppression count to the 8 violations that actually remain ([#13385](https://github.com/diegosouzapw/OmniRoute/pull/13385)) +- Bring the documented counts back in line with the code: 171 migrations (was 169) and 20 routing strategies (was 19), plus the regenerated `cli-tunnel` skill reference. ([#12970](https://github.com/diegosouzapw/OmniRoute/pull/12970)) +- Update the documented migration count to 172 after the call-logs provider-stats indexes landed. ([#13209](https://github.com/diegosouzapw/OmniRoute/pull/13209)) +- **chore(ci):** two gates that close the blind spots behind the exclusions above. `check:vitest-exclusions` requires every Vitest exclusion to name a tracking issue and to appear in `config/quality/vitest-exclusions.json` — the previous list grew to 62 files behind a comment pointing at an issue that had been closed for a month. `check-new-key-coverage` requires a key newly added to `en.json` to reach every locale; the existing coverage gate is a percentage floor per locale, so eleven absent keys out of ~13,000 left it at 99.9% while a whole feature shipped untranslated in nine languages. Both are diff-aware, so pre-existing debt stays frozen and neither needed a migration to turn on. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **build:** root layout no longer loads Inter from `next/font/google`, so a production image build does not need fonts.googleapis.com ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **docs(i18n):** bring the eight new `llm.txt` mirrors (km, kn, ml, my, ne, or, pa, si) up to the current provider and migration counts, so `check-docs-sync` stops rejecting every commit ([#13674](https://github.com/diegosouzapw/OmniRoute/pull/13674)) +- **refactor(ui):** flow surfaces (home topology, combo live studio, compression cockpit/waterfall/nodes, token-health badge) express state through the theme-aware `--orch-status-*` tokens instead of fixed dark-mode hex, so green/red/amber/grey stay legible in the light theme; dark mode is byte-identical. Categorical palettes (routing-strategy hues, compression-layer pills, provider brand colors) deliberately stay hex ([#12378](https://github.com/diegosouzapw/OmniRoute/issues/12378)) ([#13203](https://github.com/diegosouzapw/OmniRoute/pull/13203)) +- **chore(tests):** 51 test files that had been excluded from Vitest are running again, restoring roughly 350 assertions to the blocking suite. Each was measured individually first: of the 62 files parked behind the `// #8618 — pre-existing failure` comment, 51 pass against the current tree with no source change, so the exclusions had outlived the failures they were added for. The 11 that genuinely still fail stay excluded, but now point at a live tracker (#13204) rather than at #8618, which was closed in August while the list it tracked kept growing. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **chore(quality):** drop two ESLint suppression entries whose violations no longer exist, so `eslint --suppressions-location` stops rejecting every commit that touches the surrounding files ([#13384](https://github.com/diegosouzapw/OmniRoute/pull/13384)) +- **chore(release):** bring main's post-tag fixes into release/v3.8.51 — sync-back step 2/2 (direct commit `21c488f210`) +- **chore(deps):** patch toml and esbuild in the opencode plugin lockfiles ([#12965](https://github.com/diegosouzapw/OmniRoute/pull/12965)) +- **docs(changelog):** reconcile the v3.8.51 living section — fold 366 fragments, cover every cycle commit, credit every contributor ([#12971](https://github.com/diegosouzapw/OmniRoute/pull/12971)) +- **test(plugin):** wait for the v2 lazy-refresh tier instead of sleeping past it ([#12984](https://github.com/diegosouzapw/OmniRoute/pull/12984)) +- **chore(quality):** rebaseline chatCore.ts after the non-streaming regression fixes ([#13045](https://github.com/diegosouzapw/OmniRoute/pull/13045)) +- **docs(changelog):** add the missing bullet marker to the DeepSeek PoW fragment ([#13200](https://github.com/diegosouzapw/OmniRoute/pull/13200)) +- **chore(deps):** drain the Dependabot queue — 10 of 13 alerts ([#13213](https://github.com/diegosouzapw/OmniRoute/pull/13213)) +- **chore(ci):** make worktrees carry the hooks that enforce the identity gate ([#13236](https://github.com/diegosouzapw/OmniRoute/pull/13236)) +- **chore(deps):** pin csv-parse to ^7.0.2 via overrides (#13117) ([#13300](https://github.com/diegosouzapw/OmniRoute/pull/13300)) — thanks @KooshaPari +- **chore(deps):** bump hono from ^4.12.34 to ^4.13.7 (#13148) ([#13301](https://github.com/diegosouzapw/OmniRoute/pull/13301)) — thanks @KooshaPari +- **chore(deps):** pin joi to ^18.2.8 via overrides (#13085) ([#13302](https://github.com/diegosouzapw/OmniRoute/pull/13302)) — thanks @KooshaPari +- **docs(providers):** add Microsoft 365 Copilot (BizChat) provider guide (#12779) ([#13340](https://github.com/diegosouzapw/OmniRoute/pull/13340)) — thanks @KooshaPari +### 🙌 Contributors + +Thanks to everyone whose work landed in v3.8.51: + +| Contributor | PRs / Issues | +| --- | --- | +| [@5dive-bot](https://github.com/5dive-bot) | #11852 | +| [@abhisheksharma2411](https://github.com/abhisheksharma2411) | #11547, #11684, #11959, #12209 | +| [@adevwithpurpose](https://github.com/adevwithpurpose) | #11464 | +| [@adivekar-utexas](https://github.com/adivekar-utexas) | #12015, #12043, #12090, #12138 | +| [@AIB1TAL0S](https://github.com/AIB1TAL0S) | #12103 | +| [@alltomatos](https://github.com/alltomatos) | #11448 | +| [@alvinveroy](https://github.com/alvinveroy) | #11857, #12027, #12165 | +| [@amaleta](https://github.com/amaleta) | #11906, #11934 | +| [@amartinawi](https://github.com/amartinawi) | #11575 | +| [@AndrianBalanescu](https://github.com/AndrianBalanescu) | #11083, #11830, #11834, #11844 | +| [@anhtahaylove](https://github.com/anhtahaylove) | #13091, #13092, #13093, #13096, #13097, #13100, #13106, #13109, #13114, #13155, #13166, #13171, #13175, #13187, #13196 | +| [@aniruddhaadak80](https://github.com/aniruddhaadak80) | #11760 | +| [@app](https://github.com/app) | #12554 | +| [@arminanton](https://github.com/arminanton) | #11461, #11513 | +| [@AStupidBear](https://github.com/AStupidBear) | #11584 | +| [@atescivitci-cmd](https://github.com/atescivitci-cmd) | #12105 | +| [@b3nw](https://github.com/b3nw) | #11970, #11971, #12147 | +| [@backryun](https://github.com/backryun) | #11259, #11950, #12075, #12076, #12078, #12079, #12081, #12082, #12181, #12228, #12239, #12255, #12258, #12277, #12367, #12423, #12524, #12538 | +| [@benjaminkitt](https://github.com/benjaminkitt) | #11747 | +| [@benzntech](https://github.com/benzntech) | #11614, #11615 | +| [@Bl0ck154](https://github.com/Bl0ck154) | #11948, #11951, #11952, #11953, #11954 | +| [@brick30llc-ctrl](https://github.com/brick30llc-ctrl) | #11752, #12242, #12262 | +| [@Chewji9875](https://github.com/Chewji9875) | #11455, #11875, #12028 | +| [@cheynetom](https://github.com/cheynetom) | #12058 | +| [@davidebaraldo](https://github.com/davidebaraldo) | #12222, #12304 | +| [@davidlinfr](https://github.com/davidlinfr) | #6390 | +| [@ddarkr](https://github.com/ddarkr) | #12124 | +| [@Deftera186](https://github.com/Deftera186) | #11809 | +| [@dpozimski](https://github.com/dpozimski) | #11945 | +| [@DW-MediaLab](https://github.com/DW-MediaLab) | #12229 | +| [@echel0nn](https://github.com/echel0nn) | #11923 | +| [@f9td56dbgh-hub](https://github.com/f9td56dbgh-hub) | #11660 | +| [@fabioluissilva](https://github.com/fabioluissilva) | #11991 | +| [@foreveryh](https://github.com/foreveryh) | #12177 | +| [@geek007git](https://github.com/geek007git) | #12115, #12116, #12117, #12120, #12122 | +| [@geekyNads](https://github.com/geekyNads) | #11784, #12266 | +| [@ggiak](https://github.com/ggiak) | #11561, #12276, #12350, #12402 | +| [@gonisulaimann](https://github.com/gonisulaimann) | #12368, #12369, #12371 | +| [@Gorillaz322](https://github.com/Gorillaz322) | #12207 | +| [@groovecityJO](https://github.com/groovecityJO) | #12682 | +| [@hartmark](https://github.com/hartmark) | #11434, #11452, #11473, #11499, #11703, #11983, #11984, #11985, #11986, #11988, #11989, #11990, #11994, #12221, #12293, #12445, #12446, #12447, #12448, #12460, #12461, #12623, #12646, #12650, #12680, #12717, #12718, #12727, #12741, #12854 | +| [@hizzt](https://github.com/hizzt) | #11894 | +| [@hongnoul](https://github.com/hongnoul) | #11484 | +| [@HouMinXi](https://github.com/HouMinXi) | #11411, #11414, #11512, #11518, #11520, #11641, #11642, #11643, #11687, #11779, #11849, #11850, #11851, #11915, #11916, #11918, #11919, #11920, #12013, #12017, #12033, #12042, #12106, #12139, #12166, #12169, #12171, #12205, #12213, #12312, #12325, #12487, #12488, #12495, #12504, #12557, #12566, #12590, #12591, #12624, #12626, #12632, #12637, #12678, #12696, #12697, #12711, #12733, #12746, #12767, #12770, #12789, #12803, #12805, #12811, #12866, #12868, #12899, #12926, #12934, #12950, #12951, #12974, #13001, #13006, #13011, #13017, #13026, #13027, #13034, #13035, #13038, #13042, #13050, #13060, #13061, #13069, #13107, #13120, #13136, #13195, #13197 | +| [@Hsia97](https://github.com/Hsia97) | #11624 | +| [@jacobsparts](https://github.com/jacobsparts) | #11854, #12155, #12167 | +| [@joglomedia](https://github.com/joglomedia) | #11980 | +| [@jonlwheat2-gif](https://github.com/jonlwheat2-gif) | #11443, #11450, #11549, #11567, #11608, #11633, #11635, #11640, #11644, #11671, #11672, #11673, #11674, #11675, #11676, #11677, #11706, #11783, #12051, #12052, #12053, #12055 | +| [@kanade-hoshino](https://github.com/kanade-hoshino) | #12180 | +| [@Karan825](https://github.com/Karan825) | #11861 | +| [@KaspaPulse](https://github.com/KaspaPulse) | #11389, #11469, #11628, #11666, #11910 | +| [@keeltrace](https://github.com/keeltrace) | #12080, #12223 | +| [@killer30001000](https://github.com/killer30001000) | #12178 | +| [@KooshaPari](https://github.com/KooshaPari) | #12470, #12592, #12667, #12699, #12703, #12706, #12764, #12769, #12771, #13300, #13301, #13302, #13340, #13401, #13403, #13404, #13405, #13406, #13407, #13409, #13410, #13411, #13412, #13413, #13414, #13418, #13424, #13427, #13433, #13523, #13524, #13525, #13528, #13531, #13532, #13534, #13539, #13543, #13545, #13546, #13547, #13550, #13553 | +| [@kriptoburak](https://github.com/kriptoburak) | #11370 | +| [@KrzysiekSko](https://github.com/KrzysiekSko) | #12425, #12432, #12673 | +| [@marcelokarval](https://github.com/marcelokarval) | direct commit / report | +| [@maxmad64bis](https://github.com/maxmad64bis) | #1622, #11435, #11437, #11441, #11537, #11550, #11553, #11555, #11557, #11812, #11842, #11843, #11903, #12151, #12214, #12215, #12218, #12226, #12314, #12316, #12317, #12318, #12319, #12320, #12321, #12507, #12715, #12731, #12744, #12786, #12787, #12788, #12790, #12792, #12794, #12795, #12828, #12832, #12853, #12857, #12870, #12937, #12941, #12975, #13141, #13142, #13146, #13147 | +| [@mdigitalbh81](https://github.com/mdigitalbh81) | #12240 | +| [@Meet6338-X](https://github.com/Meet6338-X) | #11598, #11609, #12110 | +| [@morpheus9393](https://github.com/morpheus9393) | #11943 | +| [@MumuTW](https://github.com/MumuTW) | #11492, #11502, #11506, #11507, #11626, #11675, #11685, #11728, #11746, #11888, #11889, #11890, #11892 | +| [@Neuron-Mr-White](https://github.com/Neuron-Mr-White) | #11622, #11801, #12256 | +| [@NightStalker-87](https://github.com/NightStalker-87) | #12183 | +| [@NoxzRCW](https://github.com/NoxzRCW) | #11879, #11880, #11881, #11882, #11883, #11935 | +| [@ntdat812](https://github.com/ntdat812) | #11585, #12095 | +| [@ntdatt812](https://github.com/ntdatt812) | #11368, #11573, #11574, #11576, #11577, #11580, #11582, #11583, #11588, #11589, #11590, #11591, #11592, #11593, #11672, #12177, #12180, #12873, #12918, #12920, #12921, #12925, #12930, #13007, #13009, #13024, #13025, #13083, #13087, #13101, #13104, #13110 | +| [@official-burak](https://github.com/official-burak) | #11542 | +| [@opensource-elearning](https://github.com/opensource-elearning) | #12179, #12189, #12278, #12286 | +| [@oyi77](https://github.com/oyi77) | #11408, #11409, #11421, #11505, #11677, #12036, #12110 | +| [@pacocartones](https://github.com/pacocartones) | #11521, #11522, #11527, #11528, #11529, #11530, #11531, #11532, #11533, #11534, #11595, #11599, #11603, #11604, #11605, #11607, #11610, #11676, #11714, #11716, #11718, #11767, #11838, #11860, #11862, #11869, #11871, #11872, #11873, #11903, #11906, #11921, #11934, #12356, #12358, #12359, #12360, #12361, #12362, #12364, #12365, #12373, #12374, #12375, #12376, #12377, #12379, #12380, #12381, #12386, #12387, #12389, #12390, #12394, #12395, #12397, #12401, #12403, #12404, #12522, #12523, #12535, #12536, #12540, #12541, #12543, #12545, #12548, #12549, #12550, #12551, #12552, #12647, #12651, #12653, #13055 | +| [@patrykkopycinski](https://github.com/patrykkopycinski) | #11936, #11937, #12224 | +| [@PauloFH](https://github.com/PauloFH) | #11509 | +| [@PauloHSOliveira](https://github.com/PauloHSOliveira) | #12241 | +| [@ponkcore](https://github.com/ponkcore) | #12054 | +| [@Prajeeth-12](https://github.com/Prajeeth-12) | #11634, #12046 | +| [@quiterunner-commits](https://github.com/quiterunner-commits) | #12131, #12143 | +| [@rafacpti23](https://github.com/rafacpti23) | #11554, #11558, #12192, #12197, #12198, #12202, #12203, #12204 | +| [@ragnar-claude](https://github.com/ragnar-claude) | #11564 | +| [@raheemuddin786](https://github.com/raheemuddin786) | #11491, #11839, #11840, #11841, #12003, #12230, #12231, #12232, #12233, #12234 | +| [@rahilmavani](https://github.com/rahilmavani) | #11761 | +| [@Rahulsharma0810](https://github.com/Rahulsharma0810) | #11771 | +| [@RaviTharuma](https://github.com/RaviTharuma) | #11710, #11727, #11797, #11798, #11802, #11805, #11806, #11811, #12098, #12099, #12101, #12449, #12452, #12472, #12473, #12493, #12533, #12607, #12628, #12631, #12636, #12875, #12876, #12880, #12882, #12884 | +| [@rezjalibd](https://github.com/rezjalibd) | #12186 | +| [@RhianB14](https://github.com/RhianB14) | #11692 | +| [@rifqiawl](https://github.com/rifqiawl) | #11517, #11519, #11969 | +| [@rqzbeh](https://github.com/rqzbeh) | #11390 | +| [@Sabeekhann](https://github.com/Sabeekhann) | #11699, #11705 | +| [@santosraju99-hub](https://github.com/santosraju99-hub) | #11755, #11770 | +| [@Seramicx](https://github.com/Seramicx) | #11568 | +| [@Siva010](https://github.com/Siva010) | #12191 | +| [@solstxce](https://github.com/solstxce) | #11597 | +| [@soroush5](https://github.com/soroush5) | #12691 | +| [@tenshiak](https://github.com/tenshiak) | #12279 | +| [@TheDemonTuan](https://github.com/TheDemonTuan) | #11468, #11470, #11471, #11482, #11548, #11758, #11775, #11814 | +| [@thomasmaerz](https://github.com/thomasmaerz) | #12834 | +| [@tuandinh0801](https://github.com/tuandinh0801) | #11454 | +| [@turbolego](https://github.com/turbolego) | #11621, #11762, #11772, #11774, #11781, #12216 | +| [@Tushar49](https://github.com/Tushar49) | #11392 | +| [@ujjawalkaushik1110](https://github.com/ujjawalkaushik1110) | #11863 | +| [@vermasomesh835](https://github.com/vermasomesh835) | #11794 | +| [@vsd2807](https://github.com/vsd2807) | #11565, #11619 | +| [@wahidsadik371-coder](https://github.com/wahidsadik371-coder) | #12126 | +| [@watchingdogs](https://github.com/watchingdogs) | #12031 | +| [@wildcard](https://github.com/wildcard) | #11620 | +| [@xiaoyaner0201](https://github.com/xiaoyaner0201) | #11460, #11662, #12083 | +| [@Xxx91n](https://github.com/Xxx91n) | #11690 | +| [@yourspraveen](https://github.com/yourspraveen) | #11146 | +| [@yxyxy](https://github.com/yxyxy) | #11671 | +| [@ZaimMarzuki](https://github.com/ZaimMarzuki) | #11960 | +| [@Zartharas](https://github.com/Zartharas) | #11340 | +| [@zero-executioner](https://github.com/zero-executioner) | #11631 | +| [@diegosouzapw](https://github.com/diegosouzapw) | maintainer | + +--- ## [3.8.50] — 2026-08-25 _Living section — regenerated 2026-08-12 from all cycle commits (cycle open `ed2db6cb19` → tip). Bullets carry the merged PR and its author; direct pushes listed separately._ diff --git a/docs/i18n/el/CHANGELOG.md b/docs/i18n/el/CHANGELOG.md index 3feee7f900..e2ff1df021 100644 --- a/docs/i18n/el/CHANGELOG.md +++ b/docs/i18n/el/CHANGELOG.md @@ -95,16 +95,1119 @@ ## [3.8.51] — TBD -_Living section — cycle opened at the v3.8.50 freeze (parallel-cycle model). Bullets are aggregated from `changelog.d/` fragments at each `/generate-release` phase._ +_Living section — reconciled 2026-09-15 from all cycle commits (`091589089c` → `c0f92ec98a`, 916 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 | **123** | +| 📝 Commits in the cycle | **916** | +| 🔀 Pull requests referenced | **892** | +| 📋 Changelog entries | **919** | +| 🙌 Contributors credited in entries | **121** | +| 🤖 Automated dependency commits | 18 | + +**Entries by type** + +| Type | Count | +| --- | ---: | +| 🐛 Fixes | 592 | +| ✨ Features | 131 | +| 🧹 Chore | 93 | +| 📚 Docs | 42 | +| 🧪 Tests | 32 | +| ♻️ Refactor | 8 | +| 🏗️ Build | 7 | +| ⚡ Performance | 7 | +| ⚙️ CI | 2 | +| ⏪ Reverts | 2 | +| 📦 Dependencies | 2 | +| 🔒 Security | 1 | + +### 🏆 Top 25 contributors this cycle + +_By commits in `091589089c..c0f92ec98a`, author identities consolidated via `.mailmap` and the merged PR's GitHub login. Bots excluded._ + +| # | Contributor | Commits | +| ---: | --- | ---: | +| 🥇 | diegosouzapw | 323 | +| 🥈 | Bob.Hou (@HouMinXi) | 79 | +| 🥉 | Paco Cartones (@pacocartones) | 77 | +| 4 | Dizzle (@maxmad64bis) | 49 | +| 5 | Koosha Paridehpour (@KooshaPari) | 43 | +| 6 | Nguyen Thanh Dat (@ntdatt812) | 31 | +| 7 | Ravi Tharuma (@RaviTharuma) | 26 | +| 8 | Markus Hartung (@hartmark) | 22 | +| 9 | Webman (@jonlwheat2-gif) | 22 | +| 10 | backryun | 17 | +| 11 | anhtahaylove | 15 | +| 12 | Rafa Martins (@rafacpti23) | 10 | +| 13 | Syed Raheemuddin (@raheemuddin786) | 10 | +| 14 | Nguyễn Viết Tuấn (@TheDemonTuan) | 8 | +| 15 | MumuTW | 7 | +| 16 | Paijo (@oyi77) | 7 | +| 17 | Tobias Andersen (@turbolego) | 6 | +| 18 | Bl0ck (@Bl0ck154) | 5 | +| 19 | SHANMUGAPRIYAN (@geek007git) | 5 | +| 20 | KaspaPulse | 5 | +| 21 | Abhishek Sharma (@abhisheksharma2411) | 4 | +| 22 | Abhishek Divekar (@adivekar-utexas) | 4 | +| 23 | Andrew B. (@AndrianBalanescu) | 4 | +| 24 | NoxzRCW | 4 | +| 25 | opensource-elearning | 4 | ### ✨ New Features +- **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). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- Added Google AI Studio Gemini batch text-to-speech support through `POST /v1/audio/speech`. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- 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. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(routing):** subscription-first auto groupings — `auto/subscription` routes only through plan-included connections with a documented hard-stop overage and fails closed on exhaustion, while `auto/thrifty` orders the pool `subscription → keyless → free → cheap → premium` and steps up one rung at a time as each is exhausted. Billing class comes from a curated per-connection catalog (uncurated is treated as metered, never plan-included), both reuse STRICT_ZERO_COST's per-connection verification, and a quota reading whose `resetAt` has passed is now refreshed regardless of TTL so routing returns to plan capacity as soon as it resets ([#11146](https://github.com/diegosouzapw/OmniRoute/pull/11146)) — thanks @yourspraveen +- **feat(providers):** publish a management-authenticated versioned web-session credential contract from OmniRoute's canonical browser credential metadata ([#11340](https://github.com/diegosouzapw/OmniRoute/pull/11340)) — thanks @Zartharas +- **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)) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add Xquik X search with typed results, credential validation, REST routing, and MCP selection ([#11370](https://github.com/diegosouzapw/OmniRoute/pull/11370)) — thanks @kriptoburak +- **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)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(dashboard):** surface durable exclusive managed leases in the existing Sessions view, keeping leased clients visible across idle gaps while marking connections with in-flight work as active ([#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389)) — thanks @KaspaPulse +- **build(bun):** allow Turbopack bundler flag on Bun 1.4+ with configurable Webpack fallback ([#11471](https://github.com/diegosouzapw/OmniRoute/pull/11471)) — thanks @TheDemonTuan +- feat(api): add an opt-in `modelVisibilityAllowlist`/`modelVisibilityDenylist` settings pair to curate exactly which models `/v1/models` advertises, mirrored into every `auto/*` combo candidate pool so a denied model cannot be routed to via combo selection either (#11481) ([#11997](https://github.com/diegosouzapw/OmniRoute/pull/11997)) +- **feat(rankings):** the Free Provider Rankings page shows what each provider actually served over the last 24 h. It ranked by ELO alone, which left a provider that answers every call with an error in first place; the usage data was already served by the API but never requested. A provider with too small a sample shows a dash, not a number ([#11546](https://github.com/diegosouzapw/OmniRoute/pull/11546)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **feat(guardrails):** enforce a bounded, deterministic contract for Video Bridge transcripts — 256 cues, 4096 input code units and 4 KiB UTF-8 per cue, 64 KiB total text, malformed-Unicode rejection, focus-window scoping, cross-source reconciliation with contributing-source metadata, and a structural provenance trust boundary so caller JSON can never self-assert `embedded`/`audio-bridge` provenance ([#11652](https://github.com/diegosouzapw/OmniRoute/issues/11652)) ([#12009](https://github.com/diegosouzapw/OmniRoute/pull/12009)) +- **feat(video):** orchestrate optional Video Bridge audio extraction and Audio Bridge STT behind a dual opt-in (operator setting AND per-request signal) — a new loopback-only broker `mode=audio` operation shares the frame path's exact process queue, deadline, AbortSignal, and byte budgets to extract a bounded mono 16 kHz PCM WAV from the same already-downloaded video, then reuses the existing Audio Bridge transcription boundary; provider segment timing is preserved when available and marked coarse otherwise, and every failure degrades to a visual-only-safe partial instead of throwing (#11654). ([#12012](https://github.com/diegosouzapw/OmniRoute/pull/12012)) +- Add a tenant-bound Video Bridge drill-down lifecycle on top of the existing secure cache substrate: opaque hashed handles (never raw session/video identifiers), preview/standard/detail multiresolution variants resampled on read, response pagination capped at 8 frames and 32 MiB, and a new authenticated `/api/v1/video-bridge/drilldown` consumer route that stays disabled for remote access by default and denies cross-key access with the same response as a nonexistent handle (no existence oracle). ([#12006](https://github.com/diegosouzapw/OmniRoute/pull/12006)) +- **test(video):** Add the Video Bridge FU-07/FU-09 promotion-evidence harness (#11656) — a frozen Zod manifest schema covering the 8 required scenario kinds (static scenes, rapid cuts, late facts, fades, blur, small text, close events, visual prompt injection) with a minimum of 3 repetitions per case, deterministic declarative fixture recipes (`videoBridgePromotionFixtures.ts`), a pure medians/p95 metrics aggregator, a pure FU-07/FU-09 promotion-verdict evaluator applying the ticket's exact thresholds (missing token usage always holds), a digest-only persistence layer that never retains raw media or raw model responses, and a versioned per-model promotion allowlist shipped empty with every model defaulting to `hold`. The FU-07/FU-09 promotion verdicts themselves remain HOLD — they require a real evidence run against real models on VPS 192.168.0.15. ([#12008](https://github.com/diegosouzapw/OmniRoute/pull/12008)) +- **feat(video bridge):** "embedded" transcript provenance can now be legitimately earned instead of merely asserted — a bounded, allowlisted (`mov_text`/`subrip`/`webvtt`) subtitle probe runs through the loopback-only Video Bridge broker (at most 2 streams, 10s subdeadline bounded by the request deadline, 256 KiB output, 4096-code-unit lines), normalized through a bounded, ReDoS-safe WebVTT parser and Zod-validated end to end. The adapter always resolves to an explicit `success`/`absent`/`transient_failure` outcome — a subtitle failure never breaks the visual description path, and only a fingerprint-verified broker response (never a caller-declared label) can produce embedded cues (#11659). ([#12011](https://github.com/diegosouzapw/OmniRoute/pull/12011)) +- Default new Antigravity-family connections (agy CLI imports and Antigravity OAuth connects) to model auto-sync, so live model discovery lands in the synced catalog and `/v1/models` picks up freshly released upstream models (e.g. Gemini 3.7 Flash tiers) without code changes. Existing connections keep their current setting; the per-connection dashboard toggle remains the opt-out. (#11685 — thanks @MumuTW) ([#11887](https://github.com/diegosouzapw/OmniRoute/pull/11887)) +- **feat(ui):** enable React Compiler (`reactCompiler: true` + `babel-plugin-react-compiler`) for automatic memoization at build time ([#11783](https://github.com/diegosouzapw/OmniRoute/pull/11783)) — thanks @jonlwheat2-gif +- **feat(providers):** add SeekAi (`seekai.cc`) as an OpenAI-compatible New-API gateway — catalog id `seekai` (alias `ska`), `https://seekai.cc/v1`, live `/v1/models` via `passthroughModels`, aggregator-list membership so New-API balance detection can opt in. No referral/aff codes. ([#11786](https://github.com/diegosouzapw/OmniRoute/issues/11786)) ([#12557](https://github.com/diegosouzapw/OmniRoute/pull/12557)) — thanks @HouMinXi +- **feat(zai):** add GLM-5.3-Flash Coding Plan support (1M context, 128K output, vision, `low|high|max` reasoning) and route `zai` GLM-5.3-family API-key traffic through the OpenAI-compatible Coding Plan endpoint with native thinking defaults ([#11801](https://github.com/diegosouzapw/OmniRoute/pull/11801)) — thanks @Neuron-Mr-White +- **feat(combo):** choose how combo models are ordered — manual, provider, score, or name — via a sort control in the dashboard builder, persisted in `config.modelSort` and re-applied on load and after add ([#11812](https://github.com/diegosouzapw/OmniRoute/pull/11812)) — thanks @maxmad64bis +- **feat(free):** custom models can be marked free-tier via `customModels[].isFree`; `isFreeModel()` is the first door and `hidePaidModels` respects it even for providers outside the free budget ([#11843](https://github.com/diegosouzapw/OmniRoute/pull/11843)) — thanks @maxmad64bis +- **feat(nodejs):** add `5dive` as a `configure` target — `omniroute configure 5dive` / `omniroute setup-5dive` write a 5dive auth profile that points an agent fleet's `claude` seats at OmniRoute, with the root-only write, the loopback-vs-`https` endpoint rule and the per-seat model pin handled explicitly ([#11852](https://github.com/diegosouzapw/OmniRoute/pull/11852)) — thanks @5dive-bot +- **feat(sse):** treat `max` as a first-class reasoning-effort tier and clamp per model family (GLM 5.1+/DeepSeek V4+/Kimi K3+ keep native `max`; o1/MiniMax/Grok/Muse Spark clamp to their upstream ceiling) ([#11875](https://github.com/diegosouzapw/OmniRoute/pull/11875)) — thanks @Chewji9875 +- **feat(providers):** the provider plugin manifest now advertises a `usage-fetch` capability for the 40 providers that have a wired usage/quota fetcher, so external dashboards can read it from `GET /api/v1/provider-plugin-manifest` instead of parsing `open-sse/services/usage.ts` after every release. Discovery only — no new fetcher, no quota change, and the Dashboard quota widget stays gated by `USAGE_SUPPORTED_PROVIDERS`. `USAGE_FETCHER_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/fetcherProviders.ts`) and is re-exported from `services/usage.ts`, keeping the manifest module a light leaf instead of pulling the ~490-module usage dispatcher into the manifest route. ([#11903](https://github.com/diegosouzapw/OmniRoute/pull/11903)) — thanks @maxmad64bis / @pacocartones +- **feat(plugins):** `OMNIROUTE_PLUGINS_DIR` sets the directory the runtime plugin scanner reads — and the root the plugin manager installs into — overriding the `HOME`-derived default, so a Docker/K8s deployment can point straight at its bind-mounted plugin tree instead of moving `HOME` just to relocate the scan path. An image that exports no home no longer scans `/tmp/.omniroute/plugins` in silence: the resolved directory is logged once at startup as `scanner.dir_resolved`, naming the input that won. Unset, behaviour is unchanged. Distinct from the CLI-only `OMNIROUTE_PLUGIN_PATH`, which finds `omniroute-cmd-*` command packages and never reached this scanner ([#11906](https://github.com/diegosouzapw/OmniRoute/pull/11906)) — thanks @amaleta / @pacocartones +- **feat(leases):** add an explicit owner-authenticated status action that returns only the active lease's privacy-safe configured connection and provider labels, with generation fencing and no credential or internal-id disclosure ([#11910](https://github.com/diegosouzapw/OmniRoute/pull/11910)) — thanks @KaspaPulse +- **feat(providers):** import-from-file modal shows per-row API errors and ships a downloadable CSV template ([#12071](https://github.com/diegosouzapw/OmniRoute/issues/12071)) ([#12504](https://github.com/diegosouzapw/OmniRoute/pull/12504)) — thanks @HouMinXi +- **feat(providers):** dashboard search matches connection name and `baseUrl` so imported OpenAI-compat nodes surface on the provider card ([#12108](https://github.com/diegosouzapw/OmniRoute/issues/12108)) ([#12495](https://github.com/diegosouzapw/OmniRoute/pull/12495)) — thanks @HouMinXi +- **feat(routing):** add a `score` Auto router strategy that selects the highest configured weighted score and reuses `explorationRate`. ([#12155](https://github.com/diegosouzapw/OmniRoute/pull/12155)) — thanks @jacobsparts +- Add a runtime feature flag to disable universal context handoffs globally without changing the default behavior. ([#12167](https://github.com/diegosouzapw/OmniRoute/pull/12167)) — thanks @jacobsparts +- **feat(providers):** the provider plugin manifest now also advertises a `usage-supported` capability for the 46 providers whose usage API is accepted by the server and Dashboard routes, so integrators can distinguish "the server will serve quota for this provider" from "a fetcher is wired" without reading TypeScript. Discovery only — no fetcher or quota change. `usage-fetch` resolves on id or alias (the usage dispatcher accepts both); `usage-supported` resolves on id alone, matching the runtime guard `USAGE_SUPPORTED_PROVIDERS.includes(providerId)`. `USAGE_SUPPORTED_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/supportedProviders.ts`) and is re-exported from `providers.ts`, mirroring the `fetcherProviders` leaf from #11903 and keeping the manifest a light module. ([#12214](https://github.com/diegosouzapw/OmniRoute/pull/12214)) — thanks @maxmad64bis +- **feat(rankings):** order Free Provider Rankings by what each provider actually served — `GET /api/free-provider-rankings?sortBy=reliability` and a "Most reliable first" toggle on the page. Providers with too few calls to state a success rate keep their score order below the measured ones; the default order is unchanged ([#12218](https://github.com/diegosouzapw/OmniRoute/pull/12218)). — thanks @maxmad64bis +- **perf(sse):** defer `cloneLogPayload()` in the structured SSE collector until after the `maxEvents`/`maxBytes` cap check, eliminating ~9,800 wasted `structuredClone` calls per streaming response (65–71% faster `push()`). Reducer snapshot isolation restored for OpenAI and Responses summaries ([#12241](https://github.com/diegosouzapw/OmniRoute/pull/12241)) — thanks @PauloHSOliveira ([#12243](https://github.com/diegosouzapw/OmniRoute/pull/12243)) +- **feat(usage):** Devin CLI agentic quota (Codeium seat-management GetUserStatus) and OpenRouter key limits plus account credits now surface in Provider Limits ([#12256](https://github.com/diegosouzapw/OmniRoute/pull/12256) — thanks @Neuron-Mr-White) +- **feat(settings):** persist `headroomUrl` through Settings so status/start use the operator URL instead of only `HEADROOM_URL` ([#12306](https://github.com/diegosouzapw/OmniRoute/issues/12306)) ([#12487](https://github.com/diegosouzapw/OmniRoute/pull/12487)) — thanks @HouMinXi +- **feat(auto-combo):** Auto-Combo scoring can now weigh how often a provider/model has actually succeeded. The engine already carried that number on every candidate — 24 hours of usage history behind a ten-sample floor, real-time metrics otherwise — and the scoring function never read it, while the weight table described `stability` as if it did. `reliability` (`1 - failureRate`, with the same field precedence and the same rate-bounding the speed ranking already uses, so a corrupt reading means "nothing observed" rather than "fails every call") is now a declared factor shipping at weight `0`, so routing is unchanged until an operator gives it one, and the `stability` description now matches what that factor computes ([#12317](https://github.com/diegosouzapw/OmniRoute/pull/12317)) — thanks @maxmad64bis +- **feat(routing):** With `freeAccessPolicy: "strict"`, the read-only candidate listing (`GET /v1/auto-combo/{channel}/candidates`) no longer hides the candidates the zero-cost guard excludes — the same read-only transparency the resilience filter already honours (#9133). Each candidate now carries `freeAccessExclusion` saying why it would be kept out, and it tells an exhausted allowance apart from a quota reading that never arrived or went stale, which used to look identical from the outside. Routing is unchanged: the listing reports, it never enforces. The separate `excludeTosAvoid` guard still drops its candidates without a reason; that gap is now documented rather than closed ([#12319](https://github.com/diegosouzapw/OmniRoute/pull/12319)) — thanks @maxmad64bis +- **feat(radar):** The Radar catalog table now shows two facts it was already receiving from the feed and dropping on the floor: the per-model rate limits (requests and tokens, per minute and per day) in a new column, and a badge when a provider's terms state it may train on the prompts you send. A limit of zero renders as zero rather than "rate-only" — for a ceiling those are opposite facts — and a model with no training statement gets no badge, because an absent statement is not a guarantee ([#12320](https://github.com/diegosouzapw/OmniRoute/pull/12320)) — thanks @maxmad64bis +- **feat(radar):** explain Community, single-use, contributor, supporter, recovery, abuse, offers, and privacy rules before either Radar activation action, and remove the superseded fixed-PR grant promise from every UI locale ([#12342](https://github.com/diegosouzapw/OmniRoute/pull/12342)) +- **feat(gamification):** the dashboard Profile page now shows the real daily streak — `/api/gamification/level` returns `streak: { current, longest }` (per key with `apiKeyId`, operator-wide maximum otherwise) and the streak card reads it instead of a hard-coded 0 (#2403) ([#12377](https://github.com/diegosouzapw/OmniRoute/pull/12377)) — thanks @pacocartones +- **feat(gamification):** the dashboard leaderboard now shows each API key's display name under the Name column instead of a truncated key id; `GET /api/gamification/leaderboard` attaches `name` per entry (name only — no key material), while the shared ranking helper and the federation leaderboard stay id-only — thanks @pacocartones ([#12385](https://github.com/diegosouzapw/OmniRoute/pull/12385)) +- **feat(gamification):** enforce the documented 1000 XP/min per-API-key anti-cheat rate limit on the XP award path; over-limit awards are logged and skipped instead of persisted, and the sliding window now matches the timestamp format stored in `xp_audit_log` ([#2403](https://github.com/diegosouzapw/OmniRoute/issues/2403)) ([#12390](https://github.com/diegosouzapw/OmniRoute/pull/12390)) — thanks @pacocartones +- **feat(admin):** localize the gamification anomalies page — the loading state, the Status column and the Suspicious badge now come from the `common` catalog (new `common.suspicious` key propagated to every locale) — add it to the Gamification sidebar group as `gamification-admin` (`/dashboard/gamification/admin`), and expose the loading and empty states as polite `role="status"` live regions (#12401 — thanks @pacocartones) +- **feat(providers):** skip GitHub combo members missing from the live synced catalog, and drop Copilot models that are policy-disabled or hidden from the model picker ([#12473](https://github.com/diegosouzapw/OmniRoute/pull/12473)) — thanks @RaviTharuma +- **feat(i18n):** locale-expansion tooling — `npm run i18n:add-locale` adds a language to config, dashboard, docs mirrors, CLI, README/indexes and site in one command; browser, CLI and cookie detection resolve `uk`, `fil`/`tl`, `zh-Hant` (and the retired legacy `in` → `id`) via config aliases; `config/i18n.json` now ships in the npm package so the published CLI can read it; new real-translation ratio gate (`npm run i18n:check-ratio`, advisory) with a per-locale ratchet baseline; `run-translation --adopt` restores `.i18n-state.json`; `sync-language-bars` generates 🌐 bars from config; `validate_translation.py` loads its allowlist again. Retires the duplicate `in` locale (Indonesian mislabelled as Hindi) — 42 honest locales; saved `NEXT_LOCALE=in` / `OMNIROUTE_LANG=in` keep working. (#12496) +- **feat(api):** Emit gateway-measured `tokens_per_second` (TTFT excluded) on streaming usage and `X-OmniRoute-Tokens-Per-Second` when first-token latency is known ([#12616](https://github.com/diegosouzapw/OmniRoute/issues/12616)) ([#12631](https://github.com/diegosouzapw/OmniRoute/pull/12631)) — thanks @RaviTharuma +- **feat(opencode-plugin): map gateway cost/usage/tok/s onto OpenCode inference payloads** — the official plugin copies `X-OmniRoute-Response-Cost`, token counts, `X-OmniRoute-Tokens-Per-Second` / `usage.tokens_per_second`, TTFT, and the winning `X-OmniRoute-Model` onto the JSON/SSE body OpenCode already consumes. Missing tok/s is left unset (never `tokens / latency`). (#12636) — thanks @RaviTharuma +- **feat(combo):** add combo strategy `quota-weighted`: skip exhausted accounts, then weighted-draw by leftover / in-flight load; existing conversations stay pinned until the account is empty ([#12789](https://github.com/diegosouzapw/OmniRoute/pull/12789)) — thanks @HouMinXi +- **feat(models):** Account live listings become the chat catalog source for Claude, Codex, Copilot, and AGY; public metadata only fills prices on IDs those accounts already list. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **fix(models):** Union `agy` and `antigravity` live catalogs so `agy/gemini-3.8-flash-high` is not rejected after the prefix folds to `antigravity`. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **feat(opencode):** Add `@omniroute/opencode-plugin-v2`, an opencode v2 plugin that gives a v2 install a route to an OmniRoute gateway: it publishes models, combos and auto-combos into the host catalog, refreshes them lazily behind a 300s TTL, and keeps serving the last known catalog from an on-disk snapshot when the gateway is unreachable. The v1 plugin (`@omniroute/opencode-plugin`) is untouched and keeps working as before. The plugin adapts to the host instead of assuming one shape: it reads the shape the host seeds into the catalog draft and publishes the `api` block, the pre-`api` top-level fields, or both. The gateway key comes from the host credential store when one is connected, so no secret needs to sit in a config file; a source the gateway refuses is named in a warning instead of silently degrading the catalog to raw model ids; and tool schemas bound for a Gemini model are stripped of the JSON-Schema keywords Gemini rejects, so tool calling survives that route ([#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870)) — thanks @maxmad64bis +- feat(services): show sanitized CLIProxyAPI account health from its authenticated management API without exposing credentials, file paths, or raw account metadata (#6342) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add AnySearch free web search + URL extract (webFetch) with typed results, credential validation, REST routing, and MCP selection - fallback-only ([#11690](https://github.com/diegosouzapw/OmniRoute/pull/11690)) — thanks @Xxx91n +- **feat(dashboard):** display clamped `[0, 100]%` cached input token ratio in request logs table ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) — thanks @b3nw +- **feat(catalog):** add `OMNIROUTE_DISABLE_THINKING_LEVEL_VARIANTS` feature flag to optionally filter out thinking level variants from model catalog ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) — thanks @b3nw +- **feat(dashboard):** continuously export call logs to external analytics stores. A pluggable destination registry ships the full Logs-tab record set on an hourly `JobRegistry` cron, with a persisted per-destination cursor, batched inserts, a config UI rendered from each destination's own field descriptors, and a REST layer (`/api/log-export/*`) for CRUD, a connection test, and an on-demand run. A destination can opt into `includeBodies` to also ship the request and response payloads shown in the Logs detail pane, including the client and provider views of each call; this is off by default, and payloads inherit the dashboard's PII sanitisation, secret redaction and `noLog` handling. Google BigQuery is the first destination, using a service-account key stored encrypted at rest and streaming inserts keyed by call-log id, into a table that is day-partitioned on `timestamp` and clustered on `api_key_name`, `provider`, `model` and `status`. ([#11945](https://github.com/diegosouzapw/OmniRoute/pull/11945)) — thanks @dpozimski +- **feat(providers):** add MaxAI as a signed, OpenAI-compatible provider serving its 13 paid chat models (GPT-5.6 / Luna / Thinking, Claude 5 Sonnet, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite, Grok 4.1-fast / 4.5, DeepSeek V3.2 / R1, Llama 3.3 70B) through OmniRoute's `/v1` endpoint, with per-request HMAC-SHA1→SM3→AES request signing, live model + context-window discovery from `/models/get_config`, and prompted tool-calling translated to OpenAI `tool_calls` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI vision input — image_url content parts are forwarded inline in `message_content` to the 6 vision-capable models (GPT-5.6 / Luna / Thinking, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite) ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI image generation — 6 image models (gpt-image-1, dall-e-3, flux-1-schnell/dev/pro, sd3-medium) exposed through `POST /v1/images/generations` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI document RAG — inline base64 file/document attachments are uploaded to MaxAI (content-addressed `doc_id`) and attached to the chat via `doc_list` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** browserless MaxAI onboarding — email device-pair login (`/api/providers/[id]/login`) and signed access-token refresh, so a connection can be created and kept fresh without a real browser or Google OAuth ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** per-provider TLS impersonation profile (MaxAI presents a Windows Firefox-150 client fingerprint) so its bot-sensitive endpoints accept OmniRoute traffic ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** add **Nimble** as a web-search and web-fetch provider (`nimble-search`) — `/v1/search` routes to Nimble's search API at `lite` depth (locale, freshness and include/exclude domain filters mapped onto the shared request contract), and `/v1/web/fetch` routes to Nimble Extract, which covers all four fetch formats (`markdown`, `html`, `links`, `screenshot`) from a single call. One API key serves both surfaces. ([#11620](https://github.com/diegosouzapw/OmniRoute/pull/11620)) — thanks @wildcard +- The npm publish is automatic again, through npm Trusted Publishing (OIDC): the hosted `stage-npm` job publishes with a short-lived credential minted from GitHub's id-token — no `NPM_TOKEN`, no 2FA prompt, provenance attached. `publish_mode=staged` (owner approves with 2FA) and `direct` (token) remain available on `workflow_dispatch`. ([#11931](https://github.com/diegosouzapw/OmniRoute/pull/11931)) +- **feat(providers):** Add **Opper** as an API-key gateway provider — EU-hosted AI gateway with 700+ models from 30+ providers behind one OpenAI-compatible API and one key (`OPPER_API_KEY`); model ids use `provider/model` format (e.g. `anthropic/claude-sonnet-4-6`, `openai/gpt-5`); live model catalog at `https://api.opper.ai/v3/compat/models`; entry mirrors `requesty` (same shape, `passthroughModels: true`, no static seed) ([#11629](https://github.com/diegosouzapw/OmniRoute/pull/11629)) +- **feat(dashboard):** the `/dashboard/orchestration` snapshot hook now subscribes to the `agents` WebSocket channel (`agent.task.updated`) instead of `requests` as its refetch trigger, and relaxes its background poll from 5s to 30s while that WS connection is up — falling back to the tighter 5s cadence, reprogrammed live on any connect/disconnect transition, whenever the socket is down. ([#12409](https://github.com/diegosouzapw/OmniRoute/pull/12409)) +- **feat(dashboard):** Orchestration canvas quick wins — search box plus state/source/provider filter chips with a one-click clear, and per-source collapse/expand, all reflected in the URL so a filtered/collapsed view is shareable and survives a refresh; the detail drawer gained a "copy trace JSON" action and hardened error/empty-state and accessibility handling; the Agents-tab edges now animate traveling particles along active (running) connections; and the canvas node/edge status colors moved off fixed hex values onto theme-aware `--orch-status-*` CSS custom properties, so they adapt correctly to light/dark mode. ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- **chore(dashboard):** Orchestration UI hardening pass and the missing component/model test coverage it called for — `OrchestratorNode`/`ActivityNode`/`OverflowNode` rendering, the `?node=`/overflow-click page routing, the Agents-tab orchestrator-click no-op and `showCompleted` toggle, and the overview kanban's done-column sort order (#12270, #12271). ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- New `/dashboard/orchestration` page: live unified view of everything running — Cloud Agent, A2A and Conductor as a real-time graph (Agents tab), the combo cascade (Routing tab, reusing the Combo Live Studio) and a state kanban (Overview tab), with a detail drawer (trace, cost, approve/cancel). Read-only over existing APIs — no new backend. Canvas concept credit: PR #11815 design ([#12261](https://github.com/diegosouzapw/OmniRoute/pull/12261)) +- **feat(dashboard):** new "History" tab on `/dashboard/orchestration` — an Airflow-style grid of finished runs over a 24h/7d/30d preset window, one row per (source, identity), clicking a cell opens the existing detail drawer. It is backed by real persistence: A2A task lifecycle transitions are now written to the `a2a_tasks` table (purged after 30 days, configurable via `OMNIROUTE_A2A_HISTORY_RETENTION_DAYS`) and served by the new `GET /api/a2a/tasks/history` listing endpoint, with the task-detail route falling back to persisted history once a run leaves the in-memory snapshot. Conductor runs stay remote and are not persisted locally — the tab says so instead of silently omitting them. ([#12479](https://github.com/diegosouzapw/OmniRoute/pull/12479)) +- **feat(dashboard):** the orchestration detail drawer gained a "Repeat" action for Cloud Agent, A2A and Conductor tasks — a two-click confirm (click once to arm, click again within the confirm window to fire) re-submits the original prompt/input as a new run. The button is disabled with an explanatory tooltip whenever the original input can't be recovered from the loaded task detail (e.g. it never carried a prompt, or the detail failed to load). Two limitations of the A2A variant, by design: it targets the `/a2a` JSON-RPC endpoint, which authenticates with an API key only (`REQUIRE_API_KEY=true` or a configured `OMNIROUTE_API_KEY` makes a dashboard-session repeat answer `HTTP 400` — surfaced verbatim in the drawer's error line, never as a success), and the `message/send` call is SYNCHRONOUS: the POST blocks for the whole skill run, so the success confirmation only appears once the repeated task finishes. A dashboard-authenticated A2A creation path is deliberately left to a follow-up — widening the endpoint's auth posture is an operator decision, not a side effect of this feature. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(a2a):** A2A task execution now records which memories were consulted for the task's last user message as `metadata.memoryHits` (id/key/type/content-snippet) plus a `memory_hits` history event, purely for observability — the retrieved memory is never injected into a skill's prompt or behavior. Gated by the `OMNIROUTE_A2A_MEMORY_HITS` kill-switch (default enabled; set to `0` to skip the recall lookup entirely). The drawer's new "Memory used" section lists these hits for a2a tasks and is omitted whenever there are none. Known limitation: recall only resolves under the keyless posture — a keyed caller's task owner is a SHA-256 prefix of the API key, while memory rows are keyed by the database api-key id, and no hash→id lookup exists today, so the hit list stays empty for keyed callers. The recorded hits are also kept out of the task's own `input` (and therefore out of the persisted input and of the "Repeat" request body), so repeating a task never re-sends the previous run's memory snippets. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(providers):** add a Perplexity Agent API provider (`perplexity-agent` / `pplx-agent`) for Perplexity `/v1/responses`, including the documented Anthropic, OpenAI, Google, xAI, DeepSeek, Z.AI, Moonshot/Kimi, NVIDIA, and Perplexity model IDs plus Anthropic-model `max_output_tokens` compatibility. ([#12103](https://github.com/diegosouzapw/OmniRoute/pull/12103)) — thanks @AIB1TAL0S +- **feat(providers):** add RPD (Requests Per Day) limit to provider rate limit overrides across UI, schemas, DB, and i18n ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) — thanks @b3nw +- **feat(providers): add UC Direct (uncensored.com Developer API), the metered OpenAI-compatible surface.** A standard OpenAI-compatible passthrough (default executor) for uncensored.com's official REST API at `https://api.uncensored.com/api/v1`: `X-api-key` auth (never-expiring `uai_sk_live_` key), `POST /chat/completions` with streaming SSE and native tool-calling, and the full live metered catalog (82 models across 15 providers, discovered from the public `GET /v1/models`). Registered as provider `uc-direct` (alias `ucd`). Complements the un-metered `uc` persona provider — same models, metered credits and a plain API key instead of a subscription session. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(providers): add UC (uncensored.com), the un-metered subscription "persona" chat as an OpenAI-compatible provider.** A WebSocket web-app port: a durable Clerk credential mints a short-lived session token per connect (browserless — no API key), driving UC's persona socket. Ships the browserless email-code login (request → verify → harvest), the 19 verified persona models (Claude Opus, Gemini, Grok, GLM, Kimi, DeepSeek, MiniMax, incl. the uncensored variants), prompted `` tool-calling with a per-model code-style dialect + auto-cure retry for guardrailed models, live ``/reasoning split, streaming + non-streaming OpenAI responses, and full quota/auth error surfacing (paywall / message-limit / rate-limit → 429, invalid session → 401 re-login). Full multimodal parity via the persona blob-upload layer: **vision** (image input, 15 vision-capable models), **document RAG** (PDF/doc upload, server-side extraction), **image generation** (22 models), **video generation** (14 models, async signed-url → poll), and **TTS** (streaming MP3). Registered as provider `uc` (alias `ucn`). The metered OpenAI-compatible Developer API is a separate `uc-direct` provider. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(logs):** add copy-all button to request detail modal ([#11083](https://github.com/diegosouzapw/OmniRoute/pull/11083)) — thanks @AndrianBalanescu +- **feat(check):** make serviceKinds required and add the reverse-walk provider consistency gate ([#11392](https://github.com/diegosouzapw/OmniRoute/pull/11392)) — thanks @Tushar49 +- **feat(providers):** only advertise verified-working connections by default ([#11448](https://github.com/diegosouzapw/OmniRoute/pull/11448)) — thanks @alltomatos +- **feat(resilience):** add hierarchical concurrency admission ([#11493](https://github.com/diegosouzapw/OmniRoute/pull/11493)) +- **feat(providers):** derive + surface expiry for JWT-bearing web cookies (#11497) ([#11505](https://github.com/diegosouzapw/OmniRoute/pull/11505)) — thanks @oyi77 +- **feat(tunnels):** support named cloudflared tunnels via CLOUDFLARED_CONFIG ([#11561](https://github.com/diegosouzapw/OmniRoute/pull/11561)) — thanks @ggiak +- **feat(compression):** make proactive context-compression threshold a live setting ([#11564](https://github.com/diegosouzapw/OmniRoute/pull/11564)) — thanks @ragnar-claude +- **feat(resilience):** add provider peak-hour protection ([#11622](https://github.com/diegosouzapw/OmniRoute/pull/11622)) — thanks @Neuron-Mr-White +- **feat(providers):** add 1min.ai provider ([#11631](https://github.com/diegosouzapw/OmniRoute/pull/11631)) — thanks @zero-executioner +- **feat(dashboard):** collapsible JSON tree viewer for request/response payloads ([#11703](https://github.com/diegosouzapw/OmniRoute/pull/11703)) — thanks @hartmark +- **feat(routing):** add LiquidAI LFM2.5-2.6B free tier via OpenRouter ([#11752](https://github.com/diegosouzapw/OmniRoute/pull/11752)) — thanks @brick30llc-ctrl +- **feat(pricing):** add GLM-5.3-Flash pricing, model specs, and catalog registration ([#11830](https://github.com/diegosouzapw/OmniRoute/pull/11830)) — thanks @AndrianBalanescu +- **feat(routing):** add relayMode for schema-locked context handoffs ([#11839](https://github.com/diegosouzapw/OmniRoute/pull/11839)) — thanks @raheemuddin786 +- **feat(config):** add support for runtime system prompt configuration and hot-reloading ([#11841](https://github.com/diegosouzapw/OmniRoute/pull/11841)) — thanks @raheemuddin786 +- **feat(routing):** expand connection-aware quota prefilter across combo strategies (#11682) ([#11850](https://github.com/diegosouzapw/OmniRoute/pull/11850)) — thanks @HouMinXi +- **feat(models):** reactive discovery sync after Antigravity model-not-found 404 ([#11888](https://github.com/diegosouzapw/OmniRoute/pull/11888)) — thanks @MumuTW +- **feat(models):** auto-aliases and token limits from synced Antigravity discovery ([#11889](https://github.com/diegosouzapw/OmniRoute/pull/11889)) — thanks @MumuTW +- **feat(settings):** raise sticky round-robin limit caps to 1000 ([#12015](https://github.com/diegosouzapw/OmniRoute/pull/12015)) — thanks @adivekar-utexas +- **feat(resilience):** operator-configurable global credential health check interval ([#12043](https://github.com/diegosouzapw/OmniRoute/pull/12043)) — thanks @adivekar-utexas +- **feat(quota):** use official OpenCode Go usage API ([#12124](https://github.com/diegosouzapw/OmniRoute/pull/12124)) — thanks @ddarkr +- **feat(resilience):** default the credential health check sweep to 60 minutes ([#12138](https://github.com/diegosouzapw/OmniRoute/pull/12138)) — thanks @adivekar-utexas +- **feat(quality):** new-code mode for the complexity and dead-code ratchets (Clean as You Code) ([#12142](https://github.com/diegosouzapw/OmniRoute/pull/12142)) +- **feat(dashboard):** orchestration canvas — unified model + snapshot hook (part 1/2) ([#12156](https://github.com/diegosouzapw/OmniRoute/pull/12156)) +- **feat(usage):** add Kilo Code balance and Kilo Pass quotas ([#12178](https://github.com/diegosouzapw/OmniRoute/pull/12178)) — thanks @killer30001000 +- **feat(providers):** refresh vendored ChatGPT Web connector to v4.0.7 ([#12181](https://github.com/diegosouzapw/OmniRoute/pull/12181)) — thanks @backryun +- **feat(providers):** manual "Clear cooldown" action in the cooling panel ([#12224](https://github.com/diegosouzapw/OmniRoute/pull/12224)) — thanks @patrykkopycinski +- **feat(providers):** restore ChatGPT Web via clean-room browser transport ([#12239](https://github.com/diegosouzapw/OmniRoute/pull/12239)) — thanks @backryun +- **feat(sse):** wire the PROVIDER_PROFILES window gate into the global provider cooldown ([#12247](https://github.com/diegosouzapw/OmniRoute/pull/12247)) +- **feat(providers):** modernize CLOVA Studio chat and embeddings ([#12277](https://github.com/diegosouzapw/OmniRoute/pull/12277)) — thanks @backryun +- **feat(browser):** adopt Obscura as primary headless browser engine with Chromium fallback ([#12286](https://github.com/diegosouzapw/OmniRoute/pull/12286)) — thanks @opensource-elearning +- **feat(sse):** full language parity for output styles (es/de/fr/it/ru/zh + autoDetect) ([#12289](https://github.com/diegosouzapw/OmniRoute/pull/12289)) +- **feat(quality):** complete test:scoped — --full map rebuild, stdin selection, CI loader parity (#8084 D1) ([#12353](https://github.com/diegosouzapw/OmniRoute/pull/12353)) +- **feat(providers):** refresh Fable, Cursor, and Devin catalogs ([#12367](https://github.com/diegosouzapw/OmniRoute/pull/12367)) — thanks @backryun +- **feat(video):** redact transcript text from logs and durable memory (#12150 P1) ([#12427](https://github.com/diegosouzapw/OmniRoute/pull/12427)) +- **feat(dashboard):** parent-link, genuine-continuation badge, and modal perf fixes ([#12448](https://github.com/diegosouzapw/OmniRoute/pull/12448)) — thanks @hartmark +- **feat(dashboard):** adaptive context-budget dial on compression panel ([#12488](https://github.com/diegosouzapw/OmniRoute/pull/12488)) — thanks @HouMinXi +- **feat(providers):** refresh Z.ai Web models and browser transport ([#12524](https://github.com/diegosouzapw/OmniRoute/pull/12524)) — thanks @backryun +- **feat(video):** redact raw client-snapshot transcript fields in the detailed log (#12150 P2) ([#12528](https://github.com/diegosouzapw/OmniRoute/pull/12528)) +- **feat(providers):** refresh NVIDIA hosted models ([#12538](https://github.com/diegosouzapw/OmniRoute/pull/12538)) — thanks @backryun +- **feat(quota):** Moonshot Open Platform balance and TPD lock for custom nodes ([#12590](https://github.com/diegosouzapw/OmniRoute/pull/12590)) — thanks @HouMinXi +- **feat(video):** redact transcript in the in-memory pending-request snapshot (#12430 item 6) ([#12596](https://github.com/diegosouzapw/OmniRoute/pull/12596)) +- **feat(dashboard):** link a log entry's Conversation Context to its owning conversation ([#12646](https://github.com/diegosouzapw/OmniRoute/pull/12646)) — thanks @hartmark +- **feat(catalog):** eligibility-gated free-tier bucket ([#12669](https://github.com/diegosouzapw/OmniRoute/pull/12669)) +- **feat(db):** fail-closed previous_response_id continuation for redacted video turns (#12150 P2b) ([#12707](https://github.com/diegosouzapw/OmniRoute/pull/12707)) +- **feat(gamification): pay the documented `streak_bonus` and `badge_unlock` XP rewards.** `XP_REWARDS` listed both rewards but the award pipeline never paid them: the private reward table in `events.ts` omitted them, `updateStreak()` did not report when a streak extended, and badge unlocks carried no XP. Every request that extends a daily streak now pays `streak_bonus × streak length` once per UTC day (guarded by a same-day `xp_audit_log` check), and every badge unlocked through the pipeline pays `badge_unlock` once per badge (guarded by the `user_badges` primary key; `unlockBadge()` now reports whether it inserted). Bonus XP flows through the same `addXp` + level sync + global/weekly/monthly leaderboard path as action XP, so level-ups and rankings include it. The Radar supporter recognition unlock stays XP-free. (#12522 — thanks @pacocartones) +- **feat(providers):** advertise a `free-tier` capability in the provider plugin manifest for every provider with documented free models, so sidecars and dashboards can filter free-capable providers without reading the quota catalog ([#12786](https://github.com/diegosouzapw/OmniRoute/pull/12786)) — thanks @maxmad64bis +- **feat(providers):** Added EURouter as an OpenAI-compatible API-key gateway (`https://api.eurouter.ai/v1`), with live model discovery via `passthroughModels`. Its copy states that models are served by third-party upstreams listed per model, so an EU-based router is not read as EU data residency for inference. ([#13025](https://github.com/diegosouzapw/OmniRoute/pull/13025)) — thanks @ntdatt812 +- **feat(providers):** Added GreenPT as an OpenAI-compatible API-key provider (`https://api.greenpt.ai/v1`), with live model discovery via `passthroughModels`. No free-inference badge: the published docs describe a free API subscription billed per token, not a free tier. ([#13024](https://github.com/diegosouzapw/OmniRoute/pull/13024)) — thanks @ntdatt812 +- **feat(i18n):** 9 new locales — Greek (`el`), Croatian (`hr`), Serbian (`sr`), Lithuanian (`lt`), Estonian (`et`), Latvian (`lv`), Slovenian (`sl`), Maltese (`mt`), Irish (`ga`) — across the dashboard, docs mirrors, CLI, README and the site; OmniRoute now ships every official EU language (51 locales). (#13044) +- **feat(i18n):** 8 new locales — Kannada (`kn`), Malayalam (`ml`), Odia (`or`), Punjabi (`pa`), Nepali (`ne`), Sinhala (`si`), Burmese (`my`), Khmer (`km`) — across the dashboard, docs mirrors, CLI, README and the site (59 locales). The translator now restores the ICU literal escape around angle placeholders and splits oversized docs sections before translating. (#0000) ([#13660](https://github.com/diegosouzapw/OmniRoute/pull/13660)) +- feat(providers): **list Agnes 3.0 Flash as the current free chat model, drop retired 1.5 Flash, add Image 2.0/2.5 Flash plus Video 2.5/2.5 Flash, and discover the live `/v1/models` catalog (including the CN host `api.agnes-ai.cn`).** `agnes-1.5-flash` now forwards to `agnes-3.0-flash`. Video 2.5 polls `GET /v1/videos/{id}` (not the V2.0 `/agnesapi` contract). Live `/v1/models` (2026-09-09) no longer serves 1.5; the wiki marks it deprecated. 3.0 Flash is 512K context / 65,536 max output, same window as 2.5. CN-region keys use the existing per-connection base-URL field, default stays `apihub.agnes-ai.com`. ([#13120](https://github.com/diegosouzapw/OmniRoute/pull/13120)) — thanks @HouMinXi +- **feat(sse):** Codex and OpenAI catalogs list GPT-6 Astra with effort aliases (`-low` through `-ultra`); Codex CLI identity pins `@openai/codex@0.153.4` in lockstep with the image ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **feat(dashboard):** the orchestration History tab gained a "Compare runs" mode — toggling it turns each grid cell into a 2-item selection queue (a 3rd click drops the oldest pick), and picking two cells opens a side-by-side comparison panel instead of the usual detail drawer. The panel fetches both runs' detail the same way the drawer does (falling back to persisted history once a run leaves the live TTL window) and shows, per side: identity/source/state, start time, a signed `right - left` delta for duration/cost/event count, the event timeline aligned by index, and any memory hits. One side's fetch failing never blocks the other, and a delta is only ever computed when both sides have a finite value — otherwise it renders "—", never `NaN`. Comparing two runs from different sources or skills still works; a banner marks the deltas as informational rather than hiding them, since the two runs aren't a strict apples-to-apples pair. ([#12677](https://github.com/diegosouzapw/OmniRoute/pull/12677)) +- **feat(dashboard):** badge a conversation that never reached a clean stop ([#12717](https://github.com/diegosouzapw/OmniRoute/pull/12717)) — thanks @hartmark +- **feat(release):** reconcile-changelog tool + version-anchored fragment aggregation ([#12987](https://github.com/diegosouzapw/OmniRoute/pull/12987)) +- **feat(mcp):** carry context_length through MCP list_models_catalog projection (#12776) ([#13406](https://github.com/diegosouzapw/OmniRoute/pull/13406)) — thanks @KooshaPari +- **feat(cli):** make startup readiness budget configurable (#13369) ([#13433](https://github.com/diegosouzapw/OmniRoute/pull/13433)) — thanks @KooshaPari + ### 🐛 Bug Fixes +- **security(streaming):** sanitize generic mid-stream error messages before emitting OpenAI, + Responses, or Claude SSE failure frames and before diagnostic logging, while preserving raw + failures for internal classification and keeping client disconnects out of provider failure state. ([#12457](https://github.com/diegosouzapw/OmniRoute/pull/12457)) +- **fix(claude):** `blockExtraUsage: false` no longer lets 5h quota preflight skip the account; extra usage is billed after the session bar is gone, so the request must reach Anthropic ([#12803](https://github.com/diegosouzapw/OmniRoute/pull/12803)) — thanks @HouMinXi +- Sanitize HuggingChat conversation-creation and message-send transport failures before they reach client error bodies or provider logs. ([#12467](https://github.com/diegosouzapw/OmniRoute/pull/12467)) +- **fix(dashboard):** Keep local and theme-aware provider SVG icons at a definite layout size so Chromium does not collapse them to 0×0 after the v3.8.50 image-rendering change ([#12054](https://github.com/diegosouzapw/OmniRoute/pull/12054)) — thanks @ponkcore +- **fix(security):** Sanitize provider and runtime failures before public API, SSE and MCP responses and before persistent request, proxy and usage logs, preventing credentials, stack traces and host filesystem paths from crossing those boundaries while preserving stable error codes and useful diagnostics. ([#12506](https://github.com/diegosouzapw/OmniRoute/pull/12506)) +- **fix(cli):** `omniroute tunnel create` no longer crashes with `Cannot read properties of undefined (reading optsWithGlobals)` — removed the duplicate positional argument that caused Commander.js to misalign the action callback parameters ([#12295](https://github.com/diegosouzapw/OmniRoute/issues/12295)) ([#12368](https://github.com/diegosouzapw/OmniRoute/pull/12368)) — thanks @gonisulaimann +- Stop advertising Gemini Live-only models as supported audio endpoints until OmniRoute proxies the bidirectional Live protocol. ([#11710](https://github.com/diegosouzapw/OmniRoute/pull/11710)) — thanks @RaviTharuma +- **fix(kie):** reroute `flux/kontext` off the KIE Market `createTask` flow — it is catalogued with `isMarket: true` but has no Market catalog page, so KIE rejected it with "model name not supported"; it now hits the dedicated `POST /api/v1/flux/kontext/generate` / `GET /api/v1/flux/kontext/record-info` endpoints instead (#11296). ([#11985](https://github.com/diegosouzapw/OmniRoute/pull/11985)) — thanks @hartmark +- **fix(kie):** correct 12 more KIE Market catalog ids that were sent to `createTask` unchanged but diverge from KIE's documented upstream `model` values — GPT Image 2 T2I/I2I (drops the `gpt/` prefix), GPT Image 1.5 T2I/I2I (`gpt-image/` namespace), Seedream 5.0 Lite T2I/I2I (drops the `.0`), all 4 Flux 2 variants (`flux-2/` namespace, generic variant renamed `flex`), and Wan 2.7 Image / Image Pro (dash instead of dot) — each verified individually against the literal example request published on docs.kie.ai. `#11326`'s "everything else already matches" claim was wrong a second time (#11296); `z-image/4.0-*`/`z-image/4.5-*` and `flux/kontext` remain open, documented as unresolved in `KIE_MARKET_UPSTREAM_MODEL_IDS`'s comment pending further verification. ([#11667](https://github.com/diegosouzapw/OmniRoute/pull/11667)) +- **fix(dashboard):** `useApiKeySave.handleSaveApiKey` no longer forces a full upstream `/models` catalog sync on every non-curated provider connection save — callers can now pass `skipModelSync: true` to opt out, so a workflow that only wants to add one manual model no longer floods the provider's available-models list with hundreds/thousands of synced entries. The flag is a client-side intent signal only and is stripped before the connection payload is POSTed to `/api/providers`; default behavior (full sync on save) is unchanged when the flag is omitted (#11324) ([#11664](https://github.com/diegosouzapw/OmniRoute/pull/11664)) +- **Provider connections:** keep `tokenExpiresAt` when a connection is created. The create-path allowlist omitted it, so every insert stored NULL and the dashboard token badge could read a fresh connection as expired until its first background refresh ([#11368](https://github.com/diegosouzapw/OmniRoute/pull/11368)). — thanks @ntdatt812 +- **fix(combos):** the combo builder's precision-select, global-model-search, and manual-entry flows now serialize a model step's `model` string using the provider's already-computed routing-alias prefix (e.g. `oc/`) instead of rebuilding it from the raw canonical `providerId`, fixing the no-auth "OpenCode Free" provider (`opencode`) being routed to the unrelated paid "OpenCode Zen" provider (`opencode-zen`) because `opencode` doubles as a manual routing-prefix override ([#11433](https://github.com/diegosouzapw/OmniRoute/issues/11433)). ([#11670](https://github.com/diegosouzapw/OmniRoute/pull/11670)) +- **fix(radar):** the catalog feed cache now keeps `generatedAt`, the date the feed's data was built, next to `fetchedAt`, the date this install downloaded it (#11435). The feed schema requires that date and the sync path validates it, but the cache dropped it — so a feed fetched minutes ago and one carrying weeks-old figures looked identical to everything downstream, including the dashboard's "Last fetched" line. `getRadarCatalog().meta` and `GET /api/radar/status` now report both dates, the latter as its own field rather than folded into `version` — and omitted entirely for the offers and intel caches, which keep no build date, where a `null` would read as "unknown" rather than "never stored". The dashboard still shows only the fetch time; surfacing the build date there needs a new translated label and is left to a follow-up. Rows cached before migration 163 read back as `null`: unknown stays unknown instead of borrowing the fetch time. The referrals cache has persisted the same date since migration 142. — thanks @maxmad64bis +- **fix(cli):** postinstall no longer fills `JWT_SECRET` and `API_KEY_SECRET` in the installed package's `.env` (#11436). `.env.example` ships both blank on purpose: the server restores them from its durable store, or generates and persists them there, in `ensureSecrets()`. Pre-filling them defeated that — the file lives inside the package directory, so `npm i -g` replaced it and postinstall wrote _different_ values, while `ensureSecrets()` (which only acts on an empty variable) never got to restore the real ones. Both secrets rotated silently on every update, invalidating dashboard sessions and API-key CRCs. `STORAGE_ENCRYPTION_KEY` left the same list for the same reason in #1622; its comment pointed at a function that no longer exists and now names the real provisioning path. — thanks @maxmad64bis +- **fix(cli):** the CLI now says when a loaded `.env` lives inside the installed package directory (#11437). It already announces every env file it reads, without distinguishing the ones that survive an update from the one that does not: `npm i -g` replaces the package directory wholesale, so values set there are gone at the next update, silently. The warning names the durable path to move them to, and fires only when that file actually supplied a value — a file entirely shadowed by a durable one supplied nothing. A development checkout stays silent: there the same path is stable and documented in `SETUP_GUIDE.md`. — thanks @maxmad64bis +- **fix(free-models):** the shared `isFreeModel()` predicate no longer reports catalog entries marked `freeType: "discontinued"` as free, so `hidePaidModels` can't route to Pollinations' seven premium models that now require a paid key ([#11441](https://github.com/diegosouzapw/OmniRoute/pull/11441)) — thanks @maxmad64bis +- fix(ui): let AnySearch use the normal provider-icon fallback when LobeHub has no matching icon (#11449) +- Fixed the v3.8.50 Costs and Analytics dashboards so flat-rate Claude Code usage can be shown as an explicitly requested token-price estimate without changing default billed-cost semantics. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed archived usage retention so each request is priced individually instead of pricing a day's summed tokens once, which understated archived cost whenever a day mixed cache-heavy and ordinary requests. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed the Costs dashboard so it discloses when displayed figures include flat-rate token-price estimates instead of labelling them as billed spend, using the flag the analytics API already returns; the month-end projection and the CSV/JSON exports carry the same marker, and billed-cost mode is unchanged. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- **fix(combo):** attach the same combo-diagnostics trace (`poolSize`/`attemptOrder`/`excluded`/`terminalReason`, plus `x-omniroute-combo-*` headers) to the round-robin strategy's and the nested pipeline/fusion runtime-unit loop's "Maximum combo retry limit reached" 503 that the priority-strategy path already attaches for the identical terminal condition — previously those two paths returned a bare, contextless 503 ([#11462](https://github.com/diegosouzapw/OmniRoute/issues/11462)). ([#11665](https://github.com/diegosouzapw/OmniRoute/pull/11665)) +- **fix(bun):** use native `bun:sqlite` in `bootstrap-env` and `sync-env` to avoid loading `better-sqlite3` N-API addon during Bun startup ([#11468](https://github.com/diegosouzapw/OmniRoute/pull/11468)) — thanks @TheDemonTuan +- **fix(docker-bun):** make `Dockerfile.bun` install reliable with pre-install script helpers and native `bun:sqlite` smoke check ([#11470](https://github.com/diegosouzapw/OmniRoute/pull/11470)) — thanks @TheDemonTuan +- **fix(docker-bun):** remove every vendored `better-sqlite3` native addon from the Bun runtime image so startup cannot abort before the `bun:sqlite` fallback ([#11482](https://github.com/diegosouzapw/OmniRoute/pull/11482)) — thanks @TheDemonTuan +- **fix(autoCombo):** effort/alias model variants (`gpt-5.6-sol-xhigh`, `gpt-5.6`, cursor's `claude-4.6-opus-high`) inherit their base model's task fitness instead of falling to the wildcard 0.5 ([#11492](https://github.com/diegosouzapw/OmniRoute/pull/11492)) — thanks @MumuTW +- fix(db): rate-limit repeated Arena ELO leaderboard fetch-failure warnings instead of logging one per sync attempt (#11500) ([#11989](https://github.com/diegosouzapw/OmniRoute/pull/11989)) — thanks @hartmark +- **fix(db):** dedupe the raw `[Encryption] Decryption failed...` log line emitted by the lazy-decrypt views (`createLazyRowProxy`/`createLazyConnectionView`), which power `getProviderConnections()` and were re-triggering that line on every CredentialHealth/model-sync cycle for the same corrupt or stale-key credential — a fresh Proxy over a fresh row on every cycle meant the per-proxy memoization never suppressed it, unlike the dedup `decryptConnectionFields()` already had since [#9927](https://github.com/diegosouzapw/OmniRoute/issues/9927). Now shares that dedupe tracking so the line logs at most once per credential ([#11500](https://github.com/diegosouzapw/OmniRoute/issues/11500)). ([#11678](https://github.com/diegosouzapw/OmniRoute/pull/11678)) +- **fix(intelligence):** Arena ELO sync stores a synthesized base-model row for every effort/harness variant (`claude-opus-5-max` → `claude-opus-5`, `gpt-5.6-sol-xhigh (codex-harness)` → `gpt-5.6-sol`), so bare model requests reach the synced intelligence layer instead of the static fallback table; the hand-maintained `MODEL_ALIAS_MAP` — which collapsed generations (`gpt-5.5` → `gpt-5`) — is gone ([#11506](https://github.com/diegosouzapw/OmniRoute/pull/11506)) — thanks @MumuTW +- **fix(autoCombo,sse):** catalog hygiene — the static task-fitness table no longer ranks retired models above live ones (dead rows dropped, patterns matched on segment boundaries), the 7 deprecation aliases that forwarded to retired or non-existent models now point at the vendors' published replacements, an alias is no longer rewritten when the serving provider offers the id as-is, and a new `check:model-lifecycle` gate diffs both tables against a vendor lifecycle snapshot ([#11507](https://github.com/diegosouzapw/OmniRoute/pull/11507)) — thanks @MumuTW +- **fix(sse):** stop dropping resolved `thoughtSignature` values on parallel (multi tool-call) turns sent to Gemini 3.x — the claude→gemini and openai→gemini translators previously kept the signature only on the *first* function call of a message, causing Gemini to reject subsequent calls in the same turn with HTTP 400 "Function call is missing a thought_signature"; each function call now keeps its own resolved signature ([#11510](https://github.com/diegosouzapw/OmniRoute/issues/11510)). ([#11669](https://github.com/diegosouzapw/OmniRoute/pull/11669)) +- Treat Docker publishing from a non-default release branch as an expected skip instead of a failed workflow, while preserving the guard that prevents it from overwriting the `next` channel. ([#11527](https://github.com/diegosouzapw/OmniRoute/pull/11527)) — thanks @pacocartones +- fix(search): prefer credentialed providers over duckduckgo-free fallback (#11524) — the fallback-only loop ran before the credentialed-providers loop in `executeWebSearch`, making configured providers unreachable when `duckduckgo-free` was available. ([#11565](https://github.com/diegosouzapw/OmniRoute/pull/11565)) — thanks @vsd2807 +- **fix(sse):** cap the upstream headers-wait phase for STREAMING requests to a client-realistic ceiling (110s, under Codex's own ~120s hard client-abort window) instead of the flat 10-minute `FETCH_TIMEOUT_MS` default — that default was 5x longer than the body-phase readiness watchdog's own adaptive bound, so a request whose upstream never returned any response at all (not even headers, e.g. a stalled NVIDIA target behind a tool-heavy Responses→Chat translation) kept the client connection alive on keepalives only, guaranteeing the client's own patience ran out first with an opaque 499 instead of OmniRoute detecting and failing the stall fast. Non-streaming requests are unaffected — they keep the existing flat default (`open-sse/utils/fetchStartTimeoutPolicy.ts`) (#11526) ([#11668](https://github.com/diegosouzapw/OmniRoute/pull/11668)) +- **fix(ci):** protocol-clients E2E harness boots the peer-stamped custom server (`run-next.mjs dev`) instead of the bare `next` CLI, so `LOCAL_ONLY` locality resolves from the real TCP peer stamp and `/api/mcp/audit` answers 200 (open bootstrap preserved via an `OMNIROUTE_E2E_BOOTSTRAP_MODE=open` credential clear in `run-next.mjs`, pinned to loopback `HOST`) instead of a deterministic 403 (#11535) ([#11549](https://github.com/diegosouzapw/OmniRoute/pull/11549)) — thanks @jonlwheat2-gif +- **fix(docs):** the Auto-Combo engine is described with the number of factors it actually declares. Four different counts were in circulation — 6, 9, 13 and 14 across `AGENTS.md`, four guides and six code comments — while `DEFAULT_WEIGHTS` declares 15. `check:docs-counts` now reads that number from the source, so adding a factor without updating the prose fails CI ([#11545](https://github.com/diegosouzapw/OmniRoute/pull/11545)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **fix(quota-share):** in-flight leases are per request, so an aborted request's slot ages out instead of being kept alive by later traffic on the same connection ([#11547](https://github.com/diegosouzapw/OmniRoute/pull/11547)) — thanks @abhisheksharma2411 +- The free-tier summary route now serves the refreshed Radar catalog when the feed is active — the same numbers the dashboard shows — and states which catalog answered plus its real build date, instead of always reporting release-frozen figures with a stale curation date. The supporter-key live feed stays reserved to authenticated callers of the instance. ([#11550](https://github.com/diegosouzapw/OmniRoute/pull/11550)) — thanks @maxmad64bis +- **sse:** fix LiveWS/embed-WS servers crashing at startup under the Node/tsx runtime — `liveServer.ts`, `embedWsProxy.ts` and `apiBridgeServer.ts` imported `@/shared/utils/httpClientAbortGuard` without the `.mjs` extension, so the client-abort crash guard added by [#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556) was unreachable and every dependent test failed with `ERR_MODULE_NOT_FOUND` ([#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556)). ([#11671](https://github.com/diegosouzapw/OmniRoute/pull/11671)) — thanks @jonlwheat2-gif / @yxyxy +- fix(chat): shed any of the matched provider-node's routing identifiers (public prefix or internal id) before model lookup, so composites like `//` no longer reach upstream verbatim (#11557) — thanks @maxmad64bis +- **fix(cache):** a semantic-cache entry now expires on its TTL instead of surviving until the next UTC midnight, and `dbEntries` stops counting expired rows ([#11573](https://github.com/diegosouzapw/OmniRoute/pull/11573)) — thanks @ntdatt812 +- **fix(api):** `/v1/models` schedules its stale-while-revalidate rebuild through Next's `after()` again, so a stale catalog reaches the client before the rebuild blocks the event loop ([#11574](https://github.com/diegosouzapw/OmniRoute/pull/11574)) — thanks @ntdatt812 +- **fix(check):** the `known-symbols` executor conformance gate awaits the now-async `getExecutor()`, so it stops reporting all 142 registered aliases as dead symbols and can detect a lazy import that fails ([#11582](https://github.com/diegosouzapw/OmniRoute/pull/11582)) — thanks @ntdatt812 +- **fix(pack):** register `bin/cli/utils/volatileEnvPath.mjs` in `PACK_ARTIFACT_REQUIRED_PATHS`, so the prune can no longer silently drop a module the published CLI imports on every boot ([#11588](https://github.com/diegosouzapw/OmniRoute/pull/11588)) — thanks @ntdatt812 +- **i18n(vi):** translate the five reliability-column strings on the free-provider rankings page, which rendered as `__MISSING__:` markers in the Vietnamese UI ([#11589](https://github.com/diegosouzapw/OmniRoute/pull/11589)) — thanks @ntdatt812 +- **resilience:** restore the expired-connection retry-budget probe in the token-health sweep — the `!isGitHubAccessTokenOnlyConnection` carve-out reintroduced by #11608 contradicted the boundary pinned by #11592, so a GitHub connection parked at `expired` with retry budget remaining was never probed and could never self-heal ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)). ([#11672](https://github.com/diegosouzapw/OmniRoute/pull/11672)) — thanks @jonlwheat2-gif / @ntdatt812 +- **fix(dashboard):** Provider Stats now displays combo success rates on their native 0–100 percentage scale instead of multiplying them twice ([#11595](https://github.com/diegosouzapw/OmniRoute/pull/11595)) — thanks @pacocartones +- **fix(providers):** route DuckDuckGo Web Chat through the verified `duck.ai` endpoint, refresh its free model catalog, and avoid forwarding unsolved anti-abuse challenges ([#11597](https://github.com/diegosouzapw/OmniRoute/pull/11597)) — thanks @solstxce +- **fix(ui):** Console log Refresh and Copy controls now expose localized accessible names, keep copy actions visible on keyboard focus, and announce copy completion safely ([#11599](https://github.com/diegosouzapw/OmniRoute/pull/11599)) — thanks @pacocartones +- Dropped the stale-`.eslintcache` `restore-keys` fallback from both "Restore ESLint file cache" steps in `ci.yml`, so the blocking `Lint` job can no longer be served per-file verdicts computed under a different lint config, suppressions file or lockfile. `quality.yml` had already dropped it in #11963; `ci.yml` — the workflow that actually gates PRs — had not (#11600). ([#11996](https://github.com/diegosouzapw/OmniRoute/pull/11996)) +- fix(ci): pass `--pass-on-unpruned-suppressions` in `run-eslint-json.mjs` so the CI Lint job no longer fails when a suppression is merely orphaned by a genuine fix, mirroring the identical fix already in `validate-release-green.mjs` (#11600) ([#11983](https://github.com/diegosouzapw/OmniRoute/pull/11983)) — thanks @hartmark +- fix(handoff): enforce provider allowlist for universal handoff (#11602) — universal handoff now skips summarization when the selected summary model's provider is not included in the configured provider allowlist. ([#11619](https://github.com/diegosouzapw/OmniRoute/pull/11619)) — thanks @vsd2807 +- **fix(dashboard):** Show a stable error state when Search Analytics returns an HTTP error or malformed data ([#11603](https://github.com/diegosouzapw/OmniRoute/pull/11603)) — thanks @pacocartones +- **test(gamification):** pin the aggregate profile level to the XP-derived semantics of #11604 — `getAggregateXp()` now derives `currentLevel` from the summed XP (`calculateLevel(sum)`), not `MAX(stored current_level)`, and the #3484 fixture levels are aligned with the XP curve ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)). ([#11676](https://github.com/diegosouzapw/OmniRoute/pull/11676)) — thanks @jonlwheat2-gif / @pacocartones +- **fix(dashboard):** Keep the Profile level and progress aligned with aggregate XP, including bounded handling for invalid totals ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)) — thanks @pacocartones +- **fix(dashboard):** Prevent locked hidden badges from revealing their icon or opening private badge details before they are earned ([#11605](https://github.com/diegosouzapw/OmniRoute/pull/11605)) — thanks @pacocartones +- **fix(dashboard):** Restore keyboard focus after shared modals close and cancel delayed autofocus during cleanup ([#11607](https://github.com/diegosouzapw/OmniRoute/pull/11607)) — thanks @pacocartones +- **fix(dashboard):** Enable Enter and Space activation for clickable data-table rows without hijacking nested controls ([#11610](https://github.com/diegosouzapw/OmniRoute/pull/11610)) — thanks @pacocartones +- **fix(autoCombo,sse):** vendor-retired catalog ids are dropped from the auto-combo candidate pool and no longer win on leftover `arena_elo` / `user_override` rows; `getModelLifecycleDecision` consults `model-lifecycle.json` (prefix-stripped) so aggregator traffic is not `untracked` for ids the snapshot already knows ([#11625](https://github.com/diegosouzapw/OmniRoute/issues/11625)) ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)) — thanks @MumuTW +- **test(chatcore):** move Codex/Claude combo fixtures off models the lifecycle guard now rejects — `gpt-5.1-codex`/`gpt-5-codex` are vendor-retired (snapshot, #11626) and `claude-3-5-sonnet-20241022` is shut down, so native-passthrough and combo-fallback tests switched to `gpt-5.6-sol` and `claude-sonnet-4.6` ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)). ([#11675](https://github.com/diegosouzapw/OmniRoute/pull/11675)) — thanks @jonlwheat2-gif / @MumuTW +- fix(opencode-plugin): stop a free-tier budget that rounds up across a magnitude from rendering as `1000K`/`1000M` in the model picker — `fmtTokens` chose its unit from the raw token count and then rounded with `toFixed(1)`, so 999,950–999,999 printed as `1000K` rather than `1M` and just under a billion printed as `1000M` rather than `1B` ([#11684](https://github.com/diegosouzapw/OmniRoute/pull/11684)) — thanks @abhisheksharma2411 +- Cap prose-derived Antigravity quota resets at the configured model cooldown maximum while preserving authoritative Retry-After headers and Google RetryInfo hints. (#11695) Thanks @pacocartones. ([#11823](https://github.com/diegosouzapw/OmniRoute/pull/11823)) +- Compare `_rebaseline_*` notes structurally in the ratchet-bank verifier so an object-valued note no longer reports itself as rewritten on an unmodified tree — the nightly `bank-ratchet-shrinks` lane had been aborting on every run since 2026-08-11, leaving 41,672 lines of earned file-size shrinks unbanked (#11702). ([#11892](https://github.com/diegosouzapw/OmniRoute/pull/11892)) — thanks @MumuTW +- **fix(build):** `prepublish.ts` bundles the ChatGPT Web (Codex) MCP bridge through `runBuildTool()` instead of spawning `npx.cmd` raw, fixing the build crash on Node ≥ 20/Windows where `.cmd` shims cannot be spawned without a shell (EINVAL) ([#11704](https://github.com/diegosouzapw/OmniRoute/issues/11704)) ([#11706](https://github.com/diegosouzapw/OmniRoute/pull/11706)) — thanks @jonlwheat2-gif +- fix(codex): keep `parallel_tool_calls:false` on the translated Codex Responses Lite path (#11707) ([#11984](https://github.com/diegosouzapw/OmniRoute/pull/11984)) — thanks @hartmark +- **fix(tests):** the new Gemini Web Images retirement test (#11708) called the now-async `getExecutor()` synchronously, throwing `TypeError: getExecutor(...).getProvider is not a function` instead of asserting Gemini Web chat stays available. Awaited to match the R0.3 executor-registry refactor (#11220). ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(search):** Allow REST and MCP web-fetch requests to select the advertised `anysearch-search` provider explicitly ([#11716](https://github.com/diegosouzapw/OmniRoute/pull/11716)) — thanks @pacocartones +- **fix(providers):** Keep overnight peak-hour protection active after midnight according to the window's configured start day ([#11718](https://github.com/diegosouzapw/OmniRoute/pull/11718)) — thanks @pacocartones +- **fix(providers):** correct all five g4f.space gateway descriptions to document proof-of-work credits or member API keys, variable limits, and the remote prompt boundary instead of promising free keyless access at five requests per minute ([#11738](https://github.com/diegosouzapw/OmniRoute/pull/11738)). +- **fix(db):** `model_capabilities` is created by a migration instead of lazily on the first models.dev sync, so a clean install and an upgraded install converge on the same schema regardless of which features have run ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(ci):** `check:install-upgrade` now fails on an `npm` install truncated by ENOSPC (npm reports it as a warning and still exits 0), authenticates its health probe so the version assertion works against the hardened health payload, frees the clean-install tree before the upgrade phase, and no longer reports a schema divergence computed from a boot that never served ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(routing):** harden session-affinity key derivation against oversized explicit IDs and empty structural-payload collisions, while recognizing Gemini and common text request shapes ([#11746](https://github.com/diegosouzapw/OmniRoute/pull/11746)) — thanks @MumuTW +- **fix(packages/browser-pool):** regenerate `package-lock.json` so the `packages/browser-pool` workspace's locked `playwright`/`@types/node` (and transitives) match its `package.json` specs, fixing cache-only/offline installs (`npm ci --offline`, Nix `buildNpmPackage`) that previously failed with `ENOTCACHED` ([#11747](https://github.com/diegosouzapw/OmniRoute/issues/11747)) — thanks @benjaminkitt ([#11784](https://github.com/diegosouzapw/OmniRoute/pull/11784)) — thanks @geekyNads +- **fix(assets):** Stop distributing 79 provider and CLI logos without verified source/license provenance, keep every provider/tool available through the internal generic icon, and prevent those IDs from falling through to an external icon CDN. ([#11750](https://github.com/diegosouzapw/OmniRoute/pull/11750)) +- **fix(providers):** retire the common `chatgpt-web` / `cgpt-web` provider because its pre-key and proof-of-work provenance could not be cleared ([#11754](https://github.com/diegosouzapw/OmniRoute/pull/11754)) — explicit chat and image requests, slash-prefixed models, persisted aliases, proxy overrides, and combo targets now fail closed with a sanitized HTTP `410` / `PROVIDER_RETIRED` before upstream dispatch. Migration 163 tombstones matching connections and invalidates their active leases without deleting history or widening API-key allowlists. The independent `chatgpt-web-codex` / `cgpt-codex` provider and its MIT-noticed vendored implementation remain available. +- **fix(usage):** quota and usage refresh no longer 409 when an exclusive lease reserves the connection ([#11758](https://github.com/diegosouzapw/OmniRoute/pull/11758)) — thanks @TheDemonTuan +- Keep the embedding registry's vector width and `embedding` type on models when a synced model exists for the same id, so `/v1/models` no longer reports registry-described embedding models widthless or untyped (#11761) — thanks @rahilmavani +- Correct `google/gemini-embedding-001` on the OpenRouter route to 3072 dimensions, the width it returns when `dimensions` is not sent (#11761) — thanks @rahilmavani +- **fix(vision-bridge):** Restrict automatic vision routing and fallbacks to models available in authoritative live provider catalogs, including aliases and effort variants ([#11767](https://github.com/diegosouzapw/OmniRoute/issues/11767)) — thanks @pacocartones ([#11813](https://github.com/diegosouzapw/OmniRoute/pull/11813)) +- **fix(providers):** reclassify Cerebras as a one-time $5 signup credit (payment method required, 30-day validity), not a recurring no-card 1M tokens/day trial ([#11773](https://github.com/diegosouzapw/OmniRoute/issues/11773)) ([#12591](https://github.com/diegosouzapw/OmniRoute/pull/12591)) — thanks @HouMinXi +- fix(cli): stop prepublish from re-rebuilding the already-built ESM-only opencode-plugin dist (#11787) ([#11990](https://github.com/diegosouzapw/OmniRoute/pull/11990)) — thanks @hartmark +- **fix(db):** Fresh installs no longer log a non-fatal `no such table: compression_run_telemetry` warning when retention cleanup runs before the lazily-created telemetry table exists ([#11802](https://github.com/diegosouzapw/OmniRoute/pull/11802)) — thanks @RaviTharuma +- fix(sse): set X-OmniRoute-Selected-Connection-Id on successful combo dispatches so downstream consumers stop falling back to an empty connection id (#11810) ([#11986](https://github.com/diegosouzapw/OmniRoute/pull/11986)) — thanks @hartmark +- **fix(services):** embedded CLIProxyAPI lifecycle checks now use public `/healthz`, while model discovery uses the configured dedicated data-plane API key instead of the management password ([#11811](https://github.com/diegosouzapw/OmniRoute/pull/11811)) — thanks @RaviTharuma +- Fix persisted model steps returning an error in the playground route simulator, and warn when structural steps cannot be simulated. ([#11862](https://github.com/diegosouzapw/OmniRoute/pull/11862)) — thanks @pacocartones +- **fix(providers):** Antigravity's dynamic mitmAlias table no longer routes `gemini-3.7-flash-{high,medium,low}` to a literal tier-suffixed upstream id just because one connected account's own discovery listed it directly — those display ids always resolve through the safe `gemini-3.7-flash-tiered` static alias, so one account's Google-provisioned access no longer 404s every sibling account of the provider ([#11824](https://github.com/diegosouzapw/OmniRoute/issues/11824), [#11651](https://github.com/diegosouzapw/OmniRoute/issues/11651)) ([#11988](https://github.com/diegosouzapw/OmniRoute/pull/11988)) — thanks @hartmark +- **fix(dashboard):** Expose Profile loading, errors, page structure, and XP progress to assistive technologies ([#11838](https://github.com/diegosouzapw/OmniRoute/pull/11838)) — thanks @pacocartones +- **fix(socks):** `Agent.connectTimeout` now bounds both SOCKS handshake and TLS connect, and `family === null` no longer falls back to `fetch-socks` ([#11842](https://github.com/diegosouzapw/OmniRoute/pull/11842)) — thanks @maxmad64bis +- **perf(compression):** OOM mitigations for large payload hashing, memoization, and token estimation ([#11844](https://github.com/diegosouzapw/OmniRoute/pull/11844) — thanks @AndrianBalanescu) +- Excluded Next.js Node File Trace manifests (`*.nft.json`) from the published npm tarball. They are build-time metadata and are never read while serving, but had grown to 668.7 MB — 61% of the package — which pushed the upload past the registry limit and made `npm publish` fail with `413 Payload Too Large`. ([#11864](https://github.com/diegosouzapw/OmniRoute/pull/11864)) +- **fix(cli):** keep the global OmniRoute server `--base-url` separate from provider-node `--endpoint` values in add, update, and validate commands ([#11860](https://github.com/diegosouzapw/OmniRoute/pull/11860)) — thanks @pacocartones +- **fix(config):** Nous Research's `Hermes-4-405B` model now displays as "Hermes 4 405B (Nous Research)" in both the provider registry and the free-model catalog, instead of the mislabelled "Hermes 4 7B" ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#11993](https://github.com/diegosouzapw/OmniRoute/pull/11993)) +- **fix(provider/nous):** inject required user= tag into Nous Research inference requests to resolve upstream 400 "missing tags" error ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#12044](https://github.com/diegosouzapw/OmniRoute/pull/12044)) +- **fix(build):** `npm run build` now fails in one second with a named package and a copy-pasteable fix when npm silently drops an externalised optional native dependency, instead of dying four minutes in with `Module not found: Can't resolve 'better-sqlite3'` ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- **fix(install):** `postinstall` no longer throws `ReferenceError: isAndroid is not defined` — failing the whole `npm install` — when the `better-sqlite3` rebuild fallback times out; the manual-fix guidance is reachable again ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- Fixed the Alibaba free-tier allowlist test that went red on its own once the shipped catalog's `validUntil` (2026-08-27) passed, leaving every PR and `main` with a failing `Unit Tests (1/8)`. The test now builds its own packs with dates it controls, and covers the expired-pack fallback that production has actually been serving. ([#11867](https://github.com/diegosouzapw/OmniRoute/pull/11867)) +- Split the npm registry upload into its own GitHub-hosted job. npm refuses `--provenance` from a self-hosted runner (`422 ... Only "github-hosted" runners are supported`), which blocked the v3.8.50 publish; the heavy verification cannot move to a hosted runner, so it now hands the proven tarball over instead. ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(ui):** the shared `DataTable` loading state no longer reads its decorative ⏳ glyph out to assistive technology, and now carries the same `role="status"` / `aria-live="polite"` / `aria-busy="true"` semantics as `PageLoading` ([#11869](https://github.com/diegosouzapw/OmniRoute/pull/11869)) — thanks @pacocartones +- **fix(api):** Bound the `timeout` accepted by `POST /api/providers/[id]/login` to the same 15s–600s window the Adobe Firefly and Conol browser-login paths already enforce, so a request can no longer pin a headful browser and the single active-login slot with an arbitrarily large poll budget. ([#11871](https://github.com/diegosouzapw/OmniRoute/pull/11871)) — thanks @pacocartones +- **fix(api):** Stop the tunnel and MITM routes from returning raw child-process error text, which disclosed host paths, binary install locations and Tailscale `tskey-*` credentials in a body some of these routes serve to non-loopback callers, and return a real 400 instead of a framework 500 when a tunnel request body fails validation. ([#11872](https://github.com/diegosouzapw/OmniRoute/pull/11872)) — thanks @pacocartones +- **fix(open-sse):** stream TTFT/ITL are sampled from a monotonic clock (`performance.now`) instead of `Date.now`, so an NTP correction or wall-clock jump can no longer inflate a genuine TTFT or produce a negative one that the `ttft >= 0` guard silently drops — protecting the router's OTel/EWMA/`usage_history`/speed-ranking signals ([#11873](https://github.com/diegosouzapw/OmniRoute/pull/11873)) — thanks @pacocartones +- **fix(providers):** xAI `reasoning_effort: "xhigh"` now reaches grok-4.6+ instead of being silently clamped to `"high"` ([#11879](https://github.com/diegosouzapw/OmniRoute/pull/11879)) — thanks @NoxzRCW +- **fix(dashboard):** the providers page no longer crashes into the error boundary when a provider id collides with an `Object.prototype` member (`constructor`, `__proto__`); icon lookups are own-property guarded ([#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)) — thanks @NoxzRCW +- **fix(skills):** injected skill tools declared in shorthand (`{"content": "string"}`) now forward valid JSON Schema, unblocking providers that validate tool schemas strictly such as Zhipu GLM on the Console Go tier ([#11881](https://github.com/diegosouzapw/OmniRoute/pull/11881)) — thanks @NoxzRCW +- **fix(api):** `POST /api/playground/simulate-route` now surfaces `combo-ref` and `provider-wildcard` persisted combo steps with a specific warning (naming the referenced combo, or the unresolved `provider/model` wildcard) instead of folding them into a generic "unsupported step" count; a `provider-wildcard` step is also now included as an unresolved target so the operator can see it is in the route (ported from [#11882](https://github.com/diegosouzapw/OmniRoute/pull/11882) — thanks @NoxzRCW). ([#11930](https://github.com/diegosouzapw/OmniRoute/pull/11930)) +- **fix(translator):** the streaming OpenAI→Claude translator keeps upstream usage, including prompt-cache tokens, when it arrives on a trailing `choices: []` chunk (Fireworks and any upstream using `stream_options.include_usage`) ([#11883](https://github.com/diegosouzapw/OmniRoute/pull/11883)) — thanks @NoxzRCW +- **fix(cli):** `omniroute update --apply` now tells you explicitly whether a running server was detected and, if so, that you must run `omniroute restart` to apply the update — it never restarted anything and previously implied the update was already live once files were installed. The dashboard's npm-mode Update flow (`/api/system/version`) now tries OmniRoute's own PID-file-managed supervisor before falling back to pm2, and reports an honest "restart required" step instead of a silent pm2-only "skipped" that read like a completed update. The server-side latest-version lookup backing the dashboard's update banner also gained `--prefer-online`, closing the same stale-npm-cache class already fixed in the CLI's own copy for #4376 ([#11885](https://github.com/diegosouzapw/OmniRoute/issues/11885)). ([#12005](https://github.com/diegosouzapw/OmniRoute/pull/12005)) +- **fix(resilience):** clear persisted LKGP pins when a target suffers connection/provider exhaustion or is skipped before dispatch due to cooldown/exhaustion/unavailability, preventing subsequent requests from repeatedly prioritizing known-dead providers ([#11911](https://github.com/diegosouzapw/OmniRoute/issues/11911)). ([#12013](https://github.com/diegosouzapw/OmniRoute/pull/12013)) — thanks @HouMinXi +- **fix(sse):** the OpenAI→Claude stream translator now defers the terminal `message_delta`/`message_stop` emission until the real usage block has arrived (or a genuine end-of-stream flush forces it) instead of emitting it immediately on `finish_reason` — previously, when the trailing usage-only chunk (`{"choices":[],"usage":{...}}`) arrived *after* the `finish_reason` chunk (the normal order for Fireworks/vLLM/Together and other `stream_options.include_usage` upstreams), the client-visible `message_delta` still carried stale/zero usage even though `state.usage` was internally corrected too late to matter (ported from [#11915](https://github.com/diegosouzapw/OmniRoute/pull/11915) — thanks @HouMinXi). ([#11933](https://github.com/diegosouzapw/OmniRoute/pull/11933)) +- **fix(sse):** `stripStore()` now forces `store=false` for stateless OpenAI-compatible Responses-API targets (unless the connection has explicitly opted in via `providerSpecificData.openaiStoreEnabled`), instead of only handling the `openai`/`agentrouter` cases — a client-supplied `store` value previously passed through untouched to backends that don't actually persist responses server-side ([#11916](https://github.com/diegosouzapw/OmniRoute/pull/11916) — thanks @HouMinXi). +- **fix(models):** custom provider-node models (synced, custom, and alias-backed) now appear under their configured prefix in the unified catalog when the operator's model-id prefix mode is canonical, instead of being dropped whenever alias-inclusion was otherwise disabled ([#11918](https://github.com/diegosouzapw/OmniRoute/pull/11918) — thanks @HouMinXi). +- **fix(models):** the unified model catalog now suppresses stale static registry models (including effort-tier variants) for any provider whose active connection has an authoritative live synced catalog, not only providers already using exclusive-synced-listing — a connection with `providerUsesAuthoritativeLiveCatalog` previously kept serving both the live-synced models and the stale static rows side by side ([#11919](https://github.com/diegosouzapw/OmniRoute/pull/11919) — thanks @HouMinXi). +- **fix(ui):** `ProviderIcon`'s three remaining unguarded lookups (`PROVIDER_ICON_ALIASES`, `LOCAL_SVG_ALIASES`, `THEMED_SVGS`) now use `Object.hasOwn()` own-property checks — a provider id such as `constructor` or `__proto__` previously resolved through the prototype chain instead of falling through to the unknown-provider CDN fallback (`getLobeProviderIcon()` itself was already guarded by [#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)); `ProviderPageHeader`'s `color` field is also now optional, matching the rest of the component's defensive typing (ported from [#11920](https://github.com/diegosouzapw/OmniRoute/pull/11920) — thanks @HouMinXi). ([#11935](https://github.com/diegosouzapw/OmniRoute/pull/11935)) — thanks @NoxzRCW +- fix(ollama): preserve multi-byte UTF-8 content split across stream chunks in the Ollama NDJSON transform, which previously corrupted CJK/emoji into U+FFFD (#11921) — thanks @pacocartones +- **fix(providers):** OrcaRouter chat requests now target `/v1/chat/completions` instead of the bare `/v1` API root, fixing the upstream `404 Invalid URL (POST /v1)` ([#11923](https://github.com/diegosouzapw/OmniRoute/pull/11923)). — thanks @echel0nn +- **fix(plugins):** deliver the `onStreamComplete` event to disk-installed plugins. The event shipped in v3.8.50 (#9669) was emitted internally but had no plugin-facing wiring, so no plugin could ever subscribe: the manifest schema silently dropped `hooks.onStreamComplete`, and the loader/manager only knew the seven legacy hooks. `onStreamComplete` is now a declarable manifest hook, wired through the loader and registered by the manager like the other hooks, and its payload carries a `requestId` so consumers can correlate the stream-completion event with the originating request ([#11934](https://github.com/diegosouzapw/OmniRoute/pull/11934)) — thanks @amaleta / @pacocartones +- **fix(db):** the Qdrant embedding-model dropdown now lists local/self-hosted providers (Ollama, LM Studio, vLLM, etc.) — an active connection is treated as "configured" when the provider allows an optional API key, not only when it has a real key or OAuth, so a running local embedding provider is no longer hidden from the picker ([#11949](https://github.com/diegosouzapw/OmniRoute/issues/11949)) ([#11995](https://github.com/diegosouzapw/OmniRoute/pull/11995)) +- **fix(providers):** Vertex AI Anthropic partner-model discovery now calls the Model Garden `v1beta1` publisher list (`/v1beta1/publishers/anthropic/models`, global) and parses its `publisherModels` envelope, so Claude models auto-synced from Vertex populate the active live catalog and route at request time instead of returning `Model '' is not available in the active live catalog` ([#11991](https://github.com/diegosouzapw/OmniRoute/issues/11991)) — thanks @fabioluissilva ([#11998](https://github.com/diegosouzapw/OmniRoute/pull/11998)) +- **fix(cli):** support `--base-url` alongside `--endpoint` in `omniroute nodes add`, `update`, and `validate` subcommands to prevent global `--base-url` shadowing issues ([#11999](https://github.com/diegosouzapw/OmniRoute/issues/11999)). ([#12033](https://github.com/diegosouzapw/OmniRoute/pull/12033)) — thanks @HouMinXi +- **fix(providers):** `cloudflare-ai` no longer refuses image content parts for every Workers AI model ([#12002](https://github.com/diegosouzapw/OmniRoute/pull/12002)) — the plain-string `content` requirement behind #2539 is carried by the _model_ schema, not by the `/ai/v1/chat/completions` endpoint (measured: an all-text part array returns 200 on `@cf/mistralai/mistral-small-3.1-24b-instruct`, `@cf/meta/llama-4-scout-17b-16e-instruct` and `@cf/meta/llama-3.3-70b-instruct-fp8-fast`, and 400 on the text-only `@cf/qwen/qwen2.5-coder-32b-instruct`). `transformRequest()` flattened every array and threw on the first non-text part (#6390), so image input was refused for vision-capable Cloudflare models that accept it. All-text arrays are still flattened — the one shape every model accepts — while an array carrying a non-text part is passed through untouched, so the attachment is still never silently dropped. Regression guards: `tests/unit/cloudflare-ai-image-parts-6390.test.ts`. — thanks @davidlinfr +- **fix(resilience):** decouple the limiter-managed execution backstop from the queue-wait budget — new `requestQueue.executionMaxWaitMs` (env `RATE_LIMIT_EXECUTION_MAX_WAIT_MS`, default 600000 = 10 min) now feeds Bottleneck's post-dispatch `expiration`, while `requestQueue.maxWaitMs` keeps its documented queue-wait semantics. Previously the queue-wait budget doubled as the execution expiration, so legitimate long-running calls on non-incremental gateways (whole generation buffered before the first upstream byte, e.g. Console Go / Command Code tiers serving GLM models) were killed mid-flight at the queue budget with a false 504 `RATE_LIMIT_EXECUTION_TIMEOUT` — the local limiter undercut the provider-aware upstream fetch-start timeouts. The surfaced 504 message now names `requestQueue.executionMaxWaitMs`; the error keeps the #4165 guarantees (disclaims an upstream timeout, preserves the Bottleneck error as `cause`, branded code + trusted provenance, classified request-scoped so combo falls back). A real queue-wait bound (the `Promise.race` around `limiter.schedule()` sketched in #9533) remains future work. (#12025) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **Call logs:** keep the `error` field when an artifact exceeds the storage cap, instead of replacing it with the omission marker. The error is the only field that says *why* a request failed and is typically ~90 bytes next to the multi-hundred-KB bodies that trip the cap, so dropping it left a size-limited row undiagnosable — a provider outage, a local timeout and an upstream 400 all rendered identically. It is now preserved at every fallback stage, truncated to 4KB if it is itself large ([#12026](https://github.com/diegosouzapw/OmniRoute/issues/12026)). ([#12095](https://github.com/diegosouzapw/OmniRoute/pull/12095)) — thanks @ntdat812 +- **fix(diagnostics):** preserve the error field (truncated to 4KB with a `[truncated: …]` suffix) in every call-log artifact size-limit fallback stage. Previously the minimal fallback replaced the error with `[omitted: call log artifact size limit exceeded]`, so an oversized artifact row showed nothing about WHY the request failed — e.g. 91 of 847 opencode-go 504 rows on one production instance were undiagnosable from the dashboard. Oversized request/response bodies are still omitted exactly as before; the error cap is independent of the payload sizes that tripped the fallback. (#12026) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **fix(sse):** OpenAI Responses clients that declare the native `web_search` tool now receive a spec-shaped `web_search_call` output item with `action.sources` alongside the preserved function-call round-trip, so search results executed through OmniRoute's own search backend are consumable by standard Responses clients (Codex, pi-web-access, …). ([#12031](https://github.com/diegosouzapw/OmniRoute/pull/12031)) — thanks @watchingdogs +- **fix(cli):** use in-thread alias resolver hooks on modern runtimes to avoid deprecation noise and improve Node.js forward compatibility ([#12073](https://github.com/diegosouzapw/OmniRoute/issues/12073)). ([#12083](https://github.com/diegosouzapw/OmniRoute/pull/12083)) — thanks @xiaoyaner0201 +- **fix(combo):** an operator-set **Agent Features → Context length** on a combo is now honored at request time. The value was persisted and advertised through `/v1/models`, but `resolveComboContextLimit()` never consulted it — so a multi-target combo whose members carry no per-model window fell through to the provider's generic `defaultContextLength` (openrouter 128000, command-code 200000) and rejected large requests with `Input exceeds context window … limit 128000` despite the combo being explicitly sized much larger. An identical single-target combo worked, because it collapses to its concrete target before the guard runs. Invalid values (0/negative/NaN/Infinity) are ignored, so the existing target → combo-min → fallback order is unchanged. ([#12090](https://github.com/diegosouzapw/OmniRoute/pull/12090)) — thanks @adivekar-utexas +- **fix(sse):** passthrough streams now estimate usage on finish when upstream closes without usage even with `stream_options.include_usage` — avoids `0 tokens / 0%` for providers that stay silent (and correctly handles trailing empty-choices usage) ([#12151](https://github.com/diegosouzapw/OmniRoute/pull/12151)) — thanks @maxmad64bis +- **fix(combos):** clearing an agent feature in the combos editor now persists — unchecking context cache protection, or emptying the system message or tool filter, sends an explicit `null` instead of dropping the field from the `PUT` body, which the update merge read as "leave unchanged" ([#12177](https://github.com/diegosouzapw/OmniRoute/pull/12177)) — thanks @foreveryh / @ntdatt812 +- **fix(memory):** self-hosted embedding endpoints now vectorize — the vector width is measured from the first embedding that comes back instead of being read from a registry that cannot describe them, so `vec_memories` is created and memories stop piling up unvectorized behind a green health check ([#12180](https://github.com/diegosouzapw/OmniRoute/pull/12180)) — thanks @kanade-hoshino / @ntdatt812 +- **fix(translator):** the leading `system` message now reaches Responses-API upstreams when its `content` is a content-part array — it was read as `typeof content === "string" ? content : ""`, so a prompt-caching client (Anthropic `cache_control`, the shape LiteLLM and the Anthropic SDK emit) had its entire system prompt replaced by an empty `instructions`. The request was still accepted with a normal `prompt_tokens` count, so the model answered with no instructions and nothing in the response said they were missing. Mid-conversation system turns already handled the array shape ([#7056](https://github.com/diegosouzapw/OmniRoute/pull/7056)); only the first one did not ([#12206](https://github.com/diegosouzapw/OmniRoute/issues/12206)). Regression guard: `tests/unit/translator-openai-responses-system-content-parts.test.ts`. ([#12207](https://github.com/diegosouzapw/OmniRoute/pull/12207)) — thanks @Gorillaz322 +- **fix(free-tier):** `/api/free-tier/summary` no longer computes its totals from a Radar feed built before the catalog the running release ships. When the cached feed is older — or carries no build date at all — the route answers from the shipped catalog, resolved through the operator's local model state so disabled and tombstoned models stay out of the numbers ([#12215](https://github.com/diegosouzapw/OmniRoute/pull/12215)). — thanks @maxmad64bis +- **fix(oauth):** Keep a Claude personal workspace and a Team organization as separate connections — they share the same email and `accountUUID`, so the email-only OAuth dedup let the second login overwrite the first account's tokens; `organizationUUID` now disambiguates them, the way `workspaceId` does for Codex ([#12222](https://github.com/diegosouzapw/OmniRoute/pull/12222)) — thanks @davidebaraldo +- **fix(resilience):** a 402 on a single paid model of a passthrough/gateway provider (e.g. `kilo-gateway`, `ollama-cloud`) no longer terminalizes the whole connection with a never-auto-recovered `credits_exhausted` status — only the paid model is locked out, so free models on the same key keep serving. 402 variant of [#3027](https://github.com/diegosouzapw/OmniRoute/issues/3027). Single-credential providers are unaffected — a 402 there is still treated as the key being genuinely out of credit ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239) / [#10616](https://github.com/diegosouzapw/OmniRoute/issues/10616)) ([#12242](https://github.com/diegosouzapw/OmniRoute/issues/12242)) — thanks @brick30llc-ctrl ([#12266](https://github.com/diegosouzapw/OmniRoute/pull/12266)) — thanks @geekyNads +- **fix(sse):** trust `finish_reason: "length"`/`"max_tokens"` over the reasoning-consumed-token ratio in response quality validation, so a reasoning model truncated below the old 90% threshold correctly fails and retries instead of returning empty content as a silent "success" ([#12262](https://github.com/diegosouzapw/OmniRoute/pull/12262)) — thanks @brick30llc-ctrl +- **fix(settings):** `PUT /api/settings/cache-config` now persists `alwaysPreserveClientCache` to the flat general settings the runtime cache-control policy actually reads; previously the value landed in the databaseSettings "cache" section and was silently ignored, so the endpoint had no effect on `cache_control` passthrough ([#12304](https://github.com/diegosouzapw/OmniRoute/pull/12304)) — thanks @davidebaraldo +- **fix(grok-cli):** treat omitted SuperGrokPro `creditUsagePercent` as 0% used so Provider Limits still renders a weekly bar (proto3 zero-elision) ([#12312](https://github.com/diegosouzapw/OmniRoute/pull/12312)) — thanks @HouMinXi +- **fix(combo):** Expose the two Auto-Combo scoring factors nobody could set — the combo validation schema and the dashboard weight sliders both declared 13 of the scorer's 15 factors, so `connectionDensity` (spreads load across a provider's connections) and `quality` were dropped on save and offered nowhere. The sliders also shipped their own default table that differed from the engine's on every non-zero factor and summed to 1.05, so the percentages shown next to them added up to 105%. Both lists now match `DEFAULT_WEIGHTS`, and a test keeps them there. Note that a combo whose stored `weights` omitted the two keys was effectively running with them at zero and the other thirteen renormalized upward; it now runs with the engine's intended distribution, so its routing does shift ([#12314](https://github.com/diegosouzapw/OmniRoute/pull/12314)) — thanks @maxmad64bis +- **fix(docs):** The free-tier reference no longer says its numbers come "confidence tagged per row" — no catalog entry carries a confidence tag and the API serves none, so every figure on that page is an estimate of the same, unstated quality. The page now states what an entry does vouch for: an independently documented hard stop (set by hand with the source in a comment, never defaulted to `true`) and a prompt-training disclosure, both with live counts the `check:docs-counts` gate keeps honest ([#12318](https://github.com/diegosouzapw/OmniRoute/pull/12318)) — thanks @maxmad64bis +- **fix(usage):** `adobe-firefly` and `firefly` have had a working usage fetcher since Adobe Firefly landed, but neither was ever added to the registration list, so the provider-plugin manifest, `genericQuotaFetcher` and the free-access quota cache all reported them as having no usage support — while `USAGE_SUPPORTED_PROVIDERS` said the opposite. Both are now declared, which also means their credit balance is fetched like any other declared provider's: `registerGenericQuotaFetchers` now registers a generic quota fetcher for them, and `resolveFreeAccessState` no longer returns early. A test holds the registration list to the dispatcher's switch in both directions, which is what the module's own docstring already asked for in prose ([#12321](https://github.com/diegosouzapw/OmniRoute/pull/12321)) — thanks @maxmad64bis +- **fix(quota):** drop the generic quota cache (agy / Antigravity / Claude OAuth) on an upstream 429 so reset-aware scoring does not keep a 60s stale snapshot, and force-refresh the next usage fetch so inner provider caches cannot recache the same window ([#12325](https://github.com/diegosouzapw/OmniRoute/pull/12325)) — thanks @HouMinXi +- **fix(combos):** deleting a combo now clears its persisted LKGP pins instead of leaving unreachable `key_value` rows behind ([#12326](https://github.com/diegosouzapw/OmniRoute/issues/12326)) ([#12425](https://github.com/diegosouzapw/OmniRoute/pull/12425)) — thanks @KrzysiekSko +- **fix(sse):** Keep ZWNJ (U+200C) and ZWJ (U+200D) in assistant text, reasoning and tool-call arguments — Persian/Kurdish half-space (`ارائه‌دهنده`), Arabic/Indic shaping and emoji sequences no longer lose them; the response de-obfuscation now removes joiners only between ASCII word characters, where the request side inserts them ([#12186](https://github.com/diegosouzapw/OmniRoute/issues/12186)) — thanks @rezjalibd ([#12359](https://github.com/diegosouzapw/OmniRoute/pull/12359)) — thanks @pacocartones +- **fix(resilience):** count resolved upstream 5xx results against the provider circuit breaker on the chat path — `CircuitBreaker.execute()` no longer reads a resolved `{ success: false, status: 5xx }` as a success that cancels the call-site failure, so a provider answering 503s now trips its breaker instead of staying `CLOSED` at `failureCount: 1`; single-model and combo dispatches are each accounted exactly once ([#12254](https://github.com/diegosouzapw/OmniRoute/issues/12254)) ([#12360](https://github.com/diegosouzapw/OmniRoute/pull/12360)) — thanks @pacocartones +- **fix(providers):** resolve the Codex quota auto-ping model from the live provider catalog and lifecycle registry instead of the retired `gpt-5.1-codex-mini`, and pause the ping with one actionable warning when no selectable Codex model exists rather than retrying a shut-down id every cooldown window ([#11905](https://github.com/diegosouzapw/OmniRoute/issues/11905)) ([#12361](https://github.com/diegosouzapw/OmniRoute/pull/12361)) — thanks @pacocartones +- **fix(api):** keep the `{created, data}` wrapper on combo-routed `/v1/images/generations` responses and default Codex image results to `b64_json` on both `/v1/images/generations` and `/v1/images/edits` so Codex CLI's built-in `image_gen` can decode them ([#12268](https://github.com/diegosouzapw/OmniRoute/issues/12268)) ([#12362](https://github.com/diegosouzapw/OmniRoute/pull/12362)) — thanks @pacocartones +- **fix(sse):** Name the shadowed custom provider node when a built-in provider id/alias (e.g. `openference` → `of`) reserves the prefix of an existing OpenAI/Anthropic-compatible node, so the runtime `No active credentials for provider: ` error explains that the prefix routed to the built-in and never reached the node's healthy connections, instead of contradicting the dashboard ([#11943](https://github.com/diegosouzapw/OmniRoute/issues/11943)) — thanks @morpheus9393 ([#12365](https://github.com/diegosouzapw/OmniRoute/pull/12365)) — thanks @pacocartones +- **fix(i18n):** wrap `ccOnboardingKeyPlaceholder` in ICU single quotes across all 43 locales so angle brackets render literally instead of being parsed as rich-text tags, which crashed the Claude Code onboarding block with `INVALID_MESSAGE: INVALID_TAG` ([#12302](https://github.com/diegosouzapw/OmniRoute/issues/12302)) ([#12369](https://github.com/diegosouzapw/OmniRoute/pull/12369)) — thanks @gonisulaimann +- **fix(models):** publish `effort_tiers` on Kimi K3's synced base-model entries (`kmca/k3`, `kmca/k3-256k`) so catalog-only clients (OpenCode, plain SDK pickers) can see and select the reasoning tiers (`low`/`high`/`max`) the synced metadata already carried — the `isSkippedEffortProvider` gate no longer suppresses tier visibility on those base entries, while synthetic `-` variant generation stays prevented and Codex/GLM base models remain excluded unchanged ([#12299](https://github.com/diegosouzapw/OmniRoute/issues/12299)) ([#12371](https://github.com/diegosouzapw/OmniRoute/pull/12371)) — thanks @gonisulaimann +- **fix(guardrails):** keep `auto`/`auto/*` virtual combos exempt from the Vision Bridge `fixedModel` credential guard so a combo target is passed through instead of silently falling back to global auto-selection ([#12237](https://github.com/diegosouzapw/OmniRoute/issues/12237)) ([#12373](https://github.com/diegosouzapw/OmniRoute/pull/12373)) — thanks @pacocartones +- **fix(combo):** capability-filter exhaustion caused by `max_tokens` above every target's known output limit now reports that reason (requested `max_tokens` vs the pool's highest known ceiling) instead of the unrelated "supports structured output" message ([#12229](https://github.com/diegosouzapw/OmniRoute/issues/12229)) — thanks @DW-MediaLab ([#12374](https://github.com/diegosouzapw/OmniRoute/pull/12374)) — thanks @pacocartones +- **fix(auth):** the `least-used` account strategy now prefers accounts without backoff before falling back to oldest `lastUsedAt`, the same tie-break `round-robin` already applies, so a failover no longer lands on a just-rate-limited account for a single request ([#12279](https://github.com/diegosouzapw/OmniRoute/issues/12279)) — thanks @tenshiak ([#12375](https://github.com/diegosouzapw/OmniRoute/pull/12375)) — thanks @pacocartones +- **fix(docker):** the `chatgpt-web-codex-browser` image now finds the Chrome binary under `chrome-linux64/` (Chrome for Testing layout in `playwright:v1.62.0-noble`) as well as the legacy `chrome-linux/`, so the container no longer crash-loops with `exec: --headless=new: not found` ([#12024](https://github.com/diegosouzapw/OmniRoute/issues/12024)) ([#12376](https://github.com/diegosouzapw/OmniRoute/pull/12376)) — thanks @pacocartones +- **fix(providers):** declare `groq/compound` and `allam-2-7b` as non-reasoning models in the curated Groq registry so `reasoning_effort` / `output_config.effort` / `thinking` from Claude Code are stripped instead of forwarded, which Groq rejected with HTTP 400 ([#12134](https://github.com/diegosouzapw/OmniRoute/issues/12134)) ([#12379](https://github.com/diegosouzapw/OmniRoute/pull/12379)) — thanks @pacocartones +- **fix(executors):** `OpencodeExecutor` no longer forces a direct connection when the connection has a proxy assigned in Proxy Management but no per-account proxies: the single-account fast path used to wrap the upstream dispatch in the direct-egress sentinel, discarding the ambient proxy context the chat handler had pinned from `proxy_assignments`, so API-key `opencode`/`opencode-go` connections egressed from the host IP (and hit geoblocks) despite the assignment. The direct pin is now applied only when no ambient proxy context exists ([#11894](https://github.com/diegosouzapw/OmniRoute/issues/11894) — thanks @hizzt) ([#12380](https://github.com/diegosouzapw/OmniRoute/pull/12380)) — thanks @pacocartones +- **fix(api):** `GET /v1/models` with `MODELS_CATALOG_PREFIX_MODE=canonical` (or `?prefix=canonical`) now lists providers whose registry alias is undefined or equal to their own id (Antigravity, Antigravity CLI and other self-aliased built-ins) — their single `provider/model` id was dropped by the alias/canonical duplicate guard in the static, synced, custom and alias-backed catalog loops ([#12058](https://github.com/diegosouzapw/OmniRoute/issues/12058)) — thanks @cheynetom ([#12381](https://github.com/diegosouzapw/OmniRoute/pull/12381)) — thanks @pacocartones +- **fix(translator):** Drop replayed `thinking` blocks that carry no signature (the shape produced from cross-provider `reasoning_content`) instead of stamping the default Claude signature on them, which Anthropic rejected with `400 Invalid signature in thinking block` on the next turn served by an Anthropic rung ([#12105](https://github.com/diegosouzapw/OmniRoute/issues/12105)) — thanks @atescivitci-cmd ([#12386](https://github.com/diegosouzapw/OmniRoute/pull/12386)) — thanks @pacocartones +- **fix(cli):** Resolve Bun's `--preload` polyfill path against the package root instead of `dist/`, so `omniroute` installed with `bun install -g` no longer crashes at startup with `error: preload not found …/dist/open-sse/utils/setupPolyfill.ts` ([#11980](https://github.com/diegosouzapw/OmniRoute/issues/11980)) — thanks @joglomedia ([#12387](https://github.com/diegosouzapw/OmniRoute/pull/12387)) — thanks @pacocartones +- **fix(providers):** `gemini-business` now publishes its model catalog — `/v1/models` and `/v1/providers/gemini-business/models` list the 12 enterprise Gemini ids the executor understands instead of returning an empty list (#12107) ([#12389](https://github.com/diegosouzapw/OmniRoute/pull/12389)) — thanks @pacocartones +- **fix(db):** install `busy_timeout` before the SQLite connection's first statement so a process opening the database while another one closes its WAL connection waits out the transient EXCLUSIVE lock instead of dying with `database is locked`, and recognise the drivers' real BUSY/PROTOCOL/IOERR errors as transient in the corruption probe so the same lock no longer renames the database away as corrupt; deflakes `cross-process contenders never both acquire the same connection` (#12394 — thanks @pacocartones) +- **fix(chat-admission):** derive the `chat_admission_busy` 503 `Retry-After` from observed heavyweight-lease occupancy — the larger of the exhausted `OMNIROUTE_CHAT_ADMISSION_QUEUE_MS` window and the time since capacity last turned over, capped at 60 s — instead of a fixed 1 s (structural) / 2 s (byte-stage) hint that invited Codex/agent fan-out clients to re-send ~1 MiB `/v1/responses` bodies every second into a gate held for the whole SSE lifetime; an idle gate keeps the historical floors ([#12135](https://github.com/diegosouzapw/OmniRoute/issues/12135)) (#12395 — thanks @pacocartones) +- **fix(api-manager):** the API key permissions modal no longer silently drops `allowedCombos` entries its Combo picker cannot render — routing-rule names such as `rt-*`, which the backend already honours — when "All" is clicked and the key is switched back to "Restrict"; those entries now survive the toggle, are listed read-only under the combo list so the count and the list agree, and are saved back verbatim instead of persisting `[]` (deny-all) (#12397 — thanks @pacocartones) +- **fix(catalog):** write the NUL separator of the catalog connection memo key, the provider serviceKind memo key, the Video Bridge promotion group key and a JSON-exactness test fixture as the `\u0000` escape instead of a raw byte — same runtime value, but the raw byte made git, GitHub and ripgrep treat those files as binary (hidden PR diffs, silently skipped searches); a guard test now keeps raw NUL bytes out of `src/`, `open-sse/` and `tests/` (#12403 — thanks @pacocartones) +- **fix(providers):** add `CLAUDE_CODE_CLIENT_VERSION` and `GITHUB_COPILOT_CLI_VERSION` env overrides so Anthropic/Copilot client-version gates can be unblocked without a rebuild ([#12417](https://github.com/diegosouzapw/OmniRoute/issues/12417)) ([#12632](https://github.com/diegosouzapw/OmniRoute/pull/12632)) — thanks @HouMinXi +- **fix(db):** back-fill `last_ping_at` and `last_pinged_reset_key` on `provider_connections` during schema reconciliation so divergent lineages that skipped `123_quota_auto_ping` still accept quota auto-ping writes ([#12470](https://github.com/diegosouzapw/OmniRoute/pull/12470) — thanks @KooshaPari) +- **fix(ci):** document MIT exceptions for `@eloqnt/{config,format-json,format-po}` (next-intl transitive; locked tarballs omit `license`) and keep the A2A lifecycle vitest off the real SQLite persistence seam ([#12581](https://github.com/diegosouzapw/OmniRoute/issues/12581)) ([#12595](https://github.com/diegosouzapw/OmniRoute/pull/12595)) +- OpenCode plugin `/v1/models` catalog fetch now waits 30s by default and attaches HTTP `statusCode` on 401/5xx so host fallback plugins can hop instead of seeing an untyped AbortError/UnknownError. ([#12607](https://github.com/diegosouzapw/OmniRoute/pull/12607)) — thanks @RaviTharuma +- **CI:** the OpenAPI security-tier gate now mirrors `isAlwaysProtectedPath()` in full — it also reads `ALWAYS_PROTECTED_API_PATTERNS`, so the pattern-gated credential routes (`/api/providers/{id}/{claude,codex}-auth/{export,apply-local}`, GHSA-5926-2w35-7h4q) no longer report as unannotated. (#12605) +- **fix(api):** GET /v1/models no longer waits forever on a hung coalesced catalog rebuild; cold-path waits are bounded (`CATALOG_BUILD_TIMEOUT_MS`, default 8s) and a last-good 200 is served when the rebuild times out ([#12627](https://github.com/diegosouzapw/OmniRoute/issues/12627)). ([#12628](https://github.com/diegosouzapw/OmniRoute/pull/12628)) — thanks @RaviTharuma +- **fix(cursor):** a non-array `tool_calls` on an assistant message no longer crashes the cursor request translator with a `TypeError`; both loops now require an array ([#12689](https://github.com/diegosouzapw/OmniRoute/issues/12689)) ([#12691](https://github.com/diegosouzapw/OmniRoute/pull/12691)) — thanks @soroush5 +- **fix(chat):** Chat Completions no longer return empty `content` after a server-owned memory or skills tool; the first provider send and account/model recovery now share one pipeline so a follow-up round-trip can fill the reply ([#12696](https://github.com/diegosouzapw/OmniRoute/issues/12696)) — thanks @HouMinXi ([#12867](https://github.com/diegosouzapw/OmniRoute/pull/12867)) +- **fix(dashboard):** batch-deleting provider keys no longer toasts failure after a successful delete when the confirm button's click event is forwarded as `onAfter` ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) — thanks @HouMinXi +- **fix(glm):** drop the extra 16th argument to `createSSETransformStreamWithLogger` that TypeScript rejected (TS2554) and that never reached the TransformStream ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) ([#12770](https://github.com/diegosouzapw/OmniRoute/pull/12770)) — thanks @HouMinXi +- **fix(dashboard):** pass `nodeMap` into Runtime `QuotaGroup` so a quota monitor in error/exhausted/alerting no longer throws `ReferenceError: nodeMap is not defined`. ([#12868](https://github.com/diegosouzapw/OmniRoute/pull/12868)) — thanks @HouMinXi +- Restricted API keys whose `allowedModels` lists a combo name no longer skip every combo member at pre-dispatch (`ALL_TARGETS_SKIPPED`). Inner-target filtering still applies when the allow-list is a provider prefix or `disableNonPublicModels` is on ([#12899](https://github.com/diegosouzapw/OmniRoute/pull/12899)). — thanks @HouMinXi +- **fix(combos):** Effort-suffixed combo members inherit the base model's `model_context_overrides` row so priority order is not inverted on large requests ([#12926](https://github.com/diegosouzapw/OmniRoute/pull/12926)) — thanks @HouMinXi +- **fix(catalog):** Picker-added `customModels` enter the dispatch-time live catalog so combo members and bare inference no longer 400 ([#12934](https://github.com/diegosouzapw/OmniRoute/pull/12934)) — thanks @HouMinXi +- **fix(docker):** re-derive the Docker build's worker-pool memory budget from the MEASURED ~4.5 GB per-process RSS (the issue owner's own VPS dmesg OOM-killer reproduction) instead of the stale 2560 MB/worker inference #11419 shipped, and lower `OMNIROUTE_BUILD_WORKERS` 3 → 2 so 1 parent + 1 page-data worker (2 processes × 4.5 GB = 9 GB) fits the 12.288 GB (75%) budget on a 16 GB GitHub Actions runner — the previous default (1 parent + 2 workers = 13.5 GB) still overcommitted the runner and kept "Publish to Docker Hub" failing with `cannot allocate memory` after #11419 merged (#7518). ([#11663](https://github.com/diegosouzapw/OmniRoute/pull/11663)) +- **fix(electron):** the packaged Windows desktop build now passes the #7592 cold-restart smoke end to end. Five defects were found and fixed on the way: (1) optional-pack staging failed on any machine whose `tar` is GNU tar — it reads the drive letter in an absolute `-f C:\...` archive path as a remote rsh host (`Cannot connect to C:`), so staging now passes a bare filename with `cwd` at the tarball directory and surfaces tar stderr on failure; (2) the Electron `files` allowlist was missing `lib/loginHeaderCapture.js`, so the packaged main process crashed at startup with `Cannot find module './lib/loginHeaderCapture'`; (3) electron-builder ≥26 injects an `!**/node_modules/**` ignore into every extraResources pattern list that no positive filter can override, which silently dropped the staged runtime `node_modules` (including the better-sqlite3 N-API prebuild) from `resources/app` — a fresh v3.8.50 desktop build would have shipped with no native SQLite driver and reproduced the #7592 sql.js fallback on every machine; an `afterPack` hook now restores the staged `node_modules` after packing; (4) the packaged-app smoke harness redirected `USERPROFILE` into its temp DATA_DIR, but Electron resolves the Roaming profile from `%USERPROFILE%\AppData\Roaming\` (USERPROFILE takes precedence over the APPDATA env var) and the path service throws instead of creating it, so `requestSingleInstanceLock()` returned false and the app exited(0) silently before `app.whenReady()` — the harness now pre-creates the derived tree, and `ensureSmokeEnvDirs` is exported and unit-tested; (5) the #7592 driver assertion parsed a `[DB] Driver: ...` line that the server's primary DB path never printed (only the unused `openDatabaseAsync()` did) — `getDbInstance()` now emits the same line on open so the guard can actually assert the native driver. Also: the smoke env-allowlist unit test hardcoded POSIX paths and could only pass on Linux/macOS; expectations are now host-agnostic, with new regression tests for the USERPROFILE-derived tree and `tarPack` under absolute Windows-style paths ([#11443](https://github.com/diegosouzapw/OmniRoute/pull/11443)) — thanks @jonlwheat2-gif +- fix(sse): stop the auto-combo candidates inspector from silently dropping model-locked/cooled-down rows (#9133) ([#11994](https://github.com/diegosouzapw/OmniRoute/pull/11994)) — thanks @hartmark +- **fix(providers):** Perplexity Web no longer turns upstream stream failures into successful assistant text; pre-content failures remain eligible for fallback, partial output ends with a structured sanitized error, and failed sessions are not persisted ([#12465](https://github.com/diegosouzapw/OmniRoute/pull/12465)) +- **fix(video-bridge):** preserve chronological video and transcript fusion, source-frame timestamps, transcript provenance, full contact-sheet intervals, and pre-abort callback isolation ([#11681](https://github.com/diegosouzapw/OmniRoute/pull/11681)) +- **Z.ai Web:** HTTP 200 streams carrying an upstream error now terminate with a structured failure instead of assistant text plus a normal stop, preserving partial output while allowing pre-content combo fallback. ([#12454](https://github.com/diegosouzapw/OmniRoute/pull/12454)) +- Absorb `Error [AbortError]: request_signal_aborted` and DOMException AbortError shapes in the process-level client-abort crash guard so routine client disconnects no longer kill the server (exit code 7). ([#12165](https://github.com/diegosouzapw/OmniRoute/pull/12165)) — thanks @alvinveroy +- **fix(sse):** Treat Adapta Web `type:error` SSE events as sanitized non-stream failures instead of empty HTTP 200 completions. ([#12459](https://github.com/diegosouzapw/OmniRoute/pull/12459)) +- **fix(autoCombo):** layer 3 (`models_dev_tier`) no longer scores retired models as premium, merge cross-provider capability rows deterministically, and inherit tier scores through variant ids ([#11508](https://github.com/diegosouzapw/OmniRoute/issues/11508)). `getModelsDevTierFitness` now vetoes ids present in `config/quality/model-lifecycle.json` with status `retired` before any other signal; `loadModelCapabilities()` aggregates per `model_id` (any-non-null-true for booleans, max for `limit_context`) instead of last-write-wins over SQLite's undefined row order; a capabilities miss retries through `resolveScoresAs()`'s base id and reports `models_dev_tier:inherited`, matching layers 1–2 (#11492). Adds `getModelsDevTierFitnessWithSource()` and an `invalidateCapabilitiesCache()` test/ops hook. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) — thanks @Meet6338-X +- fix(sse): accept Claude `content:[]` + `stop_reason: max_tokens`/`tool_use` in `detectMalformedNonStream` (match `isEmptyContentResponse`) so Claude Code `/model` probes with `max_tokens: 1` no longer become a false 502 ([#11568](https://github.com/diegosouzapw/OmniRoute/pull/11568)) — thanks @Seramicx +- **fix(security):** sanitize `request.failed` diagnostics before publishing them to live dashboard listeners and replay history, while keeping status, model, provider, latency, and internal call-log diagnostics intact. ([#12469](https://github.com/diegosouzapw/OmniRoute/pull/12469)) +- **fix(pwa):** do not intercept dashboard navigations so Chrome can fall back from a stale HTTP/3 Alt-Svc advertisement (UDP :20128 is unpublished; F5 on a long-lived tab hung until a new tab opened a fresh TCP connection). ([#12767](https://github.com/diegosouzapw/OmniRoute/pull/12767)) — thanks @HouMinXi +- **fix(executors):** handle DuckDuckGo ERR_BN_LIMIT (418) without retrying — when the upstream returns `418 ERR_BN_LIMIT` (rate-limit/ban), the executor now returns the error immediately instead of burning another VQD acquisition that would only count against the IP limit. The retry logic for `418 ERR_CHALLENGE` (unsolved challenge) remains unchanged. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) ([#12110](https://github.com/diegosouzapw/OmniRoute/pull/12110)) — thanks @oyi77 / @Meet6338-X +- Fixed DuckDuckGo streaming responses losing JSON lines and UTF-8 characters split across network chunks. ([#11528](https://github.com/diegosouzapw/OmniRoute/pull/11528)) — thanks @pacocartones +- Harden generated Cloudflare, Vercel, and Deno relays by stripping hop-by-hop, framing, and proxy authentication headers before forwarding requests upstream. ([#11533](https://github.com/diegosouzapw/OmniRoute/pull/11533)) — thanks @pacocartones +- **feat(grok-cli):** Provider Limits shows grok-cli banked reset credits from `GetRemainingResets` (including a real zero; a failed RPC omits the row) and the existing View credits button now calls `ConsumerUiSvc/RedeemReset` for grok-cli. Live tokens use nested fields 10/20/30 (id + Timestamp), not compact 1/2/3. ([#12805](https://github.com/diegosouzapw/OmniRoute/pull/12805)) — thanks @HouMinXi +- **fix(memory):** Embedding Model Quick select, Embedding Source remote dropdown, and Rerank selector now list every configured provider with embedding/rerank support instead of only chat-catalog text matches plus OpenRouter live discovery; a generic OpenAI-compatible `/embeddings` + Cohere-compatible `/rerank` runtime fallback resolves any configured chat provider's embedding/rerank endpoint, so unlisted providers no longer fail with "Unknown embedding provider"; both memory selectors gained a free-text model override ([#11390](https://github.com/diegosouzapw/OmniRoute/pull/11390)) — thanks @rqzbeh +- Harden SQLite upgrades around the historical migration-074 version collision: missing discovery and inspector tables are replayed atomically, pre-existing databases (including setup-created skeletons) receive reusable content-addressed safety snapshots, and Node test/eval probes without `DATA_DIR` are isolated from the operator database. ([#12435](https://github.com/diegosouzapw/OmniRoute/pull/12435)) +- **fix(dashboard):** Moonshot/Kimi Open Platform voucher and cash leftover percentages follow the bucket balance, so an empty wallet no longer paints those rows as 100% while Available is 0% ([#12733](https://github.com/diegosouzapw/OmniRoute/pull/12733)) — thanks @HouMinXi +- **fix(combo):** return non-retryable HTTP 400 when all candidates for a pinned native Codex turn are unavailable due to model-scoped lockout, terminating the turn cleanly while preserving turn continuity and enabling standard Combo routing on subsequent turns ([#12240](https://github.com/diegosouzapw/OmniRoute/pull/12240)) — thanks @mdigitalbh81 +- Fixed auto-router strategies preserving the selected connection through ranking and dispatch when multiple connections share the same provider and model. ([#11530](https://github.com/diegosouzapw/OmniRoute/pull/11530)) — thanks @pacocartones +- Preserve Chat Completions JSON-object response formats when translating requests to the Responses API. ([#11529](https://github.com/diegosouzapw/OmniRoute/pull/11529)) — thanks @pacocartones +- Fix idempotency fingerprints so Responses API requests include `input` and other semantic generation fields using stable serialization, while excluding credentials and request metadata. ([#11532](https://github.com/diegosouzapw/OmniRoute/pull/11532)) — thanks @pacocartones +- **fix(api):** Generated API CLI commands now enforce required OpenAPI request bodies; Combo test commands forward the required `comboName` body, while API keys created by older writers after migration 149 preserve legacy allow-all Combo access without widening explicit empty allowlists — thanks @marcelokarval ([#12070](https://github.com/diegosouzapw/OmniRoute/pull/12070)) +- **fix(grok-web):** treat upstream streaming failures as failures instead of successful assistant text: error-only streams now fail readiness with HTTP 502, while failures after legitimate content preserve that partial output and terminate through the sanitized stream failure path without a normal `stop` completion. ([#12458](https://github.com/diegosouzapw/OmniRoute/pull/12458)) +- HuggingChat now turns HTTP 200 JSONL generation failures into a sanitized 502 before content, or a fixed public stream failure after partial output, so fallback and request persistence no longer record a false successful stop. ([#12456](https://github.com/diegosouzapw/OmniRoute/pull/12456)) +- **fix(providers):** keep 1min.ai HTTP 200 stream errors out of assistant content, preserve partial output, and expose sanitized terminal errors so pre-content failures can fall back. ([#12466](https://github.com/diegosouzapw/OmniRoute/pull/12466)) +- **fix(sessions):** preserve legacy Sessions data when the additive exclusive-session projection is unavailable, returning an empty projection and warning only once per contiguous outage instead of failing the endpoint; the Sessions badge intentionally reflects the merged legacy and exclusive row count introduced by [#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389) ([#11469](https://github.com/diegosouzapw/OmniRoute/pull/11469)) — thanks @KaspaPulse +- Render Nimble Search with the generic provider icon and serve Opper's proven logo locally. ([#12415](https://github.com/diegosouzapw/OmniRoute/pull/12415)) +- **fix(quality):** `config/quality/provider-assets-provenance.jsonl` was stale against the physical `public/providers/` tree (still listing 225 assets and 5 duplicate-content groups after several provider-retirement PRs removed 85 icon files), which would have made `check:provider-asset-provenance` fail on the next full run. Reconciled the manifest to the real 142-file snapshot (142 asset records, 1 duplicate-content group, 2 newly-registered files marked `unresolved` pending provenance research), repinned `auditedCommit`, and updated the regression test's hardcoded counts to match. ([#11876](https://github.com/diegosouzapw/OmniRoute/pull/11876)) +- fix(ui): wrap quota card footer action buttons to prevent clipping Refresh button on narrow cards — when extra action buttons such as `View credits` are present on Codex cards, allow the footer row to flex-wrap with `shrink-0` buttons so `Refresh now` remains visible and clickable across all responsive card widths ([#11464](https://github.com/diegosouzapw/OmniRoute/pull/11464)) — thanks @adevwithpurpose +- Keep Antigravity Gemini usable when the same connection's Claude weekly quota is empty; generic quota cache stays per-connection for every other provider. ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) — thanks @HouMinXi +- Fixed Tailscale tunnel setup to reject ports outside the valid 1-65535 range and safely install Windows MSI packages from paths containing apostrophes. ([#11534](https://github.com/diegosouzapw/OmniRoute/pull/11534)) — thanks @pacocartones +- Classify Cloudflared and Tailscale process-management routes as local-only and spawn-capable while preserving remote access to read-only tunnel status endpoints. ([#11531](https://github.com/diegosouzapw/OmniRoute/pull/11531)) — thanks @pacocartones +- Electron release workflow: the `publish-npm` job now grants `actions: read` to the reusable `npm-publish.yml` it calls (its `publish` job requests it), which is what made GitHub refuse the whole v3.8.50 run at startup and ship the release with zero desktop assets; a `workflow_dispatch` now builds the requested tag instead of the dispatching branch and can skip the npm leg (`publish_npm=false`) when only re-attaching assets ([#11974](https://github.com/diegosouzapw/OmniRoute/pull/11974)) +- **fix(dashboard):** The Combos page usage guide now reads its dismissal through `useSyncExternalStore` instead of correcting SSR state inside an effect, removing an extra commit of the page tree on every load (and the `react-hooks/set-state-in-effect` error it raised). ([#12671](https://github.com/diegosouzapw/OmniRoute/pull/12671)) +- Electron release: `electron/package-lock.json` regained the optional `electron-builder-squirrel-windows` subtree (13 entries) that `npm ci` had been refusing as out of sync, `electron-release.yml` gained a `build_ref` dispatch input and stops regenerating release notes on a re-attach dispatch, and the npm publish workflow attaches the SBOM to the GitHub Release on dispatch publishes too — so the v3.8.51 tag ships every desktop asset and the SBOM like v3.8.49 did ([#12022](https://github.com/diegosouzapw/OmniRoute/pull/12022)) +- Drained the reds every PR against `release/v3.8.51` was born with: documented the three service-worker build-id variables, registered the six retirement/tunnel tests with the mutation gate, approved `eslint-plugin-react-hooks` in the dependency allowlist, added the six `combo.sort.*` strings to `vi` and `pt-BR`, pointed the ChatGPT Web doc at the real migration-168 test, worded the g4f hint around the member key, and realigned four tests to the retired-provider catalog and the legacy-schema fixtures the retirement migrations touch. ([#11940](https://github.com/diegosouzapw/OmniRoute/pull/11940)) +- **fix(providers):** Claude, Grok, LMArena, Notion, and Perplexity web-cookie transports now use pooled `wreq-js` 3.2 instead of the native sidecar, with all nine supported bindings pinned and audited, and the applicable platform binding plus native-license evidence included in each release artifact ([#12429](https://github.com/diegosouzapw/OmniRoute/pull/12429), supersedes [#11753](https://github.com/diegosouzapw/OmniRoute/pull/11753)). +- **fix(providers):** Zed Hosted streaming failures now trigger fallback before content and end partial streams with a sanitized structured error instead of fake assistant text and a normal-success stop. ([#12455](https://github.com/diegosouzapw/OmniRoute/pull/12455)) +- **fix(test):** capture hook result after render (direct commit `b85d0abb0b`) +- **fix(sync):** repair the two regressions the v3.8.50 sync-back left on release/v3.8.51 (direct commit `5ade9e0851`) +- **revert(models):** drop the memo reset hook ported from #10055 (direct commit `282de841ae`) +- **fix(typecheck):** guard the optional connectionId before the reactive model sync (direct commit `f2825bad75`) +- **fix(quota-share):** release the winner's reserved in-flight slot (#11371) ([#11408](https://github.com/diegosouzapw/OmniRoute/pull/11408)) — thanks @oyi77 +- **fix(providers):** stop silently dropping reasoning effort on opencode families (#10788) ([#11409](https://github.com/diegosouzapw/OmniRoute/pull/11409)) — thanks @oyi77 +- **fix(combo):** enable genuine quota-aware routing for generic providers (antigravity, claude, etc.) ([#11411](https://github.com/diegosouzapw/OmniRoute/pull/11411)) — thanks @HouMinXi +- **fix(oauth):** allow expired token retry before deactivation and activate expired retry loop (#P0) ([#11414](https://github.com/diegosouzapw/OmniRoute/pull/11414)) — thanks @HouMinXi +- **perf(executors):** lazy-load the executor registry — defer class imports + construction to first use (#11220) ([#11421](https://github.com/diegosouzapw/OmniRoute/pull/11421)) — thanks @oyi77 +- **fix:** make release/v3.8.50 CI gates green (run 32786966560 root causes) ([#11450](https://github.com/diegosouzapw/OmniRoute/pull/11450)) — thanks @jonlwheat2-gif +- **fix(sse):** exact-domain cookie match and origin-equality URL assertions ([#11452](https://github.com/diegosouzapw/OmniRoute/pull/11452)) — thanks @hartmark +- **fix(providers):** send Responses API payload during OpenAI-compatible validation ([#11454](https://github.com/diegosouzapw/OmniRoute/pull/11454)) — thanks @tuandinh0801 +- **fix(command-code):** fallback to /alpha/generate for Go plan without Provider API access ([#11455](https://github.com/diegosouzapw/OmniRoute/pull/11455)) — thanks @Chewji9875 +- **fix(responses-continuation):** fail closed on a log-truncated stored input/output array ([#11473](https://github.com/diegosouzapw/OmniRoute/pull/11473)) — thanks @hartmark +- **fix(cli):** write jcode settings as [providers.omniroute] in config.toml ([#11484](https://github.com/diegosouzapw/OmniRoute/pull/11484)) — thanks @hongnoul +- **fix(logging):** raise chat-log array truncation cap to 1000 and unify duplicate implementations ([#11499](https://github.com/diegosouzapw/OmniRoute/pull/11499)) — thanks @hartmark +- **fix:** restore live websocket URL helpers ([#11509](https://github.com/diegosouzapw/OmniRoute/pull/11509)) — thanks @PauloFH +- **perf(core):** eliminate async waterfalls across home, cache, and db operations (#11396) ([#11512](https://github.com/diegosouzapw/OmniRoute/pull/11512)) — thanks @HouMinXi +- **fix(kiro):** add runtime.us-east-1.kiro.dev as first-attempt endpoint ([#11517](https://github.com/diegosouzapw/OmniRoute/pull/11517)) — thanks @rifqiawl +- **fix(providers):** remove duplicate 128-tool truncation in OpencodeExecutor (#11444) ([#11518](https://github.com/diegosouzapw/OmniRoute/pull/11518)) — thanks @HouMinXi +- **fix(providers):** poll onboardUser LRO before classifying BYOP ([#11519](https://github.com/diegosouzapw/OmniRoute/pull/11519)) — thanks @rifqiawl +- **fix(translator):** preserve exact tool name casing for Claude tool_use payloads (#11487) ([#11520](https://github.com/diegosouzapw/OmniRoute/pull/11520)) — thanks @HouMinXi +- **fix(kimi-web):** align health probe with executor domain ([#11521](https://github.com/diegosouzapw/OmniRoute/pull/11521)) — thanks @pacocartones +- **fix(cli):** preserve existing admin password during setup ([#11522](https://github.com/diegosouzapw/OmniRoute/pull/11522)) — thanks @pacocartones +- **fix(mitm):** keep Windows DNS elevation off the POSIX sudo path (#11430) ([#11542](https://github.com/diegosouzapw/OmniRoute/pull/11542)) — thanks @official-burak +- **fix(sse):** scale chat admission by ingest byte budget instead of a fixed request count ([#11548](https://github.com/diegosouzapw/OmniRoute/pull/11548)) — thanks @TheDemonTuan +- **fix:** accept Gemini Web cookie exports ([#11554](https://github.com/diegosouzapw/OmniRoute/pull/11554)) — thanks @rafacpti23 +- **fix(i18n):** complete Brazilian Portuguese translations ([#11558](https://github.com/diegosouzapw/OmniRoute/pull/11558)) — thanks @rafacpti23 +- **fix(sse):** explicit types for openai-responses pureHelpers — clears last failing core typecheck gate ([#11567](https://github.com/diegosouzapw/OmniRoute/pull/11567)) — thanks @jonlwheat2-gif +- **fix(opencode):** hydrate Proxy Pool references for no-auth connections ([#11584](https://github.com/diegosouzapw/OmniRoute/pull/11584)) — thanks @AStupidBear +- **fix(ci):** resolve inherited release/v3.8.51 base-red CI failures ([#11608](https://github.com/diegosouzapw/OmniRoute/pull/11608)) — thanks @jonlwheat2-gif +- **fix(db):** fallback load STORAGE_ENCRYPTION_KEY from env files ([#11614](https://github.com/diegosouzapw/OmniRoute/pull/11614)) — thanks @benzntech +- **fix(providers):** mark Brave Search as no longer having a free tier ([#11621](https://github.com/diegosouzapw/OmniRoute/pull/11621)) — thanks @turbolego +- **fix:** pass tool result content through as string in OpenAI/Claude -> Gemini translation ([#11624](https://github.com/diegosouzapw/OmniRoute/pull/11624)) — thanks @Hsia97 +- **fix(sse):** scope credential health to exact key ([#11628](https://github.com/diegosouzapw/OmniRoute/pull/11628)) — thanks @KaspaPulse +- **fix(install):** guard prepare script against missing husky ([#11634](https://github.com/diegosouzapw/OmniRoute/pull/11634)) — thanks @Prajeeth-12 +- **fix(security):** redact NVIDIA_API_KEY at the log sink in the ad-hoc diag script ([#11639](https://github.com/diegosouzapw/OmniRoute/pull/11639)) +- **fix(types):** clear the two type regressions red on release/v3.8.51 ([#11640](https://github.com/diegosouzapw/OmniRoute/pull/11640)) — thanks @jonlwheat2-gif +- **fix(build):** resolve workspace: protocol dependencies to concrete semver versions during prepublish (#11540) ([#11641](https://github.com/diegosouzapw/OmniRoute/pull/11641)) — thanks @HouMinXi +- **fix(search):** properly resolve configured search connection in /v1/responses pipeline (#11524) ([#11642](https://github.com/diegosouzapw/OmniRoute/pull/11642)) — thanks @HouMinXi +- **fix(authz):** trust cf-connecting-ip for via-proxy detection when peer is Cloudflare edge (#11514) ([#11643](https://github.com/diegosouzapw/OmniRoute/pull/11643)) — thanks @HouMinXi +- **fix(security):** compare the OIDC state cookie in constant time ([#11646](https://github.com/diegosouzapw/OmniRoute/pull/11646)) +- **fix(security):** namespace the dedup hash by the calling API key ([#11649](https://github.com/diegosouzapw/OmniRoute/pull/11649)) +- **fix(models):** gate the Codex-native catalog loop on prefix mode (#11632) ([#11662](https://github.com/diegosouzapw/OmniRoute/pull/11662)) — thanks @xiaoyaner0201 +- **fix(dashboard):** project Codex routing quota in account pools ([#11666](https://github.com/diegosouzapw/OmniRoute/pull/11666)) — thanks @KaspaPulse +- **fix(ddgw):** normaliza system no upstream da release v3.8.51 ([#11692](https://github.com/diegosouzapw/OmniRoute/pull/11692)) — thanks @RhianB14 +- **fix(memory):** honor category filter in GET /api/memory ([#11699](https://github.com/diegosouzapw/OmniRoute/pull/11699)) — thanks @Sabeekhann +- **fix(ci):** detect stale workspace lockfile entries ([#11714](https://github.com/diegosouzapw/OmniRoute/pull/11714)) — thanks @pacocartones +- **fix(ci):** stamp dist/BUILD_SHA before the npm publish provenance gate ([#11721](https://github.com/diegosouzapw/OmniRoute/pull/11721)) +- **fix(docker):** decouple the best-effort Bun image from the release manifest ([#11724](https://github.com/diegosouzapw/OmniRoute/pull/11724)) +- **perf(compression):** avoid cloning memo misses twice ([#11727](https://github.com/diegosouzapw/OmniRoute/pull/11727)) — thanks @RaviTharuma +- **fix(docker):** let the bun digest artifact be absent, not fatal ([#11740](https://github.com/diegosouzapw/OmniRoute/pull/11740)) +- **fix(shared):** block cloud-metadata hosts under default remote-image guard ([#11755](https://github.com/diegosouzapw/OmniRoute/pull/11755)) — thanks @santosraju99-hub +- **fix(api):** preserve caller-provided X-Correlation-Id on chat completions ([#11760](https://github.com/diegosouzapw/OmniRoute/pull/11760)) — thanks @aniruddhaadak80 +- **fix(resilience):** lock permanently retired models instead of short backoff (Gemini ban prevention) ([#11762](https://github.com/diegosouzapw/OmniRoute/pull/11762)) — thanks @turbolego +- **fix:** mark Vercel AI Gateway as passthroughModels ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **fix(resilience):** stop hammering permanently-moved endpoints and billing-suspended accounts ([#11774](https://github.com/diegosouzapw/OmniRoute/pull/11774)) — thanks @turbolego +- **fix(lease):** remove global static reservation and gate routing on live active lease occupancy ([#11775](https://github.com/diegosouzapw/OmniRoute/pull/11775)) — thanks @TheDemonTuan +- **fix(ci):** size the install-upgrade gate to a measured run, and log the pack cost ([#11776](https://github.com/diegosouzapw/OmniRoute/pull/11776)) +- **fix(pwa):** stop serving the stale shell after deploys ([#11779](https://github.com/diegosouzapw/OmniRoute/pull/11779)) — thanks @HouMinXi +- **fix(combo):** lock GitHub models rejected as "not supported" for future requests ([#11781](https://github.com/diegosouzapw/OmniRoute/pull/11781)) — thanks @turbolego +- **fix(cli):** probe both IPv4 and IPv6 loopback for server readiness (#11766) ([#11794](https://github.com/diegosouzapw/OmniRoute/pull/11794)) — thanks @vermasomesh835 +- **fix(sse):** preserve CLIProxyAPI wrappers per connection ([#11797](https://github.com/diegosouzapw/OmniRoute/pull/11797)) — thanks @RaviTharuma +- **fix(providers):** honor model auto-fetch opt-in on create ([#11798](https://github.com/diegosouzapw/OmniRoute/pull/11798)) — thanks @RaviTharuma +- **fix(dashboard):** make model auto-fetch order independent ([#11805](https://github.com/diegosouzapw/OmniRoute/pull/11805)) — thanks @RaviTharuma +- **fix(docker):** make GHA cache exports best effort ([#11806](https://github.com/diegosouzapw/OmniRoute/pull/11806)) — thanks @RaviTharuma +- **fix(kiro):** do not permanently ban on 'User is not authorized to make this call' ([#11809](https://github.com/diegosouzapw/OmniRoute/pull/11809)) — thanks @Deftera186 +- **fix(sse):** keep cache-write tokens in OpenAI-shaped usage ([#11814](https://github.com/diegosouzapw/OmniRoute/pull/11814)) — thanks @TheDemonTuan +- **fix(docs):** resolve relative markdown and wiki links across Fumadocs and GitHub wiki ([#11834](https://github.com/diegosouzapw/OmniRoute/pull/11834)) — thanks @AndrianBalanescu +- **fix(chat):** preserve unstripped model string for passthrough provider routing ([#11840](https://github.com/diegosouzapw/OmniRoute/pull/11840)) — thanks @raheemuddin786 +- **fix(mcp):** avoid async initialization inside sync esm wrappers (#11569) ([#11849](https://github.com/diegosouzapw/OmniRoute/pull/11849)) — thanks @HouMinXi +- **fix(sse):** preserve coherent markdown token boundaries across stream translation chunks (#11606) ([#11851](https://github.com/diegosouzapw/OmniRoute/pull/11851)) — thanks @HouMinXi +- **fix(ci):** run the install-upgrade gate on disk, not on the /tmp tmpfs ([#11855](https://github.com/diegosouzapw/OmniRoute/pull/11855)) +- **fix(skills):** expand shorthand property types in injected tool schemas ([#11857](https://github.com/diegosouzapw/OmniRoute/pull/11857)) — thanks @alvinveroy +- **fix(ci):** publish npm from a hosted runner so provenance is accepted ([#11877](https://github.com/diegosouzapw/OmniRoute/pull/11877)) +- **fix(lint):** pin react hooks plugin to 7.0.1 ([#11886](https://github.com/diegosouzapw/OmniRoute/pull/11886)) +- **fix(ci):** surface and clear release-green ESLint failure ([#11890](https://github.com/diegosouzapw/OmniRoute/pull/11890)) — thanks @MumuTW +- **fix(guardrails):** prevent duplicate prompt-injection-guard log output ([#11936](https://github.com/diegosouzapw/OmniRoute/pull/11936)) — thanks @patrykkopycinski +- **fix(auth):** downgrade expected transient states from warn to debug ([#11937](https://github.com/diegosouzapw/OmniRoute/pull/11937)) — thanks @patrykkopycinski +- **fix(codex):** fail over image generation for imported free plans ([#11948](https://github.com/diegosouzapw/OmniRoute/pull/11948)) — thanks @Bl0ck154 +- **fix(codex):** normalize non-stream responses ([#11951](https://github.com/diegosouzapw/OmniRoute/pull/11951)) — thanks @Bl0ck154 +- **fix(images):** forward Antigravity image size ([#11952](https://github.com/diegosouzapw/OmniRoute/pull/11952)) — thanks @Bl0ck154 +- **fix(db):** invalidate connection cache after upsert ([#11953](https://github.com/diegosouzapw/OmniRoute/pull/11953)) — thanks @Bl0ck154 +- **fix(codex):** restore imported account state ([#11954](https://github.com/diegosouzapw/OmniRoute/pull/11954)) — thanks @Bl0ck154 +- **fix(sse):** give extended-thinking targets the reasoning readiness budget ([#11959](https://github.com/diegosouzapw/OmniRoute/pull/11959)) — thanks @abhisheksharma2411 +- **fix(dashboard):** use opaque background and readable text color on cost chart tooltips ([#11960](https://github.com/diegosouzapw/OmniRoute/pull/11960)) — thanks @ZaimMarzuki +- **fix(antigravity):** send complete loadCodeAssist metadata (ideType/platform/pluginType as numeric enums) ([#11969](https://github.com/diegosouzapw/OmniRoute/pull/11969)) — thanks @rifqiawl +- **fix(core):** resolve DB init race condition and reasoning translation ([#12003](https://github.com/diegosouzapw/OmniRoute/pull/12003)) — thanks @raheemuddin786 +- **fix(admission):** exclude reclaimable page cache from the cgroup pressure ratio ([#12017](https://github.com/diegosouzapw/OmniRoute/pull/12017)) — thanks @HouMinXi +- **fix(build):** prune non-production trees in NFT trace excludes and tsconfig ([#12028](https://github.com/diegosouzapw/OmniRoute/pull/12028)) — thanks @Chewji9875 +- **fix(migrations):** add renamed migration compatibility for 056/073/077/101 ([#12036](https://github.com/diegosouzapw/OmniRoute/pull/12036)) — thanks @oyi77 +- **fix(sse):** default crash-guard logger to console.warn, not console ([#12042](https://github.com/diegosouzapw/OmniRoute/pull/12042)) — thanks @HouMinXi +- **fix(catalog):** derive vision/modalities for built-in auto combos from effective target pool ([#12046](https://github.com/diegosouzapw/OmniRoute/pull/12046)) — thanks @Prajeeth-12 +- **fix(release):** the packaged-app smoke verifies the database opened, not a driver line the primary path never prints (twin of #12032) ([#12047](https://github.com/diegosouzapw/OmniRoute/pull/12047)) +- **fix(barrel):** migrate src/app/ off the @/lib/localDb barrel import (#11795 Phase 2) ([#12051](https://github.com/diegosouzapw/OmniRoute/pull/12051)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate src/lib/ off the localDb barrel to direct db imports (#59) ([#12052](https://github.com/diegosouzapw/OmniRoute/pull/12052)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate open-sse, src/shared, src/sse, src/models, src/domain off the @/lib/localDb barrel import (#11795 Phase 4) ([#12053](https://github.com/diegosouzapw/OmniRoute/pull/12053)) — thanks @jonlwheat2-gif +- **fix(barrel):** delete the @/lib/localDb barrel — every consumer migrated (#11795 Phase 5) ([#12055](https://github.com/diegosouzapw/OmniRoute/pull/12055)) — thanks @jonlwheat2-gif +- **fix(radar):** restore D12 public boundary ([#12057](https://github.com/diegosouzapw/OmniRoute/pull/12057)) +- **fix(dev):** bound webpack and Tailwind scans ([#12075](https://github.com/diegosouzapw/OmniRoute/pull/12075)) — thanks @backryun +- **fix(dev):** isolate root layout settings reads ([#12076](https://github.com/diegosouzapw/OmniRoute/pull/12076)) — thanks @backryun +- **fix(dev):** reduce instrumentation executor fan-out (phase 3) ([#12078](https://github.com/diegosouzapw/OmniRoute/pull/12078)) — thanks @backryun +- **fix(dev):** make logging resources HMR-singleton ([#12079](https://github.com/diegosouzapw/OmniRoute/pull/12079)) — thanks @backryun +- **fix(sse):** keep unavailable forced connections scoped ([#12080](https://github.com/diegosouzapw/OmniRoute/pull/12080)) — thanks @keeltrace +- **fix(dev):** isolate batch dispatch from instrumentation ([#12081](https://github.com/diegosouzapw/OmniRoute/pull/12081)) — thanks @backryun +- **fix(ci):** reconcile release test contract drift ([#12082](https://github.com/diegosouzapw/OmniRoute/pull/12082)) — thanks @backryun +- **fix(ci):** accept CVE-2025-68121 in the prebuilt tls-client .so, auto-close base-red issues, guard Scorecard on the default branch ([#12085](https://github.com/diegosouzapw/OmniRoute/pull/12085)) +- **fix(db):** drop three consumer-less 1proxy exports — dead-code base-red on release/v3.8.51 after the barrel deletion (#12055) ([#12087](https://github.com/diegosouzapw/OmniRoute/pull/12087)) +- **fix(api):** clear the six API-route TypeScript regressions the new gate landed red on ([#12094](https://github.com/diegosouzapw/OmniRoute/pull/12094)) +- **fix(release):** never let the tag-push Create Release append auto notes to the curated body ([#12096](https://github.com/diegosouzapw/OmniRoute/pull/12096)) +- **fix(sse):** honor CLIProxyAPI environment API key ([#12099](https://github.com/diegosouzapw/OmniRoute/pull/12099)) — thanks @RaviTharuma +- **fix(oauth):** bind Google refresh to the client that issued the token ([#12106](https://github.com/diegosouzapw/OmniRoute/pull/12106)) — thanks @HouMinXi +- **fix(dashboard):** make RequestLoggerDetail loadable outside Next — CSS via globals.css + CJS/ESM interop (#11703 base-reds) ([#12114](https://github.com/diegosouzapw/OmniRoute/pull/12114)) +- **fix(leases):** project status lease row to lease columns so joined connection PII never escapes ([#12115](https://github.com/diegosouzapw/OmniRoute/pull/12115)) — thanks @geek007git +- **fix(plugins):** do not kill the plugin process when a fire-and-forget hook times out ([#12116](https://github.com/diegosouzapw/OmniRoute/pull/12116)) — thanks @geek007git +- **fix(guardrails):** restore injection-guard logging on middleware-only routes ([#12117](https://github.com/diegosouzapw/OmniRoute/pull/12117)) — thanks @geek007git +- **fix(plugins):** refresh stored manifest from disk on activate so new hook fields reach existing installs ([#12120](https://github.com/diegosouzapw/OmniRoute/pull/12120)) — thanks @geek007git +- **fix(codex):** preserve existing provider state when bulk-import upserts a matching connection ([#12122](https://github.com/diegosouzapw/OmniRoute/pull/12122)) — thanks @geek007git +- **fix(ci):** clear the two base-reds the 2026-08-30 merge batch left on release/v3.8.51 (round 3) ([#12123](https://github.com/diegosouzapw/OmniRoute/pull/12123)) +- **fix(sse):** strip type:'custom' from Claude tools on agentrouter dispatch ([#12126](https://github.com/diegosouzapw/OmniRoute/pull/12126)) — thanks @wahidsadik371-coder +- **fix(sse):** stop advertising video providers the dispatcher cannot run ([#12131](https://github.com/diegosouzapw/OmniRoute/pull/12131)) — thanks @quiterunner-commits +- **fix(search):** treat HTTP 432 and plan limit errors as transient cooldown ([#12139](https://github.com/diegosouzapw/OmniRoute/pull/12139)) — thanks @HouMinXi +- **fix(sse):** refuse an AI Horde queue that cannot fit the request budget ([#12143](https://github.com/diegosouzapw/OmniRoute/pull/12143)) — thanks @quiterunner-commits +- **fix(ci):** clear the base-reds the afternoon merge batch left on release/v3.8.51 (round 5: provider count 352, TS2554/TS2677) ([#12144](https://github.com/diegosouzapw/OmniRoute/pull/12144)) +- **fix(translator):** strip neutral tool_choice when tools absent in Responses-to-Chat (#12141) ([#12166](https://github.com/diegosouzapw/OmniRoute/pull/12166)) — thanks @HouMinXi +- **fix(guardrails):** pass providerId to getResolvedModelCapabilities in checkComboVision (#12112) ([#12169](https://github.com/diegosouzapw/OmniRoute/pull/12169)) — thanks @HouMinXi +- **fix(translator):** strip plaintext reasoning content for opaque responses backends (#12128) ([#12171](https://github.com/diegosouzapw/OmniRoute/pull/12171)) — thanks @HouMinXi +- **perf(stream):** compile hot-path regexes once, bound token caches, fix quadratic buffering ([#12179](https://github.com/diegosouzapw/OmniRoute/pull/12179)) — thanks @opensource-elearning +- **fix:** resolve compression worker file using runtime anchors instead of… ([#12183](https://github.com/diegosouzapw/OmniRoute/pull/12183)) — thanks @NightStalker-87 +- **fix:** prevent Claude Code session kills via liveness-aware readiness + auto model echo ([#12189](https://github.com/diegosouzapw/OmniRoute/pull/12189)) — thanks @opensource-elearning +- **fix(translator):** preserve falsy primitive values in Gemini and Antigravity function response results ([#12191](https://github.com/diegosouzapw/OmniRoute/pull/12191)) — thanks @Siva010 +- **fix(oauth):** mark empty Antigravity projectId as degraded and clear stale errors (#11284) ([#12205](https://github.com/diegosouzapw/OmniRoute/pull/12205)) — thanks @HouMinXi +- **fix(providers):** gate the Codex auto-ping usage read on the shared quota throttle ([#12209](https://github.com/diegosouzapw/OmniRoute/pull/12209)) — thanks @abhisheksharma2411 +- **fix(combos):** prioritize SQLite row id over inner JSON id and notify delete errors ([#12213](https://github.com/diegosouzapw/OmniRoute/pull/12213)) — thanks @HouMinXi +- **fix(providers):** mark featherless.ai as no longer having a free tier (`hasFree: false`) ([#12216](https://github.com/diegosouzapw/OmniRoute/pull/12216)) — thanks @turbolego +- **fix(deepseek-web):** stop Turbopack dev panic in the PoW worker path resolver ([#12221](https://github.com/diegosouzapw/OmniRoute/pull/12221)) — thanks @hartmark +- **fix(chat-admission):** clarify local 503 source ([#12223](https://github.com/diegosouzapw/OmniRoute/pull/12223)) — thanks @keeltrace +- **fix(dev):** silence webpack runtime module warnings ([#12228](https://github.com/diegosouzapw/OmniRoute/pull/12228)) — thanks @backryun +- **fix(db):** use module.require for CommonJS runtime driver loading ([#12230](https://github.com/diegosouzapw/OmniRoute/pull/12230)) — thanks @raheemuddin786 +- **fix(memory):** honest probe-driven FTS5 keyword status + memory_id rowid sync ([#12231](https://github.com/diegosouzapw/OmniRoute/pull/12231)) — thanks @raheemuddin786 +- **fix(system):** propagate abort signal to stream reader in HTTP version checks ([#12232](https://github.com/diegosouzapw/OmniRoute/pull/12232)) — thanks @raheemuddin786 +- **fix(resilience):** isolate local host execution errors from provider circuit breakers ([#12233](https://github.com/diegosouzapw/OmniRoute/pull/12233)) — thanks @raheemuddin786 +- **fix(sse):** sort injected tools deterministically for prompt caching ([#12234](https://github.com/diegosouzapw/OmniRoute/pull/12234)) — thanks @raheemuddin786 +- **fix(combo):** always clear the loop-safety timer, not just on the happy path (#11804) ([#12245](https://github.com/diegosouzapw/OmniRoute/pull/12245)) +- **fix(quality):** base-red pair — stryker tap registration + turn-pin suites aligned to the window gate ([#12255](https://github.com/diegosouzapw/OmniRoute/pull/12255)) — thanks @backryun +- **fix(dev):** qualify Turbopack runtime boundaries ([#12258](https://github.com/diegosouzapw/OmniRoute/pull/12258)) — thanks @backryun +- **fix(combo):** fall back to full pool when collapsed sole survivor is context-too-small ([#12278](https://github.com/diegosouzapw/OmniRoute/pull/12278)) — thanks @opensource-elearning +- **fix(combo):** bound the pre-dispatch unavailable skip so a stale label cannot dark a pool (#12168) ([#12285](https://github.com/diegosouzapw/OmniRoute/pull/12285)) +- **fix(security):** strip Qwen/Alibaba console-session cookies from provider API responses ([#12287](https://github.com/diegosouzapw/OmniRoute/pull/12287)) +- **fix(usage):** console-aware Token Plan guidance + subscription hint on bailian 401 ([#12288](https://github.com/diegosouzapw/OmniRoute/pull/12288)) +- **fix(resourcePressure):** log numeric detail on every rejection, recover faster ([#12293](https://github.com/diegosouzapw/OmniRoute/pull/12293)) — thanks @hartmark +- **fix(release):** drain the 2026-09-01 base-red window — passthrough usage regression + radarPage i18n keys ([#12327](https://github.com/diegosouzapw/OmniRoute/pull/12327)) +- **fix(ci):** openapi-security-tiers checker must honor routeGuard patterns + imported prefixes ([#12350](https://github.com/diegosouzapw/OmniRoute/pull/12350)) — thanks @ggiak +- **perf(ci):** cache node_modules in the npm-ci-retry composite (#8084 D3) ([#12408](https://github.com/diegosouzapw/OmniRoute/pull/12408)) +- **revert(ui):** point CTAs back at their real destinations ([#12410](https://github.com/diegosouzapw/OmniRoute/pull/12410)) +- **fix(release):** drain the 2026-09-02 base-red — rerank-providers import + api-typecheck baseline ratchet ([#12414](https://github.com/diegosouzapw/OmniRoute/pull/12414)) +- **fix(types):** make system prompt injection noImplicitAny-safe ([#12416](https://github.com/diegosouzapw/OmniRoute/pull/12416)) +- **fix(quality):** validate typecheck baseline schema ([#12419](https://github.com/diegosouzapw/OmniRoute/pull/12419)) +- **fix(memory):** resolve rerank provider node cache import ([#12421](https://github.com/diegosouzapw/OmniRoute/pull/12421)) +- **fix(ci):** repair release-root regressions — pack dedup, web-session syntax, uc-image ids ([#12423](https://github.com/diegosouzapw/OmniRoute/pull/12423)) — thanks @backryun +- **fix(providers):** separate MaxAI and UC credential contracts ([#12431](https://github.com/diegosouzapw/OmniRoute/pull/12431)) +- **fix(cli):** preserve Claude settings on config set ([#12432](https://github.com/diegosouzapw/OmniRoute/pull/12432)) — thanks @KrzysiekSko +- **fix(providers):** repair the maxai credential block truncated by merge auto-resolve ([#12433](https://github.com/diegosouzapw/OmniRoute/pull/12433)) +- **fix(quality):** record the 2026-09-02 merged growth in the file-size baseline ([#12434](https://github.com/diegosouzapw/OmniRoute/pull/12434)) +- **fix(uc):** route UC error strings through sanitizeErrorMessage; allowlist the retired codex id ([#12437](https://github.com/diegosouzapw/OmniRoute/pull/12437)) +- **fix(adapta):** redact streamed upstream errors ([#12438](https://github.com/diegosouzapw/OmniRoute/pull/12438)) +- **fix(sse):** remap non-contiguous upstream tool_calls index to a gap-free output_index ([#12445](https://github.com/diegosouzapw/OmniRoute/pull/12445)) — thanks @hartmark +- **fix(quality):** detect duplicate tool_calls entries in one response ([#12446](https://github.com/diegosouzapw/OmniRoute/pull/12446)) — thanks @hartmark +- **fix(conversations):** resolve turn content OmniRoute never sends back to the client ([#12447](https://github.com/diegosouzapw/OmniRoute/pull/12447)) — thanks @hartmark +- **fix(combo):** do not treat credits-exhausted 401 as auth skip ([#12449](https://github.com/diegosouzapw/OmniRoute/pull/12449)) — thanks @RaviTharuma +- **fix(auth):** do not park healthy quota accounts as expired ([#12452](https://github.com/diegosouzapw/OmniRoute/pull/12452)) — thanks @RaviTharuma +- **fix(responses-continuation):** fail closed on a collector-truncated, empty output array ([#12460](https://github.com/diegosouzapw/OmniRoute/pull/12460)) — thanks @hartmark +- **fix(logging):** raise the SSE payload collector's default cap ([#12461](https://github.com/diegosouzapw/OmniRoute/pull/12461)) — thanks @hartmark +- **fix(resilience):** surface Responses failed.error.message in 502s ([#12472](https://github.com/diegosouzapw/OmniRoute/pull/12472)) — thanks @RaviTharuma +- **fix(security):** unbiased maxai X-Random nonce + stricter URL/regex assertions ([#12502](https://github.com/diegosouzapw/OmniRoute/pull/12502)) +- **fix(video):** re-anchor transcript log-redaction so PII/credential maskers can't reopen the leak (#12150 P1 follow-up) ([#12503](https://github.com/diegosouzapw/OmniRoute/pull/12503)) +- **fix(monitoring):** serve cached credentialHealth off the request path ([#12533](https://github.com/diegosouzapw/OmniRoute/pull/12533)) — thanks @RaviTharuma +- **fix(quota):** keep Antigravity Gemini usable when Claude weekly is empty ([#12566](https://github.com/diegosouzapw/OmniRoute/pull/12566)) — thanks @HouMinXi +- **fix(authz):** hard-gate every credential export and CLI-config write (GHSA-5926-2w35-7h4q) ([#12600](https://github.com/diegosouzapw/OmniRoute/pull/12600)) +- **fix(docs):** restore the Next build — REMOVED_PROVIDERS.md had no frontmatter (base-red #12581) ([#12610](https://github.com/diegosouzapw/OmniRoute/pull/12610)) +- **fix(quality):** re-point the zcodeProtocol public-creds allowlist to line 313 ([#12615](https://github.com/diegosouzapw/OmniRoute/pull/12615)) +- **fix(security):** close 3 advisories — search baseUrl exfil, sk- in the error sanitizer, bifrost relay header leak ([#12620](https://github.com/diegosouzapw/OmniRoute/pull/12620)) +- **fix(providers):** sanitize boolean required and nested bare maps for Gemini (#12269) ([#12624](https://github.com/diegosouzapw/OmniRoute/pull/12624)) — thanks @HouMinXi +- **fix(resilience):** keep Overloaded STREAM_EARLY_EOF off the provider breaker ([#12626](https://github.com/diegosouzapw/OmniRoute/pull/12626)) — thanks @HouMinXi +- **fix(catalog):** re-audit free-tier quotas against official pages ([#12649](https://github.com/diegosouzapw/OmniRoute/pull/12649)) +- **fix(dashboard):** keep a request's pending-tracking id stable across combo target retries ([#12650](https://github.com/diegosouzapw/OmniRoute/pull/12650)) — thanks @hartmark +- **fix(tests):** retire dead model ids from the chat-pipeline integration suite (base-red #12581) ([#12670](https://github.com/diegosouzapw/OmniRoute/pull/12670)) +- **fix:** resolve SqliteError no such table compression_run_telemetry during cleanup ([#12682](https://github.com/diegosouzapw/OmniRoute/pull/12682)) — thanks @groovecityJO +- **fix(combo):** treat a pin-only step as implicit connection allowlist ([#12697](https://github.com/diegosouzapw/OmniRoute/pull/12697)) — thanks @HouMinXi +- **fix(ci):** ship .npmrc in published package so legacy-peer-deps applies to consumers (#11544) ([#12699](https://github.com/diegosouzapw/OmniRoute/pull/12699)) — thanks @KooshaPari +- **fix(docker):** pin 4 CLI tools to exact versions (#12576) ([#12703](https://github.com/diegosouzapw/OmniRoute/pull/12703)) — thanks @KooshaPari +- **fix(docker):** document and harden cli profile trust boundary (#12570) ([#12706](https://github.com/diegosouzapw/OmniRoute/pull/12706)) — thanks @KooshaPari +- **fix(sse):** redact video transcript in pre-guardrail rejected-request logs (#12150 P2 item 7) ([#12710](https://github.com/diegosouzapw/OmniRoute/pull/12710)) +- **fix(i18n):** quote placeholder in OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES description (#12505) ([#12769](https://github.com/diegosouzapw/OmniRoute/pull/12769)) — thanks @KooshaPari +- **fix(api/pricing):** surface validation error message as string, not raw object (#12494) ([#12771](https://github.com/diegosouzapw/OmniRoute/pull/12771)) — thanks @KooshaPari +- **fix(sse):** re-enable prompt compression for native Codex passthrough ([#12834](https://github.com/diegosouzapw/OmniRoute/pull/12834)) — thanks @thomasmaerz +- **fix(dashboard):** model health tests for a provider node set to the Responses API now call `/v1/responses` with a Responses-shaped body instead of `/v1/chat/completions` — those models were reported as `Provider returned HTTP 200 but no text content` even though the same model answered normally through `/v1/responses` ([#13070](https://github.com/diegosouzapw/OmniRoute/issues/13070)) ([#13087](https://github.com/diegosouzapw/OmniRoute/pull/13087)) — thanks @ntdatt812 +- fix(routing): stop a round-robin combo's "opencode" targets from collapsing onto the opencode-zen connection (#11912) ([#13283](https://github.com/diegosouzapw/OmniRoute/pull/13283)) +- fix(routing): stop the reactive-compaction debug log from lying when compression is globally disabled (#11977) ([#13276](https://github.com/diegosouzapw/OmniRoute/pull/13276)) +- fix(electron): relativize standalone-bundle symlink targets so Stage 8 manifest verification stops failing on Windows (#11979) ([#13251](https://github.com/diegosouzapw/OmniRoute/pull/13251)) +- fix(dashboard): surface a visible error when Compression Studio's combined preview run fails (#12061) ([#13288](https://github.com/diegosouzapw/OmniRoute/pull/13288)) +- fix(dashboard): make the compression "Effective pipeline" preview honor the active profile and warn when the master switch is off (#12063) ([#13287](https://github.com/diegosouzapw/OmniRoute/pull/13287)) +- fix(providers): scope TinyCMS Web signer's DOM shims to each call instead of leaking them for the process lifetime, and surface a clean HTTP status on a non-JSON interception-toggles error (#12072) ([#13254](https://github.com/diegosouzapw/OmniRoute/pull/13254)) +- fix(guardrails): stop Vision Bridge from re-selecting a model locked after a 404 (#12111) ([#13259](https://github.com/diegosouzapw/OmniRoute/pull/13259)) +- fix(sse): require Responses-shaped body before native OpenAI-compatible passthrough (#12129) ([#13278](https://github.com/diegosouzapw/OmniRoute/pull/13278)) +- fix(providers): minimax-m3 now collapses manual thinking.type:"enabled" to adaptive, preventing upstream 400 (2013) (#12132) ([#13249](https://github.com/diegosouzapw/OmniRoute/pull/13249)) +- fix(db): scope model visibility overrides by modality so hiding a Chat model no longer hides an identically-ID'd Image/Embeddings/etc. model (#12172) ([#13258](https://github.com/diegosouzapw/OmniRoute/pull/13258)) +- fix(db): scope local-provider apiKey dedup to matching base URL so LM Studio/Ollama-style connections support multiple accounts (#12173) ([#13250](https://github.com/diegosouzapw/OmniRoute/pull/13250)) +- fix(providers): refresh Trae's stale Referer/Origin and forward user timezone so imported connections stop failing with 401 (#12190) ([#13255](https://github.com/diegosouzapw/OmniRoute/pull/13255)) +- fix(providers): route opencode-go/gpt-5.6-luna to /responses instead of /chat/completions (#12196) ([#13275](https://github.com/diegosouzapw/OmniRoute/pull/13275)) +- fix(dashboard): allow deleting the last extra-upstream-header row even when invalid (#12251) ([#13252](https://github.com/diegosouzapw/OmniRoute/pull/13252)) +- **fix(i18n):** translate pre-existing `__MISSING__:` keys for `combo.sort`, `requestLogger.detail` expand/collapse, `common.profile`, and `settings.resilienceCredentialHealth*` across 39 locales ([#12272](https://github.com/diegosouzapw/OmniRoute/issues/12272)) ([#13034](https://github.com/diegosouzapw/OmniRoute/pull/13034)) — thanks @HouMinXi +- fix(cli): run the Node.js runtime compatibility guard before the heavy `tsx/esm` + Commander import chain so an unsupported runtime gets a clear message instead of a raw `Invalid regular expression flags` crash (#12296) ([#13253](https://github.com/diegosouzapw/OmniRoute/pull/13253)) +- fix(dashboard): refresh the providers list after deleting a compatible provider node (#12298) ([#13256](https://github.com/diegosouzapw/OmniRoute/pull/13256)) +- fix(usage): fail closed on API-key budget enforcement when a provider's `auto` routing alias has no pricing row, instead of silently counting it as $0 (#12341) ([#13257](https://github.com/diegosouzapw/OmniRoute/pull/13257)) +- **fix(providers):** include the submitted Agnes video model when polling by `video_id` ([#12356](https://github.com/diegosouzapw/OmniRoute/pull/12356)) — thanks @pacocartones +- **fix(routing):** custom OpenAI-compatible nodes now honor the saved Chat/Responses API type after edits instead of letting the node's original ID prefix override the live connection setting ([#11884](https://github.com/diegosouzapw/OmniRoute/issues/11884)). ([#12358](https://github.com/diegosouzapw/OmniRoute/pull/12358)) — thanks @pacocartones +- fix(sse): surface an error instead of a silent empty 200 when a Claude stream closes with zero bytes (#12398) ([#13285](https://github.com/diegosouzapw/OmniRoute/pull/13285)) +- fix(oauth): warn before the dead localhost:8080 redirect in antigravity/gemini `oauth start` (#12413) ([#13265](https://github.com/diegosouzapw/OmniRoute/pull/13265)) +- fix(providers): stop devin-cli spawn error from double-closing the SSE controller (#12517) ([#13261](https://github.com/diegosouzapw/OmniRoute/pull/13261)) +- **fix(rerank):** clamp Voyage `top_k` to the documents actually sent after empty-string filtering, and honor `return_documents: false` in the NVIDIA response adapter (#12523 — thanks @pacocartones) +- **fix(audio):** `/v1/audio/translations` now resolves combo names the way `/v1/audio/transcriptions` already does, so a combo that `GET /v1/models` advertises is fanned out to its targets instead of being rejected with `400 Invalid translation model: . Use format: provider/model`; literal `provider/model` ids and unknown bare names behave as before (#12536 — thanks @pacocartones) +- **fix(gemini):** strip the JSON-Schema-2020-12 `prefixItems` keyword from Gemini tool schemas at every nesting level, so Claude Code tool definitions no longer fail with `400 Unknown name "prefixItems"` on Gemini models (#12540 — thanks @pacocartones) +- **fix(api-manager):** Expose an accessible loading status while API keys are fetched instead of an empty accessibility tree (#12541 — thanks @pacocartones) +- **fix(video):** Clamp `estimateJpegFrameBytes` at zero for padding-only payloads and build the three encode-side frame data URIs from `JPEG_FRAME_DATA_URI_PREFIX` instead of a repeated literal (#12543 — thanks @pacocartones) +- **fix(devin):** accept Windows `DEVIN_AGENTIC_HOME` sandbox paths (`C:\...\.sandbox\...`) in the isolated-home check so the Devin Claude Bridge no longer fails closed on Windows ([#12405](https://github.com/diegosouzapw/OmniRoute/issues/12405)) (#12545 — thanks @pacocartones) +- **fix(db):** Add `conversation_turn_nodes` and orphaned `agentic_conversations` to the auto-cleanup cycle under the existing `retention.callLogs` window, so identity nodes whose call-log content has already been purged no longer accumulate without bound in `storage.sqlite` (#12548 — thanks @pacocartones) +- **fix(i18n):** Wrap the `~/.claude/profiles//settings.json` placeholder in ICU single quotes in the `OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES` feature-flag description across all 42 locales and the TypeScript default, so next-intl no longer fails with `INVALID_MESSAGE: UNCLOSED_TAG` and the Feature Flags card shows the description instead of the raw key (#12549 — thanks @pacocartones) +- **fix(orchestration):** `updateCloudAgentTask` now publishes the task's real `status` on `agent.task.updated` when an update only touches `result`, `activities` or `error`, instead of the fabricated `"updated"` state, and stays silent when no row matched the id (#12550 — thanks @pacocartones) +- **fix(i18n):** the home "Recent Requests" panel and the Provider Topology legend are now translated instead of rendering English copies on non-English dashboards; the legend reads its own `home.topologyLegend*` labels with consistent casing rather than borrowing the memory-settings "Recent" and analytics "Error" strings (#12551 — thanks @pacocartones). +- **docs(reference):** bring the `FEATURE_FLAGS.md` catalog back to 1:1 with `featureFlagDefinitions.ts` — 20 missing flags added, the two `*_BLOCK_THRESHOLD` env-only knobs moved out of the flag tables, category/total counts and the Live WS port corrected, guarded by a static test (#12552 — thanks @pacocartones) +- **fix(i18n):** backfill missing `usage.kiloPass*` strings in 39 locales and restore `featureFlagOmnirouteDisableThinkingLevelVariantsDescription` in `pt.json` ([#12561](https://github.com/diegosouzapw/OmniRoute/issues/12561)) ([#13027](https://github.com/diegosouzapw/OmniRoute/pull/13027)) — thanks @HouMinXi +- fix(docker): default docker-compose app ports (dashboard/API/live-WS) to loopback instead of `0.0.0.0`, closing the anonymous `/v1` LAN/WAN exposure gap left open by `REQUIRE_API_KEY=false` (#12568) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): close DNS-rebinding SSRF gap in webhook outbound-URL guard (#12569) ([#13243](https://github.com/diegosouzapw/OmniRoute/pull/13243)) +- fix(docker): require a per-session token on the VNC browser CDP bridge and isolate it on a dedicated Docker network (#12571) ([#13241](https://github.com/diegosouzapw/OmniRoute/pull/13241)) +- fix(open-sse): write Adobe Firefly session tokens and cookie jars with 0700/0600 permissions instead of the process umask (#12572) ([#13242](https://github.com/diegosouzapw/OmniRoute/pull/13242)) +- fix(api): remove hardcoded wildcard CORS in openai-to-gemini-sse.ts so the centralized fail-closed CORS gate is the sole source of `Access-Control-Allow-Origin` (#12573) ([#13239](https://github.com/diegosouzapw/OmniRoute/pull/13239)) +- fix(api): enforce API key policy (budget/rate-limit/schedule/endpoint scoping) on the ElevenLabs speech-to-text, text-to-speech and voices proxy routes (#12574) ([#13244](https://github.com/diegosouzapw/OmniRoute/pull/13244)) +- fix(sse): cap HuggingChat NDJSON body size and bound the read loop with the fetch timeout so a stalled or hostile upstream cannot buffer unbounded memory (#12577) ([#13282](https://github.com/diegosouzapw/OmniRoute/pull/13282)) +- fix(docker): scope the cliproxyapi/qdrant/bifrost sidecars to loopback by default and forward `CLIPROXYAPI_MANAGEMENT_KEY` into the cliproxyapi container so its management API is not left both unauthenticated and LAN-published (#12578) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): create DB export temp paths with `fs.mkdtempSync` instead of predictable timestamps (#12579) ([#13237](https://github.com/diegosouzapw/OmniRoute/pull/13237)) +- Cline 401 bodies that say "re-authenticate your Cline account" classify as a refreshable OAuth token, not a terminal expired key. The cooling panel no longer labels every cooldown as a 429; it shows the recorded last error instead. (#12594) ([#13060](https://github.com/diegosouzapw/OmniRoute/pull/13060)) — thanks @HouMinXi +- **fix(catalog):** degrade unknown combo targets instead of dropping LCD modalities, and persist OpenRouter `architecture.input_modalities` into the capability snapshot ([#12613](https://github.com/diegosouzapw/OmniRoute/issues/12613)) ([#13042](https://github.com/diegosouzapw/OmniRoute/pull/13042)) — thanks @HouMinXi +- fix(providers): send `x-api-key` instead of `Authorization: Bearer` for OpenCode Zen's `/v1/responses` endpoint (Muse Spark Contributor models), fixing a 401 on OmniRoute's auth header (#12633) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(sse): surface the actionable "Auggie CLI not found" message when the shell reports a missing `auggie` binary via exit code instead of a spawn error (#12645) ([#13263](https://github.com/diegosouzapw/OmniRoute/pull/13263)) +- **fix(config):** Persist deletions of built-in background-degradation entries — when a stored settings record exists its `degradationMap` is now authoritative instead of being merged under the defaults, so an entry the user removed in the dashboard no longer reappears on the next apply or restart ([#12424](https://github.com/diegosouzapw/OmniRoute/issues/12424)) ([#12647](https://github.com/diegosouzapw/OmniRoute/pull/12647)) — thanks @pacocartones +- **fix(gamification):** action-count badge milestones (First Token, Token Consumer, Token Machine, Token Whale, and the token-sharing tier) are now backed by a durable `xp_action_counts` counter incremented in `addXp()`, instead of a live `COUNT(*)` over `xp_audit_log`. The audit log is pruned by `retention.xpAuditLog` (default 30 days), so on a default install those "lifetime" milestones were really "actions in the last 30 days" and unlocked badges could stop unlocking once old rows aged out. `getActionCount()` and `checkActionCountBadges()` now read the same durable source, and a migration backfills existing totals from the surviving audit rows ([#12546](https://github.com/diegosouzapw/OmniRoute/issues/12546)) ([#12651](https://github.com/diegosouzapw/OmniRoute/pull/12651)) — thanks @pacocartones +- **fix(images):** `/v1/images/edits` now iterates a combo's targets the same way `/v1/images/generations` does (#9239) instead of flattening a bare combo to its first target. A combo whose first target is not edit-capable — or lacks credentials — now falls through to a later edit-capable target rather than hard-erroring, and missing credentials are skipped (not a hard `401`) to match the generations path. The per-target skip/terminal classification is extracted into a shared `runImageComboTargets` loop, so generations behavior is unchanged ([#12547](https://github.com/diegosouzapw/OmniRoute/issues/12547)). ([#12653](https://github.com/diegosouzapw/OmniRoute/pull/12653)) — thanks @pacocartones +- fix(sse): add first-byte watchdog to the TLS-fingerprint transport so a stalled wreq body falls back instead of hanging for minutes (#12656) ([#13272](https://github.com/diegosouzapw/OmniRoute/pull/13272)) +- fix(sse): exempt tiny-budget reasoning probes from combo quality failure and surface persisted-cooldown skips in ALL_TARGETS_SKIPPED diagnostics (#12659) ([#13269](https://github.com/diegosouzapw/OmniRoute/pull/13269)) +- fix(models): declare the real ~1M contextLength for OpenCode Zen's Muse Spark 1.2 models instead of falling back to the 200000 provider default (#12681) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(oauth): align codebuddy-cn OAuth User-Agent with the chat/usage CLI version to avoid WAF false positives (#12702) ([#13264](https://github.com/diegosouzapw/OmniRoute/pull/13264)) +- fix(dashboard): surface an authentication-required banner instead of silently blanking database settings for a guest session (#12709) ([#13270](https://github.com/diegosouzapw/OmniRoute/pull/13270)) +- **fix(resilience):** stop unbounded queue that hangs 6min until Aborted — gate, provider slot, and Bottleneck queue now share a per-connection `maxWaitMs` budget; `fail-closed` on exhaust (503); execution backstop `executionMaxWaitMs` overridable per connection with upstream clamp ([#12715](https://github.com/diegosouzapw/OmniRoute/pull/12715)) — thanks @maxmad64bis (with thanks to @Tushar49 for surfacing the slow-provider need in #12635) +- **fix(auto-combo):** every mode pack now carries `quality` and `reliability` weights (`quality` 0.02, 0.03 in `quality-first`; `reliability` 0.03, 0.04 in `reliability-first`), so selecting a pack no longer silences either signal; `DEFAULT_WEIGHTS` is unchanged ([#12731](https://github.com/diegosouzapw/OmniRoute/pull/12731)) — thanks @maxmad64bis +- **fix(sse):** the provider execution pipeline keeps the upstream error `code`/`type` again — both were lost when the non-streaming leg was extracted, so a config-class `422` (Antigravity missing project) degraded into a generic account cooldown instead of reaching the gate that recognises it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(db):** a legacy `call_logs` table no longer aborts startup — the provider-stats index is created after the column healing runs, not before it, so an install predating `request_type` opens instead of failing with `no such column` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(catalog):** a malformed row in the operator-writable custom-models blob no longer kills every `auto/*` pool ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(tests):** realign the guards that the same day's merges left asserting the old behavior — the provider-count total after GreenPT and EURouter, the GLM stream buffer slot that `#12925` turned into a declared parameter, and the injection scan bound that `#13104` reshaped from a head-only window into head-plus-tail ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- fix(cache): fold tool_choice/tools/response_format into the semantic cache signature so a cached tool_calls response can no longer be replayed for a request whose tool policy forbids it (#12734) ([#13267](https://github.com/diegosouzapw/OmniRoute/pull/13267)) +- **fix(models):** stop treating provider-supplied `isFree:true`/`:free`/`0/0` flags as trusted unless the provider has a documented free tier — only the shipped free-tier catalog decides otherwise; fetched `isFree:true` trusted only for a free-tier provider, custom `isFree:true` via a trusted path ([#12744](https://github.com/diegosouzapw/OmniRoute/pull/12744)) — thanks @maxmad64bis +- fix(memory): authenticate the internal /v1/rerank loopback call so memory reranking no longer silently degrades to unranked order when REQUIRE_API_KEY=true (#12745) ([#13268](https://github.com/diegosouzapw/OmniRoute/pull/13268)) +- fix(sse): parse Ollama Cloud's current usage markup (`$X of $Y used` aria-label, nested width style) (#12749) ([#13260](https://github.com/diegosouzapw/OmniRoute/pull/13260)) +- fix(cli): setup-opencode no longer sends an active context's management token to `/v1/models` when `--api-key`/`OMNIROUTE_API_KEY` is supplied — an explicit flag or the env var now always outranks the context's token, and the flag itself is no longer swallowed by the parent program's global `--api-key` option (#12783) ([#13246](https://github.com/diegosouzapw/OmniRoute/pull/13246)) +- fix(sse): register Arcee AI in the executor provider registry so requests reach api.arcee.ai instead of silently falling back to OpenAI (#12784) ([#13277](https://github.com/diegosouzapw/OmniRoute/pull/13277)) +- **fix(quota):** share one upstream quota read when identical saturation checks arrive at the same time, so traffic bursts don't multiply provider API calls ([#12787](https://github.com/diegosouzapw/OmniRoute/pull/12787)) — thanks @maxmad64bis +- **fix(db):** ignore expired or invalid rate-limit cooldown writes so a stale timestamp can't lock a connection that should be usable — clearing still works as before ([#12788](https://github.com/diegosouzapw/OmniRoute/pull/12788)) — thanks @maxmad64bis +- **fix(providers):** single source for provider order with `xao` ranking alongside `xai-oauth` ([#12790](https://github.com/diegosouzapw/OmniRoute/pull/12790)) — thanks @maxmad64bis +- **fix(routing):** off-table models check the free-model catalog before inheriting premium prices, latency bootstraps from the observed pool median, async tiers read live database pricing with a 90-day freshness gate, and the tier cache invalidates on every pricing write ([#12792](https://github.com/diegosouzapw/OmniRoute/pull/12792)) — thanks @maxmad64bis +- **fix(auto-combo):** snapshot scoring uses the observed per-connection account tier and per-model quality instead of neutral constants, and circuit-open providers rank lower at build time ([#12794](https://github.com/diegosouzapw/OmniRoute/pull/12794)) — thanks @maxmad64bis +- **fix(pool):** empty `auto/*` pools now say why they are empty, and the models listing uses the same paid check as routing ([#12795](https://github.com/diegosouzapw/OmniRoute/pull/12795)) — thanks @maxmad64bis +- fix(routing): recognize CLIProxyAPI's 'unknown provider for model' 400 as fallback-worthy (#12800) ([#13284](https://github.com/diegosouzapw/OmniRoute/pull/13284)) +- **fix(sse):** translate-mode streams now emit the estimated token counts as a trailing usage-only chunk before `[DONE]` when the upstream stays silent, so metered chat clients see totals instead of nothing ([#12828](https://github.com/diegosouzapw/OmniRoute/pull/12828)) — thanks @maxmad64bis +- **fix(db):** provider stats stay truthful on empty and legacy databases: fallback counts default to `0` instead of `null`, latency averages read `null` (not `0`) when no durations were recorded, failures logged before the error-type column existed group under `pre_migration` instead of `unclassified`, and per-provider queries use two new composite indexes ([#12832](https://github.com/diegosouzapw/OmniRoute/pull/12832)) — thanks @maxmad64bis +- fix(nvidia): fail open when a synced model catalog goes stale instead of gating forever (#12849) ([#13248](https://github.com/diegosouzapw/OmniRoute/pull/13248)) +- **fix(db):** WAL maintenance lives in its own module: a `TRUNCATE` checkpoint that hits a busy database now warns with its streak and retries once via `PASSIVE` instead of logging success, and closing-time checkpoints no longer report success on builds without a database file, with the busy totals visible in the authenticated monitoring health payload ([#12853](https://github.com/diegosouzapw/OmniRoute/pull/12853)) — thanks @maxmad64bis +- **fix(health):** quota with no snapshots reads empty instead of a contradicting 0% ([#12857](https://github.com/diegosouzapw/OmniRoute/pull/12857)) — thanks @maxmad64bis +- fix(a2a): accept the dashboard's own session cookie on /a2a so "Run message/send" no longer fails with "Unauthorized: missing or invalid API key" (#12888) ([#13271](https://github.com/diegosouzapw/OmniRoute/pull/13271)) +- **fix(logs):** the Logs grid's in-memory filter pass no longer discards rows the SQL query already matched — selecting an API key from the dropdown (which sends the key's id) returns its calls again, the Combo tab shows every combo instead of only those whose name contains a "1", and the model filter and search cover the same columns as the query ([#12896](https://github.com/diegosouzapw/OmniRoute/pull/12896)) — fixes [#12873](https://github.com/diegosouzapw/OmniRoute/issues/12873) — thanks @ntdatt812 +- **fix(a2a):** `/api/a2a/status` now builds the agent card from the request that asked for it, so a gateway reached at a non-localhost host no longer advertises `http://localhost:20128` as its A2A URL ([#12918](https://github.com/diegosouzapw/OmniRoute/pull/12918)). — thanks @ntdatt812 +- **fix(compression):** progressive aging now appends its `[COMPRESSED:aging:…]` annotation after a turn's `tool_result` blocks instead of in front of them, so Anthropic no longer rejects aged conversations with "`tool_use` ids were found without `tool_result` blocks immediately after" ([#12920](https://github.com/diegosouzapw/OmniRoute/pull/12920)). — thanks @ntdatt812 +- **fix(bedrock):** model import now resolves context limits for every vendor prefix instead of only `anthropic.*`, so `global.openai.gpt-5.6-*` no longer imports with a null `inputTokenLimit` and gets rejected pre-flight at the 200k default ([#12921](https://github.com/diegosouzapw/OmniRoute/pull/12921)). — thanks @ntdatt812 +- **fix(stream):** the 64 KB stream buffer GLM asks for is honoured instead of dropped, and the type error it caused no longer fails the API Route Typecheck gate on every open PR ([#12925](https://github.com/diegosouzapw/OmniRoute/pull/12925)) — thanks @ntdatt812 +- **fix(guardrails):** mask PII inside a `tool_result`'s nested content array, which the masker walked past while redacting its sibling block ([#12930](https://github.com/diegosouzapw/OmniRoute/pull/12930)) — thanks @ntdatt812 +- **fix(dashboard):** the Radar catalog table no longer leaves absent data unexplained — empty limits, unknown context windows, and unreported capabilities each explain themselves on hover, and a new check keeps it that way ([#12937](https://github.com/diegosouzapw/OmniRoute/pull/12937)) — thanks @maxmad64bis +- **fix(sse):** geo-blocked opencode requests rotate to the next account proxy instead of failing, so one refused egress no longer aborts the whole chain ([#12941](https://github.com/diegosouzapw/OmniRoute/pull/12941)) — thanks @maxmad64bis +- **fix(codex):** dashboard "clear cooldown" and the CAS recovery path now drop nested `codexScopeRateLimitedUntil` maps in the same write that nulls `rate_limited_until`, and fresh quota snapshots with headroom lift fallback-sourced scope cooldowns parked by quota preflight ([#12817](https://github.com/diegosouzapw/OmniRoute/issues/12817), [#12860](https://github.com/diegosouzapw/OmniRoute/issues/12860), [#12951](https://github.com/diegosouzapw/OmniRoute/pull/12951)) The dashboard reset-credit button (`consumeCodexResetCredit`) now exercises that same snapshot path after a successful redeem, so a filled quota bar is enough to unpark a leftover fallback Codex child without another manual clear. — thanks @HouMinXi +- **fix(db):** resolve `sql-wasm.wasm` across global npm install and hoisted layouts, ensuring OmniRoute can boot cleanly on Node 24 when native `better-sqlite3` is uncompiled. ([#13035](https://github.com/diegosouzapw/OmniRoute/pull/13035)) — thanks @HouMinXi +- **fix(api):** `DELETE /v1/batches/delete-completed` now sweeps only the calling API key's own completed batches (batches with no owner stay out of a key-scoped sweep on purpose), with an explicit instance-wide mode reserved for authenticated dashboard sessions, a 401 for a presented key that is unknown, revoked, deactivated, banned or expired (never falling through to the session branch), audit logging of every sweep, a sanitized 500 on failure, an owner-scoped file half (a key-scoped sweep never nulls a file another tenant owns) and an atomic sweep — chunked in 200-batch transactions in instance mode — so a mid-way error never leaves a batch pointing at a nulled file (GHSA-wvxc-jp3v-5mg5) ([#12969](https://github.com/diegosouzapw/OmniRoute/pull/12969)) +- **fix(combo):** quota-weighted routing stops drawing on an out-of-credit connection — a 402 now invalidates the stored quota snapshot instead of leaving its stale remaining percentage in place, and a snapshot older than 10 minutes no longer counts as confident headroom for the primary pool ([#12972](https://github.com/diegosouzapw/OmniRoute/pull/12972)) ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- **fix(vision):** Custom Models with "Vision capable" checked no longer have image requests swapped to `glm/glm-4.6v` when the client sends the advertised alias (`vllm/path/...`) or the bare path-shaped id — Vision Bridge now matches the stored override for all three id forms ([#12758](https://github.com/diegosouzapw/OmniRoute/issues/12758)) ([#12974](https://github.com/diegosouzapw/OmniRoute/pull/12974)) — thanks @HouMinXi +- **fix(sse):** transient opencode upstream failures rotate to the next account proxy instead of failing, so one flapping egress no longer aborts the whole chain ([#12975](https://github.com/diegosouzapw/OmniRoute/pull/12975)) — thanks @maxmad64bis +- **fix(azure):** Deployments from GPT-6 onward now send `max_completion_tokens` instead of `max_tokens`, which Azure rejects with HTTP 400. The rule matched a literal `gpt-5`, so each new generation arrived broken; it now matches the generation range, while `gpt-35-turbo` still keeps `max_tokens`. ([#13007](https://github.com/diegosouzapw/OmniRoute/pull/13007)) — thanks @ntdatt812 +- **fix(db):** add an opt-in self-restart circuit for sustained critical memory pressure, gate post-cleanup VACUUM behind a minimum freed-rows threshold, and checkpoint the SQLite WAL every 5 minutes with a size guard that escalates to TRUNCATE, so a growing WAL can no longer stall the event loop into a full outage. ([#13011](https://github.com/diegosouzapw/OmniRoute/pull/13011)) — thanks @HouMinXi +- **fix(auth):** an explicit connection pin may probe a quota-disabled row once and re-enable it on success ([#12874](https://github.com/diegosouzapw/OmniRoute/issues/12874)) ([#13017](https://github.com/diegosouzapw/OmniRoute/pull/13017)) — thanks @HouMinXi +- **fix(api):** thread `X-OmniRoute-Fallback-Attempts` through combo chat completions so streaming and non-streaming responses report how many prior legs were attempted ([#13038](https://github.com/diegosouzapw/OmniRoute/pull/13038)) — thanks @HouMinXi +- Restore provider failure classification and credential refresh on non-streaming requests: classify non-2xx failures to lock models on per-model quota exhaustion, update connection rate limits from headers and body, and pass credential refresh handlers to pipeline execution so 401 tokens can be refreshed and retried (#13043). ([#13069](https://github.com/diegosouzapw/OmniRoute/pull/13069)) — thanks @HouMinXi +- **fix(responses):** wrap forced-non-streaming web_search fallback JSON as Responses SSE so Codex still sees `response.completed` ([#13050](https://github.com/diegosouzapw/OmniRoute/pull/13050)) — thanks @HouMinXi +- **fix(antigravity):** Preserve upstream thought-token usage in normalized completion and reasoning token counts ([#13055](https://github.com/diegosouzapw/OmniRoute/pull/13055)) — thanks @pacocartones +- Fix a concurrency-slot leak in the DeepSeek PoW solver: a worker that failed to spawn (for example a missing worker script) never released its slot, so `MAX_CONCURRENT_WORKERS` failures disabled the solver until restart. ([#13097](https://github.com/diegosouzapw/OmniRoute/pull/13097)) — thanks @anhtahaylove +- **fix(acp):** bound the ACP session output buffers — `stdoutBuffer` and `stderrBuffer` now cap at 1 MiB keeping the most recent output behind a visible `[...output truncated...]` marker, and `stderrBuffer` is reset per prompt instead of accumulating for the lifetime of the session. ([#13100](https://github.com/diegosouzapw/OmniRoute/pull/13100)) — thanks @anhtahaylove +- **fix(acp):** release the `stdout`/`exit` listeners and the idle timer that a `sendPrompt` timeout used to leave attached to the `acpManager` singleton, and drop sessions that exited on their own from the session map instead of keeping them forever. ([#13096](https://github.com/diegosouzapw/OmniRoute/pull/13096)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection and PII scanners now read the text a `tool_result` block carries on `content` (string or nested block list), in messages and in system blocks, so tool output is judged by the same rules as user text ([#13101](https://github.com/diegosouzapw/OmniRoute/pull/13101)) — thanks @ntdatt812 +- **fix(gamification):** close the badge notification SSE stream when the request signal is already aborted before the stream starts — a client that disconnects while the route is still awaiting auth used to leave both the 2s unlock poll and the 15s heartbeat running for the lifetime of the process. ([#13106](https://github.com/diegosouzapw/OmniRoute/pull/13106)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection scan now spends its 16 KB budget on both ends of the request instead of the first 16 KB only, so `system`, `instructions`, `query`, `documents` and the newest turns are no longer hidden behind one long message ([#13104](https://github.com/diegosouzapw/OmniRoute/pull/13104)) — thanks @ntdatt812 +- **fix(dashboard):** expose the Volcano Ark console cookie on quota scraping and unwrap connect-error objects so the dashboard shows the upstream message ([#13107](https://github.com/diegosouzapw/OmniRoute/pull/13107)) — thanks @HouMinXi +- **fix(db):** release the `beforeExit`/`SIGINT`/`SIGTERM` handlers when a `node:sqlite` adapter closes, so a closed adapter and its database handle are no longer pinned to `process` for the lifetime of the run — the same treatment #7494 gave the sql.js adapter. ([#13109](https://github.com/diegosouzapw/OmniRoute/pull/13109)) — thanks @anhtahaylove +- **fix(translator):** `contentSchema` and `unevaluatedItems` are now treated as subschema positions by the tool-schema sanitizer, so a truncation placeholder in either is replaced with a permissive schema instead of being forwarded as a string ([#13110](https://github.com/diegosouzapw/OmniRoute/pull/13110)) — thanks @ntdatt812 +- **fix(cli-helper):** clear the `createLogStream` timeout on the abort path — `stop()` aborts the in-flight fetch and returned through the `signal.aborted` branch, which skipped `clearTimeout` and left an armed timer per stopped stream. The stream reader is now also cancelled when the read loop exits early. ([#13114](https://github.com/diegosouzapw/OmniRoute/pull/13114)) — thanks @anhtahaylove +- **fix(docs):** drop TLS-impersonation, MITM-decrypt, supply-chain attestation, and XOR-mask writeups from the public `/docs` catalog and Docker image. Files stay in git for engineers; operators who need them open the repo, not the website. ([#13136](https://github.com/diegosouzapw/OmniRoute/pull/13136)) — thanks @HouMinXi +- **fix(combo):** parse numeric-epoch `rate_limited_until` in the combo cooldown read path ([#13141](https://github.com/diegosouzapw/OmniRoute/pull/13141)) — thanks @maxmad64bis +- **fix(opencode):** both OpenCode plugins now reject a gateway address typed without `http://` at configuration time, instead of publishing every model with an api url no client can call, and the v2 plugin no longer publishes a model card whose api url is blank or relative ([#13142](https://github.com/diegosouzapw/OmniRoute/pull/13142)) — thanks @maxmad64bis +- **fix(providers):** lock opencode model on upstream 400 model-unavailable ([#13146](https://github.com/diegosouzapw/OmniRoute/pull/13146)) — thanks @maxmad64bis +- **fix(logging):** keep the provider exchange rather than the raw client bodies when a call log exceeds its size budget, and show that recovered payload in the request-detail panel instead of replacing it with the stored response body ([#13147](https://github.com/diegosouzapw/OmniRoute/pull/13147)) — thanks @maxmad64bis +- **fix(traffic-inspector):** the WebSocket route no longer leaks a traffic-buffer subscriber and a 30s ping timer when the client socket is already closed at handler time — listeners are attached before any resource is acquired, a destroyed socket bails out early, and the ping interval stops on a dead socket where `write()` never throws ([#13155](https://github.com/diegosouzapw/OmniRoute/pull/13155)) — thanks @anhtahaylove +- **fix(telegram):** bound the per-user API key cache in the Telegram chat proxy so a burst of distinct chat ids can no longer grow the process heap without limit ([#13165](https://github.com/diegosouzapw/OmniRoute/issues/13165)) ([#13166](https://github.com/diegosouzapw/OmniRoute/pull/13166)) — thanks @anhtahaylove +- **fix(stream):** cancel the upstream response body when the JSON-to-SSE sniff unwinds on a body timeout, so a stalled upstream no longer pins the connection ([#13169](https://github.com/diegosouzapw/OmniRoute/issues/13169)) ([#13171](https://github.com/diegosouzapw/OmniRoute/pull/13171)) — thanks @anhtahaylove +- **fix(telegram):** authenticate webhook deliveries with Telegram's `secret_token` so an unauthenticated caller can no longer mint API keys or spend upstream quota ([#13172](https://github.com/diegosouzapw/OmniRoute/issues/13172)) ([#13175](https://github.com/diegosouzapw/OmniRoute/pull/13175)) — thanks @anhtahaylove +- **fix(models):** give discoverable Gemini 3.8 Flash ids their own 65536 output spec so Antigravity no longer clamps them to 16384 ([#13195](https://github.com/diegosouzapw/OmniRoute/pull/13195)) — thanks @HouMinXi +- **fix(dashboard):** leftover catalog-removed provider rows (gemini-cli) can be listed and purged from the providers page ([#13067](https://github.com/diegosouzapw/OmniRoute/issues/13067)) ([#13197](https://github.com/diegosouzapw/OmniRoute/pull/13197)) — thanks @HouMinXi +- **fix(auth):** a dashboard session now requires the `authenticated: true` claim that login, OIDC and the session refresh already emit — a JWT merely signed with `JWT_SECRET` (for example the Cursor CLI passthrough token, which any API-key holder can obtain) no longer verifies as the `auth_token` cookie on any route, the WebSocket handshake or the live server; existing sessions keep working ([#13298](https://github.com/diegosouzapw/OmniRoute/issues/13298)) ([#13375](https://github.com/diegosouzapw/OmniRoute/pull/13375)) +- **fix(api):** `DELETE /v1/batches/delete-completed` now applies the caller's API-key policy (endpoint allowlist, schedule, usage cap, rate limit) like every other `/v1` route, commits the key-scoped sweep per 200-batch chunk instead of holding one write lock for the whole sweep, refuses to loop without progress, and rejects a scope that names both a key and `allTenants` ([#13684](https://github.com/diegosouzapw/OmniRoute/pull/13684)) +- Restore the API-route typecheck gate: the non-streaming leg result lost its discriminated-union narrowing after the server-owned tool loop reassignment, producing 13 new TS2339 diagnostics in `chatCore.ts`. ([#12963](https://github.com/diegosouzapw/OmniRoute/pull/12963)) +- Restore four non-streaming behaviours the server-owned tool loop refactor dropped: client aborts map to 499 with the fixed `Request aborted` message, an aborted request no longer logs a synthetic `clientResponse`, Claude prompt-cache telemetry is recorded again, and a body that cannot be canonicalized no longer throws when the tool loop is off. ([#12990](https://github.com/diegosouzapw/OmniRoute/pull/12990)) +- **fix(skills):** The CLI registry parser now reads positionals declared with `.addArgument()`, not only those written inline in `.command()`. `tunnel create [type]` was being published as `tunnel create`, so the agent-skills sync gate reported drift on every branch and regenerating would have deleted the argument. ([#13009](https://github.com/diegosouzapw/OmniRoute/pull/13009)) — thanks @ntdatt812 +- **fix(combos):** dashboard combo test uses a short prompt, serial probes, and a 60s timeout so reasoning models and rate-limited free pools do not fail the health check ([#13001](https://github.com/diegosouzapw/OmniRoute/pull/13001)) — thanks @HouMinXi +- fix(compression): terminate idle worker threads on eviction so long-running instances stop leaking OS threads and MessagePorts ([#13091](https://github.com/diegosouzapw/OmniRoute/pull/13091)) — thanks @anhtahaylove +- Redact Google API keys of any length in error bodies: the pattern required exactly 39 characters, so shorter or longer `AIza…` credentials passed through unredacted. ([#12964](https://github.com/diegosouzapw/OmniRoute/pull/12964)) +- **fix(grok-cli):** a 402 "Grok Build usage balance exhausted" parks that Grok login as out of credit (Grok Build CLI, grok.com cookie, and xAI OAuth share the weekly pool). Combo routing then tries the next login instead of locking the model for every account in the pool ([#13061](https://github.com/diegosouzapw/OmniRoute/pull/13061)) — thanks @HouMinXi +- fix(compression): spawn the LLMLingua worker with a file URL object so compression actually runs instead of silently failing open on Node ([#13093](https://github.com/diegosouzapw/OmniRoute/pull/13093)) — thanks @anhtahaylove +- **fix(test):** run the local `test` and `test:unit` scripts at concurrency 4 so a full-suite run no longer exhausts the machine's commit charge and kills unrelated processes ([#13187](https://github.com/diegosouzapw/OmniRoute/pull/13187)) — thanks @anhtahaylove +- **fix(monitoring):** `GET /api/monitoring/health` `credentialHealth` now includes a bounded `failedConnections` list (`connectionId`, `status`, sanitized `lastError`) when the probe-cache gauge `failed>0`, plus `source: probe-cache` and a cheap `staleDbNonOkCount` for sticky SQLite `test_status` on active rows. Documents that the live gauge is not `provider_connections.test_status`. ([#12876](https://github.com/diegosouzapw/OmniRoute/pull/12876)) — thanks @RaviTharuma +- **fix(resilience):** an openai-compatible multi-upstream gateway no longer marks the whole connection `credits_exhausted` when a single upstream (or the `/models` / representative-model health probe) returns 402. Quota failures stay model-scoped; true connection-wide auth failures are unchanged. Single-credential 402 key disable ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239)) is preserved. ([#12875](https://github.com/diegosouzapw/OmniRoute/pull/12875)) — thanks @RaviTharuma +- **fix(dashboard):** nine fixes on the Orchestration Canvas, all diagnosed in the Phase 2 reviews. A source that fails now keeps the timestamp of its FIRST failure instead of being re-stamped every poll — the stale line said "since the last poll" no matter how long the source had been down, and the churn also defeated the snapshot's stable identity (it serializes the source list), so the canvas re-rendered on every tick while anything was broken. A source that HAD data and only then started failing is flagged too: previously only a source with no node at all got the warning, so a source that went down mid-session kept a healthy-looking node forever. The rest are pointwise: clicking a filter chip cancels the pending search debounce (left armed, it fired ~300ms later and silently reverted the chip); the search input carries an accessible name; `?state=running, failed` parses like the unpadded form instead of dropping the padded value; the CSV toggle helper is defined once in `model/urlParams.ts` rather than twice; the agents tab tells "nothing running" apart from "the filter matched nothing", offering a clear-filters button instead of setup links that would be wrong advice there; edges stop emitting SMIL particles above 40 simultaneously active edges, keeping the colored stroke; and the drawer's error banner clears when a retried action succeeds. ([#12983](https://github.com/diegosouzapw/OmniRoute/pull/12983)) +- **fix(i18n):** the nine locales added with the EU-language batch (Greek, Estonian, Irish, Croatian, Lithuanian, Latvian, Maltese, Slovenian, Serbian) were missing the eleven Orchestration Canvas keys that Phase 3 introduced, so the compare-runs panel and the "no runs match these filters" empty state fell back to English in those languages (`deepMergeFallback` substitutes English for an absent key, so nothing rendered blank — it rendered untranslated). The coverage gate does not catch this: it enforces an 80% floor per locale, and eleven missing keys out of ~13,000 leaves coverage at 99.9%. Translated for real in each language, calibrated against the wording each file already uses for "run", "filter" and "skill". ([#13294](https://github.com/diegosouzapw/OmniRoute/pull/13294)) +- **fix(dashboard):** five follow-ups from the Phase 2 Orchestration Canvas review. The History drawer now shows the memory section for runs that already left the live TTL window: the persisted `memory_hits` event is parsed into `metadata.memoryHits` with the same defensive validation the drawer applies, and — because that event is observability rather than a state transition — it no longer leaks into the timeline, where it had been inheriting the task's state and rendering as a duplicate transition. The A2A memory recall runs against its own 1.5s deadline instead of inheriting the memory backend's 30s one; overshooting degrades exactly like any other recall failure (no hits, task proceeds), and the timer is cleared on both paths. Repeating a Conductor run carries its `requirements.cli`/`requirements.model` forward, so the new run is pinned to the same runner profile and model rather than drifting to whatever the fleet picks. A successful repeat from the Agents tab now focuses the run it created, instead of leaving the operator on the finished one. And the auth test for `POST /api/conductor/tasks` moved into the shared `ROUTES` array rather than restating the pattern. ([#12988](https://github.com/diegosouzapw/OmniRoute/pull/12988)) +- fix(plugins): stop leaking an exit listener per plugin hook timeout, which triggered MaxListenersExceededWarning on plugins that ignore SIGTERM ([#13092](https://github.com/diegosouzapw/OmniRoute/pull/13092)) — thanks @anhtahaylove +- **fix(validation):** Provider node edits no longer fail with a generic "Invalid request" when the optional daily-quota reset fields are left blank. The dashboard sends `dailyQuotaResetTimezone` and `dailyQuotaResetHour` as `null`, and only the hour accepted it. ([#13066](https://github.com/diegosouzapw/OmniRoute/issues/13066)) ([#13083](https://github.com/diegosouzapw/OmniRoute/pull/13083)) — thanks @ntdatt812 +- **fix(combo):** quota-aware expansion drops banned, inactive, missing, and wrong-provider connections before quota fetch or model dispatch; pins and allowlists stay selectors, not a bypass. Antigravity automatic exhaustion now requires a reported zero remaining, so a positive balance below 1% stays eligible. ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- Fix an intermittent failure in the quota-weighted routing test suite: scores are a function of `Date.now()`, so peers with identical quota scored microseconds apart never tied and swapped order. ([#12946](https://github.com/diegosouzapw/OmniRoute/pull/12946)) +- fix(volcengine): resolve quota window mapping gaps, safe multi-connection console binding, and response cookie sanitization ([#12950](https://github.com/diegosouzapw/OmniRoute/pull/12950)) — thanks @HouMinXi +- **fix(test):** resolve the WebDAV handler path with `fileURLToPath` so the suite's 37 WebDAV tests run on Windows instead of failing with a doubled `C:\C:\` drive prefix ([#13196](https://github.com/diegosouzapw/OmniRoute/pull/13196)) — thanks @anhtahaylove +- **fix(ui):** fall back from invalid AnySearch icon alias (direct commit `9587e07b69`) +- **fix(sse):** stop rebuilding a truncated summary from the collector's cap-dropped event array ([#12718](https://github.com/diegosouzapw/OmniRoute/pull/12718)) — thanks @hartmark +- **fix(conversations):** show a pending spinner for unresolved tool nodes instead of "(empty)" ([#12727](https://github.com/diegosouzapw/OmniRoute/pull/12727)) — thanks @hartmark +- **fix(streaming):** fail fast when an upstream stream produces only lifecycle/heartbeat events, never real content ([#12741](https://github.com/diegosouzapw/OmniRoute/pull/12741)) — thanks @hartmark +- **fix(responses-continuation):** bridge the write-in-flight window with an in-memory pending store ([#12854](https://github.com/diegosouzapw/OmniRoute/pull/12854)) — thanks @hartmark +- **fix(translator):** drop or map agent_message on Chat Completions fallback ([#12880](https://github.com/diegosouzapw/OmniRoute/pull/12880)) — thanks @RaviTharuma +- **fix(api):** page and stream-complete GET /v1/models for large catalogs ([#12882](https://github.com/diegosouzapw/OmniRoute/pull/12882)) — thanks @RaviTharuma +- **fix(quota):** align AUTH window labels with usage API and clarify 503 ([#12884](https://github.com/diegosouzapw/OmniRoute/pull/12884)) — thanks @RaviTharuma +- **fix(ci):** clear two base-reds on release/v3.8.51 — mutation-coverage gate + image-only-model guard ([#12945](https://github.com/diegosouzapw/OmniRoute/pull/12945)) +- **fix(api):** scope batch bulk-delete to the calling API key ([#13211](https://github.com/diegosouzapw/OmniRoute/pull/13211)) +- **fix(tests):** align model-sync log assertion to shipped path redaction (base-red) ([#13227](https://github.com/diegosouzapw/OmniRoute/pull/13227)) +- **fix(test):** make the ToS heading guard actually require the parentheses ([#13228](https://github.com/diegosouzapw/OmniRoute/pull/13228)) +- **fix(quality):** register 4 drifted covering tests in stryker tap.testFiles ([#13229](https://github.com/diegosouzapw/OmniRoute/pull/13229)) +- **fix(logging):** mask *-api-key headers in request-log pipeline (#13273) ([#13401](https://github.com/diegosouzapw/OmniRoute/pull/13401)) — thanks @KooshaPari +- **fix(tests):** correct stale Jina catalog prefix in custom-models test ([#13403](https://github.com/diegosouzapw/OmniRoute/pull/13403)) — thanks @KooshaPari +- **fix(backend):** prune old backups after health-check-repair snapshots (#13308) ([#13404](https://github.com/diegosouzapw/OmniRoute/pull/13404)) — thanks @KooshaPari +- **fix(backend):** guard memory_fts_au trigger with WHEN clause to stop FTS bloat (#13326) ([#13405](https://github.com/diegosouzapw/OmniRoute/pull/13405)) — thanks @KooshaPari +- **fix(free-tiers):** remove obsolete Together $25 signup credit (#12526) ([#13407](https://github.com/diegosouzapw/OmniRoute/pull/13407)) — thanks @KooshaPari +- **fix:** skip non-chat generation models in Test all models ([#13409](https://github.com/diegosouzapw/OmniRoute/pull/13409)) — thanks @KooshaPari +- **fix(providers):** de-duplicate agy and antigravity model catalogs ([#13410](https://github.com/diegosouzapw/OmniRoute/pull/13410)) — thanks @KooshaPari +- **fix(dashboard):** send provider in body for mini-playground web search ([#13411](https://github.com/diegosouzapw/OmniRoute/pull/13411)) — thanks @KooshaPari +- **fix(resilience):** add CJK quota-exhaustion patterns to 429 classifier ([#13412](https://github.com/diegosouzapw/OmniRoute/pull/13412)) — thanks @KooshaPari +- **fix(backend):** eval runner: failed calls no longer score as passed + dotAll regex ([#13413](https://github.com/diegosouzapw/OmniRoute/pull/13413)) — thanks @KooshaPari +- **fix(backend):** eval runner opts out of output-style and memory injection ([#13414](https://github.com/diegosouzapw/OmniRoute/pull/13414)) — thanks @KooshaPari +- **fix:** chain serial quarantine step in npm test script (fixes #13286) ([#13418](https://github.com/diegosouzapw/OmniRoute/pull/13418)) — thanks @KooshaPari +- **fix(antigravity):** derive stable session ID from credentials for prompt-cache reuse (#13305) ([#13424](https://github.com/diegosouzapw/OmniRoute/pull/13424)) — thanks @KooshaPari +- **fix(memory):** merge memory into top-level system field instead of unshifting at messages[0] (#13425) ([#13427](https://github.com/diegosouzapw/OmniRoute/pull/13427)) — thanks @KooshaPari +- **fix(compression):** preserve instruction blocks from lossy engines ([#13523](https://github.com/diegosouzapw/OmniRoute/pull/13523)) — thanks @KooshaPari +- **fix(compression):** preserve polarity words and newlines in ultra heuristic ([#13524](https://github.com/diegosouzapw/OmniRoute/pull/13524)) — thanks @KooshaPari +- **fix(compression):** run user preservePatterns before built-in patterns (#13457) ([#13525](https://github.com/diegosouzapw/OmniRoute/pull/13525)) — thanks @KooshaPari +- **fix(reasoning):** expand deepseek-v4 regex to match v4.1+ and add command-code provider (#13430) ([#13528](https://github.com/diegosouzapw/OmniRoute/pull/13528)) — thanks @KooshaPari +- **fix(providers):** add alwaysReasons metadata for reka-flash-3 ([#13531](https://github.com/diegosouzapw/OmniRoute/pull/13531)) — thanks @KooshaPari +- **fix(playground):** allow Compare columns to scroll independently ([#13532](https://github.com/diegosouzapw/OmniRoute/pull/13532)) — thanks @KooshaPari +- **fix(security):** empty whitelist allows through to prevent admin lockout ([#13534](https://github.com/diegosouzapw/OmniRoute/pull/13534)) — thanks @KooshaPari +- **fix(tests):** add DISABLE_IOREG_STRATEGY env var for macOS test compatibility (fixes #13467) ([#13539](https://github.com/diegosouzapw/OmniRoute/pull/13539)) — thanks @KooshaPari +- **fix(zed-hosted):** map developer role and Gemini enums for Zed proxy ([#13543](https://github.com/diegosouzapw/OmniRoute/pull/13543)) — thanks @KooshaPari +- **fix(analytics):** normalize provider aliases to canonical id in usage_history ([#13545](https://github.com/diegosouzapw/OmniRoute/pull/13545)) — thanks @KooshaPari +- **fix(combo):** use traceId for call log id to prevent UNIQUE constraint failure ([#13546](https://github.com/diegosouzapw/OmniRoute/pull/13546)) — thanks @KooshaPari +- **fix(auth):** check model lockout before returning synthetic noauth connection ([#13547](https://github.com/diegosouzapw/OmniRoute/pull/13547)) — thanks @KooshaPari +- **fix(guardrails):** skip credential redaction for base64 image data URLs ([#13550](https://github.com/diegosouzapw/OmniRoute/pull/13550)) — thanks @KooshaPari +- **fix(resilience):** increase requestQueue.maxWaitMs default from 15s to 30s ([#13553](https://github.com/diegosouzapw/OmniRoute/pull/13553)) — thanks @KooshaPari +- **fix(docs):** document OMNIROUTE_READY_TIMEOUT_MS and allowlist the test-only DISABLE_IOREG_STRATEGY ([#13692](https://github.com/diegosouzapw/OmniRoute/pull/13692)) + ### 📝 Maintenance ---- +- **docs(readme):** reconcile live v3.8.50 provider, free-tier, CLI, routing, test, community, sponsor, acknowledgment, and SVG metrics with their audited source denominators, including a deduplicated OmniRoute-in-Action snapshot and distinct contributor rankings for merged pull requests, GitHub-attributed commits, and Git history ([#11356](https://github.com/diegosouzapw/OmniRoute/pull/11356)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **docs:** sync the canonical provider count 354 → 356 across `README.md`, `AGENTS.md`, `llm.txt` (+ 42 i18n mirrors), the four README SVG diagrams, `docs/reference/PROVIDER_REFERENCE.md` (regenerated) and the `package.json` description after Opper (#11629) and 1min.ai (#11631) boarded the catalog — closes the `check:docs-counts-sync` strict drifts that kept `release/v3.8.51` red ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11673](https://github.com/diegosouzapw/OmniRoute/pull/11673)) — thanks @jonlwheat2-gif +- **test(providers):** update count-derived assertions after the v3.8.51 provider additions — `APIKEY_PROVIDERS` 233 → 235 (Opper #11629 + 1min.ai #11631), reserved-prefix REGISTRY walk 395 → 398, `WEB_FETCH_PROVIDERS` now includes `nimble-search` (#11620), and the provider translate-path golden snapshot regenerated ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11674](https://github.com/diegosouzapw/OmniRoute/pull/11674)) — thanks @jonlwheat2-gif +- **test(sse):** bump the hard-lease connection-query inventory for `src/lib/tokenHealthCheck.ts` to 2 — the verify-only web-cookie sweep added by #11495 split the single `getProviderConnections` call into oauth + cookie variants, which the frozen inventory had not tracked ([#11495](https://github.com/diegosouzapw/OmniRoute/pull/11495)). ([#11677](https://github.com/diegosouzapw/OmniRoute/pull/11677)) — thanks @jonlwheat2-gif / @oyi77 +- **chore(release):** clear the `release/v3.8.51` base-red gates — docs migration count (+38 i18n mirrors), stryker `tap.testFiles`, lockfile registry host, stale ESLint suppressions, the 7 lint regressions they were hiding, the stale `APIKEY_PROVIDERS` partition count (231 → 233), the `glm-5.3-max` tier missing from the GLM test table, a dead `modelType === "chat"` comparison in the models catalog, and the `resolveLiveWsUrl` / `sanitizeLiveWsPort` helpers from #11388 that a later merge dropped from `wsPath.ts` (dashboard live WebSocket URL resolution) ([#11502](https://github.com/diegosouzapw/OmniRoute/pull/11502)) — thanks @MumuTW +- **chore(free-models):** the free-tier regime table now declares which totals figure each regime feeds and whether it may take the no-auth shortcut, so the sets that used to repeat those answers by hand are derived from it — a new `freeType` no longer compiles until it has answered every question, instead of silently contributing to no total ([#11537](https://github.com/diegosouzapw/OmniRoute/pull/11537)) — thanks @maxmad64bis +- **docs(routing):** LKGP strategy copy now states provider-level stickiness and the explicit rules fallback instead of promising per-target pinning ([#11555](https://github.com/diegosouzapw/OmniRoute/pull/11555)) — thanks @maxmad64bis +- **test(chatcore):** pin the response tool-name alias resolution against the ordering hazard that broke every Gemini/Antigravity MCP tool call in v3.8.49. `extractRequestToolIdentityMap` deletes `translatedBody._toolNameMap`, so the later read is always undefined and the ledger survives only through the `requestToolIdentityMap` fallback — removing that fallback previously left the entire tool-name suite green. The resolution moves into `resolveResponseToolNameMap()` next to the map it depends on, with a regression guard that fails without the recovery. No behaviour change. ([#11575](https://github.com/diegosouzapw/OmniRoute/pull/11575)) — thanks @amartinawi +- **docs(env):** document `OMNIROUTE_CORPUS_CACHE_SIZE` in `.env.example` and `ENVIRONMENT.md`, so the env/docs contract check stops failing Docs Gates on every branch ([#11576](https://github.com/diegosouzapw/OmniRoute/pull/11576)) — thanks @ntdatt812 +- **chore(stryker):** register `route-guard-tunnel-processes-local-only` and `universal-quota-aware-routing` in `tap.testFiles`, so their mutant kills count for the three modules they cover ([#11577](https://github.com/diegosouzapw/OmniRoute/pull/11577)) — thanks @ntdatt812 +- **test(authz):** pin `LOCAL_ONLY_API_GET_EXEMPTIONS` by exact membership instead of by entry count, so the guard names the offending path and also catches a substitution ([#11580](https://github.com/diegosouzapw/OmniRoute/pull/11580)) — thanks @ntdatt812 +- **test(build):** pin `resolveNextBuildBundlerFlag` to the `OMNIROUTE_USE_TURBOPACK` contract it implements, across runtimes, instead of asserting an unimplemented Bun override ([#11583](https://github.com/diegosouzapw/OmniRoute/pull/11583)) — thanks @ntdatt812 +- **test(claude-web):** the Claude Web transport test's barrel anchors no longer pin the exact construction spelling. #11421 made the executor map lazy, so `/"claude-web": new ClaudeWebExecutor\(\)/` stopped matching and the test went red on `release/v3.8.51` itself — a base-red gate every open PR inherited. The anchors now match the alias key and the class name and tolerate either spelling in between, and were checked to still fail when an alias is genuinely repointed at another executor ([#11585](https://github.com/diegosouzapw/OmniRoute/pull/11585)) — thanks @ntdat812 +- **test(models):** pin `qwen3.8-max` resolution per provider — two of the four catalogs now serve the bare id and no longer carry `-preview` ([#11590](https://github.com/diegosouzapw/OmniRoute/pull/11590)) — thanks @ntdatt812 +- **test(executors):** drop the stale 128-tool truncation assertion that contradicted the #11444 pass-through contract ([#11591](https://github.com/diegosouzapw/OmniRoute/pull/11591)) — thanks @ntdatt812 +- **test(token-health):** pin the current terminal-skip boundary — expired with retry budget is probed, `account_deactivated` stays skipped ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)) — thanks @ntdatt812 +- **test(models):** follow #11503's retarget of the `gemini-3-pro-high` alias to the hyphenated catalog id ([#11593](https://github.com/diegosouzapw/OmniRoute/pull/11593)) — thanks @ntdatt812 +- **refactor(video bridge):** extract per-video acquisition, whole-result caching, description, and metrics/abort/cleanup out of `VideoBridgeGuardrail.preCall` into a `processVideoPart` seam in a new `videoBridgePipeline.ts`, behind explicit `VideoMediaBrokerPort`, `VideoAudioTranscriptionPort`, and `VideoDrilldownPort` boundaries; `preCall` now only handles request traversal, policy, and response aggregation. The Video tab's FFmpeg/ffprobe runtime status is now an explicit `unknown` / `restricted` / `unavailable` / `available` state instead of a nullable boolean pair, fixing a case where an in-flight or failed probe was mislabeled as "install FFmpeg" ([#11657](https://github.com/diegosouzapw/OmniRoute/issues/11657)). ([#12016](https://github.com/diegosouzapw/OmniRoute/pull/12016)) +- **test(opencode-plugin):** add unit test coverage for `formatFreeBudget()` naming helper ([#11660](https://github.com/diegosouzapw/OmniRoute/pull/11660)) — thanks @f9td56dbgh-hub +- **docs(video):** clarify that the Video Bridge transcript `source` field (`client`, `embedded`, `audio-bridge`) is presently caller-declared and not yet server-verified — OmniRoute enforces the enum shape but does not cryptographically confirm that an `embedded`/`audio-bridge` label came from a server-owned extraction ([#11661](https://github.com/diegosouzapw/OmniRoute/issues/11661)). ([#12001](https://github.com/diegosouzapw/OmniRoute/pull/12001)) +- **chore(providers):** retire the distributed Raycast Relay and Hailuo Web integrations after a provenance audit confirmed substantial derivation from GPL-3.0 sources; the official GitHub and MiniMax API providers remain available. Migration 163 disables legacy connections and invalidates their active leases while preserving connection IDs, audit/history rows, quota snapshots, usage records, and fail-closed API-key connection policies. ([#11691](https://github.com/diegosouzapw/OmniRoute/pull/11691)) +- **chore(providers):** retire the Felo Web provider while its GPL-derived provenance remains on licensing HOLD ([#11698](https://github.com/diegosouzapw/OmniRoute/pull/11698)) — stale runtime and database references now fail closed while existing connection, lease, and usage history remains preserved for auditability. +- **fix(sse):** retire the Gemini Web image-generation bridge while provenance and licensing remain on HOLD, preserving Gemini Web chat/auth/tools and legitimate Gemini/Nano Banana image providers ([#11708](https://github.com/diegosouzapw/OmniRoute/pull/11708)) +- **chore(audio):** retire the built-in EdgeTTS provider and its 11-voice catalog while provenance/licensing review remains on HOLD; the generic `/v1/audio/speech` endpoint and the other speech providers remain available ([#11711](https://github.com/diegosouzapw/OmniRoute/pull/11711)) +- **fix(providers):** retire the unofficial Qwen Web integration and its `qwen-web`/`qw` identities while third-party provenance remains on HOLD ([#11713](https://github.com/diegosouzapw/OmniRoute/pull/11713)) — direct requests now fail closed with a sanitized HTTP 410 and combos skip the retired target, while migration guards preserve connection IDs, allowlists, usage/audit history and invalidate active leases. Supported Qwen API, Qwen Code, cloud, token-plan, audio, local and model-name surfaces remain available; restoring Qwen Web requires a qualified provenance/license decision. +- **fix(providers):** retire `microsoft-designer-web` and its `msdesigner` alias from runtime and catalogs while preserving stored credentials, provider history and allowlists as inactive records — source dependency on gpt4free's [`MicrosoftDesigner.py`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/g4f/Provider/needs_auth/MicrosoftDesigner.py) is proven at snapshot `0943d16eef757a44deb60651c07bbe5fa958cafe`, whose [`LICENSE`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/LICENSE) is GPL-3.0; expressive adaptation is probable, which records provenance/licensing risk without asserting a legal conclusion ([#11720](https://github.com/diegosouzapw/OmniRoute/pull/11720)). +- **docs(legal):** centralize pinned MIT notices for the vendored GCF codec and local flag assets ([#11726](https://github.com/diegosouzapw/OmniRoute/pull/11726)) +- Split the 2,952-line `adobeFireflyClient.ts` into nine focused modules (catalog, credentials, payload, ARP session, upload, responses, IMS, account, poll) behind an unchanged barrel — no consumer edits and no public API change (#11728). — thanks @MumuTW +- **fix(dashboard):** replaced the Model Lockout card's unprovenanced notification MP3 with a short, locally synthesized Web Audio chime ([#11731](https://github.com/diegosouzapw/OmniRoute/pull/11731)) — both toggles retain optional audible feedback without shipping replacement media or third-party code, and unsupported, suspended, or rejected audio contexts fail open without blocking the setting change. +- **chore(deepseek-web):** remove the provenance-unresolved DeepSeek PoW WASM binary and its runtime loader/tracing while retaining the existing JavaScript solver (slower at high difficulty) ([#11732](https://github.com/diegosouzapw/OmniRoute/pull/11732)). +- **chore(assets):** remove four unused provider assets whose redistribution provenance could not be verified while retaining the active Freebuff SVG ([#11735](https://github.com/diegosouzapw/OmniRoute/pull/11735)) +- **docs(assets):** retired two raster images whose redistribution provenance could not be verified ([#11736](https://github.com/diegosouzapw/OmniRoute/pull/11736)) — PicoClaw documentation now reuses the existing SVG, while Zoo Code remains a text link without an image. +- **docs(legal):** add pinned notices for six LobeHub-derived provider SVGs and 65 byte-exact theSVG assets, retaining registry claims as unverified metadata with explicit trademark and HOLD boundaries ([#11737](https://github.com/diegosouzapw/OmniRoute/pull/11737)). +- **chore(quality):** added a versioned 225/225 provider-asset provenance manifest and blocking drift gate ([#11743](https://github.com/diegosouzapw/OmniRoute/pull/11743)) — the inventory pins magic MIME and SHA-256 for every physical file, records immutable source matches for 71 assets, keeps 69 probable and 85 unresolved entries explicit but non-blocking, and treats provenance as source evidence rather than copyright or trademark clearance. +- **refactor(mitm):** replace four previously derived Traffic Inspector utilities — conversation normalization, SSE merging, secret masking, and header sanitization — with independent clean-room implementations based on public provider schemas, WHATWG SSE, and RFC 6750, while keeping the two independently classified passthrough surfaces unchanged ([#11748](https://github.com/diegosouzapw/OmniRoute/pull/11748)). +- Added a unit test that fails seven days before any dated pack under `config/` (`validUntil` and sibling keys) lapses, naming the file and key. The Alibaba free-tier pack expired on 2026-08-27 and turned every PR red the next morning with no commit involved; renewal now happens on someone's terms, not the clock's. ([#11891](https://github.com/diegosouzapw/OmniRoute/pull/11891)) +- `check:workflows` now fails (under `--strict`/`--ratchet`) when any job routed to a self-hosted runner publishes with `--provenance` — npm rejects that with `422` at the registry, which in v3.8.50 only surfaced after the tag and Docker images were public. ([#11895](https://github.com/diegosouzapw/OmniRoute/pull/11895)) +- `scripts/ops/runner-janitor.sh` now proves a path is idle with one `lsof` snapshot and removes stale leftovers itself (tmpfs after 3 h — it is RAM — disk after 24 h), kills orphan `next-build` processes, prunes checkouts of stopped runners, and alerts on memory pressure; `--dry-run` shows exactly what it would do. `docs/ops/RUNNER_BOX.md` reconciled to the measured box (31 GB, 10 listeners). ([#11893](https://github.com/diegosouzapw/OmniRoute/pull/11893)) +- The `next-build` artefact (1.3 GB) is now written and read under `$RUNNER_TEMP` (per-runner, on disk) instead of `/tmp`, which on the self-hosted pool is a 12 GB tmpfs in RAM. Landing it there took 27–32 of the publish job's 76 minutes, and the fixed `/tmp/e2e-build.tar.gz` name let E2E jobs on different runners overwrite each other's download. ([#11896](https://github.com/diegosouzapw/OmniRoute/pull/11896)) +- The CI `build` job now runs in two concurrency lanes — `main` and pull requests — so a release build is never queued behind (or OOM-killed beside) PR builds on the self-hosted pool, which holds one `next-build` comfortably and two at the edge. ([#11901](https://github.com/diegosouzapw/OmniRoute/pull/11901)) +- Re-freeze the ESLint suppressions on `release/v3.8.51` from a clean-room measurement (2 stale file entries pruned, 55 pre-existing `no-explicit-any` in six new files frozen under #11924) and drop the dead `GPT_SIZE_MAP` constant orphaned by the Adobe Firefly client split, so `No new ESLint warnings` stops failing every PR with exit 2 (Refs #11924) ([#11955](https://github.com/diegosouzapw/OmniRoute/pull/11955)) +- Type the 55 `no-explicit-any` sites that had been frozen under #11924 — four redundant casts in `socksConnectorWithFamily.ts` (undici/socks types already accept them) and the mocks/fixtures of the socks-timeout and isFree suites — and drop their suppression entries; the ESLint ratchet shrinks from 5487 to 5432 (Closes #11924) ([#11975](https://github.com/diegosouzapw/OmniRoute/pull/11975)) +- Take the two hosted-runner builds off the PR rail: `Build App` (`build.yml`) is `workflow_dispatch`-only and `DAST smoke (PR)` runs only for PRs into `main` — the 7 GB hosted VM cannot build `release/v3.8.51` in any profile (19/30 red, VM shutdown ~8 min into `next build`) and both checks had turned into permanent noise on every release PR; the bundle stays validated by `ci.yml` on `main` and by `nightly-release-green` on `release/**` (Closes #11946) ([#11962](https://github.com/diegosouzapw/OmniRoute/pull/11962)) +- Move the four nightly jobs that build the backend (`nightly-schemathesis`, `nightly-llm-security` promptfoo + garak, `nightly-resilience` axe-a11y) off the hosted 7 GB runner — where they died on `release/v3.8.51` unseen — onto the box's new `omni-light` pool (two listeners), and document the reshaped fleet (4 active OmniRoute listeners: 2 `omni-build` + 2 `omni-light`, janitor ceiling 4) (Closes #11965) ([#11967](https://github.com/diegosouzapw/OmniRoute/pull/11967)) +- Stop painting every fork PR into `release/**` red: `quality.yml` `Build (advisory)` is skipped (GitHub still reports `continue-on-error` failures as check FAILURE). Hosted `ubuntu-latest` cannot finish `npm run build` on this tree — same class as #11962 taking `build.yml` off the PR rail. `docker-publish.yml` amd64 now runs on the `.113` `omni-build` pool (31 GB / 32 cores, two listeners) with Turbopack, shares the `heavy-build-main` lane with `ci.yml` `Build` so it queues instead of becoming a third heavy, and keeps per-ref concurrency (a merge storm was starting 8 concurrent OOM builds). arm64 stays on `ubuntu-24.04-arm` with webpack — there is no ARM box. Fallback when `USE_VPS_RUNNER` is off: hosted amd64 + webpack (#11976). ([#12021](https://github.com/diegosouzapw/OmniRoute/pull/12021)) +- `docker-publish.yml` builds with webpack (`OMNIROUTE_USE_TURBOPACK=0`) on **both** arches, including amd64 on the `.113` `omni-build` pool. Turbopack had the RAM (31 GB, `omniroute-113-6`) and still panicked (`TurbopackInternalError: there must be a path to a root`, run 33253576569, 26 min in). The same tree's arm64 webpack build on hosted `ubuntu-24.04-arm` succeeded (run 33264823398). Dockerfile already documents webpack as the Docker escape hatch (#11976). ([#12050](https://github.com/diegosouzapw/OmniRoute/pull/12050)) +- **docs(free-tier):** declare the counting vs deciding regimes for "is it free?" and guard the deciding path from DB-backed catalog resolution ([#12226](https://github.com/diegosouzapw/OmniRoute/pull/12226)) — thanks @maxmad64bis +- **docs(auto-combo):** The mode pack table in `docs/routing/AUTO-COMBO.md` now lists all six shipped packs with every weight each one sets, replacing a four-pack table whose numbers had also drifted from the source. It states plainly that no pack sets `quality`, so selecting any pack silences the observed-quality signal. Six more documents that quote the scoring factor count joined the `check:docs-counts` gate, which caught five stale claims — including one naming nine factors that do not exist — and two stale mode pack counts. The dashboard routing panel, which offered four of the six packs and labelled the default strategy "6-Factor Scoring", is now covered by a test; the two packs it was missing are `reliability-first` and `chaos-mode`, the latter labelled as the fault-injection profile it is rather than as one more routing preference ([#12316](https://github.com/diegosouzapw/OmniRoute/pull/12316)) — thanks @maxmad64bis +- **chore(quality):** rebaseline `src/lib/db/apiKeys.ts` for the ACL the key-creation path now preserves ([#12352](https://github.com/diegosouzapw/OmniRoute/pull/12352)) ([#12673](https://github.com/diegosouzapw/OmniRoute/pull/12673)) — thanks @KrzysiekSko +- **chore(providers):** bump the Claude Code wire identity and the Devin bridge image pin from `2.1.220` to `2.1.258` ([#12402](https://github.com/diegosouzapw/OmniRoute/pull/12402)) — thanks @ggiak ([#12604](https://github.com/diegosouzapw/OmniRoute/pull/12604)) +- **docs(env):** align `.env.example`, the README Bun section, and the troubleshooting guide with the code: `OMNIROUTE_USE_TURBOPACK` also governs `npm run build` (not dev-only), `bun run build` follows that flag instead of auto-selecting Webpack, `OMNIROUTE_CHAT_MAX_HEAVY_IN_FLIGHT` is unset by default (no request-count cap), and the structural `503 chat_admission_busy` message matches `chatAdmissionResponses.ts` (#12404 — thanks @pacocartones) +- **chore(electron):** upgrade the desktop app to Electron 44 (Chromium 152, Node 24.18.1) ([#12217](https://github.com/diegosouzapw/OmniRoute/pull/12217)). **Requires macOS 13 (Ventura) or later** — Chromium dropped macOS 12 (Monterey), so Monterey users must stay on an earlier OmniRoute desktop build. Windows and Linux are unaffected; the app already shipped only x64/arm64, so Electron 44 dropping 32-bit builds changes nothing. Removes the `openAsHidden`/`wasOpenedAsHidden` login-item fields deleted in Electron 44 — hidden autostart continues to work through the `--hidden` argument registered with the login item ([#12554](https://github.com/diegosouzapw/OmniRoute/pull/12554)) — thanks @app/dependabot +- **chore(quality):** rebaseline `src/sse/handlers/chat.ts` for the effective-input persistence the continuation fix needs ([#12641](https://github.com/diegosouzapw/OmniRoute/pull/12641)) ([#12680](https://github.com/diegosouzapw/OmniRoute/pull/12680)) — thanks @hartmark +- `Coverage` job on `ci.yml`: the informational Codecov upload gets its own 5-minute ceiling and `continue-on-error`, and the job budget grows from 20 to 30 minutes (the 8-shard c8 merge alone takes ~10) — a stalled upload no longer ends the job `cancelled` and drags a fully green `main` run's conclusion down with it ([#11972](https://github.com/diegosouzapw/OmniRoute/pull/11972)) +- CI hardening on the PR rail: the four `Unit Tests fast-path` shards get `timeout-minutes: 30` (a hung shard held a PR for 64 min instead of GitHub's 6 h default) and both ESLint file caches lose their `restore-keys` fallback, so a cache built under another suppressions file or lint config can no longer report stale verdicts (Refs #11600, #11924) ([#11963](https://github.com/diegosouzapw/OmniRoute/pull/11963)) +- Every CI job that runs a `next build` (`build`, the npm `publish`, both release-green validations) now targets the `omni-build` runner label, which only two of the eight self-hosted runners carry. The box holds one build comfortably and two at the edge; a third now queues on GitHub instead of being OOM-killed by the kernel. ([#11932](https://github.com/diegosouzapw/OmniRoute/pull/11932)) +- **refactor(combo):** move `handleRoundRobinCombo` (and `resolveTargetTokenLimit`) into `open-sse/services/combo/roundRobinCombo.ts`. `combo.ts` drops from 2164 to 1014 lines (`split("\n").length`); the leaf is 1199 (under the 1200 new-file cap). The round-robin call site uses a dynamic `import()` so `releaseStickyPinOnFailure` / `clearStaleLKGP` can stay exported from `combo.ts` without a static cycle. Skip / sticky / semaphore / safety-timer behavior is unchanged. ([#12811](https://github.com/diegosouzapw/OmniRoute/pull/12811)) — thanks @HouMinXi +- **chore(quality):** rebaseline the file-size caps the error-boundary campaign grew past (`open-sse/executors/codex.ts`, `open-sse/vendor/codex-chatgpt-web/bridge.ts`, both via [#12444](https://github.com/diegosouzapw/OmniRoute/pull/12444)) ([#12654](https://github.com/diegosouzapw/OmniRoute/pull/12654)) +- **chore(quality):** rebaseline the file-size caps the hartmark batch grew past (`combos/page.tsx` via [#12355](https://github.com/diegosouzapw/OmniRoute/pull/12355), `open-sse/services/combo.ts` via [#12338](https://github.com/diegosouzapw/OmniRoute/pull/12338)) ([#12623](https://github.com/diegosouzapw/OmniRoute/pull/12623)) — thanks @hartmark +- **chore(quality):** rebaseline the file-size caps the HouMinXi batch grew past when its PRs stacked (`providers/page.tsx`, `chatCore.ts`, `accountFallback.ts`) — each PR measured correctly in isolation, none saw the stacking ([#12619](https://github.com/diegosouzapw/OmniRoute/pull/12619)) +- **chore(quality):** rebaseline `open-sse/services/combo.ts` for the reset-aware scoring the HouMinXi batch stacked ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) ([#12678](https://github.com/diegosouzapw/OmniRoute/pull/12678)) — thanks @HouMinXi +- **docs:** sync provider catalog count (direct commit `4f11b2ae3d`) +- **docs(skills):** sync omni-inference search fallback (direct commit `b4e76a7ed9`) +- **chore(lint):** prune two stale base suppressions (direct commit `1325047d56`) +- **test(auto-combo):** tolerate full-suite rotation load (direct commit `ad3e293f9f`) +- **test:** drain inherited quality snapshot reds (direct commit `21ed68d8ac`) +- **chore(quality):** rebaseline measured CLI bundle growth (direct commit `d35b3f9779`) +- **test(ui):** reproduce invalid AnySearch icon alias (direct commit `12cc6ca834`) +- **test(translator):** fix the relative imports of the relocated deferred-finish test (direct commit `777d9d1629`) +- **docs(counts):** bring the documented provider and migration counts to the measured values (direct commit `e70bea30e9`) +- **test(security):** drop the guard-target set left unused by the sync (direct commit `a32ba0e362`) +- **chore(db):** renumber model_capabilities migration 163 → 169 (cross-branch collision with 163_radar_feed_cache_generated_at) (direct commit `70f5d4cbf9`) +- **docs(cli):** document CLI_PRIME_AGENT_BIN and correct the CLI Agents count (direct commit `9058e39b61`) +- **chore(deps):** refresh runtimes and adopt ESLint 10 ([#11259](https://github.com/diegosouzapw/OmniRoute/pull/11259)) — thanks @backryun +- **refactor(local-corpus):** implement dynamic root resolution and LRU cache ([#11491](https://github.com/diegosouzapw/OmniRoute/pull/11491)) — thanks @raheemuddin786 +- **chore(quality):** prune stale eslint-suppressions entries ([#11596](https://github.com/diegosouzapw/OmniRoute/pull/11596)) +- **chore(ci):** unblock quality gates - env-doc sync + stryker tap.testFiles registration (#9282) ([#11609](https://github.com/diegosouzapw/OmniRoute/pull/11609)) — thanks @Meet6338-X +- **chore(ci):** point the circular-deps gate at dpdm's real JS entrypoint ([#11615](https://github.com/diegosouzapw/OmniRoute/pull/11615)) — thanks @benzntech +- **chore(quality):** rebaseline file-size for #11448/#11495/#11561 growth ([#11630](https://github.com/diegosouzapw/OmniRoute/pull/11630)) +- **deps:** realign onnxruntime-node pin to @huggingface/transformers' 1.24.3 lockstep ([#11633](https://github.com/diegosouzapw/OmniRoute/pull/11633)) — thanks @jonlwheat2-gif +- **test(autoCombo):** port models_dev_tier #11508 guard from node:test to vitest ([#11635](https://github.com/diegosouzapw/OmniRoute/pull/11635)) — thanks @jonlwheat2-gif +- **chore(quality):** rebaseline file-size for #11083/#11631/#11628 growth ([#11636](https://github.com/diegosouzapw/OmniRoute/pull/11636)) +- **chore(lint):** prune stale ESLint suppressions (base-red #11449) ([#11644](https://github.com/diegosouzapw/OmniRoute/pull/11644)) — thanks @jonlwheat2-gif +- **docs(changelog):** aggregate the ten v3.8.50 fragments into CHANGELOG.md ([#11683](https://github.com/diegosouzapw/OmniRoute/pull/11683)) +- **chore(search):** drop unreachable credential sweep after #11642 ([#11687](https://github.com/diegosouzapw/OmniRoute/pull/11687)) — thanks @HouMinXi +- **ci:** add API route TypeScript regression gate ([#11705](https://github.com/diegosouzapw/OmniRoute/pull/11705)) — thanks @Sabeekhann +- **docs(dedup):** correct the CodeQL claim on the tenant-namespace comment ([#11712](https://github.com/diegosouzapw/OmniRoute/pull/11712)) +- **docs(changelog):** add consolidated v3.8.50 stats, top-25 ranking and a .mailmap ([#11715](https://github.com/diegosouzapw/OmniRoute/pull/11715)) +- **docs:** recommend gstack for AI-assisted workflows ([#11770](https://github.com/diegosouzapw/OmniRoute/pull/11770)) — thanks @santosraju99-hub +- **chore(quality):** tighten complexity/cognitive-complexity ratchets to the current tip; land the missed gateways.ts rebaseline ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **docs(i18n):** finish Freepik → Magnific rebrand in locale strings and README ([#11772](https://github.com/diegosouzapw/OmniRoute/pull/11772)) — thanks @turbolego +- **refactor(auto-combo):** fix divergent scoring in combo health reporting ([#11854](https://github.com/diegosouzapw/OmniRoute/pull/11854)) — thanks @jacobsparts +- **chore(changelog):** add missing fragments for #11919/#11918/#11916 ([#11938](https://github.com/diegosouzapw/OmniRoute/pull/11938)) +- **test(check):** escape the runs-on fixture with JSON.stringify, not a quote-only replace ([#11942](https://github.com/diegosouzapw/OmniRoute/pull/11942)) (direct commit `fb7445eaa3`) +- **test(release):** align five suites with the contracts #11933, #11919 and #11876 shipped on release/v3.8.51 ([#11944](https://github.com/diegosouzapw/OmniRoute/pull/11944)) +- **chore(quality):** remove unreachable code and restore test discovery ([#11950](https://github.com/diegosouzapw/OmniRoute/pull/11950)) — thanks @backryun +- **docs(agents):** sync-back landings are fast-forward, never squash ([#11964](https://github.com/diegosouzapw/OmniRoute/pull/11964)) +- **test(infra):** retry recursive temp-dir removal instead of failing a shard on ENOTEMPTY (#11966) ([#11968](https://github.com/diegosouzapw/OmniRoute/pull/11968)) +- **docs(ops):** the .113 heavy-build ceiling is one runner, not two ([#12048](https://github.com/diegosouzapw/OmniRoute/pull/12048)) +- **test(ui):** correct inactive auto-fetch expectation ([#12098](https://github.com/diegosouzapw/OmniRoute/pull/12098)) — thanks @RaviTharuma +- **docs:** document native dependency check escape hatch ([#12101](https://github.com/diegosouzapw/OmniRoute/pull/12101)) — thanks @RaviTharuma +- **test(providers):** regenerate the translate-path golden for OrcaRouter (#11923) ([#12118](https://github.com/diegosouzapw/OmniRoute/pull/12118)) +- **chore(quality):** velocity phase — loosen every numeric baseline by 20% until v4.0, monitor headroom nightly ([#12125](https://github.com/diegosouzapw/OmniRoute/pull/12125)) +- **chore(lint):** batch 0 of #12146 — type the call-log-cap sqlite rows instead of 45 as-any casts ([#12157](https://github.com/diegosouzapw/OmniRoute/pull/12157)) +- **chore(lint):** batch 4 of #12146 — shared/components react-hooks violations resolved ([#12159](https://github.com/diegosouzapw/OmniRoute/pull/12159)) +- **chore(lint):** batch 1 of #12146 — dashboard/cli-code react-hooks violations resolved ([#12160](https://github.com/diegosouzapw/OmniRoute/pull/12160)) +- **chore(lint):** batch 3 of #12146 — dashboard/settings react-hooks violations resolved ([#12162](https://github.com/diegosouzapw/OmniRoute/pull/12162)) +- **chore(lint):** batch 2 of #12146 — resolve the react-hooks compiler violations in dashboard/providers ([#12163](https://github.com/diegosouzapw/OmniRoute/pull/12163)) +- **chore(quality):** register search-432 cooldown test in stryker tap.testFiles ([#12170](https://github.com/diegosouzapw/OmniRoute/pull/12170)) +- **chore(lint):** batch 5 of #12146 — combos, endpoint, provider-stats, api-manager and costs react-hooks violations resolved ([#12174](https://github.com/diegosouzapw/OmniRoute/pull/12174)) +- **build:** add contributor fast profile ([#12192](https://github.com/diegosouzapw/OmniRoute/pull/12192)) — thanks @rafacpti23 +- **docs(readme):** README touch-ups ([#12193](https://github.com/diegosouzapw/OmniRoute/pull/12193)) ([#12194](https://github.com/diegosouzapw/OmniRoute/pull/12194)) ([#12202](https://github.com/diegosouzapw/OmniRoute/pull/12202)) — thanks @rafacpti23 +- **build:** force the webpack bundler in the contributor build profile ([#12197](https://github.com/diegosouzapw/OmniRoute/pull/12197)) — thanks @rafacpti23 +- **build:** skip standalone packaging in the contributor profile ([#12198](https://github.com/diegosouzapw/OmniRoute/pull/12198)) — thanks @rafacpti23 +- **docs(audit):** align every published number with the code and harden check:docs-counts ([#12200](https://github.com/diegosouzapw/OmniRoute/pull/12200)) +- **build:** stub the instrumentation entrypoints in the contributor profile ([#12203](https://github.com/diegosouzapw/OmniRoute/pull/12203)) — thanks @rafacpti23 +- **build:** omit the standalone output target for contributor builds ([#12204](https://github.com/diegosouzapw/OmniRoute/pull/12204)) — thanks @rafacpti23 +- **chore(lint):** batch 6 of #12146 — memory, radar, audit, analytics, cache, usage, activity, home and RequestLoggerV2 react-hooks violations resolved ([#12208](https://github.com/diegosouzapw/OmniRoute/pull/12208)) +- **docs(diagrams):** rename number-carrying diagram files to stable names ([#12210](https://github.com/diegosouzapw/OmniRoute/pull/12210)) +- **docs(dashboard):** redraw onboarding tier-flow SVGs for the real 4-tier model ([#12211](https://github.com/diegosouzapw/OmniRoute/pull/12211)) +- **docs(api):** document every implemented route in openapi.yaml (276 → 692 paths) ([#12212](https://github.com/diegosouzapw/OmniRoute/pull/12212)) +- **chore(lint):** batch 7 of #12146 — final src tail: 37 react-hooks violations across 33 files resolved ([#12244](https://github.com/diegosouzapw/OmniRoute/pull/12244)) +- Revert "docs: recommend gstack for AI-assisted workflows (#11770)" ([#12248](https://github.com/diegosouzapw/OmniRoute/pull/12248)) ([#12249](https://github.com/diegosouzapw/OmniRoute/pull/12249)) +- **docs:** Chaos Mode setup guide + weighted strategy semantics ([#12250](https://github.com/diegosouzapw/OmniRoute/pull/12250)) +- **docs(agents):** protected-surface merge rule — operator approval for agent-instruction files ([#12253](https://github.com/diegosouzapw/OmniRoute/pull/12253)) +- **chore(quality):** register native-codex turn-pin tests in stryker tap.testFiles ([#12263](https://github.com/diegosouzapw/OmniRoute/pull/12263)) +- **chore(quality):** dedupe tap.testFiles entries added by racing base-red fixes ([#12265](https://github.com/diegosouzapw/OmniRoute/pull/12265)) +- **docs(readme):** introduce OmniRouteTray — the macOS menu-bar companion ([#12276](https://github.com/diegosouzapw/OmniRoute/pull/12276)) — thanks @ggiak +- **chore(proxy):** purge the legacy 1proxy residue — sync/rotator modules, dead DB exports, dead settings tab and flag, docs (#12091) ([#12290](https://github.com/diegosouzapw/OmniRoute/pull/12290)) +- **chore(quality):** baseline-headroom skips generated and vendored files in the fileSize worst-file signal ([#12291](https://github.com/diegosouzapw/OmniRoute/pull/12291)) +- **refactor(video):** unify the JPEG frame data-URI contract ([#12322](https://github.com/diegosouzapw/OmniRoute/pull/12322)) +- **chore(deps):** freeze onnxruntime-node and eslint-plugin-react-hooks out of dependabot groups ([#12329](https://github.com/diegosouzapw/OmniRoute/pull/12329)) +- **test(executors):** restore the #10986 reasoning-only fallback guards ([#12364](https://github.com/diegosouzapw/OmniRoute/pull/12364)) — thanks @pacocartones +- **chore(quality):** re-tighten the file-size ratchet to the real LOC (plan 3.8.52 task 0) ([#12411](https://github.com/diegosouzapw/OmniRoute/pull/12411)) +- **test(grok):** format web executor suite ([#12412](https://github.com/diegosouzapw/OmniRoute/pull/12412)) +- **chore(quality):** ratchet open-sse typecheck baseline to zero ([#12418](https://github.com/diegosouzapw/OmniRoute/pull/12418)) +- **test(build):** guard the artifact path policy arrays against duplicates ([#12422](https://github.com/diegosouzapw/OmniRoute/pull/12422)) +- **chore(lint):** adopt eslint-plugin-react-hooks 7.1.1 ([#12428](https://github.com/diegosouzapw/OmniRoute/pull/12428)) +- **chore(providers):** remove a keyless provider integration at its operator's request ([#12440](https://github.com/diegosouzapw/OmniRoute/pull/12440)) +- **docs(providers):** register providers removed at their operator's request and guard against reintroduction ([#12478](https://github.com/diegosouzapw/OmniRoute/pull/12478)) +- **chore(quality):** base fixes — stryker tap.testFiles + node_modules cache key ([#12482](https://github.com/diegosouzapw/OmniRoute/pull/12482)) +- **docs(arch):** one-process recipe for tens of long /v1/responses ([#12493](https://github.com/diegosouzapw/OmniRoute/pull/12493)) — thanks @RaviTharuma +- **chore(quality):** tighten the CodeQL ratchet baseline from 11 to 6 ([#12530](https://github.com/diegosouzapw/OmniRoute/pull/12530)) +- **chore(deps):** pin browserslist override to ^4.28.8 ([#12592](https://github.com/diegosouzapw/OmniRoute/pull/12592)) — thanks @KooshaPari +- **chore(deps):** bump fast-uri to 3.1.7 in the electron lockfile ([#12601](https://github.com/diegosouzapw/OmniRoute/pull/12601)) +- **docs(quality):** document how the CodeQL ratchet refreshes and how to tighten it ([#12611](https://github.com/diegosouzapw/OmniRoute/pull/12611)) +- **test(ci):** pin the openapi-security-tiers two-arm contract with an executing gate test (#12581) ([#12652](https://github.com/diegosouzapw/OmniRoute/pull/12652)) +- **docs(gamification):** align XP Rewards table with code (#12501) ([#12667](https://github.com/diegosouzapw/OmniRoute/pull/12667)) — thanks @KooshaPari +- **refactor(combo):** split executeTarget into gates, attempt, and loop ([#12746](https://github.com/diegosouzapw/OmniRoute/pull/12746)) — thanks @HouMinXi +- **docs(security):** document socket.yml scanner config + CI workflow link (#12575) ([#12764](https://github.com/diegosouzapw/OmniRoute/pull/12764)) — thanks @KooshaPari +- **chore(ci):** guard commit identity in pre-commit to stop author misattribution ([#12772](https://github.com/diegosouzapw/OmniRoute/pull/12772)) +- **deps:** 17 Dependabot bumps — bump electron from 43.4.0 to 43.4.1 in /electron ([#11426](https://github.com/diegosouzapw/OmniRoute/pull/11426)); bump the development group across 1 directory with 8 updates ([#11428](https://github.com/diegosouzapw/OmniRoute/pull/11428)); bump the production group across 1 directory with 11 updates ([#11440](https://github.com/diegosouzapw/OmniRoute/pull/11440)); chore(deps): bump github/codeql-action/init from 4.37.7 to 4.37.8 ([#11925](https://github.com/diegosouzapw/OmniRoute/pull/11925)); chore(deps): bump github/codeql-action from 4.37.7 to 4.37.8 ([#11926](https://github.com/diegosouzapw/OmniRoute/pull/11926)); chore(deps): bump github/codeql-action/analyze from 4.37.7 to 4.37.8 ([#11927](https://github.com/diegosouzapw/OmniRoute/pull/11927)); build(deps): bump github/codeql-action/init from 4.37.8 to 4.37.9 ([#12345](https://github.com/diegosouzapw/OmniRoute/pull/12345)); build(deps): bump github/codeql-action/analyze from 4.37.8 to 4.37.9 ([#12346](https://github.com/diegosouzapw/OmniRoute/pull/12346)); bump the development group across 1 directory with 2 updates ([#12347](https://github.com/diegosouzapw/OmniRoute/pull/12347)); build(deps): bump github/codeql-action from 4.37.8 to 4.37.9 ([#12349](https://github.com/diegosouzapw/OmniRoute/pull/12349)); bump browserslist from 4.28.2 to 4.28.8 ([#12396](https://github.com/diegosouzapw/OmniRoute/pull/12396)); bump the production group across 1 directory with 4 updates ([#12399](https://github.com/diegosouzapw/OmniRoute/pull/12399)); bump @xmldom/xmldom ([#12500](https://github.com/diegosouzapw/OmniRoute/pull/12500)); bump qs from 6.15.2 to 6.16.0 ([#12512](https://github.com/diegosouzapw/OmniRoute/pull/12512)); bump @xmldom/xmldom from 0.9.10 to 0.9.12 ([#12513](https://github.com/diegosouzapw/OmniRoute/pull/12513)); bump fast-uri from 3.1.5 to 3.1.7 ([#12514](https://github.com/diegosouzapw/OmniRoute/pull/12514)); bump @humanfs/node from 0.16.7 to 0.16.8 ([#12515](https://github.com/diegosouzapw/OmniRoute/pull/12515)) +- Clear the `release/v3.8.51` `check:agent-skills-sync` base-red: regenerate `skills/cli-tunnel/SKILL.md` so the `tunnel create [type]` positional that #13009 taught the generator to read is reflected in the committed skill. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` typecheck base-red from #12731: the new `Mode pack ranking gates` candidates in `open-sse/services/autoCombo/__tests__/autoCombo.test.ts` omitted the required `provider`, `model` and `errorRate` fields of `ProviderCandidate`, failing `check:open-sse-typecheck` (and with it `Fast Quality Gates`) on every open PR. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` docs-sync base-red: EURouter (#13025) and GreenPT (#13024) took the live provider count to 358, leaving 7 STRICT drifts (`PROVIDER_REFERENCE.md`, 4 diagrams, 2 tier-flow images) plus the `AGENTS.md` / `llm.txt` / `package.json` count claims stale. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- **test(models):** Pin `CATALOG_BUILD_TIMEOUT_MS` in the #12058 canonical-catalog regression so a cold catalog build racing the 8s default bound no longer flakes the suite with a 500 ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Allow `@omniroute/opencode-plugin-v2/` in the npm pack policy — [#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870) shipped the v2 plugin beside its v1 sibling without widening the allowlist, so every packed file under it read as an unexpected artifact and failed `check:pack-policy` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Point the release-green validator's pack gate at the tree it is validating — the build-provenance guard defaulted to `origin/main`, which a release branch tip can never be an ancestor of mid-cycle, so `Package artifact` reported a false red on every run and the tarball boot-smoke cascaded off it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **docs(checks):** keep doc counts honest — headings, rankings, catalog, weights, quality gate and scoring diagram now covered ([#12507](https://github.com/diegosouzapw/OmniRoute/pull/12507)) — thanks @maxmad64bis +- **chore(lifecycle):** `check:model-lifecycle` now also diffs `DEFAULT_DEGRADATION_MAP` (the background-task redirect table) against the vendor lifecycle snapshot, refusing a retired id as source or target, with a table-driven unit test beside it. Three rows whose source the vendor had retired — `claude-sonnet-4-20250514`, `gemini-3-pro-preview` and `gpt-5.1-codex` (whose target `gpt-5.1-codex-mini` is retired too) — were dead code, since `checkLifecycle` answers 410 before the redirect runs; they are dropped (#12535 — thanks @pacocartones) +- **test(compression):** cover idle worker eviction at the resource level — the pool must call `terminate()` and must not retain the worker's `MessagePort`, complementing the `exit`-event assertion added with the fix ([#13371](https://github.com/diegosouzapw/OmniRoute/pull/13371)) +- **chore(quality):** raise the `imageGeneration.ts` file-size ceiling for the image-only-model guard that clears the #12945 base-red ([#13392](https://github.com/diegosouzapw/OmniRoute/pull/13392)) +- **chore(quality):** type the combo-test route's JSON response bodies instead of casting them to `any`, drop the now-empty suppression entry, and remove the `hasPerModelQuota` import `chatCore.ts` stopped using when the failure-classification helper was extracted ([#13379](https://github.com/diegosouzapw/OmniRoute/pull/13379)) +- **chore(electron):** the desktop bundle no longer ships the root-level files of every translated docs mirror (`README.md`, `llm.txt`, `CLAUDE.md`, `GEMINI.md`, `CONTRIBUTING.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`) — the packaged app only reads `docs/i18n//docs/**`, which stays. Saves ~11 MB on top of the translated CHANGELOGs already pruned. (#0000) ([#13235](https://github.com/diegosouzapw/OmniRoute/pull/13235)) +- **chore(quality):** retighten the `src/sse/handlers/chat.ts` unused-vars suppression count to the 8 violations that actually remain ([#13385](https://github.com/diegosouzapw/OmniRoute/pull/13385)) +- Bring the documented counts back in line with the code: 171 migrations (was 169) and 20 routing strategies (was 19), plus the regenerated `cli-tunnel` skill reference. ([#12970](https://github.com/diegosouzapw/OmniRoute/pull/12970)) +- Update the documented migration count to 172 after the call-logs provider-stats indexes landed. ([#13209](https://github.com/diegosouzapw/OmniRoute/pull/13209)) +- **chore(ci):** two gates that close the blind spots behind the exclusions above. `check:vitest-exclusions` requires every Vitest exclusion to name a tracking issue and to appear in `config/quality/vitest-exclusions.json` — the previous list grew to 62 files behind a comment pointing at an issue that had been closed for a month. `check-new-key-coverage` requires a key newly added to `en.json` to reach every locale; the existing coverage gate is a percentage floor per locale, so eleven absent keys out of ~13,000 left it at 99.9% while a whole feature shipped untranslated in nine languages. Both are diff-aware, so pre-existing debt stays frozen and neither needed a migration to turn on. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **build:** root layout no longer loads Inter from `next/font/google`, so a production image build does not need fonts.googleapis.com ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **docs(i18n):** bring the eight new `llm.txt` mirrors (km, kn, ml, my, ne, or, pa, si) up to the current provider and migration counts, so `check-docs-sync` stops rejecting every commit ([#13674](https://github.com/diegosouzapw/OmniRoute/pull/13674)) +- **refactor(ui):** flow surfaces (home topology, combo live studio, compression cockpit/waterfall/nodes, token-health badge) express state through the theme-aware `--orch-status-*` tokens instead of fixed dark-mode hex, so green/red/amber/grey stay legible in the light theme; dark mode is byte-identical. Categorical palettes (routing-strategy hues, compression-layer pills, provider brand colors) deliberately stay hex ([#12378](https://github.com/diegosouzapw/OmniRoute/issues/12378)) ([#13203](https://github.com/diegosouzapw/OmniRoute/pull/13203)) +- **chore(tests):** 51 test files that had been excluded from Vitest are running again, restoring roughly 350 assertions to the blocking suite. Each was measured individually first: of the 62 files parked behind the `// #8618 — pre-existing failure` comment, 51 pass against the current tree with no source change, so the exclusions had outlived the failures they were added for. The 11 that genuinely still fail stay excluded, but now point at a live tracker (#13204) rather than at #8618, which was closed in August while the list it tracked kept growing. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **chore(quality):** drop two ESLint suppression entries whose violations no longer exist, so `eslint --suppressions-location` stops rejecting every commit that touches the surrounding files ([#13384](https://github.com/diegosouzapw/OmniRoute/pull/13384)) +- **chore(release):** bring main's post-tag fixes into release/v3.8.51 — sync-back step 2/2 (direct commit `21c488f210`) +- **chore(deps):** patch toml and esbuild in the opencode plugin lockfiles ([#12965](https://github.com/diegosouzapw/OmniRoute/pull/12965)) +- **docs(changelog):** reconcile the v3.8.51 living section — fold 366 fragments, cover every cycle commit, credit every contributor ([#12971](https://github.com/diegosouzapw/OmniRoute/pull/12971)) +- **test(plugin):** wait for the v2 lazy-refresh tier instead of sleeping past it ([#12984](https://github.com/diegosouzapw/OmniRoute/pull/12984)) +- **chore(quality):** rebaseline chatCore.ts after the non-streaming regression fixes ([#13045](https://github.com/diegosouzapw/OmniRoute/pull/13045)) +- **docs(changelog):** add the missing bullet marker to the DeepSeek PoW fragment ([#13200](https://github.com/diegosouzapw/OmniRoute/pull/13200)) +- **chore(deps):** drain the Dependabot queue — 10 of 13 alerts ([#13213](https://github.com/diegosouzapw/OmniRoute/pull/13213)) +- **chore(ci):** make worktrees carry the hooks that enforce the identity gate ([#13236](https://github.com/diegosouzapw/OmniRoute/pull/13236)) +- **chore(deps):** pin csv-parse to ^7.0.2 via overrides (#13117) ([#13300](https://github.com/diegosouzapw/OmniRoute/pull/13300)) — thanks @KooshaPari +- **chore(deps):** bump hono from ^4.12.34 to ^4.13.7 (#13148) ([#13301](https://github.com/diegosouzapw/OmniRoute/pull/13301)) — thanks @KooshaPari +- **chore(deps):** pin joi to ^18.2.8 via overrides (#13085) ([#13302](https://github.com/diegosouzapw/OmniRoute/pull/13302)) — thanks @KooshaPari +- **docs(providers):** add Microsoft 365 Copilot (BizChat) provider guide (#12779) ([#13340](https://github.com/diegosouzapw/OmniRoute/pull/13340)) — thanks @KooshaPari +### 🙌 Contributors + +Thanks to everyone whose work landed in v3.8.51: + +| Contributor | PRs / Issues | +| --- | --- | +| [@5dive-bot](https://github.com/5dive-bot) | #11852 | +| [@abhisheksharma2411](https://github.com/abhisheksharma2411) | #11547, #11684, #11959, #12209 | +| [@adevwithpurpose](https://github.com/adevwithpurpose) | #11464 | +| [@adivekar-utexas](https://github.com/adivekar-utexas) | #12015, #12043, #12090, #12138 | +| [@AIB1TAL0S](https://github.com/AIB1TAL0S) | #12103 | +| [@alltomatos](https://github.com/alltomatos) | #11448 | +| [@alvinveroy](https://github.com/alvinveroy) | #11857, #12027, #12165 | +| [@amaleta](https://github.com/amaleta) | #11906, #11934 | +| [@amartinawi](https://github.com/amartinawi) | #11575 | +| [@AndrianBalanescu](https://github.com/AndrianBalanescu) | #11083, #11830, #11834, #11844 | +| [@anhtahaylove](https://github.com/anhtahaylove) | #13091, #13092, #13093, #13096, #13097, #13100, #13106, #13109, #13114, #13155, #13166, #13171, #13175, #13187, #13196 | +| [@aniruddhaadak80](https://github.com/aniruddhaadak80) | #11760 | +| [@app](https://github.com/app) | #12554 | +| [@arminanton](https://github.com/arminanton) | #11461, #11513 | +| [@AStupidBear](https://github.com/AStupidBear) | #11584 | +| [@atescivitci-cmd](https://github.com/atescivitci-cmd) | #12105 | +| [@b3nw](https://github.com/b3nw) | #11970, #11971, #12147 | +| [@backryun](https://github.com/backryun) | #11259, #11950, #12075, #12076, #12078, #12079, #12081, #12082, #12181, #12228, #12239, #12255, #12258, #12277, #12367, #12423, #12524, #12538 | +| [@benjaminkitt](https://github.com/benjaminkitt) | #11747 | +| [@benzntech](https://github.com/benzntech) | #11614, #11615 | +| [@Bl0ck154](https://github.com/Bl0ck154) | #11948, #11951, #11952, #11953, #11954 | +| [@brick30llc-ctrl](https://github.com/brick30llc-ctrl) | #11752, #12242, #12262 | +| [@Chewji9875](https://github.com/Chewji9875) | #11455, #11875, #12028 | +| [@cheynetom](https://github.com/cheynetom) | #12058 | +| [@davidebaraldo](https://github.com/davidebaraldo) | #12222, #12304 | +| [@davidlinfr](https://github.com/davidlinfr) | #6390 | +| [@ddarkr](https://github.com/ddarkr) | #12124 | +| [@Deftera186](https://github.com/Deftera186) | #11809 | +| [@dpozimski](https://github.com/dpozimski) | #11945 | +| [@DW-MediaLab](https://github.com/DW-MediaLab) | #12229 | +| [@echel0nn](https://github.com/echel0nn) | #11923 | +| [@f9td56dbgh-hub](https://github.com/f9td56dbgh-hub) | #11660 | +| [@fabioluissilva](https://github.com/fabioluissilva) | #11991 | +| [@foreveryh](https://github.com/foreveryh) | #12177 | +| [@geek007git](https://github.com/geek007git) | #12115, #12116, #12117, #12120, #12122 | +| [@geekyNads](https://github.com/geekyNads) | #11784, #12266 | +| [@ggiak](https://github.com/ggiak) | #11561, #12276, #12350, #12402 | +| [@gonisulaimann](https://github.com/gonisulaimann) | #12368, #12369, #12371 | +| [@Gorillaz322](https://github.com/Gorillaz322) | #12207 | +| [@groovecityJO](https://github.com/groovecityJO) | #12682 | +| [@hartmark](https://github.com/hartmark) | #11434, #11452, #11473, #11499, #11703, #11983, #11984, #11985, #11986, #11988, #11989, #11990, #11994, #12221, #12293, #12445, #12446, #12447, #12448, #12460, #12461, #12623, #12646, #12650, #12680, #12717, #12718, #12727, #12741, #12854 | +| [@hizzt](https://github.com/hizzt) | #11894 | +| [@hongnoul](https://github.com/hongnoul) | #11484 | +| [@HouMinXi](https://github.com/HouMinXi) | #11411, #11414, #11512, #11518, #11520, #11641, #11642, #11643, #11687, #11779, #11849, #11850, #11851, #11915, #11916, #11918, #11919, #11920, #12013, #12017, #12033, #12042, #12106, #12139, #12166, #12169, #12171, #12205, #12213, #12312, #12325, #12487, #12488, #12495, #12504, #12557, #12566, #12590, #12591, #12624, #12626, #12632, #12637, #12678, #12696, #12697, #12711, #12733, #12746, #12767, #12770, #12789, #12803, #12805, #12811, #12866, #12868, #12899, #12926, #12934, #12950, #12951, #12974, #13001, #13006, #13011, #13017, #13026, #13027, #13034, #13035, #13038, #13042, #13050, #13060, #13061, #13069, #13107, #13120, #13136, #13195, #13197 | +| [@Hsia97](https://github.com/Hsia97) | #11624 | +| [@jacobsparts](https://github.com/jacobsparts) | #11854, #12155, #12167 | +| [@joglomedia](https://github.com/joglomedia) | #11980 | +| [@jonlwheat2-gif](https://github.com/jonlwheat2-gif) | #11443, #11450, #11549, #11567, #11608, #11633, #11635, #11640, #11644, #11671, #11672, #11673, #11674, #11675, #11676, #11677, #11706, #11783, #12051, #12052, #12053, #12055 | +| [@kanade-hoshino](https://github.com/kanade-hoshino) | #12180 | +| [@Karan825](https://github.com/Karan825) | #11861 | +| [@KaspaPulse](https://github.com/KaspaPulse) | #11389, #11469, #11628, #11666, #11910 | +| [@keeltrace](https://github.com/keeltrace) | #12080, #12223 | +| [@killer30001000](https://github.com/killer30001000) | #12178 | +| [@KooshaPari](https://github.com/KooshaPari) | #12470, #12592, #12667, #12699, #12703, #12706, #12764, #12769, #12771, #13300, #13301, #13302, #13340, #13401, #13403, #13404, #13405, #13406, #13407, #13409, #13410, #13411, #13412, #13413, #13414, #13418, #13424, #13427, #13433, #13523, #13524, #13525, #13528, #13531, #13532, #13534, #13539, #13543, #13545, #13546, #13547, #13550, #13553 | +| [@kriptoburak](https://github.com/kriptoburak) | #11370 | +| [@KrzysiekSko](https://github.com/KrzysiekSko) | #12425, #12432, #12673 | +| [@marcelokarval](https://github.com/marcelokarval) | direct commit / report | +| [@maxmad64bis](https://github.com/maxmad64bis) | #1622, #11435, #11437, #11441, #11537, #11550, #11553, #11555, #11557, #11812, #11842, #11843, #11903, #12151, #12214, #12215, #12218, #12226, #12314, #12316, #12317, #12318, #12319, #12320, #12321, #12507, #12715, #12731, #12744, #12786, #12787, #12788, #12790, #12792, #12794, #12795, #12828, #12832, #12853, #12857, #12870, #12937, #12941, #12975, #13141, #13142, #13146, #13147 | +| [@mdigitalbh81](https://github.com/mdigitalbh81) | #12240 | +| [@Meet6338-X](https://github.com/Meet6338-X) | #11598, #11609, #12110 | +| [@morpheus9393](https://github.com/morpheus9393) | #11943 | +| [@MumuTW](https://github.com/MumuTW) | #11492, #11502, #11506, #11507, #11626, #11675, #11685, #11728, #11746, #11888, #11889, #11890, #11892 | +| [@Neuron-Mr-White](https://github.com/Neuron-Mr-White) | #11622, #11801, #12256 | +| [@NightStalker-87](https://github.com/NightStalker-87) | #12183 | +| [@NoxzRCW](https://github.com/NoxzRCW) | #11879, #11880, #11881, #11882, #11883, #11935 | +| [@ntdat812](https://github.com/ntdat812) | #11585, #12095 | +| [@ntdatt812](https://github.com/ntdatt812) | #11368, #11573, #11574, #11576, #11577, #11580, #11582, #11583, #11588, #11589, #11590, #11591, #11592, #11593, #11672, #12177, #12180, #12873, #12918, #12920, #12921, #12925, #12930, #13007, #13009, #13024, #13025, #13083, #13087, #13101, #13104, #13110 | +| [@official-burak](https://github.com/official-burak) | #11542 | +| [@opensource-elearning](https://github.com/opensource-elearning) | #12179, #12189, #12278, #12286 | +| [@oyi77](https://github.com/oyi77) | #11408, #11409, #11421, #11505, #11677, #12036, #12110 | +| [@pacocartones](https://github.com/pacocartones) | #11521, #11522, #11527, #11528, #11529, #11530, #11531, #11532, #11533, #11534, #11595, #11599, #11603, #11604, #11605, #11607, #11610, #11676, #11714, #11716, #11718, #11767, #11838, #11860, #11862, #11869, #11871, #11872, #11873, #11903, #11906, #11921, #11934, #12356, #12358, #12359, #12360, #12361, #12362, #12364, #12365, #12373, #12374, #12375, #12376, #12377, #12379, #12380, #12381, #12386, #12387, #12389, #12390, #12394, #12395, #12397, #12401, #12403, #12404, #12522, #12523, #12535, #12536, #12540, #12541, #12543, #12545, #12548, #12549, #12550, #12551, #12552, #12647, #12651, #12653, #13055 | +| [@patrykkopycinski](https://github.com/patrykkopycinski) | #11936, #11937, #12224 | +| [@PauloFH](https://github.com/PauloFH) | #11509 | +| [@PauloHSOliveira](https://github.com/PauloHSOliveira) | #12241 | +| [@ponkcore](https://github.com/ponkcore) | #12054 | +| [@Prajeeth-12](https://github.com/Prajeeth-12) | #11634, #12046 | +| [@quiterunner-commits](https://github.com/quiterunner-commits) | #12131, #12143 | +| [@rafacpti23](https://github.com/rafacpti23) | #11554, #11558, #12192, #12197, #12198, #12202, #12203, #12204 | +| [@ragnar-claude](https://github.com/ragnar-claude) | #11564 | +| [@raheemuddin786](https://github.com/raheemuddin786) | #11491, #11839, #11840, #11841, #12003, #12230, #12231, #12232, #12233, #12234 | +| [@rahilmavani](https://github.com/rahilmavani) | #11761 | +| [@Rahulsharma0810](https://github.com/Rahulsharma0810) | #11771 | +| [@RaviTharuma](https://github.com/RaviTharuma) | #11710, #11727, #11797, #11798, #11802, #11805, #11806, #11811, #12098, #12099, #12101, #12449, #12452, #12472, #12473, #12493, #12533, #12607, #12628, #12631, #12636, #12875, #12876, #12880, #12882, #12884 | +| [@rezjalibd](https://github.com/rezjalibd) | #12186 | +| [@RhianB14](https://github.com/RhianB14) | #11692 | +| [@rifqiawl](https://github.com/rifqiawl) | #11517, #11519, #11969 | +| [@rqzbeh](https://github.com/rqzbeh) | #11390 | +| [@Sabeekhann](https://github.com/Sabeekhann) | #11699, #11705 | +| [@santosraju99-hub](https://github.com/santosraju99-hub) | #11755, #11770 | +| [@Seramicx](https://github.com/Seramicx) | #11568 | +| [@Siva010](https://github.com/Siva010) | #12191 | +| [@solstxce](https://github.com/solstxce) | #11597 | +| [@soroush5](https://github.com/soroush5) | #12691 | +| [@tenshiak](https://github.com/tenshiak) | #12279 | +| [@TheDemonTuan](https://github.com/TheDemonTuan) | #11468, #11470, #11471, #11482, #11548, #11758, #11775, #11814 | +| [@thomasmaerz](https://github.com/thomasmaerz) | #12834 | +| [@tuandinh0801](https://github.com/tuandinh0801) | #11454 | +| [@turbolego](https://github.com/turbolego) | #11621, #11762, #11772, #11774, #11781, #12216 | +| [@Tushar49](https://github.com/Tushar49) | #11392 | +| [@ujjawalkaushik1110](https://github.com/ujjawalkaushik1110) | #11863 | +| [@vermasomesh835](https://github.com/vermasomesh835) | #11794 | +| [@vsd2807](https://github.com/vsd2807) | #11565, #11619 | +| [@wahidsadik371-coder](https://github.com/wahidsadik371-coder) | #12126 | +| [@watchingdogs](https://github.com/watchingdogs) | #12031 | +| [@wildcard](https://github.com/wildcard) | #11620 | +| [@xiaoyaner0201](https://github.com/xiaoyaner0201) | #11460, #11662, #12083 | +| [@Xxx91n](https://github.com/Xxx91n) | #11690 | +| [@yourspraveen](https://github.com/yourspraveen) | #11146 | +| [@yxyxy](https://github.com/yxyxy) | #11671 | +| [@ZaimMarzuki](https://github.com/ZaimMarzuki) | #11960 | +| [@Zartharas](https://github.com/Zartharas) | #11340 | +| [@zero-executioner](https://github.com/zero-executioner) | #11631 | +| [@diegosouzapw](https://github.com/diegosouzapw) | maintainer | + +--- ## [3.8.50] — 2026-08-25 _Living section — regenerated 2026-08-12 from all cycle commits (cycle open `ed2db6cb19` → tip). Bullets carry the merged PR and its author; direct pushes listed separately._ diff --git a/docs/i18n/es/CHANGELOG.md b/docs/i18n/es/CHANGELOG.md index ed2ccd154a..7bf49bffdf 100644 --- a/docs/i18n/es/CHANGELOG.md +++ b/docs/i18n/es/CHANGELOG.md @@ -8,16 +8,1119 @@ ## [3.8.51] — TBD -_Living section — cycle opened at the v3.8.50 freeze (parallel-cycle model). Bullets are aggregated from `changelog.d/` fragments at each `/generate-release` phase._ +_Living section — reconciled 2026-09-15 from all cycle commits (`091589089c` → `c0f92ec98a`, 916 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 | **123** | +| 📝 Commits in the cycle | **916** | +| 🔀 Pull requests referenced | **892** | +| 📋 Changelog entries | **919** | +| 🙌 Contributors credited in entries | **121** | +| 🤖 Automated dependency commits | 18 | + +**Entries by type** + +| Type | Count | +| --- | ---: | +| 🐛 Fixes | 592 | +| ✨ Features | 131 | +| 🧹 Chore | 93 | +| 📚 Docs | 42 | +| 🧪 Tests | 32 | +| ♻️ Refactor | 8 | +| 🏗️ Build | 7 | +| ⚡ Performance | 7 | +| ⚙️ CI | 2 | +| ⏪ Reverts | 2 | +| 📦 Dependencies | 2 | +| 🔒 Security | 1 | + +### 🏆 Top 25 contributors this cycle + +_By commits in `091589089c..c0f92ec98a`, author identities consolidated via `.mailmap` and the merged PR's GitHub login. Bots excluded._ + +| # | Contributor | Commits | +| ---: | --- | ---: | +| 🥇 | diegosouzapw | 323 | +| 🥈 | Bob.Hou (@HouMinXi) | 79 | +| 🥉 | Paco Cartones (@pacocartones) | 77 | +| 4 | Dizzle (@maxmad64bis) | 49 | +| 5 | Koosha Paridehpour (@KooshaPari) | 43 | +| 6 | Nguyen Thanh Dat (@ntdatt812) | 31 | +| 7 | Ravi Tharuma (@RaviTharuma) | 26 | +| 8 | Markus Hartung (@hartmark) | 22 | +| 9 | Webman (@jonlwheat2-gif) | 22 | +| 10 | backryun | 17 | +| 11 | anhtahaylove | 15 | +| 12 | Rafa Martins (@rafacpti23) | 10 | +| 13 | Syed Raheemuddin (@raheemuddin786) | 10 | +| 14 | Nguyễn Viết Tuấn (@TheDemonTuan) | 8 | +| 15 | MumuTW | 7 | +| 16 | Paijo (@oyi77) | 7 | +| 17 | Tobias Andersen (@turbolego) | 6 | +| 18 | Bl0ck (@Bl0ck154) | 5 | +| 19 | SHANMUGAPRIYAN (@geek007git) | 5 | +| 20 | KaspaPulse | 5 | +| 21 | Abhishek Sharma (@abhisheksharma2411) | 4 | +| 22 | Abhishek Divekar (@adivekar-utexas) | 4 | +| 23 | Andrew B. (@AndrianBalanescu) | 4 | +| 24 | NoxzRCW | 4 | +| 25 | opensource-elearning | 4 | ### ✨ New Features +- **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). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- Added Google AI Studio Gemini batch text-to-speech support through `POST /v1/audio/speech`. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- 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. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(routing):** subscription-first auto groupings — `auto/subscription` routes only through plan-included connections with a documented hard-stop overage and fails closed on exhaustion, while `auto/thrifty` orders the pool `subscription → keyless → free → cheap → premium` and steps up one rung at a time as each is exhausted. Billing class comes from a curated per-connection catalog (uncurated is treated as metered, never plan-included), both reuse STRICT_ZERO_COST's per-connection verification, and a quota reading whose `resetAt` has passed is now refreshed regardless of TTL so routing returns to plan capacity as soon as it resets ([#11146](https://github.com/diegosouzapw/OmniRoute/pull/11146)) — thanks @yourspraveen +- **feat(providers):** publish a management-authenticated versioned web-session credential contract from OmniRoute's canonical browser credential metadata ([#11340](https://github.com/diegosouzapw/OmniRoute/pull/11340)) — thanks @Zartharas +- **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)) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add Xquik X search with typed results, credential validation, REST routing, and MCP selection ([#11370](https://github.com/diegosouzapw/OmniRoute/pull/11370)) — thanks @kriptoburak +- **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)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(dashboard):** surface durable exclusive managed leases in the existing Sessions view, keeping leased clients visible across idle gaps while marking connections with in-flight work as active ([#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389)) — thanks @KaspaPulse +- **build(bun):** allow Turbopack bundler flag on Bun 1.4+ with configurable Webpack fallback ([#11471](https://github.com/diegosouzapw/OmniRoute/pull/11471)) — thanks @TheDemonTuan +- feat(api): add an opt-in `modelVisibilityAllowlist`/`modelVisibilityDenylist` settings pair to curate exactly which models `/v1/models` advertises, mirrored into every `auto/*` combo candidate pool so a denied model cannot be routed to via combo selection either (#11481) ([#11997](https://github.com/diegosouzapw/OmniRoute/pull/11997)) +- **feat(rankings):** the Free Provider Rankings page shows what each provider actually served over the last 24 h. It ranked by ELO alone, which left a provider that answers every call with an error in first place; the usage data was already served by the API but never requested. A provider with too small a sample shows a dash, not a number ([#11546](https://github.com/diegosouzapw/OmniRoute/pull/11546)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **feat(guardrails):** enforce a bounded, deterministic contract for Video Bridge transcripts — 256 cues, 4096 input code units and 4 KiB UTF-8 per cue, 64 KiB total text, malformed-Unicode rejection, focus-window scoping, cross-source reconciliation with contributing-source metadata, and a structural provenance trust boundary so caller JSON can never self-assert `embedded`/`audio-bridge` provenance ([#11652](https://github.com/diegosouzapw/OmniRoute/issues/11652)) ([#12009](https://github.com/diegosouzapw/OmniRoute/pull/12009)) +- **feat(video):** orchestrate optional Video Bridge audio extraction and Audio Bridge STT behind a dual opt-in (operator setting AND per-request signal) — a new loopback-only broker `mode=audio` operation shares the frame path's exact process queue, deadline, AbortSignal, and byte budgets to extract a bounded mono 16 kHz PCM WAV from the same already-downloaded video, then reuses the existing Audio Bridge transcription boundary; provider segment timing is preserved when available and marked coarse otherwise, and every failure degrades to a visual-only-safe partial instead of throwing (#11654). ([#12012](https://github.com/diegosouzapw/OmniRoute/pull/12012)) +- Add a tenant-bound Video Bridge drill-down lifecycle on top of the existing secure cache substrate: opaque hashed handles (never raw session/video identifiers), preview/standard/detail multiresolution variants resampled on read, response pagination capped at 8 frames and 32 MiB, and a new authenticated `/api/v1/video-bridge/drilldown` consumer route that stays disabled for remote access by default and denies cross-key access with the same response as a nonexistent handle (no existence oracle). ([#12006](https://github.com/diegosouzapw/OmniRoute/pull/12006)) +- **test(video):** Add the Video Bridge FU-07/FU-09 promotion-evidence harness (#11656) — a frozen Zod manifest schema covering the 8 required scenario kinds (static scenes, rapid cuts, late facts, fades, blur, small text, close events, visual prompt injection) with a minimum of 3 repetitions per case, deterministic declarative fixture recipes (`videoBridgePromotionFixtures.ts`), a pure medians/p95 metrics aggregator, a pure FU-07/FU-09 promotion-verdict evaluator applying the ticket's exact thresholds (missing token usage always holds), a digest-only persistence layer that never retains raw media or raw model responses, and a versioned per-model promotion allowlist shipped empty with every model defaulting to `hold`. The FU-07/FU-09 promotion verdicts themselves remain HOLD — they require a real evidence run against real models on VPS 192.168.0.15. ([#12008](https://github.com/diegosouzapw/OmniRoute/pull/12008)) +- **feat(video bridge):** "embedded" transcript provenance can now be legitimately earned instead of merely asserted — a bounded, allowlisted (`mov_text`/`subrip`/`webvtt`) subtitle probe runs through the loopback-only Video Bridge broker (at most 2 streams, 10s subdeadline bounded by the request deadline, 256 KiB output, 4096-code-unit lines), normalized through a bounded, ReDoS-safe WebVTT parser and Zod-validated end to end. The adapter always resolves to an explicit `success`/`absent`/`transient_failure` outcome — a subtitle failure never breaks the visual description path, and only a fingerprint-verified broker response (never a caller-declared label) can produce embedded cues (#11659). ([#12011](https://github.com/diegosouzapw/OmniRoute/pull/12011)) +- Default new Antigravity-family connections (agy CLI imports and Antigravity OAuth connects) to model auto-sync, so live model discovery lands in the synced catalog and `/v1/models` picks up freshly released upstream models (e.g. Gemini 3.7 Flash tiers) without code changes. Existing connections keep their current setting; the per-connection dashboard toggle remains the opt-out. (#11685 — thanks @MumuTW) ([#11887](https://github.com/diegosouzapw/OmniRoute/pull/11887)) +- **feat(ui):** enable React Compiler (`reactCompiler: true` + `babel-plugin-react-compiler`) for automatic memoization at build time ([#11783](https://github.com/diegosouzapw/OmniRoute/pull/11783)) — thanks @jonlwheat2-gif +- **feat(providers):** add SeekAi (`seekai.cc`) as an OpenAI-compatible New-API gateway — catalog id `seekai` (alias `ska`), `https://seekai.cc/v1`, live `/v1/models` via `passthroughModels`, aggregator-list membership so New-API balance detection can opt in. No referral/aff codes. ([#11786](https://github.com/diegosouzapw/OmniRoute/issues/11786)) ([#12557](https://github.com/diegosouzapw/OmniRoute/pull/12557)) — thanks @HouMinXi +- **feat(zai):** add GLM-5.3-Flash Coding Plan support (1M context, 128K output, vision, `low|high|max` reasoning) and route `zai` GLM-5.3-family API-key traffic through the OpenAI-compatible Coding Plan endpoint with native thinking defaults ([#11801](https://github.com/diegosouzapw/OmniRoute/pull/11801)) — thanks @Neuron-Mr-White +- **feat(combo):** choose how combo models are ordered — manual, provider, score, or name — via a sort control in the dashboard builder, persisted in `config.modelSort` and re-applied on load and after add ([#11812](https://github.com/diegosouzapw/OmniRoute/pull/11812)) — thanks @maxmad64bis +- **feat(free):** custom models can be marked free-tier via `customModels[].isFree`; `isFreeModel()` is the first door and `hidePaidModels` respects it even for providers outside the free budget ([#11843](https://github.com/diegosouzapw/OmniRoute/pull/11843)) — thanks @maxmad64bis +- **feat(nodejs):** add `5dive` as a `configure` target — `omniroute configure 5dive` / `omniroute setup-5dive` write a 5dive auth profile that points an agent fleet's `claude` seats at OmniRoute, with the root-only write, the loopback-vs-`https` endpoint rule and the per-seat model pin handled explicitly ([#11852](https://github.com/diegosouzapw/OmniRoute/pull/11852)) — thanks @5dive-bot +- **feat(sse):** treat `max` as a first-class reasoning-effort tier and clamp per model family (GLM 5.1+/DeepSeek V4+/Kimi K3+ keep native `max`; o1/MiniMax/Grok/Muse Spark clamp to their upstream ceiling) ([#11875](https://github.com/diegosouzapw/OmniRoute/pull/11875)) — thanks @Chewji9875 +- **feat(providers):** the provider plugin manifest now advertises a `usage-fetch` capability for the 40 providers that have a wired usage/quota fetcher, so external dashboards can read it from `GET /api/v1/provider-plugin-manifest` instead of parsing `open-sse/services/usage.ts` after every release. Discovery only — no new fetcher, no quota change, and the Dashboard quota widget stays gated by `USAGE_SUPPORTED_PROVIDERS`. `USAGE_FETCHER_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/fetcherProviders.ts`) and is re-exported from `services/usage.ts`, keeping the manifest module a light leaf instead of pulling the ~490-module usage dispatcher into the manifest route. ([#11903](https://github.com/diegosouzapw/OmniRoute/pull/11903)) — thanks @maxmad64bis / @pacocartones +- **feat(plugins):** `OMNIROUTE_PLUGINS_DIR` sets the directory the runtime plugin scanner reads — and the root the plugin manager installs into — overriding the `HOME`-derived default, so a Docker/K8s deployment can point straight at its bind-mounted plugin tree instead of moving `HOME` just to relocate the scan path. An image that exports no home no longer scans `/tmp/.omniroute/plugins` in silence: the resolved directory is logged once at startup as `scanner.dir_resolved`, naming the input that won. Unset, behaviour is unchanged. Distinct from the CLI-only `OMNIROUTE_PLUGIN_PATH`, which finds `omniroute-cmd-*` command packages and never reached this scanner ([#11906](https://github.com/diegosouzapw/OmniRoute/pull/11906)) — thanks @amaleta / @pacocartones +- **feat(leases):** add an explicit owner-authenticated status action that returns only the active lease's privacy-safe configured connection and provider labels, with generation fencing and no credential or internal-id disclosure ([#11910](https://github.com/diegosouzapw/OmniRoute/pull/11910)) — thanks @KaspaPulse +- **feat(providers):** import-from-file modal shows per-row API errors and ships a downloadable CSV template ([#12071](https://github.com/diegosouzapw/OmniRoute/issues/12071)) ([#12504](https://github.com/diegosouzapw/OmniRoute/pull/12504)) — thanks @HouMinXi +- **feat(providers):** dashboard search matches connection name and `baseUrl` so imported OpenAI-compat nodes surface on the provider card ([#12108](https://github.com/diegosouzapw/OmniRoute/issues/12108)) ([#12495](https://github.com/diegosouzapw/OmniRoute/pull/12495)) — thanks @HouMinXi +- **feat(routing):** add a `score` Auto router strategy that selects the highest configured weighted score and reuses `explorationRate`. ([#12155](https://github.com/diegosouzapw/OmniRoute/pull/12155)) — thanks @jacobsparts +- Add a runtime feature flag to disable universal context handoffs globally without changing the default behavior. ([#12167](https://github.com/diegosouzapw/OmniRoute/pull/12167)) — thanks @jacobsparts +- **feat(providers):** the provider plugin manifest now also advertises a `usage-supported` capability for the 46 providers whose usage API is accepted by the server and Dashboard routes, so integrators can distinguish "the server will serve quota for this provider" from "a fetcher is wired" without reading TypeScript. Discovery only — no fetcher or quota change. `usage-fetch` resolves on id or alias (the usage dispatcher accepts both); `usage-supported` resolves on id alone, matching the runtime guard `USAGE_SUPPORTED_PROVIDERS.includes(providerId)`. `USAGE_SUPPORTED_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/supportedProviders.ts`) and is re-exported from `providers.ts`, mirroring the `fetcherProviders` leaf from #11903 and keeping the manifest a light module. ([#12214](https://github.com/diegosouzapw/OmniRoute/pull/12214)) — thanks @maxmad64bis +- **feat(rankings):** order Free Provider Rankings by what each provider actually served — `GET /api/free-provider-rankings?sortBy=reliability` and a "Most reliable first" toggle on the page. Providers with too few calls to state a success rate keep their score order below the measured ones; the default order is unchanged ([#12218](https://github.com/diegosouzapw/OmniRoute/pull/12218)). — thanks @maxmad64bis +- **perf(sse):** defer `cloneLogPayload()` in the structured SSE collector until after the `maxEvents`/`maxBytes` cap check, eliminating ~9,800 wasted `structuredClone` calls per streaming response (65–71% faster `push()`). Reducer snapshot isolation restored for OpenAI and Responses summaries ([#12241](https://github.com/diegosouzapw/OmniRoute/pull/12241)) — thanks @PauloHSOliveira ([#12243](https://github.com/diegosouzapw/OmniRoute/pull/12243)) +- **feat(usage):** Devin CLI agentic quota (Codeium seat-management GetUserStatus) and OpenRouter key limits plus account credits now surface in Provider Limits ([#12256](https://github.com/diegosouzapw/OmniRoute/pull/12256) — thanks @Neuron-Mr-White) +- **feat(settings):** persist `headroomUrl` through Settings so status/start use the operator URL instead of only `HEADROOM_URL` ([#12306](https://github.com/diegosouzapw/OmniRoute/issues/12306)) ([#12487](https://github.com/diegosouzapw/OmniRoute/pull/12487)) — thanks @HouMinXi +- **feat(auto-combo):** Auto-Combo scoring can now weigh how often a provider/model has actually succeeded. The engine already carried that number on every candidate — 24 hours of usage history behind a ten-sample floor, real-time metrics otherwise — and the scoring function never read it, while the weight table described `stability` as if it did. `reliability` (`1 - failureRate`, with the same field precedence and the same rate-bounding the speed ranking already uses, so a corrupt reading means "nothing observed" rather than "fails every call") is now a declared factor shipping at weight `0`, so routing is unchanged until an operator gives it one, and the `stability` description now matches what that factor computes ([#12317](https://github.com/diegosouzapw/OmniRoute/pull/12317)) — thanks @maxmad64bis +- **feat(routing):** With `freeAccessPolicy: "strict"`, the read-only candidate listing (`GET /v1/auto-combo/{channel}/candidates`) no longer hides the candidates the zero-cost guard excludes — the same read-only transparency the resilience filter already honours (#9133). Each candidate now carries `freeAccessExclusion` saying why it would be kept out, and it tells an exhausted allowance apart from a quota reading that never arrived or went stale, which used to look identical from the outside. Routing is unchanged: the listing reports, it never enforces. The separate `excludeTosAvoid` guard still drops its candidates without a reason; that gap is now documented rather than closed ([#12319](https://github.com/diegosouzapw/OmniRoute/pull/12319)) — thanks @maxmad64bis +- **feat(radar):** The Radar catalog table now shows two facts it was already receiving from the feed and dropping on the floor: the per-model rate limits (requests and tokens, per minute and per day) in a new column, and a badge when a provider's terms state it may train on the prompts you send. A limit of zero renders as zero rather than "rate-only" — for a ceiling those are opposite facts — and a model with no training statement gets no badge, because an absent statement is not a guarantee ([#12320](https://github.com/diegosouzapw/OmniRoute/pull/12320)) — thanks @maxmad64bis +- **feat(radar):** explain Community, single-use, contributor, supporter, recovery, abuse, offers, and privacy rules before either Radar activation action, and remove the superseded fixed-PR grant promise from every UI locale ([#12342](https://github.com/diegosouzapw/OmniRoute/pull/12342)) +- **feat(gamification):** the dashboard Profile page now shows the real daily streak — `/api/gamification/level` returns `streak: { current, longest }` (per key with `apiKeyId`, operator-wide maximum otherwise) and the streak card reads it instead of a hard-coded 0 (#2403) ([#12377](https://github.com/diegosouzapw/OmniRoute/pull/12377)) — thanks @pacocartones +- **feat(gamification):** the dashboard leaderboard now shows each API key's display name under the Name column instead of a truncated key id; `GET /api/gamification/leaderboard` attaches `name` per entry (name only — no key material), while the shared ranking helper and the federation leaderboard stay id-only — thanks @pacocartones ([#12385](https://github.com/diegosouzapw/OmniRoute/pull/12385)) +- **feat(gamification):** enforce the documented 1000 XP/min per-API-key anti-cheat rate limit on the XP award path; over-limit awards are logged and skipped instead of persisted, and the sliding window now matches the timestamp format stored in `xp_audit_log` ([#2403](https://github.com/diegosouzapw/OmniRoute/issues/2403)) ([#12390](https://github.com/diegosouzapw/OmniRoute/pull/12390)) — thanks @pacocartones +- **feat(admin):** localize the gamification anomalies page — the loading state, the Status column and the Suspicious badge now come from the `common` catalog (new `common.suspicious` key propagated to every locale) — add it to the Gamification sidebar group as `gamification-admin` (`/dashboard/gamification/admin`), and expose the loading and empty states as polite `role="status"` live regions (#12401 — thanks @pacocartones) +- **feat(providers):** skip GitHub combo members missing from the live synced catalog, and drop Copilot models that are policy-disabled or hidden from the model picker ([#12473](https://github.com/diegosouzapw/OmniRoute/pull/12473)) — thanks @RaviTharuma +- **feat(i18n):** locale-expansion tooling — `npm run i18n:add-locale` adds a language to config, dashboard, docs mirrors, CLI, README/indexes and site in one command; browser, CLI and cookie detection resolve `uk`, `fil`/`tl`, `zh-Hant` (and the retired legacy `in` → `id`) via config aliases; `config/i18n.json` now ships in the npm package so the published CLI can read it; new real-translation ratio gate (`npm run i18n:check-ratio`, advisory) with a per-locale ratchet baseline; `run-translation --adopt` restores `.i18n-state.json`; `sync-language-bars` generates 🌐 bars from config; `validate_translation.py` loads its allowlist again. Retires the duplicate `in` locale (Indonesian mislabelled as Hindi) — 42 honest locales; saved `NEXT_LOCALE=in` / `OMNIROUTE_LANG=in` keep working. (#12496) +- **feat(api):** Emit gateway-measured `tokens_per_second` (TTFT excluded) on streaming usage and `X-OmniRoute-Tokens-Per-Second` when first-token latency is known ([#12616](https://github.com/diegosouzapw/OmniRoute/issues/12616)) ([#12631](https://github.com/diegosouzapw/OmniRoute/pull/12631)) — thanks @RaviTharuma +- **feat(opencode-plugin): map gateway cost/usage/tok/s onto OpenCode inference payloads** — the official plugin copies `X-OmniRoute-Response-Cost`, token counts, `X-OmniRoute-Tokens-Per-Second` / `usage.tokens_per_second`, TTFT, and the winning `X-OmniRoute-Model` onto the JSON/SSE body OpenCode already consumes. Missing tok/s is left unset (never `tokens / latency`). (#12636) — thanks @RaviTharuma +- **feat(combo):** add combo strategy `quota-weighted`: skip exhausted accounts, then weighted-draw by leftover / in-flight load; existing conversations stay pinned until the account is empty ([#12789](https://github.com/diegosouzapw/OmniRoute/pull/12789)) — thanks @HouMinXi +- **feat(models):** Account live listings become the chat catalog source for Claude, Codex, Copilot, and AGY; public metadata only fills prices on IDs those accounts already list. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **fix(models):** Union `agy` and `antigravity` live catalogs so `agy/gemini-3.8-flash-high` is not rejected after the prefix folds to `antigravity`. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **feat(opencode):** Add `@omniroute/opencode-plugin-v2`, an opencode v2 plugin that gives a v2 install a route to an OmniRoute gateway: it publishes models, combos and auto-combos into the host catalog, refreshes them lazily behind a 300s TTL, and keeps serving the last known catalog from an on-disk snapshot when the gateway is unreachable. The v1 plugin (`@omniroute/opencode-plugin`) is untouched and keeps working as before. The plugin adapts to the host instead of assuming one shape: it reads the shape the host seeds into the catalog draft and publishes the `api` block, the pre-`api` top-level fields, or both. The gateway key comes from the host credential store when one is connected, so no secret needs to sit in a config file; a source the gateway refuses is named in a warning instead of silently degrading the catalog to raw model ids; and tool schemas bound for a Gemini model are stripped of the JSON-Schema keywords Gemini rejects, so tool calling survives that route ([#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870)) — thanks @maxmad64bis +- feat(services): show sanitized CLIProxyAPI account health from its authenticated management API without exposing credentials, file paths, or raw account metadata (#6342) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add AnySearch free web search + URL extract (webFetch) with typed results, credential validation, REST routing, and MCP selection - fallback-only ([#11690](https://github.com/diegosouzapw/OmniRoute/pull/11690)) — thanks @Xxx91n +- **feat(dashboard):** display clamped `[0, 100]%` cached input token ratio in request logs table ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) — thanks @b3nw +- **feat(catalog):** add `OMNIROUTE_DISABLE_THINKING_LEVEL_VARIANTS` feature flag to optionally filter out thinking level variants from model catalog ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) — thanks @b3nw +- **feat(dashboard):** continuously export call logs to external analytics stores. A pluggable destination registry ships the full Logs-tab record set on an hourly `JobRegistry` cron, with a persisted per-destination cursor, batched inserts, a config UI rendered from each destination's own field descriptors, and a REST layer (`/api/log-export/*`) for CRUD, a connection test, and an on-demand run. A destination can opt into `includeBodies` to also ship the request and response payloads shown in the Logs detail pane, including the client and provider views of each call; this is off by default, and payloads inherit the dashboard's PII sanitisation, secret redaction and `noLog` handling. Google BigQuery is the first destination, using a service-account key stored encrypted at rest and streaming inserts keyed by call-log id, into a table that is day-partitioned on `timestamp` and clustered on `api_key_name`, `provider`, `model` and `status`. ([#11945](https://github.com/diegosouzapw/OmniRoute/pull/11945)) — thanks @dpozimski +- **feat(providers):** add MaxAI as a signed, OpenAI-compatible provider serving its 13 paid chat models (GPT-5.6 / Luna / Thinking, Claude 5 Sonnet, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite, Grok 4.1-fast / 4.5, DeepSeek V3.2 / R1, Llama 3.3 70B) through OmniRoute's `/v1` endpoint, with per-request HMAC-SHA1→SM3→AES request signing, live model + context-window discovery from `/models/get_config`, and prompted tool-calling translated to OpenAI `tool_calls` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI vision input — image_url content parts are forwarded inline in `message_content` to the 6 vision-capable models (GPT-5.6 / Luna / Thinking, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite) ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI image generation — 6 image models (gpt-image-1, dall-e-3, flux-1-schnell/dev/pro, sd3-medium) exposed through `POST /v1/images/generations` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI document RAG — inline base64 file/document attachments are uploaded to MaxAI (content-addressed `doc_id`) and attached to the chat via `doc_list` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** browserless MaxAI onboarding — email device-pair login (`/api/providers/[id]/login`) and signed access-token refresh, so a connection can be created and kept fresh without a real browser or Google OAuth ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** per-provider TLS impersonation profile (MaxAI presents a Windows Firefox-150 client fingerprint) so its bot-sensitive endpoints accept OmniRoute traffic ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** add **Nimble** as a web-search and web-fetch provider (`nimble-search`) — `/v1/search` routes to Nimble's search API at `lite` depth (locale, freshness and include/exclude domain filters mapped onto the shared request contract), and `/v1/web/fetch` routes to Nimble Extract, which covers all four fetch formats (`markdown`, `html`, `links`, `screenshot`) from a single call. One API key serves both surfaces. ([#11620](https://github.com/diegosouzapw/OmniRoute/pull/11620)) — thanks @wildcard +- The npm publish is automatic again, through npm Trusted Publishing (OIDC): the hosted `stage-npm` job publishes with a short-lived credential minted from GitHub's id-token — no `NPM_TOKEN`, no 2FA prompt, provenance attached. `publish_mode=staged` (owner approves with 2FA) and `direct` (token) remain available on `workflow_dispatch`. ([#11931](https://github.com/diegosouzapw/OmniRoute/pull/11931)) +- **feat(providers):** Add **Opper** as an API-key gateway provider — EU-hosted AI gateway with 700+ models from 30+ providers behind one OpenAI-compatible API and one key (`OPPER_API_KEY`); model ids use `provider/model` format (e.g. `anthropic/claude-sonnet-4-6`, `openai/gpt-5`); live model catalog at `https://api.opper.ai/v3/compat/models`; entry mirrors `requesty` (same shape, `passthroughModels: true`, no static seed) ([#11629](https://github.com/diegosouzapw/OmniRoute/pull/11629)) +- **feat(dashboard):** the `/dashboard/orchestration` snapshot hook now subscribes to the `agents` WebSocket channel (`agent.task.updated`) instead of `requests` as its refetch trigger, and relaxes its background poll from 5s to 30s while that WS connection is up — falling back to the tighter 5s cadence, reprogrammed live on any connect/disconnect transition, whenever the socket is down. ([#12409](https://github.com/diegosouzapw/OmniRoute/pull/12409)) +- **feat(dashboard):** Orchestration canvas quick wins — search box plus state/source/provider filter chips with a one-click clear, and per-source collapse/expand, all reflected in the URL so a filtered/collapsed view is shareable and survives a refresh; the detail drawer gained a "copy trace JSON" action and hardened error/empty-state and accessibility handling; the Agents-tab edges now animate traveling particles along active (running) connections; and the canvas node/edge status colors moved off fixed hex values onto theme-aware `--orch-status-*` CSS custom properties, so they adapt correctly to light/dark mode. ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- **chore(dashboard):** Orchestration UI hardening pass and the missing component/model test coverage it called for — `OrchestratorNode`/`ActivityNode`/`OverflowNode` rendering, the `?node=`/overflow-click page routing, the Agents-tab orchestrator-click no-op and `showCompleted` toggle, and the overview kanban's done-column sort order (#12270, #12271). ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- New `/dashboard/orchestration` page: live unified view of everything running — Cloud Agent, A2A and Conductor as a real-time graph (Agents tab), the combo cascade (Routing tab, reusing the Combo Live Studio) and a state kanban (Overview tab), with a detail drawer (trace, cost, approve/cancel). Read-only over existing APIs — no new backend. Canvas concept credit: PR #11815 design ([#12261](https://github.com/diegosouzapw/OmniRoute/pull/12261)) +- **feat(dashboard):** new "History" tab on `/dashboard/orchestration` — an Airflow-style grid of finished runs over a 24h/7d/30d preset window, one row per (source, identity), clicking a cell opens the existing detail drawer. It is backed by real persistence: A2A task lifecycle transitions are now written to the `a2a_tasks` table (purged after 30 days, configurable via `OMNIROUTE_A2A_HISTORY_RETENTION_DAYS`) and served by the new `GET /api/a2a/tasks/history` listing endpoint, with the task-detail route falling back to persisted history once a run leaves the in-memory snapshot. Conductor runs stay remote and are not persisted locally — the tab says so instead of silently omitting them. ([#12479](https://github.com/diegosouzapw/OmniRoute/pull/12479)) +- **feat(dashboard):** the orchestration detail drawer gained a "Repeat" action for Cloud Agent, A2A and Conductor tasks — a two-click confirm (click once to arm, click again within the confirm window to fire) re-submits the original prompt/input as a new run. The button is disabled with an explanatory tooltip whenever the original input can't be recovered from the loaded task detail (e.g. it never carried a prompt, or the detail failed to load). Two limitations of the A2A variant, by design: it targets the `/a2a` JSON-RPC endpoint, which authenticates with an API key only (`REQUIRE_API_KEY=true` or a configured `OMNIROUTE_API_KEY` makes a dashboard-session repeat answer `HTTP 400` — surfaced verbatim in the drawer's error line, never as a success), and the `message/send` call is SYNCHRONOUS: the POST blocks for the whole skill run, so the success confirmation only appears once the repeated task finishes. A dashboard-authenticated A2A creation path is deliberately left to a follow-up — widening the endpoint's auth posture is an operator decision, not a side effect of this feature. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(a2a):** A2A task execution now records which memories were consulted for the task's last user message as `metadata.memoryHits` (id/key/type/content-snippet) plus a `memory_hits` history event, purely for observability — the retrieved memory is never injected into a skill's prompt or behavior. Gated by the `OMNIROUTE_A2A_MEMORY_HITS` kill-switch (default enabled; set to `0` to skip the recall lookup entirely). The drawer's new "Memory used" section lists these hits for a2a tasks and is omitted whenever there are none. Known limitation: recall only resolves under the keyless posture — a keyed caller's task owner is a SHA-256 prefix of the API key, while memory rows are keyed by the database api-key id, and no hash→id lookup exists today, so the hit list stays empty for keyed callers. The recorded hits are also kept out of the task's own `input` (and therefore out of the persisted input and of the "Repeat" request body), so repeating a task never re-sends the previous run's memory snippets. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(providers):** add a Perplexity Agent API provider (`perplexity-agent` / `pplx-agent`) for Perplexity `/v1/responses`, including the documented Anthropic, OpenAI, Google, xAI, DeepSeek, Z.AI, Moonshot/Kimi, NVIDIA, and Perplexity model IDs plus Anthropic-model `max_output_tokens` compatibility. ([#12103](https://github.com/diegosouzapw/OmniRoute/pull/12103)) — thanks @AIB1TAL0S +- **feat(providers):** add RPD (Requests Per Day) limit to provider rate limit overrides across UI, schemas, DB, and i18n ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) — thanks @b3nw +- **feat(providers): add UC Direct (uncensored.com Developer API), the metered OpenAI-compatible surface.** A standard OpenAI-compatible passthrough (default executor) for uncensored.com's official REST API at `https://api.uncensored.com/api/v1`: `X-api-key` auth (never-expiring `uai_sk_live_` key), `POST /chat/completions` with streaming SSE and native tool-calling, and the full live metered catalog (82 models across 15 providers, discovered from the public `GET /v1/models`). Registered as provider `uc-direct` (alias `ucd`). Complements the un-metered `uc` persona provider — same models, metered credits and a plain API key instead of a subscription session. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(providers): add UC (uncensored.com), the un-metered subscription "persona" chat as an OpenAI-compatible provider.** A WebSocket web-app port: a durable Clerk credential mints a short-lived session token per connect (browserless — no API key), driving UC's persona socket. Ships the browserless email-code login (request → verify → harvest), the 19 verified persona models (Claude Opus, Gemini, Grok, GLM, Kimi, DeepSeek, MiniMax, incl. the uncensored variants), prompted `` tool-calling with a per-model code-style dialect + auto-cure retry for guardrailed models, live ``/reasoning split, streaming + non-streaming OpenAI responses, and full quota/auth error surfacing (paywall / message-limit / rate-limit → 429, invalid session → 401 re-login). Full multimodal parity via the persona blob-upload layer: **vision** (image input, 15 vision-capable models), **document RAG** (PDF/doc upload, server-side extraction), **image generation** (22 models), **video generation** (14 models, async signed-url → poll), and **TTS** (streaming MP3). Registered as provider `uc` (alias `ucn`). The metered OpenAI-compatible Developer API is a separate `uc-direct` provider. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(logs):** add copy-all button to request detail modal ([#11083](https://github.com/diegosouzapw/OmniRoute/pull/11083)) — thanks @AndrianBalanescu +- **feat(check):** make serviceKinds required and add the reverse-walk provider consistency gate ([#11392](https://github.com/diegosouzapw/OmniRoute/pull/11392)) — thanks @Tushar49 +- **feat(providers):** only advertise verified-working connections by default ([#11448](https://github.com/diegosouzapw/OmniRoute/pull/11448)) — thanks @alltomatos +- **feat(resilience):** add hierarchical concurrency admission ([#11493](https://github.com/diegosouzapw/OmniRoute/pull/11493)) +- **feat(providers):** derive + surface expiry for JWT-bearing web cookies (#11497) ([#11505](https://github.com/diegosouzapw/OmniRoute/pull/11505)) — thanks @oyi77 +- **feat(tunnels):** support named cloudflared tunnels via CLOUDFLARED_CONFIG ([#11561](https://github.com/diegosouzapw/OmniRoute/pull/11561)) — thanks @ggiak +- **feat(compression):** make proactive context-compression threshold a live setting ([#11564](https://github.com/diegosouzapw/OmniRoute/pull/11564)) — thanks @ragnar-claude +- **feat(resilience):** add provider peak-hour protection ([#11622](https://github.com/diegosouzapw/OmniRoute/pull/11622)) — thanks @Neuron-Mr-White +- **feat(providers):** add 1min.ai provider ([#11631](https://github.com/diegosouzapw/OmniRoute/pull/11631)) — thanks @zero-executioner +- **feat(dashboard):** collapsible JSON tree viewer for request/response payloads ([#11703](https://github.com/diegosouzapw/OmniRoute/pull/11703)) — thanks @hartmark +- **feat(routing):** add LiquidAI LFM2.5-2.6B free tier via OpenRouter ([#11752](https://github.com/diegosouzapw/OmniRoute/pull/11752)) — thanks @brick30llc-ctrl +- **feat(pricing):** add GLM-5.3-Flash pricing, model specs, and catalog registration ([#11830](https://github.com/diegosouzapw/OmniRoute/pull/11830)) — thanks @AndrianBalanescu +- **feat(routing):** add relayMode for schema-locked context handoffs ([#11839](https://github.com/diegosouzapw/OmniRoute/pull/11839)) — thanks @raheemuddin786 +- **feat(config):** add support for runtime system prompt configuration and hot-reloading ([#11841](https://github.com/diegosouzapw/OmniRoute/pull/11841)) — thanks @raheemuddin786 +- **feat(routing):** expand connection-aware quota prefilter across combo strategies (#11682) ([#11850](https://github.com/diegosouzapw/OmniRoute/pull/11850)) — thanks @HouMinXi +- **feat(models):** reactive discovery sync after Antigravity model-not-found 404 ([#11888](https://github.com/diegosouzapw/OmniRoute/pull/11888)) — thanks @MumuTW +- **feat(models):** auto-aliases and token limits from synced Antigravity discovery ([#11889](https://github.com/diegosouzapw/OmniRoute/pull/11889)) — thanks @MumuTW +- **feat(settings):** raise sticky round-robin limit caps to 1000 ([#12015](https://github.com/diegosouzapw/OmniRoute/pull/12015)) — thanks @adivekar-utexas +- **feat(resilience):** operator-configurable global credential health check interval ([#12043](https://github.com/diegosouzapw/OmniRoute/pull/12043)) — thanks @adivekar-utexas +- **feat(quota):** use official OpenCode Go usage API ([#12124](https://github.com/diegosouzapw/OmniRoute/pull/12124)) — thanks @ddarkr +- **feat(resilience):** default the credential health check sweep to 60 minutes ([#12138](https://github.com/diegosouzapw/OmniRoute/pull/12138)) — thanks @adivekar-utexas +- **feat(quality):** new-code mode for the complexity and dead-code ratchets (Clean as You Code) ([#12142](https://github.com/diegosouzapw/OmniRoute/pull/12142)) +- **feat(dashboard):** orchestration canvas — unified model + snapshot hook (part 1/2) ([#12156](https://github.com/diegosouzapw/OmniRoute/pull/12156)) +- **feat(usage):** add Kilo Code balance and Kilo Pass quotas ([#12178](https://github.com/diegosouzapw/OmniRoute/pull/12178)) — thanks @killer30001000 +- **feat(providers):** refresh vendored ChatGPT Web connector to v4.0.7 ([#12181](https://github.com/diegosouzapw/OmniRoute/pull/12181)) — thanks @backryun +- **feat(providers):** manual "Clear cooldown" action in the cooling panel ([#12224](https://github.com/diegosouzapw/OmniRoute/pull/12224)) — thanks @patrykkopycinski +- **feat(providers):** restore ChatGPT Web via clean-room browser transport ([#12239](https://github.com/diegosouzapw/OmniRoute/pull/12239)) — thanks @backryun +- **feat(sse):** wire the PROVIDER_PROFILES window gate into the global provider cooldown ([#12247](https://github.com/diegosouzapw/OmniRoute/pull/12247)) +- **feat(providers):** modernize CLOVA Studio chat and embeddings ([#12277](https://github.com/diegosouzapw/OmniRoute/pull/12277)) — thanks @backryun +- **feat(browser):** adopt Obscura as primary headless browser engine with Chromium fallback ([#12286](https://github.com/diegosouzapw/OmniRoute/pull/12286)) — thanks @opensource-elearning +- **feat(sse):** full language parity for output styles (es/de/fr/it/ru/zh + autoDetect) ([#12289](https://github.com/diegosouzapw/OmniRoute/pull/12289)) +- **feat(quality):** complete test:scoped — --full map rebuild, stdin selection, CI loader parity (#8084 D1) ([#12353](https://github.com/diegosouzapw/OmniRoute/pull/12353)) +- **feat(providers):** refresh Fable, Cursor, and Devin catalogs ([#12367](https://github.com/diegosouzapw/OmniRoute/pull/12367)) — thanks @backryun +- **feat(video):** redact transcript text from logs and durable memory (#12150 P1) ([#12427](https://github.com/diegosouzapw/OmniRoute/pull/12427)) +- **feat(dashboard):** parent-link, genuine-continuation badge, and modal perf fixes ([#12448](https://github.com/diegosouzapw/OmniRoute/pull/12448)) — thanks @hartmark +- **feat(dashboard):** adaptive context-budget dial on compression panel ([#12488](https://github.com/diegosouzapw/OmniRoute/pull/12488)) — thanks @HouMinXi +- **feat(providers):** refresh Z.ai Web models and browser transport ([#12524](https://github.com/diegosouzapw/OmniRoute/pull/12524)) — thanks @backryun +- **feat(video):** redact raw client-snapshot transcript fields in the detailed log (#12150 P2) ([#12528](https://github.com/diegosouzapw/OmniRoute/pull/12528)) +- **feat(providers):** refresh NVIDIA hosted models ([#12538](https://github.com/diegosouzapw/OmniRoute/pull/12538)) — thanks @backryun +- **feat(quota):** Moonshot Open Platform balance and TPD lock for custom nodes ([#12590](https://github.com/diegosouzapw/OmniRoute/pull/12590)) — thanks @HouMinXi +- **feat(video):** redact transcript in the in-memory pending-request snapshot (#12430 item 6) ([#12596](https://github.com/diegosouzapw/OmniRoute/pull/12596)) +- **feat(dashboard):** link a log entry's Conversation Context to its owning conversation ([#12646](https://github.com/diegosouzapw/OmniRoute/pull/12646)) — thanks @hartmark +- **feat(catalog):** eligibility-gated free-tier bucket ([#12669](https://github.com/diegosouzapw/OmniRoute/pull/12669)) +- **feat(db):** fail-closed previous_response_id continuation for redacted video turns (#12150 P2b) ([#12707](https://github.com/diegosouzapw/OmniRoute/pull/12707)) +- **feat(gamification): pay the documented `streak_bonus` and `badge_unlock` XP rewards.** `XP_REWARDS` listed both rewards but the award pipeline never paid them: the private reward table in `events.ts` omitted them, `updateStreak()` did not report when a streak extended, and badge unlocks carried no XP. Every request that extends a daily streak now pays `streak_bonus × streak length` once per UTC day (guarded by a same-day `xp_audit_log` check), and every badge unlocked through the pipeline pays `badge_unlock` once per badge (guarded by the `user_badges` primary key; `unlockBadge()` now reports whether it inserted). Bonus XP flows through the same `addXp` + level sync + global/weekly/monthly leaderboard path as action XP, so level-ups and rankings include it. The Radar supporter recognition unlock stays XP-free. (#12522 — thanks @pacocartones) +- **feat(providers):** advertise a `free-tier` capability in the provider plugin manifest for every provider with documented free models, so sidecars and dashboards can filter free-capable providers without reading the quota catalog ([#12786](https://github.com/diegosouzapw/OmniRoute/pull/12786)) — thanks @maxmad64bis +- **feat(providers):** Added EURouter as an OpenAI-compatible API-key gateway (`https://api.eurouter.ai/v1`), with live model discovery via `passthroughModels`. Its copy states that models are served by third-party upstreams listed per model, so an EU-based router is not read as EU data residency for inference. ([#13025](https://github.com/diegosouzapw/OmniRoute/pull/13025)) — thanks @ntdatt812 +- **feat(providers):** Added GreenPT as an OpenAI-compatible API-key provider (`https://api.greenpt.ai/v1`), with live model discovery via `passthroughModels`. No free-inference badge: the published docs describe a free API subscription billed per token, not a free tier. ([#13024](https://github.com/diegosouzapw/OmniRoute/pull/13024)) — thanks @ntdatt812 +- **feat(i18n):** 9 new locales — Greek (`el`), Croatian (`hr`), Serbian (`sr`), Lithuanian (`lt`), Estonian (`et`), Latvian (`lv`), Slovenian (`sl`), Maltese (`mt`), Irish (`ga`) — across the dashboard, docs mirrors, CLI, README and the site; OmniRoute now ships every official EU language (51 locales). (#13044) +- **feat(i18n):** 8 new locales — Kannada (`kn`), Malayalam (`ml`), Odia (`or`), Punjabi (`pa`), Nepali (`ne`), Sinhala (`si`), Burmese (`my`), Khmer (`km`) — across the dashboard, docs mirrors, CLI, README and the site (59 locales). The translator now restores the ICU literal escape around angle placeholders and splits oversized docs sections before translating. (#0000) ([#13660](https://github.com/diegosouzapw/OmniRoute/pull/13660)) +- feat(providers): **list Agnes 3.0 Flash as the current free chat model, drop retired 1.5 Flash, add Image 2.0/2.5 Flash plus Video 2.5/2.5 Flash, and discover the live `/v1/models` catalog (including the CN host `api.agnes-ai.cn`).** `agnes-1.5-flash` now forwards to `agnes-3.0-flash`. Video 2.5 polls `GET /v1/videos/{id}` (not the V2.0 `/agnesapi` contract). Live `/v1/models` (2026-09-09) no longer serves 1.5; the wiki marks it deprecated. 3.0 Flash is 512K context / 65,536 max output, same window as 2.5. CN-region keys use the existing per-connection base-URL field, default stays `apihub.agnes-ai.com`. ([#13120](https://github.com/diegosouzapw/OmniRoute/pull/13120)) — thanks @HouMinXi +- **feat(sse):** Codex and OpenAI catalogs list GPT-6 Astra with effort aliases (`-low` through `-ultra`); Codex CLI identity pins `@openai/codex@0.153.4` in lockstep with the image ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **feat(dashboard):** the orchestration History tab gained a "Compare runs" mode — toggling it turns each grid cell into a 2-item selection queue (a 3rd click drops the oldest pick), and picking two cells opens a side-by-side comparison panel instead of the usual detail drawer. The panel fetches both runs' detail the same way the drawer does (falling back to persisted history once a run leaves the live TTL window) and shows, per side: identity/source/state, start time, a signed `right - left` delta for duration/cost/event count, the event timeline aligned by index, and any memory hits. One side's fetch failing never blocks the other, and a delta is only ever computed when both sides have a finite value — otherwise it renders "—", never `NaN`. Comparing two runs from different sources or skills still works; a banner marks the deltas as informational rather than hiding them, since the two runs aren't a strict apples-to-apples pair. ([#12677](https://github.com/diegosouzapw/OmniRoute/pull/12677)) +- **feat(dashboard):** badge a conversation that never reached a clean stop ([#12717](https://github.com/diegosouzapw/OmniRoute/pull/12717)) — thanks @hartmark +- **feat(release):** reconcile-changelog tool + version-anchored fragment aggregation ([#12987](https://github.com/diegosouzapw/OmniRoute/pull/12987)) +- **feat(mcp):** carry context_length through MCP list_models_catalog projection (#12776) ([#13406](https://github.com/diegosouzapw/OmniRoute/pull/13406)) — thanks @KooshaPari +- **feat(cli):** make startup readiness budget configurable (#13369) ([#13433](https://github.com/diegosouzapw/OmniRoute/pull/13433)) — thanks @KooshaPari + ### 🐛 Bug Fixes +- **security(streaming):** sanitize generic mid-stream error messages before emitting OpenAI, + Responses, or Claude SSE failure frames and before diagnostic logging, while preserving raw + failures for internal classification and keeping client disconnects out of provider failure state. ([#12457](https://github.com/diegosouzapw/OmniRoute/pull/12457)) +- **fix(claude):** `blockExtraUsage: false` no longer lets 5h quota preflight skip the account; extra usage is billed after the session bar is gone, so the request must reach Anthropic ([#12803](https://github.com/diegosouzapw/OmniRoute/pull/12803)) — thanks @HouMinXi +- Sanitize HuggingChat conversation-creation and message-send transport failures before they reach client error bodies or provider logs. ([#12467](https://github.com/diegosouzapw/OmniRoute/pull/12467)) +- **fix(dashboard):** Keep local and theme-aware provider SVG icons at a definite layout size so Chromium does not collapse them to 0×0 after the v3.8.50 image-rendering change ([#12054](https://github.com/diegosouzapw/OmniRoute/pull/12054)) — thanks @ponkcore +- **fix(security):** Sanitize provider and runtime failures before public API, SSE and MCP responses and before persistent request, proxy and usage logs, preventing credentials, stack traces and host filesystem paths from crossing those boundaries while preserving stable error codes and useful diagnostics. ([#12506](https://github.com/diegosouzapw/OmniRoute/pull/12506)) +- **fix(cli):** `omniroute tunnel create` no longer crashes with `Cannot read properties of undefined (reading optsWithGlobals)` — removed the duplicate positional argument that caused Commander.js to misalign the action callback parameters ([#12295](https://github.com/diegosouzapw/OmniRoute/issues/12295)) ([#12368](https://github.com/diegosouzapw/OmniRoute/pull/12368)) — thanks @gonisulaimann +- Stop advertising Gemini Live-only models as supported audio endpoints until OmniRoute proxies the bidirectional Live protocol. ([#11710](https://github.com/diegosouzapw/OmniRoute/pull/11710)) — thanks @RaviTharuma +- **fix(kie):** reroute `flux/kontext` off the KIE Market `createTask` flow — it is catalogued with `isMarket: true` but has no Market catalog page, so KIE rejected it with "model name not supported"; it now hits the dedicated `POST /api/v1/flux/kontext/generate` / `GET /api/v1/flux/kontext/record-info` endpoints instead (#11296). ([#11985](https://github.com/diegosouzapw/OmniRoute/pull/11985)) — thanks @hartmark +- **fix(kie):** correct 12 more KIE Market catalog ids that were sent to `createTask` unchanged but diverge from KIE's documented upstream `model` values — GPT Image 2 T2I/I2I (drops the `gpt/` prefix), GPT Image 1.5 T2I/I2I (`gpt-image/` namespace), Seedream 5.0 Lite T2I/I2I (drops the `.0`), all 4 Flux 2 variants (`flux-2/` namespace, generic variant renamed `flex`), and Wan 2.7 Image / Image Pro (dash instead of dot) — each verified individually against the literal example request published on docs.kie.ai. `#11326`'s "everything else already matches" claim was wrong a second time (#11296); `z-image/4.0-*`/`z-image/4.5-*` and `flux/kontext` remain open, documented as unresolved in `KIE_MARKET_UPSTREAM_MODEL_IDS`'s comment pending further verification. ([#11667](https://github.com/diegosouzapw/OmniRoute/pull/11667)) +- **fix(dashboard):** `useApiKeySave.handleSaveApiKey` no longer forces a full upstream `/models` catalog sync on every non-curated provider connection save — callers can now pass `skipModelSync: true` to opt out, so a workflow that only wants to add one manual model no longer floods the provider's available-models list with hundreds/thousands of synced entries. The flag is a client-side intent signal only and is stripped before the connection payload is POSTed to `/api/providers`; default behavior (full sync on save) is unchanged when the flag is omitted (#11324) ([#11664](https://github.com/diegosouzapw/OmniRoute/pull/11664)) +- **Provider connections:** keep `tokenExpiresAt` when a connection is created. The create-path allowlist omitted it, so every insert stored NULL and the dashboard token badge could read a fresh connection as expired until its first background refresh ([#11368](https://github.com/diegosouzapw/OmniRoute/pull/11368)). — thanks @ntdatt812 +- **fix(combos):** the combo builder's precision-select, global-model-search, and manual-entry flows now serialize a model step's `model` string using the provider's already-computed routing-alias prefix (e.g. `oc/`) instead of rebuilding it from the raw canonical `providerId`, fixing the no-auth "OpenCode Free" provider (`opencode`) being routed to the unrelated paid "OpenCode Zen" provider (`opencode-zen`) because `opencode` doubles as a manual routing-prefix override ([#11433](https://github.com/diegosouzapw/OmniRoute/issues/11433)). ([#11670](https://github.com/diegosouzapw/OmniRoute/pull/11670)) +- **fix(radar):** the catalog feed cache now keeps `generatedAt`, the date the feed's data was built, next to `fetchedAt`, the date this install downloaded it (#11435). The feed schema requires that date and the sync path validates it, but the cache dropped it — so a feed fetched minutes ago and one carrying weeks-old figures looked identical to everything downstream, including the dashboard's "Last fetched" line. `getRadarCatalog().meta` and `GET /api/radar/status` now report both dates, the latter as its own field rather than folded into `version` — and omitted entirely for the offers and intel caches, which keep no build date, where a `null` would read as "unknown" rather than "never stored". The dashboard still shows only the fetch time; surfacing the build date there needs a new translated label and is left to a follow-up. Rows cached before migration 163 read back as `null`: unknown stays unknown instead of borrowing the fetch time. The referrals cache has persisted the same date since migration 142. — thanks @maxmad64bis +- **fix(cli):** postinstall no longer fills `JWT_SECRET` and `API_KEY_SECRET` in the installed package's `.env` (#11436). `.env.example` ships both blank on purpose: the server restores them from its durable store, or generates and persists them there, in `ensureSecrets()`. Pre-filling them defeated that — the file lives inside the package directory, so `npm i -g` replaced it and postinstall wrote _different_ values, while `ensureSecrets()` (which only acts on an empty variable) never got to restore the real ones. Both secrets rotated silently on every update, invalidating dashboard sessions and API-key CRCs. `STORAGE_ENCRYPTION_KEY` left the same list for the same reason in #1622; its comment pointed at a function that no longer exists and now names the real provisioning path. — thanks @maxmad64bis +- **fix(cli):** the CLI now says when a loaded `.env` lives inside the installed package directory (#11437). It already announces every env file it reads, without distinguishing the ones that survive an update from the one that does not: `npm i -g` replaces the package directory wholesale, so values set there are gone at the next update, silently. The warning names the durable path to move them to, and fires only when that file actually supplied a value — a file entirely shadowed by a durable one supplied nothing. A development checkout stays silent: there the same path is stable and documented in `SETUP_GUIDE.md`. — thanks @maxmad64bis +- **fix(free-models):** the shared `isFreeModel()` predicate no longer reports catalog entries marked `freeType: "discontinued"` as free, so `hidePaidModels` can't route to Pollinations' seven premium models that now require a paid key ([#11441](https://github.com/diegosouzapw/OmniRoute/pull/11441)) — thanks @maxmad64bis +- fix(ui): let AnySearch use the normal provider-icon fallback when LobeHub has no matching icon (#11449) +- Fixed the v3.8.50 Costs and Analytics dashboards so flat-rate Claude Code usage can be shown as an explicitly requested token-price estimate without changing default billed-cost semantics. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed archived usage retention so each request is priced individually instead of pricing a day's summed tokens once, which understated archived cost whenever a day mixed cache-heavy and ordinary requests. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed the Costs dashboard so it discloses when displayed figures include flat-rate token-price estimates instead of labelling them as billed spend, using the flag the analytics API already returns; the month-end projection and the CSV/JSON exports carry the same marker, and billed-cost mode is unchanged. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- **fix(combo):** attach the same combo-diagnostics trace (`poolSize`/`attemptOrder`/`excluded`/`terminalReason`, plus `x-omniroute-combo-*` headers) to the round-robin strategy's and the nested pipeline/fusion runtime-unit loop's "Maximum combo retry limit reached" 503 that the priority-strategy path already attaches for the identical terminal condition — previously those two paths returned a bare, contextless 503 ([#11462](https://github.com/diegosouzapw/OmniRoute/issues/11462)). ([#11665](https://github.com/diegosouzapw/OmniRoute/pull/11665)) +- **fix(bun):** use native `bun:sqlite` in `bootstrap-env` and `sync-env` to avoid loading `better-sqlite3` N-API addon during Bun startup ([#11468](https://github.com/diegosouzapw/OmniRoute/pull/11468)) — thanks @TheDemonTuan +- **fix(docker-bun):** make `Dockerfile.bun` install reliable with pre-install script helpers and native `bun:sqlite` smoke check ([#11470](https://github.com/diegosouzapw/OmniRoute/pull/11470)) — thanks @TheDemonTuan +- **fix(docker-bun):** remove every vendored `better-sqlite3` native addon from the Bun runtime image so startup cannot abort before the `bun:sqlite` fallback ([#11482](https://github.com/diegosouzapw/OmniRoute/pull/11482)) — thanks @TheDemonTuan +- **fix(autoCombo):** effort/alias model variants (`gpt-5.6-sol-xhigh`, `gpt-5.6`, cursor's `claude-4.6-opus-high`) inherit their base model's task fitness instead of falling to the wildcard 0.5 ([#11492](https://github.com/diegosouzapw/OmniRoute/pull/11492)) — thanks @MumuTW +- fix(db): rate-limit repeated Arena ELO leaderboard fetch-failure warnings instead of logging one per sync attempt (#11500) ([#11989](https://github.com/diegosouzapw/OmniRoute/pull/11989)) — thanks @hartmark +- **fix(db):** dedupe the raw `[Encryption] Decryption failed...` log line emitted by the lazy-decrypt views (`createLazyRowProxy`/`createLazyConnectionView`), which power `getProviderConnections()` and were re-triggering that line on every CredentialHealth/model-sync cycle for the same corrupt or stale-key credential — a fresh Proxy over a fresh row on every cycle meant the per-proxy memoization never suppressed it, unlike the dedup `decryptConnectionFields()` already had since [#9927](https://github.com/diegosouzapw/OmniRoute/issues/9927). Now shares that dedupe tracking so the line logs at most once per credential ([#11500](https://github.com/diegosouzapw/OmniRoute/issues/11500)). ([#11678](https://github.com/diegosouzapw/OmniRoute/pull/11678)) +- **fix(intelligence):** Arena ELO sync stores a synthesized base-model row for every effort/harness variant (`claude-opus-5-max` → `claude-opus-5`, `gpt-5.6-sol-xhigh (codex-harness)` → `gpt-5.6-sol`), so bare model requests reach the synced intelligence layer instead of the static fallback table; the hand-maintained `MODEL_ALIAS_MAP` — which collapsed generations (`gpt-5.5` → `gpt-5`) — is gone ([#11506](https://github.com/diegosouzapw/OmniRoute/pull/11506)) — thanks @MumuTW +- **fix(autoCombo,sse):** catalog hygiene — the static task-fitness table no longer ranks retired models above live ones (dead rows dropped, patterns matched on segment boundaries), the 7 deprecation aliases that forwarded to retired or non-existent models now point at the vendors' published replacements, an alias is no longer rewritten when the serving provider offers the id as-is, and a new `check:model-lifecycle` gate diffs both tables against a vendor lifecycle snapshot ([#11507](https://github.com/diegosouzapw/OmniRoute/pull/11507)) — thanks @MumuTW +- **fix(sse):** stop dropping resolved `thoughtSignature` values on parallel (multi tool-call) turns sent to Gemini 3.x — the claude→gemini and openai→gemini translators previously kept the signature only on the *first* function call of a message, causing Gemini to reject subsequent calls in the same turn with HTTP 400 "Function call is missing a thought_signature"; each function call now keeps its own resolved signature ([#11510](https://github.com/diegosouzapw/OmniRoute/issues/11510)). ([#11669](https://github.com/diegosouzapw/OmniRoute/pull/11669)) +- Treat Docker publishing from a non-default release branch as an expected skip instead of a failed workflow, while preserving the guard that prevents it from overwriting the `next` channel. ([#11527](https://github.com/diegosouzapw/OmniRoute/pull/11527)) — thanks @pacocartones +- fix(search): prefer credentialed providers over duckduckgo-free fallback (#11524) — the fallback-only loop ran before the credentialed-providers loop in `executeWebSearch`, making configured providers unreachable when `duckduckgo-free` was available. ([#11565](https://github.com/diegosouzapw/OmniRoute/pull/11565)) — thanks @vsd2807 +- **fix(sse):** cap the upstream headers-wait phase for STREAMING requests to a client-realistic ceiling (110s, under Codex's own ~120s hard client-abort window) instead of the flat 10-minute `FETCH_TIMEOUT_MS` default — that default was 5x longer than the body-phase readiness watchdog's own adaptive bound, so a request whose upstream never returned any response at all (not even headers, e.g. a stalled NVIDIA target behind a tool-heavy Responses→Chat translation) kept the client connection alive on keepalives only, guaranteeing the client's own patience ran out first with an opaque 499 instead of OmniRoute detecting and failing the stall fast. Non-streaming requests are unaffected — they keep the existing flat default (`open-sse/utils/fetchStartTimeoutPolicy.ts`) (#11526) ([#11668](https://github.com/diegosouzapw/OmniRoute/pull/11668)) +- **fix(ci):** protocol-clients E2E harness boots the peer-stamped custom server (`run-next.mjs dev`) instead of the bare `next` CLI, so `LOCAL_ONLY` locality resolves from the real TCP peer stamp and `/api/mcp/audit` answers 200 (open bootstrap preserved via an `OMNIROUTE_E2E_BOOTSTRAP_MODE=open` credential clear in `run-next.mjs`, pinned to loopback `HOST`) instead of a deterministic 403 (#11535) ([#11549](https://github.com/diegosouzapw/OmniRoute/pull/11549)) — thanks @jonlwheat2-gif +- **fix(docs):** the Auto-Combo engine is described with the number of factors it actually declares. Four different counts were in circulation — 6, 9, 13 and 14 across `AGENTS.md`, four guides and six code comments — while `DEFAULT_WEIGHTS` declares 15. `check:docs-counts` now reads that number from the source, so adding a factor without updating the prose fails CI ([#11545](https://github.com/diegosouzapw/OmniRoute/pull/11545)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **fix(quota-share):** in-flight leases are per request, so an aborted request's slot ages out instead of being kept alive by later traffic on the same connection ([#11547](https://github.com/diegosouzapw/OmniRoute/pull/11547)) — thanks @abhisheksharma2411 +- The free-tier summary route now serves the refreshed Radar catalog when the feed is active — the same numbers the dashboard shows — and states which catalog answered plus its real build date, instead of always reporting release-frozen figures with a stale curation date. The supporter-key live feed stays reserved to authenticated callers of the instance. ([#11550](https://github.com/diegosouzapw/OmniRoute/pull/11550)) — thanks @maxmad64bis +- **sse:** fix LiveWS/embed-WS servers crashing at startup under the Node/tsx runtime — `liveServer.ts`, `embedWsProxy.ts` and `apiBridgeServer.ts` imported `@/shared/utils/httpClientAbortGuard` without the `.mjs` extension, so the client-abort crash guard added by [#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556) was unreachable and every dependent test failed with `ERR_MODULE_NOT_FOUND` ([#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556)). ([#11671](https://github.com/diegosouzapw/OmniRoute/pull/11671)) — thanks @jonlwheat2-gif / @yxyxy +- fix(chat): shed any of the matched provider-node's routing identifiers (public prefix or internal id) before model lookup, so composites like `//` no longer reach upstream verbatim (#11557) — thanks @maxmad64bis +- **fix(cache):** a semantic-cache entry now expires on its TTL instead of surviving until the next UTC midnight, and `dbEntries` stops counting expired rows ([#11573](https://github.com/diegosouzapw/OmniRoute/pull/11573)) — thanks @ntdatt812 +- **fix(api):** `/v1/models` schedules its stale-while-revalidate rebuild through Next's `after()` again, so a stale catalog reaches the client before the rebuild blocks the event loop ([#11574](https://github.com/diegosouzapw/OmniRoute/pull/11574)) — thanks @ntdatt812 +- **fix(check):** the `known-symbols` executor conformance gate awaits the now-async `getExecutor()`, so it stops reporting all 142 registered aliases as dead symbols and can detect a lazy import that fails ([#11582](https://github.com/diegosouzapw/OmniRoute/pull/11582)) — thanks @ntdatt812 +- **fix(pack):** register `bin/cli/utils/volatileEnvPath.mjs` in `PACK_ARTIFACT_REQUIRED_PATHS`, so the prune can no longer silently drop a module the published CLI imports on every boot ([#11588](https://github.com/diegosouzapw/OmniRoute/pull/11588)) — thanks @ntdatt812 +- **i18n(vi):** translate the five reliability-column strings on the free-provider rankings page, which rendered as `__MISSING__:` markers in the Vietnamese UI ([#11589](https://github.com/diegosouzapw/OmniRoute/pull/11589)) — thanks @ntdatt812 +- **resilience:** restore the expired-connection retry-budget probe in the token-health sweep — the `!isGitHubAccessTokenOnlyConnection` carve-out reintroduced by #11608 contradicted the boundary pinned by #11592, so a GitHub connection parked at `expired` with retry budget remaining was never probed and could never self-heal ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)). ([#11672](https://github.com/diegosouzapw/OmniRoute/pull/11672)) — thanks @jonlwheat2-gif / @ntdatt812 +- **fix(dashboard):** Provider Stats now displays combo success rates on their native 0–100 percentage scale instead of multiplying them twice ([#11595](https://github.com/diegosouzapw/OmniRoute/pull/11595)) — thanks @pacocartones +- **fix(providers):** route DuckDuckGo Web Chat through the verified `duck.ai` endpoint, refresh its free model catalog, and avoid forwarding unsolved anti-abuse challenges ([#11597](https://github.com/diegosouzapw/OmniRoute/pull/11597)) — thanks @solstxce +- **fix(ui):** Console log Refresh and Copy controls now expose localized accessible names, keep copy actions visible on keyboard focus, and announce copy completion safely ([#11599](https://github.com/diegosouzapw/OmniRoute/pull/11599)) — thanks @pacocartones +- Dropped the stale-`.eslintcache` `restore-keys` fallback from both "Restore ESLint file cache" steps in `ci.yml`, so the blocking `Lint` job can no longer be served per-file verdicts computed under a different lint config, suppressions file or lockfile. `quality.yml` had already dropped it in #11963; `ci.yml` — the workflow that actually gates PRs — had not (#11600). ([#11996](https://github.com/diegosouzapw/OmniRoute/pull/11996)) +- fix(ci): pass `--pass-on-unpruned-suppressions` in `run-eslint-json.mjs` so the CI Lint job no longer fails when a suppression is merely orphaned by a genuine fix, mirroring the identical fix already in `validate-release-green.mjs` (#11600) ([#11983](https://github.com/diegosouzapw/OmniRoute/pull/11983)) — thanks @hartmark +- fix(handoff): enforce provider allowlist for universal handoff (#11602) — universal handoff now skips summarization when the selected summary model's provider is not included in the configured provider allowlist. ([#11619](https://github.com/diegosouzapw/OmniRoute/pull/11619)) — thanks @vsd2807 +- **fix(dashboard):** Show a stable error state when Search Analytics returns an HTTP error or malformed data ([#11603](https://github.com/diegosouzapw/OmniRoute/pull/11603)) — thanks @pacocartones +- **test(gamification):** pin the aggregate profile level to the XP-derived semantics of #11604 — `getAggregateXp()` now derives `currentLevel` from the summed XP (`calculateLevel(sum)`), not `MAX(stored current_level)`, and the #3484 fixture levels are aligned with the XP curve ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)). ([#11676](https://github.com/diegosouzapw/OmniRoute/pull/11676)) — thanks @jonlwheat2-gif / @pacocartones +- **fix(dashboard):** Keep the Profile level and progress aligned with aggregate XP, including bounded handling for invalid totals ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)) — thanks @pacocartones +- **fix(dashboard):** Prevent locked hidden badges from revealing their icon or opening private badge details before they are earned ([#11605](https://github.com/diegosouzapw/OmniRoute/pull/11605)) — thanks @pacocartones +- **fix(dashboard):** Restore keyboard focus after shared modals close and cancel delayed autofocus during cleanup ([#11607](https://github.com/diegosouzapw/OmniRoute/pull/11607)) — thanks @pacocartones +- **fix(dashboard):** Enable Enter and Space activation for clickable data-table rows without hijacking nested controls ([#11610](https://github.com/diegosouzapw/OmniRoute/pull/11610)) — thanks @pacocartones +- **fix(autoCombo,sse):** vendor-retired catalog ids are dropped from the auto-combo candidate pool and no longer win on leftover `arena_elo` / `user_override` rows; `getModelLifecycleDecision` consults `model-lifecycle.json` (prefix-stripped) so aggregator traffic is not `untracked` for ids the snapshot already knows ([#11625](https://github.com/diegosouzapw/OmniRoute/issues/11625)) ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)) — thanks @MumuTW +- **test(chatcore):** move Codex/Claude combo fixtures off models the lifecycle guard now rejects — `gpt-5.1-codex`/`gpt-5-codex` are vendor-retired (snapshot, #11626) and `claude-3-5-sonnet-20241022` is shut down, so native-passthrough and combo-fallback tests switched to `gpt-5.6-sol` and `claude-sonnet-4.6` ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)). ([#11675](https://github.com/diegosouzapw/OmniRoute/pull/11675)) — thanks @jonlwheat2-gif / @MumuTW +- fix(opencode-plugin): stop a free-tier budget that rounds up across a magnitude from rendering as `1000K`/`1000M` in the model picker — `fmtTokens` chose its unit from the raw token count and then rounded with `toFixed(1)`, so 999,950–999,999 printed as `1000K` rather than `1M` and just under a billion printed as `1000M` rather than `1B` ([#11684](https://github.com/diegosouzapw/OmniRoute/pull/11684)) — thanks @abhisheksharma2411 +- Cap prose-derived Antigravity quota resets at the configured model cooldown maximum while preserving authoritative Retry-After headers and Google RetryInfo hints. (#11695) Thanks @pacocartones. ([#11823](https://github.com/diegosouzapw/OmniRoute/pull/11823)) +- Compare `_rebaseline_*` notes structurally in the ratchet-bank verifier so an object-valued note no longer reports itself as rewritten on an unmodified tree — the nightly `bank-ratchet-shrinks` lane had been aborting on every run since 2026-08-11, leaving 41,672 lines of earned file-size shrinks unbanked (#11702). ([#11892](https://github.com/diegosouzapw/OmniRoute/pull/11892)) — thanks @MumuTW +- **fix(build):** `prepublish.ts` bundles the ChatGPT Web (Codex) MCP bridge through `runBuildTool()` instead of spawning `npx.cmd` raw, fixing the build crash on Node ≥ 20/Windows where `.cmd` shims cannot be spawned without a shell (EINVAL) ([#11704](https://github.com/diegosouzapw/OmniRoute/issues/11704)) ([#11706](https://github.com/diegosouzapw/OmniRoute/pull/11706)) — thanks @jonlwheat2-gif +- fix(codex): keep `parallel_tool_calls:false` on the translated Codex Responses Lite path (#11707) ([#11984](https://github.com/diegosouzapw/OmniRoute/pull/11984)) — thanks @hartmark +- **fix(tests):** the new Gemini Web Images retirement test (#11708) called the now-async `getExecutor()` synchronously, throwing `TypeError: getExecutor(...).getProvider is not a function` instead of asserting Gemini Web chat stays available. Awaited to match the R0.3 executor-registry refactor (#11220). ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(search):** Allow REST and MCP web-fetch requests to select the advertised `anysearch-search` provider explicitly ([#11716](https://github.com/diegosouzapw/OmniRoute/pull/11716)) — thanks @pacocartones +- **fix(providers):** Keep overnight peak-hour protection active after midnight according to the window's configured start day ([#11718](https://github.com/diegosouzapw/OmniRoute/pull/11718)) — thanks @pacocartones +- **fix(providers):** correct all five g4f.space gateway descriptions to document proof-of-work credits or member API keys, variable limits, and the remote prompt boundary instead of promising free keyless access at five requests per minute ([#11738](https://github.com/diegosouzapw/OmniRoute/pull/11738)). +- **fix(db):** `model_capabilities` is created by a migration instead of lazily on the first models.dev sync, so a clean install and an upgraded install converge on the same schema regardless of which features have run ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(ci):** `check:install-upgrade` now fails on an `npm` install truncated by ENOSPC (npm reports it as a warning and still exits 0), authenticates its health probe so the version assertion works against the hardened health payload, frees the clean-install tree before the upgrade phase, and no longer reports a schema divergence computed from a boot that never served ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(routing):** harden session-affinity key derivation against oversized explicit IDs and empty structural-payload collisions, while recognizing Gemini and common text request shapes ([#11746](https://github.com/diegosouzapw/OmniRoute/pull/11746)) — thanks @MumuTW +- **fix(packages/browser-pool):** regenerate `package-lock.json` so the `packages/browser-pool` workspace's locked `playwright`/`@types/node` (and transitives) match its `package.json` specs, fixing cache-only/offline installs (`npm ci --offline`, Nix `buildNpmPackage`) that previously failed with `ENOTCACHED` ([#11747](https://github.com/diegosouzapw/OmniRoute/issues/11747)) — thanks @benjaminkitt ([#11784](https://github.com/diegosouzapw/OmniRoute/pull/11784)) — thanks @geekyNads +- **fix(assets):** Stop distributing 79 provider and CLI logos without verified source/license provenance, keep every provider/tool available through the internal generic icon, and prevent those IDs from falling through to an external icon CDN. ([#11750](https://github.com/diegosouzapw/OmniRoute/pull/11750)) +- **fix(providers):** retire the common `chatgpt-web` / `cgpt-web` provider because its pre-key and proof-of-work provenance could not be cleared ([#11754](https://github.com/diegosouzapw/OmniRoute/pull/11754)) — explicit chat and image requests, slash-prefixed models, persisted aliases, proxy overrides, and combo targets now fail closed with a sanitized HTTP `410` / `PROVIDER_RETIRED` before upstream dispatch. Migration 163 tombstones matching connections and invalidates their active leases without deleting history or widening API-key allowlists. The independent `chatgpt-web-codex` / `cgpt-codex` provider and its MIT-noticed vendored implementation remain available. +- **fix(usage):** quota and usage refresh no longer 409 when an exclusive lease reserves the connection ([#11758](https://github.com/diegosouzapw/OmniRoute/pull/11758)) — thanks @TheDemonTuan +- Keep the embedding registry's vector width and `embedding` type on models when a synced model exists for the same id, so `/v1/models` no longer reports registry-described embedding models widthless or untyped (#11761) — thanks @rahilmavani +- Correct `google/gemini-embedding-001` on the OpenRouter route to 3072 dimensions, the width it returns when `dimensions` is not sent (#11761) — thanks @rahilmavani +- **fix(vision-bridge):** Restrict automatic vision routing and fallbacks to models available in authoritative live provider catalogs, including aliases and effort variants ([#11767](https://github.com/diegosouzapw/OmniRoute/issues/11767)) — thanks @pacocartones ([#11813](https://github.com/diegosouzapw/OmniRoute/pull/11813)) +- **fix(providers):** reclassify Cerebras as a one-time $5 signup credit (payment method required, 30-day validity), not a recurring no-card 1M tokens/day trial ([#11773](https://github.com/diegosouzapw/OmniRoute/issues/11773)) ([#12591](https://github.com/diegosouzapw/OmniRoute/pull/12591)) — thanks @HouMinXi +- fix(cli): stop prepublish from re-rebuilding the already-built ESM-only opencode-plugin dist (#11787) ([#11990](https://github.com/diegosouzapw/OmniRoute/pull/11990)) — thanks @hartmark +- **fix(db):** Fresh installs no longer log a non-fatal `no such table: compression_run_telemetry` warning when retention cleanup runs before the lazily-created telemetry table exists ([#11802](https://github.com/diegosouzapw/OmniRoute/pull/11802)) — thanks @RaviTharuma +- fix(sse): set X-OmniRoute-Selected-Connection-Id on successful combo dispatches so downstream consumers stop falling back to an empty connection id (#11810) ([#11986](https://github.com/diegosouzapw/OmniRoute/pull/11986)) — thanks @hartmark +- **fix(services):** embedded CLIProxyAPI lifecycle checks now use public `/healthz`, while model discovery uses the configured dedicated data-plane API key instead of the management password ([#11811](https://github.com/diegosouzapw/OmniRoute/pull/11811)) — thanks @RaviTharuma +- Fix persisted model steps returning an error in the playground route simulator, and warn when structural steps cannot be simulated. ([#11862](https://github.com/diegosouzapw/OmniRoute/pull/11862)) — thanks @pacocartones +- **fix(providers):** Antigravity's dynamic mitmAlias table no longer routes `gemini-3.7-flash-{high,medium,low}` to a literal tier-suffixed upstream id just because one connected account's own discovery listed it directly — those display ids always resolve through the safe `gemini-3.7-flash-tiered` static alias, so one account's Google-provisioned access no longer 404s every sibling account of the provider ([#11824](https://github.com/diegosouzapw/OmniRoute/issues/11824), [#11651](https://github.com/diegosouzapw/OmniRoute/issues/11651)) ([#11988](https://github.com/diegosouzapw/OmniRoute/pull/11988)) — thanks @hartmark +- **fix(dashboard):** Expose Profile loading, errors, page structure, and XP progress to assistive technologies ([#11838](https://github.com/diegosouzapw/OmniRoute/pull/11838)) — thanks @pacocartones +- **fix(socks):** `Agent.connectTimeout` now bounds both SOCKS handshake and TLS connect, and `family === null` no longer falls back to `fetch-socks` ([#11842](https://github.com/diegosouzapw/OmniRoute/pull/11842)) — thanks @maxmad64bis +- **perf(compression):** OOM mitigations for large payload hashing, memoization, and token estimation ([#11844](https://github.com/diegosouzapw/OmniRoute/pull/11844) — thanks @AndrianBalanescu) +- Excluded Next.js Node File Trace manifests (`*.nft.json`) from the published npm tarball. They are build-time metadata and are never read while serving, but had grown to 668.7 MB — 61% of the package — which pushed the upload past the registry limit and made `npm publish` fail with `413 Payload Too Large`. ([#11864](https://github.com/diegosouzapw/OmniRoute/pull/11864)) +- **fix(cli):** keep the global OmniRoute server `--base-url` separate from provider-node `--endpoint` values in add, update, and validate commands ([#11860](https://github.com/diegosouzapw/OmniRoute/pull/11860)) — thanks @pacocartones +- **fix(config):** Nous Research's `Hermes-4-405B` model now displays as "Hermes 4 405B (Nous Research)" in both the provider registry and the free-model catalog, instead of the mislabelled "Hermes 4 7B" ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#11993](https://github.com/diegosouzapw/OmniRoute/pull/11993)) +- **fix(provider/nous):** inject required user= tag into Nous Research inference requests to resolve upstream 400 "missing tags" error ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#12044](https://github.com/diegosouzapw/OmniRoute/pull/12044)) +- **fix(build):** `npm run build` now fails in one second with a named package and a copy-pasteable fix when npm silently drops an externalised optional native dependency, instead of dying four minutes in with `Module not found: Can't resolve 'better-sqlite3'` ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- **fix(install):** `postinstall` no longer throws `ReferenceError: isAndroid is not defined` — failing the whole `npm install` — when the `better-sqlite3` rebuild fallback times out; the manual-fix guidance is reachable again ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- Fixed the Alibaba free-tier allowlist test that went red on its own once the shipped catalog's `validUntil` (2026-08-27) passed, leaving every PR and `main` with a failing `Unit Tests (1/8)`. The test now builds its own packs with dates it controls, and covers the expired-pack fallback that production has actually been serving. ([#11867](https://github.com/diegosouzapw/OmniRoute/pull/11867)) +- Split the npm registry upload into its own GitHub-hosted job. npm refuses `--provenance` from a self-hosted runner (`422 ... Only "github-hosted" runners are supported`), which blocked the v3.8.50 publish; the heavy verification cannot move to a hosted runner, so it now hands the proven tarball over instead. ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(ui):** the shared `DataTable` loading state no longer reads its decorative ⏳ glyph out to assistive technology, and now carries the same `role="status"` / `aria-live="polite"` / `aria-busy="true"` semantics as `PageLoading` ([#11869](https://github.com/diegosouzapw/OmniRoute/pull/11869)) — thanks @pacocartones +- **fix(api):** Bound the `timeout` accepted by `POST /api/providers/[id]/login` to the same 15s–600s window the Adobe Firefly and Conol browser-login paths already enforce, so a request can no longer pin a headful browser and the single active-login slot with an arbitrarily large poll budget. ([#11871](https://github.com/diegosouzapw/OmniRoute/pull/11871)) — thanks @pacocartones +- **fix(api):** Stop the tunnel and MITM routes from returning raw child-process error text, which disclosed host paths, binary install locations and Tailscale `tskey-*` credentials in a body some of these routes serve to non-loopback callers, and return a real 400 instead of a framework 500 when a tunnel request body fails validation. ([#11872](https://github.com/diegosouzapw/OmniRoute/pull/11872)) — thanks @pacocartones +- **fix(open-sse):** stream TTFT/ITL are sampled from a monotonic clock (`performance.now`) instead of `Date.now`, so an NTP correction or wall-clock jump can no longer inflate a genuine TTFT or produce a negative one that the `ttft >= 0` guard silently drops — protecting the router's OTel/EWMA/`usage_history`/speed-ranking signals ([#11873](https://github.com/diegosouzapw/OmniRoute/pull/11873)) — thanks @pacocartones +- **fix(providers):** xAI `reasoning_effort: "xhigh"` now reaches grok-4.6+ instead of being silently clamped to `"high"` ([#11879](https://github.com/diegosouzapw/OmniRoute/pull/11879)) — thanks @NoxzRCW +- **fix(dashboard):** the providers page no longer crashes into the error boundary when a provider id collides with an `Object.prototype` member (`constructor`, `__proto__`); icon lookups are own-property guarded ([#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)) — thanks @NoxzRCW +- **fix(skills):** injected skill tools declared in shorthand (`{"content": "string"}`) now forward valid JSON Schema, unblocking providers that validate tool schemas strictly such as Zhipu GLM on the Console Go tier ([#11881](https://github.com/diegosouzapw/OmniRoute/pull/11881)) — thanks @NoxzRCW +- **fix(api):** `POST /api/playground/simulate-route` now surfaces `combo-ref` and `provider-wildcard` persisted combo steps with a specific warning (naming the referenced combo, or the unresolved `provider/model` wildcard) instead of folding them into a generic "unsupported step" count; a `provider-wildcard` step is also now included as an unresolved target so the operator can see it is in the route (ported from [#11882](https://github.com/diegosouzapw/OmniRoute/pull/11882) — thanks @NoxzRCW). ([#11930](https://github.com/diegosouzapw/OmniRoute/pull/11930)) +- **fix(translator):** the streaming OpenAI→Claude translator keeps upstream usage, including prompt-cache tokens, when it arrives on a trailing `choices: []` chunk (Fireworks and any upstream using `stream_options.include_usage`) ([#11883](https://github.com/diegosouzapw/OmniRoute/pull/11883)) — thanks @NoxzRCW +- **fix(cli):** `omniroute update --apply` now tells you explicitly whether a running server was detected and, if so, that you must run `omniroute restart` to apply the update — it never restarted anything and previously implied the update was already live once files were installed. The dashboard's npm-mode Update flow (`/api/system/version`) now tries OmniRoute's own PID-file-managed supervisor before falling back to pm2, and reports an honest "restart required" step instead of a silent pm2-only "skipped" that read like a completed update. The server-side latest-version lookup backing the dashboard's update banner also gained `--prefer-online`, closing the same stale-npm-cache class already fixed in the CLI's own copy for #4376 ([#11885](https://github.com/diegosouzapw/OmniRoute/issues/11885)). ([#12005](https://github.com/diegosouzapw/OmniRoute/pull/12005)) +- **fix(resilience):** clear persisted LKGP pins when a target suffers connection/provider exhaustion or is skipped before dispatch due to cooldown/exhaustion/unavailability, preventing subsequent requests from repeatedly prioritizing known-dead providers ([#11911](https://github.com/diegosouzapw/OmniRoute/issues/11911)). ([#12013](https://github.com/diegosouzapw/OmniRoute/pull/12013)) — thanks @HouMinXi +- **fix(sse):** the OpenAI→Claude stream translator now defers the terminal `message_delta`/`message_stop` emission until the real usage block has arrived (or a genuine end-of-stream flush forces it) instead of emitting it immediately on `finish_reason` — previously, when the trailing usage-only chunk (`{"choices":[],"usage":{...}}`) arrived *after* the `finish_reason` chunk (the normal order for Fireworks/vLLM/Together and other `stream_options.include_usage` upstreams), the client-visible `message_delta` still carried stale/zero usage even though `state.usage` was internally corrected too late to matter (ported from [#11915](https://github.com/diegosouzapw/OmniRoute/pull/11915) — thanks @HouMinXi). ([#11933](https://github.com/diegosouzapw/OmniRoute/pull/11933)) +- **fix(sse):** `stripStore()` now forces `store=false` for stateless OpenAI-compatible Responses-API targets (unless the connection has explicitly opted in via `providerSpecificData.openaiStoreEnabled`), instead of only handling the `openai`/`agentrouter` cases — a client-supplied `store` value previously passed through untouched to backends that don't actually persist responses server-side ([#11916](https://github.com/diegosouzapw/OmniRoute/pull/11916) — thanks @HouMinXi). +- **fix(models):** custom provider-node models (synced, custom, and alias-backed) now appear under their configured prefix in the unified catalog when the operator's model-id prefix mode is canonical, instead of being dropped whenever alias-inclusion was otherwise disabled ([#11918](https://github.com/diegosouzapw/OmniRoute/pull/11918) — thanks @HouMinXi). +- **fix(models):** the unified model catalog now suppresses stale static registry models (including effort-tier variants) for any provider whose active connection has an authoritative live synced catalog, not only providers already using exclusive-synced-listing — a connection with `providerUsesAuthoritativeLiveCatalog` previously kept serving both the live-synced models and the stale static rows side by side ([#11919](https://github.com/diegosouzapw/OmniRoute/pull/11919) — thanks @HouMinXi). +- **fix(ui):** `ProviderIcon`'s three remaining unguarded lookups (`PROVIDER_ICON_ALIASES`, `LOCAL_SVG_ALIASES`, `THEMED_SVGS`) now use `Object.hasOwn()` own-property checks — a provider id such as `constructor` or `__proto__` previously resolved through the prototype chain instead of falling through to the unknown-provider CDN fallback (`getLobeProviderIcon()` itself was already guarded by [#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)); `ProviderPageHeader`'s `color` field is also now optional, matching the rest of the component's defensive typing (ported from [#11920](https://github.com/diegosouzapw/OmniRoute/pull/11920) — thanks @HouMinXi). ([#11935](https://github.com/diegosouzapw/OmniRoute/pull/11935)) — thanks @NoxzRCW +- fix(ollama): preserve multi-byte UTF-8 content split across stream chunks in the Ollama NDJSON transform, which previously corrupted CJK/emoji into U+FFFD (#11921) — thanks @pacocartones +- **fix(providers):** OrcaRouter chat requests now target `/v1/chat/completions` instead of the bare `/v1` API root, fixing the upstream `404 Invalid URL (POST /v1)` ([#11923](https://github.com/diegosouzapw/OmniRoute/pull/11923)). — thanks @echel0nn +- **fix(plugins):** deliver the `onStreamComplete` event to disk-installed plugins. The event shipped in v3.8.50 (#9669) was emitted internally but had no plugin-facing wiring, so no plugin could ever subscribe: the manifest schema silently dropped `hooks.onStreamComplete`, and the loader/manager only knew the seven legacy hooks. `onStreamComplete` is now a declarable manifest hook, wired through the loader and registered by the manager like the other hooks, and its payload carries a `requestId` so consumers can correlate the stream-completion event with the originating request ([#11934](https://github.com/diegosouzapw/OmniRoute/pull/11934)) — thanks @amaleta / @pacocartones +- **fix(db):** the Qdrant embedding-model dropdown now lists local/self-hosted providers (Ollama, LM Studio, vLLM, etc.) — an active connection is treated as "configured" when the provider allows an optional API key, not only when it has a real key or OAuth, so a running local embedding provider is no longer hidden from the picker ([#11949](https://github.com/diegosouzapw/OmniRoute/issues/11949)) ([#11995](https://github.com/diegosouzapw/OmniRoute/pull/11995)) +- **fix(providers):** Vertex AI Anthropic partner-model discovery now calls the Model Garden `v1beta1` publisher list (`/v1beta1/publishers/anthropic/models`, global) and parses its `publisherModels` envelope, so Claude models auto-synced from Vertex populate the active live catalog and route at request time instead of returning `Model '' is not available in the active live catalog` ([#11991](https://github.com/diegosouzapw/OmniRoute/issues/11991)) — thanks @fabioluissilva ([#11998](https://github.com/diegosouzapw/OmniRoute/pull/11998)) +- **fix(cli):** support `--base-url` alongside `--endpoint` in `omniroute nodes add`, `update`, and `validate` subcommands to prevent global `--base-url` shadowing issues ([#11999](https://github.com/diegosouzapw/OmniRoute/issues/11999)). ([#12033](https://github.com/diegosouzapw/OmniRoute/pull/12033)) — thanks @HouMinXi +- **fix(providers):** `cloudflare-ai` no longer refuses image content parts for every Workers AI model ([#12002](https://github.com/diegosouzapw/OmniRoute/pull/12002)) — the plain-string `content` requirement behind #2539 is carried by the _model_ schema, not by the `/ai/v1/chat/completions` endpoint (measured: an all-text part array returns 200 on `@cf/mistralai/mistral-small-3.1-24b-instruct`, `@cf/meta/llama-4-scout-17b-16e-instruct` and `@cf/meta/llama-3.3-70b-instruct-fp8-fast`, and 400 on the text-only `@cf/qwen/qwen2.5-coder-32b-instruct`). `transformRequest()` flattened every array and threw on the first non-text part (#6390), so image input was refused for vision-capable Cloudflare models that accept it. All-text arrays are still flattened — the one shape every model accepts — while an array carrying a non-text part is passed through untouched, so the attachment is still never silently dropped. Regression guards: `tests/unit/cloudflare-ai-image-parts-6390.test.ts`. — thanks @davidlinfr +- **fix(resilience):** decouple the limiter-managed execution backstop from the queue-wait budget — new `requestQueue.executionMaxWaitMs` (env `RATE_LIMIT_EXECUTION_MAX_WAIT_MS`, default 600000 = 10 min) now feeds Bottleneck's post-dispatch `expiration`, while `requestQueue.maxWaitMs` keeps its documented queue-wait semantics. Previously the queue-wait budget doubled as the execution expiration, so legitimate long-running calls on non-incremental gateways (whole generation buffered before the first upstream byte, e.g. Console Go / Command Code tiers serving GLM models) were killed mid-flight at the queue budget with a false 504 `RATE_LIMIT_EXECUTION_TIMEOUT` — the local limiter undercut the provider-aware upstream fetch-start timeouts. The surfaced 504 message now names `requestQueue.executionMaxWaitMs`; the error keeps the #4165 guarantees (disclaims an upstream timeout, preserves the Bottleneck error as `cause`, branded code + trusted provenance, classified request-scoped so combo falls back). A real queue-wait bound (the `Promise.race` around `limiter.schedule()` sketched in #9533) remains future work. (#12025) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **Call logs:** keep the `error` field when an artifact exceeds the storage cap, instead of replacing it with the omission marker. The error is the only field that says *why* a request failed and is typically ~90 bytes next to the multi-hundred-KB bodies that trip the cap, so dropping it left a size-limited row undiagnosable — a provider outage, a local timeout and an upstream 400 all rendered identically. It is now preserved at every fallback stage, truncated to 4KB if it is itself large ([#12026](https://github.com/diegosouzapw/OmniRoute/issues/12026)). ([#12095](https://github.com/diegosouzapw/OmniRoute/pull/12095)) — thanks @ntdat812 +- **fix(diagnostics):** preserve the error field (truncated to 4KB with a `[truncated: …]` suffix) in every call-log artifact size-limit fallback stage. Previously the minimal fallback replaced the error with `[omitted: call log artifact size limit exceeded]`, so an oversized artifact row showed nothing about WHY the request failed — e.g. 91 of 847 opencode-go 504 rows on one production instance were undiagnosable from the dashboard. Oversized request/response bodies are still omitted exactly as before; the error cap is independent of the payload sizes that tripped the fallback. (#12026) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **fix(sse):** OpenAI Responses clients that declare the native `web_search` tool now receive a spec-shaped `web_search_call` output item with `action.sources` alongside the preserved function-call round-trip, so search results executed through OmniRoute's own search backend are consumable by standard Responses clients (Codex, pi-web-access, …). ([#12031](https://github.com/diegosouzapw/OmniRoute/pull/12031)) — thanks @watchingdogs +- **fix(cli):** use in-thread alias resolver hooks on modern runtimes to avoid deprecation noise and improve Node.js forward compatibility ([#12073](https://github.com/diegosouzapw/OmniRoute/issues/12073)). ([#12083](https://github.com/diegosouzapw/OmniRoute/pull/12083)) — thanks @xiaoyaner0201 +- **fix(combo):** an operator-set **Agent Features → Context length** on a combo is now honored at request time. The value was persisted and advertised through `/v1/models`, but `resolveComboContextLimit()` never consulted it — so a multi-target combo whose members carry no per-model window fell through to the provider's generic `defaultContextLength` (openrouter 128000, command-code 200000) and rejected large requests with `Input exceeds context window … limit 128000` despite the combo being explicitly sized much larger. An identical single-target combo worked, because it collapses to its concrete target before the guard runs. Invalid values (0/negative/NaN/Infinity) are ignored, so the existing target → combo-min → fallback order is unchanged. ([#12090](https://github.com/diegosouzapw/OmniRoute/pull/12090)) — thanks @adivekar-utexas +- **fix(sse):** passthrough streams now estimate usage on finish when upstream closes without usage even with `stream_options.include_usage` — avoids `0 tokens / 0%` for providers that stay silent (and correctly handles trailing empty-choices usage) ([#12151](https://github.com/diegosouzapw/OmniRoute/pull/12151)) — thanks @maxmad64bis +- **fix(combos):** clearing an agent feature in the combos editor now persists — unchecking context cache protection, or emptying the system message or tool filter, sends an explicit `null` instead of dropping the field from the `PUT` body, which the update merge read as "leave unchanged" ([#12177](https://github.com/diegosouzapw/OmniRoute/pull/12177)) — thanks @foreveryh / @ntdatt812 +- **fix(memory):** self-hosted embedding endpoints now vectorize — the vector width is measured from the first embedding that comes back instead of being read from a registry that cannot describe them, so `vec_memories` is created and memories stop piling up unvectorized behind a green health check ([#12180](https://github.com/diegosouzapw/OmniRoute/pull/12180)) — thanks @kanade-hoshino / @ntdatt812 +- **fix(translator):** the leading `system` message now reaches Responses-API upstreams when its `content` is a content-part array — it was read as `typeof content === "string" ? content : ""`, so a prompt-caching client (Anthropic `cache_control`, the shape LiteLLM and the Anthropic SDK emit) had its entire system prompt replaced by an empty `instructions`. The request was still accepted with a normal `prompt_tokens` count, so the model answered with no instructions and nothing in the response said they were missing. Mid-conversation system turns already handled the array shape ([#7056](https://github.com/diegosouzapw/OmniRoute/pull/7056)); only the first one did not ([#12206](https://github.com/diegosouzapw/OmniRoute/issues/12206)). Regression guard: `tests/unit/translator-openai-responses-system-content-parts.test.ts`. ([#12207](https://github.com/diegosouzapw/OmniRoute/pull/12207)) — thanks @Gorillaz322 +- **fix(free-tier):** `/api/free-tier/summary` no longer computes its totals from a Radar feed built before the catalog the running release ships. When the cached feed is older — or carries no build date at all — the route answers from the shipped catalog, resolved through the operator's local model state so disabled and tombstoned models stay out of the numbers ([#12215](https://github.com/diegosouzapw/OmniRoute/pull/12215)). — thanks @maxmad64bis +- **fix(oauth):** Keep a Claude personal workspace and a Team organization as separate connections — they share the same email and `accountUUID`, so the email-only OAuth dedup let the second login overwrite the first account's tokens; `organizationUUID` now disambiguates them, the way `workspaceId` does for Codex ([#12222](https://github.com/diegosouzapw/OmniRoute/pull/12222)) — thanks @davidebaraldo +- **fix(resilience):** a 402 on a single paid model of a passthrough/gateway provider (e.g. `kilo-gateway`, `ollama-cloud`) no longer terminalizes the whole connection with a never-auto-recovered `credits_exhausted` status — only the paid model is locked out, so free models on the same key keep serving. 402 variant of [#3027](https://github.com/diegosouzapw/OmniRoute/issues/3027). Single-credential providers are unaffected — a 402 there is still treated as the key being genuinely out of credit ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239) / [#10616](https://github.com/diegosouzapw/OmniRoute/issues/10616)) ([#12242](https://github.com/diegosouzapw/OmniRoute/issues/12242)) — thanks @brick30llc-ctrl ([#12266](https://github.com/diegosouzapw/OmniRoute/pull/12266)) — thanks @geekyNads +- **fix(sse):** trust `finish_reason: "length"`/`"max_tokens"` over the reasoning-consumed-token ratio in response quality validation, so a reasoning model truncated below the old 90% threshold correctly fails and retries instead of returning empty content as a silent "success" ([#12262](https://github.com/diegosouzapw/OmniRoute/pull/12262)) — thanks @brick30llc-ctrl +- **fix(settings):** `PUT /api/settings/cache-config` now persists `alwaysPreserveClientCache` to the flat general settings the runtime cache-control policy actually reads; previously the value landed in the databaseSettings "cache" section and was silently ignored, so the endpoint had no effect on `cache_control` passthrough ([#12304](https://github.com/diegosouzapw/OmniRoute/pull/12304)) — thanks @davidebaraldo +- **fix(grok-cli):** treat omitted SuperGrokPro `creditUsagePercent` as 0% used so Provider Limits still renders a weekly bar (proto3 zero-elision) ([#12312](https://github.com/diegosouzapw/OmniRoute/pull/12312)) — thanks @HouMinXi +- **fix(combo):** Expose the two Auto-Combo scoring factors nobody could set — the combo validation schema and the dashboard weight sliders both declared 13 of the scorer's 15 factors, so `connectionDensity` (spreads load across a provider's connections) and `quality` were dropped on save and offered nowhere. The sliders also shipped their own default table that differed from the engine's on every non-zero factor and summed to 1.05, so the percentages shown next to them added up to 105%. Both lists now match `DEFAULT_WEIGHTS`, and a test keeps them there. Note that a combo whose stored `weights` omitted the two keys was effectively running with them at zero and the other thirteen renormalized upward; it now runs with the engine's intended distribution, so its routing does shift ([#12314](https://github.com/diegosouzapw/OmniRoute/pull/12314)) — thanks @maxmad64bis +- **fix(docs):** The free-tier reference no longer says its numbers come "confidence tagged per row" — no catalog entry carries a confidence tag and the API serves none, so every figure on that page is an estimate of the same, unstated quality. The page now states what an entry does vouch for: an independently documented hard stop (set by hand with the source in a comment, never defaulted to `true`) and a prompt-training disclosure, both with live counts the `check:docs-counts` gate keeps honest ([#12318](https://github.com/diegosouzapw/OmniRoute/pull/12318)) — thanks @maxmad64bis +- **fix(usage):** `adobe-firefly` and `firefly` have had a working usage fetcher since Adobe Firefly landed, but neither was ever added to the registration list, so the provider-plugin manifest, `genericQuotaFetcher` and the free-access quota cache all reported them as having no usage support — while `USAGE_SUPPORTED_PROVIDERS` said the opposite. Both are now declared, which also means their credit balance is fetched like any other declared provider's: `registerGenericQuotaFetchers` now registers a generic quota fetcher for them, and `resolveFreeAccessState` no longer returns early. A test holds the registration list to the dispatcher's switch in both directions, which is what the module's own docstring already asked for in prose ([#12321](https://github.com/diegosouzapw/OmniRoute/pull/12321)) — thanks @maxmad64bis +- **fix(quota):** drop the generic quota cache (agy / Antigravity / Claude OAuth) on an upstream 429 so reset-aware scoring does not keep a 60s stale snapshot, and force-refresh the next usage fetch so inner provider caches cannot recache the same window ([#12325](https://github.com/diegosouzapw/OmniRoute/pull/12325)) — thanks @HouMinXi +- **fix(combos):** deleting a combo now clears its persisted LKGP pins instead of leaving unreachable `key_value` rows behind ([#12326](https://github.com/diegosouzapw/OmniRoute/issues/12326)) ([#12425](https://github.com/diegosouzapw/OmniRoute/pull/12425)) — thanks @KrzysiekSko +- **fix(sse):** Keep ZWNJ (U+200C) and ZWJ (U+200D) in assistant text, reasoning and tool-call arguments — Persian/Kurdish half-space (`ارائه‌دهنده`), Arabic/Indic shaping and emoji sequences no longer lose them; the response de-obfuscation now removes joiners only between ASCII word characters, where the request side inserts them ([#12186](https://github.com/diegosouzapw/OmniRoute/issues/12186)) — thanks @rezjalibd ([#12359](https://github.com/diegosouzapw/OmniRoute/pull/12359)) — thanks @pacocartones +- **fix(resilience):** count resolved upstream 5xx results against the provider circuit breaker on the chat path — `CircuitBreaker.execute()` no longer reads a resolved `{ success: false, status: 5xx }` as a success that cancels the call-site failure, so a provider answering 503s now trips its breaker instead of staying `CLOSED` at `failureCount: 1`; single-model and combo dispatches are each accounted exactly once ([#12254](https://github.com/diegosouzapw/OmniRoute/issues/12254)) ([#12360](https://github.com/diegosouzapw/OmniRoute/pull/12360)) — thanks @pacocartones +- **fix(providers):** resolve the Codex quota auto-ping model from the live provider catalog and lifecycle registry instead of the retired `gpt-5.1-codex-mini`, and pause the ping with one actionable warning when no selectable Codex model exists rather than retrying a shut-down id every cooldown window ([#11905](https://github.com/diegosouzapw/OmniRoute/issues/11905)) ([#12361](https://github.com/diegosouzapw/OmniRoute/pull/12361)) — thanks @pacocartones +- **fix(api):** keep the `{created, data}` wrapper on combo-routed `/v1/images/generations` responses and default Codex image results to `b64_json` on both `/v1/images/generations` and `/v1/images/edits` so Codex CLI's built-in `image_gen` can decode them ([#12268](https://github.com/diegosouzapw/OmniRoute/issues/12268)) ([#12362](https://github.com/diegosouzapw/OmniRoute/pull/12362)) — thanks @pacocartones +- **fix(sse):** Name the shadowed custom provider node when a built-in provider id/alias (e.g. `openference` → `of`) reserves the prefix of an existing OpenAI/Anthropic-compatible node, so the runtime `No active credentials for provider: ` error explains that the prefix routed to the built-in and never reached the node's healthy connections, instead of contradicting the dashboard ([#11943](https://github.com/diegosouzapw/OmniRoute/issues/11943)) — thanks @morpheus9393 ([#12365](https://github.com/diegosouzapw/OmniRoute/pull/12365)) — thanks @pacocartones +- **fix(i18n):** wrap `ccOnboardingKeyPlaceholder` in ICU single quotes across all 43 locales so angle brackets render literally instead of being parsed as rich-text tags, which crashed the Claude Code onboarding block with `INVALID_MESSAGE: INVALID_TAG` ([#12302](https://github.com/diegosouzapw/OmniRoute/issues/12302)) ([#12369](https://github.com/diegosouzapw/OmniRoute/pull/12369)) — thanks @gonisulaimann +- **fix(models):** publish `effort_tiers` on Kimi K3's synced base-model entries (`kmca/k3`, `kmca/k3-256k`) so catalog-only clients (OpenCode, plain SDK pickers) can see and select the reasoning tiers (`low`/`high`/`max`) the synced metadata already carried — the `isSkippedEffortProvider` gate no longer suppresses tier visibility on those base entries, while synthetic `-` variant generation stays prevented and Codex/GLM base models remain excluded unchanged ([#12299](https://github.com/diegosouzapw/OmniRoute/issues/12299)) ([#12371](https://github.com/diegosouzapw/OmniRoute/pull/12371)) — thanks @gonisulaimann +- **fix(guardrails):** keep `auto`/`auto/*` virtual combos exempt from the Vision Bridge `fixedModel` credential guard so a combo target is passed through instead of silently falling back to global auto-selection ([#12237](https://github.com/diegosouzapw/OmniRoute/issues/12237)) ([#12373](https://github.com/diegosouzapw/OmniRoute/pull/12373)) — thanks @pacocartones +- **fix(combo):** capability-filter exhaustion caused by `max_tokens` above every target's known output limit now reports that reason (requested `max_tokens` vs the pool's highest known ceiling) instead of the unrelated "supports structured output" message ([#12229](https://github.com/diegosouzapw/OmniRoute/issues/12229)) — thanks @DW-MediaLab ([#12374](https://github.com/diegosouzapw/OmniRoute/pull/12374)) — thanks @pacocartones +- **fix(auth):** the `least-used` account strategy now prefers accounts without backoff before falling back to oldest `lastUsedAt`, the same tie-break `round-robin` already applies, so a failover no longer lands on a just-rate-limited account for a single request ([#12279](https://github.com/diegosouzapw/OmniRoute/issues/12279)) — thanks @tenshiak ([#12375](https://github.com/diegosouzapw/OmniRoute/pull/12375)) — thanks @pacocartones +- **fix(docker):** the `chatgpt-web-codex-browser` image now finds the Chrome binary under `chrome-linux64/` (Chrome for Testing layout in `playwright:v1.62.0-noble`) as well as the legacy `chrome-linux/`, so the container no longer crash-loops with `exec: --headless=new: not found` ([#12024](https://github.com/diegosouzapw/OmniRoute/issues/12024)) ([#12376](https://github.com/diegosouzapw/OmniRoute/pull/12376)) — thanks @pacocartones +- **fix(providers):** declare `groq/compound` and `allam-2-7b` as non-reasoning models in the curated Groq registry so `reasoning_effort` / `output_config.effort` / `thinking` from Claude Code are stripped instead of forwarded, which Groq rejected with HTTP 400 ([#12134](https://github.com/diegosouzapw/OmniRoute/issues/12134)) ([#12379](https://github.com/diegosouzapw/OmniRoute/pull/12379)) — thanks @pacocartones +- **fix(executors):** `OpencodeExecutor` no longer forces a direct connection when the connection has a proxy assigned in Proxy Management but no per-account proxies: the single-account fast path used to wrap the upstream dispatch in the direct-egress sentinel, discarding the ambient proxy context the chat handler had pinned from `proxy_assignments`, so API-key `opencode`/`opencode-go` connections egressed from the host IP (and hit geoblocks) despite the assignment. The direct pin is now applied only when no ambient proxy context exists ([#11894](https://github.com/diegosouzapw/OmniRoute/issues/11894) — thanks @hizzt) ([#12380](https://github.com/diegosouzapw/OmniRoute/pull/12380)) — thanks @pacocartones +- **fix(api):** `GET /v1/models` with `MODELS_CATALOG_PREFIX_MODE=canonical` (or `?prefix=canonical`) now lists providers whose registry alias is undefined or equal to their own id (Antigravity, Antigravity CLI and other self-aliased built-ins) — their single `provider/model` id was dropped by the alias/canonical duplicate guard in the static, synced, custom and alias-backed catalog loops ([#12058](https://github.com/diegosouzapw/OmniRoute/issues/12058)) — thanks @cheynetom ([#12381](https://github.com/diegosouzapw/OmniRoute/pull/12381)) — thanks @pacocartones +- **fix(translator):** Drop replayed `thinking` blocks that carry no signature (the shape produced from cross-provider `reasoning_content`) instead of stamping the default Claude signature on them, which Anthropic rejected with `400 Invalid signature in thinking block` on the next turn served by an Anthropic rung ([#12105](https://github.com/diegosouzapw/OmniRoute/issues/12105)) — thanks @atescivitci-cmd ([#12386](https://github.com/diegosouzapw/OmniRoute/pull/12386)) — thanks @pacocartones +- **fix(cli):** Resolve Bun's `--preload` polyfill path against the package root instead of `dist/`, so `omniroute` installed with `bun install -g` no longer crashes at startup with `error: preload not found …/dist/open-sse/utils/setupPolyfill.ts` ([#11980](https://github.com/diegosouzapw/OmniRoute/issues/11980)) — thanks @joglomedia ([#12387](https://github.com/diegosouzapw/OmniRoute/pull/12387)) — thanks @pacocartones +- **fix(providers):** `gemini-business` now publishes its model catalog — `/v1/models` and `/v1/providers/gemini-business/models` list the 12 enterprise Gemini ids the executor understands instead of returning an empty list (#12107) ([#12389](https://github.com/diegosouzapw/OmniRoute/pull/12389)) — thanks @pacocartones +- **fix(db):** install `busy_timeout` before the SQLite connection's first statement so a process opening the database while another one closes its WAL connection waits out the transient EXCLUSIVE lock instead of dying with `database is locked`, and recognise the drivers' real BUSY/PROTOCOL/IOERR errors as transient in the corruption probe so the same lock no longer renames the database away as corrupt; deflakes `cross-process contenders never both acquire the same connection` (#12394 — thanks @pacocartones) +- **fix(chat-admission):** derive the `chat_admission_busy` 503 `Retry-After` from observed heavyweight-lease occupancy — the larger of the exhausted `OMNIROUTE_CHAT_ADMISSION_QUEUE_MS` window and the time since capacity last turned over, capped at 60 s — instead of a fixed 1 s (structural) / 2 s (byte-stage) hint that invited Codex/agent fan-out clients to re-send ~1 MiB `/v1/responses` bodies every second into a gate held for the whole SSE lifetime; an idle gate keeps the historical floors ([#12135](https://github.com/diegosouzapw/OmniRoute/issues/12135)) (#12395 — thanks @pacocartones) +- **fix(api-manager):** the API key permissions modal no longer silently drops `allowedCombos` entries its Combo picker cannot render — routing-rule names such as `rt-*`, which the backend already honours — when "All" is clicked and the key is switched back to "Restrict"; those entries now survive the toggle, are listed read-only under the combo list so the count and the list agree, and are saved back verbatim instead of persisting `[]` (deny-all) (#12397 — thanks @pacocartones) +- **fix(catalog):** write the NUL separator of the catalog connection memo key, the provider serviceKind memo key, the Video Bridge promotion group key and a JSON-exactness test fixture as the `\u0000` escape instead of a raw byte — same runtime value, but the raw byte made git, GitHub and ripgrep treat those files as binary (hidden PR diffs, silently skipped searches); a guard test now keeps raw NUL bytes out of `src/`, `open-sse/` and `tests/` (#12403 — thanks @pacocartones) +- **fix(providers):** add `CLAUDE_CODE_CLIENT_VERSION` and `GITHUB_COPILOT_CLI_VERSION` env overrides so Anthropic/Copilot client-version gates can be unblocked without a rebuild ([#12417](https://github.com/diegosouzapw/OmniRoute/issues/12417)) ([#12632](https://github.com/diegosouzapw/OmniRoute/pull/12632)) — thanks @HouMinXi +- **fix(db):** back-fill `last_ping_at` and `last_pinged_reset_key` on `provider_connections` during schema reconciliation so divergent lineages that skipped `123_quota_auto_ping` still accept quota auto-ping writes ([#12470](https://github.com/diegosouzapw/OmniRoute/pull/12470) — thanks @KooshaPari) +- **fix(ci):** document MIT exceptions for `@eloqnt/{config,format-json,format-po}` (next-intl transitive; locked tarballs omit `license`) and keep the A2A lifecycle vitest off the real SQLite persistence seam ([#12581](https://github.com/diegosouzapw/OmniRoute/issues/12581)) ([#12595](https://github.com/diegosouzapw/OmniRoute/pull/12595)) +- OpenCode plugin `/v1/models` catalog fetch now waits 30s by default and attaches HTTP `statusCode` on 401/5xx so host fallback plugins can hop instead of seeing an untyped AbortError/UnknownError. ([#12607](https://github.com/diegosouzapw/OmniRoute/pull/12607)) — thanks @RaviTharuma +- **CI:** the OpenAPI security-tier gate now mirrors `isAlwaysProtectedPath()` in full — it also reads `ALWAYS_PROTECTED_API_PATTERNS`, so the pattern-gated credential routes (`/api/providers/{id}/{claude,codex}-auth/{export,apply-local}`, GHSA-5926-2w35-7h4q) no longer report as unannotated. (#12605) +- **fix(api):** GET /v1/models no longer waits forever on a hung coalesced catalog rebuild; cold-path waits are bounded (`CATALOG_BUILD_TIMEOUT_MS`, default 8s) and a last-good 200 is served when the rebuild times out ([#12627](https://github.com/diegosouzapw/OmniRoute/issues/12627)). ([#12628](https://github.com/diegosouzapw/OmniRoute/pull/12628)) — thanks @RaviTharuma +- **fix(cursor):** a non-array `tool_calls` on an assistant message no longer crashes the cursor request translator with a `TypeError`; both loops now require an array ([#12689](https://github.com/diegosouzapw/OmniRoute/issues/12689)) ([#12691](https://github.com/diegosouzapw/OmniRoute/pull/12691)) — thanks @soroush5 +- **fix(chat):** Chat Completions no longer return empty `content` after a server-owned memory or skills tool; the first provider send and account/model recovery now share one pipeline so a follow-up round-trip can fill the reply ([#12696](https://github.com/diegosouzapw/OmniRoute/issues/12696)) — thanks @HouMinXi ([#12867](https://github.com/diegosouzapw/OmniRoute/pull/12867)) +- **fix(dashboard):** batch-deleting provider keys no longer toasts failure after a successful delete when the confirm button's click event is forwarded as `onAfter` ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) — thanks @HouMinXi +- **fix(glm):** drop the extra 16th argument to `createSSETransformStreamWithLogger` that TypeScript rejected (TS2554) and that never reached the TransformStream ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) ([#12770](https://github.com/diegosouzapw/OmniRoute/pull/12770)) — thanks @HouMinXi +- **fix(dashboard):** pass `nodeMap` into Runtime `QuotaGroup` so a quota monitor in error/exhausted/alerting no longer throws `ReferenceError: nodeMap is not defined`. ([#12868](https://github.com/diegosouzapw/OmniRoute/pull/12868)) — thanks @HouMinXi +- Restricted API keys whose `allowedModels` lists a combo name no longer skip every combo member at pre-dispatch (`ALL_TARGETS_SKIPPED`). Inner-target filtering still applies when the allow-list is a provider prefix or `disableNonPublicModels` is on ([#12899](https://github.com/diegosouzapw/OmniRoute/pull/12899)). — thanks @HouMinXi +- **fix(combos):** Effort-suffixed combo members inherit the base model's `model_context_overrides` row so priority order is not inverted on large requests ([#12926](https://github.com/diegosouzapw/OmniRoute/pull/12926)) — thanks @HouMinXi +- **fix(catalog):** Picker-added `customModels` enter the dispatch-time live catalog so combo members and bare inference no longer 400 ([#12934](https://github.com/diegosouzapw/OmniRoute/pull/12934)) — thanks @HouMinXi +- **fix(docker):** re-derive the Docker build's worker-pool memory budget from the MEASURED ~4.5 GB per-process RSS (the issue owner's own VPS dmesg OOM-killer reproduction) instead of the stale 2560 MB/worker inference #11419 shipped, and lower `OMNIROUTE_BUILD_WORKERS` 3 → 2 so 1 parent + 1 page-data worker (2 processes × 4.5 GB = 9 GB) fits the 12.288 GB (75%) budget on a 16 GB GitHub Actions runner — the previous default (1 parent + 2 workers = 13.5 GB) still overcommitted the runner and kept "Publish to Docker Hub" failing with `cannot allocate memory` after #11419 merged (#7518). ([#11663](https://github.com/diegosouzapw/OmniRoute/pull/11663)) +- **fix(electron):** the packaged Windows desktop build now passes the #7592 cold-restart smoke end to end. Five defects were found and fixed on the way: (1) optional-pack staging failed on any machine whose `tar` is GNU tar — it reads the drive letter in an absolute `-f C:\...` archive path as a remote rsh host (`Cannot connect to C:`), so staging now passes a bare filename with `cwd` at the tarball directory and surfaces tar stderr on failure; (2) the Electron `files` allowlist was missing `lib/loginHeaderCapture.js`, so the packaged main process crashed at startup with `Cannot find module './lib/loginHeaderCapture'`; (3) electron-builder ≥26 injects an `!**/node_modules/**` ignore into every extraResources pattern list that no positive filter can override, which silently dropped the staged runtime `node_modules` (including the better-sqlite3 N-API prebuild) from `resources/app` — a fresh v3.8.50 desktop build would have shipped with no native SQLite driver and reproduced the #7592 sql.js fallback on every machine; an `afterPack` hook now restores the staged `node_modules` after packing; (4) the packaged-app smoke harness redirected `USERPROFILE` into its temp DATA_DIR, but Electron resolves the Roaming profile from `%USERPROFILE%\AppData\Roaming\` (USERPROFILE takes precedence over the APPDATA env var) and the path service throws instead of creating it, so `requestSingleInstanceLock()` returned false and the app exited(0) silently before `app.whenReady()` — the harness now pre-creates the derived tree, and `ensureSmokeEnvDirs` is exported and unit-tested; (5) the #7592 driver assertion parsed a `[DB] Driver: ...` line that the server's primary DB path never printed (only the unused `openDatabaseAsync()` did) — `getDbInstance()` now emits the same line on open so the guard can actually assert the native driver. Also: the smoke env-allowlist unit test hardcoded POSIX paths and could only pass on Linux/macOS; expectations are now host-agnostic, with new regression tests for the USERPROFILE-derived tree and `tarPack` under absolute Windows-style paths ([#11443](https://github.com/diegosouzapw/OmniRoute/pull/11443)) — thanks @jonlwheat2-gif +- fix(sse): stop the auto-combo candidates inspector from silently dropping model-locked/cooled-down rows (#9133) ([#11994](https://github.com/diegosouzapw/OmniRoute/pull/11994)) — thanks @hartmark +- **fix(providers):** Perplexity Web no longer turns upstream stream failures into successful assistant text; pre-content failures remain eligible for fallback, partial output ends with a structured sanitized error, and failed sessions are not persisted ([#12465](https://github.com/diegosouzapw/OmniRoute/pull/12465)) +- **fix(video-bridge):** preserve chronological video and transcript fusion, source-frame timestamps, transcript provenance, full contact-sheet intervals, and pre-abort callback isolation ([#11681](https://github.com/diegosouzapw/OmniRoute/pull/11681)) +- **Z.ai Web:** HTTP 200 streams carrying an upstream error now terminate with a structured failure instead of assistant text plus a normal stop, preserving partial output while allowing pre-content combo fallback. ([#12454](https://github.com/diegosouzapw/OmniRoute/pull/12454)) +- Absorb `Error [AbortError]: request_signal_aborted` and DOMException AbortError shapes in the process-level client-abort crash guard so routine client disconnects no longer kill the server (exit code 7). ([#12165](https://github.com/diegosouzapw/OmniRoute/pull/12165)) — thanks @alvinveroy +- **fix(sse):** Treat Adapta Web `type:error` SSE events as sanitized non-stream failures instead of empty HTTP 200 completions. ([#12459](https://github.com/diegosouzapw/OmniRoute/pull/12459)) +- **fix(autoCombo):** layer 3 (`models_dev_tier`) no longer scores retired models as premium, merge cross-provider capability rows deterministically, and inherit tier scores through variant ids ([#11508](https://github.com/diegosouzapw/OmniRoute/issues/11508)). `getModelsDevTierFitness` now vetoes ids present in `config/quality/model-lifecycle.json` with status `retired` before any other signal; `loadModelCapabilities()` aggregates per `model_id` (any-non-null-true for booleans, max for `limit_context`) instead of last-write-wins over SQLite's undefined row order; a capabilities miss retries through `resolveScoresAs()`'s base id and reports `models_dev_tier:inherited`, matching layers 1–2 (#11492). Adds `getModelsDevTierFitnessWithSource()` and an `invalidateCapabilitiesCache()` test/ops hook. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) — thanks @Meet6338-X +- fix(sse): accept Claude `content:[]` + `stop_reason: max_tokens`/`tool_use` in `detectMalformedNonStream` (match `isEmptyContentResponse`) so Claude Code `/model` probes with `max_tokens: 1` no longer become a false 502 ([#11568](https://github.com/diegosouzapw/OmniRoute/pull/11568)) — thanks @Seramicx +- **fix(security):** sanitize `request.failed` diagnostics before publishing them to live dashboard listeners and replay history, while keeping status, model, provider, latency, and internal call-log diagnostics intact. ([#12469](https://github.com/diegosouzapw/OmniRoute/pull/12469)) +- **fix(pwa):** do not intercept dashboard navigations so Chrome can fall back from a stale HTTP/3 Alt-Svc advertisement (UDP :20128 is unpublished; F5 on a long-lived tab hung until a new tab opened a fresh TCP connection). ([#12767](https://github.com/diegosouzapw/OmniRoute/pull/12767)) — thanks @HouMinXi +- **fix(executors):** handle DuckDuckGo ERR_BN_LIMIT (418) without retrying — when the upstream returns `418 ERR_BN_LIMIT` (rate-limit/ban), the executor now returns the error immediately instead of burning another VQD acquisition that would only count against the IP limit. The retry logic for `418 ERR_CHALLENGE` (unsolved challenge) remains unchanged. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) ([#12110](https://github.com/diegosouzapw/OmniRoute/pull/12110)) — thanks @oyi77 / @Meet6338-X +- Fixed DuckDuckGo streaming responses losing JSON lines and UTF-8 characters split across network chunks. ([#11528](https://github.com/diegosouzapw/OmniRoute/pull/11528)) — thanks @pacocartones +- Harden generated Cloudflare, Vercel, and Deno relays by stripping hop-by-hop, framing, and proxy authentication headers before forwarding requests upstream. ([#11533](https://github.com/diegosouzapw/OmniRoute/pull/11533)) — thanks @pacocartones +- **feat(grok-cli):** Provider Limits shows grok-cli banked reset credits from `GetRemainingResets` (including a real zero; a failed RPC omits the row) and the existing View credits button now calls `ConsumerUiSvc/RedeemReset` for grok-cli. Live tokens use nested fields 10/20/30 (id + Timestamp), not compact 1/2/3. ([#12805](https://github.com/diegosouzapw/OmniRoute/pull/12805)) — thanks @HouMinXi +- **fix(memory):** Embedding Model Quick select, Embedding Source remote dropdown, and Rerank selector now list every configured provider with embedding/rerank support instead of only chat-catalog text matches plus OpenRouter live discovery; a generic OpenAI-compatible `/embeddings` + Cohere-compatible `/rerank` runtime fallback resolves any configured chat provider's embedding/rerank endpoint, so unlisted providers no longer fail with "Unknown embedding provider"; both memory selectors gained a free-text model override ([#11390](https://github.com/diegosouzapw/OmniRoute/pull/11390)) — thanks @rqzbeh +- Harden SQLite upgrades around the historical migration-074 version collision: missing discovery and inspector tables are replayed atomically, pre-existing databases (including setup-created skeletons) receive reusable content-addressed safety snapshots, and Node test/eval probes without `DATA_DIR` are isolated from the operator database. ([#12435](https://github.com/diegosouzapw/OmniRoute/pull/12435)) +- **fix(dashboard):** Moonshot/Kimi Open Platform voucher and cash leftover percentages follow the bucket balance, so an empty wallet no longer paints those rows as 100% while Available is 0% ([#12733](https://github.com/diegosouzapw/OmniRoute/pull/12733)) — thanks @HouMinXi +- **fix(combo):** return non-retryable HTTP 400 when all candidates for a pinned native Codex turn are unavailable due to model-scoped lockout, terminating the turn cleanly while preserving turn continuity and enabling standard Combo routing on subsequent turns ([#12240](https://github.com/diegosouzapw/OmniRoute/pull/12240)) — thanks @mdigitalbh81 +- Fixed auto-router strategies preserving the selected connection through ranking and dispatch when multiple connections share the same provider and model. ([#11530](https://github.com/diegosouzapw/OmniRoute/pull/11530)) — thanks @pacocartones +- Preserve Chat Completions JSON-object response formats when translating requests to the Responses API. ([#11529](https://github.com/diegosouzapw/OmniRoute/pull/11529)) — thanks @pacocartones +- Fix idempotency fingerprints so Responses API requests include `input` and other semantic generation fields using stable serialization, while excluding credentials and request metadata. ([#11532](https://github.com/diegosouzapw/OmniRoute/pull/11532)) — thanks @pacocartones +- **fix(api):** Generated API CLI commands now enforce required OpenAPI request bodies; Combo test commands forward the required `comboName` body, while API keys created by older writers after migration 149 preserve legacy allow-all Combo access without widening explicit empty allowlists — thanks @marcelokarval ([#12070](https://github.com/diegosouzapw/OmniRoute/pull/12070)) +- **fix(grok-web):** treat upstream streaming failures as failures instead of successful assistant text: error-only streams now fail readiness with HTTP 502, while failures after legitimate content preserve that partial output and terminate through the sanitized stream failure path without a normal `stop` completion. ([#12458](https://github.com/diegosouzapw/OmniRoute/pull/12458)) +- HuggingChat now turns HTTP 200 JSONL generation failures into a sanitized 502 before content, or a fixed public stream failure after partial output, so fallback and request persistence no longer record a false successful stop. ([#12456](https://github.com/diegosouzapw/OmniRoute/pull/12456)) +- **fix(providers):** keep 1min.ai HTTP 200 stream errors out of assistant content, preserve partial output, and expose sanitized terminal errors so pre-content failures can fall back. ([#12466](https://github.com/diegosouzapw/OmniRoute/pull/12466)) +- **fix(sessions):** preserve legacy Sessions data when the additive exclusive-session projection is unavailable, returning an empty projection and warning only once per contiguous outage instead of failing the endpoint; the Sessions badge intentionally reflects the merged legacy and exclusive row count introduced by [#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389) ([#11469](https://github.com/diegosouzapw/OmniRoute/pull/11469)) — thanks @KaspaPulse +- Render Nimble Search with the generic provider icon and serve Opper's proven logo locally. ([#12415](https://github.com/diegosouzapw/OmniRoute/pull/12415)) +- **fix(quality):** `config/quality/provider-assets-provenance.jsonl` was stale against the physical `public/providers/` tree (still listing 225 assets and 5 duplicate-content groups after several provider-retirement PRs removed 85 icon files), which would have made `check:provider-asset-provenance` fail on the next full run. Reconciled the manifest to the real 142-file snapshot (142 asset records, 1 duplicate-content group, 2 newly-registered files marked `unresolved` pending provenance research), repinned `auditedCommit`, and updated the regression test's hardcoded counts to match. ([#11876](https://github.com/diegosouzapw/OmniRoute/pull/11876)) +- fix(ui): wrap quota card footer action buttons to prevent clipping Refresh button on narrow cards — when extra action buttons such as `View credits` are present on Codex cards, allow the footer row to flex-wrap with `shrink-0` buttons so `Refresh now` remains visible and clickable across all responsive card widths ([#11464](https://github.com/diegosouzapw/OmniRoute/pull/11464)) — thanks @adevwithpurpose +- Keep Antigravity Gemini usable when the same connection's Claude weekly quota is empty; generic quota cache stays per-connection for every other provider. ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) — thanks @HouMinXi +- Fixed Tailscale tunnel setup to reject ports outside the valid 1-65535 range and safely install Windows MSI packages from paths containing apostrophes. ([#11534](https://github.com/diegosouzapw/OmniRoute/pull/11534)) — thanks @pacocartones +- Classify Cloudflared and Tailscale process-management routes as local-only and spawn-capable while preserving remote access to read-only tunnel status endpoints. ([#11531](https://github.com/diegosouzapw/OmniRoute/pull/11531)) — thanks @pacocartones +- Electron release workflow: the `publish-npm` job now grants `actions: read` to the reusable `npm-publish.yml` it calls (its `publish` job requests it), which is what made GitHub refuse the whole v3.8.50 run at startup and ship the release with zero desktop assets; a `workflow_dispatch` now builds the requested tag instead of the dispatching branch and can skip the npm leg (`publish_npm=false`) when only re-attaching assets ([#11974](https://github.com/diegosouzapw/OmniRoute/pull/11974)) +- **fix(dashboard):** The Combos page usage guide now reads its dismissal through `useSyncExternalStore` instead of correcting SSR state inside an effect, removing an extra commit of the page tree on every load (and the `react-hooks/set-state-in-effect` error it raised). ([#12671](https://github.com/diegosouzapw/OmniRoute/pull/12671)) +- Electron release: `electron/package-lock.json` regained the optional `electron-builder-squirrel-windows` subtree (13 entries) that `npm ci` had been refusing as out of sync, `electron-release.yml` gained a `build_ref` dispatch input and stops regenerating release notes on a re-attach dispatch, and the npm publish workflow attaches the SBOM to the GitHub Release on dispatch publishes too — so the v3.8.51 tag ships every desktop asset and the SBOM like v3.8.49 did ([#12022](https://github.com/diegosouzapw/OmniRoute/pull/12022)) +- Drained the reds every PR against `release/v3.8.51` was born with: documented the three service-worker build-id variables, registered the six retirement/tunnel tests with the mutation gate, approved `eslint-plugin-react-hooks` in the dependency allowlist, added the six `combo.sort.*` strings to `vi` and `pt-BR`, pointed the ChatGPT Web doc at the real migration-168 test, worded the g4f hint around the member key, and realigned four tests to the retired-provider catalog and the legacy-schema fixtures the retirement migrations touch. ([#11940](https://github.com/diegosouzapw/OmniRoute/pull/11940)) +- **fix(providers):** Claude, Grok, LMArena, Notion, and Perplexity web-cookie transports now use pooled `wreq-js` 3.2 instead of the native sidecar, with all nine supported bindings pinned and audited, and the applicable platform binding plus native-license evidence included in each release artifact ([#12429](https://github.com/diegosouzapw/OmniRoute/pull/12429), supersedes [#11753](https://github.com/diegosouzapw/OmniRoute/pull/11753)). +- **fix(providers):** Zed Hosted streaming failures now trigger fallback before content and end partial streams with a sanitized structured error instead of fake assistant text and a normal-success stop. ([#12455](https://github.com/diegosouzapw/OmniRoute/pull/12455)) +- **fix(test):** capture hook result after render (direct commit `b85d0abb0b`) +- **fix(sync):** repair the two regressions the v3.8.50 sync-back left on release/v3.8.51 (direct commit `5ade9e0851`) +- **revert(models):** drop the memo reset hook ported from #10055 (direct commit `282de841ae`) +- **fix(typecheck):** guard the optional connectionId before the reactive model sync (direct commit `f2825bad75`) +- **fix(quota-share):** release the winner's reserved in-flight slot (#11371) ([#11408](https://github.com/diegosouzapw/OmniRoute/pull/11408)) — thanks @oyi77 +- **fix(providers):** stop silently dropping reasoning effort on opencode families (#10788) ([#11409](https://github.com/diegosouzapw/OmniRoute/pull/11409)) — thanks @oyi77 +- **fix(combo):** enable genuine quota-aware routing for generic providers (antigravity, claude, etc.) ([#11411](https://github.com/diegosouzapw/OmniRoute/pull/11411)) — thanks @HouMinXi +- **fix(oauth):** allow expired token retry before deactivation and activate expired retry loop (#P0) ([#11414](https://github.com/diegosouzapw/OmniRoute/pull/11414)) — thanks @HouMinXi +- **perf(executors):** lazy-load the executor registry — defer class imports + construction to first use (#11220) ([#11421](https://github.com/diegosouzapw/OmniRoute/pull/11421)) — thanks @oyi77 +- **fix:** make release/v3.8.50 CI gates green (run 32786966560 root causes) ([#11450](https://github.com/diegosouzapw/OmniRoute/pull/11450)) — thanks @jonlwheat2-gif +- **fix(sse):** exact-domain cookie match and origin-equality URL assertions ([#11452](https://github.com/diegosouzapw/OmniRoute/pull/11452)) — thanks @hartmark +- **fix(providers):** send Responses API payload during OpenAI-compatible validation ([#11454](https://github.com/diegosouzapw/OmniRoute/pull/11454)) — thanks @tuandinh0801 +- **fix(command-code):** fallback to /alpha/generate for Go plan without Provider API access ([#11455](https://github.com/diegosouzapw/OmniRoute/pull/11455)) — thanks @Chewji9875 +- **fix(responses-continuation):** fail closed on a log-truncated stored input/output array ([#11473](https://github.com/diegosouzapw/OmniRoute/pull/11473)) — thanks @hartmark +- **fix(cli):** write jcode settings as [providers.omniroute] in config.toml ([#11484](https://github.com/diegosouzapw/OmniRoute/pull/11484)) — thanks @hongnoul +- **fix(logging):** raise chat-log array truncation cap to 1000 and unify duplicate implementations ([#11499](https://github.com/diegosouzapw/OmniRoute/pull/11499)) — thanks @hartmark +- **fix:** restore live websocket URL helpers ([#11509](https://github.com/diegosouzapw/OmniRoute/pull/11509)) — thanks @PauloFH +- **perf(core):** eliminate async waterfalls across home, cache, and db operations (#11396) ([#11512](https://github.com/diegosouzapw/OmniRoute/pull/11512)) — thanks @HouMinXi +- **fix(kiro):** add runtime.us-east-1.kiro.dev as first-attempt endpoint ([#11517](https://github.com/diegosouzapw/OmniRoute/pull/11517)) — thanks @rifqiawl +- **fix(providers):** remove duplicate 128-tool truncation in OpencodeExecutor (#11444) ([#11518](https://github.com/diegosouzapw/OmniRoute/pull/11518)) — thanks @HouMinXi +- **fix(providers):** poll onboardUser LRO before classifying BYOP ([#11519](https://github.com/diegosouzapw/OmniRoute/pull/11519)) — thanks @rifqiawl +- **fix(translator):** preserve exact tool name casing for Claude tool_use payloads (#11487) ([#11520](https://github.com/diegosouzapw/OmniRoute/pull/11520)) — thanks @HouMinXi +- **fix(kimi-web):** align health probe with executor domain ([#11521](https://github.com/diegosouzapw/OmniRoute/pull/11521)) — thanks @pacocartones +- **fix(cli):** preserve existing admin password during setup ([#11522](https://github.com/diegosouzapw/OmniRoute/pull/11522)) — thanks @pacocartones +- **fix(mitm):** keep Windows DNS elevation off the POSIX sudo path (#11430) ([#11542](https://github.com/diegosouzapw/OmniRoute/pull/11542)) — thanks @official-burak +- **fix(sse):** scale chat admission by ingest byte budget instead of a fixed request count ([#11548](https://github.com/diegosouzapw/OmniRoute/pull/11548)) — thanks @TheDemonTuan +- **fix:** accept Gemini Web cookie exports ([#11554](https://github.com/diegosouzapw/OmniRoute/pull/11554)) — thanks @rafacpti23 +- **fix(i18n):** complete Brazilian Portuguese translations ([#11558](https://github.com/diegosouzapw/OmniRoute/pull/11558)) — thanks @rafacpti23 +- **fix(sse):** explicit types for openai-responses pureHelpers — clears last failing core typecheck gate ([#11567](https://github.com/diegosouzapw/OmniRoute/pull/11567)) — thanks @jonlwheat2-gif +- **fix(opencode):** hydrate Proxy Pool references for no-auth connections ([#11584](https://github.com/diegosouzapw/OmniRoute/pull/11584)) — thanks @AStupidBear +- **fix(ci):** resolve inherited release/v3.8.51 base-red CI failures ([#11608](https://github.com/diegosouzapw/OmniRoute/pull/11608)) — thanks @jonlwheat2-gif +- **fix(db):** fallback load STORAGE_ENCRYPTION_KEY from env files ([#11614](https://github.com/diegosouzapw/OmniRoute/pull/11614)) — thanks @benzntech +- **fix(providers):** mark Brave Search as no longer having a free tier ([#11621](https://github.com/diegosouzapw/OmniRoute/pull/11621)) — thanks @turbolego +- **fix:** pass tool result content through as string in OpenAI/Claude -> Gemini translation ([#11624](https://github.com/diegosouzapw/OmniRoute/pull/11624)) — thanks @Hsia97 +- **fix(sse):** scope credential health to exact key ([#11628](https://github.com/diegosouzapw/OmniRoute/pull/11628)) — thanks @KaspaPulse +- **fix(install):** guard prepare script against missing husky ([#11634](https://github.com/diegosouzapw/OmniRoute/pull/11634)) — thanks @Prajeeth-12 +- **fix(security):** redact NVIDIA_API_KEY at the log sink in the ad-hoc diag script ([#11639](https://github.com/diegosouzapw/OmniRoute/pull/11639)) +- **fix(types):** clear the two type regressions red on release/v3.8.51 ([#11640](https://github.com/diegosouzapw/OmniRoute/pull/11640)) — thanks @jonlwheat2-gif +- **fix(build):** resolve workspace: protocol dependencies to concrete semver versions during prepublish (#11540) ([#11641](https://github.com/diegosouzapw/OmniRoute/pull/11641)) — thanks @HouMinXi +- **fix(search):** properly resolve configured search connection in /v1/responses pipeline (#11524) ([#11642](https://github.com/diegosouzapw/OmniRoute/pull/11642)) — thanks @HouMinXi +- **fix(authz):** trust cf-connecting-ip for via-proxy detection when peer is Cloudflare edge (#11514) ([#11643](https://github.com/diegosouzapw/OmniRoute/pull/11643)) — thanks @HouMinXi +- **fix(security):** compare the OIDC state cookie in constant time ([#11646](https://github.com/diegosouzapw/OmniRoute/pull/11646)) +- **fix(security):** namespace the dedup hash by the calling API key ([#11649](https://github.com/diegosouzapw/OmniRoute/pull/11649)) +- **fix(models):** gate the Codex-native catalog loop on prefix mode (#11632) ([#11662](https://github.com/diegosouzapw/OmniRoute/pull/11662)) — thanks @xiaoyaner0201 +- **fix(dashboard):** project Codex routing quota in account pools ([#11666](https://github.com/diegosouzapw/OmniRoute/pull/11666)) — thanks @KaspaPulse +- **fix(ddgw):** normaliza system no upstream da release v3.8.51 ([#11692](https://github.com/diegosouzapw/OmniRoute/pull/11692)) — thanks @RhianB14 +- **fix(memory):** honor category filter in GET /api/memory ([#11699](https://github.com/diegosouzapw/OmniRoute/pull/11699)) — thanks @Sabeekhann +- **fix(ci):** detect stale workspace lockfile entries ([#11714](https://github.com/diegosouzapw/OmniRoute/pull/11714)) — thanks @pacocartones +- **fix(ci):** stamp dist/BUILD_SHA before the npm publish provenance gate ([#11721](https://github.com/diegosouzapw/OmniRoute/pull/11721)) +- **fix(docker):** decouple the best-effort Bun image from the release manifest ([#11724](https://github.com/diegosouzapw/OmniRoute/pull/11724)) +- **perf(compression):** avoid cloning memo misses twice ([#11727](https://github.com/diegosouzapw/OmniRoute/pull/11727)) — thanks @RaviTharuma +- **fix(docker):** let the bun digest artifact be absent, not fatal ([#11740](https://github.com/diegosouzapw/OmniRoute/pull/11740)) +- **fix(shared):** block cloud-metadata hosts under default remote-image guard ([#11755](https://github.com/diegosouzapw/OmniRoute/pull/11755)) — thanks @santosraju99-hub +- **fix(api):** preserve caller-provided X-Correlation-Id on chat completions ([#11760](https://github.com/diegosouzapw/OmniRoute/pull/11760)) — thanks @aniruddhaadak80 +- **fix(resilience):** lock permanently retired models instead of short backoff (Gemini ban prevention) ([#11762](https://github.com/diegosouzapw/OmniRoute/pull/11762)) — thanks @turbolego +- **fix:** mark Vercel AI Gateway as passthroughModels ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **fix(resilience):** stop hammering permanently-moved endpoints and billing-suspended accounts ([#11774](https://github.com/diegosouzapw/OmniRoute/pull/11774)) — thanks @turbolego +- **fix(lease):** remove global static reservation and gate routing on live active lease occupancy ([#11775](https://github.com/diegosouzapw/OmniRoute/pull/11775)) — thanks @TheDemonTuan +- **fix(ci):** size the install-upgrade gate to a measured run, and log the pack cost ([#11776](https://github.com/diegosouzapw/OmniRoute/pull/11776)) +- **fix(pwa):** stop serving the stale shell after deploys ([#11779](https://github.com/diegosouzapw/OmniRoute/pull/11779)) — thanks @HouMinXi +- **fix(combo):** lock GitHub models rejected as "not supported" for future requests ([#11781](https://github.com/diegosouzapw/OmniRoute/pull/11781)) — thanks @turbolego +- **fix(cli):** probe both IPv4 and IPv6 loopback for server readiness (#11766) ([#11794](https://github.com/diegosouzapw/OmniRoute/pull/11794)) — thanks @vermasomesh835 +- **fix(sse):** preserve CLIProxyAPI wrappers per connection ([#11797](https://github.com/diegosouzapw/OmniRoute/pull/11797)) — thanks @RaviTharuma +- **fix(providers):** honor model auto-fetch opt-in on create ([#11798](https://github.com/diegosouzapw/OmniRoute/pull/11798)) — thanks @RaviTharuma +- **fix(dashboard):** make model auto-fetch order independent ([#11805](https://github.com/diegosouzapw/OmniRoute/pull/11805)) — thanks @RaviTharuma +- **fix(docker):** make GHA cache exports best effort ([#11806](https://github.com/diegosouzapw/OmniRoute/pull/11806)) — thanks @RaviTharuma +- **fix(kiro):** do not permanently ban on 'User is not authorized to make this call' ([#11809](https://github.com/diegosouzapw/OmniRoute/pull/11809)) — thanks @Deftera186 +- **fix(sse):** keep cache-write tokens in OpenAI-shaped usage ([#11814](https://github.com/diegosouzapw/OmniRoute/pull/11814)) — thanks @TheDemonTuan +- **fix(docs):** resolve relative markdown and wiki links across Fumadocs and GitHub wiki ([#11834](https://github.com/diegosouzapw/OmniRoute/pull/11834)) — thanks @AndrianBalanescu +- **fix(chat):** preserve unstripped model string for passthrough provider routing ([#11840](https://github.com/diegosouzapw/OmniRoute/pull/11840)) — thanks @raheemuddin786 +- **fix(mcp):** avoid async initialization inside sync esm wrappers (#11569) ([#11849](https://github.com/diegosouzapw/OmniRoute/pull/11849)) — thanks @HouMinXi +- **fix(sse):** preserve coherent markdown token boundaries across stream translation chunks (#11606) ([#11851](https://github.com/diegosouzapw/OmniRoute/pull/11851)) — thanks @HouMinXi +- **fix(ci):** run the install-upgrade gate on disk, not on the /tmp tmpfs ([#11855](https://github.com/diegosouzapw/OmniRoute/pull/11855)) +- **fix(skills):** expand shorthand property types in injected tool schemas ([#11857](https://github.com/diegosouzapw/OmniRoute/pull/11857)) — thanks @alvinveroy +- **fix(ci):** publish npm from a hosted runner so provenance is accepted ([#11877](https://github.com/diegosouzapw/OmniRoute/pull/11877)) +- **fix(lint):** pin react hooks plugin to 7.0.1 ([#11886](https://github.com/diegosouzapw/OmniRoute/pull/11886)) +- **fix(ci):** surface and clear release-green ESLint failure ([#11890](https://github.com/diegosouzapw/OmniRoute/pull/11890)) — thanks @MumuTW +- **fix(guardrails):** prevent duplicate prompt-injection-guard log output ([#11936](https://github.com/diegosouzapw/OmniRoute/pull/11936)) — thanks @patrykkopycinski +- **fix(auth):** downgrade expected transient states from warn to debug ([#11937](https://github.com/diegosouzapw/OmniRoute/pull/11937)) — thanks @patrykkopycinski +- **fix(codex):** fail over image generation for imported free plans ([#11948](https://github.com/diegosouzapw/OmniRoute/pull/11948)) — thanks @Bl0ck154 +- **fix(codex):** normalize non-stream responses ([#11951](https://github.com/diegosouzapw/OmniRoute/pull/11951)) — thanks @Bl0ck154 +- **fix(images):** forward Antigravity image size ([#11952](https://github.com/diegosouzapw/OmniRoute/pull/11952)) — thanks @Bl0ck154 +- **fix(db):** invalidate connection cache after upsert ([#11953](https://github.com/diegosouzapw/OmniRoute/pull/11953)) — thanks @Bl0ck154 +- **fix(codex):** restore imported account state ([#11954](https://github.com/diegosouzapw/OmniRoute/pull/11954)) — thanks @Bl0ck154 +- **fix(sse):** give extended-thinking targets the reasoning readiness budget ([#11959](https://github.com/diegosouzapw/OmniRoute/pull/11959)) — thanks @abhisheksharma2411 +- **fix(dashboard):** use opaque background and readable text color on cost chart tooltips ([#11960](https://github.com/diegosouzapw/OmniRoute/pull/11960)) — thanks @ZaimMarzuki +- **fix(antigravity):** send complete loadCodeAssist metadata (ideType/platform/pluginType as numeric enums) ([#11969](https://github.com/diegosouzapw/OmniRoute/pull/11969)) — thanks @rifqiawl +- **fix(core):** resolve DB init race condition and reasoning translation ([#12003](https://github.com/diegosouzapw/OmniRoute/pull/12003)) — thanks @raheemuddin786 +- **fix(admission):** exclude reclaimable page cache from the cgroup pressure ratio ([#12017](https://github.com/diegosouzapw/OmniRoute/pull/12017)) — thanks @HouMinXi +- **fix(build):** prune non-production trees in NFT trace excludes and tsconfig ([#12028](https://github.com/diegosouzapw/OmniRoute/pull/12028)) — thanks @Chewji9875 +- **fix(migrations):** add renamed migration compatibility for 056/073/077/101 ([#12036](https://github.com/diegosouzapw/OmniRoute/pull/12036)) — thanks @oyi77 +- **fix(sse):** default crash-guard logger to console.warn, not console ([#12042](https://github.com/diegosouzapw/OmniRoute/pull/12042)) — thanks @HouMinXi +- **fix(catalog):** derive vision/modalities for built-in auto combos from effective target pool ([#12046](https://github.com/diegosouzapw/OmniRoute/pull/12046)) — thanks @Prajeeth-12 +- **fix(release):** the packaged-app smoke verifies the database opened, not a driver line the primary path never prints (twin of #12032) ([#12047](https://github.com/diegosouzapw/OmniRoute/pull/12047)) +- **fix(barrel):** migrate src/app/ off the @/lib/localDb barrel import (#11795 Phase 2) ([#12051](https://github.com/diegosouzapw/OmniRoute/pull/12051)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate src/lib/ off the localDb barrel to direct db imports (#59) ([#12052](https://github.com/diegosouzapw/OmniRoute/pull/12052)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate open-sse, src/shared, src/sse, src/models, src/domain off the @/lib/localDb barrel import (#11795 Phase 4) ([#12053](https://github.com/diegosouzapw/OmniRoute/pull/12053)) — thanks @jonlwheat2-gif +- **fix(barrel):** delete the @/lib/localDb barrel — every consumer migrated (#11795 Phase 5) ([#12055](https://github.com/diegosouzapw/OmniRoute/pull/12055)) — thanks @jonlwheat2-gif +- **fix(radar):** restore D12 public boundary ([#12057](https://github.com/diegosouzapw/OmniRoute/pull/12057)) +- **fix(dev):** bound webpack and Tailwind scans ([#12075](https://github.com/diegosouzapw/OmniRoute/pull/12075)) — thanks @backryun +- **fix(dev):** isolate root layout settings reads ([#12076](https://github.com/diegosouzapw/OmniRoute/pull/12076)) — thanks @backryun +- **fix(dev):** reduce instrumentation executor fan-out (phase 3) ([#12078](https://github.com/diegosouzapw/OmniRoute/pull/12078)) — thanks @backryun +- **fix(dev):** make logging resources HMR-singleton ([#12079](https://github.com/diegosouzapw/OmniRoute/pull/12079)) — thanks @backryun +- **fix(sse):** keep unavailable forced connections scoped ([#12080](https://github.com/diegosouzapw/OmniRoute/pull/12080)) — thanks @keeltrace +- **fix(dev):** isolate batch dispatch from instrumentation ([#12081](https://github.com/diegosouzapw/OmniRoute/pull/12081)) — thanks @backryun +- **fix(ci):** reconcile release test contract drift ([#12082](https://github.com/diegosouzapw/OmniRoute/pull/12082)) — thanks @backryun +- **fix(ci):** accept CVE-2025-68121 in the prebuilt tls-client .so, auto-close base-red issues, guard Scorecard on the default branch ([#12085](https://github.com/diegosouzapw/OmniRoute/pull/12085)) +- **fix(db):** drop three consumer-less 1proxy exports — dead-code base-red on release/v3.8.51 after the barrel deletion (#12055) ([#12087](https://github.com/diegosouzapw/OmniRoute/pull/12087)) +- **fix(api):** clear the six API-route TypeScript regressions the new gate landed red on ([#12094](https://github.com/diegosouzapw/OmniRoute/pull/12094)) +- **fix(release):** never let the tag-push Create Release append auto notes to the curated body ([#12096](https://github.com/diegosouzapw/OmniRoute/pull/12096)) +- **fix(sse):** honor CLIProxyAPI environment API key ([#12099](https://github.com/diegosouzapw/OmniRoute/pull/12099)) — thanks @RaviTharuma +- **fix(oauth):** bind Google refresh to the client that issued the token ([#12106](https://github.com/diegosouzapw/OmniRoute/pull/12106)) — thanks @HouMinXi +- **fix(dashboard):** make RequestLoggerDetail loadable outside Next — CSS via globals.css + CJS/ESM interop (#11703 base-reds) ([#12114](https://github.com/diegosouzapw/OmniRoute/pull/12114)) +- **fix(leases):** project status lease row to lease columns so joined connection PII never escapes ([#12115](https://github.com/diegosouzapw/OmniRoute/pull/12115)) — thanks @geek007git +- **fix(plugins):** do not kill the plugin process when a fire-and-forget hook times out ([#12116](https://github.com/diegosouzapw/OmniRoute/pull/12116)) — thanks @geek007git +- **fix(guardrails):** restore injection-guard logging on middleware-only routes ([#12117](https://github.com/diegosouzapw/OmniRoute/pull/12117)) — thanks @geek007git +- **fix(plugins):** refresh stored manifest from disk on activate so new hook fields reach existing installs ([#12120](https://github.com/diegosouzapw/OmniRoute/pull/12120)) — thanks @geek007git +- **fix(codex):** preserve existing provider state when bulk-import upserts a matching connection ([#12122](https://github.com/diegosouzapw/OmniRoute/pull/12122)) — thanks @geek007git +- **fix(ci):** clear the two base-reds the 2026-08-30 merge batch left on release/v3.8.51 (round 3) ([#12123](https://github.com/diegosouzapw/OmniRoute/pull/12123)) +- **fix(sse):** strip type:'custom' from Claude tools on agentrouter dispatch ([#12126](https://github.com/diegosouzapw/OmniRoute/pull/12126)) — thanks @wahidsadik371-coder +- **fix(sse):** stop advertising video providers the dispatcher cannot run ([#12131](https://github.com/diegosouzapw/OmniRoute/pull/12131)) — thanks @quiterunner-commits +- **fix(search):** treat HTTP 432 and plan limit errors as transient cooldown ([#12139](https://github.com/diegosouzapw/OmniRoute/pull/12139)) — thanks @HouMinXi +- **fix(sse):** refuse an AI Horde queue that cannot fit the request budget ([#12143](https://github.com/diegosouzapw/OmniRoute/pull/12143)) — thanks @quiterunner-commits +- **fix(ci):** clear the base-reds the afternoon merge batch left on release/v3.8.51 (round 5: provider count 352, TS2554/TS2677) ([#12144](https://github.com/diegosouzapw/OmniRoute/pull/12144)) +- **fix(translator):** strip neutral tool_choice when tools absent in Responses-to-Chat (#12141) ([#12166](https://github.com/diegosouzapw/OmniRoute/pull/12166)) — thanks @HouMinXi +- **fix(guardrails):** pass providerId to getResolvedModelCapabilities in checkComboVision (#12112) ([#12169](https://github.com/diegosouzapw/OmniRoute/pull/12169)) — thanks @HouMinXi +- **fix(translator):** strip plaintext reasoning content for opaque responses backends (#12128) ([#12171](https://github.com/diegosouzapw/OmniRoute/pull/12171)) — thanks @HouMinXi +- **perf(stream):** compile hot-path regexes once, bound token caches, fix quadratic buffering ([#12179](https://github.com/diegosouzapw/OmniRoute/pull/12179)) — thanks @opensource-elearning +- **fix:** resolve compression worker file using runtime anchors instead of… ([#12183](https://github.com/diegosouzapw/OmniRoute/pull/12183)) — thanks @NightStalker-87 +- **fix:** prevent Claude Code session kills via liveness-aware readiness + auto model echo ([#12189](https://github.com/diegosouzapw/OmniRoute/pull/12189)) — thanks @opensource-elearning +- **fix(translator):** preserve falsy primitive values in Gemini and Antigravity function response results ([#12191](https://github.com/diegosouzapw/OmniRoute/pull/12191)) — thanks @Siva010 +- **fix(oauth):** mark empty Antigravity projectId as degraded and clear stale errors (#11284) ([#12205](https://github.com/diegosouzapw/OmniRoute/pull/12205)) — thanks @HouMinXi +- **fix(providers):** gate the Codex auto-ping usage read on the shared quota throttle ([#12209](https://github.com/diegosouzapw/OmniRoute/pull/12209)) — thanks @abhisheksharma2411 +- **fix(combos):** prioritize SQLite row id over inner JSON id and notify delete errors ([#12213](https://github.com/diegosouzapw/OmniRoute/pull/12213)) — thanks @HouMinXi +- **fix(providers):** mark featherless.ai as no longer having a free tier (`hasFree: false`) ([#12216](https://github.com/diegosouzapw/OmniRoute/pull/12216)) — thanks @turbolego +- **fix(deepseek-web):** stop Turbopack dev panic in the PoW worker path resolver ([#12221](https://github.com/diegosouzapw/OmniRoute/pull/12221)) — thanks @hartmark +- **fix(chat-admission):** clarify local 503 source ([#12223](https://github.com/diegosouzapw/OmniRoute/pull/12223)) — thanks @keeltrace +- **fix(dev):** silence webpack runtime module warnings ([#12228](https://github.com/diegosouzapw/OmniRoute/pull/12228)) — thanks @backryun +- **fix(db):** use module.require for CommonJS runtime driver loading ([#12230](https://github.com/diegosouzapw/OmniRoute/pull/12230)) — thanks @raheemuddin786 +- **fix(memory):** honest probe-driven FTS5 keyword status + memory_id rowid sync ([#12231](https://github.com/diegosouzapw/OmniRoute/pull/12231)) — thanks @raheemuddin786 +- **fix(system):** propagate abort signal to stream reader in HTTP version checks ([#12232](https://github.com/diegosouzapw/OmniRoute/pull/12232)) — thanks @raheemuddin786 +- **fix(resilience):** isolate local host execution errors from provider circuit breakers ([#12233](https://github.com/diegosouzapw/OmniRoute/pull/12233)) — thanks @raheemuddin786 +- **fix(sse):** sort injected tools deterministically for prompt caching ([#12234](https://github.com/diegosouzapw/OmniRoute/pull/12234)) — thanks @raheemuddin786 +- **fix(combo):** always clear the loop-safety timer, not just on the happy path (#11804) ([#12245](https://github.com/diegosouzapw/OmniRoute/pull/12245)) +- **fix(quality):** base-red pair — stryker tap registration + turn-pin suites aligned to the window gate ([#12255](https://github.com/diegosouzapw/OmniRoute/pull/12255)) — thanks @backryun +- **fix(dev):** qualify Turbopack runtime boundaries ([#12258](https://github.com/diegosouzapw/OmniRoute/pull/12258)) — thanks @backryun +- **fix(combo):** fall back to full pool when collapsed sole survivor is context-too-small ([#12278](https://github.com/diegosouzapw/OmniRoute/pull/12278)) — thanks @opensource-elearning +- **fix(combo):** bound the pre-dispatch unavailable skip so a stale label cannot dark a pool (#12168) ([#12285](https://github.com/diegosouzapw/OmniRoute/pull/12285)) +- **fix(security):** strip Qwen/Alibaba console-session cookies from provider API responses ([#12287](https://github.com/diegosouzapw/OmniRoute/pull/12287)) +- **fix(usage):** console-aware Token Plan guidance + subscription hint on bailian 401 ([#12288](https://github.com/diegosouzapw/OmniRoute/pull/12288)) +- **fix(resourcePressure):** log numeric detail on every rejection, recover faster ([#12293](https://github.com/diegosouzapw/OmniRoute/pull/12293)) — thanks @hartmark +- **fix(release):** drain the 2026-09-01 base-red window — passthrough usage regression + radarPage i18n keys ([#12327](https://github.com/diegosouzapw/OmniRoute/pull/12327)) +- **fix(ci):** openapi-security-tiers checker must honor routeGuard patterns + imported prefixes ([#12350](https://github.com/diegosouzapw/OmniRoute/pull/12350)) — thanks @ggiak +- **perf(ci):** cache node_modules in the npm-ci-retry composite (#8084 D3) ([#12408](https://github.com/diegosouzapw/OmniRoute/pull/12408)) +- **revert(ui):** point CTAs back at their real destinations ([#12410](https://github.com/diegosouzapw/OmniRoute/pull/12410)) +- **fix(release):** drain the 2026-09-02 base-red — rerank-providers import + api-typecheck baseline ratchet ([#12414](https://github.com/diegosouzapw/OmniRoute/pull/12414)) +- **fix(types):** make system prompt injection noImplicitAny-safe ([#12416](https://github.com/diegosouzapw/OmniRoute/pull/12416)) +- **fix(quality):** validate typecheck baseline schema ([#12419](https://github.com/diegosouzapw/OmniRoute/pull/12419)) +- **fix(memory):** resolve rerank provider node cache import ([#12421](https://github.com/diegosouzapw/OmniRoute/pull/12421)) +- **fix(ci):** repair release-root regressions — pack dedup, web-session syntax, uc-image ids ([#12423](https://github.com/diegosouzapw/OmniRoute/pull/12423)) — thanks @backryun +- **fix(providers):** separate MaxAI and UC credential contracts ([#12431](https://github.com/diegosouzapw/OmniRoute/pull/12431)) +- **fix(cli):** preserve Claude settings on config set ([#12432](https://github.com/diegosouzapw/OmniRoute/pull/12432)) — thanks @KrzysiekSko +- **fix(providers):** repair the maxai credential block truncated by merge auto-resolve ([#12433](https://github.com/diegosouzapw/OmniRoute/pull/12433)) +- **fix(quality):** record the 2026-09-02 merged growth in the file-size baseline ([#12434](https://github.com/diegosouzapw/OmniRoute/pull/12434)) +- **fix(uc):** route UC error strings through sanitizeErrorMessage; allowlist the retired codex id ([#12437](https://github.com/diegosouzapw/OmniRoute/pull/12437)) +- **fix(adapta):** redact streamed upstream errors ([#12438](https://github.com/diegosouzapw/OmniRoute/pull/12438)) +- **fix(sse):** remap non-contiguous upstream tool_calls index to a gap-free output_index ([#12445](https://github.com/diegosouzapw/OmniRoute/pull/12445)) — thanks @hartmark +- **fix(quality):** detect duplicate tool_calls entries in one response ([#12446](https://github.com/diegosouzapw/OmniRoute/pull/12446)) — thanks @hartmark +- **fix(conversations):** resolve turn content OmniRoute never sends back to the client ([#12447](https://github.com/diegosouzapw/OmniRoute/pull/12447)) — thanks @hartmark +- **fix(combo):** do not treat credits-exhausted 401 as auth skip ([#12449](https://github.com/diegosouzapw/OmniRoute/pull/12449)) — thanks @RaviTharuma +- **fix(auth):** do not park healthy quota accounts as expired ([#12452](https://github.com/diegosouzapw/OmniRoute/pull/12452)) — thanks @RaviTharuma +- **fix(responses-continuation):** fail closed on a collector-truncated, empty output array ([#12460](https://github.com/diegosouzapw/OmniRoute/pull/12460)) — thanks @hartmark +- **fix(logging):** raise the SSE payload collector's default cap ([#12461](https://github.com/diegosouzapw/OmniRoute/pull/12461)) — thanks @hartmark +- **fix(resilience):** surface Responses failed.error.message in 502s ([#12472](https://github.com/diegosouzapw/OmniRoute/pull/12472)) — thanks @RaviTharuma +- **fix(security):** unbiased maxai X-Random nonce + stricter URL/regex assertions ([#12502](https://github.com/diegosouzapw/OmniRoute/pull/12502)) +- **fix(video):** re-anchor transcript log-redaction so PII/credential maskers can't reopen the leak (#12150 P1 follow-up) ([#12503](https://github.com/diegosouzapw/OmniRoute/pull/12503)) +- **fix(monitoring):** serve cached credentialHealth off the request path ([#12533](https://github.com/diegosouzapw/OmniRoute/pull/12533)) — thanks @RaviTharuma +- **fix(quota):** keep Antigravity Gemini usable when Claude weekly is empty ([#12566](https://github.com/diegosouzapw/OmniRoute/pull/12566)) — thanks @HouMinXi +- **fix(authz):** hard-gate every credential export and CLI-config write (GHSA-5926-2w35-7h4q) ([#12600](https://github.com/diegosouzapw/OmniRoute/pull/12600)) +- **fix(docs):** restore the Next build — REMOVED_PROVIDERS.md had no frontmatter (base-red #12581) ([#12610](https://github.com/diegosouzapw/OmniRoute/pull/12610)) +- **fix(quality):** re-point the zcodeProtocol public-creds allowlist to line 313 ([#12615](https://github.com/diegosouzapw/OmniRoute/pull/12615)) +- **fix(security):** close 3 advisories — search baseUrl exfil, sk- in the error sanitizer, bifrost relay header leak ([#12620](https://github.com/diegosouzapw/OmniRoute/pull/12620)) +- **fix(providers):** sanitize boolean required and nested bare maps for Gemini (#12269) ([#12624](https://github.com/diegosouzapw/OmniRoute/pull/12624)) — thanks @HouMinXi +- **fix(resilience):** keep Overloaded STREAM_EARLY_EOF off the provider breaker ([#12626](https://github.com/diegosouzapw/OmniRoute/pull/12626)) — thanks @HouMinXi +- **fix(catalog):** re-audit free-tier quotas against official pages ([#12649](https://github.com/diegosouzapw/OmniRoute/pull/12649)) +- **fix(dashboard):** keep a request's pending-tracking id stable across combo target retries ([#12650](https://github.com/diegosouzapw/OmniRoute/pull/12650)) — thanks @hartmark +- **fix(tests):** retire dead model ids from the chat-pipeline integration suite (base-red #12581) ([#12670](https://github.com/diegosouzapw/OmniRoute/pull/12670)) +- **fix:** resolve SqliteError no such table compression_run_telemetry during cleanup ([#12682](https://github.com/diegosouzapw/OmniRoute/pull/12682)) — thanks @groovecityJO +- **fix(combo):** treat a pin-only step as implicit connection allowlist ([#12697](https://github.com/diegosouzapw/OmniRoute/pull/12697)) — thanks @HouMinXi +- **fix(ci):** ship .npmrc in published package so legacy-peer-deps applies to consumers (#11544) ([#12699](https://github.com/diegosouzapw/OmniRoute/pull/12699)) — thanks @KooshaPari +- **fix(docker):** pin 4 CLI tools to exact versions (#12576) ([#12703](https://github.com/diegosouzapw/OmniRoute/pull/12703)) — thanks @KooshaPari +- **fix(docker):** document and harden cli profile trust boundary (#12570) ([#12706](https://github.com/diegosouzapw/OmniRoute/pull/12706)) — thanks @KooshaPari +- **fix(sse):** redact video transcript in pre-guardrail rejected-request logs (#12150 P2 item 7) ([#12710](https://github.com/diegosouzapw/OmniRoute/pull/12710)) +- **fix(i18n):** quote placeholder in OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES description (#12505) ([#12769](https://github.com/diegosouzapw/OmniRoute/pull/12769)) — thanks @KooshaPari +- **fix(api/pricing):** surface validation error message as string, not raw object (#12494) ([#12771](https://github.com/diegosouzapw/OmniRoute/pull/12771)) — thanks @KooshaPari +- **fix(sse):** re-enable prompt compression for native Codex passthrough ([#12834](https://github.com/diegosouzapw/OmniRoute/pull/12834)) — thanks @thomasmaerz +- **fix(dashboard):** model health tests for a provider node set to the Responses API now call `/v1/responses` with a Responses-shaped body instead of `/v1/chat/completions` — those models were reported as `Provider returned HTTP 200 but no text content` even though the same model answered normally through `/v1/responses` ([#13070](https://github.com/diegosouzapw/OmniRoute/issues/13070)) ([#13087](https://github.com/diegosouzapw/OmniRoute/pull/13087)) — thanks @ntdatt812 +- fix(routing): stop a round-robin combo's "opencode" targets from collapsing onto the opencode-zen connection (#11912) ([#13283](https://github.com/diegosouzapw/OmniRoute/pull/13283)) +- fix(routing): stop the reactive-compaction debug log from lying when compression is globally disabled (#11977) ([#13276](https://github.com/diegosouzapw/OmniRoute/pull/13276)) +- fix(electron): relativize standalone-bundle symlink targets so Stage 8 manifest verification stops failing on Windows (#11979) ([#13251](https://github.com/diegosouzapw/OmniRoute/pull/13251)) +- fix(dashboard): surface a visible error when Compression Studio's combined preview run fails (#12061) ([#13288](https://github.com/diegosouzapw/OmniRoute/pull/13288)) +- fix(dashboard): make the compression "Effective pipeline" preview honor the active profile and warn when the master switch is off (#12063) ([#13287](https://github.com/diegosouzapw/OmniRoute/pull/13287)) +- fix(providers): scope TinyCMS Web signer's DOM shims to each call instead of leaking them for the process lifetime, and surface a clean HTTP status on a non-JSON interception-toggles error (#12072) ([#13254](https://github.com/diegosouzapw/OmniRoute/pull/13254)) +- fix(guardrails): stop Vision Bridge from re-selecting a model locked after a 404 (#12111) ([#13259](https://github.com/diegosouzapw/OmniRoute/pull/13259)) +- fix(sse): require Responses-shaped body before native OpenAI-compatible passthrough (#12129) ([#13278](https://github.com/diegosouzapw/OmniRoute/pull/13278)) +- fix(providers): minimax-m3 now collapses manual thinking.type:"enabled" to adaptive, preventing upstream 400 (2013) (#12132) ([#13249](https://github.com/diegosouzapw/OmniRoute/pull/13249)) +- fix(db): scope model visibility overrides by modality so hiding a Chat model no longer hides an identically-ID'd Image/Embeddings/etc. model (#12172) ([#13258](https://github.com/diegosouzapw/OmniRoute/pull/13258)) +- fix(db): scope local-provider apiKey dedup to matching base URL so LM Studio/Ollama-style connections support multiple accounts (#12173) ([#13250](https://github.com/diegosouzapw/OmniRoute/pull/13250)) +- fix(providers): refresh Trae's stale Referer/Origin and forward user timezone so imported connections stop failing with 401 (#12190) ([#13255](https://github.com/diegosouzapw/OmniRoute/pull/13255)) +- fix(providers): route opencode-go/gpt-5.6-luna to /responses instead of /chat/completions (#12196) ([#13275](https://github.com/diegosouzapw/OmniRoute/pull/13275)) +- fix(dashboard): allow deleting the last extra-upstream-header row even when invalid (#12251) ([#13252](https://github.com/diegosouzapw/OmniRoute/pull/13252)) +- **fix(i18n):** translate pre-existing `__MISSING__:` keys for `combo.sort`, `requestLogger.detail` expand/collapse, `common.profile`, and `settings.resilienceCredentialHealth*` across 39 locales ([#12272](https://github.com/diegosouzapw/OmniRoute/issues/12272)) ([#13034](https://github.com/diegosouzapw/OmniRoute/pull/13034)) — thanks @HouMinXi +- fix(cli): run the Node.js runtime compatibility guard before the heavy `tsx/esm` + Commander import chain so an unsupported runtime gets a clear message instead of a raw `Invalid regular expression flags` crash (#12296) ([#13253](https://github.com/diegosouzapw/OmniRoute/pull/13253)) +- fix(dashboard): refresh the providers list after deleting a compatible provider node (#12298) ([#13256](https://github.com/diegosouzapw/OmniRoute/pull/13256)) +- fix(usage): fail closed on API-key budget enforcement when a provider's `auto` routing alias has no pricing row, instead of silently counting it as $0 (#12341) ([#13257](https://github.com/diegosouzapw/OmniRoute/pull/13257)) +- **fix(providers):** include the submitted Agnes video model when polling by `video_id` ([#12356](https://github.com/diegosouzapw/OmniRoute/pull/12356)) — thanks @pacocartones +- **fix(routing):** custom OpenAI-compatible nodes now honor the saved Chat/Responses API type after edits instead of letting the node's original ID prefix override the live connection setting ([#11884](https://github.com/diegosouzapw/OmniRoute/issues/11884)). ([#12358](https://github.com/diegosouzapw/OmniRoute/pull/12358)) — thanks @pacocartones +- fix(sse): surface an error instead of a silent empty 200 when a Claude stream closes with zero bytes (#12398) ([#13285](https://github.com/diegosouzapw/OmniRoute/pull/13285)) +- fix(oauth): warn before the dead localhost:8080 redirect in antigravity/gemini `oauth start` (#12413) ([#13265](https://github.com/diegosouzapw/OmniRoute/pull/13265)) +- fix(providers): stop devin-cli spawn error from double-closing the SSE controller (#12517) ([#13261](https://github.com/diegosouzapw/OmniRoute/pull/13261)) +- **fix(rerank):** clamp Voyage `top_k` to the documents actually sent after empty-string filtering, and honor `return_documents: false` in the NVIDIA response adapter (#12523 — thanks @pacocartones) +- **fix(audio):** `/v1/audio/translations` now resolves combo names the way `/v1/audio/transcriptions` already does, so a combo that `GET /v1/models` advertises is fanned out to its targets instead of being rejected with `400 Invalid translation model: . Use format: provider/model`; literal `provider/model` ids and unknown bare names behave as before (#12536 — thanks @pacocartones) +- **fix(gemini):** strip the JSON-Schema-2020-12 `prefixItems` keyword from Gemini tool schemas at every nesting level, so Claude Code tool definitions no longer fail with `400 Unknown name "prefixItems"` on Gemini models (#12540 — thanks @pacocartones) +- **fix(api-manager):** Expose an accessible loading status while API keys are fetched instead of an empty accessibility tree (#12541 — thanks @pacocartones) +- **fix(video):** Clamp `estimateJpegFrameBytes` at zero for padding-only payloads and build the three encode-side frame data URIs from `JPEG_FRAME_DATA_URI_PREFIX` instead of a repeated literal (#12543 — thanks @pacocartones) +- **fix(devin):** accept Windows `DEVIN_AGENTIC_HOME` sandbox paths (`C:\...\.sandbox\...`) in the isolated-home check so the Devin Claude Bridge no longer fails closed on Windows ([#12405](https://github.com/diegosouzapw/OmniRoute/issues/12405)) (#12545 — thanks @pacocartones) +- **fix(db):** Add `conversation_turn_nodes` and orphaned `agentic_conversations` to the auto-cleanup cycle under the existing `retention.callLogs` window, so identity nodes whose call-log content has already been purged no longer accumulate without bound in `storage.sqlite` (#12548 — thanks @pacocartones) +- **fix(i18n):** Wrap the `~/.claude/profiles//settings.json` placeholder in ICU single quotes in the `OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES` feature-flag description across all 42 locales and the TypeScript default, so next-intl no longer fails with `INVALID_MESSAGE: UNCLOSED_TAG` and the Feature Flags card shows the description instead of the raw key (#12549 — thanks @pacocartones) +- **fix(orchestration):** `updateCloudAgentTask` now publishes the task's real `status` on `agent.task.updated` when an update only touches `result`, `activities` or `error`, instead of the fabricated `"updated"` state, and stays silent when no row matched the id (#12550 — thanks @pacocartones) +- **fix(i18n):** the home "Recent Requests" panel and the Provider Topology legend are now translated instead of rendering English copies on non-English dashboards; the legend reads its own `home.topologyLegend*` labels with consistent casing rather than borrowing the memory-settings "Recent" and analytics "Error" strings (#12551 — thanks @pacocartones). +- **docs(reference):** bring the `FEATURE_FLAGS.md` catalog back to 1:1 with `featureFlagDefinitions.ts` — 20 missing flags added, the two `*_BLOCK_THRESHOLD` env-only knobs moved out of the flag tables, category/total counts and the Live WS port corrected, guarded by a static test (#12552 — thanks @pacocartones) +- **fix(i18n):** backfill missing `usage.kiloPass*` strings in 39 locales and restore `featureFlagOmnirouteDisableThinkingLevelVariantsDescription` in `pt.json` ([#12561](https://github.com/diegosouzapw/OmniRoute/issues/12561)) ([#13027](https://github.com/diegosouzapw/OmniRoute/pull/13027)) — thanks @HouMinXi +- fix(docker): default docker-compose app ports (dashboard/API/live-WS) to loopback instead of `0.0.0.0`, closing the anonymous `/v1` LAN/WAN exposure gap left open by `REQUIRE_API_KEY=false` (#12568) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): close DNS-rebinding SSRF gap in webhook outbound-URL guard (#12569) ([#13243](https://github.com/diegosouzapw/OmniRoute/pull/13243)) +- fix(docker): require a per-session token on the VNC browser CDP bridge and isolate it on a dedicated Docker network (#12571) ([#13241](https://github.com/diegosouzapw/OmniRoute/pull/13241)) +- fix(open-sse): write Adobe Firefly session tokens and cookie jars with 0700/0600 permissions instead of the process umask (#12572) ([#13242](https://github.com/diegosouzapw/OmniRoute/pull/13242)) +- fix(api): remove hardcoded wildcard CORS in openai-to-gemini-sse.ts so the centralized fail-closed CORS gate is the sole source of `Access-Control-Allow-Origin` (#12573) ([#13239](https://github.com/diegosouzapw/OmniRoute/pull/13239)) +- fix(api): enforce API key policy (budget/rate-limit/schedule/endpoint scoping) on the ElevenLabs speech-to-text, text-to-speech and voices proxy routes (#12574) ([#13244](https://github.com/diegosouzapw/OmniRoute/pull/13244)) +- fix(sse): cap HuggingChat NDJSON body size and bound the read loop with the fetch timeout so a stalled or hostile upstream cannot buffer unbounded memory (#12577) ([#13282](https://github.com/diegosouzapw/OmniRoute/pull/13282)) +- fix(docker): scope the cliproxyapi/qdrant/bifrost sidecars to loopback by default and forward `CLIPROXYAPI_MANAGEMENT_KEY` into the cliproxyapi container so its management API is not left both unauthenticated and LAN-published (#12578) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): create DB export temp paths with `fs.mkdtempSync` instead of predictable timestamps (#12579) ([#13237](https://github.com/diegosouzapw/OmniRoute/pull/13237)) +- Cline 401 bodies that say "re-authenticate your Cline account" classify as a refreshable OAuth token, not a terminal expired key. The cooling panel no longer labels every cooldown as a 429; it shows the recorded last error instead. (#12594) ([#13060](https://github.com/diegosouzapw/OmniRoute/pull/13060)) — thanks @HouMinXi +- **fix(catalog):** degrade unknown combo targets instead of dropping LCD modalities, and persist OpenRouter `architecture.input_modalities` into the capability snapshot ([#12613](https://github.com/diegosouzapw/OmniRoute/issues/12613)) ([#13042](https://github.com/diegosouzapw/OmniRoute/pull/13042)) — thanks @HouMinXi +- fix(providers): send `x-api-key` instead of `Authorization: Bearer` for OpenCode Zen's `/v1/responses` endpoint (Muse Spark Contributor models), fixing a 401 on OmniRoute's auth header (#12633) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(sse): surface the actionable "Auggie CLI not found" message when the shell reports a missing `auggie` binary via exit code instead of a spawn error (#12645) ([#13263](https://github.com/diegosouzapw/OmniRoute/pull/13263)) +- **fix(config):** Persist deletions of built-in background-degradation entries — when a stored settings record exists its `degradationMap` is now authoritative instead of being merged under the defaults, so an entry the user removed in the dashboard no longer reappears on the next apply or restart ([#12424](https://github.com/diegosouzapw/OmniRoute/issues/12424)) ([#12647](https://github.com/diegosouzapw/OmniRoute/pull/12647)) — thanks @pacocartones +- **fix(gamification):** action-count badge milestones (First Token, Token Consumer, Token Machine, Token Whale, and the token-sharing tier) are now backed by a durable `xp_action_counts` counter incremented in `addXp()`, instead of a live `COUNT(*)` over `xp_audit_log`. The audit log is pruned by `retention.xpAuditLog` (default 30 days), so on a default install those "lifetime" milestones were really "actions in the last 30 days" and unlocked badges could stop unlocking once old rows aged out. `getActionCount()` and `checkActionCountBadges()` now read the same durable source, and a migration backfills existing totals from the surviving audit rows ([#12546](https://github.com/diegosouzapw/OmniRoute/issues/12546)) ([#12651](https://github.com/diegosouzapw/OmniRoute/pull/12651)) — thanks @pacocartones +- **fix(images):** `/v1/images/edits` now iterates a combo's targets the same way `/v1/images/generations` does (#9239) instead of flattening a bare combo to its first target. A combo whose first target is not edit-capable — or lacks credentials — now falls through to a later edit-capable target rather than hard-erroring, and missing credentials are skipped (not a hard `401`) to match the generations path. The per-target skip/terminal classification is extracted into a shared `runImageComboTargets` loop, so generations behavior is unchanged ([#12547](https://github.com/diegosouzapw/OmniRoute/issues/12547)). ([#12653](https://github.com/diegosouzapw/OmniRoute/pull/12653)) — thanks @pacocartones +- fix(sse): add first-byte watchdog to the TLS-fingerprint transport so a stalled wreq body falls back instead of hanging for minutes (#12656) ([#13272](https://github.com/diegosouzapw/OmniRoute/pull/13272)) +- fix(sse): exempt tiny-budget reasoning probes from combo quality failure and surface persisted-cooldown skips in ALL_TARGETS_SKIPPED diagnostics (#12659) ([#13269](https://github.com/diegosouzapw/OmniRoute/pull/13269)) +- fix(models): declare the real ~1M contextLength for OpenCode Zen's Muse Spark 1.2 models instead of falling back to the 200000 provider default (#12681) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(oauth): align codebuddy-cn OAuth User-Agent with the chat/usage CLI version to avoid WAF false positives (#12702) ([#13264](https://github.com/diegosouzapw/OmniRoute/pull/13264)) +- fix(dashboard): surface an authentication-required banner instead of silently blanking database settings for a guest session (#12709) ([#13270](https://github.com/diegosouzapw/OmniRoute/pull/13270)) +- **fix(resilience):** stop unbounded queue that hangs 6min until Aborted — gate, provider slot, and Bottleneck queue now share a per-connection `maxWaitMs` budget; `fail-closed` on exhaust (503); execution backstop `executionMaxWaitMs` overridable per connection with upstream clamp ([#12715](https://github.com/diegosouzapw/OmniRoute/pull/12715)) — thanks @maxmad64bis (with thanks to @Tushar49 for surfacing the slow-provider need in #12635) +- **fix(auto-combo):** every mode pack now carries `quality` and `reliability` weights (`quality` 0.02, 0.03 in `quality-first`; `reliability` 0.03, 0.04 in `reliability-first`), so selecting a pack no longer silences either signal; `DEFAULT_WEIGHTS` is unchanged ([#12731](https://github.com/diegosouzapw/OmniRoute/pull/12731)) — thanks @maxmad64bis +- **fix(sse):** the provider execution pipeline keeps the upstream error `code`/`type` again — both were lost when the non-streaming leg was extracted, so a config-class `422` (Antigravity missing project) degraded into a generic account cooldown instead of reaching the gate that recognises it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(db):** a legacy `call_logs` table no longer aborts startup — the provider-stats index is created after the column healing runs, not before it, so an install predating `request_type` opens instead of failing with `no such column` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(catalog):** a malformed row in the operator-writable custom-models blob no longer kills every `auto/*` pool ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(tests):** realign the guards that the same day's merges left asserting the old behavior — the provider-count total after GreenPT and EURouter, the GLM stream buffer slot that `#12925` turned into a declared parameter, and the injection scan bound that `#13104` reshaped from a head-only window into head-plus-tail ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- fix(cache): fold tool_choice/tools/response_format into the semantic cache signature so a cached tool_calls response can no longer be replayed for a request whose tool policy forbids it (#12734) ([#13267](https://github.com/diegosouzapw/OmniRoute/pull/13267)) +- **fix(models):** stop treating provider-supplied `isFree:true`/`:free`/`0/0` flags as trusted unless the provider has a documented free tier — only the shipped free-tier catalog decides otherwise; fetched `isFree:true` trusted only for a free-tier provider, custom `isFree:true` via a trusted path ([#12744](https://github.com/diegosouzapw/OmniRoute/pull/12744)) — thanks @maxmad64bis +- fix(memory): authenticate the internal /v1/rerank loopback call so memory reranking no longer silently degrades to unranked order when REQUIRE_API_KEY=true (#12745) ([#13268](https://github.com/diegosouzapw/OmniRoute/pull/13268)) +- fix(sse): parse Ollama Cloud's current usage markup (`$X of $Y used` aria-label, nested width style) (#12749) ([#13260](https://github.com/diegosouzapw/OmniRoute/pull/13260)) +- fix(cli): setup-opencode no longer sends an active context's management token to `/v1/models` when `--api-key`/`OMNIROUTE_API_KEY` is supplied — an explicit flag or the env var now always outranks the context's token, and the flag itself is no longer swallowed by the parent program's global `--api-key` option (#12783) ([#13246](https://github.com/diegosouzapw/OmniRoute/pull/13246)) +- fix(sse): register Arcee AI in the executor provider registry so requests reach api.arcee.ai instead of silently falling back to OpenAI (#12784) ([#13277](https://github.com/diegosouzapw/OmniRoute/pull/13277)) +- **fix(quota):** share one upstream quota read when identical saturation checks arrive at the same time, so traffic bursts don't multiply provider API calls ([#12787](https://github.com/diegosouzapw/OmniRoute/pull/12787)) — thanks @maxmad64bis +- **fix(db):** ignore expired or invalid rate-limit cooldown writes so a stale timestamp can't lock a connection that should be usable — clearing still works as before ([#12788](https://github.com/diegosouzapw/OmniRoute/pull/12788)) — thanks @maxmad64bis +- **fix(providers):** single source for provider order with `xao` ranking alongside `xai-oauth` ([#12790](https://github.com/diegosouzapw/OmniRoute/pull/12790)) — thanks @maxmad64bis +- **fix(routing):** off-table models check the free-model catalog before inheriting premium prices, latency bootstraps from the observed pool median, async tiers read live database pricing with a 90-day freshness gate, and the tier cache invalidates on every pricing write ([#12792](https://github.com/diegosouzapw/OmniRoute/pull/12792)) — thanks @maxmad64bis +- **fix(auto-combo):** snapshot scoring uses the observed per-connection account tier and per-model quality instead of neutral constants, and circuit-open providers rank lower at build time ([#12794](https://github.com/diegosouzapw/OmniRoute/pull/12794)) — thanks @maxmad64bis +- **fix(pool):** empty `auto/*` pools now say why they are empty, and the models listing uses the same paid check as routing ([#12795](https://github.com/diegosouzapw/OmniRoute/pull/12795)) — thanks @maxmad64bis +- fix(routing): recognize CLIProxyAPI's 'unknown provider for model' 400 as fallback-worthy (#12800) ([#13284](https://github.com/diegosouzapw/OmniRoute/pull/13284)) +- **fix(sse):** translate-mode streams now emit the estimated token counts as a trailing usage-only chunk before `[DONE]` when the upstream stays silent, so metered chat clients see totals instead of nothing ([#12828](https://github.com/diegosouzapw/OmniRoute/pull/12828)) — thanks @maxmad64bis +- **fix(db):** provider stats stay truthful on empty and legacy databases: fallback counts default to `0` instead of `null`, latency averages read `null` (not `0`) when no durations were recorded, failures logged before the error-type column existed group under `pre_migration` instead of `unclassified`, and per-provider queries use two new composite indexes ([#12832](https://github.com/diegosouzapw/OmniRoute/pull/12832)) — thanks @maxmad64bis +- fix(nvidia): fail open when a synced model catalog goes stale instead of gating forever (#12849) ([#13248](https://github.com/diegosouzapw/OmniRoute/pull/13248)) +- **fix(db):** WAL maintenance lives in its own module: a `TRUNCATE` checkpoint that hits a busy database now warns with its streak and retries once via `PASSIVE` instead of logging success, and closing-time checkpoints no longer report success on builds without a database file, with the busy totals visible in the authenticated monitoring health payload ([#12853](https://github.com/diegosouzapw/OmniRoute/pull/12853)) — thanks @maxmad64bis +- **fix(health):** quota with no snapshots reads empty instead of a contradicting 0% ([#12857](https://github.com/diegosouzapw/OmniRoute/pull/12857)) — thanks @maxmad64bis +- fix(a2a): accept the dashboard's own session cookie on /a2a so "Run message/send" no longer fails with "Unauthorized: missing or invalid API key" (#12888) ([#13271](https://github.com/diegosouzapw/OmniRoute/pull/13271)) +- **fix(logs):** the Logs grid's in-memory filter pass no longer discards rows the SQL query already matched — selecting an API key from the dropdown (which sends the key's id) returns its calls again, the Combo tab shows every combo instead of only those whose name contains a "1", and the model filter and search cover the same columns as the query ([#12896](https://github.com/diegosouzapw/OmniRoute/pull/12896)) — fixes [#12873](https://github.com/diegosouzapw/OmniRoute/issues/12873) — thanks @ntdatt812 +- **fix(a2a):** `/api/a2a/status` now builds the agent card from the request that asked for it, so a gateway reached at a non-localhost host no longer advertises `http://localhost:20128` as its A2A URL ([#12918](https://github.com/diegosouzapw/OmniRoute/pull/12918)). — thanks @ntdatt812 +- **fix(compression):** progressive aging now appends its `[COMPRESSED:aging:…]` annotation after a turn's `tool_result` blocks instead of in front of them, so Anthropic no longer rejects aged conversations with "`tool_use` ids were found without `tool_result` blocks immediately after" ([#12920](https://github.com/diegosouzapw/OmniRoute/pull/12920)). — thanks @ntdatt812 +- **fix(bedrock):** model import now resolves context limits for every vendor prefix instead of only `anthropic.*`, so `global.openai.gpt-5.6-*` no longer imports with a null `inputTokenLimit` and gets rejected pre-flight at the 200k default ([#12921](https://github.com/diegosouzapw/OmniRoute/pull/12921)). — thanks @ntdatt812 +- **fix(stream):** the 64 KB stream buffer GLM asks for is honoured instead of dropped, and the type error it caused no longer fails the API Route Typecheck gate on every open PR ([#12925](https://github.com/diegosouzapw/OmniRoute/pull/12925)) — thanks @ntdatt812 +- **fix(guardrails):** mask PII inside a `tool_result`'s nested content array, which the masker walked past while redacting its sibling block ([#12930](https://github.com/diegosouzapw/OmniRoute/pull/12930)) — thanks @ntdatt812 +- **fix(dashboard):** the Radar catalog table no longer leaves absent data unexplained — empty limits, unknown context windows, and unreported capabilities each explain themselves on hover, and a new check keeps it that way ([#12937](https://github.com/diegosouzapw/OmniRoute/pull/12937)) — thanks @maxmad64bis +- **fix(sse):** geo-blocked opencode requests rotate to the next account proxy instead of failing, so one refused egress no longer aborts the whole chain ([#12941](https://github.com/diegosouzapw/OmniRoute/pull/12941)) — thanks @maxmad64bis +- **fix(codex):** dashboard "clear cooldown" and the CAS recovery path now drop nested `codexScopeRateLimitedUntil` maps in the same write that nulls `rate_limited_until`, and fresh quota snapshots with headroom lift fallback-sourced scope cooldowns parked by quota preflight ([#12817](https://github.com/diegosouzapw/OmniRoute/issues/12817), [#12860](https://github.com/diegosouzapw/OmniRoute/issues/12860), [#12951](https://github.com/diegosouzapw/OmniRoute/pull/12951)) The dashboard reset-credit button (`consumeCodexResetCredit`) now exercises that same snapshot path after a successful redeem, so a filled quota bar is enough to unpark a leftover fallback Codex child without another manual clear. — thanks @HouMinXi +- **fix(db):** resolve `sql-wasm.wasm` across global npm install and hoisted layouts, ensuring OmniRoute can boot cleanly on Node 24 when native `better-sqlite3` is uncompiled. ([#13035](https://github.com/diegosouzapw/OmniRoute/pull/13035)) — thanks @HouMinXi +- **fix(api):** `DELETE /v1/batches/delete-completed` now sweeps only the calling API key's own completed batches (batches with no owner stay out of a key-scoped sweep on purpose), with an explicit instance-wide mode reserved for authenticated dashboard sessions, a 401 for a presented key that is unknown, revoked, deactivated, banned or expired (never falling through to the session branch), audit logging of every sweep, a sanitized 500 on failure, an owner-scoped file half (a key-scoped sweep never nulls a file another tenant owns) and an atomic sweep — chunked in 200-batch transactions in instance mode — so a mid-way error never leaves a batch pointing at a nulled file (GHSA-wvxc-jp3v-5mg5) ([#12969](https://github.com/diegosouzapw/OmniRoute/pull/12969)) +- **fix(combo):** quota-weighted routing stops drawing on an out-of-credit connection — a 402 now invalidates the stored quota snapshot instead of leaving its stale remaining percentage in place, and a snapshot older than 10 minutes no longer counts as confident headroom for the primary pool ([#12972](https://github.com/diegosouzapw/OmniRoute/pull/12972)) ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- **fix(vision):** Custom Models with "Vision capable" checked no longer have image requests swapped to `glm/glm-4.6v` when the client sends the advertised alias (`vllm/path/...`) or the bare path-shaped id — Vision Bridge now matches the stored override for all three id forms ([#12758](https://github.com/diegosouzapw/OmniRoute/issues/12758)) ([#12974](https://github.com/diegosouzapw/OmniRoute/pull/12974)) — thanks @HouMinXi +- **fix(sse):** transient opencode upstream failures rotate to the next account proxy instead of failing, so one flapping egress no longer aborts the whole chain ([#12975](https://github.com/diegosouzapw/OmniRoute/pull/12975)) — thanks @maxmad64bis +- **fix(azure):** Deployments from GPT-6 onward now send `max_completion_tokens` instead of `max_tokens`, which Azure rejects with HTTP 400. The rule matched a literal `gpt-5`, so each new generation arrived broken; it now matches the generation range, while `gpt-35-turbo` still keeps `max_tokens`. ([#13007](https://github.com/diegosouzapw/OmniRoute/pull/13007)) — thanks @ntdatt812 +- **fix(db):** add an opt-in self-restart circuit for sustained critical memory pressure, gate post-cleanup VACUUM behind a minimum freed-rows threshold, and checkpoint the SQLite WAL every 5 minutes with a size guard that escalates to TRUNCATE, so a growing WAL can no longer stall the event loop into a full outage. ([#13011](https://github.com/diegosouzapw/OmniRoute/pull/13011)) — thanks @HouMinXi +- **fix(auth):** an explicit connection pin may probe a quota-disabled row once and re-enable it on success ([#12874](https://github.com/diegosouzapw/OmniRoute/issues/12874)) ([#13017](https://github.com/diegosouzapw/OmniRoute/pull/13017)) — thanks @HouMinXi +- **fix(api):** thread `X-OmniRoute-Fallback-Attempts` through combo chat completions so streaming and non-streaming responses report how many prior legs were attempted ([#13038](https://github.com/diegosouzapw/OmniRoute/pull/13038)) — thanks @HouMinXi +- Restore provider failure classification and credential refresh on non-streaming requests: classify non-2xx failures to lock models on per-model quota exhaustion, update connection rate limits from headers and body, and pass credential refresh handlers to pipeline execution so 401 tokens can be refreshed and retried (#13043). ([#13069](https://github.com/diegosouzapw/OmniRoute/pull/13069)) — thanks @HouMinXi +- **fix(responses):** wrap forced-non-streaming web_search fallback JSON as Responses SSE so Codex still sees `response.completed` ([#13050](https://github.com/diegosouzapw/OmniRoute/pull/13050)) — thanks @HouMinXi +- **fix(antigravity):** Preserve upstream thought-token usage in normalized completion and reasoning token counts ([#13055](https://github.com/diegosouzapw/OmniRoute/pull/13055)) — thanks @pacocartones +- Fix a concurrency-slot leak in the DeepSeek PoW solver: a worker that failed to spawn (for example a missing worker script) never released its slot, so `MAX_CONCURRENT_WORKERS` failures disabled the solver until restart. ([#13097](https://github.com/diegosouzapw/OmniRoute/pull/13097)) — thanks @anhtahaylove +- **fix(acp):** bound the ACP session output buffers — `stdoutBuffer` and `stderrBuffer` now cap at 1 MiB keeping the most recent output behind a visible `[...output truncated...]` marker, and `stderrBuffer` is reset per prompt instead of accumulating for the lifetime of the session. ([#13100](https://github.com/diegosouzapw/OmniRoute/pull/13100)) — thanks @anhtahaylove +- **fix(acp):** release the `stdout`/`exit` listeners and the idle timer that a `sendPrompt` timeout used to leave attached to the `acpManager` singleton, and drop sessions that exited on their own from the session map instead of keeping them forever. ([#13096](https://github.com/diegosouzapw/OmniRoute/pull/13096)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection and PII scanners now read the text a `tool_result` block carries on `content` (string or nested block list), in messages and in system blocks, so tool output is judged by the same rules as user text ([#13101](https://github.com/diegosouzapw/OmniRoute/pull/13101)) — thanks @ntdatt812 +- **fix(gamification):** close the badge notification SSE stream when the request signal is already aborted before the stream starts — a client that disconnects while the route is still awaiting auth used to leave both the 2s unlock poll and the 15s heartbeat running for the lifetime of the process. ([#13106](https://github.com/diegosouzapw/OmniRoute/pull/13106)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection scan now spends its 16 KB budget on both ends of the request instead of the first 16 KB only, so `system`, `instructions`, `query`, `documents` and the newest turns are no longer hidden behind one long message ([#13104](https://github.com/diegosouzapw/OmniRoute/pull/13104)) — thanks @ntdatt812 +- **fix(dashboard):** expose the Volcano Ark console cookie on quota scraping and unwrap connect-error objects so the dashboard shows the upstream message ([#13107](https://github.com/diegosouzapw/OmniRoute/pull/13107)) — thanks @HouMinXi +- **fix(db):** release the `beforeExit`/`SIGINT`/`SIGTERM` handlers when a `node:sqlite` adapter closes, so a closed adapter and its database handle are no longer pinned to `process` for the lifetime of the run — the same treatment #7494 gave the sql.js adapter. ([#13109](https://github.com/diegosouzapw/OmniRoute/pull/13109)) — thanks @anhtahaylove +- **fix(translator):** `contentSchema` and `unevaluatedItems` are now treated as subschema positions by the tool-schema sanitizer, so a truncation placeholder in either is replaced with a permissive schema instead of being forwarded as a string ([#13110](https://github.com/diegosouzapw/OmniRoute/pull/13110)) — thanks @ntdatt812 +- **fix(cli-helper):** clear the `createLogStream` timeout on the abort path — `stop()` aborts the in-flight fetch and returned through the `signal.aborted` branch, which skipped `clearTimeout` and left an armed timer per stopped stream. The stream reader is now also cancelled when the read loop exits early. ([#13114](https://github.com/diegosouzapw/OmniRoute/pull/13114)) — thanks @anhtahaylove +- **fix(docs):** drop TLS-impersonation, MITM-decrypt, supply-chain attestation, and XOR-mask writeups from the public `/docs` catalog and Docker image. Files stay in git for engineers; operators who need them open the repo, not the website. ([#13136](https://github.com/diegosouzapw/OmniRoute/pull/13136)) — thanks @HouMinXi +- **fix(combo):** parse numeric-epoch `rate_limited_until` in the combo cooldown read path ([#13141](https://github.com/diegosouzapw/OmniRoute/pull/13141)) — thanks @maxmad64bis +- **fix(opencode):** both OpenCode plugins now reject a gateway address typed without `http://` at configuration time, instead of publishing every model with an api url no client can call, and the v2 plugin no longer publishes a model card whose api url is blank or relative ([#13142](https://github.com/diegosouzapw/OmniRoute/pull/13142)) — thanks @maxmad64bis +- **fix(providers):** lock opencode model on upstream 400 model-unavailable ([#13146](https://github.com/diegosouzapw/OmniRoute/pull/13146)) — thanks @maxmad64bis +- **fix(logging):** keep the provider exchange rather than the raw client bodies when a call log exceeds its size budget, and show that recovered payload in the request-detail panel instead of replacing it with the stored response body ([#13147](https://github.com/diegosouzapw/OmniRoute/pull/13147)) — thanks @maxmad64bis +- **fix(traffic-inspector):** the WebSocket route no longer leaks a traffic-buffer subscriber and a 30s ping timer when the client socket is already closed at handler time — listeners are attached before any resource is acquired, a destroyed socket bails out early, and the ping interval stops on a dead socket where `write()` never throws ([#13155](https://github.com/diegosouzapw/OmniRoute/pull/13155)) — thanks @anhtahaylove +- **fix(telegram):** bound the per-user API key cache in the Telegram chat proxy so a burst of distinct chat ids can no longer grow the process heap without limit ([#13165](https://github.com/diegosouzapw/OmniRoute/issues/13165)) ([#13166](https://github.com/diegosouzapw/OmniRoute/pull/13166)) — thanks @anhtahaylove +- **fix(stream):** cancel the upstream response body when the JSON-to-SSE sniff unwinds on a body timeout, so a stalled upstream no longer pins the connection ([#13169](https://github.com/diegosouzapw/OmniRoute/issues/13169)) ([#13171](https://github.com/diegosouzapw/OmniRoute/pull/13171)) — thanks @anhtahaylove +- **fix(telegram):** authenticate webhook deliveries with Telegram's `secret_token` so an unauthenticated caller can no longer mint API keys or spend upstream quota ([#13172](https://github.com/diegosouzapw/OmniRoute/issues/13172)) ([#13175](https://github.com/diegosouzapw/OmniRoute/pull/13175)) — thanks @anhtahaylove +- **fix(models):** give discoverable Gemini 3.8 Flash ids their own 65536 output spec so Antigravity no longer clamps them to 16384 ([#13195](https://github.com/diegosouzapw/OmniRoute/pull/13195)) — thanks @HouMinXi +- **fix(dashboard):** leftover catalog-removed provider rows (gemini-cli) can be listed and purged from the providers page ([#13067](https://github.com/diegosouzapw/OmniRoute/issues/13067)) ([#13197](https://github.com/diegosouzapw/OmniRoute/pull/13197)) — thanks @HouMinXi +- **fix(auth):** a dashboard session now requires the `authenticated: true` claim that login, OIDC and the session refresh already emit — a JWT merely signed with `JWT_SECRET` (for example the Cursor CLI passthrough token, which any API-key holder can obtain) no longer verifies as the `auth_token` cookie on any route, the WebSocket handshake or the live server; existing sessions keep working ([#13298](https://github.com/diegosouzapw/OmniRoute/issues/13298)) ([#13375](https://github.com/diegosouzapw/OmniRoute/pull/13375)) +- **fix(api):** `DELETE /v1/batches/delete-completed` now applies the caller's API-key policy (endpoint allowlist, schedule, usage cap, rate limit) like every other `/v1` route, commits the key-scoped sweep per 200-batch chunk instead of holding one write lock for the whole sweep, refuses to loop without progress, and rejects a scope that names both a key and `allTenants` ([#13684](https://github.com/diegosouzapw/OmniRoute/pull/13684)) +- Restore the API-route typecheck gate: the non-streaming leg result lost its discriminated-union narrowing after the server-owned tool loop reassignment, producing 13 new TS2339 diagnostics in `chatCore.ts`. ([#12963](https://github.com/diegosouzapw/OmniRoute/pull/12963)) +- Restore four non-streaming behaviours the server-owned tool loop refactor dropped: client aborts map to 499 with the fixed `Request aborted` message, an aborted request no longer logs a synthetic `clientResponse`, Claude prompt-cache telemetry is recorded again, and a body that cannot be canonicalized no longer throws when the tool loop is off. ([#12990](https://github.com/diegosouzapw/OmniRoute/pull/12990)) +- **fix(skills):** The CLI registry parser now reads positionals declared with `.addArgument()`, not only those written inline in `.command()`. `tunnel create [type]` was being published as `tunnel create`, so the agent-skills sync gate reported drift on every branch and regenerating would have deleted the argument. ([#13009](https://github.com/diegosouzapw/OmniRoute/pull/13009)) — thanks @ntdatt812 +- **fix(combos):** dashboard combo test uses a short prompt, serial probes, and a 60s timeout so reasoning models and rate-limited free pools do not fail the health check ([#13001](https://github.com/diegosouzapw/OmniRoute/pull/13001)) — thanks @HouMinXi +- fix(compression): terminate idle worker threads on eviction so long-running instances stop leaking OS threads and MessagePorts ([#13091](https://github.com/diegosouzapw/OmniRoute/pull/13091)) — thanks @anhtahaylove +- Redact Google API keys of any length in error bodies: the pattern required exactly 39 characters, so shorter or longer `AIza…` credentials passed through unredacted. ([#12964](https://github.com/diegosouzapw/OmniRoute/pull/12964)) +- **fix(grok-cli):** a 402 "Grok Build usage balance exhausted" parks that Grok login as out of credit (Grok Build CLI, grok.com cookie, and xAI OAuth share the weekly pool). Combo routing then tries the next login instead of locking the model for every account in the pool ([#13061](https://github.com/diegosouzapw/OmniRoute/pull/13061)) — thanks @HouMinXi +- fix(compression): spawn the LLMLingua worker with a file URL object so compression actually runs instead of silently failing open on Node ([#13093](https://github.com/diegosouzapw/OmniRoute/pull/13093)) — thanks @anhtahaylove +- **fix(test):** run the local `test` and `test:unit` scripts at concurrency 4 so a full-suite run no longer exhausts the machine's commit charge and kills unrelated processes ([#13187](https://github.com/diegosouzapw/OmniRoute/pull/13187)) — thanks @anhtahaylove +- **fix(monitoring):** `GET /api/monitoring/health` `credentialHealth` now includes a bounded `failedConnections` list (`connectionId`, `status`, sanitized `lastError`) when the probe-cache gauge `failed>0`, plus `source: probe-cache` and a cheap `staleDbNonOkCount` for sticky SQLite `test_status` on active rows. Documents that the live gauge is not `provider_connections.test_status`. ([#12876](https://github.com/diegosouzapw/OmniRoute/pull/12876)) — thanks @RaviTharuma +- **fix(resilience):** an openai-compatible multi-upstream gateway no longer marks the whole connection `credits_exhausted` when a single upstream (or the `/models` / representative-model health probe) returns 402. Quota failures stay model-scoped; true connection-wide auth failures are unchanged. Single-credential 402 key disable ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239)) is preserved. ([#12875](https://github.com/diegosouzapw/OmniRoute/pull/12875)) — thanks @RaviTharuma +- **fix(dashboard):** nine fixes on the Orchestration Canvas, all diagnosed in the Phase 2 reviews. A source that fails now keeps the timestamp of its FIRST failure instead of being re-stamped every poll — the stale line said "since the last poll" no matter how long the source had been down, and the churn also defeated the snapshot's stable identity (it serializes the source list), so the canvas re-rendered on every tick while anything was broken. A source that HAD data and only then started failing is flagged too: previously only a source with no node at all got the warning, so a source that went down mid-session kept a healthy-looking node forever. The rest are pointwise: clicking a filter chip cancels the pending search debounce (left armed, it fired ~300ms later and silently reverted the chip); the search input carries an accessible name; `?state=running, failed` parses like the unpadded form instead of dropping the padded value; the CSV toggle helper is defined once in `model/urlParams.ts` rather than twice; the agents tab tells "nothing running" apart from "the filter matched nothing", offering a clear-filters button instead of setup links that would be wrong advice there; edges stop emitting SMIL particles above 40 simultaneously active edges, keeping the colored stroke; and the drawer's error banner clears when a retried action succeeds. ([#12983](https://github.com/diegosouzapw/OmniRoute/pull/12983)) +- **fix(i18n):** the nine locales added with the EU-language batch (Greek, Estonian, Irish, Croatian, Lithuanian, Latvian, Maltese, Slovenian, Serbian) were missing the eleven Orchestration Canvas keys that Phase 3 introduced, so the compare-runs panel and the "no runs match these filters" empty state fell back to English in those languages (`deepMergeFallback` substitutes English for an absent key, so nothing rendered blank — it rendered untranslated). The coverage gate does not catch this: it enforces an 80% floor per locale, and eleven missing keys out of ~13,000 leaves coverage at 99.9%. Translated for real in each language, calibrated against the wording each file already uses for "run", "filter" and "skill". ([#13294](https://github.com/diegosouzapw/OmniRoute/pull/13294)) +- **fix(dashboard):** five follow-ups from the Phase 2 Orchestration Canvas review. The History drawer now shows the memory section for runs that already left the live TTL window: the persisted `memory_hits` event is parsed into `metadata.memoryHits` with the same defensive validation the drawer applies, and — because that event is observability rather than a state transition — it no longer leaks into the timeline, where it had been inheriting the task's state and rendering as a duplicate transition. The A2A memory recall runs against its own 1.5s deadline instead of inheriting the memory backend's 30s one; overshooting degrades exactly like any other recall failure (no hits, task proceeds), and the timer is cleared on both paths. Repeating a Conductor run carries its `requirements.cli`/`requirements.model` forward, so the new run is pinned to the same runner profile and model rather than drifting to whatever the fleet picks. A successful repeat from the Agents tab now focuses the run it created, instead of leaving the operator on the finished one. And the auth test for `POST /api/conductor/tasks` moved into the shared `ROUTES` array rather than restating the pattern. ([#12988](https://github.com/diegosouzapw/OmniRoute/pull/12988)) +- fix(plugins): stop leaking an exit listener per plugin hook timeout, which triggered MaxListenersExceededWarning on plugins that ignore SIGTERM ([#13092](https://github.com/diegosouzapw/OmniRoute/pull/13092)) — thanks @anhtahaylove +- **fix(validation):** Provider node edits no longer fail with a generic "Invalid request" when the optional daily-quota reset fields are left blank. The dashboard sends `dailyQuotaResetTimezone` and `dailyQuotaResetHour` as `null`, and only the hour accepted it. ([#13066](https://github.com/diegosouzapw/OmniRoute/issues/13066)) ([#13083](https://github.com/diegosouzapw/OmniRoute/pull/13083)) — thanks @ntdatt812 +- **fix(combo):** quota-aware expansion drops banned, inactive, missing, and wrong-provider connections before quota fetch or model dispatch; pins and allowlists stay selectors, not a bypass. Antigravity automatic exhaustion now requires a reported zero remaining, so a positive balance below 1% stays eligible. ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- Fix an intermittent failure in the quota-weighted routing test suite: scores are a function of `Date.now()`, so peers with identical quota scored microseconds apart never tied and swapped order. ([#12946](https://github.com/diegosouzapw/OmniRoute/pull/12946)) +- fix(volcengine): resolve quota window mapping gaps, safe multi-connection console binding, and response cookie sanitization ([#12950](https://github.com/diegosouzapw/OmniRoute/pull/12950)) — thanks @HouMinXi +- **fix(test):** resolve the WebDAV handler path with `fileURLToPath` so the suite's 37 WebDAV tests run on Windows instead of failing with a doubled `C:\C:\` drive prefix ([#13196](https://github.com/diegosouzapw/OmniRoute/pull/13196)) — thanks @anhtahaylove +- **fix(ui):** fall back from invalid AnySearch icon alias (direct commit `9587e07b69`) +- **fix(sse):** stop rebuilding a truncated summary from the collector's cap-dropped event array ([#12718](https://github.com/diegosouzapw/OmniRoute/pull/12718)) — thanks @hartmark +- **fix(conversations):** show a pending spinner for unresolved tool nodes instead of "(empty)" ([#12727](https://github.com/diegosouzapw/OmniRoute/pull/12727)) — thanks @hartmark +- **fix(streaming):** fail fast when an upstream stream produces only lifecycle/heartbeat events, never real content ([#12741](https://github.com/diegosouzapw/OmniRoute/pull/12741)) — thanks @hartmark +- **fix(responses-continuation):** bridge the write-in-flight window with an in-memory pending store ([#12854](https://github.com/diegosouzapw/OmniRoute/pull/12854)) — thanks @hartmark +- **fix(translator):** drop or map agent_message on Chat Completions fallback ([#12880](https://github.com/diegosouzapw/OmniRoute/pull/12880)) — thanks @RaviTharuma +- **fix(api):** page and stream-complete GET /v1/models for large catalogs ([#12882](https://github.com/diegosouzapw/OmniRoute/pull/12882)) — thanks @RaviTharuma +- **fix(quota):** align AUTH window labels with usage API and clarify 503 ([#12884](https://github.com/diegosouzapw/OmniRoute/pull/12884)) — thanks @RaviTharuma +- **fix(ci):** clear two base-reds on release/v3.8.51 — mutation-coverage gate + image-only-model guard ([#12945](https://github.com/diegosouzapw/OmniRoute/pull/12945)) +- **fix(api):** scope batch bulk-delete to the calling API key ([#13211](https://github.com/diegosouzapw/OmniRoute/pull/13211)) +- **fix(tests):** align model-sync log assertion to shipped path redaction (base-red) ([#13227](https://github.com/diegosouzapw/OmniRoute/pull/13227)) +- **fix(test):** make the ToS heading guard actually require the parentheses ([#13228](https://github.com/diegosouzapw/OmniRoute/pull/13228)) +- **fix(quality):** register 4 drifted covering tests in stryker tap.testFiles ([#13229](https://github.com/diegosouzapw/OmniRoute/pull/13229)) +- **fix(logging):** mask *-api-key headers in request-log pipeline (#13273) ([#13401](https://github.com/diegosouzapw/OmniRoute/pull/13401)) — thanks @KooshaPari +- **fix(tests):** correct stale Jina catalog prefix in custom-models test ([#13403](https://github.com/diegosouzapw/OmniRoute/pull/13403)) — thanks @KooshaPari +- **fix(backend):** prune old backups after health-check-repair snapshots (#13308) ([#13404](https://github.com/diegosouzapw/OmniRoute/pull/13404)) — thanks @KooshaPari +- **fix(backend):** guard memory_fts_au trigger with WHEN clause to stop FTS bloat (#13326) ([#13405](https://github.com/diegosouzapw/OmniRoute/pull/13405)) — thanks @KooshaPari +- **fix(free-tiers):** remove obsolete Together $25 signup credit (#12526) ([#13407](https://github.com/diegosouzapw/OmniRoute/pull/13407)) — thanks @KooshaPari +- **fix:** skip non-chat generation models in Test all models ([#13409](https://github.com/diegosouzapw/OmniRoute/pull/13409)) — thanks @KooshaPari +- **fix(providers):** de-duplicate agy and antigravity model catalogs ([#13410](https://github.com/diegosouzapw/OmniRoute/pull/13410)) — thanks @KooshaPari +- **fix(dashboard):** send provider in body for mini-playground web search ([#13411](https://github.com/diegosouzapw/OmniRoute/pull/13411)) — thanks @KooshaPari +- **fix(resilience):** add CJK quota-exhaustion patterns to 429 classifier ([#13412](https://github.com/diegosouzapw/OmniRoute/pull/13412)) — thanks @KooshaPari +- **fix(backend):** eval runner: failed calls no longer score as passed + dotAll regex ([#13413](https://github.com/diegosouzapw/OmniRoute/pull/13413)) — thanks @KooshaPari +- **fix(backend):** eval runner opts out of output-style and memory injection ([#13414](https://github.com/diegosouzapw/OmniRoute/pull/13414)) — thanks @KooshaPari +- **fix:** chain serial quarantine step in npm test script (fixes #13286) ([#13418](https://github.com/diegosouzapw/OmniRoute/pull/13418)) — thanks @KooshaPari +- **fix(antigravity):** derive stable session ID from credentials for prompt-cache reuse (#13305) ([#13424](https://github.com/diegosouzapw/OmniRoute/pull/13424)) — thanks @KooshaPari +- **fix(memory):** merge memory into top-level system field instead of unshifting at messages[0] (#13425) ([#13427](https://github.com/diegosouzapw/OmniRoute/pull/13427)) — thanks @KooshaPari +- **fix(compression):** preserve instruction blocks from lossy engines ([#13523](https://github.com/diegosouzapw/OmniRoute/pull/13523)) — thanks @KooshaPari +- **fix(compression):** preserve polarity words and newlines in ultra heuristic ([#13524](https://github.com/diegosouzapw/OmniRoute/pull/13524)) — thanks @KooshaPari +- **fix(compression):** run user preservePatterns before built-in patterns (#13457) ([#13525](https://github.com/diegosouzapw/OmniRoute/pull/13525)) — thanks @KooshaPari +- **fix(reasoning):** expand deepseek-v4 regex to match v4.1+ and add command-code provider (#13430) ([#13528](https://github.com/diegosouzapw/OmniRoute/pull/13528)) — thanks @KooshaPari +- **fix(providers):** add alwaysReasons metadata for reka-flash-3 ([#13531](https://github.com/diegosouzapw/OmniRoute/pull/13531)) — thanks @KooshaPari +- **fix(playground):** allow Compare columns to scroll independently ([#13532](https://github.com/diegosouzapw/OmniRoute/pull/13532)) — thanks @KooshaPari +- **fix(security):** empty whitelist allows through to prevent admin lockout ([#13534](https://github.com/diegosouzapw/OmniRoute/pull/13534)) — thanks @KooshaPari +- **fix(tests):** add DISABLE_IOREG_STRATEGY env var for macOS test compatibility (fixes #13467) ([#13539](https://github.com/diegosouzapw/OmniRoute/pull/13539)) — thanks @KooshaPari +- **fix(zed-hosted):** map developer role and Gemini enums for Zed proxy ([#13543](https://github.com/diegosouzapw/OmniRoute/pull/13543)) — thanks @KooshaPari +- **fix(analytics):** normalize provider aliases to canonical id in usage_history ([#13545](https://github.com/diegosouzapw/OmniRoute/pull/13545)) — thanks @KooshaPari +- **fix(combo):** use traceId for call log id to prevent UNIQUE constraint failure ([#13546](https://github.com/diegosouzapw/OmniRoute/pull/13546)) — thanks @KooshaPari +- **fix(auth):** check model lockout before returning synthetic noauth connection ([#13547](https://github.com/diegosouzapw/OmniRoute/pull/13547)) — thanks @KooshaPari +- **fix(guardrails):** skip credential redaction for base64 image data URLs ([#13550](https://github.com/diegosouzapw/OmniRoute/pull/13550)) — thanks @KooshaPari +- **fix(resilience):** increase requestQueue.maxWaitMs default from 15s to 30s ([#13553](https://github.com/diegosouzapw/OmniRoute/pull/13553)) — thanks @KooshaPari +- **fix(docs):** document OMNIROUTE_READY_TIMEOUT_MS and allowlist the test-only DISABLE_IOREG_STRATEGY ([#13692](https://github.com/diegosouzapw/OmniRoute/pull/13692)) + ### 📝 Maintenance ---- +- **docs(readme):** reconcile live v3.8.50 provider, free-tier, CLI, routing, test, community, sponsor, acknowledgment, and SVG metrics with their audited source denominators, including a deduplicated OmniRoute-in-Action snapshot and distinct contributor rankings for merged pull requests, GitHub-attributed commits, and Git history ([#11356](https://github.com/diegosouzapw/OmniRoute/pull/11356)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **docs:** sync the canonical provider count 354 → 356 across `README.md`, `AGENTS.md`, `llm.txt` (+ 42 i18n mirrors), the four README SVG diagrams, `docs/reference/PROVIDER_REFERENCE.md` (regenerated) and the `package.json` description after Opper (#11629) and 1min.ai (#11631) boarded the catalog — closes the `check:docs-counts-sync` strict drifts that kept `release/v3.8.51` red ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11673](https://github.com/diegosouzapw/OmniRoute/pull/11673)) — thanks @jonlwheat2-gif +- **test(providers):** update count-derived assertions after the v3.8.51 provider additions — `APIKEY_PROVIDERS` 233 → 235 (Opper #11629 + 1min.ai #11631), reserved-prefix REGISTRY walk 395 → 398, `WEB_FETCH_PROVIDERS` now includes `nimble-search` (#11620), and the provider translate-path golden snapshot regenerated ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11674](https://github.com/diegosouzapw/OmniRoute/pull/11674)) — thanks @jonlwheat2-gif +- **test(sse):** bump the hard-lease connection-query inventory for `src/lib/tokenHealthCheck.ts` to 2 — the verify-only web-cookie sweep added by #11495 split the single `getProviderConnections` call into oauth + cookie variants, which the frozen inventory had not tracked ([#11495](https://github.com/diegosouzapw/OmniRoute/pull/11495)). ([#11677](https://github.com/diegosouzapw/OmniRoute/pull/11677)) — thanks @jonlwheat2-gif / @oyi77 +- **chore(release):** clear the `release/v3.8.51` base-red gates — docs migration count (+38 i18n mirrors), stryker `tap.testFiles`, lockfile registry host, stale ESLint suppressions, the 7 lint regressions they were hiding, the stale `APIKEY_PROVIDERS` partition count (231 → 233), the `glm-5.3-max` tier missing from the GLM test table, a dead `modelType === "chat"` comparison in the models catalog, and the `resolveLiveWsUrl` / `sanitizeLiveWsPort` helpers from #11388 that a later merge dropped from `wsPath.ts` (dashboard live WebSocket URL resolution) ([#11502](https://github.com/diegosouzapw/OmniRoute/pull/11502)) — thanks @MumuTW +- **chore(free-models):** the free-tier regime table now declares which totals figure each regime feeds and whether it may take the no-auth shortcut, so the sets that used to repeat those answers by hand are derived from it — a new `freeType` no longer compiles until it has answered every question, instead of silently contributing to no total ([#11537](https://github.com/diegosouzapw/OmniRoute/pull/11537)) — thanks @maxmad64bis +- **docs(routing):** LKGP strategy copy now states provider-level stickiness and the explicit rules fallback instead of promising per-target pinning ([#11555](https://github.com/diegosouzapw/OmniRoute/pull/11555)) — thanks @maxmad64bis +- **test(chatcore):** pin the response tool-name alias resolution against the ordering hazard that broke every Gemini/Antigravity MCP tool call in v3.8.49. `extractRequestToolIdentityMap` deletes `translatedBody._toolNameMap`, so the later read is always undefined and the ledger survives only through the `requestToolIdentityMap` fallback — removing that fallback previously left the entire tool-name suite green. The resolution moves into `resolveResponseToolNameMap()` next to the map it depends on, with a regression guard that fails without the recovery. No behaviour change. ([#11575](https://github.com/diegosouzapw/OmniRoute/pull/11575)) — thanks @amartinawi +- **docs(env):** document `OMNIROUTE_CORPUS_CACHE_SIZE` in `.env.example` and `ENVIRONMENT.md`, so the env/docs contract check stops failing Docs Gates on every branch ([#11576](https://github.com/diegosouzapw/OmniRoute/pull/11576)) — thanks @ntdatt812 +- **chore(stryker):** register `route-guard-tunnel-processes-local-only` and `universal-quota-aware-routing` in `tap.testFiles`, so their mutant kills count for the three modules they cover ([#11577](https://github.com/diegosouzapw/OmniRoute/pull/11577)) — thanks @ntdatt812 +- **test(authz):** pin `LOCAL_ONLY_API_GET_EXEMPTIONS` by exact membership instead of by entry count, so the guard names the offending path and also catches a substitution ([#11580](https://github.com/diegosouzapw/OmniRoute/pull/11580)) — thanks @ntdatt812 +- **test(build):** pin `resolveNextBuildBundlerFlag` to the `OMNIROUTE_USE_TURBOPACK` contract it implements, across runtimes, instead of asserting an unimplemented Bun override ([#11583](https://github.com/diegosouzapw/OmniRoute/pull/11583)) — thanks @ntdatt812 +- **test(claude-web):** the Claude Web transport test's barrel anchors no longer pin the exact construction spelling. #11421 made the executor map lazy, so `/"claude-web": new ClaudeWebExecutor\(\)/` stopped matching and the test went red on `release/v3.8.51` itself — a base-red gate every open PR inherited. The anchors now match the alias key and the class name and tolerate either spelling in between, and were checked to still fail when an alias is genuinely repointed at another executor ([#11585](https://github.com/diegosouzapw/OmniRoute/pull/11585)) — thanks @ntdat812 +- **test(models):** pin `qwen3.8-max` resolution per provider — two of the four catalogs now serve the bare id and no longer carry `-preview` ([#11590](https://github.com/diegosouzapw/OmniRoute/pull/11590)) — thanks @ntdatt812 +- **test(executors):** drop the stale 128-tool truncation assertion that contradicted the #11444 pass-through contract ([#11591](https://github.com/diegosouzapw/OmniRoute/pull/11591)) — thanks @ntdatt812 +- **test(token-health):** pin the current terminal-skip boundary — expired with retry budget is probed, `account_deactivated` stays skipped ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)) — thanks @ntdatt812 +- **test(models):** follow #11503's retarget of the `gemini-3-pro-high` alias to the hyphenated catalog id ([#11593](https://github.com/diegosouzapw/OmniRoute/pull/11593)) — thanks @ntdatt812 +- **refactor(video bridge):** extract per-video acquisition, whole-result caching, description, and metrics/abort/cleanup out of `VideoBridgeGuardrail.preCall` into a `processVideoPart` seam in a new `videoBridgePipeline.ts`, behind explicit `VideoMediaBrokerPort`, `VideoAudioTranscriptionPort`, and `VideoDrilldownPort` boundaries; `preCall` now only handles request traversal, policy, and response aggregation. The Video tab's FFmpeg/ffprobe runtime status is now an explicit `unknown` / `restricted` / `unavailable` / `available` state instead of a nullable boolean pair, fixing a case where an in-flight or failed probe was mislabeled as "install FFmpeg" ([#11657](https://github.com/diegosouzapw/OmniRoute/issues/11657)). ([#12016](https://github.com/diegosouzapw/OmniRoute/pull/12016)) +- **test(opencode-plugin):** add unit test coverage for `formatFreeBudget()` naming helper ([#11660](https://github.com/diegosouzapw/OmniRoute/pull/11660)) — thanks @f9td56dbgh-hub +- **docs(video):** clarify that the Video Bridge transcript `source` field (`client`, `embedded`, `audio-bridge`) is presently caller-declared and not yet server-verified — OmniRoute enforces the enum shape but does not cryptographically confirm that an `embedded`/`audio-bridge` label came from a server-owned extraction ([#11661](https://github.com/diegosouzapw/OmniRoute/issues/11661)). ([#12001](https://github.com/diegosouzapw/OmniRoute/pull/12001)) +- **chore(providers):** retire the distributed Raycast Relay and Hailuo Web integrations after a provenance audit confirmed substantial derivation from GPL-3.0 sources; the official GitHub and MiniMax API providers remain available. Migration 163 disables legacy connections and invalidates their active leases while preserving connection IDs, audit/history rows, quota snapshots, usage records, and fail-closed API-key connection policies. ([#11691](https://github.com/diegosouzapw/OmniRoute/pull/11691)) +- **chore(providers):** retire the Felo Web provider while its GPL-derived provenance remains on licensing HOLD ([#11698](https://github.com/diegosouzapw/OmniRoute/pull/11698)) — stale runtime and database references now fail closed while existing connection, lease, and usage history remains preserved for auditability. +- **fix(sse):** retire the Gemini Web image-generation bridge while provenance and licensing remain on HOLD, preserving Gemini Web chat/auth/tools and legitimate Gemini/Nano Banana image providers ([#11708](https://github.com/diegosouzapw/OmniRoute/pull/11708)) +- **chore(audio):** retire the built-in EdgeTTS provider and its 11-voice catalog while provenance/licensing review remains on HOLD; the generic `/v1/audio/speech` endpoint and the other speech providers remain available ([#11711](https://github.com/diegosouzapw/OmniRoute/pull/11711)) +- **fix(providers):** retire the unofficial Qwen Web integration and its `qwen-web`/`qw` identities while third-party provenance remains on HOLD ([#11713](https://github.com/diegosouzapw/OmniRoute/pull/11713)) — direct requests now fail closed with a sanitized HTTP 410 and combos skip the retired target, while migration guards preserve connection IDs, allowlists, usage/audit history and invalidate active leases. Supported Qwen API, Qwen Code, cloud, token-plan, audio, local and model-name surfaces remain available; restoring Qwen Web requires a qualified provenance/license decision. +- **fix(providers):** retire `microsoft-designer-web` and its `msdesigner` alias from runtime and catalogs while preserving stored credentials, provider history and allowlists as inactive records — source dependency on gpt4free's [`MicrosoftDesigner.py`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/g4f/Provider/needs_auth/MicrosoftDesigner.py) is proven at snapshot `0943d16eef757a44deb60651c07bbe5fa958cafe`, whose [`LICENSE`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/LICENSE) is GPL-3.0; expressive adaptation is probable, which records provenance/licensing risk without asserting a legal conclusion ([#11720](https://github.com/diegosouzapw/OmniRoute/pull/11720)). +- **docs(legal):** centralize pinned MIT notices for the vendored GCF codec and local flag assets ([#11726](https://github.com/diegosouzapw/OmniRoute/pull/11726)) +- Split the 2,952-line `adobeFireflyClient.ts` into nine focused modules (catalog, credentials, payload, ARP session, upload, responses, IMS, account, poll) behind an unchanged barrel — no consumer edits and no public API change (#11728). — thanks @MumuTW +- **fix(dashboard):** replaced the Model Lockout card's unprovenanced notification MP3 with a short, locally synthesized Web Audio chime ([#11731](https://github.com/diegosouzapw/OmniRoute/pull/11731)) — both toggles retain optional audible feedback without shipping replacement media or third-party code, and unsupported, suspended, or rejected audio contexts fail open without blocking the setting change. +- **chore(deepseek-web):** remove the provenance-unresolved DeepSeek PoW WASM binary and its runtime loader/tracing while retaining the existing JavaScript solver (slower at high difficulty) ([#11732](https://github.com/diegosouzapw/OmniRoute/pull/11732)). +- **chore(assets):** remove four unused provider assets whose redistribution provenance could not be verified while retaining the active Freebuff SVG ([#11735](https://github.com/diegosouzapw/OmniRoute/pull/11735)) +- **docs(assets):** retired two raster images whose redistribution provenance could not be verified ([#11736](https://github.com/diegosouzapw/OmniRoute/pull/11736)) — PicoClaw documentation now reuses the existing SVG, while Zoo Code remains a text link without an image. +- **docs(legal):** add pinned notices for six LobeHub-derived provider SVGs and 65 byte-exact theSVG assets, retaining registry claims as unverified metadata with explicit trademark and HOLD boundaries ([#11737](https://github.com/diegosouzapw/OmniRoute/pull/11737)). +- **chore(quality):** added a versioned 225/225 provider-asset provenance manifest and blocking drift gate ([#11743](https://github.com/diegosouzapw/OmniRoute/pull/11743)) — the inventory pins magic MIME and SHA-256 for every physical file, records immutable source matches for 71 assets, keeps 69 probable and 85 unresolved entries explicit but non-blocking, and treats provenance as source evidence rather than copyright or trademark clearance. +- **refactor(mitm):** replace four previously derived Traffic Inspector utilities — conversation normalization, SSE merging, secret masking, and header sanitization — with independent clean-room implementations based on public provider schemas, WHATWG SSE, and RFC 6750, while keeping the two independently classified passthrough surfaces unchanged ([#11748](https://github.com/diegosouzapw/OmniRoute/pull/11748)). +- Added a unit test that fails seven days before any dated pack under `config/` (`validUntil` and sibling keys) lapses, naming the file and key. The Alibaba free-tier pack expired on 2026-08-27 and turned every PR red the next morning with no commit involved; renewal now happens on someone's terms, not the clock's. ([#11891](https://github.com/diegosouzapw/OmniRoute/pull/11891)) +- `check:workflows` now fails (under `--strict`/`--ratchet`) when any job routed to a self-hosted runner publishes with `--provenance` — npm rejects that with `422` at the registry, which in v3.8.50 only surfaced after the tag and Docker images were public. ([#11895](https://github.com/diegosouzapw/OmniRoute/pull/11895)) +- `scripts/ops/runner-janitor.sh` now proves a path is idle with one `lsof` snapshot and removes stale leftovers itself (tmpfs after 3 h — it is RAM — disk after 24 h), kills orphan `next-build` processes, prunes checkouts of stopped runners, and alerts on memory pressure; `--dry-run` shows exactly what it would do. `docs/ops/RUNNER_BOX.md` reconciled to the measured box (31 GB, 10 listeners). ([#11893](https://github.com/diegosouzapw/OmniRoute/pull/11893)) +- The `next-build` artefact (1.3 GB) is now written and read under `$RUNNER_TEMP` (per-runner, on disk) instead of `/tmp`, which on the self-hosted pool is a 12 GB tmpfs in RAM. Landing it there took 27–32 of the publish job's 76 minutes, and the fixed `/tmp/e2e-build.tar.gz` name let E2E jobs on different runners overwrite each other's download. ([#11896](https://github.com/diegosouzapw/OmniRoute/pull/11896)) +- The CI `build` job now runs in two concurrency lanes — `main` and pull requests — so a release build is never queued behind (or OOM-killed beside) PR builds on the self-hosted pool, which holds one `next-build` comfortably and two at the edge. ([#11901](https://github.com/diegosouzapw/OmniRoute/pull/11901)) +- Re-freeze the ESLint suppressions on `release/v3.8.51` from a clean-room measurement (2 stale file entries pruned, 55 pre-existing `no-explicit-any` in six new files frozen under #11924) and drop the dead `GPT_SIZE_MAP` constant orphaned by the Adobe Firefly client split, so `No new ESLint warnings` stops failing every PR with exit 2 (Refs #11924) ([#11955](https://github.com/diegosouzapw/OmniRoute/pull/11955)) +- Type the 55 `no-explicit-any` sites that had been frozen under #11924 — four redundant casts in `socksConnectorWithFamily.ts` (undici/socks types already accept them) and the mocks/fixtures of the socks-timeout and isFree suites — and drop their suppression entries; the ESLint ratchet shrinks from 5487 to 5432 (Closes #11924) ([#11975](https://github.com/diegosouzapw/OmniRoute/pull/11975)) +- Take the two hosted-runner builds off the PR rail: `Build App` (`build.yml`) is `workflow_dispatch`-only and `DAST smoke (PR)` runs only for PRs into `main` — the 7 GB hosted VM cannot build `release/v3.8.51` in any profile (19/30 red, VM shutdown ~8 min into `next build`) and both checks had turned into permanent noise on every release PR; the bundle stays validated by `ci.yml` on `main` and by `nightly-release-green` on `release/**` (Closes #11946) ([#11962](https://github.com/diegosouzapw/OmniRoute/pull/11962)) +- Move the four nightly jobs that build the backend (`nightly-schemathesis`, `nightly-llm-security` promptfoo + garak, `nightly-resilience` axe-a11y) off the hosted 7 GB runner — where they died on `release/v3.8.51` unseen — onto the box's new `omni-light` pool (two listeners), and document the reshaped fleet (4 active OmniRoute listeners: 2 `omni-build` + 2 `omni-light`, janitor ceiling 4) (Closes #11965) ([#11967](https://github.com/diegosouzapw/OmniRoute/pull/11967)) +- Stop painting every fork PR into `release/**` red: `quality.yml` `Build (advisory)` is skipped (GitHub still reports `continue-on-error` failures as check FAILURE). Hosted `ubuntu-latest` cannot finish `npm run build` on this tree — same class as #11962 taking `build.yml` off the PR rail. `docker-publish.yml` amd64 now runs on the `.113` `omni-build` pool (31 GB / 32 cores, two listeners) with Turbopack, shares the `heavy-build-main` lane with `ci.yml` `Build` so it queues instead of becoming a third heavy, and keeps per-ref concurrency (a merge storm was starting 8 concurrent OOM builds). arm64 stays on `ubuntu-24.04-arm` with webpack — there is no ARM box. Fallback when `USE_VPS_RUNNER` is off: hosted amd64 + webpack (#11976). ([#12021](https://github.com/diegosouzapw/OmniRoute/pull/12021)) +- `docker-publish.yml` builds with webpack (`OMNIROUTE_USE_TURBOPACK=0`) on **both** arches, including amd64 on the `.113` `omni-build` pool. Turbopack had the RAM (31 GB, `omniroute-113-6`) and still panicked (`TurbopackInternalError: there must be a path to a root`, run 33253576569, 26 min in). The same tree's arm64 webpack build on hosted `ubuntu-24.04-arm` succeeded (run 33264823398). Dockerfile already documents webpack as the Docker escape hatch (#11976). ([#12050](https://github.com/diegosouzapw/OmniRoute/pull/12050)) +- **docs(free-tier):** declare the counting vs deciding regimes for "is it free?" and guard the deciding path from DB-backed catalog resolution ([#12226](https://github.com/diegosouzapw/OmniRoute/pull/12226)) — thanks @maxmad64bis +- **docs(auto-combo):** The mode pack table in `docs/routing/AUTO-COMBO.md` now lists all six shipped packs with every weight each one sets, replacing a four-pack table whose numbers had also drifted from the source. It states plainly that no pack sets `quality`, so selecting any pack silences the observed-quality signal. Six more documents that quote the scoring factor count joined the `check:docs-counts` gate, which caught five stale claims — including one naming nine factors that do not exist — and two stale mode pack counts. The dashboard routing panel, which offered four of the six packs and labelled the default strategy "6-Factor Scoring", is now covered by a test; the two packs it was missing are `reliability-first` and `chaos-mode`, the latter labelled as the fault-injection profile it is rather than as one more routing preference ([#12316](https://github.com/diegosouzapw/OmniRoute/pull/12316)) — thanks @maxmad64bis +- **chore(quality):** rebaseline `src/lib/db/apiKeys.ts` for the ACL the key-creation path now preserves ([#12352](https://github.com/diegosouzapw/OmniRoute/pull/12352)) ([#12673](https://github.com/diegosouzapw/OmniRoute/pull/12673)) — thanks @KrzysiekSko +- **chore(providers):** bump the Claude Code wire identity and the Devin bridge image pin from `2.1.220` to `2.1.258` ([#12402](https://github.com/diegosouzapw/OmniRoute/pull/12402)) — thanks @ggiak ([#12604](https://github.com/diegosouzapw/OmniRoute/pull/12604)) +- **docs(env):** align `.env.example`, the README Bun section, and the troubleshooting guide with the code: `OMNIROUTE_USE_TURBOPACK` also governs `npm run build` (not dev-only), `bun run build` follows that flag instead of auto-selecting Webpack, `OMNIROUTE_CHAT_MAX_HEAVY_IN_FLIGHT` is unset by default (no request-count cap), and the structural `503 chat_admission_busy` message matches `chatAdmissionResponses.ts` (#12404 — thanks @pacocartones) +- **chore(electron):** upgrade the desktop app to Electron 44 (Chromium 152, Node 24.18.1) ([#12217](https://github.com/diegosouzapw/OmniRoute/pull/12217)). **Requires macOS 13 (Ventura) or later** — Chromium dropped macOS 12 (Monterey), so Monterey users must stay on an earlier OmniRoute desktop build. Windows and Linux are unaffected; the app already shipped only x64/arm64, so Electron 44 dropping 32-bit builds changes nothing. Removes the `openAsHidden`/`wasOpenedAsHidden` login-item fields deleted in Electron 44 — hidden autostart continues to work through the `--hidden` argument registered with the login item ([#12554](https://github.com/diegosouzapw/OmniRoute/pull/12554)) — thanks @app/dependabot +- **chore(quality):** rebaseline `src/sse/handlers/chat.ts` for the effective-input persistence the continuation fix needs ([#12641](https://github.com/diegosouzapw/OmniRoute/pull/12641)) ([#12680](https://github.com/diegosouzapw/OmniRoute/pull/12680)) — thanks @hartmark +- `Coverage` job on `ci.yml`: the informational Codecov upload gets its own 5-minute ceiling and `continue-on-error`, and the job budget grows from 20 to 30 minutes (the 8-shard c8 merge alone takes ~10) — a stalled upload no longer ends the job `cancelled` and drags a fully green `main` run's conclusion down with it ([#11972](https://github.com/diegosouzapw/OmniRoute/pull/11972)) +- CI hardening on the PR rail: the four `Unit Tests fast-path` shards get `timeout-minutes: 30` (a hung shard held a PR for 64 min instead of GitHub's 6 h default) and both ESLint file caches lose their `restore-keys` fallback, so a cache built under another suppressions file or lint config can no longer report stale verdicts (Refs #11600, #11924) ([#11963](https://github.com/diegosouzapw/OmniRoute/pull/11963)) +- Every CI job that runs a `next build` (`build`, the npm `publish`, both release-green validations) now targets the `omni-build` runner label, which only two of the eight self-hosted runners carry. The box holds one build comfortably and two at the edge; a third now queues on GitHub instead of being OOM-killed by the kernel. ([#11932](https://github.com/diegosouzapw/OmniRoute/pull/11932)) +- **refactor(combo):** move `handleRoundRobinCombo` (and `resolveTargetTokenLimit`) into `open-sse/services/combo/roundRobinCombo.ts`. `combo.ts` drops from 2164 to 1014 lines (`split("\n").length`); the leaf is 1199 (under the 1200 new-file cap). The round-robin call site uses a dynamic `import()` so `releaseStickyPinOnFailure` / `clearStaleLKGP` can stay exported from `combo.ts` without a static cycle. Skip / sticky / semaphore / safety-timer behavior is unchanged. ([#12811](https://github.com/diegosouzapw/OmniRoute/pull/12811)) — thanks @HouMinXi +- **chore(quality):** rebaseline the file-size caps the error-boundary campaign grew past (`open-sse/executors/codex.ts`, `open-sse/vendor/codex-chatgpt-web/bridge.ts`, both via [#12444](https://github.com/diegosouzapw/OmniRoute/pull/12444)) ([#12654](https://github.com/diegosouzapw/OmniRoute/pull/12654)) +- **chore(quality):** rebaseline the file-size caps the hartmark batch grew past (`combos/page.tsx` via [#12355](https://github.com/diegosouzapw/OmniRoute/pull/12355), `open-sse/services/combo.ts` via [#12338](https://github.com/diegosouzapw/OmniRoute/pull/12338)) ([#12623](https://github.com/diegosouzapw/OmniRoute/pull/12623)) — thanks @hartmark +- **chore(quality):** rebaseline the file-size caps the HouMinXi batch grew past when its PRs stacked (`providers/page.tsx`, `chatCore.ts`, `accountFallback.ts`) — each PR measured correctly in isolation, none saw the stacking ([#12619](https://github.com/diegosouzapw/OmniRoute/pull/12619)) +- **chore(quality):** rebaseline `open-sse/services/combo.ts` for the reset-aware scoring the HouMinXi batch stacked ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) ([#12678](https://github.com/diegosouzapw/OmniRoute/pull/12678)) — thanks @HouMinXi +- **docs:** sync provider catalog count (direct commit `4f11b2ae3d`) +- **docs(skills):** sync omni-inference search fallback (direct commit `b4e76a7ed9`) +- **chore(lint):** prune two stale base suppressions (direct commit `1325047d56`) +- **test(auto-combo):** tolerate full-suite rotation load (direct commit `ad3e293f9f`) +- **test:** drain inherited quality snapshot reds (direct commit `21ed68d8ac`) +- **chore(quality):** rebaseline measured CLI bundle growth (direct commit `d35b3f9779`) +- **test(ui):** reproduce invalid AnySearch icon alias (direct commit `12cc6ca834`) +- **test(translator):** fix the relative imports of the relocated deferred-finish test (direct commit `777d9d1629`) +- **docs(counts):** bring the documented provider and migration counts to the measured values (direct commit `e70bea30e9`) +- **test(security):** drop the guard-target set left unused by the sync (direct commit `a32ba0e362`) +- **chore(db):** renumber model_capabilities migration 163 → 169 (cross-branch collision with 163_radar_feed_cache_generated_at) (direct commit `70f5d4cbf9`) +- **docs(cli):** document CLI_PRIME_AGENT_BIN and correct the CLI Agents count (direct commit `9058e39b61`) +- **chore(deps):** refresh runtimes and adopt ESLint 10 ([#11259](https://github.com/diegosouzapw/OmniRoute/pull/11259)) — thanks @backryun +- **refactor(local-corpus):** implement dynamic root resolution and LRU cache ([#11491](https://github.com/diegosouzapw/OmniRoute/pull/11491)) — thanks @raheemuddin786 +- **chore(quality):** prune stale eslint-suppressions entries ([#11596](https://github.com/diegosouzapw/OmniRoute/pull/11596)) +- **chore(ci):** unblock quality gates - env-doc sync + stryker tap.testFiles registration (#9282) ([#11609](https://github.com/diegosouzapw/OmniRoute/pull/11609)) — thanks @Meet6338-X +- **chore(ci):** point the circular-deps gate at dpdm's real JS entrypoint ([#11615](https://github.com/diegosouzapw/OmniRoute/pull/11615)) — thanks @benzntech +- **chore(quality):** rebaseline file-size for #11448/#11495/#11561 growth ([#11630](https://github.com/diegosouzapw/OmniRoute/pull/11630)) +- **deps:** realign onnxruntime-node pin to @huggingface/transformers' 1.24.3 lockstep ([#11633](https://github.com/diegosouzapw/OmniRoute/pull/11633)) — thanks @jonlwheat2-gif +- **test(autoCombo):** port models_dev_tier #11508 guard from node:test to vitest ([#11635](https://github.com/diegosouzapw/OmniRoute/pull/11635)) — thanks @jonlwheat2-gif +- **chore(quality):** rebaseline file-size for #11083/#11631/#11628 growth ([#11636](https://github.com/diegosouzapw/OmniRoute/pull/11636)) +- **chore(lint):** prune stale ESLint suppressions (base-red #11449) ([#11644](https://github.com/diegosouzapw/OmniRoute/pull/11644)) — thanks @jonlwheat2-gif +- **docs(changelog):** aggregate the ten v3.8.50 fragments into CHANGELOG.md ([#11683](https://github.com/diegosouzapw/OmniRoute/pull/11683)) +- **chore(search):** drop unreachable credential sweep after #11642 ([#11687](https://github.com/diegosouzapw/OmniRoute/pull/11687)) — thanks @HouMinXi +- **ci:** add API route TypeScript regression gate ([#11705](https://github.com/diegosouzapw/OmniRoute/pull/11705)) — thanks @Sabeekhann +- **docs(dedup):** correct the CodeQL claim on the tenant-namespace comment ([#11712](https://github.com/diegosouzapw/OmniRoute/pull/11712)) +- **docs(changelog):** add consolidated v3.8.50 stats, top-25 ranking and a .mailmap ([#11715](https://github.com/diegosouzapw/OmniRoute/pull/11715)) +- **docs:** recommend gstack for AI-assisted workflows ([#11770](https://github.com/diegosouzapw/OmniRoute/pull/11770)) — thanks @santosraju99-hub +- **chore(quality):** tighten complexity/cognitive-complexity ratchets to the current tip; land the missed gateways.ts rebaseline ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **docs(i18n):** finish Freepik → Magnific rebrand in locale strings and README ([#11772](https://github.com/diegosouzapw/OmniRoute/pull/11772)) — thanks @turbolego +- **refactor(auto-combo):** fix divergent scoring in combo health reporting ([#11854](https://github.com/diegosouzapw/OmniRoute/pull/11854)) — thanks @jacobsparts +- **chore(changelog):** add missing fragments for #11919/#11918/#11916 ([#11938](https://github.com/diegosouzapw/OmniRoute/pull/11938)) +- **test(check):** escape the runs-on fixture with JSON.stringify, not a quote-only replace ([#11942](https://github.com/diegosouzapw/OmniRoute/pull/11942)) (direct commit `fb7445eaa3`) +- **test(release):** align five suites with the contracts #11933, #11919 and #11876 shipped on release/v3.8.51 ([#11944](https://github.com/diegosouzapw/OmniRoute/pull/11944)) +- **chore(quality):** remove unreachable code and restore test discovery ([#11950](https://github.com/diegosouzapw/OmniRoute/pull/11950)) — thanks @backryun +- **docs(agents):** sync-back landings are fast-forward, never squash ([#11964](https://github.com/diegosouzapw/OmniRoute/pull/11964)) +- **test(infra):** retry recursive temp-dir removal instead of failing a shard on ENOTEMPTY (#11966) ([#11968](https://github.com/diegosouzapw/OmniRoute/pull/11968)) +- **docs(ops):** the .113 heavy-build ceiling is one runner, not two ([#12048](https://github.com/diegosouzapw/OmniRoute/pull/12048)) +- **test(ui):** correct inactive auto-fetch expectation ([#12098](https://github.com/diegosouzapw/OmniRoute/pull/12098)) — thanks @RaviTharuma +- **docs:** document native dependency check escape hatch ([#12101](https://github.com/diegosouzapw/OmniRoute/pull/12101)) — thanks @RaviTharuma +- **test(providers):** regenerate the translate-path golden for OrcaRouter (#11923) ([#12118](https://github.com/diegosouzapw/OmniRoute/pull/12118)) +- **chore(quality):** velocity phase — loosen every numeric baseline by 20% until v4.0, monitor headroom nightly ([#12125](https://github.com/diegosouzapw/OmniRoute/pull/12125)) +- **chore(lint):** batch 0 of #12146 — type the call-log-cap sqlite rows instead of 45 as-any casts ([#12157](https://github.com/diegosouzapw/OmniRoute/pull/12157)) +- **chore(lint):** batch 4 of #12146 — shared/components react-hooks violations resolved ([#12159](https://github.com/diegosouzapw/OmniRoute/pull/12159)) +- **chore(lint):** batch 1 of #12146 — dashboard/cli-code react-hooks violations resolved ([#12160](https://github.com/diegosouzapw/OmniRoute/pull/12160)) +- **chore(lint):** batch 3 of #12146 — dashboard/settings react-hooks violations resolved ([#12162](https://github.com/diegosouzapw/OmniRoute/pull/12162)) +- **chore(lint):** batch 2 of #12146 — resolve the react-hooks compiler violations in dashboard/providers ([#12163](https://github.com/diegosouzapw/OmniRoute/pull/12163)) +- **chore(quality):** register search-432 cooldown test in stryker tap.testFiles ([#12170](https://github.com/diegosouzapw/OmniRoute/pull/12170)) +- **chore(lint):** batch 5 of #12146 — combos, endpoint, provider-stats, api-manager and costs react-hooks violations resolved ([#12174](https://github.com/diegosouzapw/OmniRoute/pull/12174)) +- **build:** add contributor fast profile ([#12192](https://github.com/diegosouzapw/OmniRoute/pull/12192)) — thanks @rafacpti23 +- **docs(readme):** README touch-ups ([#12193](https://github.com/diegosouzapw/OmniRoute/pull/12193)) ([#12194](https://github.com/diegosouzapw/OmniRoute/pull/12194)) ([#12202](https://github.com/diegosouzapw/OmniRoute/pull/12202)) — thanks @rafacpti23 +- **build:** force the webpack bundler in the contributor build profile ([#12197](https://github.com/diegosouzapw/OmniRoute/pull/12197)) — thanks @rafacpti23 +- **build:** skip standalone packaging in the contributor profile ([#12198](https://github.com/diegosouzapw/OmniRoute/pull/12198)) — thanks @rafacpti23 +- **docs(audit):** align every published number with the code and harden check:docs-counts ([#12200](https://github.com/diegosouzapw/OmniRoute/pull/12200)) +- **build:** stub the instrumentation entrypoints in the contributor profile ([#12203](https://github.com/diegosouzapw/OmniRoute/pull/12203)) — thanks @rafacpti23 +- **build:** omit the standalone output target for contributor builds ([#12204](https://github.com/diegosouzapw/OmniRoute/pull/12204)) — thanks @rafacpti23 +- **chore(lint):** batch 6 of #12146 — memory, radar, audit, analytics, cache, usage, activity, home and RequestLoggerV2 react-hooks violations resolved ([#12208](https://github.com/diegosouzapw/OmniRoute/pull/12208)) +- **docs(diagrams):** rename number-carrying diagram files to stable names ([#12210](https://github.com/diegosouzapw/OmniRoute/pull/12210)) +- **docs(dashboard):** redraw onboarding tier-flow SVGs for the real 4-tier model ([#12211](https://github.com/diegosouzapw/OmniRoute/pull/12211)) +- **docs(api):** document every implemented route in openapi.yaml (276 → 692 paths) ([#12212](https://github.com/diegosouzapw/OmniRoute/pull/12212)) +- **chore(lint):** batch 7 of #12146 — final src tail: 37 react-hooks violations across 33 files resolved ([#12244](https://github.com/diegosouzapw/OmniRoute/pull/12244)) +- Revert "docs: recommend gstack for AI-assisted workflows (#11770)" ([#12248](https://github.com/diegosouzapw/OmniRoute/pull/12248)) ([#12249](https://github.com/diegosouzapw/OmniRoute/pull/12249)) +- **docs:** Chaos Mode setup guide + weighted strategy semantics ([#12250](https://github.com/diegosouzapw/OmniRoute/pull/12250)) +- **docs(agents):** protected-surface merge rule — operator approval for agent-instruction files ([#12253](https://github.com/diegosouzapw/OmniRoute/pull/12253)) +- **chore(quality):** register native-codex turn-pin tests in stryker tap.testFiles ([#12263](https://github.com/diegosouzapw/OmniRoute/pull/12263)) +- **chore(quality):** dedupe tap.testFiles entries added by racing base-red fixes ([#12265](https://github.com/diegosouzapw/OmniRoute/pull/12265)) +- **docs(readme):** introduce OmniRouteTray — the macOS menu-bar companion ([#12276](https://github.com/diegosouzapw/OmniRoute/pull/12276)) — thanks @ggiak +- **chore(proxy):** purge the legacy 1proxy residue — sync/rotator modules, dead DB exports, dead settings tab and flag, docs (#12091) ([#12290](https://github.com/diegosouzapw/OmniRoute/pull/12290)) +- **chore(quality):** baseline-headroom skips generated and vendored files in the fileSize worst-file signal ([#12291](https://github.com/diegosouzapw/OmniRoute/pull/12291)) +- **refactor(video):** unify the JPEG frame data-URI contract ([#12322](https://github.com/diegosouzapw/OmniRoute/pull/12322)) +- **chore(deps):** freeze onnxruntime-node and eslint-plugin-react-hooks out of dependabot groups ([#12329](https://github.com/diegosouzapw/OmniRoute/pull/12329)) +- **test(executors):** restore the #10986 reasoning-only fallback guards ([#12364](https://github.com/diegosouzapw/OmniRoute/pull/12364)) — thanks @pacocartones +- **chore(quality):** re-tighten the file-size ratchet to the real LOC (plan 3.8.52 task 0) ([#12411](https://github.com/diegosouzapw/OmniRoute/pull/12411)) +- **test(grok):** format web executor suite ([#12412](https://github.com/diegosouzapw/OmniRoute/pull/12412)) +- **chore(quality):** ratchet open-sse typecheck baseline to zero ([#12418](https://github.com/diegosouzapw/OmniRoute/pull/12418)) +- **test(build):** guard the artifact path policy arrays against duplicates ([#12422](https://github.com/diegosouzapw/OmniRoute/pull/12422)) +- **chore(lint):** adopt eslint-plugin-react-hooks 7.1.1 ([#12428](https://github.com/diegosouzapw/OmniRoute/pull/12428)) +- **chore(providers):** remove a keyless provider integration at its operator's request ([#12440](https://github.com/diegosouzapw/OmniRoute/pull/12440)) +- **docs(providers):** register providers removed at their operator's request and guard against reintroduction ([#12478](https://github.com/diegosouzapw/OmniRoute/pull/12478)) +- **chore(quality):** base fixes — stryker tap.testFiles + node_modules cache key ([#12482](https://github.com/diegosouzapw/OmniRoute/pull/12482)) +- **docs(arch):** one-process recipe for tens of long /v1/responses ([#12493](https://github.com/diegosouzapw/OmniRoute/pull/12493)) — thanks @RaviTharuma +- **chore(quality):** tighten the CodeQL ratchet baseline from 11 to 6 ([#12530](https://github.com/diegosouzapw/OmniRoute/pull/12530)) +- **chore(deps):** pin browserslist override to ^4.28.8 ([#12592](https://github.com/diegosouzapw/OmniRoute/pull/12592)) — thanks @KooshaPari +- **chore(deps):** bump fast-uri to 3.1.7 in the electron lockfile ([#12601](https://github.com/diegosouzapw/OmniRoute/pull/12601)) +- **docs(quality):** document how the CodeQL ratchet refreshes and how to tighten it ([#12611](https://github.com/diegosouzapw/OmniRoute/pull/12611)) +- **test(ci):** pin the openapi-security-tiers two-arm contract with an executing gate test (#12581) ([#12652](https://github.com/diegosouzapw/OmniRoute/pull/12652)) +- **docs(gamification):** align XP Rewards table with code (#12501) ([#12667](https://github.com/diegosouzapw/OmniRoute/pull/12667)) — thanks @KooshaPari +- **refactor(combo):** split executeTarget into gates, attempt, and loop ([#12746](https://github.com/diegosouzapw/OmniRoute/pull/12746)) — thanks @HouMinXi +- **docs(security):** document socket.yml scanner config + CI workflow link (#12575) ([#12764](https://github.com/diegosouzapw/OmniRoute/pull/12764)) — thanks @KooshaPari +- **chore(ci):** guard commit identity in pre-commit to stop author misattribution ([#12772](https://github.com/diegosouzapw/OmniRoute/pull/12772)) +- **deps:** 17 Dependabot bumps — bump electron from 43.4.0 to 43.4.1 in /electron ([#11426](https://github.com/diegosouzapw/OmniRoute/pull/11426)); bump the development group across 1 directory with 8 updates ([#11428](https://github.com/diegosouzapw/OmniRoute/pull/11428)); bump the production group across 1 directory with 11 updates ([#11440](https://github.com/diegosouzapw/OmniRoute/pull/11440)); chore(deps): bump github/codeql-action/init from 4.37.7 to 4.37.8 ([#11925](https://github.com/diegosouzapw/OmniRoute/pull/11925)); chore(deps): bump github/codeql-action from 4.37.7 to 4.37.8 ([#11926](https://github.com/diegosouzapw/OmniRoute/pull/11926)); chore(deps): bump github/codeql-action/analyze from 4.37.7 to 4.37.8 ([#11927](https://github.com/diegosouzapw/OmniRoute/pull/11927)); build(deps): bump github/codeql-action/init from 4.37.8 to 4.37.9 ([#12345](https://github.com/diegosouzapw/OmniRoute/pull/12345)); build(deps): bump github/codeql-action/analyze from 4.37.8 to 4.37.9 ([#12346](https://github.com/diegosouzapw/OmniRoute/pull/12346)); bump the development group across 1 directory with 2 updates ([#12347](https://github.com/diegosouzapw/OmniRoute/pull/12347)); build(deps): bump github/codeql-action from 4.37.8 to 4.37.9 ([#12349](https://github.com/diegosouzapw/OmniRoute/pull/12349)); bump browserslist from 4.28.2 to 4.28.8 ([#12396](https://github.com/diegosouzapw/OmniRoute/pull/12396)); bump the production group across 1 directory with 4 updates ([#12399](https://github.com/diegosouzapw/OmniRoute/pull/12399)); bump @xmldom/xmldom ([#12500](https://github.com/diegosouzapw/OmniRoute/pull/12500)); bump qs from 6.15.2 to 6.16.0 ([#12512](https://github.com/diegosouzapw/OmniRoute/pull/12512)); bump @xmldom/xmldom from 0.9.10 to 0.9.12 ([#12513](https://github.com/diegosouzapw/OmniRoute/pull/12513)); bump fast-uri from 3.1.5 to 3.1.7 ([#12514](https://github.com/diegosouzapw/OmniRoute/pull/12514)); bump @humanfs/node from 0.16.7 to 0.16.8 ([#12515](https://github.com/diegosouzapw/OmniRoute/pull/12515)) +- Clear the `release/v3.8.51` `check:agent-skills-sync` base-red: regenerate `skills/cli-tunnel/SKILL.md` so the `tunnel create [type]` positional that #13009 taught the generator to read is reflected in the committed skill. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` typecheck base-red from #12731: the new `Mode pack ranking gates` candidates in `open-sse/services/autoCombo/__tests__/autoCombo.test.ts` omitted the required `provider`, `model` and `errorRate` fields of `ProviderCandidate`, failing `check:open-sse-typecheck` (and with it `Fast Quality Gates`) on every open PR. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` docs-sync base-red: EURouter (#13025) and GreenPT (#13024) took the live provider count to 358, leaving 7 STRICT drifts (`PROVIDER_REFERENCE.md`, 4 diagrams, 2 tier-flow images) plus the `AGENTS.md` / `llm.txt` / `package.json` count claims stale. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- **test(models):** Pin `CATALOG_BUILD_TIMEOUT_MS` in the #12058 canonical-catalog regression so a cold catalog build racing the 8s default bound no longer flakes the suite with a 500 ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Allow `@omniroute/opencode-plugin-v2/` in the npm pack policy — [#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870) shipped the v2 plugin beside its v1 sibling without widening the allowlist, so every packed file under it read as an unexpected artifact and failed `check:pack-policy` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Point the release-green validator's pack gate at the tree it is validating — the build-provenance guard defaulted to `origin/main`, which a release branch tip can never be an ancestor of mid-cycle, so `Package artifact` reported a false red on every run and the tarball boot-smoke cascaded off it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **docs(checks):** keep doc counts honest — headings, rankings, catalog, weights, quality gate and scoring diagram now covered ([#12507](https://github.com/diegosouzapw/OmniRoute/pull/12507)) — thanks @maxmad64bis +- **chore(lifecycle):** `check:model-lifecycle` now also diffs `DEFAULT_DEGRADATION_MAP` (the background-task redirect table) against the vendor lifecycle snapshot, refusing a retired id as source or target, with a table-driven unit test beside it. Three rows whose source the vendor had retired — `claude-sonnet-4-20250514`, `gemini-3-pro-preview` and `gpt-5.1-codex` (whose target `gpt-5.1-codex-mini` is retired too) — were dead code, since `checkLifecycle` answers 410 before the redirect runs; they are dropped (#12535 — thanks @pacocartones) +- **test(compression):** cover idle worker eviction at the resource level — the pool must call `terminate()` and must not retain the worker's `MessagePort`, complementing the `exit`-event assertion added with the fix ([#13371](https://github.com/diegosouzapw/OmniRoute/pull/13371)) +- **chore(quality):** raise the `imageGeneration.ts` file-size ceiling for the image-only-model guard that clears the #12945 base-red ([#13392](https://github.com/diegosouzapw/OmniRoute/pull/13392)) +- **chore(quality):** type the combo-test route's JSON response bodies instead of casting them to `any`, drop the now-empty suppression entry, and remove the `hasPerModelQuota` import `chatCore.ts` stopped using when the failure-classification helper was extracted ([#13379](https://github.com/diegosouzapw/OmniRoute/pull/13379)) +- **chore(electron):** the desktop bundle no longer ships the root-level files of every translated docs mirror (`README.md`, `llm.txt`, `CLAUDE.md`, `GEMINI.md`, `CONTRIBUTING.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`) — the packaged app only reads `docs/i18n//docs/**`, which stays. Saves ~11 MB on top of the translated CHANGELOGs already pruned. (#0000) ([#13235](https://github.com/diegosouzapw/OmniRoute/pull/13235)) +- **chore(quality):** retighten the `src/sse/handlers/chat.ts` unused-vars suppression count to the 8 violations that actually remain ([#13385](https://github.com/diegosouzapw/OmniRoute/pull/13385)) +- Bring the documented counts back in line with the code: 171 migrations (was 169) and 20 routing strategies (was 19), plus the regenerated `cli-tunnel` skill reference. ([#12970](https://github.com/diegosouzapw/OmniRoute/pull/12970)) +- Update the documented migration count to 172 after the call-logs provider-stats indexes landed. ([#13209](https://github.com/diegosouzapw/OmniRoute/pull/13209)) +- **chore(ci):** two gates that close the blind spots behind the exclusions above. `check:vitest-exclusions` requires every Vitest exclusion to name a tracking issue and to appear in `config/quality/vitest-exclusions.json` — the previous list grew to 62 files behind a comment pointing at an issue that had been closed for a month. `check-new-key-coverage` requires a key newly added to `en.json` to reach every locale; the existing coverage gate is a percentage floor per locale, so eleven absent keys out of ~13,000 left it at 99.9% while a whole feature shipped untranslated in nine languages. Both are diff-aware, so pre-existing debt stays frozen and neither needed a migration to turn on. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **build:** root layout no longer loads Inter from `next/font/google`, so a production image build does not need fonts.googleapis.com ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **docs(i18n):** bring the eight new `llm.txt` mirrors (km, kn, ml, my, ne, or, pa, si) up to the current provider and migration counts, so `check-docs-sync` stops rejecting every commit ([#13674](https://github.com/diegosouzapw/OmniRoute/pull/13674)) +- **refactor(ui):** flow surfaces (home topology, combo live studio, compression cockpit/waterfall/nodes, token-health badge) express state through the theme-aware `--orch-status-*` tokens instead of fixed dark-mode hex, so green/red/amber/grey stay legible in the light theme; dark mode is byte-identical. Categorical palettes (routing-strategy hues, compression-layer pills, provider brand colors) deliberately stay hex ([#12378](https://github.com/diegosouzapw/OmniRoute/issues/12378)) ([#13203](https://github.com/diegosouzapw/OmniRoute/pull/13203)) +- **chore(tests):** 51 test files that had been excluded from Vitest are running again, restoring roughly 350 assertions to the blocking suite. Each was measured individually first: of the 62 files parked behind the `// #8618 — pre-existing failure` comment, 51 pass against the current tree with no source change, so the exclusions had outlived the failures they were added for. The 11 that genuinely still fail stay excluded, but now point at a live tracker (#13204) rather than at #8618, which was closed in August while the list it tracked kept growing. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **chore(quality):** drop two ESLint suppression entries whose violations no longer exist, so `eslint --suppressions-location` stops rejecting every commit that touches the surrounding files ([#13384](https://github.com/diegosouzapw/OmniRoute/pull/13384)) +- **chore(release):** bring main's post-tag fixes into release/v3.8.51 — sync-back step 2/2 (direct commit `21c488f210`) +- **chore(deps):** patch toml and esbuild in the opencode plugin lockfiles ([#12965](https://github.com/diegosouzapw/OmniRoute/pull/12965)) +- **docs(changelog):** reconcile the v3.8.51 living section — fold 366 fragments, cover every cycle commit, credit every contributor ([#12971](https://github.com/diegosouzapw/OmniRoute/pull/12971)) +- **test(plugin):** wait for the v2 lazy-refresh tier instead of sleeping past it ([#12984](https://github.com/diegosouzapw/OmniRoute/pull/12984)) +- **chore(quality):** rebaseline chatCore.ts after the non-streaming regression fixes ([#13045](https://github.com/diegosouzapw/OmniRoute/pull/13045)) +- **docs(changelog):** add the missing bullet marker to the DeepSeek PoW fragment ([#13200](https://github.com/diegosouzapw/OmniRoute/pull/13200)) +- **chore(deps):** drain the Dependabot queue — 10 of 13 alerts ([#13213](https://github.com/diegosouzapw/OmniRoute/pull/13213)) +- **chore(ci):** make worktrees carry the hooks that enforce the identity gate ([#13236](https://github.com/diegosouzapw/OmniRoute/pull/13236)) +- **chore(deps):** pin csv-parse to ^7.0.2 via overrides (#13117) ([#13300](https://github.com/diegosouzapw/OmniRoute/pull/13300)) — thanks @KooshaPari +- **chore(deps):** bump hono from ^4.12.34 to ^4.13.7 (#13148) ([#13301](https://github.com/diegosouzapw/OmniRoute/pull/13301)) — thanks @KooshaPari +- **chore(deps):** pin joi to ^18.2.8 via overrides (#13085) ([#13302](https://github.com/diegosouzapw/OmniRoute/pull/13302)) — thanks @KooshaPari +- **docs(providers):** add Microsoft 365 Copilot (BizChat) provider guide (#12779) ([#13340](https://github.com/diegosouzapw/OmniRoute/pull/13340)) — thanks @KooshaPari +### 🙌 Contributors + +Thanks to everyone whose work landed in v3.8.51: + +| Contributor | PRs / Issues | +| --- | --- | +| [@5dive-bot](https://github.com/5dive-bot) | #11852 | +| [@abhisheksharma2411](https://github.com/abhisheksharma2411) | #11547, #11684, #11959, #12209 | +| [@adevwithpurpose](https://github.com/adevwithpurpose) | #11464 | +| [@adivekar-utexas](https://github.com/adivekar-utexas) | #12015, #12043, #12090, #12138 | +| [@AIB1TAL0S](https://github.com/AIB1TAL0S) | #12103 | +| [@alltomatos](https://github.com/alltomatos) | #11448 | +| [@alvinveroy](https://github.com/alvinveroy) | #11857, #12027, #12165 | +| [@amaleta](https://github.com/amaleta) | #11906, #11934 | +| [@amartinawi](https://github.com/amartinawi) | #11575 | +| [@AndrianBalanescu](https://github.com/AndrianBalanescu) | #11083, #11830, #11834, #11844 | +| [@anhtahaylove](https://github.com/anhtahaylove) | #13091, #13092, #13093, #13096, #13097, #13100, #13106, #13109, #13114, #13155, #13166, #13171, #13175, #13187, #13196 | +| [@aniruddhaadak80](https://github.com/aniruddhaadak80) | #11760 | +| [@app](https://github.com/app) | #12554 | +| [@arminanton](https://github.com/arminanton) | #11461, #11513 | +| [@AStupidBear](https://github.com/AStupidBear) | #11584 | +| [@atescivitci-cmd](https://github.com/atescivitci-cmd) | #12105 | +| [@b3nw](https://github.com/b3nw) | #11970, #11971, #12147 | +| [@backryun](https://github.com/backryun) | #11259, #11950, #12075, #12076, #12078, #12079, #12081, #12082, #12181, #12228, #12239, #12255, #12258, #12277, #12367, #12423, #12524, #12538 | +| [@benjaminkitt](https://github.com/benjaminkitt) | #11747 | +| [@benzntech](https://github.com/benzntech) | #11614, #11615 | +| [@Bl0ck154](https://github.com/Bl0ck154) | #11948, #11951, #11952, #11953, #11954 | +| [@brick30llc-ctrl](https://github.com/brick30llc-ctrl) | #11752, #12242, #12262 | +| [@Chewji9875](https://github.com/Chewji9875) | #11455, #11875, #12028 | +| [@cheynetom](https://github.com/cheynetom) | #12058 | +| [@davidebaraldo](https://github.com/davidebaraldo) | #12222, #12304 | +| [@davidlinfr](https://github.com/davidlinfr) | #6390 | +| [@ddarkr](https://github.com/ddarkr) | #12124 | +| [@Deftera186](https://github.com/Deftera186) | #11809 | +| [@dpozimski](https://github.com/dpozimski) | #11945 | +| [@DW-MediaLab](https://github.com/DW-MediaLab) | #12229 | +| [@echel0nn](https://github.com/echel0nn) | #11923 | +| [@f9td56dbgh-hub](https://github.com/f9td56dbgh-hub) | #11660 | +| [@fabioluissilva](https://github.com/fabioluissilva) | #11991 | +| [@foreveryh](https://github.com/foreveryh) | #12177 | +| [@geek007git](https://github.com/geek007git) | #12115, #12116, #12117, #12120, #12122 | +| [@geekyNads](https://github.com/geekyNads) | #11784, #12266 | +| [@ggiak](https://github.com/ggiak) | #11561, #12276, #12350, #12402 | +| [@gonisulaimann](https://github.com/gonisulaimann) | #12368, #12369, #12371 | +| [@Gorillaz322](https://github.com/Gorillaz322) | #12207 | +| [@groovecityJO](https://github.com/groovecityJO) | #12682 | +| [@hartmark](https://github.com/hartmark) | #11434, #11452, #11473, #11499, #11703, #11983, #11984, #11985, #11986, #11988, #11989, #11990, #11994, #12221, #12293, #12445, #12446, #12447, #12448, #12460, #12461, #12623, #12646, #12650, #12680, #12717, #12718, #12727, #12741, #12854 | +| [@hizzt](https://github.com/hizzt) | #11894 | +| [@hongnoul](https://github.com/hongnoul) | #11484 | +| [@HouMinXi](https://github.com/HouMinXi) | #11411, #11414, #11512, #11518, #11520, #11641, #11642, #11643, #11687, #11779, #11849, #11850, #11851, #11915, #11916, #11918, #11919, #11920, #12013, #12017, #12033, #12042, #12106, #12139, #12166, #12169, #12171, #12205, #12213, #12312, #12325, #12487, #12488, #12495, #12504, #12557, #12566, #12590, #12591, #12624, #12626, #12632, #12637, #12678, #12696, #12697, #12711, #12733, #12746, #12767, #12770, #12789, #12803, #12805, #12811, #12866, #12868, #12899, #12926, #12934, #12950, #12951, #12974, #13001, #13006, #13011, #13017, #13026, #13027, #13034, #13035, #13038, #13042, #13050, #13060, #13061, #13069, #13107, #13120, #13136, #13195, #13197 | +| [@Hsia97](https://github.com/Hsia97) | #11624 | +| [@jacobsparts](https://github.com/jacobsparts) | #11854, #12155, #12167 | +| [@joglomedia](https://github.com/joglomedia) | #11980 | +| [@jonlwheat2-gif](https://github.com/jonlwheat2-gif) | #11443, #11450, #11549, #11567, #11608, #11633, #11635, #11640, #11644, #11671, #11672, #11673, #11674, #11675, #11676, #11677, #11706, #11783, #12051, #12052, #12053, #12055 | +| [@kanade-hoshino](https://github.com/kanade-hoshino) | #12180 | +| [@Karan825](https://github.com/Karan825) | #11861 | +| [@KaspaPulse](https://github.com/KaspaPulse) | #11389, #11469, #11628, #11666, #11910 | +| [@keeltrace](https://github.com/keeltrace) | #12080, #12223 | +| [@killer30001000](https://github.com/killer30001000) | #12178 | +| [@KooshaPari](https://github.com/KooshaPari) | #12470, #12592, #12667, #12699, #12703, #12706, #12764, #12769, #12771, #13300, #13301, #13302, #13340, #13401, #13403, #13404, #13405, #13406, #13407, #13409, #13410, #13411, #13412, #13413, #13414, #13418, #13424, #13427, #13433, #13523, #13524, #13525, #13528, #13531, #13532, #13534, #13539, #13543, #13545, #13546, #13547, #13550, #13553 | +| [@kriptoburak](https://github.com/kriptoburak) | #11370 | +| [@KrzysiekSko](https://github.com/KrzysiekSko) | #12425, #12432, #12673 | +| [@marcelokarval](https://github.com/marcelokarval) | direct commit / report | +| [@maxmad64bis](https://github.com/maxmad64bis) | #1622, #11435, #11437, #11441, #11537, #11550, #11553, #11555, #11557, #11812, #11842, #11843, #11903, #12151, #12214, #12215, #12218, #12226, #12314, #12316, #12317, #12318, #12319, #12320, #12321, #12507, #12715, #12731, #12744, #12786, #12787, #12788, #12790, #12792, #12794, #12795, #12828, #12832, #12853, #12857, #12870, #12937, #12941, #12975, #13141, #13142, #13146, #13147 | +| [@mdigitalbh81](https://github.com/mdigitalbh81) | #12240 | +| [@Meet6338-X](https://github.com/Meet6338-X) | #11598, #11609, #12110 | +| [@morpheus9393](https://github.com/morpheus9393) | #11943 | +| [@MumuTW](https://github.com/MumuTW) | #11492, #11502, #11506, #11507, #11626, #11675, #11685, #11728, #11746, #11888, #11889, #11890, #11892 | +| [@Neuron-Mr-White](https://github.com/Neuron-Mr-White) | #11622, #11801, #12256 | +| [@NightStalker-87](https://github.com/NightStalker-87) | #12183 | +| [@NoxzRCW](https://github.com/NoxzRCW) | #11879, #11880, #11881, #11882, #11883, #11935 | +| [@ntdat812](https://github.com/ntdat812) | #11585, #12095 | +| [@ntdatt812](https://github.com/ntdatt812) | #11368, #11573, #11574, #11576, #11577, #11580, #11582, #11583, #11588, #11589, #11590, #11591, #11592, #11593, #11672, #12177, #12180, #12873, #12918, #12920, #12921, #12925, #12930, #13007, #13009, #13024, #13025, #13083, #13087, #13101, #13104, #13110 | +| [@official-burak](https://github.com/official-burak) | #11542 | +| [@opensource-elearning](https://github.com/opensource-elearning) | #12179, #12189, #12278, #12286 | +| [@oyi77](https://github.com/oyi77) | #11408, #11409, #11421, #11505, #11677, #12036, #12110 | +| [@pacocartones](https://github.com/pacocartones) | #11521, #11522, #11527, #11528, #11529, #11530, #11531, #11532, #11533, #11534, #11595, #11599, #11603, #11604, #11605, #11607, #11610, #11676, #11714, #11716, #11718, #11767, #11838, #11860, #11862, #11869, #11871, #11872, #11873, #11903, #11906, #11921, #11934, #12356, #12358, #12359, #12360, #12361, #12362, #12364, #12365, #12373, #12374, #12375, #12376, #12377, #12379, #12380, #12381, #12386, #12387, #12389, #12390, #12394, #12395, #12397, #12401, #12403, #12404, #12522, #12523, #12535, #12536, #12540, #12541, #12543, #12545, #12548, #12549, #12550, #12551, #12552, #12647, #12651, #12653, #13055 | +| [@patrykkopycinski](https://github.com/patrykkopycinski) | #11936, #11937, #12224 | +| [@PauloFH](https://github.com/PauloFH) | #11509 | +| [@PauloHSOliveira](https://github.com/PauloHSOliveira) | #12241 | +| [@ponkcore](https://github.com/ponkcore) | #12054 | +| [@Prajeeth-12](https://github.com/Prajeeth-12) | #11634, #12046 | +| [@quiterunner-commits](https://github.com/quiterunner-commits) | #12131, #12143 | +| [@rafacpti23](https://github.com/rafacpti23) | #11554, #11558, #12192, #12197, #12198, #12202, #12203, #12204 | +| [@ragnar-claude](https://github.com/ragnar-claude) | #11564 | +| [@raheemuddin786](https://github.com/raheemuddin786) | #11491, #11839, #11840, #11841, #12003, #12230, #12231, #12232, #12233, #12234 | +| [@rahilmavani](https://github.com/rahilmavani) | #11761 | +| [@Rahulsharma0810](https://github.com/Rahulsharma0810) | #11771 | +| [@RaviTharuma](https://github.com/RaviTharuma) | #11710, #11727, #11797, #11798, #11802, #11805, #11806, #11811, #12098, #12099, #12101, #12449, #12452, #12472, #12473, #12493, #12533, #12607, #12628, #12631, #12636, #12875, #12876, #12880, #12882, #12884 | +| [@rezjalibd](https://github.com/rezjalibd) | #12186 | +| [@RhianB14](https://github.com/RhianB14) | #11692 | +| [@rifqiawl](https://github.com/rifqiawl) | #11517, #11519, #11969 | +| [@rqzbeh](https://github.com/rqzbeh) | #11390 | +| [@Sabeekhann](https://github.com/Sabeekhann) | #11699, #11705 | +| [@santosraju99-hub](https://github.com/santosraju99-hub) | #11755, #11770 | +| [@Seramicx](https://github.com/Seramicx) | #11568 | +| [@Siva010](https://github.com/Siva010) | #12191 | +| [@solstxce](https://github.com/solstxce) | #11597 | +| [@soroush5](https://github.com/soroush5) | #12691 | +| [@tenshiak](https://github.com/tenshiak) | #12279 | +| [@TheDemonTuan](https://github.com/TheDemonTuan) | #11468, #11470, #11471, #11482, #11548, #11758, #11775, #11814 | +| [@thomasmaerz](https://github.com/thomasmaerz) | #12834 | +| [@tuandinh0801](https://github.com/tuandinh0801) | #11454 | +| [@turbolego](https://github.com/turbolego) | #11621, #11762, #11772, #11774, #11781, #12216 | +| [@Tushar49](https://github.com/Tushar49) | #11392 | +| [@ujjawalkaushik1110](https://github.com/ujjawalkaushik1110) | #11863 | +| [@vermasomesh835](https://github.com/vermasomesh835) | #11794 | +| [@vsd2807](https://github.com/vsd2807) | #11565, #11619 | +| [@wahidsadik371-coder](https://github.com/wahidsadik371-coder) | #12126 | +| [@watchingdogs](https://github.com/watchingdogs) | #12031 | +| [@wildcard](https://github.com/wildcard) | #11620 | +| [@xiaoyaner0201](https://github.com/xiaoyaner0201) | #11460, #11662, #12083 | +| [@Xxx91n](https://github.com/Xxx91n) | #11690 | +| [@yourspraveen](https://github.com/yourspraveen) | #11146 | +| [@yxyxy](https://github.com/yxyxy) | #11671 | +| [@ZaimMarzuki](https://github.com/ZaimMarzuki) | #11960 | +| [@Zartharas](https://github.com/Zartharas) | #11340 | +| [@zero-executioner](https://github.com/zero-executioner) | #11631 | +| [@diegosouzapw](https://github.com/diegosouzapw) | maintainer | + +--- ## [3.8.50] — 2026-08-25 _Living section — regenerated 2026-08-12 from all cycle commits (cycle open `ed2db6cb19` → tip). Bullets carry the merged PR and its author; direct pushes listed separately._ diff --git a/docs/i18n/et/CHANGELOG.md b/docs/i18n/et/CHANGELOG.md index fb8b8676f4..71af305ac0 100644 --- a/docs/i18n/et/CHANGELOG.md +++ b/docs/i18n/et/CHANGELOG.md @@ -95,16 +95,1119 @@ ## [3.8.51] — TBD -_Living section — cycle opened at the v3.8.50 freeze (parallel-cycle model). Bullets are aggregated from `changelog.d/` fragments at each `/generate-release` phase._ +_Living section — reconciled 2026-09-15 from all cycle commits (`091589089c` → `c0f92ec98a`, 916 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 | **123** | +| 📝 Commits in the cycle | **916** | +| 🔀 Pull requests referenced | **892** | +| 📋 Changelog entries | **919** | +| 🙌 Contributors credited in entries | **121** | +| 🤖 Automated dependency commits | 18 | + +**Entries by type** + +| Type | Count | +| --- | ---: | +| 🐛 Fixes | 592 | +| ✨ Features | 131 | +| 🧹 Chore | 93 | +| 📚 Docs | 42 | +| 🧪 Tests | 32 | +| ♻️ Refactor | 8 | +| 🏗️ Build | 7 | +| ⚡ Performance | 7 | +| ⚙️ CI | 2 | +| ⏪ Reverts | 2 | +| 📦 Dependencies | 2 | +| 🔒 Security | 1 | + +### 🏆 Top 25 contributors this cycle + +_By commits in `091589089c..c0f92ec98a`, author identities consolidated via `.mailmap` and the merged PR's GitHub login. Bots excluded._ + +| # | Contributor | Commits | +| ---: | --- | ---: | +| 🥇 | diegosouzapw | 323 | +| 🥈 | Bob.Hou (@HouMinXi) | 79 | +| 🥉 | Paco Cartones (@pacocartones) | 77 | +| 4 | Dizzle (@maxmad64bis) | 49 | +| 5 | Koosha Paridehpour (@KooshaPari) | 43 | +| 6 | Nguyen Thanh Dat (@ntdatt812) | 31 | +| 7 | Ravi Tharuma (@RaviTharuma) | 26 | +| 8 | Markus Hartung (@hartmark) | 22 | +| 9 | Webman (@jonlwheat2-gif) | 22 | +| 10 | backryun | 17 | +| 11 | anhtahaylove | 15 | +| 12 | Rafa Martins (@rafacpti23) | 10 | +| 13 | Syed Raheemuddin (@raheemuddin786) | 10 | +| 14 | Nguyễn Viết Tuấn (@TheDemonTuan) | 8 | +| 15 | MumuTW | 7 | +| 16 | Paijo (@oyi77) | 7 | +| 17 | Tobias Andersen (@turbolego) | 6 | +| 18 | Bl0ck (@Bl0ck154) | 5 | +| 19 | SHANMUGAPRIYAN (@geek007git) | 5 | +| 20 | KaspaPulse | 5 | +| 21 | Abhishek Sharma (@abhisheksharma2411) | 4 | +| 22 | Abhishek Divekar (@adivekar-utexas) | 4 | +| 23 | Andrew B. (@AndrianBalanescu) | 4 | +| 24 | NoxzRCW | 4 | +| 25 | opensource-elearning | 4 | ### ✨ New Features +- **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). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- Added Google AI Studio Gemini batch text-to-speech support through `POST /v1/audio/speech`. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- 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. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(routing):** subscription-first auto groupings — `auto/subscription` routes only through plan-included connections with a documented hard-stop overage and fails closed on exhaustion, while `auto/thrifty` orders the pool `subscription → keyless → free → cheap → premium` and steps up one rung at a time as each is exhausted. Billing class comes from a curated per-connection catalog (uncurated is treated as metered, never plan-included), both reuse STRICT_ZERO_COST's per-connection verification, and a quota reading whose `resetAt` has passed is now refreshed regardless of TTL so routing returns to plan capacity as soon as it resets ([#11146](https://github.com/diegosouzapw/OmniRoute/pull/11146)) — thanks @yourspraveen +- **feat(providers):** publish a management-authenticated versioned web-session credential contract from OmniRoute's canonical browser credential metadata ([#11340](https://github.com/diegosouzapw/OmniRoute/pull/11340)) — thanks @Zartharas +- **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)) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add Xquik X search with typed results, credential validation, REST routing, and MCP selection ([#11370](https://github.com/diegosouzapw/OmniRoute/pull/11370)) — thanks @kriptoburak +- **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)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(dashboard):** surface durable exclusive managed leases in the existing Sessions view, keeping leased clients visible across idle gaps while marking connections with in-flight work as active ([#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389)) — thanks @KaspaPulse +- **build(bun):** allow Turbopack bundler flag on Bun 1.4+ with configurable Webpack fallback ([#11471](https://github.com/diegosouzapw/OmniRoute/pull/11471)) — thanks @TheDemonTuan +- feat(api): add an opt-in `modelVisibilityAllowlist`/`modelVisibilityDenylist` settings pair to curate exactly which models `/v1/models` advertises, mirrored into every `auto/*` combo candidate pool so a denied model cannot be routed to via combo selection either (#11481) ([#11997](https://github.com/diegosouzapw/OmniRoute/pull/11997)) +- **feat(rankings):** the Free Provider Rankings page shows what each provider actually served over the last 24 h. It ranked by ELO alone, which left a provider that answers every call with an error in first place; the usage data was already served by the API but never requested. A provider with too small a sample shows a dash, not a number ([#11546](https://github.com/diegosouzapw/OmniRoute/pull/11546)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **feat(guardrails):** enforce a bounded, deterministic contract for Video Bridge transcripts — 256 cues, 4096 input code units and 4 KiB UTF-8 per cue, 64 KiB total text, malformed-Unicode rejection, focus-window scoping, cross-source reconciliation with contributing-source metadata, and a structural provenance trust boundary so caller JSON can never self-assert `embedded`/`audio-bridge` provenance ([#11652](https://github.com/diegosouzapw/OmniRoute/issues/11652)) ([#12009](https://github.com/diegosouzapw/OmniRoute/pull/12009)) +- **feat(video):** orchestrate optional Video Bridge audio extraction and Audio Bridge STT behind a dual opt-in (operator setting AND per-request signal) — a new loopback-only broker `mode=audio` operation shares the frame path's exact process queue, deadline, AbortSignal, and byte budgets to extract a bounded mono 16 kHz PCM WAV from the same already-downloaded video, then reuses the existing Audio Bridge transcription boundary; provider segment timing is preserved when available and marked coarse otherwise, and every failure degrades to a visual-only-safe partial instead of throwing (#11654). ([#12012](https://github.com/diegosouzapw/OmniRoute/pull/12012)) +- Add a tenant-bound Video Bridge drill-down lifecycle on top of the existing secure cache substrate: opaque hashed handles (never raw session/video identifiers), preview/standard/detail multiresolution variants resampled on read, response pagination capped at 8 frames and 32 MiB, and a new authenticated `/api/v1/video-bridge/drilldown` consumer route that stays disabled for remote access by default and denies cross-key access with the same response as a nonexistent handle (no existence oracle). ([#12006](https://github.com/diegosouzapw/OmniRoute/pull/12006)) +- **test(video):** Add the Video Bridge FU-07/FU-09 promotion-evidence harness (#11656) — a frozen Zod manifest schema covering the 8 required scenario kinds (static scenes, rapid cuts, late facts, fades, blur, small text, close events, visual prompt injection) with a minimum of 3 repetitions per case, deterministic declarative fixture recipes (`videoBridgePromotionFixtures.ts`), a pure medians/p95 metrics aggregator, a pure FU-07/FU-09 promotion-verdict evaluator applying the ticket's exact thresholds (missing token usage always holds), a digest-only persistence layer that never retains raw media or raw model responses, and a versioned per-model promotion allowlist shipped empty with every model defaulting to `hold`. The FU-07/FU-09 promotion verdicts themselves remain HOLD — they require a real evidence run against real models on VPS 192.168.0.15. ([#12008](https://github.com/diegosouzapw/OmniRoute/pull/12008)) +- **feat(video bridge):** "embedded" transcript provenance can now be legitimately earned instead of merely asserted — a bounded, allowlisted (`mov_text`/`subrip`/`webvtt`) subtitle probe runs through the loopback-only Video Bridge broker (at most 2 streams, 10s subdeadline bounded by the request deadline, 256 KiB output, 4096-code-unit lines), normalized through a bounded, ReDoS-safe WebVTT parser and Zod-validated end to end. The adapter always resolves to an explicit `success`/`absent`/`transient_failure` outcome — a subtitle failure never breaks the visual description path, and only a fingerprint-verified broker response (never a caller-declared label) can produce embedded cues (#11659). ([#12011](https://github.com/diegosouzapw/OmniRoute/pull/12011)) +- Default new Antigravity-family connections (agy CLI imports and Antigravity OAuth connects) to model auto-sync, so live model discovery lands in the synced catalog and `/v1/models` picks up freshly released upstream models (e.g. Gemini 3.7 Flash tiers) without code changes. Existing connections keep their current setting; the per-connection dashboard toggle remains the opt-out. (#11685 — thanks @MumuTW) ([#11887](https://github.com/diegosouzapw/OmniRoute/pull/11887)) +- **feat(ui):** enable React Compiler (`reactCompiler: true` + `babel-plugin-react-compiler`) for automatic memoization at build time ([#11783](https://github.com/diegosouzapw/OmniRoute/pull/11783)) — thanks @jonlwheat2-gif +- **feat(providers):** add SeekAi (`seekai.cc`) as an OpenAI-compatible New-API gateway — catalog id `seekai` (alias `ska`), `https://seekai.cc/v1`, live `/v1/models` via `passthroughModels`, aggregator-list membership so New-API balance detection can opt in. No referral/aff codes. ([#11786](https://github.com/diegosouzapw/OmniRoute/issues/11786)) ([#12557](https://github.com/diegosouzapw/OmniRoute/pull/12557)) — thanks @HouMinXi +- **feat(zai):** add GLM-5.3-Flash Coding Plan support (1M context, 128K output, vision, `low|high|max` reasoning) and route `zai` GLM-5.3-family API-key traffic through the OpenAI-compatible Coding Plan endpoint with native thinking defaults ([#11801](https://github.com/diegosouzapw/OmniRoute/pull/11801)) — thanks @Neuron-Mr-White +- **feat(combo):** choose how combo models are ordered — manual, provider, score, or name — via a sort control in the dashboard builder, persisted in `config.modelSort` and re-applied on load and after add ([#11812](https://github.com/diegosouzapw/OmniRoute/pull/11812)) — thanks @maxmad64bis +- **feat(free):** custom models can be marked free-tier via `customModels[].isFree`; `isFreeModel()` is the first door and `hidePaidModels` respects it even for providers outside the free budget ([#11843](https://github.com/diegosouzapw/OmniRoute/pull/11843)) — thanks @maxmad64bis +- **feat(nodejs):** add `5dive` as a `configure` target — `omniroute configure 5dive` / `omniroute setup-5dive` write a 5dive auth profile that points an agent fleet's `claude` seats at OmniRoute, with the root-only write, the loopback-vs-`https` endpoint rule and the per-seat model pin handled explicitly ([#11852](https://github.com/diegosouzapw/OmniRoute/pull/11852)) — thanks @5dive-bot +- **feat(sse):** treat `max` as a first-class reasoning-effort tier and clamp per model family (GLM 5.1+/DeepSeek V4+/Kimi K3+ keep native `max`; o1/MiniMax/Grok/Muse Spark clamp to their upstream ceiling) ([#11875](https://github.com/diegosouzapw/OmniRoute/pull/11875)) — thanks @Chewji9875 +- **feat(providers):** the provider plugin manifest now advertises a `usage-fetch` capability for the 40 providers that have a wired usage/quota fetcher, so external dashboards can read it from `GET /api/v1/provider-plugin-manifest` instead of parsing `open-sse/services/usage.ts` after every release. Discovery only — no new fetcher, no quota change, and the Dashboard quota widget stays gated by `USAGE_SUPPORTED_PROVIDERS`. `USAGE_FETCHER_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/fetcherProviders.ts`) and is re-exported from `services/usage.ts`, keeping the manifest module a light leaf instead of pulling the ~490-module usage dispatcher into the manifest route. ([#11903](https://github.com/diegosouzapw/OmniRoute/pull/11903)) — thanks @maxmad64bis / @pacocartones +- **feat(plugins):** `OMNIROUTE_PLUGINS_DIR` sets the directory the runtime plugin scanner reads — and the root the plugin manager installs into — overriding the `HOME`-derived default, so a Docker/K8s deployment can point straight at its bind-mounted plugin tree instead of moving `HOME` just to relocate the scan path. An image that exports no home no longer scans `/tmp/.omniroute/plugins` in silence: the resolved directory is logged once at startup as `scanner.dir_resolved`, naming the input that won. Unset, behaviour is unchanged. Distinct from the CLI-only `OMNIROUTE_PLUGIN_PATH`, which finds `omniroute-cmd-*` command packages and never reached this scanner ([#11906](https://github.com/diegosouzapw/OmniRoute/pull/11906)) — thanks @amaleta / @pacocartones +- **feat(leases):** add an explicit owner-authenticated status action that returns only the active lease's privacy-safe configured connection and provider labels, with generation fencing and no credential or internal-id disclosure ([#11910](https://github.com/diegosouzapw/OmniRoute/pull/11910)) — thanks @KaspaPulse +- **feat(providers):** import-from-file modal shows per-row API errors and ships a downloadable CSV template ([#12071](https://github.com/diegosouzapw/OmniRoute/issues/12071)) ([#12504](https://github.com/diegosouzapw/OmniRoute/pull/12504)) — thanks @HouMinXi +- **feat(providers):** dashboard search matches connection name and `baseUrl` so imported OpenAI-compat nodes surface on the provider card ([#12108](https://github.com/diegosouzapw/OmniRoute/issues/12108)) ([#12495](https://github.com/diegosouzapw/OmniRoute/pull/12495)) — thanks @HouMinXi +- **feat(routing):** add a `score` Auto router strategy that selects the highest configured weighted score and reuses `explorationRate`. ([#12155](https://github.com/diegosouzapw/OmniRoute/pull/12155)) — thanks @jacobsparts +- Add a runtime feature flag to disable universal context handoffs globally without changing the default behavior. ([#12167](https://github.com/diegosouzapw/OmniRoute/pull/12167)) — thanks @jacobsparts +- **feat(providers):** the provider plugin manifest now also advertises a `usage-supported` capability for the 46 providers whose usage API is accepted by the server and Dashboard routes, so integrators can distinguish "the server will serve quota for this provider" from "a fetcher is wired" without reading TypeScript. Discovery only — no fetcher or quota change. `usage-fetch` resolves on id or alias (the usage dispatcher accepts both); `usage-supported` resolves on id alone, matching the runtime guard `USAGE_SUPPORTED_PROVIDERS.includes(providerId)`. `USAGE_SUPPORTED_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/supportedProviders.ts`) and is re-exported from `providers.ts`, mirroring the `fetcherProviders` leaf from #11903 and keeping the manifest a light module. ([#12214](https://github.com/diegosouzapw/OmniRoute/pull/12214)) — thanks @maxmad64bis +- **feat(rankings):** order Free Provider Rankings by what each provider actually served — `GET /api/free-provider-rankings?sortBy=reliability` and a "Most reliable first" toggle on the page. Providers with too few calls to state a success rate keep their score order below the measured ones; the default order is unchanged ([#12218](https://github.com/diegosouzapw/OmniRoute/pull/12218)). — thanks @maxmad64bis +- **perf(sse):** defer `cloneLogPayload()` in the structured SSE collector until after the `maxEvents`/`maxBytes` cap check, eliminating ~9,800 wasted `structuredClone` calls per streaming response (65–71% faster `push()`). Reducer snapshot isolation restored for OpenAI and Responses summaries ([#12241](https://github.com/diegosouzapw/OmniRoute/pull/12241)) — thanks @PauloHSOliveira ([#12243](https://github.com/diegosouzapw/OmniRoute/pull/12243)) +- **feat(usage):** Devin CLI agentic quota (Codeium seat-management GetUserStatus) and OpenRouter key limits plus account credits now surface in Provider Limits ([#12256](https://github.com/diegosouzapw/OmniRoute/pull/12256) — thanks @Neuron-Mr-White) +- **feat(settings):** persist `headroomUrl` through Settings so status/start use the operator URL instead of only `HEADROOM_URL` ([#12306](https://github.com/diegosouzapw/OmniRoute/issues/12306)) ([#12487](https://github.com/diegosouzapw/OmniRoute/pull/12487)) — thanks @HouMinXi +- **feat(auto-combo):** Auto-Combo scoring can now weigh how often a provider/model has actually succeeded. The engine already carried that number on every candidate — 24 hours of usage history behind a ten-sample floor, real-time metrics otherwise — and the scoring function never read it, while the weight table described `stability` as if it did. `reliability` (`1 - failureRate`, with the same field precedence and the same rate-bounding the speed ranking already uses, so a corrupt reading means "nothing observed" rather than "fails every call") is now a declared factor shipping at weight `0`, so routing is unchanged until an operator gives it one, and the `stability` description now matches what that factor computes ([#12317](https://github.com/diegosouzapw/OmniRoute/pull/12317)) — thanks @maxmad64bis +- **feat(routing):** With `freeAccessPolicy: "strict"`, the read-only candidate listing (`GET /v1/auto-combo/{channel}/candidates`) no longer hides the candidates the zero-cost guard excludes — the same read-only transparency the resilience filter already honours (#9133). Each candidate now carries `freeAccessExclusion` saying why it would be kept out, and it tells an exhausted allowance apart from a quota reading that never arrived or went stale, which used to look identical from the outside. Routing is unchanged: the listing reports, it never enforces. The separate `excludeTosAvoid` guard still drops its candidates without a reason; that gap is now documented rather than closed ([#12319](https://github.com/diegosouzapw/OmniRoute/pull/12319)) — thanks @maxmad64bis +- **feat(radar):** The Radar catalog table now shows two facts it was already receiving from the feed and dropping on the floor: the per-model rate limits (requests and tokens, per minute and per day) in a new column, and a badge when a provider's terms state it may train on the prompts you send. A limit of zero renders as zero rather than "rate-only" — for a ceiling those are opposite facts — and a model with no training statement gets no badge, because an absent statement is not a guarantee ([#12320](https://github.com/diegosouzapw/OmniRoute/pull/12320)) — thanks @maxmad64bis +- **feat(radar):** explain Community, single-use, contributor, supporter, recovery, abuse, offers, and privacy rules before either Radar activation action, and remove the superseded fixed-PR grant promise from every UI locale ([#12342](https://github.com/diegosouzapw/OmniRoute/pull/12342)) +- **feat(gamification):** the dashboard Profile page now shows the real daily streak — `/api/gamification/level` returns `streak: { current, longest }` (per key with `apiKeyId`, operator-wide maximum otherwise) and the streak card reads it instead of a hard-coded 0 (#2403) ([#12377](https://github.com/diegosouzapw/OmniRoute/pull/12377)) — thanks @pacocartones +- **feat(gamification):** the dashboard leaderboard now shows each API key's display name under the Name column instead of a truncated key id; `GET /api/gamification/leaderboard` attaches `name` per entry (name only — no key material), while the shared ranking helper and the federation leaderboard stay id-only — thanks @pacocartones ([#12385](https://github.com/diegosouzapw/OmniRoute/pull/12385)) +- **feat(gamification):** enforce the documented 1000 XP/min per-API-key anti-cheat rate limit on the XP award path; over-limit awards are logged and skipped instead of persisted, and the sliding window now matches the timestamp format stored in `xp_audit_log` ([#2403](https://github.com/diegosouzapw/OmniRoute/issues/2403)) ([#12390](https://github.com/diegosouzapw/OmniRoute/pull/12390)) — thanks @pacocartones +- **feat(admin):** localize the gamification anomalies page — the loading state, the Status column and the Suspicious badge now come from the `common` catalog (new `common.suspicious` key propagated to every locale) — add it to the Gamification sidebar group as `gamification-admin` (`/dashboard/gamification/admin`), and expose the loading and empty states as polite `role="status"` live regions (#12401 — thanks @pacocartones) +- **feat(providers):** skip GitHub combo members missing from the live synced catalog, and drop Copilot models that are policy-disabled or hidden from the model picker ([#12473](https://github.com/diegosouzapw/OmniRoute/pull/12473)) — thanks @RaviTharuma +- **feat(i18n):** locale-expansion tooling — `npm run i18n:add-locale` adds a language to config, dashboard, docs mirrors, CLI, README/indexes and site in one command; browser, CLI and cookie detection resolve `uk`, `fil`/`tl`, `zh-Hant` (and the retired legacy `in` → `id`) via config aliases; `config/i18n.json` now ships in the npm package so the published CLI can read it; new real-translation ratio gate (`npm run i18n:check-ratio`, advisory) with a per-locale ratchet baseline; `run-translation --adopt` restores `.i18n-state.json`; `sync-language-bars` generates 🌐 bars from config; `validate_translation.py` loads its allowlist again. Retires the duplicate `in` locale (Indonesian mislabelled as Hindi) — 42 honest locales; saved `NEXT_LOCALE=in` / `OMNIROUTE_LANG=in` keep working. (#12496) +- **feat(api):** Emit gateway-measured `tokens_per_second` (TTFT excluded) on streaming usage and `X-OmniRoute-Tokens-Per-Second` when first-token latency is known ([#12616](https://github.com/diegosouzapw/OmniRoute/issues/12616)) ([#12631](https://github.com/diegosouzapw/OmniRoute/pull/12631)) — thanks @RaviTharuma +- **feat(opencode-plugin): map gateway cost/usage/tok/s onto OpenCode inference payloads** — the official plugin copies `X-OmniRoute-Response-Cost`, token counts, `X-OmniRoute-Tokens-Per-Second` / `usage.tokens_per_second`, TTFT, and the winning `X-OmniRoute-Model` onto the JSON/SSE body OpenCode already consumes. Missing tok/s is left unset (never `tokens / latency`). (#12636) — thanks @RaviTharuma +- **feat(combo):** add combo strategy `quota-weighted`: skip exhausted accounts, then weighted-draw by leftover / in-flight load; existing conversations stay pinned until the account is empty ([#12789](https://github.com/diegosouzapw/OmniRoute/pull/12789)) — thanks @HouMinXi +- **feat(models):** Account live listings become the chat catalog source for Claude, Codex, Copilot, and AGY; public metadata only fills prices on IDs those accounts already list. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **fix(models):** Union `agy` and `antigravity` live catalogs so `agy/gemini-3.8-flash-high` is not rejected after the prefix folds to `antigravity`. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **feat(opencode):** Add `@omniroute/opencode-plugin-v2`, an opencode v2 plugin that gives a v2 install a route to an OmniRoute gateway: it publishes models, combos and auto-combos into the host catalog, refreshes them lazily behind a 300s TTL, and keeps serving the last known catalog from an on-disk snapshot when the gateway is unreachable. The v1 plugin (`@omniroute/opencode-plugin`) is untouched and keeps working as before. The plugin adapts to the host instead of assuming one shape: it reads the shape the host seeds into the catalog draft and publishes the `api` block, the pre-`api` top-level fields, or both. The gateway key comes from the host credential store when one is connected, so no secret needs to sit in a config file; a source the gateway refuses is named in a warning instead of silently degrading the catalog to raw model ids; and tool schemas bound for a Gemini model are stripped of the JSON-Schema keywords Gemini rejects, so tool calling survives that route ([#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870)) — thanks @maxmad64bis +- feat(services): show sanitized CLIProxyAPI account health from its authenticated management API without exposing credentials, file paths, or raw account metadata (#6342) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add AnySearch free web search + URL extract (webFetch) with typed results, credential validation, REST routing, and MCP selection - fallback-only ([#11690](https://github.com/diegosouzapw/OmniRoute/pull/11690)) — thanks @Xxx91n +- **feat(dashboard):** display clamped `[0, 100]%` cached input token ratio in request logs table ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) — thanks @b3nw +- **feat(catalog):** add `OMNIROUTE_DISABLE_THINKING_LEVEL_VARIANTS` feature flag to optionally filter out thinking level variants from model catalog ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) — thanks @b3nw +- **feat(dashboard):** continuously export call logs to external analytics stores. A pluggable destination registry ships the full Logs-tab record set on an hourly `JobRegistry` cron, with a persisted per-destination cursor, batched inserts, a config UI rendered from each destination's own field descriptors, and a REST layer (`/api/log-export/*`) for CRUD, a connection test, and an on-demand run. A destination can opt into `includeBodies` to also ship the request and response payloads shown in the Logs detail pane, including the client and provider views of each call; this is off by default, and payloads inherit the dashboard's PII sanitisation, secret redaction and `noLog` handling. Google BigQuery is the first destination, using a service-account key stored encrypted at rest and streaming inserts keyed by call-log id, into a table that is day-partitioned on `timestamp` and clustered on `api_key_name`, `provider`, `model` and `status`. ([#11945](https://github.com/diegosouzapw/OmniRoute/pull/11945)) — thanks @dpozimski +- **feat(providers):** add MaxAI as a signed, OpenAI-compatible provider serving its 13 paid chat models (GPT-5.6 / Luna / Thinking, Claude 5 Sonnet, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite, Grok 4.1-fast / 4.5, DeepSeek V3.2 / R1, Llama 3.3 70B) through OmniRoute's `/v1` endpoint, with per-request HMAC-SHA1→SM3→AES request signing, live model + context-window discovery from `/models/get_config`, and prompted tool-calling translated to OpenAI `tool_calls` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI vision input — image_url content parts are forwarded inline in `message_content` to the 6 vision-capable models (GPT-5.6 / Luna / Thinking, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite) ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI image generation — 6 image models (gpt-image-1, dall-e-3, flux-1-schnell/dev/pro, sd3-medium) exposed through `POST /v1/images/generations` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI document RAG — inline base64 file/document attachments are uploaded to MaxAI (content-addressed `doc_id`) and attached to the chat via `doc_list` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** browserless MaxAI onboarding — email device-pair login (`/api/providers/[id]/login`) and signed access-token refresh, so a connection can be created and kept fresh without a real browser or Google OAuth ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** per-provider TLS impersonation profile (MaxAI presents a Windows Firefox-150 client fingerprint) so its bot-sensitive endpoints accept OmniRoute traffic ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** add **Nimble** as a web-search and web-fetch provider (`nimble-search`) — `/v1/search` routes to Nimble's search API at `lite` depth (locale, freshness and include/exclude domain filters mapped onto the shared request contract), and `/v1/web/fetch` routes to Nimble Extract, which covers all four fetch formats (`markdown`, `html`, `links`, `screenshot`) from a single call. One API key serves both surfaces. ([#11620](https://github.com/diegosouzapw/OmniRoute/pull/11620)) — thanks @wildcard +- The npm publish is automatic again, through npm Trusted Publishing (OIDC): the hosted `stage-npm` job publishes with a short-lived credential minted from GitHub's id-token — no `NPM_TOKEN`, no 2FA prompt, provenance attached. `publish_mode=staged` (owner approves with 2FA) and `direct` (token) remain available on `workflow_dispatch`. ([#11931](https://github.com/diegosouzapw/OmniRoute/pull/11931)) +- **feat(providers):** Add **Opper** as an API-key gateway provider — EU-hosted AI gateway with 700+ models from 30+ providers behind one OpenAI-compatible API and one key (`OPPER_API_KEY`); model ids use `provider/model` format (e.g. `anthropic/claude-sonnet-4-6`, `openai/gpt-5`); live model catalog at `https://api.opper.ai/v3/compat/models`; entry mirrors `requesty` (same shape, `passthroughModels: true`, no static seed) ([#11629](https://github.com/diegosouzapw/OmniRoute/pull/11629)) +- **feat(dashboard):** the `/dashboard/orchestration` snapshot hook now subscribes to the `agents` WebSocket channel (`agent.task.updated`) instead of `requests` as its refetch trigger, and relaxes its background poll from 5s to 30s while that WS connection is up — falling back to the tighter 5s cadence, reprogrammed live on any connect/disconnect transition, whenever the socket is down. ([#12409](https://github.com/diegosouzapw/OmniRoute/pull/12409)) +- **feat(dashboard):** Orchestration canvas quick wins — search box plus state/source/provider filter chips with a one-click clear, and per-source collapse/expand, all reflected in the URL so a filtered/collapsed view is shareable and survives a refresh; the detail drawer gained a "copy trace JSON" action and hardened error/empty-state and accessibility handling; the Agents-tab edges now animate traveling particles along active (running) connections; and the canvas node/edge status colors moved off fixed hex values onto theme-aware `--orch-status-*` CSS custom properties, so they adapt correctly to light/dark mode. ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- **chore(dashboard):** Orchestration UI hardening pass and the missing component/model test coverage it called for — `OrchestratorNode`/`ActivityNode`/`OverflowNode` rendering, the `?node=`/overflow-click page routing, the Agents-tab orchestrator-click no-op and `showCompleted` toggle, and the overview kanban's done-column sort order (#12270, #12271). ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- New `/dashboard/orchestration` page: live unified view of everything running — Cloud Agent, A2A and Conductor as a real-time graph (Agents tab), the combo cascade (Routing tab, reusing the Combo Live Studio) and a state kanban (Overview tab), with a detail drawer (trace, cost, approve/cancel). Read-only over existing APIs — no new backend. Canvas concept credit: PR #11815 design ([#12261](https://github.com/diegosouzapw/OmniRoute/pull/12261)) +- **feat(dashboard):** new "History" tab on `/dashboard/orchestration` — an Airflow-style grid of finished runs over a 24h/7d/30d preset window, one row per (source, identity), clicking a cell opens the existing detail drawer. It is backed by real persistence: A2A task lifecycle transitions are now written to the `a2a_tasks` table (purged after 30 days, configurable via `OMNIROUTE_A2A_HISTORY_RETENTION_DAYS`) and served by the new `GET /api/a2a/tasks/history` listing endpoint, with the task-detail route falling back to persisted history once a run leaves the in-memory snapshot. Conductor runs stay remote and are not persisted locally — the tab says so instead of silently omitting them. ([#12479](https://github.com/diegosouzapw/OmniRoute/pull/12479)) +- **feat(dashboard):** the orchestration detail drawer gained a "Repeat" action for Cloud Agent, A2A and Conductor tasks — a two-click confirm (click once to arm, click again within the confirm window to fire) re-submits the original prompt/input as a new run. The button is disabled with an explanatory tooltip whenever the original input can't be recovered from the loaded task detail (e.g. it never carried a prompt, or the detail failed to load). Two limitations of the A2A variant, by design: it targets the `/a2a` JSON-RPC endpoint, which authenticates with an API key only (`REQUIRE_API_KEY=true` or a configured `OMNIROUTE_API_KEY` makes a dashboard-session repeat answer `HTTP 400` — surfaced verbatim in the drawer's error line, never as a success), and the `message/send` call is SYNCHRONOUS: the POST blocks for the whole skill run, so the success confirmation only appears once the repeated task finishes. A dashboard-authenticated A2A creation path is deliberately left to a follow-up — widening the endpoint's auth posture is an operator decision, not a side effect of this feature. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(a2a):** A2A task execution now records which memories were consulted for the task's last user message as `metadata.memoryHits` (id/key/type/content-snippet) plus a `memory_hits` history event, purely for observability — the retrieved memory is never injected into a skill's prompt or behavior. Gated by the `OMNIROUTE_A2A_MEMORY_HITS` kill-switch (default enabled; set to `0` to skip the recall lookup entirely). The drawer's new "Memory used" section lists these hits for a2a tasks and is omitted whenever there are none. Known limitation: recall only resolves under the keyless posture — a keyed caller's task owner is a SHA-256 prefix of the API key, while memory rows are keyed by the database api-key id, and no hash→id lookup exists today, so the hit list stays empty for keyed callers. The recorded hits are also kept out of the task's own `input` (and therefore out of the persisted input and of the "Repeat" request body), so repeating a task never re-sends the previous run's memory snippets. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(providers):** add a Perplexity Agent API provider (`perplexity-agent` / `pplx-agent`) for Perplexity `/v1/responses`, including the documented Anthropic, OpenAI, Google, xAI, DeepSeek, Z.AI, Moonshot/Kimi, NVIDIA, and Perplexity model IDs plus Anthropic-model `max_output_tokens` compatibility. ([#12103](https://github.com/diegosouzapw/OmniRoute/pull/12103)) — thanks @AIB1TAL0S +- **feat(providers):** add RPD (Requests Per Day) limit to provider rate limit overrides across UI, schemas, DB, and i18n ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) — thanks @b3nw +- **feat(providers): add UC Direct (uncensored.com Developer API), the metered OpenAI-compatible surface.** A standard OpenAI-compatible passthrough (default executor) for uncensored.com's official REST API at `https://api.uncensored.com/api/v1`: `X-api-key` auth (never-expiring `uai_sk_live_` key), `POST /chat/completions` with streaming SSE and native tool-calling, and the full live metered catalog (82 models across 15 providers, discovered from the public `GET /v1/models`). Registered as provider `uc-direct` (alias `ucd`). Complements the un-metered `uc` persona provider — same models, metered credits and a plain API key instead of a subscription session. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(providers): add UC (uncensored.com), the un-metered subscription "persona" chat as an OpenAI-compatible provider.** A WebSocket web-app port: a durable Clerk credential mints a short-lived session token per connect (browserless — no API key), driving UC's persona socket. Ships the browserless email-code login (request → verify → harvest), the 19 verified persona models (Claude Opus, Gemini, Grok, GLM, Kimi, DeepSeek, MiniMax, incl. the uncensored variants), prompted `` tool-calling with a per-model code-style dialect + auto-cure retry for guardrailed models, live ``/reasoning split, streaming + non-streaming OpenAI responses, and full quota/auth error surfacing (paywall / message-limit / rate-limit → 429, invalid session → 401 re-login). Full multimodal parity via the persona blob-upload layer: **vision** (image input, 15 vision-capable models), **document RAG** (PDF/doc upload, server-side extraction), **image generation** (22 models), **video generation** (14 models, async signed-url → poll), and **TTS** (streaming MP3). Registered as provider `uc` (alias `ucn`). The metered OpenAI-compatible Developer API is a separate `uc-direct` provider. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(logs):** add copy-all button to request detail modal ([#11083](https://github.com/diegosouzapw/OmniRoute/pull/11083)) — thanks @AndrianBalanescu +- **feat(check):** make serviceKinds required and add the reverse-walk provider consistency gate ([#11392](https://github.com/diegosouzapw/OmniRoute/pull/11392)) — thanks @Tushar49 +- **feat(providers):** only advertise verified-working connections by default ([#11448](https://github.com/diegosouzapw/OmniRoute/pull/11448)) — thanks @alltomatos +- **feat(resilience):** add hierarchical concurrency admission ([#11493](https://github.com/diegosouzapw/OmniRoute/pull/11493)) +- **feat(providers):** derive + surface expiry for JWT-bearing web cookies (#11497) ([#11505](https://github.com/diegosouzapw/OmniRoute/pull/11505)) — thanks @oyi77 +- **feat(tunnels):** support named cloudflared tunnels via CLOUDFLARED_CONFIG ([#11561](https://github.com/diegosouzapw/OmniRoute/pull/11561)) — thanks @ggiak +- **feat(compression):** make proactive context-compression threshold a live setting ([#11564](https://github.com/diegosouzapw/OmniRoute/pull/11564)) — thanks @ragnar-claude +- **feat(resilience):** add provider peak-hour protection ([#11622](https://github.com/diegosouzapw/OmniRoute/pull/11622)) — thanks @Neuron-Mr-White +- **feat(providers):** add 1min.ai provider ([#11631](https://github.com/diegosouzapw/OmniRoute/pull/11631)) — thanks @zero-executioner +- **feat(dashboard):** collapsible JSON tree viewer for request/response payloads ([#11703](https://github.com/diegosouzapw/OmniRoute/pull/11703)) — thanks @hartmark +- **feat(routing):** add LiquidAI LFM2.5-2.6B free tier via OpenRouter ([#11752](https://github.com/diegosouzapw/OmniRoute/pull/11752)) — thanks @brick30llc-ctrl +- **feat(pricing):** add GLM-5.3-Flash pricing, model specs, and catalog registration ([#11830](https://github.com/diegosouzapw/OmniRoute/pull/11830)) — thanks @AndrianBalanescu +- **feat(routing):** add relayMode for schema-locked context handoffs ([#11839](https://github.com/diegosouzapw/OmniRoute/pull/11839)) — thanks @raheemuddin786 +- **feat(config):** add support for runtime system prompt configuration and hot-reloading ([#11841](https://github.com/diegosouzapw/OmniRoute/pull/11841)) — thanks @raheemuddin786 +- **feat(routing):** expand connection-aware quota prefilter across combo strategies (#11682) ([#11850](https://github.com/diegosouzapw/OmniRoute/pull/11850)) — thanks @HouMinXi +- **feat(models):** reactive discovery sync after Antigravity model-not-found 404 ([#11888](https://github.com/diegosouzapw/OmniRoute/pull/11888)) — thanks @MumuTW +- **feat(models):** auto-aliases and token limits from synced Antigravity discovery ([#11889](https://github.com/diegosouzapw/OmniRoute/pull/11889)) — thanks @MumuTW +- **feat(settings):** raise sticky round-robin limit caps to 1000 ([#12015](https://github.com/diegosouzapw/OmniRoute/pull/12015)) — thanks @adivekar-utexas +- **feat(resilience):** operator-configurable global credential health check interval ([#12043](https://github.com/diegosouzapw/OmniRoute/pull/12043)) — thanks @adivekar-utexas +- **feat(quota):** use official OpenCode Go usage API ([#12124](https://github.com/diegosouzapw/OmniRoute/pull/12124)) — thanks @ddarkr +- **feat(resilience):** default the credential health check sweep to 60 minutes ([#12138](https://github.com/diegosouzapw/OmniRoute/pull/12138)) — thanks @adivekar-utexas +- **feat(quality):** new-code mode for the complexity and dead-code ratchets (Clean as You Code) ([#12142](https://github.com/diegosouzapw/OmniRoute/pull/12142)) +- **feat(dashboard):** orchestration canvas — unified model + snapshot hook (part 1/2) ([#12156](https://github.com/diegosouzapw/OmniRoute/pull/12156)) +- **feat(usage):** add Kilo Code balance and Kilo Pass quotas ([#12178](https://github.com/diegosouzapw/OmniRoute/pull/12178)) — thanks @killer30001000 +- **feat(providers):** refresh vendored ChatGPT Web connector to v4.0.7 ([#12181](https://github.com/diegosouzapw/OmniRoute/pull/12181)) — thanks @backryun +- **feat(providers):** manual "Clear cooldown" action in the cooling panel ([#12224](https://github.com/diegosouzapw/OmniRoute/pull/12224)) — thanks @patrykkopycinski +- **feat(providers):** restore ChatGPT Web via clean-room browser transport ([#12239](https://github.com/diegosouzapw/OmniRoute/pull/12239)) — thanks @backryun +- **feat(sse):** wire the PROVIDER_PROFILES window gate into the global provider cooldown ([#12247](https://github.com/diegosouzapw/OmniRoute/pull/12247)) +- **feat(providers):** modernize CLOVA Studio chat and embeddings ([#12277](https://github.com/diegosouzapw/OmniRoute/pull/12277)) — thanks @backryun +- **feat(browser):** adopt Obscura as primary headless browser engine with Chromium fallback ([#12286](https://github.com/diegosouzapw/OmniRoute/pull/12286)) — thanks @opensource-elearning +- **feat(sse):** full language parity for output styles (es/de/fr/it/ru/zh + autoDetect) ([#12289](https://github.com/diegosouzapw/OmniRoute/pull/12289)) +- **feat(quality):** complete test:scoped — --full map rebuild, stdin selection, CI loader parity (#8084 D1) ([#12353](https://github.com/diegosouzapw/OmniRoute/pull/12353)) +- **feat(providers):** refresh Fable, Cursor, and Devin catalogs ([#12367](https://github.com/diegosouzapw/OmniRoute/pull/12367)) — thanks @backryun +- **feat(video):** redact transcript text from logs and durable memory (#12150 P1) ([#12427](https://github.com/diegosouzapw/OmniRoute/pull/12427)) +- **feat(dashboard):** parent-link, genuine-continuation badge, and modal perf fixes ([#12448](https://github.com/diegosouzapw/OmniRoute/pull/12448)) — thanks @hartmark +- **feat(dashboard):** adaptive context-budget dial on compression panel ([#12488](https://github.com/diegosouzapw/OmniRoute/pull/12488)) — thanks @HouMinXi +- **feat(providers):** refresh Z.ai Web models and browser transport ([#12524](https://github.com/diegosouzapw/OmniRoute/pull/12524)) — thanks @backryun +- **feat(video):** redact raw client-snapshot transcript fields in the detailed log (#12150 P2) ([#12528](https://github.com/diegosouzapw/OmniRoute/pull/12528)) +- **feat(providers):** refresh NVIDIA hosted models ([#12538](https://github.com/diegosouzapw/OmniRoute/pull/12538)) — thanks @backryun +- **feat(quota):** Moonshot Open Platform balance and TPD lock for custom nodes ([#12590](https://github.com/diegosouzapw/OmniRoute/pull/12590)) — thanks @HouMinXi +- **feat(video):** redact transcript in the in-memory pending-request snapshot (#12430 item 6) ([#12596](https://github.com/diegosouzapw/OmniRoute/pull/12596)) +- **feat(dashboard):** link a log entry's Conversation Context to its owning conversation ([#12646](https://github.com/diegosouzapw/OmniRoute/pull/12646)) — thanks @hartmark +- **feat(catalog):** eligibility-gated free-tier bucket ([#12669](https://github.com/diegosouzapw/OmniRoute/pull/12669)) +- **feat(db):** fail-closed previous_response_id continuation for redacted video turns (#12150 P2b) ([#12707](https://github.com/diegosouzapw/OmniRoute/pull/12707)) +- **feat(gamification): pay the documented `streak_bonus` and `badge_unlock` XP rewards.** `XP_REWARDS` listed both rewards but the award pipeline never paid them: the private reward table in `events.ts` omitted them, `updateStreak()` did not report when a streak extended, and badge unlocks carried no XP. Every request that extends a daily streak now pays `streak_bonus × streak length` once per UTC day (guarded by a same-day `xp_audit_log` check), and every badge unlocked through the pipeline pays `badge_unlock` once per badge (guarded by the `user_badges` primary key; `unlockBadge()` now reports whether it inserted). Bonus XP flows through the same `addXp` + level sync + global/weekly/monthly leaderboard path as action XP, so level-ups and rankings include it. The Radar supporter recognition unlock stays XP-free. (#12522 — thanks @pacocartones) +- **feat(providers):** advertise a `free-tier` capability in the provider plugin manifest for every provider with documented free models, so sidecars and dashboards can filter free-capable providers without reading the quota catalog ([#12786](https://github.com/diegosouzapw/OmniRoute/pull/12786)) — thanks @maxmad64bis +- **feat(providers):** Added EURouter as an OpenAI-compatible API-key gateway (`https://api.eurouter.ai/v1`), with live model discovery via `passthroughModels`. Its copy states that models are served by third-party upstreams listed per model, so an EU-based router is not read as EU data residency for inference. ([#13025](https://github.com/diegosouzapw/OmniRoute/pull/13025)) — thanks @ntdatt812 +- **feat(providers):** Added GreenPT as an OpenAI-compatible API-key provider (`https://api.greenpt.ai/v1`), with live model discovery via `passthroughModels`. No free-inference badge: the published docs describe a free API subscription billed per token, not a free tier. ([#13024](https://github.com/diegosouzapw/OmniRoute/pull/13024)) — thanks @ntdatt812 +- **feat(i18n):** 9 new locales — Greek (`el`), Croatian (`hr`), Serbian (`sr`), Lithuanian (`lt`), Estonian (`et`), Latvian (`lv`), Slovenian (`sl`), Maltese (`mt`), Irish (`ga`) — across the dashboard, docs mirrors, CLI, README and the site; OmniRoute now ships every official EU language (51 locales). (#13044) +- **feat(i18n):** 8 new locales — Kannada (`kn`), Malayalam (`ml`), Odia (`or`), Punjabi (`pa`), Nepali (`ne`), Sinhala (`si`), Burmese (`my`), Khmer (`km`) — across the dashboard, docs mirrors, CLI, README and the site (59 locales). The translator now restores the ICU literal escape around angle placeholders and splits oversized docs sections before translating. (#0000) ([#13660](https://github.com/diegosouzapw/OmniRoute/pull/13660)) +- feat(providers): **list Agnes 3.0 Flash as the current free chat model, drop retired 1.5 Flash, add Image 2.0/2.5 Flash plus Video 2.5/2.5 Flash, and discover the live `/v1/models` catalog (including the CN host `api.agnes-ai.cn`).** `agnes-1.5-flash` now forwards to `agnes-3.0-flash`. Video 2.5 polls `GET /v1/videos/{id}` (not the V2.0 `/agnesapi` contract). Live `/v1/models` (2026-09-09) no longer serves 1.5; the wiki marks it deprecated. 3.0 Flash is 512K context / 65,536 max output, same window as 2.5. CN-region keys use the existing per-connection base-URL field, default stays `apihub.agnes-ai.com`. ([#13120](https://github.com/diegosouzapw/OmniRoute/pull/13120)) — thanks @HouMinXi +- **feat(sse):** Codex and OpenAI catalogs list GPT-6 Astra with effort aliases (`-low` through `-ultra`); Codex CLI identity pins `@openai/codex@0.153.4` in lockstep with the image ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **feat(dashboard):** the orchestration History tab gained a "Compare runs" mode — toggling it turns each grid cell into a 2-item selection queue (a 3rd click drops the oldest pick), and picking two cells opens a side-by-side comparison panel instead of the usual detail drawer. The panel fetches both runs' detail the same way the drawer does (falling back to persisted history once a run leaves the live TTL window) and shows, per side: identity/source/state, start time, a signed `right - left` delta for duration/cost/event count, the event timeline aligned by index, and any memory hits. One side's fetch failing never blocks the other, and a delta is only ever computed when both sides have a finite value — otherwise it renders "—", never `NaN`. Comparing two runs from different sources or skills still works; a banner marks the deltas as informational rather than hiding them, since the two runs aren't a strict apples-to-apples pair. ([#12677](https://github.com/diegosouzapw/OmniRoute/pull/12677)) +- **feat(dashboard):** badge a conversation that never reached a clean stop ([#12717](https://github.com/diegosouzapw/OmniRoute/pull/12717)) — thanks @hartmark +- **feat(release):** reconcile-changelog tool + version-anchored fragment aggregation ([#12987](https://github.com/diegosouzapw/OmniRoute/pull/12987)) +- **feat(mcp):** carry context_length through MCP list_models_catalog projection (#12776) ([#13406](https://github.com/diegosouzapw/OmniRoute/pull/13406)) — thanks @KooshaPari +- **feat(cli):** make startup readiness budget configurable (#13369) ([#13433](https://github.com/diegosouzapw/OmniRoute/pull/13433)) — thanks @KooshaPari + ### 🐛 Bug Fixes +- **security(streaming):** sanitize generic mid-stream error messages before emitting OpenAI, + Responses, or Claude SSE failure frames and before diagnostic logging, while preserving raw + failures for internal classification and keeping client disconnects out of provider failure state. ([#12457](https://github.com/diegosouzapw/OmniRoute/pull/12457)) +- **fix(claude):** `blockExtraUsage: false` no longer lets 5h quota preflight skip the account; extra usage is billed after the session bar is gone, so the request must reach Anthropic ([#12803](https://github.com/diegosouzapw/OmniRoute/pull/12803)) — thanks @HouMinXi +- Sanitize HuggingChat conversation-creation and message-send transport failures before they reach client error bodies or provider logs. ([#12467](https://github.com/diegosouzapw/OmniRoute/pull/12467)) +- **fix(dashboard):** Keep local and theme-aware provider SVG icons at a definite layout size so Chromium does not collapse them to 0×0 after the v3.8.50 image-rendering change ([#12054](https://github.com/diegosouzapw/OmniRoute/pull/12054)) — thanks @ponkcore +- **fix(security):** Sanitize provider and runtime failures before public API, SSE and MCP responses and before persistent request, proxy and usage logs, preventing credentials, stack traces and host filesystem paths from crossing those boundaries while preserving stable error codes and useful diagnostics. ([#12506](https://github.com/diegosouzapw/OmniRoute/pull/12506)) +- **fix(cli):** `omniroute tunnel create` no longer crashes with `Cannot read properties of undefined (reading optsWithGlobals)` — removed the duplicate positional argument that caused Commander.js to misalign the action callback parameters ([#12295](https://github.com/diegosouzapw/OmniRoute/issues/12295)) ([#12368](https://github.com/diegosouzapw/OmniRoute/pull/12368)) — thanks @gonisulaimann +- Stop advertising Gemini Live-only models as supported audio endpoints until OmniRoute proxies the bidirectional Live protocol. ([#11710](https://github.com/diegosouzapw/OmniRoute/pull/11710)) — thanks @RaviTharuma +- **fix(kie):** reroute `flux/kontext` off the KIE Market `createTask` flow — it is catalogued with `isMarket: true` but has no Market catalog page, so KIE rejected it with "model name not supported"; it now hits the dedicated `POST /api/v1/flux/kontext/generate` / `GET /api/v1/flux/kontext/record-info` endpoints instead (#11296). ([#11985](https://github.com/diegosouzapw/OmniRoute/pull/11985)) — thanks @hartmark +- **fix(kie):** correct 12 more KIE Market catalog ids that were sent to `createTask` unchanged but diverge from KIE's documented upstream `model` values — GPT Image 2 T2I/I2I (drops the `gpt/` prefix), GPT Image 1.5 T2I/I2I (`gpt-image/` namespace), Seedream 5.0 Lite T2I/I2I (drops the `.0`), all 4 Flux 2 variants (`flux-2/` namespace, generic variant renamed `flex`), and Wan 2.7 Image / Image Pro (dash instead of dot) — each verified individually against the literal example request published on docs.kie.ai. `#11326`'s "everything else already matches" claim was wrong a second time (#11296); `z-image/4.0-*`/`z-image/4.5-*` and `flux/kontext` remain open, documented as unresolved in `KIE_MARKET_UPSTREAM_MODEL_IDS`'s comment pending further verification. ([#11667](https://github.com/diegosouzapw/OmniRoute/pull/11667)) +- **fix(dashboard):** `useApiKeySave.handleSaveApiKey` no longer forces a full upstream `/models` catalog sync on every non-curated provider connection save — callers can now pass `skipModelSync: true` to opt out, so a workflow that only wants to add one manual model no longer floods the provider's available-models list with hundreds/thousands of synced entries. The flag is a client-side intent signal only and is stripped before the connection payload is POSTed to `/api/providers`; default behavior (full sync on save) is unchanged when the flag is omitted (#11324) ([#11664](https://github.com/diegosouzapw/OmniRoute/pull/11664)) +- **Provider connections:** keep `tokenExpiresAt` when a connection is created. The create-path allowlist omitted it, so every insert stored NULL and the dashboard token badge could read a fresh connection as expired until its first background refresh ([#11368](https://github.com/diegosouzapw/OmniRoute/pull/11368)). — thanks @ntdatt812 +- **fix(combos):** the combo builder's precision-select, global-model-search, and manual-entry flows now serialize a model step's `model` string using the provider's already-computed routing-alias prefix (e.g. `oc/`) instead of rebuilding it from the raw canonical `providerId`, fixing the no-auth "OpenCode Free" provider (`opencode`) being routed to the unrelated paid "OpenCode Zen" provider (`opencode-zen`) because `opencode` doubles as a manual routing-prefix override ([#11433](https://github.com/diegosouzapw/OmniRoute/issues/11433)). ([#11670](https://github.com/diegosouzapw/OmniRoute/pull/11670)) +- **fix(radar):** the catalog feed cache now keeps `generatedAt`, the date the feed's data was built, next to `fetchedAt`, the date this install downloaded it (#11435). The feed schema requires that date and the sync path validates it, but the cache dropped it — so a feed fetched minutes ago and one carrying weeks-old figures looked identical to everything downstream, including the dashboard's "Last fetched" line. `getRadarCatalog().meta` and `GET /api/radar/status` now report both dates, the latter as its own field rather than folded into `version` — and omitted entirely for the offers and intel caches, which keep no build date, where a `null` would read as "unknown" rather than "never stored". The dashboard still shows only the fetch time; surfacing the build date there needs a new translated label and is left to a follow-up. Rows cached before migration 163 read back as `null`: unknown stays unknown instead of borrowing the fetch time. The referrals cache has persisted the same date since migration 142. — thanks @maxmad64bis +- **fix(cli):** postinstall no longer fills `JWT_SECRET` and `API_KEY_SECRET` in the installed package's `.env` (#11436). `.env.example` ships both blank on purpose: the server restores them from its durable store, or generates and persists them there, in `ensureSecrets()`. Pre-filling them defeated that — the file lives inside the package directory, so `npm i -g` replaced it and postinstall wrote _different_ values, while `ensureSecrets()` (which only acts on an empty variable) never got to restore the real ones. Both secrets rotated silently on every update, invalidating dashboard sessions and API-key CRCs. `STORAGE_ENCRYPTION_KEY` left the same list for the same reason in #1622; its comment pointed at a function that no longer exists and now names the real provisioning path. — thanks @maxmad64bis +- **fix(cli):** the CLI now says when a loaded `.env` lives inside the installed package directory (#11437). It already announces every env file it reads, without distinguishing the ones that survive an update from the one that does not: `npm i -g` replaces the package directory wholesale, so values set there are gone at the next update, silently. The warning names the durable path to move them to, and fires only when that file actually supplied a value — a file entirely shadowed by a durable one supplied nothing. A development checkout stays silent: there the same path is stable and documented in `SETUP_GUIDE.md`. — thanks @maxmad64bis +- **fix(free-models):** the shared `isFreeModel()` predicate no longer reports catalog entries marked `freeType: "discontinued"` as free, so `hidePaidModels` can't route to Pollinations' seven premium models that now require a paid key ([#11441](https://github.com/diegosouzapw/OmniRoute/pull/11441)) — thanks @maxmad64bis +- fix(ui): let AnySearch use the normal provider-icon fallback when LobeHub has no matching icon (#11449) +- Fixed the v3.8.50 Costs and Analytics dashboards so flat-rate Claude Code usage can be shown as an explicitly requested token-price estimate without changing default billed-cost semantics. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed archived usage retention so each request is priced individually instead of pricing a day's summed tokens once, which understated archived cost whenever a day mixed cache-heavy and ordinary requests. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed the Costs dashboard so it discloses when displayed figures include flat-rate token-price estimates instead of labelling them as billed spend, using the flag the analytics API already returns; the month-end projection and the CSV/JSON exports carry the same marker, and billed-cost mode is unchanged. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- **fix(combo):** attach the same combo-diagnostics trace (`poolSize`/`attemptOrder`/`excluded`/`terminalReason`, plus `x-omniroute-combo-*` headers) to the round-robin strategy's and the nested pipeline/fusion runtime-unit loop's "Maximum combo retry limit reached" 503 that the priority-strategy path already attaches for the identical terminal condition — previously those two paths returned a bare, contextless 503 ([#11462](https://github.com/diegosouzapw/OmniRoute/issues/11462)). ([#11665](https://github.com/diegosouzapw/OmniRoute/pull/11665)) +- **fix(bun):** use native `bun:sqlite` in `bootstrap-env` and `sync-env` to avoid loading `better-sqlite3` N-API addon during Bun startup ([#11468](https://github.com/diegosouzapw/OmniRoute/pull/11468)) — thanks @TheDemonTuan +- **fix(docker-bun):** make `Dockerfile.bun` install reliable with pre-install script helpers and native `bun:sqlite` smoke check ([#11470](https://github.com/diegosouzapw/OmniRoute/pull/11470)) — thanks @TheDemonTuan +- **fix(docker-bun):** remove every vendored `better-sqlite3` native addon from the Bun runtime image so startup cannot abort before the `bun:sqlite` fallback ([#11482](https://github.com/diegosouzapw/OmniRoute/pull/11482)) — thanks @TheDemonTuan +- **fix(autoCombo):** effort/alias model variants (`gpt-5.6-sol-xhigh`, `gpt-5.6`, cursor's `claude-4.6-opus-high`) inherit their base model's task fitness instead of falling to the wildcard 0.5 ([#11492](https://github.com/diegosouzapw/OmniRoute/pull/11492)) — thanks @MumuTW +- fix(db): rate-limit repeated Arena ELO leaderboard fetch-failure warnings instead of logging one per sync attempt (#11500) ([#11989](https://github.com/diegosouzapw/OmniRoute/pull/11989)) — thanks @hartmark +- **fix(db):** dedupe the raw `[Encryption] Decryption failed...` log line emitted by the lazy-decrypt views (`createLazyRowProxy`/`createLazyConnectionView`), which power `getProviderConnections()` and were re-triggering that line on every CredentialHealth/model-sync cycle for the same corrupt or stale-key credential — a fresh Proxy over a fresh row on every cycle meant the per-proxy memoization never suppressed it, unlike the dedup `decryptConnectionFields()` already had since [#9927](https://github.com/diegosouzapw/OmniRoute/issues/9927). Now shares that dedupe tracking so the line logs at most once per credential ([#11500](https://github.com/diegosouzapw/OmniRoute/issues/11500)). ([#11678](https://github.com/diegosouzapw/OmniRoute/pull/11678)) +- **fix(intelligence):** Arena ELO sync stores a synthesized base-model row for every effort/harness variant (`claude-opus-5-max` → `claude-opus-5`, `gpt-5.6-sol-xhigh (codex-harness)` → `gpt-5.6-sol`), so bare model requests reach the synced intelligence layer instead of the static fallback table; the hand-maintained `MODEL_ALIAS_MAP` — which collapsed generations (`gpt-5.5` → `gpt-5`) — is gone ([#11506](https://github.com/diegosouzapw/OmniRoute/pull/11506)) — thanks @MumuTW +- **fix(autoCombo,sse):** catalog hygiene — the static task-fitness table no longer ranks retired models above live ones (dead rows dropped, patterns matched on segment boundaries), the 7 deprecation aliases that forwarded to retired or non-existent models now point at the vendors' published replacements, an alias is no longer rewritten when the serving provider offers the id as-is, and a new `check:model-lifecycle` gate diffs both tables against a vendor lifecycle snapshot ([#11507](https://github.com/diegosouzapw/OmniRoute/pull/11507)) — thanks @MumuTW +- **fix(sse):** stop dropping resolved `thoughtSignature` values on parallel (multi tool-call) turns sent to Gemini 3.x — the claude→gemini and openai→gemini translators previously kept the signature only on the *first* function call of a message, causing Gemini to reject subsequent calls in the same turn with HTTP 400 "Function call is missing a thought_signature"; each function call now keeps its own resolved signature ([#11510](https://github.com/diegosouzapw/OmniRoute/issues/11510)). ([#11669](https://github.com/diegosouzapw/OmniRoute/pull/11669)) +- Treat Docker publishing from a non-default release branch as an expected skip instead of a failed workflow, while preserving the guard that prevents it from overwriting the `next` channel. ([#11527](https://github.com/diegosouzapw/OmniRoute/pull/11527)) — thanks @pacocartones +- fix(search): prefer credentialed providers over duckduckgo-free fallback (#11524) — the fallback-only loop ran before the credentialed-providers loop in `executeWebSearch`, making configured providers unreachable when `duckduckgo-free` was available. ([#11565](https://github.com/diegosouzapw/OmniRoute/pull/11565)) — thanks @vsd2807 +- **fix(sse):** cap the upstream headers-wait phase for STREAMING requests to a client-realistic ceiling (110s, under Codex's own ~120s hard client-abort window) instead of the flat 10-minute `FETCH_TIMEOUT_MS` default — that default was 5x longer than the body-phase readiness watchdog's own adaptive bound, so a request whose upstream never returned any response at all (not even headers, e.g. a stalled NVIDIA target behind a tool-heavy Responses→Chat translation) kept the client connection alive on keepalives only, guaranteeing the client's own patience ran out first with an opaque 499 instead of OmniRoute detecting and failing the stall fast. Non-streaming requests are unaffected — they keep the existing flat default (`open-sse/utils/fetchStartTimeoutPolicy.ts`) (#11526) ([#11668](https://github.com/diegosouzapw/OmniRoute/pull/11668)) +- **fix(ci):** protocol-clients E2E harness boots the peer-stamped custom server (`run-next.mjs dev`) instead of the bare `next` CLI, so `LOCAL_ONLY` locality resolves from the real TCP peer stamp and `/api/mcp/audit` answers 200 (open bootstrap preserved via an `OMNIROUTE_E2E_BOOTSTRAP_MODE=open` credential clear in `run-next.mjs`, pinned to loopback `HOST`) instead of a deterministic 403 (#11535) ([#11549](https://github.com/diegosouzapw/OmniRoute/pull/11549)) — thanks @jonlwheat2-gif +- **fix(docs):** the Auto-Combo engine is described with the number of factors it actually declares. Four different counts were in circulation — 6, 9, 13 and 14 across `AGENTS.md`, four guides and six code comments — while `DEFAULT_WEIGHTS` declares 15. `check:docs-counts` now reads that number from the source, so adding a factor without updating the prose fails CI ([#11545](https://github.com/diegosouzapw/OmniRoute/pull/11545)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **fix(quota-share):** in-flight leases are per request, so an aborted request's slot ages out instead of being kept alive by later traffic on the same connection ([#11547](https://github.com/diegosouzapw/OmniRoute/pull/11547)) — thanks @abhisheksharma2411 +- The free-tier summary route now serves the refreshed Radar catalog when the feed is active — the same numbers the dashboard shows — and states which catalog answered plus its real build date, instead of always reporting release-frozen figures with a stale curation date. The supporter-key live feed stays reserved to authenticated callers of the instance. ([#11550](https://github.com/diegosouzapw/OmniRoute/pull/11550)) — thanks @maxmad64bis +- **sse:** fix LiveWS/embed-WS servers crashing at startup under the Node/tsx runtime — `liveServer.ts`, `embedWsProxy.ts` and `apiBridgeServer.ts` imported `@/shared/utils/httpClientAbortGuard` without the `.mjs` extension, so the client-abort crash guard added by [#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556) was unreachable and every dependent test failed with `ERR_MODULE_NOT_FOUND` ([#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556)). ([#11671](https://github.com/diegosouzapw/OmniRoute/pull/11671)) — thanks @jonlwheat2-gif / @yxyxy +- fix(chat): shed any of the matched provider-node's routing identifiers (public prefix or internal id) before model lookup, so composites like `//` no longer reach upstream verbatim (#11557) — thanks @maxmad64bis +- **fix(cache):** a semantic-cache entry now expires on its TTL instead of surviving until the next UTC midnight, and `dbEntries` stops counting expired rows ([#11573](https://github.com/diegosouzapw/OmniRoute/pull/11573)) — thanks @ntdatt812 +- **fix(api):** `/v1/models` schedules its stale-while-revalidate rebuild through Next's `after()` again, so a stale catalog reaches the client before the rebuild blocks the event loop ([#11574](https://github.com/diegosouzapw/OmniRoute/pull/11574)) — thanks @ntdatt812 +- **fix(check):** the `known-symbols` executor conformance gate awaits the now-async `getExecutor()`, so it stops reporting all 142 registered aliases as dead symbols and can detect a lazy import that fails ([#11582](https://github.com/diegosouzapw/OmniRoute/pull/11582)) — thanks @ntdatt812 +- **fix(pack):** register `bin/cli/utils/volatileEnvPath.mjs` in `PACK_ARTIFACT_REQUIRED_PATHS`, so the prune can no longer silently drop a module the published CLI imports on every boot ([#11588](https://github.com/diegosouzapw/OmniRoute/pull/11588)) — thanks @ntdatt812 +- **i18n(vi):** translate the five reliability-column strings on the free-provider rankings page, which rendered as `__MISSING__:` markers in the Vietnamese UI ([#11589](https://github.com/diegosouzapw/OmniRoute/pull/11589)) — thanks @ntdatt812 +- **resilience:** restore the expired-connection retry-budget probe in the token-health sweep — the `!isGitHubAccessTokenOnlyConnection` carve-out reintroduced by #11608 contradicted the boundary pinned by #11592, so a GitHub connection parked at `expired` with retry budget remaining was never probed and could never self-heal ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)). ([#11672](https://github.com/diegosouzapw/OmniRoute/pull/11672)) — thanks @jonlwheat2-gif / @ntdatt812 +- **fix(dashboard):** Provider Stats now displays combo success rates on their native 0–100 percentage scale instead of multiplying them twice ([#11595](https://github.com/diegosouzapw/OmniRoute/pull/11595)) — thanks @pacocartones +- **fix(providers):** route DuckDuckGo Web Chat through the verified `duck.ai` endpoint, refresh its free model catalog, and avoid forwarding unsolved anti-abuse challenges ([#11597](https://github.com/diegosouzapw/OmniRoute/pull/11597)) — thanks @solstxce +- **fix(ui):** Console log Refresh and Copy controls now expose localized accessible names, keep copy actions visible on keyboard focus, and announce copy completion safely ([#11599](https://github.com/diegosouzapw/OmniRoute/pull/11599)) — thanks @pacocartones +- Dropped the stale-`.eslintcache` `restore-keys` fallback from both "Restore ESLint file cache" steps in `ci.yml`, so the blocking `Lint` job can no longer be served per-file verdicts computed under a different lint config, suppressions file or lockfile. `quality.yml` had already dropped it in #11963; `ci.yml` — the workflow that actually gates PRs — had not (#11600). ([#11996](https://github.com/diegosouzapw/OmniRoute/pull/11996)) +- fix(ci): pass `--pass-on-unpruned-suppressions` in `run-eslint-json.mjs` so the CI Lint job no longer fails when a suppression is merely orphaned by a genuine fix, mirroring the identical fix already in `validate-release-green.mjs` (#11600) ([#11983](https://github.com/diegosouzapw/OmniRoute/pull/11983)) — thanks @hartmark +- fix(handoff): enforce provider allowlist for universal handoff (#11602) — universal handoff now skips summarization when the selected summary model's provider is not included in the configured provider allowlist. ([#11619](https://github.com/diegosouzapw/OmniRoute/pull/11619)) — thanks @vsd2807 +- **fix(dashboard):** Show a stable error state when Search Analytics returns an HTTP error or malformed data ([#11603](https://github.com/diegosouzapw/OmniRoute/pull/11603)) — thanks @pacocartones +- **test(gamification):** pin the aggregate profile level to the XP-derived semantics of #11604 — `getAggregateXp()` now derives `currentLevel` from the summed XP (`calculateLevel(sum)`), not `MAX(stored current_level)`, and the #3484 fixture levels are aligned with the XP curve ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)). ([#11676](https://github.com/diegosouzapw/OmniRoute/pull/11676)) — thanks @jonlwheat2-gif / @pacocartones +- **fix(dashboard):** Keep the Profile level and progress aligned with aggregate XP, including bounded handling for invalid totals ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)) — thanks @pacocartones +- **fix(dashboard):** Prevent locked hidden badges from revealing their icon or opening private badge details before they are earned ([#11605](https://github.com/diegosouzapw/OmniRoute/pull/11605)) — thanks @pacocartones +- **fix(dashboard):** Restore keyboard focus after shared modals close and cancel delayed autofocus during cleanup ([#11607](https://github.com/diegosouzapw/OmniRoute/pull/11607)) — thanks @pacocartones +- **fix(dashboard):** Enable Enter and Space activation for clickable data-table rows without hijacking nested controls ([#11610](https://github.com/diegosouzapw/OmniRoute/pull/11610)) — thanks @pacocartones +- **fix(autoCombo,sse):** vendor-retired catalog ids are dropped from the auto-combo candidate pool and no longer win on leftover `arena_elo` / `user_override` rows; `getModelLifecycleDecision` consults `model-lifecycle.json` (prefix-stripped) so aggregator traffic is not `untracked` for ids the snapshot already knows ([#11625](https://github.com/diegosouzapw/OmniRoute/issues/11625)) ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)) — thanks @MumuTW +- **test(chatcore):** move Codex/Claude combo fixtures off models the lifecycle guard now rejects — `gpt-5.1-codex`/`gpt-5-codex` are vendor-retired (snapshot, #11626) and `claude-3-5-sonnet-20241022` is shut down, so native-passthrough and combo-fallback tests switched to `gpt-5.6-sol` and `claude-sonnet-4.6` ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)). ([#11675](https://github.com/diegosouzapw/OmniRoute/pull/11675)) — thanks @jonlwheat2-gif / @MumuTW +- fix(opencode-plugin): stop a free-tier budget that rounds up across a magnitude from rendering as `1000K`/`1000M` in the model picker — `fmtTokens` chose its unit from the raw token count and then rounded with `toFixed(1)`, so 999,950–999,999 printed as `1000K` rather than `1M` and just under a billion printed as `1000M` rather than `1B` ([#11684](https://github.com/diegosouzapw/OmniRoute/pull/11684)) — thanks @abhisheksharma2411 +- Cap prose-derived Antigravity quota resets at the configured model cooldown maximum while preserving authoritative Retry-After headers and Google RetryInfo hints. (#11695) Thanks @pacocartones. ([#11823](https://github.com/diegosouzapw/OmniRoute/pull/11823)) +- Compare `_rebaseline_*` notes structurally in the ratchet-bank verifier so an object-valued note no longer reports itself as rewritten on an unmodified tree — the nightly `bank-ratchet-shrinks` lane had been aborting on every run since 2026-08-11, leaving 41,672 lines of earned file-size shrinks unbanked (#11702). ([#11892](https://github.com/diegosouzapw/OmniRoute/pull/11892)) — thanks @MumuTW +- **fix(build):** `prepublish.ts` bundles the ChatGPT Web (Codex) MCP bridge through `runBuildTool()` instead of spawning `npx.cmd` raw, fixing the build crash on Node ≥ 20/Windows where `.cmd` shims cannot be spawned without a shell (EINVAL) ([#11704](https://github.com/diegosouzapw/OmniRoute/issues/11704)) ([#11706](https://github.com/diegosouzapw/OmniRoute/pull/11706)) — thanks @jonlwheat2-gif +- fix(codex): keep `parallel_tool_calls:false` on the translated Codex Responses Lite path (#11707) ([#11984](https://github.com/diegosouzapw/OmniRoute/pull/11984)) — thanks @hartmark +- **fix(tests):** the new Gemini Web Images retirement test (#11708) called the now-async `getExecutor()` synchronously, throwing `TypeError: getExecutor(...).getProvider is not a function` instead of asserting Gemini Web chat stays available. Awaited to match the R0.3 executor-registry refactor (#11220). ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(search):** Allow REST and MCP web-fetch requests to select the advertised `anysearch-search` provider explicitly ([#11716](https://github.com/diegosouzapw/OmniRoute/pull/11716)) — thanks @pacocartones +- **fix(providers):** Keep overnight peak-hour protection active after midnight according to the window's configured start day ([#11718](https://github.com/diegosouzapw/OmniRoute/pull/11718)) — thanks @pacocartones +- **fix(providers):** correct all five g4f.space gateway descriptions to document proof-of-work credits or member API keys, variable limits, and the remote prompt boundary instead of promising free keyless access at five requests per minute ([#11738](https://github.com/diegosouzapw/OmniRoute/pull/11738)). +- **fix(db):** `model_capabilities` is created by a migration instead of lazily on the first models.dev sync, so a clean install and an upgraded install converge on the same schema regardless of which features have run ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(ci):** `check:install-upgrade` now fails on an `npm` install truncated by ENOSPC (npm reports it as a warning and still exits 0), authenticates its health probe so the version assertion works against the hardened health payload, frees the clean-install tree before the upgrade phase, and no longer reports a schema divergence computed from a boot that never served ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(routing):** harden session-affinity key derivation against oversized explicit IDs and empty structural-payload collisions, while recognizing Gemini and common text request shapes ([#11746](https://github.com/diegosouzapw/OmniRoute/pull/11746)) — thanks @MumuTW +- **fix(packages/browser-pool):** regenerate `package-lock.json` so the `packages/browser-pool` workspace's locked `playwright`/`@types/node` (and transitives) match its `package.json` specs, fixing cache-only/offline installs (`npm ci --offline`, Nix `buildNpmPackage`) that previously failed with `ENOTCACHED` ([#11747](https://github.com/diegosouzapw/OmniRoute/issues/11747)) — thanks @benjaminkitt ([#11784](https://github.com/diegosouzapw/OmniRoute/pull/11784)) — thanks @geekyNads +- **fix(assets):** Stop distributing 79 provider and CLI logos without verified source/license provenance, keep every provider/tool available through the internal generic icon, and prevent those IDs from falling through to an external icon CDN. ([#11750](https://github.com/diegosouzapw/OmniRoute/pull/11750)) +- **fix(providers):** retire the common `chatgpt-web` / `cgpt-web` provider because its pre-key and proof-of-work provenance could not be cleared ([#11754](https://github.com/diegosouzapw/OmniRoute/pull/11754)) — explicit chat and image requests, slash-prefixed models, persisted aliases, proxy overrides, and combo targets now fail closed with a sanitized HTTP `410` / `PROVIDER_RETIRED` before upstream dispatch. Migration 163 tombstones matching connections and invalidates their active leases without deleting history or widening API-key allowlists. The independent `chatgpt-web-codex` / `cgpt-codex` provider and its MIT-noticed vendored implementation remain available. +- **fix(usage):** quota and usage refresh no longer 409 when an exclusive lease reserves the connection ([#11758](https://github.com/diegosouzapw/OmniRoute/pull/11758)) — thanks @TheDemonTuan +- Keep the embedding registry's vector width and `embedding` type on models when a synced model exists for the same id, so `/v1/models` no longer reports registry-described embedding models widthless or untyped (#11761) — thanks @rahilmavani +- Correct `google/gemini-embedding-001` on the OpenRouter route to 3072 dimensions, the width it returns when `dimensions` is not sent (#11761) — thanks @rahilmavani +- **fix(vision-bridge):** Restrict automatic vision routing and fallbacks to models available in authoritative live provider catalogs, including aliases and effort variants ([#11767](https://github.com/diegosouzapw/OmniRoute/issues/11767)) — thanks @pacocartones ([#11813](https://github.com/diegosouzapw/OmniRoute/pull/11813)) +- **fix(providers):** reclassify Cerebras as a one-time $5 signup credit (payment method required, 30-day validity), not a recurring no-card 1M tokens/day trial ([#11773](https://github.com/diegosouzapw/OmniRoute/issues/11773)) ([#12591](https://github.com/diegosouzapw/OmniRoute/pull/12591)) — thanks @HouMinXi +- fix(cli): stop prepublish from re-rebuilding the already-built ESM-only opencode-plugin dist (#11787) ([#11990](https://github.com/diegosouzapw/OmniRoute/pull/11990)) — thanks @hartmark +- **fix(db):** Fresh installs no longer log a non-fatal `no such table: compression_run_telemetry` warning when retention cleanup runs before the lazily-created telemetry table exists ([#11802](https://github.com/diegosouzapw/OmniRoute/pull/11802)) — thanks @RaviTharuma +- fix(sse): set X-OmniRoute-Selected-Connection-Id on successful combo dispatches so downstream consumers stop falling back to an empty connection id (#11810) ([#11986](https://github.com/diegosouzapw/OmniRoute/pull/11986)) — thanks @hartmark +- **fix(services):** embedded CLIProxyAPI lifecycle checks now use public `/healthz`, while model discovery uses the configured dedicated data-plane API key instead of the management password ([#11811](https://github.com/diegosouzapw/OmniRoute/pull/11811)) — thanks @RaviTharuma +- Fix persisted model steps returning an error in the playground route simulator, and warn when structural steps cannot be simulated. ([#11862](https://github.com/diegosouzapw/OmniRoute/pull/11862)) — thanks @pacocartones +- **fix(providers):** Antigravity's dynamic mitmAlias table no longer routes `gemini-3.7-flash-{high,medium,low}` to a literal tier-suffixed upstream id just because one connected account's own discovery listed it directly — those display ids always resolve through the safe `gemini-3.7-flash-tiered` static alias, so one account's Google-provisioned access no longer 404s every sibling account of the provider ([#11824](https://github.com/diegosouzapw/OmniRoute/issues/11824), [#11651](https://github.com/diegosouzapw/OmniRoute/issues/11651)) ([#11988](https://github.com/diegosouzapw/OmniRoute/pull/11988)) — thanks @hartmark +- **fix(dashboard):** Expose Profile loading, errors, page structure, and XP progress to assistive technologies ([#11838](https://github.com/diegosouzapw/OmniRoute/pull/11838)) — thanks @pacocartones +- **fix(socks):** `Agent.connectTimeout` now bounds both SOCKS handshake and TLS connect, and `family === null` no longer falls back to `fetch-socks` ([#11842](https://github.com/diegosouzapw/OmniRoute/pull/11842)) — thanks @maxmad64bis +- **perf(compression):** OOM mitigations for large payload hashing, memoization, and token estimation ([#11844](https://github.com/diegosouzapw/OmniRoute/pull/11844) — thanks @AndrianBalanescu) +- Excluded Next.js Node File Trace manifests (`*.nft.json`) from the published npm tarball. They are build-time metadata and are never read while serving, but had grown to 668.7 MB — 61% of the package — which pushed the upload past the registry limit and made `npm publish` fail with `413 Payload Too Large`. ([#11864](https://github.com/diegosouzapw/OmniRoute/pull/11864)) +- **fix(cli):** keep the global OmniRoute server `--base-url` separate from provider-node `--endpoint` values in add, update, and validate commands ([#11860](https://github.com/diegosouzapw/OmniRoute/pull/11860)) — thanks @pacocartones +- **fix(config):** Nous Research's `Hermes-4-405B` model now displays as "Hermes 4 405B (Nous Research)" in both the provider registry and the free-model catalog, instead of the mislabelled "Hermes 4 7B" ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#11993](https://github.com/diegosouzapw/OmniRoute/pull/11993)) +- **fix(provider/nous):** inject required user= tag into Nous Research inference requests to resolve upstream 400 "missing tags" error ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#12044](https://github.com/diegosouzapw/OmniRoute/pull/12044)) +- **fix(build):** `npm run build` now fails in one second with a named package and a copy-pasteable fix when npm silently drops an externalised optional native dependency, instead of dying four minutes in with `Module not found: Can't resolve 'better-sqlite3'` ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- **fix(install):** `postinstall` no longer throws `ReferenceError: isAndroid is not defined` — failing the whole `npm install` — when the `better-sqlite3` rebuild fallback times out; the manual-fix guidance is reachable again ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- Fixed the Alibaba free-tier allowlist test that went red on its own once the shipped catalog's `validUntil` (2026-08-27) passed, leaving every PR and `main` with a failing `Unit Tests (1/8)`. The test now builds its own packs with dates it controls, and covers the expired-pack fallback that production has actually been serving. ([#11867](https://github.com/diegosouzapw/OmniRoute/pull/11867)) +- Split the npm registry upload into its own GitHub-hosted job. npm refuses `--provenance` from a self-hosted runner (`422 ... Only "github-hosted" runners are supported`), which blocked the v3.8.50 publish; the heavy verification cannot move to a hosted runner, so it now hands the proven tarball over instead. ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(ui):** the shared `DataTable` loading state no longer reads its decorative ⏳ glyph out to assistive technology, and now carries the same `role="status"` / `aria-live="polite"` / `aria-busy="true"` semantics as `PageLoading` ([#11869](https://github.com/diegosouzapw/OmniRoute/pull/11869)) — thanks @pacocartones +- **fix(api):** Bound the `timeout` accepted by `POST /api/providers/[id]/login` to the same 15s–600s window the Adobe Firefly and Conol browser-login paths already enforce, so a request can no longer pin a headful browser and the single active-login slot with an arbitrarily large poll budget. ([#11871](https://github.com/diegosouzapw/OmniRoute/pull/11871)) — thanks @pacocartones +- **fix(api):** Stop the tunnel and MITM routes from returning raw child-process error text, which disclosed host paths, binary install locations and Tailscale `tskey-*` credentials in a body some of these routes serve to non-loopback callers, and return a real 400 instead of a framework 500 when a tunnel request body fails validation. ([#11872](https://github.com/diegosouzapw/OmniRoute/pull/11872)) — thanks @pacocartones +- **fix(open-sse):** stream TTFT/ITL are sampled from a monotonic clock (`performance.now`) instead of `Date.now`, so an NTP correction or wall-clock jump can no longer inflate a genuine TTFT or produce a negative one that the `ttft >= 0` guard silently drops — protecting the router's OTel/EWMA/`usage_history`/speed-ranking signals ([#11873](https://github.com/diegosouzapw/OmniRoute/pull/11873)) — thanks @pacocartones +- **fix(providers):** xAI `reasoning_effort: "xhigh"` now reaches grok-4.6+ instead of being silently clamped to `"high"` ([#11879](https://github.com/diegosouzapw/OmniRoute/pull/11879)) — thanks @NoxzRCW +- **fix(dashboard):** the providers page no longer crashes into the error boundary when a provider id collides with an `Object.prototype` member (`constructor`, `__proto__`); icon lookups are own-property guarded ([#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)) — thanks @NoxzRCW +- **fix(skills):** injected skill tools declared in shorthand (`{"content": "string"}`) now forward valid JSON Schema, unblocking providers that validate tool schemas strictly such as Zhipu GLM on the Console Go tier ([#11881](https://github.com/diegosouzapw/OmniRoute/pull/11881)) — thanks @NoxzRCW +- **fix(api):** `POST /api/playground/simulate-route` now surfaces `combo-ref` and `provider-wildcard` persisted combo steps with a specific warning (naming the referenced combo, or the unresolved `provider/model` wildcard) instead of folding them into a generic "unsupported step" count; a `provider-wildcard` step is also now included as an unresolved target so the operator can see it is in the route (ported from [#11882](https://github.com/diegosouzapw/OmniRoute/pull/11882) — thanks @NoxzRCW). ([#11930](https://github.com/diegosouzapw/OmniRoute/pull/11930)) +- **fix(translator):** the streaming OpenAI→Claude translator keeps upstream usage, including prompt-cache tokens, when it arrives on a trailing `choices: []` chunk (Fireworks and any upstream using `stream_options.include_usage`) ([#11883](https://github.com/diegosouzapw/OmniRoute/pull/11883)) — thanks @NoxzRCW +- **fix(cli):** `omniroute update --apply` now tells you explicitly whether a running server was detected and, if so, that you must run `omniroute restart` to apply the update — it never restarted anything and previously implied the update was already live once files were installed. The dashboard's npm-mode Update flow (`/api/system/version`) now tries OmniRoute's own PID-file-managed supervisor before falling back to pm2, and reports an honest "restart required" step instead of a silent pm2-only "skipped" that read like a completed update. The server-side latest-version lookup backing the dashboard's update banner also gained `--prefer-online`, closing the same stale-npm-cache class already fixed in the CLI's own copy for #4376 ([#11885](https://github.com/diegosouzapw/OmniRoute/issues/11885)). ([#12005](https://github.com/diegosouzapw/OmniRoute/pull/12005)) +- **fix(resilience):** clear persisted LKGP pins when a target suffers connection/provider exhaustion or is skipped before dispatch due to cooldown/exhaustion/unavailability, preventing subsequent requests from repeatedly prioritizing known-dead providers ([#11911](https://github.com/diegosouzapw/OmniRoute/issues/11911)). ([#12013](https://github.com/diegosouzapw/OmniRoute/pull/12013)) — thanks @HouMinXi +- **fix(sse):** the OpenAI→Claude stream translator now defers the terminal `message_delta`/`message_stop` emission until the real usage block has arrived (or a genuine end-of-stream flush forces it) instead of emitting it immediately on `finish_reason` — previously, when the trailing usage-only chunk (`{"choices":[],"usage":{...}}`) arrived *after* the `finish_reason` chunk (the normal order for Fireworks/vLLM/Together and other `stream_options.include_usage` upstreams), the client-visible `message_delta` still carried stale/zero usage even though `state.usage` was internally corrected too late to matter (ported from [#11915](https://github.com/diegosouzapw/OmniRoute/pull/11915) — thanks @HouMinXi). ([#11933](https://github.com/diegosouzapw/OmniRoute/pull/11933)) +- **fix(sse):** `stripStore()` now forces `store=false` for stateless OpenAI-compatible Responses-API targets (unless the connection has explicitly opted in via `providerSpecificData.openaiStoreEnabled`), instead of only handling the `openai`/`agentrouter` cases — a client-supplied `store` value previously passed through untouched to backends that don't actually persist responses server-side ([#11916](https://github.com/diegosouzapw/OmniRoute/pull/11916) — thanks @HouMinXi). +- **fix(models):** custom provider-node models (synced, custom, and alias-backed) now appear under their configured prefix in the unified catalog when the operator's model-id prefix mode is canonical, instead of being dropped whenever alias-inclusion was otherwise disabled ([#11918](https://github.com/diegosouzapw/OmniRoute/pull/11918) — thanks @HouMinXi). +- **fix(models):** the unified model catalog now suppresses stale static registry models (including effort-tier variants) for any provider whose active connection has an authoritative live synced catalog, not only providers already using exclusive-synced-listing — a connection with `providerUsesAuthoritativeLiveCatalog` previously kept serving both the live-synced models and the stale static rows side by side ([#11919](https://github.com/diegosouzapw/OmniRoute/pull/11919) — thanks @HouMinXi). +- **fix(ui):** `ProviderIcon`'s three remaining unguarded lookups (`PROVIDER_ICON_ALIASES`, `LOCAL_SVG_ALIASES`, `THEMED_SVGS`) now use `Object.hasOwn()` own-property checks — a provider id such as `constructor` or `__proto__` previously resolved through the prototype chain instead of falling through to the unknown-provider CDN fallback (`getLobeProviderIcon()` itself was already guarded by [#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)); `ProviderPageHeader`'s `color` field is also now optional, matching the rest of the component's defensive typing (ported from [#11920](https://github.com/diegosouzapw/OmniRoute/pull/11920) — thanks @HouMinXi). ([#11935](https://github.com/diegosouzapw/OmniRoute/pull/11935)) — thanks @NoxzRCW +- fix(ollama): preserve multi-byte UTF-8 content split across stream chunks in the Ollama NDJSON transform, which previously corrupted CJK/emoji into U+FFFD (#11921) — thanks @pacocartones +- **fix(providers):** OrcaRouter chat requests now target `/v1/chat/completions` instead of the bare `/v1` API root, fixing the upstream `404 Invalid URL (POST /v1)` ([#11923](https://github.com/diegosouzapw/OmniRoute/pull/11923)). — thanks @echel0nn +- **fix(plugins):** deliver the `onStreamComplete` event to disk-installed plugins. The event shipped in v3.8.50 (#9669) was emitted internally but had no plugin-facing wiring, so no plugin could ever subscribe: the manifest schema silently dropped `hooks.onStreamComplete`, and the loader/manager only knew the seven legacy hooks. `onStreamComplete` is now a declarable manifest hook, wired through the loader and registered by the manager like the other hooks, and its payload carries a `requestId` so consumers can correlate the stream-completion event with the originating request ([#11934](https://github.com/diegosouzapw/OmniRoute/pull/11934)) — thanks @amaleta / @pacocartones +- **fix(db):** the Qdrant embedding-model dropdown now lists local/self-hosted providers (Ollama, LM Studio, vLLM, etc.) — an active connection is treated as "configured" when the provider allows an optional API key, not only when it has a real key or OAuth, so a running local embedding provider is no longer hidden from the picker ([#11949](https://github.com/diegosouzapw/OmniRoute/issues/11949)) ([#11995](https://github.com/diegosouzapw/OmniRoute/pull/11995)) +- **fix(providers):** Vertex AI Anthropic partner-model discovery now calls the Model Garden `v1beta1` publisher list (`/v1beta1/publishers/anthropic/models`, global) and parses its `publisherModels` envelope, so Claude models auto-synced from Vertex populate the active live catalog and route at request time instead of returning `Model '' is not available in the active live catalog` ([#11991](https://github.com/diegosouzapw/OmniRoute/issues/11991)) — thanks @fabioluissilva ([#11998](https://github.com/diegosouzapw/OmniRoute/pull/11998)) +- **fix(cli):** support `--base-url` alongside `--endpoint` in `omniroute nodes add`, `update`, and `validate` subcommands to prevent global `--base-url` shadowing issues ([#11999](https://github.com/diegosouzapw/OmniRoute/issues/11999)). ([#12033](https://github.com/diegosouzapw/OmniRoute/pull/12033)) — thanks @HouMinXi +- **fix(providers):** `cloudflare-ai` no longer refuses image content parts for every Workers AI model ([#12002](https://github.com/diegosouzapw/OmniRoute/pull/12002)) — the plain-string `content` requirement behind #2539 is carried by the _model_ schema, not by the `/ai/v1/chat/completions` endpoint (measured: an all-text part array returns 200 on `@cf/mistralai/mistral-small-3.1-24b-instruct`, `@cf/meta/llama-4-scout-17b-16e-instruct` and `@cf/meta/llama-3.3-70b-instruct-fp8-fast`, and 400 on the text-only `@cf/qwen/qwen2.5-coder-32b-instruct`). `transformRequest()` flattened every array and threw on the first non-text part (#6390), so image input was refused for vision-capable Cloudflare models that accept it. All-text arrays are still flattened — the one shape every model accepts — while an array carrying a non-text part is passed through untouched, so the attachment is still never silently dropped. Regression guards: `tests/unit/cloudflare-ai-image-parts-6390.test.ts`. — thanks @davidlinfr +- **fix(resilience):** decouple the limiter-managed execution backstop from the queue-wait budget — new `requestQueue.executionMaxWaitMs` (env `RATE_LIMIT_EXECUTION_MAX_WAIT_MS`, default 600000 = 10 min) now feeds Bottleneck's post-dispatch `expiration`, while `requestQueue.maxWaitMs` keeps its documented queue-wait semantics. Previously the queue-wait budget doubled as the execution expiration, so legitimate long-running calls on non-incremental gateways (whole generation buffered before the first upstream byte, e.g. Console Go / Command Code tiers serving GLM models) were killed mid-flight at the queue budget with a false 504 `RATE_LIMIT_EXECUTION_TIMEOUT` — the local limiter undercut the provider-aware upstream fetch-start timeouts. The surfaced 504 message now names `requestQueue.executionMaxWaitMs`; the error keeps the #4165 guarantees (disclaims an upstream timeout, preserves the Bottleneck error as `cause`, branded code + trusted provenance, classified request-scoped so combo falls back). A real queue-wait bound (the `Promise.race` around `limiter.schedule()` sketched in #9533) remains future work. (#12025) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **Call logs:** keep the `error` field when an artifact exceeds the storage cap, instead of replacing it with the omission marker. The error is the only field that says *why* a request failed and is typically ~90 bytes next to the multi-hundred-KB bodies that trip the cap, so dropping it left a size-limited row undiagnosable — a provider outage, a local timeout and an upstream 400 all rendered identically. It is now preserved at every fallback stage, truncated to 4KB if it is itself large ([#12026](https://github.com/diegosouzapw/OmniRoute/issues/12026)). ([#12095](https://github.com/diegosouzapw/OmniRoute/pull/12095)) — thanks @ntdat812 +- **fix(diagnostics):** preserve the error field (truncated to 4KB with a `[truncated: …]` suffix) in every call-log artifact size-limit fallback stage. Previously the minimal fallback replaced the error with `[omitted: call log artifact size limit exceeded]`, so an oversized artifact row showed nothing about WHY the request failed — e.g. 91 of 847 opencode-go 504 rows on one production instance were undiagnosable from the dashboard. Oversized request/response bodies are still omitted exactly as before; the error cap is independent of the payload sizes that tripped the fallback. (#12026) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **fix(sse):** OpenAI Responses clients that declare the native `web_search` tool now receive a spec-shaped `web_search_call` output item with `action.sources` alongside the preserved function-call round-trip, so search results executed through OmniRoute's own search backend are consumable by standard Responses clients (Codex, pi-web-access, …). ([#12031](https://github.com/diegosouzapw/OmniRoute/pull/12031)) — thanks @watchingdogs +- **fix(cli):** use in-thread alias resolver hooks on modern runtimes to avoid deprecation noise and improve Node.js forward compatibility ([#12073](https://github.com/diegosouzapw/OmniRoute/issues/12073)). ([#12083](https://github.com/diegosouzapw/OmniRoute/pull/12083)) — thanks @xiaoyaner0201 +- **fix(combo):** an operator-set **Agent Features → Context length** on a combo is now honored at request time. The value was persisted and advertised through `/v1/models`, but `resolveComboContextLimit()` never consulted it — so a multi-target combo whose members carry no per-model window fell through to the provider's generic `defaultContextLength` (openrouter 128000, command-code 200000) and rejected large requests with `Input exceeds context window … limit 128000` despite the combo being explicitly sized much larger. An identical single-target combo worked, because it collapses to its concrete target before the guard runs. Invalid values (0/negative/NaN/Infinity) are ignored, so the existing target → combo-min → fallback order is unchanged. ([#12090](https://github.com/diegosouzapw/OmniRoute/pull/12090)) — thanks @adivekar-utexas +- **fix(sse):** passthrough streams now estimate usage on finish when upstream closes without usage even with `stream_options.include_usage` — avoids `0 tokens / 0%` for providers that stay silent (and correctly handles trailing empty-choices usage) ([#12151](https://github.com/diegosouzapw/OmniRoute/pull/12151)) — thanks @maxmad64bis +- **fix(combos):** clearing an agent feature in the combos editor now persists — unchecking context cache protection, or emptying the system message or tool filter, sends an explicit `null` instead of dropping the field from the `PUT` body, which the update merge read as "leave unchanged" ([#12177](https://github.com/diegosouzapw/OmniRoute/pull/12177)) — thanks @foreveryh / @ntdatt812 +- **fix(memory):** self-hosted embedding endpoints now vectorize — the vector width is measured from the first embedding that comes back instead of being read from a registry that cannot describe them, so `vec_memories` is created and memories stop piling up unvectorized behind a green health check ([#12180](https://github.com/diegosouzapw/OmniRoute/pull/12180)) — thanks @kanade-hoshino / @ntdatt812 +- **fix(translator):** the leading `system` message now reaches Responses-API upstreams when its `content` is a content-part array — it was read as `typeof content === "string" ? content : ""`, so a prompt-caching client (Anthropic `cache_control`, the shape LiteLLM and the Anthropic SDK emit) had its entire system prompt replaced by an empty `instructions`. The request was still accepted with a normal `prompt_tokens` count, so the model answered with no instructions and nothing in the response said they were missing. Mid-conversation system turns already handled the array shape ([#7056](https://github.com/diegosouzapw/OmniRoute/pull/7056)); only the first one did not ([#12206](https://github.com/diegosouzapw/OmniRoute/issues/12206)). Regression guard: `tests/unit/translator-openai-responses-system-content-parts.test.ts`. ([#12207](https://github.com/diegosouzapw/OmniRoute/pull/12207)) — thanks @Gorillaz322 +- **fix(free-tier):** `/api/free-tier/summary` no longer computes its totals from a Radar feed built before the catalog the running release ships. When the cached feed is older — or carries no build date at all — the route answers from the shipped catalog, resolved through the operator's local model state so disabled and tombstoned models stay out of the numbers ([#12215](https://github.com/diegosouzapw/OmniRoute/pull/12215)). — thanks @maxmad64bis +- **fix(oauth):** Keep a Claude personal workspace and a Team organization as separate connections — they share the same email and `accountUUID`, so the email-only OAuth dedup let the second login overwrite the first account's tokens; `organizationUUID` now disambiguates them, the way `workspaceId` does for Codex ([#12222](https://github.com/diegosouzapw/OmniRoute/pull/12222)) — thanks @davidebaraldo +- **fix(resilience):** a 402 on a single paid model of a passthrough/gateway provider (e.g. `kilo-gateway`, `ollama-cloud`) no longer terminalizes the whole connection with a never-auto-recovered `credits_exhausted` status — only the paid model is locked out, so free models on the same key keep serving. 402 variant of [#3027](https://github.com/diegosouzapw/OmniRoute/issues/3027). Single-credential providers are unaffected — a 402 there is still treated as the key being genuinely out of credit ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239) / [#10616](https://github.com/diegosouzapw/OmniRoute/issues/10616)) ([#12242](https://github.com/diegosouzapw/OmniRoute/issues/12242)) — thanks @brick30llc-ctrl ([#12266](https://github.com/diegosouzapw/OmniRoute/pull/12266)) — thanks @geekyNads +- **fix(sse):** trust `finish_reason: "length"`/`"max_tokens"` over the reasoning-consumed-token ratio in response quality validation, so a reasoning model truncated below the old 90% threshold correctly fails and retries instead of returning empty content as a silent "success" ([#12262](https://github.com/diegosouzapw/OmniRoute/pull/12262)) — thanks @brick30llc-ctrl +- **fix(settings):** `PUT /api/settings/cache-config` now persists `alwaysPreserveClientCache` to the flat general settings the runtime cache-control policy actually reads; previously the value landed in the databaseSettings "cache" section and was silently ignored, so the endpoint had no effect on `cache_control` passthrough ([#12304](https://github.com/diegosouzapw/OmniRoute/pull/12304)) — thanks @davidebaraldo +- **fix(grok-cli):** treat omitted SuperGrokPro `creditUsagePercent` as 0% used so Provider Limits still renders a weekly bar (proto3 zero-elision) ([#12312](https://github.com/diegosouzapw/OmniRoute/pull/12312)) — thanks @HouMinXi +- **fix(combo):** Expose the two Auto-Combo scoring factors nobody could set — the combo validation schema and the dashboard weight sliders both declared 13 of the scorer's 15 factors, so `connectionDensity` (spreads load across a provider's connections) and `quality` were dropped on save and offered nowhere. The sliders also shipped their own default table that differed from the engine's on every non-zero factor and summed to 1.05, so the percentages shown next to them added up to 105%. Both lists now match `DEFAULT_WEIGHTS`, and a test keeps them there. Note that a combo whose stored `weights` omitted the two keys was effectively running with them at zero and the other thirteen renormalized upward; it now runs with the engine's intended distribution, so its routing does shift ([#12314](https://github.com/diegosouzapw/OmniRoute/pull/12314)) — thanks @maxmad64bis +- **fix(docs):** The free-tier reference no longer says its numbers come "confidence tagged per row" — no catalog entry carries a confidence tag and the API serves none, so every figure on that page is an estimate of the same, unstated quality. The page now states what an entry does vouch for: an independently documented hard stop (set by hand with the source in a comment, never defaulted to `true`) and a prompt-training disclosure, both with live counts the `check:docs-counts` gate keeps honest ([#12318](https://github.com/diegosouzapw/OmniRoute/pull/12318)) — thanks @maxmad64bis +- **fix(usage):** `adobe-firefly` and `firefly` have had a working usage fetcher since Adobe Firefly landed, but neither was ever added to the registration list, so the provider-plugin manifest, `genericQuotaFetcher` and the free-access quota cache all reported them as having no usage support — while `USAGE_SUPPORTED_PROVIDERS` said the opposite. Both are now declared, which also means their credit balance is fetched like any other declared provider's: `registerGenericQuotaFetchers` now registers a generic quota fetcher for them, and `resolveFreeAccessState` no longer returns early. A test holds the registration list to the dispatcher's switch in both directions, which is what the module's own docstring already asked for in prose ([#12321](https://github.com/diegosouzapw/OmniRoute/pull/12321)) — thanks @maxmad64bis +- **fix(quota):** drop the generic quota cache (agy / Antigravity / Claude OAuth) on an upstream 429 so reset-aware scoring does not keep a 60s stale snapshot, and force-refresh the next usage fetch so inner provider caches cannot recache the same window ([#12325](https://github.com/diegosouzapw/OmniRoute/pull/12325)) — thanks @HouMinXi +- **fix(combos):** deleting a combo now clears its persisted LKGP pins instead of leaving unreachable `key_value` rows behind ([#12326](https://github.com/diegosouzapw/OmniRoute/issues/12326)) ([#12425](https://github.com/diegosouzapw/OmniRoute/pull/12425)) — thanks @KrzysiekSko +- **fix(sse):** Keep ZWNJ (U+200C) and ZWJ (U+200D) in assistant text, reasoning and tool-call arguments — Persian/Kurdish half-space (`ارائه‌دهنده`), Arabic/Indic shaping and emoji sequences no longer lose them; the response de-obfuscation now removes joiners only between ASCII word characters, where the request side inserts them ([#12186](https://github.com/diegosouzapw/OmniRoute/issues/12186)) — thanks @rezjalibd ([#12359](https://github.com/diegosouzapw/OmniRoute/pull/12359)) — thanks @pacocartones +- **fix(resilience):** count resolved upstream 5xx results against the provider circuit breaker on the chat path — `CircuitBreaker.execute()` no longer reads a resolved `{ success: false, status: 5xx }` as a success that cancels the call-site failure, so a provider answering 503s now trips its breaker instead of staying `CLOSED` at `failureCount: 1`; single-model and combo dispatches are each accounted exactly once ([#12254](https://github.com/diegosouzapw/OmniRoute/issues/12254)) ([#12360](https://github.com/diegosouzapw/OmniRoute/pull/12360)) — thanks @pacocartones +- **fix(providers):** resolve the Codex quota auto-ping model from the live provider catalog and lifecycle registry instead of the retired `gpt-5.1-codex-mini`, and pause the ping with one actionable warning when no selectable Codex model exists rather than retrying a shut-down id every cooldown window ([#11905](https://github.com/diegosouzapw/OmniRoute/issues/11905)) ([#12361](https://github.com/diegosouzapw/OmniRoute/pull/12361)) — thanks @pacocartones +- **fix(api):** keep the `{created, data}` wrapper on combo-routed `/v1/images/generations` responses and default Codex image results to `b64_json` on both `/v1/images/generations` and `/v1/images/edits` so Codex CLI's built-in `image_gen` can decode them ([#12268](https://github.com/diegosouzapw/OmniRoute/issues/12268)) ([#12362](https://github.com/diegosouzapw/OmniRoute/pull/12362)) — thanks @pacocartones +- **fix(sse):** Name the shadowed custom provider node when a built-in provider id/alias (e.g. `openference` → `of`) reserves the prefix of an existing OpenAI/Anthropic-compatible node, so the runtime `No active credentials for provider: ` error explains that the prefix routed to the built-in and never reached the node's healthy connections, instead of contradicting the dashboard ([#11943](https://github.com/diegosouzapw/OmniRoute/issues/11943)) — thanks @morpheus9393 ([#12365](https://github.com/diegosouzapw/OmniRoute/pull/12365)) — thanks @pacocartones +- **fix(i18n):** wrap `ccOnboardingKeyPlaceholder` in ICU single quotes across all 43 locales so angle brackets render literally instead of being parsed as rich-text tags, which crashed the Claude Code onboarding block with `INVALID_MESSAGE: INVALID_TAG` ([#12302](https://github.com/diegosouzapw/OmniRoute/issues/12302)) ([#12369](https://github.com/diegosouzapw/OmniRoute/pull/12369)) — thanks @gonisulaimann +- **fix(models):** publish `effort_tiers` on Kimi K3's synced base-model entries (`kmca/k3`, `kmca/k3-256k`) so catalog-only clients (OpenCode, plain SDK pickers) can see and select the reasoning tiers (`low`/`high`/`max`) the synced metadata already carried — the `isSkippedEffortProvider` gate no longer suppresses tier visibility on those base entries, while synthetic `-` variant generation stays prevented and Codex/GLM base models remain excluded unchanged ([#12299](https://github.com/diegosouzapw/OmniRoute/issues/12299)) ([#12371](https://github.com/diegosouzapw/OmniRoute/pull/12371)) — thanks @gonisulaimann +- **fix(guardrails):** keep `auto`/`auto/*` virtual combos exempt from the Vision Bridge `fixedModel` credential guard so a combo target is passed through instead of silently falling back to global auto-selection ([#12237](https://github.com/diegosouzapw/OmniRoute/issues/12237)) ([#12373](https://github.com/diegosouzapw/OmniRoute/pull/12373)) — thanks @pacocartones +- **fix(combo):** capability-filter exhaustion caused by `max_tokens` above every target's known output limit now reports that reason (requested `max_tokens` vs the pool's highest known ceiling) instead of the unrelated "supports structured output" message ([#12229](https://github.com/diegosouzapw/OmniRoute/issues/12229)) — thanks @DW-MediaLab ([#12374](https://github.com/diegosouzapw/OmniRoute/pull/12374)) — thanks @pacocartones +- **fix(auth):** the `least-used` account strategy now prefers accounts without backoff before falling back to oldest `lastUsedAt`, the same tie-break `round-robin` already applies, so a failover no longer lands on a just-rate-limited account for a single request ([#12279](https://github.com/diegosouzapw/OmniRoute/issues/12279)) — thanks @tenshiak ([#12375](https://github.com/diegosouzapw/OmniRoute/pull/12375)) — thanks @pacocartones +- **fix(docker):** the `chatgpt-web-codex-browser` image now finds the Chrome binary under `chrome-linux64/` (Chrome for Testing layout in `playwright:v1.62.0-noble`) as well as the legacy `chrome-linux/`, so the container no longer crash-loops with `exec: --headless=new: not found` ([#12024](https://github.com/diegosouzapw/OmniRoute/issues/12024)) ([#12376](https://github.com/diegosouzapw/OmniRoute/pull/12376)) — thanks @pacocartones +- **fix(providers):** declare `groq/compound` and `allam-2-7b` as non-reasoning models in the curated Groq registry so `reasoning_effort` / `output_config.effort` / `thinking` from Claude Code are stripped instead of forwarded, which Groq rejected with HTTP 400 ([#12134](https://github.com/diegosouzapw/OmniRoute/issues/12134)) ([#12379](https://github.com/diegosouzapw/OmniRoute/pull/12379)) — thanks @pacocartones +- **fix(executors):** `OpencodeExecutor` no longer forces a direct connection when the connection has a proxy assigned in Proxy Management but no per-account proxies: the single-account fast path used to wrap the upstream dispatch in the direct-egress sentinel, discarding the ambient proxy context the chat handler had pinned from `proxy_assignments`, so API-key `opencode`/`opencode-go` connections egressed from the host IP (and hit geoblocks) despite the assignment. The direct pin is now applied only when no ambient proxy context exists ([#11894](https://github.com/diegosouzapw/OmniRoute/issues/11894) — thanks @hizzt) ([#12380](https://github.com/diegosouzapw/OmniRoute/pull/12380)) — thanks @pacocartones +- **fix(api):** `GET /v1/models` with `MODELS_CATALOG_PREFIX_MODE=canonical` (or `?prefix=canonical`) now lists providers whose registry alias is undefined or equal to their own id (Antigravity, Antigravity CLI and other self-aliased built-ins) — their single `provider/model` id was dropped by the alias/canonical duplicate guard in the static, synced, custom and alias-backed catalog loops ([#12058](https://github.com/diegosouzapw/OmniRoute/issues/12058)) — thanks @cheynetom ([#12381](https://github.com/diegosouzapw/OmniRoute/pull/12381)) — thanks @pacocartones +- **fix(translator):** Drop replayed `thinking` blocks that carry no signature (the shape produced from cross-provider `reasoning_content`) instead of stamping the default Claude signature on them, which Anthropic rejected with `400 Invalid signature in thinking block` on the next turn served by an Anthropic rung ([#12105](https://github.com/diegosouzapw/OmniRoute/issues/12105)) — thanks @atescivitci-cmd ([#12386](https://github.com/diegosouzapw/OmniRoute/pull/12386)) — thanks @pacocartones +- **fix(cli):** Resolve Bun's `--preload` polyfill path against the package root instead of `dist/`, so `omniroute` installed with `bun install -g` no longer crashes at startup with `error: preload not found …/dist/open-sse/utils/setupPolyfill.ts` ([#11980](https://github.com/diegosouzapw/OmniRoute/issues/11980)) — thanks @joglomedia ([#12387](https://github.com/diegosouzapw/OmniRoute/pull/12387)) — thanks @pacocartones +- **fix(providers):** `gemini-business` now publishes its model catalog — `/v1/models` and `/v1/providers/gemini-business/models` list the 12 enterprise Gemini ids the executor understands instead of returning an empty list (#12107) ([#12389](https://github.com/diegosouzapw/OmniRoute/pull/12389)) — thanks @pacocartones +- **fix(db):** install `busy_timeout` before the SQLite connection's first statement so a process opening the database while another one closes its WAL connection waits out the transient EXCLUSIVE lock instead of dying with `database is locked`, and recognise the drivers' real BUSY/PROTOCOL/IOERR errors as transient in the corruption probe so the same lock no longer renames the database away as corrupt; deflakes `cross-process contenders never both acquire the same connection` (#12394 — thanks @pacocartones) +- **fix(chat-admission):** derive the `chat_admission_busy` 503 `Retry-After` from observed heavyweight-lease occupancy — the larger of the exhausted `OMNIROUTE_CHAT_ADMISSION_QUEUE_MS` window and the time since capacity last turned over, capped at 60 s — instead of a fixed 1 s (structural) / 2 s (byte-stage) hint that invited Codex/agent fan-out clients to re-send ~1 MiB `/v1/responses` bodies every second into a gate held for the whole SSE lifetime; an idle gate keeps the historical floors ([#12135](https://github.com/diegosouzapw/OmniRoute/issues/12135)) (#12395 — thanks @pacocartones) +- **fix(api-manager):** the API key permissions modal no longer silently drops `allowedCombos` entries its Combo picker cannot render — routing-rule names such as `rt-*`, which the backend already honours — when "All" is clicked and the key is switched back to "Restrict"; those entries now survive the toggle, are listed read-only under the combo list so the count and the list agree, and are saved back verbatim instead of persisting `[]` (deny-all) (#12397 — thanks @pacocartones) +- **fix(catalog):** write the NUL separator of the catalog connection memo key, the provider serviceKind memo key, the Video Bridge promotion group key and a JSON-exactness test fixture as the `\u0000` escape instead of a raw byte — same runtime value, but the raw byte made git, GitHub and ripgrep treat those files as binary (hidden PR diffs, silently skipped searches); a guard test now keeps raw NUL bytes out of `src/`, `open-sse/` and `tests/` (#12403 — thanks @pacocartones) +- **fix(providers):** add `CLAUDE_CODE_CLIENT_VERSION` and `GITHUB_COPILOT_CLI_VERSION` env overrides so Anthropic/Copilot client-version gates can be unblocked without a rebuild ([#12417](https://github.com/diegosouzapw/OmniRoute/issues/12417)) ([#12632](https://github.com/diegosouzapw/OmniRoute/pull/12632)) — thanks @HouMinXi +- **fix(db):** back-fill `last_ping_at` and `last_pinged_reset_key` on `provider_connections` during schema reconciliation so divergent lineages that skipped `123_quota_auto_ping` still accept quota auto-ping writes ([#12470](https://github.com/diegosouzapw/OmniRoute/pull/12470) — thanks @KooshaPari) +- **fix(ci):** document MIT exceptions for `@eloqnt/{config,format-json,format-po}` (next-intl transitive; locked tarballs omit `license`) and keep the A2A lifecycle vitest off the real SQLite persistence seam ([#12581](https://github.com/diegosouzapw/OmniRoute/issues/12581)) ([#12595](https://github.com/diegosouzapw/OmniRoute/pull/12595)) +- OpenCode plugin `/v1/models` catalog fetch now waits 30s by default and attaches HTTP `statusCode` on 401/5xx so host fallback plugins can hop instead of seeing an untyped AbortError/UnknownError. ([#12607](https://github.com/diegosouzapw/OmniRoute/pull/12607)) — thanks @RaviTharuma +- **CI:** the OpenAPI security-tier gate now mirrors `isAlwaysProtectedPath()` in full — it also reads `ALWAYS_PROTECTED_API_PATTERNS`, so the pattern-gated credential routes (`/api/providers/{id}/{claude,codex}-auth/{export,apply-local}`, GHSA-5926-2w35-7h4q) no longer report as unannotated. (#12605) +- **fix(api):** GET /v1/models no longer waits forever on a hung coalesced catalog rebuild; cold-path waits are bounded (`CATALOG_BUILD_TIMEOUT_MS`, default 8s) and a last-good 200 is served when the rebuild times out ([#12627](https://github.com/diegosouzapw/OmniRoute/issues/12627)). ([#12628](https://github.com/diegosouzapw/OmniRoute/pull/12628)) — thanks @RaviTharuma +- **fix(cursor):** a non-array `tool_calls` on an assistant message no longer crashes the cursor request translator with a `TypeError`; both loops now require an array ([#12689](https://github.com/diegosouzapw/OmniRoute/issues/12689)) ([#12691](https://github.com/diegosouzapw/OmniRoute/pull/12691)) — thanks @soroush5 +- **fix(chat):** Chat Completions no longer return empty `content` after a server-owned memory or skills tool; the first provider send and account/model recovery now share one pipeline so a follow-up round-trip can fill the reply ([#12696](https://github.com/diegosouzapw/OmniRoute/issues/12696)) — thanks @HouMinXi ([#12867](https://github.com/diegosouzapw/OmniRoute/pull/12867)) +- **fix(dashboard):** batch-deleting provider keys no longer toasts failure after a successful delete when the confirm button's click event is forwarded as `onAfter` ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) — thanks @HouMinXi +- **fix(glm):** drop the extra 16th argument to `createSSETransformStreamWithLogger` that TypeScript rejected (TS2554) and that never reached the TransformStream ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) ([#12770](https://github.com/diegosouzapw/OmniRoute/pull/12770)) — thanks @HouMinXi +- **fix(dashboard):** pass `nodeMap` into Runtime `QuotaGroup` so a quota monitor in error/exhausted/alerting no longer throws `ReferenceError: nodeMap is not defined`. ([#12868](https://github.com/diegosouzapw/OmniRoute/pull/12868)) — thanks @HouMinXi +- Restricted API keys whose `allowedModels` lists a combo name no longer skip every combo member at pre-dispatch (`ALL_TARGETS_SKIPPED`). Inner-target filtering still applies when the allow-list is a provider prefix or `disableNonPublicModels` is on ([#12899](https://github.com/diegosouzapw/OmniRoute/pull/12899)). — thanks @HouMinXi +- **fix(combos):** Effort-suffixed combo members inherit the base model's `model_context_overrides` row so priority order is not inverted on large requests ([#12926](https://github.com/diegosouzapw/OmniRoute/pull/12926)) — thanks @HouMinXi +- **fix(catalog):** Picker-added `customModels` enter the dispatch-time live catalog so combo members and bare inference no longer 400 ([#12934](https://github.com/diegosouzapw/OmniRoute/pull/12934)) — thanks @HouMinXi +- **fix(docker):** re-derive the Docker build's worker-pool memory budget from the MEASURED ~4.5 GB per-process RSS (the issue owner's own VPS dmesg OOM-killer reproduction) instead of the stale 2560 MB/worker inference #11419 shipped, and lower `OMNIROUTE_BUILD_WORKERS` 3 → 2 so 1 parent + 1 page-data worker (2 processes × 4.5 GB = 9 GB) fits the 12.288 GB (75%) budget on a 16 GB GitHub Actions runner — the previous default (1 parent + 2 workers = 13.5 GB) still overcommitted the runner and kept "Publish to Docker Hub" failing with `cannot allocate memory` after #11419 merged (#7518). ([#11663](https://github.com/diegosouzapw/OmniRoute/pull/11663)) +- **fix(electron):** the packaged Windows desktop build now passes the #7592 cold-restart smoke end to end. Five defects were found and fixed on the way: (1) optional-pack staging failed on any machine whose `tar` is GNU tar — it reads the drive letter in an absolute `-f C:\...` archive path as a remote rsh host (`Cannot connect to C:`), so staging now passes a bare filename with `cwd` at the tarball directory and surfaces tar stderr on failure; (2) the Electron `files` allowlist was missing `lib/loginHeaderCapture.js`, so the packaged main process crashed at startup with `Cannot find module './lib/loginHeaderCapture'`; (3) electron-builder ≥26 injects an `!**/node_modules/**` ignore into every extraResources pattern list that no positive filter can override, which silently dropped the staged runtime `node_modules` (including the better-sqlite3 N-API prebuild) from `resources/app` — a fresh v3.8.50 desktop build would have shipped with no native SQLite driver and reproduced the #7592 sql.js fallback on every machine; an `afterPack` hook now restores the staged `node_modules` after packing; (4) the packaged-app smoke harness redirected `USERPROFILE` into its temp DATA_DIR, but Electron resolves the Roaming profile from `%USERPROFILE%\AppData\Roaming\` (USERPROFILE takes precedence over the APPDATA env var) and the path service throws instead of creating it, so `requestSingleInstanceLock()` returned false and the app exited(0) silently before `app.whenReady()` — the harness now pre-creates the derived tree, and `ensureSmokeEnvDirs` is exported and unit-tested; (5) the #7592 driver assertion parsed a `[DB] Driver: ...` line that the server's primary DB path never printed (only the unused `openDatabaseAsync()` did) — `getDbInstance()` now emits the same line on open so the guard can actually assert the native driver. Also: the smoke env-allowlist unit test hardcoded POSIX paths and could only pass on Linux/macOS; expectations are now host-agnostic, with new regression tests for the USERPROFILE-derived tree and `tarPack` under absolute Windows-style paths ([#11443](https://github.com/diegosouzapw/OmniRoute/pull/11443)) — thanks @jonlwheat2-gif +- fix(sse): stop the auto-combo candidates inspector from silently dropping model-locked/cooled-down rows (#9133) ([#11994](https://github.com/diegosouzapw/OmniRoute/pull/11994)) — thanks @hartmark +- **fix(providers):** Perplexity Web no longer turns upstream stream failures into successful assistant text; pre-content failures remain eligible for fallback, partial output ends with a structured sanitized error, and failed sessions are not persisted ([#12465](https://github.com/diegosouzapw/OmniRoute/pull/12465)) +- **fix(video-bridge):** preserve chronological video and transcript fusion, source-frame timestamps, transcript provenance, full contact-sheet intervals, and pre-abort callback isolation ([#11681](https://github.com/diegosouzapw/OmniRoute/pull/11681)) +- **Z.ai Web:** HTTP 200 streams carrying an upstream error now terminate with a structured failure instead of assistant text plus a normal stop, preserving partial output while allowing pre-content combo fallback. ([#12454](https://github.com/diegosouzapw/OmniRoute/pull/12454)) +- Absorb `Error [AbortError]: request_signal_aborted` and DOMException AbortError shapes in the process-level client-abort crash guard so routine client disconnects no longer kill the server (exit code 7). ([#12165](https://github.com/diegosouzapw/OmniRoute/pull/12165)) — thanks @alvinveroy +- **fix(sse):** Treat Adapta Web `type:error` SSE events as sanitized non-stream failures instead of empty HTTP 200 completions. ([#12459](https://github.com/diegosouzapw/OmniRoute/pull/12459)) +- **fix(autoCombo):** layer 3 (`models_dev_tier`) no longer scores retired models as premium, merge cross-provider capability rows deterministically, and inherit tier scores through variant ids ([#11508](https://github.com/diegosouzapw/OmniRoute/issues/11508)). `getModelsDevTierFitness` now vetoes ids present in `config/quality/model-lifecycle.json` with status `retired` before any other signal; `loadModelCapabilities()` aggregates per `model_id` (any-non-null-true for booleans, max for `limit_context`) instead of last-write-wins over SQLite's undefined row order; a capabilities miss retries through `resolveScoresAs()`'s base id and reports `models_dev_tier:inherited`, matching layers 1–2 (#11492). Adds `getModelsDevTierFitnessWithSource()` and an `invalidateCapabilitiesCache()` test/ops hook. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) — thanks @Meet6338-X +- fix(sse): accept Claude `content:[]` + `stop_reason: max_tokens`/`tool_use` in `detectMalformedNonStream` (match `isEmptyContentResponse`) so Claude Code `/model` probes with `max_tokens: 1` no longer become a false 502 ([#11568](https://github.com/diegosouzapw/OmniRoute/pull/11568)) — thanks @Seramicx +- **fix(security):** sanitize `request.failed` diagnostics before publishing them to live dashboard listeners and replay history, while keeping status, model, provider, latency, and internal call-log diagnostics intact. ([#12469](https://github.com/diegosouzapw/OmniRoute/pull/12469)) +- **fix(pwa):** do not intercept dashboard navigations so Chrome can fall back from a stale HTTP/3 Alt-Svc advertisement (UDP :20128 is unpublished; F5 on a long-lived tab hung until a new tab opened a fresh TCP connection). ([#12767](https://github.com/diegosouzapw/OmniRoute/pull/12767)) — thanks @HouMinXi +- **fix(executors):** handle DuckDuckGo ERR_BN_LIMIT (418) without retrying — when the upstream returns `418 ERR_BN_LIMIT` (rate-limit/ban), the executor now returns the error immediately instead of burning another VQD acquisition that would only count against the IP limit. The retry logic for `418 ERR_CHALLENGE` (unsolved challenge) remains unchanged. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) ([#12110](https://github.com/diegosouzapw/OmniRoute/pull/12110)) — thanks @oyi77 / @Meet6338-X +- Fixed DuckDuckGo streaming responses losing JSON lines and UTF-8 characters split across network chunks. ([#11528](https://github.com/diegosouzapw/OmniRoute/pull/11528)) — thanks @pacocartones +- Harden generated Cloudflare, Vercel, and Deno relays by stripping hop-by-hop, framing, and proxy authentication headers before forwarding requests upstream. ([#11533](https://github.com/diegosouzapw/OmniRoute/pull/11533)) — thanks @pacocartones +- **feat(grok-cli):** Provider Limits shows grok-cli banked reset credits from `GetRemainingResets` (including a real zero; a failed RPC omits the row) and the existing View credits button now calls `ConsumerUiSvc/RedeemReset` for grok-cli. Live tokens use nested fields 10/20/30 (id + Timestamp), not compact 1/2/3. ([#12805](https://github.com/diegosouzapw/OmniRoute/pull/12805)) — thanks @HouMinXi +- **fix(memory):** Embedding Model Quick select, Embedding Source remote dropdown, and Rerank selector now list every configured provider with embedding/rerank support instead of only chat-catalog text matches plus OpenRouter live discovery; a generic OpenAI-compatible `/embeddings` + Cohere-compatible `/rerank` runtime fallback resolves any configured chat provider's embedding/rerank endpoint, so unlisted providers no longer fail with "Unknown embedding provider"; both memory selectors gained a free-text model override ([#11390](https://github.com/diegosouzapw/OmniRoute/pull/11390)) — thanks @rqzbeh +- Harden SQLite upgrades around the historical migration-074 version collision: missing discovery and inspector tables are replayed atomically, pre-existing databases (including setup-created skeletons) receive reusable content-addressed safety snapshots, and Node test/eval probes without `DATA_DIR` are isolated from the operator database. ([#12435](https://github.com/diegosouzapw/OmniRoute/pull/12435)) +- **fix(dashboard):** Moonshot/Kimi Open Platform voucher and cash leftover percentages follow the bucket balance, so an empty wallet no longer paints those rows as 100% while Available is 0% ([#12733](https://github.com/diegosouzapw/OmniRoute/pull/12733)) — thanks @HouMinXi +- **fix(combo):** return non-retryable HTTP 400 when all candidates for a pinned native Codex turn are unavailable due to model-scoped lockout, terminating the turn cleanly while preserving turn continuity and enabling standard Combo routing on subsequent turns ([#12240](https://github.com/diegosouzapw/OmniRoute/pull/12240)) — thanks @mdigitalbh81 +- Fixed auto-router strategies preserving the selected connection through ranking and dispatch when multiple connections share the same provider and model. ([#11530](https://github.com/diegosouzapw/OmniRoute/pull/11530)) — thanks @pacocartones +- Preserve Chat Completions JSON-object response formats when translating requests to the Responses API. ([#11529](https://github.com/diegosouzapw/OmniRoute/pull/11529)) — thanks @pacocartones +- Fix idempotency fingerprints so Responses API requests include `input` and other semantic generation fields using stable serialization, while excluding credentials and request metadata. ([#11532](https://github.com/diegosouzapw/OmniRoute/pull/11532)) — thanks @pacocartones +- **fix(api):** Generated API CLI commands now enforce required OpenAPI request bodies; Combo test commands forward the required `comboName` body, while API keys created by older writers after migration 149 preserve legacy allow-all Combo access without widening explicit empty allowlists — thanks @marcelokarval ([#12070](https://github.com/diegosouzapw/OmniRoute/pull/12070)) +- **fix(grok-web):** treat upstream streaming failures as failures instead of successful assistant text: error-only streams now fail readiness with HTTP 502, while failures after legitimate content preserve that partial output and terminate through the sanitized stream failure path without a normal `stop` completion. ([#12458](https://github.com/diegosouzapw/OmniRoute/pull/12458)) +- HuggingChat now turns HTTP 200 JSONL generation failures into a sanitized 502 before content, or a fixed public stream failure after partial output, so fallback and request persistence no longer record a false successful stop. ([#12456](https://github.com/diegosouzapw/OmniRoute/pull/12456)) +- **fix(providers):** keep 1min.ai HTTP 200 stream errors out of assistant content, preserve partial output, and expose sanitized terminal errors so pre-content failures can fall back. ([#12466](https://github.com/diegosouzapw/OmniRoute/pull/12466)) +- **fix(sessions):** preserve legacy Sessions data when the additive exclusive-session projection is unavailable, returning an empty projection and warning only once per contiguous outage instead of failing the endpoint; the Sessions badge intentionally reflects the merged legacy and exclusive row count introduced by [#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389) ([#11469](https://github.com/diegosouzapw/OmniRoute/pull/11469)) — thanks @KaspaPulse +- Render Nimble Search with the generic provider icon and serve Opper's proven logo locally. ([#12415](https://github.com/diegosouzapw/OmniRoute/pull/12415)) +- **fix(quality):** `config/quality/provider-assets-provenance.jsonl` was stale against the physical `public/providers/` tree (still listing 225 assets and 5 duplicate-content groups after several provider-retirement PRs removed 85 icon files), which would have made `check:provider-asset-provenance` fail on the next full run. Reconciled the manifest to the real 142-file snapshot (142 asset records, 1 duplicate-content group, 2 newly-registered files marked `unresolved` pending provenance research), repinned `auditedCommit`, and updated the regression test's hardcoded counts to match. ([#11876](https://github.com/diegosouzapw/OmniRoute/pull/11876)) +- fix(ui): wrap quota card footer action buttons to prevent clipping Refresh button on narrow cards — when extra action buttons such as `View credits` are present on Codex cards, allow the footer row to flex-wrap with `shrink-0` buttons so `Refresh now` remains visible and clickable across all responsive card widths ([#11464](https://github.com/diegosouzapw/OmniRoute/pull/11464)) — thanks @adevwithpurpose +- Keep Antigravity Gemini usable when the same connection's Claude weekly quota is empty; generic quota cache stays per-connection for every other provider. ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) — thanks @HouMinXi +- Fixed Tailscale tunnel setup to reject ports outside the valid 1-65535 range and safely install Windows MSI packages from paths containing apostrophes. ([#11534](https://github.com/diegosouzapw/OmniRoute/pull/11534)) — thanks @pacocartones +- Classify Cloudflared and Tailscale process-management routes as local-only and spawn-capable while preserving remote access to read-only tunnel status endpoints. ([#11531](https://github.com/diegosouzapw/OmniRoute/pull/11531)) — thanks @pacocartones +- Electron release workflow: the `publish-npm` job now grants `actions: read` to the reusable `npm-publish.yml` it calls (its `publish` job requests it), which is what made GitHub refuse the whole v3.8.50 run at startup and ship the release with zero desktop assets; a `workflow_dispatch` now builds the requested tag instead of the dispatching branch and can skip the npm leg (`publish_npm=false`) when only re-attaching assets ([#11974](https://github.com/diegosouzapw/OmniRoute/pull/11974)) +- **fix(dashboard):** The Combos page usage guide now reads its dismissal through `useSyncExternalStore` instead of correcting SSR state inside an effect, removing an extra commit of the page tree on every load (and the `react-hooks/set-state-in-effect` error it raised). ([#12671](https://github.com/diegosouzapw/OmniRoute/pull/12671)) +- Electron release: `electron/package-lock.json` regained the optional `electron-builder-squirrel-windows` subtree (13 entries) that `npm ci` had been refusing as out of sync, `electron-release.yml` gained a `build_ref` dispatch input and stops regenerating release notes on a re-attach dispatch, and the npm publish workflow attaches the SBOM to the GitHub Release on dispatch publishes too — so the v3.8.51 tag ships every desktop asset and the SBOM like v3.8.49 did ([#12022](https://github.com/diegosouzapw/OmniRoute/pull/12022)) +- Drained the reds every PR against `release/v3.8.51` was born with: documented the three service-worker build-id variables, registered the six retirement/tunnel tests with the mutation gate, approved `eslint-plugin-react-hooks` in the dependency allowlist, added the six `combo.sort.*` strings to `vi` and `pt-BR`, pointed the ChatGPT Web doc at the real migration-168 test, worded the g4f hint around the member key, and realigned four tests to the retired-provider catalog and the legacy-schema fixtures the retirement migrations touch. ([#11940](https://github.com/diegosouzapw/OmniRoute/pull/11940)) +- **fix(providers):** Claude, Grok, LMArena, Notion, and Perplexity web-cookie transports now use pooled `wreq-js` 3.2 instead of the native sidecar, with all nine supported bindings pinned and audited, and the applicable platform binding plus native-license evidence included in each release artifact ([#12429](https://github.com/diegosouzapw/OmniRoute/pull/12429), supersedes [#11753](https://github.com/diegosouzapw/OmniRoute/pull/11753)). +- **fix(providers):** Zed Hosted streaming failures now trigger fallback before content and end partial streams with a sanitized structured error instead of fake assistant text and a normal-success stop. ([#12455](https://github.com/diegosouzapw/OmniRoute/pull/12455)) +- **fix(test):** capture hook result after render (direct commit `b85d0abb0b`) +- **fix(sync):** repair the two regressions the v3.8.50 sync-back left on release/v3.8.51 (direct commit `5ade9e0851`) +- **revert(models):** drop the memo reset hook ported from #10055 (direct commit `282de841ae`) +- **fix(typecheck):** guard the optional connectionId before the reactive model sync (direct commit `f2825bad75`) +- **fix(quota-share):** release the winner's reserved in-flight slot (#11371) ([#11408](https://github.com/diegosouzapw/OmniRoute/pull/11408)) — thanks @oyi77 +- **fix(providers):** stop silently dropping reasoning effort on opencode families (#10788) ([#11409](https://github.com/diegosouzapw/OmniRoute/pull/11409)) — thanks @oyi77 +- **fix(combo):** enable genuine quota-aware routing for generic providers (antigravity, claude, etc.) ([#11411](https://github.com/diegosouzapw/OmniRoute/pull/11411)) — thanks @HouMinXi +- **fix(oauth):** allow expired token retry before deactivation and activate expired retry loop (#P0) ([#11414](https://github.com/diegosouzapw/OmniRoute/pull/11414)) — thanks @HouMinXi +- **perf(executors):** lazy-load the executor registry — defer class imports + construction to first use (#11220) ([#11421](https://github.com/diegosouzapw/OmniRoute/pull/11421)) — thanks @oyi77 +- **fix:** make release/v3.8.50 CI gates green (run 32786966560 root causes) ([#11450](https://github.com/diegosouzapw/OmniRoute/pull/11450)) — thanks @jonlwheat2-gif +- **fix(sse):** exact-domain cookie match and origin-equality URL assertions ([#11452](https://github.com/diegosouzapw/OmniRoute/pull/11452)) — thanks @hartmark +- **fix(providers):** send Responses API payload during OpenAI-compatible validation ([#11454](https://github.com/diegosouzapw/OmniRoute/pull/11454)) — thanks @tuandinh0801 +- **fix(command-code):** fallback to /alpha/generate for Go plan without Provider API access ([#11455](https://github.com/diegosouzapw/OmniRoute/pull/11455)) — thanks @Chewji9875 +- **fix(responses-continuation):** fail closed on a log-truncated stored input/output array ([#11473](https://github.com/diegosouzapw/OmniRoute/pull/11473)) — thanks @hartmark +- **fix(cli):** write jcode settings as [providers.omniroute] in config.toml ([#11484](https://github.com/diegosouzapw/OmniRoute/pull/11484)) — thanks @hongnoul +- **fix(logging):** raise chat-log array truncation cap to 1000 and unify duplicate implementations ([#11499](https://github.com/diegosouzapw/OmniRoute/pull/11499)) — thanks @hartmark +- **fix:** restore live websocket URL helpers ([#11509](https://github.com/diegosouzapw/OmniRoute/pull/11509)) — thanks @PauloFH +- **perf(core):** eliminate async waterfalls across home, cache, and db operations (#11396) ([#11512](https://github.com/diegosouzapw/OmniRoute/pull/11512)) — thanks @HouMinXi +- **fix(kiro):** add runtime.us-east-1.kiro.dev as first-attempt endpoint ([#11517](https://github.com/diegosouzapw/OmniRoute/pull/11517)) — thanks @rifqiawl +- **fix(providers):** remove duplicate 128-tool truncation in OpencodeExecutor (#11444) ([#11518](https://github.com/diegosouzapw/OmniRoute/pull/11518)) — thanks @HouMinXi +- **fix(providers):** poll onboardUser LRO before classifying BYOP ([#11519](https://github.com/diegosouzapw/OmniRoute/pull/11519)) — thanks @rifqiawl +- **fix(translator):** preserve exact tool name casing for Claude tool_use payloads (#11487) ([#11520](https://github.com/diegosouzapw/OmniRoute/pull/11520)) — thanks @HouMinXi +- **fix(kimi-web):** align health probe with executor domain ([#11521](https://github.com/diegosouzapw/OmniRoute/pull/11521)) — thanks @pacocartones +- **fix(cli):** preserve existing admin password during setup ([#11522](https://github.com/diegosouzapw/OmniRoute/pull/11522)) — thanks @pacocartones +- **fix(mitm):** keep Windows DNS elevation off the POSIX sudo path (#11430) ([#11542](https://github.com/diegosouzapw/OmniRoute/pull/11542)) — thanks @official-burak +- **fix(sse):** scale chat admission by ingest byte budget instead of a fixed request count ([#11548](https://github.com/diegosouzapw/OmniRoute/pull/11548)) — thanks @TheDemonTuan +- **fix:** accept Gemini Web cookie exports ([#11554](https://github.com/diegosouzapw/OmniRoute/pull/11554)) — thanks @rafacpti23 +- **fix(i18n):** complete Brazilian Portuguese translations ([#11558](https://github.com/diegosouzapw/OmniRoute/pull/11558)) — thanks @rafacpti23 +- **fix(sse):** explicit types for openai-responses pureHelpers — clears last failing core typecheck gate ([#11567](https://github.com/diegosouzapw/OmniRoute/pull/11567)) — thanks @jonlwheat2-gif +- **fix(opencode):** hydrate Proxy Pool references for no-auth connections ([#11584](https://github.com/diegosouzapw/OmniRoute/pull/11584)) — thanks @AStupidBear +- **fix(ci):** resolve inherited release/v3.8.51 base-red CI failures ([#11608](https://github.com/diegosouzapw/OmniRoute/pull/11608)) — thanks @jonlwheat2-gif +- **fix(db):** fallback load STORAGE_ENCRYPTION_KEY from env files ([#11614](https://github.com/diegosouzapw/OmniRoute/pull/11614)) — thanks @benzntech +- **fix(providers):** mark Brave Search as no longer having a free tier ([#11621](https://github.com/diegosouzapw/OmniRoute/pull/11621)) — thanks @turbolego +- **fix:** pass tool result content through as string in OpenAI/Claude -> Gemini translation ([#11624](https://github.com/diegosouzapw/OmniRoute/pull/11624)) — thanks @Hsia97 +- **fix(sse):** scope credential health to exact key ([#11628](https://github.com/diegosouzapw/OmniRoute/pull/11628)) — thanks @KaspaPulse +- **fix(install):** guard prepare script against missing husky ([#11634](https://github.com/diegosouzapw/OmniRoute/pull/11634)) — thanks @Prajeeth-12 +- **fix(security):** redact NVIDIA_API_KEY at the log sink in the ad-hoc diag script ([#11639](https://github.com/diegosouzapw/OmniRoute/pull/11639)) +- **fix(types):** clear the two type regressions red on release/v3.8.51 ([#11640](https://github.com/diegosouzapw/OmniRoute/pull/11640)) — thanks @jonlwheat2-gif +- **fix(build):** resolve workspace: protocol dependencies to concrete semver versions during prepublish (#11540) ([#11641](https://github.com/diegosouzapw/OmniRoute/pull/11641)) — thanks @HouMinXi +- **fix(search):** properly resolve configured search connection in /v1/responses pipeline (#11524) ([#11642](https://github.com/diegosouzapw/OmniRoute/pull/11642)) — thanks @HouMinXi +- **fix(authz):** trust cf-connecting-ip for via-proxy detection when peer is Cloudflare edge (#11514) ([#11643](https://github.com/diegosouzapw/OmniRoute/pull/11643)) — thanks @HouMinXi +- **fix(security):** compare the OIDC state cookie in constant time ([#11646](https://github.com/diegosouzapw/OmniRoute/pull/11646)) +- **fix(security):** namespace the dedup hash by the calling API key ([#11649](https://github.com/diegosouzapw/OmniRoute/pull/11649)) +- **fix(models):** gate the Codex-native catalog loop on prefix mode (#11632) ([#11662](https://github.com/diegosouzapw/OmniRoute/pull/11662)) — thanks @xiaoyaner0201 +- **fix(dashboard):** project Codex routing quota in account pools ([#11666](https://github.com/diegosouzapw/OmniRoute/pull/11666)) — thanks @KaspaPulse +- **fix(ddgw):** normaliza system no upstream da release v3.8.51 ([#11692](https://github.com/diegosouzapw/OmniRoute/pull/11692)) — thanks @RhianB14 +- **fix(memory):** honor category filter in GET /api/memory ([#11699](https://github.com/diegosouzapw/OmniRoute/pull/11699)) — thanks @Sabeekhann +- **fix(ci):** detect stale workspace lockfile entries ([#11714](https://github.com/diegosouzapw/OmniRoute/pull/11714)) — thanks @pacocartones +- **fix(ci):** stamp dist/BUILD_SHA before the npm publish provenance gate ([#11721](https://github.com/diegosouzapw/OmniRoute/pull/11721)) +- **fix(docker):** decouple the best-effort Bun image from the release manifest ([#11724](https://github.com/diegosouzapw/OmniRoute/pull/11724)) +- **perf(compression):** avoid cloning memo misses twice ([#11727](https://github.com/diegosouzapw/OmniRoute/pull/11727)) — thanks @RaviTharuma +- **fix(docker):** let the bun digest artifact be absent, not fatal ([#11740](https://github.com/diegosouzapw/OmniRoute/pull/11740)) +- **fix(shared):** block cloud-metadata hosts under default remote-image guard ([#11755](https://github.com/diegosouzapw/OmniRoute/pull/11755)) — thanks @santosraju99-hub +- **fix(api):** preserve caller-provided X-Correlation-Id on chat completions ([#11760](https://github.com/diegosouzapw/OmniRoute/pull/11760)) — thanks @aniruddhaadak80 +- **fix(resilience):** lock permanently retired models instead of short backoff (Gemini ban prevention) ([#11762](https://github.com/diegosouzapw/OmniRoute/pull/11762)) — thanks @turbolego +- **fix:** mark Vercel AI Gateway as passthroughModels ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **fix(resilience):** stop hammering permanently-moved endpoints and billing-suspended accounts ([#11774](https://github.com/diegosouzapw/OmniRoute/pull/11774)) — thanks @turbolego +- **fix(lease):** remove global static reservation and gate routing on live active lease occupancy ([#11775](https://github.com/diegosouzapw/OmniRoute/pull/11775)) — thanks @TheDemonTuan +- **fix(ci):** size the install-upgrade gate to a measured run, and log the pack cost ([#11776](https://github.com/diegosouzapw/OmniRoute/pull/11776)) +- **fix(pwa):** stop serving the stale shell after deploys ([#11779](https://github.com/diegosouzapw/OmniRoute/pull/11779)) — thanks @HouMinXi +- **fix(combo):** lock GitHub models rejected as "not supported" for future requests ([#11781](https://github.com/diegosouzapw/OmniRoute/pull/11781)) — thanks @turbolego +- **fix(cli):** probe both IPv4 and IPv6 loopback for server readiness (#11766) ([#11794](https://github.com/diegosouzapw/OmniRoute/pull/11794)) — thanks @vermasomesh835 +- **fix(sse):** preserve CLIProxyAPI wrappers per connection ([#11797](https://github.com/diegosouzapw/OmniRoute/pull/11797)) — thanks @RaviTharuma +- **fix(providers):** honor model auto-fetch opt-in on create ([#11798](https://github.com/diegosouzapw/OmniRoute/pull/11798)) — thanks @RaviTharuma +- **fix(dashboard):** make model auto-fetch order independent ([#11805](https://github.com/diegosouzapw/OmniRoute/pull/11805)) — thanks @RaviTharuma +- **fix(docker):** make GHA cache exports best effort ([#11806](https://github.com/diegosouzapw/OmniRoute/pull/11806)) — thanks @RaviTharuma +- **fix(kiro):** do not permanently ban on 'User is not authorized to make this call' ([#11809](https://github.com/diegosouzapw/OmniRoute/pull/11809)) — thanks @Deftera186 +- **fix(sse):** keep cache-write tokens in OpenAI-shaped usage ([#11814](https://github.com/diegosouzapw/OmniRoute/pull/11814)) — thanks @TheDemonTuan +- **fix(docs):** resolve relative markdown and wiki links across Fumadocs and GitHub wiki ([#11834](https://github.com/diegosouzapw/OmniRoute/pull/11834)) — thanks @AndrianBalanescu +- **fix(chat):** preserve unstripped model string for passthrough provider routing ([#11840](https://github.com/diegosouzapw/OmniRoute/pull/11840)) — thanks @raheemuddin786 +- **fix(mcp):** avoid async initialization inside sync esm wrappers (#11569) ([#11849](https://github.com/diegosouzapw/OmniRoute/pull/11849)) — thanks @HouMinXi +- **fix(sse):** preserve coherent markdown token boundaries across stream translation chunks (#11606) ([#11851](https://github.com/diegosouzapw/OmniRoute/pull/11851)) — thanks @HouMinXi +- **fix(ci):** run the install-upgrade gate on disk, not on the /tmp tmpfs ([#11855](https://github.com/diegosouzapw/OmniRoute/pull/11855)) +- **fix(skills):** expand shorthand property types in injected tool schemas ([#11857](https://github.com/diegosouzapw/OmniRoute/pull/11857)) — thanks @alvinveroy +- **fix(ci):** publish npm from a hosted runner so provenance is accepted ([#11877](https://github.com/diegosouzapw/OmniRoute/pull/11877)) +- **fix(lint):** pin react hooks plugin to 7.0.1 ([#11886](https://github.com/diegosouzapw/OmniRoute/pull/11886)) +- **fix(ci):** surface and clear release-green ESLint failure ([#11890](https://github.com/diegosouzapw/OmniRoute/pull/11890)) — thanks @MumuTW +- **fix(guardrails):** prevent duplicate prompt-injection-guard log output ([#11936](https://github.com/diegosouzapw/OmniRoute/pull/11936)) — thanks @patrykkopycinski +- **fix(auth):** downgrade expected transient states from warn to debug ([#11937](https://github.com/diegosouzapw/OmniRoute/pull/11937)) — thanks @patrykkopycinski +- **fix(codex):** fail over image generation for imported free plans ([#11948](https://github.com/diegosouzapw/OmniRoute/pull/11948)) — thanks @Bl0ck154 +- **fix(codex):** normalize non-stream responses ([#11951](https://github.com/diegosouzapw/OmniRoute/pull/11951)) — thanks @Bl0ck154 +- **fix(images):** forward Antigravity image size ([#11952](https://github.com/diegosouzapw/OmniRoute/pull/11952)) — thanks @Bl0ck154 +- **fix(db):** invalidate connection cache after upsert ([#11953](https://github.com/diegosouzapw/OmniRoute/pull/11953)) — thanks @Bl0ck154 +- **fix(codex):** restore imported account state ([#11954](https://github.com/diegosouzapw/OmniRoute/pull/11954)) — thanks @Bl0ck154 +- **fix(sse):** give extended-thinking targets the reasoning readiness budget ([#11959](https://github.com/diegosouzapw/OmniRoute/pull/11959)) — thanks @abhisheksharma2411 +- **fix(dashboard):** use opaque background and readable text color on cost chart tooltips ([#11960](https://github.com/diegosouzapw/OmniRoute/pull/11960)) — thanks @ZaimMarzuki +- **fix(antigravity):** send complete loadCodeAssist metadata (ideType/platform/pluginType as numeric enums) ([#11969](https://github.com/diegosouzapw/OmniRoute/pull/11969)) — thanks @rifqiawl +- **fix(core):** resolve DB init race condition and reasoning translation ([#12003](https://github.com/diegosouzapw/OmniRoute/pull/12003)) — thanks @raheemuddin786 +- **fix(admission):** exclude reclaimable page cache from the cgroup pressure ratio ([#12017](https://github.com/diegosouzapw/OmniRoute/pull/12017)) — thanks @HouMinXi +- **fix(build):** prune non-production trees in NFT trace excludes and tsconfig ([#12028](https://github.com/diegosouzapw/OmniRoute/pull/12028)) — thanks @Chewji9875 +- **fix(migrations):** add renamed migration compatibility for 056/073/077/101 ([#12036](https://github.com/diegosouzapw/OmniRoute/pull/12036)) — thanks @oyi77 +- **fix(sse):** default crash-guard logger to console.warn, not console ([#12042](https://github.com/diegosouzapw/OmniRoute/pull/12042)) — thanks @HouMinXi +- **fix(catalog):** derive vision/modalities for built-in auto combos from effective target pool ([#12046](https://github.com/diegosouzapw/OmniRoute/pull/12046)) — thanks @Prajeeth-12 +- **fix(release):** the packaged-app smoke verifies the database opened, not a driver line the primary path never prints (twin of #12032) ([#12047](https://github.com/diegosouzapw/OmniRoute/pull/12047)) +- **fix(barrel):** migrate src/app/ off the @/lib/localDb barrel import (#11795 Phase 2) ([#12051](https://github.com/diegosouzapw/OmniRoute/pull/12051)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate src/lib/ off the localDb barrel to direct db imports (#59) ([#12052](https://github.com/diegosouzapw/OmniRoute/pull/12052)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate open-sse, src/shared, src/sse, src/models, src/domain off the @/lib/localDb barrel import (#11795 Phase 4) ([#12053](https://github.com/diegosouzapw/OmniRoute/pull/12053)) — thanks @jonlwheat2-gif +- **fix(barrel):** delete the @/lib/localDb barrel — every consumer migrated (#11795 Phase 5) ([#12055](https://github.com/diegosouzapw/OmniRoute/pull/12055)) — thanks @jonlwheat2-gif +- **fix(radar):** restore D12 public boundary ([#12057](https://github.com/diegosouzapw/OmniRoute/pull/12057)) +- **fix(dev):** bound webpack and Tailwind scans ([#12075](https://github.com/diegosouzapw/OmniRoute/pull/12075)) — thanks @backryun +- **fix(dev):** isolate root layout settings reads ([#12076](https://github.com/diegosouzapw/OmniRoute/pull/12076)) — thanks @backryun +- **fix(dev):** reduce instrumentation executor fan-out (phase 3) ([#12078](https://github.com/diegosouzapw/OmniRoute/pull/12078)) — thanks @backryun +- **fix(dev):** make logging resources HMR-singleton ([#12079](https://github.com/diegosouzapw/OmniRoute/pull/12079)) — thanks @backryun +- **fix(sse):** keep unavailable forced connections scoped ([#12080](https://github.com/diegosouzapw/OmniRoute/pull/12080)) — thanks @keeltrace +- **fix(dev):** isolate batch dispatch from instrumentation ([#12081](https://github.com/diegosouzapw/OmniRoute/pull/12081)) — thanks @backryun +- **fix(ci):** reconcile release test contract drift ([#12082](https://github.com/diegosouzapw/OmniRoute/pull/12082)) — thanks @backryun +- **fix(ci):** accept CVE-2025-68121 in the prebuilt tls-client .so, auto-close base-red issues, guard Scorecard on the default branch ([#12085](https://github.com/diegosouzapw/OmniRoute/pull/12085)) +- **fix(db):** drop three consumer-less 1proxy exports — dead-code base-red on release/v3.8.51 after the barrel deletion (#12055) ([#12087](https://github.com/diegosouzapw/OmniRoute/pull/12087)) +- **fix(api):** clear the six API-route TypeScript regressions the new gate landed red on ([#12094](https://github.com/diegosouzapw/OmniRoute/pull/12094)) +- **fix(release):** never let the tag-push Create Release append auto notes to the curated body ([#12096](https://github.com/diegosouzapw/OmniRoute/pull/12096)) +- **fix(sse):** honor CLIProxyAPI environment API key ([#12099](https://github.com/diegosouzapw/OmniRoute/pull/12099)) — thanks @RaviTharuma +- **fix(oauth):** bind Google refresh to the client that issued the token ([#12106](https://github.com/diegosouzapw/OmniRoute/pull/12106)) — thanks @HouMinXi +- **fix(dashboard):** make RequestLoggerDetail loadable outside Next — CSS via globals.css + CJS/ESM interop (#11703 base-reds) ([#12114](https://github.com/diegosouzapw/OmniRoute/pull/12114)) +- **fix(leases):** project status lease row to lease columns so joined connection PII never escapes ([#12115](https://github.com/diegosouzapw/OmniRoute/pull/12115)) — thanks @geek007git +- **fix(plugins):** do not kill the plugin process when a fire-and-forget hook times out ([#12116](https://github.com/diegosouzapw/OmniRoute/pull/12116)) — thanks @geek007git +- **fix(guardrails):** restore injection-guard logging on middleware-only routes ([#12117](https://github.com/diegosouzapw/OmniRoute/pull/12117)) — thanks @geek007git +- **fix(plugins):** refresh stored manifest from disk on activate so new hook fields reach existing installs ([#12120](https://github.com/diegosouzapw/OmniRoute/pull/12120)) — thanks @geek007git +- **fix(codex):** preserve existing provider state when bulk-import upserts a matching connection ([#12122](https://github.com/diegosouzapw/OmniRoute/pull/12122)) — thanks @geek007git +- **fix(ci):** clear the two base-reds the 2026-08-30 merge batch left on release/v3.8.51 (round 3) ([#12123](https://github.com/diegosouzapw/OmniRoute/pull/12123)) +- **fix(sse):** strip type:'custom' from Claude tools on agentrouter dispatch ([#12126](https://github.com/diegosouzapw/OmniRoute/pull/12126)) — thanks @wahidsadik371-coder +- **fix(sse):** stop advertising video providers the dispatcher cannot run ([#12131](https://github.com/diegosouzapw/OmniRoute/pull/12131)) — thanks @quiterunner-commits +- **fix(search):** treat HTTP 432 and plan limit errors as transient cooldown ([#12139](https://github.com/diegosouzapw/OmniRoute/pull/12139)) — thanks @HouMinXi +- **fix(sse):** refuse an AI Horde queue that cannot fit the request budget ([#12143](https://github.com/diegosouzapw/OmniRoute/pull/12143)) — thanks @quiterunner-commits +- **fix(ci):** clear the base-reds the afternoon merge batch left on release/v3.8.51 (round 5: provider count 352, TS2554/TS2677) ([#12144](https://github.com/diegosouzapw/OmniRoute/pull/12144)) +- **fix(translator):** strip neutral tool_choice when tools absent in Responses-to-Chat (#12141) ([#12166](https://github.com/diegosouzapw/OmniRoute/pull/12166)) — thanks @HouMinXi +- **fix(guardrails):** pass providerId to getResolvedModelCapabilities in checkComboVision (#12112) ([#12169](https://github.com/diegosouzapw/OmniRoute/pull/12169)) — thanks @HouMinXi +- **fix(translator):** strip plaintext reasoning content for opaque responses backends (#12128) ([#12171](https://github.com/diegosouzapw/OmniRoute/pull/12171)) — thanks @HouMinXi +- **perf(stream):** compile hot-path regexes once, bound token caches, fix quadratic buffering ([#12179](https://github.com/diegosouzapw/OmniRoute/pull/12179)) — thanks @opensource-elearning +- **fix:** resolve compression worker file using runtime anchors instead of… ([#12183](https://github.com/diegosouzapw/OmniRoute/pull/12183)) — thanks @NightStalker-87 +- **fix:** prevent Claude Code session kills via liveness-aware readiness + auto model echo ([#12189](https://github.com/diegosouzapw/OmniRoute/pull/12189)) — thanks @opensource-elearning +- **fix(translator):** preserve falsy primitive values in Gemini and Antigravity function response results ([#12191](https://github.com/diegosouzapw/OmniRoute/pull/12191)) — thanks @Siva010 +- **fix(oauth):** mark empty Antigravity projectId as degraded and clear stale errors (#11284) ([#12205](https://github.com/diegosouzapw/OmniRoute/pull/12205)) — thanks @HouMinXi +- **fix(providers):** gate the Codex auto-ping usage read on the shared quota throttle ([#12209](https://github.com/diegosouzapw/OmniRoute/pull/12209)) — thanks @abhisheksharma2411 +- **fix(combos):** prioritize SQLite row id over inner JSON id and notify delete errors ([#12213](https://github.com/diegosouzapw/OmniRoute/pull/12213)) — thanks @HouMinXi +- **fix(providers):** mark featherless.ai as no longer having a free tier (`hasFree: false`) ([#12216](https://github.com/diegosouzapw/OmniRoute/pull/12216)) — thanks @turbolego +- **fix(deepseek-web):** stop Turbopack dev panic in the PoW worker path resolver ([#12221](https://github.com/diegosouzapw/OmniRoute/pull/12221)) — thanks @hartmark +- **fix(chat-admission):** clarify local 503 source ([#12223](https://github.com/diegosouzapw/OmniRoute/pull/12223)) — thanks @keeltrace +- **fix(dev):** silence webpack runtime module warnings ([#12228](https://github.com/diegosouzapw/OmniRoute/pull/12228)) — thanks @backryun +- **fix(db):** use module.require for CommonJS runtime driver loading ([#12230](https://github.com/diegosouzapw/OmniRoute/pull/12230)) — thanks @raheemuddin786 +- **fix(memory):** honest probe-driven FTS5 keyword status + memory_id rowid sync ([#12231](https://github.com/diegosouzapw/OmniRoute/pull/12231)) — thanks @raheemuddin786 +- **fix(system):** propagate abort signal to stream reader in HTTP version checks ([#12232](https://github.com/diegosouzapw/OmniRoute/pull/12232)) — thanks @raheemuddin786 +- **fix(resilience):** isolate local host execution errors from provider circuit breakers ([#12233](https://github.com/diegosouzapw/OmniRoute/pull/12233)) — thanks @raheemuddin786 +- **fix(sse):** sort injected tools deterministically for prompt caching ([#12234](https://github.com/diegosouzapw/OmniRoute/pull/12234)) — thanks @raheemuddin786 +- **fix(combo):** always clear the loop-safety timer, not just on the happy path (#11804) ([#12245](https://github.com/diegosouzapw/OmniRoute/pull/12245)) +- **fix(quality):** base-red pair — stryker tap registration + turn-pin suites aligned to the window gate ([#12255](https://github.com/diegosouzapw/OmniRoute/pull/12255)) — thanks @backryun +- **fix(dev):** qualify Turbopack runtime boundaries ([#12258](https://github.com/diegosouzapw/OmniRoute/pull/12258)) — thanks @backryun +- **fix(combo):** fall back to full pool when collapsed sole survivor is context-too-small ([#12278](https://github.com/diegosouzapw/OmniRoute/pull/12278)) — thanks @opensource-elearning +- **fix(combo):** bound the pre-dispatch unavailable skip so a stale label cannot dark a pool (#12168) ([#12285](https://github.com/diegosouzapw/OmniRoute/pull/12285)) +- **fix(security):** strip Qwen/Alibaba console-session cookies from provider API responses ([#12287](https://github.com/diegosouzapw/OmniRoute/pull/12287)) +- **fix(usage):** console-aware Token Plan guidance + subscription hint on bailian 401 ([#12288](https://github.com/diegosouzapw/OmniRoute/pull/12288)) +- **fix(resourcePressure):** log numeric detail on every rejection, recover faster ([#12293](https://github.com/diegosouzapw/OmniRoute/pull/12293)) — thanks @hartmark +- **fix(release):** drain the 2026-09-01 base-red window — passthrough usage regression + radarPage i18n keys ([#12327](https://github.com/diegosouzapw/OmniRoute/pull/12327)) +- **fix(ci):** openapi-security-tiers checker must honor routeGuard patterns + imported prefixes ([#12350](https://github.com/diegosouzapw/OmniRoute/pull/12350)) — thanks @ggiak +- **perf(ci):** cache node_modules in the npm-ci-retry composite (#8084 D3) ([#12408](https://github.com/diegosouzapw/OmniRoute/pull/12408)) +- **revert(ui):** point CTAs back at their real destinations ([#12410](https://github.com/diegosouzapw/OmniRoute/pull/12410)) +- **fix(release):** drain the 2026-09-02 base-red — rerank-providers import + api-typecheck baseline ratchet ([#12414](https://github.com/diegosouzapw/OmniRoute/pull/12414)) +- **fix(types):** make system prompt injection noImplicitAny-safe ([#12416](https://github.com/diegosouzapw/OmniRoute/pull/12416)) +- **fix(quality):** validate typecheck baseline schema ([#12419](https://github.com/diegosouzapw/OmniRoute/pull/12419)) +- **fix(memory):** resolve rerank provider node cache import ([#12421](https://github.com/diegosouzapw/OmniRoute/pull/12421)) +- **fix(ci):** repair release-root regressions — pack dedup, web-session syntax, uc-image ids ([#12423](https://github.com/diegosouzapw/OmniRoute/pull/12423)) — thanks @backryun +- **fix(providers):** separate MaxAI and UC credential contracts ([#12431](https://github.com/diegosouzapw/OmniRoute/pull/12431)) +- **fix(cli):** preserve Claude settings on config set ([#12432](https://github.com/diegosouzapw/OmniRoute/pull/12432)) — thanks @KrzysiekSko +- **fix(providers):** repair the maxai credential block truncated by merge auto-resolve ([#12433](https://github.com/diegosouzapw/OmniRoute/pull/12433)) +- **fix(quality):** record the 2026-09-02 merged growth in the file-size baseline ([#12434](https://github.com/diegosouzapw/OmniRoute/pull/12434)) +- **fix(uc):** route UC error strings through sanitizeErrorMessage; allowlist the retired codex id ([#12437](https://github.com/diegosouzapw/OmniRoute/pull/12437)) +- **fix(adapta):** redact streamed upstream errors ([#12438](https://github.com/diegosouzapw/OmniRoute/pull/12438)) +- **fix(sse):** remap non-contiguous upstream tool_calls index to a gap-free output_index ([#12445](https://github.com/diegosouzapw/OmniRoute/pull/12445)) — thanks @hartmark +- **fix(quality):** detect duplicate tool_calls entries in one response ([#12446](https://github.com/diegosouzapw/OmniRoute/pull/12446)) — thanks @hartmark +- **fix(conversations):** resolve turn content OmniRoute never sends back to the client ([#12447](https://github.com/diegosouzapw/OmniRoute/pull/12447)) — thanks @hartmark +- **fix(combo):** do not treat credits-exhausted 401 as auth skip ([#12449](https://github.com/diegosouzapw/OmniRoute/pull/12449)) — thanks @RaviTharuma +- **fix(auth):** do not park healthy quota accounts as expired ([#12452](https://github.com/diegosouzapw/OmniRoute/pull/12452)) — thanks @RaviTharuma +- **fix(responses-continuation):** fail closed on a collector-truncated, empty output array ([#12460](https://github.com/diegosouzapw/OmniRoute/pull/12460)) — thanks @hartmark +- **fix(logging):** raise the SSE payload collector's default cap ([#12461](https://github.com/diegosouzapw/OmniRoute/pull/12461)) — thanks @hartmark +- **fix(resilience):** surface Responses failed.error.message in 502s ([#12472](https://github.com/diegosouzapw/OmniRoute/pull/12472)) — thanks @RaviTharuma +- **fix(security):** unbiased maxai X-Random nonce + stricter URL/regex assertions ([#12502](https://github.com/diegosouzapw/OmniRoute/pull/12502)) +- **fix(video):** re-anchor transcript log-redaction so PII/credential maskers can't reopen the leak (#12150 P1 follow-up) ([#12503](https://github.com/diegosouzapw/OmniRoute/pull/12503)) +- **fix(monitoring):** serve cached credentialHealth off the request path ([#12533](https://github.com/diegosouzapw/OmniRoute/pull/12533)) — thanks @RaviTharuma +- **fix(quota):** keep Antigravity Gemini usable when Claude weekly is empty ([#12566](https://github.com/diegosouzapw/OmniRoute/pull/12566)) — thanks @HouMinXi +- **fix(authz):** hard-gate every credential export and CLI-config write (GHSA-5926-2w35-7h4q) ([#12600](https://github.com/diegosouzapw/OmniRoute/pull/12600)) +- **fix(docs):** restore the Next build — REMOVED_PROVIDERS.md had no frontmatter (base-red #12581) ([#12610](https://github.com/diegosouzapw/OmniRoute/pull/12610)) +- **fix(quality):** re-point the zcodeProtocol public-creds allowlist to line 313 ([#12615](https://github.com/diegosouzapw/OmniRoute/pull/12615)) +- **fix(security):** close 3 advisories — search baseUrl exfil, sk- in the error sanitizer, bifrost relay header leak ([#12620](https://github.com/diegosouzapw/OmniRoute/pull/12620)) +- **fix(providers):** sanitize boolean required and nested bare maps for Gemini (#12269) ([#12624](https://github.com/diegosouzapw/OmniRoute/pull/12624)) — thanks @HouMinXi +- **fix(resilience):** keep Overloaded STREAM_EARLY_EOF off the provider breaker ([#12626](https://github.com/diegosouzapw/OmniRoute/pull/12626)) — thanks @HouMinXi +- **fix(catalog):** re-audit free-tier quotas against official pages ([#12649](https://github.com/diegosouzapw/OmniRoute/pull/12649)) +- **fix(dashboard):** keep a request's pending-tracking id stable across combo target retries ([#12650](https://github.com/diegosouzapw/OmniRoute/pull/12650)) — thanks @hartmark +- **fix(tests):** retire dead model ids from the chat-pipeline integration suite (base-red #12581) ([#12670](https://github.com/diegosouzapw/OmniRoute/pull/12670)) +- **fix:** resolve SqliteError no such table compression_run_telemetry during cleanup ([#12682](https://github.com/diegosouzapw/OmniRoute/pull/12682)) — thanks @groovecityJO +- **fix(combo):** treat a pin-only step as implicit connection allowlist ([#12697](https://github.com/diegosouzapw/OmniRoute/pull/12697)) — thanks @HouMinXi +- **fix(ci):** ship .npmrc in published package so legacy-peer-deps applies to consumers (#11544) ([#12699](https://github.com/diegosouzapw/OmniRoute/pull/12699)) — thanks @KooshaPari +- **fix(docker):** pin 4 CLI tools to exact versions (#12576) ([#12703](https://github.com/diegosouzapw/OmniRoute/pull/12703)) — thanks @KooshaPari +- **fix(docker):** document and harden cli profile trust boundary (#12570) ([#12706](https://github.com/diegosouzapw/OmniRoute/pull/12706)) — thanks @KooshaPari +- **fix(sse):** redact video transcript in pre-guardrail rejected-request logs (#12150 P2 item 7) ([#12710](https://github.com/diegosouzapw/OmniRoute/pull/12710)) +- **fix(i18n):** quote placeholder in OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES description (#12505) ([#12769](https://github.com/diegosouzapw/OmniRoute/pull/12769)) — thanks @KooshaPari +- **fix(api/pricing):** surface validation error message as string, not raw object (#12494) ([#12771](https://github.com/diegosouzapw/OmniRoute/pull/12771)) — thanks @KooshaPari +- **fix(sse):** re-enable prompt compression for native Codex passthrough ([#12834](https://github.com/diegosouzapw/OmniRoute/pull/12834)) — thanks @thomasmaerz +- **fix(dashboard):** model health tests for a provider node set to the Responses API now call `/v1/responses` with a Responses-shaped body instead of `/v1/chat/completions` — those models were reported as `Provider returned HTTP 200 but no text content` even though the same model answered normally through `/v1/responses` ([#13070](https://github.com/diegosouzapw/OmniRoute/issues/13070)) ([#13087](https://github.com/diegosouzapw/OmniRoute/pull/13087)) — thanks @ntdatt812 +- fix(routing): stop a round-robin combo's "opencode" targets from collapsing onto the opencode-zen connection (#11912) ([#13283](https://github.com/diegosouzapw/OmniRoute/pull/13283)) +- fix(routing): stop the reactive-compaction debug log from lying when compression is globally disabled (#11977) ([#13276](https://github.com/diegosouzapw/OmniRoute/pull/13276)) +- fix(electron): relativize standalone-bundle symlink targets so Stage 8 manifest verification stops failing on Windows (#11979) ([#13251](https://github.com/diegosouzapw/OmniRoute/pull/13251)) +- fix(dashboard): surface a visible error when Compression Studio's combined preview run fails (#12061) ([#13288](https://github.com/diegosouzapw/OmniRoute/pull/13288)) +- fix(dashboard): make the compression "Effective pipeline" preview honor the active profile and warn when the master switch is off (#12063) ([#13287](https://github.com/diegosouzapw/OmniRoute/pull/13287)) +- fix(providers): scope TinyCMS Web signer's DOM shims to each call instead of leaking them for the process lifetime, and surface a clean HTTP status on a non-JSON interception-toggles error (#12072) ([#13254](https://github.com/diegosouzapw/OmniRoute/pull/13254)) +- fix(guardrails): stop Vision Bridge from re-selecting a model locked after a 404 (#12111) ([#13259](https://github.com/diegosouzapw/OmniRoute/pull/13259)) +- fix(sse): require Responses-shaped body before native OpenAI-compatible passthrough (#12129) ([#13278](https://github.com/diegosouzapw/OmniRoute/pull/13278)) +- fix(providers): minimax-m3 now collapses manual thinking.type:"enabled" to adaptive, preventing upstream 400 (2013) (#12132) ([#13249](https://github.com/diegosouzapw/OmniRoute/pull/13249)) +- fix(db): scope model visibility overrides by modality so hiding a Chat model no longer hides an identically-ID'd Image/Embeddings/etc. model (#12172) ([#13258](https://github.com/diegosouzapw/OmniRoute/pull/13258)) +- fix(db): scope local-provider apiKey dedup to matching base URL so LM Studio/Ollama-style connections support multiple accounts (#12173) ([#13250](https://github.com/diegosouzapw/OmniRoute/pull/13250)) +- fix(providers): refresh Trae's stale Referer/Origin and forward user timezone so imported connections stop failing with 401 (#12190) ([#13255](https://github.com/diegosouzapw/OmniRoute/pull/13255)) +- fix(providers): route opencode-go/gpt-5.6-luna to /responses instead of /chat/completions (#12196) ([#13275](https://github.com/diegosouzapw/OmniRoute/pull/13275)) +- fix(dashboard): allow deleting the last extra-upstream-header row even when invalid (#12251) ([#13252](https://github.com/diegosouzapw/OmniRoute/pull/13252)) +- **fix(i18n):** translate pre-existing `__MISSING__:` keys for `combo.sort`, `requestLogger.detail` expand/collapse, `common.profile`, and `settings.resilienceCredentialHealth*` across 39 locales ([#12272](https://github.com/diegosouzapw/OmniRoute/issues/12272)) ([#13034](https://github.com/diegosouzapw/OmniRoute/pull/13034)) — thanks @HouMinXi +- fix(cli): run the Node.js runtime compatibility guard before the heavy `tsx/esm` + Commander import chain so an unsupported runtime gets a clear message instead of a raw `Invalid regular expression flags` crash (#12296) ([#13253](https://github.com/diegosouzapw/OmniRoute/pull/13253)) +- fix(dashboard): refresh the providers list after deleting a compatible provider node (#12298) ([#13256](https://github.com/diegosouzapw/OmniRoute/pull/13256)) +- fix(usage): fail closed on API-key budget enforcement when a provider's `auto` routing alias has no pricing row, instead of silently counting it as $0 (#12341) ([#13257](https://github.com/diegosouzapw/OmniRoute/pull/13257)) +- **fix(providers):** include the submitted Agnes video model when polling by `video_id` ([#12356](https://github.com/diegosouzapw/OmniRoute/pull/12356)) — thanks @pacocartones +- **fix(routing):** custom OpenAI-compatible nodes now honor the saved Chat/Responses API type after edits instead of letting the node's original ID prefix override the live connection setting ([#11884](https://github.com/diegosouzapw/OmniRoute/issues/11884)). ([#12358](https://github.com/diegosouzapw/OmniRoute/pull/12358)) — thanks @pacocartones +- fix(sse): surface an error instead of a silent empty 200 when a Claude stream closes with zero bytes (#12398) ([#13285](https://github.com/diegosouzapw/OmniRoute/pull/13285)) +- fix(oauth): warn before the dead localhost:8080 redirect in antigravity/gemini `oauth start` (#12413) ([#13265](https://github.com/diegosouzapw/OmniRoute/pull/13265)) +- fix(providers): stop devin-cli spawn error from double-closing the SSE controller (#12517) ([#13261](https://github.com/diegosouzapw/OmniRoute/pull/13261)) +- **fix(rerank):** clamp Voyage `top_k` to the documents actually sent after empty-string filtering, and honor `return_documents: false` in the NVIDIA response adapter (#12523 — thanks @pacocartones) +- **fix(audio):** `/v1/audio/translations` now resolves combo names the way `/v1/audio/transcriptions` already does, so a combo that `GET /v1/models` advertises is fanned out to its targets instead of being rejected with `400 Invalid translation model: . Use format: provider/model`; literal `provider/model` ids and unknown bare names behave as before (#12536 — thanks @pacocartones) +- **fix(gemini):** strip the JSON-Schema-2020-12 `prefixItems` keyword from Gemini tool schemas at every nesting level, so Claude Code tool definitions no longer fail with `400 Unknown name "prefixItems"` on Gemini models (#12540 — thanks @pacocartones) +- **fix(api-manager):** Expose an accessible loading status while API keys are fetched instead of an empty accessibility tree (#12541 — thanks @pacocartones) +- **fix(video):** Clamp `estimateJpegFrameBytes` at zero for padding-only payloads and build the three encode-side frame data URIs from `JPEG_FRAME_DATA_URI_PREFIX` instead of a repeated literal (#12543 — thanks @pacocartones) +- **fix(devin):** accept Windows `DEVIN_AGENTIC_HOME` sandbox paths (`C:\...\.sandbox\...`) in the isolated-home check so the Devin Claude Bridge no longer fails closed on Windows ([#12405](https://github.com/diegosouzapw/OmniRoute/issues/12405)) (#12545 — thanks @pacocartones) +- **fix(db):** Add `conversation_turn_nodes` and orphaned `agentic_conversations` to the auto-cleanup cycle under the existing `retention.callLogs` window, so identity nodes whose call-log content has already been purged no longer accumulate without bound in `storage.sqlite` (#12548 — thanks @pacocartones) +- **fix(i18n):** Wrap the `~/.claude/profiles//settings.json` placeholder in ICU single quotes in the `OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES` feature-flag description across all 42 locales and the TypeScript default, so next-intl no longer fails with `INVALID_MESSAGE: UNCLOSED_TAG` and the Feature Flags card shows the description instead of the raw key (#12549 — thanks @pacocartones) +- **fix(orchestration):** `updateCloudAgentTask` now publishes the task's real `status` on `agent.task.updated` when an update only touches `result`, `activities` or `error`, instead of the fabricated `"updated"` state, and stays silent when no row matched the id (#12550 — thanks @pacocartones) +- **fix(i18n):** the home "Recent Requests" panel and the Provider Topology legend are now translated instead of rendering English copies on non-English dashboards; the legend reads its own `home.topologyLegend*` labels with consistent casing rather than borrowing the memory-settings "Recent" and analytics "Error" strings (#12551 — thanks @pacocartones). +- **docs(reference):** bring the `FEATURE_FLAGS.md` catalog back to 1:1 with `featureFlagDefinitions.ts` — 20 missing flags added, the two `*_BLOCK_THRESHOLD` env-only knobs moved out of the flag tables, category/total counts and the Live WS port corrected, guarded by a static test (#12552 — thanks @pacocartones) +- **fix(i18n):** backfill missing `usage.kiloPass*` strings in 39 locales and restore `featureFlagOmnirouteDisableThinkingLevelVariantsDescription` in `pt.json` ([#12561](https://github.com/diegosouzapw/OmniRoute/issues/12561)) ([#13027](https://github.com/diegosouzapw/OmniRoute/pull/13027)) — thanks @HouMinXi +- fix(docker): default docker-compose app ports (dashboard/API/live-WS) to loopback instead of `0.0.0.0`, closing the anonymous `/v1` LAN/WAN exposure gap left open by `REQUIRE_API_KEY=false` (#12568) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): close DNS-rebinding SSRF gap in webhook outbound-URL guard (#12569) ([#13243](https://github.com/diegosouzapw/OmniRoute/pull/13243)) +- fix(docker): require a per-session token on the VNC browser CDP bridge and isolate it on a dedicated Docker network (#12571) ([#13241](https://github.com/diegosouzapw/OmniRoute/pull/13241)) +- fix(open-sse): write Adobe Firefly session tokens and cookie jars with 0700/0600 permissions instead of the process umask (#12572) ([#13242](https://github.com/diegosouzapw/OmniRoute/pull/13242)) +- fix(api): remove hardcoded wildcard CORS in openai-to-gemini-sse.ts so the centralized fail-closed CORS gate is the sole source of `Access-Control-Allow-Origin` (#12573) ([#13239](https://github.com/diegosouzapw/OmniRoute/pull/13239)) +- fix(api): enforce API key policy (budget/rate-limit/schedule/endpoint scoping) on the ElevenLabs speech-to-text, text-to-speech and voices proxy routes (#12574) ([#13244](https://github.com/diegosouzapw/OmniRoute/pull/13244)) +- fix(sse): cap HuggingChat NDJSON body size and bound the read loop with the fetch timeout so a stalled or hostile upstream cannot buffer unbounded memory (#12577) ([#13282](https://github.com/diegosouzapw/OmniRoute/pull/13282)) +- fix(docker): scope the cliproxyapi/qdrant/bifrost sidecars to loopback by default and forward `CLIPROXYAPI_MANAGEMENT_KEY` into the cliproxyapi container so its management API is not left both unauthenticated and LAN-published (#12578) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): create DB export temp paths with `fs.mkdtempSync` instead of predictable timestamps (#12579) ([#13237](https://github.com/diegosouzapw/OmniRoute/pull/13237)) +- Cline 401 bodies that say "re-authenticate your Cline account" classify as a refreshable OAuth token, not a terminal expired key. The cooling panel no longer labels every cooldown as a 429; it shows the recorded last error instead. (#12594) ([#13060](https://github.com/diegosouzapw/OmniRoute/pull/13060)) — thanks @HouMinXi +- **fix(catalog):** degrade unknown combo targets instead of dropping LCD modalities, and persist OpenRouter `architecture.input_modalities` into the capability snapshot ([#12613](https://github.com/diegosouzapw/OmniRoute/issues/12613)) ([#13042](https://github.com/diegosouzapw/OmniRoute/pull/13042)) — thanks @HouMinXi +- fix(providers): send `x-api-key` instead of `Authorization: Bearer` for OpenCode Zen's `/v1/responses` endpoint (Muse Spark Contributor models), fixing a 401 on OmniRoute's auth header (#12633) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(sse): surface the actionable "Auggie CLI not found" message when the shell reports a missing `auggie` binary via exit code instead of a spawn error (#12645) ([#13263](https://github.com/diegosouzapw/OmniRoute/pull/13263)) +- **fix(config):** Persist deletions of built-in background-degradation entries — when a stored settings record exists its `degradationMap` is now authoritative instead of being merged under the defaults, so an entry the user removed in the dashboard no longer reappears on the next apply or restart ([#12424](https://github.com/diegosouzapw/OmniRoute/issues/12424)) ([#12647](https://github.com/diegosouzapw/OmniRoute/pull/12647)) — thanks @pacocartones +- **fix(gamification):** action-count badge milestones (First Token, Token Consumer, Token Machine, Token Whale, and the token-sharing tier) are now backed by a durable `xp_action_counts` counter incremented in `addXp()`, instead of a live `COUNT(*)` over `xp_audit_log`. The audit log is pruned by `retention.xpAuditLog` (default 30 days), so on a default install those "lifetime" milestones were really "actions in the last 30 days" and unlocked badges could stop unlocking once old rows aged out. `getActionCount()` and `checkActionCountBadges()` now read the same durable source, and a migration backfills existing totals from the surviving audit rows ([#12546](https://github.com/diegosouzapw/OmniRoute/issues/12546)) ([#12651](https://github.com/diegosouzapw/OmniRoute/pull/12651)) — thanks @pacocartones +- **fix(images):** `/v1/images/edits` now iterates a combo's targets the same way `/v1/images/generations` does (#9239) instead of flattening a bare combo to its first target. A combo whose first target is not edit-capable — or lacks credentials — now falls through to a later edit-capable target rather than hard-erroring, and missing credentials are skipped (not a hard `401`) to match the generations path. The per-target skip/terminal classification is extracted into a shared `runImageComboTargets` loop, so generations behavior is unchanged ([#12547](https://github.com/diegosouzapw/OmniRoute/issues/12547)). ([#12653](https://github.com/diegosouzapw/OmniRoute/pull/12653)) — thanks @pacocartones +- fix(sse): add first-byte watchdog to the TLS-fingerprint transport so a stalled wreq body falls back instead of hanging for minutes (#12656) ([#13272](https://github.com/diegosouzapw/OmniRoute/pull/13272)) +- fix(sse): exempt tiny-budget reasoning probes from combo quality failure and surface persisted-cooldown skips in ALL_TARGETS_SKIPPED diagnostics (#12659) ([#13269](https://github.com/diegosouzapw/OmniRoute/pull/13269)) +- fix(models): declare the real ~1M contextLength for OpenCode Zen's Muse Spark 1.2 models instead of falling back to the 200000 provider default (#12681) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(oauth): align codebuddy-cn OAuth User-Agent with the chat/usage CLI version to avoid WAF false positives (#12702) ([#13264](https://github.com/diegosouzapw/OmniRoute/pull/13264)) +- fix(dashboard): surface an authentication-required banner instead of silently blanking database settings for a guest session (#12709) ([#13270](https://github.com/diegosouzapw/OmniRoute/pull/13270)) +- **fix(resilience):** stop unbounded queue that hangs 6min until Aborted — gate, provider slot, and Bottleneck queue now share a per-connection `maxWaitMs` budget; `fail-closed` on exhaust (503); execution backstop `executionMaxWaitMs` overridable per connection with upstream clamp ([#12715](https://github.com/diegosouzapw/OmniRoute/pull/12715)) — thanks @maxmad64bis (with thanks to @Tushar49 for surfacing the slow-provider need in #12635) +- **fix(auto-combo):** every mode pack now carries `quality` and `reliability` weights (`quality` 0.02, 0.03 in `quality-first`; `reliability` 0.03, 0.04 in `reliability-first`), so selecting a pack no longer silences either signal; `DEFAULT_WEIGHTS` is unchanged ([#12731](https://github.com/diegosouzapw/OmniRoute/pull/12731)) — thanks @maxmad64bis +- **fix(sse):** the provider execution pipeline keeps the upstream error `code`/`type` again — both were lost when the non-streaming leg was extracted, so a config-class `422` (Antigravity missing project) degraded into a generic account cooldown instead of reaching the gate that recognises it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(db):** a legacy `call_logs` table no longer aborts startup — the provider-stats index is created after the column healing runs, not before it, so an install predating `request_type` opens instead of failing with `no such column` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(catalog):** a malformed row in the operator-writable custom-models blob no longer kills every `auto/*` pool ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(tests):** realign the guards that the same day's merges left asserting the old behavior — the provider-count total after GreenPT and EURouter, the GLM stream buffer slot that `#12925` turned into a declared parameter, and the injection scan bound that `#13104` reshaped from a head-only window into head-plus-tail ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- fix(cache): fold tool_choice/tools/response_format into the semantic cache signature so a cached tool_calls response can no longer be replayed for a request whose tool policy forbids it (#12734) ([#13267](https://github.com/diegosouzapw/OmniRoute/pull/13267)) +- **fix(models):** stop treating provider-supplied `isFree:true`/`:free`/`0/0` flags as trusted unless the provider has a documented free tier — only the shipped free-tier catalog decides otherwise; fetched `isFree:true` trusted only for a free-tier provider, custom `isFree:true` via a trusted path ([#12744](https://github.com/diegosouzapw/OmniRoute/pull/12744)) — thanks @maxmad64bis +- fix(memory): authenticate the internal /v1/rerank loopback call so memory reranking no longer silently degrades to unranked order when REQUIRE_API_KEY=true (#12745) ([#13268](https://github.com/diegosouzapw/OmniRoute/pull/13268)) +- fix(sse): parse Ollama Cloud's current usage markup (`$X of $Y used` aria-label, nested width style) (#12749) ([#13260](https://github.com/diegosouzapw/OmniRoute/pull/13260)) +- fix(cli): setup-opencode no longer sends an active context's management token to `/v1/models` when `--api-key`/`OMNIROUTE_API_KEY` is supplied — an explicit flag or the env var now always outranks the context's token, and the flag itself is no longer swallowed by the parent program's global `--api-key` option (#12783) ([#13246](https://github.com/diegosouzapw/OmniRoute/pull/13246)) +- fix(sse): register Arcee AI in the executor provider registry so requests reach api.arcee.ai instead of silently falling back to OpenAI (#12784) ([#13277](https://github.com/diegosouzapw/OmniRoute/pull/13277)) +- **fix(quota):** share one upstream quota read when identical saturation checks arrive at the same time, so traffic bursts don't multiply provider API calls ([#12787](https://github.com/diegosouzapw/OmniRoute/pull/12787)) — thanks @maxmad64bis +- **fix(db):** ignore expired or invalid rate-limit cooldown writes so a stale timestamp can't lock a connection that should be usable — clearing still works as before ([#12788](https://github.com/diegosouzapw/OmniRoute/pull/12788)) — thanks @maxmad64bis +- **fix(providers):** single source for provider order with `xao` ranking alongside `xai-oauth` ([#12790](https://github.com/diegosouzapw/OmniRoute/pull/12790)) — thanks @maxmad64bis +- **fix(routing):** off-table models check the free-model catalog before inheriting premium prices, latency bootstraps from the observed pool median, async tiers read live database pricing with a 90-day freshness gate, and the tier cache invalidates on every pricing write ([#12792](https://github.com/diegosouzapw/OmniRoute/pull/12792)) — thanks @maxmad64bis +- **fix(auto-combo):** snapshot scoring uses the observed per-connection account tier and per-model quality instead of neutral constants, and circuit-open providers rank lower at build time ([#12794](https://github.com/diegosouzapw/OmniRoute/pull/12794)) — thanks @maxmad64bis +- **fix(pool):** empty `auto/*` pools now say why they are empty, and the models listing uses the same paid check as routing ([#12795](https://github.com/diegosouzapw/OmniRoute/pull/12795)) — thanks @maxmad64bis +- fix(routing): recognize CLIProxyAPI's 'unknown provider for model' 400 as fallback-worthy (#12800) ([#13284](https://github.com/diegosouzapw/OmniRoute/pull/13284)) +- **fix(sse):** translate-mode streams now emit the estimated token counts as a trailing usage-only chunk before `[DONE]` when the upstream stays silent, so metered chat clients see totals instead of nothing ([#12828](https://github.com/diegosouzapw/OmniRoute/pull/12828)) — thanks @maxmad64bis +- **fix(db):** provider stats stay truthful on empty and legacy databases: fallback counts default to `0` instead of `null`, latency averages read `null` (not `0`) when no durations were recorded, failures logged before the error-type column existed group under `pre_migration` instead of `unclassified`, and per-provider queries use two new composite indexes ([#12832](https://github.com/diegosouzapw/OmniRoute/pull/12832)) — thanks @maxmad64bis +- fix(nvidia): fail open when a synced model catalog goes stale instead of gating forever (#12849) ([#13248](https://github.com/diegosouzapw/OmniRoute/pull/13248)) +- **fix(db):** WAL maintenance lives in its own module: a `TRUNCATE` checkpoint that hits a busy database now warns with its streak and retries once via `PASSIVE` instead of logging success, and closing-time checkpoints no longer report success on builds without a database file, with the busy totals visible in the authenticated monitoring health payload ([#12853](https://github.com/diegosouzapw/OmniRoute/pull/12853)) — thanks @maxmad64bis +- **fix(health):** quota with no snapshots reads empty instead of a contradicting 0% ([#12857](https://github.com/diegosouzapw/OmniRoute/pull/12857)) — thanks @maxmad64bis +- fix(a2a): accept the dashboard's own session cookie on /a2a so "Run message/send" no longer fails with "Unauthorized: missing or invalid API key" (#12888) ([#13271](https://github.com/diegosouzapw/OmniRoute/pull/13271)) +- **fix(logs):** the Logs grid's in-memory filter pass no longer discards rows the SQL query already matched — selecting an API key from the dropdown (which sends the key's id) returns its calls again, the Combo tab shows every combo instead of only those whose name contains a "1", and the model filter and search cover the same columns as the query ([#12896](https://github.com/diegosouzapw/OmniRoute/pull/12896)) — fixes [#12873](https://github.com/diegosouzapw/OmniRoute/issues/12873) — thanks @ntdatt812 +- **fix(a2a):** `/api/a2a/status` now builds the agent card from the request that asked for it, so a gateway reached at a non-localhost host no longer advertises `http://localhost:20128` as its A2A URL ([#12918](https://github.com/diegosouzapw/OmniRoute/pull/12918)). — thanks @ntdatt812 +- **fix(compression):** progressive aging now appends its `[COMPRESSED:aging:…]` annotation after a turn's `tool_result` blocks instead of in front of them, so Anthropic no longer rejects aged conversations with "`tool_use` ids were found without `tool_result` blocks immediately after" ([#12920](https://github.com/diegosouzapw/OmniRoute/pull/12920)). — thanks @ntdatt812 +- **fix(bedrock):** model import now resolves context limits for every vendor prefix instead of only `anthropic.*`, so `global.openai.gpt-5.6-*` no longer imports with a null `inputTokenLimit` and gets rejected pre-flight at the 200k default ([#12921](https://github.com/diegosouzapw/OmniRoute/pull/12921)). — thanks @ntdatt812 +- **fix(stream):** the 64 KB stream buffer GLM asks for is honoured instead of dropped, and the type error it caused no longer fails the API Route Typecheck gate on every open PR ([#12925](https://github.com/diegosouzapw/OmniRoute/pull/12925)) — thanks @ntdatt812 +- **fix(guardrails):** mask PII inside a `tool_result`'s nested content array, which the masker walked past while redacting its sibling block ([#12930](https://github.com/diegosouzapw/OmniRoute/pull/12930)) — thanks @ntdatt812 +- **fix(dashboard):** the Radar catalog table no longer leaves absent data unexplained — empty limits, unknown context windows, and unreported capabilities each explain themselves on hover, and a new check keeps it that way ([#12937](https://github.com/diegosouzapw/OmniRoute/pull/12937)) — thanks @maxmad64bis +- **fix(sse):** geo-blocked opencode requests rotate to the next account proxy instead of failing, so one refused egress no longer aborts the whole chain ([#12941](https://github.com/diegosouzapw/OmniRoute/pull/12941)) — thanks @maxmad64bis +- **fix(codex):** dashboard "clear cooldown" and the CAS recovery path now drop nested `codexScopeRateLimitedUntil` maps in the same write that nulls `rate_limited_until`, and fresh quota snapshots with headroom lift fallback-sourced scope cooldowns parked by quota preflight ([#12817](https://github.com/diegosouzapw/OmniRoute/issues/12817), [#12860](https://github.com/diegosouzapw/OmniRoute/issues/12860), [#12951](https://github.com/diegosouzapw/OmniRoute/pull/12951)) The dashboard reset-credit button (`consumeCodexResetCredit`) now exercises that same snapshot path after a successful redeem, so a filled quota bar is enough to unpark a leftover fallback Codex child without another manual clear. — thanks @HouMinXi +- **fix(db):** resolve `sql-wasm.wasm` across global npm install and hoisted layouts, ensuring OmniRoute can boot cleanly on Node 24 when native `better-sqlite3` is uncompiled. ([#13035](https://github.com/diegosouzapw/OmniRoute/pull/13035)) — thanks @HouMinXi +- **fix(api):** `DELETE /v1/batches/delete-completed` now sweeps only the calling API key's own completed batches (batches with no owner stay out of a key-scoped sweep on purpose), with an explicit instance-wide mode reserved for authenticated dashboard sessions, a 401 for a presented key that is unknown, revoked, deactivated, banned or expired (never falling through to the session branch), audit logging of every sweep, a sanitized 500 on failure, an owner-scoped file half (a key-scoped sweep never nulls a file another tenant owns) and an atomic sweep — chunked in 200-batch transactions in instance mode — so a mid-way error never leaves a batch pointing at a nulled file (GHSA-wvxc-jp3v-5mg5) ([#12969](https://github.com/diegosouzapw/OmniRoute/pull/12969)) +- **fix(combo):** quota-weighted routing stops drawing on an out-of-credit connection — a 402 now invalidates the stored quota snapshot instead of leaving its stale remaining percentage in place, and a snapshot older than 10 minutes no longer counts as confident headroom for the primary pool ([#12972](https://github.com/diegosouzapw/OmniRoute/pull/12972)) ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- **fix(vision):** Custom Models with "Vision capable" checked no longer have image requests swapped to `glm/glm-4.6v` when the client sends the advertised alias (`vllm/path/...`) or the bare path-shaped id — Vision Bridge now matches the stored override for all three id forms ([#12758](https://github.com/diegosouzapw/OmniRoute/issues/12758)) ([#12974](https://github.com/diegosouzapw/OmniRoute/pull/12974)) — thanks @HouMinXi +- **fix(sse):** transient opencode upstream failures rotate to the next account proxy instead of failing, so one flapping egress no longer aborts the whole chain ([#12975](https://github.com/diegosouzapw/OmniRoute/pull/12975)) — thanks @maxmad64bis +- **fix(azure):** Deployments from GPT-6 onward now send `max_completion_tokens` instead of `max_tokens`, which Azure rejects with HTTP 400. The rule matched a literal `gpt-5`, so each new generation arrived broken; it now matches the generation range, while `gpt-35-turbo` still keeps `max_tokens`. ([#13007](https://github.com/diegosouzapw/OmniRoute/pull/13007)) — thanks @ntdatt812 +- **fix(db):** add an opt-in self-restart circuit for sustained critical memory pressure, gate post-cleanup VACUUM behind a minimum freed-rows threshold, and checkpoint the SQLite WAL every 5 minutes with a size guard that escalates to TRUNCATE, so a growing WAL can no longer stall the event loop into a full outage. ([#13011](https://github.com/diegosouzapw/OmniRoute/pull/13011)) — thanks @HouMinXi +- **fix(auth):** an explicit connection pin may probe a quota-disabled row once and re-enable it on success ([#12874](https://github.com/diegosouzapw/OmniRoute/issues/12874)) ([#13017](https://github.com/diegosouzapw/OmniRoute/pull/13017)) — thanks @HouMinXi +- **fix(api):** thread `X-OmniRoute-Fallback-Attempts` through combo chat completions so streaming and non-streaming responses report how many prior legs were attempted ([#13038](https://github.com/diegosouzapw/OmniRoute/pull/13038)) — thanks @HouMinXi +- Restore provider failure classification and credential refresh on non-streaming requests: classify non-2xx failures to lock models on per-model quota exhaustion, update connection rate limits from headers and body, and pass credential refresh handlers to pipeline execution so 401 tokens can be refreshed and retried (#13043). ([#13069](https://github.com/diegosouzapw/OmniRoute/pull/13069)) — thanks @HouMinXi +- **fix(responses):** wrap forced-non-streaming web_search fallback JSON as Responses SSE so Codex still sees `response.completed` ([#13050](https://github.com/diegosouzapw/OmniRoute/pull/13050)) — thanks @HouMinXi +- **fix(antigravity):** Preserve upstream thought-token usage in normalized completion and reasoning token counts ([#13055](https://github.com/diegosouzapw/OmniRoute/pull/13055)) — thanks @pacocartones +- Fix a concurrency-slot leak in the DeepSeek PoW solver: a worker that failed to spawn (for example a missing worker script) never released its slot, so `MAX_CONCURRENT_WORKERS` failures disabled the solver until restart. ([#13097](https://github.com/diegosouzapw/OmniRoute/pull/13097)) — thanks @anhtahaylove +- **fix(acp):** bound the ACP session output buffers — `stdoutBuffer` and `stderrBuffer` now cap at 1 MiB keeping the most recent output behind a visible `[...output truncated...]` marker, and `stderrBuffer` is reset per prompt instead of accumulating for the lifetime of the session. ([#13100](https://github.com/diegosouzapw/OmniRoute/pull/13100)) — thanks @anhtahaylove +- **fix(acp):** release the `stdout`/`exit` listeners and the idle timer that a `sendPrompt` timeout used to leave attached to the `acpManager` singleton, and drop sessions that exited on their own from the session map instead of keeping them forever. ([#13096](https://github.com/diegosouzapw/OmniRoute/pull/13096)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection and PII scanners now read the text a `tool_result` block carries on `content` (string or nested block list), in messages and in system blocks, so tool output is judged by the same rules as user text ([#13101](https://github.com/diegosouzapw/OmniRoute/pull/13101)) — thanks @ntdatt812 +- **fix(gamification):** close the badge notification SSE stream when the request signal is already aborted before the stream starts — a client that disconnects while the route is still awaiting auth used to leave both the 2s unlock poll and the 15s heartbeat running for the lifetime of the process. ([#13106](https://github.com/diegosouzapw/OmniRoute/pull/13106)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection scan now spends its 16 KB budget on both ends of the request instead of the first 16 KB only, so `system`, `instructions`, `query`, `documents` and the newest turns are no longer hidden behind one long message ([#13104](https://github.com/diegosouzapw/OmniRoute/pull/13104)) — thanks @ntdatt812 +- **fix(dashboard):** expose the Volcano Ark console cookie on quota scraping and unwrap connect-error objects so the dashboard shows the upstream message ([#13107](https://github.com/diegosouzapw/OmniRoute/pull/13107)) — thanks @HouMinXi +- **fix(db):** release the `beforeExit`/`SIGINT`/`SIGTERM` handlers when a `node:sqlite` adapter closes, so a closed adapter and its database handle are no longer pinned to `process` for the lifetime of the run — the same treatment #7494 gave the sql.js adapter. ([#13109](https://github.com/diegosouzapw/OmniRoute/pull/13109)) — thanks @anhtahaylove +- **fix(translator):** `contentSchema` and `unevaluatedItems` are now treated as subschema positions by the tool-schema sanitizer, so a truncation placeholder in either is replaced with a permissive schema instead of being forwarded as a string ([#13110](https://github.com/diegosouzapw/OmniRoute/pull/13110)) — thanks @ntdatt812 +- **fix(cli-helper):** clear the `createLogStream` timeout on the abort path — `stop()` aborts the in-flight fetch and returned through the `signal.aborted` branch, which skipped `clearTimeout` and left an armed timer per stopped stream. The stream reader is now also cancelled when the read loop exits early. ([#13114](https://github.com/diegosouzapw/OmniRoute/pull/13114)) — thanks @anhtahaylove +- **fix(docs):** drop TLS-impersonation, MITM-decrypt, supply-chain attestation, and XOR-mask writeups from the public `/docs` catalog and Docker image. Files stay in git for engineers; operators who need them open the repo, not the website. ([#13136](https://github.com/diegosouzapw/OmniRoute/pull/13136)) — thanks @HouMinXi +- **fix(combo):** parse numeric-epoch `rate_limited_until` in the combo cooldown read path ([#13141](https://github.com/diegosouzapw/OmniRoute/pull/13141)) — thanks @maxmad64bis +- **fix(opencode):** both OpenCode plugins now reject a gateway address typed without `http://` at configuration time, instead of publishing every model with an api url no client can call, and the v2 plugin no longer publishes a model card whose api url is blank or relative ([#13142](https://github.com/diegosouzapw/OmniRoute/pull/13142)) — thanks @maxmad64bis +- **fix(providers):** lock opencode model on upstream 400 model-unavailable ([#13146](https://github.com/diegosouzapw/OmniRoute/pull/13146)) — thanks @maxmad64bis +- **fix(logging):** keep the provider exchange rather than the raw client bodies when a call log exceeds its size budget, and show that recovered payload in the request-detail panel instead of replacing it with the stored response body ([#13147](https://github.com/diegosouzapw/OmniRoute/pull/13147)) — thanks @maxmad64bis +- **fix(traffic-inspector):** the WebSocket route no longer leaks a traffic-buffer subscriber and a 30s ping timer when the client socket is already closed at handler time — listeners are attached before any resource is acquired, a destroyed socket bails out early, and the ping interval stops on a dead socket where `write()` never throws ([#13155](https://github.com/diegosouzapw/OmniRoute/pull/13155)) — thanks @anhtahaylove +- **fix(telegram):** bound the per-user API key cache in the Telegram chat proxy so a burst of distinct chat ids can no longer grow the process heap without limit ([#13165](https://github.com/diegosouzapw/OmniRoute/issues/13165)) ([#13166](https://github.com/diegosouzapw/OmniRoute/pull/13166)) — thanks @anhtahaylove +- **fix(stream):** cancel the upstream response body when the JSON-to-SSE sniff unwinds on a body timeout, so a stalled upstream no longer pins the connection ([#13169](https://github.com/diegosouzapw/OmniRoute/issues/13169)) ([#13171](https://github.com/diegosouzapw/OmniRoute/pull/13171)) — thanks @anhtahaylove +- **fix(telegram):** authenticate webhook deliveries with Telegram's `secret_token` so an unauthenticated caller can no longer mint API keys or spend upstream quota ([#13172](https://github.com/diegosouzapw/OmniRoute/issues/13172)) ([#13175](https://github.com/diegosouzapw/OmniRoute/pull/13175)) — thanks @anhtahaylove +- **fix(models):** give discoverable Gemini 3.8 Flash ids their own 65536 output spec so Antigravity no longer clamps them to 16384 ([#13195](https://github.com/diegosouzapw/OmniRoute/pull/13195)) — thanks @HouMinXi +- **fix(dashboard):** leftover catalog-removed provider rows (gemini-cli) can be listed and purged from the providers page ([#13067](https://github.com/diegosouzapw/OmniRoute/issues/13067)) ([#13197](https://github.com/diegosouzapw/OmniRoute/pull/13197)) — thanks @HouMinXi +- **fix(auth):** a dashboard session now requires the `authenticated: true` claim that login, OIDC and the session refresh already emit — a JWT merely signed with `JWT_SECRET` (for example the Cursor CLI passthrough token, which any API-key holder can obtain) no longer verifies as the `auth_token` cookie on any route, the WebSocket handshake or the live server; existing sessions keep working ([#13298](https://github.com/diegosouzapw/OmniRoute/issues/13298)) ([#13375](https://github.com/diegosouzapw/OmniRoute/pull/13375)) +- **fix(api):** `DELETE /v1/batches/delete-completed` now applies the caller's API-key policy (endpoint allowlist, schedule, usage cap, rate limit) like every other `/v1` route, commits the key-scoped sweep per 200-batch chunk instead of holding one write lock for the whole sweep, refuses to loop without progress, and rejects a scope that names both a key and `allTenants` ([#13684](https://github.com/diegosouzapw/OmniRoute/pull/13684)) +- Restore the API-route typecheck gate: the non-streaming leg result lost its discriminated-union narrowing after the server-owned tool loop reassignment, producing 13 new TS2339 diagnostics in `chatCore.ts`. ([#12963](https://github.com/diegosouzapw/OmniRoute/pull/12963)) +- Restore four non-streaming behaviours the server-owned tool loop refactor dropped: client aborts map to 499 with the fixed `Request aborted` message, an aborted request no longer logs a synthetic `clientResponse`, Claude prompt-cache telemetry is recorded again, and a body that cannot be canonicalized no longer throws when the tool loop is off. ([#12990](https://github.com/diegosouzapw/OmniRoute/pull/12990)) +- **fix(skills):** The CLI registry parser now reads positionals declared with `.addArgument()`, not only those written inline in `.command()`. `tunnel create [type]` was being published as `tunnel create`, so the agent-skills sync gate reported drift on every branch and regenerating would have deleted the argument. ([#13009](https://github.com/diegosouzapw/OmniRoute/pull/13009)) — thanks @ntdatt812 +- **fix(combos):** dashboard combo test uses a short prompt, serial probes, and a 60s timeout so reasoning models and rate-limited free pools do not fail the health check ([#13001](https://github.com/diegosouzapw/OmniRoute/pull/13001)) — thanks @HouMinXi +- fix(compression): terminate idle worker threads on eviction so long-running instances stop leaking OS threads and MessagePorts ([#13091](https://github.com/diegosouzapw/OmniRoute/pull/13091)) — thanks @anhtahaylove +- Redact Google API keys of any length in error bodies: the pattern required exactly 39 characters, so shorter or longer `AIza…` credentials passed through unredacted. ([#12964](https://github.com/diegosouzapw/OmniRoute/pull/12964)) +- **fix(grok-cli):** a 402 "Grok Build usage balance exhausted" parks that Grok login as out of credit (Grok Build CLI, grok.com cookie, and xAI OAuth share the weekly pool). Combo routing then tries the next login instead of locking the model for every account in the pool ([#13061](https://github.com/diegosouzapw/OmniRoute/pull/13061)) — thanks @HouMinXi +- fix(compression): spawn the LLMLingua worker with a file URL object so compression actually runs instead of silently failing open on Node ([#13093](https://github.com/diegosouzapw/OmniRoute/pull/13093)) — thanks @anhtahaylove +- **fix(test):** run the local `test` and `test:unit` scripts at concurrency 4 so a full-suite run no longer exhausts the machine's commit charge and kills unrelated processes ([#13187](https://github.com/diegosouzapw/OmniRoute/pull/13187)) — thanks @anhtahaylove +- **fix(monitoring):** `GET /api/monitoring/health` `credentialHealth` now includes a bounded `failedConnections` list (`connectionId`, `status`, sanitized `lastError`) when the probe-cache gauge `failed>0`, plus `source: probe-cache` and a cheap `staleDbNonOkCount` for sticky SQLite `test_status` on active rows. Documents that the live gauge is not `provider_connections.test_status`. ([#12876](https://github.com/diegosouzapw/OmniRoute/pull/12876)) — thanks @RaviTharuma +- **fix(resilience):** an openai-compatible multi-upstream gateway no longer marks the whole connection `credits_exhausted` when a single upstream (or the `/models` / representative-model health probe) returns 402. Quota failures stay model-scoped; true connection-wide auth failures are unchanged. Single-credential 402 key disable ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239)) is preserved. ([#12875](https://github.com/diegosouzapw/OmniRoute/pull/12875)) — thanks @RaviTharuma +- **fix(dashboard):** nine fixes on the Orchestration Canvas, all diagnosed in the Phase 2 reviews. A source that fails now keeps the timestamp of its FIRST failure instead of being re-stamped every poll — the stale line said "since the last poll" no matter how long the source had been down, and the churn also defeated the snapshot's stable identity (it serializes the source list), so the canvas re-rendered on every tick while anything was broken. A source that HAD data and only then started failing is flagged too: previously only a source with no node at all got the warning, so a source that went down mid-session kept a healthy-looking node forever. The rest are pointwise: clicking a filter chip cancels the pending search debounce (left armed, it fired ~300ms later and silently reverted the chip); the search input carries an accessible name; `?state=running, failed` parses like the unpadded form instead of dropping the padded value; the CSV toggle helper is defined once in `model/urlParams.ts` rather than twice; the agents tab tells "nothing running" apart from "the filter matched nothing", offering a clear-filters button instead of setup links that would be wrong advice there; edges stop emitting SMIL particles above 40 simultaneously active edges, keeping the colored stroke; and the drawer's error banner clears when a retried action succeeds. ([#12983](https://github.com/diegosouzapw/OmniRoute/pull/12983)) +- **fix(i18n):** the nine locales added with the EU-language batch (Greek, Estonian, Irish, Croatian, Lithuanian, Latvian, Maltese, Slovenian, Serbian) were missing the eleven Orchestration Canvas keys that Phase 3 introduced, so the compare-runs panel and the "no runs match these filters" empty state fell back to English in those languages (`deepMergeFallback` substitutes English for an absent key, so nothing rendered blank — it rendered untranslated). The coverage gate does not catch this: it enforces an 80% floor per locale, and eleven missing keys out of ~13,000 leaves coverage at 99.9%. Translated for real in each language, calibrated against the wording each file already uses for "run", "filter" and "skill". ([#13294](https://github.com/diegosouzapw/OmniRoute/pull/13294)) +- **fix(dashboard):** five follow-ups from the Phase 2 Orchestration Canvas review. The History drawer now shows the memory section for runs that already left the live TTL window: the persisted `memory_hits` event is parsed into `metadata.memoryHits` with the same defensive validation the drawer applies, and — because that event is observability rather than a state transition — it no longer leaks into the timeline, where it had been inheriting the task's state and rendering as a duplicate transition. The A2A memory recall runs against its own 1.5s deadline instead of inheriting the memory backend's 30s one; overshooting degrades exactly like any other recall failure (no hits, task proceeds), and the timer is cleared on both paths. Repeating a Conductor run carries its `requirements.cli`/`requirements.model` forward, so the new run is pinned to the same runner profile and model rather than drifting to whatever the fleet picks. A successful repeat from the Agents tab now focuses the run it created, instead of leaving the operator on the finished one. And the auth test for `POST /api/conductor/tasks` moved into the shared `ROUTES` array rather than restating the pattern. ([#12988](https://github.com/diegosouzapw/OmniRoute/pull/12988)) +- fix(plugins): stop leaking an exit listener per plugin hook timeout, which triggered MaxListenersExceededWarning on plugins that ignore SIGTERM ([#13092](https://github.com/diegosouzapw/OmniRoute/pull/13092)) — thanks @anhtahaylove +- **fix(validation):** Provider node edits no longer fail with a generic "Invalid request" when the optional daily-quota reset fields are left blank. The dashboard sends `dailyQuotaResetTimezone` and `dailyQuotaResetHour` as `null`, and only the hour accepted it. ([#13066](https://github.com/diegosouzapw/OmniRoute/issues/13066)) ([#13083](https://github.com/diegosouzapw/OmniRoute/pull/13083)) — thanks @ntdatt812 +- **fix(combo):** quota-aware expansion drops banned, inactive, missing, and wrong-provider connections before quota fetch or model dispatch; pins and allowlists stay selectors, not a bypass. Antigravity automatic exhaustion now requires a reported zero remaining, so a positive balance below 1% stays eligible. ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- Fix an intermittent failure in the quota-weighted routing test suite: scores are a function of `Date.now()`, so peers with identical quota scored microseconds apart never tied and swapped order. ([#12946](https://github.com/diegosouzapw/OmniRoute/pull/12946)) +- fix(volcengine): resolve quota window mapping gaps, safe multi-connection console binding, and response cookie sanitization ([#12950](https://github.com/diegosouzapw/OmniRoute/pull/12950)) — thanks @HouMinXi +- **fix(test):** resolve the WebDAV handler path with `fileURLToPath` so the suite's 37 WebDAV tests run on Windows instead of failing with a doubled `C:\C:\` drive prefix ([#13196](https://github.com/diegosouzapw/OmniRoute/pull/13196)) — thanks @anhtahaylove +- **fix(ui):** fall back from invalid AnySearch icon alias (direct commit `9587e07b69`) +- **fix(sse):** stop rebuilding a truncated summary from the collector's cap-dropped event array ([#12718](https://github.com/diegosouzapw/OmniRoute/pull/12718)) — thanks @hartmark +- **fix(conversations):** show a pending spinner for unresolved tool nodes instead of "(empty)" ([#12727](https://github.com/diegosouzapw/OmniRoute/pull/12727)) — thanks @hartmark +- **fix(streaming):** fail fast when an upstream stream produces only lifecycle/heartbeat events, never real content ([#12741](https://github.com/diegosouzapw/OmniRoute/pull/12741)) — thanks @hartmark +- **fix(responses-continuation):** bridge the write-in-flight window with an in-memory pending store ([#12854](https://github.com/diegosouzapw/OmniRoute/pull/12854)) — thanks @hartmark +- **fix(translator):** drop or map agent_message on Chat Completions fallback ([#12880](https://github.com/diegosouzapw/OmniRoute/pull/12880)) — thanks @RaviTharuma +- **fix(api):** page and stream-complete GET /v1/models for large catalogs ([#12882](https://github.com/diegosouzapw/OmniRoute/pull/12882)) — thanks @RaviTharuma +- **fix(quota):** align AUTH window labels with usage API and clarify 503 ([#12884](https://github.com/diegosouzapw/OmniRoute/pull/12884)) — thanks @RaviTharuma +- **fix(ci):** clear two base-reds on release/v3.8.51 — mutation-coverage gate + image-only-model guard ([#12945](https://github.com/diegosouzapw/OmniRoute/pull/12945)) +- **fix(api):** scope batch bulk-delete to the calling API key ([#13211](https://github.com/diegosouzapw/OmniRoute/pull/13211)) +- **fix(tests):** align model-sync log assertion to shipped path redaction (base-red) ([#13227](https://github.com/diegosouzapw/OmniRoute/pull/13227)) +- **fix(test):** make the ToS heading guard actually require the parentheses ([#13228](https://github.com/diegosouzapw/OmniRoute/pull/13228)) +- **fix(quality):** register 4 drifted covering tests in stryker tap.testFiles ([#13229](https://github.com/diegosouzapw/OmniRoute/pull/13229)) +- **fix(logging):** mask *-api-key headers in request-log pipeline (#13273) ([#13401](https://github.com/diegosouzapw/OmniRoute/pull/13401)) — thanks @KooshaPari +- **fix(tests):** correct stale Jina catalog prefix in custom-models test ([#13403](https://github.com/diegosouzapw/OmniRoute/pull/13403)) — thanks @KooshaPari +- **fix(backend):** prune old backups after health-check-repair snapshots (#13308) ([#13404](https://github.com/diegosouzapw/OmniRoute/pull/13404)) — thanks @KooshaPari +- **fix(backend):** guard memory_fts_au trigger with WHEN clause to stop FTS bloat (#13326) ([#13405](https://github.com/diegosouzapw/OmniRoute/pull/13405)) — thanks @KooshaPari +- **fix(free-tiers):** remove obsolete Together $25 signup credit (#12526) ([#13407](https://github.com/diegosouzapw/OmniRoute/pull/13407)) — thanks @KooshaPari +- **fix:** skip non-chat generation models in Test all models ([#13409](https://github.com/diegosouzapw/OmniRoute/pull/13409)) — thanks @KooshaPari +- **fix(providers):** de-duplicate agy and antigravity model catalogs ([#13410](https://github.com/diegosouzapw/OmniRoute/pull/13410)) — thanks @KooshaPari +- **fix(dashboard):** send provider in body for mini-playground web search ([#13411](https://github.com/diegosouzapw/OmniRoute/pull/13411)) — thanks @KooshaPari +- **fix(resilience):** add CJK quota-exhaustion patterns to 429 classifier ([#13412](https://github.com/diegosouzapw/OmniRoute/pull/13412)) — thanks @KooshaPari +- **fix(backend):** eval runner: failed calls no longer score as passed + dotAll regex ([#13413](https://github.com/diegosouzapw/OmniRoute/pull/13413)) — thanks @KooshaPari +- **fix(backend):** eval runner opts out of output-style and memory injection ([#13414](https://github.com/diegosouzapw/OmniRoute/pull/13414)) — thanks @KooshaPari +- **fix:** chain serial quarantine step in npm test script (fixes #13286) ([#13418](https://github.com/diegosouzapw/OmniRoute/pull/13418)) — thanks @KooshaPari +- **fix(antigravity):** derive stable session ID from credentials for prompt-cache reuse (#13305) ([#13424](https://github.com/diegosouzapw/OmniRoute/pull/13424)) — thanks @KooshaPari +- **fix(memory):** merge memory into top-level system field instead of unshifting at messages[0] (#13425) ([#13427](https://github.com/diegosouzapw/OmniRoute/pull/13427)) — thanks @KooshaPari +- **fix(compression):** preserve instruction blocks from lossy engines ([#13523](https://github.com/diegosouzapw/OmniRoute/pull/13523)) — thanks @KooshaPari +- **fix(compression):** preserve polarity words and newlines in ultra heuristic ([#13524](https://github.com/diegosouzapw/OmniRoute/pull/13524)) — thanks @KooshaPari +- **fix(compression):** run user preservePatterns before built-in patterns (#13457) ([#13525](https://github.com/diegosouzapw/OmniRoute/pull/13525)) — thanks @KooshaPari +- **fix(reasoning):** expand deepseek-v4 regex to match v4.1+ and add command-code provider (#13430) ([#13528](https://github.com/diegosouzapw/OmniRoute/pull/13528)) — thanks @KooshaPari +- **fix(providers):** add alwaysReasons metadata for reka-flash-3 ([#13531](https://github.com/diegosouzapw/OmniRoute/pull/13531)) — thanks @KooshaPari +- **fix(playground):** allow Compare columns to scroll independently ([#13532](https://github.com/diegosouzapw/OmniRoute/pull/13532)) — thanks @KooshaPari +- **fix(security):** empty whitelist allows through to prevent admin lockout ([#13534](https://github.com/diegosouzapw/OmniRoute/pull/13534)) — thanks @KooshaPari +- **fix(tests):** add DISABLE_IOREG_STRATEGY env var for macOS test compatibility (fixes #13467) ([#13539](https://github.com/diegosouzapw/OmniRoute/pull/13539)) — thanks @KooshaPari +- **fix(zed-hosted):** map developer role and Gemini enums for Zed proxy ([#13543](https://github.com/diegosouzapw/OmniRoute/pull/13543)) — thanks @KooshaPari +- **fix(analytics):** normalize provider aliases to canonical id in usage_history ([#13545](https://github.com/diegosouzapw/OmniRoute/pull/13545)) — thanks @KooshaPari +- **fix(combo):** use traceId for call log id to prevent UNIQUE constraint failure ([#13546](https://github.com/diegosouzapw/OmniRoute/pull/13546)) — thanks @KooshaPari +- **fix(auth):** check model lockout before returning synthetic noauth connection ([#13547](https://github.com/diegosouzapw/OmniRoute/pull/13547)) — thanks @KooshaPari +- **fix(guardrails):** skip credential redaction for base64 image data URLs ([#13550](https://github.com/diegosouzapw/OmniRoute/pull/13550)) — thanks @KooshaPari +- **fix(resilience):** increase requestQueue.maxWaitMs default from 15s to 30s ([#13553](https://github.com/diegosouzapw/OmniRoute/pull/13553)) — thanks @KooshaPari +- **fix(docs):** document OMNIROUTE_READY_TIMEOUT_MS and allowlist the test-only DISABLE_IOREG_STRATEGY ([#13692](https://github.com/diegosouzapw/OmniRoute/pull/13692)) + ### 📝 Maintenance ---- +- **docs(readme):** reconcile live v3.8.50 provider, free-tier, CLI, routing, test, community, sponsor, acknowledgment, and SVG metrics with their audited source denominators, including a deduplicated OmniRoute-in-Action snapshot and distinct contributor rankings for merged pull requests, GitHub-attributed commits, and Git history ([#11356](https://github.com/diegosouzapw/OmniRoute/pull/11356)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **docs:** sync the canonical provider count 354 → 356 across `README.md`, `AGENTS.md`, `llm.txt` (+ 42 i18n mirrors), the four README SVG diagrams, `docs/reference/PROVIDER_REFERENCE.md` (regenerated) and the `package.json` description after Opper (#11629) and 1min.ai (#11631) boarded the catalog — closes the `check:docs-counts-sync` strict drifts that kept `release/v3.8.51` red ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11673](https://github.com/diegosouzapw/OmniRoute/pull/11673)) — thanks @jonlwheat2-gif +- **test(providers):** update count-derived assertions after the v3.8.51 provider additions — `APIKEY_PROVIDERS` 233 → 235 (Opper #11629 + 1min.ai #11631), reserved-prefix REGISTRY walk 395 → 398, `WEB_FETCH_PROVIDERS` now includes `nimble-search` (#11620), and the provider translate-path golden snapshot regenerated ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11674](https://github.com/diegosouzapw/OmniRoute/pull/11674)) — thanks @jonlwheat2-gif +- **test(sse):** bump the hard-lease connection-query inventory for `src/lib/tokenHealthCheck.ts` to 2 — the verify-only web-cookie sweep added by #11495 split the single `getProviderConnections` call into oauth + cookie variants, which the frozen inventory had not tracked ([#11495](https://github.com/diegosouzapw/OmniRoute/pull/11495)). ([#11677](https://github.com/diegosouzapw/OmniRoute/pull/11677)) — thanks @jonlwheat2-gif / @oyi77 +- **chore(release):** clear the `release/v3.8.51` base-red gates — docs migration count (+38 i18n mirrors), stryker `tap.testFiles`, lockfile registry host, stale ESLint suppressions, the 7 lint regressions they were hiding, the stale `APIKEY_PROVIDERS` partition count (231 → 233), the `glm-5.3-max` tier missing from the GLM test table, a dead `modelType === "chat"` comparison in the models catalog, and the `resolveLiveWsUrl` / `sanitizeLiveWsPort` helpers from #11388 that a later merge dropped from `wsPath.ts` (dashboard live WebSocket URL resolution) ([#11502](https://github.com/diegosouzapw/OmniRoute/pull/11502)) — thanks @MumuTW +- **chore(free-models):** the free-tier regime table now declares which totals figure each regime feeds and whether it may take the no-auth shortcut, so the sets that used to repeat those answers by hand are derived from it — a new `freeType` no longer compiles until it has answered every question, instead of silently contributing to no total ([#11537](https://github.com/diegosouzapw/OmniRoute/pull/11537)) — thanks @maxmad64bis +- **docs(routing):** LKGP strategy copy now states provider-level stickiness and the explicit rules fallback instead of promising per-target pinning ([#11555](https://github.com/diegosouzapw/OmniRoute/pull/11555)) — thanks @maxmad64bis +- **test(chatcore):** pin the response tool-name alias resolution against the ordering hazard that broke every Gemini/Antigravity MCP tool call in v3.8.49. `extractRequestToolIdentityMap` deletes `translatedBody._toolNameMap`, so the later read is always undefined and the ledger survives only through the `requestToolIdentityMap` fallback — removing that fallback previously left the entire tool-name suite green. The resolution moves into `resolveResponseToolNameMap()` next to the map it depends on, with a regression guard that fails without the recovery. No behaviour change. ([#11575](https://github.com/diegosouzapw/OmniRoute/pull/11575)) — thanks @amartinawi +- **docs(env):** document `OMNIROUTE_CORPUS_CACHE_SIZE` in `.env.example` and `ENVIRONMENT.md`, so the env/docs contract check stops failing Docs Gates on every branch ([#11576](https://github.com/diegosouzapw/OmniRoute/pull/11576)) — thanks @ntdatt812 +- **chore(stryker):** register `route-guard-tunnel-processes-local-only` and `universal-quota-aware-routing` in `tap.testFiles`, so their mutant kills count for the three modules they cover ([#11577](https://github.com/diegosouzapw/OmniRoute/pull/11577)) — thanks @ntdatt812 +- **test(authz):** pin `LOCAL_ONLY_API_GET_EXEMPTIONS` by exact membership instead of by entry count, so the guard names the offending path and also catches a substitution ([#11580](https://github.com/diegosouzapw/OmniRoute/pull/11580)) — thanks @ntdatt812 +- **test(build):** pin `resolveNextBuildBundlerFlag` to the `OMNIROUTE_USE_TURBOPACK` contract it implements, across runtimes, instead of asserting an unimplemented Bun override ([#11583](https://github.com/diegosouzapw/OmniRoute/pull/11583)) — thanks @ntdatt812 +- **test(claude-web):** the Claude Web transport test's barrel anchors no longer pin the exact construction spelling. #11421 made the executor map lazy, so `/"claude-web": new ClaudeWebExecutor\(\)/` stopped matching and the test went red on `release/v3.8.51` itself — a base-red gate every open PR inherited. The anchors now match the alias key and the class name and tolerate either spelling in between, and were checked to still fail when an alias is genuinely repointed at another executor ([#11585](https://github.com/diegosouzapw/OmniRoute/pull/11585)) — thanks @ntdat812 +- **test(models):** pin `qwen3.8-max` resolution per provider — two of the four catalogs now serve the bare id and no longer carry `-preview` ([#11590](https://github.com/diegosouzapw/OmniRoute/pull/11590)) — thanks @ntdatt812 +- **test(executors):** drop the stale 128-tool truncation assertion that contradicted the #11444 pass-through contract ([#11591](https://github.com/diegosouzapw/OmniRoute/pull/11591)) — thanks @ntdatt812 +- **test(token-health):** pin the current terminal-skip boundary — expired with retry budget is probed, `account_deactivated` stays skipped ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)) — thanks @ntdatt812 +- **test(models):** follow #11503's retarget of the `gemini-3-pro-high` alias to the hyphenated catalog id ([#11593](https://github.com/diegosouzapw/OmniRoute/pull/11593)) — thanks @ntdatt812 +- **refactor(video bridge):** extract per-video acquisition, whole-result caching, description, and metrics/abort/cleanup out of `VideoBridgeGuardrail.preCall` into a `processVideoPart` seam in a new `videoBridgePipeline.ts`, behind explicit `VideoMediaBrokerPort`, `VideoAudioTranscriptionPort`, and `VideoDrilldownPort` boundaries; `preCall` now only handles request traversal, policy, and response aggregation. The Video tab's FFmpeg/ffprobe runtime status is now an explicit `unknown` / `restricted` / `unavailable` / `available` state instead of a nullable boolean pair, fixing a case where an in-flight or failed probe was mislabeled as "install FFmpeg" ([#11657](https://github.com/diegosouzapw/OmniRoute/issues/11657)). ([#12016](https://github.com/diegosouzapw/OmniRoute/pull/12016)) +- **test(opencode-plugin):** add unit test coverage for `formatFreeBudget()` naming helper ([#11660](https://github.com/diegosouzapw/OmniRoute/pull/11660)) — thanks @f9td56dbgh-hub +- **docs(video):** clarify that the Video Bridge transcript `source` field (`client`, `embedded`, `audio-bridge`) is presently caller-declared and not yet server-verified — OmniRoute enforces the enum shape but does not cryptographically confirm that an `embedded`/`audio-bridge` label came from a server-owned extraction ([#11661](https://github.com/diegosouzapw/OmniRoute/issues/11661)). ([#12001](https://github.com/diegosouzapw/OmniRoute/pull/12001)) +- **chore(providers):** retire the distributed Raycast Relay and Hailuo Web integrations after a provenance audit confirmed substantial derivation from GPL-3.0 sources; the official GitHub and MiniMax API providers remain available. Migration 163 disables legacy connections and invalidates their active leases while preserving connection IDs, audit/history rows, quota snapshots, usage records, and fail-closed API-key connection policies. ([#11691](https://github.com/diegosouzapw/OmniRoute/pull/11691)) +- **chore(providers):** retire the Felo Web provider while its GPL-derived provenance remains on licensing HOLD ([#11698](https://github.com/diegosouzapw/OmniRoute/pull/11698)) — stale runtime and database references now fail closed while existing connection, lease, and usage history remains preserved for auditability. +- **fix(sse):** retire the Gemini Web image-generation bridge while provenance and licensing remain on HOLD, preserving Gemini Web chat/auth/tools and legitimate Gemini/Nano Banana image providers ([#11708](https://github.com/diegosouzapw/OmniRoute/pull/11708)) +- **chore(audio):** retire the built-in EdgeTTS provider and its 11-voice catalog while provenance/licensing review remains on HOLD; the generic `/v1/audio/speech` endpoint and the other speech providers remain available ([#11711](https://github.com/diegosouzapw/OmniRoute/pull/11711)) +- **fix(providers):** retire the unofficial Qwen Web integration and its `qwen-web`/`qw` identities while third-party provenance remains on HOLD ([#11713](https://github.com/diegosouzapw/OmniRoute/pull/11713)) — direct requests now fail closed with a sanitized HTTP 410 and combos skip the retired target, while migration guards preserve connection IDs, allowlists, usage/audit history and invalidate active leases. Supported Qwen API, Qwen Code, cloud, token-plan, audio, local and model-name surfaces remain available; restoring Qwen Web requires a qualified provenance/license decision. +- **fix(providers):** retire `microsoft-designer-web` and its `msdesigner` alias from runtime and catalogs while preserving stored credentials, provider history and allowlists as inactive records — source dependency on gpt4free's [`MicrosoftDesigner.py`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/g4f/Provider/needs_auth/MicrosoftDesigner.py) is proven at snapshot `0943d16eef757a44deb60651c07bbe5fa958cafe`, whose [`LICENSE`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/LICENSE) is GPL-3.0; expressive adaptation is probable, which records provenance/licensing risk without asserting a legal conclusion ([#11720](https://github.com/diegosouzapw/OmniRoute/pull/11720)). +- **docs(legal):** centralize pinned MIT notices for the vendored GCF codec and local flag assets ([#11726](https://github.com/diegosouzapw/OmniRoute/pull/11726)) +- Split the 2,952-line `adobeFireflyClient.ts` into nine focused modules (catalog, credentials, payload, ARP session, upload, responses, IMS, account, poll) behind an unchanged barrel — no consumer edits and no public API change (#11728). — thanks @MumuTW +- **fix(dashboard):** replaced the Model Lockout card's unprovenanced notification MP3 with a short, locally synthesized Web Audio chime ([#11731](https://github.com/diegosouzapw/OmniRoute/pull/11731)) — both toggles retain optional audible feedback without shipping replacement media or third-party code, and unsupported, suspended, or rejected audio contexts fail open without blocking the setting change. +- **chore(deepseek-web):** remove the provenance-unresolved DeepSeek PoW WASM binary and its runtime loader/tracing while retaining the existing JavaScript solver (slower at high difficulty) ([#11732](https://github.com/diegosouzapw/OmniRoute/pull/11732)). +- **chore(assets):** remove four unused provider assets whose redistribution provenance could not be verified while retaining the active Freebuff SVG ([#11735](https://github.com/diegosouzapw/OmniRoute/pull/11735)) +- **docs(assets):** retired two raster images whose redistribution provenance could not be verified ([#11736](https://github.com/diegosouzapw/OmniRoute/pull/11736)) — PicoClaw documentation now reuses the existing SVG, while Zoo Code remains a text link without an image. +- **docs(legal):** add pinned notices for six LobeHub-derived provider SVGs and 65 byte-exact theSVG assets, retaining registry claims as unverified metadata with explicit trademark and HOLD boundaries ([#11737](https://github.com/diegosouzapw/OmniRoute/pull/11737)). +- **chore(quality):** added a versioned 225/225 provider-asset provenance manifest and blocking drift gate ([#11743](https://github.com/diegosouzapw/OmniRoute/pull/11743)) — the inventory pins magic MIME and SHA-256 for every physical file, records immutable source matches for 71 assets, keeps 69 probable and 85 unresolved entries explicit but non-blocking, and treats provenance as source evidence rather than copyright or trademark clearance. +- **refactor(mitm):** replace four previously derived Traffic Inspector utilities — conversation normalization, SSE merging, secret masking, and header sanitization — with independent clean-room implementations based on public provider schemas, WHATWG SSE, and RFC 6750, while keeping the two independently classified passthrough surfaces unchanged ([#11748](https://github.com/diegosouzapw/OmniRoute/pull/11748)). +- Added a unit test that fails seven days before any dated pack under `config/` (`validUntil` and sibling keys) lapses, naming the file and key. The Alibaba free-tier pack expired on 2026-08-27 and turned every PR red the next morning with no commit involved; renewal now happens on someone's terms, not the clock's. ([#11891](https://github.com/diegosouzapw/OmniRoute/pull/11891)) +- `check:workflows` now fails (under `--strict`/`--ratchet`) when any job routed to a self-hosted runner publishes with `--provenance` — npm rejects that with `422` at the registry, which in v3.8.50 only surfaced after the tag and Docker images were public. ([#11895](https://github.com/diegosouzapw/OmniRoute/pull/11895)) +- `scripts/ops/runner-janitor.sh` now proves a path is idle with one `lsof` snapshot and removes stale leftovers itself (tmpfs after 3 h — it is RAM — disk after 24 h), kills orphan `next-build` processes, prunes checkouts of stopped runners, and alerts on memory pressure; `--dry-run` shows exactly what it would do. `docs/ops/RUNNER_BOX.md` reconciled to the measured box (31 GB, 10 listeners). ([#11893](https://github.com/diegosouzapw/OmniRoute/pull/11893)) +- The `next-build` artefact (1.3 GB) is now written and read under `$RUNNER_TEMP` (per-runner, on disk) instead of `/tmp`, which on the self-hosted pool is a 12 GB tmpfs in RAM. Landing it there took 27–32 of the publish job's 76 minutes, and the fixed `/tmp/e2e-build.tar.gz` name let E2E jobs on different runners overwrite each other's download. ([#11896](https://github.com/diegosouzapw/OmniRoute/pull/11896)) +- The CI `build` job now runs in two concurrency lanes — `main` and pull requests — so a release build is never queued behind (or OOM-killed beside) PR builds on the self-hosted pool, which holds one `next-build` comfortably and two at the edge. ([#11901](https://github.com/diegosouzapw/OmniRoute/pull/11901)) +- Re-freeze the ESLint suppressions on `release/v3.8.51` from a clean-room measurement (2 stale file entries pruned, 55 pre-existing `no-explicit-any` in six new files frozen under #11924) and drop the dead `GPT_SIZE_MAP` constant orphaned by the Adobe Firefly client split, so `No new ESLint warnings` stops failing every PR with exit 2 (Refs #11924) ([#11955](https://github.com/diegosouzapw/OmniRoute/pull/11955)) +- Type the 55 `no-explicit-any` sites that had been frozen under #11924 — four redundant casts in `socksConnectorWithFamily.ts` (undici/socks types already accept them) and the mocks/fixtures of the socks-timeout and isFree suites — and drop their suppression entries; the ESLint ratchet shrinks from 5487 to 5432 (Closes #11924) ([#11975](https://github.com/diegosouzapw/OmniRoute/pull/11975)) +- Take the two hosted-runner builds off the PR rail: `Build App` (`build.yml`) is `workflow_dispatch`-only and `DAST smoke (PR)` runs only for PRs into `main` — the 7 GB hosted VM cannot build `release/v3.8.51` in any profile (19/30 red, VM shutdown ~8 min into `next build`) and both checks had turned into permanent noise on every release PR; the bundle stays validated by `ci.yml` on `main` and by `nightly-release-green` on `release/**` (Closes #11946) ([#11962](https://github.com/diegosouzapw/OmniRoute/pull/11962)) +- Move the four nightly jobs that build the backend (`nightly-schemathesis`, `nightly-llm-security` promptfoo + garak, `nightly-resilience` axe-a11y) off the hosted 7 GB runner — where they died on `release/v3.8.51` unseen — onto the box's new `omni-light` pool (two listeners), and document the reshaped fleet (4 active OmniRoute listeners: 2 `omni-build` + 2 `omni-light`, janitor ceiling 4) (Closes #11965) ([#11967](https://github.com/diegosouzapw/OmniRoute/pull/11967)) +- Stop painting every fork PR into `release/**` red: `quality.yml` `Build (advisory)` is skipped (GitHub still reports `continue-on-error` failures as check FAILURE). Hosted `ubuntu-latest` cannot finish `npm run build` on this tree — same class as #11962 taking `build.yml` off the PR rail. `docker-publish.yml` amd64 now runs on the `.113` `omni-build` pool (31 GB / 32 cores, two listeners) with Turbopack, shares the `heavy-build-main` lane with `ci.yml` `Build` so it queues instead of becoming a third heavy, and keeps per-ref concurrency (a merge storm was starting 8 concurrent OOM builds). arm64 stays on `ubuntu-24.04-arm` with webpack — there is no ARM box. Fallback when `USE_VPS_RUNNER` is off: hosted amd64 + webpack (#11976). ([#12021](https://github.com/diegosouzapw/OmniRoute/pull/12021)) +- `docker-publish.yml` builds with webpack (`OMNIROUTE_USE_TURBOPACK=0`) on **both** arches, including amd64 on the `.113` `omni-build` pool. Turbopack had the RAM (31 GB, `omniroute-113-6`) and still panicked (`TurbopackInternalError: there must be a path to a root`, run 33253576569, 26 min in). The same tree's arm64 webpack build on hosted `ubuntu-24.04-arm` succeeded (run 33264823398). Dockerfile already documents webpack as the Docker escape hatch (#11976). ([#12050](https://github.com/diegosouzapw/OmniRoute/pull/12050)) +- **docs(free-tier):** declare the counting vs deciding regimes for "is it free?" and guard the deciding path from DB-backed catalog resolution ([#12226](https://github.com/diegosouzapw/OmniRoute/pull/12226)) — thanks @maxmad64bis +- **docs(auto-combo):** The mode pack table in `docs/routing/AUTO-COMBO.md` now lists all six shipped packs with every weight each one sets, replacing a four-pack table whose numbers had also drifted from the source. It states plainly that no pack sets `quality`, so selecting any pack silences the observed-quality signal. Six more documents that quote the scoring factor count joined the `check:docs-counts` gate, which caught five stale claims — including one naming nine factors that do not exist — and two stale mode pack counts. The dashboard routing panel, which offered four of the six packs and labelled the default strategy "6-Factor Scoring", is now covered by a test; the two packs it was missing are `reliability-first` and `chaos-mode`, the latter labelled as the fault-injection profile it is rather than as one more routing preference ([#12316](https://github.com/diegosouzapw/OmniRoute/pull/12316)) — thanks @maxmad64bis +- **chore(quality):** rebaseline `src/lib/db/apiKeys.ts` for the ACL the key-creation path now preserves ([#12352](https://github.com/diegosouzapw/OmniRoute/pull/12352)) ([#12673](https://github.com/diegosouzapw/OmniRoute/pull/12673)) — thanks @KrzysiekSko +- **chore(providers):** bump the Claude Code wire identity and the Devin bridge image pin from `2.1.220` to `2.1.258` ([#12402](https://github.com/diegosouzapw/OmniRoute/pull/12402)) — thanks @ggiak ([#12604](https://github.com/diegosouzapw/OmniRoute/pull/12604)) +- **docs(env):** align `.env.example`, the README Bun section, and the troubleshooting guide with the code: `OMNIROUTE_USE_TURBOPACK` also governs `npm run build` (not dev-only), `bun run build` follows that flag instead of auto-selecting Webpack, `OMNIROUTE_CHAT_MAX_HEAVY_IN_FLIGHT` is unset by default (no request-count cap), and the structural `503 chat_admission_busy` message matches `chatAdmissionResponses.ts` (#12404 — thanks @pacocartones) +- **chore(electron):** upgrade the desktop app to Electron 44 (Chromium 152, Node 24.18.1) ([#12217](https://github.com/diegosouzapw/OmniRoute/pull/12217)). **Requires macOS 13 (Ventura) or later** — Chromium dropped macOS 12 (Monterey), so Monterey users must stay on an earlier OmniRoute desktop build. Windows and Linux are unaffected; the app already shipped only x64/arm64, so Electron 44 dropping 32-bit builds changes nothing. Removes the `openAsHidden`/`wasOpenedAsHidden` login-item fields deleted in Electron 44 — hidden autostart continues to work through the `--hidden` argument registered with the login item ([#12554](https://github.com/diegosouzapw/OmniRoute/pull/12554)) — thanks @app/dependabot +- **chore(quality):** rebaseline `src/sse/handlers/chat.ts` for the effective-input persistence the continuation fix needs ([#12641](https://github.com/diegosouzapw/OmniRoute/pull/12641)) ([#12680](https://github.com/diegosouzapw/OmniRoute/pull/12680)) — thanks @hartmark +- `Coverage` job on `ci.yml`: the informational Codecov upload gets its own 5-minute ceiling and `continue-on-error`, and the job budget grows from 20 to 30 minutes (the 8-shard c8 merge alone takes ~10) — a stalled upload no longer ends the job `cancelled` and drags a fully green `main` run's conclusion down with it ([#11972](https://github.com/diegosouzapw/OmniRoute/pull/11972)) +- CI hardening on the PR rail: the four `Unit Tests fast-path` shards get `timeout-minutes: 30` (a hung shard held a PR for 64 min instead of GitHub's 6 h default) and both ESLint file caches lose their `restore-keys` fallback, so a cache built under another suppressions file or lint config can no longer report stale verdicts (Refs #11600, #11924) ([#11963](https://github.com/diegosouzapw/OmniRoute/pull/11963)) +- Every CI job that runs a `next build` (`build`, the npm `publish`, both release-green validations) now targets the `omni-build` runner label, which only two of the eight self-hosted runners carry. The box holds one build comfortably and two at the edge; a third now queues on GitHub instead of being OOM-killed by the kernel. ([#11932](https://github.com/diegosouzapw/OmniRoute/pull/11932)) +- **refactor(combo):** move `handleRoundRobinCombo` (and `resolveTargetTokenLimit`) into `open-sse/services/combo/roundRobinCombo.ts`. `combo.ts` drops from 2164 to 1014 lines (`split("\n").length`); the leaf is 1199 (under the 1200 new-file cap). The round-robin call site uses a dynamic `import()` so `releaseStickyPinOnFailure` / `clearStaleLKGP` can stay exported from `combo.ts` without a static cycle. Skip / sticky / semaphore / safety-timer behavior is unchanged. ([#12811](https://github.com/diegosouzapw/OmniRoute/pull/12811)) — thanks @HouMinXi +- **chore(quality):** rebaseline the file-size caps the error-boundary campaign grew past (`open-sse/executors/codex.ts`, `open-sse/vendor/codex-chatgpt-web/bridge.ts`, both via [#12444](https://github.com/diegosouzapw/OmniRoute/pull/12444)) ([#12654](https://github.com/diegosouzapw/OmniRoute/pull/12654)) +- **chore(quality):** rebaseline the file-size caps the hartmark batch grew past (`combos/page.tsx` via [#12355](https://github.com/diegosouzapw/OmniRoute/pull/12355), `open-sse/services/combo.ts` via [#12338](https://github.com/diegosouzapw/OmniRoute/pull/12338)) ([#12623](https://github.com/diegosouzapw/OmniRoute/pull/12623)) — thanks @hartmark +- **chore(quality):** rebaseline the file-size caps the HouMinXi batch grew past when its PRs stacked (`providers/page.tsx`, `chatCore.ts`, `accountFallback.ts`) — each PR measured correctly in isolation, none saw the stacking ([#12619](https://github.com/diegosouzapw/OmniRoute/pull/12619)) +- **chore(quality):** rebaseline `open-sse/services/combo.ts` for the reset-aware scoring the HouMinXi batch stacked ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) ([#12678](https://github.com/diegosouzapw/OmniRoute/pull/12678)) — thanks @HouMinXi +- **docs:** sync provider catalog count (direct commit `4f11b2ae3d`) +- **docs(skills):** sync omni-inference search fallback (direct commit `b4e76a7ed9`) +- **chore(lint):** prune two stale base suppressions (direct commit `1325047d56`) +- **test(auto-combo):** tolerate full-suite rotation load (direct commit `ad3e293f9f`) +- **test:** drain inherited quality snapshot reds (direct commit `21ed68d8ac`) +- **chore(quality):** rebaseline measured CLI bundle growth (direct commit `d35b3f9779`) +- **test(ui):** reproduce invalid AnySearch icon alias (direct commit `12cc6ca834`) +- **test(translator):** fix the relative imports of the relocated deferred-finish test (direct commit `777d9d1629`) +- **docs(counts):** bring the documented provider and migration counts to the measured values (direct commit `e70bea30e9`) +- **test(security):** drop the guard-target set left unused by the sync (direct commit `a32ba0e362`) +- **chore(db):** renumber model_capabilities migration 163 → 169 (cross-branch collision with 163_radar_feed_cache_generated_at) (direct commit `70f5d4cbf9`) +- **docs(cli):** document CLI_PRIME_AGENT_BIN and correct the CLI Agents count (direct commit `9058e39b61`) +- **chore(deps):** refresh runtimes and adopt ESLint 10 ([#11259](https://github.com/diegosouzapw/OmniRoute/pull/11259)) — thanks @backryun +- **refactor(local-corpus):** implement dynamic root resolution and LRU cache ([#11491](https://github.com/diegosouzapw/OmniRoute/pull/11491)) — thanks @raheemuddin786 +- **chore(quality):** prune stale eslint-suppressions entries ([#11596](https://github.com/diegosouzapw/OmniRoute/pull/11596)) +- **chore(ci):** unblock quality gates - env-doc sync + stryker tap.testFiles registration (#9282) ([#11609](https://github.com/diegosouzapw/OmniRoute/pull/11609)) — thanks @Meet6338-X +- **chore(ci):** point the circular-deps gate at dpdm's real JS entrypoint ([#11615](https://github.com/diegosouzapw/OmniRoute/pull/11615)) — thanks @benzntech +- **chore(quality):** rebaseline file-size for #11448/#11495/#11561 growth ([#11630](https://github.com/diegosouzapw/OmniRoute/pull/11630)) +- **deps:** realign onnxruntime-node pin to @huggingface/transformers' 1.24.3 lockstep ([#11633](https://github.com/diegosouzapw/OmniRoute/pull/11633)) — thanks @jonlwheat2-gif +- **test(autoCombo):** port models_dev_tier #11508 guard from node:test to vitest ([#11635](https://github.com/diegosouzapw/OmniRoute/pull/11635)) — thanks @jonlwheat2-gif +- **chore(quality):** rebaseline file-size for #11083/#11631/#11628 growth ([#11636](https://github.com/diegosouzapw/OmniRoute/pull/11636)) +- **chore(lint):** prune stale ESLint suppressions (base-red #11449) ([#11644](https://github.com/diegosouzapw/OmniRoute/pull/11644)) — thanks @jonlwheat2-gif +- **docs(changelog):** aggregate the ten v3.8.50 fragments into CHANGELOG.md ([#11683](https://github.com/diegosouzapw/OmniRoute/pull/11683)) +- **chore(search):** drop unreachable credential sweep after #11642 ([#11687](https://github.com/diegosouzapw/OmniRoute/pull/11687)) — thanks @HouMinXi +- **ci:** add API route TypeScript regression gate ([#11705](https://github.com/diegosouzapw/OmniRoute/pull/11705)) — thanks @Sabeekhann +- **docs(dedup):** correct the CodeQL claim on the tenant-namespace comment ([#11712](https://github.com/diegosouzapw/OmniRoute/pull/11712)) +- **docs(changelog):** add consolidated v3.8.50 stats, top-25 ranking and a .mailmap ([#11715](https://github.com/diegosouzapw/OmniRoute/pull/11715)) +- **docs:** recommend gstack for AI-assisted workflows ([#11770](https://github.com/diegosouzapw/OmniRoute/pull/11770)) — thanks @santosraju99-hub +- **chore(quality):** tighten complexity/cognitive-complexity ratchets to the current tip; land the missed gateways.ts rebaseline ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **docs(i18n):** finish Freepik → Magnific rebrand in locale strings and README ([#11772](https://github.com/diegosouzapw/OmniRoute/pull/11772)) — thanks @turbolego +- **refactor(auto-combo):** fix divergent scoring in combo health reporting ([#11854](https://github.com/diegosouzapw/OmniRoute/pull/11854)) — thanks @jacobsparts +- **chore(changelog):** add missing fragments for #11919/#11918/#11916 ([#11938](https://github.com/diegosouzapw/OmniRoute/pull/11938)) +- **test(check):** escape the runs-on fixture with JSON.stringify, not a quote-only replace ([#11942](https://github.com/diegosouzapw/OmniRoute/pull/11942)) (direct commit `fb7445eaa3`) +- **test(release):** align five suites with the contracts #11933, #11919 and #11876 shipped on release/v3.8.51 ([#11944](https://github.com/diegosouzapw/OmniRoute/pull/11944)) +- **chore(quality):** remove unreachable code and restore test discovery ([#11950](https://github.com/diegosouzapw/OmniRoute/pull/11950)) — thanks @backryun +- **docs(agents):** sync-back landings are fast-forward, never squash ([#11964](https://github.com/diegosouzapw/OmniRoute/pull/11964)) +- **test(infra):** retry recursive temp-dir removal instead of failing a shard on ENOTEMPTY (#11966) ([#11968](https://github.com/diegosouzapw/OmniRoute/pull/11968)) +- **docs(ops):** the .113 heavy-build ceiling is one runner, not two ([#12048](https://github.com/diegosouzapw/OmniRoute/pull/12048)) +- **test(ui):** correct inactive auto-fetch expectation ([#12098](https://github.com/diegosouzapw/OmniRoute/pull/12098)) — thanks @RaviTharuma +- **docs:** document native dependency check escape hatch ([#12101](https://github.com/diegosouzapw/OmniRoute/pull/12101)) — thanks @RaviTharuma +- **test(providers):** regenerate the translate-path golden for OrcaRouter (#11923) ([#12118](https://github.com/diegosouzapw/OmniRoute/pull/12118)) +- **chore(quality):** velocity phase — loosen every numeric baseline by 20% until v4.0, monitor headroom nightly ([#12125](https://github.com/diegosouzapw/OmniRoute/pull/12125)) +- **chore(lint):** batch 0 of #12146 — type the call-log-cap sqlite rows instead of 45 as-any casts ([#12157](https://github.com/diegosouzapw/OmniRoute/pull/12157)) +- **chore(lint):** batch 4 of #12146 — shared/components react-hooks violations resolved ([#12159](https://github.com/diegosouzapw/OmniRoute/pull/12159)) +- **chore(lint):** batch 1 of #12146 — dashboard/cli-code react-hooks violations resolved ([#12160](https://github.com/diegosouzapw/OmniRoute/pull/12160)) +- **chore(lint):** batch 3 of #12146 — dashboard/settings react-hooks violations resolved ([#12162](https://github.com/diegosouzapw/OmniRoute/pull/12162)) +- **chore(lint):** batch 2 of #12146 — resolve the react-hooks compiler violations in dashboard/providers ([#12163](https://github.com/diegosouzapw/OmniRoute/pull/12163)) +- **chore(quality):** register search-432 cooldown test in stryker tap.testFiles ([#12170](https://github.com/diegosouzapw/OmniRoute/pull/12170)) +- **chore(lint):** batch 5 of #12146 — combos, endpoint, provider-stats, api-manager and costs react-hooks violations resolved ([#12174](https://github.com/diegosouzapw/OmniRoute/pull/12174)) +- **build:** add contributor fast profile ([#12192](https://github.com/diegosouzapw/OmniRoute/pull/12192)) — thanks @rafacpti23 +- **docs(readme):** README touch-ups ([#12193](https://github.com/diegosouzapw/OmniRoute/pull/12193)) ([#12194](https://github.com/diegosouzapw/OmniRoute/pull/12194)) ([#12202](https://github.com/diegosouzapw/OmniRoute/pull/12202)) — thanks @rafacpti23 +- **build:** force the webpack bundler in the contributor build profile ([#12197](https://github.com/diegosouzapw/OmniRoute/pull/12197)) — thanks @rafacpti23 +- **build:** skip standalone packaging in the contributor profile ([#12198](https://github.com/diegosouzapw/OmniRoute/pull/12198)) — thanks @rafacpti23 +- **docs(audit):** align every published number with the code and harden check:docs-counts ([#12200](https://github.com/diegosouzapw/OmniRoute/pull/12200)) +- **build:** stub the instrumentation entrypoints in the contributor profile ([#12203](https://github.com/diegosouzapw/OmniRoute/pull/12203)) — thanks @rafacpti23 +- **build:** omit the standalone output target for contributor builds ([#12204](https://github.com/diegosouzapw/OmniRoute/pull/12204)) — thanks @rafacpti23 +- **chore(lint):** batch 6 of #12146 — memory, radar, audit, analytics, cache, usage, activity, home and RequestLoggerV2 react-hooks violations resolved ([#12208](https://github.com/diegosouzapw/OmniRoute/pull/12208)) +- **docs(diagrams):** rename number-carrying diagram files to stable names ([#12210](https://github.com/diegosouzapw/OmniRoute/pull/12210)) +- **docs(dashboard):** redraw onboarding tier-flow SVGs for the real 4-tier model ([#12211](https://github.com/diegosouzapw/OmniRoute/pull/12211)) +- **docs(api):** document every implemented route in openapi.yaml (276 → 692 paths) ([#12212](https://github.com/diegosouzapw/OmniRoute/pull/12212)) +- **chore(lint):** batch 7 of #12146 — final src tail: 37 react-hooks violations across 33 files resolved ([#12244](https://github.com/diegosouzapw/OmniRoute/pull/12244)) +- Revert "docs: recommend gstack for AI-assisted workflows (#11770)" ([#12248](https://github.com/diegosouzapw/OmniRoute/pull/12248)) ([#12249](https://github.com/diegosouzapw/OmniRoute/pull/12249)) +- **docs:** Chaos Mode setup guide + weighted strategy semantics ([#12250](https://github.com/diegosouzapw/OmniRoute/pull/12250)) +- **docs(agents):** protected-surface merge rule — operator approval for agent-instruction files ([#12253](https://github.com/diegosouzapw/OmniRoute/pull/12253)) +- **chore(quality):** register native-codex turn-pin tests in stryker tap.testFiles ([#12263](https://github.com/diegosouzapw/OmniRoute/pull/12263)) +- **chore(quality):** dedupe tap.testFiles entries added by racing base-red fixes ([#12265](https://github.com/diegosouzapw/OmniRoute/pull/12265)) +- **docs(readme):** introduce OmniRouteTray — the macOS menu-bar companion ([#12276](https://github.com/diegosouzapw/OmniRoute/pull/12276)) — thanks @ggiak +- **chore(proxy):** purge the legacy 1proxy residue — sync/rotator modules, dead DB exports, dead settings tab and flag, docs (#12091) ([#12290](https://github.com/diegosouzapw/OmniRoute/pull/12290)) +- **chore(quality):** baseline-headroom skips generated and vendored files in the fileSize worst-file signal ([#12291](https://github.com/diegosouzapw/OmniRoute/pull/12291)) +- **refactor(video):** unify the JPEG frame data-URI contract ([#12322](https://github.com/diegosouzapw/OmniRoute/pull/12322)) +- **chore(deps):** freeze onnxruntime-node and eslint-plugin-react-hooks out of dependabot groups ([#12329](https://github.com/diegosouzapw/OmniRoute/pull/12329)) +- **test(executors):** restore the #10986 reasoning-only fallback guards ([#12364](https://github.com/diegosouzapw/OmniRoute/pull/12364)) — thanks @pacocartones +- **chore(quality):** re-tighten the file-size ratchet to the real LOC (plan 3.8.52 task 0) ([#12411](https://github.com/diegosouzapw/OmniRoute/pull/12411)) +- **test(grok):** format web executor suite ([#12412](https://github.com/diegosouzapw/OmniRoute/pull/12412)) +- **chore(quality):** ratchet open-sse typecheck baseline to zero ([#12418](https://github.com/diegosouzapw/OmniRoute/pull/12418)) +- **test(build):** guard the artifact path policy arrays against duplicates ([#12422](https://github.com/diegosouzapw/OmniRoute/pull/12422)) +- **chore(lint):** adopt eslint-plugin-react-hooks 7.1.1 ([#12428](https://github.com/diegosouzapw/OmniRoute/pull/12428)) +- **chore(providers):** remove a keyless provider integration at its operator's request ([#12440](https://github.com/diegosouzapw/OmniRoute/pull/12440)) +- **docs(providers):** register providers removed at their operator's request and guard against reintroduction ([#12478](https://github.com/diegosouzapw/OmniRoute/pull/12478)) +- **chore(quality):** base fixes — stryker tap.testFiles + node_modules cache key ([#12482](https://github.com/diegosouzapw/OmniRoute/pull/12482)) +- **docs(arch):** one-process recipe for tens of long /v1/responses ([#12493](https://github.com/diegosouzapw/OmniRoute/pull/12493)) — thanks @RaviTharuma +- **chore(quality):** tighten the CodeQL ratchet baseline from 11 to 6 ([#12530](https://github.com/diegosouzapw/OmniRoute/pull/12530)) +- **chore(deps):** pin browserslist override to ^4.28.8 ([#12592](https://github.com/diegosouzapw/OmniRoute/pull/12592)) — thanks @KooshaPari +- **chore(deps):** bump fast-uri to 3.1.7 in the electron lockfile ([#12601](https://github.com/diegosouzapw/OmniRoute/pull/12601)) +- **docs(quality):** document how the CodeQL ratchet refreshes and how to tighten it ([#12611](https://github.com/diegosouzapw/OmniRoute/pull/12611)) +- **test(ci):** pin the openapi-security-tiers two-arm contract with an executing gate test (#12581) ([#12652](https://github.com/diegosouzapw/OmniRoute/pull/12652)) +- **docs(gamification):** align XP Rewards table with code (#12501) ([#12667](https://github.com/diegosouzapw/OmniRoute/pull/12667)) — thanks @KooshaPari +- **refactor(combo):** split executeTarget into gates, attempt, and loop ([#12746](https://github.com/diegosouzapw/OmniRoute/pull/12746)) — thanks @HouMinXi +- **docs(security):** document socket.yml scanner config + CI workflow link (#12575) ([#12764](https://github.com/diegosouzapw/OmniRoute/pull/12764)) — thanks @KooshaPari +- **chore(ci):** guard commit identity in pre-commit to stop author misattribution ([#12772](https://github.com/diegosouzapw/OmniRoute/pull/12772)) +- **deps:** 17 Dependabot bumps — bump electron from 43.4.0 to 43.4.1 in /electron ([#11426](https://github.com/diegosouzapw/OmniRoute/pull/11426)); bump the development group across 1 directory with 8 updates ([#11428](https://github.com/diegosouzapw/OmniRoute/pull/11428)); bump the production group across 1 directory with 11 updates ([#11440](https://github.com/diegosouzapw/OmniRoute/pull/11440)); chore(deps): bump github/codeql-action/init from 4.37.7 to 4.37.8 ([#11925](https://github.com/diegosouzapw/OmniRoute/pull/11925)); chore(deps): bump github/codeql-action from 4.37.7 to 4.37.8 ([#11926](https://github.com/diegosouzapw/OmniRoute/pull/11926)); chore(deps): bump github/codeql-action/analyze from 4.37.7 to 4.37.8 ([#11927](https://github.com/diegosouzapw/OmniRoute/pull/11927)); build(deps): bump github/codeql-action/init from 4.37.8 to 4.37.9 ([#12345](https://github.com/diegosouzapw/OmniRoute/pull/12345)); build(deps): bump github/codeql-action/analyze from 4.37.8 to 4.37.9 ([#12346](https://github.com/diegosouzapw/OmniRoute/pull/12346)); bump the development group across 1 directory with 2 updates ([#12347](https://github.com/diegosouzapw/OmniRoute/pull/12347)); build(deps): bump github/codeql-action from 4.37.8 to 4.37.9 ([#12349](https://github.com/diegosouzapw/OmniRoute/pull/12349)); bump browserslist from 4.28.2 to 4.28.8 ([#12396](https://github.com/diegosouzapw/OmniRoute/pull/12396)); bump the production group across 1 directory with 4 updates ([#12399](https://github.com/diegosouzapw/OmniRoute/pull/12399)); bump @xmldom/xmldom ([#12500](https://github.com/diegosouzapw/OmniRoute/pull/12500)); bump qs from 6.15.2 to 6.16.0 ([#12512](https://github.com/diegosouzapw/OmniRoute/pull/12512)); bump @xmldom/xmldom from 0.9.10 to 0.9.12 ([#12513](https://github.com/diegosouzapw/OmniRoute/pull/12513)); bump fast-uri from 3.1.5 to 3.1.7 ([#12514](https://github.com/diegosouzapw/OmniRoute/pull/12514)); bump @humanfs/node from 0.16.7 to 0.16.8 ([#12515](https://github.com/diegosouzapw/OmniRoute/pull/12515)) +- Clear the `release/v3.8.51` `check:agent-skills-sync` base-red: regenerate `skills/cli-tunnel/SKILL.md` so the `tunnel create [type]` positional that #13009 taught the generator to read is reflected in the committed skill. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` typecheck base-red from #12731: the new `Mode pack ranking gates` candidates in `open-sse/services/autoCombo/__tests__/autoCombo.test.ts` omitted the required `provider`, `model` and `errorRate` fields of `ProviderCandidate`, failing `check:open-sse-typecheck` (and with it `Fast Quality Gates`) on every open PR. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` docs-sync base-red: EURouter (#13025) and GreenPT (#13024) took the live provider count to 358, leaving 7 STRICT drifts (`PROVIDER_REFERENCE.md`, 4 diagrams, 2 tier-flow images) plus the `AGENTS.md` / `llm.txt` / `package.json` count claims stale. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- **test(models):** Pin `CATALOG_BUILD_TIMEOUT_MS` in the #12058 canonical-catalog regression so a cold catalog build racing the 8s default bound no longer flakes the suite with a 500 ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Allow `@omniroute/opencode-plugin-v2/` in the npm pack policy — [#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870) shipped the v2 plugin beside its v1 sibling without widening the allowlist, so every packed file under it read as an unexpected artifact and failed `check:pack-policy` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Point the release-green validator's pack gate at the tree it is validating — the build-provenance guard defaulted to `origin/main`, which a release branch tip can never be an ancestor of mid-cycle, so `Package artifact` reported a false red on every run and the tarball boot-smoke cascaded off it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **docs(checks):** keep doc counts honest — headings, rankings, catalog, weights, quality gate and scoring diagram now covered ([#12507](https://github.com/diegosouzapw/OmniRoute/pull/12507)) — thanks @maxmad64bis +- **chore(lifecycle):** `check:model-lifecycle` now also diffs `DEFAULT_DEGRADATION_MAP` (the background-task redirect table) against the vendor lifecycle snapshot, refusing a retired id as source or target, with a table-driven unit test beside it. Three rows whose source the vendor had retired — `claude-sonnet-4-20250514`, `gemini-3-pro-preview` and `gpt-5.1-codex` (whose target `gpt-5.1-codex-mini` is retired too) — were dead code, since `checkLifecycle` answers 410 before the redirect runs; they are dropped (#12535 — thanks @pacocartones) +- **test(compression):** cover idle worker eviction at the resource level — the pool must call `terminate()` and must not retain the worker's `MessagePort`, complementing the `exit`-event assertion added with the fix ([#13371](https://github.com/diegosouzapw/OmniRoute/pull/13371)) +- **chore(quality):** raise the `imageGeneration.ts` file-size ceiling for the image-only-model guard that clears the #12945 base-red ([#13392](https://github.com/diegosouzapw/OmniRoute/pull/13392)) +- **chore(quality):** type the combo-test route's JSON response bodies instead of casting them to `any`, drop the now-empty suppression entry, and remove the `hasPerModelQuota` import `chatCore.ts` stopped using when the failure-classification helper was extracted ([#13379](https://github.com/diegosouzapw/OmniRoute/pull/13379)) +- **chore(electron):** the desktop bundle no longer ships the root-level files of every translated docs mirror (`README.md`, `llm.txt`, `CLAUDE.md`, `GEMINI.md`, `CONTRIBUTING.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`) — the packaged app only reads `docs/i18n//docs/**`, which stays. Saves ~11 MB on top of the translated CHANGELOGs already pruned. (#0000) ([#13235](https://github.com/diegosouzapw/OmniRoute/pull/13235)) +- **chore(quality):** retighten the `src/sse/handlers/chat.ts` unused-vars suppression count to the 8 violations that actually remain ([#13385](https://github.com/diegosouzapw/OmniRoute/pull/13385)) +- Bring the documented counts back in line with the code: 171 migrations (was 169) and 20 routing strategies (was 19), plus the regenerated `cli-tunnel` skill reference. ([#12970](https://github.com/diegosouzapw/OmniRoute/pull/12970)) +- Update the documented migration count to 172 after the call-logs provider-stats indexes landed. ([#13209](https://github.com/diegosouzapw/OmniRoute/pull/13209)) +- **chore(ci):** two gates that close the blind spots behind the exclusions above. `check:vitest-exclusions` requires every Vitest exclusion to name a tracking issue and to appear in `config/quality/vitest-exclusions.json` — the previous list grew to 62 files behind a comment pointing at an issue that had been closed for a month. `check-new-key-coverage` requires a key newly added to `en.json` to reach every locale; the existing coverage gate is a percentage floor per locale, so eleven absent keys out of ~13,000 left it at 99.9% while a whole feature shipped untranslated in nine languages. Both are diff-aware, so pre-existing debt stays frozen and neither needed a migration to turn on. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **build:** root layout no longer loads Inter from `next/font/google`, so a production image build does not need fonts.googleapis.com ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **docs(i18n):** bring the eight new `llm.txt` mirrors (km, kn, ml, my, ne, or, pa, si) up to the current provider and migration counts, so `check-docs-sync` stops rejecting every commit ([#13674](https://github.com/diegosouzapw/OmniRoute/pull/13674)) +- **refactor(ui):** flow surfaces (home topology, combo live studio, compression cockpit/waterfall/nodes, token-health badge) express state through the theme-aware `--orch-status-*` tokens instead of fixed dark-mode hex, so green/red/amber/grey stay legible in the light theme; dark mode is byte-identical. Categorical palettes (routing-strategy hues, compression-layer pills, provider brand colors) deliberately stay hex ([#12378](https://github.com/diegosouzapw/OmniRoute/issues/12378)) ([#13203](https://github.com/diegosouzapw/OmniRoute/pull/13203)) +- **chore(tests):** 51 test files that had been excluded from Vitest are running again, restoring roughly 350 assertions to the blocking suite. Each was measured individually first: of the 62 files parked behind the `// #8618 — pre-existing failure` comment, 51 pass against the current tree with no source change, so the exclusions had outlived the failures they were added for. The 11 that genuinely still fail stay excluded, but now point at a live tracker (#13204) rather than at #8618, which was closed in August while the list it tracked kept growing. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **chore(quality):** drop two ESLint suppression entries whose violations no longer exist, so `eslint --suppressions-location` stops rejecting every commit that touches the surrounding files ([#13384](https://github.com/diegosouzapw/OmniRoute/pull/13384)) +- **chore(release):** bring main's post-tag fixes into release/v3.8.51 — sync-back step 2/2 (direct commit `21c488f210`) +- **chore(deps):** patch toml and esbuild in the opencode plugin lockfiles ([#12965](https://github.com/diegosouzapw/OmniRoute/pull/12965)) +- **docs(changelog):** reconcile the v3.8.51 living section — fold 366 fragments, cover every cycle commit, credit every contributor ([#12971](https://github.com/diegosouzapw/OmniRoute/pull/12971)) +- **test(plugin):** wait for the v2 lazy-refresh tier instead of sleeping past it ([#12984](https://github.com/diegosouzapw/OmniRoute/pull/12984)) +- **chore(quality):** rebaseline chatCore.ts after the non-streaming regression fixes ([#13045](https://github.com/diegosouzapw/OmniRoute/pull/13045)) +- **docs(changelog):** add the missing bullet marker to the DeepSeek PoW fragment ([#13200](https://github.com/diegosouzapw/OmniRoute/pull/13200)) +- **chore(deps):** drain the Dependabot queue — 10 of 13 alerts ([#13213](https://github.com/diegosouzapw/OmniRoute/pull/13213)) +- **chore(ci):** make worktrees carry the hooks that enforce the identity gate ([#13236](https://github.com/diegosouzapw/OmniRoute/pull/13236)) +- **chore(deps):** pin csv-parse to ^7.0.2 via overrides (#13117) ([#13300](https://github.com/diegosouzapw/OmniRoute/pull/13300)) — thanks @KooshaPari +- **chore(deps):** bump hono from ^4.12.34 to ^4.13.7 (#13148) ([#13301](https://github.com/diegosouzapw/OmniRoute/pull/13301)) — thanks @KooshaPari +- **chore(deps):** pin joi to ^18.2.8 via overrides (#13085) ([#13302](https://github.com/diegosouzapw/OmniRoute/pull/13302)) — thanks @KooshaPari +- **docs(providers):** add Microsoft 365 Copilot (BizChat) provider guide (#12779) ([#13340](https://github.com/diegosouzapw/OmniRoute/pull/13340)) — thanks @KooshaPari +### 🙌 Contributors + +Thanks to everyone whose work landed in v3.8.51: + +| Contributor | PRs / Issues | +| --- | --- | +| [@5dive-bot](https://github.com/5dive-bot) | #11852 | +| [@abhisheksharma2411](https://github.com/abhisheksharma2411) | #11547, #11684, #11959, #12209 | +| [@adevwithpurpose](https://github.com/adevwithpurpose) | #11464 | +| [@adivekar-utexas](https://github.com/adivekar-utexas) | #12015, #12043, #12090, #12138 | +| [@AIB1TAL0S](https://github.com/AIB1TAL0S) | #12103 | +| [@alltomatos](https://github.com/alltomatos) | #11448 | +| [@alvinveroy](https://github.com/alvinveroy) | #11857, #12027, #12165 | +| [@amaleta](https://github.com/amaleta) | #11906, #11934 | +| [@amartinawi](https://github.com/amartinawi) | #11575 | +| [@AndrianBalanescu](https://github.com/AndrianBalanescu) | #11083, #11830, #11834, #11844 | +| [@anhtahaylove](https://github.com/anhtahaylove) | #13091, #13092, #13093, #13096, #13097, #13100, #13106, #13109, #13114, #13155, #13166, #13171, #13175, #13187, #13196 | +| [@aniruddhaadak80](https://github.com/aniruddhaadak80) | #11760 | +| [@app](https://github.com/app) | #12554 | +| [@arminanton](https://github.com/arminanton) | #11461, #11513 | +| [@AStupidBear](https://github.com/AStupidBear) | #11584 | +| [@atescivitci-cmd](https://github.com/atescivitci-cmd) | #12105 | +| [@b3nw](https://github.com/b3nw) | #11970, #11971, #12147 | +| [@backryun](https://github.com/backryun) | #11259, #11950, #12075, #12076, #12078, #12079, #12081, #12082, #12181, #12228, #12239, #12255, #12258, #12277, #12367, #12423, #12524, #12538 | +| [@benjaminkitt](https://github.com/benjaminkitt) | #11747 | +| [@benzntech](https://github.com/benzntech) | #11614, #11615 | +| [@Bl0ck154](https://github.com/Bl0ck154) | #11948, #11951, #11952, #11953, #11954 | +| [@brick30llc-ctrl](https://github.com/brick30llc-ctrl) | #11752, #12242, #12262 | +| [@Chewji9875](https://github.com/Chewji9875) | #11455, #11875, #12028 | +| [@cheynetom](https://github.com/cheynetom) | #12058 | +| [@davidebaraldo](https://github.com/davidebaraldo) | #12222, #12304 | +| [@davidlinfr](https://github.com/davidlinfr) | #6390 | +| [@ddarkr](https://github.com/ddarkr) | #12124 | +| [@Deftera186](https://github.com/Deftera186) | #11809 | +| [@dpozimski](https://github.com/dpozimski) | #11945 | +| [@DW-MediaLab](https://github.com/DW-MediaLab) | #12229 | +| [@echel0nn](https://github.com/echel0nn) | #11923 | +| [@f9td56dbgh-hub](https://github.com/f9td56dbgh-hub) | #11660 | +| [@fabioluissilva](https://github.com/fabioluissilva) | #11991 | +| [@foreveryh](https://github.com/foreveryh) | #12177 | +| [@geek007git](https://github.com/geek007git) | #12115, #12116, #12117, #12120, #12122 | +| [@geekyNads](https://github.com/geekyNads) | #11784, #12266 | +| [@ggiak](https://github.com/ggiak) | #11561, #12276, #12350, #12402 | +| [@gonisulaimann](https://github.com/gonisulaimann) | #12368, #12369, #12371 | +| [@Gorillaz322](https://github.com/Gorillaz322) | #12207 | +| [@groovecityJO](https://github.com/groovecityJO) | #12682 | +| [@hartmark](https://github.com/hartmark) | #11434, #11452, #11473, #11499, #11703, #11983, #11984, #11985, #11986, #11988, #11989, #11990, #11994, #12221, #12293, #12445, #12446, #12447, #12448, #12460, #12461, #12623, #12646, #12650, #12680, #12717, #12718, #12727, #12741, #12854 | +| [@hizzt](https://github.com/hizzt) | #11894 | +| [@hongnoul](https://github.com/hongnoul) | #11484 | +| [@HouMinXi](https://github.com/HouMinXi) | #11411, #11414, #11512, #11518, #11520, #11641, #11642, #11643, #11687, #11779, #11849, #11850, #11851, #11915, #11916, #11918, #11919, #11920, #12013, #12017, #12033, #12042, #12106, #12139, #12166, #12169, #12171, #12205, #12213, #12312, #12325, #12487, #12488, #12495, #12504, #12557, #12566, #12590, #12591, #12624, #12626, #12632, #12637, #12678, #12696, #12697, #12711, #12733, #12746, #12767, #12770, #12789, #12803, #12805, #12811, #12866, #12868, #12899, #12926, #12934, #12950, #12951, #12974, #13001, #13006, #13011, #13017, #13026, #13027, #13034, #13035, #13038, #13042, #13050, #13060, #13061, #13069, #13107, #13120, #13136, #13195, #13197 | +| [@Hsia97](https://github.com/Hsia97) | #11624 | +| [@jacobsparts](https://github.com/jacobsparts) | #11854, #12155, #12167 | +| [@joglomedia](https://github.com/joglomedia) | #11980 | +| [@jonlwheat2-gif](https://github.com/jonlwheat2-gif) | #11443, #11450, #11549, #11567, #11608, #11633, #11635, #11640, #11644, #11671, #11672, #11673, #11674, #11675, #11676, #11677, #11706, #11783, #12051, #12052, #12053, #12055 | +| [@kanade-hoshino](https://github.com/kanade-hoshino) | #12180 | +| [@Karan825](https://github.com/Karan825) | #11861 | +| [@KaspaPulse](https://github.com/KaspaPulse) | #11389, #11469, #11628, #11666, #11910 | +| [@keeltrace](https://github.com/keeltrace) | #12080, #12223 | +| [@killer30001000](https://github.com/killer30001000) | #12178 | +| [@KooshaPari](https://github.com/KooshaPari) | #12470, #12592, #12667, #12699, #12703, #12706, #12764, #12769, #12771, #13300, #13301, #13302, #13340, #13401, #13403, #13404, #13405, #13406, #13407, #13409, #13410, #13411, #13412, #13413, #13414, #13418, #13424, #13427, #13433, #13523, #13524, #13525, #13528, #13531, #13532, #13534, #13539, #13543, #13545, #13546, #13547, #13550, #13553 | +| [@kriptoburak](https://github.com/kriptoburak) | #11370 | +| [@KrzysiekSko](https://github.com/KrzysiekSko) | #12425, #12432, #12673 | +| [@marcelokarval](https://github.com/marcelokarval) | direct commit / report | +| [@maxmad64bis](https://github.com/maxmad64bis) | #1622, #11435, #11437, #11441, #11537, #11550, #11553, #11555, #11557, #11812, #11842, #11843, #11903, #12151, #12214, #12215, #12218, #12226, #12314, #12316, #12317, #12318, #12319, #12320, #12321, #12507, #12715, #12731, #12744, #12786, #12787, #12788, #12790, #12792, #12794, #12795, #12828, #12832, #12853, #12857, #12870, #12937, #12941, #12975, #13141, #13142, #13146, #13147 | +| [@mdigitalbh81](https://github.com/mdigitalbh81) | #12240 | +| [@Meet6338-X](https://github.com/Meet6338-X) | #11598, #11609, #12110 | +| [@morpheus9393](https://github.com/morpheus9393) | #11943 | +| [@MumuTW](https://github.com/MumuTW) | #11492, #11502, #11506, #11507, #11626, #11675, #11685, #11728, #11746, #11888, #11889, #11890, #11892 | +| [@Neuron-Mr-White](https://github.com/Neuron-Mr-White) | #11622, #11801, #12256 | +| [@NightStalker-87](https://github.com/NightStalker-87) | #12183 | +| [@NoxzRCW](https://github.com/NoxzRCW) | #11879, #11880, #11881, #11882, #11883, #11935 | +| [@ntdat812](https://github.com/ntdat812) | #11585, #12095 | +| [@ntdatt812](https://github.com/ntdatt812) | #11368, #11573, #11574, #11576, #11577, #11580, #11582, #11583, #11588, #11589, #11590, #11591, #11592, #11593, #11672, #12177, #12180, #12873, #12918, #12920, #12921, #12925, #12930, #13007, #13009, #13024, #13025, #13083, #13087, #13101, #13104, #13110 | +| [@official-burak](https://github.com/official-burak) | #11542 | +| [@opensource-elearning](https://github.com/opensource-elearning) | #12179, #12189, #12278, #12286 | +| [@oyi77](https://github.com/oyi77) | #11408, #11409, #11421, #11505, #11677, #12036, #12110 | +| [@pacocartones](https://github.com/pacocartones) | #11521, #11522, #11527, #11528, #11529, #11530, #11531, #11532, #11533, #11534, #11595, #11599, #11603, #11604, #11605, #11607, #11610, #11676, #11714, #11716, #11718, #11767, #11838, #11860, #11862, #11869, #11871, #11872, #11873, #11903, #11906, #11921, #11934, #12356, #12358, #12359, #12360, #12361, #12362, #12364, #12365, #12373, #12374, #12375, #12376, #12377, #12379, #12380, #12381, #12386, #12387, #12389, #12390, #12394, #12395, #12397, #12401, #12403, #12404, #12522, #12523, #12535, #12536, #12540, #12541, #12543, #12545, #12548, #12549, #12550, #12551, #12552, #12647, #12651, #12653, #13055 | +| [@patrykkopycinski](https://github.com/patrykkopycinski) | #11936, #11937, #12224 | +| [@PauloFH](https://github.com/PauloFH) | #11509 | +| [@PauloHSOliveira](https://github.com/PauloHSOliveira) | #12241 | +| [@ponkcore](https://github.com/ponkcore) | #12054 | +| [@Prajeeth-12](https://github.com/Prajeeth-12) | #11634, #12046 | +| [@quiterunner-commits](https://github.com/quiterunner-commits) | #12131, #12143 | +| [@rafacpti23](https://github.com/rafacpti23) | #11554, #11558, #12192, #12197, #12198, #12202, #12203, #12204 | +| [@ragnar-claude](https://github.com/ragnar-claude) | #11564 | +| [@raheemuddin786](https://github.com/raheemuddin786) | #11491, #11839, #11840, #11841, #12003, #12230, #12231, #12232, #12233, #12234 | +| [@rahilmavani](https://github.com/rahilmavani) | #11761 | +| [@Rahulsharma0810](https://github.com/Rahulsharma0810) | #11771 | +| [@RaviTharuma](https://github.com/RaviTharuma) | #11710, #11727, #11797, #11798, #11802, #11805, #11806, #11811, #12098, #12099, #12101, #12449, #12452, #12472, #12473, #12493, #12533, #12607, #12628, #12631, #12636, #12875, #12876, #12880, #12882, #12884 | +| [@rezjalibd](https://github.com/rezjalibd) | #12186 | +| [@RhianB14](https://github.com/RhianB14) | #11692 | +| [@rifqiawl](https://github.com/rifqiawl) | #11517, #11519, #11969 | +| [@rqzbeh](https://github.com/rqzbeh) | #11390 | +| [@Sabeekhann](https://github.com/Sabeekhann) | #11699, #11705 | +| [@santosraju99-hub](https://github.com/santosraju99-hub) | #11755, #11770 | +| [@Seramicx](https://github.com/Seramicx) | #11568 | +| [@Siva010](https://github.com/Siva010) | #12191 | +| [@solstxce](https://github.com/solstxce) | #11597 | +| [@soroush5](https://github.com/soroush5) | #12691 | +| [@tenshiak](https://github.com/tenshiak) | #12279 | +| [@TheDemonTuan](https://github.com/TheDemonTuan) | #11468, #11470, #11471, #11482, #11548, #11758, #11775, #11814 | +| [@thomasmaerz](https://github.com/thomasmaerz) | #12834 | +| [@tuandinh0801](https://github.com/tuandinh0801) | #11454 | +| [@turbolego](https://github.com/turbolego) | #11621, #11762, #11772, #11774, #11781, #12216 | +| [@Tushar49](https://github.com/Tushar49) | #11392 | +| [@ujjawalkaushik1110](https://github.com/ujjawalkaushik1110) | #11863 | +| [@vermasomesh835](https://github.com/vermasomesh835) | #11794 | +| [@vsd2807](https://github.com/vsd2807) | #11565, #11619 | +| [@wahidsadik371-coder](https://github.com/wahidsadik371-coder) | #12126 | +| [@watchingdogs](https://github.com/watchingdogs) | #12031 | +| [@wildcard](https://github.com/wildcard) | #11620 | +| [@xiaoyaner0201](https://github.com/xiaoyaner0201) | #11460, #11662, #12083 | +| [@Xxx91n](https://github.com/Xxx91n) | #11690 | +| [@yourspraveen](https://github.com/yourspraveen) | #11146 | +| [@yxyxy](https://github.com/yxyxy) | #11671 | +| [@ZaimMarzuki](https://github.com/ZaimMarzuki) | #11960 | +| [@Zartharas](https://github.com/Zartharas) | #11340 | +| [@zero-executioner](https://github.com/zero-executioner) | #11631 | +| [@diegosouzapw](https://github.com/diegosouzapw) | maintainer | + +--- ## [3.8.50] — 2026-08-25 _Living section — regenerated 2026-08-12 from all cycle commits (cycle open `ed2db6cb19` → tip). Bullets carry the merged PR and its author; direct pushes listed separately._ diff --git a/docs/i18n/fa/CHANGELOG.md b/docs/i18n/fa/CHANGELOG.md index 887088fbd9..c788036963 100644 --- a/docs/i18n/fa/CHANGELOG.md +++ b/docs/i18n/fa/CHANGELOG.md @@ -8,16 +8,1119 @@ ## [3.8.51] — TBD -_Living section — cycle opened at the v3.8.50 freeze (parallel-cycle model). Bullets are aggregated from `changelog.d/` fragments at each `/generate-release` phase._ +_Living section — reconciled 2026-09-15 from all cycle commits (`091589089c` → `c0f92ec98a`, 916 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 | **123** | +| 📝 Commits in the cycle | **916** | +| 🔀 Pull requests referenced | **892** | +| 📋 Changelog entries | **919** | +| 🙌 Contributors credited in entries | **121** | +| 🤖 Automated dependency commits | 18 | + +**Entries by type** + +| Type | Count | +| --- | ---: | +| 🐛 Fixes | 592 | +| ✨ Features | 131 | +| 🧹 Chore | 93 | +| 📚 Docs | 42 | +| 🧪 Tests | 32 | +| ♻️ Refactor | 8 | +| 🏗️ Build | 7 | +| ⚡ Performance | 7 | +| ⚙️ CI | 2 | +| ⏪ Reverts | 2 | +| 📦 Dependencies | 2 | +| 🔒 Security | 1 | + +### 🏆 Top 25 contributors this cycle + +_By commits in `091589089c..c0f92ec98a`, author identities consolidated via `.mailmap` and the merged PR's GitHub login. Bots excluded._ + +| # | Contributor | Commits | +| ---: | --- | ---: | +| 🥇 | diegosouzapw | 323 | +| 🥈 | Bob.Hou (@HouMinXi) | 79 | +| 🥉 | Paco Cartones (@pacocartones) | 77 | +| 4 | Dizzle (@maxmad64bis) | 49 | +| 5 | Koosha Paridehpour (@KooshaPari) | 43 | +| 6 | Nguyen Thanh Dat (@ntdatt812) | 31 | +| 7 | Ravi Tharuma (@RaviTharuma) | 26 | +| 8 | Markus Hartung (@hartmark) | 22 | +| 9 | Webman (@jonlwheat2-gif) | 22 | +| 10 | backryun | 17 | +| 11 | anhtahaylove | 15 | +| 12 | Rafa Martins (@rafacpti23) | 10 | +| 13 | Syed Raheemuddin (@raheemuddin786) | 10 | +| 14 | Nguyễn Viết Tuấn (@TheDemonTuan) | 8 | +| 15 | MumuTW | 7 | +| 16 | Paijo (@oyi77) | 7 | +| 17 | Tobias Andersen (@turbolego) | 6 | +| 18 | Bl0ck (@Bl0ck154) | 5 | +| 19 | SHANMUGAPRIYAN (@geek007git) | 5 | +| 20 | KaspaPulse | 5 | +| 21 | Abhishek Sharma (@abhisheksharma2411) | 4 | +| 22 | Abhishek Divekar (@adivekar-utexas) | 4 | +| 23 | Andrew B. (@AndrianBalanescu) | 4 | +| 24 | NoxzRCW | 4 | +| 25 | opensource-elearning | 4 | ### ✨ New Features +- **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). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- Added Google AI Studio Gemini batch text-to-speech support through `POST /v1/audio/speech`. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- 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. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(routing):** subscription-first auto groupings — `auto/subscription` routes only through plan-included connections with a documented hard-stop overage and fails closed on exhaustion, while `auto/thrifty` orders the pool `subscription → keyless → free → cheap → premium` and steps up one rung at a time as each is exhausted. Billing class comes from a curated per-connection catalog (uncurated is treated as metered, never plan-included), both reuse STRICT_ZERO_COST's per-connection verification, and a quota reading whose `resetAt` has passed is now refreshed regardless of TTL so routing returns to plan capacity as soon as it resets ([#11146](https://github.com/diegosouzapw/OmniRoute/pull/11146)) — thanks @yourspraveen +- **feat(providers):** publish a management-authenticated versioned web-session credential contract from OmniRoute's canonical browser credential metadata ([#11340](https://github.com/diegosouzapw/OmniRoute/pull/11340)) — thanks @Zartharas +- **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)) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add Xquik X search with typed results, credential validation, REST routing, and MCP selection ([#11370](https://github.com/diegosouzapw/OmniRoute/pull/11370)) — thanks @kriptoburak +- **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)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(dashboard):** surface durable exclusive managed leases in the existing Sessions view, keeping leased clients visible across idle gaps while marking connections with in-flight work as active ([#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389)) — thanks @KaspaPulse +- **build(bun):** allow Turbopack bundler flag on Bun 1.4+ with configurable Webpack fallback ([#11471](https://github.com/diegosouzapw/OmniRoute/pull/11471)) — thanks @TheDemonTuan +- feat(api): add an opt-in `modelVisibilityAllowlist`/`modelVisibilityDenylist` settings pair to curate exactly which models `/v1/models` advertises, mirrored into every `auto/*` combo candidate pool so a denied model cannot be routed to via combo selection either (#11481) ([#11997](https://github.com/diegosouzapw/OmniRoute/pull/11997)) +- **feat(rankings):** the Free Provider Rankings page shows what each provider actually served over the last 24 h. It ranked by ELO alone, which left a provider that answers every call with an error in first place; the usage data was already served by the API but never requested. A provider with too small a sample shows a dash, not a number ([#11546](https://github.com/diegosouzapw/OmniRoute/pull/11546)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **feat(guardrails):** enforce a bounded, deterministic contract for Video Bridge transcripts — 256 cues, 4096 input code units and 4 KiB UTF-8 per cue, 64 KiB total text, malformed-Unicode rejection, focus-window scoping, cross-source reconciliation with contributing-source metadata, and a structural provenance trust boundary so caller JSON can never self-assert `embedded`/`audio-bridge` provenance ([#11652](https://github.com/diegosouzapw/OmniRoute/issues/11652)) ([#12009](https://github.com/diegosouzapw/OmniRoute/pull/12009)) +- **feat(video):** orchestrate optional Video Bridge audio extraction and Audio Bridge STT behind a dual opt-in (operator setting AND per-request signal) — a new loopback-only broker `mode=audio` operation shares the frame path's exact process queue, deadline, AbortSignal, and byte budgets to extract a bounded mono 16 kHz PCM WAV from the same already-downloaded video, then reuses the existing Audio Bridge transcription boundary; provider segment timing is preserved when available and marked coarse otherwise, and every failure degrades to a visual-only-safe partial instead of throwing (#11654). ([#12012](https://github.com/diegosouzapw/OmniRoute/pull/12012)) +- Add a tenant-bound Video Bridge drill-down lifecycle on top of the existing secure cache substrate: opaque hashed handles (never raw session/video identifiers), preview/standard/detail multiresolution variants resampled on read, response pagination capped at 8 frames and 32 MiB, and a new authenticated `/api/v1/video-bridge/drilldown` consumer route that stays disabled for remote access by default and denies cross-key access with the same response as a nonexistent handle (no existence oracle). ([#12006](https://github.com/diegosouzapw/OmniRoute/pull/12006)) +- **test(video):** Add the Video Bridge FU-07/FU-09 promotion-evidence harness (#11656) — a frozen Zod manifest schema covering the 8 required scenario kinds (static scenes, rapid cuts, late facts, fades, blur, small text, close events, visual prompt injection) with a minimum of 3 repetitions per case, deterministic declarative fixture recipes (`videoBridgePromotionFixtures.ts`), a pure medians/p95 metrics aggregator, a pure FU-07/FU-09 promotion-verdict evaluator applying the ticket's exact thresholds (missing token usage always holds), a digest-only persistence layer that never retains raw media or raw model responses, and a versioned per-model promotion allowlist shipped empty with every model defaulting to `hold`. The FU-07/FU-09 promotion verdicts themselves remain HOLD — they require a real evidence run against real models on VPS 192.168.0.15. ([#12008](https://github.com/diegosouzapw/OmniRoute/pull/12008)) +- **feat(video bridge):** "embedded" transcript provenance can now be legitimately earned instead of merely asserted — a bounded, allowlisted (`mov_text`/`subrip`/`webvtt`) subtitle probe runs through the loopback-only Video Bridge broker (at most 2 streams, 10s subdeadline bounded by the request deadline, 256 KiB output, 4096-code-unit lines), normalized through a bounded, ReDoS-safe WebVTT parser and Zod-validated end to end. The adapter always resolves to an explicit `success`/`absent`/`transient_failure` outcome — a subtitle failure never breaks the visual description path, and only a fingerprint-verified broker response (never a caller-declared label) can produce embedded cues (#11659). ([#12011](https://github.com/diegosouzapw/OmniRoute/pull/12011)) +- Default new Antigravity-family connections (agy CLI imports and Antigravity OAuth connects) to model auto-sync, so live model discovery lands in the synced catalog and `/v1/models` picks up freshly released upstream models (e.g. Gemini 3.7 Flash tiers) without code changes. Existing connections keep their current setting; the per-connection dashboard toggle remains the opt-out. (#11685 — thanks @MumuTW) ([#11887](https://github.com/diegosouzapw/OmniRoute/pull/11887)) +- **feat(ui):** enable React Compiler (`reactCompiler: true` + `babel-plugin-react-compiler`) for automatic memoization at build time ([#11783](https://github.com/diegosouzapw/OmniRoute/pull/11783)) — thanks @jonlwheat2-gif +- **feat(providers):** add SeekAi (`seekai.cc`) as an OpenAI-compatible New-API gateway — catalog id `seekai` (alias `ska`), `https://seekai.cc/v1`, live `/v1/models` via `passthroughModels`, aggregator-list membership so New-API balance detection can opt in. No referral/aff codes. ([#11786](https://github.com/diegosouzapw/OmniRoute/issues/11786)) ([#12557](https://github.com/diegosouzapw/OmniRoute/pull/12557)) — thanks @HouMinXi +- **feat(zai):** add GLM-5.3-Flash Coding Plan support (1M context, 128K output, vision, `low|high|max` reasoning) and route `zai` GLM-5.3-family API-key traffic through the OpenAI-compatible Coding Plan endpoint with native thinking defaults ([#11801](https://github.com/diegosouzapw/OmniRoute/pull/11801)) — thanks @Neuron-Mr-White +- **feat(combo):** choose how combo models are ordered — manual, provider, score, or name — via a sort control in the dashboard builder, persisted in `config.modelSort` and re-applied on load and after add ([#11812](https://github.com/diegosouzapw/OmniRoute/pull/11812)) — thanks @maxmad64bis +- **feat(free):** custom models can be marked free-tier via `customModels[].isFree`; `isFreeModel()` is the first door and `hidePaidModels` respects it even for providers outside the free budget ([#11843](https://github.com/diegosouzapw/OmniRoute/pull/11843)) — thanks @maxmad64bis +- **feat(nodejs):** add `5dive` as a `configure` target — `omniroute configure 5dive` / `omniroute setup-5dive` write a 5dive auth profile that points an agent fleet's `claude` seats at OmniRoute, with the root-only write, the loopback-vs-`https` endpoint rule and the per-seat model pin handled explicitly ([#11852](https://github.com/diegosouzapw/OmniRoute/pull/11852)) — thanks @5dive-bot +- **feat(sse):** treat `max` as a first-class reasoning-effort tier and clamp per model family (GLM 5.1+/DeepSeek V4+/Kimi K3+ keep native `max`; o1/MiniMax/Grok/Muse Spark clamp to their upstream ceiling) ([#11875](https://github.com/diegosouzapw/OmniRoute/pull/11875)) — thanks @Chewji9875 +- **feat(providers):** the provider plugin manifest now advertises a `usage-fetch` capability for the 40 providers that have a wired usage/quota fetcher, so external dashboards can read it from `GET /api/v1/provider-plugin-manifest` instead of parsing `open-sse/services/usage.ts` after every release. Discovery only — no new fetcher, no quota change, and the Dashboard quota widget stays gated by `USAGE_SUPPORTED_PROVIDERS`. `USAGE_FETCHER_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/fetcherProviders.ts`) and is re-exported from `services/usage.ts`, keeping the manifest module a light leaf instead of pulling the ~490-module usage dispatcher into the manifest route. ([#11903](https://github.com/diegosouzapw/OmniRoute/pull/11903)) — thanks @maxmad64bis / @pacocartones +- **feat(plugins):** `OMNIROUTE_PLUGINS_DIR` sets the directory the runtime plugin scanner reads — and the root the plugin manager installs into — overriding the `HOME`-derived default, so a Docker/K8s deployment can point straight at its bind-mounted plugin tree instead of moving `HOME` just to relocate the scan path. An image that exports no home no longer scans `/tmp/.omniroute/plugins` in silence: the resolved directory is logged once at startup as `scanner.dir_resolved`, naming the input that won. Unset, behaviour is unchanged. Distinct from the CLI-only `OMNIROUTE_PLUGIN_PATH`, which finds `omniroute-cmd-*` command packages and never reached this scanner ([#11906](https://github.com/diegosouzapw/OmniRoute/pull/11906)) — thanks @amaleta / @pacocartones +- **feat(leases):** add an explicit owner-authenticated status action that returns only the active lease's privacy-safe configured connection and provider labels, with generation fencing and no credential or internal-id disclosure ([#11910](https://github.com/diegosouzapw/OmniRoute/pull/11910)) — thanks @KaspaPulse +- **feat(providers):** import-from-file modal shows per-row API errors and ships a downloadable CSV template ([#12071](https://github.com/diegosouzapw/OmniRoute/issues/12071)) ([#12504](https://github.com/diegosouzapw/OmniRoute/pull/12504)) — thanks @HouMinXi +- **feat(providers):** dashboard search matches connection name and `baseUrl` so imported OpenAI-compat nodes surface on the provider card ([#12108](https://github.com/diegosouzapw/OmniRoute/issues/12108)) ([#12495](https://github.com/diegosouzapw/OmniRoute/pull/12495)) — thanks @HouMinXi +- **feat(routing):** add a `score` Auto router strategy that selects the highest configured weighted score and reuses `explorationRate`. ([#12155](https://github.com/diegosouzapw/OmniRoute/pull/12155)) — thanks @jacobsparts +- Add a runtime feature flag to disable universal context handoffs globally without changing the default behavior. ([#12167](https://github.com/diegosouzapw/OmniRoute/pull/12167)) — thanks @jacobsparts +- **feat(providers):** the provider plugin manifest now also advertises a `usage-supported` capability for the 46 providers whose usage API is accepted by the server and Dashboard routes, so integrators can distinguish "the server will serve quota for this provider" from "a fetcher is wired" without reading TypeScript. Discovery only — no fetcher or quota change. `usage-fetch` resolves on id or alias (the usage dispatcher accepts both); `usage-supported` resolves on id alone, matching the runtime guard `USAGE_SUPPORTED_PROVIDERS.includes(providerId)`. `USAGE_SUPPORTED_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/supportedProviders.ts`) and is re-exported from `providers.ts`, mirroring the `fetcherProviders` leaf from #11903 and keeping the manifest a light module. ([#12214](https://github.com/diegosouzapw/OmniRoute/pull/12214)) — thanks @maxmad64bis +- **feat(rankings):** order Free Provider Rankings by what each provider actually served — `GET /api/free-provider-rankings?sortBy=reliability` and a "Most reliable first" toggle on the page. Providers with too few calls to state a success rate keep their score order below the measured ones; the default order is unchanged ([#12218](https://github.com/diegosouzapw/OmniRoute/pull/12218)). — thanks @maxmad64bis +- **perf(sse):** defer `cloneLogPayload()` in the structured SSE collector until after the `maxEvents`/`maxBytes` cap check, eliminating ~9,800 wasted `structuredClone` calls per streaming response (65–71% faster `push()`). Reducer snapshot isolation restored for OpenAI and Responses summaries ([#12241](https://github.com/diegosouzapw/OmniRoute/pull/12241)) — thanks @PauloHSOliveira ([#12243](https://github.com/diegosouzapw/OmniRoute/pull/12243)) +- **feat(usage):** Devin CLI agentic quota (Codeium seat-management GetUserStatus) and OpenRouter key limits plus account credits now surface in Provider Limits ([#12256](https://github.com/diegosouzapw/OmniRoute/pull/12256) — thanks @Neuron-Mr-White) +- **feat(settings):** persist `headroomUrl` through Settings so status/start use the operator URL instead of only `HEADROOM_URL` ([#12306](https://github.com/diegosouzapw/OmniRoute/issues/12306)) ([#12487](https://github.com/diegosouzapw/OmniRoute/pull/12487)) — thanks @HouMinXi +- **feat(auto-combo):** Auto-Combo scoring can now weigh how often a provider/model has actually succeeded. The engine already carried that number on every candidate — 24 hours of usage history behind a ten-sample floor, real-time metrics otherwise — and the scoring function never read it, while the weight table described `stability` as if it did. `reliability` (`1 - failureRate`, with the same field precedence and the same rate-bounding the speed ranking already uses, so a corrupt reading means "nothing observed" rather than "fails every call") is now a declared factor shipping at weight `0`, so routing is unchanged until an operator gives it one, and the `stability` description now matches what that factor computes ([#12317](https://github.com/diegosouzapw/OmniRoute/pull/12317)) — thanks @maxmad64bis +- **feat(routing):** With `freeAccessPolicy: "strict"`, the read-only candidate listing (`GET /v1/auto-combo/{channel}/candidates`) no longer hides the candidates the zero-cost guard excludes — the same read-only transparency the resilience filter already honours (#9133). Each candidate now carries `freeAccessExclusion` saying why it would be kept out, and it tells an exhausted allowance apart from a quota reading that never arrived or went stale, which used to look identical from the outside. Routing is unchanged: the listing reports, it never enforces. The separate `excludeTosAvoid` guard still drops its candidates without a reason; that gap is now documented rather than closed ([#12319](https://github.com/diegosouzapw/OmniRoute/pull/12319)) — thanks @maxmad64bis +- **feat(radar):** The Radar catalog table now shows two facts it was already receiving from the feed and dropping on the floor: the per-model rate limits (requests and tokens, per minute and per day) in a new column, and a badge when a provider's terms state it may train on the prompts you send. A limit of zero renders as zero rather than "rate-only" — for a ceiling those are opposite facts — and a model with no training statement gets no badge, because an absent statement is not a guarantee ([#12320](https://github.com/diegosouzapw/OmniRoute/pull/12320)) — thanks @maxmad64bis +- **feat(radar):** explain Community, single-use, contributor, supporter, recovery, abuse, offers, and privacy rules before either Radar activation action, and remove the superseded fixed-PR grant promise from every UI locale ([#12342](https://github.com/diegosouzapw/OmniRoute/pull/12342)) +- **feat(gamification):** the dashboard Profile page now shows the real daily streak — `/api/gamification/level` returns `streak: { current, longest }` (per key with `apiKeyId`, operator-wide maximum otherwise) and the streak card reads it instead of a hard-coded 0 (#2403) ([#12377](https://github.com/diegosouzapw/OmniRoute/pull/12377)) — thanks @pacocartones +- **feat(gamification):** the dashboard leaderboard now shows each API key's display name under the Name column instead of a truncated key id; `GET /api/gamification/leaderboard` attaches `name` per entry (name only — no key material), while the shared ranking helper and the federation leaderboard stay id-only — thanks @pacocartones ([#12385](https://github.com/diegosouzapw/OmniRoute/pull/12385)) +- **feat(gamification):** enforce the documented 1000 XP/min per-API-key anti-cheat rate limit on the XP award path; over-limit awards are logged and skipped instead of persisted, and the sliding window now matches the timestamp format stored in `xp_audit_log` ([#2403](https://github.com/diegosouzapw/OmniRoute/issues/2403)) ([#12390](https://github.com/diegosouzapw/OmniRoute/pull/12390)) — thanks @pacocartones +- **feat(admin):** localize the gamification anomalies page — the loading state, the Status column and the Suspicious badge now come from the `common` catalog (new `common.suspicious` key propagated to every locale) — add it to the Gamification sidebar group as `gamification-admin` (`/dashboard/gamification/admin`), and expose the loading and empty states as polite `role="status"` live regions (#12401 — thanks @pacocartones) +- **feat(providers):** skip GitHub combo members missing from the live synced catalog, and drop Copilot models that are policy-disabled or hidden from the model picker ([#12473](https://github.com/diegosouzapw/OmniRoute/pull/12473)) — thanks @RaviTharuma +- **feat(i18n):** locale-expansion tooling — `npm run i18n:add-locale` adds a language to config, dashboard, docs mirrors, CLI, README/indexes and site in one command; browser, CLI and cookie detection resolve `uk`, `fil`/`tl`, `zh-Hant` (and the retired legacy `in` → `id`) via config aliases; `config/i18n.json` now ships in the npm package so the published CLI can read it; new real-translation ratio gate (`npm run i18n:check-ratio`, advisory) with a per-locale ratchet baseline; `run-translation --adopt` restores `.i18n-state.json`; `sync-language-bars` generates 🌐 bars from config; `validate_translation.py` loads its allowlist again. Retires the duplicate `in` locale (Indonesian mislabelled as Hindi) — 42 honest locales; saved `NEXT_LOCALE=in` / `OMNIROUTE_LANG=in` keep working. (#12496) +- **feat(api):** Emit gateway-measured `tokens_per_second` (TTFT excluded) on streaming usage and `X-OmniRoute-Tokens-Per-Second` when first-token latency is known ([#12616](https://github.com/diegosouzapw/OmniRoute/issues/12616)) ([#12631](https://github.com/diegosouzapw/OmniRoute/pull/12631)) — thanks @RaviTharuma +- **feat(opencode-plugin): map gateway cost/usage/tok/s onto OpenCode inference payloads** — the official plugin copies `X-OmniRoute-Response-Cost`, token counts, `X-OmniRoute-Tokens-Per-Second` / `usage.tokens_per_second`, TTFT, and the winning `X-OmniRoute-Model` onto the JSON/SSE body OpenCode already consumes. Missing tok/s is left unset (never `tokens / latency`). (#12636) — thanks @RaviTharuma +- **feat(combo):** add combo strategy `quota-weighted`: skip exhausted accounts, then weighted-draw by leftover / in-flight load; existing conversations stay pinned until the account is empty ([#12789](https://github.com/diegosouzapw/OmniRoute/pull/12789)) — thanks @HouMinXi +- **feat(models):** Account live listings become the chat catalog source for Claude, Codex, Copilot, and AGY; public metadata only fills prices on IDs those accounts already list. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **fix(models):** Union `agy` and `antigravity` live catalogs so `agy/gemini-3.8-flash-high` is not rejected after the prefix folds to `antigravity`. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **feat(opencode):** Add `@omniroute/opencode-plugin-v2`, an opencode v2 plugin that gives a v2 install a route to an OmniRoute gateway: it publishes models, combos and auto-combos into the host catalog, refreshes them lazily behind a 300s TTL, and keeps serving the last known catalog from an on-disk snapshot when the gateway is unreachable. The v1 plugin (`@omniroute/opencode-plugin`) is untouched and keeps working as before. The plugin adapts to the host instead of assuming one shape: it reads the shape the host seeds into the catalog draft and publishes the `api` block, the pre-`api` top-level fields, or both. The gateway key comes from the host credential store when one is connected, so no secret needs to sit in a config file; a source the gateway refuses is named in a warning instead of silently degrading the catalog to raw model ids; and tool schemas bound for a Gemini model are stripped of the JSON-Schema keywords Gemini rejects, so tool calling survives that route ([#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870)) — thanks @maxmad64bis +- feat(services): show sanitized CLIProxyAPI account health from its authenticated management API without exposing credentials, file paths, or raw account metadata (#6342) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add AnySearch free web search + URL extract (webFetch) with typed results, credential validation, REST routing, and MCP selection - fallback-only ([#11690](https://github.com/diegosouzapw/OmniRoute/pull/11690)) — thanks @Xxx91n +- **feat(dashboard):** display clamped `[0, 100]%` cached input token ratio in request logs table ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) — thanks @b3nw +- **feat(catalog):** add `OMNIROUTE_DISABLE_THINKING_LEVEL_VARIANTS` feature flag to optionally filter out thinking level variants from model catalog ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) — thanks @b3nw +- **feat(dashboard):** continuously export call logs to external analytics stores. A pluggable destination registry ships the full Logs-tab record set on an hourly `JobRegistry` cron, with a persisted per-destination cursor, batched inserts, a config UI rendered from each destination's own field descriptors, and a REST layer (`/api/log-export/*`) for CRUD, a connection test, and an on-demand run. A destination can opt into `includeBodies` to also ship the request and response payloads shown in the Logs detail pane, including the client and provider views of each call; this is off by default, and payloads inherit the dashboard's PII sanitisation, secret redaction and `noLog` handling. Google BigQuery is the first destination, using a service-account key stored encrypted at rest and streaming inserts keyed by call-log id, into a table that is day-partitioned on `timestamp` and clustered on `api_key_name`, `provider`, `model` and `status`. ([#11945](https://github.com/diegosouzapw/OmniRoute/pull/11945)) — thanks @dpozimski +- **feat(providers):** add MaxAI as a signed, OpenAI-compatible provider serving its 13 paid chat models (GPT-5.6 / Luna / Thinking, Claude 5 Sonnet, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite, Grok 4.1-fast / 4.5, DeepSeek V3.2 / R1, Llama 3.3 70B) through OmniRoute's `/v1` endpoint, with per-request HMAC-SHA1→SM3→AES request signing, live model + context-window discovery from `/models/get_config`, and prompted tool-calling translated to OpenAI `tool_calls` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI vision input — image_url content parts are forwarded inline in `message_content` to the 6 vision-capable models (GPT-5.6 / Luna / Thinking, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite) ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI image generation — 6 image models (gpt-image-1, dall-e-3, flux-1-schnell/dev/pro, sd3-medium) exposed through `POST /v1/images/generations` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI document RAG — inline base64 file/document attachments are uploaded to MaxAI (content-addressed `doc_id`) and attached to the chat via `doc_list` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** browserless MaxAI onboarding — email device-pair login (`/api/providers/[id]/login`) and signed access-token refresh, so a connection can be created and kept fresh without a real browser or Google OAuth ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** per-provider TLS impersonation profile (MaxAI presents a Windows Firefox-150 client fingerprint) so its bot-sensitive endpoints accept OmniRoute traffic ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** add **Nimble** as a web-search and web-fetch provider (`nimble-search`) — `/v1/search` routes to Nimble's search API at `lite` depth (locale, freshness and include/exclude domain filters mapped onto the shared request contract), and `/v1/web/fetch` routes to Nimble Extract, which covers all four fetch formats (`markdown`, `html`, `links`, `screenshot`) from a single call. One API key serves both surfaces. ([#11620](https://github.com/diegosouzapw/OmniRoute/pull/11620)) — thanks @wildcard +- The npm publish is automatic again, through npm Trusted Publishing (OIDC): the hosted `stage-npm` job publishes with a short-lived credential minted from GitHub's id-token — no `NPM_TOKEN`, no 2FA prompt, provenance attached. `publish_mode=staged` (owner approves with 2FA) and `direct` (token) remain available on `workflow_dispatch`. ([#11931](https://github.com/diegosouzapw/OmniRoute/pull/11931)) +- **feat(providers):** Add **Opper** as an API-key gateway provider — EU-hosted AI gateway with 700+ models from 30+ providers behind one OpenAI-compatible API and one key (`OPPER_API_KEY`); model ids use `provider/model` format (e.g. `anthropic/claude-sonnet-4-6`, `openai/gpt-5`); live model catalog at `https://api.opper.ai/v3/compat/models`; entry mirrors `requesty` (same shape, `passthroughModels: true`, no static seed) ([#11629](https://github.com/diegosouzapw/OmniRoute/pull/11629)) +- **feat(dashboard):** the `/dashboard/orchestration` snapshot hook now subscribes to the `agents` WebSocket channel (`agent.task.updated`) instead of `requests` as its refetch trigger, and relaxes its background poll from 5s to 30s while that WS connection is up — falling back to the tighter 5s cadence, reprogrammed live on any connect/disconnect transition, whenever the socket is down. ([#12409](https://github.com/diegosouzapw/OmniRoute/pull/12409)) +- **feat(dashboard):** Orchestration canvas quick wins — search box plus state/source/provider filter chips with a one-click clear, and per-source collapse/expand, all reflected in the URL so a filtered/collapsed view is shareable and survives a refresh; the detail drawer gained a "copy trace JSON" action and hardened error/empty-state and accessibility handling; the Agents-tab edges now animate traveling particles along active (running) connections; and the canvas node/edge status colors moved off fixed hex values onto theme-aware `--orch-status-*` CSS custom properties, so they adapt correctly to light/dark mode. ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- **chore(dashboard):** Orchestration UI hardening pass and the missing component/model test coverage it called for — `OrchestratorNode`/`ActivityNode`/`OverflowNode` rendering, the `?node=`/overflow-click page routing, the Agents-tab orchestrator-click no-op and `showCompleted` toggle, and the overview kanban's done-column sort order (#12270, #12271). ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- New `/dashboard/orchestration` page: live unified view of everything running — Cloud Agent, A2A and Conductor as a real-time graph (Agents tab), the combo cascade (Routing tab, reusing the Combo Live Studio) and a state kanban (Overview tab), with a detail drawer (trace, cost, approve/cancel). Read-only over existing APIs — no new backend. Canvas concept credit: PR #11815 design ([#12261](https://github.com/diegosouzapw/OmniRoute/pull/12261)) +- **feat(dashboard):** new "History" tab on `/dashboard/orchestration` — an Airflow-style grid of finished runs over a 24h/7d/30d preset window, one row per (source, identity), clicking a cell opens the existing detail drawer. It is backed by real persistence: A2A task lifecycle transitions are now written to the `a2a_tasks` table (purged after 30 days, configurable via `OMNIROUTE_A2A_HISTORY_RETENTION_DAYS`) and served by the new `GET /api/a2a/tasks/history` listing endpoint, with the task-detail route falling back to persisted history once a run leaves the in-memory snapshot. Conductor runs stay remote and are not persisted locally — the tab says so instead of silently omitting them. ([#12479](https://github.com/diegosouzapw/OmniRoute/pull/12479)) +- **feat(dashboard):** the orchestration detail drawer gained a "Repeat" action for Cloud Agent, A2A and Conductor tasks — a two-click confirm (click once to arm, click again within the confirm window to fire) re-submits the original prompt/input as a new run. The button is disabled with an explanatory tooltip whenever the original input can't be recovered from the loaded task detail (e.g. it never carried a prompt, or the detail failed to load). Two limitations of the A2A variant, by design: it targets the `/a2a` JSON-RPC endpoint, which authenticates with an API key only (`REQUIRE_API_KEY=true` or a configured `OMNIROUTE_API_KEY` makes a dashboard-session repeat answer `HTTP 400` — surfaced verbatim in the drawer's error line, never as a success), and the `message/send` call is SYNCHRONOUS: the POST blocks for the whole skill run, so the success confirmation only appears once the repeated task finishes. A dashboard-authenticated A2A creation path is deliberately left to a follow-up — widening the endpoint's auth posture is an operator decision, not a side effect of this feature. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(a2a):** A2A task execution now records which memories were consulted for the task's last user message as `metadata.memoryHits` (id/key/type/content-snippet) plus a `memory_hits` history event, purely for observability — the retrieved memory is never injected into a skill's prompt or behavior. Gated by the `OMNIROUTE_A2A_MEMORY_HITS` kill-switch (default enabled; set to `0` to skip the recall lookup entirely). The drawer's new "Memory used" section lists these hits for a2a tasks and is omitted whenever there are none. Known limitation: recall only resolves under the keyless posture — a keyed caller's task owner is a SHA-256 prefix of the API key, while memory rows are keyed by the database api-key id, and no hash→id lookup exists today, so the hit list stays empty for keyed callers. The recorded hits are also kept out of the task's own `input` (and therefore out of the persisted input and of the "Repeat" request body), so repeating a task never re-sends the previous run's memory snippets. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(providers):** add a Perplexity Agent API provider (`perplexity-agent` / `pplx-agent`) for Perplexity `/v1/responses`, including the documented Anthropic, OpenAI, Google, xAI, DeepSeek, Z.AI, Moonshot/Kimi, NVIDIA, and Perplexity model IDs plus Anthropic-model `max_output_tokens` compatibility. ([#12103](https://github.com/diegosouzapw/OmniRoute/pull/12103)) — thanks @AIB1TAL0S +- **feat(providers):** add RPD (Requests Per Day) limit to provider rate limit overrides across UI, schemas, DB, and i18n ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) — thanks @b3nw +- **feat(providers): add UC Direct (uncensored.com Developer API), the metered OpenAI-compatible surface.** A standard OpenAI-compatible passthrough (default executor) for uncensored.com's official REST API at `https://api.uncensored.com/api/v1`: `X-api-key` auth (never-expiring `uai_sk_live_` key), `POST /chat/completions` with streaming SSE and native tool-calling, and the full live metered catalog (82 models across 15 providers, discovered from the public `GET /v1/models`). Registered as provider `uc-direct` (alias `ucd`). Complements the un-metered `uc` persona provider — same models, metered credits and a plain API key instead of a subscription session. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(providers): add UC (uncensored.com), the un-metered subscription "persona" chat as an OpenAI-compatible provider.** A WebSocket web-app port: a durable Clerk credential mints a short-lived session token per connect (browserless — no API key), driving UC's persona socket. Ships the browserless email-code login (request → verify → harvest), the 19 verified persona models (Claude Opus, Gemini, Grok, GLM, Kimi, DeepSeek, MiniMax, incl. the uncensored variants), prompted `` tool-calling with a per-model code-style dialect + auto-cure retry for guardrailed models, live ``/reasoning split, streaming + non-streaming OpenAI responses, and full quota/auth error surfacing (paywall / message-limit / rate-limit → 429, invalid session → 401 re-login). Full multimodal parity via the persona blob-upload layer: **vision** (image input, 15 vision-capable models), **document RAG** (PDF/doc upload, server-side extraction), **image generation** (22 models), **video generation** (14 models, async signed-url → poll), and **TTS** (streaming MP3). Registered as provider `uc` (alias `ucn`). The metered OpenAI-compatible Developer API is a separate `uc-direct` provider. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(logs):** add copy-all button to request detail modal ([#11083](https://github.com/diegosouzapw/OmniRoute/pull/11083)) — thanks @AndrianBalanescu +- **feat(check):** make serviceKinds required and add the reverse-walk provider consistency gate ([#11392](https://github.com/diegosouzapw/OmniRoute/pull/11392)) — thanks @Tushar49 +- **feat(providers):** only advertise verified-working connections by default ([#11448](https://github.com/diegosouzapw/OmniRoute/pull/11448)) — thanks @alltomatos +- **feat(resilience):** add hierarchical concurrency admission ([#11493](https://github.com/diegosouzapw/OmniRoute/pull/11493)) +- **feat(providers):** derive + surface expiry for JWT-bearing web cookies (#11497) ([#11505](https://github.com/diegosouzapw/OmniRoute/pull/11505)) — thanks @oyi77 +- **feat(tunnels):** support named cloudflared tunnels via CLOUDFLARED_CONFIG ([#11561](https://github.com/diegosouzapw/OmniRoute/pull/11561)) — thanks @ggiak +- **feat(compression):** make proactive context-compression threshold a live setting ([#11564](https://github.com/diegosouzapw/OmniRoute/pull/11564)) — thanks @ragnar-claude +- **feat(resilience):** add provider peak-hour protection ([#11622](https://github.com/diegosouzapw/OmniRoute/pull/11622)) — thanks @Neuron-Mr-White +- **feat(providers):** add 1min.ai provider ([#11631](https://github.com/diegosouzapw/OmniRoute/pull/11631)) — thanks @zero-executioner +- **feat(dashboard):** collapsible JSON tree viewer for request/response payloads ([#11703](https://github.com/diegosouzapw/OmniRoute/pull/11703)) — thanks @hartmark +- **feat(routing):** add LiquidAI LFM2.5-2.6B free tier via OpenRouter ([#11752](https://github.com/diegosouzapw/OmniRoute/pull/11752)) — thanks @brick30llc-ctrl +- **feat(pricing):** add GLM-5.3-Flash pricing, model specs, and catalog registration ([#11830](https://github.com/diegosouzapw/OmniRoute/pull/11830)) — thanks @AndrianBalanescu +- **feat(routing):** add relayMode for schema-locked context handoffs ([#11839](https://github.com/diegosouzapw/OmniRoute/pull/11839)) — thanks @raheemuddin786 +- **feat(config):** add support for runtime system prompt configuration and hot-reloading ([#11841](https://github.com/diegosouzapw/OmniRoute/pull/11841)) — thanks @raheemuddin786 +- **feat(routing):** expand connection-aware quota prefilter across combo strategies (#11682) ([#11850](https://github.com/diegosouzapw/OmniRoute/pull/11850)) — thanks @HouMinXi +- **feat(models):** reactive discovery sync after Antigravity model-not-found 404 ([#11888](https://github.com/diegosouzapw/OmniRoute/pull/11888)) — thanks @MumuTW +- **feat(models):** auto-aliases and token limits from synced Antigravity discovery ([#11889](https://github.com/diegosouzapw/OmniRoute/pull/11889)) — thanks @MumuTW +- **feat(settings):** raise sticky round-robin limit caps to 1000 ([#12015](https://github.com/diegosouzapw/OmniRoute/pull/12015)) — thanks @adivekar-utexas +- **feat(resilience):** operator-configurable global credential health check interval ([#12043](https://github.com/diegosouzapw/OmniRoute/pull/12043)) — thanks @adivekar-utexas +- **feat(quota):** use official OpenCode Go usage API ([#12124](https://github.com/diegosouzapw/OmniRoute/pull/12124)) — thanks @ddarkr +- **feat(resilience):** default the credential health check sweep to 60 minutes ([#12138](https://github.com/diegosouzapw/OmniRoute/pull/12138)) — thanks @adivekar-utexas +- **feat(quality):** new-code mode for the complexity and dead-code ratchets (Clean as You Code) ([#12142](https://github.com/diegosouzapw/OmniRoute/pull/12142)) +- **feat(dashboard):** orchestration canvas — unified model + snapshot hook (part 1/2) ([#12156](https://github.com/diegosouzapw/OmniRoute/pull/12156)) +- **feat(usage):** add Kilo Code balance and Kilo Pass quotas ([#12178](https://github.com/diegosouzapw/OmniRoute/pull/12178)) — thanks @killer30001000 +- **feat(providers):** refresh vendored ChatGPT Web connector to v4.0.7 ([#12181](https://github.com/diegosouzapw/OmniRoute/pull/12181)) — thanks @backryun +- **feat(providers):** manual "Clear cooldown" action in the cooling panel ([#12224](https://github.com/diegosouzapw/OmniRoute/pull/12224)) — thanks @patrykkopycinski +- **feat(providers):** restore ChatGPT Web via clean-room browser transport ([#12239](https://github.com/diegosouzapw/OmniRoute/pull/12239)) — thanks @backryun +- **feat(sse):** wire the PROVIDER_PROFILES window gate into the global provider cooldown ([#12247](https://github.com/diegosouzapw/OmniRoute/pull/12247)) +- **feat(providers):** modernize CLOVA Studio chat and embeddings ([#12277](https://github.com/diegosouzapw/OmniRoute/pull/12277)) — thanks @backryun +- **feat(browser):** adopt Obscura as primary headless browser engine with Chromium fallback ([#12286](https://github.com/diegosouzapw/OmniRoute/pull/12286)) — thanks @opensource-elearning +- **feat(sse):** full language parity for output styles (es/de/fr/it/ru/zh + autoDetect) ([#12289](https://github.com/diegosouzapw/OmniRoute/pull/12289)) +- **feat(quality):** complete test:scoped — --full map rebuild, stdin selection, CI loader parity (#8084 D1) ([#12353](https://github.com/diegosouzapw/OmniRoute/pull/12353)) +- **feat(providers):** refresh Fable, Cursor, and Devin catalogs ([#12367](https://github.com/diegosouzapw/OmniRoute/pull/12367)) — thanks @backryun +- **feat(video):** redact transcript text from logs and durable memory (#12150 P1) ([#12427](https://github.com/diegosouzapw/OmniRoute/pull/12427)) +- **feat(dashboard):** parent-link, genuine-continuation badge, and modal perf fixes ([#12448](https://github.com/diegosouzapw/OmniRoute/pull/12448)) — thanks @hartmark +- **feat(dashboard):** adaptive context-budget dial on compression panel ([#12488](https://github.com/diegosouzapw/OmniRoute/pull/12488)) — thanks @HouMinXi +- **feat(providers):** refresh Z.ai Web models and browser transport ([#12524](https://github.com/diegosouzapw/OmniRoute/pull/12524)) — thanks @backryun +- **feat(video):** redact raw client-snapshot transcript fields in the detailed log (#12150 P2) ([#12528](https://github.com/diegosouzapw/OmniRoute/pull/12528)) +- **feat(providers):** refresh NVIDIA hosted models ([#12538](https://github.com/diegosouzapw/OmniRoute/pull/12538)) — thanks @backryun +- **feat(quota):** Moonshot Open Platform balance and TPD lock for custom nodes ([#12590](https://github.com/diegosouzapw/OmniRoute/pull/12590)) — thanks @HouMinXi +- **feat(video):** redact transcript in the in-memory pending-request snapshot (#12430 item 6) ([#12596](https://github.com/diegosouzapw/OmniRoute/pull/12596)) +- **feat(dashboard):** link a log entry's Conversation Context to its owning conversation ([#12646](https://github.com/diegosouzapw/OmniRoute/pull/12646)) — thanks @hartmark +- **feat(catalog):** eligibility-gated free-tier bucket ([#12669](https://github.com/diegosouzapw/OmniRoute/pull/12669)) +- **feat(db):** fail-closed previous_response_id continuation for redacted video turns (#12150 P2b) ([#12707](https://github.com/diegosouzapw/OmniRoute/pull/12707)) +- **feat(gamification): pay the documented `streak_bonus` and `badge_unlock` XP rewards.** `XP_REWARDS` listed both rewards but the award pipeline never paid them: the private reward table in `events.ts` omitted them, `updateStreak()` did not report when a streak extended, and badge unlocks carried no XP. Every request that extends a daily streak now pays `streak_bonus × streak length` once per UTC day (guarded by a same-day `xp_audit_log` check), and every badge unlocked through the pipeline pays `badge_unlock` once per badge (guarded by the `user_badges` primary key; `unlockBadge()` now reports whether it inserted). Bonus XP flows through the same `addXp` + level sync + global/weekly/monthly leaderboard path as action XP, so level-ups and rankings include it. The Radar supporter recognition unlock stays XP-free. (#12522 — thanks @pacocartones) +- **feat(providers):** advertise a `free-tier` capability in the provider plugin manifest for every provider with documented free models, so sidecars and dashboards can filter free-capable providers without reading the quota catalog ([#12786](https://github.com/diegosouzapw/OmniRoute/pull/12786)) — thanks @maxmad64bis +- **feat(providers):** Added EURouter as an OpenAI-compatible API-key gateway (`https://api.eurouter.ai/v1`), with live model discovery via `passthroughModels`. Its copy states that models are served by third-party upstreams listed per model, so an EU-based router is not read as EU data residency for inference. ([#13025](https://github.com/diegosouzapw/OmniRoute/pull/13025)) — thanks @ntdatt812 +- **feat(providers):** Added GreenPT as an OpenAI-compatible API-key provider (`https://api.greenpt.ai/v1`), with live model discovery via `passthroughModels`. No free-inference badge: the published docs describe a free API subscription billed per token, not a free tier. ([#13024](https://github.com/diegosouzapw/OmniRoute/pull/13024)) — thanks @ntdatt812 +- **feat(i18n):** 9 new locales — Greek (`el`), Croatian (`hr`), Serbian (`sr`), Lithuanian (`lt`), Estonian (`et`), Latvian (`lv`), Slovenian (`sl`), Maltese (`mt`), Irish (`ga`) — across the dashboard, docs mirrors, CLI, README and the site; OmniRoute now ships every official EU language (51 locales). (#13044) +- **feat(i18n):** 8 new locales — Kannada (`kn`), Malayalam (`ml`), Odia (`or`), Punjabi (`pa`), Nepali (`ne`), Sinhala (`si`), Burmese (`my`), Khmer (`km`) — across the dashboard, docs mirrors, CLI, README and the site (59 locales). The translator now restores the ICU literal escape around angle placeholders and splits oversized docs sections before translating. (#0000) ([#13660](https://github.com/diegosouzapw/OmniRoute/pull/13660)) +- feat(providers): **list Agnes 3.0 Flash as the current free chat model, drop retired 1.5 Flash, add Image 2.0/2.5 Flash plus Video 2.5/2.5 Flash, and discover the live `/v1/models` catalog (including the CN host `api.agnes-ai.cn`).** `agnes-1.5-flash` now forwards to `agnes-3.0-flash`. Video 2.5 polls `GET /v1/videos/{id}` (not the V2.0 `/agnesapi` contract). Live `/v1/models` (2026-09-09) no longer serves 1.5; the wiki marks it deprecated. 3.0 Flash is 512K context / 65,536 max output, same window as 2.5. CN-region keys use the existing per-connection base-URL field, default stays `apihub.agnes-ai.com`. ([#13120](https://github.com/diegosouzapw/OmniRoute/pull/13120)) — thanks @HouMinXi +- **feat(sse):** Codex and OpenAI catalogs list GPT-6 Astra with effort aliases (`-low` through `-ultra`); Codex CLI identity pins `@openai/codex@0.153.4` in lockstep with the image ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **feat(dashboard):** the orchestration History tab gained a "Compare runs" mode — toggling it turns each grid cell into a 2-item selection queue (a 3rd click drops the oldest pick), and picking two cells opens a side-by-side comparison panel instead of the usual detail drawer. The panel fetches both runs' detail the same way the drawer does (falling back to persisted history once a run leaves the live TTL window) and shows, per side: identity/source/state, start time, a signed `right - left` delta for duration/cost/event count, the event timeline aligned by index, and any memory hits. One side's fetch failing never blocks the other, and a delta is only ever computed when both sides have a finite value — otherwise it renders "—", never `NaN`. Comparing two runs from different sources or skills still works; a banner marks the deltas as informational rather than hiding them, since the two runs aren't a strict apples-to-apples pair. ([#12677](https://github.com/diegosouzapw/OmniRoute/pull/12677)) +- **feat(dashboard):** badge a conversation that never reached a clean stop ([#12717](https://github.com/diegosouzapw/OmniRoute/pull/12717)) — thanks @hartmark +- **feat(release):** reconcile-changelog tool + version-anchored fragment aggregation ([#12987](https://github.com/diegosouzapw/OmniRoute/pull/12987)) +- **feat(mcp):** carry context_length through MCP list_models_catalog projection (#12776) ([#13406](https://github.com/diegosouzapw/OmniRoute/pull/13406)) — thanks @KooshaPari +- **feat(cli):** make startup readiness budget configurable (#13369) ([#13433](https://github.com/diegosouzapw/OmniRoute/pull/13433)) — thanks @KooshaPari + ### 🐛 Bug Fixes +- **security(streaming):** sanitize generic mid-stream error messages before emitting OpenAI, + Responses, or Claude SSE failure frames and before diagnostic logging, while preserving raw + failures for internal classification and keeping client disconnects out of provider failure state. ([#12457](https://github.com/diegosouzapw/OmniRoute/pull/12457)) +- **fix(claude):** `blockExtraUsage: false` no longer lets 5h quota preflight skip the account; extra usage is billed after the session bar is gone, so the request must reach Anthropic ([#12803](https://github.com/diegosouzapw/OmniRoute/pull/12803)) — thanks @HouMinXi +- Sanitize HuggingChat conversation-creation and message-send transport failures before they reach client error bodies or provider logs. ([#12467](https://github.com/diegosouzapw/OmniRoute/pull/12467)) +- **fix(dashboard):** Keep local and theme-aware provider SVG icons at a definite layout size so Chromium does not collapse them to 0×0 after the v3.8.50 image-rendering change ([#12054](https://github.com/diegosouzapw/OmniRoute/pull/12054)) — thanks @ponkcore +- **fix(security):** Sanitize provider and runtime failures before public API, SSE and MCP responses and before persistent request, proxy and usage logs, preventing credentials, stack traces and host filesystem paths from crossing those boundaries while preserving stable error codes and useful diagnostics. ([#12506](https://github.com/diegosouzapw/OmniRoute/pull/12506)) +- **fix(cli):** `omniroute tunnel create` no longer crashes with `Cannot read properties of undefined (reading optsWithGlobals)` — removed the duplicate positional argument that caused Commander.js to misalign the action callback parameters ([#12295](https://github.com/diegosouzapw/OmniRoute/issues/12295)) ([#12368](https://github.com/diegosouzapw/OmniRoute/pull/12368)) — thanks @gonisulaimann +- Stop advertising Gemini Live-only models as supported audio endpoints until OmniRoute proxies the bidirectional Live protocol. ([#11710](https://github.com/diegosouzapw/OmniRoute/pull/11710)) — thanks @RaviTharuma +- **fix(kie):** reroute `flux/kontext` off the KIE Market `createTask` flow — it is catalogued with `isMarket: true` but has no Market catalog page, so KIE rejected it with "model name not supported"; it now hits the dedicated `POST /api/v1/flux/kontext/generate` / `GET /api/v1/flux/kontext/record-info` endpoints instead (#11296). ([#11985](https://github.com/diegosouzapw/OmniRoute/pull/11985)) — thanks @hartmark +- **fix(kie):** correct 12 more KIE Market catalog ids that were sent to `createTask` unchanged but diverge from KIE's documented upstream `model` values — GPT Image 2 T2I/I2I (drops the `gpt/` prefix), GPT Image 1.5 T2I/I2I (`gpt-image/` namespace), Seedream 5.0 Lite T2I/I2I (drops the `.0`), all 4 Flux 2 variants (`flux-2/` namespace, generic variant renamed `flex`), and Wan 2.7 Image / Image Pro (dash instead of dot) — each verified individually against the literal example request published on docs.kie.ai. `#11326`'s "everything else already matches" claim was wrong a second time (#11296); `z-image/4.0-*`/`z-image/4.5-*` and `flux/kontext` remain open, documented as unresolved in `KIE_MARKET_UPSTREAM_MODEL_IDS`'s comment pending further verification. ([#11667](https://github.com/diegosouzapw/OmniRoute/pull/11667)) +- **fix(dashboard):** `useApiKeySave.handleSaveApiKey` no longer forces a full upstream `/models` catalog sync on every non-curated provider connection save — callers can now pass `skipModelSync: true` to opt out, so a workflow that only wants to add one manual model no longer floods the provider's available-models list with hundreds/thousands of synced entries. The flag is a client-side intent signal only and is stripped before the connection payload is POSTed to `/api/providers`; default behavior (full sync on save) is unchanged when the flag is omitted (#11324) ([#11664](https://github.com/diegosouzapw/OmniRoute/pull/11664)) +- **Provider connections:** keep `tokenExpiresAt` when a connection is created. The create-path allowlist omitted it, so every insert stored NULL and the dashboard token badge could read a fresh connection as expired until its first background refresh ([#11368](https://github.com/diegosouzapw/OmniRoute/pull/11368)). — thanks @ntdatt812 +- **fix(combos):** the combo builder's precision-select, global-model-search, and manual-entry flows now serialize a model step's `model` string using the provider's already-computed routing-alias prefix (e.g. `oc/`) instead of rebuilding it from the raw canonical `providerId`, fixing the no-auth "OpenCode Free" provider (`opencode`) being routed to the unrelated paid "OpenCode Zen" provider (`opencode-zen`) because `opencode` doubles as a manual routing-prefix override ([#11433](https://github.com/diegosouzapw/OmniRoute/issues/11433)). ([#11670](https://github.com/diegosouzapw/OmniRoute/pull/11670)) +- **fix(radar):** the catalog feed cache now keeps `generatedAt`, the date the feed's data was built, next to `fetchedAt`, the date this install downloaded it (#11435). The feed schema requires that date and the sync path validates it, but the cache dropped it — so a feed fetched minutes ago and one carrying weeks-old figures looked identical to everything downstream, including the dashboard's "Last fetched" line. `getRadarCatalog().meta` and `GET /api/radar/status` now report both dates, the latter as its own field rather than folded into `version` — and omitted entirely for the offers and intel caches, which keep no build date, where a `null` would read as "unknown" rather than "never stored". The dashboard still shows only the fetch time; surfacing the build date there needs a new translated label and is left to a follow-up. Rows cached before migration 163 read back as `null`: unknown stays unknown instead of borrowing the fetch time. The referrals cache has persisted the same date since migration 142. — thanks @maxmad64bis +- **fix(cli):** postinstall no longer fills `JWT_SECRET` and `API_KEY_SECRET` in the installed package's `.env` (#11436). `.env.example` ships both blank on purpose: the server restores them from its durable store, or generates and persists them there, in `ensureSecrets()`. Pre-filling them defeated that — the file lives inside the package directory, so `npm i -g` replaced it and postinstall wrote _different_ values, while `ensureSecrets()` (which only acts on an empty variable) never got to restore the real ones. Both secrets rotated silently on every update, invalidating dashboard sessions and API-key CRCs. `STORAGE_ENCRYPTION_KEY` left the same list for the same reason in #1622; its comment pointed at a function that no longer exists and now names the real provisioning path. — thanks @maxmad64bis +- **fix(cli):** the CLI now says when a loaded `.env` lives inside the installed package directory (#11437). It already announces every env file it reads, without distinguishing the ones that survive an update from the one that does not: `npm i -g` replaces the package directory wholesale, so values set there are gone at the next update, silently. The warning names the durable path to move them to, and fires only when that file actually supplied a value — a file entirely shadowed by a durable one supplied nothing. A development checkout stays silent: there the same path is stable and documented in `SETUP_GUIDE.md`. — thanks @maxmad64bis +- **fix(free-models):** the shared `isFreeModel()` predicate no longer reports catalog entries marked `freeType: "discontinued"` as free, so `hidePaidModels` can't route to Pollinations' seven premium models that now require a paid key ([#11441](https://github.com/diegosouzapw/OmniRoute/pull/11441)) — thanks @maxmad64bis +- fix(ui): let AnySearch use the normal provider-icon fallback when LobeHub has no matching icon (#11449) +- Fixed the v3.8.50 Costs and Analytics dashboards so flat-rate Claude Code usage can be shown as an explicitly requested token-price estimate without changing default billed-cost semantics. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed archived usage retention so each request is priced individually instead of pricing a day's summed tokens once, which understated archived cost whenever a day mixed cache-heavy and ordinary requests. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed the Costs dashboard so it discloses when displayed figures include flat-rate token-price estimates instead of labelling them as billed spend, using the flag the analytics API already returns; the month-end projection and the CSV/JSON exports carry the same marker, and billed-cost mode is unchanged. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- **fix(combo):** attach the same combo-diagnostics trace (`poolSize`/`attemptOrder`/`excluded`/`terminalReason`, plus `x-omniroute-combo-*` headers) to the round-robin strategy's and the nested pipeline/fusion runtime-unit loop's "Maximum combo retry limit reached" 503 that the priority-strategy path already attaches for the identical terminal condition — previously those two paths returned a bare, contextless 503 ([#11462](https://github.com/diegosouzapw/OmniRoute/issues/11462)). ([#11665](https://github.com/diegosouzapw/OmniRoute/pull/11665)) +- **fix(bun):** use native `bun:sqlite` in `bootstrap-env` and `sync-env` to avoid loading `better-sqlite3` N-API addon during Bun startup ([#11468](https://github.com/diegosouzapw/OmniRoute/pull/11468)) — thanks @TheDemonTuan +- **fix(docker-bun):** make `Dockerfile.bun` install reliable with pre-install script helpers and native `bun:sqlite` smoke check ([#11470](https://github.com/diegosouzapw/OmniRoute/pull/11470)) — thanks @TheDemonTuan +- **fix(docker-bun):** remove every vendored `better-sqlite3` native addon from the Bun runtime image so startup cannot abort before the `bun:sqlite` fallback ([#11482](https://github.com/diegosouzapw/OmniRoute/pull/11482)) — thanks @TheDemonTuan +- **fix(autoCombo):** effort/alias model variants (`gpt-5.6-sol-xhigh`, `gpt-5.6`, cursor's `claude-4.6-opus-high`) inherit their base model's task fitness instead of falling to the wildcard 0.5 ([#11492](https://github.com/diegosouzapw/OmniRoute/pull/11492)) — thanks @MumuTW +- fix(db): rate-limit repeated Arena ELO leaderboard fetch-failure warnings instead of logging one per sync attempt (#11500) ([#11989](https://github.com/diegosouzapw/OmniRoute/pull/11989)) — thanks @hartmark +- **fix(db):** dedupe the raw `[Encryption] Decryption failed...` log line emitted by the lazy-decrypt views (`createLazyRowProxy`/`createLazyConnectionView`), which power `getProviderConnections()` and were re-triggering that line on every CredentialHealth/model-sync cycle for the same corrupt or stale-key credential — a fresh Proxy over a fresh row on every cycle meant the per-proxy memoization never suppressed it, unlike the dedup `decryptConnectionFields()` already had since [#9927](https://github.com/diegosouzapw/OmniRoute/issues/9927). Now shares that dedupe tracking so the line logs at most once per credential ([#11500](https://github.com/diegosouzapw/OmniRoute/issues/11500)). ([#11678](https://github.com/diegosouzapw/OmniRoute/pull/11678)) +- **fix(intelligence):** Arena ELO sync stores a synthesized base-model row for every effort/harness variant (`claude-opus-5-max` → `claude-opus-5`, `gpt-5.6-sol-xhigh (codex-harness)` → `gpt-5.6-sol`), so bare model requests reach the synced intelligence layer instead of the static fallback table; the hand-maintained `MODEL_ALIAS_MAP` — which collapsed generations (`gpt-5.5` → `gpt-5`) — is gone ([#11506](https://github.com/diegosouzapw/OmniRoute/pull/11506)) — thanks @MumuTW +- **fix(autoCombo,sse):** catalog hygiene — the static task-fitness table no longer ranks retired models above live ones (dead rows dropped, patterns matched on segment boundaries), the 7 deprecation aliases that forwarded to retired or non-existent models now point at the vendors' published replacements, an alias is no longer rewritten when the serving provider offers the id as-is, and a new `check:model-lifecycle` gate diffs both tables against a vendor lifecycle snapshot ([#11507](https://github.com/diegosouzapw/OmniRoute/pull/11507)) — thanks @MumuTW +- **fix(sse):** stop dropping resolved `thoughtSignature` values on parallel (multi tool-call) turns sent to Gemini 3.x — the claude→gemini and openai→gemini translators previously kept the signature only on the *first* function call of a message, causing Gemini to reject subsequent calls in the same turn with HTTP 400 "Function call is missing a thought_signature"; each function call now keeps its own resolved signature ([#11510](https://github.com/diegosouzapw/OmniRoute/issues/11510)). ([#11669](https://github.com/diegosouzapw/OmniRoute/pull/11669)) +- Treat Docker publishing from a non-default release branch as an expected skip instead of a failed workflow, while preserving the guard that prevents it from overwriting the `next` channel. ([#11527](https://github.com/diegosouzapw/OmniRoute/pull/11527)) — thanks @pacocartones +- fix(search): prefer credentialed providers over duckduckgo-free fallback (#11524) — the fallback-only loop ran before the credentialed-providers loop in `executeWebSearch`, making configured providers unreachable when `duckduckgo-free` was available. ([#11565](https://github.com/diegosouzapw/OmniRoute/pull/11565)) — thanks @vsd2807 +- **fix(sse):** cap the upstream headers-wait phase for STREAMING requests to a client-realistic ceiling (110s, under Codex's own ~120s hard client-abort window) instead of the flat 10-minute `FETCH_TIMEOUT_MS` default — that default was 5x longer than the body-phase readiness watchdog's own adaptive bound, so a request whose upstream never returned any response at all (not even headers, e.g. a stalled NVIDIA target behind a tool-heavy Responses→Chat translation) kept the client connection alive on keepalives only, guaranteeing the client's own patience ran out first with an opaque 499 instead of OmniRoute detecting and failing the stall fast. Non-streaming requests are unaffected — they keep the existing flat default (`open-sse/utils/fetchStartTimeoutPolicy.ts`) (#11526) ([#11668](https://github.com/diegosouzapw/OmniRoute/pull/11668)) +- **fix(ci):** protocol-clients E2E harness boots the peer-stamped custom server (`run-next.mjs dev`) instead of the bare `next` CLI, so `LOCAL_ONLY` locality resolves from the real TCP peer stamp and `/api/mcp/audit` answers 200 (open bootstrap preserved via an `OMNIROUTE_E2E_BOOTSTRAP_MODE=open` credential clear in `run-next.mjs`, pinned to loopback `HOST`) instead of a deterministic 403 (#11535) ([#11549](https://github.com/diegosouzapw/OmniRoute/pull/11549)) — thanks @jonlwheat2-gif +- **fix(docs):** the Auto-Combo engine is described with the number of factors it actually declares. Four different counts were in circulation — 6, 9, 13 and 14 across `AGENTS.md`, four guides and six code comments — while `DEFAULT_WEIGHTS` declares 15. `check:docs-counts` now reads that number from the source, so adding a factor without updating the prose fails CI ([#11545](https://github.com/diegosouzapw/OmniRoute/pull/11545)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **fix(quota-share):** in-flight leases are per request, so an aborted request's slot ages out instead of being kept alive by later traffic on the same connection ([#11547](https://github.com/diegosouzapw/OmniRoute/pull/11547)) — thanks @abhisheksharma2411 +- The free-tier summary route now serves the refreshed Radar catalog when the feed is active — the same numbers the dashboard shows — and states which catalog answered plus its real build date, instead of always reporting release-frozen figures with a stale curation date. The supporter-key live feed stays reserved to authenticated callers of the instance. ([#11550](https://github.com/diegosouzapw/OmniRoute/pull/11550)) — thanks @maxmad64bis +- **sse:** fix LiveWS/embed-WS servers crashing at startup under the Node/tsx runtime — `liveServer.ts`, `embedWsProxy.ts` and `apiBridgeServer.ts` imported `@/shared/utils/httpClientAbortGuard` without the `.mjs` extension, so the client-abort crash guard added by [#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556) was unreachable and every dependent test failed with `ERR_MODULE_NOT_FOUND` ([#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556)). ([#11671](https://github.com/diegosouzapw/OmniRoute/pull/11671)) — thanks @jonlwheat2-gif / @yxyxy +- fix(chat): shed any of the matched provider-node's routing identifiers (public prefix or internal id) before model lookup, so composites like `//` no longer reach upstream verbatim (#11557) — thanks @maxmad64bis +- **fix(cache):** a semantic-cache entry now expires on its TTL instead of surviving until the next UTC midnight, and `dbEntries` stops counting expired rows ([#11573](https://github.com/diegosouzapw/OmniRoute/pull/11573)) — thanks @ntdatt812 +- **fix(api):** `/v1/models` schedules its stale-while-revalidate rebuild through Next's `after()` again, so a stale catalog reaches the client before the rebuild blocks the event loop ([#11574](https://github.com/diegosouzapw/OmniRoute/pull/11574)) — thanks @ntdatt812 +- **fix(check):** the `known-symbols` executor conformance gate awaits the now-async `getExecutor()`, so it stops reporting all 142 registered aliases as dead symbols and can detect a lazy import that fails ([#11582](https://github.com/diegosouzapw/OmniRoute/pull/11582)) — thanks @ntdatt812 +- **fix(pack):** register `bin/cli/utils/volatileEnvPath.mjs` in `PACK_ARTIFACT_REQUIRED_PATHS`, so the prune can no longer silently drop a module the published CLI imports on every boot ([#11588](https://github.com/diegosouzapw/OmniRoute/pull/11588)) — thanks @ntdatt812 +- **i18n(vi):** translate the five reliability-column strings on the free-provider rankings page, which rendered as `__MISSING__:` markers in the Vietnamese UI ([#11589](https://github.com/diegosouzapw/OmniRoute/pull/11589)) — thanks @ntdatt812 +- **resilience:** restore the expired-connection retry-budget probe in the token-health sweep — the `!isGitHubAccessTokenOnlyConnection` carve-out reintroduced by #11608 contradicted the boundary pinned by #11592, so a GitHub connection parked at `expired` with retry budget remaining was never probed and could never self-heal ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)). ([#11672](https://github.com/diegosouzapw/OmniRoute/pull/11672)) — thanks @jonlwheat2-gif / @ntdatt812 +- **fix(dashboard):** Provider Stats now displays combo success rates on their native 0–100 percentage scale instead of multiplying them twice ([#11595](https://github.com/diegosouzapw/OmniRoute/pull/11595)) — thanks @pacocartones +- **fix(providers):** route DuckDuckGo Web Chat through the verified `duck.ai` endpoint, refresh its free model catalog, and avoid forwarding unsolved anti-abuse challenges ([#11597](https://github.com/diegosouzapw/OmniRoute/pull/11597)) — thanks @solstxce +- **fix(ui):** Console log Refresh and Copy controls now expose localized accessible names, keep copy actions visible on keyboard focus, and announce copy completion safely ([#11599](https://github.com/diegosouzapw/OmniRoute/pull/11599)) — thanks @pacocartones +- Dropped the stale-`.eslintcache` `restore-keys` fallback from both "Restore ESLint file cache" steps in `ci.yml`, so the blocking `Lint` job can no longer be served per-file verdicts computed under a different lint config, suppressions file or lockfile. `quality.yml` had already dropped it in #11963; `ci.yml` — the workflow that actually gates PRs — had not (#11600). ([#11996](https://github.com/diegosouzapw/OmniRoute/pull/11996)) +- fix(ci): pass `--pass-on-unpruned-suppressions` in `run-eslint-json.mjs` so the CI Lint job no longer fails when a suppression is merely orphaned by a genuine fix, mirroring the identical fix already in `validate-release-green.mjs` (#11600) ([#11983](https://github.com/diegosouzapw/OmniRoute/pull/11983)) — thanks @hartmark +- fix(handoff): enforce provider allowlist for universal handoff (#11602) — universal handoff now skips summarization when the selected summary model's provider is not included in the configured provider allowlist. ([#11619](https://github.com/diegosouzapw/OmniRoute/pull/11619)) — thanks @vsd2807 +- **fix(dashboard):** Show a stable error state when Search Analytics returns an HTTP error or malformed data ([#11603](https://github.com/diegosouzapw/OmniRoute/pull/11603)) — thanks @pacocartones +- **test(gamification):** pin the aggregate profile level to the XP-derived semantics of #11604 — `getAggregateXp()` now derives `currentLevel` from the summed XP (`calculateLevel(sum)`), not `MAX(stored current_level)`, and the #3484 fixture levels are aligned with the XP curve ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)). ([#11676](https://github.com/diegosouzapw/OmniRoute/pull/11676)) — thanks @jonlwheat2-gif / @pacocartones +- **fix(dashboard):** Keep the Profile level and progress aligned with aggregate XP, including bounded handling for invalid totals ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)) — thanks @pacocartones +- **fix(dashboard):** Prevent locked hidden badges from revealing their icon or opening private badge details before they are earned ([#11605](https://github.com/diegosouzapw/OmniRoute/pull/11605)) — thanks @pacocartones +- **fix(dashboard):** Restore keyboard focus after shared modals close and cancel delayed autofocus during cleanup ([#11607](https://github.com/diegosouzapw/OmniRoute/pull/11607)) — thanks @pacocartones +- **fix(dashboard):** Enable Enter and Space activation for clickable data-table rows without hijacking nested controls ([#11610](https://github.com/diegosouzapw/OmniRoute/pull/11610)) — thanks @pacocartones +- **fix(autoCombo,sse):** vendor-retired catalog ids are dropped from the auto-combo candidate pool and no longer win on leftover `arena_elo` / `user_override` rows; `getModelLifecycleDecision` consults `model-lifecycle.json` (prefix-stripped) so aggregator traffic is not `untracked` for ids the snapshot already knows ([#11625](https://github.com/diegosouzapw/OmniRoute/issues/11625)) ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)) — thanks @MumuTW +- **test(chatcore):** move Codex/Claude combo fixtures off models the lifecycle guard now rejects — `gpt-5.1-codex`/`gpt-5-codex` are vendor-retired (snapshot, #11626) and `claude-3-5-sonnet-20241022` is shut down, so native-passthrough and combo-fallback tests switched to `gpt-5.6-sol` and `claude-sonnet-4.6` ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)). ([#11675](https://github.com/diegosouzapw/OmniRoute/pull/11675)) — thanks @jonlwheat2-gif / @MumuTW +- fix(opencode-plugin): stop a free-tier budget that rounds up across a magnitude from rendering as `1000K`/`1000M` in the model picker — `fmtTokens` chose its unit from the raw token count and then rounded with `toFixed(1)`, so 999,950–999,999 printed as `1000K` rather than `1M` and just under a billion printed as `1000M` rather than `1B` ([#11684](https://github.com/diegosouzapw/OmniRoute/pull/11684)) — thanks @abhisheksharma2411 +- Cap prose-derived Antigravity quota resets at the configured model cooldown maximum while preserving authoritative Retry-After headers and Google RetryInfo hints. (#11695) Thanks @pacocartones. ([#11823](https://github.com/diegosouzapw/OmniRoute/pull/11823)) +- Compare `_rebaseline_*` notes structurally in the ratchet-bank verifier so an object-valued note no longer reports itself as rewritten on an unmodified tree — the nightly `bank-ratchet-shrinks` lane had been aborting on every run since 2026-08-11, leaving 41,672 lines of earned file-size shrinks unbanked (#11702). ([#11892](https://github.com/diegosouzapw/OmniRoute/pull/11892)) — thanks @MumuTW +- **fix(build):** `prepublish.ts` bundles the ChatGPT Web (Codex) MCP bridge through `runBuildTool()` instead of spawning `npx.cmd` raw, fixing the build crash on Node ≥ 20/Windows where `.cmd` shims cannot be spawned without a shell (EINVAL) ([#11704](https://github.com/diegosouzapw/OmniRoute/issues/11704)) ([#11706](https://github.com/diegosouzapw/OmniRoute/pull/11706)) — thanks @jonlwheat2-gif +- fix(codex): keep `parallel_tool_calls:false` on the translated Codex Responses Lite path (#11707) ([#11984](https://github.com/diegosouzapw/OmniRoute/pull/11984)) — thanks @hartmark +- **fix(tests):** the new Gemini Web Images retirement test (#11708) called the now-async `getExecutor()` synchronously, throwing `TypeError: getExecutor(...).getProvider is not a function` instead of asserting Gemini Web chat stays available. Awaited to match the R0.3 executor-registry refactor (#11220). ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(search):** Allow REST and MCP web-fetch requests to select the advertised `anysearch-search` provider explicitly ([#11716](https://github.com/diegosouzapw/OmniRoute/pull/11716)) — thanks @pacocartones +- **fix(providers):** Keep overnight peak-hour protection active after midnight according to the window's configured start day ([#11718](https://github.com/diegosouzapw/OmniRoute/pull/11718)) — thanks @pacocartones +- **fix(providers):** correct all five g4f.space gateway descriptions to document proof-of-work credits or member API keys, variable limits, and the remote prompt boundary instead of promising free keyless access at five requests per minute ([#11738](https://github.com/diegosouzapw/OmniRoute/pull/11738)). +- **fix(db):** `model_capabilities` is created by a migration instead of lazily on the first models.dev sync, so a clean install and an upgraded install converge on the same schema regardless of which features have run ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(ci):** `check:install-upgrade` now fails on an `npm` install truncated by ENOSPC (npm reports it as a warning and still exits 0), authenticates its health probe so the version assertion works against the hardened health payload, frees the clean-install tree before the upgrade phase, and no longer reports a schema divergence computed from a boot that never served ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(routing):** harden session-affinity key derivation against oversized explicit IDs and empty structural-payload collisions, while recognizing Gemini and common text request shapes ([#11746](https://github.com/diegosouzapw/OmniRoute/pull/11746)) — thanks @MumuTW +- **fix(packages/browser-pool):** regenerate `package-lock.json` so the `packages/browser-pool` workspace's locked `playwright`/`@types/node` (and transitives) match its `package.json` specs, fixing cache-only/offline installs (`npm ci --offline`, Nix `buildNpmPackage`) that previously failed with `ENOTCACHED` ([#11747](https://github.com/diegosouzapw/OmniRoute/issues/11747)) — thanks @benjaminkitt ([#11784](https://github.com/diegosouzapw/OmniRoute/pull/11784)) — thanks @geekyNads +- **fix(assets):** Stop distributing 79 provider and CLI logos without verified source/license provenance, keep every provider/tool available through the internal generic icon, and prevent those IDs from falling through to an external icon CDN. ([#11750](https://github.com/diegosouzapw/OmniRoute/pull/11750)) +- **fix(providers):** retire the common `chatgpt-web` / `cgpt-web` provider because its pre-key and proof-of-work provenance could not be cleared ([#11754](https://github.com/diegosouzapw/OmniRoute/pull/11754)) — explicit chat and image requests, slash-prefixed models, persisted aliases, proxy overrides, and combo targets now fail closed with a sanitized HTTP `410` / `PROVIDER_RETIRED` before upstream dispatch. Migration 163 tombstones matching connections and invalidates their active leases without deleting history or widening API-key allowlists. The independent `chatgpt-web-codex` / `cgpt-codex` provider and its MIT-noticed vendored implementation remain available. +- **fix(usage):** quota and usage refresh no longer 409 when an exclusive lease reserves the connection ([#11758](https://github.com/diegosouzapw/OmniRoute/pull/11758)) — thanks @TheDemonTuan +- Keep the embedding registry's vector width and `embedding` type on models when a synced model exists for the same id, so `/v1/models` no longer reports registry-described embedding models widthless or untyped (#11761) — thanks @rahilmavani +- Correct `google/gemini-embedding-001` on the OpenRouter route to 3072 dimensions, the width it returns when `dimensions` is not sent (#11761) — thanks @rahilmavani +- **fix(vision-bridge):** Restrict automatic vision routing and fallbacks to models available in authoritative live provider catalogs, including aliases and effort variants ([#11767](https://github.com/diegosouzapw/OmniRoute/issues/11767)) — thanks @pacocartones ([#11813](https://github.com/diegosouzapw/OmniRoute/pull/11813)) +- **fix(providers):** reclassify Cerebras as a one-time $5 signup credit (payment method required, 30-day validity), not a recurring no-card 1M tokens/day trial ([#11773](https://github.com/diegosouzapw/OmniRoute/issues/11773)) ([#12591](https://github.com/diegosouzapw/OmniRoute/pull/12591)) — thanks @HouMinXi +- fix(cli): stop prepublish from re-rebuilding the already-built ESM-only opencode-plugin dist (#11787) ([#11990](https://github.com/diegosouzapw/OmniRoute/pull/11990)) — thanks @hartmark +- **fix(db):** Fresh installs no longer log a non-fatal `no such table: compression_run_telemetry` warning when retention cleanup runs before the lazily-created telemetry table exists ([#11802](https://github.com/diegosouzapw/OmniRoute/pull/11802)) — thanks @RaviTharuma +- fix(sse): set X-OmniRoute-Selected-Connection-Id on successful combo dispatches so downstream consumers stop falling back to an empty connection id (#11810) ([#11986](https://github.com/diegosouzapw/OmniRoute/pull/11986)) — thanks @hartmark +- **fix(services):** embedded CLIProxyAPI lifecycle checks now use public `/healthz`, while model discovery uses the configured dedicated data-plane API key instead of the management password ([#11811](https://github.com/diegosouzapw/OmniRoute/pull/11811)) — thanks @RaviTharuma +- Fix persisted model steps returning an error in the playground route simulator, and warn when structural steps cannot be simulated. ([#11862](https://github.com/diegosouzapw/OmniRoute/pull/11862)) — thanks @pacocartones +- **fix(providers):** Antigravity's dynamic mitmAlias table no longer routes `gemini-3.7-flash-{high,medium,low}` to a literal tier-suffixed upstream id just because one connected account's own discovery listed it directly — those display ids always resolve through the safe `gemini-3.7-flash-tiered` static alias, so one account's Google-provisioned access no longer 404s every sibling account of the provider ([#11824](https://github.com/diegosouzapw/OmniRoute/issues/11824), [#11651](https://github.com/diegosouzapw/OmniRoute/issues/11651)) ([#11988](https://github.com/diegosouzapw/OmniRoute/pull/11988)) — thanks @hartmark +- **fix(dashboard):** Expose Profile loading, errors, page structure, and XP progress to assistive technologies ([#11838](https://github.com/diegosouzapw/OmniRoute/pull/11838)) — thanks @pacocartones +- **fix(socks):** `Agent.connectTimeout` now bounds both SOCKS handshake and TLS connect, and `family === null` no longer falls back to `fetch-socks` ([#11842](https://github.com/diegosouzapw/OmniRoute/pull/11842)) — thanks @maxmad64bis +- **perf(compression):** OOM mitigations for large payload hashing, memoization, and token estimation ([#11844](https://github.com/diegosouzapw/OmniRoute/pull/11844) — thanks @AndrianBalanescu) +- Excluded Next.js Node File Trace manifests (`*.nft.json`) from the published npm tarball. They are build-time metadata and are never read while serving, but had grown to 668.7 MB — 61% of the package — which pushed the upload past the registry limit and made `npm publish` fail with `413 Payload Too Large`. ([#11864](https://github.com/diegosouzapw/OmniRoute/pull/11864)) +- **fix(cli):** keep the global OmniRoute server `--base-url` separate from provider-node `--endpoint` values in add, update, and validate commands ([#11860](https://github.com/diegosouzapw/OmniRoute/pull/11860)) — thanks @pacocartones +- **fix(config):** Nous Research's `Hermes-4-405B` model now displays as "Hermes 4 405B (Nous Research)" in both the provider registry and the free-model catalog, instead of the mislabelled "Hermes 4 7B" ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#11993](https://github.com/diegosouzapw/OmniRoute/pull/11993)) +- **fix(provider/nous):** inject required user= tag into Nous Research inference requests to resolve upstream 400 "missing tags" error ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#12044](https://github.com/diegosouzapw/OmniRoute/pull/12044)) +- **fix(build):** `npm run build` now fails in one second with a named package and a copy-pasteable fix when npm silently drops an externalised optional native dependency, instead of dying four minutes in with `Module not found: Can't resolve 'better-sqlite3'` ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- **fix(install):** `postinstall` no longer throws `ReferenceError: isAndroid is not defined` — failing the whole `npm install` — when the `better-sqlite3` rebuild fallback times out; the manual-fix guidance is reachable again ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- Fixed the Alibaba free-tier allowlist test that went red on its own once the shipped catalog's `validUntil` (2026-08-27) passed, leaving every PR and `main` with a failing `Unit Tests (1/8)`. The test now builds its own packs with dates it controls, and covers the expired-pack fallback that production has actually been serving. ([#11867](https://github.com/diegosouzapw/OmniRoute/pull/11867)) +- Split the npm registry upload into its own GitHub-hosted job. npm refuses `--provenance` from a self-hosted runner (`422 ... Only "github-hosted" runners are supported`), which blocked the v3.8.50 publish; the heavy verification cannot move to a hosted runner, so it now hands the proven tarball over instead. ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(ui):** the shared `DataTable` loading state no longer reads its decorative ⏳ glyph out to assistive technology, and now carries the same `role="status"` / `aria-live="polite"` / `aria-busy="true"` semantics as `PageLoading` ([#11869](https://github.com/diegosouzapw/OmniRoute/pull/11869)) — thanks @pacocartones +- **fix(api):** Bound the `timeout` accepted by `POST /api/providers/[id]/login` to the same 15s–600s window the Adobe Firefly and Conol browser-login paths already enforce, so a request can no longer pin a headful browser and the single active-login slot with an arbitrarily large poll budget. ([#11871](https://github.com/diegosouzapw/OmniRoute/pull/11871)) — thanks @pacocartones +- **fix(api):** Stop the tunnel and MITM routes from returning raw child-process error text, which disclosed host paths, binary install locations and Tailscale `tskey-*` credentials in a body some of these routes serve to non-loopback callers, and return a real 400 instead of a framework 500 when a tunnel request body fails validation. ([#11872](https://github.com/diegosouzapw/OmniRoute/pull/11872)) — thanks @pacocartones +- **fix(open-sse):** stream TTFT/ITL are sampled from a monotonic clock (`performance.now`) instead of `Date.now`, so an NTP correction or wall-clock jump can no longer inflate a genuine TTFT or produce a negative one that the `ttft >= 0` guard silently drops — protecting the router's OTel/EWMA/`usage_history`/speed-ranking signals ([#11873](https://github.com/diegosouzapw/OmniRoute/pull/11873)) — thanks @pacocartones +- **fix(providers):** xAI `reasoning_effort: "xhigh"` now reaches grok-4.6+ instead of being silently clamped to `"high"` ([#11879](https://github.com/diegosouzapw/OmniRoute/pull/11879)) — thanks @NoxzRCW +- **fix(dashboard):** the providers page no longer crashes into the error boundary when a provider id collides with an `Object.prototype` member (`constructor`, `__proto__`); icon lookups are own-property guarded ([#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)) — thanks @NoxzRCW +- **fix(skills):** injected skill tools declared in shorthand (`{"content": "string"}`) now forward valid JSON Schema, unblocking providers that validate tool schemas strictly such as Zhipu GLM on the Console Go tier ([#11881](https://github.com/diegosouzapw/OmniRoute/pull/11881)) — thanks @NoxzRCW +- **fix(api):** `POST /api/playground/simulate-route` now surfaces `combo-ref` and `provider-wildcard` persisted combo steps with a specific warning (naming the referenced combo, or the unresolved `provider/model` wildcard) instead of folding them into a generic "unsupported step" count; a `provider-wildcard` step is also now included as an unresolved target so the operator can see it is in the route (ported from [#11882](https://github.com/diegosouzapw/OmniRoute/pull/11882) — thanks @NoxzRCW). ([#11930](https://github.com/diegosouzapw/OmniRoute/pull/11930)) +- **fix(translator):** the streaming OpenAI→Claude translator keeps upstream usage, including prompt-cache tokens, when it arrives on a trailing `choices: []` chunk (Fireworks and any upstream using `stream_options.include_usage`) ([#11883](https://github.com/diegosouzapw/OmniRoute/pull/11883)) — thanks @NoxzRCW +- **fix(cli):** `omniroute update --apply` now tells you explicitly whether a running server was detected and, if so, that you must run `omniroute restart` to apply the update — it never restarted anything and previously implied the update was already live once files were installed. The dashboard's npm-mode Update flow (`/api/system/version`) now tries OmniRoute's own PID-file-managed supervisor before falling back to pm2, and reports an honest "restart required" step instead of a silent pm2-only "skipped" that read like a completed update. The server-side latest-version lookup backing the dashboard's update banner also gained `--prefer-online`, closing the same stale-npm-cache class already fixed in the CLI's own copy for #4376 ([#11885](https://github.com/diegosouzapw/OmniRoute/issues/11885)). ([#12005](https://github.com/diegosouzapw/OmniRoute/pull/12005)) +- **fix(resilience):** clear persisted LKGP pins when a target suffers connection/provider exhaustion or is skipped before dispatch due to cooldown/exhaustion/unavailability, preventing subsequent requests from repeatedly prioritizing known-dead providers ([#11911](https://github.com/diegosouzapw/OmniRoute/issues/11911)). ([#12013](https://github.com/diegosouzapw/OmniRoute/pull/12013)) — thanks @HouMinXi +- **fix(sse):** the OpenAI→Claude stream translator now defers the terminal `message_delta`/`message_stop` emission until the real usage block has arrived (or a genuine end-of-stream flush forces it) instead of emitting it immediately on `finish_reason` — previously, when the trailing usage-only chunk (`{"choices":[],"usage":{...}}`) arrived *after* the `finish_reason` chunk (the normal order for Fireworks/vLLM/Together and other `stream_options.include_usage` upstreams), the client-visible `message_delta` still carried stale/zero usage even though `state.usage` was internally corrected too late to matter (ported from [#11915](https://github.com/diegosouzapw/OmniRoute/pull/11915) — thanks @HouMinXi). ([#11933](https://github.com/diegosouzapw/OmniRoute/pull/11933)) +- **fix(sse):** `stripStore()` now forces `store=false` for stateless OpenAI-compatible Responses-API targets (unless the connection has explicitly opted in via `providerSpecificData.openaiStoreEnabled`), instead of only handling the `openai`/`agentrouter` cases — a client-supplied `store` value previously passed through untouched to backends that don't actually persist responses server-side ([#11916](https://github.com/diegosouzapw/OmniRoute/pull/11916) — thanks @HouMinXi). +- **fix(models):** custom provider-node models (synced, custom, and alias-backed) now appear under their configured prefix in the unified catalog when the operator's model-id prefix mode is canonical, instead of being dropped whenever alias-inclusion was otherwise disabled ([#11918](https://github.com/diegosouzapw/OmniRoute/pull/11918) — thanks @HouMinXi). +- **fix(models):** the unified model catalog now suppresses stale static registry models (including effort-tier variants) for any provider whose active connection has an authoritative live synced catalog, not only providers already using exclusive-synced-listing — a connection with `providerUsesAuthoritativeLiveCatalog` previously kept serving both the live-synced models and the stale static rows side by side ([#11919](https://github.com/diegosouzapw/OmniRoute/pull/11919) — thanks @HouMinXi). +- **fix(ui):** `ProviderIcon`'s three remaining unguarded lookups (`PROVIDER_ICON_ALIASES`, `LOCAL_SVG_ALIASES`, `THEMED_SVGS`) now use `Object.hasOwn()` own-property checks — a provider id such as `constructor` or `__proto__` previously resolved through the prototype chain instead of falling through to the unknown-provider CDN fallback (`getLobeProviderIcon()` itself was already guarded by [#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)); `ProviderPageHeader`'s `color` field is also now optional, matching the rest of the component's defensive typing (ported from [#11920](https://github.com/diegosouzapw/OmniRoute/pull/11920) — thanks @HouMinXi). ([#11935](https://github.com/diegosouzapw/OmniRoute/pull/11935)) — thanks @NoxzRCW +- fix(ollama): preserve multi-byte UTF-8 content split across stream chunks in the Ollama NDJSON transform, which previously corrupted CJK/emoji into U+FFFD (#11921) — thanks @pacocartones +- **fix(providers):** OrcaRouter chat requests now target `/v1/chat/completions` instead of the bare `/v1` API root, fixing the upstream `404 Invalid URL (POST /v1)` ([#11923](https://github.com/diegosouzapw/OmniRoute/pull/11923)). — thanks @echel0nn +- **fix(plugins):** deliver the `onStreamComplete` event to disk-installed plugins. The event shipped in v3.8.50 (#9669) was emitted internally but had no plugin-facing wiring, so no plugin could ever subscribe: the manifest schema silently dropped `hooks.onStreamComplete`, and the loader/manager only knew the seven legacy hooks. `onStreamComplete` is now a declarable manifest hook, wired through the loader and registered by the manager like the other hooks, and its payload carries a `requestId` so consumers can correlate the stream-completion event with the originating request ([#11934](https://github.com/diegosouzapw/OmniRoute/pull/11934)) — thanks @amaleta / @pacocartones +- **fix(db):** the Qdrant embedding-model dropdown now lists local/self-hosted providers (Ollama, LM Studio, vLLM, etc.) — an active connection is treated as "configured" when the provider allows an optional API key, not only when it has a real key or OAuth, so a running local embedding provider is no longer hidden from the picker ([#11949](https://github.com/diegosouzapw/OmniRoute/issues/11949)) ([#11995](https://github.com/diegosouzapw/OmniRoute/pull/11995)) +- **fix(providers):** Vertex AI Anthropic partner-model discovery now calls the Model Garden `v1beta1` publisher list (`/v1beta1/publishers/anthropic/models`, global) and parses its `publisherModels` envelope, so Claude models auto-synced from Vertex populate the active live catalog and route at request time instead of returning `Model '' is not available in the active live catalog` ([#11991](https://github.com/diegosouzapw/OmniRoute/issues/11991)) — thanks @fabioluissilva ([#11998](https://github.com/diegosouzapw/OmniRoute/pull/11998)) +- **fix(cli):** support `--base-url` alongside `--endpoint` in `omniroute nodes add`, `update`, and `validate` subcommands to prevent global `--base-url` shadowing issues ([#11999](https://github.com/diegosouzapw/OmniRoute/issues/11999)). ([#12033](https://github.com/diegosouzapw/OmniRoute/pull/12033)) — thanks @HouMinXi +- **fix(providers):** `cloudflare-ai` no longer refuses image content parts for every Workers AI model ([#12002](https://github.com/diegosouzapw/OmniRoute/pull/12002)) — the plain-string `content` requirement behind #2539 is carried by the _model_ schema, not by the `/ai/v1/chat/completions` endpoint (measured: an all-text part array returns 200 on `@cf/mistralai/mistral-small-3.1-24b-instruct`, `@cf/meta/llama-4-scout-17b-16e-instruct` and `@cf/meta/llama-3.3-70b-instruct-fp8-fast`, and 400 on the text-only `@cf/qwen/qwen2.5-coder-32b-instruct`). `transformRequest()` flattened every array and threw on the first non-text part (#6390), so image input was refused for vision-capable Cloudflare models that accept it. All-text arrays are still flattened — the one shape every model accepts — while an array carrying a non-text part is passed through untouched, so the attachment is still never silently dropped. Regression guards: `tests/unit/cloudflare-ai-image-parts-6390.test.ts`. — thanks @davidlinfr +- **fix(resilience):** decouple the limiter-managed execution backstop from the queue-wait budget — new `requestQueue.executionMaxWaitMs` (env `RATE_LIMIT_EXECUTION_MAX_WAIT_MS`, default 600000 = 10 min) now feeds Bottleneck's post-dispatch `expiration`, while `requestQueue.maxWaitMs` keeps its documented queue-wait semantics. Previously the queue-wait budget doubled as the execution expiration, so legitimate long-running calls on non-incremental gateways (whole generation buffered before the first upstream byte, e.g. Console Go / Command Code tiers serving GLM models) were killed mid-flight at the queue budget with a false 504 `RATE_LIMIT_EXECUTION_TIMEOUT` — the local limiter undercut the provider-aware upstream fetch-start timeouts. The surfaced 504 message now names `requestQueue.executionMaxWaitMs`; the error keeps the #4165 guarantees (disclaims an upstream timeout, preserves the Bottleneck error as `cause`, branded code + trusted provenance, classified request-scoped so combo falls back). A real queue-wait bound (the `Promise.race` around `limiter.schedule()` sketched in #9533) remains future work. (#12025) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **Call logs:** keep the `error` field when an artifact exceeds the storage cap, instead of replacing it with the omission marker. The error is the only field that says *why* a request failed and is typically ~90 bytes next to the multi-hundred-KB bodies that trip the cap, so dropping it left a size-limited row undiagnosable — a provider outage, a local timeout and an upstream 400 all rendered identically. It is now preserved at every fallback stage, truncated to 4KB if it is itself large ([#12026](https://github.com/diegosouzapw/OmniRoute/issues/12026)). ([#12095](https://github.com/diegosouzapw/OmniRoute/pull/12095)) — thanks @ntdat812 +- **fix(diagnostics):** preserve the error field (truncated to 4KB with a `[truncated: …]` suffix) in every call-log artifact size-limit fallback stage. Previously the minimal fallback replaced the error with `[omitted: call log artifact size limit exceeded]`, so an oversized artifact row showed nothing about WHY the request failed — e.g. 91 of 847 opencode-go 504 rows on one production instance were undiagnosable from the dashboard. Oversized request/response bodies are still omitted exactly as before; the error cap is independent of the payload sizes that tripped the fallback. (#12026) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **fix(sse):** OpenAI Responses clients that declare the native `web_search` tool now receive a spec-shaped `web_search_call` output item with `action.sources` alongside the preserved function-call round-trip, so search results executed through OmniRoute's own search backend are consumable by standard Responses clients (Codex, pi-web-access, …). ([#12031](https://github.com/diegosouzapw/OmniRoute/pull/12031)) — thanks @watchingdogs +- **fix(cli):** use in-thread alias resolver hooks on modern runtimes to avoid deprecation noise and improve Node.js forward compatibility ([#12073](https://github.com/diegosouzapw/OmniRoute/issues/12073)). ([#12083](https://github.com/diegosouzapw/OmniRoute/pull/12083)) — thanks @xiaoyaner0201 +- **fix(combo):** an operator-set **Agent Features → Context length** on a combo is now honored at request time. The value was persisted and advertised through `/v1/models`, but `resolveComboContextLimit()` never consulted it — so a multi-target combo whose members carry no per-model window fell through to the provider's generic `defaultContextLength` (openrouter 128000, command-code 200000) and rejected large requests with `Input exceeds context window … limit 128000` despite the combo being explicitly sized much larger. An identical single-target combo worked, because it collapses to its concrete target before the guard runs. Invalid values (0/negative/NaN/Infinity) are ignored, so the existing target → combo-min → fallback order is unchanged. ([#12090](https://github.com/diegosouzapw/OmniRoute/pull/12090)) — thanks @adivekar-utexas +- **fix(sse):** passthrough streams now estimate usage on finish when upstream closes without usage even with `stream_options.include_usage` — avoids `0 tokens / 0%` for providers that stay silent (and correctly handles trailing empty-choices usage) ([#12151](https://github.com/diegosouzapw/OmniRoute/pull/12151)) — thanks @maxmad64bis +- **fix(combos):** clearing an agent feature in the combos editor now persists — unchecking context cache protection, or emptying the system message or tool filter, sends an explicit `null` instead of dropping the field from the `PUT` body, which the update merge read as "leave unchanged" ([#12177](https://github.com/diegosouzapw/OmniRoute/pull/12177)) — thanks @foreveryh / @ntdatt812 +- **fix(memory):** self-hosted embedding endpoints now vectorize — the vector width is measured from the first embedding that comes back instead of being read from a registry that cannot describe them, so `vec_memories` is created and memories stop piling up unvectorized behind a green health check ([#12180](https://github.com/diegosouzapw/OmniRoute/pull/12180)) — thanks @kanade-hoshino / @ntdatt812 +- **fix(translator):** the leading `system` message now reaches Responses-API upstreams when its `content` is a content-part array — it was read as `typeof content === "string" ? content : ""`, so a prompt-caching client (Anthropic `cache_control`, the shape LiteLLM and the Anthropic SDK emit) had its entire system prompt replaced by an empty `instructions`. The request was still accepted with a normal `prompt_tokens` count, so the model answered with no instructions and nothing in the response said they were missing. Mid-conversation system turns already handled the array shape ([#7056](https://github.com/diegosouzapw/OmniRoute/pull/7056)); only the first one did not ([#12206](https://github.com/diegosouzapw/OmniRoute/issues/12206)). Regression guard: `tests/unit/translator-openai-responses-system-content-parts.test.ts`. ([#12207](https://github.com/diegosouzapw/OmniRoute/pull/12207)) — thanks @Gorillaz322 +- **fix(free-tier):** `/api/free-tier/summary` no longer computes its totals from a Radar feed built before the catalog the running release ships. When the cached feed is older — or carries no build date at all — the route answers from the shipped catalog, resolved through the operator's local model state so disabled and tombstoned models stay out of the numbers ([#12215](https://github.com/diegosouzapw/OmniRoute/pull/12215)). — thanks @maxmad64bis +- **fix(oauth):** Keep a Claude personal workspace and a Team organization as separate connections — they share the same email and `accountUUID`, so the email-only OAuth dedup let the second login overwrite the first account's tokens; `organizationUUID` now disambiguates them, the way `workspaceId` does for Codex ([#12222](https://github.com/diegosouzapw/OmniRoute/pull/12222)) — thanks @davidebaraldo +- **fix(resilience):** a 402 on a single paid model of a passthrough/gateway provider (e.g. `kilo-gateway`, `ollama-cloud`) no longer terminalizes the whole connection with a never-auto-recovered `credits_exhausted` status — only the paid model is locked out, so free models on the same key keep serving. 402 variant of [#3027](https://github.com/diegosouzapw/OmniRoute/issues/3027). Single-credential providers are unaffected — a 402 there is still treated as the key being genuinely out of credit ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239) / [#10616](https://github.com/diegosouzapw/OmniRoute/issues/10616)) ([#12242](https://github.com/diegosouzapw/OmniRoute/issues/12242)) — thanks @brick30llc-ctrl ([#12266](https://github.com/diegosouzapw/OmniRoute/pull/12266)) — thanks @geekyNads +- **fix(sse):** trust `finish_reason: "length"`/`"max_tokens"` over the reasoning-consumed-token ratio in response quality validation, so a reasoning model truncated below the old 90% threshold correctly fails and retries instead of returning empty content as a silent "success" ([#12262](https://github.com/diegosouzapw/OmniRoute/pull/12262)) — thanks @brick30llc-ctrl +- **fix(settings):** `PUT /api/settings/cache-config` now persists `alwaysPreserveClientCache` to the flat general settings the runtime cache-control policy actually reads; previously the value landed in the databaseSettings "cache" section and was silently ignored, so the endpoint had no effect on `cache_control` passthrough ([#12304](https://github.com/diegosouzapw/OmniRoute/pull/12304)) — thanks @davidebaraldo +- **fix(grok-cli):** treat omitted SuperGrokPro `creditUsagePercent` as 0% used so Provider Limits still renders a weekly bar (proto3 zero-elision) ([#12312](https://github.com/diegosouzapw/OmniRoute/pull/12312)) — thanks @HouMinXi +- **fix(combo):** Expose the two Auto-Combo scoring factors nobody could set — the combo validation schema and the dashboard weight sliders both declared 13 of the scorer's 15 factors, so `connectionDensity` (spreads load across a provider's connections) and `quality` were dropped on save and offered nowhere. The sliders also shipped their own default table that differed from the engine's on every non-zero factor and summed to 1.05, so the percentages shown next to them added up to 105%. Both lists now match `DEFAULT_WEIGHTS`, and a test keeps them there. Note that a combo whose stored `weights` omitted the two keys was effectively running with them at zero and the other thirteen renormalized upward; it now runs with the engine's intended distribution, so its routing does shift ([#12314](https://github.com/diegosouzapw/OmniRoute/pull/12314)) — thanks @maxmad64bis +- **fix(docs):** The free-tier reference no longer says its numbers come "confidence tagged per row" — no catalog entry carries a confidence tag and the API serves none, so every figure on that page is an estimate of the same, unstated quality. The page now states what an entry does vouch for: an independently documented hard stop (set by hand with the source in a comment, never defaulted to `true`) and a prompt-training disclosure, both with live counts the `check:docs-counts` gate keeps honest ([#12318](https://github.com/diegosouzapw/OmniRoute/pull/12318)) — thanks @maxmad64bis +- **fix(usage):** `adobe-firefly` and `firefly` have had a working usage fetcher since Adobe Firefly landed, but neither was ever added to the registration list, so the provider-plugin manifest, `genericQuotaFetcher` and the free-access quota cache all reported them as having no usage support — while `USAGE_SUPPORTED_PROVIDERS` said the opposite. Both are now declared, which also means their credit balance is fetched like any other declared provider's: `registerGenericQuotaFetchers` now registers a generic quota fetcher for them, and `resolveFreeAccessState` no longer returns early. A test holds the registration list to the dispatcher's switch in both directions, which is what the module's own docstring already asked for in prose ([#12321](https://github.com/diegosouzapw/OmniRoute/pull/12321)) — thanks @maxmad64bis +- **fix(quota):** drop the generic quota cache (agy / Antigravity / Claude OAuth) on an upstream 429 so reset-aware scoring does not keep a 60s stale snapshot, and force-refresh the next usage fetch so inner provider caches cannot recache the same window ([#12325](https://github.com/diegosouzapw/OmniRoute/pull/12325)) — thanks @HouMinXi +- **fix(combos):** deleting a combo now clears its persisted LKGP pins instead of leaving unreachable `key_value` rows behind ([#12326](https://github.com/diegosouzapw/OmniRoute/issues/12326)) ([#12425](https://github.com/diegosouzapw/OmniRoute/pull/12425)) — thanks @KrzysiekSko +- **fix(sse):** Keep ZWNJ (U+200C) and ZWJ (U+200D) in assistant text, reasoning and tool-call arguments — Persian/Kurdish half-space (`ارائه‌دهنده`), Arabic/Indic shaping and emoji sequences no longer lose them; the response de-obfuscation now removes joiners only between ASCII word characters, where the request side inserts them ([#12186](https://github.com/diegosouzapw/OmniRoute/issues/12186)) — thanks @rezjalibd ([#12359](https://github.com/diegosouzapw/OmniRoute/pull/12359)) — thanks @pacocartones +- **fix(resilience):** count resolved upstream 5xx results against the provider circuit breaker on the chat path — `CircuitBreaker.execute()` no longer reads a resolved `{ success: false, status: 5xx }` as a success that cancels the call-site failure, so a provider answering 503s now trips its breaker instead of staying `CLOSED` at `failureCount: 1`; single-model and combo dispatches are each accounted exactly once ([#12254](https://github.com/diegosouzapw/OmniRoute/issues/12254)) ([#12360](https://github.com/diegosouzapw/OmniRoute/pull/12360)) — thanks @pacocartones +- **fix(providers):** resolve the Codex quota auto-ping model from the live provider catalog and lifecycle registry instead of the retired `gpt-5.1-codex-mini`, and pause the ping with one actionable warning when no selectable Codex model exists rather than retrying a shut-down id every cooldown window ([#11905](https://github.com/diegosouzapw/OmniRoute/issues/11905)) ([#12361](https://github.com/diegosouzapw/OmniRoute/pull/12361)) — thanks @pacocartones +- **fix(api):** keep the `{created, data}` wrapper on combo-routed `/v1/images/generations` responses and default Codex image results to `b64_json` on both `/v1/images/generations` and `/v1/images/edits` so Codex CLI's built-in `image_gen` can decode them ([#12268](https://github.com/diegosouzapw/OmniRoute/issues/12268)) ([#12362](https://github.com/diegosouzapw/OmniRoute/pull/12362)) — thanks @pacocartones +- **fix(sse):** Name the shadowed custom provider node when a built-in provider id/alias (e.g. `openference` → `of`) reserves the prefix of an existing OpenAI/Anthropic-compatible node, so the runtime `No active credentials for provider: ` error explains that the prefix routed to the built-in and never reached the node's healthy connections, instead of contradicting the dashboard ([#11943](https://github.com/diegosouzapw/OmniRoute/issues/11943)) — thanks @morpheus9393 ([#12365](https://github.com/diegosouzapw/OmniRoute/pull/12365)) — thanks @pacocartones +- **fix(i18n):** wrap `ccOnboardingKeyPlaceholder` in ICU single quotes across all 43 locales so angle brackets render literally instead of being parsed as rich-text tags, which crashed the Claude Code onboarding block with `INVALID_MESSAGE: INVALID_TAG` ([#12302](https://github.com/diegosouzapw/OmniRoute/issues/12302)) ([#12369](https://github.com/diegosouzapw/OmniRoute/pull/12369)) — thanks @gonisulaimann +- **fix(models):** publish `effort_tiers` on Kimi K3's synced base-model entries (`kmca/k3`, `kmca/k3-256k`) so catalog-only clients (OpenCode, plain SDK pickers) can see and select the reasoning tiers (`low`/`high`/`max`) the synced metadata already carried — the `isSkippedEffortProvider` gate no longer suppresses tier visibility on those base entries, while synthetic `-` variant generation stays prevented and Codex/GLM base models remain excluded unchanged ([#12299](https://github.com/diegosouzapw/OmniRoute/issues/12299)) ([#12371](https://github.com/diegosouzapw/OmniRoute/pull/12371)) — thanks @gonisulaimann +- **fix(guardrails):** keep `auto`/`auto/*` virtual combos exempt from the Vision Bridge `fixedModel` credential guard so a combo target is passed through instead of silently falling back to global auto-selection ([#12237](https://github.com/diegosouzapw/OmniRoute/issues/12237)) ([#12373](https://github.com/diegosouzapw/OmniRoute/pull/12373)) — thanks @pacocartones +- **fix(combo):** capability-filter exhaustion caused by `max_tokens` above every target's known output limit now reports that reason (requested `max_tokens` vs the pool's highest known ceiling) instead of the unrelated "supports structured output" message ([#12229](https://github.com/diegosouzapw/OmniRoute/issues/12229)) — thanks @DW-MediaLab ([#12374](https://github.com/diegosouzapw/OmniRoute/pull/12374)) — thanks @pacocartones +- **fix(auth):** the `least-used` account strategy now prefers accounts without backoff before falling back to oldest `lastUsedAt`, the same tie-break `round-robin` already applies, so a failover no longer lands on a just-rate-limited account for a single request ([#12279](https://github.com/diegosouzapw/OmniRoute/issues/12279)) — thanks @tenshiak ([#12375](https://github.com/diegosouzapw/OmniRoute/pull/12375)) — thanks @pacocartones +- **fix(docker):** the `chatgpt-web-codex-browser` image now finds the Chrome binary under `chrome-linux64/` (Chrome for Testing layout in `playwright:v1.62.0-noble`) as well as the legacy `chrome-linux/`, so the container no longer crash-loops with `exec: --headless=new: not found` ([#12024](https://github.com/diegosouzapw/OmniRoute/issues/12024)) ([#12376](https://github.com/diegosouzapw/OmniRoute/pull/12376)) — thanks @pacocartones +- **fix(providers):** declare `groq/compound` and `allam-2-7b` as non-reasoning models in the curated Groq registry so `reasoning_effort` / `output_config.effort` / `thinking` from Claude Code are stripped instead of forwarded, which Groq rejected with HTTP 400 ([#12134](https://github.com/diegosouzapw/OmniRoute/issues/12134)) ([#12379](https://github.com/diegosouzapw/OmniRoute/pull/12379)) — thanks @pacocartones +- **fix(executors):** `OpencodeExecutor` no longer forces a direct connection when the connection has a proxy assigned in Proxy Management but no per-account proxies: the single-account fast path used to wrap the upstream dispatch in the direct-egress sentinel, discarding the ambient proxy context the chat handler had pinned from `proxy_assignments`, so API-key `opencode`/`opencode-go` connections egressed from the host IP (and hit geoblocks) despite the assignment. The direct pin is now applied only when no ambient proxy context exists ([#11894](https://github.com/diegosouzapw/OmniRoute/issues/11894) — thanks @hizzt) ([#12380](https://github.com/diegosouzapw/OmniRoute/pull/12380)) — thanks @pacocartones +- **fix(api):** `GET /v1/models` with `MODELS_CATALOG_PREFIX_MODE=canonical` (or `?prefix=canonical`) now lists providers whose registry alias is undefined or equal to their own id (Antigravity, Antigravity CLI and other self-aliased built-ins) — their single `provider/model` id was dropped by the alias/canonical duplicate guard in the static, synced, custom and alias-backed catalog loops ([#12058](https://github.com/diegosouzapw/OmniRoute/issues/12058)) — thanks @cheynetom ([#12381](https://github.com/diegosouzapw/OmniRoute/pull/12381)) — thanks @pacocartones +- **fix(translator):** Drop replayed `thinking` blocks that carry no signature (the shape produced from cross-provider `reasoning_content`) instead of stamping the default Claude signature on them, which Anthropic rejected with `400 Invalid signature in thinking block` on the next turn served by an Anthropic rung ([#12105](https://github.com/diegosouzapw/OmniRoute/issues/12105)) — thanks @atescivitci-cmd ([#12386](https://github.com/diegosouzapw/OmniRoute/pull/12386)) — thanks @pacocartones +- **fix(cli):** Resolve Bun's `--preload` polyfill path against the package root instead of `dist/`, so `omniroute` installed with `bun install -g` no longer crashes at startup with `error: preload not found …/dist/open-sse/utils/setupPolyfill.ts` ([#11980](https://github.com/diegosouzapw/OmniRoute/issues/11980)) — thanks @joglomedia ([#12387](https://github.com/diegosouzapw/OmniRoute/pull/12387)) — thanks @pacocartones +- **fix(providers):** `gemini-business` now publishes its model catalog — `/v1/models` and `/v1/providers/gemini-business/models` list the 12 enterprise Gemini ids the executor understands instead of returning an empty list (#12107) ([#12389](https://github.com/diegosouzapw/OmniRoute/pull/12389)) — thanks @pacocartones +- **fix(db):** install `busy_timeout` before the SQLite connection's first statement so a process opening the database while another one closes its WAL connection waits out the transient EXCLUSIVE lock instead of dying with `database is locked`, and recognise the drivers' real BUSY/PROTOCOL/IOERR errors as transient in the corruption probe so the same lock no longer renames the database away as corrupt; deflakes `cross-process contenders never both acquire the same connection` (#12394 — thanks @pacocartones) +- **fix(chat-admission):** derive the `chat_admission_busy` 503 `Retry-After` from observed heavyweight-lease occupancy — the larger of the exhausted `OMNIROUTE_CHAT_ADMISSION_QUEUE_MS` window and the time since capacity last turned over, capped at 60 s — instead of a fixed 1 s (structural) / 2 s (byte-stage) hint that invited Codex/agent fan-out clients to re-send ~1 MiB `/v1/responses` bodies every second into a gate held for the whole SSE lifetime; an idle gate keeps the historical floors ([#12135](https://github.com/diegosouzapw/OmniRoute/issues/12135)) (#12395 — thanks @pacocartones) +- **fix(api-manager):** the API key permissions modal no longer silently drops `allowedCombos` entries its Combo picker cannot render — routing-rule names such as `rt-*`, which the backend already honours — when "All" is clicked and the key is switched back to "Restrict"; those entries now survive the toggle, are listed read-only under the combo list so the count and the list agree, and are saved back verbatim instead of persisting `[]` (deny-all) (#12397 — thanks @pacocartones) +- **fix(catalog):** write the NUL separator of the catalog connection memo key, the provider serviceKind memo key, the Video Bridge promotion group key and a JSON-exactness test fixture as the `\u0000` escape instead of a raw byte — same runtime value, but the raw byte made git, GitHub and ripgrep treat those files as binary (hidden PR diffs, silently skipped searches); a guard test now keeps raw NUL bytes out of `src/`, `open-sse/` and `tests/` (#12403 — thanks @pacocartones) +- **fix(providers):** add `CLAUDE_CODE_CLIENT_VERSION` and `GITHUB_COPILOT_CLI_VERSION` env overrides so Anthropic/Copilot client-version gates can be unblocked without a rebuild ([#12417](https://github.com/diegosouzapw/OmniRoute/issues/12417)) ([#12632](https://github.com/diegosouzapw/OmniRoute/pull/12632)) — thanks @HouMinXi +- **fix(db):** back-fill `last_ping_at` and `last_pinged_reset_key` on `provider_connections` during schema reconciliation so divergent lineages that skipped `123_quota_auto_ping` still accept quota auto-ping writes ([#12470](https://github.com/diegosouzapw/OmniRoute/pull/12470) — thanks @KooshaPari) +- **fix(ci):** document MIT exceptions for `@eloqnt/{config,format-json,format-po}` (next-intl transitive; locked tarballs omit `license`) and keep the A2A lifecycle vitest off the real SQLite persistence seam ([#12581](https://github.com/diegosouzapw/OmniRoute/issues/12581)) ([#12595](https://github.com/diegosouzapw/OmniRoute/pull/12595)) +- OpenCode plugin `/v1/models` catalog fetch now waits 30s by default and attaches HTTP `statusCode` on 401/5xx so host fallback plugins can hop instead of seeing an untyped AbortError/UnknownError. ([#12607](https://github.com/diegosouzapw/OmniRoute/pull/12607)) — thanks @RaviTharuma +- **CI:** the OpenAPI security-tier gate now mirrors `isAlwaysProtectedPath()` in full — it also reads `ALWAYS_PROTECTED_API_PATTERNS`, so the pattern-gated credential routes (`/api/providers/{id}/{claude,codex}-auth/{export,apply-local}`, GHSA-5926-2w35-7h4q) no longer report as unannotated. (#12605) +- **fix(api):** GET /v1/models no longer waits forever on a hung coalesced catalog rebuild; cold-path waits are bounded (`CATALOG_BUILD_TIMEOUT_MS`, default 8s) and a last-good 200 is served when the rebuild times out ([#12627](https://github.com/diegosouzapw/OmniRoute/issues/12627)). ([#12628](https://github.com/diegosouzapw/OmniRoute/pull/12628)) — thanks @RaviTharuma +- **fix(cursor):** a non-array `tool_calls` on an assistant message no longer crashes the cursor request translator with a `TypeError`; both loops now require an array ([#12689](https://github.com/diegosouzapw/OmniRoute/issues/12689)) ([#12691](https://github.com/diegosouzapw/OmniRoute/pull/12691)) — thanks @soroush5 +- **fix(chat):** Chat Completions no longer return empty `content` after a server-owned memory or skills tool; the first provider send and account/model recovery now share one pipeline so a follow-up round-trip can fill the reply ([#12696](https://github.com/diegosouzapw/OmniRoute/issues/12696)) — thanks @HouMinXi ([#12867](https://github.com/diegosouzapw/OmniRoute/pull/12867)) +- **fix(dashboard):** batch-deleting provider keys no longer toasts failure after a successful delete when the confirm button's click event is forwarded as `onAfter` ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) — thanks @HouMinXi +- **fix(glm):** drop the extra 16th argument to `createSSETransformStreamWithLogger` that TypeScript rejected (TS2554) and that never reached the TransformStream ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) ([#12770](https://github.com/diegosouzapw/OmniRoute/pull/12770)) — thanks @HouMinXi +- **fix(dashboard):** pass `nodeMap` into Runtime `QuotaGroup` so a quota monitor in error/exhausted/alerting no longer throws `ReferenceError: nodeMap is not defined`. ([#12868](https://github.com/diegosouzapw/OmniRoute/pull/12868)) — thanks @HouMinXi +- Restricted API keys whose `allowedModels` lists a combo name no longer skip every combo member at pre-dispatch (`ALL_TARGETS_SKIPPED`). Inner-target filtering still applies when the allow-list is a provider prefix or `disableNonPublicModels` is on ([#12899](https://github.com/diegosouzapw/OmniRoute/pull/12899)). — thanks @HouMinXi +- **fix(combos):** Effort-suffixed combo members inherit the base model's `model_context_overrides` row so priority order is not inverted on large requests ([#12926](https://github.com/diegosouzapw/OmniRoute/pull/12926)) — thanks @HouMinXi +- **fix(catalog):** Picker-added `customModels` enter the dispatch-time live catalog so combo members and bare inference no longer 400 ([#12934](https://github.com/diegosouzapw/OmniRoute/pull/12934)) — thanks @HouMinXi +- **fix(docker):** re-derive the Docker build's worker-pool memory budget from the MEASURED ~4.5 GB per-process RSS (the issue owner's own VPS dmesg OOM-killer reproduction) instead of the stale 2560 MB/worker inference #11419 shipped, and lower `OMNIROUTE_BUILD_WORKERS` 3 → 2 so 1 parent + 1 page-data worker (2 processes × 4.5 GB = 9 GB) fits the 12.288 GB (75%) budget on a 16 GB GitHub Actions runner — the previous default (1 parent + 2 workers = 13.5 GB) still overcommitted the runner and kept "Publish to Docker Hub" failing with `cannot allocate memory` after #11419 merged (#7518). ([#11663](https://github.com/diegosouzapw/OmniRoute/pull/11663)) +- **fix(electron):** the packaged Windows desktop build now passes the #7592 cold-restart smoke end to end. Five defects were found and fixed on the way: (1) optional-pack staging failed on any machine whose `tar` is GNU tar — it reads the drive letter in an absolute `-f C:\...` archive path as a remote rsh host (`Cannot connect to C:`), so staging now passes a bare filename with `cwd` at the tarball directory and surfaces tar stderr on failure; (2) the Electron `files` allowlist was missing `lib/loginHeaderCapture.js`, so the packaged main process crashed at startup with `Cannot find module './lib/loginHeaderCapture'`; (3) electron-builder ≥26 injects an `!**/node_modules/**` ignore into every extraResources pattern list that no positive filter can override, which silently dropped the staged runtime `node_modules` (including the better-sqlite3 N-API prebuild) from `resources/app` — a fresh v3.8.50 desktop build would have shipped with no native SQLite driver and reproduced the #7592 sql.js fallback on every machine; an `afterPack` hook now restores the staged `node_modules` after packing; (4) the packaged-app smoke harness redirected `USERPROFILE` into its temp DATA_DIR, but Electron resolves the Roaming profile from `%USERPROFILE%\AppData\Roaming\` (USERPROFILE takes precedence over the APPDATA env var) and the path service throws instead of creating it, so `requestSingleInstanceLock()` returned false and the app exited(0) silently before `app.whenReady()` — the harness now pre-creates the derived tree, and `ensureSmokeEnvDirs` is exported and unit-tested; (5) the #7592 driver assertion parsed a `[DB] Driver: ...` line that the server's primary DB path never printed (only the unused `openDatabaseAsync()` did) — `getDbInstance()` now emits the same line on open so the guard can actually assert the native driver. Also: the smoke env-allowlist unit test hardcoded POSIX paths and could only pass on Linux/macOS; expectations are now host-agnostic, with new regression tests for the USERPROFILE-derived tree and `tarPack` under absolute Windows-style paths ([#11443](https://github.com/diegosouzapw/OmniRoute/pull/11443)) — thanks @jonlwheat2-gif +- fix(sse): stop the auto-combo candidates inspector from silently dropping model-locked/cooled-down rows (#9133) ([#11994](https://github.com/diegosouzapw/OmniRoute/pull/11994)) — thanks @hartmark +- **fix(providers):** Perplexity Web no longer turns upstream stream failures into successful assistant text; pre-content failures remain eligible for fallback, partial output ends with a structured sanitized error, and failed sessions are not persisted ([#12465](https://github.com/diegosouzapw/OmniRoute/pull/12465)) +- **fix(video-bridge):** preserve chronological video and transcript fusion, source-frame timestamps, transcript provenance, full contact-sheet intervals, and pre-abort callback isolation ([#11681](https://github.com/diegosouzapw/OmniRoute/pull/11681)) +- **Z.ai Web:** HTTP 200 streams carrying an upstream error now terminate with a structured failure instead of assistant text plus a normal stop, preserving partial output while allowing pre-content combo fallback. ([#12454](https://github.com/diegosouzapw/OmniRoute/pull/12454)) +- Absorb `Error [AbortError]: request_signal_aborted` and DOMException AbortError shapes in the process-level client-abort crash guard so routine client disconnects no longer kill the server (exit code 7). ([#12165](https://github.com/diegosouzapw/OmniRoute/pull/12165)) — thanks @alvinveroy +- **fix(sse):** Treat Adapta Web `type:error` SSE events as sanitized non-stream failures instead of empty HTTP 200 completions. ([#12459](https://github.com/diegosouzapw/OmniRoute/pull/12459)) +- **fix(autoCombo):** layer 3 (`models_dev_tier`) no longer scores retired models as premium, merge cross-provider capability rows deterministically, and inherit tier scores through variant ids ([#11508](https://github.com/diegosouzapw/OmniRoute/issues/11508)). `getModelsDevTierFitness` now vetoes ids present in `config/quality/model-lifecycle.json` with status `retired` before any other signal; `loadModelCapabilities()` aggregates per `model_id` (any-non-null-true for booleans, max for `limit_context`) instead of last-write-wins over SQLite's undefined row order; a capabilities miss retries through `resolveScoresAs()`'s base id and reports `models_dev_tier:inherited`, matching layers 1–2 (#11492). Adds `getModelsDevTierFitnessWithSource()` and an `invalidateCapabilitiesCache()` test/ops hook. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) — thanks @Meet6338-X +- fix(sse): accept Claude `content:[]` + `stop_reason: max_tokens`/`tool_use` in `detectMalformedNonStream` (match `isEmptyContentResponse`) so Claude Code `/model` probes with `max_tokens: 1` no longer become a false 502 ([#11568](https://github.com/diegosouzapw/OmniRoute/pull/11568)) — thanks @Seramicx +- **fix(security):** sanitize `request.failed` diagnostics before publishing them to live dashboard listeners and replay history, while keeping status, model, provider, latency, and internal call-log diagnostics intact. ([#12469](https://github.com/diegosouzapw/OmniRoute/pull/12469)) +- **fix(pwa):** do not intercept dashboard navigations so Chrome can fall back from a stale HTTP/3 Alt-Svc advertisement (UDP :20128 is unpublished; F5 on a long-lived tab hung until a new tab opened a fresh TCP connection). ([#12767](https://github.com/diegosouzapw/OmniRoute/pull/12767)) — thanks @HouMinXi +- **fix(executors):** handle DuckDuckGo ERR_BN_LIMIT (418) without retrying — when the upstream returns `418 ERR_BN_LIMIT` (rate-limit/ban), the executor now returns the error immediately instead of burning another VQD acquisition that would only count against the IP limit. The retry logic for `418 ERR_CHALLENGE` (unsolved challenge) remains unchanged. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) ([#12110](https://github.com/diegosouzapw/OmniRoute/pull/12110)) — thanks @oyi77 / @Meet6338-X +- Fixed DuckDuckGo streaming responses losing JSON lines and UTF-8 characters split across network chunks. ([#11528](https://github.com/diegosouzapw/OmniRoute/pull/11528)) — thanks @pacocartones +- Harden generated Cloudflare, Vercel, and Deno relays by stripping hop-by-hop, framing, and proxy authentication headers before forwarding requests upstream. ([#11533](https://github.com/diegosouzapw/OmniRoute/pull/11533)) — thanks @pacocartones +- **feat(grok-cli):** Provider Limits shows grok-cli banked reset credits from `GetRemainingResets` (including a real zero; a failed RPC omits the row) and the existing View credits button now calls `ConsumerUiSvc/RedeemReset` for grok-cli. Live tokens use nested fields 10/20/30 (id + Timestamp), not compact 1/2/3. ([#12805](https://github.com/diegosouzapw/OmniRoute/pull/12805)) — thanks @HouMinXi +- **fix(memory):** Embedding Model Quick select, Embedding Source remote dropdown, and Rerank selector now list every configured provider with embedding/rerank support instead of only chat-catalog text matches plus OpenRouter live discovery; a generic OpenAI-compatible `/embeddings` + Cohere-compatible `/rerank` runtime fallback resolves any configured chat provider's embedding/rerank endpoint, so unlisted providers no longer fail with "Unknown embedding provider"; both memory selectors gained a free-text model override ([#11390](https://github.com/diegosouzapw/OmniRoute/pull/11390)) — thanks @rqzbeh +- Harden SQLite upgrades around the historical migration-074 version collision: missing discovery and inspector tables are replayed atomically, pre-existing databases (including setup-created skeletons) receive reusable content-addressed safety snapshots, and Node test/eval probes without `DATA_DIR` are isolated from the operator database. ([#12435](https://github.com/diegosouzapw/OmniRoute/pull/12435)) +- **fix(dashboard):** Moonshot/Kimi Open Platform voucher and cash leftover percentages follow the bucket balance, so an empty wallet no longer paints those rows as 100% while Available is 0% ([#12733](https://github.com/diegosouzapw/OmniRoute/pull/12733)) — thanks @HouMinXi +- **fix(combo):** return non-retryable HTTP 400 when all candidates for a pinned native Codex turn are unavailable due to model-scoped lockout, terminating the turn cleanly while preserving turn continuity and enabling standard Combo routing on subsequent turns ([#12240](https://github.com/diegosouzapw/OmniRoute/pull/12240)) — thanks @mdigitalbh81 +- Fixed auto-router strategies preserving the selected connection through ranking and dispatch when multiple connections share the same provider and model. ([#11530](https://github.com/diegosouzapw/OmniRoute/pull/11530)) — thanks @pacocartones +- Preserve Chat Completions JSON-object response formats when translating requests to the Responses API. ([#11529](https://github.com/diegosouzapw/OmniRoute/pull/11529)) — thanks @pacocartones +- Fix idempotency fingerprints so Responses API requests include `input` and other semantic generation fields using stable serialization, while excluding credentials and request metadata. ([#11532](https://github.com/diegosouzapw/OmniRoute/pull/11532)) — thanks @pacocartones +- **fix(api):** Generated API CLI commands now enforce required OpenAPI request bodies; Combo test commands forward the required `comboName` body, while API keys created by older writers after migration 149 preserve legacy allow-all Combo access without widening explicit empty allowlists — thanks @marcelokarval ([#12070](https://github.com/diegosouzapw/OmniRoute/pull/12070)) +- **fix(grok-web):** treat upstream streaming failures as failures instead of successful assistant text: error-only streams now fail readiness with HTTP 502, while failures after legitimate content preserve that partial output and terminate through the sanitized stream failure path without a normal `stop` completion. ([#12458](https://github.com/diegosouzapw/OmniRoute/pull/12458)) +- HuggingChat now turns HTTP 200 JSONL generation failures into a sanitized 502 before content, or a fixed public stream failure after partial output, so fallback and request persistence no longer record a false successful stop. ([#12456](https://github.com/diegosouzapw/OmniRoute/pull/12456)) +- **fix(providers):** keep 1min.ai HTTP 200 stream errors out of assistant content, preserve partial output, and expose sanitized terminal errors so pre-content failures can fall back. ([#12466](https://github.com/diegosouzapw/OmniRoute/pull/12466)) +- **fix(sessions):** preserve legacy Sessions data when the additive exclusive-session projection is unavailable, returning an empty projection and warning only once per contiguous outage instead of failing the endpoint; the Sessions badge intentionally reflects the merged legacy and exclusive row count introduced by [#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389) ([#11469](https://github.com/diegosouzapw/OmniRoute/pull/11469)) — thanks @KaspaPulse +- Render Nimble Search with the generic provider icon and serve Opper's proven logo locally. ([#12415](https://github.com/diegosouzapw/OmniRoute/pull/12415)) +- **fix(quality):** `config/quality/provider-assets-provenance.jsonl` was stale against the physical `public/providers/` tree (still listing 225 assets and 5 duplicate-content groups after several provider-retirement PRs removed 85 icon files), which would have made `check:provider-asset-provenance` fail on the next full run. Reconciled the manifest to the real 142-file snapshot (142 asset records, 1 duplicate-content group, 2 newly-registered files marked `unresolved` pending provenance research), repinned `auditedCommit`, and updated the regression test's hardcoded counts to match. ([#11876](https://github.com/diegosouzapw/OmniRoute/pull/11876)) +- fix(ui): wrap quota card footer action buttons to prevent clipping Refresh button on narrow cards — when extra action buttons such as `View credits` are present on Codex cards, allow the footer row to flex-wrap with `shrink-0` buttons so `Refresh now` remains visible and clickable across all responsive card widths ([#11464](https://github.com/diegosouzapw/OmniRoute/pull/11464)) — thanks @adevwithpurpose +- Keep Antigravity Gemini usable when the same connection's Claude weekly quota is empty; generic quota cache stays per-connection for every other provider. ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) — thanks @HouMinXi +- Fixed Tailscale tunnel setup to reject ports outside the valid 1-65535 range and safely install Windows MSI packages from paths containing apostrophes. ([#11534](https://github.com/diegosouzapw/OmniRoute/pull/11534)) — thanks @pacocartones +- Classify Cloudflared and Tailscale process-management routes as local-only and spawn-capable while preserving remote access to read-only tunnel status endpoints. ([#11531](https://github.com/diegosouzapw/OmniRoute/pull/11531)) — thanks @pacocartones +- Electron release workflow: the `publish-npm` job now grants `actions: read` to the reusable `npm-publish.yml` it calls (its `publish` job requests it), which is what made GitHub refuse the whole v3.8.50 run at startup and ship the release with zero desktop assets; a `workflow_dispatch` now builds the requested tag instead of the dispatching branch and can skip the npm leg (`publish_npm=false`) when only re-attaching assets ([#11974](https://github.com/diegosouzapw/OmniRoute/pull/11974)) +- **fix(dashboard):** The Combos page usage guide now reads its dismissal through `useSyncExternalStore` instead of correcting SSR state inside an effect, removing an extra commit of the page tree on every load (and the `react-hooks/set-state-in-effect` error it raised). ([#12671](https://github.com/diegosouzapw/OmniRoute/pull/12671)) +- Electron release: `electron/package-lock.json` regained the optional `electron-builder-squirrel-windows` subtree (13 entries) that `npm ci` had been refusing as out of sync, `electron-release.yml` gained a `build_ref` dispatch input and stops regenerating release notes on a re-attach dispatch, and the npm publish workflow attaches the SBOM to the GitHub Release on dispatch publishes too — so the v3.8.51 tag ships every desktop asset and the SBOM like v3.8.49 did ([#12022](https://github.com/diegosouzapw/OmniRoute/pull/12022)) +- Drained the reds every PR against `release/v3.8.51` was born with: documented the three service-worker build-id variables, registered the six retirement/tunnel tests with the mutation gate, approved `eslint-plugin-react-hooks` in the dependency allowlist, added the six `combo.sort.*` strings to `vi` and `pt-BR`, pointed the ChatGPT Web doc at the real migration-168 test, worded the g4f hint around the member key, and realigned four tests to the retired-provider catalog and the legacy-schema fixtures the retirement migrations touch. ([#11940](https://github.com/diegosouzapw/OmniRoute/pull/11940)) +- **fix(providers):** Claude, Grok, LMArena, Notion, and Perplexity web-cookie transports now use pooled `wreq-js` 3.2 instead of the native sidecar, with all nine supported bindings pinned and audited, and the applicable platform binding plus native-license evidence included in each release artifact ([#12429](https://github.com/diegosouzapw/OmniRoute/pull/12429), supersedes [#11753](https://github.com/diegosouzapw/OmniRoute/pull/11753)). +- **fix(providers):** Zed Hosted streaming failures now trigger fallback before content and end partial streams with a sanitized structured error instead of fake assistant text and a normal-success stop. ([#12455](https://github.com/diegosouzapw/OmniRoute/pull/12455)) +- **fix(test):** capture hook result after render (direct commit `b85d0abb0b`) +- **fix(sync):** repair the two regressions the v3.8.50 sync-back left on release/v3.8.51 (direct commit `5ade9e0851`) +- **revert(models):** drop the memo reset hook ported from #10055 (direct commit `282de841ae`) +- **fix(typecheck):** guard the optional connectionId before the reactive model sync (direct commit `f2825bad75`) +- **fix(quota-share):** release the winner's reserved in-flight slot (#11371) ([#11408](https://github.com/diegosouzapw/OmniRoute/pull/11408)) — thanks @oyi77 +- **fix(providers):** stop silently dropping reasoning effort on opencode families (#10788) ([#11409](https://github.com/diegosouzapw/OmniRoute/pull/11409)) — thanks @oyi77 +- **fix(combo):** enable genuine quota-aware routing for generic providers (antigravity, claude, etc.) ([#11411](https://github.com/diegosouzapw/OmniRoute/pull/11411)) — thanks @HouMinXi +- **fix(oauth):** allow expired token retry before deactivation and activate expired retry loop (#P0) ([#11414](https://github.com/diegosouzapw/OmniRoute/pull/11414)) — thanks @HouMinXi +- **perf(executors):** lazy-load the executor registry — defer class imports + construction to first use (#11220) ([#11421](https://github.com/diegosouzapw/OmniRoute/pull/11421)) — thanks @oyi77 +- **fix:** make release/v3.8.50 CI gates green (run 32786966560 root causes) ([#11450](https://github.com/diegosouzapw/OmniRoute/pull/11450)) — thanks @jonlwheat2-gif +- **fix(sse):** exact-domain cookie match and origin-equality URL assertions ([#11452](https://github.com/diegosouzapw/OmniRoute/pull/11452)) — thanks @hartmark +- **fix(providers):** send Responses API payload during OpenAI-compatible validation ([#11454](https://github.com/diegosouzapw/OmniRoute/pull/11454)) — thanks @tuandinh0801 +- **fix(command-code):** fallback to /alpha/generate for Go plan without Provider API access ([#11455](https://github.com/diegosouzapw/OmniRoute/pull/11455)) — thanks @Chewji9875 +- **fix(responses-continuation):** fail closed on a log-truncated stored input/output array ([#11473](https://github.com/diegosouzapw/OmniRoute/pull/11473)) — thanks @hartmark +- **fix(cli):** write jcode settings as [providers.omniroute] in config.toml ([#11484](https://github.com/diegosouzapw/OmniRoute/pull/11484)) — thanks @hongnoul +- **fix(logging):** raise chat-log array truncation cap to 1000 and unify duplicate implementations ([#11499](https://github.com/diegosouzapw/OmniRoute/pull/11499)) — thanks @hartmark +- **fix:** restore live websocket URL helpers ([#11509](https://github.com/diegosouzapw/OmniRoute/pull/11509)) — thanks @PauloFH +- **perf(core):** eliminate async waterfalls across home, cache, and db operations (#11396) ([#11512](https://github.com/diegosouzapw/OmniRoute/pull/11512)) — thanks @HouMinXi +- **fix(kiro):** add runtime.us-east-1.kiro.dev as first-attempt endpoint ([#11517](https://github.com/diegosouzapw/OmniRoute/pull/11517)) — thanks @rifqiawl +- **fix(providers):** remove duplicate 128-tool truncation in OpencodeExecutor (#11444) ([#11518](https://github.com/diegosouzapw/OmniRoute/pull/11518)) — thanks @HouMinXi +- **fix(providers):** poll onboardUser LRO before classifying BYOP ([#11519](https://github.com/diegosouzapw/OmniRoute/pull/11519)) — thanks @rifqiawl +- **fix(translator):** preserve exact tool name casing for Claude tool_use payloads (#11487) ([#11520](https://github.com/diegosouzapw/OmniRoute/pull/11520)) — thanks @HouMinXi +- **fix(kimi-web):** align health probe with executor domain ([#11521](https://github.com/diegosouzapw/OmniRoute/pull/11521)) — thanks @pacocartones +- **fix(cli):** preserve existing admin password during setup ([#11522](https://github.com/diegosouzapw/OmniRoute/pull/11522)) — thanks @pacocartones +- **fix(mitm):** keep Windows DNS elevation off the POSIX sudo path (#11430) ([#11542](https://github.com/diegosouzapw/OmniRoute/pull/11542)) — thanks @official-burak +- **fix(sse):** scale chat admission by ingest byte budget instead of a fixed request count ([#11548](https://github.com/diegosouzapw/OmniRoute/pull/11548)) — thanks @TheDemonTuan +- **fix:** accept Gemini Web cookie exports ([#11554](https://github.com/diegosouzapw/OmniRoute/pull/11554)) — thanks @rafacpti23 +- **fix(i18n):** complete Brazilian Portuguese translations ([#11558](https://github.com/diegosouzapw/OmniRoute/pull/11558)) — thanks @rafacpti23 +- **fix(sse):** explicit types for openai-responses pureHelpers — clears last failing core typecheck gate ([#11567](https://github.com/diegosouzapw/OmniRoute/pull/11567)) — thanks @jonlwheat2-gif +- **fix(opencode):** hydrate Proxy Pool references for no-auth connections ([#11584](https://github.com/diegosouzapw/OmniRoute/pull/11584)) — thanks @AStupidBear +- **fix(ci):** resolve inherited release/v3.8.51 base-red CI failures ([#11608](https://github.com/diegosouzapw/OmniRoute/pull/11608)) — thanks @jonlwheat2-gif +- **fix(db):** fallback load STORAGE_ENCRYPTION_KEY from env files ([#11614](https://github.com/diegosouzapw/OmniRoute/pull/11614)) — thanks @benzntech +- **fix(providers):** mark Brave Search as no longer having a free tier ([#11621](https://github.com/diegosouzapw/OmniRoute/pull/11621)) — thanks @turbolego +- **fix:** pass tool result content through as string in OpenAI/Claude -> Gemini translation ([#11624](https://github.com/diegosouzapw/OmniRoute/pull/11624)) — thanks @Hsia97 +- **fix(sse):** scope credential health to exact key ([#11628](https://github.com/diegosouzapw/OmniRoute/pull/11628)) — thanks @KaspaPulse +- **fix(install):** guard prepare script against missing husky ([#11634](https://github.com/diegosouzapw/OmniRoute/pull/11634)) — thanks @Prajeeth-12 +- **fix(security):** redact NVIDIA_API_KEY at the log sink in the ad-hoc diag script ([#11639](https://github.com/diegosouzapw/OmniRoute/pull/11639)) +- **fix(types):** clear the two type regressions red on release/v3.8.51 ([#11640](https://github.com/diegosouzapw/OmniRoute/pull/11640)) — thanks @jonlwheat2-gif +- **fix(build):** resolve workspace: protocol dependencies to concrete semver versions during prepublish (#11540) ([#11641](https://github.com/diegosouzapw/OmniRoute/pull/11641)) — thanks @HouMinXi +- **fix(search):** properly resolve configured search connection in /v1/responses pipeline (#11524) ([#11642](https://github.com/diegosouzapw/OmniRoute/pull/11642)) — thanks @HouMinXi +- **fix(authz):** trust cf-connecting-ip for via-proxy detection when peer is Cloudflare edge (#11514) ([#11643](https://github.com/diegosouzapw/OmniRoute/pull/11643)) — thanks @HouMinXi +- **fix(security):** compare the OIDC state cookie in constant time ([#11646](https://github.com/diegosouzapw/OmniRoute/pull/11646)) +- **fix(security):** namespace the dedup hash by the calling API key ([#11649](https://github.com/diegosouzapw/OmniRoute/pull/11649)) +- **fix(models):** gate the Codex-native catalog loop on prefix mode (#11632) ([#11662](https://github.com/diegosouzapw/OmniRoute/pull/11662)) — thanks @xiaoyaner0201 +- **fix(dashboard):** project Codex routing quota in account pools ([#11666](https://github.com/diegosouzapw/OmniRoute/pull/11666)) — thanks @KaspaPulse +- **fix(ddgw):** normaliza system no upstream da release v3.8.51 ([#11692](https://github.com/diegosouzapw/OmniRoute/pull/11692)) — thanks @RhianB14 +- **fix(memory):** honor category filter in GET /api/memory ([#11699](https://github.com/diegosouzapw/OmniRoute/pull/11699)) — thanks @Sabeekhann +- **fix(ci):** detect stale workspace lockfile entries ([#11714](https://github.com/diegosouzapw/OmniRoute/pull/11714)) — thanks @pacocartones +- **fix(ci):** stamp dist/BUILD_SHA before the npm publish provenance gate ([#11721](https://github.com/diegosouzapw/OmniRoute/pull/11721)) +- **fix(docker):** decouple the best-effort Bun image from the release manifest ([#11724](https://github.com/diegosouzapw/OmniRoute/pull/11724)) +- **perf(compression):** avoid cloning memo misses twice ([#11727](https://github.com/diegosouzapw/OmniRoute/pull/11727)) — thanks @RaviTharuma +- **fix(docker):** let the bun digest artifact be absent, not fatal ([#11740](https://github.com/diegosouzapw/OmniRoute/pull/11740)) +- **fix(shared):** block cloud-metadata hosts under default remote-image guard ([#11755](https://github.com/diegosouzapw/OmniRoute/pull/11755)) — thanks @santosraju99-hub +- **fix(api):** preserve caller-provided X-Correlation-Id on chat completions ([#11760](https://github.com/diegosouzapw/OmniRoute/pull/11760)) — thanks @aniruddhaadak80 +- **fix(resilience):** lock permanently retired models instead of short backoff (Gemini ban prevention) ([#11762](https://github.com/diegosouzapw/OmniRoute/pull/11762)) — thanks @turbolego +- **fix:** mark Vercel AI Gateway as passthroughModels ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **fix(resilience):** stop hammering permanently-moved endpoints and billing-suspended accounts ([#11774](https://github.com/diegosouzapw/OmniRoute/pull/11774)) — thanks @turbolego +- **fix(lease):** remove global static reservation and gate routing on live active lease occupancy ([#11775](https://github.com/diegosouzapw/OmniRoute/pull/11775)) — thanks @TheDemonTuan +- **fix(ci):** size the install-upgrade gate to a measured run, and log the pack cost ([#11776](https://github.com/diegosouzapw/OmniRoute/pull/11776)) +- **fix(pwa):** stop serving the stale shell after deploys ([#11779](https://github.com/diegosouzapw/OmniRoute/pull/11779)) — thanks @HouMinXi +- **fix(combo):** lock GitHub models rejected as "not supported" for future requests ([#11781](https://github.com/diegosouzapw/OmniRoute/pull/11781)) — thanks @turbolego +- **fix(cli):** probe both IPv4 and IPv6 loopback for server readiness (#11766) ([#11794](https://github.com/diegosouzapw/OmniRoute/pull/11794)) — thanks @vermasomesh835 +- **fix(sse):** preserve CLIProxyAPI wrappers per connection ([#11797](https://github.com/diegosouzapw/OmniRoute/pull/11797)) — thanks @RaviTharuma +- **fix(providers):** honor model auto-fetch opt-in on create ([#11798](https://github.com/diegosouzapw/OmniRoute/pull/11798)) — thanks @RaviTharuma +- **fix(dashboard):** make model auto-fetch order independent ([#11805](https://github.com/diegosouzapw/OmniRoute/pull/11805)) — thanks @RaviTharuma +- **fix(docker):** make GHA cache exports best effort ([#11806](https://github.com/diegosouzapw/OmniRoute/pull/11806)) — thanks @RaviTharuma +- **fix(kiro):** do not permanently ban on 'User is not authorized to make this call' ([#11809](https://github.com/diegosouzapw/OmniRoute/pull/11809)) — thanks @Deftera186 +- **fix(sse):** keep cache-write tokens in OpenAI-shaped usage ([#11814](https://github.com/diegosouzapw/OmniRoute/pull/11814)) — thanks @TheDemonTuan +- **fix(docs):** resolve relative markdown and wiki links across Fumadocs and GitHub wiki ([#11834](https://github.com/diegosouzapw/OmniRoute/pull/11834)) — thanks @AndrianBalanescu +- **fix(chat):** preserve unstripped model string for passthrough provider routing ([#11840](https://github.com/diegosouzapw/OmniRoute/pull/11840)) — thanks @raheemuddin786 +- **fix(mcp):** avoid async initialization inside sync esm wrappers (#11569) ([#11849](https://github.com/diegosouzapw/OmniRoute/pull/11849)) — thanks @HouMinXi +- **fix(sse):** preserve coherent markdown token boundaries across stream translation chunks (#11606) ([#11851](https://github.com/diegosouzapw/OmniRoute/pull/11851)) — thanks @HouMinXi +- **fix(ci):** run the install-upgrade gate on disk, not on the /tmp tmpfs ([#11855](https://github.com/diegosouzapw/OmniRoute/pull/11855)) +- **fix(skills):** expand shorthand property types in injected tool schemas ([#11857](https://github.com/diegosouzapw/OmniRoute/pull/11857)) — thanks @alvinveroy +- **fix(ci):** publish npm from a hosted runner so provenance is accepted ([#11877](https://github.com/diegosouzapw/OmniRoute/pull/11877)) +- **fix(lint):** pin react hooks plugin to 7.0.1 ([#11886](https://github.com/diegosouzapw/OmniRoute/pull/11886)) +- **fix(ci):** surface and clear release-green ESLint failure ([#11890](https://github.com/diegosouzapw/OmniRoute/pull/11890)) — thanks @MumuTW +- **fix(guardrails):** prevent duplicate prompt-injection-guard log output ([#11936](https://github.com/diegosouzapw/OmniRoute/pull/11936)) — thanks @patrykkopycinski +- **fix(auth):** downgrade expected transient states from warn to debug ([#11937](https://github.com/diegosouzapw/OmniRoute/pull/11937)) — thanks @patrykkopycinski +- **fix(codex):** fail over image generation for imported free plans ([#11948](https://github.com/diegosouzapw/OmniRoute/pull/11948)) — thanks @Bl0ck154 +- **fix(codex):** normalize non-stream responses ([#11951](https://github.com/diegosouzapw/OmniRoute/pull/11951)) — thanks @Bl0ck154 +- **fix(images):** forward Antigravity image size ([#11952](https://github.com/diegosouzapw/OmniRoute/pull/11952)) — thanks @Bl0ck154 +- **fix(db):** invalidate connection cache after upsert ([#11953](https://github.com/diegosouzapw/OmniRoute/pull/11953)) — thanks @Bl0ck154 +- **fix(codex):** restore imported account state ([#11954](https://github.com/diegosouzapw/OmniRoute/pull/11954)) — thanks @Bl0ck154 +- **fix(sse):** give extended-thinking targets the reasoning readiness budget ([#11959](https://github.com/diegosouzapw/OmniRoute/pull/11959)) — thanks @abhisheksharma2411 +- **fix(dashboard):** use opaque background and readable text color on cost chart tooltips ([#11960](https://github.com/diegosouzapw/OmniRoute/pull/11960)) — thanks @ZaimMarzuki +- **fix(antigravity):** send complete loadCodeAssist metadata (ideType/platform/pluginType as numeric enums) ([#11969](https://github.com/diegosouzapw/OmniRoute/pull/11969)) — thanks @rifqiawl +- **fix(core):** resolve DB init race condition and reasoning translation ([#12003](https://github.com/diegosouzapw/OmniRoute/pull/12003)) — thanks @raheemuddin786 +- **fix(admission):** exclude reclaimable page cache from the cgroup pressure ratio ([#12017](https://github.com/diegosouzapw/OmniRoute/pull/12017)) — thanks @HouMinXi +- **fix(build):** prune non-production trees in NFT trace excludes and tsconfig ([#12028](https://github.com/diegosouzapw/OmniRoute/pull/12028)) — thanks @Chewji9875 +- **fix(migrations):** add renamed migration compatibility for 056/073/077/101 ([#12036](https://github.com/diegosouzapw/OmniRoute/pull/12036)) — thanks @oyi77 +- **fix(sse):** default crash-guard logger to console.warn, not console ([#12042](https://github.com/diegosouzapw/OmniRoute/pull/12042)) — thanks @HouMinXi +- **fix(catalog):** derive vision/modalities for built-in auto combos from effective target pool ([#12046](https://github.com/diegosouzapw/OmniRoute/pull/12046)) — thanks @Prajeeth-12 +- **fix(release):** the packaged-app smoke verifies the database opened, not a driver line the primary path never prints (twin of #12032) ([#12047](https://github.com/diegosouzapw/OmniRoute/pull/12047)) +- **fix(barrel):** migrate src/app/ off the @/lib/localDb barrel import (#11795 Phase 2) ([#12051](https://github.com/diegosouzapw/OmniRoute/pull/12051)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate src/lib/ off the localDb barrel to direct db imports (#59) ([#12052](https://github.com/diegosouzapw/OmniRoute/pull/12052)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate open-sse, src/shared, src/sse, src/models, src/domain off the @/lib/localDb barrel import (#11795 Phase 4) ([#12053](https://github.com/diegosouzapw/OmniRoute/pull/12053)) — thanks @jonlwheat2-gif +- **fix(barrel):** delete the @/lib/localDb barrel — every consumer migrated (#11795 Phase 5) ([#12055](https://github.com/diegosouzapw/OmniRoute/pull/12055)) — thanks @jonlwheat2-gif +- **fix(radar):** restore D12 public boundary ([#12057](https://github.com/diegosouzapw/OmniRoute/pull/12057)) +- **fix(dev):** bound webpack and Tailwind scans ([#12075](https://github.com/diegosouzapw/OmniRoute/pull/12075)) — thanks @backryun +- **fix(dev):** isolate root layout settings reads ([#12076](https://github.com/diegosouzapw/OmniRoute/pull/12076)) — thanks @backryun +- **fix(dev):** reduce instrumentation executor fan-out (phase 3) ([#12078](https://github.com/diegosouzapw/OmniRoute/pull/12078)) — thanks @backryun +- **fix(dev):** make logging resources HMR-singleton ([#12079](https://github.com/diegosouzapw/OmniRoute/pull/12079)) — thanks @backryun +- **fix(sse):** keep unavailable forced connections scoped ([#12080](https://github.com/diegosouzapw/OmniRoute/pull/12080)) — thanks @keeltrace +- **fix(dev):** isolate batch dispatch from instrumentation ([#12081](https://github.com/diegosouzapw/OmniRoute/pull/12081)) — thanks @backryun +- **fix(ci):** reconcile release test contract drift ([#12082](https://github.com/diegosouzapw/OmniRoute/pull/12082)) — thanks @backryun +- **fix(ci):** accept CVE-2025-68121 in the prebuilt tls-client .so, auto-close base-red issues, guard Scorecard on the default branch ([#12085](https://github.com/diegosouzapw/OmniRoute/pull/12085)) +- **fix(db):** drop three consumer-less 1proxy exports — dead-code base-red on release/v3.8.51 after the barrel deletion (#12055) ([#12087](https://github.com/diegosouzapw/OmniRoute/pull/12087)) +- **fix(api):** clear the six API-route TypeScript regressions the new gate landed red on ([#12094](https://github.com/diegosouzapw/OmniRoute/pull/12094)) +- **fix(release):** never let the tag-push Create Release append auto notes to the curated body ([#12096](https://github.com/diegosouzapw/OmniRoute/pull/12096)) +- **fix(sse):** honor CLIProxyAPI environment API key ([#12099](https://github.com/diegosouzapw/OmniRoute/pull/12099)) — thanks @RaviTharuma +- **fix(oauth):** bind Google refresh to the client that issued the token ([#12106](https://github.com/diegosouzapw/OmniRoute/pull/12106)) — thanks @HouMinXi +- **fix(dashboard):** make RequestLoggerDetail loadable outside Next — CSS via globals.css + CJS/ESM interop (#11703 base-reds) ([#12114](https://github.com/diegosouzapw/OmniRoute/pull/12114)) +- **fix(leases):** project status lease row to lease columns so joined connection PII never escapes ([#12115](https://github.com/diegosouzapw/OmniRoute/pull/12115)) — thanks @geek007git +- **fix(plugins):** do not kill the plugin process when a fire-and-forget hook times out ([#12116](https://github.com/diegosouzapw/OmniRoute/pull/12116)) — thanks @geek007git +- **fix(guardrails):** restore injection-guard logging on middleware-only routes ([#12117](https://github.com/diegosouzapw/OmniRoute/pull/12117)) — thanks @geek007git +- **fix(plugins):** refresh stored manifest from disk on activate so new hook fields reach existing installs ([#12120](https://github.com/diegosouzapw/OmniRoute/pull/12120)) — thanks @geek007git +- **fix(codex):** preserve existing provider state when bulk-import upserts a matching connection ([#12122](https://github.com/diegosouzapw/OmniRoute/pull/12122)) — thanks @geek007git +- **fix(ci):** clear the two base-reds the 2026-08-30 merge batch left on release/v3.8.51 (round 3) ([#12123](https://github.com/diegosouzapw/OmniRoute/pull/12123)) +- **fix(sse):** strip type:'custom' from Claude tools on agentrouter dispatch ([#12126](https://github.com/diegosouzapw/OmniRoute/pull/12126)) — thanks @wahidsadik371-coder +- **fix(sse):** stop advertising video providers the dispatcher cannot run ([#12131](https://github.com/diegosouzapw/OmniRoute/pull/12131)) — thanks @quiterunner-commits +- **fix(search):** treat HTTP 432 and plan limit errors as transient cooldown ([#12139](https://github.com/diegosouzapw/OmniRoute/pull/12139)) — thanks @HouMinXi +- **fix(sse):** refuse an AI Horde queue that cannot fit the request budget ([#12143](https://github.com/diegosouzapw/OmniRoute/pull/12143)) — thanks @quiterunner-commits +- **fix(ci):** clear the base-reds the afternoon merge batch left on release/v3.8.51 (round 5: provider count 352, TS2554/TS2677) ([#12144](https://github.com/diegosouzapw/OmniRoute/pull/12144)) +- **fix(translator):** strip neutral tool_choice when tools absent in Responses-to-Chat (#12141) ([#12166](https://github.com/diegosouzapw/OmniRoute/pull/12166)) — thanks @HouMinXi +- **fix(guardrails):** pass providerId to getResolvedModelCapabilities in checkComboVision (#12112) ([#12169](https://github.com/diegosouzapw/OmniRoute/pull/12169)) — thanks @HouMinXi +- **fix(translator):** strip plaintext reasoning content for opaque responses backends (#12128) ([#12171](https://github.com/diegosouzapw/OmniRoute/pull/12171)) — thanks @HouMinXi +- **perf(stream):** compile hot-path regexes once, bound token caches, fix quadratic buffering ([#12179](https://github.com/diegosouzapw/OmniRoute/pull/12179)) — thanks @opensource-elearning +- **fix:** resolve compression worker file using runtime anchors instead of… ([#12183](https://github.com/diegosouzapw/OmniRoute/pull/12183)) — thanks @NightStalker-87 +- **fix:** prevent Claude Code session kills via liveness-aware readiness + auto model echo ([#12189](https://github.com/diegosouzapw/OmniRoute/pull/12189)) — thanks @opensource-elearning +- **fix(translator):** preserve falsy primitive values in Gemini and Antigravity function response results ([#12191](https://github.com/diegosouzapw/OmniRoute/pull/12191)) — thanks @Siva010 +- **fix(oauth):** mark empty Antigravity projectId as degraded and clear stale errors (#11284) ([#12205](https://github.com/diegosouzapw/OmniRoute/pull/12205)) — thanks @HouMinXi +- **fix(providers):** gate the Codex auto-ping usage read on the shared quota throttle ([#12209](https://github.com/diegosouzapw/OmniRoute/pull/12209)) — thanks @abhisheksharma2411 +- **fix(combos):** prioritize SQLite row id over inner JSON id and notify delete errors ([#12213](https://github.com/diegosouzapw/OmniRoute/pull/12213)) — thanks @HouMinXi +- **fix(providers):** mark featherless.ai as no longer having a free tier (`hasFree: false`) ([#12216](https://github.com/diegosouzapw/OmniRoute/pull/12216)) — thanks @turbolego +- **fix(deepseek-web):** stop Turbopack dev panic in the PoW worker path resolver ([#12221](https://github.com/diegosouzapw/OmniRoute/pull/12221)) — thanks @hartmark +- **fix(chat-admission):** clarify local 503 source ([#12223](https://github.com/diegosouzapw/OmniRoute/pull/12223)) — thanks @keeltrace +- **fix(dev):** silence webpack runtime module warnings ([#12228](https://github.com/diegosouzapw/OmniRoute/pull/12228)) — thanks @backryun +- **fix(db):** use module.require for CommonJS runtime driver loading ([#12230](https://github.com/diegosouzapw/OmniRoute/pull/12230)) — thanks @raheemuddin786 +- **fix(memory):** honest probe-driven FTS5 keyword status + memory_id rowid sync ([#12231](https://github.com/diegosouzapw/OmniRoute/pull/12231)) — thanks @raheemuddin786 +- **fix(system):** propagate abort signal to stream reader in HTTP version checks ([#12232](https://github.com/diegosouzapw/OmniRoute/pull/12232)) — thanks @raheemuddin786 +- **fix(resilience):** isolate local host execution errors from provider circuit breakers ([#12233](https://github.com/diegosouzapw/OmniRoute/pull/12233)) — thanks @raheemuddin786 +- **fix(sse):** sort injected tools deterministically for prompt caching ([#12234](https://github.com/diegosouzapw/OmniRoute/pull/12234)) — thanks @raheemuddin786 +- **fix(combo):** always clear the loop-safety timer, not just on the happy path (#11804) ([#12245](https://github.com/diegosouzapw/OmniRoute/pull/12245)) +- **fix(quality):** base-red pair — stryker tap registration + turn-pin suites aligned to the window gate ([#12255](https://github.com/diegosouzapw/OmniRoute/pull/12255)) — thanks @backryun +- **fix(dev):** qualify Turbopack runtime boundaries ([#12258](https://github.com/diegosouzapw/OmniRoute/pull/12258)) — thanks @backryun +- **fix(combo):** fall back to full pool when collapsed sole survivor is context-too-small ([#12278](https://github.com/diegosouzapw/OmniRoute/pull/12278)) — thanks @opensource-elearning +- **fix(combo):** bound the pre-dispatch unavailable skip so a stale label cannot dark a pool (#12168) ([#12285](https://github.com/diegosouzapw/OmniRoute/pull/12285)) +- **fix(security):** strip Qwen/Alibaba console-session cookies from provider API responses ([#12287](https://github.com/diegosouzapw/OmniRoute/pull/12287)) +- **fix(usage):** console-aware Token Plan guidance + subscription hint on bailian 401 ([#12288](https://github.com/diegosouzapw/OmniRoute/pull/12288)) +- **fix(resourcePressure):** log numeric detail on every rejection, recover faster ([#12293](https://github.com/diegosouzapw/OmniRoute/pull/12293)) — thanks @hartmark +- **fix(release):** drain the 2026-09-01 base-red window — passthrough usage regression + radarPage i18n keys ([#12327](https://github.com/diegosouzapw/OmniRoute/pull/12327)) +- **fix(ci):** openapi-security-tiers checker must honor routeGuard patterns + imported prefixes ([#12350](https://github.com/diegosouzapw/OmniRoute/pull/12350)) — thanks @ggiak +- **perf(ci):** cache node_modules in the npm-ci-retry composite (#8084 D3) ([#12408](https://github.com/diegosouzapw/OmniRoute/pull/12408)) +- **revert(ui):** point CTAs back at their real destinations ([#12410](https://github.com/diegosouzapw/OmniRoute/pull/12410)) +- **fix(release):** drain the 2026-09-02 base-red — rerank-providers import + api-typecheck baseline ratchet ([#12414](https://github.com/diegosouzapw/OmniRoute/pull/12414)) +- **fix(types):** make system prompt injection noImplicitAny-safe ([#12416](https://github.com/diegosouzapw/OmniRoute/pull/12416)) +- **fix(quality):** validate typecheck baseline schema ([#12419](https://github.com/diegosouzapw/OmniRoute/pull/12419)) +- **fix(memory):** resolve rerank provider node cache import ([#12421](https://github.com/diegosouzapw/OmniRoute/pull/12421)) +- **fix(ci):** repair release-root regressions — pack dedup, web-session syntax, uc-image ids ([#12423](https://github.com/diegosouzapw/OmniRoute/pull/12423)) — thanks @backryun +- **fix(providers):** separate MaxAI and UC credential contracts ([#12431](https://github.com/diegosouzapw/OmniRoute/pull/12431)) +- **fix(cli):** preserve Claude settings on config set ([#12432](https://github.com/diegosouzapw/OmniRoute/pull/12432)) — thanks @KrzysiekSko +- **fix(providers):** repair the maxai credential block truncated by merge auto-resolve ([#12433](https://github.com/diegosouzapw/OmniRoute/pull/12433)) +- **fix(quality):** record the 2026-09-02 merged growth in the file-size baseline ([#12434](https://github.com/diegosouzapw/OmniRoute/pull/12434)) +- **fix(uc):** route UC error strings through sanitizeErrorMessage; allowlist the retired codex id ([#12437](https://github.com/diegosouzapw/OmniRoute/pull/12437)) +- **fix(adapta):** redact streamed upstream errors ([#12438](https://github.com/diegosouzapw/OmniRoute/pull/12438)) +- **fix(sse):** remap non-contiguous upstream tool_calls index to a gap-free output_index ([#12445](https://github.com/diegosouzapw/OmniRoute/pull/12445)) — thanks @hartmark +- **fix(quality):** detect duplicate tool_calls entries in one response ([#12446](https://github.com/diegosouzapw/OmniRoute/pull/12446)) — thanks @hartmark +- **fix(conversations):** resolve turn content OmniRoute never sends back to the client ([#12447](https://github.com/diegosouzapw/OmniRoute/pull/12447)) — thanks @hartmark +- **fix(combo):** do not treat credits-exhausted 401 as auth skip ([#12449](https://github.com/diegosouzapw/OmniRoute/pull/12449)) — thanks @RaviTharuma +- **fix(auth):** do not park healthy quota accounts as expired ([#12452](https://github.com/diegosouzapw/OmniRoute/pull/12452)) — thanks @RaviTharuma +- **fix(responses-continuation):** fail closed on a collector-truncated, empty output array ([#12460](https://github.com/diegosouzapw/OmniRoute/pull/12460)) — thanks @hartmark +- **fix(logging):** raise the SSE payload collector's default cap ([#12461](https://github.com/diegosouzapw/OmniRoute/pull/12461)) — thanks @hartmark +- **fix(resilience):** surface Responses failed.error.message in 502s ([#12472](https://github.com/diegosouzapw/OmniRoute/pull/12472)) — thanks @RaviTharuma +- **fix(security):** unbiased maxai X-Random nonce + stricter URL/regex assertions ([#12502](https://github.com/diegosouzapw/OmniRoute/pull/12502)) +- **fix(video):** re-anchor transcript log-redaction so PII/credential maskers can't reopen the leak (#12150 P1 follow-up) ([#12503](https://github.com/diegosouzapw/OmniRoute/pull/12503)) +- **fix(monitoring):** serve cached credentialHealth off the request path ([#12533](https://github.com/diegosouzapw/OmniRoute/pull/12533)) — thanks @RaviTharuma +- **fix(quota):** keep Antigravity Gemini usable when Claude weekly is empty ([#12566](https://github.com/diegosouzapw/OmniRoute/pull/12566)) — thanks @HouMinXi +- **fix(authz):** hard-gate every credential export and CLI-config write (GHSA-5926-2w35-7h4q) ([#12600](https://github.com/diegosouzapw/OmniRoute/pull/12600)) +- **fix(docs):** restore the Next build — REMOVED_PROVIDERS.md had no frontmatter (base-red #12581) ([#12610](https://github.com/diegosouzapw/OmniRoute/pull/12610)) +- **fix(quality):** re-point the zcodeProtocol public-creds allowlist to line 313 ([#12615](https://github.com/diegosouzapw/OmniRoute/pull/12615)) +- **fix(security):** close 3 advisories — search baseUrl exfil, sk- in the error sanitizer, bifrost relay header leak ([#12620](https://github.com/diegosouzapw/OmniRoute/pull/12620)) +- **fix(providers):** sanitize boolean required and nested bare maps for Gemini (#12269) ([#12624](https://github.com/diegosouzapw/OmniRoute/pull/12624)) — thanks @HouMinXi +- **fix(resilience):** keep Overloaded STREAM_EARLY_EOF off the provider breaker ([#12626](https://github.com/diegosouzapw/OmniRoute/pull/12626)) — thanks @HouMinXi +- **fix(catalog):** re-audit free-tier quotas against official pages ([#12649](https://github.com/diegosouzapw/OmniRoute/pull/12649)) +- **fix(dashboard):** keep a request's pending-tracking id stable across combo target retries ([#12650](https://github.com/diegosouzapw/OmniRoute/pull/12650)) — thanks @hartmark +- **fix(tests):** retire dead model ids from the chat-pipeline integration suite (base-red #12581) ([#12670](https://github.com/diegosouzapw/OmniRoute/pull/12670)) +- **fix:** resolve SqliteError no such table compression_run_telemetry during cleanup ([#12682](https://github.com/diegosouzapw/OmniRoute/pull/12682)) — thanks @groovecityJO +- **fix(combo):** treat a pin-only step as implicit connection allowlist ([#12697](https://github.com/diegosouzapw/OmniRoute/pull/12697)) — thanks @HouMinXi +- **fix(ci):** ship .npmrc in published package so legacy-peer-deps applies to consumers (#11544) ([#12699](https://github.com/diegosouzapw/OmniRoute/pull/12699)) — thanks @KooshaPari +- **fix(docker):** pin 4 CLI tools to exact versions (#12576) ([#12703](https://github.com/diegosouzapw/OmniRoute/pull/12703)) — thanks @KooshaPari +- **fix(docker):** document and harden cli profile trust boundary (#12570) ([#12706](https://github.com/diegosouzapw/OmniRoute/pull/12706)) — thanks @KooshaPari +- **fix(sse):** redact video transcript in pre-guardrail rejected-request logs (#12150 P2 item 7) ([#12710](https://github.com/diegosouzapw/OmniRoute/pull/12710)) +- **fix(i18n):** quote placeholder in OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES description (#12505) ([#12769](https://github.com/diegosouzapw/OmniRoute/pull/12769)) — thanks @KooshaPari +- **fix(api/pricing):** surface validation error message as string, not raw object (#12494) ([#12771](https://github.com/diegosouzapw/OmniRoute/pull/12771)) — thanks @KooshaPari +- **fix(sse):** re-enable prompt compression for native Codex passthrough ([#12834](https://github.com/diegosouzapw/OmniRoute/pull/12834)) — thanks @thomasmaerz +- **fix(dashboard):** model health tests for a provider node set to the Responses API now call `/v1/responses` with a Responses-shaped body instead of `/v1/chat/completions` — those models were reported as `Provider returned HTTP 200 but no text content` even though the same model answered normally through `/v1/responses` ([#13070](https://github.com/diegosouzapw/OmniRoute/issues/13070)) ([#13087](https://github.com/diegosouzapw/OmniRoute/pull/13087)) — thanks @ntdatt812 +- fix(routing): stop a round-robin combo's "opencode" targets from collapsing onto the opencode-zen connection (#11912) ([#13283](https://github.com/diegosouzapw/OmniRoute/pull/13283)) +- fix(routing): stop the reactive-compaction debug log from lying when compression is globally disabled (#11977) ([#13276](https://github.com/diegosouzapw/OmniRoute/pull/13276)) +- fix(electron): relativize standalone-bundle symlink targets so Stage 8 manifest verification stops failing on Windows (#11979) ([#13251](https://github.com/diegosouzapw/OmniRoute/pull/13251)) +- fix(dashboard): surface a visible error when Compression Studio's combined preview run fails (#12061) ([#13288](https://github.com/diegosouzapw/OmniRoute/pull/13288)) +- fix(dashboard): make the compression "Effective pipeline" preview honor the active profile and warn when the master switch is off (#12063) ([#13287](https://github.com/diegosouzapw/OmniRoute/pull/13287)) +- fix(providers): scope TinyCMS Web signer's DOM shims to each call instead of leaking them for the process lifetime, and surface a clean HTTP status on a non-JSON interception-toggles error (#12072) ([#13254](https://github.com/diegosouzapw/OmniRoute/pull/13254)) +- fix(guardrails): stop Vision Bridge from re-selecting a model locked after a 404 (#12111) ([#13259](https://github.com/diegosouzapw/OmniRoute/pull/13259)) +- fix(sse): require Responses-shaped body before native OpenAI-compatible passthrough (#12129) ([#13278](https://github.com/diegosouzapw/OmniRoute/pull/13278)) +- fix(providers): minimax-m3 now collapses manual thinking.type:"enabled" to adaptive, preventing upstream 400 (2013) (#12132) ([#13249](https://github.com/diegosouzapw/OmniRoute/pull/13249)) +- fix(db): scope model visibility overrides by modality so hiding a Chat model no longer hides an identically-ID'd Image/Embeddings/etc. model (#12172) ([#13258](https://github.com/diegosouzapw/OmniRoute/pull/13258)) +- fix(db): scope local-provider apiKey dedup to matching base URL so LM Studio/Ollama-style connections support multiple accounts (#12173) ([#13250](https://github.com/diegosouzapw/OmniRoute/pull/13250)) +- fix(providers): refresh Trae's stale Referer/Origin and forward user timezone so imported connections stop failing with 401 (#12190) ([#13255](https://github.com/diegosouzapw/OmniRoute/pull/13255)) +- fix(providers): route opencode-go/gpt-5.6-luna to /responses instead of /chat/completions (#12196) ([#13275](https://github.com/diegosouzapw/OmniRoute/pull/13275)) +- fix(dashboard): allow deleting the last extra-upstream-header row even when invalid (#12251) ([#13252](https://github.com/diegosouzapw/OmniRoute/pull/13252)) +- **fix(i18n):** translate pre-existing `__MISSING__:` keys for `combo.sort`, `requestLogger.detail` expand/collapse, `common.profile`, and `settings.resilienceCredentialHealth*` across 39 locales ([#12272](https://github.com/diegosouzapw/OmniRoute/issues/12272)) ([#13034](https://github.com/diegosouzapw/OmniRoute/pull/13034)) — thanks @HouMinXi +- fix(cli): run the Node.js runtime compatibility guard before the heavy `tsx/esm` + Commander import chain so an unsupported runtime gets a clear message instead of a raw `Invalid regular expression flags` crash (#12296) ([#13253](https://github.com/diegosouzapw/OmniRoute/pull/13253)) +- fix(dashboard): refresh the providers list after deleting a compatible provider node (#12298) ([#13256](https://github.com/diegosouzapw/OmniRoute/pull/13256)) +- fix(usage): fail closed on API-key budget enforcement when a provider's `auto` routing alias has no pricing row, instead of silently counting it as $0 (#12341) ([#13257](https://github.com/diegosouzapw/OmniRoute/pull/13257)) +- **fix(providers):** include the submitted Agnes video model when polling by `video_id` ([#12356](https://github.com/diegosouzapw/OmniRoute/pull/12356)) — thanks @pacocartones +- **fix(routing):** custom OpenAI-compatible nodes now honor the saved Chat/Responses API type after edits instead of letting the node's original ID prefix override the live connection setting ([#11884](https://github.com/diegosouzapw/OmniRoute/issues/11884)). ([#12358](https://github.com/diegosouzapw/OmniRoute/pull/12358)) — thanks @pacocartones +- fix(sse): surface an error instead of a silent empty 200 when a Claude stream closes with zero bytes (#12398) ([#13285](https://github.com/diegosouzapw/OmniRoute/pull/13285)) +- fix(oauth): warn before the dead localhost:8080 redirect in antigravity/gemini `oauth start` (#12413) ([#13265](https://github.com/diegosouzapw/OmniRoute/pull/13265)) +- fix(providers): stop devin-cli spawn error from double-closing the SSE controller (#12517) ([#13261](https://github.com/diegosouzapw/OmniRoute/pull/13261)) +- **fix(rerank):** clamp Voyage `top_k` to the documents actually sent after empty-string filtering, and honor `return_documents: false` in the NVIDIA response adapter (#12523 — thanks @pacocartones) +- **fix(audio):** `/v1/audio/translations` now resolves combo names the way `/v1/audio/transcriptions` already does, so a combo that `GET /v1/models` advertises is fanned out to its targets instead of being rejected with `400 Invalid translation model: . Use format: provider/model`; literal `provider/model` ids and unknown bare names behave as before (#12536 — thanks @pacocartones) +- **fix(gemini):** strip the JSON-Schema-2020-12 `prefixItems` keyword from Gemini tool schemas at every nesting level, so Claude Code tool definitions no longer fail with `400 Unknown name "prefixItems"` on Gemini models (#12540 — thanks @pacocartones) +- **fix(api-manager):** Expose an accessible loading status while API keys are fetched instead of an empty accessibility tree (#12541 — thanks @pacocartones) +- **fix(video):** Clamp `estimateJpegFrameBytes` at zero for padding-only payloads and build the three encode-side frame data URIs from `JPEG_FRAME_DATA_URI_PREFIX` instead of a repeated literal (#12543 — thanks @pacocartones) +- **fix(devin):** accept Windows `DEVIN_AGENTIC_HOME` sandbox paths (`C:\...\.sandbox\...`) in the isolated-home check so the Devin Claude Bridge no longer fails closed on Windows ([#12405](https://github.com/diegosouzapw/OmniRoute/issues/12405)) (#12545 — thanks @pacocartones) +- **fix(db):** Add `conversation_turn_nodes` and orphaned `agentic_conversations` to the auto-cleanup cycle under the existing `retention.callLogs` window, so identity nodes whose call-log content has already been purged no longer accumulate without bound in `storage.sqlite` (#12548 — thanks @pacocartones) +- **fix(i18n):** Wrap the `~/.claude/profiles//settings.json` placeholder in ICU single quotes in the `OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES` feature-flag description across all 42 locales and the TypeScript default, so next-intl no longer fails with `INVALID_MESSAGE: UNCLOSED_TAG` and the Feature Flags card shows the description instead of the raw key (#12549 — thanks @pacocartones) +- **fix(orchestration):** `updateCloudAgentTask` now publishes the task's real `status` on `agent.task.updated` when an update only touches `result`, `activities` or `error`, instead of the fabricated `"updated"` state, and stays silent when no row matched the id (#12550 — thanks @pacocartones) +- **fix(i18n):** the home "Recent Requests" panel and the Provider Topology legend are now translated instead of rendering English copies on non-English dashboards; the legend reads its own `home.topologyLegend*` labels with consistent casing rather than borrowing the memory-settings "Recent" and analytics "Error" strings (#12551 — thanks @pacocartones). +- **docs(reference):** bring the `FEATURE_FLAGS.md` catalog back to 1:1 with `featureFlagDefinitions.ts` — 20 missing flags added, the two `*_BLOCK_THRESHOLD` env-only knobs moved out of the flag tables, category/total counts and the Live WS port corrected, guarded by a static test (#12552 — thanks @pacocartones) +- **fix(i18n):** backfill missing `usage.kiloPass*` strings in 39 locales and restore `featureFlagOmnirouteDisableThinkingLevelVariantsDescription` in `pt.json` ([#12561](https://github.com/diegosouzapw/OmniRoute/issues/12561)) ([#13027](https://github.com/diegosouzapw/OmniRoute/pull/13027)) — thanks @HouMinXi +- fix(docker): default docker-compose app ports (dashboard/API/live-WS) to loopback instead of `0.0.0.0`, closing the anonymous `/v1` LAN/WAN exposure gap left open by `REQUIRE_API_KEY=false` (#12568) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): close DNS-rebinding SSRF gap in webhook outbound-URL guard (#12569) ([#13243](https://github.com/diegosouzapw/OmniRoute/pull/13243)) +- fix(docker): require a per-session token on the VNC browser CDP bridge and isolate it on a dedicated Docker network (#12571) ([#13241](https://github.com/diegosouzapw/OmniRoute/pull/13241)) +- fix(open-sse): write Adobe Firefly session tokens and cookie jars with 0700/0600 permissions instead of the process umask (#12572) ([#13242](https://github.com/diegosouzapw/OmniRoute/pull/13242)) +- fix(api): remove hardcoded wildcard CORS in openai-to-gemini-sse.ts so the centralized fail-closed CORS gate is the sole source of `Access-Control-Allow-Origin` (#12573) ([#13239](https://github.com/diegosouzapw/OmniRoute/pull/13239)) +- fix(api): enforce API key policy (budget/rate-limit/schedule/endpoint scoping) on the ElevenLabs speech-to-text, text-to-speech and voices proxy routes (#12574) ([#13244](https://github.com/diegosouzapw/OmniRoute/pull/13244)) +- fix(sse): cap HuggingChat NDJSON body size and bound the read loop with the fetch timeout so a stalled or hostile upstream cannot buffer unbounded memory (#12577) ([#13282](https://github.com/diegosouzapw/OmniRoute/pull/13282)) +- fix(docker): scope the cliproxyapi/qdrant/bifrost sidecars to loopback by default and forward `CLIPROXYAPI_MANAGEMENT_KEY` into the cliproxyapi container so its management API is not left both unauthenticated and LAN-published (#12578) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): create DB export temp paths with `fs.mkdtempSync` instead of predictable timestamps (#12579) ([#13237](https://github.com/diegosouzapw/OmniRoute/pull/13237)) +- Cline 401 bodies that say "re-authenticate your Cline account" classify as a refreshable OAuth token, not a terminal expired key. The cooling panel no longer labels every cooldown as a 429; it shows the recorded last error instead. (#12594) ([#13060](https://github.com/diegosouzapw/OmniRoute/pull/13060)) — thanks @HouMinXi +- **fix(catalog):** degrade unknown combo targets instead of dropping LCD modalities, and persist OpenRouter `architecture.input_modalities` into the capability snapshot ([#12613](https://github.com/diegosouzapw/OmniRoute/issues/12613)) ([#13042](https://github.com/diegosouzapw/OmniRoute/pull/13042)) — thanks @HouMinXi +- fix(providers): send `x-api-key` instead of `Authorization: Bearer` for OpenCode Zen's `/v1/responses` endpoint (Muse Spark Contributor models), fixing a 401 on OmniRoute's auth header (#12633) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(sse): surface the actionable "Auggie CLI not found" message when the shell reports a missing `auggie` binary via exit code instead of a spawn error (#12645) ([#13263](https://github.com/diegosouzapw/OmniRoute/pull/13263)) +- **fix(config):** Persist deletions of built-in background-degradation entries — when a stored settings record exists its `degradationMap` is now authoritative instead of being merged under the defaults, so an entry the user removed in the dashboard no longer reappears on the next apply or restart ([#12424](https://github.com/diegosouzapw/OmniRoute/issues/12424)) ([#12647](https://github.com/diegosouzapw/OmniRoute/pull/12647)) — thanks @pacocartones +- **fix(gamification):** action-count badge milestones (First Token, Token Consumer, Token Machine, Token Whale, and the token-sharing tier) are now backed by a durable `xp_action_counts` counter incremented in `addXp()`, instead of a live `COUNT(*)` over `xp_audit_log`. The audit log is pruned by `retention.xpAuditLog` (default 30 days), so on a default install those "lifetime" milestones were really "actions in the last 30 days" and unlocked badges could stop unlocking once old rows aged out. `getActionCount()` and `checkActionCountBadges()` now read the same durable source, and a migration backfills existing totals from the surviving audit rows ([#12546](https://github.com/diegosouzapw/OmniRoute/issues/12546)) ([#12651](https://github.com/diegosouzapw/OmniRoute/pull/12651)) — thanks @pacocartones +- **fix(images):** `/v1/images/edits` now iterates a combo's targets the same way `/v1/images/generations` does (#9239) instead of flattening a bare combo to its first target. A combo whose first target is not edit-capable — or lacks credentials — now falls through to a later edit-capable target rather than hard-erroring, and missing credentials are skipped (not a hard `401`) to match the generations path. The per-target skip/terminal classification is extracted into a shared `runImageComboTargets` loop, so generations behavior is unchanged ([#12547](https://github.com/diegosouzapw/OmniRoute/issues/12547)). ([#12653](https://github.com/diegosouzapw/OmniRoute/pull/12653)) — thanks @pacocartones +- fix(sse): add first-byte watchdog to the TLS-fingerprint transport so a stalled wreq body falls back instead of hanging for minutes (#12656) ([#13272](https://github.com/diegosouzapw/OmniRoute/pull/13272)) +- fix(sse): exempt tiny-budget reasoning probes from combo quality failure and surface persisted-cooldown skips in ALL_TARGETS_SKIPPED diagnostics (#12659) ([#13269](https://github.com/diegosouzapw/OmniRoute/pull/13269)) +- fix(models): declare the real ~1M contextLength for OpenCode Zen's Muse Spark 1.2 models instead of falling back to the 200000 provider default (#12681) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(oauth): align codebuddy-cn OAuth User-Agent with the chat/usage CLI version to avoid WAF false positives (#12702) ([#13264](https://github.com/diegosouzapw/OmniRoute/pull/13264)) +- fix(dashboard): surface an authentication-required banner instead of silently blanking database settings for a guest session (#12709) ([#13270](https://github.com/diegosouzapw/OmniRoute/pull/13270)) +- **fix(resilience):** stop unbounded queue that hangs 6min until Aborted — gate, provider slot, and Bottleneck queue now share a per-connection `maxWaitMs` budget; `fail-closed` on exhaust (503); execution backstop `executionMaxWaitMs` overridable per connection with upstream clamp ([#12715](https://github.com/diegosouzapw/OmniRoute/pull/12715)) — thanks @maxmad64bis (with thanks to @Tushar49 for surfacing the slow-provider need in #12635) +- **fix(auto-combo):** every mode pack now carries `quality` and `reliability` weights (`quality` 0.02, 0.03 in `quality-first`; `reliability` 0.03, 0.04 in `reliability-first`), so selecting a pack no longer silences either signal; `DEFAULT_WEIGHTS` is unchanged ([#12731](https://github.com/diegosouzapw/OmniRoute/pull/12731)) — thanks @maxmad64bis +- **fix(sse):** the provider execution pipeline keeps the upstream error `code`/`type` again — both were lost when the non-streaming leg was extracted, so a config-class `422` (Antigravity missing project) degraded into a generic account cooldown instead of reaching the gate that recognises it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(db):** a legacy `call_logs` table no longer aborts startup — the provider-stats index is created after the column healing runs, not before it, so an install predating `request_type` opens instead of failing with `no such column` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(catalog):** a malformed row in the operator-writable custom-models blob no longer kills every `auto/*` pool ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(tests):** realign the guards that the same day's merges left asserting the old behavior — the provider-count total after GreenPT and EURouter, the GLM stream buffer slot that `#12925` turned into a declared parameter, and the injection scan bound that `#13104` reshaped from a head-only window into head-plus-tail ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- fix(cache): fold tool_choice/tools/response_format into the semantic cache signature so a cached tool_calls response can no longer be replayed for a request whose tool policy forbids it (#12734) ([#13267](https://github.com/diegosouzapw/OmniRoute/pull/13267)) +- **fix(models):** stop treating provider-supplied `isFree:true`/`:free`/`0/0` flags as trusted unless the provider has a documented free tier — only the shipped free-tier catalog decides otherwise; fetched `isFree:true` trusted only for a free-tier provider, custom `isFree:true` via a trusted path ([#12744](https://github.com/diegosouzapw/OmniRoute/pull/12744)) — thanks @maxmad64bis +- fix(memory): authenticate the internal /v1/rerank loopback call so memory reranking no longer silently degrades to unranked order when REQUIRE_API_KEY=true (#12745) ([#13268](https://github.com/diegosouzapw/OmniRoute/pull/13268)) +- fix(sse): parse Ollama Cloud's current usage markup (`$X of $Y used` aria-label, nested width style) (#12749) ([#13260](https://github.com/diegosouzapw/OmniRoute/pull/13260)) +- fix(cli): setup-opencode no longer sends an active context's management token to `/v1/models` when `--api-key`/`OMNIROUTE_API_KEY` is supplied — an explicit flag or the env var now always outranks the context's token, and the flag itself is no longer swallowed by the parent program's global `--api-key` option (#12783) ([#13246](https://github.com/diegosouzapw/OmniRoute/pull/13246)) +- fix(sse): register Arcee AI in the executor provider registry so requests reach api.arcee.ai instead of silently falling back to OpenAI (#12784) ([#13277](https://github.com/diegosouzapw/OmniRoute/pull/13277)) +- **fix(quota):** share one upstream quota read when identical saturation checks arrive at the same time, so traffic bursts don't multiply provider API calls ([#12787](https://github.com/diegosouzapw/OmniRoute/pull/12787)) — thanks @maxmad64bis +- **fix(db):** ignore expired or invalid rate-limit cooldown writes so a stale timestamp can't lock a connection that should be usable — clearing still works as before ([#12788](https://github.com/diegosouzapw/OmniRoute/pull/12788)) — thanks @maxmad64bis +- **fix(providers):** single source for provider order with `xao` ranking alongside `xai-oauth` ([#12790](https://github.com/diegosouzapw/OmniRoute/pull/12790)) — thanks @maxmad64bis +- **fix(routing):** off-table models check the free-model catalog before inheriting premium prices, latency bootstraps from the observed pool median, async tiers read live database pricing with a 90-day freshness gate, and the tier cache invalidates on every pricing write ([#12792](https://github.com/diegosouzapw/OmniRoute/pull/12792)) — thanks @maxmad64bis +- **fix(auto-combo):** snapshot scoring uses the observed per-connection account tier and per-model quality instead of neutral constants, and circuit-open providers rank lower at build time ([#12794](https://github.com/diegosouzapw/OmniRoute/pull/12794)) — thanks @maxmad64bis +- **fix(pool):** empty `auto/*` pools now say why they are empty, and the models listing uses the same paid check as routing ([#12795](https://github.com/diegosouzapw/OmniRoute/pull/12795)) — thanks @maxmad64bis +- fix(routing): recognize CLIProxyAPI's 'unknown provider for model' 400 as fallback-worthy (#12800) ([#13284](https://github.com/diegosouzapw/OmniRoute/pull/13284)) +- **fix(sse):** translate-mode streams now emit the estimated token counts as a trailing usage-only chunk before `[DONE]` when the upstream stays silent, so metered chat clients see totals instead of nothing ([#12828](https://github.com/diegosouzapw/OmniRoute/pull/12828)) — thanks @maxmad64bis +- **fix(db):** provider stats stay truthful on empty and legacy databases: fallback counts default to `0` instead of `null`, latency averages read `null` (not `0`) when no durations were recorded, failures logged before the error-type column existed group under `pre_migration` instead of `unclassified`, and per-provider queries use two new composite indexes ([#12832](https://github.com/diegosouzapw/OmniRoute/pull/12832)) — thanks @maxmad64bis +- fix(nvidia): fail open when a synced model catalog goes stale instead of gating forever (#12849) ([#13248](https://github.com/diegosouzapw/OmniRoute/pull/13248)) +- **fix(db):** WAL maintenance lives in its own module: a `TRUNCATE` checkpoint that hits a busy database now warns with its streak and retries once via `PASSIVE` instead of logging success, and closing-time checkpoints no longer report success on builds without a database file, with the busy totals visible in the authenticated monitoring health payload ([#12853](https://github.com/diegosouzapw/OmniRoute/pull/12853)) — thanks @maxmad64bis +- **fix(health):** quota with no snapshots reads empty instead of a contradicting 0% ([#12857](https://github.com/diegosouzapw/OmniRoute/pull/12857)) — thanks @maxmad64bis +- fix(a2a): accept the dashboard's own session cookie on /a2a so "Run message/send" no longer fails with "Unauthorized: missing or invalid API key" (#12888) ([#13271](https://github.com/diegosouzapw/OmniRoute/pull/13271)) +- **fix(logs):** the Logs grid's in-memory filter pass no longer discards rows the SQL query already matched — selecting an API key from the dropdown (which sends the key's id) returns its calls again, the Combo tab shows every combo instead of only those whose name contains a "1", and the model filter and search cover the same columns as the query ([#12896](https://github.com/diegosouzapw/OmniRoute/pull/12896)) — fixes [#12873](https://github.com/diegosouzapw/OmniRoute/issues/12873) — thanks @ntdatt812 +- **fix(a2a):** `/api/a2a/status` now builds the agent card from the request that asked for it, so a gateway reached at a non-localhost host no longer advertises `http://localhost:20128` as its A2A URL ([#12918](https://github.com/diegosouzapw/OmniRoute/pull/12918)). — thanks @ntdatt812 +- **fix(compression):** progressive aging now appends its `[COMPRESSED:aging:…]` annotation after a turn's `tool_result` blocks instead of in front of them, so Anthropic no longer rejects aged conversations with "`tool_use` ids were found without `tool_result` blocks immediately after" ([#12920](https://github.com/diegosouzapw/OmniRoute/pull/12920)). — thanks @ntdatt812 +- **fix(bedrock):** model import now resolves context limits for every vendor prefix instead of only `anthropic.*`, so `global.openai.gpt-5.6-*` no longer imports with a null `inputTokenLimit` and gets rejected pre-flight at the 200k default ([#12921](https://github.com/diegosouzapw/OmniRoute/pull/12921)). — thanks @ntdatt812 +- **fix(stream):** the 64 KB stream buffer GLM asks for is honoured instead of dropped, and the type error it caused no longer fails the API Route Typecheck gate on every open PR ([#12925](https://github.com/diegosouzapw/OmniRoute/pull/12925)) — thanks @ntdatt812 +- **fix(guardrails):** mask PII inside a `tool_result`'s nested content array, which the masker walked past while redacting its sibling block ([#12930](https://github.com/diegosouzapw/OmniRoute/pull/12930)) — thanks @ntdatt812 +- **fix(dashboard):** the Radar catalog table no longer leaves absent data unexplained — empty limits, unknown context windows, and unreported capabilities each explain themselves on hover, and a new check keeps it that way ([#12937](https://github.com/diegosouzapw/OmniRoute/pull/12937)) — thanks @maxmad64bis +- **fix(sse):** geo-blocked opencode requests rotate to the next account proxy instead of failing, so one refused egress no longer aborts the whole chain ([#12941](https://github.com/diegosouzapw/OmniRoute/pull/12941)) — thanks @maxmad64bis +- **fix(codex):** dashboard "clear cooldown" and the CAS recovery path now drop nested `codexScopeRateLimitedUntil` maps in the same write that nulls `rate_limited_until`, and fresh quota snapshots with headroom lift fallback-sourced scope cooldowns parked by quota preflight ([#12817](https://github.com/diegosouzapw/OmniRoute/issues/12817), [#12860](https://github.com/diegosouzapw/OmniRoute/issues/12860), [#12951](https://github.com/diegosouzapw/OmniRoute/pull/12951)) The dashboard reset-credit button (`consumeCodexResetCredit`) now exercises that same snapshot path after a successful redeem, so a filled quota bar is enough to unpark a leftover fallback Codex child without another manual clear. — thanks @HouMinXi +- **fix(db):** resolve `sql-wasm.wasm` across global npm install and hoisted layouts, ensuring OmniRoute can boot cleanly on Node 24 when native `better-sqlite3` is uncompiled. ([#13035](https://github.com/diegosouzapw/OmniRoute/pull/13035)) — thanks @HouMinXi +- **fix(api):** `DELETE /v1/batches/delete-completed` now sweeps only the calling API key's own completed batches (batches with no owner stay out of a key-scoped sweep on purpose), with an explicit instance-wide mode reserved for authenticated dashboard sessions, a 401 for a presented key that is unknown, revoked, deactivated, banned or expired (never falling through to the session branch), audit logging of every sweep, a sanitized 500 on failure, an owner-scoped file half (a key-scoped sweep never nulls a file another tenant owns) and an atomic sweep — chunked in 200-batch transactions in instance mode — so a mid-way error never leaves a batch pointing at a nulled file (GHSA-wvxc-jp3v-5mg5) ([#12969](https://github.com/diegosouzapw/OmniRoute/pull/12969)) +- **fix(combo):** quota-weighted routing stops drawing on an out-of-credit connection — a 402 now invalidates the stored quota snapshot instead of leaving its stale remaining percentage in place, and a snapshot older than 10 minutes no longer counts as confident headroom for the primary pool ([#12972](https://github.com/diegosouzapw/OmniRoute/pull/12972)) ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- **fix(vision):** Custom Models with "Vision capable" checked no longer have image requests swapped to `glm/glm-4.6v` when the client sends the advertised alias (`vllm/path/...`) or the bare path-shaped id — Vision Bridge now matches the stored override for all three id forms ([#12758](https://github.com/diegosouzapw/OmniRoute/issues/12758)) ([#12974](https://github.com/diegosouzapw/OmniRoute/pull/12974)) — thanks @HouMinXi +- **fix(sse):** transient opencode upstream failures rotate to the next account proxy instead of failing, so one flapping egress no longer aborts the whole chain ([#12975](https://github.com/diegosouzapw/OmniRoute/pull/12975)) — thanks @maxmad64bis +- **fix(azure):** Deployments from GPT-6 onward now send `max_completion_tokens` instead of `max_tokens`, which Azure rejects with HTTP 400. The rule matched a literal `gpt-5`, so each new generation arrived broken; it now matches the generation range, while `gpt-35-turbo` still keeps `max_tokens`. ([#13007](https://github.com/diegosouzapw/OmniRoute/pull/13007)) — thanks @ntdatt812 +- **fix(db):** add an opt-in self-restart circuit for sustained critical memory pressure, gate post-cleanup VACUUM behind a minimum freed-rows threshold, and checkpoint the SQLite WAL every 5 minutes with a size guard that escalates to TRUNCATE, so a growing WAL can no longer stall the event loop into a full outage. ([#13011](https://github.com/diegosouzapw/OmniRoute/pull/13011)) — thanks @HouMinXi +- **fix(auth):** an explicit connection pin may probe a quota-disabled row once and re-enable it on success ([#12874](https://github.com/diegosouzapw/OmniRoute/issues/12874)) ([#13017](https://github.com/diegosouzapw/OmniRoute/pull/13017)) — thanks @HouMinXi +- **fix(api):** thread `X-OmniRoute-Fallback-Attempts` through combo chat completions so streaming and non-streaming responses report how many prior legs were attempted ([#13038](https://github.com/diegosouzapw/OmniRoute/pull/13038)) — thanks @HouMinXi +- Restore provider failure classification and credential refresh on non-streaming requests: classify non-2xx failures to lock models on per-model quota exhaustion, update connection rate limits from headers and body, and pass credential refresh handlers to pipeline execution so 401 tokens can be refreshed and retried (#13043). ([#13069](https://github.com/diegosouzapw/OmniRoute/pull/13069)) — thanks @HouMinXi +- **fix(responses):** wrap forced-non-streaming web_search fallback JSON as Responses SSE so Codex still sees `response.completed` ([#13050](https://github.com/diegosouzapw/OmniRoute/pull/13050)) — thanks @HouMinXi +- **fix(antigravity):** Preserve upstream thought-token usage in normalized completion and reasoning token counts ([#13055](https://github.com/diegosouzapw/OmniRoute/pull/13055)) — thanks @pacocartones +- Fix a concurrency-slot leak in the DeepSeek PoW solver: a worker that failed to spawn (for example a missing worker script) never released its slot, so `MAX_CONCURRENT_WORKERS` failures disabled the solver until restart. ([#13097](https://github.com/diegosouzapw/OmniRoute/pull/13097)) — thanks @anhtahaylove +- **fix(acp):** bound the ACP session output buffers — `stdoutBuffer` and `stderrBuffer` now cap at 1 MiB keeping the most recent output behind a visible `[...output truncated...]` marker, and `stderrBuffer` is reset per prompt instead of accumulating for the lifetime of the session. ([#13100](https://github.com/diegosouzapw/OmniRoute/pull/13100)) — thanks @anhtahaylove +- **fix(acp):** release the `stdout`/`exit` listeners and the idle timer that a `sendPrompt` timeout used to leave attached to the `acpManager` singleton, and drop sessions that exited on their own from the session map instead of keeping them forever. ([#13096](https://github.com/diegosouzapw/OmniRoute/pull/13096)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection and PII scanners now read the text a `tool_result` block carries on `content` (string or nested block list), in messages and in system blocks, so tool output is judged by the same rules as user text ([#13101](https://github.com/diegosouzapw/OmniRoute/pull/13101)) — thanks @ntdatt812 +- **fix(gamification):** close the badge notification SSE stream when the request signal is already aborted before the stream starts — a client that disconnects while the route is still awaiting auth used to leave both the 2s unlock poll and the 15s heartbeat running for the lifetime of the process. ([#13106](https://github.com/diegosouzapw/OmniRoute/pull/13106)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection scan now spends its 16 KB budget on both ends of the request instead of the first 16 KB only, so `system`, `instructions`, `query`, `documents` and the newest turns are no longer hidden behind one long message ([#13104](https://github.com/diegosouzapw/OmniRoute/pull/13104)) — thanks @ntdatt812 +- **fix(dashboard):** expose the Volcano Ark console cookie on quota scraping and unwrap connect-error objects so the dashboard shows the upstream message ([#13107](https://github.com/diegosouzapw/OmniRoute/pull/13107)) — thanks @HouMinXi +- **fix(db):** release the `beforeExit`/`SIGINT`/`SIGTERM` handlers when a `node:sqlite` adapter closes, so a closed adapter and its database handle are no longer pinned to `process` for the lifetime of the run — the same treatment #7494 gave the sql.js adapter. ([#13109](https://github.com/diegosouzapw/OmniRoute/pull/13109)) — thanks @anhtahaylove +- **fix(translator):** `contentSchema` and `unevaluatedItems` are now treated as subschema positions by the tool-schema sanitizer, so a truncation placeholder in either is replaced with a permissive schema instead of being forwarded as a string ([#13110](https://github.com/diegosouzapw/OmniRoute/pull/13110)) — thanks @ntdatt812 +- **fix(cli-helper):** clear the `createLogStream` timeout on the abort path — `stop()` aborts the in-flight fetch and returned through the `signal.aborted` branch, which skipped `clearTimeout` and left an armed timer per stopped stream. The stream reader is now also cancelled when the read loop exits early. ([#13114](https://github.com/diegosouzapw/OmniRoute/pull/13114)) — thanks @anhtahaylove +- **fix(docs):** drop TLS-impersonation, MITM-decrypt, supply-chain attestation, and XOR-mask writeups from the public `/docs` catalog and Docker image. Files stay in git for engineers; operators who need them open the repo, not the website. ([#13136](https://github.com/diegosouzapw/OmniRoute/pull/13136)) — thanks @HouMinXi +- **fix(combo):** parse numeric-epoch `rate_limited_until` in the combo cooldown read path ([#13141](https://github.com/diegosouzapw/OmniRoute/pull/13141)) — thanks @maxmad64bis +- **fix(opencode):** both OpenCode plugins now reject a gateway address typed without `http://` at configuration time, instead of publishing every model with an api url no client can call, and the v2 plugin no longer publishes a model card whose api url is blank or relative ([#13142](https://github.com/diegosouzapw/OmniRoute/pull/13142)) — thanks @maxmad64bis +- **fix(providers):** lock opencode model on upstream 400 model-unavailable ([#13146](https://github.com/diegosouzapw/OmniRoute/pull/13146)) — thanks @maxmad64bis +- **fix(logging):** keep the provider exchange rather than the raw client bodies when a call log exceeds its size budget, and show that recovered payload in the request-detail panel instead of replacing it with the stored response body ([#13147](https://github.com/diegosouzapw/OmniRoute/pull/13147)) — thanks @maxmad64bis +- **fix(traffic-inspector):** the WebSocket route no longer leaks a traffic-buffer subscriber and a 30s ping timer when the client socket is already closed at handler time — listeners are attached before any resource is acquired, a destroyed socket bails out early, and the ping interval stops on a dead socket where `write()` never throws ([#13155](https://github.com/diegosouzapw/OmniRoute/pull/13155)) — thanks @anhtahaylove +- **fix(telegram):** bound the per-user API key cache in the Telegram chat proxy so a burst of distinct chat ids can no longer grow the process heap without limit ([#13165](https://github.com/diegosouzapw/OmniRoute/issues/13165)) ([#13166](https://github.com/diegosouzapw/OmniRoute/pull/13166)) — thanks @anhtahaylove +- **fix(stream):** cancel the upstream response body when the JSON-to-SSE sniff unwinds on a body timeout, so a stalled upstream no longer pins the connection ([#13169](https://github.com/diegosouzapw/OmniRoute/issues/13169)) ([#13171](https://github.com/diegosouzapw/OmniRoute/pull/13171)) — thanks @anhtahaylove +- **fix(telegram):** authenticate webhook deliveries with Telegram's `secret_token` so an unauthenticated caller can no longer mint API keys or spend upstream quota ([#13172](https://github.com/diegosouzapw/OmniRoute/issues/13172)) ([#13175](https://github.com/diegosouzapw/OmniRoute/pull/13175)) — thanks @anhtahaylove +- **fix(models):** give discoverable Gemini 3.8 Flash ids their own 65536 output spec so Antigravity no longer clamps them to 16384 ([#13195](https://github.com/diegosouzapw/OmniRoute/pull/13195)) — thanks @HouMinXi +- **fix(dashboard):** leftover catalog-removed provider rows (gemini-cli) can be listed and purged from the providers page ([#13067](https://github.com/diegosouzapw/OmniRoute/issues/13067)) ([#13197](https://github.com/diegosouzapw/OmniRoute/pull/13197)) — thanks @HouMinXi +- **fix(auth):** a dashboard session now requires the `authenticated: true` claim that login, OIDC and the session refresh already emit — a JWT merely signed with `JWT_SECRET` (for example the Cursor CLI passthrough token, which any API-key holder can obtain) no longer verifies as the `auth_token` cookie on any route, the WebSocket handshake or the live server; existing sessions keep working ([#13298](https://github.com/diegosouzapw/OmniRoute/issues/13298)) ([#13375](https://github.com/diegosouzapw/OmniRoute/pull/13375)) +- **fix(api):** `DELETE /v1/batches/delete-completed` now applies the caller's API-key policy (endpoint allowlist, schedule, usage cap, rate limit) like every other `/v1` route, commits the key-scoped sweep per 200-batch chunk instead of holding one write lock for the whole sweep, refuses to loop without progress, and rejects a scope that names both a key and `allTenants` ([#13684](https://github.com/diegosouzapw/OmniRoute/pull/13684)) +- Restore the API-route typecheck gate: the non-streaming leg result lost its discriminated-union narrowing after the server-owned tool loop reassignment, producing 13 new TS2339 diagnostics in `chatCore.ts`. ([#12963](https://github.com/diegosouzapw/OmniRoute/pull/12963)) +- Restore four non-streaming behaviours the server-owned tool loop refactor dropped: client aborts map to 499 with the fixed `Request aborted` message, an aborted request no longer logs a synthetic `clientResponse`, Claude prompt-cache telemetry is recorded again, and a body that cannot be canonicalized no longer throws when the tool loop is off. ([#12990](https://github.com/diegosouzapw/OmniRoute/pull/12990)) +- **fix(skills):** The CLI registry parser now reads positionals declared with `.addArgument()`, not only those written inline in `.command()`. `tunnel create [type]` was being published as `tunnel create`, so the agent-skills sync gate reported drift on every branch and regenerating would have deleted the argument. ([#13009](https://github.com/diegosouzapw/OmniRoute/pull/13009)) — thanks @ntdatt812 +- **fix(combos):** dashboard combo test uses a short prompt, serial probes, and a 60s timeout so reasoning models and rate-limited free pools do not fail the health check ([#13001](https://github.com/diegosouzapw/OmniRoute/pull/13001)) — thanks @HouMinXi +- fix(compression): terminate idle worker threads on eviction so long-running instances stop leaking OS threads and MessagePorts ([#13091](https://github.com/diegosouzapw/OmniRoute/pull/13091)) — thanks @anhtahaylove +- Redact Google API keys of any length in error bodies: the pattern required exactly 39 characters, so shorter or longer `AIza…` credentials passed through unredacted. ([#12964](https://github.com/diegosouzapw/OmniRoute/pull/12964)) +- **fix(grok-cli):** a 402 "Grok Build usage balance exhausted" parks that Grok login as out of credit (Grok Build CLI, grok.com cookie, and xAI OAuth share the weekly pool). Combo routing then tries the next login instead of locking the model for every account in the pool ([#13061](https://github.com/diegosouzapw/OmniRoute/pull/13061)) — thanks @HouMinXi +- fix(compression): spawn the LLMLingua worker with a file URL object so compression actually runs instead of silently failing open on Node ([#13093](https://github.com/diegosouzapw/OmniRoute/pull/13093)) — thanks @anhtahaylove +- **fix(test):** run the local `test` and `test:unit` scripts at concurrency 4 so a full-suite run no longer exhausts the machine's commit charge and kills unrelated processes ([#13187](https://github.com/diegosouzapw/OmniRoute/pull/13187)) — thanks @anhtahaylove +- **fix(monitoring):** `GET /api/monitoring/health` `credentialHealth` now includes a bounded `failedConnections` list (`connectionId`, `status`, sanitized `lastError`) when the probe-cache gauge `failed>0`, plus `source: probe-cache` and a cheap `staleDbNonOkCount` for sticky SQLite `test_status` on active rows. Documents that the live gauge is not `provider_connections.test_status`. ([#12876](https://github.com/diegosouzapw/OmniRoute/pull/12876)) — thanks @RaviTharuma +- **fix(resilience):** an openai-compatible multi-upstream gateway no longer marks the whole connection `credits_exhausted` when a single upstream (or the `/models` / representative-model health probe) returns 402. Quota failures stay model-scoped; true connection-wide auth failures are unchanged. Single-credential 402 key disable ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239)) is preserved. ([#12875](https://github.com/diegosouzapw/OmniRoute/pull/12875)) — thanks @RaviTharuma +- **fix(dashboard):** nine fixes on the Orchestration Canvas, all diagnosed in the Phase 2 reviews. A source that fails now keeps the timestamp of its FIRST failure instead of being re-stamped every poll — the stale line said "since the last poll" no matter how long the source had been down, and the churn also defeated the snapshot's stable identity (it serializes the source list), so the canvas re-rendered on every tick while anything was broken. A source that HAD data and only then started failing is flagged too: previously only a source with no node at all got the warning, so a source that went down mid-session kept a healthy-looking node forever. The rest are pointwise: clicking a filter chip cancels the pending search debounce (left armed, it fired ~300ms later and silently reverted the chip); the search input carries an accessible name; `?state=running, failed` parses like the unpadded form instead of dropping the padded value; the CSV toggle helper is defined once in `model/urlParams.ts` rather than twice; the agents tab tells "nothing running" apart from "the filter matched nothing", offering a clear-filters button instead of setup links that would be wrong advice there; edges stop emitting SMIL particles above 40 simultaneously active edges, keeping the colored stroke; and the drawer's error banner clears when a retried action succeeds. ([#12983](https://github.com/diegosouzapw/OmniRoute/pull/12983)) +- **fix(i18n):** the nine locales added with the EU-language batch (Greek, Estonian, Irish, Croatian, Lithuanian, Latvian, Maltese, Slovenian, Serbian) were missing the eleven Orchestration Canvas keys that Phase 3 introduced, so the compare-runs panel and the "no runs match these filters" empty state fell back to English in those languages (`deepMergeFallback` substitutes English for an absent key, so nothing rendered blank — it rendered untranslated). The coverage gate does not catch this: it enforces an 80% floor per locale, and eleven missing keys out of ~13,000 leaves coverage at 99.9%. Translated for real in each language, calibrated against the wording each file already uses for "run", "filter" and "skill". ([#13294](https://github.com/diegosouzapw/OmniRoute/pull/13294)) +- **fix(dashboard):** five follow-ups from the Phase 2 Orchestration Canvas review. The History drawer now shows the memory section for runs that already left the live TTL window: the persisted `memory_hits` event is parsed into `metadata.memoryHits` with the same defensive validation the drawer applies, and — because that event is observability rather than a state transition — it no longer leaks into the timeline, where it had been inheriting the task's state and rendering as a duplicate transition. The A2A memory recall runs against its own 1.5s deadline instead of inheriting the memory backend's 30s one; overshooting degrades exactly like any other recall failure (no hits, task proceeds), and the timer is cleared on both paths. Repeating a Conductor run carries its `requirements.cli`/`requirements.model` forward, so the new run is pinned to the same runner profile and model rather than drifting to whatever the fleet picks. A successful repeat from the Agents tab now focuses the run it created, instead of leaving the operator on the finished one. And the auth test for `POST /api/conductor/tasks` moved into the shared `ROUTES` array rather than restating the pattern. ([#12988](https://github.com/diegosouzapw/OmniRoute/pull/12988)) +- fix(plugins): stop leaking an exit listener per plugin hook timeout, which triggered MaxListenersExceededWarning on plugins that ignore SIGTERM ([#13092](https://github.com/diegosouzapw/OmniRoute/pull/13092)) — thanks @anhtahaylove +- **fix(validation):** Provider node edits no longer fail with a generic "Invalid request" when the optional daily-quota reset fields are left blank. The dashboard sends `dailyQuotaResetTimezone` and `dailyQuotaResetHour` as `null`, and only the hour accepted it. ([#13066](https://github.com/diegosouzapw/OmniRoute/issues/13066)) ([#13083](https://github.com/diegosouzapw/OmniRoute/pull/13083)) — thanks @ntdatt812 +- **fix(combo):** quota-aware expansion drops banned, inactive, missing, and wrong-provider connections before quota fetch or model dispatch; pins and allowlists stay selectors, not a bypass. Antigravity automatic exhaustion now requires a reported zero remaining, so a positive balance below 1% stays eligible. ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- Fix an intermittent failure in the quota-weighted routing test suite: scores are a function of `Date.now()`, so peers with identical quota scored microseconds apart never tied and swapped order. ([#12946](https://github.com/diegosouzapw/OmniRoute/pull/12946)) +- fix(volcengine): resolve quota window mapping gaps, safe multi-connection console binding, and response cookie sanitization ([#12950](https://github.com/diegosouzapw/OmniRoute/pull/12950)) — thanks @HouMinXi +- **fix(test):** resolve the WebDAV handler path with `fileURLToPath` so the suite's 37 WebDAV tests run on Windows instead of failing with a doubled `C:\C:\` drive prefix ([#13196](https://github.com/diegosouzapw/OmniRoute/pull/13196)) — thanks @anhtahaylove +- **fix(ui):** fall back from invalid AnySearch icon alias (direct commit `9587e07b69`) +- **fix(sse):** stop rebuilding a truncated summary from the collector's cap-dropped event array ([#12718](https://github.com/diegosouzapw/OmniRoute/pull/12718)) — thanks @hartmark +- **fix(conversations):** show a pending spinner for unresolved tool nodes instead of "(empty)" ([#12727](https://github.com/diegosouzapw/OmniRoute/pull/12727)) — thanks @hartmark +- **fix(streaming):** fail fast when an upstream stream produces only lifecycle/heartbeat events, never real content ([#12741](https://github.com/diegosouzapw/OmniRoute/pull/12741)) — thanks @hartmark +- **fix(responses-continuation):** bridge the write-in-flight window with an in-memory pending store ([#12854](https://github.com/diegosouzapw/OmniRoute/pull/12854)) — thanks @hartmark +- **fix(translator):** drop or map agent_message on Chat Completions fallback ([#12880](https://github.com/diegosouzapw/OmniRoute/pull/12880)) — thanks @RaviTharuma +- **fix(api):** page and stream-complete GET /v1/models for large catalogs ([#12882](https://github.com/diegosouzapw/OmniRoute/pull/12882)) — thanks @RaviTharuma +- **fix(quota):** align AUTH window labels with usage API and clarify 503 ([#12884](https://github.com/diegosouzapw/OmniRoute/pull/12884)) — thanks @RaviTharuma +- **fix(ci):** clear two base-reds on release/v3.8.51 — mutation-coverage gate + image-only-model guard ([#12945](https://github.com/diegosouzapw/OmniRoute/pull/12945)) +- **fix(api):** scope batch bulk-delete to the calling API key ([#13211](https://github.com/diegosouzapw/OmniRoute/pull/13211)) +- **fix(tests):** align model-sync log assertion to shipped path redaction (base-red) ([#13227](https://github.com/diegosouzapw/OmniRoute/pull/13227)) +- **fix(test):** make the ToS heading guard actually require the parentheses ([#13228](https://github.com/diegosouzapw/OmniRoute/pull/13228)) +- **fix(quality):** register 4 drifted covering tests in stryker tap.testFiles ([#13229](https://github.com/diegosouzapw/OmniRoute/pull/13229)) +- **fix(logging):** mask *-api-key headers in request-log pipeline (#13273) ([#13401](https://github.com/diegosouzapw/OmniRoute/pull/13401)) — thanks @KooshaPari +- **fix(tests):** correct stale Jina catalog prefix in custom-models test ([#13403](https://github.com/diegosouzapw/OmniRoute/pull/13403)) — thanks @KooshaPari +- **fix(backend):** prune old backups after health-check-repair snapshots (#13308) ([#13404](https://github.com/diegosouzapw/OmniRoute/pull/13404)) — thanks @KooshaPari +- **fix(backend):** guard memory_fts_au trigger with WHEN clause to stop FTS bloat (#13326) ([#13405](https://github.com/diegosouzapw/OmniRoute/pull/13405)) — thanks @KooshaPari +- **fix(free-tiers):** remove obsolete Together $25 signup credit (#12526) ([#13407](https://github.com/diegosouzapw/OmniRoute/pull/13407)) — thanks @KooshaPari +- **fix:** skip non-chat generation models in Test all models ([#13409](https://github.com/diegosouzapw/OmniRoute/pull/13409)) — thanks @KooshaPari +- **fix(providers):** de-duplicate agy and antigravity model catalogs ([#13410](https://github.com/diegosouzapw/OmniRoute/pull/13410)) — thanks @KooshaPari +- **fix(dashboard):** send provider in body for mini-playground web search ([#13411](https://github.com/diegosouzapw/OmniRoute/pull/13411)) — thanks @KooshaPari +- **fix(resilience):** add CJK quota-exhaustion patterns to 429 classifier ([#13412](https://github.com/diegosouzapw/OmniRoute/pull/13412)) — thanks @KooshaPari +- **fix(backend):** eval runner: failed calls no longer score as passed + dotAll regex ([#13413](https://github.com/diegosouzapw/OmniRoute/pull/13413)) — thanks @KooshaPari +- **fix(backend):** eval runner opts out of output-style and memory injection ([#13414](https://github.com/diegosouzapw/OmniRoute/pull/13414)) — thanks @KooshaPari +- **fix:** chain serial quarantine step in npm test script (fixes #13286) ([#13418](https://github.com/diegosouzapw/OmniRoute/pull/13418)) — thanks @KooshaPari +- **fix(antigravity):** derive stable session ID from credentials for prompt-cache reuse (#13305) ([#13424](https://github.com/diegosouzapw/OmniRoute/pull/13424)) — thanks @KooshaPari +- **fix(memory):** merge memory into top-level system field instead of unshifting at messages[0] (#13425) ([#13427](https://github.com/diegosouzapw/OmniRoute/pull/13427)) — thanks @KooshaPari +- **fix(compression):** preserve instruction blocks from lossy engines ([#13523](https://github.com/diegosouzapw/OmniRoute/pull/13523)) — thanks @KooshaPari +- **fix(compression):** preserve polarity words and newlines in ultra heuristic ([#13524](https://github.com/diegosouzapw/OmniRoute/pull/13524)) — thanks @KooshaPari +- **fix(compression):** run user preservePatterns before built-in patterns (#13457) ([#13525](https://github.com/diegosouzapw/OmniRoute/pull/13525)) — thanks @KooshaPari +- **fix(reasoning):** expand deepseek-v4 regex to match v4.1+ and add command-code provider (#13430) ([#13528](https://github.com/diegosouzapw/OmniRoute/pull/13528)) — thanks @KooshaPari +- **fix(providers):** add alwaysReasons metadata for reka-flash-3 ([#13531](https://github.com/diegosouzapw/OmniRoute/pull/13531)) — thanks @KooshaPari +- **fix(playground):** allow Compare columns to scroll independently ([#13532](https://github.com/diegosouzapw/OmniRoute/pull/13532)) — thanks @KooshaPari +- **fix(security):** empty whitelist allows through to prevent admin lockout ([#13534](https://github.com/diegosouzapw/OmniRoute/pull/13534)) — thanks @KooshaPari +- **fix(tests):** add DISABLE_IOREG_STRATEGY env var for macOS test compatibility (fixes #13467) ([#13539](https://github.com/diegosouzapw/OmniRoute/pull/13539)) — thanks @KooshaPari +- **fix(zed-hosted):** map developer role and Gemini enums for Zed proxy ([#13543](https://github.com/diegosouzapw/OmniRoute/pull/13543)) — thanks @KooshaPari +- **fix(analytics):** normalize provider aliases to canonical id in usage_history ([#13545](https://github.com/diegosouzapw/OmniRoute/pull/13545)) — thanks @KooshaPari +- **fix(combo):** use traceId for call log id to prevent UNIQUE constraint failure ([#13546](https://github.com/diegosouzapw/OmniRoute/pull/13546)) — thanks @KooshaPari +- **fix(auth):** check model lockout before returning synthetic noauth connection ([#13547](https://github.com/diegosouzapw/OmniRoute/pull/13547)) — thanks @KooshaPari +- **fix(guardrails):** skip credential redaction for base64 image data URLs ([#13550](https://github.com/diegosouzapw/OmniRoute/pull/13550)) — thanks @KooshaPari +- **fix(resilience):** increase requestQueue.maxWaitMs default from 15s to 30s ([#13553](https://github.com/diegosouzapw/OmniRoute/pull/13553)) — thanks @KooshaPari +- **fix(docs):** document OMNIROUTE_READY_TIMEOUT_MS and allowlist the test-only DISABLE_IOREG_STRATEGY ([#13692](https://github.com/diegosouzapw/OmniRoute/pull/13692)) + ### 📝 Maintenance ---- +- **docs(readme):** reconcile live v3.8.50 provider, free-tier, CLI, routing, test, community, sponsor, acknowledgment, and SVG metrics with their audited source denominators, including a deduplicated OmniRoute-in-Action snapshot and distinct contributor rankings for merged pull requests, GitHub-attributed commits, and Git history ([#11356](https://github.com/diegosouzapw/OmniRoute/pull/11356)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **docs:** sync the canonical provider count 354 → 356 across `README.md`, `AGENTS.md`, `llm.txt` (+ 42 i18n mirrors), the four README SVG diagrams, `docs/reference/PROVIDER_REFERENCE.md` (regenerated) and the `package.json` description after Opper (#11629) and 1min.ai (#11631) boarded the catalog — closes the `check:docs-counts-sync` strict drifts that kept `release/v3.8.51` red ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11673](https://github.com/diegosouzapw/OmniRoute/pull/11673)) — thanks @jonlwheat2-gif +- **test(providers):** update count-derived assertions after the v3.8.51 provider additions — `APIKEY_PROVIDERS` 233 → 235 (Opper #11629 + 1min.ai #11631), reserved-prefix REGISTRY walk 395 → 398, `WEB_FETCH_PROVIDERS` now includes `nimble-search` (#11620), and the provider translate-path golden snapshot regenerated ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11674](https://github.com/diegosouzapw/OmniRoute/pull/11674)) — thanks @jonlwheat2-gif +- **test(sse):** bump the hard-lease connection-query inventory for `src/lib/tokenHealthCheck.ts` to 2 — the verify-only web-cookie sweep added by #11495 split the single `getProviderConnections` call into oauth + cookie variants, which the frozen inventory had not tracked ([#11495](https://github.com/diegosouzapw/OmniRoute/pull/11495)). ([#11677](https://github.com/diegosouzapw/OmniRoute/pull/11677)) — thanks @jonlwheat2-gif / @oyi77 +- **chore(release):** clear the `release/v3.8.51` base-red gates — docs migration count (+38 i18n mirrors), stryker `tap.testFiles`, lockfile registry host, stale ESLint suppressions, the 7 lint regressions they were hiding, the stale `APIKEY_PROVIDERS` partition count (231 → 233), the `glm-5.3-max` tier missing from the GLM test table, a dead `modelType === "chat"` comparison in the models catalog, and the `resolveLiveWsUrl` / `sanitizeLiveWsPort` helpers from #11388 that a later merge dropped from `wsPath.ts` (dashboard live WebSocket URL resolution) ([#11502](https://github.com/diegosouzapw/OmniRoute/pull/11502)) — thanks @MumuTW +- **chore(free-models):** the free-tier regime table now declares which totals figure each regime feeds and whether it may take the no-auth shortcut, so the sets that used to repeat those answers by hand are derived from it — a new `freeType` no longer compiles until it has answered every question, instead of silently contributing to no total ([#11537](https://github.com/diegosouzapw/OmniRoute/pull/11537)) — thanks @maxmad64bis +- **docs(routing):** LKGP strategy copy now states provider-level stickiness and the explicit rules fallback instead of promising per-target pinning ([#11555](https://github.com/diegosouzapw/OmniRoute/pull/11555)) — thanks @maxmad64bis +- **test(chatcore):** pin the response tool-name alias resolution against the ordering hazard that broke every Gemini/Antigravity MCP tool call in v3.8.49. `extractRequestToolIdentityMap` deletes `translatedBody._toolNameMap`, so the later read is always undefined and the ledger survives only through the `requestToolIdentityMap` fallback — removing that fallback previously left the entire tool-name suite green. The resolution moves into `resolveResponseToolNameMap()` next to the map it depends on, with a regression guard that fails without the recovery. No behaviour change. ([#11575](https://github.com/diegosouzapw/OmniRoute/pull/11575)) — thanks @amartinawi +- **docs(env):** document `OMNIROUTE_CORPUS_CACHE_SIZE` in `.env.example` and `ENVIRONMENT.md`, so the env/docs contract check stops failing Docs Gates on every branch ([#11576](https://github.com/diegosouzapw/OmniRoute/pull/11576)) — thanks @ntdatt812 +- **chore(stryker):** register `route-guard-tunnel-processes-local-only` and `universal-quota-aware-routing` in `tap.testFiles`, so their mutant kills count for the three modules they cover ([#11577](https://github.com/diegosouzapw/OmniRoute/pull/11577)) — thanks @ntdatt812 +- **test(authz):** pin `LOCAL_ONLY_API_GET_EXEMPTIONS` by exact membership instead of by entry count, so the guard names the offending path and also catches a substitution ([#11580](https://github.com/diegosouzapw/OmniRoute/pull/11580)) — thanks @ntdatt812 +- **test(build):** pin `resolveNextBuildBundlerFlag` to the `OMNIROUTE_USE_TURBOPACK` contract it implements, across runtimes, instead of asserting an unimplemented Bun override ([#11583](https://github.com/diegosouzapw/OmniRoute/pull/11583)) — thanks @ntdatt812 +- **test(claude-web):** the Claude Web transport test's barrel anchors no longer pin the exact construction spelling. #11421 made the executor map lazy, so `/"claude-web": new ClaudeWebExecutor\(\)/` stopped matching and the test went red on `release/v3.8.51` itself — a base-red gate every open PR inherited. The anchors now match the alias key and the class name and tolerate either spelling in between, and were checked to still fail when an alias is genuinely repointed at another executor ([#11585](https://github.com/diegosouzapw/OmniRoute/pull/11585)) — thanks @ntdat812 +- **test(models):** pin `qwen3.8-max` resolution per provider — two of the four catalogs now serve the bare id and no longer carry `-preview` ([#11590](https://github.com/diegosouzapw/OmniRoute/pull/11590)) — thanks @ntdatt812 +- **test(executors):** drop the stale 128-tool truncation assertion that contradicted the #11444 pass-through contract ([#11591](https://github.com/diegosouzapw/OmniRoute/pull/11591)) — thanks @ntdatt812 +- **test(token-health):** pin the current terminal-skip boundary — expired with retry budget is probed, `account_deactivated` stays skipped ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)) — thanks @ntdatt812 +- **test(models):** follow #11503's retarget of the `gemini-3-pro-high` alias to the hyphenated catalog id ([#11593](https://github.com/diegosouzapw/OmniRoute/pull/11593)) — thanks @ntdatt812 +- **refactor(video bridge):** extract per-video acquisition, whole-result caching, description, and metrics/abort/cleanup out of `VideoBridgeGuardrail.preCall` into a `processVideoPart` seam in a new `videoBridgePipeline.ts`, behind explicit `VideoMediaBrokerPort`, `VideoAudioTranscriptionPort`, and `VideoDrilldownPort` boundaries; `preCall` now only handles request traversal, policy, and response aggregation. The Video tab's FFmpeg/ffprobe runtime status is now an explicit `unknown` / `restricted` / `unavailable` / `available` state instead of a nullable boolean pair, fixing a case where an in-flight or failed probe was mislabeled as "install FFmpeg" ([#11657](https://github.com/diegosouzapw/OmniRoute/issues/11657)). ([#12016](https://github.com/diegosouzapw/OmniRoute/pull/12016)) +- **test(opencode-plugin):** add unit test coverage for `formatFreeBudget()` naming helper ([#11660](https://github.com/diegosouzapw/OmniRoute/pull/11660)) — thanks @f9td56dbgh-hub +- **docs(video):** clarify that the Video Bridge transcript `source` field (`client`, `embedded`, `audio-bridge`) is presently caller-declared and not yet server-verified — OmniRoute enforces the enum shape but does not cryptographically confirm that an `embedded`/`audio-bridge` label came from a server-owned extraction ([#11661](https://github.com/diegosouzapw/OmniRoute/issues/11661)). ([#12001](https://github.com/diegosouzapw/OmniRoute/pull/12001)) +- **chore(providers):** retire the distributed Raycast Relay and Hailuo Web integrations after a provenance audit confirmed substantial derivation from GPL-3.0 sources; the official GitHub and MiniMax API providers remain available. Migration 163 disables legacy connections and invalidates their active leases while preserving connection IDs, audit/history rows, quota snapshots, usage records, and fail-closed API-key connection policies. ([#11691](https://github.com/diegosouzapw/OmniRoute/pull/11691)) +- **chore(providers):** retire the Felo Web provider while its GPL-derived provenance remains on licensing HOLD ([#11698](https://github.com/diegosouzapw/OmniRoute/pull/11698)) — stale runtime and database references now fail closed while existing connection, lease, and usage history remains preserved for auditability. +- **fix(sse):** retire the Gemini Web image-generation bridge while provenance and licensing remain on HOLD, preserving Gemini Web chat/auth/tools and legitimate Gemini/Nano Banana image providers ([#11708](https://github.com/diegosouzapw/OmniRoute/pull/11708)) +- **chore(audio):** retire the built-in EdgeTTS provider and its 11-voice catalog while provenance/licensing review remains on HOLD; the generic `/v1/audio/speech` endpoint and the other speech providers remain available ([#11711](https://github.com/diegosouzapw/OmniRoute/pull/11711)) +- **fix(providers):** retire the unofficial Qwen Web integration and its `qwen-web`/`qw` identities while third-party provenance remains on HOLD ([#11713](https://github.com/diegosouzapw/OmniRoute/pull/11713)) — direct requests now fail closed with a sanitized HTTP 410 and combos skip the retired target, while migration guards preserve connection IDs, allowlists, usage/audit history and invalidate active leases. Supported Qwen API, Qwen Code, cloud, token-plan, audio, local and model-name surfaces remain available; restoring Qwen Web requires a qualified provenance/license decision. +- **fix(providers):** retire `microsoft-designer-web` and its `msdesigner` alias from runtime and catalogs while preserving stored credentials, provider history and allowlists as inactive records — source dependency on gpt4free's [`MicrosoftDesigner.py`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/g4f/Provider/needs_auth/MicrosoftDesigner.py) is proven at snapshot `0943d16eef757a44deb60651c07bbe5fa958cafe`, whose [`LICENSE`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/LICENSE) is GPL-3.0; expressive adaptation is probable, which records provenance/licensing risk without asserting a legal conclusion ([#11720](https://github.com/diegosouzapw/OmniRoute/pull/11720)). +- **docs(legal):** centralize pinned MIT notices for the vendored GCF codec and local flag assets ([#11726](https://github.com/diegosouzapw/OmniRoute/pull/11726)) +- Split the 2,952-line `adobeFireflyClient.ts` into nine focused modules (catalog, credentials, payload, ARP session, upload, responses, IMS, account, poll) behind an unchanged barrel — no consumer edits and no public API change (#11728). — thanks @MumuTW +- **fix(dashboard):** replaced the Model Lockout card's unprovenanced notification MP3 with a short, locally synthesized Web Audio chime ([#11731](https://github.com/diegosouzapw/OmniRoute/pull/11731)) — both toggles retain optional audible feedback without shipping replacement media or third-party code, and unsupported, suspended, or rejected audio contexts fail open without blocking the setting change. +- **chore(deepseek-web):** remove the provenance-unresolved DeepSeek PoW WASM binary and its runtime loader/tracing while retaining the existing JavaScript solver (slower at high difficulty) ([#11732](https://github.com/diegosouzapw/OmniRoute/pull/11732)). +- **chore(assets):** remove four unused provider assets whose redistribution provenance could not be verified while retaining the active Freebuff SVG ([#11735](https://github.com/diegosouzapw/OmniRoute/pull/11735)) +- **docs(assets):** retired two raster images whose redistribution provenance could not be verified ([#11736](https://github.com/diegosouzapw/OmniRoute/pull/11736)) — PicoClaw documentation now reuses the existing SVG, while Zoo Code remains a text link without an image. +- **docs(legal):** add pinned notices for six LobeHub-derived provider SVGs and 65 byte-exact theSVG assets, retaining registry claims as unverified metadata with explicit trademark and HOLD boundaries ([#11737](https://github.com/diegosouzapw/OmniRoute/pull/11737)). +- **chore(quality):** added a versioned 225/225 provider-asset provenance manifest and blocking drift gate ([#11743](https://github.com/diegosouzapw/OmniRoute/pull/11743)) — the inventory pins magic MIME and SHA-256 for every physical file, records immutable source matches for 71 assets, keeps 69 probable and 85 unresolved entries explicit but non-blocking, and treats provenance as source evidence rather than copyright or trademark clearance. +- **refactor(mitm):** replace four previously derived Traffic Inspector utilities — conversation normalization, SSE merging, secret masking, and header sanitization — with independent clean-room implementations based on public provider schemas, WHATWG SSE, and RFC 6750, while keeping the two independently classified passthrough surfaces unchanged ([#11748](https://github.com/diegosouzapw/OmniRoute/pull/11748)). +- Added a unit test that fails seven days before any dated pack under `config/` (`validUntil` and sibling keys) lapses, naming the file and key. The Alibaba free-tier pack expired on 2026-08-27 and turned every PR red the next morning with no commit involved; renewal now happens on someone's terms, not the clock's. ([#11891](https://github.com/diegosouzapw/OmniRoute/pull/11891)) +- `check:workflows` now fails (under `--strict`/`--ratchet`) when any job routed to a self-hosted runner publishes with `--provenance` — npm rejects that with `422` at the registry, which in v3.8.50 only surfaced after the tag and Docker images were public. ([#11895](https://github.com/diegosouzapw/OmniRoute/pull/11895)) +- `scripts/ops/runner-janitor.sh` now proves a path is idle with one `lsof` snapshot and removes stale leftovers itself (tmpfs after 3 h — it is RAM — disk after 24 h), kills orphan `next-build` processes, prunes checkouts of stopped runners, and alerts on memory pressure; `--dry-run` shows exactly what it would do. `docs/ops/RUNNER_BOX.md` reconciled to the measured box (31 GB, 10 listeners). ([#11893](https://github.com/diegosouzapw/OmniRoute/pull/11893)) +- The `next-build` artefact (1.3 GB) is now written and read under `$RUNNER_TEMP` (per-runner, on disk) instead of `/tmp`, which on the self-hosted pool is a 12 GB tmpfs in RAM. Landing it there took 27–32 of the publish job's 76 minutes, and the fixed `/tmp/e2e-build.tar.gz` name let E2E jobs on different runners overwrite each other's download. ([#11896](https://github.com/diegosouzapw/OmniRoute/pull/11896)) +- The CI `build` job now runs in two concurrency lanes — `main` and pull requests — so a release build is never queued behind (or OOM-killed beside) PR builds on the self-hosted pool, which holds one `next-build` comfortably and two at the edge. ([#11901](https://github.com/diegosouzapw/OmniRoute/pull/11901)) +- Re-freeze the ESLint suppressions on `release/v3.8.51` from a clean-room measurement (2 stale file entries pruned, 55 pre-existing `no-explicit-any` in six new files frozen under #11924) and drop the dead `GPT_SIZE_MAP` constant orphaned by the Adobe Firefly client split, so `No new ESLint warnings` stops failing every PR with exit 2 (Refs #11924) ([#11955](https://github.com/diegosouzapw/OmniRoute/pull/11955)) +- Type the 55 `no-explicit-any` sites that had been frozen under #11924 — four redundant casts in `socksConnectorWithFamily.ts` (undici/socks types already accept them) and the mocks/fixtures of the socks-timeout and isFree suites — and drop their suppression entries; the ESLint ratchet shrinks from 5487 to 5432 (Closes #11924) ([#11975](https://github.com/diegosouzapw/OmniRoute/pull/11975)) +- Take the two hosted-runner builds off the PR rail: `Build App` (`build.yml`) is `workflow_dispatch`-only and `DAST smoke (PR)` runs only for PRs into `main` — the 7 GB hosted VM cannot build `release/v3.8.51` in any profile (19/30 red, VM shutdown ~8 min into `next build`) and both checks had turned into permanent noise on every release PR; the bundle stays validated by `ci.yml` on `main` and by `nightly-release-green` on `release/**` (Closes #11946) ([#11962](https://github.com/diegosouzapw/OmniRoute/pull/11962)) +- Move the four nightly jobs that build the backend (`nightly-schemathesis`, `nightly-llm-security` promptfoo + garak, `nightly-resilience` axe-a11y) off the hosted 7 GB runner — where they died on `release/v3.8.51` unseen — onto the box's new `omni-light` pool (two listeners), and document the reshaped fleet (4 active OmniRoute listeners: 2 `omni-build` + 2 `omni-light`, janitor ceiling 4) (Closes #11965) ([#11967](https://github.com/diegosouzapw/OmniRoute/pull/11967)) +- Stop painting every fork PR into `release/**` red: `quality.yml` `Build (advisory)` is skipped (GitHub still reports `continue-on-error` failures as check FAILURE). Hosted `ubuntu-latest` cannot finish `npm run build` on this tree — same class as #11962 taking `build.yml` off the PR rail. `docker-publish.yml` amd64 now runs on the `.113` `omni-build` pool (31 GB / 32 cores, two listeners) with Turbopack, shares the `heavy-build-main` lane with `ci.yml` `Build` so it queues instead of becoming a third heavy, and keeps per-ref concurrency (a merge storm was starting 8 concurrent OOM builds). arm64 stays on `ubuntu-24.04-arm` with webpack — there is no ARM box. Fallback when `USE_VPS_RUNNER` is off: hosted amd64 + webpack (#11976). ([#12021](https://github.com/diegosouzapw/OmniRoute/pull/12021)) +- `docker-publish.yml` builds with webpack (`OMNIROUTE_USE_TURBOPACK=0`) on **both** arches, including amd64 on the `.113` `omni-build` pool. Turbopack had the RAM (31 GB, `omniroute-113-6`) and still panicked (`TurbopackInternalError: there must be a path to a root`, run 33253576569, 26 min in). The same tree's arm64 webpack build on hosted `ubuntu-24.04-arm` succeeded (run 33264823398). Dockerfile already documents webpack as the Docker escape hatch (#11976). ([#12050](https://github.com/diegosouzapw/OmniRoute/pull/12050)) +- **docs(free-tier):** declare the counting vs deciding regimes for "is it free?" and guard the deciding path from DB-backed catalog resolution ([#12226](https://github.com/diegosouzapw/OmniRoute/pull/12226)) — thanks @maxmad64bis +- **docs(auto-combo):** The mode pack table in `docs/routing/AUTO-COMBO.md` now lists all six shipped packs with every weight each one sets, replacing a four-pack table whose numbers had also drifted from the source. It states plainly that no pack sets `quality`, so selecting any pack silences the observed-quality signal. Six more documents that quote the scoring factor count joined the `check:docs-counts` gate, which caught five stale claims — including one naming nine factors that do not exist — and two stale mode pack counts. The dashboard routing panel, which offered four of the six packs and labelled the default strategy "6-Factor Scoring", is now covered by a test; the two packs it was missing are `reliability-first` and `chaos-mode`, the latter labelled as the fault-injection profile it is rather than as one more routing preference ([#12316](https://github.com/diegosouzapw/OmniRoute/pull/12316)) — thanks @maxmad64bis +- **chore(quality):** rebaseline `src/lib/db/apiKeys.ts` for the ACL the key-creation path now preserves ([#12352](https://github.com/diegosouzapw/OmniRoute/pull/12352)) ([#12673](https://github.com/diegosouzapw/OmniRoute/pull/12673)) — thanks @KrzysiekSko +- **chore(providers):** bump the Claude Code wire identity and the Devin bridge image pin from `2.1.220` to `2.1.258` ([#12402](https://github.com/diegosouzapw/OmniRoute/pull/12402)) — thanks @ggiak ([#12604](https://github.com/diegosouzapw/OmniRoute/pull/12604)) +- **docs(env):** align `.env.example`, the README Bun section, and the troubleshooting guide with the code: `OMNIROUTE_USE_TURBOPACK` also governs `npm run build` (not dev-only), `bun run build` follows that flag instead of auto-selecting Webpack, `OMNIROUTE_CHAT_MAX_HEAVY_IN_FLIGHT` is unset by default (no request-count cap), and the structural `503 chat_admission_busy` message matches `chatAdmissionResponses.ts` (#12404 — thanks @pacocartones) +- **chore(electron):** upgrade the desktop app to Electron 44 (Chromium 152, Node 24.18.1) ([#12217](https://github.com/diegosouzapw/OmniRoute/pull/12217)). **Requires macOS 13 (Ventura) or later** — Chromium dropped macOS 12 (Monterey), so Monterey users must stay on an earlier OmniRoute desktop build. Windows and Linux are unaffected; the app already shipped only x64/arm64, so Electron 44 dropping 32-bit builds changes nothing. Removes the `openAsHidden`/`wasOpenedAsHidden` login-item fields deleted in Electron 44 — hidden autostart continues to work through the `--hidden` argument registered with the login item ([#12554](https://github.com/diegosouzapw/OmniRoute/pull/12554)) — thanks @app/dependabot +- **chore(quality):** rebaseline `src/sse/handlers/chat.ts` for the effective-input persistence the continuation fix needs ([#12641](https://github.com/diegosouzapw/OmniRoute/pull/12641)) ([#12680](https://github.com/diegosouzapw/OmniRoute/pull/12680)) — thanks @hartmark +- `Coverage` job on `ci.yml`: the informational Codecov upload gets its own 5-minute ceiling and `continue-on-error`, and the job budget grows from 20 to 30 minutes (the 8-shard c8 merge alone takes ~10) — a stalled upload no longer ends the job `cancelled` and drags a fully green `main` run's conclusion down with it ([#11972](https://github.com/diegosouzapw/OmniRoute/pull/11972)) +- CI hardening on the PR rail: the four `Unit Tests fast-path` shards get `timeout-minutes: 30` (a hung shard held a PR for 64 min instead of GitHub's 6 h default) and both ESLint file caches lose their `restore-keys` fallback, so a cache built under another suppressions file or lint config can no longer report stale verdicts (Refs #11600, #11924) ([#11963](https://github.com/diegosouzapw/OmniRoute/pull/11963)) +- Every CI job that runs a `next build` (`build`, the npm `publish`, both release-green validations) now targets the `omni-build` runner label, which only two of the eight self-hosted runners carry. The box holds one build comfortably and two at the edge; a third now queues on GitHub instead of being OOM-killed by the kernel. ([#11932](https://github.com/diegosouzapw/OmniRoute/pull/11932)) +- **refactor(combo):** move `handleRoundRobinCombo` (and `resolveTargetTokenLimit`) into `open-sse/services/combo/roundRobinCombo.ts`. `combo.ts` drops from 2164 to 1014 lines (`split("\n").length`); the leaf is 1199 (under the 1200 new-file cap). The round-robin call site uses a dynamic `import()` so `releaseStickyPinOnFailure` / `clearStaleLKGP` can stay exported from `combo.ts` without a static cycle. Skip / sticky / semaphore / safety-timer behavior is unchanged. ([#12811](https://github.com/diegosouzapw/OmniRoute/pull/12811)) — thanks @HouMinXi +- **chore(quality):** rebaseline the file-size caps the error-boundary campaign grew past (`open-sse/executors/codex.ts`, `open-sse/vendor/codex-chatgpt-web/bridge.ts`, both via [#12444](https://github.com/diegosouzapw/OmniRoute/pull/12444)) ([#12654](https://github.com/diegosouzapw/OmniRoute/pull/12654)) +- **chore(quality):** rebaseline the file-size caps the hartmark batch grew past (`combos/page.tsx` via [#12355](https://github.com/diegosouzapw/OmniRoute/pull/12355), `open-sse/services/combo.ts` via [#12338](https://github.com/diegosouzapw/OmniRoute/pull/12338)) ([#12623](https://github.com/diegosouzapw/OmniRoute/pull/12623)) — thanks @hartmark +- **chore(quality):** rebaseline the file-size caps the HouMinXi batch grew past when its PRs stacked (`providers/page.tsx`, `chatCore.ts`, `accountFallback.ts`) — each PR measured correctly in isolation, none saw the stacking ([#12619](https://github.com/diegosouzapw/OmniRoute/pull/12619)) +- **chore(quality):** rebaseline `open-sse/services/combo.ts` for the reset-aware scoring the HouMinXi batch stacked ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) ([#12678](https://github.com/diegosouzapw/OmniRoute/pull/12678)) — thanks @HouMinXi +- **docs:** sync provider catalog count (direct commit `4f11b2ae3d`) +- **docs(skills):** sync omni-inference search fallback (direct commit `b4e76a7ed9`) +- **chore(lint):** prune two stale base suppressions (direct commit `1325047d56`) +- **test(auto-combo):** tolerate full-suite rotation load (direct commit `ad3e293f9f`) +- **test:** drain inherited quality snapshot reds (direct commit `21ed68d8ac`) +- **chore(quality):** rebaseline measured CLI bundle growth (direct commit `d35b3f9779`) +- **test(ui):** reproduce invalid AnySearch icon alias (direct commit `12cc6ca834`) +- **test(translator):** fix the relative imports of the relocated deferred-finish test (direct commit `777d9d1629`) +- **docs(counts):** bring the documented provider and migration counts to the measured values (direct commit `e70bea30e9`) +- **test(security):** drop the guard-target set left unused by the sync (direct commit `a32ba0e362`) +- **chore(db):** renumber model_capabilities migration 163 → 169 (cross-branch collision with 163_radar_feed_cache_generated_at) (direct commit `70f5d4cbf9`) +- **docs(cli):** document CLI_PRIME_AGENT_BIN and correct the CLI Agents count (direct commit `9058e39b61`) +- **chore(deps):** refresh runtimes and adopt ESLint 10 ([#11259](https://github.com/diegosouzapw/OmniRoute/pull/11259)) — thanks @backryun +- **refactor(local-corpus):** implement dynamic root resolution and LRU cache ([#11491](https://github.com/diegosouzapw/OmniRoute/pull/11491)) — thanks @raheemuddin786 +- **chore(quality):** prune stale eslint-suppressions entries ([#11596](https://github.com/diegosouzapw/OmniRoute/pull/11596)) +- **chore(ci):** unblock quality gates - env-doc sync + stryker tap.testFiles registration (#9282) ([#11609](https://github.com/diegosouzapw/OmniRoute/pull/11609)) — thanks @Meet6338-X +- **chore(ci):** point the circular-deps gate at dpdm's real JS entrypoint ([#11615](https://github.com/diegosouzapw/OmniRoute/pull/11615)) — thanks @benzntech +- **chore(quality):** rebaseline file-size for #11448/#11495/#11561 growth ([#11630](https://github.com/diegosouzapw/OmniRoute/pull/11630)) +- **deps:** realign onnxruntime-node pin to @huggingface/transformers' 1.24.3 lockstep ([#11633](https://github.com/diegosouzapw/OmniRoute/pull/11633)) — thanks @jonlwheat2-gif +- **test(autoCombo):** port models_dev_tier #11508 guard from node:test to vitest ([#11635](https://github.com/diegosouzapw/OmniRoute/pull/11635)) — thanks @jonlwheat2-gif +- **chore(quality):** rebaseline file-size for #11083/#11631/#11628 growth ([#11636](https://github.com/diegosouzapw/OmniRoute/pull/11636)) +- **chore(lint):** prune stale ESLint suppressions (base-red #11449) ([#11644](https://github.com/diegosouzapw/OmniRoute/pull/11644)) — thanks @jonlwheat2-gif +- **docs(changelog):** aggregate the ten v3.8.50 fragments into CHANGELOG.md ([#11683](https://github.com/diegosouzapw/OmniRoute/pull/11683)) +- **chore(search):** drop unreachable credential sweep after #11642 ([#11687](https://github.com/diegosouzapw/OmniRoute/pull/11687)) — thanks @HouMinXi +- **ci:** add API route TypeScript regression gate ([#11705](https://github.com/diegosouzapw/OmniRoute/pull/11705)) — thanks @Sabeekhann +- **docs(dedup):** correct the CodeQL claim on the tenant-namespace comment ([#11712](https://github.com/diegosouzapw/OmniRoute/pull/11712)) +- **docs(changelog):** add consolidated v3.8.50 stats, top-25 ranking and a .mailmap ([#11715](https://github.com/diegosouzapw/OmniRoute/pull/11715)) +- **docs:** recommend gstack for AI-assisted workflows ([#11770](https://github.com/diegosouzapw/OmniRoute/pull/11770)) — thanks @santosraju99-hub +- **chore(quality):** tighten complexity/cognitive-complexity ratchets to the current tip; land the missed gateways.ts rebaseline ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **docs(i18n):** finish Freepik → Magnific rebrand in locale strings and README ([#11772](https://github.com/diegosouzapw/OmniRoute/pull/11772)) — thanks @turbolego +- **refactor(auto-combo):** fix divergent scoring in combo health reporting ([#11854](https://github.com/diegosouzapw/OmniRoute/pull/11854)) — thanks @jacobsparts +- **chore(changelog):** add missing fragments for #11919/#11918/#11916 ([#11938](https://github.com/diegosouzapw/OmniRoute/pull/11938)) +- **test(check):** escape the runs-on fixture with JSON.stringify, not a quote-only replace ([#11942](https://github.com/diegosouzapw/OmniRoute/pull/11942)) (direct commit `fb7445eaa3`) +- **test(release):** align five suites with the contracts #11933, #11919 and #11876 shipped on release/v3.8.51 ([#11944](https://github.com/diegosouzapw/OmniRoute/pull/11944)) +- **chore(quality):** remove unreachable code and restore test discovery ([#11950](https://github.com/diegosouzapw/OmniRoute/pull/11950)) — thanks @backryun +- **docs(agents):** sync-back landings are fast-forward, never squash ([#11964](https://github.com/diegosouzapw/OmniRoute/pull/11964)) +- **test(infra):** retry recursive temp-dir removal instead of failing a shard on ENOTEMPTY (#11966) ([#11968](https://github.com/diegosouzapw/OmniRoute/pull/11968)) +- **docs(ops):** the .113 heavy-build ceiling is one runner, not two ([#12048](https://github.com/diegosouzapw/OmniRoute/pull/12048)) +- **test(ui):** correct inactive auto-fetch expectation ([#12098](https://github.com/diegosouzapw/OmniRoute/pull/12098)) — thanks @RaviTharuma +- **docs:** document native dependency check escape hatch ([#12101](https://github.com/diegosouzapw/OmniRoute/pull/12101)) — thanks @RaviTharuma +- **test(providers):** regenerate the translate-path golden for OrcaRouter (#11923) ([#12118](https://github.com/diegosouzapw/OmniRoute/pull/12118)) +- **chore(quality):** velocity phase — loosen every numeric baseline by 20% until v4.0, monitor headroom nightly ([#12125](https://github.com/diegosouzapw/OmniRoute/pull/12125)) +- **chore(lint):** batch 0 of #12146 — type the call-log-cap sqlite rows instead of 45 as-any casts ([#12157](https://github.com/diegosouzapw/OmniRoute/pull/12157)) +- **chore(lint):** batch 4 of #12146 — shared/components react-hooks violations resolved ([#12159](https://github.com/diegosouzapw/OmniRoute/pull/12159)) +- **chore(lint):** batch 1 of #12146 — dashboard/cli-code react-hooks violations resolved ([#12160](https://github.com/diegosouzapw/OmniRoute/pull/12160)) +- **chore(lint):** batch 3 of #12146 — dashboard/settings react-hooks violations resolved ([#12162](https://github.com/diegosouzapw/OmniRoute/pull/12162)) +- **chore(lint):** batch 2 of #12146 — resolve the react-hooks compiler violations in dashboard/providers ([#12163](https://github.com/diegosouzapw/OmniRoute/pull/12163)) +- **chore(quality):** register search-432 cooldown test in stryker tap.testFiles ([#12170](https://github.com/diegosouzapw/OmniRoute/pull/12170)) +- **chore(lint):** batch 5 of #12146 — combos, endpoint, provider-stats, api-manager and costs react-hooks violations resolved ([#12174](https://github.com/diegosouzapw/OmniRoute/pull/12174)) +- **build:** add contributor fast profile ([#12192](https://github.com/diegosouzapw/OmniRoute/pull/12192)) — thanks @rafacpti23 +- **docs(readme):** README touch-ups ([#12193](https://github.com/diegosouzapw/OmniRoute/pull/12193)) ([#12194](https://github.com/diegosouzapw/OmniRoute/pull/12194)) ([#12202](https://github.com/diegosouzapw/OmniRoute/pull/12202)) — thanks @rafacpti23 +- **build:** force the webpack bundler in the contributor build profile ([#12197](https://github.com/diegosouzapw/OmniRoute/pull/12197)) — thanks @rafacpti23 +- **build:** skip standalone packaging in the contributor profile ([#12198](https://github.com/diegosouzapw/OmniRoute/pull/12198)) — thanks @rafacpti23 +- **docs(audit):** align every published number with the code and harden check:docs-counts ([#12200](https://github.com/diegosouzapw/OmniRoute/pull/12200)) +- **build:** stub the instrumentation entrypoints in the contributor profile ([#12203](https://github.com/diegosouzapw/OmniRoute/pull/12203)) — thanks @rafacpti23 +- **build:** omit the standalone output target for contributor builds ([#12204](https://github.com/diegosouzapw/OmniRoute/pull/12204)) — thanks @rafacpti23 +- **chore(lint):** batch 6 of #12146 — memory, radar, audit, analytics, cache, usage, activity, home and RequestLoggerV2 react-hooks violations resolved ([#12208](https://github.com/diegosouzapw/OmniRoute/pull/12208)) +- **docs(diagrams):** rename number-carrying diagram files to stable names ([#12210](https://github.com/diegosouzapw/OmniRoute/pull/12210)) +- **docs(dashboard):** redraw onboarding tier-flow SVGs for the real 4-tier model ([#12211](https://github.com/diegosouzapw/OmniRoute/pull/12211)) +- **docs(api):** document every implemented route in openapi.yaml (276 → 692 paths) ([#12212](https://github.com/diegosouzapw/OmniRoute/pull/12212)) +- **chore(lint):** batch 7 of #12146 — final src tail: 37 react-hooks violations across 33 files resolved ([#12244](https://github.com/diegosouzapw/OmniRoute/pull/12244)) +- Revert "docs: recommend gstack for AI-assisted workflows (#11770)" ([#12248](https://github.com/diegosouzapw/OmniRoute/pull/12248)) ([#12249](https://github.com/diegosouzapw/OmniRoute/pull/12249)) +- **docs:** Chaos Mode setup guide + weighted strategy semantics ([#12250](https://github.com/diegosouzapw/OmniRoute/pull/12250)) +- **docs(agents):** protected-surface merge rule — operator approval for agent-instruction files ([#12253](https://github.com/diegosouzapw/OmniRoute/pull/12253)) +- **chore(quality):** register native-codex turn-pin tests in stryker tap.testFiles ([#12263](https://github.com/diegosouzapw/OmniRoute/pull/12263)) +- **chore(quality):** dedupe tap.testFiles entries added by racing base-red fixes ([#12265](https://github.com/diegosouzapw/OmniRoute/pull/12265)) +- **docs(readme):** introduce OmniRouteTray — the macOS menu-bar companion ([#12276](https://github.com/diegosouzapw/OmniRoute/pull/12276)) — thanks @ggiak +- **chore(proxy):** purge the legacy 1proxy residue — sync/rotator modules, dead DB exports, dead settings tab and flag, docs (#12091) ([#12290](https://github.com/diegosouzapw/OmniRoute/pull/12290)) +- **chore(quality):** baseline-headroom skips generated and vendored files in the fileSize worst-file signal ([#12291](https://github.com/diegosouzapw/OmniRoute/pull/12291)) +- **refactor(video):** unify the JPEG frame data-URI contract ([#12322](https://github.com/diegosouzapw/OmniRoute/pull/12322)) +- **chore(deps):** freeze onnxruntime-node and eslint-plugin-react-hooks out of dependabot groups ([#12329](https://github.com/diegosouzapw/OmniRoute/pull/12329)) +- **test(executors):** restore the #10986 reasoning-only fallback guards ([#12364](https://github.com/diegosouzapw/OmniRoute/pull/12364)) — thanks @pacocartones +- **chore(quality):** re-tighten the file-size ratchet to the real LOC (plan 3.8.52 task 0) ([#12411](https://github.com/diegosouzapw/OmniRoute/pull/12411)) +- **test(grok):** format web executor suite ([#12412](https://github.com/diegosouzapw/OmniRoute/pull/12412)) +- **chore(quality):** ratchet open-sse typecheck baseline to zero ([#12418](https://github.com/diegosouzapw/OmniRoute/pull/12418)) +- **test(build):** guard the artifact path policy arrays against duplicates ([#12422](https://github.com/diegosouzapw/OmniRoute/pull/12422)) +- **chore(lint):** adopt eslint-plugin-react-hooks 7.1.1 ([#12428](https://github.com/diegosouzapw/OmniRoute/pull/12428)) +- **chore(providers):** remove a keyless provider integration at its operator's request ([#12440](https://github.com/diegosouzapw/OmniRoute/pull/12440)) +- **docs(providers):** register providers removed at their operator's request and guard against reintroduction ([#12478](https://github.com/diegosouzapw/OmniRoute/pull/12478)) +- **chore(quality):** base fixes — stryker tap.testFiles + node_modules cache key ([#12482](https://github.com/diegosouzapw/OmniRoute/pull/12482)) +- **docs(arch):** one-process recipe for tens of long /v1/responses ([#12493](https://github.com/diegosouzapw/OmniRoute/pull/12493)) — thanks @RaviTharuma +- **chore(quality):** tighten the CodeQL ratchet baseline from 11 to 6 ([#12530](https://github.com/diegosouzapw/OmniRoute/pull/12530)) +- **chore(deps):** pin browserslist override to ^4.28.8 ([#12592](https://github.com/diegosouzapw/OmniRoute/pull/12592)) — thanks @KooshaPari +- **chore(deps):** bump fast-uri to 3.1.7 in the electron lockfile ([#12601](https://github.com/diegosouzapw/OmniRoute/pull/12601)) +- **docs(quality):** document how the CodeQL ratchet refreshes and how to tighten it ([#12611](https://github.com/diegosouzapw/OmniRoute/pull/12611)) +- **test(ci):** pin the openapi-security-tiers two-arm contract with an executing gate test (#12581) ([#12652](https://github.com/diegosouzapw/OmniRoute/pull/12652)) +- **docs(gamification):** align XP Rewards table with code (#12501) ([#12667](https://github.com/diegosouzapw/OmniRoute/pull/12667)) — thanks @KooshaPari +- **refactor(combo):** split executeTarget into gates, attempt, and loop ([#12746](https://github.com/diegosouzapw/OmniRoute/pull/12746)) — thanks @HouMinXi +- **docs(security):** document socket.yml scanner config + CI workflow link (#12575) ([#12764](https://github.com/diegosouzapw/OmniRoute/pull/12764)) — thanks @KooshaPari +- **chore(ci):** guard commit identity in pre-commit to stop author misattribution ([#12772](https://github.com/diegosouzapw/OmniRoute/pull/12772)) +- **deps:** 17 Dependabot bumps — bump electron from 43.4.0 to 43.4.1 in /electron ([#11426](https://github.com/diegosouzapw/OmniRoute/pull/11426)); bump the development group across 1 directory with 8 updates ([#11428](https://github.com/diegosouzapw/OmniRoute/pull/11428)); bump the production group across 1 directory with 11 updates ([#11440](https://github.com/diegosouzapw/OmniRoute/pull/11440)); chore(deps): bump github/codeql-action/init from 4.37.7 to 4.37.8 ([#11925](https://github.com/diegosouzapw/OmniRoute/pull/11925)); chore(deps): bump github/codeql-action from 4.37.7 to 4.37.8 ([#11926](https://github.com/diegosouzapw/OmniRoute/pull/11926)); chore(deps): bump github/codeql-action/analyze from 4.37.7 to 4.37.8 ([#11927](https://github.com/diegosouzapw/OmniRoute/pull/11927)); build(deps): bump github/codeql-action/init from 4.37.8 to 4.37.9 ([#12345](https://github.com/diegosouzapw/OmniRoute/pull/12345)); build(deps): bump github/codeql-action/analyze from 4.37.8 to 4.37.9 ([#12346](https://github.com/diegosouzapw/OmniRoute/pull/12346)); bump the development group across 1 directory with 2 updates ([#12347](https://github.com/diegosouzapw/OmniRoute/pull/12347)); build(deps): bump github/codeql-action from 4.37.8 to 4.37.9 ([#12349](https://github.com/diegosouzapw/OmniRoute/pull/12349)); bump browserslist from 4.28.2 to 4.28.8 ([#12396](https://github.com/diegosouzapw/OmniRoute/pull/12396)); bump the production group across 1 directory with 4 updates ([#12399](https://github.com/diegosouzapw/OmniRoute/pull/12399)); bump @xmldom/xmldom ([#12500](https://github.com/diegosouzapw/OmniRoute/pull/12500)); bump qs from 6.15.2 to 6.16.0 ([#12512](https://github.com/diegosouzapw/OmniRoute/pull/12512)); bump @xmldom/xmldom from 0.9.10 to 0.9.12 ([#12513](https://github.com/diegosouzapw/OmniRoute/pull/12513)); bump fast-uri from 3.1.5 to 3.1.7 ([#12514](https://github.com/diegosouzapw/OmniRoute/pull/12514)); bump @humanfs/node from 0.16.7 to 0.16.8 ([#12515](https://github.com/diegosouzapw/OmniRoute/pull/12515)) +- Clear the `release/v3.8.51` `check:agent-skills-sync` base-red: regenerate `skills/cli-tunnel/SKILL.md` so the `tunnel create [type]` positional that #13009 taught the generator to read is reflected in the committed skill. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` typecheck base-red from #12731: the new `Mode pack ranking gates` candidates in `open-sse/services/autoCombo/__tests__/autoCombo.test.ts` omitted the required `provider`, `model` and `errorRate` fields of `ProviderCandidate`, failing `check:open-sse-typecheck` (and with it `Fast Quality Gates`) on every open PR. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` docs-sync base-red: EURouter (#13025) and GreenPT (#13024) took the live provider count to 358, leaving 7 STRICT drifts (`PROVIDER_REFERENCE.md`, 4 diagrams, 2 tier-flow images) plus the `AGENTS.md` / `llm.txt` / `package.json` count claims stale. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- **test(models):** Pin `CATALOG_BUILD_TIMEOUT_MS` in the #12058 canonical-catalog regression so a cold catalog build racing the 8s default bound no longer flakes the suite with a 500 ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Allow `@omniroute/opencode-plugin-v2/` in the npm pack policy — [#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870) shipped the v2 plugin beside its v1 sibling without widening the allowlist, so every packed file under it read as an unexpected artifact and failed `check:pack-policy` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Point the release-green validator's pack gate at the tree it is validating — the build-provenance guard defaulted to `origin/main`, which a release branch tip can never be an ancestor of mid-cycle, so `Package artifact` reported a false red on every run and the tarball boot-smoke cascaded off it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **docs(checks):** keep doc counts honest — headings, rankings, catalog, weights, quality gate and scoring diagram now covered ([#12507](https://github.com/diegosouzapw/OmniRoute/pull/12507)) — thanks @maxmad64bis +- **chore(lifecycle):** `check:model-lifecycle` now also diffs `DEFAULT_DEGRADATION_MAP` (the background-task redirect table) against the vendor lifecycle snapshot, refusing a retired id as source or target, with a table-driven unit test beside it. Three rows whose source the vendor had retired — `claude-sonnet-4-20250514`, `gemini-3-pro-preview` and `gpt-5.1-codex` (whose target `gpt-5.1-codex-mini` is retired too) — were dead code, since `checkLifecycle` answers 410 before the redirect runs; they are dropped (#12535 — thanks @pacocartones) +- **test(compression):** cover idle worker eviction at the resource level — the pool must call `terminate()` and must not retain the worker's `MessagePort`, complementing the `exit`-event assertion added with the fix ([#13371](https://github.com/diegosouzapw/OmniRoute/pull/13371)) +- **chore(quality):** raise the `imageGeneration.ts` file-size ceiling for the image-only-model guard that clears the #12945 base-red ([#13392](https://github.com/diegosouzapw/OmniRoute/pull/13392)) +- **chore(quality):** type the combo-test route's JSON response bodies instead of casting them to `any`, drop the now-empty suppression entry, and remove the `hasPerModelQuota` import `chatCore.ts` stopped using when the failure-classification helper was extracted ([#13379](https://github.com/diegosouzapw/OmniRoute/pull/13379)) +- **chore(electron):** the desktop bundle no longer ships the root-level files of every translated docs mirror (`README.md`, `llm.txt`, `CLAUDE.md`, `GEMINI.md`, `CONTRIBUTING.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`) — the packaged app only reads `docs/i18n//docs/**`, which stays. Saves ~11 MB on top of the translated CHANGELOGs already pruned. (#0000) ([#13235](https://github.com/diegosouzapw/OmniRoute/pull/13235)) +- **chore(quality):** retighten the `src/sse/handlers/chat.ts` unused-vars suppression count to the 8 violations that actually remain ([#13385](https://github.com/diegosouzapw/OmniRoute/pull/13385)) +- Bring the documented counts back in line with the code: 171 migrations (was 169) and 20 routing strategies (was 19), plus the regenerated `cli-tunnel` skill reference. ([#12970](https://github.com/diegosouzapw/OmniRoute/pull/12970)) +- Update the documented migration count to 172 after the call-logs provider-stats indexes landed. ([#13209](https://github.com/diegosouzapw/OmniRoute/pull/13209)) +- **chore(ci):** two gates that close the blind spots behind the exclusions above. `check:vitest-exclusions` requires every Vitest exclusion to name a tracking issue and to appear in `config/quality/vitest-exclusions.json` — the previous list grew to 62 files behind a comment pointing at an issue that had been closed for a month. `check-new-key-coverage` requires a key newly added to `en.json` to reach every locale; the existing coverage gate is a percentage floor per locale, so eleven absent keys out of ~13,000 left it at 99.9% while a whole feature shipped untranslated in nine languages. Both are diff-aware, so pre-existing debt stays frozen and neither needed a migration to turn on. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **build:** root layout no longer loads Inter from `next/font/google`, so a production image build does not need fonts.googleapis.com ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **docs(i18n):** bring the eight new `llm.txt` mirrors (km, kn, ml, my, ne, or, pa, si) up to the current provider and migration counts, so `check-docs-sync` stops rejecting every commit ([#13674](https://github.com/diegosouzapw/OmniRoute/pull/13674)) +- **refactor(ui):** flow surfaces (home topology, combo live studio, compression cockpit/waterfall/nodes, token-health badge) express state through the theme-aware `--orch-status-*` tokens instead of fixed dark-mode hex, so green/red/amber/grey stay legible in the light theme; dark mode is byte-identical. Categorical palettes (routing-strategy hues, compression-layer pills, provider brand colors) deliberately stay hex ([#12378](https://github.com/diegosouzapw/OmniRoute/issues/12378)) ([#13203](https://github.com/diegosouzapw/OmniRoute/pull/13203)) +- **chore(tests):** 51 test files that had been excluded from Vitest are running again, restoring roughly 350 assertions to the blocking suite. Each was measured individually first: of the 62 files parked behind the `// #8618 — pre-existing failure` comment, 51 pass against the current tree with no source change, so the exclusions had outlived the failures they were added for. The 11 that genuinely still fail stay excluded, but now point at a live tracker (#13204) rather than at #8618, which was closed in August while the list it tracked kept growing. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **chore(quality):** drop two ESLint suppression entries whose violations no longer exist, so `eslint --suppressions-location` stops rejecting every commit that touches the surrounding files ([#13384](https://github.com/diegosouzapw/OmniRoute/pull/13384)) +- **chore(release):** bring main's post-tag fixes into release/v3.8.51 — sync-back step 2/2 (direct commit `21c488f210`) +- **chore(deps):** patch toml and esbuild in the opencode plugin lockfiles ([#12965](https://github.com/diegosouzapw/OmniRoute/pull/12965)) +- **docs(changelog):** reconcile the v3.8.51 living section — fold 366 fragments, cover every cycle commit, credit every contributor ([#12971](https://github.com/diegosouzapw/OmniRoute/pull/12971)) +- **test(plugin):** wait for the v2 lazy-refresh tier instead of sleeping past it ([#12984](https://github.com/diegosouzapw/OmniRoute/pull/12984)) +- **chore(quality):** rebaseline chatCore.ts after the non-streaming regression fixes ([#13045](https://github.com/diegosouzapw/OmniRoute/pull/13045)) +- **docs(changelog):** add the missing bullet marker to the DeepSeek PoW fragment ([#13200](https://github.com/diegosouzapw/OmniRoute/pull/13200)) +- **chore(deps):** drain the Dependabot queue — 10 of 13 alerts ([#13213](https://github.com/diegosouzapw/OmniRoute/pull/13213)) +- **chore(ci):** make worktrees carry the hooks that enforce the identity gate ([#13236](https://github.com/diegosouzapw/OmniRoute/pull/13236)) +- **chore(deps):** pin csv-parse to ^7.0.2 via overrides (#13117) ([#13300](https://github.com/diegosouzapw/OmniRoute/pull/13300)) — thanks @KooshaPari +- **chore(deps):** bump hono from ^4.12.34 to ^4.13.7 (#13148) ([#13301](https://github.com/diegosouzapw/OmniRoute/pull/13301)) — thanks @KooshaPari +- **chore(deps):** pin joi to ^18.2.8 via overrides (#13085) ([#13302](https://github.com/diegosouzapw/OmniRoute/pull/13302)) — thanks @KooshaPari +- **docs(providers):** add Microsoft 365 Copilot (BizChat) provider guide (#12779) ([#13340](https://github.com/diegosouzapw/OmniRoute/pull/13340)) — thanks @KooshaPari +### 🙌 Contributors + +Thanks to everyone whose work landed in v3.8.51: + +| Contributor | PRs / Issues | +| --- | --- | +| [@5dive-bot](https://github.com/5dive-bot) | #11852 | +| [@abhisheksharma2411](https://github.com/abhisheksharma2411) | #11547, #11684, #11959, #12209 | +| [@adevwithpurpose](https://github.com/adevwithpurpose) | #11464 | +| [@adivekar-utexas](https://github.com/adivekar-utexas) | #12015, #12043, #12090, #12138 | +| [@AIB1TAL0S](https://github.com/AIB1TAL0S) | #12103 | +| [@alltomatos](https://github.com/alltomatos) | #11448 | +| [@alvinveroy](https://github.com/alvinveroy) | #11857, #12027, #12165 | +| [@amaleta](https://github.com/amaleta) | #11906, #11934 | +| [@amartinawi](https://github.com/amartinawi) | #11575 | +| [@AndrianBalanescu](https://github.com/AndrianBalanescu) | #11083, #11830, #11834, #11844 | +| [@anhtahaylove](https://github.com/anhtahaylove) | #13091, #13092, #13093, #13096, #13097, #13100, #13106, #13109, #13114, #13155, #13166, #13171, #13175, #13187, #13196 | +| [@aniruddhaadak80](https://github.com/aniruddhaadak80) | #11760 | +| [@app](https://github.com/app) | #12554 | +| [@arminanton](https://github.com/arminanton) | #11461, #11513 | +| [@AStupidBear](https://github.com/AStupidBear) | #11584 | +| [@atescivitci-cmd](https://github.com/atescivitci-cmd) | #12105 | +| [@b3nw](https://github.com/b3nw) | #11970, #11971, #12147 | +| [@backryun](https://github.com/backryun) | #11259, #11950, #12075, #12076, #12078, #12079, #12081, #12082, #12181, #12228, #12239, #12255, #12258, #12277, #12367, #12423, #12524, #12538 | +| [@benjaminkitt](https://github.com/benjaminkitt) | #11747 | +| [@benzntech](https://github.com/benzntech) | #11614, #11615 | +| [@Bl0ck154](https://github.com/Bl0ck154) | #11948, #11951, #11952, #11953, #11954 | +| [@brick30llc-ctrl](https://github.com/brick30llc-ctrl) | #11752, #12242, #12262 | +| [@Chewji9875](https://github.com/Chewji9875) | #11455, #11875, #12028 | +| [@cheynetom](https://github.com/cheynetom) | #12058 | +| [@davidebaraldo](https://github.com/davidebaraldo) | #12222, #12304 | +| [@davidlinfr](https://github.com/davidlinfr) | #6390 | +| [@ddarkr](https://github.com/ddarkr) | #12124 | +| [@Deftera186](https://github.com/Deftera186) | #11809 | +| [@dpozimski](https://github.com/dpozimski) | #11945 | +| [@DW-MediaLab](https://github.com/DW-MediaLab) | #12229 | +| [@echel0nn](https://github.com/echel0nn) | #11923 | +| [@f9td56dbgh-hub](https://github.com/f9td56dbgh-hub) | #11660 | +| [@fabioluissilva](https://github.com/fabioluissilva) | #11991 | +| [@foreveryh](https://github.com/foreveryh) | #12177 | +| [@geek007git](https://github.com/geek007git) | #12115, #12116, #12117, #12120, #12122 | +| [@geekyNads](https://github.com/geekyNads) | #11784, #12266 | +| [@ggiak](https://github.com/ggiak) | #11561, #12276, #12350, #12402 | +| [@gonisulaimann](https://github.com/gonisulaimann) | #12368, #12369, #12371 | +| [@Gorillaz322](https://github.com/Gorillaz322) | #12207 | +| [@groovecityJO](https://github.com/groovecityJO) | #12682 | +| [@hartmark](https://github.com/hartmark) | #11434, #11452, #11473, #11499, #11703, #11983, #11984, #11985, #11986, #11988, #11989, #11990, #11994, #12221, #12293, #12445, #12446, #12447, #12448, #12460, #12461, #12623, #12646, #12650, #12680, #12717, #12718, #12727, #12741, #12854 | +| [@hizzt](https://github.com/hizzt) | #11894 | +| [@hongnoul](https://github.com/hongnoul) | #11484 | +| [@HouMinXi](https://github.com/HouMinXi) | #11411, #11414, #11512, #11518, #11520, #11641, #11642, #11643, #11687, #11779, #11849, #11850, #11851, #11915, #11916, #11918, #11919, #11920, #12013, #12017, #12033, #12042, #12106, #12139, #12166, #12169, #12171, #12205, #12213, #12312, #12325, #12487, #12488, #12495, #12504, #12557, #12566, #12590, #12591, #12624, #12626, #12632, #12637, #12678, #12696, #12697, #12711, #12733, #12746, #12767, #12770, #12789, #12803, #12805, #12811, #12866, #12868, #12899, #12926, #12934, #12950, #12951, #12974, #13001, #13006, #13011, #13017, #13026, #13027, #13034, #13035, #13038, #13042, #13050, #13060, #13061, #13069, #13107, #13120, #13136, #13195, #13197 | +| [@Hsia97](https://github.com/Hsia97) | #11624 | +| [@jacobsparts](https://github.com/jacobsparts) | #11854, #12155, #12167 | +| [@joglomedia](https://github.com/joglomedia) | #11980 | +| [@jonlwheat2-gif](https://github.com/jonlwheat2-gif) | #11443, #11450, #11549, #11567, #11608, #11633, #11635, #11640, #11644, #11671, #11672, #11673, #11674, #11675, #11676, #11677, #11706, #11783, #12051, #12052, #12053, #12055 | +| [@kanade-hoshino](https://github.com/kanade-hoshino) | #12180 | +| [@Karan825](https://github.com/Karan825) | #11861 | +| [@KaspaPulse](https://github.com/KaspaPulse) | #11389, #11469, #11628, #11666, #11910 | +| [@keeltrace](https://github.com/keeltrace) | #12080, #12223 | +| [@killer30001000](https://github.com/killer30001000) | #12178 | +| [@KooshaPari](https://github.com/KooshaPari) | #12470, #12592, #12667, #12699, #12703, #12706, #12764, #12769, #12771, #13300, #13301, #13302, #13340, #13401, #13403, #13404, #13405, #13406, #13407, #13409, #13410, #13411, #13412, #13413, #13414, #13418, #13424, #13427, #13433, #13523, #13524, #13525, #13528, #13531, #13532, #13534, #13539, #13543, #13545, #13546, #13547, #13550, #13553 | +| [@kriptoburak](https://github.com/kriptoburak) | #11370 | +| [@KrzysiekSko](https://github.com/KrzysiekSko) | #12425, #12432, #12673 | +| [@marcelokarval](https://github.com/marcelokarval) | direct commit / report | +| [@maxmad64bis](https://github.com/maxmad64bis) | #1622, #11435, #11437, #11441, #11537, #11550, #11553, #11555, #11557, #11812, #11842, #11843, #11903, #12151, #12214, #12215, #12218, #12226, #12314, #12316, #12317, #12318, #12319, #12320, #12321, #12507, #12715, #12731, #12744, #12786, #12787, #12788, #12790, #12792, #12794, #12795, #12828, #12832, #12853, #12857, #12870, #12937, #12941, #12975, #13141, #13142, #13146, #13147 | +| [@mdigitalbh81](https://github.com/mdigitalbh81) | #12240 | +| [@Meet6338-X](https://github.com/Meet6338-X) | #11598, #11609, #12110 | +| [@morpheus9393](https://github.com/morpheus9393) | #11943 | +| [@MumuTW](https://github.com/MumuTW) | #11492, #11502, #11506, #11507, #11626, #11675, #11685, #11728, #11746, #11888, #11889, #11890, #11892 | +| [@Neuron-Mr-White](https://github.com/Neuron-Mr-White) | #11622, #11801, #12256 | +| [@NightStalker-87](https://github.com/NightStalker-87) | #12183 | +| [@NoxzRCW](https://github.com/NoxzRCW) | #11879, #11880, #11881, #11882, #11883, #11935 | +| [@ntdat812](https://github.com/ntdat812) | #11585, #12095 | +| [@ntdatt812](https://github.com/ntdatt812) | #11368, #11573, #11574, #11576, #11577, #11580, #11582, #11583, #11588, #11589, #11590, #11591, #11592, #11593, #11672, #12177, #12180, #12873, #12918, #12920, #12921, #12925, #12930, #13007, #13009, #13024, #13025, #13083, #13087, #13101, #13104, #13110 | +| [@official-burak](https://github.com/official-burak) | #11542 | +| [@opensource-elearning](https://github.com/opensource-elearning) | #12179, #12189, #12278, #12286 | +| [@oyi77](https://github.com/oyi77) | #11408, #11409, #11421, #11505, #11677, #12036, #12110 | +| [@pacocartones](https://github.com/pacocartones) | #11521, #11522, #11527, #11528, #11529, #11530, #11531, #11532, #11533, #11534, #11595, #11599, #11603, #11604, #11605, #11607, #11610, #11676, #11714, #11716, #11718, #11767, #11838, #11860, #11862, #11869, #11871, #11872, #11873, #11903, #11906, #11921, #11934, #12356, #12358, #12359, #12360, #12361, #12362, #12364, #12365, #12373, #12374, #12375, #12376, #12377, #12379, #12380, #12381, #12386, #12387, #12389, #12390, #12394, #12395, #12397, #12401, #12403, #12404, #12522, #12523, #12535, #12536, #12540, #12541, #12543, #12545, #12548, #12549, #12550, #12551, #12552, #12647, #12651, #12653, #13055 | +| [@patrykkopycinski](https://github.com/patrykkopycinski) | #11936, #11937, #12224 | +| [@PauloFH](https://github.com/PauloFH) | #11509 | +| [@PauloHSOliveira](https://github.com/PauloHSOliveira) | #12241 | +| [@ponkcore](https://github.com/ponkcore) | #12054 | +| [@Prajeeth-12](https://github.com/Prajeeth-12) | #11634, #12046 | +| [@quiterunner-commits](https://github.com/quiterunner-commits) | #12131, #12143 | +| [@rafacpti23](https://github.com/rafacpti23) | #11554, #11558, #12192, #12197, #12198, #12202, #12203, #12204 | +| [@ragnar-claude](https://github.com/ragnar-claude) | #11564 | +| [@raheemuddin786](https://github.com/raheemuddin786) | #11491, #11839, #11840, #11841, #12003, #12230, #12231, #12232, #12233, #12234 | +| [@rahilmavani](https://github.com/rahilmavani) | #11761 | +| [@Rahulsharma0810](https://github.com/Rahulsharma0810) | #11771 | +| [@RaviTharuma](https://github.com/RaviTharuma) | #11710, #11727, #11797, #11798, #11802, #11805, #11806, #11811, #12098, #12099, #12101, #12449, #12452, #12472, #12473, #12493, #12533, #12607, #12628, #12631, #12636, #12875, #12876, #12880, #12882, #12884 | +| [@rezjalibd](https://github.com/rezjalibd) | #12186 | +| [@RhianB14](https://github.com/RhianB14) | #11692 | +| [@rifqiawl](https://github.com/rifqiawl) | #11517, #11519, #11969 | +| [@rqzbeh](https://github.com/rqzbeh) | #11390 | +| [@Sabeekhann](https://github.com/Sabeekhann) | #11699, #11705 | +| [@santosraju99-hub](https://github.com/santosraju99-hub) | #11755, #11770 | +| [@Seramicx](https://github.com/Seramicx) | #11568 | +| [@Siva010](https://github.com/Siva010) | #12191 | +| [@solstxce](https://github.com/solstxce) | #11597 | +| [@soroush5](https://github.com/soroush5) | #12691 | +| [@tenshiak](https://github.com/tenshiak) | #12279 | +| [@TheDemonTuan](https://github.com/TheDemonTuan) | #11468, #11470, #11471, #11482, #11548, #11758, #11775, #11814 | +| [@thomasmaerz](https://github.com/thomasmaerz) | #12834 | +| [@tuandinh0801](https://github.com/tuandinh0801) | #11454 | +| [@turbolego](https://github.com/turbolego) | #11621, #11762, #11772, #11774, #11781, #12216 | +| [@Tushar49](https://github.com/Tushar49) | #11392 | +| [@ujjawalkaushik1110](https://github.com/ujjawalkaushik1110) | #11863 | +| [@vermasomesh835](https://github.com/vermasomesh835) | #11794 | +| [@vsd2807](https://github.com/vsd2807) | #11565, #11619 | +| [@wahidsadik371-coder](https://github.com/wahidsadik371-coder) | #12126 | +| [@watchingdogs](https://github.com/watchingdogs) | #12031 | +| [@wildcard](https://github.com/wildcard) | #11620 | +| [@xiaoyaner0201](https://github.com/xiaoyaner0201) | #11460, #11662, #12083 | +| [@Xxx91n](https://github.com/Xxx91n) | #11690 | +| [@yourspraveen](https://github.com/yourspraveen) | #11146 | +| [@yxyxy](https://github.com/yxyxy) | #11671 | +| [@ZaimMarzuki](https://github.com/ZaimMarzuki) | #11960 | +| [@Zartharas](https://github.com/Zartharas) | #11340 | +| [@zero-executioner](https://github.com/zero-executioner) | #11631 | +| [@diegosouzapw](https://github.com/diegosouzapw) | maintainer | + +--- ## [3.8.50] — 2026-08-25 _Living section — regenerated 2026-08-12 from all cycle commits (cycle open `ed2db6cb19` → tip). Bullets carry the merged PR and its author; direct pushes listed separately._ diff --git a/docs/i18n/fi/CHANGELOG.md b/docs/i18n/fi/CHANGELOG.md index 597ceb5f78..60530fa430 100644 --- a/docs/i18n/fi/CHANGELOG.md +++ b/docs/i18n/fi/CHANGELOG.md @@ -8,16 +8,1119 @@ ## [3.8.51] — TBD -_Living section — cycle opened at the v3.8.50 freeze (parallel-cycle model). Bullets are aggregated from `changelog.d/` fragments at each `/generate-release` phase._ +_Living section — reconciled 2026-09-15 from all cycle commits (`091589089c` → `c0f92ec98a`, 916 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 | **123** | +| 📝 Commits in the cycle | **916** | +| 🔀 Pull requests referenced | **892** | +| 📋 Changelog entries | **919** | +| 🙌 Contributors credited in entries | **121** | +| 🤖 Automated dependency commits | 18 | + +**Entries by type** + +| Type | Count | +| --- | ---: | +| 🐛 Fixes | 592 | +| ✨ Features | 131 | +| 🧹 Chore | 93 | +| 📚 Docs | 42 | +| 🧪 Tests | 32 | +| ♻️ Refactor | 8 | +| 🏗️ Build | 7 | +| ⚡ Performance | 7 | +| ⚙️ CI | 2 | +| ⏪ Reverts | 2 | +| 📦 Dependencies | 2 | +| 🔒 Security | 1 | + +### 🏆 Top 25 contributors this cycle + +_By commits in `091589089c..c0f92ec98a`, author identities consolidated via `.mailmap` and the merged PR's GitHub login. Bots excluded._ + +| # | Contributor | Commits | +| ---: | --- | ---: | +| 🥇 | diegosouzapw | 323 | +| 🥈 | Bob.Hou (@HouMinXi) | 79 | +| 🥉 | Paco Cartones (@pacocartones) | 77 | +| 4 | Dizzle (@maxmad64bis) | 49 | +| 5 | Koosha Paridehpour (@KooshaPari) | 43 | +| 6 | Nguyen Thanh Dat (@ntdatt812) | 31 | +| 7 | Ravi Tharuma (@RaviTharuma) | 26 | +| 8 | Markus Hartung (@hartmark) | 22 | +| 9 | Webman (@jonlwheat2-gif) | 22 | +| 10 | backryun | 17 | +| 11 | anhtahaylove | 15 | +| 12 | Rafa Martins (@rafacpti23) | 10 | +| 13 | Syed Raheemuddin (@raheemuddin786) | 10 | +| 14 | Nguyễn Viết Tuấn (@TheDemonTuan) | 8 | +| 15 | MumuTW | 7 | +| 16 | Paijo (@oyi77) | 7 | +| 17 | Tobias Andersen (@turbolego) | 6 | +| 18 | Bl0ck (@Bl0ck154) | 5 | +| 19 | SHANMUGAPRIYAN (@geek007git) | 5 | +| 20 | KaspaPulse | 5 | +| 21 | Abhishek Sharma (@abhisheksharma2411) | 4 | +| 22 | Abhishek Divekar (@adivekar-utexas) | 4 | +| 23 | Andrew B. (@AndrianBalanescu) | 4 | +| 24 | NoxzRCW | 4 | +| 25 | opensource-elearning | 4 | ### ✨ New Features +- **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). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- Added Google AI Studio Gemini batch text-to-speech support through `POST /v1/audio/speech`. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- 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. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(routing):** subscription-first auto groupings — `auto/subscription` routes only through plan-included connections with a documented hard-stop overage and fails closed on exhaustion, while `auto/thrifty` orders the pool `subscription → keyless → free → cheap → premium` and steps up one rung at a time as each is exhausted. Billing class comes from a curated per-connection catalog (uncurated is treated as metered, never plan-included), both reuse STRICT_ZERO_COST's per-connection verification, and a quota reading whose `resetAt` has passed is now refreshed regardless of TTL so routing returns to plan capacity as soon as it resets ([#11146](https://github.com/diegosouzapw/OmniRoute/pull/11146)) — thanks @yourspraveen +- **feat(providers):** publish a management-authenticated versioned web-session credential contract from OmniRoute's canonical browser credential metadata ([#11340](https://github.com/diegosouzapw/OmniRoute/pull/11340)) — thanks @Zartharas +- **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)) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add Xquik X search with typed results, credential validation, REST routing, and MCP selection ([#11370](https://github.com/diegosouzapw/OmniRoute/pull/11370)) — thanks @kriptoburak +- **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)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(dashboard):** surface durable exclusive managed leases in the existing Sessions view, keeping leased clients visible across idle gaps while marking connections with in-flight work as active ([#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389)) — thanks @KaspaPulse +- **build(bun):** allow Turbopack bundler flag on Bun 1.4+ with configurable Webpack fallback ([#11471](https://github.com/diegosouzapw/OmniRoute/pull/11471)) — thanks @TheDemonTuan +- feat(api): add an opt-in `modelVisibilityAllowlist`/`modelVisibilityDenylist` settings pair to curate exactly which models `/v1/models` advertises, mirrored into every `auto/*` combo candidate pool so a denied model cannot be routed to via combo selection either (#11481) ([#11997](https://github.com/diegosouzapw/OmniRoute/pull/11997)) +- **feat(rankings):** the Free Provider Rankings page shows what each provider actually served over the last 24 h. It ranked by ELO alone, which left a provider that answers every call with an error in first place; the usage data was already served by the API but never requested. A provider with too small a sample shows a dash, not a number ([#11546](https://github.com/diegosouzapw/OmniRoute/pull/11546)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **feat(guardrails):** enforce a bounded, deterministic contract for Video Bridge transcripts — 256 cues, 4096 input code units and 4 KiB UTF-8 per cue, 64 KiB total text, malformed-Unicode rejection, focus-window scoping, cross-source reconciliation with contributing-source metadata, and a structural provenance trust boundary so caller JSON can never self-assert `embedded`/`audio-bridge` provenance ([#11652](https://github.com/diegosouzapw/OmniRoute/issues/11652)) ([#12009](https://github.com/diegosouzapw/OmniRoute/pull/12009)) +- **feat(video):** orchestrate optional Video Bridge audio extraction and Audio Bridge STT behind a dual opt-in (operator setting AND per-request signal) — a new loopback-only broker `mode=audio` operation shares the frame path's exact process queue, deadline, AbortSignal, and byte budgets to extract a bounded mono 16 kHz PCM WAV from the same already-downloaded video, then reuses the existing Audio Bridge transcription boundary; provider segment timing is preserved when available and marked coarse otherwise, and every failure degrades to a visual-only-safe partial instead of throwing (#11654). ([#12012](https://github.com/diegosouzapw/OmniRoute/pull/12012)) +- Add a tenant-bound Video Bridge drill-down lifecycle on top of the existing secure cache substrate: opaque hashed handles (never raw session/video identifiers), preview/standard/detail multiresolution variants resampled on read, response pagination capped at 8 frames and 32 MiB, and a new authenticated `/api/v1/video-bridge/drilldown` consumer route that stays disabled for remote access by default and denies cross-key access with the same response as a nonexistent handle (no existence oracle). ([#12006](https://github.com/diegosouzapw/OmniRoute/pull/12006)) +- **test(video):** Add the Video Bridge FU-07/FU-09 promotion-evidence harness (#11656) — a frozen Zod manifest schema covering the 8 required scenario kinds (static scenes, rapid cuts, late facts, fades, blur, small text, close events, visual prompt injection) with a minimum of 3 repetitions per case, deterministic declarative fixture recipes (`videoBridgePromotionFixtures.ts`), a pure medians/p95 metrics aggregator, a pure FU-07/FU-09 promotion-verdict evaluator applying the ticket's exact thresholds (missing token usage always holds), a digest-only persistence layer that never retains raw media or raw model responses, and a versioned per-model promotion allowlist shipped empty with every model defaulting to `hold`. The FU-07/FU-09 promotion verdicts themselves remain HOLD — they require a real evidence run against real models on VPS 192.168.0.15. ([#12008](https://github.com/diegosouzapw/OmniRoute/pull/12008)) +- **feat(video bridge):** "embedded" transcript provenance can now be legitimately earned instead of merely asserted — a bounded, allowlisted (`mov_text`/`subrip`/`webvtt`) subtitle probe runs through the loopback-only Video Bridge broker (at most 2 streams, 10s subdeadline bounded by the request deadline, 256 KiB output, 4096-code-unit lines), normalized through a bounded, ReDoS-safe WebVTT parser and Zod-validated end to end. The adapter always resolves to an explicit `success`/`absent`/`transient_failure` outcome — a subtitle failure never breaks the visual description path, and only a fingerprint-verified broker response (never a caller-declared label) can produce embedded cues (#11659). ([#12011](https://github.com/diegosouzapw/OmniRoute/pull/12011)) +- Default new Antigravity-family connections (agy CLI imports and Antigravity OAuth connects) to model auto-sync, so live model discovery lands in the synced catalog and `/v1/models` picks up freshly released upstream models (e.g. Gemini 3.7 Flash tiers) without code changes. Existing connections keep their current setting; the per-connection dashboard toggle remains the opt-out. (#11685 — thanks @MumuTW) ([#11887](https://github.com/diegosouzapw/OmniRoute/pull/11887)) +- **feat(ui):** enable React Compiler (`reactCompiler: true` + `babel-plugin-react-compiler`) for automatic memoization at build time ([#11783](https://github.com/diegosouzapw/OmniRoute/pull/11783)) — thanks @jonlwheat2-gif +- **feat(providers):** add SeekAi (`seekai.cc`) as an OpenAI-compatible New-API gateway — catalog id `seekai` (alias `ska`), `https://seekai.cc/v1`, live `/v1/models` via `passthroughModels`, aggregator-list membership so New-API balance detection can opt in. No referral/aff codes. ([#11786](https://github.com/diegosouzapw/OmniRoute/issues/11786)) ([#12557](https://github.com/diegosouzapw/OmniRoute/pull/12557)) — thanks @HouMinXi +- **feat(zai):** add GLM-5.3-Flash Coding Plan support (1M context, 128K output, vision, `low|high|max` reasoning) and route `zai` GLM-5.3-family API-key traffic through the OpenAI-compatible Coding Plan endpoint with native thinking defaults ([#11801](https://github.com/diegosouzapw/OmniRoute/pull/11801)) — thanks @Neuron-Mr-White +- **feat(combo):** choose how combo models are ordered — manual, provider, score, or name — via a sort control in the dashboard builder, persisted in `config.modelSort` and re-applied on load and after add ([#11812](https://github.com/diegosouzapw/OmniRoute/pull/11812)) — thanks @maxmad64bis +- **feat(free):** custom models can be marked free-tier via `customModels[].isFree`; `isFreeModel()` is the first door and `hidePaidModels` respects it even for providers outside the free budget ([#11843](https://github.com/diegosouzapw/OmniRoute/pull/11843)) — thanks @maxmad64bis +- **feat(nodejs):** add `5dive` as a `configure` target — `omniroute configure 5dive` / `omniroute setup-5dive` write a 5dive auth profile that points an agent fleet's `claude` seats at OmniRoute, with the root-only write, the loopback-vs-`https` endpoint rule and the per-seat model pin handled explicitly ([#11852](https://github.com/diegosouzapw/OmniRoute/pull/11852)) — thanks @5dive-bot +- **feat(sse):** treat `max` as a first-class reasoning-effort tier and clamp per model family (GLM 5.1+/DeepSeek V4+/Kimi K3+ keep native `max`; o1/MiniMax/Grok/Muse Spark clamp to their upstream ceiling) ([#11875](https://github.com/diegosouzapw/OmniRoute/pull/11875)) — thanks @Chewji9875 +- **feat(providers):** the provider plugin manifest now advertises a `usage-fetch` capability for the 40 providers that have a wired usage/quota fetcher, so external dashboards can read it from `GET /api/v1/provider-plugin-manifest` instead of parsing `open-sse/services/usage.ts` after every release. Discovery only — no new fetcher, no quota change, and the Dashboard quota widget stays gated by `USAGE_SUPPORTED_PROVIDERS`. `USAGE_FETCHER_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/fetcherProviders.ts`) and is re-exported from `services/usage.ts`, keeping the manifest module a light leaf instead of pulling the ~490-module usage dispatcher into the manifest route. ([#11903](https://github.com/diegosouzapw/OmniRoute/pull/11903)) — thanks @maxmad64bis / @pacocartones +- **feat(plugins):** `OMNIROUTE_PLUGINS_DIR` sets the directory the runtime plugin scanner reads — and the root the plugin manager installs into — overriding the `HOME`-derived default, so a Docker/K8s deployment can point straight at its bind-mounted plugin tree instead of moving `HOME` just to relocate the scan path. An image that exports no home no longer scans `/tmp/.omniroute/plugins` in silence: the resolved directory is logged once at startup as `scanner.dir_resolved`, naming the input that won. Unset, behaviour is unchanged. Distinct from the CLI-only `OMNIROUTE_PLUGIN_PATH`, which finds `omniroute-cmd-*` command packages and never reached this scanner ([#11906](https://github.com/diegosouzapw/OmniRoute/pull/11906)) — thanks @amaleta / @pacocartones +- **feat(leases):** add an explicit owner-authenticated status action that returns only the active lease's privacy-safe configured connection and provider labels, with generation fencing and no credential or internal-id disclosure ([#11910](https://github.com/diegosouzapw/OmniRoute/pull/11910)) — thanks @KaspaPulse +- **feat(providers):** import-from-file modal shows per-row API errors and ships a downloadable CSV template ([#12071](https://github.com/diegosouzapw/OmniRoute/issues/12071)) ([#12504](https://github.com/diegosouzapw/OmniRoute/pull/12504)) — thanks @HouMinXi +- **feat(providers):** dashboard search matches connection name and `baseUrl` so imported OpenAI-compat nodes surface on the provider card ([#12108](https://github.com/diegosouzapw/OmniRoute/issues/12108)) ([#12495](https://github.com/diegosouzapw/OmniRoute/pull/12495)) — thanks @HouMinXi +- **feat(routing):** add a `score` Auto router strategy that selects the highest configured weighted score and reuses `explorationRate`. ([#12155](https://github.com/diegosouzapw/OmniRoute/pull/12155)) — thanks @jacobsparts +- Add a runtime feature flag to disable universal context handoffs globally without changing the default behavior. ([#12167](https://github.com/diegosouzapw/OmniRoute/pull/12167)) — thanks @jacobsparts +- **feat(providers):** the provider plugin manifest now also advertises a `usage-supported` capability for the 46 providers whose usage API is accepted by the server and Dashboard routes, so integrators can distinguish "the server will serve quota for this provider" from "a fetcher is wired" without reading TypeScript. Discovery only — no fetcher or quota change. `usage-fetch` resolves on id or alias (the usage dispatcher accepts both); `usage-supported` resolves on id alone, matching the runtime guard `USAGE_SUPPORTED_PROVIDERS.includes(providerId)`. `USAGE_SUPPORTED_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/supportedProviders.ts`) and is re-exported from `providers.ts`, mirroring the `fetcherProviders` leaf from #11903 and keeping the manifest a light module. ([#12214](https://github.com/diegosouzapw/OmniRoute/pull/12214)) — thanks @maxmad64bis +- **feat(rankings):** order Free Provider Rankings by what each provider actually served — `GET /api/free-provider-rankings?sortBy=reliability` and a "Most reliable first" toggle on the page. Providers with too few calls to state a success rate keep their score order below the measured ones; the default order is unchanged ([#12218](https://github.com/diegosouzapw/OmniRoute/pull/12218)). — thanks @maxmad64bis +- **perf(sse):** defer `cloneLogPayload()` in the structured SSE collector until after the `maxEvents`/`maxBytes` cap check, eliminating ~9,800 wasted `structuredClone` calls per streaming response (65–71% faster `push()`). Reducer snapshot isolation restored for OpenAI and Responses summaries ([#12241](https://github.com/diegosouzapw/OmniRoute/pull/12241)) — thanks @PauloHSOliveira ([#12243](https://github.com/diegosouzapw/OmniRoute/pull/12243)) +- **feat(usage):** Devin CLI agentic quota (Codeium seat-management GetUserStatus) and OpenRouter key limits plus account credits now surface in Provider Limits ([#12256](https://github.com/diegosouzapw/OmniRoute/pull/12256) — thanks @Neuron-Mr-White) +- **feat(settings):** persist `headroomUrl` through Settings so status/start use the operator URL instead of only `HEADROOM_URL` ([#12306](https://github.com/diegosouzapw/OmniRoute/issues/12306)) ([#12487](https://github.com/diegosouzapw/OmniRoute/pull/12487)) — thanks @HouMinXi +- **feat(auto-combo):** Auto-Combo scoring can now weigh how often a provider/model has actually succeeded. The engine already carried that number on every candidate — 24 hours of usage history behind a ten-sample floor, real-time metrics otherwise — and the scoring function never read it, while the weight table described `stability` as if it did. `reliability` (`1 - failureRate`, with the same field precedence and the same rate-bounding the speed ranking already uses, so a corrupt reading means "nothing observed" rather than "fails every call") is now a declared factor shipping at weight `0`, so routing is unchanged until an operator gives it one, and the `stability` description now matches what that factor computes ([#12317](https://github.com/diegosouzapw/OmniRoute/pull/12317)) — thanks @maxmad64bis +- **feat(routing):** With `freeAccessPolicy: "strict"`, the read-only candidate listing (`GET /v1/auto-combo/{channel}/candidates`) no longer hides the candidates the zero-cost guard excludes — the same read-only transparency the resilience filter already honours (#9133). Each candidate now carries `freeAccessExclusion` saying why it would be kept out, and it tells an exhausted allowance apart from a quota reading that never arrived or went stale, which used to look identical from the outside. Routing is unchanged: the listing reports, it never enforces. The separate `excludeTosAvoid` guard still drops its candidates without a reason; that gap is now documented rather than closed ([#12319](https://github.com/diegosouzapw/OmniRoute/pull/12319)) — thanks @maxmad64bis +- **feat(radar):** The Radar catalog table now shows two facts it was already receiving from the feed and dropping on the floor: the per-model rate limits (requests and tokens, per minute and per day) in a new column, and a badge when a provider's terms state it may train on the prompts you send. A limit of zero renders as zero rather than "rate-only" — for a ceiling those are opposite facts — and a model with no training statement gets no badge, because an absent statement is not a guarantee ([#12320](https://github.com/diegosouzapw/OmniRoute/pull/12320)) — thanks @maxmad64bis +- **feat(radar):** explain Community, single-use, contributor, supporter, recovery, abuse, offers, and privacy rules before either Radar activation action, and remove the superseded fixed-PR grant promise from every UI locale ([#12342](https://github.com/diegosouzapw/OmniRoute/pull/12342)) +- **feat(gamification):** the dashboard Profile page now shows the real daily streak — `/api/gamification/level` returns `streak: { current, longest }` (per key with `apiKeyId`, operator-wide maximum otherwise) and the streak card reads it instead of a hard-coded 0 (#2403) ([#12377](https://github.com/diegosouzapw/OmniRoute/pull/12377)) — thanks @pacocartones +- **feat(gamification):** the dashboard leaderboard now shows each API key's display name under the Name column instead of a truncated key id; `GET /api/gamification/leaderboard` attaches `name` per entry (name only — no key material), while the shared ranking helper and the federation leaderboard stay id-only — thanks @pacocartones ([#12385](https://github.com/diegosouzapw/OmniRoute/pull/12385)) +- **feat(gamification):** enforce the documented 1000 XP/min per-API-key anti-cheat rate limit on the XP award path; over-limit awards are logged and skipped instead of persisted, and the sliding window now matches the timestamp format stored in `xp_audit_log` ([#2403](https://github.com/diegosouzapw/OmniRoute/issues/2403)) ([#12390](https://github.com/diegosouzapw/OmniRoute/pull/12390)) — thanks @pacocartones +- **feat(admin):** localize the gamification anomalies page — the loading state, the Status column and the Suspicious badge now come from the `common` catalog (new `common.suspicious` key propagated to every locale) — add it to the Gamification sidebar group as `gamification-admin` (`/dashboard/gamification/admin`), and expose the loading and empty states as polite `role="status"` live regions (#12401 — thanks @pacocartones) +- **feat(providers):** skip GitHub combo members missing from the live synced catalog, and drop Copilot models that are policy-disabled or hidden from the model picker ([#12473](https://github.com/diegosouzapw/OmniRoute/pull/12473)) — thanks @RaviTharuma +- **feat(i18n):** locale-expansion tooling — `npm run i18n:add-locale` adds a language to config, dashboard, docs mirrors, CLI, README/indexes and site in one command; browser, CLI and cookie detection resolve `uk`, `fil`/`tl`, `zh-Hant` (and the retired legacy `in` → `id`) via config aliases; `config/i18n.json` now ships in the npm package so the published CLI can read it; new real-translation ratio gate (`npm run i18n:check-ratio`, advisory) with a per-locale ratchet baseline; `run-translation --adopt` restores `.i18n-state.json`; `sync-language-bars` generates 🌐 bars from config; `validate_translation.py` loads its allowlist again. Retires the duplicate `in` locale (Indonesian mislabelled as Hindi) — 42 honest locales; saved `NEXT_LOCALE=in` / `OMNIROUTE_LANG=in` keep working. (#12496) +- **feat(api):** Emit gateway-measured `tokens_per_second` (TTFT excluded) on streaming usage and `X-OmniRoute-Tokens-Per-Second` when first-token latency is known ([#12616](https://github.com/diegosouzapw/OmniRoute/issues/12616)) ([#12631](https://github.com/diegosouzapw/OmniRoute/pull/12631)) — thanks @RaviTharuma +- **feat(opencode-plugin): map gateway cost/usage/tok/s onto OpenCode inference payloads** — the official plugin copies `X-OmniRoute-Response-Cost`, token counts, `X-OmniRoute-Tokens-Per-Second` / `usage.tokens_per_second`, TTFT, and the winning `X-OmniRoute-Model` onto the JSON/SSE body OpenCode already consumes. Missing tok/s is left unset (never `tokens / latency`). (#12636) — thanks @RaviTharuma +- **feat(combo):** add combo strategy `quota-weighted`: skip exhausted accounts, then weighted-draw by leftover / in-flight load; existing conversations stay pinned until the account is empty ([#12789](https://github.com/diegosouzapw/OmniRoute/pull/12789)) — thanks @HouMinXi +- **feat(models):** Account live listings become the chat catalog source for Claude, Codex, Copilot, and AGY; public metadata only fills prices on IDs those accounts already list. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **fix(models):** Union `agy` and `antigravity` live catalogs so `agy/gemini-3.8-flash-high` is not rejected after the prefix folds to `antigravity`. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **feat(opencode):** Add `@omniroute/opencode-plugin-v2`, an opencode v2 plugin that gives a v2 install a route to an OmniRoute gateway: it publishes models, combos and auto-combos into the host catalog, refreshes them lazily behind a 300s TTL, and keeps serving the last known catalog from an on-disk snapshot when the gateway is unreachable. The v1 plugin (`@omniroute/opencode-plugin`) is untouched and keeps working as before. The plugin adapts to the host instead of assuming one shape: it reads the shape the host seeds into the catalog draft and publishes the `api` block, the pre-`api` top-level fields, or both. The gateway key comes from the host credential store when one is connected, so no secret needs to sit in a config file; a source the gateway refuses is named in a warning instead of silently degrading the catalog to raw model ids; and tool schemas bound for a Gemini model are stripped of the JSON-Schema keywords Gemini rejects, so tool calling survives that route ([#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870)) — thanks @maxmad64bis +- feat(services): show sanitized CLIProxyAPI account health from its authenticated management API without exposing credentials, file paths, or raw account metadata (#6342) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add AnySearch free web search + URL extract (webFetch) with typed results, credential validation, REST routing, and MCP selection - fallback-only ([#11690](https://github.com/diegosouzapw/OmniRoute/pull/11690)) — thanks @Xxx91n +- **feat(dashboard):** display clamped `[0, 100]%` cached input token ratio in request logs table ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) — thanks @b3nw +- **feat(catalog):** add `OMNIROUTE_DISABLE_THINKING_LEVEL_VARIANTS` feature flag to optionally filter out thinking level variants from model catalog ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) — thanks @b3nw +- **feat(dashboard):** continuously export call logs to external analytics stores. A pluggable destination registry ships the full Logs-tab record set on an hourly `JobRegistry` cron, with a persisted per-destination cursor, batched inserts, a config UI rendered from each destination's own field descriptors, and a REST layer (`/api/log-export/*`) for CRUD, a connection test, and an on-demand run. A destination can opt into `includeBodies` to also ship the request and response payloads shown in the Logs detail pane, including the client and provider views of each call; this is off by default, and payloads inherit the dashboard's PII sanitisation, secret redaction and `noLog` handling. Google BigQuery is the first destination, using a service-account key stored encrypted at rest and streaming inserts keyed by call-log id, into a table that is day-partitioned on `timestamp` and clustered on `api_key_name`, `provider`, `model` and `status`. ([#11945](https://github.com/diegosouzapw/OmniRoute/pull/11945)) — thanks @dpozimski +- **feat(providers):** add MaxAI as a signed, OpenAI-compatible provider serving its 13 paid chat models (GPT-5.6 / Luna / Thinking, Claude 5 Sonnet, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite, Grok 4.1-fast / 4.5, DeepSeek V3.2 / R1, Llama 3.3 70B) through OmniRoute's `/v1` endpoint, with per-request HMAC-SHA1→SM3→AES request signing, live model + context-window discovery from `/models/get_config`, and prompted tool-calling translated to OpenAI `tool_calls` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI vision input — image_url content parts are forwarded inline in `message_content` to the 6 vision-capable models (GPT-5.6 / Luna / Thinking, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite) ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI image generation — 6 image models (gpt-image-1, dall-e-3, flux-1-schnell/dev/pro, sd3-medium) exposed through `POST /v1/images/generations` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI document RAG — inline base64 file/document attachments are uploaded to MaxAI (content-addressed `doc_id`) and attached to the chat via `doc_list` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** browserless MaxAI onboarding — email device-pair login (`/api/providers/[id]/login`) and signed access-token refresh, so a connection can be created and kept fresh without a real browser or Google OAuth ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** per-provider TLS impersonation profile (MaxAI presents a Windows Firefox-150 client fingerprint) so its bot-sensitive endpoints accept OmniRoute traffic ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** add **Nimble** as a web-search and web-fetch provider (`nimble-search`) — `/v1/search` routes to Nimble's search API at `lite` depth (locale, freshness and include/exclude domain filters mapped onto the shared request contract), and `/v1/web/fetch` routes to Nimble Extract, which covers all four fetch formats (`markdown`, `html`, `links`, `screenshot`) from a single call. One API key serves both surfaces. ([#11620](https://github.com/diegosouzapw/OmniRoute/pull/11620)) — thanks @wildcard +- The npm publish is automatic again, through npm Trusted Publishing (OIDC): the hosted `stage-npm` job publishes with a short-lived credential minted from GitHub's id-token — no `NPM_TOKEN`, no 2FA prompt, provenance attached. `publish_mode=staged` (owner approves with 2FA) and `direct` (token) remain available on `workflow_dispatch`. ([#11931](https://github.com/diegosouzapw/OmniRoute/pull/11931)) +- **feat(providers):** Add **Opper** as an API-key gateway provider — EU-hosted AI gateway with 700+ models from 30+ providers behind one OpenAI-compatible API and one key (`OPPER_API_KEY`); model ids use `provider/model` format (e.g. `anthropic/claude-sonnet-4-6`, `openai/gpt-5`); live model catalog at `https://api.opper.ai/v3/compat/models`; entry mirrors `requesty` (same shape, `passthroughModels: true`, no static seed) ([#11629](https://github.com/diegosouzapw/OmniRoute/pull/11629)) +- **feat(dashboard):** the `/dashboard/orchestration` snapshot hook now subscribes to the `agents` WebSocket channel (`agent.task.updated`) instead of `requests` as its refetch trigger, and relaxes its background poll from 5s to 30s while that WS connection is up — falling back to the tighter 5s cadence, reprogrammed live on any connect/disconnect transition, whenever the socket is down. ([#12409](https://github.com/diegosouzapw/OmniRoute/pull/12409)) +- **feat(dashboard):** Orchestration canvas quick wins — search box plus state/source/provider filter chips with a one-click clear, and per-source collapse/expand, all reflected in the URL so a filtered/collapsed view is shareable and survives a refresh; the detail drawer gained a "copy trace JSON" action and hardened error/empty-state and accessibility handling; the Agents-tab edges now animate traveling particles along active (running) connections; and the canvas node/edge status colors moved off fixed hex values onto theme-aware `--orch-status-*` CSS custom properties, so they adapt correctly to light/dark mode. ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- **chore(dashboard):** Orchestration UI hardening pass and the missing component/model test coverage it called for — `OrchestratorNode`/`ActivityNode`/`OverflowNode` rendering, the `?node=`/overflow-click page routing, the Agents-tab orchestrator-click no-op and `showCompleted` toggle, and the overview kanban's done-column sort order (#12270, #12271). ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- New `/dashboard/orchestration` page: live unified view of everything running — Cloud Agent, A2A and Conductor as a real-time graph (Agents tab), the combo cascade (Routing tab, reusing the Combo Live Studio) and a state kanban (Overview tab), with a detail drawer (trace, cost, approve/cancel). Read-only over existing APIs — no new backend. Canvas concept credit: PR #11815 design ([#12261](https://github.com/diegosouzapw/OmniRoute/pull/12261)) +- **feat(dashboard):** new "History" tab on `/dashboard/orchestration` — an Airflow-style grid of finished runs over a 24h/7d/30d preset window, one row per (source, identity), clicking a cell opens the existing detail drawer. It is backed by real persistence: A2A task lifecycle transitions are now written to the `a2a_tasks` table (purged after 30 days, configurable via `OMNIROUTE_A2A_HISTORY_RETENTION_DAYS`) and served by the new `GET /api/a2a/tasks/history` listing endpoint, with the task-detail route falling back to persisted history once a run leaves the in-memory snapshot. Conductor runs stay remote and are not persisted locally — the tab says so instead of silently omitting them. ([#12479](https://github.com/diegosouzapw/OmniRoute/pull/12479)) +- **feat(dashboard):** the orchestration detail drawer gained a "Repeat" action for Cloud Agent, A2A and Conductor tasks — a two-click confirm (click once to arm, click again within the confirm window to fire) re-submits the original prompt/input as a new run. The button is disabled with an explanatory tooltip whenever the original input can't be recovered from the loaded task detail (e.g. it never carried a prompt, or the detail failed to load). Two limitations of the A2A variant, by design: it targets the `/a2a` JSON-RPC endpoint, which authenticates with an API key only (`REQUIRE_API_KEY=true` or a configured `OMNIROUTE_API_KEY` makes a dashboard-session repeat answer `HTTP 400` — surfaced verbatim in the drawer's error line, never as a success), and the `message/send` call is SYNCHRONOUS: the POST blocks for the whole skill run, so the success confirmation only appears once the repeated task finishes. A dashboard-authenticated A2A creation path is deliberately left to a follow-up — widening the endpoint's auth posture is an operator decision, not a side effect of this feature. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(a2a):** A2A task execution now records which memories were consulted for the task's last user message as `metadata.memoryHits` (id/key/type/content-snippet) plus a `memory_hits` history event, purely for observability — the retrieved memory is never injected into a skill's prompt or behavior. Gated by the `OMNIROUTE_A2A_MEMORY_HITS` kill-switch (default enabled; set to `0` to skip the recall lookup entirely). The drawer's new "Memory used" section lists these hits for a2a tasks and is omitted whenever there are none. Known limitation: recall only resolves under the keyless posture — a keyed caller's task owner is a SHA-256 prefix of the API key, while memory rows are keyed by the database api-key id, and no hash→id lookup exists today, so the hit list stays empty for keyed callers. The recorded hits are also kept out of the task's own `input` (and therefore out of the persisted input and of the "Repeat" request body), so repeating a task never re-sends the previous run's memory snippets. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(providers):** add a Perplexity Agent API provider (`perplexity-agent` / `pplx-agent`) for Perplexity `/v1/responses`, including the documented Anthropic, OpenAI, Google, xAI, DeepSeek, Z.AI, Moonshot/Kimi, NVIDIA, and Perplexity model IDs plus Anthropic-model `max_output_tokens` compatibility. ([#12103](https://github.com/diegosouzapw/OmniRoute/pull/12103)) — thanks @AIB1TAL0S +- **feat(providers):** add RPD (Requests Per Day) limit to provider rate limit overrides across UI, schemas, DB, and i18n ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) — thanks @b3nw +- **feat(providers): add UC Direct (uncensored.com Developer API), the metered OpenAI-compatible surface.** A standard OpenAI-compatible passthrough (default executor) for uncensored.com's official REST API at `https://api.uncensored.com/api/v1`: `X-api-key` auth (never-expiring `uai_sk_live_` key), `POST /chat/completions` with streaming SSE and native tool-calling, and the full live metered catalog (82 models across 15 providers, discovered from the public `GET /v1/models`). Registered as provider `uc-direct` (alias `ucd`). Complements the un-metered `uc` persona provider — same models, metered credits and a plain API key instead of a subscription session. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(providers): add UC (uncensored.com), the un-metered subscription "persona" chat as an OpenAI-compatible provider.** A WebSocket web-app port: a durable Clerk credential mints a short-lived session token per connect (browserless — no API key), driving UC's persona socket. Ships the browserless email-code login (request → verify → harvest), the 19 verified persona models (Claude Opus, Gemini, Grok, GLM, Kimi, DeepSeek, MiniMax, incl. the uncensored variants), prompted `` tool-calling with a per-model code-style dialect + auto-cure retry for guardrailed models, live ``/reasoning split, streaming + non-streaming OpenAI responses, and full quota/auth error surfacing (paywall / message-limit / rate-limit → 429, invalid session → 401 re-login). Full multimodal parity via the persona blob-upload layer: **vision** (image input, 15 vision-capable models), **document RAG** (PDF/doc upload, server-side extraction), **image generation** (22 models), **video generation** (14 models, async signed-url → poll), and **TTS** (streaming MP3). Registered as provider `uc` (alias `ucn`). The metered OpenAI-compatible Developer API is a separate `uc-direct` provider. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(logs):** add copy-all button to request detail modal ([#11083](https://github.com/diegosouzapw/OmniRoute/pull/11083)) — thanks @AndrianBalanescu +- **feat(check):** make serviceKinds required and add the reverse-walk provider consistency gate ([#11392](https://github.com/diegosouzapw/OmniRoute/pull/11392)) — thanks @Tushar49 +- **feat(providers):** only advertise verified-working connections by default ([#11448](https://github.com/diegosouzapw/OmniRoute/pull/11448)) — thanks @alltomatos +- **feat(resilience):** add hierarchical concurrency admission ([#11493](https://github.com/diegosouzapw/OmniRoute/pull/11493)) +- **feat(providers):** derive + surface expiry for JWT-bearing web cookies (#11497) ([#11505](https://github.com/diegosouzapw/OmniRoute/pull/11505)) — thanks @oyi77 +- **feat(tunnels):** support named cloudflared tunnels via CLOUDFLARED_CONFIG ([#11561](https://github.com/diegosouzapw/OmniRoute/pull/11561)) — thanks @ggiak +- **feat(compression):** make proactive context-compression threshold a live setting ([#11564](https://github.com/diegosouzapw/OmniRoute/pull/11564)) — thanks @ragnar-claude +- **feat(resilience):** add provider peak-hour protection ([#11622](https://github.com/diegosouzapw/OmniRoute/pull/11622)) — thanks @Neuron-Mr-White +- **feat(providers):** add 1min.ai provider ([#11631](https://github.com/diegosouzapw/OmniRoute/pull/11631)) — thanks @zero-executioner +- **feat(dashboard):** collapsible JSON tree viewer for request/response payloads ([#11703](https://github.com/diegosouzapw/OmniRoute/pull/11703)) — thanks @hartmark +- **feat(routing):** add LiquidAI LFM2.5-2.6B free tier via OpenRouter ([#11752](https://github.com/diegosouzapw/OmniRoute/pull/11752)) — thanks @brick30llc-ctrl +- **feat(pricing):** add GLM-5.3-Flash pricing, model specs, and catalog registration ([#11830](https://github.com/diegosouzapw/OmniRoute/pull/11830)) — thanks @AndrianBalanescu +- **feat(routing):** add relayMode for schema-locked context handoffs ([#11839](https://github.com/diegosouzapw/OmniRoute/pull/11839)) — thanks @raheemuddin786 +- **feat(config):** add support for runtime system prompt configuration and hot-reloading ([#11841](https://github.com/diegosouzapw/OmniRoute/pull/11841)) — thanks @raheemuddin786 +- **feat(routing):** expand connection-aware quota prefilter across combo strategies (#11682) ([#11850](https://github.com/diegosouzapw/OmniRoute/pull/11850)) — thanks @HouMinXi +- **feat(models):** reactive discovery sync after Antigravity model-not-found 404 ([#11888](https://github.com/diegosouzapw/OmniRoute/pull/11888)) — thanks @MumuTW +- **feat(models):** auto-aliases and token limits from synced Antigravity discovery ([#11889](https://github.com/diegosouzapw/OmniRoute/pull/11889)) — thanks @MumuTW +- **feat(settings):** raise sticky round-robin limit caps to 1000 ([#12015](https://github.com/diegosouzapw/OmniRoute/pull/12015)) — thanks @adivekar-utexas +- **feat(resilience):** operator-configurable global credential health check interval ([#12043](https://github.com/diegosouzapw/OmniRoute/pull/12043)) — thanks @adivekar-utexas +- **feat(quota):** use official OpenCode Go usage API ([#12124](https://github.com/diegosouzapw/OmniRoute/pull/12124)) — thanks @ddarkr +- **feat(resilience):** default the credential health check sweep to 60 minutes ([#12138](https://github.com/diegosouzapw/OmniRoute/pull/12138)) — thanks @adivekar-utexas +- **feat(quality):** new-code mode for the complexity and dead-code ratchets (Clean as You Code) ([#12142](https://github.com/diegosouzapw/OmniRoute/pull/12142)) +- **feat(dashboard):** orchestration canvas — unified model + snapshot hook (part 1/2) ([#12156](https://github.com/diegosouzapw/OmniRoute/pull/12156)) +- **feat(usage):** add Kilo Code balance and Kilo Pass quotas ([#12178](https://github.com/diegosouzapw/OmniRoute/pull/12178)) — thanks @killer30001000 +- **feat(providers):** refresh vendored ChatGPT Web connector to v4.0.7 ([#12181](https://github.com/diegosouzapw/OmniRoute/pull/12181)) — thanks @backryun +- **feat(providers):** manual "Clear cooldown" action in the cooling panel ([#12224](https://github.com/diegosouzapw/OmniRoute/pull/12224)) — thanks @patrykkopycinski +- **feat(providers):** restore ChatGPT Web via clean-room browser transport ([#12239](https://github.com/diegosouzapw/OmniRoute/pull/12239)) — thanks @backryun +- **feat(sse):** wire the PROVIDER_PROFILES window gate into the global provider cooldown ([#12247](https://github.com/diegosouzapw/OmniRoute/pull/12247)) +- **feat(providers):** modernize CLOVA Studio chat and embeddings ([#12277](https://github.com/diegosouzapw/OmniRoute/pull/12277)) — thanks @backryun +- **feat(browser):** adopt Obscura as primary headless browser engine with Chromium fallback ([#12286](https://github.com/diegosouzapw/OmniRoute/pull/12286)) — thanks @opensource-elearning +- **feat(sse):** full language parity for output styles (es/de/fr/it/ru/zh + autoDetect) ([#12289](https://github.com/diegosouzapw/OmniRoute/pull/12289)) +- **feat(quality):** complete test:scoped — --full map rebuild, stdin selection, CI loader parity (#8084 D1) ([#12353](https://github.com/diegosouzapw/OmniRoute/pull/12353)) +- **feat(providers):** refresh Fable, Cursor, and Devin catalogs ([#12367](https://github.com/diegosouzapw/OmniRoute/pull/12367)) — thanks @backryun +- **feat(video):** redact transcript text from logs and durable memory (#12150 P1) ([#12427](https://github.com/diegosouzapw/OmniRoute/pull/12427)) +- **feat(dashboard):** parent-link, genuine-continuation badge, and modal perf fixes ([#12448](https://github.com/diegosouzapw/OmniRoute/pull/12448)) — thanks @hartmark +- **feat(dashboard):** adaptive context-budget dial on compression panel ([#12488](https://github.com/diegosouzapw/OmniRoute/pull/12488)) — thanks @HouMinXi +- **feat(providers):** refresh Z.ai Web models and browser transport ([#12524](https://github.com/diegosouzapw/OmniRoute/pull/12524)) — thanks @backryun +- **feat(video):** redact raw client-snapshot transcript fields in the detailed log (#12150 P2) ([#12528](https://github.com/diegosouzapw/OmniRoute/pull/12528)) +- **feat(providers):** refresh NVIDIA hosted models ([#12538](https://github.com/diegosouzapw/OmniRoute/pull/12538)) — thanks @backryun +- **feat(quota):** Moonshot Open Platform balance and TPD lock for custom nodes ([#12590](https://github.com/diegosouzapw/OmniRoute/pull/12590)) — thanks @HouMinXi +- **feat(video):** redact transcript in the in-memory pending-request snapshot (#12430 item 6) ([#12596](https://github.com/diegosouzapw/OmniRoute/pull/12596)) +- **feat(dashboard):** link a log entry's Conversation Context to its owning conversation ([#12646](https://github.com/diegosouzapw/OmniRoute/pull/12646)) — thanks @hartmark +- **feat(catalog):** eligibility-gated free-tier bucket ([#12669](https://github.com/diegosouzapw/OmniRoute/pull/12669)) +- **feat(db):** fail-closed previous_response_id continuation for redacted video turns (#12150 P2b) ([#12707](https://github.com/diegosouzapw/OmniRoute/pull/12707)) +- **feat(gamification): pay the documented `streak_bonus` and `badge_unlock` XP rewards.** `XP_REWARDS` listed both rewards but the award pipeline never paid them: the private reward table in `events.ts` omitted them, `updateStreak()` did not report when a streak extended, and badge unlocks carried no XP. Every request that extends a daily streak now pays `streak_bonus × streak length` once per UTC day (guarded by a same-day `xp_audit_log` check), and every badge unlocked through the pipeline pays `badge_unlock` once per badge (guarded by the `user_badges` primary key; `unlockBadge()` now reports whether it inserted). Bonus XP flows through the same `addXp` + level sync + global/weekly/monthly leaderboard path as action XP, so level-ups and rankings include it. The Radar supporter recognition unlock stays XP-free. (#12522 — thanks @pacocartones) +- **feat(providers):** advertise a `free-tier` capability in the provider plugin manifest for every provider with documented free models, so sidecars and dashboards can filter free-capable providers without reading the quota catalog ([#12786](https://github.com/diegosouzapw/OmniRoute/pull/12786)) — thanks @maxmad64bis +- **feat(providers):** Added EURouter as an OpenAI-compatible API-key gateway (`https://api.eurouter.ai/v1`), with live model discovery via `passthroughModels`. Its copy states that models are served by third-party upstreams listed per model, so an EU-based router is not read as EU data residency for inference. ([#13025](https://github.com/diegosouzapw/OmniRoute/pull/13025)) — thanks @ntdatt812 +- **feat(providers):** Added GreenPT as an OpenAI-compatible API-key provider (`https://api.greenpt.ai/v1`), with live model discovery via `passthroughModels`. No free-inference badge: the published docs describe a free API subscription billed per token, not a free tier. ([#13024](https://github.com/diegosouzapw/OmniRoute/pull/13024)) — thanks @ntdatt812 +- **feat(i18n):** 9 new locales — Greek (`el`), Croatian (`hr`), Serbian (`sr`), Lithuanian (`lt`), Estonian (`et`), Latvian (`lv`), Slovenian (`sl`), Maltese (`mt`), Irish (`ga`) — across the dashboard, docs mirrors, CLI, README and the site; OmniRoute now ships every official EU language (51 locales). (#13044) +- **feat(i18n):** 8 new locales — Kannada (`kn`), Malayalam (`ml`), Odia (`or`), Punjabi (`pa`), Nepali (`ne`), Sinhala (`si`), Burmese (`my`), Khmer (`km`) — across the dashboard, docs mirrors, CLI, README and the site (59 locales). The translator now restores the ICU literal escape around angle placeholders and splits oversized docs sections before translating. (#0000) ([#13660](https://github.com/diegosouzapw/OmniRoute/pull/13660)) +- feat(providers): **list Agnes 3.0 Flash as the current free chat model, drop retired 1.5 Flash, add Image 2.0/2.5 Flash plus Video 2.5/2.5 Flash, and discover the live `/v1/models` catalog (including the CN host `api.agnes-ai.cn`).** `agnes-1.5-flash` now forwards to `agnes-3.0-flash`. Video 2.5 polls `GET /v1/videos/{id}` (not the V2.0 `/agnesapi` contract). Live `/v1/models` (2026-09-09) no longer serves 1.5; the wiki marks it deprecated. 3.0 Flash is 512K context / 65,536 max output, same window as 2.5. CN-region keys use the existing per-connection base-URL field, default stays `apihub.agnes-ai.com`. ([#13120](https://github.com/diegosouzapw/OmniRoute/pull/13120)) — thanks @HouMinXi +- **feat(sse):** Codex and OpenAI catalogs list GPT-6 Astra with effort aliases (`-low` through `-ultra`); Codex CLI identity pins `@openai/codex@0.153.4` in lockstep with the image ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **feat(dashboard):** the orchestration History tab gained a "Compare runs" mode — toggling it turns each grid cell into a 2-item selection queue (a 3rd click drops the oldest pick), and picking two cells opens a side-by-side comparison panel instead of the usual detail drawer. The panel fetches both runs' detail the same way the drawer does (falling back to persisted history once a run leaves the live TTL window) and shows, per side: identity/source/state, start time, a signed `right - left` delta for duration/cost/event count, the event timeline aligned by index, and any memory hits. One side's fetch failing never blocks the other, and a delta is only ever computed when both sides have a finite value — otherwise it renders "—", never `NaN`. Comparing two runs from different sources or skills still works; a banner marks the deltas as informational rather than hiding them, since the two runs aren't a strict apples-to-apples pair. ([#12677](https://github.com/diegosouzapw/OmniRoute/pull/12677)) +- **feat(dashboard):** badge a conversation that never reached a clean stop ([#12717](https://github.com/diegosouzapw/OmniRoute/pull/12717)) — thanks @hartmark +- **feat(release):** reconcile-changelog tool + version-anchored fragment aggregation ([#12987](https://github.com/diegosouzapw/OmniRoute/pull/12987)) +- **feat(mcp):** carry context_length through MCP list_models_catalog projection (#12776) ([#13406](https://github.com/diegosouzapw/OmniRoute/pull/13406)) — thanks @KooshaPari +- **feat(cli):** make startup readiness budget configurable (#13369) ([#13433](https://github.com/diegosouzapw/OmniRoute/pull/13433)) — thanks @KooshaPari + ### 🐛 Bug Fixes +- **security(streaming):** sanitize generic mid-stream error messages before emitting OpenAI, + Responses, or Claude SSE failure frames and before diagnostic logging, while preserving raw + failures for internal classification and keeping client disconnects out of provider failure state. ([#12457](https://github.com/diegosouzapw/OmniRoute/pull/12457)) +- **fix(claude):** `blockExtraUsage: false` no longer lets 5h quota preflight skip the account; extra usage is billed after the session bar is gone, so the request must reach Anthropic ([#12803](https://github.com/diegosouzapw/OmniRoute/pull/12803)) — thanks @HouMinXi +- Sanitize HuggingChat conversation-creation and message-send transport failures before they reach client error bodies or provider logs. ([#12467](https://github.com/diegosouzapw/OmniRoute/pull/12467)) +- **fix(dashboard):** Keep local and theme-aware provider SVG icons at a definite layout size so Chromium does not collapse them to 0×0 after the v3.8.50 image-rendering change ([#12054](https://github.com/diegosouzapw/OmniRoute/pull/12054)) — thanks @ponkcore +- **fix(security):** Sanitize provider and runtime failures before public API, SSE and MCP responses and before persistent request, proxy and usage logs, preventing credentials, stack traces and host filesystem paths from crossing those boundaries while preserving stable error codes and useful diagnostics. ([#12506](https://github.com/diegosouzapw/OmniRoute/pull/12506)) +- **fix(cli):** `omniroute tunnel create` no longer crashes with `Cannot read properties of undefined (reading optsWithGlobals)` — removed the duplicate positional argument that caused Commander.js to misalign the action callback parameters ([#12295](https://github.com/diegosouzapw/OmniRoute/issues/12295)) ([#12368](https://github.com/diegosouzapw/OmniRoute/pull/12368)) — thanks @gonisulaimann +- Stop advertising Gemini Live-only models as supported audio endpoints until OmniRoute proxies the bidirectional Live protocol. ([#11710](https://github.com/diegosouzapw/OmniRoute/pull/11710)) — thanks @RaviTharuma +- **fix(kie):** reroute `flux/kontext` off the KIE Market `createTask` flow — it is catalogued with `isMarket: true` but has no Market catalog page, so KIE rejected it with "model name not supported"; it now hits the dedicated `POST /api/v1/flux/kontext/generate` / `GET /api/v1/flux/kontext/record-info` endpoints instead (#11296). ([#11985](https://github.com/diegosouzapw/OmniRoute/pull/11985)) — thanks @hartmark +- **fix(kie):** correct 12 more KIE Market catalog ids that were sent to `createTask` unchanged but diverge from KIE's documented upstream `model` values — GPT Image 2 T2I/I2I (drops the `gpt/` prefix), GPT Image 1.5 T2I/I2I (`gpt-image/` namespace), Seedream 5.0 Lite T2I/I2I (drops the `.0`), all 4 Flux 2 variants (`flux-2/` namespace, generic variant renamed `flex`), and Wan 2.7 Image / Image Pro (dash instead of dot) — each verified individually against the literal example request published on docs.kie.ai. `#11326`'s "everything else already matches" claim was wrong a second time (#11296); `z-image/4.0-*`/`z-image/4.5-*` and `flux/kontext` remain open, documented as unresolved in `KIE_MARKET_UPSTREAM_MODEL_IDS`'s comment pending further verification. ([#11667](https://github.com/diegosouzapw/OmniRoute/pull/11667)) +- **fix(dashboard):** `useApiKeySave.handleSaveApiKey` no longer forces a full upstream `/models` catalog sync on every non-curated provider connection save — callers can now pass `skipModelSync: true` to opt out, so a workflow that only wants to add one manual model no longer floods the provider's available-models list with hundreds/thousands of synced entries. The flag is a client-side intent signal only and is stripped before the connection payload is POSTed to `/api/providers`; default behavior (full sync on save) is unchanged when the flag is omitted (#11324) ([#11664](https://github.com/diegosouzapw/OmniRoute/pull/11664)) +- **Provider connections:** keep `tokenExpiresAt` when a connection is created. The create-path allowlist omitted it, so every insert stored NULL and the dashboard token badge could read a fresh connection as expired until its first background refresh ([#11368](https://github.com/diegosouzapw/OmniRoute/pull/11368)). — thanks @ntdatt812 +- **fix(combos):** the combo builder's precision-select, global-model-search, and manual-entry flows now serialize a model step's `model` string using the provider's already-computed routing-alias prefix (e.g. `oc/`) instead of rebuilding it from the raw canonical `providerId`, fixing the no-auth "OpenCode Free" provider (`opencode`) being routed to the unrelated paid "OpenCode Zen" provider (`opencode-zen`) because `opencode` doubles as a manual routing-prefix override ([#11433](https://github.com/diegosouzapw/OmniRoute/issues/11433)). ([#11670](https://github.com/diegosouzapw/OmniRoute/pull/11670)) +- **fix(radar):** the catalog feed cache now keeps `generatedAt`, the date the feed's data was built, next to `fetchedAt`, the date this install downloaded it (#11435). The feed schema requires that date and the sync path validates it, but the cache dropped it — so a feed fetched minutes ago and one carrying weeks-old figures looked identical to everything downstream, including the dashboard's "Last fetched" line. `getRadarCatalog().meta` and `GET /api/radar/status` now report both dates, the latter as its own field rather than folded into `version` — and omitted entirely for the offers and intel caches, which keep no build date, where a `null` would read as "unknown" rather than "never stored". The dashboard still shows only the fetch time; surfacing the build date there needs a new translated label and is left to a follow-up. Rows cached before migration 163 read back as `null`: unknown stays unknown instead of borrowing the fetch time. The referrals cache has persisted the same date since migration 142. — thanks @maxmad64bis +- **fix(cli):** postinstall no longer fills `JWT_SECRET` and `API_KEY_SECRET` in the installed package's `.env` (#11436). `.env.example` ships both blank on purpose: the server restores them from its durable store, or generates and persists them there, in `ensureSecrets()`. Pre-filling them defeated that — the file lives inside the package directory, so `npm i -g` replaced it and postinstall wrote _different_ values, while `ensureSecrets()` (which only acts on an empty variable) never got to restore the real ones. Both secrets rotated silently on every update, invalidating dashboard sessions and API-key CRCs. `STORAGE_ENCRYPTION_KEY` left the same list for the same reason in #1622; its comment pointed at a function that no longer exists and now names the real provisioning path. — thanks @maxmad64bis +- **fix(cli):** the CLI now says when a loaded `.env` lives inside the installed package directory (#11437). It already announces every env file it reads, without distinguishing the ones that survive an update from the one that does not: `npm i -g` replaces the package directory wholesale, so values set there are gone at the next update, silently. The warning names the durable path to move them to, and fires only when that file actually supplied a value — a file entirely shadowed by a durable one supplied nothing. A development checkout stays silent: there the same path is stable and documented in `SETUP_GUIDE.md`. — thanks @maxmad64bis +- **fix(free-models):** the shared `isFreeModel()` predicate no longer reports catalog entries marked `freeType: "discontinued"` as free, so `hidePaidModels` can't route to Pollinations' seven premium models that now require a paid key ([#11441](https://github.com/diegosouzapw/OmniRoute/pull/11441)) — thanks @maxmad64bis +- fix(ui): let AnySearch use the normal provider-icon fallback when LobeHub has no matching icon (#11449) +- Fixed the v3.8.50 Costs and Analytics dashboards so flat-rate Claude Code usage can be shown as an explicitly requested token-price estimate without changing default billed-cost semantics. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed archived usage retention so each request is priced individually instead of pricing a day's summed tokens once, which understated archived cost whenever a day mixed cache-heavy and ordinary requests. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed the Costs dashboard so it discloses when displayed figures include flat-rate token-price estimates instead of labelling them as billed spend, using the flag the analytics API already returns; the month-end projection and the CSV/JSON exports carry the same marker, and billed-cost mode is unchanged. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- **fix(combo):** attach the same combo-diagnostics trace (`poolSize`/`attemptOrder`/`excluded`/`terminalReason`, plus `x-omniroute-combo-*` headers) to the round-robin strategy's and the nested pipeline/fusion runtime-unit loop's "Maximum combo retry limit reached" 503 that the priority-strategy path already attaches for the identical terminal condition — previously those two paths returned a bare, contextless 503 ([#11462](https://github.com/diegosouzapw/OmniRoute/issues/11462)). ([#11665](https://github.com/diegosouzapw/OmniRoute/pull/11665)) +- **fix(bun):** use native `bun:sqlite` in `bootstrap-env` and `sync-env` to avoid loading `better-sqlite3` N-API addon during Bun startup ([#11468](https://github.com/diegosouzapw/OmniRoute/pull/11468)) — thanks @TheDemonTuan +- **fix(docker-bun):** make `Dockerfile.bun` install reliable with pre-install script helpers and native `bun:sqlite` smoke check ([#11470](https://github.com/diegosouzapw/OmniRoute/pull/11470)) — thanks @TheDemonTuan +- **fix(docker-bun):** remove every vendored `better-sqlite3` native addon from the Bun runtime image so startup cannot abort before the `bun:sqlite` fallback ([#11482](https://github.com/diegosouzapw/OmniRoute/pull/11482)) — thanks @TheDemonTuan +- **fix(autoCombo):** effort/alias model variants (`gpt-5.6-sol-xhigh`, `gpt-5.6`, cursor's `claude-4.6-opus-high`) inherit their base model's task fitness instead of falling to the wildcard 0.5 ([#11492](https://github.com/diegosouzapw/OmniRoute/pull/11492)) — thanks @MumuTW +- fix(db): rate-limit repeated Arena ELO leaderboard fetch-failure warnings instead of logging one per sync attempt (#11500) ([#11989](https://github.com/diegosouzapw/OmniRoute/pull/11989)) — thanks @hartmark +- **fix(db):** dedupe the raw `[Encryption] Decryption failed...` log line emitted by the lazy-decrypt views (`createLazyRowProxy`/`createLazyConnectionView`), which power `getProviderConnections()` and were re-triggering that line on every CredentialHealth/model-sync cycle for the same corrupt or stale-key credential — a fresh Proxy over a fresh row on every cycle meant the per-proxy memoization never suppressed it, unlike the dedup `decryptConnectionFields()` already had since [#9927](https://github.com/diegosouzapw/OmniRoute/issues/9927). Now shares that dedupe tracking so the line logs at most once per credential ([#11500](https://github.com/diegosouzapw/OmniRoute/issues/11500)). ([#11678](https://github.com/diegosouzapw/OmniRoute/pull/11678)) +- **fix(intelligence):** Arena ELO sync stores a synthesized base-model row for every effort/harness variant (`claude-opus-5-max` → `claude-opus-5`, `gpt-5.6-sol-xhigh (codex-harness)` → `gpt-5.6-sol`), so bare model requests reach the synced intelligence layer instead of the static fallback table; the hand-maintained `MODEL_ALIAS_MAP` — which collapsed generations (`gpt-5.5` → `gpt-5`) — is gone ([#11506](https://github.com/diegosouzapw/OmniRoute/pull/11506)) — thanks @MumuTW +- **fix(autoCombo,sse):** catalog hygiene — the static task-fitness table no longer ranks retired models above live ones (dead rows dropped, patterns matched on segment boundaries), the 7 deprecation aliases that forwarded to retired or non-existent models now point at the vendors' published replacements, an alias is no longer rewritten when the serving provider offers the id as-is, and a new `check:model-lifecycle` gate diffs both tables against a vendor lifecycle snapshot ([#11507](https://github.com/diegosouzapw/OmniRoute/pull/11507)) — thanks @MumuTW +- **fix(sse):** stop dropping resolved `thoughtSignature` values on parallel (multi tool-call) turns sent to Gemini 3.x — the claude→gemini and openai→gemini translators previously kept the signature only on the *first* function call of a message, causing Gemini to reject subsequent calls in the same turn with HTTP 400 "Function call is missing a thought_signature"; each function call now keeps its own resolved signature ([#11510](https://github.com/diegosouzapw/OmniRoute/issues/11510)). ([#11669](https://github.com/diegosouzapw/OmniRoute/pull/11669)) +- Treat Docker publishing from a non-default release branch as an expected skip instead of a failed workflow, while preserving the guard that prevents it from overwriting the `next` channel. ([#11527](https://github.com/diegosouzapw/OmniRoute/pull/11527)) — thanks @pacocartones +- fix(search): prefer credentialed providers over duckduckgo-free fallback (#11524) — the fallback-only loop ran before the credentialed-providers loop in `executeWebSearch`, making configured providers unreachable when `duckduckgo-free` was available. ([#11565](https://github.com/diegosouzapw/OmniRoute/pull/11565)) — thanks @vsd2807 +- **fix(sse):** cap the upstream headers-wait phase for STREAMING requests to a client-realistic ceiling (110s, under Codex's own ~120s hard client-abort window) instead of the flat 10-minute `FETCH_TIMEOUT_MS` default — that default was 5x longer than the body-phase readiness watchdog's own adaptive bound, so a request whose upstream never returned any response at all (not even headers, e.g. a stalled NVIDIA target behind a tool-heavy Responses→Chat translation) kept the client connection alive on keepalives only, guaranteeing the client's own patience ran out first with an opaque 499 instead of OmniRoute detecting and failing the stall fast. Non-streaming requests are unaffected — they keep the existing flat default (`open-sse/utils/fetchStartTimeoutPolicy.ts`) (#11526) ([#11668](https://github.com/diegosouzapw/OmniRoute/pull/11668)) +- **fix(ci):** protocol-clients E2E harness boots the peer-stamped custom server (`run-next.mjs dev`) instead of the bare `next` CLI, so `LOCAL_ONLY` locality resolves from the real TCP peer stamp and `/api/mcp/audit` answers 200 (open bootstrap preserved via an `OMNIROUTE_E2E_BOOTSTRAP_MODE=open` credential clear in `run-next.mjs`, pinned to loopback `HOST`) instead of a deterministic 403 (#11535) ([#11549](https://github.com/diegosouzapw/OmniRoute/pull/11549)) — thanks @jonlwheat2-gif +- **fix(docs):** the Auto-Combo engine is described with the number of factors it actually declares. Four different counts were in circulation — 6, 9, 13 and 14 across `AGENTS.md`, four guides and six code comments — while `DEFAULT_WEIGHTS` declares 15. `check:docs-counts` now reads that number from the source, so adding a factor without updating the prose fails CI ([#11545](https://github.com/diegosouzapw/OmniRoute/pull/11545)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **fix(quota-share):** in-flight leases are per request, so an aborted request's slot ages out instead of being kept alive by later traffic on the same connection ([#11547](https://github.com/diegosouzapw/OmniRoute/pull/11547)) — thanks @abhisheksharma2411 +- The free-tier summary route now serves the refreshed Radar catalog when the feed is active — the same numbers the dashboard shows — and states which catalog answered plus its real build date, instead of always reporting release-frozen figures with a stale curation date. The supporter-key live feed stays reserved to authenticated callers of the instance. ([#11550](https://github.com/diegosouzapw/OmniRoute/pull/11550)) — thanks @maxmad64bis +- **sse:** fix LiveWS/embed-WS servers crashing at startup under the Node/tsx runtime — `liveServer.ts`, `embedWsProxy.ts` and `apiBridgeServer.ts` imported `@/shared/utils/httpClientAbortGuard` without the `.mjs` extension, so the client-abort crash guard added by [#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556) was unreachable and every dependent test failed with `ERR_MODULE_NOT_FOUND` ([#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556)). ([#11671](https://github.com/diegosouzapw/OmniRoute/pull/11671)) — thanks @jonlwheat2-gif / @yxyxy +- fix(chat): shed any of the matched provider-node's routing identifiers (public prefix or internal id) before model lookup, so composites like `//` no longer reach upstream verbatim (#11557) — thanks @maxmad64bis +- **fix(cache):** a semantic-cache entry now expires on its TTL instead of surviving until the next UTC midnight, and `dbEntries` stops counting expired rows ([#11573](https://github.com/diegosouzapw/OmniRoute/pull/11573)) — thanks @ntdatt812 +- **fix(api):** `/v1/models` schedules its stale-while-revalidate rebuild through Next's `after()` again, so a stale catalog reaches the client before the rebuild blocks the event loop ([#11574](https://github.com/diegosouzapw/OmniRoute/pull/11574)) — thanks @ntdatt812 +- **fix(check):** the `known-symbols` executor conformance gate awaits the now-async `getExecutor()`, so it stops reporting all 142 registered aliases as dead symbols and can detect a lazy import that fails ([#11582](https://github.com/diegosouzapw/OmniRoute/pull/11582)) — thanks @ntdatt812 +- **fix(pack):** register `bin/cli/utils/volatileEnvPath.mjs` in `PACK_ARTIFACT_REQUIRED_PATHS`, so the prune can no longer silently drop a module the published CLI imports on every boot ([#11588](https://github.com/diegosouzapw/OmniRoute/pull/11588)) — thanks @ntdatt812 +- **i18n(vi):** translate the five reliability-column strings on the free-provider rankings page, which rendered as `__MISSING__:` markers in the Vietnamese UI ([#11589](https://github.com/diegosouzapw/OmniRoute/pull/11589)) — thanks @ntdatt812 +- **resilience:** restore the expired-connection retry-budget probe in the token-health sweep — the `!isGitHubAccessTokenOnlyConnection` carve-out reintroduced by #11608 contradicted the boundary pinned by #11592, so a GitHub connection parked at `expired` with retry budget remaining was never probed and could never self-heal ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)). ([#11672](https://github.com/diegosouzapw/OmniRoute/pull/11672)) — thanks @jonlwheat2-gif / @ntdatt812 +- **fix(dashboard):** Provider Stats now displays combo success rates on their native 0–100 percentage scale instead of multiplying them twice ([#11595](https://github.com/diegosouzapw/OmniRoute/pull/11595)) — thanks @pacocartones +- **fix(providers):** route DuckDuckGo Web Chat through the verified `duck.ai` endpoint, refresh its free model catalog, and avoid forwarding unsolved anti-abuse challenges ([#11597](https://github.com/diegosouzapw/OmniRoute/pull/11597)) — thanks @solstxce +- **fix(ui):** Console log Refresh and Copy controls now expose localized accessible names, keep copy actions visible on keyboard focus, and announce copy completion safely ([#11599](https://github.com/diegosouzapw/OmniRoute/pull/11599)) — thanks @pacocartones +- Dropped the stale-`.eslintcache` `restore-keys` fallback from both "Restore ESLint file cache" steps in `ci.yml`, so the blocking `Lint` job can no longer be served per-file verdicts computed under a different lint config, suppressions file or lockfile. `quality.yml` had already dropped it in #11963; `ci.yml` — the workflow that actually gates PRs — had not (#11600). ([#11996](https://github.com/diegosouzapw/OmniRoute/pull/11996)) +- fix(ci): pass `--pass-on-unpruned-suppressions` in `run-eslint-json.mjs` so the CI Lint job no longer fails when a suppression is merely orphaned by a genuine fix, mirroring the identical fix already in `validate-release-green.mjs` (#11600) ([#11983](https://github.com/diegosouzapw/OmniRoute/pull/11983)) — thanks @hartmark +- fix(handoff): enforce provider allowlist for universal handoff (#11602) — universal handoff now skips summarization when the selected summary model's provider is not included in the configured provider allowlist. ([#11619](https://github.com/diegosouzapw/OmniRoute/pull/11619)) — thanks @vsd2807 +- **fix(dashboard):** Show a stable error state when Search Analytics returns an HTTP error or malformed data ([#11603](https://github.com/diegosouzapw/OmniRoute/pull/11603)) — thanks @pacocartones +- **test(gamification):** pin the aggregate profile level to the XP-derived semantics of #11604 — `getAggregateXp()` now derives `currentLevel` from the summed XP (`calculateLevel(sum)`), not `MAX(stored current_level)`, and the #3484 fixture levels are aligned with the XP curve ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)). ([#11676](https://github.com/diegosouzapw/OmniRoute/pull/11676)) — thanks @jonlwheat2-gif / @pacocartones +- **fix(dashboard):** Keep the Profile level and progress aligned with aggregate XP, including bounded handling for invalid totals ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)) — thanks @pacocartones +- **fix(dashboard):** Prevent locked hidden badges from revealing their icon or opening private badge details before they are earned ([#11605](https://github.com/diegosouzapw/OmniRoute/pull/11605)) — thanks @pacocartones +- **fix(dashboard):** Restore keyboard focus after shared modals close and cancel delayed autofocus during cleanup ([#11607](https://github.com/diegosouzapw/OmniRoute/pull/11607)) — thanks @pacocartones +- **fix(dashboard):** Enable Enter and Space activation for clickable data-table rows without hijacking nested controls ([#11610](https://github.com/diegosouzapw/OmniRoute/pull/11610)) — thanks @pacocartones +- **fix(autoCombo,sse):** vendor-retired catalog ids are dropped from the auto-combo candidate pool and no longer win on leftover `arena_elo` / `user_override` rows; `getModelLifecycleDecision` consults `model-lifecycle.json` (prefix-stripped) so aggregator traffic is not `untracked` for ids the snapshot already knows ([#11625](https://github.com/diegosouzapw/OmniRoute/issues/11625)) ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)) — thanks @MumuTW +- **test(chatcore):** move Codex/Claude combo fixtures off models the lifecycle guard now rejects — `gpt-5.1-codex`/`gpt-5-codex` are vendor-retired (snapshot, #11626) and `claude-3-5-sonnet-20241022` is shut down, so native-passthrough and combo-fallback tests switched to `gpt-5.6-sol` and `claude-sonnet-4.6` ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)). ([#11675](https://github.com/diegosouzapw/OmniRoute/pull/11675)) — thanks @jonlwheat2-gif / @MumuTW +- fix(opencode-plugin): stop a free-tier budget that rounds up across a magnitude from rendering as `1000K`/`1000M` in the model picker — `fmtTokens` chose its unit from the raw token count and then rounded with `toFixed(1)`, so 999,950–999,999 printed as `1000K` rather than `1M` and just under a billion printed as `1000M` rather than `1B` ([#11684](https://github.com/diegosouzapw/OmniRoute/pull/11684)) — thanks @abhisheksharma2411 +- Cap prose-derived Antigravity quota resets at the configured model cooldown maximum while preserving authoritative Retry-After headers and Google RetryInfo hints. (#11695) Thanks @pacocartones. ([#11823](https://github.com/diegosouzapw/OmniRoute/pull/11823)) +- Compare `_rebaseline_*` notes structurally in the ratchet-bank verifier so an object-valued note no longer reports itself as rewritten on an unmodified tree — the nightly `bank-ratchet-shrinks` lane had been aborting on every run since 2026-08-11, leaving 41,672 lines of earned file-size shrinks unbanked (#11702). ([#11892](https://github.com/diegosouzapw/OmniRoute/pull/11892)) — thanks @MumuTW +- **fix(build):** `prepublish.ts` bundles the ChatGPT Web (Codex) MCP bridge through `runBuildTool()` instead of spawning `npx.cmd` raw, fixing the build crash on Node ≥ 20/Windows where `.cmd` shims cannot be spawned without a shell (EINVAL) ([#11704](https://github.com/diegosouzapw/OmniRoute/issues/11704)) ([#11706](https://github.com/diegosouzapw/OmniRoute/pull/11706)) — thanks @jonlwheat2-gif +- fix(codex): keep `parallel_tool_calls:false` on the translated Codex Responses Lite path (#11707) ([#11984](https://github.com/diegosouzapw/OmniRoute/pull/11984)) — thanks @hartmark +- **fix(tests):** the new Gemini Web Images retirement test (#11708) called the now-async `getExecutor()` synchronously, throwing `TypeError: getExecutor(...).getProvider is not a function` instead of asserting Gemini Web chat stays available. Awaited to match the R0.3 executor-registry refactor (#11220). ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(search):** Allow REST and MCP web-fetch requests to select the advertised `anysearch-search` provider explicitly ([#11716](https://github.com/diegosouzapw/OmniRoute/pull/11716)) — thanks @pacocartones +- **fix(providers):** Keep overnight peak-hour protection active after midnight according to the window's configured start day ([#11718](https://github.com/diegosouzapw/OmniRoute/pull/11718)) — thanks @pacocartones +- **fix(providers):** correct all five g4f.space gateway descriptions to document proof-of-work credits or member API keys, variable limits, and the remote prompt boundary instead of promising free keyless access at five requests per minute ([#11738](https://github.com/diegosouzapw/OmniRoute/pull/11738)). +- **fix(db):** `model_capabilities` is created by a migration instead of lazily on the first models.dev sync, so a clean install and an upgraded install converge on the same schema regardless of which features have run ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(ci):** `check:install-upgrade` now fails on an `npm` install truncated by ENOSPC (npm reports it as a warning and still exits 0), authenticates its health probe so the version assertion works against the hardened health payload, frees the clean-install tree before the upgrade phase, and no longer reports a schema divergence computed from a boot that never served ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(routing):** harden session-affinity key derivation against oversized explicit IDs and empty structural-payload collisions, while recognizing Gemini and common text request shapes ([#11746](https://github.com/diegosouzapw/OmniRoute/pull/11746)) — thanks @MumuTW +- **fix(packages/browser-pool):** regenerate `package-lock.json` so the `packages/browser-pool` workspace's locked `playwright`/`@types/node` (and transitives) match its `package.json` specs, fixing cache-only/offline installs (`npm ci --offline`, Nix `buildNpmPackage`) that previously failed with `ENOTCACHED` ([#11747](https://github.com/diegosouzapw/OmniRoute/issues/11747)) — thanks @benjaminkitt ([#11784](https://github.com/diegosouzapw/OmniRoute/pull/11784)) — thanks @geekyNads +- **fix(assets):** Stop distributing 79 provider and CLI logos without verified source/license provenance, keep every provider/tool available through the internal generic icon, and prevent those IDs from falling through to an external icon CDN. ([#11750](https://github.com/diegosouzapw/OmniRoute/pull/11750)) +- **fix(providers):** retire the common `chatgpt-web` / `cgpt-web` provider because its pre-key and proof-of-work provenance could not be cleared ([#11754](https://github.com/diegosouzapw/OmniRoute/pull/11754)) — explicit chat and image requests, slash-prefixed models, persisted aliases, proxy overrides, and combo targets now fail closed with a sanitized HTTP `410` / `PROVIDER_RETIRED` before upstream dispatch. Migration 163 tombstones matching connections and invalidates their active leases without deleting history or widening API-key allowlists. The independent `chatgpt-web-codex` / `cgpt-codex` provider and its MIT-noticed vendored implementation remain available. +- **fix(usage):** quota and usage refresh no longer 409 when an exclusive lease reserves the connection ([#11758](https://github.com/diegosouzapw/OmniRoute/pull/11758)) — thanks @TheDemonTuan +- Keep the embedding registry's vector width and `embedding` type on models when a synced model exists for the same id, so `/v1/models` no longer reports registry-described embedding models widthless or untyped (#11761) — thanks @rahilmavani +- Correct `google/gemini-embedding-001` on the OpenRouter route to 3072 dimensions, the width it returns when `dimensions` is not sent (#11761) — thanks @rahilmavani +- **fix(vision-bridge):** Restrict automatic vision routing and fallbacks to models available in authoritative live provider catalogs, including aliases and effort variants ([#11767](https://github.com/diegosouzapw/OmniRoute/issues/11767)) — thanks @pacocartones ([#11813](https://github.com/diegosouzapw/OmniRoute/pull/11813)) +- **fix(providers):** reclassify Cerebras as a one-time $5 signup credit (payment method required, 30-day validity), not a recurring no-card 1M tokens/day trial ([#11773](https://github.com/diegosouzapw/OmniRoute/issues/11773)) ([#12591](https://github.com/diegosouzapw/OmniRoute/pull/12591)) — thanks @HouMinXi +- fix(cli): stop prepublish from re-rebuilding the already-built ESM-only opencode-plugin dist (#11787) ([#11990](https://github.com/diegosouzapw/OmniRoute/pull/11990)) — thanks @hartmark +- **fix(db):** Fresh installs no longer log a non-fatal `no such table: compression_run_telemetry` warning when retention cleanup runs before the lazily-created telemetry table exists ([#11802](https://github.com/diegosouzapw/OmniRoute/pull/11802)) — thanks @RaviTharuma +- fix(sse): set X-OmniRoute-Selected-Connection-Id on successful combo dispatches so downstream consumers stop falling back to an empty connection id (#11810) ([#11986](https://github.com/diegosouzapw/OmniRoute/pull/11986)) — thanks @hartmark +- **fix(services):** embedded CLIProxyAPI lifecycle checks now use public `/healthz`, while model discovery uses the configured dedicated data-plane API key instead of the management password ([#11811](https://github.com/diegosouzapw/OmniRoute/pull/11811)) — thanks @RaviTharuma +- Fix persisted model steps returning an error in the playground route simulator, and warn when structural steps cannot be simulated. ([#11862](https://github.com/diegosouzapw/OmniRoute/pull/11862)) — thanks @pacocartones +- **fix(providers):** Antigravity's dynamic mitmAlias table no longer routes `gemini-3.7-flash-{high,medium,low}` to a literal tier-suffixed upstream id just because one connected account's own discovery listed it directly — those display ids always resolve through the safe `gemini-3.7-flash-tiered` static alias, so one account's Google-provisioned access no longer 404s every sibling account of the provider ([#11824](https://github.com/diegosouzapw/OmniRoute/issues/11824), [#11651](https://github.com/diegosouzapw/OmniRoute/issues/11651)) ([#11988](https://github.com/diegosouzapw/OmniRoute/pull/11988)) — thanks @hartmark +- **fix(dashboard):** Expose Profile loading, errors, page structure, and XP progress to assistive technologies ([#11838](https://github.com/diegosouzapw/OmniRoute/pull/11838)) — thanks @pacocartones +- **fix(socks):** `Agent.connectTimeout` now bounds both SOCKS handshake and TLS connect, and `family === null` no longer falls back to `fetch-socks` ([#11842](https://github.com/diegosouzapw/OmniRoute/pull/11842)) — thanks @maxmad64bis +- **perf(compression):** OOM mitigations for large payload hashing, memoization, and token estimation ([#11844](https://github.com/diegosouzapw/OmniRoute/pull/11844) — thanks @AndrianBalanescu) +- Excluded Next.js Node File Trace manifests (`*.nft.json`) from the published npm tarball. They are build-time metadata and are never read while serving, but had grown to 668.7 MB — 61% of the package — which pushed the upload past the registry limit and made `npm publish` fail with `413 Payload Too Large`. ([#11864](https://github.com/diegosouzapw/OmniRoute/pull/11864)) +- **fix(cli):** keep the global OmniRoute server `--base-url` separate from provider-node `--endpoint` values in add, update, and validate commands ([#11860](https://github.com/diegosouzapw/OmniRoute/pull/11860)) — thanks @pacocartones +- **fix(config):** Nous Research's `Hermes-4-405B` model now displays as "Hermes 4 405B (Nous Research)" in both the provider registry and the free-model catalog, instead of the mislabelled "Hermes 4 7B" ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#11993](https://github.com/diegosouzapw/OmniRoute/pull/11993)) +- **fix(provider/nous):** inject required user= tag into Nous Research inference requests to resolve upstream 400 "missing tags" error ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#12044](https://github.com/diegosouzapw/OmniRoute/pull/12044)) +- **fix(build):** `npm run build` now fails in one second with a named package and a copy-pasteable fix when npm silently drops an externalised optional native dependency, instead of dying four minutes in with `Module not found: Can't resolve 'better-sqlite3'` ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- **fix(install):** `postinstall` no longer throws `ReferenceError: isAndroid is not defined` — failing the whole `npm install` — when the `better-sqlite3` rebuild fallback times out; the manual-fix guidance is reachable again ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- Fixed the Alibaba free-tier allowlist test that went red on its own once the shipped catalog's `validUntil` (2026-08-27) passed, leaving every PR and `main` with a failing `Unit Tests (1/8)`. The test now builds its own packs with dates it controls, and covers the expired-pack fallback that production has actually been serving. ([#11867](https://github.com/diegosouzapw/OmniRoute/pull/11867)) +- Split the npm registry upload into its own GitHub-hosted job. npm refuses `--provenance` from a self-hosted runner (`422 ... Only "github-hosted" runners are supported`), which blocked the v3.8.50 publish; the heavy verification cannot move to a hosted runner, so it now hands the proven tarball over instead. ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(ui):** the shared `DataTable` loading state no longer reads its decorative ⏳ glyph out to assistive technology, and now carries the same `role="status"` / `aria-live="polite"` / `aria-busy="true"` semantics as `PageLoading` ([#11869](https://github.com/diegosouzapw/OmniRoute/pull/11869)) — thanks @pacocartones +- **fix(api):** Bound the `timeout` accepted by `POST /api/providers/[id]/login` to the same 15s–600s window the Adobe Firefly and Conol browser-login paths already enforce, so a request can no longer pin a headful browser and the single active-login slot with an arbitrarily large poll budget. ([#11871](https://github.com/diegosouzapw/OmniRoute/pull/11871)) — thanks @pacocartones +- **fix(api):** Stop the tunnel and MITM routes from returning raw child-process error text, which disclosed host paths, binary install locations and Tailscale `tskey-*` credentials in a body some of these routes serve to non-loopback callers, and return a real 400 instead of a framework 500 when a tunnel request body fails validation. ([#11872](https://github.com/diegosouzapw/OmniRoute/pull/11872)) — thanks @pacocartones +- **fix(open-sse):** stream TTFT/ITL are sampled from a monotonic clock (`performance.now`) instead of `Date.now`, so an NTP correction or wall-clock jump can no longer inflate a genuine TTFT or produce a negative one that the `ttft >= 0` guard silently drops — protecting the router's OTel/EWMA/`usage_history`/speed-ranking signals ([#11873](https://github.com/diegosouzapw/OmniRoute/pull/11873)) — thanks @pacocartones +- **fix(providers):** xAI `reasoning_effort: "xhigh"` now reaches grok-4.6+ instead of being silently clamped to `"high"` ([#11879](https://github.com/diegosouzapw/OmniRoute/pull/11879)) — thanks @NoxzRCW +- **fix(dashboard):** the providers page no longer crashes into the error boundary when a provider id collides with an `Object.prototype` member (`constructor`, `__proto__`); icon lookups are own-property guarded ([#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)) — thanks @NoxzRCW +- **fix(skills):** injected skill tools declared in shorthand (`{"content": "string"}`) now forward valid JSON Schema, unblocking providers that validate tool schemas strictly such as Zhipu GLM on the Console Go tier ([#11881](https://github.com/diegosouzapw/OmniRoute/pull/11881)) — thanks @NoxzRCW +- **fix(api):** `POST /api/playground/simulate-route` now surfaces `combo-ref` and `provider-wildcard` persisted combo steps with a specific warning (naming the referenced combo, or the unresolved `provider/model` wildcard) instead of folding them into a generic "unsupported step" count; a `provider-wildcard` step is also now included as an unresolved target so the operator can see it is in the route (ported from [#11882](https://github.com/diegosouzapw/OmniRoute/pull/11882) — thanks @NoxzRCW). ([#11930](https://github.com/diegosouzapw/OmniRoute/pull/11930)) +- **fix(translator):** the streaming OpenAI→Claude translator keeps upstream usage, including prompt-cache tokens, when it arrives on a trailing `choices: []` chunk (Fireworks and any upstream using `stream_options.include_usage`) ([#11883](https://github.com/diegosouzapw/OmniRoute/pull/11883)) — thanks @NoxzRCW +- **fix(cli):** `omniroute update --apply` now tells you explicitly whether a running server was detected and, if so, that you must run `omniroute restart` to apply the update — it never restarted anything and previously implied the update was already live once files were installed. The dashboard's npm-mode Update flow (`/api/system/version`) now tries OmniRoute's own PID-file-managed supervisor before falling back to pm2, and reports an honest "restart required" step instead of a silent pm2-only "skipped" that read like a completed update. The server-side latest-version lookup backing the dashboard's update banner also gained `--prefer-online`, closing the same stale-npm-cache class already fixed in the CLI's own copy for #4376 ([#11885](https://github.com/diegosouzapw/OmniRoute/issues/11885)). ([#12005](https://github.com/diegosouzapw/OmniRoute/pull/12005)) +- **fix(resilience):** clear persisted LKGP pins when a target suffers connection/provider exhaustion or is skipped before dispatch due to cooldown/exhaustion/unavailability, preventing subsequent requests from repeatedly prioritizing known-dead providers ([#11911](https://github.com/diegosouzapw/OmniRoute/issues/11911)). ([#12013](https://github.com/diegosouzapw/OmniRoute/pull/12013)) — thanks @HouMinXi +- **fix(sse):** the OpenAI→Claude stream translator now defers the terminal `message_delta`/`message_stop` emission until the real usage block has arrived (or a genuine end-of-stream flush forces it) instead of emitting it immediately on `finish_reason` — previously, when the trailing usage-only chunk (`{"choices":[],"usage":{...}}`) arrived *after* the `finish_reason` chunk (the normal order for Fireworks/vLLM/Together and other `stream_options.include_usage` upstreams), the client-visible `message_delta` still carried stale/zero usage even though `state.usage` was internally corrected too late to matter (ported from [#11915](https://github.com/diegosouzapw/OmniRoute/pull/11915) — thanks @HouMinXi). ([#11933](https://github.com/diegosouzapw/OmniRoute/pull/11933)) +- **fix(sse):** `stripStore()` now forces `store=false` for stateless OpenAI-compatible Responses-API targets (unless the connection has explicitly opted in via `providerSpecificData.openaiStoreEnabled`), instead of only handling the `openai`/`agentrouter` cases — a client-supplied `store` value previously passed through untouched to backends that don't actually persist responses server-side ([#11916](https://github.com/diegosouzapw/OmniRoute/pull/11916) — thanks @HouMinXi). +- **fix(models):** custom provider-node models (synced, custom, and alias-backed) now appear under their configured prefix in the unified catalog when the operator's model-id prefix mode is canonical, instead of being dropped whenever alias-inclusion was otherwise disabled ([#11918](https://github.com/diegosouzapw/OmniRoute/pull/11918) — thanks @HouMinXi). +- **fix(models):** the unified model catalog now suppresses stale static registry models (including effort-tier variants) for any provider whose active connection has an authoritative live synced catalog, not only providers already using exclusive-synced-listing — a connection with `providerUsesAuthoritativeLiveCatalog` previously kept serving both the live-synced models and the stale static rows side by side ([#11919](https://github.com/diegosouzapw/OmniRoute/pull/11919) — thanks @HouMinXi). +- **fix(ui):** `ProviderIcon`'s three remaining unguarded lookups (`PROVIDER_ICON_ALIASES`, `LOCAL_SVG_ALIASES`, `THEMED_SVGS`) now use `Object.hasOwn()` own-property checks — a provider id such as `constructor` or `__proto__` previously resolved through the prototype chain instead of falling through to the unknown-provider CDN fallback (`getLobeProviderIcon()` itself was already guarded by [#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)); `ProviderPageHeader`'s `color` field is also now optional, matching the rest of the component's defensive typing (ported from [#11920](https://github.com/diegosouzapw/OmniRoute/pull/11920) — thanks @HouMinXi). ([#11935](https://github.com/diegosouzapw/OmniRoute/pull/11935)) — thanks @NoxzRCW +- fix(ollama): preserve multi-byte UTF-8 content split across stream chunks in the Ollama NDJSON transform, which previously corrupted CJK/emoji into U+FFFD (#11921) — thanks @pacocartones +- **fix(providers):** OrcaRouter chat requests now target `/v1/chat/completions` instead of the bare `/v1` API root, fixing the upstream `404 Invalid URL (POST /v1)` ([#11923](https://github.com/diegosouzapw/OmniRoute/pull/11923)). — thanks @echel0nn +- **fix(plugins):** deliver the `onStreamComplete` event to disk-installed plugins. The event shipped in v3.8.50 (#9669) was emitted internally but had no plugin-facing wiring, so no plugin could ever subscribe: the manifest schema silently dropped `hooks.onStreamComplete`, and the loader/manager only knew the seven legacy hooks. `onStreamComplete` is now a declarable manifest hook, wired through the loader and registered by the manager like the other hooks, and its payload carries a `requestId` so consumers can correlate the stream-completion event with the originating request ([#11934](https://github.com/diegosouzapw/OmniRoute/pull/11934)) — thanks @amaleta / @pacocartones +- **fix(db):** the Qdrant embedding-model dropdown now lists local/self-hosted providers (Ollama, LM Studio, vLLM, etc.) — an active connection is treated as "configured" when the provider allows an optional API key, not only when it has a real key or OAuth, so a running local embedding provider is no longer hidden from the picker ([#11949](https://github.com/diegosouzapw/OmniRoute/issues/11949)) ([#11995](https://github.com/diegosouzapw/OmniRoute/pull/11995)) +- **fix(providers):** Vertex AI Anthropic partner-model discovery now calls the Model Garden `v1beta1` publisher list (`/v1beta1/publishers/anthropic/models`, global) and parses its `publisherModels` envelope, so Claude models auto-synced from Vertex populate the active live catalog and route at request time instead of returning `Model '' is not available in the active live catalog` ([#11991](https://github.com/diegosouzapw/OmniRoute/issues/11991)) — thanks @fabioluissilva ([#11998](https://github.com/diegosouzapw/OmniRoute/pull/11998)) +- **fix(cli):** support `--base-url` alongside `--endpoint` in `omniroute nodes add`, `update`, and `validate` subcommands to prevent global `--base-url` shadowing issues ([#11999](https://github.com/diegosouzapw/OmniRoute/issues/11999)). ([#12033](https://github.com/diegosouzapw/OmniRoute/pull/12033)) — thanks @HouMinXi +- **fix(providers):** `cloudflare-ai` no longer refuses image content parts for every Workers AI model ([#12002](https://github.com/diegosouzapw/OmniRoute/pull/12002)) — the plain-string `content` requirement behind #2539 is carried by the _model_ schema, not by the `/ai/v1/chat/completions` endpoint (measured: an all-text part array returns 200 on `@cf/mistralai/mistral-small-3.1-24b-instruct`, `@cf/meta/llama-4-scout-17b-16e-instruct` and `@cf/meta/llama-3.3-70b-instruct-fp8-fast`, and 400 on the text-only `@cf/qwen/qwen2.5-coder-32b-instruct`). `transformRequest()` flattened every array and threw on the first non-text part (#6390), so image input was refused for vision-capable Cloudflare models that accept it. All-text arrays are still flattened — the one shape every model accepts — while an array carrying a non-text part is passed through untouched, so the attachment is still never silently dropped. Regression guards: `tests/unit/cloudflare-ai-image-parts-6390.test.ts`. — thanks @davidlinfr +- **fix(resilience):** decouple the limiter-managed execution backstop from the queue-wait budget — new `requestQueue.executionMaxWaitMs` (env `RATE_LIMIT_EXECUTION_MAX_WAIT_MS`, default 600000 = 10 min) now feeds Bottleneck's post-dispatch `expiration`, while `requestQueue.maxWaitMs` keeps its documented queue-wait semantics. Previously the queue-wait budget doubled as the execution expiration, so legitimate long-running calls on non-incremental gateways (whole generation buffered before the first upstream byte, e.g. Console Go / Command Code tiers serving GLM models) were killed mid-flight at the queue budget with a false 504 `RATE_LIMIT_EXECUTION_TIMEOUT` — the local limiter undercut the provider-aware upstream fetch-start timeouts. The surfaced 504 message now names `requestQueue.executionMaxWaitMs`; the error keeps the #4165 guarantees (disclaims an upstream timeout, preserves the Bottleneck error as `cause`, branded code + trusted provenance, classified request-scoped so combo falls back). A real queue-wait bound (the `Promise.race` around `limiter.schedule()` sketched in #9533) remains future work. (#12025) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **Call logs:** keep the `error` field when an artifact exceeds the storage cap, instead of replacing it with the omission marker. The error is the only field that says *why* a request failed and is typically ~90 bytes next to the multi-hundred-KB bodies that trip the cap, so dropping it left a size-limited row undiagnosable — a provider outage, a local timeout and an upstream 400 all rendered identically. It is now preserved at every fallback stage, truncated to 4KB if it is itself large ([#12026](https://github.com/diegosouzapw/OmniRoute/issues/12026)). ([#12095](https://github.com/diegosouzapw/OmniRoute/pull/12095)) — thanks @ntdat812 +- **fix(diagnostics):** preserve the error field (truncated to 4KB with a `[truncated: …]` suffix) in every call-log artifact size-limit fallback stage. Previously the minimal fallback replaced the error with `[omitted: call log artifact size limit exceeded]`, so an oversized artifact row showed nothing about WHY the request failed — e.g. 91 of 847 opencode-go 504 rows on one production instance were undiagnosable from the dashboard. Oversized request/response bodies are still omitted exactly as before; the error cap is independent of the payload sizes that tripped the fallback. (#12026) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **fix(sse):** OpenAI Responses clients that declare the native `web_search` tool now receive a spec-shaped `web_search_call` output item with `action.sources` alongside the preserved function-call round-trip, so search results executed through OmniRoute's own search backend are consumable by standard Responses clients (Codex, pi-web-access, …). ([#12031](https://github.com/diegosouzapw/OmniRoute/pull/12031)) — thanks @watchingdogs +- **fix(cli):** use in-thread alias resolver hooks on modern runtimes to avoid deprecation noise and improve Node.js forward compatibility ([#12073](https://github.com/diegosouzapw/OmniRoute/issues/12073)). ([#12083](https://github.com/diegosouzapw/OmniRoute/pull/12083)) — thanks @xiaoyaner0201 +- **fix(combo):** an operator-set **Agent Features → Context length** on a combo is now honored at request time. The value was persisted and advertised through `/v1/models`, but `resolveComboContextLimit()` never consulted it — so a multi-target combo whose members carry no per-model window fell through to the provider's generic `defaultContextLength` (openrouter 128000, command-code 200000) and rejected large requests with `Input exceeds context window … limit 128000` despite the combo being explicitly sized much larger. An identical single-target combo worked, because it collapses to its concrete target before the guard runs. Invalid values (0/negative/NaN/Infinity) are ignored, so the existing target → combo-min → fallback order is unchanged. ([#12090](https://github.com/diegosouzapw/OmniRoute/pull/12090)) — thanks @adivekar-utexas +- **fix(sse):** passthrough streams now estimate usage on finish when upstream closes without usage even with `stream_options.include_usage` — avoids `0 tokens / 0%` for providers that stay silent (and correctly handles trailing empty-choices usage) ([#12151](https://github.com/diegosouzapw/OmniRoute/pull/12151)) — thanks @maxmad64bis +- **fix(combos):** clearing an agent feature in the combos editor now persists — unchecking context cache protection, or emptying the system message or tool filter, sends an explicit `null` instead of dropping the field from the `PUT` body, which the update merge read as "leave unchanged" ([#12177](https://github.com/diegosouzapw/OmniRoute/pull/12177)) — thanks @foreveryh / @ntdatt812 +- **fix(memory):** self-hosted embedding endpoints now vectorize — the vector width is measured from the first embedding that comes back instead of being read from a registry that cannot describe them, so `vec_memories` is created and memories stop piling up unvectorized behind a green health check ([#12180](https://github.com/diegosouzapw/OmniRoute/pull/12180)) — thanks @kanade-hoshino / @ntdatt812 +- **fix(translator):** the leading `system` message now reaches Responses-API upstreams when its `content` is a content-part array — it was read as `typeof content === "string" ? content : ""`, so a prompt-caching client (Anthropic `cache_control`, the shape LiteLLM and the Anthropic SDK emit) had its entire system prompt replaced by an empty `instructions`. The request was still accepted with a normal `prompt_tokens` count, so the model answered with no instructions and nothing in the response said they were missing. Mid-conversation system turns already handled the array shape ([#7056](https://github.com/diegosouzapw/OmniRoute/pull/7056)); only the first one did not ([#12206](https://github.com/diegosouzapw/OmniRoute/issues/12206)). Regression guard: `tests/unit/translator-openai-responses-system-content-parts.test.ts`. ([#12207](https://github.com/diegosouzapw/OmniRoute/pull/12207)) — thanks @Gorillaz322 +- **fix(free-tier):** `/api/free-tier/summary` no longer computes its totals from a Radar feed built before the catalog the running release ships. When the cached feed is older — or carries no build date at all — the route answers from the shipped catalog, resolved through the operator's local model state so disabled and tombstoned models stay out of the numbers ([#12215](https://github.com/diegosouzapw/OmniRoute/pull/12215)). — thanks @maxmad64bis +- **fix(oauth):** Keep a Claude personal workspace and a Team organization as separate connections — they share the same email and `accountUUID`, so the email-only OAuth dedup let the second login overwrite the first account's tokens; `organizationUUID` now disambiguates them, the way `workspaceId` does for Codex ([#12222](https://github.com/diegosouzapw/OmniRoute/pull/12222)) — thanks @davidebaraldo +- **fix(resilience):** a 402 on a single paid model of a passthrough/gateway provider (e.g. `kilo-gateway`, `ollama-cloud`) no longer terminalizes the whole connection with a never-auto-recovered `credits_exhausted` status — only the paid model is locked out, so free models on the same key keep serving. 402 variant of [#3027](https://github.com/diegosouzapw/OmniRoute/issues/3027). Single-credential providers are unaffected — a 402 there is still treated as the key being genuinely out of credit ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239) / [#10616](https://github.com/diegosouzapw/OmniRoute/issues/10616)) ([#12242](https://github.com/diegosouzapw/OmniRoute/issues/12242)) — thanks @brick30llc-ctrl ([#12266](https://github.com/diegosouzapw/OmniRoute/pull/12266)) — thanks @geekyNads +- **fix(sse):** trust `finish_reason: "length"`/`"max_tokens"` over the reasoning-consumed-token ratio in response quality validation, so a reasoning model truncated below the old 90% threshold correctly fails and retries instead of returning empty content as a silent "success" ([#12262](https://github.com/diegosouzapw/OmniRoute/pull/12262)) — thanks @brick30llc-ctrl +- **fix(settings):** `PUT /api/settings/cache-config` now persists `alwaysPreserveClientCache` to the flat general settings the runtime cache-control policy actually reads; previously the value landed in the databaseSettings "cache" section and was silently ignored, so the endpoint had no effect on `cache_control` passthrough ([#12304](https://github.com/diegosouzapw/OmniRoute/pull/12304)) — thanks @davidebaraldo +- **fix(grok-cli):** treat omitted SuperGrokPro `creditUsagePercent` as 0% used so Provider Limits still renders a weekly bar (proto3 zero-elision) ([#12312](https://github.com/diegosouzapw/OmniRoute/pull/12312)) — thanks @HouMinXi +- **fix(combo):** Expose the two Auto-Combo scoring factors nobody could set — the combo validation schema and the dashboard weight sliders both declared 13 of the scorer's 15 factors, so `connectionDensity` (spreads load across a provider's connections) and `quality` were dropped on save and offered nowhere. The sliders also shipped their own default table that differed from the engine's on every non-zero factor and summed to 1.05, so the percentages shown next to them added up to 105%. Both lists now match `DEFAULT_WEIGHTS`, and a test keeps them there. Note that a combo whose stored `weights` omitted the two keys was effectively running with them at zero and the other thirteen renormalized upward; it now runs with the engine's intended distribution, so its routing does shift ([#12314](https://github.com/diegosouzapw/OmniRoute/pull/12314)) — thanks @maxmad64bis +- **fix(docs):** The free-tier reference no longer says its numbers come "confidence tagged per row" — no catalog entry carries a confidence tag and the API serves none, so every figure on that page is an estimate of the same, unstated quality. The page now states what an entry does vouch for: an independently documented hard stop (set by hand with the source in a comment, never defaulted to `true`) and a prompt-training disclosure, both with live counts the `check:docs-counts` gate keeps honest ([#12318](https://github.com/diegosouzapw/OmniRoute/pull/12318)) — thanks @maxmad64bis +- **fix(usage):** `adobe-firefly` and `firefly` have had a working usage fetcher since Adobe Firefly landed, but neither was ever added to the registration list, so the provider-plugin manifest, `genericQuotaFetcher` and the free-access quota cache all reported them as having no usage support — while `USAGE_SUPPORTED_PROVIDERS` said the opposite. Both are now declared, which also means their credit balance is fetched like any other declared provider's: `registerGenericQuotaFetchers` now registers a generic quota fetcher for them, and `resolveFreeAccessState` no longer returns early. A test holds the registration list to the dispatcher's switch in both directions, which is what the module's own docstring already asked for in prose ([#12321](https://github.com/diegosouzapw/OmniRoute/pull/12321)) — thanks @maxmad64bis +- **fix(quota):** drop the generic quota cache (agy / Antigravity / Claude OAuth) on an upstream 429 so reset-aware scoring does not keep a 60s stale snapshot, and force-refresh the next usage fetch so inner provider caches cannot recache the same window ([#12325](https://github.com/diegosouzapw/OmniRoute/pull/12325)) — thanks @HouMinXi +- **fix(combos):** deleting a combo now clears its persisted LKGP pins instead of leaving unreachable `key_value` rows behind ([#12326](https://github.com/diegosouzapw/OmniRoute/issues/12326)) ([#12425](https://github.com/diegosouzapw/OmniRoute/pull/12425)) — thanks @KrzysiekSko +- **fix(sse):** Keep ZWNJ (U+200C) and ZWJ (U+200D) in assistant text, reasoning and tool-call arguments — Persian/Kurdish half-space (`ارائه‌دهنده`), Arabic/Indic shaping and emoji sequences no longer lose them; the response de-obfuscation now removes joiners only between ASCII word characters, where the request side inserts them ([#12186](https://github.com/diegosouzapw/OmniRoute/issues/12186)) — thanks @rezjalibd ([#12359](https://github.com/diegosouzapw/OmniRoute/pull/12359)) — thanks @pacocartones +- **fix(resilience):** count resolved upstream 5xx results against the provider circuit breaker on the chat path — `CircuitBreaker.execute()` no longer reads a resolved `{ success: false, status: 5xx }` as a success that cancels the call-site failure, so a provider answering 503s now trips its breaker instead of staying `CLOSED` at `failureCount: 1`; single-model and combo dispatches are each accounted exactly once ([#12254](https://github.com/diegosouzapw/OmniRoute/issues/12254)) ([#12360](https://github.com/diegosouzapw/OmniRoute/pull/12360)) — thanks @pacocartones +- **fix(providers):** resolve the Codex quota auto-ping model from the live provider catalog and lifecycle registry instead of the retired `gpt-5.1-codex-mini`, and pause the ping with one actionable warning when no selectable Codex model exists rather than retrying a shut-down id every cooldown window ([#11905](https://github.com/diegosouzapw/OmniRoute/issues/11905)) ([#12361](https://github.com/diegosouzapw/OmniRoute/pull/12361)) — thanks @pacocartones +- **fix(api):** keep the `{created, data}` wrapper on combo-routed `/v1/images/generations` responses and default Codex image results to `b64_json` on both `/v1/images/generations` and `/v1/images/edits` so Codex CLI's built-in `image_gen` can decode them ([#12268](https://github.com/diegosouzapw/OmniRoute/issues/12268)) ([#12362](https://github.com/diegosouzapw/OmniRoute/pull/12362)) — thanks @pacocartones +- **fix(sse):** Name the shadowed custom provider node when a built-in provider id/alias (e.g. `openference` → `of`) reserves the prefix of an existing OpenAI/Anthropic-compatible node, so the runtime `No active credentials for provider: ` error explains that the prefix routed to the built-in and never reached the node's healthy connections, instead of contradicting the dashboard ([#11943](https://github.com/diegosouzapw/OmniRoute/issues/11943)) — thanks @morpheus9393 ([#12365](https://github.com/diegosouzapw/OmniRoute/pull/12365)) — thanks @pacocartones +- **fix(i18n):** wrap `ccOnboardingKeyPlaceholder` in ICU single quotes across all 43 locales so angle brackets render literally instead of being parsed as rich-text tags, which crashed the Claude Code onboarding block with `INVALID_MESSAGE: INVALID_TAG` ([#12302](https://github.com/diegosouzapw/OmniRoute/issues/12302)) ([#12369](https://github.com/diegosouzapw/OmniRoute/pull/12369)) — thanks @gonisulaimann +- **fix(models):** publish `effort_tiers` on Kimi K3's synced base-model entries (`kmca/k3`, `kmca/k3-256k`) so catalog-only clients (OpenCode, plain SDK pickers) can see and select the reasoning tiers (`low`/`high`/`max`) the synced metadata already carried — the `isSkippedEffortProvider` gate no longer suppresses tier visibility on those base entries, while synthetic `-` variant generation stays prevented and Codex/GLM base models remain excluded unchanged ([#12299](https://github.com/diegosouzapw/OmniRoute/issues/12299)) ([#12371](https://github.com/diegosouzapw/OmniRoute/pull/12371)) — thanks @gonisulaimann +- **fix(guardrails):** keep `auto`/`auto/*` virtual combos exempt from the Vision Bridge `fixedModel` credential guard so a combo target is passed through instead of silently falling back to global auto-selection ([#12237](https://github.com/diegosouzapw/OmniRoute/issues/12237)) ([#12373](https://github.com/diegosouzapw/OmniRoute/pull/12373)) — thanks @pacocartones +- **fix(combo):** capability-filter exhaustion caused by `max_tokens` above every target's known output limit now reports that reason (requested `max_tokens` vs the pool's highest known ceiling) instead of the unrelated "supports structured output" message ([#12229](https://github.com/diegosouzapw/OmniRoute/issues/12229)) — thanks @DW-MediaLab ([#12374](https://github.com/diegosouzapw/OmniRoute/pull/12374)) — thanks @pacocartones +- **fix(auth):** the `least-used` account strategy now prefers accounts without backoff before falling back to oldest `lastUsedAt`, the same tie-break `round-robin` already applies, so a failover no longer lands on a just-rate-limited account for a single request ([#12279](https://github.com/diegosouzapw/OmniRoute/issues/12279)) — thanks @tenshiak ([#12375](https://github.com/diegosouzapw/OmniRoute/pull/12375)) — thanks @pacocartones +- **fix(docker):** the `chatgpt-web-codex-browser` image now finds the Chrome binary under `chrome-linux64/` (Chrome for Testing layout in `playwright:v1.62.0-noble`) as well as the legacy `chrome-linux/`, so the container no longer crash-loops with `exec: --headless=new: not found` ([#12024](https://github.com/diegosouzapw/OmniRoute/issues/12024)) ([#12376](https://github.com/diegosouzapw/OmniRoute/pull/12376)) — thanks @pacocartones +- **fix(providers):** declare `groq/compound` and `allam-2-7b` as non-reasoning models in the curated Groq registry so `reasoning_effort` / `output_config.effort` / `thinking` from Claude Code are stripped instead of forwarded, which Groq rejected with HTTP 400 ([#12134](https://github.com/diegosouzapw/OmniRoute/issues/12134)) ([#12379](https://github.com/diegosouzapw/OmniRoute/pull/12379)) — thanks @pacocartones +- **fix(executors):** `OpencodeExecutor` no longer forces a direct connection when the connection has a proxy assigned in Proxy Management but no per-account proxies: the single-account fast path used to wrap the upstream dispatch in the direct-egress sentinel, discarding the ambient proxy context the chat handler had pinned from `proxy_assignments`, so API-key `opencode`/`opencode-go` connections egressed from the host IP (and hit geoblocks) despite the assignment. The direct pin is now applied only when no ambient proxy context exists ([#11894](https://github.com/diegosouzapw/OmniRoute/issues/11894) — thanks @hizzt) ([#12380](https://github.com/diegosouzapw/OmniRoute/pull/12380)) — thanks @pacocartones +- **fix(api):** `GET /v1/models` with `MODELS_CATALOG_PREFIX_MODE=canonical` (or `?prefix=canonical`) now lists providers whose registry alias is undefined or equal to their own id (Antigravity, Antigravity CLI and other self-aliased built-ins) — their single `provider/model` id was dropped by the alias/canonical duplicate guard in the static, synced, custom and alias-backed catalog loops ([#12058](https://github.com/diegosouzapw/OmniRoute/issues/12058)) — thanks @cheynetom ([#12381](https://github.com/diegosouzapw/OmniRoute/pull/12381)) — thanks @pacocartones +- **fix(translator):** Drop replayed `thinking` blocks that carry no signature (the shape produced from cross-provider `reasoning_content`) instead of stamping the default Claude signature on them, which Anthropic rejected with `400 Invalid signature in thinking block` on the next turn served by an Anthropic rung ([#12105](https://github.com/diegosouzapw/OmniRoute/issues/12105)) — thanks @atescivitci-cmd ([#12386](https://github.com/diegosouzapw/OmniRoute/pull/12386)) — thanks @pacocartones +- **fix(cli):** Resolve Bun's `--preload` polyfill path against the package root instead of `dist/`, so `omniroute` installed with `bun install -g` no longer crashes at startup with `error: preload not found …/dist/open-sse/utils/setupPolyfill.ts` ([#11980](https://github.com/diegosouzapw/OmniRoute/issues/11980)) — thanks @joglomedia ([#12387](https://github.com/diegosouzapw/OmniRoute/pull/12387)) — thanks @pacocartones +- **fix(providers):** `gemini-business` now publishes its model catalog — `/v1/models` and `/v1/providers/gemini-business/models` list the 12 enterprise Gemini ids the executor understands instead of returning an empty list (#12107) ([#12389](https://github.com/diegosouzapw/OmniRoute/pull/12389)) — thanks @pacocartones +- **fix(db):** install `busy_timeout` before the SQLite connection's first statement so a process opening the database while another one closes its WAL connection waits out the transient EXCLUSIVE lock instead of dying with `database is locked`, and recognise the drivers' real BUSY/PROTOCOL/IOERR errors as transient in the corruption probe so the same lock no longer renames the database away as corrupt; deflakes `cross-process contenders never both acquire the same connection` (#12394 — thanks @pacocartones) +- **fix(chat-admission):** derive the `chat_admission_busy` 503 `Retry-After` from observed heavyweight-lease occupancy — the larger of the exhausted `OMNIROUTE_CHAT_ADMISSION_QUEUE_MS` window and the time since capacity last turned over, capped at 60 s — instead of a fixed 1 s (structural) / 2 s (byte-stage) hint that invited Codex/agent fan-out clients to re-send ~1 MiB `/v1/responses` bodies every second into a gate held for the whole SSE lifetime; an idle gate keeps the historical floors ([#12135](https://github.com/diegosouzapw/OmniRoute/issues/12135)) (#12395 — thanks @pacocartones) +- **fix(api-manager):** the API key permissions modal no longer silently drops `allowedCombos` entries its Combo picker cannot render — routing-rule names such as `rt-*`, which the backend already honours — when "All" is clicked and the key is switched back to "Restrict"; those entries now survive the toggle, are listed read-only under the combo list so the count and the list agree, and are saved back verbatim instead of persisting `[]` (deny-all) (#12397 — thanks @pacocartones) +- **fix(catalog):** write the NUL separator of the catalog connection memo key, the provider serviceKind memo key, the Video Bridge promotion group key and a JSON-exactness test fixture as the `\u0000` escape instead of a raw byte — same runtime value, but the raw byte made git, GitHub and ripgrep treat those files as binary (hidden PR diffs, silently skipped searches); a guard test now keeps raw NUL bytes out of `src/`, `open-sse/` and `tests/` (#12403 — thanks @pacocartones) +- **fix(providers):** add `CLAUDE_CODE_CLIENT_VERSION` and `GITHUB_COPILOT_CLI_VERSION` env overrides so Anthropic/Copilot client-version gates can be unblocked without a rebuild ([#12417](https://github.com/diegosouzapw/OmniRoute/issues/12417)) ([#12632](https://github.com/diegosouzapw/OmniRoute/pull/12632)) — thanks @HouMinXi +- **fix(db):** back-fill `last_ping_at` and `last_pinged_reset_key` on `provider_connections` during schema reconciliation so divergent lineages that skipped `123_quota_auto_ping` still accept quota auto-ping writes ([#12470](https://github.com/diegosouzapw/OmniRoute/pull/12470) — thanks @KooshaPari) +- **fix(ci):** document MIT exceptions for `@eloqnt/{config,format-json,format-po}` (next-intl transitive; locked tarballs omit `license`) and keep the A2A lifecycle vitest off the real SQLite persistence seam ([#12581](https://github.com/diegosouzapw/OmniRoute/issues/12581)) ([#12595](https://github.com/diegosouzapw/OmniRoute/pull/12595)) +- OpenCode plugin `/v1/models` catalog fetch now waits 30s by default and attaches HTTP `statusCode` on 401/5xx so host fallback plugins can hop instead of seeing an untyped AbortError/UnknownError. ([#12607](https://github.com/diegosouzapw/OmniRoute/pull/12607)) — thanks @RaviTharuma +- **CI:** the OpenAPI security-tier gate now mirrors `isAlwaysProtectedPath()` in full — it also reads `ALWAYS_PROTECTED_API_PATTERNS`, so the pattern-gated credential routes (`/api/providers/{id}/{claude,codex}-auth/{export,apply-local}`, GHSA-5926-2w35-7h4q) no longer report as unannotated. (#12605) +- **fix(api):** GET /v1/models no longer waits forever on a hung coalesced catalog rebuild; cold-path waits are bounded (`CATALOG_BUILD_TIMEOUT_MS`, default 8s) and a last-good 200 is served when the rebuild times out ([#12627](https://github.com/diegosouzapw/OmniRoute/issues/12627)). ([#12628](https://github.com/diegosouzapw/OmniRoute/pull/12628)) — thanks @RaviTharuma +- **fix(cursor):** a non-array `tool_calls` on an assistant message no longer crashes the cursor request translator with a `TypeError`; both loops now require an array ([#12689](https://github.com/diegosouzapw/OmniRoute/issues/12689)) ([#12691](https://github.com/diegosouzapw/OmniRoute/pull/12691)) — thanks @soroush5 +- **fix(chat):** Chat Completions no longer return empty `content` after a server-owned memory or skills tool; the first provider send and account/model recovery now share one pipeline so a follow-up round-trip can fill the reply ([#12696](https://github.com/diegosouzapw/OmniRoute/issues/12696)) — thanks @HouMinXi ([#12867](https://github.com/diegosouzapw/OmniRoute/pull/12867)) +- **fix(dashboard):** batch-deleting provider keys no longer toasts failure after a successful delete when the confirm button's click event is forwarded as `onAfter` ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) — thanks @HouMinXi +- **fix(glm):** drop the extra 16th argument to `createSSETransformStreamWithLogger` that TypeScript rejected (TS2554) and that never reached the TransformStream ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) ([#12770](https://github.com/diegosouzapw/OmniRoute/pull/12770)) — thanks @HouMinXi +- **fix(dashboard):** pass `nodeMap` into Runtime `QuotaGroup` so a quota monitor in error/exhausted/alerting no longer throws `ReferenceError: nodeMap is not defined`. ([#12868](https://github.com/diegosouzapw/OmniRoute/pull/12868)) — thanks @HouMinXi +- Restricted API keys whose `allowedModels` lists a combo name no longer skip every combo member at pre-dispatch (`ALL_TARGETS_SKIPPED`). Inner-target filtering still applies when the allow-list is a provider prefix or `disableNonPublicModels` is on ([#12899](https://github.com/diegosouzapw/OmniRoute/pull/12899)). — thanks @HouMinXi +- **fix(combos):** Effort-suffixed combo members inherit the base model's `model_context_overrides` row so priority order is not inverted on large requests ([#12926](https://github.com/diegosouzapw/OmniRoute/pull/12926)) — thanks @HouMinXi +- **fix(catalog):** Picker-added `customModels` enter the dispatch-time live catalog so combo members and bare inference no longer 400 ([#12934](https://github.com/diegosouzapw/OmniRoute/pull/12934)) — thanks @HouMinXi +- **fix(docker):** re-derive the Docker build's worker-pool memory budget from the MEASURED ~4.5 GB per-process RSS (the issue owner's own VPS dmesg OOM-killer reproduction) instead of the stale 2560 MB/worker inference #11419 shipped, and lower `OMNIROUTE_BUILD_WORKERS` 3 → 2 so 1 parent + 1 page-data worker (2 processes × 4.5 GB = 9 GB) fits the 12.288 GB (75%) budget on a 16 GB GitHub Actions runner — the previous default (1 parent + 2 workers = 13.5 GB) still overcommitted the runner and kept "Publish to Docker Hub" failing with `cannot allocate memory` after #11419 merged (#7518). ([#11663](https://github.com/diegosouzapw/OmniRoute/pull/11663)) +- **fix(electron):** the packaged Windows desktop build now passes the #7592 cold-restart smoke end to end. Five defects were found and fixed on the way: (1) optional-pack staging failed on any machine whose `tar` is GNU tar — it reads the drive letter in an absolute `-f C:\...` archive path as a remote rsh host (`Cannot connect to C:`), so staging now passes a bare filename with `cwd` at the tarball directory and surfaces tar stderr on failure; (2) the Electron `files` allowlist was missing `lib/loginHeaderCapture.js`, so the packaged main process crashed at startup with `Cannot find module './lib/loginHeaderCapture'`; (3) electron-builder ≥26 injects an `!**/node_modules/**` ignore into every extraResources pattern list that no positive filter can override, which silently dropped the staged runtime `node_modules` (including the better-sqlite3 N-API prebuild) from `resources/app` — a fresh v3.8.50 desktop build would have shipped with no native SQLite driver and reproduced the #7592 sql.js fallback on every machine; an `afterPack` hook now restores the staged `node_modules` after packing; (4) the packaged-app smoke harness redirected `USERPROFILE` into its temp DATA_DIR, but Electron resolves the Roaming profile from `%USERPROFILE%\AppData\Roaming\` (USERPROFILE takes precedence over the APPDATA env var) and the path service throws instead of creating it, so `requestSingleInstanceLock()` returned false and the app exited(0) silently before `app.whenReady()` — the harness now pre-creates the derived tree, and `ensureSmokeEnvDirs` is exported and unit-tested; (5) the #7592 driver assertion parsed a `[DB] Driver: ...` line that the server's primary DB path never printed (only the unused `openDatabaseAsync()` did) — `getDbInstance()` now emits the same line on open so the guard can actually assert the native driver. Also: the smoke env-allowlist unit test hardcoded POSIX paths and could only pass on Linux/macOS; expectations are now host-agnostic, with new regression tests for the USERPROFILE-derived tree and `tarPack` under absolute Windows-style paths ([#11443](https://github.com/diegosouzapw/OmniRoute/pull/11443)) — thanks @jonlwheat2-gif +- fix(sse): stop the auto-combo candidates inspector from silently dropping model-locked/cooled-down rows (#9133) ([#11994](https://github.com/diegosouzapw/OmniRoute/pull/11994)) — thanks @hartmark +- **fix(providers):** Perplexity Web no longer turns upstream stream failures into successful assistant text; pre-content failures remain eligible for fallback, partial output ends with a structured sanitized error, and failed sessions are not persisted ([#12465](https://github.com/diegosouzapw/OmniRoute/pull/12465)) +- **fix(video-bridge):** preserve chronological video and transcript fusion, source-frame timestamps, transcript provenance, full contact-sheet intervals, and pre-abort callback isolation ([#11681](https://github.com/diegosouzapw/OmniRoute/pull/11681)) +- **Z.ai Web:** HTTP 200 streams carrying an upstream error now terminate with a structured failure instead of assistant text plus a normal stop, preserving partial output while allowing pre-content combo fallback. ([#12454](https://github.com/diegosouzapw/OmniRoute/pull/12454)) +- Absorb `Error [AbortError]: request_signal_aborted` and DOMException AbortError shapes in the process-level client-abort crash guard so routine client disconnects no longer kill the server (exit code 7). ([#12165](https://github.com/diegosouzapw/OmniRoute/pull/12165)) — thanks @alvinveroy +- **fix(sse):** Treat Adapta Web `type:error` SSE events as sanitized non-stream failures instead of empty HTTP 200 completions. ([#12459](https://github.com/diegosouzapw/OmniRoute/pull/12459)) +- **fix(autoCombo):** layer 3 (`models_dev_tier`) no longer scores retired models as premium, merge cross-provider capability rows deterministically, and inherit tier scores through variant ids ([#11508](https://github.com/diegosouzapw/OmniRoute/issues/11508)). `getModelsDevTierFitness` now vetoes ids present in `config/quality/model-lifecycle.json` with status `retired` before any other signal; `loadModelCapabilities()` aggregates per `model_id` (any-non-null-true for booleans, max for `limit_context`) instead of last-write-wins over SQLite's undefined row order; a capabilities miss retries through `resolveScoresAs()`'s base id and reports `models_dev_tier:inherited`, matching layers 1–2 (#11492). Adds `getModelsDevTierFitnessWithSource()` and an `invalidateCapabilitiesCache()` test/ops hook. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) — thanks @Meet6338-X +- fix(sse): accept Claude `content:[]` + `stop_reason: max_tokens`/`tool_use` in `detectMalformedNonStream` (match `isEmptyContentResponse`) so Claude Code `/model` probes with `max_tokens: 1` no longer become a false 502 ([#11568](https://github.com/diegosouzapw/OmniRoute/pull/11568)) — thanks @Seramicx +- **fix(security):** sanitize `request.failed` diagnostics before publishing them to live dashboard listeners and replay history, while keeping status, model, provider, latency, and internal call-log diagnostics intact. ([#12469](https://github.com/diegosouzapw/OmniRoute/pull/12469)) +- **fix(pwa):** do not intercept dashboard navigations so Chrome can fall back from a stale HTTP/3 Alt-Svc advertisement (UDP :20128 is unpublished; F5 on a long-lived tab hung until a new tab opened a fresh TCP connection). ([#12767](https://github.com/diegosouzapw/OmniRoute/pull/12767)) — thanks @HouMinXi +- **fix(executors):** handle DuckDuckGo ERR_BN_LIMIT (418) without retrying — when the upstream returns `418 ERR_BN_LIMIT` (rate-limit/ban), the executor now returns the error immediately instead of burning another VQD acquisition that would only count against the IP limit. The retry logic for `418 ERR_CHALLENGE` (unsolved challenge) remains unchanged. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) ([#12110](https://github.com/diegosouzapw/OmniRoute/pull/12110)) — thanks @oyi77 / @Meet6338-X +- Fixed DuckDuckGo streaming responses losing JSON lines and UTF-8 characters split across network chunks. ([#11528](https://github.com/diegosouzapw/OmniRoute/pull/11528)) — thanks @pacocartones +- Harden generated Cloudflare, Vercel, and Deno relays by stripping hop-by-hop, framing, and proxy authentication headers before forwarding requests upstream. ([#11533](https://github.com/diegosouzapw/OmniRoute/pull/11533)) — thanks @pacocartones +- **feat(grok-cli):** Provider Limits shows grok-cli banked reset credits from `GetRemainingResets` (including a real zero; a failed RPC omits the row) and the existing View credits button now calls `ConsumerUiSvc/RedeemReset` for grok-cli. Live tokens use nested fields 10/20/30 (id + Timestamp), not compact 1/2/3. ([#12805](https://github.com/diegosouzapw/OmniRoute/pull/12805)) — thanks @HouMinXi +- **fix(memory):** Embedding Model Quick select, Embedding Source remote dropdown, and Rerank selector now list every configured provider with embedding/rerank support instead of only chat-catalog text matches plus OpenRouter live discovery; a generic OpenAI-compatible `/embeddings` + Cohere-compatible `/rerank` runtime fallback resolves any configured chat provider's embedding/rerank endpoint, so unlisted providers no longer fail with "Unknown embedding provider"; both memory selectors gained a free-text model override ([#11390](https://github.com/diegosouzapw/OmniRoute/pull/11390)) — thanks @rqzbeh +- Harden SQLite upgrades around the historical migration-074 version collision: missing discovery and inspector tables are replayed atomically, pre-existing databases (including setup-created skeletons) receive reusable content-addressed safety snapshots, and Node test/eval probes without `DATA_DIR` are isolated from the operator database. ([#12435](https://github.com/diegosouzapw/OmniRoute/pull/12435)) +- **fix(dashboard):** Moonshot/Kimi Open Platform voucher and cash leftover percentages follow the bucket balance, so an empty wallet no longer paints those rows as 100% while Available is 0% ([#12733](https://github.com/diegosouzapw/OmniRoute/pull/12733)) — thanks @HouMinXi +- **fix(combo):** return non-retryable HTTP 400 when all candidates for a pinned native Codex turn are unavailable due to model-scoped lockout, terminating the turn cleanly while preserving turn continuity and enabling standard Combo routing on subsequent turns ([#12240](https://github.com/diegosouzapw/OmniRoute/pull/12240)) — thanks @mdigitalbh81 +- Fixed auto-router strategies preserving the selected connection through ranking and dispatch when multiple connections share the same provider and model. ([#11530](https://github.com/diegosouzapw/OmniRoute/pull/11530)) — thanks @pacocartones +- Preserve Chat Completions JSON-object response formats when translating requests to the Responses API. ([#11529](https://github.com/diegosouzapw/OmniRoute/pull/11529)) — thanks @pacocartones +- Fix idempotency fingerprints so Responses API requests include `input` and other semantic generation fields using stable serialization, while excluding credentials and request metadata. ([#11532](https://github.com/diegosouzapw/OmniRoute/pull/11532)) — thanks @pacocartones +- **fix(api):** Generated API CLI commands now enforce required OpenAPI request bodies; Combo test commands forward the required `comboName` body, while API keys created by older writers after migration 149 preserve legacy allow-all Combo access without widening explicit empty allowlists — thanks @marcelokarval ([#12070](https://github.com/diegosouzapw/OmniRoute/pull/12070)) +- **fix(grok-web):** treat upstream streaming failures as failures instead of successful assistant text: error-only streams now fail readiness with HTTP 502, while failures after legitimate content preserve that partial output and terminate through the sanitized stream failure path without a normal `stop` completion. ([#12458](https://github.com/diegosouzapw/OmniRoute/pull/12458)) +- HuggingChat now turns HTTP 200 JSONL generation failures into a sanitized 502 before content, or a fixed public stream failure after partial output, so fallback and request persistence no longer record a false successful stop. ([#12456](https://github.com/diegosouzapw/OmniRoute/pull/12456)) +- **fix(providers):** keep 1min.ai HTTP 200 stream errors out of assistant content, preserve partial output, and expose sanitized terminal errors so pre-content failures can fall back. ([#12466](https://github.com/diegosouzapw/OmniRoute/pull/12466)) +- **fix(sessions):** preserve legacy Sessions data when the additive exclusive-session projection is unavailable, returning an empty projection and warning only once per contiguous outage instead of failing the endpoint; the Sessions badge intentionally reflects the merged legacy and exclusive row count introduced by [#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389) ([#11469](https://github.com/diegosouzapw/OmniRoute/pull/11469)) — thanks @KaspaPulse +- Render Nimble Search with the generic provider icon and serve Opper's proven logo locally. ([#12415](https://github.com/diegosouzapw/OmniRoute/pull/12415)) +- **fix(quality):** `config/quality/provider-assets-provenance.jsonl` was stale against the physical `public/providers/` tree (still listing 225 assets and 5 duplicate-content groups after several provider-retirement PRs removed 85 icon files), which would have made `check:provider-asset-provenance` fail on the next full run. Reconciled the manifest to the real 142-file snapshot (142 asset records, 1 duplicate-content group, 2 newly-registered files marked `unresolved` pending provenance research), repinned `auditedCommit`, and updated the regression test's hardcoded counts to match. ([#11876](https://github.com/diegosouzapw/OmniRoute/pull/11876)) +- fix(ui): wrap quota card footer action buttons to prevent clipping Refresh button on narrow cards — when extra action buttons such as `View credits` are present on Codex cards, allow the footer row to flex-wrap with `shrink-0` buttons so `Refresh now` remains visible and clickable across all responsive card widths ([#11464](https://github.com/diegosouzapw/OmniRoute/pull/11464)) — thanks @adevwithpurpose +- Keep Antigravity Gemini usable when the same connection's Claude weekly quota is empty; generic quota cache stays per-connection for every other provider. ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) — thanks @HouMinXi +- Fixed Tailscale tunnel setup to reject ports outside the valid 1-65535 range and safely install Windows MSI packages from paths containing apostrophes. ([#11534](https://github.com/diegosouzapw/OmniRoute/pull/11534)) — thanks @pacocartones +- Classify Cloudflared and Tailscale process-management routes as local-only and spawn-capable while preserving remote access to read-only tunnel status endpoints. ([#11531](https://github.com/diegosouzapw/OmniRoute/pull/11531)) — thanks @pacocartones +- Electron release workflow: the `publish-npm` job now grants `actions: read` to the reusable `npm-publish.yml` it calls (its `publish` job requests it), which is what made GitHub refuse the whole v3.8.50 run at startup and ship the release with zero desktop assets; a `workflow_dispatch` now builds the requested tag instead of the dispatching branch and can skip the npm leg (`publish_npm=false`) when only re-attaching assets ([#11974](https://github.com/diegosouzapw/OmniRoute/pull/11974)) +- **fix(dashboard):** The Combos page usage guide now reads its dismissal through `useSyncExternalStore` instead of correcting SSR state inside an effect, removing an extra commit of the page tree on every load (and the `react-hooks/set-state-in-effect` error it raised). ([#12671](https://github.com/diegosouzapw/OmniRoute/pull/12671)) +- Electron release: `electron/package-lock.json` regained the optional `electron-builder-squirrel-windows` subtree (13 entries) that `npm ci` had been refusing as out of sync, `electron-release.yml` gained a `build_ref` dispatch input and stops regenerating release notes on a re-attach dispatch, and the npm publish workflow attaches the SBOM to the GitHub Release on dispatch publishes too — so the v3.8.51 tag ships every desktop asset and the SBOM like v3.8.49 did ([#12022](https://github.com/diegosouzapw/OmniRoute/pull/12022)) +- Drained the reds every PR against `release/v3.8.51` was born with: documented the three service-worker build-id variables, registered the six retirement/tunnel tests with the mutation gate, approved `eslint-plugin-react-hooks` in the dependency allowlist, added the six `combo.sort.*` strings to `vi` and `pt-BR`, pointed the ChatGPT Web doc at the real migration-168 test, worded the g4f hint around the member key, and realigned four tests to the retired-provider catalog and the legacy-schema fixtures the retirement migrations touch. ([#11940](https://github.com/diegosouzapw/OmniRoute/pull/11940)) +- **fix(providers):** Claude, Grok, LMArena, Notion, and Perplexity web-cookie transports now use pooled `wreq-js` 3.2 instead of the native sidecar, with all nine supported bindings pinned and audited, and the applicable platform binding plus native-license evidence included in each release artifact ([#12429](https://github.com/diegosouzapw/OmniRoute/pull/12429), supersedes [#11753](https://github.com/diegosouzapw/OmniRoute/pull/11753)). +- **fix(providers):** Zed Hosted streaming failures now trigger fallback before content and end partial streams with a sanitized structured error instead of fake assistant text and a normal-success stop. ([#12455](https://github.com/diegosouzapw/OmniRoute/pull/12455)) +- **fix(test):** capture hook result after render (direct commit `b85d0abb0b`) +- **fix(sync):** repair the two regressions the v3.8.50 sync-back left on release/v3.8.51 (direct commit `5ade9e0851`) +- **revert(models):** drop the memo reset hook ported from #10055 (direct commit `282de841ae`) +- **fix(typecheck):** guard the optional connectionId before the reactive model sync (direct commit `f2825bad75`) +- **fix(quota-share):** release the winner's reserved in-flight slot (#11371) ([#11408](https://github.com/diegosouzapw/OmniRoute/pull/11408)) — thanks @oyi77 +- **fix(providers):** stop silently dropping reasoning effort on opencode families (#10788) ([#11409](https://github.com/diegosouzapw/OmniRoute/pull/11409)) — thanks @oyi77 +- **fix(combo):** enable genuine quota-aware routing for generic providers (antigravity, claude, etc.) ([#11411](https://github.com/diegosouzapw/OmniRoute/pull/11411)) — thanks @HouMinXi +- **fix(oauth):** allow expired token retry before deactivation and activate expired retry loop (#P0) ([#11414](https://github.com/diegosouzapw/OmniRoute/pull/11414)) — thanks @HouMinXi +- **perf(executors):** lazy-load the executor registry — defer class imports + construction to first use (#11220) ([#11421](https://github.com/diegosouzapw/OmniRoute/pull/11421)) — thanks @oyi77 +- **fix:** make release/v3.8.50 CI gates green (run 32786966560 root causes) ([#11450](https://github.com/diegosouzapw/OmniRoute/pull/11450)) — thanks @jonlwheat2-gif +- **fix(sse):** exact-domain cookie match and origin-equality URL assertions ([#11452](https://github.com/diegosouzapw/OmniRoute/pull/11452)) — thanks @hartmark +- **fix(providers):** send Responses API payload during OpenAI-compatible validation ([#11454](https://github.com/diegosouzapw/OmniRoute/pull/11454)) — thanks @tuandinh0801 +- **fix(command-code):** fallback to /alpha/generate for Go plan without Provider API access ([#11455](https://github.com/diegosouzapw/OmniRoute/pull/11455)) — thanks @Chewji9875 +- **fix(responses-continuation):** fail closed on a log-truncated stored input/output array ([#11473](https://github.com/diegosouzapw/OmniRoute/pull/11473)) — thanks @hartmark +- **fix(cli):** write jcode settings as [providers.omniroute] in config.toml ([#11484](https://github.com/diegosouzapw/OmniRoute/pull/11484)) — thanks @hongnoul +- **fix(logging):** raise chat-log array truncation cap to 1000 and unify duplicate implementations ([#11499](https://github.com/diegosouzapw/OmniRoute/pull/11499)) — thanks @hartmark +- **fix:** restore live websocket URL helpers ([#11509](https://github.com/diegosouzapw/OmniRoute/pull/11509)) — thanks @PauloFH +- **perf(core):** eliminate async waterfalls across home, cache, and db operations (#11396) ([#11512](https://github.com/diegosouzapw/OmniRoute/pull/11512)) — thanks @HouMinXi +- **fix(kiro):** add runtime.us-east-1.kiro.dev as first-attempt endpoint ([#11517](https://github.com/diegosouzapw/OmniRoute/pull/11517)) — thanks @rifqiawl +- **fix(providers):** remove duplicate 128-tool truncation in OpencodeExecutor (#11444) ([#11518](https://github.com/diegosouzapw/OmniRoute/pull/11518)) — thanks @HouMinXi +- **fix(providers):** poll onboardUser LRO before classifying BYOP ([#11519](https://github.com/diegosouzapw/OmniRoute/pull/11519)) — thanks @rifqiawl +- **fix(translator):** preserve exact tool name casing for Claude tool_use payloads (#11487) ([#11520](https://github.com/diegosouzapw/OmniRoute/pull/11520)) — thanks @HouMinXi +- **fix(kimi-web):** align health probe with executor domain ([#11521](https://github.com/diegosouzapw/OmniRoute/pull/11521)) — thanks @pacocartones +- **fix(cli):** preserve existing admin password during setup ([#11522](https://github.com/diegosouzapw/OmniRoute/pull/11522)) — thanks @pacocartones +- **fix(mitm):** keep Windows DNS elevation off the POSIX sudo path (#11430) ([#11542](https://github.com/diegosouzapw/OmniRoute/pull/11542)) — thanks @official-burak +- **fix(sse):** scale chat admission by ingest byte budget instead of a fixed request count ([#11548](https://github.com/diegosouzapw/OmniRoute/pull/11548)) — thanks @TheDemonTuan +- **fix:** accept Gemini Web cookie exports ([#11554](https://github.com/diegosouzapw/OmniRoute/pull/11554)) — thanks @rafacpti23 +- **fix(i18n):** complete Brazilian Portuguese translations ([#11558](https://github.com/diegosouzapw/OmniRoute/pull/11558)) — thanks @rafacpti23 +- **fix(sse):** explicit types for openai-responses pureHelpers — clears last failing core typecheck gate ([#11567](https://github.com/diegosouzapw/OmniRoute/pull/11567)) — thanks @jonlwheat2-gif +- **fix(opencode):** hydrate Proxy Pool references for no-auth connections ([#11584](https://github.com/diegosouzapw/OmniRoute/pull/11584)) — thanks @AStupidBear +- **fix(ci):** resolve inherited release/v3.8.51 base-red CI failures ([#11608](https://github.com/diegosouzapw/OmniRoute/pull/11608)) — thanks @jonlwheat2-gif +- **fix(db):** fallback load STORAGE_ENCRYPTION_KEY from env files ([#11614](https://github.com/diegosouzapw/OmniRoute/pull/11614)) — thanks @benzntech +- **fix(providers):** mark Brave Search as no longer having a free tier ([#11621](https://github.com/diegosouzapw/OmniRoute/pull/11621)) — thanks @turbolego +- **fix:** pass tool result content through as string in OpenAI/Claude -> Gemini translation ([#11624](https://github.com/diegosouzapw/OmniRoute/pull/11624)) — thanks @Hsia97 +- **fix(sse):** scope credential health to exact key ([#11628](https://github.com/diegosouzapw/OmniRoute/pull/11628)) — thanks @KaspaPulse +- **fix(install):** guard prepare script against missing husky ([#11634](https://github.com/diegosouzapw/OmniRoute/pull/11634)) — thanks @Prajeeth-12 +- **fix(security):** redact NVIDIA_API_KEY at the log sink in the ad-hoc diag script ([#11639](https://github.com/diegosouzapw/OmniRoute/pull/11639)) +- **fix(types):** clear the two type regressions red on release/v3.8.51 ([#11640](https://github.com/diegosouzapw/OmniRoute/pull/11640)) — thanks @jonlwheat2-gif +- **fix(build):** resolve workspace: protocol dependencies to concrete semver versions during prepublish (#11540) ([#11641](https://github.com/diegosouzapw/OmniRoute/pull/11641)) — thanks @HouMinXi +- **fix(search):** properly resolve configured search connection in /v1/responses pipeline (#11524) ([#11642](https://github.com/diegosouzapw/OmniRoute/pull/11642)) — thanks @HouMinXi +- **fix(authz):** trust cf-connecting-ip for via-proxy detection when peer is Cloudflare edge (#11514) ([#11643](https://github.com/diegosouzapw/OmniRoute/pull/11643)) — thanks @HouMinXi +- **fix(security):** compare the OIDC state cookie in constant time ([#11646](https://github.com/diegosouzapw/OmniRoute/pull/11646)) +- **fix(security):** namespace the dedup hash by the calling API key ([#11649](https://github.com/diegosouzapw/OmniRoute/pull/11649)) +- **fix(models):** gate the Codex-native catalog loop on prefix mode (#11632) ([#11662](https://github.com/diegosouzapw/OmniRoute/pull/11662)) — thanks @xiaoyaner0201 +- **fix(dashboard):** project Codex routing quota in account pools ([#11666](https://github.com/diegosouzapw/OmniRoute/pull/11666)) — thanks @KaspaPulse +- **fix(ddgw):** normaliza system no upstream da release v3.8.51 ([#11692](https://github.com/diegosouzapw/OmniRoute/pull/11692)) — thanks @RhianB14 +- **fix(memory):** honor category filter in GET /api/memory ([#11699](https://github.com/diegosouzapw/OmniRoute/pull/11699)) — thanks @Sabeekhann +- **fix(ci):** detect stale workspace lockfile entries ([#11714](https://github.com/diegosouzapw/OmniRoute/pull/11714)) — thanks @pacocartones +- **fix(ci):** stamp dist/BUILD_SHA before the npm publish provenance gate ([#11721](https://github.com/diegosouzapw/OmniRoute/pull/11721)) +- **fix(docker):** decouple the best-effort Bun image from the release manifest ([#11724](https://github.com/diegosouzapw/OmniRoute/pull/11724)) +- **perf(compression):** avoid cloning memo misses twice ([#11727](https://github.com/diegosouzapw/OmniRoute/pull/11727)) — thanks @RaviTharuma +- **fix(docker):** let the bun digest artifact be absent, not fatal ([#11740](https://github.com/diegosouzapw/OmniRoute/pull/11740)) +- **fix(shared):** block cloud-metadata hosts under default remote-image guard ([#11755](https://github.com/diegosouzapw/OmniRoute/pull/11755)) — thanks @santosraju99-hub +- **fix(api):** preserve caller-provided X-Correlation-Id on chat completions ([#11760](https://github.com/diegosouzapw/OmniRoute/pull/11760)) — thanks @aniruddhaadak80 +- **fix(resilience):** lock permanently retired models instead of short backoff (Gemini ban prevention) ([#11762](https://github.com/diegosouzapw/OmniRoute/pull/11762)) — thanks @turbolego +- **fix:** mark Vercel AI Gateway as passthroughModels ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **fix(resilience):** stop hammering permanently-moved endpoints and billing-suspended accounts ([#11774](https://github.com/diegosouzapw/OmniRoute/pull/11774)) — thanks @turbolego +- **fix(lease):** remove global static reservation and gate routing on live active lease occupancy ([#11775](https://github.com/diegosouzapw/OmniRoute/pull/11775)) — thanks @TheDemonTuan +- **fix(ci):** size the install-upgrade gate to a measured run, and log the pack cost ([#11776](https://github.com/diegosouzapw/OmniRoute/pull/11776)) +- **fix(pwa):** stop serving the stale shell after deploys ([#11779](https://github.com/diegosouzapw/OmniRoute/pull/11779)) — thanks @HouMinXi +- **fix(combo):** lock GitHub models rejected as "not supported" for future requests ([#11781](https://github.com/diegosouzapw/OmniRoute/pull/11781)) — thanks @turbolego +- **fix(cli):** probe both IPv4 and IPv6 loopback for server readiness (#11766) ([#11794](https://github.com/diegosouzapw/OmniRoute/pull/11794)) — thanks @vermasomesh835 +- **fix(sse):** preserve CLIProxyAPI wrappers per connection ([#11797](https://github.com/diegosouzapw/OmniRoute/pull/11797)) — thanks @RaviTharuma +- **fix(providers):** honor model auto-fetch opt-in on create ([#11798](https://github.com/diegosouzapw/OmniRoute/pull/11798)) — thanks @RaviTharuma +- **fix(dashboard):** make model auto-fetch order independent ([#11805](https://github.com/diegosouzapw/OmniRoute/pull/11805)) — thanks @RaviTharuma +- **fix(docker):** make GHA cache exports best effort ([#11806](https://github.com/diegosouzapw/OmniRoute/pull/11806)) — thanks @RaviTharuma +- **fix(kiro):** do not permanently ban on 'User is not authorized to make this call' ([#11809](https://github.com/diegosouzapw/OmniRoute/pull/11809)) — thanks @Deftera186 +- **fix(sse):** keep cache-write tokens in OpenAI-shaped usage ([#11814](https://github.com/diegosouzapw/OmniRoute/pull/11814)) — thanks @TheDemonTuan +- **fix(docs):** resolve relative markdown and wiki links across Fumadocs and GitHub wiki ([#11834](https://github.com/diegosouzapw/OmniRoute/pull/11834)) — thanks @AndrianBalanescu +- **fix(chat):** preserve unstripped model string for passthrough provider routing ([#11840](https://github.com/diegosouzapw/OmniRoute/pull/11840)) — thanks @raheemuddin786 +- **fix(mcp):** avoid async initialization inside sync esm wrappers (#11569) ([#11849](https://github.com/diegosouzapw/OmniRoute/pull/11849)) — thanks @HouMinXi +- **fix(sse):** preserve coherent markdown token boundaries across stream translation chunks (#11606) ([#11851](https://github.com/diegosouzapw/OmniRoute/pull/11851)) — thanks @HouMinXi +- **fix(ci):** run the install-upgrade gate on disk, not on the /tmp tmpfs ([#11855](https://github.com/diegosouzapw/OmniRoute/pull/11855)) +- **fix(skills):** expand shorthand property types in injected tool schemas ([#11857](https://github.com/diegosouzapw/OmniRoute/pull/11857)) — thanks @alvinveroy +- **fix(ci):** publish npm from a hosted runner so provenance is accepted ([#11877](https://github.com/diegosouzapw/OmniRoute/pull/11877)) +- **fix(lint):** pin react hooks plugin to 7.0.1 ([#11886](https://github.com/diegosouzapw/OmniRoute/pull/11886)) +- **fix(ci):** surface and clear release-green ESLint failure ([#11890](https://github.com/diegosouzapw/OmniRoute/pull/11890)) — thanks @MumuTW +- **fix(guardrails):** prevent duplicate prompt-injection-guard log output ([#11936](https://github.com/diegosouzapw/OmniRoute/pull/11936)) — thanks @patrykkopycinski +- **fix(auth):** downgrade expected transient states from warn to debug ([#11937](https://github.com/diegosouzapw/OmniRoute/pull/11937)) — thanks @patrykkopycinski +- **fix(codex):** fail over image generation for imported free plans ([#11948](https://github.com/diegosouzapw/OmniRoute/pull/11948)) — thanks @Bl0ck154 +- **fix(codex):** normalize non-stream responses ([#11951](https://github.com/diegosouzapw/OmniRoute/pull/11951)) — thanks @Bl0ck154 +- **fix(images):** forward Antigravity image size ([#11952](https://github.com/diegosouzapw/OmniRoute/pull/11952)) — thanks @Bl0ck154 +- **fix(db):** invalidate connection cache after upsert ([#11953](https://github.com/diegosouzapw/OmniRoute/pull/11953)) — thanks @Bl0ck154 +- **fix(codex):** restore imported account state ([#11954](https://github.com/diegosouzapw/OmniRoute/pull/11954)) — thanks @Bl0ck154 +- **fix(sse):** give extended-thinking targets the reasoning readiness budget ([#11959](https://github.com/diegosouzapw/OmniRoute/pull/11959)) — thanks @abhisheksharma2411 +- **fix(dashboard):** use opaque background and readable text color on cost chart tooltips ([#11960](https://github.com/diegosouzapw/OmniRoute/pull/11960)) — thanks @ZaimMarzuki +- **fix(antigravity):** send complete loadCodeAssist metadata (ideType/platform/pluginType as numeric enums) ([#11969](https://github.com/diegosouzapw/OmniRoute/pull/11969)) — thanks @rifqiawl +- **fix(core):** resolve DB init race condition and reasoning translation ([#12003](https://github.com/diegosouzapw/OmniRoute/pull/12003)) — thanks @raheemuddin786 +- **fix(admission):** exclude reclaimable page cache from the cgroup pressure ratio ([#12017](https://github.com/diegosouzapw/OmniRoute/pull/12017)) — thanks @HouMinXi +- **fix(build):** prune non-production trees in NFT trace excludes and tsconfig ([#12028](https://github.com/diegosouzapw/OmniRoute/pull/12028)) — thanks @Chewji9875 +- **fix(migrations):** add renamed migration compatibility for 056/073/077/101 ([#12036](https://github.com/diegosouzapw/OmniRoute/pull/12036)) — thanks @oyi77 +- **fix(sse):** default crash-guard logger to console.warn, not console ([#12042](https://github.com/diegosouzapw/OmniRoute/pull/12042)) — thanks @HouMinXi +- **fix(catalog):** derive vision/modalities for built-in auto combos from effective target pool ([#12046](https://github.com/diegosouzapw/OmniRoute/pull/12046)) — thanks @Prajeeth-12 +- **fix(release):** the packaged-app smoke verifies the database opened, not a driver line the primary path never prints (twin of #12032) ([#12047](https://github.com/diegosouzapw/OmniRoute/pull/12047)) +- **fix(barrel):** migrate src/app/ off the @/lib/localDb barrel import (#11795 Phase 2) ([#12051](https://github.com/diegosouzapw/OmniRoute/pull/12051)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate src/lib/ off the localDb barrel to direct db imports (#59) ([#12052](https://github.com/diegosouzapw/OmniRoute/pull/12052)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate open-sse, src/shared, src/sse, src/models, src/domain off the @/lib/localDb barrel import (#11795 Phase 4) ([#12053](https://github.com/diegosouzapw/OmniRoute/pull/12053)) — thanks @jonlwheat2-gif +- **fix(barrel):** delete the @/lib/localDb barrel — every consumer migrated (#11795 Phase 5) ([#12055](https://github.com/diegosouzapw/OmniRoute/pull/12055)) — thanks @jonlwheat2-gif +- **fix(radar):** restore D12 public boundary ([#12057](https://github.com/diegosouzapw/OmniRoute/pull/12057)) +- **fix(dev):** bound webpack and Tailwind scans ([#12075](https://github.com/diegosouzapw/OmniRoute/pull/12075)) — thanks @backryun +- **fix(dev):** isolate root layout settings reads ([#12076](https://github.com/diegosouzapw/OmniRoute/pull/12076)) — thanks @backryun +- **fix(dev):** reduce instrumentation executor fan-out (phase 3) ([#12078](https://github.com/diegosouzapw/OmniRoute/pull/12078)) — thanks @backryun +- **fix(dev):** make logging resources HMR-singleton ([#12079](https://github.com/diegosouzapw/OmniRoute/pull/12079)) — thanks @backryun +- **fix(sse):** keep unavailable forced connections scoped ([#12080](https://github.com/diegosouzapw/OmniRoute/pull/12080)) — thanks @keeltrace +- **fix(dev):** isolate batch dispatch from instrumentation ([#12081](https://github.com/diegosouzapw/OmniRoute/pull/12081)) — thanks @backryun +- **fix(ci):** reconcile release test contract drift ([#12082](https://github.com/diegosouzapw/OmniRoute/pull/12082)) — thanks @backryun +- **fix(ci):** accept CVE-2025-68121 in the prebuilt tls-client .so, auto-close base-red issues, guard Scorecard on the default branch ([#12085](https://github.com/diegosouzapw/OmniRoute/pull/12085)) +- **fix(db):** drop three consumer-less 1proxy exports — dead-code base-red on release/v3.8.51 after the barrel deletion (#12055) ([#12087](https://github.com/diegosouzapw/OmniRoute/pull/12087)) +- **fix(api):** clear the six API-route TypeScript regressions the new gate landed red on ([#12094](https://github.com/diegosouzapw/OmniRoute/pull/12094)) +- **fix(release):** never let the tag-push Create Release append auto notes to the curated body ([#12096](https://github.com/diegosouzapw/OmniRoute/pull/12096)) +- **fix(sse):** honor CLIProxyAPI environment API key ([#12099](https://github.com/diegosouzapw/OmniRoute/pull/12099)) — thanks @RaviTharuma +- **fix(oauth):** bind Google refresh to the client that issued the token ([#12106](https://github.com/diegosouzapw/OmniRoute/pull/12106)) — thanks @HouMinXi +- **fix(dashboard):** make RequestLoggerDetail loadable outside Next — CSS via globals.css + CJS/ESM interop (#11703 base-reds) ([#12114](https://github.com/diegosouzapw/OmniRoute/pull/12114)) +- **fix(leases):** project status lease row to lease columns so joined connection PII never escapes ([#12115](https://github.com/diegosouzapw/OmniRoute/pull/12115)) — thanks @geek007git +- **fix(plugins):** do not kill the plugin process when a fire-and-forget hook times out ([#12116](https://github.com/diegosouzapw/OmniRoute/pull/12116)) — thanks @geek007git +- **fix(guardrails):** restore injection-guard logging on middleware-only routes ([#12117](https://github.com/diegosouzapw/OmniRoute/pull/12117)) — thanks @geek007git +- **fix(plugins):** refresh stored manifest from disk on activate so new hook fields reach existing installs ([#12120](https://github.com/diegosouzapw/OmniRoute/pull/12120)) — thanks @geek007git +- **fix(codex):** preserve existing provider state when bulk-import upserts a matching connection ([#12122](https://github.com/diegosouzapw/OmniRoute/pull/12122)) — thanks @geek007git +- **fix(ci):** clear the two base-reds the 2026-08-30 merge batch left on release/v3.8.51 (round 3) ([#12123](https://github.com/diegosouzapw/OmniRoute/pull/12123)) +- **fix(sse):** strip type:'custom' from Claude tools on agentrouter dispatch ([#12126](https://github.com/diegosouzapw/OmniRoute/pull/12126)) — thanks @wahidsadik371-coder +- **fix(sse):** stop advertising video providers the dispatcher cannot run ([#12131](https://github.com/diegosouzapw/OmniRoute/pull/12131)) — thanks @quiterunner-commits +- **fix(search):** treat HTTP 432 and plan limit errors as transient cooldown ([#12139](https://github.com/diegosouzapw/OmniRoute/pull/12139)) — thanks @HouMinXi +- **fix(sse):** refuse an AI Horde queue that cannot fit the request budget ([#12143](https://github.com/diegosouzapw/OmniRoute/pull/12143)) — thanks @quiterunner-commits +- **fix(ci):** clear the base-reds the afternoon merge batch left on release/v3.8.51 (round 5: provider count 352, TS2554/TS2677) ([#12144](https://github.com/diegosouzapw/OmniRoute/pull/12144)) +- **fix(translator):** strip neutral tool_choice when tools absent in Responses-to-Chat (#12141) ([#12166](https://github.com/diegosouzapw/OmniRoute/pull/12166)) — thanks @HouMinXi +- **fix(guardrails):** pass providerId to getResolvedModelCapabilities in checkComboVision (#12112) ([#12169](https://github.com/diegosouzapw/OmniRoute/pull/12169)) — thanks @HouMinXi +- **fix(translator):** strip plaintext reasoning content for opaque responses backends (#12128) ([#12171](https://github.com/diegosouzapw/OmniRoute/pull/12171)) — thanks @HouMinXi +- **perf(stream):** compile hot-path regexes once, bound token caches, fix quadratic buffering ([#12179](https://github.com/diegosouzapw/OmniRoute/pull/12179)) — thanks @opensource-elearning +- **fix:** resolve compression worker file using runtime anchors instead of… ([#12183](https://github.com/diegosouzapw/OmniRoute/pull/12183)) — thanks @NightStalker-87 +- **fix:** prevent Claude Code session kills via liveness-aware readiness + auto model echo ([#12189](https://github.com/diegosouzapw/OmniRoute/pull/12189)) — thanks @opensource-elearning +- **fix(translator):** preserve falsy primitive values in Gemini and Antigravity function response results ([#12191](https://github.com/diegosouzapw/OmniRoute/pull/12191)) — thanks @Siva010 +- **fix(oauth):** mark empty Antigravity projectId as degraded and clear stale errors (#11284) ([#12205](https://github.com/diegosouzapw/OmniRoute/pull/12205)) — thanks @HouMinXi +- **fix(providers):** gate the Codex auto-ping usage read on the shared quota throttle ([#12209](https://github.com/diegosouzapw/OmniRoute/pull/12209)) — thanks @abhisheksharma2411 +- **fix(combos):** prioritize SQLite row id over inner JSON id and notify delete errors ([#12213](https://github.com/diegosouzapw/OmniRoute/pull/12213)) — thanks @HouMinXi +- **fix(providers):** mark featherless.ai as no longer having a free tier (`hasFree: false`) ([#12216](https://github.com/diegosouzapw/OmniRoute/pull/12216)) — thanks @turbolego +- **fix(deepseek-web):** stop Turbopack dev panic in the PoW worker path resolver ([#12221](https://github.com/diegosouzapw/OmniRoute/pull/12221)) — thanks @hartmark +- **fix(chat-admission):** clarify local 503 source ([#12223](https://github.com/diegosouzapw/OmniRoute/pull/12223)) — thanks @keeltrace +- **fix(dev):** silence webpack runtime module warnings ([#12228](https://github.com/diegosouzapw/OmniRoute/pull/12228)) — thanks @backryun +- **fix(db):** use module.require for CommonJS runtime driver loading ([#12230](https://github.com/diegosouzapw/OmniRoute/pull/12230)) — thanks @raheemuddin786 +- **fix(memory):** honest probe-driven FTS5 keyword status + memory_id rowid sync ([#12231](https://github.com/diegosouzapw/OmniRoute/pull/12231)) — thanks @raheemuddin786 +- **fix(system):** propagate abort signal to stream reader in HTTP version checks ([#12232](https://github.com/diegosouzapw/OmniRoute/pull/12232)) — thanks @raheemuddin786 +- **fix(resilience):** isolate local host execution errors from provider circuit breakers ([#12233](https://github.com/diegosouzapw/OmniRoute/pull/12233)) — thanks @raheemuddin786 +- **fix(sse):** sort injected tools deterministically for prompt caching ([#12234](https://github.com/diegosouzapw/OmniRoute/pull/12234)) — thanks @raheemuddin786 +- **fix(combo):** always clear the loop-safety timer, not just on the happy path (#11804) ([#12245](https://github.com/diegosouzapw/OmniRoute/pull/12245)) +- **fix(quality):** base-red pair — stryker tap registration + turn-pin suites aligned to the window gate ([#12255](https://github.com/diegosouzapw/OmniRoute/pull/12255)) — thanks @backryun +- **fix(dev):** qualify Turbopack runtime boundaries ([#12258](https://github.com/diegosouzapw/OmniRoute/pull/12258)) — thanks @backryun +- **fix(combo):** fall back to full pool when collapsed sole survivor is context-too-small ([#12278](https://github.com/diegosouzapw/OmniRoute/pull/12278)) — thanks @opensource-elearning +- **fix(combo):** bound the pre-dispatch unavailable skip so a stale label cannot dark a pool (#12168) ([#12285](https://github.com/diegosouzapw/OmniRoute/pull/12285)) +- **fix(security):** strip Qwen/Alibaba console-session cookies from provider API responses ([#12287](https://github.com/diegosouzapw/OmniRoute/pull/12287)) +- **fix(usage):** console-aware Token Plan guidance + subscription hint on bailian 401 ([#12288](https://github.com/diegosouzapw/OmniRoute/pull/12288)) +- **fix(resourcePressure):** log numeric detail on every rejection, recover faster ([#12293](https://github.com/diegosouzapw/OmniRoute/pull/12293)) — thanks @hartmark +- **fix(release):** drain the 2026-09-01 base-red window — passthrough usage regression + radarPage i18n keys ([#12327](https://github.com/diegosouzapw/OmniRoute/pull/12327)) +- **fix(ci):** openapi-security-tiers checker must honor routeGuard patterns + imported prefixes ([#12350](https://github.com/diegosouzapw/OmniRoute/pull/12350)) — thanks @ggiak +- **perf(ci):** cache node_modules in the npm-ci-retry composite (#8084 D3) ([#12408](https://github.com/diegosouzapw/OmniRoute/pull/12408)) +- **revert(ui):** point CTAs back at their real destinations ([#12410](https://github.com/diegosouzapw/OmniRoute/pull/12410)) +- **fix(release):** drain the 2026-09-02 base-red — rerank-providers import + api-typecheck baseline ratchet ([#12414](https://github.com/diegosouzapw/OmniRoute/pull/12414)) +- **fix(types):** make system prompt injection noImplicitAny-safe ([#12416](https://github.com/diegosouzapw/OmniRoute/pull/12416)) +- **fix(quality):** validate typecheck baseline schema ([#12419](https://github.com/diegosouzapw/OmniRoute/pull/12419)) +- **fix(memory):** resolve rerank provider node cache import ([#12421](https://github.com/diegosouzapw/OmniRoute/pull/12421)) +- **fix(ci):** repair release-root regressions — pack dedup, web-session syntax, uc-image ids ([#12423](https://github.com/diegosouzapw/OmniRoute/pull/12423)) — thanks @backryun +- **fix(providers):** separate MaxAI and UC credential contracts ([#12431](https://github.com/diegosouzapw/OmniRoute/pull/12431)) +- **fix(cli):** preserve Claude settings on config set ([#12432](https://github.com/diegosouzapw/OmniRoute/pull/12432)) — thanks @KrzysiekSko +- **fix(providers):** repair the maxai credential block truncated by merge auto-resolve ([#12433](https://github.com/diegosouzapw/OmniRoute/pull/12433)) +- **fix(quality):** record the 2026-09-02 merged growth in the file-size baseline ([#12434](https://github.com/diegosouzapw/OmniRoute/pull/12434)) +- **fix(uc):** route UC error strings through sanitizeErrorMessage; allowlist the retired codex id ([#12437](https://github.com/diegosouzapw/OmniRoute/pull/12437)) +- **fix(adapta):** redact streamed upstream errors ([#12438](https://github.com/diegosouzapw/OmniRoute/pull/12438)) +- **fix(sse):** remap non-contiguous upstream tool_calls index to a gap-free output_index ([#12445](https://github.com/diegosouzapw/OmniRoute/pull/12445)) — thanks @hartmark +- **fix(quality):** detect duplicate tool_calls entries in one response ([#12446](https://github.com/diegosouzapw/OmniRoute/pull/12446)) — thanks @hartmark +- **fix(conversations):** resolve turn content OmniRoute never sends back to the client ([#12447](https://github.com/diegosouzapw/OmniRoute/pull/12447)) — thanks @hartmark +- **fix(combo):** do not treat credits-exhausted 401 as auth skip ([#12449](https://github.com/diegosouzapw/OmniRoute/pull/12449)) — thanks @RaviTharuma +- **fix(auth):** do not park healthy quota accounts as expired ([#12452](https://github.com/diegosouzapw/OmniRoute/pull/12452)) — thanks @RaviTharuma +- **fix(responses-continuation):** fail closed on a collector-truncated, empty output array ([#12460](https://github.com/diegosouzapw/OmniRoute/pull/12460)) — thanks @hartmark +- **fix(logging):** raise the SSE payload collector's default cap ([#12461](https://github.com/diegosouzapw/OmniRoute/pull/12461)) — thanks @hartmark +- **fix(resilience):** surface Responses failed.error.message in 502s ([#12472](https://github.com/diegosouzapw/OmniRoute/pull/12472)) — thanks @RaviTharuma +- **fix(security):** unbiased maxai X-Random nonce + stricter URL/regex assertions ([#12502](https://github.com/diegosouzapw/OmniRoute/pull/12502)) +- **fix(video):** re-anchor transcript log-redaction so PII/credential maskers can't reopen the leak (#12150 P1 follow-up) ([#12503](https://github.com/diegosouzapw/OmniRoute/pull/12503)) +- **fix(monitoring):** serve cached credentialHealth off the request path ([#12533](https://github.com/diegosouzapw/OmniRoute/pull/12533)) — thanks @RaviTharuma +- **fix(quota):** keep Antigravity Gemini usable when Claude weekly is empty ([#12566](https://github.com/diegosouzapw/OmniRoute/pull/12566)) — thanks @HouMinXi +- **fix(authz):** hard-gate every credential export and CLI-config write (GHSA-5926-2w35-7h4q) ([#12600](https://github.com/diegosouzapw/OmniRoute/pull/12600)) +- **fix(docs):** restore the Next build — REMOVED_PROVIDERS.md had no frontmatter (base-red #12581) ([#12610](https://github.com/diegosouzapw/OmniRoute/pull/12610)) +- **fix(quality):** re-point the zcodeProtocol public-creds allowlist to line 313 ([#12615](https://github.com/diegosouzapw/OmniRoute/pull/12615)) +- **fix(security):** close 3 advisories — search baseUrl exfil, sk- in the error sanitizer, bifrost relay header leak ([#12620](https://github.com/diegosouzapw/OmniRoute/pull/12620)) +- **fix(providers):** sanitize boolean required and nested bare maps for Gemini (#12269) ([#12624](https://github.com/diegosouzapw/OmniRoute/pull/12624)) — thanks @HouMinXi +- **fix(resilience):** keep Overloaded STREAM_EARLY_EOF off the provider breaker ([#12626](https://github.com/diegosouzapw/OmniRoute/pull/12626)) — thanks @HouMinXi +- **fix(catalog):** re-audit free-tier quotas against official pages ([#12649](https://github.com/diegosouzapw/OmniRoute/pull/12649)) +- **fix(dashboard):** keep a request's pending-tracking id stable across combo target retries ([#12650](https://github.com/diegosouzapw/OmniRoute/pull/12650)) — thanks @hartmark +- **fix(tests):** retire dead model ids from the chat-pipeline integration suite (base-red #12581) ([#12670](https://github.com/diegosouzapw/OmniRoute/pull/12670)) +- **fix:** resolve SqliteError no such table compression_run_telemetry during cleanup ([#12682](https://github.com/diegosouzapw/OmniRoute/pull/12682)) — thanks @groovecityJO +- **fix(combo):** treat a pin-only step as implicit connection allowlist ([#12697](https://github.com/diegosouzapw/OmniRoute/pull/12697)) — thanks @HouMinXi +- **fix(ci):** ship .npmrc in published package so legacy-peer-deps applies to consumers (#11544) ([#12699](https://github.com/diegosouzapw/OmniRoute/pull/12699)) — thanks @KooshaPari +- **fix(docker):** pin 4 CLI tools to exact versions (#12576) ([#12703](https://github.com/diegosouzapw/OmniRoute/pull/12703)) — thanks @KooshaPari +- **fix(docker):** document and harden cli profile trust boundary (#12570) ([#12706](https://github.com/diegosouzapw/OmniRoute/pull/12706)) — thanks @KooshaPari +- **fix(sse):** redact video transcript in pre-guardrail rejected-request logs (#12150 P2 item 7) ([#12710](https://github.com/diegosouzapw/OmniRoute/pull/12710)) +- **fix(i18n):** quote placeholder in OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES description (#12505) ([#12769](https://github.com/diegosouzapw/OmniRoute/pull/12769)) — thanks @KooshaPari +- **fix(api/pricing):** surface validation error message as string, not raw object (#12494) ([#12771](https://github.com/diegosouzapw/OmniRoute/pull/12771)) — thanks @KooshaPari +- **fix(sse):** re-enable prompt compression for native Codex passthrough ([#12834](https://github.com/diegosouzapw/OmniRoute/pull/12834)) — thanks @thomasmaerz +- **fix(dashboard):** model health tests for a provider node set to the Responses API now call `/v1/responses` with a Responses-shaped body instead of `/v1/chat/completions` — those models were reported as `Provider returned HTTP 200 but no text content` even though the same model answered normally through `/v1/responses` ([#13070](https://github.com/diegosouzapw/OmniRoute/issues/13070)) ([#13087](https://github.com/diegosouzapw/OmniRoute/pull/13087)) — thanks @ntdatt812 +- fix(routing): stop a round-robin combo's "opencode" targets from collapsing onto the opencode-zen connection (#11912) ([#13283](https://github.com/diegosouzapw/OmniRoute/pull/13283)) +- fix(routing): stop the reactive-compaction debug log from lying when compression is globally disabled (#11977) ([#13276](https://github.com/diegosouzapw/OmniRoute/pull/13276)) +- fix(electron): relativize standalone-bundle symlink targets so Stage 8 manifest verification stops failing on Windows (#11979) ([#13251](https://github.com/diegosouzapw/OmniRoute/pull/13251)) +- fix(dashboard): surface a visible error when Compression Studio's combined preview run fails (#12061) ([#13288](https://github.com/diegosouzapw/OmniRoute/pull/13288)) +- fix(dashboard): make the compression "Effective pipeline" preview honor the active profile and warn when the master switch is off (#12063) ([#13287](https://github.com/diegosouzapw/OmniRoute/pull/13287)) +- fix(providers): scope TinyCMS Web signer's DOM shims to each call instead of leaking them for the process lifetime, and surface a clean HTTP status on a non-JSON interception-toggles error (#12072) ([#13254](https://github.com/diegosouzapw/OmniRoute/pull/13254)) +- fix(guardrails): stop Vision Bridge from re-selecting a model locked after a 404 (#12111) ([#13259](https://github.com/diegosouzapw/OmniRoute/pull/13259)) +- fix(sse): require Responses-shaped body before native OpenAI-compatible passthrough (#12129) ([#13278](https://github.com/diegosouzapw/OmniRoute/pull/13278)) +- fix(providers): minimax-m3 now collapses manual thinking.type:"enabled" to adaptive, preventing upstream 400 (2013) (#12132) ([#13249](https://github.com/diegosouzapw/OmniRoute/pull/13249)) +- fix(db): scope model visibility overrides by modality so hiding a Chat model no longer hides an identically-ID'd Image/Embeddings/etc. model (#12172) ([#13258](https://github.com/diegosouzapw/OmniRoute/pull/13258)) +- fix(db): scope local-provider apiKey dedup to matching base URL so LM Studio/Ollama-style connections support multiple accounts (#12173) ([#13250](https://github.com/diegosouzapw/OmniRoute/pull/13250)) +- fix(providers): refresh Trae's stale Referer/Origin and forward user timezone so imported connections stop failing with 401 (#12190) ([#13255](https://github.com/diegosouzapw/OmniRoute/pull/13255)) +- fix(providers): route opencode-go/gpt-5.6-luna to /responses instead of /chat/completions (#12196) ([#13275](https://github.com/diegosouzapw/OmniRoute/pull/13275)) +- fix(dashboard): allow deleting the last extra-upstream-header row even when invalid (#12251) ([#13252](https://github.com/diegosouzapw/OmniRoute/pull/13252)) +- **fix(i18n):** translate pre-existing `__MISSING__:` keys for `combo.sort`, `requestLogger.detail` expand/collapse, `common.profile`, and `settings.resilienceCredentialHealth*` across 39 locales ([#12272](https://github.com/diegosouzapw/OmniRoute/issues/12272)) ([#13034](https://github.com/diegosouzapw/OmniRoute/pull/13034)) — thanks @HouMinXi +- fix(cli): run the Node.js runtime compatibility guard before the heavy `tsx/esm` + Commander import chain so an unsupported runtime gets a clear message instead of a raw `Invalid regular expression flags` crash (#12296) ([#13253](https://github.com/diegosouzapw/OmniRoute/pull/13253)) +- fix(dashboard): refresh the providers list after deleting a compatible provider node (#12298) ([#13256](https://github.com/diegosouzapw/OmniRoute/pull/13256)) +- fix(usage): fail closed on API-key budget enforcement when a provider's `auto` routing alias has no pricing row, instead of silently counting it as $0 (#12341) ([#13257](https://github.com/diegosouzapw/OmniRoute/pull/13257)) +- **fix(providers):** include the submitted Agnes video model when polling by `video_id` ([#12356](https://github.com/diegosouzapw/OmniRoute/pull/12356)) — thanks @pacocartones +- **fix(routing):** custom OpenAI-compatible nodes now honor the saved Chat/Responses API type after edits instead of letting the node's original ID prefix override the live connection setting ([#11884](https://github.com/diegosouzapw/OmniRoute/issues/11884)). ([#12358](https://github.com/diegosouzapw/OmniRoute/pull/12358)) — thanks @pacocartones +- fix(sse): surface an error instead of a silent empty 200 when a Claude stream closes with zero bytes (#12398) ([#13285](https://github.com/diegosouzapw/OmniRoute/pull/13285)) +- fix(oauth): warn before the dead localhost:8080 redirect in antigravity/gemini `oauth start` (#12413) ([#13265](https://github.com/diegosouzapw/OmniRoute/pull/13265)) +- fix(providers): stop devin-cli spawn error from double-closing the SSE controller (#12517) ([#13261](https://github.com/diegosouzapw/OmniRoute/pull/13261)) +- **fix(rerank):** clamp Voyage `top_k` to the documents actually sent after empty-string filtering, and honor `return_documents: false` in the NVIDIA response adapter (#12523 — thanks @pacocartones) +- **fix(audio):** `/v1/audio/translations` now resolves combo names the way `/v1/audio/transcriptions` already does, so a combo that `GET /v1/models` advertises is fanned out to its targets instead of being rejected with `400 Invalid translation model: . Use format: provider/model`; literal `provider/model` ids and unknown bare names behave as before (#12536 — thanks @pacocartones) +- **fix(gemini):** strip the JSON-Schema-2020-12 `prefixItems` keyword from Gemini tool schemas at every nesting level, so Claude Code tool definitions no longer fail with `400 Unknown name "prefixItems"` on Gemini models (#12540 — thanks @pacocartones) +- **fix(api-manager):** Expose an accessible loading status while API keys are fetched instead of an empty accessibility tree (#12541 — thanks @pacocartones) +- **fix(video):** Clamp `estimateJpegFrameBytes` at zero for padding-only payloads and build the three encode-side frame data URIs from `JPEG_FRAME_DATA_URI_PREFIX` instead of a repeated literal (#12543 — thanks @pacocartones) +- **fix(devin):** accept Windows `DEVIN_AGENTIC_HOME` sandbox paths (`C:\...\.sandbox\...`) in the isolated-home check so the Devin Claude Bridge no longer fails closed on Windows ([#12405](https://github.com/diegosouzapw/OmniRoute/issues/12405)) (#12545 — thanks @pacocartones) +- **fix(db):** Add `conversation_turn_nodes` and orphaned `agentic_conversations` to the auto-cleanup cycle under the existing `retention.callLogs` window, so identity nodes whose call-log content has already been purged no longer accumulate without bound in `storage.sqlite` (#12548 — thanks @pacocartones) +- **fix(i18n):** Wrap the `~/.claude/profiles//settings.json` placeholder in ICU single quotes in the `OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES` feature-flag description across all 42 locales and the TypeScript default, so next-intl no longer fails with `INVALID_MESSAGE: UNCLOSED_TAG` and the Feature Flags card shows the description instead of the raw key (#12549 — thanks @pacocartones) +- **fix(orchestration):** `updateCloudAgentTask` now publishes the task's real `status` on `agent.task.updated` when an update only touches `result`, `activities` or `error`, instead of the fabricated `"updated"` state, and stays silent when no row matched the id (#12550 — thanks @pacocartones) +- **fix(i18n):** the home "Recent Requests" panel and the Provider Topology legend are now translated instead of rendering English copies on non-English dashboards; the legend reads its own `home.topologyLegend*` labels with consistent casing rather than borrowing the memory-settings "Recent" and analytics "Error" strings (#12551 — thanks @pacocartones). +- **docs(reference):** bring the `FEATURE_FLAGS.md` catalog back to 1:1 with `featureFlagDefinitions.ts` — 20 missing flags added, the two `*_BLOCK_THRESHOLD` env-only knobs moved out of the flag tables, category/total counts and the Live WS port corrected, guarded by a static test (#12552 — thanks @pacocartones) +- **fix(i18n):** backfill missing `usage.kiloPass*` strings in 39 locales and restore `featureFlagOmnirouteDisableThinkingLevelVariantsDescription` in `pt.json` ([#12561](https://github.com/diegosouzapw/OmniRoute/issues/12561)) ([#13027](https://github.com/diegosouzapw/OmniRoute/pull/13027)) — thanks @HouMinXi +- fix(docker): default docker-compose app ports (dashboard/API/live-WS) to loopback instead of `0.0.0.0`, closing the anonymous `/v1` LAN/WAN exposure gap left open by `REQUIRE_API_KEY=false` (#12568) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): close DNS-rebinding SSRF gap in webhook outbound-URL guard (#12569) ([#13243](https://github.com/diegosouzapw/OmniRoute/pull/13243)) +- fix(docker): require a per-session token on the VNC browser CDP bridge and isolate it on a dedicated Docker network (#12571) ([#13241](https://github.com/diegosouzapw/OmniRoute/pull/13241)) +- fix(open-sse): write Adobe Firefly session tokens and cookie jars with 0700/0600 permissions instead of the process umask (#12572) ([#13242](https://github.com/diegosouzapw/OmniRoute/pull/13242)) +- fix(api): remove hardcoded wildcard CORS in openai-to-gemini-sse.ts so the centralized fail-closed CORS gate is the sole source of `Access-Control-Allow-Origin` (#12573) ([#13239](https://github.com/diegosouzapw/OmniRoute/pull/13239)) +- fix(api): enforce API key policy (budget/rate-limit/schedule/endpoint scoping) on the ElevenLabs speech-to-text, text-to-speech and voices proxy routes (#12574) ([#13244](https://github.com/diegosouzapw/OmniRoute/pull/13244)) +- fix(sse): cap HuggingChat NDJSON body size and bound the read loop with the fetch timeout so a stalled or hostile upstream cannot buffer unbounded memory (#12577) ([#13282](https://github.com/diegosouzapw/OmniRoute/pull/13282)) +- fix(docker): scope the cliproxyapi/qdrant/bifrost sidecars to loopback by default and forward `CLIPROXYAPI_MANAGEMENT_KEY` into the cliproxyapi container so its management API is not left both unauthenticated and LAN-published (#12578) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): create DB export temp paths with `fs.mkdtempSync` instead of predictable timestamps (#12579) ([#13237](https://github.com/diegosouzapw/OmniRoute/pull/13237)) +- Cline 401 bodies that say "re-authenticate your Cline account" classify as a refreshable OAuth token, not a terminal expired key. The cooling panel no longer labels every cooldown as a 429; it shows the recorded last error instead. (#12594) ([#13060](https://github.com/diegosouzapw/OmniRoute/pull/13060)) — thanks @HouMinXi +- **fix(catalog):** degrade unknown combo targets instead of dropping LCD modalities, and persist OpenRouter `architecture.input_modalities` into the capability snapshot ([#12613](https://github.com/diegosouzapw/OmniRoute/issues/12613)) ([#13042](https://github.com/diegosouzapw/OmniRoute/pull/13042)) — thanks @HouMinXi +- fix(providers): send `x-api-key` instead of `Authorization: Bearer` for OpenCode Zen's `/v1/responses` endpoint (Muse Spark Contributor models), fixing a 401 on OmniRoute's auth header (#12633) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(sse): surface the actionable "Auggie CLI not found" message when the shell reports a missing `auggie` binary via exit code instead of a spawn error (#12645) ([#13263](https://github.com/diegosouzapw/OmniRoute/pull/13263)) +- **fix(config):** Persist deletions of built-in background-degradation entries — when a stored settings record exists its `degradationMap` is now authoritative instead of being merged under the defaults, so an entry the user removed in the dashboard no longer reappears on the next apply or restart ([#12424](https://github.com/diegosouzapw/OmniRoute/issues/12424)) ([#12647](https://github.com/diegosouzapw/OmniRoute/pull/12647)) — thanks @pacocartones +- **fix(gamification):** action-count badge milestones (First Token, Token Consumer, Token Machine, Token Whale, and the token-sharing tier) are now backed by a durable `xp_action_counts` counter incremented in `addXp()`, instead of a live `COUNT(*)` over `xp_audit_log`. The audit log is pruned by `retention.xpAuditLog` (default 30 days), so on a default install those "lifetime" milestones were really "actions in the last 30 days" and unlocked badges could stop unlocking once old rows aged out. `getActionCount()` and `checkActionCountBadges()` now read the same durable source, and a migration backfills existing totals from the surviving audit rows ([#12546](https://github.com/diegosouzapw/OmniRoute/issues/12546)) ([#12651](https://github.com/diegosouzapw/OmniRoute/pull/12651)) — thanks @pacocartones +- **fix(images):** `/v1/images/edits` now iterates a combo's targets the same way `/v1/images/generations` does (#9239) instead of flattening a bare combo to its first target. A combo whose first target is not edit-capable — or lacks credentials — now falls through to a later edit-capable target rather than hard-erroring, and missing credentials are skipped (not a hard `401`) to match the generations path. The per-target skip/terminal classification is extracted into a shared `runImageComboTargets` loop, so generations behavior is unchanged ([#12547](https://github.com/diegosouzapw/OmniRoute/issues/12547)). ([#12653](https://github.com/diegosouzapw/OmniRoute/pull/12653)) — thanks @pacocartones +- fix(sse): add first-byte watchdog to the TLS-fingerprint transport so a stalled wreq body falls back instead of hanging for minutes (#12656) ([#13272](https://github.com/diegosouzapw/OmniRoute/pull/13272)) +- fix(sse): exempt tiny-budget reasoning probes from combo quality failure and surface persisted-cooldown skips in ALL_TARGETS_SKIPPED diagnostics (#12659) ([#13269](https://github.com/diegosouzapw/OmniRoute/pull/13269)) +- fix(models): declare the real ~1M contextLength for OpenCode Zen's Muse Spark 1.2 models instead of falling back to the 200000 provider default (#12681) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(oauth): align codebuddy-cn OAuth User-Agent with the chat/usage CLI version to avoid WAF false positives (#12702) ([#13264](https://github.com/diegosouzapw/OmniRoute/pull/13264)) +- fix(dashboard): surface an authentication-required banner instead of silently blanking database settings for a guest session (#12709) ([#13270](https://github.com/diegosouzapw/OmniRoute/pull/13270)) +- **fix(resilience):** stop unbounded queue that hangs 6min until Aborted — gate, provider slot, and Bottleneck queue now share a per-connection `maxWaitMs` budget; `fail-closed` on exhaust (503); execution backstop `executionMaxWaitMs` overridable per connection with upstream clamp ([#12715](https://github.com/diegosouzapw/OmniRoute/pull/12715)) — thanks @maxmad64bis (with thanks to @Tushar49 for surfacing the slow-provider need in #12635) +- **fix(auto-combo):** every mode pack now carries `quality` and `reliability` weights (`quality` 0.02, 0.03 in `quality-first`; `reliability` 0.03, 0.04 in `reliability-first`), so selecting a pack no longer silences either signal; `DEFAULT_WEIGHTS` is unchanged ([#12731](https://github.com/diegosouzapw/OmniRoute/pull/12731)) — thanks @maxmad64bis +- **fix(sse):** the provider execution pipeline keeps the upstream error `code`/`type` again — both were lost when the non-streaming leg was extracted, so a config-class `422` (Antigravity missing project) degraded into a generic account cooldown instead of reaching the gate that recognises it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(db):** a legacy `call_logs` table no longer aborts startup — the provider-stats index is created after the column healing runs, not before it, so an install predating `request_type` opens instead of failing with `no such column` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(catalog):** a malformed row in the operator-writable custom-models blob no longer kills every `auto/*` pool ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(tests):** realign the guards that the same day's merges left asserting the old behavior — the provider-count total after GreenPT and EURouter, the GLM stream buffer slot that `#12925` turned into a declared parameter, and the injection scan bound that `#13104` reshaped from a head-only window into head-plus-tail ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- fix(cache): fold tool_choice/tools/response_format into the semantic cache signature so a cached tool_calls response can no longer be replayed for a request whose tool policy forbids it (#12734) ([#13267](https://github.com/diegosouzapw/OmniRoute/pull/13267)) +- **fix(models):** stop treating provider-supplied `isFree:true`/`:free`/`0/0` flags as trusted unless the provider has a documented free tier — only the shipped free-tier catalog decides otherwise; fetched `isFree:true` trusted only for a free-tier provider, custom `isFree:true` via a trusted path ([#12744](https://github.com/diegosouzapw/OmniRoute/pull/12744)) — thanks @maxmad64bis +- fix(memory): authenticate the internal /v1/rerank loopback call so memory reranking no longer silently degrades to unranked order when REQUIRE_API_KEY=true (#12745) ([#13268](https://github.com/diegosouzapw/OmniRoute/pull/13268)) +- fix(sse): parse Ollama Cloud's current usage markup (`$X of $Y used` aria-label, nested width style) (#12749) ([#13260](https://github.com/diegosouzapw/OmniRoute/pull/13260)) +- fix(cli): setup-opencode no longer sends an active context's management token to `/v1/models` when `--api-key`/`OMNIROUTE_API_KEY` is supplied — an explicit flag or the env var now always outranks the context's token, and the flag itself is no longer swallowed by the parent program's global `--api-key` option (#12783) ([#13246](https://github.com/diegosouzapw/OmniRoute/pull/13246)) +- fix(sse): register Arcee AI in the executor provider registry so requests reach api.arcee.ai instead of silently falling back to OpenAI (#12784) ([#13277](https://github.com/diegosouzapw/OmniRoute/pull/13277)) +- **fix(quota):** share one upstream quota read when identical saturation checks arrive at the same time, so traffic bursts don't multiply provider API calls ([#12787](https://github.com/diegosouzapw/OmniRoute/pull/12787)) — thanks @maxmad64bis +- **fix(db):** ignore expired or invalid rate-limit cooldown writes so a stale timestamp can't lock a connection that should be usable — clearing still works as before ([#12788](https://github.com/diegosouzapw/OmniRoute/pull/12788)) — thanks @maxmad64bis +- **fix(providers):** single source for provider order with `xao` ranking alongside `xai-oauth` ([#12790](https://github.com/diegosouzapw/OmniRoute/pull/12790)) — thanks @maxmad64bis +- **fix(routing):** off-table models check the free-model catalog before inheriting premium prices, latency bootstraps from the observed pool median, async tiers read live database pricing with a 90-day freshness gate, and the tier cache invalidates on every pricing write ([#12792](https://github.com/diegosouzapw/OmniRoute/pull/12792)) — thanks @maxmad64bis +- **fix(auto-combo):** snapshot scoring uses the observed per-connection account tier and per-model quality instead of neutral constants, and circuit-open providers rank lower at build time ([#12794](https://github.com/diegosouzapw/OmniRoute/pull/12794)) — thanks @maxmad64bis +- **fix(pool):** empty `auto/*` pools now say why they are empty, and the models listing uses the same paid check as routing ([#12795](https://github.com/diegosouzapw/OmniRoute/pull/12795)) — thanks @maxmad64bis +- fix(routing): recognize CLIProxyAPI's 'unknown provider for model' 400 as fallback-worthy (#12800) ([#13284](https://github.com/diegosouzapw/OmniRoute/pull/13284)) +- **fix(sse):** translate-mode streams now emit the estimated token counts as a trailing usage-only chunk before `[DONE]` when the upstream stays silent, so metered chat clients see totals instead of nothing ([#12828](https://github.com/diegosouzapw/OmniRoute/pull/12828)) — thanks @maxmad64bis +- **fix(db):** provider stats stay truthful on empty and legacy databases: fallback counts default to `0` instead of `null`, latency averages read `null` (not `0`) when no durations were recorded, failures logged before the error-type column existed group under `pre_migration` instead of `unclassified`, and per-provider queries use two new composite indexes ([#12832](https://github.com/diegosouzapw/OmniRoute/pull/12832)) — thanks @maxmad64bis +- fix(nvidia): fail open when a synced model catalog goes stale instead of gating forever (#12849) ([#13248](https://github.com/diegosouzapw/OmniRoute/pull/13248)) +- **fix(db):** WAL maintenance lives in its own module: a `TRUNCATE` checkpoint that hits a busy database now warns with its streak and retries once via `PASSIVE` instead of logging success, and closing-time checkpoints no longer report success on builds without a database file, with the busy totals visible in the authenticated monitoring health payload ([#12853](https://github.com/diegosouzapw/OmniRoute/pull/12853)) — thanks @maxmad64bis +- **fix(health):** quota with no snapshots reads empty instead of a contradicting 0% ([#12857](https://github.com/diegosouzapw/OmniRoute/pull/12857)) — thanks @maxmad64bis +- fix(a2a): accept the dashboard's own session cookie on /a2a so "Run message/send" no longer fails with "Unauthorized: missing or invalid API key" (#12888) ([#13271](https://github.com/diegosouzapw/OmniRoute/pull/13271)) +- **fix(logs):** the Logs grid's in-memory filter pass no longer discards rows the SQL query already matched — selecting an API key from the dropdown (which sends the key's id) returns its calls again, the Combo tab shows every combo instead of only those whose name contains a "1", and the model filter and search cover the same columns as the query ([#12896](https://github.com/diegosouzapw/OmniRoute/pull/12896)) — fixes [#12873](https://github.com/diegosouzapw/OmniRoute/issues/12873) — thanks @ntdatt812 +- **fix(a2a):** `/api/a2a/status` now builds the agent card from the request that asked for it, so a gateway reached at a non-localhost host no longer advertises `http://localhost:20128` as its A2A URL ([#12918](https://github.com/diegosouzapw/OmniRoute/pull/12918)). — thanks @ntdatt812 +- **fix(compression):** progressive aging now appends its `[COMPRESSED:aging:…]` annotation after a turn's `tool_result` blocks instead of in front of them, so Anthropic no longer rejects aged conversations with "`tool_use` ids were found without `tool_result` blocks immediately after" ([#12920](https://github.com/diegosouzapw/OmniRoute/pull/12920)). — thanks @ntdatt812 +- **fix(bedrock):** model import now resolves context limits for every vendor prefix instead of only `anthropic.*`, so `global.openai.gpt-5.6-*` no longer imports with a null `inputTokenLimit` and gets rejected pre-flight at the 200k default ([#12921](https://github.com/diegosouzapw/OmniRoute/pull/12921)). — thanks @ntdatt812 +- **fix(stream):** the 64 KB stream buffer GLM asks for is honoured instead of dropped, and the type error it caused no longer fails the API Route Typecheck gate on every open PR ([#12925](https://github.com/diegosouzapw/OmniRoute/pull/12925)) — thanks @ntdatt812 +- **fix(guardrails):** mask PII inside a `tool_result`'s nested content array, which the masker walked past while redacting its sibling block ([#12930](https://github.com/diegosouzapw/OmniRoute/pull/12930)) — thanks @ntdatt812 +- **fix(dashboard):** the Radar catalog table no longer leaves absent data unexplained — empty limits, unknown context windows, and unreported capabilities each explain themselves on hover, and a new check keeps it that way ([#12937](https://github.com/diegosouzapw/OmniRoute/pull/12937)) — thanks @maxmad64bis +- **fix(sse):** geo-blocked opencode requests rotate to the next account proxy instead of failing, so one refused egress no longer aborts the whole chain ([#12941](https://github.com/diegosouzapw/OmniRoute/pull/12941)) — thanks @maxmad64bis +- **fix(codex):** dashboard "clear cooldown" and the CAS recovery path now drop nested `codexScopeRateLimitedUntil` maps in the same write that nulls `rate_limited_until`, and fresh quota snapshots with headroom lift fallback-sourced scope cooldowns parked by quota preflight ([#12817](https://github.com/diegosouzapw/OmniRoute/issues/12817), [#12860](https://github.com/diegosouzapw/OmniRoute/issues/12860), [#12951](https://github.com/diegosouzapw/OmniRoute/pull/12951)) The dashboard reset-credit button (`consumeCodexResetCredit`) now exercises that same snapshot path after a successful redeem, so a filled quota bar is enough to unpark a leftover fallback Codex child without another manual clear. — thanks @HouMinXi +- **fix(db):** resolve `sql-wasm.wasm` across global npm install and hoisted layouts, ensuring OmniRoute can boot cleanly on Node 24 when native `better-sqlite3` is uncompiled. ([#13035](https://github.com/diegosouzapw/OmniRoute/pull/13035)) — thanks @HouMinXi +- **fix(api):** `DELETE /v1/batches/delete-completed` now sweeps only the calling API key's own completed batches (batches with no owner stay out of a key-scoped sweep on purpose), with an explicit instance-wide mode reserved for authenticated dashboard sessions, a 401 for a presented key that is unknown, revoked, deactivated, banned or expired (never falling through to the session branch), audit logging of every sweep, a sanitized 500 on failure, an owner-scoped file half (a key-scoped sweep never nulls a file another tenant owns) and an atomic sweep — chunked in 200-batch transactions in instance mode — so a mid-way error never leaves a batch pointing at a nulled file (GHSA-wvxc-jp3v-5mg5) ([#12969](https://github.com/diegosouzapw/OmniRoute/pull/12969)) +- **fix(combo):** quota-weighted routing stops drawing on an out-of-credit connection — a 402 now invalidates the stored quota snapshot instead of leaving its stale remaining percentage in place, and a snapshot older than 10 minutes no longer counts as confident headroom for the primary pool ([#12972](https://github.com/diegosouzapw/OmniRoute/pull/12972)) ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- **fix(vision):** Custom Models with "Vision capable" checked no longer have image requests swapped to `glm/glm-4.6v` when the client sends the advertised alias (`vllm/path/...`) or the bare path-shaped id — Vision Bridge now matches the stored override for all three id forms ([#12758](https://github.com/diegosouzapw/OmniRoute/issues/12758)) ([#12974](https://github.com/diegosouzapw/OmniRoute/pull/12974)) — thanks @HouMinXi +- **fix(sse):** transient opencode upstream failures rotate to the next account proxy instead of failing, so one flapping egress no longer aborts the whole chain ([#12975](https://github.com/diegosouzapw/OmniRoute/pull/12975)) — thanks @maxmad64bis +- **fix(azure):** Deployments from GPT-6 onward now send `max_completion_tokens` instead of `max_tokens`, which Azure rejects with HTTP 400. The rule matched a literal `gpt-5`, so each new generation arrived broken; it now matches the generation range, while `gpt-35-turbo` still keeps `max_tokens`. ([#13007](https://github.com/diegosouzapw/OmniRoute/pull/13007)) — thanks @ntdatt812 +- **fix(db):** add an opt-in self-restart circuit for sustained critical memory pressure, gate post-cleanup VACUUM behind a minimum freed-rows threshold, and checkpoint the SQLite WAL every 5 minutes with a size guard that escalates to TRUNCATE, so a growing WAL can no longer stall the event loop into a full outage. ([#13011](https://github.com/diegosouzapw/OmniRoute/pull/13011)) — thanks @HouMinXi +- **fix(auth):** an explicit connection pin may probe a quota-disabled row once and re-enable it on success ([#12874](https://github.com/diegosouzapw/OmniRoute/issues/12874)) ([#13017](https://github.com/diegosouzapw/OmniRoute/pull/13017)) — thanks @HouMinXi +- **fix(api):** thread `X-OmniRoute-Fallback-Attempts` through combo chat completions so streaming and non-streaming responses report how many prior legs were attempted ([#13038](https://github.com/diegosouzapw/OmniRoute/pull/13038)) — thanks @HouMinXi +- Restore provider failure classification and credential refresh on non-streaming requests: classify non-2xx failures to lock models on per-model quota exhaustion, update connection rate limits from headers and body, and pass credential refresh handlers to pipeline execution so 401 tokens can be refreshed and retried (#13043). ([#13069](https://github.com/diegosouzapw/OmniRoute/pull/13069)) — thanks @HouMinXi +- **fix(responses):** wrap forced-non-streaming web_search fallback JSON as Responses SSE so Codex still sees `response.completed` ([#13050](https://github.com/diegosouzapw/OmniRoute/pull/13050)) — thanks @HouMinXi +- **fix(antigravity):** Preserve upstream thought-token usage in normalized completion and reasoning token counts ([#13055](https://github.com/diegosouzapw/OmniRoute/pull/13055)) — thanks @pacocartones +- Fix a concurrency-slot leak in the DeepSeek PoW solver: a worker that failed to spawn (for example a missing worker script) never released its slot, so `MAX_CONCURRENT_WORKERS` failures disabled the solver until restart. ([#13097](https://github.com/diegosouzapw/OmniRoute/pull/13097)) — thanks @anhtahaylove +- **fix(acp):** bound the ACP session output buffers — `stdoutBuffer` and `stderrBuffer` now cap at 1 MiB keeping the most recent output behind a visible `[...output truncated...]` marker, and `stderrBuffer` is reset per prompt instead of accumulating for the lifetime of the session. ([#13100](https://github.com/diegosouzapw/OmniRoute/pull/13100)) — thanks @anhtahaylove +- **fix(acp):** release the `stdout`/`exit` listeners and the idle timer that a `sendPrompt` timeout used to leave attached to the `acpManager` singleton, and drop sessions that exited on their own from the session map instead of keeping them forever. ([#13096](https://github.com/diegosouzapw/OmniRoute/pull/13096)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection and PII scanners now read the text a `tool_result` block carries on `content` (string or nested block list), in messages and in system blocks, so tool output is judged by the same rules as user text ([#13101](https://github.com/diegosouzapw/OmniRoute/pull/13101)) — thanks @ntdatt812 +- **fix(gamification):** close the badge notification SSE stream when the request signal is already aborted before the stream starts — a client that disconnects while the route is still awaiting auth used to leave both the 2s unlock poll and the 15s heartbeat running for the lifetime of the process. ([#13106](https://github.com/diegosouzapw/OmniRoute/pull/13106)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection scan now spends its 16 KB budget on both ends of the request instead of the first 16 KB only, so `system`, `instructions`, `query`, `documents` and the newest turns are no longer hidden behind one long message ([#13104](https://github.com/diegosouzapw/OmniRoute/pull/13104)) — thanks @ntdatt812 +- **fix(dashboard):** expose the Volcano Ark console cookie on quota scraping and unwrap connect-error objects so the dashboard shows the upstream message ([#13107](https://github.com/diegosouzapw/OmniRoute/pull/13107)) — thanks @HouMinXi +- **fix(db):** release the `beforeExit`/`SIGINT`/`SIGTERM` handlers when a `node:sqlite` adapter closes, so a closed adapter and its database handle are no longer pinned to `process` for the lifetime of the run — the same treatment #7494 gave the sql.js adapter. ([#13109](https://github.com/diegosouzapw/OmniRoute/pull/13109)) — thanks @anhtahaylove +- **fix(translator):** `contentSchema` and `unevaluatedItems` are now treated as subschema positions by the tool-schema sanitizer, so a truncation placeholder in either is replaced with a permissive schema instead of being forwarded as a string ([#13110](https://github.com/diegosouzapw/OmniRoute/pull/13110)) — thanks @ntdatt812 +- **fix(cli-helper):** clear the `createLogStream` timeout on the abort path — `stop()` aborts the in-flight fetch and returned through the `signal.aborted` branch, which skipped `clearTimeout` and left an armed timer per stopped stream. The stream reader is now also cancelled when the read loop exits early. ([#13114](https://github.com/diegosouzapw/OmniRoute/pull/13114)) — thanks @anhtahaylove +- **fix(docs):** drop TLS-impersonation, MITM-decrypt, supply-chain attestation, and XOR-mask writeups from the public `/docs` catalog and Docker image. Files stay in git for engineers; operators who need them open the repo, not the website. ([#13136](https://github.com/diegosouzapw/OmniRoute/pull/13136)) — thanks @HouMinXi +- **fix(combo):** parse numeric-epoch `rate_limited_until` in the combo cooldown read path ([#13141](https://github.com/diegosouzapw/OmniRoute/pull/13141)) — thanks @maxmad64bis +- **fix(opencode):** both OpenCode plugins now reject a gateway address typed without `http://` at configuration time, instead of publishing every model with an api url no client can call, and the v2 plugin no longer publishes a model card whose api url is blank or relative ([#13142](https://github.com/diegosouzapw/OmniRoute/pull/13142)) — thanks @maxmad64bis +- **fix(providers):** lock opencode model on upstream 400 model-unavailable ([#13146](https://github.com/diegosouzapw/OmniRoute/pull/13146)) — thanks @maxmad64bis +- **fix(logging):** keep the provider exchange rather than the raw client bodies when a call log exceeds its size budget, and show that recovered payload in the request-detail panel instead of replacing it with the stored response body ([#13147](https://github.com/diegosouzapw/OmniRoute/pull/13147)) — thanks @maxmad64bis +- **fix(traffic-inspector):** the WebSocket route no longer leaks a traffic-buffer subscriber and a 30s ping timer when the client socket is already closed at handler time — listeners are attached before any resource is acquired, a destroyed socket bails out early, and the ping interval stops on a dead socket where `write()` never throws ([#13155](https://github.com/diegosouzapw/OmniRoute/pull/13155)) — thanks @anhtahaylove +- **fix(telegram):** bound the per-user API key cache in the Telegram chat proxy so a burst of distinct chat ids can no longer grow the process heap without limit ([#13165](https://github.com/diegosouzapw/OmniRoute/issues/13165)) ([#13166](https://github.com/diegosouzapw/OmniRoute/pull/13166)) — thanks @anhtahaylove +- **fix(stream):** cancel the upstream response body when the JSON-to-SSE sniff unwinds on a body timeout, so a stalled upstream no longer pins the connection ([#13169](https://github.com/diegosouzapw/OmniRoute/issues/13169)) ([#13171](https://github.com/diegosouzapw/OmniRoute/pull/13171)) — thanks @anhtahaylove +- **fix(telegram):** authenticate webhook deliveries with Telegram's `secret_token` so an unauthenticated caller can no longer mint API keys or spend upstream quota ([#13172](https://github.com/diegosouzapw/OmniRoute/issues/13172)) ([#13175](https://github.com/diegosouzapw/OmniRoute/pull/13175)) — thanks @anhtahaylove +- **fix(models):** give discoverable Gemini 3.8 Flash ids their own 65536 output spec so Antigravity no longer clamps them to 16384 ([#13195](https://github.com/diegosouzapw/OmniRoute/pull/13195)) — thanks @HouMinXi +- **fix(dashboard):** leftover catalog-removed provider rows (gemini-cli) can be listed and purged from the providers page ([#13067](https://github.com/diegosouzapw/OmniRoute/issues/13067)) ([#13197](https://github.com/diegosouzapw/OmniRoute/pull/13197)) — thanks @HouMinXi +- **fix(auth):** a dashboard session now requires the `authenticated: true` claim that login, OIDC and the session refresh already emit — a JWT merely signed with `JWT_SECRET` (for example the Cursor CLI passthrough token, which any API-key holder can obtain) no longer verifies as the `auth_token` cookie on any route, the WebSocket handshake or the live server; existing sessions keep working ([#13298](https://github.com/diegosouzapw/OmniRoute/issues/13298)) ([#13375](https://github.com/diegosouzapw/OmniRoute/pull/13375)) +- **fix(api):** `DELETE /v1/batches/delete-completed` now applies the caller's API-key policy (endpoint allowlist, schedule, usage cap, rate limit) like every other `/v1` route, commits the key-scoped sweep per 200-batch chunk instead of holding one write lock for the whole sweep, refuses to loop without progress, and rejects a scope that names both a key and `allTenants` ([#13684](https://github.com/diegosouzapw/OmniRoute/pull/13684)) +- Restore the API-route typecheck gate: the non-streaming leg result lost its discriminated-union narrowing after the server-owned tool loop reassignment, producing 13 new TS2339 diagnostics in `chatCore.ts`. ([#12963](https://github.com/diegosouzapw/OmniRoute/pull/12963)) +- Restore four non-streaming behaviours the server-owned tool loop refactor dropped: client aborts map to 499 with the fixed `Request aborted` message, an aborted request no longer logs a synthetic `clientResponse`, Claude prompt-cache telemetry is recorded again, and a body that cannot be canonicalized no longer throws when the tool loop is off. ([#12990](https://github.com/diegosouzapw/OmniRoute/pull/12990)) +- **fix(skills):** The CLI registry parser now reads positionals declared with `.addArgument()`, not only those written inline in `.command()`. `tunnel create [type]` was being published as `tunnel create`, so the agent-skills sync gate reported drift on every branch and regenerating would have deleted the argument. ([#13009](https://github.com/diegosouzapw/OmniRoute/pull/13009)) — thanks @ntdatt812 +- **fix(combos):** dashboard combo test uses a short prompt, serial probes, and a 60s timeout so reasoning models and rate-limited free pools do not fail the health check ([#13001](https://github.com/diegosouzapw/OmniRoute/pull/13001)) — thanks @HouMinXi +- fix(compression): terminate idle worker threads on eviction so long-running instances stop leaking OS threads and MessagePorts ([#13091](https://github.com/diegosouzapw/OmniRoute/pull/13091)) — thanks @anhtahaylove +- Redact Google API keys of any length in error bodies: the pattern required exactly 39 characters, so shorter or longer `AIza…` credentials passed through unredacted. ([#12964](https://github.com/diegosouzapw/OmniRoute/pull/12964)) +- **fix(grok-cli):** a 402 "Grok Build usage balance exhausted" parks that Grok login as out of credit (Grok Build CLI, grok.com cookie, and xAI OAuth share the weekly pool). Combo routing then tries the next login instead of locking the model for every account in the pool ([#13061](https://github.com/diegosouzapw/OmniRoute/pull/13061)) — thanks @HouMinXi +- fix(compression): spawn the LLMLingua worker with a file URL object so compression actually runs instead of silently failing open on Node ([#13093](https://github.com/diegosouzapw/OmniRoute/pull/13093)) — thanks @anhtahaylove +- **fix(test):** run the local `test` and `test:unit` scripts at concurrency 4 so a full-suite run no longer exhausts the machine's commit charge and kills unrelated processes ([#13187](https://github.com/diegosouzapw/OmniRoute/pull/13187)) — thanks @anhtahaylove +- **fix(monitoring):** `GET /api/monitoring/health` `credentialHealth` now includes a bounded `failedConnections` list (`connectionId`, `status`, sanitized `lastError`) when the probe-cache gauge `failed>0`, plus `source: probe-cache` and a cheap `staleDbNonOkCount` for sticky SQLite `test_status` on active rows. Documents that the live gauge is not `provider_connections.test_status`. ([#12876](https://github.com/diegosouzapw/OmniRoute/pull/12876)) — thanks @RaviTharuma +- **fix(resilience):** an openai-compatible multi-upstream gateway no longer marks the whole connection `credits_exhausted` when a single upstream (or the `/models` / representative-model health probe) returns 402. Quota failures stay model-scoped; true connection-wide auth failures are unchanged. Single-credential 402 key disable ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239)) is preserved. ([#12875](https://github.com/diegosouzapw/OmniRoute/pull/12875)) — thanks @RaviTharuma +- **fix(dashboard):** nine fixes on the Orchestration Canvas, all diagnosed in the Phase 2 reviews. A source that fails now keeps the timestamp of its FIRST failure instead of being re-stamped every poll — the stale line said "since the last poll" no matter how long the source had been down, and the churn also defeated the snapshot's stable identity (it serializes the source list), so the canvas re-rendered on every tick while anything was broken. A source that HAD data and only then started failing is flagged too: previously only a source with no node at all got the warning, so a source that went down mid-session kept a healthy-looking node forever. The rest are pointwise: clicking a filter chip cancels the pending search debounce (left armed, it fired ~300ms later and silently reverted the chip); the search input carries an accessible name; `?state=running, failed` parses like the unpadded form instead of dropping the padded value; the CSV toggle helper is defined once in `model/urlParams.ts` rather than twice; the agents tab tells "nothing running" apart from "the filter matched nothing", offering a clear-filters button instead of setup links that would be wrong advice there; edges stop emitting SMIL particles above 40 simultaneously active edges, keeping the colored stroke; and the drawer's error banner clears when a retried action succeeds. ([#12983](https://github.com/diegosouzapw/OmniRoute/pull/12983)) +- **fix(i18n):** the nine locales added with the EU-language batch (Greek, Estonian, Irish, Croatian, Lithuanian, Latvian, Maltese, Slovenian, Serbian) were missing the eleven Orchestration Canvas keys that Phase 3 introduced, so the compare-runs panel and the "no runs match these filters" empty state fell back to English in those languages (`deepMergeFallback` substitutes English for an absent key, so nothing rendered blank — it rendered untranslated). The coverage gate does not catch this: it enforces an 80% floor per locale, and eleven missing keys out of ~13,000 leaves coverage at 99.9%. Translated for real in each language, calibrated against the wording each file already uses for "run", "filter" and "skill". ([#13294](https://github.com/diegosouzapw/OmniRoute/pull/13294)) +- **fix(dashboard):** five follow-ups from the Phase 2 Orchestration Canvas review. The History drawer now shows the memory section for runs that already left the live TTL window: the persisted `memory_hits` event is parsed into `metadata.memoryHits` with the same defensive validation the drawer applies, and — because that event is observability rather than a state transition — it no longer leaks into the timeline, where it had been inheriting the task's state and rendering as a duplicate transition. The A2A memory recall runs against its own 1.5s deadline instead of inheriting the memory backend's 30s one; overshooting degrades exactly like any other recall failure (no hits, task proceeds), and the timer is cleared on both paths. Repeating a Conductor run carries its `requirements.cli`/`requirements.model` forward, so the new run is pinned to the same runner profile and model rather than drifting to whatever the fleet picks. A successful repeat from the Agents tab now focuses the run it created, instead of leaving the operator on the finished one. And the auth test for `POST /api/conductor/tasks` moved into the shared `ROUTES` array rather than restating the pattern. ([#12988](https://github.com/diegosouzapw/OmniRoute/pull/12988)) +- fix(plugins): stop leaking an exit listener per plugin hook timeout, which triggered MaxListenersExceededWarning on plugins that ignore SIGTERM ([#13092](https://github.com/diegosouzapw/OmniRoute/pull/13092)) — thanks @anhtahaylove +- **fix(validation):** Provider node edits no longer fail with a generic "Invalid request" when the optional daily-quota reset fields are left blank. The dashboard sends `dailyQuotaResetTimezone` and `dailyQuotaResetHour` as `null`, and only the hour accepted it. ([#13066](https://github.com/diegosouzapw/OmniRoute/issues/13066)) ([#13083](https://github.com/diegosouzapw/OmniRoute/pull/13083)) — thanks @ntdatt812 +- **fix(combo):** quota-aware expansion drops banned, inactive, missing, and wrong-provider connections before quota fetch or model dispatch; pins and allowlists stay selectors, not a bypass. Antigravity automatic exhaustion now requires a reported zero remaining, so a positive balance below 1% stays eligible. ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- Fix an intermittent failure in the quota-weighted routing test suite: scores are a function of `Date.now()`, so peers with identical quota scored microseconds apart never tied and swapped order. ([#12946](https://github.com/diegosouzapw/OmniRoute/pull/12946)) +- fix(volcengine): resolve quota window mapping gaps, safe multi-connection console binding, and response cookie sanitization ([#12950](https://github.com/diegosouzapw/OmniRoute/pull/12950)) — thanks @HouMinXi +- **fix(test):** resolve the WebDAV handler path with `fileURLToPath` so the suite's 37 WebDAV tests run on Windows instead of failing with a doubled `C:\C:\` drive prefix ([#13196](https://github.com/diegosouzapw/OmniRoute/pull/13196)) — thanks @anhtahaylove +- **fix(ui):** fall back from invalid AnySearch icon alias (direct commit `9587e07b69`) +- **fix(sse):** stop rebuilding a truncated summary from the collector's cap-dropped event array ([#12718](https://github.com/diegosouzapw/OmniRoute/pull/12718)) — thanks @hartmark +- **fix(conversations):** show a pending spinner for unresolved tool nodes instead of "(empty)" ([#12727](https://github.com/diegosouzapw/OmniRoute/pull/12727)) — thanks @hartmark +- **fix(streaming):** fail fast when an upstream stream produces only lifecycle/heartbeat events, never real content ([#12741](https://github.com/diegosouzapw/OmniRoute/pull/12741)) — thanks @hartmark +- **fix(responses-continuation):** bridge the write-in-flight window with an in-memory pending store ([#12854](https://github.com/diegosouzapw/OmniRoute/pull/12854)) — thanks @hartmark +- **fix(translator):** drop or map agent_message on Chat Completions fallback ([#12880](https://github.com/diegosouzapw/OmniRoute/pull/12880)) — thanks @RaviTharuma +- **fix(api):** page and stream-complete GET /v1/models for large catalogs ([#12882](https://github.com/diegosouzapw/OmniRoute/pull/12882)) — thanks @RaviTharuma +- **fix(quota):** align AUTH window labels with usage API and clarify 503 ([#12884](https://github.com/diegosouzapw/OmniRoute/pull/12884)) — thanks @RaviTharuma +- **fix(ci):** clear two base-reds on release/v3.8.51 — mutation-coverage gate + image-only-model guard ([#12945](https://github.com/diegosouzapw/OmniRoute/pull/12945)) +- **fix(api):** scope batch bulk-delete to the calling API key ([#13211](https://github.com/diegosouzapw/OmniRoute/pull/13211)) +- **fix(tests):** align model-sync log assertion to shipped path redaction (base-red) ([#13227](https://github.com/diegosouzapw/OmniRoute/pull/13227)) +- **fix(test):** make the ToS heading guard actually require the parentheses ([#13228](https://github.com/diegosouzapw/OmniRoute/pull/13228)) +- **fix(quality):** register 4 drifted covering tests in stryker tap.testFiles ([#13229](https://github.com/diegosouzapw/OmniRoute/pull/13229)) +- **fix(logging):** mask *-api-key headers in request-log pipeline (#13273) ([#13401](https://github.com/diegosouzapw/OmniRoute/pull/13401)) — thanks @KooshaPari +- **fix(tests):** correct stale Jina catalog prefix in custom-models test ([#13403](https://github.com/diegosouzapw/OmniRoute/pull/13403)) — thanks @KooshaPari +- **fix(backend):** prune old backups after health-check-repair snapshots (#13308) ([#13404](https://github.com/diegosouzapw/OmniRoute/pull/13404)) — thanks @KooshaPari +- **fix(backend):** guard memory_fts_au trigger with WHEN clause to stop FTS bloat (#13326) ([#13405](https://github.com/diegosouzapw/OmniRoute/pull/13405)) — thanks @KooshaPari +- **fix(free-tiers):** remove obsolete Together $25 signup credit (#12526) ([#13407](https://github.com/diegosouzapw/OmniRoute/pull/13407)) — thanks @KooshaPari +- **fix:** skip non-chat generation models in Test all models ([#13409](https://github.com/diegosouzapw/OmniRoute/pull/13409)) — thanks @KooshaPari +- **fix(providers):** de-duplicate agy and antigravity model catalogs ([#13410](https://github.com/diegosouzapw/OmniRoute/pull/13410)) — thanks @KooshaPari +- **fix(dashboard):** send provider in body for mini-playground web search ([#13411](https://github.com/diegosouzapw/OmniRoute/pull/13411)) — thanks @KooshaPari +- **fix(resilience):** add CJK quota-exhaustion patterns to 429 classifier ([#13412](https://github.com/diegosouzapw/OmniRoute/pull/13412)) — thanks @KooshaPari +- **fix(backend):** eval runner: failed calls no longer score as passed + dotAll regex ([#13413](https://github.com/diegosouzapw/OmniRoute/pull/13413)) — thanks @KooshaPari +- **fix(backend):** eval runner opts out of output-style and memory injection ([#13414](https://github.com/diegosouzapw/OmniRoute/pull/13414)) — thanks @KooshaPari +- **fix:** chain serial quarantine step in npm test script (fixes #13286) ([#13418](https://github.com/diegosouzapw/OmniRoute/pull/13418)) — thanks @KooshaPari +- **fix(antigravity):** derive stable session ID from credentials for prompt-cache reuse (#13305) ([#13424](https://github.com/diegosouzapw/OmniRoute/pull/13424)) — thanks @KooshaPari +- **fix(memory):** merge memory into top-level system field instead of unshifting at messages[0] (#13425) ([#13427](https://github.com/diegosouzapw/OmniRoute/pull/13427)) — thanks @KooshaPari +- **fix(compression):** preserve instruction blocks from lossy engines ([#13523](https://github.com/diegosouzapw/OmniRoute/pull/13523)) — thanks @KooshaPari +- **fix(compression):** preserve polarity words and newlines in ultra heuristic ([#13524](https://github.com/diegosouzapw/OmniRoute/pull/13524)) — thanks @KooshaPari +- **fix(compression):** run user preservePatterns before built-in patterns (#13457) ([#13525](https://github.com/diegosouzapw/OmniRoute/pull/13525)) — thanks @KooshaPari +- **fix(reasoning):** expand deepseek-v4 regex to match v4.1+ and add command-code provider (#13430) ([#13528](https://github.com/diegosouzapw/OmniRoute/pull/13528)) — thanks @KooshaPari +- **fix(providers):** add alwaysReasons metadata for reka-flash-3 ([#13531](https://github.com/diegosouzapw/OmniRoute/pull/13531)) — thanks @KooshaPari +- **fix(playground):** allow Compare columns to scroll independently ([#13532](https://github.com/diegosouzapw/OmniRoute/pull/13532)) — thanks @KooshaPari +- **fix(security):** empty whitelist allows through to prevent admin lockout ([#13534](https://github.com/diegosouzapw/OmniRoute/pull/13534)) — thanks @KooshaPari +- **fix(tests):** add DISABLE_IOREG_STRATEGY env var for macOS test compatibility (fixes #13467) ([#13539](https://github.com/diegosouzapw/OmniRoute/pull/13539)) — thanks @KooshaPari +- **fix(zed-hosted):** map developer role and Gemini enums for Zed proxy ([#13543](https://github.com/diegosouzapw/OmniRoute/pull/13543)) — thanks @KooshaPari +- **fix(analytics):** normalize provider aliases to canonical id in usage_history ([#13545](https://github.com/diegosouzapw/OmniRoute/pull/13545)) — thanks @KooshaPari +- **fix(combo):** use traceId for call log id to prevent UNIQUE constraint failure ([#13546](https://github.com/diegosouzapw/OmniRoute/pull/13546)) — thanks @KooshaPari +- **fix(auth):** check model lockout before returning synthetic noauth connection ([#13547](https://github.com/diegosouzapw/OmniRoute/pull/13547)) — thanks @KooshaPari +- **fix(guardrails):** skip credential redaction for base64 image data URLs ([#13550](https://github.com/diegosouzapw/OmniRoute/pull/13550)) — thanks @KooshaPari +- **fix(resilience):** increase requestQueue.maxWaitMs default from 15s to 30s ([#13553](https://github.com/diegosouzapw/OmniRoute/pull/13553)) — thanks @KooshaPari +- **fix(docs):** document OMNIROUTE_READY_TIMEOUT_MS and allowlist the test-only DISABLE_IOREG_STRATEGY ([#13692](https://github.com/diegosouzapw/OmniRoute/pull/13692)) + ### 📝 Maintenance ---- +- **docs(readme):** reconcile live v3.8.50 provider, free-tier, CLI, routing, test, community, sponsor, acknowledgment, and SVG metrics with their audited source denominators, including a deduplicated OmniRoute-in-Action snapshot and distinct contributor rankings for merged pull requests, GitHub-attributed commits, and Git history ([#11356](https://github.com/diegosouzapw/OmniRoute/pull/11356)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **docs:** sync the canonical provider count 354 → 356 across `README.md`, `AGENTS.md`, `llm.txt` (+ 42 i18n mirrors), the four README SVG diagrams, `docs/reference/PROVIDER_REFERENCE.md` (regenerated) and the `package.json` description after Opper (#11629) and 1min.ai (#11631) boarded the catalog — closes the `check:docs-counts-sync` strict drifts that kept `release/v3.8.51` red ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11673](https://github.com/diegosouzapw/OmniRoute/pull/11673)) — thanks @jonlwheat2-gif +- **test(providers):** update count-derived assertions after the v3.8.51 provider additions — `APIKEY_PROVIDERS` 233 → 235 (Opper #11629 + 1min.ai #11631), reserved-prefix REGISTRY walk 395 → 398, `WEB_FETCH_PROVIDERS` now includes `nimble-search` (#11620), and the provider translate-path golden snapshot regenerated ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11674](https://github.com/diegosouzapw/OmniRoute/pull/11674)) — thanks @jonlwheat2-gif +- **test(sse):** bump the hard-lease connection-query inventory for `src/lib/tokenHealthCheck.ts` to 2 — the verify-only web-cookie sweep added by #11495 split the single `getProviderConnections` call into oauth + cookie variants, which the frozen inventory had not tracked ([#11495](https://github.com/diegosouzapw/OmniRoute/pull/11495)). ([#11677](https://github.com/diegosouzapw/OmniRoute/pull/11677)) — thanks @jonlwheat2-gif / @oyi77 +- **chore(release):** clear the `release/v3.8.51` base-red gates — docs migration count (+38 i18n mirrors), stryker `tap.testFiles`, lockfile registry host, stale ESLint suppressions, the 7 lint regressions they were hiding, the stale `APIKEY_PROVIDERS` partition count (231 → 233), the `glm-5.3-max` tier missing from the GLM test table, a dead `modelType === "chat"` comparison in the models catalog, and the `resolveLiveWsUrl` / `sanitizeLiveWsPort` helpers from #11388 that a later merge dropped from `wsPath.ts` (dashboard live WebSocket URL resolution) ([#11502](https://github.com/diegosouzapw/OmniRoute/pull/11502)) — thanks @MumuTW +- **chore(free-models):** the free-tier regime table now declares which totals figure each regime feeds and whether it may take the no-auth shortcut, so the sets that used to repeat those answers by hand are derived from it — a new `freeType` no longer compiles until it has answered every question, instead of silently contributing to no total ([#11537](https://github.com/diegosouzapw/OmniRoute/pull/11537)) — thanks @maxmad64bis +- **docs(routing):** LKGP strategy copy now states provider-level stickiness and the explicit rules fallback instead of promising per-target pinning ([#11555](https://github.com/diegosouzapw/OmniRoute/pull/11555)) — thanks @maxmad64bis +- **test(chatcore):** pin the response tool-name alias resolution against the ordering hazard that broke every Gemini/Antigravity MCP tool call in v3.8.49. `extractRequestToolIdentityMap` deletes `translatedBody._toolNameMap`, so the later read is always undefined and the ledger survives only through the `requestToolIdentityMap` fallback — removing that fallback previously left the entire tool-name suite green. The resolution moves into `resolveResponseToolNameMap()` next to the map it depends on, with a regression guard that fails without the recovery. No behaviour change. ([#11575](https://github.com/diegosouzapw/OmniRoute/pull/11575)) — thanks @amartinawi +- **docs(env):** document `OMNIROUTE_CORPUS_CACHE_SIZE` in `.env.example` and `ENVIRONMENT.md`, so the env/docs contract check stops failing Docs Gates on every branch ([#11576](https://github.com/diegosouzapw/OmniRoute/pull/11576)) — thanks @ntdatt812 +- **chore(stryker):** register `route-guard-tunnel-processes-local-only` and `universal-quota-aware-routing` in `tap.testFiles`, so their mutant kills count for the three modules they cover ([#11577](https://github.com/diegosouzapw/OmniRoute/pull/11577)) — thanks @ntdatt812 +- **test(authz):** pin `LOCAL_ONLY_API_GET_EXEMPTIONS` by exact membership instead of by entry count, so the guard names the offending path and also catches a substitution ([#11580](https://github.com/diegosouzapw/OmniRoute/pull/11580)) — thanks @ntdatt812 +- **test(build):** pin `resolveNextBuildBundlerFlag` to the `OMNIROUTE_USE_TURBOPACK` contract it implements, across runtimes, instead of asserting an unimplemented Bun override ([#11583](https://github.com/diegosouzapw/OmniRoute/pull/11583)) — thanks @ntdatt812 +- **test(claude-web):** the Claude Web transport test's barrel anchors no longer pin the exact construction spelling. #11421 made the executor map lazy, so `/"claude-web": new ClaudeWebExecutor\(\)/` stopped matching and the test went red on `release/v3.8.51` itself — a base-red gate every open PR inherited. The anchors now match the alias key and the class name and tolerate either spelling in between, and were checked to still fail when an alias is genuinely repointed at another executor ([#11585](https://github.com/diegosouzapw/OmniRoute/pull/11585)) — thanks @ntdat812 +- **test(models):** pin `qwen3.8-max` resolution per provider — two of the four catalogs now serve the bare id and no longer carry `-preview` ([#11590](https://github.com/diegosouzapw/OmniRoute/pull/11590)) — thanks @ntdatt812 +- **test(executors):** drop the stale 128-tool truncation assertion that contradicted the #11444 pass-through contract ([#11591](https://github.com/diegosouzapw/OmniRoute/pull/11591)) — thanks @ntdatt812 +- **test(token-health):** pin the current terminal-skip boundary — expired with retry budget is probed, `account_deactivated` stays skipped ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)) — thanks @ntdatt812 +- **test(models):** follow #11503's retarget of the `gemini-3-pro-high` alias to the hyphenated catalog id ([#11593](https://github.com/diegosouzapw/OmniRoute/pull/11593)) — thanks @ntdatt812 +- **refactor(video bridge):** extract per-video acquisition, whole-result caching, description, and metrics/abort/cleanup out of `VideoBridgeGuardrail.preCall` into a `processVideoPart` seam in a new `videoBridgePipeline.ts`, behind explicit `VideoMediaBrokerPort`, `VideoAudioTranscriptionPort`, and `VideoDrilldownPort` boundaries; `preCall` now only handles request traversal, policy, and response aggregation. The Video tab's FFmpeg/ffprobe runtime status is now an explicit `unknown` / `restricted` / `unavailable` / `available` state instead of a nullable boolean pair, fixing a case where an in-flight or failed probe was mislabeled as "install FFmpeg" ([#11657](https://github.com/diegosouzapw/OmniRoute/issues/11657)). ([#12016](https://github.com/diegosouzapw/OmniRoute/pull/12016)) +- **test(opencode-plugin):** add unit test coverage for `formatFreeBudget()` naming helper ([#11660](https://github.com/diegosouzapw/OmniRoute/pull/11660)) — thanks @f9td56dbgh-hub +- **docs(video):** clarify that the Video Bridge transcript `source` field (`client`, `embedded`, `audio-bridge`) is presently caller-declared and not yet server-verified — OmniRoute enforces the enum shape but does not cryptographically confirm that an `embedded`/`audio-bridge` label came from a server-owned extraction ([#11661](https://github.com/diegosouzapw/OmniRoute/issues/11661)). ([#12001](https://github.com/diegosouzapw/OmniRoute/pull/12001)) +- **chore(providers):** retire the distributed Raycast Relay and Hailuo Web integrations after a provenance audit confirmed substantial derivation from GPL-3.0 sources; the official GitHub and MiniMax API providers remain available. Migration 163 disables legacy connections and invalidates their active leases while preserving connection IDs, audit/history rows, quota snapshots, usage records, and fail-closed API-key connection policies. ([#11691](https://github.com/diegosouzapw/OmniRoute/pull/11691)) +- **chore(providers):** retire the Felo Web provider while its GPL-derived provenance remains on licensing HOLD ([#11698](https://github.com/diegosouzapw/OmniRoute/pull/11698)) — stale runtime and database references now fail closed while existing connection, lease, and usage history remains preserved for auditability. +- **fix(sse):** retire the Gemini Web image-generation bridge while provenance and licensing remain on HOLD, preserving Gemini Web chat/auth/tools and legitimate Gemini/Nano Banana image providers ([#11708](https://github.com/diegosouzapw/OmniRoute/pull/11708)) +- **chore(audio):** retire the built-in EdgeTTS provider and its 11-voice catalog while provenance/licensing review remains on HOLD; the generic `/v1/audio/speech` endpoint and the other speech providers remain available ([#11711](https://github.com/diegosouzapw/OmniRoute/pull/11711)) +- **fix(providers):** retire the unofficial Qwen Web integration and its `qwen-web`/`qw` identities while third-party provenance remains on HOLD ([#11713](https://github.com/diegosouzapw/OmniRoute/pull/11713)) — direct requests now fail closed with a sanitized HTTP 410 and combos skip the retired target, while migration guards preserve connection IDs, allowlists, usage/audit history and invalidate active leases. Supported Qwen API, Qwen Code, cloud, token-plan, audio, local and model-name surfaces remain available; restoring Qwen Web requires a qualified provenance/license decision. +- **fix(providers):** retire `microsoft-designer-web` and its `msdesigner` alias from runtime and catalogs while preserving stored credentials, provider history and allowlists as inactive records — source dependency on gpt4free's [`MicrosoftDesigner.py`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/g4f/Provider/needs_auth/MicrosoftDesigner.py) is proven at snapshot `0943d16eef757a44deb60651c07bbe5fa958cafe`, whose [`LICENSE`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/LICENSE) is GPL-3.0; expressive adaptation is probable, which records provenance/licensing risk without asserting a legal conclusion ([#11720](https://github.com/diegosouzapw/OmniRoute/pull/11720)). +- **docs(legal):** centralize pinned MIT notices for the vendored GCF codec and local flag assets ([#11726](https://github.com/diegosouzapw/OmniRoute/pull/11726)) +- Split the 2,952-line `adobeFireflyClient.ts` into nine focused modules (catalog, credentials, payload, ARP session, upload, responses, IMS, account, poll) behind an unchanged barrel — no consumer edits and no public API change (#11728). — thanks @MumuTW +- **fix(dashboard):** replaced the Model Lockout card's unprovenanced notification MP3 with a short, locally synthesized Web Audio chime ([#11731](https://github.com/diegosouzapw/OmniRoute/pull/11731)) — both toggles retain optional audible feedback without shipping replacement media or third-party code, and unsupported, suspended, or rejected audio contexts fail open without blocking the setting change. +- **chore(deepseek-web):** remove the provenance-unresolved DeepSeek PoW WASM binary and its runtime loader/tracing while retaining the existing JavaScript solver (slower at high difficulty) ([#11732](https://github.com/diegosouzapw/OmniRoute/pull/11732)). +- **chore(assets):** remove four unused provider assets whose redistribution provenance could not be verified while retaining the active Freebuff SVG ([#11735](https://github.com/diegosouzapw/OmniRoute/pull/11735)) +- **docs(assets):** retired two raster images whose redistribution provenance could not be verified ([#11736](https://github.com/diegosouzapw/OmniRoute/pull/11736)) — PicoClaw documentation now reuses the existing SVG, while Zoo Code remains a text link without an image. +- **docs(legal):** add pinned notices for six LobeHub-derived provider SVGs and 65 byte-exact theSVG assets, retaining registry claims as unverified metadata with explicit trademark and HOLD boundaries ([#11737](https://github.com/diegosouzapw/OmniRoute/pull/11737)). +- **chore(quality):** added a versioned 225/225 provider-asset provenance manifest and blocking drift gate ([#11743](https://github.com/diegosouzapw/OmniRoute/pull/11743)) — the inventory pins magic MIME and SHA-256 for every physical file, records immutable source matches for 71 assets, keeps 69 probable and 85 unresolved entries explicit but non-blocking, and treats provenance as source evidence rather than copyright or trademark clearance. +- **refactor(mitm):** replace four previously derived Traffic Inspector utilities — conversation normalization, SSE merging, secret masking, and header sanitization — with independent clean-room implementations based on public provider schemas, WHATWG SSE, and RFC 6750, while keeping the two independently classified passthrough surfaces unchanged ([#11748](https://github.com/diegosouzapw/OmniRoute/pull/11748)). +- Added a unit test that fails seven days before any dated pack under `config/` (`validUntil` and sibling keys) lapses, naming the file and key. The Alibaba free-tier pack expired on 2026-08-27 and turned every PR red the next morning with no commit involved; renewal now happens on someone's terms, not the clock's. ([#11891](https://github.com/diegosouzapw/OmniRoute/pull/11891)) +- `check:workflows` now fails (under `--strict`/`--ratchet`) when any job routed to a self-hosted runner publishes with `--provenance` — npm rejects that with `422` at the registry, which in v3.8.50 only surfaced after the tag and Docker images were public. ([#11895](https://github.com/diegosouzapw/OmniRoute/pull/11895)) +- `scripts/ops/runner-janitor.sh` now proves a path is idle with one `lsof` snapshot and removes stale leftovers itself (tmpfs after 3 h — it is RAM — disk after 24 h), kills orphan `next-build` processes, prunes checkouts of stopped runners, and alerts on memory pressure; `--dry-run` shows exactly what it would do. `docs/ops/RUNNER_BOX.md` reconciled to the measured box (31 GB, 10 listeners). ([#11893](https://github.com/diegosouzapw/OmniRoute/pull/11893)) +- The `next-build` artefact (1.3 GB) is now written and read under `$RUNNER_TEMP` (per-runner, on disk) instead of `/tmp`, which on the self-hosted pool is a 12 GB tmpfs in RAM. Landing it there took 27–32 of the publish job's 76 minutes, and the fixed `/tmp/e2e-build.tar.gz` name let E2E jobs on different runners overwrite each other's download. ([#11896](https://github.com/diegosouzapw/OmniRoute/pull/11896)) +- The CI `build` job now runs in two concurrency lanes — `main` and pull requests — so a release build is never queued behind (or OOM-killed beside) PR builds on the self-hosted pool, which holds one `next-build` comfortably and two at the edge. ([#11901](https://github.com/diegosouzapw/OmniRoute/pull/11901)) +- Re-freeze the ESLint suppressions on `release/v3.8.51` from a clean-room measurement (2 stale file entries pruned, 55 pre-existing `no-explicit-any` in six new files frozen under #11924) and drop the dead `GPT_SIZE_MAP` constant orphaned by the Adobe Firefly client split, so `No new ESLint warnings` stops failing every PR with exit 2 (Refs #11924) ([#11955](https://github.com/diegosouzapw/OmniRoute/pull/11955)) +- Type the 55 `no-explicit-any` sites that had been frozen under #11924 — four redundant casts in `socksConnectorWithFamily.ts` (undici/socks types already accept them) and the mocks/fixtures of the socks-timeout and isFree suites — and drop their suppression entries; the ESLint ratchet shrinks from 5487 to 5432 (Closes #11924) ([#11975](https://github.com/diegosouzapw/OmniRoute/pull/11975)) +- Take the two hosted-runner builds off the PR rail: `Build App` (`build.yml`) is `workflow_dispatch`-only and `DAST smoke (PR)` runs only for PRs into `main` — the 7 GB hosted VM cannot build `release/v3.8.51` in any profile (19/30 red, VM shutdown ~8 min into `next build`) and both checks had turned into permanent noise on every release PR; the bundle stays validated by `ci.yml` on `main` and by `nightly-release-green` on `release/**` (Closes #11946) ([#11962](https://github.com/diegosouzapw/OmniRoute/pull/11962)) +- Move the four nightly jobs that build the backend (`nightly-schemathesis`, `nightly-llm-security` promptfoo + garak, `nightly-resilience` axe-a11y) off the hosted 7 GB runner — where they died on `release/v3.8.51` unseen — onto the box's new `omni-light` pool (two listeners), and document the reshaped fleet (4 active OmniRoute listeners: 2 `omni-build` + 2 `omni-light`, janitor ceiling 4) (Closes #11965) ([#11967](https://github.com/diegosouzapw/OmniRoute/pull/11967)) +- Stop painting every fork PR into `release/**` red: `quality.yml` `Build (advisory)` is skipped (GitHub still reports `continue-on-error` failures as check FAILURE). Hosted `ubuntu-latest` cannot finish `npm run build` on this tree — same class as #11962 taking `build.yml` off the PR rail. `docker-publish.yml` amd64 now runs on the `.113` `omni-build` pool (31 GB / 32 cores, two listeners) with Turbopack, shares the `heavy-build-main` lane with `ci.yml` `Build` so it queues instead of becoming a third heavy, and keeps per-ref concurrency (a merge storm was starting 8 concurrent OOM builds). arm64 stays on `ubuntu-24.04-arm` with webpack — there is no ARM box. Fallback when `USE_VPS_RUNNER` is off: hosted amd64 + webpack (#11976). ([#12021](https://github.com/diegosouzapw/OmniRoute/pull/12021)) +- `docker-publish.yml` builds with webpack (`OMNIROUTE_USE_TURBOPACK=0`) on **both** arches, including amd64 on the `.113` `omni-build` pool. Turbopack had the RAM (31 GB, `omniroute-113-6`) and still panicked (`TurbopackInternalError: there must be a path to a root`, run 33253576569, 26 min in). The same tree's arm64 webpack build on hosted `ubuntu-24.04-arm` succeeded (run 33264823398). Dockerfile already documents webpack as the Docker escape hatch (#11976). ([#12050](https://github.com/diegosouzapw/OmniRoute/pull/12050)) +- **docs(free-tier):** declare the counting vs deciding regimes for "is it free?" and guard the deciding path from DB-backed catalog resolution ([#12226](https://github.com/diegosouzapw/OmniRoute/pull/12226)) — thanks @maxmad64bis +- **docs(auto-combo):** The mode pack table in `docs/routing/AUTO-COMBO.md` now lists all six shipped packs with every weight each one sets, replacing a four-pack table whose numbers had also drifted from the source. It states plainly that no pack sets `quality`, so selecting any pack silences the observed-quality signal. Six more documents that quote the scoring factor count joined the `check:docs-counts` gate, which caught five stale claims — including one naming nine factors that do not exist — and two stale mode pack counts. The dashboard routing panel, which offered four of the six packs and labelled the default strategy "6-Factor Scoring", is now covered by a test; the two packs it was missing are `reliability-first` and `chaos-mode`, the latter labelled as the fault-injection profile it is rather than as one more routing preference ([#12316](https://github.com/diegosouzapw/OmniRoute/pull/12316)) — thanks @maxmad64bis +- **chore(quality):** rebaseline `src/lib/db/apiKeys.ts` for the ACL the key-creation path now preserves ([#12352](https://github.com/diegosouzapw/OmniRoute/pull/12352)) ([#12673](https://github.com/diegosouzapw/OmniRoute/pull/12673)) — thanks @KrzysiekSko +- **chore(providers):** bump the Claude Code wire identity and the Devin bridge image pin from `2.1.220` to `2.1.258` ([#12402](https://github.com/diegosouzapw/OmniRoute/pull/12402)) — thanks @ggiak ([#12604](https://github.com/diegosouzapw/OmniRoute/pull/12604)) +- **docs(env):** align `.env.example`, the README Bun section, and the troubleshooting guide with the code: `OMNIROUTE_USE_TURBOPACK` also governs `npm run build` (not dev-only), `bun run build` follows that flag instead of auto-selecting Webpack, `OMNIROUTE_CHAT_MAX_HEAVY_IN_FLIGHT` is unset by default (no request-count cap), and the structural `503 chat_admission_busy` message matches `chatAdmissionResponses.ts` (#12404 — thanks @pacocartones) +- **chore(electron):** upgrade the desktop app to Electron 44 (Chromium 152, Node 24.18.1) ([#12217](https://github.com/diegosouzapw/OmniRoute/pull/12217)). **Requires macOS 13 (Ventura) or later** — Chromium dropped macOS 12 (Monterey), so Monterey users must stay on an earlier OmniRoute desktop build. Windows and Linux are unaffected; the app already shipped only x64/arm64, so Electron 44 dropping 32-bit builds changes nothing. Removes the `openAsHidden`/`wasOpenedAsHidden` login-item fields deleted in Electron 44 — hidden autostart continues to work through the `--hidden` argument registered with the login item ([#12554](https://github.com/diegosouzapw/OmniRoute/pull/12554)) — thanks @app/dependabot +- **chore(quality):** rebaseline `src/sse/handlers/chat.ts` for the effective-input persistence the continuation fix needs ([#12641](https://github.com/diegosouzapw/OmniRoute/pull/12641)) ([#12680](https://github.com/diegosouzapw/OmniRoute/pull/12680)) — thanks @hartmark +- `Coverage` job on `ci.yml`: the informational Codecov upload gets its own 5-minute ceiling and `continue-on-error`, and the job budget grows from 20 to 30 minutes (the 8-shard c8 merge alone takes ~10) — a stalled upload no longer ends the job `cancelled` and drags a fully green `main` run's conclusion down with it ([#11972](https://github.com/diegosouzapw/OmniRoute/pull/11972)) +- CI hardening on the PR rail: the four `Unit Tests fast-path` shards get `timeout-minutes: 30` (a hung shard held a PR for 64 min instead of GitHub's 6 h default) and both ESLint file caches lose their `restore-keys` fallback, so a cache built under another suppressions file or lint config can no longer report stale verdicts (Refs #11600, #11924) ([#11963](https://github.com/diegosouzapw/OmniRoute/pull/11963)) +- Every CI job that runs a `next build` (`build`, the npm `publish`, both release-green validations) now targets the `omni-build` runner label, which only two of the eight self-hosted runners carry. The box holds one build comfortably and two at the edge; a third now queues on GitHub instead of being OOM-killed by the kernel. ([#11932](https://github.com/diegosouzapw/OmniRoute/pull/11932)) +- **refactor(combo):** move `handleRoundRobinCombo` (and `resolveTargetTokenLimit`) into `open-sse/services/combo/roundRobinCombo.ts`. `combo.ts` drops from 2164 to 1014 lines (`split("\n").length`); the leaf is 1199 (under the 1200 new-file cap). The round-robin call site uses a dynamic `import()` so `releaseStickyPinOnFailure` / `clearStaleLKGP` can stay exported from `combo.ts` without a static cycle. Skip / sticky / semaphore / safety-timer behavior is unchanged. ([#12811](https://github.com/diegosouzapw/OmniRoute/pull/12811)) — thanks @HouMinXi +- **chore(quality):** rebaseline the file-size caps the error-boundary campaign grew past (`open-sse/executors/codex.ts`, `open-sse/vendor/codex-chatgpt-web/bridge.ts`, both via [#12444](https://github.com/diegosouzapw/OmniRoute/pull/12444)) ([#12654](https://github.com/diegosouzapw/OmniRoute/pull/12654)) +- **chore(quality):** rebaseline the file-size caps the hartmark batch grew past (`combos/page.tsx` via [#12355](https://github.com/diegosouzapw/OmniRoute/pull/12355), `open-sse/services/combo.ts` via [#12338](https://github.com/diegosouzapw/OmniRoute/pull/12338)) ([#12623](https://github.com/diegosouzapw/OmniRoute/pull/12623)) — thanks @hartmark +- **chore(quality):** rebaseline the file-size caps the HouMinXi batch grew past when its PRs stacked (`providers/page.tsx`, `chatCore.ts`, `accountFallback.ts`) — each PR measured correctly in isolation, none saw the stacking ([#12619](https://github.com/diegosouzapw/OmniRoute/pull/12619)) +- **chore(quality):** rebaseline `open-sse/services/combo.ts` for the reset-aware scoring the HouMinXi batch stacked ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) ([#12678](https://github.com/diegosouzapw/OmniRoute/pull/12678)) — thanks @HouMinXi +- **docs:** sync provider catalog count (direct commit `4f11b2ae3d`) +- **docs(skills):** sync omni-inference search fallback (direct commit `b4e76a7ed9`) +- **chore(lint):** prune two stale base suppressions (direct commit `1325047d56`) +- **test(auto-combo):** tolerate full-suite rotation load (direct commit `ad3e293f9f`) +- **test:** drain inherited quality snapshot reds (direct commit `21ed68d8ac`) +- **chore(quality):** rebaseline measured CLI bundle growth (direct commit `d35b3f9779`) +- **test(ui):** reproduce invalid AnySearch icon alias (direct commit `12cc6ca834`) +- **test(translator):** fix the relative imports of the relocated deferred-finish test (direct commit `777d9d1629`) +- **docs(counts):** bring the documented provider and migration counts to the measured values (direct commit `e70bea30e9`) +- **test(security):** drop the guard-target set left unused by the sync (direct commit `a32ba0e362`) +- **chore(db):** renumber model_capabilities migration 163 → 169 (cross-branch collision with 163_radar_feed_cache_generated_at) (direct commit `70f5d4cbf9`) +- **docs(cli):** document CLI_PRIME_AGENT_BIN and correct the CLI Agents count (direct commit `9058e39b61`) +- **chore(deps):** refresh runtimes and adopt ESLint 10 ([#11259](https://github.com/diegosouzapw/OmniRoute/pull/11259)) — thanks @backryun +- **refactor(local-corpus):** implement dynamic root resolution and LRU cache ([#11491](https://github.com/diegosouzapw/OmniRoute/pull/11491)) — thanks @raheemuddin786 +- **chore(quality):** prune stale eslint-suppressions entries ([#11596](https://github.com/diegosouzapw/OmniRoute/pull/11596)) +- **chore(ci):** unblock quality gates - env-doc sync + stryker tap.testFiles registration (#9282) ([#11609](https://github.com/diegosouzapw/OmniRoute/pull/11609)) — thanks @Meet6338-X +- **chore(ci):** point the circular-deps gate at dpdm's real JS entrypoint ([#11615](https://github.com/diegosouzapw/OmniRoute/pull/11615)) — thanks @benzntech +- **chore(quality):** rebaseline file-size for #11448/#11495/#11561 growth ([#11630](https://github.com/diegosouzapw/OmniRoute/pull/11630)) +- **deps:** realign onnxruntime-node pin to @huggingface/transformers' 1.24.3 lockstep ([#11633](https://github.com/diegosouzapw/OmniRoute/pull/11633)) — thanks @jonlwheat2-gif +- **test(autoCombo):** port models_dev_tier #11508 guard from node:test to vitest ([#11635](https://github.com/diegosouzapw/OmniRoute/pull/11635)) — thanks @jonlwheat2-gif +- **chore(quality):** rebaseline file-size for #11083/#11631/#11628 growth ([#11636](https://github.com/diegosouzapw/OmniRoute/pull/11636)) +- **chore(lint):** prune stale ESLint suppressions (base-red #11449) ([#11644](https://github.com/diegosouzapw/OmniRoute/pull/11644)) — thanks @jonlwheat2-gif +- **docs(changelog):** aggregate the ten v3.8.50 fragments into CHANGELOG.md ([#11683](https://github.com/diegosouzapw/OmniRoute/pull/11683)) +- **chore(search):** drop unreachable credential sweep after #11642 ([#11687](https://github.com/diegosouzapw/OmniRoute/pull/11687)) — thanks @HouMinXi +- **ci:** add API route TypeScript regression gate ([#11705](https://github.com/diegosouzapw/OmniRoute/pull/11705)) — thanks @Sabeekhann +- **docs(dedup):** correct the CodeQL claim on the tenant-namespace comment ([#11712](https://github.com/diegosouzapw/OmniRoute/pull/11712)) +- **docs(changelog):** add consolidated v3.8.50 stats, top-25 ranking and a .mailmap ([#11715](https://github.com/diegosouzapw/OmniRoute/pull/11715)) +- **docs:** recommend gstack for AI-assisted workflows ([#11770](https://github.com/diegosouzapw/OmniRoute/pull/11770)) — thanks @santosraju99-hub +- **chore(quality):** tighten complexity/cognitive-complexity ratchets to the current tip; land the missed gateways.ts rebaseline ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **docs(i18n):** finish Freepik → Magnific rebrand in locale strings and README ([#11772](https://github.com/diegosouzapw/OmniRoute/pull/11772)) — thanks @turbolego +- **refactor(auto-combo):** fix divergent scoring in combo health reporting ([#11854](https://github.com/diegosouzapw/OmniRoute/pull/11854)) — thanks @jacobsparts +- **chore(changelog):** add missing fragments for #11919/#11918/#11916 ([#11938](https://github.com/diegosouzapw/OmniRoute/pull/11938)) +- **test(check):** escape the runs-on fixture with JSON.stringify, not a quote-only replace ([#11942](https://github.com/diegosouzapw/OmniRoute/pull/11942)) (direct commit `fb7445eaa3`) +- **test(release):** align five suites with the contracts #11933, #11919 and #11876 shipped on release/v3.8.51 ([#11944](https://github.com/diegosouzapw/OmniRoute/pull/11944)) +- **chore(quality):** remove unreachable code and restore test discovery ([#11950](https://github.com/diegosouzapw/OmniRoute/pull/11950)) — thanks @backryun +- **docs(agents):** sync-back landings are fast-forward, never squash ([#11964](https://github.com/diegosouzapw/OmniRoute/pull/11964)) +- **test(infra):** retry recursive temp-dir removal instead of failing a shard on ENOTEMPTY (#11966) ([#11968](https://github.com/diegosouzapw/OmniRoute/pull/11968)) +- **docs(ops):** the .113 heavy-build ceiling is one runner, not two ([#12048](https://github.com/diegosouzapw/OmniRoute/pull/12048)) +- **test(ui):** correct inactive auto-fetch expectation ([#12098](https://github.com/diegosouzapw/OmniRoute/pull/12098)) — thanks @RaviTharuma +- **docs:** document native dependency check escape hatch ([#12101](https://github.com/diegosouzapw/OmniRoute/pull/12101)) — thanks @RaviTharuma +- **test(providers):** regenerate the translate-path golden for OrcaRouter (#11923) ([#12118](https://github.com/diegosouzapw/OmniRoute/pull/12118)) +- **chore(quality):** velocity phase — loosen every numeric baseline by 20% until v4.0, monitor headroom nightly ([#12125](https://github.com/diegosouzapw/OmniRoute/pull/12125)) +- **chore(lint):** batch 0 of #12146 — type the call-log-cap sqlite rows instead of 45 as-any casts ([#12157](https://github.com/diegosouzapw/OmniRoute/pull/12157)) +- **chore(lint):** batch 4 of #12146 — shared/components react-hooks violations resolved ([#12159](https://github.com/diegosouzapw/OmniRoute/pull/12159)) +- **chore(lint):** batch 1 of #12146 — dashboard/cli-code react-hooks violations resolved ([#12160](https://github.com/diegosouzapw/OmniRoute/pull/12160)) +- **chore(lint):** batch 3 of #12146 — dashboard/settings react-hooks violations resolved ([#12162](https://github.com/diegosouzapw/OmniRoute/pull/12162)) +- **chore(lint):** batch 2 of #12146 — resolve the react-hooks compiler violations in dashboard/providers ([#12163](https://github.com/diegosouzapw/OmniRoute/pull/12163)) +- **chore(quality):** register search-432 cooldown test in stryker tap.testFiles ([#12170](https://github.com/diegosouzapw/OmniRoute/pull/12170)) +- **chore(lint):** batch 5 of #12146 — combos, endpoint, provider-stats, api-manager and costs react-hooks violations resolved ([#12174](https://github.com/diegosouzapw/OmniRoute/pull/12174)) +- **build:** add contributor fast profile ([#12192](https://github.com/diegosouzapw/OmniRoute/pull/12192)) — thanks @rafacpti23 +- **docs(readme):** README touch-ups ([#12193](https://github.com/diegosouzapw/OmniRoute/pull/12193)) ([#12194](https://github.com/diegosouzapw/OmniRoute/pull/12194)) ([#12202](https://github.com/diegosouzapw/OmniRoute/pull/12202)) — thanks @rafacpti23 +- **build:** force the webpack bundler in the contributor build profile ([#12197](https://github.com/diegosouzapw/OmniRoute/pull/12197)) — thanks @rafacpti23 +- **build:** skip standalone packaging in the contributor profile ([#12198](https://github.com/diegosouzapw/OmniRoute/pull/12198)) — thanks @rafacpti23 +- **docs(audit):** align every published number with the code and harden check:docs-counts ([#12200](https://github.com/diegosouzapw/OmniRoute/pull/12200)) +- **build:** stub the instrumentation entrypoints in the contributor profile ([#12203](https://github.com/diegosouzapw/OmniRoute/pull/12203)) — thanks @rafacpti23 +- **build:** omit the standalone output target for contributor builds ([#12204](https://github.com/diegosouzapw/OmniRoute/pull/12204)) — thanks @rafacpti23 +- **chore(lint):** batch 6 of #12146 — memory, radar, audit, analytics, cache, usage, activity, home and RequestLoggerV2 react-hooks violations resolved ([#12208](https://github.com/diegosouzapw/OmniRoute/pull/12208)) +- **docs(diagrams):** rename number-carrying diagram files to stable names ([#12210](https://github.com/diegosouzapw/OmniRoute/pull/12210)) +- **docs(dashboard):** redraw onboarding tier-flow SVGs for the real 4-tier model ([#12211](https://github.com/diegosouzapw/OmniRoute/pull/12211)) +- **docs(api):** document every implemented route in openapi.yaml (276 → 692 paths) ([#12212](https://github.com/diegosouzapw/OmniRoute/pull/12212)) +- **chore(lint):** batch 7 of #12146 — final src tail: 37 react-hooks violations across 33 files resolved ([#12244](https://github.com/diegosouzapw/OmniRoute/pull/12244)) +- Revert "docs: recommend gstack for AI-assisted workflows (#11770)" ([#12248](https://github.com/diegosouzapw/OmniRoute/pull/12248)) ([#12249](https://github.com/diegosouzapw/OmniRoute/pull/12249)) +- **docs:** Chaos Mode setup guide + weighted strategy semantics ([#12250](https://github.com/diegosouzapw/OmniRoute/pull/12250)) +- **docs(agents):** protected-surface merge rule — operator approval for agent-instruction files ([#12253](https://github.com/diegosouzapw/OmniRoute/pull/12253)) +- **chore(quality):** register native-codex turn-pin tests in stryker tap.testFiles ([#12263](https://github.com/diegosouzapw/OmniRoute/pull/12263)) +- **chore(quality):** dedupe tap.testFiles entries added by racing base-red fixes ([#12265](https://github.com/diegosouzapw/OmniRoute/pull/12265)) +- **docs(readme):** introduce OmniRouteTray — the macOS menu-bar companion ([#12276](https://github.com/diegosouzapw/OmniRoute/pull/12276)) — thanks @ggiak +- **chore(proxy):** purge the legacy 1proxy residue — sync/rotator modules, dead DB exports, dead settings tab and flag, docs (#12091) ([#12290](https://github.com/diegosouzapw/OmniRoute/pull/12290)) +- **chore(quality):** baseline-headroom skips generated and vendored files in the fileSize worst-file signal ([#12291](https://github.com/diegosouzapw/OmniRoute/pull/12291)) +- **refactor(video):** unify the JPEG frame data-URI contract ([#12322](https://github.com/diegosouzapw/OmniRoute/pull/12322)) +- **chore(deps):** freeze onnxruntime-node and eslint-plugin-react-hooks out of dependabot groups ([#12329](https://github.com/diegosouzapw/OmniRoute/pull/12329)) +- **test(executors):** restore the #10986 reasoning-only fallback guards ([#12364](https://github.com/diegosouzapw/OmniRoute/pull/12364)) — thanks @pacocartones +- **chore(quality):** re-tighten the file-size ratchet to the real LOC (plan 3.8.52 task 0) ([#12411](https://github.com/diegosouzapw/OmniRoute/pull/12411)) +- **test(grok):** format web executor suite ([#12412](https://github.com/diegosouzapw/OmniRoute/pull/12412)) +- **chore(quality):** ratchet open-sse typecheck baseline to zero ([#12418](https://github.com/diegosouzapw/OmniRoute/pull/12418)) +- **test(build):** guard the artifact path policy arrays against duplicates ([#12422](https://github.com/diegosouzapw/OmniRoute/pull/12422)) +- **chore(lint):** adopt eslint-plugin-react-hooks 7.1.1 ([#12428](https://github.com/diegosouzapw/OmniRoute/pull/12428)) +- **chore(providers):** remove a keyless provider integration at its operator's request ([#12440](https://github.com/diegosouzapw/OmniRoute/pull/12440)) +- **docs(providers):** register providers removed at their operator's request and guard against reintroduction ([#12478](https://github.com/diegosouzapw/OmniRoute/pull/12478)) +- **chore(quality):** base fixes — stryker tap.testFiles + node_modules cache key ([#12482](https://github.com/diegosouzapw/OmniRoute/pull/12482)) +- **docs(arch):** one-process recipe for tens of long /v1/responses ([#12493](https://github.com/diegosouzapw/OmniRoute/pull/12493)) — thanks @RaviTharuma +- **chore(quality):** tighten the CodeQL ratchet baseline from 11 to 6 ([#12530](https://github.com/diegosouzapw/OmniRoute/pull/12530)) +- **chore(deps):** pin browserslist override to ^4.28.8 ([#12592](https://github.com/diegosouzapw/OmniRoute/pull/12592)) — thanks @KooshaPari +- **chore(deps):** bump fast-uri to 3.1.7 in the electron lockfile ([#12601](https://github.com/diegosouzapw/OmniRoute/pull/12601)) +- **docs(quality):** document how the CodeQL ratchet refreshes and how to tighten it ([#12611](https://github.com/diegosouzapw/OmniRoute/pull/12611)) +- **test(ci):** pin the openapi-security-tiers two-arm contract with an executing gate test (#12581) ([#12652](https://github.com/diegosouzapw/OmniRoute/pull/12652)) +- **docs(gamification):** align XP Rewards table with code (#12501) ([#12667](https://github.com/diegosouzapw/OmniRoute/pull/12667)) — thanks @KooshaPari +- **refactor(combo):** split executeTarget into gates, attempt, and loop ([#12746](https://github.com/diegosouzapw/OmniRoute/pull/12746)) — thanks @HouMinXi +- **docs(security):** document socket.yml scanner config + CI workflow link (#12575) ([#12764](https://github.com/diegosouzapw/OmniRoute/pull/12764)) — thanks @KooshaPari +- **chore(ci):** guard commit identity in pre-commit to stop author misattribution ([#12772](https://github.com/diegosouzapw/OmniRoute/pull/12772)) +- **deps:** 17 Dependabot bumps — bump electron from 43.4.0 to 43.4.1 in /electron ([#11426](https://github.com/diegosouzapw/OmniRoute/pull/11426)); bump the development group across 1 directory with 8 updates ([#11428](https://github.com/diegosouzapw/OmniRoute/pull/11428)); bump the production group across 1 directory with 11 updates ([#11440](https://github.com/diegosouzapw/OmniRoute/pull/11440)); chore(deps): bump github/codeql-action/init from 4.37.7 to 4.37.8 ([#11925](https://github.com/diegosouzapw/OmniRoute/pull/11925)); chore(deps): bump github/codeql-action from 4.37.7 to 4.37.8 ([#11926](https://github.com/diegosouzapw/OmniRoute/pull/11926)); chore(deps): bump github/codeql-action/analyze from 4.37.7 to 4.37.8 ([#11927](https://github.com/diegosouzapw/OmniRoute/pull/11927)); build(deps): bump github/codeql-action/init from 4.37.8 to 4.37.9 ([#12345](https://github.com/diegosouzapw/OmniRoute/pull/12345)); build(deps): bump github/codeql-action/analyze from 4.37.8 to 4.37.9 ([#12346](https://github.com/diegosouzapw/OmniRoute/pull/12346)); bump the development group across 1 directory with 2 updates ([#12347](https://github.com/diegosouzapw/OmniRoute/pull/12347)); build(deps): bump github/codeql-action from 4.37.8 to 4.37.9 ([#12349](https://github.com/diegosouzapw/OmniRoute/pull/12349)); bump browserslist from 4.28.2 to 4.28.8 ([#12396](https://github.com/diegosouzapw/OmniRoute/pull/12396)); bump the production group across 1 directory with 4 updates ([#12399](https://github.com/diegosouzapw/OmniRoute/pull/12399)); bump @xmldom/xmldom ([#12500](https://github.com/diegosouzapw/OmniRoute/pull/12500)); bump qs from 6.15.2 to 6.16.0 ([#12512](https://github.com/diegosouzapw/OmniRoute/pull/12512)); bump @xmldom/xmldom from 0.9.10 to 0.9.12 ([#12513](https://github.com/diegosouzapw/OmniRoute/pull/12513)); bump fast-uri from 3.1.5 to 3.1.7 ([#12514](https://github.com/diegosouzapw/OmniRoute/pull/12514)); bump @humanfs/node from 0.16.7 to 0.16.8 ([#12515](https://github.com/diegosouzapw/OmniRoute/pull/12515)) +- Clear the `release/v3.8.51` `check:agent-skills-sync` base-red: regenerate `skills/cli-tunnel/SKILL.md` so the `tunnel create [type]` positional that #13009 taught the generator to read is reflected in the committed skill. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` typecheck base-red from #12731: the new `Mode pack ranking gates` candidates in `open-sse/services/autoCombo/__tests__/autoCombo.test.ts` omitted the required `provider`, `model` and `errorRate` fields of `ProviderCandidate`, failing `check:open-sse-typecheck` (and with it `Fast Quality Gates`) on every open PR. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` docs-sync base-red: EURouter (#13025) and GreenPT (#13024) took the live provider count to 358, leaving 7 STRICT drifts (`PROVIDER_REFERENCE.md`, 4 diagrams, 2 tier-flow images) plus the `AGENTS.md` / `llm.txt` / `package.json` count claims stale. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- **test(models):** Pin `CATALOG_BUILD_TIMEOUT_MS` in the #12058 canonical-catalog regression so a cold catalog build racing the 8s default bound no longer flakes the suite with a 500 ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Allow `@omniroute/opencode-plugin-v2/` in the npm pack policy — [#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870) shipped the v2 plugin beside its v1 sibling without widening the allowlist, so every packed file under it read as an unexpected artifact and failed `check:pack-policy` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Point the release-green validator's pack gate at the tree it is validating — the build-provenance guard defaulted to `origin/main`, which a release branch tip can never be an ancestor of mid-cycle, so `Package artifact` reported a false red on every run and the tarball boot-smoke cascaded off it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **docs(checks):** keep doc counts honest — headings, rankings, catalog, weights, quality gate and scoring diagram now covered ([#12507](https://github.com/diegosouzapw/OmniRoute/pull/12507)) — thanks @maxmad64bis +- **chore(lifecycle):** `check:model-lifecycle` now also diffs `DEFAULT_DEGRADATION_MAP` (the background-task redirect table) against the vendor lifecycle snapshot, refusing a retired id as source or target, with a table-driven unit test beside it. Three rows whose source the vendor had retired — `claude-sonnet-4-20250514`, `gemini-3-pro-preview` and `gpt-5.1-codex` (whose target `gpt-5.1-codex-mini` is retired too) — were dead code, since `checkLifecycle` answers 410 before the redirect runs; they are dropped (#12535 — thanks @pacocartones) +- **test(compression):** cover idle worker eviction at the resource level — the pool must call `terminate()` and must not retain the worker's `MessagePort`, complementing the `exit`-event assertion added with the fix ([#13371](https://github.com/diegosouzapw/OmniRoute/pull/13371)) +- **chore(quality):** raise the `imageGeneration.ts` file-size ceiling for the image-only-model guard that clears the #12945 base-red ([#13392](https://github.com/diegosouzapw/OmniRoute/pull/13392)) +- **chore(quality):** type the combo-test route's JSON response bodies instead of casting them to `any`, drop the now-empty suppression entry, and remove the `hasPerModelQuota` import `chatCore.ts` stopped using when the failure-classification helper was extracted ([#13379](https://github.com/diegosouzapw/OmniRoute/pull/13379)) +- **chore(electron):** the desktop bundle no longer ships the root-level files of every translated docs mirror (`README.md`, `llm.txt`, `CLAUDE.md`, `GEMINI.md`, `CONTRIBUTING.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`) — the packaged app only reads `docs/i18n//docs/**`, which stays. Saves ~11 MB on top of the translated CHANGELOGs already pruned. (#0000) ([#13235](https://github.com/diegosouzapw/OmniRoute/pull/13235)) +- **chore(quality):** retighten the `src/sse/handlers/chat.ts` unused-vars suppression count to the 8 violations that actually remain ([#13385](https://github.com/diegosouzapw/OmniRoute/pull/13385)) +- Bring the documented counts back in line with the code: 171 migrations (was 169) and 20 routing strategies (was 19), plus the regenerated `cli-tunnel` skill reference. ([#12970](https://github.com/diegosouzapw/OmniRoute/pull/12970)) +- Update the documented migration count to 172 after the call-logs provider-stats indexes landed. ([#13209](https://github.com/diegosouzapw/OmniRoute/pull/13209)) +- **chore(ci):** two gates that close the blind spots behind the exclusions above. `check:vitest-exclusions` requires every Vitest exclusion to name a tracking issue and to appear in `config/quality/vitest-exclusions.json` — the previous list grew to 62 files behind a comment pointing at an issue that had been closed for a month. `check-new-key-coverage` requires a key newly added to `en.json` to reach every locale; the existing coverage gate is a percentage floor per locale, so eleven absent keys out of ~13,000 left it at 99.9% while a whole feature shipped untranslated in nine languages. Both are diff-aware, so pre-existing debt stays frozen and neither needed a migration to turn on. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **build:** root layout no longer loads Inter from `next/font/google`, so a production image build does not need fonts.googleapis.com ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **docs(i18n):** bring the eight new `llm.txt` mirrors (km, kn, ml, my, ne, or, pa, si) up to the current provider and migration counts, so `check-docs-sync` stops rejecting every commit ([#13674](https://github.com/diegosouzapw/OmniRoute/pull/13674)) +- **refactor(ui):** flow surfaces (home topology, combo live studio, compression cockpit/waterfall/nodes, token-health badge) express state through the theme-aware `--orch-status-*` tokens instead of fixed dark-mode hex, so green/red/amber/grey stay legible in the light theme; dark mode is byte-identical. Categorical palettes (routing-strategy hues, compression-layer pills, provider brand colors) deliberately stay hex ([#12378](https://github.com/diegosouzapw/OmniRoute/issues/12378)) ([#13203](https://github.com/diegosouzapw/OmniRoute/pull/13203)) +- **chore(tests):** 51 test files that had been excluded from Vitest are running again, restoring roughly 350 assertions to the blocking suite. Each was measured individually first: of the 62 files parked behind the `// #8618 — pre-existing failure` comment, 51 pass against the current tree with no source change, so the exclusions had outlived the failures they were added for. The 11 that genuinely still fail stay excluded, but now point at a live tracker (#13204) rather than at #8618, which was closed in August while the list it tracked kept growing. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **chore(quality):** drop two ESLint suppression entries whose violations no longer exist, so `eslint --suppressions-location` stops rejecting every commit that touches the surrounding files ([#13384](https://github.com/diegosouzapw/OmniRoute/pull/13384)) +- **chore(release):** bring main's post-tag fixes into release/v3.8.51 — sync-back step 2/2 (direct commit `21c488f210`) +- **chore(deps):** patch toml and esbuild in the opencode plugin lockfiles ([#12965](https://github.com/diegosouzapw/OmniRoute/pull/12965)) +- **docs(changelog):** reconcile the v3.8.51 living section — fold 366 fragments, cover every cycle commit, credit every contributor ([#12971](https://github.com/diegosouzapw/OmniRoute/pull/12971)) +- **test(plugin):** wait for the v2 lazy-refresh tier instead of sleeping past it ([#12984](https://github.com/diegosouzapw/OmniRoute/pull/12984)) +- **chore(quality):** rebaseline chatCore.ts after the non-streaming regression fixes ([#13045](https://github.com/diegosouzapw/OmniRoute/pull/13045)) +- **docs(changelog):** add the missing bullet marker to the DeepSeek PoW fragment ([#13200](https://github.com/diegosouzapw/OmniRoute/pull/13200)) +- **chore(deps):** drain the Dependabot queue — 10 of 13 alerts ([#13213](https://github.com/diegosouzapw/OmniRoute/pull/13213)) +- **chore(ci):** make worktrees carry the hooks that enforce the identity gate ([#13236](https://github.com/diegosouzapw/OmniRoute/pull/13236)) +- **chore(deps):** pin csv-parse to ^7.0.2 via overrides (#13117) ([#13300](https://github.com/diegosouzapw/OmniRoute/pull/13300)) — thanks @KooshaPari +- **chore(deps):** bump hono from ^4.12.34 to ^4.13.7 (#13148) ([#13301](https://github.com/diegosouzapw/OmniRoute/pull/13301)) — thanks @KooshaPari +- **chore(deps):** pin joi to ^18.2.8 via overrides (#13085) ([#13302](https://github.com/diegosouzapw/OmniRoute/pull/13302)) — thanks @KooshaPari +- **docs(providers):** add Microsoft 365 Copilot (BizChat) provider guide (#12779) ([#13340](https://github.com/diegosouzapw/OmniRoute/pull/13340)) — thanks @KooshaPari +### 🙌 Contributors + +Thanks to everyone whose work landed in v3.8.51: + +| Contributor | PRs / Issues | +| --- | --- | +| [@5dive-bot](https://github.com/5dive-bot) | #11852 | +| [@abhisheksharma2411](https://github.com/abhisheksharma2411) | #11547, #11684, #11959, #12209 | +| [@adevwithpurpose](https://github.com/adevwithpurpose) | #11464 | +| [@adivekar-utexas](https://github.com/adivekar-utexas) | #12015, #12043, #12090, #12138 | +| [@AIB1TAL0S](https://github.com/AIB1TAL0S) | #12103 | +| [@alltomatos](https://github.com/alltomatos) | #11448 | +| [@alvinveroy](https://github.com/alvinveroy) | #11857, #12027, #12165 | +| [@amaleta](https://github.com/amaleta) | #11906, #11934 | +| [@amartinawi](https://github.com/amartinawi) | #11575 | +| [@AndrianBalanescu](https://github.com/AndrianBalanescu) | #11083, #11830, #11834, #11844 | +| [@anhtahaylove](https://github.com/anhtahaylove) | #13091, #13092, #13093, #13096, #13097, #13100, #13106, #13109, #13114, #13155, #13166, #13171, #13175, #13187, #13196 | +| [@aniruddhaadak80](https://github.com/aniruddhaadak80) | #11760 | +| [@app](https://github.com/app) | #12554 | +| [@arminanton](https://github.com/arminanton) | #11461, #11513 | +| [@AStupidBear](https://github.com/AStupidBear) | #11584 | +| [@atescivitci-cmd](https://github.com/atescivitci-cmd) | #12105 | +| [@b3nw](https://github.com/b3nw) | #11970, #11971, #12147 | +| [@backryun](https://github.com/backryun) | #11259, #11950, #12075, #12076, #12078, #12079, #12081, #12082, #12181, #12228, #12239, #12255, #12258, #12277, #12367, #12423, #12524, #12538 | +| [@benjaminkitt](https://github.com/benjaminkitt) | #11747 | +| [@benzntech](https://github.com/benzntech) | #11614, #11615 | +| [@Bl0ck154](https://github.com/Bl0ck154) | #11948, #11951, #11952, #11953, #11954 | +| [@brick30llc-ctrl](https://github.com/brick30llc-ctrl) | #11752, #12242, #12262 | +| [@Chewji9875](https://github.com/Chewji9875) | #11455, #11875, #12028 | +| [@cheynetom](https://github.com/cheynetom) | #12058 | +| [@davidebaraldo](https://github.com/davidebaraldo) | #12222, #12304 | +| [@davidlinfr](https://github.com/davidlinfr) | #6390 | +| [@ddarkr](https://github.com/ddarkr) | #12124 | +| [@Deftera186](https://github.com/Deftera186) | #11809 | +| [@dpozimski](https://github.com/dpozimski) | #11945 | +| [@DW-MediaLab](https://github.com/DW-MediaLab) | #12229 | +| [@echel0nn](https://github.com/echel0nn) | #11923 | +| [@f9td56dbgh-hub](https://github.com/f9td56dbgh-hub) | #11660 | +| [@fabioluissilva](https://github.com/fabioluissilva) | #11991 | +| [@foreveryh](https://github.com/foreveryh) | #12177 | +| [@geek007git](https://github.com/geek007git) | #12115, #12116, #12117, #12120, #12122 | +| [@geekyNads](https://github.com/geekyNads) | #11784, #12266 | +| [@ggiak](https://github.com/ggiak) | #11561, #12276, #12350, #12402 | +| [@gonisulaimann](https://github.com/gonisulaimann) | #12368, #12369, #12371 | +| [@Gorillaz322](https://github.com/Gorillaz322) | #12207 | +| [@groovecityJO](https://github.com/groovecityJO) | #12682 | +| [@hartmark](https://github.com/hartmark) | #11434, #11452, #11473, #11499, #11703, #11983, #11984, #11985, #11986, #11988, #11989, #11990, #11994, #12221, #12293, #12445, #12446, #12447, #12448, #12460, #12461, #12623, #12646, #12650, #12680, #12717, #12718, #12727, #12741, #12854 | +| [@hizzt](https://github.com/hizzt) | #11894 | +| [@hongnoul](https://github.com/hongnoul) | #11484 | +| [@HouMinXi](https://github.com/HouMinXi) | #11411, #11414, #11512, #11518, #11520, #11641, #11642, #11643, #11687, #11779, #11849, #11850, #11851, #11915, #11916, #11918, #11919, #11920, #12013, #12017, #12033, #12042, #12106, #12139, #12166, #12169, #12171, #12205, #12213, #12312, #12325, #12487, #12488, #12495, #12504, #12557, #12566, #12590, #12591, #12624, #12626, #12632, #12637, #12678, #12696, #12697, #12711, #12733, #12746, #12767, #12770, #12789, #12803, #12805, #12811, #12866, #12868, #12899, #12926, #12934, #12950, #12951, #12974, #13001, #13006, #13011, #13017, #13026, #13027, #13034, #13035, #13038, #13042, #13050, #13060, #13061, #13069, #13107, #13120, #13136, #13195, #13197 | +| [@Hsia97](https://github.com/Hsia97) | #11624 | +| [@jacobsparts](https://github.com/jacobsparts) | #11854, #12155, #12167 | +| [@joglomedia](https://github.com/joglomedia) | #11980 | +| [@jonlwheat2-gif](https://github.com/jonlwheat2-gif) | #11443, #11450, #11549, #11567, #11608, #11633, #11635, #11640, #11644, #11671, #11672, #11673, #11674, #11675, #11676, #11677, #11706, #11783, #12051, #12052, #12053, #12055 | +| [@kanade-hoshino](https://github.com/kanade-hoshino) | #12180 | +| [@Karan825](https://github.com/Karan825) | #11861 | +| [@KaspaPulse](https://github.com/KaspaPulse) | #11389, #11469, #11628, #11666, #11910 | +| [@keeltrace](https://github.com/keeltrace) | #12080, #12223 | +| [@killer30001000](https://github.com/killer30001000) | #12178 | +| [@KooshaPari](https://github.com/KooshaPari) | #12470, #12592, #12667, #12699, #12703, #12706, #12764, #12769, #12771, #13300, #13301, #13302, #13340, #13401, #13403, #13404, #13405, #13406, #13407, #13409, #13410, #13411, #13412, #13413, #13414, #13418, #13424, #13427, #13433, #13523, #13524, #13525, #13528, #13531, #13532, #13534, #13539, #13543, #13545, #13546, #13547, #13550, #13553 | +| [@kriptoburak](https://github.com/kriptoburak) | #11370 | +| [@KrzysiekSko](https://github.com/KrzysiekSko) | #12425, #12432, #12673 | +| [@marcelokarval](https://github.com/marcelokarval) | direct commit / report | +| [@maxmad64bis](https://github.com/maxmad64bis) | #1622, #11435, #11437, #11441, #11537, #11550, #11553, #11555, #11557, #11812, #11842, #11843, #11903, #12151, #12214, #12215, #12218, #12226, #12314, #12316, #12317, #12318, #12319, #12320, #12321, #12507, #12715, #12731, #12744, #12786, #12787, #12788, #12790, #12792, #12794, #12795, #12828, #12832, #12853, #12857, #12870, #12937, #12941, #12975, #13141, #13142, #13146, #13147 | +| [@mdigitalbh81](https://github.com/mdigitalbh81) | #12240 | +| [@Meet6338-X](https://github.com/Meet6338-X) | #11598, #11609, #12110 | +| [@morpheus9393](https://github.com/morpheus9393) | #11943 | +| [@MumuTW](https://github.com/MumuTW) | #11492, #11502, #11506, #11507, #11626, #11675, #11685, #11728, #11746, #11888, #11889, #11890, #11892 | +| [@Neuron-Mr-White](https://github.com/Neuron-Mr-White) | #11622, #11801, #12256 | +| [@NightStalker-87](https://github.com/NightStalker-87) | #12183 | +| [@NoxzRCW](https://github.com/NoxzRCW) | #11879, #11880, #11881, #11882, #11883, #11935 | +| [@ntdat812](https://github.com/ntdat812) | #11585, #12095 | +| [@ntdatt812](https://github.com/ntdatt812) | #11368, #11573, #11574, #11576, #11577, #11580, #11582, #11583, #11588, #11589, #11590, #11591, #11592, #11593, #11672, #12177, #12180, #12873, #12918, #12920, #12921, #12925, #12930, #13007, #13009, #13024, #13025, #13083, #13087, #13101, #13104, #13110 | +| [@official-burak](https://github.com/official-burak) | #11542 | +| [@opensource-elearning](https://github.com/opensource-elearning) | #12179, #12189, #12278, #12286 | +| [@oyi77](https://github.com/oyi77) | #11408, #11409, #11421, #11505, #11677, #12036, #12110 | +| [@pacocartones](https://github.com/pacocartones) | #11521, #11522, #11527, #11528, #11529, #11530, #11531, #11532, #11533, #11534, #11595, #11599, #11603, #11604, #11605, #11607, #11610, #11676, #11714, #11716, #11718, #11767, #11838, #11860, #11862, #11869, #11871, #11872, #11873, #11903, #11906, #11921, #11934, #12356, #12358, #12359, #12360, #12361, #12362, #12364, #12365, #12373, #12374, #12375, #12376, #12377, #12379, #12380, #12381, #12386, #12387, #12389, #12390, #12394, #12395, #12397, #12401, #12403, #12404, #12522, #12523, #12535, #12536, #12540, #12541, #12543, #12545, #12548, #12549, #12550, #12551, #12552, #12647, #12651, #12653, #13055 | +| [@patrykkopycinski](https://github.com/patrykkopycinski) | #11936, #11937, #12224 | +| [@PauloFH](https://github.com/PauloFH) | #11509 | +| [@PauloHSOliveira](https://github.com/PauloHSOliveira) | #12241 | +| [@ponkcore](https://github.com/ponkcore) | #12054 | +| [@Prajeeth-12](https://github.com/Prajeeth-12) | #11634, #12046 | +| [@quiterunner-commits](https://github.com/quiterunner-commits) | #12131, #12143 | +| [@rafacpti23](https://github.com/rafacpti23) | #11554, #11558, #12192, #12197, #12198, #12202, #12203, #12204 | +| [@ragnar-claude](https://github.com/ragnar-claude) | #11564 | +| [@raheemuddin786](https://github.com/raheemuddin786) | #11491, #11839, #11840, #11841, #12003, #12230, #12231, #12232, #12233, #12234 | +| [@rahilmavani](https://github.com/rahilmavani) | #11761 | +| [@Rahulsharma0810](https://github.com/Rahulsharma0810) | #11771 | +| [@RaviTharuma](https://github.com/RaviTharuma) | #11710, #11727, #11797, #11798, #11802, #11805, #11806, #11811, #12098, #12099, #12101, #12449, #12452, #12472, #12473, #12493, #12533, #12607, #12628, #12631, #12636, #12875, #12876, #12880, #12882, #12884 | +| [@rezjalibd](https://github.com/rezjalibd) | #12186 | +| [@RhianB14](https://github.com/RhianB14) | #11692 | +| [@rifqiawl](https://github.com/rifqiawl) | #11517, #11519, #11969 | +| [@rqzbeh](https://github.com/rqzbeh) | #11390 | +| [@Sabeekhann](https://github.com/Sabeekhann) | #11699, #11705 | +| [@santosraju99-hub](https://github.com/santosraju99-hub) | #11755, #11770 | +| [@Seramicx](https://github.com/Seramicx) | #11568 | +| [@Siva010](https://github.com/Siva010) | #12191 | +| [@solstxce](https://github.com/solstxce) | #11597 | +| [@soroush5](https://github.com/soroush5) | #12691 | +| [@tenshiak](https://github.com/tenshiak) | #12279 | +| [@TheDemonTuan](https://github.com/TheDemonTuan) | #11468, #11470, #11471, #11482, #11548, #11758, #11775, #11814 | +| [@thomasmaerz](https://github.com/thomasmaerz) | #12834 | +| [@tuandinh0801](https://github.com/tuandinh0801) | #11454 | +| [@turbolego](https://github.com/turbolego) | #11621, #11762, #11772, #11774, #11781, #12216 | +| [@Tushar49](https://github.com/Tushar49) | #11392 | +| [@ujjawalkaushik1110](https://github.com/ujjawalkaushik1110) | #11863 | +| [@vermasomesh835](https://github.com/vermasomesh835) | #11794 | +| [@vsd2807](https://github.com/vsd2807) | #11565, #11619 | +| [@wahidsadik371-coder](https://github.com/wahidsadik371-coder) | #12126 | +| [@watchingdogs](https://github.com/watchingdogs) | #12031 | +| [@wildcard](https://github.com/wildcard) | #11620 | +| [@xiaoyaner0201](https://github.com/xiaoyaner0201) | #11460, #11662, #12083 | +| [@Xxx91n](https://github.com/Xxx91n) | #11690 | +| [@yourspraveen](https://github.com/yourspraveen) | #11146 | +| [@yxyxy](https://github.com/yxyxy) | #11671 | +| [@ZaimMarzuki](https://github.com/ZaimMarzuki) | #11960 | +| [@Zartharas](https://github.com/Zartharas) | #11340 | +| [@zero-executioner](https://github.com/zero-executioner) | #11631 | +| [@diegosouzapw](https://github.com/diegosouzapw) | maintainer | + +--- ## [3.8.50] — 2026-08-25 _Living section — regenerated 2026-08-12 from all cycle commits (cycle open `ed2db6cb19` → tip). Bullets carry the merged PR and its author; direct pushes listed separately._ diff --git a/docs/i18n/fr/CHANGELOG.md b/docs/i18n/fr/CHANGELOG.md index 627d187459..0ad860a84e 100644 --- a/docs/i18n/fr/CHANGELOG.md +++ b/docs/i18n/fr/CHANGELOG.md @@ -8,16 +8,1119 @@ ## [3.8.51] — TBD -_Living section — cycle opened at the v3.8.50 freeze (parallel-cycle model). Bullets are aggregated from `changelog.d/` fragments at each `/generate-release` phase._ +_Living section — reconciled 2026-09-15 from all cycle commits (`091589089c` → `c0f92ec98a`, 916 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 | **123** | +| 📝 Commits in the cycle | **916** | +| 🔀 Pull requests referenced | **892** | +| 📋 Changelog entries | **919** | +| 🙌 Contributors credited in entries | **121** | +| 🤖 Automated dependency commits | 18 | + +**Entries by type** + +| Type | Count | +| --- | ---: | +| 🐛 Fixes | 592 | +| ✨ Features | 131 | +| 🧹 Chore | 93 | +| 📚 Docs | 42 | +| 🧪 Tests | 32 | +| ♻️ Refactor | 8 | +| 🏗️ Build | 7 | +| ⚡ Performance | 7 | +| ⚙️ CI | 2 | +| ⏪ Reverts | 2 | +| 📦 Dependencies | 2 | +| 🔒 Security | 1 | + +### 🏆 Top 25 contributors this cycle + +_By commits in `091589089c..c0f92ec98a`, author identities consolidated via `.mailmap` and the merged PR's GitHub login. Bots excluded._ + +| # | Contributor | Commits | +| ---: | --- | ---: | +| 🥇 | diegosouzapw | 323 | +| 🥈 | Bob.Hou (@HouMinXi) | 79 | +| 🥉 | Paco Cartones (@pacocartones) | 77 | +| 4 | Dizzle (@maxmad64bis) | 49 | +| 5 | Koosha Paridehpour (@KooshaPari) | 43 | +| 6 | Nguyen Thanh Dat (@ntdatt812) | 31 | +| 7 | Ravi Tharuma (@RaviTharuma) | 26 | +| 8 | Markus Hartung (@hartmark) | 22 | +| 9 | Webman (@jonlwheat2-gif) | 22 | +| 10 | backryun | 17 | +| 11 | anhtahaylove | 15 | +| 12 | Rafa Martins (@rafacpti23) | 10 | +| 13 | Syed Raheemuddin (@raheemuddin786) | 10 | +| 14 | Nguyễn Viết Tuấn (@TheDemonTuan) | 8 | +| 15 | MumuTW | 7 | +| 16 | Paijo (@oyi77) | 7 | +| 17 | Tobias Andersen (@turbolego) | 6 | +| 18 | Bl0ck (@Bl0ck154) | 5 | +| 19 | SHANMUGAPRIYAN (@geek007git) | 5 | +| 20 | KaspaPulse | 5 | +| 21 | Abhishek Sharma (@abhisheksharma2411) | 4 | +| 22 | Abhishek Divekar (@adivekar-utexas) | 4 | +| 23 | Andrew B. (@AndrianBalanescu) | 4 | +| 24 | NoxzRCW | 4 | +| 25 | opensource-elearning | 4 | ### ✨ New Features +- **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). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- Added Google AI Studio Gemini batch text-to-speech support through `POST /v1/audio/speech`. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- 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. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(routing):** subscription-first auto groupings — `auto/subscription` routes only through plan-included connections with a documented hard-stop overage and fails closed on exhaustion, while `auto/thrifty` orders the pool `subscription → keyless → free → cheap → premium` and steps up one rung at a time as each is exhausted. Billing class comes from a curated per-connection catalog (uncurated is treated as metered, never plan-included), both reuse STRICT_ZERO_COST's per-connection verification, and a quota reading whose `resetAt` has passed is now refreshed regardless of TTL so routing returns to plan capacity as soon as it resets ([#11146](https://github.com/diegosouzapw/OmniRoute/pull/11146)) — thanks @yourspraveen +- **feat(providers):** publish a management-authenticated versioned web-session credential contract from OmniRoute's canonical browser credential metadata ([#11340](https://github.com/diegosouzapw/OmniRoute/pull/11340)) — thanks @Zartharas +- **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)) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add Xquik X search with typed results, credential validation, REST routing, and MCP selection ([#11370](https://github.com/diegosouzapw/OmniRoute/pull/11370)) — thanks @kriptoburak +- **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)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(dashboard):** surface durable exclusive managed leases in the existing Sessions view, keeping leased clients visible across idle gaps while marking connections with in-flight work as active ([#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389)) — thanks @KaspaPulse +- **build(bun):** allow Turbopack bundler flag on Bun 1.4+ with configurable Webpack fallback ([#11471](https://github.com/diegosouzapw/OmniRoute/pull/11471)) — thanks @TheDemonTuan +- feat(api): add an opt-in `modelVisibilityAllowlist`/`modelVisibilityDenylist` settings pair to curate exactly which models `/v1/models` advertises, mirrored into every `auto/*` combo candidate pool so a denied model cannot be routed to via combo selection either (#11481) ([#11997](https://github.com/diegosouzapw/OmniRoute/pull/11997)) +- **feat(rankings):** the Free Provider Rankings page shows what each provider actually served over the last 24 h. It ranked by ELO alone, which left a provider that answers every call with an error in first place; the usage data was already served by the API but never requested. A provider with too small a sample shows a dash, not a number ([#11546](https://github.com/diegosouzapw/OmniRoute/pull/11546)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **feat(guardrails):** enforce a bounded, deterministic contract for Video Bridge transcripts — 256 cues, 4096 input code units and 4 KiB UTF-8 per cue, 64 KiB total text, malformed-Unicode rejection, focus-window scoping, cross-source reconciliation with contributing-source metadata, and a structural provenance trust boundary so caller JSON can never self-assert `embedded`/`audio-bridge` provenance ([#11652](https://github.com/diegosouzapw/OmniRoute/issues/11652)) ([#12009](https://github.com/diegosouzapw/OmniRoute/pull/12009)) +- **feat(video):** orchestrate optional Video Bridge audio extraction and Audio Bridge STT behind a dual opt-in (operator setting AND per-request signal) — a new loopback-only broker `mode=audio` operation shares the frame path's exact process queue, deadline, AbortSignal, and byte budgets to extract a bounded mono 16 kHz PCM WAV from the same already-downloaded video, then reuses the existing Audio Bridge transcription boundary; provider segment timing is preserved when available and marked coarse otherwise, and every failure degrades to a visual-only-safe partial instead of throwing (#11654). ([#12012](https://github.com/diegosouzapw/OmniRoute/pull/12012)) +- Add a tenant-bound Video Bridge drill-down lifecycle on top of the existing secure cache substrate: opaque hashed handles (never raw session/video identifiers), preview/standard/detail multiresolution variants resampled on read, response pagination capped at 8 frames and 32 MiB, and a new authenticated `/api/v1/video-bridge/drilldown` consumer route that stays disabled for remote access by default and denies cross-key access with the same response as a nonexistent handle (no existence oracle). ([#12006](https://github.com/diegosouzapw/OmniRoute/pull/12006)) +- **test(video):** Add the Video Bridge FU-07/FU-09 promotion-evidence harness (#11656) — a frozen Zod manifest schema covering the 8 required scenario kinds (static scenes, rapid cuts, late facts, fades, blur, small text, close events, visual prompt injection) with a minimum of 3 repetitions per case, deterministic declarative fixture recipes (`videoBridgePromotionFixtures.ts`), a pure medians/p95 metrics aggregator, a pure FU-07/FU-09 promotion-verdict evaluator applying the ticket's exact thresholds (missing token usage always holds), a digest-only persistence layer that never retains raw media or raw model responses, and a versioned per-model promotion allowlist shipped empty with every model defaulting to `hold`. The FU-07/FU-09 promotion verdicts themselves remain HOLD — they require a real evidence run against real models on VPS 192.168.0.15. ([#12008](https://github.com/diegosouzapw/OmniRoute/pull/12008)) +- **feat(video bridge):** "embedded" transcript provenance can now be legitimately earned instead of merely asserted — a bounded, allowlisted (`mov_text`/`subrip`/`webvtt`) subtitle probe runs through the loopback-only Video Bridge broker (at most 2 streams, 10s subdeadline bounded by the request deadline, 256 KiB output, 4096-code-unit lines), normalized through a bounded, ReDoS-safe WebVTT parser and Zod-validated end to end. The adapter always resolves to an explicit `success`/`absent`/`transient_failure` outcome — a subtitle failure never breaks the visual description path, and only a fingerprint-verified broker response (never a caller-declared label) can produce embedded cues (#11659). ([#12011](https://github.com/diegosouzapw/OmniRoute/pull/12011)) +- Default new Antigravity-family connections (agy CLI imports and Antigravity OAuth connects) to model auto-sync, so live model discovery lands in the synced catalog and `/v1/models` picks up freshly released upstream models (e.g. Gemini 3.7 Flash tiers) without code changes. Existing connections keep their current setting; the per-connection dashboard toggle remains the opt-out. (#11685 — thanks @MumuTW) ([#11887](https://github.com/diegosouzapw/OmniRoute/pull/11887)) +- **feat(ui):** enable React Compiler (`reactCompiler: true` + `babel-plugin-react-compiler`) for automatic memoization at build time ([#11783](https://github.com/diegosouzapw/OmniRoute/pull/11783)) — thanks @jonlwheat2-gif +- **feat(providers):** add SeekAi (`seekai.cc`) as an OpenAI-compatible New-API gateway — catalog id `seekai` (alias `ska`), `https://seekai.cc/v1`, live `/v1/models` via `passthroughModels`, aggregator-list membership so New-API balance detection can opt in. No referral/aff codes. ([#11786](https://github.com/diegosouzapw/OmniRoute/issues/11786)) ([#12557](https://github.com/diegosouzapw/OmniRoute/pull/12557)) — thanks @HouMinXi +- **feat(zai):** add GLM-5.3-Flash Coding Plan support (1M context, 128K output, vision, `low|high|max` reasoning) and route `zai` GLM-5.3-family API-key traffic through the OpenAI-compatible Coding Plan endpoint with native thinking defaults ([#11801](https://github.com/diegosouzapw/OmniRoute/pull/11801)) — thanks @Neuron-Mr-White +- **feat(combo):** choose how combo models are ordered — manual, provider, score, or name — via a sort control in the dashboard builder, persisted in `config.modelSort` and re-applied on load and after add ([#11812](https://github.com/diegosouzapw/OmniRoute/pull/11812)) — thanks @maxmad64bis +- **feat(free):** custom models can be marked free-tier via `customModels[].isFree`; `isFreeModel()` is the first door and `hidePaidModels` respects it even for providers outside the free budget ([#11843](https://github.com/diegosouzapw/OmniRoute/pull/11843)) — thanks @maxmad64bis +- **feat(nodejs):** add `5dive` as a `configure` target — `omniroute configure 5dive` / `omniroute setup-5dive` write a 5dive auth profile that points an agent fleet's `claude` seats at OmniRoute, with the root-only write, the loopback-vs-`https` endpoint rule and the per-seat model pin handled explicitly ([#11852](https://github.com/diegosouzapw/OmniRoute/pull/11852)) — thanks @5dive-bot +- **feat(sse):** treat `max` as a first-class reasoning-effort tier and clamp per model family (GLM 5.1+/DeepSeek V4+/Kimi K3+ keep native `max`; o1/MiniMax/Grok/Muse Spark clamp to their upstream ceiling) ([#11875](https://github.com/diegosouzapw/OmniRoute/pull/11875)) — thanks @Chewji9875 +- **feat(providers):** the provider plugin manifest now advertises a `usage-fetch` capability for the 40 providers that have a wired usage/quota fetcher, so external dashboards can read it from `GET /api/v1/provider-plugin-manifest` instead of parsing `open-sse/services/usage.ts` after every release. Discovery only — no new fetcher, no quota change, and the Dashboard quota widget stays gated by `USAGE_SUPPORTED_PROVIDERS`. `USAGE_FETCHER_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/fetcherProviders.ts`) and is re-exported from `services/usage.ts`, keeping the manifest module a light leaf instead of pulling the ~490-module usage dispatcher into the manifest route. ([#11903](https://github.com/diegosouzapw/OmniRoute/pull/11903)) — thanks @maxmad64bis / @pacocartones +- **feat(plugins):** `OMNIROUTE_PLUGINS_DIR` sets the directory the runtime plugin scanner reads — and the root the plugin manager installs into — overriding the `HOME`-derived default, so a Docker/K8s deployment can point straight at its bind-mounted plugin tree instead of moving `HOME` just to relocate the scan path. An image that exports no home no longer scans `/tmp/.omniroute/plugins` in silence: the resolved directory is logged once at startup as `scanner.dir_resolved`, naming the input that won. Unset, behaviour is unchanged. Distinct from the CLI-only `OMNIROUTE_PLUGIN_PATH`, which finds `omniroute-cmd-*` command packages and never reached this scanner ([#11906](https://github.com/diegosouzapw/OmniRoute/pull/11906)) — thanks @amaleta / @pacocartones +- **feat(leases):** add an explicit owner-authenticated status action that returns only the active lease's privacy-safe configured connection and provider labels, with generation fencing and no credential or internal-id disclosure ([#11910](https://github.com/diegosouzapw/OmniRoute/pull/11910)) — thanks @KaspaPulse +- **feat(providers):** import-from-file modal shows per-row API errors and ships a downloadable CSV template ([#12071](https://github.com/diegosouzapw/OmniRoute/issues/12071)) ([#12504](https://github.com/diegosouzapw/OmniRoute/pull/12504)) — thanks @HouMinXi +- **feat(providers):** dashboard search matches connection name and `baseUrl` so imported OpenAI-compat nodes surface on the provider card ([#12108](https://github.com/diegosouzapw/OmniRoute/issues/12108)) ([#12495](https://github.com/diegosouzapw/OmniRoute/pull/12495)) — thanks @HouMinXi +- **feat(routing):** add a `score` Auto router strategy that selects the highest configured weighted score and reuses `explorationRate`. ([#12155](https://github.com/diegosouzapw/OmniRoute/pull/12155)) — thanks @jacobsparts +- Add a runtime feature flag to disable universal context handoffs globally without changing the default behavior. ([#12167](https://github.com/diegosouzapw/OmniRoute/pull/12167)) — thanks @jacobsparts +- **feat(providers):** the provider plugin manifest now also advertises a `usage-supported` capability for the 46 providers whose usage API is accepted by the server and Dashboard routes, so integrators can distinguish "the server will serve quota for this provider" from "a fetcher is wired" without reading TypeScript. Discovery only — no fetcher or quota change. `usage-fetch` resolves on id or alias (the usage dispatcher accepts both); `usage-supported` resolves on id alone, matching the runtime guard `USAGE_SUPPORTED_PROVIDERS.includes(providerId)`. `USAGE_SUPPORTED_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/supportedProviders.ts`) and is re-exported from `providers.ts`, mirroring the `fetcherProviders` leaf from #11903 and keeping the manifest a light module. ([#12214](https://github.com/diegosouzapw/OmniRoute/pull/12214)) — thanks @maxmad64bis +- **feat(rankings):** order Free Provider Rankings by what each provider actually served — `GET /api/free-provider-rankings?sortBy=reliability` and a "Most reliable first" toggle on the page. Providers with too few calls to state a success rate keep their score order below the measured ones; the default order is unchanged ([#12218](https://github.com/diegosouzapw/OmniRoute/pull/12218)). — thanks @maxmad64bis +- **perf(sse):** defer `cloneLogPayload()` in the structured SSE collector until after the `maxEvents`/`maxBytes` cap check, eliminating ~9,800 wasted `structuredClone` calls per streaming response (65–71% faster `push()`). Reducer snapshot isolation restored for OpenAI and Responses summaries ([#12241](https://github.com/diegosouzapw/OmniRoute/pull/12241)) — thanks @PauloHSOliveira ([#12243](https://github.com/diegosouzapw/OmniRoute/pull/12243)) +- **feat(usage):** Devin CLI agentic quota (Codeium seat-management GetUserStatus) and OpenRouter key limits plus account credits now surface in Provider Limits ([#12256](https://github.com/diegosouzapw/OmniRoute/pull/12256) — thanks @Neuron-Mr-White) +- **feat(settings):** persist `headroomUrl` through Settings so status/start use the operator URL instead of only `HEADROOM_URL` ([#12306](https://github.com/diegosouzapw/OmniRoute/issues/12306)) ([#12487](https://github.com/diegosouzapw/OmniRoute/pull/12487)) — thanks @HouMinXi +- **feat(auto-combo):** Auto-Combo scoring can now weigh how often a provider/model has actually succeeded. The engine already carried that number on every candidate — 24 hours of usage history behind a ten-sample floor, real-time metrics otherwise — and the scoring function never read it, while the weight table described `stability` as if it did. `reliability` (`1 - failureRate`, with the same field precedence and the same rate-bounding the speed ranking already uses, so a corrupt reading means "nothing observed" rather than "fails every call") is now a declared factor shipping at weight `0`, so routing is unchanged until an operator gives it one, and the `stability` description now matches what that factor computes ([#12317](https://github.com/diegosouzapw/OmniRoute/pull/12317)) — thanks @maxmad64bis +- **feat(routing):** With `freeAccessPolicy: "strict"`, the read-only candidate listing (`GET /v1/auto-combo/{channel}/candidates`) no longer hides the candidates the zero-cost guard excludes — the same read-only transparency the resilience filter already honours (#9133). Each candidate now carries `freeAccessExclusion` saying why it would be kept out, and it tells an exhausted allowance apart from a quota reading that never arrived or went stale, which used to look identical from the outside. Routing is unchanged: the listing reports, it never enforces. The separate `excludeTosAvoid` guard still drops its candidates without a reason; that gap is now documented rather than closed ([#12319](https://github.com/diegosouzapw/OmniRoute/pull/12319)) — thanks @maxmad64bis +- **feat(radar):** The Radar catalog table now shows two facts it was already receiving from the feed and dropping on the floor: the per-model rate limits (requests and tokens, per minute and per day) in a new column, and a badge when a provider's terms state it may train on the prompts you send. A limit of zero renders as zero rather than "rate-only" — for a ceiling those are opposite facts — and a model with no training statement gets no badge, because an absent statement is not a guarantee ([#12320](https://github.com/diegosouzapw/OmniRoute/pull/12320)) — thanks @maxmad64bis +- **feat(radar):** explain Community, single-use, contributor, supporter, recovery, abuse, offers, and privacy rules before either Radar activation action, and remove the superseded fixed-PR grant promise from every UI locale ([#12342](https://github.com/diegosouzapw/OmniRoute/pull/12342)) +- **feat(gamification):** the dashboard Profile page now shows the real daily streak — `/api/gamification/level` returns `streak: { current, longest }` (per key with `apiKeyId`, operator-wide maximum otherwise) and the streak card reads it instead of a hard-coded 0 (#2403) ([#12377](https://github.com/diegosouzapw/OmniRoute/pull/12377)) — thanks @pacocartones +- **feat(gamification):** the dashboard leaderboard now shows each API key's display name under the Name column instead of a truncated key id; `GET /api/gamification/leaderboard` attaches `name` per entry (name only — no key material), while the shared ranking helper and the federation leaderboard stay id-only — thanks @pacocartones ([#12385](https://github.com/diegosouzapw/OmniRoute/pull/12385)) +- **feat(gamification):** enforce the documented 1000 XP/min per-API-key anti-cheat rate limit on the XP award path; over-limit awards are logged and skipped instead of persisted, and the sliding window now matches the timestamp format stored in `xp_audit_log` ([#2403](https://github.com/diegosouzapw/OmniRoute/issues/2403)) ([#12390](https://github.com/diegosouzapw/OmniRoute/pull/12390)) — thanks @pacocartones +- **feat(admin):** localize the gamification anomalies page — the loading state, the Status column and the Suspicious badge now come from the `common` catalog (new `common.suspicious` key propagated to every locale) — add it to the Gamification sidebar group as `gamification-admin` (`/dashboard/gamification/admin`), and expose the loading and empty states as polite `role="status"` live regions (#12401 — thanks @pacocartones) +- **feat(providers):** skip GitHub combo members missing from the live synced catalog, and drop Copilot models that are policy-disabled or hidden from the model picker ([#12473](https://github.com/diegosouzapw/OmniRoute/pull/12473)) — thanks @RaviTharuma +- **feat(i18n):** locale-expansion tooling — `npm run i18n:add-locale` adds a language to config, dashboard, docs mirrors, CLI, README/indexes and site in one command; browser, CLI and cookie detection resolve `uk`, `fil`/`tl`, `zh-Hant` (and the retired legacy `in` → `id`) via config aliases; `config/i18n.json` now ships in the npm package so the published CLI can read it; new real-translation ratio gate (`npm run i18n:check-ratio`, advisory) with a per-locale ratchet baseline; `run-translation --adopt` restores `.i18n-state.json`; `sync-language-bars` generates 🌐 bars from config; `validate_translation.py` loads its allowlist again. Retires the duplicate `in` locale (Indonesian mislabelled as Hindi) — 42 honest locales; saved `NEXT_LOCALE=in` / `OMNIROUTE_LANG=in` keep working. (#12496) +- **feat(api):** Emit gateway-measured `tokens_per_second` (TTFT excluded) on streaming usage and `X-OmniRoute-Tokens-Per-Second` when first-token latency is known ([#12616](https://github.com/diegosouzapw/OmniRoute/issues/12616)) ([#12631](https://github.com/diegosouzapw/OmniRoute/pull/12631)) — thanks @RaviTharuma +- **feat(opencode-plugin): map gateway cost/usage/tok/s onto OpenCode inference payloads** — the official plugin copies `X-OmniRoute-Response-Cost`, token counts, `X-OmniRoute-Tokens-Per-Second` / `usage.tokens_per_second`, TTFT, and the winning `X-OmniRoute-Model` onto the JSON/SSE body OpenCode already consumes. Missing tok/s is left unset (never `tokens / latency`). (#12636) — thanks @RaviTharuma +- **feat(combo):** add combo strategy `quota-weighted`: skip exhausted accounts, then weighted-draw by leftover / in-flight load; existing conversations stay pinned until the account is empty ([#12789](https://github.com/diegosouzapw/OmniRoute/pull/12789)) — thanks @HouMinXi +- **feat(models):** Account live listings become the chat catalog source for Claude, Codex, Copilot, and AGY; public metadata only fills prices on IDs those accounts already list. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **fix(models):** Union `agy` and `antigravity` live catalogs so `agy/gemini-3.8-flash-high` is not rejected after the prefix folds to `antigravity`. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **feat(opencode):** Add `@omniroute/opencode-plugin-v2`, an opencode v2 plugin that gives a v2 install a route to an OmniRoute gateway: it publishes models, combos and auto-combos into the host catalog, refreshes them lazily behind a 300s TTL, and keeps serving the last known catalog from an on-disk snapshot when the gateway is unreachable. The v1 plugin (`@omniroute/opencode-plugin`) is untouched and keeps working as before. The plugin adapts to the host instead of assuming one shape: it reads the shape the host seeds into the catalog draft and publishes the `api` block, the pre-`api` top-level fields, or both. The gateway key comes from the host credential store when one is connected, so no secret needs to sit in a config file; a source the gateway refuses is named in a warning instead of silently degrading the catalog to raw model ids; and tool schemas bound for a Gemini model are stripped of the JSON-Schema keywords Gemini rejects, so tool calling survives that route ([#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870)) — thanks @maxmad64bis +- feat(services): show sanitized CLIProxyAPI account health from its authenticated management API without exposing credentials, file paths, or raw account metadata (#6342) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add AnySearch free web search + URL extract (webFetch) with typed results, credential validation, REST routing, and MCP selection - fallback-only ([#11690](https://github.com/diegosouzapw/OmniRoute/pull/11690)) — thanks @Xxx91n +- **feat(dashboard):** display clamped `[0, 100]%` cached input token ratio in request logs table ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) — thanks @b3nw +- **feat(catalog):** add `OMNIROUTE_DISABLE_THINKING_LEVEL_VARIANTS` feature flag to optionally filter out thinking level variants from model catalog ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) — thanks @b3nw +- **feat(dashboard):** continuously export call logs to external analytics stores. A pluggable destination registry ships the full Logs-tab record set on an hourly `JobRegistry` cron, with a persisted per-destination cursor, batched inserts, a config UI rendered from each destination's own field descriptors, and a REST layer (`/api/log-export/*`) for CRUD, a connection test, and an on-demand run. A destination can opt into `includeBodies` to also ship the request and response payloads shown in the Logs detail pane, including the client and provider views of each call; this is off by default, and payloads inherit the dashboard's PII sanitisation, secret redaction and `noLog` handling. Google BigQuery is the first destination, using a service-account key stored encrypted at rest and streaming inserts keyed by call-log id, into a table that is day-partitioned on `timestamp` and clustered on `api_key_name`, `provider`, `model` and `status`. ([#11945](https://github.com/diegosouzapw/OmniRoute/pull/11945)) — thanks @dpozimski +- **feat(providers):** add MaxAI as a signed, OpenAI-compatible provider serving its 13 paid chat models (GPT-5.6 / Luna / Thinking, Claude 5 Sonnet, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite, Grok 4.1-fast / 4.5, DeepSeek V3.2 / R1, Llama 3.3 70B) through OmniRoute's `/v1` endpoint, with per-request HMAC-SHA1→SM3→AES request signing, live model + context-window discovery from `/models/get_config`, and prompted tool-calling translated to OpenAI `tool_calls` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI vision input — image_url content parts are forwarded inline in `message_content` to the 6 vision-capable models (GPT-5.6 / Luna / Thinking, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite) ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI image generation — 6 image models (gpt-image-1, dall-e-3, flux-1-schnell/dev/pro, sd3-medium) exposed through `POST /v1/images/generations` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI document RAG — inline base64 file/document attachments are uploaded to MaxAI (content-addressed `doc_id`) and attached to the chat via `doc_list` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** browserless MaxAI onboarding — email device-pair login (`/api/providers/[id]/login`) and signed access-token refresh, so a connection can be created and kept fresh without a real browser or Google OAuth ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** per-provider TLS impersonation profile (MaxAI presents a Windows Firefox-150 client fingerprint) so its bot-sensitive endpoints accept OmniRoute traffic ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** add **Nimble** as a web-search and web-fetch provider (`nimble-search`) — `/v1/search` routes to Nimble's search API at `lite` depth (locale, freshness and include/exclude domain filters mapped onto the shared request contract), and `/v1/web/fetch` routes to Nimble Extract, which covers all four fetch formats (`markdown`, `html`, `links`, `screenshot`) from a single call. One API key serves both surfaces. ([#11620](https://github.com/diegosouzapw/OmniRoute/pull/11620)) — thanks @wildcard +- The npm publish is automatic again, through npm Trusted Publishing (OIDC): the hosted `stage-npm` job publishes with a short-lived credential minted from GitHub's id-token — no `NPM_TOKEN`, no 2FA prompt, provenance attached. `publish_mode=staged` (owner approves with 2FA) and `direct` (token) remain available on `workflow_dispatch`. ([#11931](https://github.com/diegosouzapw/OmniRoute/pull/11931)) +- **feat(providers):** Add **Opper** as an API-key gateway provider — EU-hosted AI gateway with 700+ models from 30+ providers behind one OpenAI-compatible API and one key (`OPPER_API_KEY`); model ids use `provider/model` format (e.g. `anthropic/claude-sonnet-4-6`, `openai/gpt-5`); live model catalog at `https://api.opper.ai/v3/compat/models`; entry mirrors `requesty` (same shape, `passthroughModels: true`, no static seed) ([#11629](https://github.com/diegosouzapw/OmniRoute/pull/11629)) +- **feat(dashboard):** the `/dashboard/orchestration` snapshot hook now subscribes to the `agents` WebSocket channel (`agent.task.updated`) instead of `requests` as its refetch trigger, and relaxes its background poll from 5s to 30s while that WS connection is up — falling back to the tighter 5s cadence, reprogrammed live on any connect/disconnect transition, whenever the socket is down. ([#12409](https://github.com/diegosouzapw/OmniRoute/pull/12409)) +- **feat(dashboard):** Orchestration canvas quick wins — search box plus state/source/provider filter chips with a one-click clear, and per-source collapse/expand, all reflected in the URL so a filtered/collapsed view is shareable and survives a refresh; the detail drawer gained a "copy trace JSON" action and hardened error/empty-state and accessibility handling; the Agents-tab edges now animate traveling particles along active (running) connections; and the canvas node/edge status colors moved off fixed hex values onto theme-aware `--orch-status-*` CSS custom properties, so they adapt correctly to light/dark mode. ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- **chore(dashboard):** Orchestration UI hardening pass and the missing component/model test coverage it called for — `OrchestratorNode`/`ActivityNode`/`OverflowNode` rendering, the `?node=`/overflow-click page routing, the Agents-tab orchestrator-click no-op and `showCompleted` toggle, and the overview kanban's done-column sort order (#12270, #12271). ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- New `/dashboard/orchestration` page: live unified view of everything running — Cloud Agent, A2A and Conductor as a real-time graph (Agents tab), the combo cascade (Routing tab, reusing the Combo Live Studio) and a state kanban (Overview tab), with a detail drawer (trace, cost, approve/cancel). Read-only over existing APIs — no new backend. Canvas concept credit: PR #11815 design ([#12261](https://github.com/diegosouzapw/OmniRoute/pull/12261)) +- **feat(dashboard):** new "History" tab on `/dashboard/orchestration` — an Airflow-style grid of finished runs over a 24h/7d/30d preset window, one row per (source, identity), clicking a cell opens the existing detail drawer. It is backed by real persistence: A2A task lifecycle transitions are now written to the `a2a_tasks` table (purged after 30 days, configurable via `OMNIROUTE_A2A_HISTORY_RETENTION_DAYS`) and served by the new `GET /api/a2a/tasks/history` listing endpoint, with the task-detail route falling back to persisted history once a run leaves the in-memory snapshot. Conductor runs stay remote and are not persisted locally — the tab says so instead of silently omitting them. ([#12479](https://github.com/diegosouzapw/OmniRoute/pull/12479)) +- **feat(dashboard):** the orchestration detail drawer gained a "Repeat" action for Cloud Agent, A2A and Conductor tasks — a two-click confirm (click once to arm, click again within the confirm window to fire) re-submits the original prompt/input as a new run. The button is disabled with an explanatory tooltip whenever the original input can't be recovered from the loaded task detail (e.g. it never carried a prompt, or the detail failed to load). Two limitations of the A2A variant, by design: it targets the `/a2a` JSON-RPC endpoint, which authenticates with an API key only (`REQUIRE_API_KEY=true` or a configured `OMNIROUTE_API_KEY` makes a dashboard-session repeat answer `HTTP 400` — surfaced verbatim in the drawer's error line, never as a success), and the `message/send` call is SYNCHRONOUS: the POST blocks for the whole skill run, so the success confirmation only appears once the repeated task finishes. A dashboard-authenticated A2A creation path is deliberately left to a follow-up — widening the endpoint's auth posture is an operator decision, not a side effect of this feature. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(a2a):** A2A task execution now records which memories were consulted for the task's last user message as `metadata.memoryHits` (id/key/type/content-snippet) plus a `memory_hits` history event, purely for observability — the retrieved memory is never injected into a skill's prompt or behavior. Gated by the `OMNIROUTE_A2A_MEMORY_HITS` kill-switch (default enabled; set to `0` to skip the recall lookup entirely). The drawer's new "Memory used" section lists these hits for a2a tasks and is omitted whenever there are none. Known limitation: recall only resolves under the keyless posture — a keyed caller's task owner is a SHA-256 prefix of the API key, while memory rows are keyed by the database api-key id, and no hash→id lookup exists today, so the hit list stays empty for keyed callers. The recorded hits are also kept out of the task's own `input` (and therefore out of the persisted input and of the "Repeat" request body), so repeating a task never re-sends the previous run's memory snippets. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(providers):** add a Perplexity Agent API provider (`perplexity-agent` / `pplx-agent`) for Perplexity `/v1/responses`, including the documented Anthropic, OpenAI, Google, xAI, DeepSeek, Z.AI, Moonshot/Kimi, NVIDIA, and Perplexity model IDs plus Anthropic-model `max_output_tokens` compatibility. ([#12103](https://github.com/diegosouzapw/OmniRoute/pull/12103)) — thanks @AIB1TAL0S +- **feat(providers):** add RPD (Requests Per Day) limit to provider rate limit overrides across UI, schemas, DB, and i18n ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) — thanks @b3nw +- **feat(providers): add UC Direct (uncensored.com Developer API), the metered OpenAI-compatible surface.** A standard OpenAI-compatible passthrough (default executor) for uncensored.com's official REST API at `https://api.uncensored.com/api/v1`: `X-api-key` auth (never-expiring `uai_sk_live_` key), `POST /chat/completions` with streaming SSE and native tool-calling, and the full live metered catalog (82 models across 15 providers, discovered from the public `GET /v1/models`). Registered as provider `uc-direct` (alias `ucd`). Complements the un-metered `uc` persona provider — same models, metered credits and a plain API key instead of a subscription session. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(providers): add UC (uncensored.com), the un-metered subscription "persona" chat as an OpenAI-compatible provider.** A WebSocket web-app port: a durable Clerk credential mints a short-lived session token per connect (browserless — no API key), driving UC's persona socket. Ships the browserless email-code login (request → verify → harvest), the 19 verified persona models (Claude Opus, Gemini, Grok, GLM, Kimi, DeepSeek, MiniMax, incl. the uncensored variants), prompted `` tool-calling with a per-model code-style dialect + auto-cure retry for guardrailed models, live ``/reasoning split, streaming + non-streaming OpenAI responses, and full quota/auth error surfacing (paywall / message-limit / rate-limit → 429, invalid session → 401 re-login). Full multimodal parity via the persona blob-upload layer: **vision** (image input, 15 vision-capable models), **document RAG** (PDF/doc upload, server-side extraction), **image generation** (22 models), **video generation** (14 models, async signed-url → poll), and **TTS** (streaming MP3). Registered as provider `uc` (alias `ucn`). The metered OpenAI-compatible Developer API is a separate `uc-direct` provider. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(logs):** add copy-all button to request detail modal ([#11083](https://github.com/diegosouzapw/OmniRoute/pull/11083)) — thanks @AndrianBalanescu +- **feat(check):** make serviceKinds required and add the reverse-walk provider consistency gate ([#11392](https://github.com/diegosouzapw/OmniRoute/pull/11392)) — thanks @Tushar49 +- **feat(providers):** only advertise verified-working connections by default ([#11448](https://github.com/diegosouzapw/OmniRoute/pull/11448)) — thanks @alltomatos +- **feat(resilience):** add hierarchical concurrency admission ([#11493](https://github.com/diegosouzapw/OmniRoute/pull/11493)) +- **feat(providers):** derive + surface expiry for JWT-bearing web cookies (#11497) ([#11505](https://github.com/diegosouzapw/OmniRoute/pull/11505)) — thanks @oyi77 +- **feat(tunnels):** support named cloudflared tunnels via CLOUDFLARED_CONFIG ([#11561](https://github.com/diegosouzapw/OmniRoute/pull/11561)) — thanks @ggiak +- **feat(compression):** make proactive context-compression threshold a live setting ([#11564](https://github.com/diegosouzapw/OmniRoute/pull/11564)) — thanks @ragnar-claude +- **feat(resilience):** add provider peak-hour protection ([#11622](https://github.com/diegosouzapw/OmniRoute/pull/11622)) — thanks @Neuron-Mr-White +- **feat(providers):** add 1min.ai provider ([#11631](https://github.com/diegosouzapw/OmniRoute/pull/11631)) — thanks @zero-executioner +- **feat(dashboard):** collapsible JSON tree viewer for request/response payloads ([#11703](https://github.com/diegosouzapw/OmniRoute/pull/11703)) — thanks @hartmark +- **feat(routing):** add LiquidAI LFM2.5-2.6B free tier via OpenRouter ([#11752](https://github.com/diegosouzapw/OmniRoute/pull/11752)) — thanks @brick30llc-ctrl +- **feat(pricing):** add GLM-5.3-Flash pricing, model specs, and catalog registration ([#11830](https://github.com/diegosouzapw/OmniRoute/pull/11830)) — thanks @AndrianBalanescu +- **feat(routing):** add relayMode for schema-locked context handoffs ([#11839](https://github.com/diegosouzapw/OmniRoute/pull/11839)) — thanks @raheemuddin786 +- **feat(config):** add support for runtime system prompt configuration and hot-reloading ([#11841](https://github.com/diegosouzapw/OmniRoute/pull/11841)) — thanks @raheemuddin786 +- **feat(routing):** expand connection-aware quota prefilter across combo strategies (#11682) ([#11850](https://github.com/diegosouzapw/OmniRoute/pull/11850)) — thanks @HouMinXi +- **feat(models):** reactive discovery sync after Antigravity model-not-found 404 ([#11888](https://github.com/diegosouzapw/OmniRoute/pull/11888)) — thanks @MumuTW +- **feat(models):** auto-aliases and token limits from synced Antigravity discovery ([#11889](https://github.com/diegosouzapw/OmniRoute/pull/11889)) — thanks @MumuTW +- **feat(settings):** raise sticky round-robin limit caps to 1000 ([#12015](https://github.com/diegosouzapw/OmniRoute/pull/12015)) — thanks @adivekar-utexas +- **feat(resilience):** operator-configurable global credential health check interval ([#12043](https://github.com/diegosouzapw/OmniRoute/pull/12043)) — thanks @adivekar-utexas +- **feat(quota):** use official OpenCode Go usage API ([#12124](https://github.com/diegosouzapw/OmniRoute/pull/12124)) — thanks @ddarkr +- **feat(resilience):** default the credential health check sweep to 60 minutes ([#12138](https://github.com/diegosouzapw/OmniRoute/pull/12138)) — thanks @adivekar-utexas +- **feat(quality):** new-code mode for the complexity and dead-code ratchets (Clean as You Code) ([#12142](https://github.com/diegosouzapw/OmniRoute/pull/12142)) +- **feat(dashboard):** orchestration canvas — unified model + snapshot hook (part 1/2) ([#12156](https://github.com/diegosouzapw/OmniRoute/pull/12156)) +- **feat(usage):** add Kilo Code balance and Kilo Pass quotas ([#12178](https://github.com/diegosouzapw/OmniRoute/pull/12178)) — thanks @killer30001000 +- **feat(providers):** refresh vendored ChatGPT Web connector to v4.0.7 ([#12181](https://github.com/diegosouzapw/OmniRoute/pull/12181)) — thanks @backryun +- **feat(providers):** manual "Clear cooldown" action in the cooling panel ([#12224](https://github.com/diegosouzapw/OmniRoute/pull/12224)) — thanks @patrykkopycinski +- **feat(providers):** restore ChatGPT Web via clean-room browser transport ([#12239](https://github.com/diegosouzapw/OmniRoute/pull/12239)) — thanks @backryun +- **feat(sse):** wire the PROVIDER_PROFILES window gate into the global provider cooldown ([#12247](https://github.com/diegosouzapw/OmniRoute/pull/12247)) +- **feat(providers):** modernize CLOVA Studio chat and embeddings ([#12277](https://github.com/diegosouzapw/OmniRoute/pull/12277)) — thanks @backryun +- **feat(browser):** adopt Obscura as primary headless browser engine with Chromium fallback ([#12286](https://github.com/diegosouzapw/OmniRoute/pull/12286)) — thanks @opensource-elearning +- **feat(sse):** full language parity for output styles (es/de/fr/it/ru/zh + autoDetect) ([#12289](https://github.com/diegosouzapw/OmniRoute/pull/12289)) +- **feat(quality):** complete test:scoped — --full map rebuild, stdin selection, CI loader parity (#8084 D1) ([#12353](https://github.com/diegosouzapw/OmniRoute/pull/12353)) +- **feat(providers):** refresh Fable, Cursor, and Devin catalogs ([#12367](https://github.com/diegosouzapw/OmniRoute/pull/12367)) — thanks @backryun +- **feat(video):** redact transcript text from logs and durable memory (#12150 P1) ([#12427](https://github.com/diegosouzapw/OmniRoute/pull/12427)) +- **feat(dashboard):** parent-link, genuine-continuation badge, and modal perf fixes ([#12448](https://github.com/diegosouzapw/OmniRoute/pull/12448)) — thanks @hartmark +- **feat(dashboard):** adaptive context-budget dial on compression panel ([#12488](https://github.com/diegosouzapw/OmniRoute/pull/12488)) — thanks @HouMinXi +- **feat(providers):** refresh Z.ai Web models and browser transport ([#12524](https://github.com/diegosouzapw/OmniRoute/pull/12524)) — thanks @backryun +- **feat(video):** redact raw client-snapshot transcript fields in the detailed log (#12150 P2) ([#12528](https://github.com/diegosouzapw/OmniRoute/pull/12528)) +- **feat(providers):** refresh NVIDIA hosted models ([#12538](https://github.com/diegosouzapw/OmniRoute/pull/12538)) — thanks @backryun +- **feat(quota):** Moonshot Open Platform balance and TPD lock for custom nodes ([#12590](https://github.com/diegosouzapw/OmniRoute/pull/12590)) — thanks @HouMinXi +- **feat(video):** redact transcript in the in-memory pending-request snapshot (#12430 item 6) ([#12596](https://github.com/diegosouzapw/OmniRoute/pull/12596)) +- **feat(dashboard):** link a log entry's Conversation Context to its owning conversation ([#12646](https://github.com/diegosouzapw/OmniRoute/pull/12646)) — thanks @hartmark +- **feat(catalog):** eligibility-gated free-tier bucket ([#12669](https://github.com/diegosouzapw/OmniRoute/pull/12669)) +- **feat(db):** fail-closed previous_response_id continuation for redacted video turns (#12150 P2b) ([#12707](https://github.com/diegosouzapw/OmniRoute/pull/12707)) +- **feat(gamification): pay the documented `streak_bonus` and `badge_unlock` XP rewards.** `XP_REWARDS` listed both rewards but the award pipeline never paid them: the private reward table in `events.ts` omitted them, `updateStreak()` did not report when a streak extended, and badge unlocks carried no XP. Every request that extends a daily streak now pays `streak_bonus × streak length` once per UTC day (guarded by a same-day `xp_audit_log` check), and every badge unlocked through the pipeline pays `badge_unlock` once per badge (guarded by the `user_badges` primary key; `unlockBadge()` now reports whether it inserted). Bonus XP flows through the same `addXp` + level sync + global/weekly/monthly leaderboard path as action XP, so level-ups and rankings include it. The Radar supporter recognition unlock stays XP-free. (#12522 — thanks @pacocartones) +- **feat(providers):** advertise a `free-tier` capability in the provider plugin manifest for every provider with documented free models, so sidecars and dashboards can filter free-capable providers without reading the quota catalog ([#12786](https://github.com/diegosouzapw/OmniRoute/pull/12786)) — thanks @maxmad64bis +- **feat(providers):** Added EURouter as an OpenAI-compatible API-key gateway (`https://api.eurouter.ai/v1`), with live model discovery via `passthroughModels`. Its copy states that models are served by third-party upstreams listed per model, so an EU-based router is not read as EU data residency for inference. ([#13025](https://github.com/diegosouzapw/OmniRoute/pull/13025)) — thanks @ntdatt812 +- **feat(providers):** Added GreenPT as an OpenAI-compatible API-key provider (`https://api.greenpt.ai/v1`), with live model discovery via `passthroughModels`. No free-inference badge: the published docs describe a free API subscription billed per token, not a free tier. ([#13024](https://github.com/diegosouzapw/OmniRoute/pull/13024)) — thanks @ntdatt812 +- **feat(i18n):** 9 new locales — Greek (`el`), Croatian (`hr`), Serbian (`sr`), Lithuanian (`lt`), Estonian (`et`), Latvian (`lv`), Slovenian (`sl`), Maltese (`mt`), Irish (`ga`) — across the dashboard, docs mirrors, CLI, README and the site; OmniRoute now ships every official EU language (51 locales). (#13044) +- **feat(i18n):** 8 new locales — Kannada (`kn`), Malayalam (`ml`), Odia (`or`), Punjabi (`pa`), Nepali (`ne`), Sinhala (`si`), Burmese (`my`), Khmer (`km`) — across the dashboard, docs mirrors, CLI, README and the site (59 locales). The translator now restores the ICU literal escape around angle placeholders and splits oversized docs sections before translating. (#0000) ([#13660](https://github.com/diegosouzapw/OmniRoute/pull/13660)) +- feat(providers): **list Agnes 3.0 Flash as the current free chat model, drop retired 1.5 Flash, add Image 2.0/2.5 Flash plus Video 2.5/2.5 Flash, and discover the live `/v1/models` catalog (including the CN host `api.agnes-ai.cn`).** `agnes-1.5-flash` now forwards to `agnes-3.0-flash`. Video 2.5 polls `GET /v1/videos/{id}` (not the V2.0 `/agnesapi` contract). Live `/v1/models` (2026-09-09) no longer serves 1.5; the wiki marks it deprecated. 3.0 Flash is 512K context / 65,536 max output, same window as 2.5. CN-region keys use the existing per-connection base-URL field, default stays `apihub.agnes-ai.com`. ([#13120](https://github.com/diegosouzapw/OmniRoute/pull/13120)) — thanks @HouMinXi +- **feat(sse):** Codex and OpenAI catalogs list GPT-6 Astra with effort aliases (`-low` through `-ultra`); Codex CLI identity pins `@openai/codex@0.153.4` in lockstep with the image ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **feat(dashboard):** the orchestration History tab gained a "Compare runs" mode — toggling it turns each grid cell into a 2-item selection queue (a 3rd click drops the oldest pick), and picking two cells opens a side-by-side comparison panel instead of the usual detail drawer. The panel fetches both runs' detail the same way the drawer does (falling back to persisted history once a run leaves the live TTL window) and shows, per side: identity/source/state, start time, a signed `right - left` delta for duration/cost/event count, the event timeline aligned by index, and any memory hits. One side's fetch failing never blocks the other, and a delta is only ever computed when both sides have a finite value — otherwise it renders "—", never `NaN`. Comparing two runs from different sources or skills still works; a banner marks the deltas as informational rather than hiding them, since the two runs aren't a strict apples-to-apples pair. ([#12677](https://github.com/diegosouzapw/OmniRoute/pull/12677)) +- **feat(dashboard):** badge a conversation that never reached a clean stop ([#12717](https://github.com/diegosouzapw/OmniRoute/pull/12717)) — thanks @hartmark +- **feat(release):** reconcile-changelog tool + version-anchored fragment aggregation ([#12987](https://github.com/diegosouzapw/OmniRoute/pull/12987)) +- **feat(mcp):** carry context_length through MCP list_models_catalog projection (#12776) ([#13406](https://github.com/diegosouzapw/OmniRoute/pull/13406)) — thanks @KooshaPari +- **feat(cli):** make startup readiness budget configurable (#13369) ([#13433](https://github.com/diegosouzapw/OmniRoute/pull/13433)) — thanks @KooshaPari + ### 🐛 Bug Fixes +- **security(streaming):** sanitize generic mid-stream error messages before emitting OpenAI, + Responses, or Claude SSE failure frames and before diagnostic logging, while preserving raw + failures for internal classification and keeping client disconnects out of provider failure state. ([#12457](https://github.com/diegosouzapw/OmniRoute/pull/12457)) +- **fix(claude):** `blockExtraUsage: false` no longer lets 5h quota preflight skip the account; extra usage is billed after the session bar is gone, so the request must reach Anthropic ([#12803](https://github.com/diegosouzapw/OmniRoute/pull/12803)) — thanks @HouMinXi +- Sanitize HuggingChat conversation-creation and message-send transport failures before they reach client error bodies or provider logs. ([#12467](https://github.com/diegosouzapw/OmniRoute/pull/12467)) +- **fix(dashboard):** Keep local and theme-aware provider SVG icons at a definite layout size so Chromium does not collapse them to 0×0 after the v3.8.50 image-rendering change ([#12054](https://github.com/diegosouzapw/OmniRoute/pull/12054)) — thanks @ponkcore +- **fix(security):** Sanitize provider and runtime failures before public API, SSE and MCP responses and before persistent request, proxy and usage logs, preventing credentials, stack traces and host filesystem paths from crossing those boundaries while preserving stable error codes and useful diagnostics. ([#12506](https://github.com/diegosouzapw/OmniRoute/pull/12506)) +- **fix(cli):** `omniroute tunnel create` no longer crashes with `Cannot read properties of undefined (reading optsWithGlobals)` — removed the duplicate positional argument that caused Commander.js to misalign the action callback parameters ([#12295](https://github.com/diegosouzapw/OmniRoute/issues/12295)) ([#12368](https://github.com/diegosouzapw/OmniRoute/pull/12368)) — thanks @gonisulaimann +- Stop advertising Gemini Live-only models as supported audio endpoints until OmniRoute proxies the bidirectional Live protocol. ([#11710](https://github.com/diegosouzapw/OmniRoute/pull/11710)) — thanks @RaviTharuma +- **fix(kie):** reroute `flux/kontext` off the KIE Market `createTask` flow — it is catalogued with `isMarket: true` but has no Market catalog page, so KIE rejected it with "model name not supported"; it now hits the dedicated `POST /api/v1/flux/kontext/generate` / `GET /api/v1/flux/kontext/record-info` endpoints instead (#11296). ([#11985](https://github.com/diegosouzapw/OmniRoute/pull/11985)) — thanks @hartmark +- **fix(kie):** correct 12 more KIE Market catalog ids that were sent to `createTask` unchanged but diverge from KIE's documented upstream `model` values — GPT Image 2 T2I/I2I (drops the `gpt/` prefix), GPT Image 1.5 T2I/I2I (`gpt-image/` namespace), Seedream 5.0 Lite T2I/I2I (drops the `.0`), all 4 Flux 2 variants (`flux-2/` namespace, generic variant renamed `flex`), and Wan 2.7 Image / Image Pro (dash instead of dot) — each verified individually against the literal example request published on docs.kie.ai. `#11326`'s "everything else already matches" claim was wrong a second time (#11296); `z-image/4.0-*`/`z-image/4.5-*` and `flux/kontext` remain open, documented as unresolved in `KIE_MARKET_UPSTREAM_MODEL_IDS`'s comment pending further verification. ([#11667](https://github.com/diegosouzapw/OmniRoute/pull/11667)) +- **fix(dashboard):** `useApiKeySave.handleSaveApiKey` no longer forces a full upstream `/models` catalog sync on every non-curated provider connection save — callers can now pass `skipModelSync: true` to opt out, so a workflow that only wants to add one manual model no longer floods the provider's available-models list with hundreds/thousands of synced entries. The flag is a client-side intent signal only and is stripped before the connection payload is POSTed to `/api/providers`; default behavior (full sync on save) is unchanged when the flag is omitted (#11324) ([#11664](https://github.com/diegosouzapw/OmniRoute/pull/11664)) +- **Provider connections:** keep `tokenExpiresAt` when a connection is created. The create-path allowlist omitted it, so every insert stored NULL and the dashboard token badge could read a fresh connection as expired until its first background refresh ([#11368](https://github.com/diegosouzapw/OmniRoute/pull/11368)). — thanks @ntdatt812 +- **fix(combos):** the combo builder's precision-select, global-model-search, and manual-entry flows now serialize a model step's `model` string using the provider's already-computed routing-alias prefix (e.g. `oc/`) instead of rebuilding it from the raw canonical `providerId`, fixing the no-auth "OpenCode Free" provider (`opencode`) being routed to the unrelated paid "OpenCode Zen" provider (`opencode-zen`) because `opencode` doubles as a manual routing-prefix override ([#11433](https://github.com/diegosouzapw/OmniRoute/issues/11433)). ([#11670](https://github.com/diegosouzapw/OmniRoute/pull/11670)) +- **fix(radar):** the catalog feed cache now keeps `generatedAt`, the date the feed's data was built, next to `fetchedAt`, the date this install downloaded it (#11435). The feed schema requires that date and the sync path validates it, but the cache dropped it — so a feed fetched minutes ago and one carrying weeks-old figures looked identical to everything downstream, including the dashboard's "Last fetched" line. `getRadarCatalog().meta` and `GET /api/radar/status` now report both dates, the latter as its own field rather than folded into `version` — and omitted entirely for the offers and intel caches, which keep no build date, where a `null` would read as "unknown" rather than "never stored". The dashboard still shows only the fetch time; surfacing the build date there needs a new translated label and is left to a follow-up. Rows cached before migration 163 read back as `null`: unknown stays unknown instead of borrowing the fetch time. The referrals cache has persisted the same date since migration 142. — thanks @maxmad64bis +- **fix(cli):** postinstall no longer fills `JWT_SECRET` and `API_KEY_SECRET` in the installed package's `.env` (#11436). `.env.example` ships both blank on purpose: the server restores them from its durable store, or generates and persists them there, in `ensureSecrets()`. Pre-filling them defeated that — the file lives inside the package directory, so `npm i -g` replaced it and postinstall wrote _different_ values, while `ensureSecrets()` (which only acts on an empty variable) never got to restore the real ones. Both secrets rotated silently on every update, invalidating dashboard sessions and API-key CRCs. `STORAGE_ENCRYPTION_KEY` left the same list for the same reason in #1622; its comment pointed at a function that no longer exists and now names the real provisioning path. — thanks @maxmad64bis +- **fix(cli):** the CLI now says when a loaded `.env` lives inside the installed package directory (#11437). It already announces every env file it reads, without distinguishing the ones that survive an update from the one that does not: `npm i -g` replaces the package directory wholesale, so values set there are gone at the next update, silently. The warning names the durable path to move them to, and fires only when that file actually supplied a value — a file entirely shadowed by a durable one supplied nothing. A development checkout stays silent: there the same path is stable and documented in `SETUP_GUIDE.md`. — thanks @maxmad64bis +- **fix(free-models):** the shared `isFreeModel()` predicate no longer reports catalog entries marked `freeType: "discontinued"` as free, so `hidePaidModels` can't route to Pollinations' seven premium models that now require a paid key ([#11441](https://github.com/diegosouzapw/OmniRoute/pull/11441)) — thanks @maxmad64bis +- fix(ui): let AnySearch use the normal provider-icon fallback when LobeHub has no matching icon (#11449) +- Fixed the v3.8.50 Costs and Analytics dashboards so flat-rate Claude Code usage can be shown as an explicitly requested token-price estimate without changing default billed-cost semantics. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed archived usage retention so each request is priced individually instead of pricing a day's summed tokens once, which understated archived cost whenever a day mixed cache-heavy and ordinary requests. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed the Costs dashboard so it discloses when displayed figures include flat-rate token-price estimates instead of labelling them as billed spend, using the flag the analytics API already returns; the month-end projection and the CSV/JSON exports carry the same marker, and billed-cost mode is unchanged. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- **fix(combo):** attach the same combo-diagnostics trace (`poolSize`/`attemptOrder`/`excluded`/`terminalReason`, plus `x-omniroute-combo-*` headers) to the round-robin strategy's and the nested pipeline/fusion runtime-unit loop's "Maximum combo retry limit reached" 503 that the priority-strategy path already attaches for the identical terminal condition — previously those two paths returned a bare, contextless 503 ([#11462](https://github.com/diegosouzapw/OmniRoute/issues/11462)). ([#11665](https://github.com/diegosouzapw/OmniRoute/pull/11665)) +- **fix(bun):** use native `bun:sqlite` in `bootstrap-env` and `sync-env` to avoid loading `better-sqlite3` N-API addon during Bun startup ([#11468](https://github.com/diegosouzapw/OmniRoute/pull/11468)) — thanks @TheDemonTuan +- **fix(docker-bun):** make `Dockerfile.bun` install reliable with pre-install script helpers and native `bun:sqlite` smoke check ([#11470](https://github.com/diegosouzapw/OmniRoute/pull/11470)) — thanks @TheDemonTuan +- **fix(docker-bun):** remove every vendored `better-sqlite3` native addon from the Bun runtime image so startup cannot abort before the `bun:sqlite` fallback ([#11482](https://github.com/diegosouzapw/OmniRoute/pull/11482)) — thanks @TheDemonTuan +- **fix(autoCombo):** effort/alias model variants (`gpt-5.6-sol-xhigh`, `gpt-5.6`, cursor's `claude-4.6-opus-high`) inherit their base model's task fitness instead of falling to the wildcard 0.5 ([#11492](https://github.com/diegosouzapw/OmniRoute/pull/11492)) — thanks @MumuTW +- fix(db): rate-limit repeated Arena ELO leaderboard fetch-failure warnings instead of logging one per sync attempt (#11500) ([#11989](https://github.com/diegosouzapw/OmniRoute/pull/11989)) — thanks @hartmark +- **fix(db):** dedupe the raw `[Encryption] Decryption failed...` log line emitted by the lazy-decrypt views (`createLazyRowProxy`/`createLazyConnectionView`), which power `getProviderConnections()` and were re-triggering that line on every CredentialHealth/model-sync cycle for the same corrupt or stale-key credential — a fresh Proxy over a fresh row on every cycle meant the per-proxy memoization never suppressed it, unlike the dedup `decryptConnectionFields()` already had since [#9927](https://github.com/diegosouzapw/OmniRoute/issues/9927). Now shares that dedupe tracking so the line logs at most once per credential ([#11500](https://github.com/diegosouzapw/OmniRoute/issues/11500)). ([#11678](https://github.com/diegosouzapw/OmniRoute/pull/11678)) +- **fix(intelligence):** Arena ELO sync stores a synthesized base-model row for every effort/harness variant (`claude-opus-5-max` → `claude-opus-5`, `gpt-5.6-sol-xhigh (codex-harness)` → `gpt-5.6-sol`), so bare model requests reach the synced intelligence layer instead of the static fallback table; the hand-maintained `MODEL_ALIAS_MAP` — which collapsed generations (`gpt-5.5` → `gpt-5`) — is gone ([#11506](https://github.com/diegosouzapw/OmniRoute/pull/11506)) — thanks @MumuTW +- **fix(autoCombo,sse):** catalog hygiene — the static task-fitness table no longer ranks retired models above live ones (dead rows dropped, patterns matched on segment boundaries), the 7 deprecation aliases that forwarded to retired or non-existent models now point at the vendors' published replacements, an alias is no longer rewritten when the serving provider offers the id as-is, and a new `check:model-lifecycle` gate diffs both tables against a vendor lifecycle snapshot ([#11507](https://github.com/diegosouzapw/OmniRoute/pull/11507)) — thanks @MumuTW +- **fix(sse):** stop dropping resolved `thoughtSignature` values on parallel (multi tool-call) turns sent to Gemini 3.x — the claude→gemini and openai→gemini translators previously kept the signature only on the *first* function call of a message, causing Gemini to reject subsequent calls in the same turn with HTTP 400 "Function call is missing a thought_signature"; each function call now keeps its own resolved signature ([#11510](https://github.com/diegosouzapw/OmniRoute/issues/11510)). ([#11669](https://github.com/diegosouzapw/OmniRoute/pull/11669)) +- Treat Docker publishing from a non-default release branch as an expected skip instead of a failed workflow, while preserving the guard that prevents it from overwriting the `next` channel. ([#11527](https://github.com/diegosouzapw/OmniRoute/pull/11527)) — thanks @pacocartones +- fix(search): prefer credentialed providers over duckduckgo-free fallback (#11524) — the fallback-only loop ran before the credentialed-providers loop in `executeWebSearch`, making configured providers unreachable when `duckduckgo-free` was available. ([#11565](https://github.com/diegosouzapw/OmniRoute/pull/11565)) — thanks @vsd2807 +- **fix(sse):** cap the upstream headers-wait phase for STREAMING requests to a client-realistic ceiling (110s, under Codex's own ~120s hard client-abort window) instead of the flat 10-minute `FETCH_TIMEOUT_MS` default — that default was 5x longer than the body-phase readiness watchdog's own adaptive bound, so a request whose upstream never returned any response at all (not even headers, e.g. a stalled NVIDIA target behind a tool-heavy Responses→Chat translation) kept the client connection alive on keepalives only, guaranteeing the client's own patience ran out first with an opaque 499 instead of OmniRoute detecting and failing the stall fast. Non-streaming requests are unaffected — they keep the existing flat default (`open-sse/utils/fetchStartTimeoutPolicy.ts`) (#11526) ([#11668](https://github.com/diegosouzapw/OmniRoute/pull/11668)) +- **fix(ci):** protocol-clients E2E harness boots the peer-stamped custom server (`run-next.mjs dev`) instead of the bare `next` CLI, so `LOCAL_ONLY` locality resolves from the real TCP peer stamp and `/api/mcp/audit` answers 200 (open bootstrap preserved via an `OMNIROUTE_E2E_BOOTSTRAP_MODE=open` credential clear in `run-next.mjs`, pinned to loopback `HOST`) instead of a deterministic 403 (#11535) ([#11549](https://github.com/diegosouzapw/OmniRoute/pull/11549)) — thanks @jonlwheat2-gif +- **fix(docs):** the Auto-Combo engine is described with the number of factors it actually declares. Four different counts were in circulation — 6, 9, 13 and 14 across `AGENTS.md`, four guides and six code comments — while `DEFAULT_WEIGHTS` declares 15. `check:docs-counts` now reads that number from the source, so adding a factor without updating the prose fails CI ([#11545](https://github.com/diegosouzapw/OmniRoute/pull/11545)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **fix(quota-share):** in-flight leases are per request, so an aborted request's slot ages out instead of being kept alive by later traffic on the same connection ([#11547](https://github.com/diegosouzapw/OmniRoute/pull/11547)) — thanks @abhisheksharma2411 +- The free-tier summary route now serves the refreshed Radar catalog when the feed is active — the same numbers the dashboard shows — and states which catalog answered plus its real build date, instead of always reporting release-frozen figures with a stale curation date. The supporter-key live feed stays reserved to authenticated callers of the instance. ([#11550](https://github.com/diegosouzapw/OmniRoute/pull/11550)) — thanks @maxmad64bis +- **sse:** fix LiveWS/embed-WS servers crashing at startup under the Node/tsx runtime — `liveServer.ts`, `embedWsProxy.ts` and `apiBridgeServer.ts` imported `@/shared/utils/httpClientAbortGuard` without the `.mjs` extension, so the client-abort crash guard added by [#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556) was unreachable and every dependent test failed with `ERR_MODULE_NOT_FOUND` ([#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556)). ([#11671](https://github.com/diegosouzapw/OmniRoute/pull/11671)) — thanks @jonlwheat2-gif / @yxyxy +- fix(chat): shed any of the matched provider-node's routing identifiers (public prefix or internal id) before model lookup, so composites like `//` no longer reach upstream verbatim (#11557) — thanks @maxmad64bis +- **fix(cache):** a semantic-cache entry now expires on its TTL instead of surviving until the next UTC midnight, and `dbEntries` stops counting expired rows ([#11573](https://github.com/diegosouzapw/OmniRoute/pull/11573)) — thanks @ntdatt812 +- **fix(api):** `/v1/models` schedules its stale-while-revalidate rebuild through Next's `after()` again, so a stale catalog reaches the client before the rebuild blocks the event loop ([#11574](https://github.com/diegosouzapw/OmniRoute/pull/11574)) — thanks @ntdatt812 +- **fix(check):** the `known-symbols` executor conformance gate awaits the now-async `getExecutor()`, so it stops reporting all 142 registered aliases as dead symbols and can detect a lazy import that fails ([#11582](https://github.com/diegosouzapw/OmniRoute/pull/11582)) — thanks @ntdatt812 +- **fix(pack):** register `bin/cli/utils/volatileEnvPath.mjs` in `PACK_ARTIFACT_REQUIRED_PATHS`, so the prune can no longer silently drop a module the published CLI imports on every boot ([#11588](https://github.com/diegosouzapw/OmniRoute/pull/11588)) — thanks @ntdatt812 +- **i18n(vi):** translate the five reliability-column strings on the free-provider rankings page, which rendered as `__MISSING__:` markers in the Vietnamese UI ([#11589](https://github.com/diegosouzapw/OmniRoute/pull/11589)) — thanks @ntdatt812 +- **resilience:** restore the expired-connection retry-budget probe in the token-health sweep — the `!isGitHubAccessTokenOnlyConnection` carve-out reintroduced by #11608 contradicted the boundary pinned by #11592, so a GitHub connection parked at `expired` with retry budget remaining was never probed and could never self-heal ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)). ([#11672](https://github.com/diegosouzapw/OmniRoute/pull/11672)) — thanks @jonlwheat2-gif / @ntdatt812 +- **fix(dashboard):** Provider Stats now displays combo success rates on their native 0–100 percentage scale instead of multiplying them twice ([#11595](https://github.com/diegosouzapw/OmniRoute/pull/11595)) — thanks @pacocartones +- **fix(providers):** route DuckDuckGo Web Chat through the verified `duck.ai` endpoint, refresh its free model catalog, and avoid forwarding unsolved anti-abuse challenges ([#11597](https://github.com/diegosouzapw/OmniRoute/pull/11597)) — thanks @solstxce +- **fix(ui):** Console log Refresh and Copy controls now expose localized accessible names, keep copy actions visible on keyboard focus, and announce copy completion safely ([#11599](https://github.com/diegosouzapw/OmniRoute/pull/11599)) — thanks @pacocartones +- Dropped the stale-`.eslintcache` `restore-keys` fallback from both "Restore ESLint file cache" steps in `ci.yml`, so the blocking `Lint` job can no longer be served per-file verdicts computed under a different lint config, suppressions file or lockfile. `quality.yml` had already dropped it in #11963; `ci.yml` — the workflow that actually gates PRs — had not (#11600). ([#11996](https://github.com/diegosouzapw/OmniRoute/pull/11996)) +- fix(ci): pass `--pass-on-unpruned-suppressions` in `run-eslint-json.mjs` so the CI Lint job no longer fails when a suppression is merely orphaned by a genuine fix, mirroring the identical fix already in `validate-release-green.mjs` (#11600) ([#11983](https://github.com/diegosouzapw/OmniRoute/pull/11983)) — thanks @hartmark +- fix(handoff): enforce provider allowlist for universal handoff (#11602) — universal handoff now skips summarization when the selected summary model's provider is not included in the configured provider allowlist. ([#11619](https://github.com/diegosouzapw/OmniRoute/pull/11619)) — thanks @vsd2807 +- **fix(dashboard):** Show a stable error state when Search Analytics returns an HTTP error or malformed data ([#11603](https://github.com/diegosouzapw/OmniRoute/pull/11603)) — thanks @pacocartones +- **test(gamification):** pin the aggregate profile level to the XP-derived semantics of #11604 — `getAggregateXp()` now derives `currentLevel` from the summed XP (`calculateLevel(sum)`), not `MAX(stored current_level)`, and the #3484 fixture levels are aligned with the XP curve ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)). ([#11676](https://github.com/diegosouzapw/OmniRoute/pull/11676)) — thanks @jonlwheat2-gif / @pacocartones +- **fix(dashboard):** Keep the Profile level and progress aligned with aggregate XP, including bounded handling for invalid totals ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)) — thanks @pacocartones +- **fix(dashboard):** Prevent locked hidden badges from revealing their icon or opening private badge details before they are earned ([#11605](https://github.com/diegosouzapw/OmniRoute/pull/11605)) — thanks @pacocartones +- **fix(dashboard):** Restore keyboard focus after shared modals close and cancel delayed autofocus during cleanup ([#11607](https://github.com/diegosouzapw/OmniRoute/pull/11607)) — thanks @pacocartones +- **fix(dashboard):** Enable Enter and Space activation for clickable data-table rows without hijacking nested controls ([#11610](https://github.com/diegosouzapw/OmniRoute/pull/11610)) — thanks @pacocartones +- **fix(autoCombo,sse):** vendor-retired catalog ids are dropped from the auto-combo candidate pool and no longer win on leftover `arena_elo` / `user_override` rows; `getModelLifecycleDecision` consults `model-lifecycle.json` (prefix-stripped) so aggregator traffic is not `untracked` for ids the snapshot already knows ([#11625](https://github.com/diegosouzapw/OmniRoute/issues/11625)) ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)) — thanks @MumuTW +- **test(chatcore):** move Codex/Claude combo fixtures off models the lifecycle guard now rejects — `gpt-5.1-codex`/`gpt-5-codex` are vendor-retired (snapshot, #11626) and `claude-3-5-sonnet-20241022` is shut down, so native-passthrough and combo-fallback tests switched to `gpt-5.6-sol` and `claude-sonnet-4.6` ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)). ([#11675](https://github.com/diegosouzapw/OmniRoute/pull/11675)) — thanks @jonlwheat2-gif / @MumuTW +- fix(opencode-plugin): stop a free-tier budget that rounds up across a magnitude from rendering as `1000K`/`1000M` in the model picker — `fmtTokens` chose its unit from the raw token count and then rounded with `toFixed(1)`, so 999,950–999,999 printed as `1000K` rather than `1M` and just under a billion printed as `1000M` rather than `1B` ([#11684](https://github.com/diegosouzapw/OmniRoute/pull/11684)) — thanks @abhisheksharma2411 +- Cap prose-derived Antigravity quota resets at the configured model cooldown maximum while preserving authoritative Retry-After headers and Google RetryInfo hints. (#11695) Thanks @pacocartones. ([#11823](https://github.com/diegosouzapw/OmniRoute/pull/11823)) +- Compare `_rebaseline_*` notes structurally in the ratchet-bank verifier so an object-valued note no longer reports itself as rewritten on an unmodified tree — the nightly `bank-ratchet-shrinks` lane had been aborting on every run since 2026-08-11, leaving 41,672 lines of earned file-size shrinks unbanked (#11702). ([#11892](https://github.com/diegosouzapw/OmniRoute/pull/11892)) — thanks @MumuTW +- **fix(build):** `prepublish.ts` bundles the ChatGPT Web (Codex) MCP bridge through `runBuildTool()` instead of spawning `npx.cmd` raw, fixing the build crash on Node ≥ 20/Windows where `.cmd` shims cannot be spawned without a shell (EINVAL) ([#11704](https://github.com/diegosouzapw/OmniRoute/issues/11704)) ([#11706](https://github.com/diegosouzapw/OmniRoute/pull/11706)) — thanks @jonlwheat2-gif +- fix(codex): keep `parallel_tool_calls:false` on the translated Codex Responses Lite path (#11707) ([#11984](https://github.com/diegosouzapw/OmniRoute/pull/11984)) — thanks @hartmark +- **fix(tests):** the new Gemini Web Images retirement test (#11708) called the now-async `getExecutor()` synchronously, throwing `TypeError: getExecutor(...).getProvider is not a function` instead of asserting Gemini Web chat stays available. Awaited to match the R0.3 executor-registry refactor (#11220). ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(search):** Allow REST and MCP web-fetch requests to select the advertised `anysearch-search` provider explicitly ([#11716](https://github.com/diegosouzapw/OmniRoute/pull/11716)) — thanks @pacocartones +- **fix(providers):** Keep overnight peak-hour protection active after midnight according to the window's configured start day ([#11718](https://github.com/diegosouzapw/OmniRoute/pull/11718)) — thanks @pacocartones +- **fix(providers):** correct all five g4f.space gateway descriptions to document proof-of-work credits or member API keys, variable limits, and the remote prompt boundary instead of promising free keyless access at five requests per minute ([#11738](https://github.com/diegosouzapw/OmniRoute/pull/11738)). +- **fix(db):** `model_capabilities` is created by a migration instead of lazily on the first models.dev sync, so a clean install and an upgraded install converge on the same schema regardless of which features have run ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(ci):** `check:install-upgrade` now fails on an `npm` install truncated by ENOSPC (npm reports it as a warning and still exits 0), authenticates its health probe so the version assertion works against the hardened health payload, frees the clean-install tree before the upgrade phase, and no longer reports a schema divergence computed from a boot that never served ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(routing):** harden session-affinity key derivation against oversized explicit IDs and empty structural-payload collisions, while recognizing Gemini and common text request shapes ([#11746](https://github.com/diegosouzapw/OmniRoute/pull/11746)) — thanks @MumuTW +- **fix(packages/browser-pool):** regenerate `package-lock.json` so the `packages/browser-pool` workspace's locked `playwright`/`@types/node` (and transitives) match its `package.json` specs, fixing cache-only/offline installs (`npm ci --offline`, Nix `buildNpmPackage`) that previously failed with `ENOTCACHED` ([#11747](https://github.com/diegosouzapw/OmniRoute/issues/11747)) — thanks @benjaminkitt ([#11784](https://github.com/diegosouzapw/OmniRoute/pull/11784)) — thanks @geekyNads +- **fix(assets):** Stop distributing 79 provider and CLI logos without verified source/license provenance, keep every provider/tool available through the internal generic icon, and prevent those IDs from falling through to an external icon CDN. ([#11750](https://github.com/diegosouzapw/OmniRoute/pull/11750)) +- **fix(providers):** retire the common `chatgpt-web` / `cgpt-web` provider because its pre-key and proof-of-work provenance could not be cleared ([#11754](https://github.com/diegosouzapw/OmniRoute/pull/11754)) — explicit chat and image requests, slash-prefixed models, persisted aliases, proxy overrides, and combo targets now fail closed with a sanitized HTTP `410` / `PROVIDER_RETIRED` before upstream dispatch. Migration 163 tombstones matching connections and invalidates their active leases without deleting history or widening API-key allowlists. The independent `chatgpt-web-codex` / `cgpt-codex` provider and its MIT-noticed vendored implementation remain available. +- **fix(usage):** quota and usage refresh no longer 409 when an exclusive lease reserves the connection ([#11758](https://github.com/diegosouzapw/OmniRoute/pull/11758)) — thanks @TheDemonTuan +- Keep the embedding registry's vector width and `embedding` type on models when a synced model exists for the same id, so `/v1/models` no longer reports registry-described embedding models widthless or untyped (#11761) — thanks @rahilmavani +- Correct `google/gemini-embedding-001` on the OpenRouter route to 3072 dimensions, the width it returns when `dimensions` is not sent (#11761) — thanks @rahilmavani +- **fix(vision-bridge):** Restrict automatic vision routing and fallbacks to models available in authoritative live provider catalogs, including aliases and effort variants ([#11767](https://github.com/diegosouzapw/OmniRoute/issues/11767)) — thanks @pacocartones ([#11813](https://github.com/diegosouzapw/OmniRoute/pull/11813)) +- **fix(providers):** reclassify Cerebras as a one-time $5 signup credit (payment method required, 30-day validity), not a recurring no-card 1M tokens/day trial ([#11773](https://github.com/diegosouzapw/OmniRoute/issues/11773)) ([#12591](https://github.com/diegosouzapw/OmniRoute/pull/12591)) — thanks @HouMinXi +- fix(cli): stop prepublish from re-rebuilding the already-built ESM-only opencode-plugin dist (#11787) ([#11990](https://github.com/diegosouzapw/OmniRoute/pull/11990)) — thanks @hartmark +- **fix(db):** Fresh installs no longer log a non-fatal `no such table: compression_run_telemetry` warning when retention cleanup runs before the lazily-created telemetry table exists ([#11802](https://github.com/diegosouzapw/OmniRoute/pull/11802)) — thanks @RaviTharuma +- fix(sse): set X-OmniRoute-Selected-Connection-Id on successful combo dispatches so downstream consumers stop falling back to an empty connection id (#11810) ([#11986](https://github.com/diegosouzapw/OmniRoute/pull/11986)) — thanks @hartmark +- **fix(services):** embedded CLIProxyAPI lifecycle checks now use public `/healthz`, while model discovery uses the configured dedicated data-plane API key instead of the management password ([#11811](https://github.com/diegosouzapw/OmniRoute/pull/11811)) — thanks @RaviTharuma +- Fix persisted model steps returning an error in the playground route simulator, and warn when structural steps cannot be simulated. ([#11862](https://github.com/diegosouzapw/OmniRoute/pull/11862)) — thanks @pacocartones +- **fix(providers):** Antigravity's dynamic mitmAlias table no longer routes `gemini-3.7-flash-{high,medium,low}` to a literal tier-suffixed upstream id just because one connected account's own discovery listed it directly — those display ids always resolve through the safe `gemini-3.7-flash-tiered` static alias, so one account's Google-provisioned access no longer 404s every sibling account of the provider ([#11824](https://github.com/diegosouzapw/OmniRoute/issues/11824), [#11651](https://github.com/diegosouzapw/OmniRoute/issues/11651)) ([#11988](https://github.com/diegosouzapw/OmniRoute/pull/11988)) — thanks @hartmark +- **fix(dashboard):** Expose Profile loading, errors, page structure, and XP progress to assistive technologies ([#11838](https://github.com/diegosouzapw/OmniRoute/pull/11838)) — thanks @pacocartones +- **fix(socks):** `Agent.connectTimeout` now bounds both SOCKS handshake and TLS connect, and `family === null` no longer falls back to `fetch-socks` ([#11842](https://github.com/diegosouzapw/OmniRoute/pull/11842)) — thanks @maxmad64bis +- **perf(compression):** OOM mitigations for large payload hashing, memoization, and token estimation ([#11844](https://github.com/diegosouzapw/OmniRoute/pull/11844) — thanks @AndrianBalanescu) +- Excluded Next.js Node File Trace manifests (`*.nft.json`) from the published npm tarball. They are build-time metadata and are never read while serving, but had grown to 668.7 MB — 61% of the package — which pushed the upload past the registry limit and made `npm publish` fail with `413 Payload Too Large`. ([#11864](https://github.com/diegosouzapw/OmniRoute/pull/11864)) +- **fix(cli):** keep the global OmniRoute server `--base-url` separate from provider-node `--endpoint` values in add, update, and validate commands ([#11860](https://github.com/diegosouzapw/OmniRoute/pull/11860)) — thanks @pacocartones +- **fix(config):** Nous Research's `Hermes-4-405B` model now displays as "Hermes 4 405B (Nous Research)" in both the provider registry and the free-model catalog, instead of the mislabelled "Hermes 4 7B" ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#11993](https://github.com/diegosouzapw/OmniRoute/pull/11993)) +- **fix(provider/nous):** inject required user= tag into Nous Research inference requests to resolve upstream 400 "missing tags" error ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#12044](https://github.com/diegosouzapw/OmniRoute/pull/12044)) +- **fix(build):** `npm run build` now fails in one second with a named package and a copy-pasteable fix when npm silently drops an externalised optional native dependency, instead of dying four minutes in with `Module not found: Can't resolve 'better-sqlite3'` ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- **fix(install):** `postinstall` no longer throws `ReferenceError: isAndroid is not defined` — failing the whole `npm install` — when the `better-sqlite3` rebuild fallback times out; the manual-fix guidance is reachable again ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- Fixed the Alibaba free-tier allowlist test that went red on its own once the shipped catalog's `validUntil` (2026-08-27) passed, leaving every PR and `main` with a failing `Unit Tests (1/8)`. The test now builds its own packs with dates it controls, and covers the expired-pack fallback that production has actually been serving. ([#11867](https://github.com/diegosouzapw/OmniRoute/pull/11867)) +- Split the npm registry upload into its own GitHub-hosted job. npm refuses `--provenance` from a self-hosted runner (`422 ... Only "github-hosted" runners are supported`), which blocked the v3.8.50 publish; the heavy verification cannot move to a hosted runner, so it now hands the proven tarball over instead. ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(ui):** the shared `DataTable` loading state no longer reads its decorative ⏳ glyph out to assistive technology, and now carries the same `role="status"` / `aria-live="polite"` / `aria-busy="true"` semantics as `PageLoading` ([#11869](https://github.com/diegosouzapw/OmniRoute/pull/11869)) — thanks @pacocartones +- **fix(api):** Bound the `timeout` accepted by `POST /api/providers/[id]/login` to the same 15s–600s window the Adobe Firefly and Conol browser-login paths already enforce, so a request can no longer pin a headful browser and the single active-login slot with an arbitrarily large poll budget. ([#11871](https://github.com/diegosouzapw/OmniRoute/pull/11871)) — thanks @pacocartones +- **fix(api):** Stop the tunnel and MITM routes from returning raw child-process error text, which disclosed host paths, binary install locations and Tailscale `tskey-*` credentials in a body some of these routes serve to non-loopback callers, and return a real 400 instead of a framework 500 when a tunnel request body fails validation. ([#11872](https://github.com/diegosouzapw/OmniRoute/pull/11872)) — thanks @pacocartones +- **fix(open-sse):** stream TTFT/ITL are sampled from a monotonic clock (`performance.now`) instead of `Date.now`, so an NTP correction or wall-clock jump can no longer inflate a genuine TTFT or produce a negative one that the `ttft >= 0` guard silently drops — protecting the router's OTel/EWMA/`usage_history`/speed-ranking signals ([#11873](https://github.com/diegosouzapw/OmniRoute/pull/11873)) — thanks @pacocartones +- **fix(providers):** xAI `reasoning_effort: "xhigh"` now reaches grok-4.6+ instead of being silently clamped to `"high"` ([#11879](https://github.com/diegosouzapw/OmniRoute/pull/11879)) — thanks @NoxzRCW +- **fix(dashboard):** the providers page no longer crashes into the error boundary when a provider id collides with an `Object.prototype` member (`constructor`, `__proto__`); icon lookups are own-property guarded ([#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)) — thanks @NoxzRCW +- **fix(skills):** injected skill tools declared in shorthand (`{"content": "string"}`) now forward valid JSON Schema, unblocking providers that validate tool schemas strictly such as Zhipu GLM on the Console Go tier ([#11881](https://github.com/diegosouzapw/OmniRoute/pull/11881)) — thanks @NoxzRCW +- **fix(api):** `POST /api/playground/simulate-route` now surfaces `combo-ref` and `provider-wildcard` persisted combo steps with a specific warning (naming the referenced combo, or the unresolved `provider/model` wildcard) instead of folding them into a generic "unsupported step" count; a `provider-wildcard` step is also now included as an unresolved target so the operator can see it is in the route (ported from [#11882](https://github.com/diegosouzapw/OmniRoute/pull/11882) — thanks @NoxzRCW). ([#11930](https://github.com/diegosouzapw/OmniRoute/pull/11930)) +- **fix(translator):** the streaming OpenAI→Claude translator keeps upstream usage, including prompt-cache tokens, when it arrives on a trailing `choices: []` chunk (Fireworks and any upstream using `stream_options.include_usage`) ([#11883](https://github.com/diegosouzapw/OmniRoute/pull/11883)) — thanks @NoxzRCW +- **fix(cli):** `omniroute update --apply` now tells you explicitly whether a running server was detected and, if so, that you must run `omniroute restart` to apply the update — it never restarted anything and previously implied the update was already live once files were installed. The dashboard's npm-mode Update flow (`/api/system/version`) now tries OmniRoute's own PID-file-managed supervisor before falling back to pm2, and reports an honest "restart required" step instead of a silent pm2-only "skipped" that read like a completed update. The server-side latest-version lookup backing the dashboard's update banner also gained `--prefer-online`, closing the same stale-npm-cache class already fixed in the CLI's own copy for #4376 ([#11885](https://github.com/diegosouzapw/OmniRoute/issues/11885)). ([#12005](https://github.com/diegosouzapw/OmniRoute/pull/12005)) +- **fix(resilience):** clear persisted LKGP pins when a target suffers connection/provider exhaustion or is skipped before dispatch due to cooldown/exhaustion/unavailability, preventing subsequent requests from repeatedly prioritizing known-dead providers ([#11911](https://github.com/diegosouzapw/OmniRoute/issues/11911)). ([#12013](https://github.com/diegosouzapw/OmniRoute/pull/12013)) — thanks @HouMinXi +- **fix(sse):** the OpenAI→Claude stream translator now defers the terminal `message_delta`/`message_stop` emission until the real usage block has arrived (or a genuine end-of-stream flush forces it) instead of emitting it immediately on `finish_reason` — previously, when the trailing usage-only chunk (`{"choices":[],"usage":{...}}`) arrived *after* the `finish_reason` chunk (the normal order for Fireworks/vLLM/Together and other `stream_options.include_usage` upstreams), the client-visible `message_delta` still carried stale/zero usage even though `state.usage` was internally corrected too late to matter (ported from [#11915](https://github.com/diegosouzapw/OmniRoute/pull/11915) — thanks @HouMinXi). ([#11933](https://github.com/diegosouzapw/OmniRoute/pull/11933)) +- **fix(sse):** `stripStore()` now forces `store=false` for stateless OpenAI-compatible Responses-API targets (unless the connection has explicitly opted in via `providerSpecificData.openaiStoreEnabled`), instead of only handling the `openai`/`agentrouter` cases — a client-supplied `store` value previously passed through untouched to backends that don't actually persist responses server-side ([#11916](https://github.com/diegosouzapw/OmniRoute/pull/11916) — thanks @HouMinXi). +- **fix(models):** custom provider-node models (synced, custom, and alias-backed) now appear under their configured prefix in the unified catalog when the operator's model-id prefix mode is canonical, instead of being dropped whenever alias-inclusion was otherwise disabled ([#11918](https://github.com/diegosouzapw/OmniRoute/pull/11918) — thanks @HouMinXi). +- **fix(models):** the unified model catalog now suppresses stale static registry models (including effort-tier variants) for any provider whose active connection has an authoritative live synced catalog, not only providers already using exclusive-synced-listing — a connection with `providerUsesAuthoritativeLiveCatalog` previously kept serving both the live-synced models and the stale static rows side by side ([#11919](https://github.com/diegosouzapw/OmniRoute/pull/11919) — thanks @HouMinXi). +- **fix(ui):** `ProviderIcon`'s three remaining unguarded lookups (`PROVIDER_ICON_ALIASES`, `LOCAL_SVG_ALIASES`, `THEMED_SVGS`) now use `Object.hasOwn()` own-property checks — a provider id such as `constructor` or `__proto__` previously resolved through the prototype chain instead of falling through to the unknown-provider CDN fallback (`getLobeProviderIcon()` itself was already guarded by [#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)); `ProviderPageHeader`'s `color` field is also now optional, matching the rest of the component's defensive typing (ported from [#11920](https://github.com/diegosouzapw/OmniRoute/pull/11920) — thanks @HouMinXi). ([#11935](https://github.com/diegosouzapw/OmniRoute/pull/11935)) — thanks @NoxzRCW +- fix(ollama): preserve multi-byte UTF-8 content split across stream chunks in the Ollama NDJSON transform, which previously corrupted CJK/emoji into U+FFFD (#11921) — thanks @pacocartones +- **fix(providers):** OrcaRouter chat requests now target `/v1/chat/completions` instead of the bare `/v1` API root, fixing the upstream `404 Invalid URL (POST /v1)` ([#11923](https://github.com/diegosouzapw/OmniRoute/pull/11923)). — thanks @echel0nn +- **fix(plugins):** deliver the `onStreamComplete` event to disk-installed plugins. The event shipped in v3.8.50 (#9669) was emitted internally but had no plugin-facing wiring, so no plugin could ever subscribe: the manifest schema silently dropped `hooks.onStreamComplete`, and the loader/manager only knew the seven legacy hooks. `onStreamComplete` is now a declarable manifest hook, wired through the loader and registered by the manager like the other hooks, and its payload carries a `requestId` so consumers can correlate the stream-completion event with the originating request ([#11934](https://github.com/diegosouzapw/OmniRoute/pull/11934)) — thanks @amaleta / @pacocartones +- **fix(db):** the Qdrant embedding-model dropdown now lists local/self-hosted providers (Ollama, LM Studio, vLLM, etc.) — an active connection is treated as "configured" when the provider allows an optional API key, not only when it has a real key or OAuth, so a running local embedding provider is no longer hidden from the picker ([#11949](https://github.com/diegosouzapw/OmniRoute/issues/11949)) ([#11995](https://github.com/diegosouzapw/OmniRoute/pull/11995)) +- **fix(providers):** Vertex AI Anthropic partner-model discovery now calls the Model Garden `v1beta1` publisher list (`/v1beta1/publishers/anthropic/models`, global) and parses its `publisherModels` envelope, so Claude models auto-synced from Vertex populate the active live catalog and route at request time instead of returning `Model '' is not available in the active live catalog` ([#11991](https://github.com/diegosouzapw/OmniRoute/issues/11991)) — thanks @fabioluissilva ([#11998](https://github.com/diegosouzapw/OmniRoute/pull/11998)) +- **fix(cli):** support `--base-url` alongside `--endpoint` in `omniroute nodes add`, `update`, and `validate` subcommands to prevent global `--base-url` shadowing issues ([#11999](https://github.com/diegosouzapw/OmniRoute/issues/11999)). ([#12033](https://github.com/diegosouzapw/OmniRoute/pull/12033)) — thanks @HouMinXi +- **fix(providers):** `cloudflare-ai` no longer refuses image content parts for every Workers AI model ([#12002](https://github.com/diegosouzapw/OmniRoute/pull/12002)) — the plain-string `content` requirement behind #2539 is carried by the _model_ schema, not by the `/ai/v1/chat/completions` endpoint (measured: an all-text part array returns 200 on `@cf/mistralai/mistral-small-3.1-24b-instruct`, `@cf/meta/llama-4-scout-17b-16e-instruct` and `@cf/meta/llama-3.3-70b-instruct-fp8-fast`, and 400 on the text-only `@cf/qwen/qwen2.5-coder-32b-instruct`). `transformRequest()` flattened every array and threw on the first non-text part (#6390), so image input was refused for vision-capable Cloudflare models that accept it. All-text arrays are still flattened — the one shape every model accepts — while an array carrying a non-text part is passed through untouched, so the attachment is still never silently dropped. Regression guards: `tests/unit/cloudflare-ai-image-parts-6390.test.ts`. — thanks @davidlinfr +- **fix(resilience):** decouple the limiter-managed execution backstop from the queue-wait budget — new `requestQueue.executionMaxWaitMs` (env `RATE_LIMIT_EXECUTION_MAX_WAIT_MS`, default 600000 = 10 min) now feeds Bottleneck's post-dispatch `expiration`, while `requestQueue.maxWaitMs` keeps its documented queue-wait semantics. Previously the queue-wait budget doubled as the execution expiration, so legitimate long-running calls on non-incremental gateways (whole generation buffered before the first upstream byte, e.g. Console Go / Command Code tiers serving GLM models) were killed mid-flight at the queue budget with a false 504 `RATE_LIMIT_EXECUTION_TIMEOUT` — the local limiter undercut the provider-aware upstream fetch-start timeouts. The surfaced 504 message now names `requestQueue.executionMaxWaitMs`; the error keeps the #4165 guarantees (disclaims an upstream timeout, preserves the Bottleneck error as `cause`, branded code + trusted provenance, classified request-scoped so combo falls back). A real queue-wait bound (the `Promise.race` around `limiter.schedule()` sketched in #9533) remains future work. (#12025) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **Call logs:** keep the `error` field when an artifact exceeds the storage cap, instead of replacing it with the omission marker. The error is the only field that says *why* a request failed and is typically ~90 bytes next to the multi-hundred-KB bodies that trip the cap, so dropping it left a size-limited row undiagnosable — a provider outage, a local timeout and an upstream 400 all rendered identically. It is now preserved at every fallback stage, truncated to 4KB if it is itself large ([#12026](https://github.com/diegosouzapw/OmniRoute/issues/12026)). ([#12095](https://github.com/diegosouzapw/OmniRoute/pull/12095)) — thanks @ntdat812 +- **fix(diagnostics):** preserve the error field (truncated to 4KB with a `[truncated: …]` suffix) in every call-log artifact size-limit fallback stage. Previously the minimal fallback replaced the error with `[omitted: call log artifact size limit exceeded]`, so an oversized artifact row showed nothing about WHY the request failed — e.g. 91 of 847 opencode-go 504 rows on one production instance were undiagnosable from the dashboard. Oversized request/response bodies are still omitted exactly as before; the error cap is independent of the payload sizes that tripped the fallback. (#12026) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **fix(sse):** OpenAI Responses clients that declare the native `web_search` tool now receive a spec-shaped `web_search_call` output item with `action.sources` alongside the preserved function-call round-trip, so search results executed through OmniRoute's own search backend are consumable by standard Responses clients (Codex, pi-web-access, …). ([#12031](https://github.com/diegosouzapw/OmniRoute/pull/12031)) — thanks @watchingdogs +- **fix(cli):** use in-thread alias resolver hooks on modern runtimes to avoid deprecation noise and improve Node.js forward compatibility ([#12073](https://github.com/diegosouzapw/OmniRoute/issues/12073)). ([#12083](https://github.com/diegosouzapw/OmniRoute/pull/12083)) — thanks @xiaoyaner0201 +- **fix(combo):** an operator-set **Agent Features → Context length** on a combo is now honored at request time. The value was persisted and advertised through `/v1/models`, but `resolveComboContextLimit()` never consulted it — so a multi-target combo whose members carry no per-model window fell through to the provider's generic `defaultContextLength` (openrouter 128000, command-code 200000) and rejected large requests with `Input exceeds context window … limit 128000` despite the combo being explicitly sized much larger. An identical single-target combo worked, because it collapses to its concrete target before the guard runs. Invalid values (0/negative/NaN/Infinity) are ignored, so the existing target → combo-min → fallback order is unchanged. ([#12090](https://github.com/diegosouzapw/OmniRoute/pull/12090)) — thanks @adivekar-utexas +- **fix(sse):** passthrough streams now estimate usage on finish when upstream closes without usage even with `stream_options.include_usage` — avoids `0 tokens / 0%` for providers that stay silent (and correctly handles trailing empty-choices usage) ([#12151](https://github.com/diegosouzapw/OmniRoute/pull/12151)) — thanks @maxmad64bis +- **fix(combos):** clearing an agent feature in the combos editor now persists — unchecking context cache protection, or emptying the system message or tool filter, sends an explicit `null` instead of dropping the field from the `PUT` body, which the update merge read as "leave unchanged" ([#12177](https://github.com/diegosouzapw/OmniRoute/pull/12177)) — thanks @foreveryh / @ntdatt812 +- **fix(memory):** self-hosted embedding endpoints now vectorize — the vector width is measured from the first embedding that comes back instead of being read from a registry that cannot describe them, so `vec_memories` is created and memories stop piling up unvectorized behind a green health check ([#12180](https://github.com/diegosouzapw/OmniRoute/pull/12180)) — thanks @kanade-hoshino / @ntdatt812 +- **fix(translator):** the leading `system` message now reaches Responses-API upstreams when its `content` is a content-part array — it was read as `typeof content === "string" ? content : ""`, so a prompt-caching client (Anthropic `cache_control`, the shape LiteLLM and the Anthropic SDK emit) had its entire system prompt replaced by an empty `instructions`. The request was still accepted with a normal `prompt_tokens` count, so the model answered with no instructions and nothing in the response said they were missing. Mid-conversation system turns already handled the array shape ([#7056](https://github.com/diegosouzapw/OmniRoute/pull/7056)); only the first one did not ([#12206](https://github.com/diegosouzapw/OmniRoute/issues/12206)). Regression guard: `tests/unit/translator-openai-responses-system-content-parts.test.ts`. ([#12207](https://github.com/diegosouzapw/OmniRoute/pull/12207)) — thanks @Gorillaz322 +- **fix(free-tier):** `/api/free-tier/summary` no longer computes its totals from a Radar feed built before the catalog the running release ships. When the cached feed is older — or carries no build date at all — the route answers from the shipped catalog, resolved through the operator's local model state so disabled and tombstoned models stay out of the numbers ([#12215](https://github.com/diegosouzapw/OmniRoute/pull/12215)). — thanks @maxmad64bis +- **fix(oauth):** Keep a Claude personal workspace and a Team organization as separate connections — they share the same email and `accountUUID`, so the email-only OAuth dedup let the second login overwrite the first account's tokens; `organizationUUID` now disambiguates them, the way `workspaceId` does for Codex ([#12222](https://github.com/diegosouzapw/OmniRoute/pull/12222)) — thanks @davidebaraldo +- **fix(resilience):** a 402 on a single paid model of a passthrough/gateway provider (e.g. `kilo-gateway`, `ollama-cloud`) no longer terminalizes the whole connection with a never-auto-recovered `credits_exhausted` status — only the paid model is locked out, so free models on the same key keep serving. 402 variant of [#3027](https://github.com/diegosouzapw/OmniRoute/issues/3027). Single-credential providers are unaffected — a 402 there is still treated as the key being genuinely out of credit ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239) / [#10616](https://github.com/diegosouzapw/OmniRoute/issues/10616)) ([#12242](https://github.com/diegosouzapw/OmniRoute/issues/12242)) — thanks @brick30llc-ctrl ([#12266](https://github.com/diegosouzapw/OmniRoute/pull/12266)) — thanks @geekyNads +- **fix(sse):** trust `finish_reason: "length"`/`"max_tokens"` over the reasoning-consumed-token ratio in response quality validation, so a reasoning model truncated below the old 90% threshold correctly fails and retries instead of returning empty content as a silent "success" ([#12262](https://github.com/diegosouzapw/OmniRoute/pull/12262)) — thanks @brick30llc-ctrl +- **fix(settings):** `PUT /api/settings/cache-config` now persists `alwaysPreserveClientCache` to the flat general settings the runtime cache-control policy actually reads; previously the value landed in the databaseSettings "cache" section and was silently ignored, so the endpoint had no effect on `cache_control` passthrough ([#12304](https://github.com/diegosouzapw/OmniRoute/pull/12304)) — thanks @davidebaraldo +- **fix(grok-cli):** treat omitted SuperGrokPro `creditUsagePercent` as 0% used so Provider Limits still renders a weekly bar (proto3 zero-elision) ([#12312](https://github.com/diegosouzapw/OmniRoute/pull/12312)) — thanks @HouMinXi +- **fix(combo):** Expose the two Auto-Combo scoring factors nobody could set — the combo validation schema and the dashboard weight sliders both declared 13 of the scorer's 15 factors, so `connectionDensity` (spreads load across a provider's connections) and `quality` were dropped on save and offered nowhere. The sliders also shipped their own default table that differed from the engine's on every non-zero factor and summed to 1.05, so the percentages shown next to them added up to 105%. Both lists now match `DEFAULT_WEIGHTS`, and a test keeps them there. Note that a combo whose stored `weights` omitted the two keys was effectively running with them at zero and the other thirteen renormalized upward; it now runs with the engine's intended distribution, so its routing does shift ([#12314](https://github.com/diegosouzapw/OmniRoute/pull/12314)) — thanks @maxmad64bis +- **fix(docs):** The free-tier reference no longer says its numbers come "confidence tagged per row" — no catalog entry carries a confidence tag and the API serves none, so every figure on that page is an estimate of the same, unstated quality. The page now states what an entry does vouch for: an independently documented hard stop (set by hand with the source in a comment, never defaulted to `true`) and a prompt-training disclosure, both with live counts the `check:docs-counts` gate keeps honest ([#12318](https://github.com/diegosouzapw/OmniRoute/pull/12318)) — thanks @maxmad64bis +- **fix(usage):** `adobe-firefly` and `firefly` have had a working usage fetcher since Adobe Firefly landed, but neither was ever added to the registration list, so the provider-plugin manifest, `genericQuotaFetcher` and the free-access quota cache all reported them as having no usage support — while `USAGE_SUPPORTED_PROVIDERS` said the opposite. Both are now declared, which also means their credit balance is fetched like any other declared provider's: `registerGenericQuotaFetchers` now registers a generic quota fetcher for them, and `resolveFreeAccessState` no longer returns early. A test holds the registration list to the dispatcher's switch in both directions, which is what the module's own docstring already asked for in prose ([#12321](https://github.com/diegosouzapw/OmniRoute/pull/12321)) — thanks @maxmad64bis +- **fix(quota):** drop the generic quota cache (agy / Antigravity / Claude OAuth) on an upstream 429 so reset-aware scoring does not keep a 60s stale snapshot, and force-refresh the next usage fetch so inner provider caches cannot recache the same window ([#12325](https://github.com/diegosouzapw/OmniRoute/pull/12325)) — thanks @HouMinXi +- **fix(combos):** deleting a combo now clears its persisted LKGP pins instead of leaving unreachable `key_value` rows behind ([#12326](https://github.com/diegosouzapw/OmniRoute/issues/12326)) ([#12425](https://github.com/diegosouzapw/OmniRoute/pull/12425)) — thanks @KrzysiekSko +- **fix(sse):** Keep ZWNJ (U+200C) and ZWJ (U+200D) in assistant text, reasoning and tool-call arguments — Persian/Kurdish half-space (`ارائه‌دهنده`), Arabic/Indic shaping and emoji sequences no longer lose them; the response de-obfuscation now removes joiners only between ASCII word characters, where the request side inserts them ([#12186](https://github.com/diegosouzapw/OmniRoute/issues/12186)) — thanks @rezjalibd ([#12359](https://github.com/diegosouzapw/OmniRoute/pull/12359)) — thanks @pacocartones +- **fix(resilience):** count resolved upstream 5xx results against the provider circuit breaker on the chat path — `CircuitBreaker.execute()` no longer reads a resolved `{ success: false, status: 5xx }` as a success that cancels the call-site failure, so a provider answering 503s now trips its breaker instead of staying `CLOSED` at `failureCount: 1`; single-model and combo dispatches are each accounted exactly once ([#12254](https://github.com/diegosouzapw/OmniRoute/issues/12254)) ([#12360](https://github.com/diegosouzapw/OmniRoute/pull/12360)) — thanks @pacocartones +- **fix(providers):** resolve the Codex quota auto-ping model from the live provider catalog and lifecycle registry instead of the retired `gpt-5.1-codex-mini`, and pause the ping with one actionable warning when no selectable Codex model exists rather than retrying a shut-down id every cooldown window ([#11905](https://github.com/diegosouzapw/OmniRoute/issues/11905)) ([#12361](https://github.com/diegosouzapw/OmniRoute/pull/12361)) — thanks @pacocartones +- **fix(api):** keep the `{created, data}` wrapper on combo-routed `/v1/images/generations` responses and default Codex image results to `b64_json` on both `/v1/images/generations` and `/v1/images/edits` so Codex CLI's built-in `image_gen` can decode them ([#12268](https://github.com/diegosouzapw/OmniRoute/issues/12268)) ([#12362](https://github.com/diegosouzapw/OmniRoute/pull/12362)) — thanks @pacocartones +- **fix(sse):** Name the shadowed custom provider node when a built-in provider id/alias (e.g. `openference` → `of`) reserves the prefix of an existing OpenAI/Anthropic-compatible node, so the runtime `No active credentials for provider: ` error explains that the prefix routed to the built-in and never reached the node's healthy connections, instead of contradicting the dashboard ([#11943](https://github.com/diegosouzapw/OmniRoute/issues/11943)) — thanks @morpheus9393 ([#12365](https://github.com/diegosouzapw/OmniRoute/pull/12365)) — thanks @pacocartones +- **fix(i18n):** wrap `ccOnboardingKeyPlaceholder` in ICU single quotes across all 43 locales so angle brackets render literally instead of being parsed as rich-text tags, which crashed the Claude Code onboarding block with `INVALID_MESSAGE: INVALID_TAG` ([#12302](https://github.com/diegosouzapw/OmniRoute/issues/12302)) ([#12369](https://github.com/diegosouzapw/OmniRoute/pull/12369)) — thanks @gonisulaimann +- **fix(models):** publish `effort_tiers` on Kimi K3's synced base-model entries (`kmca/k3`, `kmca/k3-256k`) so catalog-only clients (OpenCode, plain SDK pickers) can see and select the reasoning tiers (`low`/`high`/`max`) the synced metadata already carried — the `isSkippedEffortProvider` gate no longer suppresses tier visibility on those base entries, while synthetic `-` variant generation stays prevented and Codex/GLM base models remain excluded unchanged ([#12299](https://github.com/diegosouzapw/OmniRoute/issues/12299)) ([#12371](https://github.com/diegosouzapw/OmniRoute/pull/12371)) — thanks @gonisulaimann +- **fix(guardrails):** keep `auto`/`auto/*` virtual combos exempt from the Vision Bridge `fixedModel` credential guard so a combo target is passed through instead of silently falling back to global auto-selection ([#12237](https://github.com/diegosouzapw/OmniRoute/issues/12237)) ([#12373](https://github.com/diegosouzapw/OmniRoute/pull/12373)) — thanks @pacocartones +- **fix(combo):** capability-filter exhaustion caused by `max_tokens` above every target's known output limit now reports that reason (requested `max_tokens` vs the pool's highest known ceiling) instead of the unrelated "supports structured output" message ([#12229](https://github.com/diegosouzapw/OmniRoute/issues/12229)) — thanks @DW-MediaLab ([#12374](https://github.com/diegosouzapw/OmniRoute/pull/12374)) — thanks @pacocartones +- **fix(auth):** the `least-used` account strategy now prefers accounts without backoff before falling back to oldest `lastUsedAt`, the same tie-break `round-robin` already applies, so a failover no longer lands on a just-rate-limited account for a single request ([#12279](https://github.com/diegosouzapw/OmniRoute/issues/12279)) — thanks @tenshiak ([#12375](https://github.com/diegosouzapw/OmniRoute/pull/12375)) — thanks @pacocartones +- **fix(docker):** the `chatgpt-web-codex-browser` image now finds the Chrome binary under `chrome-linux64/` (Chrome for Testing layout in `playwright:v1.62.0-noble`) as well as the legacy `chrome-linux/`, so the container no longer crash-loops with `exec: --headless=new: not found` ([#12024](https://github.com/diegosouzapw/OmniRoute/issues/12024)) ([#12376](https://github.com/diegosouzapw/OmniRoute/pull/12376)) — thanks @pacocartones +- **fix(providers):** declare `groq/compound` and `allam-2-7b` as non-reasoning models in the curated Groq registry so `reasoning_effort` / `output_config.effort` / `thinking` from Claude Code are stripped instead of forwarded, which Groq rejected with HTTP 400 ([#12134](https://github.com/diegosouzapw/OmniRoute/issues/12134)) ([#12379](https://github.com/diegosouzapw/OmniRoute/pull/12379)) — thanks @pacocartones +- **fix(executors):** `OpencodeExecutor` no longer forces a direct connection when the connection has a proxy assigned in Proxy Management but no per-account proxies: the single-account fast path used to wrap the upstream dispatch in the direct-egress sentinel, discarding the ambient proxy context the chat handler had pinned from `proxy_assignments`, so API-key `opencode`/`opencode-go` connections egressed from the host IP (and hit geoblocks) despite the assignment. The direct pin is now applied only when no ambient proxy context exists ([#11894](https://github.com/diegosouzapw/OmniRoute/issues/11894) — thanks @hizzt) ([#12380](https://github.com/diegosouzapw/OmniRoute/pull/12380)) — thanks @pacocartones +- **fix(api):** `GET /v1/models` with `MODELS_CATALOG_PREFIX_MODE=canonical` (or `?prefix=canonical`) now lists providers whose registry alias is undefined or equal to their own id (Antigravity, Antigravity CLI and other self-aliased built-ins) — their single `provider/model` id was dropped by the alias/canonical duplicate guard in the static, synced, custom and alias-backed catalog loops ([#12058](https://github.com/diegosouzapw/OmniRoute/issues/12058)) — thanks @cheynetom ([#12381](https://github.com/diegosouzapw/OmniRoute/pull/12381)) — thanks @pacocartones +- **fix(translator):** Drop replayed `thinking` blocks that carry no signature (the shape produced from cross-provider `reasoning_content`) instead of stamping the default Claude signature on them, which Anthropic rejected with `400 Invalid signature in thinking block` on the next turn served by an Anthropic rung ([#12105](https://github.com/diegosouzapw/OmniRoute/issues/12105)) — thanks @atescivitci-cmd ([#12386](https://github.com/diegosouzapw/OmniRoute/pull/12386)) — thanks @pacocartones +- **fix(cli):** Resolve Bun's `--preload` polyfill path against the package root instead of `dist/`, so `omniroute` installed with `bun install -g` no longer crashes at startup with `error: preload not found …/dist/open-sse/utils/setupPolyfill.ts` ([#11980](https://github.com/diegosouzapw/OmniRoute/issues/11980)) — thanks @joglomedia ([#12387](https://github.com/diegosouzapw/OmniRoute/pull/12387)) — thanks @pacocartones +- **fix(providers):** `gemini-business` now publishes its model catalog — `/v1/models` and `/v1/providers/gemini-business/models` list the 12 enterprise Gemini ids the executor understands instead of returning an empty list (#12107) ([#12389](https://github.com/diegosouzapw/OmniRoute/pull/12389)) — thanks @pacocartones +- **fix(db):** install `busy_timeout` before the SQLite connection's first statement so a process opening the database while another one closes its WAL connection waits out the transient EXCLUSIVE lock instead of dying with `database is locked`, and recognise the drivers' real BUSY/PROTOCOL/IOERR errors as transient in the corruption probe so the same lock no longer renames the database away as corrupt; deflakes `cross-process contenders never both acquire the same connection` (#12394 — thanks @pacocartones) +- **fix(chat-admission):** derive the `chat_admission_busy` 503 `Retry-After` from observed heavyweight-lease occupancy — the larger of the exhausted `OMNIROUTE_CHAT_ADMISSION_QUEUE_MS` window and the time since capacity last turned over, capped at 60 s — instead of a fixed 1 s (structural) / 2 s (byte-stage) hint that invited Codex/agent fan-out clients to re-send ~1 MiB `/v1/responses` bodies every second into a gate held for the whole SSE lifetime; an idle gate keeps the historical floors ([#12135](https://github.com/diegosouzapw/OmniRoute/issues/12135)) (#12395 — thanks @pacocartones) +- **fix(api-manager):** the API key permissions modal no longer silently drops `allowedCombos` entries its Combo picker cannot render — routing-rule names such as `rt-*`, which the backend already honours — when "All" is clicked and the key is switched back to "Restrict"; those entries now survive the toggle, are listed read-only under the combo list so the count and the list agree, and are saved back verbatim instead of persisting `[]` (deny-all) (#12397 — thanks @pacocartones) +- **fix(catalog):** write the NUL separator of the catalog connection memo key, the provider serviceKind memo key, the Video Bridge promotion group key and a JSON-exactness test fixture as the `\u0000` escape instead of a raw byte — same runtime value, but the raw byte made git, GitHub and ripgrep treat those files as binary (hidden PR diffs, silently skipped searches); a guard test now keeps raw NUL bytes out of `src/`, `open-sse/` and `tests/` (#12403 — thanks @pacocartones) +- **fix(providers):** add `CLAUDE_CODE_CLIENT_VERSION` and `GITHUB_COPILOT_CLI_VERSION` env overrides so Anthropic/Copilot client-version gates can be unblocked without a rebuild ([#12417](https://github.com/diegosouzapw/OmniRoute/issues/12417)) ([#12632](https://github.com/diegosouzapw/OmniRoute/pull/12632)) — thanks @HouMinXi +- **fix(db):** back-fill `last_ping_at` and `last_pinged_reset_key` on `provider_connections` during schema reconciliation so divergent lineages that skipped `123_quota_auto_ping` still accept quota auto-ping writes ([#12470](https://github.com/diegosouzapw/OmniRoute/pull/12470) — thanks @KooshaPari) +- **fix(ci):** document MIT exceptions for `@eloqnt/{config,format-json,format-po}` (next-intl transitive; locked tarballs omit `license`) and keep the A2A lifecycle vitest off the real SQLite persistence seam ([#12581](https://github.com/diegosouzapw/OmniRoute/issues/12581)) ([#12595](https://github.com/diegosouzapw/OmniRoute/pull/12595)) +- OpenCode plugin `/v1/models` catalog fetch now waits 30s by default and attaches HTTP `statusCode` on 401/5xx so host fallback plugins can hop instead of seeing an untyped AbortError/UnknownError. ([#12607](https://github.com/diegosouzapw/OmniRoute/pull/12607)) — thanks @RaviTharuma +- **CI:** the OpenAPI security-tier gate now mirrors `isAlwaysProtectedPath()` in full — it also reads `ALWAYS_PROTECTED_API_PATTERNS`, so the pattern-gated credential routes (`/api/providers/{id}/{claude,codex}-auth/{export,apply-local}`, GHSA-5926-2w35-7h4q) no longer report as unannotated. (#12605) +- **fix(api):** GET /v1/models no longer waits forever on a hung coalesced catalog rebuild; cold-path waits are bounded (`CATALOG_BUILD_TIMEOUT_MS`, default 8s) and a last-good 200 is served when the rebuild times out ([#12627](https://github.com/diegosouzapw/OmniRoute/issues/12627)). ([#12628](https://github.com/diegosouzapw/OmniRoute/pull/12628)) — thanks @RaviTharuma +- **fix(cursor):** a non-array `tool_calls` on an assistant message no longer crashes the cursor request translator with a `TypeError`; both loops now require an array ([#12689](https://github.com/diegosouzapw/OmniRoute/issues/12689)) ([#12691](https://github.com/diegosouzapw/OmniRoute/pull/12691)) — thanks @soroush5 +- **fix(chat):** Chat Completions no longer return empty `content` after a server-owned memory or skills tool; the first provider send and account/model recovery now share one pipeline so a follow-up round-trip can fill the reply ([#12696](https://github.com/diegosouzapw/OmniRoute/issues/12696)) — thanks @HouMinXi ([#12867](https://github.com/diegosouzapw/OmniRoute/pull/12867)) +- **fix(dashboard):** batch-deleting provider keys no longer toasts failure after a successful delete when the confirm button's click event is forwarded as `onAfter` ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) — thanks @HouMinXi +- **fix(glm):** drop the extra 16th argument to `createSSETransformStreamWithLogger` that TypeScript rejected (TS2554) and that never reached the TransformStream ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) ([#12770](https://github.com/diegosouzapw/OmniRoute/pull/12770)) — thanks @HouMinXi +- **fix(dashboard):** pass `nodeMap` into Runtime `QuotaGroup` so a quota monitor in error/exhausted/alerting no longer throws `ReferenceError: nodeMap is not defined`. ([#12868](https://github.com/diegosouzapw/OmniRoute/pull/12868)) — thanks @HouMinXi +- Restricted API keys whose `allowedModels` lists a combo name no longer skip every combo member at pre-dispatch (`ALL_TARGETS_SKIPPED`). Inner-target filtering still applies when the allow-list is a provider prefix or `disableNonPublicModels` is on ([#12899](https://github.com/diegosouzapw/OmniRoute/pull/12899)). — thanks @HouMinXi +- **fix(combos):** Effort-suffixed combo members inherit the base model's `model_context_overrides` row so priority order is not inverted on large requests ([#12926](https://github.com/diegosouzapw/OmniRoute/pull/12926)) — thanks @HouMinXi +- **fix(catalog):** Picker-added `customModels` enter the dispatch-time live catalog so combo members and bare inference no longer 400 ([#12934](https://github.com/diegosouzapw/OmniRoute/pull/12934)) — thanks @HouMinXi +- **fix(docker):** re-derive the Docker build's worker-pool memory budget from the MEASURED ~4.5 GB per-process RSS (the issue owner's own VPS dmesg OOM-killer reproduction) instead of the stale 2560 MB/worker inference #11419 shipped, and lower `OMNIROUTE_BUILD_WORKERS` 3 → 2 so 1 parent + 1 page-data worker (2 processes × 4.5 GB = 9 GB) fits the 12.288 GB (75%) budget on a 16 GB GitHub Actions runner — the previous default (1 parent + 2 workers = 13.5 GB) still overcommitted the runner and kept "Publish to Docker Hub" failing with `cannot allocate memory` after #11419 merged (#7518). ([#11663](https://github.com/diegosouzapw/OmniRoute/pull/11663)) +- **fix(electron):** the packaged Windows desktop build now passes the #7592 cold-restart smoke end to end. Five defects were found and fixed on the way: (1) optional-pack staging failed on any machine whose `tar` is GNU tar — it reads the drive letter in an absolute `-f C:\...` archive path as a remote rsh host (`Cannot connect to C:`), so staging now passes a bare filename with `cwd` at the tarball directory and surfaces tar stderr on failure; (2) the Electron `files` allowlist was missing `lib/loginHeaderCapture.js`, so the packaged main process crashed at startup with `Cannot find module './lib/loginHeaderCapture'`; (3) electron-builder ≥26 injects an `!**/node_modules/**` ignore into every extraResources pattern list that no positive filter can override, which silently dropped the staged runtime `node_modules` (including the better-sqlite3 N-API prebuild) from `resources/app` — a fresh v3.8.50 desktop build would have shipped with no native SQLite driver and reproduced the #7592 sql.js fallback on every machine; an `afterPack` hook now restores the staged `node_modules` after packing; (4) the packaged-app smoke harness redirected `USERPROFILE` into its temp DATA_DIR, but Electron resolves the Roaming profile from `%USERPROFILE%\AppData\Roaming\` (USERPROFILE takes precedence over the APPDATA env var) and the path service throws instead of creating it, so `requestSingleInstanceLock()` returned false and the app exited(0) silently before `app.whenReady()` — the harness now pre-creates the derived tree, and `ensureSmokeEnvDirs` is exported and unit-tested; (5) the #7592 driver assertion parsed a `[DB] Driver: ...` line that the server's primary DB path never printed (only the unused `openDatabaseAsync()` did) — `getDbInstance()` now emits the same line on open so the guard can actually assert the native driver. Also: the smoke env-allowlist unit test hardcoded POSIX paths and could only pass on Linux/macOS; expectations are now host-agnostic, with new regression tests for the USERPROFILE-derived tree and `tarPack` under absolute Windows-style paths ([#11443](https://github.com/diegosouzapw/OmniRoute/pull/11443)) — thanks @jonlwheat2-gif +- fix(sse): stop the auto-combo candidates inspector from silently dropping model-locked/cooled-down rows (#9133) ([#11994](https://github.com/diegosouzapw/OmniRoute/pull/11994)) — thanks @hartmark +- **fix(providers):** Perplexity Web no longer turns upstream stream failures into successful assistant text; pre-content failures remain eligible for fallback, partial output ends with a structured sanitized error, and failed sessions are not persisted ([#12465](https://github.com/diegosouzapw/OmniRoute/pull/12465)) +- **fix(video-bridge):** preserve chronological video and transcript fusion, source-frame timestamps, transcript provenance, full contact-sheet intervals, and pre-abort callback isolation ([#11681](https://github.com/diegosouzapw/OmniRoute/pull/11681)) +- **Z.ai Web:** HTTP 200 streams carrying an upstream error now terminate with a structured failure instead of assistant text plus a normal stop, preserving partial output while allowing pre-content combo fallback. ([#12454](https://github.com/diegosouzapw/OmniRoute/pull/12454)) +- Absorb `Error [AbortError]: request_signal_aborted` and DOMException AbortError shapes in the process-level client-abort crash guard so routine client disconnects no longer kill the server (exit code 7). ([#12165](https://github.com/diegosouzapw/OmniRoute/pull/12165)) — thanks @alvinveroy +- **fix(sse):** Treat Adapta Web `type:error` SSE events as sanitized non-stream failures instead of empty HTTP 200 completions. ([#12459](https://github.com/diegosouzapw/OmniRoute/pull/12459)) +- **fix(autoCombo):** layer 3 (`models_dev_tier`) no longer scores retired models as premium, merge cross-provider capability rows deterministically, and inherit tier scores through variant ids ([#11508](https://github.com/diegosouzapw/OmniRoute/issues/11508)). `getModelsDevTierFitness` now vetoes ids present in `config/quality/model-lifecycle.json` with status `retired` before any other signal; `loadModelCapabilities()` aggregates per `model_id` (any-non-null-true for booleans, max for `limit_context`) instead of last-write-wins over SQLite's undefined row order; a capabilities miss retries through `resolveScoresAs()`'s base id and reports `models_dev_tier:inherited`, matching layers 1–2 (#11492). Adds `getModelsDevTierFitnessWithSource()` and an `invalidateCapabilitiesCache()` test/ops hook. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) — thanks @Meet6338-X +- fix(sse): accept Claude `content:[]` + `stop_reason: max_tokens`/`tool_use` in `detectMalformedNonStream` (match `isEmptyContentResponse`) so Claude Code `/model` probes with `max_tokens: 1` no longer become a false 502 ([#11568](https://github.com/diegosouzapw/OmniRoute/pull/11568)) — thanks @Seramicx +- **fix(security):** sanitize `request.failed` diagnostics before publishing them to live dashboard listeners and replay history, while keeping status, model, provider, latency, and internal call-log diagnostics intact. ([#12469](https://github.com/diegosouzapw/OmniRoute/pull/12469)) +- **fix(pwa):** do not intercept dashboard navigations so Chrome can fall back from a stale HTTP/3 Alt-Svc advertisement (UDP :20128 is unpublished; F5 on a long-lived tab hung until a new tab opened a fresh TCP connection). ([#12767](https://github.com/diegosouzapw/OmniRoute/pull/12767)) — thanks @HouMinXi +- **fix(executors):** handle DuckDuckGo ERR_BN_LIMIT (418) without retrying — when the upstream returns `418 ERR_BN_LIMIT` (rate-limit/ban), the executor now returns the error immediately instead of burning another VQD acquisition that would only count against the IP limit. The retry logic for `418 ERR_CHALLENGE` (unsolved challenge) remains unchanged. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) ([#12110](https://github.com/diegosouzapw/OmniRoute/pull/12110)) — thanks @oyi77 / @Meet6338-X +- Fixed DuckDuckGo streaming responses losing JSON lines and UTF-8 characters split across network chunks. ([#11528](https://github.com/diegosouzapw/OmniRoute/pull/11528)) — thanks @pacocartones +- Harden generated Cloudflare, Vercel, and Deno relays by stripping hop-by-hop, framing, and proxy authentication headers before forwarding requests upstream. ([#11533](https://github.com/diegosouzapw/OmniRoute/pull/11533)) — thanks @pacocartones +- **feat(grok-cli):** Provider Limits shows grok-cli banked reset credits from `GetRemainingResets` (including a real zero; a failed RPC omits the row) and the existing View credits button now calls `ConsumerUiSvc/RedeemReset` for grok-cli. Live tokens use nested fields 10/20/30 (id + Timestamp), not compact 1/2/3. ([#12805](https://github.com/diegosouzapw/OmniRoute/pull/12805)) — thanks @HouMinXi +- **fix(memory):** Embedding Model Quick select, Embedding Source remote dropdown, and Rerank selector now list every configured provider with embedding/rerank support instead of only chat-catalog text matches plus OpenRouter live discovery; a generic OpenAI-compatible `/embeddings` + Cohere-compatible `/rerank` runtime fallback resolves any configured chat provider's embedding/rerank endpoint, so unlisted providers no longer fail with "Unknown embedding provider"; both memory selectors gained a free-text model override ([#11390](https://github.com/diegosouzapw/OmniRoute/pull/11390)) — thanks @rqzbeh +- Harden SQLite upgrades around the historical migration-074 version collision: missing discovery and inspector tables are replayed atomically, pre-existing databases (including setup-created skeletons) receive reusable content-addressed safety snapshots, and Node test/eval probes without `DATA_DIR` are isolated from the operator database. ([#12435](https://github.com/diegosouzapw/OmniRoute/pull/12435)) +- **fix(dashboard):** Moonshot/Kimi Open Platform voucher and cash leftover percentages follow the bucket balance, so an empty wallet no longer paints those rows as 100% while Available is 0% ([#12733](https://github.com/diegosouzapw/OmniRoute/pull/12733)) — thanks @HouMinXi +- **fix(combo):** return non-retryable HTTP 400 when all candidates for a pinned native Codex turn are unavailable due to model-scoped lockout, terminating the turn cleanly while preserving turn continuity and enabling standard Combo routing on subsequent turns ([#12240](https://github.com/diegosouzapw/OmniRoute/pull/12240)) — thanks @mdigitalbh81 +- Fixed auto-router strategies preserving the selected connection through ranking and dispatch when multiple connections share the same provider and model. ([#11530](https://github.com/diegosouzapw/OmniRoute/pull/11530)) — thanks @pacocartones +- Preserve Chat Completions JSON-object response formats when translating requests to the Responses API. ([#11529](https://github.com/diegosouzapw/OmniRoute/pull/11529)) — thanks @pacocartones +- Fix idempotency fingerprints so Responses API requests include `input` and other semantic generation fields using stable serialization, while excluding credentials and request metadata. ([#11532](https://github.com/diegosouzapw/OmniRoute/pull/11532)) — thanks @pacocartones +- **fix(api):** Generated API CLI commands now enforce required OpenAPI request bodies; Combo test commands forward the required `comboName` body, while API keys created by older writers after migration 149 preserve legacy allow-all Combo access without widening explicit empty allowlists — thanks @marcelokarval ([#12070](https://github.com/diegosouzapw/OmniRoute/pull/12070)) +- **fix(grok-web):** treat upstream streaming failures as failures instead of successful assistant text: error-only streams now fail readiness with HTTP 502, while failures after legitimate content preserve that partial output and terminate through the sanitized stream failure path without a normal `stop` completion. ([#12458](https://github.com/diegosouzapw/OmniRoute/pull/12458)) +- HuggingChat now turns HTTP 200 JSONL generation failures into a sanitized 502 before content, or a fixed public stream failure after partial output, so fallback and request persistence no longer record a false successful stop. ([#12456](https://github.com/diegosouzapw/OmniRoute/pull/12456)) +- **fix(providers):** keep 1min.ai HTTP 200 stream errors out of assistant content, preserve partial output, and expose sanitized terminal errors so pre-content failures can fall back. ([#12466](https://github.com/diegosouzapw/OmniRoute/pull/12466)) +- **fix(sessions):** preserve legacy Sessions data when the additive exclusive-session projection is unavailable, returning an empty projection and warning only once per contiguous outage instead of failing the endpoint; the Sessions badge intentionally reflects the merged legacy and exclusive row count introduced by [#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389) ([#11469](https://github.com/diegosouzapw/OmniRoute/pull/11469)) — thanks @KaspaPulse +- Render Nimble Search with the generic provider icon and serve Opper's proven logo locally. ([#12415](https://github.com/diegosouzapw/OmniRoute/pull/12415)) +- **fix(quality):** `config/quality/provider-assets-provenance.jsonl` was stale against the physical `public/providers/` tree (still listing 225 assets and 5 duplicate-content groups after several provider-retirement PRs removed 85 icon files), which would have made `check:provider-asset-provenance` fail on the next full run. Reconciled the manifest to the real 142-file snapshot (142 asset records, 1 duplicate-content group, 2 newly-registered files marked `unresolved` pending provenance research), repinned `auditedCommit`, and updated the regression test's hardcoded counts to match. ([#11876](https://github.com/diegosouzapw/OmniRoute/pull/11876)) +- fix(ui): wrap quota card footer action buttons to prevent clipping Refresh button on narrow cards — when extra action buttons such as `View credits` are present on Codex cards, allow the footer row to flex-wrap with `shrink-0` buttons so `Refresh now` remains visible and clickable across all responsive card widths ([#11464](https://github.com/diegosouzapw/OmniRoute/pull/11464)) — thanks @adevwithpurpose +- Keep Antigravity Gemini usable when the same connection's Claude weekly quota is empty; generic quota cache stays per-connection for every other provider. ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) — thanks @HouMinXi +- Fixed Tailscale tunnel setup to reject ports outside the valid 1-65535 range and safely install Windows MSI packages from paths containing apostrophes. ([#11534](https://github.com/diegosouzapw/OmniRoute/pull/11534)) — thanks @pacocartones +- Classify Cloudflared and Tailscale process-management routes as local-only and spawn-capable while preserving remote access to read-only tunnel status endpoints. ([#11531](https://github.com/diegosouzapw/OmniRoute/pull/11531)) — thanks @pacocartones +- Electron release workflow: the `publish-npm` job now grants `actions: read` to the reusable `npm-publish.yml` it calls (its `publish` job requests it), which is what made GitHub refuse the whole v3.8.50 run at startup and ship the release with zero desktop assets; a `workflow_dispatch` now builds the requested tag instead of the dispatching branch and can skip the npm leg (`publish_npm=false`) when only re-attaching assets ([#11974](https://github.com/diegosouzapw/OmniRoute/pull/11974)) +- **fix(dashboard):** The Combos page usage guide now reads its dismissal through `useSyncExternalStore` instead of correcting SSR state inside an effect, removing an extra commit of the page tree on every load (and the `react-hooks/set-state-in-effect` error it raised). ([#12671](https://github.com/diegosouzapw/OmniRoute/pull/12671)) +- Electron release: `electron/package-lock.json` regained the optional `electron-builder-squirrel-windows` subtree (13 entries) that `npm ci` had been refusing as out of sync, `electron-release.yml` gained a `build_ref` dispatch input and stops regenerating release notes on a re-attach dispatch, and the npm publish workflow attaches the SBOM to the GitHub Release on dispatch publishes too — so the v3.8.51 tag ships every desktop asset and the SBOM like v3.8.49 did ([#12022](https://github.com/diegosouzapw/OmniRoute/pull/12022)) +- Drained the reds every PR against `release/v3.8.51` was born with: documented the three service-worker build-id variables, registered the six retirement/tunnel tests with the mutation gate, approved `eslint-plugin-react-hooks` in the dependency allowlist, added the six `combo.sort.*` strings to `vi` and `pt-BR`, pointed the ChatGPT Web doc at the real migration-168 test, worded the g4f hint around the member key, and realigned four tests to the retired-provider catalog and the legacy-schema fixtures the retirement migrations touch. ([#11940](https://github.com/diegosouzapw/OmniRoute/pull/11940)) +- **fix(providers):** Claude, Grok, LMArena, Notion, and Perplexity web-cookie transports now use pooled `wreq-js` 3.2 instead of the native sidecar, with all nine supported bindings pinned and audited, and the applicable platform binding plus native-license evidence included in each release artifact ([#12429](https://github.com/diegosouzapw/OmniRoute/pull/12429), supersedes [#11753](https://github.com/diegosouzapw/OmniRoute/pull/11753)). +- **fix(providers):** Zed Hosted streaming failures now trigger fallback before content and end partial streams with a sanitized structured error instead of fake assistant text and a normal-success stop. ([#12455](https://github.com/diegosouzapw/OmniRoute/pull/12455)) +- **fix(test):** capture hook result after render (direct commit `b85d0abb0b`) +- **fix(sync):** repair the two regressions the v3.8.50 sync-back left on release/v3.8.51 (direct commit `5ade9e0851`) +- **revert(models):** drop the memo reset hook ported from #10055 (direct commit `282de841ae`) +- **fix(typecheck):** guard the optional connectionId before the reactive model sync (direct commit `f2825bad75`) +- **fix(quota-share):** release the winner's reserved in-flight slot (#11371) ([#11408](https://github.com/diegosouzapw/OmniRoute/pull/11408)) — thanks @oyi77 +- **fix(providers):** stop silently dropping reasoning effort on opencode families (#10788) ([#11409](https://github.com/diegosouzapw/OmniRoute/pull/11409)) — thanks @oyi77 +- **fix(combo):** enable genuine quota-aware routing for generic providers (antigravity, claude, etc.) ([#11411](https://github.com/diegosouzapw/OmniRoute/pull/11411)) — thanks @HouMinXi +- **fix(oauth):** allow expired token retry before deactivation and activate expired retry loop (#P0) ([#11414](https://github.com/diegosouzapw/OmniRoute/pull/11414)) — thanks @HouMinXi +- **perf(executors):** lazy-load the executor registry — defer class imports + construction to first use (#11220) ([#11421](https://github.com/diegosouzapw/OmniRoute/pull/11421)) — thanks @oyi77 +- **fix:** make release/v3.8.50 CI gates green (run 32786966560 root causes) ([#11450](https://github.com/diegosouzapw/OmniRoute/pull/11450)) — thanks @jonlwheat2-gif +- **fix(sse):** exact-domain cookie match and origin-equality URL assertions ([#11452](https://github.com/diegosouzapw/OmniRoute/pull/11452)) — thanks @hartmark +- **fix(providers):** send Responses API payload during OpenAI-compatible validation ([#11454](https://github.com/diegosouzapw/OmniRoute/pull/11454)) — thanks @tuandinh0801 +- **fix(command-code):** fallback to /alpha/generate for Go plan without Provider API access ([#11455](https://github.com/diegosouzapw/OmniRoute/pull/11455)) — thanks @Chewji9875 +- **fix(responses-continuation):** fail closed on a log-truncated stored input/output array ([#11473](https://github.com/diegosouzapw/OmniRoute/pull/11473)) — thanks @hartmark +- **fix(cli):** write jcode settings as [providers.omniroute] in config.toml ([#11484](https://github.com/diegosouzapw/OmniRoute/pull/11484)) — thanks @hongnoul +- **fix(logging):** raise chat-log array truncation cap to 1000 and unify duplicate implementations ([#11499](https://github.com/diegosouzapw/OmniRoute/pull/11499)) — thanks @hartmark +- **fix:** restore live websocket URL helpers ([#11509](https://github.com/diegosouzapw/OmniRoute/pull/11509)) — thanks @PauloFH +- **perf(core):** eliminate async waterfalls across home, cache, and db operations (#11396) ([#11512](https://github.com/diegosouzapw/OmniRoute/pull/11512)) — thanks @HouMinXi +- **fix(kiro):** add runtime.us-east-1.kiro.dev as first-attempt endpoint ([#11517](https://github.com/diegosouzapw/OmniRoute/pull/11517)) — thanks @rifqiawl +- **fix(providers):** remove duplicate 128-tool truncation in OpencodeExecutor (#11444) ([#11518](https://github.com/diegosouzapw/OmniRoute/pull/11518)) — thanks @HouMinXi +- **fix(providers):** poll onboardUser LRO before classifying BYOP ([#11519](https://github.com/diegosouzapw/OmniRoute/pull/11519)) — thanks @rifqiawl +- **fix(translator):** preserve exact tool name casing for Claude tool_use payloads (#11487) ([#11520](https://github.com/diegosouzapw/OmniRoute/pull/11520)) — thanks @HouMinXi +- **fix(kimi-web):** align health probe with executor domain ([#11521](https://github.com/diegosouzapw/OmniRoute/pull/11521)) — thanks @pacocartones +- **fix(cli):** preserve existing admin password during setup ([#11522](https://github.com/diegosouzapw/OmniRoute/pull/11522)) — thanks @pacocartones +- **fix(mitm):** keep Windows DNS elevation off the POSIX sudo path (#11430) ([#11542](https://github.com/diegosouzapw/OmniRoute/pull/11542)) — thanks @official-burak +- **fix(sse):** scale chat admission by ingest byte budget instead of a fixed request count ([#11548](https://github.com/diegosouzapw/OmniRoute/pull/11548)) — thanks @TheDemonTuan +- **fix:** accept Gemini Web cookie exports ([#11554](https://github.com/diegosouzapw/OmniRoute/pull/11554)) — thanks @rafacpti23 +- **fix(i18n):** complete Brazilian Portuguese translations ([#11558](https://github.com/diegosouzapw/OmniRoute/pull/11558)) — thanks @rafacpti23 +- **fix(sse):** explicit types for openai-responses pureHelpers — clears last failing core typecheck gate ([#11567](https://github.com/diegosouzapw/OmniRoute/pull/11567)) — thanks @jonlwheat2-gif +- **fix(opencode):** hydrate Proxy Pool references for no-auth connections ([#11584](https://github.com/diegosouzapw/OmniRoute/pull/11584)) — thanks @AStupidBear +- **fix(ci):** resolve inherited release/v3.8.51 base-red CI failures ([#11608](https://github.com/diegosouzapw/OmniRoute/pull/11608)) — thanks @jonlwheat2-gif +- **fix(db):** fallback load STORAGE_ENCRYPTION_KEY from env files ([#11614](https://github.com/diegosouzapw/OmniRoute/pull/11614)) — thanks @benzntech +- **fix(providers):** mark Brave Search as no longer having a free tier ([#11621](https://github.com/diegosouzapw/OmniRoute/pull/11621)) — thanks @turbolego +- **fix:** pass tool result content through as string in OpenAI/Claude -> Gemini translation ([#11624](https://github.com/diegosouzapw/OmniRoute/pull/11624)) — thanks @Hsia97 +- **fix(sse):** scope credential health to exact key ([#11628](https://github.com/diegosouzapw/OmniRoute/pull/11628)) — thanks @KaspaPulse +- **fix(install):** guard prepare script against missing husky ([#11634](https://github.com/diegosouzapw/OmniRoute/pull/11634)) — thanks @Prajeeth-12 +- **fix(security):** redact NVIDIA_API_KEY at the log sink in the ad-hoc diag script ([#11639](https://github.com/diegosouzapw/OmniRoute/pull/11639)) +- **fix(types):** clear the two type regressions red on release/v3.8.51 ([#11640](https://github.com/diegosouzapw/OmniRoute/pull/11640)) — thanks @jonlwheat2-gif +- **fix(build):** resolve workspace: protocol dependencies to concrete semver versions during prepublish (#11540) ([#11641](https://github.com/diegosouzapw/OmniRoute/pull/11641)) — thanks @HouMinXi +- **fix(search):** properly resolve configured search connection in /v1/responses pipeline (#11524) ([#11642](https://github.com/diegosouzapw/OmniRoute/pull/11642)) — thanks @HouMinXi +- **fix(authz):** trust cf-connecting-ip for via-proxy detection when peer is Cloudflare edge (#11514) ([#11643](https://github.com/diegosouzapw/OmniRoute/pull/11643)) — thanks @HouMinXi +- **fix(security):** compare the OIDC state cookie in constant time ([#11646](https://github.com/diegosouzapw/OmniRoute/pull/11646)) +- **fix(security):** namespace the dedup hash by the calling API key ([#11649](https://github.com/diegosouzapw/OmniRoute/pull/11649)) +- **fix(models):** gate the Codex-native catalog loop on prefix mode (#11632) ([#11662](https://github.com/diegosouzapw/OmniRoute/pull/11662)) — thanks @xiaoyaner0201 +- **fix(dashboard):** project Codex routing quota in account pools ([#11666](https://github.com/diegosouzapw/OmniRoute/pull/11666)) — thanks @KaspaPulse +- **fix(ddgw):** normaliza system no upstream da release v3.8.51 ([#11692](https://github.com/diegosouzapw/OmniRoute/pull/11692)) — thanks @RhianB14 +- **fix(memory):** honor category filter in GET /api/memory ([#11699](https://github.com/diegosouzapw/OmniRoute/pull/11699)) — thanks @Sabeekhann +- **fix(ci):** detect stale workspace lockfile entries ([#11714](https://github.com/diegosouzapw/OmniRoute/pull/11714)) — thanks @pacocartones +- **fix(ci):** stamp dist/BUILD_SHA before the npm publish provenance gate ([#11721](https://github.com/diegosouzapw/OmniRoute/pull/11721)) +- **fix(docker):** decouple the best-effort Bun image from the release manifest ([#11724](https://github.com/diegosouzapw/OmniRoute/pull/11724)) +- **perf(compression):** avoid cloning memo misses twice ([#11727](https://github.com/diegosouzapw/OmniRoute/pull/11727)) — thanks @RaviTharuma +- **fix(docker):** let the bun digest artifact be absent, not fatal ([#11740](https://github.com/diegosouzapw/OmniRoute/pull/11740)) +- **fix(shared):** block cloud-metadata hosts under default remote-image guard ([#11755](https://github.com/diegosouzapw/OmniRoute/pull/11755)) — thanks @santosraju99-hub +- **fix(api):** preserve caller-provided X-Correlation-Id on chat completions ([#11760](https://github.com/diegosouzapw/OmniRoute/pull/11760)) — thanks @aniruddhaadak80 +- **fix(resilience):** lock permanently retired models instead of short backoff (Gemini ban prevention) ([#11762](https://github.com/diegosouzapw/OmniRoute/pull/11762)) — thanks @turbolego +- **fix:** mark Vercel AI Gateway as passthroughModels ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **fix(resilience):** stop hammering permanently-moved endpoints and billing-suspended accounts ([#11774](https://github.com/diegosouzapw/OmniRoute/pull/11774)) — thanks @turbolego +- **fix(lease):** remove global static reservation and gate routing on live active lease occupancy ([#11775](https://github.com/diegosouzapw/OmniRoute/pull/11775)) — thanks @TheDemonTuan +- **fix(ci):** size the install-upgrade gate to a measured run, and log the pack cost ([#11776](https://github.com/diegosouzapw/OmniRoute/pull/11776)) +- **fix(pwa):** stop serving the stale shell after deploys ([#11779](https://github.com/diegosouzapw/OmniRoute/pull/11779)) — thanks @HouMinXi +- **fix(combo):** lock GitHub models rejected as "not supported" for future requests ([#11781](https://github.com/diegosouzapw/OmniRoute/pull/11781)) — thanks @turbolego +- **fix(cli):** probe both IPv4 and IPv6 loopback for server readiness (#11766) ([#11794](https://github.com/diegosouzapw/OmniRoute/pull/11794)) — thanks @vermasomesh835 +- **fix(sse):** preserve CLIProxyAPI wrappers per connection ([#11797](https://github.com/diegosouzapw/OmniRoute/pull/11797)) — thanks @RaviTharuma +- **fix(providers):** honor model auto-fetch opt-in on create ([#11798](https://github.com/diegosouzapw/OmniRoute/pull/11798)) — thanks @RaviTharuma +- **fix(dashboard):** make model auto-fetch order independent ([#11805](https://github.com/diegosouzapw/OmniRoute/pull/11805)) — thanks @RaviTharuma +- **fix(docker):** make GHA cache exports best effort ([#11806](https://github.com/diegosouzapw/OmniRoute/pull/11806)) — thanks @RaviTharuma +- **fix(kiro):** do not permanently ban on 'User is not authorized to make this call' ([#11809](https://github.com/diegosouzapw/OmniRoute/pull/11809)) — thanks @Deftera186 +- **fix(sse):** keep cache-write tokens in OpenAI-shaped usage ([#11814](https://github.com/diegosouzapw/OmniRoute/pull/11814)) — thanks @TheDemonTuan +- **fix(docs):** resolve relative markdown and wiki links across Fumadocs and GitHub wiki ([#11834](https://github.com/diegosouzapw/OmniRoute/pull/11834)) — thanks @AndrianBalanescu +- **fix(chat):** preserve unstripped model string for passthrough provider routing ([#11840](https://github.com/diegosouzapw/OmniRoute/pull/11840)) — thanks @raheemuddin786 +- **fix(mcp):** avoid async initialization inside sync esm wrappers (#11569) ([#11849](https://github.com/diegosouzapw/OmniRoute/pull/11849)) — thanks @HouMinXi +- **fix(sse):** preserve coherent markdown token boundaries across stream translation chunks (#11606) ([#11851](https://github.com/diegosouzapw/OmniRoute/pull/11851)) — thanks @HouMinXi +- **fix(ci):** run the install-upgrade gate on disk, not on the /tmp tmpfs ([#11855](https://github.com/diegosouzapw/OmniRoute/pull/11855)) +- **fix(skills):** expand shorthand property types in injected tool schemas ([#11857](https://github.com/diegosouzapw/OmniRoute/pull/11857)) — thanks @alvinveroy +- **fix(ci):** publish npm from a hosted runner so provenance is accepted ([#11877](https://github.com/diegosouzapw/OmniRoute/pull/11877)) +- **fix(lint):** pin react hooks plugin to 7.0.1 ([#11886](https://github.com/diegosouzapw/OmniRoute/pull/11886)) +- **fix(ci):** surface and clear release-green ESLint failure ([#11890](https://github.com/diegosouzapw/OmniRoute/pull/11890)) — thanks @MumuTW +- **fix(guardrails):** prevent duplicate prompt-injection-guard log output ([#11936](https://github.com/diegosouzapw/OmniRoute/pull/11936)) — thanks @patrykkopycinski +- **fix(auth):** downgrade expected transient states from warn to debug ([#11937](https://github.com/diegosouzapw/OmniRoute/pull/11937)) — thanks @patrykkopycinski +- **fix(codex):** fail over image generation for imported free plans ([#11948](https://github.com/diegosouzapw/OmniRoute/pull/11948)) — thanks @Bl0ck154 +- **fix(codex):** normalize non-stream responses ([#11951](https://github.com/diegosouzapw/OmniRoute/pull/11951)) — thanks @Bl0ck154 +- **fix(images):** forward Antigravity image size ([#11952](https://github.com/diegosouzapw/OmniRoute/pull/11952)) — thanks @Bl0ck154 +- **fix(db):** invalidate connection cache after upsert ([#11953](https://github.com/diegosouzapw/OmniRoute/pull/11953)) — thanks @Bl0ck154 +- **fix(codex):** restore imported account state ([#11954](https://github.com/diegosouzapw/OmniRoute/pull/11954)) — thanks @Bl0ck154 +- **fix(sse):** give extended-thinking targets the reasoning readiness budget ([#11959](https://github.com/diegosouzapw/OmniRoute/pull/11959)) — thanks @abhisheksharma2411 +- **fix(dashboard):** use opaque background and readable text color on cost chart tooltips ([#11960](https://github.com/diegosouzapw/OmniRoute/pull/11960)) — thanks @ZaimMarzuki +- **fix(antigravity):** send complete loadCodeAssist metadata (ideType/platform/pluginType as numeric enums) ([#11969](https://github.com/diegosouzapw/OmniRoute/pull/11969)) — thanks @rifqiawl +- **fix(core):** resolve DB init race condition and reasoning translation ([#12003](https://github.com/diegosouzapw/OmniRoute/pull/12003)) — thanks @raheemuddin786 +- **fix(admission):** exclude reclaimable page cache from the cgroup pressure ratio ([#12017](https://github.com/diegosouzapw/OmniRoute/pull/12017)) — thanks @HouMinXi +- **fix(build):** prune non-production trees in NFT trace excludes and tsconfig ([#12028](https://github.com/diegosouzapw/OmniRoute/pull/12028)) — thanks @Chewji9875 +- **fix(migrations):** add renamed migration compatibility for 056/073/077/101 ([#12036](https://github.com/diegosouzapw/OmniRoute/pull/12036)) — thanks @oyi77 +- **fix(sse):** default crash-guard logger to console.warn, not console ([#12042](https://github.com/diegosouzapw/OmniRoute/pull/12042)) — thanks @HouMinXi +- **fix(catalog):** derive vision/modalities for built-in auto combos from effective target pool ([#12046](https://github.com/diegosouzapw/OmniRoute/pull/12046)) — thanks @Prajeeth-12 +- **fix(release):** the packaged-app smoke verifies the database opened, not a driver line the primary path never prints (twin of #12032) ([#12047](https://github.com/diegosouzapw/OmniRoute/pull/12047)) +- **fix(barrel):** migrate src/app/ off the @/lib/localDb barrel import (#11795 Phase 2) ([#12051](https://github.com/diegosouzapw/OmniRoute/pull/12051)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate src/lib/ off the localDb barrel to direct db imports (#59) ([#12052](https://github.com/diegosouzapw/OmniRoute/pull/12052)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate open-sse, src/shared, src/sse, src/models, src/domain off the @/lib/localDb barrel import (#11795 Phase 4) ([#12053](https://github.com/diegosouzapw/OmniRoute/pull/12053)) — thanks @jonlwheat2-gif +- **fix(barrel):** delete the @/lib/localDb barrel — every consumer migrated (#11795 Phase 5) ([#12055](https://github.com/diegosouzapw/OmniRoute/pull/12055)) — thanks @jonlwheat2-gif +- **fix(radar):** restore D12 public boundary ([#12057](https://github.com/diegosouzapw/OmniRoute/pull/12057)) +- **fix(dev):** bound webpack and Tailwind scans ([#12075](https://github.com/diegosouzapw/OmniRoute/pull/12075)) — thanks @backryun +- **fix(dev):** isolate root layout settings reads ([#12076](https://github.com/diegosouzapw/OmniRoute/pull/12076)) — thanks @backryun +- **fix(dev):** reduce instrumentation executor fan-out (phase 3) ([#12078](https://github.com/diegosouzapw/OmniRoute/pull/12078)) — thanks @backryun +- **fix(dev):** make logging resources HMR-singleton ([#12079](https://github.com/diegosouzapw/OmniRoute/pull/12079)) — thanks @backryun +- **fix(sse):** keep unavailable forced connections scoped ([#12080](https://github.com/diegosouzapw/OmniRoute/pull/12080)) — thanks @keeltrace +- **fix(dev):** isolate batch dispatch from instrumentation ([#12081](https://github.com/diegosouzapw/OmniRoute/pull/12081)) — thanks @backryun +- **fix(ci):** reconcile release test contract drift ([#12082](https://github.com/diegosouzapw/OmniRoute/pull/12082)) — thanks @backryun +- **fix(ci):** accept CVE-2025-68121 in the prebuilt tls-client .so, auto-close base-red issues, guard Scorecard on the default branch ([#12085](https://github.com/diegosouzapw/OmniRoute/pull/12085)) +- **fix(db):** drop three consumer-less 1proxy exports — dead-code base-red on release/v3.8.51 after the barrel deletion (#12055) ([#12087](https://github.com/diegosouzapw/OmniRoute/pull/12087)) +- **fix(api):** clear the six API-route TypeScript regressions the new gate landed red on ([#12094](https://github.com/diegosouzapw/OmniRoute/pull/12094)) +- **fix(release):** never let the tag-push Create Release append auto notes to the curated body ([#12096](https://github.com/diegosouzapw/OmniRoute/pull/12096)) +- **fix(sse):** honor CLIProxyAPI environment API key ([#12099](https://github.com/diegosouzapw/OmniRoute/pull/12099)) — thanks @RaviTharuma +- **fix(oauth):** bind Google refresh to the client that issued the token ([#12106](https://github.com/diegosouzapw/OmniRoute/pull/12106)) — thanks @HouMinXi +- **fix(dashboard):** make RequestLoggerDetail loadable outside Next — CSS via globals.css + CJS/ESM interop (#11703 base-reds) ([#12114](https://github.com/diegosouzapw/OmniRoute/pull/12114)) +- **fix(leases):** project status lease row to lease columns so joined connection PII never escapes ([#12115](https://github.com/diegosouzapw/OmniRoute/pull/12115)) — thanks @geek007git +- **fix(plugins):** do not kill the plugin process when a fire-and-forget hook times out ([#12116](https://github.com/diegosouzapw/OmniRoute/pull/12116)) — thanks @geek007git +- **fix(guardrails):** restore injection-guard logging on middleware-only routes ([#12117](https://github.com/diegosouzapw/OmniRoute/pull/12117)) — thanks @geek007git +- **fix(plugins):** refresh stored manifest from disk on activate so new hook fields reach existing installs ([#12120](https://github.com/diegosouzapw/OmniRoute/pull/12120)) — thanks @geek007git +- **fix(codex):** preserve existing provider state when bulk-import upserts a matching connection ([#12122](https://github.com/diegosouzapw/OmniRoute/pull/12122)) — thanks @geek007git +- **fix(ci):** clear the two base-reds the 2026-08-30 merge batch left on release/v3.8.51 (round 3) ([#12123](https://github.com/diegosouzapw/OmniRoute/pull/12123)) +- **fix(sse):** strip type:'custom' from Claude tools on agentrouter dispatch ([#12126](https://github.com/diegosouzapw/OmniRoute/pull/12126)) — thanks @wahidsadik371-coder +- **fix(sse):** stop advertising video providers the dispatcher cannot run ([#12131](https://github.com/diegosouzapw/OmniRoute/pull/12131)) — thanks @quiterunner-commits +- **fix(search):** treat HTTP 432 and plan limit errors as transient cooldown ([#12139](https://github.com/diegosouzapw/OmniRoute/pull/12139)) — thanks @HouMinXi +- **fix(sse):** refuse an AI Horde queue that cannot fit the request budget ([#12143](https://github.com/diegosouzapw/OmniRoute/pull/12143)) — thanks @quiterunner-commits +- **fix(ci):** clear the base-reds the afternoon merge batch left on release/v3.8.51 (round 5: provider count 352, TS2554/TS2677) ([#12144](https://github.com/diegosouzapw/OmniRoute/pull/12144)) +- **fix(translator):** strip neutral tool_choice when tools absent in Responses-to-Chat (#12141) ([#12166](https://github.com/diegosouzapw/OmniRoute/pull/12166)) — thanks @HouMinXi +- **fix(guardrails):** pass providerId to getResolvedModelCapabilities in checkComboVision (#12112) ([#12169](https://github.com/diegosouzapw/OmniRoute/pull/12169)) — thanks @HouMinXi +- **fix(translator):** strip plaintext reasoning content for opaque responses backends (#12128) ([#12171](https://github.com/diegosouzapw/OmniRoute/pull/12171)) — thanks @HouMinXi +- **perf(stream):** compile hot-path regexes once, bound token caches, fix quadratic buffering ([#12179](https://github.com/diegosouzapw/OmniRoute/pull/12179)) — thanks @opensource-elearning +- **fix:** resolve compression worker file using runtime anchors instead of… ([#12183](https://github.com/diegosouzapw/OmniRoute/pull/12183)) — thanks @NightStalker-87 +- **fix:** prevent Claude Code session kills via liveness-aware readiness + auto model echo ([#12189](https://github.com/diegosouzapw/OmniRoute/pull/12189)) — thanks @opensource-elearning +- **fix(translator):** preserve falsy primitive values in Gemini and Antigravity function response results ([#12191](https://github.com/diegosouzapw/OmniRoute/pull/12191)) — thanks @Siva010 +- **fix(oauth):** mark empty Antigravity projectId as degraded and clear stale errors (#11284) ([#12205](https://github.com/diegosouzapw/OmniRoute/pull/12205)) — thanks @HouMinXi +- **fix(providers):** gate the Codex auto-ping usage read on the shared quota throttle ([#12209](https://github.com/diegosouzapw/OmniRoute/pull/12209)) — thanks @abhisheksharma2411 +- **fix(combos):** prioritize SQLite row id over inner JSON id and notify delete errors ([#12213](https://github.com/diegosouzapw/OmniRoute/pull/12213)) — thanks @HouMinXi +- **fix(providers):** mark featherless.ai as no longer having a free tier (`hasFree: false`) ([#12216](https://github.com/diegosouzapw/OmniRoute/pull/12216)) — thanks @turbolego +- **fix(deepseek-web):** stop Turbopack dev panic in the PoW worker path resolver ([#12221](https://github.com/diegosouzapw/OmniRoute/pull/12221)) — thanks @hartmark +- **fix(chat-admission):** clarify local 503 source ([#12223](https://github.com/diegosouzapw/OmniRoute/pull/12223)) — thanks @keeltrace +- **fix(dev):** silence webpack runtime module warnings ([#12228](https://github.com/diegosouzapw/OmniRoute/pull/12228)) — thanks @backryun +- **fix(db):** use module.require for CommonJS runtime driver loading ([#12230](https://github.com/diegosouzapw/OmniRoute/pull/12230)) — thanks @raheemuddin786 +- **fix(memory):** honest probe-driven FTS5 keyword status + memory_id rowid sync ([#12231](https://github.com/diegosouzapw/OmniRoute/pull/12231)) — thanks @raheemuddin786 +- **fix(system):** propagate abort signal to stream reader in HTTP version checks ([#12232](https://github.com/diegosouzapw/OmniRoute/pull/12232)) — thanks @raheemuddin786 +- **fix(resilience):** isolate local host execution errors from provider circuit breakers ([#12233](https://github.com/diegosouzapw/OmniRoute/pull/12233)) — thanks @raheemuddin786 +- **fix(sse):** sort injected tools deterministically for prompt caching ([#12234](https://github.com/diegosouzapw/OmniRoute/pull/12234)) — thanks @raheemuddin786 +- **fix(combo):** always clear the loop-safety timer, not just on the happy path (#11804) ([#12245](https://github.com/diegosouzapw/OmniRoute/pull/12245)) +- **fix(quality):** base-red pair — stryker tap registration + turn-pin suites aligned to the window gate ([#12255](https://github.com/diegosouzapw/OmniRoute/pull/12255)) — thanks @backryun +- **fix(dev):** qualify Turbopack runtime boundaries ([#12258](https://github.com/diegosouzapw/OmniRoute/pull/12258)) — thanks @backryun +- **fix(combo):** fall back to full pool when collapsed sole survivor is context-too-small ([#12278](https://github.com/diegosouzapw/OmniRoute/pull/12278)) — thanks @opensource-elearning +- **fix(combo):** bound the pre-dispatch unavailable skip so a stale label cannot dark a pool (#12168) ([#12285](https://github.com/diegosouzapw/OmniRoute/pull/12285)) +- **fix(security):** strip Qwen/Alibaba console-session cookies from provider API responses ([#12287](https://github.com/diegosouzapw/OmniRoute/pull/12287)) +- **fix(usage):** console-aware Token Plan guidance + subscription hint on bailian 401 ([#12288](https://github.com/diegosouzapw/OmniRoute/pull/12288)) +- **fix(resourcePressure):** log numeric detail on every rejection, recover faster ([#12293](https://github.com/diegosouzapw/OmniRoute/pull/12293)) — thanks @hartmark +- **fix(release):** drain the 2026-09-01 base-red window — passthrough usage regression + radarPage i18n keys ([#12327](https://github.com/diegosouzapw/OmniRoute/pull/12327)) +- **fix(ci):** openapi-security-tiers checker must honor routeGuard patterns + imported prefixes ([#12350](https://github.com/diegosouzapw/OmniRoute/pull/12350)) — thanks @ggiak +- **perf(ci):** cache node_modules in the npm-ci-retry composite (#8084 D3) ([#12408](https://github.com/diegosouzapw/OmniRoute/pull/12408)) +- **revert(ui):** point CTAs back at their real destinations ([#12410](https://github.com/diegosouzapw/OmniRoute/pull/12410)) +- **fix(release):** drain the 2026-09-02 base-red — rerank-providers import + api-typecheck baseline ratchet ([#12414](https://github.com/diegosouzapw/OmniRoute/pull/12414)) +- **fix(types):** make system prompt injection noImplicitAny-safe ([#12416](https://github.com/diegosouzapw/OmniRoute/pull/12416)) +- **fix(quality):** validate typecheck baseline schema ([#12419](https://github.com/diegosouzapw/OmniRoute/pull/12419)) +- **fix(memory):** resolve rerank provider node cache import ([#12421](https://github.com/diegosouzapw/OmniRoute/pull/12421)) +- **fix(ci):** repair release-root regressions — pack dedup, web-session syntax, uc-image ids ([#12423](https://github.com/diegosouzapw/OmniRoute/pull/12423)) — thanks @backryun +- **fix(providers):** separate MaxAI and UC credential contracts ([#12431](https://github.com/diegosouzapw/OmniRoute/pull/12431)) +- **fix(cli):** preserve Claude settings on config set ([#12432](https://github.com/diegosouzapw/OmniRoute/pull/12432)) — thanks @KrzysiekSko +- **fix(providers):** repair the maxai credential block truncated by merge auto-resolve ([#12433](https://github.com/diegosouzapw/OmniRoute/pull/12433)) +- **fix(quality):** record the 2026-09-02 merged growth in the file-size baseline ([#12434](https://github.com/diegosouzapw/OmniRoute/pull/12434)) +- **fix(uc):** route UC error strings through sanitizeErrorMessage; allowlist the retired codex id ([#12437](https://github.com/diegosouzapw/OmniRoute/pull/12437)) +- **fix(adapta):** redact streamed upstream errors ([#12438](https://github.com/diegosouzapw/OmniRoute/pull/12438)) +- **fix(sse):** remap non-contiguous upstream tool_calls index to a gap-free output_index ([#12445](https://github.com/diegosouzapw/OmniRoute/pull/12445)) — thanks @hartmark +- **fix(quality):** detect duplicate tool_calls entries in one response ([#12446](https://github.com/diegosouzapw/OmniRoute/pull/12446)) — thanks @hartmark +- **fix(conversations):** resolve turn content OmniRoute never sends back to the client ([#12447](https://github.com/diegosouzapw/OmniRoute/pull/12447)) — thanks @hartmark +- **fix(combo):** do not treat credits-exhausted 401 as auth skip ([#12449](https://github.com/diegosouzapw/OmniRoute/pull/12449)) — thanks @RaviTharuma +- **fix(auth):** do not park healthy quota accounts as expired ([#12452](https://github.com/diegosouzapw/OmniRoute/pull/12452)) — thanks @RaviTharuma +- **fix(responses-continuation):** fail closed on a collector-truncated, empty output array ([#12460](https://github.com/diegosouzapw/OmniRoute/pull/12460)) — thanks @hartmark +- **fix(logging):** raise the SSE payload collector's default cap ([#12461](https://github.com/diegosouzapw/OmniRoute/pull/12461)) — thanks @hartmark +- **fix(resilience):** surface Responses failed.error.message in 502s ([#12472](https://github.com/diegosouzapw/OmniRoute/pull/12472)) — thanks @RaviTharuma +- **fix(security):** unbiased maxai X-Random nonce + stricter URL/regex assertions ([#12502](https://github.com/diegosouzapw/OmniRoute/pull/12502)) +- **fix(video):** re-anchor transcript log-redaction so PII/credential maskers can't reopen the leak (#12150 P1 follow-up) ([#12503](https://github.com/diegosouzapw/OmniRoute/pull/12503)) +- **fix(monitoring):** serve cached credentialHealth off the request path ([#12533](https://github.com/diegosouzapw/OmniRoute/pull/12533)) — thanks @RaviTharuma +- **fix(quota):** keep Antigravity Gemini usable when Claude weekly is empty ([#12566](https://github.com/diegosouzapw/OmniRoute/pull/12566)) — thanks @HouMinXi +- **fix(authz):** hard-gate every credential export and CLI-config write (GHSA-5926-2w35-7h4q) ([#12600](https://github.com/diegosouzapw/OmniRoute/pull/12600)) +- **fix(docs):** restore the Next build — REMOVED_PROVIDERS.md had no frontmatter (base-red #12581) ([#12610](https://github.com/diegosouzapw/OmniRoute/pull/12610)) +- **fix(quality):** re-point the zcodeProtocol public-creds allowlist to line 313 ([#12615](https://github.com/diegosouzapw/OmniRoute/pull/12615)) +- **fix(security):** close 3 advisories — search baseUrl exfil, sk- in the error sanitizer, bifrost relay header leak ([#12620](https://github.com/diegosouzapw/OmniRoute/pull/12620)) +- **fix(providers):** sanitize boolean required and nested bare maps for Gemini (#12269) ([#12624](https://github.com/diegosouzapw/OmniRoute/pull/12624)) — thanks @HouMinXi +- **fix(resilience):** keep Overloaded STREAM_EARLY_EOF off the provider breaker ([#12626](https://github.com/diegosouzapw/OmniRoute/pull/12626)) — thanks @HouMinXi +- **fix(catalog):** re-audit free-tier quotas against official pages ([#12649](https://github.com/diegosouzapw/OmniRoute/pull/12649)) +- **fix(dashboard):** keep a request's pending-tracking id stable across combo target retries ([#12650](https://github.com/diegosouzapw/OmniRoute/pull/12650)) — thanks @hartmark +- **fix(tests):** retire dead model ids from the chat-pipeline integration suite (base-red #12581) ([#12670](https://github.com/diegosouzapw/OmniRoute/pull/12670)) +- **fix:** resolve SqliteError no such table compression_run_telemetry during cleanup ([#12682](https://github.com/diegosouzapw/OmniRoute/pull/12682)) — thanks @groovecityJO +- **fix(combo):** treat a pin-only step as implicit connection allowlist ([#12697](https://github.com/diegosouzapw/OmniRoute/pull/12697)) — thanks @HouMinXi +- **fix(ci):** ship .npmrc in published package so legacy-peer-deps applies to consumers (#11544) ([#12699](https://github.com/diegosouzapw/OmniRoute/pull/12699)) — thanks @KooshaPari +- **fix(docker):** pin 4 CLI tools to exact versions (#12576) ([#12703](https://github.com/diegosouzapw/OmniRoute/pull/12703)) — thanks @KooshaPari +- **fix(docker):** document and harden cli profile trust boundary (#12570) ([#12706](https://github.com/diegosouzapw/OmniRoute/pull/12706)) — thanks @KooshaPari +- **fix(sse):** redact video transcript in pre-guardrail rejected-request logs (#12150 P2 item 7) ([#12710](https://github.com/diegosouzapw/OmniRoute/pull/12710)) +- **fix(i18n):** quote placeholder in OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES description (#12505) ([#12769](https://github.com/diegosouzapw/OmniRoute/pull/12769)) — thanks @KooshaPari +- **fix(api/pricing):** surface validation error message as string, not raw object (#12494) ([#12771](https://github.com/diegosouzapw/OmniRoute/pull/12771)) — thanks @KooshaPari +- **fix(sse):** re-enable prompt compression for native Codex passthrough ([#12834](https://github.com/diegosouzapw/OmniRoute/pull/12834)) — thanks @thomasmaerz +- **fix(dashboard):** model health tests for a provider node set to the Responses API now call `/v1/responses` with a Responses-shaped body instead of `/v1/chat/completions` — those models were reported as `Provider returned HTTP 200 but no text content` even though the same model answered normally through `/v1/responses` ([#13070](https://github.com/diegosouzapw/OmniRoute/issues/13070)) ([#13087](https://github.com/diegosouzapw/OmniRoute/pull/13087)) — thanks @ntdatt812 +- fix(routing): stop a round-robin combo's "opencode" targets from collapsing onto the opencode-zen connection (#11912) ([#13283](https://github.com/diegosouzapw/OmniRoute/pull/13283)) +- fix(routing): stop the reactive-compaction debug log from lying when compression is globally disabled (#11977) ([#13276](https://github.com/diegosouzapw/OmniRoute/pull/13276)) +- fix(electron): relativize standalone-bundle symlink targets so Stage 8 manifest verification stops failing on Windows (#11979) ([#13251](https://github.com/diegosouzapw/OmniRoute/pull/13251)) +- fix(dashboard): surface a visible error when Compression Studio's combined preview run fails (#12061) ([#13288](https://github.com/diegosouzapw/OmniRoute/pull/13288)) +- fix(dashboard): make the compression "Effective pipeline" preview honor the active profile and warn when the master switch is off (#12063) ([#13287](https://github.com/diegosouzapw/OmniRoute/pull/13287)) +- fix(providers): scope TinyCMS Web signer's DOM shims to each call instead of leaking them for the process lifetime, and surface a clean HTTP status on a non-JSON interception-toggles error (#12072) ([#13254](https://github.com/diegosouzapw/OmniRoute/pull/13254)) +- fix(guardrails): stop Vision Bridge from re-selecting a model locked after a 404 (#12111) ([#13259](https://github.com/diegosouzapw/OmniRoute/pull/13259)) +- fix(sse): require Responses-shaped body before native OpenAI-compatible passthrough (#12129) ([#13278](https://github.com/diegosouzapw/OmniRoute/pull/13278)) +- fix(providers): minimax-m3 now collapses manual thinking.type:"enabled" to adaptive, preventing upstream 400 (2013) (#12132) ([#13249](https://github.com/diegosouzapw/OmniRoute/pull/13249)) +- fix(db): scope model visibility overrides by modality so hiding a Chat model no longer hides an identically-ID'd Image/Embeddings/etc. model (#12172) ([#13258](https://github.com/diegosouzapw/OmniRoute/pull/13258)) +- fix(db): scope local-provider apiKey dedup to matching base URL so LM Studio/Ollama-style connections support multiple accounts (#12173) ([#13250](https://github.com/diegosouzapw/OmniRoute/pull/13250)) +- fix(providers): refresh Trae's stale Referer/Origin and forward user timezone so imported connections stop failing with 401 (#12190) ([#13255](https://github.com/diegosouzapw/OmniRoute/pull/13255)) +- fix(providers): route opencode-go/gpt-5.6-luna to /responses instead of /chat/completions (#12196) ([#13275](https://github.com/diegosouzapw/OmniRoute/pull/13275)) +- fix(dashboard): allow deleting the last extra-upstream-header row even when invalid (#12251) ([#13252](https://github.com/diegosouzapw/OmniRoute/pull/13252)) +- **fix(i18n):** translate pre-existing `__MISSING__:` keys for `combo.sort`, `requestLogger.detail` expand/collapse, `common.profile`, and `settings.resilienceCredentialHealth*` across 39 locales ([#12272](https://github.com/diegosouzapw/OmniRoute/issues/12272)) ([#13034](https://github.com/diegosouzapw/OmniRoute/pull/13034)) — thanks @HouMinXi +- fix(cli): run the Node.js runtime compatibility guard before the heavy `tsx/esm` + Commander import chain so an unsupported runtime gets a clear message instead of a raw `Invalid regular expression flags` crash (#12296) ([#13253](https://github.com/diegosouzapw/OmniRoute/pull/13253)) +- fix(dashboard): refresh the providers list after deleting a compatible provider node (#12298) ([#13256](https://github.com/diegosouzapw/OmniRoute/pull/13256)) +- fix(usage): fail closed on API-key budget enforcement when a provider's `auto` routing alias has no pricing row, instead of silently counting it as $0 (#12341) ([#13257](https://github.com/diegosouzapw/OmniRoute/pull/13257)) +- **fix(providers):** include the submitted Agnes video model when polling by `video_id` ([#12356](https://github.com/diegosouzapw/OmniRoute/pull/12356)) — thanks @pacocartones +- **fix(routing):** custom OpenAI-compatible nodes now honor the saved Chat/Responses API type after edits instead of letting the node's original ID prefix override the live connection setting ([#11884](https://github.com/diegosouzapw/OmniRoute/issues/11884)). ([#12358](https://github.com/diegosouzapw/OmniRoute/pull/12358)) — thanks @pacocartones +- fix(sse): surface an error instead of a silent empty 200 when a Claude stream closes with zero bytes (#12398) ([#13285](https://github.com/diegosouzapw/OmniRoute/pull/13285)) +- fix(oauth): warn before the dead localhost:8080 redirect in antigravity/gemini `oauth start` (#12413) ([#13265](https://github.com/diegosouzapw/OmniRoute/pull/13265)) +- fix(providers): stop devin-cli spawn error from double-closing the SSE controller (#12517) ([#13261](https://github.com/diegosouzapw/OmniRoute/pull/13261)) +- **fix(rerank):** clamp Voyage `top_k` to the documents actually sent after empty-string filtering, and honor `return_documents: false` in the NVIDIA response adapter (#12523 — thanks @pacocartones) +- **fix(audio):** `/v1/audio/translations` now resolves combo names the way `/v1/audio/transcriptions` already does, so a combo that `GET /v1/models` advertises is fanned out to its targets instead of being rejected with `400 Invalid translation model: . Use format: provider/model`; literal `provider/model` ids and unknown bare names behave as before (#12536 — thanks @pacocartones) +- **fix(gemini):** strip the JSON-Schema-2020-12 `prefixItems` keyword from Gemini tool schemas at every nesting level, so Claude Code tool definitions no longer fail with `400 Unknown name "prefixItems"` on Gemini models (#12540 — thanks @pacocartones) +- **fix(api-manager):** Expose an accessible loading status while API keys are fetched instead of an empty accessibility tree (#12541 — thanks @pacocartones) +- **fix(video):** Clamp `estimateJpegFrameBytes` at zero for padding-only payloads and build the three encode-side frame data URIs from `JPEG_FRAME_DATA_URI_PREFIX` instead of a repeated literal (#12543 — thanks @pacocartones) +- **fix(devin):** accept Windows `DEVIN_AGENTIC_HOME` sandbox paths (`C:\...\.sandbox\...`) in the isolated-home check so the Devin Claude Bridge no longer fails closed on Windows ([#12405](https://github.com/diegosouzapw/OmniRoute/issues/12405)) (#12545 — thanks @pacocartones) +- **fix(db):** Add `conversation_turn_nodes` and orphaned `agentic_conversations` to the auto-cleanup cycle under the existing `retention.callLogs` window, so identity nodes whose call-log content has already been purged no longer accumulate without bound in `storage.sqlite` (#12548 — thanks @pacocartones) +- **fix(i18n):** Wrap the `~/.claude/profiles//settings.json` placeholder in ICU single quotes in the `OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES` feature-flag description across all 42 locales and the TypeScript default, so next-intl no longer fails with `INVALID_MESSAGE: UNCLOSED_TAG` and the Feature Flags card shows the description instead of the raw key (#12549 — thanks @pacocartones) +- **fix(orchestration):** `updateCloudAgentTask` now publishes the task's real `status` on `agent.task.updated` when an update only touches `result`, `activities` or `error`, instead of the fabricated `"updated"` state, and stays silent when no row matched the id (#12550 — thanks @pacocartones) +- **fix(i18n):** the home "Recent Requests" panel and the Provider Topology legend are now translated instead of rendering English copies on non-English dashboards; the legend reads its own `home.topologyLegend*` labels with consistent casing rather than borrowing the memory-settings "Recent" and analytics "Error" strings (#12551 — thanks @pacocartones). +- **docs(reference):** bring the `FEATURE_FLAGS.md` catalog back to 1:1 with `featureFlagDefinitions.ts` — 20 missing flags added, the two `*_BLOCK_THRESHOLD` env-only knobs moved out of the flag tables, category/total counts and the Live WS port corrected, guarded by a static test (#12552 — thanks @pacocartones) +- **fix(i18n):** backfill missing `usage.kiloPass*` strings in 39 locales and restore `featureFlagOmnirouteDisableThinkingLevelVariantsDescription` in `pt.json` ([#12561](https://github.com/diegosouzapw/OmniRoute/issues/12561)) ([#13027](https://github.com/diegosouzapw/OmniRoute/pull/13027)) — thanks @HouMinXi +- fix(docker): default docker-compose app ports (dashboard/API/live-WS) to loopback instead of `0.0.0.0`, closing the anonymous `/v1` LAN/WAN exposure gap left open by `REQUIRE_API_KEY=false` (#12568) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): close DNS-rebinding SSRF gap in webhook outbound-URL guard (#12569) ([#13243](https://github.com/diegosouzapw/OmniRoute/pull/13243)) +- fix(docker): require a per-session token on the VNC browser CDP bridge and isolate it on a dedicated Docker network (#12571) ([#13241](https://github.com/diegosouzapw/OmniRoute/pull/13241)) +- fix(open-sse): write Adobe Firefly session tokens and cookie jars with 0700/0600 permissions instead of the process umask (#12572) ([#13242](https://github.com/diegosouzapw/OmniRoute/pull/13242)) +- fix(api): remove hardcoded wildcard CORS in openai-to-gemini-sse.ts so the centralized fail-closed CORS gate is the sole source of `Access-Control-Allow-Origin` (#12573) ([#13239](https://github.com/diegosouzapw/OmniRoute/pull/13239)) +- fix(api): enforce API key policy (budget/rate-limit/schedule/endpoint scoping) on the ElevenLabs speech-to-text, text-to-speech and voices proxy routes (#12574) ([#13244](https://github.com/diegosouzapw/OmniRoute/pull/13244)) +- fix(sse): cap HuggingChat NDJSON body size and bound the read loop with the fetch timeout so a stalled or hostile upstream cannot buffer unbounded memory (#12577) ([#13282](https://github.com/diegosouzapw/OmniRoute/pull/13282)) +- fix(docker): scope the cliproxyapi/qdrant/bifrost sidecars to loopback by default and forward `CLIPROXYAPI_MANAGEMENT_KEY` into the cliproxyapi container so its management API is not left both unauthenticated and LAN-published (#12578) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): create DB export temp paths with `fs.mkdtempSync` instead of predictable timestamps (#12579) ([#13237](https://github.com/diegosouzapw/OmniRoute/pull/13237)) +- Cline 401 bodies that say "re-authenticate your Cline account" classify as a refreshable OAuth token, not a terminal expired key. The cooling panel no longer labels every cooldown as a 429; it shows the recorded last error instead. (#12594) ([#13060](https://github.com/diegosouzapw/OmniRoute/pull/13060)) — thanks @HouMinXi +- **fix(catalog):** degrade unknown combo targets instead of dropping LCD modalities, and persist OpenRouter `architecture.input_modalities` into the capability snapshot ([#12613](https://github.com/diegosouzapw/OmniRoute/issues/12613)) ([#13042](https://github.com/diegosouzapw/OmniRoute/pull/13042)) — thanks @HouMinXi +- fix(providers): send `x-api-key` instead of `Authorization: Bearer` for OpenCode Zen's `/v1/responses` endpoint (Muse Spark Contributor models), fixing a 401 on OmniRoute's auth header (#12633) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(sse): surface the actionable "Auggie CLI not found" message when the shell reports a missing `auggie` binary via exit code instead of a spawn error (#12645) ([#13263](https://github.com/diegosouzapw/OmniRoute/pull/13263)) +- **fix(config):** Persist deletions of built-in background-degradation entries — when a stored settings record exists its `degradationMap` is now authoritative instead of being merged under the defaults, so an entry the user removed in the dashboard no longer reappears on the next apply or restart ([#12424](https://github.com/diegosouzapw/OmniRoute/issues/12424)) ([#12647](https://github.com/diegosouzapw/OmniRoute/pull/12647)) — thanks @pacocartones +- **fix(gamification):** action-count badge milestones (First Token, Token Consumer, Token Machine, Token Whale, and the token-sharing tier) are now backed by a durable `xp_action_counts` counter incremented in `addXp()`, instead of a live `COUNT(*)` over `xp_audit_log`. The audit log is pruned by `retention.xpAuditLog` (default 30 days), so on a default install those "lifetime" milestones were really "actions in the last 30 days" and unlocked badges could stop unlocking once old rows aged out. `getActionCount()` and `checkActionCountBadges()` now read the same durable source, and a migration backfills existing totals from the surviving audit rows ([#12546](https://github.com/diegosouzapw/OmniRoute/issues/12546)) ([#12651](https://github.com/diegosouzapw/OmniRoute/pull/12651)) — thanks @pacocartones +- **fix(images):** `/v1/images/edits` now iterates a combo's targets the same way `/v1/images/generations` does (#9239) instead of flattening a bare combo to its first target. A combo whose first target is not edit-capable — or lacks credentials — now falls through to a later edit-capable target rather than hard-erroring, and missing credentials are skipped (not a hard `401`) to match the generations path. The per-target skip/terminal classification is extracted into a shared `runImageComboTargets` loop, so generations behavior is unchanged ([#12547](https://github.com/diegosouzapw/OmniRoute/issues/12547)). ([#12653](https://github.com/diegosouzapw/OmniRoute/pull/12653)) — thanks @pacocartones +- fix(sse): add first-byte watchdog to the TLS-fingerprint transport so a stalled wreq body falls back instead of hanging for minutes (#12656) ([#13272](https://github.com/diegosouzapw/OmniRoute/pull/13272)) +- fix(sse): exempt tiny-budget reasoning probes from combo quality failure and surface persisted-cooldown skips in ALL_TARGETS_SKIPPED diagnostics (#12659) ([#13269](https://github.com/diegosouzapw/OmniRoute/pull/13269)) +- fix(models): declare the real ~1M contextLength for OpenCode Zen's Muse Spark 1.2 models instead of falling back to the 200000 provider default (#12681) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(oauth): align codebuddy-cn OAuth User-Agent with the chat/usage CLI version to avoid WAF false positives (#12702) ([#13264](https://github.com/diegosouzapw/OmniRoute/pull/13264)) +- fix(dashboard): surface an authentication-required banner instead of silently blanking database settings for a guest session (#12709) ([#13270](https://github.com/diegosouzapw/OmniRoute/pull/13270)) +- **fix(resilience):** stop unbounded queue that hangs 6min until Aborted — gate, provider slot, and Bottleneck queue now share a per-connection `maxWaitMs` budget; `fail-closed` on exhaust (503); execution backstop `executionMaxWaitMs` overridable per connection with upstream clamp ([#12715](https://github.com/diegosouzapw/OmniRoute/pull/12715)) — thanks @maxmad64bis (with thanks to @Tushar49 for surfacing the slow-provider need in #12635) +- **fix(auto-combo):** every mode pack now carries `quality` and `reliability` weights (`quality` 0.02, 0.03 in `quality-first`; `reliability` 0.03, 0.04 in `reliability-first`), so selecting a pack no longer silences either signal; `DEFAULT_WEIGHTS` is unchanged ([#12731](https://github.com/diegosouzapw/OmniRoute/pull/12731)) — thanks @maxmad64bis +- **fix(sse):** the provider execution pipeline keeps the upstream error `code`/`type` again — both were lost when the non-streaming leg was extracted, so a config-class `422` (Antigravity missing project) degraded into a generic account cooldown instead of reaching the gate that recognises it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(db):** a legacy `call_logs` table no longer aborts startup — the provider-stats index is created after the column healing runs, not before it, so an install predating `request_type` opens instead of failing with `no such column` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(catalog):** a malformed row in the operator-writable custom-models blob no longer kills every `auto/*` pool ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(tests):** realign the guards that the same day's merges left asserting the old behavior — the provider-count total after GreenPT and EURouter, the GLM stream buffer slot that `#12925` turned into a declared parameter, and the injection scan bound that `#13104` reshaped from a head-only window into head-plus-tail ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- fix(cache): fold tool_choice/tools/response_format into the semantic cache signature so a cached tool_calls response can no longer be replayed for a request whose tool policy forbids it (#12734) ([#13267](https://github.com/diegosouzapw/OmniRoute/pull/13267)) +- **fix(models):** stop treating provider-supplied `isFree:true`/`:free`/`0/0` flags as trusted unless the provider has a documented free tier — only the shipped free-tier catalog decides otherwise; fetched `isFree:true` trusted only for a free-tier provider, custom `isFree:true` via a trusted path ([#12744](https://github.com/diegosouzapw/OmniRoute/pull/12744)) — thanks @maxmad64bis +- fix(memory): authenticate the internal /v1/rerank loopback call so memory reranking no longer silently degrades to unranked order when REQUIRE_API_KEY=true (#12745) ([#13268](https://github.com/diegosouzapw/OmniRoute/pull/13268)) +- fix(sse): parse Ollama Cloud's current usage markup (`$X of $Y used` aria-label, nested width style) (#12749) ([#13260](https://github.com/diegosouzapw/OmniRoute/pull/13260)) +- fix(cli): setup-opencode no longer sends an active context's management token to `/v1/models` when `--api-key`/`OMNIROUTE_API_KEY` is supplied — an explicit flag or the env var now always outranks the context's token, and the flag itself is no longer swallowed by the parent program's global `--api-key` option (#12783) ([#13246](https://github.com/diegosouzapw/OmniRoute/pull/13246)) +- fix(sse): register Arcee AI in the executor provider registry so requests reach api.arcee.ai instead of silently falling back to OpenAI (#12784) ([#13277](https://github.com/diegosouzapw/OmniRoute/pull/13277)) +- **fix(quota):** share one upstream quota read when identical saturation checks arrive at the same time, so traffic bursts don't multiply provider API calls ([#12787](https://github.com/diegosouzapw/OmniRoute/pull/12787)) — thanks @maxmad64bis +- **fix(db):** ignore expired or invalid rate-limit cooldown writes so a stale timestamp can't lock a connection that should be usable — clearing still works as before ([#12788](https://github.com/diegosouzapw/OmniRoute/pull/12788)) — thanks @maxmad64bis +- **fix(providers):** single source for provider order with `xao` ranking alongside `xai-oauth` ([#12790](https://github.com/diegosouzapw/OmniRoute/pull/12790)) — thanks @maxmad64bis +- **fix(routing):** off-table models check the free-model catalog before inheriting premium prices, latency bootstraps from the observed pool median, async tiers read live database pricing with a 90-day freshness gate, and the tier cache invalidates on every pricing write ([#12792](https://github.com/diegosouzapw/OmniRoute/pull/12792)) — thanks @maxmad64bis +- **fix(auto-combo):** snapshot scoring uses the observed per-connection account tier and per-model quality instead of neutral constants, and circuit-open providers rank lower at build time ([#12794](https://github.com/diegosouzapw/OmniRoute/pull/12794)) — thanks @maxmad64bis +- **fix(pool):** empty `auto/*` pools now say why they are empty, and the models listing uses the same paid check as routing ([#12795](https://github.com/diegosouzapw/OmniRoute/pull/12795)) — thanks @maxmad64bis +- fix(routing): recognize CLIProxyAPI's 'unknown provider for model' 400 as fallback-worthy (#12800) ([#13284](https://github.com/diegosouzapw/OmniRoute/pull/13284)) +- **fix(sse):** translate-mode streams now emit the estimated token counts as a trailing usage-only chunk before `[DONE]` when the upstream stays silent, so metered chat clients see totals instead of nothing ([#12828](https://github.com/diegosouzapw/OmniRoute/pull/12828)) — thanks @maxmad64bis +- **fix(db):** provider stats stay truthful on empty and legacy databases: fallback counts default to `0` instead of `null`, latency averages read `null` (not `0`) when no durations were recorded, failures logged before the error-type column existed group under `pre_migration` instead of `unclassified`, and per-provider queries use two new composite indexes ([#12832](https://github.com/diegosouzapw/OmniRoute/pull/12832)) — thanks @maxmad64bis +- fix(nvidia): fail open when a synced model catalog goes stale instead of gating forever (#12849) ([#13248](https://github.com/diegosouzapw/OmniRoute/pull/13248)) +- **fix(db):** WAL maintenance lives in its own module: a `TRUNCATE` checkpoint that hits a busy database now warns with its streak and retries once via `PASSIVE` instead of logging success, and closing-time checkpoints no longer report success on builds without a database file, with the busy totals visible in the authenticated monitoring health payload ([#12853](https://github.com/diegosouzapw/OmniRoute/pull/12853)) — thanks @maxmad64bis +- **fix(health):** quota with no snapshots reads empty instead of a contradicting 0% ([#12857](https://github.com/diegosouzapw/OmniRoute/pull/12857)) — thanks @maxmad64bis +- fix(a2a): accept the dashboard's own session cookie on /a2a so "Run message/send" no longer fails with "Unauthorized: missing or invalid API key" (#12888) ([#13271](https://github.com/diegosouzapw/OmniRoute/pull/13271)) +- **fix(logs):** the Logs grid's in-memory filter pass no longer discards rows the SQL query already matched — selecting an API key from the dropdown (which sends the key's id) returns its calls again, the Combo tab shows every combo instead of only those whose name contains a "1", and the model filter and search cover the same columns as the query ([#12896](https://github.com/diegosouzapw/OmniRoute/pull/12896)) — fixes [#12873](https://github.com/diegosouzapw/OmniRoute/issues/12873) — thanks @ntdatt812 +- **fix(a2a):** `/api/a2a/status` now builds the agent card from the request that asked for it, so a gateway reached at a non-localhost host no longer advertises `http://localhost:20128` as its A2A URL ([#12918](https://github.com/diegosouzapw/OmniRoute/pull/12918)). — thanks @ntdatt812 +- **fix(compression):** progressive aging now appends its `[COMPRESSED:aging:…]` annotation after a turn's `tool_result` blocks instead of in front of them, so Anthropic no longer rejects aged conversations with "`tool_use` ids were found without `tool_result` blocks immediately after" ([#12920](https://github.com/diegosouzapw/OmniRoute/pull/12920)). — thanks @ntdatt812 +- **fix(bedrock):** model import now resolves context limits for every vendor prefix instead of only `anthropic.*`, so `global.openai.gpt-5.6-*` no longer imports with a null `inputTokenLimit` and gets rejected pre-flight at the 200k default ([#12921](https://github.com/diegosouzapw/OmniRoute/pull/12921)). — thanks @ntdatt812 +- **fix(stream):** the 64 KB stream buffer GLM asks for is honoured instead of dropped, and the type error it caused no longer fails the API Route Typecheck gate on every open PR ([#12925](https://github.com/diegosouzapw/OmniRoute/pull/12925)) — thanks @ntdatt812 +- **fix(guardrails):** mask PII inside a `tool_result`'s nested content array, which the masker walked past while redacting its sibling block ([#12930](https://github.com/diegosouzapw/OmniRoute/pull/12930)) — thanks @ntdatt812 +- **fix(dashboard):** the Radar catalog table no longer leaves absent data unexplained — empty limits, unknown context windows, and unreported capabilities each explain themselves on hover, and a new check keeps it that way ([#12937](https://github.com/diegosouzapw/OmniRoute/pull/12937)) — thanks @maxmad64bis +- **fix(sse):** geo-blocked opencode requests rotate to the next account proxy instead of failing, so one refused egress no longer aborts the whole chain ([#12941](https://github.com/diegosouzapw/OmniRoute/pull/12941)) — thanks @maxmad64bis +- **fix(codex):** dashboard "clear cooldown" and the CAS recovery path now drop nested `codexScopeRateLimitedUntil` maps in the same write that nulls `rate_limited_until`, and fresh quota snapshots with headroom lift fallback-sourced scope cooldowns parked by quota preflight ([#12817](https://github.com/diegosouzapw/OmniRoute/issues/12817), [#12860](https://github.com/diegosouzapw/OmniRoute/issues/12860), [#12951](https://github.com/diegosouzapw/OmniRoute/pull/12951)) The dashboard reset-credit button (`consumeCodexResetCredit`) now exercises that same snapshot path after a successful redeem, so a filled quota bar is enough to unpark a leftover fallback Codex child without another manual clear. — thanks @HouMinXi +- **fix(db):** resolve `sql-wasm.wasm` across global npm install and hoisted layouts, ensuring OmniRoute can boot cleanly on Node 24 when native `better-sqlite3` is uncompiled. ([#13035](https://github.com/diegosouzapw/OmniRoute/pull/13035)) — thanks @HouMinXi +- **fix(api):** `DELETE /v1/batches/delete-completed` now sweeps only the calling API key's own completed batches (batches with no owner stay out of a key-scoped sweep on purpose), with an explicit instance-wide mode reserved for authenticated dashboard sessions, a 401 for a presented key that is unknown, revoked, deactivated, banned or expired (never falling through to the session branch), audit logging of every sweep, a sanitized 500 on failure, an owner-scoped file half (a key-scoped sweep never nulls a file another tenant owns) and an atomic sweep — chunked in 200-batch transactions in instance mode — so a mid-way error never leaves a batch pointing at a nulled file (GHSA-wvxc-jp3v-5mg5) ([#12969](https://github.com/diegosouzapw/OmniRoute/pull/12969)) +- **fix(combo):** quota-weighted routing stops drawing on an out-of-credit connection — a 402 now invalidates the stored quota snapshot instead of leaving its stale remaining percentage in place, and a snapshot older than 10 minutes no longer counts as confident headroom for the primary pool ([#12972](https://github.com/diegosouzapw/OmniRoute/pull/12972)) ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- **fix(vision):** Custom Models with "Vision capable" checked no longer have image requests swapped to `glm/glm-4.6v` when the client sends the advertised alias (`vllm/path/...`) or the bare path-shaped id — Vision Bridge now matches the stored override for all three id forms ([#12758](https://github.com/diegosouzapw/OmniRoute/issues/12758)) ([#12974](https://github.com/diegosouzapw/OmniRoute/pull/12974)) — thanks @HouMinXi +- **fix(sse):** transient opencode upstream failures rotate to the next account proxy instead of failing, so one flapping egress no longer aborts the whole chain ([#12975](https://github.com/diegosouzapw/OmniRoute/pull/12975)) — thanks @maxmad64bis +- **fix(azure):** Deployments from GPT-6 onward now send `max_completion_tokens` instead of `max_tokens`, which Azure rejects with HTTP 400. The rule matched a literal `gpt-5`, so each new generation arrived broken; it now matches the generation range, while `gpt-35-turbo` still keeps `max_tokens`. ([#13007](https://github.com/diegosouzapw/OmniRoute/pull/13007)) — thanks @ntdatt812 +- **fix(db):** add an opt-in self-restart circuit for sustained critical memory pressure, gate post-cleanup VACUUM behind a minimum freed-rows threshold, and checkpoint the SQLite WAL every 5 minutes with a size guard that escalates to TRUNCATE, so a growing WAL can no longer stall the event loop into a full outage. ([#13011](https://github.com/diegosouzapw/OmniRoute/pull/13011)) — thanks @HouMinXi +- **fix(auth):** an explicit connection pin may probe a quota-disabled row once and re-enable it on success ([#12874](https://github.com/diegosouzapw/OmniRoute/issues/12874)) ([#13017](https://github.com/diegosouzapw/OmniRoute/pull/13017)) — thanks @HouMinXi +- **fix(api):** thread `X-OmniRoute-Fallback-Attempts` through combo chat completions so streaming and non-streaming responses report how many prior legs were attempted ([#13038](https://github.com/diegosouzapw/OmniRoute/pull/13038)) — thanks @HouMinXi +- Restore provider failure classification and credential refresh on non-streaming requests: classify non-2xx failures to lock models on per-model quota exhaustion, update connection rate limits from headers and body, and pass credential refresh handlers to pipeline execution so 401 tokens can be refreshed and retried (#13043). ([#13069](https://github.com/diegosouzapw/OmniRoute/pull/13069)) — thanks @HouMinXi +- **fix(responses):** wrap forced-non-streaming web_search fallback JSON as Responses SSE so Codex still sees `response.completed` ([#13050](https://github.com/diegosouzapw/OmniRoute/pull/13050)) — thanks @HouMinXi +- **fix(antigravity):** Preserve upstream thought-token usage in normalized completion and reasoning token counts ([#13055](https://github.com/diegosouzapw/OmniRoute/pull/13055)) — thanks @pacocartones +- Fix a concurrency-slot leak in the DeepSeek PoW solver: a worker that failed to spawn (for example a missing worker script) never released its slot, so `MAX_CONCURRENT_WORKERS` failures disabled the solver until restart. ([#13097](https://github.com/diegosouzapw/OmniRoute/pull/13097)) — thanks @anhtahaylove +- **fix(acp):** bound the ACP session output buffers — `stdoutBuffer` and `stderrBuffer` now cap at 1 MiB keeping the most recent output behind a visible `[...output truncated...]` marker, and `stderrBuffer` is reset per prompt instead of accumulating for the lifetime of the session. ([#13100](https://github.com/diegosouzapw/OmniRoute/pull/13100)) — thanks @anhtahaylove +- **fix(acp):** release the `stdout`/`exit` listeners and the idle timer that a `sendPrompt` timeout used to leave attached to the `acpManager` singleton, and drop sessions that exited on their own from the session map instead of keeping them forever. ([#13096](https://github.com/diegosouzapw/OmniRoute/pull/13096)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection and PII scanners now read the text a `tool_result` block carries on `content` (string or nested block list), in messages and in system blocks, so tool output is judged by the same rules as user text ([#13101](https://github.com/diegosouzapw/OmniRoute/pull/13101)) — thanks @ntdatt812 +- **fix(gamification):** close the badge notification SSE stream when the request signal is already aborted before the stream starts — a client that disconnects while the route is still awaiting auth used to leave both the 2s unlock poll and the 15s heartbeat running for the lifetime of the process. ([#13106](https://github.com/diegosouzapw/OmniRoute/pull/13106)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection scan now spends its 16 KB budget on both ends of the request instead of the first 16 KB only, so `system`, `instructions`, `query`, `documents` and the newest turns are no longer hidden behind one long message ([#13104](https://github.com/diegosouzapw/OmniRoute/pull/13104)) — thanks @ntdatt812 +- **fix(dashboard):** expose the Volcano Ark console cookie on quota scraping and unwrap connect-error objects so the dashboard shows the upstream message ([#13107](https://github.com/diegosouzapw/OmniRoute/pull/13107)) — thanks @HouMinXi +- **fix(db):** release the `beforeExit`/`SIGINT`/`SIGTERM` handlers when a `node:sqlite` adapter closes, so a closed adapter and its database handle are no longer pinned to `process` for the lifetime of the run — the same treatment #7494 gave the sql.js adapter. ([#13109](https://github.com/diegosouzapw/OmniRoute/pull/13109)) — thanks @anhtahaylove +- **fix(translator):** `contentSchema` and `unevaluatedItems` are now treated as subschema positions by the tool-schema sanitizer, so a truncation placeholder in either is replaced with a permissive schema instead of being forwarded as a string ([#13110](https://github.com/diegosouzapw/OmniRoute/pull/13110)) — thanks @ntdatt812 +- **fix(cli-helper):** clear the `createLogStream` timeout on the abort path — `stop()` aborts the in-flight fetch and returned through the `signal.aborted` branch, which skipped `clearTimeout` and left an armed timer per stopped stream. The stream reader is now also cancelled when the read loop exits early. ([#13114](https://github.com/diegosouzapw/OmniRoute/pull/13114)) — thanks @anhtahaylove +- **fix(docs):** drop TLS-impersonation, MITM-decrypt, supply-chain attestation, and XOR-mask writeups from the public `/docs` catalog and Docker image. Files stay in git for engineers; operators who need them open the repo, not the website. ([#13136](https://github.com/diegosouzapw/OmniRoute/pull/13136)) — thanks @HouMinXi +- **fix(combo):** parse numeric-epoch `rate_limited_until` in the combo cooldown read path ([#13141](https://github.com/diegosouzapw/OmniRoute/pull/13141)) — thanks @maxmad64bis +- **fix(opencode):** both OpenCode plugins now reject a gateway address typed without `http://` at configuration time, instead of publishing every model with an api url no client can call, and the v2 plugin no longer publishes a model card whose api url is blank or relative ([#13142](https://github.com/diegosouzapw/OmniRoute/pull/13142)) — thanks @maxmad64bis +- **fix(providers):** lock opencode model on upstream 400 model-unavailable ([#13146](https://github.com/diegosouzapw/OmniRoute/pull/13146)) — thanks @maxmad64bis +- **fix(logging):** keep the provider exchange rather than the raw client bodies when a call log exceeds its size budget, and show that recovered payload in the request-detail panel instead of replacing it with the stored response body ([#13147](https://github.com/diegosouzapw/OmniRoute/pull/13147)) — thanks @maxmad64bis +- **fix(traffic-inspector):** the WebSocket route no longer leaks a traffic-buffer subscriber and a 30s ping timer when the client socket is already closed at handler time — listeners are attached before any resource is acquired, a destroyed socket bails out early, and the ping interval stops on a dead socket where `write()` never throws ([#13155](https://github.com/diegosouzapw/OmniRoute/pull/13155)) — thanks @anhtahaylove +- **fix(telegram):** bound the per-user API key cache in the Telegram chat proxy so a burst of distinct chat ids can no longer grow the process heap without limit ([#13165](https://github.com/diegosouzapw/OmniRoute/issues/13165)) ([#13166](https://github.com/diegosouzapw/OmniRoute/pull/13166)) — thanks @anhtahaylove +- **fix(stream):** cancel the upstream response body when the JSON-to-SSE sniff unwinds on a body timeout, so a stalled upstream no longer pins the connection ([#13169](https://github.com/diegosouzapw/OmniRoute/issues/13169)) ([#13171](https://github.com/diegosouzapw/OmniRoute/pull/13171)) — thanks @anhtahaylove +- **fix(telegram):** authenticate webhook deliveries with Telegram's `secret_token` so an unauthenticated caller can no longer mint API keys or spend upstream quota ([#13172](https://github.com/diegosouzapw/OmniRoute/issues/13172)) ([#13175](https://github.com/diegosouzapw/OmniRoute/pull/13175)) — thanks @anhtahaylove +- **fix(models):** give discoverable Gemini 3.8 Flash ids their own 65536 output spec so Antigravity no longer clamps them to 16384 ([#13195](https://github.com/diegosouzapw/OmniRoute/pull/13195)) — thanks @HouMinXi +- **fix(dashboard):** leftover catalog-removed provider rows (gemini-cli) can be listed and purged from the providers page ([#13067](https://github.com/diegosouzapw/OmniRoute/issues/13067)) ([#13197](https://github.com/diegosouzapw/OmniRoute/pull/13197)) — thanks @HouMinXi +- **fix(auth):** a dashboard session now requires the `authenticated: true` claim that login, OIDC and the session refresh already emit — a JWT merely signed with `JWT_SECRET` (for example the Cursor CLI passthrough token, which any API-key holder can obtain) no longer verifies as the `auth_token` cookie on any route, the WebSocket handshake or the live server; existing sessions keep working ([#13298](https://github.com/diegosouzapw/OmniRoute/issues/13298)) ([#13375](https://github.com/diegosouzapw/OmniRoute/pull/13375)) +- **fix(api):** `DELETE /v1/batches/delete-completed` now applies the caller's API-key policy (endpoint allowlist, schedule, usage cap, rate limit) like every other `/v1` route, commits the key-scoped sweep per 200-batch chunk instead of holding one write lock for the whole sweep, refuses to loop without progress, and rejects a scope that names both a key and `allTenants` ([#13684](https://github.com/diegosouzapw/OmniRoute/pull/13684)) +- Restore the API-route typecheck gate: the non-streaming leg result lost its discriminated-union narrowing after the server-owned tool loop reassignment, producing 13 new TS2339 diagnostics in `chatCore.ts`. ([#12963](https://github.com/diegosouzapw/OmniRoute/pull/12963)) +- Restore four non-streaming behaviours the server-owned tool loop refactor dropped: client aborts map to 499 with the fixed `Request aborted` message, an aborted request no longer logs a synthetic `clientResponse`, Claude prompt-cache telemetry is recorded again, and a body that cannot be canonicalized no longer throws when the tool loop is off. ([#12990](https://github.com/diegosouzapw/OmniRoute/pull/12990)) +- **fix(skills):** The CLI registry parser now reads positionals declared with `.addArgument()`, not only those written inline in `.command()`. `tunnel create [type]` was being published as `tunnel create`, so the agent-skills sync gate reported drift on every branch and regenerating would have deleted the argument. ([#13009](https://github.com/diegosouzapw/OmniRoute/pull/13009)) — thanks @ntdatt812 +- **fix(combos):** dashboard combo test uses a short prompt, serial probes, and a 60s timeout so reasoning models and rate-limited free pools do not fail the health check ([#13001](https://github.com/diegosouzapw/OmniRoute/pull/13001)) — thanks @HouMinXi +- fix(compression): terminate idle worker threads on eviction so long-running instances stop leaking OS threads and MessagePorts ([#13091](https://github.com/diegosouzapw/OmniRoute/pull/13091)) — thanks @anhtahaylove +- Redact Google API keys of any length in error bodies: the pattern required exactly 39 characters, so shorter or longer `AIza…` credentials passed through unredacted. ([#12964](https://github.com/diegosouzapw/OmniRoute/pull/12964)) +- **fix(grok-cli):** a 402 "Grok Build usage balance exhausted" parks that Grok login as out of credit (Grok Build CLI, grok.com cookie, and xAI OAuth share the weekly pool). Combo routing then tries the next login instead of locking the model for every account in the pool ([#13061](https://github.com/diegosouzapw/OmniRoute/pull/13061)) — thanks @HouMinXi +- fix(compression): spawn the LLMLingua worker with a file URL object so compression actually runs instead of silently failing open on Node ([#13093](https://github.com/diegosouzapw/OmniRoute/pull/13093)) — thanks @anhtahaylove +- **fix(test):** run the local `test` and `test:unit` scripts at concurrency 4 so a full-suite run no longer exhausts the machine's commit charge and kills unrelated processes ([#13187](https://github.com/diegosouzapw/OmniRoute/pull/13187)) — thanks @anhtahaylove +- **fix(monitoring):** `GET /api/monitoring/health` `credentialHealth` now includes a bounded `failedConnections` list (`connectionId`, `status`, sanitized `lastError`) when the probe-cache gauge `failed>0`, plus `source: probe-cache` and a cheap `staleDbNonOkCount` for sticky SQLite `test_status` on active rows. Documents that the live gauge is not `provider_connections.test_status`. ([#12876](https://github.com/diegosouzapw/OmniRoute/pull/12876)) — thanks @RaviTharuma +- **fix(resilience):** an openai-compatible multi-upstream gateway no longer marks the whole connection `credits_exhausted` when a single upstream (or the `/models` / representative-model health probe) returns 402. Quota failures stay model-scoped; true connection-wide auth failures are unchanged. Single-credential 402 key disable ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239)) is preserved. ([#12875](https://github.com/diegosouzapw/OmniRoute/pull/12875)) — thanks @RaviTharuma +- **fix(dashboard):** nine fixes on the Orchestration Canvas, all diagnosed in the Phase 2 reviews. A source that fails now keeps the timestamp of its FIRST failure instead of being re-stamped every poll — the stale line said "since the last poll" no matter how long the source had been down, and the churn also defeated the snapshot's stable identity (it serializes the source list), so the canvas re-rendered on every tick while anything was broken. A source that HAD data and only then started failing is flagged too: previously only a source with no node at all got the warning, so a source that went down mid-session kept a healthy-looking node forever. The rest are pointwise: clicking a filter chip cancels the pending search debounce (left armed, it fired ~300ms later and silently reverted the chip); the search input carries an accessible name; `?state=running, failed` parses like the unpadded form instead of dropping the padded value; the CSV toggle helper is defined once in `model/urlParams.ts` rather than twice; the agents tab tells "nothing running" apart from "the filter matched nothing", offering a clear-filters button instead of setup links that would be wrong advice there; edges stop emitting SMIL particles above 40 simultaneously active edges, keeping the colored stroke; and the drawer's error banner clears when a retried action succeeds. ([#12983](https://github.com/diegosouzapw/OmniRoute/pull/12983)) +- **fix(i18n):** the nine locales added with the EU-language batch (Greek, Estonian, Irish, Croatian, Lithuanian, Latvian, Maltese, Slovenian, Serbian) were missing the eleven Orchestration Canvas keys that Phase 3 introduced, so the compare-runs panel and the "no runs match these filters" empty state fell back to English in those languages (`deepMergeFallback` substitutes English for an absent key, so nothing rendered blank — it rendered untranslated). The coverage gate does not catch this: it enforces an 80% floor per locale, and eleven missing keys out of ~13,000 leaves coverage at 99.9%. Translated for real in each language, calibrated against the wording each file already uses for "run", "filter" and "skill". ([#13294](https://github.com/diegosouzapw/OmniRoute/pull/13294)) +- **fix(dashboard):** five follow-ups from the Phase 2 Orchestration Canvas review. The History drawer now shows the memory section for runs that already left the live TTL window: the persisted `memory_hits` event is parsed into `metadata.memoryHits` with the same defensive validation the drawer applies, and — because that event is observability rather than a state transition — it no longer leaks into the timeline, where it had been inheriting the task's state and rendering as a duplicate transition. The A2A memory recall runs against its own 1.5s deadline instead of inheriting the memory backend's 30s one; overshooting degrades exactly like any other recall failure (no hits, task proceeds), and the timer is cleared on both paths. Repeating a Conductor run carries its `requirements.cli`/`requirements.model` forward, so the new run is pinned to the same runner profile and model rather than drifting to whatever the fleet picks. A successful repeat from the Agents tab now focuses the run it created, instead of leaving the operator on the finished one. And the auth test for `POST /api/conductor/tasks` moved into the shared `ROUTES` array rather than restating the pattern. ([#12988](https://github.com/diegosouzapw/OmniRoute/pull/12988)) +- fix(plugins): stop leaking an exit listener per plugin hook timeout, which triggered MaxListenersExceededWarning on plugins that ignore SIGTERM ([#13092](https://github.com/diegosouzapw/OmniRoute/pull/13092)) — thanks @anhtahaylove +- **fix(validation):** Provider node edits no longer fail with a generic "Invalid request" when the optional daily-quota reset fields are left blank. The dashboard sends `dailyQuotaResetTimezone` and `dailyQuotaResetHour` as `null`, and only the hour accepted it. ([#13066](https://github.com/diegosouzapw/OmniRoute/issues/13066)) ([#13083](https://github.com/diegosouzapw/OmniRoute/pull/13083)) — thanks @ntdatt812 +- **fix(combo):** quota-aware expansion drops banned, inactive, missing, and wrong-provider connections before quota fetch or model dispatch; pins and allowlists stay selectors, not a bypass. Antigravity automatic exhaustion now requires a reported zero remaining, so a positive balance below 1% stays eligible. ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- Fix an intermittent failure in the quota-weighted routing test suite: scores are a function of `Date.now()`, so peers with identical quota scored microseconds apart never tied and swapped order. ([#12946](https://github.com/diegosouzapw/OmniRoute/pull/12946)) +- fix(volcengine): resolve quota window mapping gaps, safe multi-connection console binding, and response cookie sanitization ([#12950](https://github.com/diegosouzapw/OmniRoute/pull/12950)) — thanks @HouMinXi +- **fix(test):** resolve the WebDAV handler path with `fileURLToPath` so the suite's 37 WebDAV tests run on Windows instead of failing with a doubled `C:\C:\` drive prefix ([#13196](https://github.com/diegosouzapw/OmniRoute/pull/13196)) — thanks @anhtahaylove +- **fix(ui):** fall back from invalid AnySearch icon alias (direct commit `9587e07b69`) +- **fix(sse):** stop rebuilding a truncated summary from the collector's cap-dropped event array ([#12718](https://github.com/diegosouzapw/OmniRoute/pull/12718)) — thanks @hartmark +- **fix(conversations):** show a pending spinner for unresolved tool nodes instead of "(empty)" ([#12727](https://github.com/diegosouzapw/OmniRoute/pull/12727)) — thanks @hartmark +- **fix(streaming):** fail fast when an upstream stream produces only lifecycle/heartbeat events, never real content ([#12741](https://github.com/diegosouzapw/OmniRoute/pull/12741)) — thanks @hartmark +- **fix(responses-continuation):** bridge the write-in-flight window with an in-memory pending store ([#12854](https://github.com/diegosouzapw/OmniRoute/pull/12854)) — thanks @hartmark +- **fix(translator):** drop or map agent_message on Chat Completions fallback ([#12880](https://github.com/diegosouzapw/OmniRoute/pull/12880)) — thanks @RaviTharuma +- **fix(api):** page and stream-complete GET /v1/models for large catalogs ([#12882](https://github.com/diegosouzapw/OmniRoute/pull/12882)) — thanks @RaviTharuma +- **fix(quota):** align AUTH window labels with usage API and clarify 503 ([#12884](https://github.com/diegosouzapw/OmniRoute/pull/12884)) — thanks @RaviTharuma +- **fix(ci):** clear two base-reds on release/v3.8.51 — mutation-coverage gate + image-only-model guard ([#12945](https://github.com/diegosouzapw/OmniRoute/pull/12945)) +- **fix(api):** scope batch bulk-delete to the calling API key ([#13211](https://github.com/diegosouzapw/OmniRoute/pull/13211)) +- **fix(tests):** align model-sync log assertion to shipped path redaction (base-red) ([#13227](https://github.com/diegosouzapw/OmniRoute/pull/13227)) +- **fix(test):** make the ToS heading guard actually require the parentheses ([#13228](https://github.com/diegosouzapw/OmniRoute/pull/13228)) +- **fix(quality):** register 4 drifted covering tests in stryker tap.testFiles ([#13229](https://github.com/diegosouzapw/OmniRoute/pull/13229)) +- **fix(logging):** mask *-api-key headers in request-log pipeline (#13273) ([#13401](https://github.com/diegosouzapw/OmniRoute/pull/13401)) — thanks @KooshaPari +- **fix(tests):** correct stale Jina catalog prefix in custom-models test ([#13403](https://github.com/diegosouzapw/OmniRoute/pull/13403)) — thanks @KooshaPari +- **fix(backend):** prune old backups after health-check-repair snapshots (#13308) ([#13404](https://github.com/diegosouzapw/OmniRoute/pull/13404)) — thanks @KooshaPari +- **fix(backend):** guard memory_fts_au trigger with WHEN clause to stop FTS bloat (#13326) ([#13405](https://github.com/diegosouzapw/OmniRoute/pull/13405)) — thanks @KooshaPari +- **fix(free-tiers):** remove obsolete Together $25 signup credit (#12526) ([#13407](https://github.com/diegosouzapw/OmniRoute/pull/13407)) — thanks @KooshaPari +- **fix:** skip non-chat generation models in Test all models ([#13409](https://github.com/diegosouzapw/OmniRoute/pull/13409)) — thanks @KooshaPari +- **fix(providers):** de-duplicate agy and antigravity model catalogs ([#13410](https://github.com/diegosouzapw/OmniRoute/pull/13410)) — thanks @KooshaPari +- **fix(dashboard):** send provider in body for mini-playground web search ([#13411](https://github.com/diegosouzapw/OmniRoute/pull/13411)) — thanks @KooshaPari +- **fix(resilience):** add CJK quota-exhaustion patterns to 429 classifier ([#13412](https://github.com/diegosouzapw/OmniRoute/pull/13412)) — thanks @KooshaPari +- **fix(backend):** eval runner: failed calls no longer score as passed + dotAll regex ([#13413](https://github.com/diegosouzapw/OmniRoute/pull/13413)) — thanks @KooshaPari +- **fix(backend):** eval runner opts out of output-style and memory injection ([#13414](https://github.com/diegosouzapw/OmniRoute/pull/13414)) — thanks @KooshaPari +- **fix:** chain serial quarantine step in npm test script (fixes #13286) ([#13418](https://github.com/diegosouzapw/OmniRoute/pull/13418)) — thanks @KooshaPari +- **fix(antigravity):** derive stable session ID from credentials for prompt-cache reuse (#13305) ([#13424](https://github.com/diegosouzapw/OmniRoute/pull/13424)) — thanks @KooshaPari +- **fix(memory):** merge memory into top-level system field instead of unshifting at messages[0] (#13425) ([#13427](https://github.com/diegosouzapw/OmniRoute/pull/13427)) — thanks @KooshaPari +- **fix(compression):** preserve instruction blocks from lossy engines ([#13523](https://github.com/diegosouzapw/OmniRoute/pull/13523)) — thanks @KooshaPari +- **fix(compression):** preserve polarity words and newlines in ultra heuristic ([#13524](https://github.com/diegosouzapw/OmniRoute/pull/13524)) — thanks @KooshaPari +- **fix(compression):** run user preservePatterns before built-in patterns (#13457) ([#13525](https://github.com/diegosouzapw/OmniRoute/pull/13525)) — thanks @KooshaPari +- **fix(reasoning):** expand deepseek-v4 regex to match v4.1+ and add command-code provider (#13430) ([#13528](https://github.com/diegosouzapw/OmniRoute/pull/13528)) — thanks @KooshaPari +- **fix(providers):** add alwaysReasons metadata for reka-flash-3 ([#13531](https://github.com/diegosouzapw/OmniRoute/pull/13531)) — thanks @KooshaPari +- **fix(playground):** allow Compare columns to scroll independently ([#13532](https://github.com/diegosouzapw/OmniRoute/pull/13532)) — thanks @KooshaPari +- **fix(security):** empty whitelist allows through to prevent admin lockout ([#13534](https://github.com/diegosouzapw/OmniRoute/pull/13534)) — thanks @KooshaPari +- **fix(tests):** add DISABLE_IOREG_STRATEGY env var for macOS test compatibility (fixes #13467) ([#13539](https://github.com/diegosouzapw/OmniRoute/pull/13539)) — thanks @KooshaPari +- **fix(zed-hosted):** map developer role and Gemini enums for Zed proxy ([#13543](https://github.com/diegosouzapw/OmniRoute/pull/13543)) — thanks @KooshaPari +- **fix(analytics):** normalize provider aliases to canonical id in usage_history ([#13545](https://github.com/diegosouzapw/OmniRoute/pull/13545)) — thanks @KooshaPari +- **fix(combo):** use traceId for call log id to prevent UNIQUE constraint failure ([#13546](https://github.com/diegosouzapw/OmniRoute/pull/13546)) — thanks @KooshaPari +- **fix(auth):** check model lockout before returning synthetic noauth connection ([#13547](https://github.com/diegosouzapw/OmniRoute/pull/13547)) — thanks @KooshaPari +- **fix(guardrails):** skip credential redaction for base64 image data URLs ([#13550](https://github.com/diegosouzapw/OmniRoute/pull/13550)) — thanks @KooshaPari +- **fix(resilience):** increase requestQueue.maxWaitMs default from 15s to 30s ([#13553](https://github.com/diegosouzapw/OmniRoute/pull/13553)) — thanks @KooshaPari +- **fix(docs):** document OMNIROUTE_READY_TIMEOUT_MS and allowlist the test-only DISABLE_IOREG_STRATEGY ([#13692](https://github.com/diegosouzapw/OmniRoute/pull/13692)) + ### 📝 Maintenance ---- +- **docs(readme):** reconcile live v3.8.50 provider, free-tier, CLI, routing, test, community, sponsor, acknowledgment, and SVG metrics with their audited source denominators, including a deduplicated OmniRoute-in-Action snapshot and distinct contributor rankings for merged pull requests, GitHub-attributed commits, and Git history ([#11356](https://github.com/diegosouzapw/OmniRoute/pull/11356)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **docs:** sync the canonical provider count 354 → 356 across `README.md`, `AGENTS.md`, `llm.txt` (+ 42 i18n mirrors), the four README SVG diagrams, `docs/reference/PROVIDER_REFERENCE.md` (regenerated) and the `package.json` description after Opper (#11629) and 1min.ai (#11631) boarded the catalog — closes the `check:docs-counts-sync` strict drifts that kept `release/v3.8.51` red ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11673](https://github.com/diegosouzapw/OmniRoute/pull/11673)) — thanks @jonlwheat2-gif +- **test(providers):** update count-derived assertions after the v3.8.51 provider additions — `APIKEY_PROVIDERS` 233 → 235 (Opper #11629 + 1min.ai #11631), reserved-prefix REGISTRY walk 395 → 398, `WEB_FETCH_PROVIDERS` now includes `nimble-search` (#11620), and the provider translate-path golden snapshot regenerated ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11674](https://github.com/diegosouzapw/OmniRoute/pull/11674)) — thanks @jonlwheat2-gif +- **test(sse):** bump the hard-lease connection-query inventory for `src/lib/tokenHealthCheck.ts` to 2 — the verify-only web-cookie sweep added by #11495 split the single `getProviderConnections` call into oauth + cookie variants, which the frozen inventory had not tracked ([#11495](https://github.com/diegosouzapw/OmniRoute/pull/11495)). ([#11677](https://github.com/diegosouzapw/OmniRoute/pull/11677)) — thanks @jonlwheat2-gif / @oyi77 +- **chore(release):** clear the `release/v3.8.51` base-red gates — docs migration count (+38 i18n mirrors), stryker `tap.testFiles`, lockfile registry host, stale ESLint suppressions, the 7 lint regressions they were hiding, the stale `APIKEY_PROVIDERS` partition count (231 → 233), the `glm-5.3-max` tier missing from the GLM test table, a dead `modelType === "chat"` comparison in the models catalog, and the `resolveLiveWsUrl` / `sanitizeLiveWsPort` helpers from #11388 that a later merge dropped from `wsPath.ts` (dashboard live WebSocket URL resolution) ([#11502](https://github.com/diegosouzapw/OmniRoute/pull/11502)) — thanks @MumuTW +- **chore(free-models):** the free-tier regime table now declares which totals figure each regime feeds and whether it may take the no-auth shortcut, so the sets that used to repeat those answers by hand are derived from it — a new `freeType` no longer compiles until it has answered every question, instead of silently contributing to no total ([#11537](https://github.com/diegosouzapw/OmniRoute/pull/11537)) — thanks @maxmad64bis +- **docs(routing):** LKGP strategy copy now states provider-level stickiness and the explicit rules fallback instead of promising per-target pinning ([#11555](https://github.com/diegosouzapw/OmniRoute/pull/11555)) — thanks @maxmad64bis +- **test(chatcore):** pin the response tool-name alias resolution against the ordering hazard that broke every Gemini/Antigravity MCP tool call in v3.8.49. `extractRequestToolIdentityMap` deletes `translatedBody._toolNameMap`, so the later read is always undefined and the ledger survives only through the `requestToolIdentityMap` fallback — removing that fallback previously left the entire tool-name suite green. The resolution moves into `resolveResponseToolNameMap()` next to the map it depends on, with a regression guard that fails without the recovery. No behaviour change. ([#11575](https://github.com/diegosouzapw/OmniRoute/pull/11575)) — thanks @amartinawi +- **docs(env):** document `OMNIROUTE_CORPUS_CACHE_SIZE` in `.env.example` and `ENVIRONMENT.md`, so the env/docs contract check stops failing Docs Gates on every branch ([#11576](https://github.com/diegosouzapw/OmniRoute/pull/11576)) — thanks @ntdatt812 +- **chore(stryker):** register `route-guard-tunnel-processes-local-only` and `universal-quota-aware-routing` in `tap.testFiles`, so their mutant kills count for the three modules they cover ([#11577](https://github.com/diegosouzapw/OmniRoute/pull/11577)) — thanks @ntdatt812 +- **test(authz):** pin `LOCAL_ONLY_API_GET_EXEMPTIONS` by exact membership instead of by entry count, so the guard names the offending path and also catches a substitution ([#11580](https://github.com/diegosouzapw/OmniRoute/pull/11580)) — thanks @ntdatt812 +- **test(build):** pin `resolveNextBuildBundlerFlag` to the `OMNIROUTE_USE_TURBOPACK` contract it implements, across runtimes, instead of asserting an unimplemented Bun override ([#11583](https://github.com/diegosouzapw/OmniRoute/pull/11583)) — thanks @ntdatt812 +- **test(claude-web):** the Claude Web transport test's barrel anchors no longer pin the exact construction spelling. #11421 made the executor map lazy, so `/"claude-web": new ClaudeWebExecutor\(\)/` stopped matching and the test went red on `release/v3.8.51` itself — a base-red gate every open PR inherited. The anchors now match the alias key and the class name and tolerate either spelling in between, and were checked to still fail when an alias is genuinely repointed at another executor ([#11585](https://github.com/diegosouzapw/OmniRoute/pull/11585)) — thanks @ntdat812 +- **test(models):** pin `qwen3.8-max` resolution per provider — two of the four catalogs now serve the bare id and no longer carry `-preview` ([#11590](https://github.com/diegosouzapw/OmniRoute/pull/11590)) — thanks @ntdatt812 +- **test(executors):** drop the stale 128-tool truncation assertion that contradicted the #11444 pass-through contract ([#11591](https://github.com/diegosouzapw/OmniRoute/pull/11591)) — thanks @ntdatt812 +- **test(token-health):** pin the current terminal-skip boundary — expired with retry budget is probed, `account_deactivated` stays skipped ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)) — thanks @ntdatt812 +- **test(models):** follow #11503's retarget of the `gemini-3-pro-high` alias to the hyphenated catalog id ([#11593](https://github.com/diegosouzapw/OmniRoute/pull/11593)) — thanks @ntdatt812 +- **refactor(video bridge):** extract per-video acquisition, whole-result caching, description, and metrics/abort/cleanup out of `VideoBridgeGuardrail.preCall` into a `processVideoPart` seam in a new `videoBridgePipeline.ts`, behind explicit `VideoMediaBrokerPort`, `VideoAudioTranscriptionPort`, and `VideoDrilldownPort` boundaries; `preCall` now only handles request traversal, policy, and response aggregation. The Video tab's FFmpeg/ffprobe runtime status is now an explicit `unknown` / `restricted` / `unavailable` / `available` state instead of a nullable boolean pair, fixing a case where an in-flight or failed probe was mislabeled as "install FFmpeg" ([#11657](https://github.com/diegosouzapw/OmniRoute/issues/11657)). ([#12016](https://github.com/diegosouzapw/OmniRoute/pull/12016)) +- **test(opencode-plugin):** add unit test coverage for `formatFreeBudget()` naming helper ([#11660](https://github.com/diegosouzapw/OmniRoute/pull/11660)) — thanks @f9td56dbgh-hub +- **docs(video):** clarify that the Video Bridge transcript `source` field (`client`, `embedded`, `audio-bridge`) is presently caller-declared and not yet server-verified — OmniRoute enforces the enum shape but does not cryptographically confirm that an `embedded`/`audio-bridge` label came from a server-owned extraction ([#11661](https://github.com/diegosouzapw/OmniRoute/issues/11661)). ([#12001](https://github.com/diegosouzapw/OmniRoute/pull/12001)) +- **chore(providers):** retire the distributed Raycast Relay and Hailuo Web integrations after a provenance audit confirmed substantial derivation from GPL-3.0 sources; the official GitHub and MiniMax API providers remain available. Migration 163 disables legacy connections and invalidates their active leases while preserving connection IDs, audit/history rows, quota snapshots, usage records, and fail-closed API-key connection policies. ([#11691](https://github.com/diegosouzapw/OmniRoute/pull/11691)) +- **chore(providers):** retire the Felo Web provider while its GPL-derived provenance remains on licensing HOLD ([#11698](https://github.com/diegosouzapw/OmniRoute/pull/11698)) — stale runtime and database references now fail closed while existing connection, lease, and usage history remains preserved for auditability. +- **fix(sse):** retire the Gemini Web image-generation bridge while provenance and licensing remain on HOLD, preserving Gemini Web chat/auth/tools and legitimate Gemini/Nano Banana image providers ([#11708](https://github.com/diegosouzapw/OmniRoute/pull/11708)) +- **chore(audio):** retire the built-in EdgeTTS provider and its 11-voice catalog while provenance/licensing review remains on HOLD; the generic `/v1/audio/speech` endpoint and the other speech providers remain available ([#11711](https://github.com/diegosouzapw/OmniRoute/pull/11711)) +- **fix(providers):** retire the unofficial Qwen Web integration and its `qwen-web`/`qw` identities while third-party provenance remains on HOLD ([#11713](https://github.com/diegosouzapw/OmniRoute/pull/11713)) — direct requests now fail closed with a sanitized HTTP 410 and combos skip the retired target, while migration guards preserve connection IDs, allowlists, usage/audit history and invalidate active leases. Supported Qwen API, Qwen Code, cloud, token-plan, audio, local and model-name surfaces remain available; restoring Qwen Web requires a qualified provenance/license decision. +- **fix(providers):** retire `microsoft-designer-web` and its `msdesigner` alias from runtime and catalogs while preserving stored credentials, provider history and allowlists as inactive records — source dependency on gpt4free's [`MicrosoftDesigner.py`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/g4f/Provider/needs_auth/MicrosoftDesigner.py) is proven at snapshot `0943d16eef757a44deb60651c07bbe5fa958cafe`, whose [`LICENSE`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/LICENSE) is GPL-3.0; expressive adaptation is probable, which records provenance/licensing risk without asserting a legal conclusion ([#11720](https://github.com/diegosouzapw/OmniRoute/pull/11720)). +- **docs(legal):** centralize pinned MIT notices for the vendored GCF codec and local flag assets ([#11726](https://github.com/diegosouzapw/OmniRoute/pull/11726)) +- Split the 2,952-line `adobeFireflyClient.ts` into nine focused modules (catalog, credentials, payload, ARP session, upload, responses, IMS, account, poll) behind an unchanged barrel — no consumer edits and no public API change (#11728). — thanks @MumuTW +- **fix(dashboard):** replaced the Model Lockout card's unprovenanced notification MP3 with a short, locally synthesized Web Audio chime ([#11731](https://github.com/diegosouzapw/OmniRoute/pull/11731)) — both toggles retain optional audible feedback without shipping replacement media or third-party code, and unsupported, suspended, or rejected audio contexts fail open without blocking the setting change. +- **chore(deepseek-web):** remove the provenance-unresolved DeepSeek PoW WASM binary and its runtime loader/tracing while retaining the existing JavaScript solver (slower at high difficulty) ([#11732](https://github.com/diegosouzapw/OmniRoute/pull/11732)). +- **chore(assets):** remove four unused provider assets whose redistribution provenance could not be verified while retaining the active Freebuff SVG ([#11735](https://github.com/diegosouzapw/OmniRoute/pull/11735)) +- **docs(assets):** retired two raster images whose redistribution provenance could not be verified ([#11736](https://github.com/diegosouzapw/OmniRoute/pull/11736)) — PicoClaw documentation now reuses the existing SVG, while Zoo Code remains a text link without an image. +- **docs(legal):** add pinned notices for six LobeHub-derived provider SVGs and 65 byte-exact theSVG assets, retaining registry claims as unverified metadata with explicit trademark and HOLD boundaries ([#11737](https://github.com/diegosouzapw/OmniRoute/pull/11737)). +- **chore(quality):** added a versioned 225/225 provider-asset provenance manifest and blocking drift gate ([#11743](https://github.com/diegosouzapw/OmniRoute/pull/11743)) — the inventory pins magic MIME and SHA-256 for every physical file, records immutable source matches for 71 assets, keeps 69 probable and 85 unresolved entries explicit but non-blocking, and treats provenance as source evidence rather than copyright or trademark clearance. +- **refactor(mitm):** replace four previously derived Traffic Inspector utilities — conversation normalization, SSE merging, secret masking, and header sanitization — with independent clean-room implementations based on public provider schemas, WHATWG SSE, and RFC 6750, while keeping the two independently classified passthrough surfaces unchanged ([#11748](https://github.com/diegosouzapw/OmniRoute/pull/11748)). +- Added a unit test that fails seven days before any dated pack under `config/` (`validUntil` and sibling keys) lapses, naming the file and key. The Alibaba free-tier pack expired on 2026-08-27 and turned every PR red the next morning with no commit involved; renewal now happens on someone's terms, not the clock's. ([#11891](https://github.com/diegosouzapw/OmniRoute/pull/11891)) +- `check:workflows` now fails (under `--strict`/`--ratchet`) when any job routed to a self-hosted runner publishes with `--provenance` — npm rejects that with `422` at the registry, which in v3.8.50 only surfaced after the tag and Docker images were public. ([#11895](https://github.com/diegosouzapw/OmniRoute/pull/11895)) +- `scripts/ops/runner-janitor.sh` now proves a path is idle with one `lsof` snapshot and removes stale leftovers itself (tmpfs after 3 h — it is RAM — disk after 24 h), kills orphan `next-build` processes, prunes checkouts of stopped runners, and alerts on memory pressure; `--dry-run` shows exactly what it would do. `docs/ops/RUNNER_BOX.md` reconciled to the measured box (31 GB, 10 listeners). ([#11893](https://github.com/diegosouzapw/OmniRoute/pull/11893)) +- The `next-build` artefact (1.3 GB) is now written and read under `$RUNNER_TEMP` (per-runner, on disk) instead of `/tmp`, which on the self-hosted pool is a 12 GB tmpfs in RAM. Landing it there took 27–32 of the publish job's 76 minutes, and the fixed `/tmp/e2e-build.tar.gz` name let E2E jobs on different runners overwrite each other's download. ([#11896](https://github.com/diegosouzapw/OmniRoute/pull/11896)) +- The CI `build` job now runs in two concurrency lanes — `main` and pull requests — so a release build is never queued behind (or OOM-killed beside) PR builds on the self-hosted pool, which holds one `next-build` comfortably and two at the edge. ([#11901](https://github.com/diegosouzapw/OmniRoute/pull/11901)) +- Re-freeze the ESLint suppressions on `release/v3.8.51` from a clean-room measurement (2 stale file entries pruned, 55 pre-existing `no-explicit-any` in six new files frozen under #11924) and drop the dead `GPT_SIZE_MAP` constant orphaned by the Adobe Firefly client split, so `No new ESLint warnings` stops failing every PR with exit 2 (Refs #11924) ([#11955](https://github.com/diegosouzapw/OmniRoute/pull/11955)) +- Type the 55 `no-explicit-any` sites that had been frozen under #11924 — four redundant casts in `socksConnectorWithFamily.ts` (undici/socks types already accept them) and the mocks/fixtures of the socks-timeout and isFree suites — and drop their suppression entries; the ESLint ratchet shrinks from 5487 to 5432 (Closes #11924) ([#11975](https://github.com/diegosouzapw/OmniRoute/pull/11975)) +- Take the two hosted-runner builds off the PR rail: `Build App` (`build.yml`) is `workflow_dispatch`-only and `DAST smoke (PR)` runs only for PRs into `main` — the 7 GB hosted VM cannot build `release/v3.8.51` in any profile (19/30 red, VM shutdown ~8 min into `next build`) and both checks had turned into permanent noise on every release PR; the bundle stays validated by `ci.yml` on `main` and by `nightly-release-green` on `release/**` (Closes #11946) ([#11962](https://github.com/diegosouzapw/OmniRoute/pull/11962)) +- Move the four nightly jobs that build the backend (`nightly-schemathesis`, `nightly-llm-security` promptfoo + garak, `nightly-resilience` axe-a11y) off the hosted 7 GB runner — where they died on `release/v3.8.51` unseen — onto the box's new `omni-light` pool (two listeners), and document the reshaped fleet (4 active OmniRoute listeners: 2 `omni-build` + 2 `omni-light`, janitor ceiling 4) (Closes #11965) ([#11967](https://github.com/diegosouzapw/OmniRoute/pull/11967)) +- Stop painting every fork PR into `release/**` red: `quality.yml` `Build (advisory)` is skipped (GitHub still reports `continue-on-error` failures as check FAILURE). Hosted `ubuntu-latest` cannot finish `npm run build` on this tree — same class as #11962 taking `build.yml` off the PR rail. `docker-publish.yml` amd64 now runs on the `.113` `omni-build` pool (31 GB / 32 cores, two listeners) with Turbopack, shares the `heavy-build-main` lane with `ci.yml` `Build` so it queues instead of becoming a third heavy, and keeps per-ref concurrency (a merge storm was starting 8 concurrent OOM builds). arm64 stays on `ubuntu-24.04-arm` with webpack — there is no ARM box. Fallback when `USE_VPS_RUNNER` is off: hosted amd64 + webpack (#11976). ([#12021](https://github.com/diegosouzapw/OmniRoute/pull/12021)) +- `docker-publish.yml` builds with webpack (`OMNIROUTE_USE_TURBOPACK=0`) on **both** arches, including amd64 on the `.113` `omni-build` pool. Turbopack had the RAM (31 GB, `omniroute-113-6`) and still panicked (`TurbopackInternalError: there must be a path to a root`, run 33253576569, 26 min in). The same tree's arm64 webpack build on hosted `ubuntu-24.04-arm` succeeded (run 33264823398). Dockerfile already documents webpack as the Docker escape hatch (#11976). ([#12050](https://github.com/diegosouzapw/OmniRoute/pull/12050)) +- **docs(free-tier):** declare the counting vs deciding regimes for "is it free?" and guard the deciding path from DB-backed catalog resolution ([#12226](https://github.com/diegosouzapw/OmniRoute/pull/12226)) — thanks @maxmad64bis +- **docs(auto-combo):** The mode pack table in `docs/routing/AUTO-COMBO.md` now lists all six shipped packs with every weight each one sets, replacing a four-pack table whose numbers had also drifted from the source. It states plainly that no pack sets `quality`, so selecting any pack silences the observed-quality signal. Six more documents that quote the scoring factor count joined the `check:docs-counts` gate, which caught five stale claims — including one naming nine factors that do not exist — and two stale mode pack counts. The dashboard routing panel, which offered four of the six packs and labelled the default strategy "6-Factor Scoring", is now covered by a test; the two packs it was missing are `reliability-first` and `chaos-mode`, the latter labelled as the fault-injection profile it is rather than as one more routing preference ([#12316](https://github.com/diegosouzapw/OmniRoute/pull/12316)) — thanks @maxmad64bis +- **chore(quality):** rebaseline `src/lib/db/apiKeys.ts` for the ACL the key-creation path now preserves ([#12352](https://github.com/diegosouzapw/OmniRoute/pull/12352)) ([#12673](https://github.com/diegosouzapw/OmniRoute/pull/12673)) — thanks @KrzysiekSko +- **chore(providers):** bump the Claude Code wire identity and the Devin bridge image pin from `2.1.220` to `2.1.258` ([#12402](https://github.com/diegosouzapw/OmniRoute/pull/12402)) — thanks @ggiak ([#12604](https://github.com/diegosouzapw/OmniRoute/pull/12604)) +- **docs(env):** align `.env.example`, the README Bun section, and the troubleshooting guide with the code: `OMNIROUTE_USE_TURBOPACK` also governs `npm run build` (not dev-only), `bun run build` follows that flag instead of auto-selecting Webpack, `OMNIROUTE_CHAT_MAX_HEAVY_IN_FLIGHT` is unset by default (no request-count cap), and the structural `503 chat_admission_busy` message matches `chatAdmissionResponses.ts` (#12404 — thanks @pacocartones) +- **chore(electron):** upgrade the desktop app to Electron 44 (Chromium 152, Node 24.18.1) ([#12217](https://github.com/diegosouzapw/OmniRoute/pull/12217)). **Requires macOS 13 (Ventura) or later** — Chromium dropped macOS 12 (Monterey), so Monterey users must stay on an earlier OmniRoute desktop build. Windows and Linux are unaffected; the app already shipped only x64/arm64, so Electron 44 dropping 32-bit builds changes nothing. Removes the `openAsHidden`/`wasOpenedAsHidden` login-item fields deleted in Electron 44 — hidden autostart continues to work through the `--hidden` argument registered with the login item ([#12554](https://github.com/diegosouzapw/OmniRoute/pull/12554)) — thanks @app/dependabot +- **chore(quality):** rebaseline `src/sse/handlers/chat.ts` for the effective-input persistence the continuation fix needs ([#12641](https://github.com/diegosouzapw/OmniRoute/pull/12641)) ([#12680](https://github.com/diegosouzapw/OmniRoute/pull/12680)) — thanks @hartmark +- `Coverage` job on `ci.yml`: the informational Codecov upload gets its own 5-minute ceiling and `continue-on-error`, and the job budget grows from 20 to 30 minutes (the 8-shard c8 merge alone takes ~10) — a stalled upload no longer ends the job `cancelled` and drags a fully green `main` run's conclusion down with it ([#11972](https://github.com/diegosouzapw/OmniRoute/pull/11972)) +- CI hardening on the PR rail: the four `Unit Tests fast-path` shards get `timeout-minutes: 30` (a hung shard held a PR for 64 min instead of GitHub's 6 h default) and both ESLint file caches lose their `restore-keys` fallback, so a cache built under another suppressions file or lint config can no longer report stale verdicts (Refs #11600, #11924) ([#11963](https://github.com/diegosouzapw/OmniRoute/pull/11963)) +- Every CI job that runs a `next build` (`build`, the npm `publish`, both release-green validations) now targets the `omni-build` runner label, which only two of the eight self-hosted runners carry. The box holds one build comfortably and two at the edge; a third now queues on GitHub instead of being OOM-killed by the kernel. ([#11932](https://github.com/diegosouzapw/OmniRoute/pull/11932)) +- **refactor(combo):** move `handleRoundRobinCombo` (and `resolveTargetTokenLimit`) into `open-sse/services/combo/roundRobinCombo.ts`. `combo.ts` drops from 2164 to 1014 lines (`split("\n").length`); the leaf is 1199 (under the 1200 new-file cap). The round-robin call site uses a dynamic `import()` so `releaseStickyPinOnFailure` / `clearStaleLKGP` can stay exported from `combo.ts` without a static cycle. Skip / sticky / semaphore / safety-timer behavior is unchanged. ([#12811](https://github.com/diegosouzapw/OmniRoute/pull/12811)) — thanks @HouMinXi +- **chore(quality):** rebaseline the file-size caps the error-boundary campaign grew past (`open-sse/executors/codex.ts`, `open-sse/vendor/codex-chatgpt-web/bridge.ts`, both via [#12444](https://github.com/diegosouzapw/OmniRoute/pull/12444)) ([#12654](https://github.com/diegosouzapw/OmniRoute/pull/12654)) +- **chore(quality):** rebaseline the file-size caps the hartmark batch grew past (`combos/page.tsx` via [#12355](https://github.com/diegosouzapw/OmniRoute/pull/12355), `open-sse/services/combo.ts` via [#12338](https://github.com/diegosouzapw/OmniRoute/pull/12338)) ([#12623](https://github.com/diegosouzapw/OmniRoute/pull/12623)) — thanks @hartmark +- **chore(quality):** rebaseline the file-size caps the HouMinXi batch grew past when its PRs stacked (`providers/page.tsx`, `chatCore.ts`, `accountFallback.ts`) — each PR measured correctly in isolation, none saw the stacking ([#12619](https://github.com/diegosouzapw/OmniRoute/pull/12619)) +- **chore(quality):** rebaseline `open-sse/services/combo.ts` for the reset-aware scoring the HouMinXi batch stacked ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) ([#12678](https://github.com/diegosouzapw/OmniRoute/pull/12678)) — thanks @HouMinXi +- **docs:** sync provider catalog count (direct commit `4f11b2ae3d`) +- **docs(skills):** sync omni-inference search fallback (direct commit `b4e76a7ed9`) +- **chore(lint):** prune two stale base suppressions (direct commit `1325047d56`) +- **test(auto-combo):** tolerate full-suite rotation load (direct commit `ad3e293f9f`) +- **test:** drain inherited quality snapshot reds (direct commit `21ed68d8ac`) +- **chore(quality):** rebaseline measured CLI bundle growth (direct commit `d35b3f9779`) +- **test(ui):** reproduce invalid AnySearch icon alias (direct commit `12cc6ca834`) +- **test(translator):** fix the relative imports of the relocated deferred-finish test (direct commit `777d9d1629`) +- **docs(counts):** bring the documented provider and migration counts to the measured values (direct commit `e70bea30e9`) +- **test(security):** drop the guard-target set left unused by the sync (direct commit `a32ba0e362`) +- **chore(db):** renumber model_capabilities migration 163 → 169 (cross-branch collision with 163_radar_feed_cache_generated_at) (direct commit `70f5d4cbf9`) +- **docs(cli):** document CLI_PRIME_AGENT_BIN and correct the CLI Agents count (direct commit `9058e39b61`) +- **chore(deps):** refresh runtimes and adopt ESLint 10 ([#11259](https://github.com/diegosouzapw/OmniRoute/pull/11259)) — thanks @backryun +- **refactor(local-corpus):** implement dynamic root resolution and LRU cache ([#11491](https://github.com/diegosouzapw/OmniRoute/pull/11491)) — thanks @raheemuddin786 +- **chore(quality):** prune stale eslint-suppressions entries ([#11596](https://github.com/diegosouzapw/OmniRoute/pull/11596)) +- **chore(ci):** unblock quality gates - env-doc sync + stryker tap.testFiles registration (#9282) ([#11609](https://github.com/diegosouzapw/OmniRoute/pull/11609)) — thanks @Meet6338-X +- **chore(ci):** point the circular-deps gate at dpdm's real JS entrypoint ([#11615](https://github.com/diegosouzapw/OmniRoute/pull/11615)) — thanks @benzntech +- **chore(quality):** rebaseline file-size for #11448/#11495/#11561 growth ([#11630](https://github.com/diegosouzapw/OmniRoute/pull/11630)) +- **deps:** realign onnxruntime-node pin to @huggingface/transformers' 1.24.3 lockstep ([#11633](https://github.com/diegosouzapw/OmniRoute/pull/11633)) — thanks @jonlwheat2-gif +- **test(autoCombo):** port models_dev_tier #11508 guard from node:test to vitest ([#11635](https://github.com/diegosouzapw/OmniRoute/pull/11635)) — thanks @jonlwheat2-gif +- **chore(quality):** rebaseline file-size for #11083/#11631/#11628 growth ([#11636](https://github.com/diegosouzapw/OmniRoute/pull/11636)) +- **chore(lint):** prune stale ESLint suppressions (base-red #11449) ([#11644](https://github.com/diegosouzapw/OmniRoute/pull/11644)) — thanks @jonlwheat2-gif +- **docs(changelog):** aggregate the ten v3.8.50 fragments into CHANGELOG.md ([#11683](https://github.com/diegosouzapw/OmniRoute/pull/11683)) +- **chore(search):** drop unreachable credential sweep after #11642 ([#11687](https://github.com/diegosouzapw/OmniRoute/pull/11687)) — thanks @HouMinXi +- **ci:** add API route TypeScript regression gate ([#11705](https://github.com/diegosouzapw/OmniRoute/pull/11705)) — thanks @Sabeekhann +- **docs(dedup):** correct the CodeQL claim on the tenant-namespace comment ([#11712](https://github.com/diegosouzapw/OmniRoute/pull/11712)) +- **docs(changelog):** add consolidated v3.8.50 stats, top-25 ranking and a .mailmap ([#11715](https://github.com/diegosouzapw/OmniRoute/pull/11715)) +- **docs:** recommend gstack for AI-assisted workflows ([#11770](https://github.com/diegosouzapw/OmniRoute/pull/11770)) — thanks @santosraju99-hub +- **chore(quality):** tighten complexity/cognitive-complexity ratchets to the current tip; land the missed gateways.ts rebaseline ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **docs(i18n):** finish Freepik → Magnific rebrand in locale strings and README ([#11772](https://github.com/diegosouzapw/OmniRoute/pull/11772)) — thanks @turbolego +- **refactor(auto-combo):** fix divergent scoring in combo health reporting ([#11854](https://github.com/diegosouzapw/OmniRoute/pull/11854)) — thanks @jacobsparts +- **chore(changelog):** add missing fragments for #11919/#11918/#11916 ([#11938](https://github.com/diegosouzapw/OmniRoute/pull/11938)) +- **test(check):** escape the runs-on fixture with JSON.stringify, not a quote-only replace ([#11942](https://github.com/diegosouzapw/OmniRoute/pull/11942)) (direct commit `fb7445eaa3`) +- **test(release):** align five suites with the contracts #11933, #11919 and #11876 shipped on release/v3.8.51 ([#11944](https://github.com/diegosouzapw/OmniRoute/pull/11944)) +- **chore(quality):** remove unreachable code and restore test discovery ([#11950](https://github.com/diegosouzapw/OmniRoute/pull/11950)) — thanks @backryun +- **docs(agents):** sync-back landings are fast-forward, never squash ([#11964](https://github.com/diegosouzapw/OmniRoute/pull/11964)) +- **test(infra):** retry recursive temp-dir removal instead of failing a shard on ENOTEMPTY (#11966) ([#11968](https://github.com/diegosouzapw/OmniRoute/pull/11968)) +- **docs(ops):** the .113 heavy-build ceiling is one runner, not two ([#12048](https://github.com/diegosouzapw/OmniRoute/pull/12048)) +- **test(ui):** correct inactive auto-fetch expectation ([#12098](https://github.com/diegosouzapw/OmniRoute/pull/12098)) — thanks @RaviTharuma +- **docs:** document native dependency check escape hatch ([#12101](https://github.com/diegosouzapw/OmniRoute/pull/12101)) — thanks @RaviTharuma +- **test(providers):** regenerate the translate-path golden for OrcaRouter (#11923) ([#12118](https://github.com/diegosouzapw/OmniRoute/pull/12118)) +- **chore(quality):** velocity phase — loosen every numeric baseline by 20% until v4.0, monitor headroom nightly ([#12125](https://github.com/diegosouzapw/OmniRoute/pull/12125)) +- **chore(lint):** batch 0 of #12146 — type the call-log-cap sqlite rows instead of 45 as-any casts ([#12157](https://github.com/diegosouzapw/OmniRoute/pull/12157)) +- **chore(lint):** batch 4 of #12146 — shared/components react-hooks violations resolved ([#12159](https://github.com/diegosouzapw/OmniRoute/pull/12159)) +- **chore(lint):** batch 1 of #12146 — dashboard/cli-code react-hooks violations resolved ([#12160](https://github.com/diegosouzapw/OmniRoute/pull/12160)) +- **chore(lint):** batch 3 of #12146 — dashboard/settings react-hooks violations resolved ([#12162](https://github.com/diegosouzapw/OmniRoute/pull/12162)) +- **chore(lint):** batch 2 of #12146 — resolve the react-hooks compiler violations in dashboard/providers ([#12163](https://github.com/diegosouzapw/OmniRoute/pull/12163)) +- **chore(quality):** register search-432 cooldown test in stryker tap.testFiles ([#12170](https://github.com/diegosouzapw/OmniRoute/pull/12170)) +- **chore(lint):** batch 5 of #12146 — combos, endpoint, provider-stats, api-manager and costs react-hooks violations resolved ([#12174](https://github.com/diegosouzapw/OmniRoute/pull/12174)) +- **build:** add contributor fast profile ([#12192](https://github.com/diegosouzapw/OmniRoute/pull/12192)) — thanks @rafacpti23 +- **docs(readme):** README touch-ups ([#12193](https://github.com/diegosouzapw/OmniRoute/pull/12193)) ([#12194](https://github.com/diegosouzapw/OmniRoute/pull/12194)) ([#12202](https://github.com/diegosouzapw/OmniRoute/pull/12202)) — thanks @rafacpti23 +- **build:** force the webpack bundler in the contributor build profile ([#12197](https://github.com/diegosouzapw/OmniRoute/pull/12197)) — thanks @rafacpti23 +- **build:** skip standalone packaging in the contributor profile ([#12198](https://github.com/diegosouzapw/OmniRoute/pull/12198)) — thanks @rafacpti23 +- **docs(audit):** align every published number with the code and harden check:docs-counts ([#12200](https://github.com/diegosouzapw/OmniRoute/pull/12200)) +- **build:** stub the instrumentation entrypoints in the contributor profile ([#12203](https://github.com/diegosouzapw/OmniRoute/pull/12203)) — thanks @rafacpti23 +- **build:** omit the standalone output target for contributor builds ([#12204](https://github.com/diegosouzapw/OmniRoute/pull/12204)) — thanks @rafacpti23 +- **chore(lint):** batch 6 of #12146 — memory, radar, audit, analytics, cache, usage, activity, home and RequestLoggerV2 react-hooks violations resolved ([#12208](https://github.com/diegosouzapw/OmniRoute/pull/12208)) +- **docs(diagrams):** rename number-carrying diagram files to stable names ([#12210](https://github.com/diegosouzapw/OmniRoute/pull/12210)) +- **docs(dashboard):** redraw onboarding tier-flow SVGs for the real 4-tier model ([#12211](https://github.com/diegosouzapw/OmniRoute/pull/12211)) +- **docs(api):** document every implemented route in openapi.yaml (276 → 692 paths) ([#12212](https://github.com/diegosouzapw/OmniRoute/pull/12212)) +- **chore(lint):** batch 7 of #12146 — final src tail: 37 react-hooks violations across 33 files resolved ([#12244](https://github.com/diegosouzapw/OmniRoute/pull/12244)) +- Revert "docs: recommend gstack for AI-assisted workflows (#11770)" ([#12248](https://github.com/diegosouzapw/OmniRoute/pull/12248)) ([#12249](https://github.com/diegosouzapw/OmniRoute/pull/12249)) +- **docs:** Chaos Mode setup guide + weighted strategy semantics ([#12250](https://github.com/diegosouzapw/OmniRoute/pull/12250)) +- **docs(agents):** protected-surface merge rule — operator approval for agent-instruction files ([#12253](https://github.com/diegosouzapw/OmniRoute/pull/12253)) +- **chore(quality):** register native-codex turn-pin tests in stryker tap.testFiles ([#12263](https://github.com/diegosouzapw/OmniRoute/pull/12263)) +- **chore(quality):** dedupe tap.testFiles entries added by racing base-red fixes ([#12265](https://github.com/diegosouzapw/OmniRoute/pull/12265)) +- **docs(readme):** introduce OmniRouteTray — the macOS menu-bar companion ([#12276](https://github.com/diegosouzapw/OmniRoute/pull/12276)) — thanks @ggiak +- **chore(proxy):** purge the legacy 1proxy residue — sync/rotator modules, dead DB exports, dead settings tab and flag, docs (#12091) ([#12290](https://github.com/diegosouzapw/OmniRoute/pull/12290)) +- **chore(quality):** baseline-headroom skips generated and vendored files in the fileSize worst-file signal ([#12291](https://github.com/diegosouzapw/OmniRoute/pull/12291)) +- **refactor(video):** unify the JPEG frame data-URI contract ([#12322](https://github.com/diegosouzapw/OmniRoute/pull/12322)) +- **chore(deps):** freeze onnxruntime-node and eslint-plugin-react-hooks out of dependabot groups ([#12329](https://github.com/diegosouzapw/OmniRoute/pull/12329)) +- **test(executors):** restore the #10986 reasoning-only fallback guards ([#12364](https://github.com/diegosouzapw/OmniRoute/pull/12364)) — thanks @pacocartones +- **chore(quality):** re-tighten the file-size ratchet to the real LOC (plan 3.8.52 task 0) ([#12411](https://github.com/diegosouzapw/OmniRoute/pull/12411)) +- **test(grok):** format web executor suite ([#12412](https://github.com/diegosouzapw/OmniRoute/pull/12412)) +- **chore(quality):** ratchet open-sse typecheck baseline to zero ([#12418](https://github.com/diegosouzapw/OmniRoute/pull/12418)) +- **test(build):** guard the artifact path policy arrays against duplicates ([#12422](https://github.com/diegosouzapw/OmniRoute/pull/12422)) +- **chore(lint):** adopt eslint-plugin-react-hooks 7.1.1 ([#12428](https://github.com/diegosouzapw/OmniRoute/pull/12428)) +- **chore(providers):** remove a keyless provider integration at its operator's request ([#12440](https://github.com/diegosouzapw/OmniRoute/pull/12440)) +- **docs(providers):** register providers removed at their operator's request and guard against reintroduction ([#12478](https://github.com/diegosouzapw/OmniRoute/pull/12478)) +- **chore(quality):** base fixes — stryker tap.testFiles + node_modules cache key ([#12482](https://github.com/diegosouzapw/OmniRoute/pull/12482)) +- **docs(arch):** one-process recipe for tens of long /v1/responses ([#12493](https://github.com/diegosouzapw/OmniRoute/pull/12493)) — thanks @RaviTharuma +- **chore(quality):** tighten the CodeQL ratchet baseline from 11 to 6 ([#12530](https://github.com/diegosouzapw/OmniRoute/pull/12530)) +- **chore(deps):** pin browserslist override to ^4.28.8 ([#12592](https://github.com/diegosouzapw/OmniRoute/pull/12592)) — thanks @KooshaPari +- **chore(deps):** bump fast-uri to 3.1.7 in the electron lockfile ([#12601](https://github.com/diegosouzapw/OmniRoute/pull/12601)) +- **docs(quality):** document how the CodeQL ratchet refreshes and how to tighten it ([#12611](https://github.com/diegosouzapw/OmniRoute/pull/12611)) +- **test(ci):** pin the openapi-security-tiers two-arm contract with an executing gate test (#12581) ([#12652](https://github.com/diegosouzapw/OmniRoute/pull/12652)) +- **docs(gamification):** align XP Rewards table with code (#12501) ([#12667](https://github.com/diegosouzapw/OmniRoute/pull/12667)) — thanks @KooshaPari +- **refactor(combo):** split executeTarget into gates, attempt, and loop ([#12746](https://github.com/diegosouzapw/OmniRoute/pull/12746)) — thanks @HouMinXi +- **docs(security):** document socket.yml scanner config + CI workflow link (#12575) ([#12764](https://github.com/diegosouzapw/OmniRoute/pull/12764)) — thanks @KooshaPari +- **chore(ci):** guard commit identity in pre-commit to stop author misattribution ([#12772](https://github.com/diegosouzapw/OmniRoute/pull/12772)) +- **deps:** 17 Dependabot bumps — bump electron from 43.4.0 to 43.4.1 in /electron ([#11426](https://github.com/diegosouzapw/OmniRoute/pull/11426)); bump the development group across 1 directory with 8 updates ([#11428](https://github.com/diegosouzapw/OmniRoute/pull/11428)); bump the production group across 1 directory with 11 updates ([#11440](https://github.com/diegosouzapw/OmniRoute/pull/11440)); chore(deps): bump github/codeql-action/init from 4.37.7 to 4.37.8 ([#11925](https://github.com/diegosouzapw/OmniRoute/pull/11925)); chore(deps): bump github/codeql-action from 4.37.7 to 4.37.8 ([#11926](https://github.com/diegosouzapw/OmniRoute/pull/11926)); chore(deps): bump github/codeql-action/analyze from 4.37.7 to 4.37.8 ([#11927](https://github.com/diegosouzapw/OmniRoute/pull/11927)); build(deps): bump github/codeql-action/init from 4.37.8 to 4.37.9 ([#12345](https://github.com/diegosouzapw/OmniRoute/pull/12345)); build(deps): bump github/codeql-action/analyze from 4.37.8 to 4.37.9 ([#12346](https://github.com/diegosouzapw/OmniRoute/pull/12346)); bump the development group across 1 directory with 2 updates ([#12347](https://github.com/diegosouzapw/OmniRoute/pull/12347)); build(deps): bump github/codeql-action from 4.37.8 to 4.37.9 ([#12349](https://github.com/diegosouzapw/OmniRoute/pull/12349)); bump browserslist from 4.28.2 to 4.28.8 ([#12396](https://github.com/diegosouzapw/OmniRoute/pull/12396)); bump the production group across 1 directory with 4 updates ([#12399](https://github.com/diegosouzapw/OmniRoute/pull/12399)); bump @xmldom/xmldom ([#12500](https://github.com/diegosouzapw/OmniRoute/pull/12500)); bump qs from 6.15.2 to 6.16.0 ([#12512](https://github.com/diegosouzapw/OmniRoute/pull/12512)); bump @xmldom/xmldom from 0.9.10 to 0.9.12 ([#12513](https://github.com/diegosouzapw/OmniRoute/pull/12513)); bump fast-uri from 3.1.5 to 3.1.7 ([#12514](https://github.com/diegosouzapw/OmniRoute/pull/12514)); bump @humanfs/node from 0.16.7 to 0.16.8 ([#12515](https://github.com/diegosouzapw/OmniRoute/pull/12515)) +- Clear the `release/v3.8.51` `check:agent-skills-sync` base-red: regenerate `skills/cli-tunnel/SKILL.md` so the `tunnel create [type]` positional that #13009 taught the generator to read is reflected in the committed skill. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` typecheck base-red from #12731: the new `Mode pack ranking gates` candidates in `open-sse/services/autoCombo/__tests__/autoCombo.test.ts` omitted the required `provider`, `model` and `errorRate` fields of `ProviderCandidate`, failing `check:open-sse-typecheck` (and with it `Fast Quality Gates`) on every open PR. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` docs-sync base-red: EURouter (#13025) and GreenPT (#13024) took the live provider count to 358, leaving 7 STRICT drifts (`PROVIDER_REFERENCE.md`, 4 diagrams, 2 tier-flow images) plus the `AGENTS.md` / `llm.txt` / `package.json` count claims stale. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- **test(models):** Pin `CATALOG_BUILD_TIMEOUT_MS` in the #12058 canonical-catalog regression so a cold catalog build racing the 8s default bound no longer flakes the suite with a 500 ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Allow `@omniroute/opencode-plugin-v2/` in the npm pack policy — [#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870) shipped the v2 plugin beside its v1 sibling without widening the allowlist, so every packed file under it read as an unexpected artifact and failed `check:pack-policy` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Point the release-green validator's pack gate at the tree it is validating — the build-provenance guard defaulted to `origin/main`, which a release branch tip can never be an ancestor of mid-cycle, so `Package artifact` reported a false red on every run and the tarball boot-smoke cascaded off it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **docs(checks):** keep doc counts honest — headings, rankings, catalog, weights, quality gate and scoring diagram now covered ([#12507](https://github.com/diegosouzapw/OmniRoute/pull/12507)) — thanks @maxmad64bis +- **chore(lifecycle):** `check:model-lifecycle` now also diffs `DEFAULT_DEGRADATION_MAP` (the background-task redirect table) against the vendor lifecycle snapshot, refusing a retired id as source or target, with a table-driven unit test beside it. Three rows whose source the vendor had retired — `claude-sonnet-4-20250514`, `gemini-3-pro-preview` and `gpt-5.1-codex` (whose target `gpt-5.1-codex-mini` is retired too) — were dead code, since `checkLifecycle` answers 410 before the redirect runs; they are dropped (#12535 — thanks @pacocartones) +- **test(compression):** cover idle worker eviction at the resource level — the pool must call `terminate()` and must not retain the worker's `MessagePort`, complementing the `exit`-event assertion added with the fix ([#13371](https://github.com/diegosouzapw/OmniRoute/pull/13371)) +- **chore(quality):** raise the `imageGeneration.ts` file-size ceiling for the image-only-model guard that clears the #12945 base-red ([#13392](https://github.com/diegosouzapw/OmniRoute/pull/13392)) +- **chore(quality):** type the combo-test route's JSON response bodies instead of casting them to `any`, drop the now-empty suppression entry, and remove the `hasPerModelQuota` import `chatCore.ts` stopped using when the failure-classification helper was extracted ([#13379](https://github.com/diegosouzapw/OmniRoute/pull/13379)) +- **chore(electron):** the desktop bundle no longer ships the root-level files of every translated docs mirror (`README.md`, `llm.txt`, `CLAUDE.md`, `GEMINI.md`, `CONTRIBUTING.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`) — the packaged app only reads `docs/i18n//docs/**`, which stays. Saves ~11 MB on top of the translated CHANGELOGs already pruned. (#0000) ([#13235](https://github.com/diegosouzapw/OmniRoute/pull/13235)) +- **chore(quality):** retighten the `src/sse/handlers/chat.ts` unused-vars suppression count to the 8 violations that actually remain ([#13385](https://github.com/diegosouzapw/OmniRoute/pull/13385)) +- Bring the documented counts back in line with the code: 171 migrations (was 169) and 20 routing strategies (was 19), plus the regenerated `cli-tunnel` skill reference. ([#12970](https://github.com/diegosouzapw/OmniRoute/pull/12970)) +- Update the documented migration count to 172 after the call-logs provider-stats indexes landed. ([#13209](https://github.com/diegosouzapw/OmniRoute/pull/13209)) +- **chore(ci):** two gates that close the blind spots behind the exclusions above. `check:vitest-exclusions` requires every Vitest exclusion to name a tracking issue and to appear in `config/quality/vitest-exclusions.json` — the previous list grew to 62 files behind a comment pointing at an issue that had been closed for a month. `check-new-key-coverage` requires a key newly added to `en.json` to reach every locale; the existing coverage gate is a percentage floor per locale, so eleven absent keys out of ~13,000 left it at 99.9% while a whole feature shipped untranslated in nine languages. Both are diff-aware, so pre-existing debt stays frozen and neither needed a migration to turn on. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **build:** root layout no longer loads Inter from `next/font/google`, so a production image build does not need fonts.googleapis.com ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **docs(i18n):** bring the eight new `llm.txt` mirrors (km, kn, ml, my, ne, or, pa, si) up to the current provider and migration counts, so `check-docs-sync` stops rejecting every commit ([#13674](https://github.com/diegosouzapw/OmniRoute/pull/13674)) +- **refactor(ui):** flow surfaces (home topology, combo live studio, compression cockpit/waterfall/nodes, token-health badge) express state through the theme-aware `--orch-status-*` tokens instead of fixed dark-mode hex, so green/red/amber/grey stay legible in the light theme; dark mode is byte-identical. Categorical palettes (routing-strategy hues, compression-layer pills, provider brand colors) deliberately stay hex ([#12378](https://github.com/diegosouzapw/OmniRoute/issues/12378)) ([#13203](https://github.com/diegosouzapw/OmniRoute/pull/13203)) +- **chore(tests):** 51 test files that had been excluded from Vitest are running again, restoring roughly 350 assertions to the blocking suite. Each was measured individually first: of the 62 files parked behind the `// #8618 — pre-existing failure` comment, 51 pass against the current tree with no source change, so the exclusions had outlived the failures they were added for. The 11 that genuinely still fail stay excluded, but now point at a live tracker (#13204) rather than at #8618, which was closed in August while the list it tracked kept growing. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **chore(quality):** drop two ESLint suppression entries whose violations no longer exist, so `eslint --suppressions-location` stops rejecting every commit that touches the surrounding files ([#13384](https://github.com/diegosouzapw/OmniRoute/pull/13384)) +- **chore(release):** bring main's post-tag fixes into release/v3.8.51 — sync-back step 2/2 (direct commit `21c488f210`) +- **chore(deps):** patch toml and esbuild in the opencode plugin lockfiles ([#12965](https://github.com/diegosouzapw/OmniRoute/pull/12965)) +- **docs(changelog):** reconcile the v3.8.51 living section — fold 366 fragments, cover every cycle commit, credit every contributor ([#12971](https://github.com/diegosouzapw/OmniRoute/pull/12971)) +- **test(plugin):** wait for the v2 lazy-refresh tier instead of sleeping past it ([#12984](https://github.com/diegosouzapw/OmniRoute/pull/12984)) +- **chore(quality):** rebaseline chatCore.ts after the non-streaming regression fixes ([#13045](https://github.com/diegosouzapw/OmniRoute/pull/13045)) +- **docs(changelog):** add the missing bullet marker to the DeepSeek PoW fragment ([#13200](https://github.com/diegosouzapw/OmniRoute/pull/13200)) +- **chore(deps):** drain the Dependabot queue — 10 of 13 alerts ([#13213](https://github.com/diegosouzapw/OmniRoute/pull/13213)) +- **chore(ci):** make worktrees carry the hooks that enforce the identity gate ([#13236](https://github.com/diegosouzapw/OmniRoute/pull/13236)) +- **chore(deps):** pin csv-parse to ^7.0.2 via overrides (#13117) ([#13300](https://github.com/diegosouzapw/OmniRoute/pull/13300)) — thanks @KooshaPari +- **chore(deps):** bump hono from ^4.12.34 to ^4.13.7 (#13148) ([#13301](https://github.com/diegosouzapw/OmniRoute/pull/13301)) — thanks @KooshaPari +- **chore(deps):** pin joi to ^18.2.8 via overrides (#13085) ([#13302](https://github.com/diegosouzapw/OmniRoute/pull/13302)) — thanks @KooshaPari +- **docs(providers):** add Microsoft 365 Copilot (BizChat) provider guide (#12779) ([#13340](https://github.com/diegosouzapw/OmniRoute/pull/13340)) — thanks @KooshaPari +### 🙌 Contributors + +Thanks to everyone whose work landed in v3.8.51: + +| Contributor | PRs / Issues | +| --- | --- | +| [@5dive-bot](https://github.com/5dive-bot) | #11852 | +| [@abhisheksharma2411](https://github.com/abhisheksharma2411) | #11547, #11684, #11959, #12209 | +| [@adevwithpurpose](https://github.com/adevwithpurpose) | #11464 | +| [@adivekar-utexas](https://github.com/adivekar-utexas) | #12015, #12043, #12090, #12138 | +| [@AIB1TAL0S](https://github.com/AIB1TAL0S) | #12103 | +| [@alltomatos](https://github.com/alltomatos) | #11448 | +| [@alvinveroy](https://github.com/alvinveroy) | #11857, #12027, #12165 | +| [@amaleta](https://github.com/amaleta) | #11906, #11934 | +| [@amartinawi](https://github.com/amartinawi) | #11575 | +| [@AndrianBalanescu](https://github.com/AndrianBalanescu) | #11083, #11830, #11834, #11844 | +| [@anhtahaylove](https://github.com/anhtahaylove) | #13091, #13092, #13093, #13096, #13097, #13100, #13106, #13109, #13114, #13155, #13166, #13171, #13175, #13187, #13196 | +| [@aniruddhaadak80](https://github.com/aniruddhaadak80) | #11760 | +| [@app](https://github.com/app) | #12554 | +| [@arminanton](https://github.com/arminanton) | #11461, #11513 | +| [@AStupidBear](https://github.com/AStupidBear) | #11584 | +| [@atescivitci-cmd](https://github.com/atescivitci-cmd) | #12105 | +| [@b3nw](https://github.com/b3nw) | #11970, #11971, #12147 | +| [@backryun](https://github.com/backryun) | #11259, #11950, #12075, #12076, #12078, #12079, #12081, #12082, #12181, #12228, #12239, #12255, #12258, #12277, #12367, #12423, #12524, #12538 | +| [@benjaminkitt](https://github.com/benjaminkitt) | #11747 | +| [@benzntech](https://github.com/benzntech) | #11614, #11615 | +| [@Bl0ck154](https://github.com/Bl0ck154) | #11948, #11951, #11952, #11953, #11954 | +| [@brick30llc-ctrl](https://github.com/brick30llc-ctrl) | #11752, #12242, #12262 | +| [@Chewji9875](https://github.com/Chewji9875) | #11455, #11875, #12028 | +| [@cheynetom](https://github.com/cheynetom) | #12058 | +| [@davidebaraldo](https://github.com/davidebaraldo) | #12222, #12304 | +| [@davidlinfr](https://github.com/davidlinfr) | #6390 | +| [@ddarkr](https://github.com/ddarkr) | #12124 | +| [@Deftera186](https://github.com/Deftera186) | #11809 | +| [@dpozimski](https://github.com/dpozimski) | #11945 | +| [@DW-MediaLab](https://github.com/DW-MediaLab) | #12229 | +| [@echel0nn](https://github.com/echel0nn) | #11923 | +| [@f9td56dbgh-hub](https://github.com/f9td56dbgh-hub) | #11660 | +| [@fabioluissilva](https://github.com/fabioluissilva) | #11991 | +| [@foreveryh](https://github.com/foreveryh) | #12177 | +| [@geek007git](https://github.com/geek007git) | #12115, #12116, #12117, #12120, #12122 | +| [@geekyNads](https://github.com/geekyNads) | #11784, #12266 | +| [@ggiak](https://github.com/ggiak) | #11561, #12276, #12350, #12402 | +| [@gonisulaimann](https://github.com/gonisulaimann) | #12368, #12369, #12371 | +| [@Gorillaz322](https://github.com/Gorillaz322) | #12207 | +| [@groovecityJO](https://github.com/groovecityJO) | #12682 | +| [@hartmark](https://github.com/hartmark) | #11434, #11452, #11473, #11499, #11703, #11983, #11984, #11985, #11986, #11988, #11989, #11990, #11994, #12221, #12293, #12445, #12446, #12447, #12448, #12460, #12461, #12623, #12646, #12650, #12680, #12717, #12718, #12727, #12741, #12854 | +| [@hizzt](https://github.com/hizzt) | #11894 | +| [@hongnoul](https://github.com/hongnoul) | #11484 | +| [@HouMinXi](https://github.com/HouMinXi) | #11411, #11414, #11512, #11518, #11520, #11641, #11642, #11643, #11687, #11779, #11849, #11850, #11851, #11915, #11916, #11918, #11919, #11920, #12013, #12017, #12033, #12042, #12106, #12139, #12166, #12169, #12171, #12205, #12213, #12312, #12325, #12487, #12488, #12495, #12504, #12557, #12566, #12590, #12591, #12624, #12626, #12632, #12637, #12678, #12696, #12697, #12711, #12733, #12746, #12767, #12770, #12789, #12803, #12805, #12811, #12866, #12868, #12899, #12926, #12934, #12950, #12951, #12974, #13001, #13006, #13011, #13017, #13026, #13027, #13034, #13035, #13038, #13042, #13050, #13060, #13061, #13069, #13107, #13120, #13136, #13195, #13197 | +| [@Hsia97](https://github.com/Hsia97) | #11624 | +| [@jacobsparts](https://github.com/jacobsparts) | #11854, #12155, #12167 | +| [@joglomedia](https://github.com/joglomedia) | #11980 | +| [@jonlwheat2-gif](https://github.com/jonlwheat2-gif) | #11443, #11450, #11549, #11567, #11608, #11633, #11635, #11640, #11644, #11671, #11672, #11673, #11674, #11675, #11676, #11677, #11706, #11783, #12051, #12052, #12053, #12055 | +| [@kanade-hoshino](https://github.com/kanade-hoshino) | #12180 | +| [@Karan825](https://github.com/Karan825) | #11861 | +| [@KaspaPulse](https://github.com/KaspaPulse) | #11389, #11469, #11628, #11666, #11910 | +| [@keeltrace](https://github.com/keeltrace) | #12080, #12223 | +| [@killer30001000](https://github.com/killer30001000) | #12178 | +| [@KooshaPari](https://github.com/KooshaPari) | #12470, #12592, #12667, #12699, #12703, #12706, #12764, #12769, #12771, #13300, #13301, #13302, #13340, #13401, #13403, #13404, #13405, #13406, #13407, #13409, #13410, #13411, #13412, #13413, #13414, #13418, #13424, #13427, #13433, #13523, #13524, #13525, #13528, #13531, #13532, #13534, #13539, #13543, #13545, #13546, #13547, #13550, #13553 | +| [@kriptoburak](https://github.com/kriptoburak) | #11370 | +| [@KrzysiekSko](https://github.com/KrzysiekSko) | #12425, #12432, #12673 | +| [@marcelokarval](https://github.com/marcelokarval) | direct commit / report | +| [@maxmad64bis](https://github.com/maxmad64bis) | #1622, #11435, #11437, #11441, #11537, #11550, #11553, #11555, #11557, #11812, #11842, #11843, #11903, #12151, #12214, #12215, #12218, #12226, #12314, #12316, #12317, #12318, #12319, #12320, #12321, #12507, #12715, #12731, #12744, #12786, #12787, #12788, #12790, #12792, #12794, #12795, #12828, #12832, #12853, #12857, #12870, #12937, #12941, #12975, #13141, #13142, #13146, #13147 | +| [@mdigitalbh81](https://github.com/mdigitalbh81) | #12240 | +| [@Meet6338-X](https://github.com/Meet6338-X) | #11598, #11609, #12110 | +| [@morpheus9393](https://github.com/morpheus9393) | #11943 | +| [@MumuTW](https://github.com/MumuTW) | #11492, #11502, #11506, #11507, #11626, #11675, #11685, #11728, #11746, #11888, #11889, #11890, #11892 | +| [@Neuron-Mr-White](https://github.com/Neuron-Mr-White) | #11622, #11801, #12256 | +| [@NightStalker-87](https://github.com/NightStalker-87) | #12183 | +| [@NoxzRCW](https://github.com/NoxzRCW) | #11879, #11880, #11881, #11882, #11883, #11935 | +| [@ntdat812](https://github.com/ntdat812) | #11585, #12095 | +| [@ntdatt812](https://github.com/ntdatt812) | #11368, #11573, #11574, #11576, #11577, #11580, #11582, #11583, #11588, #11589, #11590, #11591, #11592, #11593, #11672, #12177, #12180, #12873, #12918, #12920, #12921, #12925, #12930, #13007, #13009, #13024, #13025, #13083, #13087, #13101, #13104, #13110 | +| [@official-burak](https://github.com/official-burak) | #11542 | +| [@opensource-elearning](https://github.com/opensource-elearning) | #12179, #12189, #12278, #12286 | +| [@oyi77](https://github.com/oyi77) | #11408, #11409, #11421, #11505, #11677, #12036, #12110 | +| [@pacocartones](https://github.com/pacocartones) | #11521, #11522, #11527, #11528, #11529, #11530, #11531, #11532, #11533, #11534, #11595, #11599, #11603, #11604, #11605, #11607, #11610, #11676, #11714, #11716, #11718, #11767, #11838, #11860, #11862, #11869, #11871, #11872, #11873, #11903, #11906, #11921, #11934, #12356, #12358, #12359, #12360, #12361, #12362, #12364, #12365, #12373, #12374, #12375, #12376, #12377, #12379, #12380, #12381, #12386, #12387, #12389, #12390, #12394, #12395, #12397, #12401, #12403, #12404, #12522, #12523, #12535, #12536, #12540, #12541, #12543, #12545, #12548, #12549, #12550, #12551, #12552, #12647, #12651, #12653, #13055 | +| [@patrykkopycinski](https://github.com/patrykkopycinski) | #11936, #11937, #12224 | +| [@PauloFH](https://github.com/PauloFH) | #11509 | +| [@PauloHSOliveira](https://github.com/PauloHSOliveira) | #12241 | +| [@ponkcore](https://github.com/ponkcore) | #12054 | +| [@Prajeeth-12](https://github.com/Prajeeth-12) | #11634, #12046 | +| [@quiterunner-commits](https://github.com/quiterunner-commits) | #12131, #12143 | +| [@rafacpti23](https://github.com/rafacpti23) | #11554, #11558, #12192, #12197, #12198, #12202, #12203, #12204 | +| [@ragnar-claude](https://github.com/ragnar-claude) | #11564 | +| [@raheemuddin786](https://github.com/raheemuddin786) | #11491, #11839, #11840, #11841, #12003, #12230, #12231, #12232, #12233, #12234 | +| [@rahilmavani](https://github.com/rahilmavani) | #11761 | +| [@Rahulsharma0810](https://github.com/Rahulsharma0810) | #11771 | +| [@RaviTharuma](https://github.com/RaviTharuma) | #11710, #11727, #11797, #11798, #11802, #11805, #11806, #11811, #12098, #12099, #12101, #12449, #12452, #12472, #12473, #12493, #12533, #12607, #12628, #12631, #12636, #12875, #12876, #12880, #12882, #12884 | +| [@rezjalibd](https://github.com/rezjalibd) | #12186 | +| [@RhianB14](https://github.com/RhianB14) | #11692 | +| [@rifqiawl](https://github.com/rifqiawl) | #11517, #11519, #11969 | +| [@rqzbeh](https://github.com/rqzbeh) | #11390 | +| [@Sabeekhann](https://github.com/Sabeekhann) | #11699, #11705 | +| [@santosraju99-hub](https://github.com/santosraju99-hub) | #11755, #11770 | +| [@Seramicx](https://github.com/Seramicx) | #11568 | +| [@Siva010](https://github.com/Siva010) | #12191 | +| [@solstxce](https://github.com/solstxce) | #11597 | +| [@soroush5](https://github.com/soroush5) | #12691 | +| [@tenshiak](https://github.com/tenshiak) | #12279 | +| [@TheDemonTuan](https://github.com/TheDemonTuan) | #11468, #11470, #11471, #11482, #11548, #11758, #11775, #11814 | +| [@thomasmaerz](https://github.com/thomasmaerz) | #12834 | +| [@tuandinh0801](https://github.com/tuandinh0801) | #11454 | +| [@turbolego](https://github.com/turbolego) | #11621, #11762, #11772, #11774, #11781, #12216 | +| [@Tushar49](https://github.com/Tushar49) | #11392 | +| [@ujjawalkaushik1110](https://github.com/ujjawalkaushik1110) | #11863 | +| [@vermasomesh835](https://github.com/vermasomesh835) | #11794 | +| [@vsd2807](https://github.com/vsd2807) | #11565, #11619 | +| [@wahidsadik371-coder](https://github.com/wahidsadik371-coder) | #12126 | +| [@watchingdogs](https://github.com/watchingdogs) | #12031 | +| [@wildcard](https://github.com/wildcard) | #11620 | +| [@xiaoyaner0201](https://github.com/xiaoyaner0201) | #11460, #11662, #12083 | +| [@Xxx91n](https://github.com/Xxx91n) | #11690 | +| [@yourspraveen](https://github.com/yourspraveen) | #11146 | +| [@yxyxy](https://github.com/yxyxy) | #11671 | +| [@ZaimMarzuki](https://github.com/ZaimMarzuki) | #11960 | +| [@Zartharas](https://github.com/Zartharas) | #11340 | +| [@zero-executioner](https://github.com/zero-executioner) | #11631 | +| [@diegosouzapw](https://github.com/diegosouzapw) | maintainer | + +--- ## [3.8.50] — 2026-08-25 _Living section — regenerated 2026-08-12 from all cycle commits (cycle open `ed2db6cb19` → tip). Bullets carry the merged PR and its author; direct pushes listed separately._ diff --git a/docs/i18n/ga/CHANGELOG.md b/docs/i18n/ga/CHANGELOG.md index 5fcea909cf..40adc55c86 100644 --- a/docs/i18n/ga/CHANGELOG.md +++ b/docs/i18n/ga/CHANGELOG.md @@ -95,16 +95,1119 @@ ## [3.8.51] — TBD -_Living section — cycle opened at the v3.8.50 freeze (parallel-cycle model). Bullets are aggregated from `changelog.d/` fragments at each `/generate-release` phase._ +_Living section — reconciled 2026-09-15 from all cycle commits (`091589089c` → `c0f92ec98a`, 916 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 | **123** | +| 📝 Commits in the cycle | **916** | +| 🔀 Pull requests referenced | **892** | +| 📋 Changelog entries | **919** | +| 🙌 Contributors credited in entries | **121** | +| 🤖 Automated dependency commits | 18 | + +**Entries by type** + +| Type | Count | +| --- | ---: | +| 🐛 Fixes | 592 | +| ✨ Features | 131 | +| 🧹 Chore | 93 | +| 📚 Docs | 42 | +| 🧪 Tests | 32 | +| ♻️ Refactor | 8 | +| 🏗️ Build | 7 | +| ⚡ Performance | 7 | +| ⚙️ CI | 2 | +| ⏪ Reverts | 2 | +| 📦 Dependencies | 2 | +| 🔒 Security | 1 | + +### 🏆 Top 25 contributors this cycle + +_By commits in `091589089c..c0f92ec98a`, author identities consolidated via `.mailmap` and the merged PR's GitHub login. Bots excluded._ + +| # | Contributor | Commits | +| ---: | --- | ---: | +| 🥇 | diegosouzapw | 323 | +| 🥈 | Bob.Hou (@HouMinXi) | 79 | +| 🥉 | Paco Cartones (@pacocartones) | 77 | +| 4 | Dizzle (@maxmad64bis) | 49 | +| 5 | Koosha Paridehpour (@KooshaPari) | 43 | +| 6 | Nguyen Thanh Dat (@ntdatt812) | 31 | +| 7 | Ravi Tharuma (@RaviTharuma) | 26 | +| 8 | Markus Hartung (@hartmark) | 22 | +| 9 | Webman (@jonlwheat2-gif) | 22 | +| 10 | backryun | 17 | +| 11 | anhtahaylove | 15 | +| 12 | Rafa Martins (@rafacpti23) | 10 | +| 13 | Syed Raheemuddin (@raheemuddin786) | 10 | +| 14 | Nguyễn Viết Tuấn (@TheDemonTuan) | 8 | +| 15 | MumuTW | 7 | +| 16 | Paijo (@oyi77) | 7 | +| 17 | Tobias Andersen (@turbolego) | 6 | +| 18 | Bl0ck (@Bl0ck154) | 5 | +| 19 | SHANMUGAPRIYAN (@geek007git) | 5 | +| 20 | KaspaPulse | 5 | +| 21 | Abhishek Sharma (@abhisheksharma2411) | 4 | +| 22 | Abhishek Divekar (@adivekar-utexas) | 4 | +| 23 | Andrew B. (@AndrianBalanescu) | 4 | +| 24 | NoxzRCW | 4 | +| 25 | opensource-elearning | 4 | ### ✨ New Features +- **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). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- Added Google AI Studio Gemini batch text-to-speech support through `POST /v1/audio/speech`. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- 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. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(routing):** subscription-first auto groupings — `auto/subscription` routes only through plan-included connections with a documented hard-stop overage and fails closed on exhaustion, while `auto/thrifty` orders the pool `subscription → keyless → free → cheap → premium` and steps up one rung at a time as each is exhausted. Billing class comes from a curated per-connection catalog (uncurated is treated as metered, never plan-included), both reuse STRICT_ZERO_COST's per-connection verification, and a quota reading whose `resetAt` has passed is now refreshed regardless of TTL so routing returns to plan capacity as soon as it resets ([#11146](https://github.com/diegosouzapw/OmniRoute/pull/11146)) — thanks @yourspraveen +- **feat(providers):** publish a management-authenticated versioned web-session credential contract from OmniRoute's canonical browser credential metadata ([#11340](https://github.com/diegosouzapw/OmniRoute/pull/11340)) — thanks @Zartharas +- **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)) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add Xquik X search with typed results, credential validation, REST routing, and MCP selection ([#11370](https://github.com/diegosouzapw/OmniRoute/pull/11370)) — thanks @kriptoburak +- **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)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(dashboard):** surface durable exclusive managed leases in the existing Sessions view, keeping leased clients visible across idle gaps while marking connections with in-flight work as active ([#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389)) — thanks @KaspaPulse +- **build(bun):** allow Turbopack bundler flag on Bun 1.4+ with configurable Webpack fallback ([#11471](https://github.com/diegosouzapw/OmniRoute/pull/11471)) — thanks @TheDemonTuan +- feat(api): add an opt-in `modelVisibilityAllowlist`/`modelVisibilityDenylist` settings pair to curate exactly which models `/v1/models` advertises, mirrored into every `auto/*` combo candidate pool so a denied model cannot be routed to via combo selection either (#11481) ([#11997](https://github.com/diegosouzapw/OmniRoute/pull/11997)) +- **feat(rankings):** the Free Provider Rankings page shows what each provider actually served over the last 24 h. It ranked by ELO alone, which left a provider that answers every call with an error in first place; the usage data was already served by the API but never requested. A provider with too small a sample shows a dash, not a number ([#11546](https://github.com/diegosouzapw/OmniRoute/pull/11546)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **feat(guardrails):** enforce a bounded, deterministic contract for Video Bridge transcripts — 256 cues, 4096 input code units and 4 KiB UTF-8 per cue, 64 KiB total text, malformed-Unicode rejection, focus-window scoping, cross-source reconciliation with contributing-source metadata, and a structural provenance trust boundary so caller JSON can never self-assert `embedded`/`audio-bridge` provenance ([#11652](https://github.com/diegosouzapw/OmniRoute/issues/11652)) ([#12009](https://github.com/diegosouzapw/OmniRoute/pull/12009)) +- **feat(video):** orchestrate optional Video Bridge audio extraction and Audio Bridge STT behind a dual opt-in (operator setting AND per-request signal) — a new loopback-only broker `mode=audio` operation shares the frame path's exact process queue, deadline, AbortSignal, and byte budgets to extract a bounded mono 16 kHz PCM WAV from the same already-downloaded video, then reuses the existing Audio Bridge transcription boundary; provider segment timing is preserved when available and marked coarse otherwise, and every failure degrades to a visual-only-safe partial instead of throwing (#11654). ([#12012](https://github.com/diegosouzapw/OmniRoute/pull/12012)) +- Add a tenant-bound Video Bridge drill-down lifecycle on top of the existing secure cache substrate: opaque hashed handles (never raw session/video identifiers), preview/standard/detail multiresolution variants resampled on read, response pagination capped at 8 frames and 32 MiB, and a new authenticated `/api/v1/video-bridge/drilldown` consumer route that stays disabled for remote access by default and denies cross-key access with the same response as a nonexistent handle (no existence oracle). ([#12006](https://github.com/diegosouzapw/OmniRoute/pull/12006)) +- **test(video):** Add the Video Bridge FU-07/FU-09 promotion-evidence harness (#11656) — a frozen Zod manifest schema covering the 8 required scenario kinds (static scenes, rapid cuts, late facts, fades, blur, small text, close events, visual prompt injection) with a minimum of 3 repetitions per case, deterministic declarative fixture recipes (`videoBridgePromotionFixtures.ts`), a pure medians/p95 metrics aggregator, a pure FU-07/FU-09 promotion-verdict evaluator applying the ticket's exact thresholds (missing token usage always holds), a digest-only persistence layer that never retains raw media or raw model responses, and a versioned per-model promotion allowlist shipped empty with every model defaulting to `hold`. The FU-07/FU-09 promotion verdicts themselves remain HOLD — they require a real evidence run against real models on VPS 192.168.0.15. ([#12008](https://github.com/diegosouzapw/OmniRoute/pull/12008)) +- **feat(video bridge):** "embedded" transcript provenance can now be legitimately earned instead of merely asserted — a bounded, allowlisted (`mov_text`/`subrip`/`webvtt`) subtitle probe runs through the loopback-only Video Bridge broker (at most 2 streams, 10s subdeadline bounded by the request deadline, 256 KiB output, 4096-code-unit lines), normalized through a bounded, ReDoS-safe WebVTT parser and Zod-validated end to end. The adapter always resolves to an explicit `success`/`absent`/`transient_failure` outcome — a subtitle failure never breaks the visual description path, and only a fingerprint-verified broker response (never a caller-declared label) can produce embedded cues (#11659). ([#12011](https://github.com/diegosouzapw/OmniRoute/pull/12011)) +- Default new Antigravity-family connections (agy CLI imports and Antigravity OAuth connects) to model auto-sync, so live model discovery lands in the synced catalog and `/v1/models` picks up freshly released upstream models (e.g. Gemini 3.7 Flash tiers) without code changes. Existing connections keep their current setting; the per-connection dashboard toggle remains the opt-out. (#11685 — thanks @MumuTW) ([#11887](https://github.com/diegosouzapw/OmniRoute/pull/11887)) +- **feat(ui):** enable React Compiler (`reactCompiler: true` + `babel-plugin-react-compiler`) for automatic memoization at build time ([#11783](https://github.com/diegosouzapw/OmniRoute/pull/11783)) — thanks @jonlwheat2-gif +- **feat(providers):** add SeekAi (`seekai.cc`) as an OpenAI-compatible New-API gateway — catalog id `seekai` (alias `ska`), `https://seekai.cc/v1`, live `/v1/models` via `passthroughModels`, aggregator-list membership so New-API balance detection can opt in. No referral/aff codes. ([#11786](https://github.com/diegosouzapw/OmniRoute/issues/11786)) ([#12557](https://github.com/diegosouzapw/OmniRoute/pull/12557)) — thanks @HouMinXi +- **feat(zai):** add GLM-5.3-Flash Coding Plan support (1M context, 128K output, vision, `low|high|max` reasoning) and route `zai` GLM-5.3-family API-key traffic through the OpenAI-compatible Coding Plan endpoint with native thinking defaults ([#11801](https://github.com/diegosouzapw/OmniRoute/pull/11801)) — thanks @Neuron-Mr-White +- **feat(combo):** choose how combo models are ordered — manual, provider, score, or name — via a sort control in the dashboard builder, persisted in `config.modelSort` and re-applied on load and after add ([#11812](https://github.com/diegosouzapw/OmniRoute/pull/11812)) — thanks @maxmad64bis +- **feat(free):** custom models can be marked free-tier via `customModels[].isFree`; `isFreeModel()` is the first door and `hidePaidModels` respects it even for providers outside the free budget ([#11843](https://github.com/diegosouzapw/OmniRoute/pull/11843)) — thanks @maxmad64bis +- **feat(nodejs):** add `5dive` as a `configure` target — `omniroute configure 5dive` / `omniroute setup-5dive` write a 5dive auth profile that points an agent fleet's `claude` seats at OmniRoute, with the root-only write, the loopback-vs-`https` endpoint rule and the per-seat model pin handled explicitly ([#11852](https://github.com/diegosouzapw/OmniRoute/pull/11852)) — thanks @5dive-bot +- **feat(sse):** treat `max` as a first-class reasoning-effort tier and clamp per model family (GLM 5.1+/DeepSeek V4+/Kimi K3+ keep native `max`; o1/MiniMax/Grok/Muse Spark clamp to their upstream ceiling) ([#11875](https://github.com/diegosouzapw/OmniRoute/pull/11875)) — thanks @Chewji9875 +- **feat(providers):** the provider plugin manifest now advertises a `usage-fetch` capability for the 40 providers that have a wired usage/quota fetcher, so external dashboards can read it from `GET /api/v1/provider-plugin-manifest` instead of parsing `open-sse/services/usage.ts` after every release. Discovery only — no new fetcher, no quota change, and the Dashboard quota widget stays gated by `USAGE_SUPPORTED_PROVIDERS`. `USAGE_FETCHER_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/fetcherProviders.ts`) and is re-exported from `services/usage.ts`, keeping the manifest module a light leaf instead of pulling the ~490-module usage dispatcher into the manifest route. ([#11903](https://github.com/diegosouzapw/OmniRoute/pull/11903)) — thanks @maxmad64bis / @pacocartones +- **feat(plugins):** `OMNIROUTE_PLUGINS_DIR` sets the directory the runtime plugin scanner reads — and the root the plugin manager installs into — overriding the `HOME`-derived default, so a Docker/K8s deployment can point straight at its bind-mounted plugin tree instead of moving `HOME` just to relocate the scan path. An image that exports no home no longer scans `/tmp/.omniroute/plugins` in silence: the resolved directory is logged once at startup as `scanner.dir_resolved`, naming the input that won. Unset, behaviour is unchanged. Distinct from the CLI-only `OMNIROUTE_PLUGIN_PATH`, which finds `omniroute-cmd-*` command packages and never reached this scanner ([#11906](https://github.com/diegosouzapw/OmniRoute/pull/11906)) — thanks @amaleta / @pacocartones +- **feat(leases):** add an explicit owner-authenticated status action that returns only the active lease's privacy-safe configured connection and provider labels, with generation fencing and no credential or internal-id disclosure ([#11910](https://github.com/diegosouzapw/OmniRoute/pull/11910)) — thanks @KaspaPulse +- **feat(providers):** import-from-file modal shows per-row API errors and ships a downloadable CSV template ([#12071](https://github.com/diegosouzapw/OmniRoute/issues/12071)) ([#12504](https://github.com/diegosouzapw/OmniRoute/pull/12504)) — thanks @HouMinXi +- **feat(providers):** dashboard search matches connection name and `baseUrl` so imported OpenAI-compat nodes surface on the provider card ([#12108](https://github.com/diegosouzapw/OmniRoute/issues/12108)) ([#12495](https://github.com/diegosouzapw/OmniRoute/pull/12495)) — thanks @HouMinXi +- **feat(routing):** add a `score` Auto router strategy that selects the highest configured weighted score and reuses `explorationRate`. ([#12155](https://github.com/diegosouzapw/OmniRoute/pull/12155)) — thanks @jacobsparts +- Add a runtime feature flag to disable universal context handoffs globally without changing the default behavior. ([#12167](https://github.com/diegosouzapw/OmniRoute/pull/12167)) — thanks @jacobsparts +- **feat(providers):** the provider plugin manifest now also advertises a `usage-supported` capability for the 46 providers whose usage API is accepted by the server and Dashboard routes, so integrators can distinguish "the server will serve quota for this provider" from "a fetcher is wired" without reading TypeScript. Discovery only — no fetcher or quota change. `usage-fetch` resolves on id or alias (the usage dispatcher accepts both); `usage-supported` resolves on id alone, matching the runtime guard `USAGE_SUPPORTED_PROVIDERS.includes(providerId)`. `USAGE_SUPPORTED_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/supportedProviders.ts`) and is re-exported from `providers.ts`, mirroring the `fetcherProviders` leaf from #11903 and keeping the manifest a light module. ([#12214](https://github.com/diegosouzapw/OmniRoute/pull/12214)) — thanks @maxmad64bis +- **feat(rankings):** order Free Provider Rankings by what each provider actually served — `GET /api/free-provider-rankings?sortBy=reliability` and a "Most reliable first" toggle on the page. Providers with too few calls to state a success rate keep their score order below the measured ones; the default order is unchanged ([#12218](https://github.com/diegosouzapw/OmniRoute/pull/12218)). — thanks @maxmad64bis +- **perf(sse):** defer `cloneLogPayload()` in the structured SSE collector until after the `maxEvents`/`maxBytes` cap check, eliminating ~9,800 wasted `structuredClone` calls per streaming response (65–71% faster `push()`). Reducer snapshot isolation restored for OpenAI and Responses summaries ([#12241](https://github.com/diegosouzapw/OmniRoute/pull/12241)) — thanks @PauloHSOliveira ([#12243](https://github.com/diegosouzapw/OmniRoute/pull/12243)) +- **feat(usage):** Devin CLI agentic quota (Codeium seat-management GetUserStatus) and OpenRouter key limits plus account credits now surface in Provider Limits ([#12256](https://github.com/diegosouzapw/OmniRoute/pull/12256) — thanks @Neuron-Mr-White) +- **feat(settings):** persist `headroomUrl` through Settings so status/start use the operator URL instead of only `HEADROOM_URL` ([#12306](https://github.com/diegosouzapw/OmniRoute/issues/12306)) ([#12487](https://github.com/diegosouzapw/OmniRoute/pull/12487)) — thanks @HouMinXi +- **feat(auto-combo):** Auto-Combo scoring can now weigh how often a provider/model has actually succeeded. The engine already carried that number on every candidate — 24 hours of usage history behind a ten-sample floor, real-time metrics otherwise — and the scoring function never read it, while the weight table described `stability` as if it did. `reliability` (`1 - failureRate`, with the same field precedence and the same rate-bounding the speed ranking already uses, so a corrupt reading means "nothing observed" rather than "fails every call") is now a declared factor shipping at weight `0`, so routing is unchanged until an operator gives it one, and the `stability` description now matches what that factor computes ([#12317](https://github.com/diegosouzapw/OmniRoute/pull/12317)) — thanks @maxmad64bis +- **feat(routing):** With `freeAccessPolicy: "strict"`, the read-only candidate listing (`GET /v1/auto-combo/{channel}/candidates`) no longer hides the candidates the zero-cost guard excludes — the same read-only transparency the resilience filter already honours (#9133). Each candidate now carries `freeAccessExclusion` saying why it would be kept out, and it tells an exhausted allowance apart from a quota reading that never arrived or went stale, which used to look identical from the outside. Routing is unchanged: the listing reports, it never enforces. The separate `excludeTosAvoid` guard still drops its candidates without a reason; that gap is now documented rather than closed ([#12319](https://github.com/diegosouzapw/OmniRoute/pull/12319)) — thanks @maxmad64bis +- **feat(radar):** The Radar catalog table now shows two facts it was already receiving from the feed and dropping on the floor: the per-model rate limits (requests and tokens, per minute and per day) in a new column, and a badge when a provider's terms state it may train on the prompts you send. A limit of zero renders as zero rather than "rate-only" — for a ceiling those are opposite facts — and a model with no training statement gets no badge, because an absent statement is not a guarantee ([#12320](https://github.com/diegosouzapw/OmniRoute/pull/12320)) — thanks @maxmad64bis +- **feat(radar):** explain Community, single-use, contributor, supporter, recovery, abuse, offers, and privacy rules before either Radar activation action, and remove the superseded fixed-PR grant promise from every UI locale ([#12342](https://github.com/diegosouzapw/OmniRoute/pull/12342)) +- **feat(gamification):** the dashboard Profile page now shows the real daily streak — `/api/gamification/level` returns `streak: { current, longest }` (per key with `apiKeyId`, operator-wide maximum otherwise) and the streak card reads it instead of a hard-coded 0 (#2403) ([#12377](https://github.com/diegosouzapw/OmniRoute/pull/12377)) — thanks @pacocartones +- **feat(gamification):** the dashboard leaderboard now shows each API key's display name under the Name column instead of a truncated key id; `GET /api/gamification/leaderboard` attaches `name` per entry (name only — no key material), while the shared ranking helper and the federation leaderboard stay id-only — thanks @pacocartones ([#12385](https://github.com/diegosouzapw/OmniRoute/pull/12385)) +- **feat(gamification):** enforce the documented 1000 XP/min per-API-key anti-cheat rate limit on the XP award path; over-limit awards are logged and skipped instead of persisted, and the sliding window now matches the timestamp format stored in `xp_audit_log` ([#2403](https://github.com/diegosouzapw/OmniRoute/issues/2403)) ([#12390](https://github.com/diegosouzapw/OmniRoute/pull/12390)) — thanks @pacocartones +- **feat(admin):** localize the gamification anomalies page — the loading state, the Status column and the Suspicious badge now come from the `common` catalog (new `common.suspicious` key propagated to every locale) — add it to the Gamification sidebar group as `gamification-admin` (`/dashboard/gamification/admin`), and expose the loading and empty states as polite `role="status"` live regions (#12401 — thanks @pacocartones) +- **feat(providers):** skip GitHub combo members missing from the live synced catalog, and drop Copilot models that are policy-disabled or hidden from the model picker ([#12473](https://github.com/diegosouzapw/OmniRoute/pull/12473)) — thanks @RaviTharuma +- **feat(i18n):** locale-expansion tooling — `npm run i18n:add-locale` adds a language to config, dashboard, docs mirrors, CLI, README/indexes and site in one command; browser, CLI and cookie detection resolve `uk`, `fil`/`tl`, `zh-Hant` (and the retired legacy `in` → `id`) via config aliases; `config/i18n.json` now ships in the npm package so the published CLI can read it; new real-translation ratio gate (`npm run i18n:check-ratio`, advisory) with a per-locale ratchet baseline; `run-translation --adopt` restores `.i18n-state.json`; `sync-language-bars` generates 🌐 bars from config; `validate_translation.py` loads its allowlist again. Retires the duplicate `in` locale (Indonesian mislabelled as Hindi) — 42 honest locales; saved `NEXT_LOCALE=in` / `OMNIROUTE_LANG=in` keep working. (#12496) +- **feat(api):** Emit gateway-measured `tokens_per_second` (TTFT excluded) on streaming usage and `X-OmniRoute-Tokens-Per-Second` when first-token latency is known ([#12616](https://github.com/diegosouzapw/OmniRoute/issues/12616)) ([#12631](https://github.com/diegosouzapw/OmniRoute/pull/12631)) — thanks @RaviTharuma +- **feat(opencode-plugin): map gateway cost/usage/tok/s onto OpenCode inference payloads** — the official plugin copies `X-OmniRoute-Response-Cost`, token counts, `X-OmniRoute-Tokens-Per-Second` / `usage.tokens_per_second`, TTFT, and the winning `X-OmniRoute-Model` onto the JSON/SSE body OpenCode already consumes. Missing tok/s is left unset (never `tokens / latency`). (#12636) — thanks @RaviTharuma +- **feat(combo):** add combo strategy `quota-weighted`: skip exhausted accounts, then weighted-draw by leftover / in-flight load; existing conversations stay pinned until the account is empty ([#12789](https://github.com/diegosouzapw/OmniRoute/pull/12789)) — thanks @HouMinXi +- **feat(models):** Account live listings become the chat catalog source for Claude, Codex, Copilot, and AGY; public metadata only fills prices on IDs those accounts already list. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **fix(models):** Union `agy` and `antigravity` live catalogs so `agy/gemini-3.8-flash-high` is not rejected after the prefix folds to `antigravity`. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **feat(opencode):** Add `@omniroute/opencode-plugin-v2`, an opencode v2 plugin that gives a v2 install a route to an OmniRoute gateway: it publishes models, combos and auto-combos into the host catalog, refreshes them lazily behind a 300s TTL, and keeps serving the last known catalog from an on-disk snapshot when the gateway is unreachable. The v1 plugin (`@omniroute/opencode-plugin`) is untouched and keeps working as before. The plugin adapts to the host instead of assuming one shape: it reads the shape the host seeds into the catalog draft and publishes the `api` block, the pre-`api` top-level fields, or both. The gateway key comes from the host credential store when one is connected, so no secret needs to sit in a config file; a source the gateway refuses is named in a warning instead of silently degrading the catalog to raw model ids; and tool schemas bound for a Gemini model are stripped of the JSON-Schema keywords Gemini rejects, so tool calling survives that route ([#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870)) — thanks @maxmad64bis +- feat(services): show sanitized CLIProxyAPI account health from its authenticated management API without exposing credentials, file paths, or raw account metadata (#6342) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add AnySearch free web search + URL extract (webFetch) with typed results, credential validation, REST routing, and MCP selection - fallback-only ([#11690](https://github.com/diegosouzapw/OmniRoute/pull/11690)) — thanks @Xxx91n +- **feat(dashboard):** display clamped `[0, 100]%` cached input token ratio in request logs table ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) — thanks @b3nw +- **feat(catalog):** add `OMNIROUTE_DISABLE_THINKING_LEVEL_VARIANTS` feature flag to optionally filter out thinking level variants from model catalog ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) — thanks @b3nw +- **feat(dashboard):** continuously export call logs to external analytics stores. A pluggable destination registry ships the full Logs-tab record set on an hourly `JobRegistry` cron, with a persisted per-destination cursor, batched inserts, a config UI rendered from each destination's own field descriptors, and a REST layer (`/api/log-export/*`) for CRUD, a connection test, and an on-demand run. A destination can opt into `includeBodies` to also ship the request and response payloads shown in the Logs detail pane, including the client and provider views of each call; this is off by default, and payloads inherit the dashboard's PII sanitisation, secret redaction and `noLog` handling. Google BigQuery is the first destination, using a service-account key stored encrypted at rest and streaming inserts keyed by call-log id, into a table that is day-partitioned on `timestamp` and clustered on `api_key_name`, `provider`, `model` and `status`. ([#11945](https://github.com/diegosouzapw/OmniRoute/pull/11945)) — thanks @dpozimski +- **feat(providers):** add MaxAI as a signed, OpenAI-compatible provider serving its 13 paid chat models (GPT-5.6 / Luna / Thinking, Claude 5 Sonnet, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite, Grok 4.1-fast / 4.5, DeepSeek V3.2 / R1, Llama 3.3 70B) through OmniRoute's `/v1` endpoint, with per-request HMAC-SHA1→SM3→AES request signing, live model + context-window discovery from `/models/get_config`, and prompted tool-calling translated to OpenAI `tool_calls` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI vision input — image_url content parts are forwarded inline in `message_content` to the 6 vision-capable models (GPT-5.6 / Luna / Thinking, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite) ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI image generation — 6 image models (gpt-image-1, dall-e-3, flux-1-schnell/dev/pro, sd3-medium) exposed through `POST /v1/images/generations` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI document RAG — inline base64 file/document attachments are uploaded to MaxAI (content-addressed `doc_id`) and attached to the chat via `doc_list` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** browserless MaxAI onboarding — email device-pair login (`/api/providers/[id]/login`) and signed access-token refresh, so a connection can be created and kept fresh without a real browser or Google OAuth ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** per-provider TLS impersonation profile (MaxAI presents a Windows Firefox-150 client fingerprint) so its bot-sensitive endpoints accept OmniRoute traffic ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** add **Nimble** as a web-search and web-fetch provider (`nimble-search`) — `/v1/search` routes to Nimble's search API at `lite` depth (locale, freshness and include/exclude domain filters mapped onto the shared request contract), and `/v1/web/fetch` routes to Nimble Extract, which covers all four fetch formats (`markdown`, `html`, `links`, `screenshot`) from a single call. One API key serves both surfaces. ([#11620](https://github.com/diegosouzapw/OmniRoute/pull/11620)) — thanks @wildcard +- The npm publish is automatic again, through npm Trusted Publishing (OIDC): the hosted `stage-npm` job publishes with a short-lived credential minted from GitHub's id-token — no `NPM_TOKEN`, no 2FA prompt, provenance attached. `publish_mode=staged` (owner approves with 2FA) and `direct` (token) remain available on `workflow_dispatch`. ([#11931](https://github.com/diegosouzapw/OmniRoute/pull/11931)) +- **feat(providers):** Add **Opper** as an API-key gateway provider — EU-hosted AI gateway with 700+ models from 30+ providers behind one OpenAI-compatible API and one key (`OPPER_API_KEY`); model ids use `provider/model` format (e.g. `anthropic/claude-sonnet-4-6`, `openai/gpt-5`); live model catalog at `https://api.opper.ai/v3/compat/models`; entry mirrors `requesty` (same shape, `passthroughModels: true`, no static seed) ([#11629](https://github.com/diegosouzapw/OmniRoute/pull/11629)) +- **feat(dashboard):** the `/dashboard/orchestration` snapshot hook now subscribes to the `agents` WebSocket channel (`agent.task.updated`) instead of `requests` as its refetch trigger, and relaxes its background poll from 5s to 30s while that WS connection is up — falling back to the tighter 5s cadence, reprogrammed live on any connect/disconnect transition, whenever the socket is down. ([#12409](https://github.com/diegosouzapw/OmniRoute/pull/12409)) +- **feat(dashboard):** Orchestration canvas quick wins — search box plus state/source/provider filter chips with a one-click clear, and per-source collapse/expand, all reflected in the URL so a filtered/collapsed view is shareable and survives a refresh; the detail drawer gained a "copy trace JSON" action and hardened error/empty-state and accessibility handling; the Agents-tab edges now animate traveling particles along active (running) connections; and the canvas node/edge status colors moved off fixed hex values onto theme-aware `--orch-status-*` CSS custom properties, so they adapt correctly to light/dark mode. ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- **chore(dashboard):** Orchestration UI hardening pass and the missing component/model test coverage it called for — `OrchestratorNode`/`ActivityNode`/`OverflowNode` rendering, the `?node=`/overflow-click page routing, the Agents-tab orchestrator-click no-op and `showCompleted` toggle, and the overview kanban's done-column sort order (#12270, #12271). ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- New `/dashboard/orchestration` page: live unified view of everything running — Cloud Agent, A2A and Conductor as a real-time graph (Agents tab), the combo cascade (Routing tab, reusing the Combo Live Studio) and a state kanban (Overview tab), with a detail drawer (trace, cost, approve/cancel). Read-only over existing APIs — no new backend. Canvas concept credit: PR #11815 design ([#12261](https://github.com/diegosouzapw/OmniRoute/pull/12261)) +- **feat(dashboard):** new "History" tab on `/dashboard/orchestration` — an Airflow-style grid of finished runs over a 24h/7d/30d preset window, one row per (source, identity), clicking a cell opens the existing detail drawer. It is backed by real persistence: A2A task lifecycle transitions are now written to the `a2a_tasks` table (purged after 30 days, configurable via `OMNIROUTE_A2A_HISTORY_RETENTION_DAYS`) and served by the new `GET /api/a2a/tasks/history` listing endpoint, with the task-detail route falling back to persisted history once a run leaves the in-memory snapshot. Conductor runs stay remote and are not persisted locally — the tab says so instead of silently omitting them. ([#12479](https://github.com/diegosouzapw/OmniRoute/pull/12479)) +- **feat(dashboard):** the orchestration detail drawer gained a "Repeat" action for Cloud Agent, A2A and Conductor tasks — a two-click confirm (click once to arm, click again within the confirm window to fire) re-submits the original prompt/input as a new run. The button is disabled with an explanatory tooltip whenever the original input can't be recovered from the loaded task detail (e.g. it never carried a prompt, or the detail failed to load). Two limitations of the A2A variant, by design: it targets the `/a2a` JSON-RPC endpoint, which authenticates with an API key only (`REQUIRE_API_KEY=true` or a configured `OMNIROUTE_API_KEY` makes a dashboard-session repeat answer `HTTP 400` — surfaced verbatim in the drawer's error line, never as a success), and the `message/send` call is SYNCHRONOUS: the POST blocks for the whole skill run, so the success confirmation only appears once the repeated task finishes. A dashboard-authenticated A2A creation path is deliberately left to a follow-up — widening the endpoint's auth posture is an operator decision, not a side effect of this feature. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(a2a):** A2A task execution now records which memories were consulted for the task's last user message as `metadata.memoryHits` (id/key/type/content-snippet) plus a `memory_hits` history event, purely for observability — the retrieved memory is never injected into a skill's prompt or behavior. Gated by the `OMNIROUTE_A2A_MEMORY_HITS` kill-switch (default enabled; set to `0` to skip the recall lookup entirely). The drawer's new "Memory used" section lists these hits for a2a tasks and is omitted whenever there are none. Known limitation: recall only resolves under the keyless posture — a keyed caller's task owner is a SHA-256 prefix of the API key, while memory rows are keyed by the database api-key id, and no hash→id lookup exists today, so the hit list stays empty for keyed callers. The recorded hits are also kept out of the task's own `input` (and therefore out of the persisted input and of the "Repeat" request body), so repeating a task never re-sends the previous run's memory snippets. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(providers):** add a Perplexity Agent API provider (`perplexity-agent` / `pplx-agent`) for Perplexity `/v1/responses`, including the documented Anthropic, OpenAI, Google, xAI, DeepSeek, Z.AI, Moonshot/Kimi, NVIDIA, and Perplexity model IDs plus Anthropic-model `max_output_tokens` compatibility. ([#12103](https://github.com/diegosouzapw/OmniRoute/pull/12103)) — thanks @AIB1TAL0S +- **feat(providers):** add RPD (Requests Per Day) limit to provider rate limit overrides across UI, schemas, DB, and i18n ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) — thanks @b3nw +- **feat(providers): add UC Direct (uncensored.com Developer API), the metered OpenAI-compatible surface.** A standard OpenAI-compatible passthrough (default executor) for uncensored.com's official REST API at `https://api.uncensored.com/api/v1`: `X-api-key` auth (never-expiring `uai_sk_live_` key), `POST /chat/completions` with streaming SSE and native tool-calling, and the full live metered catalog (82 models across 15 providers, discovered from the public `GET /v1/models`). Registered as provider `uc-direct` (alias `ucd`). Complements the un-metered `uc` persona provider — same models, metered credits and a plain API key instead of a subscription session. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(providers): add UC (uncensored.com), the un-metered subscription "persona" chat as an OpenAI-compatible provider.** A WebSocket web-app port: a durable Clerk credential mints a short-lived session token per connect (browserless — no API key), driving UC's persona socket. Ships the browserless email-code login (request → verify → harvest), the 19 verified persona models (Claude Opus, Gemini, Grok, GLM, Kimi, DeepSeek, MiniMax, incl. the uncensored variants), prompted `` tool-calling with a per-model code-style dialect + auto-cure retry for guardrailed models, live ``/reasoning split, streaming + non-streaming OpenAI responses, and full quota/auth error surfacing (paywall / message-limit / rate-limit → 429, invalid session → 401 re-login). Full multimodal parity via the persona blob-upload layer: **vision** (image input, 15 vision-capable models), **document RAG** (PDF/doc upload, server-side extraction), **image generation** (22 models), **video generation** (14 models, async signed-url → poll), and **TTS** (streaming MP3). Registered as provider `uc` (alias `ucn`). The metered OpenAI-compatible Developer API is a separate `uc-direct` provider. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(logs):** add copy-all button to request detail modal ([#11083](https://github.com/diegosouzapw/OmniRoute/pull/11083)) — thanks @AndrianBalanescu +- **feat(check):** make serviceKinds required and add the reverse-walk provider consistency gate ([#11392](https://github.com/diegosouzapw/OmniRoute/pull/11392)) — thanks @Tushar49 +- **feat(providers):** only advertise verified-working connections by default ([#11448](https://github.com/diegosouzapw/OmniRoute/pull/11448)) — thanks @alltomatos +- **feat(resilience):** add hierarchical concurrency admission ([#11493](https://github.com/diegosouzapw/OmniRoute/pull/11493)) +- **feat(providers):** derive + surface expiry for JWT-bearing web cookies (#11497) ([#11505](https://github.com/diegosouzapw/OmniRoute/pull/11505)) — thanks @oyi77 +- **feat(tunnels):** support named cloudflared tunnels via CLOUDFLARED_CONFIG ([#11561](https://github.com/diegosouzapw/OmniRoute/pull/11561)) — thanks @ggiak +- **feat(compression):** make proactive context-compression threshold a live setting ([#11564](https://github.com/diegosouzapw/OmniRoute/pull/11564)) — thanks @ragnar-claude +- **feat(resilience):** add provider peak-hour protection ([#11622](https://github.com/diegosouzapw/OmniRoute/pull/11622)) — thanks @Neuron-Mr-White +- **feat(providers):** add 1min.ai provider ([#11631](https://github.com/diegosouzapw/OmniRoute/pull/11631)) — thanks @zero-executioner +- **feat(dashboard):** collapsible JSON tree viewer for request/response payloads ([#11703](https://github.com/diegosouzapw/OmniRoute/pull/11703)) — thanks @hartmark +- **feat(routing):** add LiquidAI LFM2.5-2.6B free tier via OpenRouter ([#11752](https://github.com/diegosouzapw/OmniRoute/pull/11752)) — thanks @brick30llc-ctrl +- **feat(pricing):** add GLM-5.3-Flash pricing, model specs, and catalog registration ([#11830](https://github.com/diegosouzapw/OmniRoute/pull/11830)) — thanks @AndrianBalanescu +- **feat(routing):** add relayMode for schema-locked context handoffs ([#11839](https://github.com/diegosouzapw/OmniRoute/pull/11839)) — thanks @raheemuddin786 +- **feat(config):** add support for runtime system prompt configuration and hot-reloading ([#11841](https://github.com/diegosouzapw/OmniRoute/pull/11841)) — thanks @raheemuddin786 +- **feat(routing):** expand connection-aware quota prefilter across combo strategies (#11682) ([#11850](https://github.com/diegosouzapw/OmniRoute/pull/11850)) — thanks @HouMinXi +- **feat(models):** reactive discovery sync after Antigravity model-not-found 404 ([#11888](https://github.com/diegosouzapw/OmniRoute/pull/11888)) — thanks @MumuTW +- **feat(models):** auto-aliases and token limits from synced Antigravity discovery ([#11889](https://github.com/diegosouzapw/OmniRoute/pull/11889)) — thanks @MumuTW +- **feat(settings):** raise sticky round-robin limit caps to 1000 ([#12015](https://github.com/diegosouzapw/OmniRoute/pull/12015)) — thanks @adivekar-utexas +- **feat(resilience):** operator-configurable global credential health check interval ([#12043](https://github.com/diegosouzapw/OmniRoute/pull/12043)) — thanks @adivekar-utexas +- **feat(quota):** use official OpenCode Go usage API ([#12124](https://github.com/diegosouzapw/OmniRoute/pull/12124)) — thanks @ddarkr +- **feat(resilience):** default the credential health check sweep to 60 minutes ([#12138](https://github.com/diegosouzapw/OmniRoute/pull/12138)) — thanks @adivekar-utexas +- **feat(quality):** new-code mode for the complexity and dead-code ratchets (Clean as You Code) ([#12142](https://github.com/diegosouzapw/OmniRoute/pull/12142)) +- **feat(dashboard):** orchestration canvas — unified model + snapshot hook (part 1/2) ([#12156](https://github.com/diegosouzapw/OmniRoute/pull/12156)) +- **feat(usage):** add Kilo Code balance and Kilo Pass quotas ([#12178](https://github.com/diegosouzapw/OmniRoute/pull/12178)) — thanks @killer30001000 +- **feat(providers):** refresh vendored ChatGPT Web connector to v4.0.7 ([#12181](https://github.com/diegosouzapw/OmniRoute/pull/12181)) — thanks @backryun +- **feat(providers):** manual "Clear cooldown" action in the cooling panel ([#12224](https://github.com/diegosouzapw/OmniRoute/pull/12224)) — thanks @patrykkopycinski +- **feat(providers):** restore ChatGPT Web via clean-room browser transport ([#12239](https://github.com/diegosouzapw/OmniRoute/pull/12239)) — thanks @backryun +- **feat(sse):** wire the PROVIDER_PROFILES window gate into the global provider cooldown ([#12247](https://github.com/diegosouzapw/OmniRoute/pull/12247)) +- **feat(providers):** modernize CLOVA Studio chat and embeddings ([#12277](https://github.com/diegosouzapw/OmniRoute/pull/12277)) — thanks @backryun +- **feat(browser):** adopt Obscura as primary headless browser engine with Chromium fallback ([#12286](https://github.com/diegosouzapw/OmniRoute/pull/12286)) — thanks @opensource-elearning +- **feat(sse):** full language parity for output styles (es/de/fr/it/ru/zh + autoDetect) ([#12289](https://github.com/diegosouzapw/OmniRoute/pull/12289)) +- **feat(quality):** complete test:scoped — --full map rebuild, stdin selection, CI loader parity (#8084 D1) ([#12353](https://github.com/diegosouzapw/OmniRoute/pull/12353)) +- **feat(providers):** refresh Fable, Cursor, and Devin catalogs ([#12367](https://github.com/diegosouzapw/OmniRoute/pull/12367)) — thanks @backryun +- **feat(video):** redact transcript text from logs and durable memory (#12150 P1) ([#12427](https://github.com/diegosouzapw/OmniRoute/pull/12427)) +- **feat(dashboard):** parent-link, genuine-continuation badge, and modal perf fixes ([#12448](https://github.com/diegosouzapw/OmniRoute/pull/12448)) — thanks @hartmark +- **feat(dashboard):** adaptive context-budget dial on compression panel ([#12488](https://github.com/diegosouzapw/OmniRoute/pull/12488)) — thanks @HouMinXi +- **feat(providers):** refresh Z.ai Web models and browser transport ([#12524](https://github.com/diegosouzapw/OmniRoute/pull/12524)) — thanks @backryun +- **feat(video):** redact raw client-snapshot transcript fields in the detailed log (#12150 P2) ([#12528](https://github.com/diegosouzapw/OmniRoute/pull/12528)) +- **feat(providers):** refresh NVIDIA hosted models ([#12538](https://github.com/diegosouzapw/OmniRoute/pull/12538)) — thanks @backryun +- **feat(quota):** Moonshot Open Platform balance and TPD lock for custom nodes ([#12590](https://github.com/diegosouzapw/OmniRoute/pull/12590)) — thanks @HouMinXi +- **feat(video):** redact transcript in the in-memory pending-request snapshot (#12430 item 6) ([#12596](https://github.com/diegosouzapw/OmniRoute/pull/12596)) +- **feat(dashboard):** link a log entry's Conversation Context to its owning conversation ([#12646](https://github.com/diegosouzapw/OmniRoute/pull/12646)) — thanks @hartmark +- **feat(catalog):** eligibility-gated free-tier bucket ([#12669](https://github.com/diegosouzapw/OmniRoute/pull/12669)) +- **feat(db):** fail-closed previous_response_id continuation for redacted video turns (#12150 P2b) ([#12707](https://github.com/diegosouzapw/OmniRoute/pull/12707)) +- **feat(gamification): pay the documented `streak_bonus` and `badge_unlock` XP rewards.** `XP_REWARDS` listed both rewards but the award pipeline never paid them: the private reward table in `events.ts` omitted them, `updateStreak()` did not report when a streak extended, and badge unlocks carried no XP. Every request that extends a daily streak now pays `streak_bonus × streak length` once per UTC day (guarded by a same-day `xp_audit_log` check), and every badge unlocked through the pipeline pays `badge_unlock` once per badge (guarded by the `user_badges` primary key; `unlockBadge()` now reports whether it inserted). Bonus XP flows through the same `addXp` + level sync + global/weekly/monthly leaderboard path as action XP, so level-ups and rankings include it. The Radar supporter recognition unlock stays XP-free. (#12522 — thanks @pacocartones) +- **feat(providers):** advertise a `free-tier` capability in the provider plugin manifest for every provider with documented free models, so sidecars and dashboards can filter free-capable providers without reading the quota catalog ([#12786](https://github.com/diegosouzapw/OmniRoute/pull/12786)) — thanks @maxmad64bis +- **feat(providers):** Added EURouter as an OpenAI-compatible API-key gateway (`https://api.eurouter.ai/v1`), with live model discovery via `passthroughModels`. Its copy states that models are served by third-party upstreams listed per model, so an EU-based router is not read as EU data residency for inference. ([#13025](https://github.com/diegosouzapw/OmniRoute/pull/13025)) — thanks @ntdatt812 +- **feat(providers):** Added GreenPT as an OpenAI-compatible API-key provider (`https://api.greenpt.ai/v1`), with live model discovery via `passthroughModels`. No free-inference badge: the published docs describe a free API subscription billed per token, not a free tier. ([#13024](https://github.com/diegosouzapw/OmniRoute/pull/13024)) — thanks @ntdatt812 +- **feat(i18n):** 9 new locales — Greek (`el`), Croatian (`hr`), Serbian (`sr`), Lithuanian (`lt`), Estonian (`et`), Latvian (`lv`), Slovenian (`sl`), Maltese (`mt`), Irish (`ga`) — across the dashboard, docs mirrors, CLI, README and the site; OmniRoute now ships every official EU language (51 locales). (#13044) +- **feat(i18n):** 8 new locales — Kannada (`kn`), Malayalam (`ml`), Odia (`or`), Punjabi (`pa`), Nepali (`ne`), Sinhala (`si`), Burmese (`my`), Khmer (`km`) — across the dashboard, docs mirrors, CLI, README and the site (59 locales). The translator now restores the ICU literal escape around angle placeholders and splits oversized docs sections before translating. (#0000) ([#13660](https://github.com/diegosouzapw/OmniRoute/pull/13660)) +- feat(providers): **list Agnes 3.0 Flash as the current free chat model, drop retired 1.5 Flash, add Image 2.0/2.5 Flash plus Video 2.5/2.5 Flash, and discover the live `/v1/models` catalog (including the CN host `api.agnes-ai.cn`).** `agnes-1.5-flash` now forwards to `agnes-3.0-flash`. Video 2.5 polls `GET /v1/videos/{id}` (not the V2.0 `/agnesapi` contract). Live `/v1/models` (2026-09-09) no longer serves 1.5; the wiki marks it deprecated. 3.0 Flash is 512K context / 65,536 max output, same window as 2.5. CN-region keys use the existing per-connection base-URL field, default stays `apihub.agnes-ai.com`. ([#13120](https://github.com/diegosouzapw/OmniRoute/pull/13120)) — thanks @HouMinXi +- **feat(sse):** Codex and OpenAI catalogs list GPT-6 Astra with effort aliases (`-low` through `-ultra`); Codex CLI identity pins `@openai/codex@0.153.4` in lockstep with the image ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **feat(dashboard):** the orchestration History tab gained a "Compare runs" mode — toggling it turns each grid cell into a 2-item selection queue (a 3rd click drops the oldest pick), and picking two cells opens a side-by-side comparison panel instead of the usual detail drawer. The panel fetches both runs' detail the same way the drawer does (falling back to persisted history once a run leaves the live TTL window) and shows, per side: identity/source/state, start time, a signed `right - left` delta for duration/cost/event count, the event timeline aligned by index, and any memory hits. One side's fetch failing never blocks the other, and a delta is only ever computed when both sides have a finite value — otherwise it renders "—", never `NaN`. Comparing two runs from different sources or skills still works; a banner marks the deltas as informational rather than hiding them, since the two runs aren't a strict apples-to-apples pair. ([#12677](https://github.com/diegosouzapw/OmniRoute/pull/12677)) +- **feat(dashboard):** badge a conversation that never reached a clean stop ([#12717](https://github.com/diegosouzapw/OmniRoute/pull/12717)) — thanks @hartmark +- **feat(release):** reconcile-changelog tool + version-anchored fragment aggregation ([#12987](https://github.com/diegosouzapw/OmniRoute/pull/12987)) +- **feat(mcp):** carry context_length through MCP list_models_catalog projection (#12776) ([#13406](https://github.com/diegosouzapw/OmniRoute/pull/13406)) — thanks @KooshaPari +- **feat(cli):** make startup readiness budget configurable (#13369) ([#13433](https://github.com/diegosouzapw/OmniRoute/pull/13433)) — thanks @KooshaPari + ### 🐛 Bug Fixes +- **security(streaming):** sanitize generic mid-stream error messages before emitting OpenAI, + Responses, or Claude SSE failure frames and before diagnostic logging, while preserving raw + failures for internal classification and keeping client disconnects out of provider failure state. ([#12457](https://github.com/diegosouzapw/OmniRoute/pull/12457)) +- **fix(claude):** `blockExtraUsage: false` no longer lets 5h quota preflight skip the account; extra usage is billed after the session bar is gone, so the request must reach Anthropic ([#12803](https://github.com/diegosouzapw/OmniRoute/pull/12803)) — thanks @HouMinXi +- Sanitize HuggingChat conversation-creation and message-send transport failures before they reach client error bodies or provider logs. ([#12467](https://github.com/diegosouzapw/OmniRoute/pull/12467)) +- **fix(dashboard):** Keep local and theme-aware provider SVG icons at a definite layout size so Chromium does not collapse them to 0×0 after the v3.8.50 image-rendering change ([#12054](https://github.com/diegosouzapw/OmniRoute/pull/12054)) — thanks @ponkcore +- **fix(security):** Sanitize provider and runtime failures before public API, SSE and MCP responses and before persistent request, proxy and usage logs, preventing credentials, stack traces and host filesystem paths from crossing those boundaries while preserving stable error codes and useful diagnostics. ([#12506](https://github.com/diegosouzapw/OmniRoute/pull/12506)) +- **fix(cli):** `omniroute tunnel create` no longer crashes with `Cannot read properties of undefined (reading optsWithGlobals)` — removed the duplicate positional argument that caused Commander.js to misalign the action callback parameters ([#12295](https://github.com/diegosouzapw/OmniRoute/issues/12295)) ([#12368](https://github.com/diegosouzapw/OmniRoute/pull/12368)) — thanks @gonisulaimann +- Stop advertising Gemini Live-only models as supported audio endpoints until OmniRoute proxies the bidirectional Live protocol. ([#11710](https://github.com/diegosouzapw/OmniRoute/pull/11710)) — thanks @RaviTharuma +- **fix(kie):** reroute `flux/kontext` off the KIE Market `createTask` flow — it is catalogued with `isMarket: true` but has no Market catalog page, so KIE rejected it with "model name not supported"; it now hits the dedicated `POST /api/v1/flux/kontext/generate` / `GET /api/v1/flux/kontext/record-info` endpoints instead (#11296). ([#11985](https://github.com/diegosouzapw/OmniRoute/pull/11985)) — thanks @hartmark +- **fix(kie):** correct 12 more KIE Market catalog ids that were sent to `createTask` unchanged but diverge from KIE's documented upstream `model` values — GPT Image 2 T2I/I2I (drops the `gpt/` prefix), GPT Image 1.5 T2I/I2I (`gpt-image/` namespace), Seedream 5.0 Lite T2I/I2I (drops the `.0`), all 4 Flux 2 variants (`flux-2/` namespace, generic variant renamed `flex`), and Wan 2.7 Image / Image Pro (dash instead of dot) — each verified individually against the literal example request published on docs.kie.ai. `#11326`'s "everything else already matches" claim was wrong a second time (#11296); `z-image/4.0-*`/`z-image/4.5-*` and `flux/kontext` remain open, documented as unresolved in `KIE_MARKET_UPSTREAM_MODEL_IDS`'s comment pending further verification. ([#11667](https://github.com/diegosouzapw/OmniRoute/pull/11667)) +- **fix(dashboard):** `useApiKeySave.handleSaveApiKey` no longer forces a full upstream `/models` catalog sync on every non-curated provider connection save — callers can now pass `skipModelSync: true` to opt out, so a workflow that only wants to add one manual model no longer floods the provider's available-models list with hundreds/thousands of synced entries. The flag is a client-side intent signal only and is stripped before the connection payload is POSTed to `/api/providers`; default behavior (full sync on save) is unchanged when the flag is omitted (#11324) ([#11664](https://github.com/diegosouzapw/OmniRoute/pull/11664)) +- **Provider connections:** keep `tokenExpiresAt` when a connection is created. The create-path allowlist omitted it, so every insert stored NULL and the dashboard token badge could read a fresh connection as expired until its first background refresh ([#11368](https://github.com/diegosouzapw/OmniRoute/pull/11368)). — thanks @ntdatt812 +- **fix(combos):** the combo builder's precision-select, global-model-search, and manual-entry flows now serialize a model step's `model` string using the provider's already-computed routing-alias prefix (e.g. `oc/`) instead of rebuilding it from the raw canonical `providerId`, fixing the no-auth "OpenCode Free" provider (`opencode`) being routed to the unrelated paid "OpenCode Zen" provider (`opencode-zen`) because `opencode` doubles as a manual routing-prefix override ([#11433](https://github.com/diegosouzapw/OmniRoute/issues/11433)). ([#11670](https://github.com/diegosouzapw/OmniRoute/pull/11670)) +- **fix(radar):** the catalog feed cache now keeps `generatedAt`, the date the feed's data was built, next to `fetchedAt`, the date this install downloaded it (#11435). The feed schema requires that date and the sync path validates it, but the cache dropped it — so a feed fetched minutes ago and one carrying weeks-old figures looked identical to everything downstream, including the dashboard's "Last fetched" line. `getRadarCatalog().meta` and `GET /api/radar/status` now report both dates, the latter as its own field rather than folded into `version` — and omitted entirely for the offers and intel caches, which keep no build date, where a `null` would read as "unknown" rather than "never stored". The dashboard still shows only the fetch time; surfacing the build date there needs a new translated label and is left to a follow-up. Rows cached before migration 163 read back as `null`: unknown stays unknown instead of borrowing the fetch time. The referrals cache has persisted the same date since migration 142. — thanks @maxmad64bis +- **fix(cli):** postinstall no longer fills `JWT_SECRET` and `API_KEY_SECRET` in the installed package's `.env` (#11436). `.env.example` ships both blank on purpose: the server restores them from its durable store, or generates and persists them there, in `ensureSecrets()`. Pre-filling them defeated that — the file lives inside the package directory, so `npm i -g` replaced it and postinstall wrote _different_ values, while `ensureSecrets()` (which only acts on an empty variable) never got to restore the real ones. Both secrets rotated silently on every update, invalidating dashboard sessions and API-key CRCs. `STORAGE_ENCRYPTION_KEY` left the same list for the same reason in #1622; its comment pointed at a function that no longer exists and now names the real provisioning path. — thanks @maxmad64bis +- **fix(cli):** the CLI now says when a loaded `.env` lives inside the installed package directory (#11437). It already announces every env file it reads, without distinguishing the ones that survive an update from the one that does not: `npm i -g` replaces the package directory wholesale, so values set there are gone at the next update, silently. The warning names the durable path to move them to, and fires only when that file actually supplied a value — a file entirely shadowed by a durable one supplied nothing. A development checkout stays silent: there the same path is stable and documented in `SETUP_GUIDE.md`. — thanks @maxmad64bis +- **fix(free-models):** the shared `isFreeModel()` predicate no longer reports catalog entries marked `freeType: "discontinued"` as free, so `hidePaidModels` can't route to Pollinations' seven premium models that now require a paid key ([#11441](https://github.com/diegosouzapw/OmniRoute/pull/11441)) — thanks @maxmad64bis +- fix(ui): let AnySearch use the normal provider-icon fallback when LobeHub has no matching icon (#11449) +- Fixed the v3.8.50 Costs and Analytics dashboards so flat-rate Claude Code usage can be shown as an explicitly requested token-price estimate without changing default billed-cost semantics. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed archived usage retention so each request is priced individually instead of pricing a day's summed tokens once, which understated archived cost whenever a day mixed cache-heavy and ordinary requests. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed the Costs dashboard so it discloses when displayed figures include flat-rate token-price estimates instead of labelling them as billed spend, using the flag the analytics API already returns; the month-end projection and the CSV/JSON exports carry the same marker, and billed-cost mode is unchanged. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- **fix(combo):** attach the same combo-diagnostics trace (`poolSize`/`attemptOrder`/`excluded`/`terminalReason`, plus `x-omniroute-combo-*` headers) to the round-robin strategy's and the nested pipeline/fusion runtime-unit loop's "Maximum combo retry limit reached" 503 that the priority-strategy path already attaches for the identical terminal condition — previously those two paths returned a bare, contextless 503 ([#11462](https://github.com/diegosouzapw/OmniRoute/issues/11462)). ([#11665](https://github.com/diegosouzapw/OmniRoute/pull/11665)) +- **fix(bun):** use native `bun:sqlite` in `bootstrap-env` and `sync-env` to avoid loading `better-sqlite3` N-API addon during Bun startup ([#11468](https://github.com/diegosouzapw/OmniRoute/pull/11468)) — thanks @TheDemonTuan +- **fix(docker-bun):** make `Dockerfile.bun` install reliable with pre-install script helpers and native `bun:sqlite` smoke check ([#11470](https://github.com/diegosouzapw/OmniRoute/pull/11470)) — thanks @TheDemonTuan +- **fix(docker-bun):** remove every vendored `better-sqlite3` native addon from the Bun runtime image so startup cannot abort before the `bun:sqlite` fallback ([#11482](https://github.com/diegosouzapw/OmniRoute/pull/11482)) — thanks @TheDemonTuan +- **fix(autoCombo):** effort/alias model variants (`gpt-5.6-sol-xhigh`, `gpt-5.6`, cursor's `claude-4.6-opus-high`) inherit their base model's task fitness instead of falling to the wildcard 0.5 ([#11492](https://github.com/diegosouzapw/OmniRoute/pull/11492)) — thanks @MumuTW +- fix(db): rate-limit repeated Arena ELO leaderboard fetch-failure warnings instead of logging one per sync attempt (#11500) ([#11989](https://github.com/diegosouzapw/OmniRoute/pull/11989)) — thanks @hartmark +- **fix(db):** dedupe the raw `[Encryption] Decryption failed...` log line emitted by the lazy-decrypt views (`createLazyRowProxy`/`createLazyConnectionView`), which power `getProviderConnections()` and were re-triggering that line on every CredentialHealth/model-sync cycle for the same corrupt or stale-key credential — a fresh Proxy over a fresh row on every cycle meant the per-proxy memoization never suppressed it, unlike the dedup `decryptConnectionFields()` already had since [#9927](https://github.com/diegosouzapw/OmniRoute/issues/9927). Now shares that dedupe tracking so the line logs at most once per credential ([#11500](https://github.com/diegosouzapw/OmniRoute/issues/11500)). ([#11678](https://github.com/diegosouzapw/OmniRoute/pull/11678)) +- **fix(intelligence):** Arena ELO sync stores a synthesized base-model row for every effort/harness variant (`claude-opus-5-max` → `claude-opus-5`, `gpt-5.6-sol-xhigh (codex-harness)` → `gpt-5.6-sol`), so bare model requests reach the synced intelligence layer instead of the static fallback table; the hand-maintained `MODEL_ALIAS_MAP` — which collapsed generations (`gpt-5.5` → `gpt-5`) — is gone ([#11506](https://github.com/diegosouzapw/OmniRoute/pull/11506)) — thanks @MumuTW +- **fix(autoCombo,sse):** catalog hygiene — the static task-fitness table no longer ranks retired models above live ones (dead rows dropped, patterns matched on segment boundaries), the 7 deprecation aliases that forwarded to retired or non-existent models now point at the vendors' published replacements, an alias is no longer rewritten when the serving provider offers the id as-is, and a new `check:model-lifecycle` gate diffs both tables against a vendor lifecycle snapshot ([#11507](https://github.com/diegosouzapw/OmniRoute/pull/11507)) — thanks @MumuTW +- **fix(sse):** stop dropping resolved `thoughtSignature` values on parallel (multi tool-call) turns sent to Gemini 3.x — the claude→gemini and openai→gemini translators previously kept the signature only on the *first* function call of a message, causing Gemini to reject subsequent calls in the same turn with HTTP 400 "Function call is missing a thought_signature"; each function call now keeps its own resolved signature ([#11510](https://github.com/diegosouzapw/OmniRoute/issues/11510)). ([#11669](https://github.com/diegosouzapw/OmniRoute/pull/11669)) +- Treat Docker publishing from a non-default release branch as an expected skip instead of a failed workflow, while preserving the guard that prevents it from overwriting the `next` channel. ([#11527](https://github.com/diegosouzapw/OmniRoute/pull/11527)) — thanks @pacocartones +- fix(search): prefer credentialed providers over duckduckgo-free fallback (#11524) — the fallback-only loop ran before the credentialed-providers loop in `executeWebSearch`, making configured providers unreachable when `duckduckgo-free` was available. ([#11565](https://github.com/diegosouzapw/OmniRoute/pull/11565)) — thanks @vsd2807 +- **fix(sse):** cap the upstream headers-wait phase for STREAMING requests to a client-realistic ceiling (110s, under Codex's own ~120s hard client-abort window) instead of the flat 10-minute `FETCH_TIMEOUT_MS` default — that default was 5x longer than the body-phase readiness watchdog's own adaptive bound, so a request whose upstream never returned any response at all (not even headers, e.g. a stalled NVIDIA target behind a tool-heavy Responses→Chat translation) kept the client connection alive on keepalives only, guaranteeing the client's own patience ran out first with an opaque 499 instead of OmniRoute detecting and failing the stall fast. Non-streaming requests are unaffected — they keep the existing flat default (`open-sse/utils/fetchStartTimeoutPolicy.ts`) (#11526) ([#11668](https://github.com/diegosouzapw/OmniRoute/pull/11668)) +- **fix(ci):** protocol-clients E2E harness boots the peer-stamped custom server (`run-next.mjs dev`) instead of the bare `next` CLI, so `LOCAL_ONLY` locality resolves from the real TCP peer stamp and `/api/mcp/audit` answers 200 (open bootstrap preserved via an `OMNIROUTE_E2E_BOOTSTRAP_MODE=open` credential clear in `run-next.mjs`, pinned to loopback `HOST`) instead of a deterministic 403 (#11535) ([#11549](https://github.com/diegosouzapw/OmniRoute/pull/11549)) — thanks @jonlwheat2-gif +- **fix(docs):** the Auto-Combo engine is described with the number of factors it actually declares. Four different counts were in circulation — 6, 9, 13 and 14 across `AGENTS.md`, four guides and six code comments — while `DEFAULT_WEIGHTS` declares 15. `check:docs-counts` now reads that number from the source, so adding a factor without updating the prose fails CI ([#11545](https://github.com/diegosouzapw/OmniRoute/pull/11545)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **fix(quota-share):** in-flight leases are per request, so an aborted request's slot ages out instead of being kept alive by later traffic on the same connection ([#11547](https://github.com/diegosouzapw/OmniRoute/pull/11547)) — thanks @abhisheksharma2411 +- The free-tier summary route now serves the refreshed Radar catalog when the feed is active — the same numbers the dashboard shows — and states which catalog answered plus its real build date, instead of always reporting release-frozen figures with a stale curation date. The supporter-key live feed stays reserved to authenticated callers of the instance. ([#11550](https://github.com/diegosouzapw/OmniRoute/pull/11550)) — thanks @maxmad64bis +- **sse:** fix LiveWS/embed-WS servers crashing at startup under the Node/tsx runtime — `liveServer.ts`, `embedWsProxy.ts` and `apiBridgeServer.ts` imported `@/shared/utils/httpClientAbortGuard` without the `.mjs` extension, so the client-abort crash guard added by [#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556) was unreachable and every dependent test failed with `ERR_MODULE_NOT_FOUND` ([#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556)). ([#11671](https://github.com/diegosouzapw/OmniRoute/pull/11671)) — thanks @jonlwheat2-gif / @yxyxy +- fix(chat): shed any of the matched provider-node's routing identifiers (public prefix or internal id) before model lookup, so composites like `//` no longer reach upstream verbatim (#11557) — thanks @maxmad64bis +- **fix(cache):** a semantic-cache entry now expires on its TTL instead of surviving until the next UTC midnight, and `dbEntries` stops counting expired rows ([#11573](https://github.com/diegosouzapw/OmniRoute/pull/11573)) — thanks @ntdatt812 +- **fix(api):** `/v1/models` schedules its stale-while-revalidate rebuild through Next's `after()` again, so a stale catalog reaches the client before the rebuild blocks the event loop ([#11574](https://github.com/diegosouzapw/OmniRoute/pull/11574)) — thanks @ntdatt812 +- **fix(check):** the `known-symbols` executor conformance gate awaits the now-async `getExecutor()`, so it stops reporting all 142 registered aliases as dead symbols and can detect a lazy import that fails ([#11582](https://github.com/diegosouzapw/OmniRoute/pull/11582)) — thanks @ntdatt812 +- **fix(pack):** register `bin/cli/utils/volatileEnvPath.mjs` in `PACK_ARTIFACT_REQUIRED_PATHS`, so the prune can no longer silently drop a module the published CLI imports on every boot ([#11588](https://github.com/diegosouzapw/OmniRoute/pull/11588)) — thanks @ntdatt812 +- **i18n(vi):** translate the five reliability-column strings on the free-provider rankings page, which rendered as `__MISSING__:` markers in the Vietnamese UI ([#11589](https://github.com/diegosouzapw/OmniRoute/pull/11589)) — thanks @ntdatt812 +- **resilience:** restore the expired-connection retry-budget probe in the token-health sweep — the `!isGitHubAccessTokenOnlyConnection` carve-out reintroduced by #11608 contradicted the boundary pinned by #11592, so a GitHub connection parked at `expired` with retry budget remaining was never probed and could never self-heal ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)). ([#11672](https://github.com/diegosouzapw/OmniRoute/pull/11672)) — thanks @jonlwheat2-gif / @ntdatt812 +- **fix(dashboard):** Provider Stats now displays combo success rates on their native 0–100 percentage scale instead of multiplying them twice ([#11595](https://github.com/diegosouzapw/OmniRoute/pull/11595)) — thanks @pacocartones +- **fix(providers):** route DuckDuckGo Web Chat through the verified `duck.ai` endpoint, refresh its free model catalog, and avoid forwarding unsolved anti-abuse challenges ([#11597](https://github.com/diegosouzapw/OmniRoute/pull/11597)) — thanks @solstxce +- **fix(ui):** Console log Refresh and Copy controls now expose localized accessible names, keep copy actions visible on keyboard focus, and announce copy completion safely ([#11599](https://github.com/diegosouzapw/OmniRoute/pull/11599)) — thanks @pacocartones +- Dropped the stale-`.eslintcache` `restore-keys` fallback from both "Restore ESLint file cache" steps in `ci.yml`, so the blocking `Lint` job can no longer be served per-file verdicts computed under a different lint config, suppressions file or lockfile. `quality.yml` had already dropped it in #11963; `ci.yml` — the workflow that actually gates PRs — had not (#11600). ([#11996](https://github.com/diegosouzapw/OmniRoute/pull/11996)) +- fix(ci): pass `--pass-on-unpruned-suppressions` in `run-eslint-json.mjs` so the CI Lint job no longer fails when a suppression is merely orphaned by a genuine fix, mirroring the identical fix already in `validate-release-green.mjs` (#11600) ([#11983](https://github.com/diegosouzapw/OmniRoute/pull/11983)) — thanks @hartmark +- fix(handoff): enforce provider allowlist for universal handoff (#11602) — universal handoff now skips summarization when the selected summary model's provider is not included in the configured provider allowlist. ([#11619](https://github.com/diegosouzapw/OmniRoute/pull/11619)) — thanks @vsd2807 +- **fix(dashboard):** Show a stable error state when Search Analytics returns an HTTP error or malformed data ([#11603](https://github.com/diegosouzapw/OmniRoute/pull/11603)) — thanks @pacocartones +- **test(gamification):** pin the aggregate profile level to the XP-derived semantics of #11604 — `getAggregateXp()` now derives `currentLevel` from the summed XP (`calculateLevel(sum)`), not `MAX(stored current_level)`, and the #3484 fixture levels are aligned with the XP curve ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)). ([#11676](https://github.com/diegosouzapw/OmniRoute/pull/11676)) — thanks @jonlwheat2-gif / @pacocartones +- **fix(dashboard):** Keep the Profile level and progress aligned with aggregate XP, including bounded handling for invalid totals ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)) — thanks @pacocartones +- **fix(dashboard):** Prevent locked hidden badges from revealing their icon or opening private badge details before they are earned ([#11605](https://github.com/diegosouzapw/OmniRoute/pull/11605)) — thanks @pacocartones +- **fix(dashboard):** Restore keyboard focus after shared modals close and cancel delayed autofocus during cleanup ([#11607](https://github.com/diegosouzapw/OmniRoute/pull/11607)) — thanks @pacocartones +- **fix(dashboard):** Enable Enter and Space activation for clickable data-table rows without hijacking nested controls ([#11610](https://github.com/diegosouzapw/OmniRoute/pull/11610)) — thanks @pacocartones +- **fix(autoCombo,sse):** vendor-retired catalog ids are dropped from the auto-combo candidate pool and no longer win on leftover `arena_elo` / `user_override` rows; `getModelLifecycleDecision` consults `model-lifecycle.json` (prefix-stripped) so aggregator traffic is not `untracked` for ids the snapshot already knows ([#11625](https://github.com/diegosouzapw/OmniRoute/issues/11625)) ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)) — thanks @MumuTW +- **test(chatcore):** move Codex/Claude combo fixtures off models the lifecycle guard now rejects — `gpt-5.1-codex`/`gpt-5-codex` are vendor-retired (snapshot, #11626) and `claude-3-5-sonnet-20241022` is shut down, so native-passthrough and combo-fallback tests switched to `gpt-5.6-sol` and `claude-sonnet-4.6` ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)). ([#11675](https://github.com/diegosouzapw/OmniRoute/pull/11675)) — thanks @jonlwheat2-gif / @MumuTW +- fix(opencode-plugin): stop a free-tier budget that rounds up across a magnitude from rendering as `1000K`/`1000M` in the model picker — `fmtTokens` chose its unit from the raw token count and then rounded with `toFixed(1)`, so 999,950–999,999 printed as `1000K` rather than `1M` and just under a billion printed as `1000M` rather than `1B` ([#11684](https://github.com/diegosouzapw/OmniRoute/pull/11684)) — thanks @abhisheksharma2411 +- Cap prose-derived Antigravity quota resets at the configured model cooldown maximum while preserving authoritative Retry-After headers and Google RetryInfo hints. (#11695) Thanks @pacocartones. ([#11823](https://github.com/diegosouzapw/OmniRoute/pull/11823)) +- Compare `_rebaseline_*` notes structurally in the ratchet-bank verifier so an object-valued note no longer reports itself as rewritten on an unmodified tree — the nightly `bank-ratchet-shrinks` lane had been aborting on every run since 2026-08-11, leaving 41,672 lines of earned file-size shrinks unbanked (#11702). ([#11892](https://github.com/diegosouzapw/OmniRoute/pull/11892)) — thanks @MumuTW +- **fix(build):** `prepublish.ts` bundles the ChatGPT Web (Codex) MCP bridge through `runBuildTool()` instead of spawning `npx.cmd` raw, fixing the build crash on Node ≥ 20/Windows where `.cmd` shims cannot be spawned without a shell (EINVAL) ([#11704](https://github.com/diegosouzapw/OmniRoute/issues/11704)) ([#11706](https://github.com/diegosouzapw/OmniRoute/pull/11706)) — thanks @jonlwheat2-gif +- fix(codex): keep `parallel_tool_calls:false` on the translated Codex Responses Lite path (#11707) ([#11984](https://github.com/diegosouzapw/OmniRoute/pull/11984)) — thanks @hartmark +- **fix(tests):** the new Gemini Web Images retirement test (#11708) called the now-async `getExecutor()` synchronously, throwing `TypeError: getExecutor(...).getProvider is not a function` instead of asserting Gemini Web chat stays available. Awaited to match the R0.3 executor-registry refactor (#11220). ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(search):** Allow REST and MCP web-fetch requests to select the advertised `anysearch-search` provider explicitly ([#11716](https://github.com/diegosouzapw/OmniRoute/pull/11716)) — thanks @pacocartones +- **fix(providers):** Keep overnight peak-hour protection active after midnight according to the window's configured start day ([#11718](https://github.com/diegosouzapw/OmniRoute/pull/11718)) — thanks @pacocartones +- **fix(providers):** correct all five g4f.space gateway descriptions to document proof-of-work credits or member API keys, variable limits, and the remote prompt boundary instead of promising free keyless access at five requests per minute ([#11738](https://github.com/diegosouzapw/OmniRoute/pull/11738)). +- **fix(db):** `model_capabilities` is created by a migration instead of lazily on the first models.dev sync, so a clean install and an upgraded install converge on the same schema regardless of which features have run ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(ci):** `check:install-upgrade` now fails on an `npm` install truncated by ENOSPC (npm reports it as a warning and still exits 0), authenticates its health probe so the version assertion works against the hardened health payload, frees the clean-install tree before the upgrade phase, and no longer reports a schema divergence computed from a boot that never served ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(routing):** harden session-affinity key derivation against oversized explicit IDs and empty structural-payload collisions, while recognizing Gemini and common text request shapes ([#11746](https://github.com/diegosouzapw/OmniRoute/pull/11746)) — thanks @MumuTW +- **fix(packages/browser-pool):** regenerate `package-lock.json` so the `packages/browser-pool` workspace's locked `playwright`/`@types/node` (and transitives) match its `package.json` specs, fixing cache-only/offline installs (`npm ci --offline`, Nix `buildNpmPackage`) that previously failed with `ENOTCACHED` ([#11747](https://github.com/diegosouzapw/OmniRoute/issues/11747)) — thanks @benjaminkitt ([#11784](https://github.com/diegosouzapw/OmniRoute/pull/11784)) — thanks @geekyNads +- **fix(assets):** Stop distributing 79 provider and CLI logos without verified source/license provenance, keep every provider/tool available through the internal generic icon, and prevent those IDs from falling through to an external icon CDN. ([#11750](https://github.com/diegosouzapw/OmniRoute/pull/11750)) +- **fix(providers):** retire the common `chatgpt-web` / `cgpt-web` provider because its pre-key and proof-of-work provenance could not be cleared ([#11754](https://github.com/diegosouzapw/OmniRoute/pull/11754)) — explicit chat and image requests, slash-prefixed models, persisted aliases, proxy overrides, and combo targets now fail closed with a sanitized HTTP `410` / `PROVIDER_RETIRED` before upstream dispatch. Migration 163 tombstones matching connections and invalidates their active leases without deleting history or widening API-key allowlists. The independent `chatgpt-web-codex` / `cgpt-codex` provider and its MIT-noticed vendored implementation remain available. +- **fix(usage):** quota and usage refresh no longer 409 when an exclusive lease reserves the connection ([#11758](https://github.com/diegosouzapw/OmniRoute/pull/11758)) — thanks @TheDemonTuan +- Keep the embedding registry's vector width and `embedding` type on models when a synced model exists for the same id, so `/v1/models` no longer reports registry-described embedding models widthless or untyped (#11761) — thanks @rahilmavani +- Correct `google/gemini-embedding-001` on the OpenRouter route to 3072 dimensions, the width it returns when `dimensions` is not sent (#11761) — thanks @rahilmavani +- **fix(vision-bridge):** Restrict automatic vision routing and fallbacks to models available in authoritative live provider catalogs, including aliases and effort variants ([#11767](https://github.com/diegosouzapw/OmniRoute/issues/11767)) — thanks @pacocartones ([#11813](https://github.com/diegosouzapw/OmniRoute/pull/11813)) +- **fix(providers):** reclassify Cerebras as a one-time $5 signup credit (payment method required, 30-day validity), not a recurring no-card 1M tokens/day trial ([#11773](https://github.com/diegosouzapw/OmniRoute/issues/11773)) ([#12591](https://github.com/diegosouzapw/OmniRoute/pull/12591)) — thanks @HouMinXi +- fix(cli): stop prepublish from re-rebuilding the already-built ESM-only opencode-plugin dist (#11787) ([#11990](https://github.com/diegosouzapw/OmniRoute/pull/11990)) — thanks @hartmark +- **fix(db):** Fresh installs no longer log a non-fatal `no such table: compression_run_telemetry` warning when retention cleanup runs before the lazily-created telemetry table exists ([#11802](https://github.com/diegosouzapw/OmniRoute/pull/11802)) — thanks @RaviTharuma +- fix(sse): set X-OmniRoute-Selected-Connection-Id on successful combo dispatches so downstream consumers stop falling back to an empty connection id (#11810) ([#11986](https://github.com/diegosouzapw/OmniRoute/pull/11986)) — thanks @hartmark +- **fix(services):** embedded CLIProxyAPI lifecycle checks now use public `/healthz`, while model discovery uses the configured dedicated data-plane API key instead of the management password ([#11811](https://github.com/diegosouzapw/OmniRoute/pull/11811)) — thanks @RaviTharuma +- Fix persisted model steps returning an error in the playground route simulator, and warn when structural steps cannot be simulated. ([#11862](https://github.com/diegosouzapw/OmniRoute/pull/11862)) — thanks @pacocartones +- **fix(providers):** Antigravity's dynamic mitmAlias table no longer routes `gemini-3.7-flash-{high,medium,low}` to a literal tier-suffixed upstream id just because one connected account's own discovery listed it directly — those display ids always resolve through the safe `gemini-3.7-flash-tiered` static alias, so one account's Google-provisioned access no longer 404s every sibling account of the provider ([#11824](https://github.com/diegosouzapw/OmniRoute/issues/11824), [#11651](https://github.com/diegosouzapw/OmniRoute/issues/11651)) ([#11988](https://github.com/diegosouzapw/OmniRoute/pull/11988)) — thanks @hartmark +- **fix(dashboard):** Expose Profile loading, errors, page structure, and XP progress to assistive technologies ([#11838](https://github.com/diegosouzapw/OmniRoute/pull/11838)) — thanks @pacocartones +- **fix(socks):** `Agent.connectTimeout` now bounds both SOCKS handshake and TLS connect, and `family === null` no longer falls back to `fetch-socks` ([#11842](https://github.com/diegosouzapw/OmniRoute/pull/11842)) — thanks @maxmad64bis +- **perf(compression):** OOM mitigations for large payload hashing, memoization, and token estimation ([#11844](https://github.com/diegosouzapw/OmniRoute/pull/11844) — thanks @AndrianBalanescu) +- Excluded Next.js Node File Trace manifests (`*.nft.json`) from the published npm tarball. They are build-time metadata and are never read while serving, but had grown to 668.7 MB — 61% of the package — which pushed the upload past the registry limit and made `npm publish` fail with `413 Payload Too Large`. ([#11864](https://github.com/diegosouzapw/OmniRoute/pull/11864)) +- **fix(cli):** keep the global OmniRoute server `--base-url` separate from provider-node `--endpoint` values in add, update, and validate commands ([#11860](https://github.com/diegosouzapw/OmniRoute/pull/11860)) — thanks @pacocartones +- **fix(config):** Nous Research's `Hermes-4-405B` model now displays as "Hermes 4 405B (Nous Research)" in both the provider registry and the free-model catalog, instead of the mislabelled "Hermes 4 7B" ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#11993](https://github.com/diegosouzapw/OmniRoute/pull/11993)) +- **fix(provider/nous):** inject required user= tag into Nous Research inference requests to resolve upstream 400 "missing tags" error ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#12044](https://github.com/diegosouzapw/OmniRoute/pull/12044)) +- **fix(build):** `npm run build` now fails in one second with a named package and a copy-pasteable fix when npm silently drops an externalised optional native dependency, instead of dying four minutes in with `Module not found: Can't resolve 'better-sqlite3'` ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- **fix(install):** `postinstall` no longer throws `ReferenceError: isAndroid is not defined` — failing the whole `npm install` — when the `better-sqlite3` rebuild fallback times out; the manual-fix guidance is reachable again ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- Fixed the Alibaba free-tier allowlist test that went red on its own once the shipped catalog's `validUntil` (2026-08-27) passed, leaving every PR and `main` with a failing `Unit Tests (1/8)`. The test now builds its own packs with dates it controls, and covers the expired-pack fallback that production has actually been serving. ([#11867](https://github.com/diegosouzapw/OmniRoute/pull/11867)) +- Split the npm registry upload into its own GitHub-hosted job. npm refuses `--provenance` from a self-hosted runner (`422 ... Only "github-hosted" runners are supported`), which blocked the v3.8.50 publish; the heavy verification cannot move to a hosted runner, so it now hands the proven tarball over instead. ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(ui):** the shared `DataTable` loading state no longer reads its decorative ⏳ glyph out to assistive technology, and now carries the same `role="status"` / `aria-live="polite"` / `aria-busy="true"` semantics as `PageLoading` ([#11869](https://github.com/diegosouzapw/OmniRoute/pull/11869)) — thanks @pacocartones +- **fix(api):** Bound the `timeout` accepted by `POST /api/providers/[id]/login` to the same 15s–600s window the Adobe Firefly and Conol browser-login paths already enforce, so a request can no longer pin a headful browser and the single active-login slot with an arbitrarily large poll budget. ([#11871](https://github.com/diegosouzapw/OmniRoute/pull/11871)) — thanks @pacocartones +- **fix(api):** Stop the tunnel and MITM routes from returning raw child-process error text, which disclosed host paths, binary install locations and Tailscale `tskey-*` credentials in a body some of these routes serve to non-loopback callers, and return a real 400 instead of a framework 500 when a tunnel request body fails validation. ([#11872](https://github.com/diegosouzapw/OmniRoute/pull/11872)) — thanks @pacocartones +- **fix(open-sse):** stream TTFT/ITL are sampled from a monotonic clock (`performance.now`) instead of `Date.now`, so an NTP correction or wall-clock jump can no longer inflate a genuine TTFT or produce a negative one that the `ttft >= 0` guard silently drops — protecting the router's OTel/EWMA/`usage_history`/speed-ranking signals ([#11873](https://github.com/diegosouzapw/OmniRoute/pull/11873)) — thanks @pacocartones +- **fix(providers):** xAI `reasoning_effort: "xhigh"` now reaches grok-4.6+ instead of being silently clamped to `"high"` ([#11879](https://github.com/diegosouzapw/OmniRoute/pull/11879)) — thanks @NoxzRCW +- **fix(dashboard):** the providers page no longer crashes into the error boundary when a provider id collides with an `Object.prototype` member (`constructor`, `__proto__`); icon lookups are own-property guarded ([#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)) — thanks @NoxzRCW +- **fix(skills):** injected skill tools declared in shorthand (`{"content": "string"}`) now forward valid JSON Schema, unblocking providers that validate tool schemas strictly such as Zhipu GLM on the Console Go tier ([#11881](https://github.com/diegosouzapw/OmniRoute/pull/11881)) — thanks @NoxzRCW +- **fix(api):** `POST /api/playground/simulate-route` now surfaces `combo-ref` and `provider-wildcard` persisted combo steps with a specific warning (naming the referenced combo, or the unresolved `provider/model` wildcard) instead of folding them into a generic "unsupported step" count; a `provider-wildcard` step is also now included as an unresolved target so the operator can see it is in the route (ported from [#11882](https://github.com/diegosouzapw/OmniRoute/pull/11882) — thanks @NoxzRCW). ([#11930](https://github.com/diegosouzapw/OmniRoute/pull/11930)) +- **fix(translator):** the streaming OpenAI→Claude translator keeps upstream usage, including prompt-cache tokens, when it arrives on a trailing `choices: []` chunk (Fireworks and any upstream using `stream_options.include_usage`) ([#11883](https://github.com/diegosouzapw/OmniRoute/pull/11883)) — thanks @NoxzRCW +- **fix(cli):** `omniroute update --apply` now tells you explicitly whether a running server was detected and, if so, that you must run `omniroute restart` to apply the update — it never restarted anything and previously implied the update was already live once files were installed. The dashboard's npm-mode Update flow (`/api/system/version`) now tries OmniRoute's own PID-file-managed supervisor before falling back to pm2, and reports an honest "restart required" step instead of a silent pm2-only "skipped" that read like a completed update. The server-side latest-version lookup backing the dashboard's update banner also gained `--prefer-online`, closing the same stale-npm-cache class already fixed in the CLI's own copy for #4376 ([#11885](https://github.com/diegosouzapw/OmniRoute/issues/11885)). ([#12005](https://github.com/diegosouzapw/OmniRoute/pull/12005)) +- **fix(resilience):** clear persisted LKGP pins when a target suffers connection/provider exhaustion or is skipped before dispatch due to cooldown/exhaustion/unavailability, preventing subsequent requests from repeatedly prioritizing known-dead providers ([#11911](https://github.com/diegosouzapw/OmniRoute/issues/11911)). ([#12013](https://github.com/diegosouzapw/OmniRoute/pull/12013)) — thanks @HouMinXi +- **fix(sse):** the OpenAI→Claude stream translator now defers the terminal `message_delta`/`message_stop` emission until the real usage block has arrived (or a genuine end-of-stream flush forces it) instead of emitting it immediately on `finish_reason` — previously, when the trailing usage-only chunk (`{"choices":[],"usage":{...}}`) arrived *after* the `finish_reason` chunk (the normal order for Fireworks/vLLM/Together and other `stream_options.include_usage` upstreams), the client-visible `message_delta` still carried stale/zero usage even though `state.usage` was internally corrected too late to matter (ported from [#11915](https://github.com/diegosouzapw/OmniRoute/pull/11915) — thanks @HouMinXi). ([#11933](https://github.com/diegosouzapw/OmniRoute/pull/11933)) +- **fix(sse):** `stripStore()` now forces `store=false` for stateless OpenAI-compatible Responses-API targets (unless the connection has explicitly opted in via `providerSpecificData.openaiStoreEnabled`), instead of only handling the `openai`/`agentrouter` cases — a client-supplied `store` value previously passed through untouched to backends that don't actually persist responses server-side ([#11916](https://github.com/diegosouzapw/OmniRoute/pull/11916) — thanks @HouMinXi). +- **fix(models):** custom provider-node models (synced, custom, and alias-backed) now appear under their configured prefix in the unified catalog when the operator's model-id prefix mode is canonical, instead of being dropped whenever alias-inclusion was otherwise disabled ([#11918](https://github.com/diegosouzapw/OmniRoute/pull/11918) — thanks @HouMinXi). +- **fix(models):** the unified model catalog now suppresses stale static registry models (including effort-tier variants) for any provider whose active connection has an authoritative live synced catalog, not only providers already using exclusive-synced-listing — a connection with `providerUsesAuthoritativeLiveCatalog` previously kept serving both the live-synced models and the stale static rows side by side ([#11919](https://github.com/diegosouzapw/OmniRoute/pull/11919) — thanks @HouMinXi). +- **fix(ui):** `ProviderIcon`'s three remaining unguarded lookups (`PROVIDER_ICON_ALIASES`, `LOCAL_SVG_ALIASES`, `THEMED_SVGS`) now use `Object.hasOwn()` own-property checks — a provider id such as `constructor` or `__proto__` previously resolved through the prototype chain instead of falling through to the unknown-provider CDN fallback (`getLobeProviderIcon()` itself was already guarded by [#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)); `ProviderPageHeader`'s `color` field is also now optional, matching the rest of the component's defensive typing (ported from [#11920](https://github.com/diegosouzapw/OmniRoute/pull/11920) — thanks @HouMinXi). ([#11935](https://github.com/diegosouzapw/OmniRoute/pull/11935)) — thanks @NoxzRCW +- fix(ollama): preserve multi-byte UTF-8 content split across stream chunks in the Ollama NDJSON transform, which previously corrupted CJK/emoji into U+FFFD (#11921) — thanks @pacocartones +- **fix(providers):** OrcaRouter chat requests now target `/v1/chat/completions` instead of the bare `/v1` API root, fixing the upstream `404 Invalid URL (POST /v1)` ([#11923](https://github.com/diegosouzapw/OmniRoute/pull/11923)). — thanks @echel0nn +- **fix(plugins):** deliver the `onStreamComplete` event to disk-installed plugins. The event shipped in v3.8.50 (#9669) was emitted internally but had no plugin-facing wiring, so no plugin could ever subscribe: the manifest schema silently dropped `hooks.onStreamComplete`, and the loader/manager only knew the seven legacy hooks. `onStreamComplete` is now a declarable manifest hook, wired through the loader and registered by the manager like the other hooks, and its payload carries a `requestId` so consumers can correlate the stream-completion event with the originating request ([#11934](https://github.com/diegosouzapw/OmniRoute/pull/11934)) — thanks @amaleta / @pacocartones +- **fix(db):** the Qdrant embedding-model dropdown now lists local/self-hosted providers (Ollama, LM Studio, vLLM, etc.) — an active connection is treated as "configured" when the provider allows an optional API key, not only when it has a real key or OAuth, so a running local embedding provider is no longer hidden from the picker ([#11949](https://github.com/diegosouzapw/OmniRoute/issues/11949)) ([#11995](https://github.com/diegosouzapw/OmniRoute/pull/11995)) +- **fix(providers):** Vertex AI Anthropic partner-model discovery now calls the Model Garden `v1beta1` publisher list (`/v1beta1/publishers/anthropic/models`, global) and parses its `publisherModels` envelope, so Claude models auto-synced from Vertex populate the active live catalog and route at request time instead of returning `Model '' is not available in the active live catalog` ([#11991](https://github.com/diegosouzapw/OmniRoute/issues/11991)) — thanks @fabioluissilva ([#11998](https://github.com/diegosouzapw/OmniRoute/pull/11998)) +- **fix(cli):** support `--base-url` alongside `--endpoint` in `omniroute nodes add`, `update`, and `validate` subcommands to prevent global `--base-url` shadowing issues ([#11999](https://github.com/diegosouzapw/OmniRoute/issues/11999)). ([#12033](https://github.com/diegosouzapw/OmniRoute/pull/12033)) — thanks @HouMinXi +- **fix(providers):** `cloudflare-ai` no longer refuses image content parts for every Workers AI model ([#12002](https://github.com/diegosouzapw/OmniRoute/pull/12002)) — the plain-string `content` requirement behind #2539 is carried by the _model_ schema, not by the `/ai/v1/chat/completions` endpoint (measured: an all-text part array returns 200 on `@cf/mistralai/mistral-small-3.1-24b-instruct`, `@cf/meta/llama-4-scout-17b-16e-instruct` and `@cf/meta/llama-3.3-70b-instruct-fp8-fast`, and 400 on the text-only `@cf/qwen/qwen2.5-coder-32b-instruct`). `transformRequest()` flattened every array and threw on the first non-text part (#6390), so image input was refused for vision-capable Cloudflare models that accept it. All-text arrays are still flattened — the one shape every model accepts — while an array carrying a non-text part is passed through untouched, so the attachment is still never silently dropped. Regression guards: `tests/unit/cloudflare-ai-image-parts-6390.test.ts`. — thanks @davidlinfr +- **fix(resilience):** decouple the limiter-managed execution backstop from the queue-wait budget — new `requestQueue.executionMaxWaitMs` (env `RATE_LIMIT_EXECUTION_MAX_WAIT_MS`, default 600000 = 10 min) now feeds Bottleneck's post-dispatch `expiration`, while `requestQueue.maxWaitMs` keeps its documented queue-wait semantics. Previously the queue-wait budget doubled as the execution expiration, so legitimate long-running calls on non-incremental gateways (whole generation buffered before the first upstream byte, e.g. Console Go / Command Code tiers serving GLM models) were killed mid-flight at the queue budget with a false 504 `RATE_LIMIT_EXECUTION_TIMEOUT` — the local limiter undercut the provider-aware upstream fetch-start timeouts. The surfaced 504 message now names `requestQueue.executionMaxWaitMs`; the error keeps the #4165 guarantees (disclaims an upstream timeout, preserves the Bottleneck error as `cause`, branded code + trusted provenance, classified request-scoped so combo falls back). A real queue-wait bound (the `Promise.race` around `limiter.schedule()` sketched in #9533) remains future work. (#12025) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **Call logs:** keep the `error` field when an artifact exceeds the storage cap, instead of replacing it with the omission marker. The error is the only field that says *why* a request failed and is typically ~90 bytes next to the multi-hundred-KB bodies that trip the cap, so dropping it left a size-limited row undiagnosable — a provider outage, a local timeout and an upstream 400 all rendered identically. It is now preserved at every fallback stage, truncated to 4KB if it is itself large ([#12026](https://github.com/diegosouzapw/OmniRoute/issues/12026)). ([#12095](https://github.com/diegosouzapw/OmniRoute/pull/12095)) — thanks @ntdat812 +- **fix(diagnostics):** preserve the error field (truncated to 4KB with a `[truncated: …]` suffix) in every call-log artifact size-limit fallback stage. Previously the minimal fallback replaced the error with `[omitted: call log artifact size limit exceeded]`, so an oversized artifact row showed nothing about WHY the request failed — e.g. 91 of 847 opencode-go 504 rows on one production instance were undiagnosable from the dashboard. Oversized request/response bodies are still omitted exactly as before; the error cap is independent of the payload sizes that tripped the fallback. (#12026) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **fix(sse):** OpenAI Responses clients that declare the native `web_search` tool now receive a spec-shaped `web_search_call` output item with `action.sources` alongside the preserved function-call round-trip, so search results executed through OmniRoute's own search backend are consumable by standard Responses clients (Codex, pi-web-access, …). ([#12031](https://github.com/diegosouzapw/OmniRoute/pull/12031)) — thanks @watchingdogs +- **fix(cli):** use in-thread alias resolver hooks on modern runtimes to avoid deprecation noise and improve Node.js forward compatibility ([#12073](https://github.com/diegosouzapw/OmniRoute/issues/12073)). ([#12083](https://github.com/diegosouzapw/OmniRoute/pull/12083)) — thanks @xiaoyaner0201 +- **fix(combo):** an operator-set **Agent Features → Context length** on a combo is now honored at request time. The value was persisted and advertised through `/v1/models`, but `resolveComboContextLimit()` never consulted it — so a multi-target combo whose members carry no per-model window fell through to the provider's generic `defaultContextLength` (openrouter 128000, command-code 200000) and rejected large requests with `Input exceeds context window … limit 128000` despite the combo being explicitly sized much larger. An identical single-target combo worked, because it collapses to its concrete target before the guard runs. Invalid values (0/negative/NaN/Infinity) are ignored, so the existing target → combo-min → fallback order is unchanged. ([#12090](https://github.com/diegosouzapw/OmniRoute/pull/12090)) — thanks @adivekar-utexas +- **fix(sse):** passthrough streams now estimate usage on finish when upstream closes without usage even with `stream_options.include_usage` — avoids `0 tokens / 0%` for providers that stay silent (and correctly handles trailing empty-choices usage) ([#12151](https://github.com/diegosouzapw/OmniRoute/pull/12151)) — thanks @maxmad64bis +- **fix(combos):** clearing an agent feature in the combos editor now persists — unchecking context cache protection, or emptying the system message or tool filter, sends an explicit `null` instead of dropping the field from the `PUT` body, which the update merge read as "leave unchanged" ([#12177](https://github.com/diegosouzapw/OmniRoute/pull/12177)) — thanks @foreveryh / @ntdatt812 +- **fix(memory):** self-hosted embedding endpoints now vectorize — the vector width is measured from the first embedding that comes back instead of being read from a registry that cannot describe them, so `vec_memories` is created and memories stop piling up unvectorized behind a green health check ([#12180](https://github.com/diegosouzapw/OmniRoute/pull/12180)) — thanks @kanade-hoshino / @ntdatt812 +- **fix(translator):** the leading `system` message now reaches Responses-API upstreams when its `content` is a content-part array — it was read as `typeof content === "string" ? content : ""`, so a prompt-caching client (Anthropic `cache_control`, the shape LiteLLM and the Anthropic SDK emit) had its entire system prompt replaced by an empty `instructions`. The request was still accepted with a normal `prompt_tokens` count, so the model answered with no instructions and nothing in the response said they were missing. Mid-conversation system turns already handled the array shape ([#7056](https://github.com/diegosouzapw/OmniRoute/pull/7056)); only the first one did not ([#12206](https://github.com/diegosouzapw/OmniRoute/issues/12206)). Regression guard: `tests/unit/translator-openai-responses-system-content-parts.test.ts`. ([#12207](https://github.com/diegosouzapw/OmniRoute/pull/12207)) — thanks @Gorillaz322 +- **fix(free-tier):** `/api/free-tier/summary` no longer computes its totals from a Radar feed built before the catalog the running release ships. When the cached feed is older — or carries no build date at all — the route answers from the shipped catalog, resolved through the operator's local model state so disabled and tombstoned models stay out of the numbers ([#12215](https://github.com/diegosouzapw/OmniRoute/pull/12215)). — thanks @maxmad64bis +- **fix(oauth):** Keep a Claude personal workspace and a Team organization as separate connections — they share the same email and `accountUUID`, so the email-only OAuth dedup let the second login overwrite the first account's tokens; `organizationUUID` now disambiguates them, the way `workspaceId` does for Codex ([#12222](https://github.com/diegosouzapw/OmniRoute/pull/12222)) — thanks @davidebaraldo +- **fix(resilience):** a 402 on a single paid model of a passthrough/gateway provider (e.g. `kilo-gateway`, `ollama-cloud`) no longer terminalizes the whole connection with a never-auto-recovered `credits_exhausted` status — only the paid model is locked out, so free models on the same key keep serving. 402 variant of [#3027](https://github.com/diegosouzapw/OmniRoute/issues/3027). Single-credential providers are unaffected — a 402 there is still treated as the key being genuinely out of credit ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239) / [#10616](https://github.com/diegosouzapw/OmniRoute/issues/10616)) ([#12242](https://github.com/diegosouzapw/OmniRoute/issues/12242)) — thanks @brick30llc-ctrl ([#12266](https://github.com/diegosouzapw/OmniRoute/pull/12266)) — thanks @geekyNads +- **fix(sse):** trust `finish_reason: "length"`/`"max_tokens"` over the reasoning-consumed-token ratio in response quality validation, so a reasoning model truncated below the old 90% threshold correctly fails and retries instead of returning empty content as a silent "success" ([#12262](https://github.com/diegosouzapw/OmniRoute/pull/12262)) — thanks @brick30llc-ctrl +- **fix(settings):** `PUT /api/settings/cache-config` now persists `alwaysPreserveClientCache` to the flat general settings the runtime cache-control policy actually reads; previously the value landed in the databaseSettings "cache" section and was silently ignored, so the endpoint had no effect on `cache_control` passthrough ([#12304](https://github.com/diegosouzapw/OmniRoute/pull/12304)) — thanks @davidebaraldo +- **fix(grok-cli):** treat omitted SuperGrokPro `creditUsagePercent` as 0% used so Provider Limits still renders a weekly bar (proto3 zero-elision) ([#12312](https://github.com/diegosouzapw/OmniRoute/pull/12312)) — thanks @HouMinXi +- **fix(combo):** Expose the two Auto-Combo scoring factors nobody could set — the combo validation schema and the dashboard weight sliders both declared 13 of the scorer's 15 factors, so `connectionDensity` (spreads load across a provider's connections) and `quality` were dropped on save and offered nowhere. The sliders also shipped their own default table that differed from the engine's on every non-zero factor and summed to 1.05, so the percentages shown next to them added up to 105%. Both lists now match `DEFAULT_WEIGHTS`, and a test keeps them there. Note that a combo whose stored `weights` omitted the two keys was effectively running with them at zero and the other thirteen renormalized upward; it now runs with the engine's intended distribution, so its routing does shift ([#12314](https://github.com/diegosouzapw/OmniRoute/pull/12314)) — thanks @maxmad64bis +- **fix(docs):** The free-tier reference no longer says its numbers come "confidence tagged per row" — no catalog entry carries a confidence tag and the API serves none, so every figure on that page is an estimate of the same, unstated quality. The page now states what an entry does vouch for: an independently documented hard stop (set by hand with the source in a comment, never defaulted to `true`) and a prompt-training disclosure, both with live counts the `check:docs-counts` gate keeps honest ([#12318](https://github.com/diegosouzapw/OmniRoute/pull/12318)) — thanks @maxmad64bis +- **fix(usage):** `adobe-firefly` and `firefly` have had a working usage fetcher since Adobe Firefly landed, but neither was ever added to the registration list, so the provider-plugin manifest, `genericQuotaFetcher` and the free-access quota cache all reported them as having no usage support — while `USAGE_SUPPORTED_PROVIDERS` said the opposite. Both are now declared, which also means their credit balance is fetched like any other declared provider's: `registerGenericQuotaFetchers` now registers a generic quota fetcher for them, and `resolveFreeAccessState` no longer returns early. A test holds the registration list to the dispatcher's switch in both directions, which is what the module's own docstring already asked for in prose ([#12321](https://github.com/diegosouzapw/OmniRoute/pull/12321)) — thanks @maxmad64bis +- **fix(quota):** drop the generic quota cache (agy / Antigravity / Claude OAuth) on an upstream 429 so reset-aware scoring does not keep a 60s stale snapshot, and force-refresh the next usage fetch so inner provider caches cannot recache the same window ([#12325](https://github.com/diegosouzapw/OmniRoute/pull/12325)) — thanks @HouMinXi +- **fix(combos):** deleting a combo now clears its persisted LKGP pins instead of leaving unreachable `key_value` rows behind ([#12326](https://github.com/diegosouzapw/OmniRoute/issues/12326)) ([#12425](https://github.com/diegosouzapw/OmniRoute/pull/12425)) — thanks @KrzysiekSko +- **fix(sse):** Keep ZWNJ (U+200C) and ZWJ (U+200D) in assistant text, reasoning and tool-call arguments — Persian/Kurdish half-space (`ارائه‌دهنده`), Arabic/Indic shaping and emoji sequences no longer lose them; the response de-obfuscation now removes joiners only between ASCII word characters, where the request side inserts them ([#12186](https://github.com/diegosouzapw/OmniRoute/issues/12186)) — thanks @rezjalibd ([#12359](https://github.com/diegosouzapw/OmniRoute/pull/12359)) — thanks @pacocartones +- **fix(resilience):** count resolved upstream 5xx results against the provider circuit breaker on the chat path — `CircuitBreaker.execute()` no longer reads a resolved `{ success: false, status: 5xx }` as a success that cancels the call-site failure, so a provider answering 503s now trips its breaker instead of staying `CLOSED` at `failureCount: 1`; single-model and combo dispatches are each accounted exactly once ([#12254](https://github.com/diegosouzapw/OmniRoute/issues/12254)) ([#12360](https://github.com/diegosouzapw/OmniRoute/pull/12360)) — thanks @pacocartones +- **fix(providers):** resolve the Codex quota auto-ping model from the live provider catalog and lifecycle registry instead of the retired `gpt-5.1-codex-mini`, and pause the ping with one actionable warning when no selectable Codex model exists rather than retrying a shut-down id every cooldown window ([#11905](https://github.com/diegosouzapw/OmniRoute/issues/11905)) ([#12361](https://github.com/diegosouzapw/OmniRoute/pull/12361)) — thanks @pacocartones +- **fix(api):** keep the `{created, data}` wrapper on combo-routed `/v1/images/generations` responses and default Codex image results to `b64_json` on both `/v1/images/generations` and `/v1/images/edits` so Codex CLI's built-in `image_gen` can decode them ([#12268](https://github.com/diegosouzapw/OmniRoute/issues/12268)) ([#12362](https://github.com/diegosouzapw/OmniRoute/pull/12362)) — thanks @pacocartones +- **fix(sse):** Name the shadowed custom provider node when a built-in provider id/alias (e.g. `openference` → `of`) reserves the prefix of an existing OpenAI/Anthropic-compatible node, so the runtime `No active credentials for provider: ` error explains that the prefix routed to the built-in and never reached the node's healthy connections, instead of contradicting the dashboard ([#11943](https://github.com/diegosouzapw/OmniRoute/issues/11943)) — thanks @morpheus9393 ([#12365](https://github.com/diegosouzapw/OmniRoute/pull/12365)) — thanks @pacocartones +- **fix(i18n):** wrap `ccOnboardingKeyPlaceholder` in ICU single quotes across all 43 locales so angle brackets render literally instead of being parsed as rich-text tags, which crashed the Claude Code onboarding block with `INVALID_MESSAGE: INVALID_TAG` ([#12302](https://github.com/diegosouzapw/OmniRoute/issues/12302)) ([#12369](https://github.com/diegosouzapw/OmniRoute/pull/12369)) — thanks @gonisulaimann +- **fix(models):** publish `effort_tiers` on Kimi K3's synced base-model entries (`kmca/k3`, `kmca/k3-256k`) so catalog-only clients (OpenCode, plain SDK pickers) can see and select the reasoning tiers (`low`/`high`/`max`) the synced metadata already carried — the `isSkippedEffortProvider` gate no longer suppresses tier visibility on those base entries, while synthetic `-` variant generation stays prevented and Codex/GLM base models remain excluded unchanged ([#12299](https://github.com/diegosouzapw/OmniRoute/issues/12299)) ([#12371](https://github.com/diegosouzapw/OmniRoute/pull/12371)) — thanks @gonisulaimann +- **fix(guardrails):** keep `auto`/`auto/*` virtual combos exempt from the Vision Bridge `fixedModel` credential guard so a combo target is passed through instead of silently falling back to global auto-selection ([#12237](https://github.com/diegosouzapw/OmniRoute/issues/12237)) ([#12373](https://github.com/diegosouzapw/OmniRoute/pull/12373)) — thanks @pacocartones +- **fix(combo):** capability-filter exhaustion caused by `max_tokens` above every target's known output limit now reports that reason (requested `max_tokens` vs the pool's highest known ceiling) instead of the unrelated "supports structured output" message ([#12229](https://github.com/diegosouzapw/OmniRoute/issues/12229)) — thanks @DW-MediaLab ([#12374](https://github.com/diegosouzapw/OmniRoute/pull/12374)) — thanks @pacocartones +- **fix(auth):** the `least-used` account strategy now prefers accounts without backoff before falling back to oldest `lastUsedAt`, the same tie-break `round-robin` already applies, so a failover no longer lands on a just-rate-limited account for a single request ([#12279](https://github.com/diegosouzapw/OmniRoute/issues/12279)) — thanks @tenshiak ([#12375](https://github.com/diegosouzapw/OmniRoute/pull/12375)) — thanks @pacocartones +- **fix(docker):** the `chatgpt-web-codex-browser` image now finds the Chrome binary under `chrome-linux64/` (Chrome for Testing layout in `playwright:v1.62.0-noble`) as well as the legacy `chrome-linux/`, so the container no longer crash-loops with `exec: --headless=new: not found` ([#12024](https://github.com/diegosouzapw/OmniRoute/issues/12024)) ([#12376](https://github.com/diegosouzapw/OmniRoute/pull/12376)) — thanks @pacocartones +- **fix(providers):** declare `groq/compound` and `allam-2-7b` as non-reasoning models in the curated Groq registry so `reasoning_effort` / `output_config.effort` / `thinking` from Claude Code are stripped instead of forwarded, which Groq rejected with HTTP 400 ([#12134](https://github.com/diegosouzapw/OmniRoute/issues/12134)) ([#12379](https://github.com/diegosouzapw/OmniRoute/pull/12379)) — thanks @pacocartones +- **fix(executors):** `OpencodeExecutor` no longer forces a direct connection when the connection has a proxy assigned in Proxy Management but no per-account proxies: the single-account fast path used to wrap the upstream dispatch in the direct-egress sentinel, discarding the ambient proxy context the chat handler had pinned from `proxy_assignments`, so API-key `opencode`/`opencode-go` connections egressed from the host IP (and hit geoblocks) despite the assignment. The direct pin is now applied only when no ambient proxy context exists ([#11894](https://github.com/diegosouzapw/OmniRoute/issues/11894) — thanks @hizzt) ([#12380](https://github.com/diegosouzapw/OmniRoute/pull/12380)) — thanks @pacocartones +- **fix(api):** `GET /v1/models` with `MODELS_CATALOG_PREFIX_MODE=canonical` (or `?prefix=canonical`) now lists providers whose registry alias is undefined or equal to their own id (Antigravity, Antigravity CLI and other self-aliased built-ins) — their single `provider/model` id was dropped by the alias/canonical duplicate guard in the static, synced, custom and alias-backed catalog loops ([#12058](https://github.com/diegosouzapw/OmniRoute/issues/12058)) — thanks @cheynetom ([#12381](https://github.com/diegosouzapw/OmniRoute/pull/12381)) — thanks @pacocartones +- **fix(translator):** Drop replayed `thinking` blocks that carry no signature (the shape produced from cross-provider `reasoning_content`) instead of stamping the default Claude signature on them, which Anthropic rejected with `400 Invalid signature in thinking block` on the next turn served by an Anthropic rung ([#12105](https://github.com/diegosouzapw/OmniRoute/issues/12105)) — thanks @atescivitci-cmd ([#12386](https://github.com/diegosouzapw/OmniRoute/pull/12386)) — thanks @pacocartones +- **fix(cli):** Resolve Bun's `--preload` polyfill path against the package root instead of `dist/`, so `omniroute` installed with `bun install -g` no longer crashes at startup with `error: preload not found …/dist/open-sse/utils/setupPolyfill.ts` ([#11980](https://github.com/diegosouzapw/OmniRoute/issues/11980)) — thanks @joglomedia ([#12387](https://github.com/diegosouzapw/OmniRoute/pull/12387)) — thanks @pacocartones +- **fix(providers):** `gemini-business` now publishes its model catalog — `/v1/models` and `/v1/providers/gemini-business/models` list the 12 enterprise Gemini ids the executor understands instead of returning an empty list (#12107) ([#12389](https://github.com/diegosouzapw/OmniRoute/pull/12389)) — thanks @pacocartones +- **fix(db):** install `busy_timeout` before the SQLite connection's first statement so a process opening the database while another one closes its WAL connection waits out the transient EXCLUSIVE lock instead of dying with `database is locked`, and recognise the drivers' real BUSY/PROTOCOL/IOERR errors as transient in the corruption probe so the same lock no longer renames the database away as corrupt; deflakes `cross-process contenders never both acquire the same connection` (#12394 — thanks @pacocartones) +- **fix(chat-admission):** derive the `chat_admission_busy` 503 `Retry-After` from observed heavyweight-lease occupancy — the larger of the exhausted `OMNIROUTE_CHAT_ADMISSION_QUEUE_MS` window and the time since capacity last turned over, capped at 60 s — instead of a fixed 1 s (structural) / 2 s (byte-stage) hint that invited Codex/agent fan-out clients to re-send ~1 MiB `/v1/responses` bodies every second into a gate held for the whole SSE lifetime; an idle gate keeps the historical floors ([#12135](https://github.com/diegosouzapw/OmniRoute/issues/12135)) (#12395 — thanks @pacocartones) +- **fix(api-manager):** the API key permissions modal no longer silently drops `allowedCombos` entries its Combo picker cannot render — routing-rule names such as `rt-*`, which the backend already honours — when "All" is clicked and the key is switched back to "Restrict"; those entries now survive the toggle, are listed read-only under the combo list so the count and the list agree, and are saved back verbatim instead of persisting `[]` (deny-all) (#12397 — thanks @pacocartones) +- **fix(catalog):** write the NUL separator of the catalog connection memo key, the provider serviceKind memo key, the Video Bridge promotion group key and a JSON-exactness test fixture as the `\u0000` escape instead of a raw byte — same runtime value, but the raw byte made git, GitHub and ripgrep treat those files as binary (hidden PR diffs, silently skipped searches); a guard test now keeps raw NUL bytes out of `src/`, `open-sse/` and `tests/` (#12403 — thanks @pacocartones) +- **fix(providers):** add `CLAUDE_CODE_CLIENT_VERSION` and `GITHUB_COPILOT_CLI_VERSION` env overrides so Anthropic/Copilot client-version gates can be unblocked without a rebuild ([#12417](https://github.com/diegosouzapw/OmniRoute/issues/12417)) ([#12632](https://github.com/diegosouzapw/OmniRoute/pull/12632)) — thanks @HouMinXi +- **fix(db):** back-fill `last_ping_at` and `last_pinged_reset_key` on `provider_connections` during schema reconciliation so divergent lineages that skipped `123_quota_auto_ping` still accept quota auto-ping writes ([#12470](https://github.com/diegosouzapw/OmniRoute/pull/12470) — thanks @KooshaPari) +- **fix(ci):** document MIT exceptions for `@eloqnt/{config,format-json,format-po}` (next-intl transitive; locked tarballs omit `license`) and keep the A2A lifecycle vitest off the real SQLite persistence seam ([#12581](https://github.com/diegosouzapw/OmniRoute/issues/12581)) ([#12595](https://github.com/diegosouzapw/OmniRoute/pull/12595)) +- OpenCode plugin `/v1/models` catalog fetch now waits 30s by default and attaches HTTP `statusCode` on 401/5xx so host fallback plugins can hop instead of seeing an untyped AbortError/UnknownError. ([#12607](https://github.com/diegosouzapw/OmniRoute/pull/12607)) — thanks @RaviTharuma +- **CI:** the OpenAPI security-tier gate now mirrors `isAlwaysProtectedPath()` in full — it also reads `ALWAYS_PROTECTED_API_PATTERNS`, so the pattern-gated credential routes (`/api/providers/{id}/{claude,codex}-auth/{export,apply-local}`, GHSA-5926-2w35-7h4q) no longer report as unannotated. (#12605) +- **fix(api):** GET /v1/models no longer waits forever on a hung coalesced catalog rebuild; cold-path waits are bounded (`CATALOG_BUILD_TIMEOUT_MS`, default 8s) and a last-good 200 is served when the rebuild times out ([#12627](https://github.com/diegosouzapw/OmniRoute/issues/12627)). ([#12628](https://github.com/diegosouzapw/OmniRoute/pull/12628)) — thanks @RaviTharuma +- **fix(cursor):** a non-array `tool_calls` on an assistant message no longer crashes the cursor request translator with a `TypeError`; both loops now require an array ([#12689](https://github.com/diegosouzapw/OmniRoute/issues/12689)) ([#12691](https://github.com/diegosouzapw/OmniRoute/pull/12691)) — thanks @soroush5 +- **fix(chat):** Chat Completions no longer return empty `content` after a server-owned memory or skills tool; the first provider send and account/model recovery now share one pipeline so a follow-up round-trip can fill the reply ([#12696](https://github.com/diegosouzapw/OmniRoute/issues/12696)) — thanks @HouMinXi ([#12867](https://github.com/diegosouzapw/OmniRoute/pull/12867)) +- **fix(dashboard):** batch-deleting provider keys no longer toasts failure after a successful delete when the confirm button's click event is forwarded as `onAfter` ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) — thanks @HouMinXi +- **fix(glm):** drop the extra 16th argument to `createSSETransformStreamWithLogger` that TypeScript rejected (TS2554) and that never reached the TransformStream ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) ([#12770](https://github.com/diegosouzapw/OmniRoute/pull/12770)) — thanks @HouMinXi +- **fix(dashboard):** pass `nodeMap` into Runtime `QuotaGroup` so a quota monitor in error/exhausted/alerting no longer throws `ReferenceError: nodeMap is not defined`. ([#12868](https://github.com/diegosouzapw/OmniRoute/pull/12868)) — thanks @HouMinXi +- Restricted API keys whose `allowedModels` lists a combo name no longer skip every combo member at pre-dispatch (`ALL_TARGETS_SKIPPED`). Inner-target filtering still applies when the allow-list is a provider prefix or `disableNonPublicModels` is on ([#12899](https://github.com/diegosouzapw/OmniRoute/pull/12899)). — thanks @HouMinXi +- **fix(combos):** Effort-suffixed combo members inherit the base model's `model_context_overrides` row so priority order is not inverted on large requests ([#12926](https://github.com/diegosouzapw/OmniRoute/pull/12926)) — thanks @HouMinXi +- **fix(catalog):** Picker-added `customModels` enter the dispatch-time live catalog so combo members and bare inference no longer 400 ([#12934](https://github.com/diegosouzapw/OmniRoute/pull/12934)) — thanks @HouMinXi +- **fix(docker):** re-derive the Docker build's worker-pool memory budget from the MEASURED ~4.5 GB per-process RSS (the issue owner's own VPS dmesg OOM-killer reproduction) instead of the stale 2560 MB/worker inference #11419 shipped, and lower `OMNIROUTE_BUILD_WORKERS` 3 → 2 so 1 parent + 1 page-data worker (2 processes × 4.5 GB = 9 GB) fits the 12.288 GB (75%) budget on a 16 GB GitHub Actions runner — the previous default (1 parent + 2 workers = 13.5 GB) still overcommitted the runner and kept "Publish to Docker Hub" failing with `cannot allocate memory` after #11419 merged (#7518). ([#11663](https://github.com/diegosouzapw/OmniRoute/pull/11663)) +- **fix(electron):** the packaged Windows desktop build now passes the #7592 cold-restart smoke end to end. Five defects were found and fixed on the way: (1) optional-pack staging failed on any machine whose `tar` is GNU tar — it reads the drive letter in an absolute `-f C:\...` archive path as a remote rsh host (`Cannot connect to C:`), so staging now passes a bare filename with `cwd` at the tarball directory and surfaces tar stderr on failure; (2) the Electron `files` allowlist was missing `lib/loginHeaderCapture.js`, so the packaged main process crashed at startup with `Cannot find module './lib/loginHeaderCapture'`; (3) electron-builder ≥26 injects an `!**/node_modules/**` ignore into every extraResources pattern list that no positive filter can override, which silently dropped the staged runtime `node_modules` (including the better-sqlite3 N-API prebuild) from `resources/app` — a fresh v3.8.50 desktop build would have shipped with no native SQLite driver and reproduced the #7592 sql.js fallback on every machine; an `afterPack` hook now restores the staged `node_modules` after packing; (4) the packaged-app smoke harness redirected `USERPROFILE` into its temp DATA_DIR, but Electron resolves the Roaming profile from `%USERPROFILE%\AppData\Roaming\` (USERPROFILE takes precedence over the APPDATA env var) and the path service throws instead of creating it, so `requestSingleInstanceLock()` returned false and the app exited(0) silently before `app.whenReady()` — the harness now pre-creates the derived tree, and `ensureSmokeEnvDirs` is exported and unit-tested; (5) the #7592 driver assertion parsed a `[DB] Driver: ...` line that the server's primary DB path never printed (only the unused `openDatabaseAsync()` did) — `getDbInstance()` now emits the same line on open so the guard can actually assert the native driver. Also: the smoke env-allowlist unit test hardcoded POSIX paths and could only pass on Linux/macOS; expectations are now host-agnostic, with new regression tests for the USERPROFILE-derived tree and `tarPack` under absolute Windows-style paths ([#11443](https://github.com/diegosouzapw/OmniRoute/pull/11443)) — thanks @jonlwheat2-gif +- fix(sse): stop the auto-combo candidates inspector from silently dropping model-locked/cooled-down rows (#9133) ([#11994](https://github.com/diegosouzapw/OmniRoute/pull/11994)) — thanks @hartmark +- **fix(providers):** Perplexity Web no longer turns upstream stream failures into successful assistant text; pre-content failures remain eligible for fallback, partial output ends with a structured sanitized error, and failed sessions are not persisted ([#12465](https://github.com/diegosouzapw/OmniRoute/pull/12465)) +- **fix(video-bridge):** preserve chronological video and transcript fusion, source-frame timestamps, transcript provenance, full contact-sheet intervals, and pre-abort callback isolation ([#11681](https://github.com/diegosouzapw/OmniRoute/pull/11681)) +- **Z.ai Web:** HTTP 200 streams carrying an upstream error now terminate with a structured failure instead of assistant text plus a normal stop, preserving partial output while allowing pre-content combo fallback. ([#12454](https://github.com/diegosouzapw/OmniRoute/pull/12454)) +- Absorb `Error [AbortError]: request_signal_aborted` and DOMException AbortError shapes in the process-level client-abort crash guard so routine client disconnects no longer kill the server (exit code 7). ([#12165](https://github.com/diegosouzapw/OmniRoute/pull/12165)) — thanks @alvinveroy +- **fix(sse):** Treat Adapta Web `type:error` SSE events as sanitized non-stream failures instead of empty HTTP 200 completions. ([#12459](https://github.com/diegosouzapw/OmniRoute/pull/12459)) +- **fix(autoCombo):** layer 3 (`models_dev_tier`) no longer scores retired models as premium, merge cross-provider capability rows deterministically, and inherit tier scores through variant ids ([#11508](https://github.com/diegosouzapw/OmniRoute/issues/11508)). `getModelsDevTierFitness` now vetoes ids present in `config/quality/model-lifecycle.json` with status `retired` before any other signal; `loadModelCapabilities()` aggregates per `model_id` (any-non-null-true for booleans, max for `limit_context`) instead of last-write-wins over SQLite's undefined row order; a capabilities miss retries through `resolveScoresAs()`'s base id and reports `models_dev_tier:inherited`, matching layers 1–2 (#11492). Adds `getModelsDevTierFitnessWithSource()` and an `invalidateCapabilitiesCache()` test/ops hook. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) — thanks @Meet6338-X +- fix(sse): accept Claude `content:[]` + `stop_reason: max_tokens`/`tool_use` in `detectMalformedNonStream` (match `isEmptyContentResponse`) so Claude Code `/model` probes with `max_tokens: 1` no longer become a false 502 ([#11568](https://github.com/diegosouzapw/OmniRoute/pull/11568)) — thanks @Seramicx +- **fix(security):** sanitize `request.failed` diagnostics before publishing them to live dashboard listeners and replay history, while keeping status, model, provider, latency, and internal call-log diagnostics intact. ([#12469](https://github.com/diegosouzapw/OmniRoute/pull/12469)) +- **fix(pwa):** do not intercept dashboard navigations so Chrome can fall back from a stale HTTP/3 Alt-Svc advertisement (UDP :20128 is unpublished; F5 on a long-lived tab hung until a new tab opened a fresh TCP connection). ([#12767](https://github.com/diegosouzapw/OmniRoute/pull/12767)) — thanks @HouMinXi +- **fix(executors):** handle DuckDuckGo ERR_BN_LIMIT (418) without retrying — when the upstream returns `418 ERR_BN_LIMIT` (rate-limit/ban), the executor now returns the error immediately instead of burning another VQD acquisition that would only count against the IP limit. The retry logic for `418 ERR_CHALLENGE` (unsolved challenge) remains unchanged. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) ([#12110](https://github.com/diegosouzapw/OmniRoute/pull/12110)) — thanks @oyi77 / @Meet6338-X +- Fixed DuckDuckGo streaming responses losing JSON lines and UTF-8 characters split across network chunks. ([#11528](https://github.com/diegosouzapw/OmniRoute/pull/11528)) — thanks @pacocartones +- Harden generated Cloudflare, Vercel, and Deno relays by stripping hop-by-hop, framing, and proxy authentication headers before forwarding requests upstream. ([#11533](https://github.com/diegosouzapw/OmniRoute/pull/11533)) — thanks @pacocartones +- **feat(grok-cli):** Provider Limits shows grok-cli banked reset credits from `GetRemainingResets` (including a real zero; a failed RPC omits the row) and the existing View credits button now calls `ConsumerUiSvc/RedeemReset` for grok-cli. Live tokens use nested fields 10/20/30 (id + Timestamp), not compact 1/2/3. ([#12805](https://github.com/diegosouzapw/OmniRoute/pull/12805)) — thanks @HouMinXi +- **fix(memory):** Embedding Model Quick select, Embedding Source remote dropdown, and Rerank selector now list every configured provider with embedding/rerank support instead of only chat-catalog text matches plus OpenRouter live discovery; a generic OpenAI-compatible `/embeddings` + Cohere-compatible `/rerank` runtime fallback resolves any configured chat provider's embedding/rerank endpoint, so unlisted providers no longer fail with "Unknown embedding provider"; both memory selectors gained a free-text model override ([#11390](https://github.com/diegosouzapw/OmniRoute/pull/11390)) — thanks @rqzbeh +- Harden SQLite upgrades around the historical migration-074 version collision: missing discovery and inspector tables are replayed atomically, pre-existing databases (including setup-created skeletons) receive reusable content-addressed safety snapshots, and Node test/eval probes without `DATA_DIR` are isolated from the operator database. ([#12435](https://github.com/diegosouzapw/OmniRoute/pull/12435)) +- **fix(dashboard):** Moonshot/Kimi Open Platform voucher and cash leftover percentages follow the bucket balance, so an empty wallet no longer paints those rows as 100% while Available is 0% ([#12733](https://github.com/diegosouzapw/OmniRoute/pull/12733)) — thanks @HouMinXi +- **fix(combo):** return non-retryable HTTP 400 when all candidates for a pinned native Codex turn are unavailable due to model-scoped lockout, terminating the turn cleanly while preserving turn continuity and enabling standard Combo routing on subsequent turns ([#12240](https://github.com/diegosouzapw/OmniRoute/pull/12240)) — thanks @mdigitalbh81 +- Fixed auto-router strategies preserving the selected connection through ranking and dispatch when multiple connections share the same provider and model. ([#11530](https://github.com/diegosouzapw/OmniRoute/pull/11530)) — thanks @pacocartones +- Preserve Chat Completions JSON-object response formats when translating requests to the Responses API. ([#11529](https://github.com/diegosouzapw/OmniRoute/pull/11529)) — thanks @pacocartones +- Fix idempotency fingerprints so Responses API requests include `input` and other semantic generation fields using stable serialization, while excluding credentials and request metadata. ([#11532](https://github.com/diegosouzapw/OmniRoute/pull/11532)) — thanks @pacocartones +- **fix(api):** Generated API CLI commands now enforce required OpenAPI request bodies; Combo test commands forward the required `comboName` body, while API keys created by older writers after migration 149 preserve legacy allow-all Combo access without widening explicit empty allowlists — thanks @marcelokarval ([#12070](https://github.com/diegosouzapw/OmniRoute/pull/12070)) +- **fix(grok-web):** treat upstream streaming failures as failures instead of successful assistant text: error-only streams now fail readiness with HTTP 502, while failures after legitimate content preserve that partial output and terminate through the sanitized stream failure path without a normal `stop` completion. ([#12458](https://github.com/diegosouzapw/OmniRoute/pull/12458)) +- HuggingChat now turns HTTP 200 JSONL generation failures into a sanitized 502 before content, or a fixed public stream failure after partial output, so fallback and request persistence no longer record a false successful stop. ([#12456](https://github.com/diegosouzapw/OmniRoute/pull/12456)) +- **fix(providers):** keep 1min.ai HTTP 200 stream errors out of assistant content, preserve partial output, and expose sanitized terminal errors so pre-content failures can fall back. ([#12466](https://github.com/diegosouzapw/OmniRoute/pull/12466)) +- **fix(sessions):** preserve legacy Sessions data when the additive exclusive-session projection is unavailable, returning an empty projection and warning only once per contiguous outage instead of failing the endpoint; the Sessions badge intentionally reflects the merged legacy and exclusive row count introduced by [#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389) ([#11469](https://github.com/diegosouzapw/OmniRoute/pull/11469)) — thanks @KaspaPulse +- Render Nimble Search with the generic provider icon and serve Opper's proven logo locally. ([#12415](https://github.com/diegosouzapw/OmniRoute/pull/12415)) +- **fix(quality):** `config/quality/provider-assets-provenance.jsonl` was stale against the physical `public/providers/` tree (still listing 225 assets and 5 duplicate-content groups after several provider-retirement PRs removed 85 icon files), which would have made `check:provider-asset-provenance` fail on the next full run. Reconciled the manifest to the real 142-file snapshot (142 asset records, 1 duplicate-content group, 2 newly-registered files marked `unresolved` pending provenance research), repinned `auditedCommit`, and updated the regression test's hardcoded counts to match. ([#11876](https://github.com/diegosouzapw/OmniRoute/pull/11876)) +- fix(ui): wrap quota card footer action buttons to prevent clipping Refresh button on narrow cards — when extra action buttons such as `View credits` are present on Codex cards, allow the footer row to flex-wrap with `shrink-0` buttons so `Refresh now` remains visible and clickable across all responsive card widths ([#11464](https://github.com/diegosouzapw/OmniRoute/pull/11464)) — thanks @adevwithpurpose +- Keep Antigravity Gemini usable when the same connection's Claude weekly quota is empty; generic quota cache stays per-connection for every other provider. ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) — thanks @HouMinXi +- Fixed Tailscale tunnel setup to reject ports outside the valid 1-65535 range and safely install Windows MSI packages from paths containing apostrophes. ([#11534](https://github.com/diegosouzapw/OmniRoute/pull/11534)) — thanks @pacocartones +- Classify Cloudflared and Tailscale process-management routes as local-only and spawn-capable while preserving remote access to read-only tunnel status endpoints. ([#11531](https://github.com/diegosouzapw/OmniRoute/pull/11531)) — thanks @pacocartones +- Electron release workflow: the `publish-npm` job now grants `actions: read` to the reusable `npm-publish.yml` it calls (its `publish` job requests it), which is what made GitHub refuse the whole v3.8.50 run at startup and ship the release with zero desktop assets; a `workflow_dispatch` now builds the requested tag instead of the dispatching branch and can skip the npm leg (`publish_npm=false`) when only re-attaching assets ([#11974](https://github.com/diegosouzapw/OmniRoute/pull/11974)) +- **fix(dashboard):** The Combos page usage guide now reads its dismissal through `useSyncExternalStore` instead of correcting SSR state inside an effect, removing an extra commit of the page tree on every load (and the `react-hooks/set-state-in-effect` error it raised). ([#12671](https://github.com/diegosouzapw/OmniRoute/pull/12671)) +- Electron release: `electron/package-lock.json` regained the optional `electron-builder-squirrel-windows` subtree (13 entries) that `npm ci` had been refusing as out of sync, `electron-release.yml` gained a `build_ref` dispatch input and stops regenerating release notes on a re-attach dispatch, and the npm publish workflow attaches the SBOM to the GitHub Release on dispatch publishes too — so the v3.8.51 tag ships every desktop asset and the SBOM like v3.8.49 did ([#12022](https://github.com/diegosouzapw/OmniRoute/pull/12022)) +- Drained the reds every PR against `release/v3.8.51` was born with: documented the three service-worker build-id variables, registered the six retirement/tunnel tests with the mutation gate, approved `eslint-plugin-react-hooks` in the dependency allowlist, added the six `combo.sort.*` strings to `vi` and `pt-BR`, pointed the ChatGPT Web doc at the real migration-168 test, worded the g4f hint around the member key, and realigned four tests to the retired-provider catalog and the legacy-schema fixtures the retirement migrations touch. ([#11940](https://github.com/diegosouzapw/OmniRoute/pull/11940)) +- **fix(providers):** Claude, Grok, LMArena, Notion, and Perplexity web-cookie transports now use pooled `wreq-js` 3.2 instead of the native sidecar, with all nine supported bindings pinned and audited, and the applicable platform binding plus native-license evidence included in each release artifact ([#12429](https://github.com/diegosouzapw/OmniRoute/pull/12429), supersedes [#11753](https://github.com/diegosouzapw/OmniRoute/pull/11753)). +- **fix(providers):** Zed Hosted streaming failures now trigger fallback before content and end partial streams with a sanitized structured error instead of fake assistant text and a normal-success stop. ([#12455](https://github.com/diegosouzapw/OmniRoute/pull/12455)) +- **fix(test):** capture hook result after render (direct commit `b85d0abb0b`) +- **fix(sync):** repair the two regressions the v3.8.50 sync-back left on release/v3.8.51 (direct commit `5ade9e0851`) +- **revert(models):** drop the memo reset hook ported from #10055 (direct commit `282de841ae`) +- **fix(typecheck):** guard the optional connectionId before the reactive model sync (direct commit `f2825bad75`) +- **fix(quota-share):** release the winner's reserved in-flight slot (#11371) ([#11408](https://github.com/diegosouzapw/OmniRoute/pull/11408)) — thanks @oyi77 +- **fix(providers):** stop silently dropping reasoning effort on opencode families (#10788) ([#11409](https://github.com/diegosouzapw/OmniRoute/pull/11409)) — thanks @oyi77 +- **fix(combo):** enable genuine quota-aware routing for generic providers (antigravity, claude, etc.) ([#11411](https://github.com/diegosouzapw/OmniRoute/pull/11411)) — thanks @HouMinXi +- **fix(oauth):** allow expired token retry before deactivation and activate expired retry loop (#P0) ([#11414](https://github.com/diegosouzapw/OmniRoute/pull/11414)) — thanks @HouMinXi +- **perf(executors):** lazy-load the executor registry — defer class imports + construction to first use (#11220) ([#11421](https://github.com/diegosouzapw/OmniRoute/pull/11421)) — thanks @oyi77 +- **fix:** make release/v3.8.50 CI gates green (run 32786966560 root causes) ([#11450](https://github.com/diegosouzapw/OmniRoute/pull/11450)) — thanks @jonlwheat2-gif +- **fix(sse):** exact-domain cookie match and origin-equality URL assertions ([#11452](https://github.com/diegosouzapw/OmniRoute/pull/11452)) — thanks @hartmark +- **fix(providers):** send Responses API payload during OpenAI-compatible validation ([#11454](https://github.com/diegosouzapw/OmniRoute/pull/11454)) — thanks @tuandinh0801 +- **fix(command-code):** fallback to /alpha/generate for Go plan without Provider API access ([#11455](https://github.com/diegosouzapw/OmniRoute/pull/11455)) — thanks @Chewji9875 +- **fix(responses-continuation):** fail closed on a log-truncated stored input/output array ([#11473](https://github.com/diegosouzapw/OmniRoute/pull/11473)) — thanks @hartmark +- **fix(cli):** write jcode settings as [providers.omniroute] in config.toml ([#11484](https://github.com/diegosouzapw/OmniRoute/pull/11484)) — thanks @hongnoul +- **fix(logging):** raise chat-log array truncation cap to 1000 and unify duplicate implementations ([#11499](https://github.com/diegosouzapw/OmniRoute/pull/11499)) — thanks @hartmark +- **fix:** restore live websocket URL helpers ([#11509](https://github.com/diegosouzapw/OmniRoute/pull/11509)) — thanks @PauloFH +- **perf(core):** eliminate async waterfalls across home, cache, and db operations (#11396) ([#11512](https://github.com/diegosouzapw/OmniRoute/pull/11512)) — thanks @HouMinXi +- **fix(kiro):** add runtime.us-east-1.kiro.dev as first-attempt endpoint ([#11517](https://github.com/diegosouzapw/OmniRoute/pull/11517)) — thanks @rifqiawl +- **fix(providers):** remove duplicate 128-tool truncation in OpencodeExecutor (#11444) ([#11518](https://github.com/diegosouzapw/OmniRoute/pull/11518)) — thanks @HouMinXi +- **fix(providers):** poll onboardUser LRO before classifying BYOP ([#11519](https://github.com/diegosouzapw/OmniRoute/pull/11519)) — thanks @rifqiawl +- **fix(translator):** preserve exact tool name casing for Claude tool_use payloads (#11487) ([#11520](https://github.com/diegosouzapw/OmniRoute/pull/11520)) — thanks @HouMinXi +- **fix(kimi-web):** align health probe with executor domain ([#11521](https://github.com/diegosouzapw/OmniRoute/pull/11521)) — thanks @pacocartones +- **fix(cli):** preserve existing admin password during setup ([#11522](https://github.com/diegosouzapw/OmniRoute/pull/11522)) — thanks @pacocartones +- **fix(mitm):** keep Windows DNS elevation off the POSIX sudo path (#11430) ([#11542](https://github.com/diegosouzapw/OmniRoute/pull/11542)) — thanks @official-burak +- **fix(sse):** scale chat admission by ingest byte budget instead of a fixed request count ([#11548](https://github.com/diegosouzapw/OmniRoute/pull/11548)) — thanks @TheDemonTuan +- **fix:** accept Gemini Web cookie exports ([#11554](https://github.com/diegosouzapw/OmniRoute/pull/11554)) — thanks @rafacpti23 +- **fix(i18n):** complete Brazilian Portuguese translations ([#11558](https://github.com/diegosouzapw/OmniRoute/pull/11558)) — thanks @rafacpti23 +- **fix(sse):** explicit types for openai-responses pureHelpers — clears last failing core typecheck gate ([#11567](https://github.com/diegosouzapw/OmniRoute/pull/11567)) — thanks @jonlwheat2-gif +- **fix(opencode):** hydrate Proxy Pool references for no-auth connections ([#11584](https://github.com/diegosouzapw/OmniRoute/pull/11584)) — thanks @AStupidBear +- **fix(ci):** resolve inherited release/v3.8.51 base-red CI failures ([#11608](https://github.com/diegosouzapw/OmniRoute/pull/11608)) — thanks @jonlwheat2-gif +- **fix(db):** fallback load STORAGE_ENCRYPTION_KEY from env files ([#11614](https://github.com/diegosouzapw/OmniRoute/pull/11614)) — thanks @benzntech +- **fix(providers):** mark Brave Search as no longer having a free tier ([#11621](https://github.com/diegosouzapw/OmniRoute/pull/11621)) — thanks @turbolego +- **fix:** pass tool result content through as string in OpenAI/Claude -> Gemini translation ([#11624](https://github.com/diegosouzapw/OmniRoute/pull/11624)) — thanks @Hsia97 +- **fix(sse):** scope credential health to exact key ([#11628](https://github.com/diegosouzapw/OmniRoute/pull/11628)) — thanks @KaspaPulse +- **fix(install):** guard prepare script against missing husky ([#11634](https://github.com/diegosouzapw/OmniRoute/pull/11634)) — thanks @Prajeeth-12 +- **fix(security):** redact NVIDIA_API_KEY at the log sink in the ad-hoc diag script ([#11639](https://github.com/diegosouzapw/OmniRoute/pull/11639)) +- **fix(types):** clear the two type regressions red on release/v3.8.51 ([#11640](https://github.com/diegosouzapw/OmniRoute/pull/11640)) — thanks @jonlwheat2-gif +- **fix(build):** resolve workspace: protocol dependencies to concrete semver versions during prepublish (#11540) ([#11641](https://github.com/diegosouzapw/OmniRoute/pull/11641)) — thanks @HouMinXi +- **fix(search):** properly resolve configured search connection in /v1/responses pipeline (#11524) ([#11642](https://github.com/diegosouzapw/OmniRoute/pull/11642)) — thanks @HouMinXi +- **fix(authz):** trust cf-connecting-ip for via-proxy detection when peer is Cloudflare edge (#11514) ([#11643](https://github.com/diegosouzapw/OmniRoute/pull/11643)) — thanks @HouMinXi +- **fix(security):** compare the OIDC state cookie in constant time ([#11646](https://github.com/diegosouzapw/OmniRoute/pull/11646)) +- **fix(security):** namespace the dedup hash by the calling API key ([#11649](https://github.com/diegosouzapw/OmniRoute/pull/11649)) +- **fix(models):** gate the Codex-native catalog loop on prefix mode (#11632) ([#11662](https://github.com/diegosouzapw/OmniRoute/pull/11662)) — thanks @xiaoyaner0201 +- **fix(dashboard):** project Codex routing quota in account pools ([#11666](https://github.com/diegosouzapw/OmniRoute/pull/11666)) — thanks @KaspaPulse +- **fix(ddgw):** normaliza system no upstream da release v3.8.51 ([#11692](https://github.com/diegosouzapw/OmniRoute/pull/11692)) — thanks @RhianB14 +- **fix(memory):** honor category filter in GET /api/memory ([#11699](https://github.com/diegosouzapw/OmniRoute/pull/11699)) — thanks @Sabeekhann +- **fix(ci):** detect stale workspace lockfile entries ([#11714](https://github.com/diegosouzapw/OmniRoute/pull/11714)) — thanks @pacocartones +- **fix(ci):** stamp dist/BUILD_SHA before the npm publish provenance gate ([#11721](https://github.com/diegosouzapw/OmniRoute/pull/11721)) +- **fix(docker):** decouple the best-effort Bun image from the release manifest ([#11724](https://github.com/diegosouzapw/OmniRoute/pull/11724)) +- **perf(compression):** avoid cloning memo misses twice ([#11727](https://github.com/diegosouzapw/OmniRoute/pull/11727)) — thanks @RaviTharuma +- **fix(docker):** let the bun digest artifact be absent, not fatal ([#11740](https://github.com/diegosouzapw/OmniRoute/pull/11740)) +- **fix(shared):** block cloud-metadata hosts under default remote-image guard ([#11755](https://github.com/diegosouzapw/OmniRoute/pull/11755)) — thanks @santosraju99-hub +- **fix(api):** preserve caller-provided X-Correlation-Id on chat completions ([#11760](https://github.com/diegosouzapw/OmniRoute/pull/11760)) — thanks @aniruddhaadak80 +- **fix(resilience):** lock permanently retired models instead of short backoff (Gemini ban prevention) ([#11762](https://github.com/diegosouzapw/OmniRoute/pull/11762)) — thanks @turbolego +- **fix:** mark Vercel AI Gateway as passthroughModels ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **fix(resilience):** stop hammering permanently-moved endpoints and billing-suspended accounts ([#11774](https://github.com/diegosouzapw/OmniRoute/pull/11774)) — thanks @turbolego +- **fix(lease):** remove global static reservation and gate routing on live active lease occupancy ([#11775](https://github.com/diegosouzapw/OmniRoute/pull/11775)) — thanks @TheDemonTuan +- **fix(ci):** size the install-upgrade gate to a measured run, and log the pack cost ([#11776](https://github.com/diegosouzapw/OmniRoute/pull/11776)) +- **fix(pwa):** stop serving the stale shell after deploys ([#11779](https://github.com/diegosouzapw/OmniRoute/pull/11779)) — thanks @HouMinXi +- **fix(combo):** lock GitHub models rejected as "not supported" for future requests ([#11781](https://github.com/diegosouzapw/OmniRoute/pull/11781)) — thanks @turbolego +- **fix(cli):** probe both IPv4 and IPv6 loopback for server readiness (#11766) ([#11794](https://github.com/diegosouzapw/OmniRoute/pull/11794)) — thanks @vermasomesh835 +- **fix(sse):** preserve CLIProxyAPI wrappers per connection ([#11797](https://github.com/diegosouzapw/OmniRoute/pull/11797)) — thanks @RaviTharuma +- **fix(providers):** honor model auto-fetch opt-in on create ([#11798](https://github.com/diegosouzapw/OmniRoute/pull/11798)) — thanks @RaviTharuma +- **fix(dashboard):** make model auto-fetch order independent ([#11805](https://github.com/diegosouzapw/OmniRoute/pull/11805)) — thanks @RaviTharuma +- **fix(docker):** make GHA cache exports best effort ([#11806](https://github.com/diegosouzapw/OmniRoute/pull/11806)) — thanks @RaviTharuma +- **fix(kiro):** do not permanently ban on 'User is not authorized to make this call' ([#11809](https://github.com/diegosouzapw/OmniRoute/pull/11809)) — thanks @Deftera186 +- **fix(sse):** keep cache-write tokens in OpenAI-shaped usage ([#11814](https://github.com/diegosouzapw/OmniRoute/pull/11814)) — thanks @TheDemonTuan +- **fix(docs):** resolve relative markdown and wiki links across Fumadocs and GitHub wiki ([#11834](https://github.com/diegosouzapw/OmniRoute/pull/11834)) — thanks @AndrianBalanescu +- **fix(chat):** preserve unstripped model string for passthrough provider routing ([#11840](https://github.com/diegosouzapw/OmniRoute/pull/11840)) — thanks @raheemuddin786 +- **fix(mcp):** avoid async initialization inside sync esm wrappers (#11569) ([#11849](https://github.com/diegosouzapw/OmniRoute/pull/11849)) — thanks @HouMinXi +- **fix(sse):** preserve coherent markdown token boundaries across stream translation chunks (#11606) ([#11851](https://github.com/diegosouzapw/OmniRoute/pull/11851)) — thanks @HouMinXi +- **fix(ci):** run the install-upgrade gate on disk, not on the /tmp tmpfs ([#11855](https://github.com/diegosouzapw/OmniRoute/pull/11855)) +- **fix(skills):** expand shorthand property types in injected tool schemas ([#11857](https://github.com/diegosouzapw/OmniRoute/pull/11857)) — thanks @alvinveroy +- **fix(ci):** publish npm from a hosted runner so provenance is accepted ([#11877](https://github.com/diegosouzapw/OmniRoute/pull/11877)) +- **fix(lint):** pin react hooks plugin to 7.0.1 ([#11886](https://github.com/diegosouzapw/OmniRoute/pull/11886)) +- **fix(ci):** surface and clear release-green ESLint failure ([#11890](https://github.com/diegosouzapw/OmniRoute/pull/11890)) — thanks @MumuTW +- **fix(guardrails):** prevent duplicate prompt-injection-guard log output ([#11936](https://github.com/diegosouzapw/OmniRoute/pull/11936)) — thanks @patrykkopycinski +- **fix(auth):** downgrade expected transient states from warn to debug ([#11937](https://github.com/diegosouzapw/OmniRoute/pull/11937)) — thanks @patrykkopycinski +- **fix(codex):** fail over image generation for imported free plans ([#11948](https://github.com/diegosouzapw/OmniRoute/pull/11948)) — thanks @Bl0ck154 +- **fix(codex):** normalize non-stream responses ([#11951](https://github.com/diegosouzapw/OmniRoute/pull/11951)) — thanks @Bl0ck154 +- **fix(images):** forward Antigravity image size ([#11952](https://github.com/diegosouzapw/OmniRoute/pull/11952)) — thanks @Bl0ck154 +- **fix(db):** invalidate connection cache after upsert ([#11953](https://github.com/diegosouzapw/OmniRoute/pull/11953)) — thanks @Bl0ck154 +- **fix(codex):** restore imported account state ([#11954](https://github.com/diegosouzapw/OmniRoute/pull/11954)) — thanks @Bl0ck154 +- **fix(sse):** give extended-thinking targets the reasoning readiness budget ([#11959](https://github.com/diegosouzapw/OmniRoute/pull/11959)) — thanks @abhisheksharma2411 +- **fix(dashboard):** use opaque background and readable text color on cost chart tooltips ([#11960](https://github.com/diegosouzapw/OmniRoute/pull/11960)) — thanks @ZaimMarzuki +- **fix(antigravity):** send complete loadCodeAssist metadata (ideType/platform/pluginType as numeric enums) ([#11969](https://github.com/diegosouzapw/OmniRoute/pull/11969)) — thanks @rifqiawl +- **fix(core):** resolve DB init race condition and reasoning translation ([#12003](https://github.com/diegosouzapw/OmniRoute/pull/12003)) — thanks @raheemuddin786 +- **fix(admission):** exclude reclaimable page cache from the cgroup pressure ratio ([#12017](https://github.com/diegosouzapw/OmniRoute/pull/12017)) — thanks @HouMinXi +- **fix(build):** prune non-production trees in NFT trace excludes and tsconfig ([#12028](https://github.com/diegosouzapw/OmniRoute/pull/12028)) — thanks @Chewji9875 +- **fix(migrations):** add renamed migration compatibility for 056/073/077/101 ([#12036](https://github.com/diegosouzapw/OmniRoute/pull/12036)) — thanks @oyi77 +- **fix(sse):** default crash-guard logger to console.warn, not console ([#12042](https://github.com/diegosouzapw/OmniRoute/pull/12042)) — thanks @HouMinXi +- **fix(catalog):** derive vision/modalities for built-in auto combos from effective target pool ([#12046](https://github.com/diegosouzapw/OmniRoute/pull/12046)) — thanks @Prajeeth-12 +- **fix(release):** the packaged-app smoke verifies the database opened, not a driver line the primary path never prints (twin of #12032) ([#12047](https://github.com/diegosouzapw/OmniRoute/pull/12047)) +- **fix(barrel):** migrate src/app/ off the @/lib/localDb barrel import (#11795 Phase 2) ([#12051](https://github.com/diegosouzapw/OmniRoute/pull/12051)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate src/lib/ off the localDb barrel to direct db imports (#59) ([#12052](https://github.com/diegosouzapw/OmniRoute/pull/12052)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate open-sse, src/shared, src/sse, src/models, src/domain off the @/lib/localDb barrel import (#11795 Phase 4) ([#12053](https://github.com/diegosouzapw/OmniRoute/pull/12053)) — thanks @jonlwheat2-gif +- **fix(barrel):** delete the @/lib/localDb barrel — every consumer migrated (#11795 Phase 5) ([#12055](https://github.com/diegosouzapw/OmniRoute/pull/12055)) — thanks @jonlwheat2-gif +- **fix(radar):** restore D12 public boundary ([#12057](https://github.com/diegosouzapw/OmniRoute/pull/12057)) +- **fix(dev):** bound webpack and Tailwind scans ([#12075](https://github.com/diegosouzapw/OmniRoute/pull/12075)) — thanks @backryun +- **fix(dev):** isolate root layout settings reads ([#12076](https://github.com/diegosouzapw/OmniRoute/pull/12076)) — thanks @backryun +- **fix(dev):** reduce instrumentation executor fan-out (phase 3) ([#12078](https://github.com/diegosouzapw/OmniRoute/pull/12078)) — thanks @backryun +- **fix(dev):** make logging resources HMR-singleton ([#12079](https://github.com/diegosouzapw/OmniRoute/pull/12079)) — thanks @backryun +- **fix(sse):** keep unavailable forced connections scoped ([#12080](https://github.com/diegosouzapw/OmniRoute/pull/12080)) — thanks @keeltrace +- **fix(dev):** isolate batch dispatch from instrumentation ([#12081](https://github.com/diegosouzapw/OmniRoute/pull/12081)) — thanks @backryun +- **fix(ci):** reconcile release test contract drift ([#12082](https://github.com/diegosouzapw/OmniRoute/pull/12082)) — thanks @backryun +- **fix(ci):** accept CVE-2025-68121 in the prebuilt tls-client .so, auto-close base-red issues, guard Scorecard on the default branch ([#12085](https://github.com/diegosouzapw/OmniRoute/pull/12085)) +- **fix(db):** drop three consumer-less 1proxy exports — dead-code base-red on release/v3.8.51 after the barrel deletion (#12055) ([#12087](https://github.com/diegosouzapw/OmniRoute/pull/12087)) +- **fix(api):** clear the six API-route TypeScript regressions the new gate landed red on ([#12094](https://github.com/diegosouzapw/OmniRoute/pull/12094)) +- **fix(release):** never let the tag-push Create Release append auto notes to the curated body ([#12096](https://github.com/diegosouzapw/OmniRoute/pull/12096)) +- **fix(sse):** honor CLIProxyAPI environment API key ([#12099](https://github.com/diegosouzapw/OmniRoute/pull/12099)) — thanks @RaviTharuma +- **fix(oauth):** bind Google refresh to the client that issued the token ([#12106](https://github.com/diegosouzapw/OmniRoute/pull/12106)) — thanks @HouMinXi +- **fix(dashboard):** make RequestLoggerDetail loadable outside Next — CSS via globals.css + CJS/ESM interop (#11703 base-reds) ([#12114](https://github.com/diegosouzapw/OmniRoute/pull/12114)) +- **fix(leases):** project status lease row to lease columns so joined connection PII never escapes ([#12115](https://github.com/diegosouzapw/OmniRoute/pull/12115)) — thanks @geek007git +- **fix(plugins):** do not kill the plugin process when a fire-and-forget hook times out ([#12116](https://github.com/diegosouzapw/OmniRoute/pull/12116)) — thanks @geek007git +- **fix(guardrails):** restore injection-guard logging on middleware-only routes ([#12117](https://github.com/diegosouzapw/OmniRoute/pull/12117)) — thanks @geek007git +- **fix(plugins):** refresh stored manifest from disk on activate so new hook fields reach existing installs ([#12120](https://github.com/diegosouzapw/OmniRoute/pull/12120)) — thanks @geek007git +- **fix(codex):** preserve existing provider state when bulk-import upserts a matching connection ([#12122](https://github.com/diegosouzapw/OmniRoute/pull/12122)) — thanks @geek007git +- **fix(ci):** clear the two base-reds the 2026-08-30 merge batch left on release/v3.8.51 (round 3) ([#12123](https://github.com/diegosouzapw/OmniRoute/pull/12123)) +- **fix(sse):** strip type:'custom' from Claude tools on agentrouter dispatch ([#12126](https://github.com/diegosouzapw/OmniRoute/pull/12126)) — thanks @wahidsadik371-coder +- **fix(sse):** stop advertising video providers the dispatcher cannot run ([#12131](https://github.com/diegosouzapw/OmniRoute/pull/12131)) — thanks @quiterunner-commits +- **fix(search):** treat HTTP 432 and plan limit errors as transient cooldown ([#12139](https://github.com/diegosouzapw/OmniRoute/pull/12139)) — thanks @HouMinXi +- **fix(sse):** refuse an AI Horde queue that cannot fit the request budget ([#12143](https://github.com/diegosouzapw/OmniRoute/pull/12143)) — thanks @quiterunner-commits +- **fix(ci):** clear the base-reds the afternoon merge batch left on release/v3.8.51 (round 5: provider count 352, TS2554/TS2677) ([#12144](https://github.com/diegosouzapw/OmniRoute/pull/12144)) +- **fix(translator):** strip neutral tool_choice when tools absent in Responses-to-Chat (#12141) ([#12166](https://github.com/diegosouzapw/OmniRoute/pull/12166)) — thanks @HouMinXi +- **fix(guardrails):** pass providerId to getResolvedModelCapabilities in checkComboVision (#12112) ([#12169](https://github.com/diegosouzapw/OmniRoute/pull/12169)) — thanks @HouMinXi +- **fix(translator):** strip plaintext reasoning content for opaque responses backends (#12128) ([#12171](https://github.com/diegosouzapw/OmniRoute/pull/12171)) — thanks @HouMinXi +- **perf(stream):** compile hot-path regexes once, bound token caches, fix quadratic buffering ([#12179](https://github.com/diegosouzapw/OmniRoute/pull/12179)) — thanks @opensource-elearning +- **fix:** resolve compression worker file using runtime anchors instead of… ([#12183](https://github.com/diegosouzapw/OmniRoute/pull/12183)) — thanks @NightStalker-87 +- **fix:** prevent Claude Code session kills via liveness-aware readiness + auto model echo ([#12189](https://github.com/diegosouzapw/OmniRoute/pull/12189)) — thanks @opensource-elearning +- **fix(translator):** preserve falsy primitive values in Gemini and Antigravity function response results ([#12191](https://github.com/diegosouzapw/OmniRoute/pull/12191)) — thanks @Siva010 +- **fix(oauth):** mark empty Antigravity projectId as degraded and clear stale errors (#11284) ([#12205](https://github.com/diegosouzapw/OmniRoute/pull/12205)) — thanks @HouMinXi +- **fix(providers):** gate the Codex auto-ping usage read on the shared quota throttle ([#12209](https://github.com/diegosouzapw/OmniRoute/pull/12209)) — thanks @abhisheksharma2411 +- **fix(combos):** prioritize SQLite row id over inner JSON id and notify delete errors ([#12213](https://github.com/diegosouzapw/OmniRoute/pull/12213)) — thanks @HouMinXi +- **fix(providers):** mark featherless.ai as no longer having a free tier (`hasFree: false`) ([#12216](https://github.com/diegosouzapw/OmniRoute/pull/12216)) — thanks @turbolego +- **fix(deepseek-web):** stop Turbopack dev panic in the PoW worker path resolver ([#12221](https://github.com/diegosouzapw/OmniRoute/pull/12221)) — thanks @hartmark +- **fix(chat-admission):** clarify local 503 source ([#12223](https://github.com/diegosouzapw/OmniRoute/pull/12223)) — thanks @keeltrace +- **fix(dev):** silence webpack runtime module warnings ([#12228](https://github.com/diegosouzapw/OmniRoute/pull/12228)) — thanks @backryun +- **fix(db):** use module.require for CommonJS runtime driver loading ([#12230](https://github.com/diegosouzapw/OmniRoute/pull/12230)) — thanks @raheemuddin786 +- **fix(memory):** honest probe-driven FTS5 keyword status + memory_id rowid sync ([#12231](https://github.com/diegosouzapw/OmniRoute/pull/12231)) — thanks @raheemuddin786 +- **fix(system):** propagate abort signal to stream reader in HTTP version checks ([#12232](https://github.com/diegosouzapw/OmniRoute/pull/12232)) — thanks @raheemuddin786 +- **fix(resilience):** isolate local host execution errors from provider circuit breakers ([#12233](https://github.com/diegosouzapw/OmniRoute/pull/12233)) — thanks @raheemuddin786 +- **fix(sse):** sort injected tools deterministically for prompt caching ([#12234](https://github.com/diegosouzapw/OmniRoute/pull/12234)) — thanks @raheemuddin786 +- **fix(combo):** always clear the loop-safety timer, not just on the happy path (#11804) ([#12245](https://github.com/diegosouzapw/OmniRoute/pull/12245)) +- **fix(quality):** base-red pair — stryker tap registration + turn-pin suites aligned to the window gate ([#12255](https://github.com/diegosouzapw/OmniRoute/pull/12255)) — thanks @backryun +- **fix(dev):** qualify Turbopack runtime boundaries ([#12258](https://github.com/diegosouzapw/OmniRoute/pull/12258)) — thanks @backryun +- **fix(combo):** fall back to full pool when collapsed sole survivor is context-too-small ([#12278](https://github.com/diegosouzapw/OmniRoute/pull/12278)) — thanks @opensource-elearning +- **fix(combo):** bound the pre-dispatch unavailable skip so a stale label cannot dark a pool (#12168) ([#12285](https://github.com/diegosouzapw/OmniRoute/pull/12285)) +- **fix(security):** strip Qwen/Alibaba console-session cookies from provider API responses ([#12287](https://github.com/diegosouzapw/OmniRoute/pull/12287)) +- **fix(usage):** console-aware Token Plan guidance + subscription hint on bailian 401 ([#12288](https://github.com/diegosouzapw/OmniRoute/pull/12288)) +- **fix(resourcePressure):** log numeric detail on every rejection, recover faster ([#12293](https://github.com/diegosouzapw/OmniRoute/pull/12293)) — thanks @hartmark +- **fix(release):** drain the 2026-09-01 base-red window — passthrough usage regression + radarPage i18n keys ([#12327](https://github.com/diegosouzapw/OmniRoute/pull/12327)) +- **fix(ci):** openapi-security-tiers checker must honor routeGuard patterns + imported prefixes ([#12350](https://github.com/diegosouzapw/OmniRoute/pull/12350)) — thanks @ggiak +- **perf(ci):** cache node_modules in the npm-ci-retry composite (#8084 D3) ([#12408](https://github.com/diegosouzapw/OmniRoute/pull/12408)) +- **revert(ui):** point CTAs back at their real destinations ([#12410](https://github.com/diegosouzapw/OmniRoute/pull/12410)) +- **fix(release):** drain the 2026-09-02 base-red — rerank-providers import + api-typecheck baseline ratchet ([#12414](https://github.com/diegosouzapw/OmniRoute/pull/12414)) +- **fix(types):** make system prompt injection noImplicitAny-safe ([#12416](https://github.com/diegosouzapw/OmniRoute/pull/12416)) +- **fix(quality):** validate typecheck baseline schema ([#12419](https://github.com/diegosouzapw/OmniRoute/pull/12419)) +- **fix(memory):** resolve rerank provider node cache import ([#12421](https://github.com/diegosouzapw/OmniRoute/pull/12421)) +- **fix(ci):** repair release-root regressions — pack dedup, web-session syntax, uc-image ids ([#12423](https://github.com/diegosouzapw/OmniRoute/pull/12423)) — thanks @backryun +- **fix(providers):** separate MaxAI and UC credential contracts ([#12431](https://github.com/diegosouzapw/OmniRoute/pull/12431)) +- **fix(cli):** preserve Claude settings on config set ([#12432](https://github.com/diegosouzapw/OmniRoute/pull/12432)) — thanks @KrzysiekSko +- **fix(providers):** repair the maxai credential block truncated by merge auto-resolve ([#12433](https://github.com/diegosouzapw/OmniRoute/pull/12433)) +- **fix(quality):** record the 2026-09-02 merged growth in the file-size baseline ([#12434](https://github.com/diegosouzapw/OmniRoute/pull/12434)) +- **fix(uc):** route UC error strings through sanitizeErrorMessage; allowlist the retired codex id ([#12437](https://github.com/diegosouzapw/OmniRoute/pull/12437)) +- **fix(adapta):** redact streamed upstream errors ([#12438](https://github.com/diegosouzapw/OmniRoute/pull/12438)) +- **fix(sse):** remap non-contiguous upstream tool_calls index to a gap-free output_index ([#12445](https://github.com/diegosouzapw/OmniRoute/pull/12445)) — thanks @hartmark +- **fix(quality):** detect duplicate tool_calls entries in one response ([#12446](https://github.com/diegosouzapw/OmniRoute/pull/12446)) — thanks @hartmark +- **fix(conversations):** resolve turn content OmniRoute never sends back to the client ([#12447](https://github.com/diegosouzapw/OmniRoute/pull/12447)) — thanks @hartmark +- **fix(combo):** do not treat credits-exhausted 401 as auth skip ([#12449](https://github.com/diegosouzapw/OmniRoute/pull/12449)) — thanks @RaviTharuma +- **fix(auth):** do not park healthy quota accounts as expired ([#12452](https://github.com/diegosouzapw/OmniRoute/pull/12452)) — thanks @RaviTharuma +- **fix(responses-continuation):** fail closed on a collector-truncated, empty output array ([#12460](https://github.com/diegosouzapw/OmniRoute/pull/12460)) — thanks @hartmark +- **fix(logging):** raise the SSE payload collector's default cap ([#12461](https://github.com/diegosouzapw/OmniRoute/pull/12461)) — thanks @hartmark +- **fix(resilience):** surface Responses failed.error.message in 502s ([#12472](https://github.com/diegosouzapw/OmniRoute/pull/12472)) — thanks @RaviTharuma +- **fix(security):** unbiased maxai X-Random nonce + stricter URL/regex assertions ([#12502](https://github.com/diegosouzapw/OmniRoute/pull/12502)) +- **fix(video):** re-anchor transcript log-redaction so PII/credential maskers can't reopen the leak (#12150 P1 follow-up) ([#12503](https://github.com/diegosouzapw/OmniRoute/pull/12503)) +- **fix(monitoring):** serve cached credentialHealth off the request path ([#12533](https://github.com/diegosouzapw/OmniRoute/pull/12533)) — thanks @RaviTharuma +- **fix(quota):** keep Antigravity Gemini usable when Claude weekly is empty ([#12566](https://github.com/diegosouzapw/OmniRoute/pull/12566)) — thanks @HouMinXi +- **fix(authz):** hard-gate every credential export and CLI-config write (GHSA-5926-2w35-7h4q) ([#12600](https://github.com/diegosouzapw/OmniRoute/pull/12600)) +- **fix(docs):** restore the Next build — REMOVED_PROVIDERS.md had no frontmatter (base-red #12581) ([#12610](https://github.com/diegosouzapw/OmniRoute/pull/12610)) +- **fix(quality):** re-point the zcodeProtocol public-creds allowlist to line 313 ([#12615](https://github.com/diegosouzapw/OmniRoute/pull/12615)) +- **fix(security):** close 3 advisories — search baseUrl exfil, sk- in the error sanitizer, bifrost relay header leak ([#12620](https://github.com/diegosouzapw/OmniRoute/pull/12620)) +- **fix(providers):** sanitize boolean required and nested bare maps for Gemini (#12269) ([#12624](https://github.com/diegosouzapw/OmniRoute/pull/12624)) — thanks @HouMinXi +- **fix(resilience):** keep Overloaded STREAM_EARLY_EOF off the provider breaker ([#12626](https://github.com/diegosouzapw/OmniRoute/pull/12626)) — thanks @HouMinXi +- **fix(catalog):** re-audit free-tier quotas against official pages ([#12649](https://github.com/diegosouzapw/OmniRoute/pull/12649)) +- **fix(dashboard):** keep a request's pending-tracking id stable across combo target retries ([#12650](https://github.com/diegosouzapw/OmniRoute/pull/12650)) — thanks @hartmark +- **fix(tests):** retire dead model ids from the chat-pipeline integration suite (base-red #12581) ([#12670](https://github.com/diegosouzapw/OmniRoute/pull/12670)) +- **fix:** resolve SqliteError no such table compression_run_telemetry during cleanup ([#12682](https://github.com/diegosouzapw/OmniRoute/pull/12682)) — thanks @groovecityJO +- **fix(combo):** treat a pin-only step as implicit connection allowlist ([#12697](https://github.com/diegosouzapw/OmniRoute/pull/12697)) — thanks @HouMinXi +- **fix(ci):** ship .npmrc in published package so legacy-peer-deps applies to consumers (#11544) ([#12699](https://github.com/diegosouzapw/OmniRoute/pull/12699)) — thanks @KooshaPari +- **fix(docker):** pin 4 CLI tools to exact versions (#12576) ([#12703](https://github.com/diegosouzapw/OmniRoute/pull/12703)) — thanks @KooshaPari +- **fix(docker):** document and harden cli profile trust boundary (#12570) ([#12706](https://github.com/diegosouzapw/OmniRoute/pull/12706)) — thanks @KooshaPari +- **fix(sse):** redact video transcript in pre-guardrail rejected-request logs (#12150 P2 item 7) ([#12710](https://github.com/diegosouzapw/OmniRoute/pull/12710)) +- **fix(i18n):** quote placeholder in OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES description (#12505) ([#12769](https://github.com/diegosouzapw/OmniRoute/pull/12769)) — thanks @KooshaPari +- **fix(api/pricing):** surface validation error message as string, not raw object (#12494) ([#12771](https://github.com/diegosouzapw/OmniRoute/pull/12771)) — thanks @KooshaPari +- **fix(sse):** re-enable prompt compression for native Codex passthrough ([#12834](https://github.com/diegosouzapw/OmniRoute/pull/12834)) — thanks @thomasmaerz +- **fix(dashboard):** model health tests for a provider node set to the Responses API now call `/v1/responses` with a Responses-shaped body instead of `/v1/chat/completions` — those models were reported as `Provider returned HTTP 200 but no text content` even though the same model answered normally through `/v1/responses` ([#13070](https://github.com/diegosouzapw/OmniRoute/issues/13070)) ([#13087](https://github.com/diegosouzapw/OmniRoute/pull/13087)) — thanks @ntdatt812 +- fix(routing): stop a round-robin combo's "opencode" targets from collapsing onto the opencode-zen connection (#11912) ([#13283](https://github.com/diegosouzapw/OmniRoute/pull/13283)) +- fix(routing): stop the reactive-compaction debug log from lying when compression is globally disabled (#11977) ([#13276](https://github.com/diegosouzapw/OmniRoute/pull/13276)) +- fix(electron): relativize standalone-bundle symlink targets so Stage 8 manifest verification stops failing on Windows (#11979) ([#13251](https://github.com/diegosouzapw/OmniRoute/pull/13251)) +- fix(dashboard): surface a visible error when Compression Studio's combined preview run fails (#12061) ([#13288](https://github.com/diegosouzapw/OmniRoute/pull/13288)) +- fix(dashboard): make the compression "Effective pipeline" preview honor the active profile and warn when the master switch is off (#12063) ([#13287](https://github.com/diegosouzapw/OmniRoute/pull/13287)) +- fix(providers): scope TinyCMS Web signer's DOM shims to each call instead of leaking them for the process lifetime, and surface a clean HTTP status on a non-JSON interception-toggles error (#12072) ([#13254](https://github.com/diegosouzapw/OmniRoute/pull/13254)) +- fix(guardrails): stop Vision Bridge from re-selecting a model locked after a 404 (#12111) ([#13259](https://github.com/diegosouzapw/OmniRoute/pull/13259)) +- fix(sse): require Responses-shaped body before native OpenAI-compatible passthrough (#12129) ([#13278](https://github.com/diegosouzapw/OmniRoute/pull/13278)) +- fix(providers): minimax-m3 now collapses manual thinking.type:"enabled" to adaptive, preventing upstream 400 (2013) (#12132) ([#13249](https://github.com/diegosouzapw/OmniRoute/pull/13249)) +- fix(db): scope model visibility overrides by modality so hiding a Chat model no longer hides an identically-ID'd Image/Embeddings/etc. model (#12172) ([#13258](https://github.com/diegosouzapw/OmniRoute/pull/13258)) +- fix(db): scope local-provider apiKey dedup to matching base URL so LM Studio/Ollama-style connections support multiple accounts (#12173) ([#13250](https://github.com/diegosouzapw/OmniRoute/pull/13250)) +- fix(providers): refresh Trae's stale Referer/Origin and forward user timezone so imported connections stop failing with 401 (#12190) ([#13255](https://github.com/diegosouzapw/OmniRoute/pull/13255)) +- fix(providers): route opencode-go/gpt-5.6-luna to /responses instead of /chat/completions (#12196) ([#13275](https://github.com/diegosouzapw/OmniRoute/pull/13275)) +- fix(dashboard): allow deleting the last extra-upstream-header row even when invalid (#12251) ([#13252](https://github.com/diegosouzapw/OmniRoute/pull/13252)) +- **fix(i18n):** translate pre-existing `__MISSING__:` keys for `combo.sort`, `requestLogger.detail` expand/collapse, `common.profile`, and `settings.resilienceCredentialHealth*` across 39 locales ([#12272](https://github.com/diegosouzapw/OmniRoute/issues/12272)) ([#13034](https://github.com/diegosouzapw/OmniRoute/pull/13034)) — thanks @HouMinXi +- fix(cli): run the Node.js runtime compatibility guard before the heavy `tsx/esm` + Commander import chain so an unsupported runtime gets a clear message instead of a raw `Invalid regular expression flags` crash (#12296) ([#13253](https://github.com/diegosouzapw/OmniRoute/pull/13253)) +- fix(dashboard): refresh the providers list after deleting a compatible provider node (#12298) ([#13256](https://github.com/diegosouzapw/OmniRoute/pull/13256)) +- fix(usage): fail closed on API-key budget enforcement when a provider's `auto` routing alias has no pricing row, instead of silently counting it as $0 (#12341) ([#13257](https://github.com/diegosouzapw/OmniRoute/pull/13257)) +- **fix(providers):** include the submitted Agnes video model when polling by `video_id` ([#12356](https://github.com/diegosouzapw/OmniRoute/pull/12356)) — thanks @pacocartones +- **fix(routing):** custom OpenAI-compatible nodes now honor the saved Chat/Responses API type after edits instead of letting the node's original ID prefix override the live connection setting ([#11884](https://github.com/diegosouzapw/OmniRoute/issues/11884)). ([#12358](https://github.com/diegosouzapw/OmniRoute/pull/12358)) — thanks @pacocartones +- fix(sse): surface an error instead of a silent empty 200 when a Claude stream closes with zero bytes (#12398) ([#13285](https://github.com/diegosouzapw/OmniRoute/pull/13285)) +- fix(oauth): warn before the dead localhost:8080 redirect in antigravity/gemini `oauth start` (#12413) ([#13265](https://github.com/diegosouzapw/OmniRoute/pull/13265)) +- fix(providers): stop devin-cli spawn error from double-closing the SSE controller (#12517) ([#13261](https://github.com/diegosouzapw/OmniRoute/pull/13261)) +- **fix(rerank):** clamp Voyage `top_k` to the documents actually sent after empty-string filtering, and honor `return_documents: false` in the NVIDIA response adapter (#12523 — thanks @pacocartones) +- **fix(audio):** `/v1/audio/translations` now resolves combo names the way `/v1/audio/transcriptions` already does, so a combo that `GET /v1/models` advertises is fanned out to its targets instead of being rejected with `400 Invalid translation model: . Use format: provider/model`; literal `provider/model` ids and unknown bare names behave as before (#12536 — thanks @pacocartones) +- **fix(gemini):** strip the JSON-Schema-2020-12 `prefixItems` keyword from Gemini tool schemas at every nesting level, so Claude Code tool definitions no longer fail with `400 Unknown name "prefixItems"` on Gemini models (#12540 — thanks @pacocartones) +- **fix(api-manager):** Expose an accessible loading status while API keys are fetched instead of an empty accessibility tree (#12541 — thanks @pacocartones) +- **fix(video):** Clamp `estimateJpegFrameBytes` at zero for padding-only payloads and build the three encode-side frame data URIs from `JPEG_FRAME_DATA_URI_PREFIX` instead of a repeated literal (#12543 — thanks @pacocartones) +- **fix(devin):** accept Windows `DEVIN_AGENTIC_HOME` sandbox paths (`C:\...\.sandbox\...`) in the isolated-home check so the Devin Claude Bridge no longer fails closed on Windows ([#12405](https://github.com/diegosouzapw/OmniRoute/issues/12405)) (#12545 — thanks @pacocartones) +- **fix(db):** Add `conversation_turn_nodes` and orphaned `agentic_conversations` to the auto-cleanup cycle under the existing `retention.callLogs` window, so identity nodes whose call-log content has already been purged no longer accumulate without bound in `storage.sqlite` (#12548 — thanks @pacocartones) +- **fix(i18n):** Wrap the `~/.claude/profiles//settings.json` placeholder in ICU single quotes in the `OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES` feature-flag description across all 42 locales and the TypeScript default, so next-intl no longer fails with `INVALID_MESSAGE: UNCLOSED_TAG` and the Feature Flags card shows the description instead of the raw key (#12549 — thanks @pacocartones) +- **fix(orchestration):** `updateCloudAgentTask` now publishes the task's real `status` on `agent.task.updated` when an update only touches `result`, `activities` or `error`, instead of the fabricated `"updated"` state, and stays silent when no row matched the id (#12550 — thanks @pacocartones) +- **fix(i18n):** the home "Recent Requests" panel and the Provider Topology legend are now translated instead of rendering English copies on non-English dashboards; the legend reads its own `home.topologyLegend*` labels with consistent casing rather than borrowing the memory-settings "Recent" and analytics "Error" strings (#12551 — thanks @pacocartones). +- **docs(reference):** bring the `FEATURE_FLAGS.md` catalog back to 1:1 with `featureFlagDefinitions.ts` — 20 missing flags added, the two `*_BLOCK_THRESHOLD` env-only knobs moved out of the flag tables, category/total counts and the Live WS port corrected, guarded by a static test (#12552 — thanks @pacocartones) +- **fix(i18n):** backfill missing `usage.kiloPass*` strings in 39 locales and restore `featureFlagOmnirouteDisableThinkingLevelVariantsDescription` in `pt.json` ([#12561](https://github.com/diegosouzapw/OmniRoute/issues/12561)) ([#13027](https://github.com/diegosouzapw/OmniRoute/pull/13027)) — thanks @HouMinXi +- fix(docker): default docker-compose app ports (dashboard/API/live-WS) to loopback instead of `0.0.0.0`, closing the anonymous `/v1` LAN/WAN exposure gap left open by `REQUIRE_API_KEY=false` (#12568) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): close DNS-rebinding SSRF gap in webhook outbound-URL guard (#12569) ([#13243](https://github.com/diegosouzapw/OmniRoute/pull/13243)) +- fix(docker): require a per-session token on the VNC browser CDP bridge and isolate it on a dedicated Docker network (#12571) ([#13241](https://github.com/diegosouzapw/OmniRoute/pull/13241)) +- fix(open-sse): write Adobe Firefly session tokens and cookie jars with 0700/0600 permissions instead of the process umask (#12572) ([#13242](https://github.com/diegosouzapw/OmniRoute/pull/13242)) +- fix(api): remove hardcoded wildcard CORS in openai-to-gemini-sse.ts so the centralized fail-closed CORS gate is the sole source of `Access-Control-Allow-Origin` (#12573) ([#13239](https://github.com/diegosouzapw/OmniRoute/pull/13239)) +- fix(api): enforce API key policy (budget/rate-limit/schedule/endpoint scoping) on the ElevenLabs speech-to-text, text-to-speech and voices proxy routes (#12574) ([#13244](https://github.com/diegosouzapw/OmniRoute/pull/13244)) +- fix(sse): cap HuggingChat NDJSON body size and bound the read loop with the fetch timeout so a stalled or hostile upstream cannot buffer unbounded memory (#12577) ([#13282](https://github.com/diegosouzapw/OmniRoute/pull/13282)) +- fix(docker): scope the cliproxyapi/qdrant/bifrost sidecars to loopback by default and forward `CLIPROXYAPI_MANAGEMENT_KEY` into the cliproxyapi container so its management API is not left both unauthenticated and LAN-published (#12578) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): create DB export temp paths with `fs.mkdtempSync` instead of predictable timestamps (#12579) ([#13237](https://github.com/diegosouzapw/OmniRoute/pull/13237)) +- Cline 401 bodies that say "re-authenticate your Cline account" classify as a refreshable OAuth token, not a terminal expired key. The cooling panel no longer labels every cooldown as a 429; it shows the recorded last error instead. (#12594) ([#13060](https://github.com/diegosouzapw/OmniRoute/pull/13060)) — thanks @HouMinXi +- **fix(catalog):** degrade unknown combo targets instead of dropping LCD modalities, and persist OpenRouter `architecture.input_modalities` into the capability snapshot ([#12613](https://github.com/diegosouzapw/OmniRoute/issues/12613)) ([#13042](https://github.com/diegosouzapw/OmniRoute/pull/13042)) — thanks @HouMinXi +- fix(providers): send `x-api-key` instead of `Authorization: Bearer` for OpenCode Zen's `/v1/responses` endpoint (Muse Spark Contributor models), fixing a 401 on OmniRoute's auth header (#12633) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(sse): surface the actionable "Auggie CLI not found" message when the shell reports a missing `auggie` binary via exit code instead of a spawn error (#12645) ([#13263](https://github.com/diegosouzapw/OmniRoute/pull/13263)) +- **fix(config):** Persist deletions of built-in background-degradation entries — when a stored settings record exists its `degradationMap` is now authoritative instead of being merged under the defaults, so an entry the user removed in the dashboard no longer reappears on the next apply or restart ([#12424](https://github.com/diegosouzapw/OmniRoute/issues/12424)) ([#12647](https://github.com/diegosouzapw/OmniRoute/pull/12647)) — thanks @pacocartones +- **fix(gamification):** action-count badge milestones (First Token, Token Consumer, Token Machine, Token Whale, and the token-sharing tier) are now backed by a durable `xp_action_counts` counter incremented in `addXp()`, instead of a live `COUNT(*)` over `xp_audit_log`. The audit log is pruned by `retention.xpAuditLog` (default 30 days), so on a default install those "lifetime" milestones were really "actions in the last 30 days" and unlocked badges could stop unlocking once old rows aged out. `getActionCount()` and `checkActionCountBadges()` now read the same durable source, and a migration backfills existing totals from the surviving audit rows ([#12546](https://github.com/diegosouzapw/OmniRoute/issues/12546)) ([#12651](https://github.com/diegosouzapw/OmniRoute/pull/12651)) — thanks @pacocartones +- **fix(images):** `/v1/images/edits` now iterates a combo's targets the same way `/v1/images/generations` does (#9239) instead of flattening a bare combo to its first target. A combo whose first target is not edit-capable — or lacks credentials — now falls through to a later edit-capable target rather than hard-erroring, and missing credentials are skipped (not a hard `401`) to match the generations path. The per-target skip/terminal classification is extracted into a shared `runImageComboTargets` loop, so generations behavior is unchanged ([#12547](https://github.com/diegosouzapw/OmniRoute/issues/12547)). ([#12653](https://github.com/diegosouzapw/OmniRoute/pull/12653)) — thanks @pacocartones +- fix(sse): add first-byte watchdog to the TLS-fingerprint transport so a stalled wreq body falls back instead of hanging for minutes (#12656) ([#13272](https://github.com/diegosouzapw/OmniRoute/pull/13272)) +- fix(sse): exempt tiny-budget reasoning probes from combo quality failure and surface persisted-cooldown skips in ALL_TARGETS_SKIPPED diagnostics (#12659) ([#13269](https://github.com/diegosouzapw/OmniRoute/pull/13269)) +- fix(models): declare the real ~1M contextLength for OpenCode Zen's Muse Spark 1.2 models instead of falling back to the 200000 provider default (#12681) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(oauth): align codebuddy-cn OAuth User-Agent with the chat/usage CLI version to avoid WAF false positives (#12702) ([#13264](https://github.com/diegosouzapw/OmniRoute/pull/13264)) +- fix(dashboard): surface an authentication-required banner instead of silently blanking database settings for a guest session (#12709) ([#13270](https://github.com/diegosouzapw/OmniRoute/pull/13270)) +- **fix(resilience):** stop unbounded queue that hangs 6min until Aborted — gate, provider slot, and Bottleneck queue now share a per-connection `maxWaitMs` budget; `fail-closed` on exhaust (503); execution backstop `executionMaxWaitMs` overridable per connection with upstream clamp ([#12715](https://github.com/diegosouzapw/OmniRoute/pull/12715)) — thanks @maxmad64bis (with thanks to @Tushar49 for surfacing the slow-provider need in #12635) +- **fix(auto-combo):** every mode pack now carries `quality` and `reliability` weights (`quality` 0.02, 0.03 in `quality-first`; `reliability` 0.03, 0.04 in `reliability-first`), so selecting a pack no longer silences either signal; `DEFAULT_WEIGHTS` is unchanged ([#12731](https://github.com/diegosouzapw/OmniRoute/pull/12731)) — thanks @maxmad64bis +- **fix(sse):** the provider execution pipeline keeps the upstream error `code`/`type` again — both were lost when the non-streaming leg was extracted, so a config-class `422` (Antigravity missing project) degraded into a generic account cooldown instead of reaching the gate that recognises it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(db):** a legacy `call_logs` table no longer aborts startup — the provider-stats index is created after the column healing runs, not before it, so an install predating `request_type` opens instead of failing with `no such column` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(catalog):** a malformed row in the operator-writable custom-models blob no longer kills every `auto/*` pool ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(tests):** realign the guards that the same day's merges left asserting the old behavior — the provider-count total after GreenPT and EURouter, the GLM stream buffer slot that `#12925` turned into a declared parameter, and the injection scan bound that `#13104` reshaped from a head-only window into head-plus-tail ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- fix(cache): fold tool_choice/tools/response_format into the semantic cache signature so a cached tool_calls response can no longer be replayed for a request whose tool policy forbids it (#12734) ([#13267](https://github.com/diegosouzapw/OmniRoute/pull/13267)) +- **fix(models):** stop treating provider-supplied `isFree:true`/`:free`/`0/0` flags as trusted unless the provider has a documented free tier — only the shipped free-tier catalog decides otherwise; fetched `isFree:true` trusted only for a free-tier provider, custom `isFree:true` via a trusted path ([#12744](https://github.com/diegosouzapw/OmniRoute/pull/12744)) — thanks @maxmad64bis +- fix(memory): authenticate the internal /v1/rerank loopback call so memory reranking no longer silently degrades to unranked order when REQUIRE_API_KEY=true (#12745) ([#13268](https://github.com/diegosouzapw/OmniRoute/pull/13268)) +- fix(sse): parse Ollama Cloud's current usage markup (`$X of $Y used` aria-label, nested width style) (#12749) ([#13260](https://github.com/diegosouzapw/OmniRoute/pull/13260)) +- fix(cli): setup-opencode no longer sends an active context's management token to `/v1/models` when `--api-key`/`OMNIROUTE_API_KEY` is supplied — an explicit flag or the env var now always outranks the context's token, and the flag itself is no longer swallowed by the parent program's global `--api-key` option (#12783) ([#13246](https://github.com/diegosouzapw/OmniRoute/pull/13246)) +- fix(sse): register Arcee AI in the executor provider registry so requests reach api.arcee.ai instead of silently falling back to OpenAI (#12784) ([#13277](https://github.com/diegosouzapw/OmniRoute/pull/13277)) +- **fix(quota):** share one upstream quota read when identical saturation checks arrive at the same time, so traffic bursts don't multiply provider API calls ([#12787](https://github.com/diegosouzapw/OmniRoute/pull/12787)) — thanks @maxmad64bis +- **fix(db):** ignore expired or invalid rate-limit cooldown writes so a stale timestamp can't lock a connection that should be usable — clearing still works as before ([#12788](https://github.com/diegosouzapw/OmniRoute/pull/12788)) — thanks @maxmad64bis +- **fix(providers):** single source for provider order with `xao` ranking alongside `xai-oauth` ([#12790](https://github.com/diegosouzapw/OmniRoute/pull/12790)) — thanks @maxmad64bis +- **fix(routing):** off-table models check the free-model catalog before inheriting premium prices, latency bootstraps from the observed pool median, async tiers read live database pricing with a 90-day freshness gate, and the tier cache invalidates on every pricing write ([#12792](https://github.com/diegosouzapw/OmniRoute/pull/12792)) — thanks @maxmad64bis +- **fix(auto-combo):** snapshot scoring uses the observed per-connection account tier and per-model quality instead of neutral constants, and circuit-open providers rank lower at build time ([#12794](https://github.com/diegosouzapw/OmniRoute/pull/12794)) — thanks @maxmad64bis +- **fix(pool):** empty `auto/*` pools now say why they are empty, and the models listing uses the same paid check as routing ([#12795](https://github.com/diegosouzapw/OmniRoute/pull/12795)) — thanks @maxmad64bis +- fix(routing): recognize CLIProxyAPI's 'unknown provider for model' 400 as fallback-worthy (#12800) ([#13284](https://github.com/diegosouzapw/OmniRoute/pull/13284)) +- **fix(sse):** translate-mode streams now emit the estimated token counts as a trailing usage-only chunk before `[DONE]` when the upstream stays silent, so metered chat clients see totals instead of nothing ([#12828](https://github.com/diegosouzapw/OmniRoute/pull/12828)) — thanks @maxmad64bis +- **fix(db):** provider stats stay truthful on empty and legacy databases: fallback counts default to `0` instead of `null`, latency averages read `null` (not `0`) when no durations were recorded, failures logged before the error-type column existed group under `pre_migration` instead of `unclassified`, and per-provider queries use two new composite indexes ([#12832](https://github.com/diegosouzapw/OmniRoute/pull/12832)) — thanks @maxmad64bis +- fix(nvidia): fail open when a synced model catalog goes stale instead of gating forever (#12849) ([#13248](https://github.com/diegosouzapw/OmniRoute/pull/13248)) +- **fix(db):** WAL maintenance lives in its own module: a `TRUNCATE` checkpoint that hits a busy database now warns with its streak and retries once via `PASSIVE` instead of logging success, and closing-time checkpoints no longer report success on builds without a database file, with the busy totals visible in the authenticated monitoring health payload ([#12853](https://github.com/diegosouzapw/OmniRoute/pull/12853)) — thanks @maxmad64bis +- **fix(health):** quota with no snapshots reads empty instead of a contradicting 0% ([#12857](https://github.com/diegosouzapw/OmniRoute/pull/12857)) — thanks @maxmad64bis +- fix(a2a): accept the dashboard's own session cookie on /a2a so "Run message/send" no longer fails with "Unauthorized: missing or invalid API key" (#12888) ([#13271](https://github.com/diegosouzapw/OmniRoute/pull/13271)) +- **fix(logs):** the Logs grid's in-memory filter pass no longer discards rows the SQL query already matched — selecting an API key from the dropdown (which sends the key's id) returns its calls again, the Combo tab shows every combo instead of only those whose name contains a "1", and the model filter and search cover the same columns as the query ([#12896](https://github.com/diegosouzapw/OmniRoute/pull/12896)) — fixes [#12873](https://github.com/diegosouzapw/OmniRoute/issues/12873) — thanks @ntdatt812 +- **fix(a2a):** `/api/a2a/status` now builds the agent card from the request that asked for it, so a gateway reached at a non-localhost host no longer advertises `http://localhost:20128` as its A2A URL ([#12918](https://github.com/diegosouzapw/OmniRoute/pull/12918)). — thanks @ntdatt812 +- **fix(compression):** progressive aging now appends its `[COMPRESSED:aging:…]` annotation after a turn's `tool_result` blocks instead of in front of them, so Anthropic no longer rejects aged conversations with "`tool_use` ids were found without `tool_result` blocks immediately after" ([#12920](https://github.com/diegosouzapw/OmniRoute/pull/12920)). — thanks @ntdatt812 +- **fix(bedrock):** model import now resolves context limits for every vendor prefix instead of only `anthropic.*`, so `global.openai.gpt-5.6-*` no longer imports with a null `inputTokenLimit` and gets rejected pre-flight at the 200k default ([#12921](https://github.com/diegosouzapw/OmniRoute/pull/12921)). — thanks @ntdatt812 +- **fix(stream):** the 64 KB stream buffer GLM asks for is honoured instead of dropped, and the type error it caused no longer fails the API Route Typecheck gate on every open PR ([#12925](https://github.com/diegosouzapw/OmniRoute/pull/12925)) — thanks @ntdatt812 +- **fix(guardrails):** mask PII inside a `tool_result`'s nested content array, which the masker walked past while redacting its sibling block ([#12930](https://github.com/diegosouzapw/OmniRoute/pull/12930)) — thanks @ntdatt812 +- **fix(dashboard):** the Radar catalog table no longer leaves absent data unexplained — empty limits, unknown context windows, and unreported capabilities each explain themselves on hover, and a new check keeps it that way ([#12937](https://github.com/diegosouzapw/OmniRoute/pull/12937)) — thanks @maxmad64bis +- **fix(sse):** geo-blocked opencode requests rotate to the next account proxy instead of failing, so one refused egress no longer aborts the whole chain ([#12941](https://github.com/diegosouzapw/OmniRoute/pull/12941)) — thanks @maxmad64bis +- **fix(codex):** dashboard "clear cooldown" and the CAS recovery path now drop nested `codexScopeRateLimitedUntil` maps in the same write that nulls `rate_limited_until`, and fresh quota snapshots with headroom lift fallback-sourced scope cooldowns parked by quota preflight ([#12817](https://github.com/diegosouzapw/OmniRoute/issues/12817), [#12860](https://github.com/diegosouzapw/OmniRoute/issues/12860), [#12951](https://github.com/diegosouzapw/OmniRoute/pull/12951)) The dashboard reset-credit button (`consumeCodexResetCredit`) now exercises that same snapshot path after a successful redeem, so a filled quota bar is enough to unpark a leftover fallback Codex child without another manual clear. — thanks @HouMinXi +- **fix(db):** resolve `sql-wasm.wasm` across global npm install and hoisted layouts, ensuring OmniRoute can boot cleanly on Node 24 when native `better-sqlite3` is uncompiled. ([#13035](https://github.com/diegosouzapw/OmniRoute/pull/13035)) — thanks @HouMinXi +- **fix(api):** `DELETE /v1/batches/delete-completed` now sweeps only the calling API key's own completed batches (batches with no owner stay out of a key-scoped sweep on purpose), with an explicit instance-wide mode reserved for authenticated dashboard sessions, a 401 for a presented key that is unknown, revoked, deactivated, banned or expired (never falling through to the session branch), audit logging of every sweep, a sanitized 500 on failure, an owner-scoped file half (a key-scoped sweep never nulls a file another tenant owns) and an atomic sweep — chunked in 200-batch transactions in instance mode — so a mid-way error never leaves a batch pointing at a nulled file (GHSA-wvxc-jp3v-5mg5) ([#12969](https://github.com/diegosouzapw/OmniRoute/pull/12969)) +- **fix(combo):** quota-weighted routing stops drawing on an out-of-credit connection — a 402 now invalidates the stored quota snapshot instead of leaving its stale remaining percentage in place, and a snapshot older than 10 minutes no longer counts as confident headroom for the primary pool ([#12972](https://github.com/diegosouzapw/OmniRoute/pull/12972)) ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- **fix(vision):** Custom Models with "Vision capable" checked no longer have image requests swapped to `glm/glm-4.6v` when the client sends the advertised alias (`vllm/path/...`) or the bare path-shaped id — Vision Bridge now matches the stored override for all three id forms ([#12758](https://github.com/diegosouzapw/OmniRoute/issues/12758)) ([#12974](https://github.com/diegosouzapw/OmniRoute/pull/12974)) — thanks @HouMinXi +- **fix(sse):** transient opencode upstream failures rotate to the next account proxy instead of failing, so one flapping egress no longer aborts the whole chain ([#12975](https://github.com/diegosouzapw/OmniRoute/pull/12975)) — thanks @maxmad64bis +- **fix(azure):** Deployments from GPT-6 onward now send `max_completion_tokens` instead of `max_tokens`, which Azure rejects with HTTP 400. The rule matched a literal `gpt-5`, so each new generation arrived broken; it now matches the generation range, while `gpt-35-turbo` still keeps `max_tokens`. ([#13007](https://github.com/diegosouzapw/OmniRoute/pull/13007)) — thanks @ntdatt812 +- **fix(db):** add an opt-in self-restart circuit for sustained critical memory pressure, gate post-cleanup VACUUM behind a minimum freed-rows threshold, and checkpoint the SQLite WAL every 5 minutes with a size guard that escalates to TRUNCATE, so a growing WAL can no longer stall the event loop into a full outage. ([#13011](https://github.com/diegosouzapw/OmniRoute/pull/13011)) — thanks @HouMinXi +- **fix(auth):** an explicit connection pin may probe a quota-disabled row once and re-enable it on success ([#12874](https://github.com/diegosouzapw/OmniRoute/issues/12874)) ([#13017](https://github.com/diegosouzapw/OmniRoute/pull/13017)) — thanks @HouMinXi +- **fix(api):** thread `X-OmniRoute-Fallback-Attempts` through combo chat completions so streaming and non-streaming responses report how many prior legs were attempted ([#13038](https://github.com/diegosouzapw/OmniRoute/pull/13038)) — thanks @HouMinXi +- Restore provider failure classification and credential refresh on non-streaming requests: classify non-2xx failures to lock models on per-model quota exhaustion, update connection rate limits from headers and body, and pass credential refresh handlers to pipeline execution so 401 tokens can be refreshed and retried (#13043). ([#13069](https://github.com/diegosouzapw/OmniRoute/pull/13069)) — thanks @HouMinXi +- **fix(responses):** wrap forced-non-streaming web_search fallback JSON as Responses SSE so Codex still sees `response.completed` ([#13050](https://github.com/diegosouzapw/OmniRoute/pull/13050)) — thanks @HouMinXi +- **fix(antigravity):** Preserve upstream thought-token usage in normalized completion and reasoning token counts ([#13055](https://github.com/diegosouzapw/OmniRoute/pull/13055)) — thanks @pacocartones +- Fix a concurrency-slot leak in the DeepSeek PoW solver: a worker that failed to spawn (for example a missing worker script) never released its slot, so `MAX_CONCURRENT_WORKERS` failures disabled the solver until restart. ([#13097](https://github.com/diegosouzapw/OmniRoute/pull/13097)) — thanks @anhtahaylove +- **fix(acp):** bound the ACP session output buffers — `stdoutBuffer` and `stderrBuffer` now cap at 1 MiB keeping the most recent output behind a visible `[...output truncated...]` marker, and `stderrBuffer` is reset per prompt instead of accumulating for the lifetime of the session. ([#13100](https://github.com/diegosouzapw/OmniRoute/pull/13100)) — thanks @anhtahaylove +- **fix(acp):** release the `stdout`/`exit` listeners and the idle timer that a `sendPrompt` timeout used to leave attached to the `acpManager` singleton, and drop sessions that exited on their own from the session map instead of keeping them forever. ([#13096](https://github.com/diegosouzapw/OmniRoute/pull/13096)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection and PII scanners now read the text a `tool_result` block carries on `content` (string or nested block list), in messages and in system blocks, so tool output is judged by the same rules as user text ([#13101](https://github.com/diegosouzapw/OmniRoute/pull/13101)) — thanks @ntdatt812 +- **fix(gamification):** close the badge notification SSE stream when the request signal is already aborted before the stream starts — a client that disconnects while the route is still awaiting auth used to leave both the 2s unlock poll and the 15s heartbeat running for the lifetime of the process. ([#13106](https://github.com/diegosouzapw/OmniRoute/pull/13106)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection scan now spends its 16 KB budget on both ends of the request instead of the first 16 KB only, so `system`, `instructions`, `query`, `documents` and the newest turns are no longer hidden behind one long message ([#13104](https://github.com/diegosouzapw/OmniRoute/pull/13104)) — thanks @ntdatt812 +- **fix(dashboard):** expose the Volcano Ark console cookie on quota scraping and unwrap connect-error objects so the dashboard shows the upstream message ([#13107](https://github.com/diegosouzapw/OmniRoute/pull/13107)) — thanks @HouMinXi +- **fix(db):** release the `beforeExit`/`SIGINT`/`SIGTERM` handlers when a `node:sqlite` adapter closes, so a closed adapter and its database handle are no longer pinned to `process` for the lifetime of the run — the same treatment #7494 gave the sql.js adapter. ([#13109](https://github.com/diegosouzapw/OmniRoute/pull/13109)) — thanks @anhtahaylove +- **fix(translator):** `contentSchema` and `unevaluatedItems` are now treated as subschema positions by the tool-schema sanitizer, so a truncation placeholder in either is replaced with a permissive schema instead of being forwarded as a string ([#13110](https://github.com/diegosouzapw/OmniRoute/pull/13110)) — thanks @ntdatt812 +- **fix(cli-helper):** clear the `createLogStream` timeout on the abort path — `stop()` aborts the in-flight fetch and returned through the `signal.aborted` branch, which skipped `clearTimeout` and left an armed timer per stopped stream. The stream reader is now also cancelled when the read loop exits early. ([#13114](https://github.com/diegosouzapw/OmniRoute/pull/13114)) — thanks @anhtahaylove +- **fix(docs):** drop TLS-impersonation, MITM-decrypt, supply-chain attestation, and XOR-mask writeups from the public `/docs` catalog and Docker image. Files stay in git for engineers; operators who need them open the repo, not the website. ([#13136](https://github.com/diegosouzapw/OmniRoute/pull/13136)) — thanks @HouMinXi +- **fix(combo):** parse numeric-epoch `rate_limited_until` in the combo cooldown read path ([#13141](https://github.com/diegosouzapw/OmniRoute/pull/13141)) — thanks @maxmad64bis +- **fix(opencode):** both OpenCode plugins now reject a gateway address typed without `http://` at configuration time, instead of publishing every model with an api url no client can call, and the v2 plugin no longer publishes a model card whose api url is blank or relative ([#13142](https://github.com/diegosouzapw/OmniRoute/pull/13142)) — thanks @maxmad64bis +- **fix(providers):** lock opencode model on upstream 400 model-unavailable ([#13146](https://github.com/diegosouzapw/OmniRoute/pull/13146)) — thanks @maxmad64bis +- **fix(logging):** keep the provider exchange rather than the raw client bodies when a call log exceeds its size budget, and show that recovered payload in the request-detail panel instead of replacing it with the stored response body ([#13147](https://github.com/diegosouzapw/OmniRoute/pull/13147)) — thanks @maxmad64bis +- **fix(traffic-inspector):** the WebSocket route no longer leaks a traffic-buffer subscriber and a 30s ping timer when the client socket is already closed at handler time — listeners are attached before any resource is acquired, a destroyed socket bails out early, and the ping interval stops on a dead socket where `write()` never throws ([#13155](https://github.com/diegosouzapw/OmniRoute/pull/13155)) — thanks @anhtahaylove +- **fix(telegram):** bound the per-user API key cache in the Telegram chat proxy so a burst of distinct chat ids can no longer grow the process heap without limit ([#13165](https://github.com/diegosouzapw/OmniRoute/issues/13165)) ([#13166](https://github.com/diegosouzapw/OmniRoute/pull/13166)) — thanks @anhtahaylove +- **fix(stream):** cancel the upstream response body when the JSON-to-SSE sniff unwinds on a body timeout, so a stalled upstream no longer pins the connection ([#13169](https://github.com/diegosouzapw/OmniRoute/issues/13169)) ([#13171](https://github.com/diegosouzapw/OmniRoute/pull/13171)) — thanks @anhtahaylove +- **fix(telegram):** authenticate webhook deliveries with Telegram's `secret_token` so an unauthenticated caller can no longer mint API keys or spend upstream quota ([#13172](https://github.com/diegosouzapw/OmniRoute/issues/13172)) ([#13175](https://github.com/diegosouzapw/OmniRoute/pull/13175)) — thanks @anhtahaylove +- **fix(models):** give discoverable Gemini 3.8 Flash ids their own 65536 output spec so Antigravity no longer clamps them to 16384 ([#13195](https://github.com/diegosouzapw/OmniRoute/pull/13195)) — thanks @HouMinXi +- **fix(dashboard):** leftover catalog-removed provider rows (gemini-cli) can be listed and purged from the providers page ([#13067](https://github.com/diegosouzapw/OmniRoute/issues/13067)) ([#13197](https://github.com/diegosouzapw/OmniRoute/pull/13197)) — thanks @HouMinXi +- **fix(auth):** a dashboard session now requires the `authenticated: true` claim that login, OIDC and the session refresh already emit — a JWT merely signed with `JWT_SECRET` (for example the Cursor CLI passthrough token, which any API-key holder can obtain) no longer verifies as the `auth_token` cookie on any route, the WebSocket handshake or the live server; existing sessions keep working ([#13298](https://github.com/diegosouzapw/OmniRoute/issues/13298)) ([#13375](https://github.com/diegosouzapw/OmniRoute/pull/13375)) +- **fix(api):** `DELETE /v1/batches/delete-completed` now applies the caller's API-key policy (endpoint allowlist, schedule, usage cap, rate limit) like every other `/v1` route, commits the key-scoped sweep per 200-batch chunk instead of holding one write lock for the whole sweep, refuses to loop without progress, and rejects a scope that names both a key and `allTenants` ([#13684](https://github.com/diegosouzapw/OmniRoute/pull/13684)) +- Restore the API-route typecheck gate: the non-streaming leg result lost its discriminated-union narrowing after the server-owned tool loop reassignment, producing 13 new TS2339 diagnostics in `chatCore.ts`. ([#12963](https://github.com/diegosouzapw/OmniRoute/pull/12963)) +- Restore four non-streaming behaviours the server-owned tool loop refactor dropped: client aborts map to 499 with the fixed `Request aborted` message, an aborted request no longer logs a synthetic `clientResponse`, Claude prompt-cache telemetry is recorded again, and a body that cannot be canonicalized no longer throws when the tool loop is off. ([#12990](https://github.com/diegosouzapw/OmniRoute/pull/12990)) +- **fix(skills):** The CLI registry parser now reads positionals declared with `.addArgument()`, not only those written inline in `.command()`. `tunnel create [type]` was being published as `tunnel create`, so the agent-skills sync gate reported drift on every branch and regenerating would have deleted the argument. ([#13009](https://github.com/diegosouzapw/OmniRoute/pull/13009)) — thanks @ntdatt812 +- **fix(combos):** dashboard combo test uses a short prompt, serial probes, and a 60s timeout so reasoning models and rate-limited free pools do not fail the health check ([#13001](https://github.com/diegosouzapw/OmniRoute/pull/13001)) — thanks @HouMinXi +- fix(compression): terminate idle worker threads on eviction so long-running instances stop leaking OS threads and MessagePorts ([#13091](https://github.com/diegosouzapw/OmniRoute/pull/13091)) — thanks @anhtahaylove +- Redact Google API keys of any length in error bodies: the pattern required exactly 39 characters, so shorter or longer `AIza…` credentials passed through unredacted. ([#12964](https://github.com/diegosouzapw/OmniRoute/pull/12964)) +- **fix(grok-cli):** a 402 "Grok Build usage balance exhausted" parks that Grok login as out of credit (Grok Build CLI, grok.com cookie, and xAI OAuth share the weekly pool). Combo routing then tries the next login instead of locking the model for every account in the pool ([#13061](https://github.com/diegosouzapw/OmniRoute/pull/13061)) — thanks @HouMinXi +- fix(compression): spawn the LLMLingua worker with a file URL object so compression actually runs instead of silently failing open on Node ([#13093](https://github.com/diegosouzapw/OmniRoute/pull/13093)) — thanks @anhtahaylove +- **fix(test):** run the local `test` and `test:unit` scripts at concurrency 4 so a full-suite run no longer exhausts the machine's commit charge and kills unrelated processes ([#13187](https://github.com/diegosouzapw/OmniRoute/pull/13187)) — thanks @anhtahaylove +- **fix(monitoring):** `GET /api/monitoring/health` `credentialHealth` now includes a bounded `failedConnections` list (`connectionId`, `status`, sanitized `lastError`) when the probe-cache gauge `failed>0`, plus `source: probe-cache` and a cheap `staleDbNonOkCount` for sticky SQLite `test_status` on active rows. Documents that the live gauge is not `provider_connections.test_status`. ([#12876](https://github.com/diegosouzapw/OmniRoute/pull/12876)) — thanks @RaviTharuma +- **fix(resilience):** an openai-compatible multi-upstream gateway no longer marks the whole connection `credits_exhausted` when a single upstream (or the `/models` / representative-model health probe) returns 402. Quota failures stay model-scoped; true connection-wide auth failures are unchanged. Single-credential 402 key disable ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239)) is preserved. ([#12875](https://github.com/diegosouzapw/OmniRoute/pull/12875)) — thanks @RaviTharuma +- **fix(dashboard):** nine fixes on the Orchestration Canvas, all diagnosed in the Phase 2 reviews. A source that fails now keeps the timestamp of its FIRST failure instead of being re-stamped every poll — the stale line said "since the last poll" no matter how long the source had been down, and the churn also defeated the snapshot's stable identity (it serializes the source list), so the canvas re-rendered on every tick while anything was broken. A source that HAD data and only then started failing is flagged too: previously only a source with no node at all got the warning, so a source that went down mid-session kept a healthy-looking node forever. The rest are pointwise: clicking a filter chip cancels the pending search debounce (left armed, it fired ~300ms later and silently reverted the chip); the search input carries an accessible name; `?state=running, failed` parses like the unpadded form instead of dropping the padded value; the CSV toggle helper is defined once in `model/urlParams.ts` rather than twice; the agents tab tells "nothing running" apart from "the filter matched nothing", offering a clear-filters button instead of setup links that would be wrong advice there; edges stop emitting SMIL particles above 40 simultaneously active edges, keeping the colored stroke; and the drawer's error banner clears when a retried action succeeds. ([#12983](https://github.com/diegosouzapw/OmniRoute/pull/12983)) +- **fix(i18n):** the nine locales added with the EU-language batch (Greek, Estonian, Irish, Croatian, Lithuanian, Latvian, Maltese, Slovenian, Serbian) were missing the eleven Orchestration Canvas keys that Phase 3 introduced, so the compare-runs panel and the "no runs match these filters" empty state fell back to English in those languages (`deepMergeFallback` substitutes English for an absent key, so nothing rendered blank — it rendered untranslated). The coverage gate does not catch this: it enforces an 80% floor per locale, and eleven missing keys out of ~13,000 leaves coverage at 99.9%. Translated for real in each language, calibrated against the wording each file already uses for "run", "filter" and "skill". ([#13294](https://github.com/diegosouzapw/OmniRoute/pull/13294)) +- **fix(dashboard):** five follow-ups from the Phase 2 Orchestration Canvas review. The History drawer now shows the memory section for runs that already left the live TTL window: the persisted `memory_hits` event is parsed into `metadata.memoryHits` with the same defensive validation the drawer applies, and — because that event is observability rather than a state transition — it no longer leaks into the timeline, where it had been inheriting the task's state and rendering as a duplicate transition. The A2A memory recall runs against its own 1.5s deadline instead of inheriting the memory backend's 30s one; overshooting degrades exactly like any other recall failure (no hits, task proceeds), and the timer is cleared on both paths. Repeating a Conductor run carries its `requirements.cli`/`requirements.model` forward, so the new run is pinned to the same runner profile and model rather than drifting to whatever the fleet picks. A successful repeat from the Agents tab now focuses the run it created, instead of leaving the operator on the finished one. And the auth test for `POST /api/conductor/tasks` moved into the shared `ROUTES` array rather than restating the pattern. ([#12988](https://github.com/diegosouzapw/OmniRoute/pull/12988)) +- fix(plugins): stop leaking an exit listener per plugin hook timeout, which triggered MaxListenersExceededWarning on plugins that ignore SIGTERM ([#13092](https://github.com/diegosouzapw/OmniRoute/pull/13092)) — thanks @anhtahaylove +- **fix(validation):** Provider node edits no longer fail with a generic "Invalid request" when the optional daily-quota reset fields are left blank. The dashboard sends `dailyQuotaResetTimezone` and `dailyQuotaResetHour` as `null`, and only the hour accepted it. ([#13066](https://github.com/diegosouzapw/OmniRoute/issues/13066)) ([#13083](https://github.com/diegosouzapw/OmniRoute/pull/13083)) — thanks @ntdatt812 +- **fix(combo):** quota-aware expansion drops banned, inactive, missing, and wrong-provider connections before quota fetch or model dispatch; pins and allowlists stay selectors, not a bypass. Antigravity automatic exhaustion now requires a reported zero remaining, so a positive balance below 1% stays eligible. ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- Fix an intermittent failure in the quota-weighted routing test suite: scores are a function of `Date.now()`, so peers with identical quota scored microseconds apart never tied and swapped order. ([#12946](https://github.com/diegosouzapw/OmniRoute/pull/12946)) +- fix(volcengine): resolve quota window mapping gaps, safe multi-connection console binding, and response cookie sanitization ([#12950](https://github.com/diegosouzapw/OmniRoute/pull/12950)) — thanks @HouMinXi +- **fix(test):** resolve the WebDAV handler path with `fileURLToPath` so the suite's 37 WebDAV tests run on Windows instead of failing with a doubled `C:\C:\` drive prefix ([#13196](https://github.com/diegosouzapw/OmniRoute/pull/13196)) — thanks @anhtahaylove +- **fix(ui):** fall back from invalid AnySearch icon alias (direct commit `9587e07b69`) +- **fix(sse):** stop rebuilding a truncated summary from the collector's cap-dropped event array ([#12718](https://github.com/diegosouzapw/OmniRoute/pull/12718)) — thanks @hartmark +- **fix(conversations):** show a pending spinner for unresolved tool nodes instead of "(empty)" ([#12727](https://github.com/diegosouzapw/OmniRoute/pull/12727)) — thanks @hartmark +- **fix(streaming):** fail fast when an upstream stream produces only lifecycle/heartbeat events, never real content ([#12741](https://github.com/diegosouzapw/OmniRoute/pull/12741)) — thanks @hartmark +- **fix(responses-continuation):** bridge the write-in-flight window with an in-memory pending store ([#12854](https://github.com/diegosouzapw/OmniRoute/pull/12854)) — thanks @hartmark +- **fix(translator):** drop or map agent_message on Chat Completions fallback ([#12880](https://github.com/diegosouzapw/OmniRoute/pull/12880)) — thanks @RaviTharuma +- **fix(api):** page and stream-complete GET /v1/models for large catalogs ([#12882](https://github.com/diegosouzapw/OmniRoute/pull/12882)) — thanks @RaviTharuma +- **fix(quota):** align AUTH window labels with usage API and clarify 503 ([#12884](https://github.com/diegosouzapw/OmniRoute/pull/12884)) — thanks @RaviTharuma +- **fix(ci):** clear two base-reds on release/v3.8.51 — mutation-coverage gate + image-only-model guard ([#12945](https://github.com/diegosouzapw/OmniRoute/pull/12945)) +- **fix(api):** scope batch bulk-delete to the calling API key ([#13211](https://github.com/diegosouzapw/OmniRoute/pull/13211)) +- **fix(tests):** align model-sync log assertion to shipped path redaction (base-red) ([#13227](https://github.com/diegosouzapw/OmniRoute/pull/13227)) +- **fix(test):** make the ToS heading guard actually require the parentheses ([#13228](https://github.com/diegosouzapw/OmniRoute/pull/13228)) +- **fix(quality):** register 4 drifted covering tests in stryker tap.testFiles ([#13229](https://github.com/diegosouzapw/OmniRoute/pull/13229)) +- **fix(logging):** mask *-api-key headers in request-log pipeline (#13273) ([#13401](https://github.com/diegosouzapw/OmniRoute/pull/13401)) — thanks @KooshaPari +- **fix(tests):** correct stale Jina catalog prefix in custom-models test ([#13403](https://github.com/diegosouzapw/OmniRoute/pull/13403)) — thanks @KooshaPari +- **fix(backend):** prune old backups after health-check-repair snapshots (#13308) ([#13404](https://github.com/diegosouzapw/OmniRoute/pull/13404)) — thanks @KooshaPari +- **fix(backend):** guard memory_fts_au trigger with WHEN clause to stop FTS bloat (#13326) ([#13405](https://github.com/diegosouzapw/OmniRoute/pull/13405)) — thanks @KooshaPari +- **fix(free-tiers):** remove obsolete Together $25 signup credit (#12526) ([#13407](https://github.com/diegosouzapw/OmniRoute/pull/13407)) — thanks @KooshaPari +- **fix:** skip non-chat generation models in Test all models ([#13409](https://github.com/diegosouzapw/OmniRoute/pull/13409)) — thanks @KooshaPari +- **fix(providers):** de-duplicate agy and antigravity model catalogs ([#13410](https://github.com/diegosouzapw/OmniRoute/pull/13410)) — thanks @KooshaPari +- **fix(dashboard):** send provider in body for mini-playground web search ([#13411](https://github.com/diegosouzapw/OmniRoute/pull/13411)) — thanks @KooshaPari +- **fix(resilience):** add CJK quota-exhaustion patterns to 429 classifier ([#13412](https://github.com/diegosouzapw/OmniRoute/pull/13412)) — thanks @KooshaPari +- **fix(backend):** eval runner: failed calls no longer score as passed + dotAll regex ([#13413](https://github.com/diegosouzapw/OmniRoute/pull/13413)) — thanks @KooshaPari +- **fix(backend):** eval runner opts out of output-style and memory injection ([#13414](https://github.com/diegosouzapw/OmniRoute/pull/13414)) — thanks @KooshaPari +- **fix:** chain serial quarantine step in npm test script (fixes #13286) ([#13418](https://github.com/diegosouzapw/OmniRoute/pull/13418)) — thanks @KooshaPari +- **fix(antigravity):** derive stable session ID from credentials for prompt-cache reuse (#13305) ([#13424](https://github.com/diegosouzapw/OmniRoute/pull/13424)) — thanks @KooshaPari +- **fix(memory):** merge memory into top-level system field instead of unshifting at messages[0] (#13425) ([#13427](https://github.com/diegosouzapw/OmniRoute/pull/13427)) — thanks @KooshaPari +- **fix(compression):** preserve instruction blocks from lossy engines ([#13523](https://github.com/diegosouzapw/OmniRoute/pull/13523)) — thanks @KooshaPari +- **fix(compression):** preserve polarity words and newlines in ultra heuristic ([#13524](https://github.com/diegosouzapw/OmniRoute/pull/13524)) — thanks @KooshaPari +- **fix(compression):** run user preservePatterns before built-in patterns (#13457) ([#13525](https://github.com/diegosouzapw/OmniRoute/pull/13525)) — thanks @KooshaPari +- **fix(reasoning):** expand deepseek-v4 regex to match v4.1+ and add command-code provider (#13430) ([#13528](https://github.com/diegosouzapw/OmniRoute/pull/13528)) — thanks @KooshaPari +- **fix(providers):** add alwaysReasons metadata for reka-flash-3 ([#13531](https://github.com/diegosouzapw/OmniRoute/pull/13531)) — thanks @KooshaPari +- **fix(playground):** allow Compare columns to scroll independently ([#13532](https://github.com/diegosouzapw/OmniRoute/pull/13532)) — thanks @KooshaPari +- **fix(security):** empty whitelist allows through to prevent admin lockout ([#13534](https://github.com/diegosouzapw/OmniRoute/pull/13534)) — thanks @KooshaPari +- **fix(tests):** add DISABLE_IOREG_STRATEGY env var for macOS test compatibility (fixes #13467) ([#13539](https://github.com/diegosouzapw/OmniRoute/pull/13539)) — thanks @KooshaPari +- **fix(zed-hosted):** map developer role and Gemini enums for Zed proxy ([#13543](https://github.com/diegosouzapw/OmniRoute/pull/13543)) — thanks @KooshaPari +- **fix(analytics):** normalize provider aliases to canonical id in usage_history ([#13545](https://github.com/diegosouzapw/OmniRoute/pull/13545)) — thanks @KooshaPari +- **fix(combo):** use traceId for call log id to prevent UNIQUE constraint failure ([#13546](https://github.com/diegosouzapw/OmniRoute/pull/13546)) — thanks @KooshaPari +- **fix(auth):** check model lockout before returning synthetic noauth connection ([#13547](https://github.com/diegosouzapw/OmniRoute/pull/13547)) — thanks @KooshaPari +- **fix(guardrails):** skip credential redaction for base64 image data URLs ([#13550](https://github.com/diegosouzapw/OmniRoute/pull/13550)) — thanks @KooshaPari +- **fix(resilience):** increase requestQueue.maxWaitMs default from 15s to 30s ([#13553](https://github.com/diegosouzapw/OmniRoute/pull/13553)) — thanks @KooshaPari +- **fix(docs):** document OMNIROUTE_READY_TIMEOUT_MS and allowlist the test-only DISABLE_IOREG_STRATEGY ([#13692](https://github.com/diegosouzapw/OmniRoute/pull/13692)) + ### 📝 Maintenance ---- +- **docs(readme):** reconcile live v3.8.50 provider, free-tier, CLI, routing, test, community, sponsor, acknowledgment, and SVG metrics with their audited source denominators, including a deduplicated OmniRoute-in-Action snapshot and distinct contributor rankings for merged pull requests, GitHub-attributed commits, and Git history ([#11356](https://github.com/diegosouzapw/OmniRoute/pull/11356)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **docs:** sync the canonical provider count 354 → 356 across `README.md`, `AGENTS.md`, `llm.txt` (+ 42 i18n mirrors), the four README SVG diagrams, `docs/reference/PROVIDER_REFERENCE.md` (regenerated) and the `package.json` description after Opper (#11629) and 1min.ai (#11631) boarded the catalog — closes the `check:docs-counts-sync` strict drifts that kept `release/v3.8.51` red ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11673](https://github.com/diegosouzapw/OmniRoute/pull/11673)) — thanks @jonlwheat2-gif +- **test(providers):** update count-derived assertions after the v3.8.51 provider additions — `APIKEY_PROVIDERS` 233 → 235 (Opper #11629 + 1min.ai #11631), reserved-prefix REGISTRY walk 395 → 398, `WEB_FETCH_PROVIDERS` now includes `nimble-search` (#11620), and the provider translate-path golden snapshot regenerated ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11674](https://github.com/diegosouzapw/OmniRoute/pull/11674)) — thanks @jonlwheat2-gif +- **test(sse):** bump the hard-lease connection-query inventory for `src/lib/tokenHealthCheck.ts` to 2 — the verify-only web-cookie sweep added by #11495 split the single `getProviderConnections` call into oauth + cookie variants, which the frozen inventory had not tracked ([#11495](https://github.com/diegosouzapw/OmniRoute/pull/11495)). ([#11677](https://github.com/diegosouzapw/OmniRoute/pull/11677)) — thanks @jonlwheat2-gif / @oyi77 +- **chore(release):** clear the `release/v3.8.51` base-red gates — docs migration count (+38 i18n mirrors), stryker `tap.testFiles`, lockfile registry host, stale ESLint suppressions, the 7 lint regressions they were hiding, the stale `APIKEY_PROVIDERS` partition count (231 → 233), the `glm-5.3-max` tier missing from the GLM test table, a dead `modelType === "chat"` comparison in the models catalog, and the `resolveLiveWsUrl` / `sanitizeLiveWsPort` helpers from #11388 that a later merge dropped from `wsPath.ts` (dashboard live WebSocket URL resolution) ([#11502](https://github.com/diegosouzapw/OmniRoute/pull/11502)) — thanks @MumuTW +- **chore(free-models):** the free-tier regime table now declares which totals figure each regime feeds and whether it may take the no-auth shortcut, so the sets that used to repeat those answers by hand are derived from it — a new `freeType` no longer compiles until it has answered every question, instead of silently contributing to no total ([#11537](https://github.com/diegosouzapw/OmniRoute/pull/11537)) — thanks @maxmad64bis +- **docs(routing):** LKGP strategy copy now states provider-level stickiness and the explicit rules fallback instead of promising per-target pinning ([#11555](https://github.com/diegosouzapw/OmniRoute/pull/11555)) — thanks @maxmad64bis +- **test(chatcore):** pin the response tool-name alias resolution against the ordering hazard that broke every Gemini/Antigravity MCP tool call in v3.8.49. `extractRequestToolIdentityMap` deletes `translatedBody._toolNameMap`, so the later read is always undefined and the ledger survives only through the `requestToolIdentityMap` fallback — removing that fallback previously left the entire tool-name suite green. The resolution moves into `resolveResponseToolNameMap()` next to the map it depends on, with a regression guard that fails without the recovery. No behaviour change. ([#11575](https://github.com/diegosouzapw/OmniRoute/pull/11575)) — thanks @amartinawi +- **docs(env):** document `OMNIROUTE_CORPUS_CACHE_SIZE` in `.env.example` and `ENVIRONMENT.md`, so the env/docs contract check stops failing Docs Gates on every branch ([#11576](https://github.com/diegosouzapw/OmniRoute/pull/11576)) — thanks @ntdatt812 +- **chore(stryker):** register `route-guard-tunnel-processes-local-only` and `universal-quota-aware-routing` in `tap.testFiles`, so their mutant kills count for the three modules they cover ([#11577](https://github.com/diegosouzapw/OmniRoute/pull/11577)) — thanks @ntdatt812 +- **test(authz):** pin `LOCAL_ONLY_API_GET_EXEMPTIONS` by exact membership instead of by entry count, so the guard names the offending path and also catches a substitution ([#11580](https://github.com/diegosouzapw/OmniRoute/pull/11580)) — thanks @ntdatt812 +- **test(build):** pin `resolveNextBuildBundlerFlag` to the `OMNIROUTE_USE_TURBOPACK` contract it implements, across runtimes, instead of asserting an unimplemented Bun override ([#11583](https://github.com/diegosouzapw/OmniRoute/pull/11583)) — thanks @ntdatt812 +- **test(claude-web):** the Claude Web transport test's barrel anchors no longer pin the exact construction spelling. #11421 made the executor map lazy, so `/"claude-web": new ClaudeWebExecutor\(\)/` stopped matching and the test went red on `release/v3.8.51` itself — a base-red gate every open PR inherited. The anchors now match the alias key and the class name and tolerate either spelling in between, and were checked to still fail when an alias is genuinely repointed at another executor ([#11585](https://github.com/diegosouzapw/OmniRoute/pull/11585)) — thanks @ntdat812 +- **test(models):** pin `qwen3.8-max` resolution per provider — two of the four catalogs now serve the bare id and no longer carry `-preview` ([#11590](https://github.com/diegosouzapw/OmniRoute/pull/11590)) — thanks @ntdatt812 +- **test(executors):** drop the stale 128-tool truncation assertion that contradicted the #11444 pass-through contract ([#11591](https://github.com/diegosouzapw/OmniRoute/pull/11591)) — thanks @ntdatt812 +- **test(token-health):** pin the current terminal-skip boundary — expired with retry budget is probed, `account_deactivated` stays skipped ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)) — thanks @ntdatt812 +- **test(models):** follow #11503's retarget of the `gemini-3-pro-high` alias to the hyphenated catalog id ([#11593](https://github.com/diegosouzapw/OmniRoute/pull/11593)) — thanks @ntdatt812 +- **refactor(video bridge):** extract per-video acquisition, whole-result caching, description, and metrics/abort/cleanup out of `VideoBridgeGuardrail.preCall` into a `processVideoPart` seam in a new `videoBridgePipeline.ts`, behind explicit `VideoMediaBrokerPort`, `VideoAudioTranscriptionPort`, and `VideoDrilldownPort` boundaries; `preCall` now only handles request traversal, policy, and response aggregation. The Video tab's FFmpeg/ffprobe runtime status is now an explicit `unknown` / `restricted` / `unavailable` / `available` state instead of a nullable boolean pair, fixing a case where an in-flight or failed probe was mislabeled as "install FFmpeg" ([#11657](https://github.com/diegosouzapw/OmniRoute/issues/11657)). ([#12016](https://github.com/diegosouzapw/OmniRoute/pull/12016)) +- **test(opencode-plugin):** add unit test coverage for `formatFreeBudget()` naming helper ([#11660](https://github.com/diegosouzapw/OmniRoute/pull/11660)) — thanks @f9td56dbgh-hub +- **docs(video):** clarify that the Video Bridge transcript `source` field (`client`, `embedded`, `audio-bridge`) is presently caller-declared and not yet server-verified — OmniRoute enforces the enum shape but does not cryptographically confirm that an `embedded`/`audio-bridge` label came from a server-owned extraction ([#11661](https://github.com/diegosouzapw/OmniRoute/issues/11661)). ([#12001](https://github.com/diegosouzapw/OmniRoute/pull/12001)) +- **chore(providers):** retire the distributed Raycast Relay and Hailuo Web integrations after a provenance audit confirmed substantial derivation from GPL-3.0 sources; the official GitHub and MiniMax API providers remain available. Migration 163 disables legacy connections and invalidates their active leases while preserving connection IDs, audit/history rows, quota snapshots, usage records, and fail-closed API-key connection policies. ([#11691](https://github.com/diegosouzapw/OmniRoute/pull/11691)) +- **chore(providers):** retire the Felo Web provider while its GPL-derived provenance remains on licensing HOLD ([#11698](https://github.com/diegosouzapw/OmniRoute/pull/11698)) — stale runtime and database references now fail closed while existing connection, lease, and usage history remains preserved for auditability. +- **fix(sse):** retire the Gemini Web image-generation bridge while provenance and licensing remain on HOLD, preserving Gemini Web chat/auth/tools and legitimate Gemini/Nano Banana image providers ([#11708](https://github.com/diegosouzapw/OmniRoute/pull/11708)) +- **chore(audio):** retire the built-in EdgeTTS provider and its 11-voice catalog while provenance/licensing review remains on HOLD; the generic `/v1/audio/speech` endpoint and the other speech providers remain available ([#11711](https://github.com/diegosouzapw/OmniRoute/pull/11711)) +- **fix(providers):** retire the unofficial Qwen Web integration and its `qwen-web`/`qw` identities while third-party provenance remains on HOLD ([#11713](https://github.com/diegosouzapw/OmniRoute/pull/11713)) — direct requests now fail closed with a sanitized HTTP 410 and combos skip the retired target, while migration guards preserve connection IDs, allowlists, usage/audit history and invalidate active leases. Supported Qwen API, Qwen Code, cloud, token-plan, audio, local and model-name surfaces remain available; restoring Qwen Web requires a qualified provenance/license decision. +- **fix(providers):** retire `microsoft-designer-web` and its `msdesigner` alias from runtime and catalogs while preserving stored credentials, provider history and allowlists as inactive records — source dependency on gpt4free's [`MicrosoftDesigner.py`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/g4f/Provider/needs_auth/MicrosoftDesigner.py) is proven at snapshot `0943d16eef757a44deb60651c07bbe5fa958cafe`, whose [`LICENSE`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/LICENSE) is GPL-3.0; expressive adaptation is probable, which records provenance/licensing risk without asserting a legal conclusion ([#11720](https://github.com/diegosouzapw/OmniRoute/pull/11720)). +- **docs(legal):** centralize pinned MIT notices for the vendored GCF codec and local flag assets ([#11726](https://github.com/diegosouzapw/OmniRoute/pull/11726)) +- Split the 2,952-line `adobeFireflyClient.ts` into nine focused modules (catalog, credentials, payload, ARP session, upload, responses, IMS, account, poll) behind an unchanged barrel — no consumer edits and no public API change (#11728). — thanks @MumuTW +- **fix(dashboard):** replaced the Model Lockout card's unprovenanced notification MP3 with a short, locally synthesized Web Audio chime ([#11731](https://github.com/diegosouzapw/OmniRoute/pull/11731)) — both toggles retain optional audible feedback without shipping replacement media or third-party code, and unsupported, suspended, or rejected audio contexts fail open without blocking the setting change. +- **chore(deepseek-web):** remove the provenance-unresolved DeepSeek PoW WASM binary and its runtime loader/tracing while retaining the existing JavaScript solver (slower at high difficulty) ([#11732](https://github.com/diegosouzapw/OmniRoute/pull/11732)). +- **chore(assets):** remove four unused provider assets whose redistribution provenance could not be verified while retaining the active Freebuff SVG ([#11735](https://github.com/diegosouzapw/OmniRoute/pull/11735)) +- **docs(assets):** retired two raster images whose redistribution provenance could not be verified ([#11736](https://github.com/diegosouzapw/OmniRoute/pull/11736)) — PicoClaw documentation now reuses the existing SVG, while Zoo Code remains a text link without an image. +- **docs(legal):** add pinned notices for six LobeHub-derived provider SVGs and 65 byte-exact theSVG assets, retaining registry claims as unverified metadata with explicit trademark and HOLD boundaries ([#11737](https://github.com/diegosouzapw/OmniRoute/pull/11737)). +- **chore(quality):** added a versioned 225/225 provider-asset provenance manifest and blocking drift gate ([#11743](https://github.com/diegosouzapw/OmniRoute/pull/11743)) — the inventory pins magic MIME and SHA-256 for every physical file, records immutable source matches for 71 assets, keeps 69 probable and 85 unresolved entries explicit but non-blocking, and treats provenance as source evidence rather than copyright or trademark clearance. +- **refactor(mitm):** replace four previously derived Traffic Inspector utilities — conversation normalization, SSE merging, secret masking, and header sanitization — with independent clean-room implementations based on public provider schemas, WHATWG SSE, and RFC 6750, while keeping the two independently classified passthrough surfaces unchanged ([#11748](https://github.com/diegosouzapw/OmniRoute/pull/11748)). +- Added a unit test that fails seven days before any dated pack under `config/` (`validUntil` and sibling keys) lapses, naming the file and key. The Alibaba free-tier pack expired on 2026-08-27 and turned every PR red the next morning with no commit involved; renewal now happens on someone's terms, not the clock's. ([#11891](https://github.com/diegosouzapw/OmniRoute/pull/11891)) +- `check:workflows` now fails (under `--strict`/`--ratchet`) when any job routed to a self-hosted runner publishes with `--provenance` — npm rejects that with `422` at the registry, which in v3.8.50 only surfaced after the tag and Docker images were public. ([#11895](https://github.com/diegosouzapw/OmniRoute/pull/11895)) +- `scripts/ops/runner-janitor.sh` now proves a path is idle with one `lsof` snapshot and removes stale leftovers itself (tmpfs after 3 h — it is RAM — disk after 24 h), kills orphan `next-build` processes, prunes checkouts of stopped runners, and alerts on memory pressure; `--dry-run` shows exactly what it would do. `docs/ops/RUNNER_BOX.md` reconciled to the measured box (31 GB, 10 listeners). ([#11893](https://github.com/diegosouzapw/OmniRoute/pull/11893)) +- The `next-build` artefact (1.3 GB) is now written and read under `$RUNNER_TEMP` (per-runner, on disk) instead of `/tmp`, which on the self-hosted pool is a 12 GB tmpfs in RAM. Landing it there took 27–32 of the publish job's 76 minutes, and the fixed `/tmp/e2e-build.tar.gz` name let E2E jobs on different runners overwrite each other's download. ([#11896](https://github.com/diegosouzapw/OmniRoute/pull/11896)) +- The CI `build` job now runs in two concurrency lanes — `main` and pull requests — so a release build is never queued behind (or OOM-killed beside) PR builds on the self-hosted pool, which holds one `next-build` comfortably and two at the edge. ([#11901](https://github.com/diegosouzapw/OmniRoute/pull/11901)) +- Re-freeze the ESLint suppressions on `release/v3.8.51` from a clean-room measurement (2 stale file entries pruned, 55 pre-existing `no-explicit-any` in six new files frozen under #11924) and drop the dead `GPT_SIZE_MAP` constant orphaned by the Adobe Firefly client split, so `No new ESLint warnings` stops failing every PR with exit 2 (Refs #11924) ([#11955](https://github.com/diegosouzapw/OmniRoute/pull/11955)) +- Type the 55 `no-explicit-any` sites that had been frozen under #11924 — four redundant casts in `socksConnectorWithFamily.ts` (undici/socks types already accept them) and the mocks/fixtures of the socks-timeout and isFree suites — and drop their suppression entries; the ESLint ratchet shrinks from 5487 to 5432 (Closes #11924) ([#11975](https://github.com/diegosouzapw/OmniRoute/pull/11975)) +- Take the two hosted-runner builds off the PR rail: `Build App` (`build.yml`) is `workflow_dispatch`-only and `DAST smoke (PR)` runs only for PRs into `main` — the 7 GB hosted VM cannot build `release/v3.8.51` in any profile (19/30 red, VM shutdown ~8 min into `next build`) and both checks had turned into permanent noise on every release PR; the bundle stays validated by `ci.yml` on `main` and by `nightly-release-green` on `release/**` (Closes #11946) ([#11962](https://github.com/diegosouzapw/OmniRoute/pull/11962)) +- Move the four nightly jobs that build the backend (`nightly-schemathesis`, `nightly-llm-security` promptfoo + garak, `nightly-resilience` axe-a11y) off the hosted 7 GB runner — where they died on `release/v3.8.51` unseen — onto the box's new `omni-light` pool (two listeners), and document the reshaped fleet (4 active OmniRoute listeners: 2 `omni-build` + 2 `omni-light`, janitor ceiling 4) (Closes #11965) ([#11967](https://github.com/diegosouzapw/OmniRoute/pull/11967)) +- Stop painting every fork PR into `release/**` red: `quality.yml` `Build (advisory)` is skipped (GitHub still reports `continue-on-error` failures as check FAILURE). Hosted `ubuntu-latest` cannot finish `npm run build` on this tree — same class as #11962 taking `build.yml` off the PR rail. `docker-publish.yml` amd64 now runs on the `.113` `omni-build` pool (31 GB / 32 cores, two listeners) with Turbopack, shares the `heavy-build-main` lane with `ci.yml` `Build` so it queues instead of becoming a third heavy, and keeps per-ref concurrency (a merge storm was starting 8 concurrent OOM builds). arm64 stays on `ubuntu-24.04-arm` with webpack — there is no ARM box. Fallback when `USE_VPS_RUNNER` is off: hosted amd64 + webpack (#11976). ([#12021](https://github.com/diegosouzapw/OmniRoute/pull/12021)) +- `docker-publish.yml` builds with webpack (`OMNIROUTE_USE_TURBOPACK=0`) on **both** arches, including amd64 on the `.113` `omni-build` pool. Turbopack had the RAM (31 GB, `omniroute-113-6`) and still panicked (`TurbopackInternalError: there must be a path to a root`, run 33253576569, 26 min in). The same tree's arm64 webpack build on hosted `ubuntu-24.04-arm` succeeded (run 33264823398). Dockerfile already documents webpack as the Docker escape hatch (#11976). ([#12050](https://github.com/diegosouzapw/OmniRoute/pull/12050)) +- **docs(free-tier):** declare the counting vs deciding regimes for "is it free?" and guard the deciding path from DB-backed catalog resolution ([#12226](https://github.com/diegosouzapw/OmniRoute/pull/12226)) — thanks @maxmad64bis +- **docs(auto-combo):** The mode pack table in `docs/routing/AUTO-COMBO.md` now lists all six shipped packs with every weight each one sets, replacing a four-pack table whose numbers had also drifted from the source. It states plainly that no pack sets `quality`, so selecting any pack silences the observed-quality signal. Six more documents that quote the scoring factor count joined the `check:docs-counts` gate, which caught five stale claims — including one naming nine factors that do not exist — and two stale mode pack counts. The dashboard routing panel, which offered four of the six packs and labelled the default strategy "6-Factor Scoring", is now covered by a test; the two packs it was missing are `reliability-first` and `chaos-mode`, the latter labelled as the fault-injection profile it is rather than as one more routing preference ([#12316](https://github.com/diegosouzapw/OmniRoute/pull/12316)) — thanks @maxmad64bis +- **chore(quality):** rebaseline `src/lib/db/apiKeys.ts` for the ACL the key-creation path now preserves ([#12352](https://github.com/diegosouzapw/OmniRoute/pull/12352)) ([#12673](https://github.com/diegosouzapw/OmniRoute/pull/12673)) — thanks @KrzysiekSko +- **chore(providers):** bump the Claude Code wire identity and the Devin bridge image pin from `2.1.220` to `2.1.258` ([#12402](https://github.com/diegosouzapw/OmniRoute/pull/12402)) — thanks @ggiak ([#12604](https://github.com/diegosouzapw/OmniRoute/pull/12604)) +- **docs(env):** align `.env.example`, the README Bun section, and the troubleshooting guide with the code: `OMNIROUTE_USE_TURBOPACK` also governs `npm run build` (not dev-only), `bun run build` follows that flag instead of auto-selecting Webpack, `OMNIROUTE_CHAT_MAX_HEAVY_IN_FLIGHT` is unset by default (no request-count cap), and the structural `503 chat_admission_busy` message matches `chatAdmissionResponses.ts` (#12404 — thanks @pacocartones) +- **chore(electron):** upgrade the desktop app to Electron 44 (Chromium 152, Node 24.18.1) ([#12217](https://github.com/diegosouzapw/OmniRoute/pull/12217)). **Requires macOS 13 (Ventura) or later** — Chromium dropped macOS 12 (Monterey), so Monterey users must stay on an earlier OmniRoute desktop build. Windows and Linux are unaffected; the app already shipped only x64/arm64, so Electron 44 dropping 32-bit builds changes nothing. Removes the `openAsHidden`/`wasOpenedAsHidden` login-item fields deleted in Electron 44 — hidden autostart continues to work through the `--hidden` argument registered with the login item ([#12554](https://github.com/diegosouzapw/OmniRoute/pull/12554)) — thanks @app/dependabot +- **chore(quality):** rebaseline `src/sse/handlers/chat.ts` for the effective-input persistence the continuation fix needs ([#12641](https://github.com/diegosouzapw/OmniRoute/pull/12641)) ([#12680](https://github.com/diegosouzapw/OmniRoute/pull/12680)) — thanks @hartmark +- `Coverage` job on `ci.yml`: the informational Codecov upload gets its own 5-minute ceiling and `continue-on-error`, and the job budget grows from 20 to 30 minutes (the 8-shard c8 merge alone takes ~10) — a stalled upload no longer ends the job `cancelled` and drags a fully green `main` run's conclusion down with it ([#11972](https://github.com/diegosouzapw/OmniRoute/pull/11972)) +- CI hardening on the PR rail: the four `Unit Tests fast-path` shards get `timeout-minutes: 30` (a hung shard held a PR for 64 min instead of GitHub's 6 h default) and both ESLint file caches lose their `restore-keys` fallback, so a cache built under another suppressions file or lint config can no longer report stale verdicts (Refs #11600, #11924) ([#11963](https://github.com/diegosouzapw/OmniRoute/pull/11963)) +- Every CI job that runs a `next build` (`build`, the npm `publish`, both release-green validations) now targets the `omni-build` runner label, which only two of the eight self-hosted runners carry. The box holds one build comfortably and two at the edge; a third now queues on GitHub instead of being OOM-killed by the kernel. ([#11932](https://github.com/diegosouzapw/OmniRoute/pull/11932)) +- **refactor(combo):** move `handleRoundRobinCombo` (and `resolveTargetTokenLimit`) into `open-sse/services/combo/roundRobinCombo.ts`. `combo.ts` drops from 2164 to 1014 lines (`split("\n").length`); the leaf is 1199 (under the 1200 new-file cap). The round-robin call site uses a dynamic `import()` so `releaseStickyPinOnFailure` / `clearStaleLKGP` can stay exported from `combo.ts` without a static cycle. Skip / sticky / semaphore / safety-timer behavior is unchanged. ([#12811](https://github.com/diegosouzapw/OmniRoute/pull/12811)) — thanks @HouMinXi +- **chore(quality):** rebaseline the file-size caps the error-boundary campaign grew past (`open-sse/executors/codex.ts`, `open-sse/vendor/codex-chatgpt-web/bridge.ts`, both via [#12444](https://github.com/diegosouzapw/OmniRoute/pull/12444)) ([#12654](https://github.com/diegosouzapw/OmniRoute/pull/12654)) +- **chore(quality):** rebaseline the file-size caps the hartmark batch grew past (`combos/page.tsx` via [#12355](https://github.com/diegosouzapw/OmniRoute/pull/12355), `open-sse/services/combo.ts` via [#12338](https://github.com/diegosouzapw/OmniRoute/pull/12338)) ([#12623](https://github.com/diegosouzapw/OmniRoute/pull/12623)) — thanks @hartmark +- **chore(quality):** rebaseline the file-size caps the HouMinXi batch grew past when its PRs stacked (`providers/page.tsx`, `chatCore.ts`, `accountFallback.ts`) — each PR measured correctly in isolation, none saw the stacking ([#12619](https://github.com/diegosouzapw/OmniRoute/pull/12619)) +- **chore(quality):** rebaseline `open-sse/services/combo.ts` for the reset-aware scoring the HouMinXi batch stacked ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) ([#12678](https://github.com/diegosouzapw/OmniRoute/pull/12678)) — thanks @HouMinXi +- **docs:** sync provider catalog count (direct commit `4f11b2ae3d`) +- **docs(skills):** sync omni-inference search fallback (direct commit `b4e76a7ed9`) +- **chore(lint):** prune two stale base suppressions (direct commit `1325047d56`) +- **test(auto-combo):** tolerate full-suite rotation load (direct commit `ad3e293f9f`) +- **test:** drain inherited quality snapshot reds (direct commit `21ed68d8ac`) +- **chore(quality):** rebaseline measured CLI bundle growth (direct commit `d35b3f9779`) +- **test(ui):** reproduce invalid AnySearch icon alias (direct commit `12cc6ca834`) +- **test(translator):** fix the relative imports of the relocated deferred-finish test (direct commit `777d9d1629`) +- **docs(counts):** bring the documented provider and migration counts to the measured values (direct commit `e70bea30e9`) +- **test(security):** drop the guard-target set left unused by the sync (direct commit `a32ba0e362`) +- **chore(db):** renumber model_capabilities migration 163 → 169 (cross-branch collision with 163_radar_feed_cache_generated_at) (direct commit `70f5d4cbf9`) +- **docs(cli):** document CLI_PRIME_AGENT_BIN and correct the CLI Agents count (direct commit `9058e39b61`) +- **chore(deps):** refresh runtimes and adopt ESLint 10 ([#11259](https://github.com/diegosouzapw/OmniRoute/pull/11259)) — thanks @backryun +- **refactor(local-corpus):** implement dynamic root resolution and LRU cache ([#11491](https://github.com/diegosouzapw/OmniRoute/pull/11491)) — thanks @raheemuddin786 +- **chore(quality):** prune stale eslint-suppressions entries ([#11596](https://github.com/diegosouzapw/OmniRoute/pull/11596)) +- **chore(ci):** unblock quality gates - env-doc sync + stryker tap.testFiles registration (#9282) ([#11609](https://github.com/diegosouzapw/OmniRoute/pull/11609)) — thanks @Meet6338-X +- **chore(ci):** point the circular-deps gate at dpdm's real JS entrypoint ([#11615](https://github.com/diegosouzapw/OmniRoute/pull/11615)) — thanks @benzntech +- **chore(quality):** rebaseline file-size for #11448/#11495/#11561 growth ([#11630](https://github.com/diegosouzapw/OmniRoute/pull/11630)) +- **deps:** realign onnxruntime-node pin to @huggingface/transformers' 1.24.3 lockstep ([#11633](https://github.com/diegosouzapw/OmniRoute/pull/11633)) — thanks @jonlwheat2-gif +- **test(autoCombo):** port models_dev_tier #11508 guard from node:test to vitest ([#11635](https://github.com/diegosouzapw/OmniRoute/pull/11635)) — thanks @jonlwheat2-gif +- **chore(quality):** rebaseline file-size for #11083/#11631/#11628 growth ([#11636](https://github.com/diegosouzapw/OmniRoute/pull/11636)) +- **chore(lint):** prune stale ESLint suppressions (base-red #11449) ([#11644](https://github.com/diegosouzapw/OmniRoute/pull/11644)) — thanks @jonlwheat2-gif +- **docs(changelog):** aggregate the ten v3.8.50 fragments into CHANGELOG.md ([#11683](https://github.com/diegosouzapw/OmniRoute/pull/11683)) +- **chore(search):** drop unreachable credential sweep after #11642 ([#11687](https://github.com/diegosouzapw/OmniRoute/pull/11687)) — thanks @HouMinXi +- **ci:** add API route TypeScript regression gate ([#11705](https://github.com/diegosouzapw/OmniRoute/pull/11705)) — thanks @Sabeekhann +- **docs(dedup):** correct the CodeQL claim on the tenant-namespace comment ([#11712](https://github.com/diegosouzapw/OmniRoute/pull/11712)) +- **docs(changelog):** add consolidated v3.8.50 stats, top-25 ranking and a .mailmap ([#11715](https://github.com/diegosouzapw/OmniRoute/pull/11715)) +- **docs:** recommend gstack for AI-assisted workflows ([#11770](https://github.com/diegosouzapw/OmniRoute/pull/11770)) — thanks @santosraju99-hub +- **chore(quality):** tighten complexity/cognitive-complexity ratchets to the current tip; land the missed gateways.ts rebaseline ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **docs(i18n):** finish Freepik → Magnific rebrand in locale strings and README ([#11772](https://github.com/diegosouzapw/OmniRoute/pull/11772)) — thanks @turbolego +- **refactor(auto-combo):** fix divergent scoring in combo health reporting ([#11854](https://github.com/diegosouzapw/OmniRoute/pull/11854)) — thanks @jacobsparts +- **chore(changelog):** add missing fragments for #11919/#11918/#11916 ([#11938](https://github.com/diegosouzapw/OmniRoute/pull/11938)) +- **test(check):** escape the runs-on fixture with JSON.stringify, not a quote-only replace ([#11942](https://github.com/diegosouzapw/OmniRoute/pull/11942)) (direct commit `fb7445eaa3`) +- **test(release):** align five suites with the contracts #11933, #11919 and #11876 shipped on release/v3.8.51 ([#11944](https://github.com/diegosouzapw/OmniRoute/pull/11944)) +- **chore(quality):** remove unreachable code and restore test discovery ([#11950](https://github.com/diegosouzapw/OmniRoute/pull/11950)) — thanks @backryun +- **docs(agents):** sync-back landings are fast-forward, never squash ([#11964](https://github.com/diegosouzapw/OmniRoute/pull/11964)) +- **test(infra):** retry recursive temp-dir removal instead of failing a shard on ENOTEMPTY (#11966) ([#11968](https://github.com/diegosouzapw/OmniRoute/pull/11968)) +- **docs(ops):** the .113 heavy-build ceiling is one runner, not two ([#12048](https://github.com/diegosouzapw/OmniRoute/pull/12048)) +- **test(ui):** correct inactive auto-fetch expectation ([#12098](https://github.com/diegosouzapw/OmniRoute/pull/12098)) — thanks @RaviTharuma +- **docs:** document native dependency check escape hatch ([#12101](https://github.com/diegosouzapw/OmniRoute/pull/12101)) — thanks @RaviTharuma +- **test(providers):** regenerate the translate-path golden for OrcaRouter (#11923) ([#12118](https://github.com/diegosouzapw/OmniRoute/pull/12118)) +- **chore(quality):** velocity phase — loosen every numeric baseline by 20% until v4.0, monitor headroom nightly ([#12125](https://github.com/diegosouzapw/OmniRoute/pull/12125)) +- **chore(lint):** batch 0 of #12146 — type the call-log-cap sqlite rows instead of 45 as-any casts ([#12157](https://github.com/diegosouzapw/OmniRoute/pull/12157)) +- **chore(lint):** batch 4 of #12146 — shared/components react-hooks violations resolved ([#12159](https://github.com/diegosouzapw/OmniRoute/pull/12159)) +- **chore(lint):** batch 1 of #12146 — dashboard/cli-code react-hooks violations resolved ([#12160](https://github.com/diegosouzapw/OmniRoute/pull/12160)) +- **chore(lint):** batch 3 of #12146 — dashboard/settings react-hooks violations resolved ([#12162](https://github.com/diegosouzapw/OmniRoute/pull/12162)) +- **chore(lint):** batch 2 of #12146 — resolve the react-hooks compiler violations in dashboard/providers ([#12163](https://github.com/diegosouzapw/OmniRoute/pull/12163)) +- **chore(quality):** register search-432 cooldown test in stryker tap.testFiles ([#12170](https://github.com/diegosouzapw/OmniRoute/pull/12170)) +- **chore(lint):** batch 5 of #12146 — combos, endpoint, provider-stats, api-manager and costs react-hooks violations resolved ([#12174](https://github.com/diegosouzapw/OmniRoute/pull/12174)) +- **build:** add contributor fast profile ([#12192](https://github.com/diegosouzapw/OmniRoute/pull/12192)) — thanks @rafacpti23 +- **docs(readme):** README touch-ups ([#12193](https://github.com/diegosouzapw/OmniRoute/pull/12193)) ([#12194](https://github.com/diegosouzapw/OmniRoute/pull/12194)) ([#12202](https://github.com/diegosouzapw/OmniRoute/pull/12202)) — thanks @rafacpti23 +- **build:** force the webpack bundler in the contributor build profile ([#12197](https://github.com/diegosouzapw/OmniRoute/pull/12197)) — thanks @rafacpti23 +- **build:** skip standalone packaging in the contributor profile ([#12198](https://github.com/diegosouzapw/OmniRoute/pull/12198)) — thanks @rafacpti23 +- **docs(audit):** align every published number with the code and harden check:docs-counts ([#12200](https://github.com/diegosouzapw/OmniRoute/pull/12200)) +- **build:** stub the instrumentation entrypoints in the contributor profile ([#12203](https://github.com/diegosouzapw/OmniRoute/pull/12203)) — thanks @rafacpti23 +- **build:** omit the standalone output target for contributor builds ([#12204](https://github.com/diegosouzapw/OmniRoute/pull/12204)) — thanks @rafacpti23 +- **chore(lint):** batch 6 of #12146 — memory, radar, audit, analytics, cache, usage, activity, home and RequestLoggerV2 react-hooks violations resolved ([#12208](https://github.com/diegosouzapw/OmniRoute/pull/12208)) +- **docs(diagrams):** rename number-carrying diagram files to stable names ([#12210](https://github.com/diegosouzapw/OmniRoute/pull/12210)) +- **docs(dashboard):** redraw onboarding tier-flow SVGs for the real 4-tier model ([#12211](https://github.com/diegosouzapw/OmniRoute/pull/12211)) +- **docs(api):** document every implemented route in openapi.yaml (276 → 692 paths) ([#12212](https://github.com/diegosouzapw/OmniRoute/pull/12212)) +- **chore(lint):** batch 7 of #12146 — final src tail: 37 react-hooks violations across 33 files resolved ([#12244](https://github.com/diegosouzapw/OmniRoute/pull/12244)) +- Revert "docs: recommend gstack for AI-assisted workflows (#11770)" ([#12248](https://github.com/diegosouzapw/OmniRoute/pull/12248)) ([#12249](https://github.com/diegosouzapw/OmniRoute/pull/12249)) +- **docs:** Chaos Mode setup guide + weighted strategy semantics ([#12250](https://github.com/diegosouzapw/OmniRoute/pull/12250)) +- **docs(agents):** protected-surface merge rule — operator approval for agent-instruction files ([#12253](https://github.com/diegosouzapw/OmniRoute/pull/12253)) +- **chore(quality):** register native-codex turn-pin tests in stryker tap.testFiles ([#12263](https://github.com/diegosouzapw/OmniRoute/pull/12263)) +- **chore(quality):** dedupe tap.testFiles entries added by racing base-red fixes ([#12265](https://github.com/diegosouzapw/OmniRoute/pull/12265)) +- **docs(readme):** introduce OmniRouteTray — the macOS menu-bar companion ([#12276](https://github.com/diegosouzapw/OmniRoute/pull/12276)) — thanks @ggiak +- **chore(proxy):** purge the legacy 1proxy residue — sync/rotator modules, dead DB exports, dead settings tab and flag, docs (#12091) ([#12290](https://github.com/diegosouzapw/OmniRoute/pull/12290)) +- **chore(quality):** baseline-headroom skips generated and vendored files in the fileSize worst-file signal ([#12291](https://github.com/diegosouzapw/OmniRoute/pull/12291)) +- **refactor(video):** unify the JPEG frame data-URI contract ([#12322](https://github.com/diegosouzapw/OmniRoute/pull/12322)) +- **chore(deps):** freeze onnxruntime-node and eslint-plugin-react-hooks out of dependabot groups ([#12329](https://github.com/diegosouzapw/OmniRoute/pull/12329)) +- **test(executors):** restore the #10986 reasoning-only fallback guards ([#12364](https://github.com/diegosouzapw/OmniRoute/pull/12364)) — thanks @pacocartones +- **chore(quality):** re-tighten the file-size ratchet to the real LOC (plan 3.8.52 task 0) ([#12411](https://github.com/diegosouzapw/OmniRoute/pull/12411)) +- **test(grok):** format web executor suite ([#12412](https://github.com/diegosouzapw/OmniRoute/pull/12412)) +- **chore(quality):** ratchet open-sse typecheck baseline to zero ([#12418](https://github.com/diegosouzapw/OmniRoute/pull/12418)) +- **test(build):** guard the artifact path policy arrays against duplicates ([#12422](https://github.com/diegosouzapw/OmniRoute/pull/12422)) +- **chore(lint):** adopt eslint-plugin-react-hooks 7.1.1 ([#12428](https://github.com/diegosouzapw/OmniRoute/pull/12428)) +- **chore(providers):** remove a keyless provider integration at its operator's request ([#12440](https://github.com/diegosouzapw/OmniRoute/pull/12440)) +- **docs(providers):** register providers removed at their operator's request and guard against reintroduction ([#12478](https://github.com/diegosouzapw/OmniRoute/pull/12478)) +- **chore(quality):** base fixes — stryker tap.testFiles + node_modules cache key ([#12482](https://github.com/diegosouzapw/OmniRoute/pull/12482)) +- **docs(arch):** one-process recipe for tens of long /v1/responses ([#12493](https://github.com/diegosouzapw/OmniRoute/pull/12493)) — thanks @RaviTharuma +- **chore(quality):** tighten the CodeQL ratchet baseline from 11 to 6 ([#12530](https://github.com/diegosouzapw/OmniRoute/pull/12530)) +- **chore(deps):** pin browserslist override to ^4.28.8 ([#12592](https://github.com/diegosouzapw/OmniRoute/pull/12592)) — thanks @KooshaPari +- **chore(deps):** bump fast-uri to 3.1.7 in the electron lockfile ([#12601](https://github.com/diegosouzapw/OmniRoute/pull/12601)) +- **docs(quality):** document how the CodeQL ratchet refreshes and how to tighten it ([#12611](https://github.com/diegosouzapw/OmniRoute/pull/12611)) +- **test(ci):** pin the openapi-security-tiers two-arm contract with an executing gate test (#12581) ([#12652](https://github.com/diegosouzapw/OmniRoute/pull/12652)) +- **docs(gamification):** align XP Rewards table with code (#12501) ([#12667](https://github.com/diegosouzapw/OmniRoute/pull/12667)) — thanks @KooshaPari +- **refactor(combo):** split executeTarget into gates, attempt, and loop ([#12746](https://github.com/diegosouzapw/OmniRoute/pull/12746)) — thanks @HouMinXi +- **docs(security):** document socket.yml scanner config + CI workflow link (#12575) ([#12764](https://github.com/diegosouzapw/OmniRoute/pull/12764)) — thanks @KooshaPari +- **chore(ci):** guard commit identity in pre-commit to stop author misattribution ([#12772](https://github.com/diegosouzapw/OmniRoute/pull/12772)) +- **deps:** 17 Dependabot bumps — bump electron from 43.4.0 to 43.4.1 in /electron ([#11426](https://github.com/diegosouzapw/OmniRoute/pull/11426)); bump the development group across 1 directory with 8 updates ([#11428](https://github.com/diegosouzapw/OmniRoute/pull/11428)); bump the production group across 1 directory with 11 updates ([#11440](https://github.com/diegosouzapw/OmniRoute/pull/11440)); chore(deps): bump github/codeql-action/init from 4.37.7 to 4.37.8 ([#11925](https://github.com/diegosouzapw/OmniRoute/pull/11925)); chore(deps): bump github/codeql-action from 4.37.7 to 4.37.8 ([#11926](https://github.com/diegosouzapw/OmniRoute/pull/11926)); chore(deps): bump github/codeql-action/analyze from 4.37.7 to 4.37.8 ([#11927](https://github.com/diegosouzapw/OmniRoute/pull/11927)); build(deps): bump github/codeql-action/init from 4.37.8 to 4.37.9 ([#12345](https://github.com/diegosouzapw/OmniRoute/pull/12345)); build(deps): bump github/codeql-action/analyze from 4.37.8 to 4.37.9 ([#12346](https://github.com/diegosouzapw/OmniRoute/pull/12346)); bump the development group across 1 directory with 2 updates ([#12347](https://github.com/diegosouzapw/OmniRoute/pull/12347)); build(deps): bump github/codeql-action from 4.37.8 to 4.37.9 ([#12349](https://github.com/diegosouzapw/OmniRoute/pull/12349)); bump browserslist from 4.28.2 to 4.28.8 ([#12396](https://github.com/diegosouzapw/OmniRoute/pull/12396)); bump the production group across 1 directory with 4 updates ([#12399](https://github.com/diegosouzapw/OmniRoute/pull/12399)); bump @xmldom/xmldom ([#12500](https://github.com/diegosouzapw/OmniRoute/pull/12500)); bump qs from 6.15.2 to 6.16.0 ([#12512](https://github.com/diegosouzapw/OmniRoute/pull/12512)); bump @xmldom/xmldom from 0.9.10 to 0.9.12 ([#12513](https://github.com/diegosouzapw/OmniRoute/pull/12513)); bump fast-uri from 3.1.5 to 3.1.7 ([#12514](https://github.com/diegosouzapw/OmniRoute/pull/12514)); bump @humanfs/node from 0.16.7 to 0.16.8 ([#12515](https://github.com/diegosouzapw/OmniRoute/pull/12515)) +- Clear the `release/v3.8.51` `check:agent-skills-sync` base-red: regenerate `skills/cli-tunnel/SKILL.md` so the `tunnel create [type]` positional that #13009 taught the generator to read is reflected in the committed skill. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` typecheck base-red from #12731: the new `Mode pack ranking gates` candidates in `open-sse/services/autoCombo/__tests__/autoCombo.test.ts` omitted the required `provider`, `model` and `errorRate` fields of `ProviderCandidate`, failing `check:open-sse-typecheck` (and with it `Fast Quality Gates`) on every open PR. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` docs-sync base-red: EURouter (#13025) and GreenPT (#13024) took the live provider count to 358, leaving 7 STRICT drifts (`PROVIDER_REFERENCE.md`, 4 diagrams, 2 tier-flow images) plus the `AGENTS.md` / `llm.txt` / `package.json` count claims stale. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- **test(models):** Pin `CATALOG_BUILD_TIMEOUT_MS` in the #12058 canonical-catalog regression so a cold catalog build racing the 8s default bound no longer flakes the suite with a 500 ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Allow `@omniroute/opencode-plugin-v2/` in the npm pack policy — [#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870) shipped the v2 plugin beside its v1 sibling without widening the allowlist, so every packed file under it read as an unexpected artifact and failed `check:pack-policy` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Point the release-green validator's pack gate at the tree it is validating — the build-provenance guard defaulted to `origin/main`, which a release branch tip can never be an ancestor of mid-cycle, so `Package artifact` reported a false red on every run and the tarball boot-smoke cascaded off it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **docs(checks):** keep doc counts honest — headings, rankings, catalog, weights, quality gate and scoring diagram now covered ([#12507](https://github.com/diegosouzapw/OmniRoute/pull/12507)) — thanks @maxmad64bis +- **chore(lifecycle):** `check:model-lifecycle` now also diffs `DEFAULT_DEGRADATION_MAP` (the background-task redirect table) against the vendor lifecycle snapshot, refusing a retired id as source or target, with a table-driven unit test beside it. Three rows whose source the vendor had retired — `claude-sonnet-4-20250514`, `gemini-3-pro-preview` and `gpt-5.1-codex` (whose target `gpt-5.1-codex-mini` is retired too) — were dead code, since `checkLifecycle` answers 410 before the redirect runs; they are dropped (#12535 — thanks @pacocartones) +- **test(compression):** cover idle worker eviction at the resource level — the pool must call `terminate()` and must not retain the worker's `MessagePort`, complementing the `exit`-event assertion added with the fix ([#13371](https://github.com/diegosouzapw/OmniRoute/pull/13371)) +- **chore(quality):** raise the `imageGeneration.ts` file-size ceiling for the image-only-model guard that clears the #12945 base-red ([#13392](https://github.com/diegosouzapw/OmniRoute/pull/13392)) +- **chore(quality):** type the combo-test route's JSON response bodies instead of casting them to `any`, drop the now-empty suppression entry, and remove the `hasPerModelQuota` import `chatCore.ts` stopped using when the failure-classification helper was extracted ([#13379](https://github.com/diegosouzapw/OmniRoute/pull/13379)) +- **chore(electron):** the desktop bundle no longer ships the root-level files of every translated docs mirror (`README.md`, `llm.txt`, `CLAUDE.md`, `GEMINI.md`, `CONTRIBUTING.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`) — the packaged app only reads `docs/i18n//docs/**`, which stays. Saves ~11 MB on top of the translated CHANGELOGs already pruned. (#0000) ([#13235](https://github.com/diegosouzapw/OmniRoute/pull/13235)) +- **chore(quality):** retighten the `src/sse/handlers/chat.ts` unused-vars suppression count to the 8 violations that actually remain ([#13385](https://github.com/diegosouzapw/OmniRoute/pull/13385)) +- Bring the documented counts back in line with the code: 171 migrations (was 169) and 20 routing strategies (was 19), plus the regenerated `cli-tunnel` skill reference. ([#12970](https://github.com/diegosouzapw/OmniRoute/pull/12970)) +- Update the documented migration count to 172 after the call-logs provider-stats indexes landed. ([#13209](https://github.com/diegosouzapw/OmniRoute/pull/13209)) +- **chore(ci):** two gates that close the blind spots behind the exclusions above. `check:vitest-exclusions` requires every Vitest exclusion to name a tracking issue and to appear in `config/quality/vitest-exclusions.json` — the previous list grew to 62 files behind a comment pointing at an issue that had been closed for a month. `check-new-key-coverage` requires a key newly added to `en.json` to reach every locale; the existing coverage gate is a percentage floor per locale, so eleven absent keys out of ~13,000 left it at 99.9% while a whole feature shipped untranslated in nine languages. Both are diff-aware, so pre-existing debt stays frozen and neither needed a migration to turn on. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **build:** root layout no longer loads Inter from `next/font/google`, so a production image build does not need fonts.googleapis.com ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **docs(i18n):** bring the eight new `llm.txt` mirrors (km, kn, ml, my, ne, or, pa, si) up to the current provider and migration counts, so `check-docs-sync` stops rejecting every commit ([#13674](https://github.com/diegosouzapw/OmniRoute/pull/13674)) +- **refactor(ui):** flow surfaces (home topology, combo live studio, compression cockpit/waterfall/nodes, token-health badge) express state through the theme-aware `--orch-status-*` tokens instead of fixed dark-mode hex, so green/red/amber/grey stay legible in the light theme; dark mode is byte-identical. Categorical palettes (routing-strategy hues, compression-layer pills, provider brand colors) deliberately stay hex ([#12378](https://github.com/diegosouzapw/OmniRoute/issues/12378)) ([#13203](https://github.com/diegosouzapw/OmniRoute/pull/13203)) +- **chore(tests):** 51 test files that had been excluded from Vitest are running again, restoring roughly 350 assertions to the blocking suite. Each was measured individually first: of the 62 files parked behind the `// #8618 — pre-existing failure` comment, 51 pass against the current tree with no source change, so the exclusions had outlived the failures they were added for. The 11 that genuinely still fail stay excluded, but now point at a live tracker (#13204) rather than at #8618, which was closed in August while the list it tracked kept growing. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **chore(quality):** drop two ESLint suppression entries whose violations no longer exist, so `eslint --suppressions-location` stops rejecting every commit that touches the surrounding files ([#13384](https://github.com/diegosouzapw/OmniRoute/pull/13384)) +- **chore(release):** bring main's post-tag fixes into release/v3.8.51 — sync-back step 2/2 (direct commit `21c488f210`) +- **chore(deps):** patch toml and esbuild in the opencode plugin lockfiles ([#12965](https://github.com/diegosouzapw/OmniRoute/pull/12965)) +- **docs(changelog):** reconcile the v3.8.51 living section — fold 366 fragments, cover every cycle commit, credit every contributor ([#12971](https://github.com/diegosouzapw/OmniRoute/pull/12971)) +- **test(plugin):** wait for the v2 lazy-refresh tier instead of sleeping past it ([#12984](https://github.com/diegosouzapw/OmniRoute/pull/12984)) +- **chore(quality):** rebaseline chatCore.ts after the non-streaming regression fixes ([#13045](https://github.com/diegosouzapw/OmniRoute/pull/13045)) +- **docs(changelog):** add the missing bullet marker to the DeepSeek PoW fragment ([#13200](https://github.com/diegosouzapw/OmniRoute/pull/13200)) +- **chore(deps):** drain the Dependabot queue — 10 of 13 alerts ([#13213](https://github.com/diegosouzapw/OmniRoute/pull/13213)) +- **chore(ci):** make worktrees carry the hooks that enforce the identity gate ([#13236](https://github.com/diegosouzapw/OmniRoute/pull/13236)) +- **chore(deps):** pin csv-parse to ^7.0.2 via overrides (#13117) ([#13300](https://github.com/diegosouzapw/OmniRoute/pull/13300)) — thanks @KooshaPari +- **chore(deps):** bump hono from ^4.12.34 to ^4.13.7 (#13148) ([#13301](https://github.com/diegosouzapw/OmniRoute/pull/13301)) — thanks @KooshaPari +- **chore(deps):** pin joi to ^18.2.8 via overrides (#13085) ([#13302](https://github.com/diegosouzapw/OmniRoute/pull/13302)) — thanks @KooshaPari +- **docs(providers):** add Microsoft 365 Copilot (BizChat) provider guide (#12779) ([#13340](https://github.com/diegosouzapw/OmniRoute/pull/13340)) — thanks @KooshaPari +### 🙌 Contributors + +Thanks to everyone whose work landed in v3.8.51: + +| Contributor | PRs / Issues | +| --- | --- | +| [@5dive-bot](https://github.com/5dive-bot) | #11852 | +| [@abhisheksharma2411](https://github.com/abhisheksharma2411) | #11547, #11684, #11959, #12209 | +| [@adevwithpurpose](https://github.com/adevwithpurpose) | #11464 | +| [@adivekar-utexas](https://github.com/adivekar-utexas) | #12015, #12043, #12090, #12138 | +| [@AIB1TAL0S](https://github.com/AIB1TAL0S) | #12103 | +| [@alltomatos](https://github.com/alltomatos) | #11448 | +| [@alvinveroy](https://github.com/alvinveroy) | #11857, #12027, #12165 | +| [@amaleta](https://github.com/amaleta) | #11906, #11934 | +| [@amartinawi](https://github.com/amartinawi) | #11575 | +| [@AndrianBalanescu](https://github.com/AndrianBalanescu) | #11083, #11830, #11834, #11844 | +| [@anhtahaylove](https://github.com/anhtahaylove) | #13091, #13092, #13093, #13096, #13097, #13100, #13106, #13109, #13114, #13155, #13166, #13171, #13175, #13187, #13196 | +| [@aniruddhaadak80](https://github.com/aniruddhaadak80) | #11760 | +| [@app](https://github.com/app) | #12554 | +| [@arminanton](https://github.com/arminanton) | #11461, #11513 | +| [@AStupidBear](https://github.com/AStupidBear) | #11584 | +| [@atescivitci-cmd](https://github.com/atescivitci-cmd) | #12105 | +| [@b3nw](https://github.com/b3nw) | #11970, #11971, #12147 | +| [@backryun](https://github.com/backryun) | #11259, #11950, #12075, #12076, #12078, #12079, #12081, #12082, #12181, #12228, #12239, #12255, #12258, #12277, #12367, #12423, #12524, #12538 | +| [@benjaminkitt](https://github.com/benjaminkitt) | #11747 | +| [@benzntech](https://github.com/benzntech) | #11614, #11615 | +| [@Bl0ck154](https://github.com/Bl0ck154) | #11948, #11951, #11952, #11953, #11954 | +| [@brick30llc-ctrl](https://github.com/brick30llc-ctrl) | #11752, #12242, #12262 | +| [@Chewji9875](https://github.com/Chewji9875) | #11455, #11875, #12028 | +| [@cheynetom](https://github.com/cheynetom) | #12058 | +| [@davidebaraldo](https://github.com/davidebaraldo) | #12222, #12304 | +| [@davidlinfr](https://github.com/davidlinfr) | #6390 | +| [@ddarkr](https://github.com/ddarkr) | #12124 | +| [@Deftera186](https://github.com/Deftera186) | #11809 | +| [@dpozimski](https://github.com/dpozimski) | #11945 | +| [@DW-MediaLab](https://github.com/DW-MediaLab) | #12229 | +| [@echel0nn](https://github.com/echel0nn) | #11923 | +| [@f9td56dbgh-hub](https://github.com/f9td56dbgh-hub) | #11660 | +| [@fabioluissilva](https://github.com/fabioluissilva) | #11991 | +| [@foreveryh](https://github.com/foreveryh) | #12177 | +| [@geek007git](https://github.com/geek007git) | #12115, #12116, #12117, #12120, #12122 | +| [@geekyNads](https://github.com/geekyNads) | #11784, #12266 | +| [@ggiak](https://github.com/ggiak) | #11561, #12276, #12350, #12402 | +| [@gonisulaimann](https://github.com/gonisulaimann) | #12368, #12369, #12371 | +| [@Gorillaz322](https://github.com/Gorillaz322) | #12207 | +| [@groovecityJO](https://github.com/groovecityJO) | #12682 | +| [@hartmark](https://github.com/hartmark) | #11434, #11452, #11473, #11499, #11703, #11983, #11984, #11985, #11986, #11988, #11989, #11990, #11994, #12221, #12293, #12445, #12446, #12447, #12448, #12460, #12461, #12623, #12646, #12650, #12680, #12717, #12718, #12727, #12741, #12854 | +| [@hizzt](https://github.com/hizzt) | #11894 | +| [@hongnoul](https://github.com/hongnoul) | #11484 | +| [@HouMinXi](https://github.com/HouMinXi) | #11411, #11414, #11512, #11518, #11520, #11641, #11642, #11643, #11687, #11779, #11849, #11850, #11851, #11915, #11916, #11918, #11919, #11920, #12013, #12017, #12033, #12042, #12106, #12139, #12166, #12169, #12171, #12205, #12213, #12312, #12325, #12487, #12488, #12495, #12504, #12557, #12566, #12590, #12591, #12624, #12626, #12632, #12637, #12678, #12696, #12697, #12711, #12733, #12746, #12767, #12770, #12789, #12803, #12805, #12811, #12866, #12868, #12899, #12926, #12934, #12950, #12951, #12974, #13001, #13006, #13011, #13017, #13026, #13027, #13034, #13035, #13038, #13042, #13050, #13060, #13061, #13069, #13107, #13120, #13136, #13195, #13197 | +| [@Hsia97](https://github.com/Hsia97) | #11624 | +| [@jacobsparts](https://github.com/jacobsparts) | #11854, #12155, #12167 | +| [@joglomedia](https://github.com/joglomedia) | #11980 | +| [@jonlwheat2-gif](https://github.com/jonlwheat2-gif) | #11443, #11450, #11549, #11567, #11608, #11633, #11635, #11640, #11644, #11671, #11672, #11673, #11674, #11675, #11676, #11677, #11706, #11783, #12051, #12052, #12053, #12055 | +| [@kanade-hoshino](https://github.com/kanade-hoshino) | #12180 | +| [@Karan825](https://github.com/Karan825) | #11861 | +| [@KaspaPulse](https://github.com/KaspaPulse) | #11389, #11469, #11628, #11666, #11910 | +| [@keeltrace](https://github.com/keeltrace) | #12080, #12223 | +| [@killer30001000](https://github.com/killer30001000) | #12178 | +| [@KooshaPari](https://github.com/KooshaPari) | #12470, #12592, #12667, #12699, #12703, #12706, #12764, #12769, #12771, #13300, #13301, #13302, #13340, #13401, #13403, #13404, #13405, #13406, #13407, #13409, #13410, #13411, #13412, #13413, #13414, #13418, #13424, #13427, #13433, #13523, #13524, #13525, #13528, #13531, #13532, #13534, #13539, #13543, #13545, #13546, #13547, #13550, #13553 | +| [@kriptoburak](https://github.com/kriptoburak) | #11370 | +| [@KrzysiekSko](https://github.com/KrzysiekSko) | #12425, #12432, #12673 | +| [@marcelokarval](https://github.com/marcelokarval) | direct commit / report | +| [@maxmad64bis](https://github.com/maxmad64bis) | #1622, #11435, #11437, #11441, #11537, #11550, #11553, #11555, #11557, #11812, #11842, #11843, #11903, #12151, #12214, #12215, #12218, #12226, #12314, #12316, #12317, #12318, #12319, #12320, #12321, #12507, #12715, #12731, #12744, #12786, #12787, #12788, #12790, #12792, #12794, #12795, #12828, #12832, #12853, #12857, #12870, #12937, #12941, #12975, #13141, #13142, #13146, #13147 | +| [@mdigitalbh81](https://github.com/mdigitalbh81) | #12240 | +| [@Meet6338-X](https://github.com/Meet6338-X) | #11598, #11609, #12110 | +| [@morpheus9393](https://github.com/morpheus9393) | #11943 | +| [@MumuTW](https://github.com/MumuTW) | #11492, #11502, #11506, #11507, #11626, #11675, #11685, #11728, #11746, #11888, #11889, #11890, #11892 | +| [@Neuron-Mr-White](https://github.com/Neuron-Mr-White) | #11622, #11801, #12256 | +| [@NightStalker-87](https://github.com/NightStalker-87) | #12183 | +| [@NoxzRCW](https://github.com/NoxzRCW) | #11879, #11880, #11881, #11882, #11883, #11935 | +| [@ntdat812](https://github.com/ntdat812) | #11585, #12095 | +| [@ntdatt812](https://github.com/ntdatt812) | #11368, #11573, #11574, #11576, #11577, #11580, #11582, #11583, #11588, #11589, #11590, #11591, #11592, #11593, #11672, #12177, #12180, #12873, #12918, #12920, #12921, #12925, #12930, #13007, #13009, #13024, #13025, #13083, #13087, #13101, #13104, #13110 | +| [@official-burak](https://github.com/official-burak) | #11542 | +| [@opensource-elearning](https://github.com/opensource-elearning) | #12179, #12189, #12278, #12286 | +| [@oyi77](https://github.com/oyi77) | #11408, #11409, #11421, #11505, #11677, #12036, #12110 | +| [@pacocartones](https://github.com/pacocartones) | #11521, #11522, #11527, #11528, #11529, #11530, #11531, #11532, #11533, #11534, #11595, #11599, #11603, #11604, #11605, #11607, #11610, #11676, #11714, #11716, #11718, #11767, #11838, #11860, #11862, #11869, #11871, #11872, #11873, #11903, #11906, #11921, #11934, #12356, #12358, #12359, #12360, #12361, #12362, #12364, #12365, #12373, #12374, #12375, #12376, #12377, #12379, #12380, #12381, #12386, #12387, #12389, #12390, #12394, #12395, #12397, #12401, #12403, #12404, #12522, #12523, #12535, #12536, #12540, #12541, #12543, #12545, #12548, #12549, #12550, #12551, #12552, #12647, #12651, #12653, #13055 | +| [@patrykkopycinski](https://github.com/patrykkopycinski) | #11936, #11937, #12224 | +| [@PauloFH](https://github.com/PauloFH) | #11509 | +| [@PauloHSOliveira](https://github.com/PauloHSOliveira) | #12241 | +| [@ponkcore](https://github.com/ponkcore) | #12054 | +| [@Prajeeth-12](https://github.com/Prajeeth-12) | #11634, #12046 | +| [@quiterunner-commits](https://github.com/quiterunner-commits) | #12131, #12143 | +| [@rafacpti23](https://github.com/rafacpti23) | #11554, #11558, #12192, #12197, #12198, #12202, #12203, #12204 | +| [@ragnar-claude](https://github.com/ragnar-claude) | #11564 | +| [@raheemuddin786](https://github.com/raheemuddin786) | #11491, #11839, #11840, #11841, #12003, #12230, #12231, #12232, #12233, #12234 | +| [@rahilmavani](https://github.com/rahilmavani) | #11761 | +| [@Rahulsharma0810](https://github.com/Rahulsharma0810) | #11771 | +| [@RaviTharuma](https://github.com/RaviTharuma) | #11710, #11727, #11797, #11798, #11802, #11805, #11806, #11811, #12098, #12099, #12101, #12449, #12452, #12472, #12473, #12493, #12533, #12607, #12628, #12631, #12636, #12875, #12876, #12880, #12882, #12884 | +| [@rezjalibd](https://github.com/rezjalibd) | #12186 | +| [@RhianB14](https://github.com/RhianB14) | #11692 | +| [@rifqiawl](https://github.com/rifqiawl) | #11517, #11519, #11969 | +| [@rqzbeh](https://github.com/rqzbeh) | #11390 | +| [@Sabeekhann](https://github.com/Sabeekhann) | #11699, #11705 | +| [@santosraju99-hub](https://github.com/santosraju99-hub) | #11755, #11770 | +| [@Seramicx](https://github.com/Seramicx) | #11568 | +| [@Siva010](https://github.com/Siva010) | #12191 | +| [@solstxce](https://github.com/solstxce) | #11597 | +| [@soroush5](https://github.com/soroush5) | #12691 | +| [@tenshiak](https://github.com/tenshiak) | #12279 | +| [@TheDemonTuan](https://github.com/TheDemonTuan) | #11468, #11470, #11471, #11482, #11548, #11758, #11775, #11814 | +| [@thomasmaerz](https://github.com/thomasmaerz) | #12834 | +| [@tuandinh0801](https://github.com/tuandinh0801) | #11454 | +| [@turbolego](https://github.com/turbolego) | #11621, #11762, #11772, #11774, #11781, #12216 | +| [@Tushar49](https://github.com/Tushar49) | #11392 | +| [@ujjawalkaushik1110](https://github.com/ujjawalkaushik1110) | #11863 | +| [@vermasomesh835](https://github.com/vermasomesh835) | #11794 | +| [@vsd2807](https://github.com/vsd2807) | #11565, #11619 | +| [@wahidsadik371-coder](https://github.com/wahidsadik371-coder) | #12126 | +| [@watchingdogs](https://github.com/watchingdogs) | #12031 | +| [@wildcard](https://github.com/wildcard) | #11620 | +| [@xiaoyaner0201](https://github.com/xiaoyaner0201) | #11460, #11662, #12083 | +| [@Xxx91n](https://github.com/Xxx91n) | #11690 | +| [@yourspraveen](https://github.com/yourspraveen) | #11146 | +| [@yxyxy](https://github.com/yxyxy) | #11671 | +| [@ZaimMarzuki](https://github.com/ZaimMarzuki) | #11960 | +| [@Zartharas](https://github.com/Zartharas) | #11340 | +| [@zero-executioner](https://github.com/zero-executioner) | #11631 | +| [@diegosouzapw](https://github.com/diegosouzapw) | maintainer | + +--- ## [3.8.50] — 2026-08-25 _Living section — regenerated 2026-08-12 from all cycle commits (cycle open `ed2db6cb19` → tip). Bullets carry the merged PR and its author; direct pushes listed separately._ diff --git a/docs/i18n/gu/CHANGELOG.md b/docs/i18n/gu/CHANGELOG.md index 14f4b37491..cffc4c2069 100644 --- a/docs/i18n/gu/CHANGELOG.md +++ b/docs/i18n/gu/CHANGELOG.md @@ -8,16 +8,1119 @@ ## [3.8.51] — TBD -_Living section — cycle opened at the v3.8.50 freeze (parallel-cycle model). Bullets are aggregated from `changelog.d/` fragments at each `/generate-release` phase._ +_Living section — reconciled 2026-09-15 from all cycle commits (`091589089c` → `c0f92ec98a`, 916 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 | **123** | +| 📝 Commits in the cycle | **916** | +| 🔀 Pull requests referenced | **892** | +| 📋 Changelog entries | **919** | +| 🙌 Contributors credited in entries | **121** | +| 🤖 Automated dependency commits | 18 | + +**Entries by type** + +| Type | Count | +| --- | ---: | +| 🐛 Fixes | 592 | +| ✨ Features | 131 | +| 🧹 Chore | 93 | +| 📚 Docs | 42 | +| 🧪 Tests | 32 | +| ♻️ Refactor | 8 | +| 🏗️ Build | 7 | +| ⚡ Performance | 7 | +| ⚙️ CI | 2 | +| ⏪ Reverts | 2 | +| 📦 Dependencies | 2 | +| 🔒 Security | 1 | + +### 🏆 Top 25 contributors this cycle + +_By commits in `091589089c..c0f92ec98a`, author identities consolidated via `.mailmap` and the merged PR's GitHub login. Bots excluded._ + +| # | Contributor | Commits | +| ---: | --- | ---: | +| 🥇 | diegosouzapw | 323 | +| 🥈 | Bob.Hou (@HouMinXi) | 79 | +| 🥉 | Paco Cartones (@pacocartones) | 77 | +| 4 | Dizzle (@maxmad64bis) | 49 | +| 5 | Koosha Paridehpour (@KooshaPari) | 43 | +| 6 | Nguyen Thanh Dat (@ntdatt812) | 31 | +| 7 | Ravi Tharuma (@RaviTharuma) | 26 | +| 8 | Markus Hartung (@hartmark) | 22 | +| 9 | Webman (@jonlwheat2-gif) | 22 | +| 10 | backryun | 17 | +| 11 | anhtahaylove | 15 | +| 12 | Rafa Martins (@rafacpti23) | 10 | +| 13 | Syed Raheemuddin (@raheemuddin786) | 10 | +| 14 | Nguyễn Viết Tuấn (@TheDemonTuan) | 8 | +| 15 | MumuTW | 7 | +| 16 | Paijo (@oyi77) | 7 | +| 17 | Tobias Andersen (@turbolego) | 6 | +| 18 | Bl0ck (@Bl0ck154) | 5 | +| 19 | SHANMUGAPRIYAN (@geek007git) | 5 | +| 20 | KaspaPulse | 5 | +| 21 | Abhishek Sharma (@abhisheksharma2411) | 4 | +| 22 | Abhishek Divekar (@adivekar-utexas) | 4 | +| 23 | Andrew B. (@AndrianBalanescu) | 4 | +| 24 | NoxzRCW | 4 | +| 25 | opensource-elearning | 4 | ### ✨ New Features +- **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). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- Added Google AI Studio Gemini batch text-to-speech support through `POST /v1/audio/speech`. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- 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. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(routing):** subscription-first auto groupings — `auto/subscription` routes only through plan-included connections with a documented hard-stop overage and fails closed on exhaustion, while `auto/thrifty` orders the pool `subscription → keyless → free → cheap → premium` and steps up one rung at a time as each is exhausted. Billing class comes from a curated per-connection catalog (uncurated is treated as metered, never plan-included), both reuse STRICT_ZERO_COST's per-connection verification, and a quota reading whose `resetAt` has passed is now refreshed regardless of TTL so routing returns to plan capacity as soon as it resets ([#11146](https://github.com/diegosouzapw/OmniRoute/pull/11146)) — thanks @yourspraveen +- **feat(providers):** publish a management-authenticated versioned web-session credential contract from OmniRoute's canonical browser credential metadata ([#11340](https://github.com/diegosouzapw/OmniRoute/pull/11340)) — thanks @Zartharas +- **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)) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add Xquik X search with typed results, credential validation, REST routing, and MCP selection ([#11370](https://github.com/diegosouzapw/OmniRoute/pull/11370)) — thanks @kriptoburak +- **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)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(dashboard):** surface durable exclusive managed leases in the existing Sessions view, keeping leased clients visible across idle gaps while marking connections with in-flight work as active ([#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389)) — thanks @KaspaPulse +- **build(bun):** allow Turbopack bundler flag on Bun 1.4+ with configurable Webpack fallback ([#11471](https://github.com/diegosouzapw/OmniRoute/pull/11471)) — thanks @TheDemonTuan +- feat(api): add an opt-in `modelVisibilityAllowlist`/`modelVisibilityDenylist` settings pair to curate exactly which models `/v1/models` advertises, mirrored into every `auto/*` combo candidate pool so a denied model cannot be routed to via combo selection either (#11481) ([#11997](https://github.com/diegosouzapw/OmniRoute/pull/11997)) +- **feat(rankings):** the Free Provider Rankings page shows what each provider actually served over the last 24 h. It ranked by ELO alone, which left a provider that answers every call with an error in first place; the usage data was already served by the API but never requested. A provider with too small a sample shows a dash, not a number ([#11546](https://github.com/diegosouzapw/OmniRoute/pull/11546)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **feat(guardrails):** enforce a bounded, deterministic contract for Video Bridge transcripts — 256 cues, 4096 input code units and 4 KiB UTF-8 per cue, 64 KiB total text, malformed-Unicode rejection, focus-window scoping, cross-source reconciliation with contributing-source metadata, and a structural provenance trust boundary so caller JSON can never self-assert `embedded`/`audio-bridge` provenance ([#11652](https://github.com/diegosouzapw/OmniRoute/issues/11652)) ([#12009](https://github.com/diegosouzapw/OmniRoute/pull/12009)) +- **feat(video):** orchestrate optional Video Bridge audio extraction and Audio Bridge STT behind a dual opt-in (operator setting AND per-request signal) — a new loopback-only broker `mode=audio` operation shares the frame path's exact process queue, deadline, AbortSignal, and byte budgets to extract a bounded mono 16 kHz PCM WAV from the same already-downloaded video, then reuses the existing Audio Bridge transcription boundary; provider segment timing is preserved when available and marked coarse otherwise, and every failure degrades to a visual-only-safe partial instead of throwing (#11654). ([#12012](https://github.com/diegosouzapw/OmniRoute/pull/12012)) +- Add a tenant-bound Video Bridge drill-down lifecycle on top of the existing secure cache substrate: opaque hashed handles (never raw session/video identifiers), preview/standard/detail multiresolution variants resampled on read, response pagination capped at 8 frames and 32 MiB, and a new authenticated `/api/v1/video-bridge/drilldown` consumer route that stays disabled for remote access by default and denies cross-key access with the same response as a nonexistent handle (no existence oracle). ([#12006](https://github.com/diegosouzapw/OmniRoute/pull/12006)) +- **test(video):** Add the Video Bridge FU-07/FU-09 promotion-evidence harness (#11656) — a frozen Zod manifest schema covering the 8 required scenario kinds (static scenes, rapid cuts, late facts, fades, blur, small text, close events, visual prompt injection) with a minimum of 3 repetitions per case, deterministic declarative fixture recipes (`videoBridgePromotionFixtures.ts`), a pure medians/p95 metrics aggregator, a pure FU-07/FU-09 promotion-verdict evaluator applying the ticket's exact thresholds (missing token usage always holds), a digest-only persistence layer that never retains raw media or raw model responses, and a versioned per-model promotion allowlist shipped empty with every model defaulting to `hold`. The FU-07/FU-09 promotion verdicts themselves remain HOLD — they require a real evidence run against real models on VPS 192.168.0.15. ([#12008](https://github.com/diegosouzapw/OmniRoute/pull/12008)) +- **feat(video bridge):** "embedded" transcript provenance can now be legitimately earned instead of merely asserted — a bounded, allowlisted (`mov_text`/`subrip`/`webvtt`) subtitle probe runs through the loopback-only Video Bridge broker (at most 2 streams, 10s subdeadline bounded by the request deadline, 256 KiB output, 4096-code-unit lines), normalized through a bounded, ReDoS-safe WebVTT parser and Zod-validated end to end. The adapter always resolves to an explicit `success`/`absent`/`transient_failure` outcome — a subtitle failure never breaks the visual description path, and only a fingerprint-verified broker response (never a caller-declared label) can produce embedded cues (#11659). ([#12011](https://github.com/diegosouzapw/OmniRoute/pull/12011)) +- Default new Antigravity-family connections (agy CLI imports and Antigravity OAuth connects) to model auto-sync, so live model discovery lands in the synced catalog and `/v1/models` picks up freshly released upstream models (e.g. Gemini 3.7 Flash tiers) without code changes. Existing connections keep their current setting; the per-connection dashboard toggle remains the opt-out. (#11685 — thanks @MumuTW) ([#11887](https://github.com/diegosouzapw/OmniRoute/pull/11887)) +- **feat(ui):** enable React Compiler (`reactCompiler: true` + `babel-plugin-react-compiler`) for automatic memoization at build time ([#11783](https://github.com/diegosouzapw/OmniRoute/pull/11783)) — thanks @jonlwheat2-gif +- **feat(providers):** add SeekAi (`seekai.cc`) as an OpenAI-compatible New-API gateway — catalog id `seekai` (alias `ska`), `https://seekai.cc/v1`, live `/v1/models` via `passthroughModels`, aggregator-list membership so New-API balance detection can opt in. No referral/aff codes. ([#11786](https://github.com/diegosouzapw/OmniRoute/issues/11786)) ([#12557](https://github.com/diegosouzapw/OmniRoute/pull/12557)) — thanks @HouMinXi +- **feat(zai):** add GLM-5.3-Flash Coding Plan support (1M context, 128K output, vision, `low|high|max` reasoning) and route `zai` GLM-5.3-family API-key traffic through the OpenAI-compatible Coding Plan endpoint with native thinking defaults ([#11801](https://github.com/diegosouzapw/OmniRoute/pull/11801)) — thanks @Neuron-Mr-White +- **feat(combo):** choose how combo models are ordered — manual, provider, score, or name — via a sort control in the dashboard builder, persisted in `config.modelSort` and re-applied on load and after add ([#11812](https://github.com/diegosouzapw/OmniRoute/pull/11812)) — thanks @maxmad64bis +- **feat(free):** custom models can be marked free-tier via `customModels[].isFree`; `isFreeModel()` is the first door and `hidePaidModels` respects it even for providers outside the free budget ([#11843](https://github.com/diegosouzapw/OmniRoute/pull/11843)) — thanks @maxmad64bis +- **feat(nodejs):** add `5dive` as a `configure` target — `omniroute configure 5dive` / `omniroute setup-5dive` write a 5dive auth profile that points an agent fleet's `claude` seats at OmniRoute, with the root-only write, the loopback-vs-`https` endpoint rule and the per-seat model pin handled explicitly ([#11852](https://github.com/diegosouzapw/OmniRoute/pull/11852)) — thanks @5dive-bot +- **feat(sse):** treat `max` as a first-class reasoning-effort tier and clamp per model family (GLM 5.1+/DeepSeek V4+/Kimi K3+ keep native `max`; o1/MiniMax/Grok/Muse Spark clamp to their upstream ceiling) ([#11875](https://github.com/diegosouzapw/OmniRoute/pull/11875)) — thanks @Chewji9875 +- **feat(providers):** the provider plugin manifest now advertises a `usage-fetch` capability for the 40 providers that have a wired usage/quota fetcher, so external dashboards can read it from `GET /api/v1/provider-plugin-manifest` instead of parsing `open-sse/services/usage.ts` after every release. Discovery only — no new fetcher, no quota change, and the Dashboard quota widget stays gated by `USAGE_SUPPORTED_PROVIDERS`. `USAGE_FETCHER_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/fetcherProviders.ts`) and is re-exported from `services/usage.ts`, keeping the manifest module a light leaf instead of pulling the ~490-module usage dispatcher into the manifest route. ([#11903](https://github.com/diegosouzapw/OmniRoute/pull/11903)) — thanks @maxmad64bis / @pacocartones +- **feat(plugins):** `OMNIROUTE_PLUGINS_DIR` sets the directory the runtime plugin scanner reads — and the root the plugin manager installs into — overriding the `HOME`-derived default, so a Docker/K8s deployment can point straight at its bind-mounted plugin tree instead of moving `HOME` just to relocate the scan path. An image that exports no home no longer scans `/tmp/.omniroute/plugins` in silence: the resolved directory is logged once at startup as `scanner.dir_resolved`, naming the input that won. Unset, behaviour is unchanged. Distinct from the CLI-only `OMNIROUTE_PLUGIN_PATH`, which finds `omniroute-cmd-*` command packages and never reached this scanner ([#11906](https://github.com/diegosouzapw/OmniRoute/pull/11906)) — thanks @amaleta / @pacocartones +- **feat(leases):** add an explicit owner-authenticated status action that returns only the active lease's privacy-safe configured connection and provider labels, with generation fencing and no credential or internal-id disclosure ([#11910](https://github.com/diegosouzapw/OmniRoute/pull/11910)) — thanks @KaspaPulse +- **feat(providers):** import-from-file modal shows per-row API errors and ships a downloadable CSV template ([#12071](https://github.com/diegosouzapw/OmniRoute/issues/12071)) ([#12504](https://github.com/diegosouzapw/OmniRoute/pull/12504)) — thanks @HouMinXi +- **feat(providers):** dashboard search matches connection name and `baseUrl` so imported OpenAI-compat nodes surface on the provider card ([#12108](https://github.com/diegosouzapw/OmniRoute/issues/12108)) ([#12495](https://github.com/diegosouzapw/OmniRoute/pull/12495)) — thanks @HouMinXi +- **feat(routing):** add a `score` Auto router strategy that selects the highest configured weighted score and reuses `explorationRate`. ([#12155](https://github.com/diegosouzapw/OmniRoute/pull/12155)) — thanks @jacobsparts +- Add a runtime feature flag to disable universal context handoffs globally without changing the default behavior. ([#12167](https://github.com/diegosouzapw/OmniRoute/pull/12167)) — thanks @jacobsparts +- **feat(providers):** the provider plugin manifest now also advertises a `usage-supported` capability for the 46 providers whose usage API is accepted by the server and Dashboard routes, so integrators can distinguish "the server will serve quota for this provider" from "a fetcher is wired" without reading TypeScript. Discovery only — no fetcher or quota change. `usage-fetch` resolves on id or alias (the usage dispatcher accepts both); `usage-supported` resolves on id alone, matching the runtime guard `USAGE_SUPPORTED_PROVIDERS.includes(providerId)`. `USAGE_SUPPORTED_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/supportedProviders.ts`) and is re-exported from `providers.ts`, mirroring the `fetcherProviders` leaf from #11903 and keeping the manifest a light module. ([#12214](https://github.com/diegosouzapw/OmniRoute/pull/12214)) — thanks @maxmad64bis +- **feat(rankings):** order Free Provider Rankings by what each provider actually served — `GET /api/free-provider-rankings?sortBy=reliability` and a "Most reliable first" toggle on the page. Providers with too few calls to state a success rate keep their score order below the measured ones; the default order is unchanged ([#12218](https://github.com/diegosouzapw/OmniRoute/pull/12218)). — thanks @maxmad64bis +- **perf(sse):** defer `cloneLogPayload()` in the structured SSE collector until after the `maxEvents`/`maxBytes` cap check, eliminating ~9,800 wasted `structuredClone` calls per streaming response (65–71% faster `push()`). Reducer snapshot isolation restored for OpenAI and Responses summaries ([#12241](https://github.com/diegosouzapw/OmniRoute/pull/12241)) — thanks @PauloHSOliveira ([#12243](https://github.com/diegosouzapw/OmniRoute/pull/12243)) +- **feat(usage):** Devin CLI agentic quota (Codeium seat-management GetUserStatus) and OpenRouter key limits plus account credits now surface in Provider Limits ([#12256](https://github.com/diegosouzapw/OmniRoute/pull/12256) — thanks @Neuron-Mr-White) +- **feat(settings):** persist `headroomUrl` through Settings so status/start use the operator URL instead of only `HEADROOM_URL` ([#12306](https://github.com/diegosouzapw/OmniRoute/issues/12306)) ([#12487](https://github.com/diegosouzapw/OmniRoute/pull/12487)) — thanks @HouMinXi +- **feat(auto-combo):** Auto-Combo scoring can now weigh how often a provider/model has actually succeeded. The engine already carried that number on every candidate — 24 hours of usage history behind a ten-sample floor, real-time metrics otherwise — and the scoring function never read it, while the weight table described `stability` as if it did. `reliability` (`1 - failureRate`, with the same field precedence and the same rate-bounding the speed ranking already uses, so a corrupt reading means "nothing observed" rather than "fails every call") is now a declared factor shipping at weight `0`, so routing is unchanged until an operator gives it one, and the `stability` description now matches what that factor computes ([#12317](https://github.com/diegosouzapw/OmniRoute/pull/12317)) — thanks @maxmad64bis +- **feat(routing):** With `freeAccessPolicy: "strict"`, the read-only candidate listing (`GET /v1/auto-combo/{channel}/candidates`) no longer hides the candidates the zero-cost guard excludes — the same read-only transparency the resilience filter already honours (#9133). Each candidate now carries `freeAccessExclusion` saying why it would be kept out, and it tells an exhausted allowance apart from a quota reading that never arrived or went stale, which used to look identical from the outside. Routing is unchanged: the listing reports, it never enforces. The separate `excludeTosAvoid` guard still drops its candidates without a reason; that gap is now documented rather than closed ([#12319](https://github.com/diegosouzapw/OmniRoute/pull/12319)) — thanks @maxmad64bis +- **feat(radar):** The Radar catalog table now shows two facts it was already receiving from the feed and dropping on the floor: the per-model rate limits (requests and tokens, per minute and per day) in a new column, and a badge when a provider's terms state it may train on the prompts you send. A limit of zero renders as zero rather than "rate-only" — for a ceiling those are opposite facts — and a model with no training statement gets no badge, because an absent statement is not a guarantee ([#12320](https://github.com/diegosouzapw/OmniRoute/pull/12320)) — thanks @maxmad64bis +- **feat(radar):** explain Community, single-use, contributor, supporter, recovery, abuse, offers, and privacy rules before either Radar activation action, and remove the superseded fixed-PR grant promise from every UI locale ([#12342](https://github.com/diegosouzapw/OmniRoute/pull/12342)) +- **feat(gamification):** the dashboard Profile page now shows the real daily streak — `/api/gamification/level` returns `streak: { current, longest }` (per key with `apiKeyId`, operator-wide maximum otherwise) and the streak card reads it instead of a hard-coded 0 (#2403) ([#12377](https://github.com/diegosouzapw/OmniRoute/pull/12377)) — thanks @pacocartones +- **feat(gamification):** the dashboard leaderboard now shows each API key's display name under the Name column instead of a truncated key id; `GET /api/gamification/leaderboard` attaches `name` per entry (name only — no key material), while the shared ranking helper and the federation leaderboard stay id-only — thanks @pacocartones ([#12385](https://github.com/diegosouzapw/OmniRoute/pull/12385)) +- **feat(gamification):** enforce the documented 1000 XP/min per-API-key anti-cheat rate limit on the XP award path; over-limit awards are logged and skipped instead of persisted, and the sliding window now matches the timestamp format stored in `xp_audit_log` ([#2403](https://github.com/diegosouzapw/OmniRoute/issues/2403)) ([#12390](https://github.com/diegosouzapw/OmniRoute/pull/12390)) — thanks @pacocartones +- **feat(admin):** localize the gamification anomalies page — the loading state, the Status column and the Suspicious badge now come from the `common` catalog (new `common.suspicious` key propagated to every locale) — add it to the Gamification sidebar group as `gamification-admin` (`/dashboard/gamification/admin`), and expose the loading and empty states as polite `role="status"` live regions (#12401 — thanks @pacocartones) +- **feat(providers):** skip GitHub combo members missing from the live synced catalog, and drop Copilot models that are policy-disabled or hidden from the model picker ([#12473](https://github.com/diegosouzapw/OmniRoute/pull/12473)) — thanks @RaviTharuma +- **feat(i18n):** locale-expansion tooling — `npm run i18n:add-locale` adds a language to config, dashboard, docs mirrors, CLI, README/indexes and site in one command; browser, CLI and cookie detection resolve `uk`, `fil`/`tl`, `zh-Hant` (and the retired legacy `in` → `id`) via config aliases; `config/i18n.json` now ships in the npm package so the published CLI can read it; new real-translation ratio gate (`npm run i18n:check-ratio`, advisory) with a per-locale ratchet baseline; `run-translation --adopt` restores `.i18n-state.json`; `sync-language-bars` generates 🌐 bars from config; `validate_translation.py` loads its allowlist again. Retires the duplicate `in` locale (Indonesian mislabelled as Hindi) — 42 honest locales; saved `NEXT_LOCALE=in` / `OMNIROUTE_LANG=in` keep working. (#12496) +- **feat(api):** Emit gateway-measured `tokens_per_second` (TTFT excluded) on streaming usage and `X-OmniRoute-Tokens-Per-Second` when first-token latency is known ([#12616](https://github.com/diegosouzapw/OmniRoute/issues/12616)) ([#12631](https://github.com/diegosouzapw/OmniRoute/pull/12631)) — thanks @RaviTharuma +- **feat(opencode-plugin): map gateway cost/usage/tok/s onto OpenCode inference payloads** — the official plugin copies `X-OmniRoute-Response-Cost`, token counts, `X-OmniRoute-Tokens-Per-Second` / `usage.tokens_per_second`, TTFT, and the winning `X-OmniRoute-Model` onto the JSON/SSE body OpenCode already consumes. Missing tok/s is left unset (never `tokens / latency`). (#12636) — thanks @RaviTharuma +- **feat(combo):** add combo strategy `quota-weighted`: skip exhausted accounts, then weighted-draw by leftover / in-flight load; existing conversations stay pinned until the account is empty ([#12789](https://github.com/diegosouzapw/OmniRoute/pull/12789)) — thanks @HouMinXi +- **feat(models):** Account live listings become the chat catalog source for Claude, Codex, Copilot, and AGY; public metadata only fills prices on IDs those accounts already list. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **fix(models):** Union `agy` and `antigravity` live catalogs so `agy/gemini-3.8-flash-high` is not rejected after the prefix folds to `antigravity`. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **feat(opencode):** Add `@omniroute/opencode-plugin-v2`, an opencode v2 plugin that gives a v2 install a route to an OmniRoute gateway: it publishes models, combos and auto-combos into the host catalog, refreshes them lazily behind a 300s TTL, and keeps serving the last known catalog from an on-disk snapshot when the gateway is unreachable. The v1 plugin (`@omniroute/opencode-plugin`) is untouched and keeps working as before. The plugin adapts to the host instead of assuming one shape: it reads the shape the host seeds into the catalog draft and publishes the `api` block, the pre-`api` top-level fields, or both. The gateway key comes from the host credential store when one is connected, so no secret needs to sit in a config file; a source the gateway refuses is named in a warning instead of silently degrading the catalog to raw model ids; and tool schemas bound for a Gemini model are stripped of the JSON-Schema keywords Gemini rejects, so tool calling survives that route ([#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870)) — thanks @maxmad64bis +- feat(services): show sanitized CLIProxyAPI account health from its authenticated management API without exposing credentials, file paths, or raw account metadata (#6342) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add AnySearch free web search + URL extract (webFetch) with typed results, credential validation, REST routing, and MCP selection - fallback-only ([#11690](https://github.com/diegosouzapw/OmniRoute/pull/11690)) — thanks @Xxx91n +- **feat(dashboard):** display clamped `[0, 100]%` cached input token ratio in request logs table ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) — thanks @b3nw +- **feat(catalog):** add `OMNIROUTE_DISABLE_THINKING_LEVEL_VARIANTS` feature flag to optionally filter out thinking level variants from model catalog ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) — thanks @b3nw +- **feat(dashboard):** continuously export call logs to external analytics stores. A pluggable destination registry ships the full Logs-tab record set on an hourly `JobRegistry` cron, with a persisted per-destination cursor, batched inserts, a config UI rendered from each destination's own field descriptors, and a REST layer (`/api/log-export/*`) for CRUD, a connection test, and an on-demand run. A destination can opt into `includeBodies` to also ship the request and response payloads shown in the Logs detail pane, including the client and provider views of each call; this is off by default, and payloads inherit the dashboard's PII sanitisation, secret redaction and `noLog` handling. Google BigQuery is the first destination, using a service-account key stored encrypted at rest and streaming inserts keyed by call-log id, into a table that is day-partitioned on `timestamp` and clustered on `api_key_name`, `provider`, `model` and `status`. ([#11945](https://github.com/diegosouzapw/OmniRoute/pull/11945)) — thanks @dpozimski +- **feat(providers):** add MaxAI as a signed, OpenAI-compatible provider serving its 13 paid chat models (GPT-5.6 / Luna / Thinking, Claude 5 Sonnet, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite, Grok 4.1-fast / 4.5, DeepSeek V3.2 / R1, Llama 3.3 70B) through OmniRoute's `/v1` endpoint, with per-request HMAC-SHA1→SM3→AES request signing, live model + context-window discovery from `/models/get_config`, and prompted tool-calling translated to OpenAI `tool_calls` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI vision input — image_url content parts are forwarded inline in `message_content` to the 6 vision-capable models (GPT-5.6 / Luna / Thinking, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite) ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI image generation — 6 image models (gpt-image-1, dall-e-3, flux-1-schnell/dev/pro, sd3-medium) exposed through `POST /v1/images/generations` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI document RAG — inline base64 file/document attachments are uploaded to MaxAI (content-addressed `doc_id`) and attached to the chat via `doc_list` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** browserless MaxAI onboarding — email device-pair login (`/api/providers/[id]/login`) and signed access-token refresh, so a connection can be created and kept fresh without a real browser or Google OAuth ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** per-provider TLS impersonation profile (MaxAI presents a Windows Firefox-150 client fingerprint) so its bot-sensitive endpoints accept OmniRoute traffic ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** add **Nimble** as a web-search and web-fetch provider (`nimble-search`) — `/v1/search` routes to Nimble's search API at `lite` depth (locale, freshness and include/exclude domain filters mapped onto the shared request contract), and `/v1/web/fetch` routes to Nimble Extract, which covers all four fetch formats (`markdown`, `html`, `links`, `screenshot`) from a single call. One API key serves both surfaces. ([#11620](https://github.com/diegosouzapw/OmniRoute/pull/11620)) — thanks @wildcard +- The npm publish is automatic again, through npm Trusted Publishing (OIDC): the hosted `stage-npm` job publishes with a short-lived credential minted from GitHub's id-token — no `NPM_TOKEN`, no 2FA prompt, provenance attached. `publish_mode=staged` (owner approves with 2FA) and `direct` (token) remain available on `workflow_dispatch`. ([#11931](https://github.com/diegosouzapw/OmniRoute/pull/11931)) +- **feat(providers):** Add **Opper** as an API-key gateway provider — EU-hosted AI gateway with 700+ models from 30+ providers behind one OpenAI-compatible API and one key (`OPPER_API_KEY`); model ids use `provider/model` format (e.g. `anthropic/claude-sonnet-4-6`, `openai/gpt-5`); live model catalog at `https://api.opper.ai/v3/compat/models`; entry mirrors `requesty` (same shape, `passthroughModels: true`, no static seed) ([#11629](https://github.com/diegosouzapw/OmniRoute/pull/11629)) +- **feat(dashboard):** the `/dashboard/orchestration` snapshot hook now subscribes to the `agents` WebSocket channel (`agent.task.updated`) instead of `requests` as its refetch trigger, and relaxes its background poll from 5s to 30s while that WS connection is up — falling back to the tighter 5s cadence, reprogrammed live on any connect/disconnect transition, whenever the socket is down. ([#12409](https://github.com/diegosouzapw/OmniRoute/pull/12409)) +- **feat(dashboard):** Orchestration canvas quick wins — search box plus state/source/provider filter chips with a one-click clear, and per-source collapse/expand, all reflected in the URL so a filtered/collapsed view is shareable and survives a refresh; the detail drawer gained a "copy trace JSON" action and hardened error/empty-state and accessibility handling; the Agents-tab edges now animate traveling particles along active (running) connections; and the canvas node/edge status colors moved off fixed hex values onto theme-aware `--orch-status-*` CSS custom properties, so they adapt correctly to light/dark mode. ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- **chore(dashboard):** Orchestration UI hardening pass and the missing component/model test coverage it called for — `OrchestratorNode`/`ActivityNode`/`OverflowNode` rendering, the `?node=`/overflow-click page routing, the Agents-tab orchestrator-click no-op and `showCompleted` toggle, and the overview kanban's done-column sort order (#12270, #12271). ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- New `/dashboard/orchestration` page: live unified view of everything running — Cloud Agent, A2A and Conductor as a real-time graph (Agents tab), the combo cascade (Routing tab, reusing the Combo Live Studio) and a state kanban (Overview tab), with a detail drawer (trace, cost, approve/cancel). Read-only over existing APIs — no new backend. Canvas concept credit: PR #11815 design ([#12261](https://github.com/diegosouzapw/OmniRoute/pull/12261)) +- **feat(dashboard):** new "History" tab on `/dashboard/orchestration` — an Airflow-style grid of finished runs over a 24h/7d/30d preset window, one row per (source, identity), clicking a cell opens the existing detail drawer. It is backed by real persistence: A2A task lifecycle transitions are now written to the `a2a_tasks` table (purged after 30 days, configurable via `OMNIROUTE_A2A_HISTORY_RETENTION_DAYS`) and served by the new `GET /api/a2a/tasks/history` listing endpoint, with the task-detail route falling back to persisted history once a run leaves the in-memory snapshot. Conductor runs stay remote and are not persisted locally — the tab says so instead of silently omitting them. ([#12479](https://github.com/diegosouzapw/OmniRoute/pull/12479)) +- **feat(dashboard):** the orchestration detail drawer gained a "Repeat" action for Cloud Agent, A2A and Conductor tasks — a two-click confirm (click once to arm, click again within the confirm window to fire) re-submits the original prompt/input as a new run. The button is disabled with an explanatory tooltip whenever the original input can't be recovered from the loaded task detail (e.g. it never carried a prompt, or the detail failed to load). Two limitations of the A2A variant, by design: it targets the `/a2a` JSON-RPC endpoint, which authenticates with an API key only (`REQUIRE_API_KEY=true` or a configured `OMNIROUTE_API_KEY` makes a dashboard-session repeat answer `HTTP 400` — surfaced verbatim in the drawer's error line, never as a success), and the `message/send` call is SYNCHRONOUS: the POST blocks for the whole skill run, so the success confirmation only appears once the repeated task finishes. A dashboard-authenticated A2A creation path is deliberately left to a follow-up — widening the endpoint's auth posture is an operator decision, not a side effect of this feature. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(a2a):** A2A task execution now records which memories were consulted for the task's last user message as `metadata.memoryHits` (id/key/type/content-snippet) plus a `memory_hits` history event, purely for observability — the retrieved memory is never injected into a skill's prompt or behavior. Gated by the `OMNIROUTE_A2A_MEMORY_HITS` kill-switch (default enabled; set to `0` to skip the recall lookup entirely). The drawer's new "Memory used" section lists these hits for a2a tasks and is omitted whenever there are none. Known limitation: recall only resolves under the keyless posture — a keyed caller's task owner is a SHA-256 prefix of the API key, while memory rows are keyed by the database api-key id, and no hash→id lookup exists today, so the hit list stays empty for keyed callers. The recorded hits are also kept out of the task's own `input` (and therefore out of the persisted input and of the "Repeat" request body), so repeating a task never re-sends the previous run's memory snippets. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(providers):** add a Perplexity Agent API provider (`perplexity-agent` / `pplx-agent`) for Perplexity `/v1/responses`, including the documented Anthropic, OpenAI, Google, xAI, DeepSeek, Z.AI, Moonshot/Kimi, NVIDIA, and Perplexity model IDs plus Anthropic-model `max_output_tokens` compatibility. ([#12103](https://github.com/diegosouzapw/OmniRoute/pull/12103)) — thanks @AIB1TAL0S +- **feat(providers):** add RPD (Requests Per Day) limit to provider rate limit overrides across UI, schemas, DB, and i18n ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) — thanks @b3nw +- **feat(providers): add UC Direct (uncensored.com Developer API), the metered OpenAI-compatible surface.** A standard OpenAI-compatible passthrough (default executor) for uncensored.com's official REST API at `https://api.uncensored.com/api/v1`: `X-api-key` auth (never-expiring `uai_sk_live_` key), `POST /chat/completions` with streaming SSE and native tool-calling, and the full live metered catalog (82 models across 15 providers, discovered from the public `GET /v1/models`). Registered as provider `uc-direct` (alias `ucd`). Complements the un-metered `uc` persona provider — same models, metered credits and a plain API key instead of a subscription session. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(providers): add UC (uncensored.com), the un-metered subscription "persona" chat as an OpenAI-compatible provider.** A WebSocket web-app port: a durable Clerk credential mints a short-lived session token per connect (browserless — no API key), driving UC's persona socket. Ships the browserless email-code login (request → verify → harvest), the 19 verified persona models (Claude Opus, Gemini, Grok, GLM, Kimi, DeepSeek, MiniMax, incl. the uncensored variants), prompted `` tool-calling with a per-model code-style dialect + auto-cure retry for guardrailed models, live ``/reasoning split, streaming + non-streaming OpenAI responses, and full quota/auth error surfacing (paywall / message-limit / rate-limit → 429, invalid session → 401 re-login). Full multimodal parity via the persona blob-upload layer: **vision** (image input, 15 vision-capable models), **document RAG** (PDF/doc upload, server-side extraction), **image generation** (22 models), **video generation** (14 models, async signed-url → poll), and **TTS** (streaming MP3). Registered as provider `uc` (alias `ucn`). The metered OpenAI-compatible Developer API is a separate `uc-direct` provider. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(logs):** add copy-all button to request detail modal ([#11083](https://github.com/diegosouzapw/OmniRoute/pull/11083)) — thanks @AndrianBalanescu +- **feat(check):** make serviceKinds required and add the reverse-walk provider consistency gate ([#11392](https://github.com/diegosouzapw/OmniRoute/pull/11392)) — thanks @Tushar49 +- **feat(providers):** only advertise verified-working connections by default ([#11448](https://github.com/diegosouzapw/OmniRoute/pull/11448)) — thanks @alltomatos +- **feat(resilience):** add hierarchical concurrency admission ([#11493](https://github.com/diegosouzapw/OmniRoute/pull/11493)) +- **feat(providers):** derive + surface expiry for JWT-bearing web cookies (#11497) ([#11505](https://github.com/diegosouzapw/OmniRoute/pull/11505)) — thanks @oyi77 +- **feat(tunnels):** support named cloudflared tunnels via CLOUDFLARED_CONFIG ([#11561](https://github.com/diegosouzapw/OmniRoute/pull/11561)) — thanks @ggiak +- **feat(compression):** make proactive context-compression threshold a live setting ([#11564](https://github.com/diegosouzapw/OmniRoute/pull/11564)) — thanks @ragnar-claude +- **feat(resilience):** add provider peak-hour protection ([#11622](https://github.com/diegosouzapw/OmniRoute/pull/11622)) — thanks @Neuron-Mr-White +- **feat(providers):** add 1min.ai provider ([#11631](https://github.com/diegosouzapw/OmniRoute/pull/11631)) — thanks @zero-executioner +- **feat(dashboard):** collapsible JSON tree viewer for request/response payloads ([#11703](https://github.com/diegosouzapw/OmniRoute/pull/11703)) — thanks @hartmark +- **feat(routing):** add LiquidAI LFM2.5-2.6B free tier via OpenRouter ([#11752](https://github.com/diegosouzapw/OmniRoute/pull/11752)) — thanks @brick30llc-ctrl +- **feat(pricing):** add GLM-5.3-Flash pricing, model specs, and catalog registration ([#11830](https://github.com/diegosouzapw/OmniRoute/pull/11830)) — thanks @AndrianBalanescu +- **feat(routing):** add relayMode for schema-locked context handoffs ([#11839](https://github.com/diegosouzapw/OmniRoute/pull/11839)) — thanks @raheemuddin786 +- **feat(config):** add support for runtime system prompt configuration and hot-reloading ([#11841](https://github.com/diegosouzapw/OmniRoute/pull/11841)) — thanks @raheemuddin786 +- **feat(routing):** expand connection-aware quota prefilter across combo strategies (#11682) ([#11850](https://github.com/diegosouzapw/OmniRoute/pull/11850)) — thanks @HouMinXi +- **feat(models):** reactive discovery sync after Antigravity model-not-found 404 ([#11888](https://github.com/diegosouzapw/OmniRoute/pull/11888)) — thanks @MumuTW +- **feat(models):** auto-aliases and token limits from synced Antigravity discovery ([#11889](https://github.com/diegosouzapw/OmniRoute/pull/11889)) — thanks @MumuTW +- **feat(settings):** raise sticky round-robin limit caps to 1000 ([#12015](https://github.com/diegosouzapw/OmniRoute/pull/12015)) — thanks @adivekar-utexas +- **feat(resilience):** operator-configurable global credential health check interval ([#12043](https://github.com/diegosouzapw/OmniRoute/pull/12043)) — thanks @adivekar-utexas +- **feat(quota):** use official OpenCode Go usage API ([#12124](https://github.com/diegosouzapw/OmniRoute/pull/12124)) — thanks @ddarkr +- **feat(resilience):** default the credential health check sweep to 60 minutes ([#12138](https://github.com/diegosouzapw/OmniRoute/pull/12138)) — thanks @adivekar-utexas +- **feat(quality):** new-code mode for the complexity and dead-code ratchets (Clean as You Code) ([#12142](https://github.com/diegosouzapw/OmniRoute/pull/12142)) +- **feat(dashboard):** orchestration canvas — unified model + snapshot hook (part 1/2) ([#12156](https://github.com/diegosouzapw/OmniRoute/pull/12156)) +- **feat(usage):** add Kilo Code balance and Kilo Pass quotas ([#12178](https://github.com/diegosouzapw/OmniRoute/pull/12178)) — thanks @killer30001000 +- **feat(providers):** refresh vendored ChatGPT Web connector to v4.0.7 ([#12181](https://github.com/diegosouzapw/OmniRoute/pull/12181)) — thanks @backryun +- **feat(providers):** manual "Clear cooldown" action in the cooling panel ([#12224](https://github.com/diegosouzapw/OmniRoute/pull/12224)) — thanks @patrykkopycinski +- **feat(providers):** restore ChatGPT Web via clean-room browser transport ([#12239](https://github.com/diegosouzapw/OmniRoute/pull/12239)) — thanks @backryun +- **feat(sse):** wire the PROVIDER_PROFILES window gate into the global provider cooldown ([#12247](https://github.com/diegosouzapw/OmniRoute/pull/12247)) +- **feat(providers):** modernize CLOVA Studio chat and embeddings ([#12277](https://github.com/diegosouzapw/OmniRoute/pull/12277)) — thanks @backryun +- **feat(browser):** adopt Obscura as primary headless browser engine with Chromium fallback ([#12286](https://github.com/diegosouzapw/OmniRoute/pull/12286)) — thanks @opensource-elearning +- **feat(sse):** full language parity for output styles (es/de/fr/it/ru/zh + autoDetect) ([#12289](https://github.com/diegosouzapw/OmniRoute/pull/12289)) +- **feat(quality):** complete test:scoped — --full map rebuild, stdin selection, CI loader parity (#8084 D1) ([#12353](https://github.com/diegosouzapw/OmniRoute/pull/12353)) +- **feat(providers):** refresh Fable, Cursor, and Devin catalogs ([#12367](https://github.com/diegosouzapw/OmniRoute/pull/12367)) — thanks @backryun +- **feat(video):** redact transcript text from logs and durable memory (#12150 P1) ([#12427](https://github.com/diegosouzapw/OmniRoute/pull/12427)) +- **feat(dashboard):** parent-link, genuine-continuation badge, and modal perf fixes ([#12448](https://github.com/diegosouzapw/OmniRoute/pull/12448)) — thanks @hartmark +- **feat(dashboard):** adaptive context-budget dial on compression panel ([#12488](https://github.com/diegosouzapw/OmniRoute/pull/12488)) — thanks @HouMinXi +- **feat(providers):** refresh Z.ai Web models and browser transport ([#12524](https://github.com/diegosouzapw/OmniRoute/pull/12524)) — thanks @backryun +- **feat(video):** redact raw client-snapshot transcript fields in the detailed log (#12150 P2) ([#12528](https://github.com/diegosouzapw/OmniRoute/pull/12528)) +- **feat(providers):** refresh NVIDIA hosted models ([#12538](https://github.com/diegosouzapw/OmniRoute/pull/12538)) — thanks @backryun +- **feat(quota):** Moonshot Open Platform balance and TPD lock for custom nodes ([#12590](https://github.com/diegosouzapw/OmniRoute/pull/12590)) — thanks @HouMinXi +- **feat(video):** redact transcript in the in-memory pending-request snapshot (#12430 item 6) ([#12596](https://github.com/diegosouzapw/OmniRoute/pull/12596)) +- **feat(dashboard):** link a log entry's Conversation Context to its owning conversation ([#12646](https://github.com/diegosouzapw/OmniRoute/pull/12646)) — thanks @hartmark +- **feat(catalog):** eligibility-gated free-tier bucket ([#12669](https://github.com/diegosouzapw/OmniRoute/pull/12669)) +- **feat(db):** fail-closed previous_response_id continuation for redacted video turns (#12150 P2b) ([#12707](https://github.com/diegosouzapw/OmniRoute/pull/12707)) +- **feat(gamification): pay the documented `streak_bonus` and `badge_unlock` XP rewards.** `XP_REWARDS` listed both rewards but the award pipeline never paid them: the private reward table in `events.ts` omitted them, `updateStreak()` did not report when a streak extended, and badge unlocks carried no XP. Every request that extends a daily streak now pays `streak_bonus × streak length` once per UTC day (guarded by a same-day `xp_audit_log` check), and every badge unlocked through the pipeline pays `badge_unlock` once per badge (guarded by the `user_badges` primary key; `unlockBadge()` now reports whether it inserted). Bonus XP flows through the same `addXp` + level sync + global/weekly/monthly leaderboard path as action XP, so level-ups and rankings include it. The Radar supporter recognition unlock stays XP-free. (#12522 — thanks @pacocartones) +- **feat(providers):** advertise a `free-tier` capability in the provider plugin manifest for every provider with documented free models, so sidecars and dashboards can filter free-capable providers without reading the quota catalog ([#12786](https://github.com/diegosouzapw/OmniRoute/pull/12786)) — thanks @maxmad64bis +- **feat(providers):** Added EURouter as an OpenAI-compatible API-key gateway (`https://api.eurouter.ai/v1`), with live model discovery via `passthroughModels`. Its copy states that models are served by third-party upstreams listed per model, so an EU-based router is not read as EU data residency for inference. ([#13025](https://github.com/diegosouzapw/OmniRoute/pull/13025)) — thanks @ntdatt812 +- **feat(providers):** Added GreenPT as an OpenAI-compatible API-key provider (`https://api.greenpt.ai/v1`), with live model discovery via `passthroughModels`. No free-inference badge: the published docs describe a free API subscription billed per token, not a free tier. ([#13024](https://github.com/diegosouzapw/OmniRoute/pull/13024)) — thanks @ntdatt812 +- **feat(i18n):** 9 new locales — Greek (`el`), Croatian (`hr`), Serbian (`sr`), Lithuanian (`lt`), Estonian (`et`), Latvian (`lv`), Slovenian (`sl`), Maltese (`mt`), Irish (`ga`) — across the dashboard, docs mirrors, CLI, README and the site; OmniRoute now ships every official EU language (51 locales). (#13044) +- **feat(i18n):** 8 new locales — Kannada (`kn`), Malayalam (`ml`), Odia (`or`), Punjabi (`pa`), Nepali (`ne`), Sinhala (`si`), Burmese (`my`), Khmer (`km`) — across the dashboard, docs mirrors, CLI, README and the site (59 locales). The translator now restores the ICU literal escape around angle placeholders and splits oversized docs sections before translating. (#0000) ([#13660](https://github.com/diegosouzapw/OmniRoute/pull/13660)) +- feat(providers): **list Agnes 3.0 Flash as the current free chat model, drop retired 1.5 Flash, add Image 2.0/2.5 Flash plus Video 2.5/2.5 Flash, and discover the live `/v1/models` catalog (including the CN host `api.agnes-ai.cn`).** `agnes-1.5-flash` now forwards to `agnes-3.0-flash`. Video 2.5 polls `GET /v1/videos/{id}` (not the V2.0 `/agnesapi` contract). Live `/v1/models` (2026-09-09) no longer serves 1.5; the wiki marks it deprecated. 3.0 Flash is 512K context / 65,536 max output, same window as 2.5. CN-region keys use the existing per-connection base-URL field, default stays `apihub.agnes-ai.com`. ([#13120](https://github.com/diegosouzapw/OmniRoute/pull/13120)) — thanks @HouMinXi +- **feat(sse):** Codex and OpenAI catalogs list GPT-6 Astra with effort aliases (`-low` through `-ultra`); Codex CLI identity pins `@openai/codex@0.153.4` in lockstep with the image ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **feat(dashboard):** the orchestration History tab gained a "Compare runs" mode — toggling it turns each grid cell into a 2-item selection queue (a 3rd click drops the oldest pick), and picking two cells opens a side-by-side comparison panel instead of the usual detail drawer. The panel fetches both runs' detail the same way the drawer does (falling back to persisted history once a run leaves the live TTL window) and shows, per side: identity/source/state, start time, a signed `right - left` delta for duration/cost/event count, the event timeline aligned by index, and any memory hits. One side's fetch failing never blocks the other, and a delta is only ever computed when both sides have a finite value — otherwise it renders "—", never `NaN`. Comparing two runs from different sources or skills still works; a banner marks the deltas as informational rather than hiding them, since the two runs aren't a strict apples-to-apples pair. ([#12677](https://github.com/diegosouzapw/OmniRoute/pull/12677)) +- **feat(dashboard):** badge a conversation that never reached a clean stop ([#12717](https://github.com/diegosouzapw/OmniRoute/pull/12717)) — thanks @hartmark +- **feat(release):** reconcile-changelog tool + version-anchored fragment aggregation ([#12987](https://github.com/diegosouzapw/OmniRoute/pull/12987)) +- **feat(mcp):** carry context_length through MCP list_models_catalog projection (#12776) ([#13406](https://github.com/diegosouzapw/OmniRoute/pull/13406)) — thanks @KooshaPari +- **feat(cli):** make startup readiness budget configurable (#13369) ([#13433](https://github.com/diegosouzapw/OmniRoute/pull/13433)) — thanks @KooshaPari + ### 🐛 Bug Fixes +- **security(streaming):** sanitize generic mid-stream error messages before emitting OpenAI, + Responses, or Claude SSE failure frames and before diagnostic logging, while preserving raw + failures for internal classification and keeping client disconnects out of provider failure state. ([#12457](https://github.com/diegosouzapw/OmniRoute/pull/12457)) +- **fix(claude):** `blockExtraUsage: false` no longer lets 5h quota preflight skip the account; extra usage is billed after the session bar is gone, so the request must reach Anthropic ([#12803](https://github.com/diegosouzapw/OmniRoute/pull/12803)) — thanks @HouMinXi +- Sanitize HuggingChat conversation-creation and message-send transport failures before they reach client error bodies or provider logs. ([#12467](https://github.com/diegosouzapw/OmniRoute/pull/12467)) +- **fix(dashboard):** Keep local and theme-aware provider SVG icons at a definite layout size so Chromium does not collapse them to 0×0 after the v3.8.50 image-rendering change ([#12054](https://github.com/diegosouzapw/OmniRoute/pull/12054)) — thanks @ponkcore +- **fix(security):** Sanitize provider and runtime failures before public API, SSE and MCP responses and before persistent request, proxy and usage logs, preventing credentials, stack traces and host filesystem paths from crossing those boundaries while preserving stable error codes and useful diagnostics. ([#12506](https://github.com/diegosouzapw/OmniRoute/pull/12506)) +- **fix(cli):** `omniroute tunnel create` no longer crashes with `Cannot read properties of undefined (reading optsWithGlobals)` — removed the duplicate positional argument that caused Commander.js to misalign the action callback parameters ([#12295](https://github.com/diegosouzapw/OmniRoute/issues/12295)) ([#12368](https://github.com/diegosouzapw/OmniRoute/pull/12368)) — thanks @gonisulaimann +- Stop advertising Gemini Live-only models as supported audio endpoints until OmniRoute proxies the bidirectional Live protocol. ([#11710](https://github.com/diegosouzapw/OmniRoute/pull/11710)) — thanks @RaviTharuma +- **fix(kie):** reroute `flux/kontext` off the KIE Market `createTask` flow — it is catalogued with `isMarket: true` but has no Market catalog page, so KIE rejected it with "model name not supported"; it now hits the dedicated `POST /api/v1/flux/kontext/generate` / `GET /api/v1/flux/kontext/record-info` endpoints instead (#11296). ([#11985](https://github.com/diegosouzapw/OmniRoute/pull/11985)) — thanks @hartmark +- **fix(kie):** correct 12 more KIE Market catalog ids that were sent to `createTask` unchanged but diverge from KIE's documented upstream `model` values — GPT Image 2 T2I/I2I (drops the `gpt/` prefix), GPT Image 1.5 T2I/I2I (`gpt-image/` namespace), Seedream 5.0 Lite T2I/I2I (drops the `.0`), all 4 Flux 2 variants (`flux-2/` namespace, generic variant renamed `flex`), and Wan 2.7 Image / Image Pro (dash instead of dot) — each verified individually against the literal example request published on docs.kie.ai. `#11326`'s "everything else already matches" claim was wrong a second time (#11296); `z-image/4.0-*`/`z-image/4.5-*` and `flux/kontext` remain open, documented as unresolved in `KIE_MARKET_UPSTREAM_MODEL_IDS`'s comment pending further verification. ([#11667](https://github.com/diegosouzapw/OmniRoute/pull/11667)) +- **fix(dashboard):** `useApiKeySave.handleSaveApiKey` no longer forces a full upstream `/models` catalog sync on every non-curated provider connection save — callers can now pass `skipModelSync: true` to opt out, so a workflow that only wants to add one manual model no longer floods the provider's available-models list with hundreds/thousands of synced entries. The flag is a client-side intent signal only and is stripped before the connection payload is POSTed to `/api/providers`; default behavior (full sync on save) is unchanged when the flag is omitted (#11324) ([#11664](https://github.com/diegosouzapw/OmniRoute/pull/11664)) +- **Provider connections:** keep `tokenExpiresAt` when a connection is created. The create-path allowlist omitted it, so every insert stored NULL and the dashboard token badge could read a fresh connection as expired until its first background refresh ([#11368](https://github.com/diegosouzapw/OmniRoute/pull/11368)). — thanks @ntdatt812 +- **fix(combos):** the combo builder's precision-select, global-model-search, and manual-entry flows now serialize a model step's `model` string using the provider's already-computed routing-alias prefix (e.g. `oc/`) instead of rebuilding it from the raw canonical `providerId`, fixing the no-auth "OpenCode Free" provider (`opencode`) being routed to the unrelated paid "OpenCode Zen" provider (`opencode-zen`) because `opencode` doubles as a manual routing-prefix override ([#11433](https://github.com/diegosouzapw/OmniRoute/issues/11433)). ([#11670](https://github.com/diegosouzapw/OmniRoute/pull/11670)) +- **fix(radar):** the catalog feed cache now keeps `generatedAt`, the date the feed's data was built, next to `fetchedAt`, the date this install downloaded it (#11435). The feed schema requires that date and the sync path validates it, but the cache dropped it — so a feed fetched minutes ago and one carrying weeks-old figures looked identical to everything downstream, including the dashboard's "Last fetched" line. `getRadarCatalog().meta` and `GET /api/radar/status` now report both dates, the latter as its own field rather than folded into `version` — and omitted entirely for the offers and intel caches, which keep no build date, where a `null` would read as "unknown" rather than "never stored". The dashboard still shows only the fetch time; surfacing the build date there needs a new translated label and is left to a follow-up. Rows cached before migration 163 read back as `null`: unknown stays unknown instead of borrowing the fetch time. The referrals cache has persisted the same date since migration 142. — thanks @maxmad64bis +- **fix(cli):** postinstall no longer fills `JWT_SECRET` and `API_KEY_SECRET` in the installed package's `.env` (#11436). `.env.example` ships both blank on purpose: the server restores them from its durable store, or generates and persists them there, in `ensureSecrets()`. Pre-filling them defeated that — the file lives inside the package directory, so `npm i -g` replaced it and postinstall wrote _different_ values, while `ensureSecrets()` (which only acts on an empty variable) never got to restore the real ones. Both secrets rotated silently on every update, invalidating dashboard sessions and API-key CRCs. `STORAGE_ENCRYPTION_KEY` left the same list for the same reason in #1622; its comment pointed at a function that no longer exists and now names the real provisioning path. — thanks @maxmad64bis +- **fix(cli):** the CLI now says when a loaded `.env` lives inside the installed package directory (#11437). It already announces every env file it reads, without distinguishing the ones that survive an update from the one that does not: `npm i -g` replaces the package directory wholesale, so values set there are gone at the next update, silently. The warning names the durable path to move them to, and fires only when that file actually supplied a value — a file entirely shadowed by a durable one supplied nothing. A development checkout stays silent: there the same path is stable and documented in `SETUP_GUIDE.md`. — thanks @maxmad64bis +- **fix(free-models):** the shared `isFreeModel()` predicate no longer reports catalog entries marked `freeType: "discontinued"` as free, so `hidePaidModels` can't route to Pollinations' seven premium models that now require a paid key ([#11441](https://github.com/diegosouzapw/OmniRoute/pull/11441)) — thanks @maxmad64bis +- fix(ui): let AnySearch use the normal provider-icon fallback when LobeHub has no matching icon (#11449) +- Fixed the v3.8.50 Costs and Analytics dashboards so flat-rate Claude Code usage can be shown as an explicitly requested token-price estimate without changing default billed-cost semantics. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed archived usage retention so each request is priced individually instead of pricing a day's summed tokens once, which understated archived cost whenever a day mixed cache-heavy and ordinary requests. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed the Costs dashboard so it discloses when displayed figures include flat-rate token-price estimates instead of labelling them as billed spend, using the flag the analytics API already returns; the month-end projection and the CSV/JSON exports carry the same marker, and billed-cost mode is unchanged. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- **fix(combo):** attach the same combo-diagnostics trace (`poolSize`/`attemptOrder`/`excluded`/`terminalReason`, plus `x-omniroute-combo-*` headers) to the round-robin strategy's and the nested pipeline/fusion runtime-unit loop's "Maximum combo retry limit reached" 503 that the priority-strategy path already attaches for the identical terminal condition — previously those two paths returned a bare, contextless 503 ([#11462](https://github.com/diegosouzapw/OmniRoute/issues/11462)). ([#11665](https://github.com/diegosouzapw/OmniRoute/pull/11665)) +- **fix(bun):** use native `bun:sqlite` in `bootstrap-env` and `sync-env` to avoid loading `better-sqlite3` N-API addon during Bun startup ([#11468](https://github.com/diegosouzapw/OmniRoute/pull/11468)) — thanks @TheDemonTuan +- **fix(docker-bun):** make `Dockerfile.bun` install reliable with pre-install script helpers and native `bun:sqlite` smoke check ([#11470](https://github.com/diegosouzapw/OmniRoute/pull/11470)) — thanks @TheDemonTuan +- **fix(docker-bun):** remove every vendored `better-sqlite3` native addon from the Bun runtime image so startup cannot abort before the `bun:sqlite` fallback ([#11482](https://github.com/diegosouzapw/OmniRoute/pull/11482)) — thanks @TheDemonTuan +- **fix(autoCombo):** effort/alias model variants (`gpt-5.6-sol-xhigh`, `gpt-5.6`, cursor's `claude-4.6-opus-high`) inherit their base model's task fitness instead of falling to the wildcard 0.5 ([#11492](https://github.com/diegosouzapw/OmniRoute/pull/11492)) — thanks @MumuTW +- fix(db): rate-limit repeated Arena ELO leaderboard fetch-failure warnings instead of logging one per sync attempt (#11500) ([#11989](https://github.com/diegosouzapw/OmniRoute/pull/11989)) — thanks @hartmark +- **fix(db):** dedupe the raw `[Encryption] Decryption failed...` log line emitted by the lazy-decrypt views (`createLazyRowProxy`/`createLazyConnectionView`), which power `getProviderConnections()` and were re-triggering that line on every CredentialHealth/model-sync cycle for the same corrupt or stale-key credential — a fresh Proxy over a fresh row on every cycle meant the per-proxy memoization never suppressed it, unlike the dedup `decryptConnectionFields()` already had since [#9927](https://github.com/diegosouzapw/OmniRoute/issues/9927). Now shares that dedupe tracking so the line logs at most once per credential ([#11500](https://github.com/diegosouzapw/OmniRoute/issues/11500)). ([#11678](https://github.com/diegosouzapw/OmniRoute/pull/11678)) +- **fix(intelligence):** Arena ELO sync stores a synthesized base-model row for every effort/harness variant (`claude-opus-5-max` → `claude-opus-5`, `gpt-5.6-sol-xhigh (codex-harness)` → `gpt-5.6-sol`), so bare model requests reach the synced intelligence layer instead of the static fallback table; the hand-maintained `MODEL_ALIAS_MAP` — which collapsed generations (`gpt-5.5` → `gpt-5`) — is gone ([#11506](https://github.com/diegosouzapw/OmniRoute/pull/11506)) — thanks @MumuTW +- **fix(autoCombo,sse):** catalog hygiene — the static task-fitness table no longer ranks retired models above live ones (dead rows dropped, patterns matched on segment boundaries), the 7 deprecation aliases that forwarded to retired or non-existent models now point at the vendors' published replacements, an alias is no longer rewritten when the serving provider offers the id as-is, and a new `check:model-lifecycle` gate diffs both tables against a vendor lifecycle snapshot ([#11507](https://github.com/diegosouzapw/OmniRoute/pull/11507)) — thanks @MumuTW +- **fix(sse):** stop dropping resolved `thoughtSignature` values on parallel (multi tool-call) turns sent to Gemini 3.x — the claude→gemini and openai→gemini translators previously kept the signature only on the *first* function call of a message, causing Gemini to reject subsequent calls in the same turn with HTTP 400 "Function call is missing a thought_signature"; each function call now keeps its own resolved signature ([#11510](https://github.com/diegosouzapw/OmniRoute/issues/11510)). ([#11669](https://github.com/diegosouzapw/OmniRoute/pull/11669)) +- Treat Docker publishing from a non-default release branch as an expected skip instead of a failed workflow, while preserving the guard that prevents it from overwriting the `next` channel. ([#11527](https://github.com/diegosouzapw/OmniRoute/pull/11527)) — thanks @pacocartones +- fix(search): prefer credentialed providers over duckduckgo-free fallback (#11524) — the fallback-only loop ran before the credentialed-providers loop in `executeWebSearch`, making configured providers unreachable when `duckduckgo-free` was available. ([#11565](https://github.com/diegosouzapw/OmniRoute/pull/11565)) — thanks @vsd2807 +- **fix(sse):** cap the upstream headers-wait phase for STREAMING requests to a client-realistic ceiling (110s, under Codex's own ~120s hard client-abort window) instead of the flat 10-minute `FETCH_TIMEOUT_MS` default — that default was 5x longer than the body-phase readiness watchdog's own adaptive bound, so a request whose upstream never returned any response at all (not even headers, e.g. a stalled NVIDIA target behind a tool-heavy Responses→Chat translation) kept the client connection alive on keepalives only, guaranteeing the client's own patience ran out first with an opaque 499 instead of OmniRoute detecting and failing the stall fast. Non-streaming requests are unaffected — they keep the existing flat default (`open-sse/utils/fetchStartTimeoutPolicy.ts`) (#11526) ([#11668](https://github.com/diegosouzapw/OmniRoute/pull/11668)) +- **fix(ci):** protocol-clients E2E harness boots the peer-stamped custom server (`run-next.mjs dev`) instead of the bare `next` CLI, so `LOCAL_ONLY` locality resolves from the real TCP peer stamp and `/api/mcp/audit` answers 200 (open bootstrap preserved via an `OMNIROUTE_E2E_BOOTSTRAP_MODE=open` credential clear in `run-next.mjs`, pinned to loopback `HOST`) instead of a deterministic 403 (#11535) ([#11549](https://github.com/diegosouzapw/OmniRoute/pull/11549)) — thanks @jonlwheat2-gif +- **fix(docs):** the Auto-Combo engine is described with the number of factors it actually declares. Four different counts were in circulation — 6, 9, 13 and 14 across `AGENTS.md`, four guides and six code comments — while `DEFAULT_WEIGHTS` declares 15. `check:docs-counts` now reads that number from the source, so adding a factor without updating the prose fails CI ([#11545](https://github.com/diegosouzapw/OmniRoute/pull/11545)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **fix(quota-share):** in-flight leases are per request, so an aborted request's slot ages out instead of being kept alive by later traffic on the same connection ([#11547](https://github.com/diegosouzapw/OmniRoute/pull/11547)) — thanks @abhisheksharma2411 +- The free-tier summary route now serves the refreshed Radar catalog when the feed is active — the same numbers the dashboard shows — and states which catalog answered plus its real build date, instead of always reporting release-frozen figures with a stale curation date. The supporter-key live feed stays reserved to authenticated callers of the instance. ([#11550](https://github.com/diegosouzapw/OmniRoute/pull/11550)) — thanks @maxmad64bis +- **sse:** fix LiveWS/embed-WS servers crashing at startup under the Node/tsx runtime — `liveServer.ts`, `embedWsProxy.ts` and `apiBridgeServer.ts` imported `@/shared/utils/httpClientAbortGuard` without the `.mjs` extension, so the client-abort crash guard added by [#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556) was unreachable and every dependent test failed with `ERR_MODULE_NOT_FOUND` ([#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556)). ([#11671](https://github.com/diegosouzapw/OmniRoute/pull/11671)) — thanks @jonlwheat2-gif / @yxyxy +- fix(chat): shed any of the matched provider-node's routing identifiers (public prefix or internal id) before model lookup, so composites like `//` no longer reach upstream verbatim (#11557) — thanks @maxmad64bis +- **fix(cache):** a semantic-cache entry now expires on its TTL instead of surviving until the next UTC midnight, and `dbEntries` stops counting expired rows ([#11573](https://github.com/diegosouzapw/OmniRoute/pull/11573)) — thanks @ntdatt812 +- **fix(api):** `/v1/models` schedules its stale-while-revalidate rebuild through Next's `after()` again, so a stale catalog reaches the client before the rebuild blocks the event loop ([#11574](https://github.com/diegosouzapw/OmniRoute/pull/11574)) — thanks @ntdatt812 +- **fix(check):** the `known-symbols` executor conformance gate awaits the now-async `getExecutor()`, so it stops reporting all 142 registered aliases as dead symbols and can detect a lazy import that fails ([#11582](https://github.com/diegosouzapw/OmniRoute/pull/11582)) — thanks @ntdatt812 +- **fix(pack):** register `bin/cli/utils/volatileEnvPath.mjs` in `PACK_ARTIFACT_REQUIRED_PATHS`, so the prune can no longer silently drop a module the published CLI imports on every boot ([#11588](https://github.com/diegosouzapw/OmniRoute/pull/11588)) — thanks @ntdatt812 +- **i18n(vi):** translate the five reliability-column strings on the free-provider rankings page, which rendered as `__MISSING__:` markers in the Vietnamese UI ([#11589](https://github.com/diegosouzapw/OmniRoute/pull/11589)) — thanks @ntdatt812 +- **resilience:** restore the expired-connection retry-budget probe in the token-health sweep — the `!isGitHubAccessTokenOnlyConnection` carve-out reintroduced by #11608 contradicted the boundary pinned by #11592, so a GitHub connection parked at `expired` with retry budget remaining was never probed and could never self-heal ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)). ([#11672](https://github.com/diegosouzapw/OmniRoute/pull/11672)) — thanks @jonlwheat2-gif / @ntdatt812 +- **fix(dashboard):** Provider Stats now displays combo success rates on their native 0–100 percentage scale instead of multiplying them twice ([#11595](https://github.com/diegosouzapw/OmniRoute/pull/11595)) — thanks @pacocartones +- **fix(providers):** route DuckDuckGo Web Chat through the verified `duck.ai` endpoint, refresh its free model catalog, and avoid forwarding unsolved anti-abuse challenges ([#11597](https://github.com/diegosouzapw/OmniRoute/pull/11597)) — thanks @solstxce +- **fix(ui):** Console log Refresh and Copy controls now expose localized accessible names, keep copy actions visible on keyboard focus, and announce copy completion safely ([#11599](https://github.com/diegosouzapw/OmniRoute/pull/11599)) — thanks @pacocartones +- Dropped the stale-`.eslintcache` `restore-keys` fallback from both "Restore ESLint file cache" steps in `ci.yml`, so the blocking `Lint` job can no longer be served per-file verdicts computed under a different lint config, suppressions file or lockfile. `quality.yml` had already dropped it in #11963; `ci.yml` — the workflow that actually gates PRs — had not (#11600). ([#11996](https://github.com/diegosouzapw/OmniRoute/pull/11996)) +- fix(ci): pass `--pass-on-unpruned-suppressions` in `run-eslint-json.mjs` so the CI Lint job no longer fails when a suppression is merely orphaned by a genuine fix, mirroring the identical fix already in `validate-release-green.mjs` (#11600) ([#11983](https://github.com/diegosouzapw/OmniRoute/pull/11983)) — thanks @hartmark +- fix(handoff): enforce provider allowlist for universal handoff (#11602) — universal handoff now skips summarization when the selected summary model's provider is not included in the configured provider allowlist. ([#11619](https://github.com/diegosouzapw/OmniRoute/pull/11619)) — thanks @vsd2807 +- **fix(dashboard):** Show a stable error state when Search Analytics returns an HTTP error or malformed data ([#11603](https://github.com/diegosouzapw/OmniRoute/pull/11603)) — thanks @pacocartones +- **test(gamification):** pin the aggregate profile level to the XP-derived semantics of #11604 — `getAggregateXp()` now derives `currentLevel` from the summed XP (`calculateLevel(sum)`), not `MAX(stored current_level)`, and the #3484 fixture levels are aligned with the XP curve ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)). ([#11676](https://github.com/diegosouzapw/OmniRoute/pull/11676)) — thanks @jonlwheat2-gif / @pacocartones +- **fix(dashboard):** Keep the Profile level and progress aligned with aggregate XP, including bounded handling for invalid totals ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)) — thanks @pacocartones +- **fix(dashboard):** Prevent locked hidden badges from revealing their icon or opening private badge details before they are earned ([#11605](https://github.com/diegosouzapw/OmniRoute/pull/11605)) — thanks @pacocartones +- **fix(dashboard):** Restore keyboard focus after shared modals close and cancel delayed autofocus during cleanup ([#11607](https://github.com/diegosouzapw/OmniRoute/pull/11607)) — thanks @pacocartones +- **fix(dashboard):** Enable Enter and Space activation for clickable data-table rows without hijacking nested controls ([#11610](https://github.com/diegosouzapw/OmniRoute/pull/11610)) — thanks @pacocartones +- **fix(autoCombo,sse):** vendor-retired catalog ids are dropped from the auto-combo candidate pool and no longer win on leftover `arena_elo` / `user_override` rows; `getModelLifecycleDecision` consults `model-lifecycle.json` (prefix-stripped) so aggregator traffic is not `untracked` for ids the snapshot already knows ([#11625](https://github.com/diegosouzapw/OmniRoute/issues/11625)) ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)) — thanks @MumuTW +- **test(chatcore):** move Codex/Claude combo fixtures off models the lifecycle guard now rejects — `gpt-5.1-codex`/`gpt-5-codex` are vendor-retired (snapshot, #11626) and `claude-3-5-sonnet-20241022` is shut down, so native-passthrough and combo-fallback tests switched to `gpt-5.6-sol` and `claude-sonnet-4.6` ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)). ([#11675](https://github.com/diegosouzapw/OmniRoute/pull/11675)) — thanks @jonlwheat2-gif / @MumuTW +- fix(opencode-plugin): stop a free-tier budget that rounds up across a magnitude from rendering as `1000K`/`1000M` in the model picker — `fmtTokens` chose its unit from the raw token count and then rounded with `toFixed(1)`, so 999,950–999,999 printed as `1000K` rather than `1M` and just under a billion printed as `1000M` rather than `1B` ([#11684](https://github.com/diegosouzapw/OmniRoute/pull/11684)) — thanks @abhisheksharma2411 +- Cap prose-derived Antigravity quota resets at the configured model cooldown maximum while preserving authoritative Retry-After headers and Google RetryInfo hints. (#11695) Thanks @pacocartones. ([#11823](https://github.com/diegosouzapw/OmniRoute/pull/11823)) +- Compare `_rebaseline_*` notes structurally in the ratchet-bank verifier so an object-valued note no longer reports itself as rewritten on an unmodified tree — the nightly `bank-ratchet-shrinks` lane had been aborting on every run since 2026-08-11, leaving 41,672 lines of earned file-size shrinks unbanked (#11702). ([#11892](https://github.com/diegosouzapw/OmniRoute/pull/11892)) — thanks @MumuTW +- **fix(build):** `prepublish.ts` bundles the ChatGPT Web (Codex) MCP bridge through `runBuildTool()` instead of spawning `npx.cmd` raw, fixing the build crash on Node ≥ 20/Windows where `.cmd` shims cannot be spawned without a shell (EINVAL) ([#11704](https://github.com/diegosouzapw/OmniRoute/issues/11704)) ([#11706](https://github.com/diegosouzapw/OmniRoute/pull/11706)) — thanks @jonlwheat2-gif +- fix(codex): keep `parallel_tool_calls:false` on the translated Codex Responses Lite path (#11707) ([#11984](https://github.com/diegosouzapw/OmniRoute/pull/11984)) — thanks @hartmark +- **fix(tests):** the new Gemini Web Images retirement test (#11708) called the now-async `getExecutor()` synchronously, throwing `TypeError: getExecutor(...).getProvider is not a function` instead of asserting Gemini Web chat stays available. Awaited to match the R0.3 executor-registry refactor (#11220). ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(search):** Allow REST and MCP web-fetch requests to select the advertised `anysearch-search` provider explicitly ([#11716](https://github.com/diegosouzapw/OmniRoute/pull/11716)) — thanks @pacocartones +- **fix(providers):** Keep overnight peak-hour protection active after midnight according to the window's configured start day ([#11718](https://github.com/diegosouzapw/OmniRoute/pull/11718)) — thanks @pacocartones +- **fix(providers):** correct all five g4f.space gateway descriptions to document proof-of-work credits or member API keys, variable limits, and the remote prompt boundary instead of promising free keyless access at five requests per minute ([#11738](https://github.com/diegosouzapw/OmniRoute/pull/11738)). +- **fix(db):** `model_capabilities` is created by a migration instead of lazily on the first models.dev sync, so a clean install and an upgraded install converge on the same schema regardless of which features have run ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(ci):** `check:install-upgrade` now fails on an `npm` install truncated by ENOSPC (npm reports it as a warning and still exits 0), authenticates its health probe so the version assertion works against the hardened health payload, frees the clean-install tree before the upgrade phase, and no longer reports a schema divergence computed from a boot that never served ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(routing):** harden session-affinity key derivation against oversized explicit IDs and empty structural-payload collisions, while recognizing Gemini and common text request shapes ([#11746](https://github.com/diegosouzapw/OmniRoute/pull/11746)) — thanks @MumuTW +- **fix(packages/browser-pool):** regenerate `package-lock.json` so the `packages/browser-pool` workspace's locked `playwright`/`@types/node` (and transitives) match its `package.json` specs, fixing cache-only/offline installs (`npm ci --offline`, Nix `buildNpmPackage`) that previously failed with `ENOTCACHED` ([#11747](https://github.com/diegosouzapw/OmniRoute/issues/11747)) — thanks @benjaminkitt ([#11784](https://github.com/diegosouzapw/OmniRoute/pull/11784)) — thanks @geekyNads +- **fix(assets):** Stop distributing 79 provider and CLI logos without verified source/license provenance, keep every provider/tool available through the internal generic icon, and prevent those IDs from falling through to an external icon CDN. ([#11750](https://github.com/diegosouzapw/OmniRoute/pull/11750)) +- **fix(providers):** retire the common `chatgpt-web` / `cgpt-web` provider because its pre-key and proof-of-work provenance could not be cleared ([#11754](https://github.com/diegosouzapw/OmniRoute/pull/11754)) — explicit chat and image requests, slash-prefixed models, persisted aliases, proxy overrides, and combo targets now fail closed with a sanitized HTTP `410` / `PROVIDER_RETIRED` before upstream dispatch. Migration 163 tombstones matching connections and invalidates their active leases without deleting history or widening API-key allowlists. The independent `chatgpt-web-codex` / `cgpt-codex` provider and its MIT-noticed vendored implementation remain available. +- **fix(usage):** quota and usage refresh no longer 409 when an exclusive lease reserves the connection ([#11758](https://github.com/diegosouzapw/OmniRoute/pull/11758)) — thanks @TheDemonTuan +- Keep the embedding registry's vector width and `embedding` type on models when a synced model exists for the same id, so `/v1/models` no longer reports registry-described embedding models widthless or untyped (#11761) — thanks @rahilmavani +- Correct `google/gemini-embedding-001` on the OpenRouter route to 3072 dimensions, the width it returns when `dimensions` is not sent (#11761) — thanks @rahilmavani +- **fix(vision-bridge):** Restrict automatic vision routing and fallbacks to models available in authoritative live provider catalogs, including aliases and effort variants ([#11767](https://github.com/diegosouzapw/OmniRoute/issues/11767)) — thanks @pacocartones ([#11813](https://github.com/diegosouzapw/OmniRoute/pull/11813)) +- **fix(providers):** reclassify Cerebras as a one-time $5 signup credit (payment method required, 30-day validity), not a recurring no-card 1M tokens/day trial ([#11773](https://github.com/diegosouzapw/OmniRoute/issues/11773)) ([#12591](https://github.com/diegosouzapw/OmniRoute/pull/12591)) — thanks @HouMinXi +- fix(cli): stop prepublish from re-rebuilding the already-built ESM-only opencode-plugin dist (#11787) ([#11990](https://github.com/diegosouzapw/OmniRoute/pull/11990)) — thanks @hartmark +- **fix(db):** Fresh installs no longer log a non-fatal `no such table: compression_run_telemetry` warning when retention cleanup runs before the lazily-created telemetry table exists ([#11802](https://github.com/diegosouzapw/OmniRoute/pull/11802)) — thanks @RaviTharuma +- fix(sse): set X-OmniRoute-Selected-Connection-Id on successful combo dispatches so downstream consumers stop falling back to an empty connection id (#11810) ([#11986](https://github.com/diegosouzapw/OmniRoute/pull/11986)) — thanks @hartmark +- **fix(services):** embedded CLIProxyAPI lifecycle checks now use public `/healthz`, while model discovery uses the configured dedicated data-plane API key instead of the management password ([#11811](https://github.com/diegosouzapw/OmniRoute/pull/11811)) — thanks @RaviTharuma +- Fix persisted model steps returning an error in the playground route simulator, and warn when structural steps cannot be simulated. ([#11862](https://github.com/diegosouzapw/OmniRoute/pull/11862)) — thanks @pacocartones +- **fix(providers):** Antigravity's dynamic mitmAlias table no longer routes `gemini-3.7-flash-{high,medium,low}` to a literal tier-suffixed upstream id just because one connected account's own discovery listed it directly — those display ids always resolve through the safe `gemini-3.7-flash-tiered` static alias, so one account's Google-provisioned access no longer 404s every sibling account of the provider ([#11824](https://github.com/diegosouzapw/OmniRoute/issues/11824), [#11651](https://github.com/diegosouzapw/OmniRoute/issues/11651)) ([#11988](https://github.com/diegosouzapw/OmniRoute/pull/11988)) — thanks @hartmark +- **fix(dashboard):** Expose Profile loading, errors, page structure, and XP progress to assistive technologies ([#11838](https://github.com/diegosouzapw/OmniRoute/pull/11838)) — thanks @pacocartones +- **fix(socks):** `Agent.connectTimeout` now bounds both SOCKS handshake and TLS connect, and `family === null` no longer falls back to `fetch-socks` ([#11842](https://github.com/diegosouzapw/OmniRoute/pull/11842)) — thanks @maxmad64bis +- **perf(compression):** OOM mitigations for large payload hashing, memoization, and token estimation ([#11844](https://github.com/diegosouzapw/OmniRoute/pull/11844) — thanks @AndrianBalanescu) +- Excluded Next.js Node File Trace manifests (`*.nft.json`) from the published npm tarball. They are build-time metadata and are never read while serving, but had grown to 668.7 MB — 61% of the package — which pushed the upload past the registry limit and made `npm publish` fail with `413 Payload Too Large`. ([#11864](https://github.com/diegosouzapw/OmniRoute/pull/11864)) +- **fix(cli):** keep the global OmniRoute server `--base-url` separate from provider-node `--endpoint` values in add, update, and validate commands ([#11860](https://github.com/diegosouzapw/OmniRoute/pull/11860)) — thanks @pacocartones +- **fix(config):** Nous Research's `Hermes-4-405B` model now displays as "Hermes 4 405B (Nous Research)" in both the provider registry and the free-model catalog, instead of the mislabelled "Hermes 4 7B" ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#11993](https://github.com/diegosouzapw/OmniRoute/pull/11993)) +- **fix(provider/nous):** inject required user= tag into Nous Research inference requests to resolve upstream 400 "missing tags" error ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#12044](https://github.com/diegosouzapw/OmniRoute/pull/12044)) +- **fix(build):** `npm run build` now fails in one second with a named package and a copy-pasteable fix when npm silently drops an externalised optional native dependency, instead of dying four minutes in with `Module not found: Can't resolve 'better-sqlite3'` ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- **fix(install):** `postinstall` no longer throws `ReferenceError: isAndroid is not defined` — failing the whole `npm install` — when the `better-sqlite3` rebuild fallback times out; the manual-fix guidance is reachable again ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- Fixed the Alibaba free-tier allowlist test that went red on its own once the shipped catalog's `validUntil` (2026-08-27) passed, leaving every PR and `main` with a failing `Unit Tests (1/8)`. The test now builds its own packs with dates it controls, and covers the expired-pack fallback that production has actually been serving. ([#11867](https://github.com/diegosouzapw/OmniRoute/pull/11867)) +- Split the npm registry upload into its own GitHub-hosted job. npm refuses `--provenance` from a self-hosted runner (`422 ... Only "github-hosted" runners are supported`), which blocked the v3.8.50 publish; the heavy verification cannot move to a hosted runner, so it now hands the proven tarball over instead. ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(ui):** the shared `DataTable` loading state no longer reads its decorative ⏳ glyph out to assistive technology, and now carries the same `role="status"` / `aria-live="polite"` / `aria-busy="true"` semantics as `PageLoading` ([#11869](https://github.com/diegosouzapw/OmniRoute/pull/11869)) — thanks @pacocartones +- **fix(api):** Bound the `timeout` accepted by `POST /api/providers/[id]/login` to the same 15s–600s window the Adobe Firefly and Conol browser-login paths already enforce, so a request can no longer pin a headful browser and the single active-login slot with an arbitrarily large poll budget. ([#11871](https://github.com/diegosouzapw/OmniRoute/pull/11871)) — thanks @pacocartones +- **fix(api):** Stop the tunnel and MITM routes from returning raw child-process error text, which disclosed host paths, binary install locations and Tailscale `tskey-*` credentials in a body some of these routes serve to non-loopback callers, and return a real 400 instead of a framework 500 when a tunnel request body fails validation. ([#11872](https://github.com/diegosouzapw/OmniRoute/pull/11872)) — thanks @pacocartones +- **fix(open-sse):** stream TTFT/ITL are sampled from a monotonic clock (`performance.now`) instead of `Date.now`, so an NTP correction or wall-clock jump can no longer inflate a genuine TTFT or produce a negative one that the `ttft >= 0` guard silently drops — protecting the router's OTel/EWMA/`usage_history`/speed-ranking signals ([#11873](https://github.com/diegosouzapw/OmniRoute/pull/11873)) — thanks @pacocartones +- **fix(providers):** xAI `reasoning_effort: "xhigh"` now reaches grok-4.6+ instead of being silently clamped to `"high"` ([#11879](https://github.com/diegosouzapw/OmniRoute/pull/11879)) — thanks @NoxzRCW +- **fix(dashboard):** the providers page no longer crashes into the error boundary when a provider id collides with an `Object.prototype` member (`constructor`, `__proto__`); icon lookups are own-property guarded ([#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)) — thanks @NoxzRCW +- **fix(skills):** injected skill tools declared in shorthand (`{"content": "string"}`) now forward valid JSON Schema, unblocking providers that validate tool schemas strictly such as Zhipu GLM on the Console Go tier ([#11881](https://github.com/diegosouzapw/OmniRoute/pull/11881)) — thanks @NoxzRCW +- **fix(api):** `POST /api/playground/simulate-route` now surfaces `combo-ref` and `provider-wildcard` persisted combo steps with a specific warning (naming the referenced combo, or the unresolved `provider/model` wildcard) instead of folding them into a generic "unsupported step" count; a `provider-wildcard` step is also now included as an unresolved target so the operator can see it is in the route (ported from [#11882](https://github.com/diegosouzapw/OmniRoute/pull/11882) — thanks @NoxzRCW). ([#11930](https://github.com/diegosouzapw/OmniRoute/pull/11930)) +- **fix(translator):** the streaming OpenAI→Claude translator keeps upstream usage, including prompt-cache tokens, when it arrives on a trailing `choices: []` chunk (Fireworks and any upstream using `stream_options.include_usage`) ([#11883](https://github.com/diegosouzapw/OmniRoute/pull/11883)) — thanks @NoxzRCW +- **fix(cli):** `omniroute update --apply` now tells you explicitly whether a running server was detected and, if so, that you must run `omniroute restart` to apply the update — it never restarted anything and previously implied the update was already live once files were installed. The dashboard's npm-mode Update flow (`/api/system/version`) now tries OmniRoute's own PID-file-managed supervisor before falling back to pm2, and reports an honest "restart required" step instead of a silent pm2-only "skipped" that read like a completed update. The server-side latest-version lookup backing the dashboard's update banner also gained `--prefer-online`, closing the same stale-npm-cache class already fixed in the CLI's own copy for #4376 ([#11885](https://github.com/diegosouzapw/OmniRoute/issues/11885)). ([#12005](https://github.com/diegosouzapw/OmniRoute/pull/12005)) +- **fix(resilience):** clear persisted LKGP pins when a target suffers connection/provider exhaustion or is skipped before dispatch due to cooldown/exhaustion/unavailability, preventing subsequent requests from repeatedly prioritizing known-dead providers ([#11911](https://github.com/diegosouzapw/OmniRoute/issues/11911)). ([#12013](https://github.com/diegosouzapw/OmniRoute/pull/12013)) — thanks @HouMinXi +- **fix(sse):** the OpenAI→Claude stream translator now defers the terminal `message_delta`/`message_stop` emission until the real usage block has arrived (or a genuine end-of-stream flush forces it) instead of emitting it immediately on `finish_reason` — previously, when the trailing usage-only chunk (`{"choices":[],"usage":{...}}`) arrived *after* the `finish_reason` chunk (the normal order for Fireworks/vLLM/Together and other `stream_options.include_usage` upstreams), the client-visible `message_delta` still carried stale/zero usage even though `state.usage` was internally corrected too late to matter (ported from [#11915](https://github.com/diegosouzapw/OmniRoute/pull/11915) — thanks @HouMinXi). ([#11933](https://github.com/diegosouzapw/OmniRoute/pull/11933)) +- **fix(sse):** `stripStore()` now forces `store=false` for stateless OpenAI-compatible Responses-API targets (unless the connection has explicitly opted in via `providerSpecificData.openaiStoreEnabled`), instead of only handling the `openai`/`agentrouter` cases — a client-supplied `store` value previously passed through untouched to backends that don't actually persist responses server-side ([#11916](https://github.com/diegosouzapw/OmniRoute/pull/11916) — thanks @HouMinXi). +- **fix(models):** custom provider-node models (synced, custom, and alias-backed) now appear under their configured prefix in the unified catalog when the operator's model-id prefix mode is canonical, instead of being dropped whenever alias-inclusion was otherwise disabled ([#11918](https://github.com/diegosouzapw/OmniRoute/pull/11918) — thanks @HouMinXi). +- **fix(models):** the unified model catalog now suppresses stale static registry models (including effort-tier variants) for any provider whose active connection has an authoritative live synced catalog, not only providers already using exclusive-synced-listing — a connection with `providerUsesAuthoritativeLiveCatalog` previously kept serving both the live-synced models and the stale static rows side by side ([#11919](https://github.com/diegosouzapw/OmniRoute/pull/11919) — thanks @HouMinXi). +- **fix(ui):** `ProviderIcon`'s three remaining unguarded lookups (`PROVIDER_ICON_ALIASES`, `LOCAL_SVG_ALIASES`, `THEMED_SVGS`) now use `Object.hasOwn()` own-property checks — a provider id such as `constructor` or `__proto__` previously resolved through the prototype chain instead of falling through to the unknown-provider CDN fallback (`getLobeProviderIcon()` itself was already guarded by [#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)); `ProviderPageHeader`'s `color` field is also now optional, matching the rest of the component's defensive typing (ported from [#11920](https://github.com/diegosouzapw/OmniRoute/pull/11920) — thanks @HouMinXi). ([#11935](https://github.com/diegosouzapw/OmniRoute/pull/11935)) — thanks @NoxzRCW +- fix(ollama): preserve multi-byte UTF-8 content split across stream chunks in the Ollama NDJSON transform, which previously corrupted CJK/emoji into U+FFFD (#11921) — thanks @pacocartones +- **fix(providers):** OrcaRouter chat requests now target `/v1/chat/completions` instead of the bare `/v1` API root, fixing the upstream `404 Invalid URL (POST /v1)` ([#11923](https://github.com/diegosouzapw/OmniRoute/pull/11923)). — thanks @echel0nn +- **fix(plugins):** deliver the `onStreamComplete` event to disk-installed plugins. The event shipped in v3.8.50 (#9669) was emitted internally but had no plugin-facing wiring, so no plugin could ever subscribe: the manifest schema silently dropped `hooks.onStreamComplete`, and the loader/manager only knew the seven legacy hooks. `onStreamComplete` is now a declarable manifest hook, wired through the loader and registered by the manager like the other hooks, and its payload carries a `requestId` so consumers can correlate the stream-completion event with the originating request ([#11934](https://github.com/diegosouzapw/OmniRoute/pull/11934)) — thanks @amaleta / @pacocartones +- **fix(db):** the Qdrant embedding-model dropdown now lists local/self-hosted providers (Ollama, LM Studio, vLLM, etc.) — an active connection is treated as "configured" when the provider allows an optional API key, not only when it has a real key or OAuth, so a running local embedding provider is no longer hidden from the picker ([#11949](https://github.com/diegosouzapw/OmniRoute/issues/11949)) ([#11995](https://github.com/diegosouzapw/OmniRoute/pull/11995)) +- **fix(providers):** Vertex AI Anthropic partner-model discovery now calls the Model Garden `v1beta1` publisher list (`/v1beta1/publishers/anthropic/models`, global) and parses its `publisherModels` envelope, so Claude models auto-synced from Vertex populate the active live catalog and route at request time instead of returning `Model '' is not available in the active live catalog` ([#11991](https://github.com/diegosouzapw/OmniRoute/issues/11991)) — thanks @fabioluissilva ([#11998](https://github.com/diegosouzapw/OmniRoute/pull/11998)) +- **fix(cli):** support `--base-url` alongside `--endpoint` in `omniroute nodes add`, `update`, and `validate` subcommands to prevent global `--base-url` shadowing issues ([#11999](https://github.com/diegosouzapw/OmniRoute/issues/11999)). ([#12033](https://github.com/diegosouzapw/OmniRoute/pull/12033)) — thanks @HouMinXi +- **fix(providers):** `cloudflare-ai` no longer refuses image content parts for every Workers AI model ([#12002](https://github.com/diegosouzapw/OmniRoute/pull/12002)) — the plain-string `content` requirement behind #2539 is carried by the _model_ schema, not by the `/ai/v1/chat/completions` endpoint (measured: an all-text part array returns 200 on `@cf/mistralai/mistral-small-3.1-24b-instruct`, `@cf/meta/llama-4-scout-17b-16e-instruct` and `@cf/meta/llama-3.3-70b-instruct-fp8-fast`, and 400 on the text-only `@cf/qwen/qwen2.5-coder-32b-instruct`). `transformRequest()` flattened every array and threw on the first non-text part (#6390), so image input was refused for vision-capable Cloudflare models that accept it. All-text arrays are still flattened — the one shape every model accepts — while an array carrying a non-text part is passed through untouched, so the attachment is still never silently dropped. Regression guards: `tests/unit/cloudflare-ai-image-parts-6390.test.ts`. — thanks @davidlinfr +- **fix(resilience):** decouple the limiter-managed execution backstop from the queue-wait budget — new `requestQueue.executionMaxWaitMs` (env `RATE_LIMIT_EXECUTION_MAX_WAIT_MS`, default 600000 = 10 min) now feeds Bottleneck's post-dispatch `expiration`, while `requestQueue.maxWaitMs` keeps its documented queue-wait semantics. Previously the queue-wait budget doubled as the execution expiration, so legitimate long-running calls on non-incremental gateways (whole generation buffered before the first upstream byte, e.g. Console Go / Command Code tiers serving GLM models) were killed mid-flight at the queue budget with a false 504 `RATE_LIMIT_EXECUTION_TIMEOUT` — the local limiter undercut the provider-aware upstream fetch-start timeouts. The surfaced 504 message now names `requestQueue.executionMaxWaitMs`; the error keeps the #4165 guarantees (disclaims an upstream timeout, preserves the Bottleneck error as `cause`, branded code + trusted provenance, classified request-scoped so combo falls back). A real queue-wait bound (the `Promise.race` around `limiter.schedule()` sketched in #9533) remains future work. (#12025) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **Call logs:** keep the `error` field when an artifact exceeds the storage cap, instead of replacing it with the omission marker. The error is the only field that says *why* a request failed and is typically ~90 bytes next to the multi-hundred-KB bodies that trip the cap, so dropping it left a size-limited row undiagnosable — a provider outage, a local timeout and an upstream 400 all rendered identically. It is now preserved at every fallback stage, truncated to 4KB if it is itself large ([#12026](https://github.com/diegosouzapw/OmniRoute/issues/12026)). ([#12095](https://github.com/diegosouzapw/OmniRoute/pull/12095)) — thanks @ntdat812 +- **fix(diagnostics):** preserve the error field (truncated to 4KB with a `[truncated: …]` suffix) in every call-log artifact size-limit fallback stage. Previously the minimal fallback replaced the error with `[omitted: call log artifact size limit exceeded]`, so an oversized artifact row showed nothing about WHY the request failed — e.g. 91 of 847 opencode-go 504 rows on one production instance were undiagnosable from the dashboard. Oversized request/response bodies are still omitted exactly as before; the error cap is independent of the payload sizes that tripped the fallback. (#12026) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **fix(sse):** OpenAI Responses clients that declare the native `web_search` tool now receive a spec-shaped `web_search_call` output item with `action.sources` alongside the preserved function-call round-trip, so search results executed through OmniRoute's own search backend are consumable by standard Responses clients (Codex, pi-web-access, …). ([#12031](https://github.com/diegosouzapw/OmniRoute/pull/12031)) — thanks @watchingdogs +- **fix(cli):** use in-thread alias resolver hooks on modern runtimes to avoid deprecation noise and improve Node.js forward compatibility ([#12073](https://github.com/diegosouzapw/OmniRoute/issues/12073)). ([#12083](https://github.com/diegosouzapw/OmniRoute/pull/12083)) — thanks @xiaoyaner0201 +- **fix(combo):** an operator-set **Agent Features → Context length** on a combo is now honored at request time. The value was persisted and advertised through `/v1/models`, but `resolveComboContextLimit()` never consulted it — so a multi-target combo whose members carry no per-model window fell through to the provider's generic `defaultContextLength` (openrouter 128000, command-code 200000) and rejected large requests with `Input exceeds context window … limit 128000` despite the combo being explicitly sized much larger. An identical single-target combo worked, because it collapses to its concrete target before the guard runs. Invalid values (0/negative/NaN/Infinity) are ignored, so the existing target → combo-min → fallback order is unchanged. ([#12090](https://github.com/diegosouzapw/OmniRoute/pull/12090)) — thanks @adivekar-utexas +- **fix(sse):** passthrough streams now estimate usage on finish when upstream closes without usage even with `stream_options.include_usage` — avoids `0 tokens / 0%` for providers that stay silent (and correctly handles trailing empty-choices usage) ([#12151](https://github.com/diegosouzapw/OmniRoute/pull/12151)) — thanks @maxmad64bis +- **fix(combos):** clearing an agent feature in the combos editor now persists — unchecking context cache protection, or emptying the system message or tool filter, sends an explicit `null` instead of dropping the field from the `PUT` body, which the update merge read as "leave unchanged" ([#12177](https://github.com/diegosouzapw/OmniRoute/pull/12177)) — thanks @foreveryh / @ntdatt812 +- **fix(memory):** self-hosted embedding endpoints now vectorize — the vector width is measured from the first embedding that comes back instead of being read from a registry that cannot describe them, so `vec_memories` is created and memories stop piling up unvectorized behind a green health check ([#12180](https://github.com/diegosouzapw/OmniRoute/pull/12180)) — thanks @kanade-hoshino / @ntdatt812 +- **fix(translator):** the leading `system` message now reaches Responses-API upstreams when its `content` is a content-part array — it was read as `typeof content === "string" ? content : ""`, so a prompt-caching client (Anthropic `cache_control`, the shape LiteLLM and the Anthropic SDK emit) had its entire system prompt replaced by an empty `instructions`. The request was still accepted with a normal `prompt_tokens` count, so the model answered with no instructions and nothing in the response said they were missing. Mid-conversation system turns already handled the array shape ([#7056](https://github.com/diegosouzapw/OmniRoute/pull/7056)); only the first one did not ([#12206](https://github.com/diegosouzapw/OmniRoute/issues/12206)). Regression guard: `tests/unit/translator-openai-responses-system-content-parts.test.ts`. ([#12207](https://github.com/diegosouzapw/OmniRoute/pull/12207)) — thanks @Gorillaz322 +- **fix(free-tier):** `/api/free-tier/summary` no longer computes its totals from a Radar feed built before the catalog the running release ships. When the cached feed is older — or carries no build date at all — the route answers from the shipped catalog, resolved through the operator's local model state so disabled and tombstoned models stay out of the numbers ([#12215](https://github.com/diegosouzapw/OmniRoute/pull/12215)). — thanks @maxmad64bis +- **fix(oauth):** Keep a Claude personal workspace and a Team organization as separate connections — they share the same email and `accountUUID`, so the email-only OAuth dedup let the second login overwrite the first account's tokens; `organizationUUID` now disambiguates them, the way `workspaceId` does for Codex ([#12222](https://github.com/diegosouzapw/OmniRoute/pull/12222)) — thanks @davidebaraldo +- **fix(resilience):** a 402 on a single paid model of a passthrough/gateway provider (e.g. `kilo-gateway`, `ollama-cloud`) no longer terminalizes the whole connection with a never-auto-recovered `credits_exhausted` status — only the paid model is locked out, so free models on the same key keep serving. 402 variant of [#3027](https://github.com/diegosouzapw/OmniRoute/issues/3027). Single-credential providers are unaffected — a 402 there is still treated as the key being genuinely out of credit ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239) / [#10616](https://github.com/diegosouzapw/OmniRoute/issues/10616)) ([#12242](https://github.com/diegosouzapw/OmniRoute/issues/12242)) — thanks @brick30llc-ctrl ([#12266](https://github.com/diegosouzapw/OmniRoute/pull/12266)) — thanks @geekyNads +- **fix(sse):** trust `finish_reason: "length"`/`"max_tokens"` over the reasoning-consumed-token ratio in response quality validation, so a reasoning model truncated below the old 90% threshold correctly fails and retries instead of returning empty content as a silent "success" ([#12262](https://github.com/diegosouzapw/OmniRoute/pull/12262)) — thanks @brick30llc-ctrl +- **fix(settings):** `PUT /api/settings/cache-config` now persists `alwaysPreserveClientCache` to the flat general settings the runtime cache-control policy actually reads; previously the value landed in the databaseSettings "cache" section and was silently ignored, so the endpoint had no effect on `cache_control` passthrough ([#12304](https://github.com/diegosouzapw/OmniRoute/pull/12304)) — thanks @davidebaraldo +- **fix(grok-cli):** treat omitted SuperGrokPro `creditUsagePercent` as 0% used so Provider Limits still renders a weekly bar (proto3 zero-elision) ([#12312](https://github.com/diegosouzapw/OmniRoute/pull/12312)) — thanks @HouMinXi +- **fix(combo):** Expose the two Auto-Combo scoring factors nobody could set — the combo validation schema and the dashboard weight sliders both declared 13 of the scorer's 15 factors, so `connectionDensity` (spreads load across a provider's connections) and `quality` were dropped on save and offered nowhere. The sliders also shipped their own default table that differed from the engine's on every non-zero factor and summed to 1.05, so the percentages shown next to them added up to 105%. Both lists now match `DEFAULT_WEIGHTS`, and a test keeps them there. Note that a combo whose stored `weights` omitted the two keys was effectively running with them at zero and the other thirteen renormalized upward; it now runs with the engine's intended distribution, so its routing does shift ([#12314](https://github.com/diegosouzapw/OmniRoute/pull/12314)) — thanks @maxmad64bis +- **fix(docs):** The free-tier reference no longer says its numbers come "confidence tagged per row" — no catalog entry carries a confidence tag and the API serves none, so every figure on that page is an estimate of the same, unstated quality. The page now states what an entry does vouch for: an independently documented hard stop (set by hand with the source in a comment, never defaulted to `true`) and a prompt-training disclosure, both with live counts the `check:docs-counts` gate keeps honest ([#12318](https://github.com/diegosouzapw/OmniRoute/pull/12318)) — thanks @maxmad64bis +- **fix(usage):** `adobe-firefly` and `firefly` have had a working usage fetcher since Adobe Firefly landed, but neither was ever added to the registration list, so the provider-plugin manifest, `genericQuotaFetcher` and the free-access quota cache all reported them as having no usage support — while `USAGE_SUPPORTED_PROVIDERS` said the opposite. Both are now declared, which also means their credit balance is fetched like any other declared provider's: `registerGenericQuotaFetchers` now registers a generic quota fetcher for them, and `resolveFreeAccessState` no longer returns early. A test holds the registration list to the dispatcher's switch in both directions, which is what the module's own docstring already asked for in prose ([#12321](https://github.com/diegosouzapw/OmniRoute/pull/12321)) — thanks @maxmad64bis +- **fix(quota):** drop the generic quota cache (agy / Antigravity / Claude OAuth) on an upstream 429 so reset-aware scoring does not keep a 60s stale snapshot, and force-refresh the next usage fetch so inner provider caches cannot recache the same window ([#12325](https://github.com/diegosouzapw/OmniRoute/pull/12325)) — thanks @HouMinXi +- **fix(combos):** deleting a combo now clears its persisted LKGP pins instead of leaving unreachable `key_value` rows behind ([#12326](https://github.com/diegosouzapw/OmniRoute/issues/12326)) ([#12425](https://github.com/diegosouzapw/OmniRoute/pull/12425)) — thanks @KrzysiekSko +- **fix(sse):** Keep ZWNJ (U+200C) and ZWJ (U+200D) in assistant text, reasoning and tool-call arguments — Persian/Kurdish half-space (`ارائه‌دهنده`), Arabic/Indic shaping and emoji sequences no longer lose them; the response de-obfuscation now removes joiners only between ASCII word characters, where the request side inserts them ([#12186](https://github.com/diegosouzapw/OmniRoute/issues/12186)) — thanks @rezjalibd ([#12359](https://github.com/diegosouzapw/OmniRoute/pull/12359)) — thanks @pacocartones +- **fix(resilience):** count resolved upstream 5xx results against the provider circuit breaker on the chat path — `CircuitBreaker.execute()` no longer reads a resolved `{ success: false, status: 5xx }` as a success that cancels the call-site failure, so a provider answering 503s now trips its breaker instead of staying `CLOSED` at `failureCount: 1`; single-model and combo dispatches are each accounted exactly once ([#12254](https://github.com/diegosouzapw/OmniRoute/issues/12254)) ([#12360](https://github.com/diegosouzapw/OmniRoute/pull/12360)) — thanks @pacocartones +- **fix(providers):** resolve the Codex quota auto-ping model from the live provider catalog and lifecycle registry instead of the retired `gpt-5.1-codex-mini`, and pause the ping with one actionable warning when no selectable Codex model exists rather than retrying a shut-down id every cooldown window ([#11905](https://github.com/diegosouzapw/OmniRoute/issues/11905)) ([#12361](https://github.com/diegosouzapw/OmniRoute/pull/12361)) — thanks @pacocartones +- **fix(api):** keep the `{created, data}` wrapper on combo-routed `/v1/images/generations` responses and default Codex image results to `b64_json` on both `/v1/images/generations` and `/v1/images/edits` so Codex CLI's built-in `image_gen` can decode them ([#12268](https://github.com/diegosouzapw/OmniRoute/issues/12268)) ([#12362](https://github.com/diegosouzapw/OmniRoute/pull/12362)) — thanks @pacocartones +- **fix(sse):** Name the shadowed custom provider node when a built-in provider id/alias (e.g. `openference` → `of`) reserves the prefix of an existing OpenAI/Anthropic-compatible node, so the runtime `No active credentials for provider: ` error explains that the prefix routed to the built-in and never reached the node's healthy connections, instead of contradicting the dashboard ([#11943](https://github.com/diegosouzapw/OmniRoute/issues/11943)) — thanks @morpheus9393 ([#12365](https://github.com/diegosouzapw/OmniRoute/pull/12365)) — thanks @pacocartones +- **fix(i18n):** wrap `ccOnboardingKeyPlaceholder` in ICU single quotes across all 43 locales so angle brackets render literally instead of being parsed as rich-text tags, which crashed the Claude Code onboarding block with `INVALID_MESSAGE: INVALID_TAG` ([#12302](https://github.com/diegosouzapw/OmniRoute/issues/12302)) ([#12369](https://github.com/diegosouzapw/OmniRoute/pull/12369)) — thanks @gonisulaimann +- **fix(models):** publish `effort_tiers` on Kimi K3's synced base-model entries (`kmca/k3`, `kmca/k3-256k`) so catalog-only clients (OpenCode, plain SDK pickers) can see and select the reasoning tiers (`low`/`high`/`max`) the synced metadata already carried — the `isSkippedEffortProvider` gate no longer suppresses tier visibility on those base entries, while synthetic `-` variant generation stays prevented and Codex/GLM base models remain excluded unchanged ([#12299](https://github.com/diegosouzapw/OmniRoute/issues/12299)) ([#12371](https://github.com/diegosouzapw/OmniRoute/pull/12371)) — thanks @gonisulaimann +- **fix(guardrails):** keep `auto`/`auto/*` virtual combos exempt from the Vision Bridge `fixedModel` credential guard so a combo target is passed through instead of silently falling back to global auto-selection ([#12237](https://github.com/diegosouzapw/OmniRoute/issues/12237)) ([#12373](https://github.com/diegosouzapw/OmniRoute/pull/12373)) — thanks @pacocartones +- **fix(combo):** capability-filter exhaustion caused by `max_tokens` above every target's known output limit now reports that reason (requested `max_tokens` vs the pool's highest known ceiling) instead of the unrelated "supports structured output" message ([#12229](https://github.com/diegosouzapw/OmniRoute/issues/12229)) — thanks @DW-MediaLab ([#12374](https://github.com/diegosouzapw/OmniRoute/pull/12374)) — thanks @pacocartones +- **fix(auth):** the `least-used` account strategy now prefers accounts without backoff before falling back to oldest `lastUsedAt`, the same tie-break `round-robin` already applies, so a failover no longer lands on a just-rate-limited account for a single request ([#12279](https://github.com/diegosouzapw/OmniRoute/issues/12279)) — thanks @tenshiak ([#12375](https://github.com/diegosouzapw/OmniRoute/pull/12375)) — thanks @pacocartones +- **fix(docker):** the `chatgpt-web-codex-browser` image now finds the Chrome binary under `chrome-linux64/` (Chrome for Testing layout in `playwright:v1.62.0-noble`) as well as the legacy `chrome-linux/`, so the container no longer crash-loops with `exec: --headless=new: not found` ([#12024](https://github.com/diegosouzapw/OmniRoute/issues/12024)) ([#12376](https://github.com/diegosouzapw/OmniRoute/pull/12376)) — thanks @pacocartones +- **fix(providers):** declare `groq/compound` and `allam-2-7b` as non-reasoning models in the curated Groq registry so `reasoning_effort` / `output_config.effort` / `thinking` from Claude Code are stripped instead of forwarded, which Groq rejected with HTTP 400 ([#12134](https://github.com/diegosouzapw/OmniRoute/issues/12134)) ([#12379](https://github.com/diegosouzapw/OmniRoute/pull/12379)) — thanks @pacocartones +- **fix(executors):** `OpencodeExecutor` no longer forces a direct connection when the connection has a proxy assigned in Proxy Management but no per-account proxies: the single-account fast path used to wrap the upstream dispatch in the direct-egress sentinel, discarding the ambient proxy context the chat handler had pinned from `proxy_assignments`, so API-key `opencode`/`opencode-go` connections egressed from the host IP (and hit geoblocks) despite the assignment. The direct pin is now applied only when no ambient proxy context exists ([#11894](https://github.com/diegosouzapw/OmniRoute/issues/11894) — thanks @hizzt) ([#12380](https://github.com/diegosouzapw/OmniRoute/pull/12380)) — thanks @pacocartones +- **fix(api):** `GET /v1/models` with `MODELS_CATALOG_PREFIX_MODE=canonical` (or `?prefix=canonical`) now lists providers whose registry alias is undefined or equal to their own id (Antigravity, Antigravity CLI and other self-aliased built-ins) — their single `provider/model` id was dropped by the alias/canonical duplicate guard in the static, synced, custom and alias-backed catalog loops ([#12058](https://github.com/diegosouzapw/OmniRoute/issues/12058)) — thanks @cheynetom ([#12381](https://github.com/diegosouzapw/OmniRoute/pull/12381)) — thanks @pacocartones +- **fix(translator):** Drop replayed `thinking` blocks that carry no signature (the shape produced from cross-provider `reasoning_content`) instead of stamping the default Claude signature on them, which Anthropic rejected with `400 Invalid signature in thinking block` on the next turn served by an Anthropic rung ([#12105](https://github.com/diegosouzapw/OmniRoute/issues/12105)) — thanks @atescivitci-cmd ([#12386](https://github.com/diegosouzapw/OmniRoute/pull/12386)) — thanks @pacocartones +- **fix(cli):** Resolve Bun's `--preload` polyfill path against the package root instead of `dist/`, so `omniroute` installed with `bun install -g` no longer crashes at startup with `error: preload not found …/dist/open-sse/utils/setupPolyfill.ts` ([#11980](https://github.com/diegosouzapw/OmniRoute/issues/11980)) — thanks @joglomedia ([#12387](https://github.com/diegosouzapw/OmniRoute/pull/12387)) — thanks @pacocartones +- **fix(providers):** `gemini-business` now publishes its model catalog — `/v1/models` and `/v1/providers/gemini-business/models` list the 12 enterprise Gemini ids the executor understands instead of returning an empty list (#12107) ([#12389](https://github.com/diegosouzapw/OmniRoute/pull/12389)) — thanks @pacocartones +- **fix(db):** install `busy_timeout` before the SQLite connection's first statement so a process opening the database while another one closes its WAL connection waits out the transient EXCLUSIVE lock instead of dying with `database is locked`, and recognise the drivers' real BUSY/PROTOCOL/IOERR errors as transient in the corruption probe so the same lock no longer renames the database away as corrupt; deflakes `cross-process contenders never both acquire the same connection` (#12394 — thanks @pacocartones) +- **fix(chat-admission):** derive the `chat_admission_busy` 503 `Retry-After` from observed heavyweight-lease occupancy — the larger of the exhausted `OMNIROUTE_CHAT_ADMISSION_QUEUE_MS` window and the time since capacity last turned over, capped at 60 s — instead of a fixed 1 s (structural) / 2 s (byte-stage) hint that invited Codex/agent fan-out clients to re-send ~1 MiB `/v1/responses` bodies every second into a gate held for the whole SSE lifetime; an idle gate keeps the historical floors ([#12135](https://github.com/diegosouzapw/OmniRoute/issues/12135)) (#12395 — thanks @pacocartones) +- **fix(api-manager):** the API key permissions modal no longer silently drops `allowedCombos` entries its Combo picker cannot render — routing-rule names such as `rt-*`, which the backend already honours — when "All" is clicked and the key is switched back to "Restrict"; those entries now survive the toggle, are listed read-only under the combo list so the count and the list agree, and are saved back verbatim instead of persisting `[]` (deny-all) (#12397 — thanks @pacocartones) +- **fix(catalog):** write the NUL separator of the catalog connection memo key, the provider serviceKind memo key, the Video Bridge promotion group key and a JSON-exactness test fixture as the `\u0000` escape instead of a raw byte — same runtime value, but the raw byte made git, GitHub and ripgrep treat those files as binary (hidden PR diffs, silently skipped searches); a guard test now keeps raw NUL bytes out of `src/`, `open-sse/` and `tests/` (#12403 — thanks @pacocartones) +- **fix(providers):** add `CLAUDE_CODE_CLIENT_VERSION` and `GITHUB_COPILOT_CLI_VERSION` env overrides so Anthropic/Copilot client-version gates can be unblocked without a rebuild ([#12417](https://github.com/diegosouzapw/OmniRoute/issues/12417)) ([#12632](https://github.com/diegosouzapw/OmniRoute/pull/12632)) — thanks @HouMinXi +- **fix(db):** back-fill `last_ping_at` and `last_pinged_reset_key` on `provider_connections` during schema reconciliation so divergent lineages that skipped `123_quota_auto_ping` still accept quota auto-ping writes ([#12470](https://github.com/diegosouzapw/OmniRoute/pull/12470) — thanks @KooshaPari) +- **fix(ci):** document MIT exceptions for `@eloqnt/{config,format-json,format-po}` (next-intl transitive; locked tarballs omit `license`) and keep the A2A lifecycle vitest off the real SQLite persistence seam ([#12581](https://github.com/diegosouzapw/OmniRoute/issues/12581)) ([#12595](https://github.com/diegosouzapw/OmniRoute/pull/12595)) +- OpenCode plugin `/v1/models` catalog fetch now waits 30s by default and attaches HTTP `statusCode` on 401/5xx so host fallback plugins can hop instead of seeing an untyped AbortError/UnknownError. ([#12607](https://github.com/diegosouzapw/OmniRoute/pull/12607)) — thanks @RaviTharuma +- **CI:** the OpenAPI security-tier gate now mirrors `isAlwaysProtectedPath()` in full — it also reads `ALWAYS_PROTECTED_API_PATTERNS`, so the pattern-gated credential routes (`/api/providers/{id}/{claude,codex}-auth/{export,apply-local}`, GHSA-5926-2w35-7h4q) no longer report as unannotated. (#12605) +- **fix(api):** GET /v1/models no longer waits forever on a hung coalesced catalog rebuild; cold-path waits are bounded (`CATALOG_BUILD_TIMEOUT_MS`, default 8s) and a last-good 200 is served when the rebuild times out ([#12627](https://github.com/diegosouzapw/OmniRoute/issues/12627)). ([#12628](https://github.com/diegosouzapw/OmniRoute/pull/12628)) — thanks @RaviTharuma +- **fix(cursor):** a non-array `tool_calls` on an assistant message no longer crashes the cursor request translator with a `TypeError`; both loops now require an array ([#12689](https://github.com/diegosouzapw/OmniRoute/issues/12689)) ([#12691](https://github.com/diegosouzapw/OmniRoute/pull/12691)) — thanks @soroush5 +- **fix(chat):** Chat Completions no longer return empty `content` after a server-owned memory or skills tool; the first provider send and account/model recovery now share one pipeline so a follow-up round-trip can fill the reply ([#12696](https://github.com/diegosouzapw/OmniRoute/issues/12696)) — thanks @HouMinXi ([#12867](https://github.com/diegosouzapw/OmniRoute/pull/12867)) +- **fix(dashboard):** batch-deleting provider keys no longer toasts failure after a successful delete when the confirm button's click event is forwarded as `onAfter` ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) — thanks @HouMinXi +- **fix(glm):** drop the extra 16th argument to `createSSETransformStreamWithLogger` that TypeScript rejected (TS2554) and that never reached the TransformStream ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) ([#12770](https://github.com/diegosouzapw/OmniRoute/pull/12770)) — thanks @HouMinXi +- **fix(dashboard):** pass `nodeMap` into Runtime `QuotaGroup` so a quota monitor in error/exhausted/alerting no longer throws `ReferenceError: nodeMap is not defined`. ([#12868](https://github.com/diegosouzapw/OmniRoute/pull/12868)) — thanks @HouMinXi +- Restricted API keys whose `allowedModels` lists a combo name no longer skip every combo member at pre-dispatch (`ALL_TARGETS_SKIPPED`). Inner-target filtering still applies when the allow-list is a provider prefix or `disableNonPublicModels` is on ([#12899](https://github.com/diegosouzapw/OmniRoute/pull/12899)). — thanks @HouMinXi +- **fix(combos):** Effort-suffixed combo members inherit the base model's `model_context_overrides` row so priority order is not inverted on large requests ([#12926](https://github.com/diegosouzapw/OmniRoute/pull/12926)) — thanks @HouMinXi +- **fix(catalog):** Picker-added `customModels` enter the dispatch-time live catalog so combo members and bare inference no longer 400 ([#12934](https://github.com/diegosouzapw/OmniRoute/pull/12934)) — thanks @HouMinXi +- **fix(docker):** re-derive the Docker build's worker-pool memory budget from the MEASURED ~4.5 GB per-process RSS (the issue owner's own VPS dmesg OOM-killer reproduction) instead of the stale 2560 MB/worker inference #11419 shipped, and lower `OMNIROUTE_BUILD_WORKERS` 3 → 2 so 1 parent + 1 page-data worker (2 processes × 4.5 GB = 9 GB) fits the 12.288 GB (75%) budget on a 16 GB GitHub Actions runner — the previous default (1 parent + 2 workers = 13.5 GB) still overcommitted the runner and kept "Publish to Docker Hub" failing with `cannot allocate memory` after #11419 merged (#7518). ([#11663](https://github.com/diegosouzapw/OmniRoute/pull/11663)) +- **fix(electron):** the packaged Windows desktop build now passes the #7592 cold-restart smoke end to end. Five defects were found and fixed on the way: (1) optional-pack staging failed on any machine whose `tar` is GNU tar — it reads the drive letter in an absolute `-f C:\...` archive path as a remote rsh host (`Cannot connect to C:`), so staging now passes a bare filename with `cwd` at the tarball directory and surfaces tar stderr on failure; (2) the Electron `files` allowlist was missing `lib/loginHeaderCapture.js`, so the packaged main process crashed at startup with `Cannot find module './lib/loginHeaderCapture'`; (3) electron-builder ≥26 injects an `!**/node_modules/**` ignore into every extraResources pattern list that no positive filter can override, which silently dropped the staged runtime `node_modules` (including the better-sqlite3 N-API prebuild) from `resources/app` — a fresh v3.8.50 desktop build would have shipped with no native SQLite driver and reproduced the #7592 sql.js fallback on every machine; an `afterPack` hook now restores the staged `node_modules` after packing; (4) the packaged-app smoke harness redirected `USERPROFILE` into its temp DATA_DIR, but Electron resolves the Roaming profile from `%USERPROFILE%\AppData\Roaming\` (USERPROFILE takes precedence over the APPDATA env var) and the path service throws instead of creating it, so `requestSingleInstanceLock()` returned false and the app exited(0) silently before `app.whenReady()` — the harness now pre-creates the derived tree, and `ensureSmokeEnvDirs` is exported and unit-tested; (5) the #7592 driver assertion parsed a `[DB] Driver: ...` line that the server's primary DB path never printed (only the unused `openDatabaseAsync()` did) — `getDbInstance()` now emits the same line on open so the guard can actually assert the native driver. Also: the smoke env-allowlist unit test hardcoded POSIX paths and could only pass on Linux/macOS; expectations are now host-agnostic, with new regression tests for the USERPROFILE-derived tree and `tarPack` under absolute Windows-style paths ([#11443](https://github.com/diegosouzapw/OmniRoute/pull/11443)) — thanks @jonlwheat2-gif +- fix(sse): stop the auto-combo candidates inspector from silently dropping model-locked/cooled-down rows (#9133) ([#11994](https://github.com/diegosouzapw/OmniRoute/pull/11994)) — thanks @hartmark +- **fix(providers):** Perplexity Web no longer turns upstream stream failures into successful assistant text; pre-content failures remain eligible for fallback, partial output ends with a structured sanitized error, and failed sessions are not persisted ([#12465](https://github.com/diegosouzapw/OmniRoute/pull/12465)) +- **fix(video-bridge):** preserve chronological video and transcript fusion, source-frame timestamps, transcript provenance, full contact-sheet intervals, and pre-abort callback isolation ([#11681](https://github.com/diegosouzapw/OmniRoute/pull/11681)) +- **Z.ai Web:** HTTP 200 streams carrying an upstream error now terminate with a structured failure instead of assistant text plus a normal stop, preserving partial output while allowing pre-content combo fallback. ([#12454](https://github.com/diegosouzapw/OmniRoute/pull/12454)) +- Absorb `Error [AbortError]: request_signal_aborted` and DOMException AbortError shapes in the process-level client-abort crash guard so routine client disconnects no longer kill the server (exit code 7). ([#12165](https://github.com/diegosouzapw/OmniRoute/pull/12165)) — thanks @alvinveroy +- **fix(sse):** Treat Adapta Web `type:error` SSE events as sanitized non-stream failures instead of empty HTTP 200 completions. ([#12459](https://github.com/diegosouzapw/OmniRoute/pull/12459)) +- **fix(autoCombo):** layer 3 (`models_dev_tier`) no longer scores retired models as premium, merge cross-provider capability rows deterministically, and inherit tier scores through variant ids ([#11508](https://github.com/diegosouzapw/OmniRoute/issues/11508)). `getModelsDevTierFitness` now vetoes ids present in `config/quality/model-lifecycle.json` with status `retired` before any other signal; `loadModelCapabilities()` aggregates per `model_id` (any-non-null-true for booleans, max for `limit_context`) instead of last-write-wins over SQLite's undefined row order; a capabilities miss retries through `resolveScoresAs()`'s base id and reports `models_dev_tier:inherited`, matching layers 1–2 (#11492). Adds `getModelsDevTierFitnessWithSource()` and an `invalidateCapabilitiesCache()` test/ops hook. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) — thanks @Meet6338-X +- fix(sse): accept Claude `content:[]` + `stop_reason: max_tokens`/`tool_use` in `detectMalformedNonStream` (match `isEmptyContentResponse`) so Claude Code `/model` probes with `max_tokens: 1` no longer become a false 502 ([#11568](https://github.com/diegosouzapw/OmniRoute/pull/11568)) — thanks @Seramicx +- **fix(security):** sanitize `request.failed` diagnostics before publishing them to live dashboard listeners and replay history, while keeping status, model, provider, latency, and internal call-log diagnostics intact. ([#12469](https://github.com/diegosouzapw/OmniRoute/pull/12469)) +- **fix(pwa):** do not intercept dashboard navigations so Chrome can fall back from a stale HTTP/3 Alt-Svc advertisement (UDP :20128 is unpublished; F5 on a long-lived tab hung until a new tab opened a fresh TCP connection). ([#12767](https://github.com/diegosouzapw/OmniRoute/pull/12767)) — thanks @HouMinXi +- **fix(executors):** handle DuckDuckGo ERR_BN_LIMIT (418) without retrying — when the upstream returns `418 ERR_BN_LIMIT` (rate-limit/ban), the executor now returns the error immediately instead of burning another VQD acquisition that would only count against the IP limit. The retry logic for `418 ERR_CHALLENGE` (unsolved challenge) remains unchanged. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) ([#12110](https://github.com/diegosouzapw/OmniRoute/pull/12110)) — thanks @oyi77 / @Meet6338-X +- Fixed DuckDuckGo streaming responses losing JSON lines and UTF-8 characters split across network chunks. ([#11528](https://github.com/diegosouzapw/OmniRoute/pull/11528)) — thanks @pacocartones +- Harden generated Cloudflare, Vercel, and Deno relays by stripping hop-by-hop, framing, and proxy authentication headers before forwarding requests upstream. ([#11533](https://github.com/diegosouzapw/OmniRoute/pull/11533)) — thanks @pacocartones +- **feat(grok-cli):** Provider Limits shows grok-cli banked reset credits from `GetRemainingResets` (including a real zero; a failed RPC omits the row) and the existing View credits button now calls `ConsumerUiSvc/RedeemReset` for grok-cli. Live tokens use nested fields 10/20/30 (id + Timestamp), not compact 1/2/3. ([#12805](https://github.com/diegosouzapw/OmniRoute/pull/12805)) — thanks @HouMinXi +- **fix(memory):** Embedding Model Quick select, Embedding Source remote dropdown, and Rerank selector now list every configured provider with embedding/rerank support instead of only chat-catalog text matches plus OpenRouter live discovery; a generic OpenAI-compatible `/embeddings` + Cohere-compatible `/rerank` runtime fallback resolves any configured chat provider's embedding/rerank endpoint, so unlisted providers no longer fail with "Unknown embedding provider"; both memory selectors gained a free-text model override ([#11390](https://github.com/diegosouzapw/OmniRoute/pull/11390)) — thanks @rqzbeh +- Harden SQLite upgrades around the historical migration-074 version collision: missing discovery and inspector tables are replayed atomically, pre-existing databases (including setup-created skeletons) receive reusable content-addressed safety snapshots, and Node test/eval probes without `DATA_DIR` are isolated from the operator database. ([#12435](https://github.com/diegosouzapw/OmniRoute/pull/12435)) +- **fix(dashboard):** Moonshot/Kimi Open Platform voucher and cash leftover percentages follow the bucket balance, so an empty wallet no longer paints those rows as 100% while Available is 0% ([#12733](https://github.com/diegosouzapw/OmniRoute/pull/12733)) — thanks @HouMinXi +- **fix(combo):** return non-retryable HTTP 400 when all candidates for a pinned native Codex turn are unavailable due to model-scoped lockout, terminating the turn cleanly while preserving turn continuity and enabling standard Combo routing on subsequent turns ([#12240](https://github.com/diegosouzapw/OmniRoute/pull/12240)) — thanks @mdigitalbh81 +- Fixed auto-router strategies preserving the selected connection through ranking and dispatch when multiple connections share the same provider and model. ([#11530](https://github.com/diegosouzapw/OmniRoute/pull/11530)) — thanks @pacocartones +- Preserve Chat Completions JSON-object response formats when translating requests to the Responses API. ([#11529](https://github.com/diegosouzapw/OmniRoute/pull/11529)) — thanks @pacocartones +- Fix idempotency fingerprints so Responses API requests include `input` and other semantic generation fields using stable serialization, while excluding credentials and request metadata. ([#11532](https://github.com/diegosouzapw/OmniRoute/pull/11532)) — thanks @pacocartones +- **fix(api):** Generated API CLI commands now enforce required OpenAPI request bodies; Combo test commands forward the required `comboName` body, while API keys created by older writers after migration 149 preserve legacy allow-all Combo access without widening explicit empty allowlists — thanks @marcelokarval ([#12070](https://github.com/diegosouzapw/OmniRoute/pull/12070)) +- **fix(grok-web):** treat upstream streaming failures as failures instead of successful assistant text: error-only streams now fail readiness with HTTP 502, while failures after legitimate content preserve that partial output and terminate through the sanitized stream failure path without a normal `stop` completion. ([#12458](https://github.com/diegosouzapw/OmniRoute/pull/12458)) +- HuggingChat now turns HTTP 200 JSONL generation failures into a sanitized 502 before content, or a fixed public stream failure after partial output, so fallback and request persistence no longer record a false successful stop. ([#12456](https://github.com/diegosouzapw/OmniRoute/pull/12456)) +- **fix(providers):** keep 1min.ai HTTP 200 stream errors out of assistant content, preserve partial output, and expose sanitized terminal errors so pre-content failures can fall back. ([#12466](https://github.com/diegosouzapw/OmniRoute/pull/12466)) +- **fix(sessions):** preserve legacy Sessions data when the additive exclusive-session projection is unavailable, returning an empty projection and warning only once per contiguous outage instead of failing the endpoint; the Sessions badge intentionally reflects the merged legacy and exclusive row count introduced by [#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389) ([#11469](https://github.com/diegosouzapw/OmniRoute/pull/11469)) — thanks @KaspaPulse +- Render Nimble Search with the generic provider icon and serve Opper's proven logo locally. ([#12415](https://github.com/diegosouzapw/OmniRoute/pull/12415)) +- **fix(quality):** `config/quality/provider-assets-provenance.jsonl` was stale against the physical `public/providers/` tree (still listing 225 assets and 5 duplicate-content groups after several provider-retirement PRs removed 85 icon files), which would have made `check:provider-asset-provenance` fail on the next full run. Reconciled the manifest to the real 142-file snapshot (142 asset records, 1 duplicate-content group, 2 newly-registered files marked `unresolved` pending provenance research), repinned `auditedCommit`, and updated the regression test's hardcoded counts to match. ([#11876](https://github.com/diegosouzapw/OmniRoute/pull/11876)) +- fix(ui): wrap quota card footer action buttons to prevent clipping Refresh button on narrow cards — when extra action buttons such as `View credits` are present on Codex cards, allow the footer row to flex-wrap with `shrink-0` buttons so `Refresh now` remains visible and clickable across all responsive card widths ([#11464](https://github.com/diegosouzapw/OmniRoute/pull/11464)) — thanks @adevwithpurpose +- Keep Antigravity Gemini usable when the same connection's Claude weekly quota is empty; generic quota cache stays per-connection for every other provider. ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) — thanks @HouMinXi +- Fixed Tailscale tunnel setup to reject ports outside the valid 1-65535 range and safely install Windows MSI packages from paths containing apostrophes. ([#11534](https://github.com/diegosouzapw/OmniRoute/pull/11534)) — thanks @pacocartones +- Classify Cloudflared and Tailscale process-management routes as local-only and spawn-capable while preserving remote access to read-only tunnel status endpoints. ([#11531](https://github.com/diegosouzapw/OmniRoute/pull/11531)) — thanks @pacocartones +- Electron release workflow: the `publish-npm` job now grants `actions: read` to the reusable `npm-publish.yml` it calls (its `publish` job requests it), which is what made GitHub refuse the whole v3.8.50 run at startup and ship the release with zero desktop assets; a `workflow_dispatch` now builds the requested tag instead of the dispatching branch and can skip the npm leg (`publish_npm=false`) when only re-attaching assets ([#11974](https://github.com/diegosouzapw/OmniRoute/pull/11974)) +- **fix(dashboard):** The Combos page usage guide now reads its dismissal through `useSyncExternalStore` instead of correcting SSR state inside an effect, removing an extra commit of the page tree on every load (and the `react-hooks/set-state-in-effect` error it raised). ([#12671](https://github.com/diegosouzapw/OmniRoute/pull/12671)) +- Electron release: `electron/package-lock.json` regained the optional `electron-builder-squirrel-windows` subtree (13 entries) that `npm ci` had been refusing as out of sync, `electron-release.yml` gained a `build_ref` dispatch input and stops regenerating release notes on a re-attach dispatch, and the npm publish workflow attaches the SBOM to the GitHub Release on dispatch publishes too — so the v3.8.51 tag ships every desktop asset and the SBOM like v3.8.49 did ([#12022](https://github.com/diegosouzapw/OmniRoute/pull/12022)) +- Drained the reds every PR against `release/v3.8.51` was born with: documented the three service-worker build-id variables, registered the six retirement/tunnel tests with the mutation gate, approved `eslint-plugin-react-hooks` in the dependency allowlist, added the six `combo.sort.*` strings to `vi` and `pt-BR`, pointed the ChatGPT Web doc at the real migration-168 test, worded the g4f hint around the member key, and realigned four tests to the retired-provider catalog and the legacy-schema fixtures the retirement migrations touch. ([#11940](https://github.com/diegosouzapw/OmniRoute/pull/11940)) +- **fix(providers):** Claude, Grok, LMArena, Notion, and Perplexity web-cookie transports now use pooled `wreq-js` 3.2 instead of the native sidecar, with all nine supported bindings pinned and audited, and the applicable platform binding plus native-license evidence included in each release artifact ([#12429](https://github.com/diegosouzapw/OmniRoute/pull/12429), supersedes [#11753](https://github.com/diegosouzapw/OmniRoute/pull/11753)). +- **fix(providers):** Zed Hosted streaming failures now trigger fallback before content and end partial streams with a sanitized structured error instead of fake assistant text and a normal-success stop. ([#12455](https://github.com/diegosouzapw/OmniRoute/pull/12455)) +- **fix(test):** capture hook result after render (direct commit `b85d0abb0b`) +- **fix(sync):** repair the two regressions the v3.8.50 sync-back left on release/v3.8.51 (direct commit `5ade9e0851`) +- **revert(models):** drop the memo reset hook ported from #10055 (direct commit `282de841ae`) +- **fix(typecheck):** guard the optional connectionId before the reactive model sync (direct commit `f2825bad75`) +- **fix(quota-share):** release the winner's reserved in-flight slot (#11371) ([#11408](https://github.com/diegosouzapw/OmniRoute/pull/11408)) — thanks @oyi77 +- **fix(providers):** stop silently dropping reasoning effort on opencode families (#10788) ([#11409](https://github.com/diegosouzapw/OmniRoute/pull/11409)) — thanks @oyi77 +- **fix(combo):** enable genuine quota-aware routing for generic providers (antigravity, claude, etc.) ([#11411](https://github.com/diegosouzapw/OmniRoute/pull/11411)) — thanks @HouMinXi +- **fix(oauth):** allow expired token retry before deactivation and activate expired retry loop (#P0) ([#11414](https://github.com/diegosouzapw/OmniRoute/pull/11414)) — thanks @HouMinXi +- **perf(executors):** lazy-load the executor registry — defer class imports + construction to first use (#11220) ([#11421](https://github.com/diegosouzapw/OmniRoute/pull/11421)) — thanks @oyi77 +- **fix:** make release/v3.8.50 CI gates green (run 32786966560 root causes) ([#11450](https://github.com/diegosouzapw/OmniRoute/pull/11450)) — thanks @jonlwheat2-gif +- **fix(sse):** exact-domain cookie match and origin-equality URL assertions ([#11452](https://github.com/diegosouzapw/OmniRoute/pull/11452)) — thanks @hartmark +- **fix(providers):** send Responses API payload during OpenAI-compatible validation ([#11454](https://github.com/diegosouzapw/OmniRoute/pull/11454)) — thanks @tuandinh0801 +- **fix(command-code):** fallback to /alpha/generate for Go plan without Provider API access ([#11455](https://github.com/diegosouzapw/OmniRoute/pull/11455)) — thanks @Chewji9875 +- **fix(responses-continuation):** fail closed on a log-truncated stored input/output array ([#11473](https://github.com/diegosouzapw/OmniRoute/pull/11473)) — thanks @hartmark +- **fix(cli):** write jcode settings as [providers.omniroute] in config.toml ([#11484](https://github.com/diegosouzapw/OmniRoute/pull/11484)) — thanks @hongnoul +- **fix(logging):** raise chat-log array truncation cap to 1000 and unify duplicate implementations ([#11499](https://github.com/diegosouzapw/OmniRoute/pull/11499)) — thanks @hartmark +- **fix:** restore live websocket URL helpers ([#11509](https://github.com/diegosouzapw/OmniRoute/pull/11509)) — thanks @PauloFH +- **perf(core):** eliminate async waterfalls across home, cache, and db operations (#11396) ([#11512](https://github.com/diegosouzapw/OmniRoute/pull/11512)) — thanks @HouMinXi +- **fix(kiro):** add runtime.us-east-1.kiro.dev as first-attempt endpoint ([#11517](https://github.com/diegosouzapw/OmniRoute/pull/11517)) — thanks @rifqiawl +- **fix(providers):** remove duplicate 128-tool truncation in OpencodeExecutor (#11444) ([#11518](https://github.com/diegosouzapw/OmniRoute/pull/11518)) — thanks @HouMinXi +- **fix(providers):** poll onboardUser LRO before classifying BYOP ([#11519](https://github.com/diegosouzapw/OmniRoute/pull/11519)) — thanks @rifqiawl +- **fix(translator):** preserve exact tool name casing for Claude tool_use payloads (#11487) ([#11520](https://github.com/diegosouzapw/OmniRoute/pull/11520)) — thanks @HouMinXi +- **fix(kimi-web):** align health probe with executor domain ([#11521](https://github.com/diegosouzapw/OmniRoute/pull/11521)) — thanks @pacocartones +- **fix(cli):** preserve existing admin password during setup ([#11522](https://github.com/diegosouzapw/OmniRoute/pull/11522)) — thanks @pacocartones +- **fix(mitm):** keep Windows DNS elevation off the POSIX sudo path (#11430) ([#11542](https://github.com/diegosouzapw/OmniRoute/pull/11542)) — thanks @official-burak +- **fix(sse):** scale chat admission by ingest byte budget instead of a fixed request count ([#11548](https://github.com/diegosouzapw/OmniRoute/pull/11548)) — thanks @TheDemonTuan +- **fix:** accept Gemini Web cookie exports ([#11554](https://github.com/diegosouzapw/OmniRoute/pull/11554)) — thanks @rafacpti23 +- **fix(i18n):** complete Brazilian Portuguese translations ([#11558](https://github.com/diegosouzapw/OmniRoute/pull/11558)) — thanks @rafacpti23 +- **fix(sse):** explicit types for openai-responses pureHelpers — clears last failing core typecheck gate ([#11567](https://github.com/diegosouzapw/OmniRoute/pull/11567)) — thanks @jonlwheat2-gif +- **fix(opencode):** hydrate Proxy Pool references for no-auth connections ([#11584](https://github.com/diegosouzapw/OmniRoute/pull/11584)) — thanks @AStupidBear +- **fix(ci):** resolve inherited release/v3.8.51 base-red CI failures ([#11608](https://github.com/diegosouzapw/OmniRoute/pull/11608)) — thanks @jonlwheat2-gif +- **fix(db):** fallback load STORAGE_ENCRYPTION_KEY from env files ([#11614](https://github.com/diegosouzapw/OmniRoute/pull/11614)) — thanks @benzntech +- **fix(providers):** mark Brave Search as no longer having a free tier ([#11621](https://github.com/diegosouzapw/OmniRoute/pull/11621)) — thanks @turbolego +- **fix:** pass tool result content through as string in OpenAI/Claude -> Gemini translation ([#11624](https://github.com/diegosouzapw/OmniRoute/pull/11624)) — thanks @Hsia97 +- **fix(sse):** scope credential health to exact key ([#11628](https://github.com/diegosouzapw/OmniRoute/pull/11628)) — thanks @KaspaPulse +- **fix(install):** guard prepare script against missing husky ([#11634](https://github.com/diegosouzapw/OmniRoute/pull/11634)) — thanks @Prajeeth-12 +- **fix(security):** redact NVIDIA_API_KEY at the log sink in the ad-hoc diag script ([#11639](https://github.com/diegosouzapw/OmniRoute/pull/11639)) +- **fix(types):** clear the two type regressions red on release/v3.8.51 ([#11640](https://github.com/diegosouzapw/OmniRoute/pull/11640)) — thanks @jonlwheat2-gif +- **fix(build):** resolve workspace: protocol dependencies to concrete semver versions during prepublish (#11540) ([#11641](https://github.com/diegosouzapw/OmniRoute/pull/11641)) — thanks @HouMinXi +- **fix(search):** properly resolve configured search connection in /v1/responses pipeline (#11524) ([#11642](https://github.com/diegosouzapw/OmniRoute/pull/11642)) — thanks @HouMinXi +- **fix(authz):** trust cf-connecting-ip for via-proxy detection when peer is Cloudflare edge (#11514) ([#11643](https://github.com/diegosouzapw/OmniRoute/pull/11643)) — thanks @HouMinXi +- **fix(security):** compare the OIDC state cookie in constant time ([#11646](https://github.com/diegosouzapw/OmniRoute/pull/11646)) +- **fix(security):** namespace the dedup hash by the calling API key ([#11649](https://github.com/diegosouzapw/OmniRoute/pull/11649)) +- **fix(models):** gate the Codex-native catalog loop on prefix mode (#11632) ([#11662](https://github.com/diegosouzapw/OmniRoute/pull/11662)) — thanks @xiaoyaner0201 +- **fix(dashboard):** project Codex routing quota in account pools ([#11666](https://github.com/diegosouzapw/OmniRoute/pull/11666)) — thanks @KaspaPulse +- **fix(ddgw):** normaliza system no upstream da release v3.8.51 ([#11692](https://github.com/diegosouzapw/OmniRoute/pull/11692)) — thanks @RhianB14 +- **fix(memory):** honor category filter in GET /api/memory ([#11699](https://github.com/diegosouzapw/OmniRoute/pull/11699)) — thanks @Sabeekhann +- **fix(ci):** detect stale workspace lockfile entries ([#11714](https://github.com/diegosouzapw/OmniRoute/pull/11714)) — thanks @pacocartones +- **fix(ci):** stamp dist/BUILD_SHA before the npm publish provenance gate ([#11721](https://github.com/diegosouzapw/OmniRoute/pull/11721)) +- **fix(docker):** decouple the best-effort Bun image from the release manifest ([#11724](https://github.com/diegosouzapw/OmniRoute/pull/11724)) +- **perf(compression):** avoid cloning memo misses twice ([#11727](https://github.com/diegosouzapw/OmniRoute/pull/11727)) — thanks @RaviTharuma +- **fix(docker):** let the bun digest artifact be absent, not fatal ([#11740](https://github.com/diegosouzapw/OmniRoute/pull/11740)) +- **fix(shared):** block cloud-metadata hosts under default remote-image guard ([#11755](https://github.com/diegosouzapw/OmniRoute/pull/11755)) — thanks @santosraju99-hub +- **fix(api):** preserve caller-provided X-Correlation-Id on chat completions ([#11760](https://github.com/diegosouzapw/OmniRoute/pull/11760)) — thanks @aniruddhaadak80 +- **fix(resilience):** lock permanently retired models instead of short backoff (Gemini ban prevention) ([#11762](https://github.com/diegosouzapw/OmniRoute/pull/11762)) — thanks @turbolego +- **fix:** mark Vercel AI Gateway as passthroughModels ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **fix(resilience):** stop hammering permanently-moved endpoints and billing-suspended accounts ([#11774](https://github.com/diegosouzapw/OmniRoute/pull/11774)) — thanks @turbolego +- **fix(lease):** remove global static reservation and gate routing on live active lease occupancy ([#11775](https://github.com/diegosouzapw/OmniRoute/pull/11775)) — thanks @TheDemonTuan +- **fix(ci):** size the install-upgrade gate to a measured run, and log the pack cost ([#11776](https://github.com/diegosouzapw/OmniRoute/pull/11776)) +- **fix(pwa):** stop serving the stale shell after deploys ([#11779](https://github.com/diegosouzapw/OmniRoute/pull/11779)) — thanks @HouMinXi +- **fix(combo):** lock GitHub models rejected as "not supported" for future requests ([#11781](https://github.com/diegosouzapw/OmniRoute/pull/11781)) — thanks @turbolego +- **fix(cli):** probe both IPv4 and IPv6 loopback for server readiness (#11766) ([#11794](https://github.com/diegosouzapw/OmniRoute/pull/11794)) — thanks @vermasomesh835 +- **fix(sse):** preserve CLIProxyAPI wrappers per connection ([#11797](https://github.com/diegosouzapw/OmniRoute/pull/11797)) — thanks @RaviTharuma +- **fix(providers):** honor model auto-fetch opt-in on create ([#11798](https://github.com/diegosouzapw/OmniRoute/pull/11798)) — thanks @RaviTharuma +- **fix(dashboard):** make model auto-fetch order independent ([#11805](https://github.com/diegosouzapw/OmniRoute/pull/11805)) — thanks @RaviTharuma +- **fix(docker):** make GHA cache exports best effort ([#11806](https://github.com/diegosouzapw/OmniRoute/pull/11806)) — thanks @RaviTharuma +- **fix(kiro):** do not permanently ban on 'User is not authorized to make this call' ([#11809](https://github.com/diegosouzapw/OmniRoute/pull/11809)) — thanks @Deftera186 +- **fix(sse):** keep cache-write tokens in OpenAI-shaped usage ([#11814](https://github.com/diegosouzapw/OmniRoute/pull/11814)) — thanks @TheDemonTuan +- **fix(docs):** resolve relative markdown and wiki links across Fumadocs and GitHub wiki ([#11834](https://github.com/diegosouzapw/OmniRoute/pull/11834)) — thanks @AndrianBalanescu +- **fix(chat):** preserve unstripped model string for passthrough provider routing ([#11840](https://github.com/diegosouzapw/OmniRoute/pull/11840)) — thanks @raheemuddin786 +- **fix(mcp):** avoid async initialization inside sync esm wrappers (#11569) ([#11849](https://github.com/diegosouzapw/OmniRoute/pull/11849)) — thanks @HouMinXi +- **fix(sse):** preserve coherent markdown token boundaries across stream translation chunks (#11606) ([#11851](https://github.com/diegosouzapw/OmniRoute/pull/11851)) — thanks @HouMinXi +- **fix(ci):** run the install-upgrade gate on disk, not on the /tmp tmpfs ([#11855](https://github.com/diegosouzapw/OmniRoute/pull/11855)) +- **fix(skills):** expand shorthand property types in injected tool schemas ([#11857](https://github.com/diegosouzapw/OmniRoute/pull/11857)) — thanks @alvinveroy +- **fix(ci):** publish npm from a hosted runner so provenance is accepted ([#11877](https://github.com/diegosouzapw/OmniRoute/pull/11877)) +- **fix(lint):** pin react hooks plugin to 7.0.1 ([#11886](https://github.com/diegosouzapw/OmniRoute/pull/11886)) +- **fix(ci):** surface and clear release-green ESLint failure ([#11890](https://github.com/diegosouzapw/OmniRoute/pull/11890)) — thanks @MumuTW +- **fix(guardrails):** prevent duplicate prompt-injection-guard log output ([#11936](https://github.com/diegosouzapw/OmniRoute/pull/11936)) — thanks @patrykkopycinski +- **fix(auth):** downgrade expected transient states from warn to debug ([#11937](https://github.com/diegosouzapw/OmniRoute/pull/11937)) — thanks @patrykkopycinski +- **fix(codex):** fail over image generation for imported free plans ([#11948](https://github.com/diegosouzapw/OmniRoute/pull/11948)) — thanks @Bl0ck154 +- **fix(codex):** normalize non-stream responses ([#11951](https://github.com/diegosouzapw/OmniRoute/pull/11951)) — thanks @Bl0ck154 +- **fix(images):** forward Antigravity image size ([#11952](https://github.com/diegosouzapw/OmniRoute/pull/11952)) — thanks @Bl0ck154 +- **fix(db):** invalidate connection cache after upsert ([#11953](https://github.com/diegosouzapw/OmniRoute/pull/11953)) — thanks @Bl0ck154 +- **fix(codex):** restore imported account state ([#11954](https://github.com/diegosouzapw/OmniRoute/pull/11954)) — thanks @Bl0ck154 +- **fix(sse):** give extended-thinking targets the reasoning readiness budget ([#11959](https://github.com/diegosouzapw/OmniRoute/pull/11959)) — thanks @abhisheksharma2411 +- **fix(dashboard):** use opaque background and readable text color on cost chart tooltips ([#11960](https://github.com/diegosouzapw/OmniRoute/pull/11960)) — thanks @ZaimMarzuki +- **fix(antigravity):** send complete loadCodeAssist metadata (ideType/platform/pluginType as numeric enums) ([#11969](https://github.com/diegosouzapw/OmniRoute/pull/11969)) — thanks @rifqiawl +- **fix(core):** resolve DB init race condition and reasoning translation ([#12003](https://github.com/diegosouzapw/OmniRoute/pull/12003)) — thanks @raheemuddin786 +- **fix(admission):** exclude reclaimable page cache from the cgroup pressure ratio ([#12017](https://github.com/diegosouzapw/OmniRoute/pull/12017)) — thanks @HouMinXi +- **fix(build):** prune non-production trees in NFT trace excludes and tsconfig ([#12028](https://github.com/diegosouzapw/OmniRoute/pull/12028)) — thanks @Chewji9875 +- **fix(migrations):** add renamed migration compatibility for 056/073/077/101 ([#12036](https://github.com/diegosouzapw/OmniRoute/pull/12036)) — thanks @oyi77 +- **fix(sse):** default crash-guard logger to console.warn, not console ([#12042](https://github.com/diegosouzapw/OmniRoute/pull/12042)) — thanks @HouMinXi +- **fix(catalog):** derive vision/modalities for built-in auto combos from effective target pool ([#12046](https://github.com/diegosouzapw/OmniRoute/pull/12046)) — thanks @Prajeeth-12 +- **fix(release):** the packaged-app smoke verifies the database opened, not a driver line the primary path never prints (twin of #12032) ([#12047](https://github.com/diegosouzapw/OmniRoute/pull/12047)) +- **fix(barrel):** migrate src/app/ off the @/lib/localDb barrel import (#11795 Phase 2) ([#12051](https://github.com/diegosouzapw/OmniRoute/pull/12051)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate src/lib/ off the localDb barrel to direct db imports (#59) ([#12052](https://github.com/diegosouzapw/OmniRoute/pull/12052)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate open-sse, src/shared, src/sse, src/models, src/domain off the @/lib/localDb barrel import (#11795 Phase 4) ([#12053](https://github.com/diegosouzapw/OmniRoute/pull/12053)) — thanks @jonlwheat2-gif +- **fix(barrel):** delete the @/lib/localDb barrel — every consumer migrated (#11795 Phase 5) ([#12055](https://github.com/diegosouzapw/OmniRoute/pull/12055)) — thanks @jonlwheat2-gif +- **fix(radar):** restore D12 public boundary ([#12057](https://github.com/diegosouzapw/OmniRoute/pull/12057)) +- **fix(dev):** bound webpack and Tailwind scans ([#12075](https://github.com/diegosouzapw/OmniRoute/pull/12075)) — thanks @backryun +- **fix(dev):** isolate root layout settings reads ([#12076](https://github.com/diegosouzapw/OmniRoute/pull/12076)) — thanks @backryun +- **fix(dev):** reduce instrumentation executor fan-out (phase 3) ([#12078](https://github.com/diegosouzapw/OmniRoute/pull/12078)) — thanks @backryun +- **fix(dev):** make logging resources HMR-singleton ([#12079](https://github.com/diegosouzapw/OmniRoute/pull/12079)) — thanks @backryun +- **fix(sse):** keep unavailable forced connections scoped ([#12080](https://github.com/diegosouzapw/OmniRoute/pull/12080)) — thanks @keeltrace +- **fix(dev):** isolate batch dispatch from instrumentation ([#12081](https://github.com/diegosouzapw/OmniRoute/pull/12081)) — thanks @backryun +- **fix(ci):** reconcile release test contract drift ([#12082](https://github.com/diegosouzapw/OmniRoute/pull/12082)) — thanks @backryun +- **fix(ci):** accept CVE-2025-68121 in the prebuilt tls-client .so, auto-close base-red issues, guard Scorecard on the default branch ([#12085](https://github.com/diegosouzapw/OmniRoute/pull/12085)) +- **fix(db):** drop three consumer-less 1proxy exports — dead-code base-red on release/v3.8.51 after the barrel deletion (#12055) ([#12087](https://github.com/diegosouzapw/OmniRoute/pull/12087)) +- **fix(api):** clear the six API-route TypeScript regressions the new gate landed red on ([#12094](https://github.com/diegosouzapw/OmniRoute/pull/12094)) +- **fix(release):** never let the tag-push Create Release append auto notes to the curated body ([#12096](https://github.com/diegosouzapw/OmniRoute/pull/12096)) +- **fix(sse):** honor CLIProxyAPI environment API key ([#12099](https://github.com/diegosouzapw/OmniRoute/pull/12099)) — thanks @RaviTharuma +- **fix(oauth):** bind Google refresh to the client that issued the token ([#12106](https://github.com/diegosouzapw/OmniRoute/pull/12106)) — thanks @HouMinXi +- **fix(dashboard):** make RequestLoggerDetail loadable outside Next — CSS via globals.css + CJS/ESM interop (#11703 base-reds) ([#12114](https://github.com/diegosouzapw/OmniRoute/pull/12114)) +- **fix(leases):** project status lease row to lease columns so joined connection PII never escapes ([#12115](https://github.com/diegosouzapw/OmniRoute/pull/12115)) — thanks @geek007git +- **fix(plugins):** do not kill the plugin process when a fire-and-forget hook times out ([#12116](https://github.com/diegosouzapw/OmniRoute/pull/12116)) — thanks @geek007git +- **fix(guardrails):** restore injection-guard logging on middleware-only routes ([#12117](https://github.com/diegosouzapw/OmniRoute/pull/12117)) — thanks @geek007git +- **fix(plugins):** refresh stored manifest from disk on activate so new hook fields reach existing installs ([#12120](https://github.com/diegosouzapw/OmniRoute/pull/12120)) — thanks @geek007git +- **fix(codex):** preserve existing provider state when bulk-import upserts a matching connection ([#12122](https://github.com/diegosouzapw/OmniRoute/pull/12122)) — thanks @geek007git +- **fix(ci):** clear the two base-reds the 2026-08-30 merge batch left on release/v3.8.51 (round 3) ([#12123](https://github.com/diegosouzapw/OmniRoute/pull/12123)) +- **fix(sse):** strip type:'custom' from Claude tools on agentrouter dispatch ([#12126](https://github.com/diegosouzapw/OmniRoute/pull/12126)) — thanks @wahidsadik371-coder +- **fix(sse):** stop advertising video providers the dispatcher cannot run ([#12131](https://github.com/diegosouzapw/OmniRoute/pull/12131)) — thanks @quiterunner-commits +- **fix(search):** treat HTTP 432 and plan limit errors as transient cooldown ([#12139](https://github.com/diegosouzapw/OmniRoute/pull/12139)) — thanks @HouMinXi +- **fix(sse):** refuse an AI Horde queue that cannot fit the request budget ([#12143](https://github.com/diegosouzapw/OmniRoute/pull/12143)) — thanks @quiterunner-commits +- **fix(ci):** clear the base-reds the afternoon merge batch left on release/v3.8.51 (round 5: provider count 352, TS2554/TS2677) ([#12144](https://github.com/diegosouzapw/OmniRoute/pull/12144)) +- **fix(translator):** strip neutral tool_choice when tools absent in Responses-to-Chat (#12141) ([#12166](https://github.com/diegosouzapw/OmniRoute/pull/12166)) — thanks @HouMinXi +- **fix(guardrails):** pass providerId to getResolvedModelCapabilities in checkComboVision (#12112) ([#12169](https://github.com/diegosouzapw/OmniRoute/pull/12169)) — thanks @HouMinXi +- **fix(translator):** strip plaintext reasoning content for opaque responses backends (#12128) ([#12171](https://github.com/diegosouzapw/OmniRoute/pull/12171)) — thanks @HouMinXi +- **perf(stream):** compile hot-path regexes once, bound token caches, fix quadratic buffering ([#12179](https://github.com/diegosouzapw/OmniRoute/pull/12179)) — thanks @opensource-elearning +- **fix:** resolve compression worker file using runtime anchors instead of… ([#12183](https://github.com/diegosouzapw/OmniRoute/pull/12183)) — thanks @NightStalker-87 +- **fix:** prevent Claude Code session kills via liveness-aware readiness + auto model echo ([#12189](https://github.com/diegosouzapw/OmniRoute/pull/12189)) — thanks @opensource-elearning +- **fix(translator):** preserve falsy primitive values in Gemini and Antigravity function response results ([#12191](https://github.com/diegosouzapw/OmniRoute/pull/12191)) — thanks @Siva010 +- **fix(oauth):** mark empty Antigravity projectId as degraded and clear stale errors (#11284) ([#12205](https://github.com/diegosouzapw/OmniRoute/pull/12205)) — thanks @HouMinXi +- **fix(providers):** gate the Codex auto-ping usage read on the shared quota throttle ([#12209](https://github.com/diegosouzapw/OmniRoute/pull/12209)) — thanks @abhisheksharma2411 +- **fix(combos):** prioritize SQLite row id over inner JSON id and notify delete errors ([#12213](https://github.com/diegosouzapw/OmniRoute/pull/12213)) — thanks @HouMinXi +- **fix(providers):** mark featherless.ai as no longer having a free tier (`hasFree: false`) ([#12216](https://github.com/diegosouzapw/OmniRoute/pull/12216)) — thanks @turbolego +- **fix(deepseek-web):** stop Turbopack dev panic in the PoW worker path resolver ([#12221](https://github.com/diegosouzapw/OmniRoute/pull/12221)) — thanks @hartmark +- **fix(chat-admission):** clarify local 503 source ([#12223](https://github.com/diegosouzapw/OmniRoute/pull/12223)) — thanks @keeltrace +- **fix(dev):** silence webpack runtime module warnings ([#12228](https://github.com/diegosouzapw/OmniRoute/pull/12228)) — thanks @backryun +- **fix(db):** use module.require for CommonJS runtime driver loading ([#12230](https://github.com/diegosouzapw/OmniRoute/pull/12230)) — thanks @raheemuddin786 +- **fix(memory):** honest probe-driven FTS5 keyword status + memory_id rowid sync ([#12231](https://github.com/diegosouzapw/OmniRoute/pull/12231)) — thanks @raheemuddin786 +- **fix(system):** propagate abort signal to stream reader in HTTP version checks ([#12232](https://github.com/diegosouzapw/OmniRoute/pull/12232)) — thanks @raheemuddin786 +- **fix(resilience):** isolate local host execution errors from provider circuit breakers ([#12233](https://github.com/diegosouzapw/OmniRoute/pull/12233)) — thanks @raheemuddin786 +- **fix(sse):** sort injected tools deterministically for prompt caching ([#12234](https://github.com/diegosouzapw/OmniRoute/pull/12234)) — thanks @raheemuddin786 +- **fix(combo):** always clear the loop-safety timer, not just on the happy path (#11804) ([#12245](https://github.com/diegosouzapw/OmniRoute/pull/12245)) +- **fix(quality):** base-red pair — stryker tap registration + turn-pin suites aligned to the window gate ([#12255](https://github.com/diegosouzapw/OmniRoute/pull/12255)) — thanks @backryun +- **fix(dev):** qualify Turbopack runtime boundaries ([#12258](https://github.com/diegosouzapw/OmniRoute/pull/12258)) — thanks @backryun +- **fix(combo):** fall back to full pool when collapsed sole survivor is context-too-small ([#12278](https://github.com/diegosouzapw/OmniRoute/pull/12278)) — thanks @opensource-elearning +- **fix(combo):** bound the pre-dispatch unavailable skip so a stale label cannot dark a pool (#12168) ([#12285](https://github.com/diegosouzapw/OmniRoute/pull/12285)) +- **fix(security):** strip Qwen/Alibaba console-session cookies from provider API responses ([#12287](https://github.com/diegosouzapw/OmniRoute/pull/12287)) +- **fix(usage):** console-aware Token Plan guidance + subscription hint on bailian 401 ([#12288](https://github.com/diegosouzapw/OmniRoute/pull/12288)) +- **fix(resourcePressure):** log numeric detail on every rejection, recover faster ([#12293](https://github.com/diegosouzapw/OmniRoute/pull/12293)) — thanks @hartmark +- **fix(release):** drain the 2026-09-01 base-red window — passthrough usage regression + radarPage i18n keys ([#12327](https://github.com/diegosouzapw/OmniRoute/pull/12327)) +- **fix(ci):** openapi-security-tiers checker must honor routeGuard patterns + imported prefixes ([#12350](https://github.com/diegosouzapw/OmniRoute/pull/12350)) — thanks @ggiak +- **perf(ci):** cache node_modules in the npm-ci-retry composite (#8084 D3) ([#12408](https://github.com/diegosouzapw/OmniRoute/pull/12408)) +- **revert(ui):** point CTAs back at their real destinations ([#12410](https://github.com/diegosouzapw/OmniRoute/pull/12410)) +- **fix(release):** drain the 2026-09-02 base-red — rerank-providers import + api-typecheck baseline ratchet ([#12414](https://github.com/diegosouzapw/OmniRoute/pull/12414)) +- **fix(types):** make system prompt injection noImplicitAny-safe ([#12416](https://github.com/diegosouzapw/OmniRoute/pull/12416)) +- **fix(quality):** validate typecheck baseline schema ([#12419](https://github.com/diegosouzapw/OmniRoute/pull/12419)) +- **fix(memory):** resolve rerank provider node cache import ([#12421](https://github.com/diegosouzapw/OmniRoute/pull/12421)) +- **fix(ci):** repair release-root regressions — pack dedup, web-session syntax, uc-image ids ([#12423](https://github.com/diegosouzapw/OmniRoute/pull/12423)) — thanks @backryun +- **fix(providers):** separate MaxAI and UC credential contracts ([#12431](https://github.com/diegosouzapw/OmniRoute/pull/12431)) +- **fix(cli):** preserve Claude settings on config set ([#12432](https://github.com/diegosouzapw/OmniRoute/pull/12432)) — thanks @KrzysiekSko +- **fix(providers):** repair the maxai credential block truncated by merge auto-resolve ([#12433](https://github.com/diegosouzapw/OmniRoute/pull/12433)) +- **fix(quality):** record the 2026-09-02 merged growth in the file-size baseline ([#12434](https://github.com/diegosouzapw/OmniRoute/pull/12434)) +- **fix(uc):** route UC error strings through sanitizeErrorMessage; allowlist the retired codex id ([#12437](https://github.com/diegosouzapw/OmniRoute/pull/12437)) +- **fix(adapta):** redact streamed upstream errors ([#12438](https://github.com/diegosouzapw/OmniRoute/pull/12438)) +- **fix(sse):** remap non-contiguous upstream tool_calls index to a gap-free output_index ([#12445](https://github.com/diegosouzapw/OmniRoute/pull/12445)) — thanks @hartmark +- **fix(quality):** detect duplicate tool_calls entries in one response ([#12446](https://github.com/diegosouzapw/OmniRoute/pull/12446)) — thanks @hartmark +- **fix(conversations):** resolve turn content OmniRoute never sends back to the client ([#12447](https://github.com/diegosouzapw/OmniRoute/pull/12447)) — thanks @hartmark +- **fix(combo):** do not treat credits-exhausted 401 as auth skip ([#12449](https://github.com/diegosouzapw/OmniRoute/pull/12449)) — thanks @RaviTharuma +- **fix(auth):** do not park healthy quota accounts as expired ([#12452](https://github.com/diegosouzapw/OmniRoute/pull/12452)) — thanks @RaviTharuma +- **fix(responses-continuation):** fail closed on a collector-truncated, empty output array ([#12460](https://github.com/diegosouzapw/OmniRoute/pull/12460)) — thanks @hartmark +- **fix(logging):** raise the SSE payload collector's default cap ([#12461](https://github.com/diegosouzapw/OmniRoute/pull/12461)) — thanks @hartmark +- **fix(resilience):** surface Responses failed.error.message in 502s ([#12472](https://github.com/diegosouzapw/OmniRoute/pull/12472)) — thanks @RaviTharuma +- **fix(security):** unbiased maxai X-Random nonce + stricter URL/regex assertions ([#12502](https://github.com/diegosouzapw/OmniRoute/pull/12502)) +- **fix(video):** re-anchor transcript log-redaction so PII/credential maskers can't reopen the leak (#12150 P1 follow-up) ([#12503](https://github.com/diegosouzapw/OmniRoute/pull/12503)) +- **fix(monitoring):** serve cached credentialHealth off the request path ([#12533](https://github.com/diegosouzapw/OmniRoute/pull/12533)) — thanks @RaviTharuma +- **fix(quota):** keep Antigravity Gemini usable when Claude weekly is empty ([#12566](https://github.com/diegosouzapw/OmniRoute/pull/12566)) — thanks @HouMinXi +- **fix(authz):** hard-gate every credential export and CLI-config write (GHSA-5926-2w35-7h4q) ([#12600](https://github.com/diegosouzapw/OmniRoute/pull/12600)) +- **fix(docs):** restore the Next build — REMOVED_PROVIDERS.md had no frontmatter (base-red #12581) ([#12610](https://github.com/diegosouzapw/OmniRoute/pull/12610)) +- **fix(quality):** re-point the zcodeProtocol public-creds allowlist to line 313 ([#12615](https://github.com/diegosouzapw/OmniRoute/pull/12615)) +- **fix(security):** close 3 advisories — search baseUrl exfil, sk- in the error sanitizer, bifrost relay header leak ([#12620](https://github.com/diegosouzapw/OmniRoute/pull/12620)) +- **fix(providers):** sanitize boolean required and nested bare maps for Gemini (#12269) ([#12624](https://github.com/diegosouzapw/OmniRoute/pull/12624)) — thanks @HouMinXi +- **fix(resilience):** keep Overloaded STREAM_EARLY_EOF off the provider breaker ([#12626](https://github.com/diegosouzapw/OmniRoute/pull/12626)) — thanks @HouMinXi +- **fix(catalog):** re-audit free-tier quotas against official pages ([#12649](https://github.com/diegosouzapw/OmniRoute/pull/12649)) +- **fix(dashboard):** keep a request's pending-tracking id stable across combo target retries ([#12650](https://github.com/diegosouzapw/OmniRoute/pull/12650)) — thanks @hartmark +- **fix(tests):** retire dead model ids from the chat-pipeline integration suite (base-red #12581) ([#12670](https://github.com/diegosouzapw/OmniRoute/pull/12670)) +- **fix:** resolve SqliteError no such table compression_run_telemetry during cleanup ([#12682](https://github.com/diegosouzapw/OmniRoute/pull/12682)) — thanks @groovecityJO +- **fix(combo):** treat a pin-only step as implicit connection allowlist ([#12697](https://github.com/diegosouzapw/OmniRoute/pull/12697)) — thanks @HouMinXi +- **fix(ci):** ship .npmrc in published package so legacy-peer-deps applies to consumers (#11544) ([#12699](https://github.com/diegosouzapw/OmniRoute/pull/12699)) — thanks @KooshaPari +- **fix(docker):** pin 4 CLI tools to exact versions (#12576) ([#12703](https://github.com/diegosouzapw/OmniRoute/pull/12703)) — thanks @KooshaPari +- **fix(docker):** document and harden cli profile trust boundary (#12570) ([#12706](https://github.com/diegosouzapw/OmniRoute/pull/12706)) — thanks @KooshaPari +- **fix(sse):** redact video transcript in pre-guardrail rejected-request logs (#12150 P2 item 7) ([#12710](https://github.com/diegosouzapw/OmniRoute/pull/12710)) +- **fix(i18n):** quote placeholder in OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES description (#12505) ([#12769](https://github.com/diegosouzapw/OmniRoute/pull/12769)) — thanks @KooshaPari +- **fix(api/pricing):** surface validation error message as string, not raw object (#12494) ([#12771](https://github.com/diegosouzapw/OmniRoute/pull/12771)) — thanks @KooshaPari +- **fix(sse):** re-enable prompt compression for native Codex passthrough ([#12834](https://github.com/diegosouzapw/OmniRoute/pull/12834)) — thanks @thomasmaerz +- **fix(dashboard):** model health tests for a provider node set to the Responses API now call `/v1/responses` with a Responses-shaped body instead of `/v1/chat/completions` — those models were reported as `Provider returned HTTP 200 but no text content` even though the same model answered normally through `/v1/responses` ([#13070](https://github.com/diegosouzapw/OmniRoute/issues/13070)) ([#13087](https://github.com/diegosouzapw/OmniRoute/pull/13087)) — thanks @ntdatt812 +- fix(routing): stop a round-robin combo's "opencode" targets from collapsing onto the opencode-zen connection (#11912) ([#13283](https://github.com/diegosouzapw/OmniRoute/pull/13283)) +- fix(routing): stop the reactive-compaction debug log from lying when compression is globally disabled (#11977) ([#13276](https://github.com/diegosouzapw/OmniRoute/pull/13276)) +- fix(electron): relativize standalone-bundle symlink targets so Stage 8 manifest verification stops failing on Windows (#11979) ([#13251](https://github.com/diegosouzapw/OmniRoute/pull/13251)) +- fix(dashboard): surface a visible error when Compression Studio's combined preview run fails (#12061) ([#13288](https://github.com/diegosouzapw/OmniRoute/pull/13288)) +- fix(dashboard): make the compression "Effective pipeline" preview honor the active profile and warn when the master switch is off (#12063) ([#13287](https://github.com/diegosouzapw/OmniRoute/pull/13287)) +- fix(providers): scope TinyCMS Web signer's DOM shims to each call instead of leaking them for the process lifetime, and surface a clean HTTP status on a non-JSON interception-toggles error (#12072) ([#13254](https://github.com/diegosouzapw/OmniRoute/pull/13254)) +- fix(guardrails): stop Vision Bridge from re-selecting a model locked after a 404 (#12111) ([#13259](https://github.com/diegosouzapw/OmniRoute/pull/13259)) +- fix(sse): require Responses-shaped body before native OpenAI-compatible passthrough (#12129) ([#13278](https://github.com/diegosouzapw/OmniRoute/pull/13278)) +- fix(providers): minimax-m3 now collapses manual thinking.type:"enabled" to adaptive, preventing upstream 400 (2013) (#12132) ([#13249](https://github.com/diegosouzapw/OmniRoute/pull/13249)) +- fix(db): scope model visibility overrides by modality so hiding a Chat model no longer hides an identically-ID'd Image/Embeddings/etc. model (#12172) ([#13258](https://github.com/diegosouzapw/OmniRoute/pull/13258)) +- fix(db): scope local-provider apiKey dedup to matching base URL so LM Studio/Ollama-style connections support multiple accounts (#12173) ([#13250](https://github.com/diegosouzapw/OmniRoute/pull/13250)) +- fix(providers): refresh Trae's stale Referer/Origin and forward user timezone so imported connections stop failing with 401 (#12190) ([#13255](https://github.com/diegosouzapw/OmniRoute/pull/13255)) +- fix(providers): route opencode-go/gpt-5.6-luna to /responses instead of /chat/completions (#12196) ([#13275](https://github.com/diegosouzapw/OmniRoute/pull/13275)) +- fix(dashboard): allow deleting the last extra-upstream-header row even when invalid (#12251) ([#13252](https://github.com/diegosouzapw/OmniRoute/pull/13252)) +- **fix(i18n):** translate pre-existing `__MISSING__:` keys for `combo.sort`, `requestLogger.detail` expand/collapse, `common.profile`, and `settings.resilienceCredentialHealth*` across 39 locales ([#12272](https://github.com/diegosouzapw/OmniRoute/issues/12272)) ([#13034](https://github.com/diegosouzapw/OmniRoute/pull/13034)) — thanks @HouMinXi +- fix(cli): run the Node.js runtime compatibility guard before the heavy `tsx/esm` + Commander import chain so an unsupported runtime gets a clear message instead of a raw `Invalid regular expression flags` crash (#12296) ([#13253](https://github.com/diegosouzapw/OmniRoute/pull/13253)) +- fix(dashboard): refresh the providers list after deleting a compatible provider node (#12298) ([#13256](https://github.com/diegosouzapw/OmniRoute/pull/13256)) +- fix(usage): fail closed on API-key budget enforcement when a provider's `auto` routing alias has no pricing row, instead of silently counting it as $0 (#12341) ([#13257](https://github.com/diegosouzapw/OmniRoute/pull/13257)) +- **fix(providers):** include the submitted Agnes video model when polling by `video_id` ([#12356](https://github.com/diegosouzapw/OmniRoute/pull/12356)) — thanks @pacocartones +- **fix(routing):** custom OpenAI-compatible nodes now honor the saved Chat/Responses API type after edits instead of letting the node's original ID prefix override the live connection setting ([#11884](https://github.com/diegosouzapw/OmniRoute/issues/11884)). ([#12358](https://github.com/diegosouzapw/OmniRoute/pull/12358)) — thanks @pacocartones +- fix(sse): surface an error instead of a silent empty 200 when a Claude stream closes with zero bytes (#12398) ([#13285](https://github.com/diegosouzapw/OmniRoute/pull/13285)) +- fix(oauth): warn before the dead localhost:8080 redirect in antigravity/gemini `oauth start` (#12413) ([#13265](https://github.com/diegosouzapw/OmniRoute/pull/13265)) +- fix(providers): stop devin-cli spawn error from double-closing the SSE controller (#12517) ([#13261](https://github.com/diegosouzapw/OmniRoute/pull/13261)) +- **fix(rerank):** clamp Voyage `top_k` to the documents actually sent after empty-string filtering, and honor `return_documents: false` in the NVIDIA response adapter (#12523 — thanks @pacocartones) +- **fix(audio):** `/v1/audio/translations` now resolves combo names the way `/v1/audio/transcriptions` already does, so a combo that `GET /v1/models` advertises is fanned out to its targets instead of being rejected with `400 Invalid translation model: . Use format: provider/model`; literal `provider/model` ids and unknown bare names behave as before (#12536 — thanks @pacocartones) +- **fix(gemini):** strip the JSON-Schema-2020-12 `prefixItems` keyword from Gemini tool schemas at every nesting level, so Claude Code tool definitions no longer fail with `400 Unknown name "prefixItems"` on Gemini models (#12540 — thanks @pacocartones) +- **fix(api-manager):** Expose an accessible loading status while API keys are fetched instead of an empty accessibility tree (#12541 — thanks @pacocartones) +- **fix(video):** Clamp `estimateJpegFrameBytes` at zero for padding-only payloads and build the three encode-side frame data URIs from `JPEG_FRAME_DATA_URI_PREFIX` instead of a repeated literal (#12543 — thanks @pacocartones) +- **fix(devin):** accept Windows `DEVIN_AGENTIC_HOME` sandbox paths (`C:\...\.sandbox\...`) in the isolated-home check so the Devin Claude Bridge no longer fails closed on Windows ([#12405](https://github.com/diegosouzapw/OmniRoute/issues/12405)) (#12545 — thanks @pacocartones) +- **fix(db):** Add `conversation_turn_nodes` and orphaned `agentic_conversations` to the auto-cleanup cycle under the existing `retention.callLogs` window, so identity nodes whose call-log content has already been purged no longer accumulate without bound in `storage.sqlite` (#12548 — thanks @pacocartones) +- **fix(i18n):** Wrap the `~/.claude/profiles//settings.json` placeholder in ICU single quotes in the `OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES` feature-flag description across all 42 locales and the TypeScript default, so next-intl no longer fails with `INVALID_MESSAGE: UNCLOSED_TAG` and the Feature Flags card shows the description instead of the raw key (#12549 — thanks @pacocartones) +- **fix(orchestration):** `updateCloudAgentTask` now publishes the task's real `status` on `agent.task.updated` when an update only touches `result`, `activities` or `error`, instead of the fabricated `"updated"` state, and stays silent when no row matched the id (#12550 — thanks @pacocartones) +- **fix(i18n):** the home "Recent Requests" panel and the Provider Topology legend are now translated instead of rendering English copies on non-English dashboards; the legend reads its own `home.topologyLegend*` labels with consistent casing rather than borrowing the memory-settings "Recent" and analytics "Error" strings (#12551 — thanks @pacocartones). +- **docs(reference):** bring the `FEATURE_FLAGS.md` catalog back to 1:1 with `featureFlagDefinitions.ts` — 20 missing flags added, the two `*_BLOCK_THRESHOLD` env-only knobs moved out of the flag tables, category/total counts and the Live WS port corrected, guarded by a static test (#12552 — thanks @pacocartones) +- **fix(i18n):** backfill missing `usage.kiloPass*` strings in 39 locales and restore `featureFlagOmnirouteDisableThinkingLevelVariantsDescription` in `pt.json` ([#12561](https://github.com/diegosouzapw/OmniRoute/issues/12561)) ([#13027](https://github.com/diegosouzapw/OmniRoute/pull/13027)) — thanks @HouMinXi +- fix(docker): default docker-compose app ports (dashboard/API/live-WS) to loopback instead of `0.0.0.0`, closing the anonymous `/v1` LAN/WAN exposure gap left open by `REQUIRE_API_KEY=false` (#12568) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): close DNS-rebinding SSRF gap in webhook outbound-URL guard (#12569) ([#13243](https://github.com/diegosouzapw/OmniRoute/pull/13243)) +- fix(docker): require a per-session token on the VNC browser CDP bridge and isolate it on a dedicated Docker network (#12571) ([#13241](https://github.com/diegosouzapw/OmniRoute/pull/13241)) +- fix(open-sse): write Adobe Firefly session tokens and cookie jars with 0700/0600 permissions instead of the process umask (#12572) ([#13242](https://github.com/diegosouzapw/OmniRoute/pull/13242)) +- fix(api): remove hardcoded wildcard CORS in openai-to-gemini-sse.ts so the centralized fail-closed CORS gate is the sole source of `Access-Control-Allow-Origin` (#12573) ([#13239](https://github.com/diegosouzapw/OmniRoute/pull/13239)) +- fix(api): enforce API key policy (budget/rate-limit/schedule/endpoint scoping) on the ElevenLabs speech-to-text, text-to-speech and voices proxy routes (#12574) ([#13244](https://github.com/diegosouzapw/OmniRoute/pull/13244)) +- fix(sse): cap HuggingChat NDJSON body size and bound the read loop with the fetch timeout so a stalled or hostile upstream cannot buffer unbounded memory (#12577) ([#13282](https://github.com/diegosouzapw/OmniRoute/pull/13282)) +- fix(docker): scope the cliproxyapi/qdrant/bifrost sidecars to loopback by default and forward `CLIPROXYAPI_MANAGEMENT_KEY` into the cliproxyapi container so its management API is not left both unauthenticated and LAN-published (#12578) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): create DB export temp paths with `fs.mkdtempSync` instead of predictable timestamps (#12579) ([#13237](https://github.com/diegosouzapw/OmniRoute/pull/13237)) +- Cline 401 bodies that say "re-authenticate your Cline account" classify as a refreshable OAuth token, not a terminal expired key. The cooling panel no longer labels every cooldown as a 429; it shows the recorded last error instead. (#12594) ([#13060](https://github.com/diegosouzapw/OmniRoute/pull/13060)) — thanks @HouMinXi +- **fix(catalog):** degrade unknown combo targets instead of dropping LCD modalities, and persist OpenRouter `architecture.input_modalities` into the capability snapshot ([#12613](https://github.com/diegosouzapw/OmniRoute/issues/12613)) ([#13042](https://github.com/diegosouzapw/OmniRoute/pull/13042)) — thanks @HouMinXi +- fix(providers): send `x-api-key` instead of `Authorization: Bearer` for OpenCode Zen's `/v1/responses` endpoint (Muse Spark Contributor models), fixing a 401 on OmniRoute's auth header (#12633) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(sse): surface the actionable "Auggie CLI not found" message when the shell reports a missing `auggie` binary via exit code instead of a spawn error (#12645) ([#13263](https://github.com/diegosouzapw/OmniRoute/pull/13263)) +- **fix(config):** Persist deletions of built-in background-degradation entries — when a stored settings record exists its `degradationMap` is now authoritative instead of being merged under the defaults, so an entry the user removed in the dashboard no longer reappears on the next apply or restart ([#12424](https://github.com/diegosouzapw/OmniRoute/issues/12424)) ([#12647](https://github.com/diegosouzapw/OmniRoute/pull/12647)) — thanks @pacocartones +- **fix(gamification):** action-count badge milestones (First Token, Token Consumer, Token Machine, Token Whale, and the token-sharing tier) are now backed by a durable `xp_action_counts` counter incremented in `addXp()`, instead of a live `COUNT(*)` over `xp_audit_log`. The audit log is pruned by `retention.xpAuditLog` (default 30 days), so on a default install those "lifetime" milestones were really "actions in the last 30 days" and unlocked badges could stop unlocking once old rows aged out. `getActionCount()` and `checkActionCountBadges()` now read the same durable source, and a migration backfills existing totals from the surviving audit rows ([#12546](https://github.com/diegosouzapw/OmniRoute/issues/12546)) ([#12651](https://github.com/diegosouzapw/OmniRoute/pull/12651)) — thanks @pacocartones +- **fix(images):** `/v1/images/edits` now iterates a combo's targets the same way `/v1/images/generations` does (#9239) instead of flattening a bare combo to its first target. A combo whose first target is not edit-capable — or lacks credentials — now falls through to a later edit-capable target rather than hard-erroring, and missing credentials are skipped (not a hard `401`) to match the generations path. The per-target skip/terminal classification is extracted into a shared `runImageComboTargets` loop, so generations behavior is unchanged ([#12547](https://github.com/diegosouzapw/OmniRoute/issues/12547)). ([#12653](https://github.com/diegosouzapw/OmniRoute/pull/12653)) — thanks @pacocartones +- fix(sse): add first-byte watchdog to the TLS-fingerprint transport so a stalled wreq body falls back instead of hanging for minutes (#12656) ([#13272](https://github.com/diegosouzapw/OmniRoute/pull/13272)) +- fix(sse): exempt tiny-budget reasoning probes from combo quality failure and surface persisted-cooldown skips in ALL_TARGETS_SKIPPED diagnostics (#12659) ([#13269](https://github.com/diegosouzapw/OmniRoute/pull/13269)) +- fix(models): declare the real ~1M contextLength for OpenCode Zen's Muse Spark 1.2 models instead of falling back to the 200000 provider default (#12681) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(oauth): align codebuddy-cn OAuth User-Agent with the chat/usage CLI version to avoid WAF false positives (#12702) ([#13264](https://github.com/diegosouzapw/OmniRoute/pull/13264)) +- fix(dashboard): surface an authentication-required banner instead of silently blanking database settings for a guest session (#12709) ([#13270](https://github.com/diegosouzapw/OmniRoute/pull/13270)) +- **fix(resilience):** stop unbounded queue that hangs 6min until Aborted — gate, provider slot, and Bottleneck queue now share a per-connection `maxWaitMs` budget; `fail-closed` on exhaust (503); execution backstop `executionMaxWaitMs` overridable per connection with upstream clamp ([#12715](https://github.com/diegosouzapw/OmniRoute/pull/12715)) — thanks @maxmad64bis (with thanks to @Tushar49 for surfacing the slow-provider need in #12635) +- **fix(auto-combo):** every mode pack now carries `quality` and `reliability` weights (`quality` 0.02, 0.03 in `quality-first`; `reliability` 0.03, 0.04 in `reliability-first`), so selecting a pack no longer silences either signal; `DEFAULT_WEIGHTS` is unchanged ([#12731](https://github.com/diegosouzapw/OmniRoute/pull/12731)) — thanks @maxmad64bis +- **fix(sse):** the provider execution pipeline keeps the upstream error `code`/`type` again — both were lost when the non-streaming leg was extracted, so a config-class `422` (Antigravity missing project) degraded into a generic account cooldown instead of reaching the gate that recognises it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(db):** a legacy `call_logs` table no longer aborts startup — the provider-stats index is created after the column healing runs, not before it, so an install predating `request_type` opens instead of failing with `no such column` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(catalog):** a malformed row in the operator-writable custom-models blob no longer kills every `auto/*` pool ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(tests):** realign the guards that the same day's merges left asserting the old behavior — the provider-count total after GreenPT and EURouter, the GLM stream buffer slot that `#12925` turned into a declared parameter, and the injection scan bound that `#13104` reshaped from a head-only window into head-plus-tail ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- fix(cache): fold tool_choice/tools/response_format into the semantic cache signature so a cached tool_calls response can no longer be replayed for a request whose tool policy forbids it (#12734) ([#13267](https://github.com/diegosouzapw/OmniRoute/pull/13267)) +- **fix(models):** stop treating provider-supplied `isFree:true`/`:free`/`0/0` flags as trusted unless the provider has a documented free tier — only the shipped free-tier catalog decides otherwise; fetched `isFree:true` trusted only for a free-tier provider, custom `isFree:true` via a trusted path ([#12744](https://github.com/diegosouzapw/OmniRoute/pull/12744)) — thanks @maxmad64bis +- fix(memory): authenticate the internal /v1/rerank loopback call so memory reranking no longer silently degrades to unranked order when REQUIRE_API_KEY=true (#12745) ([#13268](https://github.com/diegosouzapw/OmniRoute/pull/13268)) +- fix(sse): parse Ollama Cloud's current usage markup (`$X of $Y used` aria-label, nested width style) (#12749) ([#13260](https://github.com/diegosouzapw/OmniRoute/pull/13260)) +- fix(cli): setup-opencode no longer sends an active context's management token to `/v1/models` when `--api-key`/`OMNIROUTE_API_KEY` is supplied — an explicit flag or the env var now always outranks the context's token, and the flag itself is no longer swallowed by the parent program's global `--api-key` option (#12783) ([#13246](https://github.com/diegosouzapw/OmniRoute/pull/13246)) +- fix(sse): register Arcee AI in the executor provider registry so requests reach api.arcee.ai instead of silently falling back to OpenAI (#12784) ([#13277](https://github.com/diegosouzapw/OmniRoute/pull/13277)) +- **fix(quota):** share one upstream quota read when identical saturation checks arrive at the same time, so traffic bursts don't multiply provider API calls ([#12787](https://github.com/diegosouzapw/OmniRoute/pull/12787)) — thanks @maxmad64bis +- **fix(db):** ignore expired or invalid rate-limit cooldown writes so a stale timestamp can't lock a connection that should be usable — clearing still works as before ([#12788](https://github.com/diegosouzapw/OmniRoute/pull/12788)) — thanks @maxmad64bis +- **fix(providers):** single source for provider order with `xao` ranking alongside `xai-oauth` ([#12790](https://github.com/diegosouzapw/OmniRoute/pull/12790)) — thanks @maxmad64bis +- **fix(routing):** off-table models check the free-model catalog before inheriting premium prices, latency bootstraps from the observed pool median, async tiers read live database pricing with a 90-day freshness gate, and the tier cache invalidates on every pricing write ([#12792](https://github.com/diegosouzapw/OmniRoute/pull/12792)) — thanks @maxmad64bis +- **fix(auto-combo):** snapshot scoring uses the observed per-connection account tier and per-model quality instead of neutral constants, and circuit-open providers rank lower at build time ([#12794](https://github.com/diegosouzapw/OmniRoute/pull/12794)) — thanks @maxmad64bis +- **fix(pool):** empty `auto/*` pools now say why they are empty, and the models listing uses the same paid check as routing ([#12795](https://github.com/diegosouzapw/OmniRoute/pull/12795)) — thanks @maxmad64bis +- fix(routing): recognize CLIProxyAPI's 'unknown provider for model' 400 as fallback-worthy (#12800) ([#13284](https://github.com/diegosouzapw/OmniRoute/pull/13284)) +- **fix(sse):** translate-mode streams now emit the estimated token counts as a trailing usage-only chunk before `[DONE]` when the upstream stays silent, so metered chat clients see totals instead of nothing ([#12828](https://github.com/diegosouzapw/OmniRoute/pull/12828)) — thanks @maxmad64bis +- **fix(db):** provider stats stay truthful on empty and legacy databases: fallback counts default to `0` instead of `null`, latency averages read `null` (not `0`) when no durations were recorded, failures logged before the error-type column existed group under `pre_migration` instead of `unclassified`, and per-provider queries use two new composite indexes ([#12832](https://github.com/diegosouzapw/OmniRoute/pull/12832)) — thanks @maxmad64bis +- fix(nvidia): fail open when a synced model catalog goes stale instead of gating forever (#12849) ([#13248](https://github.com/diegosouzapw/OmniRoute/pull/13248)) +- **fix(db):** WAL maintenance lives in its own module: a `TRUNCATE` checkpoint that hits a busy database now warns with its streak and retries once via `PASSIVE` instead of logging success, and closing-time checkpoints no longer report success on builds without a database file, with the busy totals visible in the authenticated monitoring health payload ([#12853](https://github.com/diegosouzapw/OmniRoute/pull/12853)) — thanks @maxmad64bis +- **fix(health):** quota with no snapshots reads empty instead of a contradicting 0% ([#12857](https://github.com/diegosouzapw/OmniRoute/pull/12857)) — thanks @maxmad64bis +- fix(a2a): accept the dashboard's own session cookie on /a2a so "Run message/send" no longer fails with "Unauthorized: missing or invalid API key" (#12888) ([#13271](https://github.com/diegosouzapw/OmniRoute/pull/13271)) +- **fix(logs):** the Logs grid's in-memory filter pass no longer discards rows the SQL query already matched — selecting an API key from the dropdown (which sends the key's id) returns its calls again, the Combo tab shows every combo instead of only those whose name contains a "1", and the model filter and search cover the same columns as the query ([#12896](https://github.com/diegosouzapw/OmniRoute/pull/12896)) — fixes [#12873](https://github.com/diegosouzapw/OmniRoute/issues/12873) — thanks @ntdatt812 +- **fix(a2a):** `/api/a2a/status` now builds the agent card from the request that asked for it, so a gateway reached at a non-localhost host no longer advertises `http://localhost:20128` as its A2A URL ([#12918](https://github.com/diegosouzapw/OmniRoute/pull/12918)). — thanks @ntdatt812 +- **fix(compression):** progressive aging now appends its `[COMPRESSED:aging:…]` annotation after a turn's `tool_result` blocks instead of in front of them, so Anthropic no longer rejects aged conversations with "`tool_use` ids were found without `tool_result` blocks immediately after" ([#12920](https://github.com/diegosouzapw/OmniRoute/pull/12920)). — thanks @ntdatt812 +- **fix(bedrock):** model import now resolves context limits for every vendor prefix instead of only `anthropic.*`, so `global.openai.gpt-5.6-*` no longer imports with a null `inputTokenLimit` and gets rejected pre-flight at the 200k default ([#12921](https://github.com/diegosouzapw/OmniRoute/pull/12921)). — thanks @ntdatt812 +- **fix(stream):** the 64 KB stream buffer GLM asks for is honoured instead of dropped, and the type error it caused no longer fails the API Route Typecheck gate on every open PR ([#12925](https://github.com/diegosouzapw/OmniRoute/pull/12925)) — thanks @ntdatt812 +- **fix(guardrails):** mask PII inside a `tool_result`'s nested content array, which the masker walked past while redacting its sibling block ([#12930](https://github.com/diegosouzapw/OmniRoute/pull/12930)) — thanks @ntdatt812 +- **fix(dashboard):** the Radar catalog table no longer leaves absent data unexplained — empty limits, unknown context windows, and unreported capabilities each explain themselves on hover, and a new check keeps it that way ([#12937](https://github.com/diegosouzapw/OmniRoute/pull/12937)) — thanks @maxmad64bis +- **fix(sse):** geo-blocked opencode requests rotate to the next account proxy instead of failing, so one refused egress no longer aborts the whole chain ([#12941](https://github.com/diegosouzapw/OmniRoute/pull/12941)) — thanks @maxmad64bis +- **fix(codex):** dashboard "clear cooldown" and the CAS recovery path now drop nested `codexScopeRateLimitedUntil` maps in the same write that nulls `rate_limited_until`, and fresh quota snapshots with headroom lift fallback-sourced scope cooldowns parked by quota preflight ([#12817](https://github.com/diegosouzapw/OmniRoute/issues/12817), [#12860](https://github.com/diegosouzapw/OmniRoute/issues/12860), [#12951](https://github.com/diegosouzapw/OmniRoute/pull/12951)) The dashboard reset-credit button (`consumeCodexResetCredit`) now exercises that same snapshot path after a successful redeem, so a filled quota bar is enough to unpark a leftover fallback Codex child without another manual clear. — thanks @HouMinXi +- **fix(db):** resolve `sql-wasm.wasm` across global npm install and hoisted layouts, ensuring OmniRoute can boot cleanly on Node 24 when native `better-sqlite3` is uncompiled. ([#13035](https://github.com/diegosouzapw/OmniRoute/pull/13035)) — thanks @HouMinXi +- **fix(api):** `DELETE /v1/batches/delete-completed` now sweeps only the calling API key's own completed batches (batches with no owner stay out of a key-scoped sweep on purpose), with an explicit instance-wide mode reserved for authenticated dashboard sessions, a 401 for a presented key that is unknown, revoked, deactivated, banned or expired (never falling through to the session branch), audit logging of every sweep, a sanitized 500 on failure, an owner-scoped file half (a key-scoped sweep never nulls a file another tenant owns) and an atomic sweep — chunked in 200-batch transactions in instance mode — so a mid-way error never leaves a batch pointing at a nulled file (GHSA-wvxc-jp3v-5mg5) ([#12969](https://github.com/diegosouzapw/OmniRoute/pull/12969)) +- **fix(combo):** quota-weighted routing stops drawing on an out-of-credit connection — a 402 now invalidates the stored quota snapshot instead of leaving its stale remaining percentage in place, and a snapshot older than 10 minutes no longer counts as confident headroom for the primary pool ([#12972](https://github.com/diegosouzapw/OmniRoute/pull/12972)) ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- **fix(vision):** Custom Models with "Vision capable" checked no longer have image requests swapped to `glm/glm-4.6v` when the client sends the advertised alias (`vllm/path/...`) or the bare path-shaped id — Vision Bridge now matches the stored override for all three id forms ([#12758](https://github.com/diegosouzapw/OmniRoute/issues/12758)) ([#12974](https://github.com/diegosouzapw/OmniRoute/pull/12974)) — thanks @HouMinXi +- **fix(sse):** transient opencode upstream failures rotate to the next account proxy instead of failing, so one flapping egress no longer aborts the whole chain ([#12975](https://github.com/diegosouzapw/OmniRoute/pull/12975)) — thanks @maxmad64bis +- **fix(azure):** Deployments from GPT-6 onward now send `max_completion_tokens` instead of `max_tokens`, which Azure rejects with HTTP 400. The rule matched a literal `gpt-5`, so each new generation arrived broken; it now matches the generation range, while `gpt-35-turbo` still keeps `max_tokens`. ([#13007](https://github.com/diegosouzapw/OmniRoute/pull/13007)) — thanks @ntdatt812 +- **fix(db):** add an opt-in self-restart circuit for sustained critical memory pressure, gate post-cleanup VACUUM behind a minimum freed-rows threshold, and checkpoint the SQLite WAL every 5 minutes with a size guard that escalates to TRUNCATE, so a growing WAL can no longer stall the event loop into a full outage. ([#13011](https://github.com/diegosouzapw/OmniRoute/pull/13011)) — thanks @HouMinXi +- **fix(auth):** an explicit connection pin may probe a quota-disabled row once and re-enable it on success ([#12874](https://github.com/diegosouzapw/OmniRoute/issues/12874)) ([#13017](https://github.com/diegosouzapw/OmniRoute/pull/13017)) — thanks @HouMinXi +- **fix(api):** thread `X-OmniRoute-Fallback-Attempts` through combo chat completions so streaming and non-streaming responses report how many prior legs were attempted ([#13038](https://github.com/diegosouzapw/OmniRoute/pull/13038)) — thanks @HouMinXi +- Restore provider failure classification and credential refresh on non-streaming requests: classify non-2xx failures to lock models on per-model quota exhaustion, update connection rate limits from headers and body, and pass credential refresh handlers to pipeline execution so 401 tokens can be refreshed and retried (#13043). ([#13069](https://github.com/diegosouzapw/OmniRoute/pull/13069)) — thanks @HouMinXi +- **fix(responses):** wrap forced-non-streaming web_search fallback JSON as Responses SSE so Codex still sees `response.completed` ([#13050](https://github.com/diegosouzapw/OmniRoute/pull/13050)) — thanks @HouMinXi +- **fix(antigravity):** Preserve upstream thought-token usage in normalized completion and reasoning token counts ([#13055](https://github.com/diegosouzapw/OmniRoute/pull/13055)) — thanks @pacocartones +- Fix a concurrency-slot leak in the DeepSeek PoW solver: a worker that failed to spawn (for example a missing worker script) never released its slot, so `MAX_CONCURRENT_WORKERS` failures disabled the solver until restart. ([#13097](https://github.com/diegosouzapw/OmniRoute/pull/13097)) — thanks @anhtahaylove +- **fix(acp):** bound the ACP session output buffers — `stdoutBuffer` and `stderrBuffer` now cap at 1 MiB keeping the most recent output behind a visible `[...output truncated...]` marker, and `stderrBuffer` is reset per prompt instead of accumulating for the lifetime of the session. ([#13100](https://github.com/diegosouzapw/OmniRoute/pull/13100)) — thanks @anhtahaylove +- **fix(acp):** release the `stdout`/`exit` listeners and the idle timer that a `sendPrompt` timeout used to leave attached to the `acpManager` singleton, and drop sessions that exited on their own from the session map instead of keeping them forever. ([#13096](https://github.com/diegosouzapw/OmniRoute/pull/13096)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection and PII scanners now read the text a `tool_result` block carries on `content` (string or nested block list), in messages and in system blocks, so tool output is judged by the same rules as user text ([#13101](https://github.com/diegosouzapw/OmniRoute/pull/13101)) — thanks @ntdatt812 +- **fix(gamification):** close the badge notification SSE stream when the request signal is already aborted before the stream starts — a client that disconnects while the route is still awaiting auth used to leave both the 2s unlock poll and the 15s heartbeat running for the lifetime of the process. ([#13106](https://github.com/diegosouzapw/OmniRoute/pull/13106)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection scan now spends its 16 KB budget on both ends of the request instead of the first 16 KB only, so `system`, `instructions`, `query`, `documents` and the newest turns are no longer hidden behind one long message ([#13104](https://github.com/diegosouzapw/OmniRoute/pull/13104)) — thanks @ntdatt812 +- **fix(dashboard):** expose the Volcano Ark console cookie on quota scraping and unwrap connect-error objects so the dashboard shows the upstream message ([#13107](https://github.com/diegosouzapw/OmniRoute/pull/13107)) — thanks @HouMinXi +- **fix(db):** release the `beforeExit`/`SIGINT`/`SIGTERM` handlers when a `node:sqlite` adapter closes, so a closed adapter and its database handle are no longer pinned to `process` for the lifetime of the run — the same treatment #7494 gave the sql.js adapter. ([#13109](https://github.com/diegosouzapw/OmniRoute/pull/13109)) — thanks @anhtahaylove +- **fix(translator):** `contentSchema` and `unevaluatedItems` are now treated as subschema positions by the tool-schema sanitizer, so a truncation placeholder in either is replaced with a permissive schema instead of being forwarded as a string ([#13110](https://github.com/diegosouzapw/OmniRoute/pull/13110)) — thanks @ntdatt812 +- **fix(cli-helper):** clear the `createLogStream` timeout on the abort path — `stop()` aborts the in-flight fetch and returned through the `signal.aborted` branch, which skipped `clearTimeout` and left an armed timer per stopped stream. The stream reader is now also cancelled when the read loop exits early. ([#13114](https://github.com/diegosouzapw/OmniRoute/pull/13114)) — thanks @anhtahaylove +- **fix(docs):** drop TLS-impersonation, MITM-decrypt, supply-chain attestation, and XOR-mask writeups from the public `/docs` catalog and Docker image. Files stay in git for engineers; operators who need them open the repo, not the website. ([#13136](https://github.com/diegosouzapw/OmniRoute/pull/13136)) — thanks @HouMinXi +- **fix(combo):** parse numeric-epoch `rate_limited_until` in the combo cooldown read path ([#13141](https://github.com/diegosouzapw/OmniRoute/pull/13141)) — thanks @maxmad64bis +- **fix(opencode):** both OpenCode plugins now reject a gateway address typed without `http://` at configuration time, instead of publishing every model with an api url no client can call, and the v2 plugin no longer publishes a model card whose api url is blank or relative ([#13142](https://github.com/diegosouzapw/OmniRoute/pull/13142)) — thanks @maxmad64bis +- **fix(providers):** lock opencode model on upstream 400 model-unavailable ([#13146](https://github.com/diegosouzapw/OmniRoute/pull/13146)) — thanks @maxmad64bis +- **fix(logging):** keep the provider exchange rather than the raw client bodies when a call log exceeds its size budget, and show that recovered payload in the request-detail panel instead of replacing it with the stored response body ([#13147](https://github.com/diegosouzapw/OmniRoute/pull/13147)) — thanks @maxmad64bis +- **fix(traffic-inspector):** the WebSocket route no longer leaks a traffic-buffer subscriber and a 30s ping timer when the client socket is already closed at handler time — listeners are attached before any resource is acquired, a destroyed socket bails out early, and the ping interval stops on a dead socket where `write()` never throws ([#13155](https://github.com/diegosouzapw/OmniRoute/pull/13155)) — thanks @anhtahaylove +- **fix(telegram):** bound the per-user API key cache in the Telegram chat proxy so a burst of distinct chat ids can no longer grow the process heap without limit ([#13165](https://github.com/diegosouzapw/OmniRoute/issues/13165)) ([#13166](https://github.com/diegosouzapw/OmniRoute/pull/13166)) — thanks @anhtahaylove +- **fix(stream):** cancel the upstream response body when the JSON-to-SSE sniff unwinds on a body timeout, so a stalled upstream no longer pins the connection ([#13169](https://github.com/diegosouzapw/OmniRoute/issues/13169)) ([#13171](https://github.com/diegosouzapw/OmniRoute/pull/13171)) — thanks @anhtahaylove +- **fix(telegram):** authenticate webhook deliveries with Telegram's `secret_token` so an unauthenticated caller can no longer mint API keys or spend upstream quota ([#13172](https://github.com/diegosouzapw/OmniRoute/issues/13172)) ([#13175](https://github.com/diegosouzapw/OmniRoute/pull/13175)) — thanks @anhtahaylove +- **fix(models):** give discoverable Gemini 3.8 Flash ids their own 65536 output spec so Antigravity no longer clamps them to 16384 ([#13195](https://github.com/diegosouzapw/OmniRoute/pull/13195)) — thanks @HouMinXi +- **fix(dashboard):** leftover catalog-removed provider rows (gemini-cli) can be listed and purged from the providers page ([#13067](https://github.com/diegosouzapw/OmniRoute/issues/13067)) ([#13197](https://github.com/diegosouzapw/OmniRoute/pull/13197)) — thanks @HouMinXi +- **fix(auth):** a dashboard session now requires the `authenticated: true` claim that login, OIDC and the session refresh already emit — a JWT merely signed with `JWT_SECRET` (for example the Cursor CLI passthrough token, which any API-key holder can obtain) no longer verifies as the `auth_token` cookie on any route, the WebSocket handshake or the live server; existing sessions keep working ([#13298](https://github.com/diegosouzapw/OmniRoute/issues/13298)) ([#13375](https://github.com/diegosouzapw/OmniRoute/pull/13375)) +- **fix(api):** `DELETE /v1/batches/delete-completed` now applies the caller's API-key policy (endpoint allowlist, schedule, usage cap, rate limit) like every other `/v1` route, commits the key-scoped sweep per 200-batch chunk instead of holding one write lock for the whole sweep, refuses to loop without progress, and rejects a scope that names both a key and `allTenants` ([#13684](https://github.com/diegosouzapw/OmniRoute/pull/13684)) +- Restore the API-route typecheck gate: the non-streaming leg result lost its discriminated-union narrowing after the server-owned tool loop reassignment, producing 13 new TS2339 diagnostics in `chatCore.ts`. ([#12963](https://github.com/diegosouzapw/OmniRoute/pull/12963)) +- Restore four non-streaming behaviours the server-owned tool loop refactor dropped: client aborts map to 499 with the fixed `Request aborted` message, an aborted request no longer logs a synthetic `clientResponse`, Claude prompt-cache telemetry is recorded again, and a body that cannot be canonicalized no longer throws when the tool loop is off. ([#12990](https://github.com/diegosouzapw/OmniRoute/pull/12990)) +- **fix(skills):** The CLI registry parser now reads positionals declared with `.addArgument()`, not only those written inline in `.command()`. `tunnel create [type]` was being published as `tunnel create`, so the agent-skills sync gate reported drift on every branch and regenerating would have deleted the argument. ([#13009](https://github.com/diegosouzapw/OmniRoute/pull/13009)) — thanks @ntdatt812 +- **fix(combos):** dashboard combo test uses a short prompt, serial probes, and a 60s timeout so reasoning models and rate-limited free pools do not fail the health check ([#13001](https://github.com/diegosouzapw/OmniRoute/pull/13001)) — thanks @HouMinXi +- fix(compression): terminate idle worker threads on eviction so long-running instances stop leaking OS threads and MessagePorts ([#13091](https://github.com/diegosouzapw/OmniRoute/pull/13091)) — thanks @anhtahaylove +- Redact Google API keys of any length in error bodies: the pattern required exactly 39 characters, so shorter or longer `AIza…` credentials passed through unredacted. ([#12964](https://github.com/diegosouzapw/OmniRoute/pull/12964)) +- **fix(grok-cli):** a 402 "Grok Build usage balance exhausted" parks that Grok login as out of credit (Grok Build CLI, grok.com cookie, and xAI OAuth share the weekly pool). Combo routing then tries the next login instead of locking the model for every account in the pool ([#13061](https://github.com/diegosouzapw/OmniRoute/pull/13061)) — thanks @HouMinXi +- fix(compression): spawn the LLMLingua worker with a file URL object so compression actually runs instead of silently failing open on Node ([#13093](https://github.com/diegosouzapw/OmniRoute/pull/13093)) — thanks @anhtahaylove +- **fix(test):** run the local `test` and `test:unit` scripts at concurrency 4 so a full-suite run no longer exhausts the machine's commit charge and kills unrelated processes ([#13187](https://github.com/diegosouzapw/OmniRoute/pull/13187)) — thanks @anhtahaylove +- **fix(monitoring):** `GET /api/monitoring/health` `credentialHealth` now includes a bounded `failedConnections` list (`connectionId`, `status`, sanitized `lastError`) when the probe-cache gauge `failed>0`, plus `source: probe-cache` and a cheap `staleDbNonOkCount` for sticky SQLite `test_status` on active rows. Documents that the live gauge is not `provider_connections.test_status`. ([#12876](https://github.com/diegosouzapw/OmniRoute/pull/12876)) — thanks @RaviTharuma +- **fix(resilience):** an openai-compatible multi-upstream gateway no longer marks the whole connection `credits_exhausted` when a single upstream (or the `/models` / representative-model health probe) returns 402. Quota failures stay model-scoped; true connection-wide auth failures are unchanged. Single-credential 402 key disable ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239)) is preserved. ([#12875](https://github.com/diegosouzapw/OmniRoute/pull/12875)) — thanks @RaviTharuma +- **fix(dashboard):** nine fixes on the Orchestration Canvas, all diagnosed in the Phase 2 reviews. A source that fails now keeps the timestamp of its FIRST failure instead of being re-stamped every poll — the stale line said "since the last poll" no matter how long the source had been down, and the churn also defeated the snapshot's stable identity (it serializes the source list), so the canvas re-rendered on every tick while anything was broken. A source that HAD data and only then started failing is flagged too: previously only a source with no node at all got the warning, so a source that went down mid-session kept a healthy-looking node forever. The rest are pointwise: clicking a filter chip cancels the pending search debounce (left armed, it fired ~300ms later and silently reverted the chip); the search input carries an accessible name; `?state=running, failed` parses like the unpadded form instead of dropping the padded value; the CSV toggle helper is defined once in `model/urlParams.ts` rather than twice; the agents tab tells "nothing running" apart from "the filter matched nothing", offering a clear-filters button instead of setup links that would be wrong advice there; edges stop emitting SMIL particles above 40 simultaneously active edges, keeping the colored stroke; and the drawer's error banner clears when a retried action succeeds. ([#12983](https://github.com/diegosouzapw/OmniRoute/pull/12983)) +- **fix(i18n):** the nine locales added with the EU-language batch (Greek, Estonian, Irish, Croatian, Lithuanian, Latvian, Maltese, Slovenian, Serbian) were missing the eleven Orchestration Canvas keys that Phase 3 introduced, so the compare-runs panel and the "no runs match these filters" empty state fell back to English in those languages (`deepMergeFallback` substitutes English for an absent key, so nothing rendered blank — it rendered untranslated). The coverage gate does not catch this: it enforces an 80% floor per locale, and eleven missing keys out of ~13,000 leaves coverage at 99.9%. Translated for real in each language, calibrated against the wording each file already uses for "run", "filter" and "skill". ([#13294](https://github.com/diegosouzapw/OmniRoute/pull/13294)) +- **fix(dashboard):** five follow-ups from the Phase 2 Orchestration Canvas review. The History drawer now shows the memory section for runs that already left the live TTL window: the persisted `memory_hits` event is parsed into `metadata.memoryHits` with the same defensive validation the drawer applies, and — because that event is observability rather than a state transition — it no longer leaks into the timeline, where it had been inheriting the task's state and rendering as a duplicate transition. The A2A memory recall runs against its own 1.5s deadline instead of inheriting the memory backend's 30s one; overshooting degrades exactly like any other recall failure (no hits, task proceeds), and the timer is cleared on both paths. Repeating a Conductor run carries its `requirements.cli`/`requirements.model` forward, so the new run is pinned to the same runner profile and model rather than drifting to whatever the fleet picks. A successful repeat from the Agents tab now focuses the run it created, instead of leaving the operator on the finished one. And the auth test for `POST /api/conductor/tasks` moved into the shared `ROUTES` array rather than restating the pattern. ([#12988](https://github.com/diegosouzapw/OmniRoute/pull/12988)) +- fix(plugins): stop leaking an exit listener per plugin hook timeout, which triggered MaxListenersExceededWarning on plugins that ignore SIGTERM ([#13092](https://github.com/diegosouzapw/OmniRoute/pull/13092)) — thanks @anhtahaylove +- **fix(validation):** Provider node edits no longer fail with a generic "Invalid request" when the optional daily-quota reset fields are left blank. The dashboard sends `dailyQuotaResetTimezone` and `dailyQuotaResetHour` as `null`, and only the hour accepted it. ([#13066](https://github.com/diegosouzapw/OmniRoute/issues/13066)) ([#13083](https://github.com/diegosouzapw/OmniRoute/pull/13083)) — thanks @ntdatt812 +- **fix(combo):** quota-aware expansion drops banned, inactive, missing, and wrong-provider connections before quota fetch or model dispatch; pins and allowlists stay selectors, not a bypass. Antigravity automatic exhaustion now requires a reported zero remaining, so a positive balance below 1% stays eligible. ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- Fix an intermittent failure in the quota-weighted routing test suite: scores are a function of `Date.now()`, so peers with identical quota scored microseconds apart never tied and swapped order. ([#12946](https://github.com/diegosouzapw/OmniRoute/pull/12946)) +- fix(volcengine): resolve quota window mapping gaps, safe multi-connection console binding, and response cookie sanitization ([#12950](https://github.com/diegosouzapw/OmniRoute/pull/12950)) — thanks @HouMinXi +- **fix(test):** resolve the WebDAV handler path with `fileURLToPath` so the suite's 37 WebDAV tests run on Windows instead of failing with a doubled `C:\C:\` drive prefix ([#13196](https://github.com/diegosouzapw/OmniRoute/pull/13196)) — thanks @anhtahaylove +- **fix(ui):** fall back from invalid AnySearch icon alias (direct commit `9587e07b69`) +- **fix(sse):** stop rebuilding a truncated summary from the collector's cap-dropped event array ([#12718](https://github.com/diegosouzapw/OmniRoute/pull/12718)) — thanks @hartmark +- **fix(conversations):** show a pending spinner for unresolved tool nodes instead of "(empty)" ([#12727](https://github.com/diegosouzapw/OmniRoute/pull/12727)) — thanks @hartmark +- **fix(streaming):** fail fast when an upstream stream produces only lifecycle/heartbeat events, never real content ([#12741](https://github.com/diegosouzapw/OmniRoute/pull/12741)) — thanks @hartmark +- **fix(responses-continuation):** bridge the write-in-flight window with an in-memory pending store ([#12854](https://github.com/diegosouzapw/OmniRoute/pull/12854)) — thanks @hartmark +- **fix(translator):** drop or map agent_message on Chat Completions fallback ([#12880](https://github.com/diegosouzapw/OmniRoute/pull/12880)) — thanks @RaviTharuma +- **fix(api):** page and stream-complete GET /v1/models for large catalogs ([#12882](https://github.com/diegosouzapw/OmniRoute/pull/12882)) — thanks @RaviTharuma +- **fix(quota):** align AUTH window labels with usage API and clarify 503 ([#12884](https://github.com/diegosouzapw/OmniRoute/pull/12884)) — thanks @RaviTharuma +- **fix(ci):** clear two base-reds on release/v3.8.51 — mutation-coverage gate + image-only-model guard ([#12945](https://github.com/diegosouzapw/OmniRoute/pull/12945)) +- **fix(api):** scope batch bulk-delete to the calling API key ([#13211](https://github.com/diegosouzapw/OmniRoute/pull/13211)) +- **fix(tests):** align model-sync log assertion to shipped path redaction (base-red) ([#13227](https://github.com/diegosouzapw/OmniRoute/pull/13227)) +- **fix(test):** make the ToS heading guard actually require the parentheses ([#13228](https://github.com/diegosouzapw/OmniRoute/pull/13228)) +- **fix(quality):** register 4 drifted covering tests in stryker tap.testFiles ([#13229](https://github.com/diegosouzapw/OmniRoute/pull/13229)) +- **fix(logging):** mask *-api-key headers in request-log pipeline (#13273) ([#13401](https://github.com/diegosouzapw/OmniRoute/pull/13401)) — thanks @KooshaPari +- **fix(tests):** correct stale Jina catalog prefix in custom-models test ([#13403](https://github.com/diegosouzapw/OmniRoute/pull/13403)) — thanks @KooshaPari +- **fix(backend):** prune old backups after health-check-repair snapshots (#13308) ([#13404](https://github.com/diegosouzapw/OmniRoute/pull/13404)) — thanks @KooshaPari +- **fix(backend):** guard memory_fts_au trigger with WHEN clause to stop FTS bloat (#13326) ([#13405](https://github.com/diegosouzapw/OmniRoute/pull/13405)) — thanks @KooshaPari +- **fix(free-tiers):** remove obsolete Together $25 signup credit (#12526) ([#13407](https://github.com/diegosouzapw/OmniRoute/pull/13407)) — thanks @KooshaPari +- **fix:** skip non-chat generation models in Test all models ([#13409](https://github.com/diegosouzapw/OmniRoute/pull/13409)) — thanks @KooshaPari +- **fix(providers):** de-duplicate agy and antigravity model catalogs ([#13410](https://github.com/diegosouzapw/OmniRoute/pull/13410)) — thanks @KooshaPari +- **fix(dashboard):** send provider in body for mini-playground web search ([#13411](https://github.com/diegosouzapw/OmniRoute/pull/13411)) — thanks @KooshaPari +- **fix(resilience):** add CJK quota-exhaustion patterns to 429 classifier ([#13412](https://github.com/diegosouzapw/OmniRoute/pull/13412)) — thanks @KooshaPari +- **fix(backend):** eval runner: failed calls no longer score as passed + dotAll regex ([#13413](https://github.com/diegosouzapw/OmniRoute/pull/13413)) — thanks @KooshaPari +- **fix(backend):** eval runner opts out of output-style and memory injection ([#13414](https://github.com/diegosouzapw/OmniRoute/pull/13414)) — thanks @KooshaPari +- **fix:** chain serial quarantine step in npm test script (fixes #13286) ([#13418](https://github.com/diegosouzapw/OmniRoute/pull/13418)) — thanks @KooshaPari +- **fix(antigravity):** derive stable session ID from credentials for prompt-cache reuse (#13305) ([#13424](https://github.com/diegosouzapw/OmniRoute/pull/13424)) — thanks @KooshaPari +- **fix(memory):** merge memory into top-level system field instead of unshifting at messages[0] (#13425) ([#13427](https://github.com/diegosouzapw/OmniRoute/pull/13427)) — thanks @KooshaPari +- **fix(compression):** preserve instruction blocks from lossy engines ([#13523](https://github.com/diegosouzapw/OmniRoute/pull/13523)) — thanks @KooshaPari +- **fix(compression):** preserve polarity words and newlines in ultra heuristic ([#13524](https://github.com/diegosouzapw/OmniRoute/pull/13524)) — thanks @KooshaPari +- **fix(compression):** run user preservePatterns before built-in patterns (#13457) ([#13525](https://github.com/diegosouzapw/OmniRoute/pull/13525)) — thanks @KooshaPari +- **fix(reasoning):** expand deepseek-v4 regex to match v4.1+ and add command-code provider (#13430) ([#13528](https://github.com/diegosouzapw/OmniRoute/pull/13528)) — thanks @KooshaPari +- **fix(providers):** add alwaysReasons metadata for reka-flash-3 ([#13531](https://github.com/diegosouzapw/OmniRoute/pull/13531)) — thanks @KooshaPari +- **fix(playground):** allow Compare columns to scroll independently ([#13532](https://github.com/diegosouzapw/OmniRoute/pull/13532)) — thanks @KooshaPari +- **fix(security):** empty whitelist allows through to prevent admin lockout ([#13534](https://github.com/diegosouzapw/OmniRoute/pull/13534)) — thanks @KooshaPari +- **fix(tests):** add DISABLE_IOREG_STRATEGY env var for macOS test compatibility (fixes #13467) ([#13539](https://github.com/diegosouzapw/OmniRoute/pull/13539)) — thanks @KooshaPari +- **fix(zed-hosted):** map developer role and Gemini enums for Zed proxy ([#13543](https://github.com/diegosouzapw/OmniRoute/pull/13543)) — thanks @KooshaPari +- **fix(analytics):** normalize provider aliases to canonical id in usage_history ([#13545](https://github.com/diegosouzapw/OmniRoute/pull/13545)) — thanks @KooshaPari +- **fix(combo):** use traceId for call log id to prevent UNIQUE constraint failure ([#13546](https://github.com/diegosouzapw/OmniRoute/pull/13546)) — thanks @KooshaPari +- **fix(auth):** check model lockout before returning synthetic noauth connection ([#13547](https://github.com/diegosouzapw/OmniRoute/pull/13547)) — thanks @KooshaPari +- **fix(guardrails):** skip credential redaction for base64 image data URLs ([#13550](https://github.com/diegosouzapw/OmniRoute/pull/13550)) — thanks @KooshaPari +- **fix(resilience):** increase requestQueue.maxWaitMs default from 15s to 30s ([#13553](https://github.com/diegosouzapw/OmniRoute/pull/13553)) — thanks @KooshaPari +- **fix(docs):** document OMNIROUTE_READY_TIMEOUT_MS and allowlist the test-only DISABLE_IOREG_STRATEGY ([#13692](https://github.com/diegosouzapw/OmniRoute/pull/13692)) + ### 📝 Maintenance ---- +- **docs(readme):** reconcile live v3.8.50 provider, free-tier, CLI, routing, test, community, sponsor, acknowledgment, and SVG metrics with their audited source denominators, including a deduplicated OmniRoute-in-Action snapshot and distinct contributor rankings for merged pull requests, GitHub-attributed commits, and Git history ([#11356](https://github.com/diegosouzapw/OmniRoute/pull/11356)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **docs:** sync the canonical provider count 354 → 356 across `README.md`, `AGENTS.md`, `llm.txt` (+ 42 i18n mirrors), the four README SVG diagrams, `docs/reference/PROVIDER_REFERENCE.md` (regenerated) and the `package.json` description after Opper (#11629) and 1min.ai (#11631) boarded the catalog — closes the `check:docs-counts-sync` strict drifts that kept `release/v3.8.51` red ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11673](https://github.com/diegosouzapw/OmniRoute/pull/11673)) — thanks @jonlwheat2-gif +- **test(providers):** update count-derived assertions after the v3.8.51 provider additions — `APIKEY_PROVIDERS` 233 → 235 (Opper #11629 + 1min.ai #11631), reserved-prefix REGISTRY walk 395 → 398, `WEB_FETCH_PROVIDERS` now includes `nimble-search` (#11620), and the provider translate-path golden snapshot regenerated ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11674](https://github.com/diegosouzapw/OmniRoute/pull/11674)) — thanks @jonlwheat2-gif +- **test(sse):** bump the hard-lease connection-query inventory for `src/lib/tokenHealthCheck.ts` to 2 — the verify-only web-cookie sweep added by #11495 split the single `getProviderConnections` call into oauth + cookie variants, which the frozen inventory had not tracked ([#11495](https://github.com/diegosouzapw/OmniRoute/pull/11495)). ([#11677](https://github.com/diegosouzapw/OmniRoute/pull/11677)) — thanks @jonlwheat2-gif / @oyi77 +- **chore(release):** clear the `release/v3.8.51` base-red gates — docs migration count (+38 i18n mirrors), stryker `tap.testFiles`, lockfile registry host, stale ESLint suppressions, the 7 lint regressions they were hiding, the stale `APIKEY_PROVIDERS` partition count (231 → 233), the `glm-5.3-max` tier missing from the GLM test table, a dead `modelType === "chat"` comparison in the models catalog, and the `resolveLiveWsUrl` / `sanitizeLiveWsPort` helpers from #11388 that a later merge dropped from `wsPath.ts` (dashboard live WebSocket URL resolution) ([#11502](https://github.com/diegosouzapw/OmniRoute/pull/11502)) — thanks @MumuTW +- **chore(free-models):** the free-tier regime table now declares which totals figure each regime feeds and whether it may take the no-auth shortcut, so the sets that used to repeat those answers by hand are derived from it — a new `freeType` no longer compiles until it has answered every question, instead of silently contributing to no total ([#11537](https://github.com/diegosouzapw/OmniRoute/pull/11537)) — thanks @maxmad64bis +- **docs(routing):** LKGP strategy copy now states provider-level stickiness and the explicit rules fallback instead of promising per-target pinning ([#11555](https://github.com/diegosouzapw/OmniRoute/pull/11555)) — thanks @maxmad64bis +- **test(chatcore):** pin the response tool-name alias resolution against the ordering hazard that broke every Gemini/Antigravity MCP tool call in v3.8.49. `extractRequestToolIdentityMap` deletes `translatedBody._toolNameMap`, so the later read is always undefined and the ledger survives only through the `requestToolIdentityMap` fallback — removing that fallback previously left the entire tool-name suite green. The resolution moves into `resolveResponseToolNameMap()` next to the map it depends on, with a regression guard that fails without the recovery. No behaviour change. ([#11575](https://github.com/diegosouzapw/OmniRoute/pull/11575)) — thanks @amartinawi +- **docs(env):** document `OMNIROUTE_CORPUS_CACHE_SIZE` in `.env.example` and `ENVIRONMENT.md`, so the env/docs contract check stops failing Docs Gates on every branch ([#11576](https://github.com/diegosouzapw/OmniRoute/pull/11576)) — thanks @ntdatt812 +- **chore(stryker):** register `route-guard-tunnel-processes-local-only` and `universal-quota-aware-routing` in `tap.testFiles`, so their mutant kills count for the three modules they cover ([#11577](https://github.com/diegosouzapw/OmniRoute/pull/11577)) — thanks @ntdatt812 +- **test(authz):** pin `LOCAL_ONLY_API_GET_EXEMPTIONS` by exact membership instead of by entry count, so the guard names the offending path and also catches a substitution ([#11580](https://github.com/diegosouzapw/OmniRoute/pull/11580)) — thanks @ntdatt812 +- **test(build):** pin `resolveNextBuildBundlerFlag` to the `OMNIROUTE_USE_TURBOPACK` contract it implements, across runtimes, instead of asserting an unimplemented Bun override ([#11583](https://github.com/diegosouzapw/OmniRoute/pull/11583)) — thanks @ntdatt812 +- **test(claude-web):** the Claude Web transport test's barrel anchors no longer pin the exact construction spelling. #11421 made the executor map lazy, so `/"claude-web": new ClaudeWebExecutor\(\)/` stopped matching and the test went red on `release/v3.8.51` itself — a base-red gate every open PR inherited. The anchors now match the alias key and the class name and tolerate either spelling in between, and were checked to still fail when an alias is genuinely repointed at another executor ([#11585](https://github.com/diegosouzapw/OmniRoute/pull/11585)) — thanks @ntdat812 +- **test(models):** pin `qwen3.8-max` resolution per provider — two of the four catalogs now serve the bare id and no longer carry `-preview` ([#11590](https://github.com/diegosouzapw/OmniRoute/pull/11590)) — thanks @ntdatt812 +- **test(executors):** drop the stale 128-tool truncation assertion that contradicted the #11444 pass-through contract ([#11591](https://github.com/diegosouzapw/OmniRoute/pull/11591)) — thanks @ntdatt812 +- **test(token-health):** pin the current terminal-skip boundary — expired with retry budget is probed, `account_deactivated` stays skipped ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)) — thanks @ntdatt812 +- **test(models):** follow #11503's retarget of the `gemini-3-pro-high` alias to the hyphenated catalog id ([#11593](https://github.com/diegosouzapw/OmniRoute/pull/11593)) — thanks @ntdatt812 +- **refactor(video bridge):** extract per-video acquisition, whole-result caching, description, and metrics/abort/cleanup out of `VideoBridgeGuardrail.preCall` into a `processVideoPart` seam in a new `videoBridgePipeline.ts`, behind explicit `VideoMediaBrokerPort`, `VideoAudioTranscriptionPort`, and `VideoDrilldownPort` boundaries; `preCall` now only handles request traversal, policy, and response aggregation. The Video tab's FFmpeg/ffprobe runtime status is now an explicit `unknown` / `restricted` / `unavailable` / `available` state instead of a nullable boolean pair, fixing a case where an in-flight or failed probe was mislabeled as "install FFmpeg" ([#11657](https://github.com/diegosouzapw/OmniRoute/issues/11657)). ([#12016](https://github.com/diegosouzapw/OmniRoute/pull/12016)) +- **test(opencode-plugin):** add unit test coverage for `formatFreeBudget()` naming helper ([#11660](https://github.com/diegosouzapw/OmniRoute/pull/11660)) — thanks @f9td56dbgh-hub +- **docs(video):** clarify that the Video Bridge transcript `source` field (`client`, `embedded`, `audio-bridge`) is presently caller-declared and not yet server-verified — OmniRoute enforces the enum shape but does not cryptographically confirm that an `embedded`/`audio-bridge` label came from a server-owned extraction ([#11661](https://github.com/diegosouzapw/OmniRoute/issues/11661)). ([#12001](https://github.com/diegosouzapw/OmniRoute/pull/12001)) +- **chore(providers):** retire the distributed Raycast Relay and Hailuo Web integrations after a provenance audit confirmed substantial derivation from GPL-3.0 sources; the official GitHub and MiniMax API providers remain available. Migration 163 disables legacy connections and invalidates their active leases while preserving connection IDs, audit/history rows, quota snapshots, usage records, and fail-closed API-key connection policies. ([#11691](https://github.com/diegosouzapw/OmniRoute/pull/11691)) +- **chore(providers):** retire the Felo Web provider while its GPL-derived provenance remains on licensing HOLD ([#11698](https://github.com/diegosouzapw/OmniRoute/pull/11698)) — stale runtime and database references now fail closed while existing connection, lease, and usage history remains preserved for auditability. +- **fix(sse):** retire the Gemini Web image-generation bridge while provenance and licensing remain on HOLD, preserving Gemini Web chat/auth/tools and legitimate Gemini/Nano Banana image providers ([#11708](https://github.com/diegosouzapw/OmniRoute/pull/11708)) +- **chore(audio):** retire the built-in EdgeTTS provider and its 11-voice catalog while provenance/licensing review remains on HOLD; the generic `/v1/audio/speech` endpoint and the other speech providers remain available ([#11711](https://github.com/diegosouzapw/OmniRoute/pull/11711)) +- **fix(providers):** retire the unofficial Qwen Web integration and its `qwen-web`/`qw` identities while third-party provenance remains on HOLD ([#11713](https://github.com/diegosouzapw/OmniRoute/pull/11713)) — direct requests now fail closed with a sanitized HTTP 410 and combos skip the retired target, while migration guards preserve connection IDs, allowlists, usage/audit history and invalidate active leases. Supported Qwen API, Qwen Code, cloud, token-plan, audio, local and model-name surfaces remain available; restoring Qwen Web requires a qualified provenance/license decision. +- **fix(providers):** retire `microsoft-designer-web` and its `msdesigner` alias from runtime and catalogs while preserving stored credentials, provider history and allowlists as inactive records — source dependency on gpt4free's [`MicrosoftDesigner.py`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/g4f/Provider/needs_auth/MicrosoftDesigner.py) is proven at snapshot `0943d16eef757a44deb60651c07bbe5fa958cafe`, whose [`LICENSE`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/LICENSE) is GPL-3.0; expressive adaptation is probable, which records provenance/licensing risk without asserting a legal conclusion ([#11720](https://github.com/diegosouzapw/OmniRoute/pull/11720)). +- **docs(legal):** centralize pinned MIT notices for the vendored GCF codec and local flag assets ([#11726](https://github.com/diegosouzapw/OmniRoute/pull/11726)) +- Split the 2,952-line `adobeFireflyClient.ts` into nine focused modules (catalog, credentials, payload, ARP session, upload, responses, IMS, account, poll) behind an unchanged barrel — no consumer edits and no public API change (#11728). — thanks @MumuTW +- **fix(dashboard):** replaced the Model Lockout card's unprovenanced notification MP3 with a short, locally synthesized Web Audio chime ([#11731](https://github.com/diegosouzapw/OmniRoute/pull/11731)) — both toggles retain optional audible feedback without shipping replacement media or third-party code, and unsupported, suspended, or rejected audio contexts fail open without blocking the setting change. +- **chore(deepseek-web):** remove the provenance-unresolved DeepSeek PoW WASM binary and its runtime loader/tracing while retaining the existing JavaScript solver (slower at high difficulty) ([#11732](https://github.com/diegosouzapw/OmniRoute/pull/11732)). +- **chore(assets):** remove four unused provider assets whose redistribution provenance could not be verified while retaining the active Freebuff SVG ([#11735](https://github.com/diegosouzapw/OmniRoute/pull/11735)) +- **docs(assets):** retired two raster images whose redistribution provenance could not be verified ([#11736](https://github.com/diegosouzapw/OmniRoute/pull/11736)) — PicoClaw documentation now reuses the existing SVG, while Zoo Code remains a text link without an image. +- **docs(legal):** add pinned notices for six LobeHub-derived provider SVGs and 65 byte-exact theSVG assets, retaining registry claims as unverified metadata with explicit trademark and HOLD boundaries ([#11737](https://github.com/diegosouzapw/OmniRoute/pull/11737)). +- **chore(quality):** added a versioned 225/225 provider-asset provenance manifest and blocking drift gate ([#11743](https://github.com/diegosouzapw/OmniRoute/pull/11743)) — the inventory pins magic MIME and SHA-256 for every physical file, records immutable source matches for 71 assets, keeps 69 probable and 85 unresolved entries explicit but non-blocking, and treats provenance as source evidence rather than copyright or trademark clearance. +- **refactor(mitm):** replace four previously derived Traffic Inspector utilities — conversation normalization, SSE merging, secret masking, and header sanitization — with independent clean-room implementations based on public provider schemas, WHATWG SSE, and RFC 6750, while keeping the two independently classified passthrough surfaces unchanged ([#11748](https://github.com/diegosouzapw/OmniRoute/pull/11748)). +- Added a unit test that fails seven days before any dated pack under `config/` (`validUntil` and sibling keys) lapses, naming the file and key. The Alibaba free-tier pack expired on 2026-08-27 and turned every PR red the next morning with no commit involved; renewal now happens on someone's terms, not the clock's. ([#11891](https://github.com/diegosouzapw/OmniRoute/pull/11891)) +- `check:workflows` now fails (under `--strict`/`--ratchet`) when any job routed to a self-hosted runner publishes with `--provenance` — npm rejects that with `422` at the registry, which in v3.8.50 only surfaced after the tag and Docker images were public. ([#11895](https://github.com/diegosouzapw/OmniRoute/pull/11895)) +- `scripts/ops/runner-janitor.sh` now proves a path is idle with one `lsof` snapshot and removes stale leftovers itself (tmpfs after 3 h — it is RAM — disk after 24 h), kills orphan `next-build` processes, prunes checkouts of stopped runners, and alerts on memory pressure; `--dry-run` shows exactly what it would do. `docs/ops/RUNNER_BOX.md` reconciled to the measured box (31 GB, 10 listeners). ([#11893](https://github.com/diegosouzapw/OmniRoute/pull/11893)) +- The `next-build` artefact (1.3 GB) is now written and read under `$RUNNER_TEMP` (per-runner, on disk) instead of `/tmp`, which on the self-hosted pool is a 12 GB tmpfs in RAM. Landing it there took 27–32 of the publish job's 76 minutes, and the fixed `/tmp/e2e-build.tar.gz` name let E2E jobs on different runners overwrite each other's download. ([#11896](https://github.com/diegosouzapw/OmniRoute/pull/11896)) +- The CI `build` job now runs in two concurrency lanes — `main` and pull requests — so a release build is never queued behind (or OOM-killed beside) PR builds on the self-hosted pool, which holds one `next-build` comfortably and two at the edge. ([#11901](https://github.com/diegosouzapw/OmniRoute/pull/11901)) +- Re-freeze the ESLint suppressions on `release/v3.8.51` from a clean-room measurement (2 stale file entries pruned, 55 pre-existing `no-explicit-any` in six new files frozen under #11924) and drop the dead `GPT_SIZE_MAP` constant orphaned by the Adobe Firefly client split, so `No new ESLint warnings` stops failing every PR with exit 2 (Refs #11924) ([#11955](https://github.com/diegosouzapw/OmniRoute/pull/11955)) +- Type the 55 `no-explicit-any` sites that had been frozen under #11924 — four redundant casts in `socksConnectorWithFamily.ts` (undici/socks types already accept them) and the mocks/fixtures of the socks-timeout and isFree suites — and drop their suppression entries; the ESLint ratchet shrinks from 5487 to 5432 (Closes #11924) ([#11975](https://github.com/diegosouzapw/OmniRoute/pull/11975)) +- Take the two hosted-runner builds off the PR rail: `Build App` (`build.yml`) is `workflow_dispatch`-only and `DAST smoke (PR)` runs only for PRs into `main` — the 7 GB hosted VM cannot build `release/v3.8.51` in any profile (19/30 red, VM shutdown ~8 min into `next build`) and both checks had turned into permanent noise on every release PR; the bundle stays validated by `ci.yml` on `main` and by `nightly-release-green` on `release/**` (Closes #11946) ([#11962](https://github.com/diegosouzapw/OmniRoute/pull/11962)) +- Move the four nightly jobs that build the backend (`nightly-schemathesis`, `nightly-llm-security` promptfoo + garak, `nightly-resilience` axe-a11y) off the hosted 7 GB runner — where they died on `release/v3.8.51` unseen — onto the box's new `omni-light` pool (two listeners), and document the reshaped fleet (4 active OmniRoute listeners: 2 `omni-build` + 2 `omni-light`, janitor ceiling 4) (Closes #11965) ([#11967](https://github.com/diegosouzapw/OmniRoute/pull/11967)) +- Stop painting every fork PR into `release/**` red: `quality.yml` `Build (advisory)` is skipped (GitHub still reports `continue-on-error` failures as check FAILURE). Hosted `ubuntu-latest` cannot finish `npm run build` on this tree — same class as #11962 taking `build.yml` off the PR rail. `docker-publish.yml` amd64 now runs on the `.113` `omni-build` pool (31 GB / 32 cores, two listeners) with Turbopack, shares the `heavy-build-main` lane with `ci.yml` `Build` so it queues instead of becoming a third heavy, and keeps per-ref concurrency (a merge storm was starting 8 concurrent OOM builds). arm64 stays on `ubuntu-24.04-arm` with webpack — there is no ARM box. Fallback when `USE_VPS_RUNNER` is off: hosted amd64 + webpack (#11976). ([#12021](https://github.com/diegosouzapw/OmniRoute/pull/12021)) +- `docker-publish.yml` builds with webpack (`OMNIROUTE_USE_TURBOPACK=0`) on **both** arches, including amd64 on the `.113` `omni-build` pool. Turbopack had the RAM (31 GB, `omniroute-113-6`) and still panicked (`TurbopackInternalError: there must be a path to a root`, run 33253576569, 26 min in). The same tree's arm64 webpack build on hosted `ubuntu-24.04-arm` succeeded (run 33264823398). Dockerfile already documents webpack as the Docker escape hatch (#11976). ([#12050](https://github.com/diegosouzapw/OmniRoute/pull/12050)) +- **docs(free-tier):** declare the counting vs deciding regimes for "is it free?" and guard the deciding path from DB-backed catalog resolution ([#12226](https://github.com/diegosouzapw/OmniRoute/pull/12226)) — thanks @maxmad64bis +- **docs(auto-combo):** The mode pack table in `docs/routing/AUTO-COMBO.md` now lists all six shipped packs with every weight each one sets, replacing a four-pack table whose numbers had also drifted from the source. It states plainly that no pack sets `quality`, so selecting any pack silences the observed-quality signal. Six more documents that quote the scoring factor count joined the `check:docs-counts` gate, which caught five stale claims — including one naming nine factors that do not exist — and two stale mode pack counts. The dashboard routing panel, which offered four of the six packs and labelled the default strategy "6-Factor Scoring", is now covered by a test; the two packs it was missing are `reliability-first` and `chaos-mode`, the latter labelled as the fault-injection profile it is rather than as one more routing preference ([#12316](https://github.com/diegosouzapw/OmniRoute/pull/12316)) — thanks @maxmad64bis +- **chore(quality):** rebaseline `src/lib/db/apiKeys.ts` for the ACL the key-creation path now preserves ([#12352](https://github.com/diegosouzapw/OmniRoute/pull/12352)) ([#12673](https://github.com/diegosouzapw/OmniRoute/pull/12673)) — thanks @KrzysiekSko +- **chore(providers):** bump the Claude Code wire identity and the Devin bridge image pin from `2.1.220` to `2.1.258` ([#12402](https://github.com/diegosouzapw/OmniRoute/pull/12402)) — thanks @ggiak ([#12604](https://github.com/diegosouzapw/OmniRoute/pull/12604)) +- **docs(env):** align `.env.example`, the README Bun section, and the troubleshooting guide with the code: `OMNIROUTE_USE_TURBOPACK` also governs `npm run build` (not dev-only), `bun run build` follows that flag instead of auto-selecting Webpack, `OMNIROUTE_CHAT_MAX_HEAVY_IN_FLIGHT` is unset by default (no request-count cap), and the structural `503 chat_admission_busy` message matches `chatAdmissionResponses.ts` (#12404 — thanks @pacocartones) +- **chore(electron):** upgrade the desktop app to Electron 44 (Chromium 152, Node 24.18.1) ([#12217](https://github.com/diegosouzapw/OmniRoute/pull/12217)). **Requires macOS 13 (Ventura) or later** — Chromium dropped macOS 12 (Monterey), so Monterey users must stay on an earlier OmniRoute desktop build. Windows and Linux are unaffected; the app already shipped only x64/arm64, so Electron 44 dropping 32-bit builds changes nothing. Removes the `openAsHidden`/`wasOpenedAsHidden` login-item fields deleted in Electron 44 — hidden autostart continues to work through the `--hidden` argument registered with the login item ([#12554](https://github.com/diegosouzapw/OmniRoute/pull/12554)) — thanks @app/dependabot +- **chore(quality):** rebaseline `src/sse/handlers/chat.ts` for the effective-input persistence the continuation fix needs ([#12641](https://github.com/diegosouzapw/OmniRoute/pull/12641)) ([#12680](https://github.com/diegosouzapw/OmniRoute/pull/12680)) — thanks @hartmark +- `Coverage` job on `ci.yml`: the informational Codecov upload gets its own 5-minute ceiling and `continue-on-error`, and the job budget grows from 20 to 30 minutes (the 8-shard c8 merge alone takes ~10) — a stalled upload no longer ends the job `cancelled` and drags a fully green `main` run's conclusion down with it ([#11972](https://github.com/diegosouzapw/OmniRoute/pull/11972)) +- CI hardening on the PR rail: the four `Unit Tests fast-path` shards get `timeout-minutes: 30` (a hung shard held a PR for 64 min instead of GitHub's 6 h default) and both ESLint file caches lose their `restore-keys` fallback, so a cache built under another suppressions file or lint config can no longer report stale verdicts (Refs #11600, #11924) ([#11963](https://github.com/diegosouzapw/OmniRoute/pull/11963)) +- Every CI job that runs a `next build` (`build`, the npm `publish`, both release-green validations) now targets the `omni-build` runner label, which only two of the eight self-hosted runners carry. The box holds one build comfortably and two at the edge; a third now queues on GitHub instead of being OOM-killed by the kernel. ([#11932](https://github.com/diegosouzapw/OmniRoute/pull/11932)) +- **refactor(combo):** move `handleRoundRobinCombo` (and `resolveTargetTokenLimit`) into `open-sse/services/combo/roundRobinCombo.ts`. `combo.ts` drops from 2164 to 1014 lines (`split("\n").length`); the leaf is 1199 (under the 1200 new-file cap). The round-robin call site uses a dynamic `import()` so `releaseStickyPinOnFailure` / `clearStaleLKGP` can stay exported from `combo.ts` without a static cycle. Skip / sticky / semaphore / safety-timer behavior is unchanged. ([#12811](https://github.com/diegosouzapw/OmniRoute/pull/12811)) — thanks @HouMinXi +- **chore(quality):** rebaseline the file-size caps the error-boundary campaign grew past (`open-sse/executors/codex.ts`, `open-sse/vendor/codex-chatgpt-web/bridge.ts`, both via [#12444](https://github.com/diegosouzapw/OmniRoute/pull/12444)) ([#12654](https://github.com/diegosouzapw/OmniRoute/pull/12654)) +- **chore(quality):** rebaseline the file-size caps the hartmark batch grew past (`combos/page.tsx` via [#12355](https://github.com/diegosouzapw/OmniRoute/pull/12355), `open-sse/services/combo.ts` via [#12338](https://github.com/diegosouzapw/OmniRoute/pull/12338)) ([#12623](https://github.com/diegosouzapw/OmniRoute/pull/12623)) — thanks @hartmark +- **chore(quality):** rebaseline the file-size caps the HouMinXi batch grew past when its PRs stacked (`providers/page.tsx`, `chatCore.ts`, `accountFallback.ts`) — each PR measured correctly in isolation, none saw the stacking ([#12619](https://github.com/diegosouzapw/OmniRoute/pull/12619)) +- **chore(quality):** rebaseline `open-sse/services/combo.ts` for the reset-aware scoring the HouMinXi batch stacked ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) ([#12678](https://github.com/diegosouzapw/OmniRoute/pull/12678)) — thanks @HouMinXi +- **docs:** sync provider catalog count (direct commit `4f11b2ae3d`) +- **docs(skills):** sync omni-inference search fallback (direct commit `b4e76a7ed9`) +- **chore(lint):** prune two stale base suppressions (direct commit `1325047d56`) +- **test(auto-combo):** tolerate full-suite rotation load (direct commit `ad3e293f9f`) +- **test:** drain inherited quality snapshot reds (direct commit `21ed68d8ac`) +- **chore(quality):** rebaseline measured CLI bundle growth (direct commit `d35b3f9779`) +- **test(ui):** reproduce invalid AnySearch icon alias (direct commit `12cc6ca834`) +- **test(translator):** fix the relative imports of the relocated deferred-finish test (direct commit `777d9d1629`) +- **docs(counts):** bring the documented provider and migration counts to the measured values (direct commit `e70bea30e9`) +- **test(security):** drop the guard-target set left unused by the sync (direct commit `a32ba0e362`) +- **chore(db):** renumber model_capabilities migration 163 → 169 (cross-branch collision with 163_radar_feed_cache_generated_at) (direct commit `70f5d4cbf9`) +- **docs(cli):** document CLI_PRIME_AGENT_BIN and correct the CLI Agents count (direct commit `9058e39b61`) +- **chore(deps):** refresh runtimes and adopt ESLint 10 ([#11259](https://github.com/diegosouzapw/OmniRoute/pull/11259)) — thanks @backryun +- **refactor(local-corpus):** implement dynamic root resolution and LRU cache ([#11491](https://github.com/diegosouzapw/OmniRoute/pull/11491)) — thanks @raheemuddin786 +- **chore(quality):** prune stale eslint-suppressions entries ([#11596](https://github.com/diegosouzapw/OmniRoute/pull/11596)) +- **chore(ci):** unblock quality gates - env-doc sync + stryker tap.testFiles registration (#9282) ([#11609](https://github.com/diegosouzapw/OmniRoute/pull/11609)) — thanks @Meet6338-X +- **chore(ci):** point the circular-deps gate at dpdm's real JS entrypoint ([#11615](https://github.com/diegosouzapw/OmniRoute/pull/11615)) — thanks @benzntech +- **chore(quality):** rebaseline file-size for #11448/#11495/#11561 growth ([#11630](https://github.com/diegosouzapw/OmniRoute/pull/11630)) +- **deps:** realign onnxruntime-node pin to @huggingface/transformers' 1.24.3 lockstep ([#11633](https://github.com/diegosouzapw/OmniRoute/pull/11633)) — thanks @jonlwheat2-gif +- **test(autoCombo):** port models_dev_tier #11508 guard from node:test to vitest ([#11635](https://github.com/diegosouzapw/OmniRoute/pull/11635)) — thanks @jonlwheat2-gif +- **chore(quality):** rebaseline file-size for #11083/#11631/#11628 growth ([#11636](https://github.com/diegosouzapw/OmniRoute/pull/11636)) +- **chore(lint):** prune stale ESLint suppressions (base-red #11449) ([#11644](https://github.com/diegosouzapw/OmniRoute/pull/11644)) — thanks @jonlwheat2-gif +- **docs(changelog):** aggregate the ten v3.8.50 fragments into CHANGELOG.md ([#11683](https://github.com/diegosouzapw/OmniRoute/pull/11683)) +- **chore(search):** drop unreachable credential sweep after #11642 ([#11687](https://github.com/diegosouzapw/OmniRoute/pull/11687)) — thanks @HouMinXi +- **ci:** add API route TypeScript regression gate ([#11705](https://github.com/diegosouzapw/OmniRoute/pull/11705)) — thanks @Sabeekhann +- **docs(dedup):** correct the CodeQL claim on the tenant-namespace comment ([#11712](https://github.com/diegosouzapw/OmniRoute/pull/11712)) +- **docs(changelog):** add consolidated v3.8.50 stats, top-25 ranking and a .mailmap ([#11715](https://github.com/diegosouzapw/OmniRoute/pull/11715)) +- **docs:** recommend gstack for AI-assisted workflows ([#11770](https://github.com/diegosouzapw/OmniRoute/pull/11770)) — thanks @santosraju99-hub +- **chore(quality):** tighten complexity/cognitive-complexity ratchets to the current tip; land the missed gateways.ts rebaseline ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **docs(i18n):** finish Freepik → Magnific rebrand in locale strings and README ([#11772](https://github.com/diegosouzapw/OmniRoute/pull/11772)) — thanks @turbolego +- **refactor(auto-combo):** fix divergent scoring in combo health reporting ([#11854](https://github.com/diegosouzapw/OmniRoute/pull/11854)) — thanks @jacobsparts +- **chore(changelog):** add missing fragments for #11919/#11918/#11916 ([#11938](https://github.com/diegosouzapw/OmniRoute/pull/11938)) +- **test(check):** escape the runs-on fixture with JSON.stringify, not a quote-only replace ([#11942](https://github.com/diegosouzapw/OmniRoute/pull/11942)) (direct commit `fb7445eaa3`) +- **test(release):** align five suites with the contracts #11933, #11919 and #11876 shipped on release/v3.8.51 ([#11944](https://github.com/diegosouzapw/OmniRoute/pull/11944)) +- **chore(quality):** remove unreachable code and restore test discovery ([#11950](https://github.com/diegosouzapw/OmniRoute/pull/11950)) — thanks @backryun +- **docs(agents):** sync-back landings are fast-forward, never squash ([#11964](https://github.com/diegosouzapw/OmniRoute/pull/11964)) +- **test(infra):** retry recursive temp-dir removal instead of failing a shard on ENOTEMPTY (#11966) ([#11968](https://github.com/diegosouzapw/OmniRoute/pull/11968)) +- **docs(ops):** the .113 heavy-build ceiling is one runner, not two ([#12048](https://github.com/diegosouzapw/OmniRoute/pull/12048)) +- **test(ui):** correct inactive auto-fetch expectation ([#12098](https://github.com/diegosouzapw/OmniRoute/pull/12098)) — thanks @RaviTharuma +- **docs:** document native dependency check escape hatch ([#12101](https://github.com/diegosouzapw/OmniRoute/pull/12101)) — thanks @RaviTharuma +- **test(providers):** regenerate the translate-path golden for OrcaRouter (#11923) ([#12118](https://github.com/diegosouzapw/OmniRoute/pull/12118)) +- **chore(quality):** velocity phase — loosen every numeric baseline by 20% until v4.0, monitor headroom nightly ([#12125](https://github.com/diegosouzapw/OmniRoute/pull/12125)) +- **chore(lint):** batch 0 of #12146 — type the call-log-cap sqlite rows instead of 45 as-any casts ([#12157](https://github.com/diegosouzapw/OmniRoute/pull/12157)) +- **chore(lint):** batch 4 of #12146 — shared/components react-hooks violations resolved ([#12159](https://github.com/diegosouzapw/OmniRoute/pull/12159)) +- **chore(lint):** batch 1 of #12146 — dashboard/cli-code react-hooks violations resolved ([#12160](https://github.com/diegosouzapw/OmniRoute/pull/12160)) +- **chore(lint):** batch 3 of #12146 — dashboard/settings react-hooks violations resolved ([#12162](https://github.com/diegosouzapw/OmniRoute/pull/12162)) +- **chore(lint):** batch 2 of #12146 — resolve the react-hooks compiler violations in dashboard/providers ([#12163](https://github.com/diegosouzapw/OmniRoute/pull/12163)) +- **chore(quality):** register search-432 cooldown test in stryker tap.testFiles ([#12170](https://github.com/diegosouzapw/OmniRoute/pull/12170)) +- **chore(lint):** batch 5 of #12146 — combos, endpoint, provider-stats, api-manager and costs react-hooks violations resolved ([#12174](https://github.com/diegosouzapw/OmniRoute/pull/12174)) +- **build:** add contributor fast profile ([#12192](https://github.com/diegosouzapw/OmniRoute/pull/12192)) — thanks @rafacpti23 +- **docs(readme):** README touch-ups ([#12193](https://github.com/diegosouzapw/OmniRoute/pull/12193)) ([#12194](https://github.com/diegosouzapw/OmniRoute/pull/12194)) ([#12202](https://github.com/diegosouzapw/OmniRoute/pull/12202)) — thanks @rafacpti23 +- **build:** force the webpack bundler in the contributor build profile ([#12197](https://github.com/diegosouzapw/OmniRoute/pull/12197)) — thanks @rafacpti23 +- **build:** skip standalone packaging in the contributor profile ([#12198](https://github.com/diegosouzapw/OmniRoute/pull/12198)) — thanks @rafacpti23 +- **docs(audit):** align every published number with the code and harden check:docs-counts ([#12200](https://github.com/diegosouzapw/OmniRoute/pull/12200)) +- **build:** stub the instrumentation entrypoints in the contributor profile ([#12203](https://github.com/diegosouzapw/OmniRoute/pull/12203)) — thanks @rafacpti23 +- **build:** omit the standalone output target for contributor builds ([#12204](https://github.com/diegosouzapw/OmniRoute/pull/12204)) — thanks @rafacpti23 +- **chore(lint):** batch 6 of #12146 — memory, radar, audit, analytics, cache, usage, activity, home and RequestLoggerV2 react-hooks violations resolved ([#12208](https://github.com/diegosouzapw/OmniRoute/pull/12208)) +- **docs(diagrams):** rename number-carrying diagram files to stable names ([#12210](https://github.com/diegosouzapw/OmniRoute/pull/12210)) +- **docs(dashboard):** redraw onboarding tier-flow SVGs for the real 4-tier model ([#12211](https://github.com/diegosouzapw/OmniRoute/pull/12211)) +- **docs(api):** document every implemented route in openapi.yaml (276 → 692 paths) ([#12212](https://github.com/diegosouzapw/OmniRoute/pull/12212)) +- **chore(lint):** batch 7 of #12146 — final src tail: 37 react-hooks violations across 33 files resolved ([#12244](https://github.com/diegosouzapw/OmniRoute/pull/12244)) +- Revert "docs: recommend gstack for AI-assisted workflows (#11770)" ([#12248](https://github.com/diegosouzapw/OmniRoute/pull/12248)) ([#12249](https://github.com/diegosouzapw/OmniRoute/pull/12249)) +- **docs:** Chaos Mode setup guide + weighted strategy semantics ([#12250](https://github.com/diegosouzapw/OmniRoute/pull/12250)) +- **docs(agents):** protected-surface merge rule — operator approval for agent-instruction files ([#12253](https://github.com/diegosouzapw/OmniRoute/pull/12253)) +- **chore(quality):** register native-codex turn-pin tests in stryker tap.testFiles ([#12263](https://github.com/diegosouzapw/OmniRoute/pull/12263)) +- **chore(quality):** dedupe tap.testFiles entries added by racing base-red fixes ([#12265](https://github.com/diegosouzapw/OmniRoute/pull/12265)) +- **docs(readme):** introduce OmniRouteTray — the macOS menu-bar companion ([#12276](https://github.com/diegosouzapw/OmniRoute/pull/12276)) — thanks @ggiak +- **chore(proxy):** purge the legacy 1proxy residue — sync/rotator modules, dead DB exports, dead settings tab and flag, docs (#12091) ([#12290](https://github.com/diegosouzapw/OmniRoute/pull/12290)) +- **chore(quality):** baseline-headroom skips generated and vendored files in the fileSize worst-file signal ([#12291](https://github.com/diegosouzapw/OmniRoute/pull/12291)) +- **refactor(video):** unify the JPEG frame data-URI contract ([#12322](https://github.com/diegosouzapw/OmniRoute/pull/12322)) +- **chore(deps):** freeze onnxruntime-node and eslint-plugin-react-hooks out of dependabot groups ([#12329](https://github.com/diegosouzapw/OmniRoute/pull/12329)) +- **test(executors):** restore the #10986 reasoning-only fallback guards ([#12364](https://github.com/diegosouzapw/OmniRoute/pull/12364)) — thanks @pacocartones +- **chore(quality):** re-tighten the file-size ratchet to the real LOC (plan 3.8.52 task 0) ([#12411](https://github.com/diegosouzapw/OmniRoute/pull/12411)) +- **test(grok):** format web executor suite ([#12412](https://github.com/diegosouzapw/OmniRoute/pull/12412)) +- **chore(quality):** ratchet open-sse typecheck baseline to zero ([#12418](https://github.com/diegosouzapw/OmniRoute/pull/12418)) +- **test(build):** guard the artifact path policy arrays against duplicates ([#12422](https://github.com/diegosouzapw/OmniRoute/pull/12422)) +- **chore(lint):** adopt eslint-plugin-react-hooks 7.1.1 ([#12428](https://github.com/diegosouzapw/OmniRoute/pull/12428)) +- **chore(providers):** remove a keyless provider integration at its operator's request ([#12440](https://github.com/diegosouzapw/OmniRoute/pull/12440)) +- **docs(providers):** register providers removed at their operator's request and guard against reintroduction ([#12478](https://github.com/diegosouzapw/OmniRoute/pull/12478)) +- **chore(quality):** base fixes — stryker tap.testFiles + node_modules cache key ([#12482](https://github.com/diegosouzapw/OmniRoute/pull/12482)) +- **docs(arch):** one-process recipe for tens of long /v1/responses ([#12493](https://github.com/diegosouzapw/OmniRoute/pull/12493)) — thanks @RaviTharuma +- **chore(quality):** tighten the CodeQL ratchet baseline from 11 to 6 ([#12530](https://github.com/diegosouzapw/OmniRoute/pull/12530)) +- **chore(deps):** pin browserslist override to ^4.28.8 ([#12592](https://github.com/diegosouzapw/OmniRoute/pull/12592)) — thanks @KooshaPari +- **chore(deps):** bump fast-uri to 3.1.7 in the electron lockfile ([#12601](https://github.com/diegosouzapw/OmniRoute/pull/12601)) +- **docs(quality):** document how the CodeQL ratchet refreshes and how to tighten it ([#12611](https://github.com/diegosouzapw/OmniRoute/pull/12611)) +- **test(ci):** pin the openapi-security-tiers two-arm contract with an executing gate test (#12581) ([#12652](https://github.com/diegosouzapw/OmniRoute/pull/12652)) +- **docs(gamification):** align XP Rewards table with code (#12501) ([#12667](https://github.com/diegosouzapw/OmniRoute/pull/12667)) — thanks @KooshaPari +- **refactor(combo):** split executeTarget into gates, attempt, and loop ([#12746](https://github.com/diegosouzapw/OmniRoute/pull/12746)) — thanks @HouMinXi +- **docs(security):** document socket.yml scanner config + CI workflow link (#12575) ([#12764](https://github.com/diegosouzapw/OmniRoute/pull/12764)) — thanks @KooshaPari +- **chore(ci):** guard commit identity in pre-commit to stop author misattribution ([#12772](https://github.com/diegosouzapw/OmniRoute/pull/12772)) +- **deps:** 17 Dependabot bumps — bump electron from 43.4.0 to 43.4.1 in /electron ([#11426](https://github.com/diegosouzapw/OmniRoute/pull/11426)); bump the development group across 1 directory with 8 updates ([#11428](https://github.com/diegosouzapw/OmniRoute/pull/11428)); bump the production group across 1 directory with 11 updates ([#11440](https://github.com/diegosouzapw/OmniRoute/pull/11440)); chore(deps): bump github/codeql-action/init from 4.37.7 to 4.37.8 ([#11925](https://github.com/diegosouzapw/OmniRoute/pull/11925)); chore(deps): bump github/codeql-action from 4.37.7 to 4.37.8 ([#11926](https://github.com/diegosouzapw/OmniRoute/pull/11926)); chore(deps): bump github/codeql-action/analyze from 4.37.7 to 4.37.8 ([#11927](https://github.com/diegosouzapw/OmniRoute/pull/11927)); build(deps): bump github/codeql-action/init from 4.37.8 to 4.37.9 ([#12345](https://github.com/diegosouzapw/OmniRoute/pull/12345)); build(deps): bump github/codeql-action/analyze from 4.37.8 to 4.37.9 ([#12346](https://github.com/diegosouzapw/OmniRoute/pull/12346)); bump the development group across 1 directory with 2 updates ([#12347](https://github.com/diegosouzapw/OmniRoute/pull/12347)); build(deps): bump github/codeql-action from 4.37.8 to 4.37.9 ([#12349](https://github.com/diegosouzapw/OmniRoute/pull/12349)); bump browserslist from 4.28.2 to 4.28.8 ([#12396](https://github.com/diegosouzapw/OmniRoute/pull/12396)); bump the production group across 1 directory with 4 updates ([#12399](https://github.com/diegosouzapw/OmniRoute/pull/12399)); bump @xmldom/xmldom ([#12500](https://github.com/diegosouzapw/OmniRoute/pull/12500)); bump qs from 6.15.2 to 6.16.0 ([#12512](https://github.com/diegosouzapw/OmniRoute/pull/12512)); bump @xmldom/xmldom from 0.9.10 to 0.9.12 ([#12513](https://github.com/diegosouzapw/OmniRoute/pull/12513)); bump fast-uri from 3.1.5 to 3.1.7 ([#12514](https://github.com/diegosouzapw/OmniRoute/pull/12514)); bump @humanfs/node from 0.16.7 to 0.16.8 ([#12515](https://github.com/diegosouzapw/OmniRoute/pull/12515)) +- Clear the `release/v3.8.51` `check:agent-skills-sync` base-red: regenerate `skills/cli-tunnel/SKILL.md` so the `tunnel create [type]` positional that #13009 taught the generator to read is reflected in the committed skill. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` typecheck base-red from #12731: the new `Mode pack ranking gates` candidates in `open-sse/services/autoCombo/__tests__/autoCombo.test.ts` omitted the required `provider`, `model` and `errorRate` fields of `ProviderCandidate`, failing `check:open-sse-typecheck` (and with it `Fast Quality Gates`) on every open PR. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` docs-sync base-red: EURouter (#13025) and GreenPT (#13024) took the live provider count to 358, leaving 7 STRICT drifts (`PROVIDER_REFERENCE.md`, 4 diagrams, 2 tier-flow images) plus the `AGENTS.md` / `llm.txt` / `package.json` count claims stale. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- **test(models):** Pin `CATALOG_BUILD_TIMEOUT_MS` in the #12058 canonical-catalog regression so a cold catalog build racing the 8s default bound no longer flakes the suite with a 500 ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Allow `@omniroute/opencode-plugin-v2/` in the npm pack policy — [#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870) shipped the v2 plugin beside its v1 sibling without widening the allowlist, so every packed file under it read as an unexpected artifact and failed `check:pack-policy` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Point the release-green validator's pack gate at the tree it is validating — the build-provenance guard defaulted to `origin/main`, which a release branch tip can never be an ancestor of mid-cycle, so `Package artifact` reported a false red on every run and the tarball boot-smoke cascaded off it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **docs(checks):** keep doc counts honest — headings, rankings, catalog, weights, quality gate and scoring diagram now covered ([#12507](https://github.com/diegosouzapw/OmniRoute/pull/12507)) — thanks @maxmad64bis +- **chore(lifecycle):** `check:model-lifecycle` now also diffs `DEFAULT_DEGRADATION_MAP` (the background-task redirect table) against the vendor lifecycle snapshot, refusing a retired id as source or target, with a table-driven unit test beside it. Three rows whose source the vendor had retired — `claude-sonnet-4-20250514`, `gemini-3-pro-preview` and `gpt-5.1-codex` (whose target `gpt-5.1-codex-mini` is retired too) — were dead code, since `checkLifecycle` answers 410 before the redirect runs; they are dropped (#12535 — thanks @pacocartones) +- **test(compression):** cover idle worker eviction at the resource level — the pool must call `terminate()` and must not retain the worker's `MessagePort`, complementing the `exit`-event assertion added with the fix ([#13371](https://github.com/diegosouzapw/OmniRoute/pull/13371)) +- **chore(quality):** raise the `imageGeneration.ts` file-size ceiling for the image-only-model guard that clears the #12945 base-red ([#13392](https://github.com/diegosouzapw/OmniRoute/pull/13392)) +- **chore(quality):** type the combo-test route's JSON response bodies instead of casting them to `any`, drop the now-empty suppression entry, and remove the `hasPerModelQuota` import `chatCore.ts` stopped using when the failure-classification helper was extracted ([#13379](https://github.com/diegosouzapw/OmniRoute/pull/13379)) +- **chore(electron):** the desktop bundle no longer ships the root-level files of every translated docs mirror (`README.md`, `llm.txt`, `CLAUDE.md`, `GEMINI.md`, `CONTRIBUTING.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`) — the packaged app only reads `docs/i18n//docs/**`, which stays. Saves ~11 MB on top of the translated CHANGELOGs already pruned. (#0000) ([#13235](https://github.com/diegosouzapw/OmniRoute/pull/13235)) +- **chore(quality):** retighten the `src/sse/handlers/chat.ts` unused-vars suppression count to the 8 violations that actually remain ([#13385](https://github.com/diegosouzapw/OmniRoute/pull/13385)) +- Bring the documented counts back in line with the code: 171 migrations (was 169) and 20 routing strategies (was 19), plus the regenerated `cli-tunnel` skill reference. ([#12970](https://github.com/diegosouzapw/OmniRoute/pull/12970)) +- Update the documented migration count to 172 after the call-logs provider-stats indexes landed. ([#13209](https://github.com/diegosouzapw/OmniRoute/pull/13209)) +- **chore(ci):** two gates that close the blind spots behind the exclusions above. `check:vitest-exclusions` requires every Vitest exclusion to name a tracking issue and to appear in `config/quality/vitest-exclusions.json` — the previous list grew to 62 files behind a comment pointing at an issue that had been closed for a month. `check-new-key-coverage` requires a key newly added to `en.json` to reach every locale; the existing coverage gate is a percentage floor per locale, so eleven absent keys out of ~13,000 left it at 99.9% while a whole feature shipped untranslated in nine languages. Both are diff-aware, so pre-existing debt stays frozen and neither needed a migration to turn on. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **build:** root layout no longer loads Inter from `next/font/google`, so a production image build does not need fonts.googleapis.com ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **docs(i18n):** bring the eight new `llm.txt` mirrors (km, kn, ml, my, ne, or, pa, si) up to the current provider and migration counts, so `check-docs-sync` stops rejecting every commit ([#13674](https://github.com/diegosouzapw/OmniRoute/pull/13674)) +- **refactor(ui):** flow surfaces (home topology, combo live studio, compression cockpit/waterfall/nodes, token-health badge) express state through the theme-aware `--orch-status-*` tokens instead of fixed dark-mode hex, so green/red/amber/grey stay legible in the light theme; dark mode is byte-identical. Categorical palettes (routing-strategy hues, compression-layer pills, provider brand colors) deliberately stay hex ([#12378](https://github.com/diegosouzapw/OmniRoute/issues/12378)) ([#13203](https://github.com/diegosouzapw/OmniRoute/pull/13203)) +- **chore(tests):** 51 test files that had been excluded from Vitest are running again, restoring roughly 350 assertions to the blocking suite. Each was measured individually first: of the 62 files parked behind the `// #8618 — pre-existing failure` comment, 51 pass against the current tree with no source change, so the exclusions had outlived the failures they were added for. The 11 that genuinely still fail stay excluded, but now point at a live tracker (#13204) rather than at #8618, which was closed in August while the list it tracked kept growing. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **chore(quality):** drop two ESLint suppression entries whose violations no longer exist, so `eslint --suppressions-location` stops rejecting every commit that touches the surrounding files ([#13384](https://github.com/diegosouzapw/OmniRoute/pull/13384)) +- **chore(release):** bring main's post-tag fixes into release/v3.8.51 — sync-back step 2/2 (direct commit `21c488f210`) +- **chore(deps):** patch toml and esbuild in the opencode plugin lockfiles ([#12965](https://github.com/diegosouzapw/OmniRoute/pull/12965)) +- **docs(changelog):** reconcile the v3.8.51 living section — fold 366 fragments, cover every cycle commit, credit every contributor ([#12971](https://github.com/diegosouzapw/OmniRoute/pull/12971)) +- **test(plugin):** wait for the v2 lazy-refresh tier instead of sleeping past it ([#12984](https://github.com/diegosouzapw/OmniRoute/pull/12984)) +- **chore(quality):** rebaseline chatCore.ts after the non-streaming regression fixes ([#13045](https://github.com/diegosouzapw/OmniRoute/pull/13045)) +- **docs(changelog):** add the missing bullet marker to the DeepSeek PoW fragment ([#13200](https://github.com/diegosouzapw/OmniRoute/pull/13200)) +- **chore(deps):** drain the Dependabot queue — 10 of 13 alerts ([#13213](https://github.com/diegosouzapw/OmniRoute/pull/13213)) +- **chore(ci):** make worktrees carry the hooks that enforce the identity gate ([#13236](https://github.com/diegosouzapw/OmniRoute/pull/13236)) +- **chore(deps):** pin csv-parse to ^7.0.2 via overrides (#13117) ([#13300](https://github.com/diegosouzapw/OmniRoute/pull/13300)) — thanks @KooshaPari +- **chore(deps):** bump hono from ^4.12.34 to ^4.13.7 (#13148) ([#13301](https://github.com/diegosouzapw/OmniRoute/pull/13301)) — thanks @KooshaPari +- **chore(deps):** pin joi to ^18.2.8 via overrides (#13085) ([#13302](https://github.com/diegosouzapw/OmniRoute/pull/13302)) — thanks @KooshaPari +- **docs(providers):** add Microsoft 365 Copilot (BizChat) provider guide (#12779) ([#13340](https://github.com/diegosouzapw/OmniRoute/pull/13340)) — thanks @KooshaPari +### 🙌 Contributors + +Thanks to everyone whose work landed in v3.8.51: + +| Contributor | PRs / Issues | +| --- | --- | +| [@5dive-bot](https://github.com/5dive-bot) | #11852 | +| [@abhisheksharma2411](https://github.com/abhisheksharma2411) | #11547, #11684, #11959, #12209 | +| [@adevwithpurpose](https://github.com/adevwithpurpose) | #11464 | +| [@adivekar-utexas](https://github.com/adivekar-utexas) | #12015, #12043, #12090, #12138 | +| [@AIB1TAL0S](https://github.com/AIB1TAL0S) | #12103 | +| [@alltomatos](https://github.com/alltomatos) | #11448 | +| [@alvinveroy](https://github.com/alvinveroy) | #11857, #12027, #12165 | +| [@amaleta](https://github.com/amaleta) | #11906, #11934 | +| [@amartinawi](https://github.com/amartinawi) | #11575 | +| [@AndrianBalanescu](https://github.com/AndrianBalanescu) | #11083, #11830, #11834, #11844 | +| [@anhtahaylove](https://github.com/anhtahaylove) | #13091, #13092, #13093, #13096, #13097, #13100, #13106, #13109, #13114, #13155, #13166, #13171, #13175, #13187, #13196 | +| [@aniruddhaadak80](https://github.com/aniruddhaadak80) | #11760 | +| [@app](https://github.com/app) | #12554 | +| [@arminanton](https://github.com/arminanton) | #11461, #11513 | +| [@AStupidBear](https://github.com/AStupidBear) | #11584 | +| [@atescivitci-cmd](https://github.com/atescivitci-cmd) | #12105 | +| [@b3nw](https://github.com/b3nw) | #11970, #11971, #12147 | +| [@backryun](https://github.com/backryun) | #11259, #11950, #12075, #12076, #12078, #12079, #12081, #12082, #12181, #12228, #12239, #12255, #12258, #12277, #12367, #12423, #12524, #12538 | +| [@benjaminkitt](https://github.com/benjaminkitt) | #11747 | +| [@benzntech](https://github.com/benzntech) | #11614, #11615 | +| [@Bl0ck154](https://github.com/Bl0ck154) | #11948, #11951, #11952, #11953, #11954 | +| [@brick30llc-ctrl](https://github.com/brick30llc-ctrl) | #11752, #12242, #12262 | +| [@Chewji9875](https://github.com/Chewji9875) | #11455, #11875, #12028 | +| [@cheynetom](https://github.com/cheynetom) | #12058 | +| [@davidebaraldo](https://github.com/davidebaraldo) | #12222, #12304 | +| [@davidlinfr](https://github.com/davidlinfr) | #6390 | +| [@ddarkr](https://github.com/ddarkr) | #12124 | +| [@Deftera186](https://github.com/Deftera186) | #11809 | +| [@dpozimski](https://github.com/dpozimski) | #11945 | +| [@DW-MediaLab](https://github.com/DW-MediaLab) | #12229 | +| [@echel0nn](https://github.com/echel0nn) | #11923 | +| [@f9td56dbgh-hub](https://github.com/f9td56dbgh-hub) | #11660 | +| [@fabioluissilva](https://github.com/fabioluissilva) | #11991 | +| [@foreveryh](https://github.com/foreveryh) | #12177 | +| [@geek007git](https://github.com/geek007git) | #12115, #12116, #12117, #12120, #12122 | +| [@geekyNads](https://github.com/geekyNads) | #11784, #12266 | +| [@ggiak](https://github.com/ggiak) | #11561, #12276, #12350, #12402 | +| [@gonisulaimann](https://github.com/gonisulaimann) | #12368, #12369, #12371 | +| [@Gorillaz322](https://github.com/Gorillaz322) | #12207 | +| [@groovecityJO](https://github.com/groovecityJO) | #12682 | +| [@hartmark](https://github.com/hartmark) | #11434, #11452, #11473, #11499, #11703, #11983, #11984, #11985, #11986, #11988, #11989, #11990, #11994, #12221, #12293, #12445, #12446, #12447, #12448, #12460, #12461, #12623, #12646, #12650, #12680, #12717, #12718, #12727, #12741, #12854 | +| [@hizzt](https://github.com/hizzt) | #11894 | +| [@hongnoul](https://github.com/hongnoul) | #11484 | +| [@HouMinXi](https://github.com/HouMinXi) | #11411, #11414, #11512, #11518, #11520, #11641, #11642, #11643, #11687, #11779, #11849, #11850, #11851, #11915, #11916, #11918, #11919, #11920, #12013, #12017, #12033, #12042, #12106, #12139, #12166, #12169, #12171, #12205, #12213, #12312, #12325, #12487, #12488, #12495, #12504, #12557, #12566, #12590, #12591, #12624, #12626, #12632, #12637, #12678, #12696, #12697, #12711, #12733, #12746, #12767, #12770, #12789, #12803, #12805, #12811, #12866, #12868, #12899, #12926, #12934, #12950, #12951, #12974, #13001, #13006, #13011, #13017, #13026, #13027, #13034, #13035, #13038, #13042, #13050, #13060, #13061, #13069, #13107, #13120, #13136, #13195, #13197 | +| [@Hsia97](https://github.com/Hsia97) | #11624 | +| [@jacobsparts](https://github.com/jacobsparts) | #11854, #12155, #12167 | +| [@joglomedia](https://github.com/joglomedia) | #11980 | +| [@jonlwheat2-gif](https://github.com/jonlwheat2-gif) | #11443, #11450, #11549, #11567, #11608, #11633, #11635, #11640, #11644, #11671, #11672, #11673, #11674, #11675, #11676, #11677, #11706, #11783, #12051, #12052, #12053, #12055 | +| [@kanade-hoshino](https://github.com/kanade-hoshino) | #12180 | +| [@Karan825](https://github.com/Karan825) | #11861 | +| [@KaspaPulse](https://github.com/KaspaPulse) | #11389, #11469, #11628, #11666, #11910 | +| [@keeltrace](https://github.com/keeltrace) | #12080, #12223 | +| [@killer30001000](https://github.com/killer30001000) | #12178 | +| [@KooshaPari](https://github.com/KooshaPari) | #12470, #12592, #12667, #12699, #12703, #12706, #12764, #12769, #12771, #13300, #13301, #13302, #13340, #13401, #13403, #13404, #13405, #13406, #13407, #13409, #13410, #13411, #13412, #13413, #13414, #13418, #13424, #13427, #13433, #13523, #13524, #13525, #13528, #13531, #13532, #13534, #13539, #13543, #13545, #13546, #13547, #13550, #13553 | +| [@kriptoburak](https://github.com/kriptoburak) | #11370 | +| [@KrzysiekSko](https://github.com/KrzysiekSko) | #12425, #12432, #12673 | +| [@marcelokarval](https://github.com/marcelokarval) | direct commit / report | +| [@maxmad64bis](https://github.com/maxmad64bis) | #1622, #11435, #11437, #11441, #11537, #11550, #11553, #11555, #11557, #11812, #11842, #11843, #11903, #12151, #12214, #12215, #12218, #12226, #12314, #12316, #12317, #12318, #12319, #12320, #12321, #12507, #12715, #12731, #12744, #12786, #12787, #12788, #12790, #12792, #12794, #12795, #12828, #12832, #12853, #12857, #12870, #12937, #12941, #12975, #13141, #13142, #13146, #13147 | +| [@mdigitalbh81](https://github.com/mdigitalbh81) | #12240 | +| [@Meet6338-X](https://github.com/Meet6338-X) | #11598, #11609, #12110 | +| [@morpheus9393](https://github.com/morpheus9393) | #11943 | +| [@MumuTW](https://github.com/MumuTW) | #11492, #11502, #11506, #11507, #11626, #11675, #11685, #11728, #11746, #11888, #11889, #11890, #11892 | +| [@Neuron-Mr-White](https://github.com/Neuron-Mr-White) | #11622, #11801, #12256 | +| [@NightStalker-87](https://github.com/NightStalker-87) | #12183 | +| [@NoxzRCW](https://github.com/NoxzRCW) | #11879, #11880, #11881, #11882, #11883, #11935 | +| [@ntdat812](https://github.com/ntdat812) | #11585, #12095 | +| [@ntdatt812](https://github.com/ntdatt812) | #11368, #11573, #11574, #11576, #11577, #11580, #11582, #11583, #11588, #11589, #11590, #11591, #11592, #11593, #11672, #12177, #12180, #12873, #12918, #12920, #12921, #12925, #12930, #13007, #13009, #13024, #13025, #13083, #13087, #13101, #13104, #13110 | +| [@official-burak](https://github.com/official-burak) | #11542 | +| [@opensource-elearning](https://github.com/opensource-elearning) | #12179, #12189, #12278, #12286 | +| [@oyi77](https://github.com/oyi77) | #11408, #11409, #11421, #11505, #11677, #12036, #12110 | +| [@pacocartones](https://github.com/pacocartones) | #11521, #11522, #11527, #11528, #11529, #11530, #11531, #11532, #11533, #11534, #11595, #11599, #11603, #11604, #11605, #11607, #11610, #11676, #11714, #11716, #11718, #11767, #11838, #11860, #11862, #11869, #11871, #11872, #11873, #11903, #11906, #11921, #11934, #12356, #12358, #12359, #12360, #12361, #12362, #12364, #12365, #12373, #12374, #12375, #12376, #12377, #12379, #12380, #12381, #12386, #12387, #12389, #12390, #12394, #12395, #12397, #12401, #12403, #12404, #12522, #12523, #12535, #12536, #12540, #12541, #12543, #12545, #12548, #12549, #12550, #12551, #12552, #12647, #12651, #12653, #13055 | +| [@patrykkopycinski](https://github.com/patrykkopycinski) | #11936, #11937, #12224 | +| [@PauloFH](https://github.com/PauloFH) | #11509 | +| [@PauloHSOliveira](https://github.com/PauloHSOliveira) | #12241 | +| [@ponkcore](https://github.com/ponkcore) | #12054 | +| [@Prajeeth-12](https://github.com/Prajeeth-12) | #11634, #12046 | +| [@quiterunner-commits](https://github.com/quiterunner-commits) | #12131, #12143 | +| [@rafacpti23](https://github.com/rafacpti23) | #11554, #11558, #12192, #12197, #12198, #12202, #12203, #12204 | +| [@ragnar-claude](https://github.com/ragnar-claude) | #11564 | +| [@raheemuddin786](https://github.com/raheemuddin786) | #11491, #11839, #11840, #11841, #12003, #12230, #12231, #12232, #12233, #12234 | +| [@rahilmavani](https://github.com/rahilmavani) | #11761 | +| [@Rahulsharma0810](https://github.com/Rahulsharma0810) | #11771 | +| [@RaviTharuma](https://github.com/RaviTharuma) | #11710, #11727, #11797, #11798, #11802, #11805, #11806, #11811, #12098, #12099, #12101, #12449, #12452, #12472, #12473, #12493, #12533, #12607, #12628, #12631, #12636, #12875, #12876, #12880, #12882, #12884 | +| [@rezjalibd](https://github.com/rezjalibd) | #12186 | +| [@RhianB14](https://github.com/RhianB14) | #11692 | +| [@rifqiawl](https://github.com/rifqiawl) | #11517, #11519, #11969 | +| [@rqzbeh](https://github.com/rqzbeh) | #11390 | +| [@Sabeekhann](https://github.com/Sabeekhann) | #11699, #11705 | +| [@santosraju99-hub](https://github.com/santosraju99-hub) | #11755, #11770 | +| [@Seramicx](https://github.com/Seramicx) | #11568 | +| [@Siva010](https://github.com/Siva010) | #12191 | +| [@solstxce](https://github.com/solstxce) | #11597 | +| [@soroush5](https://github.com/soroush5) | #12691 | +| [@tenshiak](https://github.com/tenshiak) | #12279 | +| [@TheDemonTuan](https://github.com/TheDemonTuan) | #11468, #11470, #11471, #11482, #11548, #11758, #11775, #11814 | +| [@thomasmaerz](https://github.com/thomasmaerz) | #12834 | +| [@tuandinh0801](https://github.com/tuandinh0801) | #11454 | +| [@turbolego](https://github.com/turbolego) | #11621, #11762, #11772, #11774, #11781, #12216 | +| [@Tushar49](https://github.com/Tushar49) | #11392 | +| [@ujjawalkaushik1110](https://github.com/ujjawalkaushik1110) | #11863 | +| [@vermasomesh835](https://github.com/vermasomesh835) | #11794 | +| [@vsd2807](https://github.com/vsd2807) | #11565, #11619 | +| [@wahidsadik371-coder](https://github.com/wahidsadik371-coder) | #12126 | +| [@watchingdogs](https://github.com/watchingdogs) | #12031 | +| [@wildcard](https://github.com/wildcard) | #11620 | +| [@xiaoyaner0201](https://github.com/xiaoyaner0201) | #11460, #11662, #12083 | +| [@Xxx91n](https://github.com/Xxx91n) | #11690 | +| [@yourspraveen](https://github.com/yourspraveen) | #11146 | +| [@yxyxy](https://github.com/yxyxy) | #11671 | +| [@ZaimMarzuki](https://github.com/ZaimMarzuki) | #11960 | +| [@Zartharas](https://github.com/Zartharas) | #11340 | +| [@zero-executioner](https://github.com/zero-executioner) | #11631 | +| [@diegosouzapw](https://github.com/diegosouzapw) | maintainer | + +--- ## [3.8.50] — 2026-08-25 _Living section — regenerated 2026-08-12 from all cycle commits (cycle open `ed2db6cb19` → tip). Bullets carry the merged PR and its author; direct pushes listed separately._ diff --git a/docs/i18n/he/CHANGELOG.md b/docs/i18n/he/CHANGELOG.md index dfd7669f74..5c2fcb0b7e 100644 --- a/docs/i18n/he/CHANGELOG.md +++ b/docs/i18n/he/CHANGELOG.md @@ -8,16 +8,1119 @@ ## [3.8.51] — TBD -_Living section — cycle opened at the v3.8.50 freeze (parallel-cycle model). Bullets are aggregated from `changelog.d/` fragments at each `/generate-release` phase._ +_Living section — reconciled 2026-09-15 from all cycle commits (`091589089c` → `c0f92ec98a`, 916 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 | **123** | +| 📝 Commits in the cycle | **916** | +| 🔀 Pull requests referenced | **892** | +| 📋 Changelog entries | **919** | +| 🙌 Contributors credited in entries | **121** | +| 🤖 Automated dependency commits | 18 | + +**Entries by type** + +| Type | Count | +| --- | ---: | +| 🐛 Fixes | 592 | +| ✨ Features | 131 | +| 🧹 Chore | 93 | +| 📚 Docs | 42 | +| 🧪 Tests | 32 | +| ♻️ Refactor | 8 | +| 🏗️ Build | 7 | +| ⚡ Performance | 7 | +| ⚙️ CI | 2 | +| ⏪ Reverts | 2 | +| 📦 Dependencies | 2 | +| 🔒 Security | 1 | + +### 🏆 Top 25 contributors this cycle + +_By commits in `091589089c..c0f92ec98a`, author identities consolidated via `.mailmap` and the merged PR's GitHub login. Bots excluded._ + +| # | Contributor | Commits | +| ---: | --- | ---: | +| 🥇 | diegosouzapw | 323 | +| 🥈 | Bob.Hou (@HouMinXi) | 79 | +| 🥉 | Paco Cartones (@pacocartones) | 77 | +| 4 | Dizzle (@maxmad64bis) | 49 | +| 5 | Koosha Paridehpour (@KooshaPari) | 43 | +| 6 | Nguyen Thanh Dat (@ntdatt812) | 31 | +| 7 | Ravi Tharuma (@RaviTharuma) | 26 | +| 8 | Markus Hartung (@hartmark) | 22 | +| 9 | Webman (@jonlwheat2-gif) | 22 | +| 10 | backryun | 17 | +| 11 | anhtahaylove | 15 | +| 12 | Rafa Martins (@rafacpti23) | 10 | +| 13 | Syed Raheemuddin (@raheemuddin786) | 10 | +| 14 | Nguyễn Viết Tuấn (@TheDemonTuan) | 8 | +| 15 | MumuTW | 7 | +| 16 | Paijo (@oyi77) | 7 | +| 17 | Tobias Andersen (@turbolego) | 6 | +| 18 | Bl0ck (@Bl0ck154) | 5 | +| 19 | SHANMUGAPRIYAN (@geek007git) | 5 | +| 20 | KaspaPulse | 5 | +| 21 | Abhishek Sharma (@abhisheksharma2411) | 4 | +| 22 | Abhishek Divekar (@adivekar-utexas) | 4 | +| 23 | Andrew B. (@AndrianBalanescu) | 4 | +| 24 | NoxzRCW | 4 | +| 25 | opensource-elearning | 4 | ### ✨ New Features +- **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). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- Added Google AI Studio Gemini batch text-to-speech support through `POST /v1/audio/speech`. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- 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. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(routing):** subscription-first auto groupings — `auto/subscription` routes only through plan-included connections with a documented hard-stop overage and fails closed on exhaustion, while `auto/thrifty` orders the pool `subscription → keyless → free → cheap → premium` and steps up one rung at a time as each is exhausted. Billing class comes from a curated per-connection catalog (uncurated is treated as metered, never plan-included), both reuse STRICT_ZERO_COST's per-connection verification, and a quota reading whose `resetAt` has passed is now refreshed regardless of TTL so routing returns to plan capacity as soon as it resets ([#11146](https://github.com/diegosouzapw/OmniRoute/pull/11146)) — thanks @yourspraveen +- **feat(providers):** publish a management-authenticated versioned web-session credential contract from OmniRoute's canonical browser credential metadata ([#11340](https://github.com/diegosouzapw/OmniRoute/pull/11340)) — thanks @Zartharas +- **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)) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add Xquik X search with typed results, credential validation, REST routing, and MCP selection ([#11370](https://github.com/diegosouzapw/OmniRoute/pull/11370)) — thanks @kriptoburak +- **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)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(dashboard):** surface durable exclusive managed leases in the existing Sessions view, keeping leased clients visible across idle gaps while marking connections with in-flight work as active ([#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389)) — thanks @KaspaPulse +- **build(bun):** allow Turbopack bundler flag on Bun 1.4+ with configurable Webpack fallback ([#11471](https://github.com/diegosouzapw/OmniRoute/pull/11471)) — thanks @TheDemonTuan +- feat(api): add an opt-in `modelVisibilityAllowlist`/`modelVisibilityDenylist` settings pair to curate exactly which models `/v1/models` advertises, mirrored into every `auto/*` combo candidate pool so a denied model cannot be routed to via combo selection either (#11481) ([#11997](https://github.com/diegosouzapw/OmniRoute/pull/11997)) +- **feat(rankings):** the Free Provider Rankings page shows what each provider actually served over the last 24 h. It ranked by ELO alone, which left a provider that answers every call with an error in first place; the usage data was already served by the API but never requested. A provider with too small a sample shows a dash, not a number ([#11546](https://github.com/diegosouzapw/OmniRoute/pull/11546)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **feat(guardrails):** enforce a bounded, deterministic contract for Video Bridge transcripts — 256 cues, 4096 input code units and 4 KiB UTF-8 per cue, 64 KiB total text, malformed-Unicode rejection, focus-window scoping, cross-source reconciliation with contributing-source metadata, and a structural provenance trust boundary so caller JSON can never self-assert `embedded`/`audio-bridge` provenance ([#11652](https://github.com/diegosouzapw/OmniRoute/issues/11652)) ([#12009](https://github.com/diegosouzapw/OmniRoute/pull/12009)) +- **feat(video):** orchestrate optional Video Bridge audio extraction and Audio Bridge STT behind a dual opt-in (operator setting AND per-request signal) — a new loopback-only broker `mode=audio` operation shares the frame path's exact process queue, deadline, AbortSignal, and byte budgets to extract a bounded mono 16 kHz PCM WAV from the same already-downloaded video, then reuses the existing Audio Bridge transcription boundary; provider segment timing is preserved when available and marked coarse otherwise, and every failure degrades to a visual-only-safe partial instead of throwing (#11654). ([#12012](https://github.com/diegosouzapw/OmniRoute/pull/12012)) +- Add a tenant-bound Video Bridge drill-down lifecycle on top of the existing secure cache substrate: opaque hashed handles (never raw session/video identifiers), preview/standard/detail multiresolution variants resampled on read, response pagination capped at 8 frames and 32 MiB, and a new authenticated `/api/v1/video-bridge/drilldown` consumer route that stays disabled for remote access by default and denies cross-key access with the same response as a nonexistent handle (no existence oracle). ([#12006](https://github.com/diegosouzapw/OmniRoute/pull/12006)) +- **test(video):** Add the Video Bridge FU-07/FU-09 promotion-evidence harness (#11656) — a frozen Zod manifest schema covering the 8 required scenario kinds (static scenes, rapid cuts, late facts, fades, blur, small text, close events, visual prompt injection) with a minimum of 3 repetitions per case, deterministic declarative fixture recipes (`videoBridgePromotionFixtures.ts`), a pure medians/p95 metrics aggregator, a pure FU-07/FU-09 promotion-verdict evaluator applying the ticket's exact thresholds (missing token usage always holds), a digest-only persistence layer that never retains raw media or raw model responses, and a versioned per-model promotion allowlist shipped empty with every model defaulting to `hold`. The FU-07/FU-09 promotion verdicts themselves remain HOLD — they require a real evidence run against real models on VPS 192.168.0.15. ([#12008](https://github.com/diegosouzapw/OmniRoute/pull/12008)) +- **feat(video bridge):** "embedded" transcript provenance can now be legitimately earned instead of merely asserted — a bounded, allowlisted (`mov_text`/`subrip`/`webvtt`) subtitle probe runs through the loopback-only Video Bridge broker (at most 2 streams, 10s subdeadline bounded by the request deadline, 256 KiB output, 4096-code-unit lines), normalized through a bounded, ReDoS-safe WebVTT parser and Zod-validated end to end. The adapter always resolves to an explicit `success`/`absent`/`transient_failure` outcome — a subtitle failure never breaks the visual description path, and only a fingerprint-verified broker response (never a caller-declared label) can produce embedded cues (#11659). ([#12011](https://github.com/diegosouzapw/OmniRoute/pull/12011)) +- Default new Antigravity-family connections (agy CLI imports and Antigravity OAuth connects) to model auto-sync, so live model discovery lands in the synced catalog and `/v1/models` picks up freshly released upstream models (e.g. Gemini 3.7 Flash tiers) without code changes. Existing connections keep their current setting; the per-connection dashboard toggle remains the opt-out. (#11685 — thanks @MumuTW) ([#11887](https://github.com/diegosouzapw/OmniRoute/pull/11887)) +- **feat(ui):** enable React Compiler (`reactCompiler: true` + `babel-plugin-react-compiler`) for automatic memoization at build time ([#11783](https://github.com/diegosouzapw/OmniRoute/pull/11783)) — thanks @jonlwheat2-gif +- **feat(providers):** add SeekAi (`seekai.cc`) as an OpenAI-compatible New-API gateway — catalog id `seekai` (alias `ska`), `https://seekai.cc/v1`, live `/v1/models` via `passthroughModels`, aggregator-list membership so New-API balance detection can opt in. No referral/aff codes. ([#11786](https://github.com/diegosouzapw/OmniRoute/issues/11786)) ([#12557](https://github.com/diegosouzapw/OmniRoute/pull/12557)) — thanks @HouMinXi +- **feat(zai):** add GLM-5.3-Flash Coding Plan support (1M context, 128K output, vision, `low|high|max` reasoning) and route `zai` GLM-5.3-family API-key traffic through the OpenAI-compatible Coding Plan endpoint with native thinking defaults ([#11801](https://github.com/diegosouzapw/OmniRoute/pull/11801)) — thanks @Neuron-Mr-White +- **feat(combo):** choose how combo models are ordered — manual, provider, score, or name — via a sort control in the dashboard builder, persisted in `config.modelSort` and re-applied on load and after add ([#11812](https://github.com/diegosouzapw/OmniRoute/pull/11812)) — thanks @maxmad64bis +- **feat(free):** custom models can be marked free-tier via `customModels[].isFree`; `isFreeModel()` is the first door and `hidePaidModels` respects it even for providers outside the free budget ([#11843](https://github.com/diegosouzapw/OmniRoute/pull/11843)) — thanks @maxmad64bis +- **feat(nodejs):** add `5dive` as a `configure` target — `omniroute configure 5dive` / `omniroute setup-5dive` write a 5dive auth profile that points an agent fleet's `claude` seats at OmniRoute, with the root-only write, the loopback-vs-`https` endpoint rule and the per-seat model pin handled explicitly ([#11852](https://github.com/diegosouzapw/OmniRoute/pull/11852)) — thanks @5dive-bot +- **feat(sse):** treat `max` as a first-class reasoning-effort tier and clamp per model family (GLM 5.1+/DeepSeek V4+/Kimi K3+ keep native `max`; o1/MiniMax/Grok/Muse Spark clamp to their upstream ceiling) ([#11875](https://github.com/diegosouzapw/OmniRoute/pull/11875)) — thanks @Chewji9875 +- **feat(providers):** the provider plugin manifest now advertises a `usage-fetch` capability for the 40 providers that have a wired usage/quota fetcher, so external dashboards can read it from `GET /api/v1/provider-plugin-manifest` instead of parsing `open-sse/services/usage.ts` after every release. Discovery only — no new fetcher, no quota change, and the Dashboard quota widget stays gated by `USAGE_SUPPORTED_PROVIDERS`. `USAGE_FETCHER_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/fetcherProviders.ts`) and is re-exported from `services/usage.ts`, keeping the manifest module a light leaf instead of pulling the ~490-module usage dispatcher into the manifest route. ([#11903](https://github.com/diegosouzapw/OmniRoute/pull/11903)) — thanks @maxmad64bis / @pacocartones +- **feat(plugins):** `OMNIROUTE_PLUGINS_DIR` sets the directory the runtime plugin scanner reads — and the root the plugin manager installs into — overriding the `HOME`-derived default, so a Docker/K8s deployment can point straight at its bind-mounted plugin tree instead of moving `HOME` just to relocate the scan path. An image that exports no home no longer scans `/tmp/.omniroute/plugins` in silence: the resolved directory is logged once at startup as `scanner.dir_resolved`, naming the input that won. Unset, behaviour is unchanged. Distinct from the CLI-only `OMNIROUTE_PLUGIN_PATH`, which finds `omniroute-cmd-*` command packages and never reached this scanner ([#11906](https://github.com/diegosouzapw/OmniRoute/pull/11906)) — thanks @amaleta / @pacocartones +- **feat(leases):** add an explicit owner-authenticated status action that returns only the active lease's privacy-safe configured connection and provider labels, with generation fencing and no credential or internal-id disclosure ([#11910](https://github.com/diegosouzapw/OmniRoute/pull/11910)) — thanks @KaspaPulse +- **feat(providers):** import-from-file modal shows per-row API errors and ships a downloadable CSV template ([#12071](https://github.com/diegosouzapw/OmniRoute/issues/12071)) ([#12504](https://github.com/diegosouzapw/OmniRoute/pull/12504)) — thanks @HouMinXi +- **feat(providers):** dashboard search matches connection name and `baseUrl` so imported OpenAI-compat nodes surface on the provider card ([#12108](https://github.com/diegosouzapw/OmniRoute/issues/12108)) ([#12495](https://github.com/diegosouzapw/OmniRoute/pull/12495)) — thanks @HouMinXi +- **feat(routing):** add a `score` Auto router strategy that selects the highest configured weighted score and reuses `explorationRate`. ([#12155](https://github.com/diegosouzapw/OmniRoute/pull/12155)) — thanks @jacobsparts +- Add a runtime feature flag to disable universal context handoffs globally without changing the default behavior. ([#12167](https://github.com/diegosouzapw/OmniRoute/pull/12167)) — thanks @jacobsparts +- **feat(providers):** the provider plugin manifest now also advertises a `usage-supported` capability for the 46 providers whose usage API is accepted by the server and Dashboard routes, so integrators can distinguish "the server will serve quota for this provider" from "a fetcher is wired" without reading TypeScript. Discovery only — no fetcher or quota change. `usage-fetch` resolves on id or alias (the usage dispatcher accepts both); `usage-supported` resolves on id alone, matching the runtime guard `USAGE_SUPPORTED_PROVIDERS.includes(providerId)`. `USAGE_SUPPORTED_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/supportedProviders.ts`) and is re-exported from `providers.ts`, mirroring the `fetcherProviders` leaf from #11903 and keeping the manifest a light module. ([#12214](https://github.com/diegosouzapw/OmniRoute/pull/12214)) — thanks @maxmad64bis +- **feat(rankings):** order Free Provider Rankings by what each provider actually served — `GET /api/free-provider-rankings?sortBy=reliability` and a "Most reliable first" toggle on the page. Providers with too few calls to state a success rate keep their score order below the measured ones; the default order is unchanged ([#12218](https://github.com/diegosouzapw/OmniRoute/pull/12218)). — thanks @maxmad64bis +- **perf(sse):** defer `cloneLogPayload()` in the structured SSE collector until after the `maxEvents`/`maxBytes` cap check, eliminating ~9,800 wasted `structuredClone` calls per streaming response (65–71% faster `push()`). Reducer snapshot isolation restored for OpenAI and Responses summaries ([#12241](https://github.com/diegosouzapw/OmniRoute/pull/12241)) — thanks @PauloHSOliveira ([#12243](https://github.com/diegosouzapw/OmniRoute/pull/12243)) +- **feat(usage):** Devin CLI agentic quota (Codeium seat-management GetUserStatus) and OpenRouter key limits plus account credits now surface in Provider Limits ([#12256](https://github.com/diegosouzapw/OmniRoute/pull/12256) — thanks @Neuron-Mr-White) +- **feat(settings):** persist `headroomUrl` through Settings so status/start use the operator URL instead of only `HEADROOM_URL` ([#12306](https://github.com/diegosouzapw/OmniRoute/issues/12306)) ([#12487](https://github.com/diegosouzapw/OmniRoute/pull/12487)) — thanks @HouMinXi +- **feat(auto-combo):** Auto-Combo scoring can now weigh how often a provider/model has actually succeeded. The engine already carried that number on every candidate — 24 hours of usage history behind a ten-sample floor, real-time metrics otherwise — and the scoring function never read it, while the weight table described `stability` as if it did. `reliability` (`1 - failureRate`, with the same field precedence and the same rate-bounding the speed ranking already uses, so a corrupt reading means "nothing observed" rather than "fails every call") is now a declared factor shipping at weight `0`, so routing is unchanged until an operator gives it one, and the `stability` description now matches what that factor computes ([#12317](https://github.com/diegosouzapw/OmniRoute/pull/12317)) — thanks @maxmad64bis +- **feat(routing):** With `freeAccessPolicy: "strict"`, the read-only candidate listing (`GET /v1/auto-combo/{channel}/candidates`) no longer hides the candidates the zero-cost guard excludes — the same read-only transparency the resilience filter already honours (#9133). Each candidate now carries `freeAccessExclusion` saying why it would be kept out, and it tells an exhausted allowance apart from a quota reading that never arrived or went stale, which used to look identical from the outside. Routing is unchanged: the listing reports, it never enforces. The separate `excludeTosAvoid` guard still drops its candidates without a reason; that gap is now documented rather than closed ([#12319](https://github.com/diegosouzapw/OmniRoute/pull/12319)) — thanks @maxmad64bis +- **feat(radar):** The Radar catalog table now shows two facts it was already receiving from the feed and dropping on the floor: the per-model rate limits (requests and tokens, per minute and per day) in a new column, and a badge when a provider's terms state it may train on the prompts you send. A limit of zero renders as zero rather than "rate-only" — for a ceiling those are opposite facts — and a model with no training statement gets no badge, because an absent statement is not a guarantee ([#12320](https://github.com/diegosouzapw/OmniRoute/pull/12320)) — thanks @maxmad64bis +- **feat(radar):** explain Community, single-use, contributor, supporter, recovery, abuse, offers, and privacy rules before either Radar activation action, and remove the superseded fixed-PR grant promise from every UI locale ([#12342](https://github.com/diegosouzapw/OmniRoute/pull/12342)) +- **feat(gamification):** the dashboard Profile page now shows the real daily streak — `/api/gamification/level` returns `streak: { current, longest }` (per key with `apiKeyId`, operator-wide maximum otherwise) and the streak card reads it instead of a hard-coded 0 (#2403) ([#12377](https://github.com/diegosouzapw/OmniRoute/pull/12377)) — thanks @pacocartones +- **feat(gamification):** the dashboard leaderboard now shows each API key's display name under the Name column instead of a truncated key id; `GET /api/gamification/leaderboard` attaches `name` per entry (name only — no key material), while the shared ranking helper and the federation leaderboard stay id-only — thanks @pacocartones ([#12385](https://github.com/diegosouzapw/OmniRoute/pull/12385)) +- **feat(gamification):** enforce the documented 1000 XP/min per-API-key anti-cheat rate limit on the XP award path; over-limit awards are logged and skipped instead of persisted, and the sliding window now matches the timestamp format stored in `xp_audit_log` ([#2403](https://github.com/diegosouzapw/OmniRoute/issues/2403)) ([#12390](https://github.com/diegosouzapw/OmniRoute/pull/12390)) — thanks @pacocartones +- **feat(admin):** localize the gamification anomalies page — the loading state, the Status column and the Suspicious badge now come from the `common` catalog (new `common.suspicious` key propagated to every locale) — add it to the Gamification sidebar group as `gamification-admin` (`/dashboard/gamification/admin`), and expose the loading and empty states as polite `role="status"` live regions (#12401 — thanks @pacocartones) +- **feat(providers):** skip GitHub combo members missing from the live synced catalog, and drop Copilot models that are policy-disabled or hidden from the model picker ([#12473](https://github.com/diegosouzapw/OmniRoute/pull/12473)) — thanks @RaviTharuma +- **feat(i18n):** locale-expansion tooling — `npm run i18n:add-locale` adds a language to config, dashboard, docs mirrors, CLI, README/indexes and site in one command; browser, CLI and cookie detection resolve `uk`, `fil`/`tl`, `zh-Hant` (and the retired legacy `in` → `id`) via config aliases; `config/i18n.json` now ships in the npm package so the published CLI can read it; new real-translation ratio gate (`npm run i18n:check-ratio`, advisory) with a per-locale ratchet baseline; `run-translation --adopt` restores `.i18n-state.json`; `sync-language-bars` generates 🌐 bars from config; `validate_translation.py` loads its allowlist again. Retires the duplicate `in` locale (Indonesian mislabelled as Hindi) — 42 honest locales; saved `NEXT_LOCALE=in` / `OMNIROUTE_LANG=in` keep working. (#12496) +- **feat(api):** Emit gateway-measured `tokens_per_second` (TTFT excluded) on streaming usage and `X-OmniRoute-Tokens-Per-Second` when first-token latency is known ([#12616](https://github.com/diegosouzapw/OmniRoute/issues/12616)) ([#12631](https://github.com/diegosouzapw/OmniRoute/pull/12631)) — thanks @RaviTharuma +- **feat(opencode-plugin): map gateway cost/usage/tok/s onto OpenCode inference payloads** — the official plugin copies `X-OmniRoute-Response-Cost`, token counts, `X-OmniRoute-Tokens-Per-Second` / `usage.tokens_per_second`, TTFT, and the winning `X-OmniRoute-Model` onto the JSON/SSE body OpenCode already consumes. Missing tok/s is left unset (never `tokens / latency`). (#12636) — thanks @RaviTharuma +- **feat(combo):** add combo strategy `quota-weighted`: skip exhausted accounts, then weighted-draw by leftover / in-flight load; existing conversations stay pinned until the account is empty ([#12789](https://github.com/diegosouzapw/OmniRoute/pull/12789)) — thanks @HouMinXi +- **feat(models):** Account live listings become the chat catalog source for Claude, Codex, Copilot, and AGY; public metadata only fills prices on IDs those accounts already list. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **fix(models):** Union `agy` and `antigravity` live catalogs so `agy/gemini-3.8-flash-high` is not rejected after the prefix folds to `antigravity`. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **feat(opencode):** Add `@omniroute/opencode-plugin-v2`, an opencode v2 plugin that gives a v2 install a route to an OmniRoute gateway: it publishes models, combos and auto-combos into the host catalog, refreshes them lazily behind a 300s TTL, and keeps serving the last known catalog from an on-disk snapshot when the gateway is unreachable. The v1 plugin (`@omniroute/opencode-plugin`) is untouched and keeps working as before. The plugin adapts to the host instead of assuming one shape: it reads the shape the host seeds into the catalog draft and publishes the `api` block, the pre-`api` top-level fields, or both. The gateway key comes from the host credential store when one is connected, so no secret needs to sit in a config file; a source the gateway refuses is named in a warning instead of silently degrading the catalog to raw model ids; and tool schemas bound for a Gemini model are stripped of the JSON-Schema keywords Gemini rejects, so tool calling survives that route ([#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870)) — thanks @maxmad64bis +- feat(services): show sanitized CLIProxyAPI account health from its authenticated management API without exposing credentials, file paths, or raw account metadata (#6342) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add AnySearch free web search + URL extract (webFetch) with typed results, credential validation, REST routing, and MCP selection - fallback-only ([#11690](https://github.com/diegosouzapw/OmniRoute/pull/11690)) — thanks @Xxx91n +- **feat(dashboard):** display clamped `[0, 100]%` cached input token ratio in request logs table ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) — thanks @b3nw +- **feat(catalog):** add `OMNIROUTE_DISABLE_THINKING_LEVEL_VARIANTS` feature flag to optionally filter out thinking level variants from model catalog ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) — thanks @b3nw +- **feat(dashboard):** continuously export call logs to external analytics stores. A pluggable destination registry ships the full Logs-tab record set on an hourly `JobRegistry` cron, with a persisted per-destination cursor, batched inserts, a config UI rendered from each destination's own field descriptors, and a REST layer (`/api/log-export/*`) for CRUD, a connection test, and an on-demand run. A destination can opt into `includeBodies` to also ship the request and response payloads shown in the Logs detail pane, including the client and provider views of each call; this is off by default, and payloads inherit the dashboard's PII sanitisation, secret redaction and `noLog` handling. Google BigQuery is the first destination, using a service-account key stored encrypted at rest and streaming inserts keyed by call-log id, into a table that is day-partitioned on `timestamp` and clustered on `api_key_name`, `provider`, `model` and `status`. ([#11945](https://github.com/diegosouzapw/OmniRoute/pull/11945)) — thanks @dpozimski +- **feat(providers):** add MaxAI as a signed, OpenAI-compatible provider serving its 13 paid chat models (GPT-5.6 / Luna / Thinking, Claude 5 Sonnet, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite, Grok 4.1-fast / 4.5, DeepSeek V3.2 / R1, Llama 3.3 70B) through OmniRoute's `/v1` endpoint, with per-request HMAC-SHA1→SM3→AES request signing, live model + context-window discovery from `/models/get_config`, and prompted tool-calling translated to OpenAI `tool_calls` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI vision input — image_url content parts are forwarded inline in `message_content` to the 6 vision-capable models (GPT-5.6 / Luna / Thinking, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite) ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI image generation — 6 image models (gpt-image-1, dall-e-3, flux-1-schnell/dev/pro, sd3-medium) exposed through `POST /v1/images/generations` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI document RAG — inline base64 file/document attachments are uploaded to MaxAI (content-addressed `doc_id`) and attached to the chat via `doc_list` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** browserless MaxAI onboarding — email device-pair login (`/api/providers/[id]/login`) and signed access-token refresh, so a connection can be created and kept fresh without a real browser or Google OAuth ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** per-provider TLS impersonation profile (MaxAI presents a Windows Firefox-150 client fingerprint) so its bot-sensitive endpoints accept OmniRoute traffic ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** add **Nimble** as a web-search and web-fetch provider (`nimble-search`) — `/v1/search` routes to Nimble's search API at `lite` depth (locale, freshness and include/exclude domain filters mapped onto the shared request contract), and `/v1/web/fetch` routes to Nimble Extract, which covers all four fetch formats (`markdown`, `html`, `links`, `screenshot`) from a single call. One API key serves both surfaces. ([#11620](https://github.com/diegosouzapw/OmniRoute/pull/11620)) — thanks @wildcard +- The npm publish is automatic again, through npm Trusted Publishing (OIDC): the hosted `stage-npm` job publishes with a short-lived credential minted from GitHub's id-token — no `NPM_TOKEN`, no 2FA prompt, provenance attached. `publish_mode=staged` (owner approves with 2FA) and `direct` (token) remain available on `workflow_dispatch`. ([#11931](https://github.com/diegosouzapw/OmniRoute/pull/11931)) +- **feat(providers):** Add **Opper** as an API-key gateway provider — EU-hosted AI gateway with 700+ models from 30+ providers behind one OpenAI-compatible API and one key (`OPPER_API_KEY`); model ids use `provider/model` format (e.g. `anthropic/claude-sonnet-4-6`, `openai/gpt-5`); live model catalog at `https://api.opper.ai/v3/compat/models`; entry mirrors `requesty` (same shape, `passthroughModels: true`, no static seed) ([#11629](https://github.com/diegosouzapw/OmniRoute/pull/11629)) +- **feat(dashboard):** the `/dashboard/orchestration` snapshot hook now subscribes to the `agents` WebSocket channel (`agent.task.updated`) instead of `requests` as its refetch trigger, and relaxes its background poll from 5s to 30s while that WS connection is up — falling back to the tighter 5s cadence, reprogrammed live on any connect/disconnect transition, whenever the socket is down. ([#12409](https://github.com/diegosouzapw/OmniRoute/pull/12409)) +- **feat(dashboard):** Orchestration canvas quick wins — search box plus state/source/provider filter chips with a one-click clear, and per-source collapse/expand, all reflected in the URL so a filtered/collapsed view is shareable and survives a refresh; the detail drawer gained a "copy trace JSON" action and hardened error/empty-state and accessibility handling; the Agents-tab edges now animate traveling particles along active (running) connections; and the canvas node/edge status colors moved off fixed hex values onto theme-aware `--orch-status-*` CSS custom properties, so they adapt correctly to light/dark mode. ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- **chore(dashboard):** Orchestration UI hardening pass and the missing component/model test coverage it called for — `OrchestratorNode`/`ActivityNode`/`OverflowNode` rendering, the `?node=`/overflow-click page routing, the Agents-tab orchestrator-click no-op and `showCompleted` toggle, and the overview kanban's done-column sort order (#12270, #12271). ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- New `/dashboard/orchestration` page: live unified view of everything running — Cloud Agent, A2A and Conductor as a real-time graph (Agents tab), the combo cascade (Routing tab, reusing the Combo Live Studio) and a state kanban (Overview tab), with a detail drawer (trace, cost, approve/cancel). Read-only over existing APIs — no new backend. Canvas concept credit: PR #11815 design ([#12261](https://github.com/diegosouzapw/OmniRoute/pull/12261)) +- **feat(dashboard):** new "History" tab on `/dashboard/orchestration` — an Airflow-style grid of finished runs over a 24h/7d/30d preset window, one row per (source, identity), clicking a cell opens the existing detail drawer. It is backed by real persistence: A2A task lifecycle transitions are now written to the `a2a_tasks` table (purged after 30 days, configurable via `OMNIROUTE_A2A_HISTORY_RETENTION_DAYS`) and served by the new `GET /api/a2a/tasks/history` listing endpoint, with the task-detail route falling back to persisted history once a run leaves the in-memory snapshot. Conductor runs stay remote and are not persisted locally — the tab says so instead of silently omitting them. ([#12479](https://github.com/diegosouzapw/OmniRoute/pull/12479)) +- **feat(dashboard):** the orchestration detail drawer gained a "Repeat" action for Cloud Agent, A2A and Conductor tasks — a two-click confirm (click once to arm, click again within the confirm window to fire) re-submits the original prompt/input as a new run. The button is disabled with an explanatory tooltip whenever the original input can't be recovered from the loaded task detail (e.g. it never carried a prompt, or the detail failed to load). Two limitations of the A2A variant, by design: it targets the `/a2a` JSON-RPC endpoint, which authenticates with an API key only (`REQUIRE_API_KEY=true` or a configured `OMNIROUTE_API_KEY` makes a dashboard-session repeat answer `HTTP 400` — surfaced verbatim in the drawer's error line, never as a success), and the `message/send` call is SYNCHRONOUS: the POST blocks for the whole skill run, so the success confirmation only appears once the repeated task finishes. A dashboard-authenticated A2A creation path is deliberately left to a follow-up — widening the endpoint's auth posture is an operator decision, not a side effect of this feature. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(a2a):** A2A task execution now records which memories were consulted for the task's last user message as `metadata.memoryHits` (id/key/type/content-snippet) plus a `memory_hits` history event, purely for observability — the retrieved memory is never injected into a skill's prompt or behavior. Gated by the `OMNIROUTE_A2A_MEMORY_HITS` kill-switch (default enabled; set to `0` to skip the recall lookup entirely). The drawer's new "Memory used" section lists these hits for a2a tasks and is omitted whenever there are none. Known limitation: recall only resolves under the keyless posture — a keyed caller's task owner is a SHA-256 prefix of the API key, while memory rows are keyed by the database api-key id, and no hash→id lookup exists today, so the hit list stays empty for keyed callers. The recorded hits are also kept out of the task's own `input` (and therefore out of the persisted input and of the "Repeat" request body), so repeating a task never re-sends the previous run's memory snippets. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(providers):** add a Perplexity Agent API provider (`perplexity-agent` / `pplx-agent`) for Perplexity `/v1/responses`, including the documented Anthropic, OpenAI, Google, xAI, DeepSeek, Z.AI, Moonshot/Kimi, NVIDIA, and Perplexity model IDs plus Anthropic-model `max_output_tokens` compatibility. ([#12103](https://github.com/diegosouzapw/OmniRoute/pull/12103)) — thanks @AIB1TAL0S +- **feat(providers):** add RPD (Requests Per Day) limit to provider rate limit overrides across UI, schemas, DB, and i18n ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) — thanks @b3nw +- **feat(providers): add UC Direct (uncensored.com Developer API), the metered OpenAI-compatible surface.** A standard OpenAI-compatible passthrough (default executor) for uncensored.com's official REST API at `https://api.uncensored.com/api/v1`: `X-api-key` auth (never-expiring `uai_sk_live_` key), `POST /chat/completions` with streaming SSE and native tool-calling, and the full live metered catalog (82 models across 15 providers, discovered from the public `GET /v1/models`). Registered as provider `uc-direct` (alias `ucd`). Complements the un-metered `uc` persona provider — same models, metered credits and a plain API key instead of a subscription session. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(providers): add UC (uncensored.com), the un-metered subscription "persona" chat as an OpenAI-compatible provider.** A WebSocket web-app port: a durable Clerk credential mints a short-lived session token per connect (browserless — no API key), driving UC's persona socket. Ships the browserless email-code login (request → verify → harvest), the 19 verified persona models (Claude Opus, Gemini, Grok, GLM, Kimi, DeepSeek, MiniMax, incl. the uncensored variants), prompted `` tool-calling with a per-model code-style dialect + auto-cure retry for guardrailed models, live ``/reasoning split, streaming + non-streaming OpenAI responses, and full quota/auth error surfacing (paywall / message-limit / rate-limit → 429, invalid session → 401 re-login). Full multimodal parity via the persona blob-upload layer: **vision** (image input, 15 vision-capable models), **document RAG** (PDF/doc upload, server-side extraction), **image generation** (22 models), **video generation** (14 models, async signed-url → poll), and **TTS** (streaming MP3). Registered as provider `uc` (alias `ucn`). The metered OpenAI-compatible Developer API is a separate `uc-direct` provider. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(logs):** add copy-all button to request detail modal ([#11083](https://github.com/diegosouzapw/OmniRoute/pull/11083)) — thanks @AndrianBalanescu +- **feat(check):** make serviceKinds required and add the reverse-walk provider consistency gate ([#11392](https://github.com/diegosouzapw/OmniRoute/pull/11392)) — thanks @Tushar49 +- **feat(providers):** only advertise verified-working connections by default ([#11448](https://github.com/diegosouzapw/OmniRoute/pull/11448)) — thanks @alltomatos +- **feat(resilience):** add hierarchical concurrency admission ([#11493](https://github.com/diegosouzapw/OmniRoute/pull/11493)) +- **feat(providers):** derive + surface expiry for JWT-bearing web cookies (#11497) ([#11505](https://github.com/diegosouzapw/OmniRoute/pull/11505)) — thanks @oyi77 +- **feat(tunnels):** support named cloudflared tunnels via CLOUDFLARED_CONFIG ([#11561](https://github.com/diegosouzapw/OmniRoute/pull/11561)) — thanks @ggiak +- **feat(compression):** make proactive context-compression threshold a live setting ([#11564](https://github.com/diegosouzapw/OmniRoute/pull/11564)) — thanks @ragnar-claude +- **feat(resilience):** add provider peak-hour protection ([#11622](https://github.com/diegosouzapw/OmniRoute/pull/11622)) — thanks @Neuron-Mr-White +- **feat(providers):** add 1min.ai provider ([#11631](https://github.com/diegosouzapw/OmniRoute/pull/11631)) — thanks @zero-executioner +- **feat(dashboard):** collapsible JSON tree viewer for request/response payloads ([#11703](https://github.com/diegosouzapw/OmniRoute/pull/11703)) — thanks @hartmark +- **feat(routing):** add LiquidAI LFM2.5-2.6B free tier via OpenRouter ([#11752](https://github.com/diegosouzapw/OmniRoute/pull/11752)) — thanks @brick30llc-ctrl +- **feat(pricing):** add GLM-5.3-Flash pricing, model specs, and catalog registration ([#11830](https://github.com/diegosouzapw/OmniRoute/pull/11830)) — thanks @AndrianBalanescu +- **feat(routing):** add relayMode for schema-locked context handoffs ([#11839](https://github.com/diegosouzapw/OmniRoute/pull/11839)) — thanks @raheemuddin786 +- **feat(config):** add support for runtime system prompt configuration and hot-reloading ([#11841](https://github.com/diegosouzapw/OmniRoute/pull/11841)) — thanks @raheemuddin786 +- **feat(routing):** expand connection-aware quota prefilter across combo strategies (#11682) ([#11850](https://github.com/diegosouzapw/OmniRoute/pull/11850)) — thanks @HouMinXi +- **feat(models):** reactive discovery sync after Antigravity model-not-found 404 ([#11888](https://github.com/diegosouzapw/OmniRoute/pull/11888)) — thanks @MumuTW +- **feat(models):** auto-aliases and token limits from synced Antigravity discovery ([#11889](https://github.com/diegosouzapw/OmniRoute/pull/11889)) — thanks @MumuTW +- **feat(settings):** raise sticky round-robin limit caps to 1000 ([#12015](https://github.com/diegosouzapw/OmniRoute/pull/12015)) — thanks @adivekar-utexas +- **feat(resilience):** operator-configurable global credential health check interval ([#12043](https://github.com/diegosouzapw/OmniRoute/pull/12043)) — thanks @adivekar-utexas +- **feat(quota):** use official OpenCode Go usage API ([#12124](https://github.com/diegosouzapw/OmniRoute/pull/12124)) — thanks @ddarkr +- **feat(resilience):** default the credential health check sweep to 60 minutes ([#12138](https://github.com/diegosouzapw/OmniRoute/pull/12138)) — thanks @adivekar-utexas +- **feat(quality):** new-code mode for the complexity and dead-code ratchets (Clean as You Code) ([#12142](https://github.com/diegosouzapw/OmniRoute/pull/12142)) +- **feat(dashboard):** orchestration canvas — unified model + snapshot hook (part 1/2) ([#12156](https://github.com/diegosouzapw/OmniRoute/pull/12156)) +- **feat(usage):** add Kilo Code balance and Kilo Pass quotas ([#12178](https://github.com/diegosouzapw/OmniRoute/pull/12178)) — thanks @killer30001000 +- **feat(providers):** refresh vendored ChatGPT Web connector to v4.0.7 ([#12181](https://github.com/diegosouzapw/OmniRoute/pull/12181)) — thanks @backryun +- **feat(providers):** manual "Clear cooldown" action in the cooling panel ([#12224](https://github.com/diegosouzapw/OmniRoute/pull/12224)) — thanks @patrykkopycinski +- **feat(providers):** restore ChatGPT Web via clean-room browser transport ([#12239](https://github.com/diegosouzapw/OmniRoute/pull/12239)) — thanks @backryun +- **feat(sse):** wire the PROVIDER_PROFILES window gate into the global provider cooldown ([#12247](https://github.com/diegosouzapw/OmniRoute/pull/12247)) +- **feat(providers):** modernize CLOVA Studio chat and embeddings ([#12277](https://github.com/diegosouzapw/OmniRoute/pull/12277)) — thanks @backryun +- **feat(browser):** adopt Obscura as primary headless browser engine with Chromium fallback ([#12286](https://github.com/diegosouzapw/OmniRoute/pull/12286)) — thanks @opensource-elearning +- **feat(sse):** full language parity for output styles (es/de/fr/it/ru/zh + autoDetect) ([#12289](https://github.com/diegosouzapw/OmniRoute/pull/12289)) +- **feat(quality):** complete test:scoped — --full map rebuild, stdin selection, CI loader parity (#8084 D1) ([#12353](https://github.com/diegosouzapw/OmniRoute/pull/12353)) +- **feat(providers):** refresh Fable, Cursor, and Devin catalogs ([#12367](https://github.com/diegosouzapw/OmniRoute/pull/12367)) — thanks @backryun +- **feat(video):** redact transcript text from logs and durable memory (#12150 P1) ([#12427](https://github.com/diegosouzapw/OmniRoute/pull/12427)) +- **feat(dashboard):** parent-link, genuine-continuation badge, and modal perf fixes ([#12448](https://github.com/diegosouzapw/OmniRoute/pull/12448)) — thanks @hartmark +- **feat(dashboard):** adaptive context-budget dial on compression panel ([#12488](https://github.com/diegosouzapw/OmniRoute/pull/12488)) — thanks @HouMinXi +- **feat(providers):** refresh Z.ai Web models and browser transport ([#12524](https://github.com/diegosouzapw/OmniRoute/pull/12524)) — thanks @backryun +- **feat(video):** redact raw client-snapshot transcript fields in the detailed log (#12150 P2) ([#12528](https://github.com/diegosouzapw/OmniRoute/pull/12528)) +- **feat(providers):** refresh NVIDIA hosted models ([#12538](https://github.com/diegosouzapw/OmniRoute/pull/12538)) — thanks @backryun +- **feat(quota):** Moonshot Open Platform balance and TPD lock for custom nodes ([#12590](https://github.com/diegosouzapw/OmniRoute/pull/12590)) — thanks @HouMinXi +- **feat(video):** redact transcript in the in-memory pending-request snapshot (#12430 item 6) ([#12596](https://github.com/diegosouzapw/OmniRoute/pull/12596)) +- **feat(dashboard):** link a log entry's Conversation Context to its owning conversation ([#12646](https://github.com/diegosouzapw/OmniRoute/pull/12646)) — thanks @hartmark +- **feat(catalog):** eligibility-gated free-tier bucket ([#12669](https://github.com/diegosouzapw/OmniRoute/pull/12669)) +- **feat(db):** fail-closed previous_response_id continuation for redacted video turns (#12150 P2b) ([#12707](https://github.com/diegosouzapw/OmniRoute/pull/12707)) +- **feat(gamification): pay the documented `streak_bonus` and `badge_unlock` XP rewards.** `XP_REWARDS` listed both rewards but the award pipeline never paid them: the private reward table in `events.ts` omitted them, `updateStreak()` did not report when a streak extended, and badge unlocks carried no XP. Every request that extends a daily streak now pays `streak_bonus × streak length` once per UTC day (guarded by a same-day `xp_audit_log` check), and every badge unlocked through the pipeline pays `badge_unlock` once per badge (guarded by the `user_badges` primary key; `unlockBadge()` now reports whether it inserted). Bonus XP flows through the same `addXp` + level sync + global/weekly/monthly leaderboard path as action XP, so level-ups and rankings include it. The Radar supporter recognition unlock stays XP-free. (#12522 — thanks @pacocartones) +- **feat(providers):** advertise a `free-tier` capability in the provider plugin manifest for every provider with documented free models, so sidecars and dashboards can filter free-capable providers without reading the quota catalog ([#12786](https://github.com/diegosouzapw/OmniRoute/pull/12786)) — thanks @maxmad64bis +- **feat(providers):** Added EURouter as an OpenAI-compatible API-key gateway (`https://api.eurouter.ai/v1`), with live model discovery via `passthroughModels`. Its copy states that models are served by third-party upstreams listed per model, so an EU-based router is not read as EU data residency for inference. ([#13025](https://github.com/diegosouzapw/OmniRoute/pull/13025)) — thanks @ntdatt812 +- **feat(providers):** Added GreenPT as an OpenAI-compatible API-key provider (`https://api.greenpt.ai/v1`), with live model discovery via `passthroughModels`. No free-inference badge: the published docs describe a free API subscription billed per token, not a free tier. ([#13024](https://github.com/diegosouzapw/OmniRoute/pull/13024)) — thanks @ntdatt812 +- **feat(i18n):** 9 new locales — Greek (`el`), Croatian (`hr`), Serbian (`sr`), Lithuanian (`lt`), Estonian (`et`), Latvian (`lv`), Slovenian (`sl`), Maltese (`mt`), Irish (`ga`) — across the dashboard, docs mirrors, CLI, README and the site; OmniRoute now ships every official EU language (51 locales). (#13044) +- **feat(i18n):** 8 new locales — Kannada (`kn`), Malayalam (`ml`), Odia (`or`), Punjabi (`pa`), Nepali (`ne`), Sinhala (`si`), Burmese (`my`), Khmer (`km`) — across the dashboard, docs mirrors, CLI, README and the site (59 locales). The translator now restores the ICU literal escape around angle placeholders and splits oversized docs sections before translating. (#0000) ([#13660](https://github.com/diegosouzapw/OmniRoute/pull/13660)) +- feat(providers): **list Agnes 3.0 Flash as the current free chat model, drop retired 1.5 Flash, add Image 2.0/2.5 Flash plus Video 2.5/2.5 Flash, and discover the live `/v1/models` catalog (including the CN host `api.agnes-ai.cn`).** `agnes-1.5-flash` now forwards to `agnes-3.0-flash`. Video 2.5 polls `GET /v1/videos/{id}` (not the V2.0 `/agnesapi` contract). Live `/v1/models` (2026-09-09) no longer serves 1.5; the wiki marks it deprecated. 3.0 Flash is 512K context / 65,536 max output, same window as 2.5. CN-region keys use the existing per-connection base-URL field, default stays `apihub.agnes-ai.com`. ([#13120](https://github.com/diegosouzapw/OmniRoute/pull/13120)) — thanks @HouMinXi +- **feat(sse):** Codex and OpenAI catalogs list GPT-6 Astra with effort aliases (`-low` through `-ultra`); Codex CLI identity pins `@openai/codex@0.153.4` in lockstep with the image ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **feat(dashboard):** the orchestration History tab gained a "Compare runs" mode — toggling it turns each grid cell into a 2-item selection queue (a 3rd click drops the oldest pick), and picking two cells opens a side-by-side comparison panel instead of the usual detail drawer. The panel fetches both runs' detail the same way the drawer does (falling back to persisted history once a run leaves the live TTL window) and shows, per side: identity/source/state, start time, a signed `right - left` delta for duration/cost/event count, the event timeline aligned by index, and any memory hits. One side's fetch failing never blocks the other, and a delta is only ever computed when both sides have a finite value — otherwise it renders "—", never `NaN`. Comparing two runs from different sources or skills still works; a banner marks the deltas as informational rather than hiding them, since the two runs aren't a strict apples-to-apples pair. ([#12677](https://github.com/diegosouzapw/OmniRoute/pull/12677)) +- **feat(dashboard):** badge a conversation that never reached a clean stop ([#12717](https://github.com/diegosouzapw/OmniRoute/pull/12717)) — thanks @hartmark +- **feat(release):** reconcile-changelog tool + version-anchored fragment aggregation ([#12987](https://github.com/diegosouzapw/OmniRoute/pull/12987)) +- **feat(mcp):** carry context_length through MCP list_models_catalog projection (#12776) ([#13406](https://github.com/diegosouzapw/OmniRoute/pull/13406)) — thanks @KooshaPari +- **feat(cli):** make startup readiness budget configurable (#13369) ([#13433](https://github.com/diegosouzapw/OmniRoute/pull/13433)) — thanks @KooshaPari + ### 🐛 Bug Fixes +- **security(streaming):** sanitize generic mid-stream error messages before emitting OpenAI, + Responses, or Claude SSE failure frames and before diagnostic logging, while preserving raw + failures for internal classification and keeping client disconnects out of provider failure state. ([#12457](https://github.com/diegosouzapw/OmniRoute/pull/12457)) +- **fix(claude):** `blockExtraUsage: false` no longer lets 5h quota preflight skip the account; extra usage is billed after the session bar is gone, so the request must reach Anthropic ([#12803](https://github.com/diegosouzapw/OmniRoute/pull/12803)) — thanks @HouMinXi +- Sanitize HuggingChat conversation-creation and message-send transport failures before they reach client error bodies or provider logs. ([#12467](https://github.com/diegosouzapw/OmniRoute/pull/12467)) +- **fix(dashboard):** Keep local and theme-aware provider SVG icons at a definite layout size so Chromium does not collapse them to 0×0 after the v3.8.50 image-rendering change ([#12054](https://github.com/diegosouzapw/OmniRoute/pull/12054)) — thanks @ponkcore +- **fix(security):** Sanitize provider and runtime failures before public API, SSE and MCP responses and before persistent request, proxy and usage logs, preventing credentials, stack traces and host filesystem paths from crossing those boundaries while preserving stable error codes and useful diagnostics. ([#12506](https://github.com/diegosouzapw/OmniRoute/pull/12506)) +- **fix(cli):** `omniroute tunnel create` no longer crashes with `Cannot read properties of undefined (reading optsWithGlobals)` — removed the duplicate positional argument that caused Commander.js to misalign the action callback parameters ([#12295](https://github.com/diegosouzapw/OmniRoute/issues/12295)) ([#12368](https://github.com/diegosouzapw/OmniRoute/pull/12368)) — thanks @gonisulaimann +- Stop advertising Gemini Live-only models as supported audio endpoints until OmniRoute proxies the bidirectional Live protocol. ([#11710](https://github.com/diegosouzapw/OmniRoute/pull/11710)) — thanks @RaviTharuma +- **fix(kie):** reroute `flux/kontext` off the KIE Market `createTask` flow — it is catalogued with `isMarket: true` but has no Market catalog page, so KIE rejected it with "model name not supported"; it now hits the dedicated `POST /api/v1/flux/kontext/generate` / `GET /api/v1/flux/kontext/record-info` endpoints instead (#11296). ([#11985](https://github.com/diegosouzapw/OmniRoute/pull/11985)) — thanks @hartmark +- **fix(kie):** correct 12 more KIE Market catalog ids that were sent to `createTask` unchanged but diverge from KIE's documented upstream `model` values — GPT Image 2 T2I/I2I (drops the `gpt/` prefix), GPT Image 1.5 T2I/I2I (`gpt-image/` namespace), Seedream 5.0 Lite T2I/I2I (drops the `.0`), all 4 Flux 2 variants (`flux-2/` namespace, generic variant renamed `flex`), and Wan 2.7 Image / Image Pro (dash instead of dot) — each verified individually against the literal example request published on docs.kie.ai. `#11326`'s "everything else already matches" claim was wrong a second time (#11296); `z-image/4.0-*`/`z-image/4.5-*` and `flux/kontext` remain open, documented as unresolved in `KIE_MARKET_UPSTREAM_MODEL_IDS`'s comment pending further verification. ([#11667](https://github.com/diegosouzapw/OmniRoute/pull/11667)) +- **fix(dashboard):** `useApiKeySave.handleSaveApiKey` no longer forces a full upstream `/models` catalog sync on every non-curated provider connection save — callers can now pass `skipModelSync: true` to opt out, so a workflow that only wants to add one manual model no longer floods the provider's available-models list with hundreds/thousands of synced entries. The flag is a client-side intent signal only and is stripped before the connection payload is POSTed to `/api/providers`; default behavior (full sync on save) is unchanged when the flag is omitted (#11324) ([#11664](https://github.com/diegosouzapw/OmniRoute/pull/11664)) +- **Provider connections:** keep `tokenExpiresAt` when a connection is created. The create-path allowlist omitted it, so every insert stored NULL and the dashboard token badge could read a fresh connection as expired until its first background refresh ([#11368](https://github.com/diegosouzapw/OmniRoute/pull/11368)). — thanks @ntdatt812 +- **fix(combos):** the combo builder's precision-select, global-model-search, and manual-entry flows now serialize a model step's `model` string using the provider's already-computed routing-alias prefix (e.g. `oc/`) instead of rebuilding it from the raw canonical `providerId`, fixing the no-auth "OpenCode Free" provider (`opencode`) being routed to the unrelated paid "OpenCode Zen" provider (`opencode-zen`) because `opencode` doubles as a manual routing-prefix override ([#11433](https://github.com/diegosouzapw/OmniRoute/issues/11433)). ([#11670](https://github.com/diegosouzapw/OmniRoute/pull/11670)) +- **fix(radar):** the catalog feed cache now keeps `generatedAt`, the date the feed's data was built, next to `fetchedAt`, the date this install downloaded it (#11435). The feed schema requires that date and the sync path validates it, but the cache dropped it — so a feed fetched minutes ago and one carrying weeks-old figures looked identical to everything downstream, including the dashboard's "Last fetched" line. `getRadarCatalog().meta` and `GET /api/radar/status` now report both dates, the latter as its own field rather than folded into `version` — and omitted entirely for the offers and intel caches, which keep no build date, where a `null` would read as "unknown" rather than "never stored". The dashboard still shows only the fetch time; surfacing the build date there needs a new translated label and is left to a follow-up. Rows cached before migration 163 read back as `null`: unknown stays unknown instead of borrowing the fetch time. The referrals cache has persisted the same date since migration 142. — thanks @maxmad64bis +- **fix(cli):** postinstall no longer fills `JWT_SECRET` and `API_KEY_SECRET` in the installed package's `.env` (#11436). `.env.example` ships both blank on purpose: the server restores them from its durable store, or generates and persists them there, in `ensureSecrets()`. Pre-filling them defeated that — the file lives inside the package directory, so `npm i -g` replaced it and postinstall wrote _different_ values, while `ensureSecrets()` (which only acts on an empty variable) never got to restore the real ones. Both secrets rotated silently on every update, invalidating dashboard sessions and API-key CRCs. `STORAGE_ENCRYPTION_KEY` left the same list for the same reason in #1622; its comment pointed at a function that no longer exists and now names the real provisioning path. — thanks @maxmad64bis +- **fix(cli):** the CLI now says when a loaded `.env` lives inside the installed package directory (#11437). It already announces every env file it reads, without distinguishing the ones that survive an update from the one that does not: `npm i -g` replaces the package directory wholesale, so values set there are gone at the next update, silently. The warning names the durable path to move them to, and fires only when that file actually supplied a value — a file entirely shadowed by a durable one supplied nothing. A development checkout stays silent: there the same path is stable and documented in `SETUP_GUIDE.md`. — thanks @maxmad64bis +- **fix(free-models):** the shared `isFreeModel()` predicate no longer reports catalog entries marked `freeType: "discontinued"` as free, so `hidePaidModels` can't route to Pollinations' seven premium models that now require a paid key ([#11441](https://github.com/diegosouzapw/OmniRoute/pull/11441)) — thanks @maxmad64bis +- fix(ui): let AnySearch use the normal provider-icon fallback when LobeHub has no matching icon (#11449) +- Fixed the v3.8.50 Costs and Analytics dashboards so flat-rate Claude Code usage can be shown as an explicitly requested token-price estimate without changing default billed-cost semantics. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed archived usage retention so each request is priced individually instead of pricing a day's summed tokens once, which understated archived cost whenever a day mixed cache-heavy and ordinary requests. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed the Costs dashboard so it discloses when displayed figures include flat-rate token-price estimates instead of labelling them as billed spend, using the flag the analytics API already returns; the month-end projection and the CSV/JSON exports carry the same marker, and billed-cost mode is unchanged. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- **fix(combo):** attach the same combo-diagnostics trace (`poolSize`/`attemptOrder`/`excluded`/`terminalReason`, plus `x-omniroute-combo-*` headers) to the round-robin strategy's and the nested pipeline/fusion runtime-unit loop's "Maximum combo retry limit reached" 503 that the priority-strategy path already attaches for the identical terminal condition — previously those two paths returned a bare, contextless 503 ([#11462](https://github.com/diegosouzapw/OmniRoute/issues/11462)). ([#11665](https://github.com/diegosouzapw/OmniRoute/pull/11665)) +- **fix(bun):** use native `bun:sqlite` in `bootstrap-env` and `sync-env` to avoid loading `better-sqlite3` N-API addon during Bun startup ([#11468](https://github.com/diegosouzapw/OmniRoute/pull/11468)) — thanks @TheDemonTuan +- **fix(docker-bun):** make `Dockerfile.bun` install reliable with pre-install script helpers and native `bun:sqlite` smoke check ([#11470](https://github.com/diegosouzapw/OmniRoute/pull/11470)) — thanks @TheDemonTuan +- **fix(docker-bun):** remove every vendored `better-sqlite3` native addon from the Bun runtime image so startup cannot abort before the `bun:sqlite` fallback ([#11482](https://github.com/diegosouzapw/OmniRoute/pull/11482)) — thanks @TheDemonTuan +- **fix(autoCombo):** effort/alias model variants (`gpt-5.6-sol-xhigh`, `gpt-5.6`, cursor's `claude-4.6-opus-high`) inherit their base model's task fitness instead of falling to the wildcard 0.5 ([#11492](https://github.com/diegosouzapw/OmniRoute/pull/11492)) — thanks @MumuTW +- fix(db): rate-limit repeated Arena ELO leaderboard fetch-failure warnings instead of logging one per sync attempt (#11500) ([#11989](https://github.com/diegosouzapw/OmniRoute/pull/11989)) — thanks @hartmark +- **fix(db):** dedupe the raw `[Encryption] Decryption failed...` log line emitted by the lazy-decrypt views (`createLazyRowProxy`/`createLazyConnectionView`), which power `getProviderConnections()` and were re-triggering that line on every CredentialHealth/model-sync cycle for the same corrupt or stale-key credential — a fresh Proxy over a fresh row on every cycle meant the per-proxy memoization never suppressed it, unlike the dedup `decryptConnectionFields()` already had since [#9927](https://github.com/diegosouzapw/OmniRoute/issues/9927). Now shares that dedupe tracking so the line logs at most once per credential ([#11500](https://github.com/diegosouzapw/OmniRoute/issues/11500)). ([#11678](https://github.com/diegosouzapw/OmniRoute/pull/11678)) +- **fix(intelligence):** Arena ELO sync stores a synthesized base-model row for every effort/harness variant (`claude-opus-5-max` → `claude-opus-5`, `gpt-5.6-sol-xhigh (codex-harness)` → `gpt-5.6-sol`), so bare model requests reach the synced intelligence layer instead of the static fallback table; the hand-maintained `MODEL_ALIAS_MAP` — which collapsed generations (`gpt-5.5` → `gpt-5`) — is gone ([#11506](https://github.com/diegosouzapw/OmniRoute/pull/11506)) — thanks @MumuTW +- **fix(autoCombo,sse):** catalog hygiene — the static task-fitness table no longer ranks retired models above live ones (dead rows dropped, patterns matched on segment boundaries), the 7 deprecation aliases that forwarded to retired or non-existent models now point at the vendors' published replacements, an alias is no longer rewritten when the serving provider offers the id as-is, and a new `check:model-lifecycle` gate diffs both tables against a vendor lifecycle snapshot ([#11507](https://github.com/diegosouzapw/OmniRoute/pull/11507)) — thanks @MumuTW +- **fix(sse):** stop dropping resolved `thoughtSignature` values on parallel (multi tool-call) turns sent to Gemini 3.x — the claude→gemini and openai→gemini translators previously kept the signature only on the *first* function call of a message, causing Gemini to reject subsequent calls in the same turn with HTTP 400 "Function call is missing a thought_signature"; each function call now keeps its own resolved signature ([#11510](https://github.com/diegosouzapw/OmniRoute/issues/11510)). ([#11669](https://github.com/diegosouzapw/OmniRoute/pull/11669)) +- Treat Docker publishing from a non-default release branch as an expected skip instead of a failed workflow, while preserving the guard that prevents it from overwriting the `next` channel. ([#11527](https://github.com/diegosouzapw/OmniRoute/pull/11527)) — thanks @pacocartones +- fix(search): prefer credentialed providers over duckduckgo-free fallback (#11524) — the fallback-only loop ran before the credentialed-providers loop in `executeWebSearch`, making configured providers unreachable when `duckduckgo-free` was available. ([#11565](https://github.com/diegosouzapw/OmniRoute/pull/11565)) — thanks @vsd2807 +- **fix(sse):** cap the upstream headers-wait phase for STREAMING requests to a client-realistic ceiling (110s, under Codex's own ~120s hard client-abort window) instead of the flat 10-minute `FETCH_TIMEOUT_MS` default — that default was 5x longer than the body-phase readiness watchdog's own adaptive bound, so a request whose upstream never returned any response at all (not even headers, e.g. a stalled NVIDIA target behind a tool-heavy Responses→Chat translation) kept the client connection alive on keepalives only, guaranteeing the client's own patience ran out first with an opaque 499 instead of OmniRoute detecting and failing the stall fast. Non-streaming requests are unaffected — they keep the existing flat default (`open-sse/utils/fetchStartTimeoutPolicy.ts`) (#11526) ([#11668](https://github.com/diegosouzapw/OmniRoute/pull/11668)) +- **fix(ci):** protocol-clients E2E harness boots the peer-stamped custom server (`run-next.mjs dev`) instead of the bare `next` CLI, so `LOCAL_ONLY` locality resolves from the real TCP peer stamp and `/api/mcp/audit` answers 200 (open bootstrap preserved via an `OMNIROUTE_E2E_BOOTSTRAP_MODE=open` credential clear in `run-next.mjs`, pinned to loopback `HOST`) instead of a deterministic 403 (#11535) ([#11549](https://github.com/diegosouzapw/OmniRoute/pull/11549)) — thanks @jonlwheat2-gif +- **fix(docs):** the Auto-Combo engine is described with the number of factors it actually declares. Four different counts were in circulation — 6, 9, 13 and 14 across `AGENTS.md`, four guides and six code comments — while `DEFAULT_WEIGHTS` declares 15. `check:docs-counts` now reads that number from the source, so adding a factor without updating the prose fails CI ([#11545](https://github.com/diegosouzapw/OmniRoute/pull/11545)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **fix(quota-share):** in-flight leases are per request, so an aborted request's slot ages out instead of being kept alive by later traffic on the same connection ([#11547](https://github.com/diegosouzapw/OmniRoute/pull/11547)) — thanks @abhisheksharma2411 +- The free-tier summary route now serves the refreshed Radar catalog when the feed is active — the same numbers the dashboard shows — and states which catalog answered plus its real build date, instead of always reporting release-frozen figures with a stale curation date. The supporter-key live feed stays reserved to authenticated callers of the instance. ([#11550](https://github.com/diegosouzapw/OmniRoute/pull/11550)) — thanks @maxmad64bis +- **sse:** fix LiveWS/embed-WS servers crashing at startup under the Node/tsx runtime — `liveServer.ts`, `embedWsProxy.ts` and `apiBridgeServer.ts` imported `@/shared/utils/httpClientAbortGuard` without the `.mjs` extension, so the client-abort crash guard added by [#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556) was unreachable and every dependent test failed with `ERR_MODULE_NOT_FOUND` ([#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556)). ([#11671](https://github.com/diegosouzapw/OmniRoute/pull/11671)) — thanks @jonlwheat2-gif / @yxyxy +- fix(chat): shed any of the matched provider-node's routing identifiers (public prefix or internal id) before model lookup, so composites like `//` no longer reach upstream verbatim (#11557) — thanks @maxmad64bis +- **fix(cache):** a semantic-cache entry now expires on its TTL instead of surviving until the next UTC midnight, and `dbEntries` stops counting expired rows ([#11573](https://github.com/diegosouzapw/OmniRoute/pull/11573)) — thanks @ntdatt812 +- **fix(api):** `/v1/models` schedules its stale-while-revalidate rebuild through Next's `after()` again, so a stale catalog reaches the client before the rebuild blocks the event loop ([#11574](https://github.com/diegosouzapw/OmniRoute/pull/11574)) — thanks @ntdatt812 +- **fix(check):** the `known-symbols` executor conformance gate awaits the now-async `getExecutor()`, so it stops reporting all 142 registered aliases as dead symbols and can detect a lazy import that fails ([#11582](https://github.com/diegosouzapw/OmniRoute/pull/11582)) — thanks @ntdatt812 +- **fix(pack):** register `bin/cli/utils/volatileEnvPath.mjs` in `PACK_ARTIFACT_REQUIRED_PATHS`, so the prune can no longer silently drop a module the published CLI imports on every boot ([#11588](https://github.com/diegosouzapw/OmniRoute/pull/11588)) — thanks @ntdatt812 +- **i18n(vi):** translate the five reliability-column strings on the free-provider rankings page, which rendered as `__MISSING__:` markers in the Vietnamese UI ([#11589](https://github.com/diegosouzapw/OmniRoute/pull/11589)) — thanks @ntdatt812 +- **resilience:** restore the expired-connection retry-budget probe in the token-health sweep — the `!isGitHubAccessTokenOnlyConnection` carve-out reintroduced by #11608 contradicted the boundary pinned by #11592, so a GitHub connection parked at `expired` with retry budget remaining was never probed and could never self-heal ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)). ([#11672](https://github.com/diegosouzapw/OmniRoute/pull/11672)) — thanks @jonlwheat2-gif / @ntdatt812 +- **fix(dashboard):** Provider Stats now displays combo success rates on their native 0–100 percentage scale instead of multiplying them twice ([#11595](https://github.com/diegosouzapw/OmniRoute/pull/11595)) — thanks @pacocartones +- **fix(providers):** route DuckDuckGo Web Chat through the verified `duck.ai` endpoint, refresh its free model catalog, and avoid forwarding unsolved anti-abuse challenges ([#11597](https://github.com/diegosouzapw/OmniRoute/pull/11597)) — thanks @solstxce +- **fix(ui):** Console log Refresh and Copy controls now expose localized accessible names, keep copy actions visible on keyboard focus, and announce copy completion safely ([#11599](https://github.com/diegosouzapw/OmniRoute/pull/11599)) — thanks @pacocartones +- Dropped the stale-`.eslintcache` `restore-keys` fallback from both "Restore ESLint file cache" steps in `ci.yml`, so the blocking `Lint` job can no longer be served per-file verdicts computed under a different lint config, suppressions file or lockfile. `quality.yml` had already dropped it in #11963; `ci.yml` — the workflow that actually gates PRs — had not (#11600). ([#11996](https://github.com/diegosouzapw/OmniRoute/pull/11996)) +- fix(ci): pass `--pass-on-unpruned-suppressions` in `run-eslint-json.mjs` so the CI Lint job no longer fails when a suppression is merely orphaned by a genuine fix, mirroring the identical fix already in `validate-release-green.mjs` (#11600) ([#11983](https://github.com/diegosouzapw/OmniRoute/pull/11983)) — thanks @hartmark +- fix(handoff): enforce provider allowlist for universal handoff (#11602) — universal handoff now skips summarization when the selected summary model's provider is not included in the configured provider allowlist. ([#11619](https://github.com/diegosouzapw/OmniRoute/pull/11619)) — thanks @vsd2807 +- **fix(dashboard):** Show a stable error state when Search Analytics returns an HTTP error or malformed data ([#11603](https://github.com/diegosouzapw/OmniRoute/pull/11603)) — thanks @pacocartones +- **test(gamification):** pin the aggregate profile level to the XP-derived semantics of #11604 — `getAggregateXp()` now derives `currentLevel` from the summed XP (`calculateLevel(sum)`), not `MAX(stored current_level)`, and the #3484 fixture levels are aligned with the XP curve ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)). ([#11676](https://github.com/diegosouzapw/OmniRoute/pull/11676)) — thanks @jonlwheat2-gif / @pacocartones +- **fix(dashboard):** Keep the Profile level and progress aligned with aggregate XP, including bounded handling for invalid totals ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)) — thanks @pacocartones +- **fix(dashboard):** Prevent locked hidden badges from revealing their icon or opening private badge details before they are earned ([#11605](https://github.com/diegosouzapw/OmniRoute/pull/11605)) — thanks @pacocartones +- **fix(dashboard):** Restore keyboard focus after shared modals close and cancel delayed autofocus during cleanup ([#11607](https://github.com/diegosouzapw/OmniRoute/pull/11607)) — thanks @pacocartones +- **fix(dashboard):** Enable Enter and Space activation for clickable data-table rows without hijacking nested controls ([#11610](https://github.com/diegosouzapw/OmniRoute/pull/11610)) — thanks @pacocartones +- **fix(autoCombo,sse):** vendor-retired catalog ids are dropped from the auto-combo candidate pool and no longer win on leftover `arena_elo` / `user_override` rows; `getModelLifecycleDecision` consults `model-lifecycle.json` (prefix-stripped) so aggregator traffic is not `untracked` for ids the snapshot already knows ([#11625](https://github.com/diegosouzapw/OmniRoute/issues/11625)) ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)) — thanks @MumuTW +- **test(chatcore):** move Codex/Claude combo fixtures off models the lifecycle guard now rejects — `gpt-5.1-codex`/`gpt-5-codex` are vendor-retired (snapshot, #11626) and `claude-3-5-sonnet-20241022` is shut down, so native-passthrough and combo-fallback tests switched to `gpt-5.6-sol` and `claude-sonnet-4.6` ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)). ([#11675](https://github.com/diegosouzapw/OmniRoute/pull/11675)) — thanks @jonlwheat2-gif / @MumuTW +- fix(opencode-plugin): stop a free-tier budget that rounds up across a magnitude from rendering as `1000K`/`1000M` in the model picker — `fmtTokens` chose its unit from the raw token count and then rounded with `toFixed(1)`, so 999,950–999,999 printed as `1000K` rather than `1M` and just under a billion printed as `1000M` rather than `1B` ([#11684](https://github.com/diegosouzapw/OmniRoute/pull/11684)) — thanks @abhisheksharma2411 +- Cap prose-derived Antigravity quota resets at the configured model cooldown maximum while preserving authoritative Retry-After headers and Google RetryInfo hints. (#11695) Thanks @pacocartones. ([#11823](https://github.com/diegosouzapw/OmniRoute/pull/11823)) +- Compare `_rebaseline_*` notes structurally in the ratchet-bank verifier so an object-valued note no longer reports itself as rewritten on an unmodified tree — the nightly `bank-ratchet-shrinks` lane had been aborting on every run since 2026-08-11, leaving 41,672 lines of earned file-size shrinks unbanked (#11702). ([#11892](https://github.com/diegosouzapw/OmniRoute/pull/11892)) — thanks @MumuTW +- **fix(build):** `prepublish.ts` bundles the ChatGPT Web (Codex) MCP bridge through `runBuildTool()` instead of spawning `npx.cmd` raw, fixing the build crash on Node ≥ 20/Windows where `.cmd` shims cannot be spawned without a shell (EINVAL) ([#11704](https://github.com/diegosouzapw/OmniRoute/issues/11704)) ([#11706](https://github.com/diegosouzapw/OmniRoute/pull/11706)) — thanks @jonlwheat2-gif +- fix(codex): keep `parallel_tool_calls:false` on the translated Codex Responses Lite path (#11707) ([#11984](https://github.com/diegosouzapw/OmniRoute/pull/11984)) — thanks @hartmark +- **fix(tests):** the new Gemini Web Images retirement test (#11708) called the now-async `getExecutor()` synchronously, throwing `TypeError: getExecutor(...).getProvider is not a function` instead of asserting Gemini Web chat stays available. Awaited to match the R0.3 executor-registry refactor (#11220). ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(search):** Allow REST and MCP web-fetch requests to select the advertised `anysearch-search` provider explicitly ([#11716](https://github.com/diegosouzapw/OmniRoute/pull/11716)) — thanks @pacocartones +- **fix(providers):** Keep overnight peak-hour protection active after midnight according to the window's configured start day ([#11718](https://github.com/diegosouzapw/OmniRoute/pull/11718)) — thanks @pacocartones +- **fix(providers):** correct all five g4f.space gateway descriptions to document proof-of-work credits or member API keys, variable limits, and the remote prompt boundary instead of promising free keyless access at five requests per minute ([#11738](https://github.com/diegosouzapw/OmniRoute/pull/11738)). +- **fix(db):** `model_capabilities` is created by a migration instead of lazily on the first models.dev sync, so a clean install and an upgraded install converge on the same schema regardless of which features have run ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(ci):** `check:install-upgrade` now fails on an `npm` install truncated by ENOSPC (npm reports it as a warning and still exits 0), authenticates its health probe so the version assertion works against the hardened health payload, frees the clean-install tree before the upgrade phase, and no longer reports a schema divergence computed from a boot that never served ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(routing):** harden session-affinity key derivation against oversized explicit IDs and empty structural-payload collisions, while recognizing Gemini and common text request shapes ([#11746](https://github.com/diegosouzapw/OmniRoute/pull/11746)) — thanks @MumuTW +- **fix(packages/browser-pool):** regenerate `package-lock.json` so the `packages/browser-pool` workspace's locked `playwright`/`@types/node` (and transitives) match its `package.json` specs, fixing cache-only/offline installs (`npm ci --offline`, Nix `buildNpmPackage`) that previously failed with `ENOTCACHED` ([#11747](https://github.com/diegosouzapw/OmniRoute/issues/11747)) — thanks @benjaminkitt ([#11784](https://github.com/diegosouzapw/OmniRoute/pull/11784)) — thanks @geekyNads +- **fix(assets):** Stop distributing 79 provider and CLI logos without verified source/license provenance, keep every provider/tool available through the internal generic icon, and prevent those IDs from falling through to an external icon CDN. ([#11750](https://github.com/diegosouzapw/OmniRoute/pull/11750)) +- **fix(providers):** retire the common `chatgpt-web` / `cgpt-web` provider because its pre-key and proof-of-work provenance could not be cleared ([#11754](https://github.com/diegosouzapw/OmniRoute/pull/11754)) — explicit chat and image requests, slash-prefixed models, persisted aliases, proxy overrides, and combo targets now fail closed with a sanitized HTTP `410` / `PROVIDER_RETIRED` before upstream dispatch. Migration 163 tombstones matching connections and invalidates their active leases without deleting history or widening API-key allowlists. The independent `chatgpt-web-codex` / `cgpt-codex` provider and its MIT-noticed vendored implementation remain available. +- **fix(usage):** quota and usage refresh no longer 409 when an exclusive lease reserves the connection ([#11758](https://github.com/diegosouzapw/OmniRoute/pull/11758)) — thanks @TheDemonTuan +- Keep the embedding registry's vector width and `embedding` type on models when a synced model exists for the same id, so `/v1/models` no longer reports registry-described embedding models widthless or untyped (#11761) — thanks @rahilmavani +- Correct `google/gemini-embedding-001` on the OpenRouter route to 3072 dimensions, the width it returns when `dimensions` is not sent (#11761) — thanks @rahilmavani +- **fix(vision-bridge):** Restrict automatic vision routing and fallbacks to models available in authoritative live provider catalogs, including aliases and effort variants ([#11767](https://github.com/diegosouzapw/OmniRoute/issues/11767)) — thanks @pacocartones ([#11813](https://github.com/diegosouzapw/OmniRoute/pull/11813)) +- **fix(providers):** reclassify Cerebras as a one-time $5 signup credit (payment method required, 30-day validity), not a recurring no-card 1M tokens/day trial ([#11773](https://github.com/diegosouzapw/OmniRoute/issues/11773)) ([#12591](https://github.com/diegosouzapw/OmniRoute/pull/12591)) — thanks @HouMinXi +- fix(cli): stop prepublish from re-rebuilding the already-built ESM-only opencode-plugin dist (#11787) ([#11990](https://github.com/diegosouzapw/OmniRoute/pull/11990)) — thanks @hartmark +- **fix(db):** Fresh installs no longer log a non-fatal `no such table: compression_run_telemetry` warning when retention cleanup runs before the lazily-created telemetry table exists ([#11802](https://github.com/diegosouzapw/OmniRoute/pull/11802)) — thanks @RaviTharuma +- fix(sse): set X-OmniRoute-Selected-Connection-Id on successful combo dispatches so downstream consumers stop falling back to an empty connection id (#11810) ([#11986](https://github.com/diegosouzapw/OmniRoute/pull/11986)) — thanks @hartmark +- **fix(services):** embedded CLIProxyAPI lifecycle checks now use public `/healthz`, while model discovery uses the configured dedicated data-plane API key instead of the management password ([#11811](https://github.com/diegosouzapw/OmniRoute/pull/11811)) — thanks @RaviTharuma +- Fix persisted model steps returning an error in the playground route simulator, and warn when structural steps cannot be simulated. ([#11862](https://github.com/diegosouzapw/OmniRoute/pull/11862)) — thanks @pacocartones +- **fix(providers):** Antigravity's dynamic mitmAlias table no longer routes `gemini-3.7-flash-{high,medium,low}` to a literal tier-suffixed upstream id just because one connected account's own discovery listed it directly — those display ids always resolve through the safe `gemini-3.7-flash-tiered` static alias, so one account's Google-provisioned access no longer 404s every sibling account of the provider ([#11824](https://github.com/diegosouzapw/OmniRoute/issues/11824), [#11651](https://github.com/diegosouzapw/OmniRoute/issues/11651)) ([#11988](https://github.com/diegosouzapw/OmniRoute/pull/11988)) — thanks @hartmark +- **fix(dashboard):** Expose Profile loading, errors, page structure, and XP progress to assistive technologies ([#11838](https://github.com/diegosouzapw/OmniRoute/pull/11838)) — thanks @pacocartones +- **fix(socks):** `Agent.connectTimeout` now bounds both SOCKS handshake and TLS connect, and `family === null` no longer falls back to `fetch-socks` ([#11842](https://github.com/diegosouzapw/OmniRoute/pull/11842)) — thanks @maxmad64bis +- **perf(compression):** OOM mitigations for large payload hashing, memoization, and token estimation ([#11844](https://github.com/diegosouzapw/OmniRoute/pull/11844) — thanks @AndrianBalanescu) +- Excluded Next.js Node File Trace manifests (`*.nft.json`) from the published npm tarball. They are build-time metadata and are never read while serving, but had grown to 668.7 MB — 61% of the package — which pushed the upload past the registry limit and made `npm publish` fail with `413 Payload Too Large`. ([#11864](https://github.com/diegosouzapw/OmniRoute/pull/11864)) +- **fix(cli):** keep the global OmniRoute server `--base-url` separate from provider-node `--endpoint` values in add, update, and validate commands ([#11860](https://github.com/diegosouzapw/OmniRoute/pull/11860)) — thanks @pacocartones +- **fix(config):** Nous Research's `Hermes-4-405B` model now displays as "Hermes 4 405B (Nous Research)" in both the provider registry and the free-model catalog, instead of the mislabelled "Hermes 4 7B" ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#11993](https://github.com/diegosouzapw/OmniRoute/pull/11993)) +- **fix(provider/nous):** inject required user= tag into Nous Research inference requests to resolve upstream 400 "missing tags" error ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#12044](https://github.com/diegosouzapw/OmniRoute/pull/12044)) +- **fix(build):** `npm run build` now fails in one second with a named package and a copy-pasteable fix when npm silently drops an externalised optional native dependency, instead of dying four minutes in with `Module not found: Can't resolve 'better-sqlite3'` ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- **fix(install):** `postinstall` no longer throws `ReferenceError: isAndroid is not defined` — failing the whole `npm install` — when the `better-sqlite3` rebuild fallback times out; the manual-fix guidance is reachable again ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- Fixed the Alibaba free-tier allowlist test that went red on its own once the shipped catalog's `validUntil` (2026-08-27) passed, leaving every PR and `main` with a failing `Unit Tests (1/8)`. The test now builds its own packs with dates it controls, and covers the expired-pack fallback that production has actually been serving. ([#11867](https://github.com/diegosouzapw/OmniRoute/pull/11867)) +- Split the npm registry upload into its own GitHub-hosted job. npm refuses `--provenance` from a self-hosted runner (`422 ... Only "github-hosted" runners are supported`), which blocked the v3.8.50 publish; the heavy verification cannot move to a hosted runner, so it now hands the proven tarball over instead. ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(ui):** the shared `DataTable` loading state no longer reads its decorative ⏳ glyph out to assistive technology, and now carries the same `role="status"` / `aria-live="polite"` / `aria-busy="true"` semantics as `PageLoading` ([#11869](https://github.com/diegosouzapw/OmniRoute/pull/11869)) — thanks @pacocartones +- **fix(api):** Bound the `timeout` accepted by `POST /api/providers/[id]/login` to the same 15s–600s window the Adobe Firefly and Conol browser-login paths already enforce, so a request can no longer pin a headful browser and the single active-login slot with an arbitrarily large poll budget. ([#11871](https://github.com/diegosouzapw/OmniRoute/pull/11871)) — thanks @pacocartones +- **fix(api):** Stop the tunnel and MITM routes from returning raw child-process error text, which disclosed host paths, binary install locations and Tailscale `tskey-*` credentials in a body some of these routes serve to non-loopback callers, and return a real 400 instead of a framework 500 when a tunnel request body fails validation. ([#11872](https://github.com/diegosouzapw/OmniRoute/pull/11872)) — thanks @pacocartones +- **fix(open-sse):** stream TTFT/ITL are sampled from a monotonic clock (`performance.now`) instead of `Date.now`, so an NTP correction or wall-clock jump can no longer inflate a genuine TTFT or produce a negative one that the `ttft >= 0` guard silently drops — protecting the router's OTel/EWMA/`usage_history`/speed-ranking signals ([#11873](https://github.com/diegosouzapw/OmniRoute/pull/11873)) — thanks @pacocartones +- **fix(providers):** xAI `reasoning_effort: "xhigh"` now reaches grok-4.6+ instead of being silently clamped to `"high"` ([#11879](https://github.com/diegosouzapw/OmniRoute/pull/11879)) — thanks @NoxzRCW +- **fix(dashboard):** the providers page no longer crashes into the error boundary when a provider id collides with an `Object.prototype` member (`constructor`, `__proto__`); icon lookups are own-property guarded ([#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)) — thanks @NoxzRCW +- **fix(skills):** injected skill tools declared in shorthand (`{"content": "string"}`) now forward valid JSON Schema, unblocking providers that validate tool schemas strictly such as Zhipu GLM on the Console Go tier ([#11881](https://github.com/diegosouzapw/OmniRoute/pull/11881)) — thanks @NoxzRCW +- **fix(api):** `POST /api/playground/simulate-route` now surfaces `combo-ref` and `provider-wildcard` persisted combo steps with a specific warning (naming the referenced combo, or the unresolved `provider/model` wildcard) instead of folding them into a generic "unsupported step" count; a `provider-wildcard` step is also now included as an unresolved target so the operator can see it is in the route (ported from [#11882](https://github.com/diegosouzapw/OmniRoute/pull/11882) — thanks @NoxzRCW). ([#11930](https://github.com/diegosouzapw/OmniRoute/pull/11930)) +- **fix(translator):** the streaming OpenAI→Claude translator keeps upstream usage, including prompt-cache tokens, when it arrives on a trailing `choices: []` chunk (Fireworks and any upstream using `stream_options.include_usage`) ([#11883](https://github.com/diegosouzapw/OmniRoute/pull/11883)) — thanks @NoxzRCW +- **fix(cli):** `omniroute update --apply` now tells you explicitly whether a running server was detected and, if so, that you must run `omniroute restart` to apply the update — it never restarted anything and previously implied the update was already live once files were installed. The dashboard's npm-mode Update flow (`/api/system/version`) now tries OmniRoute's own PID-file-managed supervisor before falling back to pm2, and reports an honest "restart required" step instead of a silent pm2-only "skipped" that read like a completed update. The server-side latest-version lookup backing the dashboard's update banner also gained `--prefer-online`, closing the same stale-npm-cache class already fixed in the CLI's own copy for #4376 ([#11885](https://github.com/diegosouzapw/OmniRoute/issues/11885)). ([#12005](https://github.com/diegosouzapw/OmniRoute/pull/12005)) +- **fix(resilience):** clear persisted LKGP pins when a target suffers connection/provider exhaustion or is skipped before dispatch due to cooldown/exhaustion/unavailability, preventing subsequent requests from repeatedly prioritizing known-dead providers ([#11911](https://github.com/diegosouzapw/OmniRoute/issues/11911)). ([#12013](https://github.com/diegosouzapw/OmniRoute/pull/12013)) — thanks @HouMinXi +- **fix(sse):** the OpenAI→Claude stream translator now defers the terminal `message_delta`/`message_stop` emission until the real usage block has arrived (or a genuine end-of-stream flush forces it) instead of emitting it immediately on `finish_reason` — previously, when the trailing usage-only chunk (`{"choices":[],"usage":{...}}`) arrived *after* the `finish_reason` chunk (the normal order for Fireworks/vLLM/Together and other `stream_options.include_usage` upstreams), the client-visible `message_delta` still carried stale/zero usage even though `state.usage` was internally corrected too late to matter (ported from [#11915](https://github.com/diegosouzapw/OmniRoute/pull/11915) — thanks @HouMinXi). ([#11933](https://github.com/diegosouzapw/OmniRoute/pull/11933)) +- **fix(sse):** `stripStore()` now forces `store=false` for stateless OpenAI-compatible Responses-API targets (unless the connection has explicitly opted in via `providerSpecificData.openaiStoreEnabled`), instead of only handling the `openai`/`agentrouter` cases — a client-supplied `store` value previously passed through untouched to backends that don't actually persist responses server-side ([#11916](https://github.com/diegosouzapw/OmniRoute/pull/11916) — thanks @HouMinXi). +- **fix(models):** custom provider-node models (synced, custom, and alias-backed) now appear under their configured prefix in the unified catalog when the operator's model-id prefix mode is canonical, instead of being dropped whenever alias-inclusion was otherwise disabled ([#11918](https://github.com/diegosouzapw/OmniRoute/pull/11918) — thanks @HouMinXi). +- **fix(models):** the unified model catalog now suppresses stale static registry models (including effort-tier variants) for any provider whose active connection has an authoritative live synced catalog, not only providers already using exclusive-synced-listing — a connection with `providerUsesAuthoritativeLiveCatalog` previously kept serving both the live-synced models and the stale static rows side by side ([#11919](https://github.com/diegosouzapw/OmniRoute/pull/11919) — thanks @HouMinXi). +- **fix(ui):** `ProviderIcon`'s three remaining unguarded lookups (`PROVIDER_ICON_ALIASES`, `LOCAL_SVG_ALIASES`, `THEMED_SVGS`) now use `Object.hasOwn()` own-property checks — a provider id such as `constructor` or `__proto__` previously resolved through the prototype chain instead of falling through to the unknown-provider CDN fallback (`getLobeProviderIcon()` itself was already guarded by [#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)); `ProviderPageHeader`'s `color` field is also now optional, matching the rest of the component's defensive typing (ported from [#11920](https://github.com/diegosouzapw/OmniRoute/pull/11920) — thanks @HouMinXi). ([#11935](https://github.com/diegosouzapw/OmniRoute/pull/11935)) — thanks @NoxzRCW +- fix(ollama): preserve multi-byte UTF-8 content split across stream chunks in the Ollama NDJSON transform, which previously corrupted CJK/emoji into U+FFFD (#11921) — thanks @pacocartones +- **fix(providers):** OrcaRouter chat requests now target `/v1/chat/completions` instead of the bare `/v1` API root, fixing the upstream `404 Invalid URL (POST /v1)` ([#11923](https://github.com/diegosouzapw/OmniRoute/pull/11923)). — thanks @echel0nn +- **fix(plugins):** deliver the `onStreamComplete` event to disk-installed plugins. The event shipped in v3.8.50 (#9669) was emitted internally but had no plugin-facing wiring, so no plugin could ever subscribe: the manifest schema silently dropped `hooks.onStreamComplete`, and the loader/manager only knew the seven legacy hooks. `onStreamComplete` is now a declarable manifest hook, wired through the loader and registered by the manager like the other hooks, and its payload carries a `requestId` so consumers can correlate the stream-completion event with the originating request ([#11934](https://github.com/diegosouzapw/OmniRoute/pull/11934)) — thanks @amaleta / @pacocartones +- **fix(db):** the Qdrant embedding-model dropdown now lists local/self-hosted providers (Ollama, LM Studio, vLLM, etc.) — an active connection is treated as "configured" when the provider allows an optional API key, not only when it has a real key or OAuth, so a running local embedding provider is no longer hidden from the picker ([#11949](https://github.com/diegosouzapw/OmniRoute/issues/11949)) ([#11995](https://github.com/diegosouzapw/OmniRoute/pull/11995)) +- **fix(providers):** Vertex AI Anthropic partner-model discovery now calls the Model Garden `v1beta1` publisher list (`/v1beta1/publishers/anthropic/models`, global) and parses its `publisherModels` envelope, so Claude models auto-synced from Vertex populate the active live catalog and route at request time instead of returning `Model '' is not available in the active live catalog` ([#11991](https://github.com/diegosouzapw/OmniRoute/issues/11991)) — thanks @fabioluissilva ([#11998](https://github.com/diegosouzapw/OmniRoute/pull/11998)) +- **fix(cli):** support `--base-url` alongside `--endpoint` in `omniroute nodes add`, `update`, and `validate` subcommands to prevent global `--base-url` shadowing issues ([#11999](https://github.com/diegosouzapw/OmniRoute/issues/11999)). ([#12033](https://github.com/diegosouzapw/OmniRoute/pull/12033)) — thanks @HouMinXi +- **fix(providers):** `cloudflare-ai` no longer refuses image content parts for every Workers AI model ([#12002](https://github.com/diegosouzapw/OmniRoute/pull/12002)) — the plain-string `content` requirement behind #2539 is carried by the _model_ schema, not by the `/ai/v1/chat/completions` endpoint (measured: an all-text part array returns 200 on `@cf/mistralai/mistral-small-3.1-24b-instruct`, `@cf/meta/llama-4-scout-17b-16e-instruct` and `@cf/meta/llama-3.3-70b-instruct-fp8-fast`, and 400 on the text-only `@cf/qwen/qwen2.5-coder-32b-instruct`). `transformRequest()` flattened every array and threw on the first non-text part (#6390), so image input was refused for vision-capable Cloudflare models that accept it. All-text arrays are still flattened — the one shape every model accepts — while an array carrying a non-text part is passed through untouched, so the attachment is still never silently dropped. Regression guards: `tests/unit/cloudflare-ai-image-parts-6390.test.ts`. — thanks @davidlinfr +- **fix(resilience):** decouple the limiter-managed execution backstop from the queue-wait budget — new `requestQueue.executionMaxWaitMs` (env `RATE_LIMIT_EXECUTION_MAX_WAIT_MS`, default 600000 = 10 min) now feeds Bottleneck's post-dispatch `expiration`, while `requestQueue.maxWaitMs` keeps its documented queue-wait semantics. Previously the queue-wait budget doubled as the execution expiration, so legitimate long-running calls on non-incremental gateways (whole generation buffered before the first upstream byte, e.g. Console Go / Command Code tiers serving GLM models) were killed mid-flight at the queue budget with a false 504 `RATE_LIMIT_EXECUTION_TIMEOUT` — the local limiter undercut the provider-aware upstream fetch-start timeouts. The surfaced 504 message now names `requestQueue.executionMaxWaitMs`; the error keeps the #4165 guarantees (disclaims an upstream timeout, preserves the Bottleneck error as `cause`, branded code + trusted provenance, classified request-scoped so combo falls back). A real queue-wait bound (the `Promise.race` around `limiter.schedule()` sketched in #9533) remains future work. (#12025) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **Call logs:** keep the `error` field when an artifact exceeds the storage cap, instead of replacing it with the omission marker. The error is the only field that says *why* a request failed and is typically ~90 bytes next to the multi-hundred-KB bodies that trip the cap, so dropping it left a size-limited row undiagnosable — a provider outage, a local timeout and an upstream 400 all rendered identically. It is now preserved at every fallback stage, truncated to 4KB if it is itself large ([#12026](https://github.com/diegosouzapw/OmniRoute/issues/12026)). ([#12095](https://github.com/diegosouzapw/OmniRoute/pull/12095)) — thanks @ntdat812 +- **fix(diagnostics):** preserve the error field (truncated to 4KB with a `[truncated: …]` suffix) in every call-log artifact size-limit fallback stage. Previously the minimal fallback replaced the error with `[omitted: call log artifact size limit exceeded]`, so an oversized artifact row showed nothing about WHY the request failed — e.g. 91 of 847 opencode-go 504 rows on one production instance were undiagnosable from the dashboard. Oversized request/response bodies are still omitted exactly as before; the error cap is independent of the payload sizes that tripped the fallback. (#12026) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **fix(sse):** OpenAI Responses clients that declare the native `web_search` tool now receive a spec-shaped `web_search_call` output item with `action.sources` alongside the preserved function-call round-trip, so search results executed through OmniRoute's own search backend are consumable by standard Responses clients (Codex, pi-web-access, …). ([#12031](https://github.com/diegosouzapw/OmniRoute/pull/12031)) — thanks @watchingdogs +- **fix(cli):** use in-thread alias resolver hooks on modern runtimes to avoid deprecation noise and improve Node.js forward compatibility ([#12073](https://github.com/diegosouzapw/OmniRoute/issues/12073)). ([#12083](https://github.com/diegosouzapw/OmniRoute/pull/12083)) — thanks @xiaoyaner0201 +- **fix(combo):** an operator-set **Agent Features → Context length** on a combo is now honored at request time. The value was persisted and advertised through `/v1/models`, but `resolveComboContextLimit()` never consulted it — so a multi-target combo whose members carry no per-model window fell through to the provider's generic `defaultContextLength` (openrouter 128000, command-code 200000) and rejected large requests with `Input exceeds context window … limit 128000` despite the combo being explicitly sized much larger. An identical single-target combo worked, because it collapses to its concrete target before the guard runs. Invalid values (0/negative/NaN/Infinity) are ignored, so the existing target → combo-min → fallback order is unchanged. ([#12090](https://github.com/diegosouzapw/OmniRoute/pull/12090)) — thanks @adivekar-utexas +- **fix(sse):** passthrough streams now estimate usage on finish when upstream closes without usage even with `stream_options.include_usage` — avoids `0 tokens / 0%` for providers that stay silent (and correctly handles trailing empty-choices usage) ([#12151](https://github.com/diegosouzapw/OmniRoute/pull/12151)) — thanks @maxmad64bis +- **fix(combos):** clearing an agent feature in the combos editor now persists — unchecking context cache protection, or emptying the system message or tool filter, sends an explicit `null` instead of dropping the field from the `PUT` body, which the update merge read as "leave unchanged" ([#12177](https://github.com/diegosouzapw/OmniRoute/pull/12177)) — thanks @foreveryh / @ntdatt812 +- **fix(memory):** self-hosted embedding endpoints now vectorize — the vector width is measured from the first embedding that comes back instead of being read from a registry that cannot describe them, so `vec_memories` is created and memories stop piling up unvectorized behind a green health check ([#12180](https://github.com/diegosouzapw/OmniRoute/pull/12180)) — thanks @kanade-hoshino / @ntdatt812 +- **fix(translator):** the leading `system` message now reaches Responses-API upstreams when its `content` is a content-part array — it was read as `typeof content === "string" ? content : ""`, so a prompt-caching client (Anthropic `cache_control`, the shape LiteLLM and the Anthropic SDK emit) had its entire system prompt replaced by an empty `instructions`. The request was still accepted with a normal `prompt_tokens` count, so the model answered with no instructions and nothing in the response said they were missing. Mid-conversation system turns already handled the array shape ([#7056](https://github.com/diegosouzapw/OmniRoute/pull/7056)); only the first one did not ([#12206](https://github.com/diegosouzapw/OmniRoute/issues/12206)). Regression guard: `tests/unit/translator-openai-responses-system-content-parts.test.ts`. ([#12207](https://github.com/diegosouzapw/OmniRoute/pull/12207)) — thanks @Gorillaz322 +- **fix(free-tier):** `/api/free-tier/summary` no longer computes its totals from a Radar feed built before the catalog the running release ships. When the cached feed is older — or carries no build date at all — the route answers from the shipped catalog, resolved through the operator's local model state so disabled and tombstoned models stay out of the numbers ([#12215](https://github.com/diegosouzapw/OmniRoute/pull/12215)). — thanks @maxmad64bis +- **fix(oauth):** Keep a Claude personal workspace and a Team organization as separate connections — they share the same email and `accountUUID`, so the email-only OAuth dedup let the second login overwrite the first account's tokens; `organizationUUID` now disambiguates them, the way `workspaceId` does for Codex ([#12222](https://github.com/diegosouzapw/OmniRoute/pull/12222)) — thanks @davidebaraldo +- **fix(resilience):** a 402 on a single paid model of a passthrough/gateway provider (e.g. `kilo-gateway`, `ollama-cloud`) no longer terminalizes the whole connection with a never-auto-recovered `credits_exhausted` status — only the paid model is locked out, so free models on the same key keep serving. 402 variant of [#3027](https://github.com/diegosouzapw/OmniRoute/issues/3027). Single-credential providers are unaffected — a 402 there is still treated as the key being genuinely out of credit ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239) / [#10616](https://github.com/diegosouzapw/OmniRoute/issues/10616)) ([#12242](https://github.com/diegosouzapw/OmniRoute/issues/12242)) — thanks @brick30llc-ctrl ([#12266](https://github.com/diegosouzapw/OmniRoute/pull/12266)) — thanks @geekyNads +- **fix(sse):** trust `finish_reason: "length"`/`"max_tokens"` over the reasoning-consumed-token ratio in response quality validation, so a reasoning model truncated below the old 90% threshold correctly fails and retries instead of returning empty content as a silent "success" ([#12262](https://github.com/diegosouzapw/OmniRoute/pull/12262)) — thanks @brick30llc-ctrl +- **fix(settings):** `PUT /api/settings/cache-config` now persists `alwaysPreserveClientCache` to the flat general settings the runtime cache-control policy actually reads; previously the value landed in the databaseSettings "cache" section and was silently ignored, so the endpoint had no effect on `cache_control` passthrough ([#12304](https://github.com/diegosouzapw/OmniRoute/pull/12304)) — thanks @davidebaraldo +- **fix(grok-cli):** treat omitted SuperGrokPro `creditUsagePercent` as 0% used so Provider Limits still renders a weekly bar (proto3 zero-elision) ([#12312](https://github.com/diegosouzapw/OmniRoute/pull/12312)) — thanks @HouMinXi +- **fix(combo):** Expose the two Auto-Combo scoring factors nobody could set — the combo validation schema and the dashboard weight sliders both declared 13 of the scorer's 15 factors, so `connectionDensity` (spreads load across a provider's connections) and `quality` were dropped on save and offered nowhere. The sliders also shipped their own default table that differed from the engine's on every non-zero factor and summed to 1.05, so the percentages shown next to them added up to 105%. Both lists now match `DEFAULT_WEIGHTS`, and a test keeps them there. Note that a combo whose stored `weights` omitted the two keys was effectively running with them at zero and the other thirteen renormalized upward; it now runs with the engine's intended distribution, so its routing does shift ([#12314](https://github.com/diegosouzapw/OmniRoute/pull/12314)) — thanks @maxmad64bis +- **fix(docs):** The free-tier reference no longer says its numbers come "confidence tagged per row" — no catalog entry carries a confidence tag and the API serves none, so every figure on that page is an estimate of the same, unstated quality. The page now states what an entry does vouch for: an independently documented hard stop (set by hand with the source in a comment, never defaulted to `true`) and a prompt-training disclosure, both with live counts the `check:docs-counts` gate keeps honest ([#12318](https://github.com/diegosouzapw/OmniRoute/pull/12318)) — thanks @maxmad64bis +- **fix(usage):** `adobe-firefly` and `firefly` have had a working usage fetcher since Adobe Firefly landed, but neither was ever added to the registration list, so the provider-plugin manifest, `genericQuotaFetcher` and the free-access quota cache all reported them as having no usage support — while `USAGE_SUPPORTED_PROVIDERS` said the opposite. Both are now declared, which also means their credit balance is fetched like any other declared provider's: `registerGenericQuotaFetchers` now registers a generic quota fetcher for them, and `resolveFreeAccessState` no longer returns early. A test holds the registration list to the dispatcher's switch in both directions, which is what the module's own docstring already asked for in prose ([#12321](https://github.com/diegosouzapw/OmniRoute/pull/12321)) — thanks @maxmad64bis +- **fix(quota):** drop the generic quota cache (agy / Antigravity / Claude OAuth) on an upstream 429 so reset-aware scoring does not keep a 60s stale snapshot, and force-refresh the next usage fetch so inner provider caches cannot recache the same window ([#12325](https://github.com/diegosouzapw/OmniRoute/pull/12325)) — thanks @HouMinXi +- **fix(combos):** deleting a combo now clears its persisted LKGP pins instead of leaving unreachable `key_value` rows behind ([#12326](https://github.com/diegosouzapw/OmniRoute/issues/12326)) ([#12425](https://github.com/diegosouzapw/OmniRoute/pull/12425)) — thanks @KrzysiekSko +- **fix(sse):** Keep ZWNJ (U+200C) and ZWJ (U+200D) in assistant text, reasoning and tool-call arguments — Persian/Kurdish half-space (`ارائه‌دهنده`), Arabic/Indic shaping and emoji sequences no longer lose them; the response de-obfuscation now removes joiners only between ASCII word characters, where the request side inserts them ([#12186](https://github.com/diegosouzapw/OmniRoute/issues/12186)) — thanks @rezjalibd ([#12359](https://github.com/diegosouzapw/OmniRoute/pull/12359)) — thanks @pacocartones +- **fix(resilience):** count resolved upstream 5xx results against the provider circuit breaker on the chat path — `CircuitBreaker.execute()` no longer reads a resolved `{ success: false, status: 5xx }` as a success that cancels the call-site failure, so a provider answering 503s now trips its breaker instead of staying `CLOSED` at `failureCount: 1`; single-model and combo dispatches are each accounted exactly once ([#12254](https://github.com/diegosouzapw/OmniRoute/issues/12254)) ([#12360](https://github.com/diegosouzapw/OmniRoute/pull/12360)) — thanks @pacocartones +- **fix(providers):** resolve the Codex quota auto-ping model from the live provider catalog and lifecycle registry instead of the retired `gpt-5.1-codex-mini`, and pause the ping with one actionable warning when no selectable Codex model exists rather than retrying a shut-down id every cooldown window ([#11905](https://github.com/diegosouzapw/OmniRoute/issues/11905)) ([#12361](https://github.com/diegosouzapw/OmniRoute/pull/12361)) — thanks @pacocartones +- **fix(api):** keep the `{created, data}` wrapper on combo-routed `/v1/images/generations` responses and default Codex image results to `b64_json` on both `/v1/images/generations` and `/v1/images/edits` so Codex CLI's built-in `image_gen` can decode them ([#12268](https://github.com/diegosouzapw/OmniRoute/issues/12268)) ([#12362](https://github.com/diegosouzapw/OmniRoute/pull/12362)) — thanks @pacocartones +- **fix(sse):** Name the shadowed custom provider node when a built-in provider id/alias (e.g. `openference` → `of`) reserves the prefix of an existing OpenAI/Anthropic-compatible node, so the runtime `No active credentials for provider: ` error explains that the prefix routed to the built-in and never reached the node's healthy connections, instead of contradicting the dashboard ([#11943](https://github.com/diegosouzapw/OmniRoute/issues/11943)) — thanks @morpheus9393 ([#12365](https://github.com/diegosouzapw/OmniRoute/pull/12365)) — thanks @pacocartones +- **fix(i18n):** wrap `ccOnboardingKeyPlaceholder` in ICU single quotes across all 43 locales so angle brackets render literally instead of being parsed as rich-text tags, which crashed the Claude Code onboarding block with `INVALID_MESSAGE: INVALID_TAG` ([#12302](https://github.com/diegosouzapw/OmniRoute/issues/12302)) ([#12369](https://github.com/diegosouzapw/OmniRoute/pull/12369)) — thanks @gonisulaimann +- **fix(models):** publish `effort_tiers` on Kimi K3's synced base-model entries (`kmca/k3`, `kmca/k3-256k`) so catalog-only clients (OpenCode, plain SDK pickers) can see and select the reasoning tiers (`low`/`high`/`max`) the synced metadata already carried — the `isSkippedEffortProvider` gate no longer suppresses tier visibility on those base entries, while synthetic `-` variant generation stays prevented and Codex/GLM base models remain excluded unchanged ([#12299](https://github.com/diegosouzapw/OmniRoute/issues/12299)) ([#12371](https://github.com/diegosouzapw/OmniRoute/pull/12371)) — thanks @gonisulaimann +- **fix(guardrails):** keep `auto`/`auto/*` virtual combos exempt from the Vision Bridge `fixedModel` credential guard so a combo target is passed through instead of silently falling back to global auto-selection ([#12237](https://github.com/diegosouzapw/OmniRoute/issues/12237)) ([#12373](https://github.com/diegosouzapw/OmniRoute/pull/12373)) — thanks @pacocartones +- **fix(combo):** capability-filter exhaustion caused by `max_tokens` above every target's known output limit now reports that reason (requested `max_tokens` vs the pool's highest known ceiling) instead of the unrelated "supports structured output" message ([#12229](https://github.com/diegosouzapw/OmniRoute/issues/12229)) — thanks @DW-MediaLab ([#12374](https://github.com/diegosouzapw/OmniRoute/pull/12374)) — thanks @pacocartones +- **fix(auth):** the `least-used` account strategy now prefers accounts without backoff before falling back to oldest `lastUsedAt`, the same tie-break `round-robin` already applies, so a failover no longer lands on a just-rate-limited account for a single request ([#12279](https://github.com/diegosouzapw/OmniRoute/issues/12279)) — thanks @tenshiak ([#12375](https://github.com/diegosouzapw/OmniRoute/pull/12375)) — thanks @pacocartones +- **fix(docker):** the `chatgpt-web-codex-browser` image now finds the Chrome binary under `chrome-linux64/` (Chrome for Testing layout in `playwright:v1.62.0-noble`) as well as the legacy `chrome-linux/`, so the container no longer crash-loops with `exec: --headless=new: not found` ([#12024](https://github.com/diegosouzapw/OmniRoute/issues/12024)) ([#12376](https://github.com/diegosouzapw/OmniRoute/pull/12376)) — thanks @pacocartones +- **fix(providers):** declare `groq/compound` and `allam-2-7b` as non-reasoning models in the curated Groq registry so `reasoning_effort` / `output_config.effort` / `thinking` from Claude Code are stripped instead of forwarded, which Groq rejected with HTTP 400 ([#12134](https://github.com/diegosouzapw/OmniRoute/issues/12134)) ([#12379](https://github.com/diegosouzapw/OmniRoute/pull/12379)) — thanks @pacocartones +- **fix(executors):** `OpencodeExecutor` no longer forces a direct connection when the connection has a proxy assigned in Proxy Management but no per-account proxies: the single-account fast path used to wrap the upstream dispatch in the direct-egress sentinel, discarding the ambient proxy context the chat handler had pinned from `proxy_assignments`, so API-key `opencode`/`opencode-go` connections egressed from the host IP (and hit geoblocks) despite the assignment. The direct pin is now applied only when no ambient proxy context exists ([#11894](https://github.com/diegosouzapw/OmniRoute/issues/11894) — thanks @hizzt) ([#12380](https://github.com/diegosouzapw/OmniRoute/pull/12380)) — thanks @pacocartones +- **fix(api):** `GET /v1/models` with `MODELS_CATALOG_PREFIX_MODE=canonical` (or `?prefix=canonical`) now lists providers whose registry alias is undefined or equal to their own id (Antigravity, Antigravity CLI and other self-aliased built-ins) — their single `provider/model` id was dropped by the alias/canonical duplicate guard in the static, synced, custom and alias-backed catalog loops ([#12058](https://github.com/diegosouzapw/OmniRoute/issues/12058)) — thanks @cheynetom ([#12381](https://github.com/diegosouzapw/OmniRoute/pull/12381)) — thanks @pacocartones +- **fix(translator):** Drop replayed `thinking` blocks that carry no signature (the shape produced from cross-provider `reasoning_content`) instead of stamping the default Claude signature on them, which Anthropic rejected with `400 Invalid signature in thinking block` on the next turn served by an Anthropic rung ([#12105](https://github.com/diegosouzapw/OmniRoute/issues/12105)) — thanks @atescivitci-cmd ([#12386](https://github.com/diegosouzapw/OmniRoute/pull/12386)) — thanks @pacocartones +- **fix(cli):** Resolve Bun's `--preload` polyfill path against the package root instead of `dist/`, so `omniroute` installed with `bun install -g` no longer crashes at startup with `error: preload not found …/dist/open-sse/utils/setupPolyfill.ts` ([#11980](https://github.com/diegosouzapw/OmniRoute/issues/11980)) — thanks @joglomedia ([#12387](https://github.com/diegosouzapw/OmniRoute/pull/12387)) — thanks @pacocartones +- **fix(providers):** `gemini-business` now publishes its model catalog — `/v1/models` and `/v1/providers/gemini-business/models` list the 12 enterprise Gemini ids the executor understands instead of returning an empty list (#12107) ([#12389](https://github.com/diegosouzapw/OmniRoute/pull/12389)) — thanks @pacocartones +- **fix(db):** install `busy_timeout` before the SQLite connection's first statement so a process opening the database while another one closes its WAL connection waits out the transient EXCLUSIVE lock instead of dying with `database is locked`, and recognise the drivers' real BUSY/PROTOCOL/IOERR errors as transient in the corruption probe so the same lock no longer renames the database away as corrupt; deflakes `cross-process contenders never both acquire the same connection` (#12394 — thanks @pacocartones) +- **fix(chat-admission):** derive the `chat_admission_busy` 503 `Retry-After` from observed heavyweight-lease occupancy — the larger of the exhausted `OMNIROUTE_CHAT_ADMISSION_QUEUE_MS` window and the time since capacity last turned over, capped at 60 s — instead of a fixed 1 s (structural) / 2 s (byte-stage) hint that invited Codex/agent fan-out clients to re-send ~1 MiB `/v1/responses` bodies every second into a gate held for the whole SSE lifetime; an idle gate keeps the historical floors ([#12135](https://github.com/diegosouzapw/OmniRoute/issues/12135)) (#12395 — thanks @pacocartones) +- **fix(api-manager):** the API key permissions modal no longer silently drops `allowedCombos` entries its Combo picker cannot render — routing-rule names such as `rt-*`, which the backend already honours — when "All" is clicked and the key is switched back to "Restrict"; those entries now survive the toggle, are listed read-only under the combo list so the count and the list agree, and are saved back verbatim instead of persisting `[]` (deny-all) (#12397 — thanks @pacocartones) +- **fix(catalog):** write the NUL separator of the catalog connection memo key, the provider serviceKind memo key, the Video Bridge promotion group key and a JSON-exactness test fixture as the `\u0000` escape instead of a raw byte — same runtime value, but the raw byte made git, GitHub and ripgrep treat those files as binary (hidden PR diffs, silently skipped searches); a guard test now keeps raw NUL bytes out of `src/`, `open-sse/` and `tests/` (#12403 — thanks @pacocartones) +- **fix(providers):** add `CLAUDE_CODE_CLIENT_VERSION` and `GITHUB_COPILOT_CLI_VERSION` env overrides so Anthropic/Copilot client-version gates can be unblocked without a rebuild ([#12417](https://github.com/diegosouzapw/OmniRoute/issues/12417)) ([#12632](https://github.com/diegosouzapw/OmniRoute/pull/12632)) — thanks @HouMinXi +- **fix(db):** back-fill `last_ping_at` and `last_pinged_reset_key` on `provider_connections` during schema reconciliation so divergent lineages that skipped `123_quota_auto_ping` still accept quota auto-ping writes ([#12470](https://github.com/diegosouzapw/OmniRoute/pull/12470) — thanks @KooshaPari) +- **fix(ci):** document MIT exceptions for `@eloqnt/{config,format-json,format-po}` (next-intl transitive; locked tarballs omit `license`) and keep the A2A lifecycle vitest off the real SQLite persistence seam ([#12581](https://github.com/diegosouzapw/OmniRoute/issues/12581)) ([#12595](https://github.com/diegosouzapw/OmniRoute/pull/12595)) +- OpenCode plugin `/v1/models` catalog fetch now waits 30s by default and attaches HTTP `statusCode` on 401/5xx so host fallback plugins can hop instead of seeing an untyped AbortError/UnknownError. ([#12607](https://github.com/diegosouzapw/OmniRoute/pull/12607)) — thanks @RaviTharuma +- **CI:** the OpenAPI security-tier gate now mirrors `isAlwaysProtectedPath()` in full — it also reads `ALWAYS_PROTECTED_API_PATTERNS`, so the pattern-gated credential routes (`/api/providers/{id}/{claude,codex}-auth/{export,apply-local}`, GHSA-5926-2w35-7h4q) no longer report as unannotated. (#12605) +- **fix(api):** GET /v1/models no longer waits forever on a hung coalesced catalog rebuild; cold-path waits are bounded (`CATALOG_BUILD_TIMEOUT_MS`, default 8s) and a last-good 200 is served when the rebuild times out ([#12627](https://github.com/diegosouzapw/OmniRoute/issues/12627)). ([#12628](https://github.com/diegosouzapw/OmniRoute/pull/12628)) — thanks @RaviTharuma +- **fix(cursor):** a non-array `tool_calls` on an assistant message no longer crashes the cursor request translator with a `TypeError`; both loops now require an array ([#12689](https://github.com/diegosouzapw/OmniRoute/issues/12689)) ([#12691](https://github.com/diegosouzapw/OmniRoute/pull/12691)) — thanks @soroush5 +- **fix(chat):** Chat Completions no longer return empty `content` after a server-owned memory or skills tool; the first provider send and account/model recovery now share one pipeline so a follow-up round-trip can fill the reply ([#12696](https://github.com/diegosouzapw/OmniRoute/issues/12696)) — thanks @HouMinXi ([#12867](https://github.com/diegosouzapw/OmniRoute/pull/12867)) +- **fix(dashboard):** batch-deleting provider keys no longer toasts failure after a successful delete when the confirm button's click event is forwarded as `onAfter` ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) — thanks @HouMinXi +- **fix(glm):** drop the extra 16th argument to `createSSETransformStreamWithLogger` that TypeScript rejected (TS2554) and that never reached the TransformStream ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) ([#12770](https://github.com/diegosouzapw/OmniRoute/pull/12770)) — thanks @HouMinXi +- **fix(dashboard):** pass `nodeMap` into Runtime `QuotaGroup` so a quota monitor in error/exhausted/alerting no longer throws `ReferenceError: nodeMap is not defined`. ([#12868](https://github.com/diegosouzapw/OmniRoute/pull/12868)) — thanks @HouMinXi +- Restricted API keys whose `allowedModels` lists a combo name no longer skip every combo member at pre-dispatch (`ALL_TARGETS_SKIPPED`). Inner-target filtering still applies when the allow-list is a provider prefix or `disableNonPublicModels` is on ([#12899](https://github.com/diegosouzapw/OmniRoute/pull/12899)). — thanks @HouMinXi +- **fix(combos):** Effort-suffixed combo members inherit the base model's `model_context_overrides` row so priority order is not inverted on large requests ([#12926](https://github.com/diegosouzapw/OmniRoute/pull/12926)) — thanks @HouMinXi +- **fix(catalog):** Picker-added `customModels` enter the dispatch-time live catalog so combo members and bare inference no longer 400 ([#12934](https://github.com/diegosouzapw/OmniRoute/pull/12934)) — thanks @HouMinXi +- **fix(docker):** re-derive the Docker build's worker-pool memory budget from the MEASURED ~4.5 GB per-process RSS (the issue owner's own VPS dmesg OOM-killer reproduction) instead of the stale 2560 MB/worker inference #11419 shipped, and lower `OMNIROUTE_BUILD_WORKERS` 3 → 2 so 1 parent + 1 page-data worker (2 processes × 4.5 GB = 9 GB) fits the 12.288 GB (75%) budget on a 16 GB GitHub Actions runner — the previous default (1 parent + 2 workers = 13.5 GB) still overcommitted the runner and kept "Publish to Docker Hub" failing with `cannot allocate memory` after #11419 merged (#7518). ([#11663](https://github.com/diegosouzapw/OmniRoute/pull/11663)) +- **fix(electron):** the packaged Windows desktop build now passes the #7592 cold-restart smoke end to end. Five defects were found and fixed on the way: (1) optional-pack staging failed on any machine whose `tar` is GNU tar — it reads the drive letter in an absolute `-f C:\...` archive path as a remote rsh host (`Cannot connect to C:`), so staging now passes a bare filename with `cwd` at the tarball directory and surfaces tar stderr on failure; (2) the Electron `files` allowlist was missing `lib/loginHeaderCapture.js`, so the packaged main process crashed at startup with `Cannot find module './lib/loginHeaderCapture'`; (3) electron-builder ≥26 injects an `!**/node_modules/**` ignore into every extraResources pattern list that no positive filter can override, which silently dropped the staged runtime `node_modules` (including the better-sqlite3 N-API prebuild) from `resources/app` — a fresh v3.8.50 desktop build would have shipped with no native SQLite driver and reproduced the #7592 sql.js fallback on every machine; an `afterPack` hook now restores the staged `node_modules` after packing; (4) the packaged-app smoke harness redirected `USERPROFILE` into its temp DATA_DIR, but Electron resolves the Roaming profile from `%USERPROFILE%\AppData\Roaming\` (USERPROFILE takes precedence over the APPDATA env var) and the path service throws instead of creating it, so `requestSingleInstanceLock()` returned false and the app exited(0) silently before `app.whenReady()` — the harness now pre-creates the derived tree, and `ensureSmokeEnvDirs` is exported and unit-tested; (5) the #7592 driver assertion parsed a `[DB] Driver: ...` line that the server's primary DB path never printed (only the unused `openDatabaseAsync()` did) — `getDbInstance()` now emits the same line on open so the guard can actually assert the native driver. Also: the smoke env-allowlist unit test hardcoded POSIX paths and could only pass on Linux/macOS; expectations are now host-agnostic, with new regression tests for the USERPROFILE-derived tree and `tarPack` under absolute Windows-style paths ([#11443](https://github.com/diegosouzapw/OmniRoute/pull/11443)) — thanks @jonlwheat2-gif +- fix(sse): stop the auto-combo candidates inspector from silently dropping model-locked/cooled-down rows (#9133) ([#11994](https://github.com/diegosouzapw/OmniRoute/pull/11994)) — thanks @hartmark +- **fix(providers):** Perplexity Web no longer turns upstream stream failures into successful assistant text; pre-content failures remain eligible for fallback, partial output ends with a structured sanitized error, and failed sessions are not persisted ([#12465](https://github.com/diegosouzapw/OmniRoute/pull/12465)) +- **fix(video-bridge):** preserve chronological video and transcript fusion, source-frame timestamps, transcript provenance, full contact-sheet intervals, and pre-abort callback isolation ([#11681](https://github.com/diegosouzapw/OmniRoute/pull/11681)) +- **Z.ai Web:** HTTP 200 streams carrying an upstream error now terminate with a structured failure instead of assistant text plus a normal stop, preserving partial output while allowing pre-content combo fallback. ([#12454](https://github.com/diegosouzapw/OmniRoute/pull/12454)) +- Absorb `Error [AbortError]: request_signal_aborted` and DOMException AbortError shapes in the process-level client-abort crash guard so routine client disconnects no longer kill the server (exit code 7). ([#12165](https://github.com/diegosouzapw/OmniRoute/pull/12165)) — thanks @alvinveroy +- **fix(sse):** Treat Adapta Web `type:error` SSE events as sanitized non-stream failures instead of empty HTTP 200 completions. ([#12459](https://github.com/diegosouzapw/OmniRoute/pull/12459)) +- **fix(autoCombo):** layer 3 (`models_dev_tier`) no longer scores retired models as premium, merge cross-provider capability rows deterministically, and inherit tier scores through variant ids ([#11508](https://github.com/diegosouzapw/OmniRoute/issues/11508)). `getModelsDevTierFitness` now vetoes ids present in `config/quality/model-lifecycle.json` with status `retired` before any other signal; `loadModelCapabilities()` aggregates per `model_id` (any-non-null-true for booleans, max for `limit_context`) instead of last-write-wins over SQLite's undefined row order; a capabilities miss retries through `resolveScoresAs()`'s base id and reports `models_dev_tier:inherited`, matching layers 1–2 (#11492). Adds `getModelsDevTierFitnessWithSource()` and an `invalidateCapabilitiesCache()` test/ops hook. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) — thanks @Meet6338-X +- fix(sse): accept Claude `content:[]` + `stop_reason: max_tokens`/`tool_use` in `detectMalformedNonStream` (match `isEmptyContentResponse`) so Claude Code `/model` probes with `max_tokens: 1` no longer become a false 502 ([#11568](https://github.com/diegosouzapw/OmniRoute/pull/11568)) — thanks @Seramicx +- **fix(security):** sanitize `request.failed` diagnostics before publishing them to live dashboard listeners and replay history, while keeping status, model, provider, latency, and internal call-log diagnostics intact. ([#12469](https://github.com/diegosouzapw/OmniRoute/pull/12469)) +- **fix(pwa):** do not intercept dashboard navigations so Chrome can fall back from a stale HTTP/3 Alt-Svc advertisement (UDP :20128 is unpublished; F5 on a long-lived tab hung until a new tab opened a fresh TCP connection). ([#12767](https://github.com/diegosouzapw/OmniRoute/pull/12767)) — thanks @HouMinXi +- **fix(executors):** handle DuckDuckGo ERR_BN_LIMIT (418) without retrying — when the upstream returns `418 ERR_BN_LIMIT` (rate-limit/ban), the executor now returns the error immediately instead of burning another VQD acquisition that would only count against the IP limit. The retry logic for `418 ERR_CHALLENGE` (unsolved challenge) remains unchanged. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) ([#12110](https://github.com/diegosouzapw/OmniRoute/pull/12110)) — thanks @oyi77 / @Meet6338-X +- Fixed DuckDuckGo streaming responses losing JSON lines and UTF-8 characters split across network chunks. ([#11528](https://github.com/diegosouzapw/OmniRoute/pull/11528)) — thanks @pacocartones +- Harden generated Cloudflare, Vercel, and Deno relays by stripping hop-by-hop, framing, and proxy authentication headers before forwarding requests upstream. ([#11533](https://github.com/diegosouzapw/OmniRoute/pull/11533)) — thanks @pacocartones +- **feat(grok-cli):** Provider Limits shows grok-cli banked reset credits from `GetRemainingResets` (including a real zero; a failed RPC omits the row) and the existing View credits button now calls `ConsumerUiSvc/RedeemReset` for grok-cli. Live tokens use nested fields 10/20/30 (id + Timestamp), not compact 1/2/3. ([#12805](https://github.com/diegosouzapw/OmniRoute/pull/12805)) — thanks @HouMinXi +- **fix(memory):** Embedding Model Quick select, Embedding Source remote dropdown, and Rerank selector now list every configured provider with embedding/rerank support instead of only chat-catalog text matches plus OpenRouter live discovery; a generic OpenAI-compatible `/embeddings` + Cohere-compatible `/rerank` runtime fallback resolves any configured chat provider's embedding/rerank endpoint, so unlisted providers no longer fail with "Unknown embedding provider"; both memory selectors gained a free-text model override ([#11390](https://github.com/diegosouzapw/OmniRoute/pull/11390)) — thanks @rqzbeh +- Harden SQLite upgrades around the historical migration-074 version collision: missing discovery and inspector tables are replayed atomically, pre-existing databases (including setup-created skeletons) receive reusable content-addressed safety snapshots, and Node test/eval probes without `DATA_DIR` are isolated from the operator database. ([#12435](https://github.com/diegosouzapw/OmniRoute/pull/12435)) +- **fix(dashboard):** Moonshot/Kimi Open Platform voucher and cash leftover percentages follow the bucket balance, so an empty wallet no longer paints those rows as 100% while Available is 0% ([#12733](https://github.com/diegosouzapw/OmniRoute/pull/12733)) — thanks @HouMinXi +- **fix(combo):** return non-retryable HTTP 400 when all candidates for a pinned native Codex turn are unavailable due to model-scoped lockout, terminating the turn cleanly while preserving turn continuity and enabling standard Combo routing on subsequent turns ([#12240](https://github.com/diegosouzapw/OmniRoute/pull/12240)) — thanks @mdigitalbh81 +- Fixed auto-router strategies preserving the selected connection through ranking and dispatch when multiple connections share the same provider and model. ([#11530](https://github.com/diegosouzapw/OmniRoute/pull/11530)) — thanks @pacocartones +- Preserve Chat Completions JSON-object response formats when translating requests to the Responses API. ([#11529](https://github.com/diegosouzapw/OmniRoute/pull/11529)) — thanks @pacocartones +- Fix idempotency fingerprints so Responses API requests include `input` and other semantic generation fields using stable serialization, while excluding credentials and request metadata. ([#11532](https://github.com/diegosouzapw/OmniRoute/pull/11532)) — thanks @pacocartones +- **fix(api):** Generated API CLI commands now enforce required OpenAPI request bodies; Combo test commands forward the required `comboName` body, while API keys created by older writers after migration 149 preserve legacy allow-all Combo access without widening explicit empty allowlists — thanks @marcelokarval ([#12070](https://github.com/diegosouzapw/OmniRoute/pull/12070)) +- **fix(grok-web):** treat upstream streaming failures as failures instead of successful assistant text: error-only streams now fail readiness with HTTP 502, while failures after legitimate content preserve that partial output and terminate through the sanitized stream failure path without a normal `stop` completion. ([#12458](https://github.com/diegosouzapw/OmniRoute/pull/12458)) +- HuggingChat now turns HTTP 200 JSONL generation failures into a sanitized 502 before content, or a fixed public stream failure after partial output, so fallback and request persistence no longer record a false successful stop. ([#12456](https://github.com/diegosouzapw/OmniRoute/pull/12456)) +- **fix(providers):** keep 1min.ai HTTP 200 stream errors out of assistant content, preserve partial output, and expose sanitized terminal errors so pre-content failures can fall back. ([#12466](https://github.com/diegosouzapw/OmniRoute/pull/12466)) +- **fix(sessions):** preserve legacy Sessions data when the additive exclusive-session projection is unavailable, returning an empty projection and warning only once per contiguous outage instead of failing the endpoint; the Sessions badge intentionally reflects the merged legacy and exclusive row count introduced by [#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389) ([#11469](https://github.com/diegosouzapw/OmniRoute/pull/11469)) — thanks @KaspaPulse +- Render Nimble Search with the generic provider icon and serve Opper's proven logo locally. ([#12415](https://github.com/diegosouzapw/OmniRoute/pull/12415)) +- **fix(quality):** `config/quality/provider-assets-provenance.jsonl` was stale against the physical `public/providers/` tree (still listing 225 assets and 5 duplicate-content groups after several provider-retirement PRs removed 85 icon files), which would have made `check:provider-asset-provenance` fail on the next full run. Reconciled the manifest to the real 142-file snapshot (142 asset records, 1 duplicate-content group, 2 newly-registered files marked `unresolved` pending provenance research), repinned `auditedCommit`, and updated the regression test's hardcoded counts to match. ([#11876](https://github.com/diegosouzapw/OmniRoute/pull/11876)) +- fix(ui): wrap quota card footer action buttons to prevent clipping Refresh button on narrow cards — when extra action buttons such as `View credits` are present on Codex cards, allow the footer row to flex-wrap with `shrink-0` buttons so `Refresh now` remains visible and clickable across all responsive card widths ([#11464](https://github.com/diegosouzapw/OmniRoute/pull/11464)) — thanks @adevwithpurpose +- Keep Antigravity Gemini usable when the same connection's Claude weekly quota is empty; generic quota cache stays per-connection for every other provider. ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) — thanks @HouMinXi +- Fixed Tailscale tunnel setup to reject ports outside the valid 1-65535 range and safely install Windows MSI packages from paths containing apostrophes. ([#11534](https://github.com/diegosouzapw/OmniRoute/pull/11534)) — thanks @pacocartones +- Classify Cloudflared and Tailscale process-management routes as local-only and spawn-capable while preserving remote access to read-only tunnel status endpoints. ([#11531](https://github.com/diegosouzapw/OmniRoute/pull/11531)) — thanks @pacocartones +- Electron release workflow: the `publish-npm` job now grants `actions: read` to the reusable `npm-publish.yml` it calls (its `publish` job requests it), which is what made GitHub refuse the whole v3.8.50 run at startup and ship the release with zero desktop assets; a `workflow_dispatch` now builds the requested tag instead of the dispatching branch and can skip the npm leg (`publish_npm=false`) when only re-attaching assets ([#11974](https://github.com/diegosouzapw/OmniRoute/pull/11974)) +- **fix(dashboard):** The Combos page usage guide now reads its dismissal through `useSyncExternalStore` instead of correcting SSR state inside an effect, removing an extra commit of the page tree on every load (and the `react-hooks/set-state-in-effect` error it raised). ([#12671](https://github.com/diegosouzapw/OmniRoute/pull/12671)) +- Electron release: `electron/package-lock.json` regained the optional `electron-builder-squirrel-windows` subtree (13 entries) that `npm ci` had been refusing as out of sync, `electron-release.yml` gained a `build_ref` dispatch input and stops regenerating release notes on a re-attach dispatch, and the npm publish workflow attaches the SBOM to the GitHub Release on dispatch publishes too — so the v3.8.51 tag ships every desktop asset and the SBOM like v3.8.49 did ([#12022](https://github.com/diegosouzapw/OmniRoute/pull/12022)) +- Drained the reds every PR against `release/v3.8.51` was born with: documented the three service-worker build-id variables, registered the six retirement/tunnel tests with the mutation gate, approved `eslint-plugin-react-hooks` in the dependency allowlist, added the six `combo.sort.*` strings to `vi` and `pt-BR`, pointed the ChatGPT Web doc at the real migration-168 test, worded the g4f hint around the member key, and realigned four tests to the retired-provider catalog and the legacy-schema fixtures the retirement migrations touch. ([#11940](https://github.com/diegosouzapw/OmniRoute/pull/11940)) +- **fix(providers):** Claude, Grok, LMArena, Notion, and Perplexity web-cookie transports now use pooled `wreq-js` 3.2 instead of the native sidecar, with all nine supported bindings pinned and audited, and the applicable platform binding plus native-license evidence included in each release artifact ([#12429](https://github.com/diegosouzapw/OmniRoute/pull/12429), supersedes [#11753](https://github.com/diegosouzapw/OmniRoute/pull/11753)). +- **fix(providers):** Zed Hosted streaming failures now trigger fallback before content and end partial streams with a sanitized structured error instead of fake assistant text and a normal-success stop. ([#12455](https://github.com/diegosouzapw/OmniRoute/pull/12455)) +- **fix(test):** capture hook result after render (direct commit `b85d0abb0b`) +- **fix(sync):** repair the two regressions the v3.8.50 sync-back left on release/v3.8.51 (direct commit `5ade9e0851`) +- **revert(models):** drop the memo reset hook ported from #10055 (direct commit `282de841ae`) +- **fix(typecheck):** guard the optional connectionId before the reactive model sync (direct commit `f2825bad75`) +- **fix(quota-share):** release the winner's reserved in-flight slot (#11371) ([#11408](https://github.com/diegosouzapw/OmniRoute/pull/11408)) — thanks @oyi77 +- **fix(providers):** stop silently dropping reasoning effort on opencode families (#10788) ([#11409](https://github.com/diegosouzapw/OmniRoute/pull/11409)) — thanks @oyi77 +- **fix(combo):** enable genuine quota-aware routing for generic providers (antigravity, claude, etc.) ([#11411](https://github.com/diegosouzapw/OmniRoute/pull/11411)) — thanks @HouMinXi +- **fix(oauth):** allow expired token retry before deactivation and activate expired retry loop (#P0) ([#11414](https://github.com/diegosouzapw/OmniRoute/pull/11414)) — thanks @HouMinXi +- **perf(executors):** lazy-load the executor registry — defer class imports + construction to first use (#11220) ([#11421](https://github.com/diegosouzapw/OmniRoute/pull/11421)) — thanks @oyi77 +- **fix:** make release/v3.8.50 CI gates green (run 32786966560 root causes) ([#11450](https://github.com/diegosouzapw/OmniRoute/pull/11450)) — thanks @jonlwheat2-gif +- **fix(sse):** exact-domain cookie match and origin-equality URL assertions ([#11452](https://github.com/diegosouzapw/OmniRoute/pull/11452)) — thanks @hartmark +- **fix(providers):** send Responses API payload during OpenAI-compatible validation ([#11454](https://github.com/diegosouzapw/OmniRoute/pull/11454)) — thanks @tuandinh0801 +- **fix(command-code):** fallback to /alpha/generate for Go plan without Provider API access ([#11455](https://github.com/diegosouzapw/OmniRoute/pull/11455)) — thanks @Chewji9875 +- **fix(responses-continuation):** fail closed on a log-truncated stored input/output array ([#11473](https://github.com/diegosouzapw/OmniRoute/pull/11473)) — thanks @hartmark +- **fix(cli):** write jcode settings as [providers.omniroute] in config.toml ([#11484](https://github.com/diegosouzapw/OmniRoute/pull/11484)) — thanks @hongnoul +- **fix(logging):** raise chat-log array truncation cap to 1000 and unify duplicate implementations ([#11499](https://github.com/diegosouzapw/OmniRoute/pull/11499)) — thanks @hartmark +- **fix:** restore live websocket URL helpers ([#11509](https://github.com/diegosouzapw/OmniRoute/pull/11509)) — thanks @PauloFH +- **perf(core):** eliminate async waterfalls across home, cache, and db operations (#11396) ([#11512](https://github.com/diegosouzapw/OmniRoute/pull/11512)) — thanks @HouMinXi +- **fix(kiro):** add runtime.us-east-1.kiro.dev as first-attempt endpoint ([#11517](https://github.com/diegosouzapw/OmniRoute/pull/11517)) — thanks @rifqiawl +- **fix(providers):** remove duplicate 128-tool truncation in OpencodeExecutor (#11444) ([#11518](https://github.com/diegosouzapw/OmniRoute/pull/11518)) — thanks @HouMinXi +- **fix(providers):** poll onboardUser LRO before classifying BYOP ([#11519](https://github.com/diegosouzapw/OmniRoute/pull/11519)) — thanks @rifqiawl +- **fix(translator):** preserve exact tool name casing for Claude tool_use payloads (#11487) ([#11520](https://github.com/diegosouzapw/OmniRoute/pull/11520)) — thanks @HouMinXi +- **fix(kimi-web):** align health probe with executor domain ([#11521](https://github.com/diegosouzapw/OmniRoute/pull/11521)) — thanks @pacocartones +- **fix(cli):** preserve existing admin password during setup ([#11522](https://github.com/diegosouzapw/OmniRoute/pull/11522)) — thanks @pacocartones +- **fix(mitm):** keep Windows DNS elevation off the POSIX sudo path (#11430) ([#11542](https://github.com/diegosouzapw/OmniRoute/pull/11542)) — thanks @official-burak +- **fix(sse):** scale chat admission by ingest byte budget instead of a fixed request count ([#11548](https://github.com/diegosouzapw/OmniRoute/pull/11548)) — thanks @TheDemonTuan +- **fix:** accept Gemini Web cookie exports ([#11554](https://github.com/diegosouzapw/OmniRoute/pull/11554)) — thanks @rafacpti23 +- **fix(i18n):** complete Brazilian Portuguese translations ([#11558](https://github.com/diegosouzapw/OmniRoute/pull/11558)) — thanks @rafacpti23 +- **fix(sse):** explicit types for openai-responses pureHelpers — clears last failing core typecheck gate ([#11567](https://github.com/diegosouzapw/OmniRoute/pull/11567)) — thanks @jonlwheat2-gif +- **fix(opencode):** hydrate Proxy Pool references for no-auth connections ([#11584](https://github.com/diegosouzapw/OmniRoute/pull/11584)) — thanks @AStupidBear +- **fix(ci):** resolve inherited release/v3.8.51 base-red CI failures ([#11608](https://github.com/diegosouzapw/OmniRoute/pull/11608)) — thanks @jonlwheat2-gif +- **fix(db):** fallback load STORAGE_ENCRYPTION_KEY from env files ([#11614](https://github.com/diegosouzapw/OmniRoute/pull/11614)) — thanks @benzntech +- **fix(providers):** mark Brave Search as no longer having a free tier ([#11621](https://github.com/diegosouzapw/OmniRoute/pull/11621)) — thanks @turbolego +- **fix:** pass tool result content through as string in OpenAI/Claude -> Gemini translation ([#11624](https://github.com/diegosouzapw/OmniRoute/pull/11624)) — thanks @Hsia97 +- **fix(sse):** scope credential health to exact key ([#11628](https://github.com/diegosouzapw/OmniRoute/pull/11628)) — thanks @KaspaPulse +- **fix(install):** guard prepare script against missing husky ([#11634](https://github.com/diegosouzapw/OmniRoute/pull/11634)) — thanks @Prajeeth-12 +- **fix(security):** redact NVIDIA_API_KEY at the log sink in the ad-hoc diag script ([#11639](https://github.com/diegosouzapw/OmniRoute/pull/11639)) +- **fix(types):** clear the two type regressions red on release/v3.8.51 ([#11640](https://github.com/diegosouzapw/OmniRoute/pull/11640)) — thanks @jonlwheat2-gif +- **fix(build):** resolve workspace: protocol dependencies to concrete semver versions during prepublish (#11540) ([#11641](https://github.com/diegosouzapw/OmniRoute/pull/11641)) — thanks @HouMinXi +- **fix(search):** properly resolve configured search connection in /v1/responses pipeline (#11524) ([#11642](https://github.com/diegosouzapw/OmniRoute/pull/11642)) — thanks @HouMinXi +- **fix(authz):** trust cf-connecting-ip for via-proxy detection when peer is Cloudflare edge (#11514) ([#11643](https://github.com/diegosouzapw/OmniRoute/pull/11643)) — thanks @HouMinXi +- **fix(security):** compare the OIDC state cookie in constant time ([#11646](https://github.com/diegosouzapw/OmniRoute/pull/11646)) +- **fix(security):** namespace the dedup hash by the calling API key ([#11649](https://github.com/diegosouzapw/OmniRoute/pull/11649)) +- **fix(models):** gate the Codex-native catalog loop on prefix mode (#11632) ([#11662](https://github.com/diegosouzapw/OmniRoute/pull/11662)) — thanks @xiaoyaner0201 +- **fix(dashboard):** project Codex routing quota in account pools ([#11666](https://github.com/diegosouzapw/OmniRoute/pull/11666)) — thanks @KaspaPulse +- **fix(ddgw):** normaliza system no upstream da release v3.8.51 ([#11692](https://github.com/diegosouzapw/OmniRoute/pull/11692)) — thanks @RhianB14 +- **fix(memory):** honor category filter in GET /api/memory ([#11699](https://github.com/diegosouzapw/OmniRoute/pull/11699)) — thanks @Sabeekhann +- **fix(ci):** detect stale workspace lockfile entries ([#11714](https://github.com/diegosouzapw/OmniRoute/pull/11714)) — thanks @pacocartones +- **fix(ci):** stamp dist/BUILD_SHA before the npm publish provenance gate ([#11721](https://github.com/diegosouzapw/OmniRoute/pull/11721)) +- **fix(docker):** decouple the best-effort Bun image from the release manifest ([#11724](https://github.com/diegosouzapw/OmniRoute/pull/11724)) +- **perf(compression):** avoid cloning memo misses twice ([#11727](https://github.com/diegosouzapw/OmniRoute/pull/11727)) — thanks @RaviTharuma +- **fix(docker):** let the bun digest artifact be absent, not fatal ([#11740](https://github.com/diegosouzapw/OmniRoute/pull/11740)) +- **fix(shared):** block cloud-metadata hosts under default remote-image guard ([#11755](https://github.com/diegosouzapw/OmniRoute/pull/11755)) — thanks @santosraju99-hub +- **fix(api):** preserve caller-provided X-Correlation-Id on chat completions ([#11760](https://github.com/diegosouzapw/OmniRoute/pull/11760)) — thanks @aniruddhaadak80 +- **fix(resilience):** lock permanently retired models instead of short backoff (Gemini ban prevention) ([#11762](https://github.com/diegosouzapw/OmniRoute/pull/11762)) — thanks @turbolego +- **fix:** mark Vercel AI Gateway as passthroughModels ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **fix(resilience):** stop hammering permanently-moved endpoints and billing-suspended accounts ([#11774](https://github.com/diegosouzapw/OmniRoute/pull/11774)) — thanks @turbolego +- **fix(lease):** remove global static reservation and gate routing on live active lease occupancy ([#11775](https://github.com/diegosouzapw/OmniRoute/pull/11775)) — thanks @TheDemonTuan +- **fix(ci):** size the install-upgrade gate to a measured run, and log the pack cost ([#11776](https://github.com/diegosouzapw/OmniRoute/pull/11776)) +- **fix(pwa):** stop serving the stale shell after deploys ([#11779](https://github.com/diegosouzapw/OmniRoute/pull/11779)) — thanks @HouMinXi +- **fix(combo):** lock GitHub models rejected as "not supported" for future requests ([#11781](https://github.com/diegosouzapw/OmniRoute/pull/11781)) — thanks @turbolego +- **fix(cli):** probe both IPv4 and IPv6 loopback for server readiness (#11766) ([#11794](https://github.com/diegosouzapw/OmniRoute/pull/11794)) — thanks @vermasomesh835 +- **fix(sse):** preserve CLIProxyAPI wrappers per connection ([#11797](https://github.com/diegosouzapw/OmniRoute/pull/11797)) — thanks @RaviTharuma +- **fix(providers):** honor model auto-fetch opt-in on create ([#11798](https://github.com/diegosouzapw/OmniRoute/pull/11798)) — thanks @RaviTharuma +- **fix(dashboard):** make model auto-fetch order independent ([#11805](https://github.com/diegosouzapw/OmniRoute/pull/11805)) — thanks @RaviTharuma +- **fix(docker):** make GHA cache exports best effort ([#11806](https://github.com/diegosouzapw/OmniRoute/pull/11806)) — thanks @RaviTharuma +- **fix(kiro):** do not permanently ban on 'User is not authorized to make this call' ([#11809](https://github.com/diegosouzapw/OmniRoute/pull/11809)) — thanks @Deftera186 +- **fix(sse):** keep cache-write tokens in OpenAI-shaped usage ([#11814](https://github.com/diegosouzapw/OmniRoute/pull/11814)) — thanks @TheDemonTuan +- **fix(docs):** resolve relative markdown and wiki links across Fumadocs and GitHub wiki ([#11834](https://github.com/diegosouzapw/OmniRoute/pull/11834)) — thanks @AndrianBalanescu +- **fix(chat):** preserve unstripped model string for passthrough provider routing ([#11840](https://github.com/diegosouzapw/OmniRoute/pull/11840)) — thanks @raheemuddin786 +- **fix(mcp):** avoid async initialization inside sync esm wrappers (#11569) ([#11849](https://github.com/diegosouzapw/OmniRoute/pull/11849)) — thanks @HouMinXi +- **fix(sse):** preserve coherent markdown token boundaries across stream translation chunks (#11606) ([#11851](https://github.com/diegosouzapw/OmniRoute/pull/11851)) — thanks @HouMinXi +- **fix(ci):** run the install-upgrade gate on disk, not on the /tmp tmpfs ([#11855](https://github.com/diegosouzapw/OmniRoute/pull/11855)) +- **fix(skills):** expand shorthand property types in injected tool schemas ([#11857](https://github.com/diegosouzapw/OmniRoute/pull/11857)) — thanks @alvinveroy +- **fix(ci):** publish npm from a hosted runner so provenance is accepted ([#11877](https://github.com/diegosouzapw/OmniRoute/pull/11877)) +- **fix(lint):** pin react hooks plugin to 7.0.1 ([#11886](https://github.com/diegosouzapw/OmniRoute/pull/11886)) +- **fix(ci):** surface and clear release-green ESLint failure ([#11890](https://github.com/diegosouzapw/OmniRoute/pull/11890)) — thanks @MumuTW +- **fix(guardrails):** prevent duplicate prompt-injection-guard log output ([#11936](https://github.com/diegosouzapw/OmniRoute/pull/11936)) — thanks @patrykkopycinski +- **fix(auth):** downgrade expected transient states from warn to debug ([#11937](https://github.com/diegosouzapw/OmniRoute/pull/11937)) — thanks @patrykkopycinski +- **fix(codex):** fail over image generation for imported free plans ([#11948](https://github.com/diegosouzapw/OmniRoute/pull/11948)) — thanks @Bl0ck154 +- **fix(codex):** normalize non-stream responses ([#11951](https://github.com/diegosouzapw/OmniRoute/pull/11951)) — thanks @Bl0ck154 +- **fix(images):** forward Antigravity image size ([#11952](https://github.com/diegosouzapw/OmniRoute/pull/11952)) — thanks @Bl0ck154 +- **fix(db):** invalidate connection cache after upsert ([#11953](https://github.com/diegosouzapw/OmniRoute/pull/11953)) — thanks @Bl0ck154 +- **fix(codex):** restore imported account state ([#11954](https://github.com/diegosouzapw/OmniRoute/pull/11954)) — thanks @Bl0ck154 +- **fix(sse):** give extended-thinking targets the reasoning readiness budget ([#11959](https://github.com/diegosouzapw/OmniRoute/pull/11959)) — thanks @abhisheksharma2411 +- **fix(dashboard):** use opaque background and readable text color on cost chart tooltips ([#11960](https://github.com/diegosouzapw/OmniRoute/pull/11960)) — thanks @ZaimMarzuki +- **fix(antigravity):** send complete loadCodeAssist metadata (ideType/platform/pluginType as numeric enums) ([#11969](https://github.com/diegosouzapw/OmniRoute/pull/11969)) — thanks @rifqiawl +- **fix(core):** resolve DB init race condition and reasoning translation ([#12003](https://github.com/diegosouzapw/OmniRoute/pull/12003)) — thanks @raheemuddin786 +- **fix(admission):** exclude reclaimable page cache from the cgroup pressure ratio ([#12017](https://github.com/diegosouzapw/OmniRoute/pull/12017)) — thanks @HouMinXi +- **fix(build):** prune non-production trees in NFT trace excludes and tsconfig ([#12028](https://github.com/diegosouzapw/OmniRoute/pull/12028)) — thanks @Chewji9875 +- **fix(migrations):** add renamed migration compatibility for 056/073/077/101 ([#12036](https://github.com/diegosouzapw/OmniRoute/pull/12036)) — thanks @oyi77 +- **fix(sse):** default crash-guard logger to console.warn, not console ([#12042](https://github.com/diegosouzapw/OmniRoute/pull/12042)) — thanks @HouMinXi +- **fix(catalog):** derive vision/modalities for built-in auto combos from effective target pool ([#12046](https://github.com/diegosouzapw/OmniRoute/pull/12046)) — thanks @Prajeeth-12 +- **fix(release):** the packaged-app smoke verifies the database opened, not a driver line the primary path never prints (twin of #12032) ([#12047](https://github.com/diegosouzapw/OmniRoute/pull/12047)) +- **fix(barrel):** migrate src/app/ off the @/lib/localDb barrel import (#11795 Phase 2) ([#12051](https://github.com/diegosouzapw/OmniRoute/pull/12051)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate src/lib/ off the localDb barrel to direct db imports (#59) ([#12052](https://github.com/diegosouzapw/OmniRoute/pull/12052)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate open-sse, src/shared, src/sse, src/models, src/domain off the @/lib/localDb barrel import (#11795 Phase 4) ([#12053](https://github.com/diegosouzapw/OmniRoute/pull/12053)) — thanks @jonlwheat2-gif +- **fix(barrel):** delete the @/lib/localDb barrel — every consumer migrated (#11795 Phase 5) ([#12055](https://github.com/diegosouzapw/OmniRoute/pull/12055)) — thanks @jonlwheat2-gif +- **fix(radar):** restore D12 public boundary ([#12057](https://github.com/diegosouzapw/OmniRoute/pull/12057)) +- **fix(dev):** bound webpack and Tailwind scans ([#12075](https://github.com/diegosouzapw/OmniRoute/pull/12075)) — thanks @backryun +- **fix(dev):** isolate root layout settings reads ([#12076](https://github.com/diegosouzapw/OmniRoute/pull/12076)) — thanks @backryun +- **fix(dev):** reduce instrumentation executor fan-out (phase 3) ([#12078](https://github.com/diegosouzapw/OmniRoute/pull/12078)) — thanks @backryun +- **fix(dev):** make logging resources HMR-singleton ([#12079](https://github.com/diegosouzapw/OmniRoute/pull/12079)) — thanks @backryun +- **fix(sse):** keep unavailable forced connections scoped ([#12080](https://github.com/diegosouzapw/OmniRoute/pull/12080)) — thanks @keeltrace +- **fix(dev):** isolate batch dispatch from instrumentation ([#12081](https://github.com/diegosouzapw/OmniRoute/pull/12081)) — thanks @backryun +- **fix(ci):** reconcile release test contract drift ([#12082](https://github.com/diegosouzapw/OmniRoute/pull/12082)) — thanks @backryun +- **fix(ci):** accept CVE-2025-68121 in the prebuilt tls-client .so, auto-close base-red issues, guard Scorecard on the default branch ([#12085](https://github.com/diegosouzapw/OmniRoute/pull/12085)) +- **fix(db):** drop three consumer-less 1proxy exports — dead-code base-red on release/v3.8.51 after the barrel deletion (#12055) ([#12087](https://github.com/diegosouzapw/OmniRoute/pull/12087)) +- **fix(api):** clear the six API-route TypeScript regressions the new gate landed red on ([#12094](https://github.com/diegosouzapw/OmniRoute/pull/12094)) +- **fix(release):** never let the tag-push Create Release append auto notes to the curated body ([#12096](https://github.com/diegosouzapw/OmniRoute/pull/12096)) +- **fix(sse):** honor CLIProxyAPI environment API key ([#12099](https://github.com/diegosouzapw/OmniRoute/pull/12099)) — thanks @RaviTharuma +- **fix(oauth):** bind Google refresh to the client that issued the token ([#12106](https://github.com/diegosouzapw/OmniRoute/pull/12106)) — thanks @HouMinXi +- **fix(dashboard):** make RequestLoggerDetail loadable outside Next — CSS via globals.css + CJS/ESM interop (#11703 base-reds) ([#12114](https://github.com/diegosouzapw/OmniRoute/pull/12114)) +- **fix(leases):** project status lease row to lease columns so joined connection PII never escapes ([#12115](https://github.com/diegosouzapw/OmniRoute/pull/12115)) — thanks @geek007git +- **fix(plugins):** do not kill the plugin process when a fire-and-forget hook times out ([#12116](https://github.com/diegosouzapw/OmniRoute/pull/12116)) — thanks @geek007git +- **fix(guardrails):** restore injection-guard logging on middleware-only routes ([#12117](https://github.com/diegosouzapw/OmniRoute/pull/12117)) — thanks @geek007git +- **fix(plugins):** refresh stored manifest from disk on activate so new hook fields reach existing installs ([#12120](https://github.com/diegosouzapw/OmniRoute/pull/12120)) — thanks @geek007git +- **fix(codex):** preserve existing provider state when bulk-import upserts a matching connection ([#12122](https://github.com/diegosouzapw/OmniRoute/pull/12122)) — thanks @geek007git +- **fix(ci):** clear the two base-reds the 2026-08-30 merge batch left on release/v3.8.51 (round 3) ([#12123](https://github.com/diegosouzapw/OmniRoute/pull/12123)) +- **fix(sse):** strip type:'custom' from Claude tools on agentrouter dispatch ([#12126](https://github.com/diegosouzapw/OmniRoute/pull/12126)) — thanks @wahidsadik371-coder +- **fix(sse):** stop advertising video providers the dispatcher cannot run ([#12131](https://github.com/diegosouzapw/OmniRoute/pull/12131)) — thanks @quiterunner-commits +- **fix(search):** treat HTTP 432 and plan limit errors as transient cooldown ([#12139](https://github.com/diegosouzapw/OmniRoute/pull/12139)) — thanks @HouMinXi +- **fix(sse):** refuse an AI Horde queue that cannot fit the request budget ([#12143](https://github.com/diegosouzapw/OmniRoute/pull/12143)) — thanks @quiterunner-commits +- **fix(ci):** clear the base-reds the afternoon merge batch left on release/v3.8.51 (round 5: provider count 352, TS2554/TS2677) ([#12144](https://github.com/diegosouzapw/OmniRoute/pull/12144)) +- **fix(translator):** strip neutral tool_choice when tools absent in Responses-to-Chat (#12141) ([#12166](https://github.com/diegosouzapw/OmniRoute/pull/12166)) — thanks @HouMinXi +- **fix(guardrails):** pass providerId to getResolvedModelCapabilities in checkComboVision (#12112) ([#12169](https://github.com/diegosouzapw/OmniRoute/pull/12169)) — thanks @HouMinXi +- **fix(translator):** strip plaintext reasoning content for opaque responses backends (#12128) ([#12171](https://github.com/diegosouzapw/OmniRoute/pull/12171)) — thanks @HouMinXi +- **perf(stream):** compile hot-path regexes once, bound token caches, fix quadratic buffering ([#12179](https://github.com/diegosouzapw/OmniRoute/pull/12179)) — thanks @opensource-elearning +- **fix:** resolve compression worker file using runtime anchors instead of… ([#12183](https://github.com/diegosouzapw/OmniRoute/pull/12183)) — thanks @NightStalker-87 +- **fix:** prevent Claude Code session kills via liveness-aware readiness + auto model echo ([#12189](https://github.com/diegosouzapw/OmniRoute/pull/12189)) — thanks @opensource-elearning +- **fix(translator):** preserve falsy primitive values in Gemini and Antigravity function response results ([#12191](https://github.com/diegosouzapw/OmniRoute/pull/12191)) — thanks @Siva010 +- **fix(oauth):** mark empty Antigravity projectId as degraded and clear stale errors (#11284) ([#12205](https://github.com/diegosouzapw/OmniRoute/pull/12205)) — thanks @HouMinXi +- **fix(providers):** gate the Codex auto-ping usage read on the shared quota throttle ([#12209](https://github.com/diegosouzapw/OmniRoute/pull/12209)) — thanks @abhisheksharma2411 +- **fix(combos):** prioritize SQLite row id over inner JSON id and notify delete errors ([#12213](https://github.com/diegosouzapw/OmniRoute/pull/12213)) — thanks @HouMinXi +- **fix(providers):** mark featherless.ai as no longer having a free tier (`hasFree: false`) ([#12216](https://github.com/diegosouzapw/OmniRoute/pull/12216)) — thanks @turbolego +- **fix(deepseek-web):** stop Turbopack dev panic in the PoW worker path resolver ([#12221](https://github.com/diegosouzapw/OmniRoute/pull/12221)) — thanks @hartmark +- **fix(chat-admission):** clarify local 503 source ([#12223](https://github.com/diegosouzapw/OmniRoute/pull/12223)) — thanks @keeltrace +- **fix(dev):** silence webpack runtime module warnings ([#12228](https://github.com/diegosouzapw/OmniRoute/pull/12228)) — thanks @backryun +- **fix(db):** use module.require for CommonJS runtime driver loading ([#12230](https://github.com/diegosouzapw/OmniRoute/pull/12230)) — thanks @raheemuddin786 +- **fix(memory):** honest probe-driven FTS5 keyword status + memory_id rowid sync ([#12231](https://github.com/diegosouzapw/OmniRoute/pull/12231)) — thanks @raheemuddin786 +- **fix(system):** propagate abort signal to stream reader in HTTP version checks ([#12232](https://github.com/diegosouzapw/OmniRoute/pull/12232)) — thanks @raheemuddin786 +- **fix(resilience):** isolate local host execution errors from provider circuit breakers ([#12233](https://github.com/diegosouzapw/OmniRoute/pull/12233)) — thanks @raheemuddin786 +- **fix(sse):** sort injected tools deterministically for prompt caching ([#12234](https://github.com/diegosouzapw/OmniRoute/pull/12234)) — thanks @raheemuddin786 +- **fix(combo):** always clear the loop-safety timer, not just on the happy path (#11804) ([#12245](https://github.com/diegosouzapw/OmniRoute/pull/12245)) +- **fix(quality):** base-red pair — stryker tap registration + turn-pin suites aligned to the window gate ([#12255](https://github.com/diegosouzapw/OmniRoute/pull/12255)) — thanks @backryun +- **fix(dev):** qualify Turbopack runtime boundaries ([#12258](https://github.com/diegosouzapw/OmniRoute/pull/12258)) — thanks @backryun +- **fix(combo):** fall back to full pool when collapsed sole survivor is context-too-small ([#12278](https://github.com/diegosouzapw/OmniRoute/pull/12278)) — thanks @opensource-elearning +- **fix(combo):** bound the pre-dispatch unavailable skip so a stale label cannot dark a pool (#12168) ([#12285](https://github.com/diegosouzapw/OmniRoute/pull/12285)) +- **fix(security):** strip Qwen/Alibaba console-session cookies from provider API responses ([#12287](https://github.com/diegosouzapw/OmniRoute/pull/12287)) +- **fix(usage):** console-aware Token Plan guidance + subscription hint on bailian 401 ([#12288](https://github.com/diegosouzapw/OmniRoute/pull/12288)) +- **fix(resourcePressure):** log numeric detail on every rejection, recover faster ([#12293](https://github.com/diegosouzapw/OmniRoute/pull/12293)) — thanks @hartmark +- **fix(release):** drain the 2026-09-01 base-red window — passthrough usage regression + radarPage i18n keys ([#12327](https://github.com/diegosouzapw/OmniRoute/pull/12327)) +- **fix(ci):** openapi-security-tiers checker must honor routeGuard patterns + imported prefixes ([#12350](https://github.com/diegosouzapw/OmniRoute/pull/12350)) — thanks @ggiak +- **perf(ci):** cache node_modules in the npm-ci-retry composite (#8084 D3) ([#12408](https://github.com/diegosouzapw/OmniRoute/pull/12408)) +- **revert(ui):** point CTAs back at their real destinations ([#12410](https://github.com/diegosouzapw/OmniRoute/pull/12410)) +- **fix(release):** drain the 2026-09-02 base-red — rerank-providers import + api-typecheck baseline ratchet ([#12414](https://github.com/diegosouzapw/OmniRoute/pull/12414)) +- **fix(types):** make system prompt injection noImplicitAny-safe ([#12416](https://github.com/diegosouzapw/OmniRoute/pull/12416)) +- **fix(quality):** validate typecheck baseline schema ([#12419](https://github.com/diegosouzapw/OmniRoute/pull/12419)) +- **fix(memory):** resolve rerank provider node cache import ([#12421](https://github.com/diegosouzapw/OmniRoute/pull/12421)) +- **fix(ci):** repair release-root regressions — pack dedup, web-session syntax, uc-image ids ([#12423](https://github.com/diegosouzapw/OmniRoute/pull/12423)) — thanks @backryun +- **fix(providers):** separate MaxAI and UC credential contracts ([#12431](https://github.com/diegosouzapw/OmniRoute/pull/12431)) +- **fix(cli):** preserve Claude settings on config set ([#12432](https://github.com/diegosouzapw/OmniRoute/pull/12432)) — thanks @KrzysiekSko +- **fix(providers):** repair the maxai credential block truncated by merge auto-resolve ([#12433](https://github.com/diegosouzapw/OmniRoute/pull/12433)) +- **fix(quality):** record the 2026-09-02 merged growth in the file-size baseline ([#12434](https://github.com/diegosouzapw/OmniRoute/pull/12434)) +- **fix(uc):** route UC error strings through sanitizeErrorMessage; allowlist the retired codex id ([#12437](https://github.com/diegosouzapw/OmniRoute/pull/12437)) +- **fix(adapta):** redact streamed upstream errors ([#12438](https://github.com/diegosouzapw/OmniRoute/pull/12438)) +- **fix(sse):** remap non-contiguous upstream tool_calls index to a gap-free output_index ([#12445](https://github.com/diegosouzapw/OmniRoute/pull/12445)) — thanks @hartmark +- **fix(quality):** detect duplicate tool_calls entries in one response ([#12446](https://github.com/diegosouzapw/OmniRoute/pull/12446)) — thanks @hartmark +- **fix(conversations):** resolve turn content OmniRoute never sends back to the client ([#12447](https://github.com/diegosouzapw/OmniRoute/pull/12447)) — thanks @hartmark +- **fix(combo):** do not treat credits-exhausted 401 as auth skip ([#12449](https://github.com/diegosouzapw/OmniRoute/pull/12449)) — thanks @RaviTharuma +- **fix(auth):** do not park healthy quota accounts as expired ([#12452](https://github.com/diegosouzapw/OmniRoute/pull/12452)) — thanks @RaviTharuma +- **fix(responses-continuation):** fail closed on a collector-truncated, empty output array ([#12460](https://github.com/diegosouzapw/OmniRoute/pull/12460)) — thanks @hartmark +- **fix(logging):** raise the SSE payload collector's default cap ([#12461](https://github.com/diegosouzapw/OmniRoute/pull/12461)) — thanks @hartmark +- **fix(resilience):** surface Responses failed.error.message in 502s ([#12472](https://github.com/diegosouzapw/OmniRoute/pull/12472)) — thanks @RaviTharuma +- **fix(security):** unbiased maxai X-Random nonce + stricter URL/regex assertions ([#12502](https://github.com/diegosouzapw/OmniRoute/pull/12502)) +- **fix(video):** re-anchor transcript log-redaction so PII/credential maskers can't reopen the leak (#12150 P1 follow-up) ([#12503](https://github.com/diegosouzapw/OmniRoute/pull/12503)) +- **fix(monitoring):** serve cached credentialHealth off the request path ([#12533](https://github.com/diegosouzapw/OmniRoute/pull/12533)) — thanks @RaviTharuma +- **fix(quota):** keep Antigravity Gemini usable when Claude weekly is empty ([#12566](https://github.com/diegosouzapw/OmniRoute/pull/12566)) — thanks @HouMinXi +- **fix(authz):** hard-gate every credential export and CLI-config write (GHSA-5926-2w35-7h4q) ([#12600](https://github.com/diegosouzapw/OmniRoute/pull/12600)) +- **fix(docs):** restore the Next build — REMOVED_PROVIDERS.md had no frontmatter (base-red #12581) ([#12610](https://github.com/diegosouzapw/OmniRoute/pull/12610)) +- **fix(quality):** re-point the zcodeProtocol public-creds allowlist to line 313 ([#12615](https://github.com/diegosouzapw/OmniRoute/pull/12615)) +- **fix(security):** close 3 advisories — search baseUrl exfil, sk- in the error sanitizer, bifrost relay header leak ([#12620](https://github.com/diegosouzapw/OmniRoute/pull/12620)) +- **fix(providers):** sanitize boolean required and nested bare maps for Gemini (#12269) ([#12624](https://github.com/diegosouzapw/OmniRoute/pull/12624)) — thanks @HouMinXi +- **fix(resilience):** keep Overloaded STREAM_EARLY_EOF off the provider breaker ([#12626](https://github.com/diegosouzapw/OmniRoute/pull/12626)) — thanks @HouMinXi +- **fix(catalog):** re-audit free-tier quotas against official pages ([#12649](https://github.com/diegosouzapw/OmniRoute/pull/12649)) +- **fix(dashboard):** keep a request's pending-tracking id stable across combo target retries ([#12650](https://github.com/diegosouzapw/OmniRoute/pull/12650)) — thanks @hartmark +- **fix(tests):** retire dead model ids from the chat-pipeline integration suite (base-red #12581) ([#12670](https://github.com/diegosouzapw/OmniRoute/pull/12670)) +- **fix:** resolve SqliteError no such table compression_run_telemetry during cleanup ([#12682](https://github.com/diegosouzapw/OmniRoute/pull/12682)) — thanks @groovecityJO +- **fix(combo):** treat a pin-only step as implicit connection allowlist ([#12697](https://github.com/diegosouzapw/OmniRoute/pull/12697)) — thanks @HouMinXi +- **fix(ci):** ship .npmrc in published package so legacy-peer-deps applies to consumers (#11544) ([#12699](https://github.com/diegosouzapw/OmniRoute/pull/12699)) — thanks @KooshaPari +- **fix(docker):** pin 4 CLI tools to exact versions (#12576) ([#12703](https://github.com/diegosouzapw/OmniRoute/pull/12703)) — thanks @KooshaPari +- **fix(docker):** document and harden cli profile trust boundary (#12570) ([#12706](https://github.com/diegosouzapw/OmniRoute/pull/12706)) — thanks @KooshaPari +- **fix(sse):** redact video transcript in pre-guardrail rejected-request logs (#12150 P2 item 7) ([#12710](https://github.com/diegosouzapw/OmniRoute/pull/12710)) +- **fix(i18n):** quote placeholder in OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES description (#12505) ([#12769](https://github.com/diegosouzapw/OmniRoute/pull/12769)) — thanks @KooshaPari +- **fix(api/pricing):** surface validation error message as string, not raw object (#12494) ([#12771](https://github.com/diegosouzapw/OmniRoute/pull/12771)) — thanks @KooshaPari +- **fix(sse):** re-enable prompt compression for native Codex passthrough ([#12834](https://github.com/diegosouzapw/OmniRoute/pull/12834)) — thanks @thomasmaerz +- **fix(dashboard):** model health tests for a provider node set to the Responses API now call `/v1/responses` with a Responses-shaped body instead of `/v1/chat/completions` — those models were reported as `Provider returned HTTP 200 but no text content` even though the same model answered normally through `/v1/responses` ([#13070](https://github.com/diegosouzapw/OmniRoute/issues/13070)) ([#13087](https://github.com/diegosouzapw/OmniRoute/pull/13087)) — thanks @ntdatt812 +- fix(routing): stop a round-robin combo's "opencode" targets from collapsing onto the opencode-zen connection (#11912) ([#13283](https://github.com/diegosouzapw/OmniRoute/pull/13283)) +- fix(routing): stop the reactive-compaction debug log from lying when compression is globally disabled (#11977) ([#13276](https://github.com/diegosouzapw/OmniRoute/pull/13276)) +- fix(electron): relativize standalone-bundle symlink targets so Stage 8 manifest verification stops failing on Windows (#11979) ([#13251](https://github.com/diegosouzapw/OmniRoute/pull/13251)) +- fix(dashboard): surface a visible error when Compression Studio's combined preview run fails (#12061) ([#13288](https://github.com/diegosouzapw/OmniRoute/pull/13288)) +- fix(dashboard): make the compression "Effective pipeline" preview honor the active profile and warn when the master switch is off (#12063) ([#13287](https://github.com/diegosouzapw/OmniRoute/pull/13287)) +- fix(providers): scope TinyCMS Web signer's DOM shims to each call instead of leaking them for the process lifetime, and surface a clean HTTP status on a non-JSON interception-toggles error (#12072) ([#13254](https://github.com/diegosouzapw/OmniRoute/pull/13254)) +- fix(guardrails): stop Vision Bridge from re-selecting a model locked after a 404 (#12111) ([#13259](https://github.com/diegosouzapw/OmniRoute/pull/13259)) +- fix(sse): require Responses-shaped body before native OpenAI-compatible passthrough (#12129) ([#13278](https://github.com/diegosouzapw/OmniRoute/pull/13278)) +- fix(providers): minimax-m3 now collapses manual thinking.type:"enabled" to adaptive, preventing upstream 400 (2013) (#12132) ([#13249](https://github.com/diegosouzapw/OmniRoute/pull/13249)) +- fix(db): scope model visibility overrides by modality so hiding a Chat model no longer hides an identically-ID'd Image/Embeddings/etc. model (#12172) ([#13258](https://github.com/diegosouzapw/OmniRoute/pull/13258)) +- fix(db): scope local-provider apiKey dedup to matching base URL so LM Studio/Ollama-style connections support multiple accounts (#12173) ([#13250](https://github.com/diegosouzapw/OmniRoute/pull/13250)) +- fix(providers): refresh Trae's stale Referer/Origin and forward user timezone so imported connections stop failing with 401 (#12190) ([#13255](https://github.com/diegosouzapw/OmniRoute/pull/13255)) +- fix(providers): route opencode-go/gpt-5.6-luna to /responses instead of /chat/completions (#12196) ([#13275](https://github.com/diegosouzapw/OmniRoute/pull/13275)) +- fix(dashboard): allow deleting the last extra-upstream-header row even when invalid (#12251) ([#13252](https://github.com/diegosouzapw/OmniRoute/pull/13252)) +- **fix(i18n):** translate pre-existing `__MISSING__:` keys for `combo.sort`, `requestLogger.detail` expand/collapse, `common.profile`, and `settings.resilienceCredentialHealth*` across 39 locales ([#12272](https://github.com/diegosouzapw/OmniRoute/issues/12272)) ([#13034](https://github.com/diegosouzapw/OmniRoute/pull/13034)) — thanks @HouMinXi +- fix(cli): run the Node.js runtime compatibility guard before the heavy `tsx/esm` + Commander import chain so an unsupported runtime gets a clear message instead of a raw `Invalid regular expression flags` crash (#12296) ([#13253](https://github.com/diegosouzapw/OmniRoute/pull/13253)) +- fix(dashboard): refresh the providers list after deleting a compatible provider node (#12298) ([#13256](https://github.com/diegosouzapw/OmniRoute/pull/13256)) +- fix(usage): fail closed on API-key budget enforcement when a provider's `auto` routing alias has no pricing row, instead of silently counting it as $0 (#12341) ([#13257](https://github.com/diegosouzapw/OmniRoute/pull/13257)) +- **fix(providers):** include the submitted Agnes video model when polling by `video_id` ([#12356](https://github.com/diegosouzapw/OmniRoute/pull/12356)) — thanks @pacocartones +- **fix(routing):** custom OpenAI-compatible nodes now honor the saved Chat/Responses API type after edits instead of letting the node's original ID prefix override the live connection setting ([#11884](https://github.com/diegosouzapw/OmniRoute/issues/11884)). ([#12358](https://github.com/diegosouzapw/OmniRoute/pull/12358)) — thanks @pacocartones +- fix(sse): surface an error instead of a silent empty 200 when a Claude stream closes with zero bytes (#12398) ([#13285](https://github.com/diegosouzapw/OmniRoute/pull/13285)) +- fix(oauth): warn before the dead localhost:8080 redirect in antigravity/gemini `oauth start` (#12413) ([#13265](https://github.com/diegosouzapw/OmniRoute/pull/13265)) +- fix(providers): stop devin-cli spawn error from double-closing the SSE controller (#12517) ([#13261](https://github.com/diegosouzapw/OmniRoute/pull/13261)) +- **fix(rerank):** clamp Voyage `top_k` to the documents actually sent after empty-string filtering, and honor `return_documents: false` in the NVIDIA response adapter (#12523 — thanks @pacocartones) +- **fix(audio):** `/v1/audio/translations` now resolves combo names the way `/v1/audio/transcriptions` already does, so a combo that `GET /v1/models` advertises is fanned out to its targets instead of being rejected with `400 Invalid translation model: . Use format: provider/model`; literal `provider/model` ids and unknown bare names behave as before (#12536 — thanks @pacocartones) +- **fix(gemini):** strip the JSON-Schema-2020-12 `prefixItems` keyword from Gemini tool schemas at every nesting level, so Claude Code tool definitions no longer fail with `400 Unknown name "prefixItems"` on Gemini models (#12540 — thanks @pacocartones) +- **fix(api-manager):** Expose an accessible loading status while API keys are fetched instead of an empty accessibility tree (#12541 — thanks @pacocartones) +- **fix(video):** Clamp `estimateJpegFrameBytes` at zero for padding-only payloads and build the three encode-side frame data URIs from `JPEG_FRAME_DATA_URI_PREFIX` instead of a repeated literal (#12543 — thanks @pacocartones) +- **fix(devin):** accept Windows `DEVIN_AGENTIC_HOME` sandbox paths (`C:\...\.sandbox\...`) in the isolated-home check so the Devin Claude Bridge no longer fails closed on Windows ([#12405](https://github.com/diegosouzapw/OmniRoute/issues/12405)) (#12545 — thanks @pacocartones) +- **fix(db):** Add `conversation_turn_nodes` and orphaned `agentic_conversations` to the auto-cleanup cycle under the existing `retention.callLogs` window, so identity nodes whose call-log content has already been purged no longer accumulate without bound in `storage.sqlite` (#12548 — thanks @pacocartones) +- **fix(i18n):** Wrap the `~/.claude/profiles//settings.json` placeholder in ICU single quotes in the `OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES` feature-flag description across all 42 locales and the TypeScript default, so next-intl no longer fails with `INVALID_MESSAGE: UNCLOSED_TAG` and the Feature Flags card shows the description instead of the raw key (#12549 — thanks @pacocartones) +- **fix(orchestration):** `updateCloudAgentTask` now publishes the task's real `status` on `agent.task.updated` when an update only touches `result`, `activities` or `error`, instead of the fabricated `"updated"` state, and stays silent when no row matched the id (#12550 — thanks @pacocartones) +- **fix(i18n):** the home "Recent Requests" panel and the Provider Topology legend are now translated instead of rendering English copies on non-English dashboards; the legend reads its own `home.topologyLegend*` labels with consistent casing rather than borrowing the memory-settings "Recent" and analytics "Error" strings (#12551 — thanks @pacocartones). +- **docs(reference):** bring the `FEATURE_FLAGS.md` catalog back to 1:1 with `featureFlagDefinitions.ts` — 20 missing flags added, the two `*_BLOCK_THRESHOLD` env-only knobs moved out of the flag tables, category/total counts and the Live WS port corrected, guarded by a static test (#12552 — thanks @pacocartones) +- **fix(i18n):** backfill missing `usage.kiloPass*` strings in 39 locales and restore `featureFlagOmnirouteDisableThinkingLevelVariantsDescription` in `pt.json` ([#12561](https://github.com/diegosouzapw/OmniRoute/issues/12561)) ([#13027](https://github.com/diegosouzapw/OmniRoute/pull/13027)) — thanks @HouMinXi +- fix(docker): default docker-compose app ports (dashboard/API/live-WS) to loopback instead of `0.0.0.0`, closing the anonymous `/v1` LAN/WAN exposure gap left open by `REQUIRE_API_KEY=false` (#12568) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): close DNS-rebinding SSRF gap in webhook outbound-URL guard (#12569) ([#13243](https://github.com/diegosouzapw/OmniRoute/pull/13243)) +- fix(docker): require a per-session token on the VNC browser CDP bridge and isolate it on a dedicated Docker network (#12571) ([#13241](https://github.com/diegosouzapw/OmniRoute/pull/13241)) +- fix(open-sse): write Adobe Firefly session tokens and cookie jars with 0700/0600 permissions instead of the process umask (#12572) ([#13242](https://github.com/diegosouzapw/OmniRoute/pull/13242)) +- fix(api): remove hardcoded wildcard CORS in openai-to-gemini-sse.ts so the centralized fail-closed CORS gate is the sole source of `Access-Control-Allow-Origin` (#12573) ([#13239](https://github.com/diegosouzapw/OmniRoute/pull/13239)) +- fix(api): enforce API key policy (budget/rate-limit/schedule/endpoint scoping) on the ElevenLabs speech-to-text, text-to-speech and voices proxy routes (#12574) ([#13244](https://github.com/diegosouzapw/OmniRoute/pull/13244)) +- fix(sse): cap HuggingChat NDJSON body size and bound the read loop with the fetch timeout so a stalled or hostile upstream cannot buffer unbounded memory (#12577) ([#13282](https://github.com/diegosouzapw/OmniRoute/pull/13282)) +- fix(docker): scope the cliproxyapi/qdrant/bifrost sidecars to loopback by default and forward `CLIPROXYAPI_MANAGEMENT_KEY` into the cliproxyapi container so its management API is not left both unauthenticated and LAN-published (#12578) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): create DB export temp paths with `fs.mkdtempSync` instead of predictable timestamps (#12579) ([#13237](https://github.com/diegosouzapw/OmniRoute/pull/13237)) +- Cline 401 bodies that say "re-authenticate your Cline account" classify as a refreshable OAuth token, not a terminal expired key. The cooling panel no longer labels every cooldown as a 429; it shows the recorded last error instead. (#12594) ([#13060](https://github.com/diegosouzapw/OmniRoute/pull/13060)) — thanks @HouMinXi +- **fix(catalog):** degrade unknown combo targets instead of dropping LCD modalities, and persist OpenRouter `architecture.input_modalities` into the capability snapshot ([#12613](https://github.com/diegosouzapw/OmniRoute/issues/12613)) ([#13042](https://github.com/diegosouzapw/OmniRoute/pull/13042)) — thanks @HouMinXi +- fix(providers): send `x-api-key` instead of `Authorization: Bearer` for OpenCode Zen's `/v1/responses` endpoint (Muse Spark Contributor models), fixing a 401 on OmniRoute's auth header (#12633) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(sse): surface the actionable "Auggie CLI not found" message when the shell reports a missing `auggie` binary via exit code instead of a spawn error (#12645) ([#13263](https://github.com/diegosouzapw/OmniRoute/pull/13263)) +- **fix(config):** Persist deletions of built-in background-degradation entries — when a stored settings record exists its `degradationMap` is now authoritative instead of being merged under the defaults, so an entry the user removed in the dashboard no longer reappears on the next apply or restart ([#12424](https://github.com/diegosouzapw/OmniRoute/issues/12424)) ([#12647](https://github.com/diegosouzapw/OmniRoute/pull/12647)) — thanks @pacocartones +- **fix(gamification):** action-count badge milestones (First Token, Token Consumer, Token Machine, Token Whale, and the token-sharing tier) are now backed by a durable `xp_action_counts` counter incremented in `addXp()`, instead of a live `COUNT(*)` over `xp_audit_log`. The audit log is pruned by `retention.xpAuditLog` (default 30 days), so on a default install those "lifetime" milestones were really "actions in the last 30 days" and unlocked badges could stop unlocking once old rows aged out. `getActionCount()` and `checkActionCountBadges()` now read the same durable source, and a migration backfills existing totals from the surviving audit rows ([#12546](https://github.com/diegosouzapw/OmniRoute/issues/12546)) ([#12651](https://github.com/diegosouzapw/OmniRoute/pull/12651)) — thanks @pacocartones +- **fix(images):** `/v1/images/edits` now iterates a combo's targets the same way `/v1/images/generations` does (#9239) instead of flattening a bare combo to its first target. A combo whose first target is not edit-capable — or lacks credentials — now falls through to a later edit-capable target rather than hard-erroring, and missing credentials are skipped (not a hard `401`) to match the generations path. The per-target skip/terminal classification is extracted into a shared `runImageComboTargets` loop, so generations behavior is unchanged ([#12547](https://github.com/diegosouzapw/OmniRoute/issues/12547)). ([#12653](https://github.com/diegosouzapw/OmniRoute/pull/12653)) — thanks @pacocartones +- fix(sse): add first-byte watchdog to the TLS-fingerprint transport so a stalled wreq body falls back instead of hanging for minutes (#12656) ([#13272](https://github.com/diegosouzapw/OmniRoute/pull/13272)) +- fix(sse): exempt tiny-budget reasoning probes from combo quality failure and surface persisted-cooldown skips in ALL_TARGETS_SKIPPED diagnostics (#12659) ([#13269](https://github.com/diegosouzapw/OmniRoute/pull/13269)) +- fix(models): declare the real ~1M contextLength for OpenCode Zen's Muse Spark 1.2 models instead of falling back to the 200000 provider default (#12681) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(oauth): align codebuddy-cn OAuth User-Agent with the chat/usage CLI version to avoid WAF false positives (#12702) ([#13264](https://github.com/diegosouzapw/OmniRoute/pull/13264)) +- fix(dashboard): surface an authentication-required banner instead of silently blanking database settings for a guest session (#12709) ([#13270](https://github.com/diegosouzapw/OmniRoute/pull/13270)) +- **fix(resilience):** stop unbounded queue that hangs 6min until Aborted — gate, provider slot, and Bottleneck queue now share a per-connection `maxWaitMs` budget; `fail-closed` on exhaust (503); execution backstop `executionMaxWaitMs` overridable per connection with upstream clamp ([#12715](https://github.com/diegosouzapw/OmniRoute/pull/12715)) — thanks @maxmad64bis (with thanks to @Tushar49 for surfacing the slow-provider need in #12635) +- **fix(auto-combo):** every mode pack now carries `quality` and `reliability` weights (`quality` 0.02, 0.03 in `quality-first`; `reliability` 0.03, 0.04 in `reliability-first`), so selecting a pack no longer silences either signal; `DEFAULT_WEIGHTS` is unchanged ([#12731](https://github.com/diegosouzapw/OmniRoute/pull/12731)) — thanks @maxmad64bis +- **fix(sse):** the provider execution pipeline keeps the upstream error `code`/`type` again — both were lost when the non-streaming leg was extracted, so a config-class `422` (Antigravity missing project) degraded into a generic account cooldown instead of reaching the gate that recognises it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(db):** a legacy `call_logs` table no longer aborts startup — the provider-stats index is created after the column healing runs, not before it, so an install predating `request_type` opens instead of failing with `no such column` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(catalog):** a malformed row in the operator-writable custom-models blob no longer kills every `auto/*` pool ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(tests):** realign the guards that the same day's merges left asserting the old behavior — the provider-count total after GreenPT and EURouter, the GLM stream buffer slot that `#12925` turned into a declared parameter, and the injection scan bound that `#13104` reshaped from a head-only window into head-plus-tail ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- fix(cache): fold tool_choice/tools/response_format into the semantic cache signature so a cached tool_calls response can no longer be replayed for a request whose tool policy forbids it (#12734) ([#13267](https://github.com/diegosouzapw/OmniRoute/pull/13267)) +- **fix(models):** stop treating provider-supplied `isFree:true`/`:free`/`0/0` flags as trusted unless the provider has a documented free tier — only the shipped free-tier catalog decides otherwise; fetched `isFree:true` trusted only for a free-tier provider, custom `isFree:true` via a trusted path ([#12744](https://github.com/diegosouzapw/OmniRoute/pull/12744)) — thanks @maxmad64bis +- fix(memory): authenticate the internal /v1/rerank loopback call so memory reranking no longer silently degrades to unranked order when REQUIRE_API_KEY=true (#12745) ([#13268](https://github.com/diegosouzapw/OmniRoute/pull/13268)) +- fix(sse): parse Ollama Cloud's current usage markup (`$X of $Y used` aria-label, nested width style) (#12749) ([#13260](https://github.com/diegosouzapw/OmniRoute/pull/13260)) +- fix(cli): setup-opencode no longer sends an active context's management token to `/v1/models` when `--api-key`/`OMNIROUTE_API_KEY` is supplied — an explicit flag or the env var now always outranks the context's token, and the flag itself is no longer swallowed by the parent program's global `--api-key` option (#12783) ([#13246](https://github.com/diegosouzapw/OmniRoute/pull/13246)) +- fix(sse): register Arcee AI in the executor provider registry so requests reach api.arcee.ai instead of silently falling back to OpenAI (#12784) ([#13277](https://github.com/diegosouzapw/OmniRoute/pull/13277)) +- **fix(quota):** share one upstream quota read when identical saturation checks arrive at the same time, so traffic bursts don't multiply provider API calls ([#12787](https://github.com/diegosouzapw/OmniRoute/pull/12787)) — thanks @maxmad64bis +- **fix(db):** ignore expired or invalid rate-limit cooldown writes so a stale timestamp can't lock a connection that should be usable — clearing still works as before ([#12788](https://github.com/diegosouzapw/OmniRoute/pull/12788)) — thanks @maxmad64bis +- **fix(providers):** single source for provider order with `xao` ranking alongside `xai-oauth` ([#12790](https://github.com/diegosouzapw/OmniRoute/pull/12790)) — thanks @maxmad64bis +- **fix(routing):** off-table models check the free-model catalog before inheriting premium prices, latency bootstraps from the observed pool median, async tiers read live database pricing with a 90-day freshness gate, and the tier cache invalidates on every pricing write ([#12792](https://github.com/diegosouzapw/OmniRoute/pull/12792)) — thanks @maxmad64bis +- **fix(auto-combo):** snapshot scoring uses the observed per-connection account tier and per-model quality instead of neutral constants, and circuit-open providers rank lower at build time ([#12794](https://github.com/diegosouzapw/OmniRoute/pull/12794)) — thanks @maxmad64bis +- **fix(pool):** empty `auto/*` pools now say why they are empty, and the models listing uses the same paid check as routing ([#12795](https://github.com/diegosouzapw/OmniRoute/pull/12795)) — thanks @maxmad64bis +- fix(routing): recognize CLIProxyAPI's 'unknown provider for model' 400 as fallback-worthy (#12800) ([#13284](https://github.com/diegosouzapw/OmniRoute/pull/13284)) +- **fix(sse):** translate-mode streams now emit the estimated token counts as a trailing usage-only chunk before `[DONE]` when the upstream stays silent, so metered chat clients see totals instead of nothing ([#12828](https://github.com/diegosouzapw/OmniRoute/pull/12828)) — thanks @maxmad64bis +- **fix(db):** provider stats stay truthful on empty and legacy databases: fallback counts default to `0` instead of `null`, latency averages read `null` (not `0`) when no durations were recorded, failures logged before the error-type column existed group under `pre_migration` instead of `unclassified`, and per-provider queries use two new composite indexes ([#12832](https://github.com/diegosouzapw/OmniRoute/pull/12832)) — thanks @maxmad64bis +- fix(nvidia): fail open when a synced model catalog goes stale instead of gating forever (#12849) ([#13248](https://github.com/diegosouzapw/OmniRoute/pull/13248)) +- **fix(db):** WAL maintenance lives in its own module: a `TRUNCATE` checkpoint that hits a busy database now warns with its streak and retries once via `PASSIVE` instead of logging success, and closing-time checkpoints no longer report success on builds without a database file, with the busy totals visible in the authenticated monitoring health payload ([#12853](https://github.com/diegosouzapw/OmniRoute/pull/12853)) — thanks @maxmad64bis +- **fix(health):** quota with no snapshots reads empty instead of a contradicting 0% ([#12857](https://github.com/diegosouzapw/OmniRoute/pull/12857)) — thanks @maxmad64bis +- fix(a2a): accept the dashboard's own session cookie on /a2a so "Run message/send" no longer fails with "Unauthorized: missing or invalid API key" (#12888) ([#13271](https://github.com/diegosouzapw/OmniRoute/pull/13271)) +- **fix(logs):** the Logs grid's in-memory filter pass no longer discards rows the SQL query already matched — selecting an API key from the dropdown (which sends the key's id) returns its calls again, the Combo tab shows every combo instead of only those whose name contains a "1", and the model filter and search cover the same columns as the query ([#12896](https://github.com/diegosouzapw/OmniRoute/pull/12896)) — fixes [#12873](https://github.com/diegosouzapw/OmniRoute/issues/12873) — thanks @ntdatt812 +- **fix(a2a):** `/api/a2a/status` now builds the agent card from the request that asked for it, so a gateway reached at a non-localhost host no longer advertises `http://localhost:20128` as its A2A URL ([#12918](https://github.com/diegosouzapw/OmniRoute/pull/12918)). — thanks @ntdatt812 +- **fix(compression):** progressive aging now appends its `[COMPRESSED:aging:…]` annotation after a turn's `tool_result` blocks instead of in front of them, so Anthropic no longer rejects aged conversations with "`tool_use` ids were found without `tool_result` blocks immediately after" ([#12920](https://github.com/diegosouzapw/OmniRoute/pull/12920)). — thanks @ntdatt812 +- **fix(bedrock):** model import now resolves context limits for every vendor prefix instead of only `anthropic.*`, so `global.openai.gpt-5.6-*` no longer imports with a null `inputTokenLimit` and gets rejected pre-flight at the 200k default ([#12921](https://github.com/diegosouzapw/OmniRoute/pull/12921)). — thanks @ntdatt812 +- **fix(stream):** the 64 KB stream buffer GLM asks for is honoured instead of dropped, and the type error it caused no longer fails the API Route Typecheck gate on every open PR ([#12925](https://github.com/diegosouzapw/OmniRoute/pull/12925)) — thanks @ntdatt812 +- **fix(guardrails):** mask PII inside a `tool_result`'s nested content array, which the masker walked past while redacting its sibling block ([#12930](https://github.com/diegosouzapw/OmniRoute/pull/12930)) — thanks @ntdatt812 +- **fix(dashboard):** the Radar catalog table no longer leaves absent data unexplained — empty limits, unknown context windows, and unreported capabilities each explain themselves on hover, and a new check keeps it that way ([#12937](https://github.com/diegosouzapw/OmniRoute/pull/12937)) — thanks @maxmad64bis +- **fix(sse):** geo-blocked opencode requests rotate to the next account proxy instead of failing, so one refused egress no longer aborts the whole chain ([#12941](https://github.com/diegosouzapw/OmniRoute/pull/12941)) — thanks @maxmad64bis +- **fix(codex):** dashboard "clear cooldown" and the CAS recovery path now drop nested `codexScopeRateLimitedUntil` maps in the same write that nulls `rate_limited_until`, and fresh quota snapshots with headroom lift fallback-sourced scope cooldowns parked by quota preflight ([#12817](https://github.com/diegosouzapw/OmniRoute/issues/12817), [#12860](https://github.com/diegosouzapw/OmniRoute/issues/12860), [#12951](https://github.com/diegosouzapw/OmniRoute/pull/12951)) The dashboard reset-credit button (`consumeCodexResetCredit`) now exercises that same snapshot path after a successful redeem, so a filled quota bar is enough to unpark a leftover fallback Codex child without another manual clear. — thanks @HouMinXi +- **fix(db):** resolve `sql-wasm.wasm` across global npm install and hoisted layouts, ensuring OmniRoute can boot cleanly on Node 24 when native `better-sqlite3` is uncompiled. ([#13035](https://github.com/diegosouzapw/OmniRoute/pull/13035)) — thanks @HouMinXi +- **fix(api):** `DELETE /v1/batches/delete-completed` now sweeps only the calling API key's own completed batches (batches with no owner stay out of a key-scoped sweep on purpose), with an explicit instance-wide mode reserved for authenticated dashboard sessions, a 401 for a presented key that is unknown, revoked, deactivated, banned or expired (never falling through to the session branch), audit logging of every sweep, a sanitized 500 on failure, an owner-scoped file half (a key-scoped sweep never nulls a file another tenant owns) and an atomic sweep — chunked in 200-batch transactions in instance mode — so a mid-way error never leaves a batch pointing at a nulled file (GHSA-wvxc-jp3v-5mg5) ([#12969](https://github.com/diegosouzapw/OmniRoute/pull/12969)) +- **fix(combo):** quota-weighted routing stops drawing on an out-of-credit connection — a 402 now invalidates the stored quota snapshot instead of leaving its stale remaining percentage in place, and a snapshot older than 10 minutes no longer counts as confident headroom for the primary pool ([#12972](https://github.com/diegosouzapw/OmniRoute/pull/12972)) ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- **fix(vision):** Custom Models with "Vision capable" checked no longer have image requests swapped to `glm/glm-4.6v` when the client sends the advertised alias (`vllm/path/...`) or the bare path-shaped id — Vision Bridge now matches the stored override for all three id forms ([#12758](https://github.com/diegosouzapw/OmniRoute/issues/12758)) ([#12974](https://github.com/diegosouzapw/OmniRoute/pull/12974)) — thanks @HouMinXi +- **fix(sse):** transient opencode upstream failures rotate to the next account proxy instead of failing, so one flapping egress no longer aborts the whole chain ([#12975](https://github.com/diegosouzapw/OmniRoute/pull/12975)) — thanks @maxmad64bis +- **fix(azure):** Deployments from GPT-6 onward now send `max_completion_tokens` instead of `max_tokens`, which Azure rejects with HTTP 400. The rule matched a literal `gpt-5`, so each new generation arrived broken; it now matches the generation range, while `gpt-35-turbo` still keeps `max_tokens`. ([#13007](https://github.com/diegosouzapw/OmniRoute/pull/13007)) — thanks @ntdatt812 +- **fix(db):** add an opt-in self-restart circuit for sustained critical memory pressure, gate post-cleanup VACUUM behind a minimum freed-rows threshold, and checkpoint the SQLite WAL every 5 minutes with a size guard that escalates to TRUNCATE, so a growing WAL can no longer stall the event loop into a full outage. ([#13011](https://github.com/diegosouzapw/OmniRoute/pull/13011)) — thanks @HouMinXi +- **fix(auth):** an explicit connection pin may probe a quota-disabled row once and re-enable it on success ([#12874](https://github.com/diegosouzapw/OmniRoute/issues/12874)) ([#13017](https://github.com/diegosouzapw/OmniRoute/pull/13017)) — thanks @HouMinXi +- **fix(api):** thread `X-OmniRoute-Fallback-Attempts` through combo chat completions so streaming and non-streaming responses report how many prior legs were attempted ([#13038](https://github.com/diegosouzapw/OmniRoute/pull/13038)) — thanks @HouMinXi +- Restore provider failure classification and credential refresh on non-streaming requests: classify non-2xx failures to lock models on per-model quota exhaustion, update connection rate limits from headers and body, and pass credential refresh handlers to pipeline execution so 401 tokens can be refreshed and retried (#13043). ([#13069](https://github.com/diegosouzapw/OmniRoute/pull/13069)) — thanks @HouMinXi +- **fix(responses):** wrap forced-non-streaming web_search fallback JSON as Responses SSE so Codex still sees `response.completed` ([#13050](https://github.com/diegosouzapw/OmniRoute/pull/13050)) — thanks @HouMinXi +- **fix(antigravity):** Preserve upstream thought-token usage in normalized completion and reasoning token counts ([#13055](https://github.com/diegosouzapw/OmniRoute/pull/13055)) — thanks @pacocartones +- Fix a concurrency-slot leak in the DeepSeek PoW solver: a worker that failed to spawn (for example a missing worker script) never released its slot, so `MAX_CONCURRENT_WORKERS` failures disabled the solver until restart. ([#13097](https://github.com/diegosouzapw/OmniRoute/pull/13097)) — thanks @anhtahaylove +- **fix(acp):** bound the ACP session output buffers — `stdoutBuffer` and `stderrBuffer` now cap at 1 MiB keeping the most recent output behind a visible `[...output truncated...]` marker, and `stderrBuffer` is reset per prompt instead of accumulating for the lifetime of the session. ([#13100](https://github.com/diegosouzapw/OmniRoute/pull/13100)) — thanks @anhtahaylove +- **fix(acp):** release the `stdout`/`exit` listeners and the idle timer that a `sendPrompt` timeout used to leave attached to the `acpManager` singleton, and drop sessions that exited on their own from the session map instead of keeping them forever. ([#13096](https://github.com/diegosouzapw/OmniRoute/pull/13096)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection and PII scanners now read the text a `tool_result` block carries on `content` (string or nested block list), in messages and in system blocks, so tool output is judged by the same rules as user text ([#13101](https://github.com/diegosouzapw/OmniRoute/pull/13101)) — thanks @ntdatt812 +- **fix(gamification):** close the badge notification SSE stream when the request signal is already aborted before the stream starts — a client that disconnects while the route is still awaiting auth used to leave both the 2s unlock poll and the 15s heartbeat running for the lifetime of the process. ([#13106](https://github.com/diegosouzapw/OmniRoute/pull/13106)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection scan now spends its 16 KB budget on both ends of the request instead of the first 16 KB only, so `system`, `instructions`, `query`, `documents` and the newest turns are no longer hidden behind one long message ([#13104](https://github.com/diegosouzapw/OmniRoute/pull/13104)) — thanks @ntdatt812 +- **fix(dashboard):** expose the Volcano Ark console cookie on quota scraping and unwrap connect-error objects so the dashboard shows the upstream message ([#13107](https://github.com/diegosouzapw/OmniRoute/pull/13107)) — thanks @HouMinXi +- **fix(db):** release the `beforeExit`/`SIGINT`/`SIGTERM` handlers when a `node:sqlite` adapter closes, so a closed adapter and its database handle are no longer pinned to `process` for the lifetime of the run — the same treatment #7494 gave the sql.js adapter. ([#13109](https://github.com/diegosouzapw/OmniRoute/pull/13109)) — thanks @anhtahaylove +- **fix(translator):** `contentSchema` and `unevaluatedItems` are now treated as subschema positions by the tool-schema sanitizer, so a truncation placeholder in either is replaced with a permissive schema instead of being forwarded as a string ([#13110](https://github.com/diegosouzapw/OmniRoute/pull/13110)) — thanks @ntdatt812 +- **fix(cli-helper):** clear the `createLogStream` timeout on the abort path — `stop()` aborts the in-flight fetch and returned through the `signal.aborted` branch, which skipped `clearTimeout` and left an armed timer per stopped stream. The stream reader is now also cancelled when the read loop exits early. ([#13114](https://github.com/diegosouzapw/OmniRoute/pull/13114)) — thanks @anhtahaylove +- **fix(docs):** drop TLS-impersonation, MITM-decrypt, supply-chain attestation, and XOR-mask writeups from the public `/docs` catalog and Docker image. Files stay in git for engineers; operators who need them open the repo, not the website. ([#13136](https://github.com/diegosouzapw/OmniRoute/pull/13136)) — thanks @HouMinXi +- **fix(combo):** parse numeric-epoch `rate_limited_until` in the combo cooldown read path ([#13141](https://github.com/diegosouzapw/OmniRoute/pull/13141)) — thanks @maxmad64bis +- **fix(opencode):** both OpenCode plugins now reject a gateway address typed without `http://` at configuration time, instead of publishing every model with an api url no client can call, and the v2 plugin no longer publishes a model card whose api url is blank or relative ([#13142](https://github.com/diegosouzapw/OmniRoute/pull/13142)) — thanks @maxmad64bis +- **fix(providers):** lock opencode model on upstream 400 model-unavailable ([#13146](https://github.com/diegosouzapw/OmniRoute/pull/13146)) — thanks @maxmad64bis +- **fix(logging):** keep the provider exchange rather than the raw client bodies when a call log exceeds its size budget, and show that recovered payload in the request-detail panel instead of replacing it with the stored response body ([#13147](https://github.com/diegosouzapw/OmniRoute/pull/13147)) — thanks @maxmad64bis +- **fix(traffic-inspector):** the WebSocket route no longer leaks a traffic-buffer subscriber and a 30s ping timer when the client socket is already closed at handler time — listeners are attached before any resource is acquired, a destroyed socket bails out early, and the ping interval stops on a dead socket where `write()` never throws ([#13155](https://github.com/diegosouzapw/OmniRoute/pull/13155)) — thanks @anhtahaylove +- **fix(telegram):** bound the per-user API key cache in the Telegram chat proxy so a burst of distinct chat ids can no longer grow the process heap without limit ([#13165](https://github.com/diegosouzapw/OmniRoute/issues/13165)) ([#13166](https://github.com/diegosouzapw/OmniRoute/pull/13166)) — thanks @anhtahaylove +- **fix(stream):** cancel the upstream response body when the JSON-to-SSE sniff unwinds on a body timeout, so a stalled upstream no longer pins the connection ([#13169](https://github.com/diegosouzapw/OmniRoute/issues/13169)) ([#13171](https://github.com/diegosouzapw/OmniRoute/pull/13171)) — thanks @anhtahaylove +- **fix(telegram):** authenticate webhook deliveries with Telegram's `secret_token` so an unauthenticated caller can no longer mint API keys or spend upstream quota ([#13172](https://github.com/diegosouzapw/OmniRoute/issues/13172)) ([#13175](https://github.com/diegosouzapw/OmniRoute/pull/13175)) — thanks @anhtahaylove +- **fix(models):** give discoverable Gemini 3.8 Flash ids their own 65536 output spec so Antigravity no longer clamps them to 16384 ([#13195](https://github.com/diegosouzapw/OmniRoute/pull/13195)) — thanks @HouMinXi +- **fix(dashboard):** leftover catalog-removed provider rows (gemini-cli) can be listed and purged from the providers page ([#13067](https://github.com/diegosouzapw/OmniRoute/issues/13067)) ([#13197](https://github.com/diegosouzapw/OmniRoute/pull/13197)) — thanks @HouMinXi +- **fix(auth):** a dashboard session now requires the `authenticated: true` claim that login, OIDC and the session refresh already emit — a JWT merely signed with `JWT_SECRET` (for example the Cursor CLI passthrough token, which any API-key holder can obtain) no longer verifies as the `auth_token` cookie on any route, the WebSocket handshake or the live server; existing sessions keep working ([#13298](https://github.com/diegosouzapw/OmniRoute/issues/13298)) ([#13375](https://github.com/diegosouzapw/OmniRoute/pull/13375)) +- **fix(api):** `DELETE /v1/batches/delete-completed` now applies the caller's API-key policy (endpoint allowlist, schedule, usage cap, rate limit) like every other `/v1` route, commits the key-scoped sweep per 200-batch chunk instead of holding one write lock for the whole sweep, refuses to loop without progress, and rejects a scope that names both a key and `allTenants` ([#13684](https://github.com/diegosouzapw/OmniRoute/pull/13684)) +- Restore the API-route typecheck gate: the non-streaming leg result lost its discriminated-union narrowing after the server-owned tool loop reassignment, producing 13 new TS2339 diagnostics in `chatCore.ts`. ([#12963](https://github.com/diegosouzapw/OmniRoute/pull/12963)) +- Restore four non-streaming behaviours the server-owned tool loop refactor dropped: client aborts map to 499 with the fixed `Request aborted` message, an aborted request no longer logs a synthetic `clientResponse`, Claude prompt-cache telemetry is recorded again, and a body that cannot be canonicalized no longer throws when the tool loop is off. ([#12990](https://github.com/diegosouzapw/OmniRoute/pull/12990)) +- **fix(skills):** The CLI registry parser now reads positionals declared with `.addArgument()`, not only those written inline in `.command()`. `tunnel create [type]` was being published as `tunnel create`, so the agent-skills sync gate reported drift on every branch and regenerating would have deleted the argument. ([#13009](https://github.com/diegosouzapw/OmniRoute/pull/13009)) — thanks @ntdatt812 +- **fix(combos):** dashboard combo test uses a short prompt, serial probes, and a 60s timeout so reasoning models and rate-limited free pools do not fail the health check ([#13001](https://github.com/diegosouzapw/OmniRoute/pull/13001)) — thanks @HouMinXi +- fix(compression): terminate idle worker threads on eviction so long-running instances stop leaking OS threads and MessagePorts ([#13091](https://github.com/diegosouzapw/OmniRoute/pull/13091)) — thanks @anhtahaylove +- Redact Google API keys of any length in error bodies: the pattern required exactly 39 characters, so shorter or longer `AIza…` credentials passed through unredacted. ([#12964](https://github.com/diegosouzapw/OmniRoute/pull/12964)) +- **fix(grok-cli):** a 402 "Grok Build usage balance exhausted" parks that Grok login as out of credit (Grok Build CLI, grok.com cookie, and xAI OAuth share the weekly pool). Combo routing then tries the next login instead of locking the model for every account in the pool ([#13061](https://github.com/diegosouzapw/OmniRoute/pull/13061)) — thanks @HouMinXi +- fix(compression): spawn the LLMLingua worker with a file URL object so compression actually runs instead of silently failing open on Node ([#13093](https://github.com/diegosouzapw/OmniRoute/pull/13093)) — thanks @anhtahaylove +- **fix(test):** run the local `test` and `test:unit` scripts at concurrency 4 so a full-suite run no longer exhausts the machine's commit charge and kills unrelated processes ([#13187](https://github.com/diegosouzapw/OmniRoute/pull/13187)) — thanks @anhtahaylove +- **fix(monitoring):** `GET /api/monitoring/health` `credentialHealth` now includes a bounded `failedConnections` list (`connectionId`, `status`, sanitized `lastError`) when the probe-cache gauge `failed>0`, plus `source: probe-cache` and a cheap `staleDbNonOkCount` for sticky SQLite `test_status` on active rows. Documents that the live gauge is not `provider_connections.test_status`. ([#12876](https://github.com/diegosouzapw/OmniRoute/pull/12876)) — thanks @RaviTharuma +- **fix(resilience):** an openai-compatible multi-upstream gateway no longer marks the whole connection `credits_exhausted` when a single upstream (or the `/models` / representative-model health probe) returns 402. Quota failures stay model-scoped; true connection-wide auth failures are unchanged. Single-credential 402 key disable ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239)) is preserved. ([#12875](https://github.com/diegosouzapw/OmniRoute/pull/12875)) — thanks @RaviTharuma +- **fix(dashboard):** nine fixes on the Orchestration Canvas, all diagnosed in the Phase 2 reviews. A source that fails now keeps the timestamp of its FIRST failure instead of being re-stamped every poll — the stale line said "since the last poll" no matter how long the source had been down, and the churn also defeated the snapshot's stable identity (it serializes the source list), so the canvas re-rendered on every tick while anything was broken. A source that HAD data and only then started failing is flagged too: previously only a source with no node at all got the warning, so a source that went down mid-session kept a healthy-looking node forever. The rest are pointwise: clicking a filter chip cancels the pending search debounce (left armed, it fired ~300ms later and silently reverted the chip); the search input carries an accessible name; `?state=running, failed` parses like the unpadded form instead of dropping the padded value; the CSV toggle helper is defined once in `model/urlParams.ts` rather than twice; the agents tab tells "nothing running" apart from "the filter matched nothing", offering a clear-filters button instead of setup links that would be wrong advice there; edges stop emitting SMIL particles above 40 simultaneously active edges, keeping the colored stroke; and the drawer's error banner clears when a retried action succeeds. ([#12983](https://github.com/diegosouzapw/OmniRoute/pull/12983)) +- **fix(i18n):** the nine locales added with the EU-language batch (Greek, Estonian, Irish, Croatian, Lithuanian, Latvian, Maltese, Slovenian, Serbian) were missing the eleven Orchestration Canvas keys that Phase 3 introduced, so the compare-runs panel and the "no runs match these filters" empty state fell back to English in those languages (`deepMergeFallback` substitutes English for an absent key, so nothing rendered blank — it rendered untranslated). The coverage gate does not catch this: it enforces an 80% floor per locale, and eleven missing keys out of ~13,000 leaves coverage at 99.9%. Translated for real in each language, calibrated against the wording each file already uses for "run", "filter" and "skill". ([#13294](https://github.com/diegosouzapw/OmniRoute/pull/13294)) +- **fix(dashboard):** five follow-ups from the Phase 2 Orchestration Canvas review. The History drawer now shows the memory section for runs that already left the live TTL window: the persisted `memory_hits` event is parsed into `metadata.memoryHits` with the same defensive validation the drawer applies, and — because that event is observability rather than a state transition — it no longer leaks into the timeline, where it had been inheriting the task's state and rendering as a duplicate transition. The A2A memory recall runs against its own 1.5s deadline instead of inheriting the memory backend's 30s one; overshooting degrades exactly like any other recall failure (no hits, task proceeds), and the timer is cleared on both paths. Repeating a Conductor run carries its `requirements.cli`/`requirements.model` forward, so the new run is pinned to the same runner profile and model rather than drifting to whatever the fleet picks. A successful repeat from the Agents tab now focuses the run it created, instead of leaving the operator on the finished one. And the auth test for `POST /api/conductor/tasks` moved into the shared `ROUTES` array rather than restating the pattern. ([#12988](https://github.com/diegosouzapw/OmniRoute/pull/12988)) +- fix(plugins): stop leaking an exit listener per plugin hook timeout, which triggered MaxListenersExceededWarning on plugins that ignore SIGTERM ([#13092](https://github.com/diegosouzapw/OmniRoute/pull/13092)) — thanks @anhtahaylove +- **fix(validation):** Provider node edits no longer fail with a generic "Invalid request" when the optional daily-quota reset fields are left blank. The dashboard sends `dailyQuotaResetTimezone` and `dailyQuotaResetHour` as `null`, and only the hour accepted it. ([#13066](https://github.com/diegosouzapw/OmniRoute/issues/13066)) ([#13083](https://github.com/diegosouzapw/OmniRoute/pull/13083)) — thanks @ntdatt812 +- **fix(combo):** quota-aware expansion drops banned, inactive, missing, and wrong-provider connections before quota fetch or model dispatch; pins and allowlists stay selectors, not a bypass. Antigravity automatic exhaustion now requires a reported zero remaining, so a positive balance below 1% stays eligible. ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- Fix an intermittent failure in the quota-weighted routing test suite: scores are a function of `Date.now()`, so peers with identical quota scored microseconds apart never tied and swapped order. ([#12946](https://github.com/diegosouzapw/OmniRoute/pull/12946)) +- fix(volcengine): resolve quota window mapping gaps, safe multi-connection console binding, and response cookie sanitization ([#12950](https://github.com/diegosouzapw/OmniRoute/pull/12950)) — thanks @HouMinXi +- **fix(test):** resolve the WebDAV handler path with `fileURLToPath` so the suite's 37 WebDAV tests run on Windows instead of failing with a doubled `C:\C:\` drive prefix ([#13196](https://github.com/diegosouzapw/OmniRoute/pull/13196)) — thanks @anhtahaylove +- **fix(ui):** fall back from invalid AnySearch icon alias (direct commit `9587e07b69`) +- **fix(sse):** stop rebuilding a truncated summary from the collector's cap-dropped event array ([#12718](https://github.com/diegosouzapw/OmniRoute/pull/12718)) — thanks @hartmark +- **fix(conversations):** show a pending spinner for unresolved tool nodes instead of "(empty)" ([#12727](https://github.com/diegosouzapw/OmniRoute/pull/12727)) — thanks @hartmark +- **fix(streaming):** fail fast when an upstream stream produces only lifecycle/heartbeat events, never real content ([#12741](https://github.com/diegosouzapw/OmniRoute/pull/12741)) — thanks @hartmark +- **fix(responses-continuation):** bridge the write-in-flight window with an in-memory pending store ([#12854](https://github.com/diegosouzapw/OmniRoute/pull/12854)) — thanks @hartmark +- **fix(translator):** drop or map agent_message on Chat Completions fallback ([#12880](https://github.com/diegosouzapw/OmniRoute/pull/12880)) — thanks @RaviTharuma +- **fix(api):** page and stream-complete GET /v1/models for large catalogs ([#12882](https://github.com/diegosouzapw/OmniRoute/pull/12882)) — thanks @RaviTharuma +- **fix(quota):** align AUTH window labels with usage API and clarify 503 ([#12884](https://github.com/diegosouzapw/OmniRoute/pull/12884)) — thanks @RaviTharuma +- **fix(ci):** clear two base-reds on release/v3.8.51 — mutation-coverage gate + image-only-model guard ([#12945](https://github.com/diegosouzapw/OmniRoute/pull/12945)) +- **fix(api):** scope batch bulk-delete to the calling API key ([#13211](https://github.com/diegosouzapw/OmniRoute/pull/13211)) +- **fix(tests):** align model-sync log assertion to shipped path redaction (base-red) ([#13227](https://github.com/diegosouzapw/OmniRoute/pull/13227)) +- **fix(test):** make the ToS heading guard actually require the parentheses ([#13228](https://github.com/diegosouzapw/OmniRoute/pull/13228)) +- **fix(quality):** register 4 drifted covering tests in stryker tap.testFiles ([#13229](https://github.com/diegosouzapw/OmniRoute/pull/13229)) +- **fix(logging):** mask *-api-key headers in request-log pipeline (#13273) ([#13401](https://github.com/diegosouzapw/OmniRoute/pull/13401)) — thanks @KooshaPari +- **fix(tests):** correct stale Jina catalog prefix in custom-models test ([#13403](https://github.com/diegosouzapw/OmniRoute/pull/13403)) — thanks @KooshaPari +- **fix(backend):** prune old backups after health-check-repair snapshots (#13308) ([#13404](https://github.com/diegosouzapw/OmniRoute/pull/13404)) — thanks @KooshaPari +- **fix(backend):** guard memory_fts_au trigger with WHEN clause to stop FTS bloat (#13326) ([#13405](https://github.com/diegosouzapw/OmniRoute/pull/13405)) — thanks @KooshaPari +- **fix(free-tiers):** remove obsolete Together $25 signup credit (#12526) ([#13407](https://github.com/diegosouzapw/OmniRoute/pull/13407)) — thanks @KooshaPari +- **fix:** skip non-chat generation models in Test all models ([#13409](https://github.com/diegosouzapw/OmniRoute/pull/13409)) — thanks @KooshaPari +- **fix(providers):** de-duplicate agy and antigravity model catalogs ([#13410](https://github.com/diegosouzapw/OmniRoute/pull/13410)) — thanks @KooshaPari +- **fix(dashboard):** send provider in body for mini-playground web search ([#13411](https://github.com/diegosouzapw/OmniRoute/pull/13411)) — thanks @KooshaPari +- **fix(resilience):** add CJK quota-exhaustion patterns to 429 classifier ([#13412](https://github.com/diegosouzapw/OmniRoute/pull/13412)) — thanks @KooshaPari +- **fix(backend):** eval runner: failed calls no longer score as passed + dotAll regex ([#13413](https://github.com/diegosouzapw/OmniRoute/pull/13413)) — thanks @KooshaPari +- **fix(backend):** eval runner opts out of output-style and memory injection ([#13414](https://github.com/diegosouzapw/OmniRoute/pull/13414)) — thanks @KooshaPari +- **fix:** chain serial quarantine step in npm test script (fixes #13286) ([#13418](https://github.com/diegosouzapw/OmniRoute/pull/13418)) — thanks @KooshaPari +- **fix(antigravity):** derive stable session ID from credentials for prompt-cache reuse (#13305) ([#13424](https://github.com/diegosouzapw/OmniRoute/pull/13424)) — thanks @KooshaPari +- **fix(memory):** merge memory into top-level system field instead of unshifting at messages[0] (#13425) ([#13427](https://github.com/diegosouzapw/OmniRoute/pull/13427)) — thanks @KooshaPari +- **fix(compression):** preserve instruction blocks from lossy engines ([#13523](https://github.com/diegosouzapw/OmniRoute/pull/13523)) — thanks @KooshaPari +- **fix(compression):** preserve polarity words and newlines in ultra heuristic ([#13524](https://github.com/diegosouzapw/OmniRoute/pull/13524)) — thanks @KooshaPari +- **fix(compression):** run user preservePatterns before built-in patterns (#13457) ([#13525](https://github.com/diegosouzapw/OmniRoute/pull/13525)) — thanks @KooshaPari +- **fix(reasoning):** expand deepseek-v4 regex to match v4.1+ and add command-code provider (#13430) ([#13528](https://github.com/diegosouzapw/OmniRoute/pull/13528)) — thanks @KooshaPari +- **fix(providers):** add alwaysReasons metadata for reka-flash-3 ([#13531](https://github.com/diegosouzapw/OmniRoute/pull/13531)) — thanks @KooshaPari +- **fix(playground):** allow Compare columns to scroll independently ([#13532](https://github.com/diegosouzapw/OmniRoute/pull/13532)) — thanks @KooshaPari +- **fix(security):** empty whitelist allows through to prevent admin lockout ([#13534](https://github.com/diegosouzapw/OmniRoute/pull/13534)) — thanks @KooshaPari +- **fix(tests):** add DISABLE_IOREG_STRATEGY env var for macOS test compatibility (fixes #13467) ([#13539](https://github.com/diegosouzapw/OmniRoute/pull/13539)) — thanks @KooshaPari +- **fix(zed-hosted):** map developer role and Gemini enums for Zed proxy ([#13543](https://github.com/diegosouzapw/OmniRoute/pull/13543)) — thanks @KooshaPari +- **fix(analytics):** normalize provider aliases to canonical id in usage_history ([#13545](https://github.com/diegosouzapw/OmniRoute/pull/13545)) — thanks @KooshaPari +- **fix(combo):** use traceId for call log id to prevent UNIQUE constraint failure ([#13546](https://github.com/diegosouzapw/OmniRoute/pull/13546)) — thanks @KooshaPari +- **fix(auth):** check model lockout before returning synthetic noauth connection ([#13547](https://github.com/diegosouzapw/OmniRoute/pull/13547)) — thanks @KooshaPari +- **fix(guardrails):** skip credential redaction for base64 image data URLs ([#13550](https://github.com/diegosouzapw/OmniRoute/pull/13550)) — thanks @KooshaPari +- **fix(resilience):** increase requestQueue.maxWaitMs default from 15s to 30s ([#13553](https://github.com/diegosouzapw/OmniRoute/pull/13553)) — thanks @KooshaPari +- **fix(docs):** document OMNIROUTE_READY_TIMEOUT_MS and allowlist the test-only DISABLE_IOREG_STRATEGY ([#13692](https://github.com/diegosouzapw/OmniRoute/pull/13692)) + ### 📝 Maintenance ---- +- **docs(readme):** reconcile live v3.8.50 provider, free-tier, CLI, routing, test, community, sponsor, acknowledgment, and SVG metrics with their audited source denominators, including a deduplicated OmniRoute-in-Action snapshot and distinct contributor rankings for merged pull requests, GitHub-attributed commits, and Git history ([#11356](https://github.com/diegosouzapw/OmniRoute/pull/11356)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **docs:** sync the canonical provider count 354 → 356 across `README.md`, `AGENTS.md`, `llm.txt` (+ 42 i18n mirrors), the four README SVG diagrams, `docs/reference/PROVIDER_REFERENCE.md` (regenerated) and the `package.json` description after Opper (#11629) and 1min.ai (#11631) boarded the catalog — closes the `check:docs-counts-sync` strict drifts that kept `release/v3.8.51` red ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11673](https://github.com/diegosouzapw/OmniRoute/pull/11673)) — thanks @jonlwheat2-gif +- **test(providers):** update count-derived assertions after the v3.8.51 provider additions — `APIKEY_PROVIDERS` 233 → 235 (Opper #11629 + 1min.ai #11631), reserved-prefix REGISTRY walk 395 → 398, `WEB_FETCH_PROVIDERS` now includes `nimble-search` (#11620), and the provider translate-path golden snapshot regenerated ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11674](https://github.com/diegosouzapw/OmniRoute/pull/11674)) — thanks @jonlwheat2-gif +- **test(sse):** bump the hard-lease connection-query inventory for `src/lib/tokenHealthCheck.ts` to 2 — the verify-only web-cookie sweep added by #11495 split the single `getProviderConnections` call into oauth + cookie variants, which the frozen inventory had not tracked ([#11495](https://github.com/diegosouzapw/OmniRoute/pull/11495)). ([#11677](https://github.com/diegosouzapw/OmniRoute/pull/11677)) — thanks @jonlwheat2-gif / @oyi77 +- **chore(release):** clear the `release/v3.8.51` base-red gates — docs migration count (+38 i18n mirrors), stryker `tap.testFiles`, lockfile registry host, stale ESLint suppressions, the 7 lint regressions they were hiding, the stale `APIKEY_PROVIDERS` partition count (231 → 233), the `glm-5.3-max` tier missing from the GLM test table, a dead `modelType === "chat"` comparison in the models catalog, and the `resolveLiveWsUrl` / `sanitizeLiveWsPort` helpers from #11388 that a later merge dropped from `wsPath.ts` (dashboard live WebSocket URL resolution) ([#11502](https://github.com/diegosouzapw/OmniRoute/pull/11502)) — thanks @MumuTW +- **chore(free-models):** the free-tier regime table now declares which totals figure each regime feeds and whether it may take the no-auth shortcut, so the sets that used to repeat those answers by hand are derived from it — a new `freeType` no longer compiles until it has answered every question, instead of silently contributing to no total ([#11537](https://github.com/diegosouzapw/OmniRoute/pull/11537)) — thanks @maxmad64bis +- **docs(routing):** LKGP strategy copy now states provider-level stickiness and the explicit rules fallback instead of promising per-target pinning ([#11555](https://github.com/diegosouzapw/OmniRoute/pull/11555)) — thanks @maxmad64bis +- **test(chatcore):** pin the response tool-name alias resolution against the ordering hazard that broke every Gemini/Antigravity MCP tool call in v3.8.49. `extractRequestToolIdentityMap` deletes `translatedBody._toolNameMap`, so the later read is always undefined and the ledger survives only through the `requestToolIdentityMap` fallback — removing that fallback previously left the entire tool-name suite green. The resolution moves into `resolveResponseToolNameMap()` next to the map it depends on, with a regression guard that fails without the recovery. No behaviour change. ([#11575](https://github.com/diegosouzapw/OmniRoute/pull/11575)) — thanks @amartinawi +- **docs(env):** document `OMNIROUTE_CORPUS_CACHE_SIZE` in `.env.example` and `ENVIRONMENT.md`, so the env/docs contract check stops failing Docs Gates on every branch ([#11576](https://github.com/diegosouzapw/OmniRoute/pull/11576)) — thanks @ntdatt812 +- **chore(stryker):** register `route-guard-tunnel-processes-local-only` and `universal-quota-aware-routing` in `tap.testFiles`, so their mutant kills count for the three modules they cover ([#11577](https://github.com/diegosouzapw/OmniRoute/pull/11577)) — thanks @ntdatt812 +- **test(authz):** pin `LOCAL_ONLY_API_GET_EXEMPTIONS` by exact membership instead of by entry count, so the guard names the offending path and also catches a substitution ([#11580](https://github.com/diegosouzapw/OmniRoute/pull/11580)) — thanks @ntdatt812 +- **test(build):** pin `resolveNextBuildBundlerFlag` to the `OMNIROUTE_USE_TURBOPACK` contract it implements, across runtimes, instead of asserting an unimplemented Bun override ([#11583](https://github.com/diegosouzapw/OmniRoute/pull/11583)) — thanks @ntdatt812 +- **test(claude-web):** the Claude Web transport test's barrel anchors no longer pin the exact construction spelling. #11421 made the executor map lazy, so `/"claude-web": new ClaudeWebExecutor\(\)/` stopped matching and the test went red on `release/v3.8.51` itself — a base-red gate every open PR inherited. The anchors now match the alias key and the class name and tolerate either spelling in between, and were checked to still fail when an alias is genuinely repointed at another executor ([#11585](https://github.com/diegosouzapw/OmniRoute/pull/11585)) — thanks @ntdat812 +- **test(models):** pin `qwen3.8-max` resolution per provider — two of the four catalogs now serve the bare id and no longer carry `-preview` ([#11590](https://github.com/diegosouzapw/OmniRoute/pull/11590)) — thanks @ntdatt812 +- **test(executors):** drop the stale 128-tool truncation assertion that contradicted the #11444 pass-through contract ([#11591](https://github.com/diegosouzapw/OmniRoute/pull/11591)) — thanks @ntdatt812 +- **test(token-health):** pin the current terminal-skip boundary — expired with retry budget is probed, `account_deactivated` stays skipped ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)) — thanks @ntdatt812 +- **test(models):** follow #11503's retarget of the `gemini-3-pro-high` alias to the hyphenated catalog id ([#11593](https://github.com/diegosouzapw/OmniRoute/pull/11593)) — thanks @ntdatt812 +- **refactor(video bridge):** extract per-video acquisition, whole-result caching, description, and metrics/abort/cleanup out of `VideoBridgeGuardrail.preCall` into a `processVideoPart` seam in a new `videoBridgePipeline.ts`, behind explicit `VideoMediaBrokerPort`, `VideoAudioTranscriptionPort`, and `VideoDrilldownPort` boundaries; `preCall` now only handles request traversal, policy, and response aggregation. The Video tab's FFmpeg/ffprobe runtime status is now an explicit `unknown` / `restricted` / `unavailable` / `available` state instead of a nullable boolean pair, fixing a case where an in-flight or failed probe was mislabeled as "install FFmpeg" ([#11657](https://github.com/diegosouzapw/OmniRoute/issues/11657)). ([#12016](https://github.com/diegosouzapw/OmniRoute/pull/12016)) +- **test(opencode-plugin):** add unit test coverage for `formatFreeBudget()` naming helper ([#11660](https://github.com/diegosouzapw/OmniRoute/pull/11660)) — thanks @f9td56dbgh-hub +- **docs(video):** clarify that the Video Bridge transcript `source` field (`client`, `embedded`, `audio-bridge`) is presently caller-declared and not yet server-verified — OmniRoute enforces the enum shape but does not cryptographically confirm that an `embedded`/`audio-bridge` label came from a server-owned extraction ([#11661](https://github.com/diegosouzapw/OmniRoute/issues/11661)). ([#12001](https://github.com/diegosouzapw/OmniRoute/pull/12001)) +- **chore(providers):** retire the distributed Raycast Relay and Hailuo Web integrations after a provenance audit confirmed substantial derivation from GPL-3.0 sources; the official GitHub and MiniMax API providers remain available. Migration 163 disables legacy connections and invalidates their active leases while preserving connection IDs, audit/history rows, quota snapshots, usage records, and fail-closed API-key connection policies. ([#11691](https://github.com/diegosouzapw/OmniRoute/pull/11691)) +- **chore(providers):** retire the Felo Web provider while its GPL-derived provenance remains on licensing HOLD ([#11698](https://github.com/diegosouzapw/OmniRoute/pull/11698)) — stale runtime and database references now fail closed while existing connection, lease, and usage history remains preserved for auditability. +- **fix(sse):** retire the Gemini Web image-generation bridge while provenance and licensing remain on HOLD, preserving Gemini Web chat/auth/tools and legitimate Gemini/Nano Banana image providers ([#11708](https://github.com/diegosouzapw/OmniRoute/pull/11708)) +- **chore(audio):** retire the built-in EdgeTTS provider and its 11-voice catalog while provenance/licensing review remains on HOLD; the generic `/v1/audio/speech` endpoint and the other speech providers remain available ([#11711](https://github.com/diegosouzapw/OmniRoute/pull/11711)) +- **fix(providers):** retire the unofficial Qwen Web integration and its `qwen-web`/`qw` identities while third-party provenance remains on HOLD ([#11713](https://github.com/diegosouzapw/OmniRoute/pull/11713)) — direct requests now fail closed with a sanitized HTTP 410 and combos skip the retired target, while migration guards preserve connection IDs, allowlists, usage/audit history and invalidate active leases. Supported Qwen API, Qwen Code, cloud, token-plan, audio, local and model-name surfaces remain available; restoring Qwen Web requires a qualified provenance/license decision. +- **fix(providers):** retire `microsoft-designer-web` and its `msdesigner` alias from runtime and catalogs while preserving stored credentials, provider history and allowlists as inactive records — source dependency on gpt4free's [`MicrosoftDesigner.py`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/g4f/Provider/needs_auth/MicrosoftDesigner.py) is proven at snapshot `0943d16eef757a44deb60651c07bbe5fa958cafe`, whose [`LICENSE`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/LICENSE) is GPL-3.0; expressive adaptation is probable, which records provenance/licensing risk without asserting a legal conclusion ([#11720](https://github.com/diegosouzapw/OmniRoute/pull/11720)). +- **docs(legal):** centralize pinned MIT notices for the vendored GCF codec and local flag assets ([#11726](https://github.com/diegosouzapw/OmniRoute/pull/11726)) +- Split the 2,952-line `adobeFireflyClient.ts` into nine focused modules (catalog, credentials, payload, ARP session, upload, responses, IMS, account, poll) behind an unchanged barrel — no consumer edits and no public API change (#11728). — thanks @MumuTW +- **fix(dashboard):** replaced the Model Lockout card's unprovenanced notification MP3 with a short, locally synthesized Web Audio chime ([#11731](https://github.com/diegosouzapw/OmniRoute/pull/11731)) — both toggles retain optional audible feedback without shipping replacement media or third-party code, and unsupported, suspended, or rejected audio contexts fail open without blocking the setting change. +- **chore(deepseek-web):** remove the provenance-unresolved DeepSeek PoW WASM binary and its runtime loader/tracing while retaining the existing JavaScript solver (slower at high difficulty) ([#11732](https://github.com/diegosouzapw/OmniRoute/pull/11732)). +- **chore(assets):** remove four unused provider assets whose redistribution provenance could not be verified while retaining the active Freebuff SVG ([#11735](https://github.com/diegosouzapw/OmniRoute/pull/11735)) +- **docs(assets):** retired two raster images whose redistribution provenance could not be verified ([#11736](https://github.com/diegosouzapw/OmniRoute/pull/11736)) — PicoClaw documentation now reuses the existing SVG, while Zoo Code remains a text link without an image. +- **docs(legal):** add pinned notices for six LobeHub-derived provider SVGs and 65 byte-exact theSVG assets, retaining registry claims as unverified metadata with explicit trademark and HOLD boundaries ([#11737](https://github.com/diegosouzapw/OmniRoute/pull/11737)). +- **chore(quality):** added a versioned 225/225 provider-asset provenance manifest and blocking drift gate ([#11743](https://github.com/diegosouzapw/OmniRoute/pull/11743)) — the inventory pins magic MIME and SHA-256 for every physical file, records immutable source matches for 71 assets, keeps 69 probable and 85 unresolved entries explicit but non-blocking, and treats provenance as source evidence rather than copyright or trademark clearance. +- **refactor(mitm):** replace four previously derived Traffic Inspector utilities — conversation normalization, SSE merging, secret masking, and header sanitization — with independent clean-room implementations based on public provider schemas, WHATWG SSE, and RFC 6750, while keeping the two independently classified passthrough surfaces unchanged ([#11748](https://github.com/diegosouzapw/OmniRoute/pull/11748)). +- Added a unit test that fails seven days before any dated pack under `config/` (`validUntil` and sibling keys) lapses, naming the file and key. The Alibaba free-tier pack expired on 2026-08-27 and turned every PR red the next morning with no commit involved; renewal now happens on someone's terms, not the clock's. ([#11891](https://github.com/diegosouzapw/OmniRoute/pull/11891)) +- `check:workflows` now fails (under `--strict`/`--ratchet`) when any job routed to a self-hosted runner publishes with `--provenance` — npm rejects that with `422` at the registry, which in v3.8.50 only surfaced after the tag and Docker images were public. ([#11895](https://github.com/diegosouzapw/OmniRoute/pull/11895)) +- `scripts/ops/runner-janitor.sh` now proves a path is idle with one `lsof` snapshot and removes stale leftovers itself (tmpfs after 3 h — it is RAM — disk after 24 h), kills orphan `next-build` processes, prunes checkouts of stopped runners, and alerts on memory pressure; `--dry-run` shows exactly what it would do. `docs/ops/RUNNER_BOX.md` reconciled to the measured box (31 GB, 10 listeners). ([#11893](https://github.com/diegosouzapw/OmniRoute/pull/11893)) +- The `next-build` artefact (1.3 GB) is now written and read under `$RUNNER_TEMP` (per-runner, on disk) instead of `/tmp`, which on the self-hosted pool is a 12 GB tmpfs in RAM. Landing it there took 27–32 of the publish job's 76 minutes, and the fixed `/tmp/e2e-build.tar.gz` name let E2E jobs on different runners overwrite each other's download. ([#11896](https://github.com/diegosouzapw/OmniRoute/pull/11896)) +- The CI `build` job now runs in two concurrency lanes — `main` and pull requests — so a release build is never queued behind (or OOM-killed beside) PR builds on the self-hosted pool, which holds one `next-build` comfortably and two at the edge. ([#11901](https://github.com/diegosouzapw/OmniRoute/pull/11901)) +- Re-freeze the ESLint suppressions on `release/v3.8.51` from a clean-room measurement (2 stale file entries pruned, 55 pre-existing `no-explicit-any` in six new files frozen under #11924) and drop the dead `GPT_SIZE_MAP` constant orphaned by the Adobe Firefly client split, so `No new ESLint warnings` stops failing every PR with exit 2 (Refs #11924) ([#11955](https://github.com/diegosouzapw/OmniRoute/pull/11955)) +- Type the 55 `no-explicit-any` sites that had been frozen under #11924 — four redundant casts in `socksConnectorWithFamily.ts` (undici/socks types already accept them) and the mocks/fixtures of the socks-timeout and isFree suites — and drop their suppression entries; the ESLint ratchet shrinks from 5487 to 5432 (Closes #11924) ([#11975](https://github.com/diegosouzapw/OmniRoute/pull/11975)) +- Take the two hosted-runner builds off the PR rail: `Build App` (`build.yml`) is `workflow_dispatch`-only and `DAST smoke (PR)` runs only for PRs into `main` — the 7 GB hosted VM cannot build `release/v3.8.51` in any profile (19/30 red, VM shutdown ~8 min into `next build`) and both checks had turned into permanent noise on every release PR; the bundle stays validated by `ci.yml` on `main` and by `nightly-release-green` on `release/**` (Closes #11946) ([#11962](https://github.com/diegosouzapw/OmniRoute/pull/11962)) +- Move the four nightly jobs that build the backend (`nightly-schemathesis`, `nightly-llm-security` promptfoo + garak, `nightly-resilience` axe-a11y) off the hosted 7 GB runner — where they died on `release/v3.8.51` unseen — onto the box's new `omni-light` pool (two listeners), and document the reshaped fleet (4 active OmniRoute listeners: 2 `omni-build` + 2 `omni-light`, janitor ceiling 4) (Closes #11965) ([#11967](https://github.com/diegosouzapw/OmniRoute/pull/11967)) +- Stop painting every fork PR into `release/**` red: `quality.yml` `Build (advisory)` is skipped (GitHub still reports `continue-on-error` failures as check FAILURE). Hosted `ubuntu-latest` cannot finish `npm run build` on this tree — same class as #11962 taking `build.yml` off the PR rail. `docker-publish.yml` amd64 now runs on the `.113` `omni-build` pool (31 GB / 32 cores, two listeners) with Turbopack, shares the `heavy-build-main` lane with `ci.yml` `Build` so it queues instead of becoming a third heavy, and keeps per-ref concurrency (a merge storm was starting 8 concurrent OOM builds). arm64 stays on `ubuntu-24.04-arm` with webpack — there is no ARM box. Fallback when `USE_VPS_RUNNER` is off: hosted amd64 + webpack (#11976). ([#12021](https://github.com/diegosouzapw/OmniRoute/pull/12021)) +- `docker-publish.yml` builds with webpack (`OMNIROUTE_USE_TURBOPACK=0`) on **both** arches, including amd64 on the `.113` `omni-build` pool. Turbopack had the RAM (31 GB, `omniroute-113-6`) and still panicked (`TurbopackInternalError: there must be a path to a root`, run 33253576569, 26 min in). The same tree's arm64 webpack build on hosted `ubuntu-24.04-arm` succeeded (run 33264823398). Dockerfile already documents webpack as the Docker escape hatch (#11976). ([#12050](https://github.com/diegosouzapw/OmniRoute/pull/12050)) +- **docs(free-tier):** declare the counting vs deciding regimes for "is it free?" and guard the deciding path from DB-backed catalog resolution ([#12226](https://github.com/diegosouzapw/OmniRoute/pull/12226)) — thanks @maxmad64bis +- **docs(auto-combo):** The mode pack table in `docs/routing/AUTO-COMBO.md` now lists all six shipped packs with every weight each one sets, replacing a four-pack table whose numbers had also drifted from the source. It states plainly that no pack sets `quality`, so selecting any pack silences the observed-quality signal. Six more documents that quote the scoring factor count joined the `check:docs-counts` gate, which caught five stale claims — including one naming nine factors that do not exist — and two stale mode pack counts. The dashboard routing panel, which offered four of the six packs and labelled the default strategy "6-Factor Scoring", is now covered by a test; the two packs it was missing are `reliability-first` and `chaos-mode`, the latter labelled as the fault-injection profile it is rather than as one more routing preference ([#12316](https://github.com/diegosouzapw/OmniRoute/pull/12316)) — thanks @maxmad64bis +- **chore(quality):** rebaseline `src/lib/db/apiKeys.ts` for the ACL the key-creation path now preserves ([#12352](https://github.com/diegosouzapw/OmniRoute/pull/12352)) ([#12673](https://github.com/diegosouzapw/OmniRoute/pull/12673)) — thanks @KrzysiekSko +- **chore(providers):** bump the Claude Code wire identity and the Devin bridge image pin from `2.1.220` to `2.1.258` ([#12402](https://github.com/diegosouzapw/OmniRoute/pull/12402)) — thanks @ggiak ([#12604](https://github.com/diegosouzapw/OmniRoute/pull/12604)) +- **docs(env):** align `.env.example`, the README Bun section, and the troubleshooting guide with the code: `OMNIROUTE_USE_TURBOPACK` also governs `npm run build` (not dev-only), `bun run build` follows that flag instead of auto-selecting Webpack, `OMNIROUTE_CHAT_MAX_HEAVY_IN_FLIGHT` is unset by default (no request-count cap), and the structural `503 chat_admission_busy` message matches `chatAdmissionResponses.ts` (#12404 — thanks @pacocartones) +- **chore(electron):** upgrade the desktop app to Electron 44 (Chromium 152, Node 24.18.1) ([#12217](https://github.com/diegosouzapw/OmniRoute/pull/12217)). **Requires macOS 13 (Ventura) or later** — Chromium dropped macOS 12 (Monterey), so Monterey users must stay on an earlier OmniRoute desktop build. Windows and Linux are unaffected; the app already shipped only x64/arm64, so Electron 44 dropping 32-bit builds changes nothing. Removes the `openAsHidden`/`wasOpenedAsHidden` login-item fields deleted in Electron 44 — hidden autostart continues to work through the `--hidden` argument registered with the login item ([#12554](https://github.com/diegosouzapw/OmniRoute/pull/12554)) — thanks @app/dependabot +- **chore(quality):** rebaseline `src/sse/handlers/chat.ts` for the effective-input persistence the continuation fix needs ([#12641](https://github.com/diegosouzapw/OmniRoute/pull/12641)) ([#12680](https://github.com/diegosouzapw/OmniRoute/pull/12680)) — thanks @hartmark +- `Coverage` job on `ci.yml`: the informational Codecov upload gets its own 5-minute ceiling and `continue-on-error`, and the job budget grows from 20 to 30 minutes (the 8-shard c8 merge alone takes ~10) — a stalled upload no longer ends the job `cancelled` and drags a fully green `main` run's conclusion down with it ([#11972](https://github.com/diegosouzapw/OmniRoute/pull/11972)) +- CI hardening on the PR rail: the four `Unit Tests fast-path` shards get `timeout-minutes: 30` (a hung shard held a PR for 64 min instead of GitHub's 6 h default) and both ESLint file caches lose their `restore-keys` fallback, so a cache built under another suppressions file or lint config can no longer report stale verdicts (Refs #11600, #11924) ([#11963](https://github.com/diegosouzapw/OmniRoute/pull/11963)) +- Every CI job that runs a `next build` (`build`, the npm `publish`, both release-green validations) now targets the `omni-build` runner label, which only two of the eight self-hosted runners carry. The box holds one build comfortably and two at the edge; a third now queues on GitHub instead of being OOM-killed by the kernel. ([#11932](https://github.com/diegosouzapw/OmniRoute/pull/11932)) +- **refactor(combo):** move `handleRoundRobinCombo` (and `resolveTargetTokenLimit`) into `open-sse/services/combo/roundRobinCombo.ts`. `combo.ts` drops from 2164 to 1014 lines (`split("\n").length`); the leaf is 1199 (under the 1200 new-file cap). The round-robin call site uses a dynamic `import()` so `releaseStickyPinOnFailure` / `clearStaleLKGP` can stay exported from `combo.ts` without a static cycle. Skip / sticky / semaphore / safety-timer behavior is unchanged. ([#12811](https://github.com/diegosouzapw/OmniRoute/pull/12811)) — thanks @HouMinXi +- **chore(quality):** rebaseline the file-size caps the error-boundary campaign grew past (`open-sse/executors/codex.ts`, `open-sse/vendor/codex-chatgpt-web/bridge.ts`, both via [#12444](https://github.com/diegosouzapw/OmniRoute/pull/12444)) ([#12654](https://github.com/diegosouzapw/OmniRoute/pull/12654)) +- **chore(quality):** rebaseline the file-size caps the hartmark batch grew past (`combos/page.tsx` via [#12355](https://github.com/diegosouzapw/OmniRoute/pull/12355), `open-sse/services/combo.ts` via [#12338](https://github.com/diegosouzapw/OmniRoute/pull/12338)) ([#12623](https://github.com/diegosouzapw/OmniRoute/pull/12623)) — thanks @hartmark +- **chore(quality):** rebaseline the file-size caps the HouMinXi batch grew past when its PRs stacked (`providers/page.tsx`, `chatCore.ts`, `accountFallback.ts`) — each PR measured correctly in isolation, none saw the stacking ([#12619](https://github.com/diegosouzapw/OmniRoute/pull/12619)) +- **chore(quality):** rebaseline `open-sse/services/combo.ts` for the reset-aware scoring the HouMinXi batch stacked ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) ([#12678](https://github.com/diegosouzapw/OmniRoute/pull/12678)) — thanks @HouMinXi +- **docs:** sync provider catalog count (direct commit `4f11b2ae3d`) +- **docs(skills):** sync omni-inference search fallback (direct commit `b4e76a7ed9`) +- **chore(lint):** prune two stale base suppressions (direct commit `1325047d56`) +- **test(auto-combo):** tolerate full-suite rotation load (direct commit `ad3e293f9f`) +- **test:** drain inherited quality snapshot reds (direct commit `21ed68d8ac`) +- **chore(quality):** rebaseline measured CLI bundle growth (direct commit `d35b3f9779`) +- **test(ui):** reproduce invalid AnySearch icon alias (direct commit `12cc6ca834`) +- **test(translator):** fix the relative imports of the relocated deferred-finish test (direct commit `777d9d1629`) +- **docs(counts):** bring the documented provider and migration counts to the measured values (direct commit `e70bea30e9`) +- **test(security):** drop the guard-target set left unused by the sync (direct commit `a32ba0e362`) +- **chore(db):** renumber model_capabilities migration 163 → 169 (cross-branch collision with 163_radar_feed_cache_generated_at) (direct commit `70f5d4cbf9`) +- **docs(cli):** document CLI_PRIME_AGENT_BIN and correct the CLI Agents count (direct commit `9058e39b61`) +- **chore(deps):** refresh runtimes and adopt ESLint 10 ([#11259](https://github.com/diegosouzapw/OmniRoute/pull/11259)) — thanks @backryun +- **refactor(local-corpus):** implement dynamic root resolution and LRU cache ([#11491](https://github.com/diegosouzapw/OmniRoute/pull/11491)) — thanks @raheemuddin786 +- **chore(quality):** prune stale eslint-suppressions entries ([#11596](https://github.com/diegosouzapw/OmniRoute/pull/11596)) +- **chore(ci):** unblock quality gates - env-doc sync + stryker tap.testFiles registration (#9282) ([#11609](https://github.com/diegosouzapw/OmniRoute/pull/11609)) — thanks @Meet6338-X +- **chore(ci):** point the circular-deps gate at dpdm's real JS entrypoint ([#11615](https://github.com/diegosouzapw/OmniRoute/pull/11615)) — thanks @benzntech +- **chore(quality):** rebaseline file-size for #11448/#11495/#11561 growth ([#11630](https://github.com/diegosouzapw/OmniRoute/pull/11630)) +- **deps:** realign onnxruntime-node pin to @huggingface/transformers' 1.24.3 lockstep ([#11633](https://github.com/diegosouzapw/OmniRoute/pull/11633)) — thanks @jonlwheat2-gif +- **test(autoCombo):** port models_dev_tier #11508 guard from node:test to vitest ([#11635](https://github.com/diegosouzapw/OmniRoute/pull/11635)) — thanks @jonlwheat2-gif +- **chore(quality):** rebaseline file-size for #11083/#11631/#11628 growth ([#11636](https://github.com/diegosouzapw/OmniRoute/pull/11636)) +- **chore(lint):** prune stale ESLint suppressions (base-red #11449) ([#11644](https://github.com/diegosouzapw/OmniRoute/pull/11644)) — thanks @jonlwheat2-gif +- **docs(changelog):** aggregate the ten v3.8.50 fragments into CHANGELOG.md ([#11683](https://github.com/diegosouzapw/OmniRoute/pull/11683)) +- **chore(search):** drop unreachable credential sweep after #11642 ([#11687](https://github.com/diegosouzapw/OmniRoute/pull/11687)) — thanks @HouMinXi +- **ci:** add API route TypeScript regression gate ([#11705](https://github.com/diegosouzapw/OmniRoute/pull/11705)) — thanks @Sabeekhann +- **docs(dedup):** correct the CodeQL claim on the tenant-namespace comment ([#11712](https://github.com/diegosouzapw/OmniRoute/pull/11712)) +- **docs(changelog):** add consolidated v3.8.50 stats, top-25 ranking and a .mailmap ([#11715](https://github.com/diegosouzapw/OmniRoute/pull/11715)) +- **docs:** recommend gstack for AI-assisted workflows ([#11770](https://github.com/diegosouzapw/OmniRoute/pull/11770)) — thanks @santosraju99-hub +- **chore(quality):** tighten complexity/cognitive-complexity ratchets to the current tip; land the missed gateways.ts rebaseline ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **docs(i18n):** finish Freepik → Magnific rebrand in locale strings and README ([#11772](https://github.com/diegosouzapw/OmniRoute/pull/11772)) — thanks @turbolego +- **refactor(auto-combo):** fix divergent scoring in combo health reporting ([#11854](https://github.com/diegosouzapw/OmniRoute/pull/11854)) — thanks @jacobsparts +- **chore(changelog):** add missing fragments for #11919/#11918/#11916 ([#11938](https://github.com/diegosouzapw/OmniRoute/pull/11938)) +- **test(check):** escape the runs-on fixture with JSON.stringify, not a quote-only replace ([#11942](https://github.com/diegosouzapw/OmniRoute/pull/11942)) (direct commit `fb7445eaa3`) +- **test(release):** align five suites with the contracts #11933, #11919 and #11876 shipped on release/v3.8.51 ([#11944](https://github.com/diegosouzapw/OmniRoute/pull/11944)) +- **chore(quality):** remove unreachable code and restore test discovery ([#11950](https://github.com/diegosouzapw/OmniRoute/pull/11950)) — thanks @backryun +- **docs(agents):** sync-back landings are fast-forward, never squash ([#11964](https://github.com/diegosouzapw/OmniRoute/pull/11964)) +- **test(infra):** retry recursive temp-dir removal instead of failing a shard on ENOTEMPTY (#11966) ([#11968](https://github.com/diegosouzapw/OmniRoute/pull/11968)) +- **docs(ops):** the .113 heavy-build ceiling is one runner, not two ([#12048](https://github.com/diegosouzapw/OmniRoute/pull/12048)) +- **test(ui):** correct inactive auto-fetch expectation ([#12098](https://github.com/diegosouzapw/OmniRoute/pull/12098)) — thanks @RaviTharuma +- **docs:** document native dependency check escape hatch ([#12101](https://github.com/diegosouzapw/OmniRoute/pull/12101)) — thanks @RaviTharuma +- **test(providers):** regenerate the translate-path golden for OrcaRouter (#11923) ([#12118](https://github.com/diegosouzapw/OmniRoute/pull/12118)) +- **chore(quality):** velocity phase — loosen every numeric baseline by 20% until v4.0, monitor headroom nightly ([#12125](https://github.com/diegosouzapw/OmniRoute/pull/12125)) +- **chore(lint):** batch 0 of #12146 — type the call-log-cap sqlite rows instead of 45 as-any casts ([#12157](https://github.com/diegosouzapw/OmniRoute/pull/12157)) +- **chore(lint):** batch 4 of #12146 — shared/components react-hooks violations resolved ([#12159](https://github.com/diegosouzapw/OmniRoute/pull/12159)) +- **chore(lint):** batch 1 of #12146 — dashboard/cli-code react-hooks violations resolved ([#12160](https://github.com/diegosouzapw/OmniRoute/pull/12160)) +- **chore(lint):** batch 3 of #12146 — dashboard/settings react-hooks violations resolved ([#12162](https://github.com/diegosouzapw/OmniRoute/pull/12162)) +- **chore(lint):** batch 2 of #12146 — resolve the react-hooks compiler violations in dashboard/providers ([#12163](https://github.com/diegosouzapw/OmniRoute/pull/12163)) +- **chore(quality):** register search-432 cooldown test in stryker tap.testFiles ([#12170](https://github.com/diegosouzapw/OmniRoute/pull/12170)) +- **chore(lint):** batch 5 of #12146 — combos, endpoint, provider-stats, api-manager and costs react-hooks violations resolved ([#12174](https://github.com/diegosouzapw/OmniRoute/pull/12174)) +- **build:** add contributor fast profile ([#12192](https://github.com/diegosouzapw/OmniRoute/pull/12192)) — thanks @rafacpti23 +- **docs(readme):** README touch-ups ([#12193](https://github.com/diegosouzapw/OmniRoute/pull/12193)) ([#12194](https://github.com/diegosouzapw/OmniRoute/pull/12194)) ([#12202](https://github.com/diegosouzapw/OmniRoute/pull/12202)) — thanks @rafacpti23 +- **build:** force the webpack bundler in the contributor build profile ([#12197](https://github.com/diegosouzapw/OmniRoute/pull/12197)) — thanks @rafacpti23 +- **build:** skip standalone packaging in the contributor profile ([#12198](https://github.com/diegosouzapw/OmniRoute/pull/12198)) — thanks @rafacpti23 +- **docs(audit):** align every published number with the code and harden check:docs-counts ([#12200](https://github.com/diegosouzapw/OmniRoute/pull/12200)) +- **build:** stub the instrumentation entrypoints in the contributor profile ([#12203](https://github.com/diegosouzapw/OmniRoute/pull/12203)) — thanks @rafacpti23 +- **build:** omit the standalone output target for contributor builds ([#12204](https://github.com/diegosouzapw/OmniRoute/pull/12204)) — thanks @rafacpti23 +- **chore(lint):** batch 6 of #12146 — memory, radar, audit, analytics, cache, usage, activity, home and RequestLoggerV2 react-hooks violations resolved ([#12208](https://github.com/diegosouzapw/OmniRoute/pull/12208)) +- **docs(diagrams):** rename number-carrying diagram files to stable names ([#12210](https://github.com/diegosouzapw/OmniRoute/pull/12210)) +- **docs(dashboard):** redraw onboarding tier-flow SVGs for the real 4-tier model ([#12211](https://github.com/diegosouzapw/OmniRoute/pull/12211)) +- **docs(api):** document every implemented route in openapi.yaml (276 → 692 paths) ([#12212](https://github.com/diegosouzapw/OmniRoute/pull/12212)) +- **chore(lint):** batch 7 of #12146 — final src tail: 37 react-hooks violations across 33 files resolved ([#12244](https://github.com/diegosouzapw/OmniRoute/pull/12244)) +- Revert "docs: recommend gstack for AI-assisted workflows (#11770)" ([#12248](https://github.com/diegosouzapw/OmniRoute/pull/12248)) ([#12249](https://github.com/diegosouzapw/OmniRoute/pull/12249)) +- **docs:** Chaos Mode setup guide + weighted strategy semantics ([#12250](https://github.com/diegosouzapw/OmniRoute/pull/12250)) +- **docs(agents):** protected-surface merge rule — operator approval for agent-instruction files ([#12253](https://github.com/diegosouzapw/OmniRoute/pull/12253)) +- **chore(quality):** register native-codex turn-pin tests in stryker tap.testFiles ([#12263](https://github.com/diegosouzapw/OmniRoute/pull/12263)) +- **chore(quality):** dedupe tap.testFiles entries added by racing base-red fixes ([#12265](https://github.com/diegosouzapw/OmniRoute/pull/12265)) +- **docs(readme):** introduce OmniRouteTray — the macOS menu-bar companion ([#12276](https://github.com/diegosouzapw/OmniRoute/pull/12276)) — thanks @ggiak +- **chore(proxy):** purge the legacy 1proxy residue — sync/rotator modules, dead DB exports, dead settings tab and flag, docs (#12091) ([#12290](https://github.com/diegosouzapw/OmniRoute/pull/12290)) +- **chore(quality):** baseline-headroom skips generated and vendored files in the fileSize worst-file signal ([#12291](https://github.com/diegosouzapw/OmniRoute/pull/12291)) +- **refactor(video):** unify the JPEG frame data-URI contract ([#12322](https://github.com/diegosouzapw/OmniRoute/pull/12322)) +- **chore(deps):** freeze onnxruntime-node and eslint-plugin-react-hooks out of dependabot groups ([#12329](https://github.com/diegosouzapw/OmniRoute/pull/12329)) +- **test(executors):** restore the #10986 reasoning-only fallback guards ([#12364](https://github.com/diegosouzapw/OmniRoute/pull/12364)) — thanks @pacocartones +- **chore(quality):** re-tighten the file-size ratchet to the real LOC (plan 3.8.52 task 0) ([#12411](https://github.com/diegosouzapw/OmniRoute/pull/12411)) +- **test(grok):** format web executor suite ([#12412](https://github.com/diegosouzapw/OmniRoute/pull/12412)) +- **chore(quality):** ratchet open-sse typecheck baseline to zero ([#12418](https://github.com/diegosouzapw/OmniRoute/pull/12418)) +- **test(build):** guard the artifact path policy arrays against duplicates ([#12422](https://github.com/diegosouzapw/OmniRoute/pull/12422)) +- **chore(lint):** adopt eslint-plugin-react-hooks 7.1.1 ([#12428](https://github.com/diegosouzapw/OmniRoute/pull/12428)) +- **chore(providers):** remove a keyless provider integration at its operator's request ([#12440](https://github.com/diegosouzapw/OmniRoute/pull/12440)) +- **docs(providers):** register providers removed at their operator's request and guard against reintroduction ([#12478](https://github.com/diegosouzapw/OmniRoute/pull/12478)) +- **chore(quality):** base fixes — stryker tap.testFiles + node_modules cache key ([#12482](https://github.com/diegosouzapw/OmniRoute/pull/12482)) +- **docs(arch):** one-process recipe for tens of long /v1/responses ([#12493](https://github.com/diegosouzapw/OmniRoute/pull/12493)) — thanks @RaviTharuma +- **chore(quality):** tighten the CodeQL ratchet baseline from 11 to 6 ([#12530](https://github.com/diegosouzapw/OmniRoute/pull/12530)) +- **chore(deps):** pin browserslist override to ^4.28.8 ([#12592](https://github.com/diegosouzapw/OmniRoute/pull/12592)) — thanks @KooshaPari +- **chore(deps):** bump fast-uri to 3.1.7 in the electron lockfile ([#12601](https://github.com/diegosouzapw/OmniRoute/pull/12601)) +- **docs(quality):** document how the CodeQL ratchet refreshes and how to tighten it ([#12611](https://github.com/diegosouzapw/OmniRoute/pull/12611)) +- **test(ci):** pin the openapi-security-tiers two-arm contract with an executing gate test (#12581) ([#12652](https://github.com/diegosouzapw/OmniRoute/pull/12652)) +- **docs(gamification):** align XP Rewards table with code (#12501) ([#12667](https://github.com/diegosouzapw/OmniRoute/pull/12667)) — thanks @KooshaPari +- **refactor(combo):** split executeTarget into gates, attempt, and loop ([#12746](https://github.com/diegosouzapw/OmniRoute/pull/12746)) — thanks @HouMinXi +- **docs(security):** document socket.yml scanner config + CI workflow link (#12575) ([#12764](https://github.com/diegosouzapw/OmniRoute/pull/12764)) — thanks @KooshaPari +- **chore(ci):** guard commit identity in pre-commit to stop author misattribution ([#12772](https://github.com/diegosouzapw/OmniRoute/pull/12772)) +- **deps:** 17 Dependabot bumps — bump electron from 43.4.0 to 43.4.1 in /electron ([#11426](https://github.com/diegosouzapw/OmniRoute/pull/11426)); bump the development group across 1 directory with 8 updates ([#11428](https://github.com/diegosouzapw/OmniRoute/pull/11428)); bump the production group across 1 directory with 11 updates ([#11440](https://github.com/diegosouzapw/OmniRoute/pull/11440)); chore(deps): bump github/codeql-action/init from 4.37.7 to 4.37.8 ([#11925](https://github.com/diegosouzapw/OmniRoute/pull/11925)); chore(deps): bump github/codeql-action from 4.37.7 to 4.37.8 ([#11926](https://github.com/diegosouzapw/OmniRoute/pull/11926)); chore(deps): bump github/codeql-action/analyze from 4.37.7 to 4.37.8 ([#11927](https://github.com/diegosouzapw/OmniRoute/pull/11927)); build(deps): bump github/codeql-action/init from 4.37.8 to 4.37.9 ([#12345](https://github.com/diegosouzapw/OmniRoute/pull/12345)); build(deps): bump github/codeql-action/analyze from 4.37.8 to 4.37.9 ([#12346](https://github.com/diegosouzapw/OmniRoute/pull/12346)); bump the development group across 1 directory with 2 updates ([#12347](https://github.com/diegosouzapw/OmniRoute/pull/12347)); build(deps): bump github/codeql-action from 4.37.8 to 4.37.9 ([#12349](https://github.com/diegosouzapw/OmniRoute/pull/12349)); bump browserslist from 4.28.2 to 4.28.8 ([#12396](https://github.com/diegosouzapw/OmniRoute/pull/12396)); bump the production group across 1 directory with 4 updates ([#12399](https://github.com/diegosouzapw/OmniRoute/pull/12399)); bump @xmldom/xmldom ([#12500](https://github.com/diegosouzapw/OmniRoute/pull/12500)); bump qs from 6.15.2 to 6.16.0 ([#12512](https://github.com/diegosouzapw/OmniRoute/pull/12512)); bump @xmldom/xmldom from 0.9.10 to 0.9.12 ([#12513](https://github.com/diegosouzapw/OmniRoute/pull/12513)); bump fast-uri from 3.1.5 to 3.1.7 ([#12514](https://github.com/diegosouzapw/OmniRoute/pull/12514)); bump @humanfs/node from 0.16.7 to 0.16.8 ([#12515](https://github.com/diegosouzapw/OmniRoute/pull/12515)) +- Clear the `release/v3.8.51` `check:agent-skills-sync` base-red: regenerate `skills/cli-tunnel/SKILL.md` so the `tunnel create [type]` positional that #13009 taught the generator to read is reflected in the committed skill. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` typecheck base-red from #12731: the new `Mode pack ranking gates` candidates in `open-sse/services/autoCombo/__tests__/autoCombo.test.ts` omitted the required `provider`, `model` and `errorRate` fields of `ProviderCandidate`, failing `check:open-sse-typecheck` (and with it `Fast Quality Gates`) on every open PR. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` docs-sync base-red: EURouter (#13025) and GreenPT (#13024) took the live provider count to 358, leaving 7 STRICT drifts (`PROVIDER_REFERENCE.md`, 4 diagrams, 2 tier-flow images) plus the `AGENTS.md` / `llm.txt` / `package.json` count claims stale. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- **test(models):** Pin `CATALOG_BUILD_TIMEOUT_MS` in the #12058 canonical-catalog regression so a cold catalog build racing the 8s default bound no longer flakes the suite with a 500 ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Allow `@omniroute/opencode-plugin-v2/` in the npm pack policy — [#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870) shipped the v2 plugin beside its v1 sibling without widening the allowlist, so every packed file under it read as an unexpected artifact and failed `check:pack-policy` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Point the release-green validator's pack gate at the tree it is validating — the build-provenance guard defaulted to `origin/main`, which a release branch tip can never be an ancestor of mid-cycle, so `Package artifact` reported a false red on every run and the tarball boot-smoke cascaded off it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **docs(checks):** keep doc counts honest — headings, rankings, catalog, weights, quality gate and scoring diagram now covered ([#12507](https://github.com/diegosouzapw/OmniRoute/pull/12507)) — thanks @maxmad64bis +- **chore(lifecycle):** `check:model-lifecycle` now also diffs `DEFAULT_DEGRADATION_MAP` (the background-task redirect table) against the vendor lifecycle snapshot, refusing a retired id as source or target, with a table-driven unit test beside it. Three rows whose source the vendor had retired — `claude-sonnet-4-20250514`, `gemini-3-pro-preview` and `gpt-5.1-codex` (whose target `gpt-5.1-codex-mini` is retired too) — were dead code, since `checkLifecycle` answers 410 before the redirect runs; they are dropped (#12535 — thanks @pacocartones) +- **test(compression):** cover idle worker eviction at the resource level — the pool must call `terminate()` and must not retain the worker's `MessagePort`, complementing the `exit`-event assertion added with the fix ([#13371](https://github.com/diegosouzapw/OmniRoute/pull/13371)) +- **chore(quality):** raise the `imageGeneration.ts` file-size ceiling for the image-only-model guard that clears the #12945 base-red ([#13392](https://github.com/diegosouzapw/OmniRoute/pull/13392)) +- **chore(quality):** type the combo-test route's JSON response bodies instead of casting them to `any`, drop the now-empty suppression entry, and remove the `hasPerModelQuota` import `chatCore.ts` stopped using when the failure-classification helper was extracted ([#13379](https://github.com/diegosouzapw/OmniRoute/pull/13379)) +- **chore(electron):** the desktop bundle no longer ships the root-level files of every translated docs mirror (`README.md`, `llm.txt`, `CLAUDE.md`, `GEMINI.md`, `CONTRIBUTING.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`) — the packaged app only reads `docs/i18n//docs/**`, which stays. Saves ~11 MB on top of the translated CHANGELOGs already pruned. (#0000) ([#13235](https://github.com/diegosouzapw/OmniRoute/pull/13235)) +- **chore(quality):** retighten the `src/sse/handlers/chat.ts` unused-vars suppression count to the 8 violations that actually remain ([#13385](https://github.com/diegosouzapw/OmniRoute/pull/13385)) +- Bring the documented counts back in line with the code: 171 migrations (was 169) and 20 routing strategies (was 19), plus the regenerated `cli-tunnel` skill reference. ([#12970](https://github.com/diegosouzapw/OmniRoute/pull/12970)) +- Update the documented migration count to 172 after the call-logs provider-stats indexes landed. ([#13209](https://github.com/diegosouzapw/OmniRoute/pull/13209)) +- **chore(ci):** two gates that close the blind spots behind the exclusions above. `check:vitest-exclusions` requires every Vitest exclusion to name a tracking issue and to appear in `config/quality/vitest-exclusions.json` — the previous list grew to 62 files behind a comment pointing at an issue that had been closed for a month. `check-new-key-coverage` requires a key newly added to `en.json` to reach every locale; the existing coverage gate is a percentage floor per locale, so eleven absent keys out of ~13,000 left it at 99.9% while a whole feature shipped untranslated in nine languages. Both are diff-aware, so pre-existing debt stays frozen and neither needed a migration to turn on. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **build:** root layout no longer loads Inter from `next/font/google`, so a production image build does not need fonts.googleapis.com ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **docs(i18n):** bring the eight new `llm.txt` mirrors (km, kn, ml, my, ne, or, pa, si) up to the current provider and migration counts, so `check-docs-sync` stops rejecting every commit ([#13674](https://github.com/diegosouzapw/OmniRoute/pull/13674)) +- **refactor(ui):** flow surfaces (home topology, combo live studio, compression cockpit/waterfall/nodes, token-health badge) express state through the theme-aware `--orch-status-*` tokens instead of fixed dark-mode hex, so green/red/amber/grey stay legible in the light theme; dark mode is byte-identical. Categorical palettes (routing-strategy hues, compression-layer pills, provider brand colors) deliberately stay hex ([#12378](https://github.com/diegosouzapw/OmniRoute/issues/12378)) ([#13203](https://github.com/diegosouzapw/OmniRoute/pull/13203)) +- **chore(tests):** 51 test files that had been excluded from Vitest are running again, restoring roughly 350 assertions to the blocking suite. Each was measured individually first: of the 62 files parked behind the `// #8618 — pre-existing failure` comment, 51 pass against the current tree with no source change, so the exclusions had outlived the failures they were added for. The 11 that genuinely still fail stay excluded, but now point at a live tracker (#13204) rather than at #8618, which was closed in August while the list it tracked kept growing. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **chore(quality):** drop two ESLint suppression entries whose violations no longer exist, so `eslint --suppressions-location` stops rejecting every commit that touches the surrounding files ([#13384](https://github.com/diegosouzapw/OmniRoute/pull/13384)) +- **chore(release):** bring main's post-tag fixes into release/v3.8.51 — sync-back step 2/2 (direct commit `21c488f210`) +- **chore(deps):** patch toml and esbuild in the opencode plugin lockfiles ([#12965](https://github.com/diegosouzapw/OmniRoute/pull/12965)) +- **docs(changelog):** reconcile the v3.8.51 living section — fold 366 fragments, cover every cycle commit, credit every contributor ([#12971](https://github.com/diegosouzapw/OmniRoute/pull/12971)) +- **test(plugin):** wait for the v2 lazy-refresh tier instead of sleeping past it ([#12984](https://github.com/diegosouzapw/OmniRoute/pull/12984)) +- **chore(quality):** rebaseline chatCore.ts after the non-streaming regression fixes ([#13045](https://github.com/diegosouzapw/OmniRoute/pull/13045)) +- **docs(changelog):** add the missing bullet marker to the DeepSeek PoW fragment ([#13200](https://github.com/diegosouzapw/OmniRoute/pull/13200)) +- **chore(deps):** drain the Dependabot queue — 10 of 13 alerts ([#13213](https://github.com/diegosouzapw/OmniRoute/pull/13213)) +- **chore(ci):** make worktrees carry the hooks that enforce the identity gate ([#13236](https://github.com/diegosouzapw/OmniRoute/pull/13236)) +- **chore(deps):** pin csv-parse to ^7.0.2 via overrides (#13117) ([#13300](https://github.com/diegosouzapw/OmniRoute/pull/13300)) — thanks @KooshaPari +- **chore(deps):** bump hono from ^4.12.34 to ^4.13.7 (#13148) ([#13301](https://github.com/diegosouzapw/OmniRoute/pull/13301)) — thanks @KooshaPari +- **chore(deps):** pin joi to ^18.2.8 via overrides (#13085) ([#13302](https://github.com/diegosouzapw/OmniRoute/pull/13302)) — thanks @KooshaPari +- **docs(providers):** add Microsoft 365 Copilot (BizChat) provider guide (#12779) ([#13340](https://github.com/diegosouzapw/OmniRoute/pull/13340)) — thanks @KooshaPari +### 🙌 Contributors + +Thanks to everyone whose work landed in v3.8.51: + +| Contributor | PRs / Issues | +| --- | --- | +| [@5dive-bot](https://github.com/5dive-bot) | #11852 | +| [@abhisheksharma2411](https://github.com/abhisheksharma2411) | #11547, #11684, #11959, #12209 | +| [@adevwithpurpose](https://github.com/adevwithpurpose) | #11464 | +| [@adivekar-utexas](https://github.com/adivekar-utexas) | #12015, #12043, #12090, #12138 | +| [@AIB1TAL0S](https://github.com/AIB1TAL0S) | #12103 | +| [@alltomatos](https://github.com/alltomatos) | #11448 | +| [@alvinveroy](https://github.com/alvinveroy) | #11857, #12027, #12165 | +| [@amaleta](https://github.com/amaleta) | #11906, #11934 | +| [@amartinawi](https://github.com/amartinawi) | #11575 | +| [@AndrianBalanescu](https://github.com/AndrianBalanescu) | #11083, #11830, #11834, #11844 | +| [@anhtahaylove](https://github.com/anhtahaylove) | #13091, #13092, #13093, #13096, #13097, #13100, #13106, #13109, #13114, #13155, #13166, #13171, #13175, #13187, #13196 | +| [@aniruddhaadak80](https://github.com/aniruddhaadak80) | #11760 | +| [@app](https://github.com/app) | #12554 | +| [@arminanton](https://github.com/arminanton) | #11461, #11513 | +| [@AStupidBear](https://github.com/AStupidBear) | #11584 | +| [@atescivitci-cmd](https://github.com/atescivitci-cmd) | #12105 | +| [@b3nw](https://github.com/b3nw) | #11970, #11971, #12147 | +| [@backryun](https://github.com/backryun) | #11259, #11950, #12075, #12076, #12078, #12079, #12081, #12082, #12181, #12228, #12239, #12255, #12258, #12277, #12367, #12423, #12524, #12538 | +| [@benjaminkitt](https://github.com/benjaminkitt) | #11747 | +| [@benzntech](https://github.com/benzntech) | #11614, #11615 | +| [@Bl0ck154](https://github.com/Bl0ck154) | #11948, #11951, #11952, #11953, #11954 | +| [@brick30llc-ctrl](https://github.com/brick30llc-ctrl) | #11752, #12242, #12262 | +| [@Chewji9875](https://github.com/Chewji9875) | #11455, #11875, #12028 | +| [@cheynetom](https://github.com/cheynetom) | #12058 | +| [@davidebaraldo](https://github.com/davidebaraldo) | #12222, #12304 | +| [@davidlinfr](https://github.com/davidlinfr) | #6390 | +| [@ddarkr](https://github.com/ddarkr) | #12124 | +| [@Deftera186](https://github.com/Deftera186) | #11809 | +| [@dpozimski](https://github.com/dpozimski) | #11945 | +| [@DW-MediaLab](https://github.com/DW-MediaLab) | #12229 | +| [@echel0nn](https://github.com/echel0nn) | #11923 | +| [@f9td56dbgh-hub](https://github.com/f9td56dbgh-hub) | #11660 | +| [@fabioluissilva](https://github.com/fabioluissilva) | #11991 | +| [@foreveryh](https://github.com/foreveryh) | #12177 | +| [@geek007git](https://github.com/geek007git) | #12115, #12116, #12117, #12120, #12122 | +| [@geekyNads](https://github.com/geekyNads) | #11784, #12266 | +| [@ggiak](https://github.com/ggiak) | #11561, #12276, #12350, #12402 | +| [@gonisulaimann](https://github.com/gonisulaimann) | #12368, #12369, #12371 | +| [@Gorillaz322](https://github.com/Gorillaz322) | #12207 | +| [@groovecityJO](https://github.com/groovecityJO) | #12682 | +| [@hartmark](https://github.com/hartmark) | #11434, #11452, #11473, #11499, #11703, #11983, #11984, #11985, #11986, #11988, #11989, #11990, #11994, #12221, #12293, #12445, #12446, #12447, #12448, #12460, #12461, #12623, #12646, #12650, #12680, #12717, #12718, #12727, #12741, #12854 | +| [@hizzt](https://github.com/hizzt) | #11894 | +| [@hongnoul](https://github.com/hongnoul) | #11484 | +| [@HouMinXi](https://github.com/HouMinXi) | #11411, #11414, #11512, #11518, #11520, #11641, #11642, #11643, #11687, #11779, #11849, #11850, #11851, #11915, #11916, #11918, #11919, #11920, #12013, #12017, #12033, #12042, #12106, #12139, #12166, #12169, #12171, #12205, #12213, #12312, #12325, #12487, #12488, #12495, #12504, #12557, #12566, #12590, #12591, #12624, #12626, #12632, #12637, #12678, #12696, #12697, #12711, #12733, #12746, #12767, #12770, #12789, #12803, #12805, #12811, #12866, #12868, #12899, #12926, #12934, #12950, #12951, #12974, #13001, #13006, #13011, #13017, #13026, #13027, #13034, #13035, #13038, #13042, #13050, #13060, #13061, #13069, #13107, #13120, #13136, #13195, #13197 | +| [@Hsia97](https://github.com/Hsia97) | #11624 | +| [@jacobsparts](https://github.com/jacobsparts) | #11854, #12155, #12167 | +| [@joglomedia](https://github.com/joglomedia) | #11980 | +| [@jonlwheat2-gif](https://github.com/jonlwheat2-gif) | #11443, #11450, #11549, #11567, #11608, #11633, #11635, #11640, #11644, #11671, #11672, #11673, #11674, #11675, #11676, #11677, #11706, #11783, #12051, #12052, #12053, #12055 | +| [@kanade-hoshino](https://github.com/kanade-hoshino) | #12180 | +| [@Karan825](https://github.com/Karan825) | #11861 | +| [@KaspaPulse](https://github.com/KaspaPulse) | #11389, #11469, #11628, #11666, #11910 | +| [@keeltrace](https://github.com/keeltrace) | #12080, #12223 | +| [@killer30001000](https://github.com/killer30001000) | #12178 | +| [@KooshaPari](https://github.com/KooshaPari) | #12470, #12592, #12667, #12699, #12703, #12706, #12764, #12769, #12771, #13300, #13301, #13302, #13340, #13401, #13403, #13404, #13405, #13406, #13407, #13409, #13410, #13411, #13412, #13413, #13414, #13418, #13424, #13427, #13433, #13523, #13524, #13525, #13528, #13531, #13532, #13534, #13539, #13543, #13545, #13546, #13547, #13550, #13553 | +| [@kriptoburak](https://github.com/kriptoburak) | #11370 | +| [@KrzysiekSko](https://github.com/KrzysiekSko) | #12425, #12432, #12673 | +| [@marcelokarval](https://github.com/marcelokarval) | direct commit / report | +| [@maxmad64bis](https://github.com/maxmad64bis) | #1622, #11435, #11437, #11441, #11537, #11550, #11553, #11555, #11557, #11812, #11842, #11843, #11903, #12151, #12214, #12215, #12218, #12226, #12314, #12316, #12317, #12318, #12319, #12320, #12321, #12507, #12715, #12731, #12744, #12786, #12787, #12788, #12790, #12792, #12794, #12795, #12828, #12832, #12853, #12857, #12870, #12937, #12941, #12975, #13141, #13142, #13146, #13147 | +| [@mdigitalbh81](https://github.com/mdigitalbh81) | #12240 | +| [@Meet6338-X](https://github.com/Meet6338-X) | #11598, #11609, #12110 | +| [@morpheus9393](https://github.com/morpheus9393) | #11943 | +| [@MumuTW](https://github.com/MumuTW) | #11492, #11502, #11506, #11507, #11626, #11675, #11685, #11728, #11746, #11888, #11889, #11890, #11892 | +| [@Neuron-Mr-White](https://github.com/Neuron-Mr-White) | #11622, #11801, #12256 | +| [@NightStalker-87](https://github.com/NightStalker-87) | #12183 | +| [@NoxzRCW](https://github.com/NoxzRCW) | #11879, #11880, #11881, #11882, #11883, #11935 | +| [@ntdat812](https://github.com/ntdat812) | #11585, #12095 | +| [@ntdatt812](https://github.com/ntdatt812) | #11368, #11573, #11574, #11576, #11577, #11580, #11582, #11583, #11588, #11589, #11590, #11591, #11592, #11593, #11672, #12177, #12180, #12873, #12918, #12920, #12921, #12925, #12930, #13007, #13009, #13024, #13025, #13083, #13087, #13101, #13104, #13110 | +| [@official-burak](https://github.com/official-burak) | #11542 | +| [@opensource-elearning](https://github.com/opensource-elearning) | #12179, #12189, #12278, #12286 | +| [@oyi77](https://github.com/oyi77) | #11408, #11409, #11421, #11505, #11677, #12036, #12110 | +| [@pacocartones](https://github.com/pacocartones) | #11521, #11522, #11527, #11528, #11529, #11530, #11531, #11532, #11533, #11534, #11595, #11599, #11603, #11604, #11605, #11607, #11610, #11676, #11714, #11716, #11718, #11767, #11838, #11860, #11862, #11869, #11871, #11872, #11873, #11903, #11906, #11921, #11934, #12356, #12358, #12359, #12360, #12361, #12362, #12364, #12365, #12373, #12374, #12375, #12376, #12377, #12379, #12380, #12381, #12386, #12387, #12389, #12390, #12394, #12395, #12397, #12401, #12403, #12404, #12522, #12523, #12535, #12536, #12540, #12541, #12543, #12545, #12548, #12549, #12550, #12551, #12552, #12647, #12651, #12653, #13055 | +| [@patrykkopycinski](https://github.com/patrykkopycinski) | #11936, #11937, #12224 | +| [@PauloFH](https://github.com/PauloFH) | #11509 | +| [@PauloHSOliveira](https://github.com/PauloHSOliveira) | #12241 | +| [@ponkcore](https://github.com/ponkcore) | #12054 | +| [@Prajeeth-12](https://github.com/Prajeeth-12) | #11634, #12046 | +| [@quiterunner-commits](https://github.com/quiterunner-commits) | #12131, #12143 | +| [@rafacpti23](https://github.com/rafacpti23) | #11554, #11558, #12192, #12197, #12198, #12202, #12203, #12204 | +| [@ragnar-claude](https://github.com/ragnar-claude) | #11564 | +| [@raheemuddin786](https://github.com/raheemuddin786) | #11491, #11839, #11840, #11841, #12003, #12230, #12231, #12232, #12233, #12234 | +| [@rahilmavani](https://github.com/rahilmavani) | #11761 | +| [@Rahulsharma0810](https://github.com/Rahulsharma0810) | #11771 | +| [@RaviTharuma](https://github.com/RaviTharuma) | #11710, #11727, #11797, #11798, #11802, #11805, #11806, #11811, #12098, #12099, #12101, #12449, #12452, #12472, #12473, #12493, #12533, #12607, #12628, #12631, #12636, #12875, #12876, #12880, #12882, #12884 | +| [@rezjalibd](https://github.com/rezjalibd) | #12186 | +| [@RhianB14](https://github.com/RhianB14) | #11692 | +| [@rifqiawl](https://github.com/rifqiawl) | #11517, #11519, #11969 | +| [@rqzbeh](https://github.com/rqzbeh) | #11390 | +| [@Sabeekhann](https://github.com/Sabeekhann) | #11699, #11705 | +| [@santosraju99-hub](https://github.com/santosraju99-hub) | #11755, #11770 | +| [@Seramicx](https://github.com/Seramicx) | #11568 | +| [@Siva010](https://github.com/Siva010) | #12191 | +| [@solstxce](https://github.com/solstxce) | #11597 | +| [@soroush5](https://github.com/soroush5) | #12691 | +| [@tenshiak](https://github.com/tenshiak) | #12279 | +| [@TheDemonTuan](https://github.com/TheDemonTuan) | #11468, #11470, #11471, #11482, #11548, #11758, #11775, #11814 | +| [@thomasmaerz](https://github.com/thomasmaerz) | #12834 | +| [@tuandinh0801](https://github.com/tuandinh0801) | #11454 | +| [@turbolego](https://github.com/turbolego) | #11621, #11762, #11772, #11774, #11781, #12216 | +| [@Tushar49](https://github.com/Tushar49) | #11392 | +| [@ujjawalkaushik1110](https://github.com/ujjawalkaushik1110) | #11863 | +| [@vermasomesh835](https://github.com/vermasomesh835) | #11794 | +| [@vsd2807](https://github.com/vsd2807) | #11565, #11619 | +| [@wahidsadik371-coder](https://github.com/wahidsadik371-coder) | #12126 | +| [@watchingdogs](https://github.com/watchingdogs) | #12031 | +| [@wildcard](https://github.com/wildcard) | #11620 | +| [@xiaoyaner0201](https://github.com/xiaoyaner0201) | #11460, #11662, #12083 | +| [@Xxx91n](https://github.com/Xxx91n) | #11690 | +| [@yourspraveen](https://github.com/yourspraveen) | #11146 | +| [@yxyxy](https://github.com/yxyxy) | #11671 | +| [@ZaimMarzuki](https://github.com/ZaimMarzuki) | #11960 | +| [@Zartharas](https://github.com/Zartharas) | #11340 | +| [@zero-executioner](https://github.com/zero-executioner) | #11631 | +| [@diegosouzapw](https://github.com/diegosouzapw) | maintainer | + +--- ## [3.8.50] — 2026-08-25 _Living section — regenerated 2026-08-12 from all cycle commits (cycle open `ed2db6cb19` → tip). Bullets carry the merged PR and its author; direct pushes listed separately._ diff --git a/docs/i18n/hi/CHANGELOG.md b/docs/i18n/hi/CHANGELOG.md index e122f5a804..81ac3eaeab 100644 --- a/docs/i18n/hi/CHANGELOG.md +++ b/docs/i18n/hi/CHANGELOG.md @@ -8,16 +8,1119 @@ ## [3.8.51] — TBD -_Living section — cycle opened at the v3.8.50 freeze (parallel-cycle model). Bullets are aggregated from `changelog.d/` fragments at each `/generate-release` phase._ +_Living section — reconciled 2026-09-15 from all cycle commits (`091589089c` → `c0f92ec98a`, 916 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 | **123** | +| 📝 Commits in the cycle | **916** | +| 🔀 Pull requests referenced | **892** | +| 📋 Changelog entries | **919** | +| 🙌 Contributors credited in entries | **121** | +| 🤖 Automated dependency commits | 18 | + +**Entries by type** + +| Type | Count | +| --- | ---: | +| 🐛 Fixes | 592 | +| ✨ Features | 131 | +| 🧹 Chore | 93 | +| 📚 Docs | 42 | +| 🧪 Tests | 32 | +| ♻️ Refactor | 8 | +| 🏗️ Build | 7 | +| ⚡ Performance | 7 | +| ⚙️ CI | 2 | +| ⏪ Reverts | 2 | +| 📦 Dependencies | 2 | +| 🔒 Security | 1 | + +### 🏆 Top 25 contributors this cycle + +_By commits in `091589089c..c0f92ec98a`, author identities consolidated via `.mailmap` and the merged PR's GitHub login. Bots excluded._ + +| # | Contributor | Commits | +| ---: | --- | ---: | +| 🥇 | diegosouzapw | 323 | +| 🥈 | Bob.Hou (@HouMinXi) | 79 | +| 🥉 | Paco Cartones (@pacocartones) | 77 | +| 4 | Dizzle (@maxmad64bis) | 49 | +| 5 | Koosha Paridehpour (@KooshaPari) | 43 | +| 6 | Nguyen Thanh Dat (@ntdatt812) | 31 | +| 7 | Ravi Tharuma (@RaviTharuma) | 26 | +| 8 | Markus Hartung (@hartmark) | 22 | +| 9 | Webman (@jonlwheat2-gif) | 22 | +| 10 | backryun | 17 | +| 11 | anhtahaylove | 15 | +| 12 | Rafa Martins (@rafacpti23) | 10 | +| 13 | Syed Raheemuddin (@raheemuddin786) | 10 | +| 14 | Nguyễn Viết Tuấn (@TheDemonTuan) | 8 | +| 15 | MumuTW | 7 | +| 16 | Paijo (@oyi77) | 7 | +| 17 | Tobias Andersen (@turbolego) | 6 | +| 18 | Bl0ck (@Bl0ck154) | 5 | +| 19 | SHANMUGAPRIYAN (@geek007git) | 5 | +| 20 | KaspaPulse | 5 | +| 21 | Abhishek Sharma (@abhisheksharma2411) | 4 | +| 22 | Abhishek Divekar (@adivekar-utexas) | 4 | +| 23 | Andrew B. (@AndrianBalanescu) | 4 | +| 24 | NoxzRCW | 4 | +| 25 | opensource-elearning | 4 | ### ✨ New Features +- **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). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- Added Google AI Studio Gemini batch text-to-speech support through `POST /v1/audio/speech`. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- 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. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(routing):** subscription-first auto groupings — `auto/subscription` routes only through plan-included connections with a documented hard-stop overage and fails closed on exhaustion, while `auto/thrifty` orders the pool `subscription → keyless → free → cheap → premium` and steps up one rung at a time as each is exhausted. Billing class comes from a curated per-connection catalog (uncurated is treated as metered, never plan-included), both reuse STRICT_ZERO_COST's per-connection verification, and a quota reading whose `resetAt` has passed is now refreshed regardless of TTL so routing returns to plan capacity as soon as it resets ([#11146](https://github.com/diegosouzapw/OmniRoute/pull/11146)) — thanks @yourspraveen +- **feat(providers):** publish a management-authenticated versioned web-session credential contract from OmniRoute's canonical browser credential metadata ([#11340](https://github.com/diegosouzapw/OmniRoute/pull/11340)) — thanks @Zartharas +- **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)) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add Xquik X search with typed results, credential validation, REST routing, and MCP selection ([#11370](https://github.com/diegosouzapw/OmniRoute/pull/11370)) — thanks @kriptoburak +- **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)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(dashboard):** surface durable exclusive managed leases in the existing Sessions view, keeping leased clients visible across idle gaps while marking connections with in-flight work as active ([#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389)) — thanks @KaspaPulse +- **build(bun):** allow Turbopack bundler flag on Bun 1.4+ with configurable Webpack fallback ([#11471](https://github.com/diegosouzapw/OmniRoute/pull/11471)) — thanks @TheDemonTuan +- feat(api): add an opt-in `modelVisibilityAllowlist`/`modelVisibilityDenylist` settings pair to curate exactly which models `/v1/models` advertises, mirrored into every `auto/*` combo candidate pool so a denied model cannot be routed to via combo selection either (#11481) ([#11997](https://github.com/diegosouzapw/OmniRoute/pull/11997)) +- **feat(rankings):** the Free Provider Rankings page shows what each provider actually served over the last 24 h. It ranked by ELO alone, which left a provider that answers every call with an error in first place; the usage data was already served by the API but never requested. A provider with too small a sample shows a dash, not a number ([#11546](https://github.com/diegosouzapw/OmniRoute/pull/11546)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **feat(guardrails):** enforce a bounded, deterministic contract for Video Bridge transcripts — 256 cues, 4096 input code units and 4 KiB UTF-8 per cue, 64 KiB total text, malformed-Unicode rejection, focus-window scoping, cross-source reconciliation with contributing-source metadata, and a structural provenance trust boundary so caller JSON can never self-assert `embedded`/`audio-bridge` provenance ([#11652](https://github.com/diegosouzapw/OmniRoute/issues/11652)) ([#12009](https://github.com/diegosouzapw/OmniRoute/pull/12009)) +- **feat(video):** orchestrate optional Video Bridge audio extraction and Audio Bridge STT behind a dual opt-in (operator setting AND per-request signal) — a new loopback-only broker `mode=audio` operation shares the frame path's exact process queue, deadline, AbortSignal, and byte budgets to extract a bounded mono 16 kHz PCM WAV from the same already-downloaded video, then reuses the existing Audio Bridge transcription boundary; provider segment timing is preserved when available and marked coarse otherwise, and every failure degrades to a visual-only-safe partial instead of throwing (#11654). ([#12012](https://github.com/diegosouzapw/OmniRoute/pull/12012)) +- Add a tenant-bound Video Bridge drill-down lifecycle on top of the existing secure cache substrate: opaque hashed handles (never raw session/video identifiers), preview/standard/detail multiresolution variants resampled on read, response pagination capped at 8 frames and 32 MiB, and a new authenticated `/api/v1/video-bridge/drilldown` consumer route that stays disabled for remote access by default and denies cross-key access with the same response as a nonexistent handle (no existence oracle). ([#12006](https://github.com/diegosouzapw/OmniRoute/pull/12006)) +- **test(video):** Add the Video Bridge FU-07/FU-09 promotion-evidence harness (#11656) — a frozen Zod manifest schema covering the 8 required scenario kinds (static scenes, rapid cuts, late facts, fades, blur, small text, close events, visual prompt injection) with a minimum of 3 repetitions per case, deterministic declarative fixture recipes (`videoBridgePromotionFixtures.ts`), a pure medians/p95 metrics aggregator, a pure FU-07/FU-09 promotion-verdict evaluator applying the ticket's exact thresholds (missing token usage always holds), a digest-only persistence layer that never retains raw media or raw model responses, and a versioned per-model promotion allowlist shipped empty with every model defaulting to `hold`. The FU-07/FU-09 promotion verdicts themselves remain HOLD — they require a real evidence run against real models on VPS 192.168.0.15. ([#12008](https://github.com/diegosouzapw/OmniRoute/pull/12008)) +- **feat(video bridge):** "embedded" transcript provenance can now be legitimately earned instead of merely asserted — a bounded, allowlisted (`mov_text`/`subrip`/`webvtt`) subtitle probe runs through the loopback-only Video Bridge broker (at most 2 streams, 10s subdeadline bounded by the request deadline, 256 KiB output, 4096-code-unit lines), normalized through a bounded, ReDoS-safe WebVTT parser and Zod-validated end to end. The adapter always resolves to an explicit `success`/`absent`/`transient_failure` outcome — a subtitle failure never breaks the visual description path, and only a fingerprint-verified broker response (never a caller-declared label) can produce embedded cues (#11659). ([#12011](https://github.com/diegosouzapw/OmniRoute/pull/12011)) +- Default new Antigravity-family connections (agy CLI imports and Antigravity OAuth connects) to model auto-sync, so live model discovery lands in the synced catalog and `/v1/models` picks up freshly released upstream models (e.g. Gemini 3.7 Flash tiers) without code changes. Existing connections keep their current setting; the per-connection dashboard toggle remains the opt-out. (#11685 — thanks @MumuTW) ([#11887](https://github.com/diegosouzapw/OmniRoute/pull/11887)) +- **feat(ui):** enable React Compiler (`reactCompiler: true` + `babel-plugin-react-compiler`) for automatic memoization at build time ([#11783](https://github.com/diegosouzapw/OmniRoute/pull/11783)) — thanks @jonlwheat2-gif +- **feat(providers):** add SeekAi (`seekai.cc`) as an OpenAI-compatible New-API gateway — catalog id `seekai` (alias `ska`), `https://seekai.cc/v1`, live `/v1/models` via `passthroughModels`, aggregator-list membership so New-API balance detection can opt in. No referral/aff codes. ([#11786](https://github.com/diegosouzapw/OmniRoute/issues/11786)) ([#12557](https://github.com/diegosouzapw/OmniRoute/pull/12557)) — thanks @HouMinXi +- **feat(zai):** add GLM-5.3-Flash Coding Plan support (1M context, 128K output, vision, `low|high|max` reasoning) and route `zai` GLM-5.3-family API-key traffic through the OpenAI-compatible Coding Plan endpoint with native thinking defaults ([#11801](https://github.com/diegosouzapw/OmniRoute/pull/11801)) — thanks @Neuron-Mr-White +- **feat(combo):** choose how combo models are ordered — manual, provider, score, or name — via a sort control in the dashboard builder, persisted in `config.modelSort` and re-applied on load and after add ([#11812](https://github.com/diegosouzapw/OmniRoute/pull/11812)) — thanks @maxmad64bis +- **feat(free):** custom models can be marked free-tier via `customModels[].isFree`; `isFreeModel()` is the first door and `hidePaidModels` respects it even for providers outside the free budget ([#11843](https://github.com/diegosouzapw/OmniRoute/pull/11843)) — thanks @maxmad64bis +- **feat(nodejs):** add `5dive` as a `configure` target — `omniroute configure 5dive` / `omniroute setup-5dive` write a 5dive auth profile that points an agent fleet's `claude` seats at OmniRoute, with the root-only write, the loopback-vs-`https` endpoint rule and the per-seat model pin handled explicitly ([#11852](https://github.com/diegosouzapw/OmniRoute/pull/11852)) — thanks @5dive-bot +- **feat(sse):** treat `max` as a first-class reasoning-effort tier and clamp per model family (GLM 5.1+/DeepSeek V4+/Kimi K3+ keep native `max`; o1/MiniMax/Grok/Muse Spark clamp to their upstream ceiling) ([#11875](https://github.com/diegosouzapw/OmniRoute/pull/11875)) — thanks @Chewji9875 +- **feat(providers):** the provider plugin manifest now advertises a `usage-fetch` capability for the 40 providers that have a wired usage/quota fetcher, so external dashboards can read it from `GET /api/v1/provider-plugin-manifest` instead of parsing `open-sse/services/usage.ts` after every release. Discovery only — no new fetcher, no quota change, and the Dashboard quota widget stays gated by `USAGE_SUPPORTED_PROVIDERS`. `USAGE_FETCHER_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/fetcherProviders.ts`) and is re-exported from `services/usage.ts`, keeping the manifest module a light leaf instead of pulling the ~490-module usage dispatcher into the manifest route. ([#11903](https://github.com/diegosouzapw/OmniRoute/pull/11903)) — thanks @maxmad64bis / @pacocartones +- **feat(plugins):** `OMNIROUTE_PLUGINS_DIR` sets the directory the runtime plugin scanner reads — and the root the plugin manager installs into — overriding the `HOME`-derived default, so a Docker/K8s deployment can point straight at its bind-mounted plugin tree instead of moving `HOME` just to relocate the scan path. An image that exports no home no longer scans `/tmp/.omniroute/plugins` in silence: the resolved directory is logged once at startup as `scanner.dir_resolved`, naming the input that won. Unset, behaviour is unchanged. Distinct from the CLI-only `OMNIROUTE_PLUGIN_PATH`, which finds `omniroute-cmd-*` command packages and never reached this scanner ([#11906](https://github.com/diegosouzapw/OmniRoute/pull/11906)) — thanks @amaleta / @pacocartones +- **feat(leases):** add an explicit owner-authenticated status action that returns only the active lease's privacy-safe configured connection and provider labels, with generation fencing and no credential or internal-id disclosure ([#11910](https://github.com/diegosouzapw/OmniRoute/pull/11910)) — thanks @KaspaPulse +- **feat(providers):** import-from-file modal shows per-row API errors and ships a downloadable CSV template ([#12071](https://github.com/diegosouzapw/OmniRoute/issues/12071)) ([#12504](https://github.com/diegosouzapw/OmniRoute/pull/12504)) — thanks @HouMinXi +- **feat(providers):** dashboard search matches connection name and `baseUrl` so imported OpenAI-compat nodes surface on the provider card ([#12108](https://github.com/diegosouzapw/OmniRoute/issues/12108)) ([#12495](https://github.com/diegosouzapw/OmniRoute/pull/12495)) — thanks @HouMinXi +- **feat(routing):** add a `score` Auto router strategy that selects the highest configured weighted score and reuses `explorationRate`. ([#12155](https://github.com/diegosouzapw/OmniRoute/pull/12155)) — thanks @jacobsparts +- Add a runtime feature flag to disable universal context handoffs globally without changing the default behavior. ([#12167](https://github.com/diegosouzapw/OmniRoute/pull/12167)) — thanks @jacobsparts +- **feat(providers):** the provider plugin manifest now also advertises a `usage-supported` capability for the 46 providers whose usage API is accepted by the server and Dashboard routes, so integrators can distinguish "the server will serve quota for this provider" from "a fetcher is wired" without reading TypeScript. Discovery only — no fetcher or quota change. `usage-fetch` resolves on id or alias (the usage dispatcher accepts both); `usage-supported` resolves on id alone, matching the runtime guard `USAGE_SUPPORTED_PROVIDERS.includes(providerId)`. `USAGE_SUPPORTED_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/supportedProviders.ts`) and is re-exported from `providers.ts`, mirroring the `fetcherProviders` leaf from #11903 and keeping the manifest a light module. ([#12214](https://github.com/diegosouzapw/OmniRoute/pull/12214)) — thanks @maxmad64bis +- **feat(rankings):** order Free Provider Rankings by what each provider actually served — `GET /api/free-provider-rankings?sortBy=reliability` and a "Most reliable first" toggle on the page. Providers with too few calls to state a success rate keep their score order below the measured ones; the default order is unchanged ([#12218](https://github.com/diegosouzapw/OmniRoute/pull/12218)). — thanks @maxmad64bis +- **perf(sse):** defer `cloneLogPayload()` in the structured SSE collector until after the `maxEvents`/`maxBytes` cap check, eliminating ~9,800 wasted `structuredClone` calls per streaming response (65–71% faster `push()`). Reducer snapshot isolation restored for OpenAI and Responses summaries ([#12241](https://github.com/diegosouzapw/OmniRoute/pull/12241)) — thanks @PauloHSOliveira ([#12243](https://github.com/diegosouzapw/OmniRoute/pull/12243)) +- **feat(usage):** Devin CLI agentic quota (Codeium seat-management GetUserStatus) and OpenRouter key limits plus account credits now surface in Provider Limits ([#12256](https://github.com/diegosouzapw/OmniRoute/pull/12256) — thanks @Neuron-Mr-White) +- **feat(settings):** persist `headroomUrl` through Settings so status/start use the operator URL instead of only `HEADROOM_URL` ([#12306](https://github.com/diegosouzapw/OmniRoute/issues/12306)) ([#12487](https://github.com/diegosouzapw/OmniRoute/pull/12487)) — thanks @HouMinXi +- **feat(auto-combo):** Auto-Combo scoring can now weigh how often a provider/model has actually succeeded. The engine already carried that number on every candidate — 24 hours of usage history behind a ten-sample floor, real-time metrics otherwise — and the scoring function never read it, while the weight table described `stability` as if it did. `reliability` (`1 - failureRate`, with the same field precedence and the same rate-bounding the speed ranking already uses, so a corrupt reading means "nothing observed" rather than "fails every call") is now a declared factor shipping at weight `0`, so routing is unchanged until an operator gives it one, and the `stability` description now matches what that factor computes ([#12317](https://github.com/diegosouzapw/OmniRoute/pull/12317)) — thanks @maxmad64bis +- **feat(routing):** With `freeAccessPolicy: "strict"`, the read-only candidate listing (`GET /v1/auto-combo/{channel}/candidates`) no longer hides the candidates the zero-cost guard excludes — the same read-only transparency the resilience filter already honours (#9133). Each candidate now carries `freeAccessExclusion` saying why it would be kept out, and it tells an exhausted allowance apart from a quota reading that never arrived or went stale, which used to look identical from the outside. Routing is unchanged: the listing reports, it never enforces. The separate `excludeTosAvoid` guard still drops its candidates without a reason; that gap is now documented rather than closed ([#12319](https://github.com/diegosouzapw/OmniRoute/pull/12319)) — thanks @maxmad64bis +- **feat(radar):** The Radar catalog table now shows two facts it was already receiving from the feed and dropping on the floor: the per-model rate limits (requests and tokens, per minute and per day) in a new column, and a badge when a provider's terms state it may train on the prompts you send. A limit of zero renders as zero rather than "rate-only" — for a ceiling those are opposite facts — and a model with no training statement gets no badge, because an absent statement is not a guarantee ([#12320](https://github.com/diegosouzapw/OmniRoute/pull/12320)) — thanks @maxmad64bis +- **feat(radar):** explain Community, single-use, contributor, supporter, recovery, abuse, offers, and privacy rules before either Radar activation action, and remove the superseded fixed-PR grant promise from every UI locale ([#12342](https://github.com/diegosouzapw/OmniRoute/pull/12342)) +- **feat(gamification):** the dashboard Profile page now shows the real daily streak — `/api/gamification/level` returns `streak: { current, longest }` (per key with `apiKeyId`, operator-wide maximum otherwise) and the streak card reads it instead of a hard-coded 0 (#2403) ([#12377](https://github.com/diegosouzapw/OmniRoute/pull/12377)) — thanks @pacocartones +- **feat(gamification):** the dashboard leaderboard now shows each API key's display name under the Name column instead of a truncated key id; `GET /api/gamification/leaderboard` attaches `name` per entry (name only — no key material), while the shared ranking helper and the federation leaderboard stay id-only — thanks @pacocartones ([#12385](https://github.com/diegosouzapw/OmniRoute/pull/12385)) +- **feat(gamification):** enforce the documented 1000 XP/min per-API-key anti-cheat rate limit on the XP award path; over-limit awards are logged and skipped instead of persisted, and the sliding window now matches the timestamp format stored in `xp_audit_log` ([#2403](https://github.com/diegosouzapw/OmniRoute/issues/2403)) ([#12390](https://github.com/diegosouzapw/OmniRoute/pull/12390)) — thanks @pacocartones +- **feat(admin):** localize the gamification anomalies page — the loading state, the Status column and the Suspicious badge now come from the `common` catalog (new `common.suspicious` key propagated to every locale) — add it to the Gamification sidebar group as `gamification-admin` (`/dashboard/gamification/admin`), and expose the loading and empty states as polite `role="status"` live regions (#12401 — thanks @pacocartones) +- **feat(providers):** skip GitHub combo members missing from the live synced catalog, and drop Copilot models that are policy-disabled or hidden from the model picker ([#12473](https://github.com/diegosouzapw/OmniRoute/pull/12473)) — thanks @RaviTharuma +- **feat(i18n):** locale-expansion tooling — `npm run i18n:add-locale` adds a language to config, dashboard, docs mirrors, CLI, README/indexes and site in one command; browser, CLI and cookie detection resolve `uk`, `fil`/`tl`, `zh-Hant` (and the retired legacy `in` → `id`) via config aliases; `config/i18n.json` now ships in the npm package so the published CLI can read it; new real-translation ratio gate (`npm run i18n:check-ratio`, advisory) with a per-locale ratchet baseline; `run-translation --adopt` restores `.i18n-state.json`; `sync-language-bars` generates 🌐 bars from config; `validate_translation.py` loads its allowlist again. Retires the duplicate `in` locale (Indonesian mislabelled as Hindi) — 42 honest locales; saved `NEXT_LOCALE=in` / `OMNIROUTE_LANG=in` keep working. (#12496) +- **feat(api):** Emit gateway-measured `tokens_per_second` (TTFT excluded) on streaming usage and `X-OmniRoute-Tokens-Per-Second` when first-token latency is known ([#12616](https://github.com/diegosouzapw/OmniRoute/issues/12616)) ([#12631](https://github.com/diegosouzapw/OmniRoute/pull/12631)) — thanks @RaviTharuma +- **feat(opencode-plugin): map gateway cost/usage/tok/s onto OpenCode inference payloads** — the official plugin copies `X-OmniRoute-Response-Cost`, token counts, `X-OmniRoute-Tokens-Per-Second` / `usage.tokens_per_second`, TTFT, and the winning `X-OmniRoute-Model` onto the JSON/SSE body OpenCode already consumes. Missing tok/s is left unset (never `tokens / latency`). (#12636) — thanks @RaviTharuma +- **feat(combo):** add combo strategy `quota-weighted`: skip exhausted accounts, then weighted-draw by leftover / in-flight load; existing conversations stay pinned until the account is empty ([#12789](https://github.com/diegosouzapw/OmniRoute/pull/12789)) — thanks @HouMinXi +- **feat(models):** Account live listings become the chat catalog source for Claude, Codex, Copilot, and AGY; public metadata only fills prices on IDs those accounts already list. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **fix(models):** Union `agy` and `antigravity` live catalogs so `agy/gemini-3.8-flash-high` is not rejected after the prefix folds to `antigravity`. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **feat(opencode):** Add `@omniroute/opencode-plugin-v2`, an opencode v2 plugin that gives a v2 install a route to an OmniRoute gateway: it publishes models, combos and auto-combos into the host catalog, refreshes them lazily behind a 300s TTL, and keeps serving the last known catalog from an on-disk snapshot when the gateway is unreachable. The v1 plugin (`@omniroute/opencode-plugin`) is untouched and keeps working as before. The plugin adapts to the host instead of assuming one shape: it reads the shape the host seeds into the catalog draft and publishes the `api` block, the pre-`api` top-level fields, or both. The gateway key comes from the host credential store when one is connected, so no secret needs to sit in a config file; a source the gateway refuses is named in a warning instead of silently degrading the catalog to raw model ids; and tool schemas bound for a Gemini model are stripped of the JSON-Schema keywords Gemini rejects, so tool calling survives that route ([#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870)) — thanks @maxmad64bis +- feat(services): show sanitized CLIProxyAPI account health from its authenticated management API without exposing credentials, file paths, or raw account metadata (#6342) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add AnySearch free web search + URL extract (webFetch) with typed results, credential validation, REST routing, and MCP selection - fallback-only ([#11690](https://github.com/diegosouzapw/OmniRoute/pull/11690)) — thanks @Xxx91n +- **feat(dashboard):** display clamped `[0, 100]%` cached input token ratio in request logs table ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) — thanks @b3nw +- **feat(catalog):** add `OMNIROUTE_DISABLE_THINKING_LEVEL_VARIANTS` feature flag to optionally filter out thinking level variants from model catalog ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) — thanks @b3nw +- **feat(dashboard):** continuously export call logs to external analytics stores. A pluggable destination registry ships the full Logs-tab record set on an hourly `JobRegistry` cron, with a persisted per-destination cursor, batched inserts, a config UI rendered from each destination's own field descriptors, and a REST layer (`/api/log-export/*`) for CRUD, a connection test, and an on-demand run. A destination can opt into `includeBodies` to also ship the request and response payloads shown in the Logs detail pane, including the client and provider views of each call; this is off by default, and payloads inherit the dashboard's PII sanitisation, secret redaction and `noLog` handling. Google BigQuery is the first destination, using a service-account key stored encrypted at rest and streaming inserts keyed by call-log id, into a table that is day-partitioned on `timestamp` and clustered on `api_key_name`, `provider`, `model` and `status`. ([#11945](https://github.com/diegosouzapw/OmniRoute/pull/11945)) — thanks @dpozimski +- **feat(providers):** add MaxAI as a signed, OpenAI-compatible provider serving its 13 paid chat models (GPT-5.6 / Luna / Thinking, Claude 5 Sonnet, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite, Grok 4.1-fast / 4.5, DeepSeek V3.2 / R1, Llama 3.3 70B) through OmniRoute's `/v1` endpoint, with per-request HMAC-SHA1→SM3→AES request signing, live model + context-window discovery from `/models/get_config`, and prompted tool-calling translated to OpenAI `tool_calls` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI vision input — image_url content parts are forwarded inline in `message_content` to the 6 vision-capable models (GPT-5.6 / Luna / Thinking, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite) ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI image generation — 6 image models (gpt-image-1, dall-e-3, flux-1-schnell/dev/pro, sd3-medium) exposed through `POST /v1/images/generations` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI document RAG — inline base64 file/document attachments are uploaded to MaxAI (content-addressed `doc_id`) and attached to the chat via `doc_list` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** browserless MaxAI onboarding — email device-pair login (`/api/providers/[id]/login`) and signed access-token refresh, so a connection can be created and kept fresh without a real browser or Google OAuth ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** per-provider TLS impersonation profile (MaxAI presents a Windows Firefox-150 client fingerprint) so its bot-sensitive endpoints accept OmniRoute traffic ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** add **Nimble** as a web-search and web-fetch provider (`nimble-search`) — `/v1/search` routes to Nimble's search API at `lite` depth (locale, freshness and include/exclude domain filters mapped onto the shared request contract), and `/v1/web/fetch` routes to Nimble Extract, which covers all four fetch formats (`markdown`, `html`, `links`, `screenshot`) from a single call. One API key serves both surfaces. ([#11620](https://github.com/diegosouzapw/OmniRoute/pull/11620)) — thanks @wildcard +- The npm publish is automatic again, through npm Trusted Publishing (OIDC): the hosted `stage-npm` job publishes with a short-lived credential minted from GitHub's id-token — no `NPM_TOKEN`, no 2FA prompt, provenance attached. `publish_mode=staged` (owner approves with 2FA) and `direct` (token) remain available on `workflow_dispatch`. ([#11931](https://github.com/diegosouzapw/OmniRoute/pull/11931)) +- **feat(providers):** Add **Opper** as an API-key gateway provider — EU-hosted AI gateway with 700+ models from 30+ providers behind one OpenAI-compatible API and one key (`OPPER_API_KEY`); model ids use `provider/model` format (e.g. `anthropic/claude-sonnet-4-6`, `openai/gpt-5`); live model catalog at `https://api.opper.ai/v3/compat/models`; entry mirrors `requesty` (same shape, `passthroughModels: true`, no static seed) ([#11629](https://github.com/diegosouzapw/OmniRoute/pull/11629)) +- **feat(dashboard):** the `/dashboard/orchestration` snapshot hook now subscribes to the `agents` WebSocket channel (`agent.task.updated`) instead of `requests` as its refetch trigger, and relaxes its background poll from 5s to 30s while that WS connection is up — falling back to the tighter 5s cadence, reprogrammed live on any connect/disconnect transition, whenever the socket is down. ([#12409](https://github.com/diegosouzapw/OmniRoute/pull/12409)) +- **feat(dashboard):** Orchestration canvas quick wins — search box plus state/source/provider filter chips with a one-click clear, and per-source collapse/expand, all reflected in the URL so a filtered/collapsed view is shareable and survives a refresh; the detail drawer gained a "copy trace JSON" action and hardened error/empty-state and accessibility handling; the Agents-tab edges now animate traveling particles along active (running) connections; and the canvas node/edge status colors moved off fixed hex values onto theme-aware `--orch-status-*` CSS custom properties, so they adapt correctly to light/dark mode. ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- **chore(dashboard):** Orchestration UI hardening pass and the missing component/model test coverage it called for — `OrchestratorNode`/`ActivityNode`/`OverflowNode` rendering, the `?node=`/overflow-click page routing, the Agents-tab orchestrator-click no-op and `showCompleted` toggle, and the overview kanban's done-column sort order (#12270, #12271). ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- New `/dashboard/orchestration` page: live unified view of everything running — Cloud Agent, A2A and Conductor as a real-time graph (Agents tab), the combo cascade (Routing tab, reusing the Combo Live Studio) and a state kanban (Overview tab), with a detail drawer (trace, cost, approve/cancel). Read-only over existing APIs — no new backend. Canvas concept credit: PR #11815 design ([#12261](https://github.com/diegosouzapw/OmniRoute/pull/12261)) +- **feat(dashboard):** new "History" tab on `/dashboard/orchestration` — an Airflow-style grid of finished runs over a 24h/7d/30d preset window, one row per (source, identity), clicking a cell opens the existing detail drawer. It is backed by real persistence: A2A task lifecycle transitions are now written to the `a2a_tasks` table (purged after 30 days, configurable via `OMNIROUTE_A2A_HISTORY_RETENTION_DAYS`) and served by the new `GET /api/a2a/tasks/history` listing endpoint, with the task-detail route falling back to persisted history once a run leaves the in-memory snapshot. Conductor runs stay remote and are not persisted locally — the tab says so instead of silently omitting them. ([#12479](https://github.com/diegosouzapw/OmniRoute/pull/12479)) +- **feat(dashboard):** the orchestration detail drawer gained a "Repeat" action for Cloud Agent, A2A and Conductor tasks — a two-click confirm (click once to arm, click again within the confirm window to fire) re-submits the original prompt/input as a new run. The button is disabled with an explanatory tooltip whenever the original input can't be recovered from the loaded task detail (e.g. it never carried a prompt, or the detail failed to load). Two limitations of the A2A variant, by design: it targets the `/a2a` JSON-RPC endpoint, which authenticates with an API key only (`REQUIRE_API_KEY=true` or a configured `OMNIROUTE_API_KEY` makes a dashboard-session repeat answer `HTTP 400` — surfaced verbatim in the drawer's error line, never as a success), and the `message/send` call is SYNCHRONOUS: the POST blocks for the whole skill run, so the success confirmation only appears once the repeated task finishes. A dashboard-authenticated A2A creation path is deliberately left to a follow-up — widening the endpoint's auth posture is an operator decision, not a side effect of this feature. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(a2a):** A2A task execution now records which memories were consulted for the task's last user message as `metadata.memoryHits` (id/key/type/content-snippet) plus a `memory_hits` history event, purely for observability — the retrieved memory is never injected into a skill's prompt or behavior. Gated by the `OMNIROUTE_A2A_MEMORY_HITS` kill-switch (default enabled; set to `0` to skip the recall lookup entirely). The drawer's new "Memory used" section lists these hits for a2a tasks and is omitted whenever there are none. Known limitation: recall only resolves under the keyless posture — a keyed caller's task owner is a SHA-256 prefix of the API key, while memory rows are keyed by the database api-key id, and no hash→id lookup exists today, so the hit list stays empty for keyed callers. The recorded hits are also kept out of the task's own `input` (and therefore out of the persisted input and of the "Repeat" request body), so repeating a task never re-sends the previous run's memory snippets. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(providers):** add a Perplexity Agent API provider (`perplexity-agent` / `pplx-agent`) for Perplexity `/v1/responses`, including the documented Anthropic, OpenAI, Google, xAI, DeepSeek, Z.AI, Moonshot/Kimi, NVIDIA, and Perplexity model IDs plus Anthropic-model `max_output_tokens` compatibility. ([#12103](https://github.com/diegosouzapw/OmniRoute/pull/12103)) — thanks @AIB1TAL0S +- **feat(providers):** add RPD (Requests Per Day) limit to provider rate limit overrides across UI, schemas, DB, and i18n ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) — thanks @b3nw +- **feat(providers): add UC Direct (uncensored.com Developer API), the metered OpenAI-compatible surface.** A standard OpenAI-compatible passthrough (default executor) for uncensored.com's official REST API at `https://api.uncensored.com/api/v1`: `X-api-key` auth (never-expiring `uai_sk_live_` key), `POST /chat/completions` with streaming SSE and native tool-calling, and the full live metered catalog (82 models across 15 providers, discovered from the public `GET /v1/models`). Registered as provider `uc-direct` (alias `ucd`). Complements the un-metered `uc` persona provider — same models, metered credits and a plain API key instead of a subscription session. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(providers): add UC (uncensored.com), the un-metered subscription "persona" chat as an OpenAI-compatible provider.** A WebSocket web-app port: a durable Clerk credential mints a short-lived session token per connect (browserless — no API key), driving UC's persona socket. Ships the browserless email-code login (request → verify → harvest), the 19 verified persona models (Claude Opus, Gemini, Grok, GLM, Kimi, DeepSeek, MiniMax, incl. the uncensored variants), prompted `` tool-calling with a per-model code-style dialect + auto-cure retry for guardrailed models, live ``/reasoning split, streaming + non-streaming OpenAI responses, and full quota/auth error surfacing (paywall / message-limit / rate-limit → 429, invalid session → 401 re-login). Full multimodal parity via the persona blob-upload layer: **vision** (image input, 15 vision-capable models), **document RAG** (PDF/doc upload, server-side extraction), **image generation** (22 models), **video generation** (14 models, async signed-url → poll), and **TTS** (streaming MP3). Registered as provider `uc` (alias `ucn`). The metered OpenAI-compatible Developer API is a separate `uc-direct` provider. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(logs):** add copy-all button to request detail modal ([#11083](https://github.com/diegosouzapw/OmniRoute/pull/11083)) — thanks @AndrianBalanescu +- **feat(check):** make serviceKinds required and add the reverse-walk provider consistency gate ([#11392](https://github.com/diegosouzapw/OmniRoute/pull/11392)) — thanks @Tushar49 +- **feat(providers):** only advertise verified-working connections by default ([#11448](https://github.com/diegosouzapw/OmniRoute/pull/11448)) — thanks @alltomatos +- **feat(resilience):** add hierarchical concurrency admission ([#11493](https://github.com/diegosouzapw/OmniRoute/pull/11493)) +- **feat(providers):** derive + surface expiry for JWT-bearing web cookies (#11497) ([#11505](https://github.com/diegosouzapw/OmniRoute/pull/11505)) — thanks @oyi77 +- **feat(tunnels):** support named cloudflared tunnels via CLOUDFLARED_CONFIG ([#11561](https://github.com/diegosouzapw/OmniRoute/pull/11561)) — thanks @ggiak +- **feat(compression):** make proactive context-compression threshold a live setting ([#11564](https://github.com/diegosouzapw/OmniRoute/pull/11564)) — thanks @ragnar-claude +- **feat(resilience):** add provider peak-hour protection ([#11622](https://github.com/diegosouzapw/OmniRoute/pull/11622)) — thanks @Neuron-Mr-White +- **feat(providers):** add 1min.ai provider ([#11631](https://github.com/diegosouzapw/OmniRoute/pull/11631)) — thanks @zero-executioner +- **feat(dashboard):** collapsible JSON tree viewer for request/response payloads ([#11703](https://github.com/diegosouzapw/OmniRoute/pull/11703)) — thanks @hartmark +- **feat(routing):** add LiquidAI LFM2.5-2.6B free tier via OpenRouter ([#11752](https://github.com/diegosouzapw/OmniRoute/pull/11752)) — thanks @brick30llc-ctrl +- **feat(pricing):** add GLM-5.3-Flash pricing, model specs, and catalog registration ([#11830](https://github.com/diegosouzapw/OmniRoute/pull/11830)) — thanks @AndrianBalanescu +- **feat(routing):** add relayMode for schema-locked context handoffs ([#11839](https://github.com/diegosouzapw/OmniRoute/pull/11839)) — thanks @raheemuddin786 +- **feat(config):** add support for runtime system prompt configuration and hot-reloading ([#11841](https://github.com/diegosouzapw/OmniRoute/pull/11841)) — thanks @raheemuddin786 +- **feat(routing):** expand connection-aware quota prefilter across combo strategies (#11682) ([#11850](https://github.com/diegosouzapw/OmniRoute/pull/11850)) — thanks @HouMinXi +- **feat(models):** reactive discovery sync after Antigravity model-not-found 404 ([#11888](https://github.com/diegosouzapw/OmniRoute/pull/11888)) — thanks @MumuTW +- **feat(models):** auto-aliases and token limits from synced Antigravity discovery ([#11889](https://github.com/diegosouzapw/OmniRoute/pull/11889)) — thanks @MumuTW +- **feat(settings):** raise sticky round-robin limit caps to 1000 ([#12015](https://github.com/diegosouzapw/OmniRoute/pull/12015)) — thanks @adivekar-utexas +- **feat(resilience):** operator-configurable global credential health check interval ([#12043](https://github.com/diegosouzapw/OmniRoute/pull/12043)) — thanks @adivekar-utexas +- **feat(quota):** use official OpenCode Go usage API ([#12124](https://github.com/diegosouzapw/OmniRoute/pull/12124)) — thanks @ddarkr +- **feat(resilience):** default the credential health check sweep to 60 minutes ([#12138](https://github.com/diegosouzapw/OmniRoute/pull/12138)) — thanks @adivekar-utexas +- **feat(quality):** new-code mode for the complexity and dead-code ratchets (Clean as You Code) ([#12142](https://github.com/diegosouzapw/OmniRoute/pull/12142)) +- **feat(dashboard):** orchestration canvas — unified model + snapshot hook (part 1/2) ([#12156](https://github.com/diegosouzapw/OmniRoute/pull/12156)) +- **feat(usage):** add Kilo Code balance and Kilo Pass quotas ([#12178](https://github.com/diegosouzapw/OmniRoute/pull/12178)) — thanks @killer30001000 +- **feat(providers):** refresh vendored ChatGPT Web connector to v4.0.7 ([#12181](https://github.com/diegosouzapw/OmniRoute/pull/12181)) — thanks @backryun +- **feat(providers):** manual "Clear cooldown" action in the cooling panel ([#12224](https://github.com/diegosouzapw/OmniRoute/pull/12224)) — thanks @patrykkopycinski +- **feat(providers):** restore ChatGPT Web via clean-room browser transport ([#12239](https://github.com/diegosouzapw/OmniRoute/pull/12239)) — thanks @backryun +- **feat(sse):** wire the PROVIDER_PROFILES window gate into the global provider cooldown ([#12247](https://github.com/diegosouzapw/OmniRoute/pull/12247)) +- **feat(providers):** modernize CLOVA Studio chat and embeddings ([#12277](https://github.com/diegosouzapw/OmniRoute/pull/12277)) — thanks @backryun +- **feat(browser):** adopt Obscura as primary headless browser engine with Chromium fallback ([#12286](https://github.com/diegosouzapw/OmniRoute/pull/12286)) — thanks @opensource-elearning +- **feat(sse):** full language parity for output styles (es/de/fr/it/ru/zh + autoDetect) ([#12289](https://github.com/diegosouzapw/OmniRoute/pull/12289)) +- **feat(quality):** complete test:scoped — --full map rebuild, stdin selection, CI loader parity (#8084 D1) ([#12353](https://github.com/diegosouzapw/OmniRoute/pull/12353)) +- **feat(providers):** refresh Fable, Cursor, and Devin catalogs ([#12367](https://github.com/diegosouzapw/OmniRoute/pull/12367)) — thanks @backryun +- **feat(video):** redact transcript text from logs and durable memory (#12150 P1) ([#12427](https://github.com/diegosouzapw/OmniRoute/pull/12427)) +- **feat(dashboard):** parent-link, genuine-continuation badge, and modal perf fixes ([#12448](https://github.com/diegosouzapw/OmniRoute/pull/12448)) — thanks @hartmark +- **feat(dashboard):** adaptive context-budget dial on compression panel ([#12488](https://github.com/diegosouzapw/OmniRoute/pull/12488)) — thanks @HouMinXi +- **feat(providers):** refresh Z.ai Web models and browser transport ([#12524](https://github.com/diegosouzapw/OmniRoute/pull/12524)) — thanks @backryun +- **feat(video):** redact raw client-snapshot transcript fields in the detailed log (#12150 P2) ([#12528](https://github.com/diegosouzapw/OmniRoute/pull/12528)) +- **feat(providers):** refresh NVIDIA hosted models ([#12538](https://github.com/diegosouzapw/OmniRoute/pull/12538)) — thanks @backryun +- **feat(quota):** Moonshot Open Platform balance and TPD lock for custom nodes ([#12590](https://github.com/diegosouzapw/OmniRoute/pull/12590)) — thanks @HouMinXi +- **feat(video):** redact transcript in the in-memory pending-request snapshot (#12430 item 6) ([#12596](https://github.com/diegosouzapw/OmniRoute/pull/12596)) +- **feat(dashboard):** link a log entry's Conversation Context to its owning conversation ([#12646](https://github.com/diegosouzapw/OmniRoute/pull/12646)) — thanks @hartmark +- **feat(catalog):** eligibility-gated free-tier bucket ([#12669](https://github.com/diegosouzapw/OmniRoute/pull/12669)) +- **feat(db):** fail-closed previous_response_id continuation for redacted video turns (#12150 P2b) ([#12707](https://github.com/diegosouzapw/OmniRoute/pull/12707)) +- **feat(gamification): pay the documented `streak_bonus` and `badge_unlock` XP rewards.** `XP_REWARDS` listed both rewards but the award pipeline never paid them: the private reward table in `events.ts` omitted them, `updateStreak()` did not report when a streak extended, and badge unlocks carried no XP. Every request that extends a daily streak now pays `streak_bonus × streak length` once per UTC day (guarded by a same-day `xp_audit_log` check), and every badge unlocked through the pipeline pays `badge_unlock` once per badge (guarded by the `user_badges` primary key; `unlockBadge()` now reports whether it inserted). Bonus XP flows through the same `addXp` + level sync + global/weekly/monthly leaderboard path as action XP, so level-ups and rankings include it. The Radar supporter recognition unlock stays XP-free. (#12522 — thanks @pacocartones) +- **feat(providers):** advertise a `free-tier` capability in the provider plugin manifest for every provider with documented free models, so sidecars and dashboards can filter free-capable providers without reading the quota catalog ([#12786](https://github.com/diegosouzapw/OmniRoute/pull/12786)) — thanks @maxmad64bis +- **feat(providers):** Added EURouter as an OpenAI-compatible API-key gateway (`https://api.eurouter.ai/v1`), with live model discovery via `passthroughModels`. Its copy states that models are served by third-party upstreams listed per model, so an EU-based router is not read as EU data residency for inference. ([#13025](https://github.com/diegosouzapw/OmniRoute/pull/13025)) — thanks @ntdatt812 +- **feat(providers):** Added GreenPT as an OpenAI-compatible API-key provider (`https://api.greenpt.ai/v1`), with live model discovery via `passthroughModels`. No free-inference badge: the published docs describe a free API subscription billed per token, not a free tier. ([#13024](https://github.com/diegosouzapw/OmniRoute/pull/13024)) — thanks @ntdatt812 +- **feat(i18n):** 9 new locales — Greek (`el`), Croatian (`hr`), Serbian (`sr`), Lithuanian (`lt`), Estonian (`et`), Latvian (`lv`), Slovenian (`sl`), Maltese (`mt`), Irish (`ga`) — across the dashboard, docs mirrors, CLI, README and the site; OmniRoute now ships every official EU language (51 locales). (#13044) +- **feat(i18n):** 8 new locales — Kannada (`kn`), Malayalam (`ml`), Odia (`or`), Punjabi (`pa`), Nepali (`ne`), Sinhala (`si`), Burmese (`my`), Khmer (`km`) — across the dashboard, docs mirrors, CLI, README and the site (59 locales). The translator now restores the ICU literal escape around angle placeholders and splits oversized docs sections before translating. (#0000) ([#13660](https://github.com/diegosouzapw/OmniRoute/pull/13660)) +- feat(providers): **list Agnes 3.0 Flash as the current free chat model, drop retired 1.5 Flash, add Image 2.0/2.5 Flash plus Video 2.5/2.5 Flash, and discover the live `/v1/models` catalog (including the CN host `api.agnes-ai.cn`).** `agnes-1.5-flash` now forwards to `agnes-3.0-flash`. Video 2.5 polls `GET /v1/videos/{id}` (not the V2.0 `/agnesapi` contract). Live `/v1/models` (2026-09-09) no longer serves 1.5; the wiki marks it deprecated. 3.0 Flash is 512K context / 65,536 max output, same window as 2.5. CN-region keys use the existing per-connection base-URL field, default stays `apihub.agnes-ai.com`. ([#13120](https://github.com/diegosouzapw/OmniRoute/pull/13120)) — thanks @HouMinXi +- **feat(sse):** Codex and OpenAI catalogs list GPT-6 Astra with effort aliases (`-low` through `-ultra`); Codex CLI identity pins `@openai/codex@0.153.4` in lockstep with the image ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **feat(dashboard):** the orchestration History tab gained a "Compare runs" mode — toggling it turns each grid cell into a 2-item selection queue (a 3rd click drops the oldest pick), and picking two cells opens a side-by-side comparison panel instead of the usual detail drawer. The panel fetches both runs' detail the same way the drawer does (falling back to persisted history once a run leaves the live TTL window) and shows, per side: identity/source/state, start time, a signed `right - left` delta for duration/cost/event count, the event timeline aligned by index, and any memory hits. One side's fetch failing never blocks the other, and a delta is only ever computed when both sides have a finite value — otherwise it renders "—", never `NaN`. Comparing two runs from different sources or skills still works; a banner marks the deltas as informational rather than hiding them, since the two runs aren't a strict apples-to-apples pair. ([#12677](https://github.com/diegosouzapw/OmniRoute/pull/12677)) +- **feat(dashboard):** badge a conversation that never reached a clean stop ([#12717](https://github.com/diegosouzapw/OmniRoute/pull/12717)) — thanks @hartmark +- **feat(release):** reconcile-changelog tool + version-anchored fragment aggregation ([#12987](https://github.com/diegosouzapw/OmniRoute/pull/12987)) +- **feat(mcp):** carry context_length through MCP list_models_catalog projection (#12776) ([#13406](https://github.com/diegosouzapw/OmniRoute/pull/13406)) — thanks @KooshaPari +- **feat(cli):** make startup readiness budget configurable (#13369) ([#13433](https://github.com/diegosouzapw/OmniRoute/pull/13433)) — thanks @KooshaPari + ### 🐛 Bug Fixes +- **security(streaming):** sanitize generic mid-stream error messages before emitting OpenAI, + Responses, or Claude SSE failure frames and before diagnostic logging, while preserving raw + failures for internal classification and keeping client disconnects out of provider failure state. ([#12457](https://github.com/diegosouzapw/OmniRoute/pull/12457)) +- **fix(claude):** `blockExtraUsage: false` no longer lets 5h quota preflight skip the account; extra usage is billed after the session bar is gone, so the request must reach Anthropic ([#12803](https://github.com/diegosouzapw/OmniRoute/pull/12803)) — thanks @HouMinXi +- Sanitize HuggingChat conversation-creation and message-send transport failures before they reach client error bodies or provider logs. ([#12467](https://github.com/diegosouzapw/OmniRoute/pull/12467)) +- **fix(dashboard):** Keep local and theme-aware provider SVG icons at a definite layout size so Chromium does not collapse them to 0×0 after the v3.8.50 image-rendering change ([#12054](https://github.com/diegosouzapw/OmniRoute/pull/12054)) — thanks @ponkcore +- **fix(security):** Sanitize provider and runtime failures before public API, SSE and MCP responses and before persistent request, proxy and usage logs, preventing credentials, stack traces and host filesystem paths from crossing those boundaries while preserving stable error codes and useful diagnostics. ([#12506](https://github.com/diegosouzapw/OmniRoute/pull/12506)) +- **fix(cli):** `omniroute tunnel create` no longer crashes with `Cannot read properties of undefined (reading optsWithGlobals)` — removed the duplicate positional argument that caused Commander.js to misalign the action callback parameters ([#12295](https://github.com/diegosouzapw/OmniRoute/issues/12295)) ([#12368](https://github.com/diegosouzapw/OmniRoute/pull/12368)) — thanks @gonisulaimann +- Stop advertising Gemini Live-only models as supported audio endpoints until OmniRoute proxies the bidirectional Live protocol. ([#11710](https://github.com/diegosouzapw/OmniRoute/pull/11710)) — thanks @RaviTharuma +- **fix(kie):** reroute `flux/kontext` off the KIE Market `createTask` flow — it is catalogued with `isMarket: true` but has no Market catalog page, so KIE rejected it with "model name not supported"; it now hits the dedicated `POST /api/v1/flux/kontext/generate` / `GET /api/v1/flux/kontext/record-info` endpoints instead (#11296). ([#11985](https://github.com/diegosouzapw/OmniRoute/pull/11985)) — thanks @hartmark +- **fix(kie):** correct 12 more KIE Market catalog ids that were sent to `createTask` unchanged but diverge from KIE's documented upstream `model` values — GPT Image 2 T2I/I2I (drops the `gpt/` prefix), GPT Image 1.5 T2I/I2I (`gpt-image/` namespace), Seedream 5.0 Lite T2I/I2I (drops the `.0`), all 4 Flux 2 variants (`flux-2/` namespace, generic variant renamed `flex`), and Wan 2.7 Image / Image Pro (dash instead of dot) — each verified individually against the literal example request published on docs.kie.ai. `#11326`'s "everything else already matches" claim was wrong a second time (#11296); `z-image/4.0-*`/`z-image/4.5-*` and `flux/kontext` remain open, documented as unresolved in `KIE_MARKET_UPSTREAM_MODEL_IDS`'s comment pending further verification. ([#11667](https://github.com/diegosouzapw/OmniRoute/pull/11667)) +- **fix(dashboard):** `useApiKeySave.handleSaveApiKey` no longer forces a full upstream `/models` catalog sync on every non-curated provider connection save — callers can now pass `skipModelSync: true` to opt out, so a workflow that only wants to add one manual model no longer floods the provider's available-models list with hundreds/thousands of synced entries. The flag is a client-side intent signal only and is stripped before the connection payload is POSTed to `/api/providers`; default behavior (full sync on save) is unchanged when the flag is omitted (#11324) ([#11664](https://github.com/diegosouzapw/OmniRoute/pull/11664)) +- **Provider connections:** keep `tokenExpiresAt` when a connection is created. The create-path allowlist omitted it, so every insert stored NULL and the dashboard token badge could read a fresh connection as expired until its first background refresh ([#11368](https://github.com/diegosouzapw/OmniRoute/pull/11368)). — thanks @ntdatt812 +- **fix(combos):** the combo builder's precision-select, global-model-search, and manual-entry flows now serialize a model step's `model` string using the provider's already-computed routing-alias prefix (e.g. `oc/`) instead of rebuilding it from the raw canonical `providerId`, fixing the no-auth "OpenCode Free" provider (`opencode`) being routed to the unrelated paid "OpenCode Zen" provider (`opencode-zen`) because `opencode` doubles as a manual routing-prefix override ([#11433](https://github.com/diegosouzapw/OmniRoute/issues/11433)). ([#11670](https://github.com/diegosouzapw/OmniRoute/pull/11670)) +- **fix(radar):** the catalog feed cache now keeps `generatedAt`, the date the feed's data was built, next to `fetchedAt`, the date this install downloaded it (#11435). The feed schema requires that date and the sync path validates it, but the cache dropped it — so a feed fetched minutes ago and one carrying weeks-old figures looked identical to everything downstream, including the dashboard's "Last fetched" line. `getRadarCatalog().meta` and `GET /api/radar/status` now report both dates, the latter as its own field rather than folded into `version` — and omitted entirely for the offers and intel caches, which keep no build date, where a `null` would read as "unknown" rather than "never stored". The dashboard still shows only the fetch time; surfacing the build date there needs a new translated label and is left to a follow-up. Rows cached before migration 163 read back as `null`: unknown stays unknown instead of borrowing the fetch time. The referrals cache has persisted the same date since migration 142. — thanks @maxmad64bis +- **fix(cli):** postinstall no longer fills `JWT_SECRET` and `API_KEY_SECRET` in the installed package's `.env` (#11436). `.env.example` ships both blank on purpose: the server restores them from its durable store, or generates and persists them there, in `ensureSecrets()`. Pre-filling them defeated that — the file lives inside the package directory, so `npm i -g` replaced it and postinstall wrote _different_ values, while `ensureSecrets()` (which only acts on an empty variable) never got to restore the real ones. Both secrets rotated silently on every update, invalidating dashboard sessions and API-key CRCs. `STORAGE_ENCRYPTION_KEY` left the same list for the same reason in #1622; its comment pointed at a function that no longer exists and now names the real provisioning path. — thanks @maxmad64bis +- **fix(cli):** the CLI now says when a loaded `.env` lives inside the installed package directory (#11437). It already announces every env file it reads, without distinguishing the ones that survive an update from the one that does not: `npm i -g` replaces the package directory wholesale, so values set there are gone at the next update, silently. The warning names the durable path to move them to, and fires only when that file actually supplied a value — a file entirely shadowed by a durable one supplied nothing. A development checkout stays silent: there the same path is stable and documented in `SETUP_GUIDE.md`. — thanks @maxmad64bis +- **fix(free-models):** the shared `isFreeModel()` predicate no longer reports catalog entries marked `freeType: "discontinued"` as free, so `hidePaidModels` can't route to Pollinations' seven premium models that now require a paid key ([#11441](https://github.com/diegosouzapw/OmniRoute/pull/11441)) — thanks @maxmad64bis +- fix(ui): let AnySearch use the normal provider-icon fallback when LobeHub has no matching icon (#11449) +- Fixed the v3.8.50 Costs and Analytics dashboards so flat-rate Claude Code usage can be shown as an explicitly requested token-price estimate without changing default billed-cost semantics. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed archived usage retention so each request is priced individually instead of pricing a day's summed tokens once, which understated archived cost whenever a day mixed cache-heavy and ordinary requests. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed the Costs dashboard so it discloses when displayed figures include flat-rate token-price estimates instead of labelling them as billed spend, using the flag the analytics API already returns; the month-end projection and the CSV/JSON exports carry the same marker, and billed-cost mode is unchanged. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- **fix(combo):** attach the same combo-diagnostics trace (`poolSize`/`attemptOrder`/`excluded`/`terminalReason`, plus `x-omniroute-combo-*` headers) to the round-robin strategy's and the nested pipeline/fusion runtime-unit loop's "Maximum combo retry limit reached" 503 that the priority-strategy path already attaches for the identical terminal condition — previously those two paths returned a bare, contextless 503 ([#11462](https://github.com/diegosouzapw/OmniRoute/issues/11462)). ([#11665](https://github.com/diegosouzapw/OmniRoute/pull/11665)) +- **fix(bun):** use native `bun:sqlite` in `bootstrap-env` and `sync-env` to avoid loading `better-sqlite3` N-API addon during Bun startup ([#11468](https://github.com/diegosouzapw/OmniRoute/pull/11468)) — thanks @TheDemonTuan +- **fix(docker-bun):** make `Dockerfile.bun` install reliable with pre-install script helpers and native `bun:sqlite` smoke check ([#11470](https://github.com/diegosouzapw/OmniRoute/pull/11470)) — thanks @TheDemonTuan +- **fix(docker-bun):** remove every vendored `better-sqlite3` native addon from the Bun runtime image so startup cannot abort before the `bun:sqlite` fallback ([#11482](https://github.com/diegosouzapw/OmniRoute/pull/11482)) — thanks @TheDemonTuan +- **fix(autoCombo):** effort/alias model variants (`gpt-5.6-sol-xhigh`, `gpt-5.6`, cursor's `claude-4.6-opus-high`) inherit their base model's task fitness instead of falling to the wildcard 0.5 ([#11492](https://github.com/diegosouzapw/OmniRoute/pull/11492)) — thanks @MumuTW +- fix(db): rate-limit repeated Arena ELO leaderboard fetch-failure warnings instead of logging one per sync attempt (#11500) ([#11989](https://github.com/diegosouzapw/OmniRoute/pull/11989)) — thanks @hartmark +- **fix(db):** dedupe the raw `[Encryption] Decryption failed...` log line emitted by the lazy-decrypt views (`createLazyRowProxy`/`createLazyConnectionView`), which power `getProviderConnections()` and were re-triggering that line on every CredentialHealth/model-sync cycle for the same corrupt or stale-key credential — a fresh Proxy over a fresh row on every cycle meant the per-proxy memoization never suppressed it, unlike the dedup `decryptConnectionFields()` already had since [#9927](https://github.com/diegosouzapw/OmniRoute/issues/9927). Now shares that dedupe tracking so the line logs at most once per credential ([#11500](https://github.com/diegosouzapw/OmniRoute/issues/11500)). ([#11678](https://github.com/diegosouzapw/OmniRoute/pull/11678)) +- **fix(intelligence):** Arena ELO sync stores a synthesized base-model row for every effort/harness variant (`claude-opus-5-max` → `claude-opus-5`, `gpt-5.6-sol-xhigh (codex-harness)` → `gpt-5.6-sol`), so bare model requests reach the synced intelligence layer instead of the static fallback table; the hand-maintained `MODEL_ALIAS_MAP` — which collapsed generations (`gpt-5.5` → `gpt-5`) — is gone ([#11506](https://github.com/diegosouzapw/OmniRoute/pull/11506)) — thanks @MumuTW +- **fix(autoCombo,sse):** catalog hygiene — the static task-fitness table no longer ranks retired models above live ones (dead rows dropped, patterns matched on segment boundaries), the 7 deprecation aliases that forwarded to retired or non-existent models now point at the vendors' published replacements, an alias is no longer rewritten when the serving provider offers the id as-is, and a new `check:model-lifecycle` gate diffs both tables against a vendor lifecycle snapshot ([#11507](https://github.com/diegosouzapw/OmniRoute/pull/11507)) — thanks @MumuTW +- **fix(sse):** stop dropping resolved `thoughtSignature` values on parallel (multi tool-call) turns sent to Gemini 3.x — the claude→gemini and openai→gemini translators previously kept the signature only on the *first* function call of a message, causing Gemini to reject subsequent calls in the same turn with HTTP 400 "Function call is missing a thought_signature"; each function call now keeps its own resolved signature ([#11510](https://github.com/diegosouzapw/OmniRoute/issues/11510)). ([#11669](https://github.com/diegosouzapw/OmniRoute/pull/11669)) +- Treat Docker publishing from a non-default release branch as an expected skip instead of a failed workflow, while preserving the guard that prevents it from overwriting the `next` channel. ([#11527](https://github.com/diegosouzapw/OmniRoute/pull/11527)) — thanks @pacocartones +- fix(search): prefer credentialed providers over duckduckgo-free fallback (#11524) — the fallback-only loop ran before the credentialed-providers loop in `executeWebSearch`, making configured providers unreachable when `duckduckgo-free` was available. ([#11565](https://github.com/diegosouzapw/OmniRoute/pull/11565)) — thanks @vsd2807 +- **fix(sse):** cap the upstream headers-wait phase for STREAMING requests to a client-realistic ceiling (110s, under Codex's own ~120s hard client-abort window) instead of the flat 10-minute `FETCH_TIMEOUT_MS` default — that default was 5x longer than the body-phase readiness watchdog's own adaptive bound, so a request whose upstream never returned any response at all (not even headers, e.g. a stalled NVIDIA target behind a tool-heavy Responses→Chat translation) kept the client connection alive on keepalives only, guaranteeing the client's own patience ran out first with an opaque 499 instead of OmniRoute detecting and failing the stall fast. Non-streaming requests are unaffected — they keep the existing flat default (`open-sse/utils/fetchStartTimeoutPolicy.ts`) (#11526) ([#11668](https://github.com/diegosouzapw/OmniRoute/pull/11668)) +- **fix(ci):** protocol-clients E2E harness boots the peer-stamped custom server (`run-next.mjs dev`) instead of the bare `next` CLI, so `LOCAL_ONLY` locality resolves from the real TCP peer stamp and `/api/mcp/audit` answers 200 (open bootstrap preserved via an `OMNIROUTE_E2E_BOOTSTRAP_MODE=open` credential clear in `run-next.mjs`, pinned to loopback `HOST`) instead of a deterministic 403 (#11535) ([#11549](https://github.com/diegosouzapw/OmniRoute/pull/11549)) — thanks @jonlwheat2-gif +- **fix(docs):** the Auto-Combo engine is described with the number of factors it actually declares. Four different counts were in circulation — 6, 9, 13 and 14 across `AGENTS.md`, four guides and six code comments — while `DEFAULT_WEIGHTS` declares 15. `check:docs-counts` now reads that number from the source, so adding a factor without updating the prose fails CI ([#11545](https://github.com/diegosouzapw/OmniRoute/pull/11545)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **fix(quota-share):** in-flight leases are per request, so an aborted request's slot ages out instead of being kept alive by later traffic on the same connection ([#11547](https://github.com/diegosouzapw/OmniRoute/pull/11547)) — thanks @abhisheksharma2411 +- The free-tier summary route now serves the refreshed Radar catalog when the feed is active — the same numbers the dashboard shows — and states which catalog answered plus its real build date, instead of always reporting release-frozen figures with a stale curation date. The supporter-key live feed stays reserved to authenticated callers of the instance. ([#11550](https://github.com/diegosouzapw/OmniRoute/pull/11550)) — thanks @maxmad64bis +- **sse:** fix LiveWS/embed-WS servers crashing at startup under the Node/tsx runtime — `liveServer.ts`, `embedWsProxy.ts` and `apiBridgeServer.ts` imported `@/shared/utils/httpClientAbortGuard` without the `.mjs` extension, so the client-abort crash guard added by [#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556) was unreachable and every dependent test failed with `ERR_MODULE_NOT_FOUND` ([#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556)). ([#11671](https://github.com/diegosouzapw/OmniRoute/pull/11671)) — thanks @jonlwheat2-gif / @yxyxy +- fix(chat): shed any of the matched provider-node's routing identifiers (public prefix or internal id) before model lookup, so composites like `//` no longer reach upstream verbatim (#11557) — thanks @maxmad64bis +- **fix(cache):** a semantic-cache entry now expires on its TTL instead of surviving until the next UTC midnight, and `dbEntries` stops counting expired rows ([#11573](https://github.com/diegosouzapw/OmniRoute/pull/11573)) — thanks @ntdatt812 +- **fix(api):** `/v1/models` schedules its stale-while-revalidate rebuild through Next's `after()` again, so a stale catalog reaches the client before the rebuild blocks the event loop ([#11574](https://github.com/diegosouzapw/OmniRoute/pull/11574)) — thanks @ntdatt812 +- **fix(check):** the `known-symbols` executor conformance gate awaits the now-async `getExecutor()`, so it stops reporting all 142 registered aliases as dead symbols and can detect a lazy import that fails ([#11582](https://github.com/diegosouzapw/OmniRoute/pull/11582)) — thanks @ntdatt812 +- **fix(pack):** register `bin/cli/utils/volatileEnvPath.mjs` in `PACK_ARTIFACT_REQUIRED_PATHS`, so the prune can no longer silently drop a module the published CLI imports on every boot ([#11588](https://github.com/diegosouzapw/OmniRoute/pull/11588)) — thanks @ntdatt812 +- **i18n(vi):** translate the five reliability-column strings on the free-provider rankings page, which rendered as `__MISSING__:` markers in the Vietnamese UI ([#11589](https://github.com/diegosouzapw/OmniRoute/pull/11589)) — thanks @ntdatt812 +- **resilience:** restore the expired-connection retry-budget probe in the token-health sweep — the `!isGitHubAccessTokenOnlyConnection` carve-out reintroduced by #11608 contradicted the boundary pinned by #11592, so a GitHub connection parked at `expired` with retry budget remaining was never probed and could never self-heal ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)). ([#11672](https://github.com/diegosouzapw/OmniRoute/pull/11672)) — thanks @jonlwheat2-gif / @ntdatt812 +- **fix(dashboard):** Provider Stats now displays combo success rates on their native 0–100 percentage scale instead of multiplying them twice ([#11595](https://github.com/diegosouzapw/OmniRoute/pull/11595)) — thanks @pacocartones +- **fix(providers):** route DuckDuckGo Web Chat through the verified `duck.ai` endpoint, refresh its free model catalog, and avoid forwarding unsolved anti-abuse challenges ([#11597](https://github.com/diegosouzapw/OmniRoute/pull/11597)) — thanks @solstxce +- **fix(ui):** Console log Refresh and Copy controls now expose localized accessible names, keep copy actions visible on keyboard focus, and announce copy completion safely ([#11599](https://github.com/diegosouzapw/OmniRoute/pull/11599)) — thanks @pacocartones +- Dropped the stale-`.eslintcache` `restore-keys` fallback from both "Restore ESLint file cache" steps in `ci.yml`, so the blocking `Lint` job can no longer be served per-file verdicts computed under a different lint config, suppressions file or lockfile. `quality.yml` had already dropped it in #11963; `ci.yml` — the workflow that actually gates PRs — had not (#11600). ([#11996](https://github.com/diegosouzapw/OmniRoute/pull/11996)) +- fix(ci): pass `--pass-on-unpruned-suppressions` in `run-eslint-json.mjs` so the CI Lint job no longer fails when a suppression is merely orphaned by a genuine fix, mirroring the identical fix already in `validate-release-green.mjs` (#11600) ([#11983](https://github.com/diegosouzapw/OmniRoute/pull/11983)) — thanks @hartmark +- fix(handoff): enforce provider allowlist for universal handoff (#11602) — universal handoff now skips summarization when the selected summary model's provider is not included in the configured provider allowlist. ([#11619](https://github.com/diegosouzapw/OmniRoute/pull/11619)) — thanks @vsd2807 +- **fix(dashboard):** Show a stable error state when Search Analytics returns an HTTP error or malformed data ([#11603](https://github.com/diegosouzapw/OmniRoute/pull/11603)) — thanks @pacocartones +- **test(gamification):** pin the aggregate profile level to the XP-derived semantics of #11604 — `getAggregateXp()` now derives `currentLevel` from the summed XP (`calculateLevel(sum)`), not `MAX(stored current_level)`, and the #3484 fixture levels are aligned with the XP curve ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)). ([#11676](https://github.com/diegosouzapw/OmniRoute/pull/11676)) — thanks @jonlwheat2-gif / @pacocartones +- **fix(dashboard):** Keep the Profile level and progress aligned with aggregate XP, including bounded handling for invalid totals ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)) — thanks @pacocartones +- **fix(dashboard):** Prevent locked hidden badges from revealing their icon or opening private badge details before they are earned ([#11605](https://github.com/diegosouzapw/OmniRoute/pull/11605)) — thanks @pacocartones +- **fix(dashboard):** Restore keyboard focus after shared modals close and cancel delayed autofocus during cleanup ([#11607](https://github.com/diegosouzapw/OmniRoute/pull/11607)) — thanks @pacocartones +- **fix(dashboard):** Enable Enter and Space activation for clickable data-table rows without hijacking nested controls ([#11610](https://github.com/diegosouzapw/OmniRoute/pull/11610)) — thanks @pacocartones +- **fix(autoCombo,sse):** vendor-retired catalog ids are dropped from the auto-combo candidate pool and no longer win on leftover `arena_elo` / `user_override` rows; `getModelLifecycleDecision` consults `model-lifecycle.json` (prefix-stripped) so aggregator traffic is not `untracked` for ids the snapshot already knows ([#11625](https://github.com/diegosouzapw/OmniRoute/issues/11625)) ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)) — thanks @MumuTW +- **test(chatcore):** move Codex/Claude combo fixtures off models the lifecycle guard now rejects — `gpt-5.1-codex`/`gpt-5-codex` are vendor-retired (snapshot, #11626) and `claude-3-5-sonnet-20241022` is shut down, so native-passthrough and combo-fallback tests switched to `gpt-5.6-sol` and `claude-sonnet-4.6` ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)). ([#11675](https://github.com/diegosouzapw/OmniRoute/pull/11675)) — thanks @jonlwheat2-gif / @MumuTW +- fix(opencode-plugin): stop a free-tier budget that rounds up across a magnitude from rendering as `1000K`/`1000M` in the model picker — `fmtTokens` chose its unit from the raw token count and then rounded with `toFixed(1)`, so 999,950–999,999 printed as `1000K` rather than `1M` and just under a billion printed as `1000M` rather than `1B` ([#11684](https://github.com/diegosouzapw/OmniRoute/pull/11684)) — thanks @abhisheksharma2411 +- Cap prose-derived Antigravity quota resets at the configured model cooldown maximum while preserving authoritative Retry-After headers and Google RetryInfo hints. (#11695) Thanks @pacocartones. ([#11823](https://github.com/diegosouzapw/OmniRoute/pull/11823)) +- Compare `_rebaseline_*` notes structurally in the ratchet-bank verifier so an object-valued note no longer reports itself as rewritten on an unmodified tree — the nightly `bank-ratchet-shrinks` lane had been aborting on every run since 2026-08-11, leaving 41,672 lines of earned file-size shrinks unbanked (#11702). ([#11892](https://github.com/diegosouzapw/OmniRoute/pull/11892)) — thanks @MumuTW +- **fix(build):** `prepublish.ts` bundles the ChatGPT Web (Codex) MCP bridge through `runBuildTool()` instead of spawning `npx.cmd` raw, fixing the build crash on Node ≥ 20/Windows where `.cmd` shims cannot be spawned without a shell (EINVAL) ([#11704](https://github.com/diegosouzapw/OmniRoute/issues/11704)) ([#11706](https://github.com/diegosouzapw/OmniRoute/pull/11706)) — thanks @jonlwheat2-gif +- fix(codex): keep `parallel_tool_calls:false` on the translated Codex Responses Lite path (#11707) ([#11984](https://github.com/diegosouzapw/OmniRoute/pull/11984)) — thanks @hartmark +- **fix(tests):** the new Gemini Web Images retirement test (#11708) called the now-async `getExecutor()` synchronously, throwing `TypeError: getExecutor(...).getProvider is not a function` instead of asserting Gemini Web chat stays available. Awaited to match the R0.3 executor-registry refactor (#11220). ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(search):** Allow REST and MCP web-fetch requests to select the advertised `anysearch-search` provider explicitly ([#11716](https://github.com/diegosouzapw/OmniRoute/pull/11716)) — thanks @pacocartones +- **fix(providers):** Keep overnight peak-hour protection active after midnight according to the window's configured start day ([#11718](https://github.com/diegosouzapw/OmniRoute/pull/11718)) — thanks @pacocartones +- **fix(providers):** correct all five g4f.space gateway descriptions to document proof-of-work credits or member API keys, variable limits, and the remote prompt boundary instead of promising free keyless access at five requests per minute ([#11738](https://github.com/diegosouzapw/OmniRoute/pull/11738)). +- **fix(db):** `model_capabilities` is created by a migration instead of lazily on the first models.dev sync, so a clean install and an upgraded install converge on the same schema regardless of which features have run ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(ci):** `check:install-upgrade` now fails on an `npm` install truncated by ENOSPC (npm reports it as a warning and still exits 0), authenticates its health probe so the version assertion works against the hardened health payload, frees the clean-install tree before the upgrade phase, and no longer reports a schema divergence computed from a boot that never served ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(routing):** harden session-affinity key derivation against oversized explicit IDs and empty structural-payload collisions, while recognizing Gemini and common text request shapes ([#11746](https://github.com/diegosouzapw/OmniRoute/pull/11746)) — thanks @MumuTW +- **fix(packages/browser-pool):** regenerate `package-lock.json` so the `packages/browser-pool` workspace's locked `playwright`/`@types/node` (and transitives) match its `package.json` specs, fixing cache-only/offline installs (`npm ci --offline`, Nix `buildNpmPackage`) that previously failed with `ENOTCACHED` ([#11747](https://github.com/diegosouzapw/OmniRoute/issues/11747)) — thanks @benjaminkitt ([#11784](https://github.com/diegosouzapw/OmniRoute/pull/11784)) — thanks @geekyNads +- **fix(assets):** Stop distributing 79 provider and CLI logos without verified source/license provenance, keep every provider/tool available through the internal generic icon, and prevent those IDs from falling through to an external icon CDN. ([#11750](https://github.com/diegosouzapw/OmniRoute/pull/11750)) +- **fix(providers):** retire the common `chatgpt-web` / `cgpt-web` provider because its pre-key and proof-of-work provenance could not be cleared ([#11754](https://github.com/diegosouzapw/OmniRoute/pull/11754)) — explicit chat and image requests, slash-prefixed models, persisted aliases, proxy overrides, and combo targets now fail closed with a sanitized HTTP `410` / `PROVIDER_RETIRED` before upstream dispatch. Migration 163 tombstones matching connections and invalidates their active leases without deleting history or widening API-key allowlists. The independent `chatgpt-web-codex` / `cgpt-codex` provider and its MIT-noticed vendored implementation remain available. +- **fix(usage):** quota and usage refresh no longer 409 when an exclusive lease reserves the connection ([#11758](https://github.com/diegosouzapw/OmniRoute/pull/11758)) — thanks @TheDemonTuan +- Keep the embedding registry's vector width and `embedding` type on models when a synced model exists for the same id, so `/v1/models` no longer reports registry-described embedding models widthless or untyped (#11761) — thanks @rahilmavani +- Correct `google/gemini-embedding-001` on the OpenRouter route to 3072 dimensions, the width it returns when `dimensions` is not sent (#11761) — thanks @rahilmavani +- **fix(vision-bridge):** Restrict automatic vision routing and fallbacks to models available in authoritative live provider catalogs, including aliases and effort variants ([#11767](https://github.com/diegosouzapw/OmniRoute/issues/11767)) — thanks @pacocartones ([#11813](https://github.com/diegosouzapw/OmniRoute/pull/11813)) +- **fix(providers):** reclassify Cerebras as a one-time $5 signup credit (payment method required, 30-day validity), not a recurring no-card 1M tokens/day trial ([#11773](https://github.com/diegosouzapw/OmniRoute/issues/11773)) ([#12591](https://github.com/diegosouzapw/OmniRoute/pull/12591)) — thanks @HouMinXi +- fix(cli): stop prepublish from re-rebuilding the already-built ESM-only opencode-plugin dist (#11787) ([#11990](https://github.com/diegosouzapw/OmniRoute/pull/11990)) — thanks @hartmark +- **fix(db):** Fresh installs no longer log a non-fatal `no such table: compression_run_telemetry` warning when retention cleanup runs before the lazily-created telemetry table exists ([#11802](https://github.com/diegosouzapw/OmniRoute/pull/11802)) — thanks @RaviTharuma +- fix(sse): set X-OmniRoute-Selected-Connection-Id on successful combo dispatches so downstream consumers stop falling back to an empty connection id (#11810) ([#11986](https://github.com/diegosouzapw/OmniRoute/pull/11986)) — thanks @hartmark +- **fix(services):** embedded CLIProxyAPI lifecycle checks now use public `/healthz`, while model discovery uses the configured dedicated data-plane API key instead of the management password ([#11811](https://github.com/diegosouzapw/OmniRoute/pull/11811)) — thanks @RaviTharuma +- Fix persisted model steps returning an error in the playground route simulator, and warn when structural steps cannot be simulated. ([#11862](https://github.com/diegosouzapw/OmniRoute/pull/11862)) — thanks @pacocartones +- **fix(providers):** Antigravity's dynamic mitmAlias table no longer routes `gemini-3.7-flash-{high,medium,low}` to a literal tier-suffixed upstream id just because one connected account's own discovery listed it directly — those display ids always resolve through the safe `gemini-3.7-flash-tiered` static alias, so one account's Google-provisioned access no longer 404s every sibling account of the provider ([#11824](https://github.com/diegosouzapw/OmniRoute/issues/11824), [#11651](https://github.com/diegosouzapw/OmniRoute/issues/11651)) ([#11988](https://github.com/diegosouzapw/OmniRoute/pull/11988)) — thanks @hartmark +- **fix(dashboard):** Expose Profile loading, errors, page structure, and XP progress to assistive technologies ([#11838](https://github.com/diegosouzapw/OmniRoute/pull/11838)) — thanks @pacocartones +- **fix(socks):** `Agent.connectTimeout` now bounds both SOCKS handshake and TLS connect, and `family === null` no longer falls back to `fetch-socks` ([#11842](https://github.com/diegosouzapw/OmniRoute/pull/11842)) — thanks @maxmad64bis +- **perf(compression):** OOM mitigations for large payload hashing, memoization, and token estimation ([#11844](https://github.com/diegosouzapw/OmniRoute/pull/11844) — thanks @AndrianBalanescu) +- Excluded Next.js Node File Trace manifests (`*.nft.json`) from the published npm tarball. They are build-time metadata and are never read while serving, but had grown to 668.7 MB — 61% of the package — which pushed the upload past the registry limit and made `npm publish` fail with `413 Payload Too Large`. ([#11864](https://github.com/diegosouzapw/OmniRoute/pull/11864)) +- **fix(cli):** keep the global OmniRoute server `--base-url` separate from provider-node `--endpoint` values in add, update, and validate commands ([#11860](https://github.com/diegosouzapw/OmniRoute/pull/11860)) — thanks @pacocartones +- **fix(config):** Nous Research's `Hermes-4-405B` model now displays as "Hermes 4 405B (Nous Research)" in both the provider registry and the free-model catalog, instead of the mislabelled "Hermes 4 7B" ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#11993](https://github.com/diegosouzapw/OmniRoute/pull/11993)) +- **fix(provider/nous):** inject required user= tag into Nous Research inference requests to resolve upstream 400 "missing tags" error ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#12044](https://github.com/diegosouzapw/OmniRoute/pull/12044)) +- **fix(build):** `npm run build` now fails in one second with a named package and a copy-pasteable fix when npm silently drops an externalised optional native dependency, instead of dying four minutes in with `Module not found: Can't resolve 'better-sqlite3'` ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- **fix(install):** `postinstall` no longer throws `ReferenceError: isAndroid is not defined` — failing the whole `npm install` — when the `better-sqlite3` rebuild fallback times out; the manual-fix guidance is reachable again ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- Fixed the Alibaba free-tier allowlist test that went red on its own once the shipped catalog's `validUntil` (2026-08-27) passed, leaving every PR and `main` with a failing `Unit Tests (1/8)`. The test now builds its own packs with dates it controls, and covers the expired-pack fallback that production has actually been serving. ([#11867](https://github.com/diegosouzapw/OmniRoute/pull/11867)) +- Split the npm registry upload into its own GitHub-hosted job. npm refuses `--provenance` from a self-hosted runner (`422 ... Only "github-hosted" runners are supported`), which blocked the v3.8.50 publish; the heavy verification cannot move to a hosted runner, so it now hands the proven tarball over instead. ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(ui):** the shared `DataTable` loading state no longer reads its decorative ⏳ glyph out to assistive technology, and now carries the same `role="status"` / `aria-live="polite"` / `aria-busy="true"` semantics as `PageLoading` ([#11869](https://github.com/diegosouzapw/OmniRoute/pull/11869)) — thanks @pacocartones +- **fix(api):** Bound the `timeout` accepted by `POST /api/providers/[id]/login` to the same 15s–600s window the Adobe Firefly and Conol browser-login paths already enforce, so a request can no longer pin a headful browser and the single active-login slot with an arbitrarily large poll budget. ([#11871](https://github.com/diegosouzapw/OmniRoute/pull/11871)) — thanks @pacocartones +- **fix(api):** Stop the tunnel and MITM routes from returning raw child-process error text, which disclosed host paths, binary install locations and Tailscale `tskey-*` credentials in a body some of these routes serve to non-loopback callers, and return a real 400 instead of a framework 500 when a tunnel request body fails validation. ([#11872](https://github.com/diegosouzapw/OmniRoute/pull/11872)) — thanks @pacocartones +- **fix(open-sse):** stream TTFT/ITL are sampled from a monotonic clock (`performance.now`) instead of `Date.now`, so an NTP correction or wall-clock jump can no longer inflate a genuine TTFT or produce a negative one that the `ttft >= 0` guard silently drops — protecting the router's OTel/EWMA/`usage_history`/speed-ranking signals ([#11873](https://github.com/diegosouzapw/OmniRoute/pull/11873)) — thanks @pacocartones +- **fix(providers):** xAI `reasoning_effort: "xhigh"` now reaches grok-4.6+ instead of being silently clamped to `"high"` ([#11879](https://github.com/diegosouzapw/OmniRoute/pull/11879)) — thanks @NoxzRCW +- **fix(dashboard):** the providers page no longer crashes into the error boundary when a provider id collides with an `Object.prototype` member (`constructor`, `__proto__`); icon lookups are own-property guarded ([#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)) — thanks @NoxzRCW +- **fix(skills):** injected skill tools declared in shorthand (`{"content": "string"}`) now forward valid JSON Schema, unblocking providers that validate tool schemas strictly such as Zhipu GLM on the Console Go tier ([#11881](https://github.com/diegosouzapw/OmniRoute/pull/11881)) — thanks @NoxzRCW +- **fix(api):** `POST /api/playground/simulate-route` now surfaces `combo-ref` and `provider-wildcard` persisted combo steps with a specific warning (naming the referenced combo, or the unresolved `provider/model` wildcard) instead of folding them into a generic "unsupported step" count; a `provider-wildcard` step is also now included as an unresolved target so the operator can see it is in the route (ported from [#11882](https://github.com/diegosouzapw/OmniRoute/pull/11882) — thanks @NoxzRCW). ([#11930](https://github.com/diegosouzapw/OmniRoute/pull/11930)) +- **fix(translator):** the streaming OpenAI→Claude translator keeps upstream usage, including prompt-cache tokens, when it arrives on a trailing `choices: []` chunk (Fireworks and any upstream using `stream_options.include_usage`) ([#11883](https://github.com/diegosouzapw/OmniRoute/pull/11883)) — thanks @NoxzRCW +- **fix(cli):** `omniroute update --apply` now tells you explicitly whether a running server was detected and, if so, that you must run `omniroute restart` to apply the update — it never restarted anything and previously implied the update was already live once files were installed. The dashboard's npm-mode Update flow (`/api/system/version`) now tries OmniRoute's own PID-file-managed supervisor before falling back to pm2, and reports an honest "restart required" step instead of a silent pm2-only "skipped" that read like a completed update. The server-side latest-version lookup backing the dashboard's update banner also gained `--prefer-online`, closing the same stale-npm-cache class already fixed in the CLI's own copy for #4376 ([#11885](https://github.com/diegosouzapw/OmniRoute/issues/11885)). ([#12005](https://github.com/diegosouzapw/OmniRoute/pull/12005)) +- **fix(resilience):** clear persisted LKGP pins when a target suffers connection/provider exhaustion or is skipped before dispatch due to cooldown/exhaustion/unavailability, preventing subsequent requests from repeatedly prioritizing known-dead providers ([#11911](https://github.com/diegosouzapw/OmniRoute/issues/11911)). ([#12013](https://github.com/diegosouzapw/OmniRoute/pull/12013)) — thanks @HouMinXi +- **fix(sse):** the OpenAI→Claude stream translator now defers the terminal `message_delta`/`message_stop` emission until the real usage block has arrived (or a genuine end-of-stream flush forces it) instead of emitting it immediately on `finish_reason` — previously, when the trailing usage-only chunk (`{"choices":[],"usage":{...}}`) arrived *after* the `finish_reason` chunk (the normal order for Fireworks/vLLM/Together and other `stream_options.include_usage` upstreams), the client-visible `message_delta` still carried stale/zero usage even though `state.usage` was internally corrected too late to matter (ported from [#11915](https://github.com/diegosouzapw/OmniRoute/pull/11915) — thanks @HouMinXi). ([#11933](https://github.com/diegosouzapw/OmniRoute/pull/11933)) +- **fix(sse):** `stripStore()` now forces `store=false` for stateless OpenAI-compatible Responses-API targets (unless the connection has explicitly opted in via `providerSpecificData.openaiStoreEnabled`), instead of only handling the `openai`/`agentrouter` cases — a client-supplied `store` value previously passed through untouched to backends that don't actually persist responses server-side ([#11916](https://github.com/diegosouzapw/OmniRoute/pull/11916) — thanks @HouMinXi). +- **fix(models):** custom provider-node models (synced, custom, and alias-backed) now appear under their configured prefix in the unified catalog when the operator's model-id prefix mode is canonical, instead of being dropped whenever alias-inclusion was otherwise disabled ([#11918](https://github.com/diegosouzapw/OmniRoute/pull/11918) — thanks @HouMinXi). +- **fix(models):** the unified model catalog now suppresses stale static registry models (including effort-tier variants) for any provider whose active connection has an authoritative live synced catalog, not only providers already using exclusive-synced-listing — a connection with `providerUsesAuthoritativeLiveCatalog` previously kept serving both the live-synced models and the stale static rows side by side ([#11919](https://github.com/diegosouzapw/OmniRoute/pull/11919) — thanks @HouMinXi). +- **fix(ui):** `ProviderIcon`'s three remaining unguarded lookups (`PROVIDER_ICON_ALIASES`, `LOCAL_SVG_ALIASES`, `THEMED_SVGS`) now use `Object.hasOwn()` own-property checks — a provider id such as `constructor` or `__proto__` previously resolved through the prototype chain instead of falling through to the unknown-provider CDN fallback (`getLobeProviderIcon()` itself was already guarded by [#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)); `ProviderPageHeader`'s `color` field is also now optional, matching the rest of the component's defensive typing (ported from [#11920](https://github.com/diegosouzapw/OmniRoute/pull/11920) — thanks @HouMinXi). ([#11935](https://github.com/diegosouzapw/OmniRoute/pull/11935)) — thanks @NoxzRCW +- fix(ollama): preserve multi-byte UTF-8 content split across stream chunks in the Ollama NDJSON transform, which previously corrupted CJK/emoji into U+FFFD (#11921) — thanks @pacocartones +- **fix(providers):** OrcaRouter chat requests now target `/v1/chat/completions` instead of the bare `/v1` API root, fixing the upstream `404 Invalid URL (POST /v1)` ([#11923](https://github.com/diegosouzapw/OmniRoute/pull/11923)). — thanks @echel0nn +- **fix(plugins):** deliver the `onStreamComplete` event to disk-installed plugins. The event shipped in v3.8.50 (#9669) was emitted internally but had no plugin-facing wiring, so no plugin could ever subscribe: the manifest schema silently dropped `hooks.onStreamComplete`, and the loader/manager only knew the seven legacy hooks. `onStreamComplete` is now a declarable manifest hook, wired through the loader and registered by the manager like the other hooks, and its payload carries a `requestId` so consumers can correlate the stream-completion event with the originating request ([#11934](https://github.com/diegosouzapw/OmniRoute/pull/11934)) — thanks @amaleta / @pacocartones +- **fix(db):** the Qdrant embedding-model dropdown now lists local/self-hosted providers (Ollama, LM Studio, vLLM, etc.) — an active connection is treated as "configured" when the provider allows an optional API key, not only when it has a real key or OAuth, so a running local embedding provider is no longer hidden from the picker ([#11949](https://github.com/diegosouzapw/OmniRoute/issues/11949)) ([#11995](https://github.com/diegosouzapw/OmniRoute/pull/11995)) +- **fix(providers):** Vertex AI Anthropic partner-model discovery now calls the Model Garden `v1beta1` publisher list (`/v1beta1/publishers/anthropic/models`, global) and parses its `publisherModels` envelope, so Claude models auto-synced from Vertex populate the active live catalog and route at request time instead of returning `Model '' is not available in the active live catalog` ([#11991](https://github.com/diegosouzapw/OmniRoute/issues/11991)) — thanks @fabioluissilva ([#11998](https://github.com/diegosouzapw/OmniRoute/pull/11998)) +- **fix(cli):** support `--base-url` alongside `--endpoint` in `omniroute nodes add`, `update`, and `validate` subcommands to prevent global `--base-url` shadowing issues ([#11999](https://github.com/diegosouzapw/OmniRoute/issues/11999)). ([#12033](https://github.com/diegosouzapw/OmniRoute/pull/12033)) — thanks @HouMinXi +- **fix(providers):** `cloudflare-ai` no longer refuses image content parts for every Workers AI model ([#12002](https://github.com/diegosouzapw/OmniRoute/pull/12002)) — the plain-string `content` requirement behind #2539 is carried by the _model_ schema, not by the `/ai/v1/chat/completions` endpoint (measured: an all-text part array returns 200 on `@cf/mistralai/mistral-small-3.1-24b-instruct`, `@cf/meta/llama-4-scout-17b-16e-instruct` and `@cf/meta/llama-3.3-70b-instruct-fp8-fast`, and 400 on the text-only `@cf/qwen/qwen2.5-coder-32b-instruct`). `transformRequest()` flattened every array and threw on the first non-text part (#6390), so image input was refused for vision-capable Cloudflare models that accept it. All-text arrays are still flattened — the one shape every model accepts — while an array carrying a non-text part is passed through untouched, so the attachment is still never silently dropped. Regression guards: `tests/unit/cloudflare-ai-image-parts-6390.test.ts`. — thanks @davidlinfr +- **fix(resilience):** decouple the limiter-managed execution backstop from the queue-wait budget — new `requestQueue.executionMaxWaitMs` (env `RATE_LIMIT_EXECUTION_MAX_WAIT_MS`, default 600000 = 10 min) now feeds Bottleneck's post-dispatch `expiration`, while `requestQueue.maxWaitMs` keeps its documented queue-wait semantics. Previously the queue-wait budget doubled as the execution expiration, so legitimate long-running calls on non-incremental gateways (whole generation buffered before the first upstream byte, e.g. Console Go / Command Code tiers serving GLM models) were killed mid-flight at the queue budget with a false 504 `RATE_LIMIT_EXECUTION_TIMEOUT` — the local limiter undercut the provider-aware upstream fetch-start timeouts. The surfaced 504 message now names `requestQueue.executionMaxWaitMs`; the error keeps the #4165 guarantees (disclaims an upstream timeout, preserves the Bottleneck error as `cause`, branded code + trusted provenance, classified request-scoped so combo falls back). A real queue-wait bound (the `Promise.race` around `limiter.schedule()` sketched in #9533) remains future work. (#12025) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **Call logs:** keep the `error` field when an artifact exceeds the storage cap, instead of replacing it with the omission marker. The error is the only field that says *why* a request failed and is typically ~90 bytes next to the multi-hundred-KB bodies that trip the cap, so dropping it left a size-limited row undiagnosable — a provider outage, a local timeout and an upstream 400 all rendered identically. It is now preserved at every fallback stage, truncated to 4KB if it is itself large ([#12026](https://github.com/diegosouzapw/OmniRoute/issues/12026)). ([#12095](https://github.com/diegosouzapw/OmniRoute/pull/12095)) — thanks @ntdat812 +- **fix(diagnostics):** preserve the error field (truncated to 4KB with a `[truncated: …]` suffix) in every call-log artifact size-limit fallback stage. Previously the minimal fallback replaced the error with `[omitted: call log artifact size limit exceeded]`, so an oversized artifact row showed nothing about WHY the request failed — e.g. 91 of 847 opencode-go 504 rows on one production instance were undiagnosable from the dashboard. Oversized request/response bodies are still omitted exactly as before; the error cap is independent of the payload sizes that tripped the fallback. (#12026) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **fix(sse):** OpenAI Responses clients that declare the native `web_search` tool now receive a spec-shaped `web_search_call` output item with `action.sources` alongside the preserved function-call round-trip, so search results executed through OmniRoute's own search backend are consumable by standard Responses clients (Codex, pi-web-access, …). ([#12031](https://github.com/diegosouzapw/OmniRoute/pull/12031)) — thanks @watchingdogs +- **fix(cli):** use in-thread alias resolver hooks on modern runtimes to avoid deprecation noise and improve Node.js forward compatibility ([#12073](https://github.com/diegosouzapw/OmniRoute/issues/12073)). ([#12083](https://github.com/diegosouzapw/OmniRoute/pull/12083)) — thanks @xiaoyaner0201 +- **fix(combo):** an operator-set **Agent Features → Context length** on a combo is now honored at request time. The value was persisted and advertised through `/v1/models`, but `resolveComboContextLimit()` never consulted it — so a multi-target combo whose members carry no per-model window fell through to the provider's generic `defaultContextLength` (openrouter 128000, command-code 200000) and rejected large requests with `Input exceeds context window … limit 128000` despite the combo being explicitly sized much larger. An identical single-target combo worked, because it collapses to its concrete target before the guard runs. Invalid values (0/negative/NaN/Infinity) are ignored, so the existing target → combo-min → fallback order is unchanged. ([#12090](https://github.com/diegosouzapw/OmniRoute/pull/12090)) — thanks @adivekar-utexas +- **fix(sse):** passthrough streams now estimate usage on finish when upstream closes without usage even with `stream_options.include_usage` — avoids `0 tokens / 0%` for providers that stay silent (and correctly handles trailing empty-choices usage) ([#12151](https://github.com/diegosouzapw/OmniRoute/pull/12151)) — thanks @maxmad64bis +- **fix(combos):** clearing an agent feature in the combos editor now persists — unchecking context cache protection, or emptying the system message or tool filter, sends an explicit `null` instead of dropping the field from the `PUT` body, which the update merge read as "leave unchanged" ([#12177](https://github.com/diegosouzapw/OmniRoute/pull/12177)) — thanks @foreveryh / @ntdatt812 +- **fix(memory):** self-hosted embedding endpoints now vectorize — the vector width is measured from the first embedding that comes back instead of being read from a registry that cannot describe them, so `vec_memories` is created and memories stop piling up unvectorized behind a green health check ([#12180](https://github.com/diegosouzapw/OmniRoute/pull/12180)) — thanks @kanade-hoshino / @ntdatt812 +- **fix(translator):** the leading `system` message now reaches Responses-API upstreams when its `content` is a content-part array — it was read as `typeof content === "string" ? content : ""`, so a prompt-caching client (Anthropic `cache_control`, the shape LiteLLM and the Anthropic SDK emit) had its entire system prompt replaced by an empty `instructions`. The request was still accepted with a normal `prompt_tokens` count, so the model answered with no instructions and nothing in the response said they were missing. Mid-conversation system turns already handled the array shape ([#7056](https://github.com/diegosouzapw/OmniRoute/pull/7056)); only the first one did not ([#12206](https://github.com/diegosouzapw/OmniRoute/issues/12206)). Regression guard: `tests/unit/translator-openai-responses-system-content-parts.test.ts`. ([#12207](https://github.com/diegosouzapw/OmniRoute/pull/12207)) — thanks @Gorillaz322 +- **fix(free-tier):** `/api/free-tier/summary` no longer computes its totals from a Radar feed built before the catalog the running release ships. When the cached feed is older — or carries no build date at all — the route answers from the shipped catalog, resolved through the operator's local model state so disabled and tombstoned models stay out of the numbers ([#12215](https://github.com/diegosouzapw/OmniRoute/pull/12215)). — thanks @maxmad64bis +- **fix(oauth):** Keep a Claude personal workspace and a Team organization as separate connections — they share the same email and `accountUUID`, so the email-only OAuth dedup let the second login overwrite the first account's tokens; `organizationUUID` now disambiguates them, the way `workspaceId` does for Codex ([#12222](https://github.com/diegosouzapw/OmniRoute/pull/12222)) — thanks @davidebaraldo +- **fix(resilience):** a 402 on a single paid model of a passthrough/gateway provider (e.g. `kilo-gateway`, `ollama-cloud`) no longer terminalizes the whole connection with a never-auto-recovered `credits_exhausted` status — only the paid model is locked out, so free models on the same key keep serving. 402 variant of [#3027](https://github.com/diegosouzapw/OmniRoute/issues/3027). Single-credential providers are unaffected — a 402 there is still treated as the key being genuinely out of credit ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239) / [#10616](https://github.com/diegosouzapw/OmniRoute/issues/10616)) ([#12242](https://github.com/diegosouzapw/OmniRoute/issues/12242)) — thanks @brick30llc-ctrl ([#12266](https://github.com/diegosouzapw/OmniRoute/pull/12266)) — thanks @geekyNads +- **fix(sse):** trust `finish_reason: "length"`/`"max_tokens"` over the reasoning-consumed-token ratio in response quality validation, so a reasoning model truncated below the old 90% threshold correctly fails and retries instead of returning empty content as a silent "success" ([#12262](https://github.com/diegosouzapw/OmniRoute/pull/12262)) — thanks @brick30llc-ctrl +- **fix(settings):** `PUT /api/settings/cache-config` now persists `alwaysPreserveClientCache` to the flat general settings the runtime cache-control policy actually reads; previously the value landed in the databaseSettings "cache" section and was silently ignored, so the endpoint had no effect on `cache_control` passthrough ([#12304](https://github.com/diegosouzapw/OmniRoute/pull/12304)) — thanks @davidebaraldo +- **fix(grok-cli):** treat omitted SuperGrokPro `creditUsagePercent` as 0% used so Provider Limits still renders a weekly bar (proto3 zero-elision) ([#12312](https://github.com/diegosouzapw/OmniRoute/pull/12312)) — thanks @HouMinXi +- **fix(combo):** Expose the two Auto-Combo scoring factors nobody could set — the combo validation schema and the dashboard weight sliders both declared 13 of the scorer's 15 factors, so `connectionDensity` (spreads load across a provider's connections) and `quality` were dropped on save and offered nowhere. The sliders also shipped their own default table that differed from the engine's on every non-zero factor and summed to 1.05, so the percentages shown next to them added up to 105%. Both lists now match `DEFAULT_WEIGHTS`, and a test keeps them there. Note that a combo whose stored `weights` omitted the two keys was effectively running with them at zero and the other thirteen renormalized upward; it now runs with the engine's intended distribution, so its routing does shift ([#12314](https://github.com/diegosouzapw/OmniRoute/pull/12314)) — thanks @maxmad64bis +- **fix(docs):** The free-tier reference no longer says its numbers come "confidence tagged per row" — no catalog entry carries a confidence tag and the API serves none, so every figure on that page is an estimate of the same, unstated quality. The page now states what an entry does vouch for: an independently documented hard stop (set by hand with the source in a comment, never defaulted to `true`) and a prompt-training disclosure, both with live counts the `check:docs-counts` gate keeps honest ([#12318](https://github.com/diegosouzapw/OmniRoute/pull/12318)) — thanks @maxmad64bis +- **fix(usage):** `adobe-firefly` and `firefly` have had a working usage fetcher since Adobe Firefly landed, but neither was ever added to the registration list, so the provider-plugin manifest, `genericQuotaFetcher` and the free-access quota cache all reported them as having no usage support — while `USAGE_SUPPORTED_PROVIDERS` said the opposite. Both are now declared, which also means their credit balance is fetched like any other declared provider's: `registerGenericQuotaFetchers` now registers a generic quota fetcher for them, and `resolveFreeAccessState` no longer returns early. A test holds the registration list to the dispatcher's switch in both directions, which is what the module's own docstring already asked for in prose ([#12321](https://github.com/diegosouzapw/OmniRoute/pull/12321)) — thanks @maxmad64bis +- **fix(quota):** drop the generic quota cache (agy / Antigravity / Claude OAuth) on an upstream 429 so reset-aware scoring does not keep a 60s stale snapshot, and force-refresh the next usage fetch so inner provider caches cannot recache the same window ([#12325](https://github.com/diegosouzapw/OmniRoute/pull/12325)) — thanks @HouMinXi +- **fix(combos):** deleting a combo now clears its persisted LKGP pins instead of leaving unreachable `key_value` rows behind ([#12326](https://github.com/diegosouzapw/OmniRoute/issues/12326)) ([#12425](https://github.com/diegosouzapw/OmniRoute/pull/12425)) — thanks @KrzysiekSko +- **fix(sse):** Keep ZWNJ (U+200C) and ZWJ (U+200D) in assistant text, reasoning and tool-call arguments — Persian/Kurdish half-space (`ارائه‌دهنده`), Arabic/Indic shaping and emoji sequences no longer lose them; the response de-obfuscation now removes joiners only between ASCII word characters, where the request side inserts them ([#12186](https://github.com/diegosouzapw/OmniRoute/issues/12186)) — thanks @rezjalibd ([#12359](https://github.com/diegosouzapw/OmniRoute/pull/12359)) — thanks @pacocartones +- **fix(resilience):** count resolved upstream 5xx results against the provider circuit breaker on the chat path — `CircuitBreaker.execute()` no longer reads a resolved `{ success: false, status: 5xx }` as a success that cancels the call-site failure, so a provider answering 503s now trips its breaker instead of staying `CLOSED` at `failureCount: 1`; single-model and combo dispatches are each accounted exactly once ([#12254](https://github.com/diegosouzapw/OmniRoute/issues/12254)) ([#12360](https://github.com/diegosouzapw/OmniRoute/pull/12360)) — thanks @pacocartones +- **fix(providers):** resolve the Codex quota auto-ping model from the live provider catalog and lifecycle registry instead of the retired `gpt-5.1-codex-mini`, and pause the ping with one actionable warning when no selectable Codex model exists rather than retrying a shut-down id every cooldown window ([#11905](https://github.com/diegosouzapw/OmniRoute/issues/11905)) ([#12361](https://github.com/diegosouzapw/OmniRoute/pull/12361)) — thanks @pacocartones +- **fix(api):** keep the `{created, data}` wrapper on combo-routed `/v1/images/generations` responses and default Codex image results to `b64_json` on both `/v1/images/generations` and `/v1/images/edits` so Codex CLI's built-in `image_gen` can decode them ([#12268](https://github.com/diegosouzapw/OmniRoute/issues/12268)) ([#12362](https://github.com/diegosouzapw/OmniRoute/pull/12362)) — thanks @pacocartones +- **fix(sse):** Name the shadowed custom provider node when a built-in provider id/alias (e.g. `openference` → `of`) reserves the prefix of an existing OpenAI/Anthropic-compatible node, so the runtime `No active credentials for provider: ` error explains that the prefix routed to the built-in and never reached the node's healthy connections, instead of contradicting the dashboard ([#11943](https://github.com/diegosouzapw/OmniRoute/issues/11943)) — thanks @morpheus9393 ([#12365](https://github.com/diegosouzapw/OmniRoute/pull/12365)) — thanks @pacocartones +- **fix(i18n):** wrap `ccOnboardingKeyPlaceholder` in ICU single quotes across all 43 locales so angle brackets render literally instead of being parsed as rich-text tags, which crashed the Claude Code onboarding block with `INVALID_MESSAGE: INVALID_TAG` ([#12302](https://github.com/diegosouzapw/OmniRoute/issues/12302)) ([#12369](https://github.com/diegosouzapw/OmniRoute/pull/12369)) — thanks @gonisulaimann +- **fix(models):** publish `effort_tiers` on Kimi K3's synced base-model entries (`kmca/k3`, `kmca/k3-256k`) so catalog-only clients (OpenCode, plain SDK pickers) can see and select the reasoning tiers (`low`/`high`/`max`) the synced metadata already carried — the `isSkippedEffortProvider` gate no longer suppresses tier visibility on those base entries, while synthetic `-` variant generation stays prevented and Codex/GLM base models remain excluded unchanged ([#12299](https://github.com/diegosouzapw/OmniRoute/issues/12299)) ([#12371](https://github.com/diegosouzapw/OmniRoute/pull/12371)) — thanks @gonisulaimann +- **fix(guardrails):** keep `auto`/`auto/*` virtual combos exempt from the Vision Bridge `fixedModel` credential guard so a combo target is passed through instead of silently falling back to global auto-selection ([#12237](https://github.com/diegosouzapw/OmniRoute/issues/12237)) ([#12373](https://github.com/diegosouzapw/OmniRoute/pull/12373)) — thanks @pacocartones +- **fix(combo):** capability-filter exhaustion caused by `max_tokens` above every target's known output limit now reports that reason (requested `max_tokens` vs the pool's highest known ceiling) instead of the unrelated "supports structured output" message ([#12229](https://github.com/diegosouzapw/OmniRoute/issues/12229)) — thanks @DW-MediaLab ([#12374](https://github.com/diegosouzapw/OmniRoute/pull/12374)) — thanks @pacocartones +- **fix(auth):** the `least-used` account strategy now prefers accounts without backoff before falling back to oldest `lastUsedAt`, the same tie-break `round-robin` already applies, so a failover no longer lands on a just-rate-limited account for a single request ([#12279](https://github.com/diegosouzapw/OmniRoute/issues/12279)) — thanks @tenshiak ([#12375](https://github.com/diegosouzapw/OmniRoute/pull/12375)) — thanks @pacocartones +- **fix(docker):** the `chatgpt-web-codex-browser` image now finds the Chrome binary under `chrome-linux64/` (Chrome for Testing layout in `playwright:v1.62.0-noble`) as well as the legacy `chrome-linux/`, so the container no longer crash-loops with `exec: --headless=new: not found` ([#12024](https://github.com/diegosouzapw/OmniRoute/issues/12024)) ([#12376](https://github.com/diegosouzapw/OmniRoute/pull/12376)) — thanks @pacocartones +- **fix(providers):** declare `groq/compound` and `allam-2-7b` as non-reasoning models in the curated Groq registry so `reasoning_effort` / `output_config.effort` / `thinking` from Claude Code are stripped instead of forwarded, which Groq rejected with HTTP 400 ([#12134](https://github.com/diegosouzapw/OmniRoute/issues/12134)) ([#12379](https://github.com/diegosouzapw/OmniRoute/pull/12379)) — thanks @pacocartones +- **fix(executors):** `OpencodeExecutor` no longer forces a direct connection when the connection has a proxy assigned in Proxy Management but no per-account proxies: the single-account fast path used to wrap the upstream dispatch in the direct-egress sentinel, discarding the ambient proxy context the chat handler had pinned from `proxy_assignments`, so API-key `opencode`/`opencode-go` connections egressed from the host IP (and hit geoblocks) despite the assignment. The direct pin is now applied only when no ambient proxy context exists ([#11894](https://github.com/diegosouzapw/OmniRoute/issues/11894) — thanks @hizzt) ([#12380](https://github.com/diegosouzapw/OmniRoute/pull/12380)) — thanks @pacocartones +- **fix(api):** `GET /v1/models` with `MODELS_CATALOG_PREFIX_MODE=canonical` (or `?prefix=canonical`) now lists providers whose registry alias is undefined or equal to their own id (Antigravity, Antigravity CLI and other self-aliased built-ins) — their single `provider/model` id was dropped by the alias/canonical duplicate guard in the static, synced, custom and alias-backed catalog loops ([#12058](https://github.com/diegosouzapw/OmniRoute/issues/12058)) — thanks @cheynetom ([#12381](https://github.com/diegosouzapw/OmniRoute/pull/12381)) — thanks @pacocartones +- **fix(translator):** Drop replayed `thinking` blocks that carry no signature (the shape produced from cross-provider `reasoning_content`) instead of stamping the default Claude signature on them, which Anthropic rejected with `400 Invalid signature in thinking block` on the next turn served by an Anthropic rung ([#12105](https://github.com/diegosouzapw/OmniRoute/issues/12105)) — thanks @atescivitci-cmd ([#12386](https://github.com/diegosouzapw/OmniRoute/pull/12386)) — thanks @pacocartones +- **fix(cli):** Resolve Bun's `--preload` polyfill path against the package root instead of `dist/`, so `omniroute` installed with `bun install -g` no longer crashes at startup with `error: preload not found …/dist/open-sse/utils/setupPolyfill.ts` ([#11980](https://github.com/diegosouzapw/OmniRoute/issues/11980)) — thanks @joglomedia ([#12387](https://github.com/diegosouzapw/OmniRoute/pull/12387)) — thanks @pacocartones +- **fix(providers):** `gemini-business` now publishes its model catalog — `/v1/models` and `/v1/providers/gemini-business/models` list the 12 enterprise Gemini ids the executor understands instead of returning an empty list (#12107) ([#12389](https://github.com/diegosouzapw/OmniRoute/pull/12389)) — thanks @pacocartones +- **fix(db):** install `busy_timeout` before the SQLite connection's first statement so a process opening the database while another one closes its WAL connection waits out the transient EXCLUSIVE lock instead of dying with `database is locked`, and recognise the drivers' real BUSY/PROTOCOL/IOERR errors as transient in the corruption probe so the same lock no longer renames the database away as corrupt; deflakes `cross-process contenders never both acquire the same connection` (#12394 — thanks @pacocartones) +- **fix(chat-admission):** derive the `chat_admission_busy` 503 `Retry-After` from observed heavyweight-lease occupancy — the larger of the exhausted `OMNIROUTE_CHAT_ADMISSION_QUEUE_MS` window and the time since capacity last turned over, capped at 60 s — instead of a fixed 1 s (structural) / 2 s (byte-stage) hint that invited Codex/agent fan-out clients to re-send ~1 MiB `/v1/responses` bodies every second into a gate held for the whole SSE lifetime; an idle gate keeps the historical floors ([#12135](https://github.com/diegosouzapw/OmniRoute/issues/12135)) (#12395 — thanks @pacocartones) +- **fix(api-manager):** the API key permissions modal no longer silently drops `allowedCombos` entries its Combo picker cannot render — routing-rule names such as `rt-*`, which the backend already honours — when "All" is clicked and the key is switched back to "Restrict"; those entries now survive the toggle, are listed read-only under the combo list so the count and the list agree, and are saved back verbatim instead of persisting `[]` (deny-all) (#12397 — thanks @pacocartones) +- **fix(catalog):** write the NUL separator of the catalog connection memo key, the provider serviceKind memo key, the Video Bridge promotion group key and a JSON-exactness test fixture as the `\u0000` escape instead of a raw byte — same runtime value, but the raw byte made git, GitHub and ripgrep treat those files as binary (hidden PR diffs, silently skipped searches); a guard test now keeps raw NUL bytes out of `src/`, `open-sse/` and `tests/` (#12403 — thanks @pacocartones) +- **fix(providers):** add `CLAUDE_CODE_CLIENT_VERSION` and `GITHUB_COPILOT_CLI_VERSION` env overrides so Anthropic/Copilot client-version gates can be unblocked without a rebuild ([#12417](https://github.com/diegosouzapw/OmniRoute/issues/12417)) ([#12632](https://github.com/diegosouzapw/OmniRoute/pull/12632)) — thanks @HouMinXi +- **fix(db):** back-fill `last_ping_at` and `last_pinged_reset_key` on `provider_connections` during schema reconciliation so divergent lineages that skipped `123_quota_auto_ping` still accept quota auto-ping writes ([#12470](https://github.com/diegosouzapw/OmniRoute/pull/12470) — thanks @KooshaPari) +- **fix(ci):** document MIT exceptions for `@eloqnt/{config,format-json,format-po}` (next-intl transitive; locked tarballs omit `license`) and keep the A2A lifecycle vitest off the real SQLite persistence seam ([#12581](https://github.com/diegosouzapw/OmniRoute/issues/12581)) ([#12595](https://github.com/diegosouzapw/OmniRoute/pull/12595)) +- OpenCode plugin `/v1/models` catalog fetch now waits 30s by default and attaches HTTP `statusCode` on 401/5xx so host fallback plugins can hop instead of seeing an untyped AbortError/UnknownError. ([#12607](https://github.com/diegosouzapw/OmniRoute/pull/12607)) — thanks @RaviTharuma +- **CI:** the OpenAPI security-tier gate now mirrors `isAlwaysProtectedPath()` in full — it also reads `ALWAYS_PROTECTED_API_PATTERNS`, so the pattern-gated credential routes (`/api/providers/{id}/{claude,codex}-auth/{export,apply-local}`, GHSA-5926-2w35-7h4q) no longer report as unannotated. (#12605) +- **fix(api):** GET /v1/models no longer waits forever on a hung coalesced catalog rebuild; cold-path waits are bounded (`CATALOG_BUILD_TIMEOUT_MS`, default 8s) and a last-good 200 is served when the rebuild times out ([#12627](https://github.com/diegosouzapw/OmniRoute/issues/12627)). ([#12628](https://github.com/diegosouzapw/OmniRoute/pull/12628)) — thanks @RaviTharuma +- **fix(cursor):** a non-array `tool_calls` on an assistant message no longer crashes the cursor request translator with a `TypeError`; both loops now require an array ([#12689](https://github.com/diegosouzapw/OmniRoute/issues/12689)) ([#12691](https://github.com/diegosouzapw/OmniRoute/pull/12691)) — thanks @soroush5 +- **fix(chat):** Chat Completions no longer return empty `content` after a server-owned memory or skills tool; the first provider send and account/model recovery now share one pipeline so a follow-up round-trip can fill the reply ([#12696](https://github.com/diegosouzapw/OmniRoute/issues/12696)) — thanks @HouMinXi ([#12867](https://github.com/diegosouzapw/OmniRoute/pull/12867)) +- **fix(dashboard):** batch-deleting provider keys no longer toasts failure after a successful delete when the confirm button's click event is forwarded as `onAfter` ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) — thanks @HouMinXi +- **fix(glm):** drop the extra 16th argument to `createSSETransformStreamWithLogger` that TypeScript rejected (TS2554) and that never reached the TransformStream ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) ([#12770](https://github.com/diegosouzapw/OmniRoute/pull/12770)) — thanks @HouMinXi +- **fix(dashboard):** pass `nodeMap` into Runtime `QuotaGroup` so a quota monitor in error/exhausted/alerting no longer throws `ReferenceError: nodeMap is not defined`. ([#12868](https://github.com/diegosouzapw/OmniRoute/pull/12868)) — thanks @HouMinXi +- Restricted API keys whose `allowedModels` lists a combo name no longer skip every combo member at pre-dispatch (`ALL_TARGETS_SKIPPED`). Inner-target filtering still applies when the allow-list is a provider prefix or `disableNonPublicModels` is on ([#12899](https://github.com/diegosouzapw/OmniRoute/pull/12899)). — thanks @HouMinXi +- **fix(combos):** Effort-suffixed combo members inherit the base model's `model_context_overrides` row so priority order is not inverted on large requests ([#12926](https://github.com/diegosouzapw/OmniRoute/pull/12926)) — thanks @HouMinXi +- **fix(catalog):** Picker-added `customModels` enter the dispatch-time live catalog so combo members and bare inference no longer 400 ([#12934](https://github.com/diegosouzapw/OmniRoute/pull/12934)) — thanks @HouMinXi +- **fix(docker):** re-derive the Docker build's worker-pool memory budget from the MEASURED ~4.5 GB per-process RSS (the issue owner's own VPS dmesg OOM-killer reproduction) instead of the stale 2560 MB/worker inference #11419 shipped, and lower `OMNIROUTE_BUILD_WORKERS` 3 → 2 so 1 parent + 1 page-data worker (2 processes × 4.5 GB = 9 GB) fits the 12.288 GB (75%) budget on a 16 GB GitHub Actions runner — the previous default (1 parent + 2 workers = 13.5 GB) still overcommitted the runner and kept "Publish to Docker Hub" failing with `cannot allocate memory` after #11419 merged (#7518). ([#11663](https://github.com/diegosouzapw/OmniRoute/pull/11663)) +- **fix(electron):** the packaged Windows desktop build now passes the #7592 cold-restart smoke end to end. Five defects were found and fixed on the way: (1) optional-pack staging failed on any machine whose `tar` is GNU tar — it reads the drive letter in an absolute `-f C:\...` archive path as a remote rsh host (`Cannot connect to C:`), so staging now passes a bare filename with `cwd` at the tarball directory and surfaces tar stderr on failure; (2) the Electron `files` allowlist was missing `lib/loginHeaderCapture.js`, so the packaged main process crashed at startup with `Cannot find module './lib/loginHeaderCapture'`; (3) electron-builder ≥26 injects an `!**/node_modules/**` ignore into every extraResources pattern list that no positive filter can override, which silently dropped the staged runtime `node_modules` (including the better-sqlite3 N-API prebuild) from `resources/app` — a fresh v3.8.50 desktop build would have shipped with no native SQLite driver and reproduced the #7592 sql.js fallback on every machine; an `afterPack` hook now restores the staged `node_modules` after packing; (4) the packaged-app smoke harness redirected `USERPROFILE` into its temp DATA_DIR, but Electron resolves the Roaming profile from `%USERPROFILE%\AppData\Roaming\` (USERPROFILE takes precedence over the APPDATA env var) and the path service throws instead of creating it, so `requestSingleInstanceLock()` returned false and the app exited(0) silently before `app.whenReady()` — the harness now pre-creates the derived tree, and `ensureSmokeEnvDirs` is exported and unit-tested; (5) the #7592 driver assertion parsed a `[DB] Driver: ...` line that the server's primary DB path never printed (only the unused `openDatabaseAsync()` did) — `getDbInstance()` now emits the same line on open so the guard can actually assert the native driver. Also: the smoke env-allowlist unit test hardcoded POSIX paths and could only pass on Linux/macOS; expectations are now host-agnostic, with new regression tests for the USERPROFILE-derived tree and `tarPack` under absolute Windows-style paths ([#11443](https://github.com/diegosouzapw/OmniRoute/pull/11443)) — thanks @jonlwheat2-gif +- fix(sse): stop the auto-combo candidates inspector from silently dropping model-locked/cooled-down rows (#9133) ([#11994](https://github.com/diegosouzapw/OmniRoute/pull/11994)) — thanks @hartmark +- **fix(providers):** Perplexity Web no longer turns upstream stream failures into successful assistant text; pre-content failures remain eligible for fallback, partial output ends with a structured sanitized error, and failed sessions are not persisted ([#12465](https://github.com/diegosouzapw/OmniRoute/pull/12465)) +- **fix(video-bridge):** preserve chronological video and transcript fusion, source-frame timestamps, transcript provenance, full contact-sheet intervals, and pre-abort callback isolation ([#11681](https://github.com/diegosouzapw/OmniRoute/pull/11681)) +- **Z.ai Web:** HTTP 200 streams carrying an upstream error now terminate with a structured failure instead of assistant text plus a normal stop, preserving partial output while allowing pre-content combo fallback. ([#12454](https://github.com/diegosouzapw/OmniRoute/pull/12454)) +- Absorb `Error [AbortError]: request_signal_aborted` and DOMException AbortError shapes in the process-level client-abort crash guard so routine client disconnects no longer kill the server (exit code 7). ([#12165](https://github.com/diegosouzapw/OmniRoute/pull/12165)) — thanks @alvinveroy +- **fix(sse):** Treat Adapta Web `type:error` SSE events as sanitized non-stream failures instead of empty HTTP 200 completions. ([#12459](https://github.com/diegosouzapw/OmniRoute/pull/12459)) +- **fix(autoCombo):** layer 3 (`models_dev_tier`) no longer scores retired models as premium, merge cross-provider capability rows deterministically, and inherit tier scores through variant ids ([#11508](https://github.com/diegosouzapw/OmniRoute/issues/11508)). `getModelsDevTierFitness` now vetoes ids present in `config/quality/model-lifecycle.json` with status `retired` before any other signal; `loadModelCapabilities()` aggregates per `model_id` (any-non-null-true for booleans, max for `limit_context`) instead of last-write-wins over SQLite's undefined row order; a capabilities miss retries through `resolveScoresAs()`'s base id and reports `models_dev_tier:inherited`, matching layers 1–2 (#11492). Adds `getModelsDevTierFitnessWithSource()` and an `invalidateCapabilitiesCache()` test/ops hook. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) — thanks @Meet6338-X +- fix(sse): accept Claude `content:[]` + `stop_reason: max_tokens`/`tool_use` in `detectMalformedNonStream` (match `isEmptyContentResponse`) so Claude Code `/model` probes with `max_tokens: 1` no longer become a false 502 ([#11568](https://github.com/diegosouzapw/OmniRoute/pull/11568)) — thanks @Seramicx +- **fix(security):** sanitize `request.failed` diagnostics before publishing them to live dashboard listeners and replay history, while keeping status, model, provider, latency, and internal call-log diagnostics intact. ([#12469](https://github.com/diegosouzapw/OmniRoute/pull/12469)) +- **fix(pwa):** do not intercept dashboard navigations so Chrome can fall back from a stale HTTP/3 Alt-Svc advertisement (UDP :20128 is unpublished; F5 on a long-lived tab hung until a new tab opened a fresh TCP connection). ([#12767](https://github.com/diegosouzapw/OmniRoute/pull/12767)) — thanks @HouMinXi +- **fix(executors):** handle DuckDuckGo ERR_BN_LIMIT (418) without retrying — when the upstream returns `418 ERR_BN_LIMIT` (rate-limit/ban), the executor now returns the error immediately instead of burning another VQD acquisition that would only count against the IP limit. The retry logic for `418 ERR_CHALLENGE` (unsolved challenge) remains unchanged. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) ([#12110](https://github.com/diegosouzapw/OmniRoute/pull/12110)) — thanks @oyi77 / @Meet6338-X +- Fixed DuckDuckGo streaming responses losing JSON lines and UTF-8 characters split across network chunks. ([#11528](https://github.com/diegosouzapw/OmniRoute/pull/11528)) — thanks @pacocartones +- Harden generated Cloudflare, Vercel, and Deno relays by stripping hop-by-hop, framing, and proxy authentication headers before forwarding requests upstream. ([#11533](https://github.com/diegosouzapw/OmniRoute/pull/11533)) — thanks @pacocartones +- **feat(grok-cli):** Provider Limits shows grok-cli banked reset credits from `GetRemainingResets` (including a real zero; a failed RPC omits the row) and the existing View credits button now calls `ConsumerUiSvc/RedeemReset` for grok-cli. Live tokens use nested fields 10/20/30 (id + Timestamp), not compact 1/2/3. ([#12805](https://github.com/diegosouzapw/OmniRoute/pull/12805)) — thanks @HouMinXi +- **fix(memory):** Embedding Model Quick select, Embedding Source remote dropdown, and Rerank selector now list every configured provider with embedding/rerank support instead of only chat-catalog text matches plus OpenRouter live discovery; a generic OpenAI-compatible `/embeddings` + Cohere-compatible `/rerank` runtime fallback resolves any configured chat provider's embedding/rerank endpoint, so unlisted providers no longer fail with "Unknown embedding provider"; both memory selectors gained a free-text model override ([#11390](https://github.com/diegosouzapw/OmniRoute/pull/11390)) — thanks @rqzbeh +- Harden SQLite upgrades around the historical migration-074 version collision: missing discovery and inspector tables are replayed atomically, pre-existing databases (including setup-created skeletons) receive reusable content-addressed safety snapshots, and Node test/eval probes without `DATA_DIR` are isolated from the operator database. ([#12435](https://github.com/diegosouzapw/OmniRoute/pull/12435)) +- **fix(dashboard):** Moonshot/Kimi Open Platform voucher and cash leftover percentages follow the bucket balance, so an empty wallet no longer paints those rows as 100% while Available is 0% ([#12733](https://github.com/diegosouzapw/OmniRoute/pull/12733)) — thanks @HouMinXi +- **fix(combo):** return non-retryable HTTP 400 when all candidates for a pinned native Codex turn are unavailable due to model-scoped lockout, terminating the turn cleanly while preserving turn continuity and enabling standard Combo routing on subsequent turns ([#12240](https://github.com/diegosouzapw/OmniRoute/pull/12240)) — thanks @mdigitalbh81 +- Fixed auto-router strategies preserving the selected connection through ranking and dispatch when multiple connections share the same provider and model. ([#11530](https://github.com/diegosouzapw/OmniRoute/pull/11530)) — thanks @pacocartones +- Preserve Chat Completions JSON-object response formats when translating requests to the Responses API. ([#11529](https://github.com/diegosouzapw/OmniRoute/pull/11529)) — thanks @pacocartones +- Fix idempotency fingerprints so Responses API requests include `input` and other semantic generation fields using stable serialization, while excluding credentials and request metadata. ([#11532](https://github.com/diegosouzapw/OmniRoute/pull/11532)) — thanks @pacocartones +- **fix(api):** Generated API CLI commands now enforce required OpenAPI request bodies; Combo test commands forward the required `comboName` body, while API keys created by older writers after migration 149 preserve legacy allow-all Combo access without widening explicit empty allowlists — thanks @marcelokarval ([#12070](https://github.com/diegosouzapw/OmniRoute/pull/12070)) +- **fix(grok-web):** treat upstream streaming failures as failures instead of successful assistant text: error-only streams now fail readiness with HTTP 502, while failures after legitimate content preserve that partial output and terminate through the sanitized stream failure path without a normal `stop` completion. ([#12458](https://github.com/diegosouzapw/OmniRoute/pull/12458)) +- HuggingChat now turns HTTP 200 JSONL generation failures into a sanitized 502 before content, or a fixed public stream failure after partial output, so fallback and request persistence no longer record a false successful stop. ([#12456](https://github.com/diegosouzapw/OmniRoute/pull/12456)) +- **fix(providers):** keep 1min.ai HTTP 200 stream errors out of assistant content, preserve partial output, and expose sanitized terminal errors so pre-content failures can fall back. ([#12466](https://github.com/diegosouzapw/OmniRoute/pull/12466)) +- **fix(sessions):** preserve legacy Sessions data when the additive exclusive-session projection is unavailable, returning an empty projection and warning only once per contiguous outage instead of failing the endpoint; the Sessions badge intentionally reflects the merged legacy and exclusive row count introduced by [#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389) ([#11469](https://github.com/diegosouzapw/OmniRoute/pull/11469)) — thanks @KaspaPulse +- Render Nimble Search with the generic provider icon and serve Opper's proven logo locally. ([#12415](https://github.com/diegosouzapw/OmniRoute/pull/12415)) +- **fix(quality):** `config/quality/provider-assets-provenance.jsonl` was stale against the physical `public/providers/` tree (still listing 225 assets and 5 duplicate-content groups after several provider-retirement PRs removed 85 icon files), which would have made `check:provider-asset-provenance` fail on the next full run. Reconciled the manifest to the real 142-file snapshot (142 asset records, 1 duplicate-content group, 2 newly-registered files marked `unresolved` pending provenance research), repinned `auditedCommit`, and updated the regression test's hardcoded counts to match. ([#11876](https://github.com/diegosouzapw/OmniRoute/pull/11876)) +- fix(ui): wrap quota card footer action buttons to prevent clipping Refresh button on narrow cards — when extra action buttons such as `View credits` are present on Codex cards, allow the footer row to flex-wrap with `shrink-0` buttons so `Refresh now` remains visible and clickable across all responsive card widths ([#11464](https://github.com/diegosouzapw/OmniRoute/pull/11464)) — thanks @adevwithpurpose +- Keep Antigravity Gemini usable when the same connection's Claude weekly quota is empty; generic quota cache stays per-connection for every other provider. ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) — thanks @HouMinXi +- Fixed Tailscale tunnel setup to reject ports outside the valid 1-65535 range and safely install Windows MSI packages from paths containing apostrophes. ([#11534](https://github.com/diegosouzapw/OmniRoute/pull/11534)) — thanks @pacocartones +- Classify Cloudflared and Tailscale process-management routes as local-only and spawn-capable while preserving remote access to read-only tunnel status endpoints. ([#11531](https://github.com/diegosouzapw/OmniRoute/pull/11531)) — thanks @pacocartones +- Electron release workflow: the `publish-npm` job now grants `actions: read` to the reusable `npm-publish.yml` it calls (its `publish` job requests it), which is what made GitHub refuse the whole v3.8.50 run at startup and ship the release with zero desktop assets; a `workflow_dispatch` now builds the requested tag instead of the dispatching branch and can skip the npm leg (`publish_npm=false`) when only re-attaching assets ([#11974](https://github.com/diegosouzapw/OmniRoute/pull/11974)) +- **fix(dashboard):** The Combos page usage guide now reads its dismissal through `useSyncExternalStore` instead of correcting SSR state inside an effect, removing an extra commit of the page tree on every load (and the `react-hooks/set-state-in-effect` error it raised). ([#12671](https://github.com/diegosouzapw/OmniRoute/pull/12671)) +- Electron release: `electron/package-lock.json` regained the optional `electron-builder-squirrel-windows` subtree (13 entries) that `npm ci` had been refusing as out of sync, `electron-release.yml` gained a `build_ref` dispatch input and stops regenerating release notes on a re-attach dispatch, and the npm publish workflow attaches the SBOM to the GitHub Release on dispatch publishes too — so the v3.8.51 tag ships every desktop asset and the SBOM like v3.8.49 did ([#12022](https://github.com/diegosouzapw/OmniRoute/pull/12022)) +- Drained the reds every PR against `release/v3.8.51` was born with: documented the three service-worker build-id variables, registered the six retirement/tunnel tests with the mutation gate, approved `eslint-plugin-react-hooks` in the dependency allowlist, added the six `combo.sort.*` strings to `vi` and `pt-BR`, pointed the ChatGPT Web doc at the real migration-168 test, worded the g4f hint around the member key, and realigned four tests to the retired-provider catalog and the legacy-schema fixtures the retirement migrations touch. ([#11940](https://github.com/diegosouzapw/OmniRoute/pull/11940)) +- **fix(providers):** Claude, Grok, LMArena, Notion, and Perplexity web-cookie transports now use pooled `wreq-js` 3.2 instead of the native sidecar, with all nine supported bindings pinned and audited, and the applicable platform binding plus native-license evidence included in each release artifact ([#12429](https://github.com/diegosouzapw/OmniRoute/pull/12429), supersedes [#11753](https://github.com/diegosouzapw/OmniRoute/pull/11753)). +- **fix(providers):** Zed Hosted streaming failures now trigger fallback before content and end partial streams with a sanitized structured error instead of fake assistant text and a normal-success stop. ([#12455](https://github.com/diegosouzapw/OmniRoute/pull/12455)) +- **fix(test):** capture hook result after render (direct commit `b85d0abb0b`) +- **fix(sync):** repair the two regressions the v3.8.50 sync-back left on release/v3.8.51 (direct commit `5ade9e0851`) +- **revert(models):** drop the memo reset hook ported from #10055 (direct commit `282de841ae`) +- **fix(typecheck):** guard the optional connectionId before the reactive model sync (direct commit `f2825bad75`) +- **fix(quota-share):** release the winner's reserved in-flight slot (#11371) ([#11408](https://github.com/diegosouzapw/OmniRoute/pull/11408)) — thanks @oyi77 +- **fix(providers):** stop silently dropping reasoning effort on opencode families (#10788) ([#11409](https://github.com/diegosouzapw/OmniRoute/pull/11409)) — thanks @oyi77 +- **fix(combo):** enable genuine quota-aware routing for generic providers (antigravity, claude, etc.) ([#11411](https://github.com/diegosouzapw/OmniRoute/pull/11411)) — thanks @HouMinXi +- **fix(oauth):** allow expired token retry before deactivation and activate expired retry loop (#P0) ([#11414](https://github.com/diegosouzapw/OmniRoute/pull/11414)) — thanks @HouMinXi +- **perf(executors):** lazy-load the executor registry — defer class imports + construction to first use (#11220) ([#11421](https://github.com/diegosouzapw/OmniRoute/pull/11421)) — thanks @oyi77 +- **fix:** make release/v3.8.50 CI gates green (run 32786966560 root causes) ([#11450](https://github.com/diegosouzapw/OmniRoute/pull/11450)) — thanks @jonlwheat2-gif +- **fix(sse):** exact-domain cookie match and origin-equality URL assertions ([#11452](https://github.com/diegosouzapw/OmniRoute/pull/11452)) — thanks @hartmark +- **fix(providers):** send Responses API payload during OpenAI-compatible validation ([#11454](https://github.com/diegosouzapw/OmniRoute/pull/11454)) — thanks @tuandinh0801 +- **fix(command-code):** fallback to /alpha/generate for Go plan without Provider API access ([#11455](https://github.com/diegosouzapw/OmniRoute/pull/11455)) — thanks @Chewji9875 +- **fix(responses-continuation):** fail closed on a log-truncated stored input/output array ([#11473](https://github.com/diegosouzapw/OmniRoute/pull/11473)) — thanks @hartmark +- **fix(cli):** write jcode settings as [providers.omniroute] in config.toml ([#11484](https://github.com/diegosouzapw/OmniRoute/pull/11484)) — thanks @hongnoul +- **fix(logging):** raise chat-log array truncation cap to 1000 and unify duplicate implementations ([#11499](https://github.com/diegosouzapw/OmniRoute/pull/11499)) — thanks @hartmark +- **fix:** restore live websocket URL helpers ([#11509](https://github.com/diegosouzapw/OmniRoute/pull/11509)) — thanks @PauloFH +- **perf(core):** eliminate async waterfalls across home, cache, and db operations (#11396) ([#11512](https://github.com/diegosouzapw/OmniRoute/pull/11512)) — thanks @HouMinXi +- **fix(kiro):** add runtime.us-east-1.kiro.dev as first-attempt endpoint ([#11517](https://github.com/diegosouzapw/OmniRoute/pull/11517)) — thanks @rifqiawl +- **fix(providers):** remove duplicate 128-tool truncation in OpencodeExecutor (#11444) ([#11518](https://github.com/diegosouzapw/OmniRoute/pull/11518)) — thanks @HouMinXi +- **fix(providers):** poll onboardUser LRO before classifying BYOP ([#11519](https://github.com/diegosouzapw/OmniRoute/pull/11519)) — thanks @rifqiawl +- **fix(translator):** preserve exact tool name casing for Claude tool_use payloads (#11487) ([#11520](https://github.com/diegosouzapw/OmniRoute/pull/11520)) — thanks @HouMinXi +- **fix(kimi-web):** align health probe with executor domain ([#11521](https://github.com/diegosouzapw/OmniRoute/pull/11521)) — thanks @pacocartones +- **fix(cli):** preserve existing admin password during setup ([#11522](https://github.com/diegosouzapw/OmniRoute/pull/11522)) — thanks @pacocartones +- **fix(mitm):** keep Windows DNS elevation off the POSIX sudo path (#11430) ([#11542](https://github.com/diegosouzapw/OmniRoute/pull/11542)) — thanks @official-burak +- **fix(sse):** scale chat admission by ingest byte budget instead of a fixed request count ([#11548](https://github.com/diegosouzapw/OmniRoute/pull/11548)) — thanks @TheDemonTuan +- **fix:** accept Gemini Web cookie exports ([#11554](https://github.com/diegosouzapw/OmniRoute/pull/11554)) — thanks @rafacpti23 +- **fix(i18n):** complete Brazilian Portuguese translations ([#11558](https://github.com/diegosouzapw/OmniRoute/pull/11558)) — thanks @rafacpti23 +- **fix(sse):** explicit types for openai-responses pureHelpers — clears last failing core typecheck gate ([#11567](https://github.com/diegosouzapw/OmniRoute/pull/11567)) — thanks @jonlwheat2-gif +- **fix(opencode):** hydrate Proxy Pool references for no-auth connections ([#11584](https://github.com/diegosouzapw/OmniRoute/pull/11584)) — thanks @AStupidBear +- **fix(ci):** resolve inherited release/v3.8.51 base-red CI failures ([#11608](https://github.com/diegosouzapw/OmniRoute/pull/11608)) — thanks @jonlwheat2-gif +- **fix(db):** fallback load STORAGE_ENCRYPTION_KEY from env files ([#11614](https://github.com/diegosouzapw/OmniRoute/pull/11614)) — thanks @benzntech +- **fix(providers):** mark Brave Search as no longer having a free tier ([#11621](https://github.com/diegosouzapw/OmniRoute/pull/11621)) — thanks @turbolego +- **fix:** pass tool result content through as string in OpenAI/Claude -> Gemini translation ([#11624](https://github.com/diegosouzapw/OmniRoute/pull/11624)) — thanks @Hsia97 +- **fix(sse):** scope credential health to exact key ([#11628](https://github.com/diegosouzapw/OmniRoute/pull/11628)) — thanks @KaspaPulse +- **fix(install):** guard prepare script against missing husky ([#11634](https://github.com/diegosouzapw/OmniRoute/pull/11634)) — thanks @Prajeeth-12 +- **fix(security):** redact NVIDIA_API_KEY at the log sink in the ad-hoc diag script ([#11639](https://github.com/diegosouzapw/OmniRoute/pull/11639)) +- **fix(types):** clear the two type regressions red on release/v3.8.51 ([#11640](https://github.com/diegosouzapw/OmniRoute/pull/11640)) — thanks @jonlwheat2-gif +- **fix(build):** resolve workspace: protocol dependencies to concrete semver versions during prepublish (#11540) ([#11641](https://github.com/diegosouzapw/OmniRoute/pull/11641)) — thanks @HouMinXi +- **fix(search):** properly resolve configured search connection in /v1/responses pipeline (#11524) ([#11642](https://github.com/diegosouzapw/OmniRoute/pull/11642)) — thanks @HouMinXi +- **fix(authz):** trust cf-connecting-ip for via-proxy detection when peer is Cloudflare edge (#11514) ([#11643](https://github.com/diegosouzapw/OmniRoute/pull/11643)) — thanks @HouMinXi +- **fix(security):** compare the OIDC state cookie in constant time ([#11646](https://github.com/diegosouzapw/OmniRoute/pull/11646)) +- **fix(security):** namespace the dedup hash by the calling API key ([#11649](https://github.com/diegosouzapw/OmniRoute/pull/11649)) +- **fix(models):** gate the Codex-native catalog loop on prefix mode (#11632) ([#11662](https://github.com/diegosouzapw/OmniRoute/pull/11662)) — thanks @xiaoyaner0201 +- **fix(dashboard):** project Codex routing quota in account pools ([#11666](https://github.com/diegosouzapw/OmniRoute/pull/11666)) — thanks @KaspaPulse +- **fix(ddgw):** normaliza system no upstream da release v3.8.51 ([#11692](https://github.com/diegosouzapw/OmniRoute/pull/11692)) — thanks @RhianB14 +- **fix(memory):** honor category filter in GET /api/memory ([#11699](https://github.com/diegosouzapw/OmniRoute/pull/11699)) — thanks @Sabeekhann +- **fix(ci):** detect stale workspace lockfile entries ([#11714](https://github.com/diegosouzapw/OmniRoute/pull/11714)) — thanks @pacocartones +- **fix(ci):** stamp dist/BUILD_SHA before the npm publish provenance gate ([#11721](https://github.com/diegosouzapw/OmniRoute/pull/11721)) +- **fix(docker):** decouple the best-effort Bun image from the release manifest ([#11724](https://github.com/diegosouzapw/OmniRoute/pull/11724)) +- **perf(compression):** avoid cloning memo misses twice ([#11727](https://github.com/diegosouzapw/OmniRoute/pull/11727)) — thanks @RaviTharuma +- **fix(docker):** let the bun digest artifact be absent, not fatal ([#11740](https://github.com/diegosouzapw/OmniRoute/pull/11740)) +- **fix(shared):** block cloud-metadata hosts under default remote-image guard ([#11755](https://github.com/diegosouzapw/OmniRoute/pull/11755)) — thanks @santosraju99-hub +- **fix(api):** preserve caller-provided X-Correlation-Id on chat completions ([#11760](https://github.com/diegosouzapw/OmniRoute/pull/11760)) — thanks @aniruddhaadak80 +- **fix(resilience):** lock permanently retired models instead of short backoff (Gemini ban prevention) ([#11762](https://github.com/diegosouzapw/OmniRoute/pull/11762)) — thanks @turbolego +- **fix:** mark Vercel AI Gateway as passthroughModels ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **fix(resilience):** stop hammering permanently-moved endpoints and billing-suspended accounts ([#11774](https://github.com/diegosouzapw/OmniRoute/pull/11774)) — thanks @turbolego +- **fix(lease):** remove global static reservation and gate routing on live active lease occupancy ([#11775](https://github.com/diegosouzapw/OmniRoute/pull/11775)) — thanks @TheDemonTuan +- **fix(ci):** size the install-upgrade gate to a measured run, and log the pack cost ([#11776](https://github.com/diegosouzapw/OmniRoute/pull/11776)) +- **fix(pwa):** stop serving the stale shell after deploys ([#11779](https://github.com/diegosouzapw/OmniRoute/pull/11779)) — thanks @HouMinXi +- **fix(combo):** lock GitHub models rejected as "not supported" for future requests ([#11781](https://github.com/diegosouzapw/OmniRoute/pull/11781)) — thanks @turbolego +- **fix(cli):** probe both IPv4 and IPv6 loopback for server readiness (#11766) ([#11794](https://github.com/diegosouzapw/OmniRoute/pull/11794)) — thanks @vermasomesh835 +- **fix(sse):** preserve CLIProxyAPI wrappers per connection ([#11797](https://github.com/diegosouzapw/OmniRoute/pull/11797)) — thanks @RaviTharuma +- **fix(providers):** honor model auto-fetch opt-in on create ([#11798](https://github.com/diegosouzapw/OmniRoute/pull/11798)) — thanks @RaviTharuma +- **fix(dashboard):** make model auto-fetch order independent ([#11805](https://github.com/diegosouzapw/OmniRoute/pull/11805)) — thanks @RaviTharuma +- **fix(docker):** make GHA cache exports best effort ([#11806](https://github.com/diegosouzapw/OmniRoute/pull/11806)) — thanks @RaviTharuma +- **fix(kiro):** do not permanently ban on 'User is not authorized to make this call' ([#11809](https://github.com/diegosouzapw/OmniRoute/pull/11809)) — thanks @Deftera186 +- **fix(sse):** keep cache-write tokens in OpenAI-shaped usage ([#11814](https://github.com/diegosouzapw/OmniRoute/pull/11814)) — thanks @TheDemonTuan +- **fix(docs):** resolve relative markdown and wiki links across Fumadocs and GitHub wiki ([#11834](https://github.com/diegosouzapw/OmniRoute/pull/11834)) — thanks @AndrianBalanescu +- **fix(chat):** preserve unstripped model string for passthrough provider routing ([#11840](https://github.com/diegosouzapw/OmniRoute/pull/11840)) — thanks @raheemuddin786 +- **fix(mcp):** avoid async initialization inside sync esm wrappers (#11569) ([#11849](https://github.com/diegosouzapw/OmniRoute/pull/11849)) — thanks @HouMinXi +- **fix(sse):** preserve coherent markdown token boundaries across stream translation chunks (#11606) ([#11851](https://github.com/diegosouzapw/OmniRoute/pull/11851)) — thanks @HouMinXi +- **fix(ci):** run the install-upgrade gate on disk, not on the /tmp tmpfs ([#11855](https://github.com/diegosouzapw/OmniRoute/pull/11855)) +- **fix(skills):** expand shorthand property types in injected tool schemas ([#11857](https://github.com/diegosouzapw/OmniRoute/pull/11857)) — thanks @alvinveroy +- **fix(ci):** publish npm from a hosted runner so provenance is accepted ([#11877](https://github.com/diegosouzapw/OmniRoute/pull/11877)) +- **fix(lint):** pin react hooks plugin to 7.0.1 ([#11886](https://github.com/diegosouzapw/OmniRoute/pull/11886)) +- **fix(ci):** surface and clear release-green ESLint failure ([#11890](https://github.com/diegosouzapw/OmniRoute/pull/11890)) — thanks @MumuTW +- **fix(guardrails):** prevent duplicate prompt-injection-guard log output ([#11936](https://github.com/diegosouzapw/OmniRoute/pull/11936)) — thanks @patrykkopycinski +- **fix(auth):** downgrade expected transient states from warn to debug ([#11937](https://github.com/diegosouzapw/OmniRoute/pull/11937)) — thanks @patrykkopycinski +- **fix(codex):** fail over image generation for imported free plans ([#11948](https://github.com/diegosouzapw/OmniRoute/pull/11948)) — thanks @Bl0ck154 +- **fix(codex):** normalize non-stream responses ([#11951](https://github.com/diegosouzapw/OmniRoute/pull/11951)) — thanks @Bl0ck154 +- **fix(images):** forward Antigravity image size ([#11952](https://github.com/diegosouzapw/OmniRoute/pull/11952)) — thanks @Bl0ck154 +- **fix(db):** invalidate connection cache after upsert ([#11953](https://github.com/diegosouzapw/OmniRoute/pull/11953)) — thanks @Bl0ck154 +- **fix(codex):** restore imported account state ([#11954](https://github.com/diegosouzapw/OmniRoute/pull/11954)) — thanks @Bl0ck154 +- **fix(sse):** give extended-thinking targets the reasoning readiness budget ([#11959](https://github.com/diegosouzapw/OmniRoute/pull/11959)) — thanks @abhisheksharma2411 +- **fix(dashboard):** use opaque background and readable text color on cost chart tooltips ([#11960](https://github.com/diegosouzapw/OmniRoute/pull/11960)) — thanks @ZaimMarzuki +- **fix(antigravity):** send complete loadCodeAssist metadata (ideType/platform/pluginType as numeric enums) ([#11969](https://github.com/diegosouzapw/OmniRoute/pull/11969)) — thanks @rifqiawl +- **fix(core):** resolve DB init race condition and reasoning translation ([#12003](https://github.com/diegosouzapw/OmniRoute/pull/12003)) — thanks @raheemuddin786 +- **fix(admission):** exclude reclaimable page cache from the cgroup pressure ratio ([#12017](https://github.com/diegosouzapw/OmniRoute/pull/12017)) — thanks @HouMinXi +- **fix(build):** prune non-production trees in NFT trace excludes and tsconfig ([#12028](https://github.com/diegosouzapw/OmniRoute/pull/12028)) — thanks @Chewji9875 +- **fix(migrations):** add renamed migration compatibility for 056/073/077/101 ([#12036](https://github.com/diegosouzapw/OmniRoute/pull/12036)) — thanks @oyi77 +- **fix(sse):** default crash-guard logger to console.warn, not console ([#12042](https://github.com/diegosouzapw/OmniRoute/pull/12042)) — thanks @HouMinXi +- **fix(catalog):** derive vision/modalities for built-in auto combos from effective target pool ([#12046](https://github.com/diegosouzapw/OmniRoute/pull/12046)) — thanks @Prajeeth-12 +- **fix(release):** the packaged-app smoke verifies the database opened, not a driver line the primary path never prints (twin of #12032) ([#12047](https://github.com/diegosouzapw/OmniRoute/pull/12047)) +- **fix(barrel):** migrate src/app/ off the @/lib/localDb barrel import (#11795 Phase 2) ([#12051](https://github.com/diegosouzapw/OmniRoute/pull/12051)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate src/lib/ off the localDb barrel to direct db imports (#59) ([#12052](https://github.com/diegosouzapw/OmniRoute/pull/12052)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate open-sse, src/shared, src/sse, src/models, src/domain off the @/lib/localDb barrel import (#11795 Phase 4) ([#12053](https://github.com/diegosouzapw/OmniRoute/pull/12053)) — thanks @jonlwheat2-gif +- **fix(barrel):** delete the @/lib/localDb barrel — every consumer migrated (#11795 Phase 5) ([#12055](https://github.com/diegosouzapw/OmniRoute/pull/12055)) — thanks @jonlwheat2-gif +- **fix(radar):** restore D12 public boundary ([#12057](https://github.com/diegosouzapw/OmniRoute/pull/12057)) +- **fix(dev):** bound webpack and Tailwind scans ([#12075](https://github.com/diegosouzapw/OmniRoute/pull/12075)) — thanks @backryun +- **fix(dev):** isolate root layout settings reads ([#12076](https://github.com/diegosouzapw/OmniRoute/pull/12076)) — thanks @backryun +- **fix(dev):** reduce instrumentation executor fan-out (phase 3) ([#12078](https://github.com/diegosouzapw/OmniRoute/pull/12078)) — thanks @backryun +- **fix(dev):** make logging resources HMR-singleton ([#12079](https://github.com/diegosouzapw/OmniRoute/pull/12079)) — thanks @backryun +- **fix(sse):** keep unavailable forced connections scoped ([#12080](https://github.com/diegosouzapw/OmniRoute/pull/12080)) — thanks @keeltrace +- **fix(dev):** isolate batch dispatch from instrumentation ([#12081](https://github.com/diegosouzapw/OmniRoute/pull/12081)) — thanks @backryun +- **fix(ci):** reconcile release test contract drift ([#12082](https://github.com/diegosouzapw/OmniRoute/pull/12082)) — thanks @backryun +- **fix(ci):** accept CVE-2025-68121 in the prebuilt tls-client .so, auto-close base-red issues, guard Scorecard on the default branch ([#12085](https://github.com/diegosouzapw/OmniRoute/pull/12085)) +- **fix(db):** drop three consumer-less 1proxy exports — dead-code base-red on release/v3.8.51 after the barrel deletion (#12055) ([#12087](https://github.com/diegosouzapw/OmniRoute/pull/12087)) +- **fix(api):** clear the six API-route TypeScript regressions the new gate landed red on ([#12094](https://github.com/diegosouzapw/OmniRoute/pull/12094)) +- **fix(release):** never let the tag-push Create Release append auto notes to the curated body ([#12096](https://github.com/diegosouzapw/OmniRoute/pull/12096)) +- **fix(sse):** honor CLIProxyAPI environment API key ([#12099](https://github.com/diegosouzapw/OmniRoute/pull/12099)) — thanks @RaviTharuma +- **fix(oauth):** bind Google refresh to the client that issued the token ([#12106](https://github.com/diegosouzapw/OmniRoute/pull/12106)) — thanks @HouMinXi +- **fix(dashboard):** make RequestLoggerDetail loadable outside Next — CSS via globals.css + CJS/ESM interop (#11703 base-reds) ([#12114](https://github.com/diegosouzapw/OmniRoute/pull/12114)) +- **fix(leases):** project status lease row to lease columns so joined connection PII never escapes ([#12115](https://github.com/diegosouzapw/OmniRoute/pull/12115)) — thanks @geek007git +- **fix(plugins):** do not kill the plugin process when a fire-and-forget hook times out ([#12116](https://github.com/diegosouzapw/OmniRoute/pull/12116)) — thanks @geek007git +- **fix(guardrails):** restore injection-guard logging on middleware-only routes ([#12117](https://github.com/diegosouzapw/OmniRoute/pull/12117)) — thanks @geek007git +- **fix(plugins):** refresh stored manifest from disk on activate so new hook fields reach existing installs ([#12120](https://github.com/diegosouzapw/OmniRoute/pull/12120)) — thanks @geek007git +- **fix(codex):** preserve existing provider state when bulk-import upserts a matching connection ([#12122](https://github.com/diegosouzapw/OmniRoute/pull/12122)) — thanks @geek007git +- **fix(ci):** clear the two base-reds the 2026-08-30 merge batch left on release/v3.8.51 (round 3) ([#12123](https://github.com/diegosouzapw/OmniRoute/pull/12123)) +- **fix(sse):** strip type:'custom' from Claude tools on agentrouter dispatch ([#12126](https://github.com/diegosouzapw/OmniRoute/pull/12126)) — thanks @wahidsadik371-coder +- **fix(sse):** stop advertising video providers the dispatcher cannot run ([#12131](https://github.com/diegosouzapw/OmniRoute/pull/12131)) — thanks @quiterunner-commits +- **fix(search):** treat HTTP 432 and plan limit errors as transient cooldown ([#12139](https://github.com/diegosouzapw/OmniRoute/pull/12139)) — thanks @HouMinXi +- **fix(sse):** refuse an AI Horde queue that cannot fit the request budget ([#12143](https://github.com/diegosouzapw/OmniRoute/pull/12143)) — thanks @quiterunner-commits +- **fix(ci):** clear the base-reds the afternoon merge batch left on release/v3.8.51 (round 5: provider count 352, TS2554/TS2677) ([#12144](https://github.com/diegosouzapw/OmniRoute/pull/12144)) +- **fix(translator):** strip neutral tool_choice when tools absent in Responses-to-Chat (#12141) ([#12166](https://github.com/diegosouzapw/OmniRoute/pull/12166)) — thanks @HouMinXi +- **fix(guardrails):** pass providerId to getResolvedModelCapabilities in checkComboVision (#12112) ([#12169](https://github.com/diegosouzapw/OmniRoute/pull/12169)) — thanks @HouMinXi +- **fix(translator):** strip plaintext reasoning content for opaque responses backends (#12128) ([#12171](https://github.com/diegosouzapw/OmniRoute/pull/12171)) — thanks @HouMinXi +- **perf(stream):** compile hot-path regexes once, bound token caches, fix quadratic buffering ([#12179](https://github.com/diegosouzapw/OmniRoute/pull/12179)) — thanks @opensource-elearning +- **fix:** resolve compression worker file using runtime anchors instead of… ([#12183](https://github.com/diegosouzapw/OmniRoute/pull/12183)) — thanks @NightStalker-87 +- **fix:** prevent Claude Code session kills via liveness-aware readiness + auto model echo ([#12189](https://github.com/diegosouzapw/OmniRoute/pull/12189)) — thanks @opensource-elearning +- **fix(translator):** preserve falsy primitive values in Gemini and Antigravity function response results ([#12191](https://github.com/diegosouzapw/OmniRoute/pull/12191)) — thanks @Siva010 +- **fix(oauth):** mark empty Antigravity projectId as degraded and clear stale errors (#11284) ([#12205](https://github.com/diegosouzapw/OmniRoute/pull/12205)) — thanks @HouMinXi +- **fix(providers):** gate the Codex auto-ping usage read on the shared quota throttle ([#12209](https://github.com/diegosouzapw/OmniRoute/pull/12209)) — thanks @abhisheksharma2411 +- **fix(combos):** prioritize SQLite row id over inner JSON id and notify delete errors ([#12213](https://github.com/diegosouzapw/OmniRoute/pull/12213)) — thanks @HouMinXi +- **fix(providers):** mark featherless.ai as no longer having a free tier (`hasFree: false`) ([#12216](https://github.com/diegosouzapw/OmniRoute/pull/12216)) — thanks @turbolego +- **fix(deepseek-web):** stop Turbopack dev panic in the PoW worker path resolver ([#12221](https://github.com/diegosouzapw/OmniRoute/pull/12221)) — thanks @hartmark +- **fix(chat-admission):** clarify local 503 source ([#12223](https://github.com/diegosouzapw/OmniRoute/pull/12223)) — thanks @keeltrace +- **fix(dev):** silence webpack runtime module warnings ([#12228](https://github.com/diegosouzapw/OmniRoute/pull/12228)) — thanks @backryun +- **fix(db):** use module.require for CommonJS runtime driver loading ([#12230](https://github.com/diegosouzapw/OmniRoute/pull/12230)) — thanks @raheemuddin786 +- **fix(memory):** honest probe-driven FTS5 keyword status + memory_id rowid sync ([#12231](https://github.com/diegosouzapw/OmniRoute/pull/12231)) — thanks @raheemuddin786 +- **fix(system):** propagate abort signal to stream reader in HTTP version checks ([#12232](https://github.com/diegosouzapw/OmniRoute/pull/12232)) — thanks @raheemuddin786 +- **fix(resilience):** isolate local host execution errors from provider circuit breakers ([#12233](https://github.com/diegosouzapw/OmniRoute/pull/12233)) — thanks @raheemuddin786 +- **fix(sse):** sort injected tools deterministically for prompt caching ([#12234](https://github.com/diegosouzapw/OmniRoute/pull/12234)) — thanks @raheemuddin786 +- **fix(combo):** always clear the loop-safety timer, not just on the happy path (#11804) ([#12245](https://github.com/diegosouzapw/OmniRoute/pull/12245)) +- **fix(quality):** base-red pair — stryker tap registration + turn-pin suites aligned to the window gate ([#12255](https://github.com/diegosouzapw/OmniRoute/pull/12255)) — thanks @backryun +- **fix(dev):** qualify Turbopack runtime boundaries ([#12258](https://github.com/diegosouzapw/OmniRoute/pull/12258)) — thanks @backryun +- **fix(combo):** fall back to full pool when collapsed sole survivor is context-too-small ([#12278](https://github.com/diegosouzapw/OmniRoute/pull/12278)) — thanks @opensource-elearning +- **fix(combo):** bound the pre-dispatch unavailable skip so a stale label cannot dark a pool (#12168) ([#12285](https://github.com/diegosouzapw/OmniRoute/pull/12285)) +- **fix(security):** strip Qwen/Alibaba console-session cookies from provider API responses ([#12287](https://github.com/diegosouzapw/OmniRoute/pull/12287)) +- **fix(usage):** console-aware Token Plan guidance + subscription hint on bailian 401 ([#12288](https://github.com/diegosouzapw/OmniRoute/pull/12288)) +- **fix(resourcePressure):** log numeric detail on every rejection, recover faster ([#12293](https://github.com/diegosouzapw/OmniRoute/pull/12293)) — thanks @hartmark +- **fix(release):** drain the 2026-09-01 base-red window — passthrough usage regression + radarPage i18n keys ([#12327](https://github.com/diegosouzapw/OmniRoute/pull/12327)) +- **fix(ci):** openapi-security-tiers checker must honor routeGuard patterns + imported prefixes ([#12350](https://github.com/diegosouzapw/OmniRoute/pull/12350)) — thanks @ggiak +- **perf(ci):** cache node_modules in the npm-ci-retry composite (#8084 D3) ([#12408](https://github.com/diegosouzapw/OmniRoute/pull/12408)) +- **revert(ui):** point CTAs back at their real destinations ([#12410](https://github.com/diegosouzapw/OmniRoute/pull/12410)) +- **fix(release):** drain the 2026-09-02 base-red — rerank-providers import + api-typecheck baseline ratchet ([#12414](https://github.com/diegosouzapw/OmniRoute/pull/12414)) +- **fix(types):** make system prompt injection noImplicitAny-safe ([#12416](https://github.com/diegosouzapw/OmniRoute/pull/12416)) +- **fix(quality):** validate typecheck baseline schema ([#12419](https://github.com/diegosouzapw/OmniRoute/pull/12419)) +- **fix(memory):** resolve rerank provider node cache import ([#12421](https://github.com/diegosouzapw/OmniRoute/pull/12421)) +- **fix(ci):** repair release-root regressions — pack dedup, web-session syntax, uc-image ids ([#12423](https://github.com/diegosouzapw/OmniRoute/pull/12423)) — thanks @backryun +- **fix(providers):** separate MaxAI and UC credential contracts ([#12431](https://github.com/diegosouzapw/OmniRoute/pull/12431)) +- **fix(cli):** preserve Claude settings on config set ([#12432](https://github.com/diegosouzapw/OmniRoute/pull/12432)) — thanks @KrzysiekSko +- **fix(providers):** repair the maxai credential block truncated by merge auto-resolve ([#12433](https://github.com/diegosouzapw/OmniRoute/pull/12433)) +- **fix(quality):** record the 2026-09-02 merged growth in the file-size baseline ([#12434](https://github.com/diegosouzapw/OmniRoute/pull/12434)) +- **fix(uc):** route UC error strings through sanitizeErrorMessage; allowlist the retired codex id ([#12437](https://github.com/diegosouzapw/OmniRoute/pull/12437)) +- **fix(adapta):** redact streamed upstream errors ([#12438](https://github.com/diegosouzapw/OmniRoute/pull/12438)) +- **fix(sse):** remap non-contiguous upstream tool_calls index to a gap-free output_index ([#12445](https://github.com/diegosouzapw/OmniRoute/pull/12445)) — thanks @hartmark +- **fix(quality):** detect duplicate tool_calls entries in one response ([#12446](https://github.com/diegosouzapw/OmniRoute/pull/12446)) — thanks @hartmark +- **fix(conversations):** resolve turn content OmniRoute never sends back to the client ([#12447](https://github.com/diegosouzapw/OmniRoute/pull/12447)) — thanks @hartmark +- **fix(combo):** do not treat credits-exhausted 401 as auth skip ([#12449](https://github.com/diegosouzapw/OmniRoute/pull/12449)) — thanks @RaviTharuma +- **fix(auth):** do not park healthy quota accounts as expired ([#12452](https://github.com/diegosouzapw/OmniRoute/pull/12452)) — thanks @RaviTharuma +- **fix(responses-continuation):** fail closed on a collector-truncated, empty output array ([#12460](https://github.com/diegosouzapw/OmniRoute/pull/12460)) — thanks @hartmark +- **fix(logging):** raise the SSE payload collector's default cap ([#12461](https://github.com/diegosouzapw/OmniRoute/pull/12461)) — thanks @hartmark +- **fix(resilience):** surface Responses failed.error.message in 502s ([#12472](https://github.com/diegosouzapw/OmniRoute/pull/12472)) — thanks @RaviTharuma +- **fix(security):** unbiased maxai X-Random nonce + stricter URL/regex assertions ([#12502](https://github.com/diegosouzapw/OmniRoute/pull/12502)) +- **fix(video):** re-anchor transcript log-redaction so PII/credential maskers can't reopen the leak (#12150 P1 follow-up) ([#12503](https://github.com/diegosouzapw/OmniRoute/pull/12503)) +- **fix(monitoring):** serve cached credentialHealth off the request path ([#12533](https://github.com/diegosouzapw/OmniRoute/pull/12533)) — thanks @RaviTharuma +- **fix(quota):** keep Antigravity Gemini usable when Claude weekly is empty ([#12566](https://github.com/diegosouzapw/OmniRoute/pull/12566)) — thanks @HouMinXi +- **fix(authz):** hard-gate every credential export and CLI-config write (GHSA-5926-2w35-7h4q) ([#12600](https://github.com/diegosouzapw/OmniRoute/pull/12600)) +- **fix(docs):** restore the Next build — REMOVED_PROVIDERS.md had no frontmatter (base-red #12581) ([#12610](https://github.com/diegosouzapw/OmniRoute/pull/12610)) +- **fix(quality):** re-point the zcodeProtocol public-creds allowlist to line 313 ([#12615](https://github.com/diegosouzapw/OmniRoute/pull/12615)) +- **fix(security):** close 3 advisories — search baseUrl exfil, sk- in the error sanitizer, bifrost relay header leak ([#12620](https://github.com/diegosouzapw/OmniRoute/pull/12620)) +- **fix(providers):** sanitize boolean required and nested bare maps for Gemini (#12269) ([#12624](https://github.com/diegosouzapw/OmniRoute/pull/12624)) — thanks @HouMinXi +- **fix(resilience):** keep Overloaded STREAM_EARLY_EOF off the provider breaker ([#12626](https://github.com/diegosouzapw/OmniRoute/pull/12626)) — thanks @HouMinXi +- **fix(catalog):** re-audit free-tier quotas against official pages ([#12649](https://github.com/diegosouzapw/OmniRoute/pull/12649)) +- **fix(dashboard):** keep a request's pending-tracking id stable across combo target retries ([#12650](https://github.com/diegosouzapw/OmniRoute/pull/12650)) — thanks @hartmark +- **fix(tests):** retire dead model ids from the chat-pipeline integration suite (base-red #12581) ([#12670](https://github.com/diegosouzapw/OmniRoute/pull/12670)) +- **fix:** resolve SqliteError no such table compression_run_telemetry during cleanup ([#12682](https://github.com/diegosouzapw/OmniRoute/pull/12682)) — thanks @groovecityJO +- **fix(combo):** treat a pin-only step as implicit connection allowlist ([#12697](https://github.com/diegosouzapw/OmniRoute/pull/12697)) — thanks @HouMinXi +- **fix(ci):** ship .npmrc in published package so legacy-peer-deps applies to consumers (#11544) ([#12699](https://github.com/diegosouzapw/OmniRoute/pull/12699)) — thanks @KooshaPari +- **fix(docker):** pin 4 CLI tools to exact versions (#12576) ([#12703](https://github.com/diegosouzapw/OmniRoute/pull/12703)) — thanks @KooshaPari +- **fix(docker):** document and harden cli profile trust boundary (#12570) ([#12706](https://github.com/diegosouzapw/OmniRoute/pull/12706)) — thanks @KooshaPari +- **fix(sse):** redact video transcript in pre-guardrail rejected-request logs (#12150 P2 item 7) ([#12710](https://github.com/diegosouzapw/OmniRoute/pull/12710)) +- **fix(i18n):** quote placeholder in OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES description (#12505) ([#12769](https://github.com/diegosouzapw/OmniRoute/pull/12769)) — thanks @KooshaPari +- **fix(api/pricing):** surface validation error message as string, not raw object (#12494) ([#12771](https://github.com/diegosouzapw/OmniRoute/pull/12771)) — thanks @KooshaPari +- **fix(sse):** re-enable prompt compression for native Codex passthrough ([#12834](https://github.com/diegosouzapw/OmniRoute/pull/12834)) — thanks @thomasmaerz +- **fix(dashboard):** model health tests for a provider node set to the Responses API now call `/v1/responses` with a Responses-shaped body instead of `/v1/chat/completions` — those models were reported as `Provider returned HTTP 200 but no text content` even though the same model answered normally through `/v1/responses` ([#13070](https://github.com/diegosouzapw/OmniRoute/issues/13070)) ([#13087](https://github.com/diegosouzapw/OmniRoute/pull/13087)) — thanks @ntdatt812 +- fix(routing): stop a round-robin combo's "opencode" targets from collapsing onto the opencode-zen connection (#11912) ([#13283](https://github.com/diegosouzapw/OmniRoute/pull/13283)) +- fix(routing): stop the reactive-compaction debug log from lying when compression is globally disabled (#11977) ([#13276](https://github.com/diegosouzapw/OmniRoute/pull/13276)) +- fix(electron): relativize standalone-bundle symlink targets so Stage 8 manifest verification stops failing on Windows (#11979) ([#13251](https://github.com/diegosouzapw/OmniRoute/pull/13251)) +- fix(dashboard): surface a visible error when Compression Studio's combined preview run fails (#12061) ([#13288](https://github.com/diegosouzapw/OmniRoute/pull/13288)) +- fix(dashboard): make the compression "Effective pipeline" preview honor the active profile and warn when the master switch is off (#12063) ([#13287](https://github.com/diegosouzapw/OmniRoute/pull/13287)) +- fix(providers): scope TinyCMS Web signer's DOM shims to each call instead of leaking them for the process lifetime, and surface a clean HTTP status on a non-JSON interception-toggles error (#12072) ([#13254](https://github.com/diegosouzapw/OmniRoute/pull/13254)) +- fix(guardrails): stop Vision Bridge from re-selecting a model locked after a 404 (#12111) ([#13259](https://github.com/diegosouzapw/OmniRoute/pull/13259)) +- fix(sse): require Responses-shaped body before native OpenAI-compatible passthrough (#12129) ([#13278](https://github.com/diegosouzapw/OmniRoute/pull/13278)) +- fix(providers): minimax-m3 now collapses manual thinking.type:"enabled" to adaptive, preventing upstream 400 (2013) (#12132) ([#13249](https://github.com/diegosouzapw/OmniRoute/pull/13249)) +- fix(db): scope model visibility overrides by modality so hiding a Chat model no longer hides an identically-ID'd Image/Embeddings/etc. model (#12172) ([#13258](https://github.com/diegosouzapw/OmniRoute/pull/13258)) +- fix(db): scope local-provider apiKey dedup to matching base URL so LM Studio/Ollama-style connections support multiple accounts (#12173) ([#13250](https://github.com/diegosouzapw/OmniRoute/pull/13250)) +- fix(providers): refresh Trae's stale Referer/Origin and forward user timezone so imported connections stop failing with 401 (#12190) ([#13255](https://github.com/diegosouzapw/OmniRoute/pull/13255)) +- fix(providers): route opencode-go/gpt-5.6-luna to /responses instead of /chat/completions (#12196) ([#13275](https://github.com/diegosouzapw/OmniRoute/pull/13275)) +- fix(dashboard): allow deleting the last extra-upstream-header row even when invalid (#12251) ([#13252](https://github.com/diegosouzapw/OmniRoute/pull/13252)) +- **fix(i18n):** translate pre-existing `__MISSING__:` keys for `combo.sort`, `requestLogger.detail` expand/collapse, `common.profile`, and `settings.resilienceCredentialHealth*` across 39 locales ([#12272](https://github.com/diegosouzapw/OmniRoute/issues/12272)) ([#13034](https://github.com/diegosouzapw/OmniRoute/pull/13034)) — thanks @HouMinXi +- fix(cli): run the Node.js runtime compatibility guard before the heavy `tsx/esm` + Commander import chain so an unsupported runtime gets a clear message instead of a raw `Invalid regular expression flags` crash (#12296) ([#13253](https://github.com/diegosouzapw/OmniRoute/pull/13253)) +- fix(dashboard): refresh the providers list after deleting a compatible provider node (#12298) ([#13256](https://github.com/diegosouzapw/OmniRoute/pull/13256)) +- fix(usage): fail closed on API-key budget enforcement when a provider's `auto` routing alias has no pricing row, instead of silently counting it as $0 (#12341) ([#13257](https://github.com/diegosouzapw/OmniRoute/pull/13257)) +- **fix(providers):** include the submitted Agnes video model when polling by `video_id` ([#12356](https://github.com/diegosouzapw/OmniRoute/pull/12356)) — thanks @pacocartones +- **fix(routing):** custom OpenAI-compatible nodes now honor the saved Chat/Responses API type after edits instead of letting the node's original ID prefix override the live connection setting ([#11884](https://github.com/diegosouzapw/OmniRoute/issues/11884)). ([#12358](https://github.com/diegosouzapw/OmniRoute/pull/12358)) — thanks @pacocartones +- fix(sse): surface an error instead of a silent empty 200 when a Claude stream closes with zero bytes (#12398) ([#13285](https://github.com/diegosouzapw/OmniRoute/pull/13285)) +- fix(oauth): warn before the dead localhost:8080 redirect in antigravity/gemini `oauth start` (#12413) ([#13265](https://github.com/diegosouzapw/OmniRoute/pull/13265)) +- fix(providers): stop devin-cli spawn error from double-closing the SSE controller (#12517) ([#13261](https://github.com/diegosouzapw/OmniRoute/pull/13261)) +- **fix(rerank):** clamp Voyage `top_k` to the documents actually sent after empty-string filtering, and honor `return_documents: false` in the NVIDIA response adapter (#12523 — thanks @pacocartones) +- **fix(audio):** `/v1/audio/translations` now resolves combo names the way `/v1/audio/transcriptions` already does, so a combo that `GET /v1/models` advertises is fanned out to its targets instead of being rejected with `400 Invalid translation model: . Use format: provider/model`; literal `provider/model` ids and unknown bare names behave as before (#12536 — thanks @pacocartones) +- **fix(gemini):** strip the JSON-Schema-2020-12 `prefixItems` keyword from Gemini tool schemas at every nesting level, so Claude Code tool definitions no longer fail with `400 Unknown name "prefixItems"` on Gemini models (#12540 — thanks @pacocartones) +- **fix(api-manager):** Expose an accessible loading status while API keys are fetched instead of an empty accessibility tree (#12541 — thanks @pacocartones) +- **fix(video):** Clamp `estimateJpegFrameBytes` at zero for padding-only payloads and build the three encode-side frame data URIs from `JPEG_FRAME_DATA_URI_PREFIX` instead of a repeated literal (#12543 — thanks @pacocartones) +- **fix(devin):** accept Windows `DEVIN_AGENTIC_HOME` sandbox paths (`C:\...\.sandbox\...`) in the isolated-home check so the Devin Claude Bridge no longer fails closed on Windows ([#12405](https://github.com/diegosouzapw/OmniRoute/issues/12405)) (#12545 — thanks @pacocartones) +- **fix(db):** Add `conversation_turn_nodes` and orphaned `agentic_conversations` to the auto-cleanup cycle under the existing `retention.callLogs` window, so identity nodes whose call-log content has already been purged no longer accumulate without bound in `storage.sqlite` (#12548 — thanks @pacocartones) +- **fix(i18n):** Wrap the `~/.claude/profiles//settings.json` placeholder in ICU single quotes in the `OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES` feature-flag description across all 42 locales and the TypeScript default, so next-intl no longer fails with `INVALID_MESSAGE: UNCLOSED_TAG` and the Feature Flags card shows the description instead of the raw key (#12549 — thanks @pacocartones) +- **fix(orchestration):** `updateCloudAgentTask` now publishes the task's real `status` on `agent.task.updated` when an update only touches `result`, `activities` or `error`, instead of the fabricated `"updated"` state, and stays silent when no row matched the id (#12550 — thanks @pacocartones) +- **fix(i18n):** the home "Recent Requests" panel and the Provider Topology legend are now translated instead of rendering English copies on non-English dashboards; the legend reads its own `home.topologyLegend*` labels with consistent casing rather than borrowing the memory-settings "Recent" and analytics "Error" strings (#12551 — thanks @pacocartones). +- **docs(reference):** bring the `FEATURE_FLAGS.md` catalog back to 1:1 with `featureFlagDefinitions.ts` — 20 missing flags added, the two `*_BLOCK_THRESHOLD` env-only knobs moved out of the flag tables, category/total counts and the Live WS port corrected, guarded by a static test (#12552 — thanks @pacocartones) +- **fix(i18n):** backfill missing `usage.kiloPass*` strings in 39 locales and restore `featureFlagOmnirouteDisableThinkingLevelVariantsDescription` in `pt.json` ([#12561](https://github.com/diegosouzapw/OmniRoute/issues/12561)) ([#13027](https://github.com/diegosouzapw/OmniRoute/pull/13027)) — thanks @HouMinXi +- fix(docker): default docker-compose app ports (dashboard/API/live-WS) to loopback instead of `0.0.0.0`, closing the anonymous `/v1` LAN/WAN exposure gap left open by `REQUIRE_API_KEY=false` (#12568) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): close DNS-rebinding SSRF gap in webhook outbound-URL guard (#12569) ([#13243](https://github.com/diegosouzapw/OmniRoute/pull/13243)) +- fix(docker): require a per-session token on the VNC browser CDP bridge and isolate it on a dedicated Docker network (#12571) ([#13241](https://github.com/diegosouzapw/OmniRoute/pull/13241)) +- fix(open-sse): write Adobe Firefly session tokens and cookie jars with 0700/0600 permissions instead of the process umask (#12572) ([#13242](https://github.com/diegosouzapw/OmniRoute/pull/13242)) +- fix(api): remove hardcoded wildcard CORS in openai-to-gemini-sse.ts so the centralized fail-closed CORS gate is the sole source of `Access-Control-Allow-Origin` (#12573) ([#13239](https://github.com/diegosouzapw/OmniRoute/pull/13239)) +- fix(api): enforce API key policy (budget/rate-limit/schedule/endpoint scoping) on the ElevenLabs speech-to-text, text-to-speech and voices proxy routes (#12574) ([#13244](https://github.com/diegosouzapw/OmniRoute/pull/13244)) +- fix(sse): cap HuggingChat NDJSON body size and bound the read loop with the fetch timeout so a stalled or hostile upstream cannot buffer unbounded memory (#12577) ([#13282](https://github.com/diegosouzapw/OmniRoute/pull/13282)) +- fix(docker): scope the cliproxyapi/qdrant/bifrost sidecars to loopback by default and forward `CLIPROXYAPI_MANAGEMENT_KEY` into the cliproxyapi container so its management API is not left both unauthenticated and LAN-published (#12578) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): create DB export temp paths with `fs.mkdtempSync` instead of predictable timestamps (#12579) ([#13237](https://github.com/diegosouzapw/OmniRoute/pull/13237)) +- Cline 401 bodies that say "re-authenticate your Cline account" classify as a refreshable OAuth token, not a terminal expired key. The cooling panel no longer labels every cooldown as a 429; it shows the recorded last error instead. (#12594) ([#13060](https://github.com/diegosouzapw/OmniRoute/pull/13060)) — thanks @HouMinXi +- **fix(catalog):** degrade unknown combo targets instead of dropping LCD modalities, and persist OpenRouter `architecture.input_modalities` into the capability snapshot ([#12613](https://github.com/diegosouzapw/OmniRoute/issues/12613)) ([#13042](https://github.com/diegosouzapw/OmniRoute/pull/13042)) — thanks @HouMinXi +- fix(providers): send `x-api-key` instead of `Authorization: Bearer` for OpenCode Zen's `/v1/responses` endpoint (Muse Spark Contributor models), fixing a 401 on OmniRoute's auth header (#12633) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(sse): surface the actionable "Auggie CLI not found" message when the shell reports a missing `auggie` binary via exit code instead of a spawn error (#12645) ([#13263](https://github.com/diegosouzapw/OmniRoute/pull/13263)) +- **fix(config):** Persist deletions of built-in background-degradation entries — when a stored settings record exists its `degradationMap` is now authoritative instead of being merged under the defaults, so an entry the user removed in the dashboard no longer reappears on the next apply or restart ([#12424](https://github.com/diegosouzapw/OmniRoute/issues/12424)) ([#12647](https://github.com/diegosouzapw/OmniRoute/pull/12647)) — thanks @pacocartones +- **fix(gamification):** action-count badge milestones (First Token, Token Consumer, Token Machine, Token Whale, and the token-sharing tier) are now backed by a durable `xp_action_counts` counter incremented in `addXp()`, instead of a live `COUNT(*)` over `xp_audit_log`. The audit log is pruned by `retention.xpAuditLog` (default 30 days), so on a default install those "lifetime" milestones were really "actions in the last 30 days" and unlocked badges could stop unlocking once old rows aged out. `getActionCount()` and `checkActionCountBadges()` now read the same durable source, and a migration backfills existing totals from the surviving audit rows ([#12546](https://github.com/diegosouzapw/OmniRoute/issues/12546)) ([#12651](https://github.com/diegosouzapw/OmniRoute/pull/12651)) — thanks @pacocartones +- **fix(images):** `/v1/images/edits` now iterates a combo's targets the same way `/v1/images/generations` does (#9239) instead of flattening a bare combo to its first target. A combo whose first target is not edit-capable — or lacks credentials — now falls through to a later edit-capable target rather than hard-erroring, and missing credentials are skipped (not a hard `401`) to match the generations path. The per-target skip/terminal classification is extracted into a shared `runImageComboTargets` loop, so generations behavior is unchanged ([#12547](https://github.com/diegosouzapw/OmniRoute/issues/12547)). ([#12653](https://github.com/diegosouzapw/OmniRoute/pull/12653)) — thanks @pacocartones +- fix(sse): add first-byte watchdog to the TLS-fingerprint transport so a stalled wreq body falls back instead of hanging for minutes (#12656) ([#13272](https://github.com/diegosouzapw/OmniRoute/pull/13272)) +- fix(sse): exempt tiny-budget reasoning probes from combo quality failure and surface persisted-cooldown skips in ALL_TARGETS_SKIPPED diagnostics (#12659) ([#13269](https://github.com/diegosouzapw/OmniRoute/pull/13269)) +- fix(models): declare the real ~1M contextLength for OpenCode Zen's Muse Spark 1.2 models instead of falling back to the 200000 provider default (#12681) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(oauth): align codebuddy-cn OAuth User-Agent with the chat/usage CLI version to avoid WAF false positives (#12702) ([#13264](https://github.com/diegosouzapw/OmniRoute/pull/13264)) +- fix(dashboard): surface an authentication-required banner instead of silently blanking database settings for a guest session (#12709) ([#13270](https://github.com/diegosouzapw/OmniRoute/pull/13270)) +- **fix(resilience):** stop unbounded queue that hangs 6min until Aborted — gate, provider slot, and Bottleneck queue now share a per-connection `maxWaitMs` budget; `fail-closed` on exhaust (503); execution backstop `executionMaxWaitMs` overridable per connection with upstream clamp ([#12715](https://github.com/diegosouzapw/OmniRoute/pull/12715)) — thanks @maxmad64bis (with thanks to @Tushar49 for surfacing the slow-provider need in #12635) +- **fix(auto-combo):** every mode pack now carries `quality` and `reliability` weights (`quality` 0.02, 0.03 in `quality-first`; `reliability` 0.03, 0.04 in `reliability-first`), so selecting a pack no longer silences either signal; `DEFAULT_WEIGHTS` is unchanged ([#12731](https://github.com/diegosouzapw/OmniRoute/pull/12731)) — thanks @maxmad64bis +- **fix(sse):** the provider execution pipeline keeps the upstream error `code`/`type` again — both were lost when the non-streaming leg was extracted, so a config-class `422` (Antigravity missing project) degraded into a generic account cooldown instead of reaching the gate that recognises it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(db):** a legacy `call_logs` table no longer aborts startup — the provider-stats index is created after the column healing runs, not before it, so an install predating `request_type` opens instead of failing with `no such column` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(catalog):** a malformed row in the operator-writable custom-models blob no longer kills every `auto/*` pool ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(tests):** realign the guards that the same day's merges left asserting the old behavior — the provider-count total after GreenPT and EURouter, the GLM stream buffer slot that `#12925` turned into a declared parameter, and the injection scan bound that `#13104` reshaped from a head-only window into head-plus-tail ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- fix(cache): fold tool_choice/tools/response_format into the semantic cache signature so a cached tool_calls response can no longer be replayed for a request whose tool policy forbids it (#12734) ([#13267](https://github.com/diegosouzapw/OmniRoute/pull/13267)) +- **fix(models):** stop treating provider-supplied `isFree:true`/`:free`/`0/0` flags as trusted unless the provider has a documented free tier — only the shipped free-tier catalog decides otherwise; fetched `isFree:true` trusted only for a free-tier provider, custom `isFree:true` via a trusted path ([#12744](https://github.com/diegosouzapw/OmniRoute/pull/12744)) — thanks @maxmad64bis +- fix(memory): authenticate the internal /v1/rerank loopback call so memory reranking no longer silently degrades to unranked order when REQUIRE_API_KEY=true (#12745) ([#13268](https://github.com/diegosouzapw/OmniRoute/pull/13268)) +- fix(sse): parse Ollama Cloud's current usage markup (`$X of $Y used` aria-label, nested width style) (#12749) ([#13260](https://github.com/diegosouzapw/OmniRoute/pull/13260)) +- fix(cli): setup-opencode no longer sends an active context's management token to `/v1/models` when `--api-key`/`OMNIROUTE_API_KEY` is supplied — an explicit flag or the env var now always outranks the context's token, and the flag itself is no longer swallowed by the parent program's global `--api-key` option (#12783) ([#13246](https://github.com/diegosouzapw/OmniRoute/pull/13246)) +- fix(sse): register Arcee AI in the executor provider registry so requests reach api.arcee.ai instead of silently falling back to OpenAI (#12784) ([#13277](https://github.com/diegosouzapw/OmniRoute/pull/13277)) +- **fix(quota):** share one upstream quota read when identical saturation checks arrive at the same time, so traffic bursts don't multiply provider API calls ([#12787](https://github.com/diegosouzapw/OmniRoute/pull/12787)) — thanks @maxmad64bis +- **fix(db):** ignore expired or invalid rate-limit cooldown writes so a stale timestamp can't lock a connection that should be usable — clearing still works as before ([#12788](https://github.com/diegosouzapw/OmniRoute/pull/12788)) — thanks @maxmad64bis +- **fix(providers):** single source for provider order with `xao` ranking alongside `xai-oauth` ([#12790](https://github.com/diegosouzapw/OmniRoute/pull/12790)) — thanks @maxmad64bis +- **fix(routing):** off-table models check the free-model catalog before inheriting premium prices, latency bootstraps from the observed pool median, async tiers read live database pricing with a 90-day freshness gate, and the tier cache invalidates on every pricing write ([#12792](https://github.com/diegosouzapw/OmniRoute/pull/12792)) — thanks @maxmad64bis +- **fix(auto-combo):** snapshot scoring uses the observed per-connection account tier and per-model quality instead of neutral constants, and circuit-open providers rank lower at build time ([#12794](https://github.com/diegosouzapw/OmniRoute/pull/12794)) — thanks @maxmad64bis +- **fix(pool):** empty `auto/*` pools now say why they are empty, and the models listing uses the same paid check as routing ([#12795](https://github.com/diegosouzapw/OmniRoute/pull/12795)) — thanks @maxmad64bis +- fix(routing): recognize CLIProxyAPI's 'unknown provider for model' 400 as fallback-worthy (#12800) ([#13284](https://github.com/diegosouzapw/OmniRoute/pull/13284)) +- **fix(sse):** translate-mode streams now emit the estimated token counts as a trailing usage-only chunk before `[DONE]` when the upstream stays silent, so metered chat clients see totals instead of nothing ([#12828](https://github.com/diegosouzapw/OmniRoute/pull/12828)) — thanks @maxmad64bis +- **fix(db):** provider stats stay truthful on empty and legacy databases: fallback counts default to `0` instead of `null`, latency averages read `null` (not `0`) when no durations were recorded, failures logged before the error-type column existed group under `pre_migration` instead of `unclassified`, and per-provider queries use two new composite indexes ([#12832](https://github.com/diegosouzapw/OmniRoute/pull/12832)) — thanks @maxmad64bis +- fix(nvidia): fail open when a synced model catalog goes stale instead of gating forever (#12849) ([#13248](https://github.com/diegosouzapw/OmniRoute/pull/13248)) +- **fix(db):** WAL maintenance lives in its own module: a `TRUNCATE` checkpoint that hits a busy database now warns with its streak and retries once via `PASSIVE` instead of logging success, and closing-time checkpoints no longer report success on builds without a database file, with the busy totals visible in the authenticated monitoring health payload ([#12853](https://github.com/diegosouzapw/OmniRoute/pull/12853)) — thanks @maxmad64bis +- **fix(health):** quota with no snapshots reads empty instead of a contradicting 0% ([#12857](https://github.com/diegosouzapw/OmniRoute/pull/12857)) — thanks @maxmad64bis +- fix(a2a): accept the dashboard's own session cookie on /a2a so "Run message/send" no longer fails with "Unauthorized: missing or invalid API key" (#12888) ([#13271](https://github.com/diegosouzapw/OmniRoute/pull/13271)) +- **fix(logs):** the Logs grid's in-memory filter pass no longer discards rows the SQL query already matched — selecting an API key from the dropdown (which sends the key's id) returns its calls again, the Combo tab shows every combo instead of only those whose name contains a "1", and the model filter and search cover the same columns as the query ([#12896](https://github.com/diegosouzapw/OmniRoute/pull/12896)) — fixes [#12873](https://github.com/diegosouzapw/OmniRoute/issues/12873) — thanks @ntdatt812 +- **fix(a2a):** `/api/a2a/status` now builds the agent card from the request that asked for it, so a gateway reached at a non-localhost host no longer advertises `http://localhost:20128` as its A2A URL ([#12918](https://github.com/diegosouzapw/OmniRoute/pull/12918)). — thanks @ntdatt812 +- **fix(compression):** progressive aging now appends its `[COMPRESSED:aging:…]` annotation after a turn's `tool_result` blocks instead of in front of them, so Anthropic no longer rejects aged conversations with "`tool_use` ids were found without `tool_result` blocks immediately after" ([#12920](https://github.com/diegosouzapw/OmniRoute/pull/12920)). — thanks @ntdatt812 +- **fix(bedrock):** model import now resolves context limits for every vendor prefix instead of only `anthropic.*`, so `global.openai.gpt-5.6-*` no longer imports with a null `inputTokenLimit` and gets rejected pre-flight at the 200k default ([#12921](https://github.com/diegosouzapw/OmniRoute/pull/12921)). — thanks @ntdatt812 +- **fix(stream):** the 64 KB stream buffer GLM asks for is honoured instead of dropped, and the type error it caused no longer fails the API Route Typecheck gate on every open PR ([#12925](https://github.com/diegosouzapw/OmniRoute/pull/12925)) — thanks @ntdatt812 +- **fix(guardrails):** mask PII inside a `tool_result`'s nested content array, which the masker walked past while redacting its sibling block ([#12930](https://github.com/diegosouzapw/OmniRoute/pull/12930)) — thanks @ntdatt812 +- **fix(dashboard):** the Radar catalog table no longer leaves absent data unexplained — empty limits, unknown context windows, and unreported capabilities each explain themselves on hover, and a new check keeps it that way ([#12937](https://github.com/diegosouzapw/OmniRoute/pull/12937)) — thanks @maxmad64bis +- **fix(sse):** geo-blocked opencode requests rotate to the next account proxy instead of failing, so one refused egress no longer aborts the whole chain ([#12941](https://github.com/diegosouzapw/OmniRoute/pull/12941)) — thanks @maxmad64bis +- **fix(codex):** dashboard "clear cooldown" and the CAS recovery path now drop nested `codexScopeRateLimitedUntil` maps in the same write that nulls `rate_limited_until`, and fresh quota snapshots with headroom lift fallback-sourced scope cooldowns parked by quota preflight ([#12817](https://github.com/diegosouzapw/OmniRoute/issues/12817), [#12860](https://github.com/diegosouzapw/OmniRoute/issues/12860), [#12951](https://github.com/diegosouzapw/OmniRoute/pull/12951)) The dashboard reset-credit button (`consumeCodexResetCredit`) now exercises that same snapshot path after a successful redeem, so a filled quota bar is enough to unpark a leftover fallback Codex child without another manual clear. — thanks @HouMinXi +- **fix(db):** resolve `sql-wasm.wasm` across global npm install and hoisted layouts, ensuring OmniRoute can boot cleanly on Node 24 when native `better-sqlite3` is uncompiled. ([#13035](https://github.com/diegosouzapw/OmniRoute/pull/13035)) — thanks @HouMinXi +- **fix(api):** `DELETE /v1/batches/delete-completed` now sweeps only the calling API key's own completed batches (batches with no owner stay out of a key-scoped sweep on purpose), with an explicit instance-wide mode reserved for authenticated dashboard sessions, a 401 for a presented key that is unknown, revoked, deactivated, banned or expired (never falling through to the session branch), audit logging of every sweep, a sanitized 500 on failure, an owner-scoped file half (a key-scoped sweep never nulls a file another tenant owns) and an atomic sweep — chunked in 200-batch transactions in instance mode — so a mid-way error never leaves a batch pointing at a nulled file (GHSA-wvxc-jp3v-5mg5) ([#12969](https://github.com/diegosouzapw/OmniRoute/pull/12969)) +- **fix(combo):** quota-weighted routing stops drawing on an out-of-credit connection — a 402 now invalidates the stored quota snapshot instead of leaving its stale remaining percentage in place, and a snapshot older than 10 minutes no longer counts as confident headroom for the primary pool ([#12972](https://github.com/diegosouzapw/OmniRoute/pull/12972)) ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- **fix(vision):** Custom Models with "Vision capable" checked no longer have image requests swapped to `glm/glm-4.6v` when the client sends the advertised alias (`vllm/path/...`) or the bare path-shaped id — Vision Bridge now matches the stored override for all three id forms ([#12758](https://github.com/diegosouzapw/OmniRoute/issues/12758)) ([#12974](https://github.com/diegosouzapw/OmniRoute/pull/12974)) — thanks @HouMinXi +- **fix(sse):** transient opencode upstream failures rotate to the next account proxy instead of failing, so one flapping egress no longer aborts the whole chain ([#12975](https://github.com/diegosouzapw/OmniRoute/pull/12975)) — thanks @maxmad64bis +- **fix(azure):** Deployments from GPT-6 onward now send `max_completion_tokens` instead of `max_tokens`, which Azure rejects with HTTP 400. The rule matched a literal `gpt-5`, so each new generation arrived broken; it now matches the generation range, while `gpt-35-turbo` still keeps `max_tokens`. ([#13007](https://github.com/diegosouzapw/OmniRoute/pull/13007)) — thanks @ntdatt812 +- **fix(db):** add an opt-in self-restart circuit for sustained critical memory pressure, gate post-cleanup VACUUM behind a minimum freed-rows threshold, and checkpoint the SQLite WAL every 5 minutes with a size guard that escalates to TRUNCATE, so a growing WAL can no longer stall the event loop into a full outage. ([#13011](https://github.com/diegosouzapw/OmniRoute/pull/13011)) — thanks @HouMinXi +- **fix(auth):** an explicit connection pin may probe a quota-disabled row once and re-enable it on success ([#12874](https://github.com/diegosouzapw/OmniRoute/issues/12874)) ([#13017](https://github.com/diegosouzapw/OmniRoute/pull/13017)) — thanks @HouMinXi +- **fix(api):** thread `X-OmniRoute-Fallback-Attempts` through combo chat completions so streaming and non-streaming responses report how many prior legs were attempted ([#13038](https://github.com/diegosouzapw/OmniRoute/pull/13038)) — thanks @HouMinXi +- Restore provider failure classification and credential refresh on non-streaming requests: classify non-2xx failures to lock models on per-model quota exhaustion, update connection rate limits from headers and body, and pass credential refresh handlers to pipeline execution so 401 tokens can be refreshed and retried (#13043). ([#13069](https://github.com/diegosouzapw/OmniRoute/pull/13069)) — thanks @HouMinXi +- **fix(responses):** wrap forced-non-streaming web_search fallback JSON as Responses SSE so Codex still sees `response.completed` ([#13050](https://github.com/diegosouzapw/OmniRoute/pull/13050)) — thanks @HouMinXi +- **fix(antigravity):** Preserve upstream thought-token usage in normalized completion and reasoning token counts ([#13055](https://github.com/diegosouzapw/OmniRoute/pull/13055)) — thanks @pacocartones +- Fix a concurrency-slot leak in the DeepSeek PoW solver: a worker that failed to spawn (for example a missing worker script) never released its slot, so `MAX_CONCURRENT_WORKERS` failures disabled the solver until restart. ([#13097](https://github.com/diegosouzapw/OmniRoute/pull/13097)) — thanks @anhtahaylove +- **fix(acp):** bound the ACP session output buffers — `stdoutBuffer` and `stderrBuffer` now cap at 1 MiB keeping the most recent output behind a visible `[...output truncated...]` marker, and `stderrBuffer` is reset per prompt instead of accumulating for the lifetime of the session. ([#13100](https://github.com/diegosouzapw/OmniRoute/pull/13100)) — thanks @anhtahaylove +- **fix(acp):** release the `stdout`/`exit` listeners and the idle timer that a `sendPrompt` timeout used to leave attached to the `acpManager` singleton, and drop sessions that exited on their own from the session map instead of keeping them forever. ([#13096](https://github.com/diegosouzapw/OmniRoute/pull/13096)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection and PII scanners now read the text a `tool_result` block carries on `content` (string or nested block list), in messages and in system blocks, so tool output is judged by the same rules as user text ([#13101](https://github.com/diegosouzapw/OmniRoute/pull/13101)) — thanks @ntdatt812 +- **fix(gamification):** close the badge notification SSE stream when the request signal is already aborted before the stream starts — a client that disconnects while the route is still awaiting auth used to leave both the 2s unlock poll and the 15s heartbeat running for the lifetime of the process. ([#13106](https://github.com/diegosouzapw/OmniRoute/pull/13106)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection scan now spends its 16 KB budget on both ends of the request instead of the first 16 KB only, so `system`, `instructions`, `query`, `documents` and the newest turns are no longer hidden behind one long message ([#13104](https://github.com/diegosouzapw/OmniRoute/pull/13104)) — thanks @ntdatt812 +- **fix(dashboard):** expose the Volcano Ark console cookie on quota scraping and unwrap connect-error objects so the dashboard shows the upstream message ([#13107](https://github.com/diegosouzapw/OmniRoute/pull/13107)) — thanks @HouMinXi +- **fix(db):** release the `beforeExit`/`SIGINT`/`SIGTERM` handlers when a `node:sqlite` adapter closes, so a closed adapter and its database handle are no longer pinned to `process` for the lifetime of the run — the same treatment #7494 gave the sql.js adapter. ([#13109](https://github.com/diegosouzapw/OmniRoute/pull/13109)) — thanks @anhtahaylove +- **fix(translator):** `contentSchema` and `unevaluatedItems` are now treated as subschema positions by the tool-schema sanitizer, so a truncation placeholder in either is replaced with a permissive schema instead of being forwarded as a string ([#13110](https://github.com/diegosouzapw/OmniRoute/pull/13110)) — thanks @ntdatt812 +- **fix(cli-helper):** clear the `createLogStream` timeout on the abort path — `stop()` aborts the in-flight fetch and returned through the `signal.aborted` branch, which skipped `clearTimeout` and left an armed timer per stopped stream. The stream reader is now also cancelled when the read loop exits early. ([#13114](https://github.com/diegosouzapw/OmniRoute/pull/13114)) — thanks @anhtahaylove +- **fix(docs):** drop TLS-impersonation, MITM-decrypt, supply-chain attestation, and XOR-mask writeups from the public `/docs` catalog and Docker image. Files stay in git for engineers; operators who need them open the repo, not the website. ([#13136](https://github.com/diegosouzapw/OmniRoute/pull/13136)) — thanks @HouMinXi +- **fix(combo):** parse numeric-epoch `rate_limited_until` in the combo cooldown read path ([#13141](https://github.com/diegosouzapw/OmniRoute/pull/13141)) — thanks @maxmad64bis +- **fix(opencode):** both OpenCode plugins now reject a gateway address typed without `http://` at configuration time, instead of publishing every model with an api url no client can call, and the v2 plugin no longer publishes a model card whose api url is blank or relative ([#13142](https://github.com/diegosouzapw/OmniRoute/pull/13142)) — thanks @maxmad64bis +- **fix(providers):** lock opencode model on upstream 400 model-unavailable ([#13146](https://github.com/diegosouzapw/OmniRoute/pull/13146)) — thanks @maxmad64bis +- **fix(logging):** keep the provider exchange rather than the raw client bodies when a call log exceeds its size budget, and show that recovered payload in the request-detail panel instead of replacing it with the stored response body ([#13147](https://github.com/diegosouzapw/OmniRoute/pull/13147)) — thanks @maxmad64bis +- **fix(traffic-inspector):** the WebSocket route no longer leaks a traffic-buffer subscriber and a 30s ping timer when the client socket is already closed at handler time — listeners are attached before any resource is acquired, a destroyed socket bails out early, and the ping interval stops on a dead socket where `write()` never throws ([#13155](https://github.com/diegosouzapw/OmniRoute/pull/13155)) — thanks @anhtahaylove +- **fix(telegram):** bound the per-user API key cache in the Telegram chat proxy so a burst of distinct chat ids can no longer grow the process heap without limit ([#13165](https://github.com/diegosouzapw/OmniRoute/issues/13165)) ([#13166](https://github.com/diegosouzapw/OmniRoute/pull/13166)) — thanks @anhtahaylove +- **fix(stream):** cancel the upstream response body when the JSON-to-SSE sniff unwinds on a body timeout, so a stalled upstream no longer pins the connection ([#13169](https://github.com/diegosouzapw/OmniRoute/issues/13169)) ([#13171](https://github.com/diegosouzapw/OmniRoute/pull/13171)) — thanks @anhtahaylove +- **fix(telegram):** authenticate webhook deliveries with Telegram's `secret_token` so an unauthenticated caller can no longer mint API keys or spend upstream quota ([#13172](https://github.com/diegosouzapw/OmniRoute/issues/13172)) ([#13175](https://github.com/diegosouzapw/OmniRoute/pull/13175)) — thanks @anhtahaylove +- **fix(models):** give discoverable Gemini 3.8 Flash ids their own 65536 output spec so Antigravity no longer clamps them to 16384 ([#13195](https://github.com/diegosouzapw/OmniRoute/pull/13195)) — thanks @HouMinXi +- **fix(dashboard):** leftover catalog-removed provider rows (gemini-cli) can be listed and purged from the providers page ([#13067](https://github.com/diegosouzapw/OmniRoute/issues/13067)) ([#13197](https://github.com/diegosouzapw/OmniRoute/pull/13197)) — thanks @HouMinXi +- **fix(auth):** a dashboard session now requires the `authenticated: true` claim that login, OIDC and the session refresh already emit — a JWT merely signed with `JWT_SECRET` (for example the Cursor CLI passthrough token, which any API-key holder can obtain) no longer verifies as the `auth_token` cookie on any route, the WebSocket handshake or the live server; existing sessions keep working ([#13298](https://github.com/diegosouzapw/OmniRoute/issues/13298)) ([#13375](https://github.com/diegosouzapw/OmniRoute/pull/13375)) +- **fix(api):** `DELETE /v1/batches/delete-completed` now applies the caller's API-key policy (endpoint allowlist, schedule, usage cap, rate limit) like every other `/v1` route, commits the key-scoped sweep per 200-batch chunk instead of holding one write lock for the whole sweep, refuses to loop without progress, and rejects a scope that names both a key and `allTenants` ([#13684](https://github.com/diegosouzapw/OmniRoute/pull/13684)) +- Restore the API-route typecheck gate: the non-streaming leg result lost its discriminated-union narrowing after the server-owned tool loop reassignment, producing 13 new TS2339 diagnostics in `chatCore.ts`. ([#12963](https://github.com/diegosouzapw/OmniRoute/pull/12963)) +- Restore four non-streaming behaviours the server-owned tool loop refactor dropped: client aborts map to 499 with the fixed `Request aborted` message, an aborted request no longer logs a synthetic `clientResponse`, Claude prompt-cache telemetry is recorded again, and a body that cannot be canonicalized no longer throws when the tool loop is off. ([#12990](https://github.com/diegosouzapw/OmniRoute/pull/12990)) +- **fix(skills):** The CLI registry parser now reads positionals declared with `.addArgument()`, not only those written inline in `.command()`. `tunnel create [type]` was being published as `tunnel create`, so the agent-skills sync gate reported drift on every branch and regenerating would have deleted the argument. ([#13009](https://github.com/diegosouzapw/OmniRoute/pull/13009)) — thanks @ntdatt812 +- **fix(combos):** dashboard combo test uses a short prompt, serial probes, and a 60s timeout so reasoning models and rate-limited free pools do not fail the health check ([#13001](https://github.com/diegosouzapw/OmniRoute/pull/13001)) — thanks @HouMinXi +- fix(compression): terminate idle worker threads on eviction so long-running instances stop leaking OS threads and MessagePorts ([#13091](https://github.com/diegosouzapw/OmniRoute/pull/13091)) — thanks @anhtahaylove +- Redact Google API keys of any length in error bodies: the pattern required exactly 39 characters, so shorter or longer `AIza…` credentials passed through unredacted. ([#12964](https://github.com/diegosouzapw/OmniRoute/pull/12964)) +- **fix(grok-cli):** a 402 "Grok Build usage balance exhausted" parks that Grok login as out of credit (Grok Build CLI, grok.com cookie, and xAI OAuth share the weekly pool). Combo routing then tries the next login instead of locking the model for every account in the pool ([#13061](https://github.com/diegosouzapw/OmniRoute/pull/13061)) — thanks @HouMinXi +- fix(compression): spawn the LLMLingua worker with a file URL object so compression actually runs instead of silently failing open on Node ([#13093](https://github.com/diegosouzapw/OmniRoute/pull/13093)) — thanks @anhtahaylove +- **fix(test):** run the local `test` and `test:unit` scripts at concurrency 4 so a full-suite run no longer exhausts the machine's commit charge and kills unrelated processes ([#13187](https://github.com/diegosouzapw/OmniRoute/pull/13187)) — thanks @anhtahaylove +- **fix(monitoring):** `GET /api/monitoring/health` `credentialHealth` now includes a bounded `failedConnections` list (`connectionId`, `status`, sanitized `lastError`) when the probe-cache gauge `failed>0`, plus `source: probe-cache` and a cheap `staleDbNonOkCount` for sticky SQLite `test_status` on active rows. Documents that the live gauge is not `provider_connections.test_status`. ([#12876](https://github.com/diegosouzapw/OmniRoute/pull/12876)) — thanks @RaviTharuma +- **fix(resilience):** an openai-compatible multi-upstream gateway no longer marks the whole connection `credits_exhausted` when a single upstream (or the `/models` / representative-model health probe) returns 402. Quota failures stay model-scoped; true connection-wide auth failures are unchanged. Single-credential 402 key disable ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239)) is preserved. ([#12875](https://github.com/diegosouzapw/OmniRoute/pull/12875)) — thanks @RaviTharuma +- **fix(dashboard):** nine fixes on the Orchestration Canvas, all diagnosed in the Phase 2 reviews. A source that fails now keeps the timestamp of its FIRST failure instead of being re-stamped every poll — the stale line said "since the last poll" no matter how long the source had been down, and the churn also defeated the snapshot's stable identity (it serializes the source list), so the canvas re-rendered on every tick while anything was broken. A source that HAD data and only then started failing is flagged too: previously only a source with no node at all got the warning, so a source that went down mid-session kept a healthy-looking node forever. The rest are pointwise: clicking a filter chip cancels the pending search debounce (left armed, it fired ~300ms later and silently reverted the chip); the search input carries an accessible name; `?state=running, failed` parses like the unpadded form instead of dropping the padded value; the CSV toggle helper is defined once in `model/urlParams.ts` rather than twice; the agents tab tells "nothing running" apart from "the filter matched nothing", offering a clear-filters button instead of setup links that would be wrong advice there; edges stop emitting SMIL particles above 40 simultaneously active edges, keeping the colored stroke; and the drawer's error banner clears when a retried action succeeds. ([#12983](https://github.com/diegosouzapw/OmniRoute/pull/12983)) +- **fix(i18n):** the nine locales added with the EU-language batch (Greek, Estonian, Irish, Croatian, Lithuanian, Latvian, Maltese, Slovenian, Serbian) were missing the eleven Orchestration Canvas keys that Phase 3 introduced, so the compare-runs panel and the "no runs match these filters" empty state fell back to English in those languages (`deepMergeFallback` substitutes English for an absent key, so nothing rendered blank — it rendered untranslated). The coverage gate does not catch this: it enforces an 80% floor per locale, and eleven missing keys out of ~13,000 leaves coverage at 99.9%. Translated for real in each language, calibrated against the wording each file already uses for "run", "filter" and "skill". ([#13294](https://github.com/diegosouzapw/OmniRoute/pull/13294)) +- **fix(dashboard):** five follow-ups from the Phase 2 Orchestration Canvas review. The History drawer now shows the memory section for runs that already left the live TTL window: the persisted `memory_hits` event is parsed into `metadata.memoryHits` with the same defensive validation the drawer applies, and — because that event is observability rather than a state transition — it no longer leaks into the timeline, where it had been inheriting the task's state and rendering as a duplicate transition. The A2A memory recall runs against its own 1.5s deadline instead of inheriting the memory backend's 30s one; overshooting degrades exactly like any other recall failure (no hits, task proceeds), and the timer is cleared on both paths. Repeating a Conductor run carries its `requirements.cli`/`requirements.model` forward, so the new run is pinned to the same runner profile and model rather than drifting to whatever the fleet picks. A successful repeat from the Agents tab now focuses the run it created, instead of leaving the operator on the finished one. And the auth test for `POST /api/conductor/tasks` moved into the shared `ROUTES` array rather than restating the pattern. ([#12988](https://github.com/diegosouzapw/OmniRoute/pull/12988)) +- fix(plugins): stop leaking an exit listener per plugin hook timeout, which triggered MaxListenersExceededWarning on plugins that ignore SIGTERM ([#13092](https://github.com/diegosouzapw/OmniRoute/pull/13092)) — thanks @anhtahaylove +- **fix(validation):** Provider node edits no longer fail with a generic "Invalid request" when the optional daily-quota reset fields are left blank. The dashboard sends `dailyQuotaResetTimezone` and `dailyQuotaResetHour` as `null`, and only the hour accepted it. ([#13066](https://github.com/diegosouzapw/OmniRoute/issues/13066)) ([#13083](https://github.com/diegosouzapw/OmniRoute/pull/13083)) — thanks @ntdatt812 +- **fix(combo):** quota-aware expansion drops banned, inactive, missing, and wrong-provider connections before quota fetch or model dispatch; pins and allowlists stay selectors, not a bypass. Antigravity automatic exhaustion now requires a reported zero remaining, so a positive balance below 1% stays eligible. ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- Fix an intermittent failure in the quota-weighted routing test suite: scores are a function of `Date.now()`, so peers with identical quota scored microseconds apart never tied and swapped order. ([#12946](https://github.com/diegosouzapw/OmniRoute/pull/12946)) +- fix(volcengine): resolve quota window mapping gaps, safe multi-connection console binding, and response cookie sanitization ([#12950](https://github.com/diegosouzapw/OmniRoute/pull/12950)) — thanks @HouMinXi +- **fix(test):** resolve the WebDAV handler path with `fileURLToPath` so the suite's 37 WebDAV tests run on Windows instead of failing with a doubled `C:\C:\` drive prefix ([#13196](https://github.com/diegosouzapw/OmniRoute/pull/13196)) — thanks @anhtahaylove +- **fix(ui):** fall back from invalid AnySearch icon alias (direct commit `9587e07b69`) +- **fix(sse):** stop rebuilding a truncated summary from the collector's cap-dropped event array ([#12718](https://github.com/diegosouzapw/OmniRoute/pull/12718)) — thanks @hartmark +- **fix(conversations):** show a pending spinner for unresolved tool nodes instead of "(empty)" ([#12727](https://github.com/diegosouzapw/OmniRoute/pull/12727)) — thanks @hartmark +- **fix(streaming):** fail fast when an upstream stream produces only lifecycle/heartbeat events, never real content ([#12741](https://github.com/diegosouzapw/OmniRoute/pull/12741)) — thanks @hartmark +- **fix(responses-continuation):** bridge the write-in-flight window with an in-memory pending store ([#12854](https://github.com/diegosouzapw/OmniRoute/pull/12854)) — thanks @hartmark +- **fix(translator):** drop or map agent_message on Chat Completions fallback ([#12880](https://github.com/diegosouzapw/OmniRoute/pull/12880)) — thanks @RaviTharuma +- **fix(api):** page and stream-complete GET /v1/models for large catalogs ([#12882](https://github.com/diegosouzapw/OmniRoute/pull/12882)) — thanks @RaviTharuma +- **fix(quota):** align AUTH window labels with usage API and clarify 503 ([#12884](https://github.com/diegosouzapw/OmniRoute/pull/12884)) — thanks @RaviTharuma +- **fix(ci):** clear two base-reds on release/v3.8.51 — mutation-coverage gate + image-only-model guard ([#12945](https://github.com/diegosouzapw/OmniRoute/pull/12945)) +- **fix(api):** scope batch bulk-delete to the calling API key ([#13211](https://github.com/diegosouzapw/OmniRoute/pull/13211)) +- **fix(tests):** align model-sync log assertion to shipped path redaction (base-red) ([#13227](https://github.com/diegosouzapw/OmniRoute/pull/13227)) +- **fix(test):** make the ToS heading guard actually require the parentheses ([#13228](https://github.com/diegosouzapw/OmniRoute/pull/13228)) +- **fix(quality):** register 4 drifted covering tests in stryker tap.testFiles ([#13229](https://github.com/diegosouzapw/OmniRoute/pull/13229)) +- **fix(logging):** mask *-api-key headers in request-log pipeline (#13273) ([#13401](https://github.com/diegosouzapw/OmniRoute/pull/13401)) — thanks @KooshaPari +- **fix(tests):** correct stale Jina catalog prefix in custom-models test ([#13403](https://github.com/diegosouzapw/OmniRoute/pull/13403)) — thanks @KooshaPari +- **fix(backend):** prune old backups after health-check-repair snapshots (#13308) ([#13404](https://github.com/diegosouzapw/OmniRoute/pull/13404)) — thanks @KooshaPari +- **fix(backend):** guard memory_fts_au trigger with WHEN clause to stop FTS bloat (#13326) ([#13405](https://github.com/diegosouzapw/OmniRoute/pull/13405)) — thanks @KooshaPari +- **fix(free-tiers):** remove obsolete Together $25 signup credit (#12526) ([#13407](https://github.com/diegosouzapw/OmniRoute/pull/13407)) — thanks @KooshaPari +- **fix:** skip non-chat generation models in Test all models ([#13409](https://github.com/diegosouzapw/OmniRoute/pull/13409)) — thanks @KooshaPari +- **fix(providers):** de-duplicate agy and antigravity model catalogs ([#13410](https://github.com/diegosouzapw/OmniRoute/pull/13410)) — thanks @KooshaPari +- **fix(dashboard):** send provider in body for mini-playground web search ([#13411](https://github.com/diegosouzapw/OmniRoute/pull/13411)) — thanks @KooshaPari +- **fix(resilience):** add CJK quota-exhaustion patterns to 429 classifier ([#13412](https://github.com/diegosouzapw/OmniRoute/pull/13412)) — thanks @KooshaPari +- **fix(backend):** eval runner: failed calls no longer score as passed + dotAll regex ([#13413](https://github.com/diegosouzapw/OmniRoute/pull/13413)) — thanks @KooshaPari +- **fix(backend):** eval runner opts out of output-style and memory injection ([#13414](https://github.com/diegosouzapw/OmniRoute/pull/13414)) — thanks @KooshaPari +- **fix:** chain serial quarantine step in npm test script (fixes #13286) ([#13418](https://github.com/diegosouzapw/OmniRoute/pull/13418)) — thanks @KooshaPari +- **fix(antigravity):** derive stable session ID from credentials for prompt-cache reuse (#13305) ([#13424](https://github.com/diegosouzapw/OmniRoute/pull/13424)) — thanks @KooshaPari +- **fix(memory):** merge memory into top-level system field instead of unshifting at messages[0] (#13425) ([#13427](https://github.com/diegosouzapw/OmniRoute/pull/13427)) — thanks @KooshaPari +- **fix(compression):** preserve instruction blocks from lossy engines ([#13523](https://github.com/diegosouzapw/OmniRoute/pull/13523)) — thanks @KooshaPari +- **fix(compression):** preserve polarity words and newlines in ultra heuristic ([#13524](https://github.com/diegosouzapw/OmniRoute/pull/13524)) — thanks @KooshaPari +- **fix(compression):** run user preservePatterns before built-in patterns (#13457) ([#13525](https://github.com/diegosouzapw/OmniRoute/pull/13525)) — thanks @KooshaPari +- **fix(reasoning):** expand deepseek-v4 regex to match v4.1+ and add command-code provider (#13430) ([#13528](https://github.com/diegosouzapw/OmniRoute/pull/13528)) — thanks @KooshaPari +- **fix(providers):** add alwaysReasons metadata for reka-flash-3 ([#13531](https://github.com/diegosouzapw/OmniRoute/pull/13531)) — thanks @KooshaPari +- **fix(playground):** allow Compare columns to scroll independently ([#13532](https://github.com/diegosouzapw/OmniRoute/pull/13532)) — thanks @KooshaPari +- **fix(security):** empty whitelist allows through to prevent admin lockout ([#13534](https://github.com/diegosouzapw/OmniRoute/pull/13534)) — thanks @KooshaPari +- **fix(tests):** add DISABLE_IOREG_STRATEGY env var for macOS test compatibility (fixes #13467) ([#13539](https://github.com/diegosouzapw/OmniRoute/pull/13539)) — thanks @KooshaPari +- **fix(zed-hosted):** map developer role and Gemini enums for Zed proxy ([#13543](https://github.com/diegosouzapw/OmniRoute/pull/13543)) — thanks @KooshaPari +- **fix(analytics):** normalize provider aliases to canonical id in usage_history ([#13545](https://github.com/diegosouzapw/OmniRoute/pull/13545)) — thanks @KooshaPari +- **fix(combo):** use traceId for call log id to prevent UNIQUE constraint failure ([#13546](https://github.com/diegosouzapw/OmniRoute/pull/13546)) — thanks @KooshaPari +- **fix(auth):** check model lockout before returning synthetic noauth connection ([#13547](https://github.com/diegosouzapw/OmniRoute/pull/13547)) — thanks @KooshaPari +- **fix(guardrails):** skip credential redaction for base64 image data URLs ([#13550](https://github.com/diegosouzapw/OmniRoute/pull/13550)) — thanks @KooshaPari +- **fix(resilience):** increase requestQueue.maxWaitMs default from 15s to 30s ([#13553](https://github.com/diegosouzapw/OmniRoute/pull/13553)) — thanks @KooshaPari +- **fix(docs):** document OMNIROUTE_READY_TIMEOUT_MS and allowlist the test-only DISABLE_IOREG_STRATEGY ([#13692](https://github.com/diegosouzapw/OmniRoute/pull/13692)) + ### 📝 Maintenance ---- +- **docs(readme):** reconcile live v3.8.50 provider, free-tier, CLI, routing, test, community, sponsor, acknowledgment, and SVG metrics with their audited source denominators, including a deduplicated OmniRoute-in-Action snapshot and distinct contributor rankings for merged pull requests, GitHub-attributed commits, and Git history ([#11356](https://github.com/diegosouzapw/OmniRoute/pull/11356)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **docs:** sync the canonical provider count 354 → 356 across `README.md`, `AGENTS.md`, `llm.txt` (+ 42 i18n mirrors), the four README SVG diagrams, `docs/reference/PROVIDER_REFERENCE.md` (regenerated) and the `package.json` description after Opper (#11629) and 1min.ai (#11631) boarded the catalog — closes the `check:docs-counts-sync` strict drifts that kept `release/v3.8.51` red ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11673](https://github.com/diegosouzapw/OmniRoute/pull/11673)) — thanks @jonlwheat2-gif +- **test(providers):** update count-derived assertions after the v3.8.51 provider additions — `APIKEY_PROVIDERS` 233 → 235 (Opper #11629 + 1min.ai #11631), reserved-prefix REGISTRY walk 395 → 398, `WEB_FETCH_PROVIDERS` now includes `nimble-search` (#11620), and the provider translate-path golden snapshot regenerated ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11674](https://github.com/diegosouzapw/OmniRoute/pull/11674)) — thanks @jonlwheat2-gif +- **test(sse):** bump the hard-lease connection-query inventory for `src/lib/tokenHealthCheck.ts` to 2 — the verify-only web-cookie sweep added by #11495 split the single `getProviderConnections` call into oauth + cookie variants, which the frozen inventory had not tracked ([#11495](https://github.com/diegosouzapw/OmniRoute/pull/11495)). ([#11677](https://github.com/diegosouzapw/OmniRoute/pull/11677)) — thanks @jonlwheat2-gif / @oyi77 +- **chore(release):** clear the `release/v3.8.51` base-red gates — docs migration count (+38 i18n mirrors), stryker `tap.testFiles`, lockfile registry host, stale ESLint suppressions, the 7 lint regressions they were hiding, the stale `APIKEY_PROVIDERS` partition count (231 → 233), the `glm-5.3-max` tier missing from the GLM test table, a dead `modelType === "chat"` comparison in the models catalog, and the `resolveLiveWsUrl` / `sanitizeLiveWsPort` helpers from #11388 that a later merge dropped from `wsPath.ts` (dashboard live WebSocket URL resolution) ([#11502](https://github.com/diegosouzapw/OmniRoute/pull/11502)) — thanks @MumuTW +- **chore(free-models):** the free-tier regime table now declares which totals figure each regime feeds and whether it may take the no-auth shortcut, so the sets that used to repeat those answers by hand are derived from it — a new `freeType` no longer compiles until it has answered every question, instead of silently contributing to no total ([#11537](https://github.com/diegosouzapw/OmniRoute/pull/11537)) — thanks @maxmad64bis +- **docs(routing):** LKGP strategy copy now states provider-level stickiness and the explicit rules fallback instead of promising per-target pinning ([#11555](https://github.com/diegosouzapw/OmniRoute/pull/11555)) — thanks @maxmad64bis +- **test(chatcore):** pin the response tool-name alias resolution against the ordering hazard that broke every Gemini/Antigravity MCP tool call in v3.8.49. `extractRequestToolIdentityMap` deletes `translatedBody._toolNameMap`, so the later read is always undefined and the ledger survives only through the `requestToolIdentityMap` fallback — removing that fallback previously left the entire tool-name suite green. The resolution moves into `resolveResponseToolNameMap()` next to the map it depends on, with a regression guard that fails without the recovery. No behaviour change. ([#11575](https://github.com/diegosouzapw/OmniRoute/pull/11575)) — thanks @amartinawi +- **docs(env):** document `OMNIROUTE_CORPUS_CACHE_SIZE` in `.env.example` and `ENVIRONMENT.md`, so the env/docs contract check stops failing Docs Gates on every branch ([#11576](https://github.com/diegosouzapw/OmniRoute/pull/11576)) — thanks @ntdatt812 +- **chore(stryker):** register `route-guard-tunnel-processes-local-only` and `universal-quota-aware-routing` in `tap.testFiles`, so their mutant kills count for the three modules they cover ([#11577](https://github.com/diegosouzapw/OmniRoute/pull/11577)) — thanks @ntdatt812 +- **test(authz):** pin `LOCAL_ONLY_API_GET_EXEMPTIONS` by exact membership instead of by entry count, so the guard names the offending path and also catches a substitution ([#11580](https://github.com/diegosouzapw/OmniRoute/pull/11580)) — thanks @ntdatt812 +- **test(build):** pin `resolveNextBuildBundlerFlag` to the `OMNIROUTE_USE_TURBOPACK` contract it implements, across runtimes, instead of asserting an unimplemented Bun override ([#11583](https://github.com/diegosouzapw/OmniRoute/pull/11583)) — thanks @ntdatt812 +- **test(claude-web):** the Claude Web transport test's barrel anchors no longer pin the exact construction spelling. #11421 made the executor map lazy, so `/"claude-web": new ClaudeWebExecutor\(\)/` stopped matching and the test went red on `release/v3.8.51` itself — a base-red gate every open PR inherited. The anchors now match the alias key and the class name and tolerate either spelling in between, and were checked to still fail when an alias is genuinely repointed at another executor ([#11585](https://github.com/diegosouzapw/OmniRoute/pull/11585)) — thanks @ntdat812 +- **test(models):** pin `qwen3.8-max` resolution per provider — two of the four catalogs now serve the bare id and no longer carry `-preview` ([#11590](https://github.com/diegosouzapw/OmniRoute/pull/11590)) — thanks @ntdatt812 +- **test(executors):** drop the stale 128-tool truncation assertion that contradicted the #11444 pass-through contract ([#11591](https://github.com/diegosouzapw/OmniRoute/pull/11591)) — thanks @ntdatt812 +- **test(token-health):** pin the current terminal-skip boundary — expired with retry budget is probed, `account_deactivated` stays skipped ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)) — thanks @ntdatt812 +- **test(models):** follow #11503's retarget of the `gemini-3-pro-high` alias to the hyphenated catalog id ([#11593](https://github.com/diegosouzapw/OmniRoute/pull/11593)) — thanks @ntdatt812 +- **refactor(video bridge):** extract per-video acquisition, whole-result caching, description, and metrics/abort/cleanup out of `VideoBridgeGuardrail.preCall` into a `processVideoPart` seam in a new `videoBridgePipeline.ts`, behind explicit `VideoMediaBrokerPort`, `VideoAudioTranscriptionPort`, and `VideoDrilldownPort` boundaries; `preCall` now only handles request traversal, policy, and response aggregation. The Video tab's FFmpeg/ffprobe runtime status is now an explicit `unknown` / `restricted` / `unavailable` / `available` state instead of a nullable boolean pair, fixing a case where an in-flight or failed probe was mislabeled as "install FFmpeg" ([#11657](https://github.com/diegosouzapw/OmniRoute/issues/11657)). ([#12016](https://github.com/diegosouzapw/OmniRoute/pull/12016)) +- **test(opencode-plugin):** add unit test coverage for `formatFreeBudget()` naming helper ([#11660](https://github.com/diegosouzapw/OmniRoute/pull/11660)) — thanks @f9td56dbgh-hub +- **docs(video):** clarify that the Video Bridge transcript `source` field (`client`, `embedded`, `audio-bridge`) is presently caller-declared and not yet server-verified — OmniRoute enforces the enum shape but does not cryptographically confirm that an `embedded`/`audio-bridge` label came from a server-owned extraction ([#11661](https://github.com/diegosouzapw/OmniRoute/issues/11661)). ([#12001](https://github.com/diegosouzapw/OmniRoute/pull/12001)) +- **chore(providers):** retire the distributed Raycast Relay and Hailuo Web integrations after a provenance audit confirmed substantial derivation from GPL-3.0 sources; the official GitHub and MiniMax API providers remain available. Migration 163 disables legacy connections and invalidates their active leases while preserving connection IDs, audit/history rows, quota snapshots, usage records, and fail-closed API-key connection policies. ([#11691](https://github.com/diegosouzapw/OmniRoute/pull/11691)) +- **chore(providers):** retire the Felo Web provider while its GPL-derived provenance remains on licensing HOLD ([#11698](https://github.com/diegosouzapw/OmniRoute/pull/11698)) — stale runtime and database references now fail closed while existing connection, lease, and usage history remains preserved for auditability. +- **fix(sse):** retire the Gemini Web image-generation bridge while provenance and licensing remain on HOLD, preserving Gemini Web chat/auth/tools and legitimate Gemini/Nano Banana image providers ([#11708](https://github.com/diegosouzapw/OmniRoute/pull/11708)) +- **chore(audio):** retire the built-in EdgeTTS provider and its 11-voice catalog while provenance/licensing review remains on HOLD; the generic `/v1/audio/speech` endpoint and the other speech providers remain available ([#11711](https://github.com/diegosouzapw/OmniRoute/pull/11711)) +- **fix(providers):** retire the unofficial Qwen Web integration and its `qwen-web`/`qw` identities while third-party provenance remains on HOLD ([#11713](https://github.com/diegosouzapw/OmniRoute/pull/11713)) — direct requests now fail closed with a sanitized HTTP 410 and combos skip the retired target, while migration guards preserve connection IDs, allowlists, usage/audit history and invalidate active leases. Supported Qwen API, Qwen Code, cloud, token-plan, audio, local and model-name surfaces remain available; restoring Qwen Web requires a qualified provenance/license decision. +- **fix(providers):** retire `microsoft-designer-web` and its `msdesigner` alias from runtime and catalogs while preserving stored credentials, provider history and allowlists as inactive records — source dependency on gpt4free's [`MicrosoftDesigner.py`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/g4f/Provider/needs_auth/MicrosoftDesigner.py) is proven at snapshot `0943d16eef757a44deb60651c07bbe5fa958cafe`, whose [`LICENSE`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/LICENSE) is GPL-3.0; expressive adaptation is probable, which records provenance/licensing risk without asserting a legal conclusion ([#11720](https://github.com/diegosouzapw/OmniRoute/pull/11720)). +- **docs(legal):** centralize pinned MIT notices for the vendored GCF codec and local flag assets ([#11726](https://github.com/diegosouzapw/OmniRoute/pull/11726)) +- Split the 2,952-line `adobeFireflyClient.ts` into nine focused modules (catalog, credentials, payload, ARP session, upload, responses, IMS, account, poll) behind an unchanged barrel — no consumer edits and no public API change (#11728). — thanks @MumuTW +- **fix(dashboard):** replaced the Model Lockout card's unprovenanced notification MP3 with a short, locally synthesized Web Audio chime ([#11731](https://github.com/diegosouzapw/OmniRoute/pull/11731)) — both toggles retain optional audible feedback without shipping replacement media or third-party code, and unsupported, suspended, or rejected audio contexts fail open without blocking the setting change. +- **chore(deepseek-web):** remove the provenance-unresolved DeepSeek PoW WASM binary and its runtime loader/tracing while retaining the existing JavaScript solver (slower at high difficulty) ([#11732](https://github.com/diegosouzapw/OmniRoute/pull/11732)). +- **chore(assets):** remove four unused provider assets whose redistribution provenance could not be verified while retaining the active Freebuff SVG ([#11735](https://github.com/diegosouzapw/OmniRoute/pull/11735)) +- **docs(assets):** retired two raster images whose redistribution provenance could not be verified ([#11736](https://github.com/diegosouzapw/OmniRoute/pull/11736)) — PicoClaw documentation now reuses the existing SVG, while Zoo Code remains a text link without an image. +- **docs(legal):** add pinned notices for six LobeHub-derived provider SVGs and 65 byte-exact theSVG assets, retaining registry claims as unverified metadata with explicit trademark and HOLD boundaries ([#11737](https://github.com/diegosouzapw/OmniRoute/pull/11737)). +- **chore(quality):** added a versioned 225/225 provider-asset provenance manifest and blocking drift gate ([#11743](https://github.com/diegosouzapw/OmniRoute/pull/11743)) — the inventory pins magic MIME and SHA-256 for every physical file, records immutable source matches for 71 assets, keeps 69 probable and 85 unresolved entries explicit but non-blocking, and treats provenance as source evidence rather than copyright or trademark clearance. +- **refactor(mitm):** replace four previously derived Traffic Inspector utilities — conversation normalization, SSE merging, secret masking, and header sanitization — with independent clean-room implementations based on public provider schemas, WHATWG SSE, and RFC 6750, while keeping the two independently classified passthrough surfaces unchanged ([#11748](https://github.com/diegosouzapw/OmniRoute/pull/11748)). +- Added a unit test that fails seven days before any dated pack under `config/` (`validUntil` and sibling keys) lapses, naming the file and key. The Alibaba free-tier pack expired on 2026-08-27 and turned every PR red the next morning with no commit involved; renewal now happens on someone's terms, not the clock's. ([#11891](https://github.com/diegosouzapw/OmniRoute/pull/11891)) +- `check:workflows` now fails (under `--strict`/`--ratchet`) when any job routed to a self-hosted runner publishes with `--provenance` — npm rejects that with `422` at the registry, which in v3.8.50 only surfaced after the tag and Docker images were public. ([#11895](https://github.com/diegosouzapw/OmniRoute/pull/11895)) +- `scripts/ops/runner-janitor.sh` now proves a path is idle with one `lsof` snapshot and removes stale leftovers itself (tmpfs after 3 h — it is RAM — disk after 24 h), kills orphan `next-build` processes, prunes checkouts of stopped runners, and alerts on memory pressure; `--dry-run` shows exactly what it would do. `docs/ops/RUNNER_BOX.md` reconciled to the measured box (31 GB, 10 listeners). ([#11893](https://github.com/diegosouzapw/OmniRoute/pull/11893)) +- The `next-build` artefact (1.3 GB) is now written and read under `$RUNNER_TEMP` (per-runner, on disk) instead of `/tmp`, which on the self-hosted pool is a 12 GB tmpfs in RAM. Landing it there took 27–32 of the publish job's 76 minutes, and the fixed `/tmp/e2e-build.tar.gz` name let E2E jobs on different runners overwrite each other's download. ([#11896](https://github.com/diegosouzapw/OmniRoute/pull/11896)) +- The CI `build` job now runs in two concurrency lanes — `main` and pull requests — so a release build is never queued behind (or OOM-killed beside) PR builds on the self-hosted pool, which holds one `next-build` comfortably and two at the edge. ([#11901](https://github.com/diegosouzapw/OmniRoute/pull/11901)) +- Re-freeze the ESLint suppressions on `release/v3.8.51` from a clean-room measurement (2 stale file entries pruned, 55 pre-existing `no-explicit-any` in six new files frozen under #11924) and drop the dead `GPT_SIZE_MAP` constant orphaned by the Adobe Firefly client split, so `No new ESLint warnings` stops failing every PR with exit 2 (Refs #11924) ([#11955](https://github.com/diegosouzapw/OmniRoute/pull/11955)) +- Type the 55 `no-explicit-any` sites that had been frozen under #11924 — four redundant casts in `socksConnectorWithFamily.ts` (undici/socks types already accept them) and the mocks/fixtures of the socks-timeout and isFree suites — and drop their suppression entries; the ESLint ratchet shrinks from 5487 to 5432 (Closes #11924) ([#11975](https://github.com/diegosouzapw/OmniRoute/pull/11975)) +- Take the two hosted-runner builds off the PR rail: `Build App` (`build.yml`) is `workflow_dispatch`-only and `DAST smoke (PR)` runs only for PRs into `main` — the 7 GB hosted VM cannot build `release/v3.8.51` in any profile (19/30 red, VM shutdown ~8 min into `next build`) and both checks had turned into permanent noise on every release PR; the bundle stays validated by `ci.yml` on `main` and by `nightly-release-green` on `release/**` (Closes #11946) ([#11962](https://github.com/diegosouzapw/OmniRoute/pull/11962)) +- Move the four nightly jobs that build the backend (`nightly-schemathesis`, `nightly-llm-security` promptfoo + garak, `nightly-resilience` axe-a11y) off the hosted 7 GB runner — where they died on `release/v3.8.51` unseen — onto the box's new `omni-light` pool (two listeners), and document the reshaped fleet (4 active OmniRoute listeners: 2 `omni-build` + 2 `omni-light`, janitor ceiling 4) (Closes #11965) ([#11967](https://github.com/diegosouzapw/OmniRoute/pull/11967)) +- Stop painting every fork PR into `release/**` red: `quality.yml` `Build (advisory)` is skipped (GitHub still reports `continue-on-error` failures as check FAILURE). Hosted `ubuntu-latest` cannot finish `npm run build` on this tree — same class as #11962 taking `build.yml` off the PR rail. `docker-publish.yml` amd64 now runs on the `.113` `omni-build` pool (31 GB / 32 cores, two listeners) with Turbopack, shares the `heavy-build-main` lane with `ci.yml` `Build` so it queues instead of becoming a third heavy, and keeps per-ref concurrency (a merge storm was starting 8 concurrent OOM builds). arm64 stays on `ubuntu-24.04-arm` with webpack — there is no ARM box. Fallback when `USE_VPS_RUNNER` is off: hosted amd64 + webpack (#11976). ([#12021](https://github.com/diegosouzapw/OmniRoute/pull/12021)) +- `docker-publish.yml` builds with webpack (`OMNIROUTE_USE_TURBOPACK=0`) on **both** arches, including amd64 on the `.113` `omni-build` pool. Turbopack had the RAM (31 GB, `omniroute-113-6`) and still panicked (`TurbopackInternalError: there must be a path to a root`, run 33253576569, 26 min in). The same tree's arm64 webpack build on hosted `ubuntu-24.04-arm` succeeded (run 33264823398). Dockerfile already documents webpack as the Docker escape hatch (#11976). ([#12050](https://github.com/diegosouzapw/OmniRoute/pull/12050)) +- **docs(free-tier):** declare the counting vs deciding regimes for "is it free?" and guard the deciding path from DB-backed catalog resolution ([#12226](https://github.com/diegosouzapw/OmniRoute/pull/12226)) — thanks @maxmad64bis +- **docs(auto-combo):** The mode pack table in `docs/routing/AUTO-COMBO.md` now lists all six shipped packs with every weight each one sets, replacing a four-pack table whose numbers had also drifted from the source. It states plainly that no pack sets `quality`, so selecting any pack silences the observed-quality signal. Six more documents that quote the scoring factor count joined the `check:docs-counts` gate, which caught five stale claims — including one naming nine factors that do not exist — and two stale mode pack counts. The dashboard routing panel, which offered four of the six packs and labelled the default strategy "6-Factor Scoring", is now covered by a test; the two packs it was missing are `reliability-first` and `chaos-mode`, the latter labelled as the fault-injection profile it is rather than as one more routing preference ([#12316](https://github.com/diegosouzapw/OmniRoute/pull/12316)) — thanks @maxmad64bis +- **chore(quality):** rebaseline `src/lib/db/apiKeys.ts` for the ACL the key-creation path now preserves ([#12352](https://github.com/diegosouzapw/OmniRoute/pull/12352)) ([#12673](https://github.com/diegosouzapw/OmniRoute/pull/12673)) — thanks @KrzysiekSko +- **chore(providers):** bump the Claude Code wire identity and the Devin bridge image pin from `2.1.220` to `2.1.258` ([#12402](https://github.com/diegosouzapw/OmniRoute/pull/12402)) — thanks @ggiak ([#12604](https://github.com/diegosouzapw/OmniRoute/pull/12604)) +- **docs(env):** align `.env.example`, the README Bun section, and the troubleshooting guide with the code: `OMNIROUTE_USE_TURBOPACK` also governs `npm run build` (not dev-only), `bun run build` follows that flag instead of auto-selecting Webpack, `OMNIROUTE_CHAT_MAX_HEAVY_IN_FLIGHT` is unset by default (no request-count cap), and the structural `503 chat_admission_busy` message matches `chatAdmissionResponses.ts` (#12404 — thanks @pacocartones) +- **chore(electron):** upgrade the desktop app to Electron 44 (Chromium 152, Node 24.18.1) ([#12217](https://github.com/diegosouzapw/OmniRoute/pull/12217)). **Requires macOS 13 (Ventura) or later** — Chromium dropped macOS 12 (Monterey), so Monterey users must stay on an earlier OmniRoute desktop build. Windows and Linux are unaffected; the app already shipped only x64/arm64, so Electron 44 dropping 32-bit builds changes nothing. Removes the `openAsHidden`/`wasOpenedAsHidden` login-item fields deleted in Electron 44 — hidden autostart continues to work through the `--hidden` argument registered with the login item ([#12554](https://github.com/diegosouzapw/OmniRoute/pull/12554)) — thanks @app/dependabot +- **chore(quality):** rebaseline `src/sse/handlers/chat.ts` for the effective-input persistence the continuation fix needs ([#12641](https://github.com/diegosouzapw/OmniRoute/pull/12641)) ([#12680](https://github.com/diegosouzapw/OmniRoute/pull/12680)) — thanks @hartmark +- `Coverage` job on `ci.yml`: the informational Codecov upload gets its own 5-minute ceiling and `continue-on-error`, and the job budget grows from 20 to 30 minutes (the 8-shard c8 merge alone takes ~10) — a stalled upload no longer ends the job `cancelled` and drags a fully green `main` run's conclusion down with it ([#11972](https://github.com/diegosouzapw/OmniRoute/pull/11972)) +- CI hardening on the PR rail: the four `Unit Tests fast-path` shards get `timeout-minutes: 30` (a hung shard held a PR for 64 min instead of GitHub's 6 h default) and both ESLint file caches lose their `restore-keys` fallback, so a cache built under another suppressions file or lint config can no longer report stale verdicts (Refs #11600, #11924) ([#11963](https://github.com/diegosouzapw/OmniRoute/pull/11963)) +- Every CI job that runs a `next build` (`build`, the npm `publish`, both release-green validations) now targets the `omni-build` runner label, which only two of the eight self-hosted runners carry. The box holds one build comfortably and two at the edge; a third now queues on GitHub instead of being OOM-killed by the kernel. ([#11932](https://github.com/diegosouzapw/OmniRoute/pull/11932)) +- **refactor(combo):** move `handleRoundRobinCombo` (and `resolveTargetTokenLimit`) into `open-sse/services/combo/roundRobinCombo.ts`. `combo.ts` drops from 2164 to 1014 lines (`split("\n").length`); the leaf is 1199 (under the 1200 new-file cap). The round-robin call site uses a dynamic `import()` so `releaseStickyPinOnFailure` / `clearStaleLKGP` can stay exported from `combo.ts` without a static cycle. Skip / sticky / semaphore / safety-timer behavior is unchanged. ([#12811](https://github.com/diegosouzapw/OmniRoute/pull/12811)) — thanks @HouMinXi +- **chore(quality):** rebaseline the file-size caps the error-boundary campaign grew past (`open-sse/executors/codex.ts`, `open-sse/vendor/codex-chatgpt-web/bridge.ts`, both via [#12444](https://github.com/diegosouzapw/OmniRoute/pull/12444)) ([#12654](https://github.com/diegosouzapw/OmniRoute/pull/12654)) +- **chore(quality):** rebaseline the file-size caps the hartmark batch grew past (`combos/page.tsx` via [#12355](https://github.com/diegosouzapw/OmniRoute/pull/12355), `open-sse/services/combo.ts` via [#12338](https://github.com/diegosouzapw/OmniRoute/pull/12338)) ([#12623](https://github.com/diegosouzapw/OmniRoute/pull/12623)) — thanks @hartmark +- **chore(quality):** rebaseline the file-size caps the HouMinXi batch grew past when its PRs stacked (`providers/page.tsx`, `chatCore.ts`, `accountFallback.ts`) — each PR measured correctly in isolation, none saw the stacking ([#12619](https://github.com/diegosouzapw/OmniRoute/pull/12619)) +- **chore(quality):** rebaseline `open-sse/services/combo.ts` for the reset-aware scoring the HouMinXi batch stacked ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) ([#12678](https://github.com/diegosouzapw/OmniRoute/pull/12678)) — thanks @HouMinXi +- **docs:** sync provider catalog count (direct commit `4f11b2ae3d`) +- **docs(skills):** sync omni-inference search fallback (direct commit `b4e76a7ed9`) +- **chore(lint):** prune two stale base suppressions (direct commit `1325047d56`) +- **test(auto-combo):** tolerate full-suite rotation load (direct commit `ad3e293f9f`) +- **test:** drain inherited quality snapshot reds (direct commit `21ed68d8ac`) +- **chore(quality):** rebaseline measured CLI bundle growth (direct commit `d35b3f9779`) +- **test(ui):** reproduce invalid AnySearch icon alias (direct commit `12cc6ca834`) +- **test(translator):** fix the relative imports of the relocated deferred-finish test (direct commit `777d9d1629`) +- **docs(counts):** bring the documented provider and migration counts to the measured values (direct commit `e70bea30e9`) +- **test(security):** drop the guard-target set left unused by the sync (direct commit `a32ba0e362`) +- **chore(db):** renumber model_capabilities migration 163 → 169 (cross-branch collision with 163_radar_feed_cache_generated_at) (direct commit `70f5d4cbf9`) +- **docs(cli):** document CLI_PRIME_AGENT_BIN and correct the CLI Agents count (direct commit `9058e39b61`) +- **chore(deps):** refresh runtimes and adopt ESLint 10 ([#11259](https://github.com/diegosouzapw/OmniRoute/pull/11259)) — thanks @backryun +- **refactor(local-corpus):** implement dynamic root resolution and LRU cache ([#11491](https://github.com/diegosouzapw/OmniRoute/pull/11491)) — thanks @raheemuddin786 +- **chore(quality):** prune stale eslint-suppressions entries ([#11596](https://github.com/diegosouzapw/OmniRoute/pull/11596)) +- **chore(ci):** unblock quality gates - env-doc sync + stryker tap.testFiles registration (#9282) ([#11609](https://github.com/diegosouzapw/OmniRoute/pull/11609)) — thanks @Meet6338-X +- **chore(ci):** point the circular-deps gate at dpdm's real JS entrypoint ([#11615](https://github.com/diegosouzapw/OmniRoute/pull/11615)) — thanks @benzntech +- **chore(quality):** rebaseline file-size for #11448/#11495/#11561 growth ([#11630](https://github.com/diegosouzapw/OmniRoute/pull/11630)) +- **deps:** realign onnxruntime-node pin to @huggingface/transformers' 1.24.3 lockstep ([#11633](https://github.com/diegosouzapw/OmniRoute/pull/11633)) — thanks @jonlwheat2-gif +- **test(autoCombo):** port models_dev_tier #11508 guard from node:test to vitest ([#11635](https://github.com/diegosouzapw/OmniRoute/pull/11635)) — thanks @jonlwheat2-gif +- **chore(quality):** rebaseline file-size for #11083/#11631/#11628 growth ([#11636](https://github.com/diegosouzapw/OmniRoute/pull/11636)) +- **chore(lint):** prune stale ESLint suppressions (base-red #11449) ([#11644](https://github.com/diegosouzapw/OmniRoute/pull/11644)) — thanks @jonlwheat2-gif +- **docs(changelog):** aggregate the ten v3.8.50 fragments into CHANGELOG.md ([#11683](https://github.com/diegosouzapw/OmniRoute/pull/11683)) +- **chore(search):** drop unreachable credential sweep after #11642 ([#11687](https://github.com/diegosouzapw/OmniRoute/pull/11687)) — thanks @HouMinXi +- **ci:** add API route TypeScript regression gate ([#11705](https://github.com/diegosouzapw/OmniRoute/pull/11705)) — thanks @Sabeekhann +- **docs(dedup):** correct the CodeQL claim on the tenant-namespace comment ([#11712](https://github.com/diegosouzapw/OmniRoute/pull/11712)) +- **docs(changelog):** add consolidated v3.8.50 stats, top-25 ranking and a .mailmap ([#11715](https://github.com/diegosouzapw/OmniRoute/pull/11715)) +- **docs:** recommend gstack for AI-assisted workflows ([#11770](https://github.com/diegosouzapw/OmniRoute/pull/11770)) — thanks @santosraju99-hub +- **chore(quality):** tighten complexity/cognitive-complexity ratchets to the current tip; land the missed gateways.ts rebaseline ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **docs(i18n):** finish Freepik → Magnific rebrand in locale strings and README ([#11772](https://github.com/diegosouzapw/OmniRoute/pull/11772)) — thanks @turbolego +- **refactor(auto-combo):** fix divergent scoring in combo health reporting ([#11854](https://github.com/diegosouzapw/OmniRoute/pull/11854)) — thanks @jacobsparts +- **chore(changelog):** add missing fragments for #11919/#11918/#11916 ([#11938](https://github.com/diegosouzapw/OmniRoute/pull/11938)) +- **test(check):** escape the runs-on fixture with JSON.stringify, not a quote-only replace ([#11942](https://github.com/diegosouzapw/OmniRoute/pull/11942)) (direct commit `fb7445eaa3`) +- **test(release):** align five suites with the contracts #11933, #11919 and #11876 shipped on release/v3.8.51 ([#11944](https://github.com/diegosouzapw/OmniRoute/pull/11944)) +- **chore(quality):** remove unreachable code and restore test discovery ([#11950](https://github.com/diegosouzapw/OmniRoute/pull/11950)) — thanks @backryun +- **docs(agents):** sync-back landings are fast-forward, never squash ([#11964](https://github.com/diegosouzapw/OmniRoute/pull/11964)) +- **test(infra):** retry recursive temp-dir removal instead of failing a shard on ENOTEMPTY (#11966) ([#11968](https://github.com/diegosouzapw/OmniRoute/pull/11968)) +- **docs(ops):** the .113 heavy-build ceiling is one runner, not two ([#12048](https://github.com/diegosouzapw/OmniRoute/pull/12048)) +- **test(ui):** correct inactive auto-fetch expectation ([#12098](https://github.com/diegosouzapw/OmniRoute/pull/12098)) — thanks @RaviTharuma +- **docs:** document native dependency check escape hatch ([#12101](https://github.com/diegosouzapw/OmniRoute/pull/12101)) — thanks @RaviTharuma +- **test(providers):** regenerate the translate-path golden for OrcaRouter (#11923) ([#12118](https://github.com/diegosouzapw/OmniRoute/pull/12118)) +- **chore(quality):** velocity phase — loosen every numeric baseline by 20% until v4.0, monitor headroom nightly ([#12125](https://github.com/diegosouzapw/OmniRoute/pull/12125)) +- **chore(lint):** batch 0 of #12146 — type the call-log-cap sqlite rows instead of 45 as-any casts ([#12157](https://github.com/diegosouzapw/OmniRoute/pull/12157)) +- **chore(lint):** batch 4 of #12146 — shared/components react-hooks violations resolved ([#12159](https://github.com/diegosouzapw/OmniRoute/pull/12159)) +- **chore(lint):** batch 1 of #12146 — dashboard/cli-code react-hooks violations resolved ([#12160](https://github.com/diegosouzapw/OmniRoute/pull/12160)) +- **chore(lint):** batch 3 of #12146 — dashboard/settings react-hooks violations resolved ([#12162](https://github.com/diegosouzapw/OmniRoute/pull/12162)) +- **chore(lint):** batch 2 of #12146 — resolve the react-hooks compiler violations in dashboard/providers ([#12163](https://github.com/diegosouzapw/OmniRoute/pull/12163)) +- **chore(quality):** register search-432 cooldown test in stryker tap.testFiles ([#12170](https://github.com/diegosouzapw/OmniRoute/pull/12170)) +- **chore(lint):** batch 5 of #12146 — combos, endpoint, provider-stats, api-manager and costs react-hooks violations resolved ([#12174](https://github.com/diegosouzapw/OmniRoute/pull/12174)) +- **build:** add contributor fast profile ([#12192](https://github.com/diegosouzapw/OmniRoute/pull/12192)) — thanks @rafacpti23 +- **docs(readme):** README touch-ups ([#12193](https://github.com/diegosouzapw/OmniRoute/pull/12193)) ([#12194](https://github.com/diegosouzapw/OmniRoute/pull/12194)) ([#12202](https://github.com/diegosouzapw/OmniRoute/pull/12202)) — thanks @rafacpti23 +- **build:** force the webpack bundler in the contributor build profile ([#12197](https://github.com/diegosouzapw/OmniRoute/pull/12197)) — thanks @rafacpti23 +- **build:** skip standalone packaging in the contributor profile ([#12198](https://github.com/diegosouzapw/OmniRoute/pull/12198)) — thanks @rafacpti23 +- **docs(audit):** align every published number with the code and harden check:docs-counts ([#12200](https://github.com/diegosouzapw/OmniRoute/pull/12200)) +- **build:** stub the instrumentation entrypoints in the contributor profile ([#12203](https://github.com/diegosouzapw/OmniRoute/pull/12203)) — thanks @rafacpti23 +- **build:** omit the standalone output target for contributor builds ([#12204](https://github.com/diegosouzapw/OmniRoute/pull/12204)) — thanks @rafacpti23 +- **chore(lint):** batch 6 of #12146 — memory, radar, audit, analytics, cache, usage, activity, home and RequestLoggerV2 react-hooks violations resolved ([#12208](https://github.com/diegosouzapw/OmniRoute/pull/12208)) +- **docs(diagrams):** rename number-carrying diagram files to stable names ([#12210](https://github.com/diegosouzapw/OmniRoute/pull/12210)) +- **docs(dashboard):** redraw onboarding tier-flow SVGs for the real 4-tier model ([#12211](https://github.com/diegosouzapw/OmniRoute/pull/12211)) +- **docs(api):** document every implemented route in openapi.yaml (276 → 692 paths) ([#12212](https://github.com/diegosouzapw/OmniRoute/pull/12212)) +- **chore(lint):** batch 7 of #12146 — final src tail: 37 react-hooks violations across 33 files resolved ([#12244](https://github.com/diegosouzapw/OmniRoute/pull/12244)) +- Revert "docs: recommend gstack for AI-assisted workflows (#11770)" ([#12248](https://github.com/diegosouzapw/OmniRoute/pull/12248)) ([#12249](https://github.com/diegosouzapw/OmniRoute/pull/12249)) +- **docs:** Chaos Mode setup guide + weighted strategy semantics ([#12250](https://github.com/diegosouzapw/OmniRoute/pull/12250)) +- **docs(agents):** protected-surface merge rule — operator approval for agent-instruction files ([#12253](https://github.com/diegosouzapw/OmniRoute/pull/12253)) +- **chore(quality):** register native-codex turn-pin tests in stryker tap.testFiles ([#12263](https://github.com/diegosouzapw/OmniRoute/pull/12263)) +- **chore(quality):** dedupe tap.testFiles entries added by racing base-red fixes ([#12265](https://github.com/diegosouzapw/OmniRoute/pull/12265)) +- **docs(readme):** introduce OmniRouteTray — the macOS menu-bar companion ([#12276](https://github.com/diegosouzapw/OmniRoute/pull/12276)) — thanks @ggiak +- **chore(proxy):** purge the legacy 1proxy residue — sync/rotator modules, dead DB exports, dead settings tab and flag, docs (#12091) ([#12290](https://github.com/diegosouzapw/OmniRoute/pull/12290)) +- **chore(quality):** baseline-headroom skips generated and vendored files in the fileSize worst-file signal ([#12291](https://github.com/diegosouzapw/OmniRoute/pull/12291)) +- **refactor(video):** unify the JPEG frame data-URI contract ([#12322](https://github.com/diegosouzapw/OmniRoute/pull/12322)) +- **chore(deps):** freeze onnxruntime-node and eslint-plugin-react-hooks out of dependabot groups ([#12329](https://github.com/diegosouzapw/OmniRoute/pull/12329)) +- **test(executors):** restore the #10986 reasoning-only fallback guards ([#12364](https://github.com/diegosouzapw/OmniRoute/pull/12364)) — thanks @pacocartones +- **chore(quality):** re-tighten the file-size ratchet to the real LOC (plan 3.8.52 task 0) ([#12411](https://github.com/diegosouzapw/OmniRoute/pull/12411)) +- **test(grok):** format web executor suite ([#12412](https://github.com/diegosouzapw/OmniRoute/pull/12412)) +- **chore(quality):** ratchet open-sse typecheck baseline to zero ([#12418](https://github.com/diegosouzapw/OmniRoute/pull/12418)) +- **test(build):** guard the artifact path policy arrays against duplicates ([#12422](https://github.com/diegosouzapw/OmniRoute/pull/12422)) +- **chore(lint):** adopt eslint-plugin-react-hooks 7.1.1 ([#12428](https://github.com/diegosouzapw/OmniRoute/pull/12428)) +- **chore(providers):** remove a keyless provider integration at its operator's request ([#12440](https://github.com/diegosouzapw/OmniRoute/pull/12440)) +- **docs(providers):** register providers removed at their operator's request and guard against reintroduction ([#12478](https://github.com/diegosouzapw/OmniRoute/pull/12478)) +- **chore(quality):** base fixes — stryker tap.testFiles + node_modules cache key ([#12482](https://github.com/diegosouzapw/OmniRoute/pull/12482)) +- **docs(arch):** one-process recipe for tens of long /v1/responses ([#12493](https://github.com/diegosouzapw/OmniRoute/pull/12493)) — thanks @RaviTharuma +- **chore(quality):** tighten the CodeQL ratchet baseline from 11 to 6 ([#12530](https://github.com/diegosouzapw/OmniRoute/pull/12530)) +- **chore(deps):** pin browserslist override to ^4.28.8 ([#12592](https://github.com/diegosouzapw/OmniRoute/pull/12592)) — thanks @KooshaPari +- **chore(deps):** bump fast-uri to 3.1.7 in the electron lockfile ([#12601](https://github.com/diegosouzapw/OmniRoute/pull/12601)) +- **docs(quality):** document how the CodeQL ratchet refreshes and how to tighten it ([#12611](https://github.com/diegosouzapw/OmniRoute/pull/12611)) +- **test(ci):** pin the openapi-security-tiers two-arm contract with an executing gate test (#12581) ([#12652](https://github.com/diegosouzapw/OmniRoute/pull/12652)) +- **docs(gamification):** align XP Rewards table with code (#12501) ([#12667](https://github.com/diegosouzapw/OmniRoute/pull/12667)) — thanks @KooshaPari +- **refactor(combo):** split executeTarget into gates, attempt, and loop ([#12746](https://github.com/diegosouzapw/OmniRoute/pull/12746)) — thanks @HouMinXi +- **docs(security):** document socket.yml scanner config + CI workflow link (#12575) ([#12764](https://github.com/diegosouzapw/OmniRoute/pull/12764)) — thanks @KooshaPari +- **chore(ci):** guard commit identity in pre-commit to stop author misattribution ([#12772](https://github.com/diegosouzapw/OmniRoute/pull/12772)) +- **deps:** 17 Dependabot bumps — bump electron from 43.4.0 to 43.4.1 in /electron ([#11426](https://github.com/diegosouzapw/OmniRoute/pull/11426)); bump the development group across 1 directory with 8 updates ([#11428](https://github.com/diegosouzapw/OmniRoute/pull/11428)); bump the production group across 1 directory with 11 updates ([#11440](https://github.com/diegosouzapw/OmniRoute/pull/11440)); chore(deps): bump github/codeql-action/init from 4.37.7 to 4.37.8 ([#11925](https://github.com/diegosouzapw/OmniRoute/pull/11925)); chore(deps): bump github/codeql-action from 4.37.7 to 4.37.8 ([#11926](https://github.com/diegosouzapw/OmniRoute/pull/11926)); chore(deps): bump github/codeql-action/analyze from 4.37.7 to 4.37.8 ([#11927](https://github.com/diegosouzapw/OmniRoute/pull/11927)); build(deps): bump github/codeql-action/init from 4.37.8 to 4.37.9 ([#12345](https://github.com/diegosouzapw/OmniRoute/pull/12345)); build(deps): bump github/codeql-action/analyze from 4.37.8 to 4.37.9 ([#12346](https://github.com/diegosouzapw/OmniRoute/pull/12346)); bump the development group across 1 directory with 2 updates ([#12347](https://github.com/diegosouzapw/OmniRoute/pull/12347)); build(deps): bump github/codeql-action from 4.37.8 to 4.37.9 ([#12349](https://github.com/diegosouzapw/OmniRoute/pull/12349)); bump browserslist from 4.28.2 to 4.28.8 ([#12396](https://github.com/diegosouzapw/OmniRoute/pull/12396)); bump the production group across 1 directory with 4 updates ([#12399](https://github.com/diegosouzapw/OmniRoute/pull/12399)); bump @xmldom/xmldom ([#12500](https://github.com/diegosouzapw/OmniRoute/pull/12500)); bump qs from 6.15.2 to 6.16.0 ([#12512](https://github.com/diegosouzapw/OmniRoute/pull/12512)); bump @xmldom/xmldom from 0.9.10 to 0.9.12 ([#12513](https://github.com/diegosouzapw/OmniRoute/pull/12513)); bump fast-uri from 3.1.5 to 3.1.7 ([#12514](https://github.com/diegosouzapw/OmniRoute/pull/12514)); bump @humanfs/node from 0.16.7 to 0.16.8 ([#12515](https://github.com/diegosouzapw/OmniRoute/pull/12515)) +- Clear the `release/v3.8.51` `check:agent-skills-sync` base-red: regenerate `skills/cli-tunnel/SKILL.md` so the `tunnel create [type]` positional that #13009 taught the generator to read is reflected in the committed skill. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` typecheck base-red from #12731: the new `Mode pack ranking gates` candidates in `open-sse/services/autoCombo/__tests__/autoCombo.test.ts` omitted the required `provider`, `model` and `errorRate` fields of `ProviderCandidate`, failing `check:open-sse-typecheck` (and with it `Fast Quality Gates`) on every open PR. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` docs-sync base-red: EURouter (#13025) and GreenPT (#13024) took the live provider count to 358, leaving 7 STRICT drifts (`PROVIDER_REFERENCE.md`, 4 diagrams, 2 tier-flow images) plus the `AGENTS.md` / `llm.txt` / `package.json` count claims stale. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- **test(models):** Pin `CATALOG_BUILD_TIMEOUT_MS` in the #12058 canonical-catalog regression so a cold catalog build racing the 8s default bound no longer flakes the suite with a 500 ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Allow `@omniroute/opencode-plugin-v2/` in the npm pack policy — [#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870) shipped the v2 plugin beside its v1 sibling without widening the allowlist, so every packed file under it read as an unexpected artifact and failed `check:pack-policy` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Point the release-green validator's pack gate at the tree it is validating — the build-provenance guard defaulted to `origin/main`, which a release branch tip can never be an ancestor of mid-cycle, so `Package artifact` reported a false red on every run and the tarball boot-smoke cascaded off it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **docs(checks):** keep doc counts honest — headings, rankings, catalog, weights, quality gate and scoring diagram now covered ([#12507](https://github.com/diegosouzapw/OmniRoute/pull/12507)) — thanks @maxmad64bis +- **chore(lifecycle):** `check:model-lifecycle` now also diffs `DEFAULT_DEGRADATION_MAP` (the background-task redirect table) against the vendor lifecycle snapshot, refusing a retired id as source or target, with a table-driven unit test beside it. Three rows whose source the vendor had retired — `claude-sonnet-4-20250514`, `gemini-3-pro-preview` and `gpt-5.1-codex` (whose target `gpt-5.1-codex-mini` is retired too) — were dead code, since `checkLifecycle` answers 410 before the redirect runs; they are dropped (#12535 — thanks @pacocartones) +- **test(compression):** cover idle worker eviction at the resource level — the pool must call `terminate()` and must not retain the worker's `MessagePort`, complementing the `exit`-event assertion added with the fix ([#13371](https://github.com/diegosouzapw/OmniRoute/pull/13371)) +- **chore(quality):** raise the `imageGeneration.ts` file-size ceiling for the image-only-model guard that clears the #12945 base-red ([#13392](https://github.com/diegosouzapw/OmniRoute/pull/13392)) +- **chore(quality):** type the combo-test route's JSON response bodies instead of casting them to `any`, drop the now-empty suppression entry, and remove the `hasPerModelQuota` import `chatCore.ts` stopped using when the failure-classification helper was extracted ([#13379](https://github.com/diegosouzapw/OmniRoute/pull/13379)) +- **chore(electron):** the desktop bundle no longer ships the root-level files of every translated docs mirror (`README.md`, `llm.txt`, `CLAUDE.md`, `GEMINI.md`, `CONTRIBUTING.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`) — the packaged app only reads `docs/i18n//docs/**`, which stays. Saves ~11 MB on top of the translated CHANGELOGs already pruned. (#0000) ([#13235](https://github.com/diegosouzapw/OmniRoute/pull/13235)) +- **chore(quality):** retighten the `src/sse/handlers/chat.ts` unused-vars suppression count to the 8 violations that actually remain ([#13385](https://github.com/diegosouzapw/OmniRoute/pull/13385)) +- Bring the documented counts back in line with the code: 171 migrations (was 169) and 20 routing strategies (was 19), plus the regenerated `cli-tunnel` skill reference. ([#12970](https://github.com/diegosouzapw/OmniRoute/pull/12970)) +- Update the documented migration count to 172 after the call-logs provider-stats indexes landed. ([#13209](https://github.com/diegosouzapw/OmniRoute/pull/13209)) +- **chore(ci):** two gates that close the blind spots behind the exclusions above. `check:vitest-exclusions` requires every Vitest exclusion to name a tracking issue and to appear in `config/quality/vitest-exclusions.json` — the previous list grew to 62 files behind a comment pointing at an issue that had been closed for a month. `check-new-key-coverage` requires a key newly added to `en.json` to reach every locale; the existing coverage gate is a percentage floor per locale, so eleven absent keys out of ~13,000 left it at 99.9% while a whole feature shipped untranslated in nine languages. Both are diff-aware, so pre-existing debt stays frozen and neither needed a migration to turn on. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **build:** root layout no longer loads Inter from `next/font/google`, so a production image build does not need fonts.googleapis.com ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **docs(i18n):** bring the eight new `llm.txt` mirrors (km, kn, ml, my, ne, or, pa, si) up to the current provider and migration counts, so `check-docs-sync` stops rejecting every commit ([#13674](https://github.com/diegosouzapw/OmniRoute/pull/13674)) +- **refactor(ui):** flow surfaces (home topology, combo live studio, compression cockpit/waterfall/nodes, token-health badge) express state through the theme-aware `--orch-status-*` tokens instead of fixed dark-mode hex, so green/red/amber/grey stay legible in the light theme; dark mode is byte-identical. Categorical palettes (routing-strategy hues, compression-layer pills, provider brand colors) deliberately stay hex ([#12378](https://github.com/diegosouzapw/OmniRoute/issues/12378)) ([#13203](https://github.com/diegosouzapw/OmniRoute/pull/13203)) +- **chore(tests):** 51 test files that had been excluded from Vitest are running again, restoring roughly 350 assertions to the blocking suite. Each was measured individually first: of the 62 files parked behind the `// #8618 — pre-existing failure` comment, 51 pass against the current tree with no source change, so the exclusions had outlived the failures they were added for. The 11 that genuinely still fail stay excluded, but now point at a live tracker (#13204) rather than at #8618, which was closed in August while the list it tracked kept growing. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **chore(quality):** drop two ESLint suppression entries whose violations no longer exist, so `eslint --suppressions-location` stops rejecting every commit that touches the surrounding files ([#13384](https://github.com/diegosouzapw/OmniRoute/pull/13384)) +- **chore(release):** bring main's post-tag fixes into release/v3.8.51 — sync-back step 2/2 (direct commit `21c488f210`) +- **chore(deps):** patch toml and esbuild in the opencode plugin lockfiles ([#12965](https://github.com/diegosouzapw/OmniRoute/pull/12965)) +- **docs(changelog):** reconcile the v3.8.51 living section — fold 366 fragments, cover every cycle commit, credit every contributor ([#12971](https://github.com/diegosouzapw/OmniRoute/pull/12971)) +- **test(plugin):** wait for the v2 lazy-refresh tier instead of sleeping past it ([#12984](https://github.com/diegosouzapw/OmniRoute/pull/12984)) +- **chore(quality):** rebaseline chatCore.ts after the non-streaming regression fixes ([#13045](https://github.com/diegosouzapw/OmniRoute/pull/13045)) +- **docs(changelog):** add the missing bullet marker to the DeepSeek PoW fragment ([#13200](https://github.com/diegosouzapw/OmniRoute/pull/13200)) +- **chore(deps):** drain the Dependabot queue — 10 of 13 alerts ([#13213](https://github.com/diegosouzapw/OmniRoute/pull/13213)) +- **chore(ci):** make worktrees carry the hooks that enforce the identity gate ([#13236](https://github.com/diegosouzapw/OmniRoute/pull/13236)) +- **chore(deps):** pin csv-parse to ^7.0.2 via overrides (#13117) ([#13300](https://github.com/diegosouzapw/OmniRoute/pull/13300)) — thanks @KooshaPari +- **chore(deps):** bump hono from ^4.12.34 to ^4.13.7 (#13148) ([#13301](https://github.com/diegosouzapw/OmniRoute/pull/13301)) — thanks @KooshaPari +- **chore(deps):** pin joi to ^18.2.8 via overrides (#13085) ([#13302](https://github.com/diegosouzapw/OmniRoute/pull/13302)) — thanks @KooshaPari +- **docs(providers):** add Microsoft 365 Copilot (BizChat) provider guide (#12779) ([#13340](https://github.com/diegosouzapw/OmniRoute/pull/13340)) — thanks @KooshaPari +### 🙌 Contributors + +Thanks to everyone whose work landed in v3.8.51: + +| Contributor | PRs / Issues | +| --- | --- | +| [@5dive-bot](https://github.com/5dive-bot) | #11852 | +| [@abhisheksharma2411](https://github.com/abhisheksharma2411) | #11547, #11684, #11959, #12209 | +| [@adevwithpurpose](https://github.com/adevwithpurpose) | #11464 | +| [@adivekar-utexas](https://github.com/adivekar-utexas) | #12015, #12043, #12090, #12138 | +| [@AIB1TAL0S](https://github.com/AIB1TAL0S) | #12103 | +| [@alltomatos](https://github.com/alltomatos) | #11448 | +| [@alvinveroy](https://github.com/alvinveroy) | #11857, #12027, #12165 | +| [@amaleta](https://github.com/amaleta) | #11906, #11934 | +| [@amartinawi](https://github.com/amartinawi) | #11575 | +| [@AndrianBalanescu](https://github.com/AndrianBalanescu) | #11083, #11830, #11834, #11844 | +| [@anhtahaylove](https://github.com/anhtahaylove) | #13091, #13092, #13093, #13096, #13097, #13100, #13106, #13109, #13114, #13155, #13166, #13171, #13175, #13187, #13196 | +| [@aniruddhaadak80](https://github.com/aniruddhaadak80) | #11760 | +| [@app](https://github.com/app) | #12554 | +| [@arminanton](https://github.com/arminanton) | #11461, #11513 | +| [@AStupidBear](https://github.com/AStupidBear) | #11584 | +| [@atescivitci-cmd](https://github.com/atescivitci-cmd) | #12105 | +| [@b3nw](https://github.com/b3nw) | #11970, #11971, #12147 | +| [@backryun](https://github.com/backryun) | #11259, #11950, #12075, #12076, #12078, #12079, #12081, #12082, #12181, #12228, #12239, #12255, #12258, #12277, #12367, #12423, #12524, #12538 | +| [@benjaminkitt](https://github.com/benjaminkitt) | #11747 | +| [@benzntech](https://github.com/benzntech) | #11614, #11615 | +| [@Bl0ck154](https://github.com/Bl0ck154) | #11948, #11951, #11952, #11953, #11954 | +| [@brick30llc-ctrl](https://github.com/brick30llc-ctrl) | #11752, #12242, #12262 | +| [@Chewji9875](https://github.com/Chewji9875) | #11455, #11875, #12028 | +| [@cheynetom](https://github.com/cheynetom) | #12058 | +| [@davidebaraldo](https://github.com/davidebaraldo) | #12222, #12304 | +| [@davidlinfr](https://github.com/davidlinfr) | #6390 | +| [@ddarkr](https://github.com/ddarkr) | #12124 | +| [@Deftera186](https://github.com/Deftera186) | #11809 | +| [@dpozimski](https://github.com/dpozimski) | #11945 | +| [@DW-MediaLab](https://github.com/DW-MediaLab) | #12229 | +| [@echel0nn](https://github.com/echel0nn) | #11923 | +| [@f9td56dbgh-hub](https://github.com/f9td56dbgh-hub) | #11660 | +| [@fabioluissilva](https://github.com/fabioluissilva) | #11991 | +| [@foreveryh](https://github.com/foreveryh) | #12177 | +| [@geek007git](https://github.com/geek007git) | #12115, #12116, #12117, #12120, #12122 | +| [@geekyNads](https://github.com/geekyNads) | #11784, #12266 | +| [@ggiak](https://github.com/ggiak) | #11561, #12276, #12350, #12402 | +| [@gonisulaimann](https://github.com/gonisulaimann) | #12368, #12369, #12371 | +| [@Gorillaz322](https://github.com/Gorillaz322) | #12207 | +| [@groovecityJO](https://github.com/groovecityJO) | #12682 | +| [@hartmark](https://github.com/hartmark) | #11434, #11452, #11473, #11499, #11703, #11983, #11984, #11985, #11986, #11988, #11989, #11990, #11994, #12221, #12293, #12445, #12446, #12447, #12448, #12460, #12461, #12623, #12646, #12650, #12680, #12717, #12718, #12727, #12741, #12854 | +| [@hizzt](https://github.com/hizzt) | #11894 | +| [@hongnoul](https://github.com/hongnoul) | #11484 | +| [@HouMinXi](https://github.com/HouMinXi) | #11411, #11414, #11512, #11518, #11520, #11641, #11642, #11643, #11687, #11779, #11849, #11850, #11851, #11915, #11916, #11918, #11919, #11920, #12013, #12017, #12033, #12042, #12106, #12139, #12166, #12169, #12171, #12205, #12213, #12312, #12325, #12487, #12488, #12495, #12504, #12557, #12566, #12590, #12591, #12624, #12626, #12632, #12637, #12678, #12696, #12697, #12711, #12733, #12746, #12767, #12770, #12789, #12803, #12805, #12811, #12866, #12868, #12899, #12926, #12934, #12950, #12951, #12974, #13001, #13006, #13011, #13017, #13026, #13027, #13034, #13035, #13038, #13042, #13050, #13060, #13061, #13069, #13107, #13120, #13136, #13195, #13197 | +| [@Hsia97](https://github.com/Hsia97) | #11624 | +| [@jacobsparts](https://github.com/jacobsparts) | #11854, #12155, #12167 | +| [@joglomedia](https://github.com/joglomedia) | #11980 | +| [@jonlwheat2-gif](https://github.com/jonlwheat2-gif) | #11443, #11450, #11549, #11567, #11608, #11633, #11635, #11640, #11644, #11671, #11672, #11673, #11674, #11675, #11676, #11677, #11706, #11783, #12051, #12052, #12053, #12055 | +| [@kanade-hoshino](https://github.com/kanade-hoshino) | #12180 | +| [@Karan825](https://github.com/Karan825) | #11861 | +| [@KaspaPulse](https://github.com/KaspaPulse) | #11389, #11469, #11628, #11666, #11910 | +| [@keeltrace](https://github.com/keeltrace) | #12080, #12223 | +| [@killer30001000](https://github.com/killer30001000) | #12178 | +| [@KooshaPari](https://github.com/KooshaPari) | #12470, #12592, #12667, #12699, #12703, #12706, #12764, #12769, #12771, #13300, #13301, #13302, #13340, #13401, #13403, #13404, #13405, #13406, #13407, #13409, #13410, #13411, #13412, #13413, #13414, #13418, #13424, #13427, #13433, #13523, #13524, #13525, #13528, #13531, #13532, #13534, #13539, #13543, #13545, #13546, #13547, #13550, #13553 | +| [@kriptoburak](https://github.com/kriptoburak) | #11370 | +| [@KrzysiekSko](https://github.com/KrzysiekSko) | #12425, #12432, #12673 | +| [@marcelokarval](https://github.com/marcelokarval) | direct commit / report | +| [@maxmad64bis](https://github.com/maxmad64bis) | #1622, #11435, #11437, #11441, #11537, #11550, #11553, #11555, #11557, #11812, #11842, #11843, #11903, #12151, #12214, #12215, #12218, #12226, #12314, #12316, #12317, #12318, #12319, #12320, #12321, #12507, #12715, #12731, #12744, #12786, #12787, #12788, #12790, #12792, #12794, #12795, #12828, #12832, #12853, #12857, #12870, #12937, #12941, #12975, #13141, #13142, #13146, #13147 | +| [@mdigitalbh81](https://github.com/mdigitalbh81) | #12240 | +| [@Meet6338-X](https://github.com/Meet6338-X) | #11598, #11609, #12110 | +| [@morpheus9393](https://github.com/morpheus9393) | #11943 | +| [@MumuTW](https://github.com/MumuTW) | #11492, #11502, #11506, #11507, #11626, #11675, #11685, #11728, #11746, #11888, #11889, #11890, #11892 | +| [@Neuron-Mr-White](https://github.com/Neuron-Mr-White) | #11622, #11801, #12256 | +| [@NightStalker-87](https://github.com/NightStalker-87) | #12183 | +| [@NoxzRCW](https://github.com/NoxzRCW) | #11879, #11880, #11881, #11882, #11883, #11935 | +| [@ntdat812](https://github.com/ntdat812) | #11585, #12095 | +| [@ntdatt812](https://github.com/ntdatt812) | #11368, #11573, #11574, #11576, #11577, #11580, #11582, #11583, #11588, #11589, #11590, #11591, #11592, #11593, #11672, #12177, #12180, #12873, #12918, #12920, #12921, #12925, #12930, #13007, #13009, #13024, #13025, #13083, #13087, #13101, #13104, #13110 | +| [@official-burak](https://github.com/official-burak) | #11542 | +| [@opensource-elearning](https://github.com/opensource-elearning) | #12179, #12189, #12278, #12286 | +| [@oyi77](https://github.com/oyi77) | #11408, #11409, #11421, #11505, #11677, #12036, #12110 | +| [@pacocartones](https://github.com/pacocartones) | #11521, #11522, #11527, #11528, #11529, #11530, #11531, #11532, #11533, #11534, #11595, #11599, #11603, #11604, #11605, #11607, #11610, #11676, #11714, #11716, #11718, #11767, #11838, #11860, #11862, #11869, #11871, #11872, #11873, #11903, #11906, #11921, #11934, #12356, #12358, #12359, #12360, #12361, #12362, #12364, #12365, #12373, #12374, #12375, #12376, #12377, #12379, #12380, #12381, #12386, #12387, #12389, #12390, #12394, #12395, #12397, #12401, #12403, #12404, #12522, #12523, #12535, #12536, #12540, #12541, #12543, #12545, #12548, #12549, #12550, #12551, #12552, #12647, #12651, #12653, #13055 | +| [@patrykkopycinski](https://github.com/patrykkopycinski) | #11936, #11937, #12224 | +| [@PauloFH](https://github.com/PauloFH) | #11509 | +| [@PauloHSOliveira](https://github.com/PauloHSOliveira) | #12241 | +| [@ponkcore](https://github.com/ponkcore) | #12054 | +| [@Prajeeth-12](https://github.com/Prajeeth-12) | #11634, #12046 | +| [@quiterunner-commits](https://github.com/quiterunner-commits) | #12131, #12143 | +| [@rafacpti23](https://github.com/rafacpti23) | #11554, #11558, #12192, #12197, #12198, #12202, #12203, #12204 | +| [@ragnar-claude](https://github.com/ragnar-claude) | #11564 | +| [@raheemuddin786](https://github.com/raheemuddin786) | #11491, #11839, #11840, #11841, #12003, #12230, #12231, #12232, #12233, #12234 | +| [@rahilmavani](https://github.com/rahilmavani) | #11761 | +| [@Rahulsharma0810](https://github.com/Rahulsharma0810) | #11771 | +| [@RaviTharuma](https://github.com/RaviTharuma) | #11710, #11727, #11797, #11798, #11802, #11805, #11806, #11811, #12098, #12099, #12101, #12449, #12452, #12472, #12473, #12493, #12533, #12607, #12628, #12631, #12636, #12875, #12876, #12880, #12882, #12884 | +| [@rezjalibd](https://github.com/rezjalibd) | #12186 | +| [@RhianB14](https://github.com/RhianB14) | #11692 | +| [@rifqiawl](https://github.com/rifqiawl) | #11517, #11519, #11969 | +| [@rqzbeh](https://github.com/rqzbeh) | #11390 | +| [@Sabeekhann](https://github.com/Sabeekhann) | #11699, #11705 | +| [@santosraju99-hub](https://github.com/santosraju99-hub) | #11755, #11770 | +| [@Seramicx](https://github.com/Seramicx) | #11568 | +| [@Siva010](https://github.com/Siva010) | #12191 | +| [@solstxce](https://github.com/solstxce) | #11597 | +| [@soroush5](https://github.com/soroush5) | #12691 | +| [@tenshiak](https://github.com/tenshiak) | #12279 | +| [@TheDemonTuan](https://github.com/TheDemonTuan) | #11468, #11470, #11471, #11482, #11548, #11758, #11775, #11814 | +| [@thomasmaerz](https://github.com/thomasmaerz) | #12834 | +| [@tuandinh0801](https://github.com/tuandinh0801) | #11454 | +| [@turbolego](https://github.com/turbolego) | #11621, #11762, #11772, #11774, #11781, #12216 | +| [@Tushar49](https://github.com/Tushar49) | #11392 | +| [@ujjawalkaushik1110](https://github.com/ujjawalkaushik1110) | #11863 | +| [@vermasomesh835](https://github.com/vermasomesh835) | #11794 | +| [@vsd2807](https://github.com/vsd2807) | #11565, #11619 | +| [@wahidsadik371-coder](https://github.com/wahidsadik371-coder) | #12126 | +| [@watchingdogs](https://github.com/watchingdogs) | #12031 | +| [@wildcard](https://github.com/wildcard) | #11620 | +| [@xiaoyaner0201](https://github.com/xiaoyaner0201) | #11460, #11662, #12083 | +| [@Xxx91n](https://github.com/Xxx91n) | #11690 | +| [@yourspraveen](https://github.com/yourspraveen) | #11146 | +| [@yxyxy](https://github.com/yxyxy) | #11671 | +| [@ZaimMarzuki](https://github.com/ZaimMarzuki) | #11960 | +| [@Zartharas](https://github.com/Zartharas) | #11340 | +| [@zero-executioner](https://github.com/zero-executioner) | #11631 | +| [@diegosouzapw](https://github.com/diegosouzapw) | maintainer | + +--- ## [3.8.50] — 2026-08-25 _Living section — regenerated 2026-08-12 from all cycle commits (cycle open `ed2db6cb19` → tip). Bullets carry the merged PR and its author; direct pushes listed separately._ diff --git a/docs/i18n/hr/CHANGELOG.md b/docs/i18n/hr/CHANGELOG.md index bf533c30fc..2e27299330 100644 --- a/docs/i18n/hr/CHANGELOG.md +++ b/docs/i18n/hr/CHANGELOG.md @@ -95,16 +95,1119 @@ ## [3.8.51] — TBD -_Living section — cycle opened at the v3.8.50 freeze (parallel-cycle model). Bullets are aggregated from `changelog.d/` fragments at each `/generate-release` phase._ +_Living section — reconciled 2026-09-15 from all cycle commits (`091589089c` → `c0f92ec98a`, 916 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 | **123** | +| 📝 Commits in the cycle | **916** | +| 🔀 Pull requests referenced | **892** | +| 📋 Changelog entries | **919** | +| 🙌 Contributors credited in entries | **121** | +| 🤖 Automated dependency commits | 18 | + +**Entries by type** + +| Type | Count | +| --- | ---: | +| 🐛 Fixes | 592 | +| ✨ Features | 131 | +| 🧹 Chore | 93 | +| 📚 Docs | 42 | +| 🧪 Tests | 32 | +| ♻️ Refactor | 8 | +| 🏗️ Build | 7 | +| ⚡ Performance | 7 | +| ⚙️ CI | 2 | +| ⏪ Reverts | 2 | +| 📦 Dependencies | 2 | +| 🔒 Security | 1 | + +### 🏆 Top 25 contributors this cycle + +_By commits in `091589089c..c0f92ec98a`, author identities consolidated via `.mailmap` and the merged PR's GitHub login. Bots excluded._ + +| # | Contributor | Commits | +| ---: | --- | ---: | +| 🥇 | diegosouzapw | 323 | +| 🥈 | Bob.Hou (@HouMinXi) | 79 | +| 🥉 | Paco Cartones (@pacocartones) | 77 | +| 4 | Dizzle (@maxmad64bis) | 49 | +| 5 | Koosha Paridehpour (@KooshaPari) | 43 | +| 6 | Nguyen Thanh Dat (@ntdatt812) | 31 | +| 7 | Ravi Tharuma (@RaviTharuma) | 26 | +| 8 | Markus Hartung (@hartmark) | 22 | +| 9 | Webman (@jonlwheat2-gif) | 22 | +| 10 | backryun | 17 | +| 11 | anhtahaylove | 15 | +| 12 | Rafa Martins (@rafacpti23) | 10 | +| 13 | Syed Raheemuddin (@raheemuddin786) | 10 | +| 14 | Nguyễn Viết Tuấn (@TheDemonTuan) | 8 | +| 15 | MumuTW | 7 | +| 16 | Paijo (@oyi77) | 7 | +| 17 | Tobias Andersen (@turbolego) | 6 | +| 18 | Bl0ck (@Bl0ck154) | 5 | +| 19 | SHANMUGAPRIYAN (@geek007git) | 5 | +| 20 | KaspaPulse | 5 | +| 21 | Abhishek Sharma (@abhisheksharma2411) | 4 | +| 22 | Abhishek Divekar (@adivekar-utexas) | 4 | +| 23 | Andrew B. (@AndrianBalanescu) | 4 | +| 24 | NoxzRCW | 4 | +| 25 | opensource-elearning | 4 | ### ✨ New Features +- **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). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- Added Google AI Studio Gemini batch text-to-speech support through `POST /v1/audio/speech`. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- 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. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(routing):** subscription-first auto groupings — `auto/subscription` routes only through plan-included connections with a documented hard-stop overage and fails closed on exhaustion, while `auto/thrifty` orders the pool `subscription → keyless → free → cheap → premium` and steps up one rung at a time as each is exhausted. Billing class comes from a curated per-connection catalog (uncurated is treated as metered, never plan-included), both reuse STRICT_ZERO_COST's per-connection verification, and a quota reading whose `resetAt` has passed is now refreshed regardless of TTL so routing returns to plan capacity as soon as it resets ([#11146](https://github.com/diegosouzapw/OmniRoute/pull/11146)) — thanks @yourspraveen +- **feat(providers):** publish a management-authenticated versioned web-session credential contract from OmniRoute's canonical browser credential metadata ([#11340](https://github.com/diegosouzapw/OmniRoute/pull/11340)) — thanks @Zartharas +- **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)) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add Xquik X search with typed results, credential validation, REST routing, and MCP selection ([#11370](https://github.com/diegosouzapw/OmniRoute/pull/11370)) — thanks @kriptoburak +- **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)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(dashboard):** surface durable exclusive managed leases in the existing Sessions view, keeping leased clients visible across idle gaps while marking connections with in-flight work as active ([#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389)) — thanks @KaspaPulse +- **build(bun):** allow Turbopack bundler flag on Bun 1.4+ with configurable Webpack fallback ([#11471](https://github.com/diegosouzapw/OmniRoute/pull/11471)) — thanks @TheDemonTuan +- feat(api): add an opt-in `modelVisibilityAllowlist`/`modelVisibilityDenylist` settings pair to curate exactly which models `/v1/models` advertises, mirrored into every `auto/*` combo candidate pool so a denied model cannot be routed to via combo selection either (#11481) ([#11997](https://github.com/diegosouzapw/OmniRoute/pull/11997)) +- **feat(rankings):** the Free Provider Rankings page shows what each provider actually served over the last 24 h. It ranked by ELO alone, which left a provider that answers every call with an error in first place; the usage data was already served by the API but never requested. A provider with too small a sample shows a dash, not a number ([#11546](https://github.com/diegosouzapw/OmniRoute/pull/11546)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **feat(guardrails):** enforce a bounded, deterministic contract for Video Bridge transcripts — 256 cues, 4096 input code units and 4 KiB UTF-8 per cue, 64 KiB total text, malformed-Unicode rejection, focus-window scoping, cross-source reconciliation with contributing-source metadata, and a structural provenance trust boundary so caller JSON can never self-assert `embedded`/`audio-bridge` provenance ([#11652](https://github.com/diegosouzapw/OmniRoute/issues/11652)) ([#12009](https://github.com/diegosouzapw/OmniRoute/pull/12009)) +- **feat(video):** orchestrate optional Video Bridge audio extraction and Audio Bridge STT behind a dual opt-in (operator setting AND per-request signal) — a new loopback-only broker `mode=audio` operation shares the frame path's exact process queue, deadline, AbortSignal, and byte budgets to extract a bounded mono 16 kHz PCM WAV from the same already-downloaded video, then reuses the existing Audio Bridge transcription boundary; provider segment timing is preserved when available and marked coarse otherwise, and every failure degrades to a visual-only-safe partial instead of throwing (#11654). ([#12012](https://github.com/diegosouzapw/OmniRoute/pull/12012)) +- Add a tenant-bound Video Bridge drill-down lifecycle on top of the existing secure cache substrate: opaque hashed handles (never raw session/video identifiers), preview/standard/detail multiresolution variants resampled on read, response pagination capped at 8 frames and 32 MiB, and a new authenticated `/api/v1/video-bridge/drilldown` consumer route that stays disabled for remote access by default and denies cross-key access with the same response as a nonexistent handle (no existence oracle). ([#12006](https://github.com/diegosouzapw/OmniRoute/pull/12006)) +- **test(video):** Add the Video Bridge FU-07/FU-09 promotion-evidence harness (#11656) — a frozen Zod manifest schema covering the 8 required scenario kinds (static scenes, rapid cuts, late facts, fades, blur, small text, close events, visual prompt injection) with a minimum of 3 repetitions per case, deterministic declarative fixture recipes (`videoBridgePromotionFixtures.ts`), a pure medians/p95 metrics aggregator, a pure FU-07/FU-09 promotion-verdict evaluator applying the ticket's exact thresholds (missing token usage always holds), a digest-only persistence layer that never retains raw media or raw model responses, and a versioned per-model promotion allowlist shipped empty with every model defaulting to `hold`. The FU-07/FU-09 promotion verdicts themselves remain HOLD — they require a real evidence run against real models on VPS 192.168.0.15. ([#12008](https://github.com/diegosouzapw/OmniRoute/pull/12008)) +- **feat(video bridge):** "embedded" transcript provenance can now be legitimately earned instead of merely asserted — a bounded, allowlisted (`mov_text`/`subrip`/`webvtt`) subtitle probe runs through the loopback-only Video Bridge broker (at most 2 streams, 10s subdeadline bounded by the request deadline, 256 KiB output, 4096-code-unit lines), normalized through a bounded, ReDoS-safe WebVTT parser and Zod-validated end to end. The adapter always resolves to an explicit `success`/`absent`/`transient_failure` outcome — a subtitle failure never breaks the visual description path, and only a fingerprint-verified broker response (never a caller-declared label) can produce embedded cues (#11659). ([#12011](https://github.com/diegosouzapw/OmniRoute/pull/12011)) +- Default new Antigravity-family connections (agy CLI imports and Antigravity OAuth connects) to model auto-sync, so live model discovery lands in the synced catalog and `/v1/models` picks up freshly released upstream models (e.g. Gemini 3.7 Flash tiers) without code changes. Existing connections keep their current setting; the per-connection dashboard toggle remains the opt-out. (#11685 — thanks @MumuTW) ([#11887](https://github.com/diegosouzapw/OmniRoute/pull/11887)) +- **feat(ui):** enable React Compiler (`reactCompiler: true` + `babel-plugin-react-compiler`) for automatic memoization at build time ([#11783](https://github.com/diegosouzapw/OmniRoute/pull/11783)) — thanks @jonlwheat2-gif +- **feat(providers):** add SeekAi (`seekai.cc`) as an OpenAI-compatible New-API gateway — catalog id `seekai` (alias `ska`), `https://seekai.cc/v1`, live `/v1/models` via `passthroughModels`, aggregator-list membership so New-API balance detection can opt in. No referral/aff codes. ([#11786](https://github.com/diegosouzapw/OmniRoute/issues/11786)) ([#12557](https://github.com/diegosouzapw/OmniRoute/pull/12557)) — thanks @HouMinXi +- **feat(zai):** add GLM-5.3-Flash Coding Plan support (1M context, 128K output, vision, `low|high|max` reasoning) and route `zai` GLM-5.3-family API-key traffic through the OpenAI-compatible Coding Plan endpoint with native thinking defaults ([#11801](https://github.com/diegosouzapw/OmniRoute/pull/11801)) — thanks @Neuron-Mr-White +- **feat(combo):** choose how combo models are ordered — manual, provider, score, or name — via a sort control in the dashboard builder, persisted in `config.modelSort` and re-applied on load and after add ([#11812](https://github.com/diegosouzapw/OmniRoute/pull/11812)) — thanks @maxmad64bis +- **feat(free):** custom models can be marked free-tier via `customModels[].isFree`; `isFreeModel()` is the first door and `hidePaidModels` respects it even for providers outside the free budget ([#11843](https://github.com/diegosouzapw/OmniRoute/pull/11843)) — thanks @maxmad64bis +- **feat(nodejs):** add `5dive` as a `configure` target — `omniroute configure 5dive` / `omniroute setup-5dive` write a 5dive auth profile that points an agent fleet's `claude` seats at OmniRoute, with the root-only write, the loopback-vs-`https` endpoint rule and the per-seat model pin handled explicitly ([#11852](https://github.com/diegosouzapw/OmniRoute/pull/11852)) — thanks @5dive-bot +- **feat(sse):** treat `max` as a first-class reasoning-effort tier and clamp per model family (GLM 5.1+/DeepSeek V4+/Kimi K3+ keep native `max`; o1/MiniMax/Grok/Muse Spark clamp to their upstream ceiling) ([#11875](https://github.com/diegosouzapw/OmniRoute/pull/11875)) — thanks @Chewji9875 +- **feat(providers):** the provider plugin manifest now advertises a `usage-fetch` capability for the 40 providers that have a wired usage/quota fetcher, so external dashboards can read it from `GET /api/v1/provider-plugin-manifest` instead of parsing `open-sse/services/usage.ts` after every release. Discovery only — no new fetcher, no quota change, and the Dashboard quota widget stays gated by `USAGE_SUPPORTED_PROVIDERS`. `USAGE_FETCHER_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/fetcherProviders.ts`) and is re-exported from `services/usage.ts`, keeping the manifest module a light leaf instead of pulling the ~490-module usage dispatcher into the manifest route. ([#11903](https://github.com/diegosouzapw/OmniRoute/pull/11903)) — thanks @maxmad64bis / @pacocartones +- **feat(plugins):** `OMNIROUTE_PLUGINS_DIR` sets the directory the runtime plugin scanner reads — and the root the plugin manager installs into — overriding the `HOME`-derived default, so a Docker/K8s deployment can point straight at its bind-mounted plugin tree instead of moving `HOME` just to relocate the scan path. An image that exports no home no longer scans `/tmp/.omniroute/plugins` in silence: the resolved directory is logged once at startup as `scanner.dir_resolved`, naming the input that won. Unset, behaviour is unchanged. Distinct from the CLI-only `OMNIROUTE_PLUGIN_PATH`, which finds `omniroute-cmd-*` command packages and never reached this scanner ([#11906](https://github.com/diegosouzapw/OmniRoute/pull/11906)) — thanks @amaleta / @pacocartones +- **feat(leases):** add an explicit owner-authenticated status action that returns only the active lease's privacy-safe configured connection and provider labels, with generation fencing and no credential or internal-id disclosure ([#11910](https://github.com/diegosouzapw/OmniRoute/pull/11910)) — thanks @KaspaPulse +- **feat(providers):** import-from-file modal shows per-row API errors and ships a downloadable CSV template ([#12071](https://github.com/diegosouzapw/OmniRoute/issues/12071)) ([#12504](https://github.com/diegosouzapw/OmniRoute/pull/12504)) — thanks @HouMinXi +- **feat(providers):** dashboard search matches connection name and `baseUrl` so imported OpenAI-compat nodes surface on the provider card ([#12108](https://github.com/diegosouzapw/OmniRoute/issues/12108)) ([#12495](https://github.com/diegosouzapw/OmniRoute/pull/12495)) — thanks @HouMinXi +- **feat(routing):** add a `score` Auto router strategy that selects the highest configured weighted score and reuses `explorationRate`. ([#12155](https://github.com/diegosouzapw/OmniRoute/pull/12155)) — thanks @jacobsparts +- Add a runtime feature flag to disable universal context handoffs globally without changing the default behavior. ([#12167](https://github.com/diegosouzapw/OmniRoute/pull/12167)) — thanks @jacobsparts +- **feat(providers):** the provider plugin manifest now also advertises a `usage-supported` capability for the 46 providers whose usage API is accepted by the server and Dashboard routes, so integrators can distinguish "the server will serve quota for this provider" from "a fetcher is wired" without reading TypeScript. Discovery only — no fetcher or quota change. `usage-fetch` resolves on id or alias (the usage dispatcher accepts both); `usage-supported` resolves on id alone, matching the runtime guard `USAGE_SUPPORTED_PROVIDERS.includes(providerId)`. `USAGE_SUPPORTED_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/supportedProviders.ts`) and is re-exported from `providers.ts`, mirroring the `fetcherProviders` leaf from #11903 and keeping the manifest a light module. ([#12214](https://github.com/diegosouzapw/OmniRoute/pull/12214)) — thanks @maxmad64bis +- **feat(rankings):** order Free Provider Rankings by what each provider actually served — `GET /api/free-provider-rankings?sortBy=reliability` and a "Most reliable first" toggle on the page. Providers with too few calls to state a success rate keep their score order below the measured ones; the default order is unchanged ([#12218](https://github.com/diegosouzapw/OmniRoute/pull/12218)). — thanks @maxmad64bis +- **perf(sse):** defer `cloneLogPayload()` in the structured SSE collector until after the `maxEvents`/`maxBytes` cap check, eliminating ~9,800 wasted `structuredClone` calls per streaming response (65–71% faster `push()`). Reducer snapshot isolation restored for OpenAI and Responses summaries ([#12241](https://github.com/diegosouzapw/OmniRoute/pull/12241)) — thanks @PauloHSOliveira ([#12243](https://github.com/diegosouzapw/OmniRoute/pull/12243)) +- **feat(usage):** Devin CLI agentic quota (Codeium seat-management GetUserStatus) and OpenRouter key limits plus account credits now surface in Provider Limits ([#12256](https://github.com/diegosouzapw/OmniRoute/pull/12256) — thanks @Neuron-Mr-White) +- **feat(settings):** persist `headroomUrl` through Settings so status/start use the operator URL instead of only `HEADROOM_URL` ([#12306](https://github.com/diegosouzapw/OmniRoute/issues/12306)) ([#12487](https://github.com/diegosouzapw/OmniRoute/pull/12487)) — thanks @HouMinXi +- **feat(auto-combo):** Auto-Combo scoring can now weigh how often a provider/model has actually succeeded. The engine already carried that number on every candidate — 24 hours of usage history behind a ten-sample floor, real-time metrics otherwise — and the scoring function never read it, while the weight table described `stability` as if it did. `reliability` (`1 - failureRate`, with the same field precedence and the same rate-bounding the speed ranking already uses, so a corrupt reading means "nothing observed" rather than "fails every call") is now a declared factor shipping at weight `0`, so routing is unchanged until an operator gives it one, and the `stability` description now matches what that factor computes ([#12317](https://github.com/diegosouzapw/OmniRoute/pull/12317)) — thanks @maxmad64bis +- **feat(routing):** With `freeAccessPolicy: "strict"`, the read-only candidate listing (`GET /v1/auto-combo/{channel}/candidates`) no longer hides the candidates the zero-cost guard excludes — the same read-only transparency the resilience filter already honours (#9133). Each candidate now carries `freeAccessExclusion` saying why it would be kept out, and it tells an exhausted allowance apart from a quota reading that never arrived or went stale, which used to look identical from the outside. Routing is unchanged: the listing reports, it never enforces. The separate `excludeTosAvoid` guard still drops its candidates without a reason; that gap is now documented rather than closed ([#12319](https://github.com/diegosouzapw/OmniRoute/pull/12319)) — thanks @maxmad64bis +- **feat(radar):** The Radar catalog table now shows two facts it was already receiving from the feed and dropping on the floor: the per-model rate limits (requests and tokens, per minute and per day) in a new column, and a badge when a provider's terms state it may train on the prompts you send. A limit of zero renders as zero rather than "rate-only" — for a ceiling those are opposite facts — and a model with no training statement gets no badge, because an absent statement is not a guarantee ([#12320](https://github.com/diegosouzapw/OmniRoute/pull/12320)) — thanks @maxmad64bis +- **feat(radar):** explain Community, single-use, contributor, supporter, recovery, abuse, offers, and privacy rules before either Radar activation action, and remove the superseded fixed-PR grant promise from every UI locale ([#12342](https://github.com/diegosouzapw/OmniRoute/pull/12342)) +- **feat(gamification):** the dashboard Profile page now shows the real daily streak — `/api/gamification/level` returns `streak: { current, longest }` (per key with `apiKeyId`, operator-wide maximum otherwise) and the streak card reads it instead of a hard-coded 0 (#2403) ([#12377](https://github.com/diegosouzapw/OmniRoute/pull/12377)) — thanks @pacocartones +- **feat(gamification):** the dashboard leaderboard now shows each API key's display name under the Name column instead of a truncated key id; `GET /api/gamification/leaderboard` attaches `name` per entry (name only — no key material), while the shared ranking helper and the federation leaderboard stay id-only — thanks @pacocartones ([#12385](https://github.com/diegosouzapw/OmniRoute/pull/12385)) +- **feat(gamification):** enforce the documented 1000 XP/min per-API-key anti-cheat rate limit on the XP award path; over-limit awards are logged and skipped instead of persisted, and the sliding window now matches the timestamp format stored in `xp_audit_log` ([#2403](https://github.com/diegosouzapw/OmniRoute/issues/2403)) ([#12390](https://github.com/diegosouzapw/OmniRoute/pull/12390)) — thanks @pacocartones +- **feat(admin):** localize the gamification anomalies page — the loading state, the Status column and the Suspicious badge now come from the `common` catalog (new `common.suspicious` key propagated to every locale) — add it to the Gamification sidebar group as `gamification-admin` (`/dashboard/gamification/admin`), and expose the loading and empty states as polite `role="status"` live regions (#12401 — thanks @pacocartones) +- **feat(providers):** skip GitHub combo members missing from the live synced catalog, and drop Copilot models that are policy-disabled or hidden from the model picker ([#12473](https://github.com/diegosouzapw/OmniRoute/pull/12473)) — thanks @RaviTharuma +- **feat(i18n):** locale-expansion tooling — `npm run i18n:add-locale` adds a language to config, dashboard, docs mirrors, CLI, README/indexes and site in one command; browser, CLI and cookie detection resolve `uk`, `fil`/`tl`, `zh-Hant` (and the retired legacy `in` → `id`) via config aliases; `config/i18n.json` now ships in the npm package so the published CLI can read it; new real-translation ratio gate (`npm run i18n:check-ratio`, advisory) with a per-locale ratchet baseline; `run-translation --adopt` restores `.i18n-state.json`; `sync-language-bars` generates 🌐 bars from config; `validate_translation.py` loads its allowlist again. Retires the duplicate `in` locale (Indonesian mislabelled as Hindi) — 42 honest locales; saved `NEXT_LOCALE=in` / `OMNIROUTE_LANG=in` keep working. (#12496) +- **feat(api):** Emit gateway-measured `tokens_per_second` (TTFT excluded) on streaming usage and `X-OmniRoute-Tokens-Per-Second` when first-token latency is known ([#12616](https://github.com/diegosouzapw/OmniRoute/issues/12616)) ([#12631](https://github.com/diegosouzapw/OmniRoute/pull/12631)) — thanks @RaviTharuma +- **feat(opencode-plugin): map gateway cost/usage/tok/s onto OpenCode inference payloads** — the official plugin copies `X-OmniRoute-Response-Cost`, token counts, `X-OmniRoute-Tokens-Per-Second` / `usage.tokens_per_second`, TTFT, and the winning `X-OmniRoute-Model` onto the JSON/SSE body OpenCode already consumes. Missing tok/s is left unset (never `tokens / latency`). (#12636) — thanks @RaviTharuma +- **feat(combo):** add combo strategy `quota-weighted`: skip exhausted accounts, then weighted-draw by leftover / in-flight load; existing conversations stay pinned until the account is empty ([#12789](https://github.com/diegosouzapw/OmniRoute/pull/12789)) — thanks @HouMinXi +- **feat(models):** Account live listings become the chat catalog source for Claude, Codex, Copilot, and AGY; public metadata only fills prices on IDs those accounts already list. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **fix(models):** Union `agy` and `antigravity` live catalogs so `agy/gemini-3.8-flash-high` is not rejected after the prefix folds to `antigravity`. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **feat(opencode):** Add `@omniroute/opencode-plugin-v2`, an opencode v2 plugin that gives a v2 install a route to an OmniRoute gateway: it publishes models, combos and auto-combos into the host catalog, refreshes them lazily behind a 300s TTL, and keeps serving the last known catalog from an on-disk snapshot when the gateway is unreachable. The v1 plugin (`@omniroute/opencode-plugin`) is untouched and keeps working as before. The plugin adapts to the host instead of assuming one shape: it reads the shape the host seeds into the catalog draft and publishes the `api` block, the pre-`api` top-level fields, or both. The gateway key comes from the host credential store when one is connected, so no secret needs to sit in a config file; a source the gateway refuses is named in a warning instead of silently degrading the catalog to raw model ids; and tool schemas bound for a Gemini model are stripped of the JSON-Schema keywords Gemini rejects, so tool calling survives that route ([#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870)) — thanks @maxmad64bis +- feat(services): show sanitized CLIProxyAPI account health from its authenticated management API without exposing credentials, file paths, or raw account metadata (#6342) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add AnySearch free web search + URL extract (webFetch) with typed results, credential validation, REST routing, and MCP selection - fallback-only ([#11690](https://github.com/diegosouzapw/OmniRoute/pull/11690)) — thanks @Xxx91n +- **feat(dashboard):** display clamped `[0, 100]%` cached input token ratio in request logs table ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) — thanks @b3nw +- **feat(catalog):** add `OMNIROUTE_DISABLE_THINKING_LEVEL_VARIANTS` feature flag to optionally filter out thinking level variants from model catalog ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) — thanks @b3nw +- **feat(dashboard):** continuously export call logs to external analytics stores. A pluggable destination registry ships the full Logs-tab record set on an hourly `JobRegistry` cron, with a persisted per-destination cursor, batched inserts, a config UI rendered from each destination's own field descriptors, and a REST layer (`/api/log-export/*`) for CRUD, a connection test, and an on-demand run. A destination can opt into `includeBodies` to also ship the request and response payloads shown in the Logs detail pane, including the client and provider views of each call; this is off by default, and payloads inherit the dashboard's PII sanitisation, secret redaction and `noLog` handling. Google BigQuery is the first destination, using a service-account key stored encrypted at rest and streaming inserts keyed by call-log id, into a table that is day-partitioned on `timestamp` and clustered on `api_key_name`, `provider`, `model` and `status`. ([#11945](https://github.com/diegosouzapw/OmniRoute/pull/11945)) — thanks @dpozimski +- **feat(providers):** add MaxAI as a signed, OpenAI-compatible provider serving its 13 paid chat models (GPT-5.6 / Luna / Thinking, Claude 5 Sonnet, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite, Grok 4.1-fast / 4.5, DeepSeek V3.2 / R1, Llama 3.3 70B) through OmniRoute's `/v1` endpoint, with per-request HMAC-SHA1→SM3→AES request signing, live model + context-window discovery from `/models/get_config`, and prompted tool-calling translated to OpenAI `tool_calls` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI vision input — image_url content parts are forwarded inline in `message_content` to the 6 vision-capable models (GPT-5.6 / Luna / Thinking, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite) ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI image generation — 6 image models (gpt-image-1, dall-e-3, flux-1-schnell/dev/pro, sd3-medium) exposed through `POST /v1/images/generations` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI document RAG — inline base64 file/document attachments are uploaded to MaxAI (content-addressed `doc_id`) and attached to the chat via `doc_list` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** browserless MaxAI onboarding — email device-pair login (`/api/providers/[id]/login`) and signed access-token refresh, so a connection can be created and kept fresh without a real browser or Google OAuth ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** per-provider TLS impersonation profile (MaxAI presents a Windows Firefox-150 client fingerprint) so its bot-sensitive endpoints accept OmniRoute traffic ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** add **Nimble** as a web-search and web-fetch provider (`nimble-search`) — `/v1/search` routes to Nimble's search API at `lite` depth (locale, freshness and include/exclude domain filters mapped onto the shared request contract), and `/v1/web/fetch` routes to Nimble Extract, which covers all four fetch formats (`markdown`, `html`, `links`, `screenshot`) from a single call. One API key serves both surfaces. ([#11620](https://github.com/diegosouzapw/OmniRoute/pull/11620)) — thanks @wildcard +- The npm publish is automatic again, through npm Trusted Publishing (OIDC): the hosted `stage-npm` job publishes with a short-lived credential minted from GitHub's id-token — no `NPM_TOKEN`, no 2FA prompt, provenance attached. `publish_mode=staged` (owner approves with 2FA) and `direct` (token) remain available on `workflow_dispatch`. ([#11931](https://github.com/diegosouzapw/OmniRoute/pull/11931)) +- **feat(providers):** Add **Opper** as an API-key gateway provider — EU-hosted AI gateway with 700+ models from 30+ providers behind one OpenAI-compatible API and one key (`OPPER_API_KEY`); model ids use `provider/model` format (e.g. `anthropic/claude-sonnet-4-6`, `openai/gpt-5`); live model catalog at `https://api.opper.ai/v3/compat/models`; entry mirrors `requesty` (same shape, `passthroughModels: true`, no static seed) ([#11629](https://github.com/diegosouzapw/OmniRoute/pull/11629)) +- **feat(dashboard):** the `/dashboard/orchestration` snapshot hook now subscribes to the `agents` WebSocket channel (`agent.task.updated`) instead of `requests` as its refetch trigger, and relaxes its background poll from 5s to 30s while that WS connection is up — falling back to the tighter 5s cadence, reprogrammed live on any connect/disconnect transition, whenever the socket is down. ([#12409](https://github.com/diegosouzapw/OmniRoute/pull/12409)) +- **feat(dashboard):** Orchestration canvas quick wins — search box plus state/source/provider filter chips with a one-click clear, and per-source collapse/expand, all reflected in the URL so a filtered/collapsed view is shareable and survives a refresh; the detail drawer gained a "copy trace JSON" action and hardened error/empty-state and accessibility handling; the Agents-tab edges now animate traveling particles along active (running) connections; and the canvas node/edge status colors moved off fixed hex values onto theme-aware `--orch-status-*` CSS custom properties, so they adapt correctly to light/dark mode. ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- **chore(dashboard):** Orchestration UI hardening pass and the missing component/model test coverage it called for — `OrchestratorNode`/`ActivityNode`/`OverflowNode` rendering, the `?node=`/overflow-click page routing, the Agents-tab orchestrator-click no-op and `showCompleted` toggle, and the overview kanban's done-column sort order (#12270, #12271). ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- New `/dashboard/orchestration` page: live unified view of everything running — Cloud Agent, A2A and Conductor as a real-time graph (Agents tab), the combo cascade (Routing tab, reusing the Combo Live Studio) and a state kanban (Overview tab), with a detail drawer (trace, cost, approve/cancel). Read-only over existing APIs — no new backend. Canvas concept credit: PR #11815 design ([#12261](https://github.com/diegosouzapw/OmniRoute/pull/12261)) +- **feat(dashboard):** new "History" tab on `/dashboard/orchestration` — an Airflow-style grid of finished runs over a 24h/7d/30d preset window, one row per (source, identity), clicking a cell opens the existing detail drawer. It is backed by real persistence: A2A task lifecycle transitions are now written to the `a2a_tasks` table (purged after 30 days, configurable via `OMNIROUTE_A2A_HISTORY_RETENTION_DAYS`) and served by the new `GET /api/a2a/tasks/history` listing endpoint, with the task-detail route falling back to persisted history once a run leaves the in-memory snapshot. Conductor runs stay remote and are not persisted locally — the tab says so instead of silently omitting them. ([#12479](https://github.com/diegosouzapw/OmniRoute/pull/12479)) +- **feat(dashboard):** the orchestration detail drawer gained a "Repeat" action for Cloud Agent, A2A and Conductor tasks — a two-click confirm (click once to arm, click again within the confirm window to fire) re-submits the original prompt/input as a new run. The button is disabled with an explanatory tooltip whenever the original input can't be recovered from the loaded task detail (e.g. it never carried a prompt, or the detail failed to load). Two limitations of the A2A variant, by design: it targets the `/a2a` JSON-RPC endpoint, which authenticates with an API key only (`REQUIRE_API_KEY=true` or a configured `OMNIROUTE_API_KEY` makes a dashboard-session repeat answer `HTTP 400` — surfaced verbatim in the drawer's error line, never as a success), and the `message/send` call is SYNCHRONOUS: the POST blocks for the whole skill run, so the success confirmation only appears once the repeated task finishes. A dashboard-authenticated A2A creation path is deliberately left to a follow-up — widening the endpoint's auth posture is an operator decision, not a side effect of this feature. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(a2a):** A2A task execution now records which memories were consulted for the task's last user message as `metadata.memoryHits` (id/key/type/content-snippet) plus a `memory_hits` history event, purely for observability — the retrieved memory is never injected into a skill's prompt or behavior. Gated by the `OMNIROUTE_A2A_MEMORY_HITS` kill-switch (default enabled; set to `0` to skip the recall lookup entirely). The drawer's new "Memory used" section lists these hits for a2a tasks and is omitted whenever there are none. Known limitation: recall only resolves under the keyless posture — a keyed caller's task owner is a SHA-256 prefix of the API key, while memory rows are keyed by the database api-key id, and no hash→id lookup exists today, so the hit list stays empty for keyed callers. The recorded hits are also kept out of the task's own `input` (and therefore out of the persisted input and of the "Repeat" request body), so repeating a task never re-sends the previous run's memory snippets. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(providers):** add a Perplexity Agent API provider (`perplexity-agent` / `pplx-agent`) for Perplexity `/v1/responses`, including the documented Anthropic, OpenAI, Google, xAI, DeepSeek, Z.AI, Moonshot/Kimi, NVIDIA, and Perplexity model IDs plus Anthropic-model `max_output_tokens` compatibility. ([#12103](https://github.com/diegosouzapw/OmniRoute/pull/12103)) — thanks @AIB1TAL0S +- **feat(providers):** add RPD (Requests Per Day) limit to provider rate limit overrides across UI, schemas, DB, and i18n ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) — thanks @b3nw +- **feat(providers): add UC Direct (uncensored.com Developer API), the metered OpenAI-compatible surface.** A standard OpenAI-compatible passthrough (default executor) for uncensored.com's official REST API at `https://api.uncensored.com/api/v1`: `X-api-key` auth (never-expiring `uai_sk_live_` key), `POST /chat/completions` with streaming SSE and native tool-calling, and the full live metered catalog (82 models across 15 providers, discovered from the public `GET /v1/models`). Registered as provider `uc-direct` (alias `ucd`). Complements the un-metered `uc` persona provider — same models, metered credits and a plain API key instead of a subscription session. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(providers): add UC (uncensored.com), the un-metered subscription "persona" chat as an OpenAI-compatible provider.** A WebSocket web-app port: a durable Clerk credential mints a short-lived session token per connect (browserless — no API key), driving UC's persona socket. Ships the browserless email-code login (request → verify → harvest), the 19 verified persona models (Claude Opus, Gemini, Grok, GLM, Kimi, DeepSeek, MiniMax, incl. the uncensored variants), prompted `` tool-calling with a per-model code-style dialect + auto-cure retry for guardrailed models, live ``/reasoning split, streaming + non-streaming OpenAI responses, and full quota/auth error surfacing (paywall / message-limit / rate-limit → 429, invalid session → 401 re-login). Full multimodal parity via the persona blob-upload layer: **vision** (image input, 15 vision-capable models), **document RAG** (PDF/doc upload, server-side extraction), **image generation** (22 models), **video generation** (14 models, async signed-url → poll), and **TTS** (streaming MP3). Registered as provider `uc` (alias `ucn`). The metered OpenAI-compatible Developer API is a separate `uc-direct` provider. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(logs):** add copy-all button to request detail modal ([#11083](https://github.com/diegosouzapw/OmniRoute/pull/11083)) — thanks @AndrianBalanescu +- **feat(check):** make serviceKinds required and add the reverse-walk provider consistency gate ([#11392](https://github.com/diegosouzapw/OmniRoute/pull/11392)) — thanks @Tushar49 +- **feat(providers):** only advertise verified-working connections by default ([#11448](https://github.com/diegosouzapw/OmniRoute/pull/11448)) — thanks @alltomatos +- **feat(resilience):** add hierarchical concurrency admission ([#11493](https://github.com/diegosouzapw/OmniRoute/pull/11493)) +- **feat(providers):** derive + surface expiry for JWT-bearing web cookies (#11497) ([#11505](https://github.com/diegosouzapw/OmniRoute/pull/11505)) — thanks @oyi77 +- **feat(tunnels):** support named cloudflared tunnels via CLOUDFLARED_CONFIG ([#11561](https://github.com/diegosouzapw/OmniRoute/pull/11561)) — thanks @ggiak +- **feat(compression):** make proactive context-compression threshold a live setting ([#11564](https://github.com/diegosouzapw/OmniRoute/pull/11564)) — thanks @ragnar-claude +- **feat(resilience):** add provider peak-hour protection ([#11622](https://github.com/diegosouzapw/OmniRoute/pull/11622)) — thanks @Neuron-Mr-White +- **feat(providers):** add 1min.ai provider ([#11631](https://github.com/diegosouzapw/OmniRoute/pull/11631)) — thanks @zero-executioner +- **feat(dashboard):** collapsible JSON tree viewer for request/response payloads ([#11703](https://github.com/diegosouzapw/OmniRoute/pull/11703)) — thanks @hartmark +- **feat(routing):** add LiquidAI LFM2.5-2.6B free tier via OpenRouter ([#11752](https://github.com/diegosouzapw/OmniRoute/pull/11752)) — thanks @brick30llc-ctrl +- **feat(pricing):** add GLM-5.3-Flash pricing, model specs, and catalog registration ([#11830](https://github.com/diegosouzapw/OmniRoute/pull/11830)) — thanks @AndrianBalanescu +- **feat(routing):** add relayMode for schema-locked context handoffs ([#11839](https://github.com/diegosouzapw/OmniRoute/pull/11839)) — thanks @raheemuddin786 +- **feat(config):** add support for runtime system prompt configuration and hot-reloading ([#11841](https://github.com/diegosouzapw/OmniRoute/pull/11841)) — thanks @raheemuddin786 +- **feat(routing):** expand connection-aware quota prefilter across combo strategies (#11682) ([#11850](https://github.com/diegosouzapw/OmniRoute/pull/11850)) — thanks @HouMinXi +- **feat(models):** reactive discovery sync after Antigravity model-not-found 404 ([#11888](https://github.com/diegosouzapw/OmniRoute/pull/11888)) — thanks @MumuTW +- **feat(models):** auto-aliases and token limits from synced Antigravity discovery ([#11889](https://github.com/diegosouzapw/OmniRoute/pull/11889)) — thanks @MumuTW +- **feat(settings):** raise sticky round-robin limit caps to 1000 ([#12015](https://github.com/diegosouzapw/OmniRoute/pull/12015)) — thanks @adivekar-utexas +- **feat(resilience):** operator-configurable global credential health check interval ([#12043](https://github.com/diegosouzapw/OmniRoute/pull/12043)) — thanks @adivekar-utexas +- **feat(quota):** use official OpenCode Go usage API ([#12124](https://github.com/diegosouzapw/OmniRoute/pull/12124)) — thanks @ddarkr +- **feat(resilience):** default the credential health check sweep to 60 minutes ([#12138](https://github.com/diegosouzapw/OmniRoute/pull/12138)) — thanks @adivekar-utexas +- **feat(quality):** new-code mode for the complexity and dead-code ratchets (Clean as You Code) ([#12142](https://github.com/diegosouzapw/OmniRoute/pull/12142)) +- **feat(dashboard):** orchestration canvas — unified model + snapshot hook (part 1/2) ([#12156](https://github.com/diegosouzapw/OmniRoute/pull/12156)) +- **feat(usage):** add Kilo Code balance and Kilo Pass quotas ([#12178](https://github.com/diegosouzapw/OmniRoute/pull/12178)) — thanks @killer30001000 +- **feat(providers):** refresh vendored ChatGPT Web connector to v4.0.7 ([#12181](https://github.com/diegosouzapw/OmniRoute/pull/12181)) — thanks @backryun +- **feat(providers):** manual "Clear cooldown" action in the cooling panel ([#12224](https://github.com/diegosouzapw/OmniRoute/pull/12224)) — thanks @patrykkopycinski +- **feat(providers):** restore ChatGPT Web via clean-room browser transport ([#12239](https://github.com/diegosouzapw/OmniRoute/pull/12239)) — thanks @backryun +- **feat(sse):** wire the PROVIDER_PROFILES window gate into the global provider cooldown ([#12247](https://github.com/diegosouzapw/OmniRoute/pull/12247)) +- **feat(providers):** modernize CLOVA Studio chat and embeddings ([#12277](https://github.com/diegosouzapw/OmniRoute/pull/12277)) — thanks @backryun +- **feat(browser):** adopt Obscura as primary headless browser engine with Chromium fallback ([#12286](https://github.com/diegosouzapw/OmniRoute/pull/12286)) — thanks @opensource-elearning +- **feat(sse):** full language parity for output styles (es/de/fr/it/ru/zh + autoDetect) ([#12289](https://github.com/diegosouzapw/OmniRoute/pull/12289)) +- **feat(quality):** complete test:scoped — --full map rebuild, stdin selection, CI loader parity (#8084 D1) ([#12353](https://github.com/diegosouzapw/OmniRoute/pull/12353)) +- **feat(providers):** refresh Fable, Cursor, and Devin catalogs ([#12367](https://github.com/diegosouzapw/OmniRoute/pull/12367)) — thanks @backryun +- **feat(video):** redact transcript text from logs and durable memory (#12150 P1) ([#12427](https://github.com/diegosouzapw/OmniRoute/pull/12427)) +- **feat(dashboard):** parent-link, genuine-continuation badge, and modal perf fixes ([#12448](https://github.com/diegosouzapw/OmniRoute/pull/12448)) — thanks @hartmark +- **feat(dashboard):** adaptive context-budget dial on compression panel ([#12488](https://github.com/diegosouzapw/OmniRoute/pull/12488)) — thanks @HouMinXi +- **feat(providers):** refresh Z.ai Web models and browser transport ([#12524](https://github.com/diegosouzapw/OmniRoute/pull/12524)) — thanks @backryun +- **feat(video):** redact raw client-snapshot transcript fields in the detailed log (#12150 P2) ([#12528](https://github.com/diegosouzapw/OmniRoute/pull/12528)) +- **feat(providers):** refresh NVIDIA hosted models ([#12538](https://github.com/diegosouzapw/OmniRoute/pull/12538)) — thanks @backryun +- **feat(quota):** Moonshot Open Platform balance and TPD lock for custom nodes ([#12590](https://github.com/diegosouzapw/OmniRoute/pull/12590)) — thanks @HouMinXi +- **feat(video):** redact transcript in the in-memory pending-request snapshot (#12430 item 6) ([#12596](https://github.com/diegosouzapw/OmniRoute/pull/12596)) +- **feat(dashboard):** link a log entry's Conversation Context to its owning conversation ([#12646](https://github.com/diegosouzapw/OmniRoute/pull/12646)) — thanks @hartmark +- **feat(catalog):** eligibility-gated free-tier bucket ([#12669](https://github.com/diegosouzapw/OmniRoute/pull/12669)) +- **feat(db):** fail-closed previous_response_id continuation for redacted video turns (#12150 P2b) ([#12707](https://github.com/diegosouzapw/OmniRoute/pull/12707)) +- **feat(gamification): pay the documented `streak_bonus` and `badge_unlock` XP rewards.** `XP_REWARDS` listed both rewards but the award pipeline never paid them: the private reward table in `events.ts` omitted them, `updateStreak()` did not report when a streak extended, and badge unlocks carried no XP. Every request that extends a daily streak now pays `streak_bonus × streak length` once per UTC day (guarded by a same-day `xp_audit_log` check), and every badge unlocked through the pipeline pays `badge_unlock` once per badge (guarded by the `user_badges` primary key; `unlockBadge()` now reports whether it inserted). Bonus XP flows through the same `addXp` + level sync + global/weekly/monthly leaderboard path as action XP, so level-ups and rankings include it. The Radar supporter recognition unlock stays XP-free. (#12522 — thanks @pacocartones) +- **feat(providers):** advertise a `free-tier` capability in the provider plugin manifest for every provider with documented free models, so sidecars and dashboards can filter free-capable providers without reading the quota catalog ([#12786](https://github.com/diegosouzapw/OmniRoute/pull/12786)) — thanks @maxmad64bis +- **feat(providers):** Added EURouter as an OpenAI-compatible API-key gateway (`https://api.eurouter.ai/v1`), with live model discovery via `passthroughModels`. Its copy states that models are served by third-party upstreams listed per model, so an EU-based router is not read as EU data residency for inference. ([#13025](https://github.com/diegosouzapw/OmniRoute/pull/13025)) — thanks @ntdatt812 +- **feat(providers):** Added GreenPT as an OpenAI-compatible API-key provider (`https://api.greenpt.ai/v1`), with live model discovery via `passthroughModels`. No free-inference badge: the published docs describe a free API subscription billed per token, not a free tier. ([#13024](https://github.com/diegosouzapw/OmniRoute/pull/13024)) — thanks @ntdatt812 +- **feat(i18n):** 9 new locales — Greek (`el`), Croatian (`hr`), Serbian (`sr`), Lithuanian (`lt`), Estonian (`et`), Latvian (`lv`), Slovenian (`sl`), Maltese (`mt`), Irish (`ga`) — across the dashboard, docs mirrors, CLI, README and the site; OmniRoute now ships every official EU language (51 locales). (#13044) +- **feat(i18n):** 8 new locales — Kannada (`kn`), Malayalam (`ml`), Odia (`or`), Punjabi (`pa`), Nepali (`ne`), Sinhala (`si`), Burmese (`my`), Khmer (`km`) — across the dashboard, docs mirrors, CLI, README and the site (59 locales). The translator now restores the ICU literal escape around angle placeholders and splits oversized docs sections before translating. (#0000) ([#13660](https://github.com/diegosouzapw/OmniRoute/pull/13660)) +- feat(providers): **list Agnes 3.0 Flash as the current free chat model, drop retired 1.5 Flash, add Image 2.0/2.5 Flash plus Video 2.5/2.5 Flash, and discover the live `/v1/models` catalog (including the CN host `api.agnes-ai.cn`).** `agnes-1.5-flash` now forwards to `agnes-3.0-flash`. Video 2.5 polls `GET /v1/videos/{id}` (not the V2.0 `/agnesapi` contract). Live `/v1/models` (2026-09-09) no longer serves 1.5; the wiki marks it deprecated. 3.0 Flash is 512K context / 65,536 max output, same window as 2.5. CN-region keys use the existing per-connection base-URL field, default stays `apihub.agnes-ai.com`. ([#13120](https://github.com/diegosouzapw/OmniRoute/pull/13120)) — thanks @HouMinXi +- **feat(sse):** Codex and OpenAI catalogs list GPT-6 Astra with effort aliases (`-low` through `-ultra`); Codex CLI identity pins `@openai/codex@0.153.4` in lockstep with the image ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **feat(dashboard):** the orchestration History tab gained a "Compare runs" mode — toggling it turns each grid cell into a 2-item selection queue (a 3rd click drops the oldest pick), and picking two cells opens a side-by-side comparison panel instead of the usual detail drawer. The panel fetches both runs' detail the same way the drawer does (falling back to persisted history once a run leaves the live TTL window) and shows, per side: identity/source/state, start time, a signed `right - left` delta for duration/cost/event count, the event timeline aligned by index, and any memory hits. One side's fetch failing never blocks the other, and a delta is only ever computed when both sides have a finite value — otherwise it renders "—", never `NaN`. Comparing two runs from different sources or skills still works; a banner marks the deltas as informational rather than hiding them, since the two runs aren't a strict apples-to-apples pair. ([#12677](https://github.com/diegosouzapw/OmniRoute/pull/12677)) +- **feat(dashboard):** badge a conversation that never reached a clean stop ([#12717](https://github.com/diegosouzapw/OmniRoute/pull/12717)) — thanks @hartmark +- **feat(release):** reconcile-changelog tool + version-anchored fragment aggregation ([#12987](https://github.com/diegosouzapw/OmniRoute/pull/12987)) +- **feat(mcp):** carry context_length through MCP list_models_catalog projection (#12776) ([#13406](https://github.com/diegosouzapw/OmniRoute/pull/13406)) — thanks @KooshaPari +- **feat(cli):** make startup readiness budget configurable (#13369) ([#13433](https://github.com/diegosouzapw/OmniRoute/pull/13433)) — thanks @KooshaPari + ### 🐛 Bug Fixes +- **security(streaming):** sanitize generic mid-stream error messages before emitting OpenAI, + Responses, or Claude SSE failure frames and before diagnostic logging, while preserving raw + failures for internal classification and keeping client disconnects out of provider failure state. ([#12457](https://github.com/diegosouzapw/OmniRoute/pull/12457)) +- **fix(claude):** `blockExtraUsage: false` no longer lets 5h quota preflight skip the account; extra usage is billed after the session bar is gone, so the request must reach Anthropic ([#12803](https://github.com/diegosouzapw/OmniRoute/pull/12803)) — thanks @HouMinXi +- Sanitize HuggingChat conversation-creation and message-send transport failures before they reach client error bodies or provider logs. ([#12467](https://github.com/diegosouzapw/OmniRoute/pull/12467)) +- **fix(dashboard):** Keep local and theme-aware provider SVG icons at a definite layout size so Chromium does not collapse them to 0×0 after the v3.8.50 image-rendering change ([#12054](https://github.com/diegosouzapw/OmniRoute/pull/12054)) — thanks @ponkcore +- **fix(security):** Sanitize provider and runtime failures before public API, SSE and MCP responses and before persistent request, proxy and usage logs, preventing credentials, stack traces and host filesystem paths from crossing those boundaries while preserving stable error codes and useful diagnostics. ([#12506](https://github.com/diegosouzapw/OmniRoute/pull/12506)) +- **fix(cli):** `omniroute tunnel create` no longer crashes with `Cannot read properties of undefined (reading optsWithGlobals)` — removed the duplicate positional argument that caused Commander.js to misalign the action callback parameters ([#12295](https://github.com/diegosouzapw/OmniRoute/issues/12295)) ([#12368](https://github.com/diegosouzapw/OmniRoute/pull/12368)) — thanks @gonisulaimann +- Stop advertising Gemini Live-only models as supported audio endpoints until OmniRoute proxies the bidirectional Live protocol. ([#11710](https://github.com/diegosouzapw/OmniRoute/pull/11710)) — thanks @RaviTharuma +- **fix(kie):** reroute `flux/kontext` off the KIE Market `createTask` flow — it is catalogued with `isMarket: true` but has no Market catalog page, so KIE rejected it with "model name not supported"; it now hits the dedicated `POST /api/v1/flux/kontext/generate` / `GET /api/v1/flux/kontext/record-info` endpoints instead (#11296). ([#11985](https://github.com/diegosouzapw/OmniRoute/pull/11985)) — thanks @hartmark +- **fix(kie):** correct 12 more KIE Market catalog ids that were sent to `createTask` unchanged but diverge from KIE's documented upstream `model` values — GPT Image 2 T2I/I2I (drops the `gpt/` prefix), GPT Image 1.5 T2I/I2I (`gpt-image/` namespace), Seedream 5.0 Lite T2I/I2I (drops the `.0`), all 4 Flux 2 variants (`flux-2/` namespace, generic variant renamed `flex`), and Wan 2.7 Image / Image Pro (dash instead of dot) — each verified individually against the literal example request published on docs.kie.ai. `#11326`'s "everything else already matches" claim was wrong a second time (#11296); `z-image/4.0-*`/`z-image/4.5-*` and `flux/kontext` remain open, documented as unresolved in `KIE_MARKET_UPSTREAM_MODEL_IDS`'s comment pending further verification. ([#11667](https://github.com/diegosouzapw/OmniRoute/pull/11667)) +- **fix(dashboard):** `useApiKeySave.handleSaveApiKey` no longer forces a full upstream `/models` catalog sync on every non-curated provider connection save — callers can now pass `skipModelSync: true` to opt out, so a workflow that only wants to add one manual model no longer floods the provider's available-models list with hundreds/thousands of synced entries. The flag is a client-side intent signal only and is stripped before the connection payload is POSTed to `/api/providers`; default behavior (full sync on save) is unchanged when the flag is omitted (#11324) ([#11664](https://github.com/diegosouzapw/OmniRoute/pull/11664)) +- **Provider connections:** keep `tokenExpiresAt` when a connection is created. The create-path allowlist omitted it, so every insert stored NULL and the dashboard token badge could read a fresh connection as expired until its first background refresh ([#11368](https://github.com/diegosouzapw/OmniRoute/pull/11368)). — thanks @ntdatt812 +- **fix(combos):** the combo builder's precision-select, global-model-search, and manual-entry flows now serialize a model step's `model` string using the provider's already-computed routing-alias prefix (e.g. `oc/`) instead of rebuilding it from the raw canonical `providerId`, fixing the no-auth "OpenCode Free" provider (`opencode`) being routed to the unrelated paid "OpenCode Zen" provider (`opencode-zen`) because `opencode` doubles as a manual routing-prefix override ([#11433](https://github.com/diegosouzapw/OmniRoute/issues/11433)). ([#11670](https://github.com/diegosouzapw/OmniRoute/pull/11670)) +- **fix(radar):** the catalog feed cache now keeps `generatedAt`, the date the feed's data was built, next to `fetchedAt`, the date this install downloaded it (#11435). The feed schema requires that date and the sync path validates it, but the cache dropped it — so a feed fetched minutes ago and one carrying weeks-old figures looked identical to everything downstream, including the dashboard's "Last fetched" line. `getRadarCatalog().meta` and `GET /api/radar/status` now report both dates, the latter as its own field rather than folded into `version` — and omitted entirely for the offers and intel caches, which keep no build date, where a `null` would read as "unknown" rather than "never stored". The dashboard still shows only the fetch time; surfacing the build date there needs a new translated label and is left to a follow-up. Rows cached before migration 163 read back as `null`: unknown stays unknown instead of borrowing the fetch time. The referrals cache has persisted the same date since migration 142. — thanks @maxmad64bis +- **fix(cli):** postinstall no longer fills `JWT_SECRET` and `API_KEY_SECRET` in the installed package's `.env` (#11436). `.env.example` ships both blank on purpose: the server restores them from its durable store, or generates and persists them there, in `ensureSecrets()`. Pre-filling them defeated that — the file lives inside the package directory, so `npm i -g` replaced it and postinstall wrote _different_ values, while `ensureSecrets()` (which only acts on an empty variable) never got to restore the real ones. Both secrets rotated silently on every update, invalidating dashboard sessions and API-key CRCs. `STORAGE_ENCRYPTION_KEY` left the same list for the same reason in #1622; its comment pointed at a function that no longer exists and now names the real provisioning path. — thanks @maxmad64bis +- **fix(cli):** the CLI now says when a loaded `.env` lives inside the installed package directory (#11437). It already announces every env file it reads, without distinguishing the ones that survive an update from the one that does not: `npm i -g` replaces the package directory wholesale, so values set there are gone at the next update, silently. The warning names the durable path to move them to, and fires only when that file actually supplied a value — a file entirely shadowed by a durable one supplied nothing. A development checkout stays silent: there the same path is stable and documented in `SETUP_GUIDE.md`. — thanks @maxmad64bis +- **fix(free-models):** the shared `isFreeModel()` predicate no longer reports catalog entries marked `freeType: "discontinued"` as free, so `hidePaidModels` can't route to Pollinations' seven premium models that now require a paid key ([#11441](https://github.com/diegosouzapw/OmniRoute/pull/11441)) — thanks @maxmad64bis +- fix(ui): let AnySearch use the normal provider-icon fallback when LobeHub has no matching icon (#11449) +- Fixed the v3.8.50 Costs and Analytics dashboards so flat-rate Claude Code usage can be shown as an explicitly requested token-price estimate without changing default billed-cost semantics. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed archived usage retention so each request is priced individually instead of pricing a day's summed tokens once, which understated archived cost whenever a day mixed cache-heavy and ordinary requests. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed the Costs dashboard so it discloses when displayed figures include flat-rate token-price estimates instead of labelling them as billed spend, using the flag the analytics API already returns; the month-end projection and the CSV/JSON exports carry the same marker, and billed-cost mode is unchanged. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- **fix(combo):** attach the same combo-diagnostics trace (`poolSize`/`attemptOrder`/`excluded`/`terminalReason`, plus `x-omniroute-combo-*` headers) to the round-robin strategy's and the nested pipeline/fusion runtime-unit loop's "Maximum combo retry limit reached" 503 that the priority-strategy path already attaches for the identical terminal condition — previously those two paths returned a bare, contextless 503 ([#11462](https://github.com/diegosouzapw/OmniRoute/issues/11462)). ([#11665](https://github.com/diegosouzapw/OmniRoute/pull/11665)) +- **fix(bun):** use native `bun:sqlite` in `bootstrap-env` and `sync-env` to avoid loading `better-sqlite3` N-API addon during Bun startup ([#11468](https://github.com/diegosouzapw/OmniRoute/pull/11468)) — thanks @TheDemonTuan +- **fix(docker-bun):** make `Dockerfile.bun` install reliable with pre-install script helpers and native `bun:sqlite` smoke check ([#11470](https://github.com/diegosouzapw/OmniRoute/pull/11470)) — thanks @TheDemonTuan +- **fix(docker-bun):** remove every vendored `better-sqlite3` native addon from the Bun runtime image so startup cannot abort before the `bun:sqlite` fallback ([#11482](https://github.com/diegosouzapw/OmniRoute/pull/11482)) — thanks @TheDemonTuan +- **fix(autoCombo):** effort/alias model variants (`gpt-5.6-sol-xhigh`, `gpt-5.6`, cursor's `claude-4.6-opus-high`) inherit their base model's task fitness instead of falling to the wildcard 0.5 ([#11492](https://github.com/diegosouzapw/OmniRoute/pull/11492)) — thanks @MumuTW +- fix(db): rate-limit repeated Arena ELO leaderboard fetch-failure warnings instead of logging one per sync attempt (#11500) ([#11989](https://github.com/diegosouzapw/OmniRoute/pull/11989)) — thanks @hartmark +- **fix(db):** dedupe the raw `[Encryption] Decryption failed...` log line emitted by the lazy-decrypt views (`createLazyRowProxy`/`createLazyConnectionView`), which power `getProviderConnections()` and were re-triggering that line on every CredentialHealth/model-sync cycle for the same corrupt or stale-key credential — a fresh Proxy over a fresh row on every cycle meant the per-proxy memoization never suppressed it, unlike the dedup `decryptConnectionFields()` already had since [#9927](https://github.com/diegosouzapw/OmniRoute/issues/9927). Now shares that dedupe tracking so the line logs at most once per credential ([#11500](https://github.com/diegosouzapw/OmniRoute/issues/11500)). ([#11678](https://github.com/diegosouzapw/OmniRoute/pull/11678)) +- **fix(intelligence):** Arena ELO sync stores a synthesized base-model row for every effort/harness variant (`claude-opus-5-max` → `claude-opus-5`, `gpt-5.6-sol-xhigh (codex-harness)` → `gpt-5.6-sol`), so bare model requests reach the synced intelligence layer instead of the static fallback table; the hand-maintained `MODEL_ALIAS_MAP` — which collapsed generations (`gpt-5.5` → `gpt-5`) — is gone ([#11506](https://github.com/diegosouzapw/OmniRoute/pull/11506)) — thanks @MumuTW +- **fix(autoCombo,sse):** catalog hygiene — the static task-fitness table no longer ranks retired models above live ones (dead rows dropped, patterns matched on segment boundaries), the 7 deprecation aliases that forwarded to retired or non-existent models now point at the vendors' published replacements, an alias is no longer rewritten when the serving provider offers the id as-is, and a new `check:model-lifecycle` gate diffs both tables against a vendor lifecycle snapshot ([#11507](https://github.com/diegosouzapw/OmniRoute/pull/11507)) — thanks @MumuTW +- **fix(sse):** stop dropping resolved `thoughtSignature` values on parallel (multi tool-call) turns sent to Gemini 3.x — the claude→gemini and openai→gemini translators previously kept the signature only on the *first* function call of a message, causing Gemini to reject subsequent calls in the same turn with HTTP 400 "Function call is missing a thought_signature"; each function call now keeps its own resolved signature ([#11510](https://github.com/diegosouzapw/OmniRoute/issues/11510)). ([#11669](https://github.com/diegosouzapw/OmniRoute/pull/11669)) +- Treat Docker publishing from a non-default release branch as an expected skip instead of a failed workflow, while preserving the guard that prevents it from overwriting the `next` channel. ([#11527](https://github.com/diegosouzapw/OmniRoute/pull/11527)) — thanks @pacocartones +- fix(search): prefer credentialed providers over duckduckgo-free fallback (#11524) — the fallback-only loop ran before the credentialed-providers loop in `executeWebSearch`, making configured providers unreachable when `duckduckgo-free` was available. ([#11565](https://github.com/diegosouzapw/OmniRoute/pull/11565)) — thanks @vsd2807 +- **fix(sse):** cap the upstream headers-wait phase for STREAMING requests to a client-realistic ceiling (110s, under Codex's own ~120s hard client-abort window) instead of the flat 10-minute `FETCH_TIMEOUT_MS` default — that default was 5x longer than the body-phase readiness watchdog's own adaptive bound, so a request whose upstream never returned any response at all (not even headers, e.g. a stalled NVIDIA target behind a tool-heavy Responses→Chat translation) kept the client connection alive on keepalives only, guaranteeing the client's own patience ran out first with an opaque 499 instead of OmniRoute detecting and failing the stall fast. Non-streaming requests are unaffected — they keep the existing flat default (`open-sse/utils/fetchStartTimeoutPolicy.ts`) (#11526) ([#11668](https://github.com/diegosouzapw/OmniRoute/pull/11668)) +- **fix(ci):** protocol-clients E2E harness boots the peer-stamped custom server (`run-next.mjs dev`) instead of the bare `next` CLI, so `LOCAL_ONLY` locality resolves from the real TCP peer stamp and `/api/mcp/audit` answers 200 (open bootstrap preserved via an `OMNIROUTE_E2E_BOOTSTRAP_MODE=open` credential clear in `run-next.mjs`, pinned to loopback `HOST`) instead of a deterministic 403 (#11535) ([#11549](https://github.com/diegosouzapw/OmniRoute/pull/11549)) — thanks @jonlwheat2-gif +- **fix(docs):** the Auto-Combo engine is described with the number of factors it actually declares. Four different counts were in circulation — 6, 9, 13 and 14 across `AGENTS.md`, four guides and six code comments — while `DEFAULT_WEIGHTS` declares 15. `check:docs-counts` now reads that number from the source, so adding a factor without updating the prose fails CI ([#11545](https://github.com/diegosouzapw/OmniRoute/pull/11545)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **fix(quota-share):** in-flight leases are per request, so an aborted request's slot ages out instead of being kept alive by later traffic on the same connection ([#11547](https://github.com/diegosouzapw/OmniRoute/pull/11547)) — thanks @abhisheksharma2411 +- The free-tier summary route now serves the refreshed Radar catalog when the feed is active — the same numbers the dashboard shows — and states which catalog answered plus its real build date, instead of always reporting release-frozen figures with a stale curation date. The supporter-key live feed stays reserved to authenticated callers of the instance. ([#11550](https://github.com/diegosouzapw/OmniRoute/pull/11550)) — thanks @maxmad64bis +- **sse:** fix LiveWS/embed-WS servers crashing at startup under the Node/tsx runtime — `liveServer.ts`, `embedWsProxy.ts` and `apiBridgeServer.ts` imported `@/shared/utils/httpClientAbortGuard` without the `.mjs` extension, so the client-abort crash guard added by [#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556) was unreachable and every dependent test failed with `ERR_MODULE_NOT_FOUND` ([#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556)). ([#11671](https://github.com/diegosouzapw/OmniRoute/pull/11671)) — thanks @jonlwheat2-gif / @yxyxy +- fix(chat): shed any of the matched provider-node's routing identifiers (public prefix or internal id) before model lookup, so composites like `//` no longer reach upstream verbatim (#11557) — thanks @maxmad64bis +- **fix(cache):** a semantic-cache entry now expires on its TTL instead of surviving until the next UTC midnight, and `dbEntries` stops counting expired rows ([#11573](https://github.com/diegosouzapw/OmniRoute/pull/11573)) — thanks @ntdatt812 +- **fix(api):** `/v1/models` schedules its stale-while-revalidate rebuild through Next's `after()` again, so a stale catalog reaches the client before the rebuild blocks the event loop ([#11574](https://github.com/diegosouzapw/OmniRoute/pull/11574)) — thanks @ntdatt812 +- **fix(check):** the `known-symbols` executor conformance gate awaits the now-async `getExecutor()`, so it stops reporting all 142 registered aliases as dead symbols and can detect a lazy import that fails ([#11582](https://github.com/diegosouzapw/OmniRoute/pull/11582)) — thanks @ntdatt812 +- **fix(pack):** register `bin/cli/utils/volatileEnvPath.mjs` in `PACK_ARTIFACT_REQUIRED_PATHS`, so the prune can no longer silently drop a module the published CLI imports on every boot ([#11588](https://github.com/diegosouzapw/OmniRoute/pull/11588)) — thanks @ntdatt812 +- **i18n(vi):** translate the five reliability-column strings on the free-provider rankings page, which rendered as `__MISSING__:` markers in the Vietnamese UI ([#11589](https://github.com/diegosouzapw/OmniRoute/pull/11589)) — thanks @ntdatt812 +- **resilience:** restore the expired-connection retry-budget probe in the token-health sweep — the `!isGitHubAccessTokenOnlyConnection` carve-out reintroduced by #11608 contradicted the boundary pinned by #11592, so a GitHub connection parked at `expired` with retry budget remaining was never probed and could never self-heal ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)). ([#11672](https://github.com/diegosouzapw/OmniRoute/pull/11672)) — thanks @jonlwheat2-gif / @ntdatt812 +- **fix(dashboard):** Provider Stats now displays combo success rates on their native 0–100 percentage scale instead of multiplying them twice ([#11595](https://github.com/diegosouzapw/OmniRoute/pull/11595)) — thanks @pacocartones +- **fix(providers):** route DuckDuckGo Web Chat through the verified `duck.ai` endpoint, refresh its free model catalog, and avoid forwarding unsolved anti-abuse challenges ([#11597](https://github.com/diegosouzapw/OmniRoute/pull/11597)) — thanks @solstxce +- **fix(ui):** Console log Refresh and Copy controls now expose localized accessible names, keep copy actions visible on keyboard focus, and announce copy completion safely ([#11599](https://github.com/diegosouzapw/OmniRoute/pull/11599)) — thanks @pacocartones +- Dropped the stale-`.eslintcache` `restore-keys` fallback from both "Restore ESLint file cache" steps in `ci.yml`, so the blocking `Lint` job can no longer be served per-file verdicts computed under a different lint config, suppressions file or lockfile. `quality.yml` had already dropped it in #11963; `ci.yml` — the workflow that actually gates PRs — had not (#11600). ([#11996](https://github.com/diegosouzapw/OmniRoute/pull/11996)) +- fix(ci): pass `--pass-on-unpruned-suppressions` in `run-eslint-json.mjs` so the CI Lint job no longer fails when a suppression is merely orphaned by a genuine fix, mirroring the identical fix already in `validate-release-green.mjs` (#11600) ([#11983](https://github.com/diegosouzapw/OmniRoute/pull/11983)) — thanks @hartmark +- fix(handoff): enforce provider allowlist for universal handoff (#11602) — universal handoff now skips summarization when the selected summary model's provider is not included in the configured provider allowlist. ([#11619](https://github.com/diegosouzapw/OmniRoute/pull/11619)) — thanks @vsd2807 +- **fix(dashboard):** Show a stable error state when Search Analytics returns an HTTP error or malformed data ([#11603](https://github.com/diegosouzapw/OmniRoute/pull/11603)) — thanks @pacocartones +- **test(gamification):** pin the aggregate profile level to the XP-derived semantics of #11604 — `getAggregateXp()` now derives `currentLevel` from the summed XP (`calculateLevel(sum)`), not `MAX(stored current_level)`, and the #3484 fixture levels are aligned with the XP curve ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)). ([#11676](https://github.com/diegosouzapw/OmniRoute/pull/11676)) — thanks @jonlwheat2-gif / @pacocartones +- **fix(dashboard):** Keep the Profile level and progress aligned with aggregate XP, including bounded handling for invalid totals ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)) — thanks @pacocartones +- **fix(dashboard):** Prevent locked hidden badges from revealing their icon or opening private badge details before they are earned ([#11605](https://github.com/diegosouzapw/OmniRoute/pull/11605)) — thanks @pacocartones +- **fix(dashboard):** Restore keyboard focus after shared modals close and cancel delayed autofocus during cleanup ([#11607](https://github.com/diegosouzapw/OmniRoute/pull/11607)) — thanks @pacocartones +- **fix(dashboard):** Enable Enter and Space activation for clickable data-table rows without hijacking nested controls ([#11610](https://github.com/diegosouzapw/OmniRoute/pull/11610)) — thanks @pacocartones +- **fix(autoCombo,sse):** vendor-retired catalog ids are dropped from the auto-combo candidate pool and no longer win on leftover `arena_elo` / `user_override` rows; `getModelLifecycleDecision` consults `model-lifecycle.json` (prefix-stripped) so aggregator traffic is not `untracked` for ids the snapshot already knows ([#11625](https://github.com/diegosouzapw/OmniRoute/issues/11625)) ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)) — thanks @MumuTW +- **test(chatcore):** move Codex/Claude combo fixtures off models the lifecycle guard now rejects — `gpt-5.1-codex`/`gpt-5-codex` are vendor-retired (snapshot, #11626) and `claude-3-5-sonnet-20241022` is shut down, so native-passthrough and combo-fallback tests switched to `gpt-5.6-sol` and `claude-sonnet-4.6` ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)). ([#11675](https://github.com/diegosouzapw/OmniRoute/pull/11675)) — thanks @jonlwheat2-gif / @MumuTW +- fix(opencode-plugin): stop a free-tier budget that rounds up across a magnitude from rendering as `1000K`/`1000M` in the model picker — `fmtTokens` chose its unit from the raw token count and then rounded with `toFixed(1)`, so 999,950–999,999 printed as `1000K` rather than `1M` and just under a billion printed as `1000M` rather than `1B` ([#11684](https://github.com/diegosouzapw/OmniRoute/pull/11684)) — thanks @abhisheksharma2411 +- Cap prose-derived Antigravity quota resets at the configured model cooldown maximum while preserving authoritative Retry-After headers and Google RetryInfo hints. (#11695) Thanks @pacocartones. ([#11823](https://github.com/diegosouzapw/OmniRoute/pull/11823)) +- Compare `_rebaseline_*` notes structurally in the ratchet-bank verifier so an object-valued note no longer reports itself as rewritten on an unmodified tree — the nightly `bank-ratchet-shrinks` lane had been aborting on every run since 2026-08-11, leaving 41,672 lines of earned file-size shrinks unbanked (#11702). ([#11892](https://github.com/diegosouzapw/OmniRoute/pull/11892)) — thanks @MumuTW +- **fix(build):** `prepublish.ts` bundles the ChatGPT Web (Codex) MCP bridge through `runBuildTool()` instead of spawning `npx.cmd` raw, fixing the build crash on Node ≥ 20/Windows where `.cmd` shims cannot be spawned without a shell (EINVAL) ([#11704](https://github.com/diegosouzapw/OmniRoute/issues/11704)) ([#11706](https://github.com/diegosouzapw/OmniRoute/pull/11706)) — thanks @jonlwheat2-gif +- fix(codex): keep `parallel_tool_calls:false` on the translated Codex Responses Lite path (#11707) ([#11984](https://github.com/diegosouzapw/OmniRoute/pull/11984)) — thanks @hartmark +- **fix(tests):** the new Gemini Web Images retirement test (#11708) called the now-async `getExecutor()` synchronously, throwing `TypeError: getExecutor(...).getProvider is not a function` instead of asserting Gemini Web chat stays available. Awaited to match the R0.3 executor-registry refactor (#11220). ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(search):** Allow REST and MCP web-fetch requests to select the advertised `anysearch-search` provider explicitly ([#11716](https://github.com/diegosouzapw/OmniRoute/pull/11716)) — thanks @pacocartones +- **fix(providers):** Keep overnight peak-hour protection active after midnight according to the window's configured start day ([#11718](https://github.com/diegosouzapw/OmniRoute/pull/11718)) — thanks @pacocartones +- **fix(providers):** correct all five g4f.space gateway descriptions to document proof-of-work credits or member API keys, variable limits, and the remote prompt boundary instead of promising free keyless access at five requests per minute ([#11738](https://github.com/diegosouzapw/OmniRoute/pull/11738)). +- **fix(db):** `model_capabilities` is created by a migration instead of lazily on the first models.dev sync, so a clean install and an upgraded install converge on the same schema regardless of which features have run ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(ci):** `check:install-upgrade` now fails on an `npm` install truncated by ENOSPC (npm reports it as a warning and still exits 0), authenticates its health probe so the version assertion works against the hardened health payload, frees the clean-install tree before the upgrade phase, and no longer reports a schema divergence computed from a boot that never served ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(routing):** harden session-affinity key derivation against oversized explicit IDs and empty structural-payload collisions, while recognizing Gemini and common text request shapes ([#11746](https://github.com/diegosouzapw/OmniRoute/pull/11746)) — thanks @MumuTW +- **fix(packages/browser-pool):** regenerate `package-lock.json` so the `packages/browser-pool` workspace's locked `playwright`/`@types/node` (and transitives) match its `package.json` specs, fixing cache-only/offline installs (`npm ci --offline`, Nix `buildNpmPackage`) that previously failed with `ENOTCACHED` ([#11747](https://github.com/diegosouzapw/OmniRoute/issues/11747)) — thanks @benjaminkitt ([#11784](https://github.com/diegosouzapw/OmniRoute/pull/11784)) — thanks @geekyNads +- **fix(assets):** Stop distributing 79 provider and CLI logos without verified source/license provenance, keep every provider/tool available through the internal generic icon, and prevent those IDs from falling through to an external icon CDN. ([#11750](https://github.com/diegosouzapw/OmniRoute/pull/11750)) +- **fix(providers):** retire the common `chatgpt-web` / `cgpt-web` provider because its pre-key and proof-of-work provenance could not be cleared ([#11754](https://github.com/diegosouzapw/OmniRoute/pull/11754)) — explicit chat and image requests, slash-prefixed models, persisted aliases, proxy overrides, and combo targets now fail closed with a sanitized HTTP `410` / `PROVIDER_RETIRED` before upstream dispatch. Migration 163 tombstones matching connections and invalidates their active leases without deleting history or widening API-key allowlists. The independent `chatgpt-web-codex` / `cgpt-codex` provider and its MIT-noticed vendored implementation remain available. +- **fix(usage):** quota and usage refresh no longer 409 when an exclusive lease reserves the connection ([#11758](https://github.com/diegosouzapw/OmniRoute/pull/11758)) — thanks @TheDemonTuan +- Keep the embedding registry's vector width and `embedding` type on models when a synced model exists for the same id, so `/v1/models` no longer reports registry-described embedding models widthless or untyped (#11761) — thanks @rahilmavani +- Correct `google/gemini-embedding-001` on the OpenRouter route to 3072 dimensions, the width it returns when `dimensions` is not sent (#11761) — thanks @rahilmavani +- **fix(vision-bridge):** Restrict automatic vision routing and fallbacks to models available in authoritative live provider catalogs, including aliases and effort variants ([#11767](https://github.com/diegosouzapw/OmniRoute/issues/11767)) — thanks @pacocartones ([#11813](https://github.com/diegosouzapw/OmniRoute/pull/11813)) +- **fix(providers):** reclassify Cerebras as a one-time $5 signup credit (payment method required, 30-day validity), not a recurring no-card 1M tokens/day trial ([#11773](https://github.com/diegosouzapw/OmniRoute/issues/11773)) ([#12591](https://github.com/diegosouzapw/OmniRoute/pull/12591)) — thanks @HouMinXi +- fix(cli): stop prepublish from re-rebuilding the already-built ESM-only opencode-plugin dist (#11787) ([#11990](https://github.com/diegosouzapw/OmniRoute/pull/11990)) — thanks @hartmark +- **fix(db):** Fresh installs no longer log a non-fatal `no such table: compression_run_telemetry` warning when retention cleanup runs before the lazily-created telemetry table exists ([#11802](https://github.com/diegosouzapw/OmniRoute/pull/11802)) — thanks @RaviTharuma +- fix(sse): set X-OmniRoute-Selected-Connection-Id on successful combo dispatches so downstream consumers stop falling back to an empty connection id (#11810) ([#11986](https://github.com/diegosouzapw/OmniRoute/pull/11986)) — thanks @hartmark +- **fix(services):** embedded CLIProxyAPI lifecycle checks now use public `/healthz`, while model discovery uses the configured dedicated data-plane API key instead of the management password ([#11811](https://github.com/diegosouzapw/OmniRoute/pull/11811)) — thanks @RaviTharuma +- Fix persisted model steps returning an error in the playground route simulator, and warn when structural steps cannot be simulated. ([#11862](https://github.com/diegosouzapw/OmniRoute/pull/11862)) — thanks @pacocartones +- **fix(providers):** Antigravity's dynamic mitmAlias table no longer routes `gemini-3.7-flash-{high,medium,low}` to a literal tier-suffixed upstream id just because one connected account's own discovery listed it directly — those display ids always resolve through the safe `gemini-3.7-flash-tiered` static alias, so one account's Google-provisioned access no longer 404s every sibling account of the provider ([#11824](https://github.com/diegosouzapw/OmniRoute/issues/11824), [#11651](https://github.com/diegosouzapw/OmniRoute/issues/11651)) ([#11988](https://github.com/diegosouzapw/OmniRoute/pull/11988)) — thanks @hartmark +- **fix(dashboard):** Expose Profile loading, errors, page structure, and XP progress to assistive technologies ([#11838](https://github.com/diegosouzapw/OmniRoute/pull/11838)) — thanks @pacocartones +- **fix(socks):** `Agent.connectTimeout` now bounds both SOCKS handshake and TLS connect, and `family === null` no longer falls back to `fetch-socks` ([#11842](https://github.com/diegosouzapw/OmniRoute/pull/11842)) — thanks @maxmad64bis +- **perf(compression):** OOM mitigations for large payload hashing, memoization, and token estimation ([#11844](https://github.com/diegosouzapw/OmniRoute/pull/11844) — thanks @AndrianBalanescu) +- Excluded Next.js Node File Trace manifests (`*.nft.json`) from the published npm tarball. They are build-time metadata and are never read while serving, but had grown to 668.7 MB — 61% of the package — which pushed the upload past the registry limit and made `npm publish` fail with `413 Payload Too Large`. ([#11864](https://github.com/diegosouzapw/OmniRoute/pull/11864)) +- **fix(cli):** keep the global OmniRoute server `--base-url` separate from provider-node `--endpoint` values in add, update, and validate commands ([#11860](https://github.com/diegosouzapw/OmniRoute/pull/11860)) — thanks @pacocartones +- **fix(config):** Nous Research's `Hermes-4-405B` model now displays as "Hermes 4 405B (Nous Research)" in both the provider registry and the free-model catalog, instead of the mislabelled "Hermes 4 7B" ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#11993](https://github.com/diegosouzapw/OmniRoute/pull/11993)) +- **fix(provider/nous):** inject required user= tag into Nous Research inference requests to resolve upstream 400 "missing tags" error ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#12044](https://github.com/diegosouzapw/OmniRoute/pull/12044)) +- **fix(build):** `npm run build` now fails in one second with a named package and a copy-pasteable fix when npm silently drops an externalised optional native dependency, instead of dying four minutes in with `Module not found: Can't resolve 'better-sqlite3'` ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- **fix(install):** `postinstall` no longer throws `ReferenceError: isAndroid is not defined` — failing the whole `npm install` — when the `better-sqlite3` rebuild fallback times out; the manual-fix guidance is reachable again ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- Fixed the Alibaba free-tier allowlist test that went red on its own once the shipped catalog's `validUntil` (2026-08-27) passed, leaving every PR and `main` with a failing `Unit Tests (1/8)`. The test now builds its own packs with dates it controls, and covers the expired-pack fallback that production has actually been serving. ([#11867](https://github.com/diegosouzapw/OmniRoute/pull/11867)) +- Split the npm registry upload into its own GitHub-hosted job. npm refuses `--provenance` from a self-hosted runner (`422 ... Only "github-hosted" runners are supported`), which blocked the v3.8.50 publish; the heavy verification cannot move to a hosted runner, so it now hands the proven tarball over instead. ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(ui):** the shared `DataTable` loading state no longer reads its decorative ⏳ glyph out to assistive technology, and now carries the same `role="status"` / `aria-live="polite"` / `aria-busy="true"` semantics as `PageLoading` ([#11869](https://github.com/diegosouzapw/OmniRoute/pull/11869)) — thanks @pacocartones +- **fix(api):** Bound the `timeout` accepted by `POST /api/providers/[id]/login` to the same 15s–600s window the Adobe Firefly and Conol browser-login paths already enforce, so a request can no longer pin a headful browser and the single active-login slot with an arbitrarily large poll budget. ([#11871](https://github.com/diegosouzapw/OmniRoute/pull/11871)) — thanks @pacocartones +- **fix(api):** Stop the tunnel and MITM routes from returning raw child-process error text, which disclosed host paths, binary install locations and Tailscale `tskey-*` credentials in a body some of these routes serve to non-loopback callers, and return a real 400 instead of a framework 500 when a tunnel request body fails validation. ([#11872](https://github.com/diegosouzapw/OmniRoute/pull/11872)) — thanks @pacocartones +- **fix(open-sse):** stream TTFT/ITL are sampled from a monotonic clock (`performance.now`) instead of `Date.now`, so an NTP correction or wall-clock jump can no longer inflate a genuine TTFT or produce a negative one that the `ttft >= 0` guard silently drops — protecting the router's OTel/EWMA/`usage_history`/speed-ranking signals ([#11873](https://github.com/diegosouzapw/OmniRoute/pull/11873)) — thanks @pacocartones +- **fix(providers):** xAI `reasoning_effort: "xhigh"` now reaches grok-4.6+ instead of being silently clamped to `"high"` ([#11879](https://github.com/diegosouzapw/OmniRoute/pull/11879)) — thanks @NoxzRCW +- **fix(dashboard):** the providers page no longer crashes into the error boundary when a provider id collides with an `Object.prototype` member (`constructor`, `__proto__`); icon lookups are own-property guarded ([#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)) — thanks @NoxzRCW +- **fix(skills):** injected skill tools declared in shorthand (`{"content": "string"}`) now forward valid JSON Schema, unblocking providers that validate tool schemas strictly such as Zhipu GLM on the Console Go tier ([#11881](https://github.com/diegosouzapw/OmniRoute/pull/11881)) — thanks @NoxzRCW +- **fix(api):** `POST /api/playground/simulate-route` now surfaces `combo-ref` and `provider-wildcard` persisted combo steps with a specific warning (naming the referenced combo, or the unresolved `provider/model` wildcard) instead of folding them into a generic "unsupported step" count; a `provider-wildcard` step is also now included as an unresolved target so the operator can see it is in the route (ported from [#11882](https://github.com/diegosouzapw/OmniRoute/pull/11882) — thanks @NoxzRCW). ([#11930](https://github.com/diegosouzapw/OmniRoute/pull/11930)) +- **fix(translator):** the streaming OpenAI→Claude translator keeps upstream usage, including prompt-cache tokens, when it arrives on a trailing `choices: []` chunk (Fireworks and any upstream using `stream_options.include_usage`) ([#11883](https://github.com/diegosouzapw/OmniRoute/pull/11883)) — thanks @NoxzRCW +- **fix(cli):** `omniroute update --apply` now tells you explicitly whether a running server was detected and, if so, that you must run `omniroute restart` to apply the update — it never restarted anything and previously implied the update was already live once files were installed. The dashboard's npm-mode Update flow (`/api/system/version`) now tries OmniRoute's own PID-file-managed supervisor before falling back to pm2, and reports an honest "restart required" step instead of a silent pm2-only "skipped" that read like a completed update. The server-side latest-version lookup backing the dashboard's update banner also gained `--prefer-online`, closing the same stale-npm-cache class already fixed in the CLI's own copy for #4376 ([#11885](https://github.com/diegosouzapw/OmniRoute/issues/11885)). ([#12005](https://github.com/diegosouzapw/OmniRoute/pull/12005)) +- **fix(resilience):** clear persisted LKGP pins when a target suffers connection/provider exhaustion or is skipped before dispatch due to cooldown/exhaustion/unavailability, preventing subsequent requests from repeatedly prioritizing known-dead providers ([#11911](https://github.com/diegosouzapw/OmniRoute/issues/11911)). ([#12013](https://github.com/diegosouzapw/OmniRoute/pull/12013)) — thanks @HouMinXi +- **fix(sse):** the OpenAI→Claude stream translator now defers the terminal `message_delta`/`message_stop` emission until the real usage block has arrived (or a genuine end-of-stream flush forces it) instead of emitting it immediately on `finish_reason` — previously, when the trailing usage-only chunk (`{"choices":[],"usage":{...}}`) arrived *after* the `finish_reason` chunk (the normal order for Fireworks/vLLM/Together and other `stream_options.include_usage` upstreams), the client-visible `message_delta` still carried stale/zero usage even though `state.usage` was internally corrected too late to matter (ported from [#11915](https://github.com/diegosouzapw/OmniRoute/pull/11915) — thanks @HouMinXi). ([#11933](https://github.com/diegosouzapw/OmniRoute/pull/11933)) +- **fix(sse):** `stripStore()` now forces `store=false` for stateless OpenAI-compatible Responses-API targets (unless the connection has explicitly opted in via `providerSpecificData.openaiStoreEnabled`), instead of only handling the `openai`/`agentrouter` cases — a client-supplied `store` value previously passed through untouched to backends that don't actually persist responses server-side ([#11916](https://github.com/diegosouzapw/OmniRoute/pull/11916) — thanks @HouMinXi). +- **fix(models):** custom provider-node models (synced, custom, and alias-backed) now appear under their configured prefix in the unified catalog when the operator's model-id prefix mode is canonical, instead of being dropped whenever alias-inclusion was otherwise disabled ([#11918](https://github.com/diegosouzapw/OmniRoute/pull/11918) — thanks @HouMinXi). +- **fix(models):** the unified model catalog now suppresses stale static registry models (including effort-tier variants) for any provider whose active connection has an authoritative live synced catalog, not only providers already using exclusive-synced-listing — a connection with `providerUsesAuthoritativeLiveCatalog` previously kept serving both the live-synced models and the stale static rows side by side ([#11919](https://github.com/diegosouzapw/OmniRoute/pull/11919) — thanks @HouMinXi). +- **fix(ui):** `ProviderIcon`'s three remaining unguarded lookups (`PROVIDER_ICON_ALIASES`, `LOCAL_SVG_ALIASES`, `THEMED_SVGS`) now use `Object.hasOwn()` own-property checks — a provider id such as `constructor` or `__proto__` previously resolved through the prototype chain instead of falling through to the unknown-provider CDN fallback (`getLobeProviderIcon()` itself was already guarded by [#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)); `ProviderPageHeader`'s `color` field is also now optional, matching the rest of the component's defensive typing (ported from [#11920](https://github.com/diegosouzapw/OmniRoute/pull/11920) — thanks @HouMinXi). ([#11935](https://github.com/diegosouzapw/OmniRoute/pull/11935)) — thanks @NoxzRCW +- fix(ollama): preserve multi-byte UTF-8 content split across stream chunks in the Ollama NDJSON transform, which previously corrupted CJK/emoji into U+FFFD (#11921) — thanks @pacocartones +- **fix(providers):** OrcaRouter chat requests now target `/v1/chat/completions` instead of the bare `/v1` API root, fixing the upstream `404 Invalid URL (POST /v1)` ([#11923](https://github.com/diegosouzapw/OmniRoute/pull/11923)). — thanks @echel0nn +- **fix(plugins):** deliver the `onStreamComplete` event to disk-installed plugins. The event shipped in v3.8.50 (#9669) was emitted internally but had no plugin-facing wiring, so no plugin could ever subscribe: the manifest schema silently dropped `hooks.onStreamComplete`, and the loader/manager only knew the seven legacy hooks. `onStreamComplete` is now a declarable manifest hook, wired through the loader and registered by the manager like the other hooks, and its payload carries a `requestId` so consumers can correlate the stream-completion event with the originating request ([#11934](https://github.com/diegosouzapw/OmniRoute/pull/11934)) — thanks @amaleta / @pacocartones +- **fix(db):** the Qdrant embedding-model dropdown now lists local/self-hosted providers (Ollama, LM Studio, vLLM, etc.) — an active connection is treated as "configured" when the provider allows an optional API key, not only when it has a real key or OAuth, so a running local embedding provider is no longer hidden from the picker ([#11949](https://github.com/diegosouzapw/OmniRoute/issues/11949)) ([#11995](https://github.com/diegosouzapw/OmniRoute/pull/11995)) +- **fix(providers):** Vertex AI Anthropic partner-model discovery now calls the Model Garden `v1beta1` publisher list (`/v1beta1/publishers/anthropic/models`, global) and parses its `publisherModels` envelope, so Claude models auto-synced from Vertex populate the active live catalog and route at request time instead of returning `Model '' is not available in the active live catalog` ([#11991](https://github.com/diegosouzapw/OmniRoute/issues/11991)) — thanks @fabioluissilva ([#11998](https://github.com/diegosouzapw/OmniRoute/pull/11998)) +- **fix(cli):** support `--base-url` alongside `--endpoint` in `omniroute nodes add`, `update`, and `validate` subcommands to prevent global `--base-url` shadowing issues ([#11999](https://github.com/diegosouzapw/OmniRoute/issues/11999)). ([#12033](https://github.com/diegosouzapw/OmniRoute/pull/12033)) — thanks @HouMinXi +- **fix(providers):** `cloudflare-ai` no longer refuses image content parts for every Workers AI model ([#12002](https://github.com/diegosouzapw/OmniRoute/pull/12002)) — the plain-string `content` requirement behind #2539 is carried by the _model_ schema, not by the `/ai/v1/chat/completions` endpoint (measured: an all-text part array returns 200 on `@cf/mistralai/mistral-small-3.1-24b-instruct`, `@cf/meta/llama-4-scout-17b-16e-instruct` and `@cf/meta/llama-3.3-70b-instruct-fp8-fast`, and 400 on the text-only `@cf/qwen/qwen2.5-coder-32b-instruct`). `transformRequest()` flattened every array and threw on the first non-text part (#6390), so image input was refused for vision-capable Cloudflare models that accept it. All-text arrays are still flattened — the one shape every model accepts — while an array carrying a non-text part is passed through untouched, so the attachment is still never silently dropped. Regression guards: `tests/unit/cloudflare-ai-image-parts-6390.test.ts`. — thanks @davidlinfr +- **fix(resilience):** decouple the limiter-managed execution backstop from the queue-wait budget — new `requestQueue.executionMaxWaitMs` (env `RATE_LIMIT_EXECUTION_MAX_WAIT_MS`, default 600000 = 10 min) now feeds Bottleneck's post-dispatch `expiration`, while `requestQueue.maxWaitMs` keeps its documented queue-wait semantics. Previously the queue-wait budget doubled as the execution expiration, so legitimate long-running calls on non-incremental gateways (whole generation buffered before the first upstream byte, e.g. Console Go / Command Code tiers serving GLM models) were killed mid-flight at the queue budget with a false 504 `RATE_LIMIT_EXECUTION_TIMEOUT` — the local limiter undercut the provider-aware upstream fetch-start timeouts. The surfaced 504 message now names `requestQueue.executionMaxWaitMs`; the error keeps the #4165 guarantees (disclaims an upstream timeout, preserves the Bottleneck error as `cause`, branded code + trusted provenance, classified request-scoped so combo falls back). A real queue-wait bound (the `Promise.race` around `limiter.schedule()` sketched in #9533) remains future work. (#12025) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **Call logs:** keep the `error` field when an artifact exceeds the storage cap, instead of replacing it with the omission marker. The error is the only field that says *why* a request failed and is typically ~90 bytes next to the multi-hundred-KB bodies that trip the cap, so dropping it left a size-limited row undiagnosable — a provider outage, a local timeout and an upstream 400 all rendered identically. It is now preserved at every fallback stage, truncated to 4KB if it is itself large ([#12026](https://github.com/diegosouzapw/OmniRoute/issues/12026)). ([#12095](https://github.com/diegosouzapw/OmniRoute/pull/12095)) — thanks @ntdat812 +- **fix(diagnostics):** preserve the error field (truncated to 4KB with a `[truncated: …]` suffix) in every call-log artifact size-limit fallback stage. Previously the minimal fallback replaced the error with `[omitted: call log artifact size limit exceeded]`, so an oversized artifact row showed nothing about WHY the request failed — e.g. 91 of 847 opencode-go 504 rows on one production instance were undiagnosable from the dashboard. Oversized request/response bodies are still omitted exactly as before; the error cap is independent of the payload sizes that tripped the fallback. (#12026) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **fix(sse):** OpenAI Responses clients that declare the native `web_search` tool now receive a spec-shaped `web_search_call` output item with `action.sources` alongside the preserved function-call round-trip, so search results executed through OmniRoute's own search backend are consumable by standard Responses clients (Codex, pi-web-access, …). ([#12031](https://github.com/diegosouzapw/OmniRoute/pull/12031)) — thanks @watchingdogs +- **fix(cli):** use in-thread alias resolver hooks on modern runtimes to avoid deprecation noise and improve Node.js forward compatibility ([#12073](https://github.com/diegosouzapw/OmniRoute/issues/12073)). ([#12083](https://github.com/diegosouzapw/OmniRoute/pull/12083)) — thanks @xiaoyaner0201 +- **fix(combo):** an operator-set **Agent Features → Context length** on a combo is now honored at request time. The value was persisted and advertised through `/v1/models`, but `resolveComboContextLimit()` never consulted it — so a multi-target combo whose members carry no per-model window fell through to the provider's generic `defaultContextLength` (openrouter 128000, command-code 200000) and rejected large requests with `Input exceeds context window … limit 128000` despite the combo being explicitly sized much larger. An identical single-target combo worked, because it collapses to its concrete target before the guard runs. Invalid values (0/negative/NaN/Infinity) are ignored, so the existing target → combo-min → fallback order is unchanged. ([#12090](https://github.com/diegosouzapw/OmniRoute/pull/12090)) — thanks @adivekar-utexas +- **fix(sse):** passthrough streams now estimate usage on finish when upstream closes without usage even with `stream_options.include_usage` — avoids `0 tokens / 0%` for providers that stay silent (and correctly handles trailing empty-choices usage) ([#12151](https://github.com/diegosouzapw/OmniRoute/pull/12151)) — thanks @maxmad64bis +- **fix(combos):** clearing an agent feature in the combos editor now persists — unchecking context cache protection, or emptying the system message or tool filter, sends an explicit `null` instead of dropping the field from the `PUT` body, which the update merge read as "leave unchanged" ([#12177](https://github.com/diegosouzapw/OmniRoute/pull/12177)) — thanks @foreveryh / @ntdatt812 +- **fix(memory):** self-hosted embedding endpoints now vectorize — the vector width is measured from the first embedding that comes back instead of being read from a registry that cannot describe them, so `vec_memories` is created and memories stop piling up unvectorized behind a green health check ([#12180](https://github.com/diegosouzapw/OmniRoute/pull/12180)) — thanks @kanade-hoshino / @ntdatt812 +- **fix(translator):** the leading `system` message now reaches Responses-API upstreams when its `content` is a content-part array — it was read as `typeof content === "string" ? content : ""`, so a prompt-caching client (Anthropic `cache_control`, the shape LiteLLM and the Anthropic SDK emit) had its entire system prompt replaced by an empty `instructions`. The request was still accepted with a normal `prompt_tokens` count, so the model answered with no instructions and nothing in the response said they were missing. Mid-conversation system turns already handled the array shape ([#7056](https://github.com/diegosouzapw/OmniRoute/pull/7056)); only the first one did not ([#12206](https://github.com/diegosouzapw/OmniRoute/issues/12206)). Regression guard: `tests/unit/translator-openai-responses-system-content-parts.test.ts`. ([#12207](https://github.com/diegosouzapw/OmniRoute/pull/12207)) — thanks @Gorillaz322 +- **fix(free-tier):** `/api/free-tier/summary` no longer computes its totals from a Radar feed built before the catalog the running release ships. When the cached feed is older — or carries no build date at all — the route answers from the shipped catalog, resolved through the operator's local model state so disabled and tombstoned models stay out of the numbers ([#12215](https://github.com/diegosouzapw/OmniRoute/pull/12215)). — thanks @maxmad64bis +- **fix(oauth):** Keep a Claude personal workspace and a Team organization as separate connections — they share the same email and `accountUUID`, so the email-only OAuth dedup let the second login overwrite the first account's tokens; `organizationUUID` now disambiguates them, the way `workspaceId` does for Codex ([#12222](https://github.com/diegosouzapw/OmniRoute/pull/12222)) — thanks @davidebaraldo +- **fix(resilience):** a 402 on a single paid model of a passthrough/gateway provider (e.g. `kilo-gateway`, `ollama-cloud`) no longer terminalizes the whole connection with a never-auto-recovered `credits_exhausted` status — only the paid model is locked out, so free models on the same key keep serving. 402 variant of [#3027](https://github.com/diegosouzapw/OmniRoute/issues/3027). Single-credential providers are unaffected — a 402 there is still treated as the key being genuinely out of credit ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239) / [#10616](https://github.com/diegosouzapw/OmniRoute/issues/10616)) ([#12242](https://github.com/diegosouzapw/OmniRoute/issues/12242)) — thanks @brick30llc-ctrl ([#12266](https://github.com/diegosouzapw/OmniRoute/pull/12266)) — thanks @geekyNads +- **fix(sse):** trust `finish_reason: "length"`/`"max_tokens"` over the reasoning-consumed-token ratio in response quality validation, so a reasoning model truncated below the old 90% threshold correctly fails and retries instead of returning empty content as a silent "success" ([#12262](https://github.com/diegosouzapw/OmniRoute/pull/12262)) — thanks @brick30llc-ctrl +- **fix(settings):** `PUT /api/settings/cache-config` now persists `alwaysPreserveClientCache` to the flat general settings the runtime cache-control policy actually reads; previously the value landed in the databaseSettings "cache" section and was silently ignored, so the endpoint had no effect on `cache_control` passthrough ([#12304](https://github.com/diegosouzapw/OmniRoute/pull/12304)) — thanks @davidebaraldo +- **fix(grok-cli):** treat omitted SuperGrokPro `creditUsagePercent` as 0% used so Provider Limits still renders a weekly bar (proto3 zero-elision) ([#12312](https://github.com/diegosouzapw/OmniRoute/pull/12312)) — thanks @HouMinXi +- **fix(combo):** Expose the two Auto-Combo scoring factors nobody could set — the combo validation schema and the dashboard weight sliders both declared 13 of the scorer's 15 factors, so `connectionDensity` (spreads load across a provider's connections) and `quality` were dropped on save and offered nowhere. The sliders also shipped their own default table that differed from the engine's on every non-zero factor and summed to 1.05, so the percentages shown next to them added up to 105%. Both lists now match `DEFAULT_WEIGHTS`, and a test keeps them there. Note that a combo whose stored `weights` omitted the two keys was effectively running with them at zero and the other thirteen renormalized upward; it now runs with the engine's intended distribution, so its routing does shift ([#12314](https://github.com/diegosouzapw/OmniRoute/pull/12314)) — thanks @maxmad64bis +- **fix(docs):** The free-tier reference no longer says its numbers come "confidence tagged per row" — no catalog entry carries a confidence tag and the API serves none, so every figure on that page is an estimate of the same, unstated quality. The page now states what an entry does vouch for: an independently documented hard stop (set by hand with the source in a comment, never defaulted to `true`) and a prompt-training disclosure, both with live counts the `check:docs-counts` gate keeps honest ([#12318](https://github.com/diegosouzapw/OmniRoute/pull/12318)) — thanks @maxmad64bis +- **fix(usage):** `adobe-firefly` and `firefly` have had a working usage fetcher since Adobe Firefly landed, but neither was ever added to the registration list, so the provider-plugin manifest, `genericQuotaFetcher` and the free-access quota cache all reported them as having no usage support — while `USAGE_SUPPORTED_PROVIDERS` said the opposite. Both are now declared, which also means their credit balance is fetched like any other declared provider's: `registerGenericQuotaFetchers` now registers a generic quota fetcher for them, and `resolveFreeAccessState` no longer returns early. A test holds the registration list to the dispatcher's switch in both directions, which is what the module's own docstring already asked for in prose ([#12321](https://github.com/diegosouzapw/OmniRoute/pull/12321)) — thanks @maxmad64bis +- **fix(quota):** drop the generic quota cache (agy / Antigravity / Claude OAuth) on an upstream 429 so reset-aware scoring does not keep a 60s stale snapshot, and force-refresh the next usage fetch so inner provider caches cannot recache the same window ([#12325](https://github.com/diegosouzapw/OmniRoute/pull/12325)) — thanks @HouMinXi +- **fix(combos):** deleting a combo now clears its persisted LKGP pins instead of leaving unreachable `key_value` rows behind ([#12326](https://github.com/diegosouzapw/OmniRoute/issues/12326)) ([#12425](https://github.com/diegosouzapw/OmniRoute/pull/12425)) — thanks @KrzysiekSko +- **fix(sse):** Keep ZWNJ (U+200C) and ZWJ (U+200D) in assistant text, reasoning and tool-call arguments — Persian/Kurdish half-space (`ارائه‌دهنده`), Arabic/Indic shaping and emoji sequences no longer lose them; the response de-obfuscation now removes joiners only between ASCII word characters, where the request side inserts them ([#12186](https://github.com/diegosouzapw/OmniRoute/issues/12186)) — thanks @rezjalibd ([#12359](https://github.com/diegosouzapw/OmniRoute/pull/12359)) — thanks @pacocartones +- **fix(resilience):** count resolved upstream 5xx results against the provider circuit breaker on the chat path — `CircuitBreaker.execute()` no longer reads a resolved `{ success: false, status: 5xx }` as a success that cancels the call-site failure, so a provider answering 503s now trips its breaker instead of staying `CLOSED` at `failureCount: 1`; single-model and combo dispatches are each accounted exactly once ([#12254](https://github.com/diegosouzapw/OmniRoute/issues/12254)) ([#12360](https://github.com/diegosouzapw/OmniRoute/pull/12360)) — thanks @pacocartones +- **fix(providers):** resolve the Codex quota auto-ping model from the live provider catalog and lifecycle registry instead of the retired `gpt-5.1-codex-mini`, and pause the ping with one actionable warning when no selectable Codex model exists rather than retrying a shut-down id every cooldown window ([#11905](https://github.com/diegosouzapw/OmniRoute/issues/11905)) ([#12361](https://github.com/diegosouzapw/OmniRoute/pull/12361)) — thanks @pacocartones +- **fix(api):** keep the `{created, data}` wrapper on combo-routed `/v1/images/generations` responses and default Codex image results to `b64_json` on both `/v1/images/generations` and `/v1/images/edits` so Codex CLI's built-in `image_gen` can decode them ([#12268](https://github.com/diegosouzapw/OmniRoute/issues/12268)) ([#12362](https://github.com/diegosouzapw/OmniRoute/pull/12362)) — thanks @pacocartones +- **fix(sse):** Name the shadowed custom provider node when a built-in provider id/alias (e.g. `openference` → `of`) reserves the prefix of an existing OpenAI/Anthropic-compatible node, so the runtime `No active credentials for provider: ` error explains that the prefix routed to the built-in and never reached the node's healthy connections, instead of contradicting the dashboard ([#11943](https://github.com/diegosouzapw/OmniRoute/issues/11943)) — thanks @morpheus9393 ([#12365](https://github.com/diegosouzapw/OmniRoute/pull/12365)) — thanks @pacocartones +- **fix(i18n):** wrap `ccOnboardingKeyPlaceholder` in ICU single quotes across all 43 locales so angle brackets render literally instead of being parsed as rich-text tags, which crashed the Claude Code onboarding block with `INVALID_MESSAGE: INVALID_TAG` ([#12302](https://github.com/diegosouzapw/OmniRoute/issues/12302)) ([#12369](https://github.com/diegosouzapw/OmniRoute/pull/12369)) — thanks @gonisulaimann +- **fix(models):** publish `effort_tiers` on Kimi K3's synced base-model entries (`kmca/k3`, `kmca/k3-256k`) so catalog-only clients (OpenCode, plain SDK pickers) can see and select the reasoning tiers (`low`/`high`/`max`) the synced metadata already carried — the `isSkippedEffortProvider` gate no longer suppresses tier visibility on those base entries, while synthetic `-` variant generation stays prevented and Codex/GLM base models remain excluded unchanged ([#12299](https://github.com/diegosouzapw/OmniRoute/issues/12299)) ([#12371](https://github.com/diegosouzapw/OmniRoute/pull/12371)) — thanks @gonisulaimann +- **fix(guardrails):** keep `auto`/`auto/*` virtual combos exempt from the Vision Bridge `fixedModel` credential guard so a combo target is passed through instead of silently falling back to global auto-selection ([#12237](https://github.com/diegosouzapw/OmniRoute/issues/12237)) ([#12373](https://github.com/diegosouzapw/OmniRoute/pull/12373)) — thanks @pacocartones +- **fix(combo):** capability-filter exhaustion caused by `max_tokens` above every target's known output limit now reports that reason (requested `max_tokens` vs the pool's highest known ceiling) instead of the unrelated "supports structured output" message ([#12229](https://github.com/diegosouzapw/OmniRoute/issues/12229)) — thanks @DW-MediaLab ([#12374](https://github.com/diegosouzapw/OmniRoute/pull/12374)) — thanks @pacocartones +- **fix(auth):** the `least-used` account strategy now prefers accounts without backoff before falling back to oldest `lastUsedAt`, the same tie-break `round-robin` already applies, so a failover no longer lands on a just-rate-limited account for a single request ([#12279](https://github.com/diegosouzapw/OmniRoute/issues/12279)) — thanks @tenshiak ([#12375](https://github.com/diegosouzapw/OmniRoute/pull/12375)) — thanks @pacocartones +- **fix(docker):** the `chatgpt-web-codex-browser` image now finds the Chrome binary under `chrome-linux64/` (Chrome for Testing layout in `playwright:v1.62.0-noble`) as well as the legacy `chrome-linux/`, so the container no longer crash-loops with `exec: --headless=new: not found` ([#12024](https://github.com/diegosouzapw/OmniRoute/issues/12024)) ([#12376](https://github.com/diegosouzapw/OmniRoute/pull/12376)) — thanks @pacocartones +- **fix(providers):** declare `groq/compound` and `allam-2-7b` as non-reasoning models in the curated Groq registry so `reasoning_effort` / `output_config.effort` / `thinking` from Claude Code are stripped instead of forwarded, which Groq rejected with HTTP 400 ([#12134](https://github.com/diegosouzapw/OmniRoute/issues/12134)) ([#12379](https://github.com/diegosouzapw/OmniRoute/pull/12379)) — thanks @pacocartones +- **fix(executors):** `OpencodeExecutor` no longer forces a direct connection when the connection has a proxy assigned in Proxy Management but no per-account proxies: the single-account fast path used to wrap the upstream dispatch in the direct-egress sentinel, discarding the ambient proxy context the chat handler had pinned from `proxy_assignments`, so API-key `opencode`/`opencode-go` connections egressed from the host IP (and hit geoblocks) despite the assignment. The direct pin is now applied only when no ambient proxy context exists ([#11894](https://github.com/diegosouzapw/OmniRoute/issues/11894) — thanks @hizzt) ([#12380](https://github.com/diegosouzapw/OmniRoute/pull/12380)) — thanks @pacocartones +- **fix(api):** `GET /v1/models` with `MODELS_CATALOG_PREFIX_MODE=canonical` (or `?prefix=canonical`) now lists providers whose registry alias is undefined or equal to their own id (Antigravity, Antigravity CLI and other self-aliased built-ins) — their single `provider/model` id was dropped by the alias/canonical duplicate guard in the static, synced, custom and alias-backed catalog loops ([#12058](https://github.com/diegosouzapw/OmniRoute/issues/12058)) — thanks @cheynetom ([#12381](https://github.com/diegosouzapw/OmniRoute/pull/12381)) — thanks @pacocartones +- **fix(translator):** Drop replayed `thinking` blocks that carry no signature (the shape produced from cross-provider `reasoning_content`) instead of stamping the default Claude signature on them, which Anthropic rejected with `400 Invalid signature in thinking block` on the next turn served by an Anthropic rung ([#12105](https://github.com/diegosouzapw/OmniRoute/issues/12105)) — thanks @atescivitci-cmd ([#12386](https://github.com/diegosouzapw/OmniRoute/pull/12386)) — thanks @pacocartones +- **fix(cli):** Resolve Bun's `--preload` polyfill path against the package root instead of `dist/`, so `omniroute` installed with `bun install -g` no longer crashes at startup with `error: preload not found …/dist/open-sse/utils/setupPolyfill.ts` ([#11980](https://github.com/diegosouzapw/OmniRoute/issues/11980)) — thanks @joglomedia ([#12387](https://github.com/diegosouzapw/OmniRoute/pull/12387)) — thanks @pacocartones +- **fix(providers):** `gemini-business` now publishes its model catalog — `/v1/models` and `/v1/providers/gemini-business/models` list the 12 enterprise Gemini ids the executor understands instead of returning an empty list (#12107) ([#12389](https://github.com/diegosouzapw/OmniRoute/pull/12389)) — thanks @pacocartones +- **fix(db):** install `busy_timeout` before the SQLite connection's first statement so a process opening the database while another one closes its WAL connection waits out the transient EXCLUSIVE lock instead of dying with `database is locked`, and recognise the drivers' real BUSY/PROTOCOL/IOERR errors as transient in the corruption probe so the same lock no longer renames the database away as corrupt; deflakes `cross-process contenders never both acquire the same connection` (#12394 — thanks @pacocartones) +- **fix(chat-admission):** derive the `chat_admission_busy` 503 `Retry-After` from observed heavyweight-lease occupancy — the larger of the exhausted `OMNIROUTE_CHAT_ADMISSION_QUEUE_MS` window and the time since capacity last turned over, capped at 60 s — instead of a fixed 1 s (structural) / 2 s (byte-stage) hint that invited Codex/agent fan-out clients to re-send ~1 MiB `/v1/responses` bodies every second into a gate held for the whole SSE lifetime; an idle gate keeps the historical floors ([#12135](https://github.com/diegosouzapw/OmniRoute/issues/12135)) (#12395 — thanks @pacocartones) +- **fix(api-manager):** the API key permissions modal no longer silently drops `allowedCombos` entries its Combo picker cannot render — routing-rule names such as `rt-*`, which the backend already honours — when "All" is clicked and the key is switched back to "Restrict"; those entries now survive the toggle, are listed read-only under the combo list so the count and the list agree, and are saved back verbatim instead of persisting `[]` (deny-all) (#12397 — thanks @pacocartones) +- **fix(catalog):** write the NUL separator of the catalog connection memo key, the provider serviceKind memo key, the Video Bridge promotion group key and a JSON-exactness test fixture as the `\u0000` escape instead of a raw byte — same runtime value, but the raw byte made git, GitHub and ripgrep treat those files as binary (hidden PR diffs, silently skipped searches); a guard test now keeps raw NUL bytes out of `src/`, `open-sse/` and `tests/` (#12403 — thanks @pacocartones) +- **fix(providers):** add `CLAUDE_CODE_CLIENT_VERSION` and `GITHUB_COPILOT_CLI_VERSION` env overrides so Anthropic/Copilot client-version gates can be unblocked without a rebuild ([#12417](https://github.com/diegosouzapw/OmniRoute/issues/12417)) ([#12632](https://github.com/diegosouzapw/OmniRoute/pull/12632)) — thanks @HouMinXi +- **fix(db):** back-fill `last_ping_at` and `last_pinged_reset_key` on `provider_connections` during schema reconciliation so divergent lineages that skipped `123_quota_auto_ping` still accept quota auto-ping writes ([#12470](https://github.com/diegosouzapw/OmniRoute/pull/12470) — thanks @KooshaPari) +- **fix(ci):** document MIT exceptions for `@eloqnt/{config,format-json,format-po}` (next-intl transitive; locked tarballs omit `license`) and keep the A2A lifecycle vitest off the real SQLite persistence seam ([#12581](https://github.com/diegosouzapw/OmniRoute/issues/12581)) ([#12595](https://github.com/diegosouzapw/OmniRoute/pull/12595)) +- OpenCode plugin `/v1/models` catalog fetch now waits 30s by default and attaches HTTP `statusCode` on 401/5xx so host fallback plugins can hop instead of seeing an untyped AbortError/UnknownError. ([#12607](https://github.com/diegosouzapw/OmniRoute/pull/12607)) — thanks @RaviTharuma +- **CI:** the OpenAPI security-tier gate now mirrors `isAlwaysProtectedPath()` in full — it also reads `ALWAYS_PROTECTED_API_PATTERNS`, so the pattern-gated credential routes (`/api/providers/{id}/{claude,codex}-auth/{export,apply-local}`, GHSA-5926-2w35-7h4q) no longer report as unannotated. (#12605) +- **fix(api):** GET /v1/models no longer waits forever on a hung coalesced catalog rebuild; cold-path waits are bounded (`CATALOG_BUILD_TIMEOUT_MS`, default 8s) and a last-good 200 is served when the rebuild times out ([#12627](https://github.com/diegosouzapw/OmniRoute/issues/12627)). ([#12628](https://github.com/diegosouzapw/OmniRoute/pull/12628)) — thanks @RaviTharuma +- **fix(cursor):** a non-array `tool_calls` on an assistant message no longer crashes the cursor request translator with a `TypeError`; both loops now require an array ([#12689](https://github.com/diegosouzapw/OmniRoute/issues/12689)) ([#12691](https://github.com/diegosouzapw/OmniRoute/pull/12691)) — thanks @soroush5 +- **fix(chat):** Chat Completions no longer return empty `content` after a server-owned memory or skills tool; the first provider send and account/model recovery now share one pipeline so a follow-up round-trip can fill the reply ([#12696](https://github.com/diegosouzapw/OmniRoute/issues/12696)) — thanks @HouMinXi ([#12867](https://github.com/diegosouzapw/OmniRoute/pull/12867)) +- **fix(dashboard):** batch-deleting provider keys no longer toasts failure after a successful delete when the confirm button's click event is forwarded as `onAfter` ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) — thanks @HouMinXi +- **fix(glm):** drop the extra 16th argument to `createSSETransformStreamWithLogger` that TypeScript rejected (TS2554) and that never reached the TransformStream ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) ([#12770](https://github.com/diegosouzapw/OmniRoute/pull/12770)) — thanks @HouMinXi +- **fix(dashboard):** pass `nodeMap` into Runtime `QuotaGroup` so a quota monitor in error/exhausted/alerting no longer throws `ReferenceError: nodeMap is not defined`. ([#12868](https://github.com/diegosouzapw/OmniRoute/pull/12868)) — thanks @HouMinXi +- Restricted API keys whose `allowedModels` lists a combo name no longer skip every combo member at pre-dispatch (`ALL_TARGETS_SKIPPED`). Inner-target filtering still applies when the allow-list is a provider prefix or `disableNonPublicModels` is on ([#12899](https://github.com/diegosouzapw/OmniRoute/pull/12899)). — thanks @HouMinXi +- **fix(combos):** Effort-suffixed combo members inherit the base model's `model_context_overrides` row so priority order is not inverted on large requests ([#12926](https://github.com/diegosouzapw/OmniRoute/pull/12926)) — thanks @HouMinXi +- **fix(catalog):** Picker-added `customModels` enter the dispatch-time live catalog so combo members and bare inference no longer 400 ([#12934](https://github.com/diegosouzapw/OmniRoute/pull/12934)) — thanks @HouMinXi +- **fix(docker):** re-derive the Docker build's worker-pool memory budget from the MEASURED ~4.5 GB per-process RSS (the issue owner's own VPS dmesg OOM-killer reproduction) instead of the stale 2560 MB/worker inference #11419 shipped, and lower `OMNIROUTE_BUILD_WORKERS` 3 → 2 so 1 parent + 1 page-data worker (2 processes × 4.5 GB = 9 GB) fits the 12.288 GB (75%) budget on a 16 GB GitHub Actions runner — the previous default (1 parent + 2 workers = 13.5 GB) still overcommitted the runner and kept "Publish to Docker Hub" failing with `cannot allocate memory` after #11419 merged (#7518). ([#11663](https://github.com/diegosouzapw/OmniRoute/pull/11663)) +- **fix(electron):** the packaged Windows desktop build now passes the #7592 cold-restart smoke end to end. Five defects were found and fixed on the way: (1) optional-pack staging failed on any machine whose `tar` is GNU tar — it reads the drive letter in an absolute `-f C:\...` archive path as a remote rsh host (`Cannot connect to C:`), so staging now passes a bare filename with `cwd` at the tarball directory and surfaces tar stderr on failure; (2) the Electron `files` allowlist was missing `lib/loginHeaderCapture.js`, so the packaged main process crashed at startup with `Cannot find module './lib/loginHeaderCapture'`; (3) electron-builder ≥26 injects an `!**/node_modules/**` ignore into every extraResources pattern list that no positive filter can override, which silently dropped the staged runtime `node_modules` (including the better-sqlite3 N-API prebuild) from `resources/app` — a fresh v3.8.50 desktop build would have shipped with no native SQLite driver and reproduced the #7592 sql.js fallback on every machine; an `afterPack` hook now restores the staged `node_modules` after packing; (4) the packaged-app smoke harness redirected `USERPROFILE` into its temp DATA_DIR, but Electron resolves the Roaming profile from `%USERPROFILE%\AppData\Roaming\` (USERPROFILE takes precedence over the APPDATA env var) and the path service throws instead of creating it, so `requestSingleInstanceLock()` returned false and the app exited(0) silently before `app.whenReady()` — the harness now pre-creates the derived tree, and `ensureSmokeEnvDirs` is exported and unit-tested; (5) the #7592 driver assertion parsed a `[DB] Driver: ...` line that the server's primary DB path never printed (only the unused `openDatabaseAsync()` did) — `getDbInstance()` now emits the same line on open so the guard can actually assert the native driver. Also: the smoke env-allowlist unit test hardcoded POSIX paths and could only pass on Linux/macOS; expectations are now host-agnostic, with new regression tests for the USERPROFILE-derived tree and `tarPack` under absolute Windows-style paths ([#11443](https://github.com/diegosouzapw/OmniRoute/pull/11443)) — thanks @jonlwheat2-gif +- fix(sse): stop the auto-combo candidates inspector from silently dropping model-locked/cooled-down rows (#9133) ([#11994](https://github.com/diegosouzapw/OmniRoute/pull/11994)) — thanks @hartmark +- **fix(providers):** Perplexity Web no longer turns upstream stream failures into successful assistant text; pre-content failures remain eligible for fallback, partial output ends with a structured sanitized error, and failed sessions are not persisted ([#12465](https://github.com/diegosouzapw/OmniRoute/pull/12465)) +- **fix(video-bridge):** preserve chronological video and transcript fusion, source-frame timestamps, transcript provenance, full contact-sheet intervals, and pre-abort callback isolation ([#11681](https://github.com/diegosouzapw/OmniRoute/pull/11681)) +- **Z.ai Web:** HTTP 200 streams carrying an upstream error now terminate with a structured failure instead of assistant text plus a normal stop, preserving partial output while allowing pre-content combo fallback. ([#12454](https://github.com/diegosouzapw/OmniRoute/pull/12454)) +- Absorb `Error [AbortError]: request_signal_aborted` and DOMException AbortError shapes in the process-level client-abort crash guard so routine client disconnects no longer kill the server (exit code 7). ([#12165](https://github.com/diegosouzapw/OmniRoute/pull/12165)) — thanks @alvinveroy +- **fix(sse):** Treat Adapta Web `type:error` SSE events as sanitized non-stream failures instead of empty HTTP 200 completions. ([#12459](https://github.com/diegosouzapw/OmniRoute/pull/12459)) +- **fix(autoCombo):** layer 3 (`models_dev_tier`) no longer scores retired models as premium, merge cross-provider capability rows deterministically, and inherit tier scores through variant ids ([#11508](https://github.com/diegosouzapw/OmniRoute/issues/11508)). `getModelsDevTierFitness` now vetoes ids present in `config/quality/model-lifecycle.json` with status `retired` before any other signal; `loadModelCapabilities()` aggregates per `model_id` (any-non-null-true for booleans, max for `limit_context`) instead of last-write-wins over SQLite's undefined row order; a capabilities miss retries through `resolveScoresAs()`'s base id and reports `models_dev_tier:inherited`, matching layers 1–2 (#11492). Adds `getModelsDevTierFitnessWithSource()` and an `invalidateCapabilitiesCache()` test/ops hook. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) — thanks @Meet6338-X +- fix(sse): accept Claude `content:[]` + `stop_reason: max_tokens`/`tool_use` in `detectMalformedNonStream` (match `isEmptyContentResponse`) so Claude Code `/model` probes with `max_tokens: 1` no longer become a false 502 ([#11568](https://github.com/diegosouzapw/OmniRoute/pull/11568)) — thanks @Seramicx +- **fix(security):** sanitize `request.failed` diagnostics before publishing them to live dashboard listeners and replay history, while keeping status, model, provider, latency, and internal call-log diagnostics intact. ([#12469](https://github.com/diegosouzapw/OmniRoute/pull/12469)) +- **fix(pwa):** do not intercept dashboard navigations so Chrome can fall back from a stale HTTP/3 Alt-Svc advertisement (UDP :20128 is unpublished; F5 on a long-lived tab hung until a new tab opened a fresh TCP connection). ([#12767](https://github.com/diegosouzapw/OmniRoute/pull/12767)) — thanks @HouMinXi +- **fix(executors):** handle DuckDuckGo ERR_BN_LIMIT (418) without retrying — when the upstream returns `418 ERR_BN_LIMIT` (rate-limit/ban), the executor now returns the error immediately instead of burning another VQD acquisition that would only count against the IP limit. The retry logic for `418 ERR_CHALLENGE` (unsolved challenge) remains unchanged. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) ([#12110](https://github.com/diegosouzapw/OmniRoute/pull/12110)) — thanks @oyi77 / @Meet6338-X +- Fixed DuckDuckGo streaming responses losing JSON lines and UTF-8 characters split across network chunks. ([#11528](https://github.com/diegosouzapw/OmniRoute/pull/11528)) — thanks @pacocartones +- Harden generated Cloudflare, Vercel, and Deno relays by stripping hop-by-hop, framing, and proxy authentication headers before forwarding requests upstream. ([#11533](https://github.com/diegosouzapw/OmniRoute/pull/11533)) — thanks @pacocartones +- **feat(grok-cli):** Provider Limits shows grok-cli banked reset credits from `GetRemainingResets` (including a real zero; a failed RPC omits the row) and the existing View credits button now calls `ConsumerUiSvc/RedeemReset` for grok-cli. Live tokens use nested fields 10/20/30 (id + Timestamp), not compact 1/2/3. ([#12805](https://github.com/diegosouzapw/OmniRoute/pull/12805)) — thanks @HouMinXi +- **fix(memory):** Embedding Model Quick select, Embedding Source remote dropdown, and Rerank selector now list every configured provider with embedding/rerank support instead of only chat-catalog text matches plus OpenRouter live discovery; a generic OpenAI-compatible `/embeddings` + Cohere-compatible `/rerank` runtime fallback resolves any configured chat provider's embedding/rerank endpoint, so unlisted providers no longer fail with "Unknown embedding provider"; both memory selectors gained a free-text model override ([#11390](https://github.com/diegosouzapw/OmniRoute/pull/11390)) — thanks @rqzbeh +- Harden SQLite upgrades around the historical migration-074 version collision: missing discovery and inspector tables are replayed atomically, pre-existing databases (including setup-created skeletons) receive reusable content-addressed safety snapshots, and Node test/eval probes without `DATA_DIR` are isolated from the operator database. ([#12435](https://github.com/diegosouzapw/OmniRoute/pull/12435)) +- **fix(dashboard):** Moonshot/Kimi Open Platform voucher and cash leftover percentages follow the bucket balance, so an empty wallet no longer paints those rows as 100% while Available is 0% ([#12733](https://github.com/diegosouzapw/OmniRoute/pull/12733)) — thanks @HouMinXi +- **fix(combo):** return non-retryable HTTP 400 when all candidates for a pinned native Codex turn are unavailable due to model-scoped lockout, terminating the turn cleanly while preserving turn continuity and enabling standard Combo routing on subsequent turns ([#12240](https://github.com/diegosouzapw/OmniRoute/pull/12240)) — thanks @mdigitalbh81 +- Fixed auto-router strategies preserving the selected connection through ranking and dispatch when multiple connections share the same provider and model. ([#11530](https://github.com/diegosouzapw/OmniRoute/pull/11530)) — thanks @pacocartones +- Preserve Chat Completions JSON-object response formats when translating requests to the Responses API. ([#11529](https://github.com/diegosouzapw/OmniRoute/pull/11529)) — thanks @pacocartones +- Fix idempotency fingerprints so Responses API requests include `input` and other semantic generation fields using stable serialization, while excluding credentials and request metadata. ([#11532](https://github.com/diegosouzapw/OmniRoute/pull/11532)) — thanks @pacocartones +- **fix(api):** Generated API CLI commands now enforce required OpenAPI request bodies; Combo test commands forward the required `comboName` body, while API keys created by older writers after migration 149 preserve legacy allow-all Combo access without widening explicit empty allowlists — thanks @marcelokarval ([#12070](https://github.com/diegosouzapw/OmniRoute/pull/12070)) +- **fix(grok-web):** treat upstream streaming failures as failures instead of successful assistant text: error-only streams now fail readiness with HTTP 502, while failures after legitimate content preserve that partial output and terminate through the sanitized stream failure path without a normal `stop` completion. ([#12458](https://github.com/diegosouzapw/OmniRoute/pull/12458)) +- HuggingChat now turns HTTP 200 JSONL generation failures into a sanitized 502 before content, or a fixed public stream failure after partial output, so fallback and request persistence no longer record a false successful stop. ([#12456](https://github.com/diegosouzapw/OmniRoute/pull/12456)) +- **fix(providers):** keep 1min.ai HTTP 200 stream errors out of assistant content, preserve partial output, and expose sanitized terminal errors so pre-content failures can fall back. ([#12466](https://github.com/diegosouzapw/OmniRoute/pull/12466)) +- **fix(sessions):** preserve legacy Sessions data when the additive exclusive-session projection is unavailable, returning an empty projection and warning only once per contiguous outage instead of failing the endpoint; the Sessions badge intentionally reflects the merged legacy and exclusive row count introduced by [#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389) ([#11469](https://github.com/diegosouzapw/OmniRoute/pull/11469)) — thanks @KaspaPulse +- Render Nimble Search with the generic provider icon and serve Opper's proven logo locally. ([#12415](https://github.com/diegosouzapw/OmniRoute/pull/12415)) +- **fix(quality):** `config/quality/provider-assets-provenance.jsonl` was stale against the physical `public/providers/` tree (still listing 225 assets and 5 duplicate-content groups after several provider-retirement PRs removed 85 icon files), which would have made `check:provider-asset-provenance` fail on the next full run. Reconciled the manifest to the real 142-file snapshot (142 asset records, 1 duplicate-content group, 2 newly-registered files marked `unresolved` pending provenance research), repinned `auditedCommit`, and updated the regression test's hardcoded counts to match. ([#11876](https://github.com/diegosouzapw/OmniRoute/pull/11876)) +- fix(ui): wrap quota card footer action buttons to prevent clipping Refresh button on narrow cards — when extra action buttons such as `View credits` are present on Codex cards, allow the footer row to flex-wrap with `shrink-0` buttons so `Refresh now` remains visible and clickable across all responsive card widths ([#11464](https://github.com/diegosouzapw/OmniRoute/pull/11464)) — thanks @adevwithpurpose +- Keep Antigravity Gemini usable when the same connection's Claude weekly quota is empty; generic quota cache stays per-connection for every other provider. ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) — thanks @HouMinXi +- Fixed Tailscale tunnel setup to reject ports outside the valid 1-65535 range and safely install Windows MSI packages from paths containing apostrophes. ([#11534](https://github.com/diegosouzapw/OmniRoute/pull/11534)) — thanks @pacocartones +- Classify Cloudflared and Tailscale process-management routes as local-only and spawn-capable while preserving remote access to read-only tunnel status endpoints. ([#11531](https://github.com/diegosouzapw/OmniRoute/pull/11531)) — thanks @pacocartones +- Electron release workflow: the `publish-npm` job now grants `actions: read` to the reusable `npm-publish.yml` it calls (its `publish` job requests it), which is what made GitHub refuse the whole v3.8.50 run at startup and ship the release with zero desktop assets; a `workflow_dispatch` now builds the requested tag instead of the dispatching branch and can skip the npm leg (`publish_npm=false`) when only re-attaching assets ([#11974](https://github.com/diegosouzapw/OmniRoute/pull/11974)) +- **fix(dashboard):** The Combos page usage guide now reads its dismissal through `useSyncExternalStore` instead of correcting SSR state inside an effect, removing an extra commit of the page tree on every load (and the `react-hooks/set-state-in-effect` error it raised). ([#12671](https://github.com/diegosouzapw/OmniRoute/pull/12671)) +- Electron release: `electron/package-lock.json` regained the optional `electron-builder-squirrel-windows` subtree (13 entries) that `npm ci` had been refusing as out of sync, `electron-release.yml` gained a `build_ref` dispatch input and stops regenerating release notes on a re-attach dispatch, and the npm publish workflow attaches the SBOM to the GitHub Release on dispatch publishes too — so the v3.8.51 tag ships every desktop asset and the SBOM like v3.8.49 did ([#12022](https://github.com/diegosouzapw/OmniRoute/pull/12022)) +- Drained the reds every PR against `release/v3.8.51` was born with: documented the three service-worker build-id variables, registered the six retirement/tunnel tests with the mutation gate, approved `eslint-plugin-react-hooks` in the dependency allowlist, added the six `combo.sort.*` strings to `vi` and `pt-BR`, pointed the ChatGPT Web doc at the real migration-168 test, worded the g4f hint around the member key, and realigned four tests to the retired-provider catalog and the legacy-schema fixtures the retirement migrations touch. ([#11940](https://github.com/diegosouzapw/OmniRoute/pull/11940)) +- **fix(providers):** Claude, Grok, LMArena, Notion, and Perplexity web-cookie transports now use pooled `wreq-js` 3.2 instead of the native sidecar, with all nine supported bindings pinned and audited, and the applicable platform binding plus native-license evidence included in each release artifact ([#12429](https://github.com/diegosouzapw/OmniRoute/pull/12429), supersedes [#11753](https://github.com/diegosouzapw/OmniRoute/pull/11753)). +- **fix(providers):** Zed Hosted streaming failures now trigger fallback before content and end partial streams with a sanitized structured error instead of fake assistant text and a normal-success stop. ([#12455](https://github.com/diegosouzapw/OmniRoute/pull/12455)) +- **fix(test):** capture hook result after render (direct commit `b85d0abb0b`) +- **fix(sync):** repair the two regressions the v3.8.50 sync-back left on release/v3.8.51 (direct commit `5ade9e0851`) +- **revert(models):** drop the memo reset hook ported from #10055 (direct commit `282de841ae`) +- **fix(typecheck):** guard the optional connectionId before the reactive model sync (direct commit `f2825bad75`) +- **fix(quota-share):** release the winner's reserved in-flight slot (#11371) ([#11408](https://github.com/diegosouzapw/OmniRoute/pull/11408)) — thanks @oyi77 +- **fix(providers):** stop silently dropping reasoning effort on opencode families (#10788) ([#11409](https://github.com/diegosouzapw/OmniRoute/pull/11409)) — thanks @oyi77 +- **fix(combo):** enable genuine quota-aware routing for generic providers (antigravity, claude, etc.) ([#11411](https://github.com/diegosouzapw/OmniRoute/pull/11411)) — thanks @HouMinXi +- **fix(oauth):** allow expired token retry before deactivation and activate expired retry loop (#P0) ([#11414](https://github.com/diegosouzapw/OmniRoute/pull/11414)) — thanks @HouMinXi +- **perf(executors):** lazy-load the executor registry — defer class imports + construction to first use (#11220) ([#11421](https://github.com/diegosouzapw/OmniRoute/pull/11421)) — thanks @oyi77 +- **fix:** make release/v3.8.50 CI gates green (run 32786966560 root causes) ([#11450](https://github.com/diegosouzapw/OmniRoute/pull/11450)) — thanks @jonlwheat2-gif +- **fix(sse):** exact-domain cookie match and origin-equality URL assertions ([#11452](https://github.com/diegosouzapw/OmniRoute/pull/11452)) — thanks @hartmark +- **fix(providers):** send Responses API payload during OpenAI-compatible validation ([#11454](https://github.com/diegosouzapw/OmniRoute/pull/11454)) — thanks @tuandinh0801 +- **fix(command-code):** fallback to /alpha/generate for Go plan without Provider API access ([#11455](https://github.com/diegosouzapw/OmniRoute/pull/11455)) — thanks @Chewji9875 +- **fix(responses-continuation):** fail closed on a log-truncated stored input/output array ([#11473](https://github.com/diegosouzapw/OmniRoute/pull/11473)) — thanks @hartmark +- **fix(cli):** write jcode settings as [providers.omniroute] in config.toml ([#11484](https://github.com/diegosouzapw/OmniRoute/pull/11484)) — thanks @hongnoul +- **fix(logging):** raise chat-log array truncation cap to 1000 and unify duplicate implementations ([#11499](https://github.com/diegosouzapw/OmniRoute/pull/11499)) — thanks @hartmark +- **fix:** restore live websocket URL helpers ([#11509](https://github.com/diegosouzapw/OmniRoute/pull/11509)) — thanks @PauloFH +- **perf(core):** eliminate async waterfalls across home, cache, and db operations (#11396) ([#11512](https://github.com/diegosouzapw/OmniRoute/pull/11512)) — thanks @HouMinXi +- **fix(kiro):** add runtime.us-east-1.kiro.dev as first-attempt endpoint ([#11517](https://github.com/diegosouzapw/OmniRoute/pull/11517)) — thanks @rifqiawl +- **fix(providers):** remove duplicate 128-tool truncation in OpencodeExecutor (#11444) ([#11518](https://github.com/diegosouzapw/OmniRoute/pull/11518)) — thanks @HouMinXi +- **fix(providers):** poll onboardUser LRO before classifying BYOP ([#11519](https://github.com/diegosouzapw/OmniRoute/pull/11519)) — thanks @rifqiawl +- **fix(translator):** preserve exact tool name casing for Claude tool_use payloads (#11487) ([#11520](https://github.com/diegosouzapw/OmniRoute/pull/11520)) — thanks @HouMinXi +- **fix(kimi-web):** align health probe with executor domain ([#11521](https://github.com/diegosouzapw/OmniRoute/pull/11521)) — thanks @pacocartones +- **fix(cli):** preserve existing admin password during setup ([#11522](https://github.com/diegosouzapw/OmniRoute/pull/11522)) — thanks @pacocartones +- **fix(mitm):** keep Windows DNS elevation off the POSIX sudo path (#11430) ([#11542](https://github.com/diegosouzapw/OmniRoute/pull/11542)) — thanks @official-burak +- **fix(sse):** scale chat admission by ingest byte budget instead of a fixed request count ([#11548](https://github.com/diegosouzapw/OmniRoute/pull/11548)) — thanks @TheDemonTuan +- **fix:** accept Gemini Web cookie exports ([#11554](https://github.com/diegosouzapw/OmniRoute/pull/11554)) — thanks @rafacpti23 +- **fix(i18n):** complete Brazilian Portuguese translations ([#11558](https://github.com/diegosouzapw/OmniRoute/pull/11558)) — thanks @rafacpti23 +- **fix(sse):** explicit types for openai-responses pureHelpers — clears last failing core typecheck gate ([#11567](https://github.com/diegosouzapw/OmniRoute/pull/11567)) — thanks @jonlwheat2-gif +- **fix(opencode):** hydrate Proxy Pool references for no-auth connections ([#11584](https://github.com/diegosouzapw/OmniRoute/pull/11584)) — thanks @AStupidBear +- **fix(ci):** resolve inherited release/v3.8.51 base-red CI failures ([#11608](https://github.com/diegosouzapw/OmniRoute/pull/11608)) — thanks @jonlwheat2-gif +- **fix(db):** fallback load STORAGE_ENCRYPTION_KEY from env files ([#11614](https://github.com/diegosouzapw/OmniRoute/pull/11614)) — thanks @benzntech +- **fix(providers):** mark Brave Search as no longer having a free tier ([#11621](https://github.com/diegosouzapw/OmniRoute/pull/11621)) — thanks @turbolego +- **fix:** pass tool result content through as string in OpenAI/Claude -> Gemini translation ([#11624](https://github.com/diegosouzapw/OmniRoute/pull/11624)) — thanks @Hsia97 +- **fix(sse):** scope credential health to exact key ([#11628](https://github.com/diegosouzapw/OmniRoute/pull/11628)) — thanks @KaspaPulse +- **fix(install):** guard prepare script against missing husky ([#11634](https://github.com/diegosouzapw/OmniRoute/pull/11634)) — thanks @Prajeeth-12 +- **fix(security):** redact NVIDIA_API_KEY at the log sink in the ad-hoc diag script ([#11639](https://github.com/diegosouzapw/OmniRoute/pull/11639)) +- **fix(types):** clear the two type regressions red on release/v3.8.51 ([#11640](https://github.com/diegosouzapw/OmniRoute/pull/11640)) — thanks @jonlwheat2-gif +- **fix(build):** resolve workspace: protocol dependencies to concrete semver versions during prepublish (#11540) ([#11641](https://github.com/diegosouzapw/OmniRoute/pull/11641)) — thanks @HouMinXi +- **fix(search):** properly resolve configured search connection in /v1/responses pipeline (#11524) ([#11642](https://github.com/diegosouzapw/OmniRoute/pull/11642)) — thanks @HouMinXi +- **fix(authz):** trust cf-connecting-ip for via-proxy detection when peer is Cloudflare edge (#11514) ([#11643](https://github.com/diegosouzapw/OmniRoute/pull/11643)) — thanks @HouMinXi +- **fix(security):** compare the OIDC state cookie in constant time ([#11646](https://github.com/diegosouzapw/OmniRoute/pull/11646)) +- **fix(security):** namespace the dedup hash by the calling API key ([#11649](https://github.com/diegosouzapw/OmniRoute/pull/11649)) +- **fix(models):** gate the Codex-native catalog loop on prefix mode (#11632) ([#11662](https://github.com/diegosouzapw/OmniRoute/pull/11662)) — thanks @xiaoyaner0201 +- **fix(dashboard):** project Codex routing quota in account pools ([#11666](https://github.com/diegosouzapw/OmniRoute/pull/11666)) — thanks @KaspaPulse +- **fix(ddgw):** normaliza system no upstream da release v3.8.51 ([#11692](https://github.com/diegosouzapw/OmniRoute/pull/11692)) — thanks @RhianB14 +- **fix(memory):** honor category filter in GET /api/memory ([#11699](https://github.com/diegosouzapw/OmniRoute/pull/11699)) — thanks @Sabeekhann +- **fix(ci):** detect stale workspace lockfile entries ([#11714](https://github.com/diegosouzapw/OmniRoute/pull/11714)) — thanks @pacocartones +- **fix(ci):** stamp dist/BUILD_SHA before the npm publish provenance gate ([#11721](https://github.com/diegosouzapw/OmniRoute/pull/11721)) +- **fix(docker):** decouple the best-effort Bun image from the release manifest ([#11724](https://github.com/diegosouzapw/OmniRoute/pull/11724)) +- **perf(compression):** avoid cloning memo misses twice ([#11727](https://github.com/diegosouzapw/OmniRoute/pull/11727)) — thanks @RaviTharuma +- **fix(docker):** let the bun digest artifact be absent, not fatal ([#11740](https://github.com/diegosouzapw/OmniRoute/pull/11740)) +- **fix(shared):** block cloud-metadata hosts under default remote-image guard ([#11755](https://github.com/diegosouzapw/OmniRoute/pull/11755)) — thanks @santosraju99-hub +- **fix(api):** preserve caller-provided X-Correlation-Id on chat completions ([#11760](https://github.com/diegosouzapw/OmniRoute/pull/11760)) — thanks @aniruddhaadak80 +- **fix(resilience):** lock permanently retired models instead of short backoff (Gemini ban prevention) ([#11762](https://github.com/diegosouzapw/OmniRoute/pull/11762)) — thanks @turbolego +- **fix:** mark Vercel AI Gateway as passthroughModels ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **fix(resilience):** stop hammering permanently-moved endpoints and billing-suspended accounts ([#11774](https://github.com/diegosouzapw/OmniRoute/pull/11774)) — thanks @turbolego +- **fix(lease):** remove global static reservation and gate routing on live active lease occupancy ([#11775](https://github.com/diegosouzapw/OmniRoute/pull/11775)) — thanks @TheDemonTuan +- **fix(ci):** size the install-upgrade gate to a measured run, and log the pack cost ([#11776](https://github.com/diegosouzapw/OmniRoute/pull/11776)) +- **fix(pwa):** stop serving the stale shell after deploys ([#11779](https://github.com/diegosouzapw/OmniRoute/pull/11779)) — thanks @HouMinXi +- **fix(combo):** lock GitHub models rejected as "not supported" for future requests ([#11781](https://github.com/diegosouzapw/OmniRoute/pull/11781)) — thanks @turbolego +- **fix(cli):** probe both IPv4 and IPv6 loopback for server readiness (#11766) ([#11794](https://github.com/diegosouzapw/OmniRoute/pull/11794)) — thanks @vermasomesh835 +- **fix(sse):** preserve CLIProxyAPI wrappers per connection ([#11797](https://github.com/diegosouzapw/OmniRoute/pull/11797)) — thanks @RaviTharuma +- **fix(providers):** honor model auto-fetch opt-in on create ([#11798](https://github.com/diegosouzapw/OmniRoute/pull/11798)) — thanks @RaviTharuma +- **fix(dashboard):** make model auto-fetch order independent ([#11805](https://github.com/diegosouzapw/OmniRoute/pull/11805)) — thanks @RaviTharuma +- **fix(docker):** make GHA cache exports best effort ([#11806](https://github.com/diegosouzapw/OmniRoute/pull/11806)) — thanks @RaviTharuma +- **fix(kiro):** do not permanently ban on 'User is not authorized to make this call' ([#11809](https://github.com/diegosouzapw/OmniRoute/pull/11809)) — thanks @Deftera186 +- **fix(sse):** keep cache-write tokens in OpenAI-shaped usage ([#11814](https://github.com/diegosouzapw/OmniRoute/pull/11814)) — thanks @TheDemonTuan +- **fix(docs):** resolve relative markdown and wiki links across Fumadocs and GitHub wiki ([#11834](https://github.com/diegosouzapw/OmniRoute/pull/11834)) — thanks @AndrianBalanescu +- **fix(chat):** preserve unstripped model string for passthrough provider routing ([#11840](https://github.com/diegosouzapw/OmniRoute/pull/11840)) — thanks @raheemuddin786 +- **fix(mcp):** avoid async initialization inside sync esm wrappers (#11569) ([#11849](https://github.com/diegosouzapw/OmniRoute/pull/11849)) — thanks @HouMinXi +- **fix(sse):** preserve coherent markdown token boundaries across stream translation chunks (#11606) ([#11851](https://github.com/diegosouzapw/OmniRoute/pull/11851)) — thanks @HouMinXi +- **fix(ci):** run the install-upgrade gate on disk, not on the /tmp tmpfs ([#11855](https://github.com/diegosouzapw/OmniRoute/pull/11855)) +- **fix(skills):** expand shorthand property types in injected tool schemas ([#11857](https://github.com/diegosouzapw/OmniRoute/pull/11857)) — thanks @alvinveroy +- **fix(ci):** publish npm from a hosted runner so provenance is accepted ([#11877](https://github.com/diegosouzapw/OmniRoute/pull/11877)) +- **fix(lint):** pin react hooks plugin to 7.0.1 ([#11886](https://github.com/diegosouzapw/OmniRoute/pull/11886)) +- **fix(ci):** surface and clear release-green ESLint failure ([#11890](https://github.com/diegosouzapw/OmniRoute/pull/11890)) — thanks @MumuTW +- **fix(guardrails):** prevent duplicate prompt-injection-guard log output ([#11936](https://github.com/diegosouzapw/OmniRoute/pull/11936)) — thanks @patrykkopycinski +- **fix(auth):** downgrade expected transient states from warn to debug ([#11937](https://github.com/diegosouzapw/OmniRoute/pull/11937)) — thanks @patrykkopycinski +- **fix(codex):** fail over image generation for imported free plans ([#11948](https://github.com/diegosouzapw/OmniRoute/pull/11948)) — thanks @Bl0ck154 +- **fix(codex):** normalize non-stream responses ([#11951](https://github.com/diegosouzapw/OmniRoute/pull/11951)) — thanks @Bl0ck154 +- **fix(images):** forward Antigravity image size ([#11952](https://github.com/diegosouzapw/OmniRoute/pull/11952)) — thanks @Bl0ck154 +- **fix(db):** invalidate connection cache after upsert ([#11953](https://github.com/diegosouzapw/OmniRoute/pull/11953)) — thanks @Bl0ck154 +- **fix(codex):** restore imported account state ([#11954](https://github.com/diegosouzapw/OmniRoute/pull/11954)) — thanks @Bl0ck154 +- **fix(sse):** give extended-thinking targets the reasoning readiness budget ([#11959](https://github.com/diegosouzapw/OmniRoute/pull/11959)) — thanks @abhisheksharma2411 +- **fix(dashboard):** use opaque background and readable text color on cost chart tooltips ([#11960](https://github.com/diegosouzapw/OmniRoute/pull/11960)) — thanks @ZaimMarzuki +- **fix(antigravity):** send complete loadCodeAssist metadata (ideType/platform/pluginType as numeric enums) ([#11969](https://github.com/diegosouzapw/OmniRoute/pull/11969)) — thanks @rifqiawl +- **fix(core):** resolve DB init race condition and reasoning translation ([#12003](https://github.com/diegosouzapw/OmniRoute/pull/12003)) — thanks @raheemuddin786 +- **fix(admission):** exclude reclaimable page cache from the cgroup pressure ratio ([#12017](https://github.com/diegosouzapw/OmniRoute/pull/12017)) — thanks @HouMinXi +- **fix(build):** prune non-production trees in NFT trace excludes and tsconfig ([#12028](https://github.com/diegosouzapw/OmniRoute/pull/12028)) — thanks @Chewji9875 +- **fix(migrations):** add renamed migration compatibility for 056/073/077/101 ([#12036](https://github.com/diegosouzapw/OmniRoute/pull/12036)) — thanks @oyi77 +- **fix(sse):** default crash-guard logger to console.warn, not console ([#12042](https://github.com/diegosouzapw/OmniRoute/pull/12042)) — thanks @HouMinXi +- **fix(catalog):** derive vision/modalities for built-in auto combos from effective target pool ([#12046](https://github.com/diegosouzapw/OmniRoute/pull/12046)) — thanks @Prajeeth-12 +- **fix(release):** the packaged-app smoke verifies the database opened, not a driver line the primary path never prints (twin of #12032) ([#12047](https://github.com/diegosouzapw/OmniRoute/pull/12047)) +- **fix(barrel):** migrate src/app/ off the @/lib/localDb barrel import (#11795 Phase 2) ([#12051](https://github.com/diegosouzapw/OmniRoute/pull/12051)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate src/lib/ off the localDb barrel to direct db imports (#59) ([#12052](https://github.com/diegosouzapw/OmniRoute/pull/12052)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate open-sse, src/shared, src/sse, src/models, src/domain off the @/lib/localDb barrel import (#11795 Phase 4) ([#12053](https://github.com/diegosouzapw/OmniRoute/pull/12053)) — thanks @jonlwheat2-gif +- **fix(barrel):** delete the @/lib/localDb barrel — every consumer migrated (#11795 Phase 5) ([#12055](https://github.com/diegosouzapw/OmniRoute/pull/12055)) — thanks @jonlwheat2-gif +- **fix(radar):** restore D12 public boundary ([#12057](https://github.com/diegosouzapw/OmniRoute/pull/12057)) +- **fix(dev):** bound webpack and Tailwind scans ([#12075](https://github.com/diegosouzapw/OmniRoute/pull/12075)) — thanks @backryun +- **fix(dev):** isolate root layout settings reads ([#12076](https://github.com/diegosouzapw/OmniRoute/pull/12076)) — thanks @backryun +- **fix(dev):** reduce instrumentation executor fan-out (phase 3) ([#12078](https://github.com/diegosouzapw/OmniRoute/pull/12078)) — thanks @backryun +- **fix(dev):** make logging resources HMR-singleton ([#12079](https://github.com/diegosouzapw/OmniRoute/pull/12079)) — thanks @backryun +- **fix(sse):** keep unavailable forced connections scoped ([#12080](https://github.com/diegosouzapw/OmniRoute/pull/12080)) — thanks @keeltrace +- **fix(dev):** isolate batch dispatch from instrumentation ([#12081](https://github.com/diegosouzapw/OmniRoute/pull/12081)) — thanks @backryun +- **fix(ci):** reconcile release test contract drift ([#12082](https://github.com/diegosouzapw/OmniRoute/pull/12082)) — thanks @backryun +- **fix(ci):** accept CVE-2025-68121 in the prebuilt tls-client .so, auto-close base-red issues, guard Scorecard on the default branch ([#12085](https://github.com/diegosouzapw/OmniRoute/pull/12085)) +- **fix(db):** drop three consumer-less 1proxy exports — dead-code base-red on release/v3.8.51 after the barrel deletion (#12055) ([#12087](https://github.com/diegosouzapw/OmniRoute/pull/12087)) +- **fix(api):** clear the six API-route TypeScript regressions the new gate landed red on ([#12094](https://github.com/diegosouzapw/OmniRoute/pull/12094)) +- **fix(release):** never let the tag-push Create Release append auto notes to the curated body ([#12096](https://github.com/diegosouzapw/OmniRoute/pull/12096)) +- **fix(sse):** honor CLIProxyAPI environment API key ([#12099](https://github.com/diegosouzapw/OmniRoute/pull/12099)) — thanks @RaviTharuma +- **fix(oauth):** bind Google refresh to the client that issued the token ([#12106](https://github.com/diegosouzapw/OmniRoute/pull/12106)) — thanks @HouMinXi +- **fix(dashboard):** make RequestLoggerDetail loadable outside Next — CSS via globals.css + CJS/ESM interop (#11703 base-reds) ([#12114](https://github.com/diegosouzapw/OmniRoute/pull/12114)) +- **fix(leases):** project status lease row to lease columns so joined connection PII never escapes ([#12115](https://github.com/diegosouzapw/OmniRoute/pull/12115)) — thanks @geek007git +- **fix(plugins):** do not kill the plugin process when a fire-and-forget hook times out ([#12116](https://github.com/diegosouzapw/OmniRoute/pull/12116)) — thanks @geek007git +- **fix(guardrails):** restore injection-guard logging on middleware-only routes ([#12117](https://github.com/diegosouzapw/OmniRoute/pull/12117)) — thanks @geek007git +- **fix(plugins):** refresh stored manifest from disk on activate so new hook fields reach existing installs ([#12120](https://github.com/diegosouzapw/OmniRoute/pull/12120)) — thanks @geek007git +- **fix(codex):** preserve existing provider state when bulk-import upserts a matching connection ([#12122](https://github.com/diegosouzapw/OmniRoute/pull/12122)) — thanks @geek007git +- **fix(ci):** clear the two base-reds the 2026-08-30 merge batch left on release/v3.8.51 (round 3) ([#12123](https://github.com/diegosouzapw/OmniRoute/pull/12123)) +- **fix(sse):** strip type:'custom' from Claude tools on agentrouter dispatch ([#12126](https://github.com/diegosouzapw/OmniRoute/pull/12126)) — thanks @wahidsadik371-coder +- **fix(sse):** stop advertising video providers the dispatcher cannot run ([#12131](https://github.com/diegosouzapw/OmniRoute/pull/12131)) — thanks @quiterunner-commits +- **fix(search):** treat HTTP 432 and plan limit errors as transient cooldown ([#12139](https://github.com/diegosouzapw/OmniRoute/pull/12139)) — thanks @HouMinXi +- **fix(sse):** refuse an AI Horde queue that cannot fit the request budget ([#12143](https://github.com/diegosouzapw/OmniRoute/pull/12143)) — thanks @quiterunner-commits +- **fix(ci):** clear the base-reds the afternoon merge batch left on release/v3.8.51 (round 5: provider count 352, TS2554/TS2677) ([#12144](https://github.com/diegosouzapw/OmniRoute/pull/12144)) +- **fix(translator):** strip neutral tool_choice when tools absent in Responses-to-Chat (#12141) ([#12166](https://github.com/diegosouzapw/OmniRoute/pull/12166)) — thanks @HouMinXi +- **fix(guardrails):** pass providerId to getResolvedModelCapabilities in checkComboVision (#12112) ([#12169](https://github.com/diegosouzapw/OmniRoute/pull/12169)) — thanks @HouMinXi +- **fix(translator):** strip plaintext reasoning content for opaque responses backends (#12128) ([#12171](https://github.com/diegosouzapw/OmniRoute/pull/12171)) — thanks @HouMinXi +- **perf(stream):** compile hot-path regexes once, bound token caches, fix quadratic buffering ([#12179](https://github.com/diegosouzapw/OmniRoute/pull/12179)) — thanks @opensource-elearning +- **fix:** resolve compression worker file using runtime anchors instead of… ([#12183](https://github.com/diegosouzapw/OmniRoute/pull/12183)) — thanks @NightStalker-87 +- **fix:** prevent Claude Code session kills via liveness-aware readiness + auto model echo ([#12189](https://github.com/diegosouzapw/OmniRoute/pull/12189)) — thanks @opensource-elearning +- **fix(translator):** preserve falsy primitive values in Gemini and Antigravity function response results ([#12191](https://github.com/diegosouzapw/OmniRoute/pull/12191)) — thanks @Siva010 +- **fix(oauth):** mark empty Antigravity projectId as degraded and clear stale errors (#11284) ([#12205](https://github.com/diegosouzapw/OmniRoute/pull/12205)) — thanks @HouMinXi +- **fix(providers):** gate the Codex auto-ping usage read on the shared quota throttle ([#12209](https://github.com/diegosouzapw/OmniRoute/pull/12209)) — thanks @abhisheksharma2411 +- **fix(combos):** prioritize SQLite row id over inner JSON id and notify delete errors ([#12213](https://github.com/diegosouzapw/OmniRoute/pull/12213)) — thanks @HouMinXi +- **fix(providers):** mark featherless.ai as no longer having a free tier (`hasFree: false`) ([#12216](https://github.com/diegosouzapw/OmniRoute/pull/12216)) — thanks @turbolego +- **fix(deepseek-web):** stop Turbopack dev panic in the PoW worker path resolver ([#12221](https://github.com/diegosouzapw/OmniRoute/pull/12221)) — thanks @hartmark +- **fix(chat-admission):** clarify local 503 source ([#12223](https://github.com/diegosouzapw/OmniRoute/pull/12223)) — thanks @keeltrace +- **fix(dev):** silence webpack runtime module warnings ([#12228](https://github.com/diegosouzapw/OmniRoute/pull/12228)) — thanks @backryun +- **fix(db):** use module.require for CommonJS runtime driver loading ([#12230](https://github.com/diegosouzapw/OmniRoute/pull/12230)) — thanks @raheemuddin786 +- **fix(memory):** honest probe-driven FTS5 keyword status + memory_id rowid sync ([#12231](https://github.com/diegosouzapw/OmniRoute/pull/12231)) — thanks @raheemuddin786 +- **fix(system):** propagate abort signal to stream reader in HTTP version checks ([#12232](https://github.com/diegosouzapw/OmniRoute/pull/12232)) — thanks @raheemuddin786 +- **fix(resilience):** isolate local host execution errors from provider circuit breakers ([#12233](https://github.com/diegosouzapw/OmniRoute/pull/12233)) — thanks @raheemuddin786 +- **fix(sse):** sort injected tools deterministically for prompt caching ([#12234](https://github.com/diegosouzapw/OmniRoute/pull/12234)) — thanks @raheemuddin786 +- **fix(combo):** always clear the loop-safety timer, not just on the happy path (#11804) ([#12245](https://github.com/diegosouzapw/OmniRoute/pull/12245)) +- **fix(quality):** base-red pair — stryker tap registration + turn-pin suites aligned to the window gate ([#12255](https://github.com/diegosouzapw/OmniRoute/pull/12255)) — thanks @backryun +- **fix(dev):** qualify Turbopack runtime boundaries ([#12258](https://github.com/diegosouzapw/OmniRoute/pull/12258)) — thanks @backryun +- **fix(combo):** fall back to full pool when collapsed sole survivor is context-too-small ([#12278](https://github.com/diegosouzapw/OmniRoute/pull/12278)) — thanks @opensource-elearning +- **fix(combo):** bound the pre-dispatch unavailable skip so a stale label cannot dark a pool (#12168) ([#12285](https://github.com/diegosouzapw/OmniRoute/pull/12285)) +- **fix(security):** strip Qwen/Alibaba console-session cookies from provider API responses ([#12287](https://github.com/diegosouzapw/OmniRoute/pull/12287)) +- **fix(usage):** console-aware Token Plan guidance + subscription hint on bailian 401 ([#12288](https://github.com/diegosouzapw/OmniRoute/pull/12288)) +- **fix(resourcePressure):** log numeric detail on every rejection, recover faster ([#12293](https://github.com/diegosouzapw/OmniRoute/pull/12293)) — thanks @hartmark +- **fix(release):** drain the 2026-09-01 base-red window — passthrough usage regression + radarPage i18n keys ([#12327](https://github.com/diegosouzapw/OmniRoute/pull/12327)) +- **fix(ci):** openapi-security-tiers checker must honor routeGuard patterns + imported prefixes ([#12350](https://github.com/diegosouzapw/OmniRoute/pull/12350)) — thanks @ggiak +- **perf(ci):** cache node_modules in the npm-ci-retry composite (#8084 D3) ([#12408](https://github.com/diegosouzapw/OmniRoute/pull/12408)) +- **revert(ui):** point CTAs back at their real destinations ([#12410](https://github.com/diegosouzapw/OmniRoute/pull/12410)) +- **fix(release):** drain the 2026-09-02 base-red — rerank-providers import + api-typecheck baseline ratchet ([#12414](https://github.com/diegosouzapw/OmniRoute/pull/12414)) +- **fix(types):** make system prompt injection noImplicitAny-safe ([#12416](https://github.com/diegosouzapw/OmniRoute/pull/12416)) +- **fix(quality):** validate typecheck baseline schema ([#12419](https://github.com/diegosouzapw/OmniRoute/pull/12419)) +- **fix(memory):** resolve rerank provider node cache import ([#12421](https://github.com/diegosouzapw/OmniRoute/pull/12421)) +- **fix(ci):** repair release-root regressions — pack dedup, web-session syntax, uc-image ids ([#12423](https://github.com/diegosouzapw/OmniRoute/pull/12423)) — thanks @backryun +- **fix(providers):** separate MaxAI and UC credential contracts ([#12431](https://github.com/diegosouzapw/OmniRoute/pull/12431)) +- **fix(cli):** preserve Claude settings on config set ([#12432](https://github.com/diegosouzapw/OmniRoute/pull/12432)) — thanks @KrzysiekSko +- **fix(providers):** repair the maxai credential block truncated by merge auto-resolve ([#12433](https://github.com/diegosouzapw/OmniRoute/pull/12433)) +- **fix(quality):** record the 2026-09-02 merged growth in the file-size baseline ([#12434](https://github.com/diegosouzapw/OmniRoute/pull/12434)) +- **fix(uc):** route UC error strings through sanitizeErrorMessage; allowlist the retired codex id ([#12437](https://github.com/diegosouzapw/OmniRoute/pull/12437)) +- **fix(adapta):** redact streamed upstream errors ([#12438](https://github.com/diegosouzapw/OmniRoute/pull/12438)) +- **fix(sse):** remap non-contiguous upstream tool_calls index to a gap-free output_index ([#12445](https://github.com/diegosouzapw/OmniRoute/pull/12445)) — thanks @hartmark +- **fix(quality):** detect duplicate tool_calls entries in one response ([#12446](https://github.com/diegosouzapw/OmniRoute/pull/12446)) — thanks @hartmark +- **fix(conversations):** resolve turn content OmniRoute never sends back to the client ([#12447](https://github.com/diegosouzapw/OmniRoute/pull/12447)) — thanks @hartmark +- **fix(combo):** do not treat credits-exhausted 401 as auth skip ([#12449](https://github.com/diegosouzapw/OmniRoute/pull/12449)) — thanks @RaviTharuma +- **fix(auth):** do not park healthy quota accounts as expired ([#12452](https://github.com/diegosouzapw/OmniRoute/pull/12452)) — thanks @RaviTharuma +- **fix(responses-continuation):** fail closed on a collector-truncated, empty output array ([#12460](https://github.com/diegosouzapw/OmniRoute/pull/12460)) — thanks @hartmark +- **fix(logging):** raise the SSE payload collector's default cap ([#12461](https://github.com/diegosouzapw/OmniRoute/pull/12461)) — thanks @hartmark +- **fix(resilience):** surface Responses failed.error.message in 502s ([#12472](https://github.com/diegosouzapw/OmniRoute/pull/12472)) — thanks @RaviTharuma +- **fix(security):** unbiased maxai X-Random nonce + stricter URL/regex assertions ([#12502](https://github.com/diegosouzapw/OmniRoute/pull/12502)) +- **fix(video):** re-anchor transcript log-redaction so PII/credential maskers can't reopen the leak (#12150 P1 follow-up) ([#12503](https://github.com/diegosouzapw/OmniRoute/pull/12503)) +- **fix(monitoring):** serve cached credentialHealth off the request path ([#12533](https://github.com/diegosouzapw/OmniRoute/pull/12533)) — thanks @RaviTharuma +- **fix(quota):** keep Antigravity Gemini usable when Claude weekly is empty ([#12566](https://github.com/diegosouzapw/OmniRoute/pull/12566)) — thanks @HouMinXi +- **fix(authz):** hard-gate every credential export and CLI-config write (GHSA-5926-2w35-7h4q) ([#12600](https://github.com/diegosouzapw/OmniRoute/pull/12600)) +- **fix(docs):** restore the Next build — REMOVED_PROVIDERS.md had no frontmatter (base-red #12581) ([#12610](https://github.com/diegosouzapw/OmniRoute/pull/12610)) +- **fix(quality):** re-point the zcodeProtocol public-creds allowlist to line 313 ([#12615](https://github.com/diegosouzapw/OmniRoute/pull/12615)) +- **fix(security):** close 3 advisories — search baseUrl exfil, sk- in the error sanitizer, bifrost relay header leak ([#12620](https://github.com/diegosouzapw/OmniRoute/pull/12620)) +- **fix(providers):** sanitize boolean required and nested bare maps for Gemini (#12269) ([#12624](https://github.com/diegosouzapw/OmniRoute/pull/12624)) — thanks @HouMinXi +- **fix(resilience):** keep Overloaded STREAM_EARLY_EOF off the provider breaker ([#12626](https://github.com/diegosouzapw/OmniRoute/pull/12626)) — thanks @HouMinXi +- **fix(catalog):** re-audit free-tier quotas against official pages ([#12649](https://github.com/diegosouzapw/OmniRoute/pull/12649)) +- **fix(dashboard):** keep a request's pending-tracking id stable across combo target retries ([#12650](https://github.com/diegosouzapw/OmniRoute/pull/12650)) — thanks @hartmark +- **fix(tests):** retire dead model ids from the chat-pipeline integration suite (base-red #12581) ([#12670](https://github.com/diegosouzapw/OmniRoute/pull/12670)) +- **fix:** resolve SqliteError no such table compression_run_telemetry during cleanup ([#12682](https://github.com/diegosouzapw/OmniRoute/pull/12682)) — thanks @groovecityJO +- **fix(combo):** treat a pin-only step as implicit connection allowlist ([#12697](https://github.com/diegosouzapw/OmniRoute/pull/12697)) — thanks @HouMinXi +- **fix(ci):** ship .npmrc in published package so legacy-peer-deps applies to consumers (#11544) ([#12699](https://github.com/diegosouzapw/OmniRoute/pull/12699)) — thanks @KooshaPari +- **fix(docker):** pin 4 CLI tools to exact versions (#12576) ([#12703](https://github.com/diegosouzapw/OmniRoute/pull/12703)) — thanks @KooshaPari +- **fix(docker):** document and harden cli profile trust boundary (#12570) ([#12706](https://github.com/diegosouzapw/OmniRoute/pull/12706)) — thanks @KooshaPari +- **fix(sse):** redact video transcript in pre-guardrail rejected-request logs (#12150 P2 item 7) ([#12710](https://github.com/diegosouzapw/OmniRoute/pull/12710)) +- **fix(i18n):** quote placeholder in OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES description (#12505) ([#12769](https://github.com/diegosouzapw/OmniRoute/pull/12769)) — thanks @KooshaPari +- **fix(api/pricing):** surface validation error message as string, not raw object (#12494) ([#12771](https://github.com/diegosouzapw/OmniRoute/pull/12771)) — thanks @KooshaPari +- **fix(sse):** re-enable prompt compression for native Codex passthrough ([#12834](https://github.com/diegosouzapw/OmniRoute/pull/12834)) — thanks @thomasmaerz +- **fix(dashboard):** model health tests for a provider node set to the Responses API now call `/v1/responses` with a Responses-shaped body instead of `/v1/chat/completions` — those models were reported as `Provider returned HTTP 200 but no text content` even though the same model answered normally through `/v1/responses` ([#13070](https://github.com/diegosouzapw/OmniRoute/issues/13070)) ([#13087](https://github.com/diegosouzapw/OmniRoute/pull/13087)) — thanks @ntdatt812 +- fix(routing): stop a round-robin combo's "opencode" targets from collapsing onto the opencode-zen connection (#11912) ([#13283](https://github.com/diegosouzapw/OmniRoute/pull/13283)) +- fix(routing): stop the reactive-compaction debug log from lying when compression is globally disabled (#11977) ([#13276](https://github.com/diegosouzapw/OmniRoute/pull/13276)) +- fix(electron): relativize standalone-bundle symlink targets so Stage 8 manifest verification stops failing on Windows (#11979) ([#13251](https://github.com/diegosouzapw/OmniRoute/pull/13251)) +- fix(dashboard): surface a visible error when Compression Studio's combined preview run fails (#12061) ([#13288](https://github.com/diegosouzapw/OmniRoute/pull/13288)) +- fix(dashboard): make the compression "Effective pipeline" preview honor the active profile and warn when the master switch is off (#12063) ([#13287](https://github.com/diegosouzapw/OmniRoute/pull/13287)) +- fix(providers): scope TinyCMS Web signer's DOM shims to each call instead of leaking them for the process lifetime, and surface a clean HTTP status on a non-JSON interception-toggles error (#12072) ([#13254](https://github.com/diegosouzapw/OmniRoute/pull/13254)) +- fix(guardrails): stop Vision Bridge from re-selecting a model locked after a 404 (#12111) ([#13259](https://github.com/diegosouzapw/OmniRoute/pull/13259)) +- fix(sse): require Responses-shaped body before native OpenAI-compatible passthrough (#12129) ([#13278](https://github.com/diegosouzapw/OmniRoute/pull/13278)) +- fix(providers): minimax-m3 now collapses manual thinking.type:"enabled" to adaptive, preventing upstream 400 (2013) (#12132) ([#13249](https://github.com/diegosouzapw/OmniRoute/pull/13249)) +- fix(db): scope model visibility overrides by modality so hiding a Chat model no longer hides an identically-ID'd Image/Embeddings/etc. model (#12172) ([#13258](https://github.com/diegosouzapw/OmniRoute/pull/13258)) +- fix(db): scope local-provider apiKey dedup to matching base URL so LM Studio/Ollama-style connections support multiple accounts (#12173) ([#13250](https://github.com/diegosouzapw/OmniRoute/pull/13250)) +- fix(providers): refresh Trae's stale Referer/Origin and forward user timezone so imported connections stop failing with 401 (#12190) ([#13255](https://github.com/diegosouzapw/OmniRoute/pull/13255)) +- fix(providers): route opencode-go/gpt-5.6-luna to /responses instead of /chat/completions (#12196) ([#13275](https://github.com/diegosouzapw/OmniRoute/pull/13275)) +- fix(dashboard): allow deleting the last extra-upstream-header row even when invalid (#12251) ([#13252](https://github.com/diegosouzapw/OmniRoute/pull/13252)) +- **fix(i18n):** translate pre-existing `__MISSING__:` keys for `combo.sort`, `requestLogger.detail` expand/collapse, `common.profile`, and `settings.resilienceCredentialHealth*` across 39 locales ([#12272](https://github.com/diegosouzapw/OmniRoute/issues/12272)) ([#13034](https://github.com/diegosouzapw/OmniRoute/pull/13034)) — thanks @HouMinXi +- fix(cli): run the Node.js runtime compatibility guard before the heavy `tsx/esm` + Commander import chain so an unsupported runtime gets a clear message instead of a raw `Invalid regular expression flags` crash (#12296) ([#13253](https://github.com/diegosouzapw/OmniRoute/pull/13253)) +- fix(dashboard): refresh the providers list after deleting a compatible provider node (#12298) ([#13256](https://github.com/diegosouzapw/OmniRoute/pull/13256)) +- fix(usage): fail closed on API-key budget enforcement when a provider's `auto` routing alias has no pricing row, instead of silently counting it as $0 (#12341) ([#13257](https://github.com/diegosouzapw/OmniRoute/pull/13257)) +- **fix(providers):** include the submitted Agnes video model when polling by `video_id` ([#12356](https://github.com/diegosouzapw/OmniRoute/pull/12356)) — thanks @pacocartones +- **fix(routing):** custom OpenAI-compatible nodes now honor the saved Chat/Responses API type after edits instead of letting the node's original ID prefix override the live connection setting ([#11884](https://github.com/diegosouzapw/OmniRoute/issues/11884)). ([#12358](https://github.com/diegosouzapw/OmniRoute/pull/12358)) — thanks @pacocartones +- fix(sse): surface an error instead of a silent empty 200 when a Claude stream closes with zero bytes (#12398) ([#13285](https://github.com/diegosouzapw/OmniRoute/pull/13285)) +- fix(oauth): warn before the dead localhost:8080 redirect in antigravity/gemini `oauth start` (#12413) ([#13265](https://github.com/diegosouzapw/OmniRoute/pull/13265)) +- fix(providers): stop devin-cli spawn error from double-closing the SSE controller (#12517) ([#13261](https://github.com/diegosouzapw/OmniRoute/pull/13261)) +- **fix(rerank):** clamp Voyage `top_k` to the documents actually sent after empty-string filtering, and honor `return_documents: false` in the NVIDIA response adapter (#12523 — thanks @pacocartones) +- **fix(audio):** `/v1/audio/translations` now resolves combo names the way `/v1/audio/transcriptions` already does, so a combo that `GET /v1/models` advertises is fanned out to its targets instead of being rejected with `400 Invalid translation model: . Use format: provider/model`; literal `provider/model` ids and unknown bare names behave as before (#12536 — thanks @pacocartones) +- **fix(gemini):** strip the JSON-Schema-2020-12 `prefixItems` keyword from Gemini tool schemas at every nesting level, so Claude Code tool definitions no longer fail with `400 Unknown name "prefixItems"` on Gemini models (#12540 — thanks @pacocartones) +- **fix(api-manager):** Expose an accessible loading status while API keys are fetched instead of an empty accessibility tree (#12541 — thanks @pacocartones) +- **fix(video):** Clamp `estimateJpegFrameBytes` at zero for padding-only payloads and build the three encode-side frame data URIs from `JPEG_FRAME_DATA_URI_PREFIX` instead of a repeated literal (#12543 — thanks @pacocartones) +- **fix(devin):** accept Windows `DEVIN_AGENTIC_HOME` sandbox paths (`C:\...\.sandbox\...`) in the isolated-home check so the Devin Claude Bridge no longer fails closed on Windows ([#12405](https://github.com/diegosouzapw/OmniRoute/issues/12405)) (#12545 — thanks @pacocartones) +- **fix(db):** Add `conversation_turn_nodes` and orphaned `agentic_conversations` to the auto-cleanup cycle under the existing `retention.callLogs` window, so identity nodes whose call-log content has already been purged no longer accumulate without bound in `storage.sqlite` (#12548 — thanks @pacocartones) +- **fix(i18n):** Wrap the `~/.claude/profiles//settings.json` placeholder in ICU single quotes in the `OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES` feature-flag description across all 42 locales and the TypeScript default, so next-intl no longer fails with `INVALID_MESSAGE: UNCLOSED_TAG` and the Feature Flags card shows the description instead of the raw key (#12549 — thanks @pacocartones) +- **fix(orchestration):** `updateCloudAgentTask` now publishes the task's real `status` on `agent.task.updated` when an update only touches `result`, `activities` or `error`, instead of the fabricated `"updated"` state, and stays silent when no row matched the id (#12550 — thanks @pacocartones) +- **fix(i18n):** the home "Recent Requests" panel and the Provider Topology legend are now translated instead of rendering English copies on non-English dashboards; the legend reads its own `home.topologyLegend*` labels with consistent casing rather than borrowing the memory-settings "Recent" and analytics "Error" strings (#12551 — thanks @pacocartones). +- **docs(reference):** bring the `FEATURE_FLAGS.md` catalog back to 1:1 with `featureFlagDefinitions.ts` — 20 missing flags added, the two `*_BLOCK_THRESHOLD` env-only knobs moved out of the flag tables, category/total counts and the Live WS port corrected, guarded by a static test (#12552 — thanks @pacocartones) +- **fix(i18n):** backfill missing `usage.kiloPass*` strings in 39 locales and restore `featureFlagOmnirouteDisableThinkingLevelVariantsDescription` in `pt.json` ([#12561](https://github.com/diegosouzapw/OmniRoute/issues/12561)) ([#13027](https://github.com/diegosouzapw/OmniRoute/pull/13027)) — thanks @HouMinXi +- fix(docker): default docker-compose app ports (dashboard/API/live-WS) to loopback instead of `0.0.0.0`, closing the anonymous `/v1` LAN/WAN exposure gap left open by `REQUIRE_API_KEY=false` (#12568) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): close DNS-rebinding SSRF gap in webhook outbound-URL guard (#12569) ([#13243](https://github.com/diegosouzapw/OmniRoute/pull/13243)) +- fix(docker): require a per-session token on the VNC browser CDP bridge and isolate it on a dedicated Docker network (#12571) ([#13241](https://github.com/diegosouzapw/OmniRoute/pull/13241)) +- fix(open-sse): write Adobe Firefly session tokens and cookie jars with 0700/0600 permissions instead of the process umask (#12572) ([#13242](https://github.com/diegosouzapw/OmniRoute/pull/13242)) +- fix(api): remove hardcoded wildcard CORS in openai-to-gemini-sse.ts so the centralized fail-closed CORS gate is the sole source of `Access-Control-Allow-Origin` (#12573) ([#13239](https://github.com/diegosouzapw/OmniRoute/pull/13239)) +- fix(api): enforce API key policy (budget/rate-limit/schedule/endpoint scoping) on the ElevenLabs speech-to-text, text-to-speech and voices proxy routes (#12574) ([#13244](https://github.com/diegosouzapw/OmniRoute/pull/13244)) +- fix(sse): cap HuggingChat NDJSON body size and bound the read loop with the fetch timeout so a stalled or hostile upstream cannot buffer unbounded memory (#12577) ([#13282](https://github.com/diegosouzapw/OmniRoute/pull/13282)) +- fix(docker): scope the cliproxyapi/qdrant/bifrost sidecars to loopback by default and forward `CLIPROXYAPI_MANAGEMENT_KEY` into the cliproxyapi container so its management API is not left both unauthenticated and LAN-published (#12578) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): create DB export temp paths with `fs.mkdtempSync` instead of predictable timestamps (#12579) ([#13237](https://github.com/diegosouzapw/OmniRoute/pull/13237)) +- Cline 401 bodies that say "re-authenticate your Cline account" classify as a refreshable OAuth token, not a terminal expired key. The cooling panel no longer labels every cooldown as a 429; it shows the recorded last error instead. (#12594) ([#13060](https://github.com/diegosouzapw/OmniRoute/pull/13060)) — thanks @HouMinXi +- **fix(catalog):** degrade unknown combo targets instead of dropping LCD modalities, and persist OpenRouter `architecture.input_modalities` into the capability snapshot ([#12613](https://github.com/diegosouzapw/OmniRoute/issues/12613)) ([#13042](https://github.com/diegosouzapw/OmniRoute/pull/13042)) — thanks @HouMinXi +- fix(providers): send `x-api-key` instead of `Authorization: Bearer` for OpenCode Zen's `/v1/responses` endpoint (Muse Spark Contributor models), fixing a 401 on OmniRoute's auth header (#12633) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(sse): surface the actionable "Auggie CLI not found" message when the shell reports a missing `auggie` binary via exit code instead of a spawn error (#12645) ([#13263](https://github.com/diegosouzapw/OmniRoute/pull/13263)) +- **fix(config):** Persist deletions of built-in background-degradation entries — when a stored settings record exists its `degradationMap` is now authoritative instead of being merged under the defaults, so an entry the user removed in the dashboard no longer reappears on the next apply or restart ([#12424](https://github.com/diegosouzapw/OmniRoute/issues/12424)) ([#12647](https://github.com/diegosouzapw/OmniRoute/pull/12647)) — thanks @pacocartones +- **fix(gamification):** action-count badge milestones (First Token, Token Consumer, Token Machine, Token Whale, and the token-sharing tier) are now backed by a durable `xp_action_counts` counter incremented in `addXp()`, instead of a live `COUNT(*)` over `xp_audit_log`. The audit log is pruned by `retention.xpAuditLog` (default 30 days), so on a default install those "lifetime" milestones were really "actions in the last 30 days" and unlocked badges could stop unlocking once old rows aged out. `getActionCount()` and `checkActionCountBadges()` now read the same durable source, and a migration backfills existing totals from the surviving audit rows ([#12546](https://github.com/diegosouzapw/OmniRoute/issues/12546)) ([#12651](https://github.com/diegosouzapw/OmniRoute/pull/12651)) — thanks @pacocartones +- **fix(images):** `/v1/images/edits` now iterates a combo's targets the same way `/v1/images/generations` does (#9239) instead of flattening a bare combo to its first target. A combo whose first target is not edit-capable — or lacks credentials — now falls through to a later edit-capable target rather than hard-erroring, and missing credentials are skipped (not a hard `401`) to match the generations path. The per-target skip/terminal classification is extracted into a shared `runImageComboTargets` loop, so generations behavior is unchanged ([#12547](https://github.com/diegosouzapw/OmniRoute/issues/12547)). ([#12653](https://github.com/diegosouzapw/OmniRoute/pull/12653)) — thanks @pacocartones +- fix(sse): add first-byte watchdog to the TLS-fingerprint transport so a stalled wreq body falls back instead of hanging for minutes (#12656) ([#13272](https://github.com/diegosouzapw/OmniRoute/pull/13272)) +- fix(sse): exempt tiny-budget reasoning probes from combo quality failure and surface persisted-cooldown skips in ALL_TARGETS_SKIPPED diagnostics (#12659) ([#13269](https://github.com/diegosouzapw/OmniRoute/pull/13269)) +- fix(models): declare the real ~1M contextLength for OpenCode Zen's Muse Spark 1.2 models instead of falling back to the 200000 provider default (#12681) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(oauth): align codebuddy-cn OAuth User-Agent with the chat/usage CLI version to avoid WAF false positives (#12702) ([#13264](https://github.com/diegosouzapw/OmniRoute/pull/13264)) +- fix(dashboard): surface an authentication-required banner instead of silently blanking database settings for a guest session (#12709) ([#13270](https://github.com/diegosouzapw/OmniRoute/pull/13270)) +- **fix(resilience):** stop unbounded queue that hangs 6min until Aborted — gate, provider slot, and Bottleneck queue now share a per-connection `maxWaitMs` budget; `fail-closed` on exhaust (503); execution backstop `executionMaxWaitMs` overridable per connection with upstream clamp ([#12715](https://github.com/diegosouzapw/OmniRoute/pull/12715)) — thanks @maxmad64bis (with thanks to @Tushar49 for surfacing the slow-provider need in #12635) +- **fix(auto-combo):** every mode pack now carries `quality` and `reliability` weights (`quality` 0.02, 0.03 in `quality-first`; `reliability` 0.03, 0.04 in `reliability-first`), so selecting a pack no longer silences either signal; `DEFAULT_WEIGHTS` is unchanged ([#12731](https://github.com/diegosouzapw/OmniRoute/pull/12731)) — thanks @maxmad64bis +- **fix(sse):** the provider execution pipeline keeps the upstream error `code`/`type` again — both were lost when the non-streaming leg was extracted, so a config-class `422` (Antigravity missing project) degraded into a generic account cooldown instead of reaching the gate that recognises it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(db):** a legacy `call_logs` table no longer aborts startup — the provider-stats index is created after the column healing runs, not before it, so an install predating `request_type` opens instead of failing with `no such column` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(catalog):** a malformed row in the operator-writable custom-models blob no longer kills every `auto/*` pool ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(tests):** realign the guards that the same day's merges left asserting the old behavior — the provider-count total after GreenPT and EURouter, the GLM stream buffer slot that `#12925` turned into a declared parameter, and the injection scan bound that `#13104` reshaped from a head-only window into head-plus-tail ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- fix(cache): fold tool_choice/tools/response_format into the semantic cache signature so a cached tool_calls response can no longer be replayed for a request whose tool policy forbids it (#12734) ([#13267](https://github.com/diegosouzapw/OmniRoute/pull/13267)) +- **fix(models):** stop treating provider-supplied `isFree:true`/`:free`/`0/0` flags as trusted unless the provider has a documented free tier — only the shipped free-tier catalog decides otherwise; fetched `isFree:true` trusted only for a free-tier provider, custom `isFree:true` via a trusted path ([#12744](https://github.com/diegosouzapw/OmniRoute/pull/12744)) — thanks @maxmad64bis +- fix(memory): authenticate the internal /v1/rerank loopback call so memory reranking no longer silently degrades to unranked order when REQUIRE_API_KEY=true (#12745) ([#13268](https://github.com/diegosouzapw/OmniRoute/pull/13268)) +- fix(sse): parse Ollama Cloud's current usage markup (`$X of $Y used` aria-label, nested width style) (#12749) ([#13260](https://github.com/diegosouzapw/OmniRoute/pull/13260)) +- fix(cli): setup-opencode no longer sends an active context's management token to `/v1/models` when `--api-key`/`OMNIROUTE_API_KEY` is supplied — an explicit flag or the env var now always outranks the context's token, and the flag itself is no longer swallowed by the parent program's global `--api-key` option (#12783) ([#13246](https://github.com/diegosouzapw/OmniRoute/pull/13246)) +- fix(sse): register Arcee AI in the executor provider registry so requests reach api.arcee.ai instead of silently falling back to OpenAI (#12784) ([#13277](https://github.com/diegosouzapw/OmniRoute/pull/13277)) +- **fix(quota):** share one upstream quota read when identical saturation checks arrive at the same time, so traffic bursts don't multiply provider API calls ([#12787](https://github.com/diegosouzapw/OmniRoute/pull/12787)) — thanks @maxmad64bis +- **fix(db):** ignore expired or invalid rate-limit cooldown writes so a stale timestamp can't lock a connection that should be usable — clearing still works as before ([#12788](https://github.com/diegosouzapw/OmniRoute/pull/12788)) — thanks @maxmad64bis +- **fix(providers):** single source for provider order with `xao` ranking alongside `xai-oauth` ([#12790](https://github.com/diegosouzapw/OmniRoute/pull/12790)) — thanks @maxmad64bis +- **fix(routing):** off-table models check the free-model catalog before inheriting premium prices, latency bootstraps from the observed pool median, async tiers read live database pricing with a 90-day freshness gate, and the tier cache invalidates on every pricing write ([#12792](https://github.com/diegosouzapw/OmniRoute/pull/12792)) — thanks @maxmad64bis +- **fix(auto-combo):** snapshot scoring uses the observed per-connection account tier and per-model quality instead of neutral constants, and circuit-open providers rank lower at build time ([#12794](https://github.com/diegosouzapw/OmniRoute/pull/12794)) — thanks @maxmad64bis +- **fix(pool):** empty `auto/*` pools now say why they are empty, and the models listing uses the same paid check as routing ([#12795](https://github.com/diegosouzapw/OmniRoute/pull/12795)) — thanks @maxmad64bis +- fix(routing): recognize CLIProxyAPI's 'unknown provider for model' 400 as fallback-worthy (#12800) ([#13284](https://github.com/diegosouzapw/OmniRoute/pull/13284)) +- **fix(sse):** translate-mode streams now emit the estimated token counts as a trailing usage-only chunk before `[DONE]` when the upstream stays silent, so metered chat clients see totals instead of nothing ([#12828](https://github.com/diegosouzapw/OmniRoute/pull/12828)) — thanks @maxmad64bis +- **fix(db):** provider stats stay truthful on empty and legacy databases: fallback counts default to `0` instead of `null`, latency averages read `null` (not `0`) when no durations were recorded, failures logged before the error-type column existed group under `pre_migration` instead of `unclassified`, and per-provider queries use two new composite indexes ([#12832](https://github.com/diegosouzapw/OmniRoute/pull/12832)) — thanks @maxmad64bis +- fix(nvidia): fail open when a synced model catalog goes stale instead of gating forever (#12849) ([#13248](https://github.com/diegosouzapw/OmniRoute/pull/13248)) +- **fix(db):** WAL maintenance lives in its own module: a `TRUNCATE` checkpoint that hits a busy database now warns with its streak and retries once via `PASSIVE` instead of logging success, and closing-time checkpoints no longer report success on builds without a database file, with the busy totals visible in the authenticated monitoring health payload ([#12853](https://github.com/diegosouzapw/OmniRoute/pull/12853)) — thanks @maxmad64bis +- **fix(health):** quota with no snapshots reads empty instead of a contradicting 0% ([#12857](https://github.com/diegosouzapw/OmniRoute/pull/12857)) — thanks @maxmad64bis +- fix(a2a): accept the dashboard's own session cookie on /a2a so "Run message/send" no longer fails with "Unauthorized: missing or invalid API key" (#12888) ([#13271](https://github.com/diegosouzapw/OmniRoute/pull/13271)) +- **fix(logs):** the Logs grid's in-memory filter pass no longer discards rows the SQL query already matched — selecting an API key from the dropdown (which sends the key's id) returns its calls again, the Combo tab shows every combo instead of only those whose name contains a "1", and the model filter and search cover the same columns as the query ([#12896](https://github.com/diegosouzapw/OmniRoute/pull/12896)) — fixes [#12873](https://github.com/diegosouzapw/OmniRoute/issues/12873) — thanks @ntdatt812 +- **fix(a2a):** `/api/a2a/status` now builds the agent card from the request that asked for it, so a gateway reached at a non-localhost host no longer advertises `http://localhost:20128` as its A2A URL ([#12918](https://github.com/diegosouzapw/OmniRoute/pull/12918)). — thanks @ntdatt812 +- **fix(compression):** progressive aging now appends its `[COMPRESSED:aging:…]` annotation after a turn's `tool_result` blocks instead of in front of them, so Anthropic no longer rejects aged conversations with "`tool_use` ids were found without `tool_result` blocks immediately after" ([#12920](https://github.com/diegosouzapw/OmniRoute/pull/12920)). — thanks @ntdatt812 +- **fix(bedrock):** model import now resolves context limits for every vendor prefix instead of only `anthropic.*`, so `global.openai.gpt-5.6-*` no longer imports with a null `inputTokenLimit` and gets rejected pre-flight at the 200k default ([#12921](https://github.com/diegosouzapw/OmniRoute/pull/12921)). — thanks @ntdatt812 +- **fix(stream):** the 64 KB stream buffer GLM asks for is honoured instead of dropped, and the type error it caused no longer fails the API Route Typecheck gate on every open PR ([#12925](https://github.com/diegosouzapw/OmniRoute/pull/12925)) — thanks @ntdatt812 +- **fix(guardrails):** mask PII inside a `tool_result`'s nested content array, which the masker walked past while redacting its sibling block ([#12930](https://github.com/diegosouzapw/OmniRoute/pull/12930)) — thanks @ntdatt812 +- **fix(dashboard):** the Radar catalog table no longer leaves absent data unexplained — empty limits, unknown context windows, and unreported capabilities each explain themselves on hover, and a new check keeps it that way ([#12937](https://github.com/diegosouzapw/OmniRoute/pull/12937)) — thanks @maxmad64bis +- **fix(sse):** geo-blocked opencode requests rotate to the next account proxy instead of failing, so one refused egress no longer aborts the whole chain ([#12941](https://github.com/diegosouzapw/OmniRoute/pull/12941)) — thanks @maxmad64bis +- **fix(codex):** dashboard "clear cooldown" and the CAS recovery path now drop nested `codexScopeRateLimitedUntil` maps in the same write that nulls `rate_limited_until`, and fresh quota snapshots with headroom lift fallback-sourced scope cooldowns parked by quota preflight ([#12817](https://github.com/diegosouzapw/OmniRoute/issues/12817), [#12860](https://github.com/diegosouzapw/OmniRoute/issues/12860), [#12951](https://github.com/diegosouzapw/OmniRoute/pull/12951)) The dashboard reset-credit button (`consumeCodexResetCredit`) now exercises that same snapshot path after a successful redeem, so a filled quota bar is enough to unpark a leftover fallback Codex child without another manual clear. — thanks @HouMinXi +- **fix(db):** resolve `sql-wasm.wasm` across global npm install and hoisted layouts, ensuring OmniRoute can boot cleanly on Node 24 when native `better-sqlite3` is uncompiled. ([#13035](https://github.com/diegosouzapw/OmniRoute/pull/13035)) — thanks @HouMinXi +- **fix(api):** `DELETE /v1/batches/delete-completed` now sweeps only the calling API key's own completed batches (batches with no owner stay out of a key-scoped sweep on purpose), with an explicit instance-wide mode reserved for authenticated dashboard sessions, a 401 for a presented key that is unknown, revoked, deactivated, banned or expired (never falling through to the session branch), audit logging of every sweep, a sanitized 500 on failure, an owner-scoped file half (a key-scoped sweep never nulls a file another tenant owns) and an atomic sweep — chunked in 200-batch transactions in instance mode — so a mid-way error never leaves a batch pointing at a nulled file (GHSA-wvxc-jp3v-5mg5) ([#12969](https://github.com/diegosouzapw/OmniRoute/pull/12969)) +- **fix(combo):** quota-weighted routing stops drawing on an out-of-credit connection — a 402 now invalidates the stored quota snapshot instead of leaving its stale remaining percentage in place, and a snapshot older than 10 minutes no longer counts as confident headroom for the primary pool ([#12972](https://github.com/diegosouzapw/OmniRoute/pull/12972)) ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- **fix(vision):** Custom Models with "Vision capable" checked no longer have image requests swapped to `glm/glm-4.6v` when the client sends the advertised alias (`vllm/path/...`) or the bare path-shaped id — Vision Bridge now matches the stored override for all three id forms ([#12758](https://github.com/diegosouzapw/OmniRoute/issues/12758)) ([#12974](https://github.com/diegosouzapw/OmniRoute/pull/12974)) — thanks @HouMinXi +- **fix(sse):** transient opencode upstream failures rotate to the next account proxy instead of failing, so one flapping egress no longer aborts the whole chain ([#12975](https://github.com/diegosouzapw/OmniRoute/pull/12975)) — thanks @maxmad64bis +- **fix(azure):** Deployments from GPT-6 onward now send `max_completion_tokens` instead of `max_tokens`, which Azure rejects with HTTP 400. The rule matched a literal `gpt-5`, so each new generation arrived broken; it now matches the generation range, while `gpt-35-turbo` still keeps `max_tokens`. ([#13007](https://github.com/diegosouzapw/OmniRoute/pull/13007)) — thanks @ntdatt812 +- **fix(db):** add an opt-in self-restart circuit for sustained critical memory pressure, gate post-cleanup VACUUM behind a minimum freed-rows threshold, and checkpoint the SQLite WAL every 5 minutes with a size guard that escalates to TRUNCATE, so a growing WAL can no longer stall the event loop into a full outage. ([#13011](https://github.com/diegosouzapw/OmniRoute/pull/13011)) — thanks @HouMinXi +- **fix(auth):** an explicit connection pin may probe a quota-disabled row once and re-enable it on success ([#12874](https://github.com/diegosouzapw/OmniRoute/issues/12874)) ([#13017](https://github.com/diegosouzapw/OmniRoute/pull/13017)) — thanks @HouMinXi +- **fix(api):** thread `X-OmniRoute-Fallback-Attempts` through combo chat completions so streaming and non-streaming responses report how many prior legs were attempted ([#13038](https://github.com/diegosouzapw/OmniRoute/pull/13038)) — thanks @HouMinXi +- Restore provider failure classification and credential refresh on non-streaming requests: classify non-2xx failures to lock models on per-model quota exhaustion, update connection rate limits from headers and body, and pass credential refresh handlers to pipeline execution so 401 tokens can be refreshed and retried (#13043). ([#13069](https://github.com/diegosouzapw/OmniRoute/pull/13069)) — thanks @HouMinXi +- **fix(responses):** wrap forced-non-streaming web_search fallback JSON as Responses SSE so Codex still sees `response.completed` ([#13050](https://github.com/diegosouzapw/OmniRoute/pull/13050)) — thanks @HouMinXi +- **fix(antigravity):** Preserve upstream thought-token usage in normalized completion and reasoning token counts ([#13055](https://github.com/diegosouzapw/OmniRoute/pull/13055)) — thanks @pacocartones +- Fix a concurrency-slot leak in the DeepSeek PoW solver: a worker that failed to spawn (for example a missing worker script) never released its slot, so `MAX_CONCURRENT_WORKERS` failures disabled the solver until restart. ([#13097](https://github.com/diegosouzapw/OmniRoute/pull/13097)) — thanks @anhtahaylove +- **fix(acp):** bound the ACP session output buffers — `stdoutBuffer` and `stderrBuffer` now cap at 1 MiB keeping the most recent output behind a visible `[...output truncated...]` marker, and `stderrBuffer` is reset per prompt instead of accumulating for the lifetime of the session. ([#13100](https://github.com/diegosouzapw/OmniRoute/pull/13100)) — thanks @anhtahaylove +- **fix(acp):** release the `stdout`/`exit` listeners and the idle timer that a `sendPrompt` timeout used to leave attached to the `acpManager` singleton, and drop sessions that exited on their own from the session map instead of keeping them forever. ([#13096](https://github.com/diegosouzapw/OmniRoute/pull/13096)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection and PII scanners now read the text a `tool_result` block carries on `content` (string or nested block list), in messages and in system blocks, so tool output is judged by the same rules as user text ([#13101](https://github.com/diegosouzapw/OmniRoute/pull/13101)) — thanks @ntdatt812 +- **fix(gamification):** close the badge notification SSE stream when the request signal is already aborted before the stream starts — a client that disconnects while the route is still awaiting auth used to leave both the 2s unlock poll and the 15s heartbeat running for the lifetime of the process. ([#13106](https://github.com/diegosouzapw/OmniRoute/pull/13106)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection scan now spends its 16 KB budget on both ends of the request instead of the first 16 KB only, so `system`, `instructions`, `query`, `documents` and the newest turns are no longer hidden behind one long message ([#13104](https://github.com/diegosouzapw/OmniRoute/pull/13104)) — thanks @ntdatt812 +- **fix(dashboard):** expose the Volcano Ark console cookie on quota scraping and unwrap connect-error objects so the dashboard shows the upstream message ([#13107](https://github.com/diegosouzapw/OmniRoute/pull/13107)) — thanks @HouMinXi +- **fix(db):** release the `beforeExit`/`SIGINT`/`SIGTERM` handlers when a `node:sqlite` adapter closes, so a closed adapter and its database handle are no longer pinned to `process` for the lifetime of the run — the same treatment #7494 gave the sql.js adapter. ([#13109](https://github.com/diegosouzapw/OmniRoute/pull/13109)) — thanks @anhtahaylove +- **fix(translator):** `contentSchema` and `unevaluatedItems` are now treated as subschema positions by the tool-schema sanitizer, so a truncation placeholder in either is replaced with a permissive schema instead of being forwarded as a string ([#13110](https://github.com/diegosouzapw/OmniRoute/pull/13110)) — thanks @ntdatt812 +- **fix(cli-helper):** clear the `createLogStream` timeout on the abort path — `stop()` aborts the in-flight fetch and returned through the `signal.aborted` branch, which skipped `clearTimeout` and left an armed timer per stopped stream. The stream reader is now also cancelled when the read loop exits early. ([#13114](https://github.com/diegosouzapw/OmniRoute/pull/13114)) — thanks @anhtahaylove +- **fix(docs):** drop TLS-impersonation, MITM-decrypt, supply-chain attestation, and XOR-mask writeups from the public `/docs` catalog and Docker image. Files stay in git for engineers; operators who need them open the repo, not the website. ([#13136](https://github.com/diegosouzapw/OmniRoute/pull/13136)) — thanks @HouMinXi +- **fix(combo):** parse numeric-epoch `rate_limited_until` in the combo cooldown read path ([#13141](https://github.com/diegosouzapw/OmniRoute/pull/13141)) — thanks @maxmad64bis +- **fix(opencode):** both OpenCode plugins now reject a gateway address typed without `http://` at configuration time, instead of publishing every model with an api url no client can call, and the v2 plugin no longer publishes a model card whose api url is blank or relative ([#13142](https://github.com/diegosouzapw/OmniRoute/pull/13142)) — thanks @maxmad64bis +- **fix(providers):** lock opencode model on upstream 400 model-unavailable ([#13146](https://github.com/diegosouzapw/OmniRoute/pull/13146)) — thanks @maxmad64bis +- **fix(logging):** keep the provider exchange rather than the raw client bodies when a call log exceeds its size budget, and show that recovered payload in the request-detail panel instead of replacing it with the stored response body ([#13147](https://github.com/diegosouzapw/OmniRoute/pull/13147)) — thanks @maxmad64bis +- **fix(traffic-inspector):** the WebSocket route no longer leaks a traffic-buffer subscriber and a 30s ping timer when the client socket is already closed at handler time — listeners are attached before any resource is acquired, a destroyed socket bails out early, and the ping interval stops on a dead socket where `write()` never throws ([#13155](https://github.com/diegosouzapw/OmniRoute/pull/13155)) — thanks @anhtahaylove +- **fix(telegram):** bound the per-user API key cache in the Telegram chat proxy so a burst of distinct chat ids can no longer grow the process heap without limit ([#13165](https://github.com/diegosouzapw/OmniRoute/issues/13165)) ([#13166](https://github.com/diegosouzapw/OmniRoute/pull/13166)) — thanks @anhtahaylove +- **fix(stream):** cancel the upstream response body when the JSON-to-SSE sniff unwinds on a body timeout, so a stalled upstream no longer pins the connection ([#13169](https://github.com/diegosouzapw/OmniRoute/issues/13169)) ([#13171](https://github.com/diegosouzapw/OmniRoute/pull/13171)) — thanks @anhtahaylove +- **fix(telegram):** authenticate webhook deliveries with Telegram's `secret_token` so an unauthenticated caller can no longer mint API keys or spend upstream quota ([#13172](https://github.com/diegosouzapw/OmniRoute/issues/13172)) ([#13175](https://github.com/diegosouzapw/OmniRoute/pull/13175)) — thanks @anhtahaylove +- **fix(models):** give discoverable Gemini 3.8 Flash ids their own 65536 output spec so Antigravity no longer clamps them to 16384 ([#13195](https://github.com/diegosouzapw/OmniRoute/pull/13195)) — thanks @HouMinXi +- **fix(dashboard):** leftover catalog-removed provider rows (gemini-cli) can be listed and purged from the providers page ([#13067](https://github.com/diegosouzapw/OmniRoute/issues/13067)) ([#13197](https://github.com/diegosouzapw/OmniRoute/pull/13197)) — thanks @HouMinXi +- **fix(auth):** a dashboard session now requires the `authenticated: true` claim that login, OIDC and the session refresh already emit — a JWT merely signed with `JWT_SECRET` (for example the Cursor CLI passthrough token, which any API-key holder can obtain) no longer verifies as the `auth_token` cookie on any route, the WebSocket handshake or the live server; existing sessions keep working ([#13298](https://github.com/diegosouzapw/OmniRoute/issues/13298)) ([#13375](https://github.com/diegosouzapw/OmniRoute/pull/13375)) +- **fix(api):** `DELETE /v1/batches/delete-completed` now applies the caller's API-key policy (endpoint allowlist, schedule, usage cap, rate limit) like every other `/v1` route, commits the key-scoped sweep per 200-batch chunk instead of holding one write lock for the whole sweep, refuses to loop without progress, and rejects a scope that names both a key and `allTenants` ([#13684](https://github.com/diegosouzapw/OmniRoute/pull/13684)) +- Restore the API-route typecheck gate: the non-streaming leg result lost its discriminated-union narrowing after the server-owned tool loop reassignment, producing 13 new TS2339 diagnostics in `chatCore.ts`. ([#12963](https://github.com/diegosouzapw/OmniRoute/pull/12963)) +- Restore four non-streaming behaviours the server-owned tool loop refactor dropped: client aborts map to 499 with the fixed `Request aborted` message, an aborted request no longer logs a synthetic `clientResponse`, Claude prompt-cache telemetry is recorded again, and a body that cannot be canonicalized no longer throws when the tool loop is off. ([#12990](https://github.com/diegosouzapw/OmniRoute/pull/12990)) +- **fix(skills):** The CLI registry parser now reads positionals declared with `.addArgument()`, not only those written inline in `.command()`. `tunnel create [type]` was being published as `tunnel create`, so the agent-skills sync gate reported drift on every branch and regenerating would have deleted the argument. ([#13009](https://github.com/diegosouzapw/OmniRoute/pull/13009)) — thanks @ntdatt812 +- **fix(combos):** dashboard combo test uses a short prompt, serial probes, and a 60s timeout so reasoning models and rate-limited free pools do not fail the health check ([#13001](https://github.com/diegosouzapw/OmniRoute/pull/13001)) — thanks @HouMinXi +- fix(compression): terminate idle worker threads on eviction so long-running instances stop leaking OS threads and MessagePorts ([#13091](https://github.com/diegosouzapw/OmniRoute/pull/13091)) — thanks @anhtahaylove +- Redact Google API keys of any length in error bodies: the pattern required exactly 39 characters, so shorter or longer `AIza…` credentials passed through unredacted. ([#12964](https://github.com/diegosouzapw/OmniRoute/pull/12964)) +- **fix(grok-cli):** a 402 "Grok Build usage balance exhausted" parks that Grok login as out of credit (Grok Build CLI, grok.com cookie, and xAI OAuth share the weekly pool). Combo routing then tries the next login instead of locking the model for every account in the pool ([#13061](https://github.com/diegosouzapw/OmniRoute/pull/13061)) — thanks @HouMinXi +- fix(compression): spawn the LLMLingua worker with a file URL object so compression actually runs instead of silently failing open on Node ([#13093](https://github.com/diegosouzapw/OmniRoute/pull/13093)) — thanks @anhtahaylove +- **fix(test):** run the local `test` and `test:unit` scripts at concurrency 4 so a full-suite run no longer exhausts the machine's commit charge and kills unrelated processes ([#13187](https://github.com/diegosouzapw/OmniRoute/pull/13187)) — thanks @anhtahaylove +- **fix(monitoring):** `GET /api/monitoring/health` `credentialHealth` now includes a bounded `failedConnections` list (`connectionId`, `status`, sanitized `lastError`) when the probe-cache gauge `failed>0`, plus `source: probe-cache` and a cheap `staleDbNonOkCount` for sticky SQLite `test_status` on active rows. Documents that the live gauge is not `provider_connections.test_status`. ([#12876](https://github.com/diegosouzapw/OmniRoute/pull/12876)) — thanks @RaviTharuma +- **fix(resilience):** an openai-compatible multi-upstream gateway no longer marks the whole connection `credits_exhausted` when a single upstream (or the `/models` / representative-model health probe) returns 402. Quota failures stay model-scoped; true connection-wide auth failures are unchanged. Single-credential 402 key disable ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239)) is preserved. ([#12875](https://github.com/diegosouzapw/OmniRoute/pull/12875)) — thanks @RaviTharuma +- **fix(dashboard):** nine fixes on the Orchestration Canvas, all diagnosed in the Phase 2 reviews. A source that fails now keeps the timestamp of its FIRST failure instead of being re-stamped every poll — the stale line said "since the last poll" no matter how long the source had been down, and the churn also defeated the snapshot's stable identity (it serializes the source list), so the canvas re-rendered on every tick while anything was broken. A source that HAD data and only then started failing is flagged too: previously only a source with no node at all got the warning, so a source that went down mid-session kept a healthy-looking node forever. The rest are pointwise: clicking a filter chip cancels the pending search debounce (left armed, it fired ~300ms later and silently reverted the chip); the search input carries an accessible name; `?state=running, failed` parses like the unpadded form instead of dropping the padded value; the CSV toggle helper is defined once in `model/urlParams.ts` rather than twice; the agents tab tells "nothing running" apart from "the filter matched nothing", offering a clear-filters button instead of setup links that would be wrong advice there; edges stop emitting SMIL particles above 40 simultaneously active edges, keeping the colored stroke; and the drawer's error banner clears when a retried action succeeds. ([#12983](https://github.com/diegosouzapw/OmniRoute/pull/12983)) +- **fix(i18n):** the nine locales added with the EU-language batch (Greek, Estonian, Irish, Croatian, Lithuanian, Latvian, Maltese, Slovenian, Serbian) were missing the eleven Orchestration Canvas keys that Phase 3 introduced, so the compare-runs panel and the "no runs match these filters" empty state fell back to English in those languages (`deepMergeFallback` substitutes English for an absent key, so nothing rendered blank — it rendered untranslated). The coverage gate does not catch this: it enforces an 80% floor per locale, and eleven missing keys out of ~13,000 leaves coverage at 99.9%. Translated for real in each language, calibrated against the wording each file already uses for "run", "filter" and "skill". ([#13294](https://github.com/diegosouzapw/OmniRoute/pull/13294)) +- **fix(dashboard):** five follow-ups from the Phase 2 Orchestration Canvas review. The History drawer now shows the memory section for runs that already left the live TTL window: the persisted `memory_hits` event is parsed into `metadata.memoryHits` with the same defensive validation the drawer applies, and — because that event is observability rather than a state transition — it no longer leaks into the timeline, where it had been inheriting the task's state and rendering as a duplicate transition. The A2A memory recall runs against its own 1.5s deadline instead of inheriting the memory backend's 30s one; overshooting degrades exactly like any other recall failure (no hits, task proceeds), and the timer is cleared on both paths. Repeating a Conductor run carries its `requirements.cli`/`requirements.model` forward, so the new run is pinned to the same runner profile and model rather than drifting to whatever the fleet picks. A successful repeat from the Agents tab now focuses the run it created, instead of leaving the operator on the finished one. And the auth test for `POST /api/conductor/tasks` moved into the shared `ROUTES` array rather than restating the pattern. ([#12988](https://github.com/diegosouzapw/OmniRoute/pull/12988)) +- fix(plugins): stop leaking an exit listener per plugin hook timeout, which triggered MaxListenersExceededWarning on plugins that ignore SIGTERM ([#13092](https://github.com/diegosouzapw/OmniRoute/pull/13092)) — thanks @anhtahaylove +- **fix(validation):** Provider node edits no longer fail with a generic "Invalid request" when the optional daily-quota reset fields are left blank. The dashboard sends `dailyQuotaResetTimezone` and `dailyQuotaResetHour` as `null`, and only the hour accepted it. ([#13066](https://github.com/diegosouzapw/OmniRoute/issues/13066)) ([#13083](https://github.com/diegosouzapw/OmniRoute/pull/13083)) — thanks @ntdatt812 +- **fix(combo):** quota-aware expansion drops banned, inactive, missing, and wrong-provider connections before quota fetch or model dispatch; pins and allowlists stay selectors, not a bypass. Antigravity automatic exhaustion now requires a reported zero remaining, so a positive balance below 1% stays eligible. ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- Fix an intermittent failure in the quota-weighted routing test suite: scores are a function of `Date.now()`, so peers with identical quota scored microseconds apart never tied and swapped order. ([#12946](https://github.com/diegosouzapw/OmniRoute/pull/12946)) +- fix(volcengine): resolve quota window mapping gaps, safe multi-connection console binding, and response cookie sanitization ([#12950](https://github.com/diegosouzapw/OmniRoute/pull/12950)) — thanks @HouMinXi +- **fix(test):** resolve the WebDAV handler path with `fileURLToPath` so the suite's 37 WebDAV tests run on Windows instead of failing with a doubled `C:\C:\` drive prefix ([#13196](https://github.com/diegosouzapw/OmniRoute/pull/13196)) — thanks @anhtahaylove +- **fix(ui):** fall back from invalid AnySearch icon alias (direct commit `9587e07b69`) +- **fix(sse):** stop rebuilding a truncated summary from the collector's cap-dropped event array ([#12718](https://github.com/diegosouzapw/OmniRoute/pull/12718)) — thanks @hartmark +- **fix(conversations):** show a pending spinner for unresolved tool nodes instead of "(empty)" ([#12727](https://github.com/diegosouzapw/OmniRoute/pull/12727)) — thanks @hartmark +- **fix(streaming):** fail fast when an upstream stream produces only lifecycle/heartbeat events, never real content ([#12741](https://github.com/diegosouzapw/OmniRoute/pull/12741)) — thanks @hartmark +- **fix(responses-continuation):** bridge the write-in-flight window with an in-memory pending store ([#12854](https://github.com/diegosouzapw/OmniRoute/pull/12854)) — thanks @hartmark +- **fix(translator):** drop or map agent_message on Chat Completions fallback ([#12880](https://github.com/diegosouzapw/OmniRoute/pull/12880)) — thanks @RaviTharuma +- **fix(api):** page and stream-complete GET /v1/models for large catalogs ([#12882](https://github.com/diegosouzapw/OmniRoute/pull/12882)) — thanks @RaviTharuma +- **fix(quota):** align AUTH window labels with usage API and clarify 503 ([#12884](https://github.com/diegosouzapw/OmniRoute/pull/12884)) — thanks @RaviTharuma +- **fix(ci):** clear two base-reds on release/v3.8.51 — mutation-coverage gate + image-only-model guard ([#12945](https://github.com/diegosouzapw/OmniRoute/pull/12945)) +- **fix(api):** scope batch bulk-delete to the calling API key ([#13211](https://github.com/diegosouzapw/OmniRoute/pull/13211)) +- **fix(tests):** align model-sync log assertion to shipped path redaction (base-red) ([#13227](https://github.com/diegosouzapw/OmniRoute/pull/13227)) +- **fix(test):** make the ToS heading guard actually require the parentheses ([#13228](https://github.com/diegosouzapw/OmniRoute/pull/13228)) +- **fix(quality):** register 4 drifted covering tests in stryker tap.testFiles ([#13229](https://github.com/diegosouzapw/OmniRoute/pull/13229)) +- **fix(logging):** mask *-api-key headers in request-log pipeline (#13273) ([#13401](https://github.com/diegosouzapw/OmniRoute/pull/13401)) — thanks @KooshaPari +- **fix(tests):** correct stale Jina catalog prefix in custom-models test ([#13403](https://github.com/diegosouzapw/OmniRoute/pull/13403)) — thanks @KooshaPari +- **fix(backend):** prune old backups after health-check-repair snapshots (#13308) ([#13404](https://github.com/diegosouzapw/OmniRoute/pull/13404)) — thanks @KooshaPari +- **fix(backend):** guard memory_fts_au trigger with WHEN clause to stop FTS bloat (#13326) ([#13405](https://github.com/diegosouzapw/OmniRoute/pull/13405)) — thanks @KooshaPari +- **fix(free-tiers):** remove obsolete Together $25 signup credit (#12526) ([#13407](https://github.com/diegosouzapw/OmniRoute/pull/13407)) — thanks @KooshaPari +- **fix:** skip non-chat generation models in Test all models ([#13409](https://github.com/diegosouzapw/OmniRoute/pull/13409)) — thanks @KooshaPari +- **fix(providers):** de-duplicate agy and antigravity model catalogs ([#13410](https://github.com/diegosouzapw/OmniRoute/pull/13410)) — thanks @KooshaPari +- **fix(dashboard):** send provider in body for mini-playground web search ([#13411](https://github.com/diegosouzapw/OmniRoute/pull/13411)) — thanks @KooshaPari +- **fix(resilience):** add CJK quota-exhaustion patterns to 429 classifier ([#13412](https://github.com/diegosouzapw/OmniRoute/pull/13412)) — thanks @KooshaPari +- **fix(backend):** eval runner: failed calls no longer score as passed + dotAll regex ([#13413](https://github.com/diegosouzapw/OmniRoute/pull/13413)) — thanks @KooshaPari +- **fix(backend):** eval runner opts out of output-style and memory injection ([#13414](https://github.com/diegosouzapw/OmniRoute/pull/13414)) — thanks @KooshaPari +- **fix:** chain serial quarantine step in npm test script (fixes #13286) ([#13418](https://github.com/diegosouzapw/OmniRoute/pull/13418)) — thanks @KooshaPari +- **fix(antigravity):** derive stable session ID from credentials for prompt-cache reuse (#13305) ([#13424](https://github.com/diegosouzapw/OmniRoute/pull/13424)) — thanks @KooshaPari +- **fix(memory):** merge memory into top-level system field instead of unshifting at messages[0] (#13425) ([#13427](https://github.com/diegosouzapw/OmniRoute/pull/13427)) — thanks @KooshaPari +- **fix(compression):** preserve instruction blocks from lossy engines ([#13523](https://github.com/diegosouzapw/OmniRoute/pull/13523)) — thanks @KooshaPari +- **fix(compression):** preserve polarity words and newlines in ultra heuristic ([#13524](https://github.com/diegosouzapw/OmniRoute/pull/13524)) — thanks @KooshaPari +- **fix(compression):** run user preservePatterns before built-in patterns (#13457) ([#13525](https://github.com/diegosouzapw/OmniRoute/pull/13525)) — thanks @KooshaPari +- **fix(reasoning):** expand deepseek-v4 regex to match v4.1+ and add command-code provider (#13430) ([#13528](https://github.com/diegosouzapw/OmniRoute/pull/13528)) — thanks @KooshaPari +- **fix(providers):** add alwaysReasons metadata for reka-flash-3 ([#13531](https://github.com/diegosouzapw/OmniRoute/pull/13531)) — thanks @KooshaPari +- **fix(playground):** allow Compare columns to scroll independently ([#13532](https://github.com/diegosouzapw/OmniRoute/pull/13532)) — thanks @KooshaPari +- **fix(security):** empty whitelist allows through to prevent admin lockout ([#13534](https://github.com/diegosouzapw/OmniRoute/pull/13534)) — thanks @KooshaPari +- **fix(tests):** add DISABLE_IOREG_STRATEGY env var for macOS test compatibility (fixes #13467) ([#13539](https://github.com/diegosouzapw/OmniRoute/pull/13539)) — thanks @KooshaPari +- **fix(zed-hosted):** map developer role and Gemini enums for Zed proxy ([#13543](https://github.com/diegosouzapw/OmniRoute/pull/13543)) — thanks @KooshaPari +- **fix(analytics):** normalize provider aliases to canonical id in usage_history ([#13545](https://github.com/diegosouzapw/OmniRoute/pull/13545)) — thanks @KooshaPari +- **fix(combo):** use traceId for call log id to prevent UNIQUE constraint failure ([#13546](https://github.com/diegosouzapw/OmniRoute/pull/13546)) — thanks @KooshaPari +- **fix(auth):** check model lockout before returning synthetic noauth connection ([#13547](https://github.com/diegosouzapw/OmniRoute/pull/13547)) — thanks @KooshaPari +- **fix(guardrails):** skip credential redaction for base64 image data URLs ([#13550](https://github.com/diegosouzapw/OmniRoute/pull/13550)) — thanks @KooshaPari +- **fix(resilience):** increase requestQueue.maxWaitMs default from 15s to 30s ([#13553](https://github.com/diegosouzapw/OmniRoute/pull/13553)) — thanks @KooshaPari +- **fix(docs):** document OMNIROUTE_READY_TIMEOUT_MS and allowlist the test-only DISABLE_IOREG_STRATEGY ([#13692](https://github.com/diegosouzapw/OmniRoute/pull/13692)) + ### 📝 Maintenance ---- +- **docs(readme):** reconcile live v3.8.50 provider, free-tier, CLI, routing, test, community, sponsor, acknowledgment, and SVG metrics with their audited source denominators, including a deduplicated OmniRoute-in-Action snapshot and distinct contributor rankings for merged pull requests, GitHub-attributed commits, and Git history ([#11356](https://github.com/diegosouzapw/OmniRoute/pull/11356)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **docs:** sync the canonical provider count 354 → 356 across `README.md`, `AGENTS.md`, `llm.txt` (+ 42 i18n mirrors), the four README SVG diagrams, `docs/reference/PROVIDER_REFERENCE.md` (regenerated) and the `package.json` description after Opper (#11629) and 1min.ai (#11631) boarded the catalog — closes the `check:docs-counts-sync` strict drifts that kept `release/v3.8.51` red ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11673](https://github.com/diegosouzapw/OmniRoute/pull/11673)) — thanks @jonlwheat2-gif +- **test(providers):** update count-derived assertions after the v3.8.51 provider additions — `APIKEY_PROVIDERS` 233 → 235 (Opper #11629 + 1min.ai #11631), reserved-prefix REGISTRY walk 395 → 398, `WEB_FETCH_PROVIDERS` now includes `nimble-search` (#11620), and the provider translate-path golden snapshot regenerated ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11674](https://github.com/diegosouzapw/OmniRoute/pull/11674)) — thanks @jonlwheat2-gif +- **test(sse):** bump the hard-lease connection-query inventory for `src/lib/tokenHealthCheck.ts` to 2 — the verify-only web-cookie sweep added by #11495 split the single `getProviderConnections` call into oauth + cookie variants, which the frozen inventory had not tracked ([#11495](https://github.com/diegosouzapw/OmniRoute/pull/11495)). ([#11677](https://github.com/diegosouzapw/OmniRoute/pull/11677)) — thanks @jonlwheat2-gif / @oyi77 +- **chore(release):** clear the `release/v3.8.51` base-red gates — docs migration count (+38 i18n mirrors), stryker `tap.testFiles`, lockfile registry host, stale ESLint suppressions, the 7 lint regressions they were hiding, the stale `APIKEY_PROVIDERS` partition count (231 → 233), the `glm-5.3-max` tier missing from the GLM test table, a dead `modelType === "chat"` comparison in the models catalog, and the `resolveLiveWsUrl` / `sanitizeLiveWsPort` helpers from #11388 that a later merge dropped from `wsPath.ts` (dashboard live WebSocket URL resolution) ([#11502](https://github.com/diegosouzapw/OmniRoute/pull/11502)) — thanks @MumuTW +- **chore(free-models):** the free-tier regime table now declares which totals figure each regime feeds and whether it may take the no-auth shortcut, so the sets that used to repeat those answers by hand are derived from it — a new `freeType` no longer compiles until it has answered every question, instead of silently contributing to no total ([#11537](https://github.com/diegosouzapw/OmniRoute/pull/11537)) — thanks @maxmad64bis +- **docs(routing):** LKGP strategy copy now states provider-level stickiness and the explicit rules fallback instead of promising per-target pinning ([#11555](https://github.com/diegosouzapw/OmniRoute/pull/11555)) — thanks @maxmad64bis +- **test(chatcore):** pin the response tool-name alias resolution against the ordering hazard that broke every Gemini/Antigravity MCP tool call in v3.8.49. `extractRequestToolIdentityMap` deletes `translatedBody._toolNameMap`, so the later read is always undefined and the ledger survives only through the `requestToolIdentityMap` fallback — removing that fallback previously left the entire tool-name suite green. The resolution moves into `resolveResponseToolNameMap()` next to the map it depends on, with a regression guard that fails without the recovery. No behaviour change. ([#11575](https://github.com/diegosouzapw/OmniRoute/pull/11575)) — thanks @amartinawi +- **docs(env):** document `OMNIROUTE_CORPUS_CACHE_SIZE` in `.env.example` and `ENVIRONMENT.md`, so the env/docs contract check stops failing Docs Gates on every branch ([#11576](https://github.com/diegosouzapw/OmniRoute/pull/11576)) — thanks @ntdatt812 +- **chore(stryker):** register `route-guard-tunnel-processes-local-only` and `universal-quota-aware-routing` in `tap.testFiles`, so their mutant kills count for the three modules they cover ([#11577](https://github.com/diegosouzapw/OmniRoute/pull/11577)) — thanks @ntdatt812 +- **test(authz):** pin `LOCAL_ONLY_API_GET_EXEMPTIONS` by exact membership instead of by entry count, so the guard names the offending path and also catches a substitution ([#11580](https://github.com/diegosouzapw/OmniRoute/pull/11580)) — thanks @ntdatt812 +- **test(build):** pin `resolveNextBuildBundlerFlag` to the `OMNIROUTE_USE_TURBOPACK` contract it implements, across runtimes, instead of asserting an unimplemented Bun override ([#11583](https://github.com/diegosouzapw/OmniRoute/pull/11583)) — thanks @ntdatt812 +- **test(claude-web):** the Claude Web transport test's barrel anchors no longer pin the exact construction spelling. #11421 made the executor map lazy, so `/"claude-web": new ClaudeWebExecutor\(\)/` stopped matching and the test went red on `release/v3.8.51` itself — a base-red gate every open PR inherited. The anchors now match the alias key and the class name and tolerate either spelling in between, and were checked to still fail when an alias is genuinely repointed at another executor ([#11585](https://github.com/diegosouzapw/OmniRoute/pull/11585)) — thanks @ntdat812 +- **test(models):** pin `qwen3.8-max` resolution per provider — two of the four catalogs now serve the bare id and no longer carry `-preview` ([#11590](https://github.com/diegosouzapw/OmniRoute/pull/11590)) — thanks @ntdatt812 +- **test(executors):** drop the stale 128-tool truncation assertion that contradicted the #11444 pass-through contract ([#11591](https://github.com/diegosouzapw/OmniRoute/pull/11591)) — thanks @ntdatt812 +- **test(token-health):** pin the current terminal-skip boundary — expired with retry budget is probed, `account_deactivated` stays skipped ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)) — thanks @ntdatt812 +- **test(models):** follow #11503's retarget of the `gemini-3-pro-high` alias to the hyphenated catalog id ([#11593](https://github.com/diegosouzapw/OmniRoute/pull/11593)) — thanks @ntdatt812 +- **refactor(video bridge):** extract per-video acquisition, whole-result caching, description, and metrics/abort/cleanup out of `VideoBridgeGuardrail.preCall` into a `processVideoPart` seam in a new `videoBridgePipeline.ts`, behind explicit `VideoMediaBrokerPort`, `VideoAudioTranscriptionPort`, and `VideoDrilldownPort` boundaries; `preCall` now only handles request traversal, policy, and response aggregation. The Video tab's FFmpeg/ffprobe runtime status is now an explicit `unknown` / `restricted` / `unavailable` / `available` state instead of a nullable boolean pair, fixing a case where an in-flight or failed probe was mislabeled as "install FFmpeg" ([#11657](https://github.com/diegosouzapw/OmniRoute/issues/11657)). ([#12016](https://github.com/diegosouzapw/OmniRoute/pull/12016)) +- **test(opencode-plugin):** add unit test coverage for `formatFreeBudget()` naming helper ([#11660](https://github.com/diegosouzapw/OmniRoute/pull/11660)) — thanks @f9td56dbgh-hub +- **docs(video):** clarify that the Video Bridge transcript `source` field (`client`, `embedded`, `audio-bridge`) is presently caller-declared and not yet server-verified — OmniRoute enforces the enum shape but does not cryptographically confirm that an `embedded`/`audio-bridge` label came from a server-owned extraction ([#11661](https://github.com/diegosouzapw/OmniRoute/issues/11661)). ([#12001](https://github.com/diegosouzapw/OmniRoute/pull/12001)) +- **chore(providers):** retire the distributed Raycast Relay and Hailuo Web integrations after a provenance audit confirmed substantial derivation from GPL-3.0 sources; the official GitHub and MiniMax API providers remain available. Migration 163 disables legacy connections and invalidates their active leases while preserving connection IDs, audit/history rows, quota snapshots, usage records, and fail-closed API-key connection policies. ([#11691](https://github.com/diegosouzapw/OmniRoute/pull/11691)) +- **chore(providers):** retire the Felo Web provider while its GPL-derived provenance remains on licensing HOLD ([#11698](https://github.com/diegosouzapw/OmniRoute/pull/11698)) — stale runtime and database references now fail closed while existing connection, lease, and usage history remains preserved for auditability. +- **fix(sse):** retire the Gemini Web image-generation bridge while provenance and licensing remain on HOLD, preserving Gemini Web chat/auth/tools and legitimate Gemini/Nano Banana image providers ([#11708](https://github.com/diegosouzapw/OmniRoute/pull/11708)) +- **chore(audio):** retire the built-in EdgeTTS provider and its 11-voice catalog while provenance/licensing review remains on HOLD; the generic `/v1/audio/speech` endpoint and the other speech providers remain available ([#11711](https://github.com/diegosouzapw/OmniRoute/pull/11711)) +- **fix(providers):** retire the unofficial Qwen Web integration and its `qwen-web`/`qw` identities while third-party provenance remains on HOLD ([#11713](https://github.com/diegosouzapw/OmniRoute/pull/11713)) — direct requests now fail closed with a sanitized HTTP 410 and combos skip the retired target, while migration guards preserve connection IDs, allowlists, usage/audit history and invalidate active leases. Supported Qwen API, Qwen Code, cloud, token-plan, audio, local and model-name surfaces remain available; restoring Qwen Web requires a qualified provenance/license decision. +- **fix(providers):** retire `microsoft-designer-web` and its `msdesigner` alias from runtime and catalogs while preserving stored credentials, provider history and allowlists as inactive records — source dependency on gpt4free's [`MicrosoftDesigner.py`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/g4f/Provider/needs_auth/MicrosoftDesigner.py) is proven at snapshot `0943d16eef757a44deb60651c07bbe5fa958cafe`, whose [`LICENSE`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/LICENSE) is GPL-3.0; expressive adaptation is probable, which records provenance/licensing risk without asserting a legal conclusion ([#11720](https://github.com/diegosouzapw/OmniRoute/pull/11720)). +- **docs(legal):** centralize pinned MIT notices for the vendored GCF codec and local flag assets ([#11726](https://github.com/diegosouzapw/OmniRoute/pull/11726)) +- Split the 2,952-line `adobeFireflyClient.ts` into nine focused modules (catalog, credentials, payload, ARP session, upload, responses, IMS, account, poll) behind an unchanged barrel — no consumer edits and no public API change (#11728). — thanks @MumuTW +- **fix(dashboard):** replaced the Model Lockout card's unprovenanced notification MP3 with a short, locally synthesized Web Audio chime ([#11731](https://github.com/diegosouzapw/OmniRoute/pull/11731)) — both toggles retain optional audible feedback without shipping replacement media or third-party code, and unsupported, suspended, or rejected audio contexts fail open without blocking the setting change. +- **chore(deepseek-web):** remove the provenance-unresolved DeepSeek PoW WASM binary and its runtime loader/tracing while retaining the existing JavaScript solver (slower at high difficulty) ([#11732](https://github.com/diegosouzapw/OmniRoute/pull/11732)). +- **chore(assets):** remove four unused provider assets whose redistribution provenance could not be verified while retaining the active Freebuff SVG ([#11735](https://github.com/diegosouzapw/OmniRoute/pull/11735)) +- **docs(assets):** retired two raster images whose redistribution provenance could not be verified ([#11736](https://github.com/diegosouzapw/OmniRoute/pull/11736)) — PicoClaw documentation now reuses the existing SVG, while Zoo Code remains a text link without an image. +- **docs(legal):** add pinned notices for six LobeHub-derived provider SVGs and 65 byte-exact theSVG assets, retaining registry claims as unverified metadata with explicit trademark and HOLD boundaries ([#11737](https://github.com/diegosouzapw/OmniRoute/pull/11737)). +- **chore(quality):** added a versioned 225/225 provider-asset provenance manifest and blocking drift gate ([#11743](https://github.com/diegosouzapw/OmniRoute/pull/11743)) — the inventory pins magic MIME and SHA-256 for every physical file, records immutable source matches for 71 assets, keeps 69 probable and 85 unresolved entries explicit but non-blocking, and treats provenance as source evidence rather than copyright or trademark clearance. +- **refactor(mitm):** replace four previously derived Traffic Inspector utilities — conversation normalization, SSE merging, secret masking, and header sanitization — with independent clean-room implementations based on public provider schemas, WHATWG SSE, and RFC 6750, while keeping the two independently classified passthrough surfaces unchanged ([#11748](https://github.com/diegosouzapw/OmniRoute/pull/11748)). +- Added a unit test that fails seven days before any dated pack under `config/` (`validUntil` and sibling keys) lapses, naming the file and key. The Alibaba free-tier pack expired on 2026-08-27 and turned every PR red the next morning with no commit involved; renewal now happens on someone's terms, not the clock's. ([#11891](https://github.com/diegosouzapw/OmniRoute/pull/11891)) +- `check:workflows` now fails (under `--strict`/`--ratchet`) when any job routed to a self-hosted runner publishes with `--provenance` — npm rejects that with `422` at the registry, which in v3.8.50 only surfaced after the tag and Docker images were public. ([#11895](https://github.com/diegosouzapw/OmniRoute/pull/11895)) +- `scripts/ops/runner-janitor.sh` now proves a path is idle with one `lsof` snapshot and removes stale leftovers itself (tmpfs after 3 h — it is RAM — disk after 24 h), kills orphan `next-build` processes, prunes checkouts of stopped runners, and alerts on memory pressure; `--dry-run` shows exactly what it would do. `docs/ops/RUNNER_BOX.md` reconciled to the measured box (31 GB, 10 listeners). ([#11893](https://github.com/diegosouzapw/OmniRoute/pull/11893)) +- The `next-build` artefact (1.3 GB) is now written and read under `$RUNNER_TEMP` (per-runner, on disk) instead of `/tmp`, which on the self-hosted pool is a 12 GB tmpfs in RAM. Landing it there took 27–32 of the publish job's 76 minutes, and the fixed `/tmp/e2e-build.tar.gz` name let E2E jobs on different runners overwrite each other's download. ([#11896](https://github.com/diegosouzapw/OmniRoute/pull/11896)) +- The CI `build` job now runs in two concurrency lanes — `main` and pull requests — so a release build is never queued behind (or OOM-killed beside) PR builds on the self-hosted pool, which holds one `next-build` comfortably and two at the edge. ([#11901](https://github.com/diegosouzapw/OmniRoute/pull/11901)) +- Re-freeze the ESLint suppressions on `release/v3.8.51` from a clean-room measurement (2 stale file entries pruned, 55 pre-existing `no-explicit-any` in six new files frozen under #11924) and drop the dead `GPT_SIZE_MAP` constant orphaned by the Adobe Firefly client split, so `No new ESLint warnings` stops failing every PR with exit 2 (Refs #11924) ([#11955](https://github.com/diegosouzapw/OmniRoute/pull/11955)) +- Type the 55 `no-explicit-any` sites that had been frozen under #11924 — four redundant casts in `socksConnectorWithFamily.ts` (undici/socks types already accept them) and the mocks/fixtures of the socks-timeout and isFree suites — and drop their suppression entries; the ESLint ratchet shrinks from 5487 to 5432 (Closes #11924) ([#11975](https://github.com/diegosouzapw/OmniRoute/pull/11975)) +- Take the two hosted-runner builds off the PR rail: `Build App` (`build.yml`) is `workflow_dispatch`-only and `DAST smoke (PR)` runs only for PRs into `main` — the 7 GB hosted VM cannot build `release/v3.8.51` in any profile (19/30 red, VM shutdown ~8 min into `next build`) and both checks had turned into permanent noise on every release PR; the bundle stays validated by `ci.yml` on `main` and by `nightly-release-green` on `release/**` (Closes #11946) ([#11962](https://github.com/diegosouzapw/OmniRoute/pull/11962)) +- Move the four nightly jobs that build the backend (`nightly-schemathesis`, `nightly-llm-security` promptfoo + garak, `nightly-resilience` axe-a11y) off the hosted 7 GB runner — where they died on `release/v3.8.51` unseen — onto the box's new `omni-light` pool (two listeners), and document the reshaped fleet (4 active OmniRoute listeners: 2 `omni-build` + 2 `omni-light`, janitor ceiling 4) (Closes #11965) ([#11967](https://github.com/diegosouzapw/OmniRoute/pull/11967)) +- Stop painting every fork PR into `release/**` red: `quality.yml` `Build (advisory)` is skipped (GitHub still reports `continue-on-error` failures as check FAILURE). Hosted `ubuntu-latest` cannot finish `npm run build` on this tree — same class as #11962 taking `build.yml` off the PR rail. `docker-publish.yml` amd64 now runs on the `.113` `omni-build` pool (31 GB / 32 cores, two listeners) with Turbopack, shares the `heavy-build-main` lane with `ci.yml` `Build` so it queues instead of becoming a third heavy, and keeps per-ref concurrency (a merge storm was starting 8 concurrent OOM builds). arm64 stays on `ubuntu-24.04-arm` with webpack — there is no ARM box. Fallback when `USE_VPS_RUNNER` is off: hosted amd64 + webpack (#11976). ([#12021](https://github.com/diegosouzapw/OmniRoute/pull/12021)) +- `docker-publish.yml` builds with webpack (`OMNIROUTE_USE_TURBOPACK=0`) on **both** arches, including amd64 on the `.113` `omni-build` pool. Turbopack had the RAM (31 GB, `omniroute-113-6`) and still panicked (`TurbopackInternalError: there must be a path to a root`, run 33253576569, 26 min in). The same tree's arm64 webpack build on hosted `ubuntu-24.04-arm` succeeded (run 33264823398). Dockerfile already documents webpack as the Docker escape hatch (#11976). ([#12050](https://github.com/diegosouzapw/OmniRoute/pull/12050)) +- **docs(free-tier):** declare the counting vs deciding regimes for "is it free?" and guard the deciding path from DB-backed catalog resolution ([#12226](https://github.com/diegosouzapw/OmniRoute/pull/12226)) — thanks @maxmad64bis +- **docs(auto-combo):** The mode pack table in `docs/routing/AUTO-COMBO.md` now lists all six shipped packs with every weight each one sets, replacing a four-pack table whose numbers had also drifted from the source. It states plainly that no pack sets `quality`, so selecting any pack silences the observed-quality signal. Six more documents that quote the scoring factor count joined the `check:docs-counts` gate, which caught five stale claims — including one naming nine factors that do not exist — and two stale mode pack counts. The dashboard routing panel, which offered four of the six packs and labelled the default strategy "6-Factor Scoring", is now covered by a test; the two packs it was missing are `reliability-first` and `chaos-mode`, the latter labelled as the fault-injection profile it is rather than as one more routing preference ([#12316](https://github.com/diegosouzapw/OmniRoute/pull/12316)) — thanks @maxmad64bis +- **chore(quality):** rebaseline `src/lib/db/apiKeys.ts` for the ACL the key-creation path now preserves ([#12352](https://github.com/diegosouzapw/OmniRoute/pull/12352)) ([#12673](https://github.com/diegosouzapw/OmniRoute/pull/12673)) — thanks @KrzysiekSko +- **chore(providers):** bump the Claude Code wire identity and the Devin bridge image pin from `2.1.220` to `2.1.258` ([#12402](https://github.com/diegosouzapw/OmniRoute/pull/12402)) — thanks @ggiak ([#12604](https://github.com/diegosouzapw/OmniRoute/pull/12604)) +- **docs(env):** align `.env.example`, the README Bun section, and the troubleshooting guide with the code: `OMNIROUTE_USE_TURBOPACK` also governs `npm run build` (not dev-only), `bun run build` follows that flag instead of auto-selecting Webpack, `OMNIROUTE_CHAT_MAX_HEAVY_IN_FLIGHT` is unset by default (no request-count cap), and the structural `503 chat_admission_busy` message matches `chatAdmissionResponses.ts` (#12404 — thanks @pacocartones) +- **chore(electron):** upgrade the desktop app to Electron 44 (Chromium 152, Node 24.18.1) ([#12217](https://github.com/diegosouzapw/OmniRoute/pull/12217)). **Requires macOS 13 (Ventura) or later** — Chromium dropped macOS 12 (Monterey), so Monterey users must stay on an earlier OmniRoute desktop build. Windows and Linux are unaffected; the app already shipped only x64/arm64, so Electron 44 dropping 32-bit builds changes nothing. Removes the `openAsHidden`/`wasOpenedAsHidden` login-item fields deleted in Electron 44 — hidden autostart continues to work through the `--hidden` argument registered with the login item ([#12554](https://github.com/diegosouzapw/OmniRoute/pull/12554)) — thanks @app/dependabot +- **chore(quality):** rebaseline `src/sse/handlers/chat.ts` for the effective-input persistence the continuation fix needs ([#12641](https://github.com/diegosouzapw/OmniRoute/pull/12641)) ([#12680](https://github.com/diegosouzapw/OmniRoute/pull/12680)) — thanks @hartmark +- `Coverage` job on `ci.yml`: the informational Codecov upload gets its own 5-minute ceiling and `continue-on-error`, and the job budget grows from 20 to 30 minutes (the 8-shard c8 merge alone takes ~10) — a stalled upload no longer ends the job `cancelled` and drags a fully green `main` run's conclusion down with it ([#11972](https://github.com/diegosouzapw/OmniRoute/pull/11972)) +- CI hardening on the PR rail: the four `Unit Tests fast-path` shards get `timeout-minutes: 30` (a hung shard held a PR for 64 min instead of GitHub's 6 h default) and both ESLint file caches lose their `restore-keys` fallback, so a cache built under another suppressions file or lint config can no longer report stale verdicts (Refs #11600, #11924) ([#11963](https://github.com/diegosouzapw/OmniRoute/pull/11963)) +- Every CI job that runs a `next build` (`build`, the npm `publish`, both release-green validations) now targets the `omni-build` runner label, which only two of the eight self-hosted runners carry. The box holds one build comfortably and two at the edge; a third now queues on GitHub instead of being OOM-killed by the kernel. ([#11932](https://github.com/diegosouzapw/OmniRoute/pull/11932)) +- **refactor(combo):** move `handleRoundRobinCombo` (and `resolveTargetTokenLimit`) into `open-sse/services/combo/roundRobinCombo.ts`. `combo.ts` drops from 2164 to 1014 lines (`split("\n").length`); the leaf is 1199 (under the 1200 new-file cap). The round-robin call site uses a dynamic `import()` so `releaseStickyPinOnFailure` / `clearStaleLKGP` can stay exported from `combo.ts` without a static cycle. Skip / sticky / semaphore / safety-timer behavior is unchanged. ([#12811](https://github.com/diegosouzapw/OmniRoute/pull/12811)) — thanks @HouMinXi +- **chore(quality):** rebaseline the file-size caps the error-boundary campaign grew past (`open-sse/executors/codex.ts`, `open-sse/vendor/codex-chatgpt-web/bridge.ts`, both via [#12444](https://github.com/diegosouzapw/OmniRoute/pull/12444)) ([#12654](https://github.com/diegosouzapw/OmniRoute/pull/12654)) +- **chore(quality):** rebaseline the file-size caps the hartmark batch grew past (`combos/page.tsx` via [#12355](https://github.com/diegosouzapw/OmniRoute/pull/12355), `open-sse/services/combo.ts` via [#12338](https://github.com/diegosouzapw/OmniRoute/pull/12338)) ([#12623](https://github.com/diegosouzapw/OmniRoute/pull/12623)) — thanks @hartmark +- **chore(quality):** rebaseline the file-size caps the HouMinXi batch grew past when its PRs stacked (`providers/page.tsx`, `chatCore.ts`, `accountFallback.ts`) — each PR measured correctly in isolation, none saw the stacking ([#12619](https://github.com/diegosouzapw/OmniRoute/pull/12619)) +- **chore(quality):** rebaseline `open-sse/services/combo.ts` for the reset-aware scoring the HouMinXi batch stacked ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) ([#12678](https://github.com/diegosouzapw/OmniRoute/pull/12678)) — thanks @HouMinXi +- **docs:** sync provider catalog count (direct commit `4f11b2ae3d`) +- **docs(skills):** sync omni-inference search fallback (direct commit `b4e76a7ed9`) +- **chore(lint):** prune two stale base suppressions (direct commit `1325047d56`) +- **test(auto-combo):** tolerate full-suite rotation load (direct commit `ad3e293f9f`) +- **test:** drain inherited quality snapshot reds (direct commit `21ed68d8ac`) +- **chore(quality):** rebaseline measured CLI bundle growth (direct commit `d35b3f9779`) +- **test(ui):** reproduce invalid AnySearch icon alias (direct commit `12cc6ca834`) +- **test(translator):** fix the relative imports of the relocated deferred-finish test (direct commit `777d9d1629`) +- **docs(counts):** bring the documented provider and migration counts to the measured values (direct commit `e70bea30e9`) +- **test(security):** drop the guard-target set left unused by the sync (direct commit `a32ba0e362`) +- **chore(db):** renumber model_capabilities migration 163 → 169 (cross-branch collision with 163_radar_feed_cache_generated_at) (direct commit `70f5d4cbf9`) +- **docs(cli):** document CLI_PRIME_AGENT_BIN and correct the CLI Agents count (direct commit `9058e39b61`) +- **chore(deps):** refresh runtimes and adopt ESLint 10 ([#11259](https://github.com/diegosouzapw/OmniRoute/pull/11259)) — thanks @backryun +- **refactor(local-corpus):** implement dynamic root resolution and LRU cache ([#11491](https://github.com/diegosouzapw/OmniRoute/pull/11491)) — thanks @raheemuddin786 +- **chore(quality):** prune stale eslint-suppressions entries ([#11596](https://github.com/diegosouzapw/OmniRoute/pull/11596)) +- **chore(ci):** unblock quality gates - env-doc sync + stryker tap.testFiles registration (#9282) ([#11609](https://github.com/diegosouzapw/OmniRoute/pull/11609)) — thanks @Meet6338-X +- **chore(ci):** point the circular-deps gate at dpdm's real JS entrypoint ([#11615](https://github.com/diegosouzapw/OmniRoute/pull/11615)) — thanks @benzntech +- **chore(quality):** rebaseline file-size for #11448/#11495/#11561 growth ([#11630](https://github.com/diegosouzapw/OmniRoute/pull/11630)) +- **deps:** realign onnxruntime-node pin to @huggingface/transformers' 1.24.3 lockstep ([#11633](https://github.com/diegosouzapw/OmniRoute/pull/11633)) — thanks @jonlwheat2-gif +- **test(autoCombo):** port models_dev_tier #11508 guard from node:test to vitest ([#11635](https://github.com/diegosouzapw/OmniRoute/pull/11635)) — thanks @jonlwheat2-gif +- **chore(quality):** rebaseline file-size for #11083/#11631/#11628 growth ([#11636](https://github.com/diegosouzapw/OmniRoute/pull/11636)) +- **chore(lint):** prune stale ESLint suppressions (base-red #11449) ([#11644](https://github.com/diegosouzapw/OmniRoute/pull/11644)) — thanks @jonlwheat2-gif +- **docs(changelog):** aggregate the ten v3.8.50 fragments into CHANGELOG.md ([#11683](https://github.com/diegosouzapw/OmniRoute/pull/11683)) +- **chore(search):** drop unreachable credential sweep after #11642 ([#11687](https://github.com/diegosouzapw/OmniRoute/pull/11687)) — thanks @HouMinXi +- **ci:** add API route TypeScript regression gate ([#11705](https://github.com/diegosouzapw/OmniRoute/pull/11705)) — thanks @Sabeekhann +- **docs(dedup):** correct the CodeQL claim on the tenant-namespace comment ([#11712](https://github.com/diegosouzapw/OmniRoute/pull/11712)) +- **docs(changelog):** add consolidated v3.8.50 stats, top-25 ranking and a .mailmap ([#11715](https://github.com/diegosouzapw/OmniRoute/pull/11715)) +- **docs:** recommend gstack for AI-assisted workflows ([#11770](https://github.com/diegosouzapw/OmniRoute/pull/11770)) — thanks @santosraju99-hub +- **chore(quality):** tighten complexity/cognitive-complexity ratchets to the current tip; land the missed gateways.ts rebaseline ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **docs(i18n):** finish Freepik → Magnific rebrand in locale strings and README ([#11772](https://github.com/diegosouzapw/OmniRoute/pull/11772)) — thanks @turbolego +- **refactor(auto-combo):** fix divergent scoring in combo health reporting ([#11854](https://github.com/diegosouzapw/OmniRoute/pull/11854)) — thanks @jacobsparts +- **chore(changelog):** add missing fragments for #11919/#11918/#11916 ([#11938](https://github.com/diegosouzapw/OmniRoute/pull/11938)) +- **test(check):** escape the runs-on fixture with JSON.stringify, not a quote-only replace ([#11942](https://github.com/diegosouzapw/OmniRoute/pull/11942)) (direct commit `fb7445eaa3`) +- **test(release):** align five suites with the contracts #11933, #11919 and #11876 shipped on release/v3.8.51 ([#11944](https://github.com/diegosouzapw/OmniRoute/pull/11944)) +- **chore(quality):** remove unreachable code and restore test discovery ([#11950](https://github.com/diegosouzapw/OmniRoute/pull/11950)) — thanks @backryun +- **docs(agents):** sync-back landings are fast-forward, never squash ([#11964](https://github.com/diegosouzapw/OmniRoute/pull/11964)) +- **test(infra):** retry recursive temp-dir removal instead of failing a shard on ENOTEMPTY (#11966) ([#11968](https://github.com/diegosouzapw/OmniRoute/pull/11968)) +- **docs(ops):** the .113 heavy-build ceiling is one runner, not two ([#12048](https://github.com/diegosouzapw/OmniRoute/pull/12048)) +- **test(ui):** correct inactive auto-fetch expectation ([#12098](https://github.com/diegosouzapw/OmniRoute/pull/12098)) — thanks @RaviTharuma +- **docs:** document native dependency check escape hatch ([#12101](https://github.com/diegosouzapw/OmniRoute/pull/12101)) — thanks @RaviTharuma +- **test(providers):** regenerate the translate-path golden for OrcaRouter (#11923) ([#12118](https://github.com/diegosouzapw/OmniRoute/pull/12118)) +- **chore(quality):** velocity phase — loosen every numeric baseline by 20% until v4.0, monitor headroom nightly ([#12125](https://github.com/diegosouzapw/OmniRoute/pull/12125)) +- **chore(lint):** batch 0 of #12146 — type the call-log-cap sqlite rows instead of 45 as-any casts ([#12157](https://github.com/diegosouzapw/OmniRoute/pull/12157)) +- **chore(lint):** batch 4 of #12146 — shared/components react-hooks violations resolved ([#12159](https://github.com/diegosouzapw/OmniRoute/pull/12159)) +- **chore(lint):** batch 1 of #12146 — dashboard/cli-code react-hooks violations resolved ([#12160](https://github.com/diegosouzapw/OmniRoute/pull/12160)) +- **chore(lint):** batch 3 of #12146 — dashboard/settings react-hooks violations resolved ([#12162](https://github.com/diegosouzapw/OmniRoute/pull/12162)) +- **chore(lint):** batch 2 of #12146 — resolve the react-hooks compiler violations in dashboard/providers ([#12163](https://github.com/diegosouzapw/OmniRoute/pull/12163)) +- **chore(quality):** register search-432 cooldown test in stryker tap.testFiles ([#12170](https://github.com/diegosouzapw/OmniRoute/pull/12170)) +- **chore(lint):** batch 5 of #12146 — combos, endpoint, provider-stats, api-manager and costs react-hooks violations resolved ([#12174](https://github.com/diegosouzapw/OmniRoute/pull/12174)) +- **build:** add contributor fast profile ([#12192](https://github.com/diegosouzapw/OmniRoute/pull/12192)) — thanks @rafacpti23 +- **docs(readme):** README touch-ups ([#12193](https://github.com/diegosouzapw/OmniRoute/pull/12193)) ([#12194](https://github.com/diegosouzapw/OmniRoute/pull/12194)) ([#12202](https://github.com/diegosouzapw/OmniRoute/pull/12202)) — thanks @rafacpti23 +- **build:** force the webpack bundler in the contributor build profile ([#12197](https://github.com/diegosouzapw/OmniRoute/pull/12197)) — thanks @rafacpti23 +- **build:** skip standalone packaging in the contributor profile ([#12198](https://github.com/diegosouzapw/OmniRoute/pull/12198)) — thanks @rafacpti23 +- **docs(audit):** align every published number with the code and harden check:docs-counts ([#12200](https://github.com/diegosouzapw/OmniRoute/pull/12200)) +- **build:** stub the instrumentation entrypoints in the contributor profile ([#12203](https://github.com/diegosouzapw/OmniRoute/pull/12203)) — thanks @rafacpti23 +- **build:** omit the standalone output target for contributor builds ([#12204](https://github.com/diegosouzapw/OmniRoute/pull/12204)) — thanks @rafacpti23 +- **chore(lint):** batch 6 of #12146 — memory, radar, audit, analytics, cache, usage, activity, home and RequestLoggerV2 react-hooks violations resolved ([#12208](https://github.com/diegosouzapw/OmniRoute/pull/12208)) +- **docs(diagrams):** rename number-carrying diagram files to stable names ([#12210](https://github.com/diegosouzapw/OmniRoute/pull/12210)) +- **docs(dashboard):** redraw onboarding tier-flow SVGs for the real 4-tier model ([#12211](https://github.com/diegosouzapw/OmniRoute/pull/12211)) +- **docs(api):** document every implemented route in openapi.yaml (276 → 692 paths) ([#12212](https://github.com/diegosouzapw/OmniRoute/pull/12212)) +- **chore(lint):** batch 7 of #12146 — final src tail: 37 react-hooks violations across 33 files resolved ([#12244](https://github.com/diegosouzapw/OmniRoute/pull/12244)) +- Revert "docs: recommend gstack for AI-assisted workflows (#11770)" ([#12248](https://github.com/diegosouzapw/OmniRoute/pull/12248)) ([#12249](https://github.com/diegosouzapw/OmniRoute/pull/12249)) +- **docs:** Chaos Mode setup guide + weighted strategy semantics ([#12250](https://github.com/diegosouzapw/OmniRoute/pull/12250)) +- **docs(agents):** protected-surface merge rule — operator approval for agent-instruction files ([#12253](https://github.com/diegosouzapw/OmniRoute/pull/12253)) +- **chore(quality):** register native-codex turn-pin tests in stryker tap.testFiles ([#12263](https://github.com/diegosouzapw/OmniRoute/pull/12263)) +- **chore(quality):** dedupe tap.testFiles entries added by racing base-red fixes ([#12265](https://github.com/diegosouzapw/OmniRoute/pull/12265)) +- **docs(readme):** introduce OmniRouteTray — the macOS menu-bar companion ([#12276](https://github.com/diegosouzapw/OmniRoute/pull/12276)) — thanks @ggiak +- **chore(proxy):** purge the legacy 1proxy residue — sync/rotator modules, dead DB exports, dead settings tab and flag, docs (#12091) ([#12290](https://github.com/diegosouzapw/OmniRoute/pull/12290)) +- **chore(quality):** baseline-headroom skips generated and vendored files in the fileSize worst-file signal ([#12291](https://github.com/diegosouzapw/OmniRoute/pull/12291)) +- **refactor(video):** unify the JPEG frame data-URI contract ([#12322](https://github.com/diegosouzapw/OmniRoute/pull/12322)) +- **chore(deps):** freeze onnxruntime-node and eslint-plugin-react-hooks out of dependabot groups ([#12329](https://github.com/diegosouzapw/OmniRoute/pull/12329)) +- **test(executors):** restore the #10986 reasoning-only fallback guards ([#12364](https://github.com/diegosouzapw/OmniRoute/pull/12364)) — thanks @pacocartones +- **chore(quality):** re-tighten the file-size ratchet to the real LOC (plan 3.8.52 task 0) ([#12411](https://github.com/diegosouzapw/OmniRoute/pull/12411)) +- **test(grok):** format web executor suite ([#12412](https://github.com/diegosouzapw/OmniRoute/pull/12412)) +- **chore(quality):** ratchet open-sse typecheck baseline to zero ([#12418](https://github.com/diegosouzapw/OmniRoute/pull/12418)) +- **test(build):** guard the artifact path policy arrays against duplicates ([#12422](https://github.com/diegosouzapw/OmniRoute/pull/12422)) +- **chore(lint):** adopt eslint-plugin-react-hooks 7.1.1 ([#12428](https://github.com/diegosouzapw/OmniRoute/pull/12428)) +- **chore(providers):** remove a keyless provider integration at its operator's request ([#12440](https://github.com/diegosouzapw/OmniRoute/pull/12440)) +- **docs(providers):** register providers removed at their operator's request and guard against reintroduction ([#12478](https://github.com/diegosouzapw/OmniRoute/pull/12478)) +- **chore(quality):** base fixes — stryker tap.testFiles + node_modules cache key ([#12482](https://github.com/diegosouzapw/OmniRoute/pull/12482)) +- **docs(arch):** one-process recipe for tens of long /v1/responses ([#12493](https://github.com/diegosouzapw/OmniRoute/pull/12493)) — thanks @RaviTharuma +- **chore(quality):** tighten the CodeQL ratchet baseline from 11 to 6 ([#12530](https://github.com/diegosouzapw/OmniRoute/pull/12530)) +- **chore(deps):** pin browserslist override to ^4.28.8 ([#12592](https://github.com/diegosouzapw/OmniRoute/pull/12592)) — thanks @KooshaPari +- **chore(deps):** bump fast-uri to 3.1.7 in the electron lockfile ([#12601](https://github.com/diegosouzapw/OmniRoute/pull/12601)) +- **docs(quality):** document how the CodeQL ratchet refreshes and how to tighten it ([#12611](https://github.com/diegosouzapw/OmniRoute/pull/12611)) +- **test(ci):** pin the openapi-security-tiers two-arm contract with an executing gate test (#12581) ([#12652](https://github.com/diegosouzapw/OmniRoute/pull/12652)) +- **docs(gamification):** align XP Rewards table with code (#12501) ([#12667](https://github.com/diegosouzapw/OmniRoute/pull/12667)) — thanks @KooshaPari +- **refactor(combo):** split executeTarget into gates, attempt, and loop ([#12746](https://github.com/diegosouzapw/OmniRoute/pull/12746)) — thanks @HouMinXi +- **docs(security):** document socket.yml scanner config + CI workflow link (#12575) ([#12764](https://github.com/diegosouzapw/OmniRoute/pull/12764)) — thanks @KooshaPari +- **chore(ci):** guard commit identity in pre-commit to stop author misattribution ([#12772](https://github.com/diegosouzapw/OmniRoute/pull/12772)) +- **deps:** 17 Dependabot bumps — bump electron from 43.4.0 to 43.4.1 in /electron ([#11426](https://github.com/diegosouzapw/OmniRoute/pull/11426)); bump the development group across 1 directory with 8 updates ([#11428](https://github.com/diegosouzapw/OmniRoute/pull/11428)); bump the production group across 1 directory with 11 updates ([#11440](https://github.com/diegosouzapw/OmniRoute/pull/11440)); chore(deps): bump github/codeql-action/init from 4.37.7 to 4.37.8 ([#11925](https://github.com/diegosouzapw/OmniRoute/pull/11925)); chore(deps): bump github/codeql-action from 4.37.7 to 4.37.8 ([#11926](https://github.com/diegosouzapw/OmniRoute/pull/11926)); chore(deps): bump github/codeql-action/analyze from 4.37.7 to 4.37.8 ([#11927](https://github.com/diegosouzapw/OmniRoute/pull/11927)); build(deps): bump github/codeql-action/init from 4.37.8 to 4.37.9 ([#12345](https://github.com/diegosouzapw/OmniRoute/pull/12345)); build(deps): bump github/codeql-action/analyze from 4.37.8 to 4.37.9 ([#12346](https://github.com/diegosouzapw/OmniRoute/pull/12346)); bump the development group across 1 directory with 2 updates ([#12347](https://github.com/diegosouzapw/OmniRoute/pull/12347)); build(deps): bump github/codeql-action from 4.37.8 to 4.37.9 ([#12349](https://github.com/diegosouzapw/OmniRoute/pull/12349)); bump browserslist from 4.28.2 to 4.28.8 ([#12396](https://github.com/diegosouzapw/OmniRoute/pull/12396)); bump the production group across 1 directory with 4 updates ([#12399](https://github.com/diegosouzapw/OmniRoute/pull/12399)); bump @xmldom/xmldom ([#12500](https://github.com/diegosouzapw/OmniRoute/pull/12500)); bump qs from 6.15.2 to 6.16.0 ([#12512](https://github.com/diegosouzapw/OmniRoute/pull/12512)); bump @xmldom/xmldom from 0.9.10 to 0.9.12 ([#12513](https://github.com/diegosouzapw/OmniRoute/pull/12513)); bump fast-uri from 3.1.5 to 3.1.7 ([#12514](https://github.com/diegosouzapw/OmniRoute/pull/12514)); bump @humanfs/node from 0.16.7 to 0.16.8 ([#12515](https://github.com/diegosouzapw/OmniRoute/pull/12515)) +- Clear the `release/v3.8.51` `check:agent-skills-sync` base-red: regenerate `skills/cli-tunnel/SKILL.md` so the `tunnel create [type]` positional that #13009 taught the generator to read is reflected in the committed skill. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` typecheck base-red from #12731: the new `Mode pack ranking gates` candidates in `open-sse/services/autoCombo/__tests__/autoCombo.test.ts` omitted the required `provider`, `model` and `errorRate` fields of `ProviderCandidate`, failing `check:open-sse-typecheck` (and with it `Fast Quality Gates`) on every open PR. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` docs-sync base-red: EURouter (#13025) and GreenPT (#13024) took the live provider count to 358, leaving 7 STRICT drifts (`PROVIDER_REFERENCE.md`, 4 diagrams, 2 tier-flow images) plus the `AGENTS.md` / `llm.txt` / `package.json` count claims stale. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- **test(models):** Pin `CATALOG_BUILD_TIMEOUT_MS` in the #12058 canonical-catalog regression so a cold catalog build racing the 8s default bound no longer flakes the suite with a 500 ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Allow `@omniroute/opencode-plugin-v2/` in the npm pack policy — [#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870) shipped the v2 plugin beside its v1 sibling without widening the allowlist, so every packed file under it read as an unexpected artifact and failed `check:pack-policy` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Point the release-green validator's pack gate at the tree it is validating — the build-provenance guard defaulted to `origin/main`, which a release branch tip can never be an ancestor of mid-cycle, so `Package artifact` reported a false red on every run and the tarball boot-smoke cascaded off it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **docs(checks):** keep doc counts honest — headings, rankings, catalog, weights, quality gate and scoring diagram now covered ([#12507](https://github.com/diegosouzapw/OmniRoute/pull/12507)) — thanks @maxmad64bis +- **chore(lifecycle):** `check:model-lifecycle` now also diffs `DEFAULT_DEGRADATION_MAP` (the background-task redirect table) against the vendor lifecycle snapshot, refusing a retired id as source or target, with a table-driven unit test beside it. Three rows whose source the vendor had retired — `claude-sonnet-4-20250514`, `gemini-3-pro-preview` and `gpt-5.1-codex` (whose target `gpt-5.1-codex-mini` is retired too) — were dead code, since `checkLifecycle` answers 410 before the redirect runs; they are dropped (#12535 — thanks @pacocartones) +- **test(compression):** cover idle worker eviction at the resource level — the pool must call `terminate()` and must not retain the worker's `MessagePort`, complementing the `exit`-event assertion added with the fix ([#13371](https://github.com/diegosouzapw/OmniRoute/pull/13371)) +- **chore(quality):** raise the `imageGeneration.ts` file-size ceiling for the image-only-model guard that clears the #12945 base-red ([#13392](https://github.com/diegosouzapw/OmniRoute/pull/13392)) +- **chore(quality):** type the combo-test route's JSON response bodies instead of casting them to `any`, drop the now-empty suppression entry, and remove the `hasPerModelQuota` import `chatCore.ts` stopped using when the failure-classification helper was extracted ([#13379](https://github.com/diegosouzapw/OmniRoute/pull/13379)) +- **chore(electron):** the desktop bundle no longer ships the root-level files of every translated docs mirror (`README.md`, `llm.txt`, `CLAUDE.md`, `GEMINI.md`, `CONTRIBUTING.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`) — the packaged app only reads `docs/i18n//docs/**`, which stays. Saves ~11 MB on top of the translated CHANGELOGs already pruned. (#0000) ([#13235](https://github.com/diegosouzapw/OmniRoute/pull/13235)) +- **chore(quality):** retighten the `src/sse/handlers/chat.ts` unused-vars suppression count to the 8 violations that actually remain ([#13385](https://github.com/diegosouzapw/OmniRoute/pull/13385)) +- Bring the documented counts back in line with the code: 171 migrations (was 169) and 20 routing strategies (was 19), plus the regenerated `cli-tunnel` skill reference. ([#12970](https://github.com/diegosouzapw/OmniRoute/pull/12970)) +- Update the documented migration count to 172 after the call-logs provider-stats indexes landed. ([#13209](https://github.com/diegosouzapw/OmniRoute/pull/13209)) +- **chore(ci):** two gates that close the blind spots behind the exclusions above. `check:vitest-exclusions` requires every Vitest exclusion to name a tracking issue and to appear in `config/quality/vitest-exclusions.json` — the previous list grew to 62 files behind a comment pointing at an issue that had been closed for a month. `check-new-key-coverage` requires a key newly added to `en.json` to reach every locale; the existing coverage gate is a percentage floor per locale, so eleven absent keys out of ~13,000 left it at 99.9% while a whole feature shipped untranslated in nine languages. Both are diff-aware, so pre-existing debt stays frozen and neither needed a migration to turn on. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **build:** root layout no longer loads Inter from `next/font/google`, so a production image build does not need fonts.googleapis.com ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **docs(i18n):** bring the eight new `llm.txt` mirrors (km, kn, ml, my, ne, or, pa, si) up to the current provider and migration counts, so `check-docs-sync` stops rejecting every commit ([#13674](https://github.com/diegosouzapw/OmniRoute/pull/13674)) +- **refactor(ui):** flow surfaces (home topology, combo live studio, compression cockpit/waterfall/nodes, token-health badge) express state through the theme-aware `--orch-status-*` tokens instead of fixed dark-mode hex, so green/red/amber/grey stay legible in the light theme; dark mode is byte-identical. Categorical palettes (routing-strategy hues, compression-layer pills, provider brand colors) deliberately stay hex ([#12378](https://github.com/diegosouzapw/OmniRoute/issues/12378)) ([#13203](https://github.com/diegosouzapw/OmniRoute/pull/13203)) +- **chore(tests):** 51 test files that had been excluded from Vitest are running again, restoring roughly 350 assertions to the blocking suite. Each was measured individually first: of the 62 files parked behind the `// #8618 — pre-existing failure` comment, 51 pass against the current tree with no source change, so the exclusions had outlived the failures they were added for. The 11 that genuinely still fail stay excluded, but now point at a live tracker (#13204) rather than at #8618, which was closed in August while the list it tracked kept growing. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **chore(quality):** drop two ESLint suppression entries whose violations no longer exist, so `eslint --suppressions-location` stops rejecting every commit that touches the surrounding files ([#13384](https://github.com/diegosouzapw/OmniRoute/pull/13384)) +- **chore(release):** bring main's post-tag fixes into release/v3.8.51 — sync-back step 2/2 (direct commit `21c488f210`) +- **chore(deps):** patch toml and esbuild in the opencode plugin lockfiles ([#12965](https://github.com/diegosouzapw/OmniRoute/pull/12965)) +- **docs(changelog):** reconcile the v3.8.51 living section — fold 366 fragments, cover every cycle commit, credit every contributor ([#12971](https://github.com/diegosouzapw/OmniRoute/pull/12971)) +- **test(plugin):** wait for the v2 lazy-refresh tier instead of sleeping past it ([#12984](https://github.com/diegosouzapw/OmniRoute/pull/12984)) +- **chore(quality):** rebaseline chatCore.ts after the non-streaming regression fixes ([#13045](https://github.com/diegosouzapw/OmniRoute/pull/13045)) +- **docs(changelog):** add the missing bullet marker to the DeepSeek PoW fragment ([#13200](https://github.com/diegosouzapw/OmniRoute/pull/13200)) +- **chore(deps):** drain the Dependabot queue — 10 of 13 alerts ([#13213](https://github.com/diegosouzapw/OmniRoute/pull/13213)) +- **chore(ci):** make worktrees carry the hooks that enforce the identity gate ([#13236](https://github.com/diegosouzapw/OmniRoute/pull/13236)) +- **chore(deps):** pin csv-parse to ^7.0.2 via overrides (#13117) ([#13300](https://github.com/diegosouzapw/OmniRoute/pull/13300)) — thanks @KooshaPari +- **chore(deps):** bump hono from ^4.12.34 to ^4.13.7 (#13148) ([#13301](https://github.com/diegosouzapw/OmniRoute/pull/13301)) — thanks @KooshaPari +- **chore(deps):** pin joi to ^18.2.8 via overrides (#13085) ([#13302](https://github.com/diegosouzapw/OmniRoute/pull/13302)) — thanks @KooshaPari +- **docs(providers):** add Microsoft 365 Copilot (BizChat) provider guide (#12779) ([#13340](https://github.com/diegosouzapw/OmniRoute/pull/13340)) — thanks @KooshaPari +### 🙌 Contributors + +Thanks to everyone whose work landed in v3.8.51: + +| Contributor | PRs / Issues | +| --- | --- | +| [@5dive-bot](https://github.com/5dive-bot) | #11852 | +| [@abhisheksharma2411](https://github.com/abhisheksharma2411) | #11547, #11684, #11959, #12209 | +| [@adevwithpurpose](https://github.com/adevwithpurpose) | #11464 | +| [@adivekar-utexas](https://github.com/adivekar-utexas) | #12015, #12043, #12090, #12138 | +| [@AIB1TAL0S](https://github.com/AIB1TAL0S) | #12103 | +| [@alltomatos](https://github.com/alltomatos) | #11448 | +| [@alvinveroy](https://github.com/alvinveroy) | #11857, #12027, #12165 | +| [@amaleta](https://github.com/amaleta) | #11906, #11934 | +| [@amartinawi](https://github.com/amartinawi) | #11575 | +| [@AndrianBalanescu](https://github.com/AndrianBalanescu) | #11083, #11830, #11834, #11844 | +| [@anhtahaylove](https://github.com/anhtahaylove) | #13091, #13092, #13093, #13096, #13097, #13100, #13106, #13109, #13114, #13155, #13166, #13171, #13175, #13187, #13196 | +| [@aniruddhaadak80](https://github.com/aniruddhaadak80) | #11760 | +| [@app](https://github.com/app) | #12554 | +| [@arminanton](https://github.com/arminanton) | #11461, #11513 | +| [@AStupidBear](https://github.com/AStupidBear) | #11584 | +| [@atescivitci-cmd](https://github.com/atescivitci-cmd) | #12105 | +| [@b3nw](https://github.com/b3nw) | #11970, #11971, #12147 | +| [@backryun](https://github.com/backryun) | #11259, #11950, #12075, #12076, #12078, #12079, #12081, #12082, #12181, #12228, #12239, #12255, #12258, #12277, #12367, #12423, #12524, #12538 | +| [@benjaminkitt](https://github.com/benjaminkitt) | #11747 | +| [@benzntech](https://github.com/benzntech) | #11614, #11615 | +| [@Bl0ck154](https://github.com/Bl0ck154) | #11948, #11951, #11952, #11953, #11954 | +| [@brick30llc-ctrl](https://github.com/brick30llc-ctrl) | #11752, #12242, #12262 | +| [@Chewji9875](https://github.com/Chewji9875) | #11455, #11875, #12028 | +| [@cheynetom](https://github.com/cheynetom) | #12058 | +| [@davidebaraldo](https://github.com/davidebaraldo) | #12222, #12304 | +| [@davidlinfr](https://github.com/davidlinfr) | #6390 | +| [@ddarkr](https://github.com/ddarkr) | #12124 | +| [@Deftera186](https://github.com/Deftera186) | #11809 | +| [@dpozimski](https://github.com/dpozimski) | #11945 | +| [@DW-MediaLab](https://github.com/DW-MediaLab) | #12229 | +| [@echel0nn](https://github.com/echel0nn) | #11923 | +| [@f9td56dbgh-hub](https://github.com/f9td56dbgh-hub) | #11660 | +| [@fabioluissilva](https://github.com/fabioluissilva) | #11991 | +| [@foreveryh](https://github.com/foreveryh) | #12177 | +| [@geek007git](https://github.com/geek007git) | #12115, #12116, #12117, #12120, #12122 | +| [@geekyNads](https://github.com/geekyNads) | #11784, #12266 | +| [@ggiak](https://github.com/ggiak) | #11561, #12276, #12350, #12402 | +| [@gonisulaimann](https://github.com/gonisulaimann) | #12368, #12369, #12371 | +| [@Gorillaz322](https://github.com/Gorillaz322) | #12207 | +| [@groovecityJO](https://github.com/groovecityJO) | #12682 | +| [@hartmark](https://github.com/hartmark) | #11434, #11452, #11473, #11499, #11703, #11983, #11984, #11985, #11986, #11988, #11989, #11990, #11994, #12221, #12293, #12445, #12446, #12447, #12448, #12460, #12461, #12623, #12646, #12650, #12680, #12717, #12718, #12727, #12741, #12854 | +| [@hizzt](https://github.com/hizzt) | #11894 | +| [@hongnoul](https://github.com/hongnoul) | #11484 | +| [@HouMinXi](https://github.com/HouMinXi) | #11411, #11414, #11512, #11518, #11520, #11641, #11642, #11643, #11687, #11779, #11849, #11850, #11851, #11915, #11916, #11918, #11919, #11920, #12013, #12017, #12033, #12042, #12106, #12139, #12166, #12169, #12171, #12205, #12213, #12312, #12325, #12487, #12488, #12495, #12504, #12557, #12566, #12590, #12591, #12624, #12626, #12632, #12637, #12678, #12696, #12697, #12711, #12733, #12746, #12767, #12770, #12789, #12803, #12805, #12811, #12866, #12868, #12899, #12926, #12934, #12950, #12951, #12974, #13001, #13006, #13011, #13017, #13026, #13027, #13034, #13035, #13038, #13042, #13050, #13060, #13061, #13069, #13107, #13120, #13136, #13195, #13197 | +| [@Hsia97](https://github.com/Hsia97) | #11624 | +| [@jacobsparts](https://github.com/jacobsparts) | #11854, #12155, #12167 | +| [@joglomedia](https://github.com/joglomedia) | #11980 | +| [@jonlwheat2-gif](https://github.com/jonlwheat2-gif) | #11443, #11450, #11549, #11567, #11608, #11633, #11635, #11640, #11644, #11671, #11672, #11673, #11674, #11675, #11676, #11677, #11706, #11783, #12051, #12052, #12053, #12055 | +| [@kanade-hoshino](https://github.com/kanade-hoshino) | #12180 | +| [@Karan825](https://github.com/Karan825) | #11861 | +| [@KaspaPulse](https://github.com/KaspaPulse) | #11389, #11469, #11628, #11666, #11910 | +| [@keeltrace](https://github.com/keeltrace) | #12080, #12223 | +| [@killer30001000](https://github.com/killer30001000) | #12178 | +| [@KooshaPari](https://github.com/KooshaPari) | #12470, #12592, #12667, #12699, #12703, #12706, #12764, #12769, #12771, #13300, #13301, #13302, #13340, #13401, #13403, #13404, #13405, #13406, #13407, #13409, #13410, #13411, #13412, #13413, #13414, #13418, #13424, #13427, #13433, #13523, #13524, #13525, #13528, #13531, #13532, #13534, #13539, #13543, #13545, #13546, #13547, #13550, #13553 | +| [@kriptoburak](https://github.com/kriptoburak) | #11370 | +| [@KrzysiekSko](https://github.com/KrzysiekSko) | #12425, #12432, #12673 | +| [@marcelokarval](https://github.com/marcelokarval) | direct commit / report | +| [@maxmad64bis](https://github.com/maxmad64bis) | #1622, #11435, #11437, #11441, #11537, #11550, #11553, #11555, #11557, #11812, #11842, #11843, #11903, #12151, #12214, #12215, #12218, #12226, #12314, #12316, #12317, #12318, #12319, #12320, #12321, #12507, #12715, #12731, #12744, #12786, #12787, #12788, #12790, #12792, #12794, #12795, #12828, #12832, #12853, #12857, #12870, #12937, #12941, #12975, #13141, #13142, #13146, #13147 | +| [@mdigitalbh81](https://github.com/mdigitalbh81) | #12240 | +| [@Meet6338-X](https://github.com/Meet6338-X) | #11598, #11609, #12110 | +| [@morpheus9393](https://github.com/morpheus9393) | #11943 | +| [@MumuTW](https://github.com/MumuTW) | #11492, #11502, #11506, #11507, #11626, #11675, #11685, #11728, #11746, #11888, #11889, #11890, #11892 | +| [@Neuron-Mr-White](https://github.com/Neuron-Mr-White) | #11622, #11801, #12256 | +| [@NightStalker-87](https://github.com/NightStalker-87) | #12183 | +| [@NoxzRCW](https://github.com/NoxzRCW) | #11879, #11880, #11881, #11882, #11883, #11935 | +| [@ntdat812](https://github.com/ntdat812) | #11585, #12095 | +| [@ntdatt812](https://github.com/ntdatt812) | #11368, #11573, #11574, #11576, #11577, #11580, #11582, #11583, #11588, #11589, #11590, #11591, #11592, #11593, #11672, #12177, #12180, #12873, #12918, #12920, #12921, #12925, #12930, #13007, #13009, #13024, #13025, #13083, #13087, #13101, #13104, #13110 | +| [@official-burak](https://github.com/official-burak) | #11542 | +| [@opensource-elearning](https://github.com/opensource-elearning) | #12179, #12189, #12278, #12286 | +| [@oyi77](https://github.com/oyi77) | #11408, #11409, #11421, #11505, #11677, #12036, #12110 | +| [@pacocartones](https://github.com/pacocartones) | #11521, #11522, #11527, #11528, #11529, #11530, #11531, #11532, #11533, #11534, #11595, #11599, #11603, #11604, #11605, #11607, #11610, #11676, #11714, #11716, #11718, #11767, #11838, #11860, #11862, #11869, #11871, #11872, #11873, #11903, #11906, #11921, #11934, #12356, #12358, #12359, #12360, #12361, #12362, #12364, #12365, #12373, #12374, #12375, #12376, #12377, #12379, #12380, #12381, #12386, #12387, #12389, #12390, #12394, #12395, #12397, #12401, #12403, #12404, #12522, #12523, #12535, #12536, #12540, #12541, #12543, #12545, #12548, #12549, #12550, #12551, #12552, #12647, #12651, #12653, #13055 | +| [@patrykkopycinski](https://github.com/patrykkopycinski) | #11936, #11937, #12224 | +| [@PauloFH](https://github.com/PauloFH) | #11509 | +| [@PauloHSOliveira](https://github.com/PauloHSOliveira) | #12241 | +| [@ponkcore](https://github.com/ponkcore) | #12054 | +| [@Prajeeth-12](https://github.com/Prajeeth-12) | #11634, #12046 | +| [@quiterunner-commits](https://github.com/quiterunner-commits) | #12131, #12143 | +| [@rafacpti23](https://github.com/rafacpti23) | #11554, #11558, #12192, #12197, #12198, #12202, #12203, #12204 | +| [@ragnar-claude](https://github.com/ragnar-claude) | #11564 | +| [@raheemuddin786](https://github.com/raheemuddin786) | #11491, #11839, #11840, #11841, #12003, #12230, #12231, #12232, #12233, #12234 | +| [@rahilmavani](https://github.com/rahilmavani) | #11761 | +| [@Rahulsharma0810](https://github.com/Rahulsharma0810) | #11771 | +| [@RaviTharuma](https://github.com/RaviTharuma) | #11710, #11727, #11797, #11798, #11802, #11805, #11806, #11811, #12098, #12099, #12101, #12449, #12452, #12472, #12473, #12493, #12533, #12607, #12628, #12631, #12636, #12875, #12876, #12880, #12882, #12884 | +| [@rezjalibd](https://github.com/rezjalibd) | #12186 | +| [@RhianB14](https://github.com/RhianB14) | #11692 | +| [@rifqiawl](https://github.com/rifqiawl) | #11517, #11519, #11969 | +| [@rqzbeh](https://github.com/rqzbeh) | #11390 | +| [@Sabeekhann](https://github.com/Sabeekhann) | #11699, #11705 | +| [@santosraju99-hub](https://github.com/santosraju99-hub) | #11755, #11770 | +| [@Seramicx](https://github.com/Seramicx) | #11568 | +| [@Siva010](https://github.com/Siva010) | #12191 | +| [@solstxce](https://github.com/solstxce) | #11597 | +| [@soroush5](https://github.com/soroush5) | #12691 | +| [@tenshiak](https://github.com/tenshiak) | #12279 | +| [@TheDemonTuan](https://github.com/TheDemonTuan) | #11468, #11470, #11471, #11482, #11548, #11758, #11775, #11814 | +| [@thomasmaerz](https://github.com/thomasmaerz) | #12834 | +| [@tuandinh0801](https://github.com/tuandinh0801) | #11454 | +| [@turbolego](https://github.com/turbolego) | #11621, #11762, #11772, #11774, #11781, #12216 | +| [@Tushar49](https://github.com/Tushar49) | #11392 | +| [@ujjawalkaushik1110](https://github.com/ujjawalkaushik1110) | #11863 | +| [@vermasomesh835](https://github.com/vermasomesh835) | #11794 | +| [@vsd2807](https://github.com/vsd2807) | #11565, #11619 | +| [@wahidsadik371-coder](https://github.com/wahidsadik371-coder) | #12126 | +| [@watchingdogs](https://github.com/watchingdogs) | #12031 | +| [@wildcard](https://github.com/wildcard) | #11620 | +| [@xiaoyaner0201](https://github.com/xiaoyaner0201) | #11460, #11662, #12083 | +| [@Xxx91n](https://github.com/Xxx91n) | #11690 | +| [@yourspraveen](https://github.com/yourspraveen) | #11146 | +| [@yxyxy](https://github.com/yxyxy) | #11671 | +| [@ZaimMarzuki](https://github.com/ZaimMarzuki) | #11960 | +| [@Zartharas](https://github.com/Zartharas) | #11340 | +| [@zero-executioner](https://github.com/zero-executioner) | #11631 | +| [@diegosouzapw](https://github.com/diegosouzapw) | maintainer | + +--- ## [3.8.50] — 2026-08-25 _Living section — regenerated 2026-08-12 from all cycle commits (cycle open `ed2db6cb19` → tip). Bullets carry the merged PR and its author; direct pushes listed separately._ diff --git a/docs/i18n/hu/CHANGELOG.md b/docs/i18n/hu/CHANGELOG.md index dddf9dd34b..31101850da 100644 --- a/docs/i18n/hu/CHANGELOG.md +++ b/docs/i18n/hu/CHANGELOG.md @@ -8,16 +8,1119 @@ ## [3.8.51] — TBD -_Living section — cycle opened at the v3.8.50 freeze (parallel-cycle model). Bullets are aggregated from `changelog.d/` fragments at each `/generate-release` phase._ +_Living section — reconciled 2026-09-15 from all cycle commits (`091589089c` → `c0f92ec98a`, 916 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 | **123** | +| 📝 Commits in the cycle | **916** | +| 🔀 Pull requests referenced | **892** | +| 📋 Changelog entries | **919** | +| 🙌 Contributors credited in entries | **121** | +| 🤖 Automated dependency commits | 18 | + +**Entries by type** + +| Type | Count | +| --- | ---: | +| 🐛 Fixes | 592 | +| ✨ Features | 131 | +| 🧹 Chore | 93 | +| 📚 Docs | 42 | +| 🧪 Tests | 32 | +| ♻️ Refactor | 8 | +| 🏗️ Build | 7 | +| ⚡ Performance | 7 | +| ⚙️ CI | 2 | +| ⏪ Reverts | 2 | +| 📦 Dependencies | 2 | +| 🔒 Security | 1 | + +### 🏆 Top 25 contributors this cycle + +_By commits in `091589089c..c0f92ec98a`, author identities consolidated via `.mailmap` and the merged PR's GitHub login. Bots excluded._ + +| # | Contributor | Commits | +| ---: | --- | ---: | +| 🥇 | diegosouzapw | 323 | +| 🥈 | Bob.Hou (@HouMinXi) | 79 | +| 🥉 | Paco Cartones (@pacocartones) | 77 | +| 4 | Dizzle (@maxmad64bis) | 49 | +| 5 | Koosha Paridehpour (@KooshaPari) | 43 | +| 6 | Nguyen Thanh Dat (@ntdatt812) | 31 | +| 7 | Ravi Tharuma (@RaviTharuma) | 26 | +| 8 | Markus Hartung (@hartmark) | 22 | +| 9 | Webman (@jonlwheat2-gif) | 22 | +| 10 | backryun | 17 | +| 11 | anhtahaylove | 15 | +| 12 | Rafa Martins (@rafacpti23) | 10 | +| 13 | Syed Raheemuddin (@raheemuddin786) | 10 | +| 14 | Nguyễn Viết Tuấn (@TheDemonTuan) | 8 | +| 15 | MumuTW | 7 | +| 16 | Paijo (@oyi77) | 7 | +| 17 | Tobias Andersen (@turbolego) | 6 | +| 18 | Bl0ck (@Bl0ck154) | 5 | +| 19 | SHANMUGAPRIYAN (@geek007git) | 5 | +| 20 | KaspaPulse | 5 | +| 21 | Abhishek Sharma (@abhisheksharma2411) | 4 | +| 22 | Abhishek Divekar (@adivekar-utexas) | 4 | +| 23 | Andrew B. (@AndrianBalanescu) | 4 | +| 24 | NoxzRCW | 4 | +| 25 | opensource-elearning | 4 | ### ✨ New Features +- **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). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- Added Google AI Studio Gemini batch text-to-speech support through `POST /v1/audio/speech`. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- 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. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(routing):** subscription-first auto groupings — `auto/subscription` routes only through plan-included connections with a documented hard-stop overage and fails closed on exhaustion, while `auto/thrifty` orders the pool `subscription → keyless → free → cheap → premium` and steps up one rung at a time as each is exhausted. Billing class comes from a curated per-connection catalog (uncurated is treated as metered, never plan-included), both reuse STRICT_ZERO_COST's per-connection verification, and a quota reading whose `resetAt` has passed is now refreshed regardless of TTL so routing returns to plan capacity as soon as it resets ([#11146](https://github.com/diegosouzapw/OmniRoute/pull/11146)) — thanks @yourspraveen +- **feat(providers):** publish a management-authenticated versioned web-session credential contract from OmniRoute's canonical browser credential metadata ([#11340](https://github.com/diegosouzapw/OmniRoute/pull/11340)) — thanks @Zartharas +- **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)) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add Xquik X search with typed results, credential validation, REST routing, and MCP selection ([#11370](https://github.com/diegosouzapw/OmniRoute/pull/11370)) — thanks @kriptoburak +- **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)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(dashboard):** surface durable exclusive managed leases in the existing Sessions view, keeping leased clients visible across idle gaps while marking connections with in-flight work as active ([#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389)) — thanks @KaspaPulse +- **build(bun):** allow Turbopack bundler flag on Bun 1.4+ with configurable Webpack fallback ([#11471](https://github.com/diegosouzapw/OmniRoute/pull/11471)) — thanks @TheDemonTuan +- feat(api): add an opt-in `modelVisibilityAllowlist`/`modelVisibilityDenylist` settings pair to curate exactly which models `/v1/models` advertises, mirrored into every `auto/*` combo candidate pool so a denied model cannot be routed to via combo selection either (#11481) ([#11997](https://github.com/diegosouzapw/OmniRoute/pull/11997)) +- **feat(rankings):** the Free Provider Rankings page shows what each provider actually served over the last 24 h. It ranked by ELO alone, which left a provider that answers every call with an error in first place; the usage data was already served by the API but never requested. A provider with too small a sample shows a dash, not a number ([#11546](https://github.com/diegosouzapw/OmniRoute/pull/11546)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **feat(guardrails):** enforce a bounded, deterministic contract for Video Bridge transcripts — 256 cues, 4096 input code units and 4 KiB UTF-8 per cue, 64 KiB total text, malformed-Unicode rejection, focus-window scoping, cross-source reconciliation with contributing-source metadata, and a structural provenance trust boundary so caller JSON can never self-assert `embedded`/`audio-bridge` provenance ([#11652](https://github.com/diegosouzapw/OmniRoute/issues/11652)) ([#12009](https://github.com/diegosouzapw/OmniRoute/pull/12009)) +- **feat(video):** orchestrate optional Video Bridge audio extraction and Audio Bridge STT behind a dual opt-in (operator setting AND per-request signal) — a new loopback-only broker `mode=audio` operation shares the frame path's exact process queue, deadline, AbortSignal, and byte budgets to extract a bounded mono 16 kHz PCM WAV from the same already-downloaded video, then reuses the existing Audio Bridge transcription boundary; provider segment timing is preserved when available and marked coarse otherwise, and every failure degrades to a visual-only-safe partial instead of throwing (#11654). ([#12012](https://github.com/diegosouzapw/OmniRoute/pull/12012)) +- Add a tenant-bound Video Bridge drill-down lifecycle on top of the existing secure cache substrate: opaque hashed handles (never raw session/video identifiers), preview/standard/detail multiresolution variants resampled on read, response pagination capped at 8 frames and 32 MiB, and a new authenticated `/api/v1/video-bridge/drilldown` consumer route that stays disabled for remote access by default and denies cross-key access with the same response as a nonexistent handle (no existence oracle). ([#12006](https://github.com/diegosouzapw/OmniRoute/pull/12006)) +- **test(video):** Add the Video Bridge FU-07/FU-09 promotion-evidence harness (#11656) — a frozen Zod manifest schema covering the 8 required scenario kinds (static scenes, rapid cuts, late facts, fades, blur, small text, close events, visual prompt injection) with a minimum of 3 repetitions per case, deterministic declarative fixture recipes (`videoBridgePromotionFixtures.ts`), a pure medians/p95 metrics aggregator, a pure FU-07/FU-09 promotion-verdict evaluator applying the ticket's exact thresholds (missing token usage always holds), a digest-only persistence layer that never retains raw media or raw model responses, and a versioned per-model promotion allowlist shipped empty with every model defaulting to `hold`. The FU-07/FU-09 promotion verdicts themselves remain HOLD — they require a real evidence run against real models on VPS 192.168.0.15. ([#12008](https://github.com/diegosouzapw/OmniRoute/pull/12008)) +- **feat(video bridge):** "embedded" transcript provenance can now be legitimately earned instead of merely asserted — a bounded, allowlisted (`mov_text`/`subrip`/`webvtt`) subtitle probe runs through the loopback-only Video Bridge broker (at most 2 streams, 10s subdeadline bounded by the request deadline, 256 KiB output, 4096-code-unit lines), normalized through a bounded, ReDoS-safe WebVTT parser and Zod-validated end to end. The adapter always resolves to an explicit `success`/`absent`/`transient_failure` outcome — a subtitle failure never breaks the visual description path, and only a fingerprint-verified broker response (never a caller-declared label) can produce embedded cues (#11659). ([#12011](https://github.com/diegosouzapw/OmniRoute/pull/12011)) +- Default new Antigravity-family connections (agy CLI imports and Antigravity OAuth connects) to model auto-sync, so live model discovery lands in the synced catalog and `/v1/models` picks up freshly released upstream models (e.g. Gemini 3.7 Flash tiers) without code changes. Existing connections keep their current setting; the per-connection dashboard toggle remains the opt-out. (#11685 — thanks @MumuTW) ([#11887](https://github.com/diegosouzapw/OmniRoute/pull/11887)) +- **feat(ui):** enable React Compiler (`reactCompiler: true` + `babel-plugin-react-compiler`) for automatic memoization at build time ([#11783](https://github.com/diegosouzapw/OmniRoute/pull/11783)) — thanks @jonlwheat2-gif +- **feat(providers):** add SeekAi (`seekai.cc`) as an OpenAI-compatible New-API gateway — catalog id `seekai` (alias `ska`), `https://seekai.cc/v1`, live `/v1/models` via `passthroughModels`, aggregator-list membership so New-API balance detection can opt in. No referral/aff codes. ([#11786](https://github.com/diegosouzapw/OmniRoute/issues/11786)) ([#12557](https://github.com/diegosouzapw/OmniRoute/pull/12557)) — thanks @HouMinXi +- **feat(zai):** add GLM-5.3-Flash Coding Plan support (1M context, 128K output, vision, `low|high|max` reasoning) and route `zai` GLM-5.3-family API-key traffic through the OpenAI-compatible Coding Plan endpoint with native thinking defaults ([#11801](https://github.com/diegosouzapw/OmniRoute/pull/11801)) — thanks @Neuron-Mr-White +- **feat(combo):** choose how combo models are ordered — manual, provider, score, or name — via a sort control in the dashboard builder, persisted in `config.modelSort` and re-applied on load and after add ([#11812](https://github.com/diegosouzapw/OmniRoute/pull/11812)) — thanks @maxmad64bis +- **feat(free):** custom models can be marked free-tier via `customModels[].isFree`; `isFreeModel()` is the first door and `hidePaidModels` respects it even for providers outside the free budget ([#11843](https://github.com/diegosouzapw/OmniRoute/pull/11843)) — thanks @maxmad64bis +- **feat(nodejs):** add `5dive` as a `configure` target — `omniroute configure 5dive` / `omniroute setup-5dive` write a 5dive auth profile that points an agent fleet's `claude` seats at OmniRoute, with the root-only write, the loopback-vs-`https` endpoint rule and the per-seat model pin handled explicitly ([#11852](https://github.com/diegosouzapw/OmniRoute/pull/11852)) — thanks @5dive-bot +- **feat(sse):** treat `max` as a first-class reasoning-effort tier and clamp per model family (GLM 5.1+/DeepSeek V4+/Kimi K3+ keep native `max`; o1/MiniMax/Grok/Muse Spark clamp to their upstream ceiling) ([#11875](https://github.com/diegosouzapw/OmniRoute/pull/11875)) — thanks @Chewji9875 +- **feat(providers):** the provider plugin manifest now advertises a `usage-fetch` capability for the 40 providers that have a wired usage/quota fetcher, so external dashboards can read it from `GET /api/v1/provider-plugin-manifest` instead of parsing `open-sse/services/usage.ts` after every release. Discovery only — no new fetcher, no quota change, and the Dashboard quota widget stays gated by `USAGE_SUPPORTED_PROVIDERS`. `USAGE_FETCHER_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/fetcherProviders.ts`) and is re-exported from `services/usage.ts`, keeping the manifest module a light leaf instead of pulling the ~490-module usage dispatcher into the manifest route. ([#11903](https://github.com/diegosouzapw/OmniRoute/pull/11903)) — thanks @maxmad64bis / @pacocartones +- **feat(plugins):** `OMNIROUTE_PLUGINS_DIR` sets the directory the runtime plugin scanner reads — and the root the plugin manager installs into — overriding the `HOME`-derived default, so a Docker/K8s deployment can point straight at its bind-mounted plugin tree instead of moving `HOME` just to relocate the scan path. An image that exports no home no longer scans `/tmp/.omniroute/plugins` in silence: the resolved directory is logged once at startup as `scanner.dir_resolved`, naming the input that won. Unset, behaviour is unchanged. Distinct from the CLI-only `OMNIROUTE_PLUGIN_PATH`, which finds `omniroute-cmd-*` command packages and never reached this scanner ([#11906](https://github.com/diegosouzapw/OmniRoute/pull/11906)) — thanks @amaleta / @pacocartones +- **feat(leases):** add an explicit owner-authenticated status action that returns only the active lease's privacy-safe configured connection and provider labels, with generation fencing and no credential or internal-id disclosure ([#11910](https://github.com/diegosouzapw/OmniRoute/pull/11910)) — thanks @KaspaPulse +- **feat(providers):** import-from-file modal shows per-row API errors and ships a downloadable CSV template ([#12071](https://github.com/diegosouzapw/OmniRoute/issues/12071)) ([#12504](https://github.com/diegosouzapw/OmniRoute/pull/12504)) — thanks @HouMinXi +- **feat(providers):** dashboard search matches connection name and `baseUrl` so imported OpenAI-compat nodes surface on the provider card ([#12108](https://github.com/diegosouzapw/OmniRoute/issues/12108)) ([#12495](https://github.com/diegosouzapw/OmniRoute/pull/12495)) — thanks @HouMinXi +- **feat(routing):** add a `score` Auto router strategy that selects the highest configured weighted score and reuses `explorationRate`. ([#12155](https://github.com/diegosouzapw/OmniRoute/pull/12155)) — thanks @jacobsparts +- Add a runtime feature flag to disable universal context handoffs globally without changing the default behavior. ([#12167](https://github.com/diegosouzapw/OmniRoute/pull/12167)) — thanks @jacobsparts +- **feat(providers):** the provider plugin manifest now also advertises a `usage-supported` capability for the 46 providers whose usage API is accepted by the server and Dashboard routes, so integrators can distinguish "the server will serve quota for this provider" from "a fetcher is wired" without reading TypeScript. Discovery only — no fetcher or quota change. `usage-fetch` resolves on id or alias (the usage dispatcher accepts both); `usage-supported` resolves on id alone, matching the runtime guard `USAGE_SUPPORTED_PROVIDERS.includes(providerId)`. `USAGE_SUPPORTED_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/supportedProviders.ts`) and is re-exported from `providers.ts`, mirroring the `fetcherProviders` leaf from #11903 and keeping the manifest a light module. ([#12214](https://github.com/diegosouzapw/OmniRoute/pull/12214)) — thanks @maxmad64bis +- **feat(rankings):** order Free Provider Rankings by what each provider actually served — `GET /api/free-provider-rankings?sortBy=reliability` and a "Most reliable first" toggle on the page. Providers with too few calls to state a success rate keep their score order below the measured ones; the default order is unchanged ([#12218](https://github.com/diegosouzapw/OmniRoute/pull/12218)). — thanks @maxmad64bis +- **perf(sse):** defer `cloneLogPayload()` in the structured SSE collector until after the `maxEvents`/`maxBytes` cap check, eliminating ~9,800 wasted `structuredClone` calls per streaming response (65–71% faster `push()`). Reducer snapshot isolation restored for OpenAI and Responses summaries ([#12241](https://github.com/diegosouzapw/OmniRoute/pull/12241)) — thanks @PauloHSOliveira ([#12243](https://github.com/diegosouzapw/OmniRoute/pull/12243)) +- **feat(usage):** Devin CLI agentic quota (Codeium seat-management GetUserStatus) and OpenRouter key limits plus account credits now surface in Provider Limits ([#12256](https://github.com/diegosouzapw/OmniRoute/pull/12256) — thanks @Neuron-Mr-White) +- **feat(settings):** persist `headroomUrl` through Settings so status/start use the operator URL instead of only `HEADROOM_URL` ([#12306](https://github.com/diegosouzapw/OmniRoute/issues/12306)) ([#12487](https://github.com/diegosouzapw/OmniRoute/pull/12487)) — thanks @HouMinXi +- **feat(auto-combo):** Auto-Combo scoring can now weigh how often a provider/model has actually succeeded. The engine already carried that number on every candidate — 24 hours of usage history behind a ten-sample floor, real-time metrics otherwise — and the scoring function never read it, while the weight table described `stability` as if it did. `reliability` (`1 - failureRate`, with the same field precedence and the same rate-bounding the speed ranking already uses, so a corrupt reading means "nothing observed" rather than "fails every call") is now a declared factor shipping at weight `0`, so routing is unchanged until an operator gives it one, and the `stability` description now matches what that factor computes ([#12317](https://github.com/diegosouzapw/OmniRoute/pull/12317)) — thanks @maxmad64bis +- **feat(routing):** With `freeAccessPolicy: "strict"`, the read-only candidate listing (`GET /v1/auto-combo/{channel}/candidates`) no longer hides the candidates the zero-cost guard excludes — the same read-only transparency the resilience filter already honours (#9133). Each candidate now carries `freeAccessExclusion` saying why it would be kept out, and it tells an exhausted allowance apart from a quota reading that never arrived or went stale, which used to look identical from the outside. Routing is unchanged: the listing reports, it never enforces. The separate `excludeTosAvoid` guard still drops its candidates without a reason; that gap is now documented rather than closed ([#12319](https://github.com/diegosouzapw/OmniRoute/pull/12319)) — thanks @maxmad64bis +- **feat(radar):** The Radar catalog table now shows two facts it was already receiving from the feed and dropping on the floor: the per-model rate limits (requests and tokens, per minute and per day) in a new column, and a badge when a provider's terms state it may train on the prompts you send. A limit of zero renders as zero rather than "rate-only" — for a ceiling those are opposite facts — and a model with no training statement gets no badge, because an absent statement is not a guarantee ([#12320](https://github.com/diegosouzapw/OmniRoute/pull/12320)) — thanks @maxmad64bis +- **feat(radar):** explain Community, single-use, contributor, supporter, recovery, abuse, offers, and privacy rules before either Radar activation action, and remove the superseded fixed-PR grant promise from every UI locale ([#12342](https://github.com/diegosouzapw/OmniRoute/pull/12342)) +- **feat(gamification):** the dashboard Profile page now shows the real daily streak — `/api/gamification/level` returns `streak: { current, longest }` (per key with `apiKeyId`, operator-wide maximum otherwise) and the streak card reads it instead of a hard-coded 0 (#2403) ([#12377](https://github.com/diegosouzapw/OmniRoute/pull/12377)) — thanks @pacocartones +- **feat(gamification):** the dashboard leaderboard now shows each API key's display name under the Name column instead of a truncated key id; `GET /api/gamification/leaderboard` attaches `name` per entry (name only — no key material), while the shared ranking helper and the federation leaderboard stay id-only — thanks @pacocartones ([#12385](https://github.com/diegosouzapw/OmniRoute/pull/12385)) +- **feat(gamification):** enforce the documented 1000 XP/min per-API-key anti-cheat rate limit on the XP award path; over-limit awards are logged and skipped instead of persisted, and the sliding window now matches the timestamp format stored in `xp_audit_log` ([#2403](https://github.com/diegosouzapw/OmniRoute/issues/2403)) ([#12390](https://github.com/diegosouzapw/OmniRoute/pull/12390)) — thanks @pacocartones +- **feat(admin):** localize the gamification anomalies page — the loading state, the Status column and the Suspicious badge now come from the `common` catalog (new `common.suspicious` key propagated to every locale) — add it to the Gamification sidebar group as `gamification-admin` (`/dashboard/gamification/admin`), and expose the loading and empty states as polite `role="status"` live regions (#12401 — thanks @pacocartones) +- **feat(providers):** skip GitHub combo members missing from the live synced catalog, and drop Copilot models that are policy-disabled or hidden from the model picker ([#12473](https://github.com/diegosouzapw/OmniRoute/pull/12473)) — thanks @RaviTharuma +- **feat(i18n):** locale-expansion tooling — `npm run i18n:add-locale` adds a language to config, dashboard, docs mirrors, CLI, README/indexes and site in one command; browser, CLI and cookie detection resolve `uk`, `fil`/`tl`, `zh-Hant` (and the retired legacy `in` → `id`) via config aliases; `config/i18n.json` now ships in the npm package so the published CLI can read it; new real-translation ratio gate (`npm run i18n:check-ratio`, advisory) with a per-locale ratchet baseline; `run-translation --adopt` restores `.i18n-state.json`; `sync-language-bars` generates 🌐 bars from config; `validate_translation.py` loads its allowlist again. Retires the duplicate `in` locale (Indonesian mislabelled as Hindi) — 42 honest locales; saved `NEXT_LOCALE=in` / `OMNIROUTE_LANG=in` keep working. (#12496) +- **feat(api):** Emit gateway-measured `tokens_per_second` (TTFT excluded) on streaming usage and `X-OmniRoute-Tokens-Per-Second` when first-token latency is known ([#12616](https://github.com/diegosouzapw/OmniRoute/issues/12616)) ([#12631](https://github.com/diegosouzapw/OmniRoute/pull/12631)) — thanks @RaviTharuma +- **feat(opencode-plugin): map gateway cost/usage/tok/s onto OpenCode inference payloads** — the official plugin copies `X-OmniRoute-Response-Cost`, token counts, `X-OmniRoute-Tokens-Per-Second` / `usage.tokens_per_second`, TTFT, and the winning `X-OmniRoute-Model` onto the JSON/SSE body OpenCode already consumes. Missing tok/s is left unset (never `tokens / latency`). (#12636) — thanks @RaviTharuma +- **feat(combo):** add combo strategy `quota-weighted`: skip exhausted accounts, then weighted-draw by leftover / in-flight load; existing conversations stay pinned until the account is empty ([#12789](https://github.com/diegosouzapw/OmniRoute/pull/12789)) — thanks @HouMinXi +- **feat(models):** Account live listings become the chat catalog source for Claude, Codex, Copilot, and AGY; public metadata only fills prices on IDs those accounts already list. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **fix(models):** Union `agy` and `antigravity` live catalogs so `agy/gemini-3.8-flash-high` is not rejected after the prefix folds to `antigravity`. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **feat(opencode):** Add `@omniroute/opencode-plugin-v2`, an opencode v2 plugin that gives a v2 install a route to an OmniRoute gateway: it publishes models, combos and auto-combos into the host catalog, refreshes them lazily behind a 300s TTL, and keeps serving the last known catalog from an on-disk snapshot when the gateway is unreachable. The v1 plugin (`@omniroute/opencode-plugin`) is untouched and keeps working as before. The plugin adapts to the host instead of assuming one shape: it reads the shape the host seeds into the catalog draft and publishes the `api` block, the pre-`api` top-level fields, or both. The gateway key comes from the host credential store when one is connected, so no secret needs to sit in a config file; a source the gateway refuses is named in a warning instead of silently degrading the catalog to raw model ids; and tool schemas bound for a Gemini model are stripped of the JSON-Schema keywords Gemini rejects, so tool calling survives that route ([#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870)) — thanks @maxmad64bis +- feat(services): show sanitized CLIProxyAPI account health from its authenticated management API without exposing credentials, file paths, or raw account metadata (#6342) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add AnySearch free web search + URL extract (webFetch) with typed results, credential validation, REST routing, and MCP selection - fallback-only ([#11690](https://github.com/diegosouzapw/OmniRoute/pull/11690)) — thanks @Xxx91n +- **feat(dashboard):** display clamped `[0, 100]%` cached input token ratio in request logs table ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) — thanks @b3nw +- **feat(catalog):** add `OMNIROUTE_DISABLE_THINKING_LEVEL_VARIANTS` feature flag to optionally filter out thinking level variants from model catalog ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) — thanks @b3nw +- **feat(dashboard):** continuously export call logs to external analytics stores. A pluggable destination registry ships the full Logs-tab record set on an hourly `JobRegistry` cron, with a persisted per-destination cursor, batched inserts, a config UI rendered from each destination's own field descriptors, and a REST layer (`/api/log-export/*`) for CRUD, a connection test, and an on-demand run. A destination can opt into `includeBodies` to also ship the request and response payloads shown in the Logs detail pane, including the client and provider views of each call; this is off by default, and payloads inherit the dashboard's PII sanitisation, secret redaction and `noLog` handling. Google BigQuery is the first destination, using a service-account key stored encrypted at rest and streaming inserts keyed by call-log id, into a table that is day-partitioned on `timestamp` and clustered on `api_key_name`, `provider`, `model` and `status`. ([#11945](https://github.com/diegosouzapw/OmniRoute/pull/11945)) — thanks @dpozimski +- **feat(providers):** add MaxAI as a signed, OpenAI-compatible provider serving its 13 paid chat models (GPT-5.6 / Luna / Thinking, Claude 5 Sonnet, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite, Grok 4.1-fast / 4.5, DeepSeek V3.2 / R1, Llama 3.3 70B) through OmniRoute's `/v1` endpoint, with per-request HMAC-SHA1→SM3→AES request signing, live model + context-window discovery from `/models/get_config`, and prompted tool-calling translated to OpenAI `tool_calls` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI vision input — image_url content parts are forwarded inline in `message_content` to the 6 vision-capable models (GPT-5.6 / Luna / Thinking, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite) ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI image generation — 6 image models (gpt-image-1, dall-e-3, flux-1-schnell/dev/pro, sd3-medium) exposed through `POST /v1/images/generations` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI document RAG — inline base64 file/document attachments are uploaded to MaxAI (content-addressed `doc_id`) and attached to the chat via `doc_list` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** browserless MaxAI onboarding — email device-pair login (`/api/providers/[id]/login`) and signed access-token refresh, so a connection can be created and kept fresh without a real browser or Google OAuth ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** per-provider TLS impersonation profile (MaxAI presents a Windows Firefox-150 client fingerprint) so its bot-sensitive endpoints accept OmniRoute traffic ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** add **Nimble** as a web-search and web-fetch provider (`nimble-search`) — `/v1/search` routes to Nimble's search API at `lite` depth (locale, freshness and include/exclude domain filters mapped onto the shared request contract), and `/v1/web/fetch` routes to Nimble Extract, which covers all four fetch formats (`markdown`, `html`, `links`, `screenshot`) from a single call. One API key serves both surfaces. ([#11620](https://github.com/diegosouzapw/OmniRoute/pull/11620)) — thanks @wildcard +- The npm publish is automatic again, through npm Trusted Publishing (OIDC): the hosted `stage-npm` job publishes with a short-lived credential minted from GitHub's id-token — no `NPM_TOKEN`, no 2FA prompt, provenance attached. `publish_mode=staged` (owner approves with 2FA) and `direct` (token) remain available on `workflow_dispatch`. ([#11931](https://github.com/diegosouzapw/OmniRoute/pull/11931)) +- **feat(providers):** Add **Opper** as an API-key gateway provider — EU-hosted AI gateway with 700+ models from 30+ providers behind one OpenAI-compatible API and one key (`OPPER_API_KEY`); model ids use `provider/model` format (e.g. `anthropic/claude-sonnet-4-6`, `openai/gpt-5`); live model catalog at `https://api.opper.ai/v3/compat/models`; entry mirrors `requesty` (same shape, `passthroughModels: true`, no static seed) ([#11629](https://github.com/diegosouzapw/OmniRoute/pull/11629)) +- **feat(dashboard):** the `/dashboard/orchestration` snapshot hook now subscribes to the `agents` WebSocket channel (`agent.task.updated`) instead of `requests` as its refetch trigger, and relaxes its background poll from 5s to 30s while that WS connection is up — falling back to the tighter 5s cadence, reprogrammed live on any connect/disconnect transition, whenever the socket is down. ([#12409](https://github.com/diegosouzapw/OmniRoute/pull/12409)) +- **feat(dashboard):** Orchestration canvas quick wins — search box plus state/source/provider filter chips with a one-click clear, and per-source collapse/expand, all reflected in the URL so a filtered/collapsed view is shareable and survives a refresh; the detail drawer gained a "copy trace JSON" action and hardened error/empty-state and accessibility handling; the Agents-tab edges now animate traveling particles along active (running) connections; and the canvas node/edge status colors moved off fixed hex values onto theme-aware `--orch-status-*` CSS custom properties, so they adapt correctly to light/dark mode. ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- **chore(dashboard):** Orchestration UI hardening pass and the missing component/model test coverage it called for — `OrchestratorNode`/`ActivityNode`/`OverflowNode` rendering, the `?node=`/overflow-click page routing, the Agents-tab orchestrator-click no-op and `showCompleted` toggle, and the overview kanban's done-column sort order (#12270, #12271). ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- New `/dashboard/orchestration` page: live unified view of everything running — Cloud Agent, A2A and Conductor as a real-time graph (Agents tab), the combo cascade (Routing tab, reusing the Combo Live Studio) and a state kanban (Overview tab), with a detail drawer (trace, cost, approve/cancel). Read-only over existing APIs — no new backend. Canvas concept credit: PR #11815 design ([#12261](https://github.com/diegosouzapw/OmniRoute/pull/12261)) +- **feat(dashboard):** new "History" tab on `/dashboard/orchestration` — an Airflow-style grid of finished runs over a 24h/7d/30d preset window, one row per (source, identity), clicking a cell opens the existing detail drawer. It is backed by real persistence: A2A task lifecycle transitions are now written to the `a2a_tasks` table (purged after 30 days, configurable via `OMNIROUTE_A2A_HISTORY_RETENTION_DAYS`) and served by the new `GET /api/a2a/tasks/history` listing endpoint, with the task-detail route falling back to persisted history once a run leaves the in-memory snapshot. Conductor runs stay remote and are not persisted locally — the tab says so instead of silently omitting them. ([#12479](https://github.com/diegosouzapw/OmniRoute/pull/12479)) +- **feat(dashboard):** the orchestration detail drawer gained a "Repeat" action for Cloud Agent, A2A and Conductor tasks — a two-click confirm (click once to arm, click again within the confirm window to fire) re-submits the original prompt/input as a new run. The button is disabled with an explanatory tooltip whenever the original input can't be recovered from the loaded task detail (e.g. it never carried a prompt, or the detail failed to load). Two limitations of the A2A variant, by design: it targets the `/a2a` JSON-RPC endpoint, which authenticates with an API key only (`REQUIRE_API_KEY=true` or a configured `OMNIROUTE_API_KEY` makes a dashboard-session repeat answer `HTTP 400` — surfaced verbatim in the drawer's error line, never as a success), and the `message/send` call is SYNCHRONOUS: the POST blocks for the whole skill run, so the success confirmation only appears once the repeated task finishes. A dashboard-authenticated A2A creation path is deliberately left to a follow-up — widening the endpoint's auth posture is an operator decision, not a side effect of this feature. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(a2a):** A2A task execution now records which memories were consulted for the task's last user message as `metadata.memoryHits` (id/key/type/content-snippet) plus a `memory_hits` history event, purely for observability — the retrieved memory is never injected into a skill's prompt or behavior. Gated by the `OMNIROUTE_A2A_MEMORY_HITS` kill-switch (default enabled; set to `0` to skip the recall lookup entirely). The drawer's new "Memory used" section lists these hits for a2a tasks and is omitted whenever there are none. Known limitation: recall only resolves under the keyless posture — a keyed caller's task owner is a SHA-256 prefix of the API key, while memory rows are keyed by the database api-key id, and no hash→id lookup exists today, so the hit list stays empty for keyed callers. The recorded hits are also kept out of the task's own `input` (and therefore out of the persisted input and of the "Repeat" request body), so repeating a task never re-sends the previous run's memory snippets. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(providers):** add a Perplexity Agent API provider (`perplexity-agent` / `pplx-agent`) for Perplexity `/v1/responses`, including the documented Anthropic, OpenAI, Google, xAI, DeepSeek, Z.AI, Moonshot/Kimi, NVIDIA, and Perplexity model IDs plus Anthropic-model `max_output_tokens` compatibility. ([#12103](https://github.com/diegosouzapw/OmniRoute/pull/12103)) — thanks @AIB1TAL0S +- **feat(providers):** add RPD (Requests Per Day) limit to provider rate limit overrides across UI, schemas, DB, and i18n ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) — thanks @b3nw +- **feat(providers): add UC Direct (uncensored.com Developer API), the metered OpenAI-compatible surface.** A standard OpenAI-compatible passthrough (default executor) for uncensored.com's official REST API at `https://api.uncensored.com/api/v1`: `X-api-key` auth (never-expiring `uai_sk_live_` key), `POST /chat/completions` with streaming SSE and native tool-calling, and the full live metered catalog (82 models across 15 providers, discovered from the public `GET /v1/models`). Registered as provider `uc-direct` (alias `ucd`). Complements the un-metered `uc` persona provider — same models, metered credits and a plain API key instead of a subscription session. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(providers): add UC (uncensored.com), the un-metered subscription "persona" chat as an OpenAI-compatible provider.** A WebSocket web-app port: a durable Clerk credential mints a short-lived session token per connect (browserless — no API key), driving UC's persona socket. Ships the browserless email-code login (request → verify → harvest), the 19 verified persona models (Claude Opus, Gemini, Grok, GLM, Kimi, DeepSeek, MiniMax, incl. the uncensored variants), prompted `` tool-calling with a per-model code-style dialect + auto-cure retry for guardrailed models, live ``/reasoning split, streaming + non-streaming OpenAI responses, and full quota/auth error surfacing (paywall / message-limit / rate-limit → 429, invalid session → 401 re-login). Full multimodal parity via the persona blob-upload layer: **vision** (image input, 15 vision-capable models), **document RAG** (PDF/doc upload, server-side extraction), **image generation** (22 models), **video generation** (14 models, async signed-url → poll), and **TTS** (streaming MP3). Registered as provider `uc` (alias `ucn`). The metered OpenAI-compatible Developer API is a separate `uc-direct` provider. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(logs):** add copy-all button to request detail modal ([#11083](https://github.com/diegosouzapw/OmniRoute/pull/11083)) — thanks @AndrianBalanescu +- **feat(check):** make serviceKinds required and add the reverse-walk provider consistency gate ([#11392](https://github.com/diegosouzapw/OmniRoute/pull/11392)) — thanks @Tushar49 +- **feat(providers):** only advertise verified-working connections by default ([#11448](https://github.com/diegosouzapw/OmniRoute/pull/11448)) — thanks @alltomatos +- **feat(resilience):** add hierarchical concurrency admission ([#11493](https://github.com/diegosouzapw/OmniRoute/pull/11493)) +- **feat(providers):** derive + surface expiry for JWT-bearing web cookies (#11497) ([#11505](https://github.com/diegosouzapw/OmniRoute/pull/11505)) — thanks @oyi77 +- **feat(tunnels):** support named cloudflared tunnels via CLOUDFLARED_CONFIG ([#11561](https://github.com/diegosouzapw/OmniRoute/pull/11561)) — thanks @ggiak +- **feat(compression):** make proactive context-compression threshold a live setting ([#11564](https://github.com/diegosouzapw/OmniRoute/pull/11564)) — thanks @ragnar-claude +- **feat(resilience):** add provider peak-hour protection ([#11622](https://github.com/diegosouzapw/OmniRoute/pull/11622)) — thanks @Neuron-Mr-White +- **feat(providers):** add 1min.ai provider ([#11631](https://github.com/diegosouzapw/OmniRoute/pull/11631)) — thanks @zero-executioner +- **feat(dashboard):** collapsible JSON tree viewer for request/response payloads ([#11703](https://github.com/diegosouzapw/OmniRoute/pull/11703)) — thanks @hartmark +- **feat(routing):** add LiquidAI LFM2.5-2.6B free tier via OpenRouter ([#11752](https://github.com/diegosouzapw/OmniRoute/pull/11752)) — thanks @brick30llc-ctrl +- **feat(pricing):** add GLM-5.3-Flash pricing, model specs, and catalog registration ([#11830](https://github.com/diegosouzapw/OmniRoute/pull/11830)) — thanks @AndrianBalanescu +- **feat(routing):** add relayMode for schema-locked context handoffs ([#11839](https://github.com/diegosouzapw/OmniRoute/pull/11839)) — thanks @raheemuddin786 +- **feat(config):** add support for runtime system prompt configuration and hot-reloading ([#11841](https://github.com/diegosouzapw/OmniRoute/pull/11841)) — thanks @raheemuddin786 +- **feat(routing):** expand connection-aware quota prefilter across combo strategies (#11682) ([#11850](https://github.com/diegosouzapw/OmniRoute/pull/11850)) — thanks @HouMinXi +- **feat(models):** reactive discovery sync after Antigravity model-not-found 404 ([#11888](https://github.com/diegosouzapw/OmniRoute/pull/11888)) — thanks @MumuTW +- **feat(models):** auto-aliases and token limits from synced Antigravity discovery ([#11889](https://github.com/diegosouzapw/OmniRoute/pull/11889)) — thanks @MumuTW +- **feat(settings):** raise sticky round-robin limit caps to 1000 ([#12015](https://github.com/diegosouzapw/OmniRoute/pull/12015)) — thanks @adivekar-utexas +- **feat(resilience):** operator-configurable global credential health check interval ([#12043](https://github.com/diegosouzapw/OmniRoute/pull/12043)) — thanks @adivekar-utexas +- **feat(quota):** use official OpenCode Go usage API ([#12124](https://github.com/diegosouzapw/OmniRoute/pull/12124)) — thanks @ddarkr +- **feat(resilience):** default the credential health check sweep to 60 minutes ([#12138](https://github.com/diegosouzapw/OmniRoute/pull/12138)) — thanks @adivekar-utexas +- **feat(quality):** new-code mode for the complexity and dead-code ratchets (Clean as You Code) ([#12142](https://github.com/diegosouzapw/OmniRoute/pull/12142)) +- **feat(dashboard):** orchestration canvas — unified model + snapshot hook (part 1/2) ([#12156](https://github.com/diegosouzapw/OmniRoute/pull/12156)) +- **feat(usage):** add Kilo Code balance and Kilo Pass quotas ([#12178](https://github.com/diegosouzapw/OmniRoute/pull/12178)) — thanks @killer30001000 +- **feat(providers):** refresh vendored ChatGPT Web connector to v4.0.7 ([#12181](https://github.com/diegosouzapw/OmniRoute/pull/12181)) — thanks @backryun +- **feat(providers):** manual "Clear cooldown" action in the cooling panel ([#12224](https://github.com/diegosouzapw/OmniRoute/pull/12224)) — thanks @patrykkopycinski +- **feat(providers):** restore ChatGPT Web via clean-room browser transport ([#12239](https://github.com/diegosouzapw/OmniRoute/pull/12239)) — thanks @backryun +- **feat(sse):** wire the PROVIDER_PROFILES window gate into the global provider cooldown ([#12247](https://github.com/diegosouzapw/OmniRoute/pull/12247)) +- **feat(providers):** modernize CLOVA Studio chat and embeddings ([#12277](https://github.com/diegosouzapw/OmniRoute/pull/12277)) — thanks @backryun +- **feat(browser):** adopt Obscura as primary headless browser engine with Chromium fallback ([#12286](https://github.com/diegosouzapw/OmniRoute/pull/12286)) — thanks @opensource-elearning +- **feat(sse):** full language parity for output styles (es/de/fr/it/ru/zh + autoDetect) ([#12289](https://github.com/diegosouzapw/OmniRoute/pull/12289)) +- **feat(quality):** complete test:scoped — --full map rebuild, stdin selection, CI loader parity (#8084 D1) ([#12353](https://github.com/diegosouzapw/OmniRoute/pull/12353)) +- **feat(providers):** refresh Fable, Cursor, and Devin catalogs ([#12367](https://github.com/diegosouzapw/OmniRoute/pull/12367)) — thanks @backryun +- **feat(video):** redact transcript text from logs and durable memory (#12150 P1) ([#12427](https://github.com/diegosouzapw/OmniRoute/pull/12427)) +- **feat(dashboard):** parent-link, genuine-continuation badge, and modal perf fixes ([#12448](https://github.com/diegosouzapw/OmniRoute/pull/12448)) — thanks @hartmark +- **feat(dashboard):** adaptive context-budget dial on compression panel ([#12488](https://github.com/diegosouzapw/OmniRoute/pull/12488)) — thanks @HouMinXi +- **feat(providers):** refresh Z.ai Web models and browser transport ([#12524](https://github.com/diegosouzapw/OmniRoute/pull/12524)) — thanks @backryun +- **feat(video):** redact raw client-snapshot transcript fields in the detailed log (#12150 P2) ([#12528](https://github.com/diegosouzapw/OmniRoute/pull/12528)) +- **feat(providers):** refresh NVIDIA hosted models ([#12538](https://github.com/diegosouzapw/OmniRoute/pull/12538)) — thanks @backryun +- **feat(quota):** Moonshot Open Platform balance and TPD lock for custom nodes ([#12590](https://github.com/diegosouzapw/OmniRoute/pull/12590)) — thanks @HouMinXi +- **feat(video):** redact transcript in the in-memory pending-request snapshot (#12430 item 6) ([#12596](https://github.com/diegosouzapw/OmniRoute/pull/12596)) +- **feat(dashboard):** link a log entry's Conversation Context to its owning conversation ([#12646](https://github.com/diegosouzapw/OmniRoute/pull/12646)) — thanks @hartmark +- **feat(catalog):** eligibility-gated free-tier bucket ([#12669](https://github.com/diegosouzapw/OmniRoute/pull/12669)) +- **feat(db):** fail-closed previous_response_id continuation for redacted video turns (#12150 P2b) ([#12707](https://github.com/diegosouzapw/OmniRoute/pull/12707)) +- **feat(gamification): pay the documented `streak_bonus` and `badge_unlock` XP rewards.** `XP_REWARDS` listed both rewards but the award pipeline never paid them: the private reward table in `events.ts` omitted them, `updateStreak()` did not report when a streak extended, and badge unlocks carried no XP. Every request that extends a daily streak now pays `streak_bonus × streak length` once per UTC day (guarded by a same-day `xp_audit_log` check), and every badge unlocked through the pipeline pays `badge_unlock` once per badge (guarded by the `user_badges` primary key; `unlockBadge()` now reports whether it inserted). Bonus XP flows through the same `addXp` + level sync + global/weekly/monthly leaderboard path as action XP, so level-ups and rankings include it. The Radar supporter recognition unlock stays XP-free. (#12522 — thanks @pacocartones) +- **feat(providers):** advertise a `free-tier` capability in the provider plugin manifest for every provider with documented free models, so sidecars and dashboards can filter free-capable providers without reading the quota catalog ([#12786](https://github.com/diegosouzapw/OmniRoute/pull/12786)) — thanks @maxmad64bis +- **feat(providers):** Added EURouter as an OpenAI-compatible API-key gateway (`https://api.eurouter.ai/v1`), with live model discovery via `passthroughModels`. Its copy states that models are served by third-party upstreams listed per model, so an EU-based router is not read as EU data residency for inference. ([#13025](https://github.com/diegosouzapw/OmniRoute/pull/13025)) — thanks @ntdatt812 +- **feat(providers):** Added GreenPT as an OpenAI-compatible API-key provider (`https://api.greenpt.ai/v1`), with live model discovery via `passthroughModels`. No free-inference badge: the published docs describe a free API subscription billed per token, not a free tier. ([#13024](https://github.com/diegosouzapw/OmniRoute/pull/13024)) — thanks @ntdatt812 +- **feat(i18n):** 9 new locales — Greek (`el`), Croatian (`hr`), Serbian (`sr`), Lithuanian (`lt`), Estonian (`et`), Latvian (`lv`), Slovenian (`sl`), Maltese (`mt`), Irish (`ga`) — across the dashboard, docs mirrors, CLI, README and the site; OmniRoute now ships every official EU language (51 locales). (#13044) +- **feat(i18n):** 8 new locales — Kannada (`kn`), Malayalam (`ml`), Odia (`or`), Punjabi (`pa`), Nepali (`ne`), Sinhala (`si`), Burmese (`my`), Khmer (`km`) — across the dashboard, docs mirrors, CLI, README and the site (59 locales). The translator now restores the ICU literal escape around angle placeholders and splits oversized docs sections before translating. (#0000) ([#13660](https://github.com/diegosouzapw/OmniRoute/pull/13660)) +- feat(providers): **list Agnes 3.0 Flash as the current free chat model, drop retired 1.5 Flash, add Image 2.0/2.5 Flash plus Video 2.5/2.5 Flash, and discover the live `/v1/models` catalog (including the CN host `api.agnes-ai.cn`).** `agnes-1.5-flash` now forwards to `agnes-3.0-flash`. Video 2.5 polls `GET /v1/videos/{id}` (not the V2.0 `/agnesapi` contract). Live `/v1/models` (2026-09-09) no longer serves 1.5; the wiki marks it deprecated. 3.0 Flash is 512K context / 65,536 max output, same window as 2.5. CN-region keys use the existing per-connection base-URL field, default stays `apihub.agnes-ai.com`. ([#13120](https://github.com/diegosouzapw/OmniRoute/pull/13120)) — thanks @HouMinXi +- **feat(sse):** Codex and OpenAI catalogs list GPT-6 Astra with effort aliases (`-low` through `-ultra`); Codex CLI identity pins `@openai/codex@0.153.4` in lockstep with the image ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **feat(dashboard):** the orchestration History tab gained a "Compare runs" mode — toggling it turns each grid cell into a 2-item selection queue (a 3rd click drops the oldest pick), and picking two cells opens a side-by-side comparison panel instead of the usual detail drawer. The panel fetches both runs' detail the same way the drawer does (falling back to persisted history once a run leaves the live TTL window) and shows, per side: identity/source/state, start time, a signed `right - left` delta for duration/cost/event count, the event timeline aligned by index, and any memory hits. One side's fetch failing never blocks the other, and a delta is only ever computed when both sides have a finite value — otherwise it renders "—", never `NaN`. Comparing two runs from different sources or skills still works; a banner marks the deltas as informational rather than hiding them, since the two runs aren't a strict apples-to-apples pair. ([#12677](https://github.com/diegosouzapw/OmniRoute/pull/12677)) +- **feat(dashboard):** badge a conversation that never reached a clean stop ([#12717](https://github.com/diegosouzapw/OmniRoute/pull/12717)) — thanks @hartmark +- **feat(release):** reconcile-changelog tool + version-anchored fragment aggregation ([#12987](https://github.com/diegosouzapw/OmniRoute/pull/12987)) +- **feat(mcp):** carry context_length through MCP list_models_catalog projection (#12776) ([#13406](https://github.com/diegosouzapw/OmniRoute/pull/13406)) — thanks @KooshaPari +- **feat(cli):** make startup readiness budget configurable (#13369) ([#13433](https://github.com/diegosouzapw/OmniRoute/pull/13433)) — thanks @KooshaPari + ### 🐛 Bug Fixes +- **security(streaming):** sanitize generic mid-stream error messages before emitting OpenAI, + Responses, or Claude SSE failure frames and before diagnostic logging, while preserving raw + failures for internal classification and keeping client disconnects out of provider failure state. ([#12457](https://github.com/diegosouzapw/OmniRoute/pull/12457)) +- **fix(claude):** `blockExtraUsage: false` no longer lets 5h quota preflight skip the account; extra usage is billed after the session bar is gone, so the request must reach Anthropic ([#12803](https://github.com/diegosouzapw/OmniRoute/pull/12803)) — thanks @HouMinXi +- Sanitize HuggingChat conversation-creation and message-send transport failures before they reach client error bodies or provider logs. ([#12467](https://github.com/diegosouzapw/OmniRoute/pull/12467)) +- **fix(dashboard):** Keep local and theme-aware provider SVG icons at a definite layout size so Chromium does not collapse them to 0×0 after the v3.8.50 image-rendering change ([#12054](https://github.com/diegosouzapw/OmniRoute/pull/12054)) — thanks @ponkcore +- **fix(security):** Sanitize provider and runtime failures before public API, SSE and MCP responses and before persistent request, proxy and usage logs, preventing credentials, stack traces and host filesystem paths from crossing those boundaries while preserving stable error codes and useful diagnostics. ([#12506](https://github.com/diegosouzapw/OmniRoute/pull/12506)) +- **fix(cli):** `omniroute tunnel create` no longer crashes with `Cannot read properties of undefined (reading optsWithGlobals)` — removed the duplicate positional argument that caused Commander.js to misalign the action callback parameters ([#12295](https://github.com/diegosouzapw/OmniRoute/issues/12295)) ([#12368](https://github.com/diegosouzapw/OmniRoute/pull/12368)) — thanks @gonisulaimann +- Stop advertising Gemini Live-only models as supported audio endpoints until OmniRoute proxies the bidirectional Live protocol. ([#11710](https://github.com/diegosouzapw/OmniRoute/pull/11710)) — thanks @RaviTharuma +- **fix(kie):** reroute `flux/kontext` off the KIE Market `createTask` flow — it is catalogued with `isMarket: true` but has no Market catalog page, so KIE rejected it with "model name not supported"; it now hits the dedicated `POST /api/v1/flux/kontext/generate` / `GET /api/v1/flux/kontext/record-info` endpoints instead (#11296). ([#11985](https://github.com/diegosouzapw/OmniRoute/pull/11985)) — thanks @hartmark +- **fix(kie):** correct 12 more KIE Market catalog ids that were sent to `createTask` unchanged but diverge from KIE's documented upstream `model` values — GPT Image 2 T2I/I2I (drops the `gpt/` prefix), GPT Image 1.5 T2I/I2I (`gpt-image/` namespace), Seedream 5.0 Lite T2I/I2I (drops the `.0`), all 4 Flux 2 variants (`flux-2/` namespace, generic variant renamed `flex`), and Wan 2.7 Image / Image Pro (dash instead of dot) — each verified individually against the literal example request published on docs.kie.ai. `#11326`'s "everything else already matches" claim was wrong a second time (#11296); `z-image/4.0-*`/`z-image/4.5-*` and `flux/kontext` remain open, documented as unresolved in `KIE_MARKET_UPSTREAM_MODEL_IDS`'s comment pending further verification. ([#11667](https://github.com/diegosouzapw/OmniRoute/pull/11667)) +- **fix(dashboard):** `useApiKeySave.handleSaveApiKey` no longer forces a full upstream `/models` catalog sync on every non-curated provider connection save — callers can now pass `skipModelSync: true` to opt out, so a workflow that only wants to add one manual model no longer floods the provider's available-models list with hundreds/thousands of synced entries. The flag is a client-side intent signal only and is stripped before the connection payload is POSTed to `/api/providers`; default behavior (full sync on save) is unchanged when the flag is omitted (#11324) ([#11664](https://github.com/diegosouzapw/OmniRoute/pull/11664)) +- **Provider connections:** keep `tokenExpiresAt` when a connection is created. The create-path allowlist omitted it, so every insert stored NULL and the dashboard token badge could read a fresh connection as expired until its first background refresh ([#11368](https://github.com/diegosouzapw/OmniRoute/pull/11368)). — thanks @ntdatt812 +- **fix(combos):** the combo builder's precision-select, global-model-search, and manual-entry flows now serialize a model step's `model` string using the provider's already-computed routing-alias prefix (e.g. `oc/`) instead of rebuilding it from the raw canonical `providerId`, fixing the no-auth "OpenCode Free" provider (`opencode`) being routed to the unrelated paid "OpenCode Zen" provider (`opencode-zen`) because `opencode` doubles as a manual routing-prefix override ([#11433](https://github.com/diegosouzapw/OmniRoute/issues/11433)). ([#11670](https://github.com/diegosouzapw/OmniRoute/pull/11670)) +- **fix(radar):** the catalog feed cache now keeps `generatedAt`, the date the feed's data was built, next to `fetchedAt`, the date this install downloaded it (#11435). The feed schema requires that date and the sync path validates it, but the cache dropped it — so a feed fetched minutes ago and one carrying weeks-old figures looked identical to everything downstream, including the dashboard's "Last fetched" line. `getRadarCatalog().meta` and `GET /api/radar/status` now report both dates, the latter as its own field rather than folded into `version` — and omitted entirely for the offers and intel caches, which keep no build date, where a `null` would read as "unknown" rather than "never stored". The dashboard still shows only the fetch time; surfacing the build date there needs a new translated label and is left to a follow-up. Rows cached before migration 163 read back as `null`: unknown stays unknown instead of borrowing the fetch time. The referrals cache has persisted the same date since migration 142. — thanks @maxmad64bis +- **fix(cli):** postinstall no longer fills `JWT_SECRET` and `API_KEY_SECRET` in the installed package's `.env` (#11436). `.env.example` ships both blank on purpose: the server restores them from its durable store, or generates and persists them there, in `ensureSecrets()`. Pre-filling them defeated that — the file lives inside the package directory, so `npm i -g` replaced it and postinstall wrote _different_ values, while `ensureSecrets()` (which only acts on an empty variable) never got to restore the real ones. Both secrets rotated silently on every update, invalidating dashboard sessions and API-key CRCs. `STORAGE_ENCRYPTION_KEY` left the same list for the same reason in #1622; its comment pointed at a function that no longer exists and now names the real provisioning path. — thanks @maxmad64bis +- **fix(cli):** the CLI now says when a loaded `.env` lives inside the installed package directory (#11437). It already announces every env file it reads, without distinguishing the ones that survive an update from the one that does not: `npm i -g` replaces the package directory wholesale, so values set there are gone at the next update, silently. The warning names the durable path to move them to, and fires only when that file actually supplied a value — a file entirely shadowed by a durable one supplied nothing. A development checkout stays silent: there the same path is stable and documented in `SETUP_GUIDE.md`. — thanks @maxmad64bis +- **fix(free-models):** the shared `isFreeModel()` predicate no longer reports catalog entries marked `freeType: "discontinued"` as free, so `hidePaidModels` can't route to Pollinations' seven premium models that now require a paid key ([#11441](https://github.com/diegosouzapw/OmniRoute/pull/11441)) — thanks @maxmad64bis +- fix(ui): let AnySearch use the normal provider-icon fallback when LobeHub has no matching icon (#11449) +- Fixed the v3.8.50 Costs and Analytics dashboards so flat-rate Claude Code usage can be shown as an explicitly requested token-price estimate without changing default billed-cost semantics. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed archived usage retention so each request is priced individually instead of pricing a day's summed tokens once, which understated archived cost whenever a day mixed cache-heavy and ordinary requests. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed the Costs dashboard so it discloses when displayed figures include flat-rate token-price estimates instead of labelling them as billed spend, using the flag the analytics API already returns; the month-end projection and the CSV/JSON exports carry the same marker, and billed-cost mode is unchanged. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- **fix(combo):** attach the same combo-diagnostics trace (`poolSize`/`attemptOrder`/`excluded`/`terminalReason`, plus `x-omniroute-combo-*` headers) to the round-robin strategy's and the nested pipeline/fusion runtime-unit loop's "Maximum combo retry limit reached" 503 that the priority-strategy path already attaches for the identical terminal condition — previously those two paths returned a bare, contextless 503 ([#11462](https://github.com/diegosouzapw/OmniRoute/issues/11462)). ([#11665](https://github.com/diegosouzapw/OmniRoute/pull/11665)) +- **fix(bun):** use native `bun:sqlite` in `bootstrap-env` and `sync-env` to avoid loading `better-sqlite3` N-API addon during Bun startup ([#11468](https://github.com/diegosouzapw/OmniRoute/pull/11468)) — thanks @TheDemonTuan +- **fix(docker-bun):** make `Dockerfile.bun` install reliable with pre-install script helpers and native `bun:sqlite` smoke check ([#11470](https://github.com/diegosouzapw/OmniRoute/pull/11470)) — thanks @TheDemonTuan +- **fix(docker-bun):** remove every vendored `better-sqlite3` native addon from the Bun runtime image so startup cannot abort before the `bun:sqlite` fallback ([#11482](https://github.com/diegosouzapw/OmniRoute/pull/11482)) — thanks @TheDemonTuan +- **fix(autoCombo):** effort/alias model variants (`gpt-5.6-sol-xhigh`, `gpt-5.6`, cursor's `claude-4.6-opus-high`) inherit their base model's task fitness instead of falling to the wildcard 0.5 ([#11492](https://github.com/diegosouzapw/OmniRoute/pull/11492)) — thanks @MumuTW +- fix(db): rate-limit repeated Arena ELO leaderboard fetch-failure warnings instead of logging one per sync attempt (#11500) ([#11989](https://github.com/diegosouzapw/OmniRoute/pull/11989)) — thanks @hartmark +- **fix(db):** dedupe the raw `[Encryption] Decryption failed...` log line emitted by the lazy-decrypt views (`createLazyRowProxy`/`createLazyConnectionView`), which power `getProviderConnections()` and were re-triggering that line on every CredentialHealth/model-sync cycle for the same corrupt or stale-key credential — a fresh Proxy over a fresh row on every cycle meant the per-proxy memoization never suppressed it, unlike the dedup `decryptConnectionFields()` already had since [#9927](https://github.com/diegosouzapw/OmniRoute/issues/9927). Now shares that dedupe tracking so the line logs at most once per credential ([#11500](https://github.com/diegosouzapw/OmniRoute/issues/11500)). ([#11678](https://github.com/diegosouzapw/OmniRoute/pull/11678)) +- **fix(intelligence):** Arena ELO sync stores a synthesized base-model row for every effort/harness variant (`claude-opus-5-max` → `claude-opus-5`, `gpt-5.6-sol-xhigh (codex-harness)` → `gpt-5.6-sol`), so bare model requests reach the synced intelligence layer instead of the static fallback table; the hand-maintained `MODEL_ALIAS_MAP` — which collapsed generations (`gpt-5.5` → `gpt-5`) — is gone ([#11506](https://github.com/diegosouzapw/OmniRoute/pull/11506)) — thanks @MumuTW +- **fix(autoCombo,sse):** catalog hygiene — the static task-fitness table no longer ranks retired models above live ones (dead rows dropped, patterns matched on segment boundaries), the 7 deprecation aliases that forwarded to retired or non-existent models now point at the vendors' published replacements, an alias is no longer rewritten when the serving provider offers the id as-is, and a new `check:model-lifecycle` gate diffs both tables against a vendor lifecycle snapshot ([#11507](https://github.com/diegosouzapw/OmniRoute/pull/11507)) — thanks @MumuTW +- **fix(sse):** stop dropping resolved `thoughtSignature` values on parallel (multi tool-call) turns sent to Gemini 3.x — the claude→gemini and openai→gemini translators previously kept the signature only on the *first* function call of a message, causing Gemini to reject subsequent calls in the same turn with HTTP 400 "Function call is missing a thought_signature"; each function call now keeps its own resolved signature ([#11510](https://github.com/diegosouzapw/OmniRoute/issues/11510)). ([#11669](https://github.com/diegosouzapw/OmniRoute/pull/11669)) +- Treat Docker publishing from a non-default release branch as an expected skip instead of a failed workflow, while preserving the guard that prevents it from overwriting the `next` channel. ([#11527](https://github.com/diegosouzapw/OmniRoute/pull/11527)) — thanks @pacocartones +- fix(search): prefer credentialed providers over duckduckgo-free fallback (#11524) — the fallback-only loop ran before the credentialed-providers loop in `executeWebSearch`, making configured providers unreachable when `duckduckgo-free` was available. ([#11565](https://github.com/diegosouzapw/OmniRoute/pull/11565)) — thanks @vsd2807 +- **fix(sse):** cap the upstream headers-wait phase for STREAMING requests to a client-realistic ceiling (110s, under Codex's own ~120s hard client-abort window) instead of the flat 10-minute `FETCH_TIMEOUT_MS` default — that default was 5x longer than the body-phase readiness watchdog's own adaptive bound, so a request whose upstream never returned any response at all (not even headers, e.g. a stalled NVIDIA target behind a tool-heavy Responses→Chat translation) kept the client connection alive on keepalives only, guaranteeing the client's own patience ran out first with an opaque 499 instead of OmniRoute detecting and failing the stall fast. Non-streaming requests are unaffected — they keep the existing flat default (`open-sse/utils/fetchStartTimeoutPolicy.ts`) (#11526) ([#11668](https://github.com/diegosouzapw/OmniRoute/pull/11668)) +- **fix(ci):** protocol-clients E2E harness boots the peer-stamped custom server (`run-next.mjs dev`) instead of the bare `next` CLI, so `LOCAL_ONLY` locality resolves from the real TCP peer stamp and `/api/mcp/audit` answers 200 (open bootstrap preserved via an `OMNIROUTE_E2E_BOOTSTRAP_MODE=open` credential clear in `run-next.mjs`, pinned to loopback `HOST`) instead of a deterministic 403 (#11535) ([#11549](https://github.com/diegosouzapw/OmniRoute/pull/11549)) — thanks @jonlwheat2-gif +- **fix(docs):** the Auto-Combo engine is described with the number of factors it actually declares. Four different counts were in circulation — 6, 9, 13 and 14 across `AGENTS.md`, four guides and six code comments — while `DEFAULT_WEIGHTS` declares 15. `check:docs-counts` now reads that number from the source, so adding a factor without updating the prose fails CI ([#11545](https://github.com/diegosouzapw/OmniRoute/pull/11545)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **fix(quota-share):** in-flight leases are per request, so an aborted request's slot ages out instead of being kept alive by later traffic on the same connection ([#11547](https://github.com/diegosouzapw/OmniRoute/pull/11547)) — thanks @abhisheksharma2411 +- The free-tier summary route now serves the refreshed Radar catalog when the feed is active — the same numbers the dashboard shows — and states which catalog answered plus its real build date, instead of always reporting release-frozen figures with a stale curation date. The supporter-key live feed stays reserved to authenticated callers of the instance. ([#11550](https://github.com/diegosouzapw/OmniRoute/pull/11550)) — thanks @maxmad64bis +- **sse:** fix LiveWS/embed-WS servers crashing at startup under the Node/tsx runtime — `liveServer.ts`, `embedWsProxy.ts` and `apiBridgeServer.ts` imported `@/shared/utils/httpClientAbortGuard` without the `.mjs` extension, so the client-abort crash guard added by [#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556) was unreachable and every dependent test failed with `ERR_MODULE_NOT_FOUND` ([#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556)). ([#11671](https://github.com/diegosouzapw/OmniRoute/pull/11671)) — thanks @jonlwheat2-gif / @yxyxy +- fix(chat): shed any of the matched provider-node's routing identifiers (public prefix or internal id) before model lookup, so composites like `//` no longer reach upstream verbatim (#11557) — thanks @maxmad64bis +- **fix(cache):** a semantic-cache entry now expires on its TTL instead of surviving until the next UTC midnight, and `dbEntries` stops counting expired rows ([#11573](https://github.com/diegosouzapw/OmniRoute/pull/11573)) — thanks @ntdatt812 +- **fix(api):** `/v1/models` schedules its stale-while-revalidate rebuild through Next's `after()` again, so a stale catalog reaches the client before the rebuild blocks the event loop ([#11574](https://github.com/diegosouzapw/OmniRoute/pull/11574)) — thanks @ntdatt812 +- **fix(check):** the `known-symbols` executor conformance gate awaits the now-async `getExecutor()`, so it stops reporting all 142 registered aliases as dead symbols and can detect a lazy import that fails ([#11582](https://github.com/diegosouzapw/OmniRoute/pull/11582)) — thanks @ntdatt812 +- **fix(pack):** register `bin/cli/utils/volatileEnvPath.mjs` in `PACK_ARTIFACT_REQUIRED_PATHS`, so the prune can no longer silently drop a module the published CLI imports on every boot ([#11588](https://github.com/diegosouzapw/OmniRoute/pull/11588)) — thanks @ntdatt812 +- **i18n(vi):** translate the five reliability-column strings on the free-provider rankings page, which rendered as `__MISSING__:` markers in the Vietnamese UI ([#11589](https://github.com/diegosouzapw/OmniRoute/pull/11589)) — thanks @ntdatt812 +- **resilience:** restore the expired-connection retry-budget probe in the token-health sweep — the `!isGitHubAccessTokenOnlyConnection` carve-out reintroduced by #11608 contradicted the boundary pinned by #11592, so a GitHub connection parked at `expired` with retry budget remaining was never probed and could never self-heal ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)). ([#11672](https://github.com/diegosouzapw/OmniRoute/pull/11672)) — thanks @jonlwheat2-gif / @ntdatt812 +- **fix(dashboard):** Provider Stats now displays combo success rates on their native 0–100 percentage scale instead of multiplying them twice ([#11595](https://github.com/diegosouzapw/OmniRoute/pull/11595)) — thanks @pacocartones +- **fix(providers):** route DuckDuckGo Web Chat through the verified `duck.ai` endpoint, refresh its free model catalog, and avoid forwarding unsolved anti-abuse challenges ([#11597](https://github.com/diegosouzapw/OmniRoute/pull/11597)) — thanks @solstxce +- **fix(ui):** Console log Refresh and Copy controls now expose localized accessible names, keep copy actions visible on keyboard focus, and announce copy completion safely ([#11599](https://github.com/diegosouzapw/OmniRoute/pull/11599)) — thanks @pacocartones +- Dropped the stale-`.eslintcache` `restore-keys` fallback from both "Restore ESLint file cache" steps in `ci.yml`, so the blocking `Lint` job can no longer be served per-file verdicts computed under a different lint config, suppressions file or lockfile. `quality.yml` had already dropped it in #11963; `ci.yml` — the workflow that actually gates PRs — had not (#11600). ([#11996](https://github.com/diegosouzapw/OmniRoute/pull/11996)) +- fix(ci): pass `--pass-on-unpruned-suppressions` in `run-eslint-json.mjs` so the CI Lint job no longer fails when a suppression is merely orphaned by a genuine fix, mirroring the identical fix already in `validate-release-green.mjs` (#11600) ([#11983](https://github.com/diegosouzapw/OmniRoute/pull/11983)) — thanks @hartmark +- fix(handoff): enforce provider allowlist for universal handoff (#11602) — universal handoff now skips summarization when the selected summary model's provider is not included in the configured provider allowlist. ([#11619](https://github.com/diegosouzapw/OmniRoute/pull/11619)) — thanks @vsd2807 +- **fix(dashboard):** Show a stable error state when Search Analytics returns an HTTP error or malformed data ([#11603](https://github.com/diegosouzapw/OmniRoute/pull/11603)) — thanks @pacocartones +- **test(gamification):** pin the aggregate profile level to the XP-derived semantics of #11604 — `getAggregateXp()` now derives `currentLevel` from the summed XP (`calculateLevel(sum)`), not `MAX(stored current_level)`, and the #3484 fixture levels are aligned with the XP curve ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)). ([#11676](https://github.com/diegosouzapw/OmniRoute/pull/11676)) — thanks @jonlwheat2-gif / @pacocartones +- **fix(dashboard):** Keep the Profile level and progress aligned with aggregate XP, including bounded handling for invalid totals ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)) — thanks @pacocartones +- **fix(dashboard):** Prevent locked hidden badges from revealing their icon or opening private badge details before they are earned ([#11605](https://github.com/diegosouzapw/OmniRoute/pull/11605)) — thanks @pacocartones +- **fix(dashboard):** Restore keyboard focus after shared modals close and cancel delayed autofocus during cleanup ([#11607](https://github.com/diegosouzapw/OmniRoute/pull/11607)) — thanks @pacocartones +- **fix(dashboard):** Enable Enter and Space activation for clickable data-table rows without hijacking nested controls ([#11610](https://github.com/diegosouzapw/OmniRoute/pull/11610)) — thanks @pacocartones +- **fix(autoCombo,sse):** vendor-retired catalog ids are dropped from the auto-combo candidate pool and no longer win on leftover `arena_elo` / `user_override` rows; `getModelLifecycleDecision` consults `model-lifecycle.json` (prefix-stripped) so aggregator traffic is not `untracked` for ids the snapshot already knows ([#11625](https://github.com/diegosouzapw/OmniRoute/issues/11625)) ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)) — thanks @MumuTW +- **test(chatcore):** move Codex/Claude combo fixtures off models the lifecycle guard now rejects — `gpt-5.1-codex`/`gpt-5-codex` are vendor-retired (snapshot, #11626) and `claude-3-5-sonnet-20241022` is shut down, so native-passthrough and combo-fallback tests switched to `gpt-5.6-sol` and `claude-sonnet-4.6` ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)). ([#11675](https://github.com/diegosouzapw/OmniRoute/pull/11675)) — thanks @jonlwheat2-gif / @MumuTW +- fix(opencode-plugin): stop a free-tier budget that rounds up across a magnitude from rendering as `1000K`/`1000M` in the model picker — `fmtTokens` chose its unit from the raw token count and then rounded with `toFixed(1)`, so 999,950–999,999 printed as `1000K` rather than `1M` and just under a billion printed as `1000M` rather than `1B` ([#11684](https://github.com/diegosouzapw/OmniRoute/pull/11684)) — thanks @abhisheksharma2411 +- Cap prose-derived Antigravity quota resets at the configured model cooldown maximum while preserving authoritative Retry-After headers and Google RetryInfo hints. (#11695) Thanks @pacocartones. ([#11823](https://github.com/diegosouzapw/OmniRoute/pull/11823)) +- Compare `_rebaseline_*` notes structurally in the ratchet-bank verifier so an object-valued note no longer reports itself as rewritten on an unmodified tree — the nightly `bank-ratchet-shrinks` lane had been aborting on every run since 2026-08-11, leaving 41,672 lines of earned file-size shrinks unbanked (#11702). ([#11892](https://github.com/diegosouzapw/OmniRoute/pull/11892)) — thanks @MumuTW +- **fix(build):** `prepublish.ts` bundles the ChatGPT Web (Codex) MCP bridge through `runBuildTool()` instead of spawning `npx.cmd` raw, fixing the build crash on Node ≥ 20/Windows where `.cmd` shims cannot be spawned without a shell (EINVAL) ([#11704](https://github.com/diegosouzapw/OmniRoute/issues/11704)) ([#11706](https://github.com/diegosouzapw/OmniRoute/pull/11706)) — thanks @jonlwheat2-gif +- fix(codex): keep `parallel_tool_calls:false` on the translated Codex Responses Lite path (#11707) ([#11984](https://github.com/diegosouzapw/OmniRoute/pull/11984)) — thanks @hartmark +- **fix(tests):** the new Gemini Web Images retirement test (#11708) called the now-async `getExecutor()` synchronously, throwing `TypeError: getExecutor(...).getProvider is not a function` instead of asserting Gemini Web chat stays available. Awaited to match the R0.3 executor-registry refactor (#11220). ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(search):** Allow REST and MCP web-fetch requests to select the advertised `anysearch-search` provider explicitly ([#11716](https://github.com/diegosouzapw/OmniRoute/pull/11716)) — thanks @pacocartones +- **fix(providers):** Keep overnight peak-hour protection active after midnight according to the window's configured start day ([#11718](https://github.com/diegosouzapw/OmniRoute/pull/11718)) — thanks @pacocartones +- **fix(providers):** correct all five g4f.space gateway descriptions to document proof-of-work credits or member API keys, variable limits, and the remote prompt boundary instead of promising free keyless access at five requests per minute ([#11738](https://github.com/diegosouzapw/OmniRoute/pull/11738)). +- **fix(db):** `model_capabilities` is created by a migration instead of lazily on the first models.dev sync, so a clean install and an upgraded install converge on the same schema regardless of which features have run ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(ci):** `check:install-upgrade` now fails on an `npm` install truncated by ENOSPC (npm reports it as a warning and still exits 0), authenticates its health probe so the version assertion works against the hardened health payload, frees the clean-install tree before the upgrade phase, and no longer reports a schema divergence computed from a boot that never served ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(routing):** harden session-affinity key derivation against oversized explicit IDs and empty structural-payload collisions, while recognizing Gemini and common text request shapes ([#11746](https://github.com/diegosouzapw/OmniRoute/pull/11746)) — thanks @MumuTW +- **fix(packages/browser-pool):** regenerate `package-lock.json` so the `packages/browser-pool` workspace's locked `playwright`/`@types/node` (and transitives) match its `package.json` specs, fixing cache-only/offline installs (`npm ci --offline`, Nix `buildNpmPackage`) that previously failed with `ENOTCACHED` ([#11747](https://github.com/diegosouzapw/OmniRoute/issues/11747)) — thanks @benjaminkitt ([#11784](https://github.com/diegosouzapw/OmniRoute/pull/11784)) — thanks @geekyNads +- **fix(assets):** Stop distributing 79 provider and CLI logos without verified source/license provenance, keep every provider/tool available through the internal generic icon, and prevent those IDs from falling through to an external icon CDN. ([#11750](https://github.com/diegosouzapw/OmniRoute/pull/11750)) +- **fix(providers):** retire the common `chatgpt-web` / `cgpt-web` provider because its pre-key and proof-of-work provenance could not be cleared ([#11754](https://github.com/diegosouzapw/OmniRoute/pull/11754)) — explicit chat and image requests, slash-prefixed models, persisted aliases, proxy overrides, and combo targets now fail closed with a sanitized HTTP `410` / `PROVIDER_RETIRED` before upstream dispatch. Migration 163 tombstones matching connections and invalidates their active leases without deleting history or widening API-key allowlists. The independent `chatgpt-web-codex` / `cgpt-codex` provider and its MIT-noticed vendored implementation remain available. +- **fix(usage):** quota and usage refresh no longer 409 when an exclusive lease reserves the connection ([#11758](https://github.com/diegosouzapw/OmniRoute/pull/11758)) — thanks @TheDemonTuan +- Keep the embedding registry's vector width and `embedding` type on models when a synced model exists for the same id, so `/v1/models` no longer reports registry-described embedding models widthless or untyped (#11761) — thanks @rahilmavani +- Correct `google/gemini-embedding-001` on the OpenRouter route to 3072 dimensions, the width it returns when `dimensions` is not sent (#11761) — thanks @rahilmavani +- **fix(vision-bridge):** Restrict automatic vision routing and fallbacks to models available in authoritative live provider catalogs, including aliases and effort variants ([#11767](https://github.com/diegosouzapw/OmniRoute/issues/11767)) — thanks @pacocartones ([#11813](https://github.com/diegosouzapw/OmniRoute/pull/11813)) +- **fix(providers):** reclassify Cerebras as a one-time $5 signup credit (payment method required, 30-day validity), not a recurring no-card 1M tokens/day trial ([#11773](https://github.com/diegosouzapw/OmniRoute/issues/11773)) ([#12591](https://github.com/diegosouzapw/OmniRoute/pull/12591)) — thanks @HouMinXi +- fix(cli): stop prepublish from re-rebuilding the already-built ESM-only opencode-plugin dist (#11787) ([#11990](https://github.com/diegosouzapw/OmniRoute/pull/11990)) — thanks @hartmark +- **fix(db):** Fresh installs no longer log a non-fatal `no such table: compression_run_telemetry` warning when retention cleanup runs before the lazily-created telemetry table exists ([#11802](https://github.com/diegosouzapw/OmniRoute/pull/11802)) — thanks @RaviTharuma +- fix(sse): set X-OmniRoute-Selected-Connection-Id on successful combo dispatches so downstream consumers stop falling back to an empty connection id (#11810) ([#11986](https://github.com/diegosouzapw/OmniRoute/pull/11986)) — thanks @hartmark +- **fix(services):** embedded CLIProxyAPI lifecycle checks now use public `/healthz`, while model discovery uses the configured dedicated data-plane API key instead of the management password ([#11811](https://github.com/diegosouzapw/OmniRoute/pull/11811)) — thanks @RaviTharuma +- Fix persisted model steps returning an error in the playground route simulator, and warn when structural steps cannot be simulated. ([#11862](https://github.com/diegosouzapw/OmniRoute/pull/11862)) — thanks @pacocartones +- **fix(providers):** Antigravity's dynamic mitmAlias table no longer routes `gemini-3.7-flash-{high,medium,low}` to a literal tier-suffixed upstream id just because one connected account's own discovery listed it directly — those display ids always resolve through the safe `gemini-3.7-flash-tiered` static alias, so one account's Google-provisioned access no longer 404s every sibling account of the provider ([#11824](https://github.com/diegosouzapw/OmniRoute/issues/11824), [#11651](https://github.com/diegosouzapw/OmniRoute/issues/11651)) ([#11988](https://github.com/diegosouzapw/OmniRoute/pull/11988)) — thanks @hartmark +- **fix(dashboard):** Expose Profile loading, errors, page structure, and XP progress to assistive technologies ([#11838](https://github.com/diegosouzapw/OmniRoute/pull/11838)) — thanks @pacocartones +- **fix(socks):** `Agent.connectTimeout` now bounds both SOCKS handshake and TLS connect, and `family === null` no longer falls back to `fetch-socks` ([#11842](https://github.com/diegosouzapw/OmniRoute/pull/11842)) — thanks @maxmad64bis +- **perf(compression):** OOM mitigations for large payload hashing, memoization, and token estimation ([#11844](https://github.com/diegosouzapw/OmniRoute/pull/11844) — thanks @AndrianBalanescu) +- Excluded Next.js Node File Trace manifests (`*.nft.json`) from the published npm tarball. They are build-time metadata and are never read while serving, but had grown to 668.7 MB — 61% of the package — which pushed the upload past the registry limit and made `npm publish` fail with `413 Payload Too Large`. ([#11864](https://github.com/diegosouzapw/OmniRoute/pull/11864)) +- **fix(cli):** keep the global OmniRoute server `--base-url` separate from provider-node `--endpoint` values in add, update, and validate commands ([#11860](https://github.com/diegosouzapw/OmniRoute/pull/11860)) — thanks @pacocartones +- **fix(config):** Nous Research's `Hermes-4-405B` model now displays as "Hermes 4 405B (Nous Research)" in both the provider registry and the free-model catalog, instead of the mislabelled "Hermes 4 7B" ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#11993](https://github.com/diegosouzapw/OmniRoute/pull/11993)) +- **fix(provider/nous):** inject required user= tag into Nous Research inference requests to resolve upstream 400 "missing tags" error ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#12044](https://github.com/diegosouzapw/OmniRoute/pull/12044)) +- **fix(build):** `npm run build` now fails in one second with a named package and a copy-pasteable fix when npm silently drops an externalised optional native dependency, instead of dying four minutes in with `Module not found: Can't resolve 'better-sqlite3'` ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- **fix(install):** `postinstall` no longer throws `ReferenceError: isAndroid is not defined` — failing the whole `npm install` — when the `better-sqlite3` rebuild fallback times out; the manual-fix guidance is reachable again ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- Fixed the Alibaba free-tier allowlist test that went red on its own once the shipped catalog's `validUntil` (2026-08-27) passed, leaving every PR and `main` with a failing `Unit Tests (1/8)`. The test now builds its own packs with dates it controls, and covers the expired-pack fallback that production has actually been serving. ([#11867](https://github.com/diegosouzapw/OmniRoute/pull/11867)) +- Split the npm registry upload into its own GitHub-hosted job. npm refuses `--provenance` from a self-hosted runner (`422 ... Only "github-hosted" runners are supported`), which blocked the v3.8.50 publish; the heavy verification cannot move to a hosted runner, so it now hands the proven tarball over instead. ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(ui):** the shared `DataTable` loading state no longer reads its decorative ⏳ glyph out to assistive technology, and now carries the same `role="status"` / `aria-live="polite"` / `aria-busy="true"` semantics as `PageLoading` ([#11869](https://github.com/diegosouzapw/OmniRoute/pull/11869)) — thanks @pacocartones +- **fix(api):** Bound the `timeout` accepted by `POST /api/providers/[id]/login` to the same 15s–600s window the Adobe Firefly and Conol browser-login paths already enforce, so a request can no longer pin a headful browser and the single active-login slot with an arbitrarily large poll budget. ([#11871](https://github.com/diegosouzapw/OmniRoute/pull/11871)) — thanks @pacocartones +- **fix(api):** Stop the tunnel and MITM routes from returning raw child-process error text, which disclosed host paths, binary install locations and Tailscale `tskey-*` credentials in a body some of these routes serve to non-loopback callers, and return a real 400 instead of a framework 500 when a tunnel request body fails validation. ([#11872](https://github.com/diegosouzapw/OmniRoute/pull/11872)) — thanks @pacocartones +- **fix(open-sse):** stream TTFT/ITL are sampled from a monotonic clock (`performance.now`) instead of `Date.now`, so an NTP correction or wall-clock jump can no longer inflate a genuine TTFT or produce a negative one that the `ttft >= 0` guard silently drops — protecting the router's OTel/EWMA/`usage_history`/speed-ranking signals ([#11873](https://github.com/diegosouzapw/OmniRoute/pull/11873)) — thanks @pacocartones +- **fix(providers):** xAI `reasoning_effort: "xhigh"` now reaches grok-4.6+ instead of being silently clamped to `"high"` ([#11879](https://github.com/diegosouzapw/OmniRoute/pull/11879)) — thanks @NoxzRCW +- **fix(dashboard):** the providers page no longer crashes into the error boundary when a provider id collides with an `Object.prototype` member (`constructor`, `__proto__`); icon lookups are own-property guarded ([#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)) — thanks @NoxzRCW +- **fix(skills):** injected skill tools declared in shorthand (`{"content": "string"}`) now forward valid JSON Schema, unblocking providers that validate tool schemas strictly such as Zhipu GLM on the Console Go tier ([#11881](https://github.com/diegosouzapw/OmniRoute/pull/11881)) — thanks @NoxzRCW +- **fix(api):** `POST /api/playground/simulate-route` now surfaces `combo-ref` and `provider-wildcard` persisted combo steps with a specific warning (naming the referenced combo, or the unresolved `provider/model` wildcard) instead of folding them into a generic "unsupported step" count; a `provider-wildcard` step is also now included as an unresolved target so the operator can see it is in the route (ported from [#11882](https://github.com/diegosouzapw/OmniRoute/pull/11882) — thanks @NoxzRCW). ([#11930](https://github.com/diegosouzapw/OmniRoute/pull/11930)) +- **fix(translator):** the streaming OpenAI→Claude translator keeps upstream usage, including prompt-cache tokens, when it arrives on a trailing `choices: []` chunk (Fireworks and any upstream using `stream_options.include_usage`) ([#11883](https://github.com/diegosouzapw/OmniRoute/pull/11883)) — thanks @NoxzRCW +- **fix(cli):** `omniroute update --apply` now tells you explicitly whether a running server was detected and, if so, that you must run `omniroute restart` to apply the update — it never restarted anything and previously implied the update was already live once files were installed. The dashboard's npm-mode Update flow (`/api/system/version`) now tries OmniRoute's own PID-file-managed supervisor before falling back to pm2, and reports an honest "restart required" step instead of a silent pm2-only "skipped" that read like a completed update. The server-side latest-version lookup backing the dashboard's update banner also gained `--prefer-online`, closing the same stale-npm-cache class already fixed in the CLI's own copy for #4376 ([#11885](https://github.com/diegosouzapw/OmniRoute/issues/11885)). ([#12005](https://github.com/diegosouzapw/OmniRoute/pull/12005)) +- **fix(resilience):** clear persisted LKGP pins when a target suffers connection/provider exhaustion or is skipped before dispatch due to cooldown/exhaustion/unavailability, preventing subsequent requests from repeatedly prioritizing known-dead providers ([#11911](https://github.com/diegosouzapw/OmniRoute/issues/11911)). ([#12013](https://github.com/diegosouzapw/OmniRoute/pull/12013)) — thanks @HouMinXi +- **fix(sse):** the OpenAI→Claude stream translator now defers the terminal `message_delta`/`message_stop` emission until the real usage block has arrived (or a genuine end-of-stream flush forces it) instead of emitting it immediately on `finish_reason` — previously, when the trailing usage-only chunk (`{"choices":[],"usage":{...}}`) arrived *after* the `finish_reason` chunk (the normal order for Fireworks/vLLM/Together and other `stream_options.include_usage` upstreams), the client-visible `message_delta` still carried stale/zero usage even though `state.usage` was internally corrected too late to matter (ported from [#11915](https://github.com/diegosouzapw/OmniRoute/pull/11915) — thanks @HouMinXi). ([#11933](https://github.com/diegosouzapw/OmniRoute/pull/11933)) +- **fix(sse):** `stripStore()` now forces `store=false` for stateless OpenAI-compatible Responses-API targets (unless the connection has explicitly opted in via `providerSpecificData.openaiStoreEnabled`), instead of only handling the `openai`/`agentrouter` cases — a client-supplied `store` value previously passed through untouched to backends that don't actually persist responses server-side ([#11916](https://github.com/diegosouzapw/OmniRoute/pull/11916) — thanks @HouMinXi). +- **fix(models):** custom provider-node models (synced, custom, and alias-backed) now appear under their configured prefix in the unified catalog when the operator's model-id prefix mode is canonical, instead of being dropped whenever alias-inclusion was otherwise disabled ([#11918](https://github.com/diegosouzapw/OmniRoute/pull/11918) — thanks @HouMinXi). +- **fix(models):** the unified model catalog now suppresses stale static registry models (including effort-tier variants) for any provider whose active connection has an authoritative live synced catalog, not only providers already using exclusive-synced-listing — a connection with `providerUsesAuthoritativeLiveCatalog` previously kept serving both the live-synced models and the stale static rows side by side ([#11919](https://github.com/diegosouzapw/OmniRoute/pull/11919) — thanks @HouMinXi). +- **fix(ui):** `ProviderIcon`'s three remaining unguarded lookups (`PROVIDER_ICON_ALIASES`, `LOCAL_SVG_ALIASES`, `THEMED_SVGS`) now use `Object.hasOwn()` own-property checks — a provider id such as `constructor` or `__proto__` previously resolved through the prototype chain instead of falling through to the unknown-provider CDN fallback (`getLobeProviderIcon()` itself was already guarded by [#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)); `ProviderPageHeader`'s `color` field is also now optional, matching the rest of the component's defensive typing (ported from [#11920](https://github.com/diegosouzapw/OmniRoute/pull/11920) — thanks @HouMinXi). ([#11935](https://github.com/diegosouzapw/OmniRoute/pull/11935)) — thanks @NoxzRCW +- fix(ollama): preserve multi-byte UTF-8 content split across stream chunks in the Ollama NDJSON transform, which previously corrupted CJK/emoji into U+FFFD (#11921) — thanks @pacocartones +- **fix(providers):** OrcaRouter chat requests now target `/v1/chat/completions` instead of the bare `/v1` API root, fixing the upstream `404 Invalid URL (POST /v1)` ([#11923](https://github.com/diegosouzapw/OmniRoute/pull/11923)). — thanks @echel0nn +- **fix(plugins):** deliver the `onStreamComplete` event to disk-installed plugins. The event shipped in v3.8.50 (#9669) was emitted internally but had no plugin-facing wiring, so no plugin could ever subscribe: the manifest schema silently dropped `hooks.onStreamComplete`, and the loader/manager only knew the seven legacy hooks. `onStreamComplete` is now a declarable manifest hook, wired through the loader and registered by the manager like the other hooks, and its payload carries a `requestId` so consumers can correlate the stream-completion event with the originating request ([#11934](https://github.com/diegosouzapw/OmniRoute/pull/11934)) — thanks @amaleta / @pacocartones +- **fix(db):** the Qdrant embedding-model dropdown now lists local/self-hosted providers (Ollama, LM Studio, vLLM, etc.) — an active connection is treated as "configured" when the provider allows an optional API key, not only when it has a real key or OAuth, so a running local embedding provider is no longer hidden from the picker ([#11949](https://github.com/diegosouzapw/OmniRoute/issues/11949)) ([#11995](https://github.com/diegosouzapw/OmniRoute/pull/11995)) +- **fix(providers):** Vertex AI Anthropic partner-model discovery now calls the Model Garden `v1beta1` publisher list (`/v1beta1/publishers/anthropic/models`, global) and parses its `publisherModels` envelope, so Claude models auto-synced from Vertex populate the active live catalog and route at request time instead of returning `Model '' is not available in the active live catalog` ([#11991](https://github.com/diegosouzapw/OmniRoute/issues/11991)) — thanks @fabioluissilva ([#11998](https://github.com/diegosouzapw/OmniRoute/pull/11998)) +- **fix(cli):** support `--base-url` alongside `--endpoint` in `omniroute nodes add`, `update`, and `validate` subcommands to prevent global `--base-url` shadowing issues ([#11999](https://github.com/diegosouzapw/OmniRoute/issues/11999)). ([#12033](https://github.com/diegosouzapw/OmniRoute/pull/12033)) — thanks @HouMinXi +- **fix(providers):** `cloudflare-ai` no longer refuses image content parts for every Workers AI model ([#12002](https://github.com/diegosouzapw/OmniRoute/pull/12002)) — the plain-string `content` requirement behind #2539 is carried by the _model_ schema, not by the `/ai/v1/chat/completions` endpoint (measured: an all-text part array returns 200 on `@cf/mistralai/mistral-small-3.1-24b-instruct`, `@cf/meta/llama-4-scout-17b-16e-instruct` and `@cf/meta/llama-3.3-70b-instruct-fp8-fast`, and 400 on the text-only `@cf/qwen/qwen2.5-coder-32b-instruct`). `transformRequest()` flattened every array and threw on the first non-text part (#6390), so image input was refused for vision-capable Cloudflare models that accept it. All-text arrays are still flattened — the one shape every model accepts — while an array carrying a non-text part is passed through untouched, so the attachment is still never silently dropped. Regression guards: `tests/unit/cloudflare-ai-image-parts-6390.test.ts`. — thanks @davidlinfr +- **fix(resilience):** decouple the limiter-managed execution backstop from the queue-wait budget — new `requestQueue.executionMaxWaitMs` (env `RATE_LIMIT_EXECUTION_MAX_WAIT_MS`, default 600000 = 10 min) now feeds Bottleneck's post-dispatch `expiration`, while `requestQueue.maxWaitMs` keeps its documented queue-wait semantics. Previously the queue-wait budget doubled as the execution expiration, so legitimate long-running calls on non-incremental gateways (whole generation buffered before the first upstream byte, e.g. Console Go / Command Code tiers serving GLM models) were killed mid-flight at the queue budget with a false 504 `RATE_LIMIT_EXECUTION_TIMEOUT` — the local limiter undercut the provider-aware upstream fetch-start timeouts. The surfaced 504 message now names `requestQueue.executionMaxWaitMs`; the error keeps the #4165 guarantees (disclaims an upstream timeout, preserves the Bottleneck error as `cause`, branded code + trusted provenance, classified request-scoped so combo falls back). A real queue-wait bound (the `Promise.race` around `limiter.schedule()` sketched in #9533) remains future work. (#12025) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **Call logs:** keep the `error` field when an artifact exceeds the storage cap, instead of replacing it with the omission marker. The error is the only field that says *why* a request failed and is typically ~90 bytes next to the multi-hundred-KB bodies that trip the cap, so dropping it left a size-limited row undiagnosable — a provider outage, a local timeout and an upstream 400 all rendered identically. It is now preserved at every fallback stage, truncated to 4KB if it is itself large ([#12026](https://github.com/diegosouzapw/OmniRoute/issues/12026)). ([#12095](https://github.com/diegosouzapw/OmniRoute/pull/12095)) — thanks @ntdat812 +- **fix(diagnostics):** preserve the error field (truncated to 4KB with a `[truncated: …]` suffix) in every call-log artifact size-limit fallback stage. Previously the minimal fallback replaced the error with `[omitted: call log artifact size limit exceeded]`, so an oversized artifact row showed nothing about WHY the request failed — e.g. 91 of 847 opencode-go 504 rows on one production instance were undiagnosable from the dashboard. Oversized request/response bodies are still omitted exactly as before; the error cap is independent of the payload sizes that tripped the fallback. (#12026) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **fix(sse):** OpenAI Responses clients that declare the native `web_search` tool now receive a spec-shaped `web_search_call` output item with `action.sources` alongside the preserved function-call round-trip, so search results executed through OmniRoute's own search backend are consumable by standard Responses clients (Codex, pi-web-access, …). ([#12031](https://github.com/diegosouzapw/OmniRoute/pull/12031)) — thanks @watchingdogs +- **fix(cli):** use in-thread alias resolver hooks on modern runtimes to avoid deprecation noise and improve Node.js forward compatibility ([#12073](https://github.com/diegosouzapw/OmniRoute/issues/12073)). ([#12083](https://github.com/diegosouzapw/OmniRoute/pull/12083)) — thanks @xiaoyaner0201 +- **fix(combo):** an operator-set **Agent Features → Context length** on a combo is now honored at request time. The value was persisted and advertised through `/v1/models`, but `resolveComboContextLimit()` never consulted it — so a multi-target combo whose members carry no per-model window fell through to the provider's generic `defaultContextLength` (openrouter 128000, command-code 200000) and rejected large requests with `Input exceeds context window … limit 128000` despite the combo being explicitly sized much larger. An identical single-target combo worked, because it collapses to its concrete target before the guard runs. Invalid values (0/negative/NaN/Infinity) are ignored, so the existing target → combo-min → fallback order is unchanged. ([#12090](https://github.com/diegosouzapw/OmniRoute/pull/12090)) — thanks @adivekar-utexas +- **fix(sse):** passthrough streams now estimate usage on finish when upstream closes without usage even with `stream_options.include_usage` — avoids `0 tokens / 0%` for providers that stay silent (and correctly handles trailing empty-choices usage) ([#12151](https://github.com/diegosouzapw/OmniRoute/pull/12151)) — thanks @maxmad64bis +- **fix(combos):** clearing an agent feature in the combos editor now persists — unchecking context cache protection, or emptying the system message or tool filter, sends an explicit `null` instead of dropping the field from the `PUT` body, which the update merge read as "leave unchanged" ([#12177](https://github.com/diegosouzapw/OmniRoute/pull/12177)) — thanks @foreveryh / @ntdatt812 +- **fix(memory):** self-hosted embedding endpoints now vectorize — the vector width is measured from the first embedding that comes back instead of being read from a registry that cannot describe them, so `vec_memories` is created and memories stop piling up unvectorized behind a green health check ([#12180](https://github.com/diegosouzapw/OmniRoute/pull/12180)) — thanks @kanade-hoshino / @ntdatt812 +- **fix(translator):** the leading `system` message now reaches Responses-API upstreams when its `content` is a content-part array — it was read as `typeof content === "string" ? content : ""`, so a prompt-caching client (Anthropic `cache_control`, the shape LiteLLM and the Anthropic SDK emit) had its entire system prompt replaced by an empty `instructions`. The request was still accepted with a normal `prompt_tokens` count, so the model answered with no instructions and nothing in the response said they were missing. Mid-conversation system turns already handled the array shape ([#7056](https://github.com/diegosouzapw/OmniRoute/pull/7056)); only the first one did not ([#12206](https://github.com/diegosouzapw/OmniRoute/issues/12206)). Regression guard: `tests/unit/translator-openai-responses-system-content-parts.test.ts`. ([#12207](https://github.com/diegosouzapw/OmniRoute/pull/12207)) — thanks @Gorillaz322 +- **fix(free-tier):** `/api/free-tier/summary` no longer computes its totals from a Radar feed built before the catalog the running release ships. When the cached feed is older — or carries no build date at all — the route answers from the shipped catalog, resolved through the operator's local model state so disabled and tombstoned models stay out of the numbers ([#12215](https://github.com/diegosouzapw/OmniRoute/pull/12215)). — thanks @maxmad64bis +- **fix(oauth):** Keep a Claude personal workspace and a Team organization as separate connections — they share the same email and `accountUUID`, so the email-only OAuth dedup let the second login overwrite the first account's tokens; `organizationUUID` now disambiguates them, the way `workspaceId` does for Codex ([#12222](https://github.com/diegosouzapw/OmniRoute/pull/12222)) — thanks @davidebaraldo +- **fix(resilience):** a 402 on a single paid model of a passthrough/gateway provider (e.g. `kilo-gateway`, `ollama-cloud`) no longer terminalizes the whole connection with a never-auto-recovered `credits_exhausted` status — only the paid model is locked out, so free models on the same key keep serving. 402 variant of [#3027](https://github.com/diegosouzapw/OmniRoute/issues/3027). Single-credential providers are unaffected — a 402 there is still treated as the key being genuinely out of credit ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239) / [#10616](https://github.com/diegosouzapw/OmniRoute/issues/10616)) ([#12242](https://github.com/diegosouzapw/OmniRoute/issues/12242)) — thanks @brick30llc-ctrl ([#12266](https://github.com/diegosouzapw/OmniRoute/pull/12266)) — thanks @geekyNads +- **fix(sse):** trust `finish_reason: "length"`/`"max_tokens"` over the reasoning-consumed-token ratio in response quality validation, so a reasoning model truncated below the old 90% threshold correctly fails and retries instead of returning empty content as a silent "success" ([#12262](https://github.com/diegosouzapw/OmniRoute/pull/12262)) — thanks @brick30llc-ctrl +- **fix(settings):** `PUT /api/settings/cache-config` now persists `alwaysPreserveClientCache` to the flat general settings the runtime cache-control policy actually reads; previously the value landed in the databaseSettings "cache" section and was silently ignored, so the endpoint had no effect on `cache_control` passthrough ([#12304](https://github.com/diegosouzapw/OmniRoute/pull/12304)) — thanks @davidebaraldo +- **fix(grok-cli):** treat omitted SuperGrokPro `creditUsagePercent` as 0% used so Provider Limits still renders a weekly bar (proto3 zero-elision) ([#12312](https://github.com/diegosouzapw/OmniRoute/pull/12312)) — thanks @HouMinXi +- **fix(combo):** Expose the two Auto-Combo scoring factors nobody could set — the combo validation schema and the dashboard weight sliders both declared 13 of the scorer's 15 factors, so `connectionDensity` (spreads load across a provider's connections) and `quality` were dropped on save and offered nowhere. The sliders also shipped their own default table that differed from the engine's on every non-zero factor and summed to 1.05, so the percentages shown next to them added up to 105%. Both lists now match `DEFAULT_WEIGHTS`, and a test keeps them there. Note that a combo whose stored `weights` omitted the two keys was effectively running with them at zero and the other thirteen renormalized upward; it now runs with the engine's intended distribution, so its routing does shift ([#12314](https://github.com/diegosouzapw/OmniRoute/pull/12314)) — thanks @maxmad64bis +- **fix(docs):** The free-tier reference no longer says its numbers come "confidence tagged per row" — no catalog entry carries a confidence tag and the API serves none, so every figure on that page is an estimate of the same, unstated quality. The page now states what an entry does vouch for: an independently documented hard stop (set by hand with the source in a comment, never defaulted to `true`) and a prompt-training disclosure, both with live counts the `check:docs-counts` gate keeps honest ([#12318](https://github.com/diegosouzapw/OmniRoute/pull/12318)) — thanks @maxmad64bis +- **fix(usage):** `adobe-firefly` and `firefly` have had a working usage fetcher since Adobe Firefly landed, but neither was ever added to the registration list, so the provider-plugin manifest, `genericQuotaFetcher` and the free-access quota cache all reported them as having no usage support — while `USAGE_SUPPORTED_PROVIDERS` said the opposite. Both are now declared, which also means their credit balance is fetched like any other declared provider's: `registerGenericQuotaFetchers` now registers a generic quota fetcher for them, and `resolveFreeAccessState` no longer returns early. A test holds the registration list to the dispatcher's switch in both directions, which is what the module's own docstring already asked for in prose ([#12321](https://github.com/diegosouzapw/OmniRoute/pull/12321)) — thanks @maxmad64bis +- **fix(quota):** drop the generic quota cache (agy / Antigravity / Claude OAuth) on an upstream 429 so reset-aware scoring does not keep a 60s stale snapshot, and force-refresh the next usage fetch so inner provider caches cannot recache the same window ([#12325](https://github.com/diegosouzapw/OmniRoute/pull/12325)) — thanks @HouMinXi +- **fix(combos):** deleting a combo now clears its persisted LKGP pins instead of leaving unreachable `key_value` rows behind ([#12326](https://github.com/diegosouzapw/OmniRoute/issues/12326)) ([#12425](https://github.com/diegosouzapw/OmniRoute/pull/12425)) — thanks @KrzysiekSko +- **fix(sse):** Keep ZWNJ (U+200C) and ZWJ (U+200D) in assistant text, reasoning and tool-call arguments — Persian/Kurdish half-space (`ارائه‌دهنده`), Arabic/Indic shaping and emoji sequences no longer lose them; the response de-obfuscation now removes joiners only between ASCII word characters, where the request side inserts them ([#12186](https://github.com/diegosouzapw/OmniRoute/issues/12186)) — thanks @rezjalibd ([#12359](https://github.com/diegosouzapw/OmniRoute/pull/12359)) — thanks @pacocartones +- **fix(resilience):** count resolved upstream 5xx results against the provider circuit breaker on the chat path — `CircuitBreaker.execute()` no longer reads a resolved `{ success: false, status: 5xx }` as a success that cancels the call-site failure, so a provider answering 503s now trips its breaker instead of staying `CLOSED` at `failureCount: 1`; single-model and combo dispatches are each accounted exactly once ([#12254](https://github.com/diegosouzapw/OmniRoute/issues/12254)) ([#12360](https://github.com/diegosouzapw/OmniRoute/pull/12360)) — thanks @pacocartones +- **fix(providers):** resolve the Codex quota auto-ping model from the live provider catalog and lifecycle registry instead of the retired `gpt-5.1-codex-mini`, and pause the ping with one actionable warning when no selectable Codex model exists rather than retrying a shut-down id every cooldown window ([#11905](https://github.com/diegosouzapw/OmniRoute/issues/11905)) ([#12361](https://github.com/diegosouzapw/OmniRoute/pull/12361)) — thanks @pacocartones +- **fix(api):** keep the `{created, data}` wrapper on combo-routed `/v1/images/generations` responses and default Codex image results to `b64_json` on both `/v1/images/generations` and `/v1/images/edits` so Codex CLI's built-in `image_gen` can decode them ([#12268](https://github.com/diegosouzapw/OmniRoute/issues/12268)) ([#12362](https://github.com/diegosouzapw/OmniRoute/pull/12362)) — thanks @pacocartones +- **fix(sse):** Name the shadowed custom provider node when a built-in provider id/alias (e.g. `openference` → `of`) reserves the prefix of an existing OpenAI/Anthropic-compatible node, so the runtime `No active credentials for provider: ` error explains that the prefix routed to the built-in and never reached the node's healthy connections, instead of contradicting the dashboard ([#11943](https://github.com/diegosouzapw/OmniRoute/issues/11943)) — thanks @morpheus9393 ([#12365](https://github.com/diegosouzapw/OmniRoute/pull/12365)) — thanks @pacocartones +- **fix(i18n):** wrap `ccOnboardingKeyPlaceholder` in ICU single quotes across all 43 locales so angle brackets render literally instead of being parsed as rich-text tags, which crashed the Claude Code onboarding block with `INVALID_MESSAGE: INVALID_TAG` ([#12302](https://github.com/diegosouzapw/OmniRoute/issues/12302)) ([#12369](https://github.com/diegosouzapw/OmniRoute/pull/12369)) — thanks @gonisulaimann +- **fix(models):** publish `effort_tiers` on Kimi K3's synced base-model entries (`kmca/k3`, `kmca/k3-256k`) so catalog-only clients (OpenCode, plain SDK pickers) can see and select the reasoning tiers (`low`/`high`/`max`) the synced metadata already carried — the `isSkippedEffortProvider` gate no longer suppresses tier visibility on those base entries, while synthetic `-` variant generation stays prevented and Codex/GLM base models remain excluded unchanged ([#12299](https://github.com/diegosouzapw/OmniRoute/issues/12299)) ([#12371](https://github.com/diegosouzapw/OmniRoute/pull/12371)) — thanks @gonisulaimann +- **fix(guardrails):** keep `auto`/`auto/*` virtual combos exempt from the Vision Bridge `fixedModel` credential guard so a combo target is passed through instead of silently falling back to global auto-selection ([#12237](https://github.com/diegosouzapw/OmniRoute/issues/12237)) ([#12373](https://github.com/diegosouzapw/OmniRoute/pull/12373)) — thanks @pacocartones +- **fix(combo):** capability-filter exhaustion caused by `max_tokens` above every target's known output limit now reports that reason (requested `max_tokens` vs the pool's highest known ceiling) instead of the unrelated "supports structured output" message ([#12229](https://github.com/diegosouzapw/OmniRoute/issues/12229)) — thanks @DW-MediaLab ([#12374](https://github.com/diegosouzapw/OmniRoute/pull/12374)) — thanks @pacocartones +- **fix(auth):** the `least-used` account strategy now prefers accounts without backoff before falling back to oldest `lastUsedAt`, the same tie-break `round-robin` already applies, so a failover no longer lands on a just-rate-limited account for a single request ([#12279](https://github.com/diegosouzapw/OmniRoute/issues/12279)) — thanks @tenshiak ([#12375](https://github.com/diegosouzapw/OmniRoute/pull/12375)) — thanks @pacocartones +- **fix(docker):** the `chatgpt-web-codex-browser` image now finds the Chrome binary under `chrome-linux64/` (Chrome for Testing layout in `playwright:v1.62.0-noble`) as well as the legacy `chrome-linux/`, so the container no longer crash-loops with `exec: --headless=new: not found` ([#12024](https://github.com/diegosouzapw/OmniRoute/issues/12024)) ([#12376](https://github.com/diegosouzapw/OmniRoute/pull/12376)) — thanks @pacocartones +- **fix(providers):** declare `groq/compound` and `allam-2-7b` as non-reasoning models in the curated Groq registry so `reasoning_effort` / `output_config.effort` / `thinking` from Claude Code are stripped instead of forwarded, which Groq rejected with HTTP 400 ([#12134](https://github.com/diegosouzapw/OmniRoute/issues/12134)) ([#12379](https://github.com/diegosouzapw/OmniRoute/pull/12379)) — thanks @pacocartones +- **fix(executors):** `OpencodeExecutor` no longer forces a direct connection when the connection has a proxy assigned in Proxy Management but no per-account proxies: the single-account fast path used to wrap the upstream dispatch in the direct-egress sentinel, discarding the ambient proxy context the chat handler had pinned from `proxy_assignments`, so API-key `opencode`/`opencode-go` connections egressed from the host IP (and hit geoblocks) despite the assignment. The direct pin is now applied only when no ambient proxy context exists ([#11894](https://github.com/diegosouzapw/OmniRoute/issues/11894) — thanks @hizzt) ([#12380](https://github.com/diegosouzapw/OmniRoute/pull/12380)) — thanks @pacocartones +- **fix(api):** `GET /v1/models` with `MODELS_CATALOG_PREFIX_MODE=canonical` (or `?prefix=canonical`) now lists providers whose registry alias is undefined or equal to their own id (Antigravity, Antigravity CLI and other self-aliased built-ins) — their single `provider/model` id was dropped by the alias/canonical duplicate guard in the static, synced, custom and alias-backed catalog loops ([#12058](https://github.com/diegosouzapw/OmniRoute/issues/12058)) — thanks @cheynetom ([#12381](https://github.com/diegosouzapw/OmniRoute/pull/12381)) — thanks @pacocartones +- **fix(translator):** Drop replayed `thinking` blocks that carry no signature (the shape produced from cross-provider `reasoning_content`) instead of stamping the default Claude signature on them, which Anthropic rejected with `400 Invalid signature in thinking block` on the next turn served by an Anthropic rung ([#12105](https://github.com/diegosouzapw/OmniRoute/issues/12105)) — thanks @atescivitci-cmd ([#12386](https://github.com/diegosouzapw/OmniRoute/pull/12386)) — thanks @pacocartones +- **fix(cli):** Resolve Bun's `--preload` polyfill path against the package root instead of `dist/`, so `omniroute` installed with `bun install -g` no longer crashes at startup with `error: preload not found …/dist/open-sse/utils/setupPolyfill.ts` ([#11980](https://github.com/diegosouzapw/OmniRoute/issues/11980)) — thanks @joglomedia ([#12387](https://github.com/diegosouzapw/OmniRoute/pull/12387)) — thanks @pacocartones +- **fix(providers):** `gemini-business` now publishes its model catalog — `/v1/models` and `/v1/providers/gemini-business/models` list the 12 enterprise Gemini ids the executor understands instead of returning an empty list (#12107) ([#12389](https://github.com/diegosouzapw/OmniRoute/pull/12389)) — thanks @pacocartones +- **fix(db):** install `busy_timeout` before the SQLite connection's first statement so a process opening the database while another one closes its WAL connection waits out the transient EXCLUSIVE lock instead of dying with `database is locked`, and recognise the drivers' real BUSY/PROTOCOL/IOERR errors as transient in the corruption probe so the same lock no longer renames the database away as corrupt; deflakes `cross-process contenders never both acquire the same connection` (#12394 — thanks @pacocartones) +- **fix(chat-admission):** derive the `chat_admission_busy` 503 `Retry-After` from observed heavyweight-lease occupancy — the larger of the exhausted `OMNIROUTE_CHAT_ADMISSION_QUEUE_MS` window and the time since capacity last turned over, capped at 60 s — instead of a fixed 1 s (structural) / 2 s (byte-stage) hint that invited Codex/agent fan-out clients to re-send ~1 MiB `/v1/responses` bodies every second into a gate held for the whole SSE lifetime; an idle gate keeps the historical floors ([#12135](https://github.com/diegosouzapw/OmniRoute/issues/12135)) (#12395 — thanks @pacocartones) +- **fix(api-manager):** the API key permissions modal no longer silently drops `allowedCombos` entries its Combo picker cannot render — routing-rule names such as `rt-*`, which the backend already honours — when "All" is clicked and the key is switched back to "Restrict"; those entries now survive the toggle, are listed read-only under the combo list so the count and the list agree, and are saved back verbatim instead of persisting `[]` (deny-all) (#12397 — thanks @pacocartones) +- **fix(catalog):** write the NUL separator of the catalog connection memo key, the provider serviceKind memo key, the Video Bridge promotion group key and a JSON-exactness test fixture as the `\u0000` escape instead of a raw byte — same runtime value, but the raw byte made git, GitHub and ripgrep treat those files as binary (hidden PR diffs, silently skipped searches); a guard test now keeps raw NUL bytes out of `src/`, `open-sse/` and `tests/` (#12403 — thanks @pacocartones) +- **fix(providers):** add `CLAUDE_CODE_CLIENT_VERSION` and `GITHUB_COPILOT_CLI_VERSION` env overrides so Anthropic/Copilot client-version gates can be unblocked without a rebuild ([#12417](https://github.com/diegosouzapw/OmniRoute/issues/12417)) ([#12632](https://github.com/diegosouzapw/OmniRoute/pull/12632)) — thanks @HouMinXi +- **fix(db):** back-fill `last_ping_at` and `last_pinged_reset_key` on `provider_connections` during schema reconciliation so divergent lineages that skipped `123_quota_auto_ping` still accept quota auto-ping writes ([#12470](https://github.com/diegosouzapw/OmniRoute/pull/12470) — thanks @KooshaPari) +- **fix(ci):** document MIT exceptions for `@eloqnt/{config,format-json,format-po}` (next-intl transitive; locked tarballs omit `license`) and keep the A2A lifecycle vitest off the real SQLite persistence seam ([#12581](https://github.com/diegosouzapw/OmniRoute/issues/12581)) ([#12595](https://github.com/diegosouzapw/OmniRoute/pull/12595)) +- OpenCode plugin `/v1/models` catalog fetch now waits 30s by default and attaches HTTP `statusCode` on 401/5xx so host fallback plugins can hop instead of seeing an untyped AbortError/UnknownError. ([#12607](https://github.com/diegosouzapw/OmniRoute/pull/12607)) — thanks @RaviTharuma +- **CI:** the OpenAPI security-tier gate now mirrors `isAlwaysProtectedPath()` in full — it also reads `ALWAYS_PROTECTED_API_PATTERNS`, so the pattern-gated credential routes (`/api/providers/{id}/{claude,codex}-auth/{export,apply-local}`, GHSA-5926-2w35-7h4q) no longer report as unannotated. (#12605) +- **fix(api):** GET /v1/models no longer waits forever on a hung coalesced catalog rebuild; cold-path waits are bounded (`CATALOG_BUILD_TIMEOUT_MS`, default 8s) and a last-good 200 is served when the rebuild times out ([#12627](https://github.com/diegosouzapw/OmniRoute/issues/12627)). ([#12628](https://github.com/diegosouzapw/OmniRoute/pull/12628)) — thanks @RaviTharuma +- **fix(cursor):** a non-array `tool_calls` on an assistant message no longer crashes the cursor request translator with a `TypeError`; both loops now require an array ([#12689](https://github.com/diegosouzapw/OmniRoute/issues/12689)) ([#12691](https://github.com/diegosouzapw/OmniRoute/pull/12691)) — thanks @soroush5 +- **fix(chat):** Chat Completions no longer return empty `content` after a server-owned memory or skills tool; the first provider send and account/model recovery now share one pipeline so a follow-up round-trip can fill the reply ([#12696](https://github.com/diegosouzapw/OmniRoute/issues/12696)) — thanks @HouMinXi ([#12867](https://github.com/diegosouzapw/OmniRoute/pull/12867)) +- **fix(dashboard):** batch-deleting provider keys no longer toasts failure after a successful delete when the confirm button's click event is forwarded as `onAfter` ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) — thanks @HouMinXi +- **fix(glm):** drop the extra 16th argument to `createSSETransformStreamWithLogger` that TypeScript rejected (TS2554) and that never reached the TransformStream ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) ([#12770](https://github.com/diegosouzapw/OmniRoute/pull/12770)) — thanks @HouMinXi +- **fix(dashboard):** pass `nodeMap` into Runtime `QuotaGroup` so a quota monitor in error/exhausted/alerting no longer throws `ReferenceError: nodeMap is not defined`. ([#12868](https://github.com/diegosouzapw/OmniRoute/pull/12868)) — thanks @HouMinXi +- Restricted API keys whose `allowedModels` lists a combo name no longer skip every combo member at pre-dispatch (`ALL_TARGETS_SKIPPED`). Inner-target filtering still applies when the allow-list is a provider prefix or `disableNonPublicModels` is on ([#12899](https://github.com/diegosouzapw/OmniRoute/pull/12899)). — thanks @HouMinXi +- **fix(combos):** Effort-suffixed combo members inherit the base model's `model_context_overrides` row so priority order is not inverted on large requests ([#12926](https://github.com/diegosouzapw/OmniRoute/pull/12926)) — thanks @HouMinXi +- **fix(catalog):** Picker-added `customModels` enter the dispatch-time live catalog so combo members and bare inference no longer 400 ([#12934](https://github.com/diegosouzapw/OmniRoute/pull/12934)) — thanks @HouMinXi +- **fix(docker):** re-derive the Docker build's worker-pool memory budget from the MEASURED ~4.5 GB per-process RSS (the issue owner's own VPS dmesg OOM-killer reproduction) instead of the stale 2560 MB/worker inference #11419 shipped, and lower `OMNIROUTE_BUILD_WORKERS` 3 → 2 so 1 parent + 1 page-data worker (2 processes × 4.5 GB = 9 GB) fits the 12.288 GB (75%) budget on a 16 GB GitHub Actions runner — the previous default (1 parent + 2 workers = 13.5 GB) still overcommitted the runner and kept "Publish to Docker Hub" failing with `cannot allocate memory` after #11419 merged (#7518). ([#11663](https://github.com/diegosouzapw/OmniRoute/pull/11663)) +- **fix(electron):** the packaged Windows desktop build now passes the #7592 cold-restart smoke end to end. Five defects were found and fixed on the way: (1) optional-pack staging failed on any machine whose `tar` is GNU tar — it reads the drive letter in an absolute `-f C:\...` archive path as a remote rsh host (`Cannot connect to C:`), so staging now passes a bare filename with `cwd` at the tarball directory and surfaces tar stderr on failure; (2) the Electron `files` allowlist was missing `lib/loginHeaderCapture.js`, so the packaged main process crashed at startup with `Cannot find module './lib/loginHeaderCapture'`; (3) electron-builder ≥26 injects an `!**/node_modules/**` ignore into every extraResources pattern list that no positive filter can override, which silently dropped the staged runtime `node_modules` (including the better-sqlite3 N-API prebuild) from `resources/app` — a fresh v3.8.50 desktop build would have shipped with no native SQLite driver and reproduced the #7592 sql.js fallback on every machine; an `afterPack` hook now restores the staged `node_modules` after packing; (4) the packaged-app smoke harness redirected `USERPROFILE` into its temp DATA_DIR, but Electron resolves the Roaming profile from `%USERPROFILE%\AppData\Roaming\` (USERPROFILE takes precedence over the APPDATA env var) and the path service throws instead of creating it, so `requestSingleInstanceLock()` returned false and the app exited(0) silently before `app.whenReady()` — the harness now pre-creates the derived tree, and `ensureSmokeEnvDirs` is exported and unit-tested; (5) the #7592 driver assertion parsed a `[DB] Driver: ...` line that the server's primary DB path never printed (only the unused `openDatabaseAsync()` did) — `getDbInstance()` now emits the same line on open so the guard can actually assert the native driver. Also: the smoke env-allowlist unit test hardcoded POSIX paths and could only pass on Linux/macOS; expectations are now host-agnostic, with new regression tests for the USERPROFILE-derived tree and `tarPack` under absolute Windows-style paths ([#11443](https://github.com/diegosouzapw/OmniRoute/pull/11443)) — thanks @jonlwheat2-gif +- fix(sse): stop the auto-combo candidates inspector from silently dropping model-locked/cooled-down rows (#9133) ([#11994](https://github.com/diegosouzapw/OmniRoute/pull/11994)) — thanks @hartmark +- **fix(providers):** Perplexity Web no longer turns upstream stream failures into successful assistant text; pre-content failures remain eligible for fallback, partial output ends with a structured sanitized error, and failed sessions are not persisted ([#12465](https://github.com/diegosouzapw/OmniRoute/pull/12465)) +- **fix(video-bridge):** preserve chronological video and transcript fusion, source-frame timestamps, transcript provenance, full contact-sheet intervals, and pre-abort callback isolation ([#11681](https://github.com/diegosouzapw/OmniRoute/pull/11681)) +- **Z.ai Web:** HTTP 200 streams carrying an upstream error now terminate with a structured failure instead of assistant text plus a normal stop, preserving partial output while allowing pre-content combo fallback. ([#12454](https://github.com/diegosouzapw/OmniRoute/pull/12454)) +- Absorb `Error [AbortError]: request_signal_aborted` and DOMException AbortError shapes in the process-level client-abort crash guard so routine client disconnects no longer kill the server (exit code 7). ([#12165](https://github.com/diegosouzapw/OmniRoute/pull/12165)) — thanks @alvinveroy +- **fix(sse):** Treat Adapta Web `type:error` SSE events as sanitized non-stream failures instead of empty HTTP 200 completions. ([#12459](https://github.com/diegosouzapw/OmniRoute/pull/12459)) +- **fix(autoCombo):** layer 3 (`models_dev_tier`) no longer scores retired models as premium, merge cross-provider capability rows deterministically, and inherit tier scores through variant ids ([#11508](https://github.com/diegosouzapw/OmniRoute/issues/11508)). `getModelsDevTierFitness` now vetoes ids present in `config/quality/model-lifecycle.json` with status `retired` before any other signal; `loadModelCapabilities()` aggregates per `model_id` (any-non-null-true for booleans, max for `limit_context`) instead of last-write-wins over SQLite's undefined row order; a capabilities miss retries through `resolveScoresAs()`'s base id and reports `models_dev_tier:inherited`, matching layers 1–2 (#11492). Adds `getModelsDevTierFitnessWithSource()` and an `invalidateCapabilitiesCache()` test/ops hook. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) — thanks @Meet6338-X +- fix(sse): accept Claude `content:[]` + `stop_reason: max_tokens`/`tool_use` in `detectMalformedNonStream` (match `isEmptyContentResponse`) so Claude Code `/model` probes with `max_tokens: 1` no longer become a false 502 ([#11568](https://github.com/diegosouzapw/OmniRoute/pull/11568)) — thanks @Seramicx +- **fix(security):** sanitize `request.failed` diagnostics before publishing them to live dashboard listeners and replay history, while keeping status, model, provider, latency, and internal call-log diagnostics intact. ([#12469](https://github.com/diegosouzapw/OmniRoute/pull/12469)) +- **fix(pwa):** do not intercept dashboard navigations so Chrome can fall back from a stale HTTP/3 Alt-Svc advertisement (UDP :20128 is unpublished; F5 on a long-lived tab hung until a new tab opened a fresh TCP connection). ([#12767](https://github.com/diegosouzapw/OmniRoute/pull/12767)) — thanks @HouMinXi +- **fix(executors):** handle DuckDuckGo ERR_BN_LIMIT (418) without retrying — when the upstream returns `418 ERR_BN_LIMIT` (rate-limit/ban), the executor now returns the error immediately instead of burning another VQD acquisition that would only count against the IP limit. The retry logic for `418 ERR_CHALLENGE` (unsolved challenge) remains unchanged. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) ([#12110](https://github.com/diegosouzapw/OmniRoute/pull/12110)) — thanks @oyi77 / @Meet6338-X +- Fixed DuckDuckGo streaming responses losing JSON lines and UTF-8 characters split across network chunks. ([#11528](https://github.com/diegosouzapw/OmniRoute/pull/11528)) — thanks @pacocartones +- Harden generated Cloudflare, Vercel, and Deno relays by stripping hop-by-hop, framing, and proxy authentication headers before forwarding requests upstream. ([#11533](https://github.com/diegosouzapw/OmniRoute/pull/11533)) — thanks @pacocartones +- **feat(grok-cli):** Provider Limits shows grok-cli banked reset credits from `GetRemainingResets` (including a real zero; a failed RPC omits the row) and the existing View credits button now calls `ConsumerUiSvc/RedeemReset` for grok-cli. Live tokens use nested fields 10/20/30 (id + Timestamp), not compact 1/2/3. ([#12805](https://github.com/diegosouzapw/OmniRoute/pull/12805)) — thanks @HouMinXi +- **fix(memory):** Embedding Model Quick select, Embedding Source remote dropdown, and Rerank selector now list every configured provider with embedding/rerank support instead of only chat-catalog text matches plus OpenRouter live discovery; a generic OpenAI-compatible `/embeddings` + Cohere-compatible `/rerank` runtime fallback resolves any configured chat provider's embedding/rerank endpoint, so unlisted providers no longer fail with "Unknown embedding provider"; both memory selectors gained a free-text model override ([#11390](https://github.com/diegosouzapw/OmniRoute/pull/11390)) — thanks @rqzbeh +- Harden SQLite upgrades around the historical migration-074 version collision: missing discovery and inspector tables are replayed atomically, pre-existing databases (including setup-created skeletons) receive reusable content-addressed safety snapshots, and Node test/eval probes without `DATA_DIR` are isolated from the operator database. ([#12435](https://github.com/diegosouzapw/OmniRoute/pull/12435)) +- **fix(dashboard):** Moonshot/Kimi Open Platform voucher and cash leftover percentages follow the bucket balance, so an empty wallet no longer paints those rows as 100% while Available is 0% ([#12733](https://github.com/diegosouzapw/OmniRoute/pull/12733)) — thanks @HouMinXi +- **fix(combo):** return non-retryable HTTP 400 when all candidates for a pinned native Codex turn are unavailable due to model-scoped lockout, terminating the turn cleanly while preserving turn continuity and enabling standard Combo routing on subsequent turns ([#12240](https://github.com/diegosouzapw/OmniRoute/pull/12240)) — thanks @mdigitalbh81 +- Fixed auto-router strategies preserving the selected connection through ranking and dispatch when multiple connections share the same provider and model. ([#11530](https://github.com/diegosouzapw/OmniRoute/pull/11530)) — thanks @pacocartones +- Preserve Chat Completions JSON-object response formats when translating requests to the Responses API. ([#11529](https://github.com/diegosouzapw/OmniRoute/pull/11529)) — thanks @pacocartones +- Fix idempotency fingerprints so Responses API requests include `input` and other semantic generation fields using stable serialization, while excluding credentials and request metadata. ([#11532](https://github.com/diegosouzapw/OmniRoute/pull/11532)) — thanks @pacocartones +- **fix(api):** Generated API CLI commands now enforce required OpenAPI request bodies; Combo test commands forward the required `comboName` body, while API keys created by older writers after migration 149 preserve legacy allow-all Combo access without widening explicit empty allowlists — thanks @marcelokarval ([#12070](https://github.com/diegosouzapw/OmniRoute/pull/12070)) +- **fix(grok-web):** treat upstream streaming failures as failures instead of successful assistant text: error-only streams now fail readiness with HTTP 502, while failures after legitimate content preserve that partial output and terminate through the sanitized stream failure path without a normal `stop` completion. ([#12458](https://github.com/diegosouzapw/OmniRoute/pull/12458)) +- HuggingChat now turns HTTP 200 JSONL generation failures into a sanitized 502 before content, or a fixed public stream failure after partial output, so fallback and request persistence no longer record a false successful stop. ([#12456](https://github.com/diegosouzapw/OmniRoute/pull/12456)) +- **fix(providers):** keep 1min.ai HTTP 200 stream errors out of assistant content, preserve partial output, and expose sanitized terminal errors so pre-content failures can fall back. ([#12466](https://github.com/diegosouzapw/OmniRoute/pull/12466)) +- **fix(sessions):** preserve legacy Sessions data when the additive exclusive-session projection is unavailable, returning an empty projection and warning only once per contiguous outage instead of failing the endpoint; the Sessions badge intentionally reflects the merged legacy and exclusive row count introduced by [#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389) ([#11469](https://github.com/diegosouzapw/OmniRoute/pull/11469)) — thanks @KaspaPulse +- Render Nimble Search with the generic provider icon and serve Opper's proven logo locally. ([#12415](https://github.com/diegosouzapw/OmniRoute/pull/12415)) +- **fix(quality):** `config/quality/provider-assets-provenance.jsonl` was stale against the physical `public/providers/` tree (still listing 225 assets and 5 duplicate-content groups after several provider-retirement PRs removed 85 icon files), which would have made `check:provider-asset-provenance` fail on the next full run. Reconciled the manifest to the real 142-file snapshot (142 asset records, 1 duplicate-content group, 2 newly-registered files marked `unresolved` pending provenance research), repinned `auditedCommit`, and updated the regression test's hardcoded counts to match. ([#11876](https://github.com/diegosouzapw/OmniRoute/pull/11876)) +- fix(ui): wrap quota card footer action buttons to prevent clipping Refresh button on narrow cards — when extra action buttons such as `View credits` are present on Codex cards, allow the footer row to flex-wrap with `shrink-0` buttons so `Refresh now` remains visible and clickable across all responsive card widths ([#11464](https://github.com/diegosouzapw/OmniRoute/pull/11464)) — thanks @adevwithpurpose +- Keep Antigravity Gemini usable when the same connection's Claude weekly quota is empty; generic quota cache stays per-connection for every other provider. ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) — thanks @HouMinXi +- Fixed Tailscale tunnel setup to reject ports outside the valid 1-65535 range and safely install Windows MSI packages from paths containing apostrophes. ([#11534](https://github.com/diegosouzapw/OmniRoute/pull/11534)) — thanks @pacocartones +- Classify Cloudflared and Tailscale process-management routes as local-only and spawn-capable while preserving remote access to read-only tunnel status endpoints. ([#11531](https://github.com/diegosouzapw/OmniRoute/pull/11531)) — thanks @pacocartones +- Electron release workflow: the `publish-npm` job now grants `actions: read` to the reusable `npm-publish.yml` it calls (its `publish` job requests it), which is what made GitHub refuse the whole v3.8.50 run at startup and ship the release with zero desktop assets; a `workflow_dispatch` now builds the requested tag instead of the dispatching branch and can skip the npm leg (`publish_npm=false`) when only re-attaching assets ([#11974](https://github.com/diegosouzapw/OmniRoute/pull/11974)) +- **fix(dashboard):** The Combos page usage guide now reads its dismissal through `useSyncExternalStore` instead of correcting SSR state inside an effect, removing an extra commit of the page tree on every load (and the `react-hooks/set-state-in-effect` error it raised). ([#12671](https://github.com/diegosouzapw/OmniRoute/pull/12671)) +- Electron release: `electron/package-lock.json` regained the optional `electron-builder-squirrel-windows` subtree (13 entries) that `npm ci` had been refusing as out of sync, `electron-release.yml` gained a `build_ref` dispatch input and stops regenerating release notes on a re-attach dispatch, and the npm publish workflow attaches the SBOM to the GitHub Release on dispatch publishes too — so the v3.8.51 tag ships every desktop asset and the SBOM like v3.8.49 did ([#12022](https://github.com/diegosouzapw/OmniRoute/pull/12022)) +- Drained the reds every PR against `release/v3.8.51` was born with: documented the three service-worker build-id variables, registered the six retirement/tunnel tests with the mutation gate, approved `eslint-plugin-react-hooks` in the dependency allowlist, added the six `combo.sort.*` strings to `vi` and `pt-BR`, pointed the ChatGPT Web doc at the real migration-168 test, worded the g4f hint around the member key, and realigned four tests to the retired-provider catalog and the legacy-schema fixtures the retirement migrations touch. ([#11940](https://github.com/diegosouzapw/OmniRoute/pull/11940)) +- **fix(providers):** Claude, Grok, LMArena, Notion, and Perplexity web-cookie transports now use pooled `wreq-js` 3.2 instead of the native sidecar, with all nine supported bindings pinned and audited, and the applicable platform binding plus native-license evidence included in each release artifact ([#12429](https://github.com/diegosouzapw/OmniRoute/pull/12429), supersedes [#11753](https://github.com/diegosouzapw/OmniRoute/pull/11753)). +- **fix(providers):** Zed Hosted streaming failures now trigger fallback before content and end partial streams with a sanitized structured error instead of fake assistant text and a normal-success stop. ([#12455](https://github.com/diegosouzapw/OmniRoute/pull/12455)) +- **fix(test):** capture hook result after render (direct commit `b85d0abb0b`) +- **fix(sync):** repair the two regressions the v3.8.50 sync-back left on release/v3.8.51 (direct commit `5ade9e0851`) +- **revert(models):** drop the memo reset hook ported from #10055 (direct commit `282de841ae`) +- **fix(typecheck):** guard the optional connectionId before the reactive model sync (direct commit `f2825bad75`) +- **fix(quota-share):** release the winner's reserved in-flight slot (#11371) ([#11408](https://github.com/diegosouzapw/OmniRoute/pull/11408)) — thanks @oyi77 +- **fix(providers):** stop silently dropping reasoning effort on opencode families (#10788) ([#11409](https://github.com/diegosouzapw/OmniRoute/pull/11409)) — thanks @oyi77 +- **fix(combo):** enable genuine quota-aware routing for generic providers (antigravity, claude, etc.) ([#11411](https://github.com/diegosouzapw/OmniRoute/pull/11411)) — thanks @HouMinXi +- **fix(oauth):** allow expired token retry before deactivation and activate expired retry loop (#P0) ([#11414](https://github.com/diegosouzapw/OmniRoute/pull/11414)) — thanks @HouMinXi +- **perf(executors):** lazy-load the executor registry — defer class imports + construction to first use (#11220) ([#11421](https://github.com/diegosouzapw/OmniRoute/pull/11421)) — thanks @oyi77 +- **fix:** make release/v3.8.50 CI gates green (run 32786966560 root causes) ([#11450](https://github.com/diegosouzapw/OmniRoute/pull/11450)) — thanks @jonlwheat2-gif +- **fix(sse):** exact-domain cookie match and origin-equality URL assertions ([#11452](https://github.com/diegosouzapw/OmniRoute/pull/11452)) — thanks @hartmark +- **fix(providers):** send Responses API payload during OpenAI-compatible validation ([#11454](https://github.com/diegosouzapw/OmniRoute/pull/11454)) — thanks @tuandinh0801 +- **fix(command-code):** fallback to /alpha/generate for Go plan without Provider API access ([#11455](https://github.com/diegosouzapw/OmniRoute/pull/11455)) — thanks @Chewji9875 +- **fix(responses-continuation):** fail closed on a log-truncated stored input/output array ([#11473](https://github.com/diegosouzapw/OmniRoute/pull/11473)) — thanks @hartmark +- **fix(cli):** write jcode settings as [providers.omniroute] in config.toml ([#11484](https://github.com/diegosouzapw/OmniRoute/pull/11484)) — thanks @hongnoul +- **fix(logging):** raise chat-log array truncation cap to 1000 and unify duplicate implementations ([#11499](https://github.com/diegosouzapw/OmniRoute/pull/11499)) — thanks @hartmark +- **fix:** restore live websocket URL helpers ([#11509](https://github.com/diegosouzapw/OmniRoute/pull/11509)) — thanks @PauloFH +- **perf(core):** eliminate async waterfalls across home, cache, and db operations (#11396) ([#11512](https://github.com/diegosouzapw/OmniRoute/pull/11512)) — thanks @HouMinXi +- **fix(kiro):** add runtime.us-east-1.kiro.dev as first-attempt endpoint ([#11517](https://github.com/diegosouzapw/OmniRoute/pull/11517)) — thanks @rifqiawl +- **fix(providers):** remove duplicate 128-tool truncation in OpencodeExecutor (#11444) ([#11518](https://github.com/diegosouzapw/OmniRoute/pull/11518)) — thanks @HouMinXi +- **fix(providers):** poll onboardUser LRO before classifying BYOP ([#11519](https://github.com/diegosouzapw/OmniRoute/pull/11519)) — thanks @rifqiawl +- **fix(translator):** preserve exact tool name casing for Claude tool_use payloads (#11487) ([#11520](https://github.com/diegosouzapw/OmniRoute/pull/11520)) — thanks @HouMinXi +- **fix(kimi-web):** align health probe with executor domain ([#11521](https://github.com/diegosouzapw/OmniRoute/pull/11521)) — thanks @pacocartones +- **fix(cli):** preserve existing admin password during setup ([#11522](https://github.com/diegosouzapw/OmniRoute/pull/11522)) — thanks @pacocartones +- **fix(mitm):** keep Windows DNS elevation off the POSIX sudo path (#11430) ([#11542](https://github.com/diegosouzapw/OmniRoute/pull/11542)) — thanks @official-burak +- **fix(sse):** scale chat admission by ingest byte budget instead of a fixed request count ([#11548](https://github.com/diegosouzapw/OmniRoute/pull/11548)) — thanks @TheDemonTuan +- **fix:** accept Gemini Web cookie exports ([#11554](https://github.com/diegosouzapw/OmniRoute/pull/11554)) — thanks @rafacpti23 +- **fix(i18n):** complete Brazilian Portuguese translations ([#11558](https://github.com/diegosouzapw/OmniRoute/pull/11558)) — thanks @rafacpti23 +- **fix(sse):** explicit types for openai-responses pureHelpers — clears last failing core typecheck gate ([#11567](https://github.com/diegosouzapw/OmniRoute/pull/11567)) — thanks @jonlwheat2-gif +- **fix(opencode):** hydrate Proxy Pool references for no-auth connections ([#11584](https://github.com/diegosouzapw/OmniRoute/pull/11584)) — thanks @AStupidBear +- **fix(ci):** resolve inherited release/v3.8.51 base-red CI failures ([#11608](https://github.com/diegosouzapw/OmniRoute/pull/11608)) — thanks @jonlwheat2-gif +- **fix(db):** fallback load STORAGE_ENCRYPTION_KEY from env files ([#11614](https://github.com/diegosouzapw/OmniRoute/pull/11614)) — thanks @benzntech +- **fix(providers):** mark Brave Search as no longer having a free tier ([#11621](https://github.com/diegosouzapw/OmniRoute/pull/11621)) — thanks @turbolego +- **fix:** pass tool result content through as string in OpenAI/Claude -> Gemini translation ([#11624](https://github.com/diegosouzapw/OmniRoute/pull/11624)) — thanks @Hsia97 +- **fix(sse):** scope credential health to exact key ([#11628](https://github.com/diegosouzapw/OmniRoute/pull/11628)) — thanks @KaspaPulse +- **fix(install):** guard prepare script against missing husky ([#11634](https://github.com/diegosouzapw/OmniRoute/pull/11634)) — thanks @Prajeeth-12 +- **fix(security):** redact NVIDIA_API_KEY at the log sink in the ad-hoc diag script ([#11639](https://github.com/diegosouzapw/OmniRoute/pull/11639)) +- **fix(types):** clear the two type regressions red on release/v3.8.51 ([#11640](https://github.com/diegosouzapw/OmniRoute/pull/11640)) — thanks @jonlwheat2-gif +- **fix(build):** resolve workspace: protocol dependencies to concrete semver versions during prepublish (#11540) ([#11641](https://github.com/diegosouzapw/OmniRoute/pull/11641)) — thanks @HouMinXi +- **fix(search):** properly resolve configured search connection in /v1/responses pipeline (#11524) ([#11642](https://github.com/diegosouzapw/OmniRoute/pull/11642)) — thanks @HouMinXi +- **fix(authz):** trust cf-connecting-ip for via-proxy detection when peer is Cloudflare edge (#11514) ([#11643](https://github.com/diegosouzapw/OmniRoute/pull/11643)) — thanks @HouMinXi +- **fix(security):** compare the OIDC state cookie in constant time ([#11646](https://github.com/diegosouzapw/OmniRoute/pull/11646)) +- **fix(security):** namespace the dedup hash by the calling API key ([#11649](https://github.com/diegosouzapw/OmniRoute/pull/11649)) +- **fix(models):** gate the Codex-native catalog loop on prefix mode (#11632) ([#11662](https://github.com/diegosouzapw/OmniRoute/pull/11662)) — thanks @xiaoyaner0201 +- **fix(dashboard):** project Codex routing quota in account pools ([#11666](https://github.com/diegosouzapw/OmniRoute/pull/11666)) — thanks @KaspaPulse +- **fix(ddgw):** normaliza system no upstream da release v3.8.51 ([#11692](https://github.com/diegosouzapw/OmniRoute/pull/11692)) — thanks @RhianB14 +- **fix(memory):** honor category filter in GET /api/memory ([#11699](https://github.com/diegosouzapw/OmniRoute/pull/11699)) — thanks @Sabeekhann +- **fix(ci):** detect stale workspace lockfile entries ([#11714](https://github.com/diegosouzapw/OmniRoute/pull/11714)) — thanks @pacocartones +- **fix(ci):** stamp dist/BUILD_SHA before the npm publish provenance gate ([#11721](https://github.com/diegosouzapw/OmniRoute/pull/11721)) +- **fix(docker):** decouple the best-effort Bun image from the release manifest ([#11724](https://github.com/diegosouzapw/OmniRoute/pull/11724)) +- **perf(compression):** avoid cloning memo misses twice ([#11727](https://github.com/diegosouzapw/OmniRoute/pull/11727)) — thanks @RaviTharuma +- **fix(docker):** let the bun digest artifact be absent, not fatal ([#11740](https://github.com/diegosouzapw/OmniRoute/pull/11740)) +- **fix(shared):** block cloud-metadata hosts under default remote-image guard ([#11755](https://github.com/diegosouzapw/OmniRoute/pull/11755)) — thanks @santosraju99-hub +- **fix(api):** preserve caller-provided X-Correlation-Id on chat completions ([#11760](https://github.com/diegosouzapw/OmniRoute/pull/11760)) — thanks @aniruddhaadak80 +- **fix(resilience):** lock permanently retired models instead of short backoff (Gemini ban prevention) ([#11762](https://github.com/diegosouzapw/OmniRoute/pull/11762)) — thanks @turbolego +- **fix:** mark Vercel AI Gateway as passthroughModels ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **fix(resilience):** stop hammering permanently-moved endpoints and billing-suspended accounts ([#11774](https://github.com/diegosouzapw/OmniRoute/pull/11774)) — thanks @turbolego +- **fix(lease):** remove global static reservation and gate routing on live active lease occupancy ([#11775](https://github.com/diegosouzapw/OmniRoute/pull/11775)) — thanks @TheDemonTuan +- **fix(ci):** size the install-upgrade gate to a measured run, and log the pack cost ([#11776](https://github.com/diegosouzapw/OmniRoute/pull/11776)) +- **fix(pwa):** stop serving the stale shell after deploys ([#11779](https://github.com/diegosouzapw/OmniRoute/pull/11779)) — thanks @HouMinXi +- **fix(combo):** lock GitHub models rejected as "not supported" for future requests ([#11781](https://github.com/diegosouzapw/OmniRoute/pull/11781)) — thanks @turbolego +- **fix(cli):** probe both IPv4 and IPv6 loopback for server readiness (#11766) ([#11794](https://github.com/diegosouzapw/OmniRoute/pull/11794)) — thanks @vermasomesh835 +- **fix(sse):** preserve CLIProxyAPI wrappers per connection ([#11797](https://github.com/diegosouzapw/OmniRoute/pull/11797)) — thanks @RaviTharuma +- **fix(providers):** honor model auto-fetch opt-in on create ([#11798](https://github.com/diegosouzapw/OmniRoute/pull/11798)) — thanks @RaviTharuma +- **fix(dashboard):** make model auto-fetch order independent ([#11805](https://github.com/diegosouzapw/OmniRoute/pull/11805)) — thanks @RaviTharuma +- **fix(docker):** make GHA cache exports best effort ([#11806](https://github.com/diegosouzapw/OmniRoute/pull/11806)) — thanks @RaviTharuma +- **fix(kiro):** do not permanently ban on 'User is not authorized to make this call' ([#11809](https://github.com/diegosouzapw/OmniRoute/pull/11809)) — thanks @Deftera186 +- **fix(sse):** keep cache-write tokens in OpenAI-shaped usage ([#11814](https://github.com/diegosouzapw/OmniRoute/pull/11814)) — thanks @TheDemonTuan +- **fix(docs):** resolve relative markdown and wiki links across Fumadocs and GitHub wiki ([#11834](https://github.com/diegosouzapw/OmniRoute/pull/11834)) — thanks @AndrianBalanescu +- **fix(chat):** preserve unstripped model string for passthrough provider routing ([#11840](https://github.com/diegosouzapw/OmniRoute/pull/11840)) — thanks @raheemuddin786 +- **fix(mcp):** avoid async initialization inside sync esm wrappers (#11569) ([#11849](https://github.com/diegosouzapw/OmniRoute/pull/11849)) — thanks @HouMinXi +- **fix(sse):** preserve coherent markdown token boundaries across stream translation chunks (#11606) ([#11851](https://github.com/diegosouzapw/OmniRoute/pull/11851)) — thanks @HouMinXi +- **fix(ci):** run the install-upgrade gate on disk, not on the /tmp tmpfs ([#11855](https://github.com/diegosouzapw/OmniRoute/pull/11855)) +- **fix(skills):** expand shorthand property types in injected tool schemas ([#11857](https://github.com/diegosouzapw/OmniRoute/pull/11857)) — thanks @alvinveroy +- **fix(ci):** publish npm from a hosted runner so provenance is accepted ([#11877](https://github.com/diegosouzapw/OmniRoute/pull/11877)) +- **fix(lint):** pin react hooks plugin to 7.0.1 ([#11886](https://github.com/diegosouzapw/OmniRoute/pull/11886)) +- **fix(ci):** surface and clear release-green ESLint failure ([#11890](https://github.com/diegosouzapw/OmniRoute/pull/11890)) — thanks @MumuTW +- **fix(guardrails):** prevent duplicate prompt-injection-guard log output ([#11936](https://github.com/diegosouzapw/OmniRoute/pull/11936)) — thanks @patrykkopycinski +- **fix(auth):** downgrade expected transient states from warn to debug ([#11937](https://github.com/diegosouzapw/OmniRoute/pull/11937)) — thanks @patrykkopycinski +- **fix(codex):** fail over image generation for imported free plans ([#11948](https://github.com/diegosouzapw/OmniRoute/pull/11948)) — thanks @Bl0ck154 +- **fix(codex):** normalize non-stream responses ([#11951](https://github.com/diegosouzapw/OmniRoute/pull/11951)) — thanks @Bl0ck154 +- **fix(images):** forward Antigravity image size ([#11952](https://github.com/diegosouzapw/OmniRoute/pull/11952)) — thanks @Bl0ck154 +- **fix(db):** invalidate connection cache after upsert ([#11953](https://github.com/diegosouzapw/OmniRoute/pull/11953)) — thanks @Bl0ck154 +- **fix(codex):** restore imported account state ([#11954](https://github.com/diegosouzapw/OmniRoute/pull/11954)) — thanks @Bl0ck154 +- **fix(sse):** give extended-thinking targets the reasoning readiness budget ([#11959](https://github.com/diegosouzapw/OmniRoute/pull/11959)) — thanks @abhisheksharma2411 +- **fix(dashboard):** use opaque background and readable text color on cost chart tooltips ([#11960](https://github.com/diegosouzapw/OmniRoute/pull/11960)) — thanks @ZaimMarzuki +- **fix(antigravity):** send complete loadCodeAssist metadata (ideType/platform/pluginType as numeric enums) ([#11969](https://github.com/diegosouzapw/OmniRoute/pull/11969)) — thanks @rifqiawl +- **fix(core):** resolve DB init race condition and reasoning translation ([#12003](https://github.com/diegosouzapw/OmniRoute/pull/12003)) — thanks @raheemuddin786 +- **fix(admission):** exclude reclaimable page cache from the cgroup pressure ratio ([#12017](https://github.com/diegosouzapw/OmniRoute/pull/12017)) — thanks @HouMinXi +- **fix(build):** prune non-production trees in NFT trace excludes and tsconfig ([#12028](https://github.com/diegosouzapw/OmniRoute/pull/12028)) — thanks @Chewji9875 +- **fix(migrations):** add renamed migration compatibility for 056/073/077/101 ([#12036](https://github.com/diegosouzapw/OmniRoute/pull/12036)) — thanks @oyi77 +- **fix(sse):** default crash-guard logger to console.warn, not console ([#12042](https://github.com/diegosouzapw/OmniRoute/pull/12042)) — thanks @HouMinXi +- **fix(catalog):** derive vision/modalities for built-in auto combos from effective target pool ([#12046](https://github.com/diegosouzapw/OmniRoute/pull/12046)) — thanks @Prajeeth-12 +- **fix(release):** the packaged-app smoke verifies the database opened, not a driver line the primary path never prints (twin of #12032) ([#12047](https://github.com/diegosouzapw/OmniRoute/pull/12047)) +- **fix(barrel):** migrate src/app/ off the @/lib/localDb barrel import (#11795 Phase 2) ([#12051](https://github.com/diegosouzapw/OmniRoute/pull/12051)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate src/lib/ off the localDb barrel to direct db imports (#59) ([#12052](https://github.com/diegosouzapw/OmniRoute/pull/12052)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate open-sse, src/shared, src/sse, src/models, src/domain off the @/lib/localDb barrel import (#11795 Phase 4) ([#12053](https://github.com/diegosouzapw/OmniRoute/pull/12053)) — thanks @jonlwheat2-gif +- **fix(barrel):** delete the @/lib/localDb barrel — every consumer migrated (#11795 Phase 5) ([#12055](https://github.com/diegosouzapw/OmniRoute/pull/12055)) — thanks @jonlwheat2-gif +- **fix(radar):** restore D12 public boundary ([#12057](https://github.com/diegosouzapw/OmniRoute/pull/12057)) +- **fix(dev):** bound webpack and Tailwind scans ([#12075](https://github.com/diegosouzapw/OmniRoute/pull/12075)) — thanks @backryun +- **fix(dev):** isolate root layout settings reads ([#12076](https://github.com/diegosouzapw/OmniRoute/pull/12076)) — thanks @backryun +- **fix(dev):** reduce instrumentation executor fan-out (phase 3) ([#12078](https://github.com/diegosouzapw/OmniRoute/pull/12078)) — thanks @backryun +- **fix(dev):** make logging resources HMR-singleton ([#12079](https://github.com/diegosouzapw/OmniRoute/pull/12079)) — thanks @backryun +- **fix(sse):** keep unavailable forced connections scoped ([#12080](https://github.com/diegosouzapw/OmniRoute/pull/12080)) — thanks @keeltrace +- **fix(dev):** isolate batch dispatch from instrumentation ([#12081](https://github.com/diegosouzapw/OmniRoute/pull/12081)) — thanks @backryun +- **fix(ci):** reconcile release test contract drift ([#12082](https://github.com/diegosouzapw/OmniRoute/pull/12082)) — thanks @backryun +- **fix(ci):** accept CVE-2025-68121 in the prebuilt tls-client .so, auto-close base-red issues, guard Scorecard on the default branch ([#12085](https://github.com/diegosouzapw/OmniRoute/pull/12085)) +- **fix(db):** drop three consumer-less 1proxy exports — dead-code base-red on release/v3.8.51 after the barrel deletion (#12055) ([#12087](https://github.com/diegosouzapw/OmniRoute/pull/12087)) +- **fix(api):** clear the six API-route TypeScript regressions the new gate landed red on ([#12094](https://github.com/diegosouzapw/OmniRoute/pull/12094)) +- **fix(release):** never let the tag-push Create Release append auto notes to the curated body ([#12096](https://github.com/diegosouzapw/OmniRoute/pull/12096)) +- **fix(sse):** honor CLIProxyAPI environment API key ([#12099](https://github.com/diegosouzapw/OmniRoute/pull/12099)) — thanks @RaviTharuma +- **fix(oauth):** bind Google refresh to the client that issued the token ([#12106](https://github.com/diegosouzapw/OmniRoute/pull/12106)) — thanks @HouMinXi +- **fix(dashboard):** make RequestLoggerDetail loadable outside Next — CSS via globals.css + CJS/ESM interop (#11703 base-reds) ([#12114](https://github.com/diegosouzapw/OmniRoute/pull/12114)) +- **fix(leases):** project status lease row to lease columns so joined connection PII never escapes ([#12115](https://github.com/diegosouzapw/OmniRoute/pull/12115)) — thanks @geek007git +- **fix(plugins):** do not kill the plugin process when a fire-and-forget hook times out ([#12116](https://github.com/diegosouzapw/OmniRoute/pull/12116)) — thanks @geek007git +- **fix(guardrails):** restore injection-guard logging on middleware-only routes ([#12117](https://github.com/diegosouzapw/OmniRoute/pull/12117)) — thanks @geek007git +- **fix(plugins):** refresh stored manifest from disk on activate so new hook fields reach existing installs ([#12120](https://github.com/diegosouzapw/OmniRoute/pull/12120)) — thanks @geek007git +- **fix(codex):** preserve existing provider state when bulk-import upserts a matching connection ([#12122](https://github.com/diegosouzapw/OmniRoute/pull/12122)) — thanks @geek007git +- **fix(ci):** clear the two base-reds the 2026-08-30 merge batch left on release/v3.8.51 (round 3) ([#12123](https://github.com/diegosouzapw/OmniRoute/pull/12123)) +- **fix(sse):** strip type:'custom' from Claude tools on agentrouter dispatch ([#12126](https://github.com/diegosouzapw/OmniRoute/pull/12126)) — thanks @wahidsadik371-coder +- **fix(sse):** stop advertising video providers the dispatcher cannot run ([#12131](https://github.com/diegosouzapw/OmniRoute/pull/12131)) — thanks @quiterunner-commits +- **fix(search):** treat HTTP 432 and plan limit errors as transient cooldown ([#12139](https://github.com/diegosouzapw/OmniRoute/pull/12139)) — thanks @HouMinXi +- **fix(sse):** refuse an AI Horde queue that cannot fit the request budget ([#12143](https://github.com/diegosouzapw/OmniRoute/pull/12143)) — thanks @quiterunner-commits +- **fix(ci):** clear the base-reds the afternoon merge batch left on release/v3.8.51 (round 5: provider count 352, TS2554/TS2677) ([#12144](https://github.com/diegosouzapw/OmniRoute/pull/12144)) +- **fix(translator):** strip neutral tool_choice when tools absent in Responses-to-Chat (#12141) ([#12166](https://github.com/diegosouzapw/OmniRoute/pull/12166)) — thanks @HouMinXi +- **fix(guardrails):** pass providerId to getResolvedModelCapabilities in checkComboVision (#12112) ([#12169](https://github.com/diegosouzapw/OmniRoute/pull/12169)) — thanks @HouMinXi +- **fix(translator):** strip plaintext reasoning content for opaque responses backends (#12128) ([#12171](https://github.com/diegosouzapw/OmniRoute/pull/12171)) — thanks @HouMinXi +- **perf(stream):** compile hot-path regexes once, bound token caches, fix quadratic buffering ([#12179](https://github.com/diegosouzapw/OmniRoute/pull/12179)) — thanks @opensource-elearning +- **fix:** resolve compression worker file using runtime anchors instead of… ([#12183](https://github.com/diegosouzapw/OmniRoute/pull/12183)) — thanks @NightStalker-87 +- **fix:** prevent Claude Code session kills via liveness-aware readiness + auto model echo ([#12189](https://github.com/diegosouzapw/OmniRoute/pull/12189)) — thanks @opensource-elearning +- **fix(translator):** preserve falsy primitive values in Gemini and Antigravity function response results ([#12191](https://github.com/diegosouzapw/OmniRoute/pull/12191)) — thanks @Siva010 +- **fix(oauth):** mark empty Antigravity projectId as degraded and clear stale errors (#11284) ([#12205](https://github.com/diegosouzapw/OmniRoute/pull/12205)) — thanks @HouMinXi +- **fix(providers):** gate the Codex auto-ping usage read on the shared quota throttle ([#12209](https://github.com/diegosouzapw/OmniRoute/pull/12209)) — thanks @abhisheksharma2411 +- **fix(combos):** prioritize SQLite row id over inner JSON id and notify delete errors ([#12213](https://github.com/diegosouzapw/OmniRoute/pull/12213)) — thanks @HouMinXi +- **fix(providers):** mark featherless.ai as no longer having a free tier (`hasFree: false`) ([#12216](https://github.com/diegosouzapw/OmniRoute/pull/12216)) — thanks @turbolego +- **fix(deepseek-web):** stop Turbopack dev panic in the PoW worker path resolver ([#12221](https://github.com/diegosouzapw/OmniRoute/pull/12221)) — thanks @hartmark +- **fix(chat-admission):** clarify local 503 source ([#12223](https://github.com/diegosouzapw/OmniRoute/pull/12223)) — thanks @keeltrace +- **fix(dev):** silence webpack runtime module warnings ([#12228](https://github.com/diegosouzapw/OmniRoute/pull/12228)) — thanks @backryun +- **fix(db):** use module.require for CommonJS runtime driver loading ([#12230](https://github.com/diegosouzapw/OmniRoute/pull/12230)) — thanks @raheemuddin786 +- **fix(memory):** honest probe-driven FTS5 keyword status + memory_id rowid sync ([#12231](https://github.com/diegosouzapw/OmniRoute/pull/12231)) — thanks @raheemuddin786 +- **fix(system):** propagate abort signal to stream reader in HTTP version checks ([#12232](https://github.com/diegosouzapw/OmniRoute/pull/12232)) — thanks @raheemuddin786 +- **fix(resilience):** isolate local host execution errors from provider circuit breakers ([#12233](https://github.com/diegosouzapw/OmniRoute/pull/12233)) — thanks @raheemuddin786 +- **fix(sse):** sort injected tools deterministically for prompt caching ([#12234](https://github.com/diegosouzapw/OmniRoute/pull/12234)) — thanks @raheemuddin786 +- **fix(combo):** always clear the loop-safety timer, not just on the happy path (#11804) ([#12245](https://github.com/diegosouzapw/OmniRoute/pull/12245)) +- **fix(quality):** base-red pair — stryker tap registration + turn-pin suites aligned to the window gate ([#12255](https://github.com/diegosouzapw/OmniRoute/pull/12255)) — thanks @backryun +- **fix(dev):** qualify Turbopack runtime boundaries ([#12258](https://github.com/diegosouzapw/OmniRoute/pull/12258)) — thanks @backryun +- **fix(combo):** fall back to full pool when collapsed sole survivor is context-too-small ([#12278](https://github.com/diegosouzapw/OmniRoute/pull/12278)) — thanks @opensource-elearning +- **fix(combo):** bound the pre-dispatch unavailable skip so a stale label cannot dark a pool (#12168) ([#12285](https://github.com/diegosouzapw/OmniRoute/pull/12285)) +- **fix(security):** strip Qwen/Alibaba console-session cookies from provider API responses ([#12287](https://github.com/diegosouzapw/OmniRoute/pull/12287)) +- **fix(usage):** console-aware Token Plan guidance + subscription hint on bailian 401 ([#12288](https://github.com/diegosouzapw/OmniRoute/pull/12288)) +- **fix(resourcePressure):** log numeric detail on every rejection, recover faster ([#12293](https://github.com/diegosouzapw/OmniRoute/pull/12293)) — thanks @hartmark +- **fix(release):** drain the 2026-09-01 base-red window — passthrough usage regression + radarPage i18n keys ([#12327](https://github.com/diegosouzapw/OmniRoute/pull/12327)) +- **fix(ci):** openapi-security-tiers checker must honor routeGuard patterns + imported prefixes ([#12350](https://github.com/diegosouzapw/OmniRoute/pull/12350)) — thanks @ggiak +- **perf(ci):** cache node_modules in the npm-ci-retry composite (#8084 D3) ([#12408](https://github.com/diegosouzapw/OmniRoute/pull/12408)) +- **revert(ui):** point CTAs back at their real destinations ([#12410](https://github.com/diegosouzapw/OmniRoute/pull/12410)) +- **fix(release):** drain the 2026-09-02 base-red — rerank-providers import + api-typecheck baseline ratchet ([#12414](https://github.com/diegosouzapw/OmniRoute/pull/12414)) +- **fix(types):** make system prompt injection noImplicitAny-safe ([#12416](https://github.com/diegosouzapw/OmniRoute/pull/12416)) +- **fix(quality):** validate typecheck baseline schema ([#12419](https://github.com/diegosouzapw/OmniRoute/pull/12419)) +- **fix(memory):** resolve rerank provider node cache import ([#12421](https://github.com/diegosouzapw/OmniRoute/pull/12421)) +- **fix(ci):** repair release-root regressions — pack dedup, web-session syntax, uc-image ids ([#12423](https://github.com/diegosouzapw/OmniRoute/pull/12423)) — thanks @backryun +- **fix(providers):** separate MaxAI and UC credential contracts ([#12431](https://github.com/diegosouzapw/OmniRoute/pull/12431)) +- **fix(cli):** preserve Claude settings on config set ([#12432](https://github.com/diegosouzapw/OmniRoute/pull/12432)) — thanks @KrzysiekSko +- **fix(providers):** repair the maxai credential block truncated by merge auto-resolve ([#12433](https://github.com/diegosouzapw/OmniRoute/pull/12433)) +- **fix(quality):** record the 2026-09-02 merged growth in the file-size baseline ([#12434](https://github.com/diegosouzapw/OmniRoute/pull/12434)) +- **fix(uc):** route UC error strings through sanitizeErrorMessage; allowlist the retired codex id ([#12437](https://github.com/diegosouzapw/OmniRoute/pull/12437)) +- **fix(adapta):** redact streamed upstream errors ([#12438](https://github.com/diegosouzapw/OmniRoute/pull/12438)) +- **fix(sse):** remap non-contiguous upstream tool_calls index to a gap-free output_index ([#12445](https://github.com/diegosouzapw/OmniRoute/pull/12445)) — thanks @hartmark +- **fix(quality):** detect duplicate tool_calls entries in one response ([#12446](https://github.com/diegosouzapw/OmniRoute/pull/12446)) — thanks @hartmark +- **fix(conversations):** resolve turn content OmniRoute never sends back to the client ([#12447](https://github.com/diegosouzapw/OmniRoute/pull/12447)) — thanks @hartmark +- **fix(combo):** do not treat credits-exhausted 401 as auth skip ([#12449](https://github.com/diegosouzapw/OmniRoute/pull/12449)) — thanks @RaviTharuma +- **fix(auth):** do not park healthy quota accounts as expired ([#12452](https://github.com/diegosouzapw/OmniRoute/pull/12452)) — thanks @RaviTharuma +- **fix(responses-continuation):** fail closed on a collector-truncated, empty output array ([#12460](https://github.com/diegosouzapw/OmniRoute/pull/12460)) — thanks @hartmark +- **fix(logging):** raise the SSE payload collector's default cap ([#12461](https://github.com/diegosouzapw/OmniRoute/pull/12461)) — thanks @hartmark +- **fix(resilience):** surface Responses failed.error.message in 502s ([#12472](https://github.com/diegosouzapw/OmniRoute/pull/12472)) — thanks @RaviTharuma +- **fix(security):** unbiased maxai X-Random nonce + stricter URL/regex assertions ([#12502](https://github.com/diegosouzapw/OmniRoute/pull/12502)) +- **fix(video):** re-anchor transcript log-redaction so PII/credential maskers can't reopen the leak (#12150 P1 follow-up) ([#12503](https://github.com/diegosouzapw/OmniRoute/pull/12503)) +- **fix(monitoring):** serve cached credentialHealth off the request path ([#12533](https://github.com/diegosouzapw/OmniRoute/pull/12533)) — thanks @RaviTharuma +- **fix(quota):** keep Antigravity Gemini usable when Claude weekly is empty ([#12566](https://github.com/diegosouzapw/OmniRoute/pull/12566)) — thanks @HouMinXi +- **fix(authz):** hard-gate every credential export and CLI-config write (GHSA-5926-2w35-7h4q) ([#12600](https://github.com/diegosouzapw/OmniRoute/pull/12600)) +- **fix(docs):** restore the Next build — REMOVED_PROVIDERS.md had no frontmatter (base-red #12581) ([#12610](https://github.com/diegosouzapw/OmniRoute/pull/12610)) +- **fix(quality):** re-point the zcodeProtocol public-creds allowlist to line 313 ([#12615](https://github.com/diegosouzapw/OmniRoute/pull/12615)) +- **fix(security):** close 3 advisories — search baseUrl exfil, sk- in the error sanitizer, bifrost relay header leak ([#12620](https://github.com/diegosouzapw/OmniRoute/pull/12620)) +- **fix(providers):** sanitize boolean required and nested bare maps for Gemini (#12269) ([#12624](https://github.com/diegosouzapw/OmniRoute/pull/12624)) — thanks @HouMinXi +- **fix(resilience):** keep Overloaded STREAM_EARLY_EOF off the provider breaker ([#12626](https://github.com/diegosouzapw/OmniRoute/pull/12626)) — thanks @HouMinXi +- **fix(catalog):** re-audit free-tier quotas against official pages ([#12649](https://github.com/diegosouzapw/OmniRoute/pull/12649)) +- **fix(dashboard):** keep a request's pending-tracking id stable across combo target retries ([#12650](https://github.com/diegosouzapw/OmniRoute/pull/12650)) — thanks @hartmark +- **fix(tests):** retire dead model ids from the chat-pipeline integration suite (base-red #12581) ([#12670](https://github.com/diegosouzapw/OmniRoute/pull/12670)) +- **fix:** resolve SqliteError no such table compression_run_telemetry during cleanup ([#12682](https://github.com/diegosouzapw/OmniRoute/pull/12682)) — thanks @groovecityJO +- **fix(combo):** treat a pin-only step as implicit connection allowlist ([#12697](https://github.com/diegosouzapw/OmniRoute/pull/12697)) — thanks @HouMinXi +- **fix(ci):** ship .npmrc in published package so legacy-peer-deps applies to consumers (#11544) ([#12699](https://github.com/diegosouzapw/OmniRoute/pull/12699)) — thanks @KooshaPari +- **fix(docker):** pin 4 CLI tools to exact versions (#12576) ([#12703](https://github.com/diegosouzapw/OmniRoute/pull/12703)) — thanks @KooshaPari +- **fix(docker):** document and harden cli profile trust boundary (#12570) ([#12706](https://github.com/diegosouzapw/OmniRoute/pull/12706)) — thanks @KooshaPari +- **fix(sse):** redact video transcript in pre-guardrail rejected-request logs (#12150 P2 item 7) ([#12710](https://github.com/diegosouzapw/OmniRoute/pull/12710)) +- **fix(i18n):** quote placeholder in OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES description (#12505) ([#12769](https://github.com/diegosouzapw/OmniRoute/pull/12769)) — thanks @KooshaPari +- **fix(api/pricing):** surface validation error message as string, not raw object (#12494) ([#12771](https://github.com/diegosouzapw/OmniRoute/pull/12771)) — thanks @KooshaPari +- **fix(sse):** re-enable prompt compression for native Codex passthrough ([#12834](https://github.com/diegosouzapw/OmniRoute/pull/12834)) — thanks @thomasmaerz +- **fix(dashboard):** model health tests for a provider node set to the Responses API now call `/v1/responses` with a Responses-shaped body instead of `/v1/chat/completions` — those models were reported as `Provider returned HTTP 200 but no text content` even though the same model answered normally through `/v1/responses` ([#13070](https://github.com/diegosouzapw/OmniRoute/issues/13070)) ([#13087](https://github.com/diegosouzapw/OmniRoute/pull/13087)) — thanks @ntdatt812 +- fix(routing): stop a round-robin combo's "opencode" targets from collapsing onto the opencode-zen connection (#11912) ([#13283](https://github.com/diegosouzapw/OmniRoute/pull/13283)) +- fix(routing): stop the reactive-compaction debug log from lying when compression is globally disabled (#11977) ([#13276](https://github.com/diegosouzapw/OmniRoute/pull/13276)) +- fix(electron): relativize standalone-bundle symlink targets so Stage 8 manifest verification stops failing on Windows (#11979) ([#13251](https://github.com/diegosouzapw/OmniRoute/pull/13251)) +- fix(dashboard): surface a visible error when Compression Studio's combined preview run fails (#12061) ([#13288](https://github.com/diegosouzapw/OmniRoute/pull/13288)) +- fix(dashboard): make the compression "Effective pipeline" preview honor the active profile and warn when the master switch is off (#12063) ([#13287](https://github.com/diegosouzapw/OmniRoute/pull/13287)) +- fix(providers): scope TinyCMS Web signer's DOM shims to each call instead of leaking them for the process lifetime, and surface a clean HTTP status on a non-JSON interception-toggles error (#12072) ([#13254](https://github.com/diegosouzapw/OmniRoute/pull/13254)) +- fix(guardrails): stop Vision Bridge from re-selecting a model locked after a 404 (#12111) ([#13259](https://github.com/diegosouzapw/OmniRoute/pull/13259)) +- fix(sse): require Responses-shaped body before native OpenAI-compatible passthrough (#12129) ([#13278](https://github.com/diegosouzapw/OmniRoute/pull/13278)) +- fix(providers): minimax-m3 now collapses manual thinking.type:"enabled" to adaptive, preventing upstream 400 (2013) (#12132) ([#13249](https://github.com/diegosouzapw/OmniRoute/pull/13249)) +- fix(db): scope model visibility overrides by modality so hiding a Chat model no longer hides an identically-ID'd Image/Embeddings/etc. model (#12172) ([#13258](https://github.com/diegosouzapw/OmniRoute/pull/13258)) +- fix(db): scope local-provider apiKey dedup to matching base URL so LM Studio/Ollama-style connections support multiple accounts (#12173) ([#13250](https://github.com/diegosouzapw/OmniRoute/pull/13250)) +- fix(providers): refresh Trae's stale Referer/Origin and forward user timezone so imported connections stop failing with 401 (#12190) ([#13255](https://github.com/diegosouzapw/OmniRoute/pull/13255)) +- fix(providers): route opencode-go/gpt-5.6-luna to /responses instead of /chat/completions (#12196) ([#13275](https://github.com/diegosouzapw/OmniRoute/pull/13275)) +- fix(dashboard): allow deleting the last extra-upstream-header row even when invalid (#12251) ([#13252](https://github.com/diegosouzapw/OmniRoute/pull/13252)) +- **fix(i18n):** translate pre-existing `__MISSING__:` keys for `combo.sort`, `requestLogger.detail` expand/collapse, `common.profile`, and `settings.resilienceCredentialHealth*` across 39 locales ([#12272](https://github.com/diegosouzapw/OmniRoute/issues/12272)) ([#13034](https://github.com/diegosouzapw/OmniRoute/pull/13034)) — thanks @HouMinXi +- fix(cli): run the Node.js runtime compatibility guard before the heavy `tsx/esm` + Commander import chain so an unsupported runtime gets a clear message instead of a raw `Invalid regular expression flags` crash (#12296) ([#13253](https://github.com/diegosouzapw/OmniRoute/pull/13253)) +- fix(dashboard): refresh the providers list after deleting a compatible provider node (#12298) ([#13256](https://github.com/diegosouzapw/OmniRoute/pull/13256)) +- fix(usage): fail closed on API-key budget enforcement when a provider's `auto` routing alias has no pricing row, instead of silently counting it as $0 (#12341) ([#13257](https://github.com/diegosouzapw/OmniRoute/pull/13257)) +- **fix(providers):** include the submitted Agnes video model when polling by `video_id` ([#12356](https://github.com/diegosouzapw/OmniRoute/pull/12356)) — thanks @pacocartones +- **fix(routing):** custom OpenAI-compatible nodes now honor the saved Chat/Responses API type after edits instead of letting the node's original ID prefix override the live connection setting ([#11884](https://github.com/diegosouzapw/OmniRoute/issues/11884)). ([#12358](https://github.com/diegosouzapw/OmniRoute/pull/12358)) — thanks @pacocartones +- fix(sse): surface an error instead of a silent empty 200 when a Claude stream closes with zero bytes (#12398) ([#13285](https://github.com/diegosouzapw/OmniRoute/pull/13285)) +- fix(oauth): warn before the dead localhost:8080 redirect in antigravity/gemini `oauth start` (#12413) ([#13265](https://github.com/diegosouzapw/OmniRoute/pull/13265)) +- fix(providers): stop devin-cli spawn error from double-closing the SSE controller (#12517) ([#13261](https://github.com/diegosouzapw/OmniRoute/pull/13261)) +- **fix(rerank):** clamp Voyage `top_k` to the documents actually sent after empty-string filtering, and honor `return_documents: false` in the NVIDIA response adapter (#12523 — thanks @pacocartones) +- **fix(audio):** `/v1/audio/translations` now resolves combo names the way `/v1/audio/transcriptions` already does, so a combo that `GET /v1/models` advertises is fanned out to its targets instead of being rejected with `400 Invalid translation model: . Use format: provider/model`; literal `provider/model` ids and unknown bare names behave as before (#12536 — thanks @pacocartones) +- **fix(gemini):** strip the JSON-Schema-2020-12 `prefixItems` keyword from Gemini tool schemas at every nesting level, so Claude Code tool definitions no longer fail with `400 Unknown name "prefixItems"` on Gemini models (#12540 — thanks @pacocartones) +- **fix(api-manager):** Expose an accessible loading status while API keys are fetched instead of an empty accessibility tree (#12541 — thanks @pacocartones) +- **fix(video):** Clamp `estimateJpegFrameBytes` at zero for padding-only payloads and build the three encode-side frame data URIs from `JPEG_FRAME_DATA_URI_PREFIX` instead of a repeated literal (#12543 — thanks @pacocartones) +- **fix(devin):** accept Windows `DEVIN_AGENTIC_HOME` sandbox paths (`C:\...\.sandbox\...`) in the isolated-home check so the Devin Claude Bridge no longer fails closed on Windows ([#12405](https://github.com/diegosouzapw/OmniRoute/issues/12405)) (#12545 — thanks @pacocartones) +- **fix(db):** Add `conversation_turn_nodes` and orphaned `agentic_conversations` to the auto-cleanup cycle under the existing `retention.callLogs` window, so identity nodes whose call-log content has already been purged no longer accumulate without bound in `storage.sqlite` (#12548 — thanks @pacocartones) +- **fix(i18n):** Wrap the `~/.claude/profiles//settings.json` placeholder in ICU single quotes in the `OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES` feature-flag description across all 42 locales and the TypeScript default, so next-intl no longer fails with `INVALID_MESSAGE: UNCLOSED_TAG` and the Feature Flags card shows the description instead of the raw key (#12549 — thanks @pacocartones) +- **fix(orchestration):** `updateCloudAgentTask` now publishes the task's real `status` on `agent.task.updated` when an update only touches `result`, `activities` or `error`, instead of the fabricated `"updated"` state, and stays silent when no row matched the id (#12550 — thanks @pacocartones) +- **fix(i18n):** the home "Recent Requests" panel and the Provider Topology legend are now translated instead of rendering English copies on non-English dashboards; the legend reads its own `home.topologyLegend*` labels with consistent casing rather than borrowing the memory-settings "Recent" and analytics "Error" strings (#12551 — thanks @pacocartones). +- **docs(reference):** bring the `FEATURE_FLAGS.md` catalog back to 1:1 with `featureFlagDefinitions.ts` — 20 missing flags added, the two `*_BLOCK_THRESHOLD` env-only knobs moved out of the flag tables, category/total counts and the Live WS port corrected, guarded by a static test (#12552 — thanks @pacocartones) +- **fix(i18n):** backfill missing `usage.kiloPass*` strings in 39 locales and restore `featureFlagOmnirouteDisableThinkingLevelVariantsDescription` in `pt.json` ([#12561](https://github.com/diegosouzapw/OmniRoute/issues/12561)) ([#13027](https://github.com/diegosouzapw/OmniRoute/pull/13027)) — thanks @HouMinXi +- fix(docker): default docker-compose app ports (dashboard/API/live-WS) to loopback instead of `0.0.0.0`, closing the anonymous `/v1` LAN/WAN exposure gap left open by `REQUIRE_API_KEY=false` (#12568) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): close DNS-rebinding SSRF gap in webhook outbound-URL guard (#12569) ([#13243](https://github.com/diegosouzapw/OmniRoute/pull/13243)) +- fix(docker): require a per-session token on the VNC browser CDP bridge and isolate it on a dedicated Docker network (#12571) ([#13241](https://github.com/diegosouzapw/OmniRoute/pull/13241)) +- fix(open-sse): write Adobe Firefly session tokens and cookie jars with 0700/0600 permissions instead of the process umask (#12572) ([#13242](https://github.com/diegosouzapw/OmniRoute/pull/13242)) +- fix(api): remove hardcoded wildcard CORS in openai-to-gemini-sse.ts so the centralized fail-closed CORS gate is the sole source of `Access-Control-Allow-Origin` (#12573) ([#13239](https://github.com/diegosouzapw/OmniRoute/pull/13239)) +- fix(api): enforce API key policy (budget/rate-limit/schedule/endpoint scoping) on the ElevenLabs speech-to-text, text-to-speech and voices proxy routes (#12574) ([#13244](https://github.com/diegosouzapw/OmniRoute/pull/13244)) +- fix(sse): cap HuggingChat NDJSON body size and bound the read loop with the fetch timeout so a stalled or hostile upstream cannot buffer unbounded memory (#12577) ([#13282](https://github.com/diegosouzapw/OmniRoute/pull/13282)) +- fix(docker): scope the cliproxyapi/qdrant/bifrost sidecars to loopback by default and forward `CLIPROXYAPI_MANAGEMENT_KEY` into the cliproxyapi container so its management API is not left both unauthenticated and LAN-published (#12578) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): create DB export temp paths with `fs.mkdtempSync` instead of predictable timestamps (#12579) ([#13237](https://github.com/diegosouzapw/OmniRoute/pull/13237)) +- Cline 401 bodies that say "re-authenticate your Cline account" classify as a refreshable OAuth token, not a terminal expired key. The cooling panel no longer labels every cooldown as a 429; it shows the recorded last error instead. (#12594) ([#13060](https://github.com/diegosouzapw/OmniRoute/pull/13060)) — thanks @HouMinXi +- **fix(catalog):** degrade unknown combo targets instead of dropping LCD modalities, and persist OpenRouter `architecture.input_modalities` into the capability snapshot ([#12613](https://github.com/diegosouzapw/OmniRoute/issues/12613)) ([#13042](https://github.com/diegosouzapw/OmniRoute/pull/13042)) — thanks @HouMinXi +- fix(providers): send `x-api-key` instead of `Authorization: Bearer` for OpenCode Zen's `/v1/responses` endpoint (Muse Spark Contributor models), fixing a 401 on OmniRoute's auth header (#12633) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(sse): surface the actionable "Auggie CLI not found" message when the shell reports a missing `auggie` binary via exit code instead of a spawn error (#12645) ([#13263](https://github.com/diegosouzapw/OmniRoute/pull/13263)) +- **fix(config):** Persist deletions of built-in background-degradation entries — when a stored settings record exists its `degradationMap` is now authoritative instead of being merged under the defaults, so an entry the user removed in the dashboard no longer reappears on the next apply or restart ([#12424](https://github.com/diegosouzapw/OmniRoute/issues/12424)) ([#12647](https://github.com/diegosouzapw/OmniRoute/pull/12647)) — thanks @pacocartones +- **fix(gamification):** action-count badge milestones (First Token, Token Consumer, Token Machine, Token Whale, and the token-sharing tier) are now backed by a durable `xp_action_counts` counter incremented in `addXp()`, instead of a live `COUNT(*)` over `xp_audit_log`. The audit log is pruned by `retention.xpAuditLog` (default 30 days), so on a default install those "lifetime" milestones were really "actions in the last 30 days" and unlocked badges could stop unlocking once old rows aged out. `getActionCount()` and `checkActionCountBadges()` now read the same durable source, and a migration backfills existing totals from the surviving audit rows ([#12546](https://github.com/diegosouzapw/OmniRoute/issues/12546)) ([#12651](https://github.com/diegosouzapw/OmniRoute/pull/12651)) — thanks @pacocartones +- **fix(images):** `/v1/images/edits` now iterates a combo's targets the same way `/v1/images/generations` does (#9239) instead of flattening a bare combo to its first target. A combo whose first target is not edit-capable — or lacks credentials — now falls through to a later edit-capable target rather than hard-erroring, and missing credentials are skipped (not a hard `401`) to match the generations path. The per-target skip/terminal classification is extracted into a shared `runImageComboTargets` loop, so generations behavior is unchanged ([#12547](https://github.com/diegosouzapw/OmniRoute/issues/12547)). ([#12653](https://github.com/diegosouzapw/OmniRoute/pull/12653)) — thanks @pacocartones +- fix(sse): add first-byte watchdog to the TLS-fingerprint transport so a stalled wreq body falls back instead of hanging for minutes (#12656) ([#13272](https://github.com/diegosouzapw/OmniRoute/pull/13272)) +- fix(sse): exempt tiny-budget reasoning probes from combo quality failure and surface persisted-cooldown skips in ALL_TARGETS_SKIPPED diagnostics (#12659) ([#13269](https://github.com/diegosouzapw/OmniRoute/pull/13269)) +- fix(models): declare the real ~1M contextLength for OpenCode Zen's Muse Spark 1.2 models instead of falling back to the 200000 provider default (#12681) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(oauth): align codebuddy-cn OAuth User-Agent with the chat/usage CLI version to avoid WAF false positives (#12702) ([#13264](https://github.com/diegosouzapw/OmniRoute/pull/13264)) +- fix(dashboard): surface an authentication-required banner instead of silently blanking database settings for a guest session (#12709) ([#13270](https://github.com/diegosouzapw/OmniRoute/pull/13270)) +- **fix(resilience):** stop unbounded queue that hangs 6min until Aborted — gate, provider slot, and Bottleneck queue now share a per-connection `maxWaitMs` budget; `fail-closed` on exhaust (503); execution backstop `executionMaxWaitMs` overridable per connection with upstream clamp ([#12715](https://github.com/diegosouzapw/OmniRoute/pull/12715)) — thanks @maxmad64bis (with thanks to @Tushar49 for surfacing the slow-provider need in #12635) +- **fix(auto-combo):** every mode pack now carries `quality` and `reliability` weights (`quality` 0.02, 0.03 in `quality-first`; `reliability` 0.03, 0.04 in `reliability-first`), so selecting a pack no longer silences either signal; `DEFAULT_WEIGHTS` is unchanged ([#12731](https://github.com/diegosouzapw/OmniRoute/pull/12731)) — thanks @maxmad64bis +- **fix(sse):** the provider execution pipeline keeps the upstream error `code`/`type` again — both were lost when the non-streaming leg was extracted, so a config-class `422` (Antigravity missing project) degraded into a generic account cooldown instead of reaching the gate that recognises it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(db):** a legacy `call_logs` table no longer aborts startup — the provider-stats index is created after the column healing runs, not before it, so an install predating `request_type` opens instead of failing with `no such column` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(catalog):** a malformed row in the operator-writable custom-models blob no longer kills every `auto/*` pool ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(tests):** realign the guards that the same day's merges left asserting the old behavior — the provider-count total after GreenPT and EURouter, the GLM stream buffer slot that `#12925` turned into a declared parameter, and the injection scan bound that `#13104` reshaped from a head-only window into head-plus-tail ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- fix(cache): fold tool_choice/tools/response_format into the semantic cache signature so a cached tool_calls response can no longer be replayed for a request whose tool policy forbids it (#12734) ([#13267](https://github.com/diegosouzapw/OmniRoute/pull/13267)) +- **fix(models):** stop treating provider-supplied `isFree:true`/`:free`/`0/0` flags as trusted unless the provider has a documented free tier — only the shipped free-tier catalog decides otherwise; fetched `isFree:true` trusted only for a free-tier provider, custom `isFree:true` via a trusted path ([#12744](https://github.com/diegosouzapw/OmniRoute/pull/12744)) — thanks @maxmad64bis +- fix(memory): authenticate the internal /v1/rerank loopback call so memory reranking no longer silently degrades to unranked order when REQUIRE_API_KEY=true (#12745) ([#13268](https://github.com/diegosouzapw/OmniRoute/pull/13268)) +- fix(sse): parse Ollama Cloud's current usage markup (`$X of $Y used` aria-label, nested width style) (#12749) ([#13260](https://github.com/diegosouzapw/OmniRoute/pull/13260)) +- fix(cli): setup-opencode no longer sends an active context's management token to `/v1/models` when `--api-key`/`OMNIROUTE_API_KEY` is supplied — an explicit flag or the env var now always outranks the context's token, and the flag itself is no longer swallowed by the parent program's global `--api-key` option (#12783) ([#13246](https://github.com/diegosouzapw/OmniRoute/pull/13246)) +- fix(sse): register Arcee AI in the executor provider registry so requests reach api.arcee.ai instead of silently falling back to OpenAI (#12784) ([#13277](https://github.com/diegosouzapw/OmniRoute/pull/13277)) +- **fix(quota):** share one upstream quota read when identical saturation checks arrive at the same time, so traffic bursts don't multiply provider API calls ([#12787](https://github.com/diegosouzapw/OmniRoute/pull/12787)) — thanks @maxmad64bis +- **fix(db):** ignore expired or invalid rate-limit cooldown writes so a stale timestamp can't lock a connection that should be usable — clearing still works as before ([#12788](https://github.com/diegosouzapw/OmniRoute/pull/12788)) — thanks @maxmad64bis +- **fix(providers):** single source for provider order with `xao` ranking alongside `xai-oauth` ([#12790](https://github.com/diegosouzapw/OmniRoute/pull/12790)) — thanks @maxmad64bis +- **fix(routing):** off-table models check the free-model catalog before inheriting premium prices, latency bootstraps from the observed pool median, async tiers read live database pricing with a 90-day freshness gate, and the tier cache invalidates on every pricing write ([#12792](https://github.com/diegosouzapw/OmniRoute/pull/12792)) — thanks @maxmad64bis +- **fix(auto-combo):** snapshot scoring uses the observed per-connection account tier and per-model quality instead of neutral constants, and circuit-open providers rank lower at build time ([#12794](https://github.com/diegosouzapw/OmniRoute/pull/12794)) — thanks @maxmad64bis +- **fix(pool):** empty `auto/*` pools now say why they are empty, and the models listing uses the same paid check as routing ([#12795](https://github.com/diegosouzapw/OmniRoute/pull/12795)) — thanks @maxmad64bis +- fix(routing): recognize CLIProxyAPI's 'unknown provider for model' 400 as fallback-worthy (#12800) ([#13284](https://github.com/diegosouzapw/OmniRoute/pull/13284)) +- **fix(sse):** translate-mode streams now emit the estimated token counts as a trailing usage-only chunk before `[DONE]` when the upstream stays silent, so metered chat clients see totals instead of nothing ([#12828](https://github.com/diegosouzapw/OmniRoute/pull/12828)) — thanks @maxmad64bis +- **fix(db):** provider stats stay truthful on empty and legacy databases: fallback counts default to `0` instead of `null`, latency averages read `null` (not `0`) when no durations were recorded, failures logged before the error-type column existed group under `pre_migration` instead of `unclassified`, and per-provider queries use two new composite indexes ([#12832](https://github.com/diegosouzapw/OmniRoute/pull/12832)) — thanks @maxmad64bis +- fix(nvidia): fail open when a synced model catalog goes stale instead of gating forever (#12849) ([#13248](https://github.com/diegosouzapw/OmniRoute/pull/13248)) +- **fix(db):** WAL maintenance lives in its own module: a `TRUNCATE` checkpoint that hits a busy database now warns with its streak and retries once via `PASSIVE` instead of logging success, and closing-time checkpoints no longer report success on builds without a database file, with the busy totals visible in the authenticated monitoring health payload ([#12853](https://github.com/diegosouzapw/OmniRoute/pull/12853)) — thanks @maxmad64bis +- **fix(health):** quota with no snapshots reads empty instead of a contradicting 0% ([#12857](https://github.com/diegosouzapw/OmniRoute/pull/12857)) — thanks @maxmad64bis +- fix(a2a): accept the dashboard's own session cookie on /a2a so "Run message/send" no longer fails with "Unauthorized: missing or invalid API key" (#12888) ([#13271](https://github.com/diegosouzapw/OmniRoute/pull/13271)) +- **fix(logs):** the Logs grid's in-memory filter pass no longer discards rows the SQL query already matched — selecting an API key from the dropdown (which sends the key's id) returns its calls again, the Combo tab shows every combo instead of only those whose name contains a "1", and the model filter and search cover the same columns as the query ([#12896](https://github.com/diegosouzapw/OmniRoute/pull/12896)) — fixes [#12873](https://github.com/diegosouzapw/OmniRoute/issues/12873) — thanks @ntdatt812 +- **fix(a2a):** `/api/a2a/status` now builds the agent card from the request that asked for it, so a gateway reached at a non-localhost host no longer advertises `http://localhost:20128` as its A2A URL ([#12918](https://github.com/diegosouzapw/OmniRoute/pull/12918)). — thanks @ntdatt812 +- **fix(compression):** progressive aging now appends its `[COMPRESSED:aging:…]` annotation after a turn's `tool_result` blocks instead of in front of them, so Anthropic no longer rejects aged conversations with "`tool_use` ids were found without `tool_result` blocks immediately after" ([#12920](https://github.com/diegosouzapw/OmniRoute/pull/12920)). — thanks @ntdatt812 +- **fix(bedrock):** model import now resolves context limits for every vendor prefix instead of only `anthropic.*`, so `global.openai.gpt-5.6-*` no longer imports with a null `inputTokenLimit` and gets rejected pre-flight at the 200k default ([#12921](https://github.com/diegosouzapw/OmniRoute/pull/12921)). — thanks @ntdatt812 +- **fix(stream):** the 64 KB stream buffer GLM asks for is honoured instead of dropped, and the type error it caused no longer fails the API Route Typecheck gate on every open PR ([#12925](https://github.com/diegosouzapw/OmniRoute/pull/12925)) — thanks @ntdatt812 +- **fix(guardrails):** mask PII inside a `tool_result`'s nested content array, which the masker walked past while redacting its sibling block ([#12930](https://github.com/diegosouzapw/OmniRoute/pull/12930)) — thanks @ntdatt812 +- **fix(dashboard):** the Radar catalog table no longer leaves absent data unexplained — empty limits, unknown context windows, and unreported capabilities each explain themselves on hover, and a new check keeps it that way ([#12937](https://github.com/diegosouzapw/OmniRoute/pull/12937)) — thanks @maxmad64bis +- **fix(sse):** geo-blocked opencode requests rotate to the next account proxy instead of failing, so one refused egress no longer aborts the whole chain ([#12941](https://github.com/diegosouzapw/OmniRoute/pull/12941)) — thanks @maxmad64bis +- **fix(codex):** dashboard "clear cooldown" and the CAS recovery path now drop nested `codexScopeRateLimitedUntil` maps in the same write that nulls `rate_limited_until`, and fresh quota snapshots with headroom lift fallback-sourced scope cooldowns parked by quota preflight ([#12817](https://github.com/diegosouzapw/OmniRoute/issues/12817), [#12860](https://github.com/diegosouzapw/OmniRoute/issues/12860), [#12951](https://github.com/diegosouzapw/OmniRoute/pull/12951)) The dashboard reset-credit button (`consumeCodexResetCredit`) now exercises that same snapshot path after a successful redeem, so a filled quota bar is enough to unpark a leftover fallback Codex child without another manual clear. — thanks @HouMinXi +- **fix(db):** resolve `sql-wasm.wasm` across global npm install and hoisted layouts, ensuring OmniRoute can boot cleanly on Node 24 when native `better-sqlite3` is uncompiled. ([#13035](https://github.com/diegosouzapw/OmniRoute/pull/13035)) — thanks @HouMinXi +- **fix(api):** `DELETE /v1/batches/delete-completed` now sweeps only the calling API key's own completed batches (batches with no owner stay out of a key-scoped sweep on purpose), with an explicit instance-wide mode reserved for authenticated dashboard sessions, a 401 for a presented key that is unknown, revoked, deactivated, banned or expired (never falling through to the session branch), audit logging of every sweep, a sanitized 500 on failure, an owner-scoped file half (a key-scoped sweep never nulls a file another tenant owns) and an atomic sweep — chunked in 200-batch transactions in instance mode — so a mid-way error never leaves a batch pointing at a nulled file (GHSA-wvxc-jp3v-5mg5) ([#12969](https://github.com/diegosouzapw/OmniRoute/pull/12969)) +- **fix(combo):** quota-weighted routing stops drawing on an out-of-credit connection — a 402 now invalidates the stored quota snapshot instead of leaving its stale remaining percentage in place, and a snapshot older than 10 minutes no longer counts as confident headroom for the primary pool ([#12972](https://github.com/diegosouzapw/OmniRoute/pull/12972)) ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- **fix(vision):** Custom Models with "Vision capable" checked no longer have image requests swapped to `glm/glm-4.6v` when the client sends the advertised alias (`vllm/path/...`) or the bare path-shaped id — Vision Bridge now matches the stored override for all three id forms ([#12758](https://github.com/diegosouzapw/OmniRoute/issues/12758)) ([#12974](https://github.com/diegosouzapw/OmniRoute/pull/12974)) — thanks @HouMinXi +- **fix(sse):** transient opencode upstream failures rotate to the next account proxy instead of failing, so one flapping egress no longer aborts the whole chain ([#12975](https://github.com/diegosouzapw/OmniRoute/pull/12975)) — thanks @maxmad64bis +- **fix(azure):** Deployments from GPT-6 onward now send `max_completion_tokens` instead of `max_tokens`, which Azure rejects with HTTP 400. The rule matched a literal `gpt-5`, so each new generation arrived broken; it now matches the generation range, while `gpt-35-turbo` still keeps `max_tokens`. ([#13007](https://github.com/diegosouzapw/OmniRoute/pull/13007)) — thanks @ntdatt812 +- **fix(db):** add an opt-in self-restart circuit for sustained critical memory pressure, gate post-cleanup VACUUM behind a minimum freed-rows threshold, and checkpoint the SQLite WAL every 5 minutes with a size guard that escalates to TRUNCATE, so a growing WAL can no longer stall the event loop into a full outage. ([#13011](https://github.com/diegosouzapw/OmniRoute/pull/13011)) — thanks @HouMinXi +- **fix(auth):** an explicit connection pin may probe a quota-disabled row once and re-enable it on success ([#12874](https://github.com/diegosouzapw/OmniRoute/issues/12874)) ([#13017](https://github.com/diegosouzapw/OmniRoute/pull/13017)) — thanks @HouMinXi +- **fix(api):** thread `X-OmniRoute-Fallback-Attempts` through combo chat completions so streaming and non-streaming responses report how many prior legs were attempted ([#13038](https://github.com/diegosouzapw/OmniRoute/pull/13038)) — thanks @HouMinXi +- Restore provider failure classification and credential refresh on non-streaming requests: classify non-2xx failures to lock models on per-model quota exhaustion, update connection rate limits from headers and body, and pass credential refresh handlers to pipeline execution so 401 tokens can be refreshed and retried (#13043). ([#13069](https://github.com/diegosouzapw/OmniRoute/pull/13069)) — thanks @HouMinXi +- **fix(responses):** wrap forced-non-streaming web_search fallback JSON as Responses SSE so Codex still sees `response.completed` ([#13050](https://github.com/diegosouzapw/OmniRoute/pull/13050)) — thanks @HouMinXi +- **fix(antigravity):** Preserve upstream thought-token usage in normalized completion and reasoning token counts ([#13055](https://github.com/diegosouzapw/OmniRoute/pull/13055)) — thanks @pacocartones +- Fix a concurrency-slot leak in the DeepSeek PoW solver: a worker that failed to spawn (for example a missing worker script) never released its slot, so `MAX_CONCURRENT_WORKERS` failures disabled the solver until restart. ([#13097](https://github.com/diegosouzapw/OmniRoute/pull/13097)) — thanks @anhtahaylove +- **fix(acp):** bound the ACP session output buffers — `stdoutBuffer` and `stderrBuffer` now cap at 1 MiB keeping the most recent output behind a visible `[...output truncated...]` marker, and `stderrBuffer` is reset per prompt instead of accumulating for the lifetime of the session. ([#13100](https://github.com/diegosouzapw/OmniRoute/pull/13100)) — thanks @anhtahaylove +- **fix(acp):** release the `stdout`/`exit` listeners and the idle timer that a `sendPrompt` timeout used to leave attached to the `acpManager` singleton, and drop sessions that exited on their own from the session map instead of keeping them forever. ([#13096](https://github.com/diegosouzapw/OmniRoute/pull/13096)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection and PII scanners now read the text a `tool_result` block carries on `content` (string or nested block list), in messages and in system blocks, so tool output is judged by the same rules as user text ([#13101](https://github.com/diegosouzapw/OmniRoute/pull/13101)) — thanks @ntdatt812 +- **fix(gamification):** close the badge notification SSE stream when the request signal is already aborted before the stream starts — a client that disconnects while the route is still awaiting auth used to leave both the 2s unlock poll and the 15s heartbeat running for the lifetime of the process. ([#13106](https://github.com/diegosouzapw/OmniRoute/pull/13106)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection scan now spends its 16 KB budget on both ends of the request instead of the first 16 KB only, so `system`, `instructions`, `query`, `documents` and the newest turns are no longer hidden behind one long message ([#13104](https://github.com/diegosouzapw/OmniRoute/pull/13104)) — thanks @ntdatt812 +- **fix(dashboard):** expose the Volcano Ark console cookie on quota scraping and unwrap connect-error objects so the dashboard shows the upstream message ([#13107](https://github.com/diegosouzapw/OmniRoute/pull/13107)) — thanks @HouMinXi +- **fix(db):** release the `beforeExit`/`SIGINT`/`SIGTERM` handlers when a `node:sqlite` adapter closes, so a closed adapter and its database handle are no longer pinned to `process` for the lifetime of the run — the same treatment #7494 gave the sql.js adapter. ([#13109](https://github.com/diegosouzapw/OmniRoute/pull/13109)) — thanks @anhtahaylove +- **fix(translator):** `contentSchema` and `unevaluatedItems` are now treated as subschema positions by the tool-schema sanitizer, so a truncation placeholder in either is replaced with a permissive schema instead of being forwarded as a string ([#13110](https://github.com/diegosouzapw/OmniRoute/pull/13110)) — thanks @ntdatt812 +- **fix(cli-helper):** clear the `createLogStream` timeout on the abort path — `stop()` aborts the in-flight fetch and returned through the `signal.aborted` branch, which skipped `clearTimeout` and left an armed timer per stopped stream. The stream reader is now also cancelled when the read loop exits early. ([#13114](https://github.com/diegosouzapw/OmniRoute/pull/13114)) — thanks @anhtahaylove +- **fix(docs):** drop TLS-impersonation, MITM-decrypt, supply-chain attestation, and XOR-mask writeups from the public `/docs` catalog and Docker image. Files stay in git for engineers; operators who need them open the repo, not the website. ([#13136](https://github.com/diegosouzapw/OmniRoute/pull/13136)) — thanks @HouMinXi +- **fix(combo):** parse numeric-epoch `rate_limited_until` in the combo cooldown read path ([#13141](https://github.com/diegosouzapw/OmniRoute/pull/13141)) — thanks @maxmad64bis +- **fix(opencode):** both OpenCode plugins now reject a gateway address typed without `http://` at configuration time, instead of publishing every model with an api url no client can call, and the v2 plugin no longer publishes a model card whose api url is blank or relative ([#13142](https://github.com/diegosouzapw/OmniRoute/pull/13142)) — thanks @maxmad64bis +- **fix(providers):** lock opencode model on upstream 400 model-unavailable ([#13146](https://github.com/diegosouzapw/OmniRoute/pull/13146)) — thanks @maxmad64bis +- **fix(logging):** keep the provider exchange rather than the raw client bodies when a call log exceeds its size budget, and show that recovered payload in the request-detail panel instead of replacing it with the stored response body ([#13147](https://github.com/diegosouzapw/OmniRoute/pull/13147)) — thanks @maxmad64bis +- **fix(traffic-inspector):** the WebSocket route no longer leaks a traffic-buffer subscriber and a 30s ping timer when the client socket is already closed at handler time — listeners are attached before any resource is acquired, a destroyed socket bails out early, and the ping interval stops on a dead socket where `write()` never throws ([#13155](https://github.com/diegosouzapw/OmniRoute/pull/13155)) — thanks @anhtahaylove +- **fix(telegram):** bound the per-user API key cache in the Telegram chat proxy so a burst of distinct chat ids can no longer grow the process heap without limit ([#13165](https://github.com/diegosouzapw/OmniRoute/issues/13165)) ([#13166](https://github.com/diegosouzapw/OmniRoute/pull/13166)) — thanks @anhtahaylove +- **fix(stream):** cancel the upstream response body when the JSON-to-SSE sniff unwinds on a body timeout, so a stalled upstream no longer pins the connection ([#13169](https://github.com/diegosouzapw/OmniRoute/issues/13169)) ([#13171](https://github.com/diegosouzapw/OmniRoute/pull/13171)) — thanks @anhtahaylove +- **fix(telegram):** authenticate webhook deliveries with Telegram's `secret_token` so an unauthenticated caller can no longer mint API keys or spend upstream quota ([#13172](https://github.com/diegosouzapw/OmniRoute/issues/13172)) ([#13175](https://github.com/diegosouzapw/OmniRoute/pull/13175)) — thanks @anhtahaylove +- **fix(models):** give discoverable Gemini 3.8 Flash ids their own 65536 output spec so Antigravity no longer clamps them to 16384 ([#13195](https://github.com/diegosouzapw/OmniRoute/pull/13195)) — thanks @HouMinXi +- **fix(dashboard):** leftover catalog-removed provider rows (gemini-cli) can be listed and purged from the providers page ([#13067](https://github.com/diegosouzapw/OmniRoute/issues/13067)) ([#13197](https://github.com/diegosouzapw/OmniRoute/pull/13197)) — thanks @HouMinXi +- **fix(auth):** a dashboard session now requires the `authenticated: true` claim that login, OIDC and the session refresh already emit — a JWT merely signed with `JWT_SECRET` (for example the Cursor CLI passthrough token, which any API-key holder can obtain) no longer verifies as the `auth_token` cookie on any route, the WebSocket handshake or the live server; existing sessions keep working ([#13298](https://github.com/diegosouzapw/OmniRoute/issues/13298)) ([#13375](https://github.com/diegosouzapw/OmniRoute/pull/13375)) +- **fix(api):** `DELETE /v1/batches/delete-completed` now applies the caller's API-key policy (endpoint allowlist, schedule, usage cap, rate limit) like every other `/v1` route, commits the key-scoped sweep per 200-batch chunk instead of holding one write lock for the whole sweep, refuses to loop without progress, and rejects a scope that names both a key and `allTenants` ([#13684](https://github.com/diegosouzapw/OmniRoute/pull/13684)) +- Restore the API-route typecheck gate: the non-streaming leg result lost its discriminated-union narrowing after the server-owned tool loop reassignment, producing 13 new TS2339 diagnostics in `chatCore.ts`. ([#12963](https://github.com/diegosouzapw/OmniRoute/pull/12963)) +- Restore four non-streaming behaviours the server-owned tool loop refactor dropped: client aborts map to 499 with the fixed `Request aborted` message, an aborted request no longer logs a synthetic `clientResponse`, Claude prompt-cache telemetry is recorded again, and a body that cannot be canonicalized no longer throws when the tool loop is off. ([#12990](https://github.com/diegosouzapw/OmniRoute/pull/12990)) +- **fix(skills):** The CLI registry parser now reads positionals declared with `.addArgument()`, not only those written inline in `.command()`. `tunnel create [type]` was being published as `tunnel create`, so the agent-skills sync gate reported drift on every branch and regenerating would have deleted the argument. ([#13009](https://github.com/diegosouzapw/OmniRoute/pull/13009)) — thanks @ntdatt812 +- **fix(combos):** dashboard combo test uses a short prompt, serial probes, and a 60s timeout so reasoning models and rate-limited free pools do not fail the health check ([#13001](https://github.com/diegosouzapw/OmniRoute/pull/13001)) — thanks @HouMinXi +- fix(compression): terminate idle worker threads on eviction so long-running instances stop leaking OS threads and MessagePorts ([#13091](https://github.com/diegosouzapw/OmniRoute/pull/13091)) — thanks @anhtahaylove +- Redact Google API keys of any length in error bodies: the pattern required exactly 39 characters, so shorter or longer `AIza…` credentials passed through unredacted. ([#12964](https://github.com/diegosouzapw/OmniRoute/pull/12964)) +- **fix(grok-cli):** a 402 "Grok Build usage balance exhausted" parks that Grok login as out of credit (Grok Build CLI, grok.com cookie, and xAI OAuth share the weekly pool). Combo routing then tries the next login instead of locking the model for every account in the pool ([#13061](https://github.com/diegosouzapw/OmniRoute/pull/13061)) — thanks @HouMinXi +- fix(compression): spawn the LLMLingua worker with a file URL object so compression actually runs instead of silently failing open on Node ([#13093](https://github.com/diegosouzapw/OmniRoute/pull/13093)) — thanks @anhtahaylove +- **fix(test):** run the local `test` and `test:unit` scripts at concurrency 4 so a full-suite run no longer exhausts the machine's commit charge and kills unrelated processes ([#13187](https://github.com/diegosouzapw/OmniRoute/pull/13187)) — thanks @anhtahaylove +- **fix(monitoring):** `GET /api/monitoring/health` `credentialHealth` now includes a bounded `failedConnections` list (`connectionId`, `status`, sanitized `lastError`) when the probe-cache gauge `failed>0`, plus `source: probe-cache` and a cheap `staleDbNonOkCount` for sticky SQLite `test_status` on active rows. Documents that the live gauge is not `provider_connections.test_status`. ([#12876](https://github.com/diegosouzapw/OmniRoute/pull/12876)) — thanks @RaviTharuma +- **fix(resilience):** an openai-compatible multi-upstream gateway no longer marks the whole connection `credits_exhausted` when a single upstream (or the `/models` / representative-model health probe) returns 402. Quota failures stay model-scoped; true connection-wide auth failures are unchanged. Single-credential 402 key disable ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239)) is preserved. ([#12875](https://github.com/diegosouzapw/OmniRoute/pull/12875)) — thanks @RaviTharuma +- **fix(dashboard):** nine fixes on the Orchestration Canvas, all diagnosed in the Phase 2 reviews. A source that fails now keeps the timestamp of its FIRST failure instead of being re-stamped every poll — the stale line said "since the last poll" no matter how long the source had been down, and the churn also defeated the snapshot's stable identity (it serializes the source list), so the canvas re-rendered on every tick while anything was broken. A source that HAD data and only then started failing is flagged too: previously only a source with no node at all got the warning, so a source that went down mid-session kept a healthy-looking node forever. The rest are pointwise: clicking a filter chip cancels the pending search debounce (left armed, it fired ~300ms later and silently reverted the chip); the search input carries an accessible name; `?state=running, failed` parses like the unpadded form instead of dropping the padded value; the CSV toggle helper is defined once in `model/urlParams.ts` rather than twice; the agents tab tells "nothing running" apart from "the filter matched nothing", offering a clear-filters button instead of setup links that would be wrong advice there; edges stop emitting SMIL particles above 40 simultaneously active edges, keeping the colored stroke; and the drawer's error banner clears when a retried action succeeds. ([#12983](https://github.com/diegosouzapw/OmniRoute/pull/12983)) +- **fix(i18n):** the nine locales added with the EU-language batch (Greek, Estonian, Irish, Croatian, Lithuanian, Latvian, Maltese, Slovenian, Serbian) were missing the eleven Orchestration Canvas keys that Phase 3 introduced, so the compare-runs panel and the "no runs match these filters" empty state fell back to English in those languages (`deepMergeFallback` substitutes English for an absent key, so nothing rendered blank — it rendered untranslated). The coverage gate does not catch this: it enforces an 80% floor per locale, and eleven missing keys out of ~13,000 leaves coverage at 99.9%. Translated for real in each language, calibrated against the wording each file already uses for "run", "filter" and "skill". ([#13294](https://github.com/diegosouzapw/OmniRoute/pull/13294)) +- **fix(dashboard):** five follow-ups from the Phase 2 Orchestration Canvas review. The History drawer now shows the memory section for runs that already left the live TTL window: the persisted `memory_hits` event is parsed into `metadata.memoryHits` with the same defensive validation the drawer applies, and — because that event is observability rather than a state transition — it no longer leaks into the timeline, where it had been inheriting the task's state and rendering as a duplicate transition. The A2A memory recall runs against its own 1.5s deadline instead of inheriting the memory backend's 30s one; overshooting degrades exactly like any other recall failure (no hits, task proceeds), and the timer is cleared on both paths. Repeating a Conductor run carries its `requirements.cli`/`requirements.model` forward, so the new run is pinned to the same runner profile and model rather than drifting to whatever the fleet picks. A successful repeat from the Agents tab now focuses the run it created, instead of leaving the operator on the finished one. And the auth test for `POST /api/conductor/tasks` moved into the shared `ROUTES` array rather than restating the pattern. ([#12988](https://github.com/diegosouzapw/OmniRoute/pull/12988)) +- fix(plugins): stop leaking an exit listener per plugin hook timeout, which triggered MaxListenersExceededWarning on plugins that ignore SIGTERM ([#13092](https://github.com/diegosouzapw/OmniRoute/pull/13092)) — thanks @anhtahaylove +- **fix(validation):** Provider node edits no longer fail with a generic "Invalid request" when the optional daily-quota reset fields are left blank. The dashboard sends `dailyQuotaResetTimezone` and `dailyQuotaResetHour` as `null`, and only the hour accepted it. ([#13066](https://github.com/diegosouzapw/OmniRoute/issues/13066)) ([#13083](https://github.com/diegosouzapw/OmniRoute/pull/13083)) — thanks @ntdatt812 +- **fix(combo):** quota-aware expansion drops banned, inactive, missing, and wrong-provider connections before quota fetch or model dispatch; pins and allowlists stay selectors, not a bypass. Antigravity automatic exhaustion now requires a reported zero remaining, so a positive balance below 1% stays eligible. ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- Fix an intermittent failure in the quota-weighted routing test suite: scores are a function of `Date.now()`, so peers with identical quota scored microseconds apart never tied and swapped order. ([#12946](https://github.com/diegosouzapw/OmniRoute/pull/12946)) +- fix(volcengine): resolve quota window mapping gaps, safe multi-connection console binding, and response cookie sanitization ([#12950](https://github.com/diegosouzapw/OmniRoute/pull/12950)) — thanks @HouMinXi +- **fix(test):** resolve the WebDAV handler path with `fileURLToPath` so the suite's 37 WebDAV tests run on Windows instead of failing with a doubled `C:\C:\` drive prefix ([#13196](https://github.com/diegosouzapw/OmniRoute/pull/13196)) — thanks @anhtahaylove +- **fix(ui):** fall back from invalid AnySearch icon alias (direct commit `9587e07b69`) +- **fix(sse):** stop rebuilding a truncated summary from the collector's cap-dropped event array ([#12718](https://github.com/diegosouzapw/OmniRoute/pull/12718)) — thanks @hartmark +- **fix(conversations):** show a pending spinner for unresolved tool nodes instead of "(empty)" ([#12727](https://github.com/diegosouzapw/OmniRoute/pull/12727)) — thanks @hartmark +- **fix(streaming):** fail fast when an upstream stream produces only lifecycle/heartbeat events, never real content ([#12741](https://github.com/diegosouzapw/OmniRoute/pull/12741)) — thanks @hartmark +- **fix(responses-continuation):** bridge the write-in-flight window with an in-memory pending store ([#12854](https://github.com/diegosouzapw/OmniRoute/pull/12854)) — thanks @hartmark +- **fix(translator):** drop or map agent_message on Chat Completions fallback ([#12880](https://github.com/diegosouzapw/OmniRoute/pull/12880)) — thanks @RaviTharuma +- **fix(api):** page and stream-complete GET /v1/models for large catalogs ([#12882](https://github.com/diegosouzapw/OmniRoute/pull/12882)) — thanks @RaviTharuma +- **fix(quota):** align AUTH window labels with usage API and clarify 503 ([#12884](https://github.com/diegosouzapw/OmniRoute/pull/12884)) — thanks @RaviTharuma +- **fix(ci):** clear two base-reds on release/v3.8.51 — mutation-coverage gate + image-only-model guard ([#12945](https://github.com/diegosouzapw/OmniRoute/pull/12945)) +- **fix(api):** scope batch bulk-delete to the calling API key ([#13211](https://github.com/diegosouzapw/OmniRoute/pull/13211)) +- **fix(tests):** align model-sync log assertion to shipped path redaction (base-red) ([#13227](https://github.com/diegosouzapw/OmniRoute/pull/13227)) +- **fix(test):** make the ToS heading guard actually require the parentheses ([#13228](https://github.com/diegosouzapw/OmniRoute/pull/13228)) +- **fix(quality):** register 4 drifted covering tests in stryker tap.testFiles ([#13229](https://github.com/diegosouzapw/OmniRoute/pull/13229)) +- **fix(logging):** mask *-api-key headers in request-log pipeline (#13273) ([#13401](https://github.com/diegosouzapw/OmniRoute/pull/13401)) — thanks @KooshaPari +- **fix(tests):** correct stale Jina catalog prefix in custom-models test ([#13403](https://github.com/diegosouzapw/OmniRoute/pull/13403)) — thanks @KooshaPari +- **fix(backend):** prune old backups after health-check-repair snapshots (#13308) ([#13404](https://github.com/diegosouzapw/OmniRoute/pull/13404)) — thanks @KooshaPari +- **fix(backend):** guard memory_fts_au trigger with WHEN clause to stop FTS bloat (#13326) ([#13405](https://github.com/diegosouzapw/OmniRoute/pull/13405)) — thanks @KooshaPari +- **fix(free-tiers):** remove obsolete Together $25 signup credit (#12526) ([#13407](https://github.com/diegosouzapw/OmniRoute/pull/13407)) — thanks @KooshaPari +- **fix:** skip non-chat generation models in Test all models ([#13409](https://github.com/diegosouzapw/OmniRoute/pull/13409)) — thanks @KooshaPari +- **fix(providers):** de-duplicate agy and antigravity model catalogs ([#13410](https://github.com/diegosouzapw/OmniRoute/pull/13410)) — thanks @KooshaPari +- **fix(dashboard):** send provider in body for mini-playground web search ([#13411](https://github.com/diegosouzapw/OmniRoute/pull/13411)) — thanks @KooshaPari +- **fix(resilience):** add CJK quota-exhaustion patterns to 429 classifier ([#13412](https://github.com/diegosouzapw/OmniRoute/pull/13412)) — thanks @KooshaPari +- **fix(backend):** eval runner: failed calls no longer score as passed + dotAll regex ([#13413](https://github.com/diegosouzapw/OmniRoute/pull/13413)) — thanks @KooshaPari +- **fix(backend):** eval runner opts out of output-style and memory injection ([#13414](https://github.com/diegosouzapw/OmniRoute/pull/13414)) — thanks @KooshaPari +- **fix:** chain serial quarantine step in npm test script (fixes #13286) ([#13418](https://github.com/diegosouzapw/OmniRoute/pull/13418)) — thanks @KooshaPari +- **fix(antigravity):** derive stable session ID from credentials for prompt-cache reuse (#13305) ([#13424](https://github.com/diegosouzapw/OmniRoute/pull/13424)) — thanks @KooshaPari +- **fix(memory):** merge memory into top-level system field instead of unshifting at messages[0] (#13425) ([#13427](https://github.com/diegosouzapw/OmniRoute/pull/13427)) — thanks @KooshaPari +- **fix(compression):** preserve instruction blocks from lossy engines ([#13523](https://github.com/diegosouzapw/OmniRoute/pull/13523)) — thanks @KooshaPari +- **fix(compression):** preserve polarity words and newlines in ultra heuristic ([#13524](https://github.com/diegosouzapw/OmniRoute/pull/13524)) — thanks @KooshaPari +- **fix(compression):** run user preservePatterns before built-in patterns (#13457) ([#13525](https://github.com/diegosouzapw/OmniRoute/pull/13525)) — thanks @KooshaPari +- **fix(reasoning):** expand deepseek-v4 regex to match v4.1+ and add command-code provider (#13430) ([#13528](https://github.com/diegosouzapw/OmniRoute/pull/13528)) — thanks @KooshaPari +- **fix(providers):** add alwaysReasons metadata for reka-flash-3 ([#13531](https://github.com/diegosouzapw/OmniRoute/pull/13531)) — thanks @KooshaPari +- **fix(playground):** allow Compare columns to scroll independently ([#13532](https://github.com/diegosouzapw/OmniRoute/pull/13532)) — thanks @KooshaPari +- **fix(security):** empty whitelist allows through to prevent admin lockout ([#13534](https://github.com/diegosouzapw/OmniRoute/pull/13534)) — thanks @KooshaPari +- **fix(tests):** add DISABLE_IOREG_STRATEGY env var for macOS test compatibility (fixes #13467) ([#13539](https://github.com/diegosouzapw/OmniRoute/pull/13539)) — thanks @KooshaPari +- **fix(zed-hosted):** map developer role and Gemini enums for Zed proxy ([#13543](https://github.com/diegosouzapw/OmniRoute/pull/13543)) — thanks @KooshaPari +- **fix(analytics):** normalize provider aliases to canonical id in usage_history ([#13545](https://github.com/diegosouzapw/OmniRoute/pull/13545)) — thanks @KooshaPari +- **fix(combo):** use traceId for call log id to prevent UNIQUE constraint failure ([#13546](https://github.com/diegosouzapw/OmniRoute/pull/13546)) — thanks @KooshaPari +- **fix(auth):** check model lockout before returning synthetic noauth connection ([#13547](https://github.com/diegosouzapw/OmniRoute/pull/13547)) — thanks @KooshaPari +- **fix(guardrails):** skip credential redaction for base64 image data URLs ([#13550](https://github.com/diegosouzapw/OmniRoute/pull/13550)) — thanks @KooshaPari +- **fix(resilience):** increase requestQueue.maxWaitMs default from 15s to 30s ([#13553](https://github.com/diegosouzapw/OmniRoute/pull/13553)) — thanks @KooshaPari +- **fix(docs):** document OMNIROUTE_READY_TIMEOUT_MS and allowlist the test-only DISABLE_IOREG_STRATEGY ([#13692](https://github.com/diegosouzapw/OmniRoute/pull/13692)) + ### 📝 Maintenance ---- +- **docs(readme):** reconcile live v3.8.50 provider, free-tier, CLI, routing, test, community, sponsor, acknowledgment, and SVG metrics with their audited source denominators, including a deduplicated OmniRoute-in-Action snapshot and distinct contributor rankings for merged pull requests, GitHub-attributed commits, and Git history ([#11356](https://github.com/diegosouzapw/OmniRoute/pull/11356)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **docs:** sync the canonical provider count 354 → 356 across `README.md`, `AGENTS.md`, `llm.txt` (+ 42 i18n mirrors), the four README SVG diagrams, `docs/reference/PROVIDER_REFERENCE.md` (regenerated) and the `package.json` description after Opper (#11629) and 1min.ai (#11631) boarded the catalog — closes the `check:docs-counts-sync` strict drifts that kept `release/v3.8.51` red ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11673](https://github.com/diegosouzapw/OmniRoute/pull/11673)) — thanks @jonlwheat2-gif +- **test(providers):** update count-derived assertions after the v3.8.51 provider additions — `APIKEY_PROVIDERS` 233 → 235 (Opper #11629 + 1min.ai #11631), reserved-prefix REGISTRY walk 395 → 398, `WEB_FETCH_PROVIDERS` now includes `nimble-search` (#11620), and the provider translate-path golden snapshot regenerated ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11674](https://github.com/diegosouzapw/OmniRoute/pull/11674)) — thanks @jonlwheat2-gif +- **test(sse):** bump the hard-lease connection-query inventory for `src/lib/tokenHealthCheck.ts` to 2 — the verify-only web-cookie sweep added by #11495 split the single `getProviderConnections` call into oauth + cookie variants, which the frozen inventory had not tracked ([#11495](https://github.com/diegosouzapw/OmniRoute/pull/11495)). ([#11677](https://github.com/diegosouzapw/OmniRoute/pull/11677)) — thanks @jonlwheat2-gif / @oyi77 +- **chore(release):** clear the `release/v3.8.51` base-red gates — docs migration count (+38 i18n mirrors), stryker `tap.testFiles`, lockfile registry host, stale ESLint suppressions, the 7 lint regressions they were hiding, the stale `APIKEY_PROVIDERS` partition count (231 → 233), the `glm-5.3-max` tier missing from the GLM test table, a dead `modelType === "chat"` comparison in the models catalog, and the `resolveLiveWsUrl` / `sanitizeLiveWsPort` helpers from #11388 that a later merge dropped from `wsPath.ts` (dashboard live WebSocket URL resolution) ([#11502](https://github.com/diegosouzapw/OmniRoute/pull/11502)) — thanks @MumuTW +- **chore(free-models):** the free-tier regime table now declares which totals figure each regime feeds and whether it may take the no-auth shortcut, so the sets that used to repeat those answers by hand are derived from it — a new `freeType` no longer compiles until it has answered every question, instead of silently contributing to no total ([#11537](https://github.com/diegosouzapw/OmniRoute/pull/11537)) — thanks @maxmad64bis +- **docs(routing):** LKGP strategy copy now states provider-level stickiness and the explicit rules fallback instead of promising per-target pinning ([#11555](https://github.com/diegosouzapw/OmniRoute/pull/11555)) — thanks @maxmad64bis +- **test(chatcore):** pin the response tool-name alias resolution against the ordering hazard that broke every Gemini/Antigravity MCP tool call in v3.8.49. `extractRequestToolIdentityMap` deletes `translatedBody._toolNameMap`, so the later read is always undefined and the ledger survives only through the `requestToolIdentityMap` fallback — removing that fallback previously left the entire tool-name suite green. The resolution moves into `resolveResponseToolNameMap()` next to the map it depends on, with a regression guard that fails without the recovery. No behaviour change. ([#11575](https://github.com/diegosouzapw/OmniRoute/pull/11575)) — thanks @amartinawi +- **docs(env):** document `OMNIROUTE_CORPUS_CACHE_SIZE` in `.env.example` and `ENVIRONMENT.md`, so the env/docs contract check stops failing Docs Gates on every branch ([#11576](https://github.com/diegosouzapw/OmniRoute/pull/11576)) — thanks @ntdatt812 +- **chore(stryker):** register `route-guard-tunnel-processes-local-only` and `universal-quota-aware-routing` in `tap.testFiles`, so their mutant kills count for the three modules they cover ([#11577](https://github.com/diegosouzapw/OmniRoute/pull/11577)) — thanks @ntdatt812 +- **test(authz):** pin `LOCAL_ONLY_API_GET_EXEMPTIONS` by exact membership instead of by entry count, so the guard names the offending path and also catches a substitution ([#11580](https://github.com/diegosouzapw/OmniRoute/pull/11580)) — thanks @ntdatt812 +- **test(build):** pin `resolveNextBuildBundlerFlag` to the `OMNIROUTE_USE_TURBOPACK` contract it implements, across runtimes, instead of asserting an unimplemented Bun override ([#11583](https://github.com/diegosouzapw/OmniRoute/pull/11583)) — thanks @ntdatt812 +- **test(claude-web):** the Claude Web transport test's barrel anchors no longer pin the exact construction spelling. #11421 made the executor map lazy, so `/"claude-web": new ClaudeWebExecutor\(\)/` stopped matching and the test went red on `release/v3.8.51` itself — a base-red gate every open PR inherited. The anchors now match the alias key and the class name and tolerate either spelling in between, and were checked to still fail when an alias is genuinely repointed at another executor ([#11585](https://github.com/diegosouzapw/OmniRoute/pull/11585)) — thanks @ntdat812 +- **test(models):** pin `qwen3.8-max` resolution per provider — two of the four catalogs now serve the bare id and no longer carry `-preview` ([#11590](https://github.com/diegosouzapw/OmniRoute/pull/11590)) — thanks @ntdatt812 +- **test(executors):** drop the stale 128-tool truncation assertion that contradicted the #11444 pass-through contract ([#11591](https://github.com/diegosouzapw/OmniRoute/pull/11591)) — thanks @ntdatt812 +- **test(token-health):** pin the current terminal-skip boundary — expired with retry budget is probed, `account_deactivated` stays skipped ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)) — thanks @ntdatt812 +- **test(models):** follow #11503's retarget of the `gemini-3-pro-high` alias to the hyphenated catalog id ([#11593](https://github.com/diegosouzapw/OmniRoute/pull/11593)) — thanks @ntdatt812 +- **refactor(video bridge):** extract per-video acquisition, whole-result caching, description, and metrics/abort/cleanup out of `VideoBridgeGuardrail.preCall` into a `processVideoPart` seam in a new `videoBridgePipeline.ts`, behind explicit `VideoMediaBrokerPort`, `VideoAudioTranscriptionPort`, and `VideoDrilldownPort` boundaries; `preCall` now only handles request traversal, policy, and response aggregation. The Video tab's FFmpeg/ffprobe runtime status is now an explicit `unknown` / `restricted` / `unavailable` / `available` state instead of a nullable boolean pair, fixing a case where an in-flight or failed probe was mislabeled as "install FFmpeg" ([#11657](https://github.com/diegosouzapw/OmniRoute/issues/11657)). ([#12016](https://github.com/diegosouzapw/OmniRoute/pull/12016)) +- **test(opencode-plugin):** add unit test coverage for `formatFreeBudget()` naming helper ([#11660](https://github.com/diegosouzapw/OmniRoute/pull/11660)) — thanks @f9td56dbgh-hub +- **docs(video):** clarify that the Video Bridge transcript `source` field (`client`, `embedded`, `audio-bridge`) is presently caller-declared and not yet server-verified — OmniRoute enforces the enum shape but does not cryptographically confirm that an `embedded`/`audio-bridge` label came from a server-owned extraction ([#11661](https://github.com/diegosouzapw/OmniRoute/issues/11661)). ([#12001](https://github.com/diegosouzapw/OmniRoute/pull/12001)) +- **chore(providers):** retire the distributed Raycast Relay and Hailuo Web integrations after a provenance audit confirmed substantial derivation from GPL-3.0 sources; the official GitHub and MiniMax API providers remain available. Migration 163 disables legacy connections and invalidates their active leases while preserving connection IDs, audit/history rows, quota snapshots, usage records, and fail-closed API-key connection policies. ([#11691](https://github.com/diegosouzapw/OmniRoute/pull/11691)) +- **chore(providers):** retire the Felo Web provider while its GPL-derived provenance remains on licensing HOLD ([#11698](https://github.com/diegosouzapw/OmniRoute/pull/11698)) — stale runtime and database references now fail closed while existing connection, lease, and usage history remains preserved for auditability. +- **fix(sse):** retire the Gemini Web image-generation bridge while provenance and licensing remain on HOLD, preserving Gemini Web chat/auth/tools and legitimate Gemini/Nano Banana image providers ([#11708](https://github.com/diegosouzapw/OmniRoute/pull/11708)) +- **chore(audio):** retire the built-in EdgeTTS provider and its 11-voice catalog while provenance/licensing review remains on HOLD; the generic `/v1/audio/speech` endpoint and the other speech providers remain available ([#11711](https://github.com/diegosouzapw/OmniRoute/pull/11711)) +- **fix(providers):** retire the unofficial Qwen Web integration and its `qwen-web`/`qw` identities while third-party provenance remains on HOLD ([#11713](https://github.com/diegosouzapw/OmniRoute/pull/11713)) — direct requests now fail closed with a sanitized HTTP 410 and combos skip the retired target, while migration guards preserve connection IDs, allowlists, usage/audit history and invalidate active leases. Supported Qwen API, Qwen Code, cloud, token-plan, audio, local and model-name surfaces remain available; restoring Qwen Web requires a qualified provenance/license decision. +- **fix(providers):** retire `microsoft-designer-web` and its `msdesigner` alias from runtime and catalogs while preserving stored credentials, provider history and allowlists as inactive records — source dependency on gpt4free's [`MicrosoftDesigner.py`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/g4f/Provider/needs_auth/MicrosoftDesigner.py) is proven at snapshot `0943d16eef757a44deb60651c07bbe5fa958cafe`, whose [`LICENSE`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/LICENSE) is GPL-3.0; expressive adaptation is probable, which records provenance/licensing risk without asserting a legal conclusion ([#11720](https://github.com/diegosouzapw/OmniRoute/pull/11720)). +- **docs(legal):** centralize pinned MIT notices for the vendored GCF codec and local flag assets ([#11726](https://github.com/diegosouzapw/OmniRoute/pull/11726)) +- Split the 2,952-line `adobeFireflyClient.ts` into nine focused modules (catalog, credentials, payload, ARP session, upload, responses, IMS, account, poll) behind an unchanged barrel — no consumer edits and no public API change (#11728). — thanks @MumuTW +- **fix(dashboard):** replaced the Model Lockout card's unprovenanced notification MP3 with a short, locally synthesized Web Audio chime ([#11731](https://github.com/diegosouzapw/OmniRoute/pull/11731)) — both toggles retain optional audible feedback without shipping replacement media or third-party code, and unsupported, suspended, or rejected audio contexts fail open without blocking the setting change. +- **chore(deepseek-web):** remove the provenance-unresolved DeepSeek PoW WASM binary and its runtime loader/tracing while retaining the existing JavaScript solver (slower at high difficulty) ([#11732](https://github.com/diegosouzapw/OmniRoute/pull/11732)). +- **chore(assets):** remove four unused provider assets whose redistribution provenance could not be verified while retaining the active Freebuff SVG ([#11735](https://github.com/diegosouzapw/OmniRoute/pull/11735)) +- **docs(assets):** retired two raster images whose redistribution provenance could not be verified ([#11736](https://github.com/diegosouzapw/OmniRoute/pull/11736)) — PicoClaw documentation now reuses the existing SVG, while Zoo Code remains a text link without an image. +- **docs(legal):** add pinned notices for six LobeHub-derived provider SVGs and 65 byte-exact theSVG assets, retaining registry claims as unverified metadata with explicit trademark and HOLD boundaries ([#11737](https://github.com/diegosouzapw/OmniRoute/pull/11737)). +- **chore(quality):** added a versioned 225/225 provider-asset provenance manifest and blocking drift gate ([#11743](https://github.com/diegosouzapw/OmniRoute/pull/11743)) — the inventory pins magic MIME and SHA-256 for every physical file, records immutable source matches for 71 assets, keeps 69 probable and 85 unresolved entries explicit but non-blocking, and treats provenance as source evidence rather than copyright or trademark clearance. +- **refactor(mitm):** replace four previously derived Traffic Inspector utilities — conversation normalization, SSE merging, secret masking, and header sanitization — with independent clean-room implementations based on public provider schemas, WHATWG SSE, and RFC 6750, while keeping the two independently classified passthrough surfaces unchanged ([#11748](https://github.com/diegosouzapw/OmniRoute/pull/11748)). +- Added a unit test that fails seven days before any dated pack under `config/` (`validUntil` and sibling keys) lapses, naming the file and key. The Alibaba free-tier pack expired on 2026-08-27 and turned every PR red the next morning with no commit involved; renewal now happens on someone's terms, not the clock's. ([#11891](https://github.com/diegosouzapw/OmniRoute/pull/11891)) +- `check:workflows` now fails (under `--strict`/`--ratchet`) when any job routed to a self-hosted runner publishes with `--provenance` — npm rejects that with `422` at the registry, which in v3.8.50 only surfaced after the tag and Docker images were public. ([#11895](https://github.com/diegosouzapw/OmniRoute/pull/11895)) +- `scripts/ops/runner-janitor.sh` now proves a path is idle with one `lsof` snapshot and removes stale leftovers itself (tmpfs after 3 h — it is RAM — disk after 24 h), kills orphan `next-build` processes, prunes checkouts of stopped runners, and alerts on memory pressure; `--dry-run` shows exactly what it would do. `docs/ops/RUNNER_BOX.md` reconciled to the measured box (31 GB, 10 listeners). ([#11893](https://github.com/diegosouzapw/OmniRoute/pull/11893)) +- The `next-build` artefact (1.3 GB) is now written and read under `$RUNNER_TEMP` (per-runner, on disk) instead of `/tmp`, which on the self-hosted pool is a 12 GB tmpfs in RAM. Landing it there took 27–32 of the publish job's 76 minutes, and the fixed `/tmp/e2e-build.tar.gz` name let E2E jobs on different runners overwrite each other's download. ([#11896](https://github.com/diegosouzapw/OmniRoute/pull/11896)) +- The CI `build` job now runs in two concurrency lanes — `main` and pull requests — so a release build is never queued behind (or OOM-killed beside) PR builds on the self-hosted pool, which holds one `next-build` comfortably and two at the edge. ([#11901](https://github.com/diegosouzapw/OmniRoute/pull/11901)) +- Re-freeze the ESLint suppressions on `release/v3.8.51` from a clean-room measurement (2 stale file entries pruned, 55 pre-existing `no-explicit-any` in six new files frozen under #11924) and drop the dead `GPT_SIZE_MAP` constant orphaned by the Adobe Firefly client split, so `No new ESLint warnings` stops failing every PR with exit 2 (Refs #11924) ([#11955](https://github.com/diegosouzapw/OmniRoute/pull/11955)) +- Type the 55 `no-explicit-any` sites that had been frozen under #11924 — four redundant casts in `socksConnectorWithFamily.ts` (undici/socks types already accept them) and the mocks/fixtures of the socks-timeout and isFree suites — and drop their suppression entries; the ESLint ratchet shrinks from 5487 to 5432 (Closes #11924) ([#11975](https://github.com/diegosouzapw/OmniRoute/pull/11975)) +- Take the two hosted-runner builds off the PR rail: `Build App` (`build.yml`) is `workflow_dispatch`-only and `DAST smoke (PR)` runs only for PRs into `main` — the 7 GB hosted VM cannot build `release/v3.8.51` in any profile (19/30 red, VM shutdown ~8 min into `next build`) and both checks had turned into permanent noise on every release PR; the bundle stays validated by `ci.yml` on `main` and by `nightly-release-green` on `release/**` (Closes #11946) ([#11962](https://github.com/diegosouzapw/OmniRoute/pull/11962)) +- Move the four nightly jobs that build the backend (`nightly-schemathesis`, `nightly-llm-security` promptfoo + garak, `nightly-resilience` axe-a11y) off the hosted 7 GB runner — where they died on `release/v3.8.51` unseen — onto the box's new `omni-light` pool (two listeners), and document the reshaped fleet (4 active OmniRoute listeners: 2 `omni-build` + 2 `omni-light`, janitor ceiling 4) (Closes #11965) ([#11967](https://github.com/diegosouzapw/OmniRoute/pull/11967)) +- Stop painting every fork PR into `release/**` red: `quality.yml` `Build (advisory)` is skipped (GitHub still reports `continue-on-error` failures as check FAILURE). Hosted `ubuntu-latest` cannot finish `npm run build` on this tree — same class as #11962 taking `build.yml` off the PR rail. `docker-publish.yml` amd64 now runs on the `.113` `omni-build` pool (31 GB / 32 cores, two listeners) with Turbopack, shares the `heavy-build-main` lane with `ci.yml` `Build` so it queues instead of becoming a third heavy, and keeps per-ref concurrency (a merge storm was starting 8 concurrent OOM builds). arm64 stays on `ubuntu-24.04-arm` with webpack — there is no ARM box. Fallback when `USE_VPS_RUNNER` is off: hosted amd64 + webpack (#11976). ([#12021](https://github.com/diegosouzapw/OmniRoute/pull/12021)) +- `docker-publish.yml` builds with webpack (`OMNIROUTE_USE_TURBOPACK=0`) on **both** arches, including amd64 on the `.113` `omni-build` pool. Turbopack had the RAM (31 GB, `omniroute-113-6`) and still panicked (`TurbopackInternalError: there must be a path to a root`, run 33253576569, 26 min in). The same tree's arm64 webpack build on hosted `ubuntu-24.04-arm` succeeded (run 33264823398). Dockerfile already documents webpack as the Docker escape hatch (#11976). ([#12050](https://github.com/diegosouzapw/OmniRoute/pull/12050)) +- **docs(free-tier):** declare the counting vs deciding regimes for "is it free?" and guard the deciding path from DB-backed catalog resolution ([#12226](https://github.com/diegosouzapw/OmniRoute/pull/12226)) — thanks @maxmad64bis +- **docs(auto-combo):** The mode pack table in `docs/routing/AUTO-COMBO.md` now lists all six shipped packs with every weight each one sets, replacing a four-pack table whose numbers had also drifted from the source. It states plainly that no pack sets `quality`, so selecting any pack silences the observed-quality signal. Six more documents that quote the scoring factor count joined the `check:docs-counts` gate, which caught five stale claims — including one naming nine factors that do not exist — and two stale mode pack counts. The dashboard routing panel, which offered four of the six packs and labelled the default strategy "6-Factor Scoring", is now covered by a test; the two packs it was missing are `reliability-first` and `chaos-mode`, the latter labelled as the fault-injection profile it is rather than as one more routing preference ([#12316](https://github.com/diegosouzapw/OmniRoute/pull/12316)) — thanks @maxmad64bis +- **chore(quality):** rebaseline `src/lib/db/apiKeys.ts` for the ACL the key-creation path now preserves ([#12352](https://github.com/diegosouzapw/OmniRoute/pull/12352)) ([#12673](https://github.com/diegosouzapw/OmniRoute/pull/12673)) — thanks @KrzysiekSko +- **chore(providers):** bump the Claude Code wire identity and the Devin bridge image pin from `2.1.220` to `2.1.258` ([#12402](https://github.com/diegosouzapw/OmniRoute/pull/12402)) — thanks @ggiak ([#12604](https://github.com/diegosouzapw/OmniRoute/pull/12604)) +- **docs(env):** align `.env.example`, the README Bun section, and the troubleshooting guide with the code: `OMNIROUTE_USE_TURBOPACK` also governs `npm run build` (not dev-only), `bun run build` follows that flag instead of auto-selecting Webpack, `OMNIROUTE_CHAT_MAX_HEAVY_IN_FLIGHT` is unset by default (no request-count cap), and the structural `503 chat_admission_busy` message matches `chatAdmissionResponses.ts` (#12404 — thanks @pacocartones) +- **chore(electron):** upgrade the desktop app to Electron 44 (Chromium 152, Node 24.18.1) ([#12217](https://github.com/diegosouzapw/OmniRoute/pull/12217)). **Requires macOS 13 (Ventura) or later** — Chromium dropped macOS 12 (Monterey), so Monterey users must stay on an earlier OmniRoute desktop build. Windows and Linux are unaffected; the app already shipped only x64/arm64, so Electron 44 dropping 32-bit builds changes nothing. Removes the `openAsHidden`/`wasOpenedAsHidden` login-item fields deleted in Electron 44 — hidden autostart continues to work through the `--hidden` argument registered with the login item ([#12554](https://github.com/diegosouzapw/OmniRoute/pull/12554)) — thanks @app/dependabot +- **chore(quality):** rebaseline `src/sse/handlers/chat.ts` for the effective-input persistence the continuation fix needs ([#12641](https://github.com/diegosouzapw/OmniRoute/pull/12641)) ([#12680](https://github.com/diegosouzapw/OmniRoute/pull/12680)) — thanks @hartmark +- `Coverage` job on `ci.yml`: the informational Codecov upload gets its own 5-minute ceiling and `continue-on-error`, and the job budget grows from 20 to 30 minutes (the 8-shard c8 merge alone takes ~10) — a stalled upload no longer ends the job `cancelled` and drags a fully green `main` run's conclusion down with it ([#11972](https://github.com/diegosouzapw/OmniRoute/pull/11972)) +- CI hardening on the PR rail: the four `Unit Tests fast-path` shards get `timeout-minutes: 30` (a hung shard held a PR for 64 min instead of GitHub's 6 h default) and both ESLint file caches lose their `restore-keys` fallback, so a cache built under another suppressions file or lint config can no longer report stale verdicts (Refs #11600, #11924) ([#11963](https://github.com/diegosouzapw/OmniRoute/pull/11963)) +- Every CI job that runs a `next build` (`build`, the npm `publish`, both release-green validations) now targets the `omni-build` runner label, which only two of the eight self-hosted runners carry. The box holds one build comfortably and two at the edge; a third now queues on GitHub instead of being OOM-killed by the kernel. ([#11932](https://github.com/diegosouzapw/OmniRoute/pull/11932)) +- **refactor(combo):** move `handleRoundRobinCombo` (and `resolveTargetTokenLimit`) into `open-sse/services/combo/roundRobinCombo.ts`. `combo.ts` drops from 2164 to 1014 lines (`split("\n").length`); the leaf is 1199 (under the 1200 new-file cap). The round-robin call site uses a dynamic `import()` so `releaseStickyPinOnFailure` / `clearStaleLKGP` can stay exported from `combo.ts` without a static cycle. Skip / sticky / semaphore / safety-timer behavior is unchanged. ([#12811](https://github.com/diegosouzapw/OmniRoute/pull/12811)) — thanks @HouMinXi +- **chore(quality):** rebaseline the file-size caps the error-boundary campaign grew past (`open-sse/executors/codex.ts`, `open-sse/vendor/codex-chatgpt-web/bridge.ts`, both via [#12444](https://github.com/diegosouzapw/OmniRoute/pull/12444)) ([#12654](https://github.com/diegosouzapw/OmniRoute/pull/12654)) +- **chore(quality):** rebaseline the file-size caps the hartmark batch grew past (`combos/page.tsx` via [#12355](https://github.com/diegosouzapw/OmniRoute/pull/12355), `open-sse/services/combo.ts` via [#12338](https://github.com/diegosouzapw/OmniRoute/pull/12338)) ([#12623](https://github.com/diegosouzapw/OmniRoute/pull/12623)) — thanks @hartmark +- **chore(quality):** rebaseline the file-size caps the HouMinXi batch grew past when its PRs stacked (`providers/page.tsx`, `chatCore.ts`, `accountFallback.ts`) — each PR measured correctly in isolation, none saw the stacking ([#12619](https://github.com/diegosouzapw/OmniRoute/pull/12619)) +- **chore(quality):** rebaseline `open-sse/services/combo.ts` for the reset-aware scoring the HouMinXi batch stacked ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) ([#12678](https://github.com/diegosouzapw/OmniRoute/pull/12678)) — thanks @HouMinXi +- **docs:** sync provider catalog count (direct commit `4f11b2ae3d`) +- **docs(skills):** sync omni-inference search fallback (direct commit `b4e76a7ed9`) +- **chore(lint):** prune two stale base suppressions (direct commit `1325047d56`) +- **test(auto-combo):** tolerate full-suite rotation load (direct commit `ad3e293f9f`) +- **test:** drain inherited quality snapshot reds (direct commit `21ed68d8ac`) +- **chore(quality):** rebaseline measured CLI bundle growth (direct commit `d35b3f9779`) +- **test(ui):** reproduce invalid AnySearch icon alias (direct commit `12cc6ca834`) +- **test(translator):** fix the relative imports of the relocated deferred-finish test (direct commit `777d9d1629`) +- **docs(counts):** bring the documented provider and migration counts to the measured values (direct commit `e70bea30e9`) +- **test(security):** drop the guard-target set left unused by the sync (direct commit `a32ba0e362`) +- **chore(db):** renumber model_capabilities migration 163 → 169 (cross-branch collision with 163_radar_feed_cache_generated_at) (direct commit `70f5d4cbf9`) +- **docs(cli):** document CLI_PRIME_AGENT_BIN and correct the CLI Agents count (direct commit `9058e39b61`) +- **chore(deps):** refresh runtimes and adopt ESLint 10 ([#11259](https://github.com/diegosouzapw/OmniRoute/pull/11259)) — thanks @backryun +- **refactor(local-corpus):** implement dynamic root resolution and LRU cache ([#11491](https://github.com/diegosouzapw/OmniRoute/pull/11491)) — thanks @raheemuddin786 +- **chore(quality):** prune stale eslint-suppressions entries ([#11596](https://github.com/diegosouzapw/OmniRoute/pull/11596)) +- **chore(ci):** unblock quality gates - env-doc sync + stryker tap.testFiles registration (#9282) ([#11609](https://github.com/diegosouzapw/OmniRoute/pull/11609)) — thanks @Meet6338-X +- **chore(ci):** point the circular-deps gate at dpdm's real JS entrypoint ([#11615](https://github.com/diegosouzapw/OmniRoute/pull/11615)) — thanks @benzntech +- **chore(quality):** rebaseline file-size for #11448/#11495/#11561 growth ([#11630](https://github.com/diegosouzapw/OmniRoute/pull/11630)) +- **deps:** realign onnxruntime-node pin to @huggingface/transformers' 1.24.3 lockstep ([#11633](https://github.com/diegosouzapw/OmniRoute/pull/11633)) — thanks @jonlwheat2-gif +- **test(autoCombo):** port models_dev_tier #11508 guard from node:test to vitest ([#11635](https://github.com/diegosouzapw/OmniRoute/pull/11635)) — thanks @jonlwheat2-gif +- **chore(quality):** rebaseline file-size for #11083/#11631/#11628 growth ([#11636](https://github.com/diegosouzapw/OmniRoute/pull/11636)) +- **chore(lint):** prune stale ESLint suppressions (base-red #11449) ([#11644](https://github.com/diegosouzapw/OmniRoute/pull/11644)) — thanks @jonlwheat2-gif +- **docs(changelog):** aggregate the ten v3.8.50 fragments into CHANGELOG.md ([#11683](https://github.com/diegosouzapw/OmniRoute/pull/11683)) +- **chore(search):** drop unreachable credential sweep after #11642 ([#11687](https://github.com/diegosouzapw/OmniRoute/pull/11687)) — thanks @HouMinXi +- **ci:** add API route TypeScript regression gate ([#11705](https://github.com/diegosouzapw/OmniRoute/pull/11705)) — thanks @Sabeekhann +- **docs(dedup):** correct the CodeQL claim on the tenant-namespace comment ([#11712](https://github.com/diegosouzapw/OmniRoute/pull/11712)) +- **docs(changelog):** add consolidated v3.8.50 stats, top-25 ranking and a .mailmap ([#11715](https://github.com/diegosouzapw/OmniRoute/pull/11715)) +- **docs:** recommend gstack for AI-assisted workflows ([#11770](https://github.com/diegosouzapw/OmniRoute/pull/11770)) — thanks @santosraju99-hub +- **chore(quality):** tighten complexity/cognitive-complexity ratchets to the current tip; land the missed gateways.ts rebaseline ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **docs(i18n):** finish Freepik → Magnific rebrand in locale strings and README ([#11772](https://github.com/diegosouzapw/OmniRoute/pull/11772)) — thanks @turbolego +- **refactor(auto-combo):** fix divergent scoring in combo health reporting ([#11854](https://github.com/diegosouzapw/OmniRoute/pull/11854)) — thanks @jacobsparts +- **chore(changelog):** add missing fragments for #11919/#11918/#11916 ([#11938](https://github.com/diegosouzapw/OmniRoute/pull/11938)) +- **test(check):** escape the runs-on fixture with JSON.stringify, not a quote-only replace ([#11942](https://github.com/diegosouzapw/OmniRoute/pull/11942)) (direct commit `fb7445eaa3`) +- **test(release):** align five suites with the contracts #11933, #11919 and #11876 shipped on release/v3.8.51 ([#11944](https://github.com/diegosouzapw/OmniRoute/pull/11944)) +- **chore(quality):** remove unreachable code and restore test discovery ([#11950](https://github.com/diegosouzapw/OmniRoute/pull/11950)) — thanks @backryun +- **docs(agents):** sync-back landings are fast-forward, never squash ([#11964](https://github.com/diegosouzapw/OmniRoute/pull/11964)) +- **test(infra):** retry recursive temp-dir removal instead of failing a shard on ENOTEMPTY (#11966) ([#11968](https://github.com/diegosouzapw/OmniRoute/pull/11968)) +- **docs(ops):** the .113 heavy-build ceiling is one runner, not two ([#12048](https://github.com/diegosouzapw/OmniRoute/pull/12048)) +- **test(ui):** correct inactive auto-fetch expectation ([#12098](https://github.com/diegosouzapw/OmniRoute/pull/12098)) — thanks @RaviTharuma +- **docs:** document native dependency check escape hatch ([#12101](https://github.com/diegosouzapw/OmniRoute/pull/12101)) — thanks @RaviTharuma +- **test(providers):** regenerate the translate-path golden for OrcaRouter (#11923) ([#12118](https://github.com/diegosouzapw/OmniRoute/pull/12118)) +- **chore(quality):** velocity phase — loosen every numeric baseline by 20% until v4.0, monitor headroom nightly ([#12125](https://github.com/diegosouzapw/OmniRoute/pull/12125)) +- **chore(lint):** batch 0 of #12146 — type the call-log-cap sqlite rows instead of 45 as-any casts ([#12157](https://github.com/diegosouzapw/OmniRoute/pull/12157)) +- **chore(lint):** batch 4 of #12146 — shared/components react-hooks violations resolved ([#12159](https://github.com/diegosouzapw/OmniRoute/pull/12159)) +- **chore(lint):** batch 1 of #12146 — dashboard/cli-code react-hooks violations resolved ([#12160](https://github.com/diegosouzapw/OmniRoute/pull/12160)) +- **chore(lint):** batch 3 of #12146 — dashboard/settings react-hooks violations resolved ([#12162](https://github.com/diegosouzapw/OmniRoute/pull/12162)) +- **chore(lint):** batch 2 of #12146 — resolve the react-hooks compiler violations in dashboard/providers ([#12163](https://github.com/diegosouzapw/OmniRoute/pull/12163)) +- **chore(quality):** register search-432 cooldown test in stryker tap.testFiles ([#12170](https://github.com/diegosouzapw/OmniRoute/pull/12170)) +- **chore(lint):** batch 5 of #12146 — combos, endpoint, provider-stats, api-manager and costs react-hooks violations resolved ([#12174](https://github.com/diegosouzapw/OmniRoute/pull/12174)) +- **build:** add contributor fast profile ([#12192](https://github.com/diegosouzapw/OmniRoute/pull/12192)) — thanks @rafacpti23 +- **docs(readme):** README touch-ups ([#12193](https://github.com/diegosouzapw/OmniRoute/pull/12193)) ([#12194](https://github.com/diegosouzapw/OmniRoute/pull/12194)) ([#12202](https://github.com/diegosouzapw/OmniRoute/pull/12202)) — thanks @rafacpti23 +- **build:** force the webpack bundler in the contributor build profile ([#12197](https://github.com/diegosouzapw/OmniRoute/pull/12197)) — thanks @rafacpti23 +- **build:** skip standalone packaging in the contributor profile ([#12198](https://github.com/diegosouzapw/OmniRoute/pull/12198)) — thanks @rafacpti23 +- **docs(audit):** align every published number with the code and harden check:docs-counts ([#12200](https://github.com/diegosouzapw/OmniRoute/pull/12200)) +- **build:** stub the instrumentation entrypoints in the contributor profile ([#12203](https://github.com/diegosouzapw/OmniRoute/pull/12203)) — thanks @rafacpti23 +- **build:** omit the standalone output target for contributor builds ([#12204](https://github.com/diegosouzapw/OmniRoute/pull/12204)) — thanks @rafacpti23 +- **chore(lint):** batch 6 of #12146 — memory, radar, audit, analytics, cache, usage, activity, home and RequestLoggerV2 react-hooks violations resolved ([#12208](https://github.com/diegosouzapw/OmniRoute/pull/12208)) +- **docs(diagrams):** rename number-carrying diagram files to stable names ([#12210](https://github.com/diegosouzapw/OmniRoute/pull/12210)) +- **docs(dashboard):** redraw onboarding tier-flow SVGs for the real 4-tier model ([#12211](https://github.com/diegosouzapw/OmniRoute/pull/12211)) +- **docs(api):** document every implemented route in openapi.yaml (276 → 692 paths) ([#12212](https://github.com/diegosouzapw/OmniRoute/pull/12212)) +- **chore(lint):** batch 7 of #12146 — final src tail: 37 react-hooks violations across 33 files resolved ([#12244](https://github.com/diegosouzapw/OmniRoute/pull/12244)) +- Revert "docs: recommend gstack for AI-assisted workflows (#11770)" ([#12248](https://github.com/diegosouzapw/OmniRoute/pull/12248)) ([#12249](https://github.com/diegosouzapw/OmniRoute/pull/12249)) +- **docs:** Chaos Mode setup guide + weighted strategy semantics ([#12250](https://github.com/diegosouzapw/OmniRoute/pull/12250)) +- **docs(agents):** protected-surface merge rule — operator approval for agent-instruction files ([#12253](https://github.com/diegosouzapw/OmniRoute/pull/12253)) +- **chore(quality):** register native-codex turn-pin tests in stryker tap.testFiles ([#12263](https://github.com/diegosouzapw/OmniRoute/pull/12263)) +- **chore(quality):** dedupe tap.testFiles entries added by racing base-red fixes ([#12265](https://github.com/diegosouzapw/OmniRoute/pull/12265)) +- **docs(readme):** introduce OmniRouteTray — the macOS menu-bar companion ([#12276](https://github.com/diegosouzapw/OmniRoute/pull/12276)) — thanks @ggiak +- **chore(proxy):** purge the legacy 1proxy residue — sync/rotator modules, dead DB exports, dead settings tab and flag, docs (#12091) ([#12290](https://github.com/diegosouzapw/OmniRoute/pull/12290)) +- **chore(quality):** baseline-headroom skips generated and vendored files in the fileSize worst-file signal ([#12291](https://github.com/diegosouzapw/OmniRoute/pull/12291)) +- **refactor(video):** unify the JPEG frame data-URI contract ([#12322](https://github.com/diegosouzapw/OmniRoute/pull/12322)) +- **chore(deps):** freeze onnxruntime-node and eslint-plugin-react-hooks out of dependabot groups ([#12329](https://github.com/diegosouzapw/OmniRoute/pull/12329)) +- **test(executors):** restore the #10986 reasoning-only fallback guards ([#12364](https://github.com/diegosouzapw/OmniRoute/pull/12364)) — thanks @pacocartones +- **chore(quality):** re-tighten the file-size ratchet to the real LOC (plan 3.8.52 task 0) ([#12411](https://github.com/diegosouzapw/OmniRoute/pull/12411)) +- **test(grok):** format web executor suite ([#12412](https://github.com/diegosouzapw/OmniRoute/pull/12412)) +- **chore(quality):** ratchet open-sse typecheck baseline to zero ([#12418](https://github.com/diegosouzapw/OmniRoute/pull/12418)) +- **test(build):** guard the artifact path policy arrays against duplicates ([#12422](https://github.com/diegosouzapw/OmniRoute/pull/12422)) +- **chore(lint):** adopt eslint-plugin-react-hooks 7.1.1 ([#12428](https://github.com/diegosouzapw/OmniRoute/pull/12428)) +- **chore(providers):** remove a keyless provider integration at its operator's request ([#12440](https://github.com/diegosouzapw/OmniRoute/pull/12440)) +- **docs(providers):** register providers removed at their operator's request and guard against reintroduction ([#12478](https://github.com/diegosouzapw/OmniRoute/pull/12478)) +- **chore(quality):** base fixes — stryker tap.testFiles + node_modules cache key ([#12482](https://github.com/diegosouzapw/OmniRoute/pull/12482)) +- **docs(arch):** one-process recipe for tens of long /v1/responses ([#12493](https://github.com/diegosouzapw/OmniRoute/pull/12493)) — thanks @RaviTharuma +- **chore(quality):** tighten the CodeQL ratchet baseline from 11 to 6 ([#12530](https://github.com/diegosouzapw/OmniRoute/pull/12530)) +- **chore(deps):** pin browserslist override to ^4.28.8 ([#12592](https://github.com/diegosouzapw/OmniRoute/pull/12592)) — thanks @KooshaPari +- **chore(deps):** bump fast-uri to 3.1.7 in the electron lockfile ([#12601](https://github.com/diegosouzapw/OmniRoute/pull/12601)) +- **docs(quality):** document how the CodeQL ratchet refreshes and how to tighten it ([#12611](https://github.com/diegosouzapw/OmniRoute/pull/12611)) +- **test(ci):** pin the openapi-security-tiers two-arm contract with an executing gate test (#12581) ([#12652](https://github.com/diegosouzapw/OmniRoute/pull/12652)) +- **docs(gamification):** align XP Rewards table with code (#12501) ([#12667](https://github.com/diegosouzapw/OmniRoute/pull/12667)) — thanks @KooshaPari +- **refactor(combo):** split executeTarget into gates, attempt, and loop ([#12746](https://github.com/diegosouzapw/OmniRoute/pull/12746)) — thanks @HouMinXi +- **docs(security):** document socket.yml scanner config + CI workflow link (#12575) ([#12764](https://github.com/diegosouzapw/OmniRoute/pull/12764)) — thanks @KooshaPari +- **chore(ci):** guard commit identity in pre-commit to stop author misattribution ([#12772](https://github.com/diegosouzapw/OmniRoute/pull/12772)) +- **deps:** 17 Dependabot bumps — bump electron from 43.4.0 to 43.4.1 in /electron ([#11426](https://github.com/diegosouzapw/OmniRoute/pull/11426)); bump the development group across 1 directory with 8 updates ([#11428](https://github.com/diegosouzapw/OmniRoute/pull/11428)); bump the production group across 1 directory with 11 updates ([#11440](https://github.com/diegosouzapw/OmniRoute/pull/11440)); chore(deps): bump github/codeql-action/init from 4.37.7 to 4.37.8 ([#11925](https://github.com/diegosouzapw/OmniRoute/pull/11925)); chore(deps): bump github/codeql-action from 4.37.7 to 4.37.8 ([#11926](https://github.com/diegosouzapw/OmniRoute/pull/11926)); chore(deps): bump github/codeql-action/analyze from 4.37.7 to 4.37.8 ([#11927](https://github.com/diegosouzapw/OmniRoute/pull/11927)); build(deps): bump github/codeql-action/init from 4.37.8 to 4.37.9 ([#12345](https://github.com/diegosouzapw/OmniRoute/pull/12345)); build(deps): bump github/codeql-action/analyze from 4.37.8 to 4.37.9 ([#12346](https://github.com/diegosouzapw/OmniRoute/pull/12346)); bump the development group across 1 directory with 2 updates ([#12347](https://github.com/diegosouzapw/OmniRoute/pull/12347)); build(deps): bump github/codeql-action from 4.37.8 to 4.37.9 ([#12349](https://github.com/diegosouzapw/OmniRoute/pull/12349)); bump browserslist from 4.28.2 to 4.28.8 ([#12396](https://github.com/diegosouzapw/OmniRoute/pull/12396)); bump the production group across 1 directory with 4 updates ([#12399](https://github.com/diegosouzapw/OmniRoute/pull/12399)); bump @xmldom/xmldom ([#12500](https://github.com/diegosouzapw/OmniRoute/pull/12500)); bump qs from 6.15.2 to 6.16.0 ([#12512](https://github.com/diegosouzapw/OmniRoute/pull/12512)); bump @xmldom/xmldom from 0.9.10 to 0.9.12 ([#12513](https://github.com/diegosouzapw/OmniRoute/pull/12513)); bump fast-uri from 3.1.5 to 3.1.7 ([#12514](https://github.com/diegosouzapw/OmniRoute/pull/12514)); bump @humanfs/node from 0.16.7 to 0.16.8 ([#12515](https://github.com/diegosouzapw/OmniRoute/pull/12515)) +- Clear the `release/v3.8.51` `check:agent-skills-sync` base-red: regenerate `skills/cli-tunnel/SKILL.md` so the `tunnel create [type]` positional that #13009 taught the generator to read is reflected in the committed skill. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` typecheck base-red from #12731: the new `Mode pack ranking gates` candidates in `open-sse/services/autoCombo/__tests__/autoCombo.test.ts` omitted the required `provider`, `model` and `errorRate` fields of `ProviderCandidate`, failing `check:open-sse-typecheck` (and with it `Fast Quality Gates`) on every open PR. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` docs-sync base-red: EURouter (#13025) and GreenPT (#13024) took the live provider count to 358, leaving 7 STRICT drifts (`PROVIDER_REFERENCE.md`, 4 diagrams, 2 tier-flow images) plus the `AGENTS.md` / `llm.txt` / `package.json` count claims stale. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- **test(models):** Pin `CATALOG_BUILD_TIMEOUT_MS` in the #12058 canonical-catalog regression so a cold catalog build racing the 8s default bound no longer flakes the suite with a 500 ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Allow `@omniroute/opencode-plugin-v2/` in the npm pack policy — [#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870) shipped the v2 plugin beside its v1 sibling without widening the allowlist, so every packed file under it read as an unexpected artifact and failed `check:pack-policy` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Point the release-green validator's pack gate at the tree it is validating — the build-provenance guard defaulted to `origin/main`, which a release branch tip can never be an ancestor of mid-cycle, so `Package artifact` reported a false red on every run and the tarball boot-smoke cascaded off it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **docs(checks):** keep doc counts honest — headings, rankings, catalog, weights, quality gate and scoring diagram now covered ([#12507](https://github.com/diegosouzapw/OmniRoute/pull/12507)) — thanks @maxmad64bis +- **chore(lifecycle):** `check:model-lifecycle` now also diffs `DEFAULT_DEGRADATION_MAP` (the background-task redirect table) against the vendor lifecycle snapshot, refusing a retired id as source or target, with a table-driven unit test beside it. Three rows whose source the vendor had retired — `claude-sonnet-4-20250514`, `gemini-3-pro-preview` and `gpt-5.1-codex` (whose target `gpt-5.1-codex-mini` is retired too) — were dead code, since `checkLifecycle` answers 410 before the redirect runs; they are dropped (#12535 — thanks @pacocartones) +- **test(compression):** cover idle worker eviction at the resource level — the pool must call `terminate()` and must not retain the worker's `MessagePort`, complementing the `exit`-event assertion added with the fix ([#13371](https://github.com/diegosouzapw/OmniRoute/pull/13371)) +- **chore(quality):** raise the `imageGeneration.ts` file-size ceiling for the image-only-model guard that clears the #12945 base-red ([#13392](https://github.com/diegosouzapw/OmniRoute/pull/13392)) +- **chore(quality):** type the combo-test route's JSON response bodies instead of casting them to `any`, drop the now-empty suppression entry, and remove the `hasPerModelQuota` import `chatCore.ts` stopped using when the failure-classification helper was extracted ([#13379](https://github.com/diegosouzapw/OmniRoute/pull/13379)) +- **chore(electron):** the desktop bundle no longer ships the root-level files of every translated docs mirror (`README.md`, `llm.txt`, `CLAUDE.md`, `GEMINI.md`, `CONTRIBUTING.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`) — the packaged app only reads `docs/i18n//docs/**`, which stays. Saves ~11 MB on top of the translated CHANGELOGs already pruned. (#0000) ([#13235](https://github.com/diegosouzapw/OmniRoute/pull/13235)) +- **chore(quality):** retighten the `src/sse/handlers/chat.ts` unused-vars suppression count to the 8 violations that actually remain ([#13385](https://github.com/diegosouzapw/OmniRoute/pull/13385)) +- Bring the documented counts back in line with the code: 171 migrations (was 169) and 20 routing strategies (was 19), plus the regenerated `cli-tunnel` skill reference. ([#12970](https://github.com/diegosouzapw/OmniRoute/pull/12970)) +- Update the documented migration count to 172 after the call-logs provider-stats indexes landed. ([#13209](https://github.com/diegosouzapw/OmniRoute/pull/13209)) +- **chore(ci):** two gates that close the blind spots behind the exclusions above. `check:vitest-exclusions` requires every Vitest exclusion to name a tracking issue and to appear in `config/quality/vitest-exclusions.json` — the previous list grew to 62 files behind a comment pointing at an issue that had been closed for a month. `check-new-key-coverage` requires a key newly added to `en.json` to reach every locale; the existing coverage gate is a percentage floor per locale, so eleven absent keys out of ~13,000 left it at 99.9% while a whole feature shipped untranslated in nine languages. Both are diff-aware, so pre-existing debt stays frozen and neither needed a migration to turn on. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **build:** root layout no longer loads Inter from `next/font/google`, so a production image build does not need fonts.googleapis.com ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **docs(i18n):** bring the eight new `llm.txt` mirrors (km, kn, ml, my, ne, or, pa, si) up to the current provider and migration counts, so `check-docs-sync` stops rejecting every commit ([#13674](https://github.com/diegosouzapw/OmniRoute/pull/13674)) +- **refactor(ui):** flow surfaces (home topology, combo live studio, compression cockpit/waterfall/nodes, token-health badge) express state through the theme-aware `--orch-status-*` tokens instead of fixed dark-mode hex, so green/red/amber/grey stay legible in the light theme; dark mode is byte-identical. Categorical palettes (routing-strategy hues, compression-layer pills, provider brand colors) deliberately stay hex ([#12378](https://github.com/diegosouzapw/OmniRoute/issues/12378)) ([#13203](https://github.com/diegosouzapw/OmniRoute/pull/13203)) +- **chore(tests):** 51 test files that had been excluded from Vitest are running again, restoring roughly 350 assertions to the blocking suite. Each was measured individually first: of the 62 files parked behind the `// #8618 — pre-existing failure` comment, 51 pass against the current tree with no source change, so the exclusions had outlived the failures they were added for. The 11 that genuinely still fail stay excluded, but now point at a live tracker (#13204) rather than at #8618, which was closed in August while the list it tracked kept growing. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **chore(quality):** drop two ESLint suppression entries whose violations no longer exist, so `eslint --suppressions-location` stops rejecting every commit that touches the surrounding files ([#13384](https://github.com/diegosouzapw/OmniRoute/pull/13384)) +- **chore(release):** bring main's post-tag fixes into release/v3.8.51 — sync-back step 2/2 (direct commit `21c488f210`) +- **chore(deps):** patch toml and esbuild in the opencode plugin lockfiles ([#12965](https://github.com/diegosouzapw/OmniRoute/pull/12965)) +- **docs(changelog):** reconcile the v3.8.51 living section — fold 366 fragments, cover every cycle commit, credit every contributor ([#12971](https://github.com/diegosouzapw/OmniRoute/pull/12971)) +- **test(plugin):** wait for the v2 lazy-refresh tier instead of sleeping past it ([#12984](https://github.com/diegosouzapw/OmniRoute/pull/12984)) +- **chore(quality):** rebaseline chatCore.ts after the non-streaming regression fixes ([#13045](https://github.com/diegosouzapw/OmniRoute/pull/13045)) +- **docs(changelog):** add the missing bullet marker to the DeepSeek PoW fragment ([#13200](https://github.com/diegosouzapw/OmniRoute/pull/13200)) +- **chore(deps):** drain the Dependabot queue — 10 of 13 alerts ([#13213](https://github.com/diegosouzapw/OmniRoute/pull/13213)) +- **chore(ci):** make worktrees carry the hooks that enforce the identity gate ([#13236](https://github.com/diegosouzapw/OmniRoute/pull/13236)) +- **chore(deps):** pin csv-parse to ^7.0.2 via overrides (#13117) ([#13300](https://github.com/diegosouzapw/OmniRoute/pull/13300)) — thanks @KooshaPari +- **chore(deps):** bump hono from ^4.12.34 to ^4.13.7 (#13148) ([#13301](https://github.com/diegosouzapw/OmniRoute/pull/13301)) — thanks @KooshaPari +- **chore(deps):** pin joi to ^18.2.8 via overrides (#13085) ([#13302](https://github.com/diegosouzapw/OmniRoute/pull/13302)) — thanks @KooshaPari +- **docs(providers):** add Microsoft 365 Copilot (BizChat) provider guide (#12779) ([#13340](https://github.com/diegosouzapw/OmniRoute/pull/13340)) — thanks @KooshaPari +### 🙌 Contributors + +Thanks to everyone whose work landed in v3.8.51: + +| Contributor | PRs / Issues | +| --- | --- | +| [@5dive-bot](https://github.com/5dive-bot) | #11852 | +| [@abhisheksharma2411](https://github.com/abhisheksharma2411) | #11547, #11684, #11959, #12209 | +| [@adevwithpurpose](https://github.com/adevwithpurpose) | #11464 | +| [@adivekar-utexas](https://github.com/adivekar-utexas) | #12015, #12043, #12090, #12138 | +| [@AIB1TAL0S](https://github.com/AIB1TAL0S) | #12103 | +| [@alltomatos](https://github.com/alltomatos) | #11448 | +| [@alvinveroy](https://github.com/alvinveroy) | #11857, #12027, #12165 | +| [@amaleta](https://github.com/amaleta) | #11906, #11934 | +| [@amartinawi](https://github.com/amartinawi) | #11575 | +| [@AndrianBalanescu](https://github.com/AndrianBalanescu) | #11083, #11830, #11834, #11844 | +| [@anhtahaylove](https://github.com/anhtahaylove) | #13091, #13092, #13093, #13096, #13097, #13100, #13106, #13109, #13114, #13155, #13166, #13171, #13175, #13187, #13196 | +| [@aniruddhaadak80](https://github.com/aniruddhaadak80) | #11760 | +| [@app](https://github.com/app) | #12554 | +| [@arminanton](https://github.com/arminanton) | #11461, #11513 | +| [@AStupidBear](https://github.com/AStupidBear) | #11584 | +| [@atescivitci-cmd](https://github.com/atescivitci-cmd) | #12105 | +| [@b3nw](https://github.com/b3nw) | #11970, #11971, #12147 | +| [@backryun](https://github.com/backryun) | #11259, #11950, #12075, #12076, #12078, #12079, #12081, #12082, #12181, #12228, #12239, #12255, #12258, #12277, #12367, #12423, #12524, #12538 | +| [@benjaminkitt](https://github.com/benjaminkitt) | #11747 | +| [@benzntech](https://github.com/benzntech) | #11614, #11615 | +| [@Bl0ck154](https://github.com/Bl0ck154) | #11948, #11951, #11952, #11953, #11954 | +| [@brick30llc-ctrl](https://github.com/brick30llc-ctrl) | #11752, #12242, #12262 | +| [@Chewji9875](https://github.com/Chewji9875) | #11455, #11875, #12028 | +| [@cheynetom](https://github.com/cheynetom) | #12058 | +| [@davidebaraldo](https://github.com/davidebaraldo) | #12222, #12304 | +| [@davidlinfr](https://github.com/davidlinfr) | #6390 | +| [@ddarkr](https://github.com/ddarkr) | #12124 | +| [@Deftera186](https://github.com/Deftera186) | #11809 | +| [@dpozimski](https://github.com/dpozimski) | #11945 | +| [@DW-MediaLab](https://github.com/DW-MediaLab) | #12229 | +| [@echel0nn](https://github.com/echel0nn) | #11923 | +| [@f9td56dbgh-hub](https://github.com/f9td56dbgh-hub) | #11660 | +| [@fabioluissilva](https://github.com/fabioluissilva) | #11991 | +| [@foreveryh](https://github.com/foreveryh) | #12177 | +| [@geek007git](https://github.com/geek007git) | #12115, #12116, #12117, #12120, #12122 | +| [@geekyNads](https://github.com/geekyNads) | #11784, #12266 | +| [@ggiak](https://github.com/ggiak) | #11561, #12276, #12350, #12402 | +| [@gonisulaimann](https://github.com/gonisulaimann) | #12368, #12369, #12371 | +| [@Gorillaz322](https://github.com/Gorillaz322) | #12207 | +| [@groovecityJO](https://github.com/groovecityJO) | #12682 | +| [@hartmark](https://github.com/hartmark) | #11434, #11452, #11473, #11499, #11703, #11983, #11984, #11985, #11986, #11988, #11989, #11990, #11994, #12221, #12293, #12445, #12446, #12447, #12448, #12460, #12461, #12623, #12646, #12650, #12680, #12717, #12718, #12727, #12741, #12854 | +| [@hizzt](https://github.com/hizzt) | #11894 | +| [@hongnoul](https://github.com/hongnoul) | #11484 | +| [@HouMinXi](https://github.com/HouMinXi) | #11411, #11414, #11512, #11518, #11520, #11641, #11642, #11643, #11687, #11779, #11849, #11850, #11851, #11915, #11916, #11918, #11919, #11920, #12013, #12017, #12033, #12042, #12106, #12139, #12166, #12169, #12171, #12205, #12213, #12312, #12325, #12487, #12488, #12495, #12504, #12557, #12566, #12590, #12591, #12624, #12626, #12632, #12637, #12678, #12696, #12697, #12711, #12733, #12746, #12767, #12770, #12789, #12803, #12805, #12811, #12866, #12868, #12899, #12926, #12934, #12950, #12951, #12974, #13001, #13006, #13011, #13017, #13026, #13027, #13034, #13035, #13038, #13042, #13050, #13060, #13061, #13069, #13107, #13120, #13136, #13195, #13197 | +| [@Hsia97](https://github.com/Hsia97) | #11624 | +| [@jacobsparts](https://github.com/jacobsparts) | #11854, #12155, #12167 | +| [@joglomedia](https://github.com/joglomedia) | #11980 | +| [@jonlwheat2-gif](https://github.com/jonlwheat2-gif) | #11443, #11450, #11549, #11567, #11608, #11633, #11635, #11640, #11644, #11671, #11672, #11673, #11674, #11675, #11676, #11677, #11706, #11783, #12051, #12052, #12053, #12055 | +| [@kanade-hoshino](https://github.com/kanade-hoshino) | #12180 | +| [@Karan825](https://github.com/Karan825) | #11861 | +| [@KaspaPulse](https://github.com/KaspaPulse) | #11389, #11469, #11628, #11666, #11910 | +| [@keeltrace](https://github.com/keeltrace) | #12080, #12223 | +| [@killer30001000](https://github.com/killer30001000) | #12178 | +| [@KooshaPari](https://github.com/KooshaPari) | #12470, #12592, #12667, #12699, #12703, #12706, #12764, #12769, #12771, #13300, #13301, #13302, #13340, #13401, #13403, #13404, #13405, #13406, #13407, #13409, #13410, #13411, #13412, #13413, #13414, #13418, #13424, #13427, #13433, #13523, #13524, #13525, #13528, #13531, #13532, #13534, #13539, #13543, #13545, #13546, #13547, #13550, #13553 | +| [@kriptoburak](https://github.com/kriptoburak) | #11370 | +| [@KrzysiekSko](https://github.com/KrzysiekSko) | #12425, #12432, #12673 | +| [@marcelokarval](https://github.com/marcelokarval) | direct commit / report | +| [@maxmad64bis](https://github.com/maxmad64bis) | #1622, #11435, #11437, #11441, #11537, #11550, #11553, #11555, #11557, #11812, #11842, #11843, #11903, #12151, #12214, #12215, #12218, #12226, #12314, #12316, #12317, #12318, #12319, #12320, #12321, #12507, #12715, #12731, #12744, #12786, #12787, #12788, #12790, #12792, #12794, #12795, #12828, #12832, #12853, #12857, #12870, #12937, #12941, #12975, #13141, #13142, #13146, #13147 | +| [@mdigitalbh81](https://github.com/mdigitalbh81) | #12240 | +| [@Meet6338-X](https://github.com/Meet6338-X) | #11598, #11609, #12110 | +| [@morpheus9393](https://github.com/morpheus9393) | #11943 | +| [@MumuTW](https://github.com/MumuTW) | #11492, #11502, #11506, #11507, #11626, #11675, #11685, #11728, #11746, #11888, #11889, #11890, #11892 | +| [@Neuron-Mr-White](https://github.com/Neuron-Mr-White) | #11622, #11801, #12256 | +| [@NightStalker-87](https://github.com/NightStalker-87) | #12183 | +| [@NoxzRCW](https://github.com/NoxzRCW) | #11879, #11880, #11881, #11882, #11883, #11935 | +| [@ntdat812](https://github.com/ntdat812) | #11585, #12095 | +| [@ntdatt812](https://github.com/ntdatt812) | #11368, #11573, #11574, #11576, #11577, #11580, #11582, #11583, #11588, #11589, #11590, #11591, #11592, #11593, #11672, #12177, #12180, #12873, #12918, #12920, #12921, #12925, #12930, #13007, #13009, #13024, #13025, #13083, #13087, #13101, #13104, #13110 | +| [@official-burak](https://github.com/official-burak) | #11542 | +| [@opensource-elearning](https://github.com/opensource-elearning) | #12179, #12189, #12278, #12286 | +| [@oyi77](https://github.com/oyi77) | #11408, #11409, #11421, #11505, #11677, #12036, #12110 | +| [@pacocartones](https://github.com/pacocartones) | #11521, #11522, #11527, #11528, #11529, #11530, #11531, #11532, #11533, #11534, #11595, #11599, #11603, #11604, #11605, #11607, #11610, #11676, #11714, #11716, #11718, #11767, #11838, #11860, #11862, #11869, #11871, #11872, #11873, #11903, #11906, #11921, #11934, #12356, #12358, #12359, #12360, #12361, #12362, #12364, #12365, #12373, #12374, #12375, #12376, #12377, #12379, #12380, #12381, #12386, #12387, #12389, #12390, #12394, #12395, #12397, #12401, #12403, #12404, #12522, #12523, #12535, #12536, #12540, #12541, #12543, #12545, #12548, #12549, #12550, #12551, #12552, #12647, #12651, #12653, #13055 | +| [@patrykkopycinski](https://github.com/patrykkopycinski) | #11936, #11937, #12224 | +| [@PauloFH](https://github.com/PauloFH) | #11509 | +| [@PauloHSOliveira](https://github.com/PauloHSOliveira) | #12241 | +| [@ponkcore](https://github.com/ponkcore) | #12054 | +| [@Prajeeth-12](https://github.com/Prajeeth-12) | #11634, #12046 | +| [@quiterunner-commits](https://github.com/quiterunner-commits) | #12131, #12143 | +| [@rafacpti23](https://github.com/rafacpti23) | #11554, #11558, #12192, #12197, #12198, #12202, #12203, #12204 | +| [@ragnar-claude](https://github.com/ragnar-claude) | #11564 | +| [@raheemuddin786](https://github.com/raheemuddin786) | #11491, #11839, #11840, #11841, #12003, #12230, #12231, #12232, #12233, #12234 | +| [@rahilmavani](https://github.com/rahilmavani) | #11761 | +| [@Rahulsharma0810](https://github.com/Rahulsharma0810) | #11771 | +| [@RaviTharuma](https://github.com/RaviTharuma) | #11710, #11727, #11797, #11798, #11802, #11805, #11806, #11811, #12098, #12099, #12101, #12449, #12452, #12472, #12473, #12493, #12533, #12607, #12628, #12631, #12636, #12875, #12876, #12880, #12882, #12884 | +| [@rezjalibd](https://github.com/rezjalibd) | #12186 | +| [@RhianB14](https://github.com/RhianB14) | #11692 | +| [@rifqiawl](https://github.com/rifqiawl) | #11517, #11519, #11969 | +| [@rqzbeh](https://github.com/rqzbeh) | #11390 | +| [@Sabeekhann](https://github.com/Sabeekhann) | #11699, #11705 | +| [@santosraju99-hub](https://github.com/santosraju99-hub) | #11755, #11770 | +| [@Seramicx](https://github.com/Seramicx) | #11568 | +| [@Siva010](https://github.com/Siva010) | #12191 | +| [@solstxce](https://github.com/solstxce) | #11597 | +| [@soroush5](https://github.com/soroush5) | #12691 | +| [@tenshiak](https://github.com/tenshiak) | #12279 | +| [@TheDemonTuan](https://github.com/TheDemonTuan) | #11468, #11470, #11471, #11482, #11548, #11758, #11775, #11814 | +| [@thomasmaerz](https://github.com/thomasmaerz) | #12834 | +| [@tuandinh0801](https://github.com/tuandinh0801) | #11454 | +| [@turbolego](https://github.com/turbolego) | #11621, #11762, #11772, #11774, #11781, #12216 | +| [@Tushar49](https://github.com/Tushar49) | #11392 | +| [@ujjawalkaushik1110](https://github.com/ujjawalkaushik1110) | #11863 | +| [@vermasomesh835](https://github.com/vermasomesh835) | #11794 | +| [@vsd2807](https://github.com/vsd2807) | #11565, #11619 | +| [@wahidsadik371-coder](https://github.com/wahidsadik371-coder) | #12126 | +| [@watchingdogs](https://github.com/watchingdogs) | #12031 | +| [@wildcard](https://github.com/wildcard) | #11620 | +| [@xiaoyaner0201](https://github.com/xiaoyaner0201) | #11460, #11662, #12083 | +| [@Xxx91n](https://github.com/Xxx91n) | #11690 | +| [@yourspraveen](https://github.com/yourspraveen) | #11146 | +| [@yxyxy](https://github.com/yxyxy) | #11671 | +| [@ZaimMarzuki](https://github.com/ZaimMarzuki) | #11960 | +| [@Zartharas](https://github.com/Zartharas) | #11340 | +| [@zero-executioner](https://github.com/zero-executioner) | #11631 | +| [@diegosouzapw](https://github.com/diegosouzapw) | maintainer | + +--- ## [3.8.50] — 2026-08-25 _Living section — regenerated 2026-08-12 from all cycle commits (cycle open `ed2db6cb19` → tip). Bullets carry the merged PR and its author; direct pushes listed separately._ diff --git a/docs/i18n/id/CHANGELOG.md b/docs/i18n/id/CHANGELOG.md index 56c60f40b1..8180a37688 100644 --- a/docs/i18n/id/CHANGELOG.md +++ b/docs/i18n/id/CHANGELOG.md @@ -8,16 +8,1119 @@ ## [3.8.51] — TBD -_Living section — cycle opened at the v3.8.50 freeze (parallel-cycle model). Bullets are aggregated from `changelog.d/` fragments at each `/generate-release` phase._ +_Living section — reconciled 2026-09-15 from all cycle commits (`091589089c` → `c0f92ec98a`, 916 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 | **123** | +| 📝 Commits in the cycle | **916** | +| 🔀 Pull requests referenced | **892** | +| 📋 Changelog entries | **919** | +| 🙌 Contributors credited in entries | **121** | +| 🤖 Automated dependency commits | 18 | + +**Entries by type** + +| Type | Count | +| --- | ---: | +| 🐛 Fixes | 592 | +| ✨ Features | 131 | +| 🧹 Chore | 93 | +| 📚 Docs | 42 | +| 🧪 Tests | 32 | +| ♻️ Refactor | 8 | +| 🏗️ Build | 7 | +| ⚡ Performance | 7 | +| ⚙️ CI | 2 | +| ⏪ Reverts | 2 | +| 📦 Dependencies | 2 | +| 🔒 Security | 1 | + +### 🏆 Top 25 contributors this cycle + +_By commits in `091589089c..c0f92ec98a`, author identities consolidated via `.mailmap` and the merged PR's GitHub login. Bots excluded._ + +| # | Contributor | Commits | +| ---: | --- | ---: | +| 🥇 | diegosouzapw | 323 | +| 🥈 | Bob.Hou (@HouMinXi) | 79 | +| 🥉 | Paco Cartones (@pacocartones) | 77 | +| 4 | Dizzle (@maxmad64bis) | 49 | +| 5 | Koosha Paridehpour (@KooshaPari) | 43 | +| 6 | Nguyen Thanh Dat (@ntdatt812) | 31 | +| 7 | Ravi Tharuma (@RaviTharuma) | 26 | +| 8 | Markus Hartung (@hartmark) | 22 | +| 9 | Webman (@jonlwheat2-gif) | 22 | +| 10 | backryun | 17 | +| 11 | anhtahaylove | 15 | +| 12 | Rafa Martins (@rafacpti23) | 10 | +| 13 | Syed Raheemuddin (@raheemuddin786) | 10 | +| 14 | Nguyễn Viết Tuấn (@TheDemonTuan) | 8 | +| 15 | MumuTW | 7 | +| 16 | Paijo (@oyi77) | 7 | +| 17 | Tobias Andersen (@turbolego) | 6 | +| 18 | Bl0ck (@Bl0ck154) | 5 | +| 19 | SHANMUGAPRIYAN (@geek007git) | 5 | +| 20 | KaspaPulse | 5 | +| 21 | Abhishek Sharma (@abhisheksharma2411) | 4 | +| 22 | Abhishek Divekar (@adivekar-utexas) | 4 | +| 23 | Andrew B. (@AndrianBalanescu) | 4 | +| 24 | NoxzRCW | 4 | +| 25 | opensource-elearning | 4 | ### ✨ New Features +- **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). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- Added Google AI Studio Gemini batch text-to-speech support through `POST /v1/audio/speech`. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- 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. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(routing):** subscription-first auto groupings — `auto/subscription` routes only through plan-included connections with a documented hard-stop overage and fails closed on exhaustion, while `auto/thrifty` orders the pool `subscription → keyless → free → cheap → premium` and steps up one rung at a time as each is exhausted. Billing class comes from a curated per-connection catalog (uncurated is treated as metered, never plan-included), both reuse STRICT_ZERO_COST's per-connection verification, and a quota reading whose `resetAt` has passed is now refreshed regardless of TTL so routing returns to plan capacity as soon as it resets ([#11146](https://github.com/diegosouzapw/OmniRoute/pull/11146)) — thanks @yourspraveen +- **feat(providers):** publish a management-authenticated versioned web-session credential contract from OmniRoute's canonical browser credential metadata ([#11340](https://github.com/diegosouzapw/OmniRoute/pull/11340)) — thanks @Zartharas +- **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)) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add Xquik X search with typed results, credential validation, REST routing, and MCP selection ([#11370](https://github.com/diegosouzapw/OmniRoute/pull/11370)) — thanks @kriptoburak +- **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)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(dashboard):** surface durable exclusive managed leases in the existing Sessions view, keeping leased clients visible across idle gaps while marking connections with in-flight work as active ([#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389)) — thanks @KaspaPulse +- **build(bun):** allow Turbopack bundler flag on Bun 1.4+ with configurable Webpack fallback ([#11471](https://github.com/diegosouzapw/OmniRoute/pull/11471)) — thanks @TheDemonTuan +- feat(api): add an opt-in `modelVisibilityAllowlist`/`modelVisibilityDenylist` settings pair to curate exactly which models `/v1/models` advertises, mirrored into every `auto/*` combo candidate pool so a denied model cannot be routed to via combo selection either (#11481) ([#11997](https://github.com/diegosouzapw/OmniRoute/pull/11997)) +- **feat(rankings):** the Free Provider Rankings page shows what each provider actually served over the last 24 h. It ranked by ELO alone, which left a provider that answers every call with an error in first place; the usage data was already served by the API but never requested. A provider with too small a sample shows a dash, not a number ([#11546](https://github.com/diegosouzapw/OmniRoute/pull/11546)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **feat(guardrails):** enforce a bounded, deterministic contract for Video Bridge transcripts — 256 cues, 4096 input code units and 4 KiB UTF-8 per cue, 64 KiB total text, malformed-Unicode rejection, focus-window scoping, cross-source reconciliation with contributing-source metadata, and a structural provenance trust boundary so caller JSON can never self-assert `embedded`/`audio-bridge` provenance ([#11652](https://github.com/diegosouzapw/OmniRoute/issues/11652)) ([#12009](https://github.com/diegosouzapw/OmniRoute/pull/12009)) +- **feat(video):** orchestrate optional Video Bridge audio extraction and Audio Bridge STT behind a dual opt-in (operator setting AND per-request signal) — a new loopback-only broker `mode=audio` operation shares the frame path's exact process queue, deadline, AbortSignal, and byte budgets to extract a bounded mono 16 kHz PCM WAV from the same already-downloaded video, then reuses the existing Audio Bridge transcription boundary; provider segment timing is preserved when available and marked coarse otherwise, and every failure degrades to a visual-only-safe partial instead of throwing (#11654). ([#12012](https://github.com/diegosouzapw/OmniRoute/pull/12012)) +- Add a tenant-bound Video Bridge drill-down lifecycle on top of the existing secure cache substrate: opaque hashed handles (never raw session/video identifiers), preview/standard/detail multiresolution variants resampled on read, response pagination capped at 8 frames and 32 MiB, and a new authenticated `/api/v1/video-bridge/drilldown` consumer route that stays disabled for remote access by default and denies cross-key access with the same response as a nonexistent handle (no existence oracle). ([#12006](https://github.com/diegosouzapw/OmniRoute/pull/12006)) +- **test(video):** Add the Video Bridge FU-07/FU-09 promotion-evidence harness (#11656) — a frozen Zod manifest schema covering the 8 required scenario kinds (static scenes, rapid cuts, late facts, fades, blur, small text, close events, visual prompt injection) with a minimum of 3 repetitions per case, deterministic declarative fixture recipes (`videoBridgePromotionFixtures.ts`), a pure medians/p95 metrics aggregator, a pure FU-07/FU-09 promotion-verdict evaluator applying the ticket's exact thresholds (missing token usage always holds), a digest-only persistence layer that never retains raw media or raw model responses, and a versioned per-model promotion allowlist shipped empty with every model defaulting to `hold`. The FU-07/FU-09 promotion verdicts themselves remain HOLD — they require a real evidence run against real models on VPS 192.168.0.15. ([#12008](https://github.com/diegosouzapw/OmniRoute/pull/12008)) +- **feat(video bridge):** "embedded" transcript provenance can now be legitimately earned instead of merely asserted — a bounded, allowlisted (`mov_text`/`subrip`/`webvtt`) subtitle probe runs through the loopback-only Video Bridge broker (at most 2 streams, 10s subdeadline bounded by the request deadline, 256 KiB output, 4096-code-unit lines), normalized through a bounded, ReDoS-safe WebVTT parser and Zod-validated end to end. The adapter always resolves to an explicit `success`/`absent`/`transient_failure` outcome — a subtitle failure never breaks the visual description path, and only a fingerprint-verified broker response (never a caller-declared label) can produce embedded cues (#11659). ([#12011](https://github.com/diegosouzapw/OmniRoute/pull/12011)) +- Default new Antigravity-family connections (agy CLI imports and Antigravity OAuth connects) to model auto-sync, so live model discovery lands in the synced catalog and `/v1/models` picks up freshly released upstream models (e.g. Gemini 3.7 Flash tiers) without code changes. Existing connections keep their current setting; the per-connection dashboard toggle remains the opt-out. (#11685 — thanks @MumuTW) ([#11887](https://github.com/diegosouzapw/OmniRoute/pull/11887)) +- **feat(ui):** enable React Compiler (`reactCompiler: true` + `babel-plugin-react-compiler`) for automatic memoization at build time ([#11783](https://github.com/diegosouzapw/OmniRoute/pull/11783)) — thanks @jonlwheat2-gif +- **feat(providers):** add SeekAi (`seekai.cc`) as an OpenAI-compatible New-API gateway — catalog id `seekai` (alias `ska`), `https://seekai.cc/v1`, live `/v1/models` via `passthroughModels`, aggregator-list membership so New-API balance detection can opt in. No referral/aff codes. ([#11786](https://github.com/diegosouzapw/OmniRoute/issues/11786)) ([#12557](https://github.com/diegosouzapw/OmniRoute/pull/12557)) — thanks @HouMinXi +- **feat(zai):** add GLM-5.3-Flash Coding Plan support (1M context, 128K output, vision, `low|high|max` reasoning) and route `zai` GLM-5.3-family API-key traffic through the OpenAI-compatible Coding Plan endpoint with native thinking defaults ([#11801](https://github.com/diegosouzapw/OmniRoute/pull/11801)) — thanks @Neuron-Mr-White +- **feat(combo):** choose how combo models are ordered — manual, provider, score, or name — via a sort control in the dashboard builder, persisted in `config.modelSort` and re-applied on load and after add ([#11812](https://github.com/diegosouzapw/OmniRoute/pull/11812)) — thanks @maxmad64bis +- **feat(free):** custom models can be marked free-tier via `customModels[].isFree`; `isFreeModel()` is the first door and `hidePaidModels` respects it even for providers outside the free budget ([#11843](https://github.com/diegosouzapw/OmniRoute/pull/11843)) — thanks @maxmad64bis +- **feat(nodejs):** add `5dive` as a `configure` target — `omniroute configure 5dive` / `omniroute setup-5dive` write a 5dive auth profile that points an agent fleet's `claude` seats at OmniRoute, with the root-only write, the loopback-vs-`https` endpoint rule and the per-seat model pin handled explicitly ([#11852](https://github.com/diegosouzapw/OmniRoute/pull/11852)) — thanks @5dive-bot +- **feat(sse):** treat `max` as a first-class reasoning-effort tier and clamp per model family (GLM 5.1+/DeepSeek V4+/Kimi K3+ keep native `max`; o1/MiniMax/Grok/Muse Spark clamp to their upstream ceiling) ([#11875](https://github.com/diegosouzapw/OmniRoute/pull/11875)) — thanks @Chewji9875 +- **feat(providers):** the provider plugin manifest now advertises a `usage-fetch` capability for the 40 providers that have a wired usage/quota fetcher, so external dashboards can read it from `GET /api/v1/provider-plugin-manifest` instead of parsing `open-sse/services/usage.ts` after every release. Discovery only — no new fetcher, no quota change, and the Dashboard quota widget stays gated by `USAGE_SUPPORTED_PROVIDERS`. `USAGE_FETCHER_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/fetcherProviders.ts`) and is re-exported from `services/usage.ts`, keeping the manifest module a light leaf instead of pulling the ~490-module usage dispatcher into the manifest route. ([#11903](https://github.com/diegosouzapw/OmniRoute/pull/11903)) — thanks @maxmad64bis / @pacocartones +- **feat(plugins):** `OMNIROUTE_PLUGINS_DIR` sets the directory the runtime plugin scanner reads — and the root the plugin manager installs into — overriding the `HOME`-derived default, so a Docker/K8s deployment can point straight at its bind-mounted plugin tree instead of moving `HOME` just to relocate the scan path. An image that exports no home no longer scans `/tmp/.omniroute/plugins` in silence: the resolved directory is logged once at startup as `scanner.dir_resolved`, naming the input that won. Unset, behaviour is unchanged. Distinct from the CLI-only `OMNIROUTE_PLUGIN_PATH`, which finds `omniroute-cmd-*` command packages and never reached this scanner ([#11906](https://github.com/diegosouzapw/OmniRoute/pull/11906)) — thanks @amaleta / @pacocartones +- **feat(leases):** add an explicit owner-authenticated status action that returns only the active lease's privacy-safe configured connection and provider labels, with generation fencing and no credential or internal-id disclosure ([#11910](https://github.com/diegosouzapw/OmniRoute/pull/11910)) — thanks @KaspaPulse +- **feat(providers):** import-from-file modal shows per-row API errors and ships a downloadable CSV template ([#12071](https://github.com/diegosouzapw/OmniRoute/issues/12071)) ([#12504](https://github.com/diegosouzapw/OmniRoute/pull/12504)) — thanks @HouMinXi +- **feat(providers):** dashboard search matches connection name and `baseUrl` so imported OpenAI-compat nodes surface on the provider card ([#12108](https://github.com/diegosouzapw/OmniRoute/issues/12108)) ([#12495](https://github.com/diegosouzapw/OmniRoute/pull/12495)) — thanks @HouMinXi +- **feat(routing):** add a `score` Auto router strategy that selects the highest configured weighted score and reuses `explorationRate`. ([#12155](https://github.com/diegosouzapw/OmniRoute/pull/12155)) — thanks @jacobsparts +- Add a runtime feature flag to disable universal context handoffs globally without changing the default behavior. ([#12167](https://github.com/diegosouzapw/OmniRoute/pull/12167)) — thanks @jacobsparts +- **feat(providers):** the provider plugin manifest now also advertises a `usage-supported` capability for the 46 providers whose usage API is accepted by the server and Dashboard routes, so integrators can distinguish "the server will serve quota for this provider" from "a fetcher is wired" without reading TypeScript. Discovery only — no fetcher or quota change. `usage-fetch` resolves on id or alias (the usage dispatcher accepts both); `usage-supported` resolves on id alone, matching the runtime guard `USAGE_SUPPORTED_PROVIDERS.includes(providerId)`. `USAGE_SUPPORTED_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/supportedProviders.ts`) and is re-exported from `providers.ts`, mirroring the `fetcherProviders` leaf from #11903 and keeping the manifest a light module. ([#12214](https://github.com/diegosouzapw/OmniRoute/pull/12214)) — thanks @maxmad64bis +- **feat(rankings):** order Free Provider Rankings by what each provider actually served — `GET /api/free-provider-rankings?sortBy=reliability` and a "Most reliable first" toggle on the page. Providers with too few calls to state a success rate keep their score order below the measured ones; the default order is unchanged ([#12218](https://github.com/diegosouzapw/OmniRoute/pull/12218)). — thanks @maxmad64bis +- **perf(sse):** defer `cloneLogPayload()` in the structured SSE collector until after the `maxEvents`/`maxBytes` cap check, eliminating ~9,800 wasted `structuredClone` calls per streaming response (65–71% faster `push()`). Reducer snapshot isolation restored for OpenAI and Responses summaries ([#12241](https://github.com/diegosouzapw/OmniRoute/pull/12241)) — thanks @PauloHSOliveira ([#12243](https://github.com/diegosouzapw/OmniRoute/pull/12243)) +- **feat(usage):** Devin CLI agentic quota (Codeium seat-management GetUserStatus) and OpenRouter key limits plus account credits now surface in Provider Limits ([#12256](https://github.com/diegosouzapw/OmniRoute/pull/12256) — thanks @Neuron-Mr-White) +- **feat(settings):** persist `headroomUrl` through Settings so status/start use the operator URL instead of only `HEADROOM_URL` ([#12306](https://github.com/diegosouzapw/OmniRoute/issues/12306)) ([#12487](https://github.com/diegosouzapw/OmniRoute/pull/12487)) — thanks @HouMinXi +- **feat(auto-combo):** Auto-Combo scoring can now weigh how often a provider/model has actually succeeded. The engine already carried that number on every candidate — 24 hours of usage history behind a ten-sample floor, real-time metrics otherwise — and the scoring function never read it, while the weight table described `stability` as if it did. `reliability` (`1 - failureRate`, with the same field precedence and the same rate-bounding the speed ranking already uses, so a corrupt reading means "nothing observed" rather than "fails every call") is now a declared factor shipping at weight `0`, so routing is unchanged until an operator gives it one, and the `stability` description now matches what that factor computes ([#12317](https://github.com/diegosouzapw/OmniRoute/pull/12317)) — thanks @maxmad64bis +- **feat(routing):** With `freeAccessPolicy: "strict"`, the read-only candidate listing (`GET /v1/auto-combo/{channel}/candidates`) no longer hides the candidates the zero-cost guard excludes — the same read-only transparency the resilience filter already honours (#9133). Each candidate now carries `freeAccessExclusion` saying why it would be kept out, and it tells an exhausted allowance apart from a quota reading that never arrived or went stale, which used to look identical from the outside. Routing is unchanged: the listing reports, it never enforces. The separate `excludeTosAvoid` guard still drops its candidates without a reason; that gap is now documented rather than closed ([#12319](https://github.com/diegosouzapw/OmniRoute/pull/12319)) — thanks @maxmad64bis +- **feat(radar):** The Radar catalog table now shows two facts it was already receiving from the feed and dropping on the floor: the per-model rate limits (requests and tokens, per minute and per day) in a new column, and a badge when a provider's terms state it may train on the prompts you send. A limit of zero renders as zero rather than "rate-only" — for a ceiling those are opposite facts — and a model with no training statement gets no badge, because an absent statement is not a guarantee ([#12320](https://github.com/diegosouzapw/OmniRoute/pull/12320)) — thanks @maxmad64bis +- **feat(radar):** explain Community, single-use, contributor, supporter, recovery, abuse, offers, and privacy rules before either Radar activation action, and remove the superseded fixed-PR grant promise from every UI locale ([#12342](https://github.com/diegosouzapw/OmniRoute/pull/12342)) +- **feat(gamification):** the dashboard Profile page now shows the real daily streak — `/api/gamification/level` returns `streak: { current, longest }` (per key with `apiKeyId`, operator-wide maximum otherwise) and the streak card reads it instead of a hard-coded 0 (#2403) ([#12377](https://github.com/diegosouzapw/OmniRoute/pull/12377)) — thanks @pacocartones +- **feat(gamification):** the dashboard leaderboard now shows each API key's display name under the Name column instead of a truncated key id; `GET /api/gamification/leaderboard` attaches `name` per entry (name only — no key material), while the shared ranking helper and the federation leaderboard stay id-only — thanks @pacocartones ([#12385](https://github.com/diegosouzapw/OmniRoute/pull/12385)) +- **feat(gamification):** enforce the documented 1000 XP/min per-API-key anti-cheat rate limit on the XP award path; over-limit awards are logged and skipped instead of persisted, and the sliding window now matches the timestamp format stored in `xp_audit_log` ([#2403](https://github.com/diegosouzapw/OmniRoute/issues/2403)) ([#12390](https://github.com/diegosouzapw/OmniRoute/pull/12390)) — thanks @pacocartones +- **feat(admin):** localize the gamification anomalies page — the loading state, the Status column and the Suspicious badge now come from the `common` catalog (new `common.suspicious` key propagated to every locale) — add it to the Gamification sidebar group as `gamification-admin` (`/dashboard/gamification/admin`), and expose the loading and empty states as polite `role="status"` live regions (#12401 — thanks @pacocartones) +- **feat(providers):** skip GitHub combo members missing from the live synced catalog, and drop Copilot models that are policy-disabled or hidden from the model picker ([#12473](https://github.com/diegosouzapw/OmniRoute/pull/12473)) — thanks @RaviTharuma +- **feat(i18n):** locale-expansion tooling — `npm run i18n:add-locale` adds a language to config, dashboard, docs mirrors, CLI, README/indexes and site in one command; browser, CLI and cookie detection resolve `uk`, `fil`/`tl`, `zh-Hant` (and the retired legacy `in` → `id`) via config aliases; `config/i18n.json` now ships in the npm package so the published CLI can read it; new real-translation ratio gate (`npm run i18n:check-ratio`, advisory) with a per-locale ratchet baseline; `run-translation --adopt` restores `.i18n-state.json`; `sync-language-bars` generates 🌐 bars from config; `validate_translation.py` loads its allowlist again. Retires the duplicate `in` locale (Indonesian mislabelled as Hindi) — 42 honest locales; saved `NEXT_LOCALE=in` / `OMNIROUTE_LANG=in` keep working. (#12496) +- **feat(api):** Emit gateway-measured `tokens_per_second` (TTFT excluded) on streaming usage and `X-OmniRoute-Tokens-Per-Second` when first-token latency is known ([#12616](https://github.com/diegosouzapw/OmniRoute/issues/12616)) ([#12631](https://github.com/diegosouzapw/OmniRoute/pull/12631)) — thanks @RaviTharuma +- **feat(opencode-plugin): map gateway cost/usage/tok/s onto OpenCode inference payloads** — the official plugin copies `X-OmniRoute-Response-Cost`, token counts, `X-OmniRoute-Tokens-Per-Second` / `usage.tokens_per_second`, TTFT, and the winning `X-OmniRoute-Model` onto the JSON/SSE body OpenCode already consumes. Missing tok/s is left unset (never `tokens / latency`). (#12636) — thanks @RaviTharuma +- **feat(combo):** add combo strategy `quota-weighted`: skip exhausted accounts, then weighted-draw by leftover / in-flight load; existing conversations stay pinned until the account is empty ([#12789](https://github.com/diegosouzapw/OmniRoute/pull/12789)) — thanks @HouMinXi +- **feat(models):** Account live listings become the chat catalog source for Claude, Codex, Copilot, and AGY; public metadata only fills prices on IDs those accounts already list. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **fix(models):** Union `agy` and `antigravity` live catalogs so `agy/gemini-3.8-flash-high` is not rejected after the prefix folds to `antigravity`. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **feat(opencode):** Add `@omniroute/opencode-plugin-v2`, an opencode v2 plugin that gives a v2 install a route to an OmniRoute gateway: it publishes models, combos and auto-combos into the host catalog, refreshes them lazily behind a 300s TTL, and keeps serving the last known catalog from an on-disk snapshot when the gateway is unreachable. The v1 plugin (`@omniroute/opencode-plugin`) is untouched and keeps working as before. The plugin adapts to the host instead of assuming one shape: it reads the shape the host seeds into the catalog draft and publishes the `api` block, the pre-`api` top-level fields, or both. The gateway key comes from the host credential store when one is connected, so no secret needs to sit in a config file; a source the gateway refuses is named in a warning instead of silently degrading the catalog to raw model ids; and tool schemas bound for a Gemini model are stripped of the JSON-Schema keywords Gemini rejects, so tool calling survives that route ([#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870)) — thanks @maxmad64bis +- feat(services): show sanitized CLIProxyAPI account health from its authenticated management API without exposing credentials, file paths, or raw account metadata (#6342) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add AnySearch free web search + URL extract (webFetch) with typed results, credential validation, REST routing, and MCP selection - fallback-only ([#11690](https://github.com/diegosouzapw/OmniRoute/pull/11690)) — thanks @Xxx91n +- **feat(dashboard):** display clamped `[0, 100]%` cached input token ratio in request logs table ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) — thanks @b3nw +- **feat(catalog):** add `OMNIROUTE_DISABLE_THINKING_LEVEL_VARIANTS` feature flag to optionally filter out thinking level variants from model catalog ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) — thanks @b3nw +- **feat(dashboard):** continuously export call logs to external analytics stores. A pluggable destination registry ships the full Logs-tab record set on an hourly `JobRegistry` cron, with a persisted per-destination cursor, batched inserts, a config UI rendered from each destination's own field descriptors, and a REST layer (`/api/log-export/*`) for CRUD, a connection test, and an on-demand run. A destination can opt into `includeBodies` to also ship the request and response payloads shown in the Logs detail pane, including the client and provider views of each call; this is off by default, and payloads inherit the dashboard's PII sanitisation, secret redaction and `noLog` handling. Google BigQuery is the first destination, using a service-account key stored encrypted at rest and streaming inserts keyed by call-log id, into a table that is day-partitioned on `timestamp` and clustered on `api_key_name`, `provider`, `model` and `status`. ([#11945](https://github.com/diegosouzapw/OmniRoute/pull/11945)) — thanks @dpozimski +- **feat(providers):** add MaxAI as a signed, OpenAI-compatible provider serving its 13 paid chat models (GPT-5.6 / Luna / Thinking, Claude 5 Sonnet, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite, Grok 4.1-fast / 4.5, DeepSeek V3.2 / R1, Llama 3.3 70B) through OmniRoute's `/v1` endpoint, with per-request HMAC-SHA1→SM3→AES request signing, live model + context-window discovery from `/models/get_config`, and prompted tool-calling translated to OpenAI `tool_calls` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI vision input — image_url content parts are forwarded inline in `message_content` to the 6 vision-capable models (GPT-5.6 / Luna / Thinking, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite) ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI image generation — 6 image models (gpt-image-1, dall-e-3, flux-1-schnell/dev/pro, sd3-medium) exposed through `POST /v1/images/generations` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI document RAG — inline base64 file/document attachments are uploaded to MaxAI (content-addressed `doc_id`) and attached to the chat via `doc_list` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** browserless MaxAI onboarding — email device-pair login (`/api/providers/[id]/login`) and signed access-token refresh, so a connection can be created and kept fresh without a real browser or Google OAuth ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** per-provider TLS impersonation profile (MaxAI presents a Windows Firefox-150 client fingerprint) so its bot-sensitive endpoints accept OmniRoute traffic ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** add **Nimble** as a web-search and web-fetch provider (`nimble-search`) — `/v1/search` routes to Nimble's search API at `lite` depth (locale, freshness and include/exclude domain filters mapped onto the shared request contract), and `/v1/web/fetch` routes to Nimble Extract, which covers all four fetch formats (`markdown`, `html`, `links`, `screenshot`) from a single call. One API key serves both surfaces. ([#11620](https://github.com/diegosouzapw/OmniRoute/pull/11620)) — thanks @wildcard +- The npm publish is automatic again, through npm Trusted Publishing (OIDC): the hosted `stage-npm` job publishes with a short-lived credential minted from GitHub's id-token — no `NPM_TOKEN`, no 2FA prompt, provenance attached. `publish_mode=staged` (owner approves with 2FA) and `direct` (token) remain available on `workflow_dispatch`. ([#11931](https://github.com/diegosouzapw/OmniRoute/pull/11931)) +- **feat(providers):** Add **Opper** as an API-key gateway provider — EU-hosted AI gateway with 700+ models from 30+ providers behind one OpenAI-compatible API and one key (`OPPER_API_KEY`); model ids use `provider/model` format (e.g. `anthropic/claude-sonnet-4-6`, `openai/gpt-5`); live model catalog at `https://api.opper.ai/v3/compat/models`; entry mirrors `requesty` (same shape, `passthroughModels: true`, no static seed) ([#11629](https://github.com/diegosouzapw/OmniRoute/pull/11629)) +- **feat(dashboard):** the `/dashboard/orchestration` snapshot hook now subscribes to the `agents` WebSocket channel (`agent.task.updated`) instead of `requests` as its refetch trigger, and relaxes its background poll from 5s to 30s while that WS connection is up — falling back to the tighter 5s cadence, reprogrammed live on any connect/disconnect transition, whenever the socket is down. ([#12409](https://github.com/diegosouzapw/OmniRoute/pull/12409)) +- **feat(dashboard):** Orchestration canvas quick wins — search box plus state/source/provider filter chips with a one-click clear, and per-source collapse/expand, all reflected in the URL so a filtered/collapsed view is shareable and survives a refresh; the detail drawer gained a "copy trace JSON" action and hardened error/empty-state and accessibility handling; the Agents-tab edges now animate traveling particles along active (running) connections; and the canvas node/edge status colors moved off fixed hex values onto theme-aware `--orch-status-*` CSS custom properties, so they adapt correctly to light/dark mode. ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- **chore(dashboard):** Orchestration UI hardening pass and the missing component/model test coverage it called for — `OrchestratorNode`/`ActivityNode`/`OverflowNode` rendering, the `?node=`/overflow-click page routing, the Agents-tab orchestrator-click no-op and `showCompleted` toggle, and the overview kanban's done-column sort order (#12270, #12271). ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- New `/dashboard/orchestration` page: live unified view of everything running — Cloud Agent, A2A and Conductor as a real-time graph (Agents tab), the combo cascade (Routing tab, reusing the Combo Live Studio) and a state kanban (Overview tab), with a detail drawer (trace, cost, approve/cancel). Read-only over existing APIs — no new backend. Canvas concept credit: PR #11815 design ([#12261](https://github.com/diegosouzapw/OmniRoute/pull/12261)) +- **feat(dashboard):** new "History" tab on `/dashboard/orchestration` — an Airflow-style grid of finished runs over a 24h/7d/30d preset window, one row per (source, identity), clicking a cell opens the existing detail drawer. It is backed by real persistence: A2A task lifecycle transitions are now written to the `a2a_tasks` table (purged after 30 days, configurable via `OMNIROUTE_A2A_HISTORY_RETENTION_DAYS`) and served by the new `GET /api/a2a/tasks/history` listing endpoint, with the task-detail route falling back to persisted history once a run leaves the in-memory snapshot. Conductor runs stay remote and are not persisted locally — the tab says so instead of silently omitting them. ([#12479](https://github.com/diegosouzapw/OmniRoute/pull/12479)) +- **feat(dashboard):** the orchestration detail drawer gained a "Repeat" action for Cloud Agent, A2A and Conductor tasks — a two-click confirm (click once to arm, click again within the confirm window to fire) re-submits the original prompt/input as a new run. The button is disabled with an explanatory tooltip whenever the original input can't be recovered from the loaded task detail (e.g. it never carried a prompt, or the detail failed to load). Two limitations of the A2A variant, by design: it targets the `/a2a` JSON-RPC endpoint, which authenticates with an API key only (`REQUIRE_API_KEY=true` or a configured `OMNIROUTE_API_KEY` makes a dashboard-session repeat answer `HTTP 400` — surfaced verbatim in the drawer's error line, never as a success), and the `message/send` call is SYNCHRONOUS: the POST blocks for the whole skill run, so the success confirmation only appears once the repeated task finishes. A dashboard-authenticated A2A creation path is deliberately left to a follow-up — widening the endpoint's auth posture is an operator decision, not a side effect of this feature. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(a2a):** A2A task execution now records which memories were consulted for the task's last user message as `metadata.memoryHits` (id/key/type/content-snippet) plus a `memory_hits` history event, purely for observability — the retrieved memory is never injected into a skill's prompt or behavior. Gated by the `OMNIROUTE_A2A_MEMORY_HITS` kill-switch (default enabled; set to `0` to skip the recall lookup entirely). The drawer's new "Memory used" section lists these hits for a2a tasks and is omitted whenever there are none. Known limitation: recall only resolves under the keyless posture — a keyed caller's task owner is a SHA-256 prefix of the API key, while memory rows are keyed by the database api-key id, and no hash→id lookup exists today, so the hit list stays empty for keyed callers. The recorded hits are also kept out of the task's own `input` (and therefore out of the persisted input and of the "Repeat" request body), so repeating a task never re-sends the previous run's memory snippets. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(providers):** add a Perplexity Agent API provider (`perplexity-agent` / `pplx-agent`) for Perplexity `/v1/responses`, including the documented Anthropic, OpenAI, Google, xAI, DeepSeek, Z.AI, Moonshot/Kimi, NVIDIA, and Perplexity model IDs plus Anthropic-model `max_output_tokens` compatibility. ([#12103](https://github.com/diegosouzapw/OmniRoute/pull/12103)) — thanks @AIB1TAL0S +- **feat(providers):** add RPD (Requests Per Day) limit to provider rate limit overrides across UI, schemas, DB, and i18n ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) — thanks @b3nw +- **feat(providers): add UC Direct (uncensored.com Developer API), the metered OpenAI-compatible surface.** A standard OpenAI-compatible passthrough (default executor) for uncensored.com's official REST API at `https://api.uncensored.com/api/v1`: `X-api-key` auth (never-expiring `uai_sk_live_` key), `POST /chat/completions` with streaming SSE and native tool-calling, and the full live metered catalog (82 models across 15 providers, discovered from the public `GET /v1/models`). Registered as provider `uc-direct` (alias `ucd`). Complements the un-metered `uc` persona provider — same models, metered credits and a plain API key instead of a subscription session. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(providers): add UC (uncensored.com), the un-metered subscription "persona" chat as an OpenAI-compatible provider.** A WebSocket web-app port: a durable Clerk credential mints a short-lived session token per connect (browserless — no API key), driving UC's persona socket. Ships the browserless email-code login (request → verify → harvest), the 19 verified persona models (Claude Opus, Gemini, Grok, GLM, Kimi, DeepSeek, MiniMax, incl. the uncensored variants), prompted `` tool-calling with a per-model code-style dialect + auto-cure retry for guardrailed models, live ``/reasoning split, streaming + non-streaming OpenAI responses, and full quota/auth error surfacing (paywall / message-limit / rate-limit → 429, invalid session → 401 re-login). Full multimodal parity via the persona blob-upload layer: **vision** (image input, 15 vision-capable models), **document RAG** (PDF/doc upload, server-side extraction), **image generation** (22 models), **video generation** (14 models, async signed-url → poll), and **TTS** (streaming MP3). Registered as provider `uc` (alias `ucn`). The metered OpenAI-compatible Developer API is a separate `uc-direct` provider. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(logs):** add copy-all button to request detail modal ([#11083](https://github.com/diegosouzapw/OmniRoute/pull/11083)) — thanks @AndrianBalanescu +- **feat(check):** make serviceKinds required and add the reverse-walk provider consistency gate ([#11392](https://github.com/diegosouzapw/OmniRoute/pull/11392)) — thanks @Tushar49 +- **feat(providers):** only advertise verified-working connections by default ([#11448](https://github.com/diegosouzapw/OmniRoute/pull/11448)) — thanks @alltomatos +- **feat(resilience):** add hierarchical concurrency admission ([#11493](https://github.com/diegosouzapw/OmniRoute/pull/11493)) +- **feat(providers):** derive + surface expiry for JWT-bearing web cookies (#11497) ([#11505](https://github.com/diegosouzapw/OmniRoute/pull/11505)) — thanks @oyi77 +- **feat(tunnels):** support named cloudflared tunnels via CLOUDFLARED_CONFIG ([#11561](https://github.com/diegosouzapw/OmniRoute/pull/11561)) — thanks @ggiak +- **feat(compression):** make proactive context-compression threshold a live setting ([#11564](https://github.com/diegosouzapw/OmniRoute/pull/11564)) — thanks @ragnar-claude +- **feat(resilience):** add provider peak-hour protection ([#11622](https://github.com/diegosouzapw/OmniRoute/pull/11622)) — thanks @Neuron-Mr-White +- **feat(providers):** add 1min.ai provider ([#11631](https://github.com/diegosouzapw/OmniRoute/pull/11631)) — thanks @zero-executioner +- **feat(dashboard):** collapsible JSON tree viewer for request/response payloads ([#11703](https://github.com/diegosouzapw/OmniRoute/pull/11703)) — thanks @hartmark +- **feat(routing):** add LiquidAI LFM2.5-2.6B free tier via OpenRouter ([#11752](https://github.com/diegosouzapw/OmniRoute/pull/11752)) — thanks @brick30llc-ctrl +- **feat(pricing):** add GLM-5.3-Flash pricing, model specs, and catalog registration ([#11830](https://github.com/diegosouzapw/OmniRoute/pull/11830)) — thanks @AndrianBalanescu +- **feat(routing):** add relayMode for schema-locked context handoffs ([#11839](https://github.com/diegosouzapw/OmniRoute/pull/11839)) — thanks @raheemuddin786 +- **feat(config):** add support for runtime system prompt configuration and hot-reloading ([#11841](https://github.com/diegosouzapw/OmniRoute/pull/11841)) — thanks @raheemuddin786 +- **feat(routing):** expand connection-aware quota prefilter across combo strategies (#11682) ([#11850](https://github.com/diegosouzapw/OmniRoute/pull/11850)) — thanks @HouMinXi +- **feat(models):** reactive discovery sync after Antigravity model-not-found 404 ([#11888](https://github.com/diegosouzapw/OmniRoute/pull/11888)) — thanks @MumuTW +- **feat(models):** auto-aliases and token limits from synced Antigravity discovery ([#11889](https://github.com/diegosouzapw/OmniRoute/pull/11889)) — thanks @MumuTW +- **feat(settings):** raise sticky round-robin limit caps to 1000 ([#12015](https://github.com/diegosouzapw/OmniRoute/pull/12015)) — thanks @adivekar-utexas +- **feat(resilience):** operator-configurable global credential health check interval ([#12043](https://github.com/diegosouzapw/OmniRoute/pull/12043)) — thanks @adivekar-utexas +- **feat(quota):** use official OpenCode Go usage API ([#12124](https://github.com/diegosouzapw/OmniRoute/pull/12124)) — thanks @ddarkr +- **feat(resilience):** default the credential health check sweep to 60 minutes ([#12138](https://github.com/diegosouzapw/OmniRoute/pull/12138)) — thanks @adivekar-utexas +- **feat(quality):** new-code mode for the complexity and dead-code ratchets (Clean as You Code) ([#12142](https://github.com/diegosouzapw/OmniRoute/pull/12142)) +- **feat(dashboard):** orchestration canvas — unified model + snapshot hook (part 1/2) ([#12156](https://github.com/diegosouzapw/OmniRoute/pull/12156)) +- **feat(usage):** add Kilo Code balance and Kilo Pass quotas ([#12178](https://github.com/diegosouzapw/OmniRoute/pull/12178)) — thanks @killer30001000 +- **feat(providers):** refresh vendored ChatGPT Web connector to v4.0.7 ([#12181](https://github.com/diegosouzapw/OmniRoute/pull/12181)) — thanks @backryun +- **feat(providers):** manual "Clear cooldown" action in the cooling panel ([#12224](https://github.com/diegosouzapw/OmniRoute/pull/12224)) — thanks @patrykkopycinski +- **feat(providers):** restore ChatGPT Web via clean-room browser transport ([#12239](https://github.com/diegosouzapw/OmniRoute/pull/12239)) — thanks @backryun +- **feat(sse):** wire the PROVIDER_PROFILES window gate into the global provider cooldown ([#12247](https://github.com/diegosouzapw/OmniRoute/pull/12247)) +- **feat(providers):** modernize CLOVA Studio chat and embeddings ([#12277](https://github.com/diegosouzapw/OmniRoute/pull/12277)) — thanks @backryun +- **feat(browser):** adopt Obscura as primary headless browser engine with Chromium fallback ([#12286](https://github.com/diegosouzapw/OmniRoute/pull/12286)) — thanks @opensource-elearning +- **feat(sse):** full language parity for output styles (es/de/fr/it/ru/zh + autoDetect) ([#12289](https://github.com/diegosouzapw/OmniRoute/pull/12289)) +- **feat(quality):** complete test:scoped — --full map rebuild, stdin selection, CI loader parity (#8084 D1) ([#12353](https://github.com/diegosouzapw/OmniRoute/pull/12353)) +- **feat(providers):** refresh Fable, Cursor, and Devin catalogs ([#12367](https://github.com/diegosouzapw/OmniRoute/pull/12367)) — thanks @backryun +- **feat(video):** redact transcript text from logs and durable memory (#12150 P1) ([#12427](https://github.com/diegosouzapw/OmniRoute/pull/12427)) +- **feat(dashboard):** parent-link, genuine-continuation badge, and modal perf fixes ([#12448](https://github.com/diegosouzapw/OmniRoute/pull/12448)) — thanks @hartmark +- **feat(dashboard):** adaptive context-budget dial on compression panel ([#12488](https://github.com/diegosouzapw/OmniRoute/pull/12488)) — thanks @HouMinXi +- **feat(providers):** refresh Z.ai Web models and browser transport ([#12524](https://github.com/diegosouzapw/OmniRoute/pull/12524)) — thanks @backryun +- **feat(video):** redact raw client-snapshot transcript fields in the detailed log (#12150 P2) ([#12528](https://github.com/diegosouzapw/OmniRoute/pull/12528)) +- **feat(providers):** refresh NVIDIA hosted models ([#12538](https://github.com/diegosouzapw/OmniRoute/pull/12538)) — thanks @backryun +- **feat(quota):** Moonshot Open Platform balance and TPD lock for custom nodes ([#12590](https://github.com/diegosouzapw/OmniRoute/pull/12590)) — thanks @HouMinXi +- **feat(video):** redact transcript in the in-memory pending-request snapshot (#12430 item 6) ([#12596](https://github.com/diegosouzapw/OmniRoute/pull/12596)) +- **feat(dashboard):** link a log entry's Conversation Context to its owning conversation ([#12646](https://github.com/diegosouzapw/OmniRoute/pull/12646)) — thanks @hartmark +- **feat(catalog):** eligibility-gated free-tier bucket ([#12669](https://github.com/diegosouzapw/OmniRoute/pull/12669)) +- **feat(db):** fail-closed previous_response_id continuation for redacted video turns (#12150 P2b) ([#12707](https://github.com/diegosouzapw/OmniRoute/pull/12707)) +- **feat(gamification): pay the documented `streak_bonus` and `badge_unlock` XP rewards.** `XP_REWARDS` listed both rewards but the award pipeline never paid them: the private reward table in `events.ts` omitted them, `updateStreak()` did not report when a streak extended, and badge unlocks carried no XP. Every request that extends a daily streak now pays `streak_bonus × streak length` once per UTC day (guarded by a same-day `xp_audit_log` check), and every badge unlocked through the pipeline pays `badge_unlock` once per badge (guarded by the `user_badges` primary key; `unlockBadge()` now reports whether it inserted). Bonus XP flows through the same `addXp` + level sync + global/weekly/monthly leaderboard path as action XP, so level-ups and rankings include it. The Radar supporter recognition unlock stays XP-free. (#12522 — thanks @pacocartones) +- **feat(providers):** advertise a `free-tier` capability in the provider plugin manifest for every provider with documented free models, so sidecars and dashboards can filter free-capable providers without reading the quota catalog ([#12786](https://github.com/diegosouzapw/OmniRoute/pull/12786)) — thanks @maxmad64bis +- **feat(providers):** Added EURouter as an OpenAI-compatible API-key gateway (`https://api.eurouter.ai/v1`), with live model discovery via `passthroughModels`. Its copy states that models are served by third-party upstreams listed per model, so an EU-based router is not read as EU data residency for inference. ([#13025](https://github.com/diegosouzapw/OmniRoute/pull/13025)) — thanks @ntdatt812 +- **feat(providers):** Added GreenPT as an OpenAI-compatible API-key provider (`https://api.greenpt.ai/v1`), with live model discovery via `passthroughModels`. No free-inference badge: the published docs describe a free API subscription billed per token, not a free tier. ([#13024](https://github.com/diegosouzapw/OmniRoute/pull/13024)) — thanks @ntdatt812 +- **feat(i18n):** 9 new locales — Greek (`el`), Croatian (`hr`), Serbian (`sr`), Lithuanian (`lt`), Estonian (`et`), Latvian (`lv`), Slovenian (`sl`), Maltese (`mt`), Irish (`ga`) — across the dashboard, docs mirrors, CLI, README and the site; OmniRoute now ships every official EU language (51 locales). (#13044) +- **feat(i18n):** 8 new locales — Kannada (`kn`), Malayalam (`ml`), Odia (`or`), Punjabi (`pa`), Nepali (`ne`), Sinhala (`si`), Burmese (`my`), Khmer (`km`) — across the dashboard, docs mirrors, CLI, README and the site (59 locales). The translator now restores the ICU literal escape around angle placeholders and splits oversized docs sections before translating. (#0000) ([#13660](https://github.com/diegosouzapw/OmniRoute/pull/13660)) +- feat(providers): **list Agnes 3.0 Flash as the current free chat model, drop retired 1.5 Flash, add Image 2.0/2.5 Flash plus Video 2.5/2.5 Flash, and discover the live `/v1/models` catalog (including the CN host `api.agnes-ai.cn`).** `agnes-1.5-flash` now forwards to `agnes-3.0-flash`. Video 2.5 polls `GET /v1/videos/{id}` (not the V2.0 `/agnesapi` contract). Live `/v1/models` (2026-09-09) no longer serves 1.5; the wiki marks it deprecated. 3.0 Flash is 512K context / 65,536 max output, same window as 2.5. CN-region keys use the existing per-connection base-URL field, default stays `apihub.agnes-ai.com`. ([#13120](https://github.com/diegosouzapw/OmniRoute/pull/13120)) — thanks @HouMinXi +- **feat(sse):** Codex and OpenAI catalogs list GPT-6 Astra with effort aliases (`-low` through `-ultra`); Codex CLI identity pins `@openai/codex@0.153.4` in lockstep with the image ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **feat(dashboard):** the orchestration History tab gained a "Compare runs" mode — toggling it turns each grid cell into a 2-item selection queue (a 3rd click drops the oldest pick), and picking two cells opens a side-by-side comparison panel instead of the usual detail drawer. The panel fetches both runs' detail the same way the drawer does (falling back to persisted history once a run leaves the live TTL window) and shows, per side: identity/source/state, start time, a signed `right - left` delta for duration/cost/event count, the event timeline aligned by index, and any memory hits. One side's fetch failing never blocks the other, and a delta is only ever computed when both sides have a finite value — otherwise it renders "—", never `NaN`. Comparing two runs from different sources or skills still works; a banner marks the deltas as informational rather than hiding them, since the two runs aren't a strict apples-to-apples pair. ([#12677](https://github.com/diegosouzapw/OmniRoute/pull/12677)) +- **feat(dashboard):** badge a conversation that never reached a clean stop ([#12717](https://github.com/diegosouzapw/OmniRoute/pull/12717)) — thanks @hartmark +- **feat(release):** reconcile-changelog tool + version-anchored fragment aggregation ([#12987](https://github.com/diegosouzapw/OmniRoute/pull/12987)) +- **feat(mcp):** carry context_length through MCP list_models_catalog projection (#12776) ([#13406](https://github.com/diegosouzapw/OmniRoute/pull/13406)) — thanks @KooshaPari +- **feat(cli):** make startup readiness budget configurable (#13369) ([#13433](https://github.com/diegosouzapw/OmniRoute/pull/13433)) — thanks @KooshaPari + ### 🐛 Bug Fixes +- **security(streaming):** sanitize generic mid-stream error messages before emitting OpenAI, + Responses, or Claude SSE failure frames and before diagnostic logging, while preserving raw + failures for internal classification and keeping client disconnects out of provider failure state. ([#12457](https://github.com/diegosouzapw/OmniRoute/pull/12457)) +- **fix(claude):** `blockExtraUsage: false` no longer lets 5h quota preflight skip the account; extra usage is billed after the session bar is gone, so the request must reach Anthropic ([#12803](https://github.com/diegosouzapw/OmniRoute/pull/12803)) — thanks @HouMinXi +- Sanitize HuggingChat conversation-creation and message-send transport failures before they reach client error bodies or provider logs. ([#12467](https://github.com/diegosouzapw/OmniRoute/pull/12467)) +- **fix(dashboard):** Keep local and theme-aware provider SVG icons at a definite layout size so Chromium does not collapse them to 0×0 after the v3.8.50 image-rendering change ([#12054](https://github.com/diegosouzapw/OmniRoute/pull/12054)) — thanks @ponkcore +- **fix(security):** Sanitize provider and runtime failures before public API, SSE and MCP responses and before persistent request, proxy and usage logs, preventing credentials, stack traces and host filesystem paths from crossing those boundaries while preserving stable error codes and useful diagnostics. ([#12506](https://github.com/diegosouzapw/OmniRoute/pull/12506)) +- **fix(cli):** `omniroute tunnel create` no longer crashes with `Cannot read properties of undefined (reading optsWithGlobals)` — removed the duplicate positional argument that caused Commander.js to misalign the action callback parameters ([#12295](https://github.com/diegosouzapw/OmniRoute/issues/12295)) ([#12368](https://github.com/diegosouzapw/OmniRoute/pull/12368)) — thanks @gonisulaimann +- Stop advertising Gemini Live-only models as supported audio endpoints until OmniRoute proxies the bidirectional Live protocol. ([#11710](https://github.com/diegosouzapw/OmniRoute/pull/11710)) — thanks @RaviTharuma +- **fix(kie):** reroute `flux/kontext` off the KIE Market `createTask` flow — it is catalogued with `isMarket: true` but has no Market catalog page, so KIE rejected it with "model name not supported"; it now hits the dedicated `POST /api/v1/flux/kontext/generate` / `GET /api/v1/flux/kontext/record-info` endpoints instead (#11296). ([#11985](https://github.com/diegosouzapw/OmniRoute/pull/11985)) — thanks @hartmark +- **fix(kie):** correct 12 more KIE Market catalog ids that were sent to `createTask` unchanged but diverge from KIE's documented upstream `model` values — GPT Image 2 T2I/I2I (drops the `gpt/` prefix), GPT Image 1.5 T2I/I2I (`gpt-image/` namespace), Seedream 5.0 Lite T2I/I2I (drops the `.0`), all 4 Flux 2 variants (`flux-2/` namespace, generic variant renamed `flex`), and Wan 2.7 Image / Image Pro (dash instead of dot) — each verified individually against the literal example request published on docs.kie.ai. `#11326`'s "everything else already matches" claim was wrong a second time (#11296); `z-image/4.0-*`/`z-image/4.5-*` and `flux/kontext` remain open, documented as unresolved in `KIE_MARKET_UPSTREAM_MODEL_IDS`'s comment pending further verification. ([#11667](https://github.com/diegosouzapw/OmniRoute/pull/11667)) +- **fix(dashboard):** `useApiKeySave.handleSaveApiKey` no longer forces a full upstream `/models` catalog sync on every non-curated provider connection save — callers can now pass `skipModelSync: true` to opt out, so a workflow that only wants to add one manual model no longer floods the provider's available-models list with hundreds/thousands of synced entries. The flag is a client-side intent signal only and is stripped before the connection payload is POSTed to `/api/providers`; default behavior (full sync on save) is unchanged when the flag is omitted (#11324) ([#11664](https://github.com/diegosouzapw/OmniRoute/pull/11664)) +- **Provider connections:** keep `tokenExpiresAt` when a connection is created. The create-path allowlist omitted it, so every insert stored NULL and the dashboard token badge could read a fresh connection as expired until its first background refresh ([#11368](https://github.com/diegosouzapw/OmniRoute/pull/11368)). — thanks @ntdatt812 +- **fix(combos):** the combo builder's precision-select, global-model-search, and manual-entry flows now serialize a model step's `model` string using the provider's already-computed routing-alias prefix (e.g. `oc/`) instead of rebuilding it from the raw canonical `providerId`, fixing the no-auth "OpenCode Free" provider (`opencode`) being routed to the unrelated paid "OpenCode Zen" provider (`opencode-zen`) because `opencode` doubles as a manual routing-prefix override ([#11433](https://github.com/diegosouzapw/OmniRoute/issues/11433)). ([#11670](https://github.com/diegosouzapw/OmniRoute/pull/11670)) +- **fix(radar):** the catalog feed cache now keeps `generatedAt`, the date the feed's data was built, next to `fetchedAt`, the date this install downloaded it (#11435). The feed schema requires that date and the sync path validates it, but the cache dropped it — so a feed fetched minutes ago and one carrying weeks-old figures looked identical to everything downstream, including the dashboard's "Last fetched" line. `getRadarCatalog().meta` and `GET /api/radar/status` now report both dates, the latter as its own field rather than folded into `version` — and omitted entirely for the offers and intel caches, which keep no build date, where a `null` would read as "unknown" rather than "never stored". The dashboard still shows only the fetch time; surfacing the build date there needs a new translated label and is left to a follow-up. Rows cached before migration 163 read back as `null`: unknown stays unknown instead of borrowing the fetch time. The referrals cache has persisted the same date since migration 142. — thanks @maxmad64bis +- **fix(cli):** postinstall no longer fills `JWT_SECRET` and `API_KEY_SECRET` in the installed package's `.env` (#11436). `.env.example` ships both blank on purpose: the server restores them from its durable store, or generates and persists them there, in `ensureSecrets()`. Pre-filling them defeated that — the file lives inside the package directory, so `npm i -g` replaced it and postinstall wrote _different_ values, while `ensureSecrets()` (which only acts on an empty variable) never got to restore the real ones. Both secrets rotated silently on every update, invalidating dashboard sessions and API-key CRCs. `STORAGE_ENCRYPTION_KEY` left the same list for the same reason in #1622; its comment pointed at a function that no longer exists and now names the real provisioning path. — thanks @maxmad64bis +- **fix(cli):** the CLI now says when a loaded `.env` lives inside the installed package directory (#11437). It already announces every env file it reads, without distinguishing the ones that survive an update from the one that does not: `npm i -g` replaces the package directory wholesale, so values set there are gone at the next update, silently. The warning names the durable path to move them to, and fires only when that file actually supplied a value — a file entirely shadowed by a durable one supplied nothing. A development checkout stays silent: there the same path is stable and documented in `SETUP_GUIDE.md`. — thanks @maxmad64bis +- **fix(free-models):** the shared `isFreeModel()` predicate no longer reports catalog entries marked `freeType: "discontinued"` as free, so `hidePaidModels` can't route to Pollinations' seven premium models that now require a paid key ([#11441](https://github.com/diegosouzapw/OmniRoute/pull/11441)) — thanks @maxmad64bis +- fix(ui): let AnySearch use the normal provider-icon fallback when LobeHub has no matching icon (#11449) +- Fixed the v3.8.50 Costs and Analytics dashboards so flat-rate Claude Code usage can be shown as an explicitly requested token-price estimate without changing default billed-cost semantics. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed archived usage retention so each request is priced individually instead of pricing a day's summed tokens once, which understated archived cost whenever a day mixed cache-heavy and ordinary requests. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed the Costs dashboard so it discloses when displayed figures include flat-rate token-price estimates instead of labelling them as billed spend, using the flag the analytics API already returns; the month-end projection and the CSV/JSON exports carry the same marker, and billed-cost mode is unchanged. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- **fix(combo):** attach the same combo-diagnostics trace (`poolSize`/`attemptOrder`/`excluded`/`terminalReason`, plus `x-omniroute-combo-*` headers) to the round-robin strategy's and the nested pipeline/fusion runtime-unit loop's "Maximum combo retry limit reached" 503 that the priority-strategy path already attaches for the identical terminal condition — previously those two paths returned a bare, contextless 503 ([#11462](https://github.com/diegosouzapw/OmniRoute/issues/11462)). ([#11665](https://github.com/diegosouzapw/OmniRoute/pull/11665)) +- **fix(bun):** use native `bun:sqlite` in `bootstrap-env` and `sync-env` to avoid loading `better-sqlite3` N-API addon during Bun startup ([#11468](https://github.com/diegosouzapw/OmniRoute/pull/11468)) — thanks @TheDemonTuan +- **fix(docker-bun):** make `Dockerfile.bun` install reliable with pre-install script helpers and native `bun:sqlite` smoke check ([#11470](https://github.com/diegosouzapw/OmniRoute/pull/11470)) — thanks @TheDemonTuan +- **fix(docker-bun):** remove every vendored `better-sqlite3` native addon from the Bun runtime image so startup cannot abort before the `bun:sqlite` fallback ([#11482](https://github.com/diegosouzapw/OmniRoute/pull/11482)) — thanks @TheDemonTuan +- **fix(autoCombo):** effort/alias model variants (`gpt-5.6-sol-xhigh`, `gpt-5.6`, cursor's `claude-4.6-opus-high`) inherit their base model's task fitness instead of falling to the wildcard 0.5 ([#11492](https://github.com/diegosouzapw/OmniRoute/pull/11492)) — thanks @MumuTW +- fix(db): rate-limit repeated Arena ELO leaderboard fetch-failure warnings instead of logging one per sync attempt (#11500) ([#11989](https://github.com/diegosouzapw/OmniRoute/pull/11989)) — thanks @hartmark +- **fix(db):** dedupe the raw `[Encryption] Decryption failed...` log line emitted by the lazy-decrypt views (`createLazyRowProxy`/`createLazyConnectionView`), which power `getProviderConnections()` and were re-triggering that line on every CredentialHealth/model-sync cycle for the same corrupt or stale-key credential — a fresh Proxy over a fresh row on every cycle meant the per-proxy memoization never suppressed it, unlike the dedup `decryptConnectionFields()` already had since [#9927](https://github.com/diegosouzapw/OmniRoute/issues/9927). Now shares that dedupe tracking so the line logs at most once per credential ([#11500](https://github.com/diegosouzapw/OmniRoute/issues/11500)). ([#11678](https://github.com/diegosouzapw/OmniRoute/pull/11678)) +- **fix(intelligence):** Arena ELO sync stores a synthesized base-model row for every effort/harness variant (`claude-opus-5-max` → `claude-opus-5`, `gpt-5.6-sol-xhigh (codex-harness)` → `gpt-5.6-sol`), so bare model requests reach the synced intelligence layer instead of the static fallback table; the hand-maintained `MODEL_ALIAS_MAP` — which collapsed generations (`gpt-5.5` → `gpt-5`) — is gone ([#11506](https://github.com/diegosouzapw/OmniRoute/pull/11506)) — thanks @MumuTW +- **fix(autoCombo,sse):** catalog hygiene — the static task-fitness table no longer ranks retired models above live ones (dead rows dropped, patterns matched on segment boundaries), the 7 deprecation aliases that forwarded to retired or non-existent models now point at the vendors' published replacements, an alias is no longer rewritten when the serving provider offers the id as-is, and a new `check:model-lifecycle` gate diffs both tables against a vendor lifecycle snapshot ([#11507](https://github.com/diegosouzapw/OmniRoute/pull/11507)) — thanks @MumuTW +- **fix(sse):** stop dropping resolved `thoughtSignature` values on parallel (multi tool-call) turns sent to Gemini 3.x — the claude→gemini and openai→gemini translators previously kept the signature only on the *first* function call of a message, causing Gemini to reject subsequent calls in the same turn with HTTP 400 "Function call is missing a thought_signature"; each function call now keeps its own resolved signature ([#11510](https://github.com/diegosouzapw/OmniRoute/issues/11510)). ([#11669](https://github.com/diegosouzapw/OmniRoute/pull/11669)) +- Treat Docker publishing from a non-default release branch as an expected skip instead of a failed workflow, while preserving the guard that prevents it from overwriting the `next` channel. ([#11527](https://github.com/diegosouzapw/OmniRoute/pull/11527)) — thanks @pacocartones +- fix(search): prefer credentialed providers over duckduckgo-free fallback (#11524) — the fallback-only loop ran before the credentialed-providers loop in `executeWebSearch`, making configured providers unreachable when `duckduckgo-free` was available. ([#11565](https://github.com/diegosouzapw/OmniRoute/pull/11565)) — thanks @vsd2807 +- **fix(sse):** cap the upstream headers-wait phase for STREAMING requests to a client-realistic ceiling (110s, under Codex's own ~120s hard client-abort window) instead of the flat 10-minute `FETCH_TIMEOUT_MS` default — that default was 5x longer than the body-phase readiness watchdog's own adaptive bound, so a request whose upstream never returned any response at all (not even headers, e.g. a stalled NVIDIA target behind a tool-heavy Responses→Chat translation) kept the client connection alive on keepalives only, guaranteeing the client's own patience ran out first with an opaque 499 instead of OmniRoute detecting and failing the stall fast. Non-streaming requests are unaffected — they keep the existing flat default (`open-sse/utils/fetchStartTimeoutPolicy.ts`) (#11526) ([#11668](https://github.com/diegosouzapw/OmniRoute/pull/11668)) +- **fix(ci):** protocol-clients E2E harness boots the peer-stamped custom server (`run-next.mjs dev`) instead of the bare `next` CLI, so `LOCAL_ONLY` locality resolves from the real TCP peer stamp and `/api/mcp/audit` answers 200 (open bootstrap preserved via an `OMNIROUTE_E2E_BOOTSTRAP_MODE=open` credential clear in `run-next.mjs`, pinned to loopback `HOST`) instead of a deterministic 403 (#11535) ([#11549](https://github.com/diegosouzapw/OmniRoute/pull/11549)) — thanks @jonlwheat2-gif +- **fix(docs):** the Auto-Combo engine is described with the number of factors it actually declares. Four different counts were in circulation — 6, 9, 13 and 14 across `AGENTS.md`, four guides and six code comments — while `DEFAULT_WEIGHTS` declares 15. `check:docs-counts` now reads that number from the source, so adding a factor without updating the prose fails CI ([#11545](https://github.com/diegosouzapw/OmniRoute/pull/11545)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **fix(quota-share):** in-flight leases are per request, so an aborted request's slot ages out instead of being kept alive by later traffic on the same connection ([#11547](https://github.com/diegosouzapw/OmniRoute/pull/11547)) — thanks @abhisheksharma2411 +- The free-tier summary route now serves the refreshed Radar catalog when the feed is active — the same numbers the dashboard shows — and states which catalog answered plus its real build date, instead of always reporting release-frozen figures with a stale curation date. The supporter-key live feed stays reserved to authenticated callers of the instance. ([#11550](https://github.com/diegosouzapw/OmniRoute/pull/11550)) — thanks @maxmad64bis +- **sse:** fix LiveWS/embed-WS servers crashing at startup under the Node/tsx runtime — `liveServer.ts`, `embedWsProxy.ts` and `apiBridgeServer.ts` imported `@/shared/utils/httpClientAbortGuard` without the `.mjs` extension, so the client-abort crash guard added by [#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556) was unreachable and every dependent test failed with `ERR_MODULE_NOT_FOUND` ([#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556)). ([#11671](https://github.com/diegosouzapw/OmniRoute/pull/11671)) — thanks @jonlwheat2-gif / @yxyxy +- fix(chat): shed any of the matched provider-node's routing identifiers (public prefix or internal id) before model lookup, so composites like `//` no longer reach upstream verbatim (#11557) — thanks @maxmad64bis +- **fix(cache):** a semantic-cache entry now expires on its TTL instead of surviving until the next UTC midnight, and `dbEntries` stops counting expired rows ([#11573](https://github.com/diegosouzapw/OmniRoute/pull/11573)) — thanks @ntdatt812 +- **fix(api):** `/v1/models` schedules its stale-while-revalidate rebuild through Next's `after()` again, so a stale catalog reaches the client before the rebuild blocks the event loop ([#11574](https://github.com/diegosouzapw/OmniRoute/pull/11574)) — thanks @ntdatt812 +- **fix(check):** the `known-symbols` executor conformance gate awaits the now-async `getExecutor()`, so it stops reporting all 142 registered aliases as dead symbols and can detect a lazy import that fails ([#11582](https://github.com/diegosouzapw/OmniRoute/pull/11582)) — thanks @ntdatt812 +- **fix(pack):** register `bin/cli/utils/volatileEnvPath.mjs` in `PACK_ARTIFACT_REQUIRED_PATHS`, so the prune can no longer silently drop a module the published CLI imports on every boot ([#11588](https://github.com/diegosouzapw/OmniRoute/pull/11588)) — thanks @ntdatt812 +- **i18n(vi):** translate the five reliability-column strings on the free-provider rankings page, which rendered as `__MISSING__:` markers in the Vietnamese UI ([#11589](https://github.com/diegosouzapw/OmniRoute/pull/11589)) — thanks @ntdatt812 +- **resilience:** restore the expired-connection retry-budget probe in the token-health sweep — the `!isGitHubAccessTokenOnlyConnection` carve-out reintroduced by #11608 contradicted the boundary pinned by #11592, so a GitHub connection parked at `expired` with retry budget remaining was never probed and could never self-heal ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)). ([#11672](https://github.com/diegosouzapw/OmniRoute/pull/11672)) — thanks @jonlwheat2-gif / @ntdatt812 +- **fix(dashboard):** Provider Stats now displays combo success rates on their native 0–100 percentage scale instead of multiplying them twice ([#11595](https://github.com/diegosouzapw/OmniRoute/pull/11595)) — thanks @pacocartones +- **fix(providers):** route DuckDuckGo Web Chat through the verified `duck.ai` endpoint, refresh its free model catalog, and avoid forwarding unsolved anti-abuse challenges ([#11597](https://github.com/diegosouzapw/OmniRoute/pull/11597)) — thanks @solstxce +- **fix(ui):** Console log Refresh and Copy controls now expose localized accessible names, keep copy actions visible on keyboard focus, and announce copy completion safely ([#11599](https://github.com/diegosouzapw/OmniRoute/pull/11599)) — thanks @pacocartones +- Dropped the stale-`.eslintcache` `restore-keys` fallback from both "Restore ESLint file cache" steps in `ci.yml`, so the blocking `Lint` job can no longer be served per-file verdicts computed under a different lint config, suppressions file or lockfile. `quality.yml` had already dropped it in #11963; `ci.yml` — the workflow that actually gates PRs — had not (#11600). ([#11996](https://github.com/diegosouzapw/OmniRoute/pull/11996)) +- fix(ci): pass `--pass-on-unpruned-suppressions` in `run-eslint-json.mjs` so the CI Lint job no longer fails when a suppression is merely orphaned by a genuine fix, mirroring the identical fix already in `validate-release-green.mjs` (#11600) ([#11983](https://github.com/diegosouzapw/OmniRoute/pull/11983)) — thanks @hartmark +- fix(handoff): enforce provider allowlist for universal handoff (#11602) — universal handoff now skips summarization when the selected summary model's provider is not included in the configured provider allowlist. ([#11619](https://github.com/diegosouzapw/OmniRoute/pull/11619)) — thanks @vsd2807 +- **fix(dashboard):** Show a stable error state when Search Analytics returns an HTTP error or malformed data ([#11603](https://github.com/diegosouzapw/OmniRoute/pull/11603)) — thanks @pacocartones +- **test(gamification):** pin the aggregate profile level to the XP-derived semantics of #11604 — `getAggregateXp()` now derives `currentLevel` from the summed XP (`calculateLevel(sum)`), not `MAX(stored current_level)`, and the #3484 fixture levels are aligned with the XP curve ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)). ([#11676](https://github.com/diegosouzapw/OmniRoute/pull/11676)) — thanks @jonlwheat2-gif / @pacocartones +- **fix(dashboard):** Keep the Profile level and progress aligned with aggregate XP, including bounded handling for invalid totals ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)) — thanks @pacocartones +- **fix(dashboard):** Prevent locked hidden badges from revealing their icon or opening private badge details before they are earned ([#11605](https://github.com/diegosouzapw/OmniRoute/pull/11605)) — thanks @pacocartones +- **fix(dashboard):** Restore keyboard focus after shared modals close and cancel delayed autofocus during cleanup ([#11607](https://github.com/diegosouzapw/OmniRoute/pull/11607)) — thanks @pacocartones +- **fix(dashboard):** Enable Enter and Space activation for clickable data-table rows without hijacking nested controls ([#11610](https://github.com/diegosouzapw/OmniRoute/pull/11610)) — thanks @pacocartones +- **fix(autoCombo,sse):** vendor-retired catalog ids are dropped from the auto-combo candidate pool and no longer win on leftover `arena_elo` / `user_override` rows; `getModelLifecycleDecision` consults `model-lifecycle.json` (prefix-stripped) so aggregator traffic is not `untracked` for ids the snapshot already knows ([#11625](https://github.com/diegosouzapw/OmniRoute/issues/11625)) ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)) — thanks @MumuTW +- **test(chatcore):** move Codex/Claude combo fixtures off models the lifecycle guard now rejects — `gpt-5.1-codex`/`gpt-5-codex` are vendor-retired (snapshot, #11626) and `claude-3-5-sonnet-20241022` is shut down, so native-passthrough and combo-fallback tests switched to `gpt-5.6-sol` and `claude-sonnet-4.6` ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)). ([#11675](https://github.com/diegosouzapw/OmniRoute/pull/11675)) — thanks @jonlwheat2-gif / @MumuTW +- fix(opencode-plugin): stop a free-tier budget that rounds up across a magnitude from rendering as `1000K`/`1000M` in the model picker — `fmtTokens` chose its unit from the raw token count and then rounded with `toFixed(1)`, so 999,950–999,999 printed as `1000K` rather than `1M` and just under a billion printed as `1000M` rather than `1B` ([#11684](https://github.com/diegosouzapw/OmniRoute/pull/11684)) — thanks @abhisheksharma2411 +- Cap prose-derived Antigravity quota resets at the configured model cooldown maximum while preserving authoritative Retry-After headers and Google RetryInfo hints. (#11695) Thanks @pacocartones. ([#11823](https://github.com/diegosouzapw/OmniRoute/pull/11823)) +- Compare `_rebaseline_*` notes structurally in the ratchet-bank verifier so an object-valued note no longer reports itself as rewritten on an unmodified tree — the nightly `bank-ratchet-shrinks` lane had been aborting on every run since 2026-08-11, leaving 41,672 lines of earned file-size shrinks unbanked (#11702). ([#11892](https://github.com/diegosouzapw/OmniRoute/pull/11892)) — thanks @MumuTW +- **fix(build):** `prepublish.ts` bundles the ChatGPT Web (Codex) MCP bridge through `runBuildTool()` instead of spawning `npx.cmd` raw, fixing the build crash on Node ≥ 20/Windows where `.cmd` shims cannot be spawned without a shell (EINVAL) ([#11704](https://github.com/diegosouzapw/OmniRoute/issues/11704)) ([#11706](https://github.com/diegosouzapw/OmniRoute/pull/11706)) — thanks @jonlwheat2-gif +- fix(codex): keep `parallel_tool_calls:false` on the translated Codex Responses Lite path (#11707) ([#11984](https://github.com/diegosouzapw/OmniRoute/pull/11984)) — thanks @hartmark +- **fix(tests):** the new Gemini Web Images retirement test (#11708) called the now-async `getExecutor()` synchronously, throwing `TypeError: getExecutor(...).getProvider is not a function` instead of asserting Gemini Web chat stays available. Awaited to match the R0.3 executor-registry refactor (#11220). ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(search):** Allow REST and MCP web-fetch requests to select the advertised `anysearch-search` provider explicitly ([#11716](https://github.com/diegosouzapw/OmniRoute/pull/11716)) — thanks @pacocartones +- **fix(providers):** Keep overnight peak-hour protection active after midnight according to the window's configured start day ([#11718](https://github.com/diegosouzapw/OmniRoute/pull/11718)) — thanks @pacocartones +- **fix(providers):** correct all five g4f.space gateway descriptions to document proof-of-work credits or member API keys, variable limits, and the remote prompt boundary instead of promising free keyless access at five requests per minute ([#11738](https://github.com/diegosouzapw/OmniRoute/pull/11738)). +- **fix(db):** `model_capabilities` is created by a migration instead of lazily on the first models.dev sync, so a clean install and an upgraded install converge on the same schema regardless of which features have run ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(ci):** `check:install-upgrade` now fails on an `npm` install truncated by ENOSPC (npm reports it as a warning and still exits 0), authenticates its health probe so the version assertion works against the hardened health payload, frees the clean-install tree before the upgrade phase, and no longer reports a schema divergence computed from a boot that never served ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(routing):** harden session-affinity key derivation against oversized explicit IDs and empty structural-payload collisions, while recognizing Gemini and common text request shapes ([#11746](https://github.com/diegosouzapw/OmniRoute/pull/11746)) — thanks @MumuTW +- **fix(packages/browser-pool):** regenerate `package-lock.json` so the `packages/browser-pool` workspace's locked `playwright`/`@types/node` (and transitives) match its `package.json` specs, fixing cache-only/offline installs (`npm ci --offline`, Nix `buildNpmPackage`) that previously failed with `ENOTCACHED` ([#11747](https://github.com/diegosouzapw/OmniRoute/issues/11747)) — thanks @benjaminkitt ([#11784](https://github.com/diegosouzapw/OmniRoute/pull/11784)) — thanks @geekyNads +- **fix(assets):** Stop distributing 79 provider and CLI logos without verified source/license provenance, keep every provider/tool available through the internal generic icon, and prevent those IDs from falling through to an external icon CDN. ([#11750](https://github.com/diegosouzapw/OmniRoute/pull/11750)) +- **fix(providers):** retire the common `chatgpt-web` / `cgpt-web` provider because its pre-key and proof-of-work provenance could not be cleared ([#11754](https://github.com/diegosouzapw/OmniRoute/pull/11754)) — explicit chat and image requests, slash-prefixed models, persisted aliases, proxy overrides, and combo targets now fail closed with a sanitized HTTP `410` / `PROVIDER_RETIRED` before upstream dispatch. Migration 163 tombstones matching connections and invalidates their active leases without deleting history or widening API-key allowlists. The independent `chatgpt-web-codex` / `cgpt-codex` provider and its MIT-noticed vendored implementation remain available. +- **fix(usage):** quota and usage refresh no longer 409 when an exclusive lease reserves the connection ([#11758](https://github.com/diegosouzapw/OmniRoute/pull/11758)) — thanks @TheDemonTuan +- Keep the embedding registry's vector width and `embedding` type on models when a synced model exists for the same id, so `/v1/models` no longer reports registry-described embedding models widthless or untyped (#11761) — thanks @rahilmavani +- Correct `google/gemini-embedding-001` on the OpenRouter route to 3072 dimensions, the width it returns when `dimensions` is not sent (#11761) — thanks @rahilmavani +- **fix(vision-bridge):** Restrict automatic vision routing and fallbacks to models available in authoritative live provider catalogs, including aliases and effort variants ([#11767](https://github.com/diegosouzapw/OmniRoute/issues/11767)) — thanks @pacocartones ([#11813](https://github.com/diegosouzapw/OmniRoute/pull/11813)) +- **fix(providers):** reclassify Cerebras as a one-time $5 signup credit (payment method required, 30-day validity), not a recurring no-card 1M tokens/day trial ([#11773](https://github.com/diegosouzapw/OmniRoute/issues/11773)) ([#12591](https://github.com/diegosouzapw/OmniRoute/pull/12591)) — thanks @HouMinXi +- fix(cli): stop prepublish from re-rebuilding the already-built ESM-only opencode-plugin dist (#11787) ([#11990](https://github.com/diegosouzapw/OmniRoute/pull/11990)) — thanks @hartmark +- **fix(db):** Fresh installs no longer log a non-fatal `no such table: compression_run_telemetry` warning when retention cleanup runs before the lazily-created telemetry table exists ([#11802](https://github.com/diegosouzapw/OmniRoute/pull/11802)) — thanks @RaviTharuma +- fix(sse): set X-OmniRoute-Selected-Connection-Id on successful combo dispatches so downstream consumers stop falling back to an empty connection id (#11810) ([#11986](https://github.com/diegosouzapw/OmniRoute/pull/11986)) — thanks @hartmark +- **fix(services):** embedded CLIProxyAPI lifecycle checks now use public `/healthz`, while model discovery uses the configured dedicated data-plane API key instead of the management password ([#11811](https://github.com/diegosouzapw/OmniRoute/pull/11811)) — thanks @RaviTharuma +- Fix persisted model steps returning an error in the playground route simulator, and warn when structural steps cannot be simulated. ([#11862](https://github.com/diegosouzapw/OmniRoute/pull/11862)) — thanks @pacocartones +- **fix(providers):** Antigravity's dynamic mitmAlias table no longer routes `gemini-3.7-flash-{high,medium,low}` to a literal tier-suffixed upstream id just because one connected account's own discovery listed it directly — those display ids always resolve through the safe `gemini-3.7-flash-tiered` static alias, so one account's Google-provisioned access no longer 404s every sibling account of the provider ([#11824](https://github.com/diegosouzapw/OmniRoute/issues/11824), [#11651](https://github.com/diegosouzapw/OmniRoute/issues/11651)) ([#11988](https://github.com/diegosouzapw/OmniRoute/pull/11988)) — thanks @hartmark +- **fix(dashboard):** Expose Profile loading, errors, page structure, and XP progress to assistive technologies ([#11838](https://github.com/diegosouzapw/OmniRoute/pull/11838)) — thanks @pacocartones +- **fix(socks):** `Agent.connectTimeout` now bounds both SOCKS handshake and TLS connect, and `family === null` no longer falls back to `fetch-socks` ([#11842](https://github.com/diegosouzapw/OmniRoute/pull/11842)) — thanks @maxmad64bis +- **perf(compression):** OOM mitigations for large payload hashing, memoization, and token estimation ([#11844](https://github.com/diegosouzapw/OmniRoute/pull/11844) — thanks @AndrianBalanescu) +- Excluded Next.js Node File Trace manifests (`*.nft.json`) from the published npm tarball. They are build-time metadata and are never read while serving, but had grown to 668.7 MB — 61% of the package — which pushed the upload past the registry limit and made `npm publish` fail with `413 Payload Too Large`. ([#11864](https://github.com/diegosouzapw/OmniRoute/pull/11864)) +- **fix(cli):** keep the global OmniRoute server `--base-url` separate from provider-node `--endpoint` values in add, update, and validate commands ([#11860](https://github.com/diegosouzapw/OmniRoute/pull/11860)) — thanks @pacocartones +- **fix(config):** Nous Research's `Hermes-4-405B` model now displays as "Hermes 4 405B (Nous Research)" in both the provider registry and the free-model catalog, instead of the mislabelled "Hermes 4 7B" ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#11993](https://github.com/diegosouzapw/OmniRoute/pull/11993)) +- **fix(provider/nous):** inject required user= tag into Nous Research inference requests to resolve upstream 400 "missing tags" error ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#12044](https://github.com/diegosouzapw/OmniRoute/pull/12044)) +- **fix(build):** `npm run build` now fails in one second with a named package and a copy-pasteable fix when npm silently drops an externalised optional native dependency, instead of dying four minutes in with `Module not found: Can't resolve 'better-sqlite3'` ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- **fix(install):** `postinstall` no longer throws `ReferenceError: isAndroid is not defined` — failing the whole `npm install` — when the `better-sqlite3` rebuild fallback times out; the manual-fix guidance is reachable again ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- Fixed the Alibaba free-tier allowlist test that went red on its own once the shipped catalog's `validUntil` (2026-08-27) passed, leaving every PR and `main` with a failing `Unit Tests (1/8)`. The test now builds its own packs with dates it controls, and covers the expired-pack fallback that production has actually been serving. ([#11867](https://github.com/diegosouzapw/OmniRoute/pull/11867)) +- Split the npm registry upload into its own GitHub-hosted job. npm refuses `--provenance` from a self-hosted runner (`422 ... Only "github-hosted" runners are supported`), which blocked the v3.8.50 publish; the heavy verification cannot move to a hosted runner, so it now hands the proven tarball over instead. ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(ui):** the shared `DataTable` loading state no longer reads its decorative ⏳ glyph out to assistive technology, and now carries the same `role="status"` / `aria-live="polite"` / `aria-busy="true"` semantics as `PageLoading` ([#11869](https://github.com/diegosouzapw/OmniRoute/pull/11869)) — thanks @pacocartones +- **fix(api):** Bound the `timeout` accepted by `POST /api/providers/[id]/login` to the same 15s–600s window the Adobe Firefly and Conol browser-login paths already enforce, so a request can no longer pin a headful browser and the single active-login slot with an arbitrarily large poll budget. ([#11871](https://github.com/diegosouzapw/OmniRoute/pull/11871)) — thanks @pacocartones +- **fix(api):** Stop the tunnel and MITM routes from returning raw child-process error text, which disclosed host paths, binary install locations and Tailscale `tskey-*` credentials in a body some of these routes serve to non-loopback callers, and return a real 400 instead of a framework 500 when a tunnel request body fails validation. ([#11872](https://github.com/diegosouzapw/OmniRoute/pull/11872)) — thanks @pacocartones +- **fix(open-sse):** stream TTFT/ITL are sampled from a monotonic clock (`performance.now`) instead of `Date.now`, so an NTP correction or wall-clock jump can no longer inflate a genuine TTFT or produce a negative one that the `ttft >= 0` guard silently drops — protecting the router's OTel/EWMA/`usage_history`/speed-ranking signals ([#11873](https://github.com/diegosouzapw/OmniRoute/pull/11873)) — thanks @pacocartones +- **fix(providers):** xAI `reasoning_effort: "xhigh"` now reaches grok-4.6+ instead of being silently clamped to `"high"` ([#11879](https://github.com/diegosouzapw/OmniRoute/pull/11879)) — thanks @NoxzRCW +- **fix(dashboard):** the providers page no longer crashes into the error boundary when a provider id collides with an `Object.prototype` member (`constructor`, `__proto__`); icon lookups are own-property guarded ([#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)) — thanks @NoxzRCW +- **fix(skills):** injected skill tools declared in shorthand (`{"content": "string"}`) now forward valid JSON Schema, unblocking providers that validate tool schemas strictly such as Zhipu GLM on the Console Go tier ([#11881](https://github.com/diegosouzapw/OmniRoute/pull/11881)) — thanks @NoxzRCW +- **fix(api):** `POST /api/playground/simulate-route` now surfaces `combo-ref` and `provider-wildcard` persisted combo steps with a specific warning (naming the referenced combo, or the unresolved `provider/model` wildcard) instead of folding them into a generic "unsupported step" count; a `provider-wildcard` step is also now included as an unresolved target so the operator can see it is in the route (ported from [#11882](https://github.com/diegosouzapw/OmniRoute/pull/11882) — thanks @NoxzRCW). ([#11930](https://github.com/diegosouzapw/OmniRoute/pull/11930)) +- **fix(translator):** the streaming OpenAI→Claude translator keeps upstream usage, including prompt-cache tokens, when it arrives on a trailing `choices: []` chunk (Fireworks and any upstream using `stream_options.include_usage`) ([#11883](https://github.com/diegosouzapw/OmniRoute/pull/11883)) — thanks @NoxzRCW +- **fix(cli):** `omniroute update --apply` now tells you explicitly whether a running server was detected and, if so, that you must run `omniroute restart` to apply the update — it never restarted anything and previously implied the update was already live once files were installed. The dashboard's npm-mode Update flow (`/api/system/version`) now tries OmniRoute's own PID-file-managed supervisor before falling back to pm2, and reports an honest "restart required" step instead of a silent pm2-only "skipped" that read like a completed update. The server-side latest-version lookup backing the dashboard's update banner also gained `--prefer-online`, closing the same stale-npm-cache class already fixed in the CLI's own copy for #4376 ([#11885](https://github.com/diegosouzapw/OmniRoute/issues/11885)). ([#12005](https://github.com/diegosouzapw/OmniRoute/pull/12005)) +- **fix(resilience):** clear persisted LKGP pins when a target suffers connection/provider exhaustion or is skipped before dispatch due to cooldown/exhaustion/unavailability, preventing subsequent requests from repeatedly prioritizing known-dead providers ([#11911](https://github.com/diegosouzapw/OmniRoute/issues/11911)). ([#12013](https://github.com/diegosouzapw/OmniRoute/pull/12013)) — thanks @HouMinXi +- **fix(sse):** the OpenAI→Claude stream translator now defers the terminal `message_delta`/`message_stop` emission until the real usage block has arrived (or a genuine end-of-stream flush forces it) instead of emitting it immediately on `finish_reason` — previously, when the trailing usage-only chunk (`{"choices":[],"usage":{...}}`) arrived *after* the `finish_reason` chunk (the normal order for Fireworks/vLLM/Together and other `stream_options.include_usage` upstreams), the client-visible `message_delta` still carried stale/zero usage even though `state.usage` was internally corrected too late to matter (ported from [#11915](https://github.com/diegosouzapw/OmniRoute/pull/11915) — thanks @HouMinXi). ([#11933](https://github.com/diegosouzapw/OmniRoute/pull/11933)) +- **fix(sse):** `stripStore()` now forces `store=false` for stateless OpenAI-compatible Responses-API targets (unless the connection has explicitly opted in via `providerSpecificData.openaiStoreEnabled`), instead of only handling the `openai`/`agentrouter` cases — a client-supplied `store` value previously passed through untouched to backends that don't actually persist responses server-side ([#11916](https://github.com/diegosouzapw/OmniRoute/pull/11916) — thanks @HouMinXi). +- **fix(models):** custom provider-node models (synced, custom, and alias-backed) now appear under their configured prefix in the unified catalog when the operator's model-id prefix mode is canonical, instead of being dropped whenever alias-inclusion was otherwise disabled ([#11918](https://github.com/diegosouzapw/OmniRoute/pull/11918) — thanks @HouMinXi). +- **fix(models):** the unified model catalog now suppresses stale static registry models (including effort-tier variants) for any provider whose active connection has an authoritative live synced catalog, not only providers already using exclusive-synced-listing — a connection with `providerUsesAuthoritativeLiveCatalog` previously kept serving both the live-synced models and the stale static rows side by side ([#11919](https://github.com/diegosouzapw/OmniRoute/pull/11919) — thanks @HouMinXi). +- **fix(ui):** `ProviderIcon`'s three remaining unguarded lookups (`PROVIDER_ICON_ALIASES`, `LOCAL_SVG_ALIASES`, `THEMED_SVGS`) now use `Object.hasOwn()` own-property checks — a provider id such as `constructor` or `__proto__` previously resolved through the prototype chain instead of falling through to the unknown-provider CDN fallback (`getLobeProviderIcon()` itself was already guarded by [#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)); `ProviderPageHeader`'s `color` field is also now optional, matching the rest of the component's defensive typing (ported from [#11920](https://github.com/diegosouzapw/OmniRoute/pull/11920) — thanks @HouMinXi). ([#11935](https://github.com/diegosouzapw/OmniRoute/pull/11935)) — thanks @NoxzRCW +- fix(ollama): preserve multi-byte UTF-8 content split across stream chunks in the Ollama NDJSON transform, which previously corrupted CJK/emoji into U+FFFD (#11921) — thanks @pacocartones +- **fix(providers):** OrcaRouter chat requests now target `/v1/chat/completions` instead of the bare `/v1` API root, fixing the upstream `404 Invalid URL (POST /v1)` ([#11923](https://github.com/diegosouzapw/OmniRoute/pull/11923)). — thanks @echel0nn +- **fix(plugins):** deliver the `onStreamComplete` event to disk-installed plugins. The event shipped in v3.8.50 (#9669) was emitted internally but had no plugin-facing wiring, so no plugin could ever subscribe: the manifest schema silently dropped `hooks.onStreamComplete`, and the loader/manager only knew the seven legacy hooks. `onStreamComplete` is now a declarable manifest hook, wired through the loader and registered by the manager like the other hooks, and its payload carries a `requestId` so consumers can correlate the stream-completion event with the originating request ([#11934](https://github.com/diegosouzapw/OmniRoute/pull/11934)) — thanks @amaleta / @pacocartones +- **fix(db):** the Qdrant embedding-model dropdown now lists local/self-hosted providers (Ollama, LM Studio, vLLM, etc.) — an active connection is treated as "configured" when the provider allows an optional API key, not only when it has a real key or OAuth, so a running local embedding provider is no longer hidden from the picker ([#11949](https://github.com/diegosouzapw/OmniRoute/issues/11949)) ([#11995](https://github.com/diegosouzapw/OmniRoute/pull/11995)) +- **fix(providers):** Vertex AI Anthropic partner-model discovery now calls the Model Garden `v1beta1` publisher list (`/v1beta1/publishers/anthropic/models`, global) and parses its `publisherModels` envelope, so Claude models auto-synced from Vertex populate the active live catalog and route at request time instead of returning `Model '' is not available in the active live catalog` ([#11991](https://github.com/diegosouzapw/OmniRoute/issues/11991)) — thanks @fabioluissilva ([#11998](https://github.com/diegosouzapw/OmniRoute/pull/11998)) +- **fix(cli):** support `--base-url` alongside `--endpoint` in `omniroute nodes add`, `update`, and `validate` subcommands to prevent global `--base-url` shadowing issues ([#11999](https://github.com/diegosouzapw/OmniRoute/issues/11999)). ([#12033](https://github.com/diegosouzapw/OmniRoute/pull/12033)) — thanks @HouMinXi +- **fix(providers):** `cloudflare-ai` no longer refuses image content parts for every Workers AI model ([#12002](https://github.com/diegosouzapw/OmniRoute/pull/12002)) — the plain-string `content` requirement behind #2539 is carried by the _model_ schema, not by the `/ai/v1/chat/completions` endpoint (measured: an all-text part array returns 200 on `@cf/mistralai/mistral-small-3.1-24b-instruct`, `@cf/meta/llama-4-scout-17b-16e-instruct` and `@cf/meta/llama-3.3-70b-instruct-fp8-fast`, and 400 on the text-only `@cf/qwen/qwen2.5-coder-32b-instruct`). `transformRequest()` flattened every array and threw on the first non-text part (#6390), so image input was refused for vision-capable Cloudflare models that accept it. All-text arrays are still flattened — the one shape every model accepts — while an array carrying a non-text part is passed through untouched, so the attachment is still never silently dropped. Regression guards: `tests/unit/cloudflare-ai-image-parts-6390.test.ts`. — thanks @davidlinfr +- **fix(resilience):** decouple the limiter-managed execution backstop from the queue-wait budget — new `requestQueue.executionMaxWaitMs` (env `RATE_LIMIT_EXECUTION_MAX_WAIT_MS`, default 600000 = 10 min) now feeds Bottleneck's post-dispatch `expiration`, while `requestQueue.maxWaitMs` keeps its documented queue-wait semantics. Previously the queue-wait budget doubled as the execution expiration, so legitimate long-running calls on non-incremental gateways (whole generation buffered before the first upstream byte, e.g. Console Go / Command Code tiers serving GLM models) were killed mid-flight at the queue budget with a false 504 `RATE_LIMIT_EXECUTION_TIMEOUT` — the local limiter undercut the provider-aware upstream fetch-start timeouts. The surfaced 504 message now names `requestQueue.executionMaxWaitMs`; the error keeps the #4165 guarantees (disclaims an upstream timeout, preserves the Bottleneck error as `cause`, branded code + trusted provenance, classified request-scoped so combo falls back). A real queue-wait bound (the `Promise.race` around `limiter.schedule()` sketched in #9533) remains future work. (#12025) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **Call logs:** keep the `error` field when an artifact exceeds the storage cap, instead of replacing it with the omission marker. The error is the only field that says *why* a request failed and is typically ~90 bytes next to the multi-hundred-KB bodies that trip the cap, so dropping it left a size-limited row undiagnosable — a provider outage, a local timeout and an upstream 400 all rendered identically. It is now preserved at every fallback stage, truncated to 4KB if it is itself large ([#12026](https://github.com/diegosouzapw/OmniRoute/issues/12026)). ([#12095](https://github.com/diegosouzapw/OmniRoute/pull/12095)) — thanks @ntdat812 +- **fix(diagnostics):** preserve the error field (truncated to 4KB with a `[truncated: …]` suffix) in every call-log artifact size-limit fallback stage. Previously the minimal fallback replaced the error with `[omitted: call log artifact size limit exceeded]`, so an oversized artifact row showed nothing about WHY the request failed — e.g. 91 of 847 opencode-go 504 rows on one production instance were undiagnosable from the dashboard. Oversized request/response bodies are still omitted exactly as before; the error cap is independent of the payload sizes that tripped the fallback. (#12026) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **fix(sse):** OpenAI Responses clients that declare the native `web_search` tool now receive a spec-shaped `web_search_call` output item with `action.sources` alongside the preserved function-call round-trip, so search results executed through OmniRoute's own search backend are consumable by standard Responses clients (Codex, pi-web-access, …). ([#12031](https://github.com/diegosouzapw/OmniRoute/pull/12031)) — thanks @watchingdogs +- **fix(cli):** use in-thread alias resolver hooks on modern runtimes to avoid deprecation noise and improve Node.js forward compatibility ([#12073](https://github.com/diegosouzapw/OmniRoute/issues/12073)). ([#12083](https://github.com/diegosouzapw/OmniRoute/pull/12083)) — thanks @xiaoyaner0201 +- **fix(combo):** an operator-set **Agent Features → Context length** on a combo is now honored at request time. The value was persisted and advertised through `/v1/models`, but `resolveComboContextLimit()` never consulted it — so a multi-target combo whose members carry no per-model window fell through to the provider's generic `defaultContextLength` (openrouter 128000, command-code 200000) and rejected large requests with `Input exceeds context window … limit 128000` despite the combo being explicitly sized much larger. An identical single-target combo worked, because it collapses to its concrete target before the guard runs. Invalid values (0/negative/NaN/Infinity) are ignored, so the existing target → combo-min → fallback order is unchanged. ([#12090](https://github.com/diegosouzapw/OmniRoute/pull/12090)) — thanks @adivekar-utexas +- **fix(sse):** passthrough streams now estimate usage on finish when upstream closes without usage even with `stream_options.include_usage` — avoids `0 tokens / 0%` for providers that stay silent (and correctly handles trailing empty-choices usage) ([#12151](https://github.com/diegosouzapw/OmniRoute/pull/12151)) — thanks @maxmad64bis +- **fix(combos):** clearing an agent feature in the combos editor now persists — unchecking context cache protection, or emptying the system message or tool filter, sends an explicit `null` instead of dropping the field from the `PUT` body, which the update merge read as "leave unchanged" ([#12177](https://github.com/diegosouzapw/OmniRoute/pull/12177)) — thanks @foreveryh / @ntdatt812 +- **fix(memory):** self-hosted embedding endpoints now vectorize — the vector width is measured from the first embedding that comes back instead of being read from a registry that cannot describe them, so `vec_memories` is created and memories stop piling up unvectorized behind a green health check ([#12180](https://github.com/diegosouzapw/OmniRoute/pull/12180)) — thanks @kanade-hoshino / @ntdatt812 +- **fix(translator):** the leading `system` message now reaches Responses-API upstreams when its `content` is a content-part array — it was read as `typeof content === "string" ? content : ""`, so a prompt-caching client (Anthropic `cache_control`, the shape LiteLLM and the Anthropic SDK emit) had its entire system prompt replaced by an empty `instructions`. The request was still accepted with a normal `prompt_tokens` count, so the model answered with no instructions and nothing in the response said they were missing. Mid-conversation system turns already handled the array shape ([#7056](https://github.com/diegosouzapw/OmniRoute/pull/7056)); only the first one did not ([#12206](https://github.com/diegosouzapw/OmniRoute/issues/12206)). Regression guard: `tests/unit/translator-openai-responses-system-content-parts.test.ts`. ([#12207](https://github.com/diegosouzapw/OmniRoute/pull/12207)) — thanks @Gorillaz322 +- **fix(free-tier):** `/api/free-tier/summary` no longer computes its totals from a Radar feed built before the catalog the running release ships. When the cached feed is older — or carries no build date at all — the route answers from the shipped catalog, resolved through the operator's local model state so disabled and tombstoned models stay out of the numbers ([#12215](https://github.com/diegosouzapw/OmniRoute/pull/12215)). — thanks @maxmad64bis +- **fix(oauth):** Keep a Claude personal workspace and a Team organization as separate connections — they share the same email and `accountUUID`, so the email-only OAuth dedup let the second login overwrite the first account's tokens; `organizationUUID` now disambiguates them, the way `workspaceId` does for Codex ([#12222](https://github.com/diegosouzapw/OmniRoute/pull/12222)) — thanks @davidebaraldo +- **fix(resilience):** a 402 on a single paid model of a passthrough/gateway provider (e.g. `kilo-gateway`, `ollama-cloud`) no longer terminalizes the whole connection with a never-auto-recovered `credits_exhausted` status — only the paid model is locked out, so free models on the same key keep serving. 402 variant of [#3027](https://github.com/diegosouzapw/OmniRoute/issues/3027). Single-credential providers are unaffected — a 402 there is still treated as the key being genuinely out of credit ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239) / [#10616](https://github.com/diegosouzapw/OmniRoute/issues/10616)) ([#12242](https://github.com/diegosouzapw/OmniRoute/issues/12242)) — thanks @brick30llc-ctrl ([#12266](https://github.com/diegosouzapw/OmniRoute/pull/12266)) — thanks @geekyNads +- **fix(sse):** trust `finish_reason: "length"`/`"max_tokens"` over the reasoning-consumed-token ratio in response quality validation, so a reasoning model truncated below the old 90% threshold correctly fails and retries instead of returning empty content as a silent "success" ([#12262](https://github.com/diegosouzapw/OmniRoute/pull/12262)) — thanks @brick30llc-ctrl +- **fix(settings):** `PUT /api/settings/cache-config` now persists `alwaysPreserveClientCache` to the flat general settings the runtime cache-control policy actually reads; previously the value landed in the databaseSettings "cache" section and was silently ignored, so the endpoint had no effect on `cache_control` passthrough ([#12304](https://github.com/diegosouzapw/OmniRoute/pull/12304)) — thanks @davidebaraldo +- **fix(grok-cli):** treat omitted SuperGrokPro `creditUsagePercent` as 0% used so Provider Limits still renders a weekly bar (proto3 zero-elision) ([#12312](https://github.com/diegosouzapw/OmniRoute/pull/12312)) — thanks @HouMinXi +- **fix(combo):** Expose the two Auto-Combo scoring factors nobody could set — the combo validation schema and the dashboard weight sliders both declared 13 of the scorer's 15 factors, so `connectionDensity` (spreads load across a provider's connections) and `quality` were dropped on save and offered nowhere. The sliders also shipped their own default table that differed from the engine's on every non-zero factor and summed to 1.05, so the percentages shown next to them added up to 105%. Both lists now match `DEFAULT_WEIGHTS`, and a test keeps them there. Note that a combo whose stored `weights` omitted the two keys was effectively running with them at zero and the other thirteen renormalized upward; it now runs with the engine's intended distribution, so its routing does shift ([#12314](https://github.com/diegosouzapw/OmniRoute/pull/12314)) — thanks @maxmad64bis +- **fix(docs):** The free-tier reference no longer says its numbers come "confidence tagged per row" — no catalog entry carries a confidence tag and the API serves none, so every figure on that page is an estimate of the same, unstated quality. The page now states what an entry does vouch for: an independently documented hard stop (set by hand with the source in a comment, never defaulted to `true`) and a prompt-training disclosure, both with live counts the `check:docs-counts` gate keeps honest ([#12318](https://github.com/diegosouzapw/OmniRoute/pull/12318)) — thanks @maxmad64bis +- **fix(usage):** `adobe-firefly` and `firefly` have had a working usage fetcher since Adobe Firefly landed, but neither was ever added to the registration list, so the provider-plugin manifest, `genericQuotaFetcher` and the free-access quota cache all reported them as having no usage support — while `USAGE_SUPPORTED_PROVIDERS` said the opposite. Both are now declared, which also means their credit balance is fetched like any other declared provider's: `registerGenericQuotaFetchers` now registers a generic quota fetcher for them, and `resolveFreeAccessState` no longer returns early. A test holds the registration list to the dispatcher's switch in both directions, which is what the module's own docstring already asked for in prose ([#12321](https://github.com/diegosouzapw/OmniRoute/pull/12321)) — thanks @maxmad64bis +- **fix(quota):** drop the generic quota cache (agy / Antigravity / Claude OAuth) on an upstream 429 so reset-aware scoring does not keep a 60s stale snapshot, and force-refresh the next usage fetch so inner provider caches cannot recache the same window ([#12325](https://github.com/diegosouzapw/OmniRoute/pull/12325)) — thanks @HouMinXi +- **fix(combos):** deleting a combo now clears its persisted LKGP pins instead of leaving unreachable `key_value` rows behind ([#12326](https://github.com/diegosouzapw/OmniRoute/issues/12326)) ([#12425](https://github.com/diegosouzapw/OmniRoute/pull/12425)) — thanks @KrzysiekSko +- **fix(sse):** Keep ZWNJ (U+200C) and ZWJ (U+200D) in assistant text, reasoning and tool-call arguments — Persian/Kurdish half-space (`ارائه‌دهنده`), Arabic/Indic shaping and emoji sequences no longer lose them; the response de-obfuscation now removes joiners only between ASCII word characters, where the request side inserts them ([#12186](https://github.com/diegosouzapw/OmniRoute/issues/12186)) — thanks @rezjalibd ([#12359](https://github.com/diegosouzapw/OmniRoute/pull/12359)) — thanks @pacocartones +- **fix(resilience):** count resolved upstream 5xx results against the provider circuit breaker on the chat path — `CircuitBreaker.execute()` no longer reads a resolved `{ success: false, status: 5xx }` as a success that cancels the call-site failure, so a provider answering 503s now trips its breaker instead of staying `CLOSED` at `failureCount: 1`; single-model and combo dispatches are each accounted exactly once ([#12254](https://github.com/diegosouzapw/OmniRoute/issues/12254)) ([#12360](https://github.com/diegosouzapw/OmniRoute/pull/12360)) — thanks @pacocartones +- **fix(providers):** resolve the Codex quota auto-ping model from the live provider catalog and lifecycle registry instead of the retired `gpt-5.1-codex-mini`, and pause the ping with one actionable warning when no selectable Codex model exists rather than retrying a shut-down id every cooldown window ([#11905](https://github.com/diegosouzapw/OmniRoute/issues/11905)) ([#12361](https://github.com/diegosouzapw/OmniRoute/pull/12361)) — thanks @pacocartones +- **fix(api):** keep the `{created, data}` wrapper on combo-routed `/v1/images/generations` responses and default Codex image results to `b64_json` on both `/v1/images/generations` and `/v1/images/edits` so Codex CLI's built-in `image_gen` can decode them ([#12268](https://github.com/diegosouzapw/OmniRoute/issues/12268)) ([#12362](https://github.com/diegosouzapw/OmniRoute/pull/12362)) — thanks @pacocartones +- **fix(sse):** Name the shadowed custom provider node when a built-in provider id/alias (e.g. `openference` → `of`) reserves the prefix of an existing OpenAI/Anthropic-compatible node, so the runtime `No active credentials for provider: ` error explains that the prefix routed to the built-in and never reached the node's healthy connections, instead of contradicting the dashboard ([#11943](https://github.com/diegosouzapw/OmniRoute/issues/11943)) — thanks @morpheus9393 ([#12365](https://github.com/diegosouzapw/OmniRoute/pull/12365)) — thanks @pacocartones +- **fix(i18n):** wrap `ccOnboardingKeyPlaceholder` in ICU single quotes across all 43 locales so angle brackets render literally instead of being parsed as rich-text tags, which crashed the Claude Code onboarding block with `INVALID_MESSAGE: INVALID_TAG` ([#12302](https://github.com/diegosouzapw/OmniRoute/issues/12302)) ([#12369](https://github.com/diegosouzapw/OmniRoute/pull/12369)) — thanks @gonisulaimann +- **fix(models):** publish `effort_tiers` on Kimi K3's synced base-model entries (`kmca/k3`, `kmca/k3-256k`) so catalog-only clients (OpenCode, plain SDK pickers) can see and select the reasoning tiers (`low`/`high`/`max`) the synced metadata already carried — the `isSkippedEffortProvider` gate no longer suppresses tier visibility on those base entries, while synthetic `-` variant generation stays prevented and Codex/GLM base models remain excluded unchanged ([#12299](https://github.com/diegosouzapw/OmniRoute/issues/12299)) ([#12371](https://github.com/diegosouzapw/OmniRoute/pull/12371)) — thanks @gonisulaimann +- **fix(guardrails):** keep `auto`/`auto/*` virtual combos exempt from the Vision Bridge `fixedModel` credential guard so a combo target is passed through instead of silently falling back to global auto-selection ([#12237](https://github.com/diegosouzapw/OmniRoute/issues/12237)) ([#12373](https://github.com/diegosouzapw/OmniRoute/pull/12373)) — thanks @pacocartones +- **fix(combo):** capability-filter exhaustion caused by `max_tokens` above every target's known output limit now reports that reason (requested `max_tokens` vs the pool's highest known ceiling) instead of the unrelated "supports structured output" message ([#12229](https://github.com/diegosouzapw/OmniRoute/issues/12229)) — thanks @DW-MediaLab ([#12374](https://github.com/diegosouzapw/OmniRoute/pull/12374)) — thanks @pacocartones +- **fix(auth):** the `least-used` account strategy now prefers accounts without backoff before falling back to oldest `lastUsedAt`, the same tie-break `round-robin` already applies, so a failover no longer lands on a just-rate-limited account for a single request ([#12279](https://github.com/diegosouzapw/OmniRoute/issues/12279)) — thanks @tenshiak ([#12375](https://github.com/diegosouzapw/OmniRoute/pull/12375)) — thanks @pacocartones +- **fix(docker):** the `chatgpt-web-codex-browser` image now finds the Chrome binary under `chrome-linux64/` (Chrome for Testing layout in `playwright:v1.62.0-noble`) as well as the legacy `chrome-linux/`, so the container no longer crash-loops with `exec: --headless=new: not found` ([#12024](https://github.com/diegosouzapw/OmniRoute/issues/12024)) ([#12376](https://github.com/diegosouzapw/OmniRoute/pull/12376)) — thanks @pacocartones +- **fix(providers):** declare `groq/compound` and `allam-2-7b` as non-reasoning models in the curated Groq registry so `reasoning_effort` / `output_config.effort` / `thinking` from Claude Code are stripped instead of forwarded, which Groq rejected with HTTP 400 ([#12134](https://github.com/diegosouzapw/OmniRoute/issues/12134)) ([#12379](https://github.com/diegosouzapw/OmniRoute/pull/12379)) — thanks @pacocartones +- **fix(executors):** `OpencodeExecutor` no longer forces a direct connection when the connection has a proxy assigned in Proxy Management but no per-account proxies: the single-account fast path used to wrap the upstream dispatch in the direct-egress sentinel, discarding the ambient proxy context the chat handler had pinned from `proxy_assignments`, so API-key `opencode`/`opencode-go` connections egressed from the host IP (and hit geoblocks) despite the assignment. The direct pin is now applied only when no ambient proxy context exists ([#11894](https://github.com/diegosouzapw/OmniRoute/issues/11894) — thanks @hizzt) ([#12380](https://github.com/diegosouzapw/OmniRoute/pull/12380)) — thanks @pacocartones +- **fix(api):** `GET /v1/models` with `MODELS_CATALOG_PREFIX_MODE=canonical` (or `?prefix=canonical`) now lists providers whose registry alias is undefined or equal to their own id (Antigravity, Antigravity CLI and other self-aliased built-ins) — their single `provider/model` id was dropped by the alias/canonical duplicate guard in the static, synced, custom and alias-backed catalog loops ([#12058](https://github.com/diegosouzapw/OmniRoute/issues/12058)) — thanks @cheynetom ([#12381](https://github.com/diegosouzapw/OmniRoute/pull/12381)) — thanks @pacocartones +- **fix(translator):** Drop replayed `thinking` blocks that carry no signature (the shape produced from cross-provider `reasoning_content`) instead of stamping the default Claude signature on them, which Anthropic rejected with `400 Invalid signature in thinking block` on the next turn served by an Anthropic rung ([#12105](https://github.com/diegosouzapw/OmniRoute/issues/12105)) — thanks @atescivitci-cmd ([#12386](https://github.com/diegosouzapw/OmniRoute/pull/12386)) — thanks @pacocartones +- **fix(cli):** Resolve Bun's `--preload` polyfill path against the package root instead of `dist/`, so `omniroute` installed with `bun install -g` no longer crashes at startup with `error: preload not found …/dist/open-sse/utils/setupPolyfill.ts` ([#11980](https://github.com/diegosouzapw/OmniRoute/issues/11980)) — thanks @joglomedia ([#12387](https://github.com/diegosouzapw/OmniRoute/pull/12387)) — thanks @pacocartones +- **fix(providers):** `gemini-business` now publishes its model catalog — `/v1/models` and `/v1/providers/gemini-business/models` list the 12 enterprise Gemini ids the executor understands instead of returning an empty list (#12107) ([#12389](https://github.com/diegosouzapw/OmniRoute/pull/12389)) — thanks @pacocartones +- **fix(db):** install `busy_timeout` before the SQLite connection's first statement so a process opening the database while another one closes its WAL connection waits out the transient EXCLUSIVE lock instead of dying with `database is locked`, and recognise the drivers' real BUSY/PROTOCOL/IOERR errors as transient in the corruption probe so the same lock no longer renames the database away as corrupt; deflakes `cross-process contenders never both acquire the same connection` (#12394 — thanks @pacocartones) +- **fix(chat-admission):** derive the `chat_admission_busy` 503 `Retry-After` from observed heavyweight-lease occupancy — the larger of the exhausted `OMNIROUTE_CHAT_ADMISSION_QUEUE_MS` window and the time since capacity last turned over, capped at 60 s — instead of a fixed 1 s (structural) / 2 s (byte-stage) hint that invited Codex/agent fan-out clients to re-send ~1 MiB `/v1/responses` bodies every second into a gate held for the whole SSE lifetime; an idle gate keeps the historical floors ([#12135](https://github.com/diegosouzapw/OmniRoute/issues/12135)) (#12395 — thanks @pacocartones) +- **fix(api-manager):** the API key permissions modal no longer silently drops `allowedCombos` entries its Combo picker cannot render — routing-rule names such as `rt-*`, which the backend already honours — when "All" is clicked and the key is switched back to "Restrict"; those entries now survive the toggle, are listed read-only under the combo list so the count and the list agree, and are saved back verbatim instead of persisting `[]` (deny-all) (#12397 — thanks @pacocartones) +- **fix(catalog):** write the NUL separator of the catalog connection memo key, the provider serviceKind memo key, the Video Bridge promotion group key and a JSON-exactness test fixture as the `\u0000` escape instead of a raw byte — same runtime value, but the raw byte made git, GitHub and ripgrep treat those files as binary (hidden PR diffs, silently skipped searches); a guard test now keeps raw NUL bytes out of `src/`, `open-sse/` and `tests/` (#12403 — thanks @pacocartones) +- **fix(providers):** add `CLAUDE_CODE_CLIENT_VERSION` and `GITHUB_COPILOT_CLI_VERSION` env overrides so Anthropic/Copilot client-version gates can be unblocked without a rebuild ([#12417](https://github.com/diegosouzapw/OmniRoute/issues/12417)) ([#12632](https://github.com/diegosouzapw/OmniRoute/pull/12632)) — thanks @HouMinXi +- **fix(db):** back-fill `last_ping_at` and `last_pinged_reset_key` on `provider_connections` during schema reconciliation so divergent lineages that skipped `123_quota_auto_ping` still accept quota auto-ping writes ([#12470](https://github.com/diegosouzapw/OmniRoute/pull/12470) — thanks @KooshaPari) +- **fix(ci):** document MIT exceptions for `@eloqnt/{config,format-json,format-po}` (next-intl transitive; locked tarballs omit `license`) and keep the A2A lifecycle vitest off the real SQLite persistence seam ([#12581](https://github.com/diegosouzapw/OmniRoute/issues/12581)) ([#12595](https://github.com/diegosouzapw/OmniRoute/pull/12595)) +- OpenCode plugin `/v1/models` catalog fetch now waits 30s by default and attaches HTTP `statusCode` on 401/5xx so host fallback plugins can hop instead of seeing an untyped AbortError/UnknownError. ([#12607](https://github.com/diegosouzapw/OmniRoute/pull/12607)) — thanks @RaviTharuma +- **CI:** the OpenAPI security-tier gate now mirrors `isAlwaysProtectedPath()` in full — it also reads `ALWAYS_PROTECTED_API_PATTERNS`, so the pattern-gated credential routes (`/api/providers/{id}/{claude,codex}-auth/{export,apply-local}`, GHSA-5926-2w35-7h4q) no longer report as unannotated. (#12605) +- **fix(api):** GET /v1/models no longer waits forever on a hung coalesced catalog rebuild; cold-path waits are bounded (`CATALOG_BUILD_TIMEOUT_MS`, default 8s) and a last-good 200 is served when the rebuild times out ([#12627](https://github.com/diegosouzapw/OmniRoute/issues/12627)). ([#12628](https://github.com/diegosouzapw/OmniRoute/pull/12628)) — thanks @RaviTharuma +- **fix(cursor):** a non-array `tool_calls` on an assistant message no longer crashes the cursor request translator with a `TypeError`; both loops now require an array ([#12689](https://github.com/diegosouzapw/OmniRoute/issues/12689)) ([#12691](https://github.com/diegosouzapw/OmniRoute/pull/12691)) — thanks @soroush5 +- **fix(chat):** Chat Completions no longer return empty `content` after a server-owned memory or skills tool; the first provider send and account/model recovery now share one pipeline so a follow-up round-trip can fill the reply ([#12696](https://github.com/diegosouzapw/OmniRoute/issues/12696)) — thanks @HouMinXi ([#12867](https://github.com/diegosouzapw/OmniRoute/pull/12867)) +- **fix(dashboard):** batch-deleting provider keys no longer toasts failure after a successful delete when the confirm button's click event is forwarded as `onAfter` ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) — thanks @HouMinXi +- **fix(glm):** drop the extra 16th argument to `createSSETransformStreamWithLogger` that TypeScript rejected (TS2554) and that never reached the TransformStream ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) ([#12770](https://github.com/diegosouzapw/OmniRoute/pull/12770)) — thanks @HouMinXi +- **fix(dashboard):** pass `nodeMap` into Runtime `QuotaGroup` so a quota monitor in error/exhausted/alerting no longer throws `ReferenceError: nodeMap is not defined`. ([#12868](https://github.com/diegosouzapw/OmniRoute/pull/12868)) — thanks @HouMinXi +- Restricted API keys whose `allowedModels` lists a combo name no longer skip every combo member at pre-dispatch (`ALL_TARGETS_SKIPPED`). Inner-target filtering still applies when the allow-list is a provider prefix or `disableNonPublicModels` is on ([#12899](https://github.com/diegosouzapw/OmniRoute/pull/12899)). — thanks @HouMinXi +- **fix(combos):** Effort-suffixed combo members inherit the base model's `model_context_overrides` row so priority order is not inverted on large requests ([#12926](https://github.com/diegosouzapw/OmniRoute/pull/12926)) — thanks @HouMinXi +- **fix(catalog):** Picker-added `customModels` enter the dispatch-time live catalog so combo members and bare inference no longer 400 ([#12934](https://github.com/diegosouzapw/OmniRoute/pull/12934)) — thanks @HouMinXi +- **fix(docker):** re-derive the Docker build's worker-pool memory budget from the MEASURED ~4.5 GB per-process RSS (the issue owner's own VPS dmesg OOM-killer reproduction) instead of the stale 2560 MB/worker inference #11419 shipped, and lower `OMNIROUTE_BUILD_WORKERS` 3 → 2 so 1 parent + 1 page-data worker (2 processes × 4.5 GB = 9 GB) fits the 12.288 GB (75%) budget on a 16 GB GitHub Actions runner — the previous default (1 parent + 2 workers = 13.5 GB) still overcommitted the runner and kept "Publish to Docker Hub" failing with `cannot allocate memory` after #11419 merged (#7518). ([#11663](https://github.com/diegosouzapw/OmniRoute/pull/11663)) +- **fix(electron):** the packaged Windows desktop build now passes the #7592 cold-restart smoke end to end. Five defects were found and fixed on the way: (1) optional-pack staging failed on any machine whose `tar` is GNU tar — it reads the drive letter in an absolute `-f C:\...` archive path as a remote rsh host (`Cannot connect to C:`), so staging now passes a bare filename with `cwd` at the tarball directory and surfaces tar stderr on failure; (2) the Electron `files` allowlist was missing `lib/loginHeaderCapture.js`, so the packaged main process crashed at startup with `Cannot find module './lib/loginHeaderCapture'`; (3) electron-builder ≥26 injects an `!**/node_modules/**` ignore into every extraResources pattern list that no positive filter can override, which silently dropped the staged runtime `node_modules` (including the better-sqlite3 N-API prebuild) from `resources/app` — a fresh v3.8.50 desktop build would have shipped with no native SQLite driver and reproduced the #7592 sql.js fallback on every machine; an `afterPack` hook now restores the staged `node_modules` after packing; (4) the packaged-app smoke harness redirected `USERPROFILE` into its temp DATA_DIR, but Electron resolves the Roaming profile from `%USERPROFILE%\AppData\Roaming\` (USERPROFILE takes precedence over the APPDATA env var) and the path service throws instead of creating it, so `requestSingleInstanceLock()` returned false and the app exited(0) silently before `app.whenReady()` — the harness now pre-creates the derived tree, and `ensureSmokeEnvDirs` is exported and unit-tested; (5) the #7592 driver assertion parsed a `[DB] Driver: ...` line that the server's primary DB path never printed (only the unused `openDatabaseAsync()` did) — `getDbInstance()` now emits the same line on open so the guard can actually assert the native driver. Also: the smoke env-allowlist unit test hardcoded POSIX paths and could only pass on Linux/macOS; expectations are now host-agnostic, with new regression tests for the USERPROFILE-derived tree and `tarPack` under absolute Windows-style paths ([#11443](https://github.com/diegosouzapw/OmniRoute/pull/11443)) — thanks @jonlwheat2-gif +- fix(sse): stop the auto-combo candidates inspector from silently dropping model-locked/cooled-down rows (#9133) ([#11994](https://github.com/diegosouzapw/OmniRoute/pull/11994)) — thanks @hartmark +- **fix(providers):** Perplexity Web no longer turns upstream stream failures into successful assistant text; pre-content failures remain eligible for fallback, partial output ends with a structured sanitized error, and failed sessions are not persisted ([#12465](https://github.com/diegosouzapw/OmniRoute/pull/12465)) +- **fix(video-bridge):** preserve chronological video and transcript fusion, source-frame timestamps, transcript provenance, full contact-sheet intervals, and pre-abort callback isolation ([#11681](https://github.com/diegosouzapw/OmniRoute/pull/11681)) +- **Z.ai Web:** HTTP 200 streams carrying an upstream error now terminate with a structured failure instead of assistant text plus a normal stop, preserving partial output while allowing pre-content combo fallback. ([#12454](https://github.com/diegosouzapw/OmniRoute/pull/12454)) +- Absorb `Error [AbortError]: request_signal_aborted` and DOMException AbortError shapes in the process-level client-abort crash guard so routine client disconnects no longer kill the server (exit code 7). ([#12165](https://github.com/diegosouzapw/OmniRoute/pull/12165)) — thanks @alvinveroy +- **fix(sse):** Treat Adapta Web `type:error` SSE events as sanitized non-stream failures instead of empty HTTP 200 completions. ([#12459](https://github.com/diegosouzapw/OmniRoute/pull/12459)) +- **fix(autoCombo):** layer 3 (`models_dev_tier`) no longer scores retired models as premium, merge cross-provider capability rows deterministically, and inherit tier scores through variant ids ([#11508](https://github.com/diegosouzapw/OmniRoute/issues/11508)). `getModelsDevTierFitness` now vetoes ids present in `config/quality/model-lifecycle.json` with status `retired` before any other signal; `loadModelCapabilities()` aggregates per `model_id` (any-non-null-true for booleans, max for `limit_context`) instead of last-write-wins over SQLite's undefined row order; a capabilities miss retries through `resolveScoresAs()`'s base id and reports `models_dev_tier:inherited`, matching layers 1–2 (#11492). Adds `getModelsDevTierFitnessWithSource()` and an `invalidateCapabilitiesCache()` test/ops hook. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) — thanks @Meet6338-X +- fix(sse): accept Claude `content:[]` + `stop_reason: max_tokens`/`tool_use` in `detectMalformedNonStream` (match `isEmptyContentResponse`) so Claude Code `/model` probes with `max_tokens: 1` no longer become a false 502 ([#11568](https://github.com/diegosouzapw/OmniRoute/pull/11568)) — thanks @Seramicx +- **fix(security):** sanitize `request.failed` diagnostics before publishing them to live dashboard listeners and replay history, while keeping status, model, provider, latency, and internal call-log diagnostics intact. ([#12469](https://github.com/diegosouzapw/OmniRoute/pull/12469)) +- **fix(pwa):** do not intercept dashboard navigations so Chrome can fall back from a stale HTTP/3 Alt-Svc advertisement (UDP :20128 is unpublished; F5 on a long-lived tab hung until a new tab opened a fresh TCP connection). ([#12767](https://github.com/diegosouzapw/OmniRoute/pull/12767)) — thanks @HouMinXi +- **fix(executors):** handle DuckDuckGo ERR_BN_LIMIT (418) without retrying — when the upstream returns `418 ERR_BN_LIMIT` (rate-limit/ban), the executor now returns the error immediately instead of burning another VQD acquisition that would only count against the IP limit. The retry logic for `418 ERR_CHALLENGE` (unsolved challenge) remains unchanged. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) ([#12110](https://github.com/diegosouzapw/OmniRoute/pull/12110)) — thanks @oyi77 / @Meet6338-X +- Fixed DuckDuckGo streaming responses losing JSON lines and UTF-8 characters split across network chunks. ([#11528](https://github.com/diegosouzapw/OmniRoute/pull/11528)) — thanks @pacocartones +- Harden generated Cloudflare, Vercel, and Deno relays by stripping hop-by-hop, framing, and proxy authentication headers before forwarding requests upstream. ([#11533](https://github.com/diegosouzapw/OmniRoute/pull/11533)) — thanks @pacocartones +- **feat(grok-cli):** Provider Limits shows grok-cli banked reset credits from `GetRemainingResets` (including a real zero; a failed RPC omits the row) and the existing View credits button now calls `ConsumerUiSvc/RedeemReset` for grok-cli. Live tokens use nested fields 10/20/30 (id + Timestamp), not compact 1/2/3. ([#12805](https://github.com/diegosouzapw/OmniRoute/pull/12805)) — thanks @HouMinXi +- **fix(memory):** Embedding Model Quick select, Embedding Source remote dropdown, and Rerank selector now list every configured provider with embedding/rerank support instead of only chat-catalog text matches plus OpenRouter live discovery; a generic OpenAI-compatible `/embeddings` + Cohere-compatible `/rerank` runtime fallback resolves any configured chat provider's embedding/rerank endpoint, so unlisted providers no longer fail with "Unknown embedding provider"; both memory selectors gained a free-text model override ([#11390](https://github.com/diegosouzapw/OmniRoute/pull/11390)) — thanks @rqzbeh +- Harden SQLite upgrades around the historical migration-074 version collision: missing discovery and inspector tables are replayed atomically, pre-existing databases (including setup-created skeletons) receive reusable content-addressed safety snapshots, and Node test/eval probes without `DATA_DIR` are isolated from the operator database. ([#12435](https://github.com/diegosouzapw/OmniRoute/pull/12435)) +- **fix(dashboard):** Moonshot/Kimi Open Platform voucher and cash leftover percentages follow the bucket balance, so an empty wallet no longer paints those rows as 100% while Available is 0% ([#12733](https://github.com/diegosouzapw/OmniRoute/pull/12733)) — thanks @HouMinXi +- **fix(combo):** return non-retryable HTTP 400 when all candidates for a pinned native Codex turn are unavailable due to model-scoped lockout, terminating the turn cleanly while preserving turn continuity and enabling standard Combo routing on subsequent turns ([#12240](https://github.com/diegosouzapw/OmniRoute/pull/12240)) — thanks @mdigitalbh81 +- Fixed auto-router strategies preserving the selected connection through ranking and dispatch when multiple connections share the same provider and model. ([#11530](https://github.com/diegosouzapw/OmniRoute/pull/11530)) — thanks @pacocartones +- Preserve Chat Completions JSON-object response formats when translating requests to the Responses API. ([#11529](https://github.com/diegosouzapw/OmniRoute/pull/11529)) — thanks @pacocartones +- Fix idempotency fingerprints so Responses API requests include `input` and other semantic generation fields using stable serialization, while excluding credentials and request metadata. ([#11532](https://github.com/diegosouzapw/OmniRoute/pull/11532)) — thanks @pacocartones +- **fix(api):** Generated API CLI commands now enforce required OpenAPI request bodies; Combo test commands forward the required `comboName` body, while API keys created by older writers after migration 149 preserve legacy allow-all Combo access without widening explicit empty allowlists — thanks @marcelokarval ([#12070](https://github.com/diegosouzapw/OmniRoute/pull/12070)) +- **fix(grok-web):** treat upstream streaming failures as failures instead of successful assistant text: error-only streams now fail readiness with HTTP 502, while failures after legitimate content preserve that partial output and terminate through the sanitized stream failure path without a normal `stop` completion. ([#12458](https://github.com/diegosouzapw/OmniRoute/pull/12458)) +- HuggingChat now turns HTTP 200 JSONL generation failures into a sanitized 502 before content, or a fixed public stream failure after partial output, so fallback and request persistence no longer record a false successful stop. ([#12456](https://github.com/diegosouzapw/OmniRoute/pull/12456)) +- **fix(providers):** keep 1min.ai HTTP 200 stream errors out of assistant content, preserve partial output, and expose sanitized terminal errors so pre-content failures can fall back. ([#12466](https://github.com/diegosouzapw/OmniRoute/pull/12466)) +- **fix(sessions):** preserve legacy Sessions data when the additive exclusive-session projection is unavailable, returning an empty projection and warning only once per contiguous outage instead of failing the endpoint; the Sessions badge intentionally reflects the merged legacy and exclusive row count introduced by [#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389) ([#11469](https://github.com/diegosouzapw/OmniRoute/pull/11469)) — thanks @KaspaPulse +- Render Nimble Search with the generic provider icon and serve Opper's proven logo locally. ([#12415](https://github.com/diegosouzapw/OmniRoute/pull/12415)) +- **fix(quality):** `config/quality/provider-assets-provenance.jsonl` was stale against the physical `public/providers/` tree (still listing 225 assets and 5 duplicate-content groups after several provider-retirement PRs removed 85 icon files), which would have made `check:provider-asset-provenance` fail on the next full run. Reconciled the manifest to the real 142-file snapshot (142 asset records, 1 duplicate-content group, 2 newly-registered files marked `unresolved` pending provenance research), repinned `auditedCommit`, and updated the regression test's hardcoded counts to match. ([#11876](https://github.com/diegosouzapw/OmniRoute/pull/11876)) +- fix(ui): wrap quota card footer action buttons to prevent clipping Refresh button on narrow cards — when extra action buttons such as `View credits` are present on Codex cards, allow the footer row to flex-wrap with `shrink-0` buttons so `Refresh now` remains visible and clickable across all responsive card widths ([#11464](https://github.com/diegosouzapw/OmniRoute/pull/11464)) — thanks @adevwithpurpose +- Keep Antigravity Gemini usable when the same connection's Claude weekly quota is empty; generic quota cache stays per-connection for every other provider. ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) — thanks @HouMinXi +- Fixed Tailscale tunnel setup to reject ports outside the valid 1-65535 range and safely install Windows MSI packages from paths containing apostrophes. ([#11534](https://github.com/diegosouzapw/OmniRoute/pull/11534)) — thanks @pacocartones +- Classify Cloudflared and Tailscale process-management routes as local-only and spawn-capable while preserving remote access to read-only tunnel status endpoints. ([#11531](https://github.com/diegosouzapw/OmniRoute/pull/11531)) — thanks @pacocartones +- Electron release workflow: the `publish-npm` job now grants `actions: read` to the reusable `npm-publish.yml` it calls (its `publish` job requests it), which is what made GitHub refuse the whole v3.8.50 run at startup and ship the release with zero desktop assets; a `workflow_dispatch` now builds the requested tag instead of the dispatching branch and can skip the npm leg (`publish_npm=false`) when only re-attaching assets ([#11974](https://github.com/diegosouzapw/OmniRoute/pull/11974)) +- **fix(dashboard):** The Combos page usage guide now reads its dismissal through `useSyncExternalStore` instead of correcting SSR state inside an effect, removing an extra commit of the page tree on every load (and the `react-hooks/set-state-in-effect` error it raised). ([#12671](https://github.com/diegosouzapw/OmniRoute/pull/12671)) +- Electron release: `electron/package-lock.json` regained the optional `electron-builder-squirrel-windows` subtree (13 entries) that `npm ci` had been refusing as out of sync, `electron-release.yml` gained a `build_ref` dispatch input and stops regenerating release notes on a re-attach dispatch, and the npm publish workflow attaches the SBOM to the GitHub Release on dispatch publishes too — so the v3.8.51 tag ships every desktop asset and the SBOM like v3.8.49 did ([#12022](https://github.com/diegosouzapw/OmniRoute/pull/12022)) +- Drained the reds every PR against `release/v3.8.51` was born with: documented the three service-worker build-id variables, registered the six retirement/tunnel tests with the mutation gate, approved `eslint-plugin-react-hooks` in the dependency allowlist, added the six `combo.sort.*` strings to `vi` and `pt-BR`, pointed the ChatGPT Web doc at the real migration-168 test, worded the g4f hint around the member key, and realigned four tests to the retired-provider catalog and the legacy-schema fixtures the retirement migrations touch. ([#11940](https://github.com/diegosouzapw/OmniRoute/pull/11940)) +- **fix(providers):** Claude, Grok, LMArena, Notion, and Perplexity web-cookie transports now use pooled `wreq-js` 3.2 instead of the native sidecar, with all nine supported bindings pinned and audited, and the applicable platform binding plus native-license evidence included in each release artifact ([#12429](https://github.com/diegosouzapw/OmniRoute/pull/12429), supersedes [#11753](https://github.com/diegosouzapw/OmniRoute/pull/11753)). +- **fix(providers):** Zed Hosted streaming failures now trigger fallback before content and end partial streams with a sanitized structured error instead of fake assistant text and a normal-success stop. ([#12455](https://github.com/diegosouzapw/OmniRoute/pull/12455)) +- **fix(test):** capture hook result after render (direct commit `b85d0abb0b`) +- **fix(sync):** repair the two regressions the v3.8.50 sync-back left on release/v3.8.51 (direct commit `5ade9e0851`) +- **revert(models):** drop the memo reset hook ported from #10055 (direct commit `282de841ae`) +- **fix(typecheck):** guard the optional connectionId before the reactive model sync (direct commit `f2825bad75`) +- **fix(quota-share):** release the winner's reserved in-flight slot (#11371) ([#11408](https://github.com/diegosouzapw/OmniRoute/pull/11408)) — thanks @oyi77 +- **fix(providers):** stop silently dropping reasoning effort on opencode families (#10788) ([#11409](https://github.com/diegosouzapw/OmniRoute/pull/11409)) — thanks @oyi77 +- **fix(combo):** enable genuine quota-aware routing for generic providers (antigravity, claude, etc.) ([#11411](https://github.com/diegosouzapw/OmniRoute/pull/11411)) — thanks @HouMinXi +- **fix(oauth):** allow expired token retry before deactivation and activate expired retry loop (#P0) ([#11414](https://github.com/diegosouzapw/OmniRoute/pull/11414)) — thanks @HouMinXi +- **perf(executors):** lazy-load the executor registry — defer class imports + construction to first use (#11220) ([#11421](https://github.com/diegosouzapw/OmniRoute/pull/11421)) — thanks @oyi77 +- **fix:** make release/v3.8.50 CI gates green (run 32786966560 root causes) ([#11450](https://github.com/diegosouzapw/OmniRoute/pull/11450)) — thanks @jonlwheat2-gif +- **fix(sse):** exact-domain cookie match and origin-equality URL assertions ([#11452](https://github.com/diegosouzapw/OmniRoute/pull/11452)) — thanks @hartmark +- **fix(providers):** send Responses API payload during OpenAI-compatible validation ([#11454](https://github.com/diegosouzapw/OmniRoute/pull/11454)) — thanks @tuandinh0801 +- **fix(command-code):** fallback to /alpha/generate for Go plan without Provider API access ([#11455](https://github.com/diegosouzapw/OmniRoute/pull/11455)) — thanks @Chewji9875 +- **fix(responses-continuation):** fail closed on a log-truncated stored input/output array ([#11473](https://github.com/diegosouzapw/OmniRoute/pull/11473)) — thanks @hartmark +- **fix(cli):** write jcode settings as [providers.omniroute] in config.toml ([#11484](https://github.com/diegosouzapw/OmniRoute/pull/11484)) — thanks @hongnoul +- **fix(logging):** raise chat-log array truncation cap to 1000 and unify duplicate implementations ([#11499](https://github.com/diegosouzapw/OmniRoute/pull/11499)) — thanks @hartmark +- **fix:** restore live websocket URL helpers ([#11509](https://github.com/diegosouzapw/OmniRoute/pull/11509)) — thanks @PauloFH +- **perf(core):** eliminate async waterfalls across home, cache, and db operations (#11396) ([#11512](https://github.com/diegosouzapw/OmniRoute/pull/11512)) — thanks @HouMinXi +- **fix(kiro):** add runtime.us-east-1.kiro.dev as first-attempt endpoint ([#11517](https://github.com/diegosouzapw/OmniRoute/pull/11517)) — thanks @rifqiawl +- **fix(providers):** remove duplicate 128-tool truncation in OpencodeExecutor (#11444) ([#11518](https://github.com/diegosouzapw/OmniRoute/pull/11518)) — thanks @HouMinXi +- **fix(providers):** poll onboardUser LRO before classifying BYOP ([#11519](https://github.com/diegosouzapw/OmniRoute/pull/11519)) — thanks @rifqiawl +- **fix(translator):** preserve exact tool name casing for Claude tool_use payloads (#11487) ([#11520](https://github.com/diegosouzapw/OmniRoute/pull/11520)) — thanks @HouMinXi +- **fix(kimi-web):** align health probe with executor domain ([#11521](https://github.com/diegosouzapw/OmniRoute/pull/11521)) — thanks @pacocartones +- **fix(cli):** preserve existing admin password during setup ([#11522](https://github.com/diegosouzapw/OmniRoute/pull/11522)) — thanks @pacocartones +- **fix(mitm):** keep Windows DNS elevation off the POSIX sudo path (#11430) ([#11542](https://github.com/diegosouzapw/OmniRoute/pull/11542)) — thanks @official-burak +- **fix(sse):** scale chat admission by ingest byte budget instead of a fixed request count ([#11548](https://github.com/diegosouzapw/OmniRoute/pull/11548)) — thanks @TheDemonTuan +- **fix:** accept Gemini Web cookie exports ([#11554](https://github.com/diegosouzapw/OmniRoute/pull/11554)) — thanks @rafacpti23 +- **fix(i18n):** complete Brazilian Portuguese translations ([#11558](https://github.com/diegosouzapw/OmniRoute/pull/11558)) — thanks @rafacpti23 +- **fix(sse):** explicit types for openai-responses pureHelpers — clears last failing core typecheck gate ([#11567](https://github.com/diegosouzapw/OmniRoute/pull/11567)) — thanks @jonlwheat2-gif +- **fix(opencode):** hydrate Proxy Pool references for no-auth connections ([#11584](https://github.com/diegosouzapw/OmniRoute/pull/11584)) — thanks @AStupidBear +- **fix(ci):** resolve inherited release/v3.8.51 base-red CI failures ([#11608](https://github.com/diegosouzapw/OmniRoute/pull/11608)) — thanks @jonlwheat2-gif +- **fix(db):** fallback load STORAGE_ENCRYPTION_KEY from env files ([#11614](https://github.com/diegosouzapw/OmniRoute/pull/11614)) — thanks @benzntech +- **fix(providers):** mark Brave Search as no longer having a free tier ([#11621](https://github.com/diegosouzapw/OmniRoute/pull/11621)) — thanks @turbolego +- **fix:** pass tool result content through as string in OpenAI/Claude -> Gemini translation ([#11624](https://github.com/diegosouzapw/OmniRoute/pull/11624)) — thanks @Hsia97 +- **fix(sse):** scope credential health to exact key ([#11628](https://github.com/diegosouzapw/OmniRoute/pull/11628)) — thanks @KaspaPulse +- **fix(install):** guard prepare script against missing husky ([#11634](https://github.com/diegosouzapw/OmniRoute/pull/11634)) — thanks @Prajeeth-12 +- **fix(security):** redact NVIDIA_API_KEY at the log sink in the ad-hoc diag script ([#11639](https://github.com/diegosouzapw/OmniRoute/pull/11639)) +- **fix(types):** clear the two type regressions red on release/v3.8.51 ([#11640](https://github.com/diegosouzapw/OmniRoute/pull/11640)) — thanks @jonlwheat2-gif +- **fix(build):** resolve workspace: protocol dependencies to concrete semver versions during prepublish (#11540) ([#11641](https://github.com/diegosouzapw/OmniRoute/pull/11641)) — thanks @HouMinXi +- **fix(search):** properly resolve configured search connection in /v1/responses pipeline (#11524) ([#11642](https://github.com/diegosouzapw/OmniRoute/pull/11642)) — thanks @HouMinXi +- **fix(authz):** trust cf-connecting-ip for via-proxy detection when peer is Cloudflare edge (#11514) ([#11643](https://github.com/diegosouzapw/OmniRoute/pull/11643)) — thanks @HouMinXi +- **fix(security):** compare the OIDC state cookie in constant time ([#11646](https://github.com/diegosouzapw/OmniRoute/pull/11646)) +- **fix(security):** namespace the dedup hash by the calling API key ([#11649](https://github.com/diegosouzapw/OmniRoute/pull/11649)) +- **fix(models):** gate the Codex-native catalog loop on prefix mode (#11632) ([#11662](https://github.com/diegosouzapw/OmniRoute/pull/11662)) — thanks @xiaoyaner0201 +- **fix(dashboard):** project Codex routing quota in account pools ([#11666](https://github.com/diegosouzapw/OmniRoute/pull/11666)) — thanks @KaspaPulse +- **fix(ddgw):** normaliza system no upstream da release v3.8.51 ([#11692](https://github.com/diegosouzapw/OmniRoute/pull/11692)) — thanks @RhianB14 +- **fix(memory):** honor category filter in GET /api/memory ([#11699](https://github.com/diegosouzapw/OmniRoute/pull/11699)) — thanks @Sabeekhann +- **fix(ci):** detect stale workspace lockfile entries ([#11714](https://github.com/diegosouzapw/OmniRoute/pull/11714)) — thanks @pacocartones +- **fix(ci):** stamp dist/BUILD_SHA before the npm publish provenance gate ([#11721](https://github.com/diegosouzapw/OmniRoute/pull/11721)) +- **fix(docker):** decouple the best-effort Bun image from the release manifest ([#11724](https://github.com/diegosouzapw/OmniRoute/pull/11724)) +- **perf(compression):** avoid cloning memo misses twice ([#11727](https://github.com/diegosouzapw/OmniRoute/pull/11727)) — thanks @RaviTharuma +- **fix(docker):** let the bun digest artifact be absent, not fatal ([#11740](https://github.com/diegosouzapw/OmniRoute/pull/11740)) +- **fix(shared):** block cloud-metadata hosts under default remote-image guard ([#11755](https://github.com/diegosouzapw/OmniRoute/pull/11755)) — thanks @santosraju99-hub +- **fix(api):** preserve caller-provided X-Correlation-Id on chat completions ([#11760](https://github.com/diegosouzapw/OmniRoute/pull/11760)) — thanks @aniruddhaadak80 +- **fix(resilience):** lock permanently retired models instead of short backoff (Gemini ban prevention) ([#11762](https://github.com/diegosouzapw/OmniRoute/pull/11762)) — thanks @turbolego +- **fix:** mark Vercel AI Gateway as passthroughModels ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **fix(resilience):** stop hammering permanently-moved endpoints and billing-suspended accounts ([#11774](https://github.com/diegosouzapw/OmniRoute/pull/11774)) — thanks @turbolego +- **fix(lease):** remove global static reservation and gate routing on live active lease occupancy ([#11775](https://github.com/diegosouzapw/OmniRoute/pull/11775)) — thanks @TheDemonTuan +- **fix(ci):** size the install-upgrade gate to a measured run, and log the pack cost ([#11776](https://github.com/diegosouzapw/OmniRoute/pull/11776)) +- **fix(pwa):** stop serving the stale shell after deploys ([#11779](https://github.com/diegosouzapw/OmniRoute/pull/11779)) — thanks @HouMinXi +- **fix(combo):** lock GitHub models rejected as "not supported" for future requests ([#11781](https://github.com/diegosouzapw/OmniRoute/pull/11781)) — thanks @turbolego +- **fix(cli):** probe both IPv4 and IPv6 loopback for server readiness (#11766) ([#11794](https://github.com/diegosouzapw/OmniRoute/pull/11794)) — thanks @vermasomesh835 +- **fix(sse):** preserve CLIProxyAPI wrappers per connection ([#11797](https://github.com/diegosouzapw/OmniRoute/pull/11797)) — thanks @RaviTharuma +- **fix(providers):** honor model auto-fetch opt-in on create ([#11798](https://github.com/diegosouzapw/OmniRoute/pull/11798)) — thanks @RaviTharuma +- **fix(dashboard):** make model auto-fetch order independent ([#11805](https://github.com/diegosouzapw/OmniRoute/pull/11805)) — thanks @RaviTharuma +- **fix(docker):** make GHA cache exports best effort ([#11806](https://github.com/diegosouzapw/OmniRoute/pull/11806)) — thanks @RaviTharuma +- **fix(kiro):** do not permanently ban on 'User is not authorized to make this call' ([#11809](https://github.com/diegosouzapw/OmniRoute/pull/11809)) — thanks @Deftera186 +- **fix(sse):** keep cache-write tokens in OpenAI-shaped usage ([#11814](https://github.com/diegosouzapw/OmniRoute/pull/11814)) — thanks @TheDemonTuan +- **fix(docs):** resolve relative markdown and wiki links across Fumadocs and GitHub wiki ([#11834](https://github.com/diegosouzapw/OmniRoute/pull/11834)) — thanks @AndrianBalanescu +- **fix(chat):** preserve unstripped model string for passthrough provider routing ([#11840](https://github.com/diegosouzapw/OmniRoute/pull/11840)) — thanks @raheemuddin786 +- **fix(mcp):** avoid async initialization inside sync esm wrappers (#11569) ([#11849](https://github.com/diegosouzapw/OmniRoute/pull/11849)) — thanks @HouMinXi +- **fix(sse):** preserve coherent markdown token boundaries across stream translation chunks (#11606) ([#11851](https://github.com/diegosouzapw/OmniRoute/pull/11851)) — thanks @HouMinXi +- **fix(ci):** run the install-upgrade gate on disk, not on the /tmp tmpfs ([#11855](https://github.com/diegosouzapw/OmniRoute/pull/11855)) +- **fix(skills):** expand shorthand property types in injected tool schemas ([#11857](https://github.com/diegosouzapw/OmniRoute/pull/11857)) — thanks @alvinveroy +- **fix(ci):** publish npm from a hosted runner so provenance is accepted ([#11877](https://github.com/diegosouzapw/OmniRoute/pull/11877)) +- **fix(lint):** pin react hooks plugin to 7.0.1 ([#11886](https://github.com/diegosouzapw/OmniRoute/pull/11886)) +- **fix(ci):** surface and clear release-green ESLint failure ([#11890](https://github.com/diegosouzapw/OmniRoute/pull/11890)) — thanks @MumuTW +- **fix(guardrails):** prevent duplicate prompt-injection-guard log output ([#11936](https://github.com/diegosouzapw/OmniRoute/pull/11936)) — thanks @patrykkopycinski +- **fix(auth):** downgrade expected transient states from warn to debug ([#11937](https://github.com/diegosouzapw/OmniRoute/pull/11937)) — thanks @patrykkopycinski +- **fix(codex):** fail over image generation for imported free plans ([#11948](https://github.com/diegosouzapw/OmniRoute/pull/11948)) — thanks @Bl0ck154 +- **fix(codex):** normalize non-stream responses ([#11951](https://github.com/diegosouzapw/OmniRoute/pull/11951)) — thanks @Bl0ck154 +- **fix(images):** forward Antigravity image size ([#11952](https://github.com/diegosouzapw/OmniRoute/pull/11952)) — thanks @Bl0ck154 +- **fix(db):** invalidate connection cache after upsert ([#11953](https://github.com/diegosouzapw/OmniRoute/pull/11953)) — thanks @Bl0ck154 +- **fix(codex):** restore imported account state ([#11954](https://github.com/diegosouzapw/OmniRoute/pull/11954)) — thanks @Bl0ck154 +- **fix(sse):** give extended-thinking targets the reasoning readiness budget ([#11959](https://github.com/diegosouzapw/OmniRoute/pull/11959)) — thanks @abhisheksharma2411 +- **fix(dashboard):** use opaque background and readable text color on cost chart tooltips ([#11960](https://github.com/diegosouzapw/OmniRoute/pull/11960)) — thanks @ZaimMarzuki +- **fix(antigravity):** send complete loadCodeAssist metadata (ideType/platform/pluginType as numeric enums) ([#11969](https://github.com/diegosouzapw/OmniRoute/pull/11969)) — thanks @rifqiawl +- **fix(core):** resolve DB init race condition and reasoning translation ([#12003](https://github.com/diegosouzapw/OmniRoute/pull/12003)) — thanks @raheemuddin786 +- **fix(admission):** exclude reclaimable page cache from the cgroup pressure ratio ([#12017](https://github.com/diegosouzapw/OmniRoute/pull/12017)) — thanks @HouMinXi +- **fix(build):** prune non-production trees in NFT trace excludes and tsconfig ([#12028](https://github.com/diegosouzapw/OmniRoute/pull/12028)) — thanks @Chewji9875 +- **fix(migrations):** add renamed migration compatibility for 056/073/077/101 ([#12036](https://github.com/diegosouzapw/OmniRoute/pull/12036)) — thanks @oyi77 +- **fix(sse):** default crash-guard logger to console.warn, not console ([#12042](https://github.com/diegosouzapw/OmniRoute/pull/12042)) — thanks @HouMinXi +- **fix(catalog):** derive vision/modalities for built-in auto combos from effective target pool ([#12046](https://github.com/diegosouzapw/OmniRoute/pull/12046)) — thanks @Prajeeth-12 +- **fix(release):** the packaged-app smoke verifies the database opened, not a driver line the primary path never prints (twin of #12032) ([#12047](https://github.com/diegosouzapw/OmniRoute/pull/12047)) +- **fix(barrel):** migrate src/app/ off the @/lib/localDb barrel import (#11795 Phase 2) ([#12051](https://github.com/diegosouzapw/OmniRoute/pull/12051)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate src/lib/ off the localDb barrel to direct db imports (#59) ([#12052](https://github.com/diegosouzapw/OmniRoute/pull/12052)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate open-sse, src/shared, src/sse, src/models, src/domain off the @/lib/localDb barrel import (#11795 Phase 4) ([#12053](https://github.com/diegosouzapw/OmniRoute/pull/12053)) — thanks @jonlwheat2-gif +- **fix(barrel):** delete the @/lib/localDb barrel — every consumer migrated (#11795 Phase 5) ([#12055](https://github.com/diegosouzapw/OmniRoute/pull/12055)) — thanks @jonlwheat2-gif +- **fix(radar):** restore D12 public boundary ([#12057](https://github.com/diegosouzapw/OmniRoute/pull/12057)) +- **fix(dev):** bound webpack and Tailwind scans ([#12075](https://github.com/diegosouzapw/OmniRoute/pull/12075)) — thanks @backryun +- **fix(dev):** isolate root layout settings reads ([#12076](https://github.com/diegosouzapw/OmniRoute/pull/12076)) — thanks @backryun +- **fix(dev):** reduce instrumentation executor fan-out (phase 3) ([#12078](https://github.com/diegosouzapw/OmniRoute/pull/12078)) — thanks @backryun +- **fix(dev):** make logging resources HMR-singleton ([#12079](https://github.com/diegosouzapw/OmniRoute/pull/12079)) — thanks @backryun +- **fix(sse):** keep unavailable forced connections scoped ([#12080](https://github.com/diegosouzapw/OmniRoute/pull/12080)) — thanks @keeltrace +- **fix(dev):** isolate batch dispatch from instrumentation ([#12081](https://github.com/diegosouzapw/OmniRoute/pull/12081)) — thanks @backryun +- **fix(ci):** reconcile release test contract drift ([#12082](https://github.com/diegosouzapw/OmniRoute/pull/12082)) — thanks @backryun +- **fix(ci):** accept CVE-2025-68121 in the prebuilt tls-client .so, auto-close base-red issues, guard Scorecard on the default branch ([#12085](https://github.com/diegosouzapw/OmniRoute/pull/12085)) +- **fix(db):** drop three consumer-less 1proxy exports — dead-code base-red on release/v3.8.51 after the barrel deletion (#12055) ([#12087](https://github.com/diegosouzapw/OmniRoute/pull/12087)) +- **fix(api):** clear the six API-route TypeScript regressions the new gate landed red on ([#12094](https://github.com/diegosouzapw/OmniRoute/pull/12094)) +- **fix(release):** never let the tag-push Create Release append auto notes to the curated body ([#12096](https://github.com/diegosouzapw/OmniRoute/pull/12096)) +- **fix(sse):** honor CLIProxyAPI environment API key ([#12099](https://github.com/diegosouzapw/OmniRoute/pull/12099)) — thanks @RaviTharuma +- **fix(oauth):** bind Google refresh to the client that issued the token ([#12106](https://github.com/diegosouzapw/OmniRoute/pull/12106)) — thanks @HouMinXi +- **fix(dashboard):** make RequestLoggerDetail loadable outside Next — CSS via globals.css + CJS/ESM interop (#11703 base-reds) ([#12114](https://github.com/diegosouzapw/OmniRoute/pull/12114)) +- **fix(leases):** project status lease row to lease columns so joined connection PII never escapes ([#12115](https://github.com/diegosouzapw/OmniRoute/pull/12115)) — thanks @geek007git +- **fix(plugins):** do not kill the plugin process when a fire-and-forget hook times out ([#12116](https://github.com/diegosouzapw/OmniRoute/pull/12116)) — thanks @geek007git +- **fix(guardrails):** restore injection-guard logging on middleware-only routes ([#12117](https://github.com/diegosouzapw/OmniRoute/pull/12117)) — thanks @geek007git +- **fix(plugins):** refresh stored manifest from disk on activate so new hook fields reach existing installs ([#12120](https://github.com/diegosouzapw/OmniRoute/pull/12120)) — thanks @geek007git +- **fix(codex):** preserve existing provider state when bulk-import upserts a matching connection ([#12122](https://github.com/diegosouzapw/OmniRoute/pull/12122)) — thanks @geek007git +- **fix(ci):** clear the two base-reds the 2026-08-30 merge batch left on release/v3.8.51 (round 3) ([#12123](https://github.com/diegosouzapw/OmniRoute/pull/12123)) +- **fix(sse):** strip type:'custom' from Claude tools on agentrouter dispatch ([#12126](https://github.com/diegosouzapw/OmniRoute/pull/12126)) — thanks @wahidsadik371-coder +- **fix(sse):** stop advertising video providers the dispatcher cannot run ([#12131](https://github.com/diegosouzapw/OmniRoute/pull/12131)) — thanks @quiterunner-commits +- **fix(search):** treat HTTP 432 and plan limit errors as transient cooldown ([#12139](https://github.com/diegosouzapw/OmniRoute/pull/12139)) — thanks @HouMinXi +- **fix(sse):** refuse an AI Horde queue that cannot fit the request budget ([#12143](https://github.com/diegosouzapw/OmniRoute/pull/12143)) — thanks @quiterunner-commits +- **fix(ci):** clear the base-reds the afternoon merge batch left on release/v3.8.51 (round 5: provider count 352, TS2554/TS2677) ([#12144](https://github.com/diegosouzapw/OmniRoute/pull/12144)) +- **fix(translator):** strip neutral tool_choice when tools absent in Responses-to-Chat (#12141) ([#12166](https://github.com/diegosouzapw/OmniRoute/pull/12166)) — thanks @HouMinXi +- **fix(guardrails):** pass providerId to getResolvedModelCapabilities in checkComboVision (#12112) ([#12169](https://github.com/diegosouzapw/OmniRoute/pull/12169)) — thanks @HouMinXi +- **fix(translator):** strip plaintext reasoning content for opaque responses backends (#12128) ([#12171](https://github.com/diegosouzapw/OmniRoute/pull/12171)) — thanks @HouMinXi +- **perf(stream):** compile hot-path regexes once, bound token caches, fix quadratic buffering ([#12179](https://github.com/diegosouzapw/OmniRoute/pull/12179)) — thanks @opensource-elearning +- **fix:** resolve compression worker file using runtime anchors instead of… ([#12183](https://github.com/diegosouzapw/OmniRoute/pull/12183)) — thanks @NightStalker-87 +- **fix:** prevent Claude Code session kills via liveness-aware readiness + auto model echo ([#12189](https://github.com/diegosouzapw/OmniRoute/pull/12189)) — thanks @opensource-elearning +- **fix(translator):** preserve falsy primitive values in Gemini and Antigravity function response results ([#12191](https://github.com/diegosouzapw/OmniRoute/pull/12191)) — thanks @Siva010 +- **fix(oauth):** mark empty Antigravity projectId as degraded and clear stale errors (#11284) ([#12205](https://github.com/diegosouzapw/OmniRoute/pull/12205)) — thanks @HouMinXi +- **fix(providers):** gate the Codex auto-ping usage read on the shared quota throttle ([#12209](https://github.com/diegosouzapw/OmniRoute/pull/12209)) — thanks @abhisheksharma2411 +- **fix(combos):** prioritize SQLite row id over inner JSON id and notify delete errors ([#12213](https://github.com/diegosouzapw/OmniRoute/pull/12213)) — thanks @HouMinXi +- **fix(providers):** mark featherless.ai as no longer having a free tier (`hasFree: false`) ([#12216](https://github.com/diegosouzapw/OmniRoute/pull/12216)) — thanks @turbolego +- **fix(deepseek-web):** stop Turbopack dev panic in the PoW worker path resolver ([#12221](https://github.com/diegosouzapw/OmniRoute/pull/12221)) — thanks @hartmark +- **fix(chat-admission):** clarify local 503 source ([#12223](https://github.com/diegosouzapw/OmniRoute/pull/12223)) — thanks @keeltrace +- **fix(dev):** silence webpack runtime module warnings ([#12228](https://github.com/diegosouzapw/OmniRoute/pull/12228)) — thanks @backryun +- **fix(db):** use module.require for CommonJS runtime driver loading ([#12230](https://github.com/diegosouzapw/OmniRoute/pull/12230)) — thanks @raheemuddin786 +- **fix(memory):** honest probe-driven FTS5 keyword status + memory_id rowid sync ([#12231](https://github.com/diegosouzapw/OmniRoute/pull/12231)) — thanks @raheemuddin786 +- **fix(system):** propagate abort signal to stream reader in HTTP version checks ([#12232](https://github.com/diegosouzapw/OmniRoute/pull/12232)) — thanks @raheemuddin786 +- **fix(resilience):** isolate local host execution errors from provider circuit breakers ([#12233](https://github.com/diegosouzapw/OmniRoute/pull/12233)) — thanks @raheemuddin786 +- **fix(sse):** sort injected tools deterministically for prompt caching ([#12234](https://github.com/diegosouzapw/OmniRoute/pull/12234)) — thanks @raheemuddin786 +- **fix(combo):** always clear the loop-safety timer, not just on the happy path (#11804) ([#12245](https://github.com/diegosouzapw/OmniRoute/pull/12245)) +- **fix(quality):** base-red pair — stryker tap registration + turn-pin suites aligned to the window gate ([#12255](https://github.com/diegosouzapw/OmniRoute/pull/12255)) — thanks @backryun +- **fix(dev):** qualify Turbopack runtime boundaries ([#12258](https://github.com/diegosouzapw/OmniRoute/pull/12258)) — thanks @backryun +- **fix(combo):** fall back to full pool when collapsed sole survivor is context-too-small ([#12278](https://github.com/diegosouzapw/OmniRoute/pull/12278)) — thanks @opensource-elearning +- **fix(combo):** bound the pre-dispatch unavailable skip so a stale label cannot dark a pool (#12168) ([#12285](https://github.com/diegosouzapw/OmniRoute/pull/12285)) +- **fix(security):** strip Qwen/Alibaba console-session cookies from provider API responses ([#12287](https://github.com/diegosouzapw/OmniRoute/pull/12287)) +- **fix(usage):** console-aware Token Plan guidance + subscription hint on bailian 401 ([#12288](https://github.com/diegosouzapw/OmniRoute/pull/12288)) +- **fix(resourcePressure):** log numeric detail on every rejection, recover faster ([#12293](https://github.com/diegosouzapw/OmniRoute/pull/12293)) — thanks @hartmark +- **fix(release):** drain the 2026-09-01 base-red window — passthrough usage regression + radarPage i18n keys ([#12327](https://github.com/diegosouzapw/OmniRoute/pull/12327)) +- **fix(ci):** openapi-security-tiers checker must honor routeGuard patterns + imported prefixes ([#12350](https://github.com/diegosouzapw/OmniRoute/pull/12350)) — thanks @ggiak +- **perf(ci):** cache node_modules in the npm-ci-retry composite (#8084 D3) ([#12408](https://github.com/diegosouzapw/OmniRoute/pull/12408)) +- **revert(ui):** point CTAs back at their real destinations ([#12410](https://github.com/diegosouzapw/OmniRoute/pull/12410)) +- **fix(release):** drain the 2026-09-02 base-red — rerank-providers import + api-typecheck baseline ratchet ([#12414](https://github.com/diegosouzapw/OmniRoute/pull/12414)) +- **fix(types):** make system prompt injection noImplicitAny-safe ([#12416](https://github.com/diegosouzapw/OmniRoute/pull/12416)) +- **fix(quality):** validate typecheck baseline schema ([#12419](https://github.com/diegosouzapw/OmniRoute/pull/12419)) +- **fix(memory):** resolve rerank provider node cache import ([#12421](https://github.com/diegosouzapw/OmniRoute/pull/12421)) +- **fix(ci):** repair release-root regressions — pack dedup, web-session syntax, uc-image ids ([#12423](https://github.com/diegosouzapw/OmniRoute/pull/12423)) — thanks @backryun +- **fix(providers):** separate MaxAI and UC credential contracts ([#12431](https://github.com/diegosouzapw/OmniRoute/pull/12431)) +- **fix(cli):** preserve Claude settings on config set ([#12432](https://github.com/diegosouzapw/OmniRoute/pull/12432)) — thanks @KrzysiekSko +- **fix(providers):** repair the maxai credential block truncated by merge auto-resolve ([#12433](https://github.com/diegosouzapw/OmniRoute/pull/12433)) +- **fix(quality):** record the 2026-09-02 merged growth in the file-size baseline ([#12434](https://github.com/diegosouzapw/OmniRoute/pull/12434)) +- **fix(uc):** route UC error strings through sanitizeErrorMessage; allowlist the retired codex id ([#12437](https://github.com/diegosouzapw/OmniRoute/pull/12437)) +- **fix(adapta):** redact streamed upstream errors ([#12438](https://github.com/diegosouzapw/OmniRoute/pull/12438)) +- **fix(sse):** remap non-contiguous upstream tool_calls index to a gap-free output_index ([#12445](https://github.com/diegosouzapw/OmniRoute/pull/12445)) — thanks @hartmark +- **fix(quality):** detect duplicate tool_calls entries in one response ([#12446](https://github.com/diegosouzapw/OmniRoute/pull/12446)) — thanks @hartmark +- **fix(conversations):** resolve turn content OmniRoute never sends back to the client ([#12447](https://github.com/diegosouzapw/OmniRoute/pull/12447)) — thanks @hartmark +- **fix(combo):** do not treat credits-exhausted 401 as auth skip ([#12449](https://github.com/diegosouzapw/OmniRoute/pull/12449)) — thanks @RaviTharuma +- **fix(auth):** do not park healthy quota accounts as expired ([#12452](https://github.com/diegosouzapw/OmniRoute/pull/12452)) — thanks @RaviTharuma +- **fix(responses-continuation):** fail closed on a collector-truncated, empty output array ([#12460](https://github.com/diegosouzapw/OmniRoute/pull/12460)) — thanks @hartmark +- **fix(logging):** raise the SSE payload collector's default cap ([#12461](https://github.com/diegosouzapw/OmniRoute/pull/12461)) — thanks @hartmark +- **fix(resilience):** surface Responses failed.error.message in 502s ([#12472](https://github.com/diegosouzapw/OmniRoute/pull/12472)) — thanks @RaviTharuma +- **fix(security):** unbiased maxai X-Random nonce + stricter URL/regex assertions ([#12502](https://github.com/diegosouzapw/OmniRoute/pull/12502)) +- **fix(video):** re-anchor transcript log-redaction so PII/credential maskers can't reopen the leak (#12150 P1 follow-up) ([#12503](https://github.com/diegosouzapw/OmniRoute/pull/12503)) +- **fix(monitoring):** serve cached credentialHealth off the request path ([#12533](https://github.com/diegosouzapw/OmniRoute/pull/12533)) — thanks @RaviTharuma +- **fix(quota):** keep Antigravity Gemini usable when Claude weekly is empty ([#12566](https://github.com/diegosouzapw/OmniRoute/pull/12566)) — thanks @HouMinXi +- **fix(authz):** hard-gate every credential export and CLI-config write (GHSA-5926-2w35-7h4q) ([#12600](https://github.com/diegosouzapw/OmniRoute/pull/12600)) +- **fix(docs):** restore the Next build — REMOVED_PROVIDERS.md had no frontmatter (base-red #12581) ([#12610](https://github.com/diegosouzapw/OmniRoute/pull/12610)) +- **fix(quality):** re-point the zcodeProtocol public-creds allowlist to line 313 ([#12615](https://github.com/diegosouzapw/OmniRoute/pull/12615)) +- **fix(security):** close 3 advisories — search baseUrl exfil, sk- in the error sanitizer, bifrost relay header leak ([#12620](https://github.com/diegosouzapw/OmniRoute/pull/12620)) +- **fix(providers):** sanitize boolean required and nested bare maps for Gemini (#12269) ([#12624](https://github.com/diegosouzapw/OmniRoute/pull/12624)) — thanks @HouMinXi +- **fix(resilience):** keep Overloaded STREAM_EARLY_EOF off the provider breaker ([#12626](https://github.com/diegosouzapw/OmniRoute/pull/12626)) — thanks @HouMinXi +- **fix(catalog):** re-audit free-tier quotas against official pages ([#12649](https://github.com/diegosouzapw/OmniRoute/pull/12649)) +- **fix(dashboard):** keep a request's pending-tracking id stable across combo target retries ([#12650](https://github.com/diegosouzapw/OmniRoute/pull/12650)) — thanks @hartmark +- **fix(tests):** retire dead model ids from the chat-pipeline integration suite (base-red #12581) ([#12670](https://github.com/diegosouzapw/OmniRoute/pull/12670)) +- **fix:** resolve SqliteError no such table compression_run_telemetry during cleanup ([#12682](https://github.com/diegosouzapw/OmniRoute/pull/12682)) — thanks @groovecityJO +- **fix(combo):** treat a pin-only step as implicit connection allowlist ([#12697](https://github.com/diegosouzapw/OmniRoute/pull/12697)) — thanks @HouMinXi +- **fix(ci):** ship .npmrc in published package so legacy-peer-deps applies to consumers (#11544) ([#12699](https://github.com/diegosouzapw/OmniRoute/pull/12699)) — thanks @KooshaPari +- **fix(docker):** pin 4 CLI tools to exact versions (#12576) ([#12703](https://github.com/diegosouzapw/OmniRoute/pull/12703)) — thanks @KooshaPari +- **fix(docker):** document and harden cli profile trust boundary (#12570) ([#12706](https://github.com/diegosouzapw/OmniRoute/pull/12706)) — thanks @KooshaPari +- **fix(sse):** redact video transcript in pre-guardrail rejected-request logs (#12150 P2 item 7) ([#12710](https://github.com/diegosouzapw/OmniRoute/pull/12710)) +- **fix(i18n):** quote placeholder in OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES description (#12505) ([#12769](https://github.com/diegosouzapw/OmniRoute/pull/12769)) — thanks @KooshaPari +- **fix(api/pricing):** surface validation error message as string, not raw object (#12494) ([#12771](https://github.com/diegosouzapw/OmniRoute/pull/12771)) — thanks @KooshaPari +- **fix(sse):** re-enable prompt compression for native Codex passthrough ([#12834](https://github.com/diegosouzapw/OmniRoute/pull/12834)) — thanks @thomasmaerz +- **fix(dashboard):** model health tests for a provider node set to the Responses API now call `/v1/responses` with a Responses-shaped body instead of `/v1/chat/completions` — those models were reported as `Provider returned HTTP 200 but no text content` even though the same model answered normally through `/v1/responses` ([#13070](https://github.com/diegosouzapw/OmniRoute/issues/13070)) ([#13087](https://github.com/diegosouzapw/OmniRoute/pull/13087)) — thanks @ntdatt812 +- fix(routing): stop a round-robin combo's "opencode" targets from collapsing onto the opencode-zen connection (#11912) ([#13283](https://github.com/diegosouzapw/OmniRoute/pull/13283)) +- fix(routing): stop the reactive-compaction debug log from lying when compression is globally disabled (#11977) ([#13276](https://github.com/diegosouzapw/OmniRoute/pull/13276)) +- fix(electron): relativize standalone-bundle symlink targets so Stage 8 manifest verification stops failing on Windows (#11979) ([#13251](https://github.com/diegosouzapw/OmniRoute/pull/13251)) +- fix(dashboard): surface a visible error when Compression Studio's combined preview run fails (#12061) ([#13288](https://github.com/diegosouzapw/OmniRoute/pull/13288)) +- fix(dashboard): make the compression "Effective pipeline" preview honor the active profile and warn when the master switch is off (#12063) ([#13287](https://github.com/diegosouzapw/OmniRoute/pull/13287)) +- fix(providers): scope TinyCMS Web signer's DOM shims to each call instead of leaking them for the process lifetime, and surface a clean HTTP status on a non-JSON interception-toggles error (#12072) ([#13254](https://github.com/diegosouzapw/OmniRoute/pull/13254)) +- fix(guardrails): stop Vision Bridge from re-selecting a model locked after a 404 (#12111) ([#13259](https://github.com/diegosouzapw/OmniRoute/pull/13259)) +- fix(sse): require Responses-shaped body before native OpenAI-compatible passthrough (#12129) ([#13278](https://github.com/diegosouzapw/OmniRoute/pull/13278)) +- fix(providers): minimax-m3 now collapses manual thinking.type:"enabled" to adaptive, preventing upstream 400 (2013) (#12132) ([#13249](https://github.com/diegosouzapw/OmniRoute/pull/13249)) +- fix(db): scope model visibility overrides by modality so hiding a Chat model no longer hides an identically-ID'd Image/Embeddings/etc. model (#12172) ([#13258](https://github.com/diegosouzapw/OmniRoute/pull/13258)) +- fix(db): scope local-provider apiKey dedup to matching base URL so LM Studio/Ollama-style connections support multiple accounts (#12173) ([#13250](https://github.com/diegosouzapw/OmniRoute/pull/13250)) +- fix(providers): refresh Trae's stale Referer/Origin and forward user timezone so imported connections stop failing with 401 (#12190) ([#13255](https://github.com/diegosouzapw/OmniRoute/pull/13255)) +- fix(providers): route opencode-go/gpt-5.6-luna to /responses instead of /chat/completions (#12196) ([#13275](https://github.com/diegosouzapw/OmniRoute/pull/13275)) +- fix(dashboard): allow deleting the last extra-upstream-header row even when invalid (#12251) ([#13252](https://github.com/diegosouzapw/OmniRoute/pull/13252)) +- **fix(i18n):** translate pre-existing `__MISSING__:` keys for `combo.sort`, `requestLogger.detail` expand/collapse, `common.profile`, and `settings.resilienceCredentialHealth*` across 39 locales ([#12272](https://github.com/diegosouzapw/OmniRoute/issues/12272)) ([#13034](https://github.com/diegosouzapw/OmniRoute/pull/13034)) — thanks @HouMinXi +- fix(cli): run the Node.js runtime compatibility guard before the heavy `tsx/esm` + Commander import chain so an unsupported runtime gets a clear message instead of a raw `Invalid regular expression flags` crash (#12296) ([#13253](https://github.com/diegosouzapw/OmniRoute/pull/13253)) +- fix(dashboard): refresh the providers list after deleting a compatible provider node (#12298) ([#13256](https://github.com/diegosouzapw/OmniRoute/pull/13256)) +- fix(usage): fail closed on API-key budget enforcement when a provider's `auto` routing alias has no pricing row, instead of silently counting it as $0 (#12341) ([#13257](https://github.com/diegosouzapw/OmniRoute/pull/13257)) +- **fix(providers):** include the submitted Agnes video model when polling by `video_id` ([#12356](https://github.com/diegosouzapw/OmniRoute/pull/12356)) — thanks @pacocartones +- **fix(routing):** custom OpenAI-compatible nodes now honor the saved Chat/Responses API type after edits instead of letting the node's original ID prefix override the live connection setting ([#11884](https://github.com/diegosouzapw/OmniRoute/issues/11884)). ([#12358](https://github.com/diegosouzapw/OmniRoute/pull/12358)) — thanks @pacocartones +- fix(sse): surface an error instead of a silent empty 200 when a Claude stream closes with zero bytes (#12398) ([#13285](https://github.com/diegosouzapw/OmniRoute/pull/13285)) +- fix(oauth): warn before the dead localhost:8080 redirect in antigravity/gemini `oauth start` (#12413) ([#13265](https://github.com/diegosouzapw/OmniRoute/pull/13265)) +- fix(providers): stop devin-cli spawn error from double-closing the SSE controller (#12517) ([#13261](https://github.com/diegosouzapw/OmniRoute/pull/13261)) +- **fix(rerank):** clamp Voyage `top_k` to the documents actually sent after empty-string filtering, and honor `return_documents: false` in the NVIDIA response adapter (#12523 — thanks @pacocartones) +- **fix(audio):** `/v1/audio/translations` now resolves combo names the way `/v1/audio/transcriptions` already does, so a combo that `GET /v1/models` advertises is fanned out to its targets instead of being rejected with `400 Invalid translation model: . Use format: provider/model`; literal `provider/model` ids and unknown bare names behave as before (#12536 — thanks @pacocartones) +- **fix(gemini):** strip the JSON-Schema-2020-12 `prefixItems` keyword from Gemini tool schemas at every nesting level, so Claude Code tool definitions no longer fail with `400 Unknown name "prefixItems"` on Gemini models (#12540 — thanks @pacocartones) +- **fix(api-manager):** Expose an accessible loading status while API keys are fetched instead of an empty accessibility tree (#12541 — thanks @pacocartones) +- **fix(video):** Clamp `estimateJpegFrameBytes` at zero for padding-only payloads and build the three encode-side frame data URIs from `JPEG_FRAME_DATA_URI_PREFIX` instead of a repeated literal (#12543 — thanks @pacocartones) +- **fix(devin):** accept Windows `DEVIN_AGENTIC_HOME` sandbox paths (`C:\...\.sandbox\...`) in the isolated-home check so the Devin Claude Bridge no longer fails closed on Windows ([#12405](https://github.com/diegosouzapw/OmniRoute/issues/12405)) (#12545 — thanks @pacocartones) +- **fix(db):** Add `conversation_turn_nodes` and orphaned `agentic_conversations` to the auto-cleanup cycle under the existing `retention.callLogs` window, so identity nodes whose call-log content has already been purged no longer accumulate without bound in `storage.sqlite` (#12548 — thanks @pacocartones) +- **fix(i18n):** Wrap the `~/.claude/profiles//settings.json` placeholder in ICU single quotes in the `OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES` feature-flag description across all 42 locales and the TypeScript default, so next-intl no longer fails with `INVALID_MESSAGE: UNCLOSED_TAG` and the Feature Flags card shows the description instead of the raw key (#12549 — thanks @pacocartones) +- **fix(orchestration):** `updateCloudAgentTask` now publishes the task's real `status` on `agent.task.updated` when an update only touches `result`, `activities` or `error`, instead of the fabricated `"updated"` state, and stays silent when no row matched the id (#12550 — thanks @pacocartones) +- **fix(i18n):** the home "Recent Requests" panel and the Provider Topology legend are now translated instead of rendering English copies on non-English dashboards; the legend reads its own `home.topologyLegend*` labels with consistent casing rather than borrowing the memory-settings "Recent" and analytics "Error" strings (#12551 — thanks @pacocartones). +- **docs(reference):** bring the `FEATURE_FLAGS.md` catalog back to 1:1 with `featureFlagDefinitions.ts` — 20 missing flags added, the two `*_BLOCK_THRESHOLD` env-only knobs moved out of the flag tables, category/total counts and the Live WS port corrected, guarded by a static test (#12552 — thanks @pacocartones) +- **fix(i18n):** backfill missing `usage.kiloPass*` strings in 39 locales and restore `featureFlagOmnirouteDisableThinkingLevelVariantsDescription` in `pt.json` ([#12561](https://github.com/diegosouzapw/OmniRoute/issues/12561)) ([#13027](https://github.com/diegosouzapw/OmniRoute/pull/13027)) — thanks @HouMinXi +- fix(docker): default docker-compose app ports (dashboard/API/live-WS) to loopback instead of `0.0.0.0`, closing the anonymous `/v1` LAN/WAN exposure gap left open by `REQUIRE_API_KEY=false` (#12568) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): close DNS-rebinding SSRF gap in webhook outbound-URL guard (#12569) ([#13243](https://github.com/diegosouzapw/OmniRoute/pull/13243)) +- fix(docker): require a per-session token on the VNC browser CDP bridge and isolate it on a dedicated Docker network (#12571) ([#13241](https://github.com/diegosouzapw/OmniRoute/pull/13241)) +- fix(open-sse): write Adobe Firefly session tokens and cookie jars with 0700/0600 permissions instead of the process umask (#12572) ([#13242](https://github.com/diegosouzapw/OmniRoute/pull/13242)) +- fix(api): remove hardcoded wildcard CORS in openai-to-gemini-sse.ts so the centralized fail-closed CORS gate is the sole source of `Access-Control-Allow-Origin` (#12573) ([#13239](https://github.com/diegosouzapw/OmniRoute/pull/13239)) +- fix(api): enforce API key policy (budget/rate-limit/schedule/endpoint scoping) on the ElevenLabs speech-to-text, text-to-speech and voices proxy routes (#12574) ([#13244](https://github.com/diegosouzapw/OmniRoute/pull/13244)) +- fix(sse): cap HuggingChat NDJSON body size and bound the read loop with the fetch timeout so a stalled or hostile upstream cannot buffer unbounded memory (#12577) ([#13282](https://github.com/diegosouzapw/OmniRoute/pull/13282)) +- fix(docker): scope the cliproxyapi/qdrant/bifrost sidecars to loopback by default and forward `CLIPROXYAPI_MANAGEMENT_KEY` into the cliproxyapi container so its management API is not left both unauthenticated and LAN-published (#12578) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): create DB export temp paths with `fs.mkdtempSync` instead of predictable timestamps (#12579) ([#13237](https://github.com/diegosouzapw/OmniRoute/pull/13237)) +- Cline 401 bodies that say "re-authenticate your Cline account" classify as a refreshable OAuth token, not a terminal expired key. The cooling panel no longer labels every cooldown as a 429; it shows the recorded last error instead. (#12594) ([#13060](https://github.com/diegosouzapw/OmniRoute/pull/13060)) — thanks @HouMinXi +- **fix(catalog):** degrade unknown combo targets instead of dropping LCD modalities, and persist OpenRouter `architecture.input_modalities` into the capability snapshot ([#12613](https://github.com/diegosouzapw/OmniRoute/issues/12613)) ([#13042](https://github.com/diegosouzapw/OmniRoute/pull/13042)) — thanks @HouMinXi +- fix(providers): send `x-api-key` instead of `Authorization: Bearer` for OpenCode Zen's `/v1/responses` endpoint (Muse Spark Contributor models), fixing a 401 on OmniRoute's auth header (#12633) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(sse): surface the actionable "Auggie CLI not found" message when the shell reports a missing `auggie` binary via exit code instead of a spawn error (#12645) ([#13263](https://github.com/diegosouzapw/OmniRoute/pull/13263)) +- **fix(config):** Persist deletions of built-in background-degradation entries — when a stored settings record exists its `degradationMap` is now authoritative instead of being merged under the defaults, so an entry the user removed in the dashboard no longer reappears on the next apply or restart ([#12424](https://github.com/diegosouzapw/OmniRoute/issues/12424)) ([#12647](https://github.com/diegosouzapw/OmniRoute/pull/12647)) — thanks @pacocartones +- **fix(gamification):** action-count badge milestones (First Token, Token Consumer, Token Machine, Token Whale, and the token-sharing tier) are now backed by a durable `xp_action_counts` counter incremented in `addXp()`, instead of a live `COUNT(*)` over `xp_audit_log`. The audit log is pruned by `retention.xpAuditLog` (default 30 days), so on a default install those "lifetime" milestones were really "actions in the last 30 days" and unlocked badges could stop unlocking once old rows aged out. `getActionCount()` and `checkActionCountBadges()` now read the same durable source, and a migration backfills existing totals from the surviving audit rows ([#12546](https://github.com/diegosouzapw/OmniRoute/issues/12546)) ([#12651](https://github.com/diegosouzapw/OmniRoute/pull/12651)) — thanks @pacocartones +- **fix(images):** `/v1/images/edits` now iterates a combo's targets the same way `/v1/images/generations` does (#9239) instead of flattening a bare combo to its first target. A combo whose first target is not edit-capable — or lacks credentials — now falls through to a later edit-capable target rather than hard-erroring, and missing credentials are skipped (not a hard `401`) to match the generations path. The per-target skip/terminal classification is extracted into a shared `runImageComboTargets` loop, so generations behavior is unchanged ([#12547](https://github.com/diegosouzapw/OmniRoute/issues/12547)). ([#12653](https://github.com/diegosouzapw/OmniRoute/pull/12653)) — thanks @pacocartones +- fix(sse): add first-byte watchdog to the TLS-fingerprint transport so a stalled wreq body falls back instead of hanging for minutes (#12656) ([#13272](https://github.com/diegosouzapw/OmniRoute/pull/13272)) +- fix(sse): exempt tiny-budget reasoning probes from combo quality failure and surface persisted-cooldown skips in ALL_TARGETS_SKIPPED diagnostics (#12659) ([#13269](https://github.com/diegosouzapw/OmniRoute/pull/13269)) +- fix(models): declare the real ~1M contextLength for OpenCode Zen's Muse Spark 1.2 models instead of falling back to the 200000 provider default (#12681) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(oauth): align codebuddy-cn OAuth User-Agent with the chat/usage CLI version to avoid WAF false positives (#12702) ([#13264](https://github.com/diegosouzapw/OmniRoute/pull/13264)) +- fix(dashboard): surface an authentication-required banner instead of silently blanking database settings for a guest session (#12709) ([#13270](https://github.com/diegosouzapw/OmniRoute/pull/13270)) +- **fix(resilience):** stop unbounded queue that hangs 6min until Aborted — gate, provider slot, and Bottleneck queue now share a per-connection `maxWaitMs` budget; `fail-closed` on exhaust (503); execution backstop `executionMaxWaitMs` overridable per connection with upstream clamp ([#12715](https://github.com/diegosouzapw/OmniRoute/pull/12715)) — thanks @maxmad64bis (with thanks to @Tushar49 for surfacing the slow-provider need in #12635) +- **fix(auto-combo):** every mode pack now carries `quality` and `reliability` weights (`quality` 0.02, 0.03 in `quality-first`; `reliability` 0.03, 0.04 in `reliability-first`), so selecting a pack no longer silences either signal; `DEFAULT_WEIGHTS` is unchanged ([#12731](https://github.com/diegosouzapw/OmniRoute/pull/12731)) — thanks @maxmad64bis +- **fix(sse):** the provider execution pipeline keeps the upstream error `code`/`type` again — both were lost when the non-streaming leg was extracted, so a config-class `422` (Antigravity missing project) degraded into a generic account cooldown instead of reaching the gate that recognises it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(db):** a legacy `call_logs` table no longer aborts startup — the provider-stats index is created after the column healing runs, not before it, so an install predating `request_type` opens instead of failing with `no such column` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(catalog):** a malformed row in the operator-writable custom-models blob no longer kills every `auto/*` pool ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(tests):** realign the guards that the same day's merges left asserting the old behavior — the provider-count total after GreenPT and EURouter, the GLM stream buffer slot that `#12925` turned into a declared parameter, and the injection scan bound that `#13104` reshaped from a head-only window into head-plus-tail ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- fix(cache): fold tool_choice/tools/response_format into the semantic cache signature so a cached tool_calls response can no longer be replayed for a request whose tool policy forbids it (#12734) ([#13267](https://github.com/diegosouzapw/OmniRoute/pull/13267)) +- **fix(models):** stop treating provider-supplied `isFree:true`/`:free`/`0/0` flags as trusted unless the provider has a documented free tier — only the shipped free-tier catalog decides otherwise; fetched `isFree:true` trusted only for a free-tier provider, custom `isFree:true` via a trusted path ([#12744](https://github.com/diegosouzapw/OmniRoute/pull/12744)) — thanks @maxmad64bis +- fix(memory): authenticate the internal /v1/rerank loopback call so memory reranking no longer silently degrades to unranked order when REQUIRE_API_KEY=true (#12745) ([#13268](https://github.com/diegosouzapw/OmniRoute/pull/13268)) +- fix(sse): parse Ollama Cloud's current usage markup (`$X of $Y used` aria-label, nested width style) (#12749) ([#13260](https://github.com/diegosouzapw/OmniRoute/pull/13260)) +- fix(cli): setup-opencode no longer sends an active context's management token to `/v1/models` when `--api-key`/`OMNIROUTE_API_KEY` is supplied — an explicit flag or the env var now always outranks the context's token, and the flag itself is no longer swallowed by the parent program's global `--api-key` option (#12783) ([#13246](https://github.com/diegosouzapw/OmniRoute/pull/13246)) +- fix(sse): register Arcee AI in the executor provider registry so requests reach api.arcee.ai instead of silently falling back to OpenAI (#12784) ([#13277](https://github.com/diegosouzapw/OmniRoute/pull/13277)) +- **fix(quota):** share one upstream quota read when identical saturation checks arrive at the same time, so traffic bursts don't multiply provider API calls ([#12787](https://github.com/diegosouzapw/OmniRoute/pull/12787)) — thanks @maxmad64bis +- **fix(db):** ignore expired or invalid rate-limit cooldown writes so a stale timestamp can't lock a connection that should be usable — clearing still works as before ([#12788](https://github.com/diegosouzapw/OmniRoute/pull/12788)) — thanks @maxmad64bis +- **fix(providers):** single source for provider order with `xao` ranking alongside `xai-oauth` ([#12790](https://github.com/diegosouzapw/OmniRoute/pull/12790)) — thanks @maxmad64bis +- **fix(routing):** off-table models check the free-model catalog before inheriting premium prices, latency bootstraps from the observed pool median, async tiers read live database pricing with a 90-day freshness gate, and the tier cache invalidates on every pricing write ([#12792](https://github.com/diegosouzapw/OmniRoute/pull/12792)) — thanks @maxmad64bis +- **fix(auto-combo):** snapshot scoring uses the observed per-connection account tier and per-model quality instead of neutral constants, and circuit-open providers rank lower at build time ([#12794](https://github.com/diegosouzapw/OmniRoute/pull/12794)) — thanks @maxmad64bis +- **fix(pool):** empty `auto/*` pools now say why they are empty, and the models listing uses the same paid check as routing ([#12795](https://github.com/diegosouzapw/OmniRoute/pull/12795)) — thanks @maxmad64bis +- fix(routing): recognize CLIProxyAPI's 'unknown provider for model' 400 as fallback-worthy (#12800) ([#13284](https://github.com/diegosouzapw/OmniRoute/pull/13284)) +- **fix(sse):** translate-mode streams now emit the estimated token counts as a trailing usage-only chunk before `[DONE]` when the upstream stays silent, so metered chat clients see totals instead of nothing ([#12828](https://github.com/diegosouzapw/OmniRoute/pull/12828)) — thanks @maxmad64bis +- **fix(db):** provider stats stay truthful on empty and legacy databases: fallback counts default to `0` instead of `null`, latency averages read `null` (not `0`) when no durations were recorded, failures logged before the error-type column existed group under `pre_migration` instead of `unclassified`, and per-provider queries use two new composite indexes ([#12832](https://github.com/diegosouzapw/OmniRoute/pull/12832)) — thanks @maxmad64bis +- fix(nvidia): fail open when a synced model catalog goes stale instead of gating forever (#12849) ([#13248](https://github.com/diegosouzapw/OmniRoute/pull/13248)) +- **fix(db):** WAL maintenance lives in its own module: a `TRUNCATE` checkpoint that hits a busy database now warns with its streak and retries once via `PASSIVE` instead of logging success, and closing-time checkpoints no longer report success on builds without a database file, with the busy totals visible in the authenticated monitoring health payload ([#12853](https://github.com/diegosouzapw/OmniRoute/pull/12853)) — thanks @maxmad64bis +- **fix(health):** quota with no snapshots reads empty instead of a contradicting 0% ([#12857](https://github.com/diegosouzapw/OmniRoute/pull/12857)) — thanks @maxmad64bis +- fix(a2a): accept the dashboard's own session cookie on /a2a so "Run message/send" no longer fails with "Unauthorized: missing or invalid API key" (#12888) ([#13271](https://github.com/diegosouzapw/OmniRoute/pull/13271)) +- **fix(logs):** the Logs grid's in-memory filter pass no longer discards rows the SQL query already matched — selecting an API key from the dropdown (which sends the key's id) returns its calls again, the Combo tab shows every combo instead of only those whose name contains a "1", and the model filter and search cover the same columns as the query ([#12896](https://github.com/diegosouzapw/OmniRoute/pull/12896)) — fixes [#12873](https://github.com/diegosouzapw/OmniRoute/issues/12873) — thanks @ntdatt812 +- **fix(a2a):** `/api/a2a/status` now builds the agent card from the request that asked for it, so a gateway reached at a non-localhost host no longer advertises `http://localhost:20128` as its A2A URL ([#12918](https://github.com/diegosouzapw/OmniRoute/pull/12918)). — thanks @ntdatt812 +- **fix(compression):** progressive aging now appends its `[COMPRESSED:aging:…]` annotation after a turn's `tool_result` blocks instead of in front of them, so Anthropic no longer rejects aged conversations with "`tool_use` ids were found without `tool_result` blocks immediately after" ([#12920](https://github.com/diegosouzapw/OmniRoute/pull/12920)). — thanks @ntdatt812 +- **fix(bedrock):** model import now resolves context limits for every vendor prefix instead of only `anthropic.*`, so `global.openai.gpt-5.6-*` no longer imports with a null `inputTokenLimit` and gets rejected pre-flight at the 200k default ([#12921](https://github.com/diegosouzapw/OmniRoute/pull/12921)). — thanks @ntdatt812 +- **fix(stream):** the 64 KB stream buffer GLM asks for is honoured instead of dropped, and the type error it caused no longer fails the API Route Typecheck gate on every open PR ([#12925](https://github.com/diegosouzapw/OmniRoute/pull/12925)) — thanks @ntdatt812 +- **fix(guardrails):** mask PII inside a `tool_result`'s nested content array, which the masker walked past while redacting its sibling block ([#12930](https://github.com/diegosouzapw/OmniRoute/pull/12930)) — thanks @ntdatt812 +- **fix(dashboard):** the Radar catalog table no longer leaves absent data unexplained — empty limits, unknown context windows, and unreported capabilities each explain themselves on hover, and a new check keeps it that way ([#12937](https://github.com/diegosouzapw/OmniRoute/pull/12937)) — thanks @maxmad64bis +- **fix(sse):** geo-blocked opencode requests rotate to the next account proxy instead of failing, so one refused egress no longer aborts the whole chain ([#12941](https://github.com/diegosouzapw/OmniRoute/pull/12941)) — thanks @maxmad64bis +- **fix(codex):** dashboard "clear cooldown" and the CAS recovery path now drop nested `codexScopeRateLimitedUntil` maps in the same write that nulls `rate_limited_until`, and fresh quota snapshots with headroom lift fallback-sourced scope cooldowns parked by quota preflight ([#12817](https://github.com/diegosouzapw/OmniRoute/issues/12817), [#12860](https://github.com/diegosouzapw/OmniRoute/issues/12860), [#12951](https://github.com/diegosouzapw/OmniRoute/pull/12951)) The dashboard reset-credit button (`consumeCodexResetCredit`) now exercises that same snapshot path after a successful redeem, so a filled quota bar is enough to unpark a leftover fallback Codex child without another manual clear. — thanks @HouMinXi +- **fix(db):** resolve `sql-wasm.wasm` across global npm install and hoisted layouts, ensuring OmniRoute can boot cleanly on Node 24 when native `better-sqlite3` is uncompiled. ([#13035](https://github.com/diegosouzapw/OmniRoute/pull/13035)) — thanks @HouMinXi +- **fix(api):** `DELETE /v1/batches/delete-completed` now sweeps only the calling API key's own completed batches (batches with no owner stay out of a key-scoped sweep on purpose), with an explicit instance-wide mode reserved for authenticated dashboard sessions, a 401 for a presented key that is unknown, revoked, deactivated, banned or expired (never falling through to the session branch), audit logging of every sweep, a sanitized 500 on failure, an owner-scoped file half (a key-scoped sweep never nulls a file another tenant owns) and an atomic sweep — chunked in 200-batch transactions in instance mode — so a mid-way error never leaves a batch pointing at a nulled file (GHSA-wvxc-jp3v-5mg5) ([#12969](https://github.com/diegosouzapw/OmniRoute/pull/12969)) +- **fix(combo):** quota-weighted routing stops drawing on an out-of-credit connection — a 402 now invalidates the stored quota snapshot instead of leaving its stale remaining percentage in place, and a snapshot older than 10 minutes no longer counts as confident headroom for the primary pool ([#12972](https://github.com/diegosouzapw/OmniRoute/pull/12972)) ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- **fix(vision):** Custom Models with "Vision capable" checked no longer have image requests swapped to `glm/glm-4.6v` when the client sends the advertised alias (`vllm/path/...`) or the bare path-shaped id — Vision Bridge now matches the stored override for all three id forms ([#12758](https://github.com/diegosouzapw/OmniRoute/issues/12758)) ([#12974](https://github.com/diegosouzapw/OmniRoute/pull/12974)) — thanks @HouMinXi +- **fix(sse):** transient opencode upstream failures rotate to the next account proxy instead of failing, so one flapping egress no longer aborts the whole chain ([#12975](https://github.com/diegosouzapw/OmniRoute/pull/12975)) — thanks @maxmad64bis +- **fix(azure):** Deployments from GPT-6 onward now send `max_completion_tokens` instead of `max_tokens`, which Azure rejects with HTTP 400. The rule matched a literal `gpt-5`, so each new generation arrived broken; it now matches the generation range, while `gpt-35-turbo` still keeps `max_tokens`. ([#13007](https://github.com/diegosouzapw/OmniRoute/pull/13007)) — thanks @ntdatt812 +- **fix(db):** add an opt-in self-restart circuit for sustained critical memory pressure, gate post-cleanup VACUUM behind a minimum freed-rows threshold, and checkpoint the SQLite WAL every 5 minutes with a size guard that escalates to TRUNCATE, so a growing WAL can no longer stall the event loop into a full outage. ([#13011](https://github.com/diegosouzapw/OmniRoute/pull/13011)) — thanks @HouMinXi +- **fix(auth):** an explicit connection pin may probe a quota-disabled row once and re-enable it on success ([#12874](https://github.com/diegosouzapw/OmniRoute/issues/12874)) ([#13017](https://github.com/diegosouzapw/OmniRoute/pull/13017)) — thanks @HouMinXi +- **fix(api):** thread `X-OmniRoute-Fallback-Attempts` through combo chat completions so streaming and non-streaming responses report how many prior legs were attempted ([#13038](https://github.com/diegosouzapw/OmniRoute/pull/13038)) — thanks @HouMinXi +- Restore provider failure classification and credential refresh on non-streaming requests: classify non-2xx failures to lock models on per-model quota exhaustion, update connection rate limits from headers and body, and pass credential refresh handlers to pipeline execution so 401 tokens can be refreshed and retried (#13043). ([#13069](https://github.com/diegosouzapw/OmniRoute/pull/13069)) — thanks @HouMinXi +- **fix(responses):** wrap forced-non-streaming web_search fallback JSON as Responses SSE so Codex still sees `response.completed` ([#13050](https://github.com/diegosouzapw/OmniRoute/pull/13050)) — thanks @HouMinXi +- **fix(antigravity):** Preserve upstream thought-token usage in normalized completion and reasoning token counts ([#13055](https://github.com/diegosouzapw/OmniRoute/pull/13055)) — thanks @pacocartones +- Fix a concurrency-slot leak in the DeepSeek PoW solver: a worker that failed to spawn (for example a missing worker script) never released its slot, so `MAX_CONCURRENT_WORKERS` failures disabled the solver until restart. ([#13097](https://github.com/diegosouzapw/OmniRoute/pull/13097)) — thanks @anhtahaylove +- **fix(acp):** bound the ACP session output buffers — `stdoutBuffer` and `stderrBuffer` now cap at 1 MiB keeping the most recent output behind a visible `[...output truncated...]` marker, and `stderrBuffer` is reset per prompt instead of accumulating for the lifetime of the session. ([#13100](https://github.com/diegosouzapw/OmniRoute/pull/13100)) — thanks @anhtahaylove +- **fix(acp):** release the `stdout`/`exit` listeners and the idle timer that a `sendPrompt` timeout used to leave attached to the `acpManager` singleton, and drop sessions that exited on their own from the session map instead of keeping them forever. ([#13096](https://github.com/diegosouzapw/OmniRoute/pull/13096)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection and PII scanners now read the text a `tool_result` block carries on `content` (string or nested block list), in messages and in system blocks, so tool output is judged by the same rules as user text ([#13101](https://github.com/diegosouzapw/OmniRoute/pull/13101)) — thanks @ntdatt812 +- **fix(gamification):** close the badge notification SSE stream when the request signal is already aborted before the stream starts — a client that disconnects while the route is still awaiting auth used to leave both the 2s unlock poll and the 15s heartbeat running for the lifetime of the process. ([#13106](https://github.com/diegosouzapw/OmniRoute/pull/13106)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection scan now spends its 16 KB budget on both ends of the request instead of the first 16 KB only, so `system`, `instructions`, `query`, `documents` and the newest turns are no longer hidden behind one long message ([#13104](https://github.com/diegosouzapw/OmniRoute/pull/13104)) — thanks @ntdatt812 +- **fix(dashboard):** expose the Volcano Ark console cookie on quota scraping and unwrap connect-error objects so the dashboard shows the upstream message ([#13107](https://github.com/diegosouzapw/OmniRoute/pull/13107)) — thanks @HouMinXi +- **fix(db):** release the `beforeExit`/`SIGINT`/`SIGTERM` handlers when a `node:sqlite` adapter closes, so a closed adapter and its database handle are no longer pinned to `process` for the lifetime of the run — the same treatment #7494 gave the sql.js adapter. ([#13109](https://github.com/diegosouzapw/OmniRoute/pull/13109)) — thanks @anhtahaylove +- **fix(translator):** `contentSchema` and `unevaluatedItems` are now treated as subschema positions by the tool-schema sanitizer, so a truncation placeholder in either is replaced with a permissive schema instead of being forwarded as a string ([#13110](https://github.com/diegosouzapw/OmniRoute/pull/13110)) — thanks @ntdatt812 +- **fix(cli-helper):** clear the `createLogStream` timeout on the abort path — `stop()` aborts the in-flight fetch and returned through the `signal.aborted` branch, which skipped `clearTimeout` and left an armed timer per stopped stream. The stream reader is now also cancelled when the read loop exits early. ([#13114](https://github.com/diegosouzapw/OmniRoute/pull/13114)) — thanks @anhtahaylove +- **fix(docs):** drop TLS-impersonation, MITM-decrypt, supply-chain attestation, and XOR-mask writeups from the public `/docs` catalog and Docker image. Files stay in git for engineers; operators who need them open the repo, not the website. ([#13136](https://github.com/diegosouzapw/OmniRoute/pull/13136)) — thanks @HouMinXi +- **fix(combo):** parse numeric-epoch `rate_limited_until` in the combo cooldown read path ([#13141](https://github.com/diegosouzapw/OmniRoute/pull/13141)) — thanks @maxmad64bis +- **fix(opencode):** both OpenCode plugins now reject a gateway address typed without `http://` at configuration time, instead of publishing every model with an api url no client can call, and the v2 plugin no longer publishes a model card whose api url is blank or relative ([#13142](https://github.com/diegosouzapw/OmniRoute/pull/13142)) — thanks @maxmad64bis +- **fix(providers):** lock opencode model on upstream 400 model-unavailable ([#13146](https://github.com/diegosouzapw/OmniRoute/pull/13146)) — thanks @maxmad64bis +- **fix(logging):** keep the provider exchange rather than the raw client bodies when a call log exceeds its size budget, and show that recovered payload in the request-detail panel instead of replacing it with the stored response body ([#13147](https://github.com/diegosouzapw/OmniRoute/pull/13147)) — thanks @maxmad64bis +- **fix(traffic-inspector):** the WebSocket route no longer leaks a traffic-buffer subscriber and a 30s ping timer when the client socket is already closed at handler time — listeners are attached before any resource is acquired, a destroyed socket bails out early, and the ping interval stops on a dead socket where `write()` never throws ([#13155](https://github.com/diegosouzapw/OmniRoute/pull/13155)) — thanks @anhtahaylove +- **fix(telegram):** bound the per-user API key cache in the Telegram chat proxy so a burst of distinct chat ids can no longer grow the process heap without limit ([#13165](https://github.com/diegosouzapw/OmniRoute/issues/13165)) ([#13166](https://github.com/diegosouzapw/OmniRoute/pull/13166)) — thanks @anhtahaylove +- **fix(stream):** cancel the upstream response body when the JSON-to-SSE sniff unwinds on a body timeout, so a stalled upstream no longer pins the connection ([#13169](https://github.com/diegosouzapw/OmniRoute/issues/13169)) ([#13171](https://github.com/diegosouzapw/OmniRoute/pull/13171)) — thanks @anhtahaylove +- **fix(telegram):** authenticate webhook deliveries with Telegram's `secret_token` so an unauthenticated caller can no longer mint API keys or spend upstream quota ([#13172](https://github.com/diegosouzapw/OmniRoute/issues/13172)) ([#13175](https://github.com/diegosouzapw/OmniRoute/pull/13175)) — thanks @anhtahaylove +- **fix(models):** give discoverable Gemini 3.8 Flash ids their own 65536 output spec so Antigravity no longer clamps them to 16384 ([#13195](https://github.com/diegosouzapw/OmniRoute/pull/13195)) — thanks @HouMinXi +- **fix(dashboard):** leftover catalog-removed provider rows (gemini-cli) can be listed and purged from the providers page ([#13067](https://github.com/diegosouzapw/OmniRoute/issues/13067)) ([#13197](https://github.com/diegosouzapw/OmniRoute/pull/13197)) — thanks @HouMinXi +- **fix(auth):** a dashboard session now requires the `authenticated: true` claim that login, OIDC and the session refresh already emit — a JWT merely signed with `JWT_SECRET` (for example the Cursor CLI passthrough token, which any API-key holder can obtain) no longer verifies as the `auth_token` cookie on any route, the WebSocket handshake or the live server; existing sessions keep working ([#13298](https://github.com/diegosouzapw/OmniRoute/issues/13298)) ([#13375](https://github.com/diegosouzapw/OmniRoute/pull/13375)) +- **fix(api):** `DELETE /v1/batches/delete-completed` now applies the caller's API-key policy (endpoint allowlist, schedule, usage cap, rate limit) like every other `/v1` route, commits the key-scoped sweep per 200-batch chunk instead of holding one write lock for the whole sweep, refuses to loop without progress, and rejects a scope that names both a key and `allTenants` ([#13684](https://github.com/diegosouzapw/OmniRoute/pull/13684)) +- Restore the API-route typecheck gate: the non-streaming leg result lost its discriminated-union narrowing after the server-owned tool loop reassignment, producing 13 new TS2339 diagnostics in `chatCore.ts`. ([#12963](https://github.com/diegosouzapw/OmniRoute/pull/12963)) +- Restore four non-streaming behaviours the server-owned tool loop refactor dropped: client aborts map to 499 with the fixed `Request aborted` message, an aborted request no longer logs a synthetic `clientResponse`, Claude prompt-cache telemetry is recorded again, and a body that cannot be canonicalized no longer throws when the tool loop is off. ([#12990](https://github.com/diegosouzapw/OmniRoute/pull/12990)) +- **fix(skills):** The CLI registry parser now reads positionals declared with `.addArgument()`, not only those written inline in `.command()`. `tunnel create [type]` was being published as `tunnel create`, so the agent-skills sync gate reported drift on every branch and regenerating would have deleted the argument. ([#13009](https://github.com/diegosouzapw/OmniRoute/pull/13009)) — thanks @ntdatt812 +- **fix(combos):** dashboard combo test uses a short prompt, serial probes, and a 60s timeout so reasoning models and rate-limited free pools do not fail the health check ([#13001](https://github.com/diegosouzapw/OmniRoute/pull/13001)) — thanks @HouMinXi +- fix(compression): terminate idle worker threads on eviction so long-running instances stop leaking OS threads and MessagePorts ([#13091](https://github.com/diegosouzapw/OmniRoute/pull/13091)) — thanks @anhtahaylove +- Redact Google API keys of any length in error bodies: the pattern required exactly 39 characters, so shorter or longer `AIza…` credentials passed through unredacted. ([#12964](https://github.com/diegosouzapw/OmniRoute/pull/12964)) +- **fix(grok-cli):** a 402 "Grok Build usage balance exhausted" parks that Grok login as out of credit (Grok Build CLI, grok.com cookie, and xAI OAuth share the weekly pool). Combo routing then tries the next login instead of locking the model for every account in the pool ([#13061](https://github.com/diegosouzapw/OmniRoute/pull/13061)) — thanks @HouMinXi +- fix(compression): spawn the LLMLingua worker with a file URL object so compression actually runs instead of silently failing open on Node ([#13093](https://github.com/diegosouzapw/OmniRoute/pull/13093)) — thanks @anhtahaylove +- **fix(test):** run the local `test` and `test:unit` scripts at concurrency 4 so a full-suite run no longer exhausts the machine's commit charge and kills unrelated processes ([#13187](https://github.com/diegosouzapw/OmniRoute/pull/13187)) — thanks @anhtahaylove +- **fix(monitoring):** `GET /api/monitoring/health` `credentialHealth` now includes a bounded `failedConnections` list (`connectionId`, `status`, sanitized `lastError`) when the probe-cache gauge `failed>0`, plus `source: probe-cache` and a cheap `staleDbNonOkCount` for sticky SQLite `test_status` on active rows. Documents that the live gauge is not `provider_connections.test_status`. ([#12876](https://github.com/diegosouzapw/OmniRoute/pull/12876)) — thanks @RaviTharuma +- **fix(resilience):** an openai-compatible multi-upstream gateway no longer marks the whole connection `credits_exhausted` when a single upstream (or the `/models` / representative-model health probe) returns 402. Quota failures stay model-scoped; true connection-wide auth failures are unchanged. Single-credential 402 key disable ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239)) is preserved. ([#12875](https://github.com/diegosouzapw/OmniRoute/pull/12875)) — thanks @RaviTharuma +- **fix(dashboard):** nine fixes on the Orchestration Canvas, all diagnosed in the Phase 2 reviews. A source that fails now keeps the timestamp of its FIRST failure instead of being re-stamped every poll — the stale line said "since the last poll" no matter how long the source had been down, and the churn also defeated the snapshot's stable identity (it serializes the source list), so the canvas re-rendered on every tick while anything was broken. A source that HAD data and only then started failing is flagged too: previously only a source with no node at all got the warning, so a source that went down mid-session kept a healthy-looking node forever. The rest are pointwise: clicking a filter chip cancels the pending search debounce (left armed, it fired ~300ms later and silently reverted the chip); the search input carries an accessible name; `?state=running, failed` parses like the unpadded form instead of dropping the padded value; the CSV toggle helper is defined once in `model/urlParams.ts` rather than twice; the agents tab tells "nothing running" apart from "the filter matched nothing", offering a clear-filters button instead of setup links that would be wrong advice there; edges stop emitting SMIL particles above 40 simultaneously active edges, keeping the colored stroke; and the drawer's error banner clears when a retried action succeeds. ([#12983](https://github.com/diegosouzapw/OmniRoute/pull/12983)) +- **fix(i18n):** the nine locales added with the EU-language batch (Greek, Estonian, Irish, Croatian, Lithuanian, Latvian, Maltese, Slovenian, Serbian) were missing the eleven Orchestration Canvas keys that Phase 3 introduced, so the compare-runs panel and the "no runs match these filters" empty state fell back to English in those languages (`deepMergeFallback` substitutes English for an absent key, so nothing rendered blank — it rendered untranslated). The coverage gate does not catch this: it enforces an 80% floor per locale, and eleven missing keys out of ~13,000 leaves coverage at 99.9%. Translated for real in each language, calibrated against the wording each file already uses for "run", "filter" and "skill". ([#13294](https://github.com/diegosouzapw/OmniRoute/pull/13294)) +- **fix(dashboard):** five follow-ups from the Phase 2 Orchestration Canvas review. The History drawer now shows the memory section for runs that already left the live TTL window: the persisted `memory_hits` event is parsed into `metadata.memoryHits` with the same defensive validation the drawer applies, and — because that event is observability rather than a state transition — it no longer leaks into the timeline, where it had been inheriting the task's state and rendering as a duplicate transition. The A2A memory recall runs against its own 1.5s deadline instead of inheriting the memory backend's 30s one; overshooting degrades exactly like any other recall failure (no hits, task proceeds), and the timer is cleared on both paths. Repeating a Conductor run carries its `requirements.cli`/`requirements.model` forward, so the new run is pinned to the same runner profile and model rather than drifting to whatever the fleet picks. A successful repeat from the Agents tab now focuses the run it created, instead of leaving the operator on the finished one. And the auth test for `POST /api/conductor/tasks` moved into the shared `ROUTES` array rather than restating the pattern. ([#12988](https://github.com/diegosouzapw/OmniRoute/pull/12988)) +- fix(plugins): stop leaking an exit listener per plugin hook timeout, which triggered MaxListenersExceededWarning on plugins that ignore SIGTERM ([#13092](https://github.com/diegosouzapw/OmniRoute/pull/13092)) — thanks @anhtahaylove +- **fix(validation):** Provider node edits no longer fail with a generic "Invalid request" when the optional daily-quota reset fields are left blank. The dashboard sends `dailyQuotaResetTimezone` and `dailyQuotaResetHour` as `null`, and only the hour accepted it. ([#13066](https://github.com/diegosouzapw/OmniRoute/issues/13066)) ([#13083](https://github.com/diegosouzapw/OmniRoute/pull/13083)) — thanks @ntdatt812 +- **fix(combo):** quota-aware expansion drops banned, inactive, missing, and wrong-provider connections before quota fetch or model dispatch; pins and allowlists stay selectors, not a bypass. Antigravity automatic exhaustion now requires a reported zero remaining, so a positive balance below 1% stays eligible. ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- Fix an intermittent failure in the quota-weighted routing test suite: scores are a function of `Date.now()`, so peers with identical quota scored microseconds apart never tied and swapped order. ([#12946](https://github.com/diegosouzapw/OmniRoute/pull/12946)) +- fix(volcengine): resolve quota window mapping gaps, safe multi-connection console binding, and response cookie sanitization ([#12950](https://github.com/diegosouzapw/OmniRoute/pull/12950)) — thanks @HouMinXi +- **fix(test):** resolve the WebDAV handler path with `fileURLToPath` so the suite's 37 WebDAV tests run on Windows instead of failing with a doubled `C:\C:\` drive prefix ([#13196](https://github.com/diegosouzapw/OmniRoute/pull/13196)) — thanks @anhtahaylove +- **fix(ui):** fall back from invalid AnySearch icon alias (direct commit `9587e07b69`) +- **fix(sse):** stop rebuilding a truncated summary from the collector's cap-dropped event array ([#12718](https://github.com/diegosouzapw/OmniRoute/pull/12718)) — thanks @hartmark +- **fix(conversations):** show a pending spinner for unresolved tool nodes instead of "(empty)" ([#12727](https://github.com/diegosouzapw/OmniRoute/pull/12727)) — thanks @hartmark +- **fix(streaming):** fail fast when an upstream stream produces only lifecycle/heartbeat events, never real content ([#12741](https://github.com/diegosouzapw/OmniRoute/pull/12741)) — thanks @hartmark +- **fix(responses-continuation):** bridge the write-in-flight window with an in-memory pending store ([#12854](https://github.com/diegosouzapw/OmniRoute/pull/12854)) — thanks @hartmark +- **fix(translator):** drop or map agent_message on Chat Completions fallback ([#12880](https://github.com/diegosouzapw/OmniRoute/pull/12880)) — thanks @RaviTharuma +- **fix(api):** page and stream-complete GET /v1/models for large catalogs ([#12882](https://github.com/diegosouzapw/OmniRoute/pull/12882)) — thanks @RaviTharuma +- **fix(quota):** align AUTH window labels with usage API and clarify 503 ([#12884](https://github.com/diegosouzapw/OmniRoute/pull/12884)) — thanks @RaviTharuma +- **fix(ci):** clear two base-reds on release/v3.8.51 — mutation-coverage gate + image-only-model guard ([#12945](https://github.com/diegosouzapw/OmniRoute/pull/12945)) +- **fix(api):** scope batch bulk-delete to the calling API key ([#13211](https://github.com/diegosouzapw/OmniRoute/pull/13211)) +- **fix(tests):** align model-sync log assertion to shipped path redaction (base-red) ([#13227](https://github.com/diegosouzapw/OmniRoute/pull/13227)) +- **fix(test):** make the ToS heading guard actually require the parentheses ([#13228](https://github.com/diegosouzapw/OmniRoute/pull/13228)) +- **fix(quality):** register 4 drifted covering tests in stryker tap.testFiles ([#13229](https://github.com/diegosouzapw/OmniRoute/pull/13229)) +- **fix(logging):** mask *-api-key headers in request-log pipeline (#13273) ([#13401](https://github.com/diegosouzapw/OmniRoute/pull/13401)) — thanks @KooshaPari +- **fix(tests):** correct stale Jina catalog prefix in custom-models test ([#13403](https://github.com/diegosouzapw/OmniRoute/pull/13403)) — thanks @KooshaPari +- **fix(backend):** prune old backups after health-check-repair snapshots (#13308) ([#13404](https://github.com/diegosouzapw/OmniRoute/pull/13404)) — thanks @KooshaPari +- **fix(backend):** guard memory_fts_au trigger with WHEN clause to stop FTS bloat (#13326) ([#13405](https://github.com/diegosouzapw/OmniRoute/pull/13405)) — thanks @KooshaPari +- **fix(free-tiers):** remove obsolete Together $25 signup credit (#12526) ([#13407](https://github.com/diegosouzapw/OmniRoute/pull/13407)) — thanks @KooshaPari +- **fix:** skip non-chat generation models in Test all models ([#13409](https://github.com/diegosouzapw/OmniRoute/pull/13409)) — thanks @KooshaPari +- **fix(providers):** de-duplicate agy and antigravity model catalogs ([#13410](https://github.com/diegosouzapw/OmniRoute/pull/13410)) — thanks @KooshaPari +- **fix(dashboard):** send provider in body for mini-playground web search ([#13411](https://github.com/diegosouzapw/OmniRoute/pull/13411)) — thanks @KooshaPari +- **fix(resilience):** add CJK quota-exhaustion patterns to 429 classifier ([#13412](https://github.com/diegosouzapw/OmniRoute/pull/13412)) — thanks @KooshaPari +- **fix(backend):** eval runner: failed calls no longer score as passed + dotAll regex ([#13413](https://github.com/diegosouzapw/OmniRoute/pull/13413)) — thanks @KooshaPari +- **fix(backend):** eval runner opts out of output-style and memory injection ([#13414](https://github.com/diegosouzapw/OmniRoute/pull/13414)) — thanks @KooshaPari +- **fix:** chain serial quarantine step in npm test script (fixes #13286) ([#13418](https://github.com/diegosouzapw/OmniRoute/pull/13418)) — thanks @KooshaPari +- **fix(antigravity):** derive stable session ID from credentials for prompt-cache reuse (#13305) ([#13424](https://github.com/diegosouzapw/OmniRoute/pull/13424)) — thanks @KooshaPari +- **fix(memory):** merge memory into top-level system field instead of unshifting at messages[0] (#13425) ([#13427](https://github.com/diegosouzapw/OmniRoute/pull/13427)) — thanks @KooshaPari +- **fix(compression):** preserve instruction blocks from lossy engines ([#13523](https://github.com/diegosouzapw/OmniRoute/pull/13523)) — thanks @KooshaPari +- **fix(compression):** preserve polarity words and newlines in ultra heuristic ([#13524](https://github.com/diegosouzapw/OmniRoute/pull/13524)) — thanks @KooshaPari +- **fix(compression):** run user preservePatterns before built-in patterns (#13457) ([#13525](https://github.com/diegosouzapw/OmniRoute/pull/13525)) — thanks @KooshaPari +- **fix(reasoning):** expand deepseek-v4 regex to match v4.1+ and add command-code provider (#13430) ([#13528](https://github.com/diegosouzapw/OmniRoute/pull/13528)) — thanks @KooshaPari +- **fix(providers):** add alwaysReasons metadata for reka-flash-3 ([#13531](https://github.com/diegosouzapw/OmniRoute/pull/13531)) — thanks @KooshaPari +- **fix(playground):** allow Compare columns to scroll independently ([#13532](https://github.com/diegosouzapw/OmniRoute/pull/13532)) — thanks @KooshaPari +- **fix(security):** empty whitelist allows through to prevent admin lockout ([#13534](https://github.com/diegosouzapw/OmniRoute/pull/13534)) — thanks @KooshaPari +- **fix(tests):** add DISABLE_IOREG_STRATEGY env var for macOS test compatibility (fixes #13467) ([#13539](https://github.com/diegosouzapw/OmniRoute/pull/13539)) — thanks @KooshaPari +- **fix(zed-hosted):** map developer role and Gemini enums for Zed proxy ([#13543](https://github.com/diegosouzapw/OmniRoute/pull/13543)) — thanks @KooshaPari +- **fix(analytics):** normalize provider aliases to canonical id in usage_history ([#13545](https://github.com/diegosouzapw/OmniRoute/pull/13545)) — thanks @KooshaPari +- **fix(combo):** use traceId for call log id to prevent UNIQUE constraint failure ([#13546](https://github.com/diegosouzapw/OmniRoute/pull/13546)) — thanks @KooshaPari +- **fix(auth):** check model lockout before returning synthetic noauth connection ([#13547](https://github.com/diegosouzapw/OmniRoute/pull/13547)) — thanks @KooshaPari +- **fix(guardrails):** skip credential redaction for base64 image data URLs ([#13550](https://github.com/diegosouzapw/OmniRoute/pull/13550)) — thanks @KooshaPari +- **fix(resilience):** increase requestQueue.maxWaitMs default from 15s to 30s ([#13553](https://github.com/diegosouzapw/OmniRoute/pull/13553)) — thanks @KooshaPari +- **fix(docs):** document OMNIROUTE_READY_TIMEOUT_MS and allowlist the test-only DISABLE_IOREG_STRATEGY ([#13692](https://github.com/diegosouzapw/OmniRoute/pull/13692)) + ### 📝 Maintenance ---- +- **docs(readme):** reconcile live v3.8.50 provider, free-tier, CLI, routing, test, community, sponsor, acknowledgment, and SVG metrics with their audited source denominators, including a deduplicated OmniRoute-in-Action snapshot and distinct contributor rankings for merged pull requests, GitHub-attributed commits, and Git history ([#11356](https://github.com/diegosouzapw/OmniRoute/pull/11356)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **docs:** sync the canonical provider count 354 → 356 across `README.md`, `AGENTS.md`, `llm.txt` (+ 42 i18n mirrors), the four README SVG diagrams, `docs/reference/PROVIDER_REFERENCE.md` (regenerated) and the `package.json` description after Opper (#11629) and 1min.ai (#11631) boarded the catalog — closes the `check:docs-counts-sync` strict drifts that kept `release/v3.8.51` red ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11673](https://github.com/diegosouzapw/OmniRoute/pull/11673)) — thanks @jonlwheat2-gif +- **test(providers):** update count-derived assertions after the v3.8.51 provider additions — `APIKEY_PROVIDERS` 233 → 235 (Opper #11629 + 1min.ai #11631), reserved-prefix REGISTRY walk 395 → 398, `WEB_FETCH_PROVIDERS` now includes `nimble-search` (#11620), and the provider translate-path golden snapshot regenerated ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11674](https://github.com/diegosouzapw/OmniRoute/pull/11674)) — thanks @jonlwheat2-gif +- **test(sse):** bump the hard-lease connection-query inventory for `src/lib/tokenHealthCheck.ts` to 2 — the verify-only web-cookie sweep added by #11495 split the single `getProviderConnections` call into oauth + cookie variants, which the frozen inventory had not tracked ([#11495](https://github.com/diegosouzapw/OmniRoute/pull/11495)). ([#11677](https://github.com/diegosouzapw/OmniRoute/pull/11677)) — thanks @jonlwheat2-gif / @oyi77 +- **chore(release):** clear the `release/v3.8.51` base-red gates — docs migration count (+38 i18n mirrors), stryker `tap.testFiles`, lockfile registry host, stale ESLint suppressions, the 7 lint regressions they were hiding, the stale `APIKEY_PROVIDERS` partition count (231 → 233), the `glm-5.3-max` tier missing from the GLM test table, a dead `modelType === "chat"` comparison in the models catalog, and the `resolveLiveWsUrl` / `sanitizeLiveWsPort` helpers from #11388 that a later merge dropped from `wsPath.ts` (dashboard live WebSocket URL resolution) ([#11502](https://github.com/diegosouzapw/OmniRoute/pull/11502)) — thanks @MumuTW +- **chore(free-models):** the free-tier regime table now declares which totals figure each regime feeds and whether it may take the no-auth shortcut, so the sets that used to repeat those answers by hand are derived from it — a new `freeType` no longer compiles until it has answered every question, instead of silently contributing to no total ([#11537](https://github.com/diegosouzapw/OmniRoute/pull/11537)) — thanks @maxmad64bis +- **docs(routing):** LKGP strategy copy now states provider-level stickiness and the explicit rules fallback instead of promising per-target pinning ([#11555](https://github.com/diegosouzapw/OmniRoute/pull/11555)) — thanks @maxmad64bis +- **test(chatcore):** pin the response tool-name alias resolution against the ordering hazard that broke every Gemini/Antigravity MCP tool call in v3.8.49. `extractRequestToolIdentityMap` deletes `translatedBody._toolNameMap`, so the later read is always undefined and the ledger survives only through the `requestToolIdentityMap` fallback — removing that fallback previously left the entire tool-name suite green. The resolution moves into `resolveResponseToolNameMap()` next to the map it depends on, with a regression guard that fails without the recovery. No behaviour change. ([#11575](https://github.com/diegosouzapw/OmniRoute/pull/11575)) — thanks @amartinawi +- **docs(env):** document `OMNIROUTE_CORPUS_CACHE_SIZE` in `.env.example` and `ENVIRONMENT.md`, so the env/docs contract check stops failing Docs Gates on every branch ([#11576](https://github.com/diegosouzapw/OmniRoute/pull/11576)) — thanks @ntdatt812 +- **chore(stryker):** register `route-guard-tunnel-processes-local-only` and `universal-quota-aware-routing` in `tap.testFiles`, so their mutant kills count for the three modules they cover ([#11577](https://github.com/diegosouzapw/OmniRoute/pull/11577)) — thanks @ntdatt812 +- **test(authz):** pin `LOCAL_ONLY_API_GET_EXEMPTIONS` by exact membership instead of by entry count, so the guard names the offending path and also catches a substitution ([#11580](https://github.com/diegosouzapw/OmniRoute/pull/11580)) — thanks @ntdatt812 +- **test(build):** pin `resolveNextBuildBundlerFlag` to the `OMNIROUTE_USE_TURBOPACK` contract it implements, across runtimes, instead of asserting an unimplemented Bun override ([#11583](https://github.com/diegosouzapw/OmniRoute/pull/11583)) — thanks @ntdatt812 +- **test(claude-web):** the Claude Web transport test's barrel anchors no longer pin the exact construction spelling. #11421 made the executor map lazy, so `/"claude-web": new ClaudeWebExecutor\(\)/` stopped matching and the test went red on `release/v3.8.51` itself — a base-red gate every open PR inherited. The anchors now match the alias key and the class name and tolerate either spelling in between, and were checked to still fail when an alias is genuinely repointed at another executor ([#11585](https://github.com/diegosouzapw/OmniRoute/pull/11585)) — thanks @ntdat812 +- **test(models):** pin `qwen3.8-max` resolution per provider — two of the four catalogs now serve the bare id and no longer carry `-preview` ([#11590](https://github.com/diegosouzapw/OmniRoute/pull/11590)) — thanks @ntdatt812 +- **test(executors):** drop the stale 128-tool truncation assertion that contradicted the #11444 pass-through contract ([#11591](https://github.com/diegosouzapw/OmniRoute/pull/11591)) — thanks @ntdatt812 +- **test(token-health):** pin the current terminal-skip boundary — expired with retry budget is probed, `account_deactivated` stays skipped ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)) — thanks @ntdatt812 +- **test(models):** follow #11503's retarget of the `gemini-3-pro-high` alias to the hyphenated catalog id ([#11593](https://github.com/diegosouzapw/OmniRoute/pull/11593)) — thanks @ntdatt812 +- **refactor(video bridge):** extract per-video acquisition, whole-result caching, description, and metrics/abort/cleanup out of `VideoBridgeGuardrail.preCall` into a `processVideoPart` seam in a new `videoBridgePipeline.ts`, behind explicit `VideoMediaBrokerPort`, `VideoAudioTranscriptionPort`, and `VideoDrilldownPort` boundaries; `preCall` now only handles request traversal, policy, and response aggregation. The Video tab's FFmpeg/ffprobe runtime status is now an explicit `unknown` / `restricted` / `unavailable` / `available` state instead of a nullable boolean pair, fixing a case where an in-flight or failed probe was mislabeled as "install FFmpeg" ([#11657](https://github.com/diegosouzapw/OmniRoute/issues/11657)). ([#12016](https://github.com/diegosouzapw/OmniRoute/pull/12016)) +- **test(opencode-plugin):** add unit test coverage for `formatFreeBudget()` naming helper ([#11660](https://github.com/diegosouzapw/OmniRoute/pull/11660)) — thanks @f9td56dbgh-hub +- **docs(video):** clarify that the Video Bridge transcript `source` field (`client`, `embedded`, `audio-bridge`) is presently caller-declared and not yet server-verified — OmniRoute enforces the enum shape but does not cryptographically confirm that an `embedded`/`audio-bridge` label came from a server-owned extraction ([#11661](https://github.com/diegosouzapw/OmniRoute/issues/11661)). ([#12001](https://github.com/diegosouzapw/OmniRoute/pull/12001)) +- **chore(providers):** retire the distributed Raycast Relay and Hailuo Web integrations after a provenance audit confirmed substantial derivation from GPL-3.0 sources; the official GitHub and MiniMax API providers remain available. Migration 163 disables legacy connections and invalidates their active leases while preserving connection IDs, audit/history rows, quota snapshots, usage records, and fail-closed API-key connection policies. ([#11691](https://github.com/diegosouzapw/OmniRoute/pull/11691)) +- **chore(providers):** retire the Felo Web provider while its GPL-derived provenance remains on licensing HOLD ([#11698](https://github.com/diegosouzapw/OmniRoute/pull/11698)) — stale runtime and database references now fail closed while existing connection, lease, and usage history remains preserved for auditability. +- **fix(sse):** retire the Gemini Web image-generation bridge while provenance and licensing remain on HOLD, preserving Gemini Web chat/auth/tools and legitimate Gemini/Nano Banana image providers ([#11708](https://github.com/diegosouzapw/OmniRoute/pull/11708)) +- **chore(audio):** retire the built-in EdgeTTS provider and its 11-voice catalog while provenance/licensing review remains on HOLD; the generic `/v1/audio/speech` endpoint and the other speech providers remain available ([#11711](https://github.com/diegosouzapw/OmniRoute/pull/11711)) +- **fix(providers):** retire the unofficial Qwen Web integration and its `qwen-web`/`qw` identities while third-party provenance remains on HOLD ([#11713](https://github.com/diegosouzapw/OmniRoute/pull/11713)) — direct requests now fail closed with a sanitized HTTP 410 and combos skip the retired target, while migration guards preserve connection IDs, allowlists, usage/audit history and invalidate active leases. Supported Qwen API, Qwen Code, cloud, token-plan, audio, local and model-name surfaces remain available; restoring Qwen Web requires a qualified provenance/license decision. +- **fix(providers):** retire `microsoft-designer-web` and its `msdesigner` alias from runtime and catalogs while preserving stored credentials, provider history and allowlists as inactive records — source dependency on gpt4free's [`MicrosoftDesigner.py`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/g4f/Provider/needs_auth/MicrosoftDesigner.py) is proven at snapshot `0943d16eef757a44deb60651c07bbe5fa958cafe`, whose [`LICENSE`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/LICENSE) is GPL-3.0; expressive adaptation is probable, which records provenance/licensing risk without asserting a legal conclusion ([#11720](https://github.com/diegosouzapw/OmniRoute/pull/11720)). +- **docs(legal):** centralize pinned MIT notices for the vendored GCF codec and local flag assets ([#11726](https://github.com/diegosouzapw/OmniRoute/pull/11726)) +- Split the 2,952-line `adobeFireflyClient.ts` into nine focused modules (catalog, credentials, payload, ARP session, upload, responses, IMS, account, poll) behind an unchanged barrel — no consumer edits and no public API change (#11728). — thanks @MumuTW +- **fix(dashboard):** replaced the Model Lockout card's unprovenanced notification MP3 with a short, locally synthesized Web Audio chime ([#11731](https://github.com/diegosouzapw/OmniRoute/pull/11731)) — both toggles retain optional audible feedback without shipping replacement media or third-party code, and unsupported, suspended, or rejected audio contexts fail open without blocking the setting change. +- **chore(deepseek-web):** remove the provenance-unresolved DeepSeek PoW WASM binary and its runtime loader/tracing while retaining the existing JavaScript solver (slower at high difficulty) ([#11732](https://github.com/diegosouzapw/OmniRoute/pull/11732)). +- **chore(assets):** remove four unused provider assets whose redistribution provenance could not be verified while retaining the active Freebuff SVG ([#11735](https://github.com/diegosouzapw/OmniRoute/pull/11735)) +- **docs(assets):** retired two raster images whose redistribution provenance could not be verified ([#11736](https://github.com/diegosouzapw/OmniRoute/pull/11736)) — PicoClaw documentation now reuses the existing SVG, while Zoo Code remains a text link without an image. +- **docs(legal):** add pinned notices for six LobeHub-derived provider SVGs and 65 byte-exact theSVG assets, retaining registry claims as unverified metadata with explicit trademark and HOLD boundaries ([#11737](https://github.com/diegosouzapw/OmniRoute/pull/11737)). +- **chore(quality):** added a versioned 225/225 provider-asset provenance manifest and blocking drift gate ([#11743](https://github.com/diegosouzapw/OmniRoute/pull/11743)) — the inventory pins magic MIME and SHA-256 for every physical file, records immutable source matches for 71 assets, keeps 69 probable and 85 unresolved entries explicit but non-blocking, and treats provenance as source evidence rather than copyright or trademark clearance. +- **refactor(mitm):** replace four previously derived Traffic Inspector utilities — conversation normalization, SSE merging, secret masking, and header sanitization — with independent clean-room implementations based on public provider schemas, WHATWG SSE, and RFC 6750, while keeping the two independently classified passthrough surfaces unchanged ([#11748](https://github.com/diegosouzapw/OmniRoute/pull/11748)). +- Added a unit test that fails seven days before any dated pack under `config/` (`validUntil` and sibling keys) lapses, naming the file and key. The Alibaba free-tier pack expired on 2026-08-27 and turned every PR red the next morning with no commit involved; renewal now happens on someone's terms, not the clock's. ([#11891](https://github.com/diegosouzapw/OmniRoute/pull/11891)) +- `check:workflows` now fails (under `--strict`/`--ratchet`) when any job routed to a self-hosted runner publishes with `--provenance` — npm rejects that with `422` at the registry, which in v3.8.50 only surfaced after the tag and Docker images were public. ([#11895](https://github.com/diegosouzapw/OmniRoute/pull/11895)) +- `scripts/ops/runner-janitor.sh` now proves a path is idle with one `lsof` snapshot and removes stale leftovers itself (tmpfs after 3 h — it is RAM — disk after 24 h), kills orphan `next-build` processes, prunes checkouts of stopped runners, and alerts on memory pressure; `--dry-run` shows exactly what it would do. `docs/ops/RUNNER_BOX.md` reconciled to the measured box (31 GB, 10 listeners). ([#11893](https://github.com/diegosouzapw/OmniRoute/pull/11893)) +- The `next-build` artefact (1.3 GB) is now written and read under `$RUNNER_TEMP` (per-runner, on disk) instead of `/tmp`, which on the self-hosted pool is a 12 GB tmpfs in RAM. Landing it there took 27–32 of the publish job's 76 minutes, and the fixed `/tmp/e2e-build.tar.gz` name let E2E jobs on different runners overwrite each other's download. ([#11896](https://github.com/diegosouzapw/OmniRoute/pull/11896)) +- The CI `build` job now runs in two concurrency lanes — `main` and pull requests — so a release build is never queued behind (or OOM-killed beside) PR builds on the self-hosted pool, which holds one `next-build` comfortably and two at the edge. ([#11901](https://github.com/diegosouzapw/OmniRoute/pull/11901)) +- Re-freeze the ESLint suppressions on `release/v3.8.51` from a clean-room measurement (2 stale file entries pruned, 55 pre-existing `no-explicit-any` in six new files frozen under #11924) and drop the dead `GPT_SIZE_MAP` constant orphaned by the Adobe Firefly client split, so `No new ESLint warnings` stops failing every PR with exit 2 (Refs #11924) ([#11955](https://github.com/diegosouzapw/OmniRoute/pull/11955)) +- Type the 55 `no-explicit-any` sites that had been frozen under #11924 — four redundant casts in `socksConnectorWithFamily.ts` (undici/socks types already accept them) and the mocks/fixtures of the socks-timeout and isFree suites — and drop their suppression entries; the ESLint ratchet shrinks from 5487 to 5432 (Closes #11924) ([#11975](https://github.com/diegosouzapw/OmniRoute/pull/11975)) +- Take the two hosted-runner builds off the PR rail: `Build App` (`build.yml`) is `workflow_dispatch`-only and `DAST smoke (PR)` runs only for PRs into `main` — the 7 GB hosted VM cannot build `release/v3.8.51` in any profile (19/30 red, VM shutdown ~8 min into `next build`) and both checks had turned into permanent noise on every release PR; the bundle stays validated by `ci.yml` on `main` and by `nightly-release-green` on `release/**` (Closes #11946) ([#11962](https://github.com/diegosouzapw/OmniRoute/pull/11962)) +- Move the four nightly jobs that build the backend (`nightly-schemathesis`, `nightly-llm-security` promptfoo + garak, `nightly-resilience` axe-a11y) off the hosted 7 GB runner — where they died on `release/v3.8.51` unseen — onto the box's new `omni-light` pool (two listeners), and document the reshaped fleet (4 active OmniRoute listeners: 2 `omni-build` + 2 `omni-light`, janitor ceiling 4) (Closes #11965) ([#11967](https://github.com/diegosouzapw/OmniRoute/pull/11967)) +- Stop painting every fork PR into `release/**` red: `quality.yml` `Build (advisory)` is skipped (GitHub still reports `continue-on-error` failures as check FAILURE). Hosted `ubuntu-latest` cannot finish `npm run build` on this tree — same class as #11962 taking `build.yml` off the PR rail. `docker-publish.yml` amd64 now runs on the `.113` `omni-build` pool (31 GB / 32 cores, two listeners) with Turbopack, shares the `heavy-build-main` lane with `ci.yml` `Build` so it queues instead of becoming a third heavy, and keeps per-ref concurrency (a merge storm was starting 8 concurrent OOM builds). arm64 stays on `ubuntu-24.04-arm` with webpack — there is no ARM box. Fallback when `USE_VPS_RUNNER` is off: hosted amd64 + webpack (#11976). ([#12021](https://github.com/diegosouzapw/OmniRoute/pull/12021)) +- `docker-publish.yml` builds with webpack (`OMNIROUTE_USE_TURBOPACK=0`) on **both** arches, including amd64 on the `.113` `omni-build` pool. Turbopack had the RAM (31 GB, `omniroute-113-6`) and still panicked (`TurbopackInternalError: there must be a path to a root`, run 33253576569, 26 min in). The same tree's arm64 webpack build on hosted `ubuntu-24.04-arm` succeeded (run 33264823398). Dockerfile already documents webpack as the Docker escape hatch (#11976). ([#12050](https://github.com/diegosouzapw/OmniRoute/pull/12050)) +- **docs(free-tier):** declare the counting vs deciding regimes for "is it free?" and guard the deciding path from DB-backed catalog resolution ([#12226](https://github.com/diegosouzapw/OmniRoute/pull/12226)) — thanks @maxmad64bis +- **docs(auto-combo):** The mode pack table in `docs/routing/AUTO-COMBO.md` now lists all six shipped packs with every weight each one sets, replacing a four-pack table whose numbers had also drifted from the source. It states plainly that no pack sets `quality`, so selecting any pack silences the observed-quality signal. Six more documents that quote the scoring factor count joined the `check:docs-counts` gate, which caught five stale claims — including one naming nine factors that do not exist — and two stale mode pack counts. The dashboard routing panel, which offered four of the six packs and labelled the default strategy "6-Factor Scoring", is now covered by a test; the two packs it was missing are `reliability-first` and `chaos-mode`, the latter labelled as the fault-injection profile it is rather than as one more routing preference ([#12316](https://github.com/diegosouzapw/OmniRoute/pull/12316)) — thanks @maxmad64bis +- **chore(quality):** rebaseline `src/lib/db/apiKeys.ts` for the ACL the key-creation path now preserves ([#12352](https://github.com/diegosouzapw/OmniRoute/pull/12352)) ([#12673](https://github.com/diegosouzapw/OmniRoute/pull/12673)) — thanks @KrzysiekSko +- **chore(providers):** bump the Claude Code wire identity and the Devin bridge image pin from `2.1.220` to `2.1.258` ([#12402](https://github.com/diegosouzapw/OmniRoute/pull/12402)) — thanks @ggiak ([#12604](https://github.com/diegosouzapw/OmniRoute/pull/12604)) +- **docs(env):** align `.env.example`, the README Bun section, and the troubleshooting guide with the code: `OMNIROUTE_USE_TURBOPACK` also governs `npm run build` (not dev-only), `bun run build` follows that flag instead of auto-selecting Webpack, `OMNIROUTE_CHAT_MAX_HEAVY_IN_FLIGHT` is unset by default (no request-count cap), and the structural `503 chat_admission_busy` message matches `chatAdmissionResponses.ts` (#12404 — thanks @pacocartones) +- **chore(electron):** upgrade the desktop app to Electron 44 (Chromium 152, Node 24.18.1) ([#12217](https://github.com/diegosouzapw/OmniRoute/pull/12217)). **Requires macOS 13 (Ventura) or later** — Chromium dropped macOS 12 (Monterey), so Monterey users must stay on an earlier OmniRoute desktop build. Windows and Linux are unaffected; the app already shipped only x64/arm64, so Electron 44 dropping 32-bit builds changes nothing. Removes the `openAsHidden`/`wasOpenedAsHidden` login-item fields deleted in Electron 44 — hidden autostart continues to work through the `--hidden` argument registered with the login item ([#12554](https://github.com/diegosouzapw/OmniRoute/pull/12554)) — thanks @app/dependabot +- **chore(quality):** rebaseline `src/sse/handlers/chat.ts` for the effective-input persistence the continuation fix needs ([#12641](https://github.com/diegosouzapw/OmniRoute/pull/12641)) ([#12680](https://github.com/diegosouzapw/OmniRoute/pull/12680)) — thanks @hartmark +- `Coverage` job on `ci.yml`: the informational Codecov upload gets its own 5-minute ceiling and `continue-on-error`, and the job budget grows from 20 to 30 minutes (the 8-shard c8 merge alone takes ~10) — a stalled upload no longer ends the job `cancelled` and drags a fully green `main` run's conclusion down with it ([#11972](https://github.com/diegosouzapw/OmniRoute/pull/11972)) +- CI hardening on the PR rail: the four `Unit Tests fast-path` shards get `timeout-minutes: 30` (a hung shard held a PR for 64 min instead of GitHub's 6 h default) and both ESLint file caches lose their `restore-keys` fallback, so a cache built under another suppressions file or lint config can no longer report stale verdicts (Refs #11600, #11924) ([#11963](https://github.com/diegosouzapw/OmniRoute/pull/11963)) +- Every CI job that runs a `next build` (`build`, the npm `publish`, both release-green validations) now targets the `omni-build` runner label, which only two of the eight self-hosted runners carry. The box holds one build comfortably and two at the edge; a third now queues on GitHub instead of being OOM-killed by the kernel. ([#11932](https://github.com/diegosouzapw/OmniRoute/pull/11932)) +- **refactor(combo):** move `handleRoundRobinCombo` (and `resolveTargetTokenLimit`) into `open-sse/services/combo/roundRobinCombo.ts`. `combo.ts` drops from 2164 to 1014 lines (`split("\n").length`); the leaf is 1199 (under the 1200 new-file cap). The round-robin call site uses a dynamic `import()` so `releaseStickyPinOnFailure` / `clearStaleLKGP` can stay exported from `combo.ts` without a static cycle. Skip / sticky / semaphore / safety-timer behavior is unchanged. ([#12811](https://github.com/diegosouzapw/OmniRoute/pull/12811)) — thanks @HouMinXi +- **chore(quality):** rebaseline the file-size caps the error-boundary campaign grew past (`open-sse/executors/codex.ts`, `open-sse/vendor/codex-chatgpt-web/bridge.ts`, both via [#12444](https://github.com/diegosouzapw/OmniRoute/pull/12444)) ([#12654](https://github.com/diegosouzapw/OmniRoute/pull/12654)) +- **chore(quality):** rebaseline the file-size caps the hartmark batch grew past (`combos/page.tsx` via [#12355](https://github.com/diegosouzapw/OmniRoute/pull/12355), `open-sse/services/combo.ts` via [#12338](https://github.com/diegosouzapw/OmniRoute/pull/12338)) ([#12623](https://github.com/diegosouzapw/OmniRoute/pull/12623)) — thanks @hartmark +- **chore(quality):** rebaseline the file-size caps the HouMinXi batch grew past when its PRs stacked (`providers/page.tsx`, `chatCore.ts`, `accountFallback.ts`) — each PR measured correctly in isolation, none saw the stacking ([#12619](https://github.com/diegosouzapw/OmniRoute/pull/12619)) +- **chore(quality):** rebaseline `open-sse/services/combo.ts` for the reset-aware scoring the HouMinXi batch stacked ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) ([#12678](https://github.com/diegosouzapw/OmniRoute/pull/12678)) — thanks @HouMinXi +- **docs:** sync provider catalog count (direct commit `4f11b2ae3d`) +- **docs(skills):** sync omni-inference search fallback (direct commit `b4e76a7ed9`) +- **chore(lint):** prune two stale base suppressions (direct commit `1325047d56`) +- **test(auto-combo):** tolerate full-suite rotation load (direct commit `ad3e293f9f`) +- **test:** drain inherited quality snapshot reds (direct commit `21ed68d8ac`) +- **chore(quality):** rebaseline measured CLI bundle growth (direct commit `d35b3f9779`) +- **test(ui):** reproduce invalid AnySearch icon alias (direct commit `12cc6ca834`) +- **test(translator):** fix the relative imports of the relocated deferred-finish test (direct commit `777d9d1629`) +- **docs(counts):** bring the documented provider and migration counts to the measured values (direct commit `e70bea30e9`) +- **test(security):** drop the guard-target set left unused by the sync (direct commit `a32ba0e362`) +- **chore(db):** renumber model_capabilities migration 163 → 169 (cross-branch collision with 163_radar_feed_cache_generated_at) (direct commit `70f5d4cbf9`) +- **docs(cli):** document CLI_PRIME_AGENT_BIN and correct the CLI Agents count (direct commit `9058e39b61`) +- **chore(deps):** refresh runtimes and adopt ESLint 10 ([#11259](https://github.com/diegosouzapw/OmniRoute/pull/11259)) — thanks @backryun +- **refactor(local-corpus):** implement dynamic root resolution and LRU cache ([#11491](https://github.com/diegosouzapw/OmniRoute/pull/11491)) — thanks @raheemuddin786 +- **chore(quality):** prune stale eslint-suppressions entries ([#11596](https://github.com/diegosouzapw/OmniRoute/pull/11596)) +- **chore(ci):** unblock quality gates - env-doc sync + stryker tap.testFiles registration (#9282) ([#11609](https://github.com/diegosouzapw/OmniRoute/pull/11609)) — thanks @Meet6338-X +- **chore(ci):** point the circular-deps gate at dpdm's real JS entrypoint ([#11615](https://github.com/diegosouzapw/OmniRoute/pull/11615)) — thanks @benzntech +- **chore(quality):** rebaseline file-size for #11448/#11495/#11561 growth ([#11630](https://github.com/diegosouzapw/OmniRoute/pull/11630)) +- **deps:** realign onnxruntime-node pin to @huggingface/transformers' 1.24.3 lockstep ([#11633](https://github.com/diegosouzapw/OmniRoute/pull/11633)) — thanks @jonlwheat2-gif +- **test(autoCombo):** port models_dev_tier #11508 guard from node:test to vitest ([#11635](https://github.com/diegosouzapw/OmniRoute/pull/11635)) — thanks @jonlwheat2-gif +- **chore(quality):** rebaseline file-size for #11083/#11631/#11628 growth ([#11636](https://github.com/diegosouzapw/OmniRoute/pull/11636)) +- **chore(lint):** prune stale ESLint suppressions (base-red #11449) ([#11644](https://github.com/diegosouzapw/OmniRoute/pull/11644)) — thanks @jonlwheat2-gif +- **docs(changelog):** aggregate the ten v3.8.50 fragments into CHANGELOG.md ([#11683](https://github.com/diegosouzapw/OmniRoute/pull/11683)) +- **chore(search):** drop unreachable credential sweep after #11642 ([#11687](https://github.com/diegosouzapw/OmniRoute/pull/11687)) — thanks @HouMinXi +- **ci:** add API route TypeScript regression gate ([#11705](https://github.com/diegosouzapw/OmniRoute/pull/11705)) — thanks @Sabeekhann +- **docs(dedup):** correct the CodeQL claim on the tenant-namespace comment ([#11712](https://github.com/diegosouzapw/OmniRoute/pull/11712)) +- **docs(changelog):** add consolidated v3.8.50 stats, top-25 ranking and a .mailmap ([#11715](https://github.com/diegosouzapw/OmniRoute/pull/11715)) +- **docs:** recommend gstack for AI-assisted workflows ([#11770](https://github.com/diegosouzapw/OmniRoute/pull/11770)) — thanks @santosraju99-hub +- **chore(quality):** tighten complexity/cognitive-complexity ratchets to the current tip; land the missed gateways.ts rebaseline ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **docs(i18n):** finish Freepik → Magnific rebrand in locale strings and README ([#11772](https://github.com/diegosouzapw/OmniRoute/pull/11772)) — thanks @turbolego +- **refactor(auto-combo):** fix divergent scoring in combo health reporting ([#11854](https://github.com/diegosouzapw/OmniRoute/pull/11854)) — thanks @jacobsparts +- **chore(changelog):** add missing fragments for #11919/#11918/#11916 ([#11938](https://github.com/diegosouzapw/OmniRoute/pull/11938)) +- **test(check):** escape the runs-on fixture with JSON.stringify, not a quote-only replace ([#11942](https://github.com/diegosouzapw/OmniRoute/pull/11942)) (direct commit `fb7445eaa3`) +- **test(release):** align five suites with the contracts #11933, #11919 and #11876 shipped on release/v3.8.51 ([#11944](https://github.com/diegosouzapw/OmniRoute/pull/11944)) +- **chore(quality):** remove unreachable code and restore test discovery ([#11950](https://github.com/diegosouzapw/OmniRoute/pull/11950)) — thanks @backryun +- **docs(agents):** sync-back landings are fast-forward, never squash ([#11964](https://github.com/diegosouzapw/OmniRoute/pull/11964)) +- **test(infra):** retry recursive temp-dir removal instead of failing a shard on ENOTEMPTY (#11966) ([#11968](https://github.com/diegosouzapw/OmniRoute/pull/11968)) +- **docs(ops):** the .113 heavy-build ceiling is one runner, not two ([#12048](https://github.com/diegosouzapw/OmniRoute/pull/12048)) +- **test(ui):** correct inactive auto-fetch expectation ([#12098](https://github.com/diegosouzapw/OmniRoute/pull/12098)) — thanks @RaviTharuma +- **docs:** document native dependency check escape hatch ([#12101](https://github.com/diegosouzapw/OmniRoute/pull/12101)) — thanks @RaviTharuma +- **test(providers):** regenerate the translate-path golden for OrcaRouter (#11923) ([#12118](https://github.com/diegosouzapw/OmniRoute/pull/12118)) +- **chore(quality):** velocity phase — loosen every numeric baseline by 20% until v4.0, monitor headroom nightly ([#12125](https://github.com/diegosouzapw/OmniRoute/pull/12125)) +- **chore(lint):** batch 0 of #12146 — type the call-log-cap sqlite rows instead of 45 as-any casts ([#12157](https://github.com/diegosouzapw/OmniRoute/pull/12157)) +- **chore(lint):** batch 4 of #12146 — shared/components react-hooks violations resolved ([#12159](https://github.com/diegosouzapw/OmniRoute/pull/12159)) +- **chore(lint):** batch 1 of #12146 — dashboard/cli-code react-hooks violations resolved ([#12160](https://github.com/diegosouzapw/OmniRoute/pull/12160)) +- **chore(lint):** batch 3 of #12146 — dashboard/settings react-hooks violations resolved ([#12162](https://github.com/diegosouzapw/OmniRoute/pull/12162)) +- **chore(lint):** batch 2 of #12146 — resolve the react-hooks compiler violations in dashboard/providers ([#12163](https://github.com/diegosouzapw/OmniRoute/pull/12163)) +- **chore(quality):** register search-432 cooldown test in stryker tap.testFiles ([#12170](https://github.com/diegosouzapw/OmniRoute/pull/12170)) +- **chore(lint):** batch 5 of #12146 — combos, endpoint, provider-stats, api-manager and costs react-hooks violations resolved ([#12174](https://github.com/diegosouzapw/OmniRoute/pull/12174)) +- **build:** add contributor fast profile ([#12192](https://github.com/diegosouzapw/OmniRoute/pull/12192)) — thanks @rafacpti23 +- **docs(readme):** README touch-ups ([#12193](https://github.com/diegosouzapw/OmniRoute/pull/12193)) ([#12194](https://github.com/diegosouzapw/OmniRoute/pull/12194)) ([#12202](https://github.com/diegosouzapw/OmniRoute/pull/12202)) — thanks @rafacpti23 +- **build:** force the webpack bundler in the contributor build profile ([#12197](https://github.com/diegosouzapw/OmniRoute/pull/12197)) — thanks @rafacpti23 +- **build:** skip standalone packaging in the contributor profile ([#12198](https://github.com/diegosouzapw/OmniRoute/pull/12198)) — thanks @rafacpti23 +- **docs(audit):** align every published number with the code and harden check:docs-counts ([#12200](https://github.com/diegosouzapw/OmniRoute/pull/12200)) +- **build:** stub the instrumentation entrypoints in the contributor profile ([#12203](https://github.com/diegosouzapw/OmniRoute/pull/12203)) — thanks @rafacpti23 +- **build:** omit the standalone output target for contributor builds ([#12204](https://github.com/diegosouzapw/OmniRoute/pull/12204)) — thanks @rafacpti23 +- **chore(lint):** batch 6 of #12146 — memory, radar, audit, analytics, cache, usage, activity, home and RequestLoggerV2 react-hooks violations resolved ([#12208](https://github.com/diegosouzapw/OmniRoute/pull/12208)) +- **docs(diagrams):** rename number-carrying diagram files to stable names ([#12210](https://github.com/diegosouzapw/OmniRoute/pull/12210)) +- **docs(dashboard):** redraw onboarding tier-flow SVGs for the real 4-tier model ([#12211](https://github.com/diegosouzapw/OmniRoute/pull/12211)) +- **docs(api):** document every implemented route in openapi.yaml (276 → 692 paths) ([#12212](https://github.com/diegosouzapw/OmniRoute/pull/12212)) +- **chore(lint):** batch 7 of #12146 — final src tail: 37 react-hooks violations across 33 files resolved ([#12244](https://github.com/diegosouzapw/OmniRoute/pull/12244)) +- Revert "docs: recommend gstack for AI-assisted workflows (#11770)" ([#12248](https://github.com/diegosouzapw/OmniRoute/pull/12248)) ([#12249](https://github.com/diegosouzapw/OmniRoute/pull/12249)) +- **docs:** Chaos Mode setup guide + weighted strategy semantics ([#12250](https://github.com/diegosouzapw/OmniRoute/pull/12250)) +- **docs(agents):** protected-surface merge rule — operator approval for agent-instruction files ([#12253](https://github.com/diegosouzapw/OmniRoute/pull/12253)) +- **chore(quality):** register native-codex turn-pin tests in stryker tap.testFiles ([#12263](https://github.com/diegosouzapw/OmniRoute/pull/12263)) +- **chore(quality):** dedupe tap.testFiles entries added by racing base-red fixes ([#12265](https://github.com/diegosouzapw/OmniRoute/pull/12265)) +- **docs(readme):** introduce OmniRouteTray — the macOS menu-bar companion ([#12276](https://github.com/diegosouzapw/OmniRoute/pull/12276)) — thanks @ggiak +- **chore(proxy):** purge the legacy 1proxy residue — sync/rotator modules, dead DB exports, dead settings tab and flag, docs (#12091) ([#12290](https://github.com/diegosouzapw/OmniRoute/pull/12290)) +- **chore(quality):** baseline-headroom skips generated and vendored files in the fileSize worst-file signal ([#12291](https://github.com/diegosouzapw/OmniRoute/pull/12291)) +- **refactor(video):** unify the JPEG frame data-URI contract ([#12322](https://github.com/diegosouzapw/OmniRoute/pull/12322)) +- **chore(deps):** freeze onnxruntime-node and eslint-plugin-react-hooks out of dependabot groups ([#12329](https://github.com/diegosouzapw/OmniRoute/pull/12329)) +- **test(executors):** restore the #10986 reasoning-only fallback guards ([#12364](https://github.com/diegosouzapw/OmniRoute/pull/12364)) — thanks @pacocartones +- **chore(quality):** re-tighten the file-size ratchet to the real LOC (plan 3.8.52 task 0) ([#12411](https://github.com/diegosouzapw/OmniRoute/pull/12411)) +- **test(grok):** format web executor suite ([#12412](https://github.com/diegosouzapw/OmniRoute/pull/12412)) +- **chore(quality):** ratchet open-sse typecheck baseline to zero ([#12418](https://github.com/diegosouzapw/OmniRoute/pull/12418)) +- **test(build):** guard the artifact path policy arrays against duplicates ([#12422](https://github.com/diegosouzapw/OmniRoute/pull/12422)) +- **chore(lint):** adopt eslint-plugin-react-hooks 7.1.1 ([#12428](https://github.com/diegosouzapw/OmniRoute/pull/12428)) +- **chore(providers):** remove a keyless provider integration at its operator's request ([#12440](https://github.com/diegosouzapw/OmniRoute/pull/12440)) +- **docs(providers):** register providers removed at their operator's request and guard against reintroduction ([#12478](https://github.com/diegosouzapw/OmniRoute/pull/12478)) +- **chore(quality):** base fixes — stryker tap.testFiles + node_modules cache key ([#12482](https://github.com/diegosouzapw/OmniRoute/pull/12482)) +- **docs(arch):** one-process recipe for tens of long /v1/responses ([#12493](https://github.com/diegosouzapw/OmniRoute/pull/12493)) — thanks @RaviTharuma +- **chore(quality):** tighten the CodeQL ratchet baseline from 11 to 6 ([#12530](https://github.com/diegosouzapw/OmniRoute/pull/12530)) +- **chore(deps):** pin browserslist override to ^4.28.8 ([#12592](https://github.com/diegosouzapw/OmniRoute/pull/12592)) — thanks @KooshaPari +- **chore(deps):** bump fast-uri to 3.1.7 in the electron lockfile ([#12601](https://github.com/diegosouzapw/OmniRoute/pull/12601)) +- **docs(quality):** document how the CodeQL ratchet refreshes and how to tighten it ([#12611](https://github.com/diegosouzapw/OmniRoute/pull/12611)) +- **test(ci):** pin the openapi-security-tiers two-arm contract with an executing gate test (#12581) ([#12652](https://github.com/diegosouzapw/OmniRoute/pull/12652)) +- **docs(gamification):** align XP Rewards table with code (#12501) ([#12667](https://github.com/diegosouzapw/OmniRoute/pull/12667)) — thanks @KooshaPari +- **refactor(combo):** split executeTarget into gates, attempt, and loop ([#12746](https://github.com/diegosouzapw/OmniRoute/pull/12746)) — thanks @HouMinXi +- **docs(security):** document socket.yml scanner config + CI workflow link (#12575) ([#12764](https://github.com/diegosouzapw/OmniRoute/pull/12764)) — thanks @KooshaPari +- **chore(ci):** guard commit identity in pre-commit to stop author misattribution ([#12772](https://github.com/diegosouzapw/OmniRoute/pull/12772)) +- **deps:** 17 Dependabot bumps — bump electron from 43.4.0 to 43.4.1 in /electron ([#11426](https://github.com/diegosouzapw/OmniRoute/pull/11426)); bump the development group across 1 directory with 8 updates ([#11428](https://github.com/diegosouzapw/OmniRoute/pull/11428)); bump the production group across 1 directory with 11 updates ([#11440](https://github.com/diegosouzapw/OmniRoute/pull/11440)); chore(deps): bump github/codeql-action/init from 4.37.7 to 4.37.8 ([#11925](https://github.com/diegosouzapw/OmniRoute/pull/11925)); chore(deps): bump github/codeql-action from 4.37.7 to 4.37.8 ([#11926](https://github.com/diegosouzapw/OmniRoute/pull/11926)); chore(deps): bump github/codeql-action/analyze from 4.37.7 to 4.37.8 ([#11927](https://github.com/diegosouzapw/OmniRoute/pull/11927)); build(deps): bump github/codeql-action/init from 4.37.8 to 4.37.9 ([#12345](https://github.com/diegosouzapw/OmniRoute/pull/12345)); build(deps): bump github/codeql-action/analyze from 4.37.8 to 4.37.9 ([#12346](https://github.com/diegosouzapw/OmniRoute/pull/12346)); bump the development group across 1 directory with 2 updates ([#12347](https://github.com/diegosouzapw/OmniRoute/pull/12347)); build(deps): bump github/codeql-action from 4.37.8 to 4.37.9 ([#12349](https://github.com/diegosouzapw/OmniRoute/pull/12349)); bump browserslist from 4.28.2 to 4.28.8 ([#12396](https://github.com/diegosouzapw/OmniRoute/pull/12396)); bump the production group across 1 directory with 4 updates ([#12399](https://github.com/diegosouzapw/OmniRoute/pull/12399)); bump @xmldom/xmldom ([#12500](https://github.com/diegosouzapw/OmniRoute/pull/12500)); bump qs from 6.15.2 to 6.16.0 ([#12512](https://github.com/diegosouzapw/OmniRoute/pull/12512)); bump @xmldom/xmldom from 0.9.10 to 0.9.12 ([#12513](https://github.com/diegosouzapw/OmniRoute/pull/12513)); bump fast-uri from 3.1.5 to 3.1.7 ([#12514](https://github.com/diegosouzapw/OmniRoute/pull/12514)); bump @humanfs/node from 0.16.7 to 0.16.8 ([#12515](https://github.com/diegosouzapw/OmniRoute/pull/12515)) +- Clear the `release/v3.8.51` `check:agent-skills-sync` base-red: regenerate `skills/cli-tunnel/SKILL.md` so the `tunnel create [type]` positional that #13009 taught the generator to read is reflected in the committed skill. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` typecheck base-red from #12731: the new `Mode pack ranking gates` candidates in `open-sse/services/autoCombo/__tests__/autoCombo.test.ts` omitted the required `provider`, `model` and `errorRate` fields of `ProviderCandidate`, failing `check:open-sse-typecheck` (and with it `Fast Quality Gates`) on every open PR. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` docs-sync base-red: EURouter (#13025) and GreenPT (#13024) took the live provider count to 358, leaving 7 STRICT drifts (`PROVIDER_REFERENCE.md`, 4 diagrams, 2 tier-flow images) plus the `AGENTS.md` / `llm.txt` / `package.json` count claims stale. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- **test(models):** Pin `CATALOG_BUILD_TIMEOUT_MS` in the #12058 canonical-catalog regression so a cold catalog build racing the 8s default bound no longer flakes the suite with a 500 ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Allow `@omniroute/opencode-plugin-v2/` in the npm pack policy — [#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870) shipped the v2 plugin beside its v1 sibling without widening the allowlist, so every packed file under it read as an unexpected artifact and failed `check:pack-policy` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Point the release-green validator's pack gate at the tree it is validating — the build-provenance guard defaulted to `origin/main`, which a release branch tip can never be an ancestor of mid-cycle, so `Package artifact` reported a false red on every run and the tarball boot-smoke cascaded off it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **docs(checks):** keep doc counts honest — headings, rankings, catalog, weights, quality gate and scoring diagram now covered ([#12507](https://github.com/diegosouzapw/OmniRoute/pull/12507)) — thanks @maxmad64bis +- **chore(lifecycle):** `check:model-lifecycle` now also diffs `DEFAULT_DEGRADATION_MAP` (the background-task redirect table) against the vendor lifecycle snapshot, refusing a retired id as source or target, with a table-driven unit test beside it. Three rows whose source the vendor had retired — `claude-sonnet-4-20250514`, `gemini-3-pro-preview` and `gpt-5.1-codex` (whose target `gpt-5.1-codex-mini` is retired too) — were dead code, since `checkLifecycle` answers 410 before the redirect runs; they are dropped (#12535 — thanks @pacocartones) +- **test(compression):** cover idle worker eviction at the resource level — the pool must call `terminate()` and must not retain the worker's `MessagePort`, complementing the `exit`-event assertion added with the fix ([#13371](https://github.com/diegosouzapw/OmniRoute/pull/13371)) +- **chore(quality):** raise the `imageGeneration.ts` file-size ceiling for the image-only-model guard that clears the #12945 base-red ([#13392](https://github.com/diegosouzapw/OmniRoute/pull/13392)) +- **chore(quality):** type the combo-test route's JSON response bodies instead of casting them to `any`, drop the now-empty suppression entry, and remove the `hasPerModelQuota` import `chatCore.ts` stopped using when the failure-classification helper was extracted ([#13379](https://github.com/diegosouzapw/OmniRoute/pull/13379)) +- **chore(electron):** the desktop bundle no longer ships the root-level files of every translated docs mirror (`README.md`, `llm.txt`, `CLAUDE.md`, `GEMINI.md`, `CONTRIBUTING.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`) — the packaged app only reads `docs/i18n//docs/**`, which stays. Saves ~11 MB on top of the translated CHANGELOGs already pruned. (#0000) ([#13235](https://github.com/diegosouzapw/OmniRoute/pull/13235)) +- **chore(quality):** retighten the `src/sse/handlers/chat.ts` unused-vars suppression count to the 8 violations that actually remain ([#13385](https://github.com/diegosouzapw/OmniRoute/pull/13385)) +- Bring the documented counts back in line with the code: 171 migrations (was 169) and 20 routing strategies (was 19), plus the regenerated `cli-tunnel` skill reference. ([#12970](https://github.com/diegosouzapw/OmniRoute/pull/12970)) +- Update the documented migration count to 172 after the call-logs provider-stats indexes landed. ([#13209](https://github.com/diegosouzapw/OmniRoute/pull/13209)) +- **chore(ci):** two gates that close the blind spots behind the exclusions above. `check:vitest-exclusions` requires every Vitest exclusion to name a tracking issue and to appear in `config/quality/vitest-exclusions.json` — the previous list grew to 62 files behind a comment pointing at an issue that had been closed for a month. `check-new-key-coverage` requires a key newly added to `en.json` to reach every locale; the existing coverage gate is a percentage floor per locale, so eleven absent keys out of ~13,000 left it at 99.9% while a whole feature shipped untranslated in nine languages. Both are diff-aware, so pre-existing debt stays frozen and neither needed a migration to turn on. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **build:** root layout no longer loads Inter from `next/font/google`, so a production image build does not need fonts.googleapis.com ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **docs(i18n):** bring the eight new `llm.txt` mirrors (km, kn, ml, my, ne, or, pa, si) up to the current provider and migration counts, so `check-docs-sync` stops rejecting every commit ([#13674](https://github.com/diegosouzapw/OmniRoute/pull/13674)) +- **refactor(ui):** flow surfaces (home topology, combo live studio, compression cockpit/waterfall/nodes, token-health badge) express state through the theme-aware `--orch-status-*` tokens instead of fixed dark-mode hex, so green/red/amber/grey stay legible in the light theme; dark mode is byte-identical. Categorical palettes (routing-strategy hues, compression-layer pills, provider brand colors) deliberately stay hex ([#12378](https://github.com/diegosouzapw/OmniRoute/issues/12378)) ([#13203](https://github.com/diegosouzapw/OmniRoute/pull/13203)) +- **chore(tests):** 51 test files that had been excluded from Vitest are running again, restoring roughly 350 assertions to the blocking suite. Each was measured individually first: of the 62 files parked behind the `// #8618 — pre-existing failure` comment, 51 pass against the current tree with no source change, so the exclusions had outlived the failures they were added for. The 11 that genuinely still fail stay excluded, but now point at a live tracker (#13204) rather than at #8618, which was closed in August while the list it tracked kept growing. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **chore(quality):** drop two ESLint suppression entries whose violations no longer exist, so `eslint --suppressions-location` stops rejecting every commit that touches the surrounding files ([#13384](https://github.com/diegosouzapw/OmniRoute/pull/13384)) +- **chore(release):** bring main's post-tag fixes into release/v3.8.51 — sync-back step 2/2 (direct commit `21c488f210`) +- **chore(deps):** patch toml and esbuild in the opencode plugin lockfiles ([#12965](https://github.com/diegosouzapw/OmniRoute/pull/12965)) +- **docs(changelog):** reconcile the v3.8.51 living section — fold 366 fragments, cover every cycle commit, credit every contributor ([#12971](https://github.com/diegosouzapw/OmniRoute/pull/12971)) +- **test(plugin):** wait for the v2 lazy-refresh tier instead of sleeping past it ([#12984](https://github.com/diegosouzapw/OmniRoute/pull/12984)) +- **chore(quality):** rebaseline chatCore.ts after the non-streaming regression fixes ([#13045](https://github.com/diegosouzapw/OmniRoute/pull/13045)) +- **docs(changelog):** add the missing bullet marker to the DeepSeek PoW fragment ([#13200](https://github.com/diegosouzapw/OmniRoute/pull/13200)) +- **chore(deps):** drain the Dependabot queue — 10 of 13 alerts ([#13213](https://github.com/diegosouzapw/OmniRoute/pull/13213)) +- **chore(ci):** make worktrees carry the hooks that enforce the identity gate ([#13236](https://github.com/diegosouzapw/OmniRoute/pull/13236)) +- **chore(deps):** pin csv-parse to ^7.0.2 via overrides (#13117) ([#13300](https://github.com/diegosouzapw/OmniRoute/pull/13300)) — thanks @KooshaPari +- **chore(deps):** bump hono from ^4.12.34 to ^4.13.7 (#13148) ([#13301](https://github.com/diegosouzapw/OmniRoute/pull/13301)) — thanks @KooshaPari +- **chore(deps):** pin joi to ^18.2.8 via overrides (#13085) ([#13302](https://github.com/diegosouzapw/OmniRoute/pull/13302)) — thanks @KooshaPari +- **docs(providers):** add Microsoft 365 Copilot (BizChat) provider guide (#12779) ([#13340](https://github.com/diegosouzapw/OmniRoute/pull/13340)) — thanks @KooshaPari +### 🙌 Contributors + +Thanks to everyone whose work landed in v3.8.51: + +| Contributor | PRs / Issues | +| --- | --- | +| [@5dive-bot](https://github.com/5dive-bot) | #11852 | +| [@abhisheksharma2411](https://github.com/abhisheksharma2411) | #11547, #11684, #11959, #12209 | +| [@adevwithpurpose](https://github.com/adevwithpurpose) | #11464 | +| [@adivekar-utexas](https://github.com/adivekar-utexas) | #12015, #12043, #12090, #12138 | +| [@AIB1TAL0S](https://github.com/AIB1TAL0S) | #12103 | +| [@alltomatos](https://github.com/alltomatos) | #11448 | +| [@alvinveroy](https://github.com/alvinveroy) | #11857, #12027, #12165 | +| [@amaleta](https://github.com/amaleta) | #11906, #11934 | +| [@amartinawi](https://github.com/amartinawi) | #11575 | +| [@AndrianBalanescu](https://github.com/AndrianBalanescu) | #11083, #11830, #11834, #11844 | +| [@anhtahaylove](https://github.com/anhtahaylove) | #13091, #13092, #13093, #13096, #13097, #13100, #13106, #13109, #13114, #13155, #13166, #13171, #13175, #13187, #13196 | +| [@aniruddhaadak80](https://github.com/aniruddhaadak80) | #11760 | +| [@app](https://github.com/app) | #12554 | +| [@arminanton](https://github.com/arminanton) | #11461, #11513 | +| [@AStupidBear](https://github.com/AStupidBear) | #11584 | +| [@atescivitci-cmd](https://github.com/atescivitci-cmd) | #12105 | +| [@b3nw](https://github.com/b3nw) | #11970, #11971, #12147 | +| [@backryun](https://github.com/backryun) | #11259, #11950, #12075, #12076, #12078, #12079, #12081, #12082, #12181, #12228, #12239, #12255, #12258, #12277, #12367, #12423, #12524, #12538 | +| [@benjaminkitt](https://github.com/benjaminkitt) | #11747 | +| [@benzntech](https://github.com/benzntech) | #11614, #11615 | +| [@Bl0ck154](https://github.com/Bl0ck154) | #11948, #11951, #11952, #11953, #11954 | +| [@brick30llc-ctrl](https://github.com/brick30llc-ctrl) | #11752, #12242, #12262 | +| [@Chewji9875](https://github.com/Chewji9875) | #11455, #11875, #12028 | +| [@cheynetom](https://github.com/cheynetom) | #12058 | +| [@davidebaraldo](https://github.com/davidebaraldo) | #12222, #12304 | +| [@davidlinfr](https://github.com/davidlinfr) | #6390 | +| [@ddarkr](https://github.com/ddarkr) | #12124 | +| [@Deftera186](https://github.com/Deftera186) | #11809 | +| [@dpozimski](https://github.com/dpozimski) | #11945 | +| [@DW-MediaLab](https://github.com/DW-MediaLab) | #12229 | +| [@echel0nn](https://github.com/echel0nn) | #11923 | +| [@f9td56dbgh-hub](https://github.com/f9td56dbgh-hub) | #11660 | +| [@fabioluissilva](https://github.com/fabioluissilva) | #11991 | +| [@foreveryh](https://github.com/foreveryh) | #12177 | +| [@geek007git](https://github.com/geek007git) | #12115, #12116, #12117, #12120, #12122 | +| [@geekyNads](https://github.com/geekyNads) | #11784, #12266 | +| [@ggiak](https://github.com/ggiak) | #11561, #12276, #12350, #12402 | +| [@gonisulaimann](https://github.com/gonisulaimann) | #12368, #12369, #12371 | +| [@Gorillaz322](https://github.com/Gorillaz322) | #12207 | +| [@groovecityJO](https://github.com/groovecityJO) | #12682 | +| [@hartmark](https://github.com/hartmark) | #11434, #11452, #11473, #11499, #11703, #11983, #11984, #11985, #11986, #11988, #11989, #11990, #11994, #12221, #12293, #12445, #12446, #12447, #12448, #12460, #12461, #12623, #12646, #12650, #12680, #12717, #12718, #12727, #12741, #12854 | +| [@hizzt](https://github.com/hizzt) | #11894 | +| [@hongnoul](https://github.com/hongnoul) | #11484 | +| [@HouMinXi](https://github.com/HouMinXi) | #11411, #11414, #11512, #11518, #11520, #11641, #11642, #11643, #11687, #11779, #11849, #11850, #11851, #11915, #11916, #11918, #11919, #11920, #12013, #12017, #12033, #12042, #12106, #12139, #12166, #12169, #12171, #12205, #12213, #12312, #12325, #12487, #12488, #12495, #12504, #12557, #12566, #12590, #12591, #12624, #12626, #12632, #12637, #12678, #12696, #12697, #12711, #12733, #12746, #12767, #12770, #12789, #12803, #12805, #12811, #12866, #12868, #12899, #12926, #12934, #12950, #12951, #12974, #13001, #13006, #13011, #13017, #13026, #13027, #13034, #13035, #13038, #13042, #13050, #13060, #13061, #13069, #13107, #13120, #13136, #13195, #13197 | +| [@Hsia97](https://github.com/Hsia97) | #11624 | +| [@jacobsparts](https://github.com/jacobsparts) | #11854, #12155, #12167 | +| [@joglomedia](https://github.com/joglomedia) | #11980 | +| [@jonlwheat2-gif](https://github.com/jonlwheat2-gif) | #11443, #11450, #11549, #11567, #11608, #11633, #11635, #11640, #11644, #11671, #11672, #11673, #11674, #11675, #11676, #11677, #11706, #11783, #12051, #12052, #12053, #12055 | +| [@kanade-hoshino](https://github.com/kanade-hoshino) | #12180 | +| [@Karan825](https://github.com/Karan825) | #11861 | +| [@KaspaPulse](https://github.com/KaspaPulse) | #11389, #11469, #11628, #11666, #11910 | +| [@keeltrace](https://github.com/keeltrace) | #12080, #12223 | +| [@killer30001000](https://github.com/killer30001000) | #12178 | +| [@KooshaPari](https://github.com/KooshaPari) | #12470, #12592, #12667, #12699, #12703, #12706, #12764, #12769, #12771, #13300, #13301, #13302, #13340, #13401, #13403, #13404, #13405, #13406, #13407, #13409, #13410, #13411, #13412, #13413, #13414, #13418, #13424, #13427, #13433, #13523, #13524, #13525, #13528, #13531, #13532, #13534, #13539, #13543, #13545, #13546, #13547, #13550, #13553 | +| [@kriptoburak](https://github.com/kriptoburak) | #11370 | +| [@KrzysiekSko](https://github.com/KrzysiekSko) | #12425, #12432, #12673 | +| [@marcelokarval](https://github.com/marcelokarval) | direct commit / report | +| [@maxmad64bis](https://github.com/maxmad64bis) | #1622, #11435, #11437, #11441, #11537, #11550, #11553, #11555, #11557, #11812, #11842, #11843, #11903, #12151, #12214, #12215, #12218, #12226, #12314, #12316, #12317, #12318, #12319, #12320, #12321, #12507, #12715, #12731, #12744, #12786, #12787, #12788, #12790, #12792, #12794, #12795, #12828, #12832, #12853, #12857, #12870, #12937, #12941, #12975, #13141, #13142, #13146, #13147 | +| [@mdigitalbh81](https://github.com/mdigitalbh81) | #12240 | +| [@Meet6338-X](https://github.com/Meet6338-X) | #11598, #11609, #12110 | +| [@morpheus9393](https://github.com/morpheus9393) | #11943 | +| [@MumuTW](https://github.com/MumuTW) | #11492, #11502, #11506, #11507, #11626, #11675, #11685, #11728, #11746, #11888, #11889, #11890, #11892 | +| [@Neuron-Mr-White](https://github.com/Neuron-Mr-White) | #11622, #11801, #12256 | +| [@NightStalker-87](https://github.com/NightStalker-87) | #12183 | +| [@NoxzRCW](https://github.com/NoxzRCW) | #11879, #11880, #11881, #11882, #11883, #11935 | +| [@ntdat812](https://github.com/ntdat812) | #11585, #12095 | +| [@ntdatt812](https://github.com/ntdatt812) | #11368, #11573, #11574, #11576, #11577, #11580, #11582, #11583, #11588, #11589, #11590, #11591, #11592, #11593, #11672, #12177, #12180, #12873, #12918, #12920, #12921, #12925, #12930, #13007, #13009, #13024, #13025, #13083, #13087, #13101, #13104, #13110 | +| [@official-burak](https://github.com/official-burak) | #11542 | +| [@opensource-elearning](https://github.com/opensource-elearning) | #12179, #12189, #12278, #12286 | +| [@oyi77](https://github.com/oyi77) | #11408, #11409, #11421, #11505, #11677, #12036, #12110 | +| [@pacocartones](https://github.com/pacocartones) | #11521, #11522, #11527, #11528, #11529, #11530, #11531, #11532, #11533, #11534, #11595, #11599, #11603, #11604, #11605, #11607, #11610, #11676, #11714, #11716, #11718, #11767, #11838, #11860, #11862, #11869, #11871, #11872, #11873, #11903, #11906, #11921, #11934, #12356, #12358, #12359, #12360, #12361, #12362, #12364, #12365, #12373, #12374, #12375, #12376, #12377, #12379, #12380, #12381, #12386, #12387, #12389, #12390, #12394, #12395, #12397, #12401, #12403, #12404, #12522, #12523, #12535, #12536, #12540, #12541, #12543, #12545, #12548, #12549, #12550, #12551, #12552, #12647, #12651, #12653, #13055 | +| [@patrykkopycinski](https://github.com/patrykkopycinski) | #11936, #11937, #12224 | +| [@PauloFH](https://github.com/PauloFH) | #11509 | +| [@PauloHSOliveira](https://github.com/PauloHSOliveira) | #12241 | +| [@ponkcore](https://github.com/ponkcore) | #12054 | +| [@Prajeeth-12](https://github.com/Prajeeth-12) | #11634, #12046 | +| [@quiterunner-commits](https://github.com/quiterunner-commits) | #12131, #12143 | +| [@rafacpti23](https://github.com/rafacpti23) | #11554, #11558, #12192, #12197, #12198, #12202, #12203, #12204 | +| [@ragnar-claude](https://github.com/ragnar-claude) | #11564 | +| [@raheemuddin786](https://github.com/raheemuddin786) | #11491, #11839, #11840, #11841, #12003, #12230, #12231, #12232, #12233, #12234 | +| [@rahilmavani](https://github.com/rahilmavani) | #11761 | +| [@Rahulsharma0810](https://github.com/Rahulsharma0810) | #11771 | +| [@RaviTharuma](https://github.com/RaviTharuma) | #11710, #11727, #11797, #11798, #11802, #11805, #11806, #11811, #12098, #12099, #12101, #12449, #12452, #12472, #12473, #12493, #12533, #12607, #12628, #12631, #12636, #12875, #12876, #12880, #12882, #12884 | +| [@rezjalibd](https://github.com/rezjalibd) | #12186 | +| [@RhianB14](https://github.com/RhianB14) | #11692 | +| [@rifqiawl](https://github.com/rifqiawl) | #11517, #11519, #11969 | +| [@rqzbeh](https://github.com/rqzbeh) | #11390 | +| [@Sabeekhann](https://github.com/Sabeekhann) | #11699, #11705 | +| [@santosraju99-hub](https://github.com/santosraju99-hub) | #11755, #11770 | +| [@Seramicx](https://github.com/Seramicx) | #11568 | +| [@Siva010](https://github.com/Siva010) | #12191 | +| [@solstxce](https://github.com/solstxce) | #11597 | +| [@soroush5](https://github.com/soroush5) | #12691 | +| [@tenshiak](https://github.com/tenshiak) | #12279 | +| [@TheDemonTuan](https://github.com/TheDemonTuan) | #11468, #11470, #11471, #11482, #11548, #11758, #11775, #11814 | +| [@thomasmaerz](https://github.com/thomasmaerz) | #12834 | +| [@tuandinh0801](https://github.com/tuandinh0801) | #11454 | +| [@turbolego](https://github.com/turbolego) | #11621, #11762, #11772, #11774, #11781, #12216 | +| [@Tushar49](https://github.com/Tushar49) | #11392 | +| [@ujjawalkaushik1110](https://github.com/ujjawalkaushik1110) | #11863 | +| [@vermasomesh835](https://github.com/vermasomesh835) | #11794 | +| [@vsd2807](https://github.com/vsd2807) | #11565, #11619 | +| [@wahidsadik371-coder](https://github.com/wahidsadik371-coder) | #12126 | +| [@watchingdogs](https://github.com/watchingdogs) | #12031 | +| [@wildcard](https://github.com/wildcard) | #11620 | +| [@xiaoyaner0201](https://github.com/xiaoyaner0201) | #11460, #11662, #12083 | +| [@Xxx91n](https://github.com/Xxx91n) | #11690 | +| [@yourspraveen](https://github.com/yourspraveen) | #11146 | +| [@yxyxy](https://github.com/yxyxy) | #11671 | +| [@ZaimMarzuki](https://github.com/ZaimMarzuki) | #11960 | +| [@Zartharas](https://github.com/Zartharas) | #11340 | +| [@zero-executioner](https://github.com/zero-executioner) | #11631 | +| [@diegosouzapw](https://github.com/diegosouzapw) | maintainer | + +--- ## [3.8.50] — 2026-08-25 _Living section — regenerated 2026-08-12 from all cycle commits (cycle open `ed2db6cb19` → tip). Bullets carry the merged PR and its author; direct pushes listed separately._ diff --git a/docs/i18n/it/CHANGELOG.md b/docs/i18n/it/CHANGELOG.md index d5375680dd..a8c17405f8 100644 --- a/docs/i18n/it/CHANGELOG.md +++ b/docs/i18n/it/CHANGELOG.md @@ -8,16 +8,1119 @@ ## [3.8.51] — TBD -_Living section — cycle opened at the v3.8.50 freeze (parallel-cycle model). Bullets are aggregated from `changelog.d/` fragments at each `/generate-release` phase._ +_Living section — reconciled 2026-09-15 from all cycle commits (`091589089c` → `c0f92ec98a`, 916 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 | **123** | +| 📝 Commits in the cycle | **916** | +| 🔀 Pull requests referenced | **892** | +| 📋 Changelog entries | **919** | +| 🙌 Contributors credited in entries | **121** | +| 🤖 Automated dependency commits | 18 | + +**Entries by type** + +| Type | Count | +| --- | ---: | +| 🐛 Fixes | 592 | +| ✨ Features | 131 | +| 🧹 Chore | 93 | +| 📚 Docs | 42 | +| 🧪 Tests | 32 | +| ♻️ Refactor | 8 | +| 🏗️ Build | 7 | +| ⚡ Performance | 7 | +| ⚙️ CI | 2 | +| ⏪ Reverts | 2 | +| 📦 Dependencies | 2 | +| 🔒 Security | 1 | + +### 🏆 Top 25 contributors this cycle + +_By commits in `091589089c..c0f92ec98a`, author identities consolidated via `.mailmap` and the merged PR's GitHub login. Bots excluded._ + +| # | Contributor | Commits | +| ---: | --- | ---: | +| 🥇 | diegosouzapw | 323 | +| 🥈 | Bob.Hou (@HouMinXi) | 79 | +| 🥉 | Paco Cartones (@pacocartones) | 77 | +| 4 | Dizzle (@maxmad64bis) | 49 | +| 5 | Koosha Paridehpour (@KooshaPari) | 43 | +| 6 | Nguyen Thanh Dat (@ntdatt812) | 31 | +| 7 | Ravi Tharuma (@RaviTharuma) | 26 | +| 8 | Markus Hartung (@hartmark) | 22 | +| 9 | Webman (@jonlwheat2-gif) | 22 | +| 10 | backryun | 17 | +| 11 | anhtahaylove | 15 | +| 12 | Rafa Martins (@rafacpti23) | 10 | +| 13 | Syed Raheemuddin (@raheemuddin786) | 10 | +| 14 | Nguyễn Viết Tuấn (@TheDemonTuan) | 8 | +| 15 | MumuTW | 7 | +| 16 | Paijo (@oyi77) | 7 | +| 17 | Tobias Andersen (@turbolego) | 6 | +| 18 | Bl0ck (@Bl0ck154) | 5 | +| 19 | SHANMUGAPRIYAN (@geek007git) | 5 | +| 20 | KaspaPulse | 5 | +| 21 | Abhishek Sharma (@abhisheksharma2411) | 4 | +| 22 | Abhishek Divekar (@adivekar-utexas) | 4 | +| 23 | Andrew B. (@AndrianBalanescu) | 4 | +| 24 | NoxzRCW | 4 | +| 25 | opensource-elearning | 4 | ### ✨ New Features +- **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). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- Added Google AI Studio Gemini batch text-to-speech support through `POST /v1/audio/speech`. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- 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. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(routing):** subscription-first auto groupings — `auto/subscription` routes only through plan-included connections with a documented hard-stop overage and fails closed on exhaustion, while `auto/thrifty` orders the pool `subscription → keyless → free → cheap → premium` and steps up one rung at a time as each is exhausted. Billing class comes from a curated per-connection catalog (uncurated is treated as metered, never plan-included), both reuse STRICT_ZERO_COST's per-connection verification, and a quota reading whose `resetAt` has passed is now refreshed regardless of TTL so routing returns to plan capacity as soon as it resets ([#11146](https://github.com/diegosouzapw/OmniRoute/pull/11146)) — thanks @yourspraveen +- **feat(providers):** publish a management-authenticated versioned web-session credential contract from OmniRoute's canonical browser credential metadata ([#11340](https://github.com/diegosouzapw/OmniRoute/pull/11340)) — thanks @Zartharas +- **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)) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add Xquik X search with typed results, credential validation, REST routing, and MCP selection ([#11370](https://github.com/diegosouzapw/OmniRoute/pull/11370)) — thanks @kriptoburak +- **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)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(dashboard):** surface durable exclusive managed leases in the existing Sessions view, keeping leased clients visible across idle gaps while marking connections with in-flight work as active ([#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389)) — thanks @KaspaPulse +- **build(bun):** allow Turbopack bundler flag on Bun 1.4+ with configurable Webpack fallback ([#11471](https://github.com/diegosouzapw/OmniRoute/pull/11471)) — thanks @TheDemonTuan +- feat(api): add an opt-in `modelVisibilityAllowlist`/`modelVisibilityDenylist` settings pair to curate exactly which models `/v1/models` advertises, mirrored into every `auto/*` combo candidate pool so a denied model cannot be routed to via combo selection either (#11481) ([#11997](https://github.com/diegosouzapw/OmniRoute/pull/11997)) +- **feat(rankings):** the Free Provider Rankings page shows what each provider actually served over the last 24 h. It ranked by ELO alone, which left a provider that answers every call with an error in first place; the usage data was already served by the API but never requested. A provider with too small a sample shows a dash, not a number ([#11546](https://github.com/diegosouzapw/OmniRoute/pull/11546)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **feat(guardrails):** enforce a bounded, deterministic contract for Video Bridge transcripts — 256 cues, 4096 input code units and 4 KiB UTF-8 per cue, 64 KiB total text, malformed-Unicode rejection, focus-window scoping, cross-source reconciliation with contributing-source metadata, and a structural provenance trust boundary so caller JSON can never self-assert `embedded`/`audio-bridge` provenance ([#11652](https://github.com/diegosouzapw/OmniRoute/issues/11652)) ([#12009](https://github.com/diegosouzapw/OmniRoute/pull/12009)) +- **feat(video):** orchestrate optional Video Bridge audio extraction and Audio Bridge STT behind a dual opt-in (operator setting AND per-request signal) — a new loopback-only broker `mode=audio` operation shares the frame path's exact process queue, deadline, AbortSignal, and byte budgets to extract a bounded mono 16 kHz PCM WAV from the same already-downloaded video, then reuses the existing Audio Bridge transcription boundary; provider segment timing is preserved when available and marked coarse otherwise, and every failure degrades to a visual-only-safe partial instead of throwing (#11654). ([#12012](https://github.com/diegosouzapw/OmniRoute/pull/12012)) +- Add a tenant-bound Video Bridge drill-down lifecycle on top of the existing secure cache substrate: opaque hashed handles (never raw session/video identifiers), preview/standard/detail multiresolution variants resampled on read, response pagination capped at 8 frames and 32 MiB, and a new authenticated `/api/v1/video-bridge/drilldown` consumer route that stays disabled for remote access by default and denies cross-key access with the same response as a nonexistent handle (no existence oracle). ([#12006](https://github.com/diegosouzapw/OmniRoute/pull/12006)) +- **test(video):** Add the Video Bridge FU-07/FU-09 promotion-evidence harness (#11656) — a frozen Zod manifest schema covering the 8 required scenario kinds (static scenes, rapid cuts, late facts, fades, blur, small text, close events, visual prompt injection) with a minimum of 3 repetitions per case, deterministic declarative fixture recipes (`videoBridgePromotionFixtures.ts`), a pure medians/p95 metrics aggregator, a pure FU-07/FU-09 promotion-verdict evaluator applying the ticket's exact thresholds (missing token usage always holds), a digest-only persistence layer that never retains raw media or raw model responses, and a versioned per-model promotion allowlist shipped empty with every model defaulting to `hold`. The FU-07/FU-09 promotion verdicts themselves remain HOLD — they require a real evidence run against real models on VPS 192.168.0.15. ([#12008](https://github.com/diegosouzapw/OmniRoute/pull/12008)) +- **feat(video bridge):** "embedded" transcript provenance can now be legitimately earned instead of merely asserted — a bounded, allowlisted (`mov_text`/`subrip`/`webvtt`) subtitle probe runs through the loopback-only Video Bridge broker (at most 2 streams, 10s subdeadline bounded by the request deadline, 256 KiB output, 4096-code-unit lines), normalized through a bounded, ReDoS-safe WebVTT parser and Zod-validated end to end. The adapter always resolves to an explicit `success`/`absent`/`transient_failure` outcome — a subtitle failure never breaks the visual description path, and only a fingerprint-verified broker response (never a caller-declared label) can produce embedded cues (#11659). ([#12011](https://github.com/diegosouzapw/OmniRoute/pull/12011)) +- Default new Antigravity-family connections (agy CLI imports and Antigravity OAuth connects) to model auto-sync, so live model discovery lands in the synced catalog and `/v1/models` picks up freshly released upstream models (e.g. Gemini 3.7 Flash tiers) without code changes. Existing connections keep their current setting; the per-connection dashboard toggle remains the opt-out. (#11685 — thanks @MumuTW) ([#11887](https://github.com/diegosouzapw/OmniRoute/pull/11887)) +- **feat(ui):** enable React Compiler (`reactCompiler: true` + `babel-plugin-react-compiler`) for automatic memoization at build time ([#11783](https://github.com/diegosouzapw/OmniRoute/pull/11783)) — thanks @jonlwheat2-gif +- **feat(providers):** add SeekAi (`seekai.cc`) as an OpenAI-compatible New-API gateway — catalog id `seekai` (alias `ska`), `https://seekai.cc/v1`, live `/v1/models` via `passthroughModels`, aggregator-list membership so New-API balance detection can opt in. No referral/aff codes. ([#11786](https://github.com/diegosouzapw/OmniRoute/issues/11786)) ([#12557](https://github.com/diegosouzapw/OmniRoute/pull/12557)) — thanks @HouMinXi +- **feat(zai):** add GLM-5.3-Flash Coding Plan support (1M context, 128K output, vision, `low|high|max` reasoning) and route `zai` GLM-5.3-family API-key traffic through the OpenAI-compatible Coding Plan endpoint with native thinking defaults ([#11801](https://github.com/diegosouzapw/OmniRoute/pull/11801)) — thanks @Neuron-Mr-White +- **feat(combo):** choose how combo models are ordered — manual, provider, score, or name — via a sort control in the dashboard builder, persisted in `config.modelSort` and re-applied on load and after add ([#11812](https://github.com/diegosouzapw/OmniRoute/pull/11812)) — thanks @maxmad64bis +- **feat(free):** custom models can be marked free-tier via `customModels[].isFree`; `isFreeModel()` is the first door and `hidePaidModels` respects it even for providers outside the free budget ([#11843](https://github.com/diegosouzapw/OmniRoute/pull/11843)) — thanks @maxmad64bis +- **feat(nodejs):** add `5dive` as a `configure` target — `omniroute configure 5dive` / `omniroute setup-5dive` write a 5dive auth profile that points an agent fleet's `claude` seats at OmniRoute, with the root-only write, the loopback-vs-`https` endpoint rule and the per-seat model pin handled explicitly ([#11852](https://github.com/diegosouzapw/OmniRoute/pull/11852)) — thanks @5dive-bot +- **feat(sse):** treat `max` as a first-class reasoning-effort tier and clamp per model family (GLM 5.1+/DeepSeek V4+/Kimi K3+ keep native `max`; o1/MiniMax/Grok/Muse Spark clamp to their upstream ceiling) ([#11875](https://github.com/diegosouzapw/OmniRoute/pull/11875)) — thanks @Chewji9875 +- **feat(providers):** the provider plugin manifest now advertises a `usage-fetch` capability for the 40 providers that have a wired usage/quota fetcher, so external dashboards can read it from `GET /api/v1/provider-plugin-manifest` instead of parsing `open-sse/services/usage.ts` after every release. Discovery only — no new fetcher, no quota change, and the Dashboard quota widget stays gated by `USAGE_SUPPORTED_PROVIDERS`. `USAGE_FETCHER_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/fetcherProviders.ts`) and is re-exported from `services/usage.ts`, keeping the manifest module a light leaf instead of pulling the ~490-module usage dispatcher into the manifest route. ([#11903](https://github.com/diegosouzapw/OmniRoute/pull/11903)) — thanks @maxmad64bis / @pacocartones +- **feat(plugins):** `OMNIROUTE_PLUGINS_DIR` sets the directory the runtime plugin scanner reads — and the root the plugin manager installs into — overriding the `HOME`-derived default, so a Docker/K8s deployment can point straight at its bind-mounted plugin tree instead of moving `HOME` just to relocate the scan path. An image that exports no home no longer scans `/tmp/.omniroute/plugins` in silence: the resolved directory is logged once at startup as `scanner.dir_resolved`, naming the input that won. Unset, behaviour is unchanged. Distinct from the CLI-only `OMNIROUTE_PLUGIN_PATH`, which finds `omniroute-cmd-*` command packages and never reached this scanner ([#11906](https://github.com/diegosouzapw/OmniRoute/pull/11906)) — thanks @amaleta / @pacocartones +- **feat(leases):** add an explicit owner-authenticated status action that returns only the active lease's privacy-safe configured connection and provider labels, with generation fencing and no credential or internal-id disclosure ([#11910](https://github.com/diegosouzapw/OmniRoute/pull/11910)) — thanks @KaspaPulse +- **feat(providers):** import-from-file modal shows per-row API errors and ships a downloadable CSV template ([#12071](https://github.com/diegosouzapw/OmniRoute/issues/12071)) ([#12504](https://github.com/diegosouzapw/OmniRoute/pull/12504)) — thanks @HouMinXi +- **feat(providers):** dashboard search matches connection name and `baseUrl` so imported OpenAI-compat nodes surface on the provider card ([#12108](https://github.com/diegosouzapw/OmniRoute/issues/12108)) ([#12495](https://github.com/diegosouzapw/OmniRoute/pull/12495)) — thanks @HouMinXi +- **feat(routing):** add a `score` Auto router strategy that selects the highest configured weighted score and reuses `explorationRate`. ([#12155](https://github.com/diegosouzapw/OmniRoute/pull/12155)) — thanks @jacobsparts +- Add a runtime feature flag to disable universal context handoffs globally without changing the default behavior. ([#12167](https://github.com/diegosouzapw/OmniRoute/pull/12167)) — thanks @jacobsparts +- **feat(providers):** the provider plugin manifest now also advertises a `usage-supported` capability for the 46 providers whose usage API is accepted by the server and Dashboard routes, so integrators can distinguish "the server will serve quota for this provider" from "a fetcher is wired" without reading TypeScript. Discovery only — no fetcher or quota change. `usage-fetch` resolves on id or alias (the usage dispatcher accepts both); `usage-supported` resolves on id alone, matching the runtime guard `USAGE_SUPPORTED_PROVIDERS.includes(providerId)`. `USAGE_SUPPORTED_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/supportedProviders.ts`) and is re-exported from `providers.ts`, mirroring the `fetcherProviders` leaf from #11903 and keeping the manifest a light module. ([#12214](https://github.com/diegosouzapw/OmniRoute/pull/12214)) — thanks @maxmad64bis +- **feat(rankings):** order Free Provider Rankings by what each provider actually served — `GET /api/free-provider-rankings?sortBy=reliability` and a "Most reliable first" toggle on the page. Providers with too few calls to state a success rate keep their score order below the measured ones; the default order is unchanged ([#12218](https://github.com/diegosouzapw/OmniRoute/pull/12218)). — thanks @maxmad64bis +- **perf(sse):** defer `cloneLogPayload()` in the structured SSE collector until after the `maxEvents`/`maxBytes` cap check, eliminating ~9,800 wasted `structuredClone` calls per streaming response (65–71% faster `push()`). Reducer snapshot isolation restored for OpenAI and Responses summaries ([#12241](https://github.com/diegosouzapw/OmniRoute/pull/12241)) — thanks @PauloHSOliveira ([#12243](https://github.com/diegosouzapw/OmniRoute/pull/12243)) +- **feat(usage):** Devin CLI agentic quota (Codeium seat-management GetUserStatus) and OpenRouter key limits plus account credits now surface in Provider Limits ([#12256](https://github.com/diegosouzapw/OmniRoute/pull/12256) — thanks @Neuron-Mr-White) +- **feat(settings):** persist `headroomUrl` through Settings so status/start use the operator URL instead of only `HEADROOM_URL` ([#12306](https://github.com/diegosouzapw/OmniRoute/issues/12306)) ([#12487](https://github.com/diegosouzapw/OmniRoute/pull/12487)) — thanks @HouMinXi +- **feat(auto-combo):** Auto-Combo scoring can now weigh how often a provider/model has actually succeeded. The engine already carried that number on every candidate — 24 hours of usage history behind a ten-sample floor, real-time metrics otherwise — and the scoring function never read it, while the weight table described `stability` as if it did. `reliability` (`1 - failureRate`, with the same field precedence and the same rate-bounding the speed ranking already uses, so a corrupt reading means "nothing observed" rather than "fails every call") is now a declared factor shipping at weight `0`, so routing is unchanged until an operator gives it one, and the `stability` description now matches what that factor computes ([#12317](https://github.com/diegosouzapw/OmniRoute/pull/12317)) — thanks @maxmad64bis +- **feat(routing):** With `freeAccessPolicy: "strict"`, the read-only candidate listing (`GET /v1/auto-combo/{channel}/candidates`) no longer hides the candidates the zero-cost guard excludes — the same read-only transparency the resilience filter already honours (#9133). Each candidate now carries `freeAccessExclusion` saying why it would be kept out, and it tells an exhausted allowance apart from a quota reading that never arrived or went stale, which used to look identical from the outside. Routing is unchanged: the listing reports, it never enforces. The separate `excludeTosAvoid` guard still drops its candidates without a reason; that gap is now documented rather than closed ([#12319](https://github.com/diegosouzapw/OmniRoute/pull/12319)) — thanks @maxmad64bis +- **feat(radar):** The Radar catalog table now shows two facts it was already receiving from the feed and dropping on the floor: the per-model rate limits (requests and tokens, per minute and per day) in a new column, and a badge when a provider's terms state it may train on the prompts you send. A limit of zero renders as zero rather than "rate-only" — for a ceiling those are opposite facts — and a model with no training statement gets no badge, because an absent statement is not a guarantee ([#12320](https://github.com/diegosouzapw/OmniRoute/pull/12320)) — thanks @maxmad64bis +- **feat(radar):** explain Community, single-use, contributor, supporter, recovery, abuse, offers, and privacy rules before either Radar activation action, and remove the superseded fixed-PR grant promise from every UI locale ([#12342](https://github.com/diegosouzapw/OmniRoute/pull/12342)) +- **feat(gamification):** the dashboard Profile page now shows the real daily streak — `/api/gamification/level` returns `streak: { current, longest }` (per key with `apiKeyId`, operator-wide maximum otherwise) and the streak card reads it instead of a hard-coded 0 (#2403) ([#12377](https://github.com/diegosouzapw/OmniRoute/pull/12377)) — thanks @pacocartones +- **feat(gamification):** the dashboard leaderboard now shows each API key's display name under the Name column instead of a truncated key id; `GET /api/gamification/leaderboard` attaches `name` per entry (name only — no key material), while the shared ranking helper and the federation leaderboard stay id-only — thanks @pacocartones ([#12385](https://github.com/diegosouzapw/OmniRoute/pull/12385)) +- **feat(gamification):** enforce the documented 1000 XP/min per-API-key anti-cheat rate limit on the XP award path; over-limit awards are logged and skipped instead of persisted, and the sliding window now matches the timestamp format stored in `xp_audit_log` ([#2403](https://github.com/diegosouzapw/OmniRoute/issues/2403)) ([#12390](https://github.com/diegosouzapw/OmniRoute/pull/12390)) — thanks @pacocartones +- **feat(admin):** localize the gamification anomalies page — the loading state, the Status column and the Suspicious badge now come from the `common` catalog (new `common.suspicious` key propagated to every locale) — add it to the Gamification sidebar group as `gamification-admin` (`/dashboard/gamification/admin`), and expose the loading and empty states as polite `role="status"` live regions (#12401 — thanks @pacocartones) +- **feat(providers):** skip GitHub combo members missing from the live synced catalog, and drop Copilot models that are policy-disabled or hidden from the model picker ([#12473](https://github.com/diegosouzapw/OmniRoute/pull/12473)) — thanks @RaviTharuma +- **feat(i18n):** locale-expansion tooling — `npm run i18n:add-locale` adds a language to config, dashboard, docs mirrors, CLI, README/indexes and site in one command; browser, CLI and cookie detection resolve `uk`, `fil`/`tl`, `zh-Hant` (and the retired legacy `in` → `id`) via config aliases; `config/i18n.json` now ships in the npm package so the published CLI can read it; new real-translation ratio gate (`npm run i18n:check-ratio`, advisory) with a per-locale ratchet baseline; `run-translation --adopt` restores `.i18n-state.json`; `sync-language-bars` generates 🌐 bars from config; `validate_translation.py` loads its allowlist again. Retires the duplicate `in` locale (Indonesian mislabelled as Hindi) — 42 honest locales; saved `NEXT_LOCALE=in` / `OMNIROUTE_LANG=in` keep working. (#12496) +- **feat(api):** Emit gateway-measured `tokens_per_second` (TTFT excluded) on streaming usage and `X-OmniRoute-Tokens-Per-Second` when first-token latency is known ([#12616](https://github.com/diegosouzapw/OmniRoute/issues/12616)) ([#12631](https://github.com/diegosouzapw/OmniRoute/pull/12631)) — thanks @RaviTharuma +- **feat(opencode-plugin): map gateway cost/usage/tok/s onto OpenCode inference payloads** — the official plugin copies `X-OmniRoute-Response-Cost`, token counts, `X-OmniRoute-Tokens-Per-Second` / `usage.tokens_per_second`, TTFT, and the winning `X-OmniRoute-Model` onto the JSON/SSE body OpenCode already consumes. Missing tok/s is left unset (never `tokens / latency`). (#12636) — thanks @RaviTharuma +- **feat(combo):** add combo strategy `quota-weighted`: skip exhausted accounts, then weighted-draw by leftover / in-flight load; existing conversations stay pinned until the account is empty ([#12789](https://github.com/diegosouzapw/OmniRoute/pull/12789)) — thanks @HouMinXi +- **feat(models):** Account live listings become the chat catalog source for Claude, Codex, Copilot, and AGY; public metadata only fills prices on IDs those accounts already list. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **fix(models):** Union `agy` and `antigravity` live catalogs so `agy/gemini-3.8-flash-high` is not rejected after the prefix folds to `antigravity`. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **feat(opencode):** Add `@omniroute/opencode-plugin-v2`, an opencode v2 plugin that gives a v2 install a route to an OmniRoute gateway: it publishes models, combos and auto-combos into the host catalog, refreshes them lazily behind a 300s TTL, and keeps serving the last known catalog from an on-disk snapshot when the gateway is unreachable. The v1 plugin (`@omniroute/opencode-plugin`) is untouched and keeps working as before. The plugin adapts to the host instead of assuming one shape: it reads the shape the host seeds into the catalog draft and publishes the `api` block, the pre-`api` top-level fields, or both. The gateway key comes from the host credential store when one is connected, so no secret needs to sit in a config file; a source the gateway refuses is named in a warning instead of silently degrading the catalog to raw model ids; and tool schemas bound for a Gemini model are stripped of the JSON-Schema keywords Gemini rejects, so tool calling survives that route ([#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870)) — thanks @maxmad64bis +- feat(services): show sanitized CLIProxyAPI account health from its authenticated management API without exposing credentials, file paths, or raw account metadata (#6342) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add AnySearch free web search + URL extract (webFetch) with typed results, credential validation, REST routing, and MCP selection - fallback-only ([#11690](https://github.com/diegosouzapw/OmniRoute/pull/11690)) — thanks @Xxx91n +- **feat(dashboard):** display clamped `[0, 100]%` cached input token ratio in request logs table ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) — thanks @b3nw +- **feat(catalog):** add `OMNIROUTE_DISABLE_THINKING_LEVEL_VARIANTS` feature flag to optionally filter out thinking level variants from model catalog ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) — thanks @b3nw +- **feat(dashboard):** continuously export call logs to external analytics stores. A pluggable destination registry ships the full Logs-tab record set on an hourly `JobRegistry` cron, with a persisted per-destination cursor, batched inserts, a config UI rendered from each destination's own field descriptors, and a REST layer (`/api/log-export/*`) for CRUD, a connection test, and an on-demand run. A destination can opt into `includeBodies` to also ship the request and response payloads shown in the Logs detail pane, including the client and provider views of each call; this is off by default, and payloads inherit the dashboard's PII sanitisation, secret redaction and `noLog` handling. Google BigQuery is the first destination, using a service-account key stored encrypted at rest and streaming inserts keyed by call-log id, into a table that is day-partitioned on `timestamp` and clustered on `api_key_name`, `provider`, `model` and `status`. ([#11945](https://github.com/diegosouzapw/OmniRoute/pull/11945)) — thanks @dpozimski +- **feat(providers):** add MaxAI as a signed, OpenAI-compatible provider serving its 13 paid chat models (GPT-5.6 / Luna / Thinking, Claude 5 Sonnet, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite, Grok 4.1-fast / 4.5, DeepSeek V3.2 / R1, Llama 3.3 70B) through OmniRoute's `/v1` endpoint, with per-request HMAC-SHA1→SM3→AES request signing, live model + context-window discovery from `/models/get_config`, and prompted tool-calling translated to OpenAI `tool_calls` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI vision input — image_url content parts are forwarded inline in `message_content` to the 6 vision-capable models (GPT-5.6 / Luna / Thinking, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite) ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI image generation — 6 image models (gpt-image-1, dall-e-3, flux-1-schnell/dev/pro, sd3-medium) exposed through `POST /v1/images/generations` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI document RAG — inline base64 file/document attachments are uploaded to MaxAI (content-addressed `doc_id`) and attached to the chat via `doc_list` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** browserless MaxAI onboarding — email device-pair login (`/api/providers/[id]/login`) and signed access-token refresh, so a connection can be created and kept fresh without a real browser or Google OAuth ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** per-provider TLS impersonation profile (MaxAI presents a Windows Firefox-150 client fingerprint) so its bot-sensitive endpoints accept OmniRoute traffic ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** add **Nimble** as a web-search and web-fetch provider (`nimble-search`) — `/v1/search` routes to Nimble's search API at `lite` depth (locale, freshness and include/exclude domain filters mapped onto the shared request contract), and `/v1/web/fetch` routes to Nimble Extract, which covers all four fetch formats (`markdown`, `html`, `links`, `screenshot`) from a single call. One API key serves both surfaces. ([#11620](https://github.com/diegosouzapw/OmniRoute/pull/11620)) — thanks @wildcard +- The npm publish is automatic again, through npm Trusted Publishing (OIDC): the hosted `stage-npm` job publishes with a short-lived credential minted from GitHub's id-token — no `NPM_TOKEN`, no 2FA prompt, provenance attached. `publish_mode=staged` (owner approves with 2FA) and `direct` (token) remain available on `workflow_dispatch`. ([#11931](https://github.com/diegosouzapw/OmniRoute/pull/11931)) +- **feat(providers):** Add **Opper** as an API-key gateway provider — EU-hosted AI gateway with 700+ models from 30+ providers behind one OpenAI-compatible API and one key (`OPPER_API_KEY`); model ids use `provider/model` format (e.g. `anthropic/claude-sonnet-4-6`, `openai/gpt-5`); live model catalog at `https://api.opper.ai/v3/compat/models`; entry mirrors `requesty` (same shape, `passthroughModels: true`, no static seed) ([#11629](https://github.com/diegosouzapw/OmniRoute/pull/11629)) +- **feat(dashboard):** the `/dashboard/orchestration` snapshot hook now subscribes to the `agents` WebSocket channel (`agent.task.updated`) instead of `requests` as its refetch trigger, and relaxes its background poll from 5s to 30s while that WS connection is up — falling back to the tighter 5s cadence, reprogrammed live on any connect/disconnect transition, whenever the socket is down. ([#12409](https://github.com/diegosouzapw/OmniRoute/pull/12409)) +- **feat(dashboard):** Orchestration canvas quick wins — search box plus state/source/provider filter chips with a one-click clear, and per-source collapse/expand, all reflected in the URL so a filtered/collapsed view is shareable and survives a refresh; the detail drawer gained a "copy trace JSON" action and hardened error/empty-state and accessibility handling; the Agents-tab edges now animate traveling particles along active (running) connections; and the canvas node/edge status colors moved off fixed hex values onto theme-aware `--orch-status-*` CSS custom properties, so they adapt correctly to light/dark mode. ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- **chore(dashboard):** Orchestration UI hardening pass and the missing component/model test coverage it called for — `OrchestratorNode`/`ActivityNode`/`OverflowNode` rendering, the `?node=`/overflow-click page routing, the Agents-tab orchestrator-click no-op and `showCompleted` toggle, and the overview kanban's done-column sort order (#12270, #12271). ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- New `/dashboard/orchestration` page: live unified view of everything running — Cloud Agent, A2A and Conductor as a real-time graph (Agents tab), the combo cascade (Routing tab, reusing the Combo Live Studio) and a state kanban (Overview tab), with a detail drawer (trace, cost, approve/cancel). Read-only over existing APIs — no new backend. Canvas concept credit: PR #11815 design ([#12261](https://github.com/diegosouzapw/OmniRoute/pull/12261)) +- **feat(dashboard):** new "History" tab on `/dashboard/orchestration` — an Airflow-style grid of finished runs over a 24h/7d/30d preset window, one row per (source, identity), clicking a cell opens the existing detail drawer. It is backed by real persistence: A2A task lifecycle transitions are now written to the `a2a_tasks` table (purged after 30 days, configurable via `OMNIROUTE_A2A_HISTORY_RETENTION_DAYS`) and served by the new `GET /api/a2a/tasks/history` listing endpoint, with the task-detail route falling back to persisted history once a run leaves the in-memory snapshot. Conductor runs stay remote and are not persisted locally — the tab says so instead of silently omitting them. ([#12479](https://github.com/diegosouzapw/OmniRoute/pull/12479)) +- **feat(dashboard):** the orchestration detail drawer gained a "Repeat" action for Cloud Agent, A2A and Conductor tasks — a two-click confirm (click once to arm, click again within the confirm window to fire) re-submits the original prompt/input as a new run. The button is disabled with an explanatory tooltip whenever the original input can't be recovered from the loaded task detail (e.g. it never carried a prompt, or the detail failed to load). Two limitations of the A2A variant, by design: it targets the `/a2a` JSON-RPC endpoint, which authenticates with an API key only (`REQUIRE_API_KEY=true` or a configured `OMNIROUTE_API_KEY` makes a dashboard-session repeat answer `HTTP 400` — surfaced verbatim in the drawer's error line, never as a success), and the `message/send` call is SYNCHRONOUS: the POST blocks for the whole skill run, so the success confirmation only appears once the repeated task finishes. A dashboard-authenticated A2A creation path is deliberately left to a follow-up — widening the endpoint's auth posture is an operator decision, not a side effect of this feature. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(a2a):** A2A task execution now records which memories were consulted for the task's last user message as `metadata.memoryHits` (id/key/type/content-snippet) plus a `memory_hits` history event, purely for observability — the retrieved memory is never injected into a skill's prompt or behavior. Gated by the `OMNIROUTE_A2A_MEMORY_HITS` kill-switch (default enabled; set to `0` to skip the recall lookup entirely). The drawer's new "Memory used" section lists these hits for a2a tasks and is omitted whenever there are none. Known limitation: recall only resolves under the keyless posture — a keyed caller's task owner is a SHA-256 prefix of the API key, while memory rows are keyed by the database api-key id, and no hash→id lookup exists today, so the hit list stays empty for keyed callers. The recorded hits are also kept out of the task's own `input` (and therefore out of the persisted input and of the "Repeat" request body), so repeating a task never re-sends the previous run's memory snippets. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(providers):** add a Perplexity Agent API provider (`perplexity-agent` / `pplx-agent`) for Perplexity `/v1/responses`, including the documented Anthropic, OpenAI, Google, xAI, DeepSeek, Z.AI, Moonshot/Kimi, NVIDIA, and Perplexity model IDs plus Anthropic-model `max_output_tokens` compatibility. ([#12103](https://github.com/diegosouzapw/OmniRoute/pull/12103)) — thanks @AIB1TAL0S +- **feat(providers):** add RPD (Requests Per Day) limit to provider rate limit overrides across UI, schemas, DB, and i18n ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) — thanks @b3nw +- **feat(providers): add UC Direct (uncensored.com Developer API), the metered OpenAI-compatible surface.** A standard OpenAI-compatible passthrough (default executor) for uncensored.com's official REST API at `https://api.uncensored.com/api/v1`: `X-api-key` auth (never-expiring `uai_sk_live_` key), `POST /chat/completions` with streaming SSE and native tool-calling, and the full live metered catalog (82 models across 15 providers, discovered from the public `GET /v1/models`). Registered as provider `uc-direct` (alias `ucd`). Complements the un-metered `uc` persona provider — same models, metered credits and a plain API key instead of a subscription session. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(providers): add UC (uncensored.com), the un-metered subscription "persona" chat as an OpenAI-compatible provider.** A WebSocket web-app port: a durable Clerk credential mints a short-lived session token per connect (browserless — no API key), driving UC's persona socket. Ships the browserless email-code login (request → verify → harvest), the 19 verified persona models (Claude Opus, Gemini, Grok, GLM, Kimi, DeepSeek, MiniMax, incl. the uncensored variants), prompted `` tool-calling with a per-model code-style dialect + auto-cure retry for guardrailed models, live ``/reasoning split, streaming + non-streaming OpenAI responses, and full quota/auth error surfacing (paywall / message-limit / rate-limit → 429, invalid session → 401 re-login). Full multimodal parity via the persona blob-upload layer: **vision** (image input, 15 vision-capable models), **document RAG** (PDF/doc upload, server-side extraction), **image generation** (22 models), **video generation** (14 models, async signed-url → poll), and **TTS** (streaming MP3). Registered as provider `uc` (alias `ucn`). The metered OpenAI-compatible Developer API is a separate `uc-direct` provider. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(logs):** add copy-all button to request detail modal ([#11083](https://github.com/diegosouzapw/OmniRoute/pull/11083)) — thanks @AndrianBalanescu +- **feat(check):** make serviceKinds required and add the reverse-walk provider consistency gate ([#11392](https://github.com/diegosouzapw/OmniRoute/pull/11392)) — thanks @Tushar49 +- **feat(providers):** only advertise verified-working connections by default ([#11448](https://github.com/diegosouzapw/OmniRoute/pull/11448)) — thanks @alltomatos +- **feat(resilience):** add hierarchical concurrency admission ([#11493](https://github.com/diegosouzapw/OmniRoute/pull/11493)) +- **feat(providers):** derive + surface expiry for JWT-bearing web cookies (#11497) ([#11505](https://github.com/diegosouzapw/OmniRoute/pull/11505)) — thanks @oyi77 +- **feat(tunnels):** support named cloudflared tunnels via CLOUDFLARED_CONFIG ([#11561](https://github.com/diegosouzapw/OmniRoute/pull/11561)) — thanks @ggiak +- **feat(compression):** make proactive context-compression threshold a live setting ([#11564](https://github.com/diegosouzapw/OmniRoute/pull/11564)) — thanks @ragnar-claude +- **feat(resilience):** add provider peak-hour protection ([#11622](https://github.com/diegosouzapw/OmniRoute/pull/11622)) — thanks @Neuron-Mr-White +- **feat(providers):** add 1min.ai provider ([#11631](https://github.com/diegosouzapw/OmniRoute/pull/11631)) — thanks @zero-executioner +- **feat(dashboard):** collapsible JSON tree viewer for request/response payloads ([#11703](https://github.com/diegosouzapw/OmniRoute/pull/11703)) — thanks @hartmark +- **feat(routing):** add LiquidAI LFM2.5-2.6B free tier via OpenRouter ([#11752](https://github.com/diegosouzapw/OmniRoute/pull/11752)) — thanks @brick30llc-ctrl +- **feat(pricing):** add GLM-5.3-Flash pricing, model specs, and catalog registration ([#11830](https://github.com/diegosouzapw/OmniRoute/pull/11830)) — thanks @AndrianBalanescu +- **feat(routing):** add relayMode for schema-locked context handoffs ([#11839](https://github.com/diegosouzapw/OmniRoute/pull/11839)) — thanks @raheemuddin786 +- **feat(config):** add support for runtime system prompt configuration and hot-reloading ([#11841](https://github.com/diegosouzapw/OmniRoute/pull/11841)) — thanks @raheemuddin786 +- **feat(routing):** expand connection-aware quota prefilter across combo strategies (#11682) ([#11850](https://github.com/diegosouzapw/OmniRoute/pull/11850)) — thanks @HouMinXi +- **feat(models):** reactive discovery sync after Antigravity model-not-found 404 ([#11888](https://github.com/diegosouzapw/OmniRoute/pull/11888)) — thanks @MumuTW +- **feat(models):** auto-aliases and token limits from synced Antigravity discovery ([#11889](https://github.com/diegosouzapw/OmniRoute/pull/11889)) — thanks @MumuTW +- **feat(settings):** raise sticky round-robin limit caps to 1000 ([#12015](https://github.com/diegosouzapw/OmniRoute/pull/12015)) — thanks @adivekar-utexas +- **feat(resilience):** operator-configurable global credential health check interval ([#12043](https://github.com/diegosouzapw/OmniRoute/pull/12043)) — thanks @adivekar-utexas +- **feat(quota):** use official OpenCode Go usage API ([#12124](https://github.com/diegosouzapw/OmniRoute/pull/12124)) — thanks @ddarkr +- **feat(resilience):** default the credential health check sweep to 60 minutes ([#12138](https://github.com/diegosouzapw/OmniRoute/pull/12138)) — thanks @adivekar-utexas +- **feat(quality):** new-code mode for the complexity and dead-code ratchets (Clean as You Code) ([#12142](https://github.com/diegosouzapw/OmniRoute/pull/12142)) +- **feat(dashboard):** orchestration canvas — unified model + snapshot hook (part 1/2) ([#12156](https://github.com/diegosouzapw/OmniRoute/pull/12156)) +- **feat(usage):** add Kilo Code balance and Kilo Pass quotas ([#12178](https://github.com/diegosouzapw/OmniRoute/pull/12178)) — thanks @killer30001000 +- **feat(providers):** refresh vendored ChatGPT Web connector to v4.0.7 ([#12181](https://github.com/diegosouzapw/OmniRoute/pull/12181)) — thanks @backryun +- **feat(providers):** manual "Clear cooldown" action in the cooling panel ([#12224](https://github.com/diegosouzapw/OmniRoute/pull/12224)) — thanks @patrykkopycinski +- **feat(providers):** restore ChatGPT Web via clean-room browser transport ([#12239](https://github.com/diegosouzapw/OmniRoute/pull/12239)) — thanks @backryun +- **feat(sse):** wire the PROVIDER_PROFILES window gate into the global provider cooldown ([#12247](https://github.com/diegosouzapw/OmniRoute/pull/12247)) +- **feat(providers):** modernize CLOVA Studio chat and embeddings ([#12277](https://github.com/diegosouzapw/OmniRoute/pull/12277)) — thanks @backryun +- **feat(browser):** adopt Obscura as primary headless browser engine with Chromium fallback ([#12286](https://github.com/diegosouzapw/OmniRoute/pull/12286)) — thanks @opensource-elearning +- **feat(sse):** full language parity for output styles (es/de/fr/it/ru/zh + autoDetect) ([#12289](https://github.com/diegosouzapw/OmniRoute/pull/12289)) +- **feat(quality):** complete test:scoped — --full map rebuild, stdin selection, CI loader parity (#8084 D1) ([#12353](https://github.com/diegosouzapw/OmniRoute/pull/12353)) +- **feat(providers):** refresh Fable, Cursor, and Devin catalogs ([#12367](https://github.com/diegosouzapw/OmniRoute/pull/12367)) — thanks @backryun +- **feat(video):** redact transcript text from logs and durable memory (#12150 P1) ([#12427](https://github.com/diegosouzapw/OmniRoute/pull/12427)) +- **feat(dashboard):** parent-link, genuine-continuation badge, and modal perf fixes ([#12448](https://github.com/diegosouzapw/OmniRoute/pull/12448)) — thanks @hartmark +- **feat(dashboard):** adaptive context-budget dial on compression panel ([#12488](https://github.com/diegosouzapw/OmniRoute/pull/12488)) — thanks @HouMinXi +- **feat(providers):** refresh Z.ai Web models and browser transport ([#12524](https://github.com/diegosouzapw/OmniRoute/pull/12524)) — thanks @backryun +- **feat(video):** redact raw client-snapshot transcript fields in the detailed log (#12150 P2) ([#12528](https://github.com/diegosouzapw/OmniRoute/pull/12528)) +- **feat(providers):** refresh NVIDIA hosted models ([#12538](https://github.com/diegosouzapw/OmniRoute/pull/12538)) — thanks @backryun +- **feat(quota):** Moonshot Open Platform balance and TPD lock for custom nodes ([#12590](https://github.com/diegosouzapw/OmniRoute/pull/12590)) — thanks @HouMinXi +- **feat(video):** redact transcript in the in-memory pending-request snapshot (#12430 item 6) ([#12596](https://github.com/diegosouzapw/OmniRoute/pull/12596)) +- **feat(dashboard):** link a log entry's Conversation Context to its owning conversation ([#12646](https://github.com/diegosouzapw/OmniRoute/pull/12646)) — thanks @hartmark +- **feat(catalog):** eligibility-gated free-tier bucket ([#12669](https://github.com/diegosouzapw/OmniRoute/pull/12669)) +- **feat(db):** fail-closed previous_response_id continuation for redacted video turns (#12150 P2b) ([#12707](https://github.com/diegosouzapw/OmniRoute/pull/12707)) +- **feat(gamification): pay the documented `streak_bonus` and `badge_unlock` XP rewards.** `XP_REWARDS` listed both rewards but the award pipeline never paid them: the private reward table in `events.ts` omitted them, `updateStreak()` did not report when a streak extended, and badge unlocks carried no XP. Every request that extends a daily streak now pays `streak_bonus × streak length` once per UTC day (guarded by a same-day `xp_audit_log` check), and every badge unlocked through the pipeline pays `badge_unlock` once per badge (guarded by the `user_badges` primary key; `unlockBadge()` now reports whether it inserted). Bonus XP flows through the same `addXp` + level sync + global/weekly/monthly leaderboard path as action XP, so level-ups and rankings include it. The Radar supporter recognition unlock stays XP-free. (#12522 — thanks @pacocartones) +- **feat(providers):** advertise a `free-tier` capability in the provider plugin manifest for every provider with documented free models, so sidecars and dashboards can filter free-capable providers without reading the quota catalog ([#12786](https://github.com/diegosouzapw/OmniRoute/pull/12786)) — thanks @maxmad64bis +- **feat(providers):** Added EURouter as an OpenAI-compatible API-key gateway (`https://api.eurouter.ai/v1`), with live model discovery via `passthroughModels`. Its copy states that models are served by third-party upstreams listed per model, so an EU-based router is not read as EU data residency for inference. ([#13025](https://github.com/diegosouzapw/OmniRoute/pull/13025)) — thanks @ntdatt812 +- **feat(providers):** Added GreenPT as an OpenAI-compatible API-key provider (`https://api.greenpt.ai/v1`), with live model discovery via `passthroughModels`. No free-inference badge: the published docs describe a free API subscription billed per token, not a free tier. ([#13024](https://github.com/diegosouzapw/OmniRoute/pull/13024)) — thanks @ntdatt812 +- **feat(i18n):** 9 new locales — Greek (`el`), Croatian (`hr`), Serbian (`sr`), Lithuanian (`lt`), Estonian (`et`), Latvian (`lv`), Slovenian (`sl`), Maltese (`mt`), Irish (`ga`) — across the dashboard, docs mirrors, CLI, README and the site; OmniRoute now ships every official EU language (51 locales). (#13044) +- **feat(i18n):** 8 new locales — Kannada (`kn`), Malayalam (`ml`), Odia (`or`), Punjabi (`pa`), Nepali (`ne`), Sinhala (`si`), Burmese (`my`), Khmer (`km`) — across the dashboard, docs mirrors, CLI, README and the site (59 locales). The translator now restores the ICU literal escape around angle placeholders and splits oversized docs sections before translating. (#0000) ([#13660](https://github.com/diegosouzapw/OmniRoute/pull/13660)) +- feat(providers): **list Agnes 3.0 Flash as the current free chat model, drop retired 1.5 Flash, add Image 2.0/2.5 Flash plus Video 2.5/2.5 Flash, and discover the live `/v1/models` catalog (including the CN host `api.agnes-ai.cn`).** `agnes-1.5-flash` now forwards to `agnes-3.0-flash`. Video 2.5 polls `GET /v1/videos/{id}` (not the V2.0 `/agnesapi` contract). Live `/v1/models` (2026-09-09) no longer serves 1.5; the wiki marks it deprecated. 3.0 Flash is 512K context / 65,536 max output, same window as 2.5. CN-region keys use the existing per-connection base-URL field, default stays `apihub.agnes-ai.com`. ([#13120](https://github.com/diegosouzapw/OmniRoute/pull/13120)) — thanks @HouMinXi +- **feat(sse):** Codex and OpenAI catalogs list GPT-6 Astra with effort aliases (`-low` through `-ultra`); Codex CLI identity pins `@openai/codex@0.153.4` in lockstep with the image ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **feat(dashboard):** the orchestration History tab gained a "Compare runs" mode — toggling it turns each grid cell into a 2-item selection queue (a 3rd click drops the oldest pick), and picking two cells opens a side-by-side comparison panel instead of the usual detail drawer. The panel fetches both runs' detail the same way the drawer does (falling back to persisted history once a run leaves the live TTL window) and shows, per side: identity/source/state, start time, a signed `right - left` delta for duration/cost/event count, the event timeline aligned by index, and any memory hits. One side's fetch failing never blocks the other, and a delta is only ever computed when both sides have a finite value — otherwise it renders "—", never `NaN`. Comparing two runs from different sources or skills still works; a banner marks the deltas as informational rather than hiding them, since the two runs aren't a strict apples-to-apples pair. ([#12677](https://github.com/diegosouzapw/OmniRoute/pull/12677)) +- **feat(dashboard):** badge a conversation that never reached a clean stop ([#12717](https://github.com/diegosouzapw/OmniRoute/pull/12717)) — thanks @hartmark +- **feat(release):** reconcile-changelog tool + version-anchored fragment aggregation ([#12987](https://github.com/diegosouzapw/OmniRoute/pull/12987)) +- **feat(mcp):** carry context_length through MCP list_models_catalog projection (#12776) ([#13406](https://github.com/diegosouzapw/OmniRoute/pull/13406)) — thanks @KooshaPari +- **feat(cli):** make startup readiness budget configurable (#13369) ([#13433](https://github.com/diegosouzapw/OmniRoute/pull/13433)) — thanks @KooshaPari + ### 🐛 Bug Fixes +- **security(streaming):** sanitize generic mid-stream error messages before emitting OpenAI, + Responses, or Claude SSE failure frames and before diagnostic logging, while preserving raw + failures for internal classification and keeping client disconnects out of provider failure state. ([#12457](https://github.com/diegosouzapw/OmniRoute/pull/12457)) +- **fix(claude):** `blockExtraUsage: false` no longer lets 5h quota preflight skip the account; extra usage is billed after the session bar is gone, so the request must reach Anthropic ([#12803](https://github.com/diegosouzapw/OmniRoute/pull/12803)) — thanks @HouMinXi +- Sanitize HuggingChat conversation-creation and message-send transport failures before they reach client error bodies or provider logs. ([#12467](https://github.com/diegosouzapw/OmniRoute/pull/12467)) +- **fix(dashboard):** Keep local and theme-aware provider SVG icons at a definite layout size so Chromium does not collapse them to 0×0 after the v3.8.50 image-rendering change ([#12054](https://github.com/diegosouzapw/OmniRoute/pull/12054)) — thanks @ponkcore +- **fix(security):** Sanitize provider and runtime failures before public API, SSE and MCP responses and before persistent request, proxy and usage logs, preventing credentials, stack traces and host filesystem paths from crossing those boundaries while preserving stable error codes and useful diagnostics. ([#12506](https://github.com/diegosouzapw/OmniRoute/pull/12506)) +- **fix(cli):** `omniroute tunnel create` no longer crashes with `Cannot read properties of undefined (reading optsWithGlobals)` — removed the duplicate positional argument that caused Commander.js to misalign the action callback parameters ([#12295](https://github.com/diegosouzapw/OmniRoute/issues/12295)) ([#12368](https://github.com/diegosouzapw/OmniRoute/pull/12368)) — thanks @gonisulaimann +- Stop advertising Gemini Live-only models as supported audio endpoints until OmniRoute proxies the bidirectional Live protocol. ([#11710](https://github.com/diegosouzapw/OmniRoute/pull/11710)) — thanks @RaviTharuma +- **fix(kie):** reroute `flux/kontext` off the KIE Market `createTask` flow — it is catalogued with `isMarket: true` but has no Market catalog page, so KIE rejected it with "model name not supported"; it now hits the dedicated `POST /api/v1/flux/kontext/generate` / `GET /api/v1/flux/kontext/record-info` endpoints instead (#11296). ([#11985](https://github.com/diegosouzapw/OmniRoute/pull/11985)) — thanks @hartmark +- **fix(kie):** correct 12 more KIE Market catalog ids that were sent to `createTask` unchanged but diverge from KIE's documented upstream `model` values — GPT Image 2 T2I/I2I (drops the `gpt/` prefix), GPT Image 1.5 T2I/I2I (`gpt-image/` namespace), Seedream 5.0 Lite T2I/I2I (drops the `.0`), all 4 Flux 2 variants (`flux-2/` namespace, generic variant renamed `flex`), and Wan 2.7 Image / Image Pro (dash instead of dot) — each verified individually against the literal example request published on docs.kie.ai. `#11326`'s "everything else already matches" claim was wrong a second time (#11296); `z-image/4.0-*`/`z-image/4.5-*` and `flux/kontext` remain open, documented as unresolved in `KIE_MARKET_UPSTREAM_MODEL_IDS`'s comment pending further verification. ([#11667](https://github.com/diegosouzapw/OmniRoute/pull/11667)) +- **fix(dashboard):** `useApiKeySave.handleSaveApiKey` no longer forces a full upstream `/models` catalog sync on every non-curated provider connection save — callers can now pass `skipModelSync: true` to opt out, so a workflow that only wants to add one manual model no longer floods the provider's available-models list with hundreds/thousands of synced entries. The flag is a client-side intent signal only and is stripped before the connection payload is POSTed to `/api/providers`; default behavior (full sync on save) is unchanged when the flag is omitted (#11324) ([#11664](https://github.com/diegosouzapw/OmniRoute/pull/11664)) +- **Provider connections:** keep `tokenExpiresAt` when a connection is created. The create-path allowlist omitted it, so every insert stored NULL and the dashboard token badge could read a fresh connection as expired until its first background refresh ([#11368](https://github.com/diegosouzapw/OmniRoute/pull/11368)). — thanks @ntdatt812 +- **fix(combos):** the combo builder's precision-select, global-model-search, and manual-entry flows now serialize a model step's `model` string using the provider's already-computed routing-alias prefix (e.g. `oc/`) instead of rebuilding it from the raw canonical `providerId`, fixing the no-auth "OpenCode Free" provider (`opencode`) being routed to the unrelated paid "OpenCode Zen" provider (`opencode-zen`) because `opencode` doubles as a manual routing-prefix override ([#11433](https://github.com/diegosouzapw/OmniRoute/issues/11433)). ([#11670](https://github.com/diegosouzapw/OmniRoute/pull/11670)) +- **fix(radar):** the catalog feed cache now keeps `generatedAt`, the date the feed's data was built, next to `fetchedAt`, the date this install downloaded it (#11435). The feed schema requires that date and the sync path validates it, but the cache dropped it — so a feed fetched minutes ago and one carrying weeks-old figures looked identical to everything downstream, including the dashboard's "Last fetched" line. `getRadarCatalog().meta` and `GET /api/radar/status` now report both dates, the latter as its own field rather than folded into `version` — and omitted entirely for the offers and intel caches, which keep no build date, where a `null` would read as "unknown" rather than "never stored". The dashboard still shows only the fetch time; surfacing the build date there needs a new translated label and is left to a follow-up. Rows cached before migration 163 read back as `null`: unknown stays unknown instead of borrowing the fetch time. The referrals cache has persisted the same date since migration 142. — thanks @maxmad64bis +- **fix(cli):** postinstall no longer fills `JWT_SECRET` and `API_KEY_SECRET` in the installed package's `.env` (#11436). `.env.example` ships both blank on purpose: the server restores them from its durable store, or generates and persists them there, in `ensureSecrets()`. Pre-filling them defeated that — the file lives inside the package directory, so `npm i -g` replaced it and postinstall wrote _different_ values, while `ensureSecrets()` (which only acts on an empty variable) never got to restore the real ones. Both secrets rotated silently on every update, invalidating dashboard sessions and API-key CRCs. `STORAGE_ENCRYPTION_KEY` left the same list for the same reason in #1622; its comment pointed at a function that no longer exists and now names the real provisioning path. — thanks @maxmad64bis +- **fix(cli):** the CLI now says when a loaded `.env` lives inside the installed package directory (#11437). It already announces every env file it reads, without distinguishing the ones that survive an update from the one that does not: `npm i -g` replaces the package directory wholesale, so values set there are gone at the next update, silently. The warning names the durable path to move them to, and fires only when that file actually supplied a value — a file entirely shadowed by a durable one supplied nothing. A development checkout stays silent: there the same path is stable and documented in `SETUP_GUIDE.md`. — thanks @maxmad64bis +- **fix(free-models):** the shared `isFreeModel()` predicate no longer reports catalog entries marked `freeType: "discontinued"` as free, so `hidePaidModels` can't route to Pollinations' seven premium models that now require a paid key ([#11441](https://github.com/diegosouzapw/OmniRoute/pull/11441)) — thanks @maxmad64bis +- fix(ui): let AnySearch use the normal provider-icon fallback when LobeHub has no matching icon (#11449) +- Fixed the v3.8.50 Costs and Analytics dashboards so flat-rate Claude Code usage can be shown as an explicitly requested token-price estimate without changing default billed-cost semantics. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed archived usage retention so each request is priced individually instead of pricing a day's summed tokens once, which understated archived cost whenever a day mixed cache-heavy and ordinary requests. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed the Costs dashboard so it discloses when displayed figures include flat-rate token-price estimates instead of labelling them as billed spend, using the flag the analytics API already returns; the month-end projection and the CSV/JSON exports carry the same marker, and billed-cost mode is unchanged. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- **fix(combo):** attach the same combo-diagnostics trace (`poolSize`/`attemptOrder`/`excluded`/`terminalReason`, plus `x-omniroute-combo-*` headers) to the round-robin strategy's and the nested pipeline/fusion runtime-unit loop's "Maximum combo retry limit reached" 503 that the priority-strategy path already attaches for the identical terminal condition — previously those two paths returned a bare, contextless 503 ([#11462](https://github.com/diegosouzapw/OmniRoute/issues/11462)). ([#11665](https://github.com/diegosouzapw/OmniRoute/pull/11665)) +- **fix(bun):** use native `bun:sqlite` in `bootstrap-env` and `sync-env` to avoid loading `better-sqlite3` N-API addon during Bun startup ([#11468](https://github.com/diegosouzapw/OmniRoute/pull/11468)) — thanks @TheDemonTuan +- **fix(docker-bun):** make `Dockerfile.bun` install reliable with pre-install script helpers and native `bun:sqlite` smoke check ([#11470](https://github.com/diegosouzapw/OmniRoute/pull/11470)) — thanks @TheDemonTuan +- **fix(docker-bun):** remove every vendored `better-sqlite3` native addon from the Bun runtime image so startup cannot abort before the `bun:sqlite` fallback ([#11482](https://github.com/diegosouzapw/OmniRoute/pull/11482)) — thanks @TheDemonTuan +- **fix(autoCombo):** effort/alias model variants (`gpt-5.6-sol-xhigh`, `gpt-5.6`, cursor's `claude-4.6-opus-high`) inherit their base model's task fitness instead of falling to the wildcard 0.5 ([#11492](https://github.com/diegosouzapw/OmniRoute/pull/11492)) — thanks @MumuTW +- fix(db): rate-limit repeated Arena ELO leaderboard fetch-failure warnings instead of logging one per sync attempt (#11500) ([#11989](https://github.com/diegosouzapw/OmniRoute/pull/11989)) — thanks @hartmark +- **fix(db):** dedupe the raw `[Encryption] Decryption failed...` log line emitted by the lazy-decrypt views (`createLazyRowProxy`/`createLazyConnectionView`), which power `getProviderConnections()` and were re-triggering that line on every CredentialHealth/model-sync cycle for the same corrupt or stale-key credential — a fresh Proxy over a fresh row on every cycle meant the per-proxy memoization never suppressed it, unlike the dedup `decryptConnectionFields()` already had since [#9927](https://github.com/diegosouzapw/OmniRoute/issues/9927). Now shares that dedupe tracking so the line logs at most once per credential ([#11500](https://github.com/diegosouzapw/OmniRoute/issues/11500)). ([#11678](https://github.com/diegosouzapw/OmniRoute/pull/11678)) +- **fix(intelligence):** Arena ELO sync stores a synthesized base-model row for every effort/harness variant (`claude-opus-5-max` → `claude-opus-5`, `gpt-5.6-sol-xhigh (codex-harness)` → `gpt-5.6-sol`), so bare model requests reach the synced intelligence layer instead of the static fallback table; the hand-maintained `MODEL_ALIAS_MAP` — which collapsed generations (`gpt-5.5` → `gpt-5`) — is gone ([#11506](https://github.com/diegosouzapw/OmniRoute/pull/11506)) — thanks @MumuTW +- **fix(autoCombo,sse):** catalog hygiene — the static task-fitness table no longer ranks retired models above live ones (dead rows dropped, patterns matched on segment boundaries), the 7 deprecation aliases that forwarded to retired or non-existent models now point at the vendors' published replacements, an alias is no longer rewritten when the serving provider offers the id as-is, and a new `check:model-lifecycle` gate diffs both tables against a vendor lifecycle snapshot ([#11507](https://github.com/diegosouzapw/OmniRoute/pull/11507)) — thanks @MumuTW +- **fix(sse):** stop dropping resolved `thoughtSignature` values on parallel (multi tool-call) turns sent to Gemini 3.x — the claude→gemini and openai→gemini translators previously kept the signature only on the *first* function call of a message, causing Gemini to reject subsequent calls in the same turn with HTTP 400 "Function call is missing a thought_signature"; each function call now keeps its own resolved signature ([#11510](https://github.com/diegosouzapw/OmniRoute/issues/11510)). ([#11669](https://github.com/diegosouzapw/OmniRoute/pull/11669)) +- Treat Docker publishing from a non-default release branch as an expected skip instead of a failed workflow, while preserving the guard that prevents it from overwriting the `next` channel. ([#11527](https://github.com/diegosouzapw/OmniRoute/pull/11527)) — thanks @pacocartones +- fix(search): prefer credentialed providers over duckduckgo-free fallback (#11524) — the fallback-only loop ran before the credentialed-providers loop in `executeWebSearch`, making configured providers unreachable when `duckduckgo-free` was available. ([#11565](https://github.com/diegosouzapw/OmniRoute/pull/11565)) — thanks @vsd2807 +- **fix(sse):** cap the upstream headers-wait phase for STREAMING requests to a client-realistic ceiling (110s, under Codex's own ~120s hard client-abort window) instead of the flat 10-minute `FETCH_TIMEOUT_MS` default — that default was 5x longer than the body-phase readiness watchdog's own adaptive bound, so a request whose upstream never returned any response at all (not even headers, e.g. a stalled NVIDIA target behind a tool-heavy Responses→Chat translation) kept the client connection alive on keepalives only, guaranteeing the client's own patience ran out first with an opaque 499 instead of OmniRoute detecting and failing the stall fast. Non-streaming requests are unaffected — they keep the existing flat default (`open-sse/utils/fetchStartTimeoutPolicy.ts`) (#11526) ([#11668](https://github.com/diegosouzapw/OmniRoute/pull/11668)) +- **fix(ci):** protocol-clients E2E harness boots the peer-stamped custom server (`run-next.mjs dev`) instead of the bare `next` CLI, so `LOCAL_ONLY` locality resolves from the real TCP peer stamp and `/api/mcp/audit` answers 200 (open bootstrap preserved via an `OMNIROUTE_E2E_BOOTSTRAP_MODE=open` credential clear in `run-next.mjs`, pinned to loopback `HOST`) instead of a deterministic 403 (#11535) ([#11549](https://github.com/diegosouzapw/OmniRoute/pull/11549)) — thanks @jonlwheat2-gif +- **fix(docs):** the Auto-Combo engine is described with the number of factors it actually declares. Four different counts were in circulation — 6, 9, 13 and 14 across `AGENTS.md`, four guides and six code comments — while `DEFAULT_WEIGHTS` declares 15. `check:docs-counts` now reads that number from the source, so adding a factor without updating the prose fails CI ([#11545](https://github.com/diegosouzapw/OmniRoute/pull/11545)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **fix(quota-share):** in-flight leases are per request, so an aborted request's slot ages out instead of being kept alive by later traffic on the same connection ([#11547](https://github.com/diegosouzapw/OmniRoute/pull/11547)) — thanks @abhisheksharma2411 +- The free-tier summary route now serves the refreshed Radar catalog when the feed is active — the same numbers the dashboard shows — and states which catalog answered plus its real build date, instead of always reporting release-frozen figures with a stale curation date. The supporter-key live feed stays reserved to authenticated callers of the instance. ([#11550](https://github.com/diegosouzapw/OmniRoute/pull/11550)) — thanks @maxmad64bis +- **sse:** fix LiveWS/embed-WS servers crashing at startup under the Node/tsx runtime — `liveServer.ts`, `embedWsProxy.ts` and `apiBridgeServer.ts` imported `@/shared/utils/httpClientAbortGuard` without the `.mjs` extension, so the client-abort crash guard added by [#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556) was unreachable and every dependent test failed with `ERR_MODULE_NOT_FOUND` ([#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556)). ([#11671](https://github.com/diegosouzapw/OmniRoute/pull/11671)) — thanks @jonlwheat2-gif / @yxyxy +- fix(chat): shed any of the matched provider-node's routing identifiers (public prefix or internal id) before model lookup, so composites like `//` no longer reach upstream verbatim (#11557) — thanks @maxmad64bis +- **fix(cache):** a semantic-cache entry now expires on its TTL instead of surviving until the next UTC midnight, and `dbEntries` stops counting expired rows ([#11573](https://github.com/diegosouzapw/OmniRoute/pull/11573)) — thanks @ntdatt812 +- **fix(api):** `/v1/models` schedules its stale-while-revalidate rebuild through Next's `after()` again, so a stale catalog reaches the client before the rebuild blocks the event loop ([#11574](https://github.com/diegosouzapw/OmniRoute/pull/11574)) — thanks @ntdatt812 +- **fix(check):** the `known-symbols` executor conformance gate awaits the now-async `getExecutor()`, so it stops reporting all 142 registered aliases as dead symbols and can detect a lazy import that fails ([#11582](https://github.com/diegosouzapw/OmniRoute/pull/11582)) — thanks @ntdatt812 +- **fix(pack):** register `bin/cli/utils/volatileEnvPath.mjs` in `PACK_ARTIFACT_REQUIRED_PATHS`, so the prune can no longer silently drop a module the published CLI imports on every boot ([#11588](https://github.com/diegosouzapw/OmniRoute/pull/11588)) — thanks @ntdatt812 +- **i18n(vi):** translate the five reliability-column strings on the free-provider rankings page, which rendered as `__MISSING__:` markers in the Vietnamese UI ([#11589](https://github.com/diegosouzapw/OmniRoute/pull/11589)) — thanks @ntdatt812 +- **resilience:** restore the expired-connection retry-budget probe in the token-health sweep — the `!isGitHubAccessTokenOnlyConnection` carve-out reintroduced by #11608 contradicted the boundary pinned by #11592, so a GitHub connection parked at `expired` with retry budget remaining was never probed and could never self-heal ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)). ([#11672](https://github.com/diegosouzapw/OmniRoute/pull/11672)) — thanks @jonlwheat2-gif / @ntdatt812 +- **fix(dashboard):** Provider Stats now displays combo success rates on their native 0–100 percentage scale instead of multiplying them twice ([#11595](https://github.com/diegosouzapw/OmniRoute/pull/11595)) — thanks @pacocartones +- **fix(providers):** route DuckDuckGo Web Chat through the verified `duck.ai` endpoint, refresh its free model catalog, and avoid forwarding unsolved anti-abuse challenges ([#11597](https://github.com/diegosouzapw/OmniRoute/pull/11597)) — thanks @solstxce +- **fix(ui):** Console log Refresh and Copy controls now expose localized accessible names, keep copy actions visible on keyboard focus, and announce copy completion safely ([#11599](https://github.com/diegosouzapw/OmniRoute/pull/11599)) — thanks @pacocartones +- Dropped the stale-`.eslintcache` `restore-keys` fallback from both "Restore ESLint file cache" steps in `ci.yml`, so the blocking `Lint` job can no longer be served per-file verdicts computed under a different lint config, suppressions file or lockfile. `quality.yml` had already dropped it in #11963; `ci.yml` — the workflow that actually gates PRs — had not (#11600). ([#11996](https://github.com/diegosouzapw/OmniRoute/pull/11996)) +- fix(ci): pass `--pass-on-unpruned-suppressions` in `run-eslint-json.mjs` so the CI Lint job no longer fails when a suppression is merely orphaned by a genuine fix, mirroring the identical fix already in `validate-release-green.mjs` (#11600) ([#11983](https://github.com/diegosouzapw/OmniRoute/pull/11983)) — thanks @hartmark +- fix(handoff): enforce provider allowlist for universal handoff (#11602) — universal handoff now skips summarization when the selected summary model's provider is not included in the configured provider allowlist. ([#11619](https://github.com/diegosouzapw/OmniRoute/pull/11619)) — thanks @vsd2807 +- **fix(dashboard):** Show a stable error state when Search Analytics returns an HTTP error or malformed data ([#11603](https://github.com/diegosouzapw/OmniRoute/pull/11603)) — thanks @pacocartones +- **test(gamification):** pin the aggregate profile level to the XP-derived semantics of #11604 — `getAggregateXp()` now derives `currentLevel` from the summed XP (`calculateLevel(sum)`), not `MAX(stored current_level)`, and the #3484 fixture levels are aligned with the XP curve ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)). ([#11676](https://github.com/diegosouzapw/OmniRoute/pull/11676)) — thanks @jonlwheat2-gif / @pacocartones +- **fix(dashboard):** Keep the Profile level and progress aligned with aggregate XP, including bounded handling for invalid totals ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)) — thanks @pacocartones +- **fix(dashboard):** Prevent locked hidden badges from revealing their icon or opening private badge details before they are earned ([#11605](https://github.com/diegosouzapw/OmniRoute/pull/11605)) — thanks @pacocartones +- **fix(dashboard):** Restore keyboard focus after shared modals close and cancel delayed autofocus during cleanup ([#11607](https://github.com/diegosouzapw/OmniRoute/pull/11607)) — thanks @pacocartones +- **fix(dashboard):** Enable Enter and Space activation for clickable data-table rows without hijacking nested controls ([#11610](https://github.com/diegosouzapw/OmniRoute/pull/11610)) — thanks @pacocartones +- **fix(autoCombo,sse):** vendor-retired catalog ids are dropped from the auto-combo candidate pool and no longer win on leftover `arena_elo` / `user_override` rows; `getModelLifecycleDecision` consults `model-lifecycle.json` (prefix-stripped) so aggregator traffic is not `untracked` for ids the snapshot already knows ([#11625](https://github.com/diegosouzapw/OmniRoute/issues/11625)) ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)) — thanks @MumuTW +- **test(chatcore):** move Codex/Claude combo fixtures off models the lifecycle guard now rejects — `gpt-5.1-codex`/`gpt-5-codex` are vendor-retired (snapshot, #11626) and `claude-3-5-sonnet-20241022` is shut down, so native-passthrough and combo-fallback tests switched to `gpt-5.6-sol` and `claude-sonnet-4.6` ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)). ([#11675](https://github.com/diegosouzapw/OmniRoute/pull/11675)) — thanks @jonlwheat2-gif / @MumuTW +- fix(opencode-plugin): stop a free-tier budget that rounds up across a magnitude from rendering as `1000K`/`1000M` in the model picker — `fmtTokens` chose its unit from the raw token count and then rounded with `toFixed(1)`, so 999,950–999,999 printed as `1000K` rather than `1M` and just under a billion printed as `1000M` rather than `1B` ([#11684](https://github.com/diegosouzapw/OmniRoute/pull/11684)) — thanks @abhisheksharma2411 +- Cap prose-derived Antigravity quota resets at the configured model cooldown maximum while preserving authoritative Retry-After headers and Google RetryInfo hints. (#11695) Thanks @pacocartones. ([#11823](https://github.com/diegosouzapw/OmniRoute/pull/11823)) +- Compare `_rebaseline_*` notes structurally in the ratchet-bank verifier so an object-valued note no longer reports itself as rewritten on an unmodified tree — the nightly `bank-ratchet-shrinks` lane had been aborting on every run since 2026-08-11, leaving 41,672 lines of earned file-size shrinks unbanked (#11702). ([#11892](https://github.com/diegosouzapw/OmniRoute/pull/11892)) — thanks @MumuTW +- **fix(build):** `prepublish.ts` bundles the ChatGPT Web (Codex) MCP bridge through `runBuildTool()` instead of spawning `npx.cmd` raw, fixing the build crash on Node ≥ 20/Windows where `.cmd` shims cannot be spawned without a shell (EINVAL) ([#11704](https://github.com/diegosouzapw/OmniRoute/issues/11704)) ([#11706](https://github.com/diegosouzapw/OmniRoute/pull/11706)) — thanks @jonlwheat2-gif +- fix(codex): keep `parallel_tool_calls:false` on the translated Codex Responses Lite path (#11707) ([#11984](https://github.com/diegosouzapw/OmniRoute/pull/11984)) — thanks @hartmark +- **fix(tests):** the new Gemini Web Images retirement test (#11708) called the now-async `getExecutor()` synchronously, throwing `TypeError: getExecutor(...).getProvider is not a function` instead of asserting Gemini Web chat stays available. Awaited to match the R0.3 executor-registry refactor (#11220). ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(search):** Allow REST and MCP web-fetch requests to select the advertised `anysearch-search` provider explicitly ([#11716](https://github.com/diegosouzapw/OmniRoute/pull/11716)) — thanks @pacocartones +- **fix(providers):** Keep overnight peak-hour protection active after midnight according to the window's configured start day ([#11718](https://github.com/diegosouzapw/OmniRoute/pull/11718)) — thanks @pacocartones +- **fix(providers):** correct all five g4f.space gateway descriptions to document proof-of-work credits or member API keys, variable limits, and the remote prompt boundary instead of promising free keyless access at five requests per minute ([#11738](https://github.com/diegosouzapw/OmniRoute/pull/11738)). +- **fix(db):** `model_capabilities` is created by a migration instead of lazily on the first models.dev sync, so a clean install and an upgraded install converge on the same schema regardless of which features have run ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(ci):** `check:install-upgrade` now fails on an `npm` install truncated by ENOSPC (npm reports it as a warning and still exits 0), authenticates its health probe so the version assertion works against the hardened health payload, frees the clean-install tree before the upgrade phase, and no longer reports a schema divergence computed from a boot that never served ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(routing):** harden session-affinity key derivation against oversized explicit IDs and empty structural-payload collisions, while recognizing Gemini and common text request shapes ([#11746](https://github.com/diegosouzapw/OmniRoute/pull/11746)) — thanks @MumuTW +- **fix(packages/browser-pool):** regenerate `package-lock.json` so the `packages/browser-pool` workspace's locked `playwright`/`@types/node` (and transitives) match its `package.json` specs, fixing cache-only/offline installs (`npm ci --offline`, Nix `buildNpmPackage`) that previously failed with `ENOTCACHED` ([#11747](https://github.com/diegosouzapw/OmniRoute/issues/11747)) — thanks @benjaminkitt ([#11784](https://github.com/diegosouzapw/OmniRoute/pull/11784)) — thanks @geekyNads +- **fix(assets):** Stop distributing 79 provider and CLI logos without verified source/license provenance, keep every provider/tool available through the internal generic icon, and prevent those IDs from falling through to an external icon CDN. ([#11750](https://github.com/diegosouzapw/OmniRoute/pull/11750)) +- **fix(providers):** retire the common `chatgpt-web` / `cgpt-web` provider because its pre-key and proof-of-work provenance could not be cleared ([#11754](https://github.com/diegosouzapw/OmniRoute/pull/11754)) — explicit chat and image requests, slash-prefixed models, persisted aliases, proxy overrides, and combo targets now fail closed with a sanitized HTTP `410` / `PROVIDER_RETIRED` before upstream dispatch. Migration 163 tombstones matching connections and invalidates their active leases without deleting history or widening API-key allowlists. The independent `chatgpt-web-codex` / `cgpt-codex` provider and its MIT-noticed vendored implementation remain available. +- **fix(usage):** quota and usage refresh no longer 409 when an exclusive lease reserves the connection ([#11758](https://github.com/diegosouzapw/OmniRoute/pull/11758)) — thanks @TheDemonTuan +- Keep the embedding registry's vector width and `embedding` type on models when a synced model exists for the same id, so `/v1/models` no longer reports registry-described embedding models widthless or untyped (#11761) — thanks @rahilmavani +- Correct `google/gemini-embedding-001` on the OpenRouter route to 3072 dimensions, the width it returns when `dimensions` is not sent (#11761) — thanks @rahilmavani +- **fix(vision-bridge):** Restrict automatic vision routing and fallbacks to models available in authoritative live provider catalogs, including aliases and effort variants ([#11767](https://github.com/diegosouzapw/OmniRoute/issues/11767)) — thanks @pacocartones ([#11813](https://github.com/diegosouzapw/OmniRoute/pull/11813)) +- **fix(providers):** reclassify Cerebras as a one-time $5 signup credit (payment method required, 30-day validity), not a recurring no-card 1M tokens/day trial ([#11773](https://github.com/diegosouzapw/OmniRoute/issues/11773)) ([#12591](https://github.com/diegosouzapw/OmniRoute/pull/12591)) — thanks @HouMinXi +- fix(cli): stop prepublish from re-rebuilding the already-built ESM-only opencode-plugin dist (#11787) ([#11990](https://github.com/diegosouzapw/OmniRoute/pull/11990)) — thanks @hartmark +- **fix(db):** Fresh installs no longer log a non-fatal `no such table: compression_run_telemetry` warning when retention cleanup runs before the lazily-created telemetry table exists ([#11802](https://github.com/diegosouzapw/OmniRoute/pull/11802)) — thanks @RaviTharuma +- fix(sse): set X-OmniRoute-Selected-Connection-Id on successful combo dispatches so downstream consumers stop falling back to an empty connection id (#11810) ([#11986](https://github.com/diegosouzapw/OmniRoute/pull/11986)) — thanks @hartmark +- **fix(services):** embedded CLIProxyAPI lifecycle checks now use public `/healthz`, while model discovery uses the configured dedicated data-plane API key instead of the management password ([#11811](https://github.com/diegosouzapw/OmniRoute/pull/11811)) — thanks @RaviTharuma +- Fix persisted model steps returning an error in the playground route simulator, and warn when structural steps cannot be simulated. ([#11862](https://github.com/diegosouzapw/OmniRoute/pull/11862)) — thanks @pacocartones +- **fix(providers):** Antigravity's dynamic mitmAlias table no longer routes `gemini-3.7-flash-{high,medium,low}` to a literal tier-suffixed upstream id just because one connected account's own discovery listed it directly — those display ids always resolve through the safe `gemini-3.7-flash-tiered` static alias, so one account's Google-provisioned access no longer 404s every sibling account of the provider ([#11824](https://github.com/diegosouzapw/OmniRoute/issues/11824), [#11651](https://github.com/diegosouzapw/OmniRoute/issues/11651)) ([#11988](https://github.com/diegosouzapw/OmniRoute/pull/11988)) — thanks @hartmark +- **fix(dashboard):** Expose Profile loading, errors, page structure, and XP progress to assistive technologies ([#11838](https://github.com/diegosouzapw/OmniRoute/pull/11838)) — thanks @pacocartones +- **fix(socks):** `Agent.connectTimeout` now bounds both SOCKS handshake and TLS connect, and `family === null` no longer falls back to `fetch-socks` ([#11842](https://github.com/diegosouzapw/OmniRoute/pull/11842)) — thanks @maxmad64bis +- **perf(compression):** OOM mitigations for large payload hashing, memoization, and token estimation ([#11844](https://github.com/diegosouzapw/OmniRoute/pull/11844) — thanks @AndrianBalanescu) +- Excluded Next.js Node File Trace manifests (`*.nft.json`) from the published npm tarball. They are build-time metadata and are never read while serving, but had grown to 668.7 MB — 61% of the package — which pushed the upload past the registry limit and made `npm publish` fail with `413 Payload Too Large`. ([#11864](https://github.com/diegosouzapw/OmniRoute/pull/11864)) +- **fix(cli):** keep the global OmniRoute server `--base-url` separate from provider-node `--endpoint` values in add, update, and validate commands ([#11860](https://github.com/diegosouzapw/OmniRoute/pull/11860)) — thanks @pacocartones +- **fix(config):** Nous Research's `Hermes-4-405B` model now displays as "Hermes 4 405B (Nous Research)" in both the provider registry and the free-model catalog, instead of the mislabelled "Hermes 4 7B" ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#11993](https://github.com/diegosouzapw/OmniRoute/pull/11993)) +- **fix(provider/nous):** inject required user= tag into Nous Research inference requests to resolve upstream 400 "missing tags" error ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#12044](https://github.com/diegosouzapw/OmniRoute/pull/12044)) +- **fix(build):** `npm run build` now fails in one second with a named package and a copy-pasteable fix when npm silently drops an externalised optional native dependency, instead of dying four minutes in with `Module not found: Can't resolve 'better-sqlite3'` ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- **fix(install):** `postinstall` no longer throws `ReferenceError: isAndroid is not defined` — failing the whole `npm install` — when the `better-sqlite3` rebuild fallback times out; the manual-fix guidance is reachable again ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- Fixed the Alibaba free-tier allowlist test that went red on its own once the shipped catalog's `validUntil` (2026-08-27) passed, leaving every PR and `main` with a failing `Unit Tests (1/8)`. The test now builds its own packs with dates it controls, and covers the expired-pack fallback that production has actually been serving. ([#11867](https://github.com/diegosouzapw/OmniRoute/pull/11867)) +- Split the npm registry upload into its own GitHub-hosted job. npm refuses `--provenance` from a self-hosted runner (`422 ... Only "github-hosted" runners are supported`), which blocked the v3.8.50 publish; the heavy verification cannot move to a hosted runner, so it now hands the proven tarball over instead. ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(ui):** the shared `DataTable` loading state no longer reads its decorative ⏳ glyph out to assistive technology, and now carries the same `role="status"` / `aria-live="polite"` / `aria-busy="true"` semantics as `PageLoading` ([#11869](https://github.com/diegosouzapw/OmniRoute/pull/11869)) — thanks @pacocartones +- **fix(api):** Bound the `timeout` accepted by `POST /api/providers/[id]/login` to the same 15s–600s window the Adobe Firefly and Conol browser-login paths already enforce, so a request can no longer pin a headful browser and the single active-login slot with an arbitrarily large poll budget. ([#11871](https://github.com/diegosouzapw/OmniRoute/pull/11871)) — thanks @pacocartones +- **fix(api):** Stop the tunnel and MITM routes from returning raw child-process error text, which disclosed host paths, binary install locations and Tailscale `tskey-*` credentials in a body some of these routes serve to non-loopback callers, and return a real 400 instead of a framework 500 when a tunnel request body fails validation. ([#11872](https://github.com/diegosouzapw/OmniRoute/pull/11872)) — thanks @pacocartones +- **fix(open-sse):** stream TTFT/ITL are sampled from a monotonic clock (`performance.now`) instead of `Date.now`, so an NTP correction or wall-clock jump can no longer inflate a genuine TTFT or produce a negative one that the `ttft >= 0` guard silently drops — protecting the router's OTel/EWMA/`usage_history`/speed-ranking signals ([#11873](https://github.com/diegosouzapw/OmniRoute/pull/11873)) — thanks @pacocartones +- **fix(providers):** xAI `reasoning_effort: "xhigh"` now reaches grok-4.6+ instead of being silently clamped to `"high"` ([#11879](https://github.com/diegosouzapw/OmniRoute/pull/11879)) — thanks @NoxzRCW +- **fix(dashboard):** the providers page no longer crashes into the error boundary when a provider id collides with an `Object.prototype` member (`constructor`, `__proto__`); icon lookups are own-property guarded ([#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)) — thanks @NoxzRCW +- **fix(skills):** injected skill tools declared in shorthand (`{"content": "string"}`) now forward valid JSON Schema, unblocking providers that validate tool schemas strictly such as Zhipu GLM on the Console Go tier ([#11881](https://github.com/diegosouzapw/OmniRoute/pull/11881)) — thanks @NoxzRCW +- **fix(api):** `POST /api/playground/simulate-route` now surfaces `combo-ref` and `provider-wildcard` persisted combo steps with a specific warning (naming the referenced combo, or the unresolved `provider/model` wildcard) instead of folding them into a generic "unsupported step" count; a `provider-wildcard` step is also now included as an unresolved target so the operator can see it is in the route (ported from [#11882](https://github.com/diegosouzapw/OmniRoute/pull/11882) — thanks @NoxzRCW). ([#11930](https://github.com/diegosouzapw/OmniRoute/pull/11930)) +- **fix(translator):** the streaming OpenAI→Claude translator keeps upstream usage, including prompt-cache tokens, when it arrives on a trailing `choices: []` chunk (Fireworks and any upstream using `stream_options.include_usage`) ([#11883](https://github.com/diegosouzapw/OmniRoute/pull/11883)) — thanks @NoxzRCW +- **fix(cli):** `omniroute update --apply` now tells you explicitly whether a running server was detected and, if so, that you must run `omniroute restart` to apply the update — it never restarted anything and previously implied the update was already live once files were installed. The dashboard's npm-mode Update flow (`/api/system/version`) now tries OmniRoute's own PID-file-managed supervisor before falling back to pm2, and reports an honest "restart required" step instead of a silent pm2-only "skipped" that read like a completed update. The server-side latest-version lookup backing the dashboard's update banner also gained `--prefer-online`, closing the same stale-npm-cache class already fixed in the CLI's own copy for #4376 ([#11885](https://github.com/diegosouzapw/OmniRoute/issues/11885)). ([#12005](https://github.com/diegosouzapw/OmniRoute/pull/12005)) +- **fix(resilience):** clear persisted LKGP pins when a target suffers connection/provider exhaustion or is skipped before dispatch due to cooldown/exhaustion/unavailability, preventing subsequent requests from repeatedly prioritizing known-dead providers ([#11911](https://github.com/diegosouzapw/OmniRoute/issues/11911)). ([#12013](https://github.com/diegosouzapw/OmniRoute/pull/12013)) — thanks @HouMinXi +- **fix(sse):** the OpenAI→Claude stream translator now defers the terminal `message_delta`/`message_stop` emission until the real usage block has arrived (or a genuine end-of-stream flush forces it) instead of emitting it immediately on `finish_reason` — previously, when the trailing usage-only chunk (`{"choices":[],"usage":{...}}`) arrived *after* the `finish_reason` chunk (the normal order for Fireworks/vLLM/Together and other `stream_options.include_usage` upstreams), the client-visible `message_delta` still carried stale/zero usage even though `state.usage` was internally corrected too late to matter (ported from [#11915](https://github.com/diegosouzapw/OmniRoute/pull/11915) — thanks @HouMinXi). ([#11933](https://github.com/diegosouzapw/OmniRoute/pull/11933)) +- **fix(sse):** `stripStore()` now forces `store=false` for stateless OpenAI-compatible Responses-API targets (unless the connection has explicitly opted in via `providerSpecificData.openaiStoreEnabled`), instead of only handling the `openai`/`agentrouter` cases — a client-supplied `store` value previously passed through untouched to backends that don't actually persist responses server-side ([#11916](https://github.com/diegosouzapw/OmniRoute/pull/11916) — thanks @HouMinXi). +- **fix(models):** custom provider-node models (synced, custom, and alias-backed) now appear under their configured prefix in the unified catalog when the operator's model-id prefix mode is canonical, instead of being dropped whenever alias-inclusion was otherwise disabled ([#11918](https://github.com/diegosouzapw/OmniRoute/pull/11918) — thanks @HouMinXi). +- **fix(models):** the unified model catalog now suppresses stale static registry models (including effort-tier variants) for any provider whose active connection has an authoritative live synced catalog, not only providers already using exclusive-synced-listing — a connection with `providerUsesAuthoritativeLiveCatalog` previously kept serving both the live-synced models and the stale static rows side by side ([#11919](https://github.com/diegosouzapw/OmniRoute/pull/11919) — thanks @HouMinXi). +- **fix(ui):** `ProviderIcon`'s three remaining unguarded lookups (`PROVIDER_ICON_ALIASES`, `LOCAL_SVG_ALIASES`, `THEMED_SVGS`) now use `Object.hasOwn()` own-property checks — a provider id such as `constructor` or `__proto__` previously resolved through the prototype chain instead of falling through to the unknown-provider CDN fallback (`getLobeProviderIcon()` itself was already guarded by [#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)); `ProviderPageHeader`'s `color` field is also now optional, matching the rest of the component's defensive typing (ported from [#11920](https://github.com/diegosouzapw/OmniRoute/pull/11920) — thanks @HouMinXi). ([#11935](https://github.com/diegosouzapw/OmniRoute/pull/11935)) — thanks @NoxzRCW +- fix(ollama): preserve multi-byte UTF-8 content split across stream chunks in the Ollama NDJSON transform, which previously corrupted CJK/emoji into U+FFFD (#11921) — thanks @pacocartones +- **fix(providers):** OrcaRouter chat requests now target `/v1/chat/completions` instead of the bare `/v1` API root, fixing the upstream `404 Invalid URL (POST /v1)` ([#11923](https://github.com/diegosouzapw/OmniRoute/pull/11923)). — thanks @echel0nn +- **fix(plugins):** deliver the `onStreamComplete` event to disk-installed plugins. The event shipped in v3.8.50 (#9669) was emitted internally but had no plugin-facing wiring, so no plugin could ever subscribe: the manifest schema silently dropped `hooks.onStreamComplete`, and the loader/manager only knew the seven legacy hooks. `onStreamComplete` is now a declarable manifest hook, wired through the loader and registered by the manager like the other hooks, and its payload carries a `requestId` so consumers can correlate the stream-completion event with the originating request ([#11934](https://github.com/diegosouzapw/OmniRoute/pull/11934)) — thanks @amaleta / @pacocartones +- **fix(db):** the Qdrant embedding-model dropdown now lists local/self-hosted providers (Ollama, LM Studio, vLLM, etc.) — an active connection is treated as "configured" when the provider allows an optional API key, not only when it has a real key or OAuth, so a running local embedding provider is no longer hidden from the picker ([#11949](https://github.com/diegosouzapw/OmniRoute/issues/11949)) ([#11995](https://github.com/diegosouzapw/OmniRoute/pull/11995)) +- **fix(providers):** Vertex AI Anthropic partner-model discovery now calls the Model Garden `v1beta1` publisher list (`/v1beta1/publishers/anthropic/models`, global) and parses its `publisherModels` envelope, so Claude models auto-synced from Vertex populate the active live catalog and route at request time instead of returning `Model '' is not available in the active live catalog` ([#11991](https://github.com/diegosouzapw/OmniRoute/issues/11991)) — thanks @fabioluissilva ([#11998](https://github.com/diegosouzapw/OmniRoute/pull/11998)) +- **fix(cli):** support `--base-url` alongside `--endpoint` in `omniroute nodes add`, `update`, and `validate` subcommands to prevent global `--base-url` shadowing issues ([#11999](https://github.com/diegosouzapw/OmniRoute/issues/11999)). ([#12033](https://github.com/diegosouzapw/OmniRoute/pull/12033)) — thanks @HouMinXi +- **fix(providers):** `cloudflare-ai` no longer refuses image content parts for every Workers AI model ([#12002](https://github.com/diegosouzapw/OmniRoute/pull/12002)) — the plain-string `content` requirement behind #2539 is carried by the _model_ schema, not by the `/ai/v1/chat/completions` endpoint (measured: an all-text part array returns 200 on `@cf/mistralai/mistral-small-3.1-24b-instruct`, `@cf/meta/llama-4-scout-17b-16e-instruct` and `@cf/meta/llama-3.3-70b-instruct-fp8-fast`, and 400 on the text-only `@cf/qwen/qwen2.5-coder-32b-instruct`). `transformRequest()` flattened every array and threw on the first non-text part (#6390), so image input was refused for vision-capable Cloudflare models that accept it. All-text arrays are still flattened — the one shape every model accepts — while an array carrying a non-text part is passed through untouched, so the attachment is still never silently dropped. Regression guards: `tests/unit/cloudflare-ai-image-parts-6390.test.ts`. — thanks @davidlinfr +- **fix(resilience):** decouple the limiter-managed execution backstop from the queue-wait budget — new `requestQueue.executionMaxWaitMs` (env `RATE_LIMIT_EXECUTION_MAX_WAIT_MS`, default 600000 = 10 min) now feeds Bottleneck's post-dispatch `expiration`, while `requestQueue.maxWaitMs` keeps its documented queue-wait semantics. Previously the queue-wait budget doubled as the execution expiration, so legitimate long-running calls on non-incremental gateways (whole generation buffered before the first upstream byte, e.g. Console Go / Command Code tiers serving GLM models) were killed mid-flight at the queue budget with a false 504 `RATE_LIMIT_EXECUTION_TIMEOUT` — the local limiter undercut the provider-aware upstream fetch-start timeouts. The surfaced 504 message now names `requestQueue.executionMaxWaitMs`; the error keeps the #4165 guarantees (disclaims an upstream timeout, preserves the Bottleneck error as `cause`, branded code + trusted provenance, classified request-scoped so combo falls back). A real queue-wait bound (the `Promise.race` around `limiter.schedule()` sketched in #9533) remains future work. (#12025) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **Call logs:** keep the `error` field when an artifact exceeds the storage cap, instead of replacing it with the omission marker. The error is the only field that says *why* a request failed and is typically ~90 bytes next to the multi-hundred-KB bodies that trip the cap, so dropping it left a size-limited row undiagnosable — a provider outage, a local timeout and an upstream 400 all rendered identically. It is now preserved at every fallback stage, truncated to 4KB if it is itself large ([#12026](https://github.com/diegosouzapw/OmniRoute/issues/12026)). ([#12095](https://github.com/diegosouzapw/OmniRoute/pull/12095)) — thanks @ntdat812 +- **fix(diagnostics):** preserve the error field (truncated to 4KB with a `[truncated: …]` suffix) in every call-log artifact size-limit fallback stage. Previously the minimal fallback replaced the error with `[omitted: call log artifact size limit exceeded]`, so an oversized artifact row showed nothing about WHY the request failed — e.g. 91 of 847 opencode-go 504 rows on one production instance were undiagnosable from the dashboard. Oversized request/response bodies are still omitted exactly as before; the error cap is independent of the payload sizes that tripped the fallback. (#12026) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **fix(sse):** OpenAI Responses clients that declare the native `web_search` tool now receive a spec-shaped `web_search_call` output item with `action.sources` alongside the preserved function-call round-trip, so search results executed through OmniRoute's own search backend are consumable by standard Responses clients (Codex, pi-web-access, …). ([#12031](https://github.com/diegosouzapw/OmniRoute/pull/12031)) — thanks @watchingdogs +- **fix(cli):** use in-thread alias resolver hooks on modern runtimes to avoid deprecation noise and improve Node.js forward compatibility ([#12073](https://github.com/diegosouzapw/OmniRoute/issues/12073)). ([#12083](https://github.com/diegosouzapw/OmniRoute/pull/12083)) — thanks @xiaoyaner0201 +- **fix(combo):** an operator-set **Agent Features → Context length** on a combo is now honored at request time. The value was persisted and advertised through `/v1/models`, but `resolveComboContextLimit()` never consulted it — so a multi-target combo whose members carry no per-model window fell through to the provider's generic `defaultContextLength` (openrouter 128000, command-code 200000) and rejected large requests with `Input exceeds context window … limit 128000` despite the combo being explicitly sized much larger. An identical single-target combo worked, because it collapses to its concrete target before the guard runs. Invalid values (0/negative/NaN/Infinity) are ignored, so the existing target → combo-min → fallback order is unchanged. ([#12090](https://github.com/diegosouzapw/OmniRoute/pull/12090)) — thanks @adivekar-utexas +- **fix(sse):** passthrough streams now estimate usage on finish when upstream closes without usage even with `stream_options.include_usage` — avoids `0 tokens / 0%` for providers that stay silent (and correctly handles trailing empty-choices usage) ([#12151](https://github.com/diegosouzapw/OmniRoute/pull/12151)) — thanks @maxmad64bis +- **fix(combos):** clearing an agent feature in the combos editor now persists — unchecking context cache protection, or emptying the system message or tool filter, sends an explicit `null` instead of dropping the field from the `PUT` body, which the update merge read as "leave unchanged" ([#12177](https://github.com/diegosouzapw/OmniRoute/pull/12177)) — thanks @foreveryh / @ntdatt812 +- **fix(memory):** self-hosted embedding endpoints now vectorize — the vector width is measured from the first embedding that comes back instead of being read from a registry that cannot describe them, so `vec_memories` is created and memories stop piling up unvectorized behind a green health check ([#12180](https://github.com/diegosouzapw/OmniRoute/pull/12180)) — thanks @kanade-hoshino / @ntdatt812 +- **fix(translator):** the leading `system` message now reaches Responses-API upstreams when its `content` is a content-part array — it was read as `typeof content === "string" ? content : ""`, so a prompt-caching client (Anthropic `cache_control`, the shape LiteLLM and the Anthropic SDK emit) had its entire system prompt replaced by an empty `instructions`. The request was still accepted with a normal `prompt_tokens` count, so the model answered with no instructions and nothing in the response said they were missing. Mid-conversation system turns already handled the array shape ([#7056](https://github.com/diegosouzapw/OmniRoute/pull/7056)); only the first one did not ([#12206](https://github.com/diegosouzapw/OmniRoute/issues/12206)). Regression guard: `tests/unit/translator-openai-responses-system-content-parts.test.ts`. ([#12207](https://github.com/diegosouzapw/OmniRoute/pull/12207)) — thanks @Gorillaz322 +- **fix(free-tier):** `/api/free-tier/summary` no longer computes its totals from a Radar feed built before the catalog the running release ships. When the cached feed is older — or carries no build date at all — the route answers from the shipped catalog, resolved through the operator's local model state so disabled and tombstoned models stay out of the numbers ([#12215](https://github.com/diegosouzapw/OmniRoute/pull/12215)). — thanks @maxmad64bis +- **fix(oauth):** Keep a Claude personal workspace and a Team organization as separate connections — they share the same email and `accountUUID`, so the email-only OAuth dedup let the second login overwrite the first account's tokens; `organizationUUID` now disambiguates them, the way `workspaceId` does for Codex ([#12222](https://github.com/diegosouzapw/OmniRoute/pull/12222)) — thanks @davidebaraldo +- **fix(resilience):** a 402 on a single paid model of a passthrough/gateway provider (e.g. `kilo-gateway`, `ollama-cloud`) no longer terminalizes the whole connection with a never-auto-recovered `credits_exhausted` status — only the paid model is locked out, so free models on the same key keep serving. 402 variant of [#3027](https://github.com/diegosouzapw/OmniRoute/issues/3027). Single-credential providers are unaffected — a 402 there is still treated as the key being genuinely out of credit ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239) / [#10616](https://github.com/diegosouzapw/OmniRoute/issues/10616)) ([#12242](https://github.com/diegosouzapw/OmniRoute/issues/12242)) — thanks @brick30llc-ctrl ([#12266](https://github.com/diegosouzapw/OmniRoute/pull/12266)) — thanks @geekyNads +- **fix(sse):** trust `finish_reason: "length"`/`"max_tokens"` over the reasoning-consumed-token ratio in response quality validation, so a reasoning model truncated below the old 90% threshold correctly fails and retries instead of returning empty content as a silent "success" ([#12262](https://github.com/diegosouzapw/OmniRoute/pull/12262)) — thanks @brick30llc-ctrl +- **fix(settings):** `PUT /api/settings/cache-config` now persists `alwaysPreserveClientCache` to the flat general settings the runtime cache-control policy actually reads; previously the value landed in the databaseSettings "cache" section and was silently ignored, so the endpoint had no effect on `cache_control` passthrough ([#12304](https://github.com/diegosouzapw/OmniRoute/pull/12304)) — thanks @davidebaraldo +- **fix(grok-cli):** treat omitted SuperGrokPro `creditUsagePercent` as 0% used so Provider Limits still renders a weekly bar (proto3 zero-elision) ([#12312](https://github.com/diegosouzapw/OmniRoute/pull/12312)) — thanks @HouMinXi +- **fix(combo):** Expose the two Auto-Combo scoring factors nobody could set — the combo validation schema and the dashboard weight sliders both declared 13 of the scorer's 15 factors, so `connectionDensity` (spreads load across a provider's connections) and `quality` were dropped on save and offered nowhere. The sliders also shipped their own default table that differed from the engine's on every non-zero factor and summed to 1.05, so the percentages shown next to them added up to 105%. Both lists now match `DEFAULT_WEIGHTS`, and a test keeps them there. Note that a combo whose stored `weights` omitted the two keys was effectively running with them at zero and the other thirteen renormalized upward; it now runs with the engine's intended distribution, so its routing does shift ([#12314](https://github.com/diegosouzapw/OmniRoute/pull/12314)) — thanks @maxmad64bis +- **fix(docs):** The free-tier reference no longer says its numbers come "confidence tagged per row" — no catalog entry carries a confidence tag and the API serves none, so every figure on that page is an estimate of the same, unstated quality. The page now states what an entry does vouch for: an independently documented hard stop (set by hand with the source in a comment, never defaulted to `true`) and a prompt-training disclosure, both with live counts the `check:docs-counts` gate keeps honest ([#12318](https://github.com/diegosouzapw/OmniRoute/pull/12318)) — thanks @maxmad64bis +- **fix(usage):** `adobe-firefly` and `firefly` have had a working usage fetcher since Adobe Firefly landed, but neither was ever added to the registration list, so the provider-plugin manifest, `genericQuotaFetcher` and the free-access quota cache all reported them as having no usage support — while `USAGE_SUPPORTED_PROVIDERS` said the opposite. Both are now declared, which also means their credit balance is fetched like any other declared provider's: `registerGenericQuotaFetchers` now registers a generic quota fetcher for them, and `resolveFreeAccessState` no longer returns early. A test holds the registration list to the dispatcher's switch in both directions, which is what the module's own docstring already asked for in prose ([#12321](https://github.com/diegosouzapw/OmniRoute/pull/12321)) — thanks @maxmad64bis +- **fix(quota):** drop the generic quota cache (agy / Antigravity / Claude OAuth) on an upstream 429 so reset-aware scoring does not keep a 60s stale snapshot, and force-refresh the next usage fetch so inner provider caches cannot recache the same window ([#12325](https://github.com/diegosouzapw/OmniRoute/pull/12325)) — thanks @HouMinXi +- **fix(combos):** deleting a combo now clears its persisted LKGP pins instead of leaving unreachable `key_value` rows behind ([#12326](https://github.com/diegosouzapw/OmniRoute/issues/12326)) ([#12425](https://github.com/diegosouzapw/OmniRoute/pull/12425)) — thanks @KrzysiekSko +- **fix(sse):** Keep ZWNJ (U+200C) and ZWJ (U+200D) in assistant text, reasoning and tool-call arguments — Persian/Kurdish half-space (`ارائه‌دهنده`), Arabic/Indic shaping and emoji sequences no longer lose them; the response de-obfuscation now removes joiners only between ASCII word characters, where the request side inserts them ([#12186](https://github.com/diegosouzapw/OmniRoute/issues/12186)) — thanks @rezjalibd ([#12359](https://github.com/diegosouzapw/OmniRoute/pull/12359)) — thanks @pacocartones +- **fix(resilience):** count resolved upstream 5xx results against the provider circuit breaker on the chat path — `CircuitBreaker.execute()` no longer reads a resolved `{ success: false, status: 5xx }` as a success that cancels the call-site failure, so a provider answering 503s now trips its breaker instead of staying `CLOSED` at `failureCount: 1`; single-model and combo dispatches are each accounted exactly once ([#12254](https://github.com/diegosouzapw/OmniRoute/issues/12254)) ([#12360](https://github.com/diegosouzapw/OmniRoute/pull/12360)) — thanks @pacocartones +- **fix(providers):** resolve the Codex quota auto-ping model from the live provider catalog and lifecycle registry instead of the retired `gpt-5.1-codex-mini`, and pause the ping with one actionable warning when no selectable Codex model exists rather than retrying a shut-down id every cooldown window ([#11905](https://github.com/diegosouzapw/OmniRoute/issues/11905)) ([#12361](https://github.com/diegosouzapw/OmniRoute/pull/12361)) — thanks @pacocartones +- **fix(api):** keep the `{created, data}` wrapper on combo-routed `/v1/images/generations` responses and default Codex image results to `b64_json` on both `/v1/images/generations` and `/v1/images/edits` so Codex CLI's built-in `image_gen` can decode them ([#12268](https://github.com/diegosouzapw/OmniRoute/issues/12268)) ([#12362](https://github.com/diegosouzapw/OmniRoute/pull/12362)) — thanks @pacocartones +- **fix(sse):** Name the shadowed custom provider node when a built-in provider id/alias (e.g. `openference` → `of`) reserves the prefix of an existing OpenAI/Anthropic-compatible node, so the runtime `No active credentials for provider: ` error explains that the prefix routed to the built-in and never reached the node's healthy connections, instead of contradicting the dashboard ([#11943](https://github.com/diegosouzapw/OmniRoute/issues/11943)) — thanks @morpheus9393 ([#12365](https://github.com/diegosouzapw/OmniRoute/pull/12365)) — thanks @pacocartones +- **fix(i18n):** wrap `ccOnboardingKeyPlaceholder` in ICU single quotes across all 43 locales so angle brackets render literally instead of being parsed as rich-text tags, which crashed the Claude Code onboarding block with `INVALID_MESSAGE: INVALID_TAG` ([#12302](https://github.com/diegosouzapw/OmniRoute/issues/12302)) ([#12369](https://github.com/diegosouzapw/OmniRoute/pull/12369)) — thanks @gonisulaimann +- **fix(models):** publish `effort_tiers` on Kimi K3's synced base-model entries (`kmca/k3`, `kmca/k3-256k`) so catalog-only clients (OpenCode, plain SDK pickers) can see and select the reasoning tiers (`low`/`high`/`max`) the synced metadata already carried — the `isSkippedEffortProvider` gate no longer suppresses tier visibility on those base entries, while synthetic `-` variant generation stays prevented and Codex/GLM base models remain excluded unchanged ([#12299](https://github.com/diegosouzapw/OmniRoute/issues/12299)) ([#12371](https://github.com/diegosouzapw/OmniRoute/pull/12371)) — thanks @gonisulaimann +- **fix(guardrails):** keep `auto`/`auto/*` virtual combos exempt from the Vision Bridge `fixedModel` credential guard so a combo target is passed through instead of silently falling back to global auto-selection ([#12237](https://github.com/diegosouzapw/OmniRoute/issues/12237)) ([#12373](https://github.com/diegosouzapw/OmniRoute/pull/12373)) — thanks @pacocartones +- **fix(combo):** capability-filter exhaustion caused by `max_tokens` above every target's known output limit now reports that reason (requested `max_tokens` vs the pool's highest known ceiling) instead of the unrelated "supports structured output" message ([#12229](https://github.com/diegosouzapw/OmniRoute/issues/12229)) — thanks @DW-MediaLab ([#12374](https://github.com/diegosouzapw/OmniRoute/pull/12374)) — thanks @pacocartones +- **fix(auth):** the `least-used` account strategy now prefers accounts without backoff before falling back to oldest `lastUsedAt`, the same tie-break `round-robin` already applies, so a failover no longer lands on a just-rate-limited account for a single request ([#12279](https://github.com/diegosouzapw/OmniRoute/issues/12279)) — thanks @tenshiak ([#12375](https://github.com/diegosouzapw/OmniRoute/pull/12375)) — thanks @pacocartones +- **fix(docker):** the `chatgpt-web-codex-browser` image now finds the Chrome binary under `chrome-linux64/` (Chrome for Testing layout in `playwright:v1.62.0-noble`) as well as the legacy `chrome-linux/`, so the container no longer crash-loops with `exec: --headless=new: not found` ([#12024](https://github.com/diegosouzapw/OmniRoute/issues/12024)) ([#12376](https://github.com/diegosouzapw/OmniRoute/pull/12376)) — thanks @pacocartones +- **fix(providers):** declare `groq/compound` and `allam-2-7b` as non-reasoning models in the curated Groq registry so `reasoning_effort` / `output_config.effort` / `thinking` from Claude Code are stripped instead of forwarded, which Groq rejected with HTTP 400 ([#12134](https://github.com/diegosouzapw/OmniRoute/issues/12134)) ([#12379](https://github.com/diegosouzapw/OmniRoute/pull/12379)) — thanks @pacocartones +- **fix(executors):** `OpencodeExecutor` no longer forces a direct connection when the connection has a proxy assigned in Proxy Management but no per-account proxies: the single-account fast path used to wrap the upstream dispatch in the direct-egress sentinel, discarding the ambient proxy context the chat handler had pinned from `proxy_assignments`, so API-key `opencode`/`opencode-go` connections egressed from the host IP (and hit geoblocks) despite the assignment. The direct pin is now applied only when no ambient proxy context exists ([#11894](https://github.com/diegosouzapw/OmniRoute/issues/11894) — thanks @hizzt) ([#12380](https://github.com/diegosouzapw/OmniRoute/pull/12380)) — thanks @pacocartones +- **fix(api):** `GET /v1/models` with `MODELS_CATALOG_PREFIX_MODE=canonical` (or `?prefix=canonical`) now lists providers whose registry alias is undefined or equal to their own id (Antigravity, Antigravity CLI and other self-aliased built-ins) — their single `provider/model` id was dropped by the alias/canonical duplicate guard in the static, synced, custom and alias-backed catalog loops ([#12058](https://github.com/diegosouzapw/OmniRoute/issues/12058)) — thanks @cheynetom ([#12381](https://github.com/diegosouzapw/OmniRoute/pull/12381)) — thanks @pacocartones +- **fix(translator):** Drop replayed `thinking` blocks that carry no signature (the shape produced from cross-provider `reasoning_content`) instead of stamping the default Claude signature on them, which Anthropic rejected with `400 Invalid signature in thinking block` on the next turn served by an Anthropic rung ([#12105](https://github.com/diegosouzapw/OmniRoute/issues/12105)) — thanks @atescivitci-cmd ([#12386](https://github.com/diegosouzapw/OmniRoute/pull/12386)) — thanks @pacocartones +- **fix(cli):** Resolve Bun's `--preload` polyfill path against the package root instead of `dist/`, so `omniroute` installed with `bun install -g` no longer crashes at startup with `error: preload not found …/dist/open-sse/utils/setupPolyfill.ts` ([#11980](https://github.com/diegosouzapw/OmniRoute/issues/11980)) — thanks @joglomedia ([#12387](https://github.com/diegosouzapw/OmniRoute/pull/12387)) — thanks @pacocartones +- **fix(providers):** `gemini-business` now publishes its model catalog — `/v1/models` and `/v1/providers/gemini-business/models` list the 12 enterprise Gemini ids the executor understands instead of returning an empty list (#12107) ([#12389](https://github.com/diegosouzapw/OmniRoute/pull/12389)) — thanks @pacocartones +- **fix(db):** install `busy_timeout` before the SQLite connection's first statement so a process opening the database while another one closes its WAL connection waits out the transient EXCLUSIVE lock instead of dying with `database is locked`, and recognise the drivers' real BUSY/PROTOCOL/IOERR errors as transient in the corruption probe so the same lock no longer renames the database away as corrupt; deflakes `cross-process contenders never both acquire the same connection` (#12394 — thanks @pacocartones) +- **fix(chat-admission):** derive the `chat_admission_busy` 503 `Retry-After` from observed heavyweight-lease occupancy — the larger of the exhausted `OMNIROUTE_CHAT_ADMISSION_QUEUE_MS` window and the time since capacity last turned over, capped at 60 s — instead of a fixed 1 s (structural) / 2 s (byte-stage) hint that invited Codex/agent fan-out clients to re-send ~1 MiB `/v1/responses` bodies every second into a gate held for the whole SSE lifetime; an idle gate keeps the historical floors ([#12135](https://github.com/diegosouzapw/OmniRoute/issues/12135)) (#12395 — thanks @pacocartones) +- **fix(api-manager):** the API key permissions modal no longer silently drops `allowedCombos` entries its Combo picker cannot render — routing-rule names such as `rt-*`, which the backend already honours — when "All" is clicked and the key is switched back to "Restrict"; those entries now survive the toggle, are listed read-only under the combo list so the count and the list agree, and are saved back verbatim instead of persisting `[]` (deny-all) (#12397 — thanks @pacocartones) +- **fix(catalog):** write the NUL separator of the catalog connection memo key, the provider serviceKind memo key, the Video Bridge promotion group key and a JSON-exactness test fixture as the `\u0000` escape instead of a raw byte — same runtime value, but the raw byte made git, GitHub and ripgrep treat those files as binary (hidden PR diffs, silently skipped searches); a guard test now keeps raw NUL bytes out of `src/`, `open-sse/` and `tests/` (#12403 — thanks @pacocartones) +- **fix(providers):** add `CLAUDE_CODE_CLIENT_VERSION` and `GITHUB_COPILOT_CLI_VERSION` env overrides so Anthropic/Copilot client-version gates can be unblocked without a rebuild ([#12417](https://github.com/diegosouzapw/OmniRoute/issues/12417)) ([#12632](https://github.com/diegosouzapw/OmniRoute/pull/12632)) — thanks @HouMinXi +- **fix(db):** back-fill `last_ping_at` and `last_pinged_reset_key` on `provider_connections` during schema reconciliation so divergent lineages that skipped `123_quota_auto_ping` still accept quota auto-ping writes ([#12470](https://github.com/diegosouzapw/OmniRoute/pull/12470) — thanks @KooshaPari) +- **fix(ci):** document MIT exceptions for `@eloqnt/{config,format-json,format-po}` (next-intl transitive; locked tarballs omit `license`) and keep the A2A lifecycle vitest off the real SQLite persistence seam ([#12581](https://github.com/diegosouzapw/OmniRoute/issues/12581)) ([#12595](https://github.com/diegosouzapw/OmniRoute/pull/12595)) +- OpenCode plugin `/v1/models` catalog fetch now waits 30s by default and attaches HTTP `statusCode` on 401/5xx so host fallback plugins can hop instead of seeing an untyped AbortError/UnknownError. ([#12607](https://github.com/diegosouzapw/OmniRoute/pull/12607)) — thanks @RaviTharuma +- **CI:** the OpenAPI security-tier gate now mirrors `isAlwaysProtectedPath()` in full — it also reads `ALWAYS_PROTECTED_API_PATTERNS`, so the pattern-gated credential routes (`/api/providers/{id}/{claude,codex}-auth/{export,apply-local}`, GHSA-5926-2w35-7h4q) no longer report as unannotated. (#12605) +- **fix(api):** GET /v1/models no longer waits forever on a hung coalesced catalog rebuild; cold-path waits are bounded (`CATALOG_BUILD_TIMEOUT_MS`, default 8s) and a last-good 200 is served when the rebuild times out ([#12627](https://github.com/diegosouzapw/OmniRoute/issues/12627)). ([#12628](https://github.com/diegosouzapw/OmniRoute/pull/12628)) — thanks @RaviTharuma +- **fix(cursor):** a non-array `tool_calls` on an assistant message no longer crashes the cursor request translator with a `TypeError`; both loops now require an array ([#12689](https://github.com/diegosouzapw/OmniRoute/issues/12689)) ([#12691](https://github.com/diegosouzapw/OmniRoute/pull/12691)) — thanks @soroush5 +- **fix(chat):** Chat Completions no longer return empty `content` after a server-owned memory or skills tool; the first provider send and account/model recovery now share one pipeline so a follow-up round-trip can fill the reply ([#12696](https://github.com/diegosouzapw/OmniRoute/issues/12696)) — thanks @HouMinXi ([#12867](https://github.com/diegosouzapw/OmniRoute/pull/12867)) +- **fix(dashboard):** batch-deleting provider keys no longer toasts failure after a successful delete when the confirm button's click event is forwarded as `onAfter` ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) — thanks @HouMinXi +- **fix(glm):** drop the extra 16th argument to `createSSETransformStreamWithLogger` that TypeScript rejected (TS2554) and that never reached the TransformStream ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) ([#12770](https://github.com/diegosouzapw/OmniRoute/pull/12770)) — thanks @HouMinXi +- **fix(dashboard):** pass `nodeMap` into Runtime `QuotaGroup` so a quota monitor in error/exhausted/alerting no longer throws `ReferenceError: nodeMap is not defined`. ([#12868](https://github.com/diegosouzapw/OmniRoute/pull/12868)) — thanks @HouMinXi +- Restricted API keys whose `allowedModels` lists a combo name no longer skip every combo member at pre-dispatch (`ALL_TARGETS_SKIPPED`). Inner-target filtering still applies when the allow-list is a provider prefix or `disableNonPublicModels` is on ([#12899](https://github.com/diegosouzapw/OmniRoute/pull/12899)). — thanks @HouMinXi +- **fix(combos):** Effort-suffixed combo members inherit the base model's `model_context_overrides` row so priority order is not inverted on large requests ([#12926](https://github.com/diegosouzapw/OmniRoute/pull/12926)) — thanks @HouMinXi +- **fix(catalog):** Picker-added `customModels` enter the dispatch-time live catalog so combo members and bare inference no longer 400 ([#12934](https://github.com/diegosouzapw/OmniRoute/pull/12934)) — thanks @HouMinXi +- **fix(docker):** re-derive the Docker build's worker-pool memory budget from the MEASURED ~4.5 GB per-process RSS (the issue owner's own VPS dmesg OOM-killer reproduction) instead of the stale 2560 MB/worker inference #11419 shipped, and lower `OMNIROUTE_BUILD_WORKERS` 3 → 2 so 1 parent + 1 page-data worker (2 processes × 4.5 GB = 9 GB) fits the 12.288 GB (75%) budget on a 16 GB GitHub Actions runner — the previous default (1 parent + 2 workers = 13.5 GB) still overcommitted the runner and kept "Publish to Docker Hub" failing with `cannot allocate memory` after #11419 merged (#7518). ([#11663](https://github.com/diegosouzapw/OmniRoute/pull/11663)) +- **fix(electron):** the packaged Windows desktop build now passes the #7592 cold-restart smoke end to end. Five defects were found and fixed on the way: (1) optional-pack staging failed on any machine whose `tar` is GNU tar — it reads the drive letter in an absolute `-f C:\...` archive path as a remote rsh host (`Cannot connect to C:`), so staging now passes a bare filename with `cwd` at the tarball directory and surfaces tar stderr on failure; (2) the Electron `files` allowlist was missing `lib/loginHeaderCapture.js`, so the packaged main process crashed at startup with `Cannot find module './lib/loginHeaderCapture'`; (3) electron-builder ≥26 injects an `!**/node_modules/**` ignore into every extraResources pattern list that no positive filter can override, which silently dropped the staged runtime `node_modules` (including the better-sqlite3 N-API prebuild) from `resources/app` — a fresh v3.8.50 desktop build would have shipped with no native SQLite driver and reproduced the #7592 sql.js fallback on every machine; an `afterPack` hook now restores the staged `node_modules` after packing; (4) the packaged-app smoke harness redirected `USERPROFILE` into its temp DATA_DIR, but Electron resolves the Roaming profile from `%USERPROFILE%\AppData\Roaming\` (USERPROFILE takes precedence over the APPDATA env var) and the path service throws instead of creating it, so `requestSingleInstanceLock()` returned false and the app exited(0) silently before `app.whenReady()` — the harness now pre-creates the derived tree, and `ensureSmokeEnvDirs` is exported and unit-tested; (5) the #7592 driver assertion parsed a `[DB] Driver: ...` line that the server's primary DB path never printed (only the unused `openDatabaseAsync()` did) — `getDbInstance()` now emits the same line on open so the guard can actually assert the native driver. Also: the smoke env-allowlist unit test hardcoded POSIX paths and could only pass on Linux/macOS; expectations are now host-agnostic, with new regression tests for the USERPROFILE-derived tree and `tarPack` under absolute Windows-style paths ([#11443](https://github.com/diegosouzapw/OmniRoute/pull/11443)) — thanks @jonlwheat2-gif +- fix(sse): stop the auto-combo candidates inspector from silently dropping model-locked/cooled-down rows (#9133) ([#11994](https://github.com/diegosouzapw/OmniRoute/pull/11994)) — thanks @hartmark +- **fix(providers):** Perplexity Web no longer turns upstream stream failures into successful assistant text; pre-content failures remain eligible for fallback, partial output ends with a structured sanitized error, and failed sessions are not persisted ([#12465](https://github.com/diegosouzapw/OmniRoute/pull/12465)) +- **fix(video-bridge):** preserve chronological video and transcript fusion, source-frame timestamps, transcript provenance, full contact-sheet intervals, and pre-abort callback isolation ([#11681](https://github.com/diegosouzapw/OmniRoute/pull/11681)) +- **Z.ai Web:** HTTP 200 streams carrying an upstream error now terminate with a structured failure instead of assistant text plus a normal stop, preserving partial output while allowing pre-content combo fallback. ([#12454](https://github.com/diegosouzapw/OmniRoute/pull/12454)) +- Absorb `Error [AbortError]: request_signal_aborted` and DOMException AbortError shapes in the process-level client-abort crash guard so routine client disconnects no longer kill the server (exit code 7). ([#12165](https://github.com/diegosouzapw/OmniRoute/pull/12165)) — thanks @alvinveroy +- **fix(sse):** Treat Adapta Web `type:error` SSE events as sanitized non-stream failures instead of empty HTTP 200 completions. ([#12459](https://github.com/diegosouzapw/OmniRoute/pull/12459)) +- **fix(autoCombo):** layer 3 (`models_dev_tier`) no longer scores retired models as premium, merge cross-provider capability rows deterministically, and inherit tier scores through variant ids ([#11508](https://github.com/diegosouzapw/OmniRoute/issues/11508)). `getModelsDevTierFitness` now vetoes ids present in `config/quality/model-lifecycle.json` with status `retired` before any other signal; `loadModelCapabilities()` aggregates per `model_id` (any-non-null-true for booleans, max for `limit_context`) instead of last-write-wins over SQLite's undefined row order; a capabilities miss retries through `resolveScoresAs()`'s base id and reports `models_dev_tier:inherited`, matching layers 1–2 (#11492). Adds `getModelsDevTierFitnessWithSource()` and an `invalidateCapabilitiesCache()` test/ops hook. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) — thanks @Meet6338-X +- fix(sse): accept Claude `content:[]` + `stop_reason: max_tokens`/`tool_use` in `detectMalformedNonStream` (match `isEmptyContentResponse`) so Claude Code `/model` probes with `max_tokens: 1` no longer become a false 502 ([#11568](https://github.com/diegosouzapw/OmniRoute/pull/11568)) — thanks @Seramicx +- **fix(security):** sanitize `request.failed` diagnostics before publishing them to live dashboard listeners and replay history, while keeping status, model, provider, latency, and internal call-log diagnostics intact. ([#12469](https://github.com/diegosouzapw/OmniRoute/pull/12469)) +- **fix(pwa):** do not intercept dashboard navigations so Chrome can fall back from a stale HTTP/3 Alt-Svc advertisement (UDP :20128 is unpublished; F5 on a long-lived tab hung until a new tab opened a fresh TCP connection). ([#12767](https://github.com/diegosouzapw/OmniRoute/pull/12767)) — thanks @HouMinXi +- **fix(executors):** handle DuckDuckGo ERR_BN_LIMIT (418) without retrying — when the upstream returns `418 ERR_BN_LIMIT` (rate-limit/ban), the executor now returns the error immediately instead of burning another VQD acquisition that would only count against the IP limit. The retry logic for `418 ERR_CHALLENGE` (unsolved challenge) remains unchanged. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) ([#12110](https://github.com/diegosouzapw/OmniRoute/pull/12110)) — thanks @oyi77 / @Meet6338-X +- Fixed DuckDuckGo streaming responses losing JSON lines and UTF-8 characters split across network chunks. ([#11528](https://github.com/diegosouzapw/OmniRoute/pull/11528)) — thanks @pacocartones +- Harden generated Cloudflare, Vercel, and Deno relays by stripping hop-by-hop, framing, and proxy authentication headers before forwarding requests upstream. ([#11533](https://github.com/diegosouzapw/OmniRoute/pull/11533)) — thanks @pacocartones +- **feat(grok-cli):** Provider Limits shows grok-cli banked reset credits from `GetRemainingResets` (including a real zero; a failed RPC omits the row) and the existing View credits button now calls `ConsumerUiSvc/RedeemReset` for grok-cli. Live tokens use nested fields 10/20/30 (id + Timestamp), not compact 1/2/3. ([#12805](https://github.com/diegosouzapw/OmniRoute/pull/12805)) — thanks @HouMinXi +- **fix(memory):** Embedding Model Quick select, Embedding Source remote dropdown, and Rerank selector now list every configured provider with embedding/rerank support instead of only chat-catalog text matches plus OpenRouter live discovery; a generic OpenAI-compatible `/embeddings` + Cohere-compatible `/rerank` runtime fallback resolves any configured chat provider's embedding/rerank endpoint, so unlisted providers no longer fail with "Unknown embedding provider"; both memory selectors gained a free-text model override ([#11390](https://github.com/diegosouzapw/OmniRoute/pull/11390)) — thanks @rqzbeh +- Harden SQLite upgrades around the historical migration-074 version collision: missing discovery and inspector tables are replayed atomically, pre-existing databases (including setup-created skeletons) receive reusable content-addressed safety snapshots, and Node test/eval probes without `DATA_DIR` are isolated from the operator database. ([#12435](https://github.com/diegosouzapw/OmniRoute/pull/12435)) +- **fix(dashboard):** Moonshot/Kimi Open Platform voucher and cash leftover percentages follow the bucket balance, so an empty wallet no longer paints those rows as 100% while Available is 0% ([#12733](https://github.com/diegosouzapw/OmniRoute/pull/12733)) — thanks @HouMinXi +- **fix(combo):** return non-retryable HTTP 400 when all candidates for a pinned native Codex turn are unavailable due to model-scoped lockout, terminating the turn cleanly while preserving turn continuity and enabling standard Combo routing on subsequent turns ([#12240](https://github.com/diegosouzapw/OmniRoute/pull/12240)) — thanks @mdigitalbh81 +- Fixed auto-router strategies preserving the selected connection through ranking and dispatch when multiple connections share the same provider and model. ([#11530](https://github.com/diegosouzapw/OmniRoute/pull/11530)) — thanks @pacocartones +- Preserve Chat Completions JSON-object response formats when translating requests to the Responses API. ([#11529](https://github.com/diegosouzapw/OmniRoute/pull/11529)) — thanks @pacocartones +- Fix idempotency fingerprints so Responses API requests include `input` and other semantic generation fields using stable serialization, while excluding credentials and request metadata. ([#11532](https://github.com/diegosouzapw/OmniRoute/pull/11532)) — thanks @pacocartones +- **fix(api):** Generated API CLI commands now enforce required OpenAPI request bodies; Combo test commands forward the required `comboName` body, while API keys created by older writers after migration 149 preserve legacy allow-all Combo access without widening explicit empty allowlists — thanks @marcelokarval ([#12070](https://github.com/diegosouzapw/OmniRoute/pull/12070)) +- **fix(grok-web):** treat upstream streaming failures as failures instead of successful assistant text: error-only streams now fail readiness with HTTP 502, while failures after legitimate content preserve that partial output and terminate through the sanitized stream failure path without a normal `stop` completion. ([#12458](https://github.com/diegosouzapw/OmniRoute/pull/12458)) +- HuggingChat now turns HTTP 200 JSONL generation failures into a sanitized 502 before content, or a fixed public stream failure after partial output, so fallback and request persistence no longer record a false successful stop. ([#12456](https://github.com/diegosouzapw/OmniRoute/pull/12456)) +- **fix(providers):** keep 1min.ai HTTP 200 stream errors out of assistant content, preserve partial output, and expose sanitized terminal errors so pre-content failures can fall back. ([#12466](https://github.com/diegosouzapw/OmniRoute/pull/12466)) +- **fix(sessions):** preserve legacy Sessions data when the additive exclusive-session projection is unavailable, returning an empty projection and warning only once per contiguous outage instead of failing the endpoint; the Sessions badge intentionally reflects the merged legacy and exclusive row count introduced by [#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389) ([#11469](https://github.com/diegosouzapw/OmniRoute/pull/11469)) — thanks @KaspaPulse +- Render Nimble Search with the generic provider icon and serve Opper's proven logo locally. ([#12415](https://github.com/diegosouzapw/OmniRoute/pull/12415)) +- **fix(quality):** `config/quality/provider-assets-provenance.jsonl` was stale against the physical `public/providers/` tree (still listing 225 assets and 5 duplicate-content groups after several provider-retirement PRs removed 85 icon files), which would have made `check:provider-asset-provenance` fail on the next full run. Reconciled the manifest to the real 142-file snapshot (142 asset records, 1 duplicate-content group, 2 newly-registered files marked `unresolved` pending provenance research), repinned `auditedCommit`, and updated the regression test's hardcoded counts to match. ([#11876](https://github.com/diegosouzapw/OmniRoute/pull/11876)) +- fix(ui): wrap quota card footer action buttons to prevent clipping Refresh button on narrow cards — when extra action buttons such as `View credits` are present on Codex cards, allow the footer row to flex-wrap with `shrink-0` buttons so `Refresh now` remains visible and clickable across all responsive card widths ([#11464](https://github.com/diegosouzapw/OmniRoute/pull/11464)) — thanks @adevwithpurpose +- Keep Antigravity Gemini usable when the same connection's Claude weekly quota is empty; generic quota cache stays per-connection for every other provider. ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) — thanks @HouMinXi +- Fixed Tailscale tunnel setup to reject ports outside the valid 1-65535 range and safely install Windows MSI packages from paths containing apostrophes. ([#11534](https://github.com/diegosouzapw/OmniRoute/pull/11534)) — thanks @pacocartones +- Classify Cloudflared and Tailscale process-management routes as local-only and spawn-capable while preserving remote access to read-only tunnel status endpoints. ([#11531](https://github.com/diegosouzapw/OmniRoute/pull/11531)) — thanks @pacocartones +- Electron release workflow: the `publish-npm` job now grants `actions: read` to the reusable `npm-publish.yml` it calls (its `publish` job requests it), which is what made GitHub refuse the whole v3.8.50 run at startup and ship the release with zero desktop assets; a `workflow_dispatch` now builds the requested tag instead of the dispatching branch and can skip the npm leg (`publish_npm=false`) when only re-attaching assets ([#11974](https://github.com/diegosouzapw/OmniRoute/pull/11974)) +- **fix(dashboard):** The Combos page usage guide now reads its dismissal through `useSyncExternalStore` instead of correcting SSR state inside an effect, removing an extra commit of the page tree on every load (and the `react-hooks/set-state-in-effect` error it raised). ([#12671](https://github.com/diegosouzapw/OmniRoute/pull/12671)) +- Electron release: `electron/package-lock.json` regained the optional `electron-builder-squirrel-windows` subtree (13 entries) that `npm ci` had been refusing as out of sync, `electron-release.yml` gained a `build_ref` dispatch input and stops regenerating release notes on a re-attach dispatch, and the npm publish workflow attaches the SBOM to the GitHub Release on dispatch publishes too — so the v3.8.51 tag ships every desktop asset and the SBOM like v3.8.49 did ([#12022](https://github.com/diegosouzapw/OmniRoute/pull/12022)) +- Drained the reds every PR against `release/v3.8.51` was born with: documented the three service-worker build-id variables, registered the six retirement/tunnel tests with the mutation gate, approved `eslint-plugin-react-hooks` in the dependency allowlist, added the six `combo.sort.*` strings to `vi` and `pt-BR`, pointed the ChatGPT Web doc at the real migration-168 test, worded the g4f hint around the member key, and realigned four tests to the retired-provider catalog and the legacy-schema fixtures the retirement migrations touch. ([#11940](https://github.com/diegosouzapw/OmniRoute/pull/11940)) +- **fix(providers):** Claude, Grok, LMArena, Notion, and Perplexity web-cookie transports now use pooled `wreq-js` 3.2 instead of the native sidecar, with all nine supported bindings pinned and audited, and the applicable platform binding plus native-license evidence included in each release artifact ([#12429](https://github.com/diegosouzapw/OmniRoute/pull/12429), supersedes [#11753](https://github.com/diegosouzapw/OmniRoute/pull/11753)). +- **fix(providers):** Zed Hosted streaming failures now trigger fallback before content and end partial streams with a sanitized structured error instead of fake assistant text and a normal-success stop. ([#12455](https://github.com/diegosouzapw/OmniRoute/pull/12455)) +- **fix(test):** capture hook result after render (direct commit `b85d0abb0b`) +- **fix(sync):** repair the two regressions the v3.8.50 sync-back left on release/v3.8.51 (direct commit `5ade9e0851`) +- **revert(models):** drop the memo reset hook ported from #10055 (direct commit `282de841ae`) +- **fix(typecheck):** guard the optional connectionId before the reactive model sync (direct commit `f2825bad75`) +- **fix(quota-share):** release the winner's reserved in-flight slot (#11371) ([#11408](https://github.com/diegosouzapw/OmniRoute/pull/11408)) — thanks @oyi77 +- **fix(providers):** stop silently dropping reasoning effort on opencode families (#10788) ([#11409](https://github.com/diegosouzapw/OmniRoute/pull/11409)) — thanks @oyi77 +- **fix(combo):** enable genuine quota-aware routing for generic providers (antigravity, claude, etc.) ([#11411](https://github.com/diegosouzapw/OmniRoute/pull/11411)) — thanks @HouMinXi +- **fix(oauth):** allow expired token retry before deactivation and activate expired retry loop (#P0) ([#11414](https://github.com/diegosouzapw/OmniRoute/pull/11414)) — thanks @HouMinXi +- **perf(executors):** lazy-load the executor registry — defer class imports + construction to first use (#11220) ([#11421](https://github.com/diegosouzapw/OmniRoute/pull/11421)) — thanks @oyi77 +- **fix:** make release/v3.8.50 CI gates green (run 32786966560 root causes) ([#11450](https://github.com/diegosouzapw/OmniRoute/pull/11450)) — thanks @jonlwheat2-gif +- **fix(sse):** exact-domain cookie match and origin-equality URL assertions ([#11452](https://github.com/diegosouzapw/OmniRoute/pull/11452)) — thanks @hartmark +- **fix(providers):** send Responses API payload during OpenAI-compatible validation ([#11454](https://github.com/diegosouzapw/OmniRoute/pull/11454)) — thanks @tuandinh0801 +- **fix(command-code):** fallback to /alpha/generate for Go plan without Provider API access ([#11455](https://github.com/diegosouzapw/OmniRoute/pull/11455)) — thanks @Chewji9875 +- **fix(responses-continuation):** fail closed on a log-truncated stored input/output array ([#11473](https://github.com/diegosouzapw/OmniRoute/pull/11473)) — thanks @hartmark +- **fix(cli):** write jcode settings as [providers.omniroute] in config.toml ([#11484](https://github.com/diegosouzapw/OmniRoute/pull/11484)) — thanks @hongnoul +- **fix(logging):** raise chat-log array truncation cap to 1000 and unify duplicate implementations ([#11499](https://github.com/diegosouzapw/OmniRoute/pull/11499)) — thanks @hartmark +- **fix:** restore live websocket URL helpers ([#11509](https://github.com/diegosouzapw/OmniRoute/pull/11509)) — thanks @PauloFH +- **perf(core):** eliminate async waterfalls across home, cache, and db operations (#11396) ([#11512](https://github.com/diegosouzapw/OmniRoute/pull/11512)) — thanks @HouMinXi +- **fix(kiro):** add runtime.us-east-1.kiro.dev as first-attempt endpoint ([#11517](https://github.com/diegosouzapw/OmniRoute/pull/11517)) — thanks @rifqiawl +- **fix(providers):** remove duplicate 128-tool truncation in OpencodeExecutor (#11444) ([#11518](https://github.com/diegosouzapw/OmniRoute/pull/11518)) — thanks @HouMinXi +- **fix(providers):** poll onboardUser LRO before classifying BYOP ([#11519](https://github.com/diegosouzapw/OmniRoute/pull/11519)) — thanks @rifqiawl +- **fix(translator):** preserve exact tool name casing for Claude tool_use payloads (#11487) ([#11520](https://github.com/diegosouzapw/OmniRoute/pull/11520)) — thanks @HouMinXi +- **fix(kimi-web):** align health probe with executor domain ([#11521](https://github.com/diegosouzapw/OmniRoute/pull/11521)) — thanks @pacocartones +- **fix(cli):** preserve existing admin password during setup ([#11522](https://github.com/diegosouzapw/OmniRoute/pull/11522)) — thanks @pacocartones +- **fix(mitm):** keep Windows DNS elevation off the POSIX sudo path (#11430) ([#11542](https://github.com/diegosouzapw/OmniRoute/pull/11542)) — thanks @official-burak +- **fix(sse):** scale chat admission by ingest byte budget instead of a fixed request count ([#11548](https://github.com/diegosouzapw/OmniRoute/pull/11548)) — thanks @TheDemonTuan +- **fix:** accept Gemini Web cookie exports ([#11554](https://github.com/diegosouzapw/OmniRoute/pull/11554)) — thanks @rafacpti23 +- **fix(i18n):** complete Brazilian Portuguese translations ([#11558](https://github.com/diegosouzapw/OmniRoute/pull/11558)) — thanks @rafacpti23 +- **fix(sse):** explicit types for openai-responses pureHelpers — clears last failing core typecheck gate ([#11567](https://github.com/diegosouzapw/OmniRoute/pull/11567)) — thanks @jonlwheat2-gif +- **fix(opencode):** hydrate Proxy Pool references for no-auth connections ([#11584](https://github.com/diegosouzapw/OmniRoute/pull/11584)) — thanks @AStupidBear +- **fix(ci):** resolve inherited release/v3.8.51 base-red CI failures ([#11608](https://github.com/diegosouzapw/OmniRoute/pull/11608)) — thanks @jonlwheat2-gif +- **fix(db):** fallback load STORAGE_ENCRYPTION_KEY from env files ([#11614](https://github.com/diegosouzapw/OmniRoute/pull/11614)) — thanks @benzntech +- **fix(providers):** mark Brave Search as no longer having a free tier ([#11621](https://github.com/diegosouzapw/OmniRoute/pull/11621)) — thanks @turbolego +- **fix:** pass tool result content through as string in OpenAI/Claude -> Gemini translation ([#11624](https://github.com/diegosouzapw/OmniRoute/pull/11624)) — thanks @Hsia97 +- **fix(sse):** scope credential health to exact key ([#11628](https://github.com/diegosouzapw/OmniRoute/pull/11628)) — thanks @KaspaPulse +- **fix(install):** guard prepare script against missing husky ([#11634](https://github.com/diegosouzapw/OmniRoute/pull/11634)) — thanks @Prajeeth-12 +- **fix(security):** redact NVIDIA_API_KEY at the log sink in the ad-hoc diag script ([#11639](https://github.com/diegosouzapw/OmniRoute/pull/11639)) +- **fix(types):** clear the two type regressions red on release/v3.8.51 ([#11640](https://github.com/diegosouzapw/OmniRoute/pull/11640)) — thanks @jonlwheat2-gif +- **fix(build):** resolve workspace: protocol dependencies to concrete semver versions during prepublish (#11540) ([#11641](https://github.com/diegosouzapw/OmniRoute/pull/11641)) — thanks @HouMinXi +- **fix(search):** properly resolve configured search connection in /v1/responses pipeline (#11524) ([#11642](https://github.com/diegosouzapw/OmniRoute/pull/11642)) — thanks @HouMinXi +- **fix(authz):** trust cf-connecting-ip for via-proxy detection when peer is Cloudflare edge (#11514) ([#11643](https://github.com/diegosouzapw/OmniRoute/pull/11643)) — thanks @HouMinXi +- **fix(security):** compare the OIDC state cookie in constant time ([#11646](https://github.com/diegosouzapw/OmniRoute/pull/11646)) +- **fix(security):** namespace the dedup hash by the calling API key ([#11649](https://github.com/diegosouzapw/OmniRoute/pull/11649)) +- **fix(models):** gate the Codex-native catalog loop on prefix mode (#11632) ([#11662](https://github.com/diegosouzapw/OmniRoute/pull/11662)) — thanks @xiaoyaner0201 +- **fix(dashboard):** project Codex routing quota in account pools ([#11666](https://github.com/diegosouzapw/OmniRoute/pull/11666)) — thanks @KaspaPulse +- **fix(ddgw):** normaliza system no upstream da release v3.8.51 ([#11692](https://github.com/diegosouzapw/OmniRoute/pull/11692)) — thanks @RhianB14 +- **fix(memory):** honor category filter in GET /api/memory ([#11699](https://github.com/diegosouzapw/OmniRoute/pull/11699)) — thanks @Sabeekhann +- **fix(ci):** detect stale workspace lockfile entries ([#11714](https://github.com/diegosouzapw/OmniRoute/pull/11714)) — thanks @pacocartones +- **fix(ci):** stamp dist/BUILD_SHA before the npm publish provenance gate ([#11721](https://github.com/diegosouzapw/OmniRoute/pull/11721)) +- **fix(docker):** decouple the best-effort Bun image from the release manifest ([#11724](https://github.com/diegosouzapw/OmniRoute/pull/11724)) +- **perf(compression):** avoid cloning memo misses twice ([#11727](https://github.com/diegosouzapw/OmniRoute/pull/11727)) — thanks @RaviTharuma +- **fix(docker):** let the bun digest artifact be absent, not fatal ([#11740](https://github.com/diegosouzapw/OmniRoute/pull/11740)) +- **fix(shared):** block cloud-metadata hosts under default remote-image guard ([#11755](https://github.com/diegosouzapw/OmniRoute/pull/11755)) — thanks @santosraju99-hub +- **fix(api):** preserve caller-provided X-Correlation-Id on chat completions ([#11760](https://github.com/diegosouzapw/OmniRoute/pull/11760)) — thanks @aniruddhaadak80 +- **fix(resilience):** lock permanently retired models instead of short backoff (Gemini ban prevention) ([#11762](https://github.com/diegosouzapw/OmniRoute/pull/11762)) — thanks @turbolego +- **fix:** mark Vercel AI Gateway as passthroughModels ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **fix(resilience):** stop hammering permanently-moved endpoints and billing-suspended accounts ([#11774](https://github.com/diegosouzapw/OmniRoute/pull/11774)) — thanks @turbolego +- **fix(lease):** remove global static reservation and gate routing on live active lease occupancy ([#11775](https://github.com/diegosouzapw/OmniRoute/pull/11775)) — thanks @TheDemonTuan +- **fix(ci):** size the install-upgrade gate to a measured run, and log the pack cost ([#11776](https://github.com/diegosouzapw/OmniRoute/pull/11776)) +- **fix(pwa):** stop serving the stale shell after deploys ([#11779](https://github.com/diegosouzapw/OmniRoute/pull/11779)) — thanks @HouMinXi +- **fix(combo):** lock GitHub models rejected as "not supported" for future requests ([#11781](https://github.com/diegosouzapw/OmniRoute/pull/11781)) — thanks @turbolego +- **fix(cli):** probe both IPv4 and IPv6 loopback for server readiness (#11766) ([#11794](https://github.com/diegosouzapw/OmniRoute/pull/11794)) — thanks @vermasomesh835 +- **fix(sse):** preserve CLIProxyAPI wrappers per connection ([#11797](https://github.com/diegosouzapw/OmniRoute/pull/11797)) — thanks @RaviTharuma +- **fix(providers):** honor model auto-fetch opt-in on create ([#11798](https://github.com/diegosouzapw/OmniRoute/pull/11798)) — thanks @RaviTharuma +- **fix(dashboard):** make model auto-fetch order independent ([#11805](https://github.com/diegosouzapw/OmniRoute/pull/11805)) — thanks @RaviTharuma +- **fix(docker):** make GHA cache exports best effort ([#11806](https://github.com/diegosouzapw/OmniRoute/pull/11806)) — thanks @RaviTharuma +- **fix(kiro):** do not permanently ban on 'User is not authorized to make this call' ([#11809](https://github.com/diegosouzapw/OmniRoute/pull/11809)) — thanks @Deftera186 +- **fix(sse):** keep cache-write tokens in OpenAI-shaped usage ([#11814](https://github.com/diegosouzapw/OmniRoute/pull/11814)) — thanks @TheDemonTuan +- **fix(docs):** resolve relative markdown and wiki links across Fumadocs and GitHub wiki ([#11834](https://github.com/diegosouzapw/OmniRoute/pull/11834)) — thanks @AndrianBalanescu +- **fix(chat):** preserve unstripped model string for passthrough provider routing ([#11840](https://github.com/diegosouzapw/OmniRoute/pull/11840)) — thanks @raheemuddin786 +- **fix(mcp):** avoid async initialization inside sync esm wrappers (#11569) ([#11849](https://github.com/diegosouzapw/OmniRoute/pull/11849)) — thanks @HouMinXi +- **fix(sse):** preserve coherent markdown token boundaries across stream translation chunks (#11606) ([#11851](https://github.com/diegosouzapw/OmniRoute/pull/11851)) — thanks @HouMinXi +- **fix(ci):** run the install-upgrade gate on disk, not on the /tmp tmpfs ([#11855](https://github.com/diegosouzapw/OmniRoute/pull/11855)) +- **fix(skills):** expand shorthand property types in injected tool schemas ([#11857](https://github.com/diegosouzapw/OmniRoute/pull/11857)) — thanks @alvinveroy +- **fix(ci):** publish npm from a hosted runner so provenance is accepted ([#11877](https://github.com/diegosouzapw/OmniRoute/pull/11877)) +- **fix(lint):** pin react hooks plugin to 7.0.1 ([#11886](https://github.com/diegosouzapw/OmniRoute/pull/11886)) +- **fix(ci):** surface and clear release-green ESLint failure ([#11890](https://github.com/diegosouzapw/OmniRoute/pull/11890)) — thanks @MumuTW +- **fix(guardrails):** prevent duplicate prompt-injection-guard log output ([#11936](https://github.com/diegosouzapw/OmniRoute/pull/11936)) — thanks @patrykkopycinski +- **fix(auth):** downgrade expected transient states from warn to debug ([#11937](https://github.com/diegosouzapw/OmniRoute/pull/11937)) — thanks @patrykkopycinski +- **fix(codex):** fail over image generation for imported free plans ([#11948](https://github.com/diegosouzapw/OmniRoute/pull/11948)) — thanks @Bl0ck154 +- **fix(codex):** normalize non-stream responses ([#11951](https://github.com/diegosouzapw/OmniRoute/pull/11951)) — thanks @Bl0ck154 +- **fix(images):** forward Antigravity image size ([#11952](https://github.com/diegosouzapw/OmniRoute/pull/11952)) — thanks @Bl0ck154 +- **fix(db):** invalidate connection cache after upsert ([#11953](https://github.com/diegosouzapw/OmniRoute/pull/11953)) — thanks @Bl0ck154 +- **fix(codex):** restore imported account state ([#11954](https://github.com/diegosouzapw/OmniRoute/pull/11954)) — thanks @Bl0ck154 +- **fix(sse):** give extended-thinking targets the reasoning readiness budget ([#11959](https://github.com/diegosouzapw/OmniRoute/pull/11959)) — thanks @abhisheksharma2411 +- **fix(dashboard):** use opaque background and readable text color on cost chart tooltips ([#11960](https://github.com/diegosouzapw/OmniRoute/pull/11960)) — thanks @ZaimMarzuki +- **fix(antigravity):** send complete loadCodeAssist metadata (ideType/platform/pluginType as numeric enums) ([#11969](https://github.com/diegosouzapw/OmniRoute/pull/11969)) — thanks @rifqiawl +- **fix(core):** resolve DB init race condition and reasoning translation ([#12003](https://github.com/diegosouzapw/OmniRoute/pull/12003)) — thanks @raheemuddin786 +- **fix(admission):** exclude reclaimable page cache from the cgroup pressure ratio ([#12017](https://github.com/diegosouzapw/OmniRoute/pull/12017)) — thanks @HouMinXi +- **fix(build):** prune non-production trees in NFT trace excludes and tsconfig ([#12028](https://github.com/diegosouzapw/OmniRoute/pull/12028)) — thanks @Chewji9875 +- **fix(migrations):** add renamed migration compatibility for 056/073/077/101 ([#12036](https://github.com/diegosouzapw/OmniRoute/pull/12036)) — thanks @oyi77 +- **fix(sse):** default crash-guard logger to console.warn, not console ([#12042](https://github.com/diegosouzapw/OmniRoute/pull/12042)) — thanks @HouMinXi +- **fix(catalog):** derive vision/modalities for built-in auto combos from effective target pool ([#12046](https://github.com/diegosouzapw/OmniRoute/pull/12046)) — thanks @Prajeeth-12 +- **fix(release):** the packaged-app smoke verifies the database opened, not a driver line the primary path never prints (twin of #12032) ([#12047](https://github.com/diegosouzapw/OmniRoute/pull/12047)) +- **fix(barrel):** migrate src/app/ off the @/lib/localDb barrel import (#11795 Phase 2) ([#12051](https://github.com/diegosouzapw/OmniRoute/pull/12051)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate src/lib/ off the localDb barrel to direct db imports (#59) ([#12052](https://github.com/diegosouzapw/OmniRoute/pull/12052)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate open-sse, src/shared, src/sse, src/models, src/domain off the @/lib/localDb barrel import (#11795 Phase 4) ([#12053](https://github.com/diegosouzapw/OmniRoute/pull/12053)) — thanks @jonlwheat2-gif +- **fix(barrel):** delete the @/lib/localDb barrel — every consumer migrated (#11795 Phase 5) ([#12055](https://github.com/diegosouzapw/OmniRoute/pull/12055)) — thanks @jonlwheat2-gif +- **fix(radar):** restore D12 public boundary ([#12057](https://github.com/diegosouzapw/OmniRoute/pull/12057)) +- **fix(dev):** bound webpack and Tailwind scans ([#12075](https://github.com/diegosouzapw/OmniRoute/pull/12075)) — thanks @backryun +- **fix(dev):** isolate root layout settings reads ([#12076](https://github.com/diegosouzapw/OmniRoute/pull/12076)) — thanks @backryun +- **fix(dev):** reduce instrumentation executor fan-out (phase 3) ([#12078](https://github.com/diegosouzapw/OmniRoute/pull/12078)) — thanks @backryun +- **fix(dev):** make logging resources HMR-singleton ([#12079](https://github.com/diegosouzapw/OmniRoute/pull/12079)) — thanks @backryun +- **fix(sse):** keep unavailable forced connections scoped ([#12080](https://github.com/diegosouzapw/OmniRoute/pull/12080)) — thanks @keeltrace +- **fix(dev):** isolate batch dispatch from instrumentation ([#12081](https://github.com/diegosouzapw/OmniRoute/pull/12081)) — thanks @backryun +- **fix(ci):** reconcile release test contract drift ([#12082](https://github.com/diegosouzapw/OmniRoute/pull/12082)) — thanks @backryun +- **fix(ci):** accept CVE-2025-68121 in the prebuilt tls-client .so, auto-close base-red issues, guard Scorecard on the default branch ([#12085](https://github.com/diegosouzapw/OmniRoute/pull/12085)) +- **fix(db):** drop three consumer-less 1proxy exports — dead-code base-red on release/v3.8.51 after the barrel deletion (#12055) ([#12087](https://github.com/diegosouzapw/OmniRoute/pull/12087)) +- **fix(api):** clear the six API-route TypeScript regressions the new gate landed red on ([#12094](https://github.com/diegosouzapw/OmniRoute/pull/12094)) +- **fix(release):** never let the tag-push Create Release append auto notes to the curated body ([#12096](https://github.com/diegosouzapw/OmniRoute/pull/12096)) +- **fix(sse):** honor CLIProxyAPI environment API key ([#12099](https://github.com/diegosouzapw/OmniRoute/pull/12099)) — thanks @RaviTharuma +- **fix(oauth):** bind Google refresh to the client that issued the token ([#12106](https://github.com/diegosouzapw/OmniRoute/pull/12106)) — thanks @HouMinXi +- **fix(dashboard):** make RequestLoggerDetail loadable outside Next — CSS via globals.css + CJS/ESM interop (#11703 base-reds) ([#12114](https://github.com/diegosouzapw/OmniRoute/pull/12114)) +- **fix(leases):** project status lease row to lease columns so joined connection PII never escapes ([#12115](https://github.com/diegosouzapw/OmniRoute/pull/12115)) — thanks @geek007git +- **fix(plugins):** do not kill the plugin process when a fire-and-forget hook times out ([#12116](https://github.com/diegosouzapw/OmniRoute/pull/12116)) — thanks @geek007git +- **fix(guardrails):** restore injection-guard logging on middleware-only routes ([#12117](https://github.com/diegosouzapw/OmniRoute/pull/12117)) — thanks @geek007git +- **fix(plugins):** refresh stored manifest from disk on activate so new hook fields reach existing installs ([#12120](https://github.com/diegosouzapw/OmniRoute/pull/12120)) — thanks @geek007git +- **fix(codex):** preserve existing provider state when bulk-import upserts a matching connection ([#12122](https://github.com/diegosouzapw/OmniRoute/pull/12122)) — thanks @geek007git +- **fix(ci):** clear the two base-reds the 2026-08-30 merge batch left on release/v3.8.51 (round 3) ([#12123](https://github.com/diegosouzapw/OmniRoute/pull/12123)) +- **fix(sse):** strip type:'custom' from Claude tools on agentrouter dispatch ([#12126](https://github.com/diegosouzapw/OmniRoute/pull/12126)) — thanks @wahidsadik371-coder +- **fix(sse):** stop advertising video providers the dispatcher cannot run ([#12131](https://github.com/diegosouzapw/OmniRoute/pull/12131)) — thanks @quiterunner-commits +- **fix(search):** treat HTTP 432 and plan limit errors as transient cooldown ([#12139](https://github.com/diegosouzapw/OmniRoute/pull/12139)) — thanks @HouMinXi +- **fix(sse):** refuse an AI Horde queue that cannot fit the request budget ([#12143](https://github.com/diegosouzapw/OmniRoute/pull/12143)) — thanks @quiterunner-commits +- **fix(ci):** clear the base-reds the afternoon merge batch left on release/v3.8.51 (round 5: provider count 352, TS2554/TS2677) ([#12144](https://github.com/diegosouzapw/OmniRoute/pull/12144)) +- **fix(translator):** strip neutral tool_choice when tools absent in Responses-to-Chat (#12141) ([#12166](https://github.com/diegosouzapw/OmniRoute/pull/12166)) — thanks @HouMinXi +- **fix(guardrails):** pass providerId to getResolvedModelCapabilities in checkComboVision (#12112) ([#12169](https://github.com/diegosouzapw/OmniRoute/pull/12169)) — thanks @HouMinXi +- **fix(translator):** strip plaintext reasoning content for opaque responses backends (#12128) ([#12171](https://github.com/diegosouzapw/OmniRoute/pull/12171)) — thanks @HouMinXi +- **perf(stream):** compile hot-path regexes once, bound token caches, fix quadratic buffering ([#12179](https://github.com/diegosouzapw/OmniRoute/pull/12179)) — thanks @opensource-elearning +- **fix:** resolve compression worker file using runtime anchors instead of… ([#12183](https://github.com/diegosouzapw/OmniRoute/pull/12183)) — thanks @NightStalker-87 +- **fix:** prevent Claude Code session kills via liveness-aware readiness + auto model echo ([#12189](https://github.com/diegosouzapw/OmniRoute/pull/12189)) — thanks @opensource-elearning +- **fix(translator):** preserve falsy primitive values in Gemini and Antigravity function response results ([#12191](https://github.com/diegosouzapw/OmniRoute/pull/12191)) — thanks @Siva010 +- **fix(oauth):** mark empty Antigravity projectId as degraded and clear stale errors (#11284) ([#12205](https://github.com/diegosouzapw/OmniRoute/pull/12205)) — thanks @HouMinXi +- **fix(providers):** gate the Codex auto-ping usage read on the shared quota throttle ([#12209](https://github.com/diegosouzapw/OmniRoute/pull/12209)) — thanks @abhisheksharma2411 +- **fix(combos):** prioritize SQLite row id over inner JSON id and notify delete errors ([#12213](https://github.com/diegosouzapw/OmniRoute/pull/12213)) — thanks @HouMinXi +- **fix(providers):** mark featherless.ai as no longer having a free tier (`hasFree: false`) ([#12216](https://github.com/diegosouzapw/OmniRoute/pull/12216)) — thanks @turbolego +- **fix(deepseek-web):** stop Turbopack dev panic in the PoW worker path resolver ([#12221](https://github.com/diegosouzapw/OmniRoute/pull/12221)) — thanks @hartmark +- **fix(chat-admission):** clarify local 503 source ([#12223](https://github.com/diegosouzapw/OmniRoute/pull/12223)) — thanks @keeltrace +- **fix(dev):** silence webpack runtime module warnings ([#12228](https://github.com/diegosouzapw/OmniRoute/pull/12228)) — thanks @backryun +- **fix(db):** use module.require for CommonJS runtime driver loading ([#12230](https://github.com/diegosouzapw/OmniRoute/pull/12230)) — thanks @raheemuddin786 +- **fix(memory):** honest probe-driven FTS5 keyword status + memory_id rowid sync ([#12231](https://github.com/diegosouzapw/OmniRoute/pull/12231)) — thanks @raheemuddin786 +- **fix(system):** propagate abort signal to stream reader in HTTP version checks ([#12232](https://github.com/diegosouzapw/OmniRoute/pull/12232)) — thanks @raheemuddin786 +- **fix(resilience):** isolate local host execution errors from provider circuit breakers ([#12233](https://github.com/diegosouzapw/OmniRoute/pull/12233)) — thanks @raheemuddin786 +- **fix(sse):** sort injected tools deterministically for prompt caching ([#12234](https://github.com/diegosouzapw/OmniRoute/pull/12234)) — thanks @raheemuddin786 +- **fix(combo):** always clear the loop-safety timer, not just on the happy path (#11804) ([#12245](https://github.com/diegosouzapw/OmniRoute/pull/12245)) +- **fix(quality):** base-red pair — stryker tap registration + turn-pin suites aligned to the window gate ([#12255](https://github.com/diegosouzapw/OmniRoute/pull/12255)) — thanks @backryun +- **fix(dev):** qualify Turbopack runtime boundaries ([#12258](https://github.com/diegosouzapw/OmniRoute/pull/12258)) — thanks @backryun +- **fix(combo):** fall back to full pool when collapsed sole survivor is context-too-small ([#12278](https://github.com/diegosouzapw/OmniRoute/pull/12278)) — thanks @opensource-elearning +- **fix(combo):** bound the pre-dispatch unavailable skip so a stale label cannot dark a pool (#12168) ([#12285](https://github.com/diegosouzapw/OmniRoute/pull/12285)) +- **fix(security):** strip Qwen/Alibaba console-session cookies from provider API responses ([#12287](https://github.com/diegosouzapw/OmniRoute/pull/12287)) +- **fix(usage):** console-aware Token Plan guidance + subscription hint on bailian 401 ([#12288](https://github.com/diegosouzapw/OmniRoute/pull/12288)) +- **fix(resourcePressure):** log numeric detail on every rejection, recover faster ([#12293](https://github.com/diegosouzapw/OmniRoute/pull/12293)) — thanks @hartmark +- **fix(release):** drain the 2026-09-01 base-red window — passthrough usage regression + radarPage i18n keys ([#12327](https://github.com/diegosouzapw/OmniRoute/pull/12327)) +- **fix(ci):** openapi-security-tiers checker must honor routeGuard patterns + imported prefixes ([#12350](https://github.com/diegosouzapw/OmniRoute/pull/12350)) — thanks @ggiak +- **perf(ci):** cache node_modules in the npm-ci-retry composite (#8084 D3) ([#12408](https://github.com/diegosouzapw/OmniRoute/pull/12408)) +- **revert(ui):** point CTAs back at their real destinations ([#12410](https://github.com/diegosouzapw/OmniRoute/pull/12410)) +- **fix(release):** drain the 2026-09-02 base-red — rerank-providers import + api-typecheck baseline ratchet ([#12414](https://github.com/diegosouzapw/OmniRoute/pull/12414)) +- **fix(types):** make system prompt injection noImplicitAny-safe ([#12416](https://github.com/diegosouzapw/OmniRoute/pull/12416)) +- **fix(quality):** validate typecheck baseline schema ([#12419](https://github.com/diegosouzapw/OmniRoute/pull/12419)) +- **fix(memory):** resolve rerank provider node cache import ([#12421](https://github.com/diegosouzapw/OmniRoute/pull/12421)) +- **fix(ci):** repair release-root regressions — pack dedup, web-session syntax, uc-image ids ([#12423](https://github.com/diegosouzapw/OmniRoute/pull/12423)) — thanks @backryun +- **fix(providers):** separate MaxAI and UC credential contracts ([#12431](https://github.com/diegosouzapw/OmniRoute/pull/12431)) +- **fix(cli):** preserve Claude settings on config set ([#12432](https://github.com/diegosouzapw/OmniRoute/pull/12432)) — thanks @KrzysiekSko +- **fix(providers):** repair the maxai credential block truncated by merge auto-resolve ([#12433](https://github.com/diegosouzapw/OmniRoute/pull/12433)) +- **fix(quality):** record the 2026-09-02 merged growth in the file-size baseline ([#12434](https://github.com/diegosouzapw/OmniRoute/pull/12434)) +- **fix(uc):** route UC error strings through sanitizeErrorMessage; allowlist the retired codex id ([#12437](https://github.com/diegosouzapw/OmniRoute/pull/12437)) +- **fix(adapta):** redact streamed upstream errors ([#12438](https://github.com/diegosouzapw/OmniRoute/pull/12438)) +- **fix(sse):** remap non-contiguous upstream tool_calls index to a gap-free output_index ([#12445](https://github.com/diegosouzapw/OmniRoute/pull/12445)) — thanks @hartmark +- **fix(quality):** detect duplicate tool_calls entries in one response ([#12446](https://github.com/diegosouzapw/OmniRoute/pull/12446)) — thanks @hartmark +- **fix(conversations):** resolve turn content OmniRoute never sends back to the client ([#12447](https://github.com/diegosouzapw/OmniRoute/pull/12447)) — thanks @hartmark +- **fix(combo):** do not treat credits-exhausted 401 as auth skip ([#12449](https://github.com/diegosouzapw/OmniRoute/pull/12449)) — thanks @RaviTharuma +- **fix(auth):** do not park healthy quota accounts as expired ([#12452](https://github.com/diegosouzapw/OmniRoute/pull/12452)) — thanks @RaviTharuma +- **fix(responses-continuation):** fail closed on a collector-truncated, empty output array ([#12460](https://github.com/diegosouzapw/OmniRoute/pull/12460)) — thanks @hartmark +- **fix(logging):** raise the SSE payload collector's default cap ([#12461](https://github.com/diegosouzapw/OmniRoute/pull/12461)) — thanks @hartmark +- **fix(resilience):** surface Responses failed.error.message in 502s ([#12472](https://github.com/diegosouzapw/OmniRoute/pull/12472)) — thanks @RaviTharuma +- **fix(security):** unbiased maxai X-Random nonce + stricter URL/regex assertions ([#12502](https://github.com/diegosouzapw/OmniRoute/pull/12502)) +- **fix(video):** re-anchor transcript log-redaction so PII/credential maskers can't reopen the leak (#12150 P1 follow-up) ([#12503](https://github.com/diegosouzapw/OmniRoute/pull/12503)) +- **fix(monitoring):** serve cached credentialHealth off the request path ([#12533](https://github.com/diegosouzapw/OmniRoute/pull/12533)) — thanks @RaviTharuma +- **fix(quota):** keep Antigravity Gemini usable when Claude weekly is empty ([#12566](https://github.com/diegosouzapw/OmniRoute/pull/12566)) — thanks @HouMinXi +- **fix(authz):** hard-gate every credential export and CLI-config write (GHSA-5926-2w35-7h4q) ([#12600](https://github.com/diegosouzapw/OmniRoute/pull/12600)) +- **fix(docs):** restore the Next build — REMOVED_PROVIDERS.md had no frontmatter (base-red #12581) ([#12610](https://github.com/diegosouzapw/OmniRoute/pull/12610)) +- **fix(quality):** re-point the zcodeProtocol public-creds allowlist to line 313 ([#12615](https://github.com/diegosouzapw/OmniRoute/pull/12615)) +- **fix(security):** close 3 advisories — search baseUrl exfil, sk- in the error sanitizer, bifrost relay header leak ([#12620](https://github.com/diegosouzapw/OmniRoute/pull/12620)) +- **fix(providers):** sanitize boolean required and nested bare maps for Gemini (#12269) ([#12624](https://github.com/diegosouzapw/OmniRoute/pull/12624)) — thanks @HouMinXi +- **fix(resilience):** keep Overloaded STREAM_EARLY_EOF off the provider breaker ([#12626](https://github.com/diegosouzapw/OmniRoute/pull/12626)) — thanks @HouMinXi +- **fix(catalog):** re-audit free-tier quotas against official pages ([#12649](https://github.com/diegosouzapw/OmniRoute/pull/12649)) +- **fix(dashboard):** keep a request's pending-tracking id stable across combo target retries ([#12650](https://github.com/diegosouzapw/OmniRoute/pull/12650)) — thanks @hartmark +- **fix(tests):** retire dead model ids from the chat-pipeline integration suite (base-red #12581) ([#12670](https://github.com/diegosouzapw/OmniRoute/pull/12670)) +- **fix:** resolve SqliteError no such table compression_run_telemetry during cleanup ([#12682](https://github.com/diegosouzapw/OmniRoute/pull/12682)) — thanks @groovecityJO +- **fix(combo):** treat a pin-only step as implicit connection allowlist ([#12697](https://github.com/diegosouzapw/OmniRoute/pull/12697)) — thanks @HouMinXi +- **fix(ci):** ship .npmrc in published package so legacy-peer-deps applies to consumers (#11544) ([#12699](https://github.com/diegosouzapw/OmniRoute/pull/12699)) — thanks @KooshaPari +- **fix(docker):** pin 4 CLI tools to exact versions (#12576) ([#12703](https://github.com/diegosouzapw/OmniRoute/pull/12703)) — thanks @KooshaPari +- **fix(docker):** document and harden cli profile trust boundary (#12570) ([#12706](https://github.com/diegosouzapw/OmniRoute/pull/12706)) — thanks @KooshaPari +- **fix(sse):** redact video transcript in pre-guardrail rejected-request logs (#12150 P2 item 7) ([#12710](https://github.com/diegosouzapw/OmniRoute/pull/12710)) +- **fix(i18n):** quote placeholder in OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES description (#12505) ([#12769](https://github.com/diegosouzapw/OmniRoute/pull/12769)) — thanks @KooshaPari +- **fix(api/pricing):** surface validation error message as string, not raw object (#12494) ([#12771](https://github.com/diegosouzapw/OmniRoute/pull/12771)) — thanks @KooshaPari +- **fix(sse):** re-enable prompt compression for native Codex passthrough ([#12834](https://github.com/diegosouzapw/OmniRoute/pull/12834)) — thanks @thomasmaerz +- **fix(dashboard):** model health tests for a provider node set to the Responses API now call `/v1/responses` with a Responses-shaped body instead of `/v1/chat/completions` — those models were reported as `Provider returned HTTP 200 but no text content` even though the same model answered normally through `/v1/responses` ([#13070](https://github.com/diegosouzapw/OmniRoute/issues/13070)) ([#13087](https://github.com/diegosouzapw/OmniRoute/pull/13087)) — thanks @ntdatt812 +- fix(routing): stop a round-robin combo's "opencode" targets from collapsing onto the opencode-zen connection (#11912) ([#13283](https://github.com/diegosouzapw/OmniRoute/pull/13283)) +- fix(routing): stop the reactive-compaction debug log from lying when compression is globally disabled (#11977) ([#13276](https://github.com/diegosouzapw/OmniRoute/pull/13276)) +- fix(electron): relativize standalone-bundle symlink targets so Stage 8 manifest verification stops failing on Windows (#11979) ([#13251](https://github.com/diegosouzapw/OmniRoute/pull/13251)) +- fix(dashboard): surface a visible error when Compression Studio's combined preview run fails (#12061) ([#13288](https://github.com/diegosouzapw/OmniRoute/pull/13288)) +- fix(dashboard): make the compression "Effective pipeline" preview honor the active profile and warn when the master switch is off (#12063) ([#13287](https://github.com/diegosouzapw/OmniRoute/pull/13287)) +- fix(providers): scope TinyCMS Web signer's DOM shims to each call instead of leaking them for the process lifetime, and surface a clean HTTP status on a non-JSON interception-toggles error (#12072) ([#13254](https://github.com/diegosouzapw/OmniRoute/pull/13254)) +- fix(guardrails): stop Vision Bridge from re-selecting a model locked after a 404 (#12111) ([#13259](https://github.com/diegosouzapw/OmniRoute/pull/13259)) +- fix(sse): require Responses-shaped body before native OpenAI-compatible passthrough (#12129) ([#13278](https://github.com/diegosouzapw/OmniRoute/pull/13278)) +- fix(providers): minimax-m3 now collapses manual thinking.type:"enabled" to adaptive, preventing upstream 400 (2013) (#12132) ([#13249](https://github.com/diegosouzapw/OmniRoute/pull/13249)) +- fix(db): scope model visibility overrides by modality so hiding a Chat model no longer hides an identically-ID'd Image/Embeddings/etc. model (#12172) ([#13258](https://github.com/diegosouzapw/OmniRoute/pull/13258)) +- fix(db): scope local-provider apiKey dedup to matching base URL so LM Studio/Ollama-style connections support multiple accounts (#12173) ([#13250](https://github.com/diegosouzapw/OmniRoute/pull/13250)) +- fix(providers): refresh Trae's stale Referer/Origin and forward user timezone so imported connections stop failing with 401 (#12190) ([#13255](https://github.com/diegosouzapw/OmniRoute/pull/13255)) +- fix(providers): route opencode-go/gpt-5.6-luna to /responses instead of /chat/completions (#12196) ([#13275](https://github.com/diegosouzapw/OmniRoute/pull/13275)) +- fix(dashboard): allow deleting the last extra-upstream-header row even when invalid (#12251) ([#13252](https://github.com/diegosouzapw/OmniRoute/pull/13252)) +- **fix(i18n):** translate pre-existing `__MISSING__:` keys for `combo.sort`, `requestLogger.detail` expand/collapse, `common.profile`, and `settings.resilienceCredentialHealth*` across 39 locales ([#12272](https://github.com/diegosouzapw/OmniRoute/issues/12272)) ([#13034](https://github.com/diegosouzapw/OmniRoute/pull/13034)) — thanks @HouMinXi +- fix(cli): run the Node.js runtime compatibility guard before the heavy `tsx/esm` + Commander import chain so an unsupported runtime gets a clear message instead of a raw `Invalid regular expression flags` crash (#12296) ([#13253](https://github.com/diegosouzapw/OmniRoute/pull/13253)) +- fix(dashboard): refresh the providers list after deleting a compatible provider node (#12298) ([#13256](https://github.com/diegosouzapw/OmniRoute/pull/13256)) +- fix(usage): fail closed on API-key budget enforcement when a provider's `auto` routing alias has no pricing row, instead of silently counting it as $0 (#12341) ([#13257](https://github.com/diegosouzapw/OmniRoute/pull/13257)) +- **fix(providers):** include the submitted Agnes video model when polling by `video_id` ([#12356](https://github.com/diegosouzapw/OmniRoute/pull/12356)) — thanks @pacocartones +- **fix(routing):** custom OpenAI-compatible nodes now honor the saved Chat/Responses API type after edits instead of letting the node's original ID prefix override the live connection setting ([#11884](https://github.com/diegosouzapw/OmniRoute/issues/11884)). ([#12358](https://github.com/diegosouzapw/OmniRoute/pull/12358)) — thanks @pacocartones +- fix(sse): surface an error instead of a silent empty 200 when a Claude stream closes with zero bytes (#12398) ([#13285](https://github.com/diegosouzapw/OmniRoute/pull/13285)) +- fix(oauth): warn before the dead localhost:8080 redirect in antigravity/gemini `oauth start` (#12413) ([#13265](https://github.com/diegosouzapw/OmniRoute/pull/13265)) +- fix(providers): stop devin-cli spawn error from double-closing the SSE controller (#12517) ([#13261](https://github.com/diegosouzapw/OmniRoute/pull/13261)) +- **fix(rerank):** clamp Voyage `top_k` to the documents actually sent after empty-string filtering, and honor `return_documents: false` in the NVIDIA response adapter (#12523 — thanks @pacocartones) +- **fix(audio):** `/v1/audio/translations` now resolves combo names the way `/v1/audio/transcriptions` already does, so a combo that `GET /v1/models` advertises is fanned out to its targets instead of being rejected with `400 Invalid translation model: . Use format: provider/model`; literal `provider/model` ids and unknown bare names behave as before (#12536 — thanks @pacocartones) +- **fix(gemini):** strip the JSON-Schema-2020-12 `prefixItems` keyword from Gemini tool schemas at every nesting level, so Claude Code tool definitions no longer fail with `400 Unknown name "prefixItems"` on Gemini models (#12540 — thanks @pacocartones) +- **fix(api-manager):** Expose an accessible loading status while API keys are fetched instead of an empty accessibility tree (#12541 — thanks @pacocartones) +- **fix(video):** Clamp `estimateJpegFrameBytes` at zero for padding-only payloads and build the three encode-side frame data URIs from `JPEG_FRAME_DATA_URI_PREFIX` instead of a repeated literal (#12543 — thanks @pacocartones) +- **fix(devin):** accept Windows `DEVIN_AGENTIC_HOME` sandbox paths (`C:\...\.sandbox\...`) in the isolated-home check so the Devin Claude Bridge no longer fails closed on Windows ([#12405](https://github.com/diegosouzapw/OmniRoute/issues/12405)) (#12545 — thanks @pacocartones) +- **fix(db):** Add `conversation_turn_nodes` and orphaned `agentic_conversations` to the auto-cleanup cycle under the existing `retention.callLogs` window, so identity nodes whose call-log content has already been purged no longer accumulate without bound in `storage.sqlite` (#12548 — thanks @pacocartones) +- **fix(i18n):** Wrap the `~/.claude/profiles//settings.json` placeholder in ICU single quotes in the `OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES` feature-flag description across all 42 locales and the TypeScript default, so next-intl no longer fails with `INVALID_MESSAGE: UNCLOSED_TAG` and the Feature Flags card shows the description instead of the raw key (#12549 — thanks @pacocartones) +- **fix(orchestration):** `updateCloudAgentTask` now publishes the task's real `status` on `agent.task.updated` when an update only touches `result`, `activities` or `error`, instead of the fabricated `"updated"` state, and stays silent when no row matched the id (#12550 — thanks @pacocartones) +- **fix(i18n):** the home "Recent Requests" panel and the Provider Topology legend are now translated instead of rendering English copies on non-English dashboards; the legend reads its own `home.topologyLegend*` labels with consistent casing rather than borrowing the memory-settings "Recent" and analytics "Error" strings (#12551 — thanks @pacocartones). +- **docs(reference):** bring the `FEATURE_FLAGS.md` catalog back to 1:1 with `featureFlagDefinitions.ts` — 20 missing flags added, the two `*_BLOCK_THRESHOLD` env-only knobs moved out of the flag tables, category/total counts and the Live WS port corrected, guarded by a static test (#12552 — thanks @pacocartones) +- **fix(i18n):** backfill missing `usage.kiloPass*` strings in 39 locales and restore `featureFlagOmnirouteDisableThinkingLevelVariantsDescription` in `pt.json` ([#12561](https://github.com/diegosouzapw/OmniRoute/issues/12561)) ([#13027](https://github.com/diegosouzapw/OmniRoute/pull/13027)) — thanks @HouMinXi +- fix(docker): default docker-compose app ports (dashboard/API/live-WS) to loopback instead of `0.0.0.0`, closing the anonymous `/v1` LAN/WAN exposure gap left open by `REQUIRE_API_KEY=false` (#12568) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): close DNS-rebinding SSRF gap in webhook outbound-URL guard (#12569) ([#13243](https://github.com/diegosouzapw/OmniRoute/pull/13243)) +- fix(docker): require a per-session token on the VNC browser CDP bridge and isolate it on a dedicated Docker network (#12571) ([#13241](https://github.com/diegosouzapw/OmniRoute/pull/13241)) +- fix(open-sse): write Adobe Firefly session tokens and cookie jars with 0700/0600 permissions instead of the process umask (#12572) ([#13242](https://github.com/diegosouzapw/OmniRoute/pull/13242)) +- fix(api): remove hardcoded wildcard CORS in openai-to-gemini-sse.ts so the centralized fail-closed CORS gate is the sole source of `Access-Control-Allow-Origin` (#12573) ([#13239](https://github.com/diegosouzapw/OmniRoute/pull/13239)) +- fix(api): enforce API key policy (budget/rate-limit/schedule/endpoint scoping) on the ElevenLabs speech-to-text, text-to-speech and voices proxy routes (#12574) ([#13244](https://github.com/diegosouzapw/OmniRoute/pull/13244)) +- fix(sse): cap HuggingChat NDJSON body size and bound the read loop with the fetch timeout so a stalled or hostile upstream cannot buffer unbounded memory (#12577) ([#13282](https://github.com/diegosouzapw/OmniRoute/pull/13282)) +- fix(docker): scope the cliproxyapi/qdrant/bifrost sidecars to loopback by default and forward `CLIPROXYAPI_MANAGEMENT_KEY` into the cliproxyapi container so its management API is not left both unauthenticated and LAN-published (#12578) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): create DB export temp paths with `fs.mkdtempSync` instead of predictable timestamps (#12579) ([#13237](https://github.com/diegosouzapw/OmniRoute/pull/13237)) +- Cline 401 bodies that say "re-authenticate your Cline account" classify as a refreshable OAuth token, not a terminal expired key. The cooling panel no longer labels every cooldown as a 429; it shows the recorded last error instead. (#12594) ([#13060](https://github.com/diegosouzapw/OmniRoute/pull/13060)) — thanks @HouMinXi +- **fix(catalog):** degrade unknown combo targets instead of dropping LCD modalities, and persist OpenRouter `architecture.input_modalities` into the capability snapshot ([#12613](https://github.com/diegosouzapw/OmniRoute/issues/12613)) ([#13042](https://github.com/diegosouzapw/OmniRoute/pull/13042)) — thanks @HouMinXi +- fix(providers): send `x-api-key` instead of `Authorization: Bearer` for OpenCode Zen's `/v1/responses` endpoint (Muse Spark Contributor models), fixing a 401 on OmniRoute's auth header (#12633) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(sse): surface the actionable "Auggie CLI not found" message when the shell reports a missing `auggie` binary via exit code instead of a spawn error (#12645) ([#13263](https://github.com/diegosouzapw/OmniRoute/pull/13263)) +- **fix(config):** Persist deletions of built-in background-degradation entries — when a stored settings record exists its `degradationMap` is now authoritative instead of being merged under the defaults, so an entry the user removed in the dashboard no longer reappears on the next apply or restart ([#12424](https://github.com/diegosouzapw/OmniRoute/issues/12424)) ([#12647](https://github.com/diegosouzapw/OmniRoute/pull/12647)) — thanks @pacocartones +- **fix(gamification):** action-count badge milestones (First Token, Token Consumer, Token Machine, Token Whale, and the token-sharing tier) are now backed by a durable `xp_action_counts` counter incremented in `addXp()`, instead of a live `COUNT(*)` over `xp_audit_log`. The audit log is pruned by `retention.xpAuditLog` (default 30 days), so on a default install those "lifetime" milestones were really "actions in the last 30 days" and unlocked badges could stop unlocking once old rows aged out. `getActionCount()` and `checkActionCountBadges()` now read the same durable source, and a migration backfills existing totals from the surviving audit rows ([#12546](https://github.com/diegosouzapw/OmniRoute/issues/12546)) ([#12651](https://github.com/diegosouzapw/OmniRoute/pull/12651)) — thanks @pacocartones +- **fix(images):** `/v1/images/edits` now iterates a combo's targets the same way `/v1/images/generations` does (#9239) instead of flattening a bare combo to its first target. A combo whose first target is not edit-capable — or lacks credentials — now falls through to a later edit-capable target rather than hard-erroring, and missing credentials are skipped (not a hard `401`) to match the generations path. The per-target skip/terminal classification is extracted into a shared `runImageComboTargets` loop, so generations behavior is unchanged ([#12547](https://github.com/diegosouzapw/OmniRoute/issues/12547)). ([#12653](https://github.com/diegosouzapw/OmniRoute/pull/12653)) — thanks @pacocartones +- fix(sse): add first-byte watchdog to the TLS-fingerprint transport so a stalled wreq body falls back instead of hanging for minutes (#12656) ([#13272](https://github.com/diegosouzapw/OmniRoute/pull/13272)) +- fix(sse): exempt tiny-budget reasoning probes from combo quality failure and surface persisted-cooldown skips in ALL_TARGETS_SKIPPED diagnostics (#12659) ([#13269](https://github.com/diegosouzapw/OmniRoute/pull/13269)) +- fix(models): declare the real ~1M contextLength for OpenCode Zen's Muse Spark 1.2 models instead of falling back to the 200000 provider default (#12681) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(oauth): align codebuddy-cn OAuth User-Agent with the chat/usage CLI version to avoid WAF false positives (#12702) ([#13264](https://github.com/diegosouzapw/OmniRoute/pull/13264)) +- fix(dashboard): surface an authentication-required banner instead of silently blanking database settings for a guest session (#12709) ([#13270](https://github.com/diegosouzapw/OmniRoute/pull/13270)) +- **fix(resilience):** stop unbounded queue that hangs 6min until Aborted — gate, provider slot, and Bottleneck queue now share a per-connection `maxWaitMs` budget; `fail-closed` on exhaust (503); execution backstop `executionMaxWaitMs` overridable per connection with upstream clamp ([#12715](https://github.com/diegosouzapw/OmniRoute/pull/12715)) — thanks @maxmad64bis (with thanks to @Tushar49 for surfacing the slow-provider need in #12635) +- **fix(auto-combo):** every mode pack now carries `quality` and `reliability` weights (`quality` 0.02, 0.03 in `quality-first`; `reliability` 0.03, 0.04 in `reliability-first`), so selecting a pack no longer silences either signal; `DEFAULT_WEIGHTS` is unchanged ([#12731](https://github.com/diegosouzapw/OmniRoute/pull/12731)) — thanks @maxmad64bis +- **fix(sse):** the provider execution pipeline keeps the upstream error `code`/`type` again — both were lost when the non-streaming leg was extracted, so a config-class `422` (Antigravity missing project) degraded into a generic account cooldown instead of reaching the gate that recognises it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(db):** a legacy `call_logs` table no longer aborts startup — the provider-stats index is created after the column healing runs, not before it, so an install predating `request_type` opens instead of failing with `no such column` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(catalog):** a malformed row in the operator-writable custom-models blob no longer kills every `auto/*` pool ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(tests):** realign the guards that the same day's merges left asserting the old behavior — the provider-count total after GreenPT and EURouter, the GLM stream buffer slot that `#12925` turned into a declared parameter, and the injection scan bound that `#13104` reshaped from a head-only window into head-plus-tail ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- fix(cache): fold tool_choice/tools/response_format into the semantic cache signature so a cached tool_calls response can no longer be replayed for a request whose tool policy forbids it (#12734) ([#13267](https://github.com/diegosouzapw/OmniRoute/pull/13267)) +- **fix(models):** stop treating provider-supplied `isFree:true`/`:free`/`0/0` flags as trusted unless the provider has a documented free tier — only the shipped free-tier catalog decides otherwise; fetched `isFree:true` trusted only for a free-tier provider, custom `isFree:true` via a trusted path ([#12744](https://github.com/diegosouzapw/OmniRoute/pull/12744)) — thanks @maxmad64bis +- fix(memory): authenticate the internal /v1/rerank loopback call so memory reranking no longer silently degrades to unranked order when REQUIRE_API_KEY=true (#12745) ([#13268](https://github.com/diegosouzapw/OmniRoute/pull/13268)) +- fix(sse): parse Ollama Cloud's current usage markup (`$X of $Y used` aria-label, nested width style) (#12749) ([#13260](https://github.com/diegosouzapw/OmniRoute/pull/13260)) +- fix(cli): setup-opencode no longer sends an active context's management token to `/v1/models` when `--api-key`/`OMNIROUTE_API_KEY` is supplied — an explicit flag or the env var now always outranks the context's token, and the flag itself is no longer swallowed by the parent program's global `--api-key` option (#12783) ([#13246](https://github.com/diegosouzapw/OmniRoute/pull/13246)) +- fix(sse): register Arcee AI in the executor provider registry so requests reach api.arcee.ai instead of silently falling back to OpenAI (#12784) ([#13277](https://github.com/diegosouzapw/OmniRoute/pull/13277)) +- **fix(quota):** share one upstream quota read when identical saturation checks arrive at the same time, so traffic bursts don't multiply provider API calls ([#12787](https://github.com/diegosouzapw/OmniRoute/pull/12787)) — thanks @maxmad64bis +- **fix(db):** ignore expired or invalid rate-limit cooldown writes so a stale timestamp can't lock a connection that should be usable — clearing still works as before ([#12788](https://github.com/diegosouzapw/OmniRoute/pull/12788)) — thanks @maxmad64bis +- **fix(providers):** single source for provider order with `xao` ranking alongside `xai-oauth` ([#12790](https://github.com/diegosouzapw/OmniRoute/pull/12790)) — thanks @maxmad64bis +- **fix(routing):** off-table models check the free-model catalog before inheriting premium prices, latency bootstraps from the observed pool median, async tiers read live database pricing with a 90-day freshness gate, and the tier cache invalidates on every pricing write ([#12792](https://github.com/diegosouzapw/OmniRoute/pull/12792)) — thanks @maxmad64bis +- **fix(auto-combo):** snapshot scoring uses the observed per-connection account tier and per-model quality instead of neutral constants, and circuit-open providers rank lower at build time ([#12794](https://github.com/diegosouzapw/OmniRoute/pull/12794)) — thanks @maxmad64bis +- **fix(pool):** empty `auto/*` pools now say why they are empty, and the models listing uses the same paid check as routing ([#12795](https://github.com/diegosouzapw/OmniRoute/pull/12795)) — thanks @maxmad64bis +- fix(routing): recognize CLIProxyAPI's 'unknown provider for model' 400 as fallback-worthy (#12800) ([#13284](https://github.com/diegosouzapw/OmniRoute/pull/13284)) +- **fix(sse):** translate-mode streams now emit the estimated token counts as a trailing usage-only chunk before `[DONE]` when the upstream stays silent, so metered chat clients see totals instead of nothing ([#12828](https://github.com/diegosouzapw/OmniRoute/pull/12828)) — thanks @maxmad64bis +- **fix(db):** provider stats stay truthful on empty and legacy databases: fallback counts default to `0` instead of `null`, latency averages read `null` (not `0`) when no durations were recorded, failures logged before the error-type column existed group under `pre_migration` instead of `unclassified`, and per-provider queries use two new composite indexes ([#12832](https://github.com/diegosouzapw/OmniRoute/pull/12832)) — thanks @maxmad64bis +- fix(nvidia): fail open when a synced model catalog goes stale instead of gating forever (#12849) ([#13248](https://github.com/diegosouzapw/OmniRoute/pull/13248)) +- **fix(db):** WAL maintenance lives in its own module: a `TRUNCATE` checkpoint that hits a busy database now warns with its streak and retries once via `PASSIVE` instead of logging success, and closing-time checkpoints no longer report success on builds without a database file, with the busy totals visible in the authenticated monitoring health payload ([#12853](https://github.com/diegosouzapw/OmniRoute/pull/12853)) — thanks @maxmad64bis +- **fix(health):** quota with no snapshots reads empty instead of a contradicting 0% ([#12857](https://github.com/diegosouzapw/OmniRoute/pull/12857)) — thanks @maxmad64bis +- fix(a2a): accept the dashboard's own session cookie on /a2a so "Run message/send" no longer fails with "Unauthorized: missing or invalid API key" (#12888) ([#13271](https://github.com/diegosouzapw/OmniRoute/pull/13271)) +- **fix(logs):** the Logs grid's in-memory filter pass no longer discards rows the SQL query already matched — selecting an API key from the dropdown (which sends the key's id) returns its calls again, the Combo tab shows every combo instead of only those whose name contains a "1", and the model filter and search cover the same columns as the query ([#12896](https://github.com/diegosouzapw/OmniRoute/pull/12896)) — fixes [#12873](https://github.com/diegosouzapw/OmniRoute/issues/12873) — thanks @ntdatt812 +- **fix(a2a):** `/api/a2a/status` now builds the agent card from the request that asked for it, so a gateway reached at a non-localhost host no longer advertises `http://localhost:20128` as its A2A URL ([#12918](https://github.com/diegosouzapw/OmniRoute/pull/12918)). — thanks @ntdatt812 +- **fix(compression):** progressive aging now appends its `[COMPRESSED:aging:…]` annotation after a turn's `tool_result` blocks instead of in front of them, so Anthropic no longer rejects aged conversations with "`tool_use` ids were found without `tool_result` blocks immediately after" ([#12920](https://github.com/diegosouzapw/OmniRoute/pull/12920)). — thanks @ntdatt812 +- **fix(bedrock):** model import now resolves context limits for every vendor prefix instead of only `anthropic.*`, so `global.openai.gpt-5.6-*` no longer imports with a null `inputTokenLimit` and gets rejected pre-flight at the 200k default ([#12921](https://github.com/diegosouzapw/OmniRoute/pull/12921)). — thanks @ntdatt812 +- **fix(stream):** the 64 KB stream buffer GLM asks for is honoured instead of dropped, and the type error it caused no longer fails the API Route Typecheck gate on every open PR ([#12925](https://github.com/diegosouzapw/OmniRoute/pull/12925)) — thanks @ntdatt812 +- **fix(guardrails):** mask PII inside a `tool_result`'s nested content array, which the masker walked past while redacting its sibling block ([#12930](https://github.com/diegosouzapw/OmniRoute/pull/12930)) — thanks @ntdatt812 +- **fix(dashboard):** the Radar catalog table no longer leaves absent data unexplained — empty limits, unknown context windows, and unreported capabilities each explain themselves on hover, and a new check keeps it that way ([#12937](https://github.com/diegosouzapw/OmniRoute/pull/12937)) — thanks @maxmad64bis +- **fix(sse):** geo-blocked opencode requests rotate to the next account proxy instead of failing, so one refused egress no longer aborts the whole chain ([#12941](https://github.com/diegosouzapw/OmniRoute/pull/12941)) — thanks @maxmad64bis +- **fix(codex):** dashboard "clear cooldown" and the CAS recovery path now drop nested `codexScopeRateLimitedUntil` maps in the same write that nulls `rate_limited_until`, and fresh quota snapshots with headroom lift fallback-sourced scope cooldowns parked by quota preflight ([#12817](https://github.com/diegosouzapw/OmniRoute/issues/12817), [#12860](https://github.com/diegosouzapw/OmniRoute/issues/12860), [#12951](https://github.com/diegosouzapw/OmniRoute/pull/12951)) The dashboard reset-credit button (`consumeCodexResetCredit`) now exercises that same snapshot path after a successful redeem, so a filled quota bar is enough to unpark a leftover fallback Codex child without another manual clear. — thanks @HouMinXi +- **fix(db):** resolve `sql-wasm.wasm` across global npm install and hoisted layouts, ensuring OmniRoute can boot cleanly on Node 24 when native `better-sqlite3` is uncompiled. ([#13035](https://github.com/diegosouzapw/OmniRoute/pull/13035)) — thanks @HouMinXi +- **fix(api):** `DELETE /v1/batches/delete-completed` now sweeps only the calling API key's own completed batches (batches with no owner stay out of a key-scoped sweep on purpose), with an explicit instance-wide mode reserved for authenticated dashboard sessions, a 401 for a presented key that is unknown, revoked, deactivated, banned or expired (never falling through to the session branch), audit logging of every sweep, a sanitized 500 on failure, an owner-scoped file half (a key-scoped sweep never nulls a file another tenant owns) and an atomic sweep — chunked in 200-batch transactions in instance mode — so a mid-way error never leaves a batch pointing at a nulled file (GHSA-wvxc-jp3v-5mg5) ([#12969](https://github.com/diegosouzapw/OmniRoute/pull/12969)) +- **fix(combo):** quota-weighted routing stops drawing on an out-of-credit connection — a 402 now invalidates the stored quota snapshot instead of leaving its stale remaining percentage in place, and a snapshot older than 10 minutes no longer counts as confident headroom for the primary pool ([#12972](https://github.com/diegosouzapw/OmniRoute/pull/12972)) ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- **fix(vision):** Custom Models with "Vision capable" checked no longer have image requests swapped to `glm/glm-4.6v` when the client sends the advertised alias (`vllm/path/...`) or the bare path-shaped id — Vision Bridge now matches the stored override for all three id forms ([#12758](https://github.com/diegosouzapw/OmniRoute/issues/12758)) ([#12974](https://github.com/diegosouzapw/OmniRoute/pull/12974)) — thanks @HouMinXi +- **fix(sse):** transient opencode upstream failures rotate to the next account proxy instead of failing, so one flapping egress no longer aborts the whole chain ([#12975](https://github.com/diegosouzapw/OmniRoute/pull/12975)) — thanks @maxmad64bis +- **fix(azure):** Deployments from GPT-6 onward now send `max_completion_tokens` instead of `max_tokens`, which Azure rejects with HTTP 400. The rule matched a literal `gpt-5`, so each new generation arrived broken; it now matches the generation range, while `gpt-35-turbo` still keeps `max_tokens`. ([#13007](https://github.com/diegosouzapw/OmniRoute/pull/13007)) — thanks @ntdatt812 +- **fix(db):** add an opt-in self-restart circuit for sustained critical memory pressure, gate post-cleanup VACUUM behind a minimum freed-rows threshold, and checkpoint the SQLite WAL every 5 minutes with a size guard that escalates to TRUNCATE, so a growing WAL can no longer stall the event loop into a full outage. ([#13011](https://github.com/diegosouzapw/OmniRoute/pull/13011)) — thanks @HouMinXi +- **fix(auth):** an explicit connection pin may probe a quota-disabled row once and re-enable it on success ([#12874](https://github.com/diegosouzapw/OmniRoute/issues/12874)) ([#13017](https://github.com/diegosouzapw/OmniRoute/pull/13017)) — thanks @HouMinXi +- **fix(api):** thread `X-OmniRoute-Fallback-Attempts` through combo chat completions so streaming and non-streaming responses report how many prior legs were attempted ([#13038](https://github.com/diegosouzapw/OmniRoute/pull/13038)) — thanks @HouMinXi +- Restore provider failure classification and credential refresh on non-streaming requests: classify non-2xx failures to lock models on per-model quota exhaustion, update connection rate limits from headers and body, and pass credential refresh handlers to pipeline execution so 401 tokens can be refreshed and retried (#13043). ([#13069](https://github.com/diegosouzapw/OmniRoute/pull/13069)) — thanks @HouMinXi +- **fix(responses):** wrap forced-non-streaming web_search fallback JSON as Responses SSE so Codex still sees `response.completed` ([#13050](https://github.com/diegosouzapw/OmniRoute/pull/13050)) — thanks @HouMinXi +- **fix(antigravity):** Preserve upstream thought-token usage in normalized completion and reasoning token counts ([#13055](https://github.com/diegosouzapw/OmniRoute/pull/13055)) — thanks @pacocartones +- Fix a concurrency-slot leak in the DeepSeek PoW solver: a worker that failed to spawn (for example a missing worker script) never released its slot, so `MAX_CONCURRENT_WORKERS` failures disabled the solver until restart. ([#13097](https://github.com/diegosouzapw/OmniRoute/pull/13097)) — thanks @anhtahaylove +- **fix(acp):** bound the ACP session output buffers — `stdoutBuffer` and `stderrBuffer` now cap at 1 MiB keeping the most recent output behind a visible `[...output truncated...]` marker, and `stderrBuffer` is reset per prompt instead of accumulating for the lifetime of the session. ([#13100](https://github.com/diegosouzapw/OmniRoute/pull/13100)) — thanks @anhtahaylove +- **fix(acp):** release the `stdout`/`exit` listeners and the idle timer that a `sendPrompt` timeout used to leave attached to the `acpManager` singleton, and drop sessions that exited on their own from the session map instead of keeping them forever. ([#13096](https://github.com/diegosouzapw/OmniRoute/pull/13096)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection and PII scanners now read the text a `tool_result` block carries on `content` (string or nested block list), in messages and in system blocks, so tool output is judged by the same rules as user text ([#13101](https://github.com/diegosouzapw/OmniRoute/pull/13101)) — thanks @ntdatt812 +- **fix(gamification):** close the badge notification SSE stream when the request signal is already aborted before the stream starts — a client that disconnects while the route is still awaiting auth used to leave both the 2s unlock poll and the 15s heartbeat running for the lifetime of the process. ([#13106](https://github.com/diegosouzapw/OmniRoute/pull/13106)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection scan now spends its 16 KB budget on both ends of the request instead of the first 16 KB only, so `system`, `instructions`, `query`, `documents` and the newest turns are no longer hidden behind one long message ([#13104](https://github.com/diegosouzapw/OmniRoute/pull/13104)) — thanks @ntdatt812 +- **fix(dashboard):** expose the Volcano Ark console cookie on quota scraping and unwrap connect-error objects so the dashboard shows the upstream message ([#13107](https://github.com/diegosouzapw/OmniRoute/pull/13107)) — thanks @HouMinXi +- **fix(db):** release the `beforeExit`/`SIGINT`/`SIGTERM` handlers when a `node:sqlite` adapter closes, so a closed adapter and its database handle are no longer pinned to `process` for the lifetime of the run — the same treatment #7494 gave the sql.js adapter. ([#13109](https://github.com/diegosouzapw/OmniRoute/pull/13109)) — thanks @anhtahaylove +- **fix(translator):** `contentSchema` and `unevaluatedItems` are now treated as subschema positions by the tool-schema sanitizer, so a truncation placeholder in either is replaced with a permissive schema instead of being forwarded as a string ([#13110](https://github.com/diegosouzapw/OmniRoute/pull/13110)) — thanks @ntdatt812 +- **fix(cli-helper):** clear the `createLogStream` timeout on the abort path — `stop()` aborts the in-flight fetch and returned through the `signal.aborted` branch, which skipped `clearTimeout` and left an armed timer per stopped stream. The stream reader is now also cancelled when the read loop exits early. ([#13114](https://github.com/diegosouzapw/OmniRoute/pull/13114)) — thanks @anhtahaylove +- **fix(docs):** drop TLS-impersonation, MITM-decrypt, supply-chain attestation, and XOR-mask writeups from the public `/docs` catalog and Docker image. Files stay in git for engineers; operators who need them open the repo, not the website. ([#13136](https://github.com/diegosouzapw/OmniRoute/pull/13136)) — thanks @HouMinXi +- **fix(combo):** parse numeric-epoch `rate_limited_until` in the combo cooldown read path ([#13141](https://github.com/diegosouzapw/OmniRoute/pull/13141)) — thanks @maxmad64bis +- **fix(opencode):** both OpenCode plugins now reject a gateway address typed without `http://` at configuration time, instead of publishing every model with an api url no client can call, and the v2 plugin no longer publishes a model card whose api url is blank or relative ([#13142](https://github.com/diegosouzapw/OmniRoute/pull/13142)) — thanks @maxmad64bis +- **fix(providers):** lock opencode model on upstream 400 model-unavailable ([#13146](https://github.com/diegosouzapw/OmniRoute/pull/13146)) — thanks @maxmad64bis +- **fix(logging):** keep the provider exchange rather than the raw client bodies when a call log exceeds its size budget, and show that recovered payload in the request-detail panel instead of replacing it with the stored response body ([#13147](https://github.com/diegosouzapw/OmniRoute/pull/13147)) — thanks @maxmad64bis +- **fix(traffic-inspector):** the WebSocket route no longer leaks a traffic-buffer subscriber and a 30s ping timer when the client socket is already closed at handler time — listeners are attached before any resource is acquired, a destroyed socket bails out early, and the ping interval stops on a dead socket where `write()` never throws ([#13155](https://github.com/diegosouzapw/OmniRoute/pull/13155)) — thanks @anhtahaylove +- **fix(telegram):** bound the per-user API key cache in the Telegram chat proxy so a burst of distinct chat ids can no longer grow the process heap without limit ([#13165](https://github.com/diegosouzapw/OmniRoute/issues/13165)) ([#13166](https://github.com/diegosouzapw/OmniRoute/pull/13166)) — thanks @anhtahaylove +- **fix(stream):** cancel the upstream response body when the JSON-to-SSE sniff unwinds on a body timeout, so a stalled upstream no longer pins the connection ([#13169](https://github.com/diegosouzapw/OmniRoute/issues/13169)) ([#13171](https://github.com/diegosouzapw/OmniRoute/pull/13171)) — thanks @anhtahaylove +- **fix(telegram):** authenticate webhook deliveries with Telegram's `secret_token` so an unauthenticated caller can no longer mint API keys or spend upstream quota ([#13172](https://github.com/diegosouzapw/OmniRoute/issues/13172)) ([#13175](https://github.com/diegosouzapw/OmniRoute/pull/13175)) — thanks @anhtahaylove +- **fix(models):** give discoverable Gemini 3.8 Flash ids their own 65536 output spec so Antigravity no longer clamps them to 16384 ([#13195](https://github.com/diegosouzapw/OmniRoute/pull/13195)) — thanks @HouMinXi +- **fix(dashboard):** leftover catalog-removed provider rows (gemini-cli) can be listed and purged from the providers page ([#13067](https://github.com/diegosouzapw/OmniRoute/issues/13067)) ([#13197](https://github.com/diegosouzapw/OmniRoute/pull/13197)) — thanks @HouMinXi +- **fix(auth):** a dashboard session now requires the `authenticated: true` claim that login, OIDC and the session refresh already emit — a JWT merely signed with `JWT_SECRET` (for example the Cursor CLI passthrough token, which any API-key holder can obtain) no longer verifies as the `auth_token` cookie on any route, the WebSocket handshake or the live server; existing sessions keep working ([#13298](https://github.com/diegosouzapw/OmniRoute/issues/13298)) ([#13375](https://github.com/diegosouzapw/OmniRoute/pull/13375)) +- **fix(api):** `DELETE /v1/batches/delete-completed` now applies the caller's API-key policy (endpoint allowlist, schedule, usage cap, rate limit) like every other `/v1` route, commits the key-scoped sweep per 200-batch chunk instead of holding one write lock for the whole sweep, refuses to loop without progress, and rejects a scope that names both a key and `allTenants` ([#13684](https://github.com/diegosouzapw/OmniRoute/pull/13684)) +- Restore the API-route typecheck gate: the non-streaming leg result lost its discriminated-union narrowing after the server-owned tool loop reassignment, producing 13 new TS2339 diagnostics in `chatCore.ts`. ([#12963](https://github.com/diegosouzapw/OmniRoute/pull/12963)) +- Restore four non-streaming behaviours the server-owned tool loop refactor dropped: client aborts map to 499 with the fixed `Request aborted` message, an aborted request no longer logs a synthetic `clientResponse`, Claude prompt-cache telemetry is recorded again, and a body that cannot be canonicalized no longer throws when the tool loop is off. ([#12990](https://github.com/diegosouzapw/OmniRoute/pull/12990)) +- **fix(skills):** The CLI registry parser now reads positionals declared with `.addArgument()`, not only those written inline in `.command()`. `tunnel create [type]` was being published as `tunnel create`, so the agent-skills sync gate reported drift on every branch and regenerating would have deleted the argument. ([#13009](https://github.com/diegosouzapw/OmniRoute/pull/13009)) — thanks @ntdatt812 +- **fix(combos):** dashboard combo test uses a short prompt, serial probes, and a 60s timeout so reasoning models and rate-limited free pools do not fail the health check ([#13001](https://github.com/diegosouzapw/OmniRoute/pull/13001)) — thanks @HouMinXi +- fix(compression): terminate idle worker threads on eviction so long-running instances stop leaking OS threads and MessagePorts ([#13091](https://github.com/diegosouzapw/OmniRoute/pull/13091)) — thanks @anhtahaylove +- Redact Google API keys of any length in error bodies: the pattern required exactly 39 characters, so shorter or longer `AIza…` credentials passed through unredacted. ([#12964](https://github.com/diegosouzapw/OmniRoute/pull/12964)) +- **fix(grok-cli):** a 402 "Grok Build usage balance exhausted" parks that Grok login as out of credit (Grok Build CLI, grok.com cookie, and xAI OAuth share the weekly pool). Combo routing then tries the next login instead of locking the model for every account in the pool ([#13061](https://github.com/diegosouzapw/OmniRoute/pull/13061)) — thanks @HouMinXi +- fix(compression): spawn the LLMLingua worker with a file URL object so compression actually runs instead of silently failing open on Node ([#13093](https://github.com/diegosouzapw/OmniRoute/pull/13093)) — thanks @anhtahaylove +- **fix(test):** run the local `test` and `test:unit` scripts at concurrency 4 so a full-suite run no longer exhausts the machine's commit charge and kills unrelated processes ([#13187](https://github.com/diegosouzapw/OmniRoute/pull/13187)) — thanks @anhtahaylove +- **fix(monitoring):** `GET /api/monitoring/health` `credentialHealth` now includes a bounded `failedConnections` list (`connectionId`, `status`, sanitized `lastError`) when the probe-cache gauge `failed>0`, plus `source: probe-cache` and a cheap `staleDbNonOkCount` for sticky SQLite `test_status` on active rows. Documents that the live gauge is not `provider_connections.test_status`. ([#12876](https://github.com/diegosouzapw/OmniRoute/pull/12876)) — thanks @RaviTharuma +- **fix(resilience):** an openai-compatible multi-upstream gateway no longer marks the whole connection `credits_exhausted` when a single upstream (or the `/models` / representative-model health probe) returns 402. Quota failures stay model-scoped; true connection-wide auth failures are unchanged. Single-credential 402 key disable ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239)) is preserved. ([#12875](https://github.com/diegosouzapw/OmniRoute/pull/12875)) — thanks @RaviTharuma +- **fix(dashboard):** nine fixes on the Orchestration Canvas, all diagnosed in the Phase 2 reviews. A source that fails now keeps the timestamp of its FIRST failure instead of being re-stamped every poll — the stale line said "since the last poll" no matter how long the source had been down, and the churn also defeated the snapshot's stable identity (it serializes the source list), so the canvas re-rendered on every tick while anything was broken. A source that HAD data and only then started failing is flagged too: previously only a source with no node at all got the warning, so a source that went down mid-session kept a healthy-looking node forever. The rest are pointwise: clicking a filter chip cancels the pending search debounce (left armed, it fired ~300ms later and silently reverted the chip); the search input carries an accessible name; `?state=running, failed` parses like the unpadded form instead of dropping the padded value; the CSV toggle helper is defined once in `model/urlParams.ts` rather than twice; the agents tab tells "nothing running" apart from "the filter matched nothing", offering a clear-filters button instead of setup links that would be wrong advice there; edges stop emitting SMIL particles above 40 simultaneously active edges, keeping the colored stroke; and the drawer's error banner clears when a retried action succeeds. ([#12983](https://github.com/diegosouzapw/OmniRoute/pull/12983)) +- **fix(i18n):** the nine locales added with the EU-language batch (Greek, Estonian, Irish, Croatian, Lithuanian, Latvian, Maltese, Slovenian, Serbian) were missing the eleven Orchestration Canvas keys that Phase 3 introduced, so the compare-runs panel and the "no runs match these filters" empty state fell back to English in those languages (`deepMergeFallback` substitutes English for an absent key, so nothing rendered blank — it rendered untranslated). The coverage gate does not catch this: it enforces an 80% floor per locale, and eleven missing keys out of ~13,000 leaves coverage at 99.9%. Translated for real in each language, calibrated against the wording each file already uses for "run", "filter" and "skill". ([#13294](https://github.com/diegosouzapw/OmniRoute/pull/13294)) +- **fix(dashboard):** five follow-ups from the Phase 2 Orchestration Canvas review. The History drawer now shows the memory section for runs that already left the live TTL window: the persisted `memory_hits` event is parsed into `metadata.memoryHits` with the same defensive validation the drawer applies, and — because that event is observability rather than a state transition — it no longer leaks into the timeline, where it had been inheriting the task's state and rendering as a duplicate transition. The A2A memory recall runs against its own 1.5s deadline instead of inheriting the memory backend's 30s one; overshooting degrades exactly like any other recall failure (no hits, task proceeds), and the timer is cleared on both paths. Repeating a Conductor run carries its `requirements.cli`/`requirements.model` forward, so the new run is pinned to the same runner profile and model rather than drifting to whatever the fleet picks. A successful repeat from the Agents tab now focuses the run it created, instead of leaving the operator on the finished one. And the auth test for `POST /api/conductor/tasks` moved into the shared `ROUTES` array rather than restating the pattern. ([#12988](https://github.com/diegosouzapw/OmniRoute/pull/12988)) +- fix(plugins): stop leaking an exit listener per plugin hook timeout, which triggered MaxListenersExceededWarning on plugins that ignore SIGTERM ([#13092](https://github.com/diegosouzapw/OmniRoute/pull/13092)) — thanks @anhtahaylove +- **fix(validation):** Provider node edits no longer fail with a generic "Invalid request" when the optional daily-quota reset fields are left blank. The dashboard sends `dailyQuotaResetTimezone` and `dailyQuotaResetHour` as `null`, and only the hour accepted it. ([#13066](https://github.com/diegosouzapw/OmniRoute/issues/13066)) ([#13083](https://github.com/diegosouzapw/OmniRoute/pull/13083)) — thanks @ntdatt812 +- **fix(combo):** quota-aware expansion drops banned, inactive, missing, and wrong-provider connections before quota fetch or model dispatch; pins and allowlists stay selectors, not a bypass. Antigravity automatic exhaustion now requires a reported zero remaining, so a positive balance below 1% stays eligible. ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- Fix an intermittent failure in the quota-weighted routing test suite: scores are a function of `Date.now()`, so peers with identical quota scored microseconds apart never tied and swapped order. ([#12946](https://github.com/diegosouzapw/OmniRoute/pull/12946)) +- fix(volcengine): resolve quota window mapping gaps, safe multi-connection console binding, and response cookie sanitization ([#12950](https://github.com/diegosouzapw/OmniRoute/pull/12950)) — thanks @HouMinXi +- **fix(test):** resolve the WebDAV handler path with `fileURLToPath` so the suite's 37 WebDAV tests run on Windows instead of failing with a doubled `C:\C:\` drive prefix ([#13196](https://github.com/diegosouzapw/OmniRoute/pull/13196)) — thanks @anhtahaylove +- **fix(ui):** fall back from invalid AnySearch icon alias (direct commit `9587e07b69`) +- **fix(sse):** stop rebuilding a truncated summary from the collector's cap-dropped event array ([#12718](https://github.com/diegosouzapw/OmniRoute/pull/12718)) — thanks @hartmark +- **fix(conversations):** show a pending spinner for unresolved tool nodes instead of "(empty)" ([#12727](https://github.com/diegosouzapw/OmniRoute/pull/12727)) — thanks @hartmark +- **fix(streaming):** fail fast when an upstream stream produces only lifecycle/heartbeat events, never real content ([#12741](https://github.com/diegosouzapw/OmniRoute/pull/12741)) — thanks @hartmark +- **fix(responses-continuation):** bridge the write-in-flight window with an in-memory pending store ([#12854](https://github.com/diegosouzapw/OmniRoute/pull/12854)) — thanks @hartmark +- **fix(translator):** drop or map agent_message on Chat Completions fallback ([#12880](https://github.com/diegosouzapw/OmniRoute/pull/12880)) — thanks @RaviTharuma +- **fix(api):** page and stream-complete GET /v1/models for large catalogs ([#12882](https://github.com/diegosouzapw/OmniRoute/pull/12882)) — thanks @RaviTharuma +- **fix(quota):** align AUTH window labels with usage API and clarify 503 ([#12884](https://github.com/diegosouzapw/OmniRoute/pull/12884)) — thanks @RaviTharuma +- **fix(ci):** clear two base-reds on release/v3.8.51 — mutation-coverage gate + image-only-model guard ([#12945](https://github.com/diegosouzapw/OmniRoute/pull/12945)) +- **fix(api):** scope batch bulk-delete to the calling API key ([#13211](https://github.com/diegosouzapw/OmniRoute/pull/13211)) +- **fix(tests):** align model-sync log assertion to shipped path redaction (base-red) ([#13227](https://github.com/diegosouzapw/OmniRoute/pull/13227)) +- **fix(test):** make the ToS heading guard actually require the parentheses ([#13228](https://github.com/diegosouzapw/OmniRoute/pull/13228)) +- **fix(quality):** register 4 drifted covering tests in stryker tap.testFiles ([#13229](https://github.com/diegosouzapw/OmniRoute/pull/13229)) +- **fix(logging):** mask *-api-key headers in request-log pipeline (#13273) ([#13401](https://github.com/diegosouzapw/OmniRoute/pull/13401)) — thanks @KooshaPari +- **fix(tests):** correct stale Jina catalog prefix in custom-models test ([#13403](https://github.com/diegosouzapw/OmniRoute/pull/13403)) — thanks @KooshaPari +- **fix(backend):** prune old backups after health-check-repair snapshots (#13308) ([#13404](https://github.com/diegosouzapw/OmniRoute/pull/13404)) — thanks @KooshaPari +- **fix(backend):** guard memory_fts_au trigger with WHEN clause to stop FTS bloat (#13326) ([#13405](https://github.com/diegosouzapw/OmniRoute/pull/13405)) — thanks @KooshaPari +- **fix(free-tiers):** remove obsolete Together $25 signup credit (#12526) ([#13407](https://github.com/diegosouzapw/OmniRoute/pull/13407)) — thanks @KooshaPari +- **fix:** skip non-chat generation models in Test all models ([#13409](https://github.com/diegosouzapw/OmniRoute/pull/13409)) — thanks @KooshaPari +- **fix(providers):** de-duplicate agy and antigravity model catalogs ([#13410](https://github.com/diegosouzapw/OmniRoute/pull/13410)) — thanks @KooshaPari +- **fix(dashboard):** send provider in body for mini-playground web search ([#13411](https://github.com/diegosouzapw/OmniRoute/pull/13411)) — thanks @KooshaPari +- **fix(resilience):** add CJK quota-exhaustion patterns to 429 classifier ([#13412](https://github.com/diegosouzapw/OmniRoute/pull/13412)) — thanks @KooshaPari +- **fix(backend):** eval runner: failed calls no longer score as passed + dotAll regex ([#13413](https://github.com/diegosouzapw/OmniRoute/pull/13413)) — thanks @KooshaPari +- **fix(backend):** eval runner opts out of output-style and memory injection ([#13414](https://github.com/diegosouzapw/OmniRoute/pull/13414)) — thanks @KooshaPari +- **fix:** chain serial quarantine step in npm test script (fixes #13286) ([#13418](https://github.com/diegosouzapw/OmniRoute/pull/13418)) — thanks @KooshaPari +- **fix(antigravity):** derive stable session ID from credentials for prompt-cache reuse (#13305) ([#13424](https://github.com/diegosouzapw/OmniRoute/pull/13424)) — thanks @KooshaPari +- **fix(memory):** merge memory into top-level system field instead of unshifting at messages[0] (#13425) ([#13427](https://github.com/diegosouzapw/OmniRoute/pull/13427)) — thanks @KooshaPari +- **fix(compression):** preserve instruction blocks from lossy engines ([#13523](https://github.com/diegosouzapw/OmniRoute/pull/13523)) — thanks @KooshaPari +- **fix(compression):** preserve polarity words and newlines in ultra heuristic ([#13524](https://github.com/diegosouzapw/OmniRoute/pull/13524)) — thanks @KooshaPari +- **fix(compression):** run user preservePatterns before built-in patterns (#13457) ([#13525](https://github.com/diegosouzapw/OmniRoute/pull/13525)) — thanks @KooshaPari +- **fix(reasoning):** expand deepseek-v4 regex to match v4.1+ and add command-code provider (#13430) ([#13528](https://github.com/diegosouzapw/OmniRoute/pull/13528)) — thanks @KooshaPari +- **fix(providers):** add alwaysReasons metadata for reka-flash-3 ([#13531](https://github.com/diegosouzapw/OmniRoute/pull/13531)) — thanks @KooshaPari +- **fix(playground):** allow Compare columns to scroll independently ([#13532](https://github.com/diegosouzapw/OmniRoute/pull/13532)) — thanks @KooshaPari +- **fix(security):** empty whitelist allows through to prevent admin lockout ([#13534](https://github.com/diegosouzapw/OmniRoute/pull/13534)) — thanks @KooshaPari +- **fix(tests):** add DISABLE_IOREG_STRATEGY env var for macOS test compatibility (fixes #13467) ([#13539](https://github.com/diegosouzapw/OmniRoute/pull/13539)) — thanks @KooshaPari +- **fix(zed-hosted):** map developer role and Gemini enums for Zed proxy ([#13543](https://github.com/diegosouzapw/OmniRoute/pull/13543)) — thanks @KooshaPari +- **fix(analytics):** normalize provider aliases to canonical id in usage_history ([#13545](https://github.com/diegosouzapw/OmniRoute/pull/13545)) — thanks @KooshaPari +- **fix(combo):** use traceId for call log id to prevent UNIQUE constraint failure ([#13546](https://github.com/diegosouzapw/OmniRoute/pull/13546)) — thanks @KooshaPari +- **fix(auth):** check model lockout before returning synthetic noauth connection ([#13547](https://github.com/diegosouzapw/OmniRoute/pull/13547)) — thanks @KooshaPari +- **fix(guardrails):** skip credential redaction for base64 image data URLs ([#13550](https://github.com/diegosouzapw/OmniRoute/pull/13550)) — thanks @KooshaPari +- **fix(resilience):** increase requestQueue.maxWaitMs default from 15s to 30s ([#13553](https://github.com/diegosouzapw/OmniRoute/pull/13553)) — thanks @KooshaPari +- **fix(docs):** document OMNIROUTE_READY_TIMEOUT_MS and allowlist the test-only DISABLE_IOREG_STRATEGY ([#13692](https://github.com/diegosouzapw/OmniRoute/pull/13692)) + ### 📝 Maintenance ---- +- **docs(readme):** reconcile live v3.8.50 provider, free-tier, CLI, routing, test, community, sponsor, acknowledgment, and SVG metrics with their audited source denominators, including a deduplicated OmniRoute-in-Action snapshot and distinct contributor rankings for merged pull requests, GitHub-attributed commits, and Git history ([#11356](https://github.com/diegosouzapw/OmniRoute/pull/11356)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **docs:** sync the canonical provider count 354 → 356 across `README.md`, `AGENTS.md`, `llm.txt` (+ 42 i18n mirrors), the four README SVG diagrams, `docs/reference/PROVIDER_REFERENCE.md` (regenerated) and the `package.json` description after Opper (#11629) and 1min.ai (#11631) boarded the catalog — closes the `check:docs-counts-sync` strict drifts that kept `release/v3.8.51` red ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11673](https://github.com/diegosouzapw/OmniRoute/pull/11673)) — thanks @jonlwheat2-gif +- **test(providers):** update count-derived assertions after the v3.8.51 provider additions — `APIKEY_PROVIDERS` 233 → 235 (Opper #11629 + 1min.ai #11631), reserved-prefix REGISTRY walk 395 → 398, `WEB_FETCH_PROVIDERS` now includes `nimble-search` (#11620), and the provider translate-path golden snapshot regenerated ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11674](https://github.com/diegosouzapw/OmniRoute/pull/11674)) — thanks @jonlwheat2-gif +- **test(sse):** bump the hard-lease connection-query inventory for `src/lib/tokenHealthCheck.ts` to 2 — the verify-only web-cookie sweep added by #11495 split the single `getProviderConnections` call into oauth + cookie variants, which the frozen inventory had not tracked ([#11495](https://github.com/diegosouzapw/OmniRoute/pull/11495)). ([#11677](https://github.com/diegosouzapw/OmniRoute/pull/11677)) — thanks @jonlwheat2-gif / @oyi77 +- **chore(release):** clear the `release/v3.8.51` base-red gates — docs migration count (+38 i18n mirrors), stryker `tap.testFiles`, lockfile registry host, stale ESLint suppressions, the 7 lint regressions they were hiding, the stale `APIKEY_PROVIDERS` partition count (231 → 233), the `glm-5.3-max` tier missing from the GLM test table, a dead `modelType === "chat"` comparison in the models catalog, and the `resolveLiveWsUrl` / `sanitizeLiveWsPort` helpers from #11388 that a later merge dropped from `wsPath.ts` (dashboard live WebSocket URL resolution) ([#11502](https://github.com/diegosouzapw/OmniRoute/pull/11502)) — thanks @MumuTW +- **chore(free-models):** the free-tier regime table now declares which totals figure each regime feeds and whether it may take the no-auth shortcut, so the sets that used to repeat those answers by hand are derived from it — a new `freeType` no longer compiles until it has answered every question, instead of silently contributing to no total ([#11537](https://github.com/diegosouzapw/OmniRoute/pull/11537)) — thanks @maxmad64bis +- **docs(routing):** LKGP strategy copy now states provider-level stickiness and the explicit rules fallback instead of promising per-target pinning ([#11555](https://github.com/diegosouzapw/OmniRoute/pull/11555)) — thanks @maxmad64bis +- **test(chatcore):** pin the response tool-name alias resolution against the ordering hazard that broke every Gemini/Antigravity MCP tool call in v3.8.49. `extractRequestToolIdentityMap` deletes `translatedBody._toolNameMap`, so the later read is always undefined and the ledger survives only through the `requestToolIdentityMap` fallback — removing that fallback previously left the entire tool-name suite green. The resolution moves into `resolveResponseToolNameMap()` next to the map it depends on, with a regression guard that fails without the recovery. No behaviour change. ([#11575](https://github.com/diegosouzapw/OmniRoute/pull/11575)) — thanks @amartinawi +- **docs(env):** document `OMNIROUTE_CORPUS_CACHE_SIZE` in `.env.example` and `ENVIRONMENT.md`, so the env/docs contract check stops failing Docs Gates on every branch ([#11576](https://github.com/diegosouzapw/OmniRoute/pull/11576)) — thanks @ntdatt812 +- **chore(stryker):** register `route-guard-tunnel-processes-local-only` and `universal-quota-aware-routing` in `tap.testFiles`, so their mutant kills count for the three modules they cover ([#11577](https://github.com/diegosouzapw/OmniRoute/pull/11577)) — thanks @ntdatt812 +- **test(authz):** pin `LOCAL_ONLY_API_GET_EXEMPTIONS` by exact membership instead of by entry count, so the guard names the offending path and also catches a substitution ([#11580](https://github.com/diegosouzapw/OmniRoute/pull/11580)) — thanks @ntdatt812 +- **test(build):** pin `resolveNextBuildBundlerFlag` to the `OMNIROUTE_USE_TURBOPACK` contract it implements, across runtimes, instead of asserting an unimplemented Bun override ([#11583](https://github.com/diegosouzapw/OmniRoute/pull/11583)) — thanks @ntdatt812 +- **test(claude-web):** the Claude Web transport test's barrel anchors no longer pin the exact construction spelling. #11421 made the executor map lazy, so `/"claude-web": new ClaudeWebExecutor\(\)/` stopped matching and the test went red on `release/v3.8.51` itself — a base-red gate every open PR inherited. The anchors now match the alias key and the class name and tolerate either spelling in between, and were checked to still fail when an alias is genuinely repointed at another executor ([#11585](https://github.com/diegosouzapw/OmniRoute/pull/11585)) — thanks @ntdat812 +- **test(models):** pin `qwen3.8-max` resolution per provider — two of the four catalogs now serve the bare id and no longer carry `-preview` ([#11590](https://github.com/diegosouzapw/OmniRoute/pull/11590)) — thanks @ntdatt812 +- **test(executors):** drop the stale 128-tool truncation assertion that contradicted the #11444 pass-through contract ([#11591](https://github.com/diegosouzapw/OmniRoute/pull/11591)) — thanks @ntdatt812 +- **test(token-health):** pin the current terminal-skip boundary — expired with retry budget is probed, `account_deactivated` stays skipped ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)) — thanks @ntdatt812 +- **test(models):** follow #11503's retarget of the `gemini-3-pro-high` alias to the hyphenated catalog id ([#11593](https://github.com/diegosouzapw/OmniRoute/pull/11593)) — thanks @ntdatt812 +- **refactor(video bridge):** extract per-video acquisition, whole-result caching, description, and metrics/abort/cleanup out of `VideoBridgeGuardrail.preCall` into a `processVideoPart` seam in a new `videoBridgePipeline.ts`, behind explicit `VideoMediaBrokerPort`, `VideoAudioTranscriptionPort`, and `VideoDrilldownPort` boundaries; `preCall` now only handles request traversal, policy, and response aggregation. The Video tab's FFmpeg/ffprobe runtime status is now an explicit `unknown` / `restricted` / `unavailable` / `available` state instead of a nullable boolean pair, fixing a case where an in-flight or failed probe was mislabeled as "install FFmpeg" ([#11657](https://github.com/diegosouzapw/OmniRoute/issues/11657)). ([#12016](https://github.com/diegosouzapw/OmniRoute/pull/12016)) +- **test(opencode-plugin):** add unit test coverage for `formatFreeBudget()` naming helper ([#11660](https://github.com/diegosouzapw/OmniRoute/pull/11660)) — thanks @f9td56dbgh-hub +- **docs(video):** clarify that the Video Bridge transcript `source` field (`client`, `embedded`, `audio-bridge`) is presently caller-declared and not yet server-verified — OmniRoute enforces the enum shape but does not cryptographically confirm that an `embedded`/`audio-bridge` label came from a server-owned extraction ([#11661](https://github.com/diegosouzapw/OmniRoute/issues/11661)). ([#12001](https://github.com/diegosouzapw/OmniRoute/pull/12001)) +- **chore(providers):** retire the distributed Raycast Relay and Hailuo Web integrations after a provenance audit confirmed substantial derivation from GPL-3.0 sources; the official GitHub and MiniMax API providers remain available. Migration 163 disables legacy connections and invalidates their active leases while preserving connection IDs, audit/history rows, quota snapshots, usage records, and fail-closed API-key connection policies. ([#11691](https://github.com/diegosouzapw/OmniRoute/pull/11691)) +- **chore(providers):** retire the Felo Web provider while its GPL-derived provenance remains on licensing HOLD ([#11698](https://github.com/diegosouzapw/OmniRoute/pull/11698)) — stale runtime and database references now fail closed while existing connection, lease, and usage history remains preserved for auditability. +- **fix(sse):** retire the Gemini Web image-generation bridge while provenance and licensing remain on HOLD, preserving Gemini Web chat/auth/tools and legitimate Gemini/Nano Banana image providers ([#11708](https://github.com/diegosouzapw/OmniRoute/pull/11708)) +- **chore(audio):** retire the built-in EdgeTTS provider and its 11-voice catalog while provenance/licensing review remains on HOLD; the generic `/v1/audio/speech` endpoint and the other speech providers remain available ([#11711](https://github.com/diegosouzapw/OmniRoute/pull/11711)) +- **fix(providers):** retire the unofficial Qwen Web integration and its `qwen-web`/`qw` identities while third-party provenance remains on HOLD ([#11713](https://github.com/diegosouzapw/OmniRoute/pull/11713)) — direct requests now fail closed with a sanitized HTTP 410 and combos skip the retired target, while migration guards preserve connection IDs, allowlists, usage/audit history and invalidate active leases. Supported Qwen API, Qwen Code, cloud, token-plan, audio, local and model-name surfaces remain available; restoring Qwen Web requires a qualified provenance/license decision. +- **fix(providers):** retire `microsoft-designer-web` and its `msdesigner` alias from runtime and catalogs while preserving stored credentials, provider history and allowlists as inactive records — source dependency on gpt4free's [`MicrosoftDesigner.py`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/g4f/Provider/needs_auth/MicrosoftDesigner.py) is proven at snapshot `0943d16eef757a44deb60651c07bbe5fa958cafe`, whose [`LICENSE`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/LICENSE) is GPL-3.0; expressive adaptation is probable, which records provenance/licensing risk without asserting a legal conclusion ([#11720](https://github.com/diegosouzapw/OmniRoute/pull/11720)). +- **docs(legal):** centralize pinned MIT notices for the vendored GCF codec and local flag assets ([#11726](https://github.com/diegosouzapw/OmniRoute/pull/11726)) +- Split the 2,952-line `adobeFireflyClient.ts` into nine focused modules (catalog, credentials, payload, ARP session, upload, responses, IMS, account, poll) behind an unchanged barrel — no consumer edits and no public API change (#11728). — thanks @MumuTW +- **fix(dashboard):** replaced the Model Lockout card's unprovenanced notification MP3 with a short, locally synthesized Web Audio chime ([#11731](https://github.com/diegosouzapw/OmniRoute/pull/11731)) — both toggles retain optional audible feedback without shipping replacement media or third-party code, and unsupported, suspended, or rejected audio contexts fail open without blocking the setting change. +- **chore(deepseek-web):** remove the provenance-unresolved DeepSeek PoW WASM binary and its runtime loader/tracing while retaining the existing JavaScript solver (slower at high difficulty) ([#11732](https://github.com/diegosouzapw/OmniRoute/pull/11732)). +- **chore(assets):** remove four unused provider assets whose redistribution provenance could not be verified while retaining the active Freebuff SVG ([#11735](https://github.com/diegosouzapw/OmniRoute/pull/11735)) +- **docs(assets):** retired two raster images whose redistribution provenance could not be verified ([#11736](https://github.com/diegosouzapw/OmniRoute/pull/11736)) — PicoClaw documentation now reuses the existing SVG, while Zoo Code remains a text link without an image. +- **docs(legal):** add pinned notices for six LobeHub-derived provider SVGs and 65 byte-exact theSVG assets, retaining registry claims as unverified metadata with explicit trademark and HOLD boundaries ([#11737](https://github.com/diegosouzapw/OmniRoute/pull/11737)). +- **chore(quality):** added a versioned 225/225 provider-asset provenance manifest and blocking drift gate ([#11743](https://github.com/diegosouzapw/OmniRoute/pull/11743)) — the inventory pins magic MIME and SHA-256 for every physical file, records immutable source matches for 71 assets, keeps 69 probable and 85 unresolved entries explicit but non-blocking, and treats provenance as source evidence rather than copyright or trademark clearance. +- **refactor(mitm):** replace four previously derived Traffic Inspector utilities — conversation normalization, SSE merging, secret masking, and header sanitization — with independent clean-room implementations based on public provider schemas, WHATWG SSE, and RFC 6750, while keeping the two independently classified passthrough surfaces unchanged ([#11748](https://github.com/diegosouzapw/OmniRoute/pull/11748)). +- Added a unit test that fails seven days before any dated pack under `config/` (`validUntil` and sibling keys) lapses, naming the file and key. The Alibaba free-tier pack expired on 2026-08-27 and turned every PR red the next morning with no commit involved; renewal now happens on someone's terms, not the clock's. ([#11891](https://github.com/diegosouzapw/OmniRoute/pull/11891)) +- `check:workflows` now fails (under `--strict`/`--ratchet`) when any job routed to a self-hosted runner publishes with `--provenance` — npm rejects that with `422` at the registry, which in v3.8.50 only surfaced after the tag and Docker images were public. ([#11895](https://github.com/diegosouzapw/OmniRoute/pull/11895)) +- `scripts/ops/runner-janitor.sh` now proves a path is idle with one `lsof` snapshot and removes stale leftovers itself (tmpfs after 3 h — it is RAM — disk after 24 h), kills orphan `next-build` processes, prunes checkouts of stopped runners, and alerts on memory pressure; `--dry-run` shows exactly what it would do. `docs/ops/RUNNER_BOX.md` reconciled to the measured box (31 GB, 10 listeners). ([#11893](https://github.com/diegosouzapw/OmniRoute/pull/11893)) +- The `next-build` artefact (1.3 GB) is now written and read under `$RUNNER_TEMP` (per-runner, on disk) instead of `/tmp`, which on the self-hosted pool is a 12 GB tmpfs in RAM. Landing it there took 27–32 of the publish job's 76 minutes, and the fixed `/tmp/e2e-build.tar.gz` name let E2E jobs on different runners overwrite each other's download. ([#11896](https://github.com/diegosouzapw/OmniRoute/pull/11896)) +- The CI `build` job now runs in two concurrency lanes — `main` and pull requests — so a release build is never queued behind (or OOM-killed beside) PR builds on the self-hosted pool, which holds one `next-build` comfortably and two at the edge. ([#11901](https://github.com/diegosouzapw/OmniRoute/pull/11901)) +- Re-freeze the ESLint suppressions on `release/v3.8.51` from a clean-room measurement (2 stale file entries pruned, 55 pre-existing `no-explicit-any` in six new files frozen under #11924) and drop the dead `GPT_SIZE_MAP` constant orphaned by the Adobe Firefly client split, so `No new ESLint warnings` stops failing every PR with exit 2 (Refs #11924) ([#11955](https://github.com/diegosouzapw/OmniRoute/pull/11955)) +- Type the 55 `no-explicit-any` sites that had been frozen under #11924 — four redundant casts in `socksConnectorWithFamily.ts` (undici/socks types already accept them) and the mocks/fixtures of the socks-timeout and isFree suites — and drop their suppression entries; the ESLint ratchet shrinks from 5487 to 5432 (Closes #11924) ([#11975](https://github.com/diegosouzapw/OmniRoute/pull/11975)) +- Take the two hosted-runner builds off the PR rail: `Build App` (`build.yml`) is `workflow_dispatch`-only and `DAST smoke (PR)` runs only for PRs into `main` — the 7 GB hosted VM cannot build `release/v3.8.51` in any profile (19/30 red, VM shutdown ~8 min into `next build`) and both checks had turned into permanent noise on every release PR; the bundle stays validated by `ci.yml` on `main` and by `nightly-release-green` on `release/**` (Closes #11946) ([#11962](https://github.com/diegosouzapw/OmniRoute/pull/11962)) +- Move the four nightly jobs that build the backend (`nightly-schemathesis`, `nightly-llm-security` promptfoo + garak, `nightly-resilience` axe-a11y) off the hosted 7 GB runner — where they died on `release/v3.8.51` unseen — onto the box's new `omni-light` pool (two listeners), and document the reshaped fleet (4 active OmniRoute listeners: 2 `omni-build` + 2 `omni-light`, janitor ceiling 4) (Closes #11965) ([#11967](https://github.com/diegosouzapw/OmniRoute/pull/11967)) +- Stop painting every fork PR into `release/**` red: `quality.yml` `Build (advisory)` is skipped (GitHub still reports `continue-on-error` failures as check FAILURE). Hosted `ubuntu-latest` cannot finish `npm run build` on this tree — same class as #11962 taking `build.yml` off the PR rail. `docker-publish.yml` amd64 now runs on the `.113` `omni-build` pool (31 GB / 32 cores, two listeners) with Turbopack, shares the `heavy-build-main` lane with `ci.yml` `Build` so it queues instead of becoming a third heavy, and keeps per-ref concurrency (a merge storm was starting 8 concurrent OOM builds). arm64 stays on `ubuntu-24.04-arm` with webpack — there is no ARM box. Fallback when `USE_VPS_RUNNER` is off: hosted amd64 + webpack (#11976). ([#12021](https://github.com/diegosouzapw/OmniRoute/pull/12021)) +- `docker-publish.yml` builds with webpack (`OMNIROUTE_USE_TURBOPACK=0`) on **both** arches, including amd64 on the `.113` `omni-build` pool. Turbopack had the RAM (31 GB, `omniroute-113-6`) and still panicked (`TurbopackInternalError: there must be a path to a root`, run 33253576569, 26 min in). The same tree's arm64 webpack build on hosted `ubuntu-24.04-arm` succeeded (run 33264823398). Dockerfile already documents webpack as the Docker escape hatch (#11976). ([#12050](https://github.com/diegosouzapw/OmniRoute/pull/12050)) +- **docs(free-tier):** declare the counting vs deciding regimes for "is it free?" and guard the deciding path from DB-backed catalog resolution ([#12226](https://github.com/diegosouzapw/OmniRoute/pull/12226)) — thanks @maxmad64bis +- **docs(auto-combo):** The mode pack table in `docs/routing/AUTO-COMBO.md` now lists all six shipped packs with every weight each one sets, replacing a four-pack table whose numbers had also drifted from the source. It states plainly that no pack sets `quality`, so selecting any pack silences the observed-quality signal. Six more documents that quote the scoring factor count joined the `check:docs-counts` gate, which caught five stale claims — including one naming nine factors that do not exist — and two stale mode pack counts. The dashboard routing panel, which offered four of the six packs and labelled the default strategy "6-Factor Scoring", is now covered by a test; the two packs it was missing are `reliability-first` and `chaos-mode`, the latter labelled as the fault-injection profile it is rather than as one more routing preference ([#12316](https://github.com/diegosouzapw/OmniRoute/pull/12316)) — thanks @maxmad64bis +- **chore(quality):** rebaseline `src/lib/db/apiKeys.ts` for the ACL the key-creation path now preserves ([#12352](https://github.com/diegosouzapw/OmniRoute/pull/12352)) ([#12673](https://github.com/diegosouzapw/OmniRoute/pull/12673)) — thanks @KrzysiekSko +- **chore(providers):** bump the Claude Code wire identity and the Devin bridge image pin from `2.1.220` to `2.1.258` ([#12402](https://github.com/diegosouzapw/OmniRoute/pull/12402)) — thanks @ggiak ([#12604](https://github.com/diegosouzapw/OmniRoute/pull/12604)) +- **docs(env):** align `.env.example`, the README Bun section, and the troubleshooting guide with the code: `OMNIROUTE_USE_TURBOPACK` also governs `npm run build` (not dev-only), `bun run build` follows that flag instead of auto-selecting Webpack, `OMNIROUTE_CHAT_MAX_HEAVY_IN_FLIGHT` is unset by default (no request-count cap), and the structural `503 chat_admission_busy` message matches `chatAdmissionResponses.ts` (#12404 — thanks @pacocartones) +- **chore(electron):** upgrade the desktop app to Electron 44 (Chromium 152, Node 24.18.1) ([#12217](https://github.com/diegosouzapw/OmniRoute/pull/12217)). **Requires macOS 13 (Ventura) or later** — Chromium dropped macOS 12 (Monterey), so Monterey users must stay on an earlier OmniRoute desktop build. Windows and Linux are unaffected; the app already shipped only x64/arm64, so Electron 44 dropping 32-bit builds changes nothing. Removes the `openAsHidden`/`wasOpenedAsHidden` login-item fields deleted in Electron 44 — hidden autostart continues to work through the `--hidden` argument registered with the login item ([#12554](https://github.com/diegosouzapw/OmniRoute/pull/12554)) — thanks @app/dependabot +- **chore(quality):** rebaseline `src/sse/handlers/chat.ts` for the effective-input persistence the continuation fix needs ([#12641](https://github.com/diegosouzapw/OmniRoute/pull/12641)) ([#12680](https://github.com/diegosouzapw/OmniRoute/pull/12680)) — thanks @hartmark +- `Coverage` job on `ci.yml`: the informational Codecov upload gets its own 5-minute ceiling and `continue-on-error`, and the job budget grows from 20 to 30 minutes (the 8-shard c8 merge alone takes ~10) — a stalled upload no longer ends the job `cancelled` and drags a fully green `main` run's conclusion down with it ([#11972](https://github.com/diegosouzapw/OmniRoute/pull/11972)) +- CI hardening on the PR rail: the four `Unit Tests fast-path` shards get `timeout-minutes: 30` (a hung shard held a PR for 64 min instead of GitHub's 6 h default) and both ESLint file caches lose their `restore-keys` fallback, so a cache built under another suppressions file or lint config can no longer report stale verdicts (Refs #11600, #11924) ([#11963](https://github.com/diegosouzapw/OmniRoute/pull/11963)) +- Every CI job that runs a `next build` (`build`, the npm `publish`, both release-green validations) now targets the `omni-build` runner label, which only two of the eight self-hosted runners carry. The box holds one build comfortably and two at the edge; a third now queues on GitHub instead of being OOM-killed by the kernel. ([#11932](https://github.com/diegosouzapw/OmniRoute/pull/11932)) +- **refactor(combo):** move `handleRoundRobinCombo` (and `resolveTargetTokenLimit`) into `open-sse/services/combo/roundRobinCombo.ts`. `combo.ts` drops from 2164 to 1014 lines (`split("\n").length`); the leaf is 1199 (under the 1200 new-file cap). The round-robin call site uses a dynamic `import()` so `releaseStickyPinOnFailure` / `clearStaleLKGP` can stay exported from `combo.ts` without a static cycle. Skip / sticky / semaphore / safety-timer behavior is unchanged. ([#12811](https://github.com/diegosouzapw/OmniRoute/pull/12811)) — thanks @HouMinXi +- **chore(quality):** rebaseline the file-size caps the error-boundary campaign grew past (`open-sse/executors/codex.ts`, `open-sse/vendor/codex-chatgpt-web/bridge.ts`, both via [#12444](https://github.com/diegosouzapw/OmniRoute/pull/12444)) ([#12654](https://github.com/diegosouzapw/OmniRoute/pull/12654)) +- **chore(quality):** rebaseline the file-size caps the hartmark batch grew past (`combos/page.tsx` via [#12355](https://github.com/diegosouzapw/OmniRoute/pull/12355), `open-sse/services/combo.ts` via [#12338](https://github.com/diegosouzapw/OmniRoute/pull/12338)) ([#12623](https://github.com/diegosouzapw/OmniRoute/pull/12623)) — thanks @hartmark +- **chore(quality):** rebaseline the file-size caps the HouMinXi batch grew past when its PRs stacked (`providers/page.tsx`, `chatCore.ts`, `accountFallback.ts`) — each PR measured correctly in isolation, none saw the stacking ([#12619](https://github.com/diegosouzapw/OmniRoute/pull/12619)) +- **chore(quality):** rebaseline `open-sse/services/combo.ts` for the reset-aware scoring the HouMinXi batch stacked ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) ([#12678](https://github.com/diegosouzapw/OmniRoute/pull/12678)) — thanks @HouMinXi +- **docs:** sync provider catalog count (direct commit `4f11b2ae3d`) +- **docs(skills):** sync omni-inference search fallback (direct commit `b4e76a7ed9`) +- **chore(lint):** prune two stale base suppressions (direct commit `1325047d56`) +- **test(auto-combo):** tolerate full-suite rotation load (direct commit `ad3e293f9f`) +- **test:** drain inherited quality snapshot reds (direct commit `21ed68d8ac`) +- **chore(quality):** rebaseline measured CLI bundle growth (direct commit `d35b3f9779`) +- **test(ui):** reproduce invalid AnySearch icon alias (direct commit `12cc6ca834`) +- **test(translator):** fix the relative imports of the relocated deferred-finish test (direct commit `777d9d1629`) +- **docs(counts):** bring the documented provider and migration counts to the measured values (direct commit `e70bea30e9`) +- **test(security):** drop the guard-target set left unused by the sync (direct commit `a32ba0e362`) +- **chore(db):** renumber model_capabilities migration 163 → 169 (cross-branch collision with 163_radar_feed_cache_generated_at) (direct commit `70f5d4cbf9`) +- **docs(cli):** document CLI_PRIME_AGENT_BIN and correct the CLI Agents count (direct commit `9058e39b61`) +- **chore(deps):** refresh runtimes and adopt ESLint 10 ([#11259](https://github.com/diegosouzapw/OmniRoute/pull/11259)) — thanks @backryun +- **refactor(local-corpus):** implement dynamic root resolution and LRU cache ([#11491](https://github.com/diegosouzapw/OmniRoute/pull/11491)) — thanks @raheemuddin786 +- **chore(quality):** prune stale eslint-suppressions entries ([#11596](https://github.com/diegosouzapw/OmniRoute/pull/11596)) +- **chore(ci):** unblock quality gates - env-doc sync + stryker tap.testFiles registration (#9282) ([#11609](https://github.com/diegosouzapw/OmniRoute/pull/11609)) — thanks @Meet6338-X +- **chore(ci):** point the circular-deps gate at dpdm's real JS entrypoint ([#11615](https://github.com/diegosouzapw/OmniRoute/pull/11615)) — thanks @benzntech +- **chore(quality):** rebaseline file-size for #11448/#11495/#11561 growth ([#11630](https://github.com/diegosouzapw/OmniRoute/pull/11630)) +- **deps:** realign onnxruntime-node pin to @huggingface/transformers' 1.24.3 lockstep ([#11633](https://github.com/diegosouzapw/OmniRoute/pull/11633)) — thanks @jonlwheat2-gif +- **test(autoCombo):** port models_dev_tier #11508 guard from node:test to vitest ([#11635](https://github.com/diegosouzapw/OmniRoute/pull/11635)) — thanks @jonlwheat2-gif +- **chore(quality):** rebaseline file-size for #11083/#11631/#11628 growth ([#11636](https://github.com/diegosouzapw/OmniRoute/pull/11636)) +- **chore(lint):** prune stale ESLint suppressions (base-red #11449) ([#11644](https://github.com/diegosouzapw/OmniRoute/pull/11644)) — thanks @jonlwheat2-gif +- **docs(changelog):** aggregate the ten v3.8.50 fragments into CHANGELOG.md ([#11683](https://github.com/diegosouzapw/OmniRoute/pull/11683)) +- **chore(search):** drop unreachable credential sweep after #11642 ([#11687](https://github.com/diegosouzapw/OmniRoute/pull/11687)) — thanks @HouMinXi +- **ci:** add API route TypeScript regression gate ([#11705](https://github.com/diegosouzapw/OmniRoute/pull/11705)) — thanks @Sabeekhann +- **docs(dedup):** correct the CodeQL claim on the tenant-namespace comment ([#11712](https://github.com/diegosouzapw/OmniRoute/pull/11712)) +- **docs(changelog):** add consolidated v3.8.50 stats, top-25 ranking and a .mailmap ([#11715](https://github.com/diegosouzapw/OmniRoute/pull/11715)) +- **docs:** recommend gstack for AI-assisted workflows ([#11770](https://github.com/diegosouzapw/OmniRoute/pull/11770)) — thanks @santosraju99-hub +- **chore(quality):** tighten complexity/cognitive-complexity ratchets to the current tip; land the missed gateways.ts rebaseline ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **docs(i18n):** finish Freepik → Magnific rebrand in locale strings and README ([#11772](https://github.com/diegosouzapw/OmniRoute/pull/11772)) — thanks @turbolego +- **refactor(auto-combo):** fix divergent scoring in combo health reporting ([#11854](https://github.com/diegosouzapw/OmniRoute/pull/11854)) — thanks @jacobsparts +- **chore(changelog):** add missing fragments for #11919/#11918/#11916 ([#11938](https://github.com/diegosouzapw/OmniRoute/pull/11938)) +- **test(check):** escape the runs-on fixture with JSON.stringify, not a quote-only replace ([#11942](https://github.com/diegosouzapw/OmniRoute/pull/11942)) (direct commit `fb7445eaa3`) +- **test(release):** align five suites with the contracts #11933, #11919 and #11876 shipped on release/v3.8.51 ([#11944](https://github.com/diegosouzapw/OmniRoute/pull/11944)) +- **chore(quality):** remove unreachable code and restore test discovery ([#11950](https://github.com/diegosouzapw/OmniRoute/pull/11950)) — thanks @backryun +- **docs(agents):** sync-back landings are fast-forward, never squash ([#11964](https://github.com/diegosouzapw/OmniRoute/pull/11964)) +- **test(infra):** retry recursive temp-dir removal instead of failing a shard on ENOTEMPTY (#11966) ([#11968](https://github.com/diegosouzapw/OmniRoute/pull/11968)) +- **docs(ops):** the .113 heavy-build ceiling is one runner, not two ([#12048](https://github.com/diegosouzapw/OmniRoute/pull/12048)) +- **test(ui):** correct inactive auto-fetch expectation ([#12098](https://github.com/diegosouzapw/OmniRoute/pull/12098)) — thanks @RaviTharuma +- **docs:** document native dependency check escape hatch ([#12101](https://github.com/diegosouzapw/OmniRoute/pull/12101)) — thanks @RaviTharuma +- **test(providers):** regenerate the translate-path golden for OrcaRouter (#11923) ([#12118](https://github.com/diegosouzapw/OmniRoute/pull/12118)) +- **chore(quality):** velocity phase — loosen every numeric baseline by 20% until v4.0, monitor headroom nightly ([#12125](https://github.com/diegosouzapw/OmniRoute/pull/12125)) +- **chore(lint):** batch 0 of #12146 — type the call-log-cap sqlite rows instead of 45 as-any casts ([#12157](https://github.com/diegosouzapw/OmniRoute/pull/12157)) +- **chore(lint):** batch 4 of #12146 — shared/components react-hooks violations resolved ([#12159](https://github.com/diegosouzapw/OmniRoute/pull/12159)) +- **chore(lint):** batch 1 of #12146 — dashboard/cli-code react-hooks violations resolved ([#12160](https://github.com/diegosouzapw/OmniRoute/pull/12160)) +- **chore(lint):** batch 3 of #12146 — dashboard/settings react-hooks violations resolved ([#12162](https://github.com/diegosouzapw/OmniRoute/pull/12162)) +- **chore(lint):** batch 2 of #12146 — resolve the react-hooks compiler violations in dashboard/providers ([#12163](https://github.com/diegosouzapw/OmniRoute/pull/12163)) +- **chore(quality):** register search-432 cooldown test in stryker tap.testFiles ([#12170](https://github.com/diegosouzapw/OmniRoute/pull/12170)) +- **chore(lint):** batch 5 of #12146 — combos, endpoint, provider-stats, api-manager and costs react-hooks violations resolved ([#12174](https://github.com/diegosouzapw/OmniRoute/pull/12174)) +- **build:** add contributor fast profile ([#12192](https://github.com/diegosouzapw/OmniRoute/pull/12192)) — thanks @rafacpti23 +- **docs(readme):** README touch-ups ([#12193](https://github.com/diegosouzapw/OmniRoute/pull/12193)) ([#12194](https://github.com/diegosouzapw/OmniRoute/pull/12194)) ([#12202](https://github.com/diegosouzapw/OmniRoute/pull/12202)) — thanks @rafacpti23 +- **build:** force the webpack bundler in the contributor build profile ([#12197](https://github.com/diegosouzapw/OmniRoute/pull/12197)) — thanks @rafacpti23 +- **build:** skip standalone packaging in the contributor profile ([#12198](https://github.com/diegosouzapw/OmniRoute/pull/12198)) — thanks @rafacpti23 +- **docs(audit):** align every published number with the code and harden check:docs-counts ([#12200](https://github.com/diegosouzapw/OmniRoute/pull/12200)) +- **build:** stub the instrumentation entrypoints in the contributor profile ([#12203](https://github.com/diegosouzapw/OmniRoute/pull/12203)) — thanks @rafacpti23 +- **build:** omit the standalone output target for contributor builds ([#12204](https://github.com/diegosouzapw/OmniRoute/pull/12204)) — thanks @rafacpti23 +- **chore(lint):** batch 6 of #12146 — memory, radar, audit, analytics, cache, usage, activity, home and RequestLoggerV2 react-hooks violations resolved ([#12208](https://github.com/diegosouzapw/OmniRoute/pull/12208)) +- **docs(diagrams):** rename number-carrying diagram files to stable names ([#12210](https://github.com/diegosouzapw/OmniRoute/pull/12210)) +- **docs(dashboard):** redraw onboarding tier-flow SVGs for the real 4-tier model ([#12211](https://github.com/diegosouzapw/OmniRoute/pull/12211)) +- **docs(api):** document every implemented route in openapi.yaml (276 → 692 paths) ([#12212](https://github.com/diegosouzapw/OmniRoute/pull/12212)) +- **chore(lint):** batch 7 of #12146 — final src tail: 37 react-hooks violations across 33 files resolved ([#12244](https://github.com/diegosouzapw/OmniRoute/pull/12244)) +- Revert "docs: recommend gstack for AI-assisted workflows (#11770)" ([#12248](https://github.com/diegosouzapw/OmniRoute/pull/12248)) ([#12249](https://github.com/diegosouzapw/OmniRoute/pull/12249)) +- **docs:** Chaos Mode setup guide + weighted strategy semantics ([#12250](https://github.com/diegosouzapw/OmniRoute/pull/12250)) +- **docs(agents):** protected-surface merge rule — operator approval for agent-instruction files ([#12253](https://github.com/diegosouzapw/OmniRoute/pull/12253)) +- **chore(quality):** register native-codex turn-pin tests in stryker tap.testFiles ([#12263](https://github.com/diegosouzapw/OmniRoute/pull/12263)) +- **chore(quality):** dedupe tap.testFiles entries added by racing base-red fixes ([#12265](https://github.com/diegosouzapw/OmniRoute/pull/12265)) +- **docs(readme):** introduce OmniRouteTray — the macOS menu-bar companion ([#12276](https://github.com/diegosouzapw/OmniRoute/pull/12276)) — thanks @ggiak +- **chore(proxy):** purge the legacy 1proxy residue — sync/rotator modules, dead DB exports, dead settings tab and flag, docs (#12091) ([#12290](https://github.com/diegosouzapw/OmniRoute/pull/12290)) +- **chore(quality):** baseline-headroom skips generated and vendored files in the fileSize worst-file signal ([#12291](https://github.com/diegosouzapw/OmniRoute/pull/12291)) +- **refactor(video):** unify the JPEG frame data-URI contract ([#12322](https://github.com/diegosouzapw/OmniRoute/pull/12322)) +- **chore(deps):** freeze onnxruntime-node and eslint-plugin-react-hooks out of dependabot groups ([#12329](https://github.com/diegosouzapw/OmniRoute/pull/12329)) +- **test(executors):** restore the #10986 reasoning-only fallback guards ([#12364](https://github.com/diegosouzapw/OmniRoute/pull/12364)) — thanks @pacocartones +- **chore(quality):** re-tighten the file-size ratchet to the real LOC (plan 3.8.52 task 0) ([#12411](https://github.com/diegosouzapw/OmniRoute/pull/12411)) +- **test(grok):** format web executor suite ([#12412](https://github.com/diegosouzapw/OmniRoute/pull/12412)) +- **chore(quality):** ratchet open-sse typecheck baseline to zero ([#12418](https://github.com/diegosouzapw/OmniRoute/pull/12418)) +- **test(build):** guard the artifact path policy arrays against duplicates ([#12422](https://github.com/diegosouzapw/OmniRoute/pull/12422)) +- **chore(lint):** adopt eslint-plugin-react-hooks 7.1.1 ([#12428](https://github.com/diegosouzapw/OmniRoute/pull/12428)) +- **chore(providers):** remove a keyless provider integration at its operator's request ([#12440](https://github.com/diegosouzapw/OmniRoute/pull/12440)) +- **docs(providers):** register providers removed at their operator's request and guard against reintroduction ([#12478](https://github.com/diegosouzapw/OmniRoute/pull/12478)) +- **chore(quality):** base fixes — stryker tap.testFiles + node_modules cache key ([#12482](https://github.com/diegosouzapw/OmniRoute/pull/12482)) +- **docs(arch):** one-process recipe for tens of long /v1/responses ([#12493](https://github.com/diegosouzapw/OmniRoute/pull/12493)) — thanks @RaviTharuma +- **chore(quality):** tighten the CodeQL ratchet baseline from 11 to 6 ([#12530](https://github.com/diegosouzapw/OmniRoute/pull/12530)) +- **chore(deps):** pin browserslist override to ^4.28.8 ([#12592](https://github.com/diegosouzapw/OmniRoute/pull/12592)) — thanks @KooshaPari +- **chore(deps):** bump fast-uri to 3.1.7 in the electron lockfile ([#12601](https://github.com/diegosouzapw/OmniRoute/pull/12601)) +- **docs(quality):** document how the CodeQL ratchet refreshes and how to tighten it ([#12611](https://github.com/diegosouzapw/OmniRoute/pull/12611)) +- **test(ci):** pin the openapi-security-tiers two-arm contract with an executing gate test (#12581) ([#12652](https://github.com/diegosouzapw/OmniRoute/pull/12652)) +- **docs(gamification):** align XP Rewards table with code (#12501) ([#12667](https://github.com/diegosouzapw/OmniRoute/pull/12667)) — thanks @KooshaPari +- **refactor(combo):** split executeTarget into gates, attempt, and loop ([#12746](https://github.com/diegosouzapw/OmniRoute/pull/12746)) — thanks @HouMinXi +- **docs(security):** document socket.yml scanner config + CI workflow link (#12575) ([#12764](https://github.com/diegosouzapw/OmniRoute/pull/12764)) — thanks @KooshaPari +- **chore(ci):** guard commit identity in pre-commit to stop author misattribution ([#12772](https://github.com/diegosouzapw/OmniRoute/pull/12772)) +- **deps:** 17 Dependabot bumps — bump electron from 43.4.0 to 43.4.1 in /electron ([#11426](https://github.com/diegosouzapw/OmniRoute/pull/11426)); bump the development group across 1 directory with 8 updates ([#11428](https://github.com/diegosouzapw/OmniRoute/pull/11428)); bump the production group across 1 directory with 11 updates ([#11440](https://github.com/diegosouzapw/OmniRoute/pull/11440)); chore(deps): bump github/codeql-action/init from 4.37.7 to 4.37.8 ([#11925](https://github.com/diegosouzapw/OmniRoute/pull/11925)); chore(deps): bump github/codeql-action from 4.37.7 to 4.37.8 ([#11926](https://github.com/diegosouzapw/OmniRoute/pull/11926)); chore(deps): bump github/codeql-action/analyze from 4.37.7 to 4.37.8 ([#11927](https://github.com/diegosouzapw/OmniRoute/pull/11927)); build(deps): bump github/codeql-action/init from 4.37.8 to 4.37.9 ([#12345](https://github.com/diegosouzapw/OmniRoute/pull/12345)); build(deps): bump github/codeql-action/analyze from 4.37.8 to 4.37.9 ([#12346](https://github.com/diegosouzapw/OmniRoute/pull/12346)); bump the development group across 1 directory with 2 updates ([#12347](https://github.com/diegosouzapw/OmniRoute/pull/12347)); build(deps): bump github/codeql-action from 4.37.8 to 4.37.9 ([#12349](https://github.com/diegosouzapw/OmniRoute/pull/12349)); bump browserslist from 4.28.2 to 4.28.8 ([#12396](https://github.com/diegosouzapw/OmniRoute/pull/12396)); bump the production group across 1 directory with 4 updates ([#12399](https://github.com/diegosouzapw/OmniRoute/pull/12399)); bump @xmldom/xmldom ([#12500](https://github.com/diegosouzapw/OmniRoute/pull/12500)); bump qs from 6.15.2 to 6.16.0 ([#12512](https://github.com/diegosouzapw/OmniRoute/pull/12512)); bump @xmldom/xmldom from 0.9.10 to 0.9.12 ([#12513](https://github.com/diegosouzapw/OmniRoute/pull/12513)); bump fast-uri from 3.1.5 to 3.1.7 ([#12514](https://github.com/diegosouzapw/OmniRoute/pull/12514)); bump @humanfs/node from 0.16.7 to 0.16.8 ([#12515](https://github.com/diegosouzapw/OmniRoute/pull/12515)) +- Clear the `release/v3.8.51` `check:agent-skills-sync` base-red: regenerate `skills/cli-tunnel/SKILL.md` so the `tunnel create [type]` positional that #13009 taught the generator to read is reflected in the committed skill. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` typecheck base-red from #12731: the new `Mode pack ranking gates` candidates in `open-sse/services/autoCombo/__tests__/autoCombo.test.ts` omitted the required `provider`, `model` and `errorRate` fields of `ProviderCandidate`, failing `check:open-sse-typecheck` (and with it `Fast Quality Gates`) on every open PR. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` docs-sync base-red: EURouter (#13025) and GreenPT (#13024) took the live provider count to 358, leaving 7 STRICT drifts (`PROVIDER_REFERENCE.md`, 4 diagrams, 2 tier-flow images) plus the `AGENTS.md` / `llm.txt` / `package.json` count claims stale. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- **test(models):** Pin `CATALOG_BUILD_TIMEOUT_MS` in the #12058 canonical-catalog regression so a cold catalog build racing the 8s default bound no longer flakes the suite with a 500 ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Allow `@omniroute/opencode-plugin-v2/` in the npm pack policy — [#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870) shipped the v2 plugin beside its v1 sibling without widening the allowlist, so every packed file under it read as an unexpected artifact and failed `check:pack-policy` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Point the release-green validator's pack gate at the tree it is validating — the build-provenance guard defaulted to `origin/main`, which a release branch tip can never be an ancestor of mid-cycle, so `Package artifact` reported a false red on every run and the tarball boot-smoke cascaded off it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **docs(checks):** keep doc counts honest — headings, rankings, catalog, weights, quality gate and scoring diagram now covered ([#12507](https://github.com/diegosouzapw/OmniRoute/pull/12507)) — thanks @maxmad64bis +- **chore(lifecycle):** `check:model-lifecycle` now also diffs `DEFAULT_DEGRADATION_MAP` (the background-task redirect table) against the vendor lifecycle snapshot, refusing a retired id as source or target, with a table-driven unit test beside it. Three rows whose source the vendor had retired — `claude-sonnet-4-20250514`, `gemini-3-pro-preview` and `gpt-5.1-codex` (whose target `gpt-5.1-codex-mini` is retired too) — were dead code, since `checkLifecycle` answers 410 before the redirect runs; they are dropped (#12535 — thanks @pacocartones) +- **test(compression):** cover idle worker eviction at the resource level — the pool must call `terminate()` and must not retain the worker's `MessagePort`, complementing the `exit`-event assertion added with the fix ([#13371](https://github.com/diegosouzapw/OmniRoute/pull/13371)) +- **chore(quality):** raise the `imageGeneration.ts` file-size ceiling for the image-only-model guard that clears the #12945 base-red ([#13392](https://github.com/diegosouzapw/OmniRoute/pull/13392)) +- **chore(quality):** type the combo-test route's JSON response bodies instead of casting them to `any`, drop the now-empty suppression entry, and remove the `hasPerModelQuota` import `chatCore.ts` stopped using when the failure-classification helper was extracted ([#13379](https://github.com/diegosouzapw/OmniRoute/pull/13379)) +- **chore(electron):** the desktop bundle no longer ships the root-level files of every translated docs mirror (`README.md`, `llm.txt`, `CLAUDE.md`, `GEMINI.md`, `CONTRIBUTING.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`) — the packaged app only reads `docs/i18n//docs/**`, which stays. Saves ~11 MB on top of the translated CHANGELOGs already pruned. (#0000) ([#13235](https://github.com/diegosouzapw/OmniRoute/pull/13235)) +- **chore(quality):** retighten the `src/sse/handlers/chat.ts` unused-vars suppression count to the 8 violations that actually remain ([#13385](https://github.com/diegosouzapw/OmniRoute/pull/13385)) +- Bring the documented counts back in line with the code: 171 migrations (was 169) and 20 routing strategies (was 19), plus the regenerated `cli-tunnel` skill reference. ([#12970](https://github.com/diegosouzapw/OmniRoute/pull/12970)) +- Update the documented migration count to 172 after the call-logs provider-stats indexes landed. ([#13209](https://github.com/diegosouzapw/OmniRoute/pull/13209)) +- **chore(ci):** two gates that close the blind spots behind the exclusions above. `check:vitest-exclusions` requires every Vitest exclusion to name a tracking issue and to appear in `config/quality/vitest-exclusions.json` — the previous list grew to 62 files behind a comment pointing at an issue that had been closed for a month. `check-new-key-coverage` requires a key newly added to `en.json` to reach every locale; the existing coverage gate is a percentage floor per locale, so eleven absent keys out of ~13,000 left it at 99.9% while a whole feature shipped untranslated in nine languages. Both are diff-aware, so pre-existing debt stays frozen and neither needed a migration to turn on. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **build:** root layout no longer loads Inter from `next/font/google`, so a production image build does not need fonts.googleapis.com ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **docs(i18n):** bring the eight new `llm.txt` mirrors (km, kn, ml, my, ne, or, pa, si) up to the current provider and migration counts, so `check-docs-sync` stops rejecting every commit ([#13674](https://github.com/diegosouzapw/OmniRoute/pull/13674)) +- **refactor(ui):** flow surfaces (home topology, combo live studio, compression cockpit/waterfall/nodes, token-health badge) express state through the theme-aware `--orch-status-*` tokens instead of fixed dark-mode hex, so green/red/amber/grey stay legible in the light theme; dark mode is byte-identical. Categorical palettes (routing-strategy hues, compression-layer pills, provider brand colors) deliberately stay hex ([#12378](https://github.com/diegosouzapw/OmniRoute/issues/12378)) ([#13203](https://github.com/diegosouzapw/OmniRoute/pull/13203)) +- **chore(tests):** 51 test files that had been excluded from Vitest are running again, restoring roughly 350 assertions to the blocking suite. Each was measured individually first: of the 62 files parked behind the `// #8618 — pre-existing failure` comment, 51 pass against the current tree with no source change, so the exclusions had outlived the failures they were added for. The 11 that genuinely still fail stay excluded, but now point at a live tracker (#13204) rather than at #8618, which was closed in August while the list it tracked kept growing. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **chore(quality):** drop two ESLint suppression entries whose violations no longer exist, so `eslint --suppressions-location` stops rejecting every commit that touches the surrounding files ([#13384](https://github.com/diegosouzapw/OmniRoute/pull/13384)) +- **chore(release):** bring main's post-tag fixes into release/v3.8.51 — sync-back step 2/2 (direct commit `21c488f210`) +- **chore(deps):** patch toml and esbuild in the opencode plugin lockfiles ([#12965](https://github.com/diegosouzapw/OmniRoute/pull/12965)) +- **docs(changelog):** reconcile the v3.8.51 living section — fold 366 fragments, cover every cycle commit, credit every contributor ([#12971](https://github.com/diegosouzapw/OmniRoute/pull/12971)) +- **test(plugin):** wait for the v2 lazy-refresh tier instead of sleeping past it ([#12984](https://github.com/diegosouzapw/OmniRoute/pull/12984)) +- **chore(quality):** rebaseline chatCore.ts after the non-streaming regression fixes ([#13045](https://github.com/diegosouzapw/OmniRoute/pull/13045)) +- **docs(changelog):** add the missing bullet marker to the DeepSeek PoW fragment ([#13200](https://github.com/diegosouzapw/OmniRoute/pull/13200)) +- **chore(deps):** drain the Dependabot queue — 10 of 13 alerts ([#13213](https://github.com/diegosouzapw/OmniRoute/pull/13213)) +- **chore(ci):** make worktrees carry the hooks that enforce the identity gate ([#13236](https://github.com/diegosouzapw/OmniRoute/pull/13236)) +- **chore(deps):** pin csv-parse to ^7.0.2 via overrides (#13117) ([#13300](https://github.com/diegosouzapw/OmniRoute/pull/13300)) — thanks @KooshaPari +- **chore(deps):** bump hono from ^4.12.34 to ^4.13.7 (#13148) ([#13301](https://github.com/diegosouzapw/OmniRoute/pull/13301)) — thanks @KooshaPari +- **chore(deps):** pin joi to ^18.2.8 via overrides (#13085) ([#13302](https://github.com/diegosouzapw/OmniRoute/pull/13302)) — thanks @KooshaPari +- **docs(providers):** add Microsoft 365 Copilot (BizChat) provider guide (#12779) ([#13340](https://github.com/diegosouzapw/OmniRoute/pull/13340)) — thanks @KooshaPari +### 🙌 Contributors + +Thanks to everyone whose work landed in v3.8.51: + +| Contributor | PRs / Issues | +| --- | --- | +| [@5dive-bot](https://github.com/5dive-bot) | #11852 | +| [@abhisheksharma2411](https://github.com/abhisheksharma2411) | #11547, #11684, #11959, #12209 | +| [@adevwithpurpose](https://github.com/adevwithpurpose) | #11464 | +| [@adivekar-utexas](https://github.com/adivekar-utexas) | #12015, #12043, #12090, #12138 | +| [@AIB1TAL0S](https://github.com/AIB1TAL0S) | #12103 | +| [@alltomatos](https://github.com/alltomatos) | #11448 | +| [@alvinveroy](https://github.com/alvinveroy) | #11857, #12027, #12165 | +| [@amaleta](https://github.com/amaleta) | #11906, #11934 | +| [@amartinawi](https://github.com/amartinawi) | #11575 | +| [@AndrianBalanescu](https://github.com/AndrianBalanescu) | #11083, #11830, #11834, #11844 | +| [@anhtahaylove](https://github.com/anhtahaylove) | #13091, #13092, #13093, #13096, #13097, #13100, #13106, #13109, #13114, #13155, #13166, #13171, #13175, #13187, #13196 | +| [@aniruddhaadak80](https://github.com/aniruddhaadak80) | #11760 | +| [@app](https://github.com/app) | #12554 | +| [@arminanton](https://github.com/arminanton) | #11461, #11513 | +| [@AStupidBear](https://github.com/AStupidBear) | #11584 | +| [@atescivitci-cmd](https://github.com/atescivitci-cmd) | #12105 | +| [@b3nw](https://github.com/b3nw) | #11970, #11971, #12147 | +| [@backryun](https://github.com/backryun) | #11259, #11950, #12075, #12076, #12078, #12079, #12081, #12082, #12181, #12228, #12239, #12255, #12258, #12277, #12367, #12423, #12524, #12538 | +| [@benjaminkitt](https://github.com/benjaminkitt) | #11747 | +| [@benzntech](https://github.com/benzntech) | #11614, #11615 | +| [@Bl0ck154](https://github.com/Bl0ck154) | #11948, #11951, #11952, #11953, #11954 | +| [@brick30llc-ctrl](https://github.com/brick30llc-ctrl) | #11752, #12242, #12262 | +| [@Chewji9875](https://github.com/Chewji9875) | #11455, #11875, #12028 | +| [@cheynetom](https://github.com/cheynetom) | #12058 | +| [@davidebaraldo](https://github.com/davidebaraldo) | #12222, #12304 | +| [@davidlinfr](https://github.com/davidlinfr) | #6390 | +| [@ddarkr](https://github.com/ddarkr) | #12124 | +| [@Deftera186](https://github.com/Deftera186) | #11809 | +| [@dpozimski](https://github.com/dpozimski) | #11945 | +| [@DW-MediaLab](https://github.com/DW-MediaLab) | #12229 | +| [@echel0nn](https://github.com/echel0nn) | #11923 | +| [@f9td56dbgh-hub](https://github.com/f9td56dbgh-hub) | #11660 | +| [@fabioluissilva](https://github.com/fabioluissilva) | #11991 | +| [@foreveryh](https://github.com/foreveryh) | #12177 | +| [@geek007git](https://github.com/geek007git) | #12115, #12116, #12117, #12120, #12122 | +| [@geekyNads](https://github.com/geekyNads) | #11784, #12266 | +| [@ggiak](https://github.com/ggiak) | #11561, #12276, #12350, #12402 | +| [@gonisulaimann](https://github.com/gonisulaimann) | #12368, #12369, #12371 | +| [@Gorillaz322](https://github.com/Gorillaz322) | #12207 | +| [@groovecityJO](https://github.com/groovecityJO) | #12682 | +| [@hartmark](https://github.com/hartmark) | #11434, #11452, #11473, #11499, #11703, #11983, #11984, #11985, #11986, #11988, #11989, #11990, #11994, #12221, #12293, #12445, #12446, #12447, #12448, #12460, #12461, #12623, #12646, #12650, #12680, #12717, #12718, #12727, #12741, #12854 | +| [@hizzt](https://github.com/hizzt) | #11894 | +| [@hongnoul](https://github.com/hongnoul) | #11484 | +| [@HouMinXi](https://github.com/HouMinXi) | #11411, #11414, #11512, #11518, #11520, #11641, #11642, #11643, #11687, #11779, #11849, #11850, #11851, #11915, #11916, #11918, #11919, #11920, #12013, #12017, #12033, #12042, #12106, #12139, #12166, #12169, #12171, #12205, #12213, #12312, #12325, #12487, #12488, #12495, #12504, #12557, #12566, #12590, #12591, #12624, #12626, #12632, #12637, #12678, #12696, #12697, #12711, #12733, #12746, #12767, #12770, #12789, #12803, #12805, #12811, #12866, #12868, #12899, #12926, #12934, #12950, #12951, #12974, #13001, #13006, #13011, #13017, #13026, #13027, #13034, #13035, #13038, #13042, #13050, #13060, #13061, #13069, #13107, #13120, #13136, #13195, #13197 | +| [@Hsia97](https://github.com/Hsia97) | #11624 | +| [@jacobsparts](https://github.com/jacobsparts) | #11854, #12155, #12167 | +| [@joglomedia](https://github.com/joglomedia) | #11980 | +| [@jonlwheat2-gif](https://github.com/jonlwheat2-gif) | #11443, #11450, #11549, #11567, #11608, #11633, #11635, #11640, #11644, #11671, #11672, #11673, #11674, #11675, #11676, #11677, #11706, #11783, #12051, #12052, #12053, #12055 | +| [@kanade-hoshino](https://github.com/kanade-hoshino) | #12180 | +| [@Karan825](https://github.com/Karan825) | #11861 | +| [@KaspaPulse](https://github.com/KaspaPulse) | #11389, #11469, #11628, #11666, #11910 | +| [@keeltrace](https://github.com/keeltrace) | #12080, #12223 | +| [@killer30001000](https://github.com/killer30001000) | #12178 | +| [@KooshaPari](https://github.com/KooshaPari) | #12470, #12592, #12667, #12699, #12703, #12706, #12764, #12769, #12771, #13300, #13301, #13302, #13340, #13401, #13403, #13404, #13405, #13406, #13407, #13409, #13410, #13411, #13412, #13413, #13414, #13418, #13424, #13427, #13433, #13523, #13524, #13525, #13528, #13531, #13532, #13534, #13539, #13543, #13545, #13546, #13547, #13550, #13553 | +| [@kriptoburak](https://github.com/kriptoburak) | #11370 | +| [@KrzysiekSko](https://github.com/KrzysiekSko) | #12425, #12432, #12673 | +| [@marcelokarval](https://github.com/marcelokarval) | direct commit / report | +| [@maxmad64bis](https://github.com/maxmad64bis) | #1622, #11435, #11437, #11441, #11537, #11550, #11553, #11555, #11557, #11812, #11842, #11843, #11903, #12151, #12214, #12215, #12218, #12226, #12314, #12316, #12317, #12318, #12319, #12320, #12321, #12507, #12715, #12731, #12744, #12786, #12787, #12788, #12790, #12792, #12794, #12795, #12828, #12832, #12853, #12857, #12870, #12937, #12941, #12975, #13141, #13142, #13146, #13147 | +| [@mdigitalbh81](https://github.com/mdigitalbh81) | #12240 | +| [@Meet6338-X](https://github.com/Meet6338-X) | #11598, #11609, #12110 | +| [@morpheus9393](https://github.com/morpheus9393) | #11943 | +| [@MumuTW](https://github.com/MumuTW) | #11492, #11502, #11506, #11507, #11626, #11675, #11685, #11728, #11746, #11888, #11889, #11890, #11892 | +| [@Neuron-Mr-White](https://github.com/Neuron-Mr-White) | #11622, #11801, #12256 | +| [@NightStalker-87](https://github.com/NightStalker-87) | #12183 | +| [@NoxzRCW](https://github.com/NoxzRCW) | #11879, #11880, #11881, #11882, #11883, #11935 | +| [@ntdat812](https://github.com/ntdat812) | #11585, #12095 | +| [@ntdatt812](https://github.com/ntdatt812) | #11368, #11573, #11574, #11576, #11577, #11580, #11582, #11583, #11588, #11589, #11590, #11591, #11592, #11593, #11672, #12177, #12180, #12873, #12918, #12920, #12921, #12925, #12930, #13007, #13009, #13024, #13025, #13083, #13087, #13101, #13104, #13110 | +| [@official-burak](https://github.com/official-burak) | #11542 | +| [@opensource-elearning](https://github.com/opensource-elearning) | #12179, #12189, #12278, #12286 | +| [@oyi77](https://github.com/oyi77) | #11408, #11409, #11421, #11505, #11677, #12036, #12110 | +| [@pacocartones](https://github.com/pacocartones) | #11521, #11522, #11527, #11528, #11529, #11530, #11531, #11532, #11533, #11534, #11595, #11599, #11603, #11604, #11605, #11607, #11610, #11676, #11714, #11716, #11718, #11767, #11838, #11860, #11862, #11869, #11871, #11872, #11873, #11903, #11906, #11921, #11934, #12356, #12358, #12359, #12360, #12361, #12362, #12364, #12365, #12373, #12374, #12375, #12376, #12377, #12379, #12380, #12381, #12386, #12387, #12389, #12390, #12394, #12395, #12397, #12401, #12403, #12404, #12522, #12523, #12535, #12536, #12540, #12541, #12543, #12545, #12548, #12549, #12550, #12551, #12552, #12647, #12651, #12653, #13055 | +| [@patrykkopycinski](https://github.com/patrykkopycinski) | #11936, #11937, #12224 | +| [@PauloFH](https://github.com/PauloFH) | #11509 | +| [@PauloHSOliveira](https://github.com/PauloHSOliveira) | #12241 | +| [@ponkcore](https://github.com/ponkcore) | #12054 | +| [@Prajeeth-12](https://github.com/Prajeeth-12) | #11634, #12046 | +| [@quiterunner-commits](https://github.com/quiterunner-commits) | #12131, #12143 | +| [@rafacpti23](https://github.com/rafacpti23) | #11554, #11558, #12192, #12197, #12198, #12202, #12203, #12204 | +| [@ragnar-claude](https://github.com/ragnar-claude) | #11564 | +| [@raheemuddin786](https://github.com/raheemuddin786) | #11491, #11839, #11840, #11841, #12003, #12230, #12231, #12232, #12233, #12234 | +| [@rahilmavani](https://github.com/rahilmavani) | #11761 | +| [@Rahulsharma0810](https://github.com/Rahulsharma0810) | #11771 | +| [@RaviTharuma](https://github.com/RaviTharuma) | #11710, #11727, #11797, #11798, #11802, #11805, #11806, #11811, #12098, #12099, #12101, #12449, #12452, #12472, #12473, #12493, #12533, #12607, #12628, #12631, #12636, #12875, #12876, #12880, #12882, #12884 | +| [@rezjalibd](https://github.com/rezjalibd) | #12186 | +| [@RhianB14](https://github.com/RhianB14) | #11692 | +| [@rifqiawl](https://github.com/rifqiawl) | #11517, #11519, #11969 | +| [@rqzbeh](https://github.com/rqzbeh) | #11390 | +| [@Sabeekhann](https://github.com/Sabeekhann) | #11699, #11705 | +| [@santosraju99-hub](https://github.com/santosraju99-hub) | #11755, #11770 | +| [@Seramicx](https://github.com/Seramicx) | #11568 | +| [@Siva010](https://github.com/Siva010) | #12191 | +| [@solstxce](https://github.com/solstxce) | #11597 | +| [@soroush5](https://github.com/soroush5) | #12691 | +| [@tenshiak](https://github.com/tenshiak) | #12279 | +| [@TheDemonTuan](https://github.com/TheDemonTuan) | #11468, #11470, #11471, #11482, #11548, #11758, #11775, #11814 | +| [@thomasmaerz](https://github.com/thomasmaerz) | #12834 | +| [@tuandinh0801](https://github.com/tuandinh0801) | #11454 | +| [@turbolego](https://github.com/turbolego) | #11621, #11762, #11772, #11774, #11781, #12216 | +| [@Tushar49](https://github.com/Tushar49) | #11392 | +| [@ujjawalkaushik1110](https://github.com/ujjawalkaushik1110) | #11863 | +| [@vermasomesh835](https://github.com/vermasomesh835) | #11794 | +| [@vsd2807](https://github.com/vsd2807) | #11565, #11619 | +| [@wahidsadik371-coder](https://github.com/wahidsadik371-coder) | #12126 | +| [@watchingdogs](https://github.com/watchingdogs) | #12031 | +| [@wildcard](https://github.com/wildcard) | #11620 | +| [@xiaoyaner0201](https://github.com/xiaoyaner0201) | #11460, #11662, #12083 | +| [@Xxx91n](https://github.com/Xxx91n) | #11690 | +| [@yourspraveen](https://github.com/yourspraveen) | #11146 | +| [@yxyxy](https://github.com/yxyxy) | #11671 | +| [@ZaimMarzuki](https://github.com/ZaimMarzuki) | #11960 | +| [@Zartharas](https://github.com/Zartharas) | #11340 | +| [@zero-executioner](https://github.com/zero-executioner) | #11631 | +| [@diegosouzapw](https://github.com/diegosouzapw) | maintainer | + +--- ## [3.8.50] — 2026-08-25 _Living section — regenerated 2026-08-12 from all cycle commits (cycle open `ed2db6cb19` → tip). Bullets carry the merged PR and its author; direct pushes listed separately._ diff --git a/docs/i18n/ja/CHANGELOG.md b/docs/i18n/ja/CHANGELOG.md index de6262db2e..abfb242e4d 100644 --- a/docs/i18n/ja/CHANGELOG.md +++ b/docs/i18n/ja/CHANGELOG.md @@ -8,16 +8,1119 @@ ## [3.8.51] — TBD -_Living section — cycle opened at the v3.8.50 freeze (parallel-cycle model). Bullets are aggregated from `changelog.d/` fragments at each `/generate-release` phase._ +_Living section — reconciled 2026-09-15 from all cycle commits (`091589089c` → `c0f92ec98a`, 916 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 | **123** | +| 📝 Commits in the cycle | **916** | +| 🔀 Pull requests referenced | **892** | +| 📋 Changelog entries | **919** | +| 🙌 Contributors credited in entries | **121** | +| 🤖 Automated dependency commits | 18 | + +**Entries by type** + +| Type | Count | +| --- | ---: | +| 🐛 Fixes | 592 | +| ✨ Features | 131 | +| 🧹 Chore | 93 | +| 📚 Docs | 42 | +| 🧪 Tests | 32 | +| ♻️ Refactor | 8 | +| 🏗️ Build | 7 | +| ⚡ Performance | 7 | +| ⚙️ CI | 2 | +| ⏪ Reverts | 2 | +| 📦 Dependencies | 2 | +| 🔒 Security | 1 | + +### 🏆 Top 25 contributors this cycle + +_By commits in `091589089c..c0f92ec98a`, author identities consolidated via `.mailmap` and the merged PR's GitHub login. Bots excluded._ + +| # | Contributor | Commits | +| ---: | --- | ---: | +| 🥇 | diegosouzapw | 323 | +| 🥈 | Bob.Hou (@HouMinXi) | 79 | +| 🥉 | Paco Cartones (@pacocartones) | 77 | +| 4 | Dizzle (@maxmad64bis) | 49 | +| 5 | Koosha Paridehpour (@KooshaPari) | 43 | +| 6 | Nguyen Thanh Dat (@ntdatt812) | 31 | +| 7 | Ravi Tharuma (@RaviTharuma) | 26 | +| 8 | Markus Hartung (@hartmark) | 22 | +| 9 | Webman (@jonlwheat2-gif) | 22 | +| 10 | backryun | 17 | +| 11 | anhtahaylove | 15 | +| 12 | Rafa Martins (@rafacpti23) | 10 | +| 13 | Syed Raheemuddin (@raheemuddin786) | 10 | +| 14 | Nguyễn Viết Tuấn (@TheDemonTuan) | 8 | +| 15 | MumuTW | 7 | +| 16 | Paijo (@oyi77) | 7 | +| 17 | Tobias Andersen (@turbolego) | 6 | +| 18 | Bl0ck (@Bl0ck154) | 5 | +| 19 | SHANMUGAPRIYAN (@geek007git) | 5 | +| 20 | KaspaPulse | 5 | +| 21 | Abhishek Sharma (@abhisheksharma2411) | 4 | +| 22 | Abhishek Divekar (@adivekar-utexas) | 4 | +| 23 | Andrew B. (@AndrianBalanescu) | 4 | +| 24 | NoxzRCW | 4 | +| 25 | opensource-elearning | 4 | ### ✨ New Features +- **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). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- Added Google AI Studio Gemini batch text-to-speech support through `POST /v1/audio/speech`. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- 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. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(routing):** subscription-first auto groupings — `auto/subscription` routes only through plan-included connections with a documented hard-stop overage and fails closed on exhaustion, while `auto/thrifty` orders the pool `subscription → keyless → free → cheap → premium` and steps up one rung at a time as each is exhausted. Billing class comes from a curated per-connection catalog (uncurated is treated as metered, never plan-included), both reuse STRICT_ZERO_COST's per-connection verification, and a quota reading whose `resetAt` has passed is now refreshed regardless of TTL so routing returns to plan capacity as soon as it resets ([#11146](https://github.com/diegosouzapw/OmniRoute/pull/11146)) — thanks @yourspraveen +- **feat(providers):** publish a management-authenticated versioned web-session credential contract from OmniRoute's canonical browser credential metadata ([#11340](https://github.com/diegosouzapw/OmniRoute/pull/11340)) — thanks @Zartharas +- **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)) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add Xquik X search with typed results, credential validation, REST routing, and MCP selection ([#11370](https://github.com/diegosouzapw/OmniRoute/pull/11370)) — thanks @kriptoburak +- **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)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(dashboard):** surface durable exclusive managed leases in the existing Sessions view, keeping leased clients visible across idle gaps while marking connections with in-flight work as active ([#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389)) — thanks @KaspaPulse +- **build(bun):** allow Turbopack bundler flag on Bun 1.4+ with configurable Webpack fallback ([#11471](https://github.com/diegosouzapw/OmniRoute/pull/11471)) — thanks @TheDemonTuan +- feat(api): add an opt-in `modelVisibilityAllowlist`/`modelVisibilityDenylist` settings pair to curate exactly which models `/v1/models` advertises, mirrored into every `auto/*` combo candidate pool so a denied model cannot be routed to via combo selection either (#11481) ([#11997](https://github.com/diegosouzapw/OmniRoute/pull/11997)) +- **feat(rankings):** the Free Provider Rankings page shows what each provider actually served over the last 24 h. It ranked by ELO alone, which left a provider that answers every call with an error in first place; the usage data was already served by the API but never requested. A provider with too small a sample shows a dash, not a number ([#11546](https://github.com/diegosouzapw/OmniRoute/pull/11546)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **feat(guardrails):** enforce a bounded, deterministic contract for Video Bridge transcripts — 256 cues, 4096 input code units and 4 KiB UTF-8 per cue, 64 KiB total text, malformed-Unicode rejection, focus-window scoping, cross-source reconciliation with contributing-source metadata, and a structural provenance trust boundary so caller JSON can never self-assert `embedded`/`audio-bridge` provenance ([#11652](https://github.com/diegosouzapw/OmniRoute/issues/11652)) ([#12009](https://github.com/diegosouzapw/OmniRoute/pull/12009)) +- **feat(video):** orchestrate optional Video Bridge audio extraction and Audio Bridge STT behind a dual opt-in (operator setting AND per-request signal) — a new loopback-only broker `mode=audio` operation shares the frame path's exact process queue, deadline, AbortSignal, and byte budgets to extract a bounded mono 16 kHz PCM WAV from the same already-downloaded video, then reuses the existing Audio Bridge transcription boundary; provider segment timing is preserved when available and marked coarse otherwise, and every failure degrades to a visual-only-safe partial instead of throwing (#11654). ([#12012](https://github.com/diegosouzapw/OmniRoute/pull/12012)) +- Add a tenant-bound Video Bridge drill-down lifecycle on top of the existing secure cache substrate: opaque hashed handles (never raw session/video identifiers), preview/standard/detail multiresolution variants resampled on read, response pagination capped at 8 frames and 32 MiB, and a new authenticated `/api/v1/video-bridge/drilldown` consumer route that stays disabled for remote access by default and denies cross-key access with the same response as a nonexistent handle (no existence oracle). ([#12006](https://github.com/diegosouzapw/OmniRoute/pull/12006)) +- **test(video):** Add the Video Bridge FU-07/FU-09 promotion-evidence harness (#11656) — a frozen Zod manifest schema covering the 8 required scenario kinds (static scenes, rapid cuts, late facts, fades, blur, small text, close events, visual prompt injection) with a minimum of 3 repetitions per case, deterministic declarative fixture recipes (`videoBridgePromotionFixtures.ts`), a pure medians/p95 metrics aggregator, a pure FU-07/FU-09 promotion-verdict evaluator applying the ticket's exact thresholds (missing token usage always holds), a digest-only persistence layer that never retains raw media or raw model responses, and a versioned per-model promotion allowlist shipped empty with every model defaulting to `hold`. The FU-07/FU-09 promotion verdicts themselves remain HOLD — they require a real evidence run against real models on VPS 192.168.0.15. ([#12008](https://github.com/diegosouzapw/OmniRoute/pull/12008)) +- **feat(video bridge):** "embedded" transcript provenance can now be legitimately earned instead of merely asserted — a bounded, allowlisted (`mov_text`/`subrip`/`webvtt`) subtitle probe runs through the loopback-only Video Bridge broker (at most 2 streams, 10s subdeadline bounded by the request deadline, 256 KiB output, 4096-code-unit lines), normalized through a bounded, ReDoS-safe WebVTT parser and Zod-validated end to end. The adapter always resolves to an explicit `success`/`absent`/`transient_failure` outcome — a subtitle failure never breaks the visual description path, and only a fingerprint-verified broker response (never a caller-declared label) can produce embedded cues (#11659). ([#12011](https://github.com/diegosouzapw/OmniRoute/pull/12011)) +- Default new Antigravity-family connections (agy CLI imports and Antigravity OAuth connects) to model auto-sync, so live model discovery lands in the synced catalog and `/v1/models` picks up freshly released upstream models (e.g. Gemini 3.7 Flash tiers) without code changes. Existing connections keep their current setting; the per-connection dashboard toggle remains the opt-out. (#11685 — thanks @MumuTW) ([#11887](https://github.com/diegosouzapw/OmniRoute/pull/11887)) +- **feat(ui):** enable React Compiler (`reactCompiler: true` + `babel-plugin-react-compiler`) for automatic memoization at build time ([#11783](https://github.com/diegosouzapw/OmniRoute/pull/11783)) — thanks @jonlwheat2-gif +- **feat(providers):** add SeekAi (`seekai.cc`) as an OpenAI-compatible New-API gateway — catalog id `seekai` (alias `ska`), `https://seekai.cc/v1`, live `/v1/models` via `passthroughModels`, aggregator-list membership so New-API balance detection can opt in. No referral/aff codes. ([#11786](https://github.com/diegosouzapw/OmniRoute/issues/11786)) ([#12557](https://github.com/diegosouzapw/OmniRoute/pull/12557)) — thanks @HouMinXi +- **feat(zai):** add GLM-5.3-Flash Coding Plan support (1M context, 128K output, vision, `low|high|max` reasoning) and route `zai` GLM-5.3-family API-key traffic through the OpenAI-compatible Coding Plan endpoint with native thinking defaults ([#11801](https://github.com/diegosouzapw/OmniRoute/pull/11801)) — thanks @Neuron-Mr-White +- **feat(combo):** choose how combo models are ordered — manual, provider, score, or name — via a sort control in the dashboard builder, persisted in `config.modelSort` and re-applied on load and after add ([#11812](https://github.com/diegosouzapw/OmniRoute/pull/11812)) — thanks @maxmad64bis +- **feat(free):** custom models can be marked free-tier via `customModels[].isFree`; `isFreeModel()` is the first door and `hidePaidModels` respects it even for providers outside the free budget ([#11843](https://github.com/diegosouzapw/OmniRoute/pull/11843)) — thanks @maxmad64bis +- **feat(nodejs):** add `5dive` as a `configure` target — `omniroute configure 5dive` / `omniroute setup-5dive` write a 5dive auth profile that points an agent fleet's `claude` seats at OmniRoute, with the root-only write, the loopback-vs-`https` endpoint rule and the per-seat model pin handled explicitly ([#11852](https://github.com/diegosouzapw/OmniRoute/pull/11852)) — thanks @5dive-bot +- **feat(sse):** treat `max` as a first-class reasoning-effort tier and clamp per model family (GLM 5.1+/DeepSeek V4+/Kimi K3+ keep native `max`; o1/MiniMax/Grok/Muse Spark clamp to their upstream ceiling) ([#11875](https://github.com/diegosouzapw/OmniRoute/pull/11875)) — thanks @Chewji9875 +- **feat(providers):** the provider plugin manifest now advertises a `usage-fetch` capability for the 40 providers that have a wired usage/quota fetcher, so external dashboards can read it from `GET /api/v1/provider-plugin-manifest` instead of parsing `open-sse/services/usage.ts` after every release. Discovery only — no new fetcher, no quota change, and the Dashboard quota widget stays gated by `USAGE_SUPPORTED_PROVIDERS`. `USAGE_FETCHER_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/fetcherProviders.ts`) and is re-exported from `services/usage.ts`, keeping the manifest module a light leaf instead of pulling the ~490-module usage dispatcher into the manifest route. ([#11903](https://github.com/diegosouzapw/OmniRoute/pull/11903)) — thanks @maxmad64bis / @pacocartones +- **feat(plugins):** `OMNIROUTE_PLUGINS_DIR` sets the directory the runtime plugin scanner reads — and the root the plugin manager installs into — overriding the `HOME`-derived default, so a Docker/K8s deployment can point straight at its bind-mounted plugin tree instead of moving `HOME` just to relocate the scan path. An image that exports no home no longer scans `/tmp/.omniroute/plugins` in silence: the resolved directory is logged once at startup as `scanner.dir_resolved`, naming the input that won. Unset, behaviour is unchanged. Distinct from the CLI-only `OMNIROUTE_PLUGIN_PATH`, which finds `omniroute-cmd-*` command packages and never reached this scanner ([#11906](https://github.com/diegosouzapw/OmniRoute/pull/11906)) — thanks @amaleta / @pacocartones +- **feat(leases):** add an explicit owner-authenticated status action that returns only the active lease's privacy-safe configured connection and provider labels, with generation fencing and no credential or internal-id disclosure ([#11910](https://github.com/diegosouzapw/OmniRoute/pull/11910)) — thanks @KaspaPulse +- **feat(providers):** import-from-file modal shows per-row API errors and ships a downloadable CSV template ([#12071](https://github.com/diegosouzapw/OmniRoute/issues/12071)) ([#12504](https://github.com/diegosouzapw/OmniRoute/pull/12504)) — thanks @HouMinXi +- **feat(providers):** dashboard search matches connection name and `baseUrl` so imported OpenAI-compat nodes surface on the provider card ([#12108](https://github.com/diegosouzapw/OmniRoute/issues/12108)) ([#12495](https://github.com/diegosouzapw/OmniRoute/pull/12495)) — thanks @HouMinXi +- **feat(routing):** add a `score` Auto router strategy that selects the highest configured weighted score and reuses `explorationRate`. ([#12155](https://github.com/diegosouzapw/OmniRoute/pull/12155)) — thanks @jacobsparts +- Add a runtime feature flag to disable universal context handoffs globally without changing the default behavior. ([#12167](https://github.com/diegosouzapw/OmniRoute/pull/12167)) — thanks @jacobsparts +- **feat(providers):** the provider plugin manifest now also advertises a `usage-supported` capability for the 46 providers whose usage API is accepted by the server and Dashboard routes, so integrators can distinguish "the server will serve quota for this provider" from "a fetcher is wired" without reading TypeScript. Discovery only — no fetcher or quota change. `usage-fetch` resolves on id or alias (the usage dispatcher accepts both); `usage-supported` resolves on id alone, matching the runtime guard `USAGE_SUPPORTED_PROVIDERS.includes(providerId)`. `USAGE_SUPPORTED_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/supportedProviders.ts`) and is re-exported from `providers.ts`, mirroring the `fetcherProviders` leaf from #11903 and keeping the manifest a light module. ([#12214](https://github.com/diegosouzapw/OmniRoute/pull/12214)) — thanks @maxmad64bis +- **feat(rankings):** order Free Provider Rankings by what each provider actually served — `GET /api/free-provider-rankings?sortBy=reliability` and a "Most reliable first" toggle on the page. Providers with too few calls to state a success rate keep their score order below the measured ones; the default order is unchanged ([#12218](https://github.com/diegosouzapw/OmniRoute/pull/12218)). — thanks @maxmad64bis +- **perf(sse):** defer `cloneLogPayload()` in the structured SSE collector until after the `maxEvents`/`maxBytes` cap check, eliminating ~9,800 wasted `structuredClone` calls per streaming response (65–71% faster `push()`). Reducer snapshot isolation restored for OpenAI and Responses summaries ([#12241](https://github.com/diegosouzapw/OmniRoute/pull/12241)) — thanks @PauloHSOliveira ([#12243](https://github.com/diegosouzapw/OmniRoute/pull/12243)) +- **feat(usage):** Devin CLI agentic quota (Codeium seat-management GetUserStatus) and OpenRouter key limits plus account credits now surface in Provider Limits ([#12256](https://github.com/diegosouzapw/OmniRoute/pull/12256) — thanks @Neuron-Mr-White) +- **feat(settings):** persist `headroomUrl` through Settings so status/start use the operator URL instead of only `HEADROOM_URL` ([#12306](https://github.com/diegosouzapw/OmniRoute/issues/12306)) ([#12487](https://github.com/diegosouzapw/OmniRoute/pull/12487)) — thanks @HouMinXi +- **feat(auto-combo):** Auto-Combo scoring can now weigh how often a provider/model has actually succeeded. The engine already carried that number on every candidate — 24 hours of usage history behind a ten-sample floor, real-time metrics otherwise — and the scoring function never read it, while the weight table described `stability` as if it did. `reliability` (`1 - failureRate`, with the same field precedence and the same rate-bounding the speed ranking already uses, so a corrupt reading means "nothing observed" rather than "fails every call") is now a declared factor shipping at weight `0`, so routing is unchanged until an operator gives it one, and the `stability` description now matches what that factor computes ([#12317](https://github.com/diegosouzapw/OmniRoute/pull/12317)) — thanks @maxmad64bis +- **feat(routing):** With `freeAccessPolicy: "strict"`, the read-only candidate listing (`GET /v1/auto-combo/{channel}/candidates`) no longer hides the candidates the zero-cost guard excludes — the same read-only transparency the resilience filter already honours (#9133). Each candidate now carries `freeAccessExclusion` saying why it would be kept out, and it tells an exhausted allowance apart from a quota reading that never arrived or went stale, which used to look identical from the outside. Routing is unchanged: the listing reports, it never enforces. The separate `excludeTosAvoid` guard still drops its candidates without a reason; that gap is now documented rather than closed ([#12319](https://github.com/diegosouzapw/OmniRoute/pull/12319)) — thanks @maxmad64bis +- **feat(radar):** The Radar catalog table now shows two facts it was already receiving from the feed and dropping on the floor: the per-model rate limits (requests and tokens, per minute and per day) in a new column, and a badge when a provider's terms state it may train on the prompts you send. A limit of zero renders as zero rather than "rate-only" — for a ceiling those are opposite facts — and a model with no training statement gets no badge, because an absent statement is not a guarantee ([#12320](https://github.com/diegosouzapw/OmniRoute/pull/12320)) — thanks @maxmad64bis +- **feat(radar):** explain Community, single-use, contributor, supporter, recovery, abuse, offers, and privacy rules before either Radar activation action, and remove the superseded fixed-PR grant promise from every UI locale ([#12342](https://github.com/diegosouzapw/OmniRoute/pull/12342)) +- **feat(gamification):** the dashboard Profile page now shows the real daily streak — `/api/gamification/level` returns `streak: { current, longest }` (per key with `apiKeyId`, operator-wide maximum otherwise) and the streak card reads it instead of a hard-coded 0 (#2403) ([#12377](https://github.com/diegosouzapw/OmniRoute/pull/12377)) — thanks @pacocartones +- **feat(gamification):** the dashboard leaderboard now shows each API key's display name under the Name column instead of a truncated key id; `GET /api/gamification/leaderboard` attaches `name` per entry (name only — no key material), while the shared ranking helper and the federation leaderboard stay id-only — thanks @pacocartones ([#12385](https://github.com/diegosouzapw/OmniRoute/pull/12385)) +- **feat(gamification):** enforce the documented 1000 XP/min per-API-key anti-cheat rate limit on the XP award path; over-limit awards are logged and skipped instead of persisted, and the sliding window now matches the timestamp format stored in `xp_audit_log` ([#2403](https://github.com/diegosouzapw/OmniRoute/issues/2403)) ([#12390](https://github.com/diegosouzapw/OmniRoute/pull/12390)) — thanks @pacocartones +- **feat(admin):** localize the gamification anomalies page — the loading state, the Status column and the Suspicious badge now come from the `common` catalog (new `common.suspicious` key propagated to every locale) — add it to the Gamification sidebar group as `gamification-admin` (`/dashboard/gamification/admin`), and expose the loading and empty states as polite `role="status"` live regions (#12401 — thanks @pacocartones) +- **feat(providers):** skip GitHub combo members missing from the live synced catalog, and drop Copilot models that are policy-disabled or hidden from the model picker ([#12473](https://github.com/diegosouzapw/OmniRoute/pull/12473)) — thanks @RaviTharuma +- **feat(i18n):** locale-expansion tooling — `npm run i18n:add-locale` adds a language to config, dashboard, docs mirrors, CLI, README/indexes and site in one command; browser, CLI and cookie detection resolve `uk`, `fil`/`tl`, `zh-Hant` (and the retired legacy `in` → `id`) via config aliases; `config/i18n.json` now ships in the npm package so the published CLI can read it; new real-translation ratio gate (`npm run i18n:check-ratio`, advisory) with a per-locale ratchet baseline; `run-translation --adopt` restores `.i18n-state.json`; `sync-language-bars` generates 🌐 bars from config; `validate_translation.py` loads its allowlist again. Retires the duplicate `in` locale (Indonesian mislabelled as Hindi) — 42 honest locales; saved `NEXT_LOCALE=in` / `OMNIROUTE_LANG=in` keep working. (#12496) +- **feat(api):** Emit gateway-measured `tokens_per_second` (TTFT excluded) on streaming usage and `X-OmniRoute-Tokens-Per-Second` when first-token latency is known ([#12616](https://github.com/diegosouzapw/OmniRoute/issues/12616)) ([#12631](https://github.com/diegosouzapw/OmniRoute/pull/12631)) — thanks @RaviTharuma +- **feat(opencode-plugin): map gateway cost/usage/tok/s onto OpenCode inference payloads** — the official plugin copies `X-OmniRoute-Response-Cost`, token counts, `X-OmniRoute-Tokens-Per-Second` / `usage.tokens_per_second`, TTFT, and the winning `X-OmniRoute-Model` onto the JSON/SSE body OpenCode already consumes. Missing tok/s is left unset (never `tokens / latency`). (#12636) — thanks @RaviTharuma +- **feat(combo):** add combo strategy `quota-weighted`: skip exhausted accounts, then weighted-draw by leftover / in-flight load; existing conversations stay pinned until the account is empty ([#12789](https://github.com/diegosouzapw/OmniRoute/pull/12789)) — thanks @HouMinXi +- **feat(models):** Account live listings become the chat catalog source for Claude, Codex, Copilot, and AGY; public metadata only fills prices on IDs those accounts already list. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **fix(models):** Union `agy` and `antigravity` live catalogs so `agy/gemini-3.8-flash-high` is not rejected after the prefix folds to `antigravity`. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **feat(opencode):** Add `@omniroute/opencode-plugin-v2`, an opencode v2 plugin that gives a v2 install a route to an OmniRoute gateway: it publishes models, combos and auto-combos into the host catalog, refreshes them lazily behind a 300s TTL, and keeps serving the last known catalog from an on-disk snapshot when the gateway is unreachable. The v1 plugin (`@omniroute/opencode-plugin`) is untouched and keeps working as before. The plugin adapts to the host instead of assuming one shape: it reads the shape the host seeds into the catalog draft and publishes the `api` block, the pre-`api` top-level fields, or both. The gateway key comes from the host credential store when one is connected, so no secret needs to sit in a config file; a source the gateway refuses is named in a warning instead of silently degrading the catalog to raw model ids; and tool schemas bound for a Gemini model are stripped of the JSON-Schema keywords Gemini rejects, so tool calling survives that route ([#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870)) — thanks @maxmad64bis +- feat(services): show sanitized CLIProxyAPI account health from its authenticated management API without exposing credentials, file paths, or raw account metadata (#6342) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add AnySearch free web search + URL extract (webFetch) with typed results, credential validation, REST routing, and MCP selection - fallback-only ([#11690](https://github.com/diegosouzapw/OmniRoute/pull/11690)) — thanks @Xxx91n +- **feat(dashboard):** display clamped `[0, 100]%` cached input token ratio in request logs table ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) — thanks @b3nw +- **feat(catalog):** add `OMNIROUTE_DISABLE_THINKING_LEVEL_VARIANTS` feature flag to optionally filter out thinking level variants from model catalog ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) — thanks @b3nw +- **feat(dashboard):** continuously export call logs to external analytics stores. A pluggable destination registry ships the full Logs-tab record set on an hourly `JobRegistry` cron, with a persisted per-destination cursor, batched inserts, a config UI rendered from each destination's own field descriptors, and a REST layer (`/api/log-export/*`) for CRUD, a connection test, and an on-demand run. A destination can opt into `includeBodies` to also ship the request and response payloads shown in the Logs detail pane, including the client and provider views of each call; this is off by default, and payloads inherit the dashboard's PII sanitisation, secret redaction and `noLog` handling. Google BigQuery is the first destination, using a service-account key stored encrypted at rest and streaming inserts keyed by call-log id, into a table that is day-partitioned on `timestamp` and clustered on `api_key_name`, `provider`, `model` and `status`. ([#11945](https://github.com/diegosouzapw/OmniRoute/pull/11945)) — thanks @dpozimski +- **feat(providers):** add MaxAI as a signed, OpenAI-compatible provider serving its 13 paid chat models (GPT-5.6 / Luna / Thinking, Claude 5 Sonnet, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite, Grok 4.1-fast / 4.5, DeepSeek V3.2 / R1, Llama 3.3 70B) through OmniRoute's `/v1` endpoint, with per-request HMAC-SHA1→SM3→AES request signing, live model + context-window discovery from `/models/get_config`, and prompted tool-calling translated to OpenAI `tool_calls` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI vision input — image_url content parts are forwarded inline in `message_content` to the 6 vision-capable models (GPT-5.6 / Luna / Thinking, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite) ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI image generation — 6 image models (gpt-image-1, dall-e-3, flux-1-schnell/dev/pro, sd3-medium) exposed through `POST /v1/images/generations` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI document RAG — inline base64 file/document attachments are uploaded to MaxAI (content-addressed `doc_id`) and attached to the chat via `doc_list` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** browserless MaxAI onboarding — email device-pair login (`/api/providers/[id]/login`) and signed access-token refresh, so a connection can be created and kept fresh without a real browser or Google OAuth ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** per-provider TLS impersonation profile (MaxAI presents a Windows Firefox-150 client fingerprint) so its bot-sensitive endpoints accept OmniRoute traffic ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** add **Nimble** as a web-search and web-fetch provider (`nimble-search`) — `/v1/search` routes to Nimble's search API at `lite` depth (locale, freshness and include/exclude domain filters mapped onto the shared request contract), and `/v1/web/fetch` routes to Nimble Extract, which covers all four fetch formats (`markdown`, `html`, `links`, `screenshot`) from a single call. One API key serves both surfaces. ([#11620](https://github.com/diegosouzapw/OmniRoute/pull/11620)) — thanks @wildcard +- The npm publish is automatic again, through npm Trusted Publishing (OIDC): the hosted `stage-npm` job publishes with a short-lived credential minted from GitHub's id-token — no `NPM_TOKEN`, no 2FA prompt, provenance attached. `publish_mode=staged` (owner approves with 2FA) and `direct` (token) remain available on `workflow_dispatch`. ([#11931](https://github.com/diegosouzapw/OmniRoute/pull/11931)) +- **feat(providers):** Add **Opper** as an API-key gateway provider — EU-hosted AI gateway with 700+ models from 30+ providers behind one OpenAI-compatible API and one key (`OPPER_API_KEY`); model ids use `provider/model` format (e.g. `anthropic/claude-sonnet-4-6`, `openai/gpt-5`); live model catalog at `https://api.opper.ai/v3/compat/models`; entry mirrors `requesty` (same shape, `passthroughModels: true`, no static seed) ([#11629](https://github.com/diegosouzapw/OmniRoute/pull/11629)) +- **feat(dashboard):** the `/dashboard/orchestration` snapshot hook now subscribes to the `agents` WebSocket channel (`agent.task.updated`) instead of `requests` as its refetch trigger, and relaxes its background poll from 5s to 30s while that WS connection is up — falling back to the tighter 5s cadence, reprogrammed live on any connect/disconnect transition, whenever the socket is down. ([#12409](https://github.com/diegosouzapw/OmniRoute/pull/12409)) +- **feat(dashboard):** Orchestration canvas quick wins — search box plus state/source/provider filter chips with a one-click clear, and per-source collapse/expand, all reflected in the URL so a filtered/collapsed view is shareable and survives a refresh; the detail drawer gained a "copy trace JSON" action and hardened error/empty-state and accessibility handling; the Agents-tab edges now animate traveling particles along active (running) connections; and the canvas node/edge status colors moved off fixed hex values onto theme-aware `--orch-status-*` CSS custom properties, so they adapt correctly to light/dark mode. ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- **chore(dashboard):** Orchestration UI hardening pass and the missing component/model test coverage it called for — `OrchestratorNode`/`ActivityNode`/`OverflowNode` rendering, the `?node=`/overflow-click page routing, the Agents-tab orchestrator-click no-op and `showCompleted` toggle, and the overview kanban's done-column sort order (#12270, #12271). ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- New `/dashboard/orchestration` page: live unified view of everything running — Cloud Agent, A2A and Conductor as a real-time graph (Agents tab), the combo cascade (Routing tab, reusing the Combo Live Studio) and a state kanban (Overview tab), with a detail drawer (trace, cost, approve/cancel). Read-only over existing APIs — no new backend. Canvas concept credit: PR #11815 design ([#12261](https://github.com/diegosouzapw/OmniRoute/pull/12261)) +- **feat(dashboard):** new "History" tab on `/dashboard/orchestration` — an Airflow-style grid of finished runs over a 24h/7d/30d preset window, one row per (source, identity), clicking a cell opens the existing detail drawer. It is backed by real persistence: A2A task lifecycle transitions are now written to the `a2a_tasks` table (purged after 30 days, configurable via `OMNIROUTE_A2A_HISTORY_RETENTION_DAYS`) and served by the new `GET /api/a2a/tasks/history` listing endpoint, with the task-detail route falling back to persisted history once a run leaves the in-memory snapshot. Conductor runs stay remote and are not persisted locally — the tab says so instead of silently omitting them. ([#12479](https://github.com/diegosouzapw/OmniRoute/pull/12479)) +- **feat(dashboard):** the orchestration detail drawer gained a "Repeat" action for Cloud Agent, A2A and Conductor tasks — a two-click confirm (click once to arm, click again within the confirm window to fire) re-submits the original prompt/input as a new run. The button is disabled with an explanatory tooltip whenever the original input can't be recovered from the loaded task detail (e.g. it never carried a prompt, or the detail failed to load). Two limitations of the A2A variant, by design: it targets the `/a2a` JSON-RPC endpoint, which authenticates with an API key only (`REQUIRE_API_KEY=true` or a configured `OMNIROUTE_API_KEY` makes a dashboard-session repeat answer `HTTP 400` — surfaced verbatim in the drawer's error line, never as a success), and the `message/send` call is SYNCHRONOUS: the POST blocks for the whole skill run, so the success confirmation only appears once the repeated task finishes. A dashboard-authenticated A2A creation path is deliberately left to a follow-up — widening the endpoint's auth posture is an operator decision, not a side effect of this feature. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(a2a):** A2A task execution now records which memories were consulted for the task's last user message as `metadata.memoryHits` (id/key/type/content-snippet) plus a `memory_hits` history event, purely for observability — the retrieved memory is never injected into a skill's prompt or behavior. Gated by the `OMNIROUTE_A2A_MEMORY_HITS` kill-switch (default enabled; set to `0` to skip the recall lookup entirely). The drawer's new "Memory used" section lists these hits for a2a tasks and is omitted whenever there are none. Known limitation: recall only resolves under the keyless posture — a keyed caller's task owner is a SHA-256 prefix of the API key, while memory rows are keyed by the database api-key id, and no hash→id lookup exists today, so the hit list stays empty for keyed callers. The recorded hits are also kept out of the task's own `input` (and therefore out of the persisted input and of the "Repeat" request body), so repeating a task never re-sends the previous run's memory snippets. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(providers):** add a Perplexity Agent API provider (`perplexity-agent` / `pplx-agent`) for Perplexity `/v1/responses`, including the documented Anthropic, OpenAI, Google, xAI, DeepSeek, Z.AI, Moonshot/Kimi, NVIDIA, and Perplexity model IDs plus Anthropic-model `max_output_tokens` compatibility. ([#12103](https://github.com/diegosouzapw/OmniRoute/pull/12103)) — thanks @AIB1TAL0S +- **feat(providers):** add RPD (Requests Per Day) limit to provider rate limit overrides across UI, schemas, DB, and i18n ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) — thanks @b3nw +- **feat(providers): add UC Direct (uncensored.com Developer API), the metered OpenAI-compatible surface.** A standard OpenAI-compatible passthrough (default executor) for uncensored.com's official REST API at `https://api.uncensored.com/api/v1`: `X-api-key` auth (never-expiring `uai_sk_live_` key), `POST /chat/completions` with streaming SSE and native tool-calling, and the full live metered catalog (82 models across 15 providers, discovered from the public `GET /v1/models`). Registered as provider `uc-direct` (alias `ucd`). Complements the un-metered `uc` persona provider — same models, metered credits and a plain API key instead of a subscription session. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(providers): add UC (uncensored.com), the un-metered subscription "persona" chat as an OpenAI-compatible provider.** A WebSocket web-app port: a durable Clerk credential mints a short-lived session token per connect (browserless — no API key), driving UC's persona socket. Ships the browserless email-code login (request → verify → harvest), the 19 verified persona models (Claude Opus, Gemini, Grok, GLM, Kimi, DeepSeek, MiniMax, incl. the uncensored variants), prompted `` tool-calling with a per-model code-style dialect + auto-cure retry for guardrailed models, live ``/reasoning split, streaming + non-streaming OpenAI responses, and full quota/auth error surfacing (paywall / message-limit / rate-limit → 429, invalid session → 401 re-login). Full multimodal parity via the persona blob-upload layer: **vision** (image input, 15 vision-capable models), **document RAG** (PDF/doc upload, server-side extraction), **image generation** (22 models), **video generation** (14 models, async signed-url → poll), and **TTS** (streaming MP3). Registered as provider `uc` (alias `ucn`). The metered OpenAI-compatible Developer API is a separate `uc-direct` provider. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(logs):** add copy-all button to request detail modal ([#11083](https://github.com/diegosouzapw/OmniRoute/pull/11083)) — thanks @AndrianBalanescu +- **feat(check):** make serviceKinds required and add the reverse-walk provider consistency gate ([#11392](https://github.com/diegosouzapw/OmniRoute/pull/11392)) — thanks @Tushar49 +- **feat(providers):** only advertise verified-working connections by default ([#11448](https://github.com/diegosouzapw/OmniRoute/pull/11448)) — thanks @alltomatos +- **feat(resilience):** add hierarchical concurrency admission ([#11493](https://github.com/diegosouzapw/OmniRoute/pull/11493)) +- **feat(providers):** derive + surface expiry for JWT-bearing web cookies (#11497) ([#11505](https://github.com/diegosouzapw/OmniRoute/pull/11505)) — thanks @oyi77 +- **feat(tunnels):** support named cloudflared tunnels via CLOUDFLARED_CONFIG ([#11561](https://github.com/diegosouzapw/OmniRoute/pull/11561)) — thanks @ggiak +- **feat(compression):** make proactive context-compression threshold a live setting ([#11564](https://github.com/diegosouzapw/OmniRoute/pull/11564)) — thanks @ragnar-claude +- **feat(resilience):** add provider peak-hour protection ([#11622](https://github.com/diegosouzapw/OmniRoute/pull/11622)) — thanks @Neuron-Mr-White +- **feat(providers):** add 1min.ai provider ([#11631](https://github.com/diegosouzapw/OmniRoute/pull/11631)) — thanks @zero-executioner +- **feat(dashboard):** collapsible JSON tree viewer for request/response payloads ([#11703](https://github.com/diegosouzapw/OmniRoute/pull/11703)) — thanks @hartmark +- **feat(routing):** add LiquidAI LFM2.5-2.6B free tier via OpenRouter ([#11752](https://github.com/diegosouzapw/OmniRoute/pull/11752)) — thanks @brick30llc-ctrl +- **feat(pricing):** add GLM-5.3-Flash pricing, model specs, and catalog registration ([#11830](https://github.com/diegosouzapw/OmniRoute/pull/11830)) — thanks @AndrianBalanescu +- **feat(routing):** add relayMode for schema-locked context handoffs ([#11839](https://github.com/diegosouzapw/OmniRoute/pull/11839)) — thanks @raheemuddin786 +- **feat(config):** add support for runtime system prompt configuration and hot-reloading ([#11841](https://github.com/diegosouzapw/OmniRoute/pull/11841)) — thanks @raheemuddin786 +- **feat(routing):** expand connection-aware quota prefilter across combo strategies (#11682) ([#11850](https://github.com/diegosouzapw/OmniRoute/pull/11850)) — thanks @HouMinXi +- **feat(models):** reactive discovery sync after Antigravity model-not-found 404 ([#11888](https://github.com/diegosouzapw/OmniRoute/pull/11888)) — thanks @MumuTW +- **feat(models):** auto-aliases and token limits from synced Antigravity discovery ([#11889](https://github.com/diegosouzapw/OmniRoute/pull/11889)) — thanks @MumuTW +- **feat(settings):** raise sticky round-robin limit caps to 1000 ([#12015](https://github.com/diegosouzapw/OmniRoute/pull/12015)) — thanks @adivekar-utexas +- **feat(resilience):** operator-configurable global credential health check interval ([#12043](https://github.com/diegosouzapw/OmniRoute/pull/12043)) — thanks @adivekar-utexas +- **feat(quota):** use official OpenCode Go usage API ([#12124](https://github.com/diegosouzapw/OmniRoute/pull/12124)) — thanks @ddarkr +- **feat(resilience):** default the credential health check sweep to 60 minutes ([#12138](https://github.com/diegosouzapw/OmniRoute/pull/12138)) — thanks @adivekar-utexas +- **feat(quality):** new-code mode for the complexity and dead-code ratchets (Clean as You Code) ([#12142](https://github.com/diegosouzapw/OmniRoute/pull/12142)) +- **feat(dashboard):** orchestration canvas — unified model + snapshot hook (part 1/2) ([#12156](https://github.com/diegosouzapw/OmniRoute/pull/12156)) +- **feat(usage):** add Kilo Code balance and Kilo Pass quotas ([#12178](https://github.com/diegosouzapw/OmniRoute/pull/12178)) — thanks @killer30001000 +- **feat(providers):** refresh vendored ChatGPT Web connector to v4.0.7 ([#12181](https://github.com/diegosouzapw/OmniRoute/pull/12181)) — thanks @backryun +- **feat(providers):** manual "Clear cooldown" action in the cooling panel ([#12224](https://github.com/diegosouzapw/OmniRoute/pull/12224)) — thanks @patrykkopycinski +- **feat(providers):** restore ChatGPT Web via clean-room browser transport ([#12239](https://github.com/diegosouzapw/OmniRoute/pull/12239)) — thanks @backryun +- **feat(sse):** wire the PROVIDER_PROFILES window gate into the global provider cooldown ([#12247](https://github.com/diegosouzapw/OmniRoute/pull/12247)) +- **feat(providers):** modernize CLOVA Studio chat and embeddings ([#12277](https://github.com/diegosouzapw/OmniRoute/pull/12277)) — thanks @backryun +- **feat(browser):** adopt Obscura as primary headless browser engine with Chromium fallback ([#12286](https://github.com/diegosouzapw/OmniRoute/pull/12286)) — thanks @opensource-elearning +- **feat(sse):** full language parity for output styles (es/de/fr/it/ru/zh + autoDetect) ([#12289](https://github.com/diegosouzapw/OmniRoute/pull/12289)) +- **feat(quality):** complete test:scoped — --full map rebuild, stdin selection, CI loader parity (#8084 D1) ([#12353](https://github.com/diegosouzapw/OmniRoute/pull/12353)) +- **feat(providers):** refresh Fable, Cursor, and Devin catalogs ([#12367](https://github.com/diegosouzapw/OmniRoute/pull/12367)) — thanks @backryun +- **feat(video):** redact transcript text from logs and durable memory (#12150 P1) ([#12427](https://github.com/diegosouzapw/OmniRoute/pull/12427)) +- **feat(dashboard):** parent-link, genuine-continuation badge, and modal perf fixes ([#12448](https://github.com/diegosouzapw/OmniRoute/pull/12448)) — thanks @hartmark +- **feat(dashboard):** adaptive context-budget dial on compression panel ([#12488](https://github.com/diegosouzapw/OmniRoute/pull/12488)) — thanks @HouMinXi +- **feat(providers):** refresh Z.ai Web models and browser transport ([#12524](https://github.com/diegosouzapw/OmniRoute/pull/12524)) — thanks @backryun +- **feat(video):** redact raw client-snapshot transcript fields in the detailed log (#12150 P2) ([#12528](https://github.com/diegosouzapw/OmniRoute/pull/12528)) +- **feat(providers):** refresh NVIDIA hosted models ([#12538](https://github.com/diegosouzapw/OmniRoute/pull/12538)) — thanks @backryun +- **feat(quota):** Moonshot Open Platform balance and TPD lock for custom nodes ([#12590](https://github.com/diegosouzapw/OmniRoute/pull/12590)) — thanks @HouMinXi +- **feat(video):** redact transcript in the in-memory pending-request snapshot (#12430 item 6) ([#12596](https://github.com/diegosouzapw/OmniRoute/pull/12596)) +- **feat(dashboard):** link a log entry's Conversation Context to its owning conversation ([#12646](https://github.com/diegosouzapw/OmniRoute/pull/12646)) — thanks @hartmark +- **feat(catalog):** eligibility-gated free-tier bucket ([#12669](https://github.com/diegosouzapw/OmniRoute/pull/12669)) +- **feat(db):** fail-closed previous_response_id continuation for redacted video turns (#12150 P2b) ([#12707](https://github.com/diegosouzapw/OmniRoute/pull/12707)) +- **feat(gamification): pay the documented `streak_bonus` and `badge_unlock` XP rewards.** `XP_REWARDS` listed both rewards but the award pipeline never paid them: the private reward table in `events.ts` omitted them, `updateStreak()` did not report when a streak extended, and badge unlocks carried no XP. Every request that extends a daily streak now pays `streak_bonus × streak length` once per UTC day (guarded by a same-day `xp_audit_log` check), and every badge unlocked through the pipeline pays `badge_unlock` once per badge (guarded by the `user_badges` primary key; `unlockBadge()` now reports whether it inserted). Bonus XP flows through the same `addXp` + level sync + global/weekly/monthly leaderboard path as action XP, so level-ups and rankings include it. The Radar supporter recognition unlock stays XP-free. (#12522 — thanks @pacocartones) +- **feat(providers):** advertise a `free-tier` capability in the provider plugin manifest for every provider with documented free models, so sidecars and dashboards can filter free-capable providers without reading the quota catalog ([#12786](https://github.com/diegosouzapw/OmniRoute/pull/12786)) — thanks @maxmad64bis +- **feat(providers):** Added EURouter as an OpenAI-compatible API-key gateway (`https://api.eurouter.ai/v1`), with live model discovery via `passthroughModels`. Its copy states that models are served by third-party upstreams listed per model, so an EU-based router is not read as EU data residency for inference. ([#13025](https://github.com/diegosouzapw/OmniRoute/pull/13025)) — thanks @ntdatt812 +- **feat(providers):** Added GreenPT as an OpenAI-compatible API-key provider (`https://api.greenpt.ai/v1`), with live model discovery via `passthroughModels`. No free-inference badge: the published docs describe a free API subscription billed per token, not a free tier. ([#13024](https://github.com/diegosouzapw/OmniRoute/pull/13024)) — thanks @ntdatt812 +- **feat(i18n):** 9 new locales — Greek (`el`), Croatian (`hr`), Serbian (`sr`), Lithuanian (`lt`), Estonian (`et`), Latvian (`lv`), Slovenian (`sl`), Maltese (`mt`), Irish (`ga`) — across the dashboard, docs mirrors, CLI, README and the site; OmniRoute now ships every official EU language (51 locales). (#13044) +- **feat(i18n):** 8 new locales — Kannada (`kn`), Malayalam (`ml`), Odia (`or`), Punjabi (`pa`), Nepali (`ne`), Sinhala (`si`), Burmese (`my`), Khmer (`km`) — across the dashboard, docs mirrors, CLI, README and the site (59 locales). The translator now restores the ICU literal escape around angle placeholders and splits oversized docs sections before translating. (#0000) ([#13660](https://github.com/diegosouzapw/OmniRoute/pull/13660)) +- feat(providers): **list Agnes 3.0 Flash as the current free chat model, drop retired 1.5 Flash, add Image 2.0/2.5 Flash plus Video 2.5/2.5 Flash, and discover the live `/v1/models` catalog (including the CN host `api.agnes-ai.cn`).** `agnes-1.5-flash` now forwards to `agnes-3.0-flash`. Video 2.5 polls `GET /v1/videos/{id}` (not the V2.0 `/agnesapi` contract). Live `/v1/models` (2026-09-09) no longer serves 1.5; the wiki marks it deprecated. 3.0 Flash is 512K context / 65,536 max output, same window as 2.5. CN-region keys use the existing per-connection base-URL field, default stays `apihub.agnes-ai.com`. ([#13120](https://github.com/diegosouzapw/OmniRoute/pull/13120)) — thanks @HouMinXi +- **feat(sse):** Codex and OpenAI catalogs list GPT-6 Astra with effort aliases (`-low` through `-ultra`); Codex CLI identity pins `@openai/codex@0.153.4` in lockstep with the image ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **feat(dashboard):** the orchestration History tab gained a "Compare runs" mode — toggling it turns each grid cell into a 2-item selection queue (a 3rd click drops the oldest pick), and picking two cells opens a side-by-side comparison panel instead of the usual detail drawer. The panel fetches both runs' detail the same way the drawer does (falling back to persisted history once a run leaves the live TTL window) and shows, per side: identity/source/state, start time, a signed `right - left` delta for duration/cost/event count, the event timeline aligned by index, and any memory hits. One side's fetch failing never blocks the other, and a delta is only ever computed when both sides have a finite value — otherwise it renders "—", never `NaN`. Comparing two runs from different sources or skills still works; a banner marks the deltas as informational rather than hiding them, since the two runs aren't a strict apples-to-apples pair. ([#12677](https://github.com/diegosouzapw/OmniRoute/pull/12677)) +- **feat(dashboard):** badge a conversation that never reached a clean stop ([#12717](https://github.com/diegosouzapw/OmniRoute/pull/12717)) — thanks @hartmark +- **feat(release):** reconcile-changelog tool + version-anchored fragment aggregation ([#12987](https://github.com/diegosouzapw/OmniRoute/pull/12987)) +- **feat(mcp):** carry context_length through MCP list_models_catalog projection (#12776) ([#13406](https://github.com/diegosouzapw/OmniRoute/pull/13406)) — thanks @KooshaPari +- **feat(cli):** make startup readiness budget configurable (#13369) ([#13433](https://github.com/diegosouzapw/OmniRoute/pull/13433)) — thanks @KooshaPari + ### 🐛 Bug Fixes +- **security(streaming):** sanitize generic mid-stream error messages before emitting OpenAI, + Responses, or Claude SSE failure frames and before diagnostic logging, while preserving raw + failures for internal classification and keeping client disconnects out of provider failure state. ([#12457](https://github.com/diegosouzapw/OmniRoute/pull/12457)) +- **fix(claude):** `blockExtraUsage: false` no longer lets 5h quota preflight skip the account; extra usage is billed after the session bar is gone, so the request must reach Anthropic ([#12803](https://github.com/diegosouzapw/OmniRoute/pull/12803)) — thanks @HouMinXi +- Sanitize HuggingChat conversation-creation and message-send transport failures before they reach client error bodies or provider logs. ([#12467](https://github.com/diegosouzapw/OmniRoute/pull/12467)) +- **fix(dashboard):** Keep local and theme-aware provider SVG icons at a definite layout size so Chromium does not collapse them to 0×0 after the v3.8.50 image-rendering change ([#12054](https://github.com/diegosouzapw/OmniRoute/pull/12054)) — thanks @ponkcore +- **fix(security):** Sanitize provider and runtime failures before public API, SSE and MCP responses and before persistent request, proxy and usage logs, preventing credentials, stack traces and host filesystem paths from crossing those boundaries while preserving stable error codes and useful diagnostics. ([#12506](https://github.com/diegosouzapw/OmniRoute/pull/12506)) +- **fix(cli):** `omniroute tunnel create` no longer crashes with `Cannot read properties of undefined (reading optsWithGlobals)` — removed the duplicate positional argument that caused Commander.js to misalign the action callback parameters ([#12295](https://github.com/diegosouzapw/OmniRoute/issues/12295)) ([#12368](https://github.com/diegosouzapw/OmniRoute/pull/12368)) — thanks @gonisulaimann +- Stop advertising Gemini Live-only models as supported audio endpoints until OmniRoute proxies the bidirectional Live protocol. ([#11710](https://github.com/diegosouzapw/OmniRoute/pull/11710)) — thanks @RaviTharuma +- **fix(kie):** reroute `flux/kontext` off the KIE Market `createTask` flow — it is catalogued with `isMarket: true` but has no Market catalog page, so KIE rejected it with "model name not supported"; it now hits the dedicated `POST /api/v1/flux/kontext/generate` / `GET /api/v1/flux/kontext/record-info` endpoints instead (#11296). ([#11985](https://github.com/diegosouzapw/OmniRoute/pull/11985)) — thanks @hartmark +- **fix(kie):** correct 12 more KIE Market catalog ids that were sent to `createTask` unchanged but diverge from KIE's documented upstream `model` values — GPT Image 2 T2I/I2I (drops the `gpt/` prefix), GPT Image 1.5 T2I/I2I (`gpt-image/` namespace), Seedream 5.0 Lite T2I/I2I (drops the `.0`), all 4 Flux 2 variants (`flux-2/` namespace, generic variant renamed `flex`), and Wan 2.7 Image / Image Pro (dash instead of dot) — each verified individually against the literal example request published on docs.kie.ai. `#11326`'s "everything else already matches" claim was wrong a second time (#11296); `z-image/4.0-*`/`z-image/4.5-*` and `flux/kontext` remain open, documented as unresolved in `KIE_MARKET_UPSTREAM_MODEL_IDS`'s comment pending further verification. ([#11667](https://github.com/diegosouzapw/OmniRoute/pull/11667)) +- **fix(dashboard):** `useApiKeySave.handleSaveApiKey` no longer forces a full upstream `/models` catalog sync on every non-curated provider connection save — callers can now pass `skipModelSync: true` to opt out, so a workflow that only wants to add one manual model no longer floods the provider's available-models list with hundreds/thousands of synced entries. The flag is a client-side intent signal only and is stripped before the connection payload is POSTed to `/api/providers`; default behavior (full sync on save) is unchanged when the flag is omitted (#11324) ([#11664](https://github.com/diegosouzapw/OmniRoute/pull/11664)) +- **Provider connections:** keep `tokenExpiresAt` when a connection is created. The create-path allowlist omitted it, so every insert stored NULL and the dashboard token badge could read a fresh connection as expired until its first background refresh ([#11368](https://github.com/diegosouzapw/OmniRoute/pull/11368)). — thanks @ntdatt812 +- **fix(combos):** the combo builder's precision-select, global-model-search, and manual-entry flows now serialize a model step's `model` string using the provider's already-computed routing-alias prefix (e.g. `oc/`) instead of rebuilding it from the raw canonical `providerId`, fixing the no-auth "OpenCode Free" provider (`opencode`) being routed to the unrelated paid "OpenCode Zen" provider (`opencode-zen`) because `opencode` doubles as a manual routing-prefix override ([#11433](https://github.com/diegosouzapw/OmniRoute/issues/11433)). ([#11670](https://github.com/diegosouzapw/OmniRoute/pull/11670)) +- **fix(radar):** the catalog feed cache now keeps `generatedAt`, the date the feed's data was built, next to `fetchedAt`, the date this install downloaded it (#11435). The feed schema requires that date and the sync path validates it, but the cache dropped it — so a feed fetched minutes ago and one carrying weeks-old figures looked identical to everything downstream, including the dashboard's "Last fetched" line. `getRadarCatalog().meta` and `GET /api/radar/status` now report both dates, the latter as its own field rather than folded into `version` — and omitted entirely for the offers and intel caches, which keep no build date, where a `null` would read as "unknown" rather than "never stored". The dashboard still shows only the fetch time; surfacing the build date there needs a new translated label and is left to a follow-up. Rows cached before migration 163 read back as `null`: unknown stays unknown instead of borrowing the fetch time. The referrals cache has persisted the same date since migration 142. — thanks @maxmad64bis +- **fix(cli):** postinstall no longer fills `JWT_SECRET` and `API_KEY_SECRET` in the installed package's `.env` (#11436). `.env.example` ships both blank on purpose: the server restores them from its durable store, or generates and persists them there, in `ensureSecrets()`. Pre-filling them defeated that — the file lives inside the package directory, so `npm i -g` replaced it and postinstall wrote _different_ values, while `ensureSecrets()` (which only acts on an empty variable) never got to restore the real ones. Both secrets rotated silently on every update, invalidating dashboard sessions and API-key CRCs. `STORAGE_ENCRYPTION_KEY` left the same list for the same reason in #1622; its comment pointed at a function that no longer exists and now names the real provisioning path. — thanks @maxmad64bis +- **fix(cli):** the CLI now says when a loaded `.env` lives inside the installed package directory (#11437). It already announces every env file it reads, without distinguishing the ones that survive an update from the one that does not: `npm i -g` replaces the package directory wholesale, so values set there are gone at the next update, silently. The warning names the durable path to move them to, and fires only when that file actually supplied a value — a file entirely shadowed by a durable one supplied nothing. A development checkout stays silent: there the same path is stable and documented in `SETUP_GUIDE.md`. — thanks @maxmad64bis +- **fix(free-models):** the shared `isFreeModel()` predicate no longer reports catalog entries marked `freeType: "discontinued"` as free, so `hidePaidModels` can't route to Pollinations' seven premium models that now require a paid key ([#11441](https://github.com/diegosouzapw/OmniRoute/pull/11441)) — thanks @maxmad64bis +- fix(ui): let AnySearch use the normal provider-icon fallback when LobeHub has no matching icon (#11449) +- Fixed the v3.8.50 Costs and Analytics dashboards so flat-rate Claude Code usage can be shown as an explicitly requested token-price estimate without changing default billed-cost semantics. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed archived usage retention so each request is priced individually instead of pricing a day's summed tokens once, which understated archived cost whenever a day mixed cache-heavy and ordinary requests. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed the Costs dashboard so it discloses when displayed figures include flat-rate token-price estimates instead of labelling them as billed spend, using the flag the analytics API already returns; the month-end projection and the CSV/JSON exports carry the same marker, and billed-cost mode is unchanged. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- **fix(combo):** attach the same combo-diagnostics trace (`poolSize`/`attemptOrder`/`excluded`/`terminalReason`, plus `x-omniroute-combo-*` headers) to the round-robin strategy's and the nested pipeline/fusion runtime-unit loop's "Maximum combo retry limit reached" 503 that the priority-strategy path already attaches for the identical terminal condition — previously those two paths returned a bare, contextless 503 ([#11462](https://github.com/diegosouzapw/OmniRoute/issues/11462)). ([#11665](https://github.com/diegosouzapw/OmniRoute/pull/11665)) +- **fix(bun):** use native `bun:sqlite` in `bootstrap-env` and `sync-env` to avoid loading `better-sqlite3` N-API addon during Bun startup ([#11468](https://github.com/diegosouzapw/OmniRoute/pull/11468)) — thanks @TheDemonTuan +- **fix(docker-bun):** make `Dockerfile.bun` install reliable with pre-install script helpers and native `bun:sqlite` smoke check ([#11470](https://github.com/diegosouzapw/OmniRoute/pull/11470)) — thanks @TheDemonTuan +- **fix(docker-bun):** remove every vendored `better-sqlite3` native addon from the Bun runtime image so startup cannot abort before the `bun:sqlite` fallback ([#11482](https://github.com/diegosouzapw/OmniRoute/pull/11482)) — thanks @TheDemonTuan +- **fix(autoCombo):** effort/alias model variants (`gpt-5.6-sol-xhigh`, `gpt-5.6`, cursor's `claude-4.6-opus-high`) inherit their base model's task fitness instead of falling to the wildcard 0.5 ([#11492](https://github.com/diegosouzapw/OmniRoute/pull/11492)) — thanks @MumuTW +- fix(db): rate-limit repeated Arena ELO leaderboard fetch-failure warnings instead of logging one per sync attempt (#11500) ([#11989](https://github.com/diegosouzapw/OmniRoute/pull/11989)) — thanks @hartmark +- **fix(db):** dedupe the raw `[Encryption] Decryption failed...` log line emitted by the lazy-decrypt views (`createLazyRowProxy`/`createLazyConnectionView`), which power `getProviderConnections()` and were re-triggering that line on every CredentialHealth/model-sync cycle for the same corrupt or stale-key credential — a fresh Proxy over a fresh row on every cycle meant the per-proxy memoization never suppressed it, unlike the dedup `decryptConnectionFields()` already had since [#9927](https://github.com/diegosouzapw/OmniRoute/issues/9927). Now shares that dedupe tracking so the line logs at most once per credential ([#11500](https://github.com/diegosouzapw/OmniRoute/issues/11500)). ([#11678](https://github.com/diegosouzapw/OmniRoute/pull/11678)) +- **fix(intelligence):** Arena ELO sync stores a synthesized base-model row for every effort/harness variant (`claude-opus-5-max` → `claude-opus-5`, `gpt-5.6-sol-xhigh (codex-harness)` → `gpt-5.6-sol`), so bare model requests reach the synced intelligence layer instead of the static fallback table; the hand-maintained `MODEL_ALIAS_MAP` — which collapsed generations (`gpt-5.5` → `gpt-5`) — is gone ([#11506](https://github.com/diegosouzapw/OmniRoute/pull/11506)) — thanks @MumuTW +- **fix(autoCombo,sse):** catalog hygiene — the static task-fitness table no longer ranks retired models above live ones (dead rows dropped, patterns matched on segment boundaries), the 7 deprecation aliases that forwarded to retired or non-existent models now point at the vendors' published replacements, an alias is no longer rewritten when the serving provider offers the id as-is, and a new `check:model-lifecycle` gate diffs both tables against a vendor lifecycle snapshot ([#11507](https://github.com/diegosouzapw/OmniRoute/pull/11507)) — thanks @MumuTW +- **fix(sse):** stop dropping resolved `thoughtSignature` values on parallel (multi tool-call) turns sent to Gemini 3.x — the claude→gemini and openai→gemini translators previously kept the signature only on the *first* function call of a message, causing Gemini to reject subsequent calls in the same turn with HTTP 400 "Function call is missing a thought_signature"; each function call now keeps its own resolved signature ([#11510](https://github.com/diegosouzapw/OmniRoute/issues/11510)). ([#11669](https://github.com/diegosouzapw/OmniRoute/pull/11669)) +- Treat Docker publishing from a non-default release branch as an expected skip instead of a failed workflow, while preserving the guard that prevents it from overwriting the `next` channel. ([#11527](https://github.com/diegosouzapw/OmniRoute/pull/11527)) — thanks @pacocartones +- fix(search): prefer credentialed providers over duckduckgo-free fallback (#11524) — the fallback-only loop ran before the credentialed-providers loop in `executeWebSearch`, making configured providers unreachable when `duckduckgo-free` was available. ([#11565](https://github.com/diegosouzapw/OmniRoute/pull/11565)) — thanks @vsd2807 +- **fix(sse):** cap the upstream headers-wait phase for STREAMING requests to a client-realistic ceiling (110s, under Codex's own ~120s hard client-abort window) instead of the flat 10-minute `FETCH_TIMEOUT_MS` default — that default was 5x longer than the body-phase readiness watchdog's own adaptive bound, so a request whose upstream never returned any response at all (not even headers, e.g. a stalled NVIDIA target behind a tool-heavy Responses→Chat translation) kept the client connection alive on keepalives only, guaranteeing the client's own patience ran out first with an opaque 499 instead of OmniRoute detecting and failing the stall fast. Non-streaming requests are unaffected — they keep the existing flat default (`open-sse/utils/fetchStartTimeoutPolicy.ts`) (#11526) ([#11668](https://github.com/diegosouzapw/OmniRoute/pull/11668)) +- **fix(ci):** protocol-clients E2E harness boots the peer-stamped custom server (`run-next.mjs dev`) instead of the bare `next` CLI, so `LOCAL_ONLY` locality resolves from the real TCP peer stamp and `/api/mcp/audit` answers 200 (open bootstrap preserved via an `OMNIROUTE_E2E_BOOTSTRAP_MODE=open` credential clear in `run-next.mjs`, pinned to loopback `HOST`) instead of a deterministic 403 (#11535) ([#11549](https://github.com/diegosouzapw/OmniRoute/pull/11549)) — thanks @jonlwheat2-gif +- **fix(docs):** the Auto-Combo engine is described with the number of factors it actually declares. Four different counts were in circulation — 6, 9, 13 and 14 across `AGENTS.md`, four guides and six code comments — while `DEFAULT_WEIGHTS` declares 15. `check:docs-counts` now reads that number from the source, so adding a factor without updating the prose fails CI ([#11545](https://github.com/diegosouzapw/OmniRoute/pull/11545)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **fix(quota-share):** in-flight leases are per request, so an aborted request's slot ages out instead of being kept alive by later traffic on the same connection ([#11547](https://github.com/diegosouzapw/OmniRoute/pull/11547)) — thanks @abhisheksharma2411 +- The free-tier summary route now serves the refreshed Radar catalog when the feed is active — the same numbers the dashboard shows — and states which catalog answered plus its real build date, instead of always reporting release-frozen figures with a stale curation date. The supporter-key live feed stays reserved to authenticated callers of the instance. ([#11550](https://github.com/diegosouzapw/OmniRoute/pull/11550)) — thanks @maxmad64bis +- **sse:** fix LiveWS/embed-WS servers crashing at startup under the Node/tsx runtime — `liveServer.ts`, `embedWsProxy.ts` and `apiBridgeServer.ts` imported `@/shared/utils/httpClientAbortGuard` without the `.mjs` extension, so the client-abort crash guard added by [#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556) was unreachable and every dependent test failed with `ERR_MODULE_NOT_FOUND` ([#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556)). ([#11671](https://github.com/diegosouzapw/OmniRoute/pull/11671)) — thanks @jonlwheat2-gif / @yxyxy +- fix(chat): shed any of the matched provider-node's routing identifiers (public prefix or internal id) before model lookup, so composites like `//` no longer reach upstream verbatim (#11557) — thanks @maxmad64bis +- **fix(cache):** a semantic-cache entry now expires on its TTL instead of surviving until the next UTC midnight, and `dbEntries` stops counting expired rows ([#11573](https://github.com/diegosouzapw/OmniRoute/pull/11573)) — thanks @ntdatt812 +- **fix(api):** `/v1/models` schedules its stale-while-revalidate rebuild through Next's `after()` again, so a stale catalog reaches the client before the rebuild blocks the event loop ([#11574](https://github.com/diegosouzapw/OmniRoute/pull/11574)) — thanks @ntdatt812 +- **fix(check):** the `known-symbols` executor conformance gate awaits the now-async `getExecutor()`, so it stops reporting all 142 registered aliases as dead symbols and can detect a lazy import that fails ([#11582](https://github.com/diegosouzapw/OmniRoute/pull/11582)) — thanks @ntdatt812 +- **fix(pack):** register `bin/cli/utils/volatileEnvPath.mjs` in `PACK_ARTIFACT_REQUIRED_PATHS`, so the prune can no longer silently drop a module the published CLI imports on every boot ([#11588](https://github.com/diegosouzapw/OmniRoute/pull/11588)) — thanks @ntdatt812 +- **i18n(vi):** translate the five reliability-column strings on the free-provider rankings page, which rendered as `__MISSING__:` markers in the Vietnamese UI ([#11589](https://github.com/diegosouzapw/OmniRoute/pull/11589)) — thanks @ntdatt812 +- **resilience:** restore the expired-connection retry-budget probe in the token-health sweep — the `!isGitHubAccessTokenOnlyConnection` carve-out reintroduced by #11608 contradicted the boundary pinned by #11592, so a GitHub connection parked at `expired` with retry budget remaining was never probed and could never self-heal ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)). ([#11672](https://github.com/diegosouzapw/OmniRoute/pull/11672)) — thanks @jonlwheat2-gif / @ntdatt812 +- **fix(dashboard):** Provider Stats now displays combo success rates on their native 0–100 percentage scale instead of multiplying them twice ([#11595](https://github.com/diegosouzapw/OmniRoute/pull/11595)) — thanks @pacocartones +- **fix(providers):** route DuckDuckGo Web Chat through the verified `duck.ai` endpoint, refresh its free model catalog, and avoid forwarding unsolved anti-abuse challenges ([#11597](https://github.com/diegosouzapw/OmniRoute/pull/11597)) — thanks @solstxce +- **fix(ui):** Console log Refresh and Copy controls now expose localized accessible names, keep copy actions visible on keyboard focus, and announce copy completion safely ([#11599](https://github.com/diegosouzapw/OmniRoute/pull/11599)) — thanks @pacocartones +- Dropped the stale-`.eslintcache` `restore-keys` fallback from both "Restore ESLint file cache" steps in `ci.yml`, so the blocking `Lint` job can no longer be served per-file verdicts computed under a different lint config, suppressions file or lockfile. `quality.yml` had already dropped it in #11963; `ci.yml` — the workflow that actually gates PRs — had not (#11600). ([#11996](https://github.com/diegosouzapw/OmniRoute/pull/11996)) +- fix(ci): pass `--pass-on-unpruned-suppressions` in `run-eslint-json.mjs` so the CI Lint job no longer fails when a suppression is merely orphaned by a genuine fix, mirroring the identical fix already in `validate-release-green.mjs` (#11600) ([#11983](https://github.com/diegosouzapw/OmniRoute/pull/11983)) — thanks @hartmark +- fix(handoff): enforce provider allowlist for universal handoff (#11602) — universal handoff now skips summarization when the selected summary model's provider is not included in the configured provider allowlist. ([#11619](https://github.com/diegosouzapw/OmniRoute/pull/11619)) — thanks @vsd2807 +- **fix(dashboard):** Show a stable error state when Search Analytics returns an HTTP error or malformed data ([#11603](https://github.com/diegosouzapw/OmniRoute/pull/11603)) — thanks @pacocartones +- **test(gamification):** pin the aggregate profile level to the XP-derived semantics of #11604 — `getAggregateXp()` now derives `currentLevel` from the summed XP (`calculateLevel(sum)`), not `MAX(stored current_level)`, and the #3484 fixture levels are aligned with the XP curve ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)). ([#11676](https://github.com/diegosouzapw/OmniRoute/pull/11676)) — thanks @jonlwheat2-gif / @pacocartones +- **fix(dashboard):** Keep the Profile level and progress aligned with aggregate XP, including bounded handling for invalid totals ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)) — thanks @pacocartones +- **fix(dashboard):** Prevent locked hidden badges from revealing their icon or opening private badge details before they are earned ([#11605](https://github.com/diegosouzapw/OmniRoute/pull/11605)) — thanks @pacocartones +- **fix(dashboard):** Restore keyboard focus after shared modals close and cancel delayed autofocus during cleanup ([#11607](https://github.com/diegosouzapw/OmniRoute/pull/11607)) — thanks @pacocartones +- **fix(dashboard):** Enable Enter and Space activation for clickable data-table rows without hijacking nested controls ([#11610](https://github.com/diegosouzapw/OmniRoute/pull/11610)) — thanks @pacocartones +- **fix(autoCombo,sse):** vendor-retired catalog ids are dropped from the auto-combo candidate pool and no longer win on leftover `arena_elo` / `user_override` rows; `getModelLifecycleDecision` consults `model-lifecycle.json` (prefix-stripped) so aggregator traffic is not `untracked` for ids the snapshot already knows ([#11625](https://github.com/diegosouzapw/OmniRoute/issues/11625)) ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)) — thanks @MumuTW +- **test(chatcore):** move Codex/Claude combo fixtures off models the lifecycle guard now rejects — `gpt-5.1-codex`/`gpt-5-codex` are vendor-retired (snapshot, #11626) and `claude-3-5-sonnet-20241022` is shut down, so native-passthrough and combo-fallback tests switched to `gpt-5.6-sol` and `claude-sonnet-4.6` ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)). ([#11675](https://github.com/diegosouzapw/OmniRoute/pull/11675)) — thanks @jonlwheat2-gif / @MumuTW +- fix(opencode-plugin): stop a free-tier budget that rounds up across a magnitude from rendering as `1000K`/`1000M` in the model picker — `fmtTokens` chose its unit from the raw token count and then rounded with `toFixed(1)`, so 999,950–999,999 printed as `1000K` rather than `1M` and just under a billion printed as `1000M` rather than `1B` ([#11684](https://github.com/diegosouzapw/OmniRoute/pull/11684)) — thanks @abhisheksharma2411 +- Cap prose-derived Antigravity quota resets at the configured model cooldown maximum while preserving authoritative Retry-After headers and Google RetryInfo hints. (#11695) Thanks @pacocartones. ([#11823](https://github.com/diegosouzapw/OmniRoute/pull/11823)) +- Compare `_rebaseline_*` notes structurally in the ratchet-bank verifier so an object-valued note no longer reports itself as rewritten on an unmodified tree — the nightly `bank-ratchet-shrinks` lane had been aborting on every run since 2026-08-11, leaving 41,672 lines of earned file-size shrinks unbanked (#11702). ([#11892](https://github.com/diegosouzapw/OmniRoute/pull/11892)) — thanks @MumuTW +- **fix(build):** `prepublish.ts` bundles the ChatGPT Web (Codex) MCP bridge through `runBuildTool()` instead of spawning `npx.cmd` raw, fixing the build crash on Node ≥ 20/Windows where `.cmd` shims cannot be spawned without a shell (EINVAL) ([#11704](https://github.com/diegosouzapw/OmniRoute/issues/11704)) ([#11706](https://github.com/diegosouzapw/OmniRoute/pull/11706)) — thanks @jonlwheat2-gif +- fix(codex): keep `parallel_tool_calls:false` on the translated Codex Responses Lite path (#11707) ([#11984](https://github.com/diegosouzapw/OmniRoute/pull/11984)) — thanks @hartmark +- **fix(tests):** the new Gemini Web Images retirement test (#11708) called the now-async `getExecutor()` synchronously, throwing `TypeError: getExecutor(...).getProvider is not a function` instead of asserting Gemini Web chat stays available. Awaited to match the R0.3 executor-registry refactor (#11220). ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(search):** Allow REST and MCP web-fetch requests to select the advertised `anysearch-search` provider explicitly ([#11716](https://github.com/diegosouzapw/OmniRoute/pull/11716)) — thanks @pacocartones +- **fix(providers):** Keep overnight peak-hour protection active after midnight according to the window's configured start day ([#11718](https://github.com/diegosouzapw/OmniRoute/pull/11718)) — thanks @pacocartones +- **fix(providers):** correct all five g4f.space gateway descriptions to document proof-of-work credits or member API keys, variable limits, and the remote prompt boundary instead of promising free keyless access at five requests per minute ([#11738](https://github.com/diegosouzapw/OmniRoute/pull/11738)). +- **fix(db):** `model_capabilities` is created by a migration instead of lazily on the first models.dev sync, so a clean install and an upgraded install converge on the same schema regardless of which features have run ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(ci):** `check:install-upgrade` now fails on an `npm` install truncated by ENOSPC (npm reports it as a warning and still exits 0), authenticates its health probe so the version assertion works against the hardened health payload, frees the clean-install tree before the upgrade phase, and no longer reports a schema divergence computed from a boot that never served ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(routing):** harden session-affinity key derivation against oversized explicit IDs and empty structural-payload collisions, while recognizing Gemini and common text request shapes ([#11746](https://github.com/diegosouzapw/OmniRoute/pull/11746)) — thanks @MumuTW +- **fix(packages/browser-pool):** regenerate `package-lock.json` so the `packages/browser-pool` workspace's locked `playwright`/`@types/node` (and transitives) match its `package.json` specs, fixing cache-only/offline installs (`npm ci --offline`, Nix `buildNpmPackage`) that previously failed with `ENOTCACHED` ([#11747](https://github.com/diegosouzapw/OmniRoute/issues/11747)) — thanks @benjaminkitt ([#11784](https://github.com/diegosouzapw/OmniRoute/pull/11784)) — thanks @geekyNads +- **fix(assets):** Stop distributing 79 provider and CLI logos without verified source/license provenance, keep every provider/tool available through the internal generic icon, and prevent those IDs from falling through to an external icon CDN. ([#11750](https://github.com/diegosouzapw/OmniRoute/pull/11750)) +- **fix(providers):** retire the common `chatgpt-web` / `cgpt-web` provider because its pre-key and proof-of-work provenance could not be cleared ([#11754](https://github.com/diegosouzapw/OmniRoute/pull/11754)) — explicit chat and image requests, slash-prefixed models, persisted aliases, proxy overrides, and combo targets now fail closed with a sanitized HTTP `410` / `PROVIDER_RETIRED` before upstream dispatch. Migration 163 tombstones matching connections and invalidates their active leases without deleting history or widening API-key allowlists. The independent `chatgpt-web-codex` / `cgpt-codex` provider and its MIT-noticed vendored implementation remain available. +- **fix(usage):** quota and usage refresh no longer 409 when an exclusive lease reserves the connection ([#11758](https://github.com/diegosouzapw/OmniRoute/pull/11758)) — thanks @TheDemonTuan +- Keep the embedding registry's vector width and `embedding` type on models when a synced model exists for the same id, so `/v1/models` no longer reports registry-described embedding models widthless or untyped (#11761) — thanks @rahilmavani +- Correct `google/gemini-embedding-001` on the OpenRouter route to 3072 dimensions, the width it returns when `dimensions` is not sent (#11761) — thanks @rahilmavani +- **fix(vision-bridge):** Restrict automatic vision routing and fallbacks to models available in authoritative live provider catalogs, including aliases and effort variants ([#11767](https://github.com/diegosouzapw/OmniRoute/issues/11767)) — thanks @pacocartones ([#11813](https://github.com/diegosouzapw/OmniRoute/pull/11813)) +- **fix(providers):** reclassify Cerebras as a one-time $5 signup credit (payment method required, 30-day validity), not a recurring no-card 1M tokens/day trial ([#11773](https://github.com/diegosouzapw/OmniRoute/issues/11773)) ([#12591](https://github.com/diegosouzapw/OmniRoute/pull/12591)) — thanks @HouMinXi +- fix(cli): stop prepublish from re-rebuilding the already-built ESM-only opencode-plugin dist (#11787) ([#11990](https://github.com/diegosouzapw/OmniRoute/pull/11990)) — thanks @hartmark +- **fix(db):** Fresh installs no longer log a non-fatal `no such table: compression_run_telemetry` warning when retention cleanup runs before the lazily-created telemetry table exists ([#11802](https://github.com/diegosouzapw/OmniRoute/pull/11802)) — thanks @RaviTharuma +- fix(sse): set X-OmniRoute-Selected-Connection-Id on successful combo dispatches so downstream consumers stop falling back to an empty connection id (#11810) ([#11986](https://github.com/diegosouzapw/OmniRoute/pull/11986)) — thanks @hartmark +- **fix(services):** embedded CLIProxyAPI lifecycle checks now use public `/healthz`, while model discovery uses the configured dedicated data-plane API key instead of the management password ([#11811](https://github.com/diegosouzapw/OmniRoute/pull/11811)) — thanks @RaviTharuma +- Fix persisted model steps returning an error in the playground route simulator, and warn when structural steps cannot be simulated. ([#11862](https://github.com/diegosouzapw/OmniRoute/pull/11862)) — thanks @pacocartones +- **fix(providers):** Antigravity's dynamic mitmAlias table no longer routes `gemini-3.7-flash-{high,medium,low}` to a literal tier-suffixed upstream id just because one connected account's own discovery listed it directly — those display ids always resolve through the safe `gemini-3.7-flash-tiered` static alias, so one account's Google-provisioned access no longer 404s every sibling account of the provider ([#11824](https://github.com/diegosouzapw/OmniRoute/issues/11824), [#11651](https://github.com/diegosouzapw/OmniRoute/issues/11651)) ([#11988](https://github.com/diegosouzapw/OmniRoute/pull/11988)) — thanks @hartmark +- **fix(dashboard):** Expose Profile loading, errors, page structure, and XP progress to assistive technologies ([#11838](https://github.com/diegosouzapw/OmniRoute/pull/11838)) — thanks @pacocartones +- **fix(socks):** `Agent.connectTimeout` now bounds both SOCKS handshake and TLS connect, and `family === null` no longer falls back to `fetch-socks` ([#11842](https://github.com/diegosouzapw/OmniRoute/pull/11842)) — thanks @maxmad64bis +- **perf(compression):** OOM mitigations for large payload hashing, memoization, and token estimation ([#11844](https://github.com/diegosouzapw/OmniRoute/pull/11844) — thanks @AndrianBalanescu) +- Excluded Next.js Node File Trace manifests (`*.nft.json`) from the published npm tarball. They are build-time metadata and are never read while serving, but had grown to 668.7 MB — 61% of the package — which pushed the upload past the registry limit and made `npm publish` fail with `413 Payload Too Large`. ([#11864](https://github.com/diegosouzapw/OmniRoute/pull/11864)) +- **fix(cli):** keep the global OmniRoute server `--base-url` separate from provider-node `--endpoint` values in add, update, and validate commands ([#11860](https://github.com/diegosouzapw/OmniRoute/pull/11860)) — thanks @pacocartones +- **fix(config):** Nous Research's `Hermes-4-405B` model now displays as "Hermes 4 405B (Nous Research)" in both the provider registry and the free-model catalog, instead of the mislabelled "Hermes 4 7B" ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#11993](https://github.com/diegosouzapw/OmniRoute/pull/11993)) +- **fix(provider/nous):** inject required user= tag into Nous Research inference requests to resolve upstream 400 "missing tags" error ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#12044](https://github.com/diegosouzapw/OmniRoute/pull/12044)) +- **fix(build):** `npm run build` now fails in one second with a named package and a copy-pasteable fix when npm silently drops an externalised optional native dependency, instead of dying four minutes in with `Module not found: Can't resolve 'better-sqlite3'` ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- **fix(install):** `postinstall` no longer throws `ReferenceError: isAndroid is not defined` — failing the whole `npm install` — when the `better-sqlite3` rebuild fallback times out; the manual-fix guidance is reachable again ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- Fixed the Alibaba free-tier allowlist test that went red on its own once the shipped catalog's `validUntil` (2026-08-27) passed, leaving every PR and `main` with a failing `Unit Tests (1/8)`. The test now builds its own packs with dates it controls, and covers the expired-pack fallback that production has actually been serving. ([#11867](https://github.com/diegosouzapw/OmniRoute/pull/11867)) +- Split the npm registry upload into its own GitHub-hosted job. npm refuses `--provenance` from a self-hosted runner (`422 ... Only "github-hosted" runners are supported`), which blocked the v3.8.50 publish; the heavy verification cannot move to a hosted runner, so it now hands the proven tarball over instead. ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(ui):** the shared `DataTable` loading state no longer reads its decorative ⏳ glyph out to assistive technology, and now carries the same `role="status"` / `aria-live="polite"` / `aria-busy="true"` semantics as `PageLoading` ([#11869](https://github.com/diegosouzapw/OmniRoute/pull/11869)) — thanks @pacocartones +- **fix(api):** Bound the `timeout` accepted by `POST /api/providers/[id]/login` to the same 15s–600s window the Adobe Firefly and Conol browser-login paths already enforce, so a request can no longer pin a headful browser and the single active-login slot with an arbitrarily large poll budget. ([#11871](https://github.com/diegosouzapw/OmniRoute/pull/11871)) — thanks @pacocartones +- **fix(api):** Stop the tunnel and MITM routes from returning raw child-process error text, which disclosed host paths, binary install locations and Tailscale `tskey-*` credentials in a body some of these routes serve to non-loopback callers, and return a real 400 instead of a framework 500 when a tunnel request body fails validation. ([#11872](https://github.com/diegosouzapw/OmniRoute/pull/11872)) — thanks @pacocartones +- **fix(open-sse):** stream TTFT/ITL are sampled from a monotonic clock (`performance.now`) instead of `Date.now`, so an NTP correction or wall-clock jump can no longer inflate a genuine TTFT or produce a negative one that the `ttft >= 0` guard silently drops — protecting the router's OTel/EWMA/`usage_history`/speed-ranking signals ([#11873](https://github.com/diegosouzapw/OmniRoute/pull/11873)) — thanks @pacocartones +- **fix(providers):** xAI `reasoning_effort: "xhigh"` now reaches grok-4.6+ instead of being silently clamped to `"high"` ([#11879](https://github.com/diegosouzapw/OmniRoute/pull/11879)) — thanks @NoxzRCW +- **fix(dashboard):** the providers page no longer crashes into the error boundary when a provider id collides with an `Object.prototype` member (`constructor`, `__proto__`); icon lookups are own-property guarded ([#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)) — thanks @NoxzRCW +- **fix(skills):** injected skill tools declared in shorthand (`{"content": "string"}`) now forward valid JSON Schema, unblocking providers that validate tool schemas strictly such as Zhipu GLM on the Console Go tier ([#11881](https://github.com/diegosouzapw/OmniRoute/pull/11881)) — thanks @NoxzRCW +- **fix(api):** `POST /api/playground/simulate-route` now surfaces `combo-ref` and `provider-wildcard` persisted combo steps with a specific warning (naming the referenced combo, or the unresolved `provider/model` wildcard) instead of folding them into a generic "unsupported step" count; a `provider-wildcard` step is also now included as an unresolved target so the operator can see it is in the route (ported from [#11882](https://github.com/diegosouzapw/OmniRoute/pull/11882) — thanks @NoxzRCW). ([#11930](https://github.com/diegosouzapw/OmniRoute/pull/11930)) +- **fix(translator):** the streaming OpenAI→Claude translator keeps upstream usage, including prompt-cache tokens, when it arrives on a trailing `choices: []` chunk (Fireworks and any upstream using `stream_options.include_usage`) ([#11883](https://github.com/diegosouzapw/OmniRoute/pull/11883)) — thanks @NoxzRCW +- **fix(cli):** `omniroute update --apply` now tells you explicitly whether a running server was detected and, if so, that you must run `omniroute restart` to apply the update — it never restarted anything and previously implied the update was already live once files were installed. The dashboard's npm-mode Update flow (`/api/system/version`) now tries OmniRoute's own PID-file-managed supervisor before falling back to pm2, and reports an honest "restart required" step instead of a silent pm2-only "skipped" that read like a completed update. The server-side latest-version lookup backing the dashboard's update banner also gained `--prefer-online`, closing the same stale-npm-cache class already fixed in the CLI's own copy for #4376 ([#11885](https://github.com/diegosouzapw/OmniRoute/issues/11885)). ([#12005](https://github.com/diegosouzapw/OmniRoute/pull/12005)) +- **fix(resilience):** clear persisted LKGP pins when a target suffers connection/provider exhaustion or is skipped before dispatch due to cooldown/exhaustion/unavailability, preventing subsequent requests from repeatedly prioritizing known-dead providers ([#11911](https://github.com/diegosouzapw/OmniRoute/issues/11911)). ([#12013](https://github.com/diegosouzapw/OmniRoute/pull/12013)) — thanks @HouMinXi +- **fix(sse):** the OpenAI→Claude stream translator now defers the terminal `message_delta`/`message_stop` emission until the real usage block has arrived (or a genuine end-of-stream flush forces it) instead of emitting it immediately on `finish_reason` — previously, when the trailing usage-only chunk (`{"choices":[],"usage":{...}}`) arrived *after* the `finish_reason` chunk (the normal order for Fireworks/vLLM/Together and other `stream_options.include_usage` upstreams), the client-visible `message_delta` still carried stale/zero usage even though `state.usage` was internally corrected too late to matter (ported from [#11915](https://github.com/diegosouzapw/OmniRoute/pull/11915) — thanks @HouMinXi). ([#11933](https://github.com/diegosouzapw/OmniRoute/pull/11933)) +- **fix(sse):** `stripStore()` now forces `store=false` for stateless OpenAI-compatible Responses-API targets (unless the connection has explicitly opted in via `providerSpecificData.openaiStoreEnabled`), instead of only handling the `openai`/`agentrouter` cases — a client-supplied `store` value previously passed through untouched to backends that don't actually persist responses server-side ([#11916](https://github.com/diegosouzapw/OmniRoute/pull/11916) — thanks @HouMinXi). +- **fix(models):** custom provider-node models (synced, custom, and alias-backed) now appear under their configured prefix in the unified catalog when the operator's model-id prefix mode is canonical, instead of being dropped whenever alias-inclusion was otherwise disabled ([#11918](https://github.com/diegosouzapw/OmniRoute/pull/11918) — thanks @HouMinXi). +- **fix(models):** the unified model catalog now suppresses stale static registry models (including effort-tier variants) for any provider whose active connection has an authoritative live synced catalog, not only providers already using exclusive-synced-listing — a connection with `providerUsesAuthoritativeLiveCatalog` previously kept serving both the live-synced models and the stale static rows side by side ([#11919](https://github.com/diegosouzapw/OmniRoute/pull/11919) — thanks @HouMinXi). +- **fix(ui):** `ProviderIcon`'s three remaining unguarded lookups (`PROVIDER_ICON_ALIASES`, `LOCAL_SVG_ALIASES`, `THEMED_SVGS`) now use `Object.hasOwn()` own-property checks — a provider id such as `constructor` or `__proto__` previously resolved through the prototype chain instead of falling through to the unknown-provider CDN fallback (`getLobeProviderIcon()` itself was already guarded by [#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)); `ProviderPageHeader`'s `color` field is also now optional, matching the rest of the component's defensive typing (ported from [#11920](https://github.com/diegosouzapw/OmniRoute/pull/11920) — thanks @HouMinXi). ([#11935](https://github.com/diegosouzapw/OmniRoute/pull/11935)) — thanks @NoxzRCW +- fix(ollama): preserve multi-byte UTF-8 content split across stream chunks in the Ollama NDJSON transform, which previously corrupted CJK/emoji into U+FFFD (#11921) — thanks @pacocartones +- **fix(providers):** OrcaRouter chat requests now target `/v1/chat/completions` instead of the bare `/v1` API root, fixing the upstream `404 Invalid URL (POST /v1)` ([#11923](https://github.com/diegosouzapw/OmniRoute/pull/11923)). — thanks @echel0nn +- **fix(plugins):** deliver the `onStreamComplete` event to disk-installed plugins. The event shipped in v3.8.50 (#9669) was emitted internally but had no plugin-facing wiring, so no plugin could ever subscribe: the manifest schema silently dropped `hooks.onStreamComplete`, and the loader/manager only knew the seven legacy hooks. `onStreamComplete` is now a declarable manifest hook, wired through the loader and registered by the manager like the other hooks, and its payload carries a `requestId` so consumers can correlate the stream-completion event with the originating request ([#11934](https://github.com/diegosouzapw/OmniRoute/pull/11934)) — thanks @amaleta / @pacocartones +- **fix(db):** the Qdrant embedding-model dropdown now lists local/self-hosted providers (Ollama, LM Studio, vLLM, etc.) — an active connection is treated as "configured" when the provider allows an optional API key, not only when it has a real key or OAuth, so a running local embedding provider is no longer hidden from the picker ([#11949](https://github.com/diegosouzapw/OmniRoute/issues/11949)) ([#11995](https://github.com/diegosouzapw/OmniRoute/pull/11995)) +- **fix(providers):** Vertex AI Anthropic partner-model discovery now calls the Model Garden `v1beta1` publisher list (`/v1beta1/publishers/anthropic/models`, global) and parses its `publisherModels` envelope, so Claude models auto-synced from Vertex populate the active live catalog and route at request time instead of returning `Model '' is not available in the active live catalog` ([#11991](https://github.com/diegosouzapw/OmniRoute/issues/11991)) — thanks @fabioluissilva ([#11998](https://github.com/diegosouzapw/OmniRoute/pull/11998)) +- **fix(cli):** support `--base-url` alongside `--endpoint` in `omniroute nodes add`, `update`, and `validate` subcommands to prevent global `--base-url` shadowing issues ([#11999](https://github.com/diegosouzapw/OmniRoute/issues/11999)). ([#12033](https://github.com/diegosouzapw/OmniRoute/pull/12033)) — thanks @HouMinXi +- **fix(providers):** `cloudflare-ai` no longer refuses image content parts for every Workers AI model ([#12002](https://github.com/diegosouzapw/OmniRoute/pull/12002)) — the plain-string `content` requirement behind #2539 is carried by the _model_ schema, not by the `/ai/v1/chat/completions` endpoint (measured: an all-text part array returns 200 on `@cf/mistralai/mistral-small-3.1-24b-instruct`, `@cf/meta/llama-4-scout-17b-16e-instruct` and `@cf/meta/llama-3.3-70b-instruct-fp8-fast`, and 400 on the text-only `@cf/qwen/qwen2.5-coder-32b-instruct`). `transformRequest()` flattened every array and threw on the first non-text part (#6390), so image input was refused for vision-capable Cloudflare models that accept it. All-text arrays are still flattened — the one shape every model accepts — while an array carrying a non-text part is passed through untouched, so the attachment is still never silently dropped. Regression guards: `tests/unit/cloudflare-ai-image-parts-6390.test.ts`. — thanks @davidlinfr +- **fix(resilience):** decouple the limiter-managed execution backstop from the queue-wait budget — new `requestQueue.executionMaxWaitMs` (env `RATE_LIMIT_EXECUTION_MAX_WAIT_MS`, default 600000 = 10 min) now feeds Bottleneck's post-dispatch `expiration`, while `requestQueue.maxWaitMs` keeps its documented queue-wait semantics. Previously the queue-wait budget doubled as the execution expiration, so legitimate long-running calls on non-incremental gateways (whole generation buffered before the first upstream byte, e.g. Console Go / Command Code tiers serving GLM models) were killed mid-flight at the queue budget with a false 504 `RATE_LIMIT_EXECUTION_TIMEOUT` — the local limiter undercut the provider-aware upstream fetch-start timeouts. The surfaced 504 message now names `requestQueue.executionMaxWaitMs`; the error keeps the #4165 guarantees (disclaims an upstream timeout, preserves the Bottleneck error as `cause`, branded code + trusted provenance, classified request-scoped so combo falls back). A real queue-wait bound (the `Promise.race` around `limiter.schedule()` sketched in #9533) remains future work. (#12025) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **Call logs:** keep the `error` field when an artifact exceeds the storage cap, instead of replacing it with the omission marker. The error is the only field that says *why* a request failed and is typically ~90 bytes next to the multi-hundred-KB bodies that trip the cap, so dropping it left a size-limited row undiagnosable — a provider outage, a local timeout and an upstream 400 all rendered identically. It is now preserved at every fallback stage, truncated to 4KB if it is itself large ([#12026](https://github.com/diegosouzapw/OmniRoute/issues/12026)). ([#12095](https://github.com/diegosouzapw/OmniRoute/pull/12095)) — thanks @ntdat812 +- **fix(diagnostics):** preserve the error field (truncated to 4KB with a `[truncated: …]` suffix) in every call-log artifact size-limit fallback stage. Previously the minimal fallback replaced the error with `[omitted: call log artifact size limit exceeded]`, so an oversized artifact row showed nothing about WHY the request failed — e.g. 91 of 847 opencode-go 504 rows on one production instance were undiagnosable from the dashboard. Oversized request/response bodies are still omitted exactly as before; the error cap is independent of the payload sizes that tripped the fallback. (#12026) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **fix(sse):** OpenAI Responses clients that declare the native `web_search` tool now receive a spec-shaped `web_search_call` output item with `action.sources` alongside the preserved function-call round-trip, so search results executed through OmniRoute's own search backend are consumable by standard Responses clients (Codex, pi-web-access, …). ([#12031](https://github.com/diegosouzapw/OmniRoute/pull/12031)) — thanks @watchingdogs +- **fix(cli):** use in-thread alias resolver hooks on modern runtimes to avoid deprecation noise and improve Node.js forward compatibility ([#12073](https://github.com/diegosouzapw/OmniRoute/issues/12073)). ([#12083](https://github.com/diegosouzapw/OmniRoute/pull/12083)) — thanks @xiaoyaner0201 +- **fix(combo):** an operator-set **Agent Features → Context length** on a combo is now honored at request time. The value was persisted and advertised through `/v1/models`, but `resolveComboContextLimit()` never consulted it — so a multi-target combo whose members carry no per-model window fell through to the provider's generic `defaultContextLength` (openrouter 128000, command-code 200000) and rejected large requests with `Input exceeds context window … limit 128000` despite the combo being explicitly sized much larger. An identical single-target combo worked, because it collapses to its concrete target before the guard runs. Invalid values (0/negative/NaN/Infinity) are ignored, so the existing target → combo-min → fallback order is unchanged. ([#12090](https://github.com/diegosouzapw/OmniRoute/pull/12090)) — thanks @adivekar-utexas +- **fix(sse):** passthrough streams now estimate usage on finish when upstream closes without usage even with `stream_options.include_usage` — avoids `0 tokens / 0%` for providers that stay silent (and correctly handles trailing empty-choices usage) ([#12151](https://github.com/diegosouzapw/OmniRoute/pull/12151)) — thanks @maxmad64bis +- **fix(combos):** clearing an agent feature in the combos editor now persists — unchecking context cache protection, or emptying the system message or tool filter, sends an explicit `null` instead of dropping the field from the `PUT` body, which the update merge read as "leave unchanged" ([#12177](https://github.com/diegosouzapw/OmniRoute/pull/12177)) — thanks @foreveryh / @ntdatt812 +- **fix(memory):** self-hosted embedding endpoints now vectorize — the vector width is measured from the first embedding that comes back instead of being read from a registry that cannot describe them, so `vec_memories` is created and memories stop piling up unvectorized behind a green health check ([#12180](https://github.com/diegosouzapw/OmniRoute/pull/12180)) — thanks @kanade-hoshino / @ntdatt812 +- **fix(translator):** the leading `system` message now reaches Responses-API upstreams when its `content` is a content-part array — it was read as `typeof content === "string" ? content : ""`, so a prompt-caching client (Anthropic `cache_control`, the shape LiteLLM and the Anthropic SDK emit) had its entire system prompt replaced by an empty `instructions`. The request was still accepted with a normal `prompt_tokens` count, so the model answered with no instructions and nothing in the response said they were missing. Mid-conversation system turns already handled the array shape ([#7056](https://github.com/diegosouzapw/OmniRoute/pull/7056)); only the first one did not ([#12206](https://github.com/diegosouzapw/OmniRoute/issues/12206)). Regression guard: `tests/unit/translator-openai-responses-system-content-parts.test.ts`. ([#12207](https://github.com/diegosouzapw/OmniRoute/pull/12207)) — thanks @Gorillaz322 +- **fix(free-tier):** `/api/free-tier/summary` no longer computes its totals from a Radar feed built before the catalog the running release ships. When the cached feed is older — or carries no build date at all — the route answers from the shipped catalog, resolved through the operator's local model state so disabled and tombstoned models stay out of the numbers ([#12215](https://github.com/diegosouzapw/OmniRoute/pull/12215)). — thanks @maxmad64bis +- **fix(oauth):** Keep a Claude personal workspace and a Team organization as separate connections — they share the same email and `accountUUID`, so the email-only OAuth dedup let the second login overwrite the first account's tokens; `organizationUUID` now disambiguates them, the way `workspaceId` does for Codex ([#12222](https://github.com/diegosouzapw/OmniRoute/pull/12222)) — thanks @davidebaraldo +- **fix(resilience):** a 402 on a single paid model of a passthrough/gateway provider (e.g. `kilo-gateway`, `ollama-cloud`) no longer terminalizes the whole connection with a never-auto-recovered `credits_exhausted` status — only the paid model is locked out, so free models on the same key keep serving. 402 variant of [#3027](https://github.com/diegosouzapw/OmniRoute/issues/3027). Single-credential providers are unaffected — a 402 there is still treated as the key being genuinely out of credit ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239) / [#10616](https://github.com/diegosouzapw/OmniRoute/issues/10616)) ([#12242](https://github.com/diegosouzapw/OmniRoute/issues/12242)) — thanks @brick30llc-ctrl ([#12266](https://github.com/diegosouzapw/OmniRoute/pull/12266)) — thanks @geekyNads +- **fix(sse):** trust `finish_reason: "length"`/`"max_tokens"` over the reasoning-consumed-token ratio in response quality validation, so a reasoning model truncated below the old 90% threshold correctly fails and retries instead of returning empty content as a silent "success" ([#12262](https://github.com/diegosouzapw/OmniRoute/pull/12262)) — thanks @brick30llc-ctrl +- **fix(settings):** `PUT /api/settings/cache-config` now persists `alwaysPreserveClientCache` to the flat general settings the runtime cache-control policy actually reads; previously the value landed in the databaseSettings "cache" section and was silently ignored, so the endpoint had no effect on `cache_control` passthrough ([#12304](https://github.com/diegosouzapw/OmniRoute/pull/12304)) — thanks @davidebaraldo +- **fix(grok-cli):** treat omitted SuperGrokPro `creditUsagePercent` as 0% used so Provider Limits still renders a weekly bar (proto3 zero-elision) ([#12312](https://github.com/diegosouzapw/OmniRoute/pull/12312)) — thanks @HouMinXi +- **fix(combo):** Expose the two Auto-Combo scoring factors nobody could set — the combo validation schema and the dashboard weight sliders both declared 13 of the scorer's 15 factors, so `connectionDensity` (spreads load across a provider's connections) and `quality` were dropped on save and offered nowhere. The sliders also shipped their own default table that differed from the engine's on every non-zero factor and summed to 1.05, so the percentages shown next to them added up to 105%. Both lists now match `DEFAULT_WEIGHTS`, and a test keeps them there. Note that a combo whose stored `weights` omitted the two keys was effectively running with them at zero and the other thirteen renormalized upward; it now runs with the engine's intended distribution, so its routing does shift ([#12314](https://github.com/diegosouzapw/OmniRoute/pull/12314)) — thanks @maxmad64bis +- **fix(docs):** The free-tier reference no longer says its numbers come "confidence tagged per row" — no catalog entry carries a confidence tag and the API serves none, so every figure on that page is an estimate of the same, unstated quality. The page now states what an entry does vouch for: an independently documented hard stop (set by hand with the source in a comment, never defaulted to `true`) and a prompt-training disclosure, both with live counts the `check:docs-counts` gate keeps honest ([#12318](https://github.com/diegosouzapw/OmniRoute/pull/12318)) — thanks @maxmad64bis +- **fix(usage):** `adobe-firefly` and `firefly` have had a working usage fetcher since Adobe Firefly landed, but neither was ever added to the registration list, so the provider-plugin manifest, `genericQuotaFetcher` and the free-access quota cache all reported them as having no usage support — while `USAGE_SUPPORTED_PROVIDERS` said the opposite. Both are now declared, which also means their credit balance is fetched like any other declared provider's: `registerGenericQuotaFetchers` now registers a generic quota fetcher for them, and `resolveFreeAccessState` no longer returns early. A test holds the registration list to the dispatcher's switch in both directions, which is what the module's own docstring already asked for in prose ([#12321](https://github.com/diegosouzapw/OmniRoute/pull/12321)) — thanks @maxmad64bis +- **fix(quota):** drop the generic quota cache (agy / Antigravity / Claude OAuth) on an upstream 429 so reset-aware scoring does not keep a 60s stale snapshot, and force-refresh the next usage fetch so inner provider caches cannot recache the same window ([#12325](https://github.com/diegosouzapw/OmniRoute/pull/12325)) — thanks @HouMinXi +- **fix(combos):** deleting a combo now clears its persisted LKGP pins instead of leaving unreachable `key_value` rows behind ([#12326](https://github.com/diegosouzapw/OmniRoute/issues/12326)) ([#12425](https://github.com/diegosouzapw/OmniRoute/pull/12425)) — thanks @KrzysiekSko +- **fix(sse):** Keep ZWNJ (U+200C) and ZWJ (U+200D) in assistant text, reasoning and tool-call arguments — Persian/Kurdish half-space (`ارائه‌دهنده`), Arabic/Indic shaping and emoji sequences no longer lose them; the response de-obfuscation now removes joiners only between ASCII word characters, where the request side inserts them ([#12186](https://github.com/diegosouzapw/OmniRoute/issues/12186)) — thanks @rezjalibd ([#12359](https://github.com/diegosouzapw/OmniRoute/pull/12359)) — thanks @pacocartones +- **fix(resilience):** count resolved upstream 5xx results against the provider circuit breaker on the chat path — `CircuitBreaker.execute()` no longer reads a resolved `{ success: false, status: 5xx }` as a success that cancels the call-site failure, so a provider answering 503s now trips its breaker instead of staying `CLOSED` at `failureCount: 1`; single-model and combo dispatches are each accounted exactly once ([#12254](https://github.com/diegosouzapw/OmniRoute/issues/12254)) ([#12360](https://github.com/diegosouzapw/OmniRoute/pull/12360)) — thanks @pacocartones +- **fix(providers):** resolve the Codex quota auto-ping model from the live provider catalog and lifecycle registry instead of the retired `gpt-5.1-codex-mini`, and pause the ping with one actionable warning when no selectable Codex model exists rather than retrying a shut-down id every cooldown window ([#11905](https://github.com/diegosouzapw/OmniRoute/issues/11905)) ([#12361](https://github.com/diegosouzapw/OmniRoute/pull/12361)) — thanks @pacocartones +- **fix(api):** keep the `{created, data}` wrapper on combo-routed `/v1/images/generations` responses and default Codex image results to `b64_json` on both `/v1/images/generations` and `/v1/images/edits` so Codex CLI's built-in `image_gen` can decode them ([#12268](https://github.com/diegosouzapw/OmniRoute/issues/12268)) ([#12362](https://github.com/diegosouzapw/OmniRoute/pull/12362)) — thanks @pacocartones +- **fix(sse):** Name the shadowed custom provider node when a built-in provider id/alias (e.g. `openference` → `of`) reserves the prefix of an existing OpenAI/Anthropic-compatible node, so the runtime `No active credentials for provider: ` error explains that the prefix routed to the built-in and never reached the node's healthy connections, instead of contradicting the dashboard ([#11943](https://github.com/diegosouzapw/OmniRoute/issues/11943)) — thanks @morpheus9393 ([#12365](https://github.com/diegosouzapw/OmniRoute/pull/12365)) — thanks @pacocartones +- **fix(i18n):** wrap `ccOnboardingKeyPlaceholder` in ICU single quotes across all 43 locales so angle brackets render literally instead of being parsed as rich-text tags, which crashed the Claude Code onboarding block with `INVALID_MESSAGE: INVALID_TAG` ([#12302](https://github.com/diegosouzapw/OmniRoute/issues/12302)) ([#12369](https://github.com/diegosouzapw/OmniRoute/pull/12369)) — thanks @gonisulaimann +- **fix(models):** publish `effort_tiers` on Kimi K3's synced base-model entries (`kmca/k3`, `kmca/k3-256k`) so catalog-only clients (OpenCode, plain SDK pickers) can see and select the reasoning tiers (`low`/`high`/`max`) the synced metadata already carried — the `isSkippedEffortProvider` gate no longer suppresses tier visibility on those base entries, while synthetic `-` variant generation stays prevented and Codex/GLM base models remain excluded unchanged ([#12299](https://github.com/diegosouzapw/OmniRoute/issues/12299)) ([#12371](https://github.com/diegosouzapw/OmniRoute/pull/12371)) — thanks @gonisulaimann +- **fix(guardrails):** keep `auto`/`auto/*` virtual combos exempt from the Vision Bridge `fixedModel` credential guard so a combo target is passed through instead of silently falling back to global auto-selection ([#12237](https://github.com/diegosouzapw/OmniRoute/issues/12237)) ([#12373](https://github.com/diegosouzapw/OmniRoute/pull/12373)) — thanks @pacocartones +- **fix(combo):** capability-filter exhaustion caused by `max_tokens` above every target's known output limit now reports that reason (requested `max_tokens` vs the pool's highest known ceiling) instead of the unrelated "supports structured output" message ([#12229](https://github.com/diegosouzapw/OmniRoute/issues/12229)) — thanks @DW-MediaLab ([#12374](https://github.com/diegosouzapw/OmniRoute/pull/12374)) — thanks @pacocartones +- **fix(auth):** the `least-used` account strategy now prefers accounts without backoff before falling back to oldest `lastUsedAt`, the same tie-break `round-robin` already applies, so a failover no longer lands on a just-rate-limited account for a single request ([#12279](https://github.com/diegosouzapw/OmniRoute/issues/12279)) — thanks @tenshiak ([#12375](https://github.com/diegosouzapw/OmniRoute/pull/12375)) — thanks @pacocartones +- **fix(docker):** the `chatgpt-web-codex-browser` image now finds the Chrome binary under `chrome-linux64/` (Chrome for Testing layout in `playwright:v1.62.0-noble`) as well as the legacy `chrome-linux/`, so the container no longer crash-loops with `exec: --headless=new: not found` ([#12024](https://github.com/diegosouzapw/OmniRoute/issues/12024)) ([#12376](https://github.com/diegosouzapw/OmniRoute/pull/12376)) — thanks @pacocartones +- **fix(providers):** declare `groq/compound` and `allam-2-7b` as non-reasoning models in the curated Groq registry so `reasoning_effort` / `output_config.effort` / `thinking` from Claude Code are stripped instead of forwarded, which Groq rejected with HTTP 400 ([#12134](https://github.com/diegosouzapw/OmniRoute/issues/12134)) ([#12379](https://github.com/diegosouzapw/OmniRoute/pull/12379)) — thanks @pacocartones +- **fix(executors):** `OpencodeExecutor` no longer forces a direct connection when the connection has a proxy assigned in Proxy Management but no per-account proxies: the single-account fast path used to wrap the upstream dispatch in the direct-egress sentinel, discarding the ambient proxy context the chat handler had pinned from `proxy_assignments`, so API-key `opencode`/`opencode-go` connections egressed from the host IP (and hit geoblocks) despite the assignment. The direct pin is now applied only when no ambient proxy context exists ([#11894](https://github.com/diegosouzapw/OmniRoute/issues/11894) — thanks @hizzt) ([#12380](https://github.com/diegosouzapw/OmniRoute/pull/12380)) — thanks @pacocartones +- **fix(api):** `GET /v1/models` with `MODELS_CATALOG_PREFIX_MODE=canonical` (or `?prefix=canonical`) now lists providers whose registry alias is undefined or equal to their own id (Antigravity, Antigravity CLI and other self-aliased built-ins) — their single `provider/model` id was dropped by the alias/canonical duplicate guard in the static, synced, custom and alias-backed catalog loops ([#12058](https://github.com/diegosouzapw/OmniRoute/issues/12058)) — thanks @cheynetom ([#12381](https://github.com/diegosouzapw/OmniRoute/pull/12381)) — thanks @pacocartones +- **fix(translator):** Drop replayed `thinking` blocks that carry no signature (the shape produced from cross-provider `reasoning_content`) instead of stamping the default Claude signature on them, which Anthropic rejected with `400 Invalid signature in thinking block` on the next turn served by an Anthropic rung ([#12105](https://github.com/diegosouzapw/OmniRoute/issues/12105)) — thanks @atescivitci-cmd ([#12386](https://github.com/diegosouzapw/OmniRoute/pull/12386)) — thanks @pacocartones +- **fix(cli):** Resolve Bun's `--preload` polyfill path against the package root instead of `dist/`, so `omniroute` installed with `bun install -g` no longer crashes at startup with `error: preload not found …/dist/open-sse/utils/setupPolyfill.ts` ([#11980](https://github.com/diegosouzapw/OmniRoute/issues/11980)) — thanks @joglomedia ([#12387](https://github.com/diegosouzapw/OmniRoute/pull/12387)) — thanks @pacocartones +- **fix(providers):** `gemini-business` now publishes its model catalog — `/v1/models` and `/v1/providers/gemini-business/models` list the 12 enterprise Gemini ids the executor understands instead of returning an empty list (#12107) ([#12389](https://github.com/diegosouzapw/OmniRoute/pull/12389)) — thanks @pacocartones +- **fix(db):** install `busy_timeout` before the SQLite connection's first statement so a process opening the database while another one closes its WAL connection waits out the transient EXCLUSIVE lock instead of dying with `database is locked`, and recognise the drivers' real BUSY/PROTOCOL/IOERR errors as transient in the corruption probe so the same lock no longer renames the database away as corrupt; deflakes `cross-process contenders never both acquire the same connection` (#12394 — thanks @pacocartones) +- **fix(chat-admission):** derive the `chat_admission_busy` 503 `Retry-After` from observed heavyweight-lease occupancy — the larger of the exhausted `OMNIROUTE_CHAT_ADMISSION_QUEUE_MS` window and the time since capacity last turned over, capped at 60 s — instead of a fixed 1 s (structural) / 2 s (byte-stage) hint that invited Codex/agent fan-out clients to re-send ~1 MiB `/v1/responses` bodies every second into a gate held for the whole SSE lifetime; an idle gate keeps the historical floors ([#12135](https://github.com/diegosouzapw/OmniRoute/issues/12135)) (#12395 — thanks @pacocartones) +- **fix(api-manager):** the API key permissions modal no longer silently drops `allowedCombos` entries its Combo picker cannot render — routing-rule names such as `rt-*`, which the backend already honours — when "All" is clicked and the key is switched back to "Restrict"; those entries now survive the toggle, are listed read-only under the combo list so the count and the list agree, and are saved back verbatim instead of persisting `[]` (deny-all) (#12397 — thanks @pacocartones) +- **fix(catalog):** write the NUL separator of the catalog connection memo key, the provider serviceKind memo key, the Video Bridge promotion group key and a JSON-exactness test fixture as the `\u0000` escape instead of a raw byte — same runtime value, but the raw byte made git, GitHub and ripgrep treat those files as binary (hidden PR diffs, silently skipped searches); a guard test now keeps raw NUL bytes out of `src/`, `open-sse/` and `tests/` (#12403 — thanks @pacocartones) +- **fix(providers):** add `CLAUDE_CODE_CLIENT_VERSION` and `GITHUB_COPILOT_CLI_VERSION` env overrides so Anthropic/Copilot client-version gates can be unblocked without a rebuild ([#12417](https://github.com/diegosouzapw/OmniRoute/issues/12417)) ([#12632](https://github.com/diegosouzapw/OmniRoute/pull/12632)) — thanks @HouMinXi +- **fix(db):** back-fill `last_ping_at` and `last_pinged_reset_key` on `provider_connections` during schema reconciliation so divergent lineages that skipped `123_quota_auto_ping` still accept quota auto-ping writes ([#12470](https://github.com/diegosouzapw/OmniRoute/pull/12470) — thanks @KooshaPari) +- **fix(ci):** document MIT exceptions for `@eloqnt/{config,format-json,format-po}` (next-intl transitive; locked tarballs omit `license`) and keep the A2A lifecycle vitest off the real SQLite persistence seam ([#12581](https://github.com/diegosouzapw/OmniRoute/issues/12581)) ([#12595](https://github.com/diegosouzapw/OmniRoute/pull/12595)) +- OpenCode plugin `/v1/models` catalog fetch now waits 30s by default and attaches HTTP `statusCode` on 401/5xx so host fallback plugins can hop instead of seeing an untyped AbortError/UnknownError. ([#12607](https://github.com/diegosouzapw/OmniRoute/pull/12607)) — thanks @RaviTharuma +- **CI:** the OpenAPI security-tier gate now mirrors `isAlwaysProtectedPath()` in full — it also reads `ALWAYS_PROTECTED_API_PATTERNS`, so the pattern-gated credential routes (`/api/providers/{id}/{claude,codex}-auth/{export,apply-local}`, GHSA-5926-2w35-7h4q) no longer report as unannotated. (#12605) +- **fix(api):** GET /v1/models no longer waits forever on a hung coalesced catalog rebuild; cold-path waits are bounded (`CATALOG_BUILD_TIMEOUT_MS`, default 8s) and a last-good 200 is served when the rebuild times out ([#12627](https://github.com/diegosouzapw/OmniRoute/issues/12627)). ([#12628](https://github.com/diegosouzapw/OmniRoute/pull/12628)) — thanks @RaviTharuma +- **fix(cursor):** a non-array `tool_calls` on an assistant message no longer crashes the cursor request translator with a `TypeError`; both loops now require an array ([#12689](https://github.com/diegosouzapw/OmniRoute/issues/12689)) ([#12691](https://github.com/diegosouzapw/OmniRoute/pull/12691)) — thanks @soroush5 +- **fix(chat):** Chat Completions no longer return empty `content` after a server-owned memory or skills tool; the first provider send and account/model recovery now share one pipeline so a follow-up round-trip can fill the reply ([#12696](https://github.com/diegosouzapw/OmniRoute/issues/12696)) — thanks @HouMinXi ([#12867](https://github.com/diegosouzapw/OmniRoute/pull/12867)) +- **fix(dashboard):** batch-deleting provider keys no longer toasts failure after a successful delete when the confirm button's click event is forwarded as `onAfter` ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) — thanks @HouMinXi +- **fix(glm):** drop the extra 16th argument to `createSSETransformStreamWithLogger` that TypeScript rejected (TS2554) and that never reached the TransformStream ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) ([#12770](https://github.com/diegosouzapw/OmniRoute/pull/12770)) — thanks @HouMinXi +- **fix(dashboard):** pass `nodeMap` into Runtime `QuotaGroup` so a quota monitor in error/exhausted/alerting no longer throws `ReferenceError: nodeMap is not defined`. ([#12868](https://github.com/diegosouzapw/OmniRoute/pull/12868)) — thanks @HouMinXi +- Restricted API keys whose `allowedModels` lists a combo name no longer skip every combo member at pre-dispatch (`ALL_TARGETS_SKIPPED`). Inner-target filtering still applies when the allow-list is a provider prefix or `disableNonPublicModels` is on ([#12899](https://github.com/diegosouzapw/OmniRoute/pull/12899)). — thanks @HouMinXi +- **fix(combos):** Effort-suffixed combo members inherit the base model's `model_context_overrides` row so priority order is not inverted on large requests ([#12926](https://github.com/diegosouzapw/OmniRoute/pull/12926)) — thanks @HouMinXi +- **fix(catalog):** Picker-added `customModels` enter the dispatch-time live catalog so combo members and bare inference no longer 400 ([#12934](https://github.com/diegosouzapw/OmniRoute/pull/12934)) — thanks @HouMinXi +- **fix(docker):** re-derive the Docker build's worker-pool memory budget from the MEASURED ~4.5 GB per-process RSS (the issue owner's own VPS dmesg OOM-killer reproduction) instead of the stale 2560 MB/worker inference #11419 shipped, and lower `OMNIROUTE_BUILD_WORKERS` 3 → 2 so 1 parent + 1 page-data worker (2 processes × 4.5 GB = 9 GB) fits the 12.288 GB (75%) budget on a 16 GB GitHub Actions runner — the previous default (1 parent + 2 workers = 13.5 GB) still overcommitted the runner and kept "Publish to Docker Hub" failing with `cannot allocate memory` after #11419 merged (#7518). ([#11663](https://github.com/diegosouzapw/OmniRoute/pull/11663)) +- **fix(electron):** the packaged Windows desktop build now passes the #7592 cold-restart smoke end to end. Five defects were found and fixed on the way: (1) optional-pack staging failed on any machine whose `tar` is GNU tar — it reads the drive letter in an absolute `-f C:\...` archive path as a remote rsh host (`Cannot connect to C:`), so staging now passes a bare filename with `cwd` at the tarball directory and surfaces tar stderr on failure; (2) the Electron `files` allowlist was missing `lib/loginHeaderCapture.js`, so the packaged main process crashed at startup with `Cannot find module './lib/loginHeaderCapture'`; (3) electron-builder ≥26 injects an `!**/node_modules/**` ignore into every extraResources pattern list that no positive filter can override, which silently dropped the staged runtime `node_modules` (including the better-sqlite3 N-API prebuild) from `resources/app` — a fresh v3.8.50 desktop build would have shipped with no native SQLite driver and reproduced the #7592 sql.js fallback on every machine; an `afterPack` hook now restores the staged `node_modules` after packing; (4) the packaged-app smoke harness redirected `USERPROFILE` into its temp DATA_DIR, but Electron resolves the Roaming profile from `%USERPROFILE%\AppData\Roaming\` (USERPROFILE takes precedence over the APPDATA env var) and the path service throws instead of creating it, so `requestSingleInstanceLock()` returned false and the app exited(0) silently before `app.whenReady()` — the harness now pre-creates the derived tree, and `ensureSmokeEnvDirs` is exported and unit-tested; (5) the #7592 driver assertion parsed a `[DB] Driver: ...` line that the server's primary DB path never printed (only the unused `openDatabaseAsync()` did) — `getDbInstance()` now emits the same line on open so the guard can actually assert the native driver. Also: the smoke env-allowlist unit test hardcoded POSIX paths and could only pass on Linux/macOS; expectations are now host-agnostic, with new regression tests for the USERPROFILE-derived tree and `tarPack` under absolute Windows-style paths ([#11443](https://github.com/diegosouzapw/OmniRoute/pull/11443)) — thanks @jonlwheat2-gif +- fix(sse): stop the auto-combo candidates inspector from silently dropping model-locked/cooled-down rows (#9133) ([#11994](https://github.com/diegosouzapw/OmniRoute/pull/11994)) — thanks @hartmark +- **fix(providers):** Perplexity Web no longer turns upstream stream failures into successful assistant text; pre-content failures remain eligible for fallback, partial output ends with a structured sanitized error, and failed sessions are not persisted ([#12465](https://github.com/diegosouzapw/OmniRoute/pull/12465)) +- **fix(video-bridge):** preserve chronological video and transcript fusion, source-frame timestamps, transcript provenance, full contact-sheet intervals, and pre-abort callback isolation ([#11681](https://github.com/diegosouzapw/OmniRoute/pull/11681)) +- **Z.ai Web:** HTTP 200 streams carrying an upstream error now terminate with a structured failure instead of assistant text plus a normal stop, preserving partial output while allowing pre-content combo fallback. ([#12454](https://github.com/diegosouzapw/OmniRoute/pull/12454)) +- Absorb `Error [AbortError]: request_signal_aborted` and DOMException AbortError shapes in the process-level client-abort crash guard so routine client disconnects no longer kill the server (exit code 7). ([#12165](https://github.com/diegosouzapw/OmniRoute/pull/12165)) — thanks @alvinveroy +- **fix(sse):** Treat Adapta Web `type:error` SSE events as sanitized non-stream failures instead of empty HTTP 200 completions. ([#12459](https://github.com/diegosouzapw/OmniRoute/pull/12459)) +- **fix(autoCombo):** layer 3 (`models_dev_tier`) no longer scores retired models as premium, merge cross-provider capability rows deterministically, and inherit tier scores through variant ids ([#11508](https://github.com/diegosouzapw/OmniRoute/issues/11508)). `getModelsDevTierFitness` now vetoes ids present in `config/quality/model-lifecycle.json` with status `retired` before any other signal; `loadModelCapabilities()` aggregates per `model_id` (any-non-null-true for booleans, max for `limit_context`) instead of last-write-wins over SQLite's undefined row order; a capabilities miss retries through `resolveScoresAs()`'s base id and reports `models_dev_tier:inherited`, matching layers 1–2 (#11492). Adds `getModelsDevTierFitnessWithSource()` and an `invalidateCapabilitiesCache()` test/ops hook. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) — thanks @Meet6338-X +- fix(sse): accept Claude `content:[]` + `stop_reason: max_tokens`/`tool_use` in `detectMalformedNonStream` (match `isEmptyContentResponse`) so Claude Code `/model` probes with `max_tokens: 1` no longer become a false 502 ([#11568](https://github.com/diegosouzapw/OmniRoute/pull/11568)) — thanks @Seramicx +- **fix(security):** sanitize `request.failed` diagnostics before publishing them to live dashboard listeners and replay history, while keeping status, model, provider, latency, and internal call-log diagnostics intact. ([#12469](https://github.com/diegosouzapw/OmniRoute/pull/12469)) +- **fix(pwa):** do not intercept dashboard navigations so Chrome can fall back from a stale HTTP/3 Alt-Svc advertisement (UDP :20128 is unpublished; F5 on a long-lived tab hung until a new tab opened a fresh TCP connection). ([#12767](https://github.com/diegosouzapw/OmniRoute/pull/12767)) — thanks @HouMinXi +- **fix(executors):** handle DuckDuckGo ERR_BN_LIMIT (418) without retrying — when the upstream returns `418 ERR_BN_LIMIT` (rate-limit/ban), the executor now returns the error immediately instead of burning another VQD acquisition that would only count against the IP limit. The retry logic for `418 ERR_CHALLENGE` (unsolved challenge) remains unchanged. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) ([#12110](https://github.com/diegosouzapw/OmniRoute/pull/12110)) — thanks @oyi77 / @Meet6338-X +- Fixed DuckDuckGo streaming responses losing JSON lines and UTF-8 characters split across network chunks. ([#11528](https://github.com/diegosouzapw/OmniRoute/pull/11528)) — thanks @pacocartones +- Harden generated Cloudflare, Vercel, and Deno relays by stripping hop-by-hop, framing, and proxy authentication headers before forwarding requests upstream. ([#11533](https://github.com/diegosouzapw/OmniRoute/pull/11533)) — thanks @pacocartones +- **feat(grok-cli):** Provider Limits shows grok-cli banked reset credits from `GetRemainingResets` (including a real zero; a failed RPC omits the row) and the existing View credits button now calls `ConsumerUiSvc/RedeemReset` for grok-cli. Live tokens use nested fields 10/20/30 (id + Timestamp), not compact 1/2/3. ([#12805](https://github.com/diegosouzapw/OmniRoute/pull/12805)) — thanks @HouMinXi +- **fix(memory):** Embedding Model Quick select, Embedding Source remote dropdown, and Rerank selector now list every configured provider with embedding/rerank support instead of only chat-catalog text matches plus OpenRouter live discovery; a generic OpenAI-compatible `/embeddings` + Cohere-compatible `/rerank` runtime fallback resolves any configured chat provider's embedding/rerank endpoint, so unlisted providers no longer fail with "Unknown embedding provider"; both memory selectors gained a free-text model override ([#11390](https://github.com/diegosouzapw/OmniRoute/pull/11390)) — thanks @rqzbeh +- Harden SQLite upgrades around the historical migration-074 version collision: missing discovery and inspector tables are replayed atomically, pre-existing databases (including setup-created skeletons) receive reusable content-addressed safety snapshots, and Node test/eval probes without `DATA_DIR` are isolated from the operator database. ([#12435](https://github.com/diegosouzapw/OmniRoute/pull/12435)) +- **fix(dashboard):** Moonshot/Kimi Open Platform voucher and cash leftover percentages follow the bucket balance, so an empty wallet no longer paints those rows as 100% while Available is 0% ([#12733](https://github.com/diegosouzapw/OmniRoute/pull/12733)) — thanks @HouMinXi +- **fix(combo):** return non-retryable HTTP 400 when all candidates for a pinned native Codex turn are unavailable due to model-scoped lockout, terminating the turn cleanly while preserving turn continuity and enabling standard Combo routing on subsequent turns ([#12240](https://github.com/diegosouzapw/OmniRoute/pull/12240)) — thanks @mdigitalbh81 +- Fixed auto-router strategies preserving the selected connection through ranking and dispatch when multiple connections share the same provider and model. ([#11530](https://github.com/diegosouzapw/OmniRoute/pull/11530)) — thanks @pacocartones +- Preserve Chat Completions JSON-object response formats when translating requests to the Responses API. ([#11529](https://github.com/diegosouzapw/OmniRoute/pull/11529)) — thanks @pacocartones +- Fix idempotency fingerprints so Responses API requests include `input` and other semantic generation fields using stable serialization, while excluding credentials and request metadata. ([#11532](https://github.com/diegosouzapw/OmniRoute/pull/11532)) — thanks @pacocartones +- **fix(api):** Generated API CLI commands now enforce required OpenAPI request bodies; Combo test commands forward the required `comboName` body, while API keys created by older writers after migration 149 preserve legacy allow-all Combo access without widening explicit empty allowlists — thanks @marcelokarval ([#12070](https://github.com/diegosouzapw/OmniRoute/pull/12070)) +- **fix(grok-web):** treat upstream streaming failures as failures instead of successful assistant text: error-only streams now fail readiness with HTTP 502, while failures after legitimate content preserve that partial output and terminate through the sanitized stream failure path without a normal `stop` completion. ([#12458](https://github.com/diegosouzapw/OmniRoute/pull/12458)) +- HuggingChat now turns HTTP 200 JSONL generation failures into a sanitized 502 before content, or a fixed public stream failure after partial output, so fallback and request persistence no longer record a false successful stop. ([#12456](https://github.com/diegosouzapw/OmniRoute/pull/12456)) +- **fix(providers):** keep 1min.ai HTTP 200 stream errors out of assistant content, preserve partial output, and expose sanitized terminal errors so pre-content failures can fall back. ([#12466](https://github.com/diegosouzapw/OmniRoute/pull/12466)) +- **fix(sessions):** preserve legacy Sessions data when the additive exclusive-session projection is unavailable, returning an empty projection and warning only once per contiguous outage instead of failing the endpoint; the Sessions badge intentionally reflects the merged legacy and exclusive row count introduced by [#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389) ([#11469](https://github.com/diegosouzapw/OmniRoute/pull/11469)) — thanks @KaspaPulse +- Render Nimble Search with the generic provider icon and serve Opper's proven logo locally. ([#12415](https://github.com/diegosouzapw/OmniRoute/pull/12415)) +- **fix(quality):** `config/quality/provider-assets-provenance.jsonl` was stale against the physical `public/providers/` tree (still listing 225 assets and 5 duplicate-content groups after several provider-retirement PRs removed 85 icon files), which would have made `check:provider-asset-provenance` fail on the next full run. Reconciled the manifest to the real 142-file snapshot (142 asset records, 1 duplicate-content group, 2 newly-registered files marked `unresolved` pending provenance research), repinned `auditedCommit`, and updated the regression test's hardcoded counts to match. ([#11876](https://github.com/diegosouzapw/OmniRoute/pull/11876)) +- fix(ui): wrap quota card footer action buttons to prevent clipping Refresh button on narrow cards — when extra action buttons such as `View credits` are present on Codex cards, allow the footer row to flex-wrap with `shrink-0` buttons so `Refresh now` remains visible and clickable across all responsive card widths ([#11464](https://github.com/diegosouzapw/OmniRoute/pull/11464)) — thanks @adevwithpurpose +- Keep Antigravity Gemini usable when the same connection's Claude weekly quota is empty; generic quota cache stays per-connection for every other provider. ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) — thanks @HouMinXi +- Fixed Tailscale tunnel setup to reject ports outside the valid 1-65535 range and safely install Windows MSI packages from paths containing apostrophes. ([#11534](https://github.com/diegosouzapw/OmniRoute/pull/11534)) — thanks @pacocartones +- Classify Cloudflared and Tailscale process-management routes as local-only and spawn-capable while preserving remote access to read-only tunnel status endpoints. ([#11531](https://github.com/diegosouzapw/OmniRoute/pull/11531)) — thanks @pacocartones +- Electron release workflow: the `publish-npm` job now grants `actions: read` to the reusable `npm-publish.yml` it calls (its `publish` job requests it), which is what made GitHub refuse the whole v3.8.50 run at startup and ship the release with zero desktop assets; a `workflow_dispatch` now builds the requested tag instead of the dispatching branch and can skip the npm leg (`publish_npm=false`) when only re-attaching assets ([#11974](https://github.com/diegosouzapw/OmniRoute/pull/11974)) +- **fix(dashboard):** The Combos page usage guide now reads its dismissal through `useSyncExternalStore` instead of correcting SSR state inside an effect, removing an extra commit of the page tree on every load (and the `react-hooks/set-state-in-effect` error it raised). ([#12671](https://github.com/diegosouzapw/OmniRoute/pull/12671)) +- Electron release: `electron/package-lock.json` regained the optional `electron-builder-squirrel-windows` subtree (13 entries) that `npm ci` had been refusing as out of sync, `electron-release.yml` gained a `build_ref` dispatch input and stops regenerating release notes on a re-attach dispatch, and the npm publish workflow attaches the SBOM to the GitHub Release on dispatch publishes too — so the v3.8.51 tag ships every desktop asset and the SBOM like v3.8.49 did ([#12022](https://github.com/diegosouzapw/OmniRoute/pull/12022)) +- Drained the reds every PR against `release/v3.8.51` was born with: documented the three service-worker build-id variables, registered the six retirement/tunnel tests with the mutation gate, approved `eslint-plugin-react-hooks` in the dependency allowlist, added the six `combo.sort.*` strings to `vi` and `pt-BR`, pointed the ChatGPT Web doc at the real migration-168 test, worded the g4f hint around the member key, and realigned four tests to the retired-provider catalog and the legacy-schema fixtures the retirement migrations touch. ([#11940](https://github.com/diegosouzapw/OmniRoute/pull/11940)) +- **fix(providers):** Claude, Grok, LMArena, Notion, and Perplexity web-cookie transports now use pooled `wreq-js` 3.2 instead of the native sidecar, with all nine supported bindings pinned and audited, and the applicable platform binding plus native-license evidence included in each release artifact ([#12429](https://github.com/diegosouzapw/OmniRoute/pull/12429), supersedes [#11753](https://github.com/diegosouzapw/OmniRoute/pull/11753)). +- **fix(providers):** Zed Hosted streaming failures now trigger fallback before content and end partial streams with a sanitized structured error instead of fake assistant text and a normal-success stop. ([#12455](https://github.com/diegosouzapw/OmniRoute/pull/12455)) +- **fix(test):** capture hook result after render (direct commit `b85d0abb0b`) +- **fix(sync):** repair the two regressions the v3.8.50 sync-back left on release/v3.8.51 (direct commit `5ade9e0851`) +- **revert(models):** drop the memo reset hook ported from #10055 (direct commit `282de841ae`) +- **fix(typecheck):** guard the optional connectionId before the reactive model sync (direct commit `f2825bad75`) +- **fix(quota-share):** release the winner's reserved in-flight slot (#11371) ([#11408](https://github.com/diegosouzapw/OmniRoute/pull/11408)) — thanks @oyi77 +- **fix(providers):** stop silently dropping reasoning effort on opencode families (#10788) ([#11409](https://github.com/diegosouzapw/OmniRoute/pull/11409)) — thanks @oyi77 +- **fix(combo):** enable genuine quota-aware routing for generic providers (antigravity, claude, etc.) ([#11411](https://github.com/diegosouzapw/OmniRoute/pull/11411)) — thanks @HouMinXi +- **fix(oauth):** allow expired token retry before deactivation and activate expired retry loop (#P0) ([#11414](https://github.com/diegosouzapw/OmniRoute/pull/11414)) — thanks @HouMinXi +- **perf(executors):** lazy-load the executor registry — defer class imports + construction to first use (#11220) ([#11421](https://github.com/diegosouzapw/OmniRoute/pull/11421)) — thanks @oyi77 +- **fix:** make release/v3.8.50 CI gates green (run 32786966560 root causes) ([#11450](https://github.com/diegosouzapw/OmniRoute/pull/11450)) — thanks @jonlwheat2-gif +- **fix(sse):** exact-domain cookie match and origin-equality URL assertions ([#11452](https://github.com/diegosouzapw/OmniRoute/pull/11452)) — thanks @hartmark +- **fix(providers):** send Responses API payload during OpenAI-compatible validation ([#11454](https://github.com/diegosouzapw/OmniRoute/pull/11454)) — thanks @tuandinh0801 +- **fix(command-code):** fallback to /alpha/generate for Go plan without Provider API access ([#11455](https://github.com/diegosouzapw/OmniRoute/pull/11455)) — thanks @Chewji9875 +- **fix(responses-continuation):** fail closed on a log-truncated stored input/output array ([#11473](https://github.com/diegosouzapw/OmniRoute/pull/11473)) — thanks @hartmark +- **fix(cli):** write jcode settings as [providers.omniroute] in config.toml ([#11484](https://github.com/diegosouzapw/OmniRoute/pull/11484)) — thanks @hongnoul +- **fix(logging):** raise chat-log array truncation cap to 1000 and unify duplicate implementations ([#11499](https://github.com/diegosouzapw/OmniRoute/pull/11499)) — thanks @hartmark +- **fix:** restore live websocket URL helpers ([#11509](https://github.com/diegosouzapw/OmniRoute/pull/11509)) — thanks @PauloFH +- **perf(core):** eliminate async waterfalls across home, cache, and db operations (#11396) ([#11512](https://github.com/diegosouzapw/OmniRoute/pull/11512)) — thanks @HouMinXi +- **fix(kiro):** add runtime.us-east-1.kiro.dev as first-attempt endpoint ([#11517](https://github.com/diegosouzapw/OmniRoute/pull/11517)) — thanks @rifqiawl +- **fix(providers):** remove duplicate 128-tool truncation in OpencodeExecutor (#11444) ([#11518](https://github.com/diegosouzapw/OmniRoute/pull/11518)) — thanks @HouMinXi +- **fix(providers):** poll onboardUser LRO before classifying BYOP ([#11519](https://github.com/diegosouzapw/OmniRoute/pull/11519)) — thanks @rifqiawl +- **fix(translator):** preserve exact tool name casing for Claude tool_use payloads (#11487) ([#11520](https://github.com/diegosouzapw/OmniRoute/pull/11520)) — thanks @HouMinXi +- **fix(kimi-web):** align health probe with executor domain ([#11521](https://github.com/diegosouzapw/OmniRoute/pull/11521)) — thanks @pacocartones +- **fix(cli):** preserve existing admin password during setup ([#11522](https://github.com/diegosouzapw/OmniRoute/pull/11522)) — thanks @pacocartones +- **fix(mitm):** keep Windows DNS elevation off the POSIX sudo path (#11430) ([#11542](https://github.com/diegosouzapw/OmniRoute/pull/11542)) — thanks @official-burak +- **fix(sse):** scale chat admission by ingest byte budget instead of a fixed request count ([#11548](https://github.com/diegosouzapw/OmniRoute/pull/11548)) — thanks @TheDemonTuan +- **fix:** accept Gemini Web cookie exports ([#11554](https://github.com/diegosouzapw/OmniRoute/pull/11554)) — thanks @rafacpti23 +- **fix(i18n):** complete Brazilian Portuguese translations ([#11558](https://github.com/diegosouzapw/OmniRoute/pull/11558)) — thanks @rafacpti23 +- **fix(sse):** explicit types for openai-responses pureHelpers — clears last failing core typecheck gate ([#11567](https://github.com/diegosouzapw/OmniRoute/pull/11567)) — thanks @jonlwheat2-gif +- **fix(opencode):** hydrate Proxy Pool references for no-auth connections ([#11584](https://github.com/diegosouzapw/OmniRoute/pull/11584)) — thanks @AStupidBear +- **fix(ci):** resolve inherited release/v3.8.51 base-red CI failures ([#11608](https://github.com/diegosouzapw/OmniRoute/pull/11608)) — thanks @jonlwheat2-gif +- **fix(db):** fallback load STORAGE_ENCRYPTION_KEY from env files ([#11614](https://github.com/diegosouzapw/OmniRoute/pull/11614)) — thanks @benzntech +- **fix(providers):** mark Brave Search as no longer having a free tier ([#11621](https://github.com/diegosouzapw/OmniRoute/pull/11621)) — thanks @turbolego +- **fix:** pass tool result content through as string in OpenAI/Claude -> Gemini translation ([#11624](https://github.com/diegosouzapw/OmniRoute/pull/11624)) — thanks @Hsia97 +- **fix(sse):** scope credential health to exact key ([#11628](https://github.com/diegosouzapw/OmniRoute/pull/11628)) — thanks @KaspaPulse +- **fix(install):** guard prepare script against missing husky ([#11634](https://github.com/diegosouzapw/OmniRoute/pull/11634)) — thanks @Prajeeth-12 +- **fix(security):** redact NVIDIA_API_KEY at the log sink in the ad-hoc diag script ([#11639](https://github.com/diegosouzapw/OmniRoute/pull/11639)) +- **fix(types):** clear the two type regressions red on release/v3.8.51 ([#11640](https://github.com/diegosouzapw/OmniRoute/pull/11640)) — thanks @jonlwheat2-gif +- **fix(build):** resolve workspace: protocol dependencies to concrete semver versions during prepublish (#11540) ([#11641](https://github.com/diegosouzapw/OmniRoute/pull/11641)) — thanks @HouMinXi +- **fix(search):** properly resolve configured search connection in /v1/responses pipeline (#11524) ([#11642](https://github.com/diegosouzapw/OmniRoute/pull/11642)) — thanks @HouMinXi +- **fix(authz):** trust cf-connecting-ip for via-proxy detection when peer is Cloudflare edge (#11514) ([#11643](https://github.com/diegosouzapw/OmniRoute/pull/11643)) — thanks @HouMinXi +- **fix(security):** compare the OIDC state cookie in constant time ([#11646](https://github.com/diegosouzapw/OmniRoute/pull/11646)) +- **fix(security):** namespace the dedup hash by the calling API key ([#11649](https://github.com/diegosouzapw/OmniRoute/pull/11649)) +- **fix(models):** gate the Codex-native catalog loop on prefix mode (#11632) ([#11662](https://github.com/diegosouzapw/OmniRoute/pull/11662)) — thanks @xiaoyaner0201 +- **fix(dashboard):** project Codex routing quota in account pools ([#11666](https://github.com/diegosouzapw/OmniRoute/pull/11666)) — thanks @KaspaPulse +- **fix(ddgw):** normaliza system no upstream da release v3.8.51 ([#11692](https://github.com/diegosouzapw/OmniRoute/pull/11692)) — thanks @RhianB14 +- **fix(memory):** honor category filter in GET /api/memory ([#11699](https://github.com/diegosouzapw/OmniRoute/pull/11699)) — thanks @Sabeekhann +- **fix(ci):** detect stale workspace lockfile entries ([#11714](https://github.com/diegosouzapw/OmniRoute/pull/11714)) — thanks @pacocartones +- **fix(ci):** stamp dist/BUILD_SHA before the npm publish provenance gate ([#11721](https://github.com/diegosouzapw/OmniRoute/pull/11721)) +- **fix(docker):** decouple the best-effort Bun image from the release manifest ([#11724](https://github.com/diegosouzapw/OmniRoute/pull/11724)) +- **perf(compression):** avoid cloning memo misses twice ([#11727](https://github.com/diegosouzapw/OmniRoute/pull/11727)) — thanks @RaviTharuma +- **fix(docker):** let the bun digest artifact be absent, not fatal ([#11740](https://github.com/diegosouzapw/OmniRoute/pull/11740)) +- **fix(shared):** block cloud-metadata hosts under default remote-image guard ([#11755](https://github.com/diegosouzapw/OmniRoute/pull/11755)) — thanks @santosraju99-hub +- **fix(api):** preserve caller-provided X-Correlation-Id on chat completions ([#11760](https://github.com/diegosouzapw/OmniRoute/pull/11760)) — thanks @aniruddhaadak80 +- **fix(resilience):** lock permanently retired models instead of short backoff (Gemini ban prevention) ([#11762](https://github.com/diegosouzapw/OmniRoute/pull/11762)) — thanks @turbolego +- **fix:** mark Vercel AI Gateway as passthroughModels ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **fix(resilience):** stop hammering permanently-moved endpoints and billing-suspended accounts ([#11774](https://github.com/diegosouzapw/OmniRoute/pull/11774)) — thanks @turbolego +- **fix(lease):** remove global static reservation and gate routing on live active lease occupancy ([#11775](https://github.com/diegosouzapw/OmniRoute/pull/11775)) — thanks @TheDemonTuan +- **fix(ci):** size the install-upgrade gate to a measured run, and log the pack cost ([#11776](https://github.com/diegosouzapw/OmniRoute/pull/11776)) +- **fix(pwa):** stop serving the stale shell after deploys ([#11779](https://github.com/diegosouzapw/OmniRoute/pull/11779)) — thanks @HouMinXi +- **fix(combo):** lock GitHub models rejected as "not supported" for future requests ([#11781](https://github.com/diegosouzapw/OmniRoute/pull/11781)) — thanks @turbolego +- **fix(cli):** probe both IPv4 and IPv6 loopback for server readiness (#11766) ([#11794](https://github.com/diegosouzapw/OmniRoute/pull/11794)) — thanks @vermasomesh835 +- **fix(sse):** preserve CLIProxyAPI wrappers per connection ([#11797](https://github.com/diegosouzapw/OmniRoute/pull/11797)) — thanks @RaviTharuma +- **fix(providers):** honor model auto-fetch opt-in on create ([#11798](https://github.com/diegosouzapw/OmniRoute/pull/11798)) — thanks @RaviTharuma +- **fix(dashboard):** make model auto-fetch order independent ([#11805](https://github.com/diegosouzapw/OmniRoute/pull/11805)) — thanks @RaviTharuma +- **fix(docker):** make GHA cache exports best effort ([#11806](https://github.com/diegosouzapw/OmniRoute/pull/11806)) — thanks @RaviTharuma +- **fix(kiro):** do not permanently ban on 'User is not authorized to make this call' ([#11809](https://github.com/diegosouzapw/OmniRoute/pull/11809)) — thanks @Deftera186 +- **fix(sse):** keep cache-write tokens in OpenAI-shaped usage ([#11814](https://github.com/diegosouzapw/OmniRoute/pull/11814)) — thanks @TheDemonTuan +- **fix(docs):** resolve relative markdown and wiki links across Fumadocs and GitHub wiki ([#11834](https://github.com/diegosouzapw/OmniRoute/pull/11834)) — thanks @AndrianBalanescu +- **fix(chat):** preserve unstripped model string for passthrough provider routing ([#11840](https://github.com/diegosouzapw/OmniRoute/pull/11840)) — thanks @raheemuddin786 +- **fix(mcp):** avoid async initialization inside sync esm wrappers (#11569) ([#11849](https://github.com/diegosouzapw/OmniRoute/pull/11849)) — thanks @HouMinXi +- **fix(sse):** preserve coherent markdown token boundaries across stream translation chunks (#11606) ([#11851](https://github.com/diegosouzapw/OmniRoute/pull/11851)) — thanks @HouMinXi +- **fix(ci):** run the install-upgrade gate on disk, not on the /tmp tmpfs ([#11855](https://github.com/diegosouzapw/OmniRoute/pull/11855)) +- **fix(skills):** expand shorthand property types in injected tool schemas ([#11857](https://github.com/diegosouzapw/OmniRoute/pull/11857)) — thanks @alvinveroy +- **fix(ci):** publish npm from a hosted runner so provenance is accepted ([#11877](https://github.com/diegosouzapw/OmniRoute/pull/11877)) +- **fix(lint):** pin react hooks plugin to 7.0.1 ([#11886](https://github.com/diegosouzapw/OmniRoute/pull/11886)) +- **fix(ci):** surface and clear release-green ESLint failure ([#11890](https://github.com/diegosouzapw/OmniRoute/pull/11890)) — thanks @MumuTW +- **fix(guardrails):** prevent duplicate prompt-injection-guard log output ([#11936](https://github.com/diegosouzapw/OmniRoute/pull/11936)) — thanks @patrykkopycinski +- **fix(auth):** downgrade expected transient states from warn to debug ([#11937](https://github.com/diegosouzapw/OmniRoute/pull/11937)) — thanks @patrykkopycinski +- **fix(codex):** fail over image generation for imported free plans ([#11948](https://github.com/diegosouzapw/OmniRoute/pull/11948)) — thanks @Bl0ck154 +- **fix(codex):** normalize non-stream responses ([#11951](https://github.com/diegosouzapw/OmniRoute/pull/11951)) — thanks @Bl0ck154 +- **fix(images):** forward Antigravity image size ([#11952](https://github.com/diegosouzapw/OmniRoute/pull/11952)) — thanks @Bl0ck154 +- **fix(db):** invalidate connection cache after upsert ([#11953](https://github.com/diegosouzapw/OmniRoute/pull/11953)) — thanks @Bl0ck154 +- **fix(codex):** restore imported account state ([#11954](https://github.com/diegosouzapw/OmniRoute/pull/11954)) — thanks @Bl0ck154 +- **fix(sse):** give extended-thinking targets the reasoning readiness budget ([#11959](https://github.com/diegosouzapw/OmniRoute/pull/11959)) — thanks @abhisheksharma2411 +- **fix(dashboard):** use opaque background and readable text color on cost chart tooltips ([#11960](https://github.com/diegosouzapw/OmniRoute/pull/11960)) — thanks @ZaimMarzuki +- **fix(antigravity):** send complete loadCodeAssist metadata (ideType/platform/pluginType as numeric enums) ([#11969](https://github.com/diegosouzapw/OmniRoute/pull/11969)) — thanks @rifqiawl +- **fix(core):** resolve DB init race condition and reasoning translation ([#12003](https://github.com/diegosouzapw/OmniRoute/pull/12003)) — thanks @raheemuddin786 +- **fix(admission):** exclude reclaimable page cache from the cgroup pressure ratio ([#12017](https://github.com/diegosouzapw/OmniRoute/pull/12017)) — thanks @HouMinXi +- **fix(build):** prune non-production trees in NFT trace excludes and tsconfig ([#12028](https://github.com/diegosouzapw/OmniRoute/pull/12028)) — thanks @Chewji9875 +- **fix(migrations):** add renamed migration compatibility for 056/073/077/101 ([#12036](https://github.com/diegosouzapw/OmniRoute/pull/12036)) — thanks @oyi77 +- **fix(sse):** default crash-guard logger to console.warn, not console ([#12042](https://github.com/diegosouzapw/OmniRoute/pull/12042)) — thanks @HouMinXi +- **fix(catalog):** derive vision/modalities for built-in auto combos from effective target pool ([#12046](https://github.com/diegosouzapw/OmniRoute/pull/12046)) — thanks @Prajeeth-12 +- **fix(release):** the packaged-app smoke verifies the database opened, not a driver line the primary path never prints (twin of #12032) ([#12047](https://github.com/diegosouzapw/OmniRoute/pull/12047)) +- **fix(barrel):** migrate src/app/ off the @/lib/localDb barrel import (#11795 Phase 2) ([#12051](https://github.com/diegosouzapw/OmniRoute/pull/12051)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate src/lib/ off the localDb barrel to direct db imports (#59) ([#12052](https://github.com/diegosouzapw/OmniRoute/pull/12052)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate open-sse, src/shared, src/sse, src/models, src/domain off the @/lib/localDb barrel import (#11795 Phase 4) ([#12053](https://github.com/diegosouzapw/OmniRoute/pull/12053)) — thanks @jonlwheat2-gif +- **fix(barrel):** delete the @/lib/localDb barrel — every consumer migrated (#11795 Phase 5) ([#12055](https://github.com/diegosouzapw/OmniRoute/pull/12055)) — thanks @jonlwheat2-gif +- **fix(radar):** restore D12 public boundary ([#12057](https://github.com/diegosouzapw/OmniRoute/pull/12057)) +- **fix(dev):** bound webpack and Tailwind scans ([#12075](https://github.com/diegosouzapw/OmniRoute/pull/12075)) — thanks @backryun +- **fix(dev):** isolate root layout settings reads ([#12076](https://github.com/diegosouzapw/OmniRoute/pull/12076)) — thanks @backryun +- **fix(dev):** reduce instrumentation executor fan-out (phase 3) ([#12078](https://github.com/diegosouzapw/OmniRoute/pull/12078)) — thanks @backryun +- **fix(dev):** make logging resources HMR-singleton ([#12079](https://github.com/diegosouzapw/OmniRoute/pull/12079)) — thanks @backryun +- **fix(sse):** keep unavailable forced connections scoped ([#12080](https://github.com/diegosouzapw/OmniRoute/pull/12080)) — thanks @keeltrace +- **fix(dev):** isolate batch dispatch from instrumentation ([#12081](https://github.com/diegosouzapw/OmniRoute/pull/12081)) — thanks @backryun +- **fix(ci):** reconcile release test contract drift ([#12082](https://github.com/diegosouzapw/OmniRoute/pull/12082)) — thanks @backryun +- **fix(ci):** accept CVE-2025-68121 in the prebuilt tls-client .so, auto-close base-red issues, guard Scorecard on the default branch ([#12085](https://github.com/diegosouzapw/OmniRoute/pull/12085)) +- **fix(db):** drop three consumer-less 1proxy exports — dead-code base-red on release/v3.8.51 after the barrel deletion (#12055) ([#12087](https://github.com/diegosouzapw/OmniRoute/pull/12087)) +- **fix(api):** clear the six API-route TypeScript regressions the new gate landed red on ([#12094](https://github.com/diegosouzapw/OmniRoute/pull/12094)) +- **fix(release):** never let the tag-push Create Release append auto notes to the curated body ([#12096](https://github.com/diegosouzapw/OmniRoute/pull/12096)) +- **fix(sse):** honor CLIProxyAPI environment API key ([#12099](https://github.com/diegosouzapw/OmniRoute/pull/12099)) — thanks @RaviTharuma +- **fix(oauth):** bind Google refresh to the client that issued the token ([#12106](https://github.com/diegosouzapw/OmniRoute/pull/12106)) — thanks @HouMinXi +- **fix(dashboard):** make RequestLoggerDetail loadable outside Next — CSS via globals.css + CJS/ESM interop (#11703 base-reds) ([#12114](https://github.com/diegosouzapw/OmniRoute/pull/12114)) +- **fix(leases):** project status lease row to lease columns so joined connection PII never escapes ([#12115](https://github.com/diegosouzapw/OmniRoute/pull/12115)) — thanks @geek007git +- **fix(plugins):** do not kill the plugin process when a fire-and-forget hook times out ([#12116](https://github.com/diegosouzapw/OmniRoute/pull/12116)) — thanks @geek007git +- **fix(guardrails):** restore injection-guard logging on middleware-only routes ([#12117](https://github.com/diegosouzapw/OmniRoute/pull/12117)) — thanks @geek007git +- **fix(plugins):** refresh stored manifest from disk on activate so new hook fields reach existing installs ([#12120](https://github.com/diegosouzapw/OmniRoute/pull/12120)) — thanks @geek007git +- **fix(codex):** preserve existing provider state when bulk-import upserts a matching connection ([#12122](https://github.com/diegosouzapw/OmniRoute/pull/12122)) — thanks @geek007git +- **fix(ci):** clear the two base-reds the 2026-08-30 merge batch left on release/v3.8.51 (round 3) ([#12123](https://github.com/diegosouzapw/OmniRoute/pull/12123)) +- **fix(sse):** strip type:'custom' from Claude tools on agentrouter dispatch ([#12126](https://github.com/diegosouzapw/OmniRoute/pull/12126)) — thanks @wahidsadik371-coder +- **fix(sse):** stop advertising video providers the dispatcher cannot run ([#12131](https://github.com/diegosouzapw/OmniRoute/pull/12131)) — thanks @quiterunner-commits +- **fix(search):** treat HTTP 432 and plan limit errors as transient cooldown ([#12139](https://github.com/diegosouzapw/OmniRoute/pull/12139)) — thanks @HouMinXi +- **fix(sse):** refuse an AI Horde queue that cannot fit the request budget ([#12143](https://github.com/diegosouzapw/OmniRoute/pull/12143)) — thanks @quiterunner-commits +- **fix(ci):** clear the base-reds the afternoon merge batch left on release/v3.8.51 (round 5: provider count 352, TS2554/TS2677) ([#12144](https://github.com/diegosouzapw/OmniRoute/pull/12144)) +- **fix(translator):** strip neutral tool_choice when tools absent in Responses-to-Chat (#12141) ([#12166](https://github.com/diegosouzapw/OmniRoute/pull/12166)) — thanks @HouMinXi +- **fix(guardrails):** pass providerId to getResolvedModelCapabilities in checkComboVision (#12112) ([#12169](https://github.com/diegosouzapw/OmniRoute/pull/12169)) — thanks @HouMinXi +- **fix(translator):** strip plaintext reasoning content for opaque responses backends (#12128) ([#12171](https://github.com/diegosouzapw/OmniRoute/pull/12171)) — thanks @HouMinXi +- **perf(stream):** compile hot-path regexes once, bound token caches, fix quadratic buffering ([#12179](https://github.com/diegosouzapw/OmniRoute/pull/12179)) — thanks @opensource-elearning +- **fix:** resolve compression worker file using runtime anchors instead of… ([#12183](https://github.com/diegosouzapw/OmniRoute/pull/12183)) — thanks @NightStalker-87 +- **fix:** prevent Claude Code session kills via liveness-aware readiness + auto model echo ([#12189](https://github.com/diegosouzapw/OmniRoute/pull/12189)) — thanks @opensource-elearning +- **fix(translator):** preserve falsy primitive values in Gemini and Antigravity function response results ([#12191](https://github.com/diegosouzapw/OmniRoute/pull/12191)) — thanks @Siva010 +- **fix(oauth):** mark empty Antigravity projectId as degraded and clear stale errors (#11284) ([#12205](https://github.com/diegosouzapw/OmniRoute/pull/12205)) — thanks @HouMinXi +- **fix(providers):** gate the Codex auto-ping usage read on the shared quota throttle ([#12209](https://github.com/diegosouzapw/OmniRoute/pull/12209)) — thanks @abhisheksharma2411 +- **fix(combos):** prioritize SQLite row id over inner JSON id and notify delete errors ([#12213](https://github.com/diegosouzapw/OmniRoute/pull/12213)) — thanks @HouMinXi +- **fix(providers):** mark featherless.ai as no longer having a free tier (`hasFree: false`) ([#12216](https://github.com/diegosouzapw/OmniRoute/pull/12216)) — thanks @turbolego +- **fix(deepseek-web):** stop Turbopack dev panic in the PoW worker path resolver ([#12221](https://github.com/diegosouzapw/OmniRoute/pull/12221)) — thanks @hartmark +- **fix(chat-admission):** clarify local 503 source ([#12223](https://github.com/diegosouzapw/OmniRoute/pull/12223)) — thanks @keeltrace +- **fix(dev):** silence webpack runtime module warnings ([#12228](https://github.com/diegosouzapw/OmniRoute/pull/12228)) — thanks @backryun +- **fix(db):** use module.require for CommonJS runtime driver loading ([#12230](https://github.com/diegosouzapw/OmniRoute/pull/12230)) — thanks @raheemuddin786 +- **fix(memory):** honest probe-driven FTS5 keyword status + memory_id rowid sync ([#12231](https://github.com/diegosouzapw/OmniRoute/pull/12231)) — thanks @raheemuddin786 +- **fix(system):** propagate abort signal to stream reader in HTTP version checks ([#12232](https://github.com/diegosouzapw/OmniRoute/pull/12232)) — thanks @raheemuddin786 +- **fix(resilience):** isolate local host execution errors from provider circuit breakers ([#12233](https://github.com/diegosouzapw/OmniRoute/pull/12233)) — thanks @raheemuddin786 +- **fix(sse):** sort injected tools deterministically for prompt caching ([#12234](https://github.com/diegosouzapw/OmniRoute/pull/12234)) — thanks @raheemuddin786 +- **fix(combo):** always clear the loop-safety timer, not just on the happy path (#11804) ([#12245](https://github.com/diegosouzapw/OmniRoute/pull/12245)) +- **fix(quality):** base-red pair — stryker tap registration + turn-pin suites aligned to the window gate ([#12255](https://github.com/diegosouzapw/OmniRoute/pull/12255)) — thanks @backryun +- **fix(dev):** qualify Turbopack runtime boundaries ([#12258](https://github.com/diegosouzapw/OmniRoute/pull/12258)) — thanks @backryun +- **fix(combo):** fall back to full pool when collapsed sole survivor is context-too-small ([#12278](https://github.com/diegosouzapw/OmniRoute/pull/12278)) — thanks @opensource-elearning +- **fix(combo):** bound the pre-dispatch unavailable skip so a stale label cannot dark a pool (#12168) ([#12285](https://github.com/diegosouzapw/OmniRoute/pull/12285)) +- **fix(security):** strip Qwen/Alibaba console-session cookies from provider API responses ([#12287](https://github.com/diegosouzapw/OmniRoute/pull/12287)) +- **fix(usage):** console-aware Token Plan guidance + subscription hint on bailian 401 ([#12288](https://github.com/diegosouzapw/OmniRoute/pull/12288)) +- **fix(resourcePressure):** log numeric detail on every rejection, recover faster ([#12293](https://github.com/diegosouzapw/OmniRoute/pull/12293)) — thanks @hartmark +- **fix(release):** drain the 2026-09-01 base-red window — passthrough usage regression + radarPage i18n keys ([#12327](https://github.com/diegosouzapw/OmniRoute/pull/12327)) +- **fix(ci):** openapi-security-tiers checker must honor routeGuard patterns + imported prefixes ([#12350](https://github.com/diegosouzapw/OmniRoute/pull/12350)) — thanks @ggiak +- **perf(ci):** cache node_modules in the npm-ci-retry composite (#8084 D3) ([#12408](https://github.com/diegosouzapw/OmniRoute/pull/12408)) +- **revert(ui):** point CTAs back at their real destinations ([#12410](https://github.com/diegosouzapw/OmniRoute/pull/12410)) +- **fix(release):** drain the 2026-09-02 base-red — rerank-providers import + api-typecheck baseline ratchet ([#12414](https://github.com/diegosouzapw/OmniRoute/pull/12414)) +- **fix(types):** make system prompt injection noImplicitAny-safe ([#12416](https://github.com/diegosouzapw/OmniRoute/pull/12416)) +- **fix(quality):** validate typecheck baseline schema ([#12419](https://github.com/diegosouzapw/OmniRoute/pull/12419)) +- **fix(memory):** resolve rerank provider node cache import ([#12421](https://github.com/diegosouzapw/OmniRoute/pull/12421)) +- **fix(ci):** repair release-root regressions — pack dedup, web-session syntax, uc-image ids ([#12423](https://github.com/diegosouzapw/OmniRoute/pull/12423)) — thanks @backryun +- **fix(providers):** separate MaxAI and UC credential contracts ([#12431](https://github.com/diegosouzapw/OmniRoute/pull/12431)) +- **fix(cli):** preserve Claude settings on config set ([#12432](https://github.com/diegosouzapw/OmniRoute/pull/12432)) — thanks @KrzysiekSko +- **fix(providers):** repair the maxai credential block truncated by merge auto-resolve ([#12433](https://github.com/diegosouzapw/OmniRoute/pull/12433)) +- **fix(quality):** record the 2026-09-02 merged growth in the file-size baseline ([#12434](https://github.com/diegosouzapw/OmniRoute/pull/12434)) +- **fix(uc):** route UC error strings through sanitizeErrorMessage; allowlist the retired codex id ([#12437](https://github.com/diegosouzapw/OmniRoute/pull/12437)) +- **fix(adapta):** redact streamed upstream errors ([#12438](https://github.com/diegosouzapw/OmniRoute/pull/12438)) +- **fix(sse):** remap non-contiguous upstream tool_calls index to a gap-free output_index ([#12445](https://github.com/diegosouzapw/OmniRoute/pull/12445)) — thanks @hartmark +- **fix(quality):** detect duplicate tool_calls entries in one response ([#12446](https://github.com/diegosouzapw/OmniRoute/pull/12446)) — thanks @hartmark +- **fix(conversations):** resolve turn content OmniRoute never sends back to the client ([#12447](https://github.com/diegosouzapw/OmniRoute/pull/12447)) — thanks @hartmark +- **fix(combo):** do not treat credits-exhausted 401 as auth skip ([#12449](https://github.com/diegosouzapw/OmniRoute/pull/12449)) — thanks @RaviTharuma +- **fix(auth):** do not park healthy quota accounts as expired ([#12452](https://github.com/diegosouzapw/OmniRoute/pull/12452)) — thanks @RaviTharuma +- **fix(responses-continuation):** fail closed on a collector-truncated, empty output array ([#12460](https://github.com/diegosouzapw/OmniRoute/pull/12460)) — thanks @hartmark +- **fix(logging):** raise the SSE payload collector's default cap ([#12461](https://github.com/diegosouzapw/OmniRoute/pull/12461)) — thanks @hartmark +- **fix(resilience):** surface Responses failed.error.message in 502s ([#12472](https://github.com/diegosouzapw/OmniRoute/pull/12472)) — thanks @RaviTharuma +- **fix(security):** unbiased maxai X-Random nonce + stricter URL/regex assertions ([#12502](https://github.com/diegosouzapw/OmniRoute/pull/12502)) +- **fix(video):** re-anchor transcript log-redaction so PII/credential maskers can't reopen the leak (#12150 P1 follow-up) ([#12503](https://github.com/diegosouzapw/OmniRoute/pull/12503)) +- **fix(monitoring):** serve cached credentialHealth off the request path ([#12533](https://github.com/diegosouzapw/OmniRoute/pull/12533)) — thanks @RaviTharuma +- **fix(quota):** keep Antigravity Gemini usable when Claude weekly is empty ([#12566](https://github.com/diegosouzapw/OmniRoute/pull/12566)) — thanks @HouMinXi +- **fix(authz):** hard-gate every credential export and CLI-config write (GHSA-5926-2w35-7h4q) ([#12600](https://github.com/diegosouzapw/OmniRoute/pull/12600)) +- **fix(docs):** restore the Next build — REMOVED_PROVIDERS.md had no frontmatter (base-red #12581) ([#12610](https://github.com/diegosouzapw/OmniRoute/pull/12610)) +- **fix(quality):** re-point the zcodeProtocol public-creds allowlist to line 313 ([#12615](https://github.com/diegosouzapw/OmniRoute/pull/12615)) +- **fix(security):** close 3 advisories — search baseUrl exfil, sk- in the error sanitizer, bifrost relay header leak ([#12620](https://github.com/diegosouzapw/OmniRoute/pull/12620)) +- **fix(providers):** sanitize boolean required and nested bare maps for Gemini (#12269) ([#12624](https://github.com/diegosouzapw/OmniRoute/pull/12624)) — thanks @HouMinXi +- **fix(resilience):** keep Overloaded STREAM_EARLY_EOF off the provider breaker ([#12626](https://github.com/diegosouzapw/OmniRoute/pull/12626)) — thanks @HouMinXi +- **fix(catalog):** re-audit free-tier quotas against official pages ([#12649](https://github.com/diegosouzapw/OmniRoute/pull/12649)) +- **fix(dashboard):** keep a request's pending-tracking id stable across combo target retries ([#12650](https://github.com/diegosouzapw/OmniRoute/pull/12650)) — thanks @hartmark +- **fix(tests):** retire dead model ids from the chat-pipeline integration suite (base-red #12581) ([#12670](https://github.com/diegosouzapw/OmniRoute/pull/12670)) +- **fix:** resolve SqliteError no such table compression_run_telemetry during cleanup ([#12682](https://github.com/diegosouzapw/OmniRoute/pull/12682)) — thanks @groovecityJO +- **fix(combo):** treat a pin-only step as implicit connection allowlist ([#12697](https://github.com/diegosouzapw/OmniRoute/pull/12697)) — thanks @HouMinXi +- **fix(ci):** ship .npmrc in published package so legacy-peer-deps applies to consumers (#11544) ([#12699](https://github.com/diegosouzapw/OmniRoute/pull/12699)) — thanks @KooshaPari +- **fix(docker):** pin 4 CLI tools to exact versions (#12576) ([#12703](https://github.com/diegosouzapw/OmniRoute/pull/12703)) — thanks @KooshaPari +- **fix(docker):** document and harden cli profile trust boundary (#12570) ([#12706](https://github.com/diegosouzapw/OmniRoute/pull/12706)) — thanks @KooshaPari +- **fix(sse):** redact video transcript in pre-guardrail rejected-request logs (#12150 P2 item 7) ([#12710](https://github.com/diegosouzapw/OmniRoute/pull/12710)) +- **fix(i18n):** quote placeholder in OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES description (#12505) ([#12769](https://github.com/diegosouzapw/OmniRoute/pull/12769)) — thanks @KooshaPari +- **fix(api/pricing):** surface validation error message as string, not raw object (#12494) ([#12771](https://github.com/diegosouzapw/OmniRoute/pull/12771)) — thanks @KooshaPari +- **fix(sse):** re-enable prompt compression for native Codex passthrough ([#12834](https://github.com/diegosouzapw/OmniRoute/pull/12834)) — thanks @thomasmaerz +- **fix(dashboard):** model health tests for a provider node set to the Responses API now call `/v1/responses` with a Responses-shaped body instead of `/v1/chat/completions` — those models were reported as `Provider returned HTTP 200 but no text content` even though the same model answered normally through `/v1/responses` ([#13070](https://github.com/diegosouzapw/OmniRoute/issues/13070)) ([#13087](https://github.com/diegosouzapw/OmniRoute/pull/13087)) — thanks @ntdatt812 +- fix(routing): stop a round-robin combo's "opencode" targets from collapsing onto the opencode-zen connection (#11912) ([#13283](https://github.com/diegosouzapw/OmniRoute/pull/13283)) +- fix(routing): stop the reactive-compaction debug log from lying when compression is globally disabled (#11977) ([#13276](https://github.com/diegosouzapw/OmniRoute/pull/13276)) +- fix(electron): relativize standalone-bundle symlink targets so Stage 8 manifest verification stops failing on Windows (#11979) ([#13251](https://github.com/diegosouzapw/OmniRoute/pull/13251)) +- fix(dashboard): surface a visible error when Compression Studio's combined preview run fails (#12061) ([#13288](https://github.com/diegosouzapw/OmniRoute/pull/13288)) +- fix(dashboard): make the compression "Effective pipeline" preview honor the active profile and warn when the master switch is off (#12063) ([#13287](https://github.com/diegosouzapw/OmniRoute/pull/13287)) +- fix(providers): scope TinyCMS Web signer's DOM shims to each call instead of leaking them for the process lifetime, and surface a clean HTTP status on a non-JSON interception-toggles error (#12072) ([#13254](https://github.com/diegosouzapw/OmniRoute/pull/13254)) +- fix(guardrails): stop Vision Bridge from re-selecting a model locked after a 404 (#12111) ([#13259](https://github.com/diegosouzapw/OmniRoute/pull/13259)) +- fix(sse): require Responses-shaped body before native OpenAI-compatible passthrough (#12129) ([#13278](https://github.com/diegosouzapw/OmniRoute/pull/13278)) +- fix(providers): minimax-m3 now collapses manual thinking.type:"enabled" to adaptive, preventing upstream 400 (2013) (#12132) ([#13249](https://github.com/diegosouzapw/OmniRoute/pull/13249)) +- fix(db): scope model visibility overrides by modality so hiding a Chat model no longer hides an identically-ID'd Image/Embeddings/etc. model (#12172) ([#13258](https://github.com/diegosouzapw/OmniRoute/pull/13258)) +- fix(db): scope local-provider apiKey dedup to matching base URL so LM Studio/Ollama-style connections support multiple accounts (#12173) ([#13250](https://github.com/diegosouzapw/OmniRoute/pull/13250)) +- fix(providers): refresh Trae's stale Referer/Origin and forward user timezone so imported connections stop failing with 401 (#12190) ([#13255](https://github.com/diegosouzapw/OmniRoute/pull/13255)) +- fix(providers): route opencode-go/gpt-5.6-luna to /responses instead of /chat/completions (#12196) ([#13275](https://github.com/diegosouzapw/OmniRoute/pull/13275)) +- fix(dashboard): allow deleting the last extra-upstream-header row even when invalid (#12251) ([#13252](https://github.com/diegosouzapw/OmniRoute/pull/13252)) +- **fix(i18n):** translate pre-existing `__MISSING__:` keys for `combo.sort`, `requestLogger.detail` expand/collapse, `common.profile`, and `settings.resilienceCredentialHealth*` across 39 locales ([#12272](https://github.com/diegosouzapw/OmniRoute/issues/12272)) ([#13034](https://github.com/diegosouzapw/OmniRoute/pull/13034)) — thanks @HouMinXi +- fix(cli): run the Node.js runtime compatibility guard before the heavy `tsx/esm` + Commander import chain so an unsupported runtime gets a clear message instead of a raw `Invalid regular expression flags` crash (#12296) ([#13253](https://github.com/diegosouzapw/OmniRoute/pull/13253)) +- fix(dashboard): refresh the providers list after deleting a compatible provider node (#12298) ([#13256](https://github.com/diegosouzapw/OmniRoute/pull/13256)) +- fix(usage): fail closed on API-key budget enforcement when a provider's `auto` routing alias has no pricing row, instead of silently counting it as $0 (#12341) ([#13257](https://github.com/diegosouzapw/OmniRoute/pull/13257)) +- **fix(providers):** include the submitted Agnes video model when polling by `video_id` ([#12356](https://github.com/diegosouzapw/OmniRoute/pull/12356)) — thanks @pacocartones +- **fix(routing):** custom OpenAI-compatible nodes now honor the saved Chat/Responses API type after edits instead of letting the node's original ID prefix override the live connection setting ([#11884](https://github.com/diegosouzapw/OmniRoute/issues/11884)). ([#12358](https://github.com/diegosouzapw/OmniRoute/pull/12358)) — thanks @pacocartones +- fix(sse): surface an error instead of a silent empty 200 when a Claude stream closes with zero bytes (#12398) ([#13285](https://github.com/diegosouzapw/OmniRoute/pull/13285)) +- fix(oauth): warn before the dead localhost:8080 redirect in antigravity/gemini `oauth start` (#12413) ([#13265](https://github.com/diegosouzapw/OmniRoute/pull/13265)) +- fix(providers): stop devin-cli spawn error from double-closing the SSE controller (#12517) ([#13261](https://github.com/diegosouzapw/OmniRoute/pull/13261)) +- **fix(rerank):** clamp Voyage `top_k` to the documents actually sent after empty-string filtering, and honor `return_documents: false` in the NVIDIA response adapter (#12523 — thanks @pacocartones) +- **fix(audio):** `/v1/audio/translations` now resolves combo names the way `/v1/audio/transcriptions` already does, so a combo that `GET /v1/models` advertises is fanned out to its targets instead of being rejected with `400 Invalid translation model: . Use format: provider/model`; literal `provider/model` ids and unknown bare names behave as before (#12536 — thanks @pacocartones) +- **fix(gemini):** strip the JSON-Schema-2020-12 `prefixItems` keyword from Gemini tool schemas at every nesting level, so Claude Code tool definitions no longer fail with `400 Unknown name "prefixItems"` on Gemini models (#12540 — thanks @pacocartones) +- **fix(api-manager):** Expose an accessible loading status while API keys are fetched instead of an empty accessibility tree (#12541 — thanks @pacocartones) +- **fix(video):** Clamp `estimateJpegFrameBytes` at zero for padding-only payloads and build the three encode-side frame data URIs from `JPEG_FRAME_DATA_URI_PREFIX` instead of a repeated literal (#12543 — thanks @pacocartones) +- **fix(devin):** accept Windows `DEVIN_AGENTIC_HOME` sandbox paths (`C:\...\.sandbox\...`) in the isolated-home check so the Devin Claude Bridge no longer fails closed on Windows ([#12405](https://github.com/diegosouzapw/OmniRoute/issues/12405)) (#12545 — thanks @pacocartones) +- **fix(db):** Add `conversation_turn_nodes` and orphaned `agentic_conversations` to the auto-cleanup cycle under the existing `retention.callLogs` window, so identity nodes whose call-log content has already been purged no longer accumulate without bound in `storage.sqlite` (#12548 — thanks @pacocartones) +- **fix(i18n):** Wrap the `~/.claude/profiles//settings.json` placeholder in ICU single quotes in the `OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES` feature-flag description across all 42 locales and the TypeScript default, so next-intl no longer fails with `INVALID_MESSAGE: UNCLOSED_TAG` and the Feature Flags card shows the description instead of the raw key (#12549 — thanks @pacocartones) +- **fix(orchestration):** `updateCloudAgentTask` now publishes the task's real `status` on `agent.task.updated` when an update only touches `result`, `activities` or `error`, instead of the fabricated `"updated"` state, and stays silent when no row matched the id (#12550 — thanks @pacocartones) +- **fix(i18n):** the home "Recent Requests" panel and the Provider Topology legend are now translated instead of rendering English copies on non-English dashboards; the legend reads its own `home.topologyLegend*` labels with consistent casing rather than borrowing the memory-settings "Recent" and analytics "Error" strings (#12551 — thanks @pacocartones). +- **docs(reference):** bring the `FEATURE_FLAGS.md` catalog back to 1:1 with `featureFlagDefinitions.ts` — 20 missing flags added, the two `*_BLOCK_THRESHOLD` env-only knobs moved out of the flag tables, category/total counts and the Live WS port corrected, guarded by a static test (#12552 — thanks @pacocartones) +- **fix(i18n):** backfill missing `usage.kiloPass*` strings in 39 locales and restore `featureFlagOmnirouteDisableThinkingLevelVariantsDescription` in `pt.json` ([#12561](https://github.com/diegosouzapw/OmniRoute/issues/12561)) ([#13027](https://github.com/diegosouzapw/OmniRoute/pull/13027)) — thanks @HouMinXi +- fix(docker): default docker-compose app ports (dashboard/API/live-WS) to loopback instead of `0.0.0.0`, closing the anonymous `/v1` LAN/WAN exposure gap left open by `REQUIRE_API_KEY=false` (#12568) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): close DNS-rebinding SSRF gap in webhook outbound-URL guard (#12569) ([#13243](https://github.com/diegosouzapw/OmniRoute/pull/13243)) +- fix(docker): require a per-session token on the VNC browser CDP bridge and isolate it on a dedicated Docker network (#12571) ([#13241](https://github.com/diegosouzapw/OmniRoute/pull/13241)) +- fix(open-sse): write Adobe Firefly session tokens and cookie jars with 0700/0600 permissions instead of the process umask (#12572) ([#13242](https://github.com/diegosouzapw/OmniRoute/pull/13242)) +- fix(api): remove hardcoded wildcard CORS in openai-to-gemini-sse.ts so the centralized fail-closed CORS gate is the sole source of `Access-Control-Allow-Origin` (#12573) ([#13239](https://github.com/diegosouzapw/OmniRoute/pull/13239)) +- fix(api): enforce API key policy (budget/rate-limit/schedule/endpoint scoping) on the ElevenLabs speech-to-text, text-to-speech and voices proxy routes (#12574) ([#13244](https://github.com/diegosouzapw/OmniRoute/pull/13244)) +- fix(sse): cap HuggingChat NDJSON body size and bound the read loop with the fetch timeout so a stalled or hostile upstream cannot buffer unbounded memory (#12577) ([#13282](https://github.com/diegosouzapw/OmniRoute/pull/13282)) +- fix(docker): scope the cliproxyapi/qdrant/bifrost sidecars to loopback by default and forward `CLIPROXYAPI_MANAGEMENT_KEY` into the cliproxyapi container so its management API is not left both unauthenticated and LAN-published (#12578) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): create DB export temp paths with `fs.mkdtempSync` instead of predictable timestamps (#12579) ([#13237](https://github.com/diegosouzapw/OmniRoute/pull/13237)) +- Cline 401 bodies that say "re-authenticate your Cline account" classify as a refreshable OAuth token, not a terminal expired key. The cooling panel no longer labels every cooldown as a 429; it shows the recorded last error instead. (#12594) ([#13060](https://github.com/diegosouzapw/OmniRoute/pull/13060)) — thanks @HouMinXi +- **fix(catalog):** degrade unknown combo targets instead of dropping LCD modalities, and persist OpenRouter `architecture.input_modalities` into the capability snapshot ([#12613](https://github.com/diegosouzapw/OmniRoute/issues/12613)) ([#13042](https://github.com/diegosouzapw/OmniRoute/pull/13042)) — thanks @HouMinXi +- fix(providers): send `x-api-key` instead of `Authorization: Bearer` for OpenCode Zen's `/v1/responses` endpoint (Muse Spark Contributor models), fixing a 401 on OmniRoute's auth header (#12633) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(sse): surface the actionable "Auggie CLI not found" message when the shell reports a missing `auggie` binary via exit code instead of a spawn error (#12645) ([#13263](https://github.com/diegosouzapw/OmniRoute/pull/13263)) +- **fix(config):** Persist deletions of built-in background-degradation entries — when a stored settings record exists its `degradationMap` is now authoritative instead of being merged under the defaults, so an entry the user removed in the dashboard no longer reappears on the next apply or restart ([#12424](https://github.com/diegosouzapw/OmniRoute/issues/12424)) ([#12647](https://github.com/diegosouzapw/OmniRoute/pull/12647)) — thanks @pacocartones +- **fix(gamification):** action-count badge milestones (First Token, Token Consumer, Token Machine, Token Whale, and the token-sharing tier) are now backed by a durable `xp_action_counts` counter incremented in `addXp()`, instead of a live `COUNT(*)` over `xp_audit_log`. The audit log is pruned by `retention.xpAuditLog` (default 30 days), so on a default install those "lifetime" milestones were really "actions in the last 30 days" and unlocked badges could stop unlocking once old rows aged out. `getActionCount()` and `checkActionCountBadges()` now read the same durable source, and a migration backfills existing totals from the surviving audit rows ([#12546](https://github.com/diegosouzapw/OmniRoute/issues/12546)) ([#12651](https://github.com/diegosouzapw/OmniRoute/pull/12651)) — thanks @pacocartones +- **fix(images):** `/v1/images/edits` now iterates a combo's targets the same way `/v1/images/generations` does (#9239) instead of flattening a bare combo to its first target. A combo whose first target is not edit-capable — or lacks credentials — now falls through to a later edit-capable target rather than hard-erroring, and missing credentials are skipped (not a hard `401`) to match the generations path. The per-target skip/terminal classification is extracted into a shared `runImageComboTargets` loop, so generations behavior is unchanged ([#12547](https://github.com/diegosouzapw/OmniRoute/issues/12547)). ([#12653](https://github.com/diegosouzapw/OmniRoute/pull/12653)) — thanks @pacocartones +- fix(sse): add first-byte watchdog to the TLS-fingerprint transport so a stalled wreq body falls back instead of hanging for minutes (#12656) ([#13272](https://github.com/diegosouzapw/OmniRoute/pull/13272)) +- fix(sse): exempt tiny-budget reasoning probes from combo quality failure and surface persisted-cooldown skips in ALL_TARGETS_SKIPPED diagnostics (#12659) ([#13269](https://github.com/diegosouzapw/OmniRoute/pull/13269)) +- fix(models): declare the real ~1M contextLength for OpenCode Zen's Muse Spark 1.2 models instead of falling back to the 200000 provider default (#12681) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(oauth): align codebuddy-cn OAuth User-Agent with the chat/usage CLI version to avoid WAF false positives (#12702) ([#13264](https://github.com/diegosouzapw/OmniRoute/pull/13264)) +- fix(dashboard): surface an authentication-required banner instead of silently blanking database settings for a guest session (#12709) ([#13270](https://github.com/diegosouzapw/OmniRoute/pull/13270)) +- **fix(resilience):** stop unbounded queue that hangs 6min until Aborted — gate, provider slot, and Bottleneck queue now share a per-connection `maxWaitMs` budget; `fail-closed` on exhaust (503); execution backstop `executionMaxWaitMs` overridable per connection with upstream clamp ([#12715](https://github.com/diegosouzapw/OmniRoute/pull/12715)) — thanks @maxmad64bis (with thanks to @Tushar49 for surfacing the slow-provider need in #12635) +- **fix(auto-combo):** every mode pack now carries `quality` and `reliability` weights (`quality` 0.02, 0.03 in `quality-first`; `reliability` 0.03, 0.04 in `reliability-first`), so selecting a pack no longer silences either signal; `DEFAULT_WEIGHTS` is unchanged ([#12731](https://github.com/diegosouzapw/OmniRoute/pull/12731)) — thanks @maxmad64bis +- **fix(sse):** the provider execution pipeline keeps the upstream error `code`/`type` again — both were lost when the non-streaming leg was extracted, so a config-class `422` (Antigravity missing project) degraded into a generic account cooldown instead of reaching the gate that recognises it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(db):** a legacy `call_logs` table no longer aborts startup — the provider-stats index is created after the column healing runs, not before it, so an install predating `request_type` opens instead of failing with `no such column` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(catalog):** a malformed row in the operator-writable custom-models blob no longer kills every `auto/*` pool ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(tests):** realign the guards that the same day's merges left asserting the old behavior — the provider-count total after GreenPT and EURouter, the GLM stream buffer slot that `#12925` turned into a declared parameter, and the injection scan bound that `#13104` reshaped from a head-only window into head-plus-tail ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- fix(cache): fold tool_choice/tools/response_format into the semantic cache signature so a cached tool_calls response can no longer be replayed for a request whose tool policy forbids it (#12734) ([#13267](https://github.com/diegosouzapw/OmniRoute/pull/13267)) +- **fix(models):** stop treating provider-supplied `isFree:true`/`:free`/`0/0` flags as trusted unless the provider has a documented free tier — only the shipped free-tier catalog decides otherwise; fetched `isFree:true` trusted only for a free-tier provider, custom `isFree:true` via a trusted path ([#12744](https://github.com/diegosouzapw/OmniRoute/pull/12744)) — thanks @maxmad64bis +- fix(memory): authenticate the internal /v1/rerank loopback call so memory reranking no longer silently degrades to unranked order when REQUIRE_API_KEY=true (#12745) ([#13268](https://github.com/diegosouzapw/OmniRoute/pull/13268)) +- fix(sse): parse Ollama Cloud's current usage markup (`$X of $Y used` aria-label, nested width style) (#12749) ([#13260](https://github.com/diegosouzapw/OmniRoute/pull/13260)) +- fix(cli): setup-opencode no longer sends an active context's management token to `/v1/models` when `--api-key`/`OMNIROUTE_API_KEY` is supplied — an explicit flag or the env var now always outranks the context's token, and the flag itself is no longer swallowed by the parent program's global `--api-key` option (#12783) ([#13246](https://github.com/diegosouzapw/OmniRoute/pull/13246)) +- fix(sse): register Arcee AI in the executor provider registry so requests reach api.arcee.ai instead of silently falling back to OpenAI (#12784) ([#13277](https://github.com/diegosouzapw/OmniRoute/pull/13277)) +- **fix(quota):** share one upstream quota read when identical saturation checks arrive at the same time, so traffic bursts don't multiply provider API calls ([#12787](https://github.com/diegosouzapw/OmniRoute/pull/12787)) — thanks @maxmad64bis +- **fix(db):** ignore expired or invalid rate-limit cooldown writes so a stale timestamp can't lock a connection that should be usable — clearing still works as before ([#12788](https://github.com/diegosouzapw/OmniRoute/pull/12788)) — thanks @maxmad64bis +- **fix(providers):** single source for provider order with `xao` ranking alongside `xai-oauth` ([#12790](https://github.com/diegosouzapw/OmniRoute/pull/12790)) — thanks @maxmad64bis +- **fix(routing):** off-table models check the free-model catalog before inheriting premium prices, latency bootstraps from the observed pool median, async tiers read live database pricing with a 90-day freshness gate, and the tier cache invalidates on every pricing write ([#12792](https://github.com/diegosouzapw/OmniRoute/pull/12792)) — thanks @maxmad64bis +- **fix(auto-combo):** snapshot scoring uses the observed per-connection account tier and per-model quality instead of neutral constants, and circuit-open providers rank lower at build time ([#12794](https://github.com/diegosouzapw/OmniRoute/pull/12794)) — thanks @maxmad64bis +- **fix(pool):** empty `auto/*` pools now say why they are empty, and the models listing uses the same paid check as routing ([#12795](https://github.com/diegosouzapw/OmniRoute/pull/12795)) — thanks @maxmad64bis +- fix(routing): recognize CLIProxyAPI's 'unknown provider for model' 400 as fallback-worthy (#12800) ([#13284](https://github.com/diegosouzapw/OmniRoute/pull/13284)) +- **fix(sse):** translate-mode streams now emit the estimated token counts as a trailing usage-only chunk before `[DONE]` when the upstream stays silent, so metered chat clients see totals instead of nothing ([#12828](https://github.com/diegosouzapw/OmniRoute/pull/12828)) — thanks @maxmad64bis +- **fix(db):** provider stats stay truthful on empty and legacy databases: fallback counts default to `0` instead of `null`, latency averages read `null` (not `0`) when no durations were recorded, failures logged before the error-type column existed group under `pre_migration` instead of `unclassified`, and per-provider queries use two new composite indexes ([#12832](https://github.com/diegosouzapw/OmniRoute/pull/12832)) — thanks @maxmad64bis +- fix(nvidia): fail open when a synced model catalog goes stale instead of gating forever (#12849) ([#13248](https://github.com/diegosouzapw/OmniRoute/pull/13248)) +- **fix(db):** WAL maintenance lives in its own module: a `TRUNCATE` checkpoint that hits a busy database now warns with its streak and retries once via `PASSIVE` instead of logging success, and closing-time checkpoints no longer report success on builds without a database file, with the busy totals visible in the authenticated monitoring health payload ([#12853](https://github.com/diegosouzapw/OmniRoute/pull/12853)) — thanks @maxmad64bis +- **fix(health):** quota with no snapshots reads empty instead of a contradicting 0% ([#12857](https://github.com/diegosouzapw/OmniRoute/pull/12857)) — thanks @maxmad64bis +- fix(a2a): accept the dashboard's own session cookie on /a2a so "Run message/send" no longer fails with "Unauthorized: missing or invalid API key" (#12888) ([#13271](https://github.com/diegosouzapw/OmniRoute/pull/13271)) +- **fix(logs):** the Logs grid's in-memory filter pass no longer discards rows the SQL query already matched — selecting an API key from the dropdown (which sends the key's id) returns its calls again, the Combo tab shows every combo instead of only those whose name contains a "1", and the model filter and search cover the same columns as the query ([#12896](https://github.com/diegosouzapw/OmniRoute/pull/12896)) — fixes [#12873](https://github.com/diegosouzapw/OmniRoute/issues/12873) — thanks @ntdatt812 +- **fix(a2a):** `/api/a2a/status` now builds the agent card from the request that asked for it, so a gateway reached at a non-localhost host no longer advertises `http://localhost:20128` as its A2A URL ([#12918](https://github.com/diegosouzapw/OmniRoute/pull/12918)). — thanks @ntdatt812 +- **fix(compression):** progressive aging now appends its `[COMPRESSED:aging:…]` annotation after a turn's `tool_result` blocks instead of in front of them, so Anthropic no longer rejects aged conversations with "`tool_use` ids were found without `tool_result` blocks immediately after" ([#12920](https://github.com/diegosouzapw/OmniRoute/pull/12920)). — thanks @ntdatt812 +- **fix(bedrock):** model import now resolves context limits for every vendor prefix instead of only `anthropic.*`, so `global.openai.gpt-5.6-*` no longer imports with a null `inputTokenLimit` and gets rejected pre-flight at the 200k default ([#12921](https://github.com/diegosouzapw/OmniRoute/pull/12921)). — thanks @ntdatt812 +- **fix(stream):** the 64 KB stream buffer GLM asks for is honoured instead of dropped, and the type error it caused no longer fails the API Route Typecheck gate on every open PR ([#12925](https://github.com/diegosouzapw/OmniRoute/pull/12925)) — thanks @ntdatt812 +- **fix(guardrails):** mask PII inside a `tool_result`'s nested content array, which the masker walked past while redacting its sibling block ([#12930](https://github.com/diegosouzapw/OmniRoute/pull/12930)) — thanks @ntdatt812 +- **fix(dashboard):** the Radar catalog table no longer leaves absent data unexplained — empty limits, unknown context windows, and unreported capabilities each explain themselves on hover, and a new check keeps it that way ([#12937](https://github.com/diegosouzapw/OmniRoute/pull/12937)) — thanks @maxmad64bis +- **fix(sse):** geo-blocked opencode requests rotate to the next account proxy instead of failing, so one refused egress no longer aborts the whole chain ([#12941](https://github.com/diegosouzapw/OmniRoute/pull/12941)) — thanks @maxmad64bis +- **fix(codex):** dashboard "clear cooldown" and the CAS recovery path now drop nested `codexScopeRateLimitedUntil` maps in the same write that nulls `rate_limited_until`, and fresh quota snapshots with headroom lift fallback-sourced scope cooldowns parked by quota preflight ([#12817](https://github.com/diegosouzapw/OmniRoute/issues/12817), [#12860](https://github.com/diegosouzapw/OmniRoute/issues/12860), [#12951](https://github.com/diegosouzapw/OmniRoute/pull/12951)) The dashboard reset-credit button (`consumeCodexResetCredit`) now exercises that same snapshot path after a successful redeem, so a filled quota bar is enough to unpark a leftover fallback Codex child without another manual clear. — thanks @HouMinXi +- **fix(db):** resolve `sql-wasm.wasm` across global npm install and hoisted layouts, ensuring OmniRoute can boot cleanly on Node 24 when native `better-sqlite3` is uncompiled. ([#13035](https://github.com/diegosouzapw/OmniRoute/pull/13035)) — thanks @HouMinXi +- **fix(api):** `DELETE /v1/batches/delete-completed` now sweeps only the calling API key's own completed batches (batches with no owner stay out of a key-scoped sweep on purpose), with an explicit instance-wide mode reserved for authenticated dashboard sessions, a 401 for a presented key that is unknown, revoked, deactivated, banned or expired (never falling through to the session branch), audit logging of every sweep, a sanitized 500 on failure, an owner-scoped file half (a key-scoped sweep never nulls a file another tenant owns) and an atomic sweep — chunked in 200-batch transactions in instance mode — so a mid-way error never leaves a batch pointing at a nulled file (GHSA-wvxc-jp3v-5mg5) ([#12969](https://github.com/diegosouzapw/OmniRoute/pull/12969)) +- **fix(combo):** quota-weighted routing stops drawing on an out-of-credit connection — a 402 now invalidates the stored quota snapshot instead of leaving its stale remaining percentage in place, and a snapshot older than 10 minutes no longer counts as confident headroom for the primary pool ([#12972](https://github.com/diegosouzapw/OmniRoute/pull/12972)) ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- **fix(vision):** Custom Models with "Vision capable" checked no longer have image requests swapped to `glm/glm-4.6v` when the client sends the advertised alias (`vllm/path/...`) or the bare path-shaped id — Vision Bridge now matches the stored override for all three id forms ([#12758](https://github.com/diegosouzapw/OmniRoute/issues/12758)) ([#12974](https://github.com/diegosouzapw/OmniRoute/pull/12974)) — thanks @HouMinXi +- **fix(sse):** transient opencode upstream failures rotate to the next account proxy instead of failing, so one flapping egress no longer aborts the whole chain ([#12975](https://github.com/diegosouzapw/OmniRoute/pull/12975)) — thanks @maxmad64bis +- **fix(azure):** Deployments from GPT-6 onward now send `max_completion_tokens` instead of `max_tokens`, which Azure rejects with HTTP 400. The rule matched a literal `gpt-5`, so each new generation arrived broken; it now matches the generation range, while `gpt-35-turbo` still keeps `max_tokens`. ([#13007](https://github.com/diegosouzapw/OmniRoute/pull/13007)) — thanks @ntdatt812 +- **fix(db):** add an opt-in self-restart circuit for sustained critical memory pressure, gate post-cleanup VACUUM behind a minimum freed-rows threshold, and checkpoint the SQLite WAL every 5 minutes with a size guard that escalates to TRUNCATE, so a growing WAL can no longer stall the event loop into a full outage. ([#13011](https://github.com/diegosouzapw/OmniRoute/pull/13011)) — thanks @HouMinXi +- **fix(auth):** an explicit connection pin may probe a quota-disabled row once and re-enable it on success ([#12874](https://github.com/diegosouzapw/OmniRoute/issues/12874)) ([#13017](https://github.com/diegosouzapw/OmniRoute/pull/13017)) — thanks @HouMinXi +- **fix(api):** thread `X-OmniRoute-Fallback-Attempts` through combo chat completions so streaming and non-streaming responses report how many prior legs were attempted ([#13038](https://github.com/diegosouzapw/OmniRoute/pull/13038)) — thanks @HouMinXi +- Restore provider failure classification and credential refresh on non-streaming requests: classify non-2xx failures to lock models on per-model quota exhaustion, update connection rate limits from headers and body, and pass credential refresh handlers to pipeline execution so 401 tokens can be refreshed and retried (#13043). ([#13069](https://github.com/diegosouzapw/OmniRoute/pull/13069)) — thanks @HouMinXi +- **fix(responses):** wrap forced-non-streaming web_search fallback JSON as Responses SSE so Codex still sees `response.completed` ([#13050](https://github.com/diegosouzapw/OmniRoute/pull/13050)) — thanks @HouMinXi +- **fix(antigravity):** Preserve upstream thought-token usage in normalized completion and reasoning token counts ([#13055](https://github.com/diegosouzapw/OmniRoute/pull/13055)) — thanks @pacocartones +- Fix a concurrency-slot leak in the DeepSeek PoW solver: a worker that failed to spawn (for example a missing worker script) never released its slot, so `MAX_CONCURRENT_WORKERS` failures disabled the solver until restart. ([#13097](https://github.com/diegosouzapw/OmniRoute/pull/13097)) — thanks @anhtahaylove +- **fix(acp):** bound the ACP session output buffers — `stdoutBuffer` and `stderrBuffer` now cap at 1 MiB keeping the most recent output behind a visible `[...output truncated...]` marker, and `stderrBuffer` is reset per prompt instead of accumulating for the lifetime of the session. ([#13100](https://github.com/diegosouzapw/OmniRoute/pull/13100)) — thanks @anhtahaylove +- **fix(acp):** release the `stdout`/`exit` listeners and the idle timer that a `sendPrompt` timeout used to leave attached to the `acpManager` singleton, and drop sessions that exited on their own from the session map instead of keeping them forever. ([#13096](https://github.com/diegosouzapw/OmniRoute/pull/13096)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection and PII scanners now read the text a `tool_result` block carries on `content` (string or nested block list), in messages and in system blocks, so tool output is judged by the same rules as user text ([#13101](https://github.com/diegosouzapw/OmniRoute/pull/13101)) — thanks @ntdatt812 +- **fix(gamification):** close the badge notification SSE stream when the request signal is already aborted before the stream starts — a client that disconnects while the route is still awaiting auth used to leave both the 2s unlock poll and the 15s heartbeat running for the lifetime of the process. ([#13106](https://github.com/diegosouzapw/OmniRoute/pull/13106)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection scan now spends its 16 KB budget on both ends of the request instead of the first 16 KB only, so `system`, `instructions`, `query`, `documents` and the newest turns are no longer hidden behind one long message ([#13104](https://github.com/diegosouzapw/OmniRoute/pull/13104)) — thanks @ntdatt812 +- **fix(dashboard):** expose the Volcano Ark console cookie on quota scraping and unwrap connect-error objects so the dashboard shows the upstream message ([#13107](https://github.com/diegosouzapw/OmniRoute/pull/13107)) — thanks @HouMinXi +- **fix(db):** release the `beforeExit`/`SIGINT`/`SIGTERM` handlers when a `node:sqlite` adapter closes, so a closed adapter and its database handle are no longer pinned to `process` for the lifetime of the run — the same treatment #7494 gave the sql.js adapter. ([#13109](https://github.com/diegosouzapw/OmniRoute/pull/13109)) — thanks @anhtahaylove +- **fix(translator):** `contentSchema` and `unevaluatedItems` are now treated as subschema positions by the tool-schema sanitizer, so a truncation placeholder in either is replaced with a permissive schema instead of being forwarded as a string ([#13110](https://github.com/diegosouzapw/OmniRoute/pull/13110)) — thanks @ntdatt812 +- **fix(cli-helper):** clear the `createLogStream` timeout on the abort path — `stop()` aborts the in-flight fetch and returned through the `signal.aborted` branch, which skipped `clearTimeout` and left an armed timer per stopped stream. The stream reader is now also cancelled when the read loop exits early. ([#13114](https://github.com/diegosouzapw/OmniRoute/pull/13114)) — thanks @anhtahaylove +- **fix(docs):** drop TLS-impersonation, MITM-decrypt, supply-chain attestation, and XOR-mask writeups from the public `/docs` catalog and Docker image. Files stay in git for engineers; operators who need them open the repo, not the website. ([#13136](https://github.com/diegosouzapw/OmniRoute/pull/13136)) — thanks @HouMinXi +- **fix(combo):** parse numeric-epoch `rate_limited_until` in the combo cooldown read path ([#13141](https://github.com/diegosouzapw/OmniRoute/pull/13141)) — thanks @maxmad64bis +- **fix(opencode):** both OpenCode plugins now reject a gateway address typed without `http://` at configuration time, instead of publishing every model with an api url no client can call, and the v2 plugin no longer publishes a model card whose api url is blank or relative ([#13142](https://github.com/diegosouzapw/OmniRoute/pull/13142)) — thanks @maxmad64bis +- **fix(providers):** lock opencode model on upstream 400 model-unavailable ([#13146](https://github.com/diegosouzapw/OmniRoute/pull/13146)) — thanks @maxmad64bis +- **fix(logging):** keep the provider exchange rather than the raw client bodies when a call log exceeds its size budget, and show that recovered payload in the request-detail panel instead of replacing it with the stored response body ([#13147](https://github.com/diegosouzapw/OmniRoute/pull/13147)) — thanks @maxmad64bis +- **fix(traffic-inspector):** the WebSocket route no longer leaks a traffic-buffer subscriber and a 30s ping timer when the client socket is already closed at handler time — listeners are attached before any resource is acquired, a destroyed socket bails out early, and the ping interval stops on a dead socket where `write()` never throws ([#13155](https://github.com/diegosouzapw/OmniRoute/pull/13155)) — thanks @anhtahaylove +- **fix(telegram):** bound the per-user API key cache in the Telegram chat proxy so a burst of distinct chat ids can no longer grow the process heap without limit ([#13165](https://github.com/diegosouzapw/OmniRoute/issues/13165)) ([#13166](https://github.com/diegosouzapw/OmniRoute/pull/13166)) — thanks @anhtahaylove +- **fix(stream):** cancel the upstream response body when the JSON-to-SSE sniff unwinds on a body timeout, so a stalled upstream no longer pins the connection ([#13169](https://github.com/diegosouzapw/OmniRoute/issues/13169)) ([#13171](https://github.com/diegosouzapw/OmniRoute/pull/13171)) — thanks @anhtahaylove +- **fix(telegram):** authenticate webhook deliveries with Telegram's `secret_token` so an unauthenticated caller can no longer mint API keys or spend upstream quota ([#13172](https://github.com/diegosouzapw/OmniRoute/issues/13172)) ([#13175](https://github.com/diegosouzapw/OmniRoute/pull/13175)) — thanks @anhtahaylove +- **fix(models):** give discoverable Gemini 3.8 Flash ids their own 65536 output spec so Antigravity no longer clamps them to 16384 ([#13195](https://github.com/diegosouzapw/OmniRoute/pull/13195)) — thanks @HouMinXi +- **fix(dashboard):** leftover catalog-removed provider rows (gemini-cli) can be listed and purged from the providers page ([#13067](https://github.com/diegosouzapw/OmniRoute/issues/13067)) ([#13197](https://github.com/diegosouzapw/OmniRoute/pull/13197)) — thanks @HouMinXi +- **fix(auth):** a dashboard session now requires the `authenticated: true` claim that login, OIDC and the session refresh already emit — a JWT merely signed with `JWT_SECRET` (for example the Cursor CLI passthrough token, which any API-key holder can obtain) no longer verifies as the `auth_token` cookie on any route, the WebSocket handshake or the live server; existing sessions keep working ([#13298](https://github.com/diegosouzapw/OmniRoute/issues/13298)) ([#13375](https://github.com/diegosouzapw/OmniRoute/pull/13375)) +- **fix(api):** `DELETE /v1/batches/delete-completed` now applies the caller's API-key policy (endpoint allowlist, schedule, usage cap, rate limit) like every other `/v1` route, commits the key-scoped sweep per 200-batch chunk instead of holding one write lock for the whole sweep, refuses to loop without progress, and rejects a scope that names both a key and `allTenants` ([#13684](https://github.com/diegosouzapw/OmniRoute/pull/13684)) +- Restore the API-route typecheck gate: the non-streaming leg result lost its discriminated-union narrowing after the server-owned tool loop reassignment, producing 13 new TS2339 diagnostics in `chatCore.ts`. ([#12963](https://github.com/diegosouzapw/OmniRoute/pull/12963)) +- Restore four non-streaming behaviours the server-owned tool loop refactor dropped: client aborts map to 499 with the fixed `Request aborted` message, an aborted request no longer logs a synthetic `clientResponse`, Claude prompt-cache telemetry is recorded again, and a body that cannot be canonicalized no longer throws when the tool loop is off. ([#12990](https://github.com/diegosouzapw/OmniRoute/pull/12990)) +- **fix(skills):** The CLI registry parser now reads positionals declared with `.addArgument()`, not only those written inline in `.command()`. `tunnel create [type]` was being published as `tunnel create`, so the agent-skills sync gate reported drift on every branch and regenerating would have deleted the argument. ([#13009](https://github.com/diegosouzapw/OmniRoute/pull/13009)) — thanks @ntdatt812 +- **fix(combos):** dashboard combo test uses a short prompt, serial probes, and a 60s timeout so reasoning models and rate-limited free pools do not fail the health check ([#13001](https://github.com/diegosouzapw/OmniRoute/pull/13001)) — thanks @HouMinXi +- fix(compression): terminate idle worker threads on eviction so long-running instances stop leaking OS threads and MessagePorts ([#13091](https://github.com/diegosouzapw/OmniRoute/pull/13091)) — thanks @anhtahaylove +- Redact Google API keys of any length in error bodies: the pattern required exactly 39 characters, so shorter or longer `AIza…` credentials passed through unredacted. ([#12964](https://github.com/diegosouzapw/OmniRoute/pull/12964)) +- **fix(grok-cli):** a 402 "Grok Build usage balance exhausted" parks that Grok login as out of credit (Grok Build CLI, grok.com cookie, and xAI OAuth share the weekly pool). Combo routing then tries the next login instead of locking the model for every account in the pool ([#13061](https://github.com/diegosouzapw/OmniRoute/pull/13061)) — thanks @HouMinXi +- fix(compression): spawn the LLMLingua worker with a file URL object so compression actually runs instead of silently failing open on Node ([#13093](https://github.com/diegosouzapw/OmniRoute/pull/13093)) — thanks @anhtahaylove +- **fix(test):** run the local `test` and `test:unit` scripts at concurrency 4 so a full-suite run no longer exhausts the machine's commit charge and kills unrelated processes ([#13187](https://github.com/diegosouzapw/OmniRoute/pull/13187)) — thanks @anhtahaylove +- **fix(monitoring):** `GET /api/monitoring/health` `credentialHealth` now includes a bounded `failedConnections` list (`connectionId`, `status`, sanitized `lastError`) when the probe-cache gauge `failed>0`, plus `source: probe-cache` and a cheap `staleDbNonOkCount` for sticky SQLite `test_status` on active rows. Documents that the live gauge is not `provider_connections.test_status`. ([#12876](https://github.com/diegosouzapw/OmniRoute/pull/12876)) — thanks @RaviTharuma +- **fix(resilience):** an openai-compatible multi-upstream gateway no longer marks the whole connection `credits_exhausted` when a single upstream (or the `/models` / representative-model health probe) returns 402. Quota failures stay model-scoped; true connection-wide auth failures are unchanged. Single-credential 402 key disable ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239)) is preserved. ([#12875](https://github.com/diegosouzapw/OmniRoute/pull/12875)) — thanks @RaviTharuma +- **fix(dashboard):** nine fixes on the Orchestration Canvas, all diagnosed in the Phase 2 reviews. A source that fails now keeps the timestamp of its FIRST failure instead of being re-stamped every poll — the stale line said "since the last poll" no matter how long the source had been down, and the churn also defeated the snapshot's stable identity (it serializes the source list), so the canvas re-rendered on every tick while anything was broken. A source that HAD data and only then started failing is flagged too: previously only a source with no node at all got the warning, so a source that went down mid-session kept a healthy-looking node forever. The rest are pointwise: clicking a filter chip cancels the pending search debounce (left armed, it fired ~300ms later and silently reverted the chip); the search input carries an accessible name; `?state=running, failed` parses like the unpadded form instead of dropping the padded value; the CSV toggle helper is defined once in `model/urlParams.ts` rather than twice; the agents tab tells "nothing running" apart from "the filter matched nothing", offering a clear-filters button instead of setup links that would be wrong advice there; edges stop emitting SMIL particles above 40 simultaneously active edges, keeping the colored stroke; and the drawer's error banner clears when a retried action succeeds. ([#12983](https://github.com/diegosouzapw/OmniRoute/pull/12983)) +- **fix(i18n):** the nine locales added with the EU-language batch (Greek, Estonian, Irish, Croatian, Lithuanian, Latvian, Maltese, Slovenian, Serbian) were missing the eleven Orchestration Canvas keys that Phase 3 introduced, so the compare-runs panel and the "no runs match these filters" empty state fell back to English in those languages (`deepMergeFallback` substitutes English for an absent key, so nothing rendered blank — it rendered untranslated). The coverage gate does not catch this: it enforces an 80% floor per locale, and eleven missing keys out of ~13,000 leaves coverage at 99.9%. Translated for real in each language, calibrated against the wording each file already uses for "run", "filter" and "skill". ([#13294](https://github.com/diegosouzapw/OmniRoute/pull/13294)) +- **fix(dashboard):** five follow-ups from the Phase 2 Orchestration Canvas review. The History drawer now shows the memory section for runs that already left the live TTL window: the persisted `memory_hits` event is parsed into `metadata.memoryHits` with the same defensive validation the drawer applies, and — because that event is observability rather than a state transition — it no longer leaks into the timeline, where it had been inheriting the task's state and rendering as a duplicate transition. The A2A memory recall runs against its own 1.5s deadline instead of inheriting the memory backend's 30s one; overshooting degrades exactly like any other recall failure (no hits, task proceeds), and the timer is cleared on both paths. Repeating a Conductor run carries its `requirements.cli`/`requirements.model` forward, so the new run is pinned to the same runner profile and model rather than drifting to whatever the fleet picks. A successful repeat from the Agents tab now focuses the run it created, instead of leaving the operator on the finished one. And the auth test for `POST /api/conductor/tasks` moved into the shared `ROUTES` array rather than restating the pattern. ([#12988](https://github.com/diegosouzapw/OmniRoute/pull/12988)) +- fix(plugins): stop leaking an exit listener per plugin hook timeout, which triggered MaxListenersExceededWarning on plugins that ignore SIGTERM ([#13092](https://github.com/diegosouzapw/OmniRoute/pull/13092)) — thanks @anhtahaylove +- **fix(validation):** Provider node edits no longer fail with a generic "Invalid request" when the optional daily-quota reset fields are left blank. The dashboard sends `dailyQuotaResetTimezone` and `dailyQuotaResetHour` as `null`, and only the hour accepted it. ([#13066](https://github.com/diegosouzapw/OmniRoute/issues/13066)) ([#13083](https://github.com/diegosouzapw/OmniRoute/pull/13083)) — thanks @ntdatt812 +- **fix(combo):** quota-aware expansion drops banned, inactive, missing, and wrong-provider connections before quota fetch or model dispatch; pins and allowlists stay selectors, not a bypass. Antigravity automatic exhaustion now requires a reported zero remaining, so a positive balance below 1% stays eligible. ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- Fix an intermittent failure in the quota-weighted routing test suite: scores are a function of `Date.now()`, so peers with identical quota scored microseconds apart never tied and swapped order. ([#12946](https://github.com/diegosouzapw/OmniRoute/pull/12946)) +- fix(volcengine): resolve quota window mapping gaps, safe multi-connection console binding, and response cookie sanitization ([#12950](https://github.com/diegosouzapw/OmniRoute/pull/12950)) — thanks @HouMinXi +- **fix(test):** resolve the WebDAV handler path with `fileURLToPath` so the suite's 37 WebDAV tests run on Windows instead of failing with a doubled `C:\C:\` drive prefix ([#13196](https://github.com/diegosouzapw/OmniRoute/pull/13196)) — thanks @anhtahaylove +- **fix(ui):** fall back from invalid AnySearch icon alias (direct commit `9587e07b69`) +- **fix(sse):** stop rebuilding a truncated summary from the collector's cap-dropped event array ([#12718](https://github.com/diegosouzapw/OmniRoute/pull/12718)) — thanks @hartmark +- **fix(conversations):** show a pending spinner for unresolved tool nodes instead of "(empty)" ([#12727](https://github.com/diegosouzapw/OmniRoute/pull/12727)) — thanks @hartmark +- **fix(streaming):** fail fast when an upstream stream produces only lifecycle/heartbeat events, never real content ([#12741](https://github.com/diegosouzapw/OmniRoute/pull/12741)) — thanks @hartmark +- **fix(responses-continuation):** bridge the write-in-flight window with an in-memory pending store ([#12854](https://github.com/diegosouzapw/OmniRoute/pull/12854)) — thanks @hartmark +- **fix(translator):** drop or map agent_message on Chat Completions fallback ([#12880](https://github.com/diegosouzapw/OmniRoute/pull/12880)) — thanks @RaviTharuma +- **fix(api):** page and stream-complete GET /v1/models for large catalogs ([#12882](https://github.com/diegosouzapw/OmniRoute/pull/12882)) — thanks @RaviTharuma +- **fix(quota):** align AUTH window labels with usage API and clarify 503 ([#12884](https://github.com/diegosouzapw/OmniRoute/pull/12884)) — thanks @RaviTharuma +- **fix(ci):** clear two base-reds on release/v3.8.51 — mutation-coverage gate + image-only-model guard ([#12945](https://github.com/diegosouzapw/OmniRoute/pull/12945)) +- **fix(api):** scope batch bulk-delete to the calling API key ([#13211](https://github.com/diegosouzapw/OmniRoute/pull/13211)) +- **fix(tests):** align model-sync log assertion to shipped path redaction (base-red) ([#13227](https://github.com/diegosouzapw/OmniRoute/pull/13227)) +- **fix(test):** make the ToS heading guard actually require the parentheses ([#13228](https://github.com/diegosouzapw/OmniRoute/pull/13228)) +- **fix(quality):** register 4 drifted covering tests in stryker tap.testFiles ([#13229](https://github.com/diegosouzapw/OmniRoute/pull/13229)) +- **fix(logging):** mask *-api-key headers in request-log pipeline (#13273) ([#13401](https://github.com/diegosouzapw/OmniRoute/pull/13401)) — thanks @KooshaPari +- **fix(tests):** correct stale Jina catalog prefix in custom-models test ([#13403](https://github.com/diegosouzapw/OmniRoute/pull/13403)) — thanks @KooshaPari +- **fix(backend):** prune old backups after health-check-repair snapshots (#13308) ([#13404](https://github.com/diegosouzapw/OmniRoute/pull/13404)) — thanks @KooshaPari +- **fix(backend):** guard memory_fts_au trigger with WHEN clause to stop FTS bloat (#13326) ([#13405](https://github.com/diegosouzapw/OmniRoute/pull/13405)) — thanks @KooshaPari +- **fix(free-tiers):** remove obsolete Together $25 signup credit (#12526) ([#13407](https://github.com/diegosouzapw/OmniRoute/pull/13407)) — thanks @KooshaPari +- **fix:** skip non-chat generation models in Test all models ([#13409](https://github.com/diegosouzapw/OmniRoute/pull/13409)) — thanks @KooshaPari +- **fix(providers):** de-duplicate agy and antigravity model catalogs ([#13410](https://github.com/diegosouzapw/OmniRoute/pull/13410)) — thanks @KooshaPari +- **fix(dashboard):** send provider in body for mini-playground web search ([#13411](https://github.com/diegosouzapw/OmniRoute/pull/13411)) — thanks @KooshaPari +- **fix(resilience):** add CJK quota-exhaustion patterns to 429 classifier ([#13412](https://github.com/diegosouzapw/OmniRoute/pull/13412)) — thanks @KooshaPari +- **fix(backend):** eval runner: failed calls no longer score as passed + dotAll regex ([#13413](https://github.com/diegosouzapw/OmniRoute/pull/13413)) — thanks @KooshaPari +- **fix(backend):** eval runner opts out of output-style and memory injection ([#13414](https://github.com/diegosouzapw/OmniRoute/pull/13414)) — thanks @KooshaPari +- **fix:** chain serial quarantine step in npm test script (fixes #13286) ([#13418](https://github.com/diegosouzapw/OmniRoute/pull/13418)) — thanks @KooshaPari +- **fix(antigravity):** derive stable session ID from credentials for prompt-cache reuse (#13305) ([#13424](https://github.com/diegosouzapw/OmniRoute/pull/13424)) — thanks @KooshaPari +- **fix(memory):** merge memory into top-level system field instead of unshifting at messages[0] (#13425) ([#13427](https://github.com/diegosouzapw/OmniRoute/pull/13427)) — thanks @KooshaPari +- **fix(compression):** preserve instruction blocks from lossy engines ([#13523](https://github.com/diegosouzapw/OmniRoute/pull/13523)) — thanks @KooshaPari +- **fix(compression):** preserve polarity words and newlines in ultra heuristic ([#13524](https://github.com/diegosouzapw/OmniRoute/pull/13524)) — thanks @KooshaPari +- **fix(compression):** run user preservePatterns before built-in patterns (#13457) ([#13525](https://github.com/diegosouzapw/OmniRoute/pull/13525)) — thanks @KooshaPari +- **fix(reasoning):** expand deepseek-v4 regex to match v4.1+ and add command-code provider (#13430) ([#13528](https://github.com/diegosouzapw/OmniRoute/pull/13528)) — thanks @KooshaPari +- **fix(providers):** add alwaysReasons metadata for reka-flash-3 ([#13531](https://github.com/diegosouzapw/OmniRoute/pull/13531)) — thanks @KooshaPari +- **fix(playground):** allow Compare columns to scroll independently ([#13532](https://github.com/diegosouzapw/OmniRoute/pull/13532)) — thanks @KooshaPari +- **fix(security):** empty whitelist allows through to prevent admin lockout ([#13534](https://github.com/diegosouzapw/OmniRoute/pull/13534)) — thanks @KooshaPari +- **fix(tests):** add DISABLE_IOREG_STRATEGY env var for macOS test compatibility (fixes #13467) ([#13539](https://github.com/diegosouzapw/OmniRoute/pull/13539)) — thanks @KooshaPari +- **fix(zed-hosted):** map developer role and Gemini enums for Zed proxy ([#13543](https://github.com/diegosouzapw/OmniRoute/pull/13543)) — thanks @KooshaPari +- **fix(analytics):** normalize provider aliases to canonical id in usage_history ([#13545](https://github.com/diegosouzapw/OmniRoute/pull/13545)) — thanks @KooshaPari +- **fix(combo):** use traceId for call log id to prevent UNIQUE constraint failure ([#13546](https://github.com/diegosouzapw/OmniRoute/pull/13546)) — thanks @KooshaPari +- **fix(auth):** check model lockout before returning synthetic noauth connection ([#13547](https://github.com/diegosouzapw/OmniRoute/pull/13547)) — thanks @KooshaPari +- **fix(guardrails):** skip credential redaction for base64 image data URLs ([#13550](https://github.com/diegosouzapw/OmniRoute/pull/13550)) — thanks @KooshaPari +- **fix(resilience):** increase requestQueue.maxWaitMs default from 15s to 30s ([#13553](https://github.com/diegosouzapw/OmniRoute/pull/13553)) — thanks @KooshaPari +- **fix(docs):** document OMNIROUTE_READY_TIMEOUT_MS and allowlist the test-only DISABLE_IOREG_STRATEGY ([#13692](https://github.com/diegosouzapw/OmniRoute/pull/13692)) + ### 📝 Maintenance ---- +- **docs(readme):** reconcile live v3.8.50 provider, free-tier, CLI, routing, test, community, sponsor, acknowledgment, and SVG metrics with their audited source denominators, including a deduplicated OmniRoute-in-Action snapshot and distinct contributor rankings for merged pull requests, GitHub-attributed commits, and Git history ([#11356](https://github.com/diegosouzapw/OmniRoute/pull/11356)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **docs:** sync the canonical provider count 354 → 356 across `README.md`, `AGENTS.md`, `llm.txt` (+ 42 i18n mirrors), the four README SVG diagrams, `docs/reference/PROVIDER_REFERENCE.md` (regenerated) and the `package.json` description after Opper (#11629) and 1min.ai (#11631) boarded the catalog — closes the `check:docs-counts-sync` strict drifts that kept `release/v3.8.51` red ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11673](https://github.com/diegosouzapw/OmniRoute/pull/11673)) — thanks @jonlwheat2-gif +- **test(providers):** update count-derived assertions after the v3.8.51 provider additions — `APIKEY_PROVIDERS` 233 → 235 (Opper #11629 + 1min.ai #11631), reserved-prefix REGISTRY walk 395 → 398, `WEB_FETCH_PROVIDERS` now includes `nimble-search` (#11620), and the provider translate-path golden snapshot regenerated ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11674](https://github.com/diegosouzapw/OmniRoute/pull/11674)) — thanks @jonlwheat2-gif +- **test(sse):** bump the hard-lease connection-query inventory for `src/lib/tokenHealthCheck.ts` to 2 — the verify-only web-cookie sweep added by #11495 split the single `getProviderConnections` call into oauth + cookie variants, which the frozen inventory had not tracked ([#11495](https://github.com/diegosouzapw/OmniRoute/pull/11495)). ([#11677](https://github.com/diegosouzapw/OmniRoute/pull/11677)) — thanks @jonlwheat2-gif / @oyi77 +- **chore(release):** clear the `release/v3.8.51` base-red gates — docs migration count (+38 i18n mirrors), stryker `tap.testFiles`, lockfile registry host, stale ESLint suppressions, the 7 lint regressions they were hiding, the stale `APIKEY_PROVIDERS` partition count (231 → 233), the `glm-5.3-max` tier missing from the GLM test table, a dead `modelType === "chat"` comparison in the models catalog, and the `resolveLiveWsUrl` / `sanitizeLiveWsPort` helpers from #11388 that a later merge dropped from `wsPath.ts` (dashboard live WebSocket URL resolution) ([#11502](https://github.com/diegosouzapw/OmniRoute/pull/11502)) — thanks @MumuTW +- **chore(free-models):** the free-tier regime table now declares which totals figure each regime feeds and whether it may take the no-auth shortcut, so the sets that used to repeat those answers by hand are derived from it — a new `freeType` no longer compiles until it has answered every question, instead of silently contributing to no total ([#11537](https://github.com/diegosouzapw/OmniRoute/pull/11537)) — thanks @maxmad64bis +- **docs(routing):** LKGP strategy copy now states provider-level stickiness and the explicit rules fallback instead of promising per-target pinning ([#11555](https://github.com/diegosouzapw/OmniRoute/pull/11555)) — thanks @maxmad64bis +- **test(chatcore):** pin the response tool-name alias resolution against the ordering hazard that broke every Gemini/Antigravity MCP tool call in v3.8.49. `extractRequestToolIdentityMap` deletes `translatedBody._toolNameMap`, so the later read is always undefined and the ledger survives only through the `requestToolIdentityMap` fallback — removing that fallback previously left the entire tool-name suite green. The resolution moves into `resolveResponseToolNameMap()` next to the map it depends on, with a regression guard that fails without the recovery. No behaviour change. ([#11575](https://github.com/diegosouzapw/OmniRoute/pull/11575)) — thanks @amartinawi +- **docs(env):** document `OMNIROUTE_CORPUS_CACHE_SIZE` in `.env.example` and `ENVIRONMENT.md`, so the env/docs contract check stops failing Docs Gates on every branch ([#11576](https://github.com/diegosouzapw/OmniRoute/pull/11576)) — thanks @ntdatt812 +- **chore(stryker):** register `route-guard-tunnel-processes-local-only` and `universal-quota-aware-routing` in `tap.testFiles`, so their mutant kills count for the three modules they cover ([#11577](https://github.com/diegosouzapw/OmniRoute/pull/11577)) — thanks @ntdatt812 +- **test(authz):** pin `LOCAL_ONLY_API_GET_EXEMPTIONS` by exact membership instead of by entry count, so the guard names the offending path and also catches a substitution ([#11580](https://github.com/diegosouzapw/OmniRoute/pull/11580)) — thanks @ntdatt812 +- **test(build):** pin `resolveNextBuildBundlerFlag` to the `OMNIROUTE_USE_TURBOPACK` contract it implements, across runtimes, instead of asserting an unimplemented Bun override ([#11583](https://github.com/diegosouzapw/OmniRoute/pull/11583)) — thanks @ntdatt812 +- **test(claude-web):** the Claude Web transport test's barrel anchors no longer pin the exact construction spelling. #11421 made the executor map lazy, so `/"claude-web": new ClaudeWebExecutor\(\)/` stopped matching and the test went red on `release/v3.8.51` itself — a base-red gate every open PR inherited. The anchors now match the alias key and the class name and tolerate either spelling in between, and were checked to still fail when an alias is genuinely repointed at another executor ([#11585](https://github.com/diegosouzapw/OmniRoute/pull/11585)) — thanks @ntdat812 +- **test(models):** pin `qwen3.8-max` resolution per provider — two of the four catalogs now serve the bare id and no longer carry `-preview` ([#11590](https://github.com/diegosouzapw/OmniRoute/pull/11590)) — thanks @ntdatt812 +- **test(executors):** drop the stale 128-tool truncation assertion that contradicted the #11444 pass-through contract ([#11591](https://github.com/diegosouzapw/OmniRoute/pull/11591)) — thanks @ntdatt812 +- **test(token-health):** pin the current terminal-skip boundary — expired with retry budget is probed, `account_deactivated` stays skipped ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)) — thanks @ntdatt812 +- **test(models):** follow #11503's retarget of the `gemini-3-pro-high` alias to the hyphenated catalog id ([#11593](https://github.com/diegosouzapw/OmniRoute/pull/11593)) — thanks @ntdatt812 +- **refactor(video bridge):** extract per-video acquisition, whole-result caching, description, and metrics/abort/cleanup out of `VideoBridgeGuardrail.preCall` into a `processVideoPart` seam in a new `videoBridgePipeline.ts`, behind explicit `VideoMediaBrokerPort`, `VideoAudioTranscriptionPort`, and `VideoDrilldownPort` boundaries; `preCall` now only handles request traversal, policy, and response aggregation. The Video tab's FFmpeg/ffprobe runtime status is now an explicit `unknown` / `restricted` / `unavailable` / `available` state instead of a nullable boolean pair, fixing a case where an in-flight or failed probe was mislabeled as "install FFmpeg" ([#11657](https://github.com/diegosouzapw/OmniRoute/issues/11657)). ([#12016](https://github.com/diegosouzapw/OmniRoute/pull/12016)) +- **test(opencode-plugin):** add unit test coverage for `formatFreeBudget()` naming helper ([#11660](https://github.com/diegosouzapw/OmniRoute/pull/11660)) — thanks @f9td56dbgh-hub +- **docs(video):** clarify that the Video Bridge transcript `source` field (`client`, `embedded`, `audio-bridge`) is presently caller-declared and not yet server-verified — OmniRoute enforces the enum shape but does not cryptographically confirm that an `embedded`/`audio-bridge` label came from a server-owned extraction ([#11661](https://github.com/diegosouzapw/OmniRoute/issues/11661)). ([#12001](https://github.com/diegosouzapw/OmniRoute/pull/12001)) +- **chore(providers):** retire the distributed Raycast Relay and Hailuo Web integrations after a provenance audit confirmed substantial derivation from GPL-3.0 sources; the official GitHub and MiniMax API providers remain available. Migration 163 disables legacy connections and invalidates their active leases while preserving connection IDs, audit/history rows, quota snapshots, usage records, and fail-closed API-key connection policies. ([#11691](https://github.com/diegosouzapw/OmniRoute/pull/11691)) +- **chore(providers):** retire the Felo Web provider while its GPL-derived provenance remains on licensing HOLD ([#11698](https://github.com/diegosouzapw/OmniRoute/pull/11698)) — stale runtime and database references now fail closed while existing connection, lease, and usage history remains preserved for auditability. +- **fix(sse):** retire the Gemini Web image-generation bridge while provenance and licensing remain on HOLD, preserving Gemini Web chat/auth/tools and legitimate Gemini/Nano Banana image providers ([#11708](https://github.com/diegosouzapw/OmniRoute/pull/11708)) +- **chore(audio):** retire the built-in EdgeTTS provider and its 11-voice catalog while provenance/licensing review remains on HOLD; the generic `/v1/audio/speech` endpoint and the other speech providers remain available ([#11711](https://github.com/diegosouzapw/OmniRoute/pull/11711)) +- **fix(providers):** retire the unofficial Qwen Web integration and its `qwen-web`/`qw` identities while third-party provenance remains on HOLD ([#11713](https://github.com/diegosouzapw/OmniRoute/pull/11713)) — direct requests now fail closed with a sanitized HTTP 410 and combos skip the retired target, while migration guards preserve connection IDs, allowlists, usage/audit history and invalidate active leases. Supported Qwen API, Qwen Code, cloud, token-plan, audio, local and model-name surfaces remain available; restoring Qwen Web requires a qualified provenance/license decision. +- **fix(providers):** retire `microsoft-designer-web` and its `msdesigner` alias from runtime and catalogs while preserving stored credentials, provider history and allowlists as inactive records — source dependency on gpt4free's [`MicrosoftDesigner.py`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/g4f/Provider/needs_auth/MicrosoftDesigner.py) is proven at snapshot `0943d16eef757a44deb60651c07bbe5fa958cafe`, whose [`LICENSE`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/LICENSE) is GPL-3.0; expressive adaptation is probable, which records provenance/licensing risk without asserting a legal conclusion ([#11720](https://github.com/diegosouzapw/OmniRoute/pull/11720)). +- **docs(legal):** centralize pinned MIT notices for the vendored GCF codec and local flag assets ([#11726](https://github.com/diegosouzapw/OmniRoute/pull/11726)) +- Split the 2,952-line `adobeFireflyClient.ts` into nine focused modules (catalog, credentials, payload, ARP session, upload, responses, IMS, account, poll) behind an unchanged barrel — no consumer edits and no public API change (#11728). — thanks @MumuTW +- **fix(dashboard):** replaced the Model Lockout card's unprovenanced notification MP3 with a short, locally synthesized Web Audio chime ([#11731](https://github.com/diegosouzapw/OmniRoute/pull/11731)) — both toggles retain optional audible feedback without shipping replacement media or third-party code, and unsupported, suspended, or rejected audio contexts fail open without blocking the setting change. +- **chore(deepseek-web):** remove the provenance-unresolved DeepSeek PoW WASM binary and its runtime loader/tracing while retaining the existing JavaScript solver (slower at high difficulty) ([#11732](https://github.com/diegosouzapw/OmniRoute/pull/11732)). +- **chore(assets):** remove four unused provider assets whose redistribution provenance could not be verified while retaining the active Freebuff SVG ([#11735](https://github.com/diegosouzapw/OmniRoute/pull/11735)) +- **docs(assets):** retired two raster images whose redistribution provenance could not be verified ([#11736](https://github.com/diegosouzapw/OmniRoute/pull/11736)) — PicoClaw documentation now reuses the existing SVG, while Zoo Code remains a text link without an image. +- **docs(legal):** add pinned notices for six LobeHub-derived provider SVGs and 65 byte-exact theSVG assets, retaining registry claims as unverified metadata with explicit trademark and HOLD boundaries ([#11737](https://github.com/diegosouzapw/OmniRoute/pull/11737)). +- **chore(quality):** added a versioned 225/225 provider-asset provenance manifest and blocking drift gate ([#11743](https://github.com/diegosouzapw/OmniRoute/pull/11743)) — the inventory pins magic MIME and SHA-256 for every physical file, records immutable source matches for 71 assets, keeps 69 probable and 85 unresolved entries explicit but non-blocking, and treats provenance as source evidence rather than copyright or trademark clearance. +- **refactor(mitm):** replace four previously derived Traffic Inspector utilities — conversation normalization, SSE merging, secret masking, and header sanitization — with independent clean-room implementations based on public provider schemas, WHATWG SSE, and RFC 6750, while keeping the two independently classified passthrough surfaces unchanged ([#11748](https://github.com/diegosouzapw/OmniRoute/pull/11748)). +- Added a unit test that fails seven days before any dated pack under `config/` (`validUntil` and sibling keys) lapses, naming the file and key. The Alibaba free-tier pack expired on 2026-08-27 and turned every PR red the next morning with no commit involved; renewal now happens on someone's terms, not the clock's. ([#11891](https://github.com/diegosouzapw/OmniRoute/pull/11891)) +- `check:workflows` now fails (under `--strict`/`--ratchet`) when any job routed to a self-hosted runner publishes with `--provenance` — npm rejects that with `422` at the registry, which in v3.8.50 only surfaced after the tag and Docker images were public. ([#11895](https://github.com/diegosouzapw/OmniRoute/pull/11895)) +- `scripts/ops/runner-janitor.sh` now proves a path is idle with one `lsof` snapshot and removes stale leftovers itself (tmpfs after 3 h — it is RAM — disk after 24 h), kills orphan `next-build` processes, prunes checkouts of stopped runners, and alerts on memory pressure; `--dry-run` shows exactly what it would do. `docs/ops/RUNNER_BOX.md` reconciled to the measured box (31 GB, 10 listeners). ([#11893](https://github.com/diegosouzapw/OmniRoute/pull/11893)) +- The `next-build` artefact (1.3 GB) is now written and read under `$RUNNER_TEMP` (per-runner, on disk) instead of `/tmp`, which on the self-hosted pool is a 12 GB tmpfs in RAM. Landing it there took 27–32 of the publish job's 76 minutes, and the fixed `/tmp/e2e-build.tar.gz` name let E2E jobs on different runners overwrite each other's download. ([#11896](https://github.com/diegosouzapw/OmniRoute/pull/11896)) +- The CI `build` job now runs in two concurrency lanes — `main` and pull requests — so a release build is never queued behind (or OOM-killed beside) PR builds on the self-hosted pool, which holds one `next-build` comfortably and two at the edge. ([#11901](https://github.com/diegosouzapw/OmniRoute/pull/11901)) +- Re-freeze the ESLint suppressions on `release/v3.8.51` from a clean-room measurement (2 stale file entries pruned, 55 pre-existing `no-explicit-any` in six new files frozen under #11924) and drop the dead `GPT_SIZE_MAP` constant orphaned by the Adobe Firefly client split, so `No new ESLint warnings` stops failing every PR with exit 2 (Refs #11924) ([#11955](https://github.com/diegosouzapw/OmniRoute/pull/11955)) +- Type the 55 `no-explicit-any` sites that had been frozen under #11924 — four redundant casts in `socksConnectorWithFamily.ts` (undici/socks types already accept them) and the mocks/fixtures of the socks-timeout and isFree suites — and drop their suppression entries; the ESLint ratchet shrinks from 5487 to 5432 (Closes #11924) ([#11975](https://github.com/diegosouzapw/OmniRoute/pull/11975)) +- Take the two hosted-runner builds off the PR rail: `Build App` (`build.yml`) is `workflow_dispatch`-only and `DAST smoke (PR)` runs only for PRs into `main` — the 7 GB hosted VM cannot build `release/v3.8.51` in any profile (19/30 red, VM shutdown ~8 min into `next build`) and both checks had turned into permanent noise on every release PR; the bundle stays validated by `ci.yml` on `main` and by `nightly-release-green` on `release/**` (Closes #11946) ([#11962](https://github.com/diegosouzapw/OmniRoute/pull/11962)) +- Move the four nightly jobs that build the backend (`nightly-schemathesis`, `nightly-llm-security` promptfoo + garak, `nightly-resilience` axe-a11y) off the hosted 7 GB runner — where they died on `release/v3.8.51` unseen — onto the box's new `omni-light` pool (two listeners), and document the reshaped fleet (4 active OmniRoute listeners: 2 `omni-build` + 2 `omni-light`, janitor ceiling 4) (Closes #11965) ([#11967](https://github.com/diegosouzapw/OmniRoute/pull/11967)) +- Stop painting every fork PR into `release/**` red: `quality.yml` `Build (advisory)` is skipped (GitHub still reports `continue-on-error` failures as check FAILURE). Hosted `ubuntu-latest` cannot finish `npm run build` on this tree — same class as #11962 taking `build.yml` off the PR rail. `docker-publish.yml` amd64 now runs on the `.113` `omni-build` pool (31 GB / 32 cores, two listeners) with Turbopack, shares the `heavy-build-main` lane with `ci.yml` `Build` so it queues instead of becoming a third heavy, and keeps per-ref concurrency (a merge storm was starting 8 concurrent OOM builds). arm64 stays on `ubuntu-24.04-arm` with webpack — there is no ARM box. Fallback when `USE_VPS_RUNNER` is off: hosted amd64 + webpack (#11976). ([#12021](https://github.com/diegosouzapw/OmniRoute/pull/12021)) +- `docker-publish.yml` builds with webpack (`OMNIROUTE_USE_TURBOPACK=0`) on **both** arches, including amd64 on the `.113` `omni-build` pool. Turbopack had the RAM (31 GB, `omniroute-113-6`) and still panicked (`TurbopackInternalError: there must be a path to a root`, run 33253576569, 26 min in). The same tree's arm64 webpack build on hosted `ubuntu-24.04-arm` succeeded (run 33264823398). Dockerfile already documents webpack as the Docker escape hatch (#11976). ([#12050](https://github.com/diegosouzapw/OmniRoute/pull/12050)) +- **docs(free-tier):** declare the counting vs deciding regimes for "is it free?" and guard the deciding path from DB-backed catalog resolution ([#12226](https://github.com/diegosouzapw/OmniRoute/pull/12226)) — thanks @maxmad64bis +- **docs(auto-combo):** The mode pack table in `docs/routing/AUTO-COMBO.md` now lists all six shipped packs with every weight each one sets, replacing a four-pack table whose numbers had also drifted from the source. It states plainly that no pack sets `quality`, so selecting any pack silences the observed-quality signal. Six more documents that quote the scoring factor count joined the `check:docs-counts` gate, which caught five stale claims — including one naming nine factors that do not exist — and two stale mode pack counts. The dashboard routing panel, which offered four of the six packs and labelled the default strategy "6-Factor Scoring", is now covered by a test; the two packs it was missing are `reliability-first` and `chaos-mode`, the latter labelled as the fault-injection profile it is rather than as one more routing preference ([#12316](https://github.com/diegosouzapw/OmniRoute/pull/12316)) — thanks @maxmad64bis +- **chore(quality):** rebaseline `src/lib/db/apiKeys.ts` for the ACL the key-creation path now preserves ([#12352](https://github.com/diegosouzapw/OmniRoute/pull/12352)) ([#12673](https://github.com/diegosouzapw/OmniRoute/pull/12673)) — thanks @KrzysiekSko +- **chore(providers):** bump the Claude Code wire identity and the Devin bridge image pin from `2.1.220` to `2.1.258` ([#12402](https://github.com/diegosouzapw/OmniRoute/pull/12402)) — thanks @ggiak ([#12604](https://github.com/diegosouzapw/OmniRoute/pull/12604)) +- **docs(env):** align `.env.example`, the README Bun section, and the troubleshooting guide with the code: `OMNIROUTE_USE_TURBOPACK` also governs `npm run build` (not dev-only), `bun run build` follows that flag instead of auto-selecting Webpack, `OMNIROUTE_CHAT_MAX_HEAVY_IN_FLIGHT` is unset by default (no request-count cap), and the structural `503 chat_admission_busy` message matches `chatAdmissionResponses.ts` (#12404 — thanks @pacocartones) +- **chore(electron):** upgrade the desktop app to Electron 44 (Chromium 152, Node 24.18.1) ([#12217](https://github.com/diegosouzapw/OmniRoute/pull/12217)). **Requires macOS 13 (Ventura) or later** — Chromium dropped macOS 12 (Monterey), so Monterey users must stay on an earlier OmniRoute desktop build. Windows and Linux are unaffected; the app already shipped only x64/arm64, so Electron 44 dropping 32-bit builds changes nothing. Removes the `openAsHidden`/`wasOpenedAsHidden` login-item fields deleted in Electron 44 — hidden autostart continues to work through the `--hidden` argument registered with the login item ([#12554](https://github.com/diegosouzapw/OmniRoute/pull/12554)) — thanks @app/dependabot +- **chore(quality):** rebaseline `src/sse/handlers/chat.ts` for the effective-input persistence the continuation fix needs ([#12641](https://github.com/diegosouzapw/OmniRoute/pull/12641)) ([#12680](https://github.com/diegosouzapw/OmniRoute/pull/12680)) — thanks @hartmark +- `Coverage` job on `ci.yml`: the informational Codecov upload gets its own 5-minute ceiling and `continue-on-error`, and the job budget grows from 20 to 30 minutes (the 8-shard c8 merge alone takes ~10) — a stalled upload no longer ends the job `cancelled` and drags a fully green `main` run's conclusion down with it ([#11972](https://github.com/diegosouzapw/OmniRoute/pull/11972)) +- CI hardening on the PR rail: the four `Unit Tests fast-path` shards get `timeout-minutes: 30` (a hung shard held a PR for 64 min instead of GitHub's 6 h default) and both ESLint file caches lose their `restore-keys` fallback, so a cache built under another suppressions file or lint config can no longer report stale verdicts (Refs #11600, #11924) ([#11963](https://github.com/diegosouzapw/OmniRoute/pull/11963)) +- Every CI job that runs a `next build` (`build`, the npm `publish`, both release-green validations) now targets the `omni-build` runner label, which only two of the eight self-hosted runners carry. The box holds one build comfortably and two at the edge; a third now queues on GitHub instead of being OOM-killed by the kernel. ([#11932](https://github.com/diegosouzapw/OmniRoute/pull/11932)) +- **refactor(combo):** move `handleRoundRobinCombo` (and `resolveTargetTokenLimit`) into `open-sse/services/combo/roundRobinCombo.ts`. `combo.ts` drops from 2164 to 1014 lines (`split("\n").length`); the leaf is 1199 (under the 1200 new-file cap). The round-robin call site uses a dynamic `import()` so `releaseStickyPinOnFailure` / `clearStaleLKGP` can stay exported from `combo.ts` without a static cycle. Skip / sticky / semaphore / safety-timer behavior is unchanged. ([#12811](https://github.com/diegosouzapw/OmniRoute/pull/12811)) — thanks @HouMinXi +- **chore(quality):** rebaseline the file-size caps the error-boundary campaign grew past (`open-sse/executors/codex.ts`, `open-sse/vendor/codex-chatgpt-web/bridge.ts`, both via [#12444](https://github.com/diegosouzapw/OmniRoute/pull/12444)) ([#12654](https://github.com/diegosouzapw/OmniRoute/pull/12654)) +- **chore(quality):** rebaseline the file-size caps the hartmark batch grew past (`combos/page.tsx` via [#12355](https://github.com/diegosouzapw/OmniRoute/pull/12355), `open-sse/services/combo.ts` via [#12338](https://github.com/diegosouzapw/OmniRoute/pull/12338)) ([#12623](https://github.com/diegosouzapw/OmniRoute/pull/12623)) — thanks @hartmark +- **chore(quality):** rebaseline the file-size caps the HouMinXi batch grew past when its PRs stacked (`providers/page.tsx`, `chatCore.ts`, `accountFallback.ts`) — each PR measured correctly in isolation, none saw the stacking ([#12619](https://github.com/diegosouzapw/OmniRoute/pull/12619)) +- **chore(quality):** rebaseline `open-sse/services/combo.ts` for the reset-aware scoring the HouMinXi batch stacked ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) ([#12678](https://github.com/diegosouzapw/OmniRoute/pull/12678)) — thanks @HouMinXi +- **docs:** sync provider catalog count (direct commit `4f11b2ae3d`) +- **docs(skills):** sync omni-inference search fallback (direct commit `b4e76a7ed9`) +- **chore(lint):** prune two stale base suppressions (direct commit `1325047d56`) +- **test(auto-combo):** tolerate full-suite rotation load (direct commit `ad3e293f9f`) +- **test:** drain inherited quality snapshot reds (direct commit `21ed68d8ac`) +- **chore(quality):** rebaseline measured CLI bundle growth (direct commit `d35b3f9779`) +- **test(ui):** reproduce invalid AnySearch icon alias (direct commit `12cc6ca834`) +- **test(translator):** fix the relative imports of the relocated deferred-finish test (direct commit `777d9d1629`) +- **docs(counts):** bring the documented provider and migration counts to the measured values (direct commit `e70bea30e9`) +- **test(security):** drop the guard-target set left unused by the sync (direct commit `a32ba0e362`) +- **chore(db):** renumber model_capabilities migration 163 → 169 (cross-branch collision with 163_radar_feed_cache_generated_at) (direct commit `70f5d4cbf9`) +- **docs(cli):** document CLI_PRIME_AGENT_BIN and correct the CLI Agents count (direct commit `9058e39b61`) +- **chore(deps):** refresh runtimes and adopt ESLint 10 ([#11259](https://github.com/diegosouzapw/OmniRoute/pull/11259)) — thanks @backryun +- **refactor(local-corpus):** implement dynamic root resolution and LRU cache ([#11491](https://github.com/diegosouzapw/OmniRoute/pull/11491)) — thanks @raheemuddin786 +- **chore(quality):** prune stale eslint-suppressions entries ([#11596](https://github.com/diegosouzapw/OmniRoute/pull/11596)) +- **chore(ci):** unblock quality gates - env-doc sync + stryker tap.testFiles registration (#9282) ([#11609](https://github.com/diegosouzapw/OmniRoute/pull/11609)) — thanks @Meet6338-X +- **chore(ci):** point the circular-deps gate at dpdm's real JS entrypoint ([#11615](https://github.com/diegosouzapw/OmniRoute/pull/11615)) — thanks @benzntech +- **chore(quality):** rebaseline file-size for #11448/#11495/#11561 growth ([#11630](https://github.com/diegosouzapw/OmniRoute/pull/11630)) +- **deps:** realign onnxruntime-node pin to @huggingface/transformers' 1.24.3 lockstep ([#11633](https://github.com/diegosouzapw/OmniRoute/pull/11633)) — thanks @jonlwheat2-gif +- **test(autoCombo):** port models_dev_tier #11508 guard from node:test to vitest ([#11635](https://github.com/diegosouzapw/OmniRoute/pull/11635)) — thanks @jonlwheat2-gif +- **chore(quality):** rebaseline file-size for #11083/#11631/#11628 growth ([#11636](https://github.com/diegosouzapw/OmniRoute/pull/11636)) +- **chore(lint):** prune stale ESLint suppressions (base-red #11449) ([#11644](https://github.com/diegosouzapw/OmniRoute/pull/11644)) — thanks @jonlwheat2-gif +- **docs(changelog):** aggregate the ten v3.8.50 fragments into CHANGELOG.md ([#11683](https://github.com/diegosouzapw/OmniRoute/pull/11683)) +- **chore(search):** drop unreachable credential sweep after #11642 ([#11687](https://github.com/diegosouzapw/OmniRoute/pull/11687)) — thanks @HouMinXi +- **ci:** add API route TypeScript regression gate ([#11705](https://github.com/diegosouzapw/OmniRoute/pull/11705)) — thanks @Sabeekhann +- **docs(dedup):** correct the CodeQL claim on the tenant-namespace comment ([#11712](https://github.com/diegosouzapw/OmniRoute/pull/11712)) +- **docs(changelog):** add consolidated v3.8.50 stats, top-25 ranking and a .mailmap ([#11715](https://github.com/diegosouzapw/OmniRoute/pull/11715)) +- **docs:** recommend gstack for AI-assisted workflows ([#11770](https://github.com/diegosouzapw/OmniRoute/pull/11770)) — thanks @santosraju99-hub +- **chore(quality):** tighten complexity/cognitive-complexity ratchets to the current tip; land the missed gateways.ts rebaseline ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **docs(i18n):** finish Freepik → Magnific rebrand in locale strings and README ([#11772](https://github.com/diegosouzapw/OmniRoute/pull/11772)) — thanks @turbolego +- **refactor(auto-combo):** fix divergent scoring in combo health reporting ([#11854](https://github.com/diegosouzapw/OmniRoute/pull/11854)) — thanks @jacobsparts +- **chore(changelog):** add missing fragments for #11919/#11918/#11916 ([#11938](https://github.com/diegosouzapw/OmniRoute/pull/11938)) +- **test(check):** escape the runs-on fixture with JSON.stringify, not a quote-only replace ([#11942](https://github.com/diegosouzapw/OmniRoute/pull/11942)) (direct commit `fb7445eaa3`) +- **test(release):** align five suites with the contracts #11933, #11919 and #11876 shipped on release/v3.8.51 ([#11944](https://github.com/diegosouzapw/OmniRoute/pull/11944)) +- **chore(quality):** remove unreachable code and restore test discovery ([#11950](https://github.com/diegosouzapw/OmniRoute/pull/11950)) — thanks @backryun +- **docs(agents):** sync-back landings are fast-forward, never squash ([#11964](https://github.com/diegosouzapw/OmniRoute/pull/11964)) +- **test(infra):** retry recursive temp-dir removal instead of failing a shard on ENOTEMPTY (#11966) ([#11968](https://github.com/diegosouzapw/OmniRoute/pull/11968)) +- **docs(ops):** the .113 heavy-build ceiling is one runner, not two ([#12048](https://github.com/diegosouzapw/OmniRoute/pull/12048)) +- **test(ui):** correct inactive auto-fetch expectation ([#12098](https://github.com/diegosouzapw/OmniRoute/pull/12098)) — thanks @RaviTharuma +- **docs:** document native dependency check escape hatch ([#12101](https://github.com/diegosouzapw/OmniRoute/pull/12101)) — thanks @RaviTharuma +- **test(providers):** regenerate the translate-path golden for OrcaRouter (#11923) ([#12118](https://github.com/diegosouzapw/OmniRoute/pull/12118)) +- **chore(quality):** velocity phase — loosen every numeric baseline by 20% until v4.0, monitor headroom nightly ([#12125](https://github.com/diegosouzapw/OmniRoute/pull/12125)) +- **chore(lint):** batch 0 of #12146 — type the call-log-cap sqlite rows instead of 45 as-any casts ([#12157](https://github.com/diegosouzapw/OmniRoute/pull/12157)) +- **chore(lint):** batch 4 of #12146 — shared/components react-hooks violations resolved ([#12159](https://github.com/diegosouzapw/OmniRoute/pull/12159)) +- **chore(lint):** batch 1 of #12146 — dashboard/cli-code react-hooks violations resolved ([#12160](https://github.com/diegosouzapw/OmniRoute/pull/12160)) +- **chore(lint):** batch 3 of #12146 — dashboard/settings react-hooks violations resolved ([#12162](https://github.com/diegosouzapw/OmniRoute/pull/12162)) +- **chore(lint):** batch 2 of #12146 — resolve the react-hooks compiler violations in dashboard/providers ([#12163](https://github.com/diegosouzapw/OmniRoute/pull/12163)) +- **chore(quality):** register search-432 cooldown test in stryker tap.testFiles ([#12170](https://github.com/diegosouzapw/OmniRoute/pull/12170)) +- **chore(lint):** batch 5 of #12146 — combos, endpoint, provider-stats, api-manager and costs react-hooks violations resolved ([#12174](https://github.com/diegosouzapw/OmniRoute/pull/12174)) +- **build:** add contributor fast profile ([#12192](https://github.com/diegosouzapw/OmniRoute/pull/12192)) — thanks @rafacpti23 +- **docs(readme):** README touch-ups ([#12193](https://github.com/diegosouzapw/OmniRoute/pull/12193)) ([#12194](https://github.com/diegosouzapw/OmniRoute/pull/12194)) ([#12202](https://github.com/diegosouzapw/OmniRoute/pull/12202)) — thanks @rafacpti23 +- **build:** force the webpack bundler in the contributor build profile ([#12197](https://github.com/diegosouzapw/OmniRoute/pull/12197)) — thanks @rafacpti23 +- **build:** skip standalone packaging in the contributor profile ([#12198](https://github.com/diegosouzapw/OmniRoute/pull/12198)) — thanks @rafacpti23 +- **docs(audit):** align every published number with the code and harden check:docs-counts ([#12200](https://github.com/diegosouzapw/OmniRoute/pull/12200)) +- **build:** stub the instrumentation entrypoints in the contributor profile ([#12203](https://github.com/diegosouzapw/OmniRoute/pull/12203)) — thanks @rafacpti23 +- **build:** omit the standalone output target for contributor builds ([#12204](https://github.com/diegosouzapw/OmniRoute/pull/12204)) — thanks @rafacpti23 +- **chore(lint):** batch 6 of #12146 — memory, radar, audit, analytics, cache, usage, activity, home and RequestLoggerV2 react-hooks violations resolved ([#12208](https://github.com/diegosouzapw/OmniRoute/pull/12208)) +- **docs(diagrams):** rename number-carrying diagram files to stable names ([#12210](https://github.com/diegosouzapw/OmniRoute/pull/12210)) +- **docs(dashboard):** redraw onboarding tier-flow SVGs for the real 4-tier model ([#12211](https://github.com/diegosouzapw/OmniRoute/pull/12211)) +- **docs(api):** document every implemented route in openapi.yaml (276 → 692 paths) ([#12212](https://github.com/diegosouzapw/OmniRoute/pull/12212)) +- **chore(lint):** batch 7 of #12146 — final src tail: 37 react-hooks violations across 33 files resolved ([#12244](https://github.com/diegosouzapw/OmniRoute/pull/12244)) +- Revert "docs: recommend gstack for AI-assisted workflows (#11770)" ([#12248](https://github.com/diegosouzapw/OmniRoute/pull/12248)) ([#12249](https://github.com/diegosouzapw/OmniRoute/pull/12249)) +- **docs:** Chaos Mode setup guide + weighted strategy semantics ([#12250](https://github.com/diegosouzapw/OmniRoute/pull/12250)) +- **docs(agents):** protected-surface merge rule — operator approval for agent-instruction files ([#12253](https://github.com/diegosouzapw/OmniRoute/pull/12253)) +- **chore(quality):** register native-codex turn-pin tests in stryker tap.testFiles ([#12263](https://github.com/diegosouzapw/OmniRoute/pull/12263)) +- **chore(quality):** dedupe tap.testFiles entries added by racing base-red fixes ([#12265](https://github.com/diegosouzapw/OmniRoute/pull/12265)) +- **docs(readme):** introduce OmniRouteTray — the macOS menu-bar companion ([#12276](https://github.com/diegosouzapw/OmniRoute/pull/12276)) — thanks @ggiak +- **chore(proxy):** purge the legacy 1proxy residue — sync/rotator modules, dead DB exports, dead settings tab and flag, docs (#12091) ([#12290](https://github.com/diegosouzapw/OmniRoute/pull/12290)) +- **chore(quality):** baseline-headroom skips generated and vendored files in the fileSize worst-file signal ([#12291](https://github.com/diegosouzapw/OmniRoute/pull/12291)) +- **refactor(video):** unify the JPEG frame data-URI contract ([#12322](https://github.com/diegosouzapw/OmniRoute/pull/12322)) +- **chore(deps):** freeze onnxruntime-node and eslint-plugin-react-hooks out of dependabot groups ([#12329](https://github.com/diegosouzapw/OmniRoute/pull/12329)) +- **test(executors):** restore the #10986 reasoning-only fallback guards ([#12364](https://github.com/diegosouzapw/OmniRoute/pull/12364)) — thanks @pacocartones +- **chore(quality):** re-tighten the file-size ratchet to the real LOC (plan 3.8.52 task 0) ([#12411](https://github.com/diegosouzapw/OmniRoute/pull/12411)) +- **test(grok):** format web executor suite ([#12412](https://github.com/diegosouzapw/OmniRoute/pull/12412)) +- **chore(quality):** ratchet open-sse typecheck baseline to zero ([#12418](https://github.com/diegosouzapw/OmniRoute/pull/12418)) +- **test(build):** guard the artifact path policy arrays against duplicates ([#12422](https://github.com/diegosouzapw/OmniRoute/pull/12422)) +- **chore(lint):** adopt eslint-plugin-react-hooks 7.1.1 ([#12428](https://github.com/diegosouzapw/OmniRoute/pull/12428)) +- **chore(providers):** remove a keyless provider integration at its operator's request ([#12440](https://github.com/diegosouzapw/OmniRoute/pull/12440)) +- **docs(providers):** register providers removed at their operator's request and guard against reintroduction ([#12478](https://github.com/diegosouzapw/OmniRoute/pull/12478)) +- **chore(quality):** base fixes — stryker tap.testFiles + node_modules cache key ([#12482](https://github.com/diegosouzapw/OmniRoute/pull/12482)) +- **docs(arch):** one-process recipe for tens of long /v1/responses ([#12493](https://github.com/diegosouzapw/OmniRoute/pull/12493)) — thanks @RaviTharuma +- **chore(quality):** tighten the CodeQL ratchet baseline from 11 to 6 ([#12530](https://github.com/diegosouzapw/OmniRoute/pull/12530)) +- **chore(deps):** pin browserslist override to ^4.28.8 ([#12592](https://github.com/diegosouzapw/OmniRoute/pull/12592)) — thanks @KooshaPari +- **chore(deps):** bump fast-uri to 3.1.7 in the electron lockfile ([#12601](https://github.com/diegosouzapw/OmniRoute/pull/12601)) +- **docs(quality):** document how the CodeQL ratchet refreshes and how to tighten it ([#12611](https://github.com/diegosouzapw/OmniRoute/pull/12611)) +- **test(ci):** pin the openapi-security-tiers two-arm contract with an executing gate test (#12581) ([#12652](https://github.com/diegosouzapw/OmniRoute/pull/12652)) +- **docs(gamification):** align XP Rewards table with code (#12501) ([#12667](https://github.com/diegosouzapw/OmniRoute/pull/12667)) — thanks @KooshaPari +- **refactor(combo):** split executeTarget into gates, attempt, and loop ([#12746](https://github.com/diegosouzapw/OmniRoute/pull/12746)) — thanks @HouMinXi +- **docs(security):** document socket.yml scanner config + CI workflow link (#12575) ([#12764](https://github.com/diegosouzapw/OmniRoute/pull/12764)) — thanks @KooshaPari +- **chore(ci):** guard commit identity in pre-commit to stop author misattribution ([#12772](https://github.com/diegosouzapw/OmniRoute/pull/12772)) +- **deps:** 17 Dependabot bumps — bump electron from 43.4.0 to 43.4.1 in /electron ([#11426](https://github.com/diegosouzapw/OmniRoute/pull/11426)); bump the development group across 1 directory with 8 updates ([#11428](https://github.com/diegosouzapw/OmniRoute/pull/11428)); bump the production group across 1 directory with 11 updates ([#11440](https://github.com/diegosouzapw/OmniRoute/pull/11440)); chore(deps): bump github/codeql-action/init from 4.37.7 to 4.37.8 ([#11925](https://github.com/diegosouzapw/OmniRoute/pull/11925)); chore(deps): bump github/codeql-action from 4.37.7 to 4.37.8 ([#11926](https://github.com/diegosouzapw/OmniRoute/pull/11926)); chore(deps): bump github/codeql-action/analyze from 4.37.7 to 4.37.8 ([#11927](https://github.com/diegosouzapw/OmniRoute/pull/11927)); build(deps): bump github/codeql-action/init from 4.37.8 to 4.37.9 ([#12345](https://github.com/diegosouzapw/OmniRoute/pull/12345)); build(deps): bump github/codeql-action/analyze from 4.37.8 to 4.37.9 ([#12346](https://github.com/diegosouzapw/OmniRoute/pull/12346)); bump the development group across 1 directory with 2 updates ([#12347](https://github.com/diegosouzapw/OmniRoute/pull/12347)); build(deps): bump github/codeql-action from 4.37.8 to 4.37.9 ([#12349](https://github.com/diegosouzapw/OmniRoute/pull/12349)); bump browserslist from 4.28.2 to 4.28.8 ([#12396](https://github.com/diegosouzapw/OmniRoute/pull/12396)); bump the production group across 1 directory with 4 updates ([#12399](https://github.com/diegosouzapw/OmniRoute/pull/12399)); bump @xmldom/xmldom ([#12500](https://github.com/diegosouzapw/OmniRoute/pull/12500)); bump qs from 6.15.2 to 6.16.0 ([#12512](https://github.com/diegosouzapw/OmniRoute/pull/12512)); bump @xmldom/xmldom from 0.9.10 to 0.9.12 ([#12513](https://github.com/diegosouzapw/OmniRoute/pull/12513)); bump fast-uri from 3.1.5 to 3.1.7 ([#12514](https://github.com/diegosouzapw/OmniRoute/pull/12514)); bump @humanfs/node from 0.16.7 to 0.16.8 ([#12515](https://github.com/diegosouzapw/OmniRoute/pull/12515)) +- Clear the `release/v3.8.51` `check:agent-skills-sync` base-red: regenerate `skills/cli-tunnel/SKILL.md` so the `tunnel create [type]` positional that #13009 taught the generator to read is reflected in the committed skill. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` typecheck base-red from #12731: the new `Mode pack ranking gates` candidates in `open-sse/services/autoCombo/__tests__/autoCombo.test.ts` omitted the required `provider`, `model` and `errorRate` fields of `ProviderCandidate`, failing `check:open-sse-typecheck` (and with it `Fast Quality Gates`) on every open PR. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` docs-sync base-red: EURouter (#13025) and GreenPT (#13024) took the live provider count to 358, leaving 7 STRICT drifts (`PROVIDER_REFERENCE.md`, 4 diagrams, 2 tier-flow images) plus the `AGENTS.md` / `llm.txt` / `package.json` count claims stale. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- **test(models):** Pin `CATALOG_BUILD_TIMEOUT_MS` in the #12058 canonical-catalog regression so a cold catalog build racing the 8s default bound no longer flakes the suite with a 500 ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Allow `@omniroute/opencode-plugin-v2/` in the npm pack policy — [#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870) shipped the v2 plugin beside its v1 sibling without widening the allowlist, so every packed file under it read as an unexpected artifact and failed `check:pack-policy` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Point the release-green validator's pack gate at the tree it is validating — the build-provenance guard defaulted to `origin/main`, which a release branch tip can never be an ancestor of mid-cycle, so `Package artifact` reported a false red on every run and the tarball boot-smoke cascaded off it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **docs(checks):** keep doc counts honest — headings, rankings, catalog, weights, quality gate and scoring diagram now covered ([#12507](https://github.com/diegosouzapw/OmniRoute/pull/12507)) — thanks @maxmad64bis +- **chore(lifecycle):** `check:model-lifecycle` now also diffs `DEFAULT_DEGRADATION_MAP` (the background-task redirect table) against the vendor lifecycle snapshot, refusing a retired id as source or target, with a table-driven unit test beside it. Three rows whose source the vendor had retired — `claude-sonnet-4-20250514`, `gemini-3-pro-preview` and `gpt-5.1-codex` (whose target `gpt-5.1-codex-mini` is retired too) — were dead code, since `checkLifecycle` answers 410 before the redirect runs; they are dropped (#12535 — thanks @pacocartones) +- **test(compression):** cover idle worker eviction at the resource level — the pool must call `terminate()` and must not retain the worker's `MessagePort`, complementing the `exit`-event assertion added with the fix ([#13371](https://github.com/diegosouzapw/OmniRoute/pull/13371)) +- **chore(quality):** raise the `imageGeneration.ts` file-size ceiling for the image-only-model guard that clears the #12945 base-red ([#13392](https://github.com/diegosouzapw/OmniRoute/pull/13392)) +- **chore(quality):** type the combo-test route's JSON response bodies instead of casting them to `any`, drop the now-empty suppression entry, and remove the `hasPerModelQuota` import `chatCore.ts` stopped using when the failure-classification helper was extracted ([#13379](https://github.com/diegosouzapw/OmniRoute/pull/13379)) +- **chore(electron):** the desktop bundle no longer ships the root-level files of every translated docs mirror (`README.md`, `llm.txt`, `CLAUDE.md`, `GEMINI.md`, `CONTRIBUTING.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`) — the packaged app only reads `docs/i18n//docs/**`, which stays. Saves ~11 MB on top of the translated CHANGELOGs already pruned. (#0000) ([#13235](https://github.com/diegosouzapw/OmniRoute/pull/13235)) +- **chore(quality):** retighten the `src/sse/handlers/chat.ts` unused-vars suppression count to the 8 violations that actually remain ([#13385](https://github.com/diegosouzapw/OmniRoute/pull/13385)) +- Bring the documented counts back in line with the code: 171 migrations (was 169) and 20 routing strategies (was 19), plus the regenerated `cli-tunnel` skill reference. ([#12970](https://github.com/diegosouzapw/OmniRoute/pull/12970)) +- Update the documented migration count to 172 after the call-logs provider-stats indexes landed. ([#13209](https://github.com/diegosouzapw/OmniRoute/pull/13209)) +- **chore(ci):** two gates that close the blind spots behind the exclusions above. `check:vitest-exclusions` requires every Vitest exclusion to name a tracking issue and to appear in `config/quality/vitest-exclusions.json` — the previous list grew to 62 files behind a comment pointing at an issue that had been closed for a month. `check-new-key-coverage` requires a key newly added to `en.json` to reach every locale; the existing coverage gate is a percentage floor per locale, so eleven absent keys out of ~13,000 left it at 99.9% while a whole feature shipped untranslated in nine languages. Both are diff-aware, so pre-existing debt stays frozen and neither needed a migration to turn on. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **build:** root layout no longer loads Inter from `next/font/google`, so a production image build does not need fonts.googleapis.com ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **docs(i18n):** bring the eight new `llm.txt` mirrors (km, kn, ml, my, ne, or, pa, si) up to the current provider and migration counts, so `check-docs-sync` stops rejecting every commit ([#13674](https://github.com/diegosouzapw/OmniRoute/pull/13674)) +- **refactor(ui):** flow surfaces (home topology, combo live studio, compression cockpit/waterfall/nodes, token-health badge) express state through the theme-aware `--orch-status-*` tokens instead of fixed dark-mode hex, so green/red/amber/grey stay legible in the light theme; dark mode is byte-identical. Categorical palettes (routing-strategy hues, compression-layer pills, provider brand colors) deliberately stay hex ([#12378](https://github.com/diegosouzapw/OmniRoute/issues/12378)) ([#13203](https://github.com/diegosouzapw/OmniRoute/pull/13203)) +- **chore(tests):** 51 test files that had been excluded from Vitest are running again, restoring roughly 350 assertions to the blocking suite. Each was measured individually first: of the 62 files parked behind the `// #8618 — pre-existing failure` comment, 51 pass against the current tree with no source change, so the exclusions had outlived the failures they were added for. The 11 that genuinely still fail stay excluded, but now point at a live tracker (#13204) rather than at #8618, which was closed in August while the list it tracked kept growing. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **chore(quality):** drop two ESLint suppression entries whose violations no longer exist, so `eslint --suppressions-location` stops rejecting every commit that touches the surrounding files ([#13384](https://github.com/diegosouzapw/OmniRoute/pull/13384)) +- **chore(release):** bring main's post-tag fixes into release/v3.8.51 — sync-back step 2/2 (direct commit `21c488f210`) +- **chore(deps):** patch toml and esbuild in the opencode plugin lockfiles ([#12965](https://github.com/diegosouzapw/OmniRoute/pull/12965)) +- **docs(changelog):** reconcile the v3.8.51 living section — fold 366 fragments, cover every cycle commit, credit every contributor ([#12971](https://github.com/diegosouzapw/OmniRoute/pull/12971)) +- **test(plugin):** wait for the v2 lazy-refresh tier instead of sleeping past it ([#12984](https://github.com/diegosouzapw/OmniRoute/pull/12984)) +- **chore(quality):** rebaseline chatCore.ts after the non-streaming regression fixes ([#13045](https://github.com/diegosouzapw/OmniRoute/pull/13045)) +- **docs(changelog):** add the missing bullet marker to the DeepSeek PoW fragment ([#13200](https://github.com/diegosouzapw/OmniRoute/pull/13200)) +- **chore(deps):** drain the Dependabot queue — 10 of 13 alerts ([#13213](https://github.com/diegosouzapw/OmniRoute/pull/13213)) +- **chore(ci):** make worktrees carry the hooks that enforce the identity gate ([#13236](https://github.com/diegosouzapw/OmniRoute/pull/13236)) +- **chore(deps):** pin csv-parse to ^7.0.2 via overrides (#13117) ([#13300](https://github.com/diegosouzapw/OmniRoute/pull/13300)) — thanks @KooshaPari +- **chore(deps):** bump hono from ^4.12.34 to ^4.13.7 (#13148) ([#13301](https://github.com/diegosouzapw/OmniRoute/pull/13301)) — thanks @KooshaPari +- **chore(deps):** pin joi to ^18.2.8 via overrides (#13085) ([#13302](https://github.com/diegosouzapw/OmniRoute/pull/13302)) — thanks @KooshaPari +- **docs(providers):** add Microsoft 365 Copilot (BizChat) provider guide (#12779) ([#13340](https://github.com/diegosouzapw/OmniRoute/pull/13340)) — thanks @KooshaPari +### 🙌 Contributors + +Thanks to everyone whose work landed in v3.8.51: + +| Contributor | PRs / Issues | +| --- | --- | +| [@5dive-bot](https://github.com/5dive-bot) | #11852 | +| [@abhisheksharma2411](https://github.com/abhisheksharma2411) | #11547, #11684, #11959, #12209 | +| [@adevwithpurpose](https://github.com/adevwithpurpose) | #11464 | +| [@adivekar-utexas](https://github.com/adivekar-utexas) | #12015, #12043, #12090, #12138 | +| [@AIB1TAL0S](https://github.com/AIB1TAL0S) | #12103 | +| [@alltomatos](https://github.com/alltomatos) | #11448 | +| [@alvinveroy](https://github.com/alvinveroy) | #11857, #12027, #12165 | +| [@amaleta](https://github.com/amaleta) | #11906, #11934 | +| [@amartinawi](https://github.com/amartinawi) | #11575 | +| [@AndrianBalanescu](https://github.com/AndrianBalanescu) | #11083, #11830, #11834, #11844 | +| [@anhtahaylove](https://github.com/anhtahaylove) | #13091, #13092, #13093, #13096, #13097, #13100, #13106, #13109, #13114, #13155, #13166, #13171, #13175, #13187, #13196 | +| [@aniruddhaadak80](https://github.com/aniruddhaadak80) | #11760 | +| [@app](https://github.com/app) | #12554 | +| [@arminanton](https://github.com/arminanton) | #11461, #11513 | +| [@AStupidBear](https://github.com/AStupidBear) | #11584 | +| [@atescivitci-cmd](https://github.com/atescivitci-cmd) | #12105 | +| [@b3nw](https://github.com/b3nw) | #11970, #11971, #12147 | +| [@backryun](https://github.com/backryun) | #11259, #11950, #12075, #12076, #12078, #12079, #12081, #12082, #12181, #12228, #12239, #12255, #12258, #12277, #12367, #12423, #12524, #12538 | +| [@benjaminkitt](https://github.com/benjaminkitt) | #11747 | +| [@benzntech](https://github.com/benzntech) | #11614, #11615 | +| [@Bl0ck154](https://github.com/Bl0ck154) | #11948, #11951, #11952, #11953, #11954 | +| [@brick30llc-ctrl](https://github.com/brick30llc-ctrl) | #11752, #12242, #12262 | +| [@Chewji9875](https://github.com/Chewji9875) | #11455, #11875, #12028 | +| [@cheynetom](https://github.com/cheynetom) | #12058 | +| [@davidebaraldo](https://github.com/davidebaraldo) | #12222, #12304 | +| [@davidlinfr](https://github.com/davidlinfr) | #6390 | +| [@ddarkr](https://github.com/ddarkr) | #12124 | +| [@Deftera186](https://github.com/Deftera186) | #11809 | +| [@dpozimski](https://github.com/dpozimski) | #11945 | +| [@DW-MediaLab](https://github.com/DW-MediaLab) | #12229 | +| [@echel0nn](https://github.com/echel0nn) | #11923 | +| [@f9td56dbgh-hub](https://github.com/f9td56dbgh-hub) | #11660 | +| [@fabioluissilva](https://github.com/fabioluissilva) | #11991 | +| [@foreveryh](https://github.com/foreveryh) | #12177 | +| [@geek007git](https://github.com/geek007git) | #12115, #12116, #12117, #12120, #12122 | +| [@geekyNads](https://github.com/geekyNads) | #11784, #12266 | +| [@ggiak](https://github.com/ggiak) | #11561, #12276, #12350, #12402 | +| [@gonisulaimann](https://github.com/gonisulaimann) | #12368, #12369, #12371 | +| [@Gorillaz322](https://github.com/Gorillaz322) | #12207 | +| [@groovecityJO](https://github.com/groovecityJO) | #12682 | +| [@hartmark](https://github.com/hartmark) | #11434, #11452, #11473, #11499, #11703, #11983, #11984, #11985, #11986, #11988, #11989, #11990, #11994, #12221, #12293, #12445, #12446, #12447, #12448, #12460, #12461, #12623, #12646, #12650, #12680, #12717, #12718, #12727, #12741, #12854 | +| [@hizzt](https://github.com/hizzt) | #11894 | +| [@hongnoul](https://github.com/hongnoul) | #11484 | +| [@HouMinXi](https://github.com/HouMinXi) | #11411, #11414, #11512, #11518, #11520, #11641, #11642, #11643, #11687, #11779, #11849, #11850, #11851, #11915, #11916, #11918, #11919, #11920, #12013, #12017, #12033, #12042, #12106, #12139, #12166, #12169, #12171, #12205, #12213, #12312, #12325, #12487, #12488, #12495, #12504, #12557, #12566, #12590, #12591, #12624, #12626, #12632, #12637, #12678, #12696, #12697, #12711, #12733, #12746, #12767, #12770, #12789, #12803, #12805, #12811, #12866, #12868, #12899, #12926, #12934, #12950, #12951, #12974, #13001, #13006, #13011, #13017, #13026, #13027, #13034, #13035, #13038, #13042, #13050, #13060, #13061, #13069, #13107, #13120, #13136, #13195, #13197 | +| [@Hsia97](https://github.com/Hsia97) | #11624 | +| [@jacobsparts](https://github.com/jacobsparts) | #11854, #12155, #12167 | +| [@joglomedia](https://github.com/joglomedia) | #11980 | +| [@jonlwheat2-gif](https://github.com/jonlwheat2-gif) | #11443, #11450, #11549, #11567, #11608, #11633, #11635, #11640, #11644, #11671, #11672, #11673, #11674, #11675, #11676, #11677, #11706, #11783, #12051, #12052, #12053, #12055 | +| [@kanade-hoshino](https://github.com/kanade-hoshino) | #12180 | +| [@Karan825](https://github.com/Karan825) | #11861 | +| [@KaspaPulse](https://github.com/KaspaPulse) | #11389, #11469, #11628, #11666, #11910 | +| [@keeltrace](https://github.com/keeltrace) | #12080, #12223 | +| [@killer30001000](https://github.com/killer30001000) | #12178 | +| [@KooshaPari](https://github.com/KooshaPari) | #12470, #12592, #12667, #12699, #12703, #12706, #12764, #12769, #12771, #13300, #13301, #13302, #13340, #13401, #13403, #13404, #13405, #13406, #13407, #13409, #13410, #13411, #13412, #13413, #13414, #13418, #13424, #13427, #13433, #13523, #13524, #13525, #13528, #13531, #13532, #13534, #13539, #13543, #13545, #13546, #13547, #13550, #13553 | +| [@kriptoburak](https://github.com/kriptoburak) | #11370 | +| [@KrzysiekSko](https://github.com/KrzysiekSko) | #12425, #12432, #12673 | +| [@marcelokarval](https://github.com/marcelokarval) | direct commit / report | +| [@maxmad64bis](https://github.com/maxmad64bis) | #1622, #11435, #11437, #11441, #11537, #11550, #11553, #11555, #11557, #11812, #11842, #11843, #11903, #12151, #12214, #12215, #12218, #12226, #12314, #12316, #12317, #12318, #12319, #12320, #12321, #12507, #12715, #12731, #12744, #12786, #12787, #12788, #12790, #12792, #12794, #12795, #12828, #12832, #12853, #12857, #12870, #12937, #12941, #12975, #13141, #13142, #13146, #13147 | +| [@mdigitalbh81](https://github.com/mdigitalbh81) | #12240 | +| [@Meet6338-X](https://github.com/Meet6338-X) | #11598, #11609, #12110 | +| [@morpheus9393](https://github.com/morpheus9393) | #11943 | +| [@MumuTW](https://github.com/MumuTW) | #11492, #11502, #11506, #11507, #11626, #11675, #11685, #11728, #11746, #11888, #11889, #11890, #11892 | +| [@Neuron-Mr-White](https://github.com/Neuron-Mr-White) | #11622, #11801, #12256 | +| [@NightStalker-87](https://github.com/NightStalker-87) | #12183 | +| [@NoxzRCW](https://github.com/NoxzRCW) | #11879, #11880, #11881, #11882, #11883, #11935 | +| [@ntdat812](https://github.com/ntdat812) | #11585, #12095 | +| [@ntdatt812](https://github.com/ntdatt812) | #11368, #11573, #11574, #11576, #11577, #11580, #11582, #11583, #11588, #11589, #11590, #11591, #11592, #11593, #11672, #12177, #12180, #12873, #12918, #12920, #12921, #12925, #12930, #13007, #13009, #13024, #13025, #13083, #13087, #13101, #13104, #13110 | +| [@official-burak](https://github.com/official-burak) | #11542 | +| [@opensource-elearning](https://github.com/opensource-elearning) | #12179, #12189, #12278, #12286 | +| [@oyi77](https://github.com/oyi77) | #11408, #11409, #11421, #11505, #11677, #12036, #12110 | +| [@pacocartones](https://github.com/pacocartones) | #11521, #11522, #11527, #11528, #11529, #11530, #11531, #11532, #11533, #11534, #11595, #11599, #11603, #11604, #11605, #11607, #11610, #11676, #11714, #11716, #11718, #11767, #11838, #11860, #11862, #11869, #11871, #11872, #11873, #11903, #11906, #11921, #11934, #12356, #12358, #12359, #12360, #12361, #12362, #12364, #12365, #12373, #12374, #12375, #12376, #12377, #12379, #12380, #12381, #12386, #12387, #12389, #12390, #12394, #12395, #12397, #12401, #12403, #12404, #12522, #12523, #12535, #12536, #12540, #12541, #12543, #12545, #12548, #12549, #12550, #12551, #12552, #12647, #12651, #12653, #13055 | +| [@patrykkopycinski](https://github.com/patrykkopycinski) | #11936, #11937, #12224 | +| [@PauloFH](https://github.com/PauloFH) | #11509 | +| [@PauloHSOliveira](https://github.com/PauloHSOliveira) | #12241 | +| [@ponkcore](https://github.com/ponkcore) | #12054 | +| [@Prajeeth-12](https://github.com/Prajeeth-12) | #11634, #12046 | +| [@quiterunner-commits](https://github.com/quiterunner-commits) | #12131, #12143 | +| [@rafacpti23](https://github.com/rafacpti23) | #11554, #11558, #12192, #12197, #12198, #12202, #12203, #12204 | +| [@ragnar-claude](https://github.com/ragnar-claude) | #11564 | +| [@raheemuddin786](https://github.com/raheemuddin786) | #11491, #11839, #11840, #11841, #12003, #12230, #12231, #12232, #12233, #12234 | +| [@rahilmavani](https://github.com/rahilmavani) | #11761 | +| [@Rahulsharma0810](https://github.com/Rahulsharma0810) | #11771 | +| [@RaviTharuma](https://github.com/RaviTharuma) | #11710, #11727, #11797, #11798, #11802, #11805, #11806, #11811, #12098, #12099, #12101, #12449, #12452, #12472, #12473, #12493, #12533, #12607, #12628, #12631, #12636, #12875, #12876, #12880, #12882, #12884 | +| [@rezjalibd](https://github.com/rezjalibd) | #12186 | +| [@RhianB14](https://github.com/RhianB14) | #11692 | +| [@rifqiawl](https://github.com/rifqiawl) | #11517, #11519, #11969 | +| [@rqzbeh](https://github.com/rqzbeh) | #11390 | +| [@Sabeekhann](https://github.com/Sabeekhann) | #11699, #11705 | +| [@santosraju99-hub](https://github.com/santosraju99-hub) | #11755, #11770 | +| [@Seramicx](https://github.com/Seramicx) | #11568 | +| [@Siva010](https://github.com/Siva010) | #12191 | +| [@solstxce](https://github.com/solstxce) | #11597 | +| [@soroush5](https://github.com/soroush5) | #12691 | +| [@tenshiak](https://github.com/tenshiak) | #12279 | +| [@TheDemonTuan](https://github.com/TheDemonTuan) | #11468, #11470, #11471, #11482, #11548, #11758, #11775, #11814 | +| [@thomasmaerz](https://github.com/thomasmaerz) | #12834 | +| [@tuandinh0801](https://github.com/tuandinh0801) | #11454 | +| [@turbolego](https://github.com/turbolego) | #11621, #11762, #11772, #11774, #11781, #12216 | +| [@Tushar49](https://github.com/Tushar49) | #11392 | +| [@ujjawalkaushik1110](https://github.com/ujjawalkaushik1110) | #11863 | +| [@vermasomesh835](https://github.com/vermasomesh835) | #11794 | +| [@vsd2807](https://github.com/vsd2807) | #11565, #11619 | +| [@wahidsadik371-coder](https://github.com/wahidsadik371-coder) | #12126 | +| [@watchingdogs](https://github.com/watchingdogs) | #12031 | +| [@wildcard](https://github.com/wildcard) | #11620 | +| [@xiaoyaner0201](https://github.com/xiaoyaner0201) | #11460, #11662, #12083 | +| [@Xxx91n](https://github.com/Xxx91n) | #11690 | +| [@yourspraveen](https://github.com/yourspraveen) | #11146 | +| [@yxyxy](https://github.com/yxyxy) | #11671 | +| [@ZaimMarzuki](https://github.com/ZaimMarzuki) | #11960 | +| [@Zartharas](https://github.com/Zartharas) | #11340 | +| [@zero-executioner](https://github.com/zero-executioner) | #11631 | +| [@diegosouzapw](https://github.com/diegosouzapw) | maintainer | + +--- ## [3.8.50] — 2026-08-25 _Living section — regenerated 2026-08-12 from all cycle commits (cycle open `ed2db6cb19` → tip). Bullets carry the merged PR and its author; direct pushes listed separately._ diff --git a/docs/i18n/km/CHANGELOG.md b/docs/i18n/km/CHANGELOG.md index b01594d978..2d85d4bb1d 100644 --- a/docs/i18n/km/CHANGELOG.md +++ b/docs/i18n/km/CHANGELOG.md @@ -96,31 +96,31 @@ ## [3.8.51] — TBD -_Living section — reconciled 2026-09-07 from all cycle commits (`release/v3.8.50` tip `091589089c` → `release/v3.8.51` tip `d6f315018a`, 696 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._ +_Living section — reconciled 2026-09-15 from all cycle commits (`091589089c` → `c0f92ec98a`, 916 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 | **120** | -| 📝 Commits in the cycle | **696** | -| 🔀 Pull requests referenced | **672** | -| 📋 Changelog entries | **685** | -| 🙌 Contributors credited in entries | **118** | +| 👥 People who contributed | **123** | +| 📝 Commits in the cycle | **916** | +| 🔀 Pull requests referenced | **892** | +| 📋 Changelog entries | **919** | +| 🙌 Contributors credited in entries | **121** | | 🤖 Automated dependency commits | 18 | **Entries by type** | Type | Count | | --- | ---: | -| 🐛 Fixes | 403 | -| ✨ Features | 118 | -| 🧹 Chore | 72 | -| 📚 Docs | 36 | -| 🧪 Tests | 29 | +| 🐛 Fixes | 592 | +| ✨ Features | 131 | +| 🧹 Chore | 93 | +| 📚 Docs | 42 | +| 🧪 Tests | 32 | +| ♻️ Refactor | 8 | +| 🏗️ Build | 7 | | ⚡ Performance | 7 | -| ♻️ Refactor | 7 | -| 🏗️ Build | 6 | | ⚙️ CI | 2 | | ⏪ Reverts | 2 | | 📦 Dependencies | 2 | @@ -128,35 +128,35 @@ _Living section — reconciled 2026-09-07 from all cycle commits (`release/v3.8. ### 🏆 Top 25 contributors this cycle -_By commits in `release/v3.8.50..release/v3.8.51` (`091589089c..d6f315018a`), author identities consolidated via `.mailmap` and the merged PR's GitHub login. Bots excluded._ +_By commits in `091589089c..c0f92ec98a`, author identities consolidated via `.mailmap` and the merged PR's GitHub login. Bots excluded._ | # | Contributor | Commits | | ---: | --- | ---: | -| 🥇 | diegosouzapw | 240 | -| 🥈 | Paco Cartones (@pacocartones) | 58 | -| 🥉 | Bob.Hou (@HouMinXi) | 57 | -| 4 | Dizzle (@maxmad64bis) | 27 | -| 5 | Webman (@jonlwheat2-gif) | 22 | -| 6 | Ravi Tharuma (@RaviTharuma) | 21 | -| 7 | backryun | 17 | -| 8 | Markus Hartung (@hartmark) | 17 | -| 9 | Nguyen Thanh Dat (@ntdatt812) | 16 | -| 10 | Rafa Martins (@rafacpti23) | 10 | -| 11 | Syed Raheemuddin (@raheemuddin786) | 10 | -| 12 | Koosha Paridehpour (@KooshaPari) | 9 | -| 13 | Nguyễn Viết Tuấn (@TheDemonTuan) | 8 | -| 14 | MumuTW | 7 | -| 15 | Paijo (@oyi77) | 7 | -| 16 | Tobias Andersen (@turbolego) | 6 | -| 17 | Bl0ck (@Bl0ck154) | 5 | -| 18 | SHANMUGAPRIYAN (@geek007git) | 5 | -| 19 | KaspaPulse | 5 | -| 20 | Abhishek Sharma (@abhisheksharma2411) | 4 | -| 21 | Abhishek Divekar (@adivekar-utexas) | 4 | -| 22 | Andrew B. (@AndrianBalanescu) | 4 | -| 23 | NoxzRCW | 4 | -| 24 | opensource-elearning | 4 | -| 25 | Alvin T. Veroy (@alvinveroy) | 3 | +| 🥇 | diegosouzapw | 323 | +| 🥈 | Bob.Hou (@HouMinXi) | 79 | +| 🥉 | Paco Cartones (@pacocartones) | 77 | +| 4 | Dizzle (@maxmad64bis) | 49 | +| 5 | Koosha Paridehpour (@KooshaPari) | 43 | +| 6 | Nguyen Thanh Dat (@ntdatt812) | 31 | +| 7 | Ravi Tharuma (@RaviTharuma) | 26 | +| 8 | Markus Hartung (@hartmark) | 22 | +| 9 | Webman (@jonlwheat2-gif) | 22 | +| 10 | backryun | 17 | +| 11 | anhtahaylove | 15 | +| 12 | Rafa Martins (@rafacpti23) | 10 | +| 13 | Syed Raheemuddin (@raheemuddin786) | 10 | +| 14 | Nguyễn Viết Tuấn (@TheDemonTuan) | 8 | +| 15 | MumuTW | 7 | +| 16 | Paijo (@oyi77) | 7 | +| 17 | Tobias Andersen (@turbolego) | 6 | +| 18 | Bl0ck (@Bl0ck154) | 5 | +| 19 | SHANMUGAPRIYAN (@geek007git) | 5 | +| 20 | KaspaPulse | 5 | +| 21 | Abhishek Sharma (@abhisheksharma2411) | 4 | +| 22 | Abhishek Divekar (@adivekar-utexas) | 4 | +| 23 | Andrew B. (@AndrianBalanescu) | 4 | +| 24 | NoxzRCW | 4 | +| 25 | opensource-elearning | 4 | ### ✨ New Features @@ -282,6 +282,19 @@ _By commits in `release/v3.8.50..release/v3.8.51` (`091589089c..d6f315018a`), au - **feat(dashboard):** link a log entry's Conversation Context to its owning conversation ([#12646](https://github.com/diegosouzapw/OmniRoute/pull/12646)) — thanks @hartmark - **feat(catalog):** eligibility-gated free-tier bucket ([#12669](https://github.com/diegosouzapw/OmniRoute/pull/12669)) - **feat(db):** fail-closed previous_response_id continuation for redacted video turns (#12150 P2b) ([#12707](https://github.com/diegosouzapw/OmniRoute/pull/12707)) +- **feat(gamification): pay the documented `streak_bonus` and `badge_unlock` XP rewards.** `XP_REWARDS` listed both rewards but the award pipeline never paid them: the private reward table in `events.ts` omitted them, `updateStreak()` did not report when a streak extended, and badge unlocks carried no XP. Every request that extends a daily streak now pays `streak_bonus × streak length` once per UTC day (guarded by a same-day `xp_audit_log` check), and every badge unlocked through the pipeline pays `badge_unlock` once per badge (guarded by the `user_badges` primary key; `unlockBadge()` now reports whether it inserted). Bonus XP flows through the same `addXp` + level sync + global/weekly/monthly leaderboard path as action XP, so level-ups and rankings include it. The Radar supporter recognition unlock stays XP-free. (#12522 — thanks @pacocartones) +- **feat(providers):** advertise a `free-tier` capability in the provider plugin manifest for every provider with documented free models, so sidecars and dashboards can filter free-capable providers without reading the quota catalog ([#12786](https://github.com/diegosouzapw/OmniRoute/pull/12786)) — thanks @maxmad64bis +- **feat(providers):** Added EURouter as an OpenAI-compatible API-key gateway (`https://api.eurouter.ai/v1`), with live model discovery via `passthroughModels`. Its copy states that models are served by third-party upstreams listed per model, so an EU-based router is not read as EU data residency for inference. ([#13025](https://github.com/diegosouzapw/OmniRoute/pull/13025)) — thanks @ntdatt812 +- **feat(providers):** Added GreenPT as an OpenAI-compatible API-key provider (`https://api.greenpt.ai/v1`), with live model discovery via `passthroughModels`. No free-inference badge: the published docs describe a free API subscription billed per token, not a free tier. ([#13024](https://github.com/diegosouzapw/OmniRoute/pull/13024)) — thanks @ntdatt812 +- **feat(i18n):** 9 new locales — Greek (`el`), Croatian (`hr`), Serbian (`sr`), Lithuanian (`lt`), Estonian (`et`), Latvian (`lv`), Slovenian (`sl`), Maltese (`mt`), Irish (`ga`) — across the dashboard, docs mirrors, CLI, README and the site; OmniRoute now ships every official EU language (51 locales). (#13044) +- **feat(i18n):** 8 new locales — Kannada (`kn`), Malayalam (`ml`), Odia (`or`), Punjabi (`pa`), Nepali (`ne`), Sinhala (`si`), Burmese (`my`), Khmer (`km`) — across the dashboard, docs mirrors, CLI, README and the site (59 locales). The translator now restores the ICU literal escape around angle placeholders and splits oversized docs sections before translating. (#0000) ([#13660](https://github.com/diegosouzapw/OmniRoute/pull/13660)) +- feat(providers): **list Agnes 3.0 Flash as the current free chat model, drop retired 1.5 Flash, add Image 2.0/2.5 Flash plus Video 2.5/2.5 Flash, and discover the live `/v1/models` catalog (including the CN host `api.agnes-ai.cn`).** `agnes-1.5-flash` now forwards to `agnes-3.0-flash`. Video 2.5 polls `GET /v1/videos/{id}` (not the V2.0 `/agnesapi` contract). Live `/v1/models` (2026-09-09) no longer serves 1.5; the wiki marks it deprecated. 3.0 Flash is 512K context / 65,536 max output, same window as 2.5. CN-region keys use the existing per-connection base-URL field, default stays `apihub.agnes-ai.com`. ([#13120](https://github.com/diegosouzapw/OmniRoute/pull/13120)) — thanks @HouMinXi +- **feat(sse):** Codex and OpenAI catalogs list GPT-6 Astra with effort aliases (`-low` through `-ultra`); Codex CLI identity pins `@openai/codex@0.153.4` in lockstep with the image ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **feat(dashboard):** the orchestration History tab gained a "Compare runs" mode — toggling it turns each grid cell into a 2-item selection queue (a 3rd click drops the oldest pick), and picking two cells opens a side-by-side comparison panel instead of the usual detail drawer. The panel fetches both runs' detail the same way the drawer does (falling back to persisted history once a run leaves the live TTL window) and shows, per side: identity/source/state, start time, a signed `right - left` delta for duration/cost/event count, the event timeline aligned by index, and any memory hits. One side's fetch failing never blocks the other, and a delta is only ever computed when both sides have a finite value — otherwise it renders "—", never `NaN`. Comparing two runs from different sources or skills still works; a banner marks the deltas as informational rather than hiding them, since the two runs aren't a strict apples-to-apples pair. ([#12677](https://github.com/diegosouzapw/OmniRoute/pull/12677)) +- **feat(dashboard):** badge a conversation that never reached a clean stop ([#12717](https://github.com/diegosouzapw/OmniRoute/pull/12717)) — thanks @hartmark +- **feat(release):** reconcile-changelog tool + version-anchored fragment aggregation ([#12987](https://github.com/diegosouzapw/OmniRoute/pull/12987)) +- **feat(mcp):** carry context_length through MCP list_models_catalog projection (#12776) ([#13406](https://github.com/diegosouzapw/OmniRoute/pull/13406)) — thanks @KooshaPari +- **feat(cli):** make startup readiness budget configurable (#13369) ([#13433](https://github.com/diegosouzapw/OmniRoute/pull/13433)) — thanks @KooshaPari ### 🐛 Bug Fixes @@ -698,6 +711,194 @@ _By commits in `release/v3.8.50..release/v3.8.51` (`091589089c..d6f315018a`), au - **fix(i18n):** quote placeholder in OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES description (#12505) ([#12769](https://github.com/diegosouzapw/OmniRoute/pull/12769)) — thanks @KooshaPari - **fix(api/pricing):** surface validation error message as string, not raw object (#12494) ([#12771](https://github.com/diegosouzapw/OmniRoute/pull/12771)) — thanks @KooshaPari - **fix(sse):** re-enable prompt compression for native Codex passthrough ([#12834](https://github.com/diegosouzapw/OmniRoute/pull/12834)) — thanks @thomasmaerz +- **fix(dashboard):** model health tests for a provider node set to the Responses API now call `/v1/responses` with a Responses-shaped body instead of `/v1/chat/completions` — those models were reported as `Provider returned HTTP 200 but no text content` even though the same model answered normally through `/v1/responses` ([#13070](https://github.com/diegosouzapw/OmniRoute/issues/13070)) ([#13087](https://github.com/diegosouzapw/OmniRoute/pull/13087)) — thanks @ntdatt812 +- fix(routing): stop a round-robin combo's "opencode" targets from collapsing onto the opencode-zen connection (#11912) ([#13283](https://github.com/diegosouzapw/OmniRoute/pull/13283)) +- fix(routing): stop the reactive-compaction debug log from lying when compression is globally disabled (#11977) ([#13276](https://github.com/diegosouzapw/OmniRoute/pull/13276)) +- fix(electron): relativize standalone-bundle symlink targets so Stage 8 manifest verification stops failing on Windows (#11979) ([#13251](https://github.com/diegosouzapw/OmniRoute/pull/13251)) +- fix(dashboard): surface a visible error when Compression Studio's combined preview run fails (#12061) ([#13288](https://github.com/diegosouzapw/OmniRoute/pull/13288)) +- fix(dashboard): make the compression "Effective pipeline" preview honor the active profile and warn when the master switch is off (#12063) ([#13287](https://github.com/diegosouzapw/OmniRoute/pull/13287)) +- fix(providers): scope TinyCMS Web signer's DOM shims to each call instead of leaking them for the process lifetime, and surface a clean HTTP status on a non-JSON interception-toggles error (#12072) ([#13254](https://github.com/diegosouzapw/OmniRoute/pull/13254)) +- fix(guardrails): stop Vision Bridge from re-selecting a model locked after a 404 (#12111) ([#13259](https://github.com/diegosouzapw/OmniRoute/pull/13259)) +- fix(sse): require Responses-shaped body before native OpenAI-compatible passthrough (#12129) ([#13278](https://github.com/diegosouzapw/OmniRoute/pull/13278)) +- fix(providers): minimax-m3 now collapses manual thinking.type:"enabled" to adaptive, preventing upstream 400 (2013) (#12132) ([#13249](https://github.com/diegosouzapw/OmniRoute/pull/13249)) +- fix(db): scope model visibility overrides by modality so hiding a Chat model no longer hides an identically-ID'd Image/Embeddings/etc. model (#12172) ([#13258](https://github.com/diegosouzapw/OmniRoute/pull/13258)) +- fix(db): scope local-provider apiKey dedup to matching base URL so LM Studio/Ollama-style connections support multiple accounts (#12173) ([#13250](https://github.com/diegosouzapw/OmniRoute/pull/13250)) +- fix(providers): refresh Trae's stale Referer/Origin and forward user timezone so imported connections stop failing with 401 (#12190) ([#13255](https://github.com/diegosouzapw/OmniRoute/pull/13255)) +- fix(providers): route opencode-go/gpt-5.6-luna to /responses instead of /chat/completions (#12196) ([#13275](https://github.com/diegosouzapw/OmniRoute/pull/13275)) +- fix(dashboard): allow deleting the last extra-upstream-header row even when invalid (#12251) ([#13252](https://github.com/diegosouzapw/OmniRoute/pull/13252)) +- **fix(i18n):** translate pre-existing `__MISSING__:` keys for `combo.sort`, `requestLogger.detail` expand/collapse, `common.profile`, and `settings.resilienceCredentialHealth*` across 39 locales ([#12272](https://github.com/diegosouzapw/OmniRoute/issues/12272)) ([#13034](https://github.com/diegosouzapw/OmniRoute/pull/13034)) — thanks @HouMinXi +- fix(cli): run the Node.js runtime compatibility guard before the heavy `tsx/esm` + Commander import chain so an unsupported runtime gets a clear message instead of a raw `Invalid regular expression flags` crash (#12296) ([#13253](https://github.com/diegosouzapw/OmniRoute/pull/13253)) +- fix(dashboard): refresh the providers list after deleting a compatible provider node (#12298) ([#13256](https://github.com/diegosouzapw/OmniRoute/pull/13256)) +- fix(usage): fail closed on API-key budget enforcement when a provider's `auto` routing alias has no pricing row, instead of silently counting it as $0 (#12341) ([#13257](https://github.com/diegosouzapw/OmniRoute/pull/13257)) +- **fix(providers):** include the submitted Agnes video model when polling by `video_id` ([#12356](https://github.com/diegosouzapw/OmniRoute/pull/12356)) — thanks @pacocartones +- **fix(routing):** custom OpenAI-compatible nodes now honor the saved Chat/Responses API type after edits instead of letting the node's original ID prefix override the live connection setting ([#11884](https://github.com/diegosouzapw/OmniRoute/issues/11884)). ([#12358](https://github.com/diegosouzapw/OmniRoute/pull/12358)) — thanks @pacocartones +- fix(sse): surface an error instead of a silent empty 200 when a Claude stream closes with zero bytes (#12398) ([#13285](https://github.com/diegosouzapw/OmniRoute/pull/13285)) +- fix(oauth): warn before the dead localhost:8080 redirect in antigravity/gemini `oauth start` (#12413) ([#13265](https://github.com/diegosouzapw/OmniRoute/pull/13265)) +- fix(providers): stop devin-cli spawn error from double-closing the SSE controller (#12517) ([#13261](https://github.com/diegosouzapw/OmniRoute/pull/13261)) +- **fix(rerank):** clamp Voyage `top_k` to the documents actually sent after empty-string filtering, and honor `return_documents: false` in the NVIDIA response adapter (#12523 — thanks @pacocartones) +- **fix(audio):** `/v1/audio/translations` now resolves combo names the way `/v1/audio/transcriptions` already does, so a combo that `GET /v1/models` advertises is fanned out to its targets instead of being rejected with `400 Invalid translation model: . Use format: provider/model`; literal `provider/model` ids and unknown bare names behave as before (#12536 — thanks @pacocartones) +- **fix(gemini):** strip the JSON-Schema-2020-12 `prefixItems` keyword from Gemini tool schemas at every nesting level, so Claude Code tool definitions no longer fail with `400 Unknown name "prefixItems"` on Gemini models (#12540 — thanks @pacocartones) +- **fix(api-manager):** Expose an accessible loading status while API keys are fetched instead of an empty accessibility tree (#12541 — thanks @pacocartones) +- **fix(video):** Clamp `estimateJpegFrameBytes` at zero for padding-only payloads and build the three encode-side frame data URIs from `JPEG_FRAME_DATA_URI_PREFIX` instead of a repeated literal (#12543 — thanks @pacocartones) +- **fix(devin):** accept Windows `DEVIN_AGENTIC_HOME` sandbox paths (`C:\...\.sandbox\...`) in the isolated-home check so the Devin Claude Bridge no longer fails closed on Windows ([#12405](https://github.com/diegosouzapw/OmniRoute/issues/12405)) (#12545 — thanks @pacocartones) +- **fix(db):** Add `conversation_turn_nodes` and orphaned `agentic_conversations` to the auto-cleanup cycle under the existing `retention.callLogs` window, so identity nodes whose call-log content has already been purged no longer accumulate without bound in `storage.sqlite` (#12548 — thanks @pacocartones) +- **fix(i18n):** Wrap the `~/.claude/profiles//settings.json` placeholder in ICU single quotes in the `OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES` feature-flag description across all 42 locales and the TypeScript default, so next-intl no longer fails with `INVALID_MESSAGE: UNCLOSED_TAG` and the Feature Flags card shows the description instead of the raw key (#12549 — thanks @pacocartones) +- **fix(orchestration):** `updateCloudAgentTask` now publishes the task's real `status` on `agent.task.updated` when an update only touches `result`, `activities` or `error`, instead of the fabricated `"updated"` state, and stays silent when no row matched the id (#12550 — thanks @pacocartones) +- **fix(i18n):** the home "Recent Requests" panel and the Provider Topology legend are now translated instead of rendering English copies on non-English dashboards; the legend reads its own `home.topologyLegend*` labels with consistent casing rather than borrowing the memory-settings "Recent" and analytics "Error" strings (#12551 — thanks @pacocartones). +- **docs(reference):** bring the `FEATURE_FLAGS.md` catalog back to 1:1 with `featureFlagDefinitions.ts` — 20 missing flags added, the two `*_BLOCK_THRESHOLD` env-only knobs moved out of the flag tables, category/total counts and the Live WS port corrected, guarded by a static test (#12552 — thanks @pacocartones) +- **fix(i18n):** backfill missing `usage.kiloPass*` strings in 39 locales and restore `featureFlagOmnirouteDisableThinkingLevelVariantsDescription` in `pt.json` ([#12561](https://github.com/diegosouzapw/OmniRoute/issues/12561)) ([#13027](https://github.com/diegosouzapw/OmniRoute/pull/13027)) — thanks @HouMinXi +- fix(docker): default docker-compose app ports (dashboard/API/live-WS) to loopback instead of `0.0.0.0`, closing the anonymous `/v1` LAN/WAN exposure gap left open by `REQUIRE_API_KEY=false` (#12568) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): close DNS-rebinding SSRF gap in webhook outbound-URL guard (#12569) ([#13243](https://github.com/diegosouzapw/OmniRoute/pull/13243)) +- fix(docker): require a per-session token on the VNC browser CDP bridge and isolate it on a dedicated Docker network (#12571) ([#13241](https://github.com/diegosouzapw/OmniRoute/pull/13241)) +- fix(open-sse): write Adobe Firefly session tokens and cookie jars with 0700/0600 permissions instead of the process umask (#12572) ([#13242](https://github.com/diegosouzapw/OmniRoute/pull/13242)) +- fix(api): remove hardcoded wildcard CORS in openai-to-gemini-sse.ts so the centralized fail-closed CORS gate is the sole source of `Access-Control-Allow-Origin` (#12573) ([#13239](https://github.com/diegosouzapw/OmniRoute/pull/13239)) +- fix(api): enforce API key policy (budget/rate-limit/schedule/endpoint scoping) on the ElevenLabs speech-to-text, text-to-speech and voices proxy routes (#12574) ([#13244](https://github.com/diegosouzapw/OmniRoute/pull/13244)) +- fix(sse): cap HuggingChat NDJSON body size and bound the read loop with the fetch timeout so a stalled or hostile upstream cannot buffer unbounded memory (#12577) ([#13282](https://github.com/diegosouzapw/OmniRoute/pull/13282)) +- fix(docker): scope the cliproxyapi/qdrant/bifrost sidecars to loopback by default and forward `CLIPROXYAPI_MANAGEMENT_KEY` into the cliproxyapi container so its management API is not left both unauthenticated and LAN-published (#12578) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): create DB export temp paths with `fs.mkdtempSync` instead of predictable timestamps (#12579) ([#13237](https://github.com/diegosouzapw/OmniRoute/pull/13237)) +- Cline 401 bodies that say "re-authenticate your Cline account" classify as a refreshable OAuth token, not a terminal expired key. The cooling panel no longer labels every cooldown as a 429; it shows the recorded last error instead. (#12594) ([#13060](https://github.com/diegosouzapw/OmniRoute/pull/13060)) — thanks @HouMinXi +- **fix(catalog):** degrade unknown combo targets instead of dropping LCD modalities, and persist OpenRouter `architecture.input_modalities` into the capability snapshot ([#12613](https://github.com/diegosouzapw/OmniRoute/issues/12613)) ([#13042](https://github.com/diegosouzapw/OmniRoute/pull/13042)) — thanks @HouMinXi +- fix(providers): send `x-api-key` instead of `Authorization: Bearer` for OpenCode Zen's `/v1/responses` endpoint (Muse Spark Contributor models), fixing a 401 on OmniRoute's auth header (#12633) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(sse): surface the actionable "Auggie CLI not found" message when the shell reports a missing `auggie` binary via exit code instead of a spawn error (#12645) ([#13263](https://github.com/diegosouzapw/OmniRoute/pull/13263)) +- **fix(config):** Persist deletions of built-in background-degradation entries — when a stored settings record exists its `degradationMap` is now authoritative instead of being merged under the defaults, so an entry the user removed in the dashboard no longer reappears on the next apply or restart ([#12424](https://github.com/diegosouzapw/OmniRoute/issues/12424)) ([#12647](https://github.com/diegosouzapw/OmniRoute/pull/12647)) — thanks @pacocartones +- **fix(gamification):** action-count badge milestones (First Token, Token Consumer, Token Machine, Token Whale, and the token-sharing tier) are now backed by a durable `xp_action_counts` counter incremented in `addXp()`, instead of a live `COUNT(*)` over `xp_audit_log`. The audit log is pruned by `retention.xpAuditLog` (default 30 days), so on a default install those "lifetime" milestones were really "actions in the last 30 days" and unlocked badges could stop unlocking once old rows aged out. `getActionCount()` and `checkActionCountBadges()` now read the same durable source, and a migration backfills existing totals from the surviving audit rows ([#12546](https://github.com/diegosouzapw/OmniRoute/issues/12546)) ([#12651](https://github.com/diegosouzapw/OmniRoute/pull/12651)) — thanks @pacocartones +- **fix(images):** `/v1/images/edits` now iterates a combo's targets the same way `/v1/images/generations` does (#9239) instead of flattening a bare combo to its first target. A combo whose first target is not edit-capable — or lacks credentials — now falls through to a later edit-capable target rather than hard-erroring, and missing credentials are skipped (not a hard `401`) to match the generations path. The per-target skip/terminal classification is extracted into a shared `runImageComboTargets` loop, so generations behavior is unchanged ([#12547](https://github.com/diegosouzapw/OmniRoute/issues/12547)). ([#12653](https://github.com/diegosouzapw/OmniRoute/pull/12653)) — thanks @pacocartones +- fix(sse): add first-byte watchdog to the TLS-fingerprint transport so a stalled wreq body falls back instead of hanging for minutes (#12656) ([#13272](https://github.com/diegosouzapw/OmniRoute/pull/13272)) +- fix(sse): exempt tiny-budget reasoning probes from combo quality failure and surface persisted-cooldown skips in ALL_TARGETS_SKIPPED diagnostics (#12659) ([#13269](https://github.com/diegosouzapw/OmniRoute/pull/13269)) +- fix(models): declare the real ~1M contextLength for OpenCode Zen's Muse Spark 1.2 models instead of falling back to the 200000 provider default (#12681) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(oauth): align codebuddy-cn OAuth User-Agent with the chat/usage CLI version to avoid WAF false positives (#12702) ([#13264](https://github.com/diegosouzapw/OmniRoute/pull/13264)) +- fix(dashboard): surface an authentication-required banner instead of silently blanking database settings for a guest session (#12709) ([#13270](https://github.com/diegosouzapw/OmniRoute/pull/13270)) +- **fix(resilience):** stop unbounded queue that hangs 6min until Aborted — gate, provider slot, and Bottleneck queue now share a per-connection `maxWaitMs` budget; `fail-closed` on exhaust (503); execution backstop `executionMaxWaitMs` overridable per connection with upstream clamp ([#12715](https://github.com/diegosouzapw/OmniRoute/pull/12715)) — thanks @maxmad64bis (with thanks to @Tushar49 for surfacing the slow-provider need in #12635) +- **fix(auto-combo):** every mode pack now carries `quality` and `reliability` weights (`quality` 0.02, 0.03 in `quality-first`; `reliability` 0.03, 0.04 in `reliability-first`), so selecting a pack no longer silences either signal; `DEFAULT_WEIGHTS` is unchanged ([#12731](https://github.com/diegosouzapw/OmniRoute/pull/12731)) — thanks @maxmad64bis +- **fix(sse):** the provider execution pipeline keeps the upstream error `code`/`type` again — both were lost when the non-streaming leg was extracted, so a config-class `422` (Antigravity missing project) degraded into a generic account cooldown instead of reaching the gate that recognises it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(db):** a legacy `call_logs` table no longer aborts startup — the provider-stats index is created after the column healing runs, not before it, so an install predating `request_type` opens instead of failing with `no such column` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(catalog):** a malformed row in the operator-writable custom-models blob no longer kills every `auto/*` pool ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(tests):** realign the guards that the same day's merges left asserting the old behavior — the provider-count total after GreenPT and EURouter, the GLM stream buffer slot that `#12925` turned into a declared parameter, and the injection scan bound that `#13104` reshaped from a head-only window into head-plus-tail ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- fix(cache): fold tool_choice/tools/response_format into the semantic cache signature so a cached tool_calls response can no longer be replayed for a request whose tool policy forbids it (#12734) ([#13267](https://github.com/diegosouzapw/OmniRoute/pull/13267)) +- **fix(models):** stop treating provider-supplied `isFree:true`/`:free`/`0/0` flags as trusted unless the provider has a documented free tier — only the shipped free-tier catalog decides otherwise; fetched `isFree:true` trusted only for a free-tier provider, custom `isFree:true` via a trusted path ([#12744](https://github.com/diegosouzapw/OmniRoute/pull/12744)) — thanks @maxmad64bis +- fix(memory): authenticate the internal /v1/rerank loopback call so memory reranking no longer silently degrades to unranked order when REQUIRE_API_KEY=true (#12745) ([#13268](https://github.com/diegosouzapw/OmniRoute/pull/13268)) +- fix(sse): parse Ollama Cloud's current usage markup (`$X of $Y used` aria-label, nested width style) (#12749) ([#13260](https://github.com/diegosouzapw/OmniRoute/pull/13260)) +- fix(cli): setup-opencode no longer sends an active context's management token to `/v1/models` when `--api-key`/`OMNIROUTE_API_KEY` is supplied — an explicit flag or the env var now always outranks the context's token, and the flag itself is no longer swallowed by the parent program's global `--api-key` option (#12783) ([#13246](https://github.com/diegosouzapw/OmniRoute/pull/13246)) +- fix(sse): register Arcee AI in the executor provider registry so requests reach api.arcee.ai instead of silently falling back to OpenAI (#12784) ([#13277](https://github.com/diegosouzapw/OmniRoute/pull/13277)) +- **fix(quota):** share one upstream quota read when identical saturation checks arrive at the same time, so traffic bursts don't multiply provider API calls ([#12787](https://github.com/diegosouzapw/OmniRoute/pull/12787)) — thanks @maxmad64bis +- **fix(db):** ignore expired or invalid rate-limit cooldown writes so a stale timestamp can't lock a connection that should be usable — clearing still works as before ([#12788](https://github.com/diegosouzapw/OmniRoute/pull/12788)) — thanks @maxmad64bis +- **fix(providers):** single source for provider order with `xao` ranking alongside `xai-oauth` ([#12790](https://github.com/diegosouzapw/OmniRoute/pull/12790)) — thanks @maxmad64bis +- **fix(routing):** off-table models check the free-model catalog before inheriting premium prices, latency bootstraps from the observed pool median, async tiers read live database pricing with a 90-day freshness gate, and the tier cache invalidates on every pricing write ([#12792](https://github.com/diegosouzapw/OmniRoute/pull/12792)) — thanks @maxmad64bis +- **fix(auto-combo):** snapshot scoring uses the observed per-connection account tier and per-model quality instead of neutral constants, and circuit-open providers rank lower at build time ([#12794](https://github.com/diegosouzapw/OmniRoute/pull/12794)) — thanks @maxmad64bis +- **fix(pool):** empty `auto/*` pools now say why they are empty, and the models listing uses the same paid check as routing ([#12795](https://github.com/diegosouzapw/OmniRoute/pull/12795)) — thanks @maxmad64bis +- fix(routing): recognize CLIProxyAPI's 'unknown provider for model' 400 as fallback-worthy (#12800) ([#13284](https://github.com/diegosouzapw/OmniRoute/pull/13284)) +- **fix(sse):** translate-mode streams now emit the estimated token counts as a trailing usage-only chunk before `[DONE]` when the upstream stays silent, so metered chat clients see totals instead of nothing ([#12828](https://github.com/diegosouzapw/OmniRoute/pull/12828)) — thanks @maxmad64bis +- **fix(db):** provider stats stay truthful on empty and legacy databases: fallback counts default to `0` instead of `null`, latency averages read `null` (not `0`) when no durations were recorded, failures logged before the error-type column existed group under `pre_migration` instead of `unclassified`, and per-provider queries use two new composite indexes ([#12832](https://github.com/diegosouzapw/OmniRoute/pull/12832)) — thanks @maxmad64bis +- fix(nvidia): fail open when a synced model catalog goes stale instead of gating forever (#12849) ([#13248](https://github.com/diegosouzapw/OmniRoute/pull/13248)) +- **fix(db):** WAL maintenance lives in its own module: a `TRUNCATE` checkpoint that hits a busy database now warns with its streak and retries once via `PASSIVE` instead of logging success, and closing-time checkpoints no longer report success on builds without a database file, with the busy totals visible in the authenticated monitoring health payload ([#12853](https://github.com/diegosouzapw/OmniRoute/pull/12853)) — thanks @maxmad64bis +- **fix(health):** quota with no snapshots reads empty instead of a contradicting 0% ([#12857](https://github.com/diegosouzapw/OmniRoute/pull/12857)) — thanks @maxmad64bis +- fix(a2a): accept the dashboard's own session cookie on /a2a so "Run message/send" no longer fails with "Unauthorized: missing or invalid API key" (#12888) ([#13271](https://github.com/diegosouzapw/OmniRoute/pull/13271)) +- **fix(logs):** the Logs grid's in-memory filter pass no longer discards rows the SQL query already matched — selecting an API key from the dropdown (which sends the key's id) returns its calls again, the Combo tab shows every combo instead of only those whose name contains a "1", and the model filter and search cover the same columns as the query ([#12896](https://github.com/diegosouzapw/OmniRoute/pull/12896)) — fixes [#12873](https://github.com/diegosouzapw/OmniRoute/issues/12873) — thanks @ntdatt812 +- **fix(a2a):** `/api/a2a/status` now builds the agent card from the request that asked for it, so a gateway reached at a non-localhost host no longer advertises `http://localhost:20128` as its A2A URL ([#12918](https://github.com/diegosouzapw/OmniRoute/pull/12918)). — thanks @ntdatt812 +- **fix(compression):** progressive aging now appends its `[COMPRESSED:aging:…]` annotation after a turn's `tool_result` blocks instead of in front of them, so Anthropic no longer rejects aged conversations with "`tool_use` ids were found without `tool_result` blocks immediately after" ([#12920](https://github.com/diegosouzapw/OmniRoute/pull/12920)). — thanks @ntdatt812 +- **fix(bedrock):** model import now resolves context limits for every vendor prefix instead of only `anthropic.*`, so `global.openai.gpt-5.6-*` no longer imports with a null `inputTokenLimit` and gets rejected pre-flight at the 200k default ([#12921](https://github.com/diegosouzapw/OmniRoute/pull/12921)). — thanks @ntdatt812 +- **fix(stream):** the 64 KB stream buffer GLM asks for is honoured instead of dropped, and the type error it caused no longer fails the API Route Typecheck gate on every open PR ([#12925](https://github.com/diegosouzapw/OmniRoute/pull/12925)) — thanks @ntdatt812 +- **fix(guardrails):** mask PII inside a `tool_result`'s nested content array, which the masker walked past while redacting its sibling block ([#12930](https://github.com/diegosouzapw/OmniRoute/pull/12930)) — thanks @ntdatt812 +- **fix(dashboard):** the Radar catalog table no longer leaves absent data unexplained — empty limits, unknown context windows, and unreported capabilities each explain themselves on hover, and a new check keeps it that way ([#12937](https://github.com/diegosouzapw/OmniRoute/pull/12937)) — thanks @maxmad64bis +- **fix(sse):** geo-blocked opencode requests rotate to the next account proxy instead of failing, so one refused egress no longer aborts the whole chain ([#12941](https://github.com/diegosouzapw/OmniRoute/pull/12941)) — thanks @maxmad64bis +- **fix(codex):** dashboard "clear cooldown" and the CAS recovery path now drop nested `codexScopeRateLimitedUntil` maps in the same write that nulls `rate_limited_until`, and fresh quota snapshots with headroom lift fallback-sourced scope cooldowns parked by quota preflight ([#12817](https://github.com/diegosouzapw/OmniRoute/issues/12817), [#12860](https://github.com/diegosouzapw/OmniRoute/issues/12860), [#12951](https://github.com/diegosouzapw/OmniRoute/pull/12951)) The dashboard reset-credit button (`consumeCodexResetCredit`) now exercises that same snapshot path after a successful redeem, so a filled quota bar is enough to unpark a leftover fallback Codex child without another manual clear. — thanks @HouMinXi +- **fix(db):** resolve `sql-wasm.wasm` across global npm install and hoisted layouts, ensuring OmniRoute can boot cleanly on Node 24 when native `better-sqlite3` is uncompiled. ([#13035](https://github.com/diegosouzapw/OmniRoute/pull/13035)) — thanks @HouMinXi +- **fix(api):** `DELETE /v1/batches/delete-completed` now sweeps only the calling API key's own completed batches (batches with no owner stay out of a key-scoped sweep on purpose), with an explicit instance-wide mode reserved for authenticated dashboard sessions, a 401 for a presented key that is unknown, revoked, deactivated, banned or expired (never falling through to the session branch), audit logging of every sweep, a sanitized 500 on failure, an owner-scoped file half (a key-scoped sweep never nulls a file another tenant owns) and an atomic sweep — chunked in 200-batch transactions in instance mode — so a mid-way error never leaves a batch pointing at a nulled file (GHSA-wvxc-jp3v-5mg5) ([#12969](https://github.com/diegosouzapw/OmniRoute/pull/12969)) +- **fix(combo):** quota-weighted routing stops drawing on an out-of-credit connection — a 402 now invalidates the stored quota snapshot instead of leaving its stale remaining percentage in place, and a snapshot older than 10 minutes no longer counts as confident headroom for the primary pool ([#12972](https://github.com/diegosouzapw/OmniRoute/pull/12972)) ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- **fix(vision):** Custom Models with "Vision capable" checked no longer have image requests swapped to `glm/glm-4.6v` when the client sends the advertised alias (`vllm/path/...`) or the bare path-shaped id — Vision Bridge now matches the stored override for all three id forms ([#12758](https://github.com/diegosouzapw/OmniRoute/issues/12758)) ([#12974](https://github.com/diegosouzapw/OmniRoute/pull/12974)) — thanks @HouMinXi +- **fix(sse):** transient opencode upstream failures rotate to the next account proxy instead of failing, so one flapping egress no longer aborts the whole chain ([#12975](https://github.com/diegosouzapw/OmniRoute/pull/12975)) — thanks @maxmad64bis +- **fix(azure):** Deployments from GPT-6 onward now send `max_completion_tokens` instead of `max_tokens`, which Azure rejects with HTTP 400. The rule matched a literal `gpt-5`, so each new generation arrived broken; it now matches the generation range, while `gpt-35-turbo` still keeps `max_tokens`. ([#13007](https://github.com/diegosouzapw/OmniRoute/pull/13007)) — thanks @ntdatt812 +- **fix(db):** add an opt-in self-restart circuit for sustained critical memory pressure, gate post-cleanup VACUUM behind a minimum freed-rows threshold, and checkpoint the SQLite WAL every 5 minutes with a size guard that escalates to TRUNCATE, so a growing WAL can no longer stall the event loop into a full outage. ([#13011](https://github.com/diegosouzapw/OmniRoute/pull/13011)) — thanks @HouMinXi +- **fix(auth):** an explicit connection pin may probe a quota-disabled row once and re-enable it on success ([#12874](https://github.com/diegosouzapw/OmniRoute/issues/12874)) ([#13017](https://github.com/diegosouzapw/OmniRoute/pull/13017)) — thanks @HouMinXi +- **fix(api):** thread `X-OmniRoute-Fallback-Attempts` through combo chat completions so streaming and non-streaming responses report how many prior legs were attempted ([#13038](https://github.com/diegosouzapw/OmniRoute/pull/13038)) — thanks @HouMinXi +- Restore provider failure classification and credential refresh on non-streaming requests: classify non-2xx failures to lock models on per-model quota exhaustion, update connection rate limits from headers and body, and pass credential refresh handlers to pipeline execution so 401 tokens can be refreshed and retried (#13043). ([#13069](https://github.com/diegosouzapw/OmniRoute/pull/13069)) — thanks @HouMinXi +- **fix(responses):** wrap forced-non-streaming web_search fallback JSON as Responses SSE so Codex still sees `response.completed` ([#13050](https://github.com/diegosouzapw/OmniRoute/pull/13050)) — thanks @HouMinXi +- **fix(antigravity):** Preserve upstream thought-token usage in normalized completion and reasoning token counts ([#13055](https://github.com/diegosouzapw/OmniRoute/pull/13055)) — thanks @pacocartones +- Fix a concurrency-slot leak in the DeepSeek PoW solver: a worker that failed to spawn (for example a missing worker script) never released its slot, so `MAX_CONCURRENT_WORKERS` failures disabled the solver until restart. ([#13097](https://github.com/diegosouzapw/OmniRoute/pull/13097)) — thanks @anhtahaylove +- **fix(acp):** bound the ACP session output buffers — `stdoutBuffer` and `stderrBuffer` now cap at 1 MiB keeping the most recent output behind a visible `[...output truncated...]` marker, and `stderrBuffer` is reset per prompt instead of accumulating for the lifetime of the session. ([#13100](https://github.com/diegosouzapw/OmniRoute/pull/13100)) — thanks @anhtahaylove +- **fix(acp):** release the `stdout`/`exit` listeners and the idle timer that a `sendPrompt` timeout used to leave attached to the `acpManager` singleton, and drop sessions that exited on their own from the session map instead of keeping them forever. ([#13096](https://github.com/diegosouzapw/OmniRoute/pull/13096)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection and PII scanners now read the text a `tool_result` block carries on `content` (string or nested block list), in messages and in system blocks, so tool output is judged by the same rules as user text ([#13101](https://github.com/diegosouzapw/OmniRoute/pull/13101)) — thanks @ntdatt812 +- **fix(gamification):** close the badge notification SSE stream when the request signal is already aborted before the stream starts — a client that disconnects while the route is still awaiting auth used to leave both the 2s unlock poll and the 15s heartbeat running for the lifetime of the process. ([#13106](https://github.com/diegosouzapw/OmniRoute/pull/13106)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection scan now spends its 16 KB budget on both ends of the request instead of the first 16 KB only, so `system`, `instructions`, `query`, `documents` and the newest turns are no longer hidden behind one long message ([#13104](https://github.com/diegosouzapw/OmniRoute/pull/13104)) — thanks @ntdatt812 +- **fix(dashboard):** expose the Volcano Ark console cookie on quota scraping and unwrap connect-error objects so the dashboard shows the upstream message ([#13107](https://github.com/diegosouzapw/OmniRoute/pull/13107)) — thanks @HouMinXi +- **fix(db):** release the `beforeExit`/`SIGINT`/`SIGTERM` handlers when a `node:sqlite` adapter closes, so a closed adapter and its database handle are no longer pinned to `process` for the lifetime of the run — the same treatment #7494 gave the sql.js adapter. ([#13109](https://github.com/diegosouzapw/OmniRoute/pull/13109)) — thanks @anhtahaylove +- **fix(translator):** `contentSchema` and `unevaluatedItems` are now treated as subschema positions by the tool-schema sanitizer, so a truncation placeholder in either is replaced with a permissive schema instead of being forwarded as a string ([#13110](https://github.com/diegosouzapw/OmniRoute/pull/13110)) — thanks @ntdatt812 +- **fix(cli-helper):** clear the `createLogStream` timeout on the abort path — `stop()` aborts the in-flight fetch and returned through the `signal.aborted` branch, which skipped `clearTimeout` and left an armed timer per stopped stream. The stream reader is now also cancelled when the read loop exits early. ([#13114](https://github.com/diegosouzapw/OmniRoute/pull/13114)) — thanks @anhtahaylove +- **fix(docs):** drop TLS-impersonation, MITM-decrypt, supply-chain attestation, and XOR-mask writeups from the public `/docs` catalog and Docker image. Files stay in git for engineers; operators who need them open the repo, not the website. ([#13136](https://github.com/diegosouzapw/OmniRoute/pull/13136)) — thanks @HouMinXi +- **fix(combo):** parse numeric-epoch `rate_limited_until` in the combo cooldown read path ([#13141](https://github.com/diegosouzapw/OmniRoute/pull/13141)) — thanks @maxmad64bis +- **fix(opencode):** both OpenCode plugins now reject a gateway address typed without `http://` at configuration time, instead of publishing every model with an api url no client can call, and the v2 plugin no longer publishes a model card whose api url is blank or relative ([#13142](https://github.com/diegosouzapw/OmniRoute/pull/13142)) — thanks @maxmad64bis +- **fix(providers):** lock opencode model on upstream 400 model-unavailable ([#13146](https://github.com/diegosouzapw/OmniRoute/pull/13146)) — thanks @maxmad64bis +- **fix(logging):** keep the provider exchange rather than the raw client bodies when a call log exceeds its size budget, and show that recovered payload in the request-detail panel instead of replacing it with the stored response body ([#13147](https://github.com/diegosouzapw/OmniRoute/pull/13147)) — thanks @maxmad64bis +- **fix(traffic-inspector):** the WebSocket route no longer leaks a traffic-buffer subscriber and a 30s ping timer when the client socket is already closed at handler time — listeners are attached before any resource is acquired, a destroyed socket bails out early, and the ping interval stops on a dead socket where `write()` never throws ([#13155](https://github.com/diegosouzapw/OmniRoute/pull/13155)) — thanks @anhtahaylove +- **fix(telegram):** bound the per-user API key cache in the Telegram chat proxy so a burst of distinct chat ids can no longer grow the process heap without limit ([#13165](https://github.com/diegosouzapw/OmniRoute/issues/13165)) ([#13166](https://github.com/diegosouzapw/OmniRoute/pull/13166)) — thanks @anhtahaylove +- **fix(stream):** cancel the upstream response body when the JSON-to-SSE sniff unwinds on a body timeout, so a stalled upstream no longer pins the connection ([#13169](https://github.com/diegosouzapw/OmniRoute/issues/13169)) ([#13171](https://github.com/diegosouzapw/OmniRoute/pull/13171)) — thanks @anhtahaylove +- **fix(telegram):** authenticate webhook deliveries with Telegram's `secret_token` so an unauthenticated caller can no longer mint API keys or spend upstream quota ([#13172](https://github.com/diegosouzapw/OmniRoute/issues/13172)) ([#13175](https://github.com/diegosouzapw/OmniRoute/pull/13175)) — thanks @anhtahaylove +- **fix(models):** give discoverable Gemini 3.8 Flash ids their own 65536 output spec so Antigravity no longer clamps them to 16384 ([#13195](https://github.com/diegosouzapw/OmniRoute/pull/13195)) — thanks @HouMinXi +- **fix(dashboard):** leftover catalog-removed provider rows (gemini-cli) can be listed and purged from the providers page ([#13067](https://github.com/diegosouzapw/OmniRoute/issues/13067)) ([#13197](https://github.com/diegosouzapw/OmniRoute/pull/13197)) — thanks @HouMinXi +- **fix(auth):** a dashboard session now requires the `authenticated: true` claim that login, OIDC and the session refresh already emit — a JWT merely signed with `JWT_SECRET` (for example the Cursor CLI passthrough token, which any API-key holder can obtain) no longer verifies as the `auth_token` cookie on any route, the WebSocket handshake or the live server; existing sessions keep working ([#13298](https://github.com/diegosouzapw/OmniRoute/issues/13298)) ([#13375](https://github.com/diegosouzapw/OmniRoute/pull/13375)) +- **fix(api):** `DELETE /v1/batches/delete-completed` now applies the caller's API-key policy (endpoint allowlist, schedule, usage cap, rate limit) like every other `/v1` route, commits the key-scoped sweep per 200-batch chunk instead of holding one write lock for the whole sweep, refuses to loop without progress, and rejects a scope that names both a key and `allTenants` ([#13684](https://github.com/diegosouzapw/OmniRoute/pull/13684)) +- Restore the API-route typecheck gate: the non-streaming leg result lost its discriminated-union narrowing after the server-owned tool loop reassignment, producing 13 new TS2339 diagnostics in `chatCore.ts`. ([#12963](https://github.com/diegosouzapw/OmniRoute/pull/12963)) +- Restore four non-streaming behaviours the server-owned tool loop refactor dropped: client aborts map to 499 with the fixed `Request aborted` message, an aborted request no longer logs a synthetic `clientResponse`, Claude prompt-cache telemetry is recorded again, and a body that cannot be canonicalized no longer throws when the tool loop is off. ([#12990](https://github.com/diegosouzapw/OmniRoute/pull/12990)) +- **fix(skills):** The CLI registry parser now reads positionals declared with `.addArgument()`, not only those written inline in `.command()`. `tunnel create [type]` was being published as `tunnel create`, so the agent-skills sync gate reported drift on every branch and regenerating would have deleted the argument. ([#13009](https://github.com/diegosouzapw/OmniRoute/pull/13009)) — thanks @ntdatt812 +- **fix(combos):** dashboard combo test uses a short prompt, serial probes, and a 60s timeout so reasoning models and rate-limited free pools do not fail the health check ([#13001](https://github.com/diegosouzapw/OmniRoute/pull/13001)) — thanks @HouMinXi +- fix(compression): terminate idle worker threads on eviction so long-running instances stop leaking OS threads and MessagePorts ([#13091](https://github.com/diegosouzapw/OmniRoute/pull/13091)) — thanks @anhtahaylove +- Redact Google API keys of any length in error bodies: the pattern required exactly 39 characters, so shorter or longer `AIza…` credentials passed through unredacted. ([#12964](https://github.com/diegosouzapw/OmniRoute/pull/12964)) +- **fix(grok-cli):** a 402 "Grok Build usage balance exhausted" parks that Grok login as out of credit (Grok Build CLI, grok.com cookie, and xAI OAuth share the weekly pool). Combo routing then tries the next login instead of locking the model for every account in the pool ([#13061](https://github.com/diegosouzapw/OmniRoute/pull/13061)) — thanks @HouMinXi +- fix(compression): spawn the LLMLingua worker with a file URL object so compression actually runs instead of silently failing open on Node ([#13093](https://github.com/diegosouzapw/OmniRoute/pull/13093)) — thanks @anhtahaylove +- **fix(test):** run the local `test` and `test:unit` scripts at concurrency 4 so a full-suite run no longer exhausts the machine's commit charge and kills unrelated processes ([#13187](https://github.com/diegosouzapw/OmniRoute/pull/13187)) — thanks @anhtahaylove +- **fix(monitoring):** `GET /api/monitoring/health` `credentialHealth` now includes a bounded `failedConnections` list (`connectionId`, `status`, sanitized `lastError`) when the probe-cache gauge `failed>0`, plus `source: probe-cache` and a cheap `staleDbNonOkCount` for sticky SQLite `test_status` on active rows. Documents that the live gauge is not `provider_connections.test_status`. ([#12876](https://github.com/diegosouzapw/OmniRoute/pull/12876)) — thanks @RaviTharuma +- **fix(resilience):** an openai-compatible multi-upstream gateway no longer marks the whole connection `credits_exhausted` when a single upstream (or the `/models` / representative-model health probe) returns 402. Quota failures stay model-scoped; true connection-wide auth failures are unchanged. Single-credential 402 key disable ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239)) is preserved. ([#12875](https://github.com/diegosouzapw/OmniRoute/pull/12875)) — thanks @RaviTharuma +- **fix(dashboard):** nine fixes on the Orchestration Canvas, all diagnosed in the Phase 2 reviews. A source that fails now keeps the timestamp of its FIRST failure instead of being re-stamped every poll — the stale line said "since the last poll" no matter how long the source had been down, and the churn also defeated the snapshot's stable identity (it serializes the source list), so the canvas re-rendered on every tick while anything was broken. A source that HAD data and only then started failing is flagged too: previously only a source with no node at all got the warning, so a source that went down mid-session kept a healthy-looking node forever. The rest are pointwise: clicking a filter chip cancels the pending search debounce (left armed, it fired ~300ms later and silently reverted the chip); the search input carries an accessible name; `?state=running, failed` parses like the unpadded form instead of dropping the padded value; the CSV toggle helper is defined once in `model/urlParams.ts` rather than twice; the agents tab tells "nothing running" apart from "the filter matched nothing", offering a clear-filters button instead of setup links that would be wrong advice there; edges stop emitting SMIL particles above 40 simultaneously active edges, keeping the colored stroke; and the drawer's error banner clears when a retried action succeeds. ([#12983](https://github.com/diegosouzapw/OmniRoute/pull/12983)) +- **fix(i18n):** the nine locales added with the EU-language batch (Greek, Estonian, Irish, Croatian, Lithuanian, Latvian, Maltese, Slovenian, Serbian) were missing the eleven Orchestration Canvas keys that Phase 3 introduced, so the compare-runs panel and the "no runs match these filters" empty state fell back to English in those languages (`deepMergeFallback` substitutes English for an absent key, so nothing rendered blank — it rendered untranslated). The coverage gate does not catch this: it enforces an 80% floor per locale, and eleven missing keys out of ~13,000 leaves coverage at 99.9%. Translated for real in each language, calibrated against the wording each file already uses for "run", "filter" and "skill". ([#13294](https://github.com/diegosouzapw/OmniRoute/pull/13294)) +- **fix(dashboard):** five follow-ups from the Phase 2 Orchestration Canvas review. The History drawer now shows the memory section for runs that already left the live TTL window: the persisted `memory_hits` event is parsed into `metadata.memoryHits` with the same defensive validation the drawer applies, and — because that event is observability rather than a state transition — it no longer leaks into the timeline, where it had been inheriting the task's state and rendering as a duplicate transition. The A2A memory recall runs against its own 1.5s deadline instead of inheriting the memory backend's 30s one; overshooting degrades exactly like any other recall failure (no hits, task proceeds), and the timer is cleared on both paths. Repeating a Conductor run carries its `requirements.cli`/`requirements.model` forward, so the new run is pinned to the same runner profile and model rather than drifting to whatever the fleet picks. A successful repeat from the Agents tab now focuses the run it created, instead of leaving the operator on the finished one. And the auth test for `POST /api/conductor/tasks` moved into the shared `ROUTES` array rather than restating the pattern. ([#12988](https://github.com/diegosouzapw/OmniRoute/pull/12988)) +- fix(plugins): stop leaking an exit listener per plugin hook timeout, which triggered MaxListenersExceededWarning on plugins that ignore SIGTERM ([#13092](https://github.com/diegosouzapw/OmniRoute/pull/13092)) — thanks @anhtahaylove +- **fix(validation):** Provider node edits no longer fail with a generic "Invalid request" when the optional daily-quota reset fields are left blank. The dashboard sends `dailyQuotaResetTimezone` and `dailyQuotaResetHour` as `null`, and only the hour accepted it. ([#13066](https://github.com/diegosouzapw/OmniRoute/issues/13066)) ([#13083](https://github.com/diegosouzapw/OmniRoute/pull/13083)) — thanks @ntdatt812 +- **fix(combo):** quota-aware expansion drops banned, inactive, missing, and wrong-provider connections before quota fetch or model dispatch; pins and allowlists stay selectors, not a bypass. Antigravity automatic exhaustion now requires a reported zero remaining, so a positive balance below 1% stays eligible. ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- Fix an intermittent failure in the quota-weighted routing test suite: scores are a function of `Date.now()`, so peers with identical quota scored microseconds apart never tied and swapped order. ([#12946](https://github.com/diegosouzapw/OmniRoute/pull/12946)) +- fix(volcengine): resolve quota window mapping gaps, safe multi-connection console binding, and response cookie sanitization ([#12950](https://github.com/diegosouzapw/OmniRoute/pull/12950)) — thanks @HouMinXi +- **fix(test):** resolve the WebDAV handler path with `fileURLToPath` so the suite's 37 WebDAV tests run on Windows instead of failing with a doubled `C:\C:\` drive prefix ([#13196](https://github.com/diegosouzapw/OmniRoute/pull/13196)) — thanks @anhtahaylove +- **fix(ui):** fall back from invalid AnySearch icon alias (direct commit `9587e07b69`) +- **fix(sse):** stop rebuilding a truncated summary from the collector's cap-dropped event array ([#12718](https://github.com/diegosouzapw/OmniRoute/pull/12718)) — thanks @hartmark +- **fix(conversations):** show a pending spinner for unresolved tool nodes instead of "(empty)" ([#12727](https://github.com/diegosouzapw/OmniRoute/pull/12727)) — thanks @hartmark +- **fix(streaming):** fail fast when an upstream stream produces only lifecycle/heartbeat events, never real content ([#12741](https://github.com/diegosouzapw/OmniRoute/pull/12741)) — thanks @hartmark +- **fix(responses-continuation):** bridge the write-in-flight window with an in-memory pending store ([#12854](https://github.com/diegosouzapw/OmniRoute/pull/12854)) — thanks @hartmark +- **fix(translator):** drop or map agent_message on Chat Completions fallback ([#12880](https://github.com/diegosouzapw/OmniRoute/pull/12880)) — thanks @RaviTharuma +- **fix(api):** page and stream-complete GET /v1/models for large catalogs ([#12882](https://github.com/diegosouzapw/OmniRoute/pull/12882)) — thanks @RaviTharuma +- **fix(quota):** align AUTH window labels with usage API and clarify 503 ([#12884](https://github.com/diegosouzapw/OmniRoute/pull/12884)) — thanks @RaviTharuma +- **fix(ci):** clear two base-reds on release/v3.8.51 — mutation-coverage gate + image-only-model guard ([#12945](https://github.com/diegosouzapw/OmniRoute/pull/12945)) +- **fix(api):** scope batch bulk-delete to the calling API key ([#13211](https://github.com/diegosouzapw/OmniRoute/pull/13211)) +- **fix(tests):** align model-sync log assertion to shipped path redaction (base-red) ([#13227](https://github.com/diegosouzapw/OmniRoute/pull/13227)) +- **fix(test):** make the ToS heading guard actually require the parentheses ([#13228](https://github.com/diegosouzapw/OmniRoute/pull/13228)) +- **fix(quality):** register 4 drifted covering tests in stryker tap.testFiles ([#13229](https://github.com/diegosouzapw/OmniRoute/pull/13229)) +- **fix(logging):** mask *-api-key headers in request-log pipeline (#13273) ([#13401](https://github.com/diegosouzapw/OmniRoute/pull/13401)) — thanks @KooshaPari +- **fix(tests):** correct stale Jina catalog prefix in custom-models test ([#13403](https://github.com/diegosouzapw/OmniRoute/pull/13403)) — thanks @KooshaPari +- **fix(backend):** prune old backups after health-check-repair snapshots (#13308) ([#13404](https://github.com/diegosouzapw/OmniRoute/pull/13404)) — thanks @KooshaPari +- **fix(backend):** guard memory_fts_au trigger with WHEN clause to stop FTS bloat (#13326) ([#13405](https://github.com/diegosouzapw/OmniRoute/pull/13405)) — thanks @KooshaPari +- **fix(free-tiers):** remove obsolete Together $25 signup credit (#12526) ([#13407](https://github.com/diegosouzapw/OmniRoute/pull/13407)) — thanks @KooshaPari +- **fix:** skip non-chat generation models in Test all models ([#13409](https://github.com/diegosouzapw/OmniRoute/pull/13409)) — thanks @KooshaPari +- **fix(providers):** de-duplicate agy and antigravity model catalogs ([#13410](https://github.com/diegosouzapw/OmniRoute/pull/13410)) — thanks @KooshaPari +- **fix(dashboard):** send provider in body for mini-playground web search ([#13411](https://github.com/diegosouzapw/OmniRoute/pull/13411)) — thanks @KooshaPari +- **fix(resilience):** add CJK quota-exhaustion patterns to 429 classifier ([#13412](https://github.com/diegosouzapw/OmniRoute/pull/13412)) — thanks @KooshaPari +- **fix(backend):** eval runner: failed calls no longer score as passed + dotAll regex ([#13413](https://github.com/diegosouzapw/OmniRoute/pull/13413)) — thanks @KooshaPari +- **fix(backend):** eval runner opts out of output-style and memory injection ([#13414](https://github.com/diegosouzapw/OmniRoute/pull/13414)) — thanks @KooshaPari +- **fix:** chain serial quarantine step in npm test script (fixes #13286) ([#13418](https://github.com/diegosouzapw/OmniRoute/pull/13418)) — thanks @KooshaPari +- **fix(antigravity):** derive stable session ID from credentials for prompt-cache reuse (#13305) ([#13424](https://github.com/diegosouzapw/OmniRoute/pull/13424)) — thanks @KooshaPari +- **fix(memory):** merge memory into top-level system field instead of unshifting at messages[0] (#13425) ([#13427](https://github.com/diegosouzapw/OmniRoute/pull/13427)) — thanks @KooshaPari +- **fix(compression):** preserve instruction blocks from lossy engines ([#13523](https://github.com/diegosouzapw/OmniRoute/pull/13523)) — thanks @KooshaPari +- **fix(compression):** preserve polarity words and newlines in ultra heuristic ([#13524](https://github.com/diegosouzapw/OmniRoute/pull/13524)) — thanks @KooshaPari +- **fix(compression):** run user preservePatterns before built-in patterns (#13457) ([#13525](https://github.com/diegosouzapw/OmniRoute/pull/13525)) — thanks @KooshaPari +- **fix(reasoning):** expand deepseek-v4 regex to match v4.1+ and add command-code provider (#13430) ([#13528](https://github.com/diegosouzapw/OmniRoute/pull/13528)) — thanks @KooshaPari +- **fix(providers):** add alwaysReasons metadata for reka-flash-3 ([#13531](https://github.com/diegosouzapw/OmniRoute/pull/13531)) — thanks @KooshaPari +- **fix(playground):** allow Compare columns to scroll independently ([#13532](https://github.com/diegosouzapw/OmniRoute/pull/13532)) — thanks @KooshaPari +- **fix(security):** empty whitelist allows through to prevent admin lockout ([#13534](https://github.com/diegosouzapw/OmniRoute/pull/13534)) — thanks @KooshaPari +- **fix(tests):** add DISABLE_IOREG_STRATEGY env var for macOS test compatibility (fixes #13467) ([#13539](https://github.com/diegosouzapw/OmniRoute/pull/13539)) — thanks @KooshaPari +- **fix(zed-hosted):** map developer role and Gemini enums for Zed proxy ([#13543](https://github.com/diegosouzapw/OmniRoute/pull/13543)) — thanks @KooshaPari +- **fix(analytics):** normalize provider aliases to canonical id in usage_history ([#13545](https://github.com/diegosouzapw/OmniRoute/pull/13545)) — thanks @KooshaPari +- **fix(combo):** use traceId for call log id to prevent UNIQUE constraint failure ([#13546](https://github.com/diegosouzapw/OmniRoute/pull/13546)) — thanks @KooshaPari +- **fix(auth):** check model lockout before returning synthetic noauth connection ([#13547](https://github.com/diegosouzapw/OmniRoute/pull/13547)) — thanks @KooshaPari +- **fix(guardrails):** skip credential redaction for base64 image data URLs ([#13550](https://github.com/diegosouzapw/OmniRoute/pull/13550)) — thanks @KooshaPari +- **fix(resilience):** increase requestQueue.maxWaitMs default from 15s to 30s ([#13553](https://github.com/diegosouzapw/OmniRoute/pull/13553)) — thanks @KooshaPari +- **fix(docs):** document OMNIROUTE_READY_TIMEOUT_MS and allowlist the test-only DISABLE_IOREG_STRATEGY ([#13692](https://github.com/diegosouzapw/OmniRoute/pull/13692)) ### 📝 Maintenance @@ -853,6 +1054,39 @@ _By commits in `release/v3.8.50..release/v3.8.51` (`091589089c..d6f315018a`), au - **docs(security):** document socket.yml scanner config + CI workflow link (#12575) ([#12764](https://github.com/diegosouzapw/OmniRoute/pull/12764)) — thanks @KooshaPari - **chore(ci):** guard commit identity in pre-commit to stop author misattribution ([#12772](https://github.com/diegosouzapw/OmniRoute/pull/12772)) - **deps:** 17 Dependabot bumps — bump electron from 43.4.0 to 43.4.1 in /electron ([#11426](https://github.com/diegosouzapw/OmniRoute/pull/11426)); bump the development group across 1 directory with 8 updates ([#11428](https://github.com/diegosouzapw/OmniRoute/pull/11428)); bump the production group across 1 directory with 11 updates ([#11440](https://github.com/diegosouzapw/OmniRoute/pull/11440)); chore(deps): bump github/codeql-action/init from 4.37.7 to 4.37.8 ([#11925](https://github.com/diegosouzapw/OmniRoute/pull/11925)); chore(deps): bump github/codeql-action from 4.37.7 to 4.37.8 ([#11926](https://github.com/diegosouzapw/OmniRoute/pull/11926)); chore(deps): bump github/codeql-action/analyze from 4.37.7 to 4.37.8 ([#11927](https://github.com/diegosouzapw/OmniRoute/pull/11927)); build(deps): bump github/codeql-action/init from 4.37.8 to 4.37.9 ([#12345](https://github.com/diegosouzapw/OmniRoute/pull/12345)); build(deps): bump github/codeql-action/analyze from 4.37.8 to 4.37.9 ([#12346](https://github.com/diegosouzapw/OmniRoute/pull/12346)); bump the development group across 1 directory with 2 updates ([#12347](https://github.com/diegosouzapw/OmniRoute/pull/12347)); build(deps): bump github/codeql-action from 4.37.8 to 4.37.9 ([#12349](https://github.com/diegosouzapw/OmniRoute/pull/12349)); bump browserslist from 4.28.2 to 4.28.8 ([#12396](https://github.com/diegosouzapw/OmniRoute/pull/12396)); bump the production group across 1 directory with 4 updates ([#12399](https://github.com/diegosouzapw/OmniRoute/pull/12399)); bump @xmldom/xmldom ([#12500](https://github.com/diegosouzapw/OmniRoute/pull/12500)); bump qs from 6.15.2 to 6.16.0 ([#12512](https://github.com/diegosouzapw/OmniRoute/pull/12512)); bump @xmldom/xmldom from 0.9.10 to 0.9.12 ([#12513](https://github.com/diegosouzapw/OmniRoute/pull/12513)); bump fast-uri from 3.1.5 to 3.1.7 ([#12514](https://github.com/diegosouzapw/OmniRoute/pull/12514)); bump @humanfs/node from 0.16.7 to 0.16.8 ([#12515](https://github.com/diegosouzapw/OmniRoute/pull/12515)) +- Clear the `release/v3.8.51` `check:agent-skills-sync` base-red: regenerate `skills/cli-tunnel/SKILL.md` so the `tunnel create [type]` positional that #13009 taught the generator to read is reflected in the committed skill. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` typecheck base-red from #12731: the new `Mode pack ranking gates` candidates in `open-sse/services/autoCombo/__tests__/autoCombo.test.ts` omitted the required `provider`, `model` and `errorRate` fields of `ProviderCandidate`, failing `check:open-sse-typecheck` (and with it `Fast Quality Gates`) on every open PR. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` docs-sync base-red: EURouter (#13025) and GreenPT (#13024) took the live provider count to 358, leaving 7 STRICT drifts (`PROVIDER_REFERENCE.md`, 4 diagrams, 2 tier-flow images) plus the `AGENTS.md` / `llm.txt` / `package.json` count claims stale. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- **test(models):** Pin `CATALOG_BUILD_TIMEOUT_MS` in the #12058 canonical-catalog regression so a cold catalog build racing the 8s default bound no longer flakes the suite with a 500 ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Allow `@omniroute/opencode-plugin-v2/` in the npm pack policy — [#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870) shipped the v2 plugin beside its v1 sibling without widening the allowlist, so every packed file under it read as an unexpected artifact and failed `check:pack-policy` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Point the release-green validator's pack gate at the tree it is validating — the build-provenance guard defaulted to `origin/main`, which a release branch tip can never be an ancestor of mid-cycle, so `Package artifact` reported a false red on every run and the tarball boot-smoke cascaded off it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **docs(checks):** keep doc counts honest — headings, rankings, catalog, weights, quality gate and scoring diagram now covered ([#12507](https://github.com/diegosouzapw/OmniRoute/pull/12507)) — thanks @maxmad64bis +- **chore(lifecycle):** `check:model-lifecycle` now also diffs `DEFAULT_DEGRADATION_MAP` (the background-task redirect table) against the vendor lifecycle snapshot, refusing a retired id as source or target, with a table-driven unit test beside it. Three rows whose source the vendor had retired — `claude-sonnet-4-20250514`, `gemini-3-pro-preview` and `gpt-5.1-codex` (whose target `gpt-5.1-codex-mini` is retired too) — were dead code, since `checkLifecycle` answers 410 before the redirect runs; they are dropped (#12535 — thanks @pacocartones) +- **test(compression):** cover idle worker eviction at the resource level — the pool must call `terminate()` and must not retain the worker's `MessagePort`, complementing the `exit`-event assertion added with the fix ([#13371](https://github.com/diegosouzapw/OmniRoute/pull/13371)) +- **chore(quality):** raise the `imageGeneration.ts` file-size ceiling for the image-only-model guard that clears the #12945 base-red ([#13392](https://github.com/diegosouzapw/OmniRoute/pull/13392)) +- **chore(quality):** type the combo-test route's JSON response bodies instead of casting them to `any`, drop the now-empty suppression entry, and remove the `hasPerModelQuota` import `chatCore.ts` stopped using when the failure-classification helper was extracted ([#13379](https://github.com/diegosouzapw/OmniRoute/pull/13379)) +- **chore(electron):** the desktop bundle no longer ships the root-level files of every translated docs mirror (`README.md`, `llm.txt`, `CLAUDE.md`, `GEMINI.md`, `CONTRIBUTING.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`) — the packaged app only reads `docs/i18n//docs/**`, which stays. Saves ~11 MB on top of the translated CHANGELOGs already pruned. (#0000) ([#13235](https://github.com/diegosouzapw/OmniRoute/pull/13235)) +- **chore(quality):** retighten the `src/sse/handlers/chat.ts` unused-vars suppression count to the 8 violations that actually remain ([#13385](https://github.com/diegosouzapw/OmniRoute/pull/13385)) +- Bring the documented counts back in line with the code: 171 migrations (was 169) and 20 routing strategies (was 19), plus the regenerated `cli-tunnel` skill reference. ([#12970](https://github.com/diegosouzapw/OmniRoute/pull/12970)) +- Update the documented migration count to 172 after the call-logs provider-stats indexes landed. ([#13209](https://github.com/diegosouzapw/OmniRoute/pull/13209)) +- **chore(ci):** two gates that close the blind spots behind the exclusions above. `check:vitest-exclusions` requires every Vitest exclusion to name a tracking issue and to appear in `config/quality/vitest-exclusions.json` — the previous list grew to 62 files behind a comment pointing at an issue that had been closed for a month. `check-new-key-coverage` requires a key newly added to `en.json` to reach every locale; the existing coverage gate is a percentage floor per locale, so eleven absent keys out of ~13,000 left it at 99.9% while a whole feature shipped untranslated in nine languages. Both are diff-aware, so pre-existing debt stays frozen and neither needed a migration to turn on. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **build:** root layout no longer loads Inter from `next/font/google`, so a production image build does not need fonts.googleapis.com ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **docs(i18n):** bring the eight new `llm.txt` mirrors (km, kn, ml, my, ne, or, pa, si) up to the current provider and migration counts, so `check-docs-sync` stops rejecting every commit ([#13674](https://github.com/diegosouzapw/OmniRoute/pull/13674)) +- **refactor(ui):** flow surfaces (home topology, combo live studio, compression cockpit/waterfall/nodes, token-health badge) express state through the theme-aware `--orch-status-*` tokens instead of fixed dark-mode hex, so green/red/amber/grey stay legible in the light theme; dark mode is byte-identical. Categorical palettes (routing-strategy hues, compression-layer pills, provider brand colors) deliberately stay hex ([#12378](https://github.com/diegosouzapw/OmniRoute/issues/12378)) ([#13203](https://github.com/diegosouzapw/OmniRoute/pull/13203)) +- **chore(tests):** 51 test files that had been excluded from Vitest are running again, restoring roughly 350 assertions to the blocking suite. Each was measured individually first: of the 62 files parked behind the `// #8618 — pre-existing failure` comment, 51 pass against the current tree with no source change, so the exclusions had outlived the failures they were added for. The 11 that genuinely still fail stay excluded, but now point at a live tracker (#13204) rather than at #8618, which was closed in August while the list it tracked kept growing. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **chore(quality):** drop two ESLint suppression entries whose violations no longer exist, so `eslint --suppressions-location` stops rejecting every commit that touches the surrounding files ([#13384](https://github.com/diegosouzapw/OmniRoute/pull/13384)) +- **chore(release):** bring main's post-tag fixes into release/v3.8.51 — sync-back step 2/2 (direct commit `21c488f210`) +- **chore(deps):** patch toml and esbuild in the opencode plugin lockfiles ([#12965](https://github.com/diegosouzapw/OmniRoute/pull/12965)) +- **docs(changelog):** reconcile the v3.8.51 living section — fold 366 fragments, cover every cycle commit, credit every contributor ([#12971](https://github.com/diegosouzapw/OmniRoute/pull/12971)) +- **test(plugin):** wait for the v2 lazy-refresh tier instead of sleeping past it ([#12984](https://github.com/diegosouzapw/OmniRoute/pull/12984)) +- **chore(quality):** rebaseline chatCore.ts after the non-streaming regression fixes ([#13045](https://github.com/diegosouzapw/OmniRoute/pull/13045)) +- **docs(changelog):** add the missing bullet marker to the DeepSeek PoW fragment ([#13200](https://github.com/diegosouzapw/OmniRoute/pull/13200)) +- **chore(deps):** drain the Dependabot queue — 10 of 13 alerts ([#13213](https://github.com/diegosouzapw/OmniRoute/pull/13213)) +- **chore(ci):** make worktrees carry the hooks that enforce the identity gate ([#13236](https://github.com/diegosouzapw/OmniRoute/pull/13236)) +- **chore(deps):** pin csv-parse to ^7.0.2 via overrides (#13117) ([#13300](https://github.com/diegosouzapw/OmniRoute/pull/13300)) — thanks @KooshaPari +- **chore(deps):** bump hono from ^4.12.34 to ^4.13.7 (#13148) ([#13301](https://github.com/diegosouzapw/OmniRoute/pull/13301)) — thanks @KooshaPari +- **chore(deps):** pin joi to ^18.2.8 via overrides (#13085) ([#13302](https://github.com/diegosouzapw/OmniRoute/pull/13302)) — thanks @KooshaPari +- **docs(providers):** add Microsoft 365 Copilot (BizChat) provider guide (#12779) ([#13340](https://github.com/diegosouzapw/OmniRoute/pull/13340)) — thanks @KooshaPari ### 🙌 Contributors @@ -870,6 +1104,7 @@ Thanks to everyone whose work landed in v3.8.51: | [@amaleta](https://github.com/amaleta) | #11906, #11934 | | [@amartinawi](https://github.com/amartinawi) | #11575 | | [@AndrianBalanescu](https://github.com/AndrianBalanescu) | #11083, #11830, #11834, #11844 | +| [@anhtahaylove](https://github.com/anhtahaylove) | #13091, #13092, #13093, #13096, #13097, #13100, #13106, #13109, #13114, #13155, #13166, #13171, #13175, #13187, #13196 | | [@aniruddhaadak80](https://github.com/aniruddhaadak80) | #11760 | | [@app](https://github.com/app) | #12554 | | [@arminanton](https://github.com/arminanton) | #11461, #11513 | @@ -899,10 +1134,10 @@ Thanks to everyone whose work landed in v3.8.51: | [@gonisulaimann](https://github.com/gonisulaimann) | #12368, #12369, #12371 | | [@Gorillaz322](https://github.com/Gorillaz322) | #12207 | | [@groovecityJO](https://github.com/groovecityJO) | #12682 | -| [@hartmark](https://github.com/hartmark) | #11434, #11452, #11473, #11499, #11703, #11983, #11984, #11985, #11986, #11988, #11989, #11990, #11994, #12221, #12293, #12445, #12446, #12447, #12448, #12460, #12461, #12623, #12646, #12650, #12680 | +| [@hartmark](https://github.com/hartmark) | #11434, #11452, #11473, #11499, #11703, #11983, #11984, #11985, #11986, #11988, #11989, #11990, #11994, #12221, #12293, #12445, #12446, #12447, #12448, #12460, #12461, #12623, #12646, #12650, #12680, #12717, #12718, #12727, #12741, #12854 | | [@hizzt](https://github.com/hizzt) | #11894 | | [@hongnoul](https://github.com/hongnoul) | #11484 | -| [@HouMinXi](https://github.com/HouMinXi) | #11411, #11414, #11512, #11518, #11520, #11641, #11642, #11643, #11687, #11779, #11849, #11850, #11851, #11915, #11916, #11918, #11919, #11920, #12013, #12017, #12033, #12042, #12106, #12139, #12166, #12169, #12171, #12205, #12213, #12312, #12325, #12487, #12488, #12495, #12504, #12557, #12566, #12590, #12591, #12624, #12626, #12632, #12637, #12678, #12696, #12697, #12711, #12733, #12746, #12767, #12770, #12789, #12803, #12805, #12811, #12866, #12868, #12899, #12926, #12934 | +| [@HouMinXi](https://github.com/HouMinXi) | #11411, #11414, #11512, #11518, #11520, #11641, #11642, #11643, #11687, #11779, #11849, #11850, #11851, #11915, #11916, #11918, #11919, #11920, #12013, #12017, #12033, #12042, #12106, #12139, #12166, #12169, #12171, #12205, #12213, #12312, #12325, #12487, #12488, #12495, #12504, #12557, #12566, #12590, #12591, #12624, #12626, #12632, #12637, #12678, #12696, #12697, #12711, #12733, #12746, #12767, #12770, #12789, #12803, #12805, #12811, #12866, #12868, #12899, #12926, #12934, #12950, #12951, #12974, #13001, #13006, #13011, #13017, #13026, #13027, #13034, #13035, #13038, #13042, #13050, #13060, #13061, #13069, #13107, #13120, #13136, #13195, #13197 | | [@Hsia97](https://github.com/Hsia97) | #11624 | | [@jacobsparts](https://github.com/jacobsparts) | #11854, #12155, #12167 | | [@joglomedia](https://github.com/joglomedia) | #11980 | @@ -912,11 +1147,11 @@ Thanks to everyone whose work landed in v3.8.51: | [@KaspaPulse](https://github.com/KaspaPulse) | #11389, #11469, #11628, #11666, #11910 | | [@keeltrace](https://github.com/keeltrace) | #12080, #12223 | | [@killer30001000](https://github.com/killer30001000) | #12178 | -| [@KooshaPari](https://github.com/KooshaPari) | #12470, #12592, #12667, #12699, #12703, #12706, #12764, #12769, #12771 | +| [@KooshaPari](https://github.com/KooshaPari) | #12470, #12592, #12667, #12699, #12703, #12706, #12764, #12769, #12771, #13300, #13301, #13302, #13340, #13401, #13403, #13404, #13405, #13406, #13407, #13409, #13410, #13411, #13412, #13413, #13414, #13418, #13424, #13427, #13433, #13523, #13524, #13525, #13528, #13531, #13532, #13534, #13539, #13543, #13545, #13546, #13547, #13550, #13553 | | [@kriptoburak](https://github.com/kriptoburak) | #11370 | | [@KrzysiekSko](https://github.com/KrzysiekSko) | #12425, #12432, #12673 | | [@marcelokarval](https://github.com/marcelokarval) | direct commit / report | -| [@maxmad64bis](https://github.com/maxmad64bis) | #1622, #11435, #11437, #11441, #11537, #11550, #11553, #11555, #11557, #11812, #11842, #11843, #11903, #12151, #12214, #12215, #12218, #12226, #12314, #12316, #12317, #12318, #12319, #12320, #12321, #12870 | +| [@maxmad64bis](https://github.com/maxmad64bis) | #1622, #11435, #11437, #11441, #11537, #11550, #11553, #11555, #11557, #11812, #11842, #11843, #11903, #12151, #12214, #12215, #12218, #12226, #12314, #12316, #12317, #12318, #12319, #12320, #12321, #12507, #12715, #12731, #12744, #12786, #12787, #12788, #12790, #12792, #12794, #12795, #12828, #12832, #12853, #12857, #12870, #12937, #12941, #12975, #13141, #13142, #13146, #13147 | | [@mdigitalbh81](https://github.com/mdigitalbh81) | #12240 | | [@Meet6338-X](https://github.com/Meet6338-X) | #11598, #11609, #12110 | | [@morpheus9393](https://github.com/morpheus9393) | #11943 | @@ -925,11 +1160,11 @@ Thanks to everyone whose work landed in v3.8.51: | [@NightStalker-87](https://github.com/NightStalker-87) | #12183 | | [@NoxzRCW](https://github.com/NoxzRCW) | #11879, #11880, #11881, #11882, #11883, #11935 | | [@ntdat812](https://github.com/ntdat812) | #11585, #12095 | -| [@ntdatt812](https://github.com/ntdatt812) | #11368, #11573, #11574, #11576, #11577, #11580, #11582, #11583, #11588, #11589, #11590, #11591, #11592, #11593, #11672, #12177, #12180 | +| [@ntdatt812](https://github.com/ntdatt812) | #11368, #11573, #11574, #11576, #11577, #11580, #11582, #11583, #11588, #11589, #11590, #11591, #11592, #11593, #11672, #12177, #12180, #12873, #12918, #12920, #12921, #12925, #12930, #13007, #13009, #13024, #13025, #13083, #13087, #13101, #13104, #13110 | | [@official-burak](https://github.com/official-burak) | #11542 | | [@opensource-elearning](https://github.com/opensource-elearning) | #12179, #12189, #12278, #12286 | | [@oyi77](https://github.com/oyi77) | #11408, #11409, #11421, #11505, #11677, #12036, #12110 | -| [@pacocartones](https://github.com/pacocartones) | #11521, #11522, #11527, #11528, #11529, #11530, #11531, #11532, #11533, #11534, #11595, #11599, #11603, #11604, #11605, #11607, #11610, #11676, #11714, #11716, #11718, #11767, #11838, #11860, #11862, #11869, #11871, #11872, #11873, #11903, #11906, #11921, #11934, #12359, #12360, #12361, #12362, #12364, #12365, #12373, #12374, #12375, #12376, #12377, #12379, #12380, #12381, #12386, #12387, #12389, #12390, #12394, #12395, #12397, #12401, #12403, #12404 | +| [@pacocartones](https://github.com/pacocartones) | #11521, #11522, #11527, #11528, #11529, #11530, #11531, #11532, #11533, #11534, #11595, #11599, #11603, #11604, #11605, #11607, #11610, #11676, #11714, #11716, #11718, #11767, #11838, #11860, #11862, #11869, #11871, #11872, #11873, #11903, #11906, #11921, #11934, #12356, #12358, #12359, #12360, #12361, #12362, #12364, #12365, #12373, #12374, #12375, #12376, #12377, #12379, #12380, #12381, #12386, #12387, #12389, #12390, #12394, #12395, #12397, #12401, #12403, #12404, #12522, #12523, #12535, #12536, #12540, #12541, #12543, #12545, #12548, #12549, #12550, #12551, #12552, #12647, #12651, #12653, #13055 | | [@patrykkopycinski](https://github.com/patrykkopycinski) | #11936, #11937, #12224 | | [@PauloFH](https://github.com/PauloFH) | #11509 | | [@PauloHSOliveira](https://github.com/PauloHSOliveira) | #12241 | @@ -941,7 +1176,7 @@ Thanks to everyone whose work landed in v3.8.51: | [@raheemuddin786](https://github.com/raheemuddin786) | #11491, #11839, #11840, #11841, #12003, #12230, #12231, #12232, #12233, #12234 | | [@rahilmavani](https://github.com/rahilmavani) | #11761 | | [@Rahulsharma0810](https://github.com/Rahulsharma0810) | #11771 | -| [@RaviTharuma](https://github.com/RaviTharuma) | #11710, #11727, #11797, #11798, #11802, #11805, #11806, #11811, #12098, #12099, #12101, #12449, #12452, #12472, #12473, #12493, #12533, #12607, #12628, #12631, #12636 | +| [@RaviTharuma](https://github.com/RaviTharuma) | #11710, #11727, #11797, #11798, #11802, #11805, #11806, #11811, #12098, #12099, #12101, #12449, #12452, #12472, #12473, #12493, #12533, #12607, #12628, #12631, #12636, #12875, #12876, #12880, #12882, #12884 | | [@rezjalibd](https://github.com/rezjalibd) | #12186 | | [@RhianB14](https://github.com/RhianB14) | #11692 | | [@rifqiawl](https://github.com/rifqiawl) | #11517, #11519, #11969 | diff --git a/docs/i18n/kn/CHANGELOG.md b/docs/i18n/kn/CHANGELOG.md index e4d57688a5..1b235e8b26 100644 --- a/docs/i18n/kn/CHANGELOG.md +++ b/docs/i18n/kn/CHANGELOG.md @@ -96,31 +96,31 @@ ## [3.8.51] — TBD -_Living section — reconciled 2026-09-07 from all cycle commits (`release/v3.8.50` tip `091589089c` → `release/v3.8.51` tip `d6f315018a`, 696 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._ +_Living section — reconciled 2026-09-15 from all cycle commits (`091589089c` → `c0f92ec98a`, 916 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 | **120** | -| 📝 Commits in the cycle | **696** | -| 🔀 Pull requests referenced | **672** | -| 📋 Changelog entries | **685** | -| 🙌 Contributors credited in entries | **118** | +| 👥 People who contributed | **123** | +| 📝 Commits in the cycle | **916** | +| 🔀 Pull requests referenced | **892** | +| 📋 Changelog entries | **919** | +| 🙌 Contributors credited in entries | **121** | | 🤖 Automated dependency commits | 18 | **Entries by type** | Type | Count | | --- | ---: | -| 🐛 Fixes | 403 | -| ✨ Features | 118 | -| 🧹 Chore | 72 | -| 📚 Docs | 36 | -| 🧪 Tests | 29 | +| 🐛 Fixes | 592 | +| ✨ Features | 131 | +| 🧹 Chore | 93 | +| 📚 Docs | 42 | +| 🧪 Tests | 32 | +| ♻️ Refactor | 8 | +| 🏗️ Build | 7 | | ⚡ Performance | 7 | -| ♻️ Refactor | 7 | -| 🏗️ Build | 6 | | ⚙️ CI | 2 | | ⏪ Reverts | 2 | | 📦 Dependencies | 2 | @@ -128,35 +128,35 @@ _Living section — reconciled 2026-09-07 from all cycle commits (`release/v3.8. ### 🏆 Top 25 contributors this cycle -_By commits in `release/v3.8.50..release/v3.8.51` (`091589089c..d6f315018a`), author identities consolidated via `.mailmap` and the merged PR's GitHub login. Bots excluded._ +_By commits in `091589089c..c0f92ec98a`, author identities consolidated via `.mailmap` and the merged PR's GitHub login. Bots excluded._ | # | Contributor | Commits | | ---: | --- | ---: | -| 🥇 | diegosouzapw | 240 | -| 🥈 | Paco Cartones (@pacocartones) | 58 | -| 🥉 | Bob.Hou (@HouMinXi) | 57 | -| 4 | Dizzle (@maxmad64bis) | 27 | -| 5 | Webman (@jonlwheat2-gif) | 22 | -| 6 | Ravi Tharuma (@RaviTharuma) | 21 | -| 7 | backryun | 17 | -| 8 | Markus Hartung (@hartmark) | 17 | -| 9 | Nguyen Thanh Dat (@ntdatt812) | 16 | -| 10 | Rafa Martins (@rafacpti23) | 10 | -| 11 | Syed Raheemuddin (@raheemuddin786) | 10 | -| 12 | Koosha Paridehpour (@KooshaPari) | 9 | -| 13 | Nguyễn Viết Tuấn (@TheDemonTuan) | 8 | -| 14 | MumuTW | 7 | -| 15 | Paijo (@oyi77) | 7 | -| 16 | Tobias Andersen (@turbolego) | 6 | -| 17 | Bl0ck (@Bl0ck154) | 5 | -| 18 | SHANMUGAPRIYAN (@geek007git) | 5 | -| 19 | KaspaPulse | 5 | -| 20 | Abhishek Sharma (@abhisheksharma2411) | 4 | -| 21 | Abhishek Divekar (@adivekar-utexas) | 4 | -| 22 | Andrew B. (@AndrianBalanescu) | 4 | -| 23 | NoxzRCW | 4 | -| 24 | opensource-elearning | 4 | -| 25 | Alvin T. Veroy (@alvinveroy) | 3 | +| 🥇 | diegosouzapw | 323 | +| 🥈 | Bob.Hou (@HouMinXi) | 79 | +| 🥉 | Paco Cartones (@pacocartones) | 77 | +| 4 | Dizzle (@maxmad64bis) | 49 | +| 5 | Koosha Paridehpour (@KooshaPari) | 43 | +| 6 | Nguyen Thanh Dat (@ntdatt812) | 31 | +| 7 | Ravi Tharuma (@RaviTharuma) | 26 | +| 8 | Markus Hartung (@hartmark) | 22 | +| 9 | Webman (@jonlwheat2-gif) | 22 | +| 10 | backryun | 17 | +| 11 | anhtahaylove | 15 | +| 12 | Rafa Martins (@rafacpti23) | 10 | +| 13 | Syed Raheemuddin (@raheemuddin786) | 10 | +| 14 | Nguyễn Viết Tuấn (@TheDemonTuan) | 8 | +| 15 | MumuTW | 7 | +| 16 | Paijo (@oyi77) | 7 | +| 17 | Tobias Andersen (@turbolego) | 6 | +| 18 | Bl0ck (@Bl0ck154) | 5 | +| 19 | SHANMUGAPRIYAN (@geek007git) | 5 | +| 20 | KaspaPulse | 5 | +| 21 | Abhishek Sharma (@abhisheksharma2411) | 4 | +| 22 | Abhishek Divekar (@adivekar-utexas) | 4 | +| 23 | Andrew B. (@AndrianBalanescu) | 4 | +| 24 | NoxzRCW | 4 | +| 25 | opensource-elearning | 4 | ### ✨ New Features @@ -282,6 +282,19 @@ _By commits in `release/v3.8.50..release/v3.8.51` (`091589089c..d6f315018a`), au - **feat(dashboard):** link a log entry's Conversation Context to its owning conversation ([#12646](https://github.com/diegosouzapw/OmniRoute/pull/12646)) — thanks @hartmark - **feat(catalog):** eligibility-gated free-tier bucket ([#12669](https://github.com/diegosouzapw/OmniRoute/pull/12669)) - **feat(db):** fail-closed previous_response_id continuation for redacted video turns (#12150 P2b) ([#12707](https://github.com/diegosouzapw/OmniRoute/pull/12707)) +- **feat(gamification): pay the documented `streak_bonus` and `badge_unlock` XP rewards.** `XP_REWARDS` listed both rewards but the award pipeline never paid them: the private reward table in `events.ts` omitted them, `updateStreak()` did not report when a streak extended, and badge unlocks carried no XP. Every request that extends a daily streak now pays `streak_bonus × streak length` once per UTC day (guarded by a same-day `xp_audit_log` check), and every badge unlocked through the pipeline pays `badge_unlock` once per badge (guarded by the `user_badges` primary key; `unlockBadge()` now reports whether it inserted). Bonus XP flows through the same `addXp` + level sync + global/weekly/monthly leaderboard path as action XP, so level-ups and rankings include it. The Radar supporter recognition unlock stays XP-free. (#12522 — thanks @pacocartones) +- **feat(providers):** advertise a `free-tier` capability in the provider plugin manifest for every provider with documented free models, so sidecars and dashboards can filter free-capable providers without reading the quota catalog ([#12786](https://github.com/diegosouzapw/OmniRoute/pull/12786)) — thanks @maxmad64bis +- **feat(providers):** Added EURouter as an OpenAI-compatible API-key gateway (`https://api.eurouter.ai/v1`), with live model discovery via `passthroughModels`. Its copy states that models are served by third-party upstreams listed per model, so an EU-based router is not read as EU data residency for inference. ([#13025](https://github.com/diegosouzapw/OmniRoute/pull/13025)) — thanks @ntdatt812 +- **feat(providers):** Added GreenPT as an OpenAI-compatible API-key provider (`https://api.greenpt.ai/v1`), with live model discovery via `passthroughModels`. No free-inference badge: the published docs describe a free API subscription billed per token, not a free tier. ([#13024](https://github.com/diegosouzapw/OmniRoute/pull/13024)) — thanks @ntdatt812 +- **feat(i18n):** 9 new locales — Greek (`el`), Croatian (`hr`), Serbian (`sr`), Lithuanian (`lt`), Estonian (`et`), Latvian (`lv`), Slovenian (`sl`), Maltese (`mt`), Irish (`ga`) — across the dashboard, docs mirrors, CLI, README and the site; OmniRoute now ships every official EU language (51 locales). (#13044) +- **feat(i18n):** 8 new locales — Kannada (`kn`), Malayalam (`ml`), Odia (`or`), Punjabi (`pa`), Nepali (`ne`), Sinhala (`si`), Burmese (`my`), Khmer (`km`) — across the dashboard, docs mirrors, CLI, README and the site (59 locales). The translator now restores the ICU literal escape around angle placeholders and splits oversized docs sections before translating. (#0000) ([#13660](https://github.com/diegosouzapw/OmniRoute/pull/13660)) +- feat(providers): **list Agnes 3.0 Flash as the current free chat model, drop retired 1.5 Flash, add Image 2.0/2.5 Flash plus Video 2.5/2.5 Flash, and discover the live `/v1/models` catalog (including the CN host `api.agnes-ai.cn`).** `agnes-1.5-flash` now forwards to `agnes-3.0-flash`. Video 2.5 polls `GET /v1/videos/{id}` (not the V2.0 `/agnesapi` contract). Live `/v1/models` (2026-09-09) no longer serves 1.5; the wiki marks it deprecated. 3.0 Flash is 512K context / 65,536 max output, same window as 2.5. CN-region keys use the existing per-connection base-URL field, default stays `apihub.agnes-ai.com`. ([#13120](https://github.com/diegosouzapw/OmniRoute/pull/13120)) — thanks @HouMinXi +- **feat(sse):** Codex and OpenAI catalogs list GPT-6 Astra with effort aliases (`-low` through `-ultra`); Codex CLI identity pins `@openai/codex@0.153.4` in lockstep with the image ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **feat(dashboard):** the orchestration History tab gained a "Compare runs" mode — toggling it turns each grid cell into a 2-item selection queue (a 3rd click drops the oldest pick), and picking two cells opens a side-by-side comparison panel instead of the usual detail drawer. The panel fetches both runs' detail the same way the drawer does (falling back to persisted history once a run leaves the live TTL window) and shows, per side: identity/source/state, start time, a signed `right - left` delta for duration/cost/event count, the event timeline aligned by index, and any memory hits. One side's fetch failing never blocks the other, and a delta is only ever computed when both sides have a finite value — otherwise it renders "—", never `NaN`. Comparing two runs from different sources or skills still works; a banner marks the deltas as informational rather than hiding them, since the two runs aren't a strict apples-to-apples pair. ([#12677](https://github.com/diegosouzapw/OmniRoute/pull/12677)) +- **feat(dashboard):** badge a conversation that never reached a clean stop ([#12717](https://github.com/diegosouzapw/OmniRoute/pull/12717)) — thanks @hartmark +- **feat(release):** reconcile-changelog tool + version-anchored fragment aggregation ([#12987](https://github.com/diegosouzapw/OmniRoute/pull/12987)) +- **feat(mcp):** carry context_length through MCP list_models_catalog projection (#12776) ([#13406](https://github.com/diegosouzapw/OmniRoute/pull/13406)) — thanks @KooshaPari +- **feat(cli):** make startup readiness budget configurable (#13369) ([#13433](https://github.com/diegosouzapw/OmniRoute/pull/13433)) — thanks @KooshaPari ### 🐛 Bug Fixes @@ -698,6 +711,194 @@ _By commits in `release/v3.8.50..release/v3.8.51` (`091589089c..d6f315018a`), au - **fix(i18n):** quote placeholder in OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES description (#12505) ([#12769](https://github.com/diegosouzapw/OmniRoute/pull/12769)) — thanks @KooshaPari - **fix(api/pricing):** surface validation error message as string, not raw object (#12494) ([#12771](https://github.com/diegosouzapw/OmniRoute/pull/12771)) — thanks @KooshaPari - **fix(sse):** re-enable prompt compression for native Codex passthrough ([#12834](https://github.com/diegosouzapw/OmniRoute/pull/12834)) — thanks @thomasmaerz +- **fix(dashboard):** model health tests for a provider node set to the Responses API now call `/v1/responses` with a Responses-shaped body instead of `/v1/chat/completions` — those models were reported as `Provider returned HTTP 200 but no text content` even though the same model answered normally through `/v1/responses` ([#13070](https://github.com/diegosouzapw/OmniRoute/issues/13070)) ([#13087](https://github.com/diegosouzapw/OmniRoute/pull/13087)) — thanks @ntdatt812 +- fix(routing): stop a round-robin combo's "opencode" targets from collapsing onto the opencode-zen connection (#11912) ([#13283](https://github.com/diegosouzapw/OmniRoute/pull/13283)) +- fix(routing): stop the reactive-compaction debug log from lying when compression is globally disabled (#11977) ([#13276](https://github.com/diegosouzapw/OmniRoute/pull/13276)) +- fix(electron): relativize standalone-bundle symlink targets so Stage 8 manifest verification stops failing on Windows (#11979) ([#13251](https://github.com/diegosouzapw/OmniRoute/pull/13251)) +- fix(dashboard): surface a visible error when Compression Studio's combined preview run fails (#12061) ([#13288](https://github.com/diegosouzapw/OmniRoute/pull/13288)) +- fix(dashboard): make the compression "Effective pipeline" preview honor the active profile and warn when the master switch is off (#12063) ([#13287](https://github.com/diegosouzapw/OmniRoute/pull/13287)) +- fix(providers): scope TinyCMS Web signer's DOM shims to each call instead of leaking them for the process lifetime, and surface a clean HTTP status on a non-JSON interception-toggles error (#12072) ([#13254](https://github.com/diegosouzapw/OmniRoute/pull/13254)) +- fix(guardrails): stop Vision Bridge from re-selecting a model locked after a 404 (#12111) ([#13259](https://github.com/diegosouzapw/OmniRoute/pull/13259)) +- fix(sse): require Responses-shaped body before native OpenAI-compatible passthrough (#12129) ([#13278](https://github.com/diegosouzapw/OmniRoute/pull/13278)) +- fix(providers): minimax-m3 now collapses manual thinking.type:"enabled" to adaptive, preventing upstream 400 (2013) (#12132) ([#13249](https://github.com/diegosouzapw/OmniRoute/pull/13249)) +- fix(db): scope model visibility overrides by modality so hiding a Chat model no longer hides an identically-ID'd Image/Embeddings/etc. model (#12172) ([#13258](https://github.com/diegosouzapw/OmniRoute/pull/13258)) +- fix(db): scope local-provider apiKey dedup to matching base URL so LM Studio/Ollama-style connections support multiple accounts (#12173) ([#13250](https://github.com/diegosouzapw/OmniRoute/pull/13250)) +- fix(providers): refresh Trae's stale Referer/Origin and forward user timezone so imported connections stop failing with 401 (#12190) ([#13255](https://github.com/diegosouzapw/OmniRoute/pull/13255)) +- fix(providers): route opencode-go/gpt-5.6-luna to /responses instead of /chat/completions (#12196) ([#13275](https://github.com/diegosouzapw/OmniRoute/pull/13275)) +- fix(dashboard): allow deleting the last extra-upstream-header row even when invalid (#12251) ([#13252](https://github.com/diegosouzapw/OmniRoute/pull/13252)) +- **fix(i18n):** translate pre-existing `__MISSING__:` keys for `combo.sort`, `requestLogger.detail` expand/collapse, `common.profile`, and `settings.resilienceCredentialHealth*` across 39 locales ([#12272](https://github.com/diegosouzapw/OmniRoute/issues/12272)) ([#13034](https://github.com/diegosouzapw/OmniRoute/pull/13034)) — thanks @HouMinXi +- fix(cli): run the Node.js runtime compatibility guard before the heavy `tsx/esm` + Commander import chain so an unsupported runtime gets a clear message instead of a raw `Invalid regular expression flags` crash (#12296) ([#13253](https://github.com/diegosouzapw/OmniRoute/pull/13253)) +- fix(dashboard): refresh the providers list after deleting a compatible provider node (#12298) ([#13256](https://github.com/diegosouzapw/OmniRoute/pull/13256)) +- fix(usage): fail closed on API-key budget enforcement when a provider's `auto` routing alias has no pricing row, instead of silently counting it as $0 (#12341) ([#13257](https://github.com/diegosouzapw/OmniRoute/pull/13257)) +- **fix(providers):** include the submitted Agnes video model when polling by `video_id` ([#12356](https://github.com/diegosouzapw/OmniRoute/pull/12356)) — thanks @pacocartones +- **fix(routing):** custom OpenAI-compatible nodes now honor the saved Chat/Responses API type after edits instead of letting the node's original ID prefix override the live connection setting ([#11884](https://github.com/diegosouzapw/OmniRoute/issues/11884)). ([#12358](https://github.com/diegosouzapw/OmniRoute/pull/12358)) — thanks @pacocartones +- fix(sse): surface an error instead of a silent empty 200 when a Claude stream closes with zero bytes (#12398) ([#13285](https://github.com/diegosouzapw/OmniRoute/pull/13285)) +- fix(oauth): warn before the dead localhost:8080 redirect in antigravity/gemini `oauth start` (#12413) ([#13265](https://github.com/diegosouzapw/OmniRoute/pull/13265)) +- fix(providers): stop devin-cli spawn error from double-closing the SSE controller (#12517) ([#13261](https://github.com/diegosouzapw/OmniRoute/pull/13261)) +- **fix(rerank):** clamp Voyage `top_k` to the documents actually sent after empty-string filtering, and honor `return_documents: false` in the NVIDIA response adapter (#12523 — thanks @pacocartones) +- **fix(audio):** `/v1/audio/translations` now resolves combo names the way `/v1/audio/transcriptions` already does, so a combo that `GET /v1/models` advertises is fanned out to its targets instead of being rejected with `400 Invalid translation model: . Use format: provider/model`; literal `provider/model` ids and unknown bare names behave as before (#12536 — thanks @pacocartones) +- **fix(gemini):** strip the JSON-Schema-2020-12 `prefixItems` keyword from Gemini tool schemas at every nesting level, so Claude Code tool definitions no longer fail with `400 Unknown name "prefixItems"` on Gemini models (#12540 — thanks @pacocartones) +- **fix(api-manager):** Expose an accessible loading status while API keys are fetched instead of an empty accessibility tree (#12541 — thanks @pacocartones) +- **fix(video):** Clamp `estimateJpegFrameBytes` at zero for padding-only payloads and build the three encode-side frame data URIs from `JPEG_FRAME_DATA_URI_PREFIX` instead of a repeated literal (#12543 — thanks @pacocartones) +- **fix(devin):** accept Windows `DEVIN_AGENTIC_HOME` sandbox paths (`C:\...\.sandbox\...`) in the isolated-home check so the Devin Claude Bridge no longer fails closed on Windows ([#12405](https://github.com/diegosouzapw/OmniRoute/issues/12405)) (#12545 — thanks @pacocartones) +- **fix(db):** Add `conversation_turn_nodes` and orphaned `agentic_conversations` to the auto-cleanup cycle under the existing `retention.callLogs` window, so identity nodes whose call-log content has already been purged no longer accumulate without bound in `storage.sqlite` (#12548 — thanks @pacocartones) +- **fix(i18n):** Wrap the `~/.claude/profiles//settings.json` placeholder in ICU single quotes in the `OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES` feature-flag description across all 42 locales and the TypeScript default, so next-intl no longer fails with `INVALID_MESSAGE: UNCLOSED_TAG` and the Feature Flags card shows the description instead of the raw key (#12549 — thanks @pacocartones) +- **fix(orchestration):** `updateCloudAgentTask` now publishes the task's real `status` on `agent.task.updated` when an update only touches `result`, `activities` or `error`, instead of the fabricated `"updated"` state, and stays silent when no row matched the id (#12550 — thanks @pacocartones) +- **fix(i18n):** the home "Recent Requests" panel and the Provider Topology legend are now translated instead of rendering English copies on non-English dashboards; the legend reads its own `home.topologyLegend*` labels with consistent casing rather than borrowing the memory-settings "Recent" and analytics "Error" strings (#12551 — thanks @pacocartones). +- **docs(reference):** bring the `FEATURE_FLAGS.md` catalog back to 1:1 with `featureFlagDefinitions.ts` — 20 missing flags added, the two `*_BLOCK_THRESHOLD` env-only knobs moved out of the flag tables, category/total counts and the Live WS port corrected, guarded by a static test (#12552 — thanks @pacocartones) +- **fix(i18n):** backfill missing `usage.kiloPass*` strings in 39 locales and restore `featureFlagOmnirouteDisableThinkingLevelVariantsDescription` in `pt.json` ([#12561](https://github.com/diegosouzapw/OmniRoute/issues/12561)) ([#13027](https://github.com/diegosouzapw/OmniRoute/pull/13027)) — thanks @HouMinXi +- fix(docker): default docker-compose app ports (dashboard/API/live-WS) to loopback instead of `0.0.0.0`, closing the anonymous `/v1` LAN/WAN exposure gap left open by `REQUIRE_API_KEY=false` (#12568) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): close DNS-rebinding SSRF gap in webhook outbound-URL guard (#12569) ([#13243](https://github.com/diegosouzapw/OmniRoute/pull/13243)) +- fix(docker): require a per-session token on the VNC browser CDP bridge and isolate it on a dedicated Docker network (#12571) ([#13241](https://github.com/diegosouzapw/OmniRoute/pull/13241)) +- fix(open-sse): write Adobe Firefly session tokens and cookie jars with 0700/0600 permissions instead of the process umask (#12572) ([#13242](https://github.com/diegosouzapw/OmniRoute/pull/13242)) +- fix(api): remove hardcoded wildcard CORS in openai-to-gemini-sse.ts so the centralized fail-closed CORS gate is the sole source of `Access-Control-Allow-Origin` (#12573) ([#13239](https://github.com/diegosouzapw/OmniRoute/pull/13239)) +- fix(api): enforce API key policy (budget/rate-limit/schedule/endpoint scoping) on the ElevenLabs speech-to-text, text-to-speech and voices proxy routes (#12574) ([#13244](https://github.com/diegosouzapw/OmniRoute/pull/13244)) +- fix(sse): cap HuggingChat NDJSON body size and bound the read loop with the fetch timeout so a stalled or hostile upstream cannot buffer unbounded memory (#12577) ([#13282](https://github.com/diegosouzapw/OmniRoute/pull/13282)) +- fix(docker): scope the cliproxyapi/qdrant/bifrost sidecars to loopback by default and forward `CLIPROXYAPI_MANAGEMENT_KEY` into the cliproxyapi container so its management API is not left both unauthenticated and LAN-published (#12578) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): create DB export temp paths with `fs.mkdtempSync` instead of predictable timestamps (#12579) ([#13237](https://github.com/diegosouzapw/OmniRoute/pull/13237)) +- Cline 401 bodies that say "re-authenticate your Cline account" classify as a refreshable OAuth token, not a terminal expired key. The cooling panel no longer labels every cooldown as a 429; it shows the recorded last error instead. (#12594) ([#13060](https://github.com/diegosouzapw/OmniRoute/pull/13060)) — thanks @HouMinXi +- **fix(catalog):** degrade unknown combo targets instead of dropping LCD modalities, and persist OpenRouter `architecture.input_modalities` into the capability snapshot ([#12613](https://github.com/diegosouzapw/OmniRoute/issues/12613)) ([#13042](https://github.com/diegosouzapw/OmniRoute/pull/13042)) — thanks @HouMinXi +- fix(providers): send `x-api-key` instead of `Authorization: Bearer` for OpenCode Zen's `/v1/responses` endpoint (Muse Spark Contributor models), fixing a 401 on OmniRoute's auth header (#12633) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(sse): surface the actionable "Auggie CLI not found" message when the shell reports a missing `auggie` binary via exit code instead of a spawn error (#12645) ([#13263](https://github.com/diegosouzapw/OmniRoute/pull/13263)) +- **fix(config):** Persist deletions of built-in background-degradation entries — when a stored settings record exists its `degradationMap` is now authoritative instead of being merged under the defaults, so an entry the user removed in the dashboard no longer reappears on the next apply or restart ([#12424](https://github.com/diegosouzapw/OmniRoute/issues/12424)) ([#12647](https://github.com/diegosouzapw/OmniRoute/pull/12647)) — thanks @pacocartones +- **fix(gamification):** action-count badge milestones (First Token, Token Consumer, Token Machine, Token Whale, and the token-sharing tier) are now backed by a durable `xp_action_counts` counter incremented in `addXp()`, instead of a live `COUNT(*)` over `xp_audit_log`. The audit log is pruned by `retention.xpAuditLog` (default 30 days), so on a default install those "lifetime" milestones were really "actions in the last 30 days" and unlocked badges could stop unlocking once old rows aged out. `getActionCount()` and `checkActionCountBadges()` now read the same durable source, and a migration backfills existing totals from the surviving audit rows ([#12546](https://github.com/diegosouzapw/OmniRoute/issues/12546)) ([#12651](https://github.com/diegosouzapw/OmniRoute/pull/12651)) — thanks @pacocartones +- **fix(images):** `/v1/images/edits` now iterates a combo's targets the same way `/v1/images/generations` does (#9239) instead of flattening a bare combo to its first target. A combo whose first target is not edit-capable — or lacks credentials — now falls through to a later edit-capable target rather than hard-erroring, and missing credentials are skipped (not a hard `401`) to match the generations path. The per-target skip/terminal classification is extracted into a shared `runImageComboTargets` loop, so generations behavior is unchanged ([#12547](https://github.com/diegosouzapw/OmniRoute/issues/12547)). ([#12653](https://github.com/diegosouzapw/OmniRoute/pull/12653)) — thanks @pacocartones +- fix(sse): add first-byte watchdog to the TLS-fingerprint transport so a stalled wreq body falls back instead of hanging for minutes (#12656) ([#13272](https://github.com/diegosouzapw/OmniRoute/pull/13272)) +- fix(sse): exempt tiny-budget reasoning probes from combo quality failure and surface persisted-cooldown skips in ALL_TARGETS_SKIPPED diagnostics (#12659) ([#13269](https://github.com/diegosouzapw/OmniRoute/pull/13269)) +- fix(models): declare the real ~1M contextLength for OpenCode Zen's Muse Spark 1.2 models instead of falling back to the 200000 provider default (#12681) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(oauth): align codebuddy-cn OAuth User-Agent with the chat/usage CLI version to avoid WAF false positives (#12702) ([#13264](https://github.com/diegosouzapw/OmniRoute/pull/13264)) +- fix(dashboard): surface an authentication-required banner instead of silently blanking database settings for a guest session (#12709) ([#13270](https://github.com/diegosouzapw/OmniRoute/pull/13270)) +- **fix(resilience):** stop unbounded queue that hangs 6min until Aborted — gate, provider slot, and Bottleneck queue now share a per-connection `maxWaitMs` budget; `fail-closed` on exhaust (503); execution backstop `executionMaxWaitMs` overridable per connection with upstream clamp ([#12715](https://github.com/diegosouzapw/OmniRoute/pull/12715)) — thanks @maxmad64bis (with thanks to @Tushar49 for surfacing the slow-provider need in #12635) +- **fix(auto-combo):** every mode pack now carries `quality` and `reliability` weights (`quality` 0.02, 0.03 in `quality-first`; `reliability` 0.03, 0.04 in `reliability-first`), so selecting a pack no longer silences either signal; `DEFAULT_WEIGHTS` is unchanged ([#12731](https://github.com/diegosouzapw/OmniRoute/pull/12731)) — thanks @maxmad64bis +- **fix(sse):** the provider execution pipeline keeps the upstream error `code`/`type` again — both were lost when the non-streaming leg was extracted, so a config-class `422` (Antigravity missing project) degraded into a generic account cooldown instead of reaching the gate that recognises it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(db):** a legacy `call_logs` table no longer aborts startup — the provider-stats index is created after the column healing runs, not before it, so an install predating `request_type` opens instead of failing with `no such column` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(catalog):** a malformed row in the operator-writable custom-models blob no longer kills every `auto/*` pool ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(tests):** realign the guards that the same day's merges left asserting the old behavior — the provider-count total after GreenPT and EURouter, the GLM stream buffer slot that `#12925` turned into a declared parameter, and the injection scan bound that `#13104` reshaped from a head-only window into head-plus-tail ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- fix(cache): fold tool_choice/tools/response_format into the semantic cache signature so a cached tool_calls response can no longer be replayed for a request whose tool policy forbids it (#12734) ([#13267](https://github.com/diegosouzapw/OmniRoute/pull/13267)) +- **fix(models):** stop treating provider-supplied `isFree:true`/`:free`/`0/0` flags as trusted unless the provider has a documented free tier — only the shipped free-tier catalog decides otherwise; fetched `isFree:true` trusted only for a free-tier provider, custom `isFree:true` via a trusted path ([#12744](https://github.com/diegosouzapw/OmniRoute/pull/12744)) — thanks @maxmad64bis +- fix(memory): authenticate the internal /v1/rerank loopback call so memory reranking no longer silently degrades to unranked order when REQUIRE_API_KEY=true (#12745) ([#13268](https://github.com/diegosouzapw/OmniRoute/pull/13268)) +- fix(sse): parse Ollama Cloud's current usage markup (`$X of $Y used` aria-label, nested width style) (#12749) ([#13260](https://github.com/diegosouzapw/OmniRoute/pull/13260)) +- fix(cli): setup-opencode no longer sends an active context's management token to `/v1/models` when `--api-key`/`OMNIROUTE_API_KEY` is supplied — an explicit flag or the env var now always outranks the context's token, and the flag itself is no longer swallowed by the parent program's global `--api-key` option (#12783) ([#13246](https://github.com/diegosouzapw/OmniRoute/pull/13246)) +- fix(sse): register Arcee AI in the executor provider registry so requests reach api.arcee.ai instead of silently falling back to OpenAI (#12784) ([#13277](https://github.com/diegosouzapw/OmniRoute/pull/13277)) +- **fix(quota):** share one upstream quota read when identical saturation checks arrive at the same time, so traffic bursts don't multiply provider API calls ([#12787](https://github.com/diegosouzapw/OmniRoute/pull/12787)) — thanks @maxmad64bis +- **fix(db):** ignore expired or invalid rate-limit cooldown writes so a stale timestamp can't lock a connection that should be usable — clearing still works as before ([#12788](https://github.com/diegosouzapw/OmniRoute/pull/12788)) — thanks @maxmad64bis +- **fix(providers):** single source for provider order with `xao` ranking alongside `xai-oauth` ([#12790](https://github.com/diegosouzapw/OmniRoute/pull/12790)) — thanks @maxmad64bis +- **fix(routing):** off-table models check the free-model catalog before inheriting premium prices, latency bootstraps from the observed pool median, async tiers read live database pricing with a 90-day freshness gate, and the tier cache invalidates on every pricing write ([#12792](https://github.com/diegosouzapw/OmniRoute/pull/12792)) — thanks @maxmad64bis +- **fix(auto-combo):** snapshot scoring uses the observed per-connection account tier and per-model quality instead of neutral constants, and circuit-open providers rank lower at build time ([#12794](https://github.com/diegosouzapw/OmniRoute/pull/12794)) — thanks @maxmad64bis +- **fix(pool):** empty `auto/*` pools now say why they are empty, and the models listing uses the same paid check as routing ([#12795](https://github.com/diegosouzapw/OmniRoute/pull/12795)) — thanks @maxmad64bis +- fix(routing): recognize CLIProxyAPI's 'unknown provider for model' 400 as fallback-worthy (#12800) ([#13284](https://github.com/diegosouzapw/OmniRoute/pull/13284)) +- **fix(sse):** translate-mode streams now emit the estimated token counts as a trailing usage-only chunk before `[DONE]` when the upstream stays silent, so metered chat clients see totals instead of nothing ([#12828](https://github.com/diegosouzapw/OmniRoute/pull/12828)) — thanks @maxmad64bis +- **fix(db):** provider stats stay truthful on empty and legacy databases: fallback counts default to `0` instead of `null`, latency averages read `null` (not `0`) when no durations were recorded, failures logged before the error-type column existed group under `pre_migration` instead of `unclassified`, and per-provider queries use two new composite indexes ([#12832](https://github.com/diegosouzapw/OmniRoute/pull/12832)) — thanks @maxmad64bis +- fix(nvidia): fail open when a synced model catalog goes stale instead of gating forever (#12849) ([#13248](https://github.com/diegosouzapw/OmniRoute/pull/13248)) +- **fix(db):** WAL maintenance lives in its own module: a `TRUNCATE` checkpoint that hits a busy database now warns with its streak and retries once via `PASSIVE` instead of logging success, and closing-time checkpoints no longer report success on builds without a database file, with the busy totals visible in the authenticated monitoring health payload ([#12853](https://github.com/diegosouzapw/OmniRoute/pull/12853)) — thanks @maxmad64bis +- **fix(health):** quota with no snapshots reads empty instead of a contradicting 0% ([#12857](https://github.com/diegosouzapw/OmniRoute/pull/12857)) — thanks @maxmad64bis +- fix(a2a): accept the dashboard's own session cookie on /a2a so "Run message/send" no longer fails with "Unauthorized: missing or invalid API key" (#12888) ([#13271](https://github.com/diegosouzapw/OmniRoute/pull/13271)) +- **fix(logs):** the Logs grid's in-memory filter pass no longer discards rows the SQL query already matched — selecting an API key from the dropdown (which sends the key's id) returns its calls again, the Combo tab shows every combo instead of only those whose name contains a "1", and the model filter and search cover the same columns as the query ([#12896](https://github.com/diegosouzapw/OmniRoute/pull/12896)) — fixes [#12873](https://github.com/diegosouzapw/OmniRoute/issues/12873) — thanks @ntdatt812 +- **fix(a2a):** `/api/a2a/status` now builds the agent card from the request that asked for it, so a gateway reached at a non-localhost host no longer advertises `http://localhost:20128` as its A2A URL ([#12918](https://github.com/diegosouzapw/OmniRoute/pull/12918)). — thanks @ntdatt812 +- **fix(compression):** progressive aging now appends its `[COMPRESSED:aging:…]` annotation after a turn's `tool_result` blocks instead of in front of them, so Anthropic no longer rejects aged conversations with "`tool_use` ids were found without `tool_result` blocks immediately after" ([#12920](https://github.com/diegosouzapw/OmniRoute/pull/12920)). — thanks @ntdatt812 +- **fix(bedrock):** model import now resolves context limits for every vendor prefix instead of only `anthropic.*`, so `global.openai.gpt-5.6-*` no longer imports with a null `inputTokenLimit` and gets rejected pre-flight at the 200k default ([#12921](https://github.com/diegosouzapw/OmniRoute/pull/12921)). — thanks @ntdatt812 +- **fix(stream):** the 64 KB stream buffer GLM asks for is honoured instead of dropped, and the type error it caused no longer fails the API Route Typecheck gate on every open PR ([#12925](https://github.com/diegosouzapw/OmniRoute/pull/12925)) — thanks @ntdatt812 +- **fix(guardrails):** mask PII inside a `tool_result`'s nested content array, which the masker walked past while redacting its sibling block ([#12930](https://github.com/diegosouzapw/OmniRoute/pull/12930)) — thanks @ntdatt812 +- **fix(dashboard):** the Radar catalog table no longer leaves absent data unexplained — empty limits, unknown context windows, and unreported capabilities each explain themselves on hover, and a new check keeps it that way ([#12937](https://github.com/diegosouzapw/OmniRoute/pull/12937)) — thanks @maxmad64bis +- **fix(sse):** geo-blocked opencode requests rotate to the next account proxy instead of failing, so one refused egress no longer aborts the whole chain ([#12941](https://github.com/diegosouzapw/OmniRoute/pull/12941)) — thanks @maxmad64bis +- **fix(codex):** dashboard "clear cooldown" and the CAS recovery path now drop nested `codexScopeRateLimitedUntil` maps in the same write that nulls `rate_limited_until`, and fresh quota snapshots with headroom lift fallback-sourced scope cooldowns parked by quota preflight ([#12817](https://github.com/diegosouzapw/OmniRoute/issues/12817), [#12860](https://github.com/diegosouzapw/OmniRoute/issues/12860), [#12951](https://github.com/diegosouzapw/OmniRoute/pull/12951)) The dashboard reset-credit button (`consumeCodexResetCredit`) now exercises that same snapshot path after a successful redeem, so a filled quota bar is enough to unpark a leftover fallback Codex child without another manual clear. — thanks @HouMinXi +- **fix(db):** resolve `sql-wasm.wasm` across global npm install and hoisted layouts, ensuring OmniRoute can boot cleanly on Node 24 when native `better-sqlite3` is uncompiled. ([#13035](https://github.com/diegosouzapw/OmniRoute/pull/13035)) — thanks @HouMinXi +- **fix(api):** `DELETE /v1/batches/delete-completed` now sweeps only the calling API key's own completed batches (batches with no owner stay out of a key-scoped sweep on purpose), with an explicit instance-wide mode reserved for authenticated dashboard sessions, a 401 for a presented key that is unknown, revoked, deactivated, banned or expired (never falling through to the session branch), audit logging of every sweep, a sanitized 500 on failure, an owner-scoped file half (a key-scoped sweep never nulls a file another tenant owns) and an atomic sweep — chunked in 200-batch transactions in instance mode — so a mid-way error never leaves a batch pointing at a nulled file (GHSA-wvxc-jp3v-5mg5) ([#12969](https://github.com/diegosouzapw/OmniRoute/pull/12969)) +- **fix(combo):** quota-weighted routing stops drawing on an out-of-credit connection — a 402 now invalidates the stored quota snapshot instead of leaving its stale remaining percentage in place, and a snapshot older than 10 minutes no longer counts as confident headroom for the primary pool ([#12972](https://github.com/diegosouzapw/OmniRoute/pull/12972)) ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- **fix(vision):** Custom Models with "Vision capable" checked no longer have image requests swapped to `glm/glm-4.6v` when the client sends the advertised alias (`vllm/path/...`) or the bare path-shaped id — Vision Bridge now matches the stored override for all three id forms ([#12758](https://github.com/diegosouzapw/OmniRoute/issues/12758)) ([#12974](https://github.com/diegosouzapw/OmniRoute/pull/12974)) — thanks @HouMinXi +- **fix(sse):** transient opencode upstream failures rotate to the next account proxy instead of failing, so one flapping egress no longer aborts the whole chain ([#12975](https://github.com/diegosouzapw/OmniRoute/pull/12975)) — thanks @maxmad64bis +- **fix(azure):** Deployments from GPT-6 onward now send `max_completion_tokens` instead of `max_tokens`, which Azure rejects with HTTP 400. The rule matched a literal `gpt-5`, so each new generation arrived broken; it now matches the generation range, while `gpt-35-turbo` still keeps `max_tokens`. ([#13007](https://github.com/diegosouzapw/OmniRoute/pull/13007)) — thanks @ntdatt812 +- **fix(db):** add an opt-in self-restart circuit for sustained critical memory pressure, gate post-cleanup VACUUM behind a minimum freed-rows threshold, and checkpoint the SQLite WAL every 5 minutes with a size guard that escalates to TRUNCATE, so a growing WAL can no longer stall the event loop into a full outage. ([#13011](https://github.com/diegosouzapw/OmniRoute/pull/13011)) — thanks @HouMinXi +- **fix(auth):** an explicit connection pin may probe a quota-disabled row once and re-enable it on success ([#12874](https://github.com/diegosouzapw/OmniRoute/issues/12874)) ([#13017](https://github.com/diegosouzapw/OmniRoute/pull/13017)) — thanks @HouMinXi +- **fix(api):** thread `X-OmniRoute-Fallback-Attempts` through combo chat completions so streaming and non-streaming responses report how many prior legs were attempted ([#13038](https://github.com/diegosouzapw/OmniRoute/pull/13038)) — thanks @HouMinXi +- Restore provider failure classification and credential refresh on non-streaming requests: classify non-2xx failures to lock models on per-model quota exhaustion, update connection rate limits from headers and body, and pass credential refresh handlers to pipeline execution so 401 tokens can be refreshed and retried (#13043). ([#13069](https://github.com/diegosouzapw/OmniRoute/pull/13069)) — thanks @HouMinXi +- **fix(responses):** wrap forced-non-streaming web_search fallback JSON as Responses SSE so Codex still sees `response.completed` ([#13050](https://github.com/diegosouzapw/OmniRoute/pull/13050)) — thanks @HouMinXi +- **fix(antigravity):** Preserve upstream thought-token usage in normalized completion and reasoning token counts ([#13055](https://github.com/diegosouzapw/OmniRoute/pull/13055)) — thanks @pacocartones +- Fix a concurrency-slot leak in the DeepSeek PoW solver: a worker that failed to spawn (for example a missing worker script) never released its slot, so `MAX_CONCURRENT_WORKERS` failures disabled the solver until restart. ([#13097](https://github.com/diegosouzapw/OmniRoute/pull/13097)) — thanks @anhtahaylove +- **fix(acp):** bound the ACP session output buffers — `stdoutBuffer` and `stderrBuffer` now cap at 1 MiB keeping the most recent output behind a visible `[...output truncated...]` marker, and `stderrBuffer` is reset per prompt instead of accumulating for the lifetime of the session. ([#13100](https://github.com/diegosouzapw/OmniRoute/pull/13100)) — thanks @anhtahaylove +- **fix(acp):** release the `stdout`/`exit` listeners and the idle timer that a `sendPrompt` timeout used to leave attached to the `acpManager` singleton, and drop sessions that exited on their own from the session map instead of keeping them forever. ([#13096](https://github.com/diegosouzapw/OmniRoute/pull/13096)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection and PII scanners now read the text a `tool_result` block carries on `content` (string or nested block list), in messages and in system blocks, so tool output is judged by the same rules as user text ([#13101](https://github.com/diegosouzapw/OmniRoute/pull/13101)) — thanks @ntdatt812 +- **fix(gamification):** close the badge notification SSE stream when the request signal is already aborted before the stream starts — a client that disconnects while the route is still awaiting auth used to leave both the 2s unlock poll and the 15s heartbeat running for the lifetime of the process. ([#13106](https://github.com/diegosouzapw/OmniRoute/pull/13106)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection scan now spends its 16 KB budget on both ends of the request instead of the first 16 KB only, so `system`, `instructions`, `query`, `documents` and the newest turns are no longer hidden behind one long message ([#13104](https://github.com/diegosouzapw/OmniRoute/pull/13104)) — thanks @ntdatt812 +- **fix(dashboard):** expose the Volcano Ark console cookie on quota scraping and unwrap connect-error objects so the dashboard shows the upstream message ([#13107](https://github.com/diegosouzapw/OmniRoute/pull/13107)) — thanks @HouMinXi +- **fix(db):** release the `beforeExit`/`SIGINT`/`SIGTERM` handlers when a `node:sqlite` adapter closes, so a closed adapter and its database handle are no longer pinned to `process` for the lifetime of the run — the same treatment #7494 gave the sql.js adapter. ([#13109](https://github.com/diegosouzapw/OmniRoute/pull/13109)) — thanks @anhtahaylove +- **fix(translator):** `contentSchema` and `unevaluatedItems` are now treated as subschema positions by the tool-schema sanitizer, so a truncation placeholder in either is replaced with a permissive schema instead of being forwarded as a string ([#13110](https://github.com/diegosouzapw/OmniRoute/pull/13110)) — thanks @ntdatt812 +- **fix(cli-helper):** clear the `createLogStream` timeout on the abort path — `stop()` aborts the in-flight fetch and returned through the `signal.aborted` branch, which skipped `clearTimeout` and left an armed timer per stopped stream. The stream reader is now also cancelled when the read loop exits early. ([#13114](https://github.com/diegosouzapw/OmniRoute/pull/13114)) — thanks @anhtahaylove +- **fix(docs):** drop TLS-impersonation, MITM-decrypt, supply-chain attestation, and XOR-mask writeups from the public `/docs` catalog and Docker image. Files stay in git for engineers; operators who need them open the repo, not the website. ([#13136](https://github.com/diegosouzapw/OmniRoute/pull/13136)) — thanks @HouMinXi +- **fix(combo):** parse numeric-epoch `rate_limited_until` in the combo cooldown read path ([#13141](https://github.com/diegosouzapw/OmniRoute/pull/13141)) — thanks @maxmad64bis +- **fix(opencode):** both OpenCode plugins now reject a gateway address typed without `http://` at configuration time, instead of publishing every model with an api url no client can call, and the v2 plugin no longer publishes a model card whose api url is blank or relative ([#13142](https://github.com/diegosouzapw/OmniRoute/pull/13142)) — thanks @maxmad64bis +- **fix(providers):** lock opencode model on upstream 400 model-unavailable ([#13146](https://github.com/diegosouzapw/OmniRoute/pull/13146)) — thanks @maxmad64bis +- **fix(logging):** keep the provider exchange rather than the raw client bodies when a call log exceeds its size budget, and show that recovered payload in the request-detail panel instead of replacing it with the stored response body ([#13147](https://github.com/diegosouzapw/OmniRoute/pull/13147)) — thanks @maxmad64bis +- **fix(traffic-inspector):** the WebSocket route no longer leaks a traffic-buffer subscriber and a 30s ping timer when the client socket is already closed at handler time — listeners are attached before any resource is acquired, a destroyed socket bails out early, and the ping interval stops on a dead socket where `write()` never throws ([#13155](https://github.com/diegosouzapw/OmniRoute/pull/13155)) — thanks @anhtahaylove +- **fix(telegram):** bound the per-user API key cache in the Telegram chat proxy so a burst of distinct chat ids can no longer grow the process heap without limit ([#13165](https://github.com/diegosouzapw/OmniRoute/issues/13165)) ([#13166](https://github.com/diegosouzapw/OmniRoute/pull/13166)) — thanks @anhtahaylove +- **fix(stream):** cancel the upstream response body when the JSON-to-SSE sniff unwinds on a body timeout, so a stalled upstream no longer pins the connection ([#13169](https://github.com/diegosouzapw/OmniRoute/issues/13169)) ([#13171](https://github.com/diegosouzapw/OmniRoute/pull/13171)) — thanks @anhtahaylove +- **fix(telegram):** authenticate webhook deliveries with Telegram's `secret_token` so an unauthenticated caller can no longer mint API keys or spend upstream quota ([#13172](https://github.com/diegosouzapw/OmniRoute/issues/13172)) ([#13175](https://github.com/diegosouzapw/OmniRoute/pull/13175)) — thanks @anhtahaylove +- **fix(models):** give discoverable Gemini 3.8 Flash ids their own 65536 output spec so Antigravity no longer clamps them to 16384 ([#13195](https://github.com/diegosouzapw/OmniRoute/pull/13195)) — thanks @HouMinXi +- **fix(dashboard):** leftover catalog-removed provider rows (gemini-cli) can be listed and purged from the providers page ([#13067](https://github.com/diegosouzapw/OmniRoute/issues/13067)) ([#13197](https://github.com/diegosouzapw/OmniRoute/pull/13197)) — thanks @HouMinXi +- **fix(auth):** a dashboard session now requires the `authenticated: true` claim that login, OIDC and the session refresh already emit — a JWT merely signed with `JWT_SECRET` (for example the Cursor CLI passthrough token, which any API-key holder can obtain) no longer verifies as the `auth_token` cookie on any route, the WebSocket handshake or the live server; existing sessions keep working ([#13298](https://github.com/diegosouzapw/OmniRoute/issues/13298)) ([#13375](https://github.com/diegosouzapw/OmniRoute/pull/13375)) +- **fix(api):** `DELETE /v1/batches/delete-completed` now applies the caller's API-key policy (endpoint allowlist, schedule, usage cap, rate limit) like every other `/v1` route, commits the key-scoped sweep per 200-batch chunk instead of holding one write lock for the whole sweep, refuses to loop without progress, and rejects a scope that names both a key and `allTenants` ([#13684](https://github.com/diegosouzapw/OmniRoute/pull/13684)) +- Restore the API-route typecheck gate: the non-streaming leg result lost its discriminated-union narrowing after the server-owned tool loop reassignment, producing 13 new TS2339 diagnostics in `chatCore.ts`. ([#12963](https://github.com/diegosouzapw/OmniRoute/pull/12963)) +- Restore four non-streaming behaviours the server-owned tool loop refactor dropped: client aborts map to 499 with the fixed `Request aborted` message, an aborted request no longer logs a synthetic `clientResponse`, Claude prompt-cache telemetry is recorded again, and a body that cannot be canonicalized no longer throws when the tool loop is off. ([#12990](https://github.com/diegosouzapw/OmniRoute/pull/12990)) +- **fix(skills):** The CLI registry parser now reads positionals declared with `.addArgument()`, not only those written inline in `.command()`. `tunnel create [type]` was being published as `tunnel create`, so the agent-skills sync gate reported drift on every branch and regenerating would have deleted the argument. ([#13009](https://github.com/diegosouzapw/OmniRoute/pull/13009)) — thanks @ntdatt812 +- **fix(combos):** dashboard combo test uses a short prompt, serial probes, and a 60s timeout so reasoning models and rate-limited free pools do not fail the health check ([#13001](https://github.com/diegosouzapw/OmniRoute/pull/13001)) — thanks @HouMinXi +- fix(compression): terminate idle worker threads on eviction so long-running instances stop leaking OS threads and MessagePorts ([#13091](https://github.com/diegosouzapw/OmniRoute/pull/13091)) — thanks @anhtahaylove +- Redact Google API keys of any length in error bodies: the pattern required exactly 39 characters, so shorter or longer `AIza…` credentials passed through unredacted. ([#12964](https://github.com/diegosouzapw/OmniRoute/pull/12964)) +- **fix(grok-cli):** a 402 "Grok Build usage balance exhausted" parks that Grok login as out of credit (Grok Build CLI, grok.com cookie, and xAI OAuth share the weekly pool). Combo routing then tries the next login instead of locking the model for every account in the pool ([#13061](https://github.com/diegosouzapw/OmniRoute/pull/13061)) — thanks @HouMinXi +- fix(compression): spawn the LLMLingua worker with a file URL object so compression actually runs instead of silently failing open on Node ([#13093](https://github.com/diegosouzapw/OmniRoute/pull/13093)) — thanks @anhtahaylove +- **fix(test):** run the local `test` and `test:unit` scripts at concurrency 4 so a full-suite run no longer exhausts the machine's commit charge and kills unrelated processes ([#13187](https://github.com/diegosouzapw/OmniRoute/pull/13187)) — thanks @anhtahaylove +- **fix(monitoring):** `GET /api/monitoring/health` `credentialHealth` now includes a bounded `failedConnections` list (`connectionId`, `status`, sanitized `lastError`) when the probe-cache gauge `failed>0`, plus `source: probe-cache` and a cheap `staleDbNonOkCount` for sticky SQLite `test_status` on active rows. Documents that the live gauge is not `provider_connections.test_status`. ([#12876](https://github.com/diegosouzapw/OmniRoute/pull/12876)) — thanks @RaviTharuma +- **fix(resilience):** an openai-compatible multi-upstream gateway no longer marks the whole connection `credits_exhausted` when a single upstream (or the `/models` / representative-model health probe) returns 402. Quota failures stay model-scoped; true connection-wide auth failures are unchanged. Single-credential 402 key disable ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239)) is preserved. ([#12875](https://github.com/diegosouzapw/OmniRoute/pull/12875)) — thanks @RaviTharuma +- **fix(dashboard):** nine fixes on the Orchestration Canvas, all diagnosed in the Phase 2 reviews. A source that fails now keeps the timestamp of its FIRST failure instead of being re-stamped every poll — the stale line said "since the last poll" no matter how long the source had been down, and the churn also defeated the snapshot's stable identity (it serializes the source list), so the canvas re-rendered on every tick while anything was broken. A source that HAD data and only then started failing is flagged too: previously only a source with no node at all got the warning, so a source that went down mid-session kept a healthy-looking node forever. The rest are pointwise: clicking a filter chip cancels the pending search debounce (left armed, it fired ~300ms later and silently reverted the chip); the search input carries an accessible name; `?state=running, failed` parses like the unpadded form instead of dropping the padded value; the CSV toggle helper is defined once in `model/urlParams.ts` rather than twice; the agents tab tells "nothing running" apart from "the filter matched nothing", offering a clear-filters button instead of setup links that would be wrong advice there; edges stop emitting SMIL particles above 40 simultaneously active edges, keeping the colored stroke; and the drawer's error banner clears when a retried action succeeds. ([#12983](https://github.com/diegosouzapw/OmniRoute/pull/12983)) +- **fix(i18n):** the nine locales added with the EU-language batch (Greek, Estonian, Irish, Croatian, Lithuanian, Latvian, Maltese, Slovenian, Serbian) were missing the eleven Orchestration Canvas keys that Phase 3 introduced, so the compare-runs panel and the "no runs match these filters" empty state fell back to English in those languages (`deepMergeFallback` substitutes English for an absent key, so nothing rendered blank — it rendered untranslated). The coverage gate does not catch this: it enforces an 80% floor per locale, and eleven missing keys out of ~13,000 leaves coverage at 99.9%. Translated for real in each language, calibrated against the wording each file already uses for "run", "filter" and "skill". ([#13294](https://github.com/diegosouzapw/OmniRoute/pull/13294)) +- **fix(dashboard):** five follow-ups from the Phase 2 Orchestration Canvas review. The History drawer now shows the memory section for runs that already left the live TTL window: the persisted `memory_hits` event is parsed into `metadata.memoryHits` with the same defensive validation the drawer applies, and — because that event is observability rather than a state transition — it no longer leaks into the timeline, where it had been inheriting the task's state and rendering as a duplicate transition. The A2A memory recall runs against its own 1.5s deadline instead of inheriting the memory backend's 30s one; overshooting degrades exactly like any other recall failure (no hits, task proceeds), and the timer is cleared on both paths. Repeating a Conductor run carries its `requirements.cli`/`requirements.model` forward, so the new run is pinned to the same runner profile and model rather than drifting to whatever the fleet picks. A successful repeat from the Agents tab now focuses the run it created, instead of leaving the operator on the finished one. And the auth test for `POST /api/conductor/tasks` moved into the shared `ROUTES` array rather than restating the pattern. ([#12988](https://github.com/diegosouzapw/OmniRoute/pull/12988)) +- fix(plugins): stop leaking an exit listener per plugin hook timeout, which triggered MaxListenersExceededWarning on plugins that ignore SIGTERM ([#13092](https://github.com/diegosouzapw/OmniRoute/pull/13092)) — thanks @anhtahaylove +- **fix(validation):** Provider node edits no longer fail with a generic "Invalid request" when the optional daily-quota reset fields are left blank. The dashboard sends `dailyQuotaResetTimezone` and `dailyQuotaResetHour` as `null`, and only the hour accepted it. ([#13066](https://github.com/diegosouzapw/OmniRoute/issues/13066)) ([#13083](https://github.com/diegosouzapw/OmniRoute/pull/13083)) — thanks @ntdatt812 +- **fix(combo):** quota-aware expansion drops banned, inactive, missing, and wrong-provider connections before quota fetch or model dispatch; pins and allowlists stay selectors, not a bypass. Antigravity automatic exhaustion now requires a reported zero remaining, so a positive balance below 1% stays eligible. ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- Fix an intermittent failure in the quota-weighted routing test suite: scores are a function of `Date.now()`, so peers with identical quota scored microseconds apart never tied and swapped order. ([#12946](https://github.com/diegosouzapw/OmniRoute/pull/12946)) +- fix(volcengine): resolve quota window mapping gaps, safe multi-connection console binding, and response cookie sanitization ([#12950](https://github.com/diegosouzapw/OmniRoute/pull/12950)) — thanks @HouMinXi +- **fix(test):** resolve the WebDAV handler path with `fileURLToPath` so the suite's 37 WebDAV tests run on Windows instead of failing with a doubled `C:\C:\` drive prefix ([#13196](https://github.com/diegosouzapw/OmniRoute/pull/13196)) — thanks @anhtahaylove +- **fix(ui):** fall back from invalid AnySearch icon alias (direct commit `9587e07b69`) +- **fix(sse):** stop rebuilding a truncated summary from the collector's cap-dropped event array ([#12718](https://github.com/diegosouzapw/OmniRoute/pull/12718)) — thanks @hartmark +- **fix(conversations):** show a pending spinner for unresolved tool nodes instead of "(empty)" ([#12727](https://github.com/diegosouzapw/OmniRoute/pull/12727)) — thanks @hartmark +- **fix(streaming):** fail fast when an upstream stream produces only lifecycle/heartbeat events, never real content ([#12741](https://github.com/diegosouzapw/OmniRoute/pull/12741)) — thanks @hartmark +- **fix(responses-continuation):** bridge the write-in-flight window with an in-memory pending store ([#12854](https://github.com/diegosouzapw/OmniRoute/pull/12854)) — thanks @hartmark +- **fix(translator):** drop or map agent_message on Chat Completions fallback ([#12880](https://github.com/diegosouzapw/OmniRoute/pull/12880)) — thanks @RaviTharuma +- **fix(api):** page and stream-complete GET /v1/models for large catalogs ([#12882](https://github.com/diegosouzapw/OmniRoute/pull/12882)) — thanks @RaviTharuma +- **fix(quota):** align AUTH window labels with usage API and clarify 503 ([#12884](https://github.com/diegosouzapw/OmniRoute/pull/12884)) — thanks @RaviTharuma +- **fix(ci):** clear two base-reds on release/v3.8.51 — mutation-coverage gate + image-only-model guard ([#12945](https://github.com/diegosouzapw/OmniRoute/pull/12945)) +- **fix(api):** scope batch bulk-delete to the calling API key ([#13211](https://github.com/diegosouzapw/OmniRoute/pull/13211)) +- **fix(tests):** align model-sync log assertion to shipped path redaction (base-red) ([#13227](https://github.com/diegosouzapw/OmniRoute/pull/13227)) +- **fix(test):** make the ToS heading guard actually require the parentheses ([#13228](https://github.com/diegosouzapw/OmniRoute/pull/13228)) +- **fix(quality):** register 4 drifted covering tests in stryker tap.testFiles ([#13229](https://github.com/diegosouzapw/OmniRoute/pull/13229)) +- **fix(logging):** mask *-api-key headers in request-log pipeline (#13273) ([#13401](https://github.com/diegosouzapw/OmniRoute/pull/13401)) — thanks @KooshaPari +- **fix(tests):** correct stale Jina catalog prefix in custom-models test ([#13403](https://github.com/diegosouzapw/OmniRoute/pull/13403)) — thanks @KooshaPari +- **fix(backend):** prune old backups after health-check-repair snapshots (#13308) ([#13404](https://github.com/diegosouzapw/OmniRoute/pull/13404)) — thanks @KooshaPari +- **fix(backend):** guard memory_fts_au trigger with WHEN clause to stop FTS bloat (#13326) ([#13405](https://github.com/diegosouzapw/OmniRoute/pull/13405)) — thanks @KooshaPari +- **fix(free-tiers):** remove obsolete Together $25 signup credit (#12526) ([#13407](https://github.com/diegosouzapw/OmniRoute/pull/13407)) — thanks @KooshaPari +- **fix:** skip non-chat generation models in Test all models ([#13409](https://github.com/diegosouzapw/OmniRoute/pull/13409)) — thanks @KooshaPari +- **fix(providers):** de-duplicate agy and antigravity model catalogs ([#13410](https://github.com/diegosouzapw/OmniRoute/pull/13410)) — thanks @KooshaPari +- **fix(dashboard):** send provider in body for mini-playground web search ([#13411](https://github.com/diegosouzapw/OmniRoute/pull/13411)) — thanks @KooshaPari +- **fix(resilience):** add CJK quota-exhaustion patterns to 429 classifier ([#13412](https://github.com/diegosouzapw/OmniRoute/pull/13412)) — thanks @KooshaPari +- **fix(backend):** eval runner: failed calls no longer score as passed + dotAll regex ([#13413](https://github.com/diegosouzapw/OmniRoute/pull/13413)) — thanks @KooshaPari +- **fix(backend):** eval runner opts out of output-style and memory injection ([#13414](https://github.com/diegosouzapw/OmniRoute/pull/13414)) — thanks @KooshaPari +- **fix:** chain serial quarantine step in npm test script (fixes #13286) ([#13418](https://github.com/diegosouzapw/OmniRoute/pull/13418)) — thanks @KooshaPari +- **fix(antigravity):** derive stable session ID from credentials for prompt-cache reuse (#13305) ([#13424](https://github.com/diegosouzapw/OmniRoute/pull/13424)) — thanks @KooshaPari +- **fix(memory):** merge memory into top-level system field instead of unshifting at messages[0] (#13425) ([#13427](https://github.com/diegosouzapw/OmniRoute/pull/13427)) — thanks @KooshaPari +- **fix(compression):** preserve instruction blocks from lossy engines ([#13523](https://github.com/diegosouzapw/OmniRoute/pull/13523)) — thanks @KooshaPari +- **fix(compression):** preserve polarity words and newlines in ultra heuristic ([#13524](https://github.com/diegosouzapw/OmniRoute/pull/13524)) — thanks @KooshaPari +- **fix(compression):** run user preservePatterns before built-in patterns (#13457) ([#13525](https://github.com/diegosouzapw/OmniRoute/pull/13525)) — thanks @KooshaPari +- **fix(reasoning):** expand deepseek-v4 regex to match v4.1+ and add command-code provider (#13430) ([#13528](https://github.com/diegosouzapw/OmniRoute/pull/13528)) — thanks @KooshaPari +- **fix(providers):** add alwaysReasons metadata for reka-flash-3 ([#13531](https://github.com/diegosouzapw/OmniRoute/pull/13531)) — thanks @KooshaPari +- **fix(playground):** allow Compare columns to scroll independently ([#13532](https://github.com/diegosouzapw/OmniRoute/pull/13532)) — thanks @KooshaPari +- **fix(security):** empty whitelist allows through to prevent admin lockout ([#13534](https://github.com/diegosouzapw/OmniRoute/pull/13534)) — thanks @KooshaPari +- **fix(tests):** add DISABLE_IOREG_STRATEGY env var for macOS test compatibility (fixes #13467) ([#13539](https://github.com/diegosouzapw/OmniRoute/pull/13539)) — thanks @KooshaPari +- **fix(zed-hosted):** map developer role and Gemini enums for Zed proxy ([#13543](https://github.com/diegosouzapw/OmniRoute/pull/13543)) — thanks @KooshaPari +- **fix(analytics):** normalize provider aliases to canonical id in usage_history ([#13545](https://github.com/diegosouzapw/OmniRoute/pull/13545)) — thanks @KooshaPari +- **fix(combo):** use traceId for call log id to prevent UNIQUE constraint failure ([#13546](https://github.com/diegosouzapw/OmniRoute/pull/13546)) — thanks @KooshaPari +- **fix(auth):** check model lockout before returning synthetic noauth connection ([#13547](https://github.com/diegosouzapw/OmniRoute/pull/13547)) — thanks @KooshaPari +- **fix(guardrails):** skip credential redaction for base64 image data URLs ([#13550](https://github.com/diegosouzapw/OmniRoute/pull/13550)) — thanks @KooshaPari +- **fix(resilience):** increase requestQueue.maxWaitMs default from 15s to 30s ([#13553](https://github.com/diegosouzapw/OmniRoute/pull/13553)) — thanks @KooshaPari +- **fix(docs):** document OMNIROUTE_READY_TIMEOUT_MS and allowlist the test-only DISABLE_IOREG_STRATEGY ([#13692](https://github.com/diegosouzapw/OmniRoute/pull/13692)) ### 📝 Maintenance @@ -853,6 +1054,39 @@ _By commits in `release/v3.8.50..release/v3.8.51` (`091589089c..d6f315018a`), au - **docs(security):** document socket.yml scanner config + CI workflow link (#12575) ([#12764](https://github.com/diegosouzapw/OmniRoute/pull/12764)) — thanks @KooshaPari - **chore(ci):** guard commit identity in pre-commit to stop author misattribution ([#12772](https://github.com/diegosouzapw/OmniRoute/pull/12772)) - **deps:** 17 Dependabot bumps — bump electron from 43.4.0 to 43.4.1 in /electron ([#11426](https://github.com/diegosouzapw/OmniRoute/pull/11426)); bump the development group across 1 directory with 8 updates ([#11428](https://github.com/diegosouzapw/OmniRoute/pull/11428)); bump the production group across 1 directory with 11 updates ([#11440](https://github.com/diegosouzapw/OmniRoute/pull/11440)); chore(deps): bump github/codeql-action/init from 4.37.7 to 4.37.8 ([#11925](https://github.com/diegosouzapw/OmniRoute/pull/11925)); chore(deps): bump github/codeql-action from 4.37.7 to 4.37.8 ([#11926](https://github.com/diegosouzapw/OmniRoute/pull/11926)); chore(deps): bump github/codeql-action/analyze from 4.37.7 to 4.37.8 ([#11927](https://github.com/diegosouzapw/OmniRoute/pull/11927)); build(deps): bump github/codeql-action/init from 4.37.8 to 4.37.9 ([#12345](https://github.com/diegosouzapw/OmniRoute/pull/12345)); build(deps): bump github/codeql-action/analyze from 4.37.8 to 4.37.9 ([#12346](https://github.com/diegosouzapw/OmniRoute/pull/12346)); bump the development group across 1 directory with 2 updates ([#12347](https://github.com/diegosouzapw/OmniRoute/pull/12347)); build(deps): bump github/codeql-action from 4.37.8 to 4.37.9 ([#12349](https://github.com/diegosouzapw/OmniRoute/pull/12349)); bump browserslist from 4.28.2 to 4.28.8 ([#12396](https://github.com/diegosouzapw/OmniRoute/pull/12396)); bump the production group across 1 directory with 4 updates ([#12399](https://github.com/diegosouzapw/OmniRoute/pull/12399)); bump @xmldom/xmldom ([#12500](https://github.com/diegosouzapw/OmniRoute/pull/12500)); bump qs from 6.15.2 to 6.16.0 ([#12512](https://github.com/diegosouzapw/OmniRoute/pull/12512)); bump @xmldom/xmldom from 0.9.10 to 0.9.12 ([#12513](https://github.com/diegosouzapw/OmniRoute/pull/12513)); bump fast-uri from 3.1.5 to 3.1.7 ([#12514](https://github.com/diegosouzapw/OmniRoute/pull/12514)); bump @humanfs/node from 0.16.7 to 0.16.8 ([#12515](https://github.com/diegosouzapw/OmniRoute/pull/12515)) +- Clear the `release/v3.8.51` `check:agent-skills-sync` base-red: regenerate `skills/cli-tunnel/SKILL.md` so the `tunnel create [type]` positional that #13009 taught the generator to read is reflected in the committed skill. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` typecheck base-red from #12731: the new `Mode pack ranking gates` candidates in `open-sse/services/autoCombo/__tests__/autoCombo.test.ts` omitted the required `provider`, `model` and `errorRate` fields of `ProviderCandidate`, failing `check:open-sse-typecheck` (and with it `Fast Quality Gates`) on every open PR. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` docs-sync base-red: EURouter (#13025) and GreenPT (#13024) took the live provider count to 358, leaving 7 STRICT drifts (`PROVIDER_REFERENCE.md`, 4 diagrams, 2 tier-flow images) plus the `AGENTS.md` / `llm.txt` / `package.json` count claims stale. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- **test(models):** Pin `CATALOG_BUILD_TIMEOUT_MS` in the #12058 canonical-catalog regression so a cold catalog build racing the 8s default bound no longer flakes the suite with a 500 ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Allow `@omniroute/opencode-plugin-v2/` in the npm pack policy — [#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870) shipped the v2 plugin beside its v1 sibling without widening the allowlist, so every packed file under it read as an unexpected artifact and failed `check:pack-policy` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Point the release-green validator's pack gate at the tree it is validating — the build-provenance guard defaulted to `origin/main`, which a release branch tip can never be an ancestor of mid-cycle, so `Package artifact` reported a false red on every run and the tarball boot-smoke cascaded off it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **docs(checks):** keep doc counts honest — headings, rankings, catalog, weights, quality gate and scoring diagram now covered ([#12507](https://github.com/diegosouzapw/OmniRoute/pull/12507)) — thanks @maxmad64bis +- **chore(lifecycle):** `check:model-lifecycle` now also diffs `DEFAULT_DEGRADATION_MAP` (the background-task redirect table) against the vendor lifecycle snapshot, refusing a retired id as source or target, with a table-driven unit test beside it. Three rows whose source the vendor had retired — `claude-sonnet-4-20250514`, `gemini-3-pro-preview` and `gpt-5.1-codex` (whose target `gpt-5.1-codex-mini` is retired too) — were dead code, since `checkLifecycle` answers 410 before the redirect runs; they are dropped (#12535 — thanks @pacocartones) +- **test(compression):** cover idle worker eviction at the resource level — the pool must call `terminate()` and must not retain the worker's `MessagePort`, complementing the `exit`-event assertion added with the fix ([#13371](https://github.com/diegosouzapw/OmniRoute/pull/13371)) +- **chore(quality):** raise the `imageGeneration.ts` file-size ceiling for the image-only-model guard that clears the #12945 base-red ([#13392](https://github.com/diegosouzapw/OmniRoute/pull/13392)) +- **chore(quality):** type the combo-test route's JSON response bodies instead of casting them to `any`, drop the now-empty suppression entry, and remove the `hasPerModelQuota` import `chatCore.ts` stopped using when the failure-classification helper was extracted ([#13379](https://github.com/diegosouzapw/OmniRoute/pull/13379)) +- **chore(electron):** the desktop bundle no longer ships the root-level files of every translated docs mirror (`README.md`, `llm.txt`, `CLAUDE.md`, `GEMINI.md`, `CONTRIBUTING.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`) — the packaged app only reads `docs/i18n//docs/**`, which stays. Saves ~11 MB on top of the translated CHANGELOGs already pruned. (#0000) ([#13235](https://github.com/diegosouzapw/OmniRoute/pull/13235)) +- **chore(quality):** retighten the `src/sse/handlers/chat.ts` unused-vars suppression count to the 8 violations that actually remain ([#13385](https://github.com/diegosouzapw/OmniRoute/pull/13385)) +- Bring the documented counts back in line with the code: 171 migrations (was 169) and 20 routing strategies (was 19), plus the regenerated `cli-tunnel` skill reference. ([#12970](https://github.com/diegosouzapw/OmniRoute/pull/12970)) +- Update the documented migration count to 172 after the call-logs provider-stats indexes landed. ([#13209](https://github.com/diegosouzapw/OmniRoute/pull/13209)) +- **chore(ci):** two gates that close the blind spots behind the exclusions above. `check:vitest-exclusions` requires every Vitest exclusion to name a tracking issue and to appear in `config/quality/vitest-exclusions.json` — the previous list grew to 62 files behind a comment pointing at an issue that had been closed for a month. `check-new-key-coverage` requires a key newly added to `en.json` to reach every locale; the existing coverage gate is a percentage floor per locale, so eleven absent keys out of ~13,000 left it at 99.9% while a whole feature shipped untranslated in nine languages. Both are diff-aware, so pre-existing debt stays frozen and neither needed a migration to turn on. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **build:** root layout no longer loads Inter from `next/font/google`, so a production image build does not need fonts.googleapis.com ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **docs(i18n):** bring the eight new `llm.txt` mirrors (km, kn, ml, my, ne, or, pa, si) up to the current provider and migration counts, so `check-docs-sync` stops rejecting every commit ([#13674](https://github.com/diegosouzapw/OmniRoute/pull/13674)) +- **refactor(ui):** flow surfaces (home topology, combo live studio, compression cockpit/waterfall/nodes, token-health badge) express state through the theme-aware `--orch-status-*` tokens instead of fixed dark-mode hex, so green/red/amber/grey stay legible in the light theme; dark mode is byte-identical. Categorical palettes (routing-strategy hues, compression-layer pills, provider brand colors) deliberately stay hex ([#12378](https://github.com/diegosouzapw/OmniRoute/issues/12378)) ([#13203](https://github.com/diegosouzapw/OmniRoute/pull/13203)) +- **chore(tests):** 51 test files that had been excluded from Vitest are running again, restoring roughly 350 assertions to the blocking suite. Each was measured individually first: of the 62 files parked behind the `// #8618 — pre-existing failure` comment, 51 pass against the current tree with no source change, so the exclusions had outlived the failures they were added for. The 11 that genuinely still fail stay excluded, but now point at a live tracker (#13204) rather than at #8618, which was closed in August while the list it tracked kept growing. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **chore(quality):** drop two ESLint suppression entries whose violations no longer exist, so `eslint --suppressions-location` stops rejecting every commit that touches the surrounding files ([#13384](https://github.com/diegosouzapw/OmniRoute/pull/13384)) +- **chore(release):** bring main's post-tag fixes into release/v3.8.51 — sync-back step 2/2 (direct commit `21c488f210`) +- **chore(deps):** patch toml and esbuild in the opencode plugin lockfiles ([#12965](https://github.com/diegosouzapw/OmniRoute/pull/12965)) +- **docs(changelog):** reconcile the v3.8.51 living section — fold 366 fragments, cover every cycle commit, credit every contributor ([#12971](https://github.com/diegosouzapw/OmniRoute/pull/12971)) +- **test(plugin):** wait for the v2 lazy-refresh tier instead of sleeping past it ([#12984](https://github.com/diegosouzapw/OmniRoute/pull/12984)) +- **chore(quality):** rebaseline chatCore.ts after the non-streaming regression fixes ([#13045](https://github.com/diegosouzapw/OmniRoute/pull/13045)) +- **docs(changelog):** add the missing bullet marker to the DeepSeek PoW fragment ([#13200](https://github.com/diegosouzapw/OmniRoute/pull/13200)) +- **chore(deps):** drain the Dependabot queue — 10 of 13 alerts ([#13213](https://github.com/diegosouzapw/OmniRoute/pull/13213)) +- **chore(ci):** make worktrees carry the hooks that enforce the identity gate ([#13236](https://github.com/diegosouzapw/OmniRoute/pull/13236)) +- **chore(deps):** pin csv-parse to ^7.0.2 via overrides (#13117) ([#13300](https://github.com/diegosouzapw/OmniRoute/pull/13300)) — thanks @KooshaPari +- **chore(deps):** bump hono from ^4.12.34 to ^4.13.7 (#13148) ([#13301](https://github.com/diegosouzapw/OmniRoute/pull/13301)) — thanks @KooshaPari +- **chore(deps):** pin joi to ^18.2.8 via overrides (#13085) ([#13302](https://github.com/diegosouzapw/OmniRoute/pull/13302)) — thanks @KooshaPari +- **docs(providers):** add Microsoft 365 Copilot (BizChat) provider guide (#12779) ([#13340](https://github.com/diegosouzapw/OmniRoute/pull/13340)) — thanks @KooshaPari ### 🙌 Contributors @@ -870,6 +1104,7 @@ Thanks to everyone whose work landed in v3.8.51: | [@amaleta](https://github.com/amaleta) | #11906, #11934 | | [@amartinawi](https://github.com/amartinawi) | #11575 | | [@AndrianBalanescu](https://github.com/AndrianBalanescu) | #11083, #11830, #11834, #11844 | +| [@anhtahaylove](https://github.com/anhtahaylove) | #13091, #13092, #13093, #13096, #13097, #13100, #13106, #13109, #13114, #13155, #13166, #13171, #13175, #13187, #13196 | | [@aniruddhaadak80](https://github.com/aniruddhaadak80) | #11760 | | [@app](https://github.com/app) | #12554 | | [@arminanton](https://github.com/arminanton) | #11461, #11513 | @@ -899,10 +1134,10 @@ Thanks to everyone whose work landed in v3.8.51: | [@gonisulaimann](https://github.com/gonisulaimann) | #12368, #12369, #12371 | | [@Gorillaz322](https://github.com/Gorillaz322) | #12207 | | [@groovecityJO](https://github.com/groovecityJO) | #12682 | -| [@hartmark](https://github.com/hartmark) | #11434, #11452, #11473, #11499, #11703, #11983, #11984, #11985, #11986, #11988, #11989, #11990, #11994, #12221, #12293, #12445, #12446, #12447, #12448, #12460, #12461, #12623, #12646, #12650, #12680 | +| [@hartmark](https://github.com/hartmark) | #11434, #11452, #11473, #11499, #11703, #11983, #11984, #11985, #11986, #11988, #11989, #11990, #11994, #12221, #12293, #12445, #12446, #12447, #12448, #12460, #12461, #12623, #12646, #12650, #12680, #12717, #12718, #12727, #12741, #12854 | | [@hizzt](https://github.com/hizzt) | #11894 | | [@hongnoul](https://github.com/hongnoul) | #11484 | -| [@HouMinXi](https://github.com/HouMinXi) | #11411, #11414, #11512, #11518, #11520, #11641, #11642, #11643, #11687, #11779, #11849, #11850, #11851, #11915, #11916, #11918, #11919, #11920, #12013, #12017, #12033, #12042, #12106, #12139, #12166, #12169, #12171, #12205, #12213, #12312, #12325, #12487, #12488, #12495, #12504, #12557, #12566, #12590, #12591, #12624, #12626, #12632, #12637, #12678, #12696, #12697, #12711, #12733, #12746, #12767, #12770, #12789, #12803, #12805, #12811, #12866, #12868, #12899, #12926, #12934 | +| [@HouMinXi](https://github.com/HouMinXi) | #11411, #11414, #11512, #11518, #11520, #11641, #11642, #11643, #11687, #11779, #11849, #11850, #11851, #11915, #11916, #11918, #11919, #11920, #12013, #12017, #12033, #12042, #12106, #12139, #12166, #12169, #12171, #12205, #12213, #12312, #12325, #12487, #12488, #12495, #12504, #12557, #12566, #12590, #12591, #12624, #12626, #12632, #12637, #12678, #12696, #12697, #12711, #12733, #12746, #12767, #12770, #12789, #12803, #12805, #12811, #12866, #12868, #12899, #12926, #12934, #12950, #12951, #12974, #13001, #13006, #13011, #13017, #13026, #13027, #13034, #13035, #13038, #13042, #13050, #13060, #13061, #13069, #13107, #13120, #13136, #13195, #13197 | | [@Hsia97](https://github.com/Hsia97) | #11624 | | [@jacobsparts](https://github.com/jacobsparts) | #11854, #12155, #12167 | | [@joglomedia](https://github.com/joglomedia) | #11980 | @@ -912,11 +1147,11 @@ Thanks to everyone whose work landed in v3.8.51: | [@KaspaPulse](https://github.com/KaspaPulse) | #11389, #11469, #11628, #11666, #11910 | | [@keeltrace](https://github.com/keeltrace) | #12080, #12223 | | [@killer30001000](https://github.com/killer30001000) | #12178 | -| [@KooshaPari](https://github.com/KooshaPari) | #12470, #12592, #12667, #12699, #12703, #12706, #12764, #12769, #12771 | +| [@KooshaPari](https://github.com/KooshaPari) | #12470, #12592, #12667, #12699, #12703, #12706, #12764, #12769, #12771, #13300, #13301, #13302, #13340, #13401, #13403, #13404, #13405, #13406, #13407, #13409, #13410, #13411, #13412, #13413, #13414, #13418, #13424, #13427, #13433, #13523, #13524, #13525, #13528, #13531, #13532, #13534, #13539, #13543, #13545, #13546, #13547, #13550, #13553 | | [@kriptoburak](https://github.com/kriptoburak) | #11370 | | [@KrzysiekSko](https://github.com/KrzysiekSko) | #12425, #12432, #12673 | | [@marcelokarval](https://github.com/marcelokarval) | direct commit / report | -| [@maxmad64bis](https://github.com/maxmad64bis) | #1622, #11435, #11437, #11441, #11537, #11550, #11553, #11555, #11557, #11812, #11842, #11843, #11903, #12151, #12214, #12215, #12218, #12226, #12314, #12316, #12317, #12318, #12319, #12320, #12321, #12870 | +| [@maxmad64bis](https://github.com/maxmad64bis) | #1622, #11435, #11437, #11441, #11537, #11550, #11553, #11555, #11557, #11812, #11842, #11843, #11903, #12151, #12214, #12215, #12218, #12226, #12314, #12316, #12317, #12318, #12319, #12320, #12321, #12507, #12715, #12731, #12744, #12786, #12787, #12788, #12790, #12792, #12794, #12795, #12828, #12832, #12853, #12857, #12870, #12937, #12941, #12975, #13141, #13142, #13146, #13147 | | [@mdigitalbh81](https://github.com/mdigitalbh81) | #12240 | | [@Meet6338-X](https://github.com/Meet6338-X) | #11598, #11609, #12110 | | [@morpheus9393](https://github.com/morpheus9393) | #11943 | @@ -925,11 +1160,11 @@ Thanks to everyone whose work landed in v3.8.51: | [@NightStalker-87](https://github.com/NightStalker-87) | #12183 | | [@NoxzRCW](https://github.com/NoxzRCW) | #11879, #11880, #11881, #11882, #11883, #11935 | | [@ntdat812](https://github.com/ntdat812) | #11585, #12095 | -| [@ntdatt812](https://github.com/ntdatt812) | #11368, #11573, #11574, #11576, #11577, #11580, #11582, #11583, #11588, #11589, #11590, #11591, #11592, #11593, #11672, #12177, #12180 | +| [@ntdatt812](https://github.com/ntdatt812) | #11368, #11573, #11574, #11576, #11577, #11580, #11582, #11583, #11588, #11589, #11590, #11591, #11592, #11593, #11672, #12177, #12180, #12873, #12918, #12920, #12921, #12925, #12930, #13007, #13009, #13024, #13025, #13083, #13087, #13101, #13104, #13110 | | [@official-burak](https://github.com/official-burak) | #11542 | | [@opensource-elearning](https://github.com/opensource-elearning) | #12179, #12189, #12278, #12286 | | [@oyi77](https://github.com/oyi77) | #11408, #11409, #11421, #11505, #11677, #12036, #12110 | -| [@pacocartones](https://github.com/pacocartones) | #11521, #11522, #11527, #11528, #11529, #11530, #11531, #11532, #11533, #11534, #11595, #11599, #11603, #11604, #11605, #11607, #11610, #11676, #11714, #11716, #11718, #11767, #11838, #11860, #11862, #11869, #11871, #11872, #11873, #11903, #11906, #11921, #11934, #12359, #12360, #12361, #12362, #12364, #12365, #12373, #12374, #12375, #12376, #12377, #12379, #12380, #12381, #12386, #12387, #12389, #12390, #12394, #12395, #12397, #12401, #12403, #12404 | +| [@pacocartones](https://github.com/pacocartones) | #11521, #11522, #11527, #11528, #11529, #11530, #11531, #11532, #11533, #11534, #11595, #11599, #11603, #11604, #11605, #11607, #11610, #11676, #11714, #11716, #11718, #11767, #11838, #11860, #11862, #11869, #11871, #11872, #11873, #11903, #11906, #11921, #11934, #12356, #12358, #12359, #12360, #12361, #12362, #12364, #12365, #12373, #12374, #12375, #12376, #12377, #12379, #12380, #12381, #12386, #12387, #12389, #12390, #12394, #12395, #12397, #12401, #12403, #12404, #12522, #12523, #12535, #12536, #12540, #12541, #12543, #12545, #12548, #12549, #12550, #12551, #12552, #12647, #12651, #12653, #13055 | | [@patrykkopycinski](https://github.com/patrykkopycinski) | #11936, #11937, #12224 | | [@PauloFH](https://github.com/PauloFH) | #11509 | | [@PauloHSOliveira](https://github.com/PauloHSOliveira) | #12241 | @@ -941,7 +1176,7 @@ Thanks to everyone whose work landed in v3.8.51: | [@raheemuddin786](https://github.com/raheemuddin786) | #11491, #11839, #11840, #11841, #12003, #12230, #12231, #12232, #12233, #12234 | | [@rahilmavani](https://github.com/rahilmavani) | #11761 | | [@Rahulsharma0810](https://github.com/Rahulsharma0810) | #11771 | -| [@RaviTharuma](https://github.com/RaviTharuma) | #11710, #11727, #11797, #11798, #11802, #11805, #11806, #11811, #12098, #12099, #12101, #12449, #12452, #12472, #12473, #12493, #12533, #12607, #12628, #12631, #12636 | +| [@RaviTharuma](https://github.com/RaviTharuma) | #11710, #11727, #11797, #11798, #11802, #11805, #11806, #11811, #12098, #12099, #12101, #12449, #12452, #12472, #12473, #12493, #12533, #12607, #12628, #12631, #12636, #12875, #12876, #12880, #12882, #12884 | | [@rezjalibd](https://github.com/rezjalibd) | #12186 | | [@RhianB14](https://github.com/RhianB14) | #11692 | | [@rifqiawl](https://github.com/rifqiawl) | #11517, #11519, #11969 | diff --git a/docs/i18n/ko/CHANGELOG.md b/docs/i18n/ko/CHANGELOG.md index c1068e215a..5e5ac28754 100644 --- a/docs/i18n/ko/CHANGELOG.md +++ b/docs/i18n/ko/CHANGELOG.md @@ -8,16 +8,1119 @@ ## [3.8.51] — TBD -_Living section — cycle opened at the v3.8.50 freeze (parallel-cycle model). Bullets are aggregated from `changelog.d/` fragments at each `/generate-release` phase._ +_Living section — reconciled 2026-09-15 from all cycle commits (`091589089c` → `c0f92ec98a`, 916 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 | **123** | +| 📝 Commits in the cycle | **916** | +| 🔀 Pull requests referenced | **892** | +| 📋 Changelog entries | **919** | +| 🙌 Contributors credited in entries | **121** | +| 🤖 Automated dependency commits | 18 | + +**Entries by type** + +| Type | Count | +| --- | ---: | +| 🐛 Fixes | 592 | +| ✨ Features | 131 | +| 🧹 Chore | 93 | +| 📚 Docs | 42 | +| 🧪 Tests | 32 | +| ♻️ Refactor | 8 | +| 🏗️ Build | 7 | +| ⚡ Performance | 7 | +| ⚙️ CI | 2 | +| ⏪ Reverts | 2 | +| 📦 Dependencies | 2 | +| 🔒 Security | 1 | + +### 🏆 Top 25 contributors this cycle + +_By commits in `091589089c..c0f92ec98a`, author identities consolidated via `.mailmap` and the merged PR's GitHub login. Bots excluded._ + +| # | Contributor | Commits | +| ---: | --- | ---: | +| 🥇 | diegosouzapw | 323 | +| 🥈 | Bob.Hou (@HouMinXi) | 79 | +| 🥉 | Paco Cartones (@pacocartones) | 77 | +| 4 | Dizzle (@maxmad64bis) | 49 | +| 5 | Koosha Paridehpour (@KooshaPari) | 43 | +| 6 | Nguyen Thanh Dat (@ntdatt812) | 31 | +| 7 | Ravi Tharuma (@RaviTharuma) | 26 | +| 8 | Markus Hartung (@hartmark) | 22 | +| 9 | Webman (@jonlwheat2-gif) | 22 | +| 10 | backryun | 17 | +| 11 | anhtahaylove | 15 | +| 12 | Rafa Martins (@rafacpti23) | 10 | +| 13 | Syed Raheemuddin (@raheemuddin786) | 10 | +| 14 | Nguyễn Viết Tuấn (@TheDemonTuan) | 8 | +| 15 | MumuTW | 7 | +| 16 | Paijo (@oyi77) | 7 | +| 17 | Tobias Andersen (@turbolego) | 6 | +| 18 | Bl0ck (@Bl0ck154) | 5 | +| 19 | SHANMUGAPRIYAN (@geek007git) | 5 | +| 20 | KaspaPulse | 5 | +| 21 | Abhishek Sharma (@abhisheksharma2411) | 4 | +| 22 | Abhishek Divekar (@adivekar-utexas) | 4 | +| 23 | Andrew B. (@AndrianBalanescu) | 4 | +| 24 | NoxzRCW | 4 | +| 25 | opensource-elearning | 4 | ### ✨ New Features +- **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). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- Added Google AI Studio Gemini batch text-to-speech support through `POST /v1/audio/speech`. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- 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. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(routing):** subscription-first auto groupings — `auto/subscription` routes only through plan-included connections with a documented hard-stop overage and fails closed on exhaustion, while `auto/thrifty` orders the pool `subscription → keyless → free → cheap → premium` and steps up one rung at a time as each is exhausted. Billing class comes from a curated per-connection catalog (uncurated is treated as metered, never plan-included), both reuse STRICT_ZERO_COST's per-connection verification, and a quota reading whose `resetAt` has passed is now refreshed regardless of TTL so routing returns to plan capacity as soon as it resets ([#11146](https://github.com/diegosouzapw/OmniRoute/pull/11146)) — thanks @yourspraveen +- **feat(providers):** publish a management-authenticated versioned web-session credential contract from OmniRoute's canonical browser credential metadata ([#11340](https://github.com/diegosouzapw/OmniRoute/pull/11340)) — thanks @Zartharas +- **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)) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add Xquik X search with typed results, credential validation, REST routing, and MCP selection ([#11370](https://github.com/diegosouzapw/OmniRoute/pull/11370)) — thanks @kriptoburak +- **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)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(dashboard):** surface durable exclusive managed leases in the existing Sessions view, keeping leased clients visible across idle gaps while marking connections with in-flight work as active ([#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389)) — thanks @KaspaPulse +- **build(bun):** allow Turbopack bundler flag on Bun 1.4+ with configurable Webpack fallback ([#11471](https://github.com/diegosouzapw/OmniRoute/pull/11471)) — thanks @TheDemonTuan +- feat(api): add an opt-in `modelVisibilityAllowlist`/`modelVisibilityDenylist` settings pair to curate exactly which models `/v1/models` advertises, mirrored into every `auto/*` combo candidate pool so a denied model cannot be routed to via combo selection either (#11481) ([#11997](https://github.com/diegosouzapw/OmniRoute/pull/11997)) +- **feat(rankings):** the Free Provider Rankings page shows what each provider actually served over the last 24 h. It ranked by ELO alone, which left a provider that answers every call with an error in first place; the usage data was already served by the API but never requested. A provider with too small a sample shows a dash, not a number ([#11546](https://github.com/diegosouzapw/OmniRoute/pull/11546)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **feat(guardrails):** enforce a bounded, deterministic contract for Video Bridge transcripts — 256 cues, 4096 input code units and 4 KiB UTF-8 per cue, 64 KiB total text, malformed-Unicode rejection, focus-window scoping, cross-source reconciliation with contributing-source metadata, and a structural provenance trust boundary so caller JSON can never self-assert `embedded`/`audio-bridge` provenance ([#11652](https://github.com/diegosouzapw/OmniRoute/issues/11652)) ([#12009](https://github.com/diegosouzapw/OmniRoute/pull/12009)) +- **feat(video):** orchestrate optional Video Bridge audio extraction and Audio Bridge STT behind a dual opt-in (operator setting AND per-request signal) — a new loopback-only broker `mode=audio` operation shares the frame path's exact process queue, deadline, AbortSignal, and byte budgets to extract a bounded mono 16 kHz PCM WAV from the same already-downloaded video, then reuses the existing Audio Bridge transcription boundary; provider segment timing is preserved when available and marked coarse otherwise, and every failure degrades to a visual-only-safe partial instead of throwing (#11654). ([#12012](https://github.com/diegosouzapw/OmniRoute/pull/12012)) +- Add a tenant-bound Video Bridge drill-down lifecycle on top of the existing secure cache substrate: opaque hashed handles (never raw session/video identifiers), preview/standard/detail multiresolution variants resampled on read, response pagination capped at 8 frames and 32 MiB, and a new authenticated `/api/v1/video-bridge/drilldown` consumer route that stays disabled for remote access by default and denies cross-key access with the same response as a nonexistent handle (no existence oracle). ([#12006](https://github.com/diegosouzapw/OmniRoute/pull/12006)) +- **test(video):** Add the Video Bridge FU-07/FU-09 promotion-evidence harness (#11656) — a frozen Zod manifest schema covering the 8 required scenario kinds (static scenes, rapid cuts, late facts, fades, blur, small text, close events, visual prompt injection) with a minimum of 3 repetitions per case, deterministic declarative fixture recipes (`videoBridgePromotionFixtures.ts`), a pure medians/p95 metrics aggregator, a pure FU-07/FU-09 promotion-verdict evaluator applying the ticket's exact thresholds (missing token usage always holds), a digest-only persistence layer that never retains raw media or raw model responses, and a versioned per-model promotion allowlist shipped empty with every model defaulting to `hold`. The FU-07/FU-09 promotion verdicts themselves remain HOLD — they require a real evidence run against real models on VPS 192.168.0.15. ([#12008](https://github.com/diegosouzapw/OmniRoute/pull/12008)) +- **feat(video bridge):** "embedded" transcript provenance can now be legitimately earned instead of merely asserted — a bounded, allowlisted (`mov_text`/`subrip`/`webvtt`) subtitle probe runs through the loopback-only Video Bridge broker (at most 2 streams, 10s subdeadline bounded by the request deadline, 256 KiB output, 4096-code-unit lines), normalized through a bounded, ReDoS-safe WebVTT parser and Zod-validated end to end. The adapter always resolves to an explicit `success`/`absent`/`transient_failure` outcome — a subtitle failure never breaks the visual description path, and only a fingerprint-verified broker response (never a caller-declared label) can produce embedded cues (#11659). ([#12011](https://github.com/diegosouzapw/OmniRoute/pull/12011)) +- Default new Antigravity-family connections (agy CLI imports and Antigravity OAuth connects) to model auto-sync, so live model discovery lands in the synced catalog and `/v1/models` picks up freshly released upstream models (e.g. Gemini 3.7 Flash tiers) without code changes. Existing connections keep their current setting; the per-connection dashboard toggle remains the opt-out. (#11685 — thanks @MumuTW) ([#11887](https://github.com/diegosouzapw/OmniRoute/pull/11887)) +- **feat(ui):** enable React Compiler (`reactCompiler: true` + `babel-plugin-react-compiler`) for automatic memoization at build time ([#11783](https://github.com/diegosouzapw/OmniRoute/pull/11783)) — thanks @jonlwheat2-gif +- **feat(providers):** add SeekAi (`seekai.cc`) as an OpenAI-compatible New-API gateway — catalog id `seekai` (alias `ska`), `https://seekai.cc/v1`, live `/v1/models` via `passthroughModels`, aggregator-list membership so New-API balance detection can opt in. No referral/aff codes. ([#11786](https://github.com/diegosouzapw/OmniRoute/issues/11786)) ([#12557](https://github.com/diegosouzapw/OmniRoute/pull/12557)) — thanks @HouMinXi +- **feat(zai):** add GLM-5.3-Flash Coding Plan support (1M context, 128K output, vision, `low|high|max` reasoning) and route `zai` GLM-5.3-family API-key traffic through the OpenAI-compatible Coding Plan endpoint with native thinking defaults ([#11801](https://github.com/diegosouzapw/OmniRoute/pull/11801)) — thanks @Neuron-Mr-White +- **feat(combo):** choose how combo models are ordered — manual, provider, score, or name — via a sort control in the dashboard builder, persisted in `config.modelSort` and re-applied on load and after add ([#11812](https://github.com/diegosouzapw/OmniRoute/pull/11812)) — thanks @maxmad64bis +- **feat(free):** custom models can be marked free-tier via `customModels[].isFree`; `isFreeModel()` is the first door and `hidePaidModels` respects it even for providers outside the free budget ([#11843](https://github.com/diegosouzapw/OmniRoute/pull/11843)) — thanks @maxmad64bis +- **feat(nodejs):** add `5dive` as a `configure` target — `omniroute configure 5dive` / `omniroute setup-5dive` write a 5dive auth profile that points an agent fleet's `claude` seats at OmniRoute, with the root-only write, the loopback-vs-`https` endpoint rule and the per-seat model pin handled explicitly ([#11852](https://github.com/diegosouzapw/OmniRoute/pull/11852)) — thanks @5dive-bot +- **feat(sse):** treat `max` as a first-class reasoning-effort tier and clamp per model family (GLM 5.1+/DeepSeek V4+/Kimi K3+ keep native `max`; o1/MiniMax/Grok/Muse Spark clamp to their upstream ceiling) ([#11875](https://github.com/diegosouzapw/OmniRoute/pull/11875)) — thanks @Chewji9875 +- **feat(providers):** the provider plugin manifest now advertises a `usage-fetch` capability for the 40 providers that have a wired usage/quota fetcher, so external dashboards can read it from `GET /api/v1/provider-plugin-manifest` instead of parsing `open-sse/services/usage.ts` after every release. Discovery only — no new fetcher, no quota change, and the Dashboard quota widget stays gated by `USAGE_SUPPORTED_PROVIDERS`. `USAGE_FETCHER_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/fetcherProviders.ts`) and is re-exported from `services/usage.ts`, keeping the manifest module a light leaf instead of pulling the ~490-module usage dispatcher into the manifest route. ([#11903](https://github.com/diegosouzapw/OmniRoute/pull/11903)) — thanks @maxmad64bis / @pacocartones +- **feat(plugins):** `OMNIROUTE_PLUGINS_DIR` sets the directory the runtime plugin scanner reads — and the root the plugin manager installs into — overriding the `HOME`-derived default, so a Docker/K8s deployment can point straight at its bind-mounted plugin tree instead of moving `HOME` just to relocate the scan path. An image that exports no home no longer scans `/tmp/.omniroute/plugins` in silence: the resolved directory is logged once at startup as `scanner.dir_resolved`, naming the input that won. Unset, behaviour is unchanged. Distinct from the CLI-only `OMNIROUTE_PLUGIN_PATH`, which finds `omniroute-cmd-*` command packages and never reached this scanner ([#11906](https://github.com/diegosouzapw/OmniRoute/pull/11906)) — thanks @amaleta / @pacocartones +- **feat(leases):** add an explicit owner-authenticated status action that returns only the active lease's privacy-safe configured connection and provider labels, with generation fencing and no credential or internal-id disclosure ([#11910](https://github.com/diegosouzapw/OmniRoute/pull/11910)) — thanks @KaspaPulse +- **feat(providers):** import-from-file modal shows per-row API errors and ships a downloadable CSV template ([#12071](https://github.com/diegosouzapw/OmniRoute/issues/12071)) ([#12504](https://github.com/diegosouzapw/OmniRoute/pull/12504)) — thanks @HouMinXi +- **feat(providers):** dashboard search matches connection name and `baseUrl` so imported OpenAI-compat nodes surface on the provider card ([#12108](https://github.com/diegosouzapw/OmniRoute/issues/12108)) ([#12495](https://github.com/diegosouzapw/OmniRoute/pull/12495)) — thanks @HouMinXi +- **feat(routing):** add a `score` Auto router strategy that selects the highest configured weighted score and reuses `explorationRate`. ([#12155](https://github.com/diegosouzapw/OmniRoute/pull/12155)) — thanks @jacobsparts +- Add a runtime feature flag to disable universal context handoffs globally without changing the default behavior. ([#12167](https://github.com/diegosouzapw/OmniRoute/pull/12167)) — thanks @jacobsparts +- **feat(providers):** the provider plugin manifest now also advertises a `usage-supported` capability for the 46 providers whose usage API is accepted by the server and Dashboard routes, so integrators can distinguish "the server will serve quota for this provider" from "a fetcher is wired" without reading TypeScript. Discovery only — no fetcher or quota change. `usage-fetch` resolves on id or alias (the usage dispatcher accepts both); `usage-supported` resolves on id alone, matching the runtime guard `USAGE_SUPPORTED_PROVIDERS.includes(providerId)`. `USAGE_SUPPORTED_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/supportedProviders.ts`) and is re-exported from `providers.ts`, mirroring the `fetcherProviders` leaf from #11903 and keeping the manifest a light module. ([#12214](https://github.com/diegosouzapw/OmniRoute/pull/12214)) — thanks @maxmad64bis +- **feat(rankings):** order Free Provider Rankings by what each provider actually served — `GET /api/free-provider-rankings?sortBy=reliability` and a "Most reliable first" toggle on the page. Providers with too few calls to state a success rate keep their score order below the measured ones; the default order is unchanged ([#12218](https://github.com/diegosouzapw/OmniRoute/pull/12218)). — thanks @maxmad64bis +- **perf(sse):** defer `cloneLogPayload()` in the structured SSE collector until after the `maxEvents`/`maxBytes` cap check, eliminating ~9,800 wasted `structuredClone` calls per streaming response (65–71% faster `push()`). Reducer snapshot isolation restored for OpenAI and Responses summaries ([#12241](https://github.com/diegosouzapw/OmniRoute/pull/12241)) — thanks @PauloHSOliveira ([#12243](https://github.com/diegosouzapw/OmniRoute/pull/12243)) +- **feat(usage):** Devin CLI agentic quota (Codeium seat-management GetUserStatus) and OpenRouter key limits plus account credits now surface in Provider Limits ([#12256](https://github.com/diegosouzapw/OmniRoute/pull/12256) — thanks @Neuron-Mr-White) +- **feat(settings):** persist `headroomUrl` through Settings so status/start use the operator URL instead of only `HEADROOM_URL` ([#12306](https://github.com/diegosouzapw/OmniRoute/issues/12306)) ([#12487](https://github.com/diegosouzapw/OmniRoute/pull/12487)) — thanks @HouMinXi +- **feat(auto-combo):** Auto-Combo scoring can now weigh how often a provider/model has actually succeeded. The engine already carried that number on every candidate — 24 hours of usage history behind a ten-sample floor, real-time metrics otherwise — and the scoring function never read it, while the weight table described `stability` as if it did. `reliability` (`1 - failureRate`, with the same field precedence and the same rate-bounding the speed ranking already uses, so a corrupt reading means "nothing observed" rather than "fails every call") is now a declared factor shipping at weight `0`, so routing is unchanged until an operator gives it one, and the `stability` description now matches what that factor computes ([#12317](https://github.com/diegosouzapw/OmniRoute/pull/12317)) — thanks @maxmad64bis +- **feat(routing):** With `freeAccessPolicy: "strict"`, the read-only candidate listing (`GET /v1/auto-combo/{channel}/candidates`) no longer hides the candidates the zero-cost guard excludes — the same read-only transparency the resilience filter already honours (#9133). Each candidate now carries `freeAccessExclusion` saying why it would be kept out, and it tells an exhausted allowance apart from a quota reading that never arrived or went stale, which used to look identical from the outside. Routing is unchanged: the listing reports, it never enforces. The separate `excludeTosAvoid` guard still drops its candidates without a reason; that gap is now documented rather than closed ([#12319](https://github.com/diegosouzapw/OmniRoute/pull/12319)) — thanks @maxmad64bis +- **feat(radar):** The Radar catalog table now shows two facts it was already receiving from the feed and dropping on the floor: the per-model rate limits (requests and tokens, per minute and per day) in a new column, and a badge when a provider's terms state it may train on the prompts you send. A limit of zero renders as zero rather than "rate-only" — for a ceiling those are opposite facts — and a model with no training statement gets no badge, because an absent statement is not a guarantee ([#12320](https://github.com/diegosouzapw/OmniRoute/pull/12320)) — thanks @maxmad64bis +- **feat(radar):** explain Community, single-use, contributor, supporter, recovery, abuse, offers, and privacy rules before either Radar activation action, and remove the superseded fixed-PR grant promise from every UI locale ([#12342](https://github.com/diegosouzapw/OmniRoute/pull/12342)) +- **feat(gamification):** the dashboard Profile page now shows the real daily streak — `/api/gamification/level` returns `streak: { current, longest }` (per key with `apiKeyId`, operator-wide maximum otherwise) and the streak card reads it instead of a hard-coded 0 (#2403) ([#12377](https://github.com/diegosouzapw/OmniRoute/pull/12377)) — thanks @pacocartones +- **feat(gamification):** the dashboard leaderboard now shows each API key's display name under the Name column instead of a truncated key id; `GET /api/gamification/leaderboard` attaches `name` per entry (name only — no key material), while the shared ranking helper and the federation leaderboard stay id-only — thanks @pacocartones ([#12385](https://github.com/diegosouzapw/OmniRoute/pull/12385)) +- **feat(gamification):** enforce the documented 1000 XP/min per-API-key anti-cheat rate limit on the XP award path; over-limit awards are logged and skipped instead of persisted, and the sliding window now matches the timestamp format stored in `xp_audit_log` ([#2403](https://github.com/diegosouzapw/OmniRoute/issues/2403)) ([#12390](https://github.com/diegosouzapw/OmniRoute/pull/12390)) — thanks @pacocartones +- **feat(admin):** localize the gamification anomalies page — the loading state, the Status column and the Suspicious badge now come from the `common` catalog (new `common.suspicious` key propagated to every locale) — add it to the Gamification sidebar group as `gamification-admin` (`/dashboard/gamification/admin`), and expose the loading and empty states as polite `role="status"` live regions (#12401 — thanks @pacocartones) +- **feat(providers):** skip GitHub combo members missing from the live synced catalog, and drop Copilot models that are policy-disabled or hidden from the model picker ([#12473](https://github.com/diegosouzapw/OmniRoute/pull/12473)) — thanks @RaviTharuma +- **feat(i18n):** locale-expansion tooling — `npm run i18n:add-locale` adds a language to config, dashboard, docs mirrors, CLI, README/indexes and site in one command; browser, CLI and cookie detection resolve `uk`, `fil`/`tl`, `zh-Hant` (and the retired legacy `in` → `id`) via config aliases; `config/i18n.json` now ships in the npm package so the published CLI can read it; new real-translation ratio gate (`npm run i18n:check-ratio`, advisory) with a per-locale ratchet baseline; `run-translation --adopt` restores `.i18n-state.json`; `sync-language-bars` generates 🌐 bars from config; `validate_translation.py` loads its allowlist again. Retires the duplicate `in` locale (Indonesian mislabelled as Hindi) — 42 honest locales; saved `NEXT_LOCALE=in` / `OMNIROUTE_LANG=in` keep working. (#12496) +- **feat(api):** Emit gateway-measured `tokens_per_second` (TTFT excluded) on streaming usage and `X-OmniRoute-Tokens-Per-Second` when first-token latency is known ([#12616](https://github.com/diegosouzapw/OmniRoute/issues/12616)) ([#12631](https://github.com/diegosouzapw/OmniRoute/pull/12631)) — thanks @RaviTharuma +- **feat(opencode-plugin): map gateway cost/usage/tok/s onto OpenCode inference payloads** — the official plugin copies `X-OmniRoute-Response-Cost`, token counts, `X-OmniRoute-Tokens-Per-Second` / `usage.tokens_per_second`, TTFT, and the winning `X-OmniRoute-Model` onto the JSON/SSE body OpenCode already consumes. Missing tok/s is left unset (never `tokens / latency`). (#12636) — thanks @RaviTharuma +- **feat(combo):** add combo strategy `quota-weighted`: skip exhausted accounts, then weighted-draw by leftover / in-flight load; existing conversations stay pinned until the account is empty ([#12789](https://github.com/diegosouzapw/OmniRoute/pull/12789)) — thanks @HouMinXi +- **feat(models):** Account live listings become the chat catalog source for Claude, Codex, Copilot, and AGY; public metadata only fills prices on IDs those accounts already list. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **fix(models):** Union `agy` and `antigravity` live catalogs so `agy/gemini-3.8-flash-high` is not rejected after the prefix folds to `antigravity`. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **feat(opencode):** Add `@omniroute/opencode-plugin-v2`, an opencode v2 plugin that gives a v2 install a route to an OmniRoute gateway: it publishes models, combos and auto-combos into the host catalog, refreshes them lazily behind a 300s TTL, and keeps serving the last known catalog from an on-disk snapshot when the gateway is unreachable. The v1 plugin (`@omniroute/opencode-plugin`) is untouched and keeps working as before. The plugin adapts to the host instead of assuming one shape: it reads the shape the host seeds into the catalog draft and publishes the `api` block, the pre-`api` top-level fields, or both. The gateway key comes from the host credential store when one is connected, so no secret needs to sit in a config file; a source the gateway refuses is named in a warning instead of silently degrading the catalog to raw model ids; and tool schemas bound for a Gemini model are stripped of the JSON-Schema keywords Gemini rejects, so tool calling survives that route ([#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870)) — thanks @maxmad64bis +- feat(services): show sanitized CLIProxyAPI account health from its authenticated management API without exposing credentials, file paths, or raw account metadata (#6342) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add AnySearch free web search + URL extract (webFetch) with typed results, credential validation, REST routing, and MCP selection - fallback-only ([#11690](https://github.com/diegosouzapw/OmniRoute/pull/11690)) — thanks @Xxx91n +- **feat(dashboard):** display clamped `[0, 100]%` cached input token ratio in request logs table ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) — thanks @b3nw +- **feat(catalog):** add `OMNIROUTE_DISABLE_THINKING_LEVEL_VARIANTS` feature flag to optionally filter out thinking level variants from model catalog ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) — thanks @b3nw +- **feat(dashboard):** continuously export call logs to external analytics stores. A pluggable destination registry ships the full Logs-tab record set on an hourly `JobRegistry` cron, with a persisted per-destination cursor, batched inserts, a config UI rendered from each destination's own field descriptors, and a REST layer (`/api/log-export/*`) for CRUD, a connection test, and an on-demand run. A destination can opt into `includeBodies` to also ship the request and response payloads shown in the Logs detail pane, including the client and provider views of each call; this is off by default, and payloads inherit the dashboard's PII sanitisation, secret redaction and `noLog` handling. Google BigQuery is the first destination, using a service-account key stored encrypted at rest and streaming inserts keyed by call-log id, into a table that is day-partitioned on `timestamp` and clustered on `api_key_name`, `provider`, `model` and `status`. ([#11945](https://github.com/diegosouzapw/OmniRoute/pull/11945)) — thanks @dpozimski +- **feat(providers):** add MaxAI as a signed, OpenAI-compatible provider serving its 13 paid chat models (GPT-5.6 / Luna / Thinking, Claude 5 Sonnet, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite, Grok 4.1-fast / 4.5, DeepSeek V3.2 / R1, Llama 3.3 70B) through OmniRoute's `/v1` endpoint, with per-request HMAC-SHA1→SM3→AES request signing, live model + context-window discovery from `/models/get_config`, and prompted tool-calling translated to OpenAI `tool_calls` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI vision input — image_url content parts are forwarded inline in `message_content` to the 6 vision-capable models (GPT-5.6 / Luna / Thinking, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite) ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI image generation — 6 image models (gpt-image-1, dall-e-3, flux-1-schnell/dev/pro, sd3-medium) exposed through `POST /v1/images/generations` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI document RAG — inline base64 file/document attachments are uploaded to MaxAI (content-addressed `doc_id`) and attached to the chat via `doc_list` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** browserless MaxAI onboarding — email device-pair login (`/api/providers/[id]/login`) and signed access-token refresh, so a connection can be created and kept fresh without a real browser or Google OAuth ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** per-provider TLS impersonation profile (MaxAI presents a Windows Firefox-150 client fingerprint) so its bot-sensitive endpoints accept OmniRoute traffic ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** add **Nimble** as a web-search and web-fetch provider (`nimble-search`) — `/v1/search` routes to Nimble's search API at `lite` depth (locale, freshness and include/exclude domain filters mapped onto the shared request contract), and `/v1/web/fetch` routes to Nimble Extract, which covers all four fetch formats (`markdown`, `html`, `links`, `screenshot`) from a single call. One API key serves both surfaces. ([#11620](https://github.com/diegosouzapw/OmniRoute/pull/11620)) — thanks @wildcard +- The npm publish is automatic again, through npm Trusted Publishing (OIDC): the hosted `stage-npm` job publishes with a short-lived credential minted from GitHub's id-token — no `NPM_TOKEN`, no 2FA prompt, provenance attached. `publish_mode=staged` (owner approves with 2FA) and `direct` (token) remain available on `workflow_dispatch`. ([#11931](https://github.com/diegosouzapw/OmniRoute/pull/11931)) +- **feat(providers):** Add **Opper** as an API-key gateway provider — EU-hosted AI gateway with 700+ models from 30+ providers behind one OpenAI-compatible API and one key (`OPPER_API_KEY`); model ids use `provider/model` format (e.g. `anthropic/claude-sonnet-4-6`, `openai/gpt-5`); live model catalog at `https://api.opper.ai/v3/compat/models`; entry mirrors `requesty` (same shape, `passthroughModels: true`, no static seed) ([#11629](https://github.com/diegosouzapw/OmniRoute/pull/11629)) +- **feat(dashboard):** the `/dashboard/orchestration` snapshot hook now subscribes to the `agents` WebSocket channel (`agent.task.updated`) instead of `requests` as its refetch trigger, and relaxes its background poll from 5s to 30s while that WS connection is up — falling back to the tighter 5s cadence, reprogrammed live on any connect/disconnect transition, whenever the socket is down. ([#12409](https://github.com/diegosouzapw/OmniRoute/pull/12409)) +- **feat(dashboard):** Orchestration canvas quick wins — search box plus state/source/provider filter chips with a one-click clear, and per-source collapse/expand, all reflected in the URL so a filtered/collapsed view is shareable and survives a refresh; the detail drawer gained a "copy trace JSON" action and hardened error/empty-state and accessibility handling; the Agents-tab edges now animate traveling particles along active (running) connections; and the canvas node/edge status colors moved off fixed hex values onto theme-aware `--orch-status-*` CSS custom properties, so they adapt correctly to light/dark mode. ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- **chore(dashboard):** Orchestration UI hardening pass and the missing component/model test coverage it called for — `OrchestratorNode`/`ActivityNode`/`OverflowNode` rendering, the `?node=`/overflow-click page routing, the Agents-tab orchestrator-click no-op and `showCompleted` toggle, and the overview kanban's done-column sort order (#12270, #12271). ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- New `/dashboard/orchestration` page: live unified view of everything running — Cloud Agent, A2A and Conductor as a real-time graph (Agents tab), the combo cascade (Routing tab, reusing the Combo Live Studio) and a state kanban (Overview tab), with a detail drawer (trace, cost, approve/cancel). Read-only over existing APIs — no new backend. Canvas concept credit: PR #11815 design ([#12261](https://github.com/diegosouzapw/OmniRoute/pull/12261)) +- **feat(dashboard):** new "History" tab on `/dashboard/orchestration` — an Airflow-style grid of finished runs over a 24h/7d/30d preset window, one row per (source, identity), clicking a cell opens the existing detail drawer. It is backed by real persistence: A2A task lifecycle transitions are now written to the `a2a_tasks` table (purged after 30 days, configurable via `OMNIROUTE_A2A_HISTORY_RETENTION_DAYS`) and served by the new `GET /api/a2a/tasks/history` listing endpoint, with the task-detail route falling back to persisted history once a run leaves the in-memory snapshot. Conductor runs stay remote and are not persisted locally — the tab says so instead of silently omitting them. ([#12479](https://github.com/diegosouzapw/OmniRoute/pull/12479)) +- **feat(dashboard):** the orchestration detail drawer gained a "Repeat" action for Cloud Agent, A2A and Conductor tasks — a two-click confirm (click once to arm, click again within the confirm window to fire) re-submits the original prompt/input as a new run. The button is disabled with an explanatory tooltip whenever the original input can't be recovered from the loaded task detail (e.g. it never carried a prompt, or the detail failed to load). Two limitations of the A2A variant, by design: it targets the `/a2a` JSON-RPC endpoint, which authenticates with an API key only (`REQUIRE_API_KEY=true` or a configured `OMNIROUTE_API_KEY` makes a dashboard-session repeat answer `HTTP 400` — surfaced verbatim in the drawer's error line, never as a success), and the `message/send` call is SYNCHRONOUS: the POST blocks for the whole skill run, so the success confirmation only appears once the repeated task finishes. A dashboard-authenticated A2A creation path is deliberately left to a follow-up — widening the endpoint's auth posture is an operator decision, not a side effect of this feature. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(a2a):** A2A task execution now records which memories were consulted for the task's last user message as `metadata.memoryHits` (id/key/type/content-snippet) plus a `memory_hits` history event, purely for observability — the retrieved memory is never injected into a skill's prompt or behavior. Gated by the `OMNIROUTE_A2A_MEMORY_HITS` kill-switch (default enabled; set to `0` to skip the recall lookup entirely). The drawer's new "Memory used" section lists these hits for a2a tasks and is omitted whenever there are none. Known limitation: recall only resolves under the keyless posture — a keyed caller's task owner is a SHA-256 prefix of the API key, while memory rows are keyed by the database api-key id, and no hash→id lookup exists today, so the hit list stays empty for keyed callers. The recorded hits are also kept out of the task's own `input` (and therefore out of the persisted input and of the "Repeat" request body), so repeating a task never re-sends the previous run's memory snippets. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(providers):** add a Perplexity Agent API provider (`perplexity-agent` / `pplx-agent`) for Perplexity `/v1/responses`, including the documented Anthropic, OpenAI, Google, xAI, DeepSeek, Z.AI, Moonshot/Kimi, NVIDIA, and Perplexity model IDs plus Anthropic-model `max_output_tokens` compatibility. ([#12103](https://github.com/diegosouzapw/OmniRoute/pull/12103)) — thanks @AIB1TAL0S +- **feat(providers):** add RPD (Requests Per Day) limit to provider rate limit overrides across UI, schemas, DB, and i18n ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) — thanks @b3nw +- **feat(providers): add UC Direct (uncensored.com Developer API), the metered OpenAI-compatible surface.** A standard OpenAI-compatible passthrough (default executor) for uncensored.com's official REST API at `https://api.uncensored.com/api/v1`: `X-api-key` auth (never-expiring `uai_sk_live_` key), `POST /chat/completions` with streaming SSE and native tool-calling, and the full live metered catalog (82 models across 15 providers, discovered from the public `GET /v1/models`). Registered as provider `uc-direct` (alias `ucd`). Complements the un-metered `uc` persona provider — same models, metered credits and a plain API key instead of a subscription session. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(providers): add UC (uncensored.com), the un-metered subscription "persona" chat as an OpenAI-compatible provider.** A WebSocket web-app port: a durable Clerk credential mints a short-lived session token per connect (browserless — no API key), driving UC's persona socket. Ships the browserless email-code login (request → verify → harvest), the 19 verified persona models (Claude Opus, Gemini, Grok, GLM, Kimi, DeepSeek, MiniMax, incl. the uncensored variants), prompted `` tool-calling with a per-model code-style dialect + auto-cure retry for guardrailed models, live ``/reasoning split, streaming + non-streaming OpenAI responses, and full quota/auth error surfacing (paywall / message-limit / rate-limit → 429, invalid session → 401 re-login). Full multimodal parity via the persona blob-upload layer: **vision** (image input, 15 vision-capable models), **document RAG** (PDF/doc upload, server-side extraction), **image generation** (22 models), **video generation** (14 models, async signed-url → poll), and **TTS** (streaming MP3). Registered as provider `uc` (alias `ucn`). The metered OpenAI-compatible Developer API is a separate `uc-direct` provider. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(logs):** add copy-all button to request detail modal ([#11083](https://github.com/diegosouzapw/OmniRoute/pull/11083)) — thanks @AndrianBalanescu +- **feat(check):** make serviceKinds required and add the reverse-walk provider consistency gate ([#11392](https://github.com/diegosouzapw/OmniRoute/pull/11392)) — thanks @Tushar49 +- **feat(providers):** only advertise verified-working connections by default ([#11448](https://github.com/diegosouzapw/OmniRoute/pull/11448)) — thanks @alltomatos +- **feat(resilience):** add hierarchical concurrency admission ([#11493](https://github.com/diegosouzapw/OmniRoute/pull/11493)) +- **feat(providers):** derive + surface expiry for JWT-bearing web cookies (#11497) ([#11505](https://github.com/diegosouzapw/OmniRoute/pull/11505)) — thanks @oyi77 +- **feat(tunnels):** support named cloudflared tunnels via CLOUDFLARED_CONFIG ([#11561](https://github.com/diegosouzapw/OmniRoute/pull/11561)) — thanks @ggiak +- **feat(compression):** make proactive context-compression threshold a live setting ([#11564](https://github.com/diegosouzapw/OmniRoute/pull/11564)) — thanks @ragnar-claude +- **feat(resilience):** add provider peak-hour protection ([#11622](https://github.com/diegosouzapw/OmniRoute/pull/11622)) — thanks @Neuron-Mr-White +- **feat(providers):** add 1min.ai provider ([#11631](https://github.com/diegosouzapw/OmniRoute/pull/11631)) — thanks @zero-executioner +- **feat(dashboard):** collapsible JSON tree viewer for request/response payloads ([#11703](https://github.com/diegosouzapw/OmniRoute/pull/11703)) — thanks @hartmark +- **feat(routing):** add LiquidAI LFM2.5-2.6B free tier via OpenRouter ([#11752](https://github.com/diegosouzapw/OmniRoute/pull/11752)) — thanks @brick30llc-ctrl +- **feat(pricing):** add GLM-5.3-Flash pricing, model specs, and catalog registration ([#11830](https://github.com/diegosouzapw/OmniRoute/pull/11830)) — thanks @AndrianBalanescu +- **feat(routing):** add relayMode for schema-locked context handoffs ([#11839](https://github.com/diegosouzapw/OmniRoute/pull/11839)) — thanks @raheemuddin786 +- **feat(config):** add support for runtime system prompt configuration and hot-reloading ([#11841](https://github.com/diegosouzapw/OmniRoute/pull/11841)) — thanks @raheemuddin786 +- **feat(routing):** expand connection-aware quota prefilter across combo strategies (#11682) ([#11850](https://github.com/diegosouzapw/OmniRoute/pull/11850)) — thanks @HouMinXi +- **feat(models):** reactive discovery sync after Antigravity model-not-found 404 ([#11888](https://github.com/diegosouzapw/OmniRoute/pull/11888)) — thanks @MumuTW +- **feat(models):** auto-aliases and token limits from synced Antigravity discovery ([#11889](https://github.com/diegosouzapw/OmniRoute/pull/11889)) — thanks @MumuTW +- **feat(settings):** raise sticky round-robin limit caps to 1000 ([#12015](https://github.com/diegosouzapw/OmniRoute/pull/12015)) — thanks @adivekar-utexas +- **feat(resilience):** operator-configurable global credential health check interval ([#12043](https://github.com/diegosouzapw/OmniRoute/pull/12043)) — thanks @adivekar-utexas +- **feat(quota):** use official OpenCode Go usage API ([#12124](https://github.com/diegosouzapw/OmniRoute/pull/12124)) — thanks @ddarkr +- **feat(resilience):** default the credential health check sweep to 60 minutes ([#12138](https://github.com/diegosouzapw/OmniRoute/pull/12138)) — thanks @adivekar-utexas +- **feat(quality):** new-code mode for the complexity and dead-code ratchets (Clean as You Code) ([#12142](https://github.com/diegosouzapw/OmniRoute/pull/12142)) +- **feat(dashboard):** orchestration canvas — unified model + snapshot hook (part 1/2) ([#12156](https://github.com/diegosouzapw/OmniRoute/pull/12156)) +- **feat(usage):** add Kilo Code balance and Kilo Pass quotas ([#12178](https://github.com/diegosouzapw/OmniRoute/pull/12178)) — thanks @killer30001000 +- **feat(providers):** refresh vendored ChatGPT Web connector to v4.0.7 ([#12181](https://github.com/diegosouzapw/OmniRoute/pull/12181)) — thanks @backryun +- **feat(providers):** manual "Clear cooldown" action in the cooling panel ([#12224](https://github.com/diegosouzapw/OmniRoute/pull/12224)) — thanks @patrykkopycinski +- **feat(providers):** restore ChatGPT Web via clean-room browser transport ([#12239](https://github.com/diegosouzapw/OmniRoute/pull/12239)) — thanks @backryun +- **feat(sse):** wire the PROVIDER_PROFILES window gate into the global provider cooldown ([#12247](https://github.com/diegosouzapw/OmniRoute/pull/12247)) +- **feat(providers):** modernize CLOVA Studio chat and embeddings ([#12277](https://github.com/diegosouzapw/OmniRoute/pull/12277)) — thanks @backryun +- **feat(browser):** adopt Obscura as primary headless browser engine with Chromium fallback ([#12286](https://github.com/diegosouzapw/OmniRoute/pull/12286)) — thanks @opensource-elearning +- **feat(sse):** full language parity for output styles (es/de/fr/it/ru/zh + autoDetect) ([#12289](https://github.com/diegosouzapw/OmniRoute/pull/12289)) +- **feat(quality):** complete test:scoped — --full map rebuild, stdin selection, CI loader parity (#8084 D1) ([#12353](https://github.com/diegosouzapw/OmniRoute/pull/12353)) +- **feat(providers):** refresh Fable, Cursor, and Devin catalogs ([#12367](https://github.com/diegosouzapw/OmniRoute/pull/12367)) — thanks @backryun +- **feat(video):** redact transcript text from logs and durable memory (#12150 P1) ([#12427](https://github.com/diegosouzapw/OmniRoute/pull/12427)) +- **feat(dashboard):** parent-link, genuine-continuation badge, and modal perf fixes ([#12448](https://github.com/diegosouzapw/OmniRoute/pull/12448)) — thanks @hartmark +- **feat(dashboard):** adaptive context-budget dial on compression panel ([#12488](https://github.com/diegosouzapw/OmniRoute/pull/12488)) — thanks @HouMinXi +- **feat(providers):** refresh Z.ai Web models and browser transport ([#12524](https://github.com/diegosouzapw/OmniRoute/pull/12524)) — thanks @backryun +- **feat(video):** redact raw client-snapshot transcript fields in the detailed log (#12150 P2) ([#12528](https://github.com/diegosouzapw/OmniRoute/pull/12528)) +- **feat(providers):** refresh NVIDIA hosted models ([#12538](https://github.com/diegosouzapw/OmniRoute/pull/12538)) — thanks @backryun +- **feat(quota):** Moonshot Open Platform balance and TPD lock for custom nodes ([#12590](https://github.com/diegosouzapw/OmniRoute/pull/12590)) — thanks @HouMinXi +- **feat(video):** redact transcript in the in-memory pending-request snapshot (#12430 item 6) ([#12596](https://github.com/diegosouzapw/OmniRoute/pull/12596)) +- **feat(dashboard):** link a log entry's Conversation Context to its owning conversation ([#12646](https://github.com/diegosouzapw/OmniRoute/pull/12646)) — thanks @hartmark +- **feat(catalog):** eligibility-gated free-tier bucket ([#12669](https://github.com/diegosouzapw/OmniRoute/pull/12669)) +- **feat(db):** fail-closed previous_response_id continuation for redacted video turns (#12150 P2b) ([#12707](https://github.com/diegosouzapw/OmniRoute/pull/12707)) +- **feat(gamification): pay the documented `streak_bonus` and `badge_unlock` XP rewards.** `XP_REWARDS` listed both rewards but the award pipeline never paid them: the private reward table in `events.ts` omitted them, `updateStreak()` did not report when a streak extended, and badge unlocks carried no XP. Every request that extends a daily streak now pays `streak_bonus × streak length` once per UTC day (guarded by a same-day `xp_audit_log` check), and every badge unlocked through the pipeline pays `badge_unlock` once per badge (guarded by the `user_badges` primary key; `unlockBadge()` now reports whether it inserted). Bonus XP flows through the same `addXp` + level sync + global/weekly/monthly leaderboard path as action XP, so level-ups and rankings include it. The Radar supporter recognition unlock stays XP-free. (#12522 — thanks @pacocartones) +- **feat(providers):** advertise a `free-tier` capability in the provider plugin manifest for every provider with documented free models, so sidecars and dashboards can filter free-capable providers without reading the quota catalog ([#12786](https://github.com/diegosouzapw/OmniRoute/pull/12786)) — thanks @maxmad64bis +- **feat(providers):** Added EURouter as an OpenAI-compatible API-key gateway (`https://api.eurouter.ai/v1`), with live model discovery via `passthroughModels`. Its copy states that models are served by third-party upstreams listed per model, so an EU-based router is not read as EU data residency for inference. ([#13025](https://github.com/diegosouzapw/OmniRoute/pull/13025)) — thanks @ntdatt812 +- **feat(providers):** Added GreenPT as an OpenAI-compatible API-key provider (`https://api.greenpt.ai/v1`), with live model discovery via `passthroughModels`. No free-inference badge: the published docs describe a free API subscription billed per token, not a free tier. ([#13024](https://github.com/diegosouzapw/OmniRoute/pull/13024)) — thanks @ntdatt812 +- **feat(i18n):** 9 new locales — Greek (`el`), Croatian (`hr`), Serbian (`sr`), Lithuanian (`lt`), Estonian (`et`), Latvian (`lv`), Slovenian (`sl`), Maltese (`mt`), Irish (`ga`) — across the dashboard, docs mirrors, CLI, README and the site; OmniRoute now ships every official EU language (51 locales). (#13044) +- **feat(i18n):** 8 new locales — Kannada (`kn`), Malayalam (`ml`), Odia (`or`), Punjabi (`pa`), Nepali (`ne`), Sinhala (`si`), Burmese (`my`), Khmer (`km`) — across the dashboard, docs mirrors, CLI, README and the site (59 locales). The translator now restores the ICU literal escape around angle placeholders and splits oversized docs sections before translating. (#0000) ([#13660](https://github.com/diegosouzapw/OmniRoute/pull/13660)) +- feat(providers): **list Agnes 3.0 Flash as the current free chat model, drop retired 1.5 Flash, add Image 2.0/2.5 Flash plus Video 2.5/2.5 Flash, and discover the live `/v1/models` catalog (including the CN host `api.agnes-ai.cn`).** `agnes-1.5-flash` now forwards to `agnes-3.0-flash`. Video 2.5 polls `GET /v1/videos/{id}` (not the V2.0 `/agnesapi` contract). Live `/v1/models` (2026-09-09) no longer serves 1.5; the wiki marks it deprecated. 3.0 Flash is 512K context / 65,536 max output, same window as 2.5. CN-region keys use the existing per-connection base-URL field, default stays `apihub.agnes-ai.com`. ([#13120](https://github.com/diegosouzapw/OmniRoute/pull/13120)) — thanks @HouMinXi +- **feat(sse):** Codex and OpenAI catalogs list GPT-6 Astra with effort aliases (`-low` through `-ultra`); Codex CLI identity pins `@openai/codex@0.153.4` in lockstep with the image ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **feat(dashboard):** the orchestration History tab gained a "Compare runs" mode — toggling it turns each grid cell into a 2-item selection queue (a 3rd click drops the oldest pick), and picking two cells opens a side-by-side comparison panel instead of the usual detail drawer. The panel fetches both runs' detail the same way the drawer does (falling back to persisted history once a run leaves the live TTL window) and shows, per side: identity/source/state, start time, a signed `right - left` delta for duration/cost/event count, the event timeline aligned by index, and any memory hits. One side's fetch failing never blocks the other, and a delta is only ever computed when both sides have a finite value — otherwise it renders "—", never `NaN`. Comparing two runs from different sources or skills still works; a banner marks the deltas as informational rather than hiding them, since the two runs aren't a strict apples-to-apples pair. ([#12677](https://github.com/diegosouzapw/OmniRoute/pull/12677)) +- **feat(dashboard):** badge a conversation that never reached a clean stop ([#12717](https://github.com/diegosouzapw/OmniRoute/pull/12717)) — thanks @hartmark +- **feat(release):** reconcile-changelog tool + version-anchored fragment aggregation ([#12987](https://github.com/diegosouzapw/OmniRoute/pull/12987)) +- **feat(mcp):** carry context_length through MCP list_models_catalog projection (#12776) ([#13406](https://github.com/diegosouzapw/OmniRoute/pull/13406)) — thanks @KooshaPari +- **feat(cli):** make startup readiness budget configurable (#13369) ([#13433](https://github.com/diegosouzapw/OmniRoute/pull/13433)) — thanks @KooshaPari + ### 🐛 Bug Fixes +- **security(streaming):** sanitize generic mid-stream error messages before emitting OpenAI, + Responses, or Claude SSE failure frames and before diagnostic logging, while preserving raw + failures for internal classification and keeping client disconnects out of provider failure state. ([#12457](https://github.com/diegosouzapw/OmniRoute/pull/12457)) +- **fix(claude):** `blockExtraUsage: false` no longer lets 5h quota preflight skip the account; extra usage is billed after the session bar is gone, so the request must reach Anthropic ([#12803](https://github.com/diegosouzapw/OmniRoute/pull/12803)) — thanks @HouMinXi +- Sanitize HuggingChat conversation-creation and message-send transport failures before they reach client error bodies or provider logs. ([#12467](https://github.com/diegosouzapw/OmniRoute/pull/12467)) +- **fix(dashboard):** Keep local and theme-aware provider SVG icons at a definite layout size so Chromium does not collapse them to 0×0 after the v3.8.50 image-rendering change ([#12054](https://github.com/diegosouzapw/OmniRoute/pull/12054)) — thanks @ponkcore +- **fix(security):** Sanitize provider and runtime failures before public API, SSE and MCP responses and before persistent request, proxy and usage logs, preventing credentials, stack traces and host filesystem paths from crossing those boundaries while preserving stable error codes and useful diagnostics. ([#12506](https://github.com/diegosouzapw/OmniRoute/pull/12506)) +- **fix(cli):** `omniroute tunnel create` no longer crashes with `Cannot read properties of undefined (reading optsWithGlobals)` — removed the duplicate positional argument that caused Commander.js to misalign the action callback parameters ([#12295](https://github.com/diegosouzapw/OmniRoute/issues/12295)) ([#12368](https://github.com/diegosouzapw/OmniRoute/pull/12368)) — thanks @gonisulaimann +- Stop advertising Gemini Live-only models as supported audio endpoints until OmniRoute proxies the bidirectional Live protocol. ([#11710](https://github.com/diegosouzapw/OmniRoute/pull/11710)) — thanks @RaviTharuma +- **fix(kie):** reroute `flux/kontext` off the KIE Market `createTask` flow — it is catalogued with `isMarket: true` but has no Market catalog page, so KIE rejected it with "model name not supported"; it now hits the dedicated `POST /api/v1/flux/kontext/generate` / `GET /api/v1/flux/kontext/record-info` endpoints instead (#11296). ([#11985](https://github.com/diegosouzapw/OmniRoute/pull/11985)) — thanks @hartmark +- **fix(kie):** correct 12 more KIE Market catalog ids that were sent to `createTask` unchanged but diverge from KIE's documented upstream `model` values — GPT Image 2 T2I/I2I (drops the `gpt/` prefix), GPT Image 1.5 T2I/I2I (`gpt-image/` namespace), Seedream 5.0 Lite T2I/I2I (drops the `.0`), all 4 Flux 2 variants (`flux-2/` namespace, generic variant renamed `flex`), and Wan 2.7 Image / Image Pro (dash instead of dot) — each verified individually against the literal example request published on docs.kie.ai. `#11326`'s "everything else already matches" claim was wrong a second time (#11296); `z-image/4.0-*`/`z-image/4.5-*` and `flux/kontext` remain open, documented as unresolved in `KIE_MARKET_UPSTREAM_MODEL_IDS`'s comment pending further verification. ([#11667](https://github.com/diegosouzapw/OmniRoute/pull/11667)) +- **fix(dashboard):** `useApiKeySave.handleSaveApiKey` no longer forces a full upstream `/models` catalog sync on every non-curated provider connection save — callers can now pass `skipModelSync: true` to opt out, so a workflow that only wants to add one manual model no longer floods the provider's available-models list with hundreds/thousands of synced entries. The flag is a client-side intent signal only and is stripped before the connection payload is POSTed to `/api/providers`; default behavior (full sync on save) is unchanged when the flag is omitted (#11324) ([#11664](https://github.com/diegosouzapw/OmniRoute/pull/11664)) +- **Provider connections:** keep `tokenExpiresAt` when a connection is created. The create-path allowlist omitted it, so every insert stored NULL and the dashboard token badge could read a fresh connection as expired until its first background refresh ([#11368](https://github.com/diegosouzapw/OmniRoute/pull/11368)). — thanks @ntdatt812 +- **fix(combos):** the combo builder's precision-select, global-model-search, and manual-entry flows now serialize a model step's `model` string using the provider's already-computed routing-alias prefix (e.g. `oc/`) instead of rebuilding it from the raw canonical `providerId`, fixing the no-auth "OpenCode Free" provider (`opencode`) being routed to the unrelated paid "OpenCode Zen" provider (`opencode-zen`) because `opencode` doubles as a manual routing-prefix override ([#11433](https://github.com/diegosouzapw/OmniRoute/issues/11433)). ([#11670](https://github.com/diegosouzapw/OmniRoute/pull/11670)) +- **fix(radar):** the catalog feed cache now keeps `generatedAt`, the date the feed's data was built, next to `fetchedAt`, the date this install downloaded it (#11435). The feed schema requires that date and the sync path validates it, but the cache dropped it — so a feed fetched minutes ago and one carrying weeks-old figures looked identical to everything downstream, including the dashboard's "Last fetched" line. `getRadarCatalog().meta` and `GET /api/radar/status` now report both dates, the latter as its own field rather than folded into `version` — and omitted entirely for the offers and intel caches, which keep no build date, where a `null` would read as "unknown" rather than "never stored". The dashboard still shows only the fetch time; surfacing the build date there needs a new translated label and is left to a follow-up. Rows cached before migration 163 read back as `null`: unknown stays unknown instead of borrowing the fetch time. The referrals cache has persisted the same date since migration 142. — thanks @maxmad64bis +- **fix(cli):** postinstall no longer fills `JWT_SECRET` and `API_KEY_SECRET` in the installed package's `.env` (#11436). `.env.example` ships both blank on purpose: the server restores them from its durable store, or generates and persists them there, in `ensureSecrets()`. Pre-filling them defeated that — the file lives inside the package directory, so `npm i -g` replaced it and postinstall wrote _different_ values, while `ensureSecrets()` (which only acts on an empty variable) never got to restore the real ones. Both secrets rotated silently on every update, invalidating dashboard sessions and API-key CRCs. `STORAGE_ENCRYPTION_KEY` left the same list for the same reason in #1622; its comment pointed at a function that no longer exists and now names the real provisioning path. — thanks @maxmad64bis +- **fix(cli):** the CLI now says when a loaded `.env` lives inside the installed package directory (#11437). It already announces every env file it reads, without distinguishing the ones that survive an update from the one that does not: `npm i -g` replaces the package directory wholesale, so values set there are gone at the next update, silently. The warning names the durable path to move them to, and fires only when that file actually supplied a value — a file entirely shadowed by a durable one supplied nothing. A development checkout stays silent: there the same path is stable and documented in `SETUP_GUIDE.md`. — thanks @maxmad64bis +- **fix(free-models):** the shared `isFreeModel()` predicate no longer reports catalog entries marked `freeType: "discontinued"` as free, so `hidePaidModels` can't route to Pollinations' seven premium models that now require a paid key ([#11441](https://github.com/diegosouzapw/OmniRoute/pull/11441)) — thanks @maxmad64bis +- fix(ui): let AnySearch use the normal provider-icon fallback when LobeHub has no matching icon (#11449) +- Fixed the v3.8.50 Costs and Analytics dashboards so flat-rate Claude Code usage can be shown as an explicitly requested token-price estimate without changing default billed-cost semantics. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed archived usage retention so each request is priced individually instead of pricing a day's summed tokens once, which understated archived cost whenever a day mixed cache-heavy and ordinary requests. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed the Costs dashboard so it discloses when displayed figures include flat-rate token-price estimates instead of labelling them as billed spend, using the flag the analytics API already returns; the month-end projection and the CSV/JSON exports carry the same marker, and billed-cost mode is unchanged. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- **fix(combo):** attach the same combo-diagnostics trace (`poolSize`/`attemptOrder`/`excluded`/`terminalReason`, plus `x-omniroute-combo-*` headers) to the round-robin strategy's and the nested pipeline/fusion runtime-unit loop's "Maximum combo retry limit reached" 503 that the priority-strategy path already attaches for the identical terminal condition — previously those two paths returned a bare, contextless 503 ([#11462](https://github.com/diegosouzapw/OmniRoute/issues/11462)). ([#11665](https://github.com/diegosouzapw/OmniRoute/pull/11665)) +- **fix(bun):** use native `bun:sqlite` in `bootstrap-env` and `sync-env` to avoid loading `better-sqlite3` N-API addon during Bun startup ([#11468](https://github.com/diegosouzapw/OmniRoute/pull/11468)) — thanks @TheDemonTuan +- **fix(docker-bun):** make `Dockerfile.bun` install reliable with pre-install script helpers and native `bun:sqlite` smoke check ([#11470](https://github.com/diegosouzapw/OmniRoute/pull/11470)) — thanks @TheDemonTuan +- **fix(docker-bun):** remove every vendored `better-sqlite3` native addon from the Bun runtime image so startup cannot abort before the `bun:sqlite` fallback ([#11482](https://github.com/diegosouzapw/OmniRoute/pull/11482)) — thanks @TheDemonTuan +- **fix(autoCombo):** effort/alias model variants (`gpt-5.6-sol-xhigh`, `gpt-5.6`, cursor's `claude-4.6-opus-high`) inherit their base model's task fitness instead of falling to the wildcard 0.5 ([#11492](https://github.com/diegosouzapw/OmniRoute/pull/11492)) — thanks @MumuTW +- fix(db): rate-limit repeated Arena ELO leaderboard fetch-failure warnings instead of logging one per sync attempt (#11500) ([#11989](https://github.com/diegosouzapw/OmniRoute/pull/11989)) — thanks @hartmark +- **fix(db):** dedupe the raw `[Encryption] Decryption failed...` log line emitted by the lazy-decrypt views (`createLazyRowProxy`/`createLazyConnectionView`), which power `getProviderConnections()` and were re-triggering that line on every CredentialHealth/model-sync cycle for the same corrupt or stale-key credential — a fresh Proxy over a fresh row on every cycle meant the per-proxy memoization never suppressed it, unlike the dedup `decryptConnectionFields()` already had since [#9927](https://github.com/diegosouzapw/OmniRoute/issues/9927). Now shares that dedupe tracking so the line logs at most once per credential ([#11500](https://github.com/diegosouzapw/OmniRoute/issues/11500)). ([#11678](https://github.com/diegosouzapw/OmniRoute/pull/11678)) +- **fix(intelligence):** Arena ELO sync stores a synthesized base-model row for every effort/harness variant (`claude-opus-5-max` → `claude-opus-5`, `gpt-5.6-sol-xhigh (codex-harness)` → `gpt-5.6-sol`), so bare model requests reach the synced intelligence layer instead of the static fallback table; the hand-maintained `MODEL_ALIAS_MAP` — which collapsed generations (`gpt-5.5` → `gpt-5`) — is gone ([#11506](https://github.com/diegosouzapw/OmniRoute/pull/11506)) — thanks @MumuTW +- **fix(autoCombo,sse):** catalog hygiene — the static task-fitness table no longer ranks retired models above live ones (dead rows dropped, patterns matched on segment boundaries), the 7 deprecation aliases that forwarded to retired or non-existent models now point at the vendors' published replacements, an alias is no longer rewritten when the serving provider offers the id as-is, and a new `check:model-lifecycle` gate diffs both tables against a vendor lifecycle snapshot ([#11507](https://github.com/diegosouzapw/OmniRoute/pull/11507)) — thanks @MumuTW +- **fix(sse):** stop dropping resolved `thoughtSignature` values on parallel (multi tool-call) turns sent to Gemini 3.x — the claude→gemini and openai→gemini translators previously kept the signature only on the *first* function call of a message, causing Gemini to reject subsequent calls in the same turn with HTTP 400 "Function call is missing a thought_signature"; each function call now keeps its own resolved signature ([#11510](https://github.com/diegosouzapw/OmniRoute/issues/11510)). ([#11669](https://github.com/diegosouzapw/OmniRoute/pull/11669)) +- Treat Docker publishing from a non-default release branch as an expected skip instead of a failed workflow, while preserving the guard that prevents it from overwriting the `next` channel. ([#11527](https://github.com/diegosouzapw/OmniRoute/pull/11527)) — thanks @pacocartones +- fix(search): prefer credentialed providers over duckduckgo-free fallback (#11524) — the fallback-only loop ran before the credentialed-providers loop in `executeWebSearch`, making configured providers unreachable when `duckduckgo-free` was available. ([#11565](https://github.com/diegosouzapw/OmniRoute/pull/11565)) — thanks @vsd2807 +- **fix(sse):** cap the upstream headers-wait phase for STREAMING requests to a client-realistic ceiling (110s, under Codex's own ~120s hard client-abort window) instead of the flat 10-minute `FETCH_TIMEOUT_MS` default — that default was 5x longer than the body-phase readiness watchdog's own adaptive bound, so a request whose upstream never returned any response at all (not even headers, e.g. a stalled NVIDIA target behind a tool-heavy Responses→Chat translation) kept the client connection alive on keepalives only, guaranteeing the client's own patience ran out first with an opaque 499 instead of OmniRoute detecting and failing the stall fast. Non-streaming requests are unaffected — they keep the existing flat default (`open-sse/utils/fetchStartTimeoutPolicy.ts`) (#11526) ([#11668](https://github.com/diegosouzapw/OmniRoute/pull/11668)) +- **fix(ci):** protocol-clients E2E harness boots the peer-stamped custom server (`run-next.mjs dev`) instead of the bare `next` CLI, so `LOCAL_ONLY` locality resolves from the real TCP peer stamp and `/api/mcp/audit` answers 200 (open bootstrap preserved via an `OMNIROUTE_E2E_BOOTSTRAP_MODE=open` credential clear in `run-next.mjs`, pinned to loopback `HOST`) instead of a deterministic 403 (#11535) ([#11549](https://github.com/diegosouzapw/OmniRoute/pull/11549)) — thanks @jonlwheat2-gif +- **fix(docs):** the Auto-Combo engine is described with the number of factors it actually declares. Four different counts were in circulation — 6, 9, 13 and 14 across `AGENTS.md`, four guides and six code comments — while `DEFAULT_WEIGHTS` declares 15. `check:docs-counts` now reads that number from the source, so adding a factor without updating the prose fails CI ([#11545](https://github.com/diegosouzapw/OmniRoute/pull/11545)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **fix(quota-share):** in-flight leases are per request, so an aborted request's slot ages out instead of being kept alive by later traffic on the same connection ([#11547](https://github.com/diegosouzapw/OmniRoute/pull/11547)) — thanks @abhisheksharma2411 +- The free-tier summary route now serves the refreshed Radar catalog when the feed is active — the same numbers the dashboard shows — and states which catalog answered plus its real build date, instead of always reporting release-frozen figures with a stale curation date. The supporter-key live feed stays reserved to authenticated callers of the instance. ([#11550](https://github.com/diegosouzapw/OmniRoute/pull/11550)) — thanks @maxmad64bis +- **sse:** fix LiveWS/embed-WS servers crashing at startup under the Node/tsx runtime — `liveServer.ts`, `embedWsProxy.ts` and `apiBridgeServer.ts` imported `@/shared/utils/httpClientAbortGuard` without the `.mjs` extension, so the client-abort crash guard added by [#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556) was unreachable and every dependent test failed with `ERR_MODULE_NOT_FOUND` ([#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556)). ([#11671](https://github.com/diegosouzapw/OmniRoute/pull/11671)) — thanks @jonlwheat2-gif / @yxyxy +- fix(chat): shed any of the matched provider-node's routing identifiers (public prefix or internal id) before model lookup, so composites like `//` no longer reach upstream verbatim (#11557) — thanks @maxmad64bis +- **fix(cache):** a semantic-cache entry now expires on its TTL instead of surviving until the next UTC midnight, and `dbEntries` stops counting expired rows ([#11573](https://github.com/diegosouzapw/OmniRoute/pull/11573)) — thanks @ntdatt812 +- **fix(api):** `/v1/models` schedules its stale-while-revalidate rebuild through Next's `after()` again, so a stale catalog reaches the client before the rebuild blocks the event loop ([#11574](https://github.com/diegosouzapw/OmniRoute/pull/11574)) — thanks @ntdatt812 +- **fix(check):** the `known-symbols` executor conformance gate awaits the now-async `getExecutor()`, so it stops reporting all 142 registered aliases as dead symbols and can detect a lazy import that fails ([#11582](https://github.com/diegosouzapw/OmniRoute/pull/11582)) — thanks @ntdatt812 +- **fix(pack):** register `bin/cli/utils/volatileEnvPath.mjs` in `PACK_ARTIFACT_REQUIRED_PATHS`, so the prune can no longer silently drop a module the published CLI imports on every boot ([#11588](https://github.com/diegosouzapw/OmniRoute/pull/11588)) — thanks @ntdatt812 +- **i18n(vi):** translate the five reliability-column strings on the free-provider rankings page, which rendered as `__MISSING__:` markers in the Vietnamese UI ([#11589](https://github.com/diegosouzapw/OmniRoute/pull/11589)) — thanks @ntdatt812 +- **resilience:** restore the expired-connection retry-budget probe in the token-health sweep — the `!isGitHubAccessTokenOnlyConnection` carve-out reintroduced by #11608 contradicted the boundary pinned by #11592, so a GitHub connection parked at `expired` with retry budget remaining was never probed and could never self-heal ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)). ([#11672](https://github.com/diegosouzapw/OmniRoute/pull/11672)) — thanks @jonlwheat2-gif / @ntdatt812 +- **fix(dashboard):** Provider Stats now displays combo success rates on their native 0–100 percentage scale instead of multiplying them twice ([#11595](https://github.com/diegosouzapw/OmniRoute/pull/11595)) — thanks @pacocartones +- **fix(providers):** route DuckDuckGo Web Chat through the verified `duck.ai` endpoint, refresh its free model catalog, and avoid forwarding unsolved anti-abuse challenges ([#11597](https://github.com/diegosouzapw/OmniRoute/pull/11597)) — thanks @solstxce +- **fix(ui):** Console log Refresh and Copy controls now expose localized accessible names, keep copy actions visible on keyboard focus, and announce copy completion safely ([#11599](https://github.com/diegosouzapw/OmniRoute/pull/11599)) — thanks @pacocartones +- Dropped the stale-`.eslintcache` `restore-keys` fallback from both "Restore ESLint file cache" steps in `ci.yml`, so the blocking `Lint` job can no longer be served per-file verdicts computed under a different lint config, suppressions file or lockfile. `quality.yml` had already dropped it in #11963; `ci.yml` — the workflow that actually gates PRs — had not (#11600). ([#11996](https://github.com/diegosouzapw/OmniRoute/pull/11996)) +- fix(ci): pass `--pass-on-unpruned-suppressions` in `run-eslint-json.mjs` so the CI Lint job no longer fails when a suppression is merely orphaned by a genuine fix, mirroring the identical fix already in `validate-release-green.mjs` (#11600) ([#11983](https://github.com/diegosouzapw/OmniRoute/pull/11983)) — thanks @hartmark +- fix(handoff): enforce provider allowlist for universal handoff (#11602) — universal handoff now skips summarization when the selected summary model's provider is not included in the configured provider allowlist. ([#11619](https://github.com/diegosouzapw/OmniRoute/pull/11619)) — thanks @vsd2807 +- **fix(dashboard):** Show a stable error state when Search Analytics returns an HTTP error or malformed data ([#11603](https://github.com/diegosouzapw/OmniRoute/pull/11603)) — thanks @pacocartones +- **test(gamification):** pin the aggregate profile level to the XP-derived semantics of #11604 — `getAggregateXp()` now derives `currentLevel` from the summed XP (`calculateLevel(sum)`), not `MAX(stored current_level)`, and the #3484 fixture levels are aligned with the XP curve ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)). ([#11676](https://github.com/diegosouzapw/OmniRoute/pull/11676)) — thanks @jonlwheat2-gif / @pacocartones +- **fix(dashboard):** Keep the Profile level and progress aligned with aggregate XP, including bounded handling for invalid totals ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)) — thanks @pacocartones +- **fix(dashboard):** Prevent locked hidden badges from revealing their icon or opening private badge details before they are earned ([#11605](https://github.com/diegosouzapw/OmniRoute/pull/11605)) — thanks @pacocartones +- **fix(dashboard):** Restore keyboard focus after shared modals close and cancel delayed autofocus during cleanup ([#11607](https://github.com/diegosouzapw/OmniRoute/pull/11607)) — thanks @pacocartones +- **fix(dashboard):** Enable Enter and Space activation for clickable data-table rows without hijacking nested controls ([#11610](https://github.com/diegosouzapw/OmniRoute/pull/11610)) — thanks @pacocartones +- **fix(autoCombo,sse):** vendor-retired catalog ids are dropped from the auto-combo candidate pool and no longer win on leftover `arena_elo` / `user_override` rows; `getModelLifecycleDecision` consults `model-lifecycle.json` (prefix-stripped) so aggregator traffic is not `untracked` for ids the snapshot already knows ([#11625](https://github.com/diegosouzapw/OmniRoute/issues/11625)) ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)) — thanks @MumuTW +- **test(chatcore):** move Codex/Claude combo fixtures off models the lifecycle guard now rejects — `gpt-5.1-codex`/`gpt-5-codex` are vendor-retired (snapshot, #11626) and `claude-3-5-sonnet-20241022` is shut down, so native-passthrough and combo-fallback tests switched to `gpt-5.6-sol` and `claude-sonnet-4.6` ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)). ([#11675](https://github.com/diegosouzapw/OmniRoute/pull/11675)) — thanks @jonlwheat2-gif / @MumuTW +- fix(opencode-plugin): stop a free-tier budget that rounds up across a magnitude from rendering as `1000K`/`1000M` in the model picker — `fmtTokens` chose its unit from the raw token count and then rounded with `toFixed(1)`, so 999,950–999,999 printed as `1000K` rather than `1M` and just under a billion printed as `1000M` rather than `1B` ([#11684](https://github.com/diegosouzapw/OmniRoute/pull/11684)) — thanks @abhisheksharma2411 +- Cap prose-derived Antigravity quota resets at the configured model cooldown maximum while preserving authoritative Retry-After headers and Google RetryInfo hints. (#11695) Thanks @pacocartones. ([#11823](https://github.com/diegosouzapw/OmniRoute/pull/11823)) +- Compare `_rebaseline_*` notes structurally in the ratchet-bank verifier so an object-valued note no longer reports itself as rewritten on an unmodified tree — the nightly `bank-ratchet-shrinks` lane had been aborting on every run since 2026-08-11, leaving 41,672 lines of earned file-size shrinks unbanked (#11702). ([#11892](https://github.com/diegosouzapw/OmniRoute/pull/11892)) — thanks @MumuTW +- **fix(build):** `prepublish.ts` bundles the ChatGPT Web (Codex) MCP bridge through `runBuildTool()` instead of spawning `npx.cmd` raw, fixing the build crash on Node ≥ 20/Windows where `.cmd` shims cannot be spawned without a shell (EINVAL) ([#11704](https://github.com/diegosouzapw/OmniRoute/issues/11704)) ([#11706](https://github.com/diegosouzapw/OmniRoute/pull/11706)) — thanks @jonlwheat2-gif +- fix(codex): keep `parallel_tool_calls:false` on the translated Codex Responses Lite path (#11707) ([#11984](https://github.com/diegosouzapw/OmniRoute/pull/11984)) — thanks @hartmark +- **fix(tests):** the new Gemini Web Images retirement test (#11708) called the now-async `getExecutor()` synchronously, throwing `TypeError: getExecutor(...).getProvider is not a function` instead of asserting Gemini Web chat stays available. Awaited to match the R0.3 executor-registry refactor (#11220). ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(search):** Allow REST and MCP web-fetch requests to select the advertised `anysearch-search` provider explicitly ([#11716](https://github.com/diegosouzapw/OmniRoute/pull/11716)) — thanks @pacocartones +- **fix(providers):** Keep overnight peak-hour protection active after midnight according to the window's configured start day ([#11718](https://github.com/diegosouzapw/OmniRoute/pull/11718)) — thanks @pacocartones +- **fix(providers):** correct all five g4f.space gateway descriptions to document proof-of-work credits or member API keys, variable limits, and the remote prompt boundary instead of promising free keyless access at five requests per minute ([#11738](https://github.com/diegosouzapw/OmniRoute/pull/11738)). +- **fix(db):** `model_capabilities` is created by a migration instead of lazily on the first models.dev sync, so a clean install and an upgraded install converge on the same schema regardless of which features have run ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(ci):** `check:install-upgrade` now fails on an `npm` install truncated by ENOSPC (npm reports it as a warning and still exits 0), authenticates its health probe so the version assertion works against the hardened health payload, frees the clean-install tree before the upgrade phase, and no longer reports a schema divergence computed from a boot that never served ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(routing):** harden session-affinity key derivation against oversized explicit IDs and empty structural-payload collisions, while recognizing Gemini and common text request shapes ([#11746](https://github.com/diegosouzapw/OmniRoute/pull/11746)) — thanks @MumuTW +- **fix(packages/browser-pool):** regenerate `package-lock.json` so the `packages/browser-pool` workspace's locked `playwright`/`@types/node` (and transitives) match its `package.json` specs, fixing cache-only/offline installs (`npm ci --offline`, Nix `buildNpmPackage`) that previously failed with `ENOTCACHED` ([#11747](https://github.com/diegosouzapw/OmniRoute/issues/11747)) — thanks @benjaminkitt ([#11784](https://github.com/diegosouzapw/OmniRoute/pull/11784)) — thanks @geekyNads +- **fix(assets):** Stop distributing 79 provider and CLI logos without verified source/license provenance, keep every provider/tool available through the internal generic icon, and prevent those IDs from falling through to an external icon CDN. ([#11750](https://github.com/diegosouzapw/OmniRoute/pull/11750)) +- **fix(providers):** retire the common `chatgpt-web` / `cgpt-web` provider because its pre-key and proof-of-work provenance could not be cleared ([#11754](https://github.com/diegosouzapw/OmniRoute/pull/11754)) — explicit chat and image requests, slash-prefixed models, persisted aliases, proxy overrides, and combo targets now fail closed with a sanitized HTTP `410` / `PROVIDER_RETIRED` before upstream dispatch. Migration 163 tombstones matching connections and invalidates their active leases without deleting history or widening API-key allowlists. The independent `chatgpt-web-codex` / `cgpt-codex` provider and its MIT-noticed vendored implementation remain available. +- **fix(usage):** quota and usage refresh no longer 409 when an exclusive lease reserves the connection ([#11758](https://github.com/diegosouzapw/OmniRoute/pull/11758)) — thanks @TheDemonTuan +- Keep the embedding registry's vector width and `embedding` type on models when a synced model exists for the same id, so `/v1/models` no longer reports registry-described embedding models widthless or untyped (#11761) — thanks @rahilmavani +- Correct `google/gemini-embedding-001` on the OpenRouter route to 3072 dimensions, the width it returns when `dimensions` is not sent (#11761) — thanks @rahilmavani +- **fix(vision-bridge):** Restrict automatic vision routing and fallbacks to models available in authoritative live provider catalogs, including aliases and effort variants ([#11767](https://github.com/diegosouzapw/OmniRoute/issues/11767)) — thanks @pacocartones ([#11813](https://github.com/diegosouzapw/OmniRoute/pull/11813)) +- **fix(providers):** reclassify Cerebras as a one-time $5 signup credit (payment method required, 30-day validity), not a recurring no-card 1M tokens/day trial ([#11773](https://github.com/diegosouzapw/OmniRoute/issues/11773)) ([#12591](https://github.com/diegosouzapw/OmniRoute/pull/12591)) — thanks @HouMinXi +- fix(cli): stop prepublish from re-rebuilding the already-built ESM-only opencode-plugin dist (#11787) ([#11990](https://github.com/diegosouzapw/OmniRoute/pull/11990)) — thanks @hartmark +- **fix(db):** Fresh installs no longer log a non-fatal `no such table: compression_run_telemetry` warning when retention cleanup runs before the lazily-created telemetry table exists ([#11802](https://github.com/diegosouzapw/OmniRoute/pull/11802)) — thanks @RaviTharuma +- fix(sse): set X-OmniRoute-Selected-Connection-Id on successful combo dispatches so downstream consumers stop falling back to an empty connection id (#11810) ([#11986](https://github.com/diegosouzapw/OmniRoute/pull/11986)) — thanks @hartmark +- **fix(services):** embedded CLIProxyAPI lifecycle checks now use public `/healthz`, while model discovery uses the configured dedicated data-plane API key instead of the management password ([#11811](https://github.com/diegosouzapw/OmniRoute/pull/11811)) — thanks @RaviTharuma +- Fix persisted model steps returning an error in the playground route simulator, and warn when structural steps cannot be simulated. ([#11862](https://github.com/diegosouzapw/OmniRoute/pull/11862)) — thanks @pacocartones +- **fix(providers):** Antigravity's dynamic mitmAlias table no longer routes `gemini-3.7-flash-{high,medium,low}` to a literal tier-suffixed upstream id just because one connected account's own discovery listed it directly — those display ids always resolve through the safe `gemini-3.7-flash-tiered` static alias, so one account's Google-provisioned access no longer 404s every sibling account of the provider ([#11824](https://github.com/diegosouzapw/OmniRoute/issues/11824), [#11651](https://github.com/diegosouzapw/OmniRoute/issues/11651)) ([#11988](https://github.com/diegosouzapw/OmniRoute/pull/11988)) — thanks @hartmark +- **fix(dashboard):** Expose Profile loading, errors, page structure, and XP progress to assistive technologies ([#11838](https://github.com/diegosouzapw/OmniRoute/pull/11838)) — thanks @pacocartones +- **fix(socks):** `Agent.connectTimeout` now bounds both SOCKS handshake and TLS connect, and `family === null` no longer falls back to `fetch-socks` ([#11842](https://github.com/diegosouzapw/OmniRoute/pull/11842)) — thanks @maxmad64bis +- **perf(compression):** OOM mitigations for large payload hashing, memoization, and token estimation ([#11844](https://github.com/diegosouzapw/OmniRoute/pull/11844) — thanks @AndrianBalanescu) +- Excluded Next.js Node File Trace manifests (`*.nft.json`) from the published npm tarball. They are build-time metadata and are never read while serving, but had grown to 668.7 MB — 61% of the package — which pushed the upload past the registry limit and made `npm publish` fail with `413 Payload Too Large`. ([#11864](https://github.com/diegosouzapw/OmniRoute/pull/11864)) +- **fix(cli):** keep the global OmniRoute server `--base-url` separate from provider-node `--endpoint` values in add, update, and validate commands ([#11860](https://github.com/diegosouzapw/OmniRoute/pull/11860)) — thanks @pacocartones +- **fix(config):** Nous Research's `Hermes-4-405B` model now displays as "Hermes 4 405B (Nous Research)" in both the provider registry and the free-model catalog, instead of the mislabelled "Hermes 4 7B" ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#11993](https://github.com/diegosouzapw/OmniRoute/pull/11993)) +- **fix(provider/nous):** inject required user= tag into Nous Research inference requests to resolve upstream 400 "missing tags" error ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#12044](https://github.com/diegosouzapw/OmniRoute/pull/12044)) +- **fix(build):** `npm run build` now fails in one second with a named package and a copy-pasteable fix when npm silently drops an externalised optional native dependency, instead of dying four minutes in with `Module not found: Can't resolve 'better-sqlite3'` ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- **fix(install):** `postinstall` no longer throws `ReferenceError: isAndroid is not defined` — failing the whole `npm install` — when the `better-sqlite3` rebuild fallback times out; the manual-fix guidance is reachable again ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- Fixed the Alibaba free-tier allowlist test that went red on its own once the shipped catalog's `validUntil` (2026-08-27) passed, leaving every PR and `main` with a failing `Unit Tests (1/8)`. The test now builds its own packs with dates it controls, and covers the expired-pack fallback that production has actually been serving. ([#11867](https://github.com/diegosouzapw/OmniRoute/pull/11867)) +- Split the npm registry upload into its own GitHub-hosted job. npm refuses `--provenance` from a self-hosted runner (`422 ... Only "github-hosted" runners are supported`), which blocked the v3.8.50 publish; the heavy verification cannot move to a hosted runner, so it now hands the proven tarball over instead. ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(ui):** the shared `DataTable` loading state no longer reads its decorative ⏳ glyph out to assistive technology, and now carries the same `role="status"` / `aria-live="polite"` / `aria-busy="true"` semantics as `PageLoading` ([#11869](https://github.com/diegosouzapw/OmniRoute/pull/11869)) — thanks @pacocartones +- **fix(api):** Bound the `timeout` accepted by `POST /api/providers/[id]/login` to the same 15s–600s window the Adobe Firefly and Conol browser-login paths already enforce, so a request can no longer pin a headful browser and the single active-login slot with an arbitrarily large poll budget. ([#11871](https://github.com/diegosouzapw/OmniRoute/pull/11871)) — thanks @pacocartones +- **fix(api):** Stop the tunnel and MITM routes from returning raw child-process error text, which disclosed host paths, binary install locations and Tailscale `tskey-*` credentials in a body some of these routes serve to non-loopback callers, and return a real 400 instead of a framework 500 when a tunnel request body fails validation. ([#11872](https://github.com/diegosouzapw/OmniRoute/pull/11872)) — thanks @pacocartones +- **fix(open-sse):** stream TTFT/ITL are sampled from a monotonic clock (`performance.now`) instead of `Date.now`, so an NTP correction or wall-clock jump can no longer inflate a genuine TTFT or produce a negative one that the `ttft >= 0` guard silently drops — protecting the router's OTel/EWMA/`usage_history`/speed-ranking signals ([#11873](https://github.com/diegosouzapw/OmniRoute/pull/11873)) — thanks @pacocartones +- **fix(providers):** xAI `reasoning_effort: "xhigh"` now reaches grok-4.6+ instead of being silently clamped to `"high"` ([#11879](https://github.com/diegosouzapw/OmniRoute/pull/11879)) — thanks @NoxzRCW +- **fix(dashboard):** the providers page no longer crashes into the error boundary when a provider id collides with an `Object.prototype` member (`constructor`, `__proto__`); icon lookups are own-property guarded ([#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)) — thanks @NoxzRCW +- **fix(skills):** injected skill tools declared in shorthand (`{"content": "string"}`) now forward valid JSON Schema, unblocking providers that validate tool schemas strictly such as Zhipu GLM on the Console Go tier ([#11881](https://github.com/diegosouzapw/OmniRoute/pull/11881)) — thanks @NoxzRCW +- **fix(api):** `POST /api/playground/simulate-route` now surfaces `combo-ref` and `provider-wildcard` persisted combo steps with a specific warning (naming the referenced combo, or the unresolved `provider/model` wildcard) instead of folding them into a generic "unsupported step" count; a `provider-wildcard` step is also now included as an unresolved target so the operator can see it is in the route (ported from [#11882](https://github.com/diegosouzapw/OmniRoute/pull/11882) — thanks @NoxzRCW). ([#11930](https://github.com/diegosouzapw/OmniRoute/pull/11930)) +- **fix(translator):** the streaming OpenAI→Claude translator keeps upstream usage, including prompt-cache tokens, when it arrives on a trailing `choices: []` chunk (Fireworks and any upstream using `stream_options.include_usage`) ([#11883](https://github.com/diegosouzapw/OmniRoute/pull/11883)) — thanks @NoxzRCW +- **fix(cli):** `omniroute update --apply` now tells you explicitly whether a running server was detected and, if so, that you must run `omniroute restart` to apply the update — it never restarted anything and previously implied the update was already live once files were installed. The dashboard's npm-mode Update flow (`/api/system/version`) now tries OmniRoute's own PID-file-managed supervisor before falling back to pm2, and reports an honest "restart required" step instead of a silent pm2-only "skipped" that read like a completed update. The server-side latest-version lookup backing the dashboard's update banner also gained `--prefer-online`, closing the same stale-npm-cache class already fixed in the CLI's own copy for #4376 ([#11885](https://github.com/diegosouzapw/OmniRoute/issues/11885)). ([#12005](https://github.com/diegosouzapw/OmniRoute/pull/12005)) +- **fix(resilience):** clear persisted LKGP pins when a target suffers connection/provider exhaustion or is skipped before dispatch due to cooldown/exhaustion/unavailability, preventing subsequent requests from repeatedly prioritizing known-dead providers ([#11911](https://github.com/diegosouzapw/OmniRoute/issues/11911)). ([#12013](https://github.com/diegosouzapw/OmniRoute/pull/12013)) — thanks @HouMinXi +- **fix(sse):** the OpenAI→Claude stream translator now defers the terminal `message_delta`/`message_stop` emission until the real usage block has arrived (or a genuine end-of-stream flush forces it) instead of emitting it immediately on `finish_reason` — previously, when the trailing usage-only chunk (`{"choices":[],"usage":{...}}`) arrived *after* the `finish_reason` chunk (the normal order for Fireworks/vLLM/Together and other `stream_options.include_usage` upstreams), the client-visible `message_delta` still carried stale/zero usage even though `state.usage` was internally corrected too late to matter (ported from [#11915](https://github.com/diegosouzapw/OmniRoute/pull/11915) — thanks @HouMinXi). ([#11933](https://github.com/diegosouzapw/OmniRoute/pull/11933)) +- **fix(sse):** `stripStore()` now forces `store=false` for stateless OpenAI-compatible Responses-API targets (unless the connection has explicitly opted in via `providerSpecificData.openaiStoreEnabled`), instead of only handling the `openai`/`agentrouter` cases — a client-supplied `store` value previously passed through untouched to backends that don't actually persist responses server-side ([#11916](https://github.com/diegosouzapw/OmniRoute/pull/11916) — thanks @HouMinXi). +- **fix(models):** custom provider-node models (synced, custom, and alias-backed) now appear under their configured prefix in the unified catalog when the operator's model-id prefix mode is canonical, instead of being dropped whenever alias-inclusion was otherwise disabled ([#11918](https://github.com/diegosouzapw/OmniRoute/pull/11918) — thanks @HouMinXi). +- **fix(models):** the unified model catalog now suppresses stale static registry models (including effort-tier variants) for any provider whose active connection has an authoritative live synced catalog, not only providers already using exclusive-synced-listing — a connection with `providerUsesAuthoritativeLiveCatalog` previously kept serving both the live-synced models and the stale static rows side by side ([#11919](https://github.com/diegosouzapw/OmniRoute/pull/11919) — thanks @HouMinXi). +- **fix(ui):** `ProviderIcon`'s three remaining unguarded lookups (`PROVIDER_ICON_ALIASES`, `LOCAL_SVG_ALIASES`, `THEMED_SVGS`) now use `Object.hasOwn()` own-property checks — a provider id such as `constructor` or `__proto__` previously resolved through the prototype chain instead of falling through to the unknown-provider CDN fallback (`getLobeProviderIcon()` itself was already guarded by [#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)); `ProviderPageHeader`'s `color` field is also now optional, matching the rest of the component's defensive typing (ported from [#11920](https://github.com/diegosouzapw/OmniRoute/pull/11920) — thanks @HouMinXi). ([#11935](https://github.com/diegosouzapw/OmniRoute/pull/11935)) — thanks @NoxzRCW +- fix(ollama): preserve multi-byte UTF-8 content split across stream chunks in the Ollama NDJSON transform, which previously corrupted CJK/emoji into U+FFFD (#11921) — thanks @pacocartones +- **fix(providers):** OrcaRouter chat requests now target `/v1/chat/completions` instead of the bare `/v1` API root, fixing the upstream `404 Invalid URL (POST /v1)` ([#11923](https://github.com/diegosouzapw/OmniRoute/pull/11923)). — thanks @echel0nn +- **fix(plugins):** deliver the `onStreamComplete` event to disk-installed plugins. The event shipped in v3.8.50 (#9669) was emitted internally but had no plugin-facing wiring, so no plugin could ever subscribe: the manifest schema silently dropped `hooks.onStreamComplete`, and the loader/manager only knew the seven legacy hooks. `onStreamComplete` is now a declarable manifest hook, wired through the loader and registered by the manager like the other hooks, and its payload carries a `requestId` so consumers can correlate the stream-completion event with the originating request ([#11934](https://github.com/diegosouzapw/OmniRoute/pull/11934)) — thanks @amaleta / @pacocartones +- **fix(db):** the Qdrant embedding-model dropdown now lists local/self-hosted providers (Ollama, LM Studio, vLLM, etc.) — an active connection is treated as "configured" when the provider allows an optional API key, not only when it has a real key or OAuth, so a running local embedding provider is no longer hidden from the picker ([#11949](https://github.com/diegosouzapw/OmniRoute/issues/11949)) ([#11995](https://github.com/diegosouzapw/OmniRoute/pull/11995)) +- **fix(providers):** Vertex AI Anthropic partner-model discovery now calls the Model Garden `v1beta1` publisher list (`/v1beta1/publishers/anthropic/models`, global) and parses its `publisherModels` envelope, so Claude models auto-synced from Vertex populate the active live catalog and route at request time instead of returning `Model '' is not available in the active live catalog` ([#11991](https://github.com/diegosouzapw/OmniRoute/issues/11991)) — thanks @fabioluissilva ([#11998](https://github.com/diegosouzapw/OmniRoute/pull/11998)) +- **fix(cli):** support `--base-url` alongside `--endpoint` in `omniroute nodes add`, `update`, and `validate` subcommands to prevent global `--base-url` shadowing issues ([#11999](https://github.com/diegosouzapw/OmniRoute/issues/11999)). ([#12033](https://github.com/diegosouzapw/OmniRoute/pull/12033)) — thanks @HouMinXi +- **fix(providers):** `cloudflare-ai` no longer refuses image content parts for every Workers AI model ([#12002](https://github.com/diegosouzapw/OmniRoute/pull/12002)) — the plain-string `content` requirement behind #2539 is carried by the _model_ schema, not by the `/ai/v1/chat/completions` endpoint (measured: an all-text part array returns 200 on `@cf/mistralai/mistral-small-3.1-24b-instruct`, `@cf/meta/llama-4-scout-17b-16e-instruct` and `@cf/meta/llama-3.3-70b-instruct-fp8-fast`, and 400 on the text-only `@cf/qwen/qwen2.5-coder-32b-instruct`). `transformRequest()` flattened every array and threw on the first non-text part (#6390), so image input was refused for vision-capable Cloudflare models that accept it. All-text arrays are still flattened — the one shape every model accepts — while an array carrying a non-text part is passed through untouched, so the attachment is still never silently dropped. Regression guards: `tests/unit/cloudflare-ai-image-parts-6390.test.ts`. — thanks @davidlinfr +- **fix(resilience):** decouple the limiter-managed execution backstop from the queue-wait budget — new `requestQueue.executionMaxWaitMs` (env `RATE_LIMIT_EXECUTION_MAX_WAIT_MS`, default 600000 = 10 min) now feeds Bottleneck's post-dispatch `expiration`, while `requestQueue.maxWaitMs` keeps its documented queue-wait semantics. Previously the queue-wait budget doubled as the execution expiration, so legitimate long-running calls on non-incremental gateways (whole generation buffered before the first upstream byte, e.g. Console Go / Command Code tiers serving GLM models) were killed mid-flight at the queue budget with a false 504 `RATE_LIMIT_EXECUTION_TIMEOUT` — the local limiter undercut the provider-aware upstream fetch-start timeouts. The surfaced 504 message now names `requestQueue.executionMaxWaitMs`; the error keeps the #4165 guarantees (disclaims an upstream timeout, preserves the Bottleneck error as `cause`, branded code + trusted provenance, classified request-scoped so combo falls back). A real queue-wait bound (the `Promise.race` around `limiter.schedule()` sketched in #9533) remains future work. (#12025) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **Call logs:** keep the `error` field when an artifact exceeds the storage cap, instead of replacing it with the omission marker. The error is the only field that says *why* a request failed and is typically ~90 bytes next to the multi-hundred-KB bodies that trip the cap, so dropping it left a size-limited row undiagnosable — a provider outage, a local timeout and an upstream 400 all rendered identically. It is now preserved at every fallback stage, truncated to 4KB if it is itself large ([#12026](https://github.com/diegosouzapw/OmniRoute/issues/12026)). ([#12095](https://github.com/diegosouzapw/OmniRoute/pull/12095)) — thanks @ntdat812 +- **fix(diagnostics):** preserve the error field (truncated to 4KB with a `[truncated: …]` suffix) in every call-log artifact size-limit fallback stage. Previously the minimal fallback replaced the error with `[omitted: call log artifact size limit exceeded]`, so an oversized artifact row showed nothing about WHY the request failed — e.g. 91 of 847 opencode-go 504 rows on one production instance were undiagnosable from the dashboard. Oversized request/response bodies are still omitted exactly as before; the error cap is independent of the payload sizes that tripped the fallback. (#12026) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **fix(sse):** OpenAI Responses clients that declare the native `web_search` tool now receive a spec-shaped `web_search_call` output item with `action.sources` alongside the preserved function-call round-trip, so search results executed through OmniRoute's own search backend are consumable by standard Responses clients (Codex, pi-web-access, …). ([#12031](https://github.com/diegosouzapw/OmniRoute/pull/12031)) — thanks @watchingdogs +- **fix(cli):** use in-thread alias resolver hooks on modern runtimes to avoid deprecation noise and improve Node.js forward compatibility ([#12073](https://github.com/diegosouzapw/OmniRoute/issues/12073)). ([#12083](https://github.com/diegosouzapw/OmniRoute/pull/12083)) — thanks @xiaoyaner0201 +- **fix(combo):** an operator-set **Agent Features → Context length** on a combo is now honored at request time. The value was persisted and advertised through `/v1/models`, but `resolveComboContextLimit()` never consulted it — so a multi-target combo whose members carry no per-model window fell through to the provider's generic `defaultContextLength` (openrouter 128000, command-code 200000) and rejected large requests with `Input exceeds context window … limit 128000` despite the combo being explicitly sized much larger. An identical single-target combo worked, because it collapses to its concrete target before the guard runs. Invalid values (0/negative/NaN/Infinity) are ignored, so the existing target → combo-min → fallback order is unchanged. ([#12090](https://github.com/diegosouzapw/OmniRoute/pull/12090)) — thanks @adivekar-utexas +- **fix(sse):** passthrough streams now estimate usage on finish when upstream closes without usage even with `stream_options.include_usage` — avoids `0 tokens / 0%` for providers that stay silent (and correctly handles trailing empty-choices usage) ([#12151](https://github.com/diegosouzapw/OmniRoute/pull/12151)) — thanks @maxmad64bis +- **fix(combos):** clearing an agent feature in the combos editor now persists — unchecking context cache protection, or emptying the system message or tool filter, sends an explicit `null` instead of dropping the field from the `PUT` body, which the update merge read as "leave unchanged" ([#12177](https://github.com/diegosouzapw/OmniRoute/pull/12177)) — thanks @foreveryh / @ntdatt812 +- **fix(memory):** self-hosted embedding endpoints now vectorize — the vector width is measured from the first embedding that comes back instead of being read from a registry that cannot describe them, so `vec_memories` is created and memories stop piling up unvectorized behind a green health check ([#12180](https://github.com/diegosouzapw/OmniRoute/pull/12180)) — thanks @kanade-hoshino / @ntdatt812 +- **fix(translator):** the leading `system` message now reaches Responses-API upstreams when its `content` is a content-part array — it was read as `typeof content === "string" ? content : ""`, so a prompt-caching client (Anthropic `cache_control`, the shape LiteLLM and the Anthropic SDK emit) had its entire system prompt replaced by an empty `instructions`. The request was still accepted with a normal `prompt_tokens` count, so the model answered with no instructions and nothing in the response said they were missing. Mid-conversation system turns already handled the array shape ([#7056](https://github.com/diegosouzapw/OmniRoute/pull/7056)); only the first one did not ([#12206](https://github.com/diegosouzapw/OmniRoute/issues/12206)). Regression guard: `tests/unit/translator-openai-responses-system-content-parts.test.ts`. ([#12207](https://github.com/diegosouzapw/OmniRoute/pull/12207)) — thanks @Gorillaz322 +- **fix(free-tier):** `/api/free-tier/summary` no longer computes its totals from a Radar feed built before the catalog the running release ships. When the cached feed is older — or carries no build date at all — the route answers from the shipped catalog, resolved through the operator's local model state so disabled and tombstoned models stay out of the numbers ([#12215](https://github.com/diegosouzapw/OmniRoute/pull/12215)). — thanks @maxmad64bis +- **fix(oauth):** Keep a Claude personal workspace and a Team organization as separate connections — they share the same email and `accountUUID`, so the email-only OAuth dedup let the second login overwrite the first account's tokens; `organizationUUID` now disambiguates them, the way `workspaceId` does for Codex ([#12222](https://github.com/diegosouzapw/OmniRoute/pull/12222)) — thanks @davidebaraldo +- **fix(resilience):** a 402 on a single paid model of a passthrough/gateway provider (e.g. `kilo-gateway`, `ollama-cloud`) no longer terminalizes the whole connection with a never-auto-recovered `credits_exhausted` status — only the paid model is locked out, so free models on the same key keep serving. 402 variant of [#3027](https://github.com/diegosouzapw/OmniRoute/issues/3027). Single-credential providers are unaffected — a 402 there is still treated as the key being genuinely out of credit ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239) / [#10616](https://github.com/diegosouzapw/OmniRoute/issues/10616)) ([#12242](https://github.com/diegosouzapw/OmniRoute/issues/12242)) — thanks @brick30llc-ctrl ([#12266](https://github.com/diegosouzapw/OmniRoute/pull/12266)) — thanks @geekyNads +- **fix(sse):** trust `finish_reason: "length"`/`"max_tokens"` over the reasoning-consumed-token ratio in response quality validation, so a reasoning model truncated below the old 90% threshold correctly fails and retries instead of returning empty content as a silent "success" ([#12262](https://github.com/diegosouzapw/OmniRoute/pull/12262)) — thanks @brick30llc-ctrl +- **fix(settings):** `PUT /api/settings/cache-config` now persists `alwaysPreserveClientCache` to the flat general settings the runtime cache-control policy actually reads; previously the value landed in the databaseSettings "cache" section and was silently ignored, so the endpoint had no effect on `cache_control` passthrough ([#12304](https://github.com/diegosouzapw/OmniRoute/pull/12304)) — thanks @davidebaraldo +- **fix(grok-cli):** treat omitted SuperGrokPro `creditUsagePercent` as 0% used so Provider Limits still renders a weekly bar (proto3 zero-elision) ([#12312](https://github.com/diegosouzapw/OmniRoute/pull/12312)) — thanks @HouMinXi +- **fix(combo):** Expose the two Auto-Combo scoring factors nobody could set — the combo validation schema and the dashboard weight sliders both declared 13 of the scorer's 15 factors, so `connectionDensity` (spreads load across a provider's connections) and `quality` were dropped on save and offered nowhere. The sliders also shipped their own default table that differed from the engine's on every non-zero factor and summed to 1.05, so the percentages shown next to them added up to 105%. Both lists now match `DEFAULT_WEIGHTS`, and a test keeps them there. Note that a combo whose stored `weights` omitted the two keys was effectively running with them at zero and the other thirteen renormalized upward; it now runs with the engine's intended distribution, so its routing does shift ([#12314](https://github.com/diegosouzapw/OmniRoute/pull/12314)) — thanks @maxmad64bis +- **fix(docs):** The free-tier reference no longer says its numbers come "confidence tagged per row" — no catalog entry carries a confidence tag and the API serves none, so every figure on that page is an estimate of the same, unstated quality. The page now states what an entry does vouch for: an independently documented hard stop (set by hand with the source in a comment, never defaulted to `true`) and a prompt-training disclosure, both with live counts the `check:docs-counts` gate keeps honest ([#12318](https://github.com/diegosouzapw/OmniRoute/pull/12318)) — thanks @maxmad64bis +- **fix(usage):** `adobe-firefly` and `firefly` have had a working usage fetcher since Adobe Firefly landed, but neither was ever added to the registration list, so the provider-plugin manifest, `genericQuotaFetcher` and the free-access quota cache all reported them as having no usage support — while `USAGE_SUPPORTED_PROVIDERS` said the opposite. Both are now declared, which also means their credit balance is fetched like any other declared provider's: `registerGenericQuotaFetchers` now registers a generic quota fetcher for them, and `resolveFreeAccessState` no longer returns early. A test holds the registration list to the dispatcher's switch in both directions, which is what the module's own docstring already asked for in prose ([#12321](https://github.com/diegosouzapw/OmniRoute/pull/12321)) — thanks @maxmad64bis +- **fix(quota):** drop the generic quota cache (agy / Antigravity / Claude OAuth) on an upstream 429 so reset-aware scoring does not keep a 60s stale snapshot, and force-refresh the next usage fetch so inner provider caches cannot recache the same window ([#12325](https://github.com/diegosouzapw/OmniRoute/pull/12325)) — thanks @HouMinXi +- **fix(combos):** deleting a combo now clears its persisted LKGP pins instead of leaving unreachable `key_value` rows behind ([#12326](https://github.com/diegosouzapw/OmniRoute/issues/12326)) ([#12425](https://github.com/diegosouzapw/OmniRoute/pull/12425)) — thanks @KrzysiekSko +- **fix(sse):** Keep ZWNJ (U+200C) and ZWJ (U+200D) in assistant text, reasoning and tool-call arguments — Persian/Kurdish half-space (`ارائه‌دهنده`), Arabic/Indic shaping and emoji sequences no longer lose them; the response de-obfuscation now removes joiners only between ASCII word characters, where the request side inserts them ([#12186](https://github.com/diegosouzapw/OmniRoute/issues/12186)) — thanks @rezjalibd ([#12359](https://github.com/diegosouzapw/OmniRoute/pull/12359)) — thanks @pacocartones +- **fix(resilience):** count resolved upstream 5xx results against the provider circuit breaker on the chat path — `CircuitBreaker.execute()` no longer reads a resolved `{ success: false, status: 5xx }` as a success that cancels the call-site failure, so a provider answering 503s now trips its breaker instead of staying `CLOSED` at `failureCount: 1`; single-model and combo dispatches are each accounted exactly once ([#12254](https://github.com/diegosouzapw/OmniRoute/issues/12254)) ([#12360](https://github.com/diegosouzapw/OmniRoute/pull/12360)) — thanks @pacocartones +- **fix(providers):** resolve the Codex quota auto-ping model from the live provider catalog and lifecycle registry instead of the retired `gpt-5.1-codex-mini`, and pause the ping with one actionable warning when no selectable Codex model exists rather than retrying a shut-down id every cooldown window ([#11905](https://github.com/diegosouzapw/OmniRoute/issues/11905)) ([#12361](https://github.com/diegosouzapw/OmniRoute/pull/12361)) — thanks @pacocartones +- **fix(api):** keep the `{created, data}` wrapper on combo-routed `/v1/images/generations` responses and default Codex image results to `b64_json` on both `/v1/images/generations` and `/v1/images/edits` so Codex CLI's built-in `image_gen` can decode them ([#12268](https://github.com/diegosouzapw/OmniRoute/issues/12268)) ([#12362](https://github.com/diegosouzapw/OmniRoute/pull/12362)) — thanks @pacocartones +- **fix(sse):** Name the shadowed custom provider node when a built-in provider id/alias (e.g. `openference` → `of`) reserves the prefix of an existing OpenAI/Anthropic-compatible node, so the runtime `No active credentials for provider: ` error explains that the prefix routed to the built-in and never reached the node's healthy connections, instead of contradicting the dashboard ([#11943](https://github.com/diegosouzapw/OmniRoute/issues/11943)) — thanks @morpheus9393 ([#12365](https://github.com/diegosouzapw/OmniRoute/pull/12365)) — thanks @pacocartones +- **fix(i18n):** wrap `ccOnboardingKeyPlaceholder` in ICU single quotes across all 43 locales so angle brackets render literally instead of being parsed as rich-text tags, which crashed the Claude Code onboarding block with `INVALID_MESSAGE: INVALID_TAG` ([#12302](https://github.com/diegosouzapw/OmniRoute/issues/12302)) ([#12369](https://github.com/diegosouzapw/OmniRoute/pull/12369)) — thanks @gonisulaimann +- **fix(models):** publish `effort_tiers` on Kimi K3's synced base-model entries (`kmca/k3`, `kmca/k3-256k`) so catalog-only clients (OpenCode, plain SDK pickers) can see and select the reasoning tiers (`low`/`high`/`max`) the synced metadata already carried — the `isSkippedEffortProvider` gate no longer suppresses tier visibility on those base entries, while synthetic `-` variant generation stays prevented and Codex/GLM base models remain excluded unchanged ([#12299](https://github.com/diegosouzapw/OmniRoute/issues/12299)) ([#12371](https://github.com/diegosouzapw/OmniRoute/pull/12371)) — thanks @gonisulaimann +- **fix(guardrails):** keep `auto`/`auto/*` virtual combos exempt from the Vision Bridge `fixedModel` credential guard so a combo target is passed through instead of silently falling back to global auto-selection ([#12237](https://github.com/diegosouzapw/OmniRoute/issues/12237)) ([#12373](https://github.com/diegosouzapw/OmniRoute/pull/12373)) — thanks @pacocartones +- **fix(combo):** capability-filter exhaustion caused by `max_tokens` above every target's known output limit now reports that reason (requested `max_tokens` vs the pool's highest known ceiling) instead of the unrelated "supports structured output" message ([#12229](https://github.com/diegosouzapw/OmniRoute/issues/12229)) — thanks @DW-MediaLab ([#12374](https://github.com/diegosouzapw/OmniRoute/pull/12374)) — thanks @pacocartones +- **fix(auth):** the `least-used` account strategy now prefers accounts without backoff before falling back to oldest `lastUsedAt`, the same tie-break `round-robin` already applies, so a failover no longer lands on a just-rate-limited account for a single request ([#12279](https://github.com/diegosouzapw/OmniRoute/issues/12279)) — thanks @tenshiak ([#12375](https://github.com/diegosouzapw/OmniRoute/pull/12375)) — thanks @pacocartones +- **fix(docker):** the `chatgpt-web-codex-browser` image now finds the Chrome binary under `chrome-linux64/` (Chrome for Testing layout in `playwright:v1.62.0-noble`) as well as the legacy `chrome-linux/`, so the container no longer crash-loops with `exec: --headless=new: not found` ([#12024](https://github.com/diegosouzapw/OmniRoute/issues/12024)) ([#12376](https://github.com/diegosouzapw/OmniRoute/pull/12376)) — thanks @pacocartones +- **fix(providers):** declare `groq/compound` and `allam-2-7b` as non-reasoning models in the curated Groq registry so `reasoning_effort` / `output_config.effort` / `thinking` from Claude Code are stripped instead of forwarded, which Groq rejected with HTTP 400 ([#12134](https://github.com/diegosouzapw/OmniRoute/issues/12134)) ([#12379](https://github.com/diegosouzapw/OmniRoute/pull/12379)) — thanks @pacocartones +- **fix(executors):** `OpencodeExecutor` no longer forces a direct connection when the connection has a proxy assigned in Proxy Management but no per-account proxies: the single-account fast path used to wrap the upstream dispatch in the direct-egress sentinel, discarding the ambient proxy context the chat handler had pinned from `proxy_assignments`, so API-key `opencode`/`opencode-go` connections egressed from the host IP (and hit geoblocks) despite the assignment. The direct pin is now applied only when no ambient proxy context exists ([#11894](https://github.com/diegosouzapw/OmniRoute/issues/11894) — thanks @hizzt) ([#12380](https://github.com/diegosouzapw/OmniRoute/pull/12380)) — thanks @pacocartones +- **fix(api):** `GET /v1/models` with `MODELS_CATALOG_PREFIX_MODE=canonical` (or `?prefix=canonical`) now lists providers whose registry alias is undefined or equal to their own id (Antigravity, Antigravity CLI and other self-aliased built-ins) — their single `provider/model` id was dropped by the alias/canonical duplicate guard in the static, synced, custom and alias-backed catalog loops ([#12058](https://github.com/diegosouzapw/OmniRoute/issues/12058)) — thanks @cheynetom ([#12381](https://github.com/diegosouzapw/OmniRoute/pull/12381)) — thanks @pacocartones +- **fix(translator):** Drop replayed `thinking` blocks that carry no signature (the shape produced from cross-provider `reasoning_content`) instead of stamping the default Claude signature on them, which Anthropic rejected with `400 Invalid signature in thinking block` on the next turn served by an Anthropic rung ([#12105](https://github.com/diegosouzapw/OmniRoute/issues/12105)) — thanks @atescivitci-cmd ([#12386](https://github.com/diegosouzapw/OmniRoute/pull/12386)) — thanks @pacocartones +- **fix(cli):** Resolve Bun's `--preload` polyfill path against the package root instead of `dist/`, so `omniroute` installed with `bun install -g` no longer crashes at startup with `error: preload not found …/dist/open-sse/utils/setupPolyfill.ts` ([#11980](https://github.com/diegosouzapw/OmniRoute/issues/11980)) — thanks @joglomedia ([#12387](https://github.com/diegosouzapw/OmniRoute/pull/12387)) — thanks @pacocartones +- **fix(providers):** `gemini-business` now publishes its model catalog — `/v1/models` and `/v1/providers/gemini-business/models` list the 12 enterprise Gemini ids the executor understands instead of returning an empty list (#12107) ([#12389](https://github.com/diegosouzapw/OmniRoute/pull/12389)) — thanks @pacocartones +- **fix(db):** install `busy_timeout` before the SQLite connection's first statement so a process opening the database while another one closes its WAL connection waits out the transient EXCLUSIVE lock instead of dying with `database is locked`, and recognise the drivers' real BUSY/PROTOCOL/IOERR errors as transient in the corruption probe so the same lock no longer renames the database away as corrupt; deflakes `cross-process contenders never both acquire the same connection` (#12394 — thanks @pacocartones) +- **fix(chat-admission):** derive the `chat_admission_busy` 503 `Retry-After` from observed heavyweight-lease occupancy — the larger of the exhausted `OMNIROUTE_CHAT_ADMISSION_QUEUE_MS` window and the time since capacity last turned over, capped at 60 s — instead of a fixed 1 s (structural) / 2 s (byte-stage) hint that invited Codex/agent fan-out clients to re-send ~1 MiB `/v1/responses` bodies every second into a gate held for the whole SSE lifetime; an idle gate keeps the historical floors ([#12135](https://github.com/diegosouzapw/OmniRoute/issues/12135)) (#12395 — thanks @pacocartones) +- **fix(api-manager):** the API key permissions modal no longer silently drops `allowedCombos` entries its Combo picker cannot render — routing-rule names such as `rt-*`, which the backend already honours — when "All" is clicked and the key is switched back to "Restrict"; those entries now survive the toggle, are listed read-only under the combo list so the count and the list agree, and are saved back verbatim instead of persisting `[]` (deny-all) (#12397 — thanks @pacocartones) +- **fix(catalog):** write the NUL separator of the catalog connection memo key, the provider serviceKind memo key, the Video Bridge promotion group key and a JSON-exactness test fixture as the `\u0000` escape instead of a raw byte — same runtime value, but the raw byte made git, GitHub and ripgrep treat those files as binary (hidden PR diffs, silently skipped searches); a guard test now keeps raw NUL bytes out of `src/`, `open-sse/` and `tests/` (#12403 — thanks @pacocartones) +- **fix(providers):** add `CLAUDE_CODE_CLIENT_VERSION` and `GITHUB_COPILOT_CLI_VERSION` env overrides so Anthropic/Copilot client-version gates can be unblocked without a rebuild ([#12417](https://github.com/diegosouzapw/OmniRoute/issues/12417)) ([#12632](https://github.com/diegosouzapw/OmniRoute/pull/12632)) — thanks @HouMinXi +- **fix(db):** back-fill `last_ping_at` and `last_pinged_reset_key` on `provider_connections` during schema reconciliation so divergent lineages that skipped `123_quota_auto_ping` still accept quota auto-ping writes ([#12470](https://github.com/diegosouzapw/OmniRoute/pull/12470) — thanks @KooshaPari) +- **fix(ci):** document MIT exceptions for `@eloqnt/{config,format-json,format-po}` (next-intl transitive; locked tarballs omit `license`) and keep the A2A lifecycle vitest off the real SQLite persistence seam ([#12581](https://github.com/diegosouzapw/OmniRoute/issues/12581)) ([#12595](https://github.com/diegosouzapw/OmniRoute/pull/12595)) +- OpenCode plugin `/v1/models` catalog fetch now waits 30s by default and attaches HTTP `statusCode` on 401/5xx so host fallback plugins can hop instead of seeing an untyped AbortError/UnknownError. ([#12607](https://github.com/diegosouzapw/OmniRoute/pull/12607)) — thanks @RaviTharuma +- **CI:** the OpenAPI security-tier gate now mirrors `isAlwaysProtectedPath()` in full — it also reads `ALWAYS_PROTECTED_API_PATTERNS`, so the pattern-gated credential routes (`/api/providers/{id}/{claude,codex}-auth/{export,apply-local}`, GHSA-5926-2w35-7h4q) no longer report as unannotated. (#12605) +- **fix(api):** GET /v1/models no longer waits forever on a hung coalesced catalog rebuild; cold-path waits are bounded (`CATALOG_BUILD_TIMEOUT_MS`, default 8s) and a last-good 200 is served when the rebuild times out ([#12627](https://github.com/diegosouzapw/OmniRoute/issues/12627)). ([#12628](https://github.com/diegosouzapw/OmniRoute/pull/12628)) — thanks @RaviTharuma +- **fix(cursor):** a non-array `tool_calls` on an assistant message no longer crashes the cursor request translator with a `TypeError`; both loops now require an array ([#12689](https://github.com/diegosouzapw/OmniRoute/issues/12689)) ([#12691](https://github.com/diegosouzapw/OmniRoute/pull/12691)) — thanks @soroush5 +- **fix(chat):** Chat Completions no longer return empty `content` after a server-owned memory or skills tool; the first provider send and account/model recovery now share one pipeline so a follow-up round-trip can fill the reply ([#12696](https://github.com/diegosouzapw/OmniRoute/issues/12696)) — thanks @HouMinXi ([#12867](https://github.com/diegosouzapw/OmniRoute/pull/12867)) +- **fix(dashboard):** batch-deleting provider keys no longer toasts failure after a successful delete when the confirm button's click event is forwarded as `onAfter` ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) — thanks @HouMinXi +- **fix(glm):** drop the extra 16th argument to `createSSETransformStreamWithLogger` that TypeScript rejected (TS2554) and that never reached the TransformStream ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) ([#12770](https://github.com/diegosouzapw/OmniRoute/pull/12770)) — thanks @HouMinXi +- **fix(dashboard):** pass `nodeMap` into Runtime `QuotaGroup` so a quota monitor in error/exhausted/alerting no longer throws `ReferenceError: nodeMap is not defined`. ([#12868](https://github.com/diegosouzapw/OmniRoute/pull/12868)) — thanks @HouMinXi +- Restricted API keys whose `allowedModels` lists a combo name no longer skip every combo member at pre-dispatch (`ALL_TARGETS_SKIPPED`). Inner-target filtering still applies when the allow-list is a provider prefix or `disableNonPublicModels` is on ([#12899](https://github.com/diegosouzapw/OmniRoute/pull/12899)). — thanks @HouMinXi +- **fix(combos):** Effort-suffixed combo members inherit the base model's `model_context_overrides` row so priority order is not inverted on large requests ([#12926](https://github.com/diegosouzapw/OmniRoute/pull/12926)) — thanks @HouMinXi +- **fix(catalog):** Picker-added `customModels` enter the dispatch-time live catalog so combo members and bare inference no longer 400 ([#12934](https://github.com/diegosouzapw/OmniRoute/pull/12934)) — thanks @HouMinXi +- **fix(docker):** re-derive the Docker build's worker-pool memory budget from the MEASURED ~4.5 GB per-process RSS (the issue owner's own VPS dmesg OOM-killer reproduction) instead of the stale 2560 MB/worker inference #11419 shipped, and lower `OMNIROUTE_BUILD_WORKERS` 3 → 2 so 1 parent + 1 page-data worker (2 processes × 4.5 GB = 9 GB) fits the 12.288 GB (75%) budget on a 16 GB GitHub Actions runner — the previous default (1 parent + 2 workers = 13.5 GB) still overcommitted the runner and kept "Publish to Docker Hub" failing with `cannot allocate memory` after #11419 merged (#7518). ([#11663](https://github.com/diegosouzapw/OmniRoute/pull/11663)) +- **fix(electron):** the packaged Windows desktop build now passes the #7592 cold-restart smoke end to end. Five defects were found and fixed on the way: (1) optional-pack staging failed on any machine whose `tar` is GNU tar — it reads the drive letter in an absolute `-f C:\...` archive path as a remote rsh host (`Cannot connect to C:`), so staging now passes a bare filename with `cwd` at the tarball directory and surfaces tar stderr on failure; (2) the Electron `files` allowlist was missing `lib/loginHeaderCapture.js`, so the packaged main process crashed at startup with `Cannot find module './lib/loginHeaderCapture'`; (3) electron-builder ≥26 injects an `!**/node_modules/**` ignore into every extraResources pattern list that no positive filter can override, which silently dropped the staged runtime `node_modules` (including the better-sqlite3 N-API prebuild) from `resources/app` — a fresh v3.8.50 desktop build would have shipped with no native SQLite driver and reproduced the #7592 sql.js fallback on every machine; an `afterPack` hook now restores the staged `node_modules` after packing; (4) the packaged-app smoke harness redirected `USERPROFILE` into its temp DATA_DIR, but Electron resolves the Roaming profile from `%USERPROFILE%\AppData\Roaming\` (USERPROFILE takes precedence over the APPDATA env var) and the path service throws instead of creating it, so `requestSingleInstanceLock()` returned false and the app exited(0) silently before `app.whenReady()` — the harness now pre-creates the derived tree, and `ensureSmokeEnvDirs` is exported and unit-tested; (5) the #7592 driver assertion parsed a `[DB] Driver: ...` line that the server's primary DB path never printed (only the unused `openDatabaseAsync()` did) — `getDbInstance()` now emits the same line on open so the guard can actually assert the native driver. Also: the smoke env-allowlist unit test hardcoded POSIX paths and could only pass on Linux/macOS; expectations are now host-agnostic, with new regression tests for the USERPROFILE-derived tree and `tarPack` under absolute Windows-style paths ([#11443](https://github.com/diegosouzapw/OmniRoute/pull/11443)) — thanks @jonlwheat2-gif +- fix(sse): stop the auto-combo candidates inspector from silently dropping model-locked/cooled-down rows (#9133) ([#11994](https://github.com/diegosouzapw/OmniRoute/pull/11994)) — thanks @hartmark +- **fix(providers):** Perplexity Web no longer turns upstream stream failures into successful assistant text; pre-content failures remain eligible for fallback, partial output ends with a structured sanitized error, and failed sessions are not persisted ([#12465](https://github.com/diegosouzapw/OmniRoute/pull/12465)) +- **fix(video-bridge):** preserve chronological video and transcript fusion, source-frame timestamps, transcript provenance, full contact-sheet intervals, and pre-abort callback isolation ([#11681](https://github.com/diegosouzapw/OmniRoute/pull/11681)) +- **Z.ai Web:** HTTP 200 streams carrying an upstream error now terminate with a structured failure instead of assistant text plus a normal stop, preserving partial output while allowing pre-content combo fallback. ([#12454](https://github.com/diegosouzapw/OmniRoute/pull/12454)) +- Absorb `Error [AbortError]: request_signal_aborted` and DOMException AbortError shapes in the process-level client-abort crash guard so routine client disconnects no longer kill the server (exit code 7). ([#12165](https://github.com/diegosouzapw/OmniRoute/pull/12165)) — thanks @alvinveroy +- **fix(sse):** Treat Adapta Web `type:error` SSE events as sanitized non-stream failures instead of empty HTTP 200 completions. ([#12459](https://github.com/diegosouzapw/OmniRoute/pull/12459)) +- **fix(autoCombo):** layer 3 (`models_dev_tier`) no longer scores retired models as premium, merge cross-provider capability rows deterministically, and inherit tier scores through variant ids ([#11508](https://github.com/diegosouzapw/OmniRoute/issues/11508)). `getModelsDevTierFitness` now vetoes ids present in `config/quality/model-lifecycle.json` with status `retired` before any other signal; `loadModelCapabilities()` aggregates per `model_id` (any-non-null-true for booleans, max for `limit_context`) instead of last-write-wins over SQLite's undefined row order; a capabilities miss retries through `resolveScoresAs()`'s base id and reports `models_dev_tier:inherited`, matching layers 1–2 (#11492). Adds `getModelsDevTierFitnessWithSource()` and an `invalidateCapabilitiesCache()` test/ops hook. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) — thanks @Meet6338-X +- fix(sse): accept Claude `content:[]` + `stop_reason: max_tokens`/`tool_use` in `detectMalformedNonStream` (match `isEmptyContentResponse`) so Claude Code `/model` probes with `max_tokens: 1` no longer become a false 502 ([#11568](https://github.com/diegosouzapw/OmniRoute/pull/11568)) — thanks @Seramicx +- **fix(security):** sanitize `request.failed` diagnostics before publishing them to live dashboard listeners and replay history, while keeping status, model, provider, latency, and internal call-log diagnostics intact. ([#12469](https://github.com/diegosouzapw/OmniRoute/pull/12469)) +- **fix(pwa):** do not intercept dashboard navigations so Chrome can fall back from a stale HTTP/3 Alt-Svc advertisement (UDP :20128 is unpublished; F5 on a long-lived tab hung until a new tab opened a fresh TCP connection). ([#12767](https://github.com/diegosouzapw/OmniRoute/pull/12767)) — thanks @HouMinXi +- **fix(executors):** handle DuckDuckGo ERR_BN_LIMIT (418) without retrying — when the upstream returns `418 ERR_BN_LIMIT` (rate-limit/ban), the executor now returns the error immediately instead of burning another VQD acquisition that would only count against the IP limit. The retry logic for `418 ERR_CHALLENGE` (unsolved challenge) remains unchanged. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) ([#12110](https://github.com/diegosouzapw/OmniRoute/pull/12110)) — thanks @oyi77 / @Meet6338-X +- Fixed DuckDuckGo streaming responses losing JSON lines and UTF-8 characters split across network chunks. ([#11528](https://github.com/diegosouzapw/OmniRoute/pull/11528)) — thanks @pacocartones +- Harden generated Cloudflare, Vercel, and Deno relays by stripping hop-by-hop, framing, and proxy authentication headers before forwarding requests upstream. ([#11533](https://github.com/diegosouzapw/OmniRoute/pull/11533)) — thanks @pacocartones +- **feat(grok-cli):** Provider Limits shows grok-cli banked reset credits from `GetRemainingResets` (including a real zero; a failed RPC omits the row) and the existing View credits button now calls `ConsumerUiSvc/RedeemReset` for grok-cli. Live tokens use nested fields 10/20/30 (id + Timestamp), not compact 1/2/3. ([#12805](https://github.com/diegosouzapw/OmniRoute/pull/12805)) — thanks @HouMinXi +- **fix(memory):** Embedding Model Quick select, Embedding Source remote dropdown, and Rerank selector now list every configured provider with embedding/rerank support instead of only chat-catalog text matches plus OpenRouter live discovery; a generic OpenAI-compatible `/embeddings` + Cohere-compatible `/rerank` runtime fallback resolves any configured chat provider's embedding/rerank endpoint, so unlisted providers no longer fail with "Unknown embedding provider"; both memory selectors gained a free-text model override ([#11390](https://github.com/diegosouzapw/OmniRoute/pull/11390)) — thanks @rqzbeh +- Harden SQLite upgrades around the historical migration-074 version collision: missing discovery and inspector tables are replayed atomically, pre-existing databases (including setup-created skeletons) receive reusable content-addressed safety snapshots, and Node test/eval probes without `DATA_DIR` are isolated from the operator database. ([#12435](https://github.com/diegosouzapw/OmniRoute/pull/12435)) +- **fix(dashboard):** Moonshot/Kimi Open Platform voucher and cash leftover percentages follow the bucket balance, so an empty wallet no longer paints those rows as 100% while Available is 0% ([#12733](https://github.com/diegosouzapw/OmniRoute/pull/12733)) — thanks @HouMinXi +- **fix(combo):** return non-retryable HTTP 400 when all candidates for a pinned native Codex turn are unavailable due to model-scoped lockout, terminating the turn cleanly while preserving turn continuity and enabling standard Combo routing on subsequent turns ([#12240](https://github.com/diegosouzapw/OmniRoute/pull/12240)) — thanks @mdigitalbh81 +- Fixed auto-router strategies preserving the selected connection through ranking and dispatch when multiple connections share the same provider and model. ([#11530](https://github.com/diegosouzapw/OmniRoute/pull/11530)) — thanks @pacocartones +- Preserve Chat Completions JSON-object response formats when translating requests to the Responses API. ([#11529](https://github.com/diegosouzapw/OmniRoute/pull/11529)) — thanks @pacocartones +- Fix idempotency fingerprints so Responses API requests include `input` and other semantic generation fields using stable serialization, while excluding credentials and request metadata. ([#11532](https://github.com/diegosouzapw/OmniRoute/pull/11532)) — thanks @pacocartones +- **fix(api):** Generated API CLI commands now enforce required OpenAPI request bodies; Combo test commands forward the required `comboName` body, while API keys created by older writers after migration 149 preserve legacy allow-all Combo access without widening explicit empty allowlists — thanks @marcelokarval ([#12070](https://github.com/diegosouzapw/OmniRoute/pull/12070)) +- **fix(grok-web):** treat upstream streaming failures as failures instead of successful assistant text: error-only streams now fail readiness with HTTP 502, while failures after legitimate content preserve that partial output and terminate through the sanitized stream failure path without a normal `stop` completion. ([#12458](https://github.com/diegosouzapw/OmniRoute/pull/12458)) +- HuggingChat now turns HTTP 200 JSONL generation failures into a sanitized 502 before content, or a fixed public stream failure after partial output, so fallback and request persistence no longer record a false successful stop. ([#12456](https://github.com/diegosouzapw/OmniRoute/pull/12456)) +- **fix(providers):** keep 1min.ai HTTP 200 stream errors out of assistant content, preserve partial output, and expose sanitized terminal errors so pre-content failures can fall back. ([#12466](https://github.com/diegosouzapw/OmniRoute/pull/12466)) +- **fix(sessions):** preserve legacy Sessions data when the additive exclusive-session projection is unavailable, returning an empty projection and warning only once per contiguous outage instead of failing the endpoint; the Sessions badge intentionally reflects the merged legacy and exclusive row count introduced by [#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389) ([#11469](https://github.com/diegosouzapw/OmniRoute/pull/11469)) — thanks @KaspaPulse +- Render Nimble Search with the generic provider icon and serve Opper's proven logo locally. ([#12415](https://github.com/diegosouzapw/OmniRoute/pull/12415)) +- **fix(quality):** `config/quality/provider-assets-provenance.jsonl` was stale against the physical `public/providers/` tree (still listing 225 assets and 5 duplicate-content groups after several provider-retirement PRs removed 85 icon files), which would have made `check:provider-asset-provenance` fail on the next full run. Reconciled the manifest to the real 142-file snapshot (142 asset records, 1 duplicate-content group, 2 newly-registered files marked `unresolved` pending provenance research), repinned `auditedCommit`, and updated the regression test's hardcoded counts to match. ([#11876](https://github.com/diegosouzapw/OmniRoute/pull/11876)) +- fix(ui): wrap quota card footer action buttons to prevent clipping Refresh button on narrow cards — when extra action buttons such as `View credits` are present on Codex cards, allow the footer row to flex-wrap with `shrink-0` buttons so `Refresh now` remains visible and clickable across all responsive card widths ([#11464](https://github.com/diegosouzapw/OmniRoute/pull/11464)) — thanks @adevwithpurpose +- Keep Antigravity Gemini usable when the same connection's Claude weekly quota is empty; generic quota cache stays per-connection for every other provider. ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) — thanks @HouMinXi +- Fixed Tailscale tunnel setup to reject ports outside the valid 1-65535 range and safely install Windows MSI packages from paths containing apostrophes. ([#11534](https://github.com/diegosouzapw/OmniRoute/pull/11534)) — thanks @pacocartones +- Classify Cloudflared and Tailscale process-management routes as local-only and spawn-capable while preserving remote access to read-only tunnel status endpoints. ([#11531](https://github.com/diegosouzapw/OmniRoute/pull/11531)) — thanks @pacocartones +- Electron release workflow: the `publish-npm` job now grants `actions: read` to the reusable `npm-publish.yml` it calls (its `publish` job requests it), which is what made GitHub refuse the whole v3.8.50 run at startup and ship the release with zero desktop assets; a `workflow_dispatch` now builds the requested tag instead of the dispatching branch and can skip the npm leg (`publish_npm=false`) when only re-attaching assets ([#11974](https://github.com/diegosouzapw/OmniRoute/pull/11974)) +- **fix(dashboard):** The Combos page usage guide now reads its dismissal through `useSyncExternalStore` instead of correcting SSR state inside an effect, removing an extra commit of the page tree on every load (and the `react-hooks/set-state-in-effect` error it raised). ([#12671](https://github.com/diegosouzapw/OmniRoute/pull/12671)) +- Electron release: `electron/package-lock.json` regained the optional `electron-builder-squirrel-windows` subtree (13 entries) that `npm ci` had been refusing as out of sync, `electron-release.yml` gained a `build_ref` dispatch input and stops regenerating release notes on a re-attach dispatch, and the npm publish workflow attaches the SBOM to the GitHub Release on dispatch publishes too — so the v3.8.51 tag ships every desktop asset and the SBOM like v3.8.49 did ([#12022](https://github.com/diegosouzapw/OmniRoute/pull/12022)) +- Drained the reds every PR against `release/v3.8.51` was born with: documented the three service-worker build-id variables, registered the six retirement/tunnel tests with the mutation gate, approved `eslint-plugin-react-hooks` in the dependency allowlist, added the six `combo.sort.*` strings to `vi` and `pt-BR`, pointed the ChatGPT Web doc at the real migration-168 test, worded the g4f hint around the member key, and realigned four tests to the retired-provider catalog and the legacy-schema fixtures the retirement migrations touch. ([#11940](https://github.com/diegosouzapw/OmniRoute/pull/11940)) +- **fix(providers):** Claude, Grok, LMArena, Notion, and Perplexity web-cookie transports now use pooled `wreq-js` 3.2 instead of the native sidecar, with all nine supported bindings pinned and audited, and the applicable platform binding plus native-license evidence included in each release artifact ([#12429](https://github.com/diegosouzapw/OmniRoute/pull/12429), supersedes [#11753](https://github.com/diegosouzapw/OmniRoute/pull/11753)). +- **fix(providers):** Zed Hosted streaming failures now trigger fallback before content and end partial streams with a sanitized structured error instead of fake assistant text and a normal-success stop. ([#12455](https://github.com/diegosouzapw/OmniRoute/pull/12455)) +- **fix(test):** capture hook result after render (direct commit `b85d0abb0b`) +- **fix(sync):** repair the two regressions the v3.8.50 sync-back left on release/v3.8.51 (direct commit `5ade9e0851`) +- **revert(models):** drop the memo reset hook ported from #10055 (direct commit `282de841ae`) +- **fix(typecheck):** guard the optional connectionId before the reactive model sync (direct commit `f2825bad75`) +- **fix(quota-share):** release the winner's reserved in-flight slot (#11371) ([#11408](https://github.com/diegosouzapw/OmniRoute/pull/11408)) — thanks @oyi77 +- **fix(providers):** stop silently dropping reasoning effort on opencode families (#10788) ([#11409](https://github.com/diegosouzapw/OmniRoute/pull/11409)) — thanks @oyi77 +- **fix(combo):** enable genuine quota-aware routing for generic providers (antigravity, claude, etc.) ([#11411](https://github.com/diegosouzapw/OmniRoute/pull/11411)) — thanks @HouMinXi +- **fix(oauth):** allow expired token retry before deactivation and activate expired retry loop (#P0) ([#11414](https://github.com/diegosouzapw/OmniRoute/pull/11414)) — thanks @HouMinXi +- **perf(executors):** lazy-load the executor registry — defer class imports + construction to first use (#11220) ([#11421](https://github.com/diegosouzapw/OmniRoute/pull/11421)) — thanks @oyi77 +- **fix:** make release/v3.8.50 CI gates green (run 32786966560 root causes) ([#11450](https://github.com/diegosouzapw/OmniRoute/pull/11450)) — thanks @jonlwheat2-gif +- **fix(sse):** exact-domain cookie match and origin-equality URL assertions ([#11452](https://github.com/diegosouzapw/OmniRoute/pull/11452)) — thanks @hartmark +- **fix(providers):** send Responses API payload during OpenAI-compatible validation ([#11454](https://github.com/diegosouzapw/OmniRoute/pull/11454)) — thanks @tuandinh0801 +- **fix(command-code):** fallback to /alpha/generate for Go plan without Provider API access ([#11455](https://github.com/diegosouzapw/OmniRoute/pull/11455)) — thanks @Chewji9875 +- **fix(responses-continuation):** fail closed on a log-truncated stored input/output array ([#11473](https://github.com/diegosouzapw/OmniRoute/pull/11473)) — thanks @hartmark +- **fix(cli):** write jcode settings as [providers.omniroute] in config.toml ([#11484](https://github.com/diegosouzapw/OmniRoute/pull/11484)) — thanks @hongnoul +- **fix(logging):** raise chat-log array truncation cap to 1000 and unify duplicate implementations ([#11499](https://github.com/diegosouzapw/OmniRoute/pull/11499)) — thanks @hartmark +- **fix:** restore live websocket URL helpers ([#11509](https://github.com/diegosouzapw/OmniRoute/pull/11509)) — thanks @PauloFH +- **perf(core):** eliminate async waterfalls across home, cache, and db operations (#11396) ([#11512](https://github.com/diegosouzapw/OmniRoute/pull/11512)) — thanks @HouMinXi +- **fix(kiro):** add runtime.us-east-1.kiro.dev as first-attempt endpoint ([#11517](https://github.com/diegosouzapw/OmniRoute/pull/11517)) — thanks @rifqiawl +- **fix(providers):** remove duplicate 128-tool truncation in OpencodeExecutor (#11444) ([#11518](https://github.com/diegosouzapw/OmniRoute/pull/11518)) — thanks @HouMinXi +- **fix(providers):** poll onboardUser LRO before classifying BYOP ([#11519](https://github.com/diegosouzapw/OmniRoute/pull/11519)) — thanks @rifqiawl +- **fix(translator):** preserve exact tool name casing for Claude tool_use payloads (#11487) ([#11520](https://github.com/diegosouzapw/OmniRoute/pull/11520)) — thanks @HouMinXi +- **fix(kimi-web):** align health probe with executor domain ([#11521](https://github.com/diegosouzapw/OmniRoute/pull/11521)) — thanks @pacocartones +- **fix(cli):** preserve existing admin password during setup ([#11522](https://github.com/diegosouzapw/OmniRoute/pull/11522)) — thanks @pacocartones +- **fix(mitm):** keep Windows DNS elevation off the POSIX sudo path (#11430) ([#11542](https://github.com/diegosouzapw/OmniRoute/pull/11542)) — thanks @official-burak +- **fix(sse):** scale chat admission by ingest byte budget instead of a fixed request count ([#11548](https://github.com/diegosouzapw/OmniRoute/pull/11548)) — thanks @TheDemonTuan +- **fix:** accept Gemini Web cookie exports ([#11554](https://github.com/diegosouzapw/OmniRoute/pull/11554)) — thanks @rafacpti23 +- **fix(i18n):** complete Brazilian Portuguese translations ([#11558](https://github.com/diegosouzapw/OmniRoute/pull/11558)) — thanks @rafacpti23 +- **fix(sse):** explicit types for openai-responses pureHelpers — clears last failing core typecheck gate ([#11567](https://github.com/diegosouzapw/OmniRoute/pull/11567)) — thanks @jonlwheat2-gif +- **fix(opencode):** hydrate Proxy Pool references for no-auth connections ([#11584](https://github.com/diegosouzapw/OmniRoute/pull/11584)) — thanks @AStupidBear +- **fix(ci):** resolve inherited release/v3.8.51 base-red CI failures ([#11608](https://github.com/diegosouzapw/OmniRoute/pull/11608)) — thanks @jonlwheat2-gif +- **fix(db):** fallback load STORAGE_ENCRYPTION_KEY from env files ([#11614](https://github.com/diegosouzapw/OmniRoute/pull/11614)) — thanks @benzntech +- **fix(providers):** mark Brave Search as no longer having a free tier ([#11621](https://github.com/diegosouzapw/OmniRoute/pull/11621)) — thanks @turbolego +- **fix:** pass tool result content through as string in OpenAI/Claude -> Gemini translation ([#11624](https://github.com/diegosouzapw/OmniRoute/pull/11624)) — thanks @Hsia97 +- **fix(sse):** scope credential health to exact key ([#11628](https://github.com/diegosouzapw/OmniRoute/pull/11628)) — thanks @KaspaPulse +- **fix(install):** guard prepare script against missing husky ([#11634](https://github.com/diegosouzapw/OmniRoute/pull/11634)) — thanks @Prajeeth-12 +- **fix(security):** redact NVIDIA_API_KEY at the log sink in the ad-hoc diag script ([#11639](https://github.com/diegosouzapw/OmniRoute/pull/11639)) +- **fix(types):** clear the two type regressions red on release/v3.8.51 ([#11640](https://github.com/diegosouzapw/OmniRoute/pull/11640)) — thanks @jonlwheat2-gif +- **fix(build):** resolve workspace: protocol dependencies to concrete semver versions during prepublish (#11540) ([#11641](https://github.com/diegosouzapw/OmniRoute/pull/11641)) — thanks @HouMinXi +- **fix(search):** properly resolve configured search connection in /v1/responses pipeline (#11524) ([#11642](https://github.com/diegosouzapw/OmniRoute/pull/11642)) — thanks @HouMinXi +- **fix(authz):** trust cf-connecting-ip for via-proxy detection when peer is Cloudflare edge (#11514) ([#11643](https://github.com/diegosouzapw/OmniRoute/pull/11643)) — thanks @HouMinXi +- **fix(security):** compare the OIDC state cookie in constant time ([#11646](https://github.com/diegosouzapw/OmniRoute/pull/11646)) +- **fix(security):** namespace the dedup hash by the calling API key ([#11649](https://github.com/diegosouzapw/OmniRoute/pull/11649)) +- **fix(models):** gate the Codex-native catalog loop on prefix mode (#11632) ([#11662](https://github.com/diegosouzapw/OmniRoute/pull/11662)) — thanks @xiaoyaner0201 +- **fix(dashboard):** project Codex routing quota in account pools ([#11666](https://github.com/diegosouzapw/OmniRoute/pull/11666)) — thanks @KaspaPulse +- **fix(ddgw):** normaliza system no upstream da release v3.8.51 ([#11692](https://github.com/diegosouzapw/OmniRoute/pull/11692)) — thanks @RhianB14 +- **fix(memory):** honor category filter in GET /api/memory ([#11699](https://github.com/diegosouzapw/OmniRoute/pull/11699)) — thanks @Sabeekhann +- **fix(ci):** detect stale workspace lockfile entries ([#11714](https://github.com/diegosouzapw/OmniRoute/pull/11714)) — thanks @pacocartones +- **fix(ci):** stamp dist/BUILD_SHA before the npm publish provenance gate ([#11721](https://github.com/diegosouzapw/OmniRoute/pull/11721)) +- **fix(docker):** decouple the best-effort Bun image from the release manifest ([#11724](https://github.com/diegosouzapw/OmniRoute/pull/11724)) +- **perf(compression):** avoid cloning memo misses twice ([#11727](https://github.com/diegosouzapw/OmniRoute/pull/11727)) — thanks @RaviTharuma +- **fix(docker):** let the bun digest artifact be absent, not fatal ([#11740](https://github.com/diegosouzapw/OmniRoute/pull/11740)) +- **fix(shared):** block cloud-metadata hosts under default remote-image guard ([#11755](https://github.com/diegosouzapw/OmniRoute/pull/11755)) — thanks @santosraju99-hub +- **fix(api):** preserve caller-provided X-Correlation-Id on chat completions ([#11760](https://github.com/diegosouzapw/OmniRoute/pull/11760)) — thanks @aniruddhaadak80 +- **fix(resilience):** lock permanently retired models instead of short backoff (Gemini ban prevention) ([#11762](https://github.com/diegosouzapw/OmniRoute/pull/11762)) — thanks @turbolego +- **fix:** mark Vercel AI Gateway as passthroughModels ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **fix(resilience):** stop hammering permanently-moved endpoints and billing-suspended accounts ([#11774](https://github.com/diegosouzapw/OmniRoute/pull/11774)) — thanks @turbolego +- **fix(lease):** remove global static reservation and gate routing on live active lease occupancy ([#11775](https://github.com/diegosouzapw/OmniRoute/pull/11775)) — thanks @TheDemonTuan +- **fix(ci):** size the install-upgrade gate to a measured run, and log the pack cost ([#11776](https://github.com/diegosouzapw/OmniRoute/pull/11776)) +- **fix(pwa):** stop serving the stale shell after deploys ([#11779](https://github.com/diegosouzapw/OmniRoute/pull/11779)) — thanks @HouMinXi +- **fix(combo):** lock GitHub models rejected as "not supported" for future requests ([#11781](https://github.com/diegosouzapw/OmniRoute/pull/11781)) — thanks @turbolego +- **fix(cli):** probe both IPv4 and IPv6 loopback for server readiness (#11766) ([#11794](https://github.com/diegosouzapw/OmniRoute/pull/11794)) — thanks @vermasomesh835 +- **fix(sse):** preserve CLIProxyAPI wrappers per connection ([#11797](https://github.com/diegosouzapw/OmniRoute/pull/11797)) — thanks @RaviTharuma +- **fix(providers):** honor model auto-fetch opt-in on create ([#11798](https://github.com/diegosouzapw/OmniRoute/pull/11798)) — thanks @RaviTharuma +- **fix(dashboard):** make model auto-fetch order independent ([#11805](https://github.com/diegosouzapw/OmniRoute/pull/11805)) — thanks @RaviTharuma +- **fix(docker):** make GHA cache exports best effort ([#11806](https://github.com/diegosouzapw/OmniRoute/pull/11806)) — thanks @RaviTharuma +- **fix(kiro):** do not permanently ban on 'User is not authorized to make this call' ([#11809](https://github.com/diegosouzapw/OmniRoute/pull/11809)) — thanks @Deftera186 +- **fix(sse):** keep cache-write tokens in OpenAI-shaped usage ([#11814](https://github.com/diegosouzapw/OmniRoute/pull/11814)) — thanks @TheDemonTuan +- **fix(docs):** resolve relative markdown and wiki links across Fumadocs and GitHub wiki ([#11834](https://github.com/diegosouzapw/OmniRoute/pull/11834)) — thanks @AndrianBalanescu +- **fix(chat):** preserve unstripped model string for passthrough provider routing ([#11840](https://github.com/diegosouzapw/OmniRoute/pull/11840)) — thanks @raheemuddin786 +- **fix(mcp):** avoid async initialization inside sync esm wrappers (#11569) ([#11849](https://github.com/diegosouzapw/OmniRoute/pull/11849)) — thanks @HouMinXi +- **fix(sse):** preserve coherent markdown token boundaries across stream translation chunks (#11606) ([#11851](https://github.com/diegosouzapw/OmniRoute/pull/11851)) — thanks @HouMinXi +- **fix(ci):** run the install-upgrade gate on disk, not on the /tmp tmpfs ([#11855](https://github.com/diegosouzapw/OmniRoute/pull/11855)) +- **fix(skills):** expand shorthand property types in injected tool schemas ([#11857](https://github.com/diegosouzapw/OmniRoute/pull/11857)) — thanks @alvinveroy +- **fix(ci):** publish npm from a hosted runner so provenance is accepted ([#11877](https://github.com/diegosouzapw/OmniRoute/pull/11877)) +- **fix(lint):** pin react hooks plugin to 7.0.1 ([#11886](https://github.com/diegosouzapw/OmniRoute/pull/11886)) +- **fix(ci):** surface and clear release-green ESLint failure ([#11890](https://github.com/diegosouzapw/OmniRoute/pull/11890)) — thanks @MumuTW +- **fix(guardrails):** prevent duplicate prompt-injection-guard log output ([#11936](https://github.com/diegosouzapw/OmniRoute/pull/11936)) — thanks @patrykkopycinski +- **fix(auth):** downgrade expected transient states from warn to debug ([#11937](https://github.com/diegosouzapw/OmniRoute/pull/11937)) — thanks @patrykkopycinski +- **fix(codex):** fail over image generation for imported free plans ([#11948](https://github.com/diegosouzapw/OmniRoute/pull/11948)) — thanks @Bl0ck154 +- **fix(codex):** normalize non-stream responses ([#11951](https://github.com/diegosouzapw/OmniRoute/pull/11951)) — thanks @Bl0ck154 +- **fix(images):** forward Antigravity image size ([#11952](https://github.com/diegosouzapw/OmniRoute/pull/11952)) — thanks @Bl0ck154 +- **fix(db):** invalidate connection cache after upsert ([#11953](https://github.com/diegosouzapw/OmniRoute/pull/11953)) — thanks @Bl0ck154 +- **fix(codex):** restore imported account state ([#11954](https://github.com/diegosouzapw/OmniRoute/pull/11954)) — thanks @Bl0ck154 +- **fix(sse):** give extended-thinking targets the reasoning readiness budget ([#11959](https://github.com/diegosouzapw/OmniRoute/pull/11959)) — thanks @abhisheksharma2411 +- **fix(dashboard):** use opaque background and readable text color on cost chart tooltips ([#11960](https://github.com/diegosouzapw/OmniRoute/pull/11960)) — thanks @ZaimMarzuki +- **fix(antigravity):** send complete loadCodeAssist metadata (ideType/platform/pluginType as numeric enums) ([#11969](https://github.com/diegosouzapw/OmniRoute/pull/11969)) — thanks @rifqiawl +- **fix(core):** resolve DB init race condition and reasoning translation ([#12003](https://github.com/diegosouzapw/OmniRoute/pull/12003)) — thanks @raheemuddin786 +- **fix(admission):** exclude reclaimable page cache from the cgroup pressure ratio ([#12017](https://github.com/diegosouzapw/OmniRoute/pull/12017)) — thanks @HouMinXi +- **fix(build):** prune non-production trees in NFT trace excludes and tsconfig ([#12028](https://github.com/diegosouzapw/OmniRoute/pull/12028)) — thanks @Chewji9875 +- **fix(migrations):** add renamed migration compatibility for 056/073/077/101 ([#12036](https://github.com/diegosouzapw/OmniRoute/pull/12036)) — thanks @oyi77 +- **fix(sse):** default crash-guard logger to console.warn, not console ([#12042](https://github.com/diegosouzapw/OmniRoute/pull/12042)) — thanks @HouMinXi +- **fix(catalog):** derive vision/modalities for built-in auto combos from effective target pool ([#12046](https://github.com/diegosouzapw/OmniRoute/pull/12046)) — thanks @Prajeeth-12 +- **fix(release):** the packaged-app smoke verifies the database opened, not a driver line the primary path never prints (twin of #12032) ([#12047](https://github.com/diegosouzapw/OmniRoute/pull/12047)) +- **fix(barrel):** migrate src/app/ off the @/lib/localDb barrel import (#11795 Phase 2) ([#12051](https://github.com/diegosouzapw/OmniRoute/pull/12051)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate src/lib/ off the localDb barrel to direct db imports (#59) ([#12052](https://github.com/diegosouzapw/OmniRoute/pull/12052)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate open-sse, src/shared, src/sse, src/models, src/domain off the @/lib/localDb barrel import (#11795 Phase 4) ([#12053](https://github.com/diegosouzapw/OmniRoute/pull/12053)) — thanks @jonlwheat2-gif +- **fix(barrel):** delete the @/lib/localDb barrel — every consumer migrated (#11795 Phase 5) ([#12055](https://github.com/diegosouzapw/OmniRoute/pull/12055)) — thanks @jonlwheat2-gif +- **fix(radar):** restore D12 public boundary ([#12057](https://github.com/diegosouzapw/OmniRoute/pull/12057)) +- **fix(dev):** bound webpack and Tailwind scans ([#12075](https://github.com/diegosouzapw/OmniRoute/pull/12075)) — thanks @backryun +- **fix(dev):** isolate root layout settings reads ([#12076](https://github.com/diegosouzapw/OmniRoute/pull/12076)) — thanks @backryun +- **fix(dev):** reduce instrumentation executor fan-out (phase 3) ([#12078](https://github.com/diegosouzapw/OmniRoute/pull/12078)) — thanks @backryun +- **fix(dev):** make logging resources HMR-singleton ([#12079](https://github.com/diegosouzapw/OmniRoute/pull/12079)) — thanks @backryun +- **fix(sse):** keep unavailable forced connections scoped ([#12080](https://github.com/diegosouzapw/OmniRoute/pull/12080)) — thanks @keeltrace +- **fix(dev):** isolate batch dispatch from instrumentation ([#12081](https://github.com/diegosouzapw/OmniRoute/pull/12081)) — thanks @backryun +- **fix(ci):** reconcile release test contract drift ([#12082](https://github.com/diegosouzapw/OmniRoute/pull/12082)) — thanks @backryun +- **fix(ci):** accept CVE-2025-68121 in the prebuilt tls-client .so, auto-close base-red issues, guard Scorecard on the default branch ([#12085](https://github.com/diegosouzapw/OmniRoute/pull/12085)) +- **fix(db):** drop three consumer-less 1proxy exports — dead-code base-red on release/v3.8.51 after the barrel deletion (#12055) ([#12087](https://github.com/diegosouzapw/OmniRoute/pull/12087)) +- **fix(api):** clear the six API-route TypeScript regressions the new gate landed red on ([#12094](https://github.com/diegosouzapw/OmniRoute/pull/12094)) +- **fix(release):** never let the tag-push Create Release append auto notes to the curated body ([#12096](https://github.com/diegosouzapw/OmniRoute/pull/12096)) +- **fix(sse):** honor CLIProxyAPI environment API key ([#12099](https://github.com/diegosouzapw/OmniRoute/pull/12099)) — thanks @RaviTharuma +- **fix(oauth):** bind Google refresh to the client that issued the token ([#12106](https://github.com/diegosouzapw/OmniRoute/pull/12106)) — thanks @HouMinXi +- **fix(dashboard):** make RequestLoggerDetail loadable outside Next — CSS via globals.css + CJS/ESM interop (#11703 base-reds) ([#12114](https://github.com/diegosouzapw/OmniRoute/pull/12114)) +- **fix(leases):** project status lease row to lease columns so joined connection PII never escapes ([#12115](https://github.com/diegosouzapw/OmniRoute/pull/12115)) — thanks @geek007git +- **fix(plugins):** do not kill the plugin process when a fire-and-forget hook times out ([#12116](https://github.com/diegosouzapw/OmniRoute/pull/12116)) — thanks @geek007git +- **fix(guardrails):** restore injection-guard logging on middleware-only routes ([#12117](https://github.com/diegosouzapw/OmniRoute/pull/12117)) — thanks @geek007git +- **fix(plugins):** refresh stored manifest from disk on activate so new hook fields reach existing installs ([#12120](https://github.com/diegosouzapw/OmniRoute/pull/12120)) — thanks @geek007git +- **fix(codex):** preserve existing provider state when bulk-import upserts a matching connection ([#12122](https://github.com/diegosouzapw/OmniRoute/pull/12122)) — thanks @geek007git +- **fix(ci):** clear the two base-reds the 2026-08-30 merge batch left on release/v3.8.51 (round 3) ([#12123](https://github.com/diegosouzapw/OmniRoute/pull/12123)) +- **fix(sse):** strip type:'custom' from Claude tools on agentrouter dispatch ([#12126](https://github.com/diegosouzapw/OmniRoute/pull/12126)) — thanks @wahidsadik371-coder +- **fix(sse):** stop advertising video providers the dispatcher cannot run ([#12131](https://github.com/diegosouzapw/OmniRoute/pull/12131)) — thanks @quiterunner-commits +- **fix(search):** treat HTTP 432 and plan limit errors as transient cooldown ([#12139](https://github.com/diegosouzapw/OmniRoute/pull/12139)) — thanks @HouMinXi +- **fix(sse):** refuse an AI Horde queue that cannot fit the request budget ([#12143](https://github.com/diegosouzapw/OmniRoute/pull/12143)) — thanks @quiterunner-commits +- **fix(ci):** clear the base-reds the afternoon merge batch left on release/v3.8.51 (round 5: provider count 352, TS2554/TS2677) ([#12144](https://github.com/diegosouzapw/OmniRoute/pull/12144)) +- **fix(translator):** strip neutral tool_choice when tools absent in Responses-to-Chat (#12141) ([#12166](https://github.com/diegosouzapw/OmniRoute/pull/12166)) — thanks @HouMinXi +- **fix(guardrails):** pass providerId to getResolvedModelCapabilities in checkComboVision (#12112) ([#12169](https://github.com/diegosouzapw/OmniRoute/pull/12169)) — thanks @HouMinXi +- **fix(translator):** strip plaintext reasoning content for opaque responses backends (#12128) ([#12171](https://github.com/diegosouzapw/OmniRoute/pull/12171)) — thanks @HouMinXi +- **perf(stream):** compile hot-path regexes once, bound token caches, fix quadratic buffering ([#12179](https://github.com/diegosouzapw/OmniRoute/pull/12179)) — thanks @opensource-elearning +- **fix:** resolve compression worker file using runtime anchors instead of… ([#12183](https://github.com/diegosouzapw/OmniRoute/pull/12183)) — thanks @NightStalker-87 +- **fix:** prevent Claude Code session kills via liveness-aware readiness + auto model echo ([#12189](https://github.com/diegosouzapw/OmniRoute/pull/12189)) — thanks @opensource-elearning +- **fix(translator):** preserve falsy primitive values in Gemini and Antigravity function response results ([#12191](https://github.com/diegosouzapw/OmniRoute/pull/12191)) — thanks @Siva010 +- **fix(oauth):** mark empty Antigravity projectId as degraded and clear stale errors (#11284) ([#12205](https://github.com/diegosouzapw/OmniRoute/pull/12205)) — thanks @HouMinXi +- **fix(providers):** gate the Codex auto-ping usage read on the shared quota throttle ([#12209](https://github.com/diegosouzapw/OmniRoute/pull/12209)) — thanks @abhisheksharma2411 +- **fix(combos):** prioritize SQLite row id over inner JSON id and notify delete errors ([#12213](https://github.com/diegosouzapw/OmniRoute/pull/12213)) — thanks @HouMinXi +- **fix(providers):** mark featherless.ai as no longer having a free tier (`hasFree: false`) ([#12216](https://github.com/diegosouzapw/OmniRoute/pull/12216)) — thanks @turbolego +- **fix(deepseek-web):** stop Turbopack dev panic in the PoW worker path resolver ([#12221](https://github.com/diegosouzapw/OmniRoute/pull/12221)) — thanks @hartmark +- **fix(chat-admission):** clarify local 503 source ([#12223](https://github.com/diegosouzapw/OmniRoute/pull/12223)) — thanks @keeltrace +- **fix(dev):** silence webpack runtime module warnings ([#12228](https://github.com/diegosouzapw/OmniRoute/pull/12228)) — thanks @backryun +- **fix(db):** use module.require for CommonJS runtime driver loading ([#12230](https://github.com/diegosouzapw/OmniRoute/pull/12230)) — thanks @raheemuddin786 +- **fix(memory):** honest probe-driven FTS5 keyword status + memory_id rowid sync ([#12231](https://github.com/diegosouzapw/OmniRoute/pull/12231)) — thanks @raheemuddin786 +- **fix(system):** propagate abort signal to stream reader in HTTP version checks ([#12232](https://github.com/diegosouzapw/OmniRoute/pull/12232)) — thanks @raheemuddin786 +- **fix(resilience):** isolate local host execution errors from provider circuit breakers ([#12233](https://github.com/diegosouzapw/OmniRoute/pull/12233)) — thanks @raheemuddin786 +- **fix(sse):** sort injected tools deterministically for prompt caching ([#12234](https://github.com/diegosouzapw/OmniRoute/pull/12234)) — thanks @raheemuddin786 +- **fix(combo):** always clear the loop-safety timer, not just on the happy path (#11804) ([#12245](https://github.com/diegosouzapw/OmniRoute/pull/12245)) +- **fix(quality):** base-red pair — stryker tap registration + turn-pin suites aligned to the window gate ([#12255](https://github.com/diegosouzapw/OmniRoute/pull/12255)) — thanks @backryun +- **fix(dev):** qualify Turbopack runtime boundaries ([#12258](https://github.com/diegosouzapw/OmniRoute/pull/12258)) — thanks @backryun +- **fix(combo):** fall back to full pool when collapsed sole survivor is context-too-small ([#12278](https://github.com/diegosouzapw/OmniRoute/pull/12278)) — thanks @opensource-elearning +- **fix(combo):** bound the pre-dispatch unavailable skip so a stale label cannot dark a pool (#12168) ([#12285](https://github.com/diegosouzapw/OmniRoute/pull/12285)) +- **fix(security):** strip Qwen/Alibaba console-session cookies from provider API responses ([#12287](https://github.com/diegosouzapw/OmniRoute/pull/12287)) +- **fix(usage):** console-aware Token Plan guidance + subscription hint on bailian 401 ([#12288](https://github.com/diegosouzapw/OmniRoute/pull/12288)) +- **fix(resourcePressure):** log numeric detail on every rejection, recover faster ([#12293](https://github.com/diegosouzapw/OmniRoute/pull/12293)) — thanks @hartmark +- **fix(release):** drain the 2026-09-01 base-red window — passthrough usage regression + radarPage i18n keys ([#12327](https://github.com/diegosouzapw/OmniRoute/pull/12327)) +- **fix(ci):** openapi-security-tiers checker must honor routeGuard patterns + imported prefixes ([#12350](https://github.com/diegosouzapw/OmniRoute/pull/12350)) — thanks @ggiak +- **perf(ci):** cache node_modules in the npm-ci-retry composite (#8084 D3) ([#12408](https://github.com/diegosouzapw/OmniRoute/pull/12408)) +- **revert(ui):** point CTAs back at their real destinations ([#12410](https://github.com/diegosouzapw/OmniRoute/pull/12410)) +- **fix(release):** drain the 2026-09-02 base-red — rerank-providers import + api-typecheck baseline ratchet ([#12414](https://github.com/diegosouzapw/OmniRoute/pull/12414)) +- **fix(types):** make system prompt injection noImplicitAny-safe ([#12416](https://github.com/diegosouzapw/OmniRoute/pull/12416)) +- **fix(quality):** validate typecheck baseline schema ([#12419](https://github.com/diegosouzapw/OmniRoute/pull/12419)) +- **fix(memory):** resolve rerank provider node cache import ([#12421](https://github.com/diegosouzapw/OmniRoute/pull/12421)) +- **fix(ci):** repair release-root regressions — pack dedup, web-session syntax, uc-image ids ([#12423](https://github.com/diegosouzapw/OmniRoute/pull/12423)) — thanks @backryun +- **fix(providers):** separate MaxAI and UC credential contracts ([#12431](https://github.com/diegosouzapw/OmniRoute/pull/12431)) +- **fix(cli):** preserve Claude settings on config set ([#12432](https://github.com/diegosouzapw/OmniRoute/pull/12432)) — thanks @KrzysiekSko +- **fix(providers):** repair the maxai credential block truncated by merge auto-resolve ([#12433](https://github.com/diegosouzapw/OmniRoute/pull/12433)) +- **fix(quality):** record the 2026-09-02 merged growth in the file-size baseline ([#12434](https://github.com/diegosouzapw/OmniRoute/pull/12434)) +- **fix(uc):** route UC error strings through sanitizeErrorMessage; allowlist the retired codex id ([#12437](https://github.com/diegosouzapw/OmniRoute/pull/12437)) +- **fix(adapta):** redact streamed upstream errors ([#12438](https://github.com/diegosouzapw/OmniRoute/pull/12438)) +- **fix(sse):** remap non-contiguous upstream tool_calls index to a gap-free output_index ([#12445](https://github.com/diegosouzapw/OmniRoute/pull/12445)) — thanks @hartmark +- **fix(quality):** detect duplicate tool_calls entries in one response ([#12446](https://github.com/diegosouzapw/OmniRoute/pull/12446)) — thanks @hartmark +- **fix(conversations):** resolve turn content OmniRoute never sends back to the client ([#12447](https://github.com/diegosouzapw/OmniRoute/pull/12447)) — thanks @hartmark +- **fix(combo):** do not treat credits-exhausted 401 as auth skip ([#12449](https://github.com/diegosouzapw/OmniRoute/pull/12449)) — thanks @RaviTharuma +- **fix(auth):** do not park healthy quota accounts as expired ([#12452](https://github.com/diegosouzapw/OmniRoute/pull/12452)) — thanks @RaviTharuma +- **fix(responses-continuation):** fail closed on a collector-truncated, empty output array ([#12460](https://github.com/diegosouzapw/OmniRoute/pull/12460)) — thanks @hartmark +- **fix(logging):** raise the SSE payload collector's default cap ([#12461](https://github.com/diegosouzapw/OmniRoute/pull/12461)) — thanks @hartmark +- **fix(resilience):** surface Responses failed.error.message in 502s ([#12472](https://github.com/diegosouzapw/OmniRoute/pull/12472)) — thanks @RaviTharuma +- **fix(security):** unbiased maxai X-Random nonce + stricter URL/regex assertions ([#12502](https://github.com/diegosouzapw/OmniRoute/pull/12502)) +- **fix(video):** re-anchor transcript log-redaction so PII/credential maskers can't reopen the leak (#12150 P1 follow-up) ([#12503](https://github.com/diegosouzapw/OmniRoute/pull/12503)) +- **fix(monitoring):** serve cached credentialHealth off the request path ([#12533](https://github.com/diegosouzapw/OmniRoute/pull/12533)) — thanks @RaviTharuma +- **fix(quota):** keep Antigravity Gemini usable when Claude weekly is empty ([#12566](https://github.com/diegosouzapw/OmniRoute/pull/12566)) — thanks @HouMinXi +- **fix(authz):** hard-gate every credential export and CLI-config write (GHSA-5926-2w35-7h4q) ([#12600](https://github.com/diegosouzapw/OmniRoute/pull/12600)) +- **fix(docs):** restore the Next build — REMOVED_PROVIDERS.md had no frontmatter (base-red #12581) ([#12610](https://github.com/diegosouzapw/OmniRoute/pull/12610)) +- **fix(quality):** re-point the zcodeProtocol public-creds allowlist to line 313 ([#12615](https://github.com/diegosouzapw/OmniRoute/pull/12615)) +- **fix(security):** close 3 advisories — search baseUrl exfil, sk- in the error sanitizer, bifrost relay header leak ([#12620](https://github.com/diegosouzapw/OmniRoute/pull/12620)) +- **fix(providers):** sanitize boolean required and nested bare maps for Gemini (#12269) ([#12624](https://github.com/diegosouzapw/OmniRoute/pull/12624)) — thanks @HouMinXi +- **fix(resilience):** keep Overloaded STREAM_EARLY_EOF off the provider breaker ([#12626](https://github.com/diegosouzapw/OmniRoute/pull/12626)) — thanks @HouMinXi +- **fix(catalog):** re-audit free-tier quotas against official pages ([#12649](https://github.com/diegosouzapw/OmniRoute/pull/12649)) +- **fix(dashboard):** keep a request's pending-tracking id stable across combo target retries ([#12650](https://github.com/diegosouzapw/OmniRoute/pull/12650)) — thanks @hartmark +- **fix(tests):** retire dead model ids from the chat-pipeline integration suite (base-red #12581) ([#12670](https://github.com/diegosouzapw/OmniRoute/pull/12670)) +- **fix:** resolve SqliteError no such table compression_run_telemetry during cleanup ([#12682](https://github.com/diegosouzapw/OmniRoute/pull/12682)) — thanks @groovecityJO +- **fix(combo):** treat a pin-only step as implicit connection allowlist ([#12697](https://github.com/diegosouzapw/OmniRoute/pull/12697)) — thanks @HouMinXi +- **fix(ci):** ship .npmrc in published package so legacy-peer-deps applies to consumers (#11544) ([#12699](https://github.com/diegosouzapw/OmniRoute/pull/12699)) — thanks @KooshaPari +- **fix(docker):** pin 4 CLI tools to exact versions (#12576) ([#12703](https://github.com/diegosouzapw/OmniRoute/pull/12703)) — thanks @KooshaPari +- **fix(docker):** document and harden cli profile trust boundary (#12570) ([#12706](https://github.com/diegosouzapw/OmniRoute/pull/12706)) — thanks @KooshaPari +- **fix(sse):** redact video transcript in pre-guardrail rejected-request logs (#12150 P2 item 7) ([#12710](https://github.com/diegosouzapw/OmniRoute/pull/12710)) +- **fix(i18n):** quote placeholder in OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES description (#12505) ([#12769](https://github.com/diegosouzapw/OmniRoute/pull/12769)) — thanks @KooshaPari +- **fix(api/pricing):** surface validation error message as string, not raw object (#12494) ([#12771](https://github.com/diegosouzapw/OmniRoute/pull/12771)) — thanks @KooshaPari +- **fix(sse):** re-enable prompt compression for native Codex passthrough ([#12834](https://github.com/diegosouzapw/OmniRoute/pull/12834)) — thanks @thomasmaerz +- **fix(dashboard):** model health tests for a provider node set to the Responses API now call `/v1/responses` with a Responses-shaped body instead of `/v1/chat/completions` — those models were reported as `Provider returned HTTP 200 but no text content` even though the same model answered normally through `/v1/responses` ([#13070](https://github.com/diegosouzapw/OmniRoute/issues/13070)) ([#13087](https://github.com/diegosouzapw/OmniRoute/pull/13087)) — thanks @ntdatt812 +- fix(routing): stop a round-robin combo's "opencode" targets from collapsing onto the opencode-zen connection (#11912) ([#13283](https://github.com/diegosouzapw/OmniRoute/pull/13283)) +- fix(routing): stop the reactive-compaction debug log from lying when compression is globally disabled (#11977) ([#13276](https://github.com/diegosouzapw/OmniRoute/pull/13276)) +- fix(electron): relativize standalone-bundle symlink targets so Stage 8 manifest verification stops failing on Windows (#11979) ([#13251](https://github.com/diegosouzapw/OmniRoute/pull/13251)) +- fix(dashboard): surface a visible error when Compression Studio's combined preview run fails (#12061) ([#13288](https://github.com/diegosouzapw/OmniRoute/pull/13288)) +- fix(dashboard): make the compression "Effective pipeline" preview honor the active profile and warn when the master switch is off (#12063) ([#13287](https://github.com/diegosouzapw/OmniRoute/pull/13287)) +- fix(providers): scope TinyCMS Web signer's DOM shims to each call instead of leaking them for the process lifetime, and surface a clean HTTP status on a non-JSON interception-toggles error (#12072) ([#13254](https://github.com/diegosouzapw/OmniRoute/pull/13254)) +- fix(guardrails): stop Vision Bridge from re-selecting a model locked after a 404 (#12111) ([#13259](https://github.com/diegosouzapw/OmniRoute/pull/13259)) +- fix(sse): require Responses-shaped body before native OpenAI-compatible passthrough (#12129) ([#13278](https://github.com/diegosouzapw/OmniRoute/pull/13278)) +- fix(providers): minimax-m3 now collapses manual thinking.type:"enabled" to adaptive, preventing upstream 400 (2013) (#12132) ([#13249](https://github.com/diegosouzapw/OmniRoute/pull/13249)) +- fix(db): scope model visibility overrides by modality so hiding a Chat model no longer hides an identically-ID'd Image/Embeddings/etc. model (#12172) ([#13258](https://github.com/diegosouzapw/OmniRoute/pull/13258)) +- fix(db): scope local-provider apiKey dedup to matching base URL so LM Studio/Ollama-style connections support multiple accounts (#12173) ([#13250](https://github.com/diegosouzapw/OmniRoute/pull/13250)) +- fix(providers): refresh Trae's stale Referer/Origin and forward user timezone so imported connections stop failing with 401 (#12190) ([#13255](https://github.com/diegosouzapw/OmniRoute/pull/13255)) +- fix(providers): route opencode-go/gpt-5.6-luna to /responses instead of /chat/completions (#12196) ([#13275](https://github.com/diegosouzapw/OmniRoute/pull/13275)) +- fix(dashboard): allow deleting the last extra-upstream-header row even when invalid (#12251) ([#13252](https://github.com/diegosouzapw/OmniRoute/pull/13252)) +- **fix(i18n):** translate pre-existing `__MISSING__:` keys for `combo.sort`, `requestLogger.detail` expand/collapse, `common.profile`, and `settings.resilienceCredentialHealth*` across 39 locales ([#12272](https://github.com/diegosouzapw/OmniRoute/issues/12272)) ([#13034](https://github.com/diegosouzapw/OmniRoute/pull/13034)) — thanks @HouMinXi +- fix(cli): run the Node.js runtime compatibility guard before the heavy `tsx/esm` + Commander import chain so an unsupported runtime gets a clear message instead of a raw `Invalid regular expression flags` crash (#12296) ([#13253](https://github.com/diegosouzapw/OmniRoute/pull/13253)) +- fix(dashboard): refresh the providers list after deleting a compatible provider node (#12298) ([#13256](https://github.com/diegosouzapw/OmniRoute/pull/13256)) +- fix(usage): fail closed on API-key budget enforcement when a provider's `auto` routing alias has no pricing row, instead of silently counting it as $0 (#12341) ([#13257](https://github.com/diegosouzapw/OmniRoute/pull/13257)) +- **fix(providers):** include the submitted Agnes video model when polling by `video_id` ([#12356](https://github.com/diegosouzapw/OmniRoute/pull/12356)) — thanks @pacocartones +- **fix(routing):** custom OpenAI-compatible nodes now honor the saved Chat/Responses API type after edits instead of letting the node's original ID prefix override the live connection setting ([#11884](https://github.com/diegosouzapw/OmniRoute/issues/11884)). ([#12358](https://github.com/diegosouzapw/OmniRoute/pull/12358)) — thanks @pacocartones +- fix(sse): surface an error instead of a silent empty 200 when a Claude stream closes with zero bytes (#12398) ([#13285](https://github.com/diegosouzapw/OmniRoute/pull/13285)) +- fix(oauth): warn before the dead localhost:8080 redirect in antigravity/gemini `oauth start` (#12413) ([#13265](https://github.com/diegosouzapw/OmniRoute/pull/13265)) +- fix(providers): stop devin-cli spawn error from double-closing the SSE controller (#12517) ([#13261](https://github.com/diegosouzapw/OmniRoute/pull/13261)) +- **fix(rerank):** clamp Voyage `top_k` to the documents actually sent after empty-string filtering, and honor `return_documents: false` in the NVIDIA response adapter (#12523 — thanks @pacocartones) +- **fix(audio):** `/v1/audio/translations` now resolves combo names the way `/v1/audio/transcriptions` already does, so a combo that `GET /v1/models` advertises is fanned out to its targets instead of being rejected with `400 Invalid translation model: . Use format: provider/model`; literal `provider/model` ids and unknown bare names behave as before (#12536 — thanks @pacocartones) +- **fix(gemini):** strip the JSON-Schema-2020-12 `prefixItems` keyword from Gemini tool schemas at every nesting level, so Claude Code tool definitions no longer fail with `400 Unknown name "prefixItems"` on Gemini models (#12540 — thanks @pacocartones) +- **fix(api-manager):** Expose an accessible loading status while API keys are fetched instead of an empty accessibility tree (#12541 — thanks @pacocartones) +- **fix(video):** Clamp `estimateJpegFrameBytes` at zero for padding-only payloads and build the three encode-side frame data URIs from `JPEG_FRAME_DATA_URI_PREFIX` instead of a repeated literal (#12543 — thanks @pacocartones) +- **fix(devin):** accept Windows `DEVIN_AGENTIC_HOME` sandbox paths (`C:\...\.sandbox\...`) in the isolated-home check so the Devin Claude Bridge no longer fails closed on Windows ([#12405](https://github.com/diegosouzapw/OmniRoute/issues/12405)) (#12545 — thanks @pacocartones) +- **fix(db):** Add `conversation_turn_nodes` and orphaned `agentic_conversations` to the auto-cleanup cycle under the existing `retention.callLogs` window, so identity nodes whose call-log content has already been purged no longer accumulate without bound in `storage.sqlite` (#12548 — thanks @pacocartones) +- **fix(i18n):** Wrap the `~/.claude/profiles//settings.json` placeholder in ICU single quotes in the `OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES` feature-flag description across all 42 locales and the TypeScript default, so next-intl no longer fails with `INVALID_MESSAGE: UNCLOSED_TAG` and the Feature Flags card shows the description instead of the raw key (#12549 — thanks @pacocartones) +- **fix(orchestration):** `updateCloudAgentTask` now publishes the task's real `status` on `agent.task.updated` when an update only touches `result`, `activities` or `error`, instead of the fabricated `"updated"` state, and stays silent when no row matched the id (#12550 — thanks @pacocartones) +- **fix(i18n):** the home "Recent Requests" panel and the Provider Topology legend are now translated instead of rendering English copies on non-English dashboards; the legend reads its own `home.topologyLegend*` labels with consistent casing rather than borrowing the memory-settings "Recent" and analytics "Error" strings (#12551 — thanks @pacocartones). +- **docs(reference):** bring the `FEATURE_FLAGS.md` catalog back to 1:1 with `featureFlagDefinitions.ts` — 20 missing flags added, the two `*_BLOCK_THRESHOLD` env-only knobs moved out of the flag tables, category/total counts and the Live WS port corrected, guarded by a static test (#12552 — thanks @pacocartones) +- **fix(i18n):** backfill missing `usage.kiloPass*` strings in 39 locales and restore `featureFlagOmnirouteDisableThinkingLevelVariantsDescription` in `pt.json` ([#12561](https://github.com/diegosouzapw/OmniRoute/issues/12561)) ([#13027](https://github.com/diegosouzapw/OmniRoute/pull/13027)) — thanks @HouMinXi +- fix(docker): default docker-compose app ports (dashboard/API/live-WS) to loopback instead of `0.0.0.0`, closing the anonymous `/v1` LAN/WAN exposure gap left open by `REQUIRE_API_KEY=false` (#12568) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): close DNS-rebinding SSRF gap in webhook outbound-URL guard (#12569) ([#13243](https://github.com/diegosouzapw/OmniRoute/pull/13243)) +- fix(docker): require a per-session token on the VNC browser CDP bridge and isolate it on a dedicated Docker network (#12571) ([#13241](https://github.com/diegosouzapw/OmniRoute/pull/13241)) +- fix(open-sse): write Adobe Firefly session tokens and cookie jars with 0700/0600 permissions instead of the process umask (#12572) ([#13242](https://github.com/diegosouzapw/OmniRoute/pull/13242)) +- fix(api): remove hardcoded wildcard CORS in openai-to-gemini-sse.ts so the centralized fail-closed CORS gate is the sole source of `Access-Control-Allow-Origin` (#12573) ([#13239](https://github.com/diegosouzapw/OmniRoute/pull/13239)) +- fix(api): enforce API key policy (budget/rate-limit/schedule/endpoint scoping) on the ElevenLabs speech-to-text, text-to-speech and voices proxy routes (#12574) ([#13244](https://github.com/diegosouzapw/OmniRoute/pull/13244)) +- fix(sse): cap HuggingChat NDJSON body size and bound the read loop with the fetch timeout so a stalled or hostile upstream cannot buffer unbounded memory (#12577) ([#13282](https://github.com/diegosouzapw/OmniRoute/pull/13282)) +- fix(docker): scope the cliproxyapi/qdrant/bifrost sidecars to loopback by default and forward `CLIPROXYAPI_MANAGEMENT_KEY` into the cliproxyapi container so its management API is not left both unauthenticated and LAN-published (#12578) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): create DB export temp paths with `fs.mkdtempSync` instead of predictable timestamps (#12579) ([#13237](https://github.com/diegosouzapw/OmniRoute/pull/13237)) +- Cline 401 bodies that say "re-authenticate your Cline account" classify as a refreshable OAuth token, not a terminal expired key. The cooling panel no longer labels every cooldown as a 429; it shows the recorded last error instead. (#12594) ([#13060](https://github.com/diegosouzapw/OmniRoute/pull/13060)) — thanks @HouMinXi +- **fix(catalog):** degrade unknown combo targets instead of dropping LCD modalities, and persist OpenRouter `architecture.input_modalities` into the capability snapshot ([#12613](https://github.com/diegosouzapw/OmniRoute/issues/12613)) ([#13042](https://github.com/diegosouzapw/OmniRoute/pull/13042)) — thanks @HouMinXi +- fix(providers): send `x-api-key` instead of `Authorization: Bearer` for OpenCode Zen's `/v1/responses` endpoint (Muse Spark Contributor models), fixing a 401 on OmniRoute's auth header (#12633) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(sse): surface the actionable "Auggie CLI not found" message when the shell reports a missing `auggie` binary via exit code instead of a spawn error (#12645) ([#13263](https://github.com/diegosouzapw/OmniRoute/pull/13263)) +- **fix(config):** Persist deletions of built-in background-degradation entries — when a stored settings record exists its `degradationMap` is now authoritative instead of being merged under the defaults, so an entry the user removed in the dashboard no longer reappears on the next apply or restart ([#12424](https://github.com/diegosouzapw/OmniRoute/issues/12424)) ([#12647](https://github.com/diegosouzapw/OmniRoute/pull/12647)) — thanks @pacocartones +- **fix(gamification):** action-count badge milestones (First Token, Token Consumer, Token Machine, Token Whale, and the token-sharing tier) are now backed by a durable `xp_action_counts` counter incremented in `addXp()`, instead of a live `COUNT(*)` over `xp_audit_log`. The audit log is pruned by `retention.xpAuditLog` (default 30 days), so on a default install those "lifetime" milestones were really "actions in the last 30 days" and unlocked badges could stop unlocking once old rows aged out. `getActionCount()` and `checkActionCountBadges()` now read the same durable source, and a migration backfills existing totals from the surviving audit rows ([#12546](https://github.com/diegosouzapw/OmniRoute/issues/12546)) ([#12651](https://github.com/diegosouzapw/OmniRoute/pull/12651)) — thanks @pacocartones +- **fix(images):** `/v1/images/edits` now iterates a combo's targets the same way `/v1/images/generations` does (#9239) instead of flattening a bare combo to its first target. A combo whose first target is not edit-capable — or lacks credentials — now falls through to a later edit-capable target rather than hard-erroring, and missing credentials are skipped (not a hard `401`) to match the generations path. The per-target skip/terminal classification is extracted into a shared `runImageComboTargets` loop, so generations behavior is unchanged ([#12547](https://github.com/diegosouzapw/OmniRoute/issues/12547)). ([#12653](https://github.com/diegosouzapw/OmniRoute/pull/12653)) — thanks @pacocartones +- fix(sse): add first-byte watchdog to the TLS-fingerprint transport so a stalled wreq body falls back instead of hanging for minutes (#12656) ([#13272](https://github.com/diegosouzapw/OmniRoute/pull/13272)) +- fix(sse): exempt tiny-budget reasoning probes from combo quality failure and surface persisted-cooldown skips in ALL_TARGETS_SKIPPED diagnostics (#12659) ([#13269](https://github.com/diegosouzapw/OmniRoute/pull/13269)) +- fix(models): declare the real ~1M contextLength for OpenCode Zen's Muse Spark 1.2 models instead of falling back to the 200000 provider default (#12681) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(oauth): align codebuddy-cn OAuth User-Agent with the chat/usage CLI version to avoid WAF false positives (#12702) ([#13264](https://github.com/diegosouzapw/OmniRoute/pull/13264)) +- fix(dashboard): surface an authentication-required banner instead of silently blanking database settings for a guest session (#12709) ([#13270](https://github.com/diegosouzapw/OmniRoute/pull/13270)) +- **fix(resilience):** stop unbounded queue that hangs 6min until Aborted — gate, provider slot, and Bottleneck queue now share a per-connection `maxWaitMs` budget; `fail-closed` on exhaust (503); execution backstop `executionMaxWaitMs` overridable per connection with upstream clamp ([#12715](https://github.com/diegosouzapw/OmniRoute/pull/12715)) — thanks @maxmad64bis (with thanks to @Tushar49 for surfacing the slow-provider need in #12635) +- **fix(auto-combo):** every mode pack now carries `quality` and `reliability` weights (`quality` 0.02, 0.03 in `quality-first`; `reliability` 0.03, 0.04 in `reliability-first`), so selecting a pack no longer silences either signal; `DEFAULT_WEIGHTS` is unchanged ([#12731](https://github.com/diegosouzapw/OmniRoute/pull/12731)) — thanks @maxmad64bis +- **fix(sse):** the provider execution pipeline keeps the upstream error `code`/`type` again — both were lost when the non-streaming leg was extracted, so a config-class `422` (Antigravity missing project) degraded into a generic account cooldown instead of reaching the gate that recognises it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(db):** a legacy `call_logs` table no longer aborts startup — the provider-stats index is created after the column healing runs, not before it, so an install predating `request_type` opens instead of failing with `no such column` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(catalog):** a malformed row in the operator-writable custom-models blob no longer kills every `auto/*` pool ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(tests):** realign the guards that the same day's merges left asserting the old behavior — the provider-count total after GreenPT and EURouter, the GLM stream buffer slot that `#12925` turned into a declared parameter, and the injection scan bound that `#13104` reshaped from a head-only window into head-plus-tail ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- fix(cache): fold tool_choice/tools/response_format into the semantic cache signature so a cached tool_calls response can no longer be replayed for a request whose tool policy forbids it (#12734) ([#13267](https://github.com/diegosouzapw/OmniRoute/pull/13267)) +- **fix(models):** stop treating provider-supplied `isFree:true`/`:free`/`0/0` flags as trusted unless the provider has a documented free tier — only the shipped free-tier catalog decides otherwise; fetched `isFree:true` trusted only for a free-tier provider, custom `isFree:true` via a trusted path ([#12744](https://github.com/diegosouzapw/OmniRoute/pull/12744)) — thanks @maxmad64bis +- fix(memory): authenticate the internal /v1/rerank loopback call so memory reranking no longer silently degrades to unranked order when REQUIRE_API_KEY=true (#12745) ([#13268](https://github.com/diegosouzapw/OmniRoute/pull/13268)) +- fix(sse): parse Ollama Cloud's current usage markup (`$X of $Y used` aria-label, nested width style) (#12749) ([#13260](https://github.com/diegosouzapw/OmniRoute/pull/13260)) +- fix(cli): setup-opencode no longer sends an active context's management token to `/v1/models` when `--api-key`/`OMNIROUTE_API_KEY` is supplied — an explicit flag or the env var now always outranks the context's token, and the flag itself is no longer swallowed by the parent program's global `--api-key` option (#12783) ([#13246](https://github.com/diegosouzapw/OmniRoute/pull/13246)) +- fix(sse): register Arcee AI in the executor provider registry so requests reach api.arcee.ai instead of silently falling back to OpenAI (#12784) ([#13277](https://github.com/diegosouzapw/OmniRoute/pull/13277)) +- **fix(quota):** share one upstream quota read when identical saturation checks arrive at the same time, so traffic bursts don't multiply provider API calls ([#12787](https://github.com/diegosouzapw/OmniRoute/pull/12787)) — thanks @maxmad64bis +- **fix(db):** ignore expired or invalid rate-limit cooldown writes so a stale timestamp can't lock a connection that should be usable — clearing still works as before ([#12788](https://github.com/diegosouzapw/OmniRoute/pull/12788)) — thanks @maxmad64bis +- **fix(providers):** single source for provider order with `xao` ranking alongside `xai-oauth` ([#12790](https://github.com/diegosouzapw/OmniRoute/pull/12790)) — thanks @maxmad64bis +- **fix(routing):** off-table models check the free-model catalog before inheriting premium prices, latency bootstraps from the observed pool median, async tiers read live database pricing with a 90-day freshness gate, and the tier cache invalidates on every pricing write ([#12792](https://github.com/diegosouzapw/OmniRoute/pull/12792)) — thanks @maxmad64bis +- **fix(auto-combo):** snapshot scoring uses the observed per-connection account tier and per-model quality instead of neutral constants, and circuit-open providers rank lower at build time ([#12794](https://github.com/diegosouzapw/OmniRoute/pull/12794)) — thanks @maxmad64bis +- **fix(pool):** empty `auto/*` pools now say why they are empty, and the models listing uses the same paid check as routing ([#12795](https://github.com/diegosouzapw/OmniRoute/pull/12795)) — thanks @maxmad64bis +- fix(routing): recognize CLIProxyAPI's 'unknown provider for model' 400 as fallback-worthy (#12800) ([#13284](https://github.com/diegosouzapw/OmniRoute/pull/13284)) +- **fix(sse):** translate-mode streams now emit the estimated token counts as a trailing usage-only chunk before `[DONE]` when the upstream stays silent, so metered chat clients see totals instead of nothing ([#12828](https://github.com/diegosouzapw/OmniRoute/pull/12828)) — thanks @maxmad64bis +- **fix(db):** provider stats stay truthful on empty and legacy databases: fallback counts default to `0` instead of `null`, latency averages read `null` (not `0`) when no durations were recorded, failures logged before the error-type column existed group under `pre_migration` instead of `unclassified`, and per-provider queries use two new composite indexes ([#12832](https://github.com/diegosouzapw/OmniRoute/pull/12832)) — thanks @maxmad64bis +- fix(nvidia): fail open when a synced model catalog goes stale instead of gating forever (#12849) ([#13248](https://github.com/diegosouzapw/OmniRoute/pull/13248)) +- **fix(db):** WAL maintenance lives in its own module: a `TRUNCATE` checkpoint that hits a busy database now warns with its streak and retries once via `PASSIVE` instead of logging success, and closing-time checkpoints no longer report success on builds without a database file, with the busy totals visible in the authenticated monitoring health payload ([#12853](https://github.com/diegosouzapw/OmniRoute/pull/12853)) — thanks @maxmad64bis +- **fix(health):** quota with no snapshots reads empty instead of a contradicting 0% ([#12857](https://github.com/diegosouzapw/OmniRoute/pull/12857)) — thanks @maxmad64bis +- fix(a2a): accept the dashboard's own session cookie on /a2a so "Run message/send" no longer fails with "Unauthorized: missing or invalid API key" (#12888) ([#13271](https://github.com/diegosouzapw/OmniRoute/pull/13271)) +- **fix(logs):** the Logs grid's in-memory filter pass no longer discards rows the SQL query already matched — selecting an API key from the dropdown (which sends the key's id) returns its calls again, the Combo tab shows every combo instead of only those whose name contains a "1", and the model filter and search cover the same columns as the query ([#12896](https://github.com/diegosouzapw/OmniRoute/pull/12896)) — fixes [#12873](https://github.com/diegosouzapw/OmniRoute/issues/12873) — thanks @ntdatt812 +- **fix(a2a):** `/api/a2a/status` now builds the agent card from the request that asked for it, so a gateway reached at a non-localhost host no longer advertises `http://localhost:20128` as its A2A URL ([#12918](https://github.com/diegosouzapw/OmniRoute/pull/12918)). — thanks @ntdatt812 +- **fix(compression):** progressive aging now appends its `[COMPRESSED:aging:…]` annotation after a turn's `tool_result` blocks instead of in front of them, so Anthropic no longer rejects aged conversations with "`tool_use` ids were found without `tool_result` blocks immediately after" ([#12920](https://github.com/diegosouzapw/OmniRoute/pull/12920)). — thanks @ntdatt812 +- **fix(bedrock):** model import now resolves context limits for every vendor prefix instead of only `anthropic.*`, so `global.openai.gpt-5.6-*` no longer imports with a null `inputTokenLimit` and gets rejected pre-flight at the 200k default ([#12921](https://github.com/diegosouzapw/OmniRoute/pull/12921)). — thanks @ntdatt812 +- **fix(stream):** the 64 KB stream buffer GLM asks for is honoured instead of dropped, and the type error it caused no longer fails the API Route Typecheck gate on every open PR ([#12925](https://github.com/diegosouzapw/OmniRoute/pull/12925)) — thanks @ntdatt812 +- **fix(guardrails):** mask PII inside a `tool_result`'s nested content array, which the masker walked past while redacting its sibling block ([#12930](https://github.com/diegosouzapw/OmniRoute/pull/12930)) — thanks @ntdatt812 +- **fix(dashboard):** the Radar catalog table no longer leaves absent data unexplained — empty limits, unknown context windows, and unreported capabilities each explain themselves on hover, and a new check keeps it that way ([#12937](https://github.com/diegosouzapw/OmniRoute/pull/12937)) — thanks @maxmad64bis +- **fix(sse):** geo-blocked opencode requests rotate to the next account proxy instead of failing, so one refused egress no longer aborts the whole chain ([#12941](https://github.com/diegosouzapw/OmniRoute/pull/12941)) — thanks @maxmad64bis +- **fix(codex):** dashboard "clear cooldown" and the CAS recovery path now drop nested `codexScopeRateLimitedUntil` maps in the same write that nulls `rate_limited_until`, and fresh quota snapshots with headroom lift fallback-sourced scope cooldowns parked by quota preflight ([#12817](https://github.com/diegosouzapw/OmniRoute/issues/12817), [#12860](https://github.com/diegosouzapw/OmniRoute/issues/12860), [#12951](https://github.com/diegosouzapw/OmniRoute/pull/12951)) The dashboard reset-credit button (`consumeCodexResetCredit`) now exercises that same snapshot path after a successful redeem, so a filled quota bar is enough to unpark a leftover fallback Codex child without another manual clear. — thanks @HouMinXi +- **fix(db):** resolve `sql-wasm.wasm` across global npm install and hoisted layouts, ensuring OmniRoute can boot cleanly on Node 24 when native `better-sqlite3` is uncompiled. ([#13035](https://github.com/diegosouzapw/OmniRoute/pull/13035)) — thanks @HouMinXi +- **fix(api):** `DELETE /v1/batches/delete-completed` now sweeps only the calling API key's own completed batches (batches with no owner stay out of a key-scoped sweep on purpose), with an explicit instance-wide mode reserved for authenticated dashboard sessions, a 401 for a presented key that is unknown, revoked, deactivated, banned or expired (never falling through to the session branch), audit logging of every sweep, a sanitized 500 on failure, an owner-scoped file half (a key-scoped sweep never nulls a file another tenant owns) and an atomic sweep — chunked in 200-batch transactions in instance mode — so a mid-way error never leaves a batch pointing at a nulled file (GHSA-wvxc-jp3v-5mg5) ([#12969](https://github.com/diegosouzapw/OmniRoute/pull/12969)) +- **fix(combo):** quota-weighted routing stops drawing on an out-of-credit connection — a 402 now invalidates the stored quota snapshot instead of leaving its stale remaining percentage in place, and a snapshot older than 10 minutes no longer counts as confident headroom for the primary pool ([#12972](https://github.com/diegosouzapw/OmniRoute/pull/12972)) ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- **fix(vision):** Custom Models with "Vision capable" checked no longer have image requests swapped to `glm/glm-4.6v` when the client sends the advertised alias (`vllm/path/...`) or the bare path-shaped id — Vision Bridge now matches the stored override for all three id forms ([#12758](https://github.com/diegosouzapw/OmniRoute/issues/12758)) ([#12974](https://github.com/diegosouzapw/OmniRoute/pull/12974)) — thanks @HouMinXi +- **fix(sse):** transient opencode upstream failures rotate to the next account proxy instead of failing, so one flapping egress no longer aborts the whole chain ([#12975](https://github.com/diegosouzapw/OmniRoute/pull/12975)) — thanks @maxmad64bis +- **fix(azure):** Deployments from GPT-6 onward now send `max_completion_tokens` instead of `max_tokens`, which Azure rejects with HTTP 400. The rule matched a literal `gpt-5`, so each new generation arrived broken; it now matches the generation range, while `gpt-35-turbo` still keeps `max_tokens`. ([#13007](https://github.com/diegosouzapw/OmniRoute/pull/13007)) — thanks @ntdatt812 +- **fix(db):** add an opt-in self-restart circuit for sustained critical memory pressure, gate post-cleanup VACUUM behind a minimum freed-rows threshold, and checkpoint the SQLite WAL every 5 minutes with a size guard that escalates to TRUNCATE, so a growing WAL can no longer stall the event loop into a full outage. ([#13011](https://github.com/diegosouzapw/OmniRoute/pull/13011)) — thanks @HouMinXi +- **fix(auth):** an explicit connection pin may probe a quota-disabled row once and re-enable it on success ([#12874](https://github.com/diegosouzapw/OmniRoute/issues/12874)) ([#13017](https://github.com/diegosouzapw/OmniRoute/pull/13017)) — thanks @HouMinXi +- **fix(api):** thread `X-OmniRoute-Fallback-Attempts` through combo chat completions so streaming and non-streaming responses report how many prior legs were attempted ([#13038](https://github.com/diegosouzapw/OmniRoute/pull/13038)) — thanks @HouMinXi +- Restore provider failure classification and credential refresh on non-streaming requests: classify non-2xx failures to lock models on per-model quota exhaustion, update connection rate limits from headers and body, and pass credential refresh handlers to pipeline execution so 401 tokens can be refreshed and retried (#13043). ([#13069](https://github.com/diegosouzapw/OmniRoute/pull/13069)) — thanks @HouMinXi +- **fix(responses):** wrap forced-non-streaming web_search fallback JSON as Responses SSE so Codex still sees `response.completed` ([#13050](https://github.com/diegosouzapw/OmniRoute/pull/13050)) — thanks @HouMinXi +- **fix(antigravity):** Preserve upstream thought-token usage in normalized completion and reasoning token counts ([#13055](https://github.com/diegosouzapw/OmniRoute/pull/13055)) — thanks @pacocartones +- Fix a concurrency-slot leak in the DeepSeek PoW solver: a worker that failed to spawn (for example a missing worker script) never released its slot, so `MAX_CONCURRENT_WORKERS` failures disabled the solver until restart. ([#13097](https://github.com/diegosouzapw/OmniRoute/pull/13097)) — thanks @anhtahaylove +- **fix(acp):** bound the ACP session output buffers — `stdoutBuffer` and `stderrBuffer` now cap at 1 MiB keeping the most recent output behind a visible `[...output truncated...]` marker, and `stderrBuffer` is reset per prompt instead of accumulating for the lifetime of the session. ([#13100](https://github.com/diegosouzapw/OmniRoute/pull/13100)) — thanks @anhtahaylove +- **fix(acp):** release the `stdout`/`exit` listeners and the idle timer that a `sendPrompt` timeout used to leave attached to the `acpManager` singleton, and drop sessions that exited on their own from the session map instead of keeping them forever. ([#13096](https://github.com/diegosouzapw/OmniRoute/pull/13096)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection and PII scanners now read the text a `tool_result` block carries on `content` (string or nested block list), in messages and in system blocks, so tool output is judged by the same rules as user text ([#13101](https://github.com/diegosouzapw/OmniRoute/pull/13101)) — thanks @ntdatt812 +- **fix(gamification):** close the badge notification SSE stream when the request signal is already aborted before the stream starts — a client that disconnects while the route is still awaiting auth used to leave both the 2s unlock poll and the 15s heartbeat running for the lifetime of the process. ([#13106](https://github.com/diegosouzapw/OmniRoute/pull/13106)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection scan now spends its 16 KB budget on both ends of the request instead of the first 16 KB only, so `system`, `instructions`, `query`, `documents` and the newest turns are no longer hidden behind one long message ([#13104](https://github.com/diegosouzapw/OmniRoute/pull/13104)) — thanks @ntdatt812 +- **fix(dashboard):** expose the Volcano Ark console cookie on quota scraping and unwrap connect-error objects so the dashboard shows the upstream message ([#13107](https://github.com/diegosouzapw/OmniRoute/pull/13107)) — thanks @HouMinXi +- **fix(db):** release the `beforeExit`/`SIGINT`/`SIGTERM` handlers when a `node:sqlite` adapter closes, so a closed adapter and its database handle are no longer pinned to `process` for the lifetime of the run — the same treatment #7494 gave the sql.js adapter. ([#13109](https://github.com/diegosouzapw/OmniRoute/pull/13109)) — thanks @anhtahaylove +- **fix(translator):** `contentSchema` and `unevaluatedItems` are now treated as subschema positions by the tool-schema sanitizer, so a truncation placeholder in either is replaced with a permissive schema instead of being forwarded as a string ([#13110](https://github.com/diegosouzapw/OmniRoute/pull/13110)) — thanks @ntdatt812 +- **fix(cli-helper):** clear the `createLogStream` timeout on the abort path — `stop()` aborts the in-flight fetch and returned through the `signal.aborted` branch, which skipped `clearTimeout` and left an armed timer per stopped stream. The stream reader is now also cancelled when the read loop exits early. ([#13114](https://github.com/diegosouzapw/OmniRoute/pull/13114)) — thanks @anhtahaylove +- **fix(docs):** drop TLS-impersonation, MITM-decrypt, supply-chain attestation, and XOR-mask writeups from the public `/docs` catalog and Docker image. Files stay in git for engineers; operators who need them open the repo, not the website. ([#13136](https://github.com/diegosouzapw/OmniRoute/pull/13136)) — thanks @HouMinXi +- **fix(combo):** parse numeric-epoch `rate_limited_until` in the combo cooldown read path ([#13141](https://github.com/diegosouzapw/OmniRoute/pull/13141)) — thanks @maxmad64bis +- **fix(opencode):** both OpenCode plugins now reject a gateway address typed without `http://` at configuration time, instead of publishing every model with an api url no client can call, and the v2 plugin no longer publishes a model card whose api url is blank or relative ([#13142](https://github.com/diegosouzapw/OmniRoute/pull/13142)) — thanks @maxmad64bis +- **fix(providers):** lock opencode model on upstream 400 model-unavailable ([#13146](https://github.com/diegosouzapw/OmniRoute/pull/13146)) — thanks @maxmad64bis +- **fix(logging):** keep the provider exchange rather than the raw client bodies when a call log exceeds its size budget, and show that recovered payload in the request-detail panel instead of replacing it with the stored response body ([#13147](https://github.com/diegosouzapw/OmniRoute/pull/13147)) — thanks @maxmad64bis +- **fix(traffic-inspector):** the WebSocket route no longer leaks a traffic-buffer subscriber and a 30s ping timer when the client socket is already closed at handler time — listeners are attached before any resource is acquired, a destroyed socket bails out early, and the ping interval stops on a dead socket where `write()` never throws ([#13155](https://github.com/diegosouzapw/OmniRoute/pull/13155)) — thanks @anhtahaylove +- **fix(telegram):** bound the per-user API key cache in the Telegram chat proxy so a burst of distinct chat ids can no longer grow the process heap without limit ([#13165](https://github.com/diegosouzapw/OmniRoute/issues/13165)) ([#13166](https://github.com/diegosouzapw/OmniRoute/pull/13166)) — thanks @anhtahaylove +- **fix(stream):** cancel the upstream response body when the JSON-to-SSE sniff unwinds on a body timeout, so a stalled upstream no longer pins the connection ([#13169](https://github.com/diegosouzapw/OmniRoute/issues/13169)) ([#13171](https://github.com/diegosouzapw/OmniRoute/pull/13171)) — thanks @anhtahaylove +- **fix(telegram):** authenticate webhook deliveries with Telegram's `secret_token` so an unauthenticated caller can no longer mint API keys or spend upstream quota ([#13172](https://github.com/diegosouzapw/OmniRoute/issues/13172)) ([#13175](https://github.com/diegosouzapw/OmniRoute/pull/13175)) — thanks @anhtahaylove +- **fix(models):** give discoverable Gemini 3.8 Flash ids their own 65536 output spec so Antigravity no longer clamps them to 16384 ([#13195](https://github.com/diegosouzapw/OmniRoute/pull/13195)) — thanks @HouMinXi +- **fix(dashboard):** leftover catalog-removed provider rows (gemini-cli) can be listed and purged from the providers page ([#13067](https://github.com/diegosouzapw/OmniRoute/issues/13067)) ([#13197](https://github.com/diegosouzapw/OmniRoute/pull/13197)) — thanks @HouMinXi +- **fix(auth):** a dashboard session now requires the `authenticated: true` claim that login, OIDC and the session refresh already emit — a JWT merely signed with `JWT_SECRET` (for example the Cursor CLI passthrough token, which any API-key holder can obtain) no longer verifies as the `auth_token` cookie on any route, the WebSocket handshake or the live server; existing sessions keep working ([#13298](https://github.com/diegosouzapw/OmniRoute/issues/13298)) ([#13375](https://github.com/diegosouzapw/OmniRoute/pull/13375)) +- **fix(api):** `DELETE /v1/batches/delete-completed` now applies the caller's API-key policy (endpoint allowlist, schedule, usage cap, rate limit) like every other `/v1` route, commits the key-scoped sweep per 200-batch chunk instead of holding one write lock for the whole sweep, refuses to loop without progress, and rejects a scope that names both a key and `allTenants` ([#13684](https://github.com/diegosouzapw/OmniRoute/pull/13684)) +- Restore the API-route typecheck gate: the non-streaming leg result lost its discriminated-union narrowing after the server-owned tool loop reassignment, producing 13 new TS2339 diagnostics in `chatCore.ts`. ([#12963](https://github.com/diegosouzapw/OmniRoute/pull/12963)) +- Restore four non-streaming behaviours the server-owned tool loop refactor dropped: client aborts map to 499 with the fixed `Request aborted` message, an aborted request no longer logs a synthetic `clientResponse`, Claude prompt-cache telemetry is recorded again, and a body that cannot be canonicalized no longer throws when the tool loop is off. ([#12990](https://github.com/diegosouzapw/OmniRoute/pull/12990)) +- **fix(skills):** The CLI registry parser now reads positionals declared with `.addArgument()`, not only those written inline in `.command()`. `tunnel create [type]` was being published as `tunnel create`, so the agent-skills sync gate reported drift on every branch and regenerating would have deleted the argument. ([#13009](https://github.com/diegosouzapw/OmniRoute/pull/13009)) — thanks @ntdatt812 +- **fix(combos):** dashboard combo test uses a short prompt, serial probes, and a 60s timeout so reasoning models and rate-limited free pools do not fail the health check ([#13001](https://github.com/diegosouzapw/OmniRoute/pull/13001)) — thanks @HouMinXi +- fix(compression): terminate idle worker threads on eviction so long-running instances stop leaking OS threads and MessagePorts ([#13091](https://github.com/diegosouzapw/OmniRoute/pull/13091)) — thanks @anhtahaylove +- Redact Google API keys of any length in error bodies: the pattern required exactly 39 characters, so shorter or longer `AIza…` credentials passed through unredacted. ([#12964](https://github.com/diegosouzapw/OmniRoute/pull/12964)) +- **fix(grok-cli):** a 402 "Grok Build usage balance exhausted" parks that Grok login as out of credit (Grok Build CLI, grok.com cookie, and xAI OAuth share the weekly pool). Combo routing then tries the next login instead of locking the model for every account in the pool ([#13061](https://github.com/diegosouzapw/OmniRoute/pull/13061)) — thanks @HouMinXi +- fix(compression): spawn the LLMLingua worker with a file URL object so compression actually runs instead of silently failing open on Node ([#13093](https://github.com/diegosouzapw/OmniRoute/pull/13093)) — thanks @anhtahaylove +- **fix(test):** run the local `test` and `test:unit` scripts at concurrency 4 so a full-suite run no longer exhausts the machine's commit charge and kills unrelated processes ([#13187](https://github.com/diegosouzapw/OmniRoute/pull/13187)) — thanks @anhtahaylove +- **fix(monitoring):** `GET /api/monitoring/health` `credentialHealth` now includes a bounded `failedConnections` list (`connectionId`, `status`, sanitized `lastError`) when the probe-cache gauge `failed>0`, plus `source: probe-cache` and a cheap `staleDbNonOkCount` for sticky SQLite `test_status` on active rows. Documents that the live gauge is not `provider_connections.test_status`. ([#12876](https://github.com/diegosouzapw/OmniRoute/pull/12876)) — thanks @RaviTharuma +- **fix(resilience):** an openai-compatible multi-upstream gateway no longer marks the whole connection `credits_exhausted` when a single upstream (or the `/models` / representative-model health probe) returns 402. Quota failures stay model-scoped; true connection-wide auth failures are unchanged. Single-credential 402 key disable ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239)) is preserved. ([#12875](https://github.com/diegosouzapw/OmniRoute/pull/12875)) — thanks @RaviTharuma +- **fix(dashboard):** nine fixes on the Orchestration Canvas, all diagnosed in the Phase 2 reviews. A source that fails now keeps the timestamp of its FIRST failure instead of being re-stamped every poll — the stale line said "since the last poll" no matter how long the source had been down, and the churn also defeated the snapshot's stable identity (it serializes the source list), so the canvas re-rendered on every tick while anything was broken. A source that HAD data and only then started failing is flagged too: previously only a source with no node at all got the warning, so a source that went down mid-session kept a healthy-looking node forever. The rest are pointwise: clicking a filter chip cancels the pending search debounce (left armed, it fired ~300ms later and silently reverted the chip); the search input carries an accessible name; `?state=running, failed` parses like the unpadded form instead of dropping the padded value; the CSV toggle helper is defined once in `model/urlParams.ts` rather than twice; the agents tab tells "nothing running" apart from "the filter matched nothing", offering a clear-filters button instead of setup links that would be wrong advice there; edges stop emitting SMIL particles above 40 simultaneously active edges, keeping the colored stroke; and the drawer's error banner clears when a retried action succeeds. ([#12983](https://github.com/diegosouzapw/OmniRoute/pull/12983)) +- **fix(i18n):** the nine locales added with the EU-language batch (Greek, Estonian, Irish, Croatian, Lithuanian, Latvian, Maltese, Slovenian, Serbian) were missing the eleven Orchestration Canvas keys that Phase 3 introduced, so the compare-runs panel and the "no runs match these filters" empty state fell back to English in those languages (`deepMergeFallback` substitutes English for an absent key, so nothing rendered blank — it rendered untranslated). The coverage gate does not catch this: it enforces an 80% floor per locale, and eleven missing keys out of ~13,000 leaves coverage at 99.9%. Translated for real in each language, calibrated against the wording each file already uses for "run", "filter" and "skill". ([#13294](https://github.com/diegosouzapw/OmniRoute/pull/13294)) +- **fix(dashboard):** five follow-ups from the Phase 2 Orchestration Canvas review. The History drawer now shows the memory section for runs that already left the live TTL window: the persisted `memory_hits` event is parsed into `metadata.memoryHits` with the same defensive validation the drawer applies, and — because that event is observability rather than a state transition — it no longer leaks into the timeline, where it had been inheriting the task's state and rendering as a duplicate transition. The A2A memory recall runs against its own 1.5s deadline instead of inheriting the memory backend's 30s one; overshooting degrades exactly like any other recall failure (no hits, task proceeds), and the timer is cleared on both paths. Repeating a Conductor run carries its `requirements.cli`/`requirements.model` forward, so the new run is pinned to the same runner profile and model rather than drifting to whatever the fleet picks. A successful repeat from the Agents tab now focuses the run it created, instead of leaving the operator on the finished one. And the auth test for `POST /api/conductor/tasks` moved into the shared `ROUTES` array rather than restating the pattern. ([#12988](https://github.com/diegosouzapw/OmniRoute/pull/12988)) +- fix(plugins): stop leaking an exit listener per plugin hook timeout, which triggered MaxListenersExceededWarning on plugins that ignore SIGTERM ([#13092](https://github.com/diegosouzapw/OmniRoute/pull/13092)) — thanks @anhtahaylove +- **fix(validation):** Provider node edits no longer fail with a generic "Invalid request" when the optional daily-quota reset fields are left blank. The dashboard sends `dailyQuotaResetTimezone` and `dailyQuotaResetHour` as `null`, and only the hour accepted it. ([#13066](https://github.com/diegosouzapw/OmniRoute/issues/13066)) ([#13083](https://github.com/diegosouzapw/OmniRoute/pull/13083)) — thanks @ntdatt812 +- **fix(combo):** quota-aware expansion drops banned, inactive, missing, and wrong-provider connections before quota fetch or model dispatch; pins and allowlists stay selectors, not a bypass. Antigravity automatic exhaustion now requires a reported zero remaining, so a positive balance below 1% stays eligible. ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- Fix an intermittent failure in the quota-weighted routing test suite: scores are a function of `Date.now()`, so peers with identical quota scored microseconds apart never tied and swapped order. ([#12946](https://github.com/diegosouzapw/OmniRoute/pull/12946)) +- fix(volcengine): resolve quota window mapping gaps, safe multi-connection console binding, and response cookie sanitization ([#12950](https://github.com/diegosouzapw/OmniRoute/pull/12950)) — thanks @HouMinXi +- **fix(test):** resolve the WebDAV handler path with `fileURLToPath` so the suite's 37 WebDAV tests run on Windows instead of failing with a doubled `C:\C:\` drive prefix ([#13196](https://github.com/diegosouzapw/OmniRoute/pull/13196)) — thanks @anhtahaylove +- **fix(ui):** fall back from invalid AnySearch icon alias (direct commit `9587e07b69`) +- **fix(sse):** stop rebuilding a truncated summary from the collector's cap-dropped event array ([#12718](https://github.com/diegosouzapw/OmniRoute/pull/12718)) — thanks @hartmark +- **fix(conversations):** show a pending spinner for unresolved tool nodes instead of "(empty)" ([#12727](https://github.com/diegosouzapw/OmniRoute/pull/12727)) — thanks @hartmark +- **fix(streaming):** fail fast when an upstream stream produces only lifecycle/heartbeat events, never real content ([#12741](https://github.com/diegosouzapw/OmniRoute/pull/12741)) — thanks @hartmark +- **fix(responses-continuation):** bridge the write-in-flight window with an in-memory pending store ([#12854](https://github.com/diegosouzapw/OmniRoute/pull/12854)) — thanks @hartmark +- **fix(translator):** drop or map agent_message on Chat Completions fallback ([#12880](https://github.com/diegosouzapw/OmniRoute/pull/12880)) — thanks @RaviTharuma +- **fix(api):** page and stream-complete GET /v1/models for large catalogs ([#12882](https://github.com/diegosouzapw/OmniRoute/pull/12882)) — thanks @RaviTharuma +- **fix(quota):** align AUTH window labels with usage API and clarify 503 ([#12884](https://github.com/diegosouzapw/OmniRoute/pull/12884)) — thanks @RaviTharuma +- **fix(ci):** clear two base-reds on release/v3.8.51 — mutation-coverage gate + image-only-model guard ([#12945](https://github.com/diegosouzapw/OmniRoute/pull/12945)) +- **fix(api):** scope batch bulk-delete to the calling API key ([#13211](https://github.com/diegosouzapw/OmniRoute/pull/13211)) +- **fix(tests):** align model-sync log assertion to shipped path redaction (base-red) ([#13227](https://github.com/diegosouzapw/OmniRoute/pull/13227)) +- **fix(test):** make the ToS heading guard actually require the parentheses ([#13228](https://github.com/diegosouzapw/OmniRoute/pull/13228)) +- **fix(quality):** register 4 drifted covering tests in stryker tap.testFiles ([#13229](https://github.com/diegosouzapw/OmniRoute/pull/13229)) +- **fix(logging):** mask *-api-key headers in request-log pipeline (#13273) ([#13401](https://github.com/diegosouzapw/OmniRoute/pull/13401)) — thanks @KooshaPari +- **fix(tests):** correct stale Jina catalog prefix in custom-models test ([#13403](https://github.com/diegosouzapw/OmniRoute/pull/13403)) — thanks @KooshaPari +- **fix(backend):** prune old backups after health-check-repair snapshots (#13308) ([#13404](https://github.com/diegosouzapw/OmniRoute/pull/13404)) — thanks @KooshaPari +- **fix(backend):** guard memory_fts_au trigger with WHEN clause to stop FTS bloat (#13326) ([#13405](https://github.com/diegosouzapw/OmniRoute/pull/13405)) — thanks @KooshaPari +- **fix(free-tiers):** remove obsolete Together $25 signup credit (#12526) ([#13407](https://github.com/diegosouzapw/OmniRoute/pull/13407)) — thanks @KooshaPari +- **fix:** skip non-chat generation models in Test all models ([#13409](https://github.com/diegosouzapw/OmniRoute/pull/13409)) — thanks @KooshaPari +- **fix(providers):** de-duplicate agy and antigravity model catalogs ([#13410](https://github.com/diegosouzapw/OmniRoute/pull/13410)) — thanks @KooshaPari +- **fix(dashboard):** send provider in body for mini-playground web search ([#13411](https://github.com/diegosouzapw/OmniRoute/pull/13411)) — thanks @KooshaPari +- **fix(resilience):** add CJK quota-exhaustion patterns to 429 classifier ([#13412](https://github.com/diegosouzapw/OmniRoute/pull/13412)) — thanks @KooshaPari +- **fix(backend):** eval runner: failed calls no longer score as passed + dotAll regex ([#13413](https://github.com/diegosouzapw/OmniRoute/pull/13413)) — thanks @KooshaPari +- **fix(backend):** eval runner opts out of output-style and memory injection ([#13414](https://github.com/diegosouzapw/OmniRoute/pull/13414)) — thanks @KooshaPari +- **fix:** chain serial quarantine step in npm test script (fixes #13286) ([#13418](https://github.com/diegosouzapw/OmniRoute/pull/13418)) — thanks @KooshaPari +- **fix(antigravity):** derive stable session ID from credentials for prompt-cache reuse (#13305) ([#13424](https://github.com/diegosouzapw/OmniRoute/pull/13424)) — thanks @KooshaPari +- **fix(memory):** merge memory into top-level system field instead of unshifting at messages[0] (#13425) ([#13427](https://github.com/diegosouzapw/OmniRoute/pull/13427)) — thanks @KooshaPari +- **fix(compression):** preserve instruction blocks from lossy engines ([#13523](https://github.com/diegosouzapw/OmniRoute/pull/13523)) — thanks @KooshaPari +- **fix(compression):** preserve polarity words and newlines in ultra heuristic ([#13524](https://github.com/diegosouzapw/OmniRoute/pull/13524)) — thanks @KooshaPari +- **fix(compression):** run user preservePatterns before built-in patterns (#13457) ([#13525](https://github.com/diegosouzapw/OmniRoute/pull/13525)) — thanks @KooshaPari +- **fix(reasoning):** expand deepseek-v4 regex to match v4.1+ and add command-code provider (#13430) ([#13528](https://github.com/diegosouzapw/OmniRoute/pull/13528)) — thanks @KooshaPari +- **fix(providers):** add alwaysReasons metadata for reka-flash-3 ([#13531](https://github.com/diegosouzapw/OmniRoute/pull/13531)) — thanks @KooshaPari +- **fix(playground):** allow Compare columns to scroll independently ([#13532](https://github.com/diegosouzapw/OmniRoute/pull/13532)) — thanks @KooshaPari +- **fix(security):** empty whitelist allows through to prevent admin lockout ([#13534](https://github.com/diegosouzapw/OmniRoute/pull/13534)) — thanks @KooshaPari +- **fix(tests):** add DISABLE_IOREG_STRATEGY env var for macOS test compatibility (fixes #13467) ([#13539](https://github.com/diegosouzapw/OmniRoute/pull/13539)) — thanks @KooshaPari +- **fix(zed-hosted):** map developer role and Gemini enums for Zed proxy ([#13543](https://github.com/diegosouzapw/OmniRoute/pull/13543)) — thanks @KooshaPari +- **fix(analytics):** normalize provider aliases to canonical id in usage_history ([#13545](https://github.com/diegosouzapw/OmniRoute/pull/13545)) — thanks @KooshaPari +- **fix(combo):** use traceId for call log id to prevent UNIQUE constraint failure ([#13546](https://github.com/diegosouzapw/OmniRoute/pull/13546)) — thanks @KooshaPari +- **fix(auth):** check model lockout before returning synthetic noauth connection ([#13547](https://github.com/diegosouzapw/OmniRoute/pull/13547)) — thanks @KooshaPari +- **fix(guardrails):** skip credential redaction for base64 image data URLs ([#13550](https://github.com/diegosouzapw/OmniRoute/pull/13550)) — thanks @KooshaPari +- **fix(resilience):** increase requestQueue.maxWaitMs default from 15s to 30s ([#13553](https://github.com/diegosouzapw/OmniRoute/pull/13553)) — thanks @KooshaPari +- **fix(docs):** document OMNIROUTE_READY_TIMEOUT_MS and allowlist the test-only DISABLE_IOREG_STRATEGY ([#13692](https://github.com/diegosouzapw/OmniRoute/pull/13692)) + ### 📝 Maintenance ---- +- **docs(readme):** reconcile live v3.8.50 provider, free-tier, CLI, routing, test, community, sponsor, acknowledgment, and SVG metrics with their audited source denominators, including a deduplicated OmniRoute-in-Action snapshot and distinct contributor rankings for merged pull requests, GitHub-attributed commits, and Git history ([#11356](https://github.com/diegosouzapw/OmniRoute/pull/11356)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **docs:** sync the canonical provider count 354 → 356 across `README.md`, `AGENTS.md`, `llm.txt` (+ 42 i18n mirrors), the four README SVG diagrams, `docs/reference/PROVIDER_REFERENCE.md` (regenerated) and the `package.json` description after Opper (#11629) and 1min.ai (#11631) boarded the catalog — closes the `check:docs-counts-sync` strict drifts that kept `release/v3.8.51` red ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11673](https://github.com/diegosouzapw/OmniRoute/pull/11673)) — thanks @jonlwheat2-gif +- **test(providers):** update count-derived assertions after the v3.8.51 provider additions — `APIKEY_PROVIDERS` 233 → 235 (Opper #11629 + 1min.ai #11631), reserved-prefix REGISTRY walk 395 → 398, `WEB_FETCH_PROVIDERS` now includes `nimble-search` (#11620), and the provider translate-path golden snapshot regenerated ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11674](https://github.com/diegosouzapw/OmniRoute/pull/11674)) — thanks @jonlwheat2-gif +- **test(sse):** bump the hard-lease connection-query inventory for `src/lib/tokenHealthCheck.ts` to 2 — the verify-only web-cookie sweep added by #11495 split the single `getProviderConnections` call into oauth + cookie variants, which the frozen inventory had not tracked ([#11495](https://github.com/diegosouzapw/OmniRoute/pull/11495)). ([#11677](https://github.com/diegosouzapw/OmniRoute/pull/11677)) — thanks @jonlwheat2-gif / @oyi77 +- **chore(release):** clear the `release/v3.8.51` base-red gates — docs migration count (+38 i18n mirrors), stryker `tap.testFiles`, lockfile registry host, stale ESLint suppressions, the 7 lint regressions they were hiding, the stale `APIKEY_PROVIDERS` partition count (231 → 233), the `glm-5.3-max` tier missing from the GLM test table, a dead `modelType === "chat"` comparison in the models catalog, and the `resolveLiveWsUrl` / `sanitizeLiveWsPort` helpers from #11388 that a later merge dropped from `wsPath.ts` (dashboard live WebSocket URL resolution) ([#11502](https://github.com/diegosouzapw/OmniRoute/pull/11502)) — thanks @MumuTW +- **chore(free-models):** the free-tier regime table now declares which totals figure each regime feeds and whether it may take the no-auth shortcut, so the sets that used to repeat those answers by hand are derived from it — a new `freeType` no longer compiles until it has answered every question, instead of silently contributing to no total ([#11537](https://github.com/diegosouzapw/OmniRoute/pull/11537)) — thanks @maxmad64bis +- **docs(routing):** LKGP strategy copy now states provider-level stickiness and the explicit rules fallback instead of promising per-target pinning ([#11555](https://github.com/diegosouzapw/OmniRoute/pull/11555)) — thanks @maxmad64bis +- **test(chatcore):** pin the response tool-name alias resolution against the ordering hazard that broke every Gemini/Antigravity MCP tool call in v3.8.49. `extractRequestToolIdentityMap` deletes `translatedBody._toolNameMap`, so the later read is always undefined and the ledger survives only through the `requestToolIdentityMap` fallback — removing that fallback previously left the entire tool-name suite green. The resolution moves into `resolveResponseToolNameMap()` next to the map it depends on, with a regression guard that fails without the recovery. No behaviour change. ([#11575](https://github.com/diegosouzapw/OmniRoute/pull/11575)) — thanks @amartinawi +- **docs(env):** document `OMNIROUTE_CORPUS_CACHE_SIZE` in `.env.example` and `ENVIRONMENT.md`, so the env/docs contract check stops failing Docs Gates on every branch ([#11576](https://github.com/diegosouzapw/OmniRoute/pull/11576)) — thanks @ntdatt812 +- **chore(stryker):** register `route-guard-tunnel-processes-local-only` and `universal-quota-aware-routing` in `tap.testFiles`, so their mutant kills count for the three modules they cover ([#11577](https://github.com/diegosouzapw/OmniRoute/pull/11577)) — thanks @ntdatt812 +- **test(authz):** pin `LOCAL_ONLY_API_GET_EXEMPTIONS` by exact membership instead of by entry count, so the guard names the offending path and also catches a substitution ([#11580](https://github.com/diegosouzapw/OmniRoute/pull/11580)) — thanks @ntdatt812 +- **test(build):** pin `resolveNextBuildBundlerFlag` to the `OMNIROUTE_USE_TURBOPACK` contract it implements, across runtimes, instead of asserting an unimplemented Bun override ([#11583](https://github.com/diegosouzapw/OmniRoute/pull/11583)) — thanks @ntdatt812 +- **test(claude-web):** the Claude Web transport test's barrel anchors no longer pin the exact construction spelling. #11421 made the executor map lazy, so `/"claude-web": new ClaudeWebExecutor\(\)/` stopped matching and the test went red on `release/v3.8.51` itself — a base-red gate every open PR inherited. The anchors now match the alias key and the class name and tolerate either spelling in between, and were checked to still fail when an alias is genuinely repointed at another executor ([#11585](https://github.com/diegosouzapw/OmniRoute/pull/11585)) — thanks @ntdat812 +- **test(models):** pin `qwen3.8-max` resolution per provider — two of the four catalogs now serve the bare id and no longer carry `-preview` ([#11590](https://github.com/diegosouzapw/OmniRoute/pull/11590)) — thanks @ntdatt812 +- **test(executors):** drop the stale 128-tool truncation assertion that contradicted the #11444 pass-through contract ([#11591](https://github.com/diegosouzapw/OmniRoute/pull/11591)) — thanks @ntdatt812 +- **test(token-health):** pin the current terminal-skip boundary — expired with retry budget is probed, `account_deactivated` stays skipped ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)) — thanks @ntdatt812 +- **test(models):** follow #11503's retarget of the `gemini-3-pro-high` alias to the hyphenated catalog id ([#11593](https://github.com/diegosouzapw/OmniRoute/pull/11593)) — thanks @ntdatt812 +- **refactor(video bridge):** extract per-video acquisition, whole-result caching, description, and metrics/abort/cleanup out of `VideoBridgeGuardrail.preCall` into a `processVideoPart` seam in a new `videoBridgePipeline.ts`, behind explicit `VideoMediaBrokerPort`, `VideoAudioTranscriptionPort`, and `VideoDrilldownPort` boundaries; `preCall` now only handles request traversal, policy, and response aggregation. The Video tab's FFmpeg/ffprobe runtime status is now an explicit `unknown` / `restricted` / `unavailable` / `available` state instead of a nullable boolean pair, fixing a case where an in-flight or failed probe was mislabeled as "install FFmpeg" ([#11657](https://github.com/diegosouzapw/OmniRoute/issues/11657)). ([#12016](https://github.com/diegosouzapw/OmniRoute/pull/12016)) +- **test(opencode-plugin):** add unit test coverage for `formatFreeBudget()` naming helper ([#11660](https://github.com/diegosouzapw/OmniRoute/pull/11660)) — thanks @f9td56dbgh-hub +- **docs(video):** clarify that the Video Bridge transcript `source` field (`client`, `embedded`, `audio-bridge`) is presently caller-declared and not yet server-verified — OmniRoute enforces the enum shape but does not cryptographically confirm that an `embedded`/`audio-bridge` label came from a server-owned extraction ([#11661](https://github.com/diegosouzapw/OmniRoute/issues/11661)). ([#12001](https://github.com/diegosouzapw/OmniRoute/pull/12001)) +- **chore(providers):** retire the distributed Raycast Relay and Hailuo Web integrations after a provenance audit confirmed substantial derivation from GPL-3.0 sources; the official GitHub and MiniMax API providers remain available. Migration 163 disables legacy connections and invalidates their active leases while preserving connection IDs, audit/history rows, quota snapshots, usage records, and fail-closed API-key connection policies. ([#11691](https://github.com/diegosouzapw/OmniRoute/pull/11691)) +- **chore(providers):** retire the Felo Web provider while its GPL-derived provenance remains on licensing HOLD ([#11698](https://github.com/diegosouzapw/OmniRoute/pull/11698)) — stale runtime and database references now fail closed while existing connection, lease, and usage history remains preserved for auditability. +- **fix(sse):** retire the Gemini Web image-generation bridge while provenance and licensing remain on HOLD, preserving Gemini Web chat/auth/tools and legitimate Gemini/Nano Banana image providers ([#11708](https://github.com/diegosouzapw/OmniRoute/pull/11708)) +- **chore(audio):** retire the built-in EdgeTTS provider and its 11-voice catalog while provenance/licensing review remains on HOLD; the generic `/v1/audio/speech` endpoint and the other speech providers remain available ([#11711](https://github.com/diegosouzapw/OmniRoute/pull/11711)) +- **fix(providers):** retire the unofficial Qwen Web integration and its `qwen-web`/`qw` identities while third-party provenance remains on HOLD ([#11713](https://github.com/diegosouzapw/OmniRoute/pull/11713)) — direct requests now fail closed with a sanitized HTTP 410 and combos skip the retired target, while migration guards preserve connection IDs, allowlists, usage/audit history and invalidate active leases. Supported Qwen API, Qwen Code, cloud, token-plan, audio, local and model-name surfaces remain available; restoring Qwen Web requires a qualified provenance/license decision. +- **fix(providers):** retire `microsoft-designer-web` and its `msdesigner` alias from runtime and catalogs while preserving stored credentials, provider history and allowlists as inactive records — source dependency on gpt4free's [`MicrosoftDesigner.py`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/g4f/Provider/needs_auth/MicrosoftDesigner.py) is proven at snapshot `0943d16eef757a44deb60651c07bbe5fa958cafe`, whose [`LICENSE`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/LICENSE) is GPL-3.0; expressive adaptation is probable, which records provenance/licensing risk without asserting a legal conclusion ([#11720](https://github.com/diegosouzapw/OmniRoute/pull/11720)). +- **docs(legal):** centralize pinned MIT notices for the vendored GCF codec and local flag assets ([#11726](https://github.com/diegosouzapw/OmniRoute/pull/11726)) +- Split the 2,952-line `adobeFireflyClient.ts` into nine focused modules (catalog, credentials, payload, ARP session, upload, responses, IMS, account, poll) behind an unchanged barrel — no consumer edits and no public API change (#11728). — thanks @MumuTW +- **fix(dashboard):** replaced the Model Lockout card's unprovenanced notification MP3 with a short, locally synthesized Web Audio chime ([#11731](https://github.com/diegosouzapw/OmniRoute/pull/11731)) — both toggles retain optional audible feedback without shipping replacement media or third-party code, and unsupported, suspended, or rejected audio contexts fail open without blocking the setting change. +- **chore(deepseek-web):** remove the provenance-unresolved DeepSeek PoW WASM binary and its runtime loader/tracing while retaining the existing JavaScript solver (slower at high difficulty) ([#11732](https://github.com/diegosouzapw/OmniRoute/pull/11732)). +- **chore(assets):** remove four unused provider assets whose redistribution provenance could not be verified while retaining the active Freebuff SVG ([#11735](https://github.com/diegosouzapw/OmniRoute/pull/11735)) +- **docs(assets):** retired two raster images whose redistribution provenance could not be verified ([#11736](https://github.com/diegosouzapw/OmniRoute/pull/11736)) — PicoClaw documentation now reuses the existing SVG, while Zoo Code remains a text link without an image. +- **docs(legal):** add pinned notices for six LobeHub-derived provider SVGs and 65 byte-exact theSVG assets, retaining registry claims as unverified metadata with explicit trademark and HOLD boundaries ([#11737](https://github.com/diegosouzapw/OmniRoute/pull/11737)). +- **chore(quality):** added a versioned 225/225 provider-asset provenance manifest and blocking drift gate ([#11743](https://github.com/diegosouzapw/OmniRoute/pull/11743)) — the inventory pins magic MIME and SHA-256 for every physical file, records immutable source matches for 71 assets, keeps 69 probable and 85 unresolved entries explicit but non-blocking, and treats provenance as source evidence rather than copyright or trademark clearance. +- **refactor(mitm):** replace four previously derived Traffic Inspector utilities — conversation normalization, SSE merging, secret masking, and header sanitization — with independent clean-room implementations based on public provider schemas, WHATWG SSE, and RFC 6750, while keeping the two independently classified passthrough surfaces unchanged ([#11748](https://github.com/diegosouzapw/OmniRoute/pull/11748)). +- Added a unit test that fails seven days before any dated pack under `config/` (`validUntil` and sibling keys) lapses, naming the file and key. The Alibaba free-tier pack expired on 2026-08-27 and turned every PR red the next morning with no commit involved; renewal now happens on someone's terms, not the clock's. ([#11891](https://github.com/diegosouzapw/OmniRoute/pull/11891)) +- `check:workflows` now fails (under `--strict`/`--ratchet`) when any job routed to a self-hosted runner publishes with `--provenance` — npm rejects that with `422` at the registry, which in v3.8.50 only surfaced after the tag and Docker images were public. ([#11895](https://github.com/diegosouzapw/OmniRoute/pull/11895)) +- `scripts/ops/runner-janitor.sh` now proves a path is idle with one `lsof` snapshot and removes stale leftovers itself (tmpfs after 3 h — it is RAM — disk after 24 h), kills orphan `next-build` processes, prunes checkouts of stopped runners, and alerts on memory pressure; `--dry-run` shows exactly what it would do. `docs/ops/RUNNER_BOX.md` reconciled to the measured box (31 GB, 10 listeners). ([#11893](https://github.com/diegosouzapw/OmniRoute/pull/11893)) +- The `next-build` artefact (1.3 GB) is now written and read under `$RUNNER_TEMP` (per-runner, on disk) instead of `/tmp`, which on the self-hosted pool is a 12 GB tmpfs in RAM. Landing it there took 27–32 of the publish job's 76 minutes, and the fixed `/tmp/e2e-build.tar.gz` name let E2E jobs on different runners overwrite each other's download. ([#11896](https://github.com/diegosouzapw/OmniRoute/pull/11896)) +- The CI `build` job now runs in two concurrency lanes — `main` and pull requests — so a release build is never queued behind (or OOM-killed beside) PR builds on the self-hosted pool, which holds one `next-build` comfortably and two at the edge. ([#11901](https://github.com/diegosouzapw/OmniRoute/pull/11901)) +- Re-freeze the ESLint suppressions on `release/v3.8.51` from a clean-room measurement (2 stale file entries pruned, 55 pre-existing `no-explicit-any` in six new files frozen under #11924) and drop the dead `GPT_SIZE_MAP` constant orphaned by the Adobe Firefly client split, so `No new ESLint warnings` stops failing every PR with exit 2 (Refs #11924) ([#11955](https://github.com/diegosouzapw/OmniRoute/pull/11955)) +- Type the 55 `no-explicit-any` sites that had been frozen under #11924 — four redundant casts in `socksConnectorWithFamily.ts` (undici/socks types already accept them) and the mocks/fixtures of the socks-timeout and isFree suites — and drop their suppression entries; the ESLint ratchet shrinks from 5487 to 5432 (Closes #11924) ([#11975](https://github.com/diegosouzapw/OmniRoute/pull/11975)) +- Take the two hosted-runner builds off the PR rail: `Build App` (`build.yml`) is `workflow_dispatch`-only and `DAST smoke (PR)` runs only for PRs into `main` — the 7 GB hosted VM cannot build `release/v3.8.51` in any profile (19/30 red, VM shutdown ~8 min into `next build`) and both checks had turned into permanent noise on every release PR; the bundle stays validated by `ci.yml` on `main` and by `nightly-release-green` on `release/**` (Closes #11946) ([#11962](https://github.com/diegosouzapw/OmniRoute/pull/11962)) +- Move the four nightly jobs that build the backend (`nightly-schemathesis`, `nightly-llm-security` promptfoo + garak, `nightly-resilience` axe-a11y) off the hosted 7 GB runner — where they died on `release/v3.8.51` unseen — onto the box's new `omni-light` pool (two listeners), and document the reshaped fleet (4 active OmniRoute listeners: 2 `omni-build` + 2 `omni-light`, janitor ceiling 4) (Closes #11965) ([#11967](https://github.com/diegosouzapw/OmniRoute/pull/11967)) +- Stop painting every fork PR into `release/**` red: `quality.yml` `Build (advisory)` is skipped (GitHub still reports `continue-on-error` failures as check FAILURE). Hosted `ubuntu-latest` cannot finish `npm run build` on this tree — same class as #11962 taking `build.yml` off the PR rail. `docker-publish.yml` amd64 now runs on the `.113` `omni-build` pool (31 GB / 32 cores, two listeners) with Turbopack, shares the `heavy-build-main` lane with `ci.yml` `Build` so it queues instead of becoming a third heavy, and keeps per-ref concurrency (a merge storm was starting 8 concurrent OOM builds). arm64 stays on `ubuntu-24.04-arm` with webpack — there is no ARM box. Fallback when `USE_VPS_RUNNER` is off: hosted amd64 + webpack (#11976). ([#12021](https://github.com/diegosouzapw/OmniRoute/pull/12021)) +- `docker-publish.yml` builds with webpack (`OMNIROUTE_USE_TURBOPACK=0`) on **both** arches, including amd64 on the `.113` `omni-build` pool. Turbopack had the RAM (31 GB, `omniroute-113-6`) and still panicked (`TurbopackInternalError: there must be a path to a root`, run 33253576569, 26 min in). The same tree's arm64 webpack build on hosted `ubuntu-24.04-arm` succeeded (run 33264823398). Dockerfile already documents webpack as the Docker escape hatch (#11976). ([#12050](https://github.com/diegosouzapw/OmniRoute/pull/12050)) +- **docs(free-tier):** declare the counting vs deciding regimes for "is it free?" and guard the deciding path from DB-backed catalog resolution ([#12226](https://github.com/diegosouzapw/OmniRoute/pull/12226)) — thanks @maxmad64bis +- **docs(auto-combo):** The mode pack table in `docs/routing/AUTO-COMBO.md` now lists all six shipped packs with every weight each one sets, replacing a four-pack table whose numbers had also drifted from the source. It states plainly that no pack sets `quality`, so selecting any pack silences the observed-quality signal. Six more documents that quote the scoring factor count joined the `check:docs-counts` gate, which caught five stale claims — including one naming nine factors that do not exist — and two stale mode pack counts. The dashboard routing panel, which offered four of the six packs and labelled the default strategy "6-Factor Scoring", is now covered by a test; the two packs it was missing are `reliability-first` and `chaos-mode`, the latter labelled as the fault-injection profile it is rather than as one more routing preference ([#12316](https://github.com/diegosouzapw/OmniRoute/pull/12316)) — thanks @maxmad64bis +- **chore(quality):** rebaseline `src/lib/db/apiKeys.ts` for the ACL the key-creation path now preserves ([#12352](https://github.com/diegosouzapw/OmniRoute/pull/12352)) ([#12673](https://github.com/diegosouzapw/OmniRoute/pull/12673)) — thanks @KrzysiekSko +- **chore(providers):** bump the Claude Code wire identity and the Devin bridge image pin from `2.1.220` to `2.1.258` ([#12402](https://github.com/diegosouzapw/OmniRoute/pull/12402)) — thanks @ggiak ([#12604](https://github.com/diegosouzapw/OmniRoute/pull/12604)) +- **docs(env):** align `.env.example`, the README Bun section, and the troubleshooting guide with the code: `OMNIROUTE_USE_TURBOPACK` also governs `npm run build` (not dev-only), `bun run build` follows that flag instead of auto-selecting Webpack, `OMNIROUTE_CHAT_MAX_HEAVY_IN_FLIGHT` is unset by default (no request-count cap), and the structural `503 chat_admission_busy` message matches `chatAdmissionResponses.ts` (#12404 — thanks @pacocartones) +- **chore(electron):** upgrade the desktop app to Electron 44 (Chromium 152, Node 24.18.1) ([#12217](https://github.com/diegosouzapw/OmniRoute/pull/12217)). **Requires macOS 13 (Ventura) or later** — Chromium dropped macOS 12 (Monterey), so Monterey users must stay on an earlier OmniRoute desktop build. Windows and Linux are unaffected; the app already shipped only x64/arm64, so Electron 44 dropping 32-bit builds changes nothing. Removes the `openAsHidden`/`wasOpenedAsHidden` login-item fields deleted in Electron 44 — hidden autostart continues to work through the `--hidden` argument registered with the login item ([#12554](https://github.com/diegosouzapw/OmniRoute/pull/12554)) — thanks @app/dependabot +- **chore(quality):** rebaseline `src/sse/handlers/chat.ts` for the effective-input persistence the continuation fix needs ([#12641](https://github.com/diegosouzapw/OmniRoute/pull/12641)) ([#12680](https://github.com/diegosouzapw/OmniRoute/pull/12680)) — thanks @hartmark +- `Coverage` job on `ci.yml`: the informational Codecov upload gets its own 5-minute ceiling and `continue-on-error`, and the job budget grows from 20 to 30 minutes (the 8-shard c8 merge alone takes ~10) — a stalled upload no longer ends the job `cancelled` and drags a fully green `main` run's conclusion down with it ([#11972](https://github.com/diegosouzapw/OmniRoute/pull/11972)) +- CI hardening on the PR rail: the four `Unit Tests fast-path` shards get `timeout-minutes: 30` (a hung shard held a PR for 64 min instead of GitHub's 6 h default) and both ESLint file caches lose their `restore-keys` fallback, so a cache built under another suppressions file or lint config can no longer report stale verdicts (Refs #11600, #11924) ([#11963](https://github.com/diegosouzapw/OmniRoute/pull/11963)) +- Every CI job that runs a `next build` (`build`, the npm `publish`, both release-green validations) now targets the `omni-build` runner label, which only two of the eight self-hosted runners carry. The box holds one build comfortably and two at the edge; a third now queues on GitHub instead of being OOM-killed by the kernel. ([#11932](https://github.com/diegosouzapw/OmniRoute/pull/11932)) +- **refactor(combo):** move `handleRoundRobinCombo` (and `resolveTargetTokenLimit`) into `open-sse/services/combo/roundRobinCombo.ts`. `combo.ts` drops from 2164 to 1014 lines (`split("\n").length`); the leaf is 1199 (under the 1200 new-file cap). The round-robin call site uses a dynamic `import()` so `releaseStickyPinOnFailure` / `clearStaleLKGP` can stay exported from `combo.ts` without a static cycle. Skip / sticky / semaphore / safety-timer behavior is unchanged. ([#12811](https://github.com/diegosouzapw/OmniRoute/pull/12811)) — thanks @HouMinXi +- **chore(quality):** rebaseline the file-size caps the error-boundary campaign grew past (`open-sse/executors/codex.ts`, `open-sse/vendor/codex-chatgpt-web/bridge.ts`, both via [#12444](https://github.com/diegosouzapw/OmniRoute/pull/12444)) ([#12654](https://github.com/diegosouzapw/OmniRoute/pull/12654)) +- **chore(quality):** rebaseline the file-size caps the hartmark batch grew past (`combos/page.tsx` via [#12355](https://github.com/diegosouzapw/OmniRoute/pull/12355), `open-sse/services/combo.ts` via [#12338](https://github.com/diegosouzapw/OmniRoute/pull/12338)) ([#12623](https://github.com/diegosouzapw/OmniRoute/pull/12623)) — thanks @hartmark +- **chore(quality):** rebaseline the file-size caps the HouMinXi batch grew past when its PRs stacked (`providers/page.tsx`, `chatCore.ts`, `accountFallback.ts`) — each PR measured correctly in isolation, none saw the stacking ([#12619](https://github.com/diegosouzapw/OmniRoute/pull/12619)) +- **chore(quality):** rebaseline `open-sse/services/combo.ts` for the reset-aware scoring the HouMinXi batch stacked ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) ([#12678](https://github.com/diegosouzapw/OmniRoute/pull/12678)) — thanks @HouMinXi +- **docs:** sync provider catalog count (direct commit `4f11b2ae3d`) +- **docs(skills):** sync omni-inference search fallback (direct commit `b4e76a7ed9`) +- **chore(lint):** prune two stale base suppressions (direct commit `1325047d56`) +- **test(auto-combo):** tolerate full-suite rotation load (direct commit `ad3e293f9f`) +- **test:** drain inherited quality snapshot reds (direct commit `21ed68d8ac`) +- **chore(quality):** rebaseline measured CLI bundle growth (direct commit `d35b3f9779`) +- **test(ui):** reproduce invalid AnySearch icon alias (direct commit `12cc6ca834`) +- **test(translator):** fix the relative imports of the relocated deferred-finish test (direct commit `777d9d1629`) +- **docs(counts):** bring the documented provider and migration counts to the measured values (direct commit `e70bea30e9`) +- **test(security):** drop the guard-target set left unused by the sync (direct commit `a32ba0e362`) +- **chore(db):** renumber model_capabilities migration 163 → 169 (cross-branch collision with 163_radar_feed_cache_generated_at) (direct commit `70f5d4cbf9`) +- **docs(cli):** document CLI_PRIME_AGENT_BIN and correct the CLI Agents count (direct commit `9058e39b61`) +- **chore(deps):** refresh runtimes and adopt ESLint 10 ([#11259](https://github.com/diegosouzapw/OmniRoute/pull/11259)) — thanks @backryun +- **refactor(local-corpus):** implement dynamic root resolution and LRU cache ([#11491](https://github.com/diegosouzapw/OmniRoute/pull/11491)) — thanks @raheemuddin786 +- **chore(quality):** prune stale eslint-suppressions entries ([#11596](https://github.com/diegosouzapw/OmniRoute/pull/11596)) +- **chore(ci):** unblock quality gates - env-doc sync + stryker tap.testFiles registration (#9282) ([#11609](https://github.com/diegosouzapw/OmniRoute/pull/11609)) — thanks @Meet6338-X +- **chore(ci):** point the circular-deps gate at dpdm's real JS entrypoint ([#11615](https://github.com/diegosouzapw/OmniRoute/pull/11615)) — thanks @benzntech +- **chore(quality):** rebaseline file-size for #11448/#11495/#11561 growth ([#11630](https://github.com/diegosouzapw/OmniRoute/pull/11630)) +- **deps:** realign onnxruntime-node pin to @huggingface/transformers' 1.24.3 lockstep ([#11633](https://github.com/diegosouzapw/OmniRoute/pull/11633)) — thanks @jonlwheat2-gif +- **test(autoCombo):** port models_dev_tier #11508 guard from node:test to vitest ([#11635](https://github.com/diegosouzapw/OmniRoute/pull/11635)) — thanks @jonlwheat2-gif +- **chore(quality):** rebaseline file-size for #11083/#11631/#11628 growth ([#11636](https://github.com/diegosouzapw/OmniRoute/pull/11636)) +- **chore(lint):** prune stale ESLint suppressions (base-red #11449) ([#11644](https://github.com/diegosouzapw/OmniRoute/pull/11644)) — thanks @jonlwheat2-gif +- **docs(changelog):** aggregate the ten v3.8.50 fragments into CHANGELOG.md ([#11683](https://github.com/diegosouzapw/OmniRoute/pull/11683)) +- **chore(search):** drop unreachable credential sweep after #11642 ([#11687](https://github.com/diegosouzapw/OmniRoute/pull/11687)) — thanks @HouMinXi +- **ci:** add API route TypeScript regression gate ([#11705](https://github.com/diegosouzapw/OmniRoute/pull/11705)) — thanks @Sabeekhann +- **docs(dedup):** correct the CodeQL claim on the tenant-namespace comment ([#11712](https://github.com/diegosouzapw/OmniRoute/pull/11712)) +- **docs(changelog):** add consolidated v3.8.50 stats, top-25 ranking and a .mailmap ([#11715](https://github.com/diegosouzapw/OmniRoute/pull/11715)) +- **docs:** recommend gstack for AI-assisted workflows ([#11770](https://github.com/diegosouzapw/OmniRoute/pull/11770)) — thanks @santosraju99-hub +- **chore(quality):** tighten complexity/cognitive-complexity ratchets to the current tip; land the missed gateways.ts rebaseline ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **docs(i18n):** finish Freepik → Magnific rebrand in locale strings and README ([#11772](https://github.com/diegosouzapw/OmniRoute/pull/11772)) — thanks @turbolego +- **refactor(auto-combo):** fix divergent scoring in combo health reporting ([#11854](https://github.com/diegosouzapw/OmniRoute/pull/11854)) — thanks @jacobsparts +- **chore(changelog):** add missing fragments for #11919/#11918/#11916 ([#11938](https://github.com/diegosouzapw/OmniRoute/pull/11938)) +- **test(check):** escape the runs-on fixture with JSON.stringify, not a quote-only replace ([#11942](https://github.com/diegosouzapw/OmniRoute/pull/11942)) (direct commit `fb7445eaa3`) +- **test(release):** align five suites with the contracts #11933, #11919 and #11876 shipped on release/v3.8.51 ([#11944](https://github.com/diegosouzapw/OmniRoute/pull/11944)) +- **chore(quality):** remove unreachable code and restore test discovery ([#11950](https://github.com/diegosouzapw/OmniRoute/pull/11950)) — thanks @backryun +- **docs(agents):** sync-back landings are fast-forward, never squash ([#11964](https://github.com/diegosouzapw/OmniRoute/pull/11964)) +- **test(infra):** retry recursive temp-dir removal instead of failing a shard on ENOTEMPTY (#11966) ([#11968](https://github.com/diegosouzapw/OmniRoute/pull/11968)) +- **docs(ops):** the .113 heavy-build ceiling is one runner, not two ([#12048](https://github.com/diegosouzapw/OmniRoute/pull/12048)) +- **test(ui):** correct inactive auto-fetch expectation ([#12098](https://github.com/diegosouzapw/OmniRoute/pull/12098)) — thanks @RaviTharuma +- **docs:** document native dependency check escape hatch ([#12101](https://github.com/diegosouzapw/OmniRoute/pull/12101)) — thanks @RaviTharuma +- **test(providers):** regenerate the translate-path golden for OrcaRouter (#11923) ([#12118](https://github.com/diegosouzapw/OmniRoute/pull/12118)) +- **chore(quality):** velocity phase — loosen every numeric baseline by 20% until v4.0, monitor headroom nightly ([#12125](https://github.com/diegosouzapw/OmniRoute/pull/12125)) +- **chore(lint):** batch 0 of #12146 — type the call-log-cap sqlite rows instead of 45 as-any casts ([#12157](https://github.com/diegosouzapw/OmniRoute/pull/12157)) +- **chore(lint):** batch 4 of #12146 — shared/components react-hooks violations resolved ([#12159](https://github.com/diegosouzapw/OmniRoute/pull/12159)) +- **chore(lint):** batch 1 of #12146 — dashboard/cli-code react-hooks violations resolved ([#12160](https://github.com/diegosouzapw/OmniRoute/pull/12160)) +- **chore(lint):** batch 3 of #12146 — dashboard/settings react-hooks violations resolved ([#12162](https://github.com/diegosouzapw/OmniRoute/pull/12162)) +- **chore(lint):** batch 2 of #12146 — resolve the react-hooks compiler violations in dashboard/providers ([#12163](https://github.com/diegosouzapw/OmniRoute/pull/12163)) +- **chore(quality):** register search-432 cooldown test in stryker tap.testFiles ([#12170](https://github.com/diegosouzapw/OmniRoute/pull/12170)) +- **chore(lint):** batch 5 of #12146 — combos, endpoint, provider-stats, api-manager and costs react-hooks violations resolved ([#12174](https://github.com/diegosouzapw/OmniRoute/pull/12174)) +- **build:** add contributor fast profile ([#12192](https://github.com/diegosouzapw/OmniRoute/pull/12192)) — thanks @rafacpti23 +- **docs(readme):** README touch-ups ([#12193](https://github.com/diegosouzapw/OmniRoute/pull/12193)) ([#12194](https://github.com/diegosouzapw/OmniRoute/pull/12194)) ([#12202](https://github.com/diegosouzapw/OmniRoute/pull/12202)) — thanks @rafacpti23 +- **build:** force the webpack bundler in the contributor build profile ([#12197](https://github.com/diegosouzapw/OmniRoute/pull/12197)) — thanks @rafacpti23 +- **build:** skip standalone packaging in the contributor profile ([#12198](https://github.com/diegosouzapw/OmniRoute/pull/12198)) — thanks @rafacpti23 +- **docs(audit):** align every published number with the code and harden check:docs-counts ([#12200](https://github.com/diegosouzapw/OmniRoute/pull/12200)) +- **build:** stub the instrumentation entrypoints in the contributor profile ([#12203](https://github.com/diegosouzapw/OmniRoute/pull/12203)) — thanks @rafacpti23 +- **build:** omit the standalone output target for contributor builds ([#12204](https://github.com/diegosouzapw/OmniRoute/pull/12204)) — thanks @rafacpti23 +- **chore(lint):** batch 6 of #12146 — memory, radar, audit, analytics, cache, usage, activity, home and RequestLoggerV2 react-hooks violations resolved ([#12208](https://github.com/diegosouzapw/OmniRoute/pull/12208)) +- **docs(diagrams):** rename number-carrying diagram files to stable names ([#12210](https://github.com/diegosouzapw/OmniRoute/pull/12210)) +- **docs(dashboard):** redraw onboarding tier-flow SVGs for the real 4-tier model ([#12211](https://github.com/diegosouzapw/OmniRoute/pull/12211)) +- **docs(api):** document every implemented route in openapi.yaml (276 → 692 paths) ([#12212](https://github.com/diegosouzapw/OmniRoute/pull/12212)) +- **chore(lint):** batch 7 of #12146 — final src tail: 37 react-hooks violations across 33 files resolved ([#12244](https://github.com/diegosouzapw/OmniRoute/pull/12244)) +- Revert "docs: recommend gstack for AI-assisted workflows (#11770)" ([#12248](https://github.com/diegosouzapw/OmniRoute/pull/12248)) ([#12249](https://github.com/diegosouzapw/OmniRoute/pull/12249)) +- **docs:** Chaos Mode setup guide + weighted strategy semantics ([#12250](https://github.com/diegosouzapw/OmniRoute/pull/12250)) +- **docs(agents):** protected-surface merge rule — operator approval for agent-instruction files ([#12253](https://github.com/diegosouzapw/OmniRoute/pull/12253)) +- **chore(quality):** register native-codex turn-pin tests in stryker tap.testFiles ([#12263](https://github.com/diegosouzapw/OmniRoute/pull/12263)) +- **chore(quality):** dedupe tap.testFiles entries added by racing base-red fixes ([#12265](https://github.com/diegosouzapw/OmniRoute/pull/12265)) +- **docs(readme):** introduce OmniRouteTray — the macOS menu-bar companion ([#12276](https://github.com/diegosouzapw/OmniRoute/pull/12276)) — thanks @ggiak +- **chore(proxy):** purge the legacy 1proxy residue — sync/rotator modules, dead DB exports, dead settings tab and flag, docs (#12091) ([#12290](https://github.com/diegosouzapw/OmniRoute/pull/12290)) +- **chore(quality):** baseline-headroom skips generated and vendored files in the fileSize worst-file signal ([#12291](https://github.com/diegosouzapw/OmniRoute/pull/12291)) +- **refactor(video):** unify the JPEG frame data-URI contract ([#12322](https://github.com/diegosouzapw/OmniRoute/pull/12322)) +- **chore(deps):** freeze onnxruntime-node and eslint-plugin-react-hooks out of dependabot groups ([#12329](https://github.com/diegosouzapw/OmniRoute/pull/12329)) +- **test(executors):** restore the #10986 reasoning-only fallback guards ([#12364](https://github.com/diegosouzapw/OmniRoute/pull/12364)) — thanks @pacocartones +- **chore(quality):** re-tighten the file-size ratchet to the real LOC (plan 3.8.52 task 0) ([#12411](https://github.com/diegosouzapw/OmniRoute/pull/12411)) +- **test(grok):** format web executor suite ([#12412](https://github.com/diegosouzapw/OmniRoute/pull/12412)) +- **chore(quality):** ratchet open-sse typecheck baseline to zero ([#12418](https://github.com/diegosouzapw/OmniRoute/pull/12418)) +- **test(build):** guard the artifact path policy arrays against duplicates ([#12422](https://github.com/diegosouzapw/OmniRoute/pull/12422)) +- **chore(lint):** adopt eslint-plugin-react-hooks 7.1.1 ([#12428](https://github.com/diegosouzapw/OmniRoute/pull/12428)) +- **chore(providers):** remove a keyless provider integration at its operator's request ([#12440](https://github.com/diegosouzapw/OmniRoute/pull/12440)) +- **docs(providers):** register providers removed at their operator's request and guard against reintroduction ([#12478](https://github.com/diegosouzapw/OmniRoute/pull/12478)) +- **chore(quality):** base fixes — stryker tap.testFiles + node_modules cache key ([#12482](https://github.com/diegosouzapw/OmniRoute/pull/12482)) +- **docs(arch):** one-process recipe for tens of long /v1/responses ([#12493](https://github.com/diegosouzapw/OmniRoute/pull/12493)) — thanks @RaviTharuma +- **chore(quality):** tighten the CodeQL ratchet baseline from 11 to 6 ([#12530](https://github.com/diegosouzapw/OmniRoute/pull/12530)) +- **chore(deps):** pin browserslist override to ^4.28.8 ([#12592](https://github.com/diegosouzapw/OmniRoute/pull/12592)) — thanks @KooshaPari +- **chore(deps):** bump fast-uri to 3.1.7 in the electron lockfile ([#12601](https://github.com/diegosouzapw/OmniRoute/pull/12601)) +- **docs(quality):** document how the CodeQL ratchet refreshes and how to tighten it ([#12611](https://github.com/diegosouzapw/OmniRoute/pull/12611)) +- **test(ci):** pin the openapi-security-tiers two-arm contract with an executing gate test (#12581) ([#12652](https://github.com/diegosouzapw/OmniRoute/pull/12652)) +- **docs(gamification):** align XP Rewards table with code (#12501) ([#12667](https://github.com/diegosouzapw/OmniRoute/pull/12667)) — thanks @KooshaPari +- **refactor(combo):** split executeTarget into gates, attempt, and loop ([#12746](https://github.com/diegosouzapw/OmniRoute/pull/12746)) — thanks @HouMinXi +- **docs(security):** document socket.yml scanner config + CI workflow link (#12575) ([#12764](https://github.com/diegosouzapw/OmniRoute/pull/12764)) — thanks @KooshaPari +- **chore(ci):** guard commit identity in pre-commit to stop author misattribution ([#12772](https://github.com/diegosouzapw/OmniRoute/pull/12772)) +- **deps:** 17 Dependabot bumps — bump electron from 43.4.0 to 43.4.1 in /electron ([#11426](https://github.com/diegosouzapw/OmniRoute/pull/11426)); bump the development group across 1 directory with 8 updates ([#11428](https://github.com/diegosouzapw/OmniRoute/pull/11428)); bump the production group across 1 directory with 11 updates ([#11440](https://github.com/diegosouzapw/OmniRoute/pull/11440)); chore(deps): bump github/codeql-action/init from 4.37.7 to 4.37.8 ([#11925](https://github.com/diegosouzapw/OmniRoute/pull/11925)); chore(deps): bump github/codeql-action from 4.37.7 to 4.37.8 ([#11926](https://github.com/diegosouzapw/OmniRoute/pull/11926)); chore(deps): bump github/codeql-action/analyze from 4.37.7 to 4.37.8 ([#11927](https://github.com/diegosouzapw/OmniRoute/pull/11927)); build(deps): bump github/codeql-action/init from 4.37.8 to 4.37.9 ([#12345](https://github.com/diegosouzapw/OmniRoute/pull/12345)); build(deps): bump github/codeql-action/analyze from 4.37.8 to 4.37.9 ([#12346](https://github.com/diegosouzapw/OmniRoute/pull/12346)); bump the development group across 1 directory with 2 updates ([#12347](https://github.com/diegosouzapw/OmniRoute/pull/12347)); build(deps): bump github/codeql-action from 4.37.8 to 4.37.9 ([#12349](https://github.com/diegosouzapw/OmniRoute/pull/12349)); bump browserslist from 4.28.2 to 4.28.8 ([#12396](https://github.com/diegosouzapw/OmniRoute/pull/12396)); bump the production group across 1 directory with 4 updates ([#12399](https://github.com/diegosouzapw/OmniRoute/pull/12399)); bump @xmldom/xmldom ([#12500](https://github.com/diegosouzapw/OmniRoute/pull/12500)); bump qs from 6.15.2 to 6.16.0 ([#12512](https://github.com/diegosouzapw/OmniRoute/pull/12512)); bump @xmldom/xmldom from 0.9.10 to 0.9.12 ([#12513](https://github.com/diegosouzapw/OmniRoute/pull/12513)); bump fast-uri from 3.1.5 to 3.1.7 ([#12514](https://github.com/diegosouzapw/OmniRoute/pull/12514)); bump @humanfs/node from 0.16.7 to 0.16.8 ([#12515](https://github.com/diegosouzapw/OmniRoute/pull/12515)) +- Clear the `release/v3.8.51` `check:agent-skills-sync` base-red: regenerate `skills/cli-tunnel/SKILL.md` so the `tunnel create [type]` positional that #13009 taught the generator to read is reflected in the committed skill. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` typecheck base-red from #12731: the new `Mode pack ranking gates` candidates in `open-sse/services/autoCombo/__tests__/autoCombo.test.ts` omitted the required `provider`, `model` and `errorRate` fields of `ProviderCandidate`, failing `check:open-sse-typecheck` (and with it `Fast Quality Gates`) on every open PR. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` docs-sync base-red: EURouter (#13025) and GreenPT (#13024) took the live provider count to 358, leaving 7 STRICT drifts (`PROVIDER_REFERENCE.md`, 4 diagrams, 2 tier-flow images) plus the `AGENTS.md` / `llm.txt` / `package.json` count claims stale. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- **test(models):** Pin `CATALOG_BUILD_TIMEOUT_MS` in the #12058 canonical-catalog regression so a cold catalog build racing the 8s default bound no longer flakes the suite with a 500 ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Allow `@omniroute/opencode-plugin-v2/` in the npm pack policy — [#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870) shipped the v2 plugin beside its v1 sibling without widening the allowlist, so every packed file under it read as an unexpected artifact and failed `check:pack-policy` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Point the release-green validator's pack gate at the tree it is validating — the build-provenance guard defaulted to `origin/main`, which a release branch tip can never be an ancestor of mid-cycle, so `Package artifact` reported a false red on every run and the tarball boot-smoke cascaded off it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **docs(checks):** keep doc counts honest — headings, rankings, catalog, weights, quality gate and scoring diagram now covered ([#12507](https://github.com/diegosouzapw/OmniRoute/pull/12507)) — thanks @maxmad64bis +- **chore(lifecycle):** `check:model-lifecycle` now also diffs `DEFAULT_DEGRADATION_MAP` (the background-task redirect table) against the vendor lifecycle snapshot, refusing a retired id as source or target, with a table-driven unit test beside it. Three rows whose source the vendor had retired — `claude-sonnet-4-20250514`, `gemini-3-pro-preview` and `gpt-5.1-codex` (whose target `gpt-5.1-codex-mini` is retired too) — were dead code, since `checkLifecycle` answers 410 before the redirect runs; they are dropped (#12535 — thanks @pacocartones) +- **test(compression):** cover idle worker eviction at the resource level — the pool must call `terminate()` and must not retain the worker's `MessagePort`, complementing the `exit`-event assertion added with the fix ([#13371](https://github.com/diegosouzapw/OmniRoute/pull/13371)) +- **chore(quality):** raise the `imageGeneration.ts` file-size ceiling for the image-only-model guard that clears the #12945 base-red ([#13392](https://github.com/diegosouzapw/OmniRoute/pull/13392)) +- **chore(quality):** type the combo-test route's JSON response bodies instead of casting them to `any`, drop the now-empty suppression entry, and remove the `hasPerModelQuota` import `chatCore.ts` stopped using when the failure-classification helper was extracted ([#13379](https://github.com/diegosouzapw/OmniRoute/pull/13379)) +- **chore(electron):** the desktop bundle no longer ships the root-level files of every translated docs mirror (`README.md`, `llm.txt`, `CLAUDE.md`, `GEMINI.md`, `CONTRIBUTING.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`) — the packaged app only reads `docs/i18n//docs/**`, which stays. Saves ~11 MB on top of the translated CHANGELOGs already pruned. (#0000) ([#13235](https://github.com/diegosouzapw/OmniRoute/pull/13235)) +- **chore(quality):** retighten the `src/sse/handlers/chat.ts` unused-vars suppression count to the 8 violations that actually remain ([#13385](https://github.com/diegosouzapw/OmniRoute/pull/13385)) +- Bring the documented counts back in line with the code: 171 migrations (was 169) and 20 routing strategies (was 19), plus the regenerated `cli-tunnel` skill reference. ([#12970](https://github.com/diegosouzapw/OmniRoute/pull/12970)) +- Update the documented migration count to 172 after the call-logs provider-stats indexes landed. ([#13209](https://github.com/diegosouzapw/OmniRoute/pull/13209)) +- **chore(ci):** two gates that close the blind spots behind the exclusions above. `check:vitest-exclusions` requires every Vitest exclusion to name a tracking issue and to appear in `config/quality/vitest-exclusions.json` — the previous list grew to 62 files behind a comment pointing at an issue that had been closed for a month. `check-new-key-coverage` requires a key newly added to `en.json` to reach every locale; the existing coverage gate is a percentage floor per locale, so eleven absent keys out of ~13,000 left it at 99.9% while a whole feature shipped untranslated in nine languages. Both are diff-aware, so pre-existing debt stays frozen and neither needed a migration to turn on. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **build:** root layout no longer loads Inter from `next/font/google`, so a production image build does not need fonts.googleapis.com ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **docs(i18n):** bring the eight new `llm.txt` mirrors (km, kn, ml, my, ne, or, pa, si) up to the current provider and migration counts, so `check-docs-sync` stops rejecting every commit ([#13674](https://github.com/diegosouzapw/OmniRoute/pull/13674)) +- **refactor(ui):** flow surfaces (home topology, combo live studio, compression cockpit/waterfall/nodes, token-health badge) express state through the theme-aware `--orch-status-*` tokens instead of fixed dark-mode hex, so green/red/amber/grey stay legible in the light theme; dark mode is byte-identical. Categorical palettes (routing-strategy hues, compression-layer pills, provider brand colors) deliberately stay hex ([#12378](https://github.com/diegosouzapw/OmniRoute/issues/12378)) ([#13203](https://github.com/diegosouzapw/OmniRoute/pull/13203)) +- **chore(tests):** 51 test files that had been excluded from Vitest are running again, restoring roughly 350 assertions to the blocking suite. Each was measured individually first: of the 62 files parked behind the `// #8618 — pre-existing failure` comment, 51 pass against the current tree with no source change, so the exclusions had outlived the failures they were added for. The 11 that genuinely still fail stay excluded, but now point at a live tracker (#13204) rather than at #8618, which was closed in August while the list it tracked kept growing. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **chore(quality):** drop two ESLint suppression entries whose violations no longer exist, so `eslint --suppressions-location` stops rejecting every commit that touches the surrounding files ([#13384](https://github.com/diegosouzapw/OmniRoute/pull/13384)) +- **chore(release):** bring main's post-tag fixes into release/v3.8.51 — sync-back step 2/2 (direct commit `21c488f210`) +- **chore(deps):** patch toml and esbuild in the opencode plugin lockfiles ([#12965](https://github.com/diegosouzapw/OmniRoute/pull/12965)) +- **docs(changelog):** reconcile the v3.8.51 living section — fold 366 fragments, cover every cycle commit, credit every contributor ([#12971](https://github.com/diegosouzapw/OmniRoute/pull/12971)) +- **test(plugin):** wait for the v2 lazy-refresh tier instead of sleeping past it ([#12984](https://github.com/diegosouzapw/OmniRoute/pull/12984)) +- **chore(quality):** rebaseline chatCore.ts after the non-streaming regression fixes ([#13045](https://github.com/diegosouzapw/OmniRoute/pull/13045)) +- **docs(changelog):** add the missing bullet marker to the DeepSeek PoW fragment ([#13200](https://github.com/diegosouzapw/OmniRoute/pull/13200)) +- **chore(deps):** drain the Dependabot queue — 10 of 13 alerts ([#13213](https://github.com/diegosouzapw/OmniRoute/pull/13213)) +- **chore(ci):** make worktrees carry the hooks that enforce the identity gate ([#13236](https://github.com/diegosouzapw/OmniRoute/pull/13236)) +- **chore(deps):** pin csv-parse to ^7.0.2 via overrides (#13117) ([#13300](https://github.com/diegosouzapw/OmniRoute/pull/13300)) — thanks @KooshaPari +- **chore(deps):** bump hono from ^4.12.34 to ^4.13.7 (#13148) ([#13301](https://github.com/diegosouzapw/OmniRoute/pull/13301)) — thanks @KooshaPari +- **chore(deps):** pin joi to ^18.2.8 via overrides (#13085) ([#13302](https://github.com/diegosouzapw/OmniRoute/pull/13302)) — thanks @KooshaPari +- **docs(providers):** add Microsoft 365 Copilot (BizChat) provider guide (#12779) ([#13340](https://github.com/diegosouzapw/OmniRoute/pull/13340)) — thanks @KooshaPari +### 🙌 Contributors + +Thanks to everyone whose work landed in v3.8.51: + +| Contributor | PRs / Issues | +| --- | --- | +| [@5dive-bot](https://github.com/5dive-bot) | #11852 | +| [@abhisheksharma2411](https://github.com/abhisheksharma2411) | #11547, #11684, #11959, #12209 | +| [@adevwithpurpose](https://github.com/adevwithpurpose) | #11464 | +| [@adivekar-utexas](https://github.com/adivekar-utexas) | #12015, #12043, #12090, #12138 | +| [@AIB1TAL0S](https://github.com/AIB1TAL0S) | #12103 | +| [@alltomatos](https://github.com/alltomatos) | #11448 | +| [@alvinveroy](https://github.com/alvinveroy) | #11857, #12027, #12165 | +| [@amaleta](https://github.com/amaleta) | #11906, #11934 | +| [@amartinawi](https://github.com/amartinawi) | #11575 | +| [@AndrianBalanescu](https://github.com/AndrianBalanescu) | #11083, #11830, #11834, #11844 | +| [@anhtahaylove](https://github.com/anhtahaylove) | #13091, #13092, #13093, #13096, #13097, #13100, #13106, #13109, #13114, #13155, #13166, #13171, #13175, #13187, #13196 | +| [@aniruddhaadak80](https://github.com/aniruddhaadak80) | #11760 | +| [@app](https://github.com/app) | #12554 | +| [@arminanton](https://github.com/arminanton) | #11461, #11513 | +| [@AStupidBear](https://github.com/AStupidBear) | #11584 | +| [@atescivitci-cmd](https://github.com/atescivitci-cmd) | #12105 | +| [@b3nw](https://github.com/b3nw) | #11970, #11971, #12147 | +| [@backryun](https://github.com/backryun) | #11259, #11950, #12075, #12076, #12078, #12079, #12081, #12082, #12181, #12228, #12239, #12255, #12258, #12277, #12367, #12423, #12524, #12538 | +| [@benjaminkitt](https://github.com/benjaminkitt) | #11747 | +| [@benzntech](https://github.com/benzntech) | #11614, #11615 | +| [@Bl0ck154](https://github.com/Bl0ck154) | #11948, #11951, #11952, #11953, #11954 | +| [@brick30llc-ctrl](https://github.com/brick30llc-ctrl) | #11752, #12242, #12262 | +| [@Chewji9875](https://github.com/Chewji9875) | #11455, #11875, #12028 | +| [@cheynetom](https://github.com/cheynetom) | #12058 | +| [@davidebaraldo](https://github.com/davidebaraldo) | #12222, #12304 | +| [@davidlinfr](https://github.com/davidlinfr) | #6390 | +| [@ddarkr](https://github.com/ddarkr) | #12124 | +| [@Deftera186](https://github.com/Deftera186) | #11809 | +| [@dpozimski](https://github.com/dpozimski) | #11945 | +| [@DW-MediaLab](https://github.com/DW-MediaLab) | #12229 | +| [@echel0nn](https://github.com/echel0nn) | #11923 | +| [@f9td56dbgh-hub](https://github.com/f9td56dbgh-hub) | #11660 | +| [@fabioluissilva](https://github.com/fabioluissilva) | #11991 | +| [@foreveryh](https://github.com/foreveryh) | #12177 | +| [@geek007git](https://github.com/geek007git) | #12115, #12116, #12117, #12120, #12122 | +| [@geekyNads](https://github.com/geekyNads) | #11784, #12266 | +| [@ggiak](https://github.com/ggiak) | #11561, #12276, #12350, #12402 | +| [@gonisulaimann](https://github.com/gonisulaimann) | #12368, #12369, #12371 | +| [@Gorillaz322](https://github.com/Gorillaz322) | #12207 | +| [@groovecityJO](https://github.com/groovecityJO) | #12682 | +| [@hartmark](https://github.com/hartmark) | #11434, #11452, #11473, #11499, #11703, #11983, #11984, #11985, #11986, #11988, #11989, #11990, #11994, #12221, #12293, #12445, #12446, #12447, #12448, #12460, #12461, #12623, #12646, #12650, #12680, #12717, #12718, #12727, #12741, #12854 | +| [@hizzt](https://github.com/hizzt) | #11894 | +| [@hongnoul](https://github.com/hongnoul) | #11484 | +| [@HouMinXi](https://github.com/HouMinXi) | #11411, #11414, #11512, #11518, #11520, #11641, #11642, #11643, #11687, #11779, #11849, #11850, #11851, #11915, #11916, #11918, #11919, #11920, #12013, #12017, #12033, #12042, #12106, #12139, #12166, #12169, #12171, #12205, #12213, #12312, #12325, #12487, #12488, #12495, #12504, #12557, #12566, #12590, #12591, #12624, #12626, #12632, #12637, #12678, #12696, #12697, #12711, #12733, #12746, #12767, #12770, #12789, #12803, #12805, #12811, #12866, #12868, #12899, #12926, #12934, #12950, #12951, #12974, #13001, #13006, #13011, #13017, #13026, #13027, #13034, #13035, #13038, #13042, #13050, #13060, #13061, #13069, #13107, #13120, #13136, #13195, #13197 | +| [@Hsia97](https://github.com/Hsia97) | #11624 | +| [@jacobsparts](https://github.com/jacobsparts) | #11854, #12155, #12167 | +| [@joglomedia](https://github.com/joglomedia) | #11980 | +| [@jonlwheat2-gif](https://github.com/jonlwheat2-gif) | #11443, #11450, #11549, #11567, #11608, #11633, #11635, #11640, #11644, #11671, #11672, #11673, #11674, #11675, #11676, #11677, #11706, #11783, #12051, #12052, #12053, #12055 | +| [@kanade-hoshino](https://github.com/kanade-hoshino) | #12180 | +| [@Karan825](https://github.com/Karan825) | #11861 | +| [@KaspaPulse](https://github.com/KaspaPulse) | #11389, #11469, #11628, #11666, #11910 | +| [@keeltrace](https://github.com/keeltrace) | #12080, #12223 | +| [@killer30001000](https://github.com/killer30001000) | #12178 | +| [@KooshaPari](https://github.com/KooshaPari) | #12470, #12592, #12667, #12699, #12703, #12706, #12764, #12769, #12771, #13300, #13301, #13302, #13340, #13401, #13403, #13404, #13405, #13406, #13407, #13409, #13410, #13411, #13412, #13413, #13414, #13418, #13424, #13427, #13433, #13523, #13524, #13525, #13528, #13531, #13532, #13534, #13539, #13543, #13545, #13546, #13547, #13550, #13553 | +| [@kriptoburak](https://github.com/kriptoburak) | #11370 | +| [@KrzysiekSko](https://github.com/KrzysiekSko) | #12425, #12432, #12673 | +| [@marcelokarval](https://github.com/marcelokarval) | direct commit / report | +| [@maxmad64bis](https://github.com/maxmad64bis) | #1622, #11435, #11437, #11441, #11537, #11550, #11553, #11555, #11557, #11812, #11842, #11843, #11903, #12151, #12214, #12215, #12218, #12226, #12314, #12316, #12317, #12318, #12319, #12320, #12321, #12507, #12715, #12731, #12744, #12786, #12787, #12788, #12790, #12792, #12794, #12795, #12828, #12832, #12853, #12857, #12870, #12937, #12941, #12975, #13141, #13142, #13146, #13147 | +| [@mdigitalbh81](https://github.com/mdigitalbh81) | #12240 | +| [@Meet6338-X](https://github.com/Meet6338-X) | #11598, #11609, #12110 | +| [@morpheus9393](https://github.com/morpheus9393) | #11943 | +| [@MumuTW](https://github.com/MumuTW) | #11492, #11502, #11506, #11507, #11626, #11675, #11685, #11728, #11746, #11888, #11889, #11890, #11892 | +| [@Neuron-Mr-White](https://github.com/Neuron-Mr-White) | #11622, #11801, #12256 | +| [@NightStalker-87](https://github.com/NightStalker-87) | #12183 | +| [@NoxzRCW](https://github.com/NoxzRCW) | #11879, #11880, #11881, #11882, #11883, #11935 | +| [@ntdat812](https://github.com/ntdat812) | #11585, #12095 | +| [@ntdatt812](https://github.com/ntdatt812) | #11368, #11573, #11574, #11576, #11577, #11580, #11582, #11583, #11588, #11589, #11590, #11591, #11592, #11593, #11672, #12177, #12180, #12873, #12918, #12920, #12921, #12925, #12930, #13007, #13009, #13024, #13025, #13083, #13087, #13101, #13104, #13110 | +| [@official-burak](https://github.com/official-burak) | #11542 | +| [@opensource-elearning](https://github.com/opensource-elearning) | #12179, #12189, #12278, #12286 | +| [@oyi77](https://github.com/oyi77) | #11408, #11409, #11421, #11505, #11677, #12036, #12110 | +| [@pacocartones](https://github.com/pacocartones) | #11521, #11522, #11527, #11528, #11529, #11530, #11531, #11532, #11533, #11534, #11595, #11599, #11603, #11604, #11605, #11607, #11610, #11676, #11714, #11716, #11718, #11767, #11838, #11860, #11862, #11869, #11871, #11872, #11873, #11903, #11906, #11921, #11934, #12356, #12358, #12359, #12360, #12361, #12362, #12364, #12365, #12373, #12374, #12375, #12376, #12377, #12379, #12380, #12381, #12386, #12387, #12389, #12390, #12394, #12395, #12397, #12401, #12403, #12404, #12522, #12523, #12535, #12536, #12540, #12541, #12543, #12545, #12548, #12549, #12550, #12551, #12552, #12647, #12651, #12653, #13055 | +| [@patrykkopycinski](https://github.com/patrykkopycinski) | #11936, #11937, #12224 | +| [@PauloFH](https://github.com/PauloFH) | #11509 | +| [@PauloHSOliveira](https://github.com/PauloHSOliveira) | #12241 | +| [@ponkcore](https://github.com/ponkcore) | #12054 | +| [@Prajeeth-12](https://github.com/Prajeeth-12) | #11634, #12046 | +| [@quiterunner-commits](https://github.com/quiterunner-commits) | #12131, #12143 | +| [@rafacpti23](https://github.com/rafacpti23) | #11554, #11558, #12192, #12197, #12198, #12202, #12203, #12204 | +| [@ragnar-claude](https://github.com/ragnar-claude) | #11564 | +| [@raheemuddin786](https://github.com/raheemuddin786) | #11491, #11839, #11840, #11841, #12003, #12230, #12231, #12232, #12233, #12234 | +| [@rahilmavani](https://github.com/rahilmavani) | #11761 | +| [@Rahulsharma0810](https://github.com/Rahulsharma0810) | #11771 | +| [@RaviTharuma](https://github.com/RaviTharuma) | #11710, #11727, #11797, #11798, #11802, #11805, #11806, #11811, #12098, #12099, #12101, #12449, #12452, #12472, #12473, #12493, #12533, #12607, #12628, #12631, #12636, #12875, #12876, #12880, #12882, #12884 | +| [@rezjalibd](https://github.com/rezjalibd) | #12186 | +| [@RhianB14](https://github.com/RhianB14) | #11692 | +| [@rifqiawl](https://github.com/rifqiawl) | #11517, #11519, #11969 | +| [@rqzbeh](https://github.com/rqzbeh) | #11390 | +| [@Sabeekhann](https://github.com/Sabeekhann) | #11699, #11705 | +| [@santosraju99-hub](https://github.com/santosraju99-hub) | #11755, #11770 | +| [@Seramicx](https://github.com/Seramicx) | #11568 | +| [@Siva010](https://github.com/Siva010) | #12191 | +| [@solstxce](https://github.com/solstxce) | #11597 | +| [@soroush5](https://github.com/soroush5) | #12691 | +| [@tenshiak](https://github.com/tenshiak) | #12279 | +| [@TheDemonTuan](https://github.com/TheDemonTuan) | #11468, #11470, #11471, #11482, #11548, #11758, #11775, #11814 | +| [@thomasmaerz](https://github.com/thomasmaerz) | #12834 | +| [@tuandinh0801](https://github.com/tuandinh0801) | #11454 | +| [@turbolego](https://github.com/turbolego) | #11621, #11762, #11772, #11774, #11781, #12216 | +| [@Tushar49](https://github.com/Tushar49) | #11392 | +| [@ujjawalkaushik1110](https://github.com/ujjawalkaushik1110) | #11863 | +| [@vermasomesh835](https://github.com/vermasomesh835) | #11794 | +| [@vsd2807](https://github.com/vsd2807) | #11565, #11619 | +| [@wahidsadik371-coder](https://github.com/wahidsadik371-coder) | #12126 | +| [@watchingdogs](https://github.com/watchingdogs) | #12031 | +| [@wildcard](https://github.com/wildcard) | #11620 | +| [@xiaoyaner0201](https://github.com/xiaoyaner0201) | #11460, #11662, #12083 | +| [@Xxx91n](https://github.com/Xxx91n) | #11690 | +| [@yourspraveen](https://github.com/yourspraveen) | #11146 | +| [@yxyxy](https://github.com/yxyxy) | #11671 | +| [@ZaimMarzuki](https://github.com/ZaimMarzuki) | #11960 | +| [@Zartharas](https://github.com/Zartharas) | #11340 | +| [@zero-executioner](https://github.com/zero-executioner) | #11631 | +| [@diegosouzapw](https://github.com/diegosouzapw) | maintainer | + +--- ## [3.8.50] — 2026-08-25 _Living section — regenerated 2026-08-12 from all cycle commits (cycle open `ed2db6cb19` → tip). Bullets carry the merged PR and its author; direct pushes listed separately._ diff --git a/docs/i18n/lt/CHANGELOG.md b/docs/i18n/lt/CHANGELOG.md index 24faf13f1b..ef5bc1782a 100644 --- a/docs/i18n/lt/CHANGELOG.md +++ b/docs/i18n/lt/CHANGELOG.md @@ -95,16 +95,1119 @@ ## [3.8.51] — TBD -_Living section — cycle opened at the v3.8.50 freeze (parallel-cycle model). Bullets are aggregated from `changelog.d/` fragments at each `/generate-release` phase._ +_Living section — reconciled 2026-09-15 from all cycle commits (`091589089c` → `c0f92ec98a`, 916 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 | **123** | +| 📝 Commits in the cycle | **916** | +| 🔀 Pull requests referenced | **892** | +| 📋 Changelog entries | **919** | +| 🙌 Contributors credited in entries | **121** | +| 🤖 Automated dependency commits | 18 | + +**Entries by type** + +| Type | Count | +| --- | ---: | +| 🐛 Fixes | 592 | +| ✨ Features | 131 | +| 🧹 Chore | 93 | +| 📚 Docs | 42 | +| 🧪 Tests | 32 | +| ♻️ Refactor | 8 | +| 🏗️ Build | 7 | +| ⚡ Performance | 7 | +| ⚙️ CI | 2 | +| ⏪ Reverts | 2 | +| 📦 Dependencies | 2 | +| 🔒 Security | 1 | + +### 🏆 Top 25 contributors this cycle + +_By commits in `091589089c..c0f92ec98a`, author identities consolidated via `.mailmap` and the merged PR's GitHub login. Bots excluded._ + +| # | Contributor | Commits | +| ---: | --- | ---: | +| 🥇 | diegosouzapw | 323 | +| 🥈 | Bob.Hou (@HouMinXi) | 79 | +| 🥉 | Paco Cartones (@pacocartones) | 77 | +| 4 | Dizzle (@maxmad64bis) | 49 | +| 5 | Koosha Paridehpour (@KooshaPari) | 43 | +| 6 | Nguyen Thanh Dat (@ntdatt812) | 31 | +| 7 | Ravi Tharuma (@RaviTharuma) | 26 | +| 8 | Markus Hartung (@hartmark) | 22 | +| 9 | Webman (@jonlwheat2-gif) | 22 | +| 10 | backryun | 17 | +| 11 | anhtahaylove | 15 | +| 12 | Rafa Martins (@rafacpti23) | 10 | +| 13 | Syed Raheemuddin (@raheemuddin786) | 10 | +| 14 | Nguyễn Viết Tuấn (@TheDemonTuan) | 8 | +| 15 | MumuTW | 7 | +| 16 | Paijo (@oyi77) | 7 | +| 17 | Tobias Andersen (@turbolego) | 6 | +| 18 | Bl0ck (@Bl0ck154) | 5 | +| 19 | SHANMUGAPRIYAN (@geek007git) | 5 | +| 20 | KaspaPulse | 5 | +| 21 | Abhishek Sharma (@abhisheksharma2411) | 4 | +| 22 | Abhishek Divekar (@adivekar-utexas) | 4 | +| 23 | Andrew B. (@AndrianBalanescu) | 4 | +| 24 | NoxzRCW | 4 | +| 25 | opensource-elearning | 4 | ### ✨ New Features +- **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). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- Added Google AI Studio Gemini batch text-to-speech support through `POST /v1/audio/speech`. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- 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. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(routing):** subscription-first auto groupings — `auto/subscription` routes only through plan-included connections with a documented hard-stop overage and fails closed on exhaustion, while `auto/thrifty` orders the pool `subscription → keyless → free → cheap → premium` and steps up one rung at a time as each is exhausted. Billing class comes from a curated per-connection catalog (uncurated is treated as metered, never plan-included), both reuse STRICT_ZERO_COST's per-connection verification, and a quota reading whose `resetAt` has passed is now refreshed regardless of TTL so routing returns to plan capacity as soon as it resets ([#11146](https://github.com/diegosouzapw/OmniRoute/pull/11146)) — thanks @yourspraveen +- **feat(providers):** publish a management-authenticated versioned web-session credential contract from OmniRoute's canonical browser credential metadata ([#11340](https://github.com/diegosouzapw/OmniRoute/pull/11340)) — thanks @Zartharas +- **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)) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add Xquik X search with typed results, credential validation, REST routing, and MCP selection ([#11370](https://github.com/diegosouzapw/OmniRoute/pull/11370)) — thanks @kriptoburak +- **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)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(dashboard):** surface durable exclusive managed leases in the existing Sessions view, keeping leased clients visible across idle gaps while marking connections with in-flight work as active ([#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389)) — thanks @KaspaPulse +- **build(bun):** allow Turbopack bundler flag on Bun 1.4+ with configurable Webpack fallback ([#11471](https://github.com/diegosouzapw/OmniRoute/pull/11471)) — thanks @TheDemonTuan +- feat(api): add an opt-in `modelVisibilityAllowlist`/`modelVisibilityDenylist` settings pair to curate exactly which models `/v1/models` advertises, mirrored into every `auto/*` combo candidate pool so a denied model cannot be routed to via combo selection either (#11481) ([#11997](https://github.com/diegosouzapw/OmniRoute/pull/11997)) +- **feat(rankings):** the Free Provider Rankings page shows what each provider actually served over the last 24 h. It ranked by ELO alone, which left a provider that answers every call with an error in first place; the usage data was already served by the API but never requested. A provider with too small a sample shows a dash, not a number ([#11546](https://github.com/diegosouzapw/OmniRoute/pull/11546)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **feat(guardrails):** enforce a bounded, deterministic contract for Video Bridge transcripts — 256 cues, 4096 input code units and 4 KiB UTF-8 per cue, 64 KiB total text, malformed-Unicode rejection, focus-window scoping, cross-source reconciliation with contributing-source metadata, and a structural provenance trust boundary so caller JSON can never self-assert `embedded`/`audio-bridge` provenance ([#11652](https://github.com/diegosouzapw/OmniRoute/issues/11652)) ([#12009](https://github.com/diegosouzapw/OmniRoute/pull/12009)) +- **feat(video):** orchestrate optional Video Bridge audio extraction and Audio Bridge STT behind a dual opt-in (operator setting AND per-request signal) — a new loopback-only broker `mode=audio` operation shares the frame path's exact process queue, deadline, AbortSignal, and byte budgets to extract a bounded mono 16 kHz PCM WAV from the same already-downloaded video, then reuses the existing Audio Bridge transcription boundary; provider segment timing is preserved when available and marked coarse otherwise, and every failure degrades to a visual-only-safe partial instead of throwing (#11654). ([#12012](https://github.com/diegosouzapw/OmniRoute/pull/12012)) +- Add a tenant-bound Video Bridge drill-down lifecycle on top of the existing secure cache substrate: opaque hashed handles (never raw session/video identifiers), preview/standard/detail multiresolution variants resampled on read, response pagination capped at 8 frames and 32 MiB, and a new authenticated `/api/v1/video-bridge/drilldown` consumer route that stays disabled for remote access by default and denies cross-key access with the same response as a nonexistent handle (no existence oracle). ([#12006](https://github.com/diegosouzapw/OmniRoute/pull/12006)) +- **test(video):** Add the Video Bridge FU-07/FU-09 promotion-evidence harness (#11656) — a frozen Zod manifest schema covering the 8 required scenario kinds (static scenes, rapid cuts, late facts, fades, blur, small text, close events, visual prompt injection) with a minimum of 3 repetitions per case, deterministic declarative fixture recipes (`videoBridgePromotionFixtures.ts`), a pure medians/p95 metrics aggregator, a pure FU-07/FU-09 promotion-verdict evaluator applying the ticket's exact thresholds (missing token usage always holds), a digest-only persistence layer that never retains raw media or raw model responses, and a versioned per-model promotion allowlist shipped empty with every model defaulting to `hold`. The FU-07/FU-09 promotion verdicts themselves remain HOLD — they require a real evidence run against real models on VPS 192.168.0.15. ([#12008](https://github.com/diegosouzapw/OmniRoute/pull/12008)) +- **feat(video bridge):** "embedded" transcript provenance can now be legitimately earned instead of merely asserted — a bounded, allowlisted (`mov_text`/`subrip`/`webvtt`) subtitle probe runs through the loopback-only Video Bridge broker (at most 2 streams, 10s subdeadline bounded by the request deadline, 256 KiB output, 4096-code-unit lines), normalized through a bounded, ReDoS-safe WebVTT parser and Zod-validated end to end. The adapter always resolves to an explicit `success`/`absent`/`transient_failure` outcome — a subtitle failure never breaks the visual description path, and only a fingerprint-verified broker response (never a caller-declared label) can produce embedded cues (#11659). ([#12011](https://github.com/diegosouzapw/OmniRoute/pull/12011)) +- Default new Antigravity-family connections (agy CLI imports and Antigravity OAuth connects) to model auto-sync, so live model discovery lands in the synced catalog and `/v1/models` picks up freshly released upstream models (e.g. Gemini 3.7 Flash tiers) without code changes. Existing connections keep their current setting; the per-connection dashboard toggle remains the opt-out. (#11685 — thanks @MumuTW) ([#11887](https://github.com/diegosouzapw/OmniRoute/pull/11887)) +- **feat(ui):** enable React Compiler (`reactCompiler: true` + `babel-plugin-react-compiler`) for automatic memoization at build time ([#11783](https://github.com/diegosouzapw/OmniRoute/pull/11783)) — thanks @jonlwheat2-gif +- **feat(providers):** add SeekAi (`seekai.cc`) as an OpenAI-compatible New-API gateway — catalog id `seekai` (alias `ska`), `https://seekai.cc/v1`, live `/v1/models` via `passthroughModels`, aggregator-list membership so New-API balance detection can opt in. No referral/aff codes. ([#11786](https://github.com/diegosouzapw/OmniRoute/issues/11786)) ([#12557](https://github.com/diegosouzapw/OmniRoute/pull/12557)) — thanks @HouMinXi +- **feat(zai):** add GLM-5.3-Flash Coding Plan support (1M context, 128K output, vision, `low|high|max` reasoning) and route `zai` GLM-5.3-family API-key traffic through the OpenAI-compatible Coding Plan endpoint with native thinking defaults ([#11801](https://github.com/diegosouzapw/OmniRoute/pull/11801)) — thanks @Neuron-Mr-White +- **feat(combo):** choose how combo models are ordered — manual, provider, score, or name — via a sort control in the dashboard builder, persisted in `config.modelSort` and re-applied on load and after add ([#11812](https://github.com/diegosouzapw/OmniRoute/pull/11812)) — thanks @maxmad64bis +- **feat(free):** custom models can be marked free-tier via `customModels[].isFree`; `isFreeModel()` is the first door and `hidePaidModels` respects it even for providers outside the free budget ([#11843](https://github.com/diegosouzapw/OmniRoute/pull/11843)) — thanks @maxmad64bis +- **feat(nodejs):** add `5dive` as a `configure` target — `omniroute configure 5dive` / `omniroute setup-5dive` write a 5dive auth profile that points an agent fleet's `claude` seats at OmniRoute, with the root-only write, the loopback-vs-`https` endpoint rule and the per-seat model pin handled explicitly ([#11852](https://github.com/diegosouzapw/OmniRoute/pull/11852)) — thanks @5dive-bot +- **feat(sse):** treat `max` as a first-class reasoning-effort tier and clamp per model family (GLM 5.1+/DeepSeek V4+/Kimi K3+ keep native `max`; o1/MiniMax/Grok/Muse Spark clamp to their upstream ceiling) ([#11875](https://github.com/diegosouzapw/OmniRoute/pull/11875)) — thanks @Chewji9875 +- **feat(providers):** the provider plugin manifest now advertises a `usage-fetch` capability for the 40 providers that have a wired usage/quota fetcher, so external dashboards can read it from `GET /api/v1/provider-plugin-manifest` instead of parsing `open-sse/services/usage.ts` after every release. Discovery only — no new fetcher, no quota change, and the Dashboard quota widget stays gated by `USAGE_SUPPORTED_PROVIDERS`. `USAGE_FETCHER_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/fetcherProviders.ts`) and is re-exported from `services/usage.ts`, keeping the manifest module a light leaf instead of pulling the ~490-module usage dispatcher into the manifest route. ([#11903](https://github.com/diegosouzapw/OmniRoute/pull/11903)) — thanks @maxmad64bis / @pacocartones +- **feat(plugins):** `OMNIROUTE_PLUGINS_DIR` sets the directory the runtime plugin scanner reads — and the root the plugin manager installs into — overriding the `HOME`-derived default, so a Docker/K8s deployment can point straight at its bind-mounted plugin tree instead of moving `HOME` just to relocate the scan path. An image that exports no home no longer scans `/tmp/.omniroute/plugins` in silence: the resolved directory is logged once at startup as `scanner.dir_resolved`, naming the input that won. Unset, behaviour is unchanged. Distinct from the CLI-only `OMNIROUTE_PLUGIN_PATH`, which finds `omniroute-cmd-*` command packages and never reached this scanner ([#11906](https://github.com/diegosouzapw/OmniRoute/pull/11906)) — thanks @amaleta / @pacocartones +- **feat(leases):** add an explicit owner-authenticated status action that returns only the active lease's privacy-safe configured connection and provider labels, with generation fencing and no credential or internal-id disclosure ([#11910](https://github.com/diegosouzapw/OmniRoute/pull/11910)) — thanks @KaspaPulse +- **feat(providers):** import-from-file modal shows per-row API errors and ships a downloadable CSV template ([#12071](https://github.com/diegosouzapw/OmniRoute/issues/12071)) ([#12504](https://github.com/diegosouzapw/OmniRoute/pull/12504)) — thanks @HouMinXi +- **feat(providers):** dashboard search matches connection name and `baseUrl` so imported OpenAI-compat nodes surface on the provider card ([#12108](https://github.com/diegosouzapw/OmniRoute/issues/12108)) ([#12495](https://github.com/diegosouzapw/OmniRoute/pull/12495)) — thanks @HouMinXi +- **feat(routing):** add a `score` Auto router strategy that selects the highest configured weighted score and reuses `explorationRate`. ([#12155](https://github.com/diegosouzapw/OmniRoute/pull/12155)) — thanks @jacobsparts +- Add a runtime feature flag to disable universal context handoffs globally without changing the default behavior. ([#12167](https://github.com/diegosouzapw/OmniRoute/pull/12167)) — thanks @jacobsparts +- **feat(providers):** the provider plugin manifest now also advertises a `usage-supported` capability for the 46 providers whose usage API is accepted by the server and Dashboard routes, so integrators can distinguish "the server will serve quota for this provider" from "a fetcher is wired" without reading TypeScript. Discovery only — no fetcher or quota change. `usage-fetch` resolves on id or alias (the usage dispatcher accepts both); `usage-supported` resolves on id alone, matching the runtime guard `USAGE_SUPPORTED_PROVIDERS.includes(providerId)`. `USAGE_SUPPORTED_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/supportedProviders.ts`) and is re-exported from `providers.ts`, mirroring the `fetcherProviders` leaf from #11903 and keeping the manifest a light module. ([#12214](https://github.com/diegosouzapw/OmniRoute/pull/12214)) — thanks @maxmad64bis +- **feat(rankings):** order Free Provider Rankings by what each provider actually served — `GET /api/free-provider-rankings?sortBy=reliability` and a "Most reliable first" toggle on the page. Providers with too few calls to state a success rate keep their score order below the measured ones; the default order is unchanged ([#12218](https://github.com/diegosouzapw/OmniRoute/pull/12218)). — thanks @maxmad64bis +- **perf(sse):** defer `cloneLogPayload()` in the structured SSE collector until after the `maxEvents`/`maxBytes` cap check, eliminating ~9,800 wasted `structuredClone` calls per streaming response (65–71% faster `push()`). Reducer snapshot isolation restored for OpenAI and Responses summaries ([#12241](https://github.com/diegosouzapw/OmniRoute/pull/12241)) — thanks @PauloHSOliveira ([#12243](https://github.com/diegosouzapw/OmniRoute/pull/12243)) +- **feat(usage):** Devin CLI agentic quota (Codeium seat-management GetUserStatus) and OpenRouter key limits plus account credits now surface in Provider Limits ([#12256](https://github.com/diegosouzapw/OmniRoute/pull/12256) — thanks @Neuron-Mr-White) +- **feat(settings):** persist `headroomUrl` through Settings so status/start use the operator URL instead of only `HEADROOM_URL` ([#12306](https://github.com/diegosouzapw/OmniRoute/issues/12306)) ([#12487](https://github.com/diegosouzapw/OmniRoute/pull/12487)) — thanks @HouMinXi +- **feat(auto-combo):** Auto-Combo scoring can now weigh how often a provider/model has actually succeeded. The engine already carried that number on every candidate — 24 hours of usage history behind a ten-sample floor, real-time metrics otherwise — and the scoring function never read it, while the weight table described `stability` as if it did. `reliability` (`1 - failureRate`, with the same field precedence and the same rate-bounding the speed ranking already uses, so a corrupt reading means "nothing observed" rather than "fails every call") is now a declared factor shipping at weight `0`, so routing is unchanged until an operator gives it one, and the `stability` description now matches what that factor computes ([#12317](https://github.com/diegosouzapw/OmniRoute/pull/12317)) — thanks @maxmad64bis +- **feat(routing):** With `freeAccessPolicy: "strict"`, the read-only candidate listing (`GET /v1/auto-combo/{channel}/candidates`) no longer hides the candidates the zero-cost guard excludes — the same read-only transparency the resilience filter already honours (#9133). Each candidate now carries `freeAccessExclusion` saying why it would be kept out, and it tells an exhausted allowance apart from a quota reading that never arrived or went stale, which used to look identical from the outside. Routing is unchanged: the listing reports, it never enforces. The separate `excludeTosAvoid` guard still drops its candidates without a reason; that gap is now documented rather than closed ([#12319](https://github.com/diegosouzapw/OmniRoute/pull/12319)) — thanks @maxmad64bis +- **feat(radar):** The Radar catalog table now shows two facts it was already receiving from the feed and dropping on the floor: the per-model rate limits (requests and tokens, per minute and per day) in a new column, and a badge when a provider's terms state it may train on the prompts you send. A limit of zero renders as zero rather than "rate-only" — for a ceiling those are opposite facts — and a model with no training statement gets no badge, because an absent statement is not a guarantee ([#12320](https://github.com/diegosouzapw/OmniRoute/pull/12320)) — thanks @maxmad64bis +- **feat(radar):** explain Community, single-use, contributor, supporter, recovery, abuse, offers, and privacy rules before either Radar activation action, and remove the superseded fixed-PR grant promise from every UI locale ([#12342](https://github.com/diegosouzapw/OmniRoute/pull/12342)) +- **feat(gamification):** the dashboard Profile page now shows the real daily streak — `/api/gamification/level` returns `streak: { current, longest }` (per key with `apiKeyId`, operator-wide maximum otherwise) and the streak card reads it instead of a hard-coded 0 (#2403) ([#12377](https://github.com/diegosouzapw/OmniRoute/pull/12377)) — thanks @pacocartones +- **feat(gamification):** the dashboard leaderboard now shows each API key's display name under the Name column instead of a truncated key id; `GET /api/gamification/leaderboard` attaches `name` per entry (name only — no key material), while the shared ranking helper and the federation leaderboard stay id-only — thanks @pacocartones ([#12385](https://github.com/diegosouzapw/OmniRoute/pull/12385)) +- **feat(gamification):** enforce the documented 1000 XP/min per-API-key anti-cheat rate limit on the XP award path; over-limit awards are logged and skipped instead of persisted, and the sliding window now matches the timestamp format stored in `xp_audit_log` ([#2403](https://github.com/diegosouzapw/OmniRoute/issues/2403)) ([#12390](https://github.com/diegosouzapw/OmniRoute/pull/12390)) — thanks @pacocartones +- **feat(admin):** localize the gamification anomalies page — the loading state, the Status column and the Suspicious badge now come from the `common` catalog (new `common.suspicious` key propagated to every locale) — add it to the Gamification sidebar group as `gamification-admin` (`/dashboard/gamification/admin`), and expose the loading and empty states as polite `role="status"` live regions (#12401 — thanks @pacocartones) +- **feat(providers):** skip GitHub combo members missing from the live synced catalog, and drop Copilot models that are policy-disabled or hidden from the model picker ([#12473](https://github.com/diegosouzapw/OmniRoute/pull/12473)) — thanks @RaviTharuma +- **feat(i18n):** locale-expansion tooling — `npm run i18n:add-locale` adds a language to config, dashboard, docs mirrors, CLI, README/indexes and site in one command; browser, CLI and cookie detection resolve `uk`, `fil`/`tl`, `zh-Hant` (and the retired legacy `in` → `id`) via config aliases; `config/i18n.json` now ships in the npm package so the published CLI can read it; new real-translation ratio gate (`npm run i18n:check-ratio`, advisory) with a per-locale ratchet baseline; `run-translation --adopt` restores `.i18n-state.json`; `sync-language-bars` generates 🌐 bars from config; `validate_translation.py` loads its allowlist again. Retires the duplicate `in` locale (Indonesian mislabelled as Hindi) — 42 honest locales; saved `NEXT_LOCALE=in` / `OMNIROUTE_LANG=in` keep working. (#12496) +- **feat(api):** Emit gateway-measured `tokens_per_second` (TTFT excluded) on streaming usage and `X-OmniRoute-Tokens-Per-Second` when first-token latency is known ([#12616](https://github.com/diegosouzapw/OmniRoute/issues/12616)) ([#12631](https://github.com/diegosouzapw/OmniRoute/pull/12631)) — thanks @RaviTharuma +- **feat(opencode-plugin): map gateway cost/usage/tok/s onto OpenCode inference payloads** — the official plugin copies `X-OmniRoute-Response-Cost`, token counts, `X-OmniRoute-Tokens-Per-Second` / `usage.tokens_per_second`, TTFT, and the winning `X-OmniRoute-Model` onto the JSON/SSE body OpenCode already consumes. Missing tok/s is left unset (never `tokens / latency`). (#12636) — thanks @RaviTharuma +- **feat(combo):** add combo strategy `quota-weighted`: skip exhausted accounts, then weighted-draw by leftover / in-flight load; existing conversations stay pinned until the account is empty ([#12789](https://github.com/diegosouzapw/OmniRoute/pull/12789)) — thanks @HouMinXi +- **feat(models):** Account live listings become the chat catalog source for Claude, Codex, Copilot, and AGY; public metadata only fills prices on IDs those accounts already list. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **fix(models):** Union `agy` and `antigravity` live catalogs so `agy/gemini-3.8-flash-high` is not rejected after the prefix folds to `antigravity`. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **feat(opencode):** Add `@omniroute/opencode-plugin-v2`, an opencode v2 plugin that gives a v2 install a route to an OmniRoute gateway: it publishes models, combos and auto-combos into the host catalog, refreshes them lazily behind a 300s TTL, and keeps serving the last known catalog from an on-disk snapshot when the gateway is unreachable. The v1 plugin (`@omniroute/opencode-plugin`) is untouched and keeps working as before. The plugin adapts to the host instead of assuming one shape: it reads the shape the host seeds into the catalog draft and publishes the `api` block, the pre-`api` top-level fields, or both. The gateway key comes from the host credential store when one is connected, so no secret needs to sit in a config file; a source the gateway refuses is named in a warning instead of silently degrading the catalog to raw model ids; and tool schemas bound for a Gemini model are stripped of the JSON-Schema keywords Gemini rejects, so tool calling survives that route ([#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870)) — thanks @maxmad64bis +- feat(services): show sanitized CLIProxyAPI account health from its authenticated management API without exposing credentials, file paths, or raw account metadata (#6342) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add AnySearch free web search + URL extract (webFetch) with typed results, credential validation, REST routing, and MCP selection - fallback-only ([#11690](https://github.com/diegosouzapw/OmniRoute/pull/11690)) — thanks @Xxx91n +- **feat(dashboard):** display clamped `[0, 100]%` cached input token ratio in request logs table ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) — thanks @b3nw +- **feat(catalog):** add `OMNIROUTE_DISABLE_THINKING_LEVEL_VARIANTS` feature flag to optionally filter out thinking level variants from model catalog ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) — thanks @b3nw +- **feat(dashboard):** continuously export call logs to external analytics stores. A pluggable destination registry ships the full Logs-tab record set on an hourly `JobRegistry` cron, with a persisted per-destination cursor, batched inserts, a config UI rendered from each destination's own field descriptors, and a REST layer (`/api/log-export/*`) for CRUD, a connection test, and an on-demand run. A destination can opt into `includeBodies` to also ship the request and response payloads shown in the Logs detail pane, including the client and provider views of each call; this is off by default, and payloads inherit the dashboard's PII sanitisation, secret redaction and `noLog` handling. Google BigQuery is the first destination, using a service-account key stored encrypted at rest and streaming inserts keyed by call-log id, into a table that is day-partitioned on `timestamp` and clustered on `api_key_name`, `provider`, `model` and `status`. ([#11945](https://github.com/diegosouzapw/OmniRoute/pull/11945)) — thanks @dpozimski +- **feat(providers):** add MaxAI as a signed, OpenAI-compatible provider serving its 13 paid chat models (GPT-5.6 / Luna / Thinking, Claude 5 Sonnet, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite, Grok 4.1-fast / 4.5, DeepSeek V3.2 / R1, Llama 3.3 70B) through OmniRoute's `/v1` endpoint, with per-request HMAC-SHA1→SM3→AES request signing, live model + context-window discovery from `/models/get_config`, and prompted tool-calling translated to OpenAI `tool_calls` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI vision input — image_url content parts are forwarded inline in `message_content` to the 6 vision-capable models (GPT-5.6 / Luna / Thinking, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite) ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI image generation — 6 image models (gpt-image-1, dall-e-3, flux-1-schnell/dev/pro, sd3-medium) exposed through `POST /v1/images/generations` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI document RAG — inline base64 file/document attachments are uploaded to MaxAI (content-addressed `doc_id`) and attached to the chat via `doc_list` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** browserless MaxAI onboarding — email device-pair login (`/api/providers/[id]/login`) and signed access-token refresh, so a connection can be created and kept fresh without a real browser or Google OAuth ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** per-provider TLS impersonation profile (MaxAI presents a Windows Firefox-150 client fingerprint) so its bot-sensitive endpoints accept OmniRoute traffic ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** add **Nimble** as a web-search and web-fetch provider (`nimble-search`) — `/v1/search` routes to Nimble's search API at `lite` depth (locale, freshness and include/exclude domain filters mapped onto the shared request contract), and `/v1/web/fetch` routes to Nimble Extract, which covers all four fetch formats (`markdown`, `html`, `links`, `screenshot`) from a single call. One API key serves both surfaces. ([#11620](https://github.com/diegosouzapw/OmniRoute/pull/11620)) — thanks @wildcard +- The npm publish is automatic again, through npm Trusted Publishing (OIDC): the hosted `stage-npm` job publishes with a short-lived credential minted from GitHub's id-token — no `NPM_TOKEN`, no 2FA prompt, provenance attached. `publish_mode=staged` (owner approves with 2FA) and `direct` (token) remain available on `workflow_dispatch`. ([#11931](https://github.com/diegosouzapw/OmniRoute/pull/11931)) +- **feat(providers):** Add **Opper** as an API-key gateway provider — EU-hosted AI gateway with 700+ models from 30+ providers behind one OpenAI-compatible API and one key (`OPPER_API_KEY`); model ids use `provider/model` format (e.g. `anthropic/claude-sonnet-4-6`, `openai/gpt-5`); live model catalog at `https://api.opper.ai/v3/compat/models`; entry mirrors `requesty` (same shape, `passthroughModels: true`, no static seed) ([#11629](https://github.com/diegosouzapw/OmniRoute/pull/11629)) +- **feat(dashboard):** the `/dashboard/orchestration` snapshot hook now subscribes to the `agents` WebSocket channel (`agent.task.updated`) instead of `requests` as its refetch trigger, and relaxes its background poll from 5s to 30s while that WS connection is up — falling back to the tighter 5s cadence, reprogrammed live on any connect/disconnect transition, whenever the socket is down. ([#12409](https://github.com/diegosouzapw/OmniRoute/pull/12409)) +- **feat(dashboard):** Orchestration canvas quick wins — search box plus state/source/provider filter chips with a one-click clear, and per-source collapse/expand, all reflected in the URL so a filtered/collapsed view is shareable and survives a refresh; the detail drawer gained a "copy trace JSON" action and hardened error/empty-state and accessibility handling; the Agents-tab edges now animate traveling particles along active (running) connections; and the canvas node/edge status colors moved off fixed hex values onto theme-aware `--orch-status-*` CSS custom properties, so they adapt correctly to light/dark mode. ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- **chore(dashboard):** Orchestration UI hardening pass and the missing component/model test coverage it called for — `OrchestratorNode`/`ActivityNode`/`OverflowNode` rendering, the `?node=`/overflow-click page routing, the Agents-tab orchestrator-click no-op and `showCompleted` toggle, and the overview kanban's done-column sort order (#12270, #12271). ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- New `/dashboard/orchestration` page: live unified view of everything running — Cloud Agent, A2A and Conductor as a real-time graph (Agents tab), the combo cascade (Routing tab, reusing the Combo Live Studio) and a state kanban (Overview tab), with a detail drawer (trace, cost, approve/cancel). Read-only over existing APIs — no new backend. Canvas concept credit: PR #11815 design ([#12261](https://github.com/diegosouzapw/OmniRoute/pull/12261)) +- **feat(dashboard):** new "History" tab on `/dashboard/orchestration` — an Airflow-style grid of finished runs over a 24h/7d/30d preset window, one row per (source, identity), clicking a cell opens the existing detail drawer. It is backed by real persistence: A2A task lifecycle transitions are now written to the `a2a_tasks` table (purged after 30 days, configurable via `OMNIROUTE_A2A_HISTORY_RETENTION_DAYS`) and served by the new `GET /api/a2a/tasks/history` listing endpoint, with the task-detail route falling back to persisted history once a run leaves the in-memory snapshot. Conductor runs stay remote and are not persisted locally — the tab says so instead of silently omitting them. ([#12479](https://github.com/diegosouzapw/OmniRoute/pull/12479)) +- **feat(dashboard):** the orchestration detail drawer gained a "Repeat" action for Cloud Agent, A2A and Conductor tasks — a two-click confirm (click once to arm, click again within the confirm window to fire) re-submits the original prompt/input as a new run. The button is disabled with an explanatory tooltip whenever the original input can't be recovered from the loaded task detail (e.g. it never carried a prompt, or the detail failed to load). Two limitations of the A2A variant, by design: it targets the `/a2a` JSON-RPC endpoint, which authenticates with an API key only (`REQUIRE_API_KEY=true` or a configured `OMNIROUTE_API_KEY` makes a dashboard-session repeat answer `HTTP 400` — surfaced verbatim in the drawer's error line, never as a success), and the `message/send` call is SYNCHRONOUS: the POST blocks for the whole skill run, so the success confirmation only appears once the repeated task finishes. A dashboard-authenticated A2A creation path is deliberately left to a follow-up — widening the endpoint's auth posture is an operator decision, not a side effect of this feature. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(a2a):** A2A task execution now records which memories were consulted for the task's last user message as `metadata.memoryHits` (id/key/type/content-snippet) plus a `memory_hits` history event, purely for observability — the retrieved memory is never injected into a skill's prompt or behavior. Gated by the `OMNIROUTE_A2A_MEMORY_HITS` kill-switch (default enabled; set to `0` to skip the recall lookup entirely). The drawer's new "Memory used" section lists these hits for a2a tasks and is omitted whenever there are none. Known limitation: recall only resolves under the keyless posture — a keyed caller's task owner is a SHA-256 prefix of the API key, while memory rows are keyed by the database api-key id, and no hash→id lookup exists today, so the hit list stays empty for keyed callers. The recorded hits are also kept out of the task's own `input` (and therefore out of the persisted input and of the "Repeat" request body), so repeating a task never re-sends the previous run's memory snippets. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(providers):** add a Perplexity Agent API provider (`perplexity-agent` / `pplx-agent`) for Perplexity `/v1/responses`, including the documented Anthropic, OpenAI, Google, xAI, DeepSeek, Z.AI, Moonshot/Kimi, NVIDIA, and Perplexity model IDs plus Anthropic-model `max_output_tokens` compatibility. ([#12103](https://github.com/diegosouzapw/OmniRoute/pull/12103)) — thanks @AIB1TAL0S +- **feat(providers):** add RPD (Requests Per Day) limit to provider rate limit overrides across UI, schemas, DB, and i18n ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) — thanks @b3nw +- **feat(providers): add UC Direct (uncensored.com Developer API), the metered OpenAI-compatible surface.** A standard OpenAI-compatible passthrough (default executor) for uncensored.com's official REST API at `https://api.uncensored.com/api/v1`: `X-api-key` auth (never-expiring `uai_sk_live_` key), `POST /chat/completions` with streaming SSE and native tool-calling, and the full live metered catalog (82 models across 15 providers, discovered from the public `GET /v1/models`). Registered as provider `uc-direct` (alias `ucd`). Complements the un-metered `uc` persona provider — same models, metered credits and a plain API key instead of a subscription session. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(providers): add UC (uncensored.com), the un-metered subscription "persona" chat as an OpenAI-compatible provider.** A WebSocket web-app port: a durable Clerk credential mints a short-lived session token per connect (browserless — no API key), driving UC's persona socket. Ships the browserless email-code login (request → verify → harvest), the 19 verified persona models (Claude Opus, Gemini, Grok, GLM, Kimi, DeepSeek, MiniMax, incl. the uncensored variants), prompted `` tool-calling with a per-model code-style dialect + auto-cure retry for guardrailed models, live ``/reasoning split, streaming + non-streaming OpenAI responses, and full quota/auth error surfacing (paywall / message-limit / rate-limit → 429, invalid session → 401 re-login). Full multimodal parity via the persona blob-upload layer: **vision** (image input, 15 vision-capable models), **document RAG** (PDF/doc upload, server-side extraction), **image generation** (22 models), **video generation** (14 models, async signed-url → poll), and **TTS** (streaming MP3). Registered as provider `uc` (alias `ucn`). The metered OpenAI-compatible Developer API is a separate `uc-direct` provider. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(logs):** add copy-all button to request detail modal ([#11083](https://github.com/diegosouzapw/OmniRoute/pull/11083)) — thanks @AndrianBalanescu +- **feat(check):** make serviceKinds required and add the reverse-walk provider consistency gate ([#11392](https://github.com/diegosouzapw/OmniRoute/pull/11392)) — thanks @Tushar49 +- **feat(providers):** only advertise verified-working connections by default ([#11448](https://github.com/diegosouzapw/OmniRoute/pull/11448)) — thanks @alltomatos +- **feat(resilience):** add hierarchical concurrency admission ([#11493](https://github.com/diegosouzapw/OmniRoute/pull/11493)) +- **feat(providers):** derive + surface expiry for JWT-bearing web cookies (#11497) ([#11505](https://github.com/diegosouzapw/OmniRoute/pull/11505)) — thanks @oyi77 +- **feat(tunnels):** support named cloudflared tunnels via CLOUDFLARED_CONFIG ([#11561](https://github.com/diegosouzapw/OmniRoute/pull/11561)) — thanks @ggiak +- **feat(compression):** make proactive context-compression threshold a live setting ([#11564](https://github.com/diegosouzapw/OmniRoute/pull/11564)) — thanks @ragnar-claude +- **feat(resilience):** add provider peak-hour protection ([#11622](https://github.com/diegosouzapw/OmniRoute/pull/11622)) — thanks @Neuron-Mr-White +- **feat(providers):** add 1min.ai provider ([#11631](https://github.com/diegosouzapw/OmniRoute/pull/11631)) — thanks @zero-executioner +- **feat(dashboard):** collapsible JSON tree viewer for request/response payloads ([#11703](https://github.com/diegosouzapw/OmniRoute/pull/11703)) — thanks @hartmark +- **feat(routing):** add LiquidAI LFM2.5-2.6B free tier via OpenRouter ([#11752](https://github.com/diegosouzapw/OmniRoute/pull/11752)) — thanks @brick30llc-ctrl +- **feat(pricing):** add GLM-5.3-Flash pricing, model specs, and catalog registration ([#11830](https://github.com/diegosouzapw/OmniRoute/pull/11830)) — thanks @AndrianBalanescu +- **feat(routing):** add relayMode for schema-locked context handoffs ([#11839](https://github.com/diegosouzapw/OmniRoute/pull/11839)) — thanks @raheemuddin786 +- **feat(config):** add support for runtime system prompt configuration and hot-reloading ([#11841](https://github.com/diegosouzapw/OmniRoute/pull/11841)) — thanks @raheemuddin786 +- **feat(routing):** expand connection-aware quota prefilter across combo strategies (#11682) ([#11850](https://github.com/diegosouzapw/OmniRoute/pull/11850)) — thanks @HouMinXi +- **feat(models):** reactive discovery sync after Antigravity model-not-found 404 ([#11888](https://github.com/diegosouzapw/OmniRoute/pull/11888)) — thanks @MumuTW +- **feat(models):** auto-aliases and token limits from synced Antigravity discovery ([#11889](https://github.com/diegosouzapw/OmniRoute/pull/11889)) — thanks @MumuTW +- **feat(settings):** raise sticky round-robin limit caps to 1000 ([#12015](https://github.com/diegosouzapw/OmniRoute/pull/12015)) — thanks @adivekar-utexas +- **feat(resilience):** operator-configurable global credential health check interval ([#12043](https://github.com/diegosouzapw/OmniRoute/pull/12043)) — thanks @adivekar-utexas +- **feat(quota):** use official OpenCode Go usage API ([#12124](https://github.com/diegosouzapw/OmniRoute/pull/12124)) — thanks @ddarkr +- **feat(resilience):** default the credential health check sweep to 60 minutes ([#12138](https://github.com/diegosouzapw/OmniRoute/pull/12138)) — thanks @adivekar-utexas +- **feat(quality):** new-code mode for the complexity and dead-code ratchets (Clean as You Code) ([#12142](https://github.com/diegosouzapw/OmniRoute/pull/12142)) +- **feat(dashboard):** orchestration canvas — unified model + snapshot hook (part 1/2) ([#12156](https://github.com/diegosouzapw/OmniRoute/pull/12156)) +- **feat(usage):** add Kilo Code balance and Kilo Pass quotas ([#12178](https://github.com/diegosouzapw/OmniRoute/pull/12178)) — thanks @killer30001000 +- **feat(providers):** refresh vendored ChatGPT Web connector to v4.0.7 ([#12181](https://github.com/diegosouzapw/OmniRoute/pull/12181)) — thanks @backryun +- **feat(providers):** manual "Clear cooldown" action in the cooling panel ([#12224](https://github.com/diegosouzapw/OmniRoute/pull/12224)) — thanks @patrykkopycinski +- **feat(providers):** restore ChatGPT Web via clean-room browser transport ([#12239](https://github.com/diegosouzapw/OmniRoute/pull/12239)) — thanks @backryun +- **feat(sse):** wire the PROVIDER_PROFILES window gate into the global provider cooldown ([#12247](https://github.com/diegosouzapw/OmniRoute/pull/12247)) +- **feat(providers):** modernize CLOVA Studio chat and embeddings ([#12277](https://github.com/diegosouzapw/OmniRoute/pull/12277)) — thanks @backryun +- **feat(browser):** adopt Obscura as primary headless browser engine with Chromium fallback ([#12286](https://github.com/diegosouzapw/OmniRoute/pull/12286)) — thanks @opensource-elearning +- **feat(sse):** full language parity for output styles (es/de/fr/it/ru/zh + autoDetect) ([#12289](https://github.com/diegosouzapw/OmniRoute/pull/12289)) +- **feat(quality):** complete test:scoped — --full map rebuild, stdin selection, CI loader parity (#8084 D1) ([#12353](https://github.com/diegosouzapw/OmniRoute/pull/12353)) +- **feat(providers):** refresh Fable, Cursor, and Devin catalogs ([#12367](https://github.com/diegosouzapw/OmniRoute/pull/12367)) — thanks @backryun +- **feat(video):** redact transcript text from logs and durable memory (#12150 P1) ([#12427](https://github.com/diegosouzapw/OmniRoute/pull/12427)) +- **feat(dashboard):** parent-link, genuine-continuation badge, and modal perf fixes ([#12448](https://github.com/diegosouzapw/OmniRoute/pull/12448)) — thanks @hartmark +- **feat(dashboard):** adaptive context-budget dial on compression panel ([#12488](https://github.com/diegosouzapw/OmniRoute/pull/12488)) — thanks @HouMinXi +- **feat(providers):** refresh Z.ai Web models and browser transport ([#12524](https://github.com/diegosouzapw/OmniRoute/pull/12524)) — thanks @backryun +- **feat(video):** redact raw client-snapshot transcript fields in the detailed log (#12150 P2) ([#12528](https://github.com/diegosouzapw/OmniRoute/pull/12528)) +- **feat(providers):** refresh NVIDIA hosted models ([#12538](https://github.com/diegosouzapw/OmniRoute/pull/12538)) — thanks @backryun +- **feat(quota):** Moonshot Open Platform balance and TPD lock for custom nodes ([#12590](https://github.com/diegosouzapw/OmniRoute/pull/12590)) — thanks @HouMinXi +- **feat(video):** redact transcript in the in-memory pending-request snapshot (#12430 item 6) ([#12596](https://github.com/diegosouzapw/OmniRoute/pull/12596)) +- **feat(dashboard):** link a log entry's Conversation Context to its owning conversation ([#12646](https://github.com/diegosouzapw/OmniRoute/pull/12646)) — thanks @hartmark +- **feat(catalog):** eligibility-gated free-tier bucket ([#12669](https://github.com/diegosouzapw/OmniRoute/pull/12669)) +- **feat(db):** fail-closed previous_response_id continuation for redacted video turns (#12150 P2b) ([#12707](https://github.com/diegosouzapw/OmniRoute/pull/12707)) +- **feat(gamification): pay the documented `streak_bonus` and `badge_unlock` XP rewards.** `XP_REWARDS` listed both rewards but the award pipeline never paid them: the private reward table in `events.ts` omitted them, `updateStreak()` did not report when a streak extended, and badge unlocks carried no XP. Every request that extends a daily streak now pays `streak_bonus × streak length` once per UTC day (guarded by a same-day `xp_audit_log` check), and every badge unlocked through the pipeline pays `badge_unlock` once per badge (guarded by the `user_badges` primary key; `unlockBadge()` now reports whether it inserted). Bonus XP flows through the same `addXp` + level sync + global/weekly/monthly leaderboard path as action XP, so level-ups and rankings include it. The Radar supporter recognition unlock stays XP-free. (#12522 — thanks @pacocartones) +- **feat(providers):** advertise a `free-tier` capability in the provider plugin manifest for every provider with documented free models, so sidecars and dashboards can filter free-capable providers without reading the quota catalog ([#12786](https://github.com/diegosouzapw/OmniRoute/pull/12786)) — thanks @maxmad64bis +- **feat(providers):** Added EURouter as an OpenAI-compatible API-key gateway (`https://api.eurouter.ai/v1`), with live model discovery via `passthroughModels`. Its copy states that models are served by third-party upstreams listed per model, so an EU-based router is not read as EU data residency for inference. ([#13025](https://github.com/diegosouzapw/OmniRoute/pull/13025)) — thanks @ntdatt812 +- **feat(providers):** Added GreenPT as an OpenAI-compatible API-key provider (`https://api.greenpt.ai/v1`), with live model discovery via `passthroughModels`. No free-inference badge: the published docs describe a free API subscription billed per token, not a free tier. ([#13024](https://github.com/diegosouzapw/OmniRoute/pull/13024)) — thanks @ntdatt812 +- **feat(i18n):** 9 new locales — Greek (`el`), Croatian (`hr`), Serbian (`sr`), Lithuanian (`lt`), Estonian (`et`), Latvian (`lv`), Slovenian (`sl`), Maltese (`mt`), Irish (`ga`) — across the dashboard, docs mirrors, CLI, README and the site; OmniRoute now ships every official EU language (51 locales). (#13044) +- **feat(i18n):** 8 new locales — Kannada (`kn`), Malayalam (`ml`), Odia (`or`), Punjabi (`pa`), Nepali (`ne`), Sinhala (`si`), Burmese (`my`), Khmer (`km`) — across the dashboard, docs mirrors, CLI, README and the site (59 locales). The translator now restores the ICU literal escape around angle placeholders and splits oversized docs sections before translating. (#0000) ([#13660](https://github.com/diegosouzapw/OmniRoute/pull/13660)) +- feat(providers): **list Agnes 3.0 Flash as the current free chat model, drop retired 1.5 Flash, add Image 2.0/2.5 Flash plus Video 2.5/2.5 Flash, and discover the live `/v1/models` catalog (including the CN host `api.agnes-ai.cn`).** `agnes-1.5-flash` now forwards to `agnes-3.0-flash`. Video 2.5 polls `GET /v1/videos/{id}` (not the V2.0 `/agnesapi` contract). Live `/v1/models` (2026-09-09) no longer serves 1.5; the wiki marks it deprecated. 3.0 Flash is 512K context / 65,536 max output, same window as 2.5. CN-region keys use the existing per-connection base-URL field, default stays `apihub.agnes-ai.com`. ([#13120](https://github.com/diegosouzapw/OmniRoute/pull/13120)) — thanks @HouMinXi +- **feat(sse):** Codex and OpenAI catalogs list GPT-6 Astra with effort aliases (`-low` through `-ultra`); Codex CLI identity pins `@openai/codex@0.153.4` in lockstep with the image ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **feat(dashboard):** the orchestration History tab gained a "Compare runs" mode — toggling it turns each grid cell into a 2-item selection queue (a 3rd click drops the oldest pick), and picking two cells opens a side-by-side comparison panel instead of the usual detail drawer. The panel fetches both runs' detail the same way the drawer does (falling back to persisted history once a run leaves the live TTL window) and shows, per side: identity/source/state, start time, a signed `right - left` delta for duration/cost/event count, the event timeline aligned by index, and any memory hits. One side's fetch failing never blocks the other, and a delta is only ever computed when both sides have a finite value — otherwise it renders "—", never `NaN`. Comparing two runs from different sources or skills still works; a banner marks the deltas as informational rather than hiding them, since the two runs aren't a strict apples-to-apples pair. ([#12677](https://github.com/diegosouzapw/OmniRoute/pull/12677)) +- **feat(dashboard):** badge a conversation that never reached a clean stop ([#12717](https://github.com/diegosouzapw/OmniRoute/pull/12717)) — thanks @hartmark +- **feat(release):** reconcile-changelog tool + version-anchored fragment aggregation ([#12987](https://github.com/diegosouzapw/OmniRoute/pull/12987)) +- **feat(mcp):** carry context_length through MCP list_models_catalog projection (#12776) ([#13406](https://github.com/diegosouzapw/OmniRoute/pull/13406)) — thanks @KooshaPari +- **feat(cli):** make startup readiness budget configurable (#13369) ([#13433](https://github.com/diegosouzapw/OmniRoute/pull/13433)) — thanks @KooshaPari + ### 🐛 Bug Fixes +- **security(streaming):** sanitize generic mid-stream error messages before emitting OpenAI, + Responses, or Claude SSE failure frames and before diagnostic logging, while preserving raw + failures for internal classification and keeping client disconnects out of provider failure state. ([#12457](https://github.com/diegosouzapw/OmniRoute/pull/12457)) +- **fix(claude):** `blockExtraUsage: false` no longer lets 5h quota preflight skip the account; extra usage is billed after the session bar is gone, so the request must reach Anthropic ([#12803](https://github.com/diegosouzapw/OmniRoute/pull/12803)) — thanks @HouMinXi +- Sanitize HuggingChat conversation-creation and message-send transport failures before they reach client error bodies or provider logs. ([#12467](https://github.com/diegosouzapw/OmniRoute/pull/12467)) +- **fix(dashboard):** Keep local and theme-aware provider SVG icons at a definite layout size so Chromium does not collapse them to 0×0 after the v3.8.50 image-rendering change ([#12054](https://github.com/diegosouzapw/OmniRoute/pull/12054)) — thanks @ponkcore +- **fix(security):** Sanitize provider and runtime failures before public API, SSE and MCP responses and before persistent request, proxy and usage logs, preventing credentials, stack traces and host filesystem paths from crossing those boundaries while preserving stable error codes and useful diagnostics. ([#12506](https://github.com/diegosouzapw/OmniRoute/pull/12506)) +- **fix(cli):** `omniroute tunnel create` no longer crashes with `Cannot read properties of undefined (reading optsWithGlobals)` — removed the duplicate positional argument that caused Commander.js to misalign the action callback parameters ([#12295](https://github.com/diegosouzapw/OmniRoute/issues/12295)) ([#12368](https://github.com/diegosouzapw/OmniRoute/pull/12368)) — thanks @gonisulaimann +- Stop advertising Gemini Live-only models as supported audio endpoints until OmniRoute proxies the bidirectional Live protocol. ([#11710](https://github.com/diegosouzapw/OmniRoute/pull/11710)) — thanks @RaviTharuma +- **fix(kie):** reroute `flux/kontext` off the KIE Market `createTask` flow — it is catalogued with `isMarket: true` but has no Market catalog page, so KIE rejected it with "model name not supported"; it now hits the dedicated `POST /api/v1/flux/kontext/generate` / `GET /api/v1/flux/kontext/record-info` endpoints instead (#11296). ([#11985](https://github.com/diegosouzapw/OmniRoute/pull/11985)) — thanks @hartmark +- **fix(kie):** correct 12 more KIE Market catalog ids that were sent to `createTask` unchanged but diverge from KIE's documented upstream `model` values — GPT Image 2 T2I/I2I (drops the `gpt/` prefix), GPT Image 1.5 T2I/I2I (`gpt-image/` namespace), Seedream 5.0 Lite T2I/I2I (drops the `.0`), all 4 Flux 2 variants (`flux-2/` namespace, generic variant renamed `flex`), and Wan 2.7 Image / Image Pro (dash instead of dot) — each verified individually against the literal example request published on docs.kie.ai. `#11326`'s "everything else already matches" claim was wrong a second time (#11296); `z-image/4.0-*`/`z-image/4.5-*` and `flux/kontext` remain open, documented as unresolved in `KIE_MARKET_UPSTREAM_MODEL_IDS`'s comment pending further verification. ([#11667](https://github.com/diegosouzapw/OmniRoute/pull/11667)) +- **fix(dashboard):** `useApiKeySave.handleSaveApiKey` no longer forces a full upstream `/models` catalog sync on every non-curated provider connection save — callers can now pass `skipModelSync: true` to opt out, so a workflow that only wants to add one manual model no longer floods the provider's available-models list with hundreds/thousands of synced entries. The flag is a client-side intent signal only and is stripped before the connection payload is POSTed to `/api/providers`; default behavior (full sync on save) is unchanged when the flag is omitted (#11324) ([#11664](https://github.com/diegosouzapw/OmniRoute/pull/11664)) +- **Provider connections:** keep `tokenExpiresAt` when a connection is created. The create-path allowlist omitted it, so every insert stored NULL and the dashboard token badge could read a fresh connection as expired until its first background refresh ([#11368](https://github.com/diegosouzapw/OmniRoute/pull/11368)). — thanks @ntdatt812 +- **fix(combos):** the combo builder's precision-select, global-model-search, and manual-entry flows now serialize a model step's `model` string using the provider's already-computed routing-alias prefix (e.g. `oc/`) instead of rebuilding it from the raw canonical `providerId`, fixing the no-auth "OpenCode Free" provider (`opencode`) being routed to the unrelated paid "OpenCode Zen" provider (`opencode-zen`) because `opencode` doubles as a manual routing-prefix override ([#11433](https://github.com/diegosouzapw/OmniRoute/issues/11433)). ([#11670](https://github.com/diegosouzapw/OmniRoute/pull/11670)) +- **fix(radar):** the catalog feed cache now keeps `generatedAt`, the date the feed's data was built, next to `fetchedAt`, the date this install downloaded it (#11435). The feed schema requires that date and the sync path validates it, but the cache dropped it — so a feed fetched minutes ago and one carrying weeks-old figures looked identical to everything downstream, including the dashboard's "Last fetched" line. `getRadarCatalog().meta` and `GET /api/radar/status` now report both dates, the latter as its own field rather than folded into `version` — and omitted entirely for the offers and intel caches, which keep no build date, where a `null` would read as "unknown" rather than "never stored". The dashboard still shows only the fetch time; surfacing the build date there needs a new translated label and is left to a follow-up. Rows cached before migration 163 read back as `null`: unknown stays unknown instead of borrowing the fetch time. The referrals cache has persisted the same date since migration 142. — thanks @maxmad64bis +- **fix(cli):** postinstall no longer fills `JWT_SECRET` and `API_KEY_SECRET` in the installed package's `.env` (#11436). `.env.example` ships both blank on purpose: the server restores them from its durable store, or generates and persists them there, in `ensureSecrets()`. Pre-filling them defeated that — the file lives inside the package directory, so `npm i -g` replaced it and postinstall wrote _different_ values, while `ensureSecrets()` (which only acts on an empty variable) never got to restore the real ones. Both secrets rotated silently on every update, invalidating dashboard sessions and API-key CRCs. `STORAGE_ENCRYPTION_KEY` left the same list for the same reason in #1622; its comment pointed at a function that no longer exists and now names the real provisioning path. — thanks @maxmad64bis +- **fix(cli):** the CLI now says when a loaded `.env` lives inside the installed package directory (#11437). It already announces every env file it reads, without distinguishing the ones that survive an update from the one that does not: `npm i -g` replaces the package directory wholesale, so values set there are gone at the next update, silently. The warning names the durable path to move them to, and fires only when that file actually supplied a value — a file entirely shadowed by a durable one supplied nothing. A development checkout stays silent: there the same path is stable and documented in `SETUP_GUIDE.md`. — thanks @maxmad64bis +- **fix(free-models):** the shared `isFreeModel()` predicate no longer reports catalog entries marked `freeType: "discontinued"` as free, so `hidePaidModels` can't route to Pollinations' seven premium models that now require a paid key ([#11441](https://github.com/diegosouzapw/OmniRoute/pull/11441)) — thanks @maxmad64bis +- fix(ui): let AnySearch use the normal provider-icon fallback when LobeHub has no matching icon (#11449) +- Fixed the v3.8.50 Costs and Analytics dashboards so flat-rate Claude Code usage can be shown as an explicitly requested token-price estimate without changing default billed-cost semantics. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed archived usage retention so each request is priced individually instead of pricing a day's summed tokens once, which understated archived cost whenever a day mixed cache-heavy and ordinary requests. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed the Costs dashboard so it discloses when displayed figures include flat-rate token-price estimates instead of labelling them as billed spend, using the flag the analytics API already returns; the month-end projection and the CSV/JSON exports carry the same marker, and billed-cost mode is unchanged. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- **fix(combo):** attach the same combo-diagnostics trace (`poolSize`/`attemptOrder`/`excluded`/`terminalReason`, plus `x-omniroute-combo-*` headers) to the round-robin strategy's and the nested pipeline/fusion runtime-unit loop's "Maximum combo retry limit reached" 503 that the priority-strategy path already attaches for the identical terminal condition — previously those two paths returned a bare, contextless 503 ([#11462](https://github.com/diegosouzapw/OmniRoute/issues/11462)). ([#11665](https://github.com/diegosouzapw/OmniRoute/pull/11665)) +- **fix(bun):** use native `bun:sqlite` in `bootstrap-env` and `sync-env` to avoid loading `better-sqlite3` N-API addon during Bun startup ([#11468](https://github.com/diegosouzapw/OmniRoute/pull/11468)) — thanks @TheDemonTuan +- **fix(docker-bun):** make `Dockerfile.bun` install reliable with pre-install script helpers and native `bun:sqlite` smoke check ([#11470](https://github.com/diegosouzapw/OmniRoute/pull/11470)) — thanks @TheDemonTuan +- **fix(docker-bun):** remove every vendored `better-sqlite3` native addon from the Bun runtime image so startup cannot abort before the `bun:sqlite` fallback ([#11482](https://github.com/diegosouzapw/OmniRoute/pull/11482)) — thanks @TheDemonTuan +- **fix(autoCombo):** effort/alias model variants (`gpt-5.6-sol-xhigh`, `gpt-5.6`, cursor's `claude-4.6-opus-high`) inherit their base model's task fitness instead of falling to the wildcard 0.5 ([#11492](https://github.com/diegosouzapw/OmniRoute/pull/11492)) — thanks @MumuTW +- fix(db): rate-limit repeated Arena ELO leaderboard fetch-failure warnings instead of logging one per sync attempt (#11500) ([#11989](https://github.com/diegosouzapw/OmniRoute/pull/11989)) — thanks @hartmark +- **fix(db):** dedupe the raw `[Encryption] Decryption failed...` log line emitted by the lazy-decrypt views (`createLazyRowProxy`/`createLazyConnectionView`), which power `getProviderConnections()` and were re-triggering that line on every CredentialHealth/model-sync cycle for the same corrupt or stale-key credential — a fresh Proxy over a fresh row on every cycle meant the per-proxy memoization never suppressed it, unlike the dedup `decryptConnectionFields()` already had since [#9927](https://github.com/diegosouzapw/OmniRoute/issues/9927). Now shares that dedupe tracking so the line logs at most once per credential ([#11500](https://github.com/diegosouzapw/OmniRoute/issues/11500)). ([#11678](https://github.com/diegosouzapw/OmniRoute/pull/11678)) +- **fix(intelligence):** Arena ELO sync stores a synthesized base-model row for every effort/harness variant (`claude-opus-5-max` → `claude-opus-5`, `gpt-5.6-sol-xhigh (codex-harness)` → `gpt-5.6-sol`), so bare model requests reach the synced intelligence layer instead of the static fallback table; the hand-maintained `MODEL_ALIAS_MAP` — which collapsed generations (`gpt-5.5` → `gpt-5`) — is gone ([#11506](https://github.com/diegosouzapw/OmniRoute/pull/11506)) — thanks @MumuTW +- **fix(autoCombo,sse):** catalog hygiene — the static task-fitness table no longer ranks retired models above live ones (dead rows dropped, patterns matched on segment boundaries), the 7 deprecation aliases that forwarded to retired or non-existent models now point at the vendors' published replacements, an alias is no longer rewritten when the serving provider offers the id as-is, and a new `check:model-lifecycle` gate diffs both tables against a vendor lifecycle snapshot ([#11507](https://github.com/diegosouzapw/OmniRoute/pull/11507)) — thanks @MumuTW +- **fix(sse):** stop dropping resolved `thoughtSignature` values on parallel (multi tool-call) turns sent to Gemini 3.x — the claude→gemini and openai→gemini translators previously kept the signature only on the *first* function call of a message, causing Gemini to reject subsequent calls in the same turn with HTTP 400 "Function call is missing a thought_signature"; each function call now keeps its own resolved signature ([#11510](https://github.com/diegosouzapw/OmniRoute/issues/11510)). ([#11669](https://github.com/diegosouzapw/OmniRoute/pull/11669)) +- Treat Docker publishing from a non-default release branch as an expected skip instead of a failed workflow, while preserving the guard that prevents it from overwriting the `next` channel. ([#11527](https://github.com/diegosouzapw/OmniRoute/pull/11527)) — thanks @pacocartones +- fix(search): prefer credentialed providers over duckduckgo-free fallback (#11524) — the fallback-only loop ran before the credentialed-providers loop in `executeWebSearch`, making configured providers unreachable when `duckduckgo-free` was available. ([#11565](https://github.com/diegosouzapw/OmniRoute/pull/11565)) — thanks @vsd2807 +- **fix(sse):** cap the upstream headers-wait phase for STREAMING requests to a client-realistic ceiling (110s, under Codex's own ~120s hard client-abort window) instead of the flat 10-minute `FETCH_TIMEOUT_MS` default — that default was 5x longer than the body-phase readiness watchdog's own adaptive bound, so a request whose upstream never returned any response at all (not even headers, e.g. a stalled NVIDIA target behind a tool-heavy Responses→Chat translation) kept the client connection alive on keepalives only, guaranteeing the client's own patience ran out first with an opaque 499 instead of OmniRoute detecting and failing the stall fast. Non-streaming requests are unaffected — they keep the existing flat default (`open-sse/utils/fetchStartTimeoutPolicy.ts`) (#11526) ([#11668](https://github.com/diegosouzapw/OmniRoute/pull/11668)) +- **fix(ci):** protocol-clients E2E harness boots the peer-stamped custom server (`run-next.mjs dev`) instead of the bare `next` CLI, so `LOCAL_ONLY` locality resolves from the real TCP peer stamp and `/api/mcp/audit` answers 200 (open bootstrap preserved via an `OMNIROUTE_E2E_BOOTSTRAP_MODE=open` credential clear in `run-next.mjs`, pinned to loopback `HOST`) instead of a deterministic 403 (#11535) ([#11549](https://github.com/diegosouzapw/OmniRoute/pull/11549)) — thanks @jonlwheat2-gif +- **fix(docs):** the Auto-Combo engine is described with the number of factors it actually declares. Four different counts were in circulation — 6, 9, 13 and 14 across `AGENTS.md`, four guides and six code comments — while `DEFAULT_WEIGHTS` declares 15. `check:docs-counts` now reads that number from the source, so adding a factor without updating the prose fails CI ([#11545](https://github.com/diegosouzapw/OmniRoute/pull/11545)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **fix(quota-share):** in-flight leases are per request, so an aborted request's slot ages out instead of being kept alive by later traffic on the same connection ([#11547](https://github.com/diegosouzapw/OmniRoute/pull/11547)) — thanks @abhisheksharma2411 +- The free-tier summary route now serves the refreshed Radar catalog when the feed is active — the same numbers the dashboard shows — and states which catalog answered plus its real build date, instead of always reporting release-frozen figures with a stale curation date. The supporter-key live feed stays reserved to authenticated callers of the instance. ([#11550](https://github.com/diegosouzapw/OmniRoute/pull/11550)) — thanks @maxmad64bis +- **sse:** fix LiveWS/embed-WS servers crashing at startup under the Node/tsx runtime — `liveServer.ts`, `embedWsProxy.ts` and `apiBridgeServer.ts` imported `@/shared/utils/httpClientAbortGuard` without the `.mjs` extension, so the client-abort crash guard added by [#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556) was unreachable and every dependent test failed with `ERR_MODULE_NOT_FOUND` ([#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556)). ([#11671](https://github.com/diegosouzapw/OmniRoute/pull/11671)) — thanks @jonlwheat2-gif / @yxyxy +- fix(chat): shed any of the matched provider-node's routing identifiers (public prefix or internal id) before model lookup, so composites like `//` no longer reach upstream verbatim (#11557) — thanks @maxmad64bis +- **fix(cache):** a semantic-cache entry now expires on its TTL instead of surviving until the next UTC midnight, and `dbEntries` stops counting expired rows ([#11573](https://github.com/diegosouzapw/OmniRoute/pull/11573)) — thanks @ntdatt812 +- **fix(api):** `/v1/models` schedules its stale-while-revalidate rebuild through Next's `after()` again, so a stale catalog reaches the client before the rebuild blocks the event loop ([#11574](https://github.com/diegosouzapw/OmniRoute/pull/11574)) — thanks @ntdatt812 +- **fix(check):** the `known-symbols` executor conformance gate awaits the now-async `getExecutor()`, so it stops reporting all 142 registered aliases as dead symbols and can detect a lazy import that fails ([#11582](https://github.com/diegosouzapw/OmniRoute/pull/11582)) — thanks @ntdatt812 +- **fix(pack):** register `bin/cli/utils/volatileEnvPath.mjs` in `PACK_ARTIFACT_REQUIRED_PATHS`, so the prune can no longer silently drop a module the published CLI imports on every boot ([#11588](https://github.com/diegosouzapw/OmniRoute/pull/11588)) — thanks @ntdatt812 +- **i18n(vi):** translate the five reliability-column strings on the free-provider rankings page, which rendered as `__MISSING__:` markers in the Vietnamese UI ([#11589](https://github.com/diegosouzapw/OmniRoute/pull/11589)) — thanks @ntdatt812 +- **resilience:** restore the expired-connection retry-budget probe in the token-health sweep — the `!isGitHubAccessTokenOnlyConnection` carve-out reintroduced by #11608 contradicted the boundary pinned by #11592, so a GitHub connection parked at `expired` with retry budget remaining was never probed and could never self-heal ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)). ([#11672](https://github.com/diegosouzapw/OmniRoute/pull/11672)) — thanks @jonlwheat2-gif / @ntdatt812 +- **fix(dashboard):** Provider Stats now displays combo success rates on their native 0–100 percentage scale instead of multiplying them twice ([#11595](https://github.com/diegosouzapw/OmniRoute/pull/11595)) — thanks @pacocartones +- **fix(providers):** route DuckDuckGo Web Chat through the verified `duck.ai` endpoint, refresh its free model catalog, and avoid forwarding unsolved anti-abuse challenges ([#11597](https://github.com/diegosouzapw/OmniRoute/pull/11597)) — thanks @solstxce +- **fix(ui):** Console log Refresh and Copy controls now expose localized accessible names, keep copy actions visible on keyboard focus, and announce copy completion safely ([#11599](https://github.com/diegosouzapw/OmniRoute/pull/11599)) — thanks @pacocartones +- Dropped the stale-`.eslintcache` `restore-keys` fallback from both "Restore ESLint file cache" steps in `ci.yml`, so the blocking `Lint` job can no longer be served per-file verdicts computed under a different lint config, suppressions file or lockfile. `quality.yml` had already dropped it in #11963; `ci.yml` — the workflow that actually gates PRs — had not (#11600). ([#11996](https://github.com/diegosouzapw/OmniRoute/pull/11996)) +- fix(ci): pass `--pass-on-unpruned-suppressions` in `run-eslint-json.mjs` so the CI Lint job no longer fails when a suppression is merely orphaned by a genuine fix, mirroring the identical fix already in `validate-release-green.mjs` (#11600) ([#11983](https://github.com/diegosouzapw/OmniRoute/pull/11983)) — thanks @hartmark +- fix(handoff): enforce provider allowlist for universal handoff (#11602) — universal handoff now skips summarization when the selected summary model's provider is not included in the configured provider allowlist. ([#11619](https://github.com/diegosouzapw/OmniRoute/pull/11619)) — thanks @vsd2807 +- **fix(dashboard):** Show a stable error state when Search Analytics returns an HTTP error or malformed data ([#11603](https://github.com/diegosouzapw/OmniRoute/pull/11603)) — thanks @pacocartones +- **test(gamification):** pin the aggregate profile level to the XP-derived semantics of #11604 — `getAggregateXp()` now derives `currentLevel` from the summed XP (`calculateLevel(sum)`), not `MAX(stored current_level)`, and the #3484 fixture levels are aligned with the XP curve ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)). ([#11676](https://github.com/diegosouzapw/OmniRoute/pull/11676)) — thanks @jonlwheat2-gif / @pacocartones +- **fix(dashboard):** Keep the Profile level and progress aligned with aggregate XP, including bounded handling for invalid totals ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)) — thanks @pacocartones +- **fix(dashboard):** Prevent locked hidden badges from revealing their icon or opening private badge details before they are earned ([#11605](https://github.com/diegosouzapw/OmniRoute/pull/11605)) — thanks @pacocartones +- **fix(dashboard):** Restore keyboard focus after shared modals close and cancel delayed autofocus during cleanup ([#11607](https://github.com/diegosouzapw/OmniRoute/pull/11607)) — thanks @pacocartones +- **fix(dashboard):** Enable Enter and Space activation for clickable data-table rows without hijacking nested controls ([#11610](https://github.com/diegosouzapw/OmniRoute/pull/11610)) — thanks @pacocartones +- **fix(autoCombo,sse):** vendor-retired catalog ids are dropped from the auto-combo candidate pool and no longer win on leftover `arena_elo` / `user_override` rows; `getModelLifecycleDecision` consults `model-lifecycle.json` (prefix-stripped) so aggregator traffic is not `untracked` for ids the snapshot already knows ([#11625](https://github.com/diegosouzapw/OmniRoute/issues/11625)) ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)) — thanks @MumuTW +- **test(chatcore):** move Codex/Claude combo fixtures off models the lifecycle guard now rejects — `gpt-5.1-codex`/`gpt-5-codex` are vendor-retired (snapshot, #11626) and `claude-3-5-sonnet-20241022` is shut down, so native-passthrough and combo-fallback tests switched to `gpt-5.6-sol` and `claude-sonnet-4.6` ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)). ([#11675](https://github.com/diegosouzapw/OmniRoute/pull/11675)) — thanks @jonlwheat2-gif / @MumuTW +- fix(opencode-plugin): stop a free-tier budget that rounds up across a magnitude from rendering as `1000K`/`1000M` in the model picker — `fmtTokens` chose its unit from the raw token count and then rounded with `toFixed(1)`, so 999,950–999,999 printed as `1000K` rather than `1M` and just under a billion printed as `1000M` rather than `1B` ([#11684](https://github.com/diegosouzapw/OmniRoute/pull/11684)) — thanks @abhisheksharma2411 +- Cap prose-derived Antigravity quota resets at the configured model cooldown maximum while preserving authoritative Retry-After headers and Google RetryInfo hints. (#11695) Thanks @pacocartones. ([#11823](https://github.com/diegosouzapw/OmniRoute/pull/11823)) +- Compare `_rebaseline_*` notes structurally in the ratchet-bank verifier so an object-valued note no longer reports itself as rewritten on an unmodified tree — the nightly `bank-ratchet-shrinks` lane had been aborting on every run since 2026-08-11, leaving 41,672 lines of earned file-size shrinks unbanked (#11702). ([#11892](https://github.com/diegosouzapw/OmniRoute/pull/11892)) — thanks @MumuTW +- **fix(build):** `prepublish.ts` bundles the ChatGPT Web (Codex) MCP bridge through `runBuildTool()` instead of spawning `npx.cmd` raw, fixing the build crash on Node ≥ 20/Windows where `.cmd` shims cannot be spawned without a shell (EINVAL) ([#11704](https://github.com/diegosouzapw/OmniRoute/issues/11704)) ([#11706](https://github.com/diegosouzapw/OmniRoute/pull/11706)) — thanks @jonlwheat2-gif +- fix(codex): keep `parallel_tool_calls:false` on the translated Codex Responses Lite path (#11707) ([#11984](https://github.com/diegosouzapw/OmniRoute/pull/11984)) — thanks @hartmark +- **fix(tests):** the new Gemini Web Images retirement test (#11708) called the now-async `getExecutor()` synchronously, throwing `TypeError: getExecutor(...).getProvider is not a function` instead of asserting Gemini Web chat stays available. Awaited to match the R0.3 executor-registry refactor (#11220). ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(search):** Allow REST and MCP web-fetch requests to select the advertised `anysearch-search` provider explicitly ([#11716](https://github.com/diegosouzapw/OmniRoute/pull/11716)) — thanks @pacocartones +- **fix(providers):** Keep overnight peak-hour protection active after midnight according to the window's configured start day ([#11718](https://github.com/diegosouzapw/OmniRoute/pull/11718)) — thanks @pacocartones +- **fix(providers):** correct all five g4f.space gateway descriptions to document proof-of-work credits or member API keys, variable limits, and the remote prompt boundary instead of promising free keyless access at five requests per minute ([#11738](https://github.com/diegosouzapw/OmniRoute/pull/11738)). +- **fix(db):** `model_capabilities` is created by a migration instead of lazily on the first models.dev sync, so a clean install and an upgraded install converge on the same schema regardless of which features have run ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(ci):** `check:install-upgrade` now fails on an `npm` install truncated by ENOSPC (npm reports it as a warning and still exits 0), authenticates its health probe so the version assertion works against the hardened health payload, frees the clean-install tree before the upgrade phase, and no longer reports a schema divergence computed from a boot that never served ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(routing):** harden session-affinity key derivation against oversized explicit IDs and empty structural-payload collisions, while recognizing Gemini and common text request shapes ([#11746](https://github.com/diegosouzapw/OmniRoute/pull/11746)) — thanks @MumuTW +- **fix(packages/browser-pool):** regenerate `package-lock.json` so the `packages/browser-pool` workspace's locked `playwright`/`@types/node` (and transitives) match its `package.json` specs, fixing cache-only/offline installs (`npm ci --offline`, Nix `buildNpmPackage`) that previously failed with `ENOTCACHED` ([#11747](https://github.com/diegosouzapw/OmniRoute/issues/11747)) — thanks @benjaminkitt ([#11784](https://github.com/diegosouzapw/OmniRoute/pull/11784)) — thanks @geekyNads +- **fix(assets):** Stop distributing 79 provider and CLI logos without verified source/license provenance, keep every provider/tool available through the internal generic icon, and prevent those IDs from falling through to an external icon CDN. ([#11750](https://github.com/diegosouzapw/OmniRoute/pull/11750)) +- **fix(providers):** retire the common `chatgpt-web` / `cgpt-web` provider because its pre-key and proof-of-work provenance could not be cleared ([#11754](https://github.com/diegosouzapw/OmniRoute/pull/11754)) — explicit chat and image requests, slash-prefixed models, persisted aliases, proxy overrides, and combo targets now fail closed with a sanitized HTTP `410` / `PROVIDER_RETIRED` before upstream dispatch. Migration 163 tombstones matching connections and invalidates their active leases without deleting history or widening API-key allowlists. The independent `chatgpt-web-codex` / `cgpt-codex` provider and its MIT-noticed vendored implementation remain available. +- **fix(usage):** quota and usage refresh no longer 409 when an exclusive lease reserves the connection ([#11758](https://github.com/diegosouzapw/OmniRoute/pull/11758)) — thanks @TheDemonTuan +- Keep the embedding registry's vector width and `embedding` type on models when a synced model exists for the same id, so `/v1/models` no longer reports registry-described embedding models widthless or untyped (#11761) — thanks @rahilmavani +- Correct `google/gemini-embedding-001` on the OpenRouter route to 3072 dimensions, the width it returns when `dimensions` is not sent (#11761) — thanks @rahilmavani +- **fix(vision-bridge):** Restrict automatic vision routing and fallbacks to models available in authoritative live provider catalogs, including aliases and effort variants ([#11767](https://github.com/diegosouzapw/OmniRoute/issues/11767)) — thanks @pacocartones ([#11813](https://github.com/diegosouzapw/OmniRoute/pull/11813)) +- **fix(providers):** reclassify Cerebras as a one-time $5 signup credit (payment method required, 30-day validity), not a recurring no-card 1M tokens/day trial ([#11773](https://github.com/diegosouzapw/OmniRoute/issues/11773)) ([#12591](https://github.com/diegosouzapw/OmniRoute/pull/12591)) — thanks @HouMinXi +- fix(cli): stop prepublish from re-rebuilding the already-built ESM-only opencode-plugin dist (#11787) ([#11990](https://github.com/diegosouzapw/OmniRoute/pull/11990)) — thanks @hartmark +- **fix(db):** Fresh installs no longer log a non-fatal `no such table: compression_run_telemetry` warning when retention cleanup runs before the lazily-created telemetry table exists ([#11802](https://github.com/diegosouzapw/OmniRoute/pull/11802)) — thanks @RaviTharuma +- fix(sse): set X-OmniRoute-Selected-Connection-Id on successful combo dispatches so downstream consumers stop falling back to an empty connection id (#11810) ([#11986](https://github.com/diegosouzapw/OmniRoute/pull/11986)) — thanks @hartmark +- **fix(services):** embedded CLIProxyAPI lifecycle checks now use public `/healthz`, while model discovery uses the configured dedicated data-plane API key instead of the management password ([#11811](https://github.com/diegosouzapw/OmniRoute/pull/11811)) — thanks @RaviTharuma +- Fix persisted model steps returning an error in the playground route simulator, and warn when structural steps cannot be simulated. ([#11862](https://github.com/diegosouzapw/OmniRoute/pull/11862)) — thanks @pacocartones +- **fix(providers):** Antigravity's dynamic mitmAlias table no longer routes `gemini-3.7-flash-{high,medium,low}` to a literal tier-suffixed upstream id just because one connected account's own discovery listed it directly — those display ids always resolve through the safe `gemini-3.7-flash-tiered` static alias, so one account's Google-provisioned access no longer 404s every sibling account of the provider ([#11824](https://github.com/diegosouzapw/OmniRoute/issues/11824), [#11651](https://github.com/diegosouzapw/OmniRoute/issues/11651)) ([#11988](https://github.com/diegosouzapw/OmniRoute/pull/11988)) — thanks @hartmark +- **fix(dashboard):** Expose Profile loading, errors, page structure, and XP progress to assistive technologies ([#11838](https://github.com/diegosouzapw/OmniRoute/pull/11838)) — thanks @pacocartones +- **fix(socks):** `Agent.connectTimeout` now bounds both SOCKS handshake and TLS connect, and `family === null` no longer falls back to `fetch-socks` ([#11842](https://github.com/diegosouzapw/OmniRoute/pull/11842)) — thanks @maxmad64bis +- **perf(compression):** OOM mitigations for large payload hashing, memoization, and token estimation ([#11844](https://github.com/diegosouzapw/OmniRoute/pull/11844) — thanks @AndrianBalanescu) +- Excluded Next.js Node File Trace manifests (`*.nft.json`) from the published npm tarball. They are build-time metadata and are never read while serving, but had grown to 668.7 MB — 61% of the package — which pushed the upload past the registry limit and made `npm publish` fail with `413 Payload Too Large`. ([#11864](https://github.com/diegosouzapw/OmniRoute/pull/11864)) +- **fix(cli):** keep the global OmniRoute server `--base-url` separate from provider-node `--endpoint` values in add, update, and validate commands ([#11860](https://github.com/diegosouzapw/OmniRoute/pull/11860)) — thanks @pacocartones +- **fix(config):** Nous Research's `Hermes-4-405B` model now displays as "Hermes 4 405B (Nous Research)" in both the provider registry and the free-model catalog, instead of the mislabelled "Hermes 4 7B" ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#11993](https://github.com/diegosouzapw/OmniRoute/pull/11993)) +- **fix(provider/nous):** inject required user= tag into Nous Research inference requests to resolve upstream 400 "missing tags" error ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#12044](https://github.com/diegosouzapw/OmniRoute/pull/12044)) +- **fix(build):** `npm run build` now fails in one second with a named package and a copy-pasteable fix when npm silently drops an externalised optional native dependency, instead of dying four minutes in with `Module not found: Can't resolve 'better-sqlite3'` ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- **fix(install):** `postinstall` no longer throws `ReferenceError: isAndroid is not defined` — failing the whole `npm install` — when the `better-sqlite3` rebuild fallback times out; the manual-fix guidance is reachable again ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- Fixed the Alibaba free-tier allowlist test that went red on its own once the shipped catalog's `validUntil` (2026-08-27) passed, leaving every PR and `main` with a failing `Unit Tests (1/8)`. The test now builds its own packs with dates it controls, and covers the expired-pack fallback that production has actually been serving. ([#11867](https://github.com/diegosouzapw/OmniRoute/pull/11867)) +- Split the npm registry upload into its own GitHub-hosted job. npm refuses `--provenance` from a self-hosted runner (`422 ... Only "github-hosted" runners are supported`), which blocked the v3.8.50 publish; the heavy verification cannot move to a hosted runner, so it now hands the proven tarball over instead. ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(ui):** the shared `DataTable` loading state no longer reads its decorative ⏳ glyph out to assistive technology, and now carries the same `role="status"` / `aria-live="polite"` / `aria-busy="true"` semantics as `PageLoading` ([#11869](https://github.com/diegosouzapw/OmniRoute/pull/11869)) — thanks @pacocartones +- **fix(api):** Bound the `timeout` accepted by `POST /api/providers/[id]/login` to the same 15s–600s window the Adobe Firefly and Conol browser-login paths already enforce, so a request can no longer pin a headful browser and the single active-login slot with an arbitrarily large poll budget. ([#11871](https://github.com/diegosouzapw/OmniRoute/pull/11871)) — thanks @pacocartones +- **fix(api):** Stop the tunnel and MITM routes from returning raw child-process error text, which disclosed host paths, binary install locations and Tailscale `tskey-*` credentials in a body some of these routes serve to non-loopback callers, and return a real 400 instead of a framework 500 when a tunnel request body fails validation. ([#11872](https://github.com/diegosouzapw/OmniRoute/pull/11872)) — thanks @pacocartones +- **fix(open-sse):** stream TTFT/ITL are sampled from a monotonic clock (`performance.now`) instead of `Date.now`, so an NTP correction or wall-clock jump can no longer inflate a genuine TTFT or produce a negative one that the `ttft >= 0` guard silently drops — protecting the router's OTel/EWMA/`usage_history`/speed-ranking signals ([#11873](https://github.com/diegosouzapw/OmniRoute/pull/11873)) — thanks @pacocartones +- **fix(providers):** xAI `reasoning_effort: "xhigh"` now reaches grok-4.6+ instead of being silently clamped to `"high"` ([#11879](https://github.com/diegosouzapw/OmniRoute/pull/11879)) — thanks @NoxzRCW +- **fix(dashboard):** the providers page no longer crashes into the error boundary when a provider id collides with an `Object.prototype` member (`constructor`, `__proto__`); icon lookups are own-property guarded ([#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)) — thanks @NoxzRCW +- **fix(skills):** injected skill tools declared in shorthand (`{"content": "string"}`) now forward valid JSON Schema, unblocking providers that validate tool schemas strictly such as Zhipu GLM on the Console Go tier ([#11881](https://github.com/diegosouzapw/OmniRoute/pull/11881)) — thanks @NoxzRCW +- **fix(api):** `POST /api/playground/simulate-route` now surfaces `combo-ref` and `provider-wildcard` persisted combo steps with a specific warning (naming the referenced combo, or the unresolved `provider/model` wildcard) instead of folding them into a generic "unsupported step" count; a `provider-wildcard` step is also now included as an unresolved target so the operator can see it is in the route (ported from [#11882](https://github.com/diegosouzapw/OmniRoute/pull/11882) — thanks @NoxzRCW). ([#11930](https://github.com/diegosouzapw/OmniRoute/pull/11930)) +- **fix(translator):** the streaming OpenAI→Claude translator keeps upstream usage, including prompt-cache tokens, when it arrives on a trailing `choices: []` chunk (Fireworks and any upstream using `stream_options.include_usage`) ([#11883](https://github.com/diegosouzapw/OmniRoute/pull/11883)) — thanks @NoxzRCW +- **fix(cli):** `omniroute update --apply` now tells you explicitly whether a running server was detected and, if so, that you must run `omniroute restart` to apply the update — it never restarted anything and previously implied the update was already live once files were installed. The dashboard's npm-mode Update flow (`/api/system/version`) now tries OmniRoute's own PID-file-managed supervisor before falling back to pm2, and reports an honest "restart required" step instead of a silent pm2-only "skipped" that read like a completed update. The server-side latest-version lookup backing the dashboard's update banner also gained `--prefer-online`, closing the same stale-npm-cache class already fixed in the CLI's own copy for #4376 ([#11885](https://github.com/diegosouzapw/OmniRoute/issues/11885)). ([#12005](https://github.com/diegosouzapw/OmniRoute/pull/12005)) +- **fix(resilience):** clear persisted LKGP pins when a target suffers connection/provider exhaustion or is skipped before dispatch due to cooldown/exhaustion/unavailability, preventing subsequent requests from repeatedly prioritizing known-dead providers ([#11911](https://github.com/diegosouzapw/OmniRoute/issues/11911)). ([#12013](https://github.com/diegosouzapw/OmniRoute/pull/12013)) — thanks @HouMinXi +- **fix(sse):** the OpenAI→Claude stream translator now defers the terminal `message_delta`/`message_stop` emission until the real usage block has arrived (or a genuine end-of-stream flush forces it) instead of emitting it immediately on `finish_reason` — previously, when the trailing usage-only chunk (`{"choices":[],"usage":{...}}`) arrived *after* the `finish_reason` chunk (the normal order for Fireworks/vLLM/Together and other `stream_options.include_usage` upstreams), the client-visible `message_delta` still carried stale/zero usage even though `state.usage` was internally corrected too late to matter (ported from [#11915](https://github.com/diegosouzapw/OmniRoute/pull/11915) — thanks @HouMinXi). ([#11933](https://github.com/diegosouzapw/OmniRoute/pull/11933)) +- **fix(sse):** `stripStore()` now forces `store=false` for stateless OpenAI-compatible Responses-API targets (unless the connection has explicitly opted in via `providerSpecificData.openaiStoreEnabled`), instead of only handling the `openai`/`agentrouter` cases — a client-supplied `store` value previously passed through untouched to backends that don't actually persist responses server-side ([#11916](https://github.com/diegosouzapw/OmniRoute/pull/11916) — thanks @HouMinXi). +- **fix(models):** custom provider-node models (synced, custom, and alias-backed) now appear under their configured prefix in the unified catalog when the operator's model-id prefix mode is canonical, instead of being dropped whenever alias-inclusion was otherwise disabled ([#11918](https://github.com/diegosouzapw/OmniRoute/pull/11918) — thanks @HouMinXi). +- **fix(models):** the unified model catalog now suppresses stale static registry models (including effort-tier variants) for any provider whose active connection has an authoritative live synced catalog, not only providers already using exclusive-synced-listing — a connection with `providerUsesAuthoritativeLiveCatalog` previously kept serving both the live-synced models and the stale static rows side by side ([#11919](https://github.com/diegosouzapw/OmniRoute/pull/11919) — thanks @HouMinXi). +- **fix(ui):** `ProviderIcon`'s three remaining unguarded lookups (`PROVIDER_ICON_ALIASES`, `LOCAL_SVG_ALIASES`, `THEMED_SVGS`) now use `Object.hasOwn()` own-property checks — a provider id such as `constructor` or `__proto__` previously resolved through the prototype chain instead of falling through to the unknown-provider CDN fallback (`getLobeProviderIcon()` itself was already guarded by [#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)); `ProviderPageHeader`'s `color` field is also now optional, matching the rest of the component's defensive typing (ported from [#11920](https://github.com/diegosouzapw/OmniRoute/pull/11920) — thanks @HouMinXi). ([#11935](https://github.com/diegosouzapw/OmniRoute/pull/11935)) — thanks @NoxzRCW +- fix(ollama): preserve multi-byte UTF-8 content split across stream chunks in the Ollama NDJSON transform, which previously corrupted CJK/emoji into U+FFFD (#11921) — thanks @pacocartones +- **fix(providers):** OrcaRouter chat requests now target `/v1/chat/completions` instead of the bare `/v1` API root, fixing the upstream `404 Invalid URL (POST /v1)` ([#11923](https://github.com/diegosouzapw/OmniRoute/pull/11923)). — thanks @echel0nn +- **fix(plugins):** deliver the `onStreamComplete` event to disk-installed plugins. The event shipped in v3.8.50 (#9669) was emitted internally but had no plugin-facing wiring, so no plugin could ever subscribe: the manifest schema silently dropped `hooks.onStreamComplete`, and the loader/manager only knew the seven legacy hooks. `onStreamComplete` is now a declarable manifest hook, wired through the loader and registered by the manager like the other hooks, and its payload carries a `requestId` so consumers can correlate the stream-completion event with the originating request ([#11934](https://github.com/diegosouzapw/OmniRoute/pull/11934)) — thanks @amaleta / @pacocartones +- **fix(db):** the Qdrant embedding-model dropdown now lists local/self-hosted providers (Ollama, LM Studio, vLLM, etc.) — an active connection is treated as "configured" when the provider allows an optional API key, not only when it has a real key or OAuth, so a running local embedding provider is no longer hidden from the picker ([#11949](https://github.com/diegosouzapw/OmniRoute/issues/11949)) ([#11995](https://github.com/diegosouzapw/OmniRoute/pull/11995)) +- **fix(providers):** Vertex AI Anthropic partner-model discovery now calls the Model Garden `v1beta1` publisher list (`/v1beta1/publishers/anthropic/models`, global) and parses its `publisherModels` envelope, so Claude models auto-synced from Vertex populate the active live catalog and route at request time instead of returning `Model '' is not available in the active live catalog` ([#11991](https://github.com/diegosouzapw/OmniRoute/issues/11991)) — thanks @fabioluissilva ([#11998](https://github.com/diegosouzapw/OmniRoute/pull/11998)) +- **fix(cli):** support `--base-url` alongside `--endpoint` in `omniroute nodes add`, `update`, and `validate` subcommands to prevent global `--base-url` shadowing issues ([#11999](https://github.com/diegosouzapw/OmniRoute/issues/11999)). ([#12033](https://github.com/diegosouzapw/OmniRoute/pull/12033)) — thanks @HouMinXi +- **fix(providers):** `cloudflare-ai` no longer refuses image content parts for every Workers AI model ([#12002](https://github.com/diegosouzapw/OmniRoute/pull/12002)) — the plain-string `content` requirement behind #2539 is carried by the _model_ schema, not by the `/ai/v1/chat/completions` endpoint (measured: an all-text part array returns 200 on `@cf/mistralai/mistral-small-3.1-24b-instruct`, `@cf/meta/llama-4-scout-17b-16e-instruct` and `@cf/meta/llama-3.3-70b-instruct-fp8-fast`, and 400 on the text-only `@cf/qwen/qwen2.5-coder-32b-instruct`). `transformRequest()` flattened every array and threw on the first non-text part (#6390), so image input was refused for vision-capable Cloudflare models that accept it. All-text arrays are still flattened — the one shape every model accepts — while an array carrying a non-text part is passed through untouched, so the attachment is still never silently dropped. Regression guards: `tests/unit/cloudflare-ai-image-parts-6390.test.ts`. — thanks @davidlinfr +- **fix(resilience):** decouple the limiter-managed execution backstop from the queue-wait budget — new `requestQueue.executionMaxWaitMs` (env `RATE_LIMIT_EXECUTION_MAX_WAIT_MS`, default 600000 = 10 min) now feeds Bottleneck's post-dispatch `expiration`, while `requestQueue.maxWaitMs` keeps its documented queue-wait semantics. Previously the queue-wait budget doubled as the execution expiration, so legitimate long-running calls on non-incremental gateways (whole generation buffered before the first upstream byte, e.g. Console Go / Command Code tiers serving GLM models) were killed mid-flight at the queue budget with a false 504 `RATE_LIMIT_EXECUTION_TIMEOUT` — the local limiter undercut the provider-aware upstream fetch-start timeouts. The surfaced 504 message now names `requestQueue.executionMaxWaitMs`; the error keeps the #4165 guarantees (disclaims an upstream timeout, preserves the Bottleneck error as `cause`, branded code + trusted provenance, classified request-scoped so combo falls back). A real queue-wait bound (the `Promise.race` around `limiter.schedule()` sketched in #9533) remains future work. (#12025) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **Call logs:** keep the `error` field when an artifact exceeds the storage cap, instead of replacing it with the omission marker. The error is the only field that says *why* a request failed and is typically ~90 bytes next to the multi-hundred-KB bodies that trip the cap, so dropping it left a size-limited row undiagnosable — a provider outage, a local timeout and an upstream 400 all rendered identically. It is now preserved at every fallback stage, truncated to 4KB if it is itself large ([#12026](https://github.com/diegosouzapw/OmniRoute/issues/12026)). ([#12095](https://github.com/diegosouzapw/OmniRoute/pull/12095)) — thanks @ntdat812 +- **fix(diagnostics):** preserve the error field (truncated to 4KB with a `[truncated: …]` suffix) in every call-log artifact size-limit fallback stage. Previously the minimal fallback replaced the error with `[omitted: call log artifact size limit exceeded]`, so an oversized artifact row showed nothing about WHY the request failed — e.g. 91 of 847 opencode-go 504 rows on one production instance were undiagnosable from the dashboard. Oversized request/response bodies are still omitted exactly as before; the error cap is independent of the payload sizes that tripped the fallback. (#12026) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **fix(sse):** OpenAI Responses clients that declare the native `web_search` tool now receive a spec-shaped `web_search_call` output item with `action.sources` alongside the preserved function-call round-trip, so search results executed through OmniRoute's own search backend are consumable by standard Responses clients (Codex, pi-web-access, …). ([#12031](https://github.com/diegosouzapw/OmniRoute/pull/12031)) — thanks @watchingdogs +- **fix(cli):** use in-thread alias resolver hooks on modern runtimes to avoid deprecation noise and improve Node.js forward compatibility ([#12073](https://github.com/diegosouzapw/OmniRoute/issues/12073)). ([#12083](https://github.com/diegosouzapw/OmniRoute/pull/12083)) — thanks @xiaoyaner0201 +- **fix(combo):** an operator-set **Agent Features → Context length** on a combo is now honored at request time. The value was persisted and advertised through `/v1/models`, but `resolveComboContextLimit()` never consulted it — so a multi-target combo whose members carry no per-model window fell through to the provider's generic `defaultContextLength` (openrouter 128000, command-code 200000) and rejected large requests with `Input exceeds context window … limit 128000` despite the combo being explicitly sized much larger. An identical single-target combo worked, because it collapses to its concrete target before the guard runs. Invalid values (0/negative/NaN/Infinity) are ignored, so the existing target → combo-min → fallback order is unchanged. ([#12090](https://github.com/diegosouzapw/OmniRoute/pull/12090)) — thanks @adivekar-utexas +- **fix(sse):** passthrough streams now estimate usage on finish when upstream closes without usage even with `stream_options.include_usage` — avoids `0 tokens / 0%` for providers that stay silent (and correctly handles trailing empty-choices usage) ([#12151](https://github.com/diegosouzapw/OmniRoute/pull/12151)) — thanks @maxmad64bis +- **fix(combos):** clearing an agent feature in the combos editor now persists — unchecking context cache protection, or emptying the system message or tool filter, sends an explicit `null` instead of dropping the field from the `PUT` body, which the update merge read as "leave unchanged" ([#12177](https://github.com/diegosouzapw/OmniRoute/pull/12177)) — thanks @foreveryh / @ntdatt812 +- **fix(memory):** self-hosted embedding endpoints now vectorize — the vector width is measured from the first embedding that comes back instead of being read from a registry that cannot describe them, so `vec_memories` is created and memories stop piling up unvectorized behind a green health check ([#12180](https://github.com/diegosouzapw/OmniRoute/pull/12180)) — thanks @kanade-hoshino / @ntdatt812 +- **fix(translator):** the leading `system` message now reaches Responses-API upstreams when its `content` is a content-part array — it was read as `typeof content === "string" ? content : ""`, so a prompt-caching client (Anthropic `cache_control`, the shape LiteLLM and the Anthropic SDK emit) had its entire system prompt replaced by an empty `instructions`. The request was still accepted with a normal `prompt_tokens` count, so the model answered with no instructions and nothing in the response said they were missing. Mid-conversation system turns already handled the array shape ([#7056](https://github.com/diegosouzapw/OmniRoute/pull/7056)); only the first one did not ([#12206](https://github.com/diegosouzapw/OmniRoute/issues/12206)). Regression guard: `tests/unit/translator-openai-responses-system-content-parts.test.ts`. ([#12207](https://github.com/diegosouzapw/OmniRoute/pull/12207)) — thanks @Gorillaz322 +- **fix(free-tier):** `/api/free-tier/summary` no longer computes its totals from a Radar feed built before the catalog the running release ships. When the cached feed is older — or carries no build date at all — the route answers from the shipped catalog, resolved through the operator's local model state so disabled and tombstoned models stay out of the numbers ([#12215](https://github.com/diegosouzapw/OmniRoute/pull/12215)). — thanks @maxmad64bis +- **fix(oauth):** Keep a Claude personal workspace and a Team organization as separate connections — they share the same email and `accountUUID`, so the email-only OAuth dedup let the second login overwrite the first account's tokens; `organizationUUID` now disambiguates them, the way `workspaceId` does for Codex ([#12222](https://github.com/diegosouzapw/OmniRoute/pull/12222)) — thanks @davidebaraldo +- **fix(resilience):** a 402 on a single paid model of a passthrough/gateway provider (e.g. `kilo-gateway`, `ollama-cloud`) no longer terminalizes the whole connection with a never-auto-recovered `credits_exhausted` status — only the paid model is locked out, so free models on the same key keep serving. 402 variant of [#3027](https://github.com/diegosouzapw/OmniRoute/issues/3027). Single-credential providers are unaffected — a 402 there is still treated as the key being genuinely out of credit ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239) / [#10616](https://github.com/diegosouzapw/OmniRoute/issues/10616)) ([#12242](https://github.com/diegosouzapw/OmniRoute/issues/12242)) — thanks @brick30llc-ctrl ([#12266](https://github.com/diegosouzapw/OmniRoute/pull/12266)) — thanks @geekyNads +- **fix(sse):** trust `finish_reason: "length"`/`"max_tokens"` over the reasoning-consumed-token ratio in response quality validation, so a reasoning model truncated below the old 90% threshold correctly fails and retries instead of returning empty content as a silent "success" ([#12262](https://github.com/diegosouzapw/OmniRoute/pull/12262)) — thanks @brick30llc-ctrl +- **fix(settings):** `PUT /api/settings/cache-config` now persists `alwaysPreserveClientCache` to the flat general settings the runtime cache-control policy actually reads; previously the value landed in the databaseSettings "cache" section and was silently ignored, so the endpoint had no effect on `cache_control` passthrough ([#12304](https://github.com/diegosouzapw/OmniRoute/pull/12304)) — thanks @davidebaraldo +- **fix(grok-cli):** treat omitted SuperGrokPro `creditUsagePercent` as 0% used so Provider Limits still renders a weekly bar (proto3 zero-elision) ([#12312](https://github.com/diegosouzapw/OmniRoute/pull/12312)) — thanks @HouMinXi +- **fix(combo):** Expose the two Auto-Combo scoring factors nobody could set — the combo validation schema and the dashboard weight sliders both declared 13 of the scorer's 15 factors, so `connectionDensity` (spreads load across a provider's connections) and `quality` were dropped on save and offered nowhere. The sliders also shipped their own default table that differed from the engine's on every non-zero factor and summed to 1.05, so the percentages shown next to them added up to 105%. Both lists now match `DEFAULT_WEIGHTS`, and a test keeps them there. Note that a combo whose stored `weights` omitted the two keys was effectively running with them at zero and the other thirteen renormalized upward; it now runs with the engine's intended distribution, so its routing does shift ([#12314](https://github.com/diegosouzapw/OmniRoute/pull/12314)) — thanks @maxmad64bis +- **fix(docs):** The free-tier reference no longer says its numbers come "confidence tagged per row" — no catalog entry carries a confidence tag and the API serves none, so every figure on that page is an estimate of the same, unstated quality. The page now states what an entry does vouch for: an independently documented hard stop (set by hand with the source in a comment, never defaulted to `true`) and a prompt-training disclosure, both with live counts the `check:docs-counts` gate keeps honest ([#12318](https://github.com/diegosouzapw/OmniRoute/pull/12318)) — thanks @maxmad64bis +- **fix(usage):** `adobe-firefly` and `firefly` have had a working usage fetcher since Adobe Firefly landed, but neither was ever added to the registration list, so the provider-plugin manifest, `genericQuotaFetcher` and the free-access quota cache all reported them as having no usage support — while `USAGE_SUPPORTED_PROVIDERS` said the opposite. Both are now declared, which also means their credit balance is fetched like any other declared provider's: `registerGenericQuotaFetchers` now registers a generic quota fetcher for them, and `resolveFreeAccessState` no longer returns early. A test holds the registration list to the dispatcher's switch in both directions, which is what the module's own docstring already asked for in prose ([#12321](https://github.com/diegosouzapw/OmniRoute/pull/12321)) — thanks @maxmad64bis +- **fix(quota):** drop the generic quota cache (agy / Antigravity / Claude OAuth) on an upstream 429 so reset-aware scoring does not keep a 60s stale snapshot, and force-refresh the next usage fetch so inner provider caches cannot recache the same window ([#12325](https://github.com/diegosouzapw/OmniRoute/pull/12325)) — thanks @HouMinXi +- **fix(combos):** deleting a combo now clears its persisted LKGP pins instead of leaving unreachable `key_value` rows behind ([#12326](https://github.com/diegosouzapw/OmniRoute/issues/12326)) ([#12425](https://github.com/diegosouzapw/OmniRoute/pull/12425)) — thanks @KrzysiekSko +- **fix(sse):** Keep ZWNJ (U+200C) and ZWJ (U+200D) in assistant text, reasoning and tool-call arguments — Persian/Kurdish half-space (`ارائه‌دهنده`), Arabic/Indic shaping and emoji sequences no longer lose them; the response de-obfuscation now removes joiners only between ASCII word characters, where the request side inserts them ([#12186](https://github.com/diegosouzapw/OmniRoute/issues/12186)) — thanks @rezjalibd ([#12359](https://github.com/diegosouzapw/OmniRoute/pull/12359)) — thanks @pacocartones +- **fix(resilience):** count resolved upstream 5xx results against the provider circuit breaker on the chat path — `CircuitBreaker.execute()` no longer reads a resolved `{ success: false, status: 5xx }` as a success that cancels the call-site failure, so a provider answering 503s now trips its breaker instead of staying `CLOSED` at `failureCount: 1`; single-model and combo dispatches are each accounted exactly once ([#12254](https://github.com/diegosouzapw/OmniRoute/issues/12254)) ([#12360](https://github.com/diegosouzapw/OmniRoute/pull/12360)) — thanks @pacocartones +- **fix(providers):** resolve the Codex quota auto-ping model from the live provider catalog and lifecycle registry instead of the retired `gpt-5.1-codex-mini`, and pause the ping with one actionable warning when no selectable Codex model exists rather than retrying a shut-down id every cooldown window ([#11905](https://github.com/diegosouzapw/OmniRoute/issues/11905)) ([#12361](https://github.com/diegosouzapw/OmniRoute/pull/12361)) — thanks @pacocartones +- **fix(api):** keep the `{created, data}` wrapper on combo-routed `/v1/images/generations` responses and default Codex image results to `b64_json` on both `/v1/images/generations` and `/v1/images/edits` so Codex CLI's built-in `image_gen` can decode them ([#12268](https://github.com/diegosouzapw/OmniRoute/issues/12268)) ([#12362](https://github.com/diegosouzapw/OmniRoute/pull/12362)) — thanks @pacocartones +- **fix(sse):** Name the shadowed custom provider node when a built-in provider id/alias (e.g. `openference` → `of`) reserves the prefix of an existing OpenAI/Anthropic-compatible node, so the runtime `No active credentials for provider: ` error explains that the prefix routed to the built-in and never reached the node's healthy connections, instead of contradicting the dashboard ([#11943](https://github.com/diegosouzapw/OmniRoute/issues/11943)) — thanks @morpheus9393 ([#12365](https://github.com/diegosouzapw/OmniRoute/pull/12365)) — thanks @pacocartones +- **fix(i18n):** wrap `ccOnboardingKeyPlaceholder` in ICU single quotes across all 43 locales so angle brackets render literally instead of being parsed as rich-text tags, which crashed the Claude Code onboarding block with `INVALID_MESSAGE: INVALID_TAG` ([#12302](https://github.com/diegosouzapw/OmniRoute/issues/12302)) ([#12369](https://github.com/diegosouzapw/OmniRoute/pull/12369)) — thanks @gonisulaimann +- **fix(models):** publish `effort_tiers` on Kimi K3's synced base-model entries (`kmca/k3`, `kmca/k3-256k`) so catalog-only clients (OpenCode, plain SDK pickers) can see and select the reasoning tiers (`low`/`high`/`max`) the synced metadata already carried — the `isSkippedEffortProvider` gate no longer suppresses tier visibility on those base entries, while synthetic `-` variant generation stays prevented and Codex/GLM base models remain excluded unchanged ([#12299](https://github.com/diegosouzapw/OmniRoute/issues/12299)) ([#12371](https://github.com/diegosouzapw/OmniRoute/pull/12371)) — thanks @gonisulaimann +- **fix(guardrails):** keep `auto`/`auto/*` virtual combos exempt from the Vision Bridge `fixedModel` credential guard so a combo target is passed through instead of silently falling back to global auto-selection ([#12237](https://github.com/diegosouzapw/OmniRoute/issues/12237)) ([#12373](https://github.com/diegosouzapw/OmniRoute/pull/12373)) — thanks @pacocartones +- **fix(combo):** capability-filter exhaustion caused by `max_tokens` above every target's known output limit now reports that reason (requested `max_tokens` vs the pool's highest known ceiling) instead of the unrelated "supports structured output" message ([#12229](https://github.com/diegosouzapw/OmniRoute/issues/12229)) — thanks @DW-MediaLab ([#12374](https://github.com/diegosouzapw/OmniRoute/pull/12374)) — thanks @pacocartones +- **fix(auth):** the `least-used` account strategy now prefers accounts without backoff before falling back to oldest `lastUsedAt`, the same tie-break `round-robin` already applies, so a failover no longer lands on a just-rate-limited account for a single request ([#12279](https://github.com/diegosouzapw/OmniRoute/issues/12279)) — thanks @tenshiak ([#12375](https://github.com/diegosouzapw/OmniRoute/pull/12375)) — thanks @pacocartones +- **fix(docker):** the `chatgpt-web-codex-browser` image now finds the Chrome binary under `chrome-linux64/` (Chrome for Testing layout in `playwright:v1.62.0-noble`) as well as the legacy `chrome-linux/`, so the container no longer crash-loops with `exec: --headless=new: not found` ([#12024](https://github.com/diegosouzapw/OmniRoute/issues/12024)) ([#12376](https://github.com/diegosouzapw/OmniRoute/pull/12376)) — thanks @pacocartones +- **fix(providers):** declare `groq/compound` and `allam-2-7b` as non-reasoning models in the curated Groq registry so `reasoning_effort` / `output_config.effort` / `thinking` from Claude Code are stripped instead of forwarded, which Groq rejected with HTTP 400 ([#12134](https://github.com/diegosouzapw/OmniRoute/issues/12134)) ([#12379](https://github.com/diegosouzapw/OmniRoute/pull/12379)) — thanks @pacocartones +- **fix(executors):** `OpencodeExecutor` no longer forces a direct connection when the connection has a proxy assigned in Proxy Management but no per-account proxies: the single-account fast path used to wrap the upstream dispatch in the direct-egress sentinel, discarding the ambient proxy context the chat handler had pinned from `proxy_assignments`, so API-key `opencode`/`opencode-go` connections egressed from the host IP (and hit geoblocks) despite the assignment. The direct pin is now applied only when no ambient proxy context exists ([#11894](https://github.com/diegosouzapw/OmniRoute/issues/11894) — thanks @hizzt) ([#12380](https://github.com/diegosouzapw/OmniRoute/pull/12380)) — thanks @pacocartones +- **fix(api):** `GET /v1/models` with `MODELS_CATALOG_PREFIX_MODE=canonical` (or `?prefix=canonical`) now lists providers whose registry alias is undefined or equal to their own id (Antigravity, Antigravity CLI and other self-aliased built-ins) — their single `provider/model` id was dropped by the alias/canonical duplicate guard in the static, synced, custom and alias-backed catalog loops ([#12058](https://github.com/diegosouzapw/OmniRoute/issues/12058)) — thanks @cheynetom ([#12381](https://github.com/diegosouzapw/OmniRoute/pull/12381)) — thanks @pacocartones +- **fix(translator):** Drop replayed `thinking` blocks that carry no signature (the shape produced from cross-provider `reasoning_content`) instead of stamping the default Claude signature on them, which Anthropic rejected with `400 Invalid signature in thinking block` on the next turn served by an Anthropic rung ([#12105](https://github.com/diegosouzapw/OmniRoute/issues/12105)) — thanks @atescivitci-cmd ([#12386](https://github.com/diegosouzapw/OmniRoute/pull/12386)) — thanks @pacocartones +- **fix(cli):** Resolve Bun's `--preload` polyfill path against the package root instead of `dist/`, so `omniroute` installed with `bun install -g` no longer crashes at startup with `error: preload not found …/dist/open-sse/utils/setupPolyfill.ts` ([#11980](https://github.com/diegosouzapw/OmniRoute/issues/11980)) — thanks @joglomedia ([#12387](https://github.com/diegosouzapw/OmniRoute/pull/12387)) — thanks @pacocartones +- **fix(providers):** `gemini-business` now publishes its model catalog — `/v1/models` and `/v1/providers/gemini-business/models` list the 12 enterprise Gemini ids the executor understands instead of returning an empty list (#12107) ([#12389](https://github.com/diegosouzapw/OmniRoute/pull/12389)) — thanks @pacocartones +- **fix(db):** install `busy_timeout` before the SQLite connection's first statement so a process opening the database while another one closes its WAL connection waits out the transient EXCLUSIVE lock instead of dying with `database is locked`, and recognise the drivers' real BUSY/PROTOCOL/IOERR errors as transient in the corruption probe so the same lock no longer renames the database away as corrupt; deflakes `cross-process contenders never both acquire the same connection` (#12394 — thanks @pacocartones) +- **fix(chat-admission):** derive the `chat_admission_busy` 503 `Retry-After` from observed heavyweight-lease occupancy — the larger of the exhausted `OMNIROUTE_CHAT_ADMISSION_QUEUE_MS` window and the time since capacity last turned over, capped at 60 s — instead of a fixed 1 s (structural) / 2 s (byte-stage) hint that invited Codex/agent fan-out clients to re-send ~1 MiB `/v1/responses` bodies every second into a gate held for the whole SSE lifetime; an idle gate keeps the historical floors ([#12135](https://github.com/diegosouzapw/OmniRoute/issues/12135)) (#12395 — thanks @pacocartones) +- **fix(api-manager):** the API key permissions modal no longer silently drops `allowedCombos` entries its Combo picker cannot render — routing-rule names such as `rt-*`, which the backend already honours — when "All" is clicked and the key is switched back to "Restrict"; those entries now survive the toggle, are listed read-only under the combo list so the count and the list agree, and are saved back verbatim instead of persisting `[]` (deny-all) (#12397 — thanks @pacocartones) +- **fix(catalog):** write the NUL separator of the catalog connection memo key, the provider serviceKind memo key, the Video Bridge promotion group key and a JSON-exactness test fixture as the `\u0000` escape instead of a raw byte — same runtime value, but the raw byte made git, GitHub and ripgrep treat those files as binary (hidden PR diffs, silently skipped searches); a guard test now keeps raw NUL bytes out of `src/`, `open-sse/` and `tests/` (#12403 — thanks @pacocartones) +- **fix(providers):** add `CLAUDE_CODE_CLIENT_VERSION` and `GITHUB_COPILOT_CLI_VERSION` env overrides so Anthropic/Copilot client-version gates can be unblocked without a rebuild ([#12417](https://github.com/diegosouzapw/OmniRoute/issues/12417)) ([#12632](https://github.com/diegosouzapw/OmniRoute/pull/12632)) — thanks @HouMinXi +- **fix(db):** back-fill `last_ping_at` and `last_pinged_reset_key` on `provider_connections` during schema reconciliation so divergent lineages that skipped `123_quota_auto_ping` still accept quota auto-ping writes ([#12470](https://github.com/diegosouzapw/OmniRoute/pull/12470) — thanks @KooshaPari) +- **fix(ci):** document MIT exceptions for `@eloqnt/{config,format-json,format-po}` (next-intl transitive; locked tarballs omit `license`) and keep the A2A lifecycle vitest off the real SQLite persistence seam ([#12581](https://github.com/diegosouzapw/OmniRoute/issues/12581)) ([#12595](https://github.com/diegosouzapw/OmniRoute/pull/12595)) +- OpenCode plugin `/v1/models` catalog fetch now waits 30s by default and attaches HTTP `statusCode` on 401/5xx so host fallback plugins can hop instead of seeing an untyped AbortError/UnknownError. ([#12607](https://github.com/diegosouzapw/OmniRoute/pull/12607)) — thanks @RaviTharuma +- **CI:** the OpenAPI security-tier gate now mirrors `isAlwaysProtectedPath()` in full — it also reads `ALWAYS_PROTECTED_API_PATTERNS`, so the pattern-gated credential routes (`/api/providers/{id}/{claude,codex}-auth/{export,apply-local}`, GHSA-5926-2w35-7h4q) no longer report as unannotated. (#12605) +- **fix(api):** GET /v1/models no longer waits forever on a hung coalesced catalog rebuild; cold-path waits are bounded (`CATALOG_BUILD_TIMEOUT_MS`, default 8s) and a last-good 200 is served when the rebuild times out ([#12627](https://github.com/diegosouzapw/OmniRoute/issues/12627)). ([#12628](https://github.com/diegosouzapw/OmniRoute/pull/12628)) — thanks @RaviTharuma +- **fix(cursor):** a non-array `tool_calls` on an assistant message no longer crashes the cursor request translator with a `TypeError`; both loops now require an array ([#12689](https://github.com/diegosouzapw/OmniRoute/issues/12689)) ([#12691](https://github.com/diegosouzapw/OmniRoute/pull/12691)) — thanks @soroush5 +- **fix(chat):** Chat Completions no longer return empty `content` after a server-owned memory or skills tool; the first provider send and account/model recovery now share one pipeline so a follow-up round-trip can fill the reply ([#12696](https://github.com/diegosouzapw/OmniRoute/issues/12696)) — thanks @HouMinXi ([#12867](https://github.com/diegosouzapw/OmniRoute/pull/12867)) +- **fix(dashboard):** batch-deleting provider keys no longer toasts failure after a successful delete when the confirm button's click event is forwarded as `onAfter` ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) — thanks @HouMinXi +- **fix(glm):** drop the extra 16th argument to `createSSETransformStreamWithLogger` that TypeScript rejected (TS2554) and that never reached the TransformStream ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) ([#12770](https://github.com/diegosouzapw/OmniRoute/pull/12770)) — thanks @HouMinXi +- **fix(dashboard):** pass `nodeMap` into Runtime `QuotaGroup` so a quota monitor in error/exhausted/alerting no longer throws `ReferenceError: nodeMap is not defined`. ([#12868](https://github.com/diegosouzapw/OmniRoute/pull/12868)) — thanks @HouMinXi +- Restricted API keys whose `allowedModels` lists a combo name no longer skip every combo member at pre-dispatch (`ALL_TARGETS_SKIPPED`). Inner-target filtering still applies when the allow-list is a provider prefix or `disableNonPublicModels` is on ([#12899](https://github.com/diegosouzapw/OmniRoute/pull/12899)). — thanks @HouMinXi +- **fix(combos):** Effort-suffixed combo members inherit the base model's `model_context_overrides` row so priority order is not inverted on large requests ([#12926](https://github.com/diegosouzapw/OmniRoute/pull/12926)) — thanks @HouMinXi +- **fix(catalog):** Picker-added `customModels` enter the dispatch-time live catalog so combo members and bare inference no longer 400 ([#12934](https://github.com/diegosouzapw/OmniRoute/pull/12934)) — thanks @HouMinXi +- **fix(docker):** re-derive the Docker build's worker-pool memory budget from the MEASURED ~4.5 GB per-process RSS (the issue owner's own VPS dmesg OOM-killer reproduction) instead of the stale 2560 MB/worker inference #11419 shipped, and lower `OMNIROUTE_BUILD_WORKERS` 3 → 2 so 1 parent + 1 page-data worker (2 processes × 4.5 GB = 9 GB) fits the 12.288 GB (75%) budget on a 16 GB GitHub Actions runner — the previous default (1 parent + 2 workers = 13.5 GB) still overcommitted the runner and kept "Publish to Docker Hub" failing with `cannot allocate memory` after #11419 merged (#7518). ([#11663](https://github.com/diegosouzapw/OmniRoute/pull/11663)) +- **fix(electron):** the packaged Windows desktop build now passes the #7592 cold-restart smoke end to end. Five defects were found and fixed on the way: (1) optional-pack staging failed on any machine whose `tar` is GNU tar — it reads the drive letter in an absolute `-f C:\...` archive path as a remote rsh host (`Cannot connect to C:`), so staging now passes a bare filename with `cwd` at the tarball directory and surfaces tar stderr on failure; (2) the Electron `files` allowlist was missing `lib/loginHeaderCapture.js`, so the packaged main process crashed at startup with `Cannot find module './lib/loginHeaderCapture'`; (3) electron-builder ≥26 injects an `!**/node_modules/**` ignore into every extraResources pattern list that no positive filter can override, which silently dropped the staged runtime `node_modules` (including the better-sqlite3 N-API prebuild) from `resources/app` — a fresh v3.8.50 desktop build would have shipped with no native SQLite driver and reproduced the #7592 sql.js fallback on every machine; an `afterPack` hook now restores the staged `node_modules` after packing; (4) the packaged-app smoke harness redirected `USERPROFILE` into its temp DATA_DIR, but Electron resolves the Roaming profile from `%USERPROFILE%\AppData\Roaming\` (USERPROFILE takes precedence over the APPDATA env var) and the path service throws instead of creating it, so `requestSingleInstanceLock()` returned false and the app exited(0) silently before `app.whenReady()` — the harness now pre-creates the derived tree, and `ensureSmokeEnvDirs` is exported and unit-tested; (5) the #7592 driver assertion parsed a `[DB] Driver: ...` line that the server's primary DB path never printed (only the unused `openDatabaseAsync()` did) — `getDbInstance()` now emits the same line on open so the guard can actually assert the native driver. Also: the smoke env-allowlist unit test hardcoded POSIX paths and could only pass on Linux/macOS; expectations are now host-agnostic, with new regression tests for the USERPROFILE-derived tree and `tarPack` under absolute Windows-style paths ([#11443](https://github.com/diegosouzapw/OmniRoute/pull/11443)) — thanks @jonlwheat2-gif +- fix(sse): stop the auto-combo candidates inspector from silently dropping model-locked/cooled-down rows (#9133) ([#11994](https://github.com/diegosouzapw/OmniRoute/pull/11994)) — thanks @hartmark +- **fix(providers):** Perplexity Web no longer turns upstream stream failures into successful assistant text; pre-content failures remain eligible for fallback, partial output ends with a structured sanitized error, and failed sessions are not persisted ([#12465](https://github.com/diegosouzapw/OmniRoute/pull/12465)) +- **fix(video-bridge):** preserve chronological video and transcript fusion, source-frame timestamps, transcript provenance, full contact-sheet intervals, and pre-abort callback isolation ([#11681](https://github.com/diegosouzapw/OmniRoute/pull/11681)) +- **Z.ai Web:** HTTP 200 streams carrying an upstream error now terminate with a structured failure instead of assistant text plus a normal stop, preserving partial output while allowing pre-content combo fallback. ([#12454](https://github.com/diegosouzapw/OmniRoute/pull/12454)) +- Absorb `Error [AbortError]: request_signal_aborted` and DOMException AbortError shapes in the process-level client-abort crash guard so routine client disconnects no longer kill the server (exit code 7). ([#12165](https://github.com/diegosouzapw/OmniRoute/pull/12165)) — thanks @alvinveroy +- **fix(sse):** Treat Adapta Web `type:error` SSE events as sanitized non-stream failures instead of empty HTTP 200 completions. ([#12459](https://github.com/diegosouzapw/OmniRoute/pull/12459)) +- **fix(autoCombo):** layer 3 (`models_dev_tier`) no longer scores retired models as premium, merge cross-provider capability rows deterministically, and inherit tier scores through variant ids ([#11508](https://github.com/diegosouzapw/OmniRoute/issues/11508)). `getModelsDevTierFitness` now vetoes ids present in `config/quality/model-lifecycle.json` with status `retired` before any other signal; `loadModelCapabilities()` aggregates per `model_id` (any-non-null-true for booleans, max for `limit_context`) instead of last-write-wins over SQLite's undefined row order; a capabilities miss retries through `resolveScoresAs()`'s base id and reports `models_dev_tier:inherited`, matching layers 1–2 (#11492). Adds `getModelsDevTierFitnessWithSource()` and an `invalidateCapabilitiesCache()` test/ops hook. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) — thanks @Meet6338-X +- fix(sse): accept Claude `content:[]` + `stop_reason: max_tokens`/`tool_use` in `detectMalformedNonStream` (match `isEmptyContentResponse`) so Claude Code `/model` probes with `max_tokens: 1` no longer become a false 502 ([#11568](https://github.com/diegosouzapw/OmniRoute/pull/11568)) — thanks @Seramicx +- **fix(security):** sanitize `request.failed` diagnostics before publishing them to live dashboard listeners and replay history, while keeping status, model, provider, latency, and internal call-log diagnostics intact. ([#12469](https://github.com/diegosouzapw/OmniRoute/pull/12469)) +- **fix(pwa):** do not intercept dashboard navigations so Chrome can fall back from a stale HTTP/3 Alt-Svc advertisement (UDP :20128 is unpublished; F5 on a long-lived tab hung until a new tab opened a fresh TCP connection). ([#12767](https://github.com/diegosouzapw/OmniRoute/pull/12767)) — thanks @HouMinXi +- **fix(executors):** handle DuckDuckGo ERR_BN_LIMIT (418) without retrying — when the upstream returns `418 ERR_BN_LIMIT` (rate-limit/ban), the executor now returns the error immediately instead of burning another VQD acquisition that would only count against the IP limit. The retry logic for `418 ERR_CHALLENGE` (unsolved challenge) remains unchanged. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) ([#12110](https://github.com/diegosouzapw/OmniRoute/pull/12110)) — thanks @oyi77 / @Meet6338-X +- Fixed DuckDuckGo streaming responses losing JSON lines and UTF-8 characters split across network chunks. ([#11528](https://github.com/diegosouzapw/OmniRoute/pull/11528)) — thanks @pacocartones +- Harden generated Cloudflare, Vercel, and Deno relays by stripping hop-by-hop, framing, and proxy authentication headers before forwarding requests upstream. ([#11533](https://github.com/diegosouzapw/OmniRoute/pull/11533)) — thanks @pacocartones +- **feat(grok-cli):** Provider Limits shows grok-cli banked reset credits from `GetRemainingResets` (including a real zero; a failed RPC omits the row) and the existing View credits button now calls `ConsumerUiSvc/RedeemReset` for grok-cli. Live tokens use nested fields 10/20/30 (id + Timestamp), not compact 1/2/3. ([#12805](https://github.com/diegosouzapw/OmniRoute/pull/12805)) — thanks @HouMinXi +- **fix(memory):** Embedding Model Quick select, Embedding Source remote dropdown, and Rerank selector now list every configured provider with embedding/rerank support instead of only chat-catalog text matches plus OpenRouter live discovery; a generic OpenAI-compatible `/embeddings` + Cohere-compatible `/rerank` runtime fallback resolves any configured chat provider's embedding/rerank endpoint, so unlisted providers no longer fail with "Unknown embedding provider"; both memory selectors gained a free-text model override ([#11390](https://github.com/diegosouzapw/OmniRoute/pull/11390)) — thanks @rqzbeh +- Harden SQLite upgrades around the historical migration-074 version collision: missing discovery and inspector tables are replayed atomically, pre-existing databases (including setup-created skeletons) receive reusable content-addressed safety snapshots, and Node test/eval probes without `DATA_DIR` are isolated from the operator database. ([#12435](https://github.com/diegosouzapw/OmniRoute/pull/12435)) +- **fix(dashboard):** Moonshot/Kimi Open Platform voucher and cash leftover percentages follow the bucket balance, so an empty wallet no longer paints those rows as 100% while Available is 0% ([#12733](https://github.com/diegosouzapw/OmniRoute/pull/12733)) — thanks @HouMinXi +- **fix(combo):** return non-retryable HTTP 400 when all candidates for a pinned native Codex turn are unavailable due to model-scoped lockout, terminating the turn cleanly while preserving turn continuity and enabling standard Combo routing on subsequent turns ([#12240](https://github.com/diegosouzapw/OmniRoute/pull/12240)) — thanks @mdigitalbh81 +- Fixed auto-router strategies preserving the selected connection through ranking and dispatch when multiple connections share the same provider and model. ([#11530](https://github.com/diegosouzapw/OmniRoute/pull/11530)) — thanks @pacocartones +- Preserve Chat Completions JSON-object response formats when translating requests to the Responses API. ([#11529](https://github.com/diegosouzapw/OmniRoute/pull/11529)) — thanks @pacocartones +- Fix idempotency fingerprints so Responses API requests include `input` and other semantic generation fields using stable serialization, while excluding credentials and request metadata. ([#11532](https://github.com/diegosouzapw/OmniRoute/pull/11532)) — thanks @pacocartones +- **fix(api):** Generated API CLI commands now enforce required OpenAPI request bodies; Combo test commands forward the required `comboName` body, while API keys created by older writers after migration 149 preserve legacy allow-all Combo access without widening explicit empty allowlists — thanks @marcelokarval ([#12070](https://github.com/diegosouzapw/OmniRoute/pull/12070)) +- **fix(grok-web):** treat upstream streaming failures as failures instead of successful assistant text: error-only streams now fail readiness with HTTP 502, while failures after legitimate content preserve that partial output and terminate through the sanitized stream failure path without a normal `stop` completion. ([#12458](https://github.com/diegosouzapw/OmniRoute/pull/12458)) +- HuggingChat now turns HTTP 200 JSONL generation failures into a sanitized 502 before content, or a fixed public stream failure after partial output, so fallback and request persistence no longer record a false successful stop. ([#12456](https://github.com/diegosouzapw/OmniRoute/pull/12456)) +- **fix(providers):** keep 1min.ai HTTP 200 stream errors out of assistant content, preserve partial output, and expose sanitized terminal errors so pre-content failures can fall back. ([#12466](https://github.com/diegosouzapw/OmniRoute/pull/12466)) +- **fix(sessions):** preserve legacy Sessions data when the additive exclusive-session projection is unavailable, returning an empty projection and warning only once per contiguous outage instead of failing the endpoint; the Sessions badge intentionally reflects the merged legacy and exclusive row count introduced by [#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389) ([#11469](https://github.com/diegosouzapw/OmniRoute/pull/11469)) — thanks @KaspaPulse +- Render Nimble Search with the generic provider icon and serve Opper's proven logo locally. ([#12415](https://github.com/diegosouzapw/OmniRoute/pull/12415)) +- **fix(quality):** `config/quality/provider-assets-provenance.jsonl` was stale against the physical `public/providers/` tree (still listing 225 assets and 5 duplicate-content groups after several provider-retirement PRs removed 85 icon files), which would have made `check:provider-asset-provenance` fail on the next full run. Reconciled the manifest to the real 142-file snapshot (142 asset records, 1 duplicate-content group, 2 newly-registered files marked `unresolved` pending provenance research), repinned `auditedCommit`, and updated the regression test's hardcoded counts to match. ([#11876](https://github.com/diegosouzapw/OmniRoute/pull/11876)) +- fix(ui): wrap quota card footer action buttons to prevent clipping Refresh button on narrow cards — when extra action buttons such as `View credits` are present on Codex cards, allow the footer row to flex-wrap with `shrink-0` buttons so `Refresh now` remains visible and clickable across all responsive card widths ([#11464](https://github.com/diegosouzapw/OmniRoute/pull/11464)) — thanks @adevwithpurpose +- Keep Antigravity Gemini usable when the same connection's Claude weekly quota is empty; generic quota cache stays per-connection for every other provider. ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) — thanks @HouMinXi +- Fixed Tailscale tunnel setup to reject ports outside the valid 1-65535 range and safely install Windows MSI packages from paths containing apostrophes. ([#11534](https://github.com/diegosouzapw/OmniRoute/pull/11534)) — thanks @pacocartones +- Classify Cloudflared and Tailscale process-management routes as local-only and spawn-capable while preserving remote access to read-only tunnel status endpoints. ([#11531](https://github.com/diegosouzapw/OmniRoute/pull/11531)) — thanks @pacocartones +- Electron release workflow: the `publish-npm` job now grants `actions: read` to the reusable `npm-publish.yml` it calls (its `publish` job requests it), which is what made GitHub refuse the whole v3.8.50 run at startup and ship the release with zero desktop assets; a `workflow_dispatch` now builds the requested tag instead of the dispatching branch and can skip the npm leg (`publish_npm=false`) when only re-attaching assets ([#11974](https://github.com/diegosouzapw/OmniRoute/pull/11974)) +- **fix(dashboard):** The Combos page usage guide now reads its dismissal through `useSyncExternalStore` instead of correcting SSR state inside an effect, removing an extra commit of the page tree on every load (and the `react-hooks/set-state-in-effect` error it raised). ([#12671](https://github.com/diegosouzapw/OmniRoute/pull/12671)) +- Electron release: `electron/package-lock.json` regained the optional `electron-builder-squirrel-windows` subtree (13 entries) that `npm ci` had been refusing as out of sync, `electron-release.yml` gained a `build_ref` dispatch input and stops regenerating release notes on a re-attach dispatch, and the npm publish workflow attaches the SBOM to the GitHub Release on dispatch publishes too — so the v3.8.51 tag ships every desktop asset and the SBOM like v3.8.49 did ([#12022](https://github.com/diegosouzapw/OmniRoute/pull/12022)) +- Drained the reds every PR against `release/v3.8.51` was born with: documented the three service-worker build-id variables, registered the six retirement/tunnel tests with the mutation gate, approved `eslint-plugin-react-hooks` in the dependency allowlist, added the six `combo.sort.*` strings to `vi` and `pt-BR`, pointed the ChatGPT Web doc at the real migration-168 test, worded the g4f hint around the member key, and realigned four tests to the retired-provider catalog and the legacy-schema fixtures the retirement migrations touch. ([#11940](https://github.com/diegosouzapw/OmniRoute/pull/11940)) +- **fix(providers):** Claude, Grok, LMArena, Notion, and Perplexity web-cookie transports now use pooled `wreq-js` 3.2 instead of the native sidecar, with all nine supported bindings pinned and audited, and the applicable platform binding plus native-license evidence included in each release artifact ([#12429](https://github.com/diegosouzapw/OmniRoute/pull/12429), supersedes [#11753](https://github.com/diegosouzapw/OmniRoute/pull/11753)). +- **fix(providers):** Zed Hosted streaming failures now trigger fallback before content and end partial streams with a sanitized structured error instead of fake assistant text and a normal-success stop. ([#12455](https://github.com/diegosouzapw/OmniRoute/pull/12455)) +- **fix(test):** capture hook result after render (direct commit `b85d0abb0b`) +- **fix(sync):** repair the two regressions the v3.8.50 sync-back left on release/v3.8.51 (direct commit `5ade9e0851`) +- **revert(models):** drop the memo reset hook ported from #10055 (direct commit `282de841ae`) +- **fix(typecheck):** guard the optional connectionId before the reactive model sync (direct commit `f2825bad75`) +- **fix(quota-share):** release the winner's reserved in-flight slot (#11371) ([#11408](https://github.com/diegosouzapw/OmniRoute/pull/11408)) — thanks @oyi77 +- **fix(providers):** stop silently dropping reasoning effort on opencode families (#10788) ([#11409](https://github.com/diegosouzapw/OmniRoute/pull/11409)) — thanks @oyi77 +- **fix(combo):** enable genuine quota-aware routing for generic providers (antigravity, claude, etc.) ([#11411](https://github.com/diegosouzapw/OmniRoute/pull/11411)) — thanks @HouMinXi +- **fix(oauth):** allow expired token retry before deactivation and activate expired retry loop (#P0) ([#11414](https://github.com/diegosouzapw/OmniRoute/pull/11414)) — thanks @HouMinXi +- **perf(executors):** lazy-load the executor registry — defer class imports + construction to first use (#11220) ([#11421](https://github.com/diegosouzapw/OmniRoute/pull/11421)) — thanks @oyi77 +- **fix:** make release/v3.8.50 CI gates green (run 32786966560 root causes) ([#11450](https://github.com/diegosouzapw/OmniRoute/pull/11450)) — thanks @jonlwheat2-gif +- **fix(sse):** exact-domain cookie match and origin-equality URL assertions ([#11452](https://github.com/diegosouzapw/OmniRoute/pull/11452)) — thanks @hartmark +- **fix(providers):** send Responses API payload during OpenAI-compatible validation ([#11454](https://github.com/diegosouzapw/OmniRoute/pull/11454)) — thanks @tuandinh0801 +- **fix(command-code):** fallback to /alpha/generate for Go plan without Provider API access ([#11455](https://github.com/diegosouzapw/OmniRoute/pull/11455)) — thanks @Chewji9875 +- **fix(responses-continuation):** fail closed on a log-truncated stored input/output array ([#11473](https://github.com/diegosouzapw/OmniRoute/pull/11473)) — thanks @hartmark +- **fix(cli):** write jcode settings as [providers.omniroute] in config.toml ([#11484](https://github.com/diegosouzapw/OmniRoute/pull/11484)) — thanks @hongnoul +- **fix(logging):** raise chat-log array truncation cap to 1000 and unify duplicate implementations ([#11499](https://github.com/diegosouzapw/OmniRoute/pull/11499)) — thanks @hartmark +- **fix:** restore live websocket URL helpers ([#11509](https://github.com/diegosouzapw/OmniRoute/pull/11509)) — thanks @PauloFH +- **perf(core):** eliminate async waterfalls across home, cache, and db operations (#11396) ([#11512](https://github.com/diegosouzapw/OmniRoute/pull/11512)) — thanks @HouMinXi +- **fix(kiro):** add runtime.us-east-1.kiro.dev as first-attempt endpoint ([#11517](https://github.com/diegosouzapw/OmniRoute/pull/11517)) — thanks @rifqiawl +- **fix(providers):** remove duplicate 128-tool truncation in OpencodeExecutor (#11444) ([#11518](https://github.com/diegosouzapw/OmniRoute/pull/11518)) — thanks @HouMinXi +- **fix(providers):** poll onboardUser LRO before classifying BYOP ([#11519](https://github.com/diegosouzapw/OmniRoute/pull/11519)) — thanks @rifqiawl +- **fix(translator):** preserve exact tool name casing for Claude tool_use payloads (#11487) ([#11520](https://github.com/diegosouzapw/OmniRoute/pull/11520)) — thanks @HouMinXi +- **fix(kimi-web):** align health probe with executor domain ([#11521](https://github.com/diegosouzapw/OmniRoute/pull/11521)) — thanks @pacocartones +- **fix(cli):** preserve existing admin password during setup ([#11522](https://github.com/diegosouzapw/OmniRoute/pull/11522)) — thanks @pacocartones +- **fix(mitm):** keep Windows DNS elevation off the POSIX sudo path (#11430) ([#11542](https://github.com/diegosouzapw/OmniRoute/pull/11542)) — thanks @official-burak +- **fix(sse):** scale chat admission by ingest byte budget instead of a fixed request count ([#11548](https://github.com/diegosouzapw/OmniRoute/pull/11548)) — thanks @TheDemonTuan +- **fix:** accept Gemini Web cookie exports ([#11554](https://github.com/diegosouzapw/OmniRoute/pull/11554)) — thanks @rafacpti23 +- **fix(i18n):** complete Brazilian Portuguese translations ([#11558](https://github.com/diegosouzapw/OmniRoute/pull/11558)) — thanks @rafacpti23 +- **fix(sse):** explicit types for openai-responses pureHelpers — clears last failing core typecheck gate ([#11567](https://github.com/diegosouzapw/OmniRoute/pull/11567)) — thanks @jonlwheat2-gif +- **fix(opencode):** hydrate Proxy Pool references for no-auth connections ([#11584](https://github.com/diegosouzapw/OmniRoute/pull/11584)) — thanks @AStupidBear +- **fix(ci):** resolve inherited release/v3.8.51 base-red CI failures ([#11608](https://github.com/diegosouzapw/OmniRoute/pull/11608)) — thanks @jonlwheat2-gif +- **fix(db):** fallback load STORAGE_ENCRYPTION_KEY from env files ([#11614](https://github.com/diegosouzapw/OmniRoute/pull/11614)) — thanks @benzntech +- **fix(providers):** mark Brave Search as no longer having a free tier ([#11621](https://github.com/diegosouzapw/OmniRoute/pull/11621)) — thanks @turbolego +- **fix:** pass tool result content through as string in OpenAI/Claude -> Gemini translation ([#11624](https://github.com/diegosouzapw/OmniRoute/pull/11624)) — thanks @Hsia97 +- **fix(sse):** scope credential health to exact key ([#11628](https://github.com/diegosouzapw/OmniRoute/pull/11628)) — thanks @KaspaPulse +- **fix(install):** guard prepare script against missing husky ([#11634](https://github.com/diegosouzapw/OmniRoute/pull/11634)) — thanks @Prajeeth-12 +- **fix(security):** redact NVIDIA_API_KEY at the log sink in the ad-hoc diag script ([#11639](https://github.com/diegosouzapw/OmniRoute/pull/11639)) +- **fix(types):** clear the two type regressions red on release/v3.8.51 ([#11640](https://github.com/diegosouzapw/OmniRoute/pull/11640)) — thanks @jonlwheat2-gif +- **fix(build):** resolve workspace: protocol dependencies to concrete semver versions during prepublish (#11540) ([#11641](https://github.com/diegosouzapw/OmniRoute/pull/11641)) — thanks @HouMinXi +- **fix(search):** properly resolve configured search connection in /v1/responses pipeline (#11524) ([#11642](https://github.com/diegosouzapw/OmniRoute/pull/11642)) — thanks @HouMinXi +- **fix(authz):** trust cf-connecting-ip for via-proxy detection when peer is Cloudflare edge (#11514) ([#11643](https://github.com/diegosouzapw/OmniRoute/pull/11643)) — thanks @HouMinXi +- **fix(security):** compare the OIDC state cookie in constant time ([#11646](https://github.com/diegosouzapw/OmniRoute/pull/11646)) +- **fix(security):** namespace the dedup hash by the calling API key ([#11649](https://github.com/diegosouzapw/OmniRoute/pull/11649)) +- **fix(models):** gate the Codex-native catalog loop on prefix mode (#11632) ([#11662](https://github.com/diegosouzapw/OmniRoute/pull/11662)) — thanks @xiaoyaner0201 +- **fix(dashboard):** project Codex routing quota in account pools ([#11666](https://github.com/diegosouzapw/OmniRoute/pull/11666)) — thanks @KaspaPulse +- **fix(ddgw):** normaliza system no upstream da release v3.8.51 ([#11692](https://github.com/diegosouzapw/OmniRoute/pull/11692)) — thanks @RhianB14 +- **fix(memory):** honor category filter in GET /api/memory ([#11699](https://github.com/diegosouzapw/OmniRoute/pull/11699)) — thanks @Sabeekhann +- **fix(ci):** detect stale workspace lockfile entries ([#11714](https://github.com/diegosouzapw/OmniRoute/pull/11714)) — thanks @pacocartones +- **fix(ci):** stamp dist/BUILD_SHA before the npm publish provenance gate ([#11721](https://github.com/diegosouzapw/OmniRoute/pull/11721)) +- **fix(docker):** decouple the best-effort Bun image from the release manifest ([#11724](https://github.com/diegosouzapw/OmniRoute/pull/11724)) +- **perf(compression):** avoid cloning memo misses twice ([#11727](https://github.com/diegosouzapw/OmniRoute/pull/11727)) — thanks @RaviTharuma +- **fix(docker):** let the bun digest artifact be absent, not fatal ([#11740](https://github.com/diegosouzapw/OmniRoute/pull/11740)) +- **fix(shared):** block cloud-metadata hosts under default remote-image guard ([#11755](https://github.com/diegosouzapw/OmniRoute/pull/11755)) — thanks @santosraju99-hub +- **fix(api):** preserve caller-provided X-Correlation-Id on chat completions ([#11760](https://github.com/diegosouzapw/OmniRoute/pull/11760)) — thanks @aniruddhaadak80 +- **fix(resilience):** lock permanently retired models instead of short backoff (Gemini ban prevention) ([#11762](https://github.com/diegosouzapw/OmniRoute/pull/11762)) — thanks @turbolego +- **fix:** mark Vercel AI Gateway as passthroughModels ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **fix(resilience):** stop hammering permanently-moved endpoints and billing-suspended accounts ([#11774](https://github.com/diegosouzapw/OmniRoute/pull/11774)) — thanks @turbolego +- **fix(lease):** remove global static reservation and gate routing on live active lease occupancy ([#11775](https://github.com/diegosouzapw/OmniRoute/pull/11775)) — thanks @TheDemonTuan +- **fix(ci):** size the install-upgrade gate to a measured run, and log the pack cost ([#11776](https://github.com/diegosouzapw/OmniRoute/pull/11776)) +- **fix(pwa):** stop serving the stale shell after deploys ([#11779](https://github.com/diegosouzapw/OmniRoute/pull/11779)) — thanks @HouMinXi +- **fix(combo):** lock GitHub models rejected as "not supported" for future requests ([#11781](https://github.com/diegosouzapw/OmniRoute/pull/11781)) — thanks @turbolego +- **fix(cli):** probe both IPv4 and IPv6 loopback for server readiness (#11766) ([#11794](https://github.com/diegosouzapw/OmniRoute/pull/11794)) — thanks @vermasomesh835 +- **fix(sse):** preserve CLIProxyAPI wrappers per connection ([#11797](https://github.com/diegosouzapw/OmniRoute/pull/11797)) — thanks @RaviTharuma +- **fix(providers):** honor model auto-fetch opt-in on create ([#11798](https://github.com/diegosouzapw/OmniRoute/pull/11798)) — thanks @RaviTharuma +- **fix(dashboard):** make model auto-fetch order independent ([#11805](https://github.com/diegosouzapw/OmniRoute/pull/11805)) — thanks @RaviTharuma +- **fix(docker):** make GHA cache exports best effort ([#11806](https://github.com/diegosouzapw/OmniRoute/pull/11806)) — thanks @RaviTharuma +- **fix(kiro):** do not permanently ban on 'User is not authorized to make this call' ([#11809](https://github.com/diegosouzapw/OmniRoute/pull/11809)) — thanks @Deftera186 +- **fix(sse):** keep cache-write tokens in OpenAI-shaped usage ([#11814](https://github.com/diegosouzapw/OmniRoute/pull/11814)) — thanks @TheDemonTuan +- **fix(docs):** resolve relative markdown and wiki links across Fumadocs and GitHub wiki ([#11834](https://github.com/diegosouzapw/OmniRoute/pull/11834)) — thanks @AndrianBalanescu +- **fix(chat):** preserve unstripped model string for passthrough provider routing ([#11840](https://github.com/diegosouzapw/OmniRoute/pull/11840)) — thanks @raheemuddin786 +- **fix(mcp):** avoid async initialization inside sync esm wrappers (#11569) ([#11849](https://github.com/diegosouzapw/OmniRoute/pull/11849)) — thanks @HouMinXi +- **fix(sse):** preserve coherent markdown token boundaries across stream translation chunks (#11606) ([#11851](https://github.com/diegosouzapw/OmniRoute/pull/11851)) — thanks @HouMinXi +- **fix(ci):** run the install-upgrade gate on disk, not on the /tmp tmpfs ([#11855](https://github.com/diegosouzapw/OmniRoute/pull/11855)) +- **fix(skills):** expand shorthand property types in injected tool schemas ([#11857](https://github.com/diegosouzapw/OmniRoute/pull/11857)) — thanks @alvinveroy +- **fix(ci):** publish npm from a hosted runner so provenance is accepted ([#11877](https://github.com/diegosouzapw/OmniRoute/pull/11877)) +- **fix(lint):** pin react hooks plugin to 7.0.1 ([#11886](https://github.com/diegosouzapw/OmniRoute/pull/11886)) +- **fix(ci):** surface and clear release-green ESLint failure ([#11890](https://github.com/diegosouzapw/OmniRoute/pull/11890)) — thanks @MumuTW +- **fix(guardrails):** prevent duplicate prompt-injection-guard log output ([#11936](https://github.com/diegosouzapw/OmniRoute/pull/11936)) — thanks @patrykkopycinski +- **fix(auth):** downgrade expected transient states from warn to debug ([#11937](https://github.com/diegosouzapw/OmniRoute/pull/11937)) — thanks @patrykkopycinski +- **fix(codex):** fail over image generation for imported free plans ([#11948](https://github.com/diegosouzapw/OmniRoute/pull/11948)) — thanks @Bl0ck154 +- **fix(codex):** normalize non-stream responses ([#11951](https://github.com/diegosouzapw/OmniRoute/pull/11951)) — thanks @Bl0ck154 +- **fix(images):** forward Antigravity image size ([#11952](https://github.com/diegosouzapw/OmniRoute/pull/11952)) — thanks @Bl0ck154 +- **fix(db):** invalidate connection cache after upsert ([#11953](https://github.com/diegosouzapw/OmniRoute/pull/11953)) — thanks @Bl0ck154 +- **fix(codex):** restore imported account state ([#11954](https://github.com/diegosouzapw/OmniRoute/pull/11954)) — thanks @Bl0ck154 +- **fix(sse):** give extended-thinking targets the reasoning readiness budget ([#11959](https://github.com/diegosouzapw/OmniRoute/pull/11959)) — thanks @abhisheksharma2411 +- **fix(dashboard):** use opaque background and readable text color on cost chart tooltips ([#11960](https://github.com/diegosouzapw/OmniRoute/pull/11960)) — thanks @ZaimMarzuki +- **fix(antigravity):** send complete loadCodeAssist metadata (ideType/platform/pluginType as numeric enums) ([#11969](https://github.com/diegosouzapw/OmniRoute/pull/11969)) — thanks @rifqiawl +- **fix(core):** resolve DB init race condition and reasoning translation ([#12003](https://github.com/diegosouzapw/OmniRoute/pull/12003)) — thanks @raheemuddin786 +- **fix(admission):** exclude reclaimable page cache from the cgroup pressure ratio ([#12017](https://github.com/diegosouzapw/OmniRoute/pull/12017)) — thanks @HouMinXi +- **fix(build):** prune non-production trees in NFT trace excludes and tsconfig ([#12028](https://github.com/diegosouzapw/OmniRoute/pull/12028)) — thanks @Chewji9875 +- **fix(migrations):** add renamed migration compatibility for 056/073/077/101 ([#12036](https://github.com/diegosouzapw/OmniRoute/pull/12036)) — thanks @oyi77 +- **fix(sse):** default crash-guard logger to console.warn, not console ([#12042](https://github.com/diegosouzapw/OmniRoute/pull/12042)) — thanks @HouMinXi +- **fix(catalog):** derive vision/modalities for built-in auto combos from effective target pool ([#12046](https://github.com/diegosouzapw/OmniRoute/pull/12046)) — thanks @Prajeeth-12 +- **fix(release):** the packaged-app smoke verifies the database opened, not a driver line the primary path never prints (twin of #12032) ([#12047](https://github.com/diegosouzapw/OmniRoute/pull/12047)) +- **fix(barrel):** migrate src/app/ off the @/lib/localDb barrel import (#11795 Phase 2) ([#12051](https://github.com/diegosouzapw/OmniRoute/pull/12051)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate src/lib/ off the localDb barrel to direct db imports (#59) ([#12052](https://github.com/diegosouzapw/OmniRoute/pull/12052)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate open-sse, src/shared, src/sse, src/models, src/domain off the @/lib/localDb barrel import (#11795 Phase 4) ([#12053](https://github.com/diegosouzapw/OmniRoute/pull/12053)) — thanks @jonlwheat2-gif +- **fix(barrel):** delete the @/lib/localDb barrel — every consumer migrated (#11795 Phase 5) ([#12055](https://github.com/diegosouzapw/OmniRoute/pull/12055)) — thanks @jonlwheat2-gif +- **fix(radar):** restore D12 public boundary ([#12057](https://github.com/diegosouzapw/OmniRoute/pull/12057)) +- **fix(dev):** bound webpack and Tailwind scans ([#12075](https://github.com/diegosouzapw/OmniRoute/pull/12075)) — thanks @backryun +- **fix(dev):** isolate root layout settings reads ([#12076](https://github.com/diegosouzapw/OmniRoute/pull/12076)) — thanks @backryun +- **fix(dev):** reduce instrumentation executor fan-out (phase 3) ([#12078](https://github.com/diegosouzapw/OmniRoute/pull/12078)) — thanks @backryun +- **fix(dev):** make logging resources HMR-singleton ([#12079](https://github.com/diegosouzapw/OmniRoute/pull/12079)) — thanks @backryun +- **fix(sse):** keep unavailable forced connections scoped ([#12080](https://github.com/diegosouzapw/OmniRoute/pull/12080)) — thanks @keeltrace +- **fix(dev):** isolate batch dispatch from instrumentation ([#12081](https://github.com/diegosouzapw/OmniRoute/pull/12081)) — thanks @backryun +- **fix(ci):** reconcile release test contract drift ([#12082](https://github.com/diegosouzapw/OmniRoute/pull/12082)) — thanks @backryun +- **fix(ci):** accept CVE-2025-68121 in the prebuilt tls-client .so, auto-close base-red issues, guard Scorecard on the default branch ([#12085](https://github.com/diegosouzapw/OmniRoute/pull/12085)) +- **fix(db):** drop three consumer-less 1proxy exports — dead-code base-red on release/v3.8.51 after the barrel deletion (#12055) ([#12087](https://github.com/diegosouzapw/OmniRoute/pull/12087)) +- **fix(api):** clear the six API-route TypeScript regressions the new gate landed red on ([#12094](https://github.com/diegosouzapw/OmniRoute/pull/12094)) +- **fix(release):** never let the tag-push Create Release append auto notes to the curated body ([#12096](https://github.com/diegosouzapw/OmniRoute/pull/12096)) +- **fix(sse):** honor CLIProxyAPI environment API key ([#12099](https://github.com/diegosouzapw/OmniRoute/pull/12099)) — thanks @RaviTharuma +- **fix(oauth):** bind Google refresh to the client that issued the token ([#12106](https://github.com/diegosouzapw/OmniRoute/pull/12106)) — thanks @HouMinXi +- **fix(dashboard):** make RequestLoggerDetail loadable outside Next — CSS via globals.css + CJS/ESM interop (#11703 base-reds) ([#12114](https://github.com/diegosouzapw/OmniRoute/pull/12114)) +- **fix(leases):** project status lease row to lease columns so joined connection PII never escapes ([#12115](https://github.com/diegosouzapw/OmniRoute/pull/12115)) — thanks @geek007git +- **fix(plugins):** do not kill the plugin process when a fire-and-forget hook times out ([#12116](https://github.com/diegosouzapw/OmniRoute/pull/12116)) — thanks @geek007git +- **fix(guardrails):** restore injection-guard logging on middleware-only routes ([#12117](https://github.com/diegosouzapw/OmniRoute/pull/12117)) — thanks @geek007git +- **fix(plugins):** refresh stored manifest from disk on activate so new hook fields reach existing installs ([#12120](https://github.com/diegosouzapw/OmniRoute/pull/12120)) — thanks @geek007git +- **fix(codex):** preserve existing provider state when bulk-import upserts a matching connection ([#12122](https://github.com/diegosouzapw/OmniRoute/pull/12122)) — thanks @geek007git +- **fix(ci):** clear the two base-reds the 2026-08-30 merge batch left on release/v3.8.51 (round 3) ([#12123](https://github.com/diegosouzapw/OmniRoute/pull/12123)) +- **fix(sse):** strip type:'custom' from Claude tools on agentrouter dispatch ([#12126](https://github.com/diegosouzapw/OmniRoute/pull/12126)) — thanks @wahidsadik371-coder +- **fix(sse):** stop advertising video providers the dispatcher cannot run ([#12131](https://github.com/diegosouzapw/OmniRoute/pull/12131)) — thanks @quiterunner-commits +- **fix(search):** treat HTTP 432 and plan limit errors as transient cooldown ([#12139](https://github.com/diegosouzapw/OmniRoute/pull/12139)) — thanks @HouMinXi +- **fix(sse):** refuse an AI Horde queue that cannot fit the request budget ([#12143](https://github.com/diegosouzapw/OmniRoute/pull/12143)) — thanks @quiterunner-commits +- **fix(ci):** clear the base-reds the afternoon merge batch left on release/v3.8.51 (round 5: provider count 352, TS2554/TS2677) ([#12144](https://github.com/diegosouzapw/OmniRoute/pull/12144)) +- **fix(translator):** strip neutral tool_choice when tools absent in Responses-to-Chat (#12141) ([#12166](https://github.com/diegosouzapw/OmniRoute/pull/12166)) — thanks @HouMinXi +- **fix(guardrails):** pass providerId to getResolvedModelCapabilities in checkComboVision (#12112) ([#12169](https://github.com/diegosouzapw/OmniRoute/pull/12169)) — thanks @HouMinXi +- **fix(translator):** strip plaintext reasoning content for opaque responses backends (#12128) ([#12171](https://github.com/diegosouzapw/OmniRoute/pull/12171)) — thanks @HouMinXi +- **perf(stream):** compile hot-path regexes once, bound token caches, fix quadratic buffering ([#12179](https://github.com/diegosouzapw/OmniRoute/pull/12179)) — thanks @opensource-elearning +- **fix:** resolve compression worker file using runtime anchors instead of… ([#12183](https://github.com/diegosouzapw/OmniRoute/pull/12183)) — thanks @NightStalker-87 +- **fix:** prevent Claude Code session kills via liveness-aware readiness + auto model echo ([#12189](https://github.com/diegosouzapw/OmniRoute/pull/12189)) — thanks @opensource-elearning +- **fix(translator):** preserve falsy primitive values in Gemini and Antigravity function response results ([#12191](https://github.com/diegosouzapw/OmniRoute/pull/12191)) — thanks @Siva010 +- **fix(oauth):** mark empty Antigravity projectId as degraded and clear stale errors (#11284) ([#12205](https://github.com/diegosouzapw/OmniRoute/pull/12205)) — thanks @HouMinXi +- **fix(providers):** gate the Codex auto-ping usage read on the shared quota throttle ([#12209](https://github.com/diegosouzapw/OmniRoute/pull/12209)) — thanks @abhisheksharma2411 +- **fix(combos):** prioritize SQLite row id over inner JSON id and notify delete errors ([#12213](https://github.com/diegosouzapw/OmniRoute/pull/12213)) — thanks @HouMinXi +- **fix(providers):** mark featherless.ai as no longer having a free tier (`hasFree: false`) ([#12216](https://github.com/diegosouzapw/OmniRoute/pull/12216)) — thanks @turbolego +- **fix(deepseek-web):** stop Turbopack dev panic in the PoW worker path resolver ([#12221](https://github.com/diegosouzapw/OmniRoute/pull/12221)) — thanks @hartmark +- **fix(chat-admission):** clarify local 503 source ([#12223](https://github.com/diegosouzapw/OmniRoute/pull/12223)) — thanks @keeltrace +- **fix(dev):** silence webpack runtime module warnings ([#12228](https://github.com/diegosouzapw/OmniRoute/pull/12228)) — thanks @backryun +- **fix(db):** use module.require for CommonJS runtime driver loading ([#12230](https://github.com/diegosouzapw/OmniRoute/pull/12230)) — thanks @raheemuddin786 +- **fix(memory):** honest probe-driven FTS5 keyword status + memory_id rowid sync ([#12231](https://github.com/diegosouzapw/OmniRoute/pull/12231)) — thanks @raheemuddin786 +- **fix(system):** propagate abort signal to stream reader in HTTP version checks ([#12232](https://github.com/diegosouzapw/OmniRoute/pull/12232)) — thanks @raheemuddin786 +- **fix(resilience):** isolate local host execution errors from provider circuit breakers ([#12233](https://github.com/diegosouzapw/OmniRoute/pull/12233)) — thanks @raheemuddin786 +- **fix(sse):** sort injected tools deterministically for prompt caching ([#12234](https://github.com/diegosouzapw/OmniRoute/pull/12234)) — thanks @raheemuddin786 +- **fix(combo):** always clear the loop-safety timer, not just on the happy path (#11804) ([#12245](https://github.com/diegosouzapw/OmniRoute/pull/12245)) +- **fix(quality):** base-red pair — stryker tap registration + turn-pin suites aligned to the window gate ([#12255](https://github.com/diegosouzapw/OmniRoute/pull/12255)) — thanks @backryun +- **fix(dev):** qualify Turbopack runtime boundaries ([#12258](https://github.com/diegosouzapw/OmniRoute/pull/12258)) — thanks @backryun +- **fix(combo):** fall back to full pool when collapsed sole survivor is context-too-small ([#12278](https://github.com/diegosouzapw/OmniRoute/pull/12278)) — thanks @opensource-elearning +- **fix(combo):** bound the pre-dispatch unavailable skip so a stale label cannot dark a pool (#12168) ([#12285](https://github.com/diegosouzapw/OmniRoute/pull/12285)) +- **fix(security):** strip Qwen/Alibaba console-session cookies from provider API responses ([#12287](https://github.com/diegosouzapw/OmniRoute/pull/12287)) +- **fix(usage):** console-aware Token Plan guidance + subscription hint on bailian 401 ([#12288](https://github.com/diegosouzapw/OmniRoute/pull/12288)) +- **fix(resourcePressure):** log numeric detail on every rejection, recover faster ([#12293](https://github.com/diegosouzapw/OmniRoute/pull/12293)) — thanks @hartmark +- **fix(release):** drain the 2026-09-01 base-red window — passthrough usage regression + radarPage i18n keys ([#12327](https://github.com/diegosouzapw/OmniRoute/pull/12327)) +- **fix(ci):** openapi-security-tiers checker must honor routeGuard patterns + imported prefixes ([#12350](https://github.com/diegosouzapw/OmniRoute/pull/12350)) — thanks @ggiak +- **perf(ci):** cache node_modules in the npm-ci-retry composite (#8084 D3) ([#12408](https://github.com/diegosouzapw/OmniRoute/pull/12408)) +- **revert(ui):** point CTAs back at their real destinations ([#12410](https://github.com/diegosouzapw/OmniRoute/pull/12410)) +- **fix(release):** drain the 2026-09-02 base-red — rerank-providers import + api-typecheck baseline ratchet ([#12414](https://github.com/diegosouzapw/OmniRoute/pull/12414)) +- **fix(types):** make system prompt injection noImplicitAny-safe ([#12416](https://github.com/diegosouzapw/OmniRoute/pull/12416)) +- **fix(quality):** validate typecheck baseline schema ([#12419](https://github.com/diegosouzapw/OmniRoute/pull/12419)) +- **fix(memory):** resolve rerank provider node cache import ([#12421](https://github.com/diegosouzapw/OmniRoute/pull/12421)) +- **fix(ci):** repair release-root regressions — pack dedup, web-session syntax, uc-image ids ([#12423](https://github.com/diegosouzapw/OmniRoute/pull/12423)) — thanks @backryun +- **fix(providers):** separate MaxAI and UC credential contracts ([#12431](https://github.com/diegosouzapw/OmniRoute/pull/12431)) +- **fix(cli):** preserve Claude settings on config set ([#12432](https://github.com/diegosouzapw/OmniRoute/pull/12432)) — thanks @KrzysiekSko +- **fix(providers):** repair the maxai credential block truncated by merge auto-resolve ([#12433](https://github.com/diegosouzapw/OmniRoute/pull/12433)) +- **fix(quality):** record the 2026-09-02 merged growth in the file-size baseline ([#12434](https://github.com/diegosouzapw/OmniRoute/pull/12434)) +- **fix(uc):** route UC error strings through sanitizeErrorMessage; allowlist the retired codex id ([#12437](https://github.com/diegosouzapw/OmniRoute/pull/12437)) +- **fix(adapta):** redact streamed upstream errors ([#12438](https://github.com/diegosouzapw/OmniRoute/pull/12438)) +- **fix(sse):** remap non-contiguous upstream tool_calls index to a gap-free output_index ([#12445](https://github.com/diegosouzapw/OmniRoute/pull/12445)) — thanks @hartmark +- **fix(quality):** detect duplicate tool_calls entries in one response ([#12446](https://github.com/diegosouzapw/OmniRoute/pull/12446)) — thanks @hartmark +- **fix(conversations):** resolve turn content OmniRoute never sends back to the client ([#12447](https://github.com/diegosouzapw/OmniRoute/pull/12447)) — thanks @hartmark +- **fix(combo):** do not treat credits-exhausted 401 as auth skip ([#12449](https://github.com/diegosouzapw/OmniRoute/pull/12449)) — thanks @RaviTharuma +- **fix(auth):** do not park healthy quota accounts as expired ([#12452](https://github.com/diegosouzapw/OmniRoute/pull/12452)) — thanks @RaviTharuma +- **fix(responses-continuation):** fail closed on a collector-truncated, empty output array ([#12460](https://github.com/diegosouzapw/OmniRoute/pull/12460)) — thanks @hartmark +- **fix(logging):** raise the SSE payload collector's default cap ([#12461](https://github.com/diegosouzapw/OmniRoute/pull/12461)) — thanks @hartmark +- **fix(resilience):** surface Responses failed.error.message in 502s ([#12472](https://github.com/diegosouzapw/OmniRoute/pull/12472)) — thanks @RaviTharuma +- **fix(security):** unbiased maxai X-Random nonce + stricter URL/regex assertions ([#12502](https://github.com/diegosouzapw/OmniRoute/pull/12502)) +- **fix(video):** re-anchor transcript log-redaction so PII/credential maskers can't reopen the leak (#12150 P1 follow-up) ([#12503](https://github.com/diegosouzapw/OmniRoute/pull/12503)) +- **fix(monitoring):** serve cached credentialHealth off the request path ([#12533](https://github.com/diegosouzapw/OmniRoute/pull/12533)) — thanks @RaviTharuma +- **fix(quota):** keep Antigravity Gemini usable when Claude weekly is empty ([#12566](https://github.com/diegosouzapw/OmniRoute/pull/12566)) — thanks @HouMinXi +- **fix(authz):** hard-gate every credential export and CLI-config write (GHSA-5926-2w35-7h4q) ([#12600](https://github.com/diegosouzapw/OmniRoute/pull/12600)) +- **fix(docs):** restore the Next build — REMOVED_PROVIDERS.md had no frontmatter (base-red #12581) ([#12610](https://github.com/diegosouzapw/OmniRoute/pull/12610)) +- **fix(quality):** re-point the zcodeProtocol public-creds allowlist to line 313 ([#12615](https://github.com/diegosouzapw/OmniRoute/pull/12615)) +- **fix(security):** close 3 advisories — search baseUrl exfil, sk- in the error sanitizer, bifrost relay header leak ([#12620](https://github.com/diegosouzapw/OmniRoute/pull/12620)) +- **fix(providers):** sanitize boolean required and nested bare maps for Gemini (#12269) ([#12624](https://github.com/diegosouzapw/OmniRoute/pull/12624)) — thanks @HouMinXi +- **fix(resilience):** keep Overloaded STREAM_EARLY_EOF off the provider breaker ([#12626](https://github.com/diegosouzapw/OmniRoute/pull/12626)) — thanks @HouMinXi +- **fix(catalog):** re-audit free-tier quotas against official pages ([#12649](https://github.com/diegosouzapw/OmniRoute/pull/12649)) +- **fix(dashboard):** keep a request's pending-tracking id stable across combo target retries ([#12650](https://github.com/diegosouzapw/OmniRoute/pull/12650)) — thanks @hartmark +- **fix(tests):** retire dead model ids from the chat-pipeline integration suite (base-red #12581) ([#12670](https://github.com/diegosouzapw/OmniRoute/pull/12670)) +- **fix:** resolve SqliteError no such table compression_run_telemetry during cleanup ([#12682](https://github.com/diegosouzapw/OmniRoute/pull/12682)) — thanks @groovecityJO +- **fix(combo):** treat a pin-only step as implicit connection allowlist ([#12697](https://github.com/diegosouzapw/OmniRoute/pull/12697)) — thanks @HouMinXi +- **fix(ci):** ship .npmrc in published package so legacy-peer-deps applies to consumers (#11544) ([#12699](https://github.com/diegosouzapw/OmniRoute/pull/12699)) — thanks @KooshaPari +- **fix(docker):** pin 4 CLI tools to exact versions (#12576) ([#12703](https://github.com/diegosouzapw/OmniRoute/pull/12703)) — thanks @KooshaPari +- **fix(docker):** document and harden cli profile trust boundary (#12570) ([#12706](https://github.com/diegosouzapw/OmniRoute/pull/12706)) — thanks @KooshaPari +- **fix(sse):** redact video transcript in pre-guardrail rejected-request logs (#12150 P2 item 7) ([#12710](https://github.com/diegosouzapw/OmniRoute/pull/12710)) +- **fix(i18n):** quote placeholder in OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES description (#12505) ([#12769](https://github.com/diegosouzapw/OmniRoute/pull/12769)) — thanks @KooshaPari +- **fix(api/pricing):** surface validation error message as string, not raw object (#12494) ([#12771](https://github.com/diegosouzapw/OmniRoute/pull/12771)) — thanks @KooshaPari +- **fix(sse):** re-enable prompt compression for native Codex passthrough ([#12834](https://github.com/diegosouzapw/OmniRoute/pull/12834)) — thanks @thomasmaerz +- **fix(dashboard):** model health tests for a provider node set to the Responses API now call `/v1/responses` with a Responses-shaped body instead of `/v1/chat/completions` — those models were reported as `Provider returned HTTP 200 but no text content` even though the same model answered normally through `/v1/responses` ([#13070](https://github.com/diegosouzapw/OmniRoute/issues/13070)) ([#13087](https://github.com/diegosouzapw/OmniRoute/pull/13087)) — thanks @ntdatt812 +- fix(routing): stop a round-robin combo's "opencode" targets from collapsing onto the opencode-zen connection (#11912) ([#13283](https://github.com/diegosouzapw/OmniRoute/pull/13283)) +- fix(routing): stop the reactive-compaction debug log from lying when compression is globally disabled (#11977) ([#13276](https://github.com/diegosouzapw/OmniRoute/pull/13276)) +- fix(electron): relativize standalone-bundle symlink targets so Stage 8 manifest verification stops failing on Windows (#11979) ([#13251](https://github.com/diegosouzapw/OmniRoute/pull/13251)) +- fix(dashboard): surface a visible error when Compression Studio's combined preview run fails (#12061) ([#13288](https://github.com/diegosouzapw/OmniRoute/pull/13288)) +- fix(dashboard): make the compression "Effective pipeline" preview honor the active profile and warn when the master switch is off (#12063) ([#13287](https://github.com/diegosouzapw/OmniRoute/pull/13287)) +- fix(providers): scope TinyCMS Web signer's DOM shims to each call instead of leaking them for the process lifetime, and surface a clean HTTP status on a non-JSON interception-toggles error (#12072) ([#13254](https://github.com/diegosouzapw/OmniRoute/pull/13254)) +- fix(guardrails): stop Vision Bridge from re-selecting a model locked after a 404 (#12111) ([#13259](https://github.com/diegosouzapw/OmniRoute/pull/13259)) +- fix(sse): require Responses-shaped body before native OpenAI-compatible passthrough (#12129) ([#13278](https://github.com/diegosouzapw/OmniRoute/pull/13278)) +- fix(providers): minimax-m3 now collapses manual thinking.type:"enabled" to adaptive, preventing upstream 400 (2013) (#12132) ([#13249](https://github.com/diegosouzapw/OmniRoute/pull/13249)) +- fix(db): scope model visibility overrides by modality so hiding a Chat model no longer hides an identically-ID'd Image/Embeddings/etc. model (#12172) ([#13258](https://github.com/diegosouzapw/OmniRoute/pull/13258)) +- fix(db): scope local-provider apiKey dedup to matching base URL so LM Studio/Ollama-style connections support multiple accounts (#12173) ([#13250](https://github.com/diegosouzapw/OmniRoute/pull/13250)) +- fix(providers): refresh Trae's stale Referer/Origin and forward user timezone so imported connections stop failing with 401 (#12190) ([#13255](https://github.com/diegosouzapw/OmniRoute/pull/13255)) +- fix(providers): route opencode-go/gpt-5.6-luna to /responses instead of /chat/completions (#12196) ([#13275](https://github.com/diegosouzapw/OmniRoute/pull/13275)) +- fix(dashboard): allow deleting the last extra-upstream-header row even when invalid (#12251) ([#13252](https://github.com/diegosouzapw/OmniRoute/pull/13252)) +- **fix(i18n):** translate pre-existing `__MISSING__:` keys for `combo.sort`, `requestLogger.detail` expand/collapse, `common.profile`, and `settings.resilienceCredentialHealth*` across 39 locales ([#12272](https://github.com/diegosouzapw/OmniRoute/issues/12272)) ([#13034](https://github.com/diegosouzapw/OmniRoute/pull/13034)) — thanks @HouMinXi +- fix(cli): run the Node.js runtime compatibility guard before the heavy `tsx/esm` + Commander import chain so an unsupported runtime gets a clear message instead of a raw `Invalid regular expression flags` crash (#12296) ([#13253](https://github.com/diegosouzapw/OmniRoute/pull/13253)) +- fix(dashboard): refresh the providers list after deleting a compatible provider node (#12298) ([#13256](https://github.com/diegosouzapw/OmniRoute/pull/13256)) +- fix(usage): fail closed on API-key budget enforcement when a provider's `auto` routing alias has no pricing row, instead of silently counting it as $0 (#12341) ([#13257](https://github.com/diegosouzapw/OmniRoute/pull/13257)) +- **fix(providers):** include the submitted Agnes video model when polling by `video_id` ([#12356](https://github.com/diegosouzapw/OmniRoute/pull/12356)) — thanks @pacocartones +- **fix(routing):** custom OpenAI-compatible nodes now honor the saved Chat/Responses API type after edits instead of letting the node's original ID prefix override the live connection setting ([#11884](https://github.com/diegosouzapw/OmniRoute/issues/11884)). ([#12358](https://github.com/diegosouzapw/OmniRoute/pull/12358)) — thanks @pacocartones +- fix(sse): surface an error instead of a silent empty 200 when a Claude stream closes with zero bytes (#12398) ([#13285](https://github.com/diegosouzapw/OmniRoute/pull/13285)) +- fix(oauth): warn before the dead localhost:8080 redirect in antigravity/gemini `oauth start` (#12413) ([#13265](https://github.com/diegosouzapw/OmniRoute/pull/13265)) +- fix(providers): stop devin-cli spawn error from double-closing the SSE controller (#12517) ([#13261](https://github.com/diegosouzapw/OmniRoute/pull/13261)) +- **fix(rerank):** clamp Voyage `top_k` to the documents actually sent after empty-string filtering, and honor `return_documents: false` in the NVIDIA response adapter (#12523 — thanks @pacocartones) +- **fix(audio):** `/v1/audio/translations` now resolves combo names the way `/v1/audio/transcriptions` already does, so a combo that `GET /v1/models` advertises is fanned out to its targets instead of being rejected with `400 Invalid translation model: . Use format: provider/model`; literal `provider/model` ids and unknown bare names behave as before (#12536 — thanks @pacocartones) +- **fix(gemini):** strip the JSON-Schema-2020-12 `prefixItems` keyword from Gemini tool schemas at every nesting level, so Claude Code tool definitions no longer fail with `400 Unknown name "prefixItems"` on Gemini models (#12540 — thanks @pacocartones) +- **fix(api-manager):** Expose an accessible loading status while API keys are fetched instead of an empty accessibility tree (#12541 — thanks @pacocartones) +- **fix(video):** Clamp `estimateJpegFrameBytes` at zero for padding-only payloads and build the three encode-side frame data URIs from `JPEG_FRAME_DATA_URI_PREFIX` instead of a repeated literal (#12543 — thanks @pacocartones) +- **fix(devin):** accept Windows `DEVIN_AGENTIC_HOME` sandbox paths (`C:\...\.sandbox\...`) in the isolated-home check so the Devin Claude Bridge no longer fails closed on Windows ([#12405](https://github.com/diegosouzapw/OmniRoute/issues/12405)) (#12545 — thanks @pacocartones) +- **fix(db):** Add `conversation_turn_nodes` and orphaned `agentic_conversations` to the auto-cleanup cycle under the existing `retention.callLogs` window, so identity nodes whose call-log content has already been purged no longer accumulate without bound in `storage.sqlite` (#12548 — thanks @pacocartones) +- **fix(i18n):** Wrap the `~/.claude/profiles//settings.json` placeholder in ICU single quotes in the `OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES` feature-flag description across all 42 locales and the TypeScript default, so next-intl no longer fails with `INVALID_MESSAGE: UNCLOSED_TAG` and the Feature Flags card shows the description instead of the raw key (#12549 — thanks @pacocartones) +- **fix(orchestration):** `updateCloudAgentTask` now publishes the task's real `status` on `agent.task.updated` when an update only touches `result`, `activities` or `error`, instead of the fabricated `"updated"` state, and stays silent when no row matched the id (#12550 — thanks @pacocartones) +- **fix(i18n):** the home "Recent Requests" panel and the Provider Topology legend are now translated instead of rendering English copies on non-English dashboards; the legend reads its own `home.topologyLegend*` labels with consistent casing rather than borrowing the memory-settings "Recent" and analytics "Error" strings (#12551 — thanks @pacocartones). +- **docs(reference):** bring the `FEATURE_FLAGS.md` catalog back to 1:1 with `featureFlagDefinitions.ts` — 20 missing flags added, the two `*_BLOCK_THRESHOLD` env-only knobs moved out of the flag tables, category/total counts and the Live WS port corrected, guarded by a static test (#12552 — thanks @pacocartones) +- **fix(i18n):** backfill missing `usage.kiloPass*` strings in 39 locales and restore `featureFlagOmnirouteDisableThinkingLevelVariantsDescription` in `pt.json` ([#12561](https://github.com/diegosouzapw/OmniRoute/issues/12561)) ([#13027](https://github.com/diegosouzapw/OmniRoute/pull/13027)) — thanks @HouMinXi +- fix(docker): default docker-compose app ports (dashboard/API/live-WS) to loopback instead of `0.0.0.0`, closing the anonymous `/v1` LAN/WAN exposure gap left open by `REQUIRE_API_KEY=false` (#12568) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): close DNS-rebinding SSRF gap in webhook outbound-URL guard (#12569) ([#13243](https://github.com/diegosouzapw/OmniRoute/pull/13243)) +- fix(docker): require a per-session token on the VNC browser CDP bridge and isolate it on a dedicated Docker network (#12571) ([#13241](https://github.com/diegosouzapw/OmniRoute/pull/13241)) +- fix(open-sse): write Adobe Firefly session tokens and cookie jars with 0700/0600 permissions instead of the process umask (#12572) ([#13242](https://github.com/diegosouzapw/OmniRoute/pull/13242)) +- fix(api): remove hardcoded wildcard CORS in openai-to-gemini-sse.ts so the centralized fail-closed CORS gate is the sole source of `Access-Control-Allow-Origin` (#12573) ([#13239](https://github.com/diegosouzapw/OmniRoute/pull/13239)) +- fix(api): enforce API key policy (budget/rate-limit/schedule/endpoint scoping) on the ElevenLabs speech-to-text, text-to-speech and voices proxy routes (#12574) ([#13244](https://github.com/diegosouzapw/OmniRoute/pull/13244)) +- fix(sse): cap HuggingChat NDJSON body size and bound the read loop with the fetch timeout so a stalled or hostile upstream cannot buffer unbounded memory (#12577) ([#13282](https://github.com/diegosouzapw/OmniRoute/pull/13282)) +- fix(docker): scope the cliproxyapi/qdrant/bifrost sidecars to loopback by default and forward `CLIPROXYAPI_MANAGEMENT_KEY` into the cliproxyapi container so its management API is not left both unauthenticated and LAN-published (#12578) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): create DB export temp paths with `fs.mkdtempSync` instead of predictable timestamps (#12579) ([#13237](https://github.com/diegosouzapw/OmniRoute/pull/13237)) +- Cline 401 bodies that say "re-authenticate your Cline account" classify as a refreshable OAuth token, not a terminal expired key. The cooling panel no longer labels every cooldown as a 429; it shows the recorded last error instead. (#12594) ([#13060](https://github.com/diegosouzapw/OmniRoute/pull/13060)) — thanks @HouMinXi +- **fix(catalog):** degrade unknown combo targets instead of dropping LCD modalities, and persist OpenRouter `architecture.input_modalities` into the capability snapshot ([#12613](https://github.com/diegosouzapw/OmniRoute/issues/12613)) ([#13042](https://github.com/diegosouzapw/OmniRoute/pull/13042)) — thanks @HouMinXi +- fix(providers): send `x-api-key` instead of `Authorization: Bearer` for OpenCode Zen's `/v1/responses` endpoint (Muse Spark Contributor models), fixing a 401 on OmniRoute's auth header (#12633) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(sse): surface the actionable "Auggie CLI not found" message when the shell reports a missing `auggie` binary via exit code instead of a spawn error (#12645) ([#13263](https://github.com/diegosouzapw/OmniRoute/pull/13263)) +- **fix(config):** Persist deletions of built-in background-degradation entries — when a stored settings record exists its `degradationMap` is now authoritative instead of being merged under the defaults, so an entry the user removed in the dashboard no longer reappears on the next apply or restart ([#12424](https://github.com/diegosouzapw/OmniRoute/issues/12424)) ([#12647](https://github.com/diegosouzapw/OmniRoute/pull/12647)) — thanks @pacocartones +- **fix(gamification):** action-count badge milestones (First Token, Token Consumer, Token Machine, Token Whale, and the token-sharing tier) are now backed by a durable `xp_action_counts` counter incremented in `addXp()`, instead of a live `COUNT(*)` over `xp_audit_log`. The audit log is pruned by `retention.xpAuditLog` (default 30 days), so on a default install those "lifetime" milestones were really "actions in the last 30 days" and unlocked badges could stop unlocking once old rows aged out. `getActionCount()` and `checkActionCountBadges()` now read the same durable source, and a migration backfills existing totals from the surviving audit rows ([#12546](https://github.com/diegosouzapw/OmniRoute/issues/12546)) ([#12651](https://github.com/diegosouzapw/OmniRoute/pull/12651)) — thanks @pacocartones +- **fix(images):** `/v1/images/edits` now iterates a combo's targets the same way `/v1/images/generations` does (#9239) instead of flattening a bare combo to its first target. A combo whose first target is not edit-capable — or lacks credentials — now falls through to a later edit-capable target rather than hard-erroring, and missing credentials are skipped (not a hard `401`) to match the generations path. The per-target skip/terminal classification is extracted into a shared `runImageComboTargets` loop, so generations behavior is unchanged ([#12547](https://github.com/diegosouzapw/OmniRoute/issues/12547)). ([#12653](https://github.com/diegosouzapw/OmniRoute/pull/12653)) — thanks @pacocartones +- fix(sse): add first-byte watchdog to the TLS-fingerprint transport so a stalled wreq body falls back instead of hanging for minutes (#12656) ([#13272](https://github.com/diegosouzapw/OmniRoute/pull/13272)) +- fix(sse): exempt tiny-budget reasoning probes from combo quality failure and surface persisted-cooldown skips in ALL_TARGETS_SKIPPED diagnostics (#12659) ([#13269](https://github.com/diegosouzapw/OmniRoute/pull/13269)) +- fix(models): declare the real ~1M contextLength for OpenCode Zen's Muse Spark 1.2 models instead of falling back to the 200000 provider default (#12681) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(oauth): align codebuddy-cn OAuth User-Agent with the chat/usage CLI version to avoid WAF false positives (#12702) ([#13264](https://github.com/diegosouzapw/OmniRoute/pull/13264)) +- fix(dashboard): surface an authentication-required banner instead of silently blanking database settings for a guest session (#12709) ([#13270](https://github.com/diegosouzapw/OmniRoute/pull/13270)) +- **fix(resilience):** stop unbounded queue that hangs 6min until Aborted — gate, provider slot, and Bottleneck queue now share a per-connection `maxWaitMs` budget; `fail-closed` on exhaust (503); execution backstop `executionMaxWaitMs` overridable per connection with upstream clamp ([#12715](https://github.com/diegosouzapw/OmniRoute/pull/12715)) — thanks @maxmad64bis (with thanks to @Tushar49 for surfacing the slow-provider need in #12635) +- **fix(auto-combo):** every mode pack now carries `quality` and `reliability` weights (`quality` 0.02, 0.03 in `quality-first`; `reliability` 0.03, 0.04 in `reliability-first`), so selecting a pack no longer silences either signal; `DEFAULT_WEIGHTS` is unchanged ([#12731](https://github.com/diegosouzapw/OmniRoute/pull/12731)) — thanks @maxmad64bis +- **fix(sse):** the provider execution pipeline keeps the upstream error `code`/`type` again — both were lost when the non-streaming leg was extracted, so a config-class `422` (Antigravity missing project) degraded into a generic account cooldown instead of reaching the gate that recognises it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(db):** a legacy `call_logs` table no longer aborts startup — the provider-stats index is created after the column healing runs, not before it, so an install predating `request_type` opens instead of failing with `no such column` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(catalog):** a malformed row in the operator-writable custom-models blob no longer kills every `auto/*` pool ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(tests):** realign the guards that the same day's merges left asserting the old behavior — the provider-count total after GreenPT and EURouter, the GLM stream buffer slot that `#12925` turned into a declared parameter, and the injection scan bound that `#13104` reshaped from a head-only window into head-plus-tail ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- fix(cache): fold tool_choice/tools/response_format into the semantic cache signature so a cached tool_calls response can no longer be replayed for a request whose tool policy forbids it (#12734) ([#13267](https://github.com/diegosouzapw/OmniRoute/pull/13267)) +- **fix(models):** stop treating provider-supplied `isFree:true`/`:free`/`0/0` flags as trusted unless the provider has a documented free tier — only the shipped free-tier catalog decides otherwise; fetched `isFree:true` trusted only for a free-tier provider, custom `isFree:true` via a trusted path ([#12744](https://github.com/diegosouzapw/OmniRoute/pull/12744)) — thanks @maxmad64bis +- fix(memory): authenticate the internal /v1/rerank loopback call so memory reranking no longer silently degrades to unranked order when REQUIRE_API_KEY=true (#12745) ([#13268](https://github.com/diegosouzapw/OmniRoute/pull/13268)) +- fix(sse): parse Ollama Cloud's current usage markup (`$X of $Y used` aria-label, nested width style) (#12749) ([#13260](https://github.com/diegosouzapw/OmniRoute/pull/13260)) +- fix(cli): setup-opencode no longer sends an active context's management token to `/v1/models` when `--api-key`/`OMNIROUTE_API_KEY` is supplied — an explicit flag or the env var now always outranks the context's token, and the flag itself is no longer swallowed by the parent program's global `--api-key` option (#12783) ([#13246](https://github.com/diegosouzapw/OmniRoute/pull/13246)) +- fix(sse): register Arcee AI in the executor provider registry so requests reach api.arcee.ai instead of silently falling back to OpenAI (#12784) ([#13277](https://github.com/diegosouzapw/OmniRoute/pull/13277)) +- **fix(quota):** share one upstream quota read when identical saturation checks arrive at the same time, so traffic bursts don't multiply provider API calls ([#12787](https://github.com/diegosouzapw/OmniRoute/pull/12787)) — thanks @maxmad64bis +- **fix(db):** ignore expired or invalid rate-limit cooldown writes so a stale timestamp can't lock a connection that should be usable — clearing still works as before ([#12788](https://github.com/diegosouzapw/OmniRoute/pull/12788)) — thanks @maxmad64bis +- **fix(providers):** single source for provider order with `xao` ranking alongside `xai-oauth` ([#12790](https://github.com/diegosouzapw/OmniRoute/pull/12790)) — thanks @maxmad64bis +- **fix(routing):** off-table models check the free-model catalog before inheriting premium prices, latency bootstraps from the observed pool median, async tiers read live database pricing with a 90-day freshness gate, and the tier cache invalidates on every pricing write ([#12792](https://github.com/diegosouzapw/OmniRoute/pull/12792)) — thanks @maxmad64bis +- **fix(auto-combo):** snapshot scoring uses the observed per-connection account tier and per-model quality instead of neutral constants, and circuit-open providers rank lower at build time ([#12794](https://github.com/diegosouzapw/OmniRoute/pull/12794)) — thanks @maxmad64bis +- **fix(pool):** empty `auto/*` pools now say why they are empty, and the models listing uses the same paid check as routing ([#12795](https://github.com/diegosouzapw/OmniRoute/pull/12795)) — thanks @maxmad64bis +- fix(routing): recognize CLIProxyAPI's 'unknown provider for model' 400 as fallback-worthy (#12800) ([#13284](https://github.com/diegosouzapw/OmniRoute/pull/13284)) +- **fix(sse):** translate-mode streams now emit the estimated token counts as a trailing usage-only chunk before `[DONE]` when the upstream stays silent, so metered chat clients see totals instead of nothing ([#12828](https://github.com/diegosouzapw/OmniRoute/pull/12828)) — thanks @maxmad64bis +- **fix(db):** provider stats stay truthful on empty and legacy databases: fallback counts default to `0` instead of `null`, latency averages read `null` (not `0`) when no durations were recorded, failures logged before the error-type column existed group under `pre_migration` instead of `unclassified`, and per-provider queries use two new composite indexes ([#12832](https://github.com/diegosouzapw/OmniRoute/pull/12832)) — thanks @maxmad64bis +- fix(nvidia): fail open when a synced model catalog goes stale instead of gating forever (#12849) ([#13248](https://github.com/diegosouzapw/OmniRoute/pull/13248)) +- **fix(db):** WAL maintenance lives in its own module: a `TRUNCATE` checkpoint that hits a busy database now warns with its streak and retries once via `PASSIVE` instead of logging success, and closing-time checkpoints no longer report success on builds without a database file, with the busy totals visible in the authenticated monitoring health payload ([#12853](https://github.com/diegosouzapw/OmniRoute/pull/12853)) — thanks @maxmad64bis +- **fix(health):** quota with no snapshots reads empty instead of a contradicting 0% ([#12857](https://github.com/diegosouzapw/OmniRoute/pull/12857)) — thanks @maxmad64bis +- fix(a2a): accept the dashboard's own session cookie on /a2a so "Run message/send" no longer fails with "Unauthorized: missing or invalid API key" (#12888) ([#13271](https://github.com/diegosouzapw/OmniRoute/pull/13271)) +- **fix(logs):** the Logs grid's in-memory filter pass no longer discards rows the SQL query already matched — selecting an API key from the dropdown (which sends the key's id) returns its calls again, the Combo tab shows every combo instead of only those whose name contains a "1", and the model filter and search cover the same columns as the query ([#12896](https://github.com/diegosouzapw/OmniRoute/pull/12896)) — fixes [#12873](https://github.com/diegosouzapw/OmniRoute/issues/12873) — thanks @ntdatt812 +- **fix(a2a):** `/api/a2a/status` now builds the agent card from the request that asked for it, so a gateway reached at a non-localhost host no longer advertises `http://localhost:20128` as its A2A URL ([#12918](https://github.com/diegosouzapw/OmniRoute/pull/12918)). — thanks @ntdatt812 +- **fix(compression):** progressive aging now appends its `[COMPRESSED:aging:…]` annotation after a turn's `tool_result` blocks instead of in front of them, so Anthropic no longer rejects aged conversations with "`tool_use` ids were found without `tool_result` blocks immediately after" ([#12920](https://github.com/diegosouzapw/OmniRoute/pull/12920)). — thanks @ntdatt812 +- **fix(bedrock):** model import now resolves context limits for every vendor prefix instead of only `anthropic.*`, so `global.openai.gpt-5.6-*` no longer imports with a null `inputTokenLimit` and gets rejected pre-flight at the 200k default ([#12921](https://github.com/diegosouzapw/OmniRoute/pull/12921)). — thanks @ntdatt812 +- **fix(stream):** the 64 KB stream buffer GLM asks for is honoured instead of dropped, and the type error it caused no longer fails the API Route Typecheck gate on every open PR ([#12925](https://github.com/diegosouzapw/OmniRoute/pull/12925)) — thanks @ntdatt812 +- **fix(guardrails):** mask PII inside a `tool_result`'s nested content array, which the masker walked past while redacting its sibling block ([#12930](https://github.com/diegosouzapw/OmniRoute/pull/12930)) — thanks @ntdatt812 +- **fix(dashboard):** the Radar catalog table no longer leaves absent data unexplained — empty limits, unknown context windows, and unreported capabilities each explain themselves on hover, and a new check keeps it that way ([#12937](https://github.com/diegosouzapw/OmniRoute/pull/12937)) — thanks @maxmad64bis +- **fix(sse):** geo-blocked opencode requests rotate to the next account proxy instead of failing, so one refused egress no longer aborts the whole chain ([#12941](https://github.com/diegosouzapw/OmniRoute/pull/12941)) — thanks @maxmad64bis +- **fix(codex):** dashboard "clear cooldown" and the CAS recovery path now drop nested `codexScopeRateLimitedUntil` maps in the same write that nulls `rate_limited_until`, and fresh quota snapshots with headroom lift fallback-sourced scope cooldowns parked by quota preflight ([#12817](https://github.com/diegosouzapw/OmniRoute/issues/12817), [#12860](https://github.com/diegosouzapw/OmniRoute/issues/12860), [#12951](https://github.com/diegosouzapw/OmniRoute/pull/12951)) The dashboard reset-credit button (`consumeCodexResetCredit`) now exercises that same snapshot path after a successful redeem, so a filled quota bar is enough to unpark a leftover fallback Codex child without another manual clear. — thanks @HouMinXi +- **fix(db):** resolve `sql-wasm.wasm` across global npm install and hoisted layouts, ensuring OmniRoute can boot cleanly on Node 24 when native `better-sqlite3` is uncompiled. ([#13035](https://github.com/diegosouzapw/OmniRoute/pull/13035)) — thanks @HouMinXi +- **fix(api):** `DELETE /v1/batches/delete-completed` now sweeps only the calling API key's own completed batches (batches with no owner stay out of a key-scoped sweep on purpose), with an explicit instance-wide mode reserved for authenticated dashboard sessions, a 401 for a presented key that is unknown, revoked, deactivated, banned or expired (never falling through to the session branch), audit logging of every sweep, a sanitized 500 on failure, an owner-scoped file half (a key-scoped sweep never nulls a file another tenant owns) and an atomic sweep — chunked in 200-batch transactions in instance mode — so a mid-way error never leaves a batch pointing at a nulled file (GHSA-wvxc-jp3v-5mg5) ([#12969](https://github.com/diegosouzapw/OmniRoute/pull/12969)) +- **fix(combo):** quota-weighted routing stops drawing on an out-of-credit connection — a 402 now invalidates the stored quota snapshot instead of leaving its stale remaining percentage in place, and a snapshot older than 10 minutes no longer counts as confident headroom for the primary pool ([#12972](https://github.com/diegosouzapw/OmniRoute/pull/12972)) ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- **fix(vision):** Custom Models with "Vision capable" checked no longer have image requests swapped to `glm/glm-4.6v` when the client sends the advertised alias (`vllm/path/...`) or the bare path-shaped id — Vision Bridge now matches the stored override for all three id forms ([#12758](https://github.com/diegosouzapw/OmniRoute/issues/12758)) ([#12974](https://github.com/diegosouzapw/OmniRoute/pull/12974)) — thanks @HouMinXi +- **fix(sse):** transient opencode upstream failures rotate to the next account proxy instead of failing, so one flapping egress no longer aborts the whole chain ([#12975](https://github.com/diegosouzapw/OmniRoute/pull/12975)) — thanks @maxmad64bis +- **fix(azure):** Deployments from GPT-6 onward now send `max_completion_tokens` instead of `max_tokens`, which Azure rejects with HTTP 400. The rule matched a literal `gpt-5`, so each new generation arrived broken; it now matches the generation range, while `gpt-35-turbo` still keeps `max_tokens`. ([#13007](https://github.com/diegosouzapw/OmniRoute/pull/13007)) — thanks @ntdatt812 +- **fix(db):** add an opt-in self-restart circuit for sustained critical memory pressure, gate post-cleanup VACUUM behind a minimum freed-rows threshold, and checkpoint the SQLite WAL every 5 minutes with a size guard that escalates to TRUNCATE, so a growing WAL can no longer stall the event loop into a full outage. ([#13011](https://github.com/diegosouzapw/OmniRoute/pull/13011)) — thanks @HouMinXi +- **fix(auth):** an explicit connection pin may probe a quota-disabled row once and re-enable it on success ([#12874](https://github.com/diegosouzapw/OmniRoute/issues/12874)) ([#13017](https://github.com/diegosouzapw/OmniRoute/pull/13017)) — thanks @HouMinXi +- **fix(api):** thread `X-OmniRoute-Fallback-Attempts` through combo chat completions so streaming and non-streaming responses report how many prior legs were attempted ([#13038](https://github.com/diegosouzapw/OmniRoute/pull/13038)) — thanks @HouMinXi +- Restore provider failure classification and credential refresh on non-streaming requests: classify non-2xx failures to lock models on per-model quota exhaustion, update connection rate limits from headers and body, and pass credential refresh handlers to pipeline execution so 401 tokens can be refreshed and retried (#13043). ([#13069](https://github.com/diegosouzapw/OmniRoute/pull/13069)) — thanks @HouMinXi +- **fix(responses):** wrap forced-non-streaming web_search fallback JSON as Responses SSE so Codex still sees `response.completed` ([#13050](https://github.com/diegosouzapw/OmniRoute/pull/13050)) — thanks @HouMinXi +- **fix(antigravity):** Preserve upstream thought-token usage in normalized completion and reasoning token counts ([#13055](https://github.com/diegosouzapw/OmniRoute/pull/13055)) — thanks @pacocartones +- Fix a concurrency-slot leak in the DeepSeek PoW solver: a worker that failed to spawn (for example a missing worker script) never released its slot, so `MAX_CONCURRENT_WORKERS` failures disabled the solver until restart. ([#13097](https://github.com/diegosouzapw/OmniRoute/pull/13097)) — thanks @anhtahaylove +- **fix(acp):** bound the ACP session output buffers — `stdoutBuffer` and `stderrBuffer` now cap at 1 MiB keeping the most recent output behind a visible `[...output truncated...]` marker, and `stderrBuffer` is reset per prompt instead of accumulating for the lifetime of the session. ([#13100](https://github.com/diegosouzapw/OmniRoute/pull/13100)) — thanks @anhtahaylove +- **fix(acp):** release the `stdout`/`exit` listeners and the idle timer that a `sendPrompt` timeout used to leave attached to the `acpManager` singleton, and drop sessions that exited on their own from the session map instead of keeping them forever. ([#13096](https://github.com/diegosouzapw/OmniRoute/pull/13096)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection and PII scanners now read the text a `tool_result` block carries on `content` (string or nested block list), in messages and in system blocks, so tool output is judged by the same rules as user text ([#13101](https://github.com/diegosouzapw/OmniRoute/pull/13101)) — thanks @ntdatt812 +- **fix(gamification):** close the badge notification SSE stream when the request signal is already aborted before the stream starts — a client that disconnects while the route is still awaiting auth used to leave both the 2s unlock poll and the 15s heartbeat running for the lifetime of the process. ([#13106](https://github.com/diegosouzapw/OmniRoute/pull/13106)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection scan now spends its 16 KB budget on both ends of the request instead of the first 16 KB only, so `system`, `instructions`, `query`, `documents` and the newest turns are no longer hidden behind one long message ([#13104](https://github.com/diegosouzapw/OmniRoute/pull/13104)) — thanks @ntdatt812 +- **fix(dashboard):** expose the Volcano Ark console cookie on quota scraping and unwrap connect-error objects so the dashboard shows the upstream message ([#13107](https://github.com/diegosouzapw/OmniRoute/pull/13107)) — thanks @HouMinXi +- **fix(db):** release the `beforeExit`/`SIGINT`/`SIGTERM` handlers when a `node:sqlite` adapter closes, so a closed adapter and its database handle are no longer pinned to `process` for the lifetime of the run — the same treatment #7494 gave the sql.js adapter. ([#13109](https://github.com/diegosouzapw/OmniRoute/pull/13109)) — thanks @anhtahaylove +- **fix(translator):** `contentSchema` and `unevaluatedItems` are now treated as subschema positions by the tool-schema sanitizer, so a truncation placeholder in either is replaced with a permissive schema instead of being forwarded as a string ([#13110](https://github.com/diegosouzapw/OmniRoute/pull/13110)) — thanks @ntdatt812 +- **fix(cli-helper):** clear the `createLogStream` timeout on the abort path — `stop()` aborts the in-flight fetch and returned through the `signal.aborted` branch, which skipped `clearTimeout` and left an armed timer per stopped stream. The stream reader is now also cancelled when the read loop exits early. ([#13114](https://github.com/diegosouzapw/OmniRoute/pull/13114)) — thanks @anhtahaylove +- **fix(docs):** drop TLS-impersonation, MITM-decrypt, supply-chain attestation, and XOR-mask writeups from the public `/docs` catalog and Docker image. Files stay in git for engineers; operators who need them open the repo, not the website. ([#13136](https://github.com/diegosouzapw/OmniRoute/pull/13136)) — thanks @HouMinXi +- **fix(combo):** parse numeric-epoch `rate_limited_until` in the combo cooldown read path ([#13141](https://github.com/diegosouzapw/OmniRoute/pull/13141)) — thanks @maxmad64bis +- **fix(opencode):** both OpenCode plugins now reject a gateway address typed without `http://` at configuration time, instead of publishing every model with an api url no client can call, and the v2 plugin no longer publishes a model card whose api url is blank or relative ([#13142](https://github.com/diegosouzapw/OmniRoute/pull/13142)) — thanks @maxmad64bis +- **fix(providers):** lock opencode model on upstream 400 model-unavailable ([#13146](https://github.com/diegosouzapw/OmniRoute/pull/13146)) — thanks @maxmad64bis +- **fix(logging):** keep the provider exchange rather than the raw client bodies when a call log exceeds its size budget, and show that recovered payload in the request-detail panel instead of replacing it with the stored response body ([#13147](https://github.com/diegosouzapw/OmniRoute/pull/13147)) — thanks @maxmad64bis +- **fix(traffic-inspector):** the WebSocket route no longer leaks a traffic-buffer subscriber and a 30s ping timer when the client socket is already closed at handler time — listeners are attached before any resource is acquired, a destroyed socket bails out early, and the ping interval stops on a dead socket where `write()` never throws ([#13155](https://github.com/diegosouzapw/OmniRoute/pull/13155)) — thanks @anhtahaylove +- **fix(telegram):** bound the per-user API key cache in the Telegram chat proxy so a burst of distinct chat ids can no longer grow the process heap without limit ([#13165](https://github.com/diegosouzapw/OmniRoute/issues/13165)) ([#13166](https://github.com/diegosouzapw/OmniRoute/pull/13166)) — thanks @anhtahaylove +- **fix(stream):** cancel the upstream response body when the JSON-to-SSE sniff unwinds on a body timeout, so a stalled upstream no longer pins the connection ([#13169](https://github.com/diegosouzapw/OmniRoute/issues/13169)) ([#13171](https://github.com/diegosouzapw/OmniRoute/pull/13171)) — thanks @anhtahaylove +- **fix(telegram):** authenticate webhook deliveries with Telegram's `secret_token` so an unauthenticated caller can no longer mint API keys or spend upstream quota ([#13172](https://github.com/diegosouzapw/OmniRoute/issues/13172)) ([#13175](https://github.com/diegosouzapw/OmniRoute/pull/13175)) — thanks @anhtahaylove +- **fix(models):** give discoverable Gemini 3.8 Flash ids their own 65536 output spec so Antigravity no longer clamps them to 16384 ([#13195](https://github.com/diegosouzapw/OmniRoute/pull/13195)) — thanks @HouMinXi +- **fix(dashboard):** leftover catalog-removed provider rows (gemini-cli) can be listed and purged from the providers page ([#13067](https://github.com/diegosouzapw/OmniRoute/issues/13067)) ([#13197](https://github.com/diegosouzapw/OmniRoute/pull/13197)) — thanks @HouMinXi +- **fix(auth):** a dashboard session now requires the `authenticated: true` claim that login, OIDC and the session refresh already emit — a JWT merely signed with `JWT_SECRET` (for example the Cursor CLI passthrough token, which any API-key holder can obtain) no longer verifies as the `auth_token` cookie on any route, the WebSocket handshake or the live server; existing sessions keep working ([#13298](https://github.com/diegosouzapw/OmniRoute/issues/13298)) ([#13375](https://github.com/diegosouzapw/OmniRoute/pull/13375)) +- **fix(api):** `DELETE /v1/batches/delete-completed` now applies the caller's API-key policy (endpoint allowlist, schedule, usage cap, rate limit) like every other `/v1` route, commits the key-scoped sweep per 200-batch chunk instead of holding one write lock for the whole sweep, refuses to loop without progress, and rejects a scope that names both a key and `allTenants` ([#13684](https://github.com/diegosouzapw/OmniRoute/pull/13684)) +- Restore the API-route typecheck gate: the non-streaming leg result lost its discriminated-union narrowing after the server-owned tool loop reassignment, producing 13 new TS2339 diagnostics in `chatCore.ts`. ([#12963](https://github.com/diegosouzapw/OmniRoute/pull/12963)) +- Restore four non-streaming behaviours the server-owned tool loop refactor dropped: client aborts map to 499 with the fixed `Request aborted` message, an aborted request no longer logs a synthetic `clientResponse`, Claude prompt-cache telemetry is recorded again, and a body that cannot be canonicalized no longer throws when the tool loop is off. ([#12990](https://github.com/diegosouzapw/OmniRoute/pull/12990)) +- **fix(skills):** The CLI registry parser now reads positionals declared with `.addArgument()`, not only those written inline in `.command()`. `tunnel create [type]` was being published as `tunnel create`, so the agent-skills sync gate reported drift on every branch and regenerating would have deleted the argument. ([#13009](https://github.com/diegosouzapw/OmniRoute/pull/13009)) — thanks @ntdatt812 +- **fix(combos):** dashboard combo test uses a short prompt, serial probes, and a 60s timeout so reasoning models and rate-limited free pools do not fail the health check ([#13001](https://github.com/diegosouzapw/OmniRoute/pull/13001)) — thanks @HouMinXi +- fix(compression): terminate idle worker threads on eviction so long-running instances stop leaking OS threads and MessagePorts ([#13091](https://github.com/diegosouzapw/OmniRoute/pull/13091)) — thanks @anhtahaylove +- Redact Google API keys of any length in error bodies: the pattern required exactly 39 characters, so shorter or longer `AIza…` credentials passed through unredacted. ([#12964](https://github.com/diegosouzapw/OmniRoute/pull/12964)) +- **fix(grok-cli):** a 402 "Grok Build usage balance exhausted" parks that Grok login as out of credit (Grok Build CLI, grok.com cookie, and xAI OAuth share the weekly pool). Combo routing then tries the next login instead of locking the model for every account in the pool ([#13061](https://github.com/diegosouzapw/OmniRoute/pull/13061)) — thanks @HouMinXi +- fix(compression): spawn the LLMLingua worker with a file URL object so compression actually runs instead of silently failing open on Node ([#13093](https://github.com/diegosouzapw/OmniRoute/pull/13093)) — thanks @anhtahaylove +- **fix(test):** run the local `test` and `test:unit` scripts at concurrency 4 so a full-suite run no longer exhausts the machine's commit charge and kills unrelated processes ([#13187](https://github.com/diegosouzapw/OmniRoute/pull/13187)) — thanks @anhtahaylove +- **fix(monitoring):** `GET /api/monitoring/health` `credentialHealth` now includes a bounded `failedConnections` list (`connectionId`, `status`, sanitized `lastError`) when the probe-cache gauge `failed>0`, plus `source: probe-cache` and a cheap `staleDbNonOkCount` for sticky SQLite `test_status` on active rows. Documents that the live gauge is not `provider_connections.test_status`. ([#12876](https://github.com/diegosouzapw/OmniRoute/pull/12876)) — thanks @RaviTharuma +- **fix(resilience):** an openai-compatible multi-upstream gateway no longer marks the whole connection `credits_exhausted` when a single upstream (or the `/models` / representative-model health probe) returns 402. Quota failures stay model-scoped; true connection-wide auth failures are unchanged. Single-credential 402 key disable ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239)) is preserved. ([#12875](https://github.com/diegosouzapw/OmniRoute/pull/12875)) — thanks @RaviTharuma +- **fix(dashboard):** nine fixes on the Orchestration Canvas, all diagnosed in the Phase 2 reviews. A source that fails now keeps the timestamp of its FIRST failure instead of being re-stamped every poll — the stale line said "since the last poll" no matter how long the source had been down, and the churn also defeated the snapshot's stable identity (it serializes the source list), so the canvas re-rendered on every tick while anything was broken. A source that HAD data and only then started failing is flagged too: previously only a source with no node at all got the warning, so a source that went down mid-session kept a healthy-looking node forever. The rest are pointwise: clicking a filter chip cancels the pending search debounce (left armed, it fired ~300ms later and silently reverted the chip); the search input carries an accessible name; `?state=running, failed` parses like the unpadded form instead of dropping the padded value; the CSV toggle helper is defined once in `model/urlParams.ts` rather than twice; the agents tab tells "nothing running" apart from "the filter matched nothing", offering a clear-filters button instead of setup links that would be wrong advice there; edges stop emitting SMIL particles above 40 simultaneously active edges, keeping the colored stroke; and the drawer's error banner clears when a retried action succeeds. ([#12983](https://github.com/diegosouzapw/OmniRoute/pull/12983)) +- **fix(i18n):** the nine locales added with the EU-language batch (Greek, Estonian, Irish, Croatian, Lithuanian, Latvian, Maltese, Slovenian, Serbian) were missing the eleven Orchestration Canvas keys that Phase 3 introduced, so the compare-runs panel and the "no runs match these filters" empty state fell back to English in those languages (`deepMergeFallback` substitutes English for an absent key, so nothing rendered blank — it rendered untranslated). The coverage gate does not catch this: it enforces an 80% floor per locale, and eleven missing keys out of ~13,000 leaves coverage at 99.9%. Translated for real in each language, calibrated against the wording each file already uses for "run", "filter" and "skill". ([#13294](https://github.com/diegosouzapw/OmniRoute/pull/13294)) +- **fix(dashboard):** five follow-ups from the Phase 2 Orchestration Canvas review. The History drawer now shows the memory section for runs that already left the live TTL window: the persisted `memory_hits` event is parsed into `metadata.memoryHits` with the same defensive validation the drawer applies, and — because that event is observability rather than a state transition — it no longer leaks into the timeline, where it had been inheriting the task's state and rendering as a duplicate transition. The A2A memory recall runs against its own 1.5s deadline instead of inheriting the memory backend's 30s one; overshooting degrades exactly like any other recall failure (no hits, task proceeds), and the timer is cleared on both paths. Repeating a Conductor run carries its `requirements.cli`/`requirements.model` forward, so the new run is pinned to the same runner profile and model rather than drifting to whatever the fleet picks. A successful repeat from the Agents tab now focuses the run it created, instead of leaving the operator on the finished one. And the auth test for `POST /api/conductor/tasks` moved into the shared `ROUTES` array rather than restating the pattern. ([#12988](https://github.com/diegosouzapw/OmniRoute/pull/12988)) +- fix(plugins): stop leaking an exit listener per plugin hook timeout, which triggered MaxListenersExceededWarning on plugins that ignore SIGTERM ([#13092](https://github.com/diegosouzapw/OmniRoute/pull/13092)) — thanks @anhtahaylove +- **fix(validation):** Provider node edits no longer fail with a generic "Invalid request" when the optional daily-quota reset fields are left blank. The dashboard sends `dailyQuotaResetTimezone` and `dailyQuotaResetHour` as `null`, and only the hour accepted it. ([#13066](https://github.com/diegosouzapw/OmniRoute/issues/13066)) ([#13083](https://github.com/diegosouzapw/OmniRoute/pull/13083)) — thanks @ntdatt812 +- **fix(combo):** quota-aware expansion drops banned, inactive, missing, and wrong-provider connections before quota fetch or model dispatch; pins and allowlists stay selectors, not a bypass. Antigravity automatic exhaustion now requires a reported zero remaining, so a positive balance below 1% stays eligible. ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- Fix an intermittent failure in the quota-weighted routing test suite: scores are a function of `Date.now()`, so peers with identical quota scored microseconds apart never tied and swapped order. ([#12946](https://github.com/diegosouzapw/OmniRoute/pull/12946)) +- fix(volcengine): resolve quota window mapping gaps, safe multi-connection console binding, and response cookie sanitization ([#12950](https://github.com/diegosouzapw/OmniRoute/pull/12950)) — thanks @HouMinXi +- **fix(test):** resolve the WebDAV handler path with `fileURLToPath` so the suite's 37 WebDAV tests run on Windows instead of failing with a doubled `C:\C:\` drive prefix ([#13196](https://github.com/diegosouzapw/OmniRoute/pull/13196)) — thanks @anhtahaylove +- **fix(ui):** fall back from invalid AnySearch icon alias (direct commit `9587e07b69`) +- **fix(sse):** stop rebuilding a truncated summary from the collector's cap-dropped event array ([#12718](https://github.com/diegosouzapw/OmniRoute/pull/12718)) — thanks @hartmark +- **fix(conversations):** show a pending spinner for unresolved tool nodes instead of "(empty)" ([#12727](https://github.com/diegosouzapw/OmniRoute/pull/12727)) — thanks @hartmark +- **fix(streaming):** fail fast when an upstream stream produces only lifecycle/heartbeat events, never real content ([#12741](https://github.com/diegosouzapw/OmniRoute/pull/12741)) — thanks @hartmark +- **fix(responses-continuation):** bridge the write-in-flight window with an in-memory pending store ([#12854](https://github.com/diegosouzapw/OmniRoute/pull/12854)) — thanks @hartmark +- **fix(translator):** drop or map agent_message on Chat Completions fallback ([#12880](https://github.com/diegosouzapw/OmniRoute/pull/12880)) — thanks @RaviTharuma +- **fix(api):** page and stream-complete GET /v1/models for large catalogs ([#12882](https://github.com/diegosouzapw/OmniRoute/pull/12882)) — thanks @RaviTharuma +- **fix(quota):** align AUTH window labels with usage API and clarify 503 ([#12884](https://github.com/diegosouzapw/OmniRoute/pull/12884)) — thanks @RaviTharuma +- **fix(ci):** clear two base-reds on release/v3.8.51 — mutation-coverage gate + image-only-model guard ([#12945](https://github.com/diegosouzapw/OmniRoute/pull/12945)) +- **fix(api):** scope batch bulk-delete to the calling API key ([#13211](https://github.com/diegosouzapw/OmniRoute/pull/13211)) +- **fix(tests):** align model-sync log assertion to shipped path redaction (base-red) ([#13227](https://github.com/diegosouzapw/OmniRoute/pull/13227)) +- **fix(test):** make the ToS heading guard actually require the parentheses ([#13228](https://github.com/diegosouzapw/OmniRoute/pull/13228)) +- **fix(quality):** register 4 drifted covering tests in stryker tap.testFiles ([#13229](https://github.com/diegosouzapw/OmniRoute/pull/13229)) +- **fix(logging):** mask *-api-key headers in request-log pipeline (#13273) ([#13401](https://github.com/diegosouzapw/OmniRoute/pull/13401)) — thanks @KooshaPari +- **fix(tests):** correct stale Jina catalog prefix in custom-models test ([#13403](https://github.com/diegosouzapw/OmniRoute/pull/13403)) — thanks @KooshaPari +- **fix(backend):** prune old backups after health-check-repair snapshots (#13308) ([#13404](https://github.com/diegosouzapw/OmniRoute/pull/13404)) — thanks @KooshaPari +- **fix(backend):** guard memory_fts_au trigger with WHEN clause to stop FTS bloat (#13326) ([#13405](https://github.com/diegosouzapw/OmniRoute/pull/13405)) — thanks @KooshaPari +- **fix(free-tiers):** remove obsolete Together $25 signup credit (#12526) ([#13407](https://github.com/diegosouzapw/OmniRoute/pull/13407)) — thanks @KooshaPari +- **fix:** skip non-chat generation models in Test all models ([#13409](https://github.com/diegosouzapw/OmniRoute/pull/13409)) — thanks @KooshaPari +- **fix(providers):** de-duplicate agy and antigravity model catalogs ([#13410](https://github.com/diegosouzapw/OmniRoute/pull/13410)) — thanks @KooshaPari +- **fix(dashboard):** send provider in body for mini-playground web search ([#13411](https://github.com/diegosouzapw/OmniRoute/pull/13411)) — thanks @KooshaPari +- **fix(resilience):** add CJK quota-exhaustion patterns to 429 classifier ([#13412](https://github.com/diegosouzapw/OmniRoute/pull/13412)) — thanks @KooshaPari +- **fix(backend):** eval runner: failed calls no longer score as passed + dotAll regex ([#13413](https://github.com/diegosouzapw/OmniRoute/pull/13413)) — thanks @KooshaPari +- **fix(backend):** eval runner opts out of output-style and memory injection ([#13414](https://github.com/diegosouzapw/OmniRoute/pull/13414)) — thanks @KooshaPari +- **fix:** chain serial quarantine step in npm test script (fixes #13286) ([#13418](https://github.com/diegosouzapw/OmniRoute/pull/13418)) — thanks @KooshaPari +- **fix(antigravity):** derive stable session ID from credentials for prompt-cache reuse (#13305) ([#13424](https://github.com/diegosouzapw/OmniRoute/pull/13424)) — thanks @KooshaPari +- **fix(memory):** merge memory into top-level system field instead of unshifting at messages[0] (#13425) ([#13427](https://github.com/diegosouzapw/OmniRoute/pull/13427)) — thanks @KooshaPari +- **fix(compression):** preserve instruction blocks from lossy engines ([#13523](https://github.com/diegosouzapw/OmniRoute/pull/13523)) — thanks @KooshaPari +- **fix(compression):** preserve polarity words and newlines in ultra heuristic ([#13524](https://github.com/diegosouzapw/OmniRoute/pull/13524)) — thanks @KooshaPari +- **fix(compression):** run user preservePatterns before built-in patterns (#13457) ([#13525](https://github.com/diegosouzapw/OmniRoute/pull/13525)) — thanks @KooshaPari +- **fix(reasoning):** expand deepseek-v4 regex to match v4.1+ and add command-code provider (#13430) ([#13528](https://github.com/diegosouzapw/OmniRoute/pull/13528)) — thanks @KooshaPari +- **fix(providers):** add alwaysReasons metadata for reka-flash-3 ([#13531](https://github.com/diegosouzapw/OmniRoute/pull/13531)) — thanks @KooshaPari +- **fix(playground):** allow Compare columns to scroll independently ([#13532](https://github.com/diegosouzapw/OmniRoute/pull/13532)) — thanks @KooshaPari +- **fix(security):** empty whitelist allows through to prevent admin lockout ([#13534](https://github.com/diegosouzapw/OmniRoute/pull/13534)) — thanks @KooshaPari +- **fix(tests):** add DISABLE_IOREG_STRATEGY env var for macOS test compatibility (fixes #13467) ([#13539](https://github.com/diegosouzapw/OmniRoute/pull/13539)) — thanks @KooshaPari +- **fix(zed-hosted):** map developer role and Gemini enums for Zed proxy ([#13543](https://github.com/diegosouzapw/OmniRoute/pull/13543)) — thanks @KooshaPari +- **fix(analytics):** normalize provider aliases to canonical id in usage_history ([#13545](https://github.com/diegosouzapw/OmniRoute/pull/13545)) — thanks @KooshaPari +- **fix(combo):** use traceId for call log id to prevent UNIQUE constraint failure ([#13546](https://github.com/diegosouzapw/OmniRoute/pull/13546)) — thanks @KooshaPari +- **fix(auth):** check model lockout before returning synthetic noauth connection ([#13547](https://github.com/diegosouzapw/OmniRoute/pull/13547)) — thanks @KooshaPari +- **fix(guardrails):** skip credential redaction for base64 image data URLs ([#13550](https://github.com/diegosouzapw/OmniRoute/pull/13550)) — thanks @KooshaPari +- **fix(resilience):** increase requestQueue.maxWaitMs default from 15s to 30s ([#13553](https://github.com/diegosouzapw/OmniRoute/pull/13553)) — thanks @KooshaPari +- **fix(docs):** document OMNIROUTE_READY_TIMEOUT_MS and allowlist the test-only DISABLE_IOREG_STRATEGY ([#13692](https://github.com/diegosouzapw/OmniRoute/pull/13692)) + ### 📝 Maintenance ---- +- **docs(readme):** reconcile live v3.8.50 provider, free-tier, CLI, routing, test, community, sponsor, acknowledgment, and SVG metrics with their audited source denominators, including a deduplicated OmniRoute-in-Action snapshot and distinct contributor rankings for merged pull requests, GitHub-attributed commits, and Git history ([#11356](https://github.com/diegosouzapw/OmniRoute/pull/11356)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **docs:** sync the canonical provider count 354 → 356 across `README.md`, `AGENTS.md`, `llm.txt` (+ 42 i18n mirrors), the four README SVG diagrams, `docs/reference/PROVIDER_REFERENCE.md` (regenerated) and the `package.json` description after Opper (#11629) and 1min.ai (#11631) boarded the catalog — closes the `check:docs-counts-sync` strict drifts that kept `release/v3.8.51` red ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11673](https://github.com/diegosouzapw/OmniRoute/pull/11673)) — thanks @jonlwheat2-gif +- **test(providers):** update count-derived assertions after the v3.8.51 provider additions — `APIKEY_PROVIDERS` 233 → 235 (Opper #11629 + 1min.ai #11631), reserved-prefix REGISTRY walk 395 → 398, `WEB_FETCH_PROVIDERS` now includes `nimble-search` (#11620), and the provider translate-path golden snapshot regenerated ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11674](https://github.com/diegosouzapw/OmniRoute/pull/11674)) — thanks @jonlwheat2-gif +- **test(sse):** bump the hard-lease connection-query inventory for `src/lib/tokenHealthCheck.ts` to 2 — the verify-only web-cookie sweep added by #11495 split the single `getProviderConnections` call into oauth + cookie variants, which the frozen inventory had not tracked ([#11495](https://github.com/diegosouzapw/OmniRoute/pull/11495)). ([#11677](https://github.com/diegosouzapw/OmniRoute/pull/11677)) — thanks @jonlwheat2-gif / @oyi77 +- **chore(release):** clear the `release/v3.8.51` base-red gates — docs migration count (+38 i18n mirrors), stryker `tap.testFiles`, lockfile registry host, stale ESLint suppressions, the 7 lint regressions they were hiding, the stale `APIKEY_PROVIDERS` partition count (231 → 233), the `glm-5.3-max` tier missing from the GLM test table, a dead `modelType === "chat"` comparison in the models catalog, and the `resolveLiveWsUrl` / `sanitizeLiveWsPort` helpers from #11388 that a later merge dropped from `wsPath.ts` (dashboard live WebSocket URL resolution) ([#11502](https://github.com/diegosouzapw/OmniRoute/pull/11502)) — thanks @MumuTW +- **chore(free-models):** the free-tier regime table now declares which totals figure each regime feeds and whether it may take the no-auth shortcut, so the sets that used to repeat those answers by hand are derived from it — a new `freeType` no longer compiles until it has answered every question, instead of silently contributing to no total ([#11537](https://github.com/diegosouzapw/OmniRoute/pull/11537)) — thanks @maxmad64bis +- **docs(routing):** LKGP strategy copy now states provider-level stickiness and the explicit rules fallback instead of promising per-target pinning ([#11555](https://github.com/diegosouzapw/OmniRoute/pull/11555)) — thanks @maxmad64bis +- **test(chatcore):** pin the response tool-name alias resolution against the ordering hazard that broke every Gemini/Antigravity MCP tool call in v3.8.49. `extractRequestToolIdentityMap` deletes `translatedBody._toolNameMap`, so the later read is always undefined and the ledger survives only through the `requestToolIdentityMap` fallback — removing that fallback previously left the entire tool-name suite green. The resolution moves into `resolveResponseToolNameMap()` next to the map it depends on, with a regression guard that fails without the recovery. No behaviour change. ([#11575](https://github.com/diegosouzapw/OmniRoute/pull/11575)) — thanks @amartinawi +- **docs(env):** document `OMNIROUTE_CORPUS_CACHE_SIZE` in `.env.example` and `ENVIRONMENT.md`, so the env/docs contract check stops failing Docs Gates on every branch ([#11576](https://github.com/diegosouzapw/OmniRoute/pull/11576)) — thanks @ntdatt812 +- **chore(stryker):** register `route-guard-tunnel-processes-local-only` and `universal-quota-aware-routing` in `tap.testFiles`, so their mutant kills count for the three modules they cover ([#11577](https://github.com/diegosouzapw/OmniRoute/pull/11577)) — thanks @ntdatt812 +- **test(authz):** pin `LOCAL_ONLY_API_GET_EXEMPTIONS` by exact membership instead of by entry count, so the guard names the offending path and also catches a substitution ([#11580](https://github.com/diegosouzapw/OmniRoute/pull/11580)) — thanks @ntdatt812 +- **test(build):** pin `resolveNextBuildBundlerFlag` to the `OMNIROUTE_USE_TURBOPACK` contract it implements, across runtimes, instead of asserting an unimplemented Bun override ([#11583](https://github.com/diegosouzapw/OmniRoute/pull/11583)) — thanks @ntdatt812 +- **test(claude-web):** the Claude Web transport test's barrel anchors no longer pin the exact construction spelling. #11421 made the executor map lazy, so `/"claude-web": new ClaudeWebExecutor\(\)/` stopped matching and the test went red on `release/v3.8.51` itself — a base-red gate every open PR inherited. The anchors now match the alias key and the class name and tolerate either spelling in between, and were checked to still fail when an alias is genuinely repointed at another executor ([#11585](https://github.com/diegosouzapw/OmniRoute/pull/11585)) — thanks @ntdat812 +- **test(models):** pin `qwen3.8-max` resolution per provider — two of the four catalogs now serve the bare id and no longer carry `-preview` ([#11590](https://github.com/diegosouzapw/OmniRoute/pull/11590)) — thanks @ntdatt812 +- **test(executors):** drop the stale 128-tool truncation assertion that contradicted the #11444 pass-through contract ([#11591](https://github.com/diegosouzapw/OmniRoute/pull/11591)) — thanks @ntdatt812 +- **test(token-health):** pin the current terminal-skip boundary — expired with retry budget is probed, `account_deactivated` stays skipped ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)) — thanks @ntdatt812 +- **test(models):** follow #11503's retarget of the `gemini-3-pro-high` alias to the hyphenated catalog id ([#11593](https://github.com/diegosouzapw/OmniRoute/pull/11593)) — thanks @ntdatt812 +- **refactor(video bridge):** extract per-video acquisition, whole-result caching, description, and metrics/abort/cleanup out of `VideoBridgeGuardrail.preCall` into a `processVideoPart` seam in a new `videoBridgePipeline.ts`, behind explicit `VideoMediaBrokerPort`, `VideoAudioTranscriptionPort`, and `VideoDrilldownPort` boundaries; `preCall` now only handles request traversal, policy, and response aggregation. The Video tab's FFmpeg/ffprobe runtime status is now an explicit `unknown` / `restricted` / `unavailable` / `available` state instead of a nullable boolean pair, fixing a case where an in-flight or failed probe was mislabeled as "install FFmpeg" ([#11657](https://github.com/diegosouzapw/OmniRoute/issues/11657)). ([#12016](https://github.com/diegosouzapw/OmniRoute/pull/12016)) +- **test(opencode-plugin):** add unit test coverage for `formatFreeBudget()` naming helper ([#11660](https://github.com/diegosouzapw/OmniRoute/pull/11660)) — thanks @f9td56dbgh-hub +- **docs(video):** clarify that the Video Bridge transcript `source` field (`client`, `embedded`, `audio-bridge`) is presently caller-declared and not yet server-verified — OmniRoute enforces the enum shape but does not cryptographically confirm that an `embedded`/`audio-bridge` label came from a server-owned extraction ([#11661](https://github.com/diegosouzapw/OmniRoute/issues/11661)). ([#12001](https://github.com/diegosouzapw/OmniRoute/pull/12001)) +- **chore(providers):** retire the distributed Raycast Relay and Hailuo Web integrations after a provenance audit confirmed substantial derivation from GPL-3.0 sources; the official GitHub and MiniMax API providers remain available. Migration 163 disables legacy connections and invalidates their active leases while preserving connection IDs, audit/history rows, quota snapshots, usage records, and fail-closed API-key connection policies. ([#11691](https://github.com/diegosouzapw/OmniRoute/pull/11691)) +- **chore(providers):** retire the Felo Web provider while its GPL-derived provenance remains on licensing HOLD ([#11698](https://github.com/diegosouzapw/OmniRoute/pull/11698)) — stale runtime and database references now fail closed while existing connection, lease, and usage history remains preserved for auditability. +- **fix(sse):** retire the Gemini Web image-generation bridge while provenance and licensing remain on HOLD, preserving Gemini Web chat/auth/tools and legitimate Gemini/Nano Banana image providers ([#11708](https://github.com/diegosouzapw/OmniRoute/pull/11708)) +- **chore(audio):** retire the built-in EdgeTTS provider and its 11-voice catalog while provenance/licensing review remains on HOLD; the generic `/v1/audio/speech` endpoint and the other speech providers remain available ([#11711](https://github.com/diegosouzapw/OmniRoute/pull/11711)) +- **fix(providers):** retire the unofficial Qwen Web integration and its `qwen-web`/`qw` identities while third-party provenance remains on HOLD ([#11713](https://github.com/diegosouzapw/OmniRoute/pull/11713)) — direct requests now fail closed with a sanitized HTTP 410 and combos skip the retired target, while migration guards preserve connection IDs, allowlists, usage/audit history and invalidate active leases. Supported Qwen API, Qwen Code, cloud, token-plan, audio, local and model-name surfaces remain available; restoring Qwen Web requires a qualified provenance/license decision. +- **fix(providers):** retire `microsoft-designer-web` and its `msdesigner` alias from runtime and catalogs while preserving stored credentials, provider history and allowlists as inactive records — source dependency on gpt4free's [`MicrosoftDesigner.py`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/g4f/Provider/needs_auth/MicrosoftDesigner.py) is proven at snapshot `0943d16eef757a44deb60651c07bbe5fa958cafe`, whose [`LICENSE`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/LICENSE) is GPL-3.0; expressive adaptation is probable, which records provenance/licensing risk without asserting a legal conclusion ([#11720](https://github.com/diegosouzapw/OmniRoute/pull/11720)). +- **docs(legal):** centralize pinned MIT notices for the vendored GCF codec and local flag assets ([#11726](https://github.com/diegosouzapw/OmniRoute/pull/11726)) +- Split the 2,952-line `adobeFireflyClient.ts` into nine focused modules (catalog, credentials, payload, ARP session, upload, responses, IMS, account, poll) behind an unchanged barrel — no consumer edits and no public API change (#11728). — thanks @MumuTW +- **fix(dashboard):** replaced the Model Lockout card's unprovenanced notification MP3 with a short, locally synthesized Web Audio chime ([#11731](https://github.com/diegosouzapw/OmniRoute/pull/11731)) — both toggles retain optional audible feedback without shipping replacement media or third-party code, and unsupported, suspended, or rejected audio contexts fail open without blocking the setting change. +- **chore(deepseek-web):** remove the provenance-unresolved DeepSeek PoW WASM binary and its runtime loader/tracing while retaining the existing JavaScript solver (slower at high difficulty) ([#11732](https://github.com/diegosouzapw/OmniRoute/pull/11732)). +- **chore(assets):** remove four unused provider assets whose redistribution provenance could not be verified while retaining the active Freebuff SVG ([#11735](https://github.com/diegosouzapw/OmniRoute/pull/11735)) +- **docs(assets):** retired two raster images whose redistribution provenance could not be verified ([#11736](https://github.com/diegosouzapw/OmniRoute/pull/11736)) — PicoClaw documentation now reuses the existing SVG, while Zoo Code remains a text link without an image. +- **docs(legal):** add pinned notices for six LobeHub-derived provider SVGs and 65 byte-exact theSVG assets, retaining registry claims as unverified metadata with explicit trademark and HOLD boundaries ([#11737](https://github.com/diegosouzapw/OmniRoute/pull/11737)). +- **chore(quality):** added a versioned 225/225 provider-asset provenance manifest and blocking drift gate ([#11743](https://github.com/diegosouzapw/OmniRoute/pull/11743)) — the inventory pins magic MIME and SHA-256 for every physical file, records immutable source matches for 71 assets, keeps 69 probable and 85 unresolved entries explicit but non-blocking, and treats provenance as source evidence rather than copyright or trademark clearance. +- **refactor(mitm):** replace four previously derived Traffic Inspector utilities — conversation normalization, SSE merging, secret masking, and header sanitization — with independent clean-room implementations based on public provider schemas, WHATWG SSE, and RFC 6750, while keeping the two independently classified passthrough surfaces unchanged ([#11748](https://github.com/diegosouzapw/OmniRoute/pull/11748)). +- Added a unit test that fails seven days before any dated pack under `config/` (`validUntil` and sibling keys) lapses, naming the file and key. The Alibaba free-tier pack expired on 2026-08-27 and turned every PR red the next morning with no commit involved; renewal now happens on someone's terms, not the clock's. ([#11891](https://github.com/diegosouzapw/OmniRoute/pull/11891)) +- `check:workflows` now fails (under `--strict`/`--ratchet`) when any job routed to a self-hosted runner publishes with `--provenance` — npm rejects that with `422` at the registry, which in v3.8.50 only surfaced after the tag and Docker images were public. ([#11895](https://github.com/diegosouzapw/OmniRoute/pull/11895)) +- `scripts/ops/runner-janitor.sh` now proves a path is idle with one `lsof` snapshot and removes stale leftovers itself (tmpfs after 3 h — it is RAM — disk after 24 h), kills orphan `next-build` processes, prunes checkouts of stopped runners, and alerts on memory pressure; `--dry-run` shows exactly what it would do. `docs/ops/RUNNER_BOX.md` reconciled to the measured box (31 GB, 10 listeners). ([#11893](https://github.com/diegosouzapw/OmniRoute/pull/11893)) +- The `next-build` artefact (1.3 GB) is now written and read under `$RUNNER_TEMP` (per-runner, on disk) instead of `/tmp`, which on the self-hosted pool is a 12 GB tmpfs in RAM. Landing it there took 27–32 of the publish job's 76 minutes, and the fixed `/tmp/e2e-build.tar.gz` name let E2E jobs on different runners overwrite each other's download. ([#11896](https://github.com/diegosouzapw/OmniRoute/pull/11896)) +- The CI `build` job now runs in two concurrency lanes — `main` and pull requests — so a release build is never queued behind (or OOM-killed beside) PR builds on the self-hosted pool, which holds one `next-build` comfortably and two at the edge. ([#11901](https://github.com/diegosouzapw/OmniRoute/pull/11901)) +- Re-freeze the ESLint suppressions on `release/v3.8.51` from a clean-room measurement (2 stale file entries pruned, 55 pre-existing `no-explicit-any` in six new files frozen under #11924) and drop the dead `GPT_SIZE_MAP` constant orphaned by the Adobe Firefly client split, so `No new ESLint warnings` stops failing every PR with exit 2 (Refs #11924) ([#11955](https://github.com/diegosouzapw/OmniRoute/pull/11955)) +- Type the 55 `no-explicit-any` sites that had been frozen under #11924 — four redundant casts in `socksConnectorWithFamily.ts` (undici/socks types already accept them) and the mocks/fixtures of the socks-timeout and isFree suites — and drop their suppression entries; the ESLint ratchet shrinks from 5487 to 5432 (Closes #11924) ([#11975](https://github.com/diegosouzapw/OmniRoute/pull/11975)) +- Take the two hosted-runner builds off the PR rail: `Build App` (`build.yml`) is `workflow_dispatch`-only and `DAST smoke (PR)` runs only for PRs into `main` — the 7 GB hosted VM cannot build `release/v3.8.51` in any profile (19/30 red, VM shutdown ~8 min into `next build`) and both checks had turned into permanent noise on every release PR; the bundle stays validated by `ci.yml` on `main` and by `nightly-release-green` on `release/**` (Closes #11946) ([#11962](https://github.com/diegosouzapw/OmniRoute/pull/11962)) +- Move the four nightly jobs that build the backend (`nightly-schemathesis`, `nightly-llm-security` promptfoo + garak, `nightly-resilience` axe-a11y) off the hosted 7 GB runner — where they died on `release/v3.8.51` unseen — onto the box's new `omni-light` pool (two listeners), and document the reshaped fleet (4 active OmniRoute listeners: 2 `omni-build` + 2 `omni-light`, janitor ceiling 4) (Closes #11965) ([#11967](https://github.com/diegosouzapw/OmniRoute/pull/11967)) +- Stop painting every fork PR into `release/**` red: `quality.yml` `Build (advisory)` is skipped (GitHub still reports `continue-on-error` failures as check FAILURE). Hosted `ubuntu-latest` cannot finish `npm run build` on this tree — same class as #11962 taking `build.yml` off the PR rail. `docker-publish.yml` amd64 now runs on the `.113` `omni-build` pool (31 GB / 32 cores, two listeners) with Turbopack, shares the `heavy-build-main` lane with `ci.yml` `Build` so it queues instead of becoming a third heavy, and keeps per-ref concurrency (a merge storm was starting 8 concurrent OOM builds). arm64 stays on `ubuntu-24.04-arm` with webpack — there is no ARM box. Fallback when `USE_VPS_RUNNER` is off: hosted amd64 + webpack (#11976). ([#12021](https://github.com/diegosouzapw/OmniRoute/pull/12021)) +- `docker-publish.yml` builds with webpack (`OMNIROUTE_USE_TURBOPACK=0`) on **both** arches, including amd64 on the `.113` `omni-build` pool. Turbopack had the RAM (31 GB, `omniroute-113-6`) and still panicked (`TurbopackInternalError: there must be a path to a root`, run 33253576569, 26 min in). The same tree's arm64 webpack build on hosted `ubuntu-24.04-arm` succeeded (run 33264823398). Dockerfile already documents webpack as the Docker escape hatch (#11976). ([#12050](https://github.com/diegosouzapw/OmniRoute/pull/12050)) +- **docs(free-tier):** declare the counting vs deciding regimes for "is it free?" and guard the deciding path from DB-backed catalog resolution ([#12226](https://github.com/diegosouzapw/OmniRoute/pull/12226)) — thanks @maxmad64bis +- **docs(auto-combo):** The mode pack table in `docs/routing/AUTO-COMBO.md` now lists all six shipped packs with every weight each one sets, replacing a four-pack table whose numbers had also drifted from the source. It states plainly that no pack sets `quality`, so selecting any pack silences the observed-quality signal. Six more documents that quote the scoring factor count joined the `check:docs-counts` gate, which caught five stale claims — including one naming nine factors that do not exist — and two stale mode pack counts. The dashboard routing panel, which offered four of the six packs and labelled the default strategy "6-Factor Scoring", is now covered by a test; the two packs it was missing are `reliability-first` and `chaos-mode`, the latter labelled as the fault-injection profile it is rather than as one more routing preference ([#12316](https://github.com/diegosouzapw/OmniRoute/pull/12316)) — thanks @maxmad64bis +- **chore(quality):** rebaseline `src/lib/db/apiKeys.ts` for the ACL the key-creation path now preserves ([#12352](https://github.com/diegosouzapw/OmniRoute/pull/12352)) ([#12673](https://github.com/diegosouzapw/OmniRoute/pull/12673)) — thanks @KrzysiekSko +- **chore(providers):** bump the Claude Code wire identity and the Devin bridge image pin from `2.1.220` to `2.1.258` ([#12402](https://github.com/diegosouzapw/OmniRoute/pull/12402)) — thanks @ggiak ([#12604](https://github.com/diegosouzapw/OmniRoute/pull/12604)) +- **docs(env):** align `.env.example`, the README Bun section, and the troubleshooting guide with the code: `OMNIROUTE_USE_TURBOPACK` also governs `npm run build` (not dev-only), `bun run build` follows that flag instead of auto-selecting Webpack, `OMNIROUTE_CHAT_MAX_HEAVY_IN_FLIGHT` is unset by default (no request-count cap), and the structural `503 chat_admission_busy` message matches `chatAdmissionResponses.ts` (#12404 — thanks @pacocartones) +- **chore(electron):** upgrade the desktop app to Electron 44 (Chromium 152, Node 24.18.1) ([#12217](https://github.com/diegosouzapw/OmniRoute/pull/12217)). **Requires macOS 13 (Ventura) or later** — Chromium dropped macOS 12 (Monterey), so Monterey users must stay on an earlier OmniRoute desktop build. Windows and Linux are unaffected; the app already shipped only x64/arm64, so Electron 44 dropping 32-bit builds changes nothing. Removes the `openAsHidden`/`wasOpenedAsHidden` login-item fields deleted in Electron 44 — hidden autostart continues to work through the `--hidden` argument registered with the login item ([#12554](https://github.com/diegosouzapw/OmniRoute/pull/12554)) — thanks @app/dependabot +- **chore(quality):** rebaseline `src/sse/handlers/chat.ts` for the effective-input persistence the continuation fix needs ([#12641](https://github.com/diegosouzapw/OmniRoute/pull/12641)) ([#12680](https://github.com/diegosouzapw/OmniRoute/pull/12680)) — thanks @hartmark +- `Coverage` job on `ci.yml`: the informational Codecov upload gets its own 5-minute ceiling and `continue-on-error`, and the job budget grows from 20 to 30 minutes (the 8-shard c8 merge alone takes ~10) — a stalled upload no longer ends the job `cancelled` and drags a fully green `main` run's conclusion down with it ([#11972](https://github.com/diegosouzapw/OmniRoute/pull/11972)) +- CI hardening on the PR rail: the four `Unit Tests fast-path` shards get `timeout-minutes: 30` (a hung shard held a PR for 64 min instead of GitHub's 6 h default) and both ESLint file caches lose their `restore-keys` fallback, so a cache built under another suppressions file or lint config can no longer report stale verdicts (Refs #11600, #11924) ([#11963](https://github.com/diegosouzapw/OmniRoute/pull/11963)) +- Every CI job that runs a `next build` (`build`, the npm `publish`, both release-green validations) now targets the `omni-build` runner label, which only two of the eight self-hosted runners carry. The box holds one build comfortably and two at the edge; a third now queues on GitHub instead of being OOM-killed by the kernel. ([#11932](https://github.com/diegosouzapw/OmniRoute/pull/11932)) +- **refactor(combo):** move `handleRoundRobinCombo` (and `resolveTargetTokenLimit`) into `open-sse/services/combo/roundRobinCombo.ts`. `combo.ts` drops from 2164 to 1014 lines (`split("\n").length`); the leaf is 1199 (under the 1200 new-file cap). The round-robin call site uses a dynamic `import()` so `releaseStickyPinOnFailure` / `clearStaleLKGP` can stay exported from `combo.ts` without a static cycle. Skip / sticky / semaphore / safety-timer behavior is unchanged. ([#12811](https://github.com/diegosouzapw/OmniRoute/pull/12811)) — thanks @HouMinXi +- **chore(quality):** rebaseline the file-size caps the error-boundary campaign grew past (`open-sse/executors/codex.ts`, `open-sse/vendor/codex-chatgpt-web/bridge.ts`, both via [#12444](https://github.com/diegosouzapw/OmniRoute/pull/12444)) ([#12654](https://github.com/diegosouzapw/OmniRoute/pull/12654)) +- **chore(quality):** rebaseline the file-size caps the hartmark batch grew past (`combos/page.tsx` via [#12355](https://github.com/diegosouzapw/OmniRoute/pull/12355), `open-sse/services/combo.ts` via [#12338](https://github.com/diegosouzapw/OmniRoute/pull/12338)) ([#12623](https://github.com/diegosouzapw/OmniRoute/pull/12623)) — thanks @hartmark +- **chore(quality):** rebaseline the file-size caps the HouMinXi batch grew past when its PRs stacked (`providers/page.tsx`, `chatCore.ts`, `accountFallback.ts`) — each PR measured correctly in isolation, none saw the stacking ([#12619](https://github.com/diegosouzapw/OmniRoute/pull/12619)) +- **chore(quality):** rebaseline `open-sse/services/combo.ts` for the reset-aware scoring the HouMinXi batch stacked ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) ([#12678](https://github.com/diegosouzapw/OmniRoute/pull/12678)) — thanks @HouMinXi +- **docs:** sync provider catalog count (direct commit `4f11b2ae3d`) +- **docs(skills):** sync omni-inference search fallback (direct commit `b4e76a7ed9`) +- **chore(lint):** prune two stale base suppressions (direct commit `1325047d56`) +- **test(auto-combo):** tolerate full-suite rotation load (direct commit `ad3e293f9f`) +- **test:** drain inherited quality snapshot reds (direct commit `21ed68d8ac`) +- **chore(quality):** rebaseline measured CLI bundle growth (direct commit `d35b3f9779`) +- **test(ui):** reproduce invalid AnySearch icon alias (direct commit `12cc6ca834`) +- **test(translator):** fix the relative imports of the relocated deferred-finish test (direct commit `777d9d1629`) +- **docs(counts):** bring the documented provider and migration counts to the measured values (direct commit `e70bea30e9`) +- **test(security):** drop the guard-target set left unused by the sync (direct commit `a32ba0e362`) +- **chore(db):** renumber model_capabilities migration 163 → 169 (cross-branch collision with 163_radar_feed_cache_generated_at) (direct commit `70f5d4cbf9`) +- **docs(cli):** document CLI_PRIME_AGENT_BIN and correct the CLI Agents count (direct commit `9058e39b61`) +- **chore(deps):** refresh runtimes and adopt ESLint 10 ([#11259](https://github.com/diegosouzapw/OmniRoute/pull/11259)) — thanks @backryun +- **refactor(local-corpus):** implement dynamic root resolution and LRU cache ([#11491](https://github.com/diegosouzapw/OmniRoute/pull/11491)) — thanks @raheemuddin786 +- **chore(quality):** prune stale eslint-suppressions entries ([#11596](https://github.com/diegosouzapw/OmniRoute/pull/11596)) +- **chore(ci):** unblock quality gates - env-doc sync + stryker tap.testFiles registration (#9282) ([#11609](https://github.com/diegosouzapw/OmniRoute/pull/11609)) — thanks @Meet6338-X +- **chore(ci):** point the circular-deps gate at dpdm's real JS entrypoint ([#11615](https://github.com/diegosouzapw/OmniRoute/pull/11615)) — thanks @benzntech +- **chore(quality):** rebaseline file-size for #11448/#11495/#11561 growth ([#11630](https://github.com/diegosouzapw/OmniRoute/pull/11630)) +- **deps:** realign onnxruntime-node pin to @huggingface/transformers' 1.24.3 lockstep ([#11633](https://github.com/diegosouzapw/OmniRoute/pull/11633)) — thanks @jonlwheat2-gif +- **test(autoCombo):** port models_dev_tier #11508 guard from node:test to vitest ([#11635](https://github.com/diegosouzapw/OmniRoute/pull/11635)) — thanks @jonlwheat2-gif +- **chore(quality):** rebaseline file-size for #11083/#11631/#11628 growth ([#11636](https://github.com/diegosouzapw/OmniRoute/pull/11636)) +- **chore(lint):** prune stale ESLint suppressions (base-red #11449) ([#11644](https://github.com/diegosouzapw/OmniRoute/pull/11644)) — thanks @jonlwheat2-gif +- **docs(changelog):** aggregate the ten v3.8.50 fragments into CHANGELOG.md ([#11683](https://github.com/diegosouzapw/OmniRoute/pull/11683)) +- **chore(search):** drop unreachable credential sweep after #11642 ([#11687](https://github.com/diegosouzapw/OmniRoute/pull/11687)) — thanks @HouMinXi +- **ci:** add API route TypeScript regression gate ([#11705](https://github.com/diegosouzapw/OmniRoute/pull/11705)) — thanks @Sabeekhann +- **docs(dedup):** correct the CodeQL claim on the tenant-namespace comment ([#11712](https://github.com/diegosouzapw/OmniRoute/pull/11712)) +- **docs(changelog):** add consolidated v3.8.50 stats, top-25 ranking and a .mailmap ([#11715](https://github.com/diegosouzapw/OmniRoute/pull/11715)) +- **docs:** recommend gstack for AI-assisted workflows ([#11770](https://github.com/diegosouzapw/OmniRoute/pull/11770)) — thanks @santosraju99-hub +- **chore(quality):** tighten complexity/cognitive-complexity ratchets to the current tip; land the missed gateways.ts rebaseline ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **docs(i18n):** finish Freepik → Magnific rebrand in locale strings and README ([#11772](https://github.com/diegosouzapw/OmniRoute/pull/11772)) — thanks @turbolego +- **refactor(auto-combo):** fix divergent scoring in combo health reporting ([#11854](https://github.com/diegosouzapw/OmniRoute/pull/11854)) — thanks @jacobsparts +- **chore(changelog):** add missing fragments for #11919/#11918/#11916 ([#11938](https://github.com/diegosouzapw/OmniRoute/pull/11938)) +- **test(check):** escape the runs-on fixture with JSON.stringify, not a quote-only replace ([#11942](https://github.com/diegosouzapw/OmniRoute/pull/11942)) (direct commit `fb7445eaa3`) +- **test(release):** align five suites with the contracts #11933, #11919 and #11876 shipped on release/v3.8.51 ([#11944](https://github.com/diegosouzapw/OmniRoute/pull/11944)) +- **chore(quality):** remove unreachable code and restore test discovery ([#11950](https://github.com/diegosouzapw/OmniRoute/pull/11950)) — thanks @backryun +- **docs(agents):** sync-back landings are fast-forward, never squash ([#11964](https://github.com/diegosouzapw/OmniRoute/pull/11964)) +- **test(infra):** retry recursive temp-dir removal instead of failing a shard on ENOTEMPTY (#11966) ([#11968](https://github.com/diegosouzapw/OmniRoute/pull/11968)) +- **docs(ops):** the .113 heavy-build ceiling is one runner, not two ([#12048](https://github.com/diegosouzapw/OmniRoute/pull/12048)) +- **test(ui):** correct inactive auto-fetch expectation ([#12098](https://github.com/diegosouzapw/OmniRoute/pull/12098)) — thanks @RaviTharuma +- **docs:** document native dependency check escape hatch ([#12101](https://github.com/diegosouzapw/OmniRoute/pull/12101)) — thanks @RaviTharuma +- **test(providers):** regenerate the translate-path golden for OrcaRouter (#11923) ([#12118](https://github.com/diegosouzapw/OmniRoute/pull/12118)) +- **chore(quality):** velocity phase — loosen every numeric baseline by 20% until v4.0, monitor headroom nightly ([#12125](https://github.com/diegosouzapw/OmniRoute/pull/12125)) +- **chore(lint):** batch 0 of #12146 — type the call-log-cap sqlite rows instead of 45 as-any casts ([#12157](https://github.com/diegosouzapw/OmniRoute/pull/12157)) +- **chore(lint):** batch 4 of #12146 — shared/components react-hooks violations resolved ([#12159](https://github.com/diegosouzapw/OmniRoute/pull/12159)) +- **chore(lint):** batch 1 of #12146 — dashboard/cli-code react-hooks violations resolved ([#12160](https://github.com/diegosouzapw/OmniRoute/pull/12160)) +- **chore(lint):** batch 3 of #12146 — dashboard/settings react-hooks violations resolved ([#12162](https://github.com/diegosouzapw/OmniRoute/pull/12162)) +- **chore(lint):** batch 2 of #12146 — resolve the react-hooks compiler violations in dashboard/providers ([#12163](https://github.com/diegosouzapw/OmniRoute/pull/12163)) +- **chore(quality):** register search-432 cooldown test in stryker tap.testFiles ([#12170](https://github.com/diegosouzapw/OmniRoute/pull/12170)) +- **chore(lint):** batch 5 of #12146 — combos, endpoint, provider-stats, api-manager and costs react-hooks violations resolved ([#12174](https://github.com/diegosouzapw/OmniRoute/pull/12174)) +- **build:** add contributor fast profile ([#12192](https://github.com/diegosouzapw/OmniRoute/pull/12192)) — thanks @rafacpti23 +- **docs(readme):** README touch-ups ([#12193](https://github.com/diegosouzapw/OmniRoute/pull/12193)) ([#12194](https://github.com/diegosouzapw/OmniRoute/pull/12194)) ([#12202](https://github.com/diegosouzapw/OmniRoute/pull/12202)) — thanks @rafacpti23 +- **build:** force the webpack bundler in the contributor build profile ([#12197](https://github.com/diegosouzapw/OmniRoute/pull/12197)) — thanks @rafacpti23 +- **build:** skip standalone packaging in the contributor profile ([#12198](https://github.com/diegosouzapw/OmniRoute/pull/12198)) — thanks @rafacpti23 +- **docs(audit):** align every published number with the code and harden check:docs-counts ([#12200](https://github.com/diegosouzapw/OmniRoute/pull/12200)) +- **build:** stub the instrumentation entrypoints in the contributor profile ([#12203](https://github.com/diegosouzapw/OmniRoute/pull/12203)) — thanks @rafacpti23 +- **build:** omit the standalone output target for contributor builds ([#12204](https://github.com/diegosouzapw/OmniRoute/pull/12204)) — thanks @rafacpti23 +- **chore(lint):** batch 6 of #12146 — memory, radar, audit, analytics, cache, usage, activity, home and RequestLoggerV2 react-hooks violations resolved ([#12208](https://github.com/diegosouzapw/OmniRoute/pull/12208)) +- **docs(diagrams):** rename number-carrying diagram files to stable names ([#12210](https://github.com/diegosouzapw/OmniRoute/pull/12210)) +- **docs(dashboard):** redraw onboarding tier-flow SVGs for the real 4-tier model ([#12211](https://github.com/diegosouzapw/OmniRoute/pull/12211)) +- **docs(api):** document every implemented route in openapi.yaml (276 → 692 paths) ([#12212](https://github.com/diegosouzapw/OmniRoute/pull/12212)) +- **chore(lint):** batch 7 of #12146 — final src tail: 37 react-hooks violations across 33 files resolved ([#12244](https://github.com/diegosouzapw/OmniRoute/pull/12244)) +- Revert "docs: recommend gstack for AI-assisted workflows (#11770)" ([#12248](https://github.com/diegosouzapw/OmniRoute/pull/12248)) ([#12249](https://github.com/diegosouzapw/OmniRoute/pull/12249)) +- **docs:** Chaos Mode setup guide + weighted strategy semantics ([#12250](https://github.com/diegosouzapw/OmniRoute/pull/12250)) +- **docs(agents):** protected-surface merge rule — operator approval for agent-instruction files ([#12253](https://github.com/diegosouzapw/OmniRoute/pull/12253)) +- **chore(quality):** register native-codex turn-pin tests in stryker tap.testFiles ([#12263](https://github.com/diegosouzapw/OmniRoute/pull/12263)) +- **chore(quality):** dedupe tap.testFiles entries added by racing base-red fixes ([#12265](https://github.com/diegosouzapw/OmniRoute/pull/12265)) +- **docs(readme):** introduce OmniRouteTray — the macOS menu-bar companion ([#12276](https://github.com/diegosouzapw/OmniRoute/pull/12276)) — thanks @ggiak +- **chore(proxy):** purge the legacy 1proxy residue — sync/rotator modules, dead DB exports, dead settings tab and flag, docs (#12091) ([#12290](https://github.com/diegosouzapw/OmniRoute/pull/12290)) +- **chore(quality):** baseline-headroom skips generated and vendored files in the fileSize worst-file signal ([#12291](https://github.com/diegosouzapw/OmniRoute/pull/12291)) +- **refactor(video):** unify the JPEG frame data-URI contract ([#12322](https://github.com/diegosouzapw/OmniRoute/pull/12322)) +- **chore(deps):** freeze onnxruntime-node and eslint-plugin-react-hooks out of dependabot groups ([#12329](https://github.com/diegosouzapw/OmniRoute/pull/12329)) +- **test(executors):** restore the #10986 reasoning-only fallback guards ([#12364](https://github.com/diegosouzapw/OmniRoute/pull/12364)) — thanks @pacocartones +- **chore(quality):** re-tighten the file-size ratchet to the real LOC (plan 3.8.52 task 0) ([#12411](https://github.com/diegosouzapw/OmniRoute/pull/12411)) +- **test(grok):** format web executor suite ([#12412](https://github.com/diegosouzapw/OmniRoute/pull/12412)) +- **chore(quality):** ratchet open-sse typecheck baseline to zero ([#12418](https://github.com/diegosouzapw/OmniRoute/pull/12418)) +- **test(build):** guard the artifact path policy arrays against duplicates ([#12422](https://github.com/diegosouzapw/OmniRoute/pull/12422)) +- **chore(lint):** adopt eslint-plugin-react-hooks 7.1.1 ([#12428](https://github.com/diegosouzapw/OmniRoute/pull/12428)) +- **chore(providers):** remove a keyless provider integration at its operator's request ([#12440](https://github.com/diegosouzapw/OmniRoute/pull/12440)) +- **docs(providers):** register providers removed at their operator's request and guard against reintroduction ([#12478](https://github.com/diegosouzapw/OmniRoute/pull/12478)) +- **chore(quality):** base fixes — stryker tap.testFiles + node_modules cache key ([#12482](https://github.com/diegosouzapw/OmniRoute/pull/12482)) +- **docs(arch):** one-process recipe for tens of long /v1/responses ([#12493](https://github.com/diegosouzapw/OmniRoute/pull/12493)) — thanks @RaviTharuma +- **chore(quality):** tighten the CodeQL ratchet baseline from 11 to 6 ([#12530](https://github.com/diegosouzapw/OmniRoute/pull/12530)) +- **chore(deps):** pin browserslist override to ^4.28.8 ([#12592](https://github.com/diegosouzapw/OmniRoute/pull/12592)) — thanks @KooshaPari +- **chore(deps):** bump fast-uri to 3.1.7 in the electron lockfile ([#12601](https://github.com/diegosouzapw/OmniRoute/pull/12601)) +- **docs(quality):** document how the CodeQL ratchet refreshes and how to tighten it ([#12611](https://github.com/diegosouzapw/OmniRoute/pull/12611)) +- **test(ci):** pin the openapi-security-tiers two-arm contract with an executing gate test (#12581) ([#12652](https://github.com/diegosouzapw/OmniRoute/pull/12652)) +- **docs(gamification):** align XP Rewards table with code (#12501) ([#12667](https://github.com/diegosouzapw/OmniRoute/pull/12667)) — thanks @KooshaPari +- **refactor(combo):** split executeTarget into gates, attempt, and loop ([#12746](https://github.com/diegosouzapw/OmniRoute/pull/12746)) — thanks @HouMinXi +- **docs(security):** document socket.yml scanner config + CI workflow link (#12575) ([#12764](https://github.com/diegosouzapw/OmniRoute/pull/12764)) — thanks @KooshaPari +- **chore(ci):** guard commit identity in pre-commit to stop author misattribution ([#12772](https://github.com/diegosouzapw/OmniRoute/pull/12772)) +- **deps:** 17 Dependabot bumps — bump electron from 43.4.0 to 43.4.1 in /electron ([#11426](https://github.com/diegosouzapw/OmniRoute/pull/11426)); bump the development group across 1 directory with 8 updates ([#11428](https://github.com/diegosouzapw/OmniRoute/pull/11428)); bump the production group across 1 directory with 11 updates ([#11440](https://github.com/diegosouzapw/OmniRoute/pull/11440)); chore(deps): bump github/codeql-action/init from 4.37.7 to 4.37.8 ([#11925](https://github.com/diegosouzapw/OmniRoute/pull/11925)); chore(deps): bump github/codeql-action from 4.37.7 to 4.37.8 ([#11926](https://github.com/diegosouzapw/OmniRoute/pull/11926)); chore(deps): bump github/codeql-action/analyze from 4.37.7 to 4.37.8 ([#11927](https://github.com/diegosouzapw/OmniRoute/pull/11927)); build(deps): bump github/codeql-action/init from 4.37.8 to 4.37.9 ([#12345](https://github.com/diegosouzapw/OmniRoute/pull/12345)); build(deps): bump github/codeql-action/analyze from 4.37.8 to 4.37.9 ([#12346](https://github.com/diegosouzapw/OmniRoute/pull/12346)); bump the development group across 1 directory with 2 updates ([#12347](https://github.com/diegosouzapw/OmniRoute/pull/12347)); build(deps): bump github/codeql-action from 4.37.8 to 4.37.9 ([#12349](https://github.com/diegosouzapw/OmniRoute/pull/12349)); bump browserslist from 4.28.2 to 4.28.8 ([#12396](https://github.com/diegosouzapw/OmniRoute/pull/12396)); bump the production group across 1 directory with 4 updates ([#12399](https://github.com/diegosouzapw/OmniRoute/pull/12399)); bump @xmldom/xmldom ([#12500](https://github.com/diegosouzapw/OmniRoute/pull/12500)); bump qs from 6.15.2 to 6.16.0 ([#12512](https://github.com/diegosouzapw/OmniRoute/pull/12512)); bump @xmldom/xmldom from 0.9.10 to 0.9.12 ([#12513](https://github.com/diegosouzapw/OmniRoute/pull/12513)); bump fast-uri from 3.1.5 to 3.1.7 ([#12514](https://github.com/diegosouzapw/OmniRoute/pull/12514)); bump @humanfs/node from 0.16.7 to 0.16.8 ([#12515](https://github.com/diegosouzapw/OmniRoute/pull/12515)) +- Clear the `release/v3.8.51` `check:agent-skills-sync` base-red: regenerate `skills/cli-tunnel/SKILL.md` so the `tunnel create [type]` positional that #13009 taught the generator to read is reflected in the committed skill. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` typecheck base-red from #12731: the new `Mode pack ranking gates` candidates in `open-sse/services/autoCombo/__tests__/autoCombo.test.ts` omitted the required `provider`, `model` and `errorRate` fields of `ProviderCandidate`, failing `check:open-sse-typecheck` (and with it `Fast Quality Gates`) on every open PR. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` docs-sync base-red: EURouter (#13025) and GreenPT (#13024) took the live provider count to 358, leaving 7 STRICT drifts (`PROVIDER_REFERENCE.md`, 4 diagrams, 2 tier-flow images) plus the `AGENTS.md` / `llm.txt` / `package.json` count claims stale. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- **test(models):** Pin `CATALOG_BUILD_TIMEOUT_MS` in the #12058 canonical-catalog regression so a cold catalog build racing the 8s default bound no longer flakes the suite with a 500 ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Allow `@omniroute/opencode-plugin-v2/` in the npm pack policy — [#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870) shipped the v2 plugin beside its v1 sibling without widening the allowlist, so every packed file under it read as an unexpected artifact and failed `check:pack-policy` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Point the release-green validator's pack gate at the tree it is validating — the build-provenance guard defaulted to `origin/main`, which a release branch tip can never be an ancestor of mid-cycle, so `Package artifact` reported a false red on every run and the tarball boot-smoke cascaded off it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **docs(checks):** keep doc counts honest — headings, rankings, catalog, weights, quality gate and scoring diagram now covered ([#12507](https://github.com/diegosouzapw/OmniRoute/pull/12507)) — thanks @maxmad64bis +- **chore(lifecycle):** `check:model-lifecycle` now also diffs `DEFAULT_DEGRADATION_MAP` (the background-task redirect table) against the vendor lifecycle snapshot, refusing a retired id as source or target, with a table-driven unit test beside it. Three rows whose source the vendor had retired — `claude-sonnet-4-20250514`, `gemini-3-pro-preview` and `gpt-5.1-codex` (whose target `gpt-5.1-codex-mini` is retired too) — were dead code, since `checkLifecycle` answers 410 before the redirect runs; they are dropped (#12535 — thanks @pacocartones) +- **test(compression):** cover idle worker eviction at the resource level — the pool must call `terminate()` and must not retain the worker's `MessagePort`, complementing the `exit`-event assertion added with the fix ([#13371](https://github.com/diegosouzapw/OmniRoute/pull/13371)) +- **chore(quality):** raise the `imageGeneration.ts` file-size ceiling for the image-only-model guard that clears the #12945 base-red ([#13392](https://github.com/diegosouzapw/OmniRoute/pull/13392)) +- **chore(quality):** type the combo-test route's JSON response bodies instead of casting them to `any`, drop the now-empty suppression entry, and remove the `hasPerModelQuota` import `chatCore.ts` stopped using when the failure-classification helper was extracted ([#13379](https://github.com/diegosouzapw/OmniRoute/pull/13379)) +- **chore(electron):** the desktop bundle no longer ships the root-level files of every translated docs mirror (`README.md`, `llm.txt`, `CLAUDE.md`, `GEMINI.md`, `CONTRIBUTING.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`) — the packaged app only reads `docs/i18n//docs/**`, which stays. Saves ~11 MB on top of the translated CHANGELOGs already pruned. (#0000) ([#13235](https://github.com/diegosouzapw/OmniRoute/pull/13235)) +- **chore(quality):** retighten the `src/sse/handlers/chat.ts` unused-vars suppression count to the 8 violations that actually remain ([#13385](https://github.com/diegosouzapw/OmniRoute/pull/13385)) +- Bring the documented counts back in line with the code: 171 migrations (was 169) and 20 routing strategies (was 19), plus the regenerated `cli-tunnel` skill reference. ([#12970](https://github.com/diegosouzapw/OmniRoute/pull/12970)) +- Update the documented migration count to 172 after the call-logs provider-stats indexes landed. ([#13209](https://github.com/diegosouzapw/OmniRoute/pull/13209)) +- **chore(ci):** two gates that close the blind spots behind the exclusions above. `check:vitest-exclusions` requires every Vitest exclusion to name a tracking issue and to appear in `config/quality/vitest-exclusions.json` — the previous list grew to 62 files behind a comment pointing at an issue that had been closed for a month. `check-new-key-coverage` requires a key newly added to `en.json` to reach every locale; the existing coverage gate is a percentage floor per locale, so eleven absent keys out of ~13,000 left it at 99.9% while a whole feature shipped untranslated in nine languages. Both are diff-aware, so pre-existing debt stays frozen and neither needed a migration to turn on. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **build:** root layout no longer loads Inter from `next/font/google`, so a production image build does not need fonts.googleapis.com ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **docs(i18n):** bring the eight new `llm.txt` mirrors (km, kn, ml, my, ne, or, pa, si) up to the current provider and migration counts, so `check-docs-sync` stops rejecting every commit ([#13674](https://github.com/diegosouzapw/OmniRoute/pull/13674)) +- **refactor(ui):** flow surfaces (home topology, combo live studio, compression cockpit/waterfall/nodes, token-health badge) express state through the theme-aware `--orch-status-*` tokens instead of fixed dark-mode hex, so green/red/amber/grey stay legible in the light theme; dark mode is byte-identical. Categorical palettes (routing-strategy hues, compression-layer pills, provider brand colors) deliberately stay hex ([#12378](https://github.com/diegosouzapw/OmniRoute/issues/12378)) ([#13203](https://github.com/diegosouzapw/OmniRoute/pull/13203)) +- **chore(tests):** 51 test files that had been excluded from Vitest are running again, restoring roughly 350 assertions to the blocking suite. Each was measured individually first: of the 62 files parked behind the `// #8618 — pre-existing failure` comment, 51 pass against the current tree with no source change, so the exclusions had outlived the failures they were added for. The 11 that genuinely still fail stay excluded, but now point at a live tracker (#13204) rather than at #8618, which was closed in August while the list it tracked kept growing. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **chore(quality):** drop two ESLint suppression entries whose violations no longer exist, so `eslint --suppressions-location` stops rejecting every commit that touches the surrounding files ([#13384](https://github.com/diegosouzapw/OmniRoute/pull/13384)) +- **chore(release):** bring main's post-tag fixes into release/v3.8.51 — sync-back step 2/2 (direct commit `21c488f210`) +- **chore(deps):** patch toml and esbuild in the opencode plugin lockfiles ([#12965](https://github.com/diegosouzapw/OmniRoute/pull/12965)) +- **docs(changelog):** reconcile the v3.8.51 living section — fold 366 fragments, cover every cycle commit, credit every contributor ([#12971](https://github.com/diegosouzapw/OmniRoute/pull/12971)) +- **test(plugin):** wait for the v2 lazy-refresh tier instead of sleeping past it ([#12984](https://github.com/diegosouzapw/OmniRoute/pull/12984)) +- **chore(quality):** rebaseline chatCore.ts after the non-streaming regression fixes ([#13045](https://github.com/diegosouzapw/OmniRoute/pull/13045)) +- **docs(changelog):** add the missing bullet marker to the DeepSeek PoW fragment ([#13200](https://github.com/diegosouzapw/OmniRoute/pull/13200)) +- **chore(deps):** drain the Dependabot queue — 10 of 13 alerts ([#13213](https://github.com/diegosouzapw/OmniRoute/pull/13213)) +- **chore(ci):** make worktrees carry the hooks that enforce the identity gate ([#13236](https://github.com/diegosouzapw/OmniRoute/pull/13236)) +- **chore(deps):** pin csv-parse to ^7.0.2 via overrides (#13117) ([#13300](https://github.com/diegosouzapw/OmniRoute/pull/13300)) — thanks @KooshaPari +- **chore(deps):** bump hono from ^4.12.34 to ^4.13.7 (#13148) ([#13301](https://github.com/diegosouzapw/OmniRoute/pull/13301)) — thanks @KooshaPari +- **chore(deps):** pin joi to ^18.2.8 via overrides (#13085) ([#13302](https://github.com/diegosouzapw/OmniRoute/pull/13302)) — thanks @KooshaPari +- **docs(providers):** add Microsoft 365 Copilot (BizChat) provider guide (#12779) ([#13340](https://github.com/diegosouzapw/OmniRoute/pull/13340)) — thanks @KooshaPari +### 🙌 Contributors + +Thanks to everyone whose work landed in v3.8.51: + +| Contributor | PRs / Issues | +| --- | --- | +| [@5dive-bot](https://github.com/5dive-bot) | #11852 | +| [@abhisheksharma2411](https://github.com/abhisheksharma2411) | #11547, #11684, #11959, #12209 | +| [@adevwithpurpose](https://github.com/adevwithpurpose) | #11464 | +| [@adivekar-utexas](https://github.com/adivekar-utexas) | #12015, #12043, #12090, #12138 | +| [@AIB1TAL0S](https://github.com/AIB1TAL0S) | #12103 | +| [@alltomatos](https://github.com/alltomatos) | #11448 | +| [@alvinveroy](https://github.com/alvinveroy) | #11857, #12027, #12165 | +| [@amaleta](https://github.com/amaleta) | #11906, #11934 | +| [@amartinawi](https://github.com/amartinawi) | #11575 | +| [@AndrianBalanescu](https://github.com/AndrianBalanescu) | #11083, #11830, #11834, #11844 | +| [@anhtahaylove](https://github.com/anhtahaylove) | #13091, #13092, #13093, #13096, #13097, #13100, #13106, #13109, #13114, #13155, #13166, #13171, #13175, #13187, #13196 | +| [@aniruddhaadak80](https://github.com/aniruddhaadak80) | #11760 | +| [@app](https://github.com/app) | #12554 | +| [@arminanton](https://github.com/arminanton) | #11461, #11513 | +| [@AStupidBear](https://github.com/AStupidBear) | #11584 | +| [@atescivitci-cmd](https://github.com/atescivitci-cmd) | #12105 | +| [@b3nw](https://github.com/b3nw) | #11970, #11971, #12147 | +| [@backryun](https://github.com/backryun) | #11259, #11950, #12075, #12076, #12078, #12079, #12081, #12082, #12181, #12228, #12239, #12255, #12258, #12277, #12367, #12423, #12524, #12538 | +| [@benjaminkitt](https://github.com/benjaminkitt) | #11747 | +| [@benzntech](https://github.com/benzntech) | #11614, #11615 | +| [@Bl0ck154](https://github.com/Bl0ck154) | #11948, #11951, #11952, #11953, #11954 | +| [@brick30llc-ctrl](https://github.com/brick30llc-ctrl) | #11752, #12242, #12262 | +| [@Chewji9875](https://github.com/Chewji9875) | #11455, #11875, #12028 | +| [@cheynetom](https://github.com/cheynetom) | #12058 | +| [@davidebaraldo](https://github.com/davidebaraldo) | #12222, #12304 | +| [@davidlinfr](https://github.com/davidlinfr) | #6390 | +| [@ddarkr](https://github.com/ddarkr) | #12124 | +| [@Deftera186](https://github.com/Deftera186) | #11809 | +| [@dpozimski](https://github.com/dpozimski) | #11945 | +| [@DW-MediaLab](https://github.com/DW-MediaLab) | #12229 | +| [@echel0nn](https://github.com/echel0nn) | #11923 | +| [@f9td56dbgh-hub](https://github.com/f9td56dbgh-hub) | #11660 | +| [@fabioluissilva](https://github.com/fabioluissilva) | #11991 | +| [@foreveryh](https://github.com/foreveryh) | #12177 | +| [@geek007git](https://github.com/geek007git) | #12115, #12116, #12117, #12120, #12122 | +| [@geekyNads](https://github.com/geekyNads) | #11784, #12266 | +| [@ggiak](https://github.com/ggiak) | #11561, #12276, #12350, #12402 | +| [@gonisulaimann](https://github.com/gonisulaimann) | #12368, #12369, #12371 | +| [@Gorillaz322](https://github.com/Gorillaz322) | #12207 | +| [@groovecityJO](https://github.com/groovecityJO) | #12682 | +| [@hartmark](https://github.com/hartmark) | #11434, #11452, #11473, #11499, #11703, #11983, #11984, #11985, #11986, #11988, #11989, #11990, #11994, #12221, #12293, #12445, #12446, #12447, #12448, #12460, #12461, #12623, #12646, #12650, #12680, #12717, #12718, #12727, #12741, #12854 | +| [@hizzt](https://github.com/hizzt) | #11894 | +| [@hongnoul](https://github.com/hongnoul) | #11484 | +| [@HouMinXi](https://github.com/HouMinXi) | #11411, #11414, #11512, #11518, #11520, #11641, #11642, #11643, #11687, #11779, #11849, #11850, #11851, #11915, #11916, #11918, #11919, #11920, #12013, #12017, #12033, #12042, #12106, #12139, #12166, #12169, #12171, #12205, #12213, #12312, #12325, #12487, #12488, #12495, #12504, #12557, #12566, #12590, #12591, #12624, #12626, #12632, #12637, #12678, #12696, #12697, #12711, #12733, #12746, #12767, #12770, #12789, #12803, #12805, #12811, #12866, #12868, #12899, #12926, #12934, #12950, #12951, #12974, #13001, #13006, #13011, #13017, #13026, #13027, #13034, #13035, #13038, #13042, #13050, #13060, #13061, #13069, #13107, #13120, #13136, #13195, #13197 | +| [@Hsia97](https://github.com/Hsia97) | #11624 | +| [@jacobsparts](https://github.com/jacobsparts) | #11854, #12155, #12167 | +| [@joglomedia](https://github.com/joglomedia) | #11980 | +| [@jonlwheat2-gif](https://github.com/jonlwheat2-gif) | #11443, #11450, #11549, #11567, #11608, #11633, #11635, #11640, #11644, #11671, #11672, #11673, #11674, #11675, #11676, #11677, #11706, #11783, #12051, #12052, #12053, #12055 | +| [@kanade-hoshino](https://github.com/kanade-hoshino) | #12180 | +| [@Karan825](https://github.com/Karan825) | #11861 | +| [@KaspaPulse](https://github.com/KaspaPulse) | #11389, #11469, #11628, #11666, #11910 | +| [@keeltrace](https://github.com/keeltrace) | #12080, #12223 | +| [@killer30001000](https://github.com/killer30001000) | #12178 | +| [@KooshaPari](https://github.com/KooshaPari) | #12470, #12592, #12667, #12699, #12703, #12706, #12764, #12769, #12771, #13300, #13301, #13302, #13340, #13401, #13403, #13404, #13405, #13406, #13407, #13409, #13410, #13411, #13412, #13413, #13414, #13418, #13424, #13427, #13433, #13523, #13524, #13525, #13528, #13531, #13532, #13534, #13539, #13543, #13545, #13546, #13547, #13550, #13553 | +| [@kriptoburak](https://github.com/kriptoburak) | #11370 | +| [@KrzysiekSko](https://github.com/KrzysiekSko) | #12425, #12432, #12673 | +| [@marcelokarval](https://github.com/marcelokarval) | direct commit / report | +| [@maxmad64bis](https://github.com/maxmad64bis) | #1622, #11435, #11437, #11441, #11537, #11550, #11553, #11555, #11557, #11812, #11842, #11843, #11903, #12151, #12214, #12215, #12218, #12226, #12314, #12316, #12317, #12318, #12319, #12320, #12321, #12507, #12715, #12731, #12744, #12786, #12787, #12788, #12790, #12792, #12794, #12795, #12828, #12832, #12853, #12857, #12870, #12937, #12941, #12975, #13141, #13142, #13146, #13147 | +| [@mdigitalbh81](https://github.com/mdigitalbh81) | #12240 | +| [@Meet6338-X](https://github.com/Meet6338-X) | #11598, #11609, #12110 | +| [@morpheus9393](https://github.com/morpheus9393) | #11943 | +| [@MumuTW](https://github.com/MumuTW) | #11492, #11502, #11506, #11507, #11626, #11675, #11685, #11728, #11746, #11888, #11889, #11890, #11892 | +| [@Neuron-Mr-White](https://github.com/Neuron-Mr-White) | #11622, #11801, #12256 | +| [@NightStalker-87](https://github.com/NightStalker-87) | #12183 | +| [@NoxzRCW](https://github.com/NoxzRCW) | #11879, #11880, #11881, #11882, #11883, #11935 | +| [@ntdat812](https://github.com/ntdat812) | #11585, #12095 | +| [@ntdatt812](https://github.com/ntdatt812) | #11368, #11573, #11574, #11576, #11577, #11580, #11582, #11583, #11588, #11589, #11590, #11591, #11592, #11593, #11672, #12177, #12180, #12873, #12918, #12920, #12921, #12925, #12930, #13007, #13009, #13024, #13025, #13083, #13087, #13101, #13104, #13110 | +| [@official-burak](https://github.com/official-burak) | #11542 | +| [@opensource-elearning](https://github.com/opensource-elearning) | #12179, #12189, #12278, #12286 | +| [@oyi77](https://github.com/oyi77) | #11408, #11409, #11421, #11505, #11677, #12036, #12110 | +| [@pacocartones](https://github.com/pacocartones) | #11521, #11522, #11527, #11528, #11529, #11530, #11531, #11532, #11533, #11534, #11595, #11599, #11603, #11604, #11605, #11607, #11610, #11676, #11714, #11716, #11718, #11767, #11838, #11860, #11862, #11869, #11871, #11872, #11873, #11903, #11906, #11921, #11934, #12356, #12358, #12359, #12360, #12361, #12362, #12364, #12365, #12373, #12374, #12375, #12376, #12377, #12379, #12380, #12381, #12386, #12387, #12389, #12390, #12394, #12395, #12397, #12401, #12403, #12404, #12522, #12523, #12535, #12536, #12540, #12541, #12543, #12545, #12548, #12549, #12550, #12551, #12552, #12647, #12651, #12653, #13055 | +| [@patrykkopycinski](https://github.com/patrykkopycinski) | #11936, #11937, #12224 | +| [@PauloFH](https://github.com/PauloFH) | #11509 | +| [@PauloHSOliveira](https://github.com/PauloHSOliveira) | #12241 | +| [@ponkcore](https://github.com/ponkcore) | #12054 | +| [@Prajeeth-12](https://github.com/Prajeeth-12) | #11634, #12046 | +| [@quiterunner-commits](https://github.com/quiterunner-commits) | #12131, #12143 | +| [@rafacpti23](https://github.com/rafacpti23) | #11554, #11558, #12192, #12197, #12198, #12202, #12203, #12204 | +| [@ragnar-claude](https://github.com/ragnar-claude) | #11564 | +| [@raheemuddin786](https://github.com/raheemuddin786) | #11491, #11839, #11840, #11841, #12003, #12230, #12231, #12232, #12233, #12234 | +| [@rahilmavani](https://github.com/rahilmavani) | #11761 | +| [@Rahulsharma0810](https://github.com/Rahulsharma0810) | #11771 | +| [@RaviTharuma](https://github.com/RaviTharuma) | #11710, #11727, #11797, #11798, #11802, #11805, #11806, #11811, #12098, #12099, #12101, #12449, #12452, #12472, #12473, #12493, #12533, #12607, #12628, #12631, #12636, #12875, #12876, #12880, #12882, #12884 | +| [@rezjalibd](https://github.com/rezjalibd) | #12186 | +| [@RhianB14](https://github.com/RhianB14) | #11692 | +| [@rifqiawl](https://github.com/rifqiawl) | #11517, #11519, #11969 | +| [@rqzbeh](https://github.com/rqzbeh) | #11390 | +| [@Sabeekhann](https://github.com/Sabeekhann) | #11699, #11705 | +| [@santosraju99-hub](https://github.com/santosraju99-hub) | #11755, #11770 | +| [@Seramicx](https://github.com/Seramicx) | #11568 | +| [@Siva010](https://github.com/Siva010) | #12191 | +| [@solstxce](https://github.com/solstxce) | #11597 | +| [@soroush5](https://github.com/soroush5) | #12691 | +| [@tenshiak](https://github.com/tenshiak) | #12279 | +| [@TheDemonTuan](https://github.com/TheDemonTuan) | #11468, #11470, #11471, #11482, #11548, #11758, #11775, #11814 | +| [@thomasmaerz](https://github.com/thomasmaerz) | #12834 | +| [@tuandinh0801](https://github.com/tuandinh0801) | #11454 | +| [@turbolego](https://github.com/turbolego) | #11621, #11762, #11772, #11774, #11781, #12216 | +| [@Tushar49](https://github.com/Tushar49) | #11392 | +| [@ujjawalkaushik1110](https://github.com/ujjawalkaushik1110) | #11863 | +| [@vermasomesh835](https://github.com/vermasomesh835) | #11794 | +| [@vsd2807](https://github.com/vsd2807) | #11565, #11619 | +| [@wahidsadik371-coder](https://github.com/wahidsadik371-coder) | #12126 | +| [@watchingdogs](https://github.com/watchingdogs) | #12031 | +| [@wildcard](https://github.com/wildcard) | #11620 | +| [@xiaoyaner0201](https://github.com/xiaoyaner0201) | #11460, #11662, #12083 | +| [@Xxx91n](https://github.com/Xxx91n) | #11690 | +| [@yourspraveen](https://github.com/yourspraveen) | #11146 | +| [@yxyxy](https://github.com/yxyxy) | #11671 | +| [@ZaimMarzuki](https://github.com/ZaimMarzuki) | #11960 | +| [@Zartharas](https://github.com/Zartharas) | #11340 | +| [@zero-executioner](https://github.com/zero-executioner) | #11631 | +| [@diegosouzapw](https://github.com/diegosouzapw) | maintainer | + +--- ## [3.8.50] — 2026-08-25 _Living section — regenerated 2026-08-12 from all cycle commits (cycle open `ed2db6cb19` → tip). Bullets carry the merged PR and its author; direct pushes listed separately._ diff --git a/docs/i18n/lv/CHANGELOG.md b/docs/i18n/lv/CHANGELOG.md index bcb98ae1fd..a82f06497b 100644 --- a/docs/i18n/lv/CHANGELOG.md +++ b/docs/i18n/lv/CHANGELOG.md @@ -95,16 +95,1119 @@ ## [3.8.51] — TBD -_Living section — cycle opened at the v3.8.50 freeze (parallel-cycle model). Bullets are aggregated from `changelog.d/` fragments at each `/generate-release` phase._ +_Living section — reconciled 2026-09-15 from all cycle commits (`091589089c` → `c0f92ec98a`, 916 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 | **123** | +| 📝 Commits in the cycle | **916** | +| 🔀 Pull requests referenced | **892** | +| 📋 Changelog entries | **919** | +| 🙌 Contributors credited in entries | **121** | +| 🤖 Automated dependency commits | 18 | + +**Entries by type** + +| Type | Count | +| --- | ---: | +| 🐛 Fixes | 592 | +| ✨ Features | 131 | +| 🧹 Chore | 93 | +| 📚 Docs | 42 | +| 🧪 Tests | 32 | +| ♻️ Refactor | 8 | +| 🏗️ Build | 7 | +| ⚡ Performance | 7 | +| ⚙️ CI | 2 | +| ⏪ Reverts | 2 | +| 📦 Dependencies | 2 | +| 🔒 Security | 1 | + +### 🏆 Top 25 contributors this cycle + +_By commits in `091589089c..c0f92ec98a`, author identities consolidated via `.mailmap` and the merged PR's GitHub login. Bots excluded._ + +| # | Contributor | Commits | +| ---: | --- | ---: | +| 🥇 | diegosouzapw | 323 | +| 🥈 | Bob.Hou (@HouMinXi) | 79 | +| 🥉 | Paco Cartones (@pacocartones) | 77 | +| 4 | Dizzle (@maxmad64bis) | 49 | +| 5 | Koosha Paridehpour (@KooshaPari) | 43 | +| 6 | Nguyen Thanh Dat (@ntdatt812) | 31 | +| 7 | Ravi Tharuma (@RaviTharuma) | 26 | +| 8 | Markus Hartung (@hartmark) | 22 | +| 9 | Webman (@jonlwheat2-gif) | 22 | +| 10 | backryun | 17 | +| 11 | anhtahaylove | 15 | +| 12 | Rafa Martins (@rafacpti23) | 10 | +| 13 | Syed Raheemuddin (@raheemuddin786) | 10 | +| 14 | Nguyễn Viết Tuấn (@TheDemonTuan) | 8 | +| 15 | MumuTW | 7 | +| 16 | Paijo (@oyi77) | 7 | +| 17 | Tobias Andersen (@turbolego) | 6 | +| 18 | Bl0ck (@Bl0ck154) | 5 | +| 19 | SHANMUGAPRIYAN (@geek007git) | 5 | +| 20 | KaspaPulse | 5 | +| 21 | Abhishek Sharma (@abhisheksharma2411) | 4 | +| 22 | Abhishek Divekar (@adivekar-utexas) | 4 | +| 23 | Andrew B. (@AndrianBalanescu) | 4 | +| 24 | NoxzRCW | 4 | +| 25 | opensource-elearning | 4 | ### ✨ New Features +- **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). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- Added Google AI Studio Gemini batch text-to-speech support through `POST /v1/audio/speech`. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- 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. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(routing):** subscription-first auto groupings — `auto/subscription` routes only through plan-included connections with a documented hard-stop overage and fails closed on exhaustion, while `auto/thrifty` orders the pool `subscription → keyless → free → cheap → premium` and steps up one rung at a time as each is exhausted. Billing class comes from a curated per-connection catalog (uncurated is treated as metered, never plan-included), both reuse STRICT_ZERO_COST's per-connection verification, and a quota reading whose `resetAt` has passed is now refreshed regardless of TTL so routing returns to plan capacity as soon as it resets ([#11146](https://github.com/diegosouzapw/OmniRoute/pull/11146)) — thanks @yourspraveen +- **feat(providers):** publish a management-authenticated versioned web-session credential contract from OmniRoute's canonical browser credential metadata ([#11340](https://github.com/diegosouzapw/OmniRoute/pull/11340)) — thanks @Zartharas +- **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)) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add Xquik X search with typed results, credential validation, REST routing, and MCP selection ([#11370](https://github.com/diegosouzapw/OmniRoute/pull/11370)) — thanks @kriptoburak +- **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)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(dashboard):** surface durable exclusive managed leases in the existing Sessions view, keeping leased clients visible across idle gaps while marking connections with in-flight work as active ([#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389)) — thanks @KaspaPulse +- **build(bun):** allow Turbopack bundler flag on Bun 1.4+ with configurable Webpack fallback ([#11471](https://github.com/diegosouzapw/OmniRoute/pull/11471)) — thanks @TheDemonTuan +- feat(api): add an opt-in `modelVisibilityAllowlist`/`modelVisibilityDenylist` settings pair to curate exactly which models `/v1/models` advertises, mirrored into every `auto/*` combo candidate pool so a denied model cannot be routed to via combo selection either (#11481) ([#11997](https://github.com/diegosouzapw/OmniRoute/pull/11997)) +- **feat(rankings):** the Free Provider Rankings page shows what each provider actually served over the last 24 h. It ranked by ELO alone, which left a provider that answers every call with an error in first place; the usage data was already served by the API but never requested. A provider with too small a sample shows a dash, not a number ([#11546](https://github.com/diegosouzapw/OmniRoute/pull/11546)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **feat(guardrails):** enforce a bounded, deterministic contract for Video Bridge transcripts — 256 cues, 4096 input code units and 4 KiB UTF-8 per cue, 64 KiB total text, malformed-Unicode rejection, focus-window scoping, cross-source reconciliation with contributing-source metadata, and a structural provenance trust boundary so caller JSON can never self-assert `embedded`/`audio-bridge` provenance ([#11652](https://github.com/diegosouzapw/OmniRoute/issues/11652)) ([#12009](https://github.com/diegosouzapw/OmniRoute/pull/12009)) +- **feat(video):** orchestrate optional Video Bridge audio extraction and Audio Bridge STT behind a dual opt-in (operator setting AND per-request signal) — a new loopback-only broker `mode=audio` operation shares the frame path's exact process queue, deadline, AbortSignal, and byte budgets to extract a bounded mono 16 kHz PCM WAV from the same already-downloaded video, then reuses the existing Audio Bridge transcription boundary; provider segment timing is preserved when available and marked coarse otherwise, and every failure degrades to a visual-only-safe partial instead of throwing (#11654). ([#12012](https://github.com/diegosouzapw/OmniRoute/pull/12012)) +- Add a tenant-bound Video Bridge drill-down lifecycle on top of the existing secure cache substrate: opaque hashed handles (never raw session/video identifiers), preview/standard/detail multiresolution variants resampled on read, response pagination capped at 8 frames and 32 MiB, and a new authenticated `/api/v1/video-bridge/drilldown` consumer route that stays disabled for remote access by default and denies cross-key access with the same response as a nonexistent handle (no existence oracle). ([#12006](https://github.com/diegosouzapw/OmniRoute/pull/12006)) +- **test(video):** Add the Video Bridge FU-07/FU-09 promotion-evidence harness (#11656) — a frozen Zod manifest schema covering the 8 required scenario kinds (static scenes, rapid cuts, late facts, fades, blur, small text, close events, visual prompt injection) with a minimum of 3 repetitions per case, deterministic declarative fixture recipes (`videoBridgePromotionFixtures.ts`), a pure medians/p95 metrics aggregator, a pure FU-07/FU-09 promotion-verdict evaluator applying the ticket's exact thresholds (missing token usage always holds), a digest-only persistence layer that never retains raw media or raw model responses, and a versioned per-model promotion allowlist shipped empty with every model defaulting to `hold`. The FU-07/FU-09 promotion verdicts themselves remain HOLD — they require a real evidence run against real models on VPS 192.168.0.15. ([#12008](https://github.com/diegosouzapw/OmniRoute/pull/12008)) +- **feat(video bridge):** "embedded" transcript provenance can now be legitimately earned instead of merely asserted — a bounded, allowlisted (`mov_text`/`subrip`/`webvtt`) subtitle probe runs through the loopback-only Video Bridge broker (at most 2 streams, 10s subdeadline bounded by the request deadline, 256 KiB output, 4096-code-unit lines), normalized through a bounded, ReDoS-safe WebVTT parser and Zod-validated end to end. The adapter always resolves to an explicit `success`/`absent`/`transient_failure` outcome — a subtitle failure never breaks the visual description path, and only a fingerprint-verified broker response (never a caller-declared label) can produce embedded cues (#11659). ([#12011](https://github.com/diegosouzapw/OmniRoute/pull/12011)) +- Default new Antigravity-family connections (agy CLI imports and Antigravity OAuth connects) to model auto-sync, so live model discovery lands in the synced catalog and `/v1/models` picks up freshly released upstream models (e.g. Gemini 3.7 Flash tiers) without code changes. Existing connections keep their current setting; the per-connection dashboard toggle remains the opt-out. (#11685 — thanks @MumuTW) ([#11887](https://github.com/diegosouzapw/OmniRoute/pull/11887)) +- **feat(ui):** enable React Compiler (`reactCompiler: true` + `babel-plugin-react-compiler`) for automatic memoization at build time ([#11783](https://github.com/diegosouzapw/OmniRoute/pull/11783)) — thanks @jonlwheat2-gif +- **feat(providers):** add SeekAi (`seekai.cc`) as an OpenAI-compatible New-API gateway — catalog id `seekai` (alias `ska`), `https://seekai.cc/v1`, live `/v1/models` via `passthroughModels`, aggregator-list membership so New-API balance detection can opt in. No referral/aff codes. ([#11786](https://github.com/diegosouzapw/OmniRoute/issues/11786)) ([#12557](https://github.com/diegosouzapw/OmniRoute/pull/12557)) — thanks @HouMinXi +- **feat(zai):** add GLM-5.3-Flash Coding Plan support (1M context, 128K output, vision, `low|high|max` reasoning) and route `zai` GLM-5.3-family API-key traffic through the OpenAI-compatible Coding Plan endpoint with native thinking defaults ([#11801](https://github.com/diegosouzapw/OmniRoute/pull/11801)) — thanks @Neuron-Mr-White +- **feat(combo):** choose how combo models are ordered — manual, provider, score, or name — via a sort control in the dashboard builder, persisted in `config.modelSort` and re-applied on load and after add ([#11812](https://github.com/diegosouzapw/OmniRoute/pull/11812)) — thanks @maxmad64bis +- **feat(free):** custom models can be marked free-tier via `customModels[].isFree`; `isFreeModel()` is the first door and `hidePaidModels` respects it even for providers outside the free budget ([#11843](https://github.com/diegosouzapw/OmniRoute/pull/11843)) — thanks @maxmad64bis +- **feat(nodejs):** add `5dive` as a `configure` target — `omniroute configure 5dive` / `omniroute setup-5dive` write a 5dive auth profile that points an agent fleet's `claude` seats at OmniRoute, with the root-only write, the loopback-vs-`https` endpoint rule and the per-seat model pin handled explicitly ([#11852](https://github.com/diegosouzapw/OmniRoute/pull/11852)) — thanks @5dive-bot +- **feat(sse):** treat `max` as a first-class reasoning-effort tier and clamp per model family (GLM 5.1+/DeepSeek V4+/Kimi K3+ keep native `max`; o1/MiniMax/Grok/Muse Spark clamp to their upstream ceiling) ([#11875](https://github.com/diegosouzapw/OmniRoute/pull/11875)) — thanks @Chewji9875 +- **feat(providers):** the provider plugin manifest now advertises a `usage-fetch` capability for the 40 providers that have a wired usage/quota fetcher, so external dashboards can read it from `GET /api/v1/provider-plugin-manifest` instead of parsing `open-sse/services/usage.ts` after every release. Discovery only — no new fetcher, no quota change, and the Dashboard quota widget stays gated by `USAGE_SUPPORTED_PROVIDERS`. `USAGE_FETCHER_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/fetcherProviders.ts`) and is re-exported from `services/usage.ts`, keeping the manifest module a light leaf instead of pulling the ~490-module usage dispatcher into the manifest route. ([#11903](https://github.com/diegosouzapw/OmniRoute/pull/11903)) — thanks @maxmad64bis / @pacocartones +- **feat(plugins):** `OMNIROUTE_PLUGINS_DIR` sets the directory the runtime plugin scanner reads — and the root the plugin manager installs into — overriding the `HOME`-derived default, so a Docker/K8s deployment can point straight at its bind-mounted plugin tree instead of moving `HOME` just to relocate the scan path. An image that exports no home no longer scans `/tmp/.omniroute/plugins` in silence: the resolved directory is logged once at startup as `scanner.dir_resolved`, naming the input that won. Unset, behaviour is unchanged. Distinct from the CLI-only `OMNIROUTE_PLUGIN_PATH`, which finds `omniroute-cmd-*` command packages and never reached this scanner ([#11906](https://github.com/diegosouzapw/OmniRoute/pull/11906)) — thanks @amaleta / @pacocartones +- **feat(leases):** add an explicit owner-authenticated status action that returns only the active lease's privacy-safe configured connection and provider labels, with generation fencing and no credential or internal-id disclosure ([#11910](https://github.com/diegosouzapw/OmniRoute/pull/11910)) — thanks @KaspaPulse +- **feat(providers):** import-from-file modal shows per-row API errors and ships a downloadable CSV template ([#12071](https://github.com/diegosouzapw/OmniRoute/issues/12071)) ([#12504](https://github.com/diegosouzapw/OmniRoute/pull/12504)) — thanks @HouMinXi +- **feat(providers):** dashboard search matches connection name and `baseUrl` so imported OpenAI-compat nodes surface on the provider card ([#12108](https://github.com/diegosouzapw/OmniRoute/issues/12108)) ([#12495](https://github.com/diegosouzapw/OmniRoute/pull/12495)) — thanks @HouMinXi +- **feat(routing):** add a `score` Auto router strategy that selects the highest configured weighted score and reuses `explorationRate`. ([#12155](https://github.com/diegosouzapw/OmniRoute/pull/12155)) — thanks @jacobsparts +- Add a runtime feature flag to disable universal context handoffs globally without changing the default behavior. ([#12167](https://github.com/diegosouzapw/OmniRoute/pull/12167)) — thanks @jacobsparts +- **feat(providers):** the provider plugin manifest now also advertises a `usage-supported` capability for the 46 providers whose usage API is accepted by the server and Dashboard routes, so integrators can distinguish "the server will serve quota for this provider" from "a fetcher is wired" without reading TypeScript. Discovery only — no fetcher or quota change. `usage-fetch` resolves on id or alias (the usage dispatcher accepts both); `usage-supported` resolves on id alone, matching the runtime guard `USAGE_SUPPORTED_PROVIDERS.includes(providerId)`. `USAGE_SUPPORTED_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/supportedProviders.ts`) and is re-exported from `providers.ts`, mirroring the `fetcherProviders` leaf from #11903 and keeping the manifest a light module. ([#12214](https://github.com/diegosouzapw/OmniRoute/pull/12214)) — thanks @maxmad64bis +- **feat(rankings):** order Free Provider Rankings by what each provider actually served — `GET /api/free-provider-rankings?sortBy=reliability` and a "Most reliable first" toggle on the page. Providers with too few calls to state a success rate keep their score order below the measured ones; the default order is unchanged ([#12218](https://github.com/diegosouzapw/OmniRoute/pull/12218)). — thanks @maxmad64bis +- **perf(sse):** defer `cloneLogPayload()` in the structured SSE collector until after the `maxEvents`/`maxBytes` cap check, eliminating ~9,800 wasted `structuredClone` calls per streaming response (65–71% faster `push()`). Reducer snapshot isolation restored for OpenAI and Responses summaries ([#12241](https://github.com/diegosouzapw/OmniRoute/pull/12241)) — thanks @PauloHSOliveira ([#12243](https://github.com/diegosouzapw/OmniRoute/pull/12243)) +- **feat(usage):** Devin CLI agentic quota (Codeium seat-management GetUserStatus) and OpenRouter key limits plus account credits now surface in Provider Limits ([#12256](https://github.com/diegosouzapw/OmniRoute/pull/12256) — thanks @Neuron-Mr-White) +- **feat(settings):** persist `headroomUrl` through Settings so status/start use the operator URL instead of only `HEADROOM_URL` ([#12306](https://github.com/diegosouzapw/OmniRoute/issues/12306)) ([#12487](https://github.com/diegosouzapw/OmniRoute/pull/12487)) — thanks @HouMinXi +- **feat(auto-combo):** Auto-Combo scoring can now weigh how often a provider/model has actually succeeded. The engine already carried that number on every candidate — 24 hours of usage history behind a ten-sample floor, real-time metrics otherwise — and the scoring function never read it, while the weight table described `stability` as if it did. `reliability` (`1 - failureRate`, with the same field precedence and the same rate-bounding the speed ranking already uses, so a corrupt reading means "nothing observed" rather than "fails every call") is now a declared factor shipping at weight `0`, so routing is unchanged until an operator gives it one, and the `stability` description now matches what that factor computes ([#12317](https://github.com/diegosouzapw/OmniRoute/pull/12317)) — thanks @maxmad64bis +- **feat(routing):** With `freeAccessPolicy: "strict"`, the read-only candidate listing (`GET /v1/auto-combo/{channel}/candidates`) no longer hides the candidates the zero-cost guard excludes — the same read-only transparency the resilience filter already honours (#9133). Each candidate now carries `freeAccessExclusion` saying why it would be kept out, and it tells an exhausted allowance apart from a quota reading that never arrived or went stale, which used to look identical from the outside. Routing is unchanged: the listing reports, it never enforces. The separate `excludeTosAvoid` guard still drops its candidates without a reason; that gap is now documented rather than closed ([#12319](https://github.com/diegosouzapw/OmniRoute/pull/12319)) — thanks @maxmad64bis +- **feat(radar):** The Radar catalog table now shows two facts it was already receiving from the feed and dropping on the floor: the per-model rate limits (requests and tokens, per minute and per day) in a new column, and a badge when a provider's terms state it may train on the prompts you send. A limit of zero renders as zero rather than "rate-only" — for a ceiling those are opposite facts — and a model with no training statement gets no badge, because an absent statement is not a guarantee ([#12320](https://github.com/diegosouzapw/OmniRoute/pull/12320)) — thanks @maxmad64bis +- **feat(radar):** explain Community, single-use, contributor, supporter, recovery, abuse, offers, and privacy rules before either Radar activation action, and remove the superseded fixed-PR grant promise from every UI locale ([#12342](https://github.com/diegosouzapw/OmniRoute/pull/12342)) +- **feat(gamification):** the dashboard Profile page now shows the real daily streak — `/api/gamification/level` returns `streak: { current, longest }` (per key with `apiKeyId`, operator-wide maximum otherwise) and the streak card reads it instead of a hard-coded 0 (#2403) ([#12377](https://github.com/diegosouzapw/OmniRoute/pull/12377)) — thanks @pacocartones +- **feat(gamification):** the dashboard leaderboard now shows each API key's display name under the Name column instead of a truncated key id; `GET /api/gamification/leaderboard` attaches `name` per entry (name only — no key material), while the shared ranking helper and the federation leaderboard stay id-only — thanks @pacocartones ([#12385](https://github.com/diegosouzapw/OmniRoute/pull/12385)) +- **feat(gamification):** enforce the documented 1000 XP/min per-API-key anti-cheat rate limit on the XP award path; over-limit awards are logged and skipped instead of persisted, and the sliding window now matches the timestamp format stored in `xp_audit_log` ([#2403](https://github.com/diegosouzapw/OmniRoute/issues/2403)) ([#12390](https://github.com/diegosouzapw/OmniRoute/pull/12390)) — thanks @pacocartones +- **feat(admin):** localize the gamification anomalies page — the loading state, the Status column and the Suspicious badge now come from the `common` catalog (new `common.suspicious` key propagated to every locale) — add it to the Gamification sidebar group as `gamification-admin` (`/dashboard/gamification/admin`), and expose the loading and empty states as polite `role="status"` live regions (#12401 — thanks @pacocartones) +- **feat(providers):** skip GitHub combo members missing from the live synced catalog, and drop Copilot models that are policy-disabled or hidden from the model picker ([#12473](https://github.com/diegosouzapw/OmniRoute/pull/12473)) — thanks @RaviTharuma +- **feat(i18n):** locale-expansion tooling — `npm run i18n:add-locale` adds a language to config, dashboard, docs mirrors, CLI, README/indexes and site in one command; browser, CLI and cookie detection resolve `uk`, `fil`/`tl`, `zh-Hant` (and the retired legacy `in` → `id`) via config aliases; `config/i18n.json` now ships in the npm package so the published CLI can read it; new real-translation ratio gate (`npm run i18n:check-ratio`, advisory) with a per-locale ratchet baseline; `run-translation --adopt` restores `.i18n-state.json`; `sync-language-bars` generates 🌐 bars from config; `validate_translation.py` loads its allowlist again. Retires the duplicate `in` locale (Indonesian mislabelled as Hindi) — 42 honest locales; saved `NEXT_LOCALE=in` / `OMNIROUTE_LANG=in` keep working. (#12496) +- **feat(api):** Emit gateway-measured `tokens_per_second` (TTFT excluded) on streaming usage and `X-OmniRoute-Tokens-Per-Second` when first-token latency is known ([#12616](https://github.com/diegosouzapw/OmniRoute/issues/12616)) ([#12631](https://github.com/diegosouzapw/OmniRoute/pull/12631)) — thanks @RaviTharuma +- **feat(opencode-plugin): map gateway cost/usage/tok/s onto OpenCode inference payloads** — the official plugin copies `X-OmniRoute-Response-Cost`, token counts, `X-OmniRoute-Tokens-Per-Second` / `usage.tokens_per_second`, TTFT, and the winning `X-OmniRoute-Model` onto the JSON/SSE body OpenCode already consumes. Missing tok/s is left unset (never `tokens / latency`). (#12636) — thanks @RaviTharuma +- **feat(combo):** add combo strategy `quota-weighted`: skip exhausted accounts, then weighted-draw by leftover / in-flight load; existing conversations stay pinned until the account is empty ([#12789](https://github.com/diegosouzapw/OmniRoute/pull/12789)) — thanks @HouMinXi +- **feat(models):** Account live listings become the chat catalog source for Claude, Codex, Copilot, and AGY; public metadata only fills prices on IDs those accounts already list. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **fix(models):** Union `agy` and `antigravity` live catalogs so `agy/gemini-3.8-flash-high` is not rejected after the prefix folds to `antigravity`. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **feat(opencode):** Add `@omniroute/opencode-plugin-v2`, an opencode v2 plugin that gives a v2 install a route to an OmniRoute gateway: it publishes models, combos and auto-combos into the host catalog, refreshes them lazily behind a 300s TTL, and keeps serving the last known catalog from an on-disk snapshot when the gateway is unreachable. The v1 plugin (`@omniroute/opencode-plugin`) is untouched and keeps working as before. The plugin adapts to the host instead of assuming one shape: it reads the shape the host seeds into the catalog draft and publishes the `api` block, the pre-`api` top-level fields, or both. The gateway key comes from the host credential store when one is connected, so no secret needs to sit in a config file; a source the gateway refuses is named in a warning instead of silently degrading the catalog to raw model ids; and tool schemas bound for a Gemini model are stripped of the JSON-Schema keywords Gemini rejects, so tool calling survives that route ([#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870)) — thanks @maxmad64bis +- feat(services): show sanitized CLIProxyAPI account health from its authenticated management API without exposing credentials, file paths, or raw account metadata (#6342) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add AnySearch free web search + URL extract (webFetch) with typed results, credential validation, REST routing, and MCP selection - fallback-only ([#11690](https://github.com/diegosouzapw/OmniRoute/pull/11690)) — thanks @Xxx91n +- **feat(dashboard):** display clamped `[0, 100]%` cached input token ratio in request logs table ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) — thanks @b3nw +- **feat(catalog):** add `OMNIROUTE_DISABLE_THINKING_LEVEL_VARIANTS` feature flag to optionally filter out thinking level variants from model catalog ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) — thanks @b3nw +- **feat(dashboard):** continuously export call logs to external analytics stores. A pluggable destination registry ships the full Logs-tab record set on an hourly `JobRegistry` cron, with a persisted per-destination cursor, batched inserts, a config UI rendered from each destination's own field descriptors, and a REST layer (`/api/log-export/*`) for CRUD, a connection test, and an on-demand run. A destination can opt into `includeBodies` to also ship the request and response payloads shown in the Logs detail pane, including the client and provider views of each call; this is off by default, and payloads inherit the dashboard's PII sanitisation, secret redaction and `noLog` handling. Google BigQuery is the first destination, using a service-account key stored encrypted at rest and streaming inserts keyed by call-log id, into a table that is day-partitioned on `timestamp` and clustered on `api_key_name`, `provider`, `model` and `status`. ([#11945](https://github.com/diegosouzapw/OmniRoute/pull/11945)) — thanks @dpozimski +- **feat(providers):** add MaxAI as a signed, OpenAI-compatible provider serving its 13 paid chat models (GPT-5.6 / Luna / Thinking, Claude 5 Sonnet, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite, Grok 4.1-fast / 4.5, DeepSeek V3.2 / R1, Llama 3.3 70B) through OmniRoute's `/v1` endpoint, with per-request HMAC-SHA1→SM3→AES request signing, live model + context-window discovery from `/models/get_config`, and prompted tool-calling translated to OpenAI `tool_calls` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI vision input — image_url content parts are forwarded inline in `message_content` to the 6 vision-capable models (GPT-5.6 / Luna / Thinking, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite) ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI image generation — 6 image models (gpt-image-1, dall-e-3, flux-1-schnell/dev/pro, sd3-medium) exposed through `POST /v1/images/generations` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI document RAG — inline base64 file/document attachments are uploaded to MaxAI (content-addressed `doc_id`) and attached to the chat via `doc_list` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** browserless MaxAI onboarding — email device-pair login (`/api/providers/[id]/login`) and signed access-token refresh, so a connection can be created and kept fresh without a real browser or Google OAuth ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** per-provider TLS impersonation profile (MaxAI presents a Windows Firefox-150 client fingerprint) so its bot-sensitive endpoints accept OmniRoute traffic ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** add **Nimble** as a web-search and web-fetch provider (`nimble-search`) — `/v1/search` routes to Nimble's search API at `lite` depth (locale, freshness and include/exclude domain filters mapped onto the shared request contract), and `/v1/web/fetch` routes to Nimble Extract, which covers all four fetch formats (`markdown`, `html`, `links`, `screenshot`) from a single call. One API key serves both surfaces. ([#11620](https://github.com/diegosouzapw/OmniRoute/pull/11620)) — thanks @wildcard +- The npm publish is automatic again, through npm Trusted Publishing (OIDC): the hosted `stage-npm` job publishes with a short-lived credential minted from GitHub's id-token — no `NPM_TOKEN`, no 2FA prompt, provenance attached. `publish_mode=staged` (owner approves with 2FA) and `direct` (token) remain available on `workflow_dispatch`. ([#11931](https://github.com/diegosouzapw/OmniRoute/pull/11931)) +- **feat(providers):** Add **Opper** as an API-key gateway provider — EU-hosted AI gateway with 700+ models from 30+ providers behind one OpenAI-compatible API and one key (`OPPER_API_KEY`); model ids use `provider/model` format (e.g. `anthropic/claude-sonnet-4-6`, `openai/gpt-5`); live model catalog at `https://api.opper.ai/v3/compat/models`; entry mirrors `requesty` (same shape, `passthroughModels: true`, no static seed) ([#11629](https://github.com/diegosouzapw/OmniRoute/pull/11629)) +- **feat(dashboard):** the `/dashboard/orchestration` snapshot hook now subscribes to the `agents` WebSocket channel (`agent.task.updated`) instead of `requests` as its refetch trigger, and relaxes its background poll from 5s to 30s while that WS connection is up — falling back to the tighter 5s cadence, reprogrammed live on any connect/disconnect transition, whenever the socket is down. ([#12409](https://github.com/diegosouzapw/OmniRoute/pull/12409)) +- **feat(dashboard):** Orchestration canvas quick wins — search box plus state/source/provider filter chips with a one-click clear, and per-source collapse/expand, all reflected in the URL so a filtered/collapsed view is shareable and survives a refresh; the detail drawer gained a "copy trace JSON" action and hardened error/empty-state and accessibility handling; the Agents-tab edges now animate traveling particles along active (running) connections; and the canvas node/edge status colors moved off fixed hex values onto theme-aware `--orch-status-*` CSS custom properties, so they adapt correctly to light/dark mode. ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- **chore(dashboard):** Orchestration UI hardening pass and the missing component/model test coverage it called for — `OrchestratorNode`/`ActivityNode`/`OverflowNode` rendering, the `?node=`/overflow-click page routing, the Agents-tab orchestrator-click no-op and `showCompleted` toggle, and the overview kanban's done-column sort order (#12270, #12271). ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- New `/dashboard/orchestration` page: live unified view of everything running — Cloud Agent, A2A and Conductor as a real-time graph (Agents tab), the combo cascade (Routing tab, reusing the Combo Live Studio) and a state kanban (Overview tab), with a detail drawer (trace, cost, approve/cancel). Read-only over existing APIs — no new backend. Canvas concept credit: PR #11815 design ([#12261](https://github.com/diegosouzapw/OmniRoute/pull/12261)) +- **feat(dashboard):** new "History" tab on `/dashboard/orchestration` — an Airflow-style grid of finished runs over a 24h/7d/30d preset window, one row per (source, identity), clicking a cell opens the existing detail drawer. It is backed by real persistence: A2A task lifecycle transitions are now written to the `a2a_tasks` table (purged after 30 days, configurable via `OMNIROUTE_A2A_HISTORY_RETENTION_DAYS`) and served by the new `GET /api/a2a/tasks/history` listing endpoint, with the task-detail route falling back to persisted history once a run leaves the in-memory snapshot. Conductor runs stay remote and are not persisted locally — the tab says so instead of silently omitting them. ([#12479](https://github.com/diegosouzapw/OmniRoute/pull/12479)) +- **feat(dashboard):** the orchestration detail drawer gained a "Repeat" action for Cloud Agent, A2A and Conductor tasks — a two-click confirm (click once to arm, click again within the confirm window to fire) re-submits the original prompt/input as a new run. The button is disabled with an explanatory tooltip whenever the original input can't be recovered from the loaded task detail (e.g. it never carried a prompt, or the detail failed to load). Two limitations of the A2A variant, by design: it targets the `/a2a` JSON-RPC endpoint, which authenticates with an API key only (`REQUIRE_API_KEY=true` or a configured `OMNIROUTE_API_KEY` makes a dashboard-session repeat answer `HTTP 400` — surfaced verbatim in the drawer's error line, never as a success), and the `message/send` call is SYNCHRONOUS: the POST blocks for the whole skill run, so the success confirmation only appears once the repeated task finishes. A dashboard-authenticated A2A creation path is deliberately left to a follow-up — widening the endpoint's auth posture is an operator decision, not a side effect of this feature. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(a2a):** A2A task execution now records which memories were consulted for the task's last user message as `metadata.memoryHits` (id/key/type/content-snippet) plus a `memory_hits` history event, purely for observability — the retrieved memory is never injected into a skill's prompt or behavior. Gated by the `OMNIROUTE_A2A_MEMORY_HITS` kill-switch (default enabled; set to `0` to skip the recall lookup entirely). The drawer's new "Memory used" section lists these hits for a2a tasks and is omitted whenever there are none. Known limitation: recall only resolves under the keyless posture — a keyed caller's task owner is a SHA-256 prefix of the API key, while memory rows are keyed by the database api-key id, and no hash→id lookup exists today, so the hit list stays empty for keyed callers. The recorded hits are also kept out of the task's own `input` (and therefore out of the persisted input and of the "Repeat" request body), so repeating a task never re-sends the previous run's memory snippets. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(providers):** add a Perplexity Agent API provider (`perplexity-agent` / `pplx-agent`) for Perplexity `/v1/responses`, including the documented Anthropic, OpenAI, Google, xAI, DeepSeek, Z.AI, Moonshot/Kimi, NVIDIA, and Perplexity model IDs plus Anthropic-model `max_output_tokens` compatibility. ([#12103](https://github.com/diegosouzapw/OmniRoute/pull/12103)) — thanks @AIB1TAL0S +- **feat(providers):** add RPD (Requests Per Day) limit to provider rate limit overrides across UI, schemas, DB, and i18n ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) — thanks @b3nw +- **feat(providers): add UC Direct (uncensored.com Developer API), the metered OpenAI-compatible surface.** A standard OpenAI-compatible passthrough (default executor) for uncensored.com's official REST API at `https://api.uncensored.com/api/v1`: `X-api-key` auth (never-expiring `uai_sk_live_` key), `POST /chat/completions` with streaming SSE and native tool-calling, and the full live metered catalog (82 models across 15 providers, discovered from the public `GET /v1/models`). Registered as provider `uc-direct` (alias `ucd`). Complements the un-metered `uc` persona provider — same models, metered credits and a plain API key instead of a subscription session. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(providers): add UC (uncensored.com), the un-metered subscription "persona" chat as an OpenAI-compatible provider.** A WebSocket web-app port: a durable Clerk credential mints a short-lived session token per connect (browserless — no API key), driving UC's persona socket. Ships the browserless email-code login (request → verify → harvest), the 19 verified persona models (Claude Opus, Gemini, Grok, GLM, Kimi, DeepSeek, MiniMax, incl. the uncensored variants), prompted `` tool-calling with a per-model code-style dialect + auto-cure retry for guardrailed models, live ``/reasoning split, streaming + non-streaming OpenAI responses, and full quota/auth error surfacing (paywall / message-limit / rate-limit → 429, invalid session → 401 re-login). Full multimodal parity via the persona blob-upload layer: **vision** (image input, 15 vision-capable models), **document RAG** (PDF/doc upload, server-side extraction), **image generation** (22 models), **video generation** (14 models, async signed-url → poll), and **TTS** (streaming MP3). Registered as provider `uc` (alias `ucn`). The metered OpenAI-compatible Developer API is a separate `uc-direct` provider. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(logs):** add copy-all button to request detail modal ([#11083](https://github.com/diegosouzapw/OmniRoute/pull/11083)) — thanks @AndrianBalanescu +- **feat(check):** make serviceKinds required and add the reverse-walk provider consistency gate ([#11392](https://github.com/diegosouzapw/OmniRoute/pull/11392)) — thanks @Tushar49 +- **feat(providers):** only advertise verified-working connections by default ([#11448](https://github.com/diegosouzapw/OmniRoute/pull/11448)) — thanks @alltomatos +- **feat(resilience):** add hierarchical concurrency admission ([#11493](https://github.com/diegosouzapw/OmniRoute/pull/11493)) +- **feat(providers):** derive + surface expiry for JWT-bearing web cookies (#11497) ([#11505](https://github.com/diegosouzapw/OmniRoute/pull/11505)) — thanks @oyi77 +- **feat(tunnels):** support named cloudflared tunnels via CLOUDFLARED_CONFIG ([#11561](https://github.com/diegosouzapw/OmniRoute/pull/11561)) — thanks @ggiak +- **feat(compression):** make proactive context-compression threshold a live setting ([#11564](https://github.com/diegosouzapw/OmniRoute/pull/11564)) — thanks @ragnar-claude +- **feat(resilience):** add provider peak-hour protection ([#11622](https://github.com/diegosouzapw/OmniRoute/pull/11622)) — thanks @Neuron-Mr-White +- **feat(providers):** add 1min.ai provider ([#11631](https://github.com/diegosouzapw/OmniRoute/pull/11631)) — thanks @zero-executioner +- **feat(dashboard):** collapsible JSON tree viewer for request/response payloads ([#11703](https://github.com/diegosouzapw/OmniRoute/pull/11703)) — thanks @hartmark +- **feat(routing):** add LiquidAI LFM2.5-2.6B free tier via OpenRouter ([#11752](https://github.com/diegosouzapw/OmniRoute/pull/11752)) — thanks @brick30llc-ctrl +- **feat(pricing):** add GLM-5.3-Flash pricing, model specs, and catalog registration ([#11830](https://github.com/diegosouzapw/OmniRoute/pull/11830)) — thanks @AndrianBalanescu +- **feat(routing):** add relayMode for schema-locked context handoffs ([#11839](https://github.com/diegosouzapw/OmniRoute/pull/11839)) — thanks @raheemuddin786 +- **feat(config):** add support for runtime system prompt configuration and hot-reloading ([#11841](https://github.com/diegosouzapw/OmniRoute/pull/11841)) — thanks @raheemuddin786 +- **feat(routing):** expand connection-aware quota prefilter across combo strategies (#11682) ([#11850](https://github.com/diegosouzapw/OmniRoute/pull/11850)) — thanks @HouMinXi +- **feat(models):** reactive discovery sync after Antigravity model-not-found 404 ([#11888](https://github.com/diegosouzapw/OmniRoute/pull/11888)) — thanks @MumuTW +- **feat(models):** auto-aliases and token limits from synced Antigravity discovery ([#11889](https://github.com/diegosouzapw/OmniRoute/pull/11889)) — thanks @MumuTW +- **feat(settings):** raise sticky round-robin limit caps to 1000 ([#12015](https://github.com/diegosouzapw/OmniRoute/pull/12015)) — thanks @adivekar-utexas +- **feat(resilience):** operator-configurable global credential health check interval ([#12043](https://github.com/diegosouzapw/OmniRoute/pull/12043)) — thanks @adivekar-utexas +- **feat(quota):** use official OpenCode Go usage API ([#12124](https://github.com/diegosouzapw/OmniRoute/pull/12124)) — thanks @ddarkr +- **feat(resilience):** default the credential health check sweep to 60 minutes ([#12138](https://github.com/diegosouzapw/OmniRoute/pull/12138)) — thanks @adivekar-utexas +- **feat(quality):** new-code mode for the complexity and dead-code ratchets (Clean as You Code) ([#12142](https://github.com/diegosouzapw/OmniRoute/pull/12142)) +- **feat(dashboard):** orchestration canvas — unified model + snapshot hook (part 1/2) ([#12156](https://github.com/diegosouzapw/OmniRoute/pull/12156)) +- **feat(usage):** add Kilo Code balance and Kilo Pass quotas ([#12178](https://github.com/diegosouzapw/OmniRoute/pull/12178)) — thanks @killer30001000 +- **feat(providers):** refresh vendored ChatGPT Web connector to v4.0.7 ([#12181](https://github.com/diegosouzapw/OmniRoute/pull/12181)) — thanks @backryun +- **feat(providers):** manual "Clear cooldown" action in the cooling panel ([#12224](https://github.com/diegosouzapw/OmniRoute/pull/12224)) — thanks @patrykkopycinski +- **feat(providers):** restore ChatGPT Web via clean-room browser transport ([#12239](https://github.com/diegosouzapw/OmniRoute/pull/12239)) — thanks @backryun +- **feat(sse):** wire the PROVIDER_PROFILES window gate into the global provider cooldown ([#12247](https://github.com/diegosouzapw/OmniRoute/pull/12247)) +- **feat(providers):** modernize CLOVA Studio chat and embeddings ([#12277](https://github.com/diegosouzapw/OmniRoute/pull/12277)) — thanks @backryun +- **feat(browser):** adopt Obscura as primary headless browser engine with Chromium fallback ([#12286](https://github.com/diegosouzapw/OmniRoute/pull/12286)) — thanks @opensource-elearning +- **feat(sse):** full language parity for output styles (es/de/fr/it/ru/zh + autoDetect) ([#12289](https://github.com/diegosouzapw/OmniRoute/pull/12289)) +- **feat(quality):** complete test:scoped — --full map rebuild, stdin selection, CI loader parity (#8084 D1) ([#12353](https://github.com/diegosouzapw/OmniRoute/pull/12353)) +- **feat(providers):** refresh Fable, Cursor, and Devin catalogs ([#12367](https://github.com/diegosouzapw/OmniRoute/pull/12367)) — thanks @backryun +- **feat(video):** redact transcript text from logs and durable memory (#12150 P1) ([#12427](https://github.com/diegosouzapw/OmniRoute/pull/12427)) +- **feat(dashboard):** parent-link, genuine-continuation badge, and modal perf fixes ([#12448](https://github.com/diegosouzapw/OmniRoute/pull/12448)) — thanks @hartmark +- **feat(dashboard):** adaptive context-budget dial on compression panel ([#12488](https://github.com/diegosouzapw/OmniRoute/pull/12488)) — thanks @HouMinXi +- **feat(providers):** refresh Z.ai Web models and browser transport ([#12524](https://github.com/diegosouzapw/OmniRoute/pull/12524)) — thanks @backryun +- **feat(video):** redact raw client-snapshot transcript fields in the detailed log (#12150 P2) ([#12528](https://github.com/diegosouzapw/OmniRoute/pull/12528)) +- **feat(providers):** refresh NVIDIA hosted models ([#12538](https://github.com/diegosouzapw/OmniRoute/pull/12538)) — thanks @backryun +- **feat(quota):** Moonshot Open Platform balance and TPD lock for custom nodes ([#12590](https://github.com/diegosouzapw/OmniRoute/pull/12590)) — thanks @HouMinXi +- **feat(video):** redact transcript in the in-memory pending-request snapshot (#12430 item 6) ([#12596](https://github.com/diegosouzapw/OmniRoute/pull/12596)) +- **feat(dashboard):** link a log entry's Conversation Context to its owning conversation ([#12646](https://github.com/diegosouzapw/OmniRoute/pull/12646)) — thanks @hartmark +- **feat(catalog):** eligibility-gated free-tier bucket ([#12669](https://github.com/diegosouzapw/OmniRoute/pull/12669)) +- **feat(db):** fail-closed previous_response_id continuation for redacted video turns (#12150 P2b) ([#12707](https://github.com/diegosouzapw/OmniRoute/pull/12707)) +- **feat(gamification): pay the documented `streak_bonus` and `badge_unlock` XP rewards.** `XP_REWARDS` listed both rewards but the award pipeline never paid them: the private reward table in `events.ts` omitted them, `updateStreak()` did not report when a streak extended, and badge unlocks carried no XP. Every request that extends a daily streak now pays `streak_bonus × streak length` once per UTC day (guarded by a same-day `xp_audit_log` check), and every badge unlocked through the pipeline pays `badge_unlock` once per badge (guarded by the `user_badges` primary key; `unlockBadge()` now reports whether it inserted). Bonus XP flows through the same `addXp` + level sync + global/weekly/monthly leaderboard path as action XP, so level-ups and rankings include it. The Radar supporter recognition unlock stays XP-free. (#12522 — thanks @pacocartones) +- **feat(providers):** advertise a `free-tier` capability in the provider plugin manifest for every provider with documented free models, so sidecars and dashboards can filter free-capable providers without reading the quota catalog ([#12786](https://github.com/diegosouzapw/OmniRoute/pull/12786)) — thanks @maxmad64bis +- **feat(providers):** Added EURouter as an OpenAI-compatible API-key gateway (`https://api.eurouter.ai/v1`), with live model discovery via `passthroughModels`. Its copy states that models are served by third-party upstreams listed per model, so an EU-based router is not read as EU data residency for inference. ([#13025](https://github.com/diegosouzapw/OmniRoute/pull/13025)) — thanks @ntdatt812 +- **feat(providers):** Added GreenPT as an OpenAI-compatible API-key provider (`https://api.greenpt.ai/v1`), with live model discovery via `passthroughModels`. No free-inference badge: the published docs describe a free API subscription billed per token, not a free tier. ([#13024](https://github.com/diegosouzapw/OmniRoute/pull/13024)) — thanks @ntdatt812 +- **feat(i18n):** 9 new locales — Greek (`el`), Croatian (`hr`), Serbian (`sr`), Lithuanian (`lt`), Estonian (`et`), Latvian (`lv`), Slovenian (`sl`), Maltese (`mt`), Irish (`ga`) — across the dashboard, docs mirrors, CLI, README and the site; OmniRoute now ships every official EU language (51 locales). (#13044) +- **feat(i18n):** 8 new locales — Kannada (`kn`), Malayalam (`ml`), Odia (`or`), Punjabi (`pa`), Nepali (`ne`), Sinhala (`si`), Burmese (`my`), Khmer (`km`) — across the dashboard, docs mirrors, CLI, README and the site (59 locales). The translator now restores the ICU literal escape around angle placeholders and splits oversized docs sections before translating. (#0000) ([#13660](https://github.com/diegosouzapw/OmniRoute/pull/13660)) +- feat(providers): **list Agnes 3.0 Flash as the current free chat model, drop retired 1.5 Flash, add Image 2.0/2.5 Flash plus Video 2.5/2.5 Flash, and discover the live `/v1/models` catalog (including the CN host `api.agnes-ai.cn`).** `agnes-1.5-flash` now forwards to `agnes-3.0-flash`. Video 2.5 polls `GET /v1/videos/{id}` (not the V2.0 `/agnesapi` contract). Live `/v1/models` (2026-09-09) no longer serves 1.5; the wiki marks it deprecated. 3.0 Flash is 512K context / 65,536 max output, same window as 2.5. CN-region keys use the existing per-connection base-URL field, default stays `apihub.agnes-ai.com`. ([#13120](https://github.com/diegosouzapw/OmniRoute/pull/13120)) — thanks @HouMinXi +- **feat(sse):** Codex and OpenAI catalogs list GPT-6 Astra with effort aliases (`-low` through `-ultra`); Codex CLI identity pins `@openai/codex@0.153.4` in lockstep with the image ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **feat(dashboard):** the orchestration History tab gained a "Compare runs" mode — toggling it turns each grid cell into a 2-item selection queue (a 3rd click drops the oldest pick), and picking two cells opens a side-by-side comparison panel instead of the usual detail drawer. The panel fetches both runs' detail the same way the drawer does (falling back to persisted history once a run leaves the live TTL window) and shows, per side: identity/source/state, start time, a signed `right - left` delta for duration/cost/event count, the event timeline aligned by index, and any memory hits. One side's fetch failing never blocks the other, and a delta is only ever computed when both sides have a finite value — otherwise it renders "—", never `NaN`. Comparing two runs from different sources or skills still works; a banner marks the deltas as informational rather than hiding them, since the two runs aren't a strict apples-to-apples pair. ([#12677](https://github.com/diegosouzapw/OmniRoute/pull/12677)) +- **feat(dashboard):** badge a conversation that never reached a clean stop ([#12717](https://github.com/diegosouzapw/OmniRoute/pull/12717)) — thanks @hartmark +- **feat(release):** reconcile-changelog tool + version-anchored fragment aggregation ([#12987](https://github.com/diegosouzapw/OmniRoute/pull/12987)) +- **feat(mcp):** carry context_length through MCP list_models_catalog projection (#12776) ([#13406](https://github.com/diegosouzapw/OmniRoute/pull/13406)) — thanks @KooshaPari +- **feat(cli):** make startup readiness budget configurable (#13369) ([#13433](https://github.com/diegosouzapw/OmniRoute/pull/13433)) — thanks @KooshaPari + ### 🐛 Bug Fixes +- **security(streaming):** sanitize generic mid-stream error messages before emitting OpenAI, + Responses, or Claude SSE failure frames and before diagnostic logging, while preserving raw + failures for internal classification and keeping client disconnects out of provider failure state. ([#12457](https://github.com/diegosouzapw/OmniRoute/pull/12457)) +- **fix(claude):** `blockExtraUsage: false` no longer lets 5h quota preflight skip the account; extra usage is billed after the session bar is gone, so the request must reach Anthropic ([#12803](https://github.com/diegosouzapw/OmniRoute/pull/12803)) — thanks @HouMinXi +- Sanitize HuggingChat conversation-creation and message-send transport failures before they reach client error bodies or provider logs. ([#12467](https://github.com/diegosouzapw/OmniRoute/pull/12467)) +- **fix(dashboard):** Keep local and theme-aware provider SVG icons at a definite layout size so Chromium does not collapse them to 0×0 after the v3.8.50 image-rendering change ([#12054](https://github.com/diegosouzapw/OmniRoute/pull/12054)) — thanks @ponkcore +- **fix(security):** Sanitize provider and runtime failures before public API, SSE and MCP responses and before persistent request, proxy and usage logs, preventing credentials, stack traces and host filesystem paths from crossing those boundaries while preserving stable error codes and useful diagnostics. ([#12506](https://github.com/diegosouzapw/OmniRoute/pull/12506)) +- **fix(cli):** `omniroute tunnel create` no longer crashes with `Cannot read properties of undefined (reading optsWithGlobals)` — removed the duplicate positional argument that caused Commander.js to misalign the action callback parameters ([#12295](https://github.com/diegosouzapw/OmniRoute/issues/12295)) ([#12368](https://github.com/diegosouzapw/OmniRoute/pull/12368)) — thanks @gonisulaimann +- Stop advertising Gemini Live-only models as supported audio endpoints until OmniRoute proxies the bidirectional Live protocol. ([#11710](https://github.com/diegosouzapw/OmniRoute/pull/11710)) — thanks @RaviTharuma +- **fix(kie):** reroute `flux/kontext` off the KIE Market `createTask` flow — it is catalogued with `isMarket: true` but has no Market catalog page, so KIE rejected it with "model name not supported"; it now hits the dedicated `POST /api/v1/flux/kontext/generate` / `GET /api/v1/flux/kontext/record-info` endpoints instead (#11296). ([#11985](https://github.com/diegosouzapw/OmniRoute/pull/11985)) — thanks @hartmark +- **fix(kie):** correct 12 more KIE Market catalog ids that were sent to `createTask` unchanged but diverge from KIE's documented upstream `model` values — GPT Image 2 T2I/I2I (drops the `gpt/` prefix), GPT Image 1.5 T2I/I2I (`gpt-image/` namespace), Seedream 5.0 Lite T2I/I2I (drops the `.0`), all 4 Flux 2 variants (`flux-2/` namespace, generic variant renamed `flex`), and Wan 2.7 Image / Image Pro (dash instead of dot) — each verified individually against the literal example request published on docs.kie.ai. `#11326`'s "everything else already matches" claim was wrong a second time (#11296); `z-image/4.0-*`/`z-image/4.5-*` and `flux/kontext` remain open, documented as unresolved in `KIE_MARKET_UPSTREAM_MODEL_IDS`'s comment pending further verification. ([#11667](https://github.com/diegosouzapw/OmniRoute/pull/11667)) +- **fix(dashboard):** `useApiKeySave.handleSaveApiKey` no longer forces a full upstream `/models` catalog sync on every non-curated provider connection save — callers can now pass `skipModelSync: true` to opt out, so a workflow that only wants to add one manual model no longer floods the provider's available-models list with hundreds/thousands of synced entries. The flag is a client-side intent signal only and is stripped before the connection payload is POSTed to `/api/providers`; default behavior (full sync on save) is unchanged when the flag is omitted (#11324) ([#11664](https://github.com/diegosouzapw/OmniRoute/pull/11664)) +- **Provider connections:** keep `tokenExpiresAt` when a connection is created. The create-path allowlist omitted it, so every insert stored NULL and the dashboard token badge could read a fresh connection as expired until its first background refresh ([#11368](https://github.com/diegosouzapw/OmniRoute/pull/11368)). — thanks @ntdatt812 +- **fix(combos):** the combo builder's precision-select, global-model-search, and manual-entry flows now serialize a model step's `model` string using the provider's already-computed routing-alias prefix (e.g. `oc/`) instead of rebuilding it from the raw canonical `providerId`, fixing the no-auth "OpenCode Free" provider (`opencode`) being routed to the unrelated paid "OpenCode Zen" provider (`opencode-zen`) because `opencode` doubles as a manual routing-prefix override ([#11433](https://github.com/diegosouzapw/OmniRoute/issues/11433)). ([#11670](https://github.com/diegosouzapw/OmniRoute/pull/11670)) +- **fix(radar):** the catalog feed cache now keeps `generatedAt`, the date the feed's data was built, next to `fetchedAt`, the date this install downloaded it (#11435). The feed schema requires that date and the sync path validates it, but the cache dropped it — so a feed fetched minutes ago and one carrying weeks-old figures looked identical to everything downstream, including the dashboard's "Last fetched" line. `getRadarCatalog().meta` and `GET /api/radar/status` now report both dates, the latter as its own field rather than folded into `version` — and omitted entirely for the offers and intel caches, which keep no build date, where a `null` would read as "unknown" rather than "never stored". The dashboard still shows only the fetch time; surfacing the build date there needs a new translated label and is left to a follow-up. Rows cached before migration 163 read back as `null`: unknown stays unknown instead of borrowing the fetch time. The referrals cache has persisted the same date since migration 142. — thanks @maxmad64bis +- **fix(cli):** postinstall no longer fills `JWT_SECRET` and `API_KEY_SECRET` in the installed package's `.env` (#11436). `.env.example` ships both blank on purpose: the server restores them from its durable store, or generates and persists them there, in `ensureSecrets()`. Pre-filling them defeated that — the file lives inside the package directory, so `npm i -g` replaced it and postinstall wrote _different_ values, while `ensureSecrets()` (which only acts on an empty variable) never got to restore the real ones. Both secrets rotated silently on every update, invalidating dashboard sessions and API-key CRCs. `STORAGE_ENCRYPTION_KEY` left the same list for the same reason in #1622; its comment pointed at a function that no longer exists and now names the real provisioning path. — thanks @maxmad64bis +- **fix(cli):** the CLI now says when a loaded `.env` lives inside the installed package directory (#11437). It already announces every env file it reads, without distinguishing the ones that survive an update from the one that does not: `npm i -g` replaces the package directory wholesale, so values set there are gone at the next update, silently. The warning names the durable path to move them to, and fires only when that file actually supplied a value — a file entirely shadowed by a durable one supplied nothing. A development checkout stays silent: there the same path is stable and documented in `SETUP_GUIDE.md`. — thanks @maxmad64bis +- **fix(free-models):** the shared `isFreeModel()` predicate no longer reports catalog entries marked `freeType: "discontinued"` as free, so `hidePaidModels` can't route to Pollinations' seven premium models that now require a paid key ([#11441](https://github.com/diegosouzapw/OmniRoute/pull/11441)) — thanks @maxmad64bis +- fix(ui): let AnySearch use the normal provider-icon fallback when LobeHub has no matching icon (#11449) +- Fixed the v3.8.50 Costs and Analytics dashboards so flat-rate Claude Code usage can be shown as an explicitly requested token-price estimate without changing default billed-cost semantics. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed archived usage retention so each request is priced individually instead of pricing a day's summed tokens once, which understated archived cost whenever a day mixed cache-heavy and ordinary requests. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed the Costs dashboard so it discloses when displayed figures include flat-rate token-price estimates instead of labelling them as billed spend, using the flag the analytics API already returns; the month-end projection and the CSV/JSON exports carry the same marker, and billed-cost mode is unchanged. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- **fix(combo):** attach the same combo-diagnostics trace (`poolSize`/`attemptOrder`/`excluded`/`terminalReason`, plus `x-omniroute-combo-*` headers) to the round-robin strategy's and the nested pipeline/fusion runtime-unit loop's "Maximum combo retry limit reached" 503 that the priority-strategy path already attaches for the identical terminal condition — previously those two paths returned a bare, contextless 503 ([#11462](https://github.com/diegosouzapw/OmniRoute/issues/11462)). ([#11665](https://github.com/diegosouzapw/OmniRoute/pull/11665)) +- **fix(bun):** use native `bun:sqlite` in `bootstrap-env` and `sync-env` to avoid loading `better-sqlite3` N-API addon during Bun startup ([#11468](https://github.com/diegosouzapw/OmniRoute/pull/11468)) — thanks @TheDemonTuan +- **fix(docker-bun):** make `Dockerfile.bun` install reliable with pre-install script helpers and native `bun:sqlite` smoke check ([#11470](https://github.com/diegosouzapw/OmniRoute/pull/11470)) — thanks @TheDemonTuan +- **fix(docker-bun):** remove every vendored `better-sqlite3` native addon from the Bun runtime image so startup cannot abort before the `bun:sqlite` fallback ([#11482](https://github.com/diegosouzapw/OmniRoute/pull/11482)) — thanks @TheDemonTuan +- **fix(autoCombo):** effort/alias model variants (`gpt-5.6-sol-xhigh`, `gpt-5.6`, cursor's `claude-4.6-opus-high`) inherit their base model's task fitness instead of falling to the wildcard 0.5 ([#11492](https://github.com/diegosouzapw/OmniRoute/pull/11492)) — thanks @MumuTW +- fix(db): rate-limit repeated Arena ELO leaderboard fetch-failure warnings instead of logging one per sync attempt (#11500) ([#11989](https://github.com/diegosouzapw/OmniRoute/pull/11989)) — thanks @hartmark +- **fix(db):** dedupe the raw `[Encryption] Decryption failed...` log line emitted by the lazy-decrypt views (`createLazyRowProxy`/`createLazyConnectionView`), which power `getProviderConnections()` and were re-triggering that line on every CredentialHealth/model-sync cycle for the same corrupt or stale-key credential — a fresh Proxy over a fresh row on every cycle meant the per-proxy memoization never suppressed it, unlike the dedup `decryptConnectionFields()` already had since [#9927](https://github.com/diegosouzapw/OmniRoute/issues/9927). Now shares that dedupe tracking so the line logs at most once per credential ([#11500](https://github.com/diegosouzapw/OmniRoute/issues/11500)). ([#11678](https://github.com/diegosouzapw/OmniRoute/pull/11678)) +- **fix(intelligence):** Arena ELO sync stores a synthesized base-model row for every effort/harness variant (`claude-opus-5-max` → `claude-opus-5`, `gpt-5.6-sol-xhigh (codex-harness)` → `gpt-5.6-sol`), so bare model requests reach the synced intelligence layer instead of the static fallback table; the hand-maintained `MODEL_ALIAS_MAP` — which collapsed generations (`gpt-5.5` → `gpt-5`) — is gone ([#11506](https://github.com/diegosouzapw/OmniRoute/pull/11506)) — thanks @MumuTW +- **fix(autoCombo,sse):** catalog hygiene — the static task-fitness table no longer ranks retired models above live ones (dead rows dropped, patterns matched on segment boundaries), the 7 deprecation aliases that forwarded to retired or non-existent models now point at the vendors' published replacements, an alias is no longer rewritten when the serving provider offers the id as-is, and a new `check:model-lifecycle` gate diffs both tables against a vendor lifecycle snapshot ([#11507](https://github.com/diegosouzapw/OmniRoute/pull/11507)) — thanks @MumuTW +- **fix(sse):** stop dropping resolved `thoughtSignature` values on parallel (multi tool-call) turns sent to Gemini 3.x — the claude→gemini and openai→gemini translators previously kept the signature only on the *first* function call of a message, causing Gemini to reject subsequent calls in the same turn with HTTP 400 "Function call is missing a thought_signature"; each function call now keeps its own resolved signature ([#11510](https://github.com/diegosouzapw/OmniRoute/issues/11510)). ([#11669](https://github.com/diegosouzapw/OmniRoute/pull/11669)) +- Treat Docker publishing from a non-default release branch as an expected skip instead of a failed workflow, while preserving the guard that prevents it from overwriting the `next` channel. ([#11527](https://github.com/diegosouzapw/OmniRoute/pull/11527)) — thanks @pacocartones +- fix(search): prefer credentialed providers over duckduckgo-free fallback (#11524) — the fallback-only loop ran before the credentialed-providers loop in `executeWebSearch`, making configured providers unreachable when `duckduckgo-free` was available. ([#11565](https://github.com/diegosouzapw/OmniRoute/pull/11565)) — thanks @vsd2807 +- **fix(sse):** cap the upstream headers-wait phase for STREAMING requests to a client-realistic ceiling (110s, under Codex's own ~120s hard client-abort window) instead of the flat 10-minute `FETCH_TIMEOUT_MS` default — that default was 5x longer than the body-phase readiness watchdog's own adaptive bound, so a request whose upstream never returned any response at all (not even headers, e.g. a stalled NVIDIA target behind a tool-heavy Responses→Chat translation) kept the client connection alive on keepalives only, guaranteeing the client's own patience ran out first with an opaque 499 instead of OmniRoute detecting and failing the stall fast. Non-streaming requests are unaffected — they keep the existing flat default (`open-sse/utils/fetchStartTimeoutPolicy.ts`) (#11526) ([#11668](https://github.com/diegosouzapw/OmniRoute/pull/11668)) +- **fix(ci):** protocol-clients E2E harness boots the peer-stamped custom server (`run-next.mjs dev`) instead of the bare `next` CLI, so `LOCAL_ONLY` locality resolves from the real TCP peer stamp and `/api/mcp/audit` answers 200 (open bootstrap preserved via an `OMNIROUTE_E2E_BOOTSTRAP_MODE=open` credential clear in `run-next.mjs`, pinned to loopback `HOST`) instead of a deterministic 403 (#11535) ([#11549](https://github.com/diegosouzapw/OmniRoute/pull/11549)) — thanks @jonlwheat2-gif +- **fix(docs):** the Auto-Combo engine is described with the number of factors it actually declares. Four different counts were in circulation — 6, 9, 13 and 14 across `AGENTS.md`, four guides and six code comments — while `DEFAULT_WEIGHTS` declares 15. `check:docs-counts` now reads that number from the source, so adding a factor without updating the prose fails CI ([#11545](https://github.com/diegosouzapw/OmniRoute/pull/11545)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **fix(quota-share):** in-flight leases are per request, so an aborted request's slot ages out instead of being kept alive by later traffic on the same connection ([#11547](https://github.com/diegosouzapw/OmniRoute/pull/11547)) — thanks @abhisheksharma2411 +- The free-tier summary route now serves the refreshed Radar catalog when the feed is active — the same numbers the dashboard shows — and states which catalog answered plus its real build date, instead of always reporting release-frozen figures with a stale curation date. The supporter-key live feed stays reserved to authenticated callers of the instance. ([#11550](https://github.com/diegosouzapw/OmniRoute/pull/11550)) — thanks @maxmad64bis +- **sse:** fix LiveWS/embed-WS servers crashing at startup under the Node/tsx runtime — `liveServer.ts`, `embedWsProxy.ts` and `apiBridgeServer.ts` imported `@/shared/utils/httpClientAbortGuard` without the `.mjs` extension, so the client-abort crash guard added by [#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556) was unreachable and every dependent test failed with `ERR_MODULE_NOT_FOUND` ([#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556)). ([#11671](https://github.com/diegosouzapw/OmniRoute/pull/11671)) — thanks @jonlwheat2-gif / @yxyxy +- fix(chat): shed any of the matched provider-node's routing identifiers (public prefix or internal id) before model lookup, so composites like `//` no longer reach upstream verbatim (#11557) — thanks @maxmad64bis +- **fix(cache):** a semantic-cache entry now expires on its TTL instead of surviving until the next UTC midnight, and `dbEntries` stops counting expired rows ([#11573](https://github.com/diegosouzapw/OmniRoute/pull/11573)) — thanks @ntdatt812 +- **fix(api):** `/v1/models` schedules its stale-while-revalidate rebuild through Next's `after()` again, so a stale catalog reaches the client before the rebuild blocks the event loop ([#11574](https://github.com/diegosouzapw/OmniRoute/pull/11574)) — thanks @ntdatt812 +- **fix(check):** the `known-symbols` executor conformance gate awaits the now-async `getExecutor()`, so it stops reporting all 142 registered aliases as dead symbols and can detect a lazy import that fails ([#11582](https://github.com/diegosouzapw/OmniRoute/pull/11582)) — thanks @ntdatt812 +- **fix(pack):** register `bin/cli/utils/volatileEnvPath.mjs` in `PACK_ARTIFACT_REQUIRED_PATHS`, so the prune can no longer silently drop a module the published CLI imports on every boot ([#11588](https://github.com/diegosouzapw/OmniRoute/pull/11588)) — thanks @ntdatt812 +- **i18n(vi):** translate the five reliability-column strings on the free-provider rankings page, which rendered as `__MISSING__:` markers in the Vietnamese UI ([#11589](https://github.com/diegosouzapw/OmniRoute/pull/11589)) — thanks @ntdatt812 +- **resilience:** restore the expired-connection retry-budget probe in the token-health sweep — the `!isGitHubAccessTokenOnlyConnection` carve-out reintroduced by #11608 contradicted the boundary pinned by #11592, so a GitHub connection parked at `expired` with retry budget remaining was never probed and could never self-heal ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)). ([#11672](https://github.com/diegosouzapw/OmniRoute/pull/11672)) — thanks @jonlwheat2-gif / @ntdatt812 +- **fix(dashboard):** Provider Stats now displays combo success rates on their native 0–100 percentage scale instead of multiplying them twice ([#11595](https://github.com/diegosouzapw/OmniRoute/pull/11595)) — thanks @pacocartones +- **fix(providers):** route DuckDuckGo Web Chat through the verified `duck.ai` endpoint, refresh its free model catalog, and avoid forwarding unsolved anti-abuse challenges ([#11597](https://github.com/diegosouzapw/OmniRoute/pull/11597)) — thanks @solstxce +- **fix(ui):** Console log Refresh and Copy controls now expose localized accessible names, keep copy actions visible on keyboard focus, and announce copy completion safely ([#11599](https://github.com/diegosouzapw/OmniRoute/pull/11599)) — thanks @pacocartones +- Dropped the stale-`.eslintcache` `restore-keys` fallback from both "Restore ESLint file cache" steps in `ci.yml`, so the blocking `Lint` job can no longer be served per-file verdicts computed under a different lint config, suppressions file or lockfile. `quality.yml` had already dropped it in #11963; `ci.yml` — the workflow that actually gates PRs — had not (#11600). ([#11996](https://github.com/diegosouzapw/OmniRoute/pull/11996)) +- fix(ci): pass `--pass-on-unpruned-suppressions` in `run-eslint-json.mjs` so the CI Lint job no longer fails when a suppression is merely orphaned by a genuine fix, mirroring the identical fix already in `validate-release-green.mjs` (#11600) ([#11983](https://github.com/diegosouzapw/OmniRoute/pull/11983)) — thanks @hartmark +- fix(handoff): enforce provider allowlist for universal handoff (#11602) — universal handoff now skips summarization when the selected summary model's provider is not included in the configured provider allowlist. ([#11619](https://github.com/diegosouzapw/OmniRoute/pull/11619)) — thanks @vsd2807 +- **fix(dashboard):** Show a stable error state when Search Analytics returns an HTTP error or malformed data ([#11603](https://github.com/diegosouzapw/OmniRoute/pull/11603)) — thanks @pacocartones +- **test(gamification):** pin the aggregate profile level to the XP-derived semantics of #11604 — `getAggregateXp()` now derives `currentLevel` from the summed XP (`calculateLevel(sum)`), not `MAX(stored current_level)`, and the #3484 fixture levels are aligned with the XP curve ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)). ([#11676](https://github.com/diegosouzapw/OmniRoute/pull/11676)) — thanks @jonlwheat2-gif / @pacocartones +- **fix(dashboard):** Keep the Profile level and progress aligned with aggregate XP, including bounded handling for invalid totals ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)) — thanks @pacocartones +- **fix(dashboard):** Prevent locked hidden badges from revealing their icon or opening private badge details before they are earned ([#11605](https://github.com/diegosouzapw/OmniRoute/pull/11605)) — thanks @pacocartones +- **fix(dashboard):** Restore keyboard focus after shared modals close and cancel delayed autofocus during cleanup ([#11607](https://github.com/diegosouzapw/OmniRoute/pull/11607)) — thanks @pacocartones +- **fix(dashboard):** Enable Enter and Space activation for clickable data-table rows without hijacking nested controls ([#11610](https://github.com/diegosouzapw/OmniRoute/pull/11610)) — thanks @pacocartones +- **fix(autoCombo,sse):** vendor-retired catalog ids are dropped from the auto-combo candidate pool and no longer win on leftover `arena_elo` / `user_override` rows; `getModelLifecycleDecision` consults `model-lifecycle.json` (prefix-stripped) so aggregator traffic is not `untracked` for ids the snapshot already knows ([#11625](https://github.com/diegosouzapw/OmniRoute/issues/11625)) ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)) — thanks @MumuTW +- **test(chatcore):** move Codex/Claude combo fixtures off models the lifecycle guard now rejects — `gpt-5.1-codex`/`gpt-5-codex` are vendor-retired (snapshot, #11626) and `claude-3-5-sonnet-20241022` is shut down, so native-passthrough and combo-fallback tests switched to `gpt-5.6-sol` and `claude-sonnet-4.6` ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)). ([#11675](https://github.com/diegosouzapw/OmniRoute/pull/11675)) — thanks @jonlwheat2-gif / @MumuTW +- fix(opencode-plugin): stop a free-tier budget that rounds up across a magnitude from rendering as `1000K`/`1000M` in the model picker — `fmtTokens` chose its unit from the raw token count and then rounded with `toFixed(1)`, so 999,950–999,999 printed as `1000K` rather than `1M` and just under a billion printed as `1000M` rather than `1B` ([#11684](https://github.com/diegosouzapw/OmniRoute/pull/11684)) — thanks @abhisheksharma2411 +- Cap prose-derived Antigravity quota resets at the configured model cooldown maximum while preserving authoritative Retry-After headers and Google RetryInfo hints. (#11695) Thanks @pacocartones. ([#11823](https://github.com/diegosouzapw/OmniRoute/pull/11823)) +- Compare `_rebaseline_*` notes structurally in the ratchet-bank verifier so an object-valued note no longer reports itself as rewritten on an unmodified tree — the nightly `bank-ratchet-shrinks` lane had been aborting on every run since 2026-08-11, leaving 41,672 lines of earned file-size shrinks unbanked (#11702). ([#11892](https://github.com/diegosouzapw/OmniRoute/pull/11892)) — thanks @MumuTW +- **fix(build):** `prepublish.ts` bundles the ChatGPT Web (Codex) MCP bridge through `runBuildTool()` instead of spawning `npx.cmd` raw, fixing the build crash on Node ≥ 20/Windows where `.cmd` shims cannot be spawned without a shell (EINVAL) ([#11704](https://github.com/diegosouzapw/OmniRoute/issues/11704)) ([#11706](https://github.com/diegosouzapw/OmniRoute/pull/11706)) — thanks @jonlwheat2-gif +- fix(codex): keep `parallel_tool_calls:false` on the translated Codex Responses Lite path (#11707) ([#11984](https://github.com/diegosouzapw/OmniRoute/pull/11984)) — thanks @hartmark +- **fix(tests):** the new Gemini Web Images retirement test (#11708) called the now-async `getExecutor()` synchronously, throwing `TypeError: getExecutor(...).getProvider is not a function` instead of asserting Gemini Web chat stays available. Awaited to match the R0.3 executor-registry refactor (#11220). ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(search):** Allow REST and MCP web-fetch requests to select the advertised `anysearch-search` provider explicitly ([#11716](https://github.com/diegosouzapw/OmniRoute/pull/11716)) — thanks @pacocartones +- **fix(providers):** Keep overnight peak-hour protection active after midnight according to the window's configured start day ([#11718](https://github.com/diegosouzapw/OmniRoute/pull/11718)) — thanks @pacocartones +- **fix(providers):** correct all five g4f.space gateway descriptions to document proof-of-work credits or member API keys, variable limits, and the remote prompt boundary instead of promising free keyless access at five requests per minute ([#11738](https://github.com/diegosouzapw/OmniRoute/pull/11738)). +- **fix(db):** `model_capabilities` is created by a migration instead of lazily on the first models.dev sync, so a clean install and an upgraded install converge on the same schema regardless of which features have run ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(ci):** `check:install-upgrade` now fails on an `npm` install truncated by ENOSPC (npm reports it as a warning and still exits 0), authenticates its health probe so the version assertion works against the hardened health payload, frees the clean-install tree before the upgrade phase, and no longer reports a schema divergence computed from a boot that never served ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(routing):** harden session-affinity key derivation against oversized explicit IDs and empty structural-payload collisions, while recognizing Gemini and common text request shapes ([#11746](https://github.com/diegosouzapw/OmniRoute/pull/11746)) — thanks @MumuTW +- **fix(packages/browser-pool):** regenerate `package-lock.json` so the `packages/browser-pool` workspace's locked `playwright`/`@types/node` (and transitives) match its `package.json` specs, fixing cache-only/offline installs (`npm ci --offline`, Nix `buildNpmPackage`) that previously failed with `ENOTCACHED` ([#11747](https://github.com/diegosouzapw/OmniRoute/issues/11747)) — thanks @benjaminkitt ([#11784](https://github.com/diegosouzapw/OmniRoute/pull/11784)) — thanks @geekyNads +- **fix(assets):** Stop distributing 79 provider and CLI logos without verified source/license provenance, keep every provider/tool available through the internal generic icon, and prevent those IDs from falling through to an external icon CDN. ([#11750](https://github.com/diegosouzapw/OmniRoute/pull/11750)) +- **fix(providers):** retire the common `chatgpt-web` / `cgpt-web` provider because its pre-key and proof-of-work provenance could not be cleared ([#11754](https://github.com/diegosouzapw/OmniRoute/pull/11754)) — explicit chat and image requests, slash-prefixed models, persisted aliases, proxy overrides, and combo targets now fail closed with a sanitized HTTP `410` / `PROVIDER_RETIRED` before upstream dispatch. Migration 163 tombstones matching connections and invalidates their active leases without deleting history or widening API-key allowlists. The independent `chatgpt-web-codex` / `cgpt-codex` provider and its MIT-noticed vendored implementation remain available. +- **fix(usage):** quota and usage refresh no longer 409 when an exclusive lease reserves the connection ([#11758](https://github.com/diegosouzapw/OmniRoute/pull/11758)) — thanks @TheDemonTuan +- Keep the embedding registry's vector width and `embedding` type on models when a synced model exists for the same id, so `/v1/models` no longer reports registry-described embedding models widthless or untyped (#11761) — thanks @rahilmavani +- Correct `google/gemini-embedding-001` on the OpenRouter route to 3072 dimensions, the width it returns when `dimensions` is not sent (#11761) — thanks @rahilmavani +- **fix(vision-bridge):** Restrict automatic vision routing and fallbacks to models available in authoritative live provider catalogs, including aliases and effort variants ([#11767](https://github.com/diegosouzapw/OmniRoute/issues/11767)) — thanks @pacocartones ([#11813](https://github.com/diegosouzapw/OmniRoute/pull/11813)) +- **fix(providers):** reclassify Cerebras as a one-time $5 signup credit (payment method required, 30-day validity), not a recurring no-card 1M tokens/day trial ([#11773](https://github.com/diegosouzapw/OmniRoute/issues/11773)) ([#12591](https://github.com/diegosouzapw/OmniRoute/pull/12591)) — thanks @HouMinXi +- fix(cli): stop prepublish from re-rebuilding the already-built ESM-only opencode-plugin dist (#11787) ([#11990](https://github.com/diegosouzapw/OmniRoute/pull/11990)) — thanks @hartmark +- **fix(db):** Fresh installs no longer log a non-fatal `no such table: compression_run_telemetry` warning when retention cleanup runs before the lazily-created telemetry table exists ([#11802](https://github.com/diegosouzapw/OmniRoute/pull/11802)) — thanks @RaviTharuma +- fix(sse): set X-OmniRoute-Selected-Connection-Id on successful combo dispatches so downstream consumers stop falling back to an empty connection id (#11810) ([#11986](https://github.com/diegosouzapw/OmniRoute/pull/11986)) — thanks @hartmark +- **fix(services):** embedded CLIProxyAPI lifecycle checks now use public `/healthz`, while model discovery uses the configured dedicated data-plane API key instead of the management password ([#11811](https://github.com/diegosouzapw/OmniRoute/pull/11811)) — thanks @RaviTharuma +- Fix persisted model steps returning an error in the playground route simulator, and warn when structural steps cannot be simulated. ([#11862](https://github.com/diegosouzapw/OmniRoute/pull/11862)) — thanks @pacocartones +- **fix(providers):** Antigravity's dynamic mitmAlias table no longer routes `gemini-3.7-flash-{high,medium,low}` to a literal tier-suffixed upstream id just because one connected account's own discovery listed it directly — those display ids always resolve through the safe `gemini-3.7-flash-tiered` static alias, so one account's Google-provisioned access no longer 404s every sibling account of the provider ([#11824](https://github.com/diegosouzapw/OmniRoute/issues/11824), [#11651](https://github.com/diegosouzapw/OmniRoute/issues/11651)) ([#11988](https://github.com/diegosouzapw/OmniRoute/pull/11988)) — thanks @hartmark +- **fix(dashboard):** Expose Profile loading, errors, page structure, and XP progress to assistive technologies ([#11838](https://github.com/diegosouzapw/OmniRoute/pull/11838)) — thanks @pacocartones +- **fix(socks):** `Agent.connectTimeout` now bounds both SOCKS handshake and TLS connect, and `family === null` no longer falls back to `fetch-socks` ([#11842](https://github.com/diegosouzapw/OmniRoute/pull/11842)) — thanks @maxmad64bis +- **perf(compression):** OOM mitigations for large payload hashing, memoization, and token estimation ([#11844](https://github.com/diegosouzapw/OmniRoute/pull/11844) — thanks @AndrianBalanescu) +- Excluded Next.js Node File Trace manifests (`*.nft.json`) from the published npm tarball. They are build-time metadata and are never read while serving, but had grown to 668.7 MB — 61% of the package — which pushed the upload past the registry limit and made `npm publish` fail with `413 Payload Too Large`. ([#11864](https://github.com/diegosouzapw/OmniRoute/pull/11864)) +- **fix(cli):** keep the global OmniRoute server `--base-url` separate from provider-node `--endpoint` values in add, update, and validate commands ([#11860](https://github.com/diegosouzapw/OmniRoute/pull/11860)) — thanks @pacocartones +- **fix(config):** Nous Research's `Hermes-4-405B` model now displays as "Hermes 4 405B (Nous Research)" in both the provider registry and the free-model catalog, instead of the mislabelled "Hermes 4 7B" ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#11993](https://github.com/diegosouzapw/OmniRoute/pull/11993)) +- **fix(provider/nous):** inject required user= tag into Nous Research inference requests to resolve upstream 400 "missing tags" error ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#12044](https://github.com/diegosouzapw/OmniRoute/pull/12044)) +- **fix(build):** `npm run build` now fails in one second with a named package and a copy-pasteable fix when npm silently drops an externalised optional native dependency, instead of dying four minutes in with `Module not found: Can't resolve 'better-sqlite3'` ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- **fix(install):** `postinstall` no longer throws `ReferenceError: isAndroid is not defined` — failing the whole `npm install` — when the `better-sqlite3` rebuild fallback times out; the manual-fix guidance is reachable again ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- Fixed the Alibaba free-tier allowlist test that went red on its own once the shipped catalog's `validUntil` (2026-08-27) passed, leaving every PR and `main` with a failing `Unit Tests (1/8)`. The test now builds its own packs with dates it controls, and covers the expired-pack fallback that production has actually been serving. ([#11867](https://github.com/diegosouzapw/OmniRoute/pull/11867)) +- Split the npm registry upload into its own GitHub-hosted job. npm refuses `--provenance` from a self-hosted runner (`422 ... Only "github-hosted" runners are supported`), which blocked the v3.8.50 publish; the heavy verification cannot move to a hosted runner, so it now hands the proven tarball over instead. ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(ui):** the shared `DataTable` loading state no longer reads its decorative ⏳ glyph out to assistive technology, and now carries the same `role="status"` / `aria-live="polite"` / `aria-busy="true"` semantics as `PageLoading` ([#11869](https://github.com/diegosouzapw/OmniRoute/pull/11869)) — thanks @pacocartones +- **fix(api):** Bound the `timeout` accepted by `POST /api/providers/[id]/login` to the same 15s–600s window the Adobe Firefly and Conol browser-login paths already enforce, so a request can no longer pin a headful browser and the single active-login slot with an arbitrarily large poll budget. ([#11871](https://github.com/diegosouzapw/OmniRoute/pull/11871)) — thanks @pacocartones +- **fix(api):** Stop the tunnel and MITM routes from returning raw child-process error text, which disclosed host paths, binary install locations and Tailscale `tskey-*` credentials in a body some of these routes serve to non-loopback callers, and return a real 400 instead of a framework 500 when a tunnel request body fails validation. ([#11872](https://github.com/diegosouzapw/OmniRoute/pull/11872)) — thanks @pacocartones +- **fix(open-sse):** stream TTFT/ITL are sampled from a monotonic clock (`performance.now`) instead of `Date.now`, so an NTP correction or wall-clock jump can no longer inflate a genuine TTFT or produce a negative one that the `ttft >= 0` guard silently drops — protecting the router's OTel/EWMA/`usage_history`/speed-ranking signals ([#11873](https://github.com/diegosouzapw/OmniRoute/pull/11873)) — thanks @pacocartones +- **fix(providers):** xAI `reasoning_effort: "xhigh"` now reaches grok-4.6+ instead of being silently clamped to `"high"` ([#11879](https://github.com/diegosouzapw/OmniRoute/pull/11879)) — thanks @NoxzRCW +- **fix(dashboard):** the providers page no longer crashes into the error boundary when a provider id collides with an `Object.prototype` member (`constructor`, `__proto__`); icon lookups are own-property guarded ([#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)) — thanks @NoxzRCW +- **fix(skills):** injected skill tools declared in shorthand (`{"content": "string"}`) now forward valid JSON Schema, unblocking providers that validate tool schemas strictly such as Zhipu GLM on the Console Go tier ([#11881](https://github.com/diegosouzapw/OmniRoute/pull/11881)) — thanks @NoxzRCW +- **fix(api):** `POST /api/playground/simulate-route` now surfaces `combo-ref` and `provider-wildcard` persisted combo steps with a specific warning (naming the referenced combo, or the unresolved `provider/model` wildcard) instead of folding them into a generic "unsupported step" count; a `provider-wildcard` step is also now included as an unresolved target so the operator can see it is in the route (ported from [#11882](https://github.com/diegosouzapw/OmniRoute/pull/11882) — thanks @NoxzRCW). ([#11930](https://github.com/diegosouzapw/OmniRoute/pull/11930)) +- **fix(translator):** the streaming OpenAI→Claude translator keeps upstream usage, including prompt-cache tokens, when it arrives on a trailing `choices: []` chunk (Fireworks and any upstream using `stream_options.include_usage`) ([#11883](https://github.com/diegosouzapw/OmniRoute/pull/11883)) — thanks @NoxzRCW +- **fix(cli):** `omniroute update --apply` now tells you explicitly whether a running server was detected and, if so, that you must run `omniroute restart` to apply the update — it never restarted anything and previously implied the update was already live once files were installed. The dashboard's npm-mode Update flow (`/api/system/version`) now tries OmniRoute's own PID-file-managed supervisor before falling back to pm2, and reports an honest "restart required" step instead of a silent pm2-only "skipped" that read like a completed update. The server-side latest-version lookup backing the dashboard's update banner also gained `--prefer-online`, closing the same stale-npm-cache class already fixed in the CLI's own copy for #4376 ([#11885](https://github.com/diegosouzapw/OmniRoute/issues/11885)). ([#12005](https://github.com/diegosouzapw/OmniRoute/pull/12005)) +- **fix(resilience):** clear persisted LKGP pins when a target suffers connection/provider exhaustion or is skipped before dispatch due to cooldown/exhaustion/unavailability, preventing subsequent requests from repeatedly prioritizing known-dead providers ([#11911](https://github.com/diegosouzapw/OmniRoute/issues/11911)). ([#12013](https://github.com/diegosouzapw/OmniRoute/pull/12013)) — thanks @HouMinXi +- **fix(sse):** the OpenAI→Claude stream translator now defers the terminal `message_delta`/`message_stop` emission until the real usage block has arrived (or a genuine end-of-stream flush forces it) instead of emitting it immediately on `finish_reason` — previously, when the trailing usage-only chunk (`{"choices":[],"usage":{...}}`) arrived *after* the `finish_reason` chunk (the normal order for Fireworks/vLLM/Together and other `stream_options.include_usage` upstreams), the client-visible `message_delta` still carried stale/zero usage even though `state.usage` was internally corrected too late to matter (ported from [#11915](https://github.com/diegosouzapw/OmniRoute/pull/11915) — thanks @HouMinXi). ([#11933](https://github.com/diegosouzapw/OmniRoute/pull/11933)) +- **fix(sse):** `stripStore()` now forces `store=false` for stateless OpenAI-compatible Responses-API targets (unless the connection has explicitly opted in via `providerSpecificData.openaiStoreEnabled`), instead of only handling the `openai`/`agentrouter` cases — a client-supplied `store` value previously passed through untouched to backends that don't actually persist responses server-side ([#11916](https://github.com/diegosouzapw/OmniRoute/pull/11916) — thanks @HouMinXi). +- **fix(models):** custom provider-node models (synced, custom, and alias-backed) now appear under their configured prefix in the unified catalog when the operator's model-id prefix mode is canonical, instead of being dropped whenever alias-inclusion was otherwise disabled ([#11918](https://github.com/diegosouzapw/OmniRoute/pull/11918) — thanks @HouMinXi). +- **fix(models):** the unified model catalog now suppresses stale static registry models (including effort-tier variants) for any provider whose active connection has an authoritative live synced catalog, not only providers already using exclusive-synced-listing — a connection with `providerUsesAuthoritativeLiveCatalog` previously kept serving both the live-synced models and the stale static rows side by side ([#11919](https://github.com/diegosouzapw/OmniRoute/pull/11919) — thanks @HouMinXi). +- **fix(ui):** `ProviderIcon`'s three remaining unguarded lookups (`PROVIDER_ICON_ALIASES`, `LOCAL_SVG_ALIASES`, `THEMED_SVGS`) now use `Object.hasOwn()` own-property checks — a provider id such as `constructor` or `__proto__` previously resolved through the prototype chain instead of falling through to the unknown-provider CDN fallback (`getLobeProviderIcon()` itself was already guarded by [#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)); `ProviderPageHeader`'s `color` field is also now optional, matching the rest of the component's defensive typing (ported from [#11920](https://github.com/diegosouzapw/OmniRoute/pull/11920) — thanks @HouMinXi). ([#11935](https://github.com/diegosouzapw/OmniRoute/pull/11935)) — thanks @NoxzRCW +- fix(ollama): preserve multi-byte UTF-8 content split across stream chunks in the Ollama NDJSON transform, which previously corrupted CJK/emoji into U+FFFD (#11921) — thanks @pacocartones +- **fix(providers):** OrcaRouter chat requests now target `/v1/chat/completions` instead of the bare `/v1` API root, fixing the upstream `404 Invalid URL (POST /v1)` ([#11923](https://github.com/diegosouzapw/OmniRoute/pull/11923)). — thanks @echel0nn +- **fix(plugins):** deliver the `onStreamComplete` event to disk-installed plugins. The event shipped in v3.8.50 (#9669) was emitted internally but had no plugin-facing wiring, so no plugin could ever subscribe: the manifest schema silently dropped `hooks.onStreamComplete`, and the loader/manager only knew the seven legacy hooks. `onStreamComplete` is now a declarable manifest hook, wired through the loader and registered by the manager like the other hooks, and its payload carries a `requestId` so consumers can correlate the stream-completion event with the originating request ([#11934](https://github.com/diegosouzapw/OmniRoute/pull/11934)) — thanks @amaleta / @pacocartones +- **fix(db):** the Qdrant embedding-model dropdown now lists local/self-hosted providers (Ollama, LM Studio, vLLM, etc.) — an active connection is treated as "configured" when the provider allows an optional API key, not only when it has a real key or OAuth, so a running local embedding provider is no longer hidden from the picker ([#11949](https://github.com/diegosouzapw/OmniRoute/issues/11949)) ([#11995](https://github.com/diegosouzapw/OmniRoute/pull/11995)) +- **fix(providers):** Vertex AI Anthropic partner-model discovery now calls the Model Garden `v1beta1` publisher list (`/v1beta1/publishers/anthropic/models`, global) and parses its `publisherModels` envelope, so Claude models auto-synced from Vertex populate the active live catalog and route at request time instead of returning `Model '' is not available in the active live catalog` ([#11991](https://github.com/diegosouzapw/OmniRoute/issues/11991)) — thanks @fabioluissilva ([#11998](https://github.com/diegosouzapw/OmniRoute/pull/11998)) +- **fix(cli):** support `--base-url` alongside `--endpoint` in `omniroute nodes add`, `update`, and `validate` subcommands to prevent global `--base-url` shadowing issues ([#11999](https://github.com/diegosouzapw/OmniRoute/issues/11999)). ([#12033](https://github.com/diegosouzapw/OmniRoute/pull/12033)) — thanks @HouMinXi +- **fix(providers):** `cloudflare-ai` no longer refuses image content parts for every Workers AI model ([#12002](https://github.com/diegosouzapw/OmniRoute/pull/12002)) — the plain-string `content` requirement behind #2539 is carried by the _model_ schema, not by the `/ai/v1/chat/completions` endpoint (measured: an all-text part array returns 200 on `@cf/mistralai/mistral-small-3.1-24b-instruct`, `@cf/meta/llama-4-scout-17b-16e-instruct` and `@cf/meta/llama-3.3-70b-instruct-fp8-fast`, and 400 on the text-only `@cf/qwen/qwen2.5-coder-32b-instruct`). `transformRequest()` flattened every array and threw on the first non-text part (#6390), so image input was refused for vision-capable Cloudflare models that accept it. All-text arrays are still flattened — the one shape every model accepts — while an array carrying a non-text part is passed through untouched, so the attachment is still never silently dropped. Regression guards: `tests/unit/cloudflare-ai-image-parts-6390.test.ts`. — thanks @davidlinfr +- **fix(resilience):** decouple the limiter-managed execution backstop from the queue-wait budget — new `requestQueue.executionMaxWaitMs` (env `RATE_LIMIT_EXECUTION_MAX_WAIT_MS`, default 600000 = 10 min) now feeds Bottleneck's post-dispatch `expiration`, while `requestQueue.maxWaitMs` keeps its documented queue-wait semantics. Previously the queue-wait budget doubled as the execution expiration, so legitimate long-running calls on non-incremental gateways (whole generation buffered before the first upstream byte, e.g. Console Go / Command Code tiers serving GLM models) were killed mid-flight at the queue budget with a false 504 `RATE_LIMIT_EXECUTION_TIMEOUT` — the local limiter undercut the provider-aware upstream fetch-start timeouts. The surfaced 504 message now names `requestQueue.executionMaxWaitMs`; the error keeps the #4165 guarantees (disclaims an upstream timeout, preserves the Bottleneck error as `cause`, branded code + trusted provenance, classified request-scoped so combo falls back). A real queue-wait bound (the `Promise.race` around `limiter.schedule()` sketched in #9533) remains future work. (#12025) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **Call logs:** keep the `error` field when an artifact exceeds the storage cap, instead of replacing it with the omission marker. The error is the only field that says *why* a request failed and is typically ~90 bytes next to the multi-hundred-KB bodies that trip the cap, so dropping it left a size-limited row undiagnosable — a provider outage, a local timeout and an upstream 400 all rendered identically. It is now preserved at every fallback stage, truncated to 4KB if it is itself large ([#12026](https://github.com/diegosouzapw/OmniRoute/issues/12026)). ([#12095](https://github.com/diegosouzapw/OmniRoute/pull/12095)) — thanks @ntdat812 +- **fix(diagnostics):** preserve the error field (truncated to 4KB with a `[truncated: …]` suffix) in every call-log artifact size-limit fallback stage. Previously the minimal fallback replaced the error with `[omitted: call log artifact size limit exceeded]`, so an oversized artifact row showed nothing about WHY the request failed — e.g. 91 of 847 opencode-go 504 rows on one production instance were undiagnosable from the dashboard. Oversized request/response bodies are still omitted exactly as before; the error cap is independent of the payload sizes that tripped the fallback. (#12026) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **fix(sse):** OpenAI Responses clients that declare the native `web_search` tool now receive a spec-shaped `web_search_call` output item with `action.sources` alongside the preserved function-call round-trip, so search results executed through OmniRoute's own search backend are consumable by standard Responses clients (Codex, pi-web-access, …). ([#12031](https://github.com/diegosouzapw/OmniRoute/pull/12031)) — thanks @watchingdogs +- **fix(cli):** use in-thread alias resolver hooks on modern runtimes to avoid deprecation noise and improve Node.js forward compatibility ([#12073](https://github.com/diegosouzapw/OmniRoute/issues/12073)). ([#12083](https://github.com/diegosouzapw/OmniRoute/pull/12083)) — thanks @xiaoyaner0201 +- **fix(combo):** an operator-set **Agent Features → Context length** on a combo is now honored at request time. The value was persisted and advertised through `/v1/models`, but `resolveComboContextLimit()` never consulted it — so a multi-target combo whose members carry no per-model window fell through to the provider's generic `defaultContextLength` (openrouter 128000, command-code 200000) and rejected large requests with `Input exceeds context window … limit 128000` despite the combo being explicitly sized much larger. An identical single-target combo worked, because it collapses to its concrete target before the guard runs. Invalid values (0/negative/NaN/Infinity) are ignored, so the existing target → combo-min → fallback order is unchanged. ([#12090](https://github.com/diegosouzapw/OmniRoute/pull/12090)) — thanks @adivekar-utexas +- **fix(sse):** passthrough streams now estimate usage on finish when upstream closes without usage even with `stream_options.include_usage` — avoids `0 tokens / 0%` for providers that stay silent (and correctly handles trailing empty-choices usage) ([#12151](https://github.com/diegosouzapw/OmniRoute/pull/12151)) — thanks @maxmad64bis +- **fix(combos):** clearing an agent feature in the combos editor now persists — unchecking context cache protection, or emptying the system message or tool filter, sends an explicit `null` instead of dropping the field from the `PUT` body, which the update merge read as "leave unchanged" ([#12177](https://github.com/diegosouzapw/OmniRoute/pull/12177)) — thanks @foreveryh / @ntdatt812 +- **fix(memory):** self-hosted embedding endpoints now vectorize — the vector width is measured from the first embedding that comes back instead of being read from a registry that cannot describe them, so `vec_memories` is created and memories stop piling up unvectorized behind a green health check ([#12180](https://github.com/diegosouzapw/OmniRoute/pull/12180)) — thanks @kanade-hoshino / @ntdatt812 +- **fix(translator):** the leading `system` message now reaches Responses-API upstreams when its `content` is a content-part array — it was read as `typeof content === "string" ? content : ""`, so a prompt-caching client (Anthropic `cache_control`, the shape LiteLLM and the Anthropic SDK emit) had its entire system prompt replaced by an empty `instructions`. The request was still accepted with a normal `prompt_tokens` count, so the model answered with no instructions and nothing in the response said they were missing. Mid-conversation system turns already handled the array shape ([#7056](https://github.com/diegosouzapw/OmniRoute/pull/7056)); only the first one did not ([#12206](https://github.com/diegosouzapw/OmniRoute/issues/12206)). Regression guard: `tests/unit/translator-openai-responses-system-content-parts.test.ts`. ([#12207](https://github.com/diegosouzapw/OmniRoute/pull/12207)) — thanks @Gorillaz322 +- **fix(free-tier):** `/api/free-tier/summary` no longer computes its totals from a Radar feed built before the catalog the running release ships. When the cached feed is older — or carries no build date at all — the route answers from the shipped catalog, resolved through the operator's local model state so disabled and tombstoned models stay out of the numbers ([#12215](https://github.com/diegosouzapw/OmniRoute/pull/12215)). — thanks @maxmad64bis +- **fix(oauth):** Keep a Claude personal workspace and a Team organization as separate connections — they share the same email and `accountUUID`, so the email-only OAuth dedup let the second login overwrite the first account's tokens; `organizationUUID` now disambiguates them, the way `workspaceId` does for Codex ([#12222](https://github.com/diegosouzapw/OmniRoute/pull/12222)) — thanks @davidebaraldo +- **fix(resilience):** a 402 on a single paid model of a passthrough/gateway provider (e.g. `kilo-gateway`, `ollama-cloud`) no longer terminalizes the whole connection with a never-auto-recovered `credits_exhausted` status — only the paid model is locked out, so free models on the same key keep serving. 402 variant of [#3027](https://github.com/diegosouzapw/OmniRoute/issues/3027). Single-credential providers are unaffected — a 402 there is still treated as the key being genuinely out of credit ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239) / [#10616](https://github.com/diegosouzapw/OmniRoute/issues/10616)) ([#12242](https://github.com/diegosouzapw/OmniRoute/issues/12242)) — thanks @brick30llc-ctrl ([#12266](https://github.com/diegosouzapw/OmniRoute/pull/12266)) — thanks @geekyNads +- **fix(sse):** trust `finish_reason: "length"`/`"max_tokens"` over the reasoning-consumed-token ratio in response quality validation, so a reasoning model truncated below the old 90% threshold correctly fails and retries instead of returning empty content as a silent "success" ([#12262](https://github.com/diegosouzapw/OmniRoute/pull/12262)) — thanks @brick30llc-ctrl +- **fix(settings):** `PUT /api/settings/cache-config` now persists `alwaysPreserveClientCache` to the flat general settings the runtime cache-control policy actually reads; previously the value landed in the databaseSettings "cache" section and was silently ignored, so the endpoint had no effect on `cache_control` passthrough ([#12304](https://github.com/diegosouzapw/OmniRoute/pull/12304)) — thanks @davidebaraldo +- **fix(grok-cli):** treat omitted SuperGrokPro `creditUsagePercent` as 0% used so Provider Limits still renders a weekly bar (proto3 zero-elision) ([#12312](https://github.com/diegosouzapw/OmniRoute/pull/12312)) — thanks @HouMinXi +- **fix(combo):** Expose the two Auto-Combo scoring factors nobody could set — the combo validation schema and the dashboard weight sliders both declared 13 of the scorer's 15 factors, so `connectionDensity` (spreads load across a provider's connections) and `quality` were dropped on save and offered nowhere. The sliders also shipped their own default table that differed from the engine's on every non-zero factor and summed to 1.05, so the percentages shown next to them added up to 105%. Both lists now match `DEFAULT_WEIGHTS`, and a test keeps them there. Note that a combo whose stored `weights` omitted the two keys was effectively running with them at zero and the other thirteen renormalized upward; it now runs with the engine's intended distribution, so its routing does shift ([#12314](https://github.com/diegosouzapw/OmniRoute/pull/12314)) — thanks @maxmad64bis +- **fix(docs):** The free-tier reference no longer says its numbers come "confidence tagged per row" — no catalog entry carries a confidence tag and the API serves none, so every figure on that page is an estimate of the same, unstated quality. The page now states what an entry does vouch for: an independently documented hard stop (set by hand with the source in a comment, never defaulted to `true`) and a prompt-training disclosure, both with live counts the `check:docs-counts` gate keeps honest ([#12318](https://github.com/diegosouzapw/OmniRoute/pull/12318)) — thanks @maxmad64bis +- **fix(usage):** `adobe-firefly` and `firefly` have had a working usage fetcher since Adobe Firefly landed, but neither was ever added to the registration list, so the provider-plugin manifest, `genericQuotaFetcher` and the free-access quota cache all reported them as having no usage support — while `USAGE_SUPPORTED_PROVIDERS` said the opposite. Both are now declared, which also means their credit balance is fetched like any other declared provider's: `registerGenericQuotaFetchers` now registers a generic quota fetcher for them, and `resolveFreeAccessState` no longer returns early. A test holds the registration list to the dispatcher's switch in both directions, which is what the module's own docstring already asked for in prose ([#12321](https://github.com/diegosouzapw/OmniRoute/pull/12321)) — thanks @maxmad64bis +- **fix(quota):** drop the generic quota cache (agy / Antigravity / Claude OAuth) on an upstream 429 so reset-aware scoring does not keep a 60s stale snapshot, and force-refresh the next usage fetch so inner provider caches cannot recache the same window ([#12325](https://github.com/diegosouzapw/OmniRoute/pull/12325)) — thanks @HouMinXi +- **fix(combos):** deleting a combo now clears its persisted LKGP pins instead of leaving unreachable `key_value` rows behind ([#12326](https://github.com/diegosouzapw/OmniRoute/issues/12326)) ([#12425](https://github.com/diegosouzapw/OmniRoute/pull/12425)) — thanks @KrzysiekSko +- **fix(sse):** Keep ZWNJ (U+200C) and ZWJ (U+200D) in assistant text, reasoning and tool-call arguments — Persian/Kurdish half-space (`ارائه‌دهنده`), Arabic/Indic shaping and emoji sequences no longer lose them; the response de-obfuscation now removes joiners only between ASCII word characters, where the request side inserts them ([#12186](https://github.com/diegosouzapw/OmniRoute/issues/12186)) — thanks @rezjalibd ([#12359](https://github.com/diegosouzapw/OmniRoute/pull/12359)) — thanks @pacocartones +- **fix(resilience):** count resolved upstream 5xx results against the provider circuit breaker on the chat path — `CircuitBreaker.execute()` no longer reads a resolved `{ success: false, status: 5xx }` as a success that cancels the call-site failure, so a provider answering 503s now trips its breaker instead of staying `CLOSED` at `failureCount: 1`; single-model and combo dispatches are each accounted exactly once ([#12254](https://github.com/diegosouzapw/OmniRoute/issues/12254)) ([#12360](https://github.com/diegosouzapw/OmniRoute/pull/12360)) — thanks @pacocartones +- **fix(providers):** resolve the Codex quota auto-ping model from the live provider catalog and lifecycle registry instead of the retired `gpt-5.1-codex-mini`, and pause the ping with one actionable warning when no selectable Codex model exists rather than retrying a shut-down id every cooldown window ([#11905](https://github.com/diegosouzapw/OmniRoute/issues/11905)) ([#12361](https://github.com/diegosouzapw/OmniRoute/pull/12361)) — thanks @pacocartones +- **fix(api):** keep the `{created, data}` wrapper on combo-routed `/v1/images/generations` responses and default Codex image results to `b64_json` on both `/v1/images/generations` and `/v1/images/edits` so Codex CLI's built-in `image_gen` can decode them ([#12268](https://github.com/diegosouzapw/OmniRoute/issues/12268)) ([#12362](https://github.com/diegosouzapw/OmniRoute/pull/12362)) — thanks @pacocartones +- **fix(sse):** Name the shadowed custom provider node when a built-in provider id/alias (e.g. `openference` → `of`) reserves the prefix of an existing OpenAI/Anthropic-compatible node, so the runtime `No active credentials for provider: ` error explains that the prefix routed to the built-in and never reached the node's healthy connections, instead of contradicting the dashboard ([#11943](https://github.com/diegosouzapw/OmniRoute/issues/11943)) — thanks @morpheus9393 ([#12365](https://github.com/diegosouzapw/OmniRoute/pull/12365)) — thanks @pacocartones +- **fix(i18n):** wrap `ccOnboardingKeyPlaceholder` in ICU single quotes across all 43 locales so angle brackets render literally instead of being parsed as rich-text tags, which crashed the Claude Code onboarding block with `INVALID_MESSAGE: INVALID_TAG` ([#12302](https://github.com/diegosouzapw/OmniRoute/issues/12302)) ([#12369](https://github.com/diegosouzapw/OmniRoute/pull/12369)) — thanks @gonisulaimann +- **fix(models):** publish `effort_tiers` on Kimi K3's synced base-model entries (`kmca/k3`, `kmca/k3-256k`) so catalog-only clients (OpenCode, plain SDK pickers) can see and select the reasoning tiers (`low`/`high`/`max`) the synced metadata already carried — the `isSkippedEffortProvider` gate no longer suppresses tier visibility on those base entries, while synthetic `-` variant generation stays prevented and Codex/GLM base models remain excluded unchanged ([#12299](https://github.com/diegosouzapw/OmniRoute/issues/12299)) ([#12371](https://github.com/diegosouzapw/OmniRoute/pull/12371)) — thanks @gonisulaimann +- **fix(guardrails):** keep `auto`/`auto/*` virtual combos exempt from the Vision Bridge `fixedModel` credential guard so a combo target is passed through instead of silently falling back to global auto-selection ([#12237](https://github.com/diegosouzapw/OmniRoute/issues/12237)) ([#12373](https://github.com/diegosouzapw/OmniRoute/pull/12373)) — thanks @pacocartones +- **fix(combo):** capability-filter exhaustion caused by `max_tokens` above every target's known output limit now reports that reason (requested `max_tokens` vs the pool's highest known ceiling) instead of the unrelated "supports structured output" message ([#12229](https://github.com/diegosouzapw/OmniRoute/issues/12229)) — thanks @DW-MediaLab ([#12374](https://github.com/diegosouzapw/OmniRoute/pull/12374)) — thanks @pacocartones +- **fix(auth):** the `least-used` account strategy now prefers accounts without backoff before falling back to oldest `lastUsedAt`, the same tie-break `round-robin` already applies, so a failover no longer lands on a just-rate-limited account for a single request ([#12279](https://github.com/diegosouzapw/OmniRoute/issues/12279)) — thanks @tenshiak ([#12375](https://github.com/diegosouzapw/OmniRoute/pull/12375)) — thanks @pacocartones +- **fix(docker):** the `chatgpt-web-codex-browser` image now finds the Chrome binary under `chrome-linux64/` (Chrome for Testing layout in `playwright:v1.62.0-noble`) as well as the legacy `chrome-linux/`, so the container no longer crash-loops with `exec: --headless=new: not found` ([#12024](https://github.com/diegosouzapw/OmniRoute/issues/12024)) ([#12376](https://github.com/diegosouzapw/OmniRoute/pull/12376)) — thanks @pacocartones +- **fix(providers):** declare `groq/compound` and `allam-2-7b` as non-reasoning models in the curated Groq registry so `reasoning_effort` / `output_config.effort` / `thinking` from Claude Code are stripped instead of forwarded, which Groq rejected with HTTP 400 ([#12134](https://github.com/diegosouzapw/OmniRoute/issues/12134)) ([#12379](https://github.com/diegosouzapw/OmniRoute/pull/12379)) — thanks @pacocartones +- **fix(executors):** `OpencodeExecutor` no longer forces a direct connection when the connection has a proxy assigned in Proxy Management but no per-account proxies: the single-account fast path used to wrap the upstream dispatch in the direct-egress sentinel, discarding the ambient proxy context the chat handler had pinned from `proxy_assignments`, so API-key `opencode`/`opencode-go` connections egressed from the host IP (and hit geoblocks) despite the assignment. The direct pin is now applied only when no ambient proxy context exists ([#11894](https://github.com/diegosouzapw/OmniRoute/issues/11894) — thanks @hizzt) ([#12380](https://github.com/diegosouzapw/OmniRoute/pull/12380)) — thanks @pacocartones +- **fix(api):** `GET /v1/models` with `MODELS_CATALOG_PREFIX_MODE=canonical` (or `?prefix=canonical`) now lists providers whose registry alias is undefined or equal to their own id (Antigravity, Antigravity CLI and other self-aliased built-ins) — their single `provider/model` id was dropped by the alias/canonical duplicate guard in the static, synced, custom and alias-backed catalog loops ([#12058](https://github.com/diegosouzapw/OmniRoute/issues/12058)) — thanks @cheynetom ([#12381](https://github.com/diegosouzapw/OmniRoute/pull/12381)) — thanks @pacocartones +- **fix(translator):** Drop replayed `thinking` blocks that carry no signature (the shape produced from cross-provider `reasoning_content`) instead of stamping the default Claude signature on them, which Anthropic rejected with `400 Invalid signature in thinking block` on the next turn served by an Anthropic rung ([#12105](https://github.com/diegosouzapw/OmniRoute/issues/12105)) — thanks @atescivitci-cmd ([#12386](https://github.com/diegosouzapw/OmniRoute/pull/12386)) — thanks @pacocartones +- **fix(cli):** Resolve Bun's `--preload` polyfill path against the package root instead of `dist/`, so `omniroute` installed with `bun install -g` no longer crashes at startup with `error: preload not found …/dist/open-sse/utils/setupPolyfill.ts` ([#11980](https://github.com/diegosouzapw/OmniRoute/issues/11980)) — thanks @joglomedia ([#12387](https://github.com/diegosouzapw/OmniRoute/pull/12387)) — thanks @pacocartones +- **fix(providers):** `gemini-business` now publishes its model catalog — `/v1/models` and `/v1/providers/gemini-business/models` list the 12 enterprise Gemini ids the executor understands instead of returning an empty list (#12107) ([#12389](https://github.com/diegosouzapw/OmniRoute/pull/12389)) — thanks @pacocartones +- **fix(db):** install `busy_timeout` before the SQLite connection's first statement so a process opening the database while another one closes its WAL connection waits out the transient EXCLUSIVE lock instead of dying with `database is locked`, and recognise the drivers' real BUSY/PROTOCOL/IOERR errors as transient in the corruption probe so the same lock no longer renames the database away as corrupt; deflakes `cross-process contenders never both acquire the same connection` (#12394 — thanks @pacocartones) +- **fix(chat-admission):** derive the `chat_admission_busy` 503 `Retry-After` from observed heavyweight-lease occupancy — the larger of the exhausted `OMNIROUTE_CHAT_ADMISSION_QUEUE_MS` window and the time since capacity last turned over, capped at 60 s — instead of a fixed 1 s (structural) / 2 s (byte-stage) hint that invited Codex/agent fan-out clients to re-send ~1 MiB `/v1/responses` bodies every second into a gate held for the whole SSE lifetime; an idle gate keeps the historical floors ([#12135](https://github.com/diegosouzapw/OmniRoute/issues/12135)) (#12395 — thanks @pacocartones) +- **fix(api-manager):** the API key permissions modal no longer silently drops `allowedCombos` entries its Combo picker cannot render — routing-rule names such as `rt-*`, which the backend already honours — when "All" is clicked and the key is switched back to "Restrict"; those entries now survive the toggle, are listed read-only under the combo list so the count and the list agree, and are saved back verbatim instead of persisting `[]` (deny-all) (#12397 — thanks @pacocartones) +- **fix(catalog):** write the NUL separator of the catalog connection memo key, the provider serviceKind memo key, the Video Bridge promotion group key and a JSON-exactness test fixture as the `\u0000` escape instead of a raw byte — same runtime value, but the raw byte made git, GitHub and ripgrep treat those files as binary (hidden PR diffs, silently skipped searches); a guard test now keeps raw NUL bytes out of `src/`, `open-sse/` and `tests/` (#12403 — thanks @pacocartones) +- **fix(providers):** add `CLAUDE_CODE_CLIENT_VERSION` and `GITHUB_COPILOT_CLI_VERSION` env overrides so Anthropic/Copilot client-version gates can be unblocked without a rebuild ([#12417](https://github.com/diegosouzapw/OmniRoute/issues/12417)) ([#12632](https://github.com/diegosouzapw/OmniRoute/pull/12632)) — thanks @HouMinXi +- **fix(db):** back-fill `last_ping_at` and `last_pinged_reset_key` on `provider_connections` during schema reconciliation so divergent lineages that skipped `123_quota_auto_ping` still accept quota auto-ping writes ([#12470](https://github.com/diegosouzapw/OmniRoute/pull/12470) — thanks @KooshaPari) +- **fix(ci):** document MIT exceptions for `@eloqnt/{config,format-json,format-po}` (next-intl transitive; locked tarballs omit `license`) and keep the A2A lifecycle vitest off the real SQLite persistence seam ([#12581](https://github.com/diegosouzapw/OmniRoute/issues/12581)) ([#12595](https://github.com/diegosouzapw/OmniRoute/pull/12595)) +- OpenCode plugin `/v1/models` catalog fetch now waits 30s by default and attaches HTTP `statusCode` on 401/5xx so host fallback plugins can hop instead of seeing an untyped AbortError/UnknownError. ([#12607](https://github.com/diegosouzapw/OmniRoute/pull/12607)) — thanks @RaviTharuma +- **CI:** the OpenAPI security-tier gate now mirrors `isAlwaysProtectedPath()` in full — it also reads `ALWAYS_PROTECTED_API_PATTERNS`, so the pattern-gated credential routes (`/api/providers/{id}/{claude,codex}-auth/{export,apply-local}`, GHSA-5926-2w35-7h4q) no longer report as unannotated. (#12605) +- **fix(api):** GET /v1/models no longer waits forever on a hung coalesced catalog rebuild; cold-path waits are bounded (`CATALOG_BUILD_TIMEOUT_MS`, default 8s) and a last-good 200 is served when the rebuild times out ([#12627](https://github.com/diegosouzapw/OmniRoute/issues/12627)). ([#12628](https://github.com/diegosouzapw/OmniRoute/pull/12628)) — thanks @RaviTharuma +- **fix(cursor):** a non-array `tool_calls` on an assistant message no longer crashes the cursor request translator with a `TypeError`; both loops now require an array ([#12689](https://github.com/diegosouzapw/OmniRoute/issues/12689)) ([#12691](https://github.com/diegosouzapw/OmniRoute/pull/12691)) — thanks @soroush5 +- **fix(chat):** Chat Completions no longer return empty `content` after a server-owned memory or skills tool; the first provider send and account/model recovery now share one pipeline so a follow-up round-trip can fill the reply ([#12696](https://github.com/diegosouzapw/OmniRoute/issues/12696)) — thanks @HouMinXi ([#12867](https://github.com/diegosouzapw/OmniRoute/pull/12867)) +- **fix(dashboard):** batch-deleting provider keys no longer toasts failure after a successful delete when the confirm button's click event is forwarded as `onAfter` ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) — thanks @HouMinXi +- **fix(glm):** drop the extra 16th argument to `createSSETransformStreamWithLogger` that TypeScript rejected (TS2554) and that never reached the TransformStream ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) ([#12770](https://github.com/diegosouzapw/OmniRoute/pull/12770)) — thanks @HouMinXi +- **fix(dashboard):** pass `nodeMap` into Runtime `QuotaGroup` so a quota monitor in error/exhausted/alerting no longer throws `ReferenceError: nodeMap is not defined`. ([#12868](https://github.com/diegosouzapw/OmniRoute/pull/12868)) — thanks @HouMinXi +- Restricted API keys whose `allowedModels` lists a combo name no longer skip every combo member at pre-dispatch (`ALL_TARGETS_SKIPPED`). Inner-target filtering still applies when the allow-list is a provider prefix or `disableNonPublicModels` is on ([#12899](https://github.com/diegosouzapw/OmniRoute/pull/12899)). — thanks @HouMinXi +- **fix(combos):** Effort-suffixed combo members inherit the base model's `model_context_overrides` row so priority order is not inverted on large requests ([#12926](https://github.com/diegosouzapw/OmniRoute/pull/12926)) — thanks @HouMinXi +- **fix(catalog):** Picker-added `customModels` enter the dispatch-time live catalog so combo members and bare inference no longer 400 ([#12934](https://github.com/diegosouzapw/OmniRoute/pull/12934)) — thanks @HouMinXi +- **fix(docker):** re-derive the Docker build's worker-pool memory budget from the MEASURED ~4.5 GB per-process RSS (the issue owner's own VPS dmesg OOM-killer reproduction) instead of the stale 2560 MB/worker inference #11419 shipped, and lower `OMNIROUTE_BUILD_WORKERS` 3 → 2 so 1 parent + 1 page-data worker (2 processes × 4.5 GB = 9 GB) fits the 12.288 GB (75%) budget on a 16 GB GitHub Actions runner — the previous default (1 parent + 2 workers = 13.5 GB) still overcommitted the runner and kept "Publish to Docker Hub" failing with `cannot allocate memory` after #11419 merged (#7518). ([#11663](https://github.com/diegosouzapw/OmniRoute/pull/11663)) +- **fix(electron):** the packaged Windows desktop build now passes the #7592 cold-restart smoke end to end. Five defects were found and fixed on the way: (1) optional-pack staging failed on any machine whose `tar` is GNU tar — it reads the drive letter in an absolute `-f C:\...` archive path as a remote rsh host (`Cannot connect to C:`), so staging now passes a bare filename with `cwd` at the tarball directory and surfaces tar stderr on failure; (2) the Electron `files` allowlist was missing `lib/loginHeaderCapture.js`, so the packaged main process crashed at startup with `Cannot find module './lib/loginHeaderCapture'`; (3) electron-builder ≥26 injects an `!**/node_modules/**` ignore into every extraResources pattern list that no positive filter can override, which silently dropped the staged runtime `node_modules` (including the better-sqlite3 N-API prebuild) from `resources/app` — a fresh v3.8.50 desktop build would have shipped with no native SQLite driver and reproduced the #7592 sql.js fallback on every machine; an `afterPack` hook now restores the staged `node_modules` after packing; (4) the packaged-app smoke harness redirected `USERPROFILE` into its temp DATA_DIR, but Electron resolves the Roaming profile from `%USERPROFILE%\AppData\Roaming\` (USERPROFILE takes precedence over the APPDATA env var) and the path service throws instead of creating it, so `requestSingleInstanceLock()` returned false and the app exited(0) silently before `app.whenReady()` — the harness now pre-creates the derived tree, and `ensureSmokeEnvDirs` is exported and unit-tested; (5) the #7592 driver assertion parsed a `[DB] Driver: ...` line that the server's primary DB path never printed (only the unused `openDatabaseAsync()` did) — `getDbInstance()` now emits the same line on open so the guard can actually assert the native driver. Also: the smoke env-allowlist unit test hardcoded POSIX paths and could only pass on Linux/macOS; expectations are now host-agnostic, with new regression tests for the USERPROFILE-derived tree and `tarPack` under absolute Windows-style paths ([#11443](https://github.com/diegosouzapw/OmniRoute/pull/11443)) — thanks @jonlwheat2-gif +- fix(sse): stop the auto-combo candidates inspector from silently dropping model-locked/cooled-down rows (#9133) ([#11994](https://github.com/diegosouzapw/OmniRoute/pull/11994)) — thanks @hartmark +- **fix(providers):** Perplexity Web no longer turns upstream stream failures into successful assistant text; pre-content failures remain eligible for fallback, partial output ends with a structured sanitized error, and failed sessions are not persisted ([#12465](https://github.com/diegosouzapw/OmniRoute/pull/12465)) +- **fix(video-bridge):** preserve chronological video and transcript fusion, source-frame timestamps, transcript provenance, full contact-sheet intervals, and pre-abort callback isolation ([#11681](https://github.com/diegosouzapw/OmniRoute/pull/11681)) +- **Z.ai Web:** HTTP 200 streams carrying an upstream error now terminate with a structured failure instead of assistant text plus a normal stop, preserving partial output while allowing pre-content combo fallback. ([#12454](https://github.com/diegosouzapw/OmniRoute/pull/12454)) +- Absorb `Error [AbortError]: request_signal_aborted` and DOMException AbortError shapes in the process-level client-abort crash guard so routine client disconnects no longer kill the server (exit code 7). ([#12165](https://github.com/diegosouzapw/OmniRoute/pull/12165)) — thanks @alvinveroy +- **fix(sse):** Treat Adapta Web `type:error` SSE events as sanitized non-stream failures instead of empty HTTP 200 completions. ([#12459](https://github.com/diegosouzapw/OmniRoute/pull/12459)) +- **fix(autoCombo):** layer 3 (`models_dev_tier`) no longer scores retired models as premium, merge cross-provider capability rows deterministically, and inherit tier scores through variant ids ([#11508](https://github.com/diegosouzapw/OmniRoute/issues/11508)). `getModelsDevTierFitness` now vetoes ids present in `config/quality/model-lifecycle.json` with status `retired` before any other signal; `loadModelCapabilities()` aggregates per `model_id` (any-non-null-true for booleans, max for `limit_context`) instead of last-write-wins over SQLite's undefined row order; a capabilities miss retries through `resolveScoresAs()`'s base id and reports `models_dev_tier:inherited`, matching layers 1–2 (#11492). Adds `getModelsDevTierFitnessWithSource()` and an `invalidateCapabilitiesCache()` test/ops hook. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) — thanks @Meet6338-X +- fix(sse): accept Claude `content:[]` + `stop_reason: max_tokens`/`tool_use` in `detectMalformedNonStream` (match `isEmptyContentResponse`) so Claude Code `/model` probes with `max_tokens: 1` no longer become a false 502 ([#11568](https://github.com/diegosouzapw/OmniRoute/pull/11568)) — thanks @Seramicx +- **fix(security):** sanitize `request.failed` diagnostics before publishing them to live dashboard listeners and replay history, while keeping status, model, provider, latency, and internal call-log diagnostics intact. ([#12469](https://github.com/diegosouzapw/OmniRoute/pull/12469)) +- **fix(pwa):** do not intercept dashboard navigations so Chrome can fall back from a stale HTTP/3 Alt-Svc advertisement (UDP :20128 is unpublished; F5 on a long-lived tab hung until a new tab opened a fresh TCP connection). ([#12767](https://github.com/diegosouzapw/OmniRoute/pull/12767)) — thanks @HouMinXi +- **fix(executors):** handle DuckDuckGo ERR_BN_LIMIT (418) without retrying — when the upstream returns `418 ERR_BN_LIMIT` (rate-limit/ban), the executor now returns the error immediately instead of burning another VQD acquisition that would only count against the IP limit. The retry logic for `418 ERR_CHALLENGE` (unsolved challenge) remains unchanged. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) ([#12110](https://github.com/diegosouzapw/OmniRoute/pull/12110)) — thanks @oyi77 / @Meet6338-X +- Fixed DuckDuckGo streaming responses losing JSON lines and UTF-8 characters split across network chunks. ([#11528](https://github.com/diegosouzapw/OmniRoute/pull/11528)) — thanks @pacocartones +- Harden generated Cloudflare, Vercel, and Deno relays by stripping hop-by-hop, framing, and proxy authentication headers before forwarding requests upstream. ([#11533](https://github.com/diegosouzapw/OmniRoute/pull/11533)) — thanks @pacocartones +- **feat(grok-cli):** Provider Limits shows grok-cli banked reset credits from `GetRemainingResets` (including a real zero; a failed RPC omits the row) and the existing View credits button now calls `ConsumerUiSvc/RedeemReset` for grok-cli. Live tokens use nested fields 10/20/30 (id + Timestamp), not compact 1/2/3. ([#12805](https://github.com/diegosouzapw/OmniRoute/pull/12805)) — thanks @HouMinXi +- **fix(memory):** Embedding Model Quick select, Embedding Source remote dropdown, and Rerank selector now list every configured provider with embedding/rerank support instead of only chat-catalog text matches plus OpenRouter live discovery; a generic OpenAI-compatible `/embeddings` + Cohere-compatible `/rerank` runtime fallback resolves any configured chat provider's embedding/rerank endpoint, so unlisted providers no longer fail with "Unknown embedding provider"; both memory selectors gained a free-text model override ([#11390](https://github.com/diegosouzapw/OmniRoute/pull/11390)) — thanks @rqzbeh +- Harden SQLite upgrades around the historical migration-074 version collision: missing discovery and inspector tables are replayed atomically, pre-existing databases (including setup-created skeletons) receive reusable content-addressed safety snapshots, and Node test/eval probes without `DATA_DIR` are isolated from the operator database. ([#12435](https://github.com/diegosouzapw/OmniRoute/pull/12435)) +- **fix(dashboard):** Moonshot/Kimi Open Platform voucher and cash leftover percentages follow the bucket balance, so an empty wallet no longer paints those rows as 100% while Available is 0% ([#12733](https://github.com/diegosouzapw/OmniRoute/pull/12733)) — thanks @HouMinXi +- **fix(combo):** return non-retryable HTTP 400 when all candidates for a pinned native Codex turn are unavailable due to model-scoped lockout, terminating the turn cleanly while preserving turn continuity and enabling standard Combo routing on subsequent turns ([#12240](https://github.com/diegosouzapw/OmniRoute/pull/12240)) — thanks @mdigitalbh81 +- Fixed auto-router strategies preserving the selected connection through ranking and dispatch when multiple connections share the same provider and model. ([#11530](https://github.com/diegosouzapw/OmniRoute/pull/11530)) — thanks @pacocartones +- Preserve Chat Completions JSON-object response formats when translating requests to the Responses API. ([#11529](https://github.com/diegosouzapw/OmniRoute/pull/11529)) — thanks @pacocartones +- Fix idempotency fingerprints so Responses API requests include `input` and other semantic generation fields using stable serialization, while excluding credentials and request metadata. ([#11532](https://github.com/diegosouzapw/OmniRoute/pull/11532)) — thanks @pacocartones +- **fix(api):** Generated API CLI commands now enforce required OpenAPI request bodies; Combo test commands forward the required `comboName` body, while API keys created by older writers after migration 149 preserve legacy allow-all Combo access without widening explicit empty allowlists — thanks @marcelokarval ([#12070](https://github.com/diegosouzapw/OmniRoute/pull/12070)) +- **fix(grok-web):** treat upstream streaming failures as failures instead of successful assistant text: error-only streams now fail readiness with HTTP 502, while failures after legitimate content preserve that partial output and terminate through the sanitized stream failure path without a normal `stop` completion. ([#12458](https://github.com/diegosouzapw/OmniRoute/pull/12458)) +- HuggingChat now turns HTTP 200 JSONL generation failures into a sanitized 502 before content, or a fixed public stream failure after partial output, so fallback and request persistence no longer record a false successful stop. ([#12456](https://github.com/diegosouzapw/OmniRoute/pull/12456)) +- **fix(providers):** keep 1min.ai HTTP 200 stream errors out of assistant content, preserve partial output, and expose sanitized terminal errors so pre-content failures can fall back. ([#12466](https://github.com/diegosouzapw/OmniRoute/pull/12466)) +- **fix(sessions):** preserve legacy Sessions data when the additive exclusive-session projection is unavailable, returning an empty projection and warning only once per contiguous outage instead of failing the endpoint; the Sessions badge intentionally reflects the merged legacy and exclusive row count introduced by [#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389) ([#11469](https://github.com/diegosouzapw/OmniRoute/pull/11469)) — thanks @KaspaPulse +- Render Nimble Search with the generic provider icon and serve Opper's proven logo locally. ([#12415](https://github.com/diegosouzapw/OmniRoute/pull/12415)) +- **fix(quality):** `config/quality/provider-assets-provenance.jsonl` was stale against the physical `public/providers/` tree (still listing 225 assets and 5 duplicate-content groups after several provider-retirement PRs removed 85 icon files), which would have made `check:provider-asset-provenance` fail on the next full run. Reconciled the manifest to the real 142-file snapshot (142 asset records, 1 duplicate-content group, 2 newly-registered files marked `unresolved` pending provenance research), repinned `auditedCommit`, and updated the regression test's hardcoded counts to match. ([#11876](https://github.com/diegosouzapw/OmniRoute/pull/11876)) +- fix(ui): wrap quota card footer action buttons to prevent clipping Refresh button on narrow cards — when extra action buttons such as `View credits` are present on Codex cards, allow the footer row to flex-wrap with `shrink-0` buttons so `Refresh now` remains visible and clickable across all responsive card widths ([#11464](https://github.com/diegosouzapw/OmniRoute/pull/11464)) — thanks @adevwithpurpose +- Keep Antigravity Gemini usable when the same connection's Claude weekly quota is empty; generic quota cache stays per-connection for every other provider. ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) — thanks @HouMinXi +- Fixed Tailscale tunnel setup to reject ports outside the valid 1-65535 range and safely install Windows MSI packages from paths containing apostrophes. ([#11534](https://github.com/diegosouzapw/OmniRoute/pull/11534)) — thanks @pacocartones +- Classify Cloudflared and Tailscale process-management routes as local-only and spawn-capable while preserving remote access to read-only tunnel status endpoints. ([#11531](https://github.com/diegosouzapw/OmniRoute/pull/11531)) — thanks @pacocartones +- Electron release workflow: the `publish-npm` job now grants `actions: read` to the reusable `npm-publish.yml` it calls (its `publish` job requests it), which is what made GitHub refuse the whole v3.8.50 run at startup and ship the release with zero desktop assets; a `workflow_dispatch` now builds the requested tag instead of the dispatching branch and can skip the npm leg (`publish_npm=false`) when only re-attaching assets ([#11974](https://github.com/diegosouzapw/OmniRoute/pull/11974)) +- **fix(dashboard):** The Combos page usage guide now reads its dismissal through `useSyncExternalStore` instead of correcting SSR state inside an effect, removing an extra commit of the page tree on every load (and the `react-hooks/set-state-in-effect` error it raised). ([#12671](https://github.com/diegosouzapw/OmniRoute/pull/12671)) +- Electron release: `electron/package-lock.json` regained the optional `electron-builder-squirrel-windows` subtree (13 entries) that `npm ci` had been refusing as out of sync, `electron-release.yml` gained a `build_ref` dispatch input and stops regenerating release notes on a re-attach dispatch, and the npm publish workflow attaches the SBOM to the GitHub Release on dispatch publishes too — so the v3.8.51 tag ships every desktop asset and the SBOM like v3.8.49 did ([#12022](https://github.com/diegosouzapw/OmniRoute/pull/12022)) +- Drained the reds every PR against `release/v3.8.51` was born with: documented the three service-worker build-id variables, registered the six retirement/tunnel tests with the mutation gate, approved `eslint-plugin-react-hooks` in the dependency allowlist, added the six `combo.sort.*` strings to `vi` and `pt-BR`, pointed the ChatGPT Web doc at the real migration-168 test, worded the g4f hint around the member key, and realigned four tests to the retired-provider catalog and the legacy-schema fixtures the retirement migrations touch. ([#11940](https://github.com/diegosouzapw/OmniRoute/pull/11940)) +- **fix(providers):** Claude, Grok, LMArena, Notion, and Perplexity web-cookie transports now use pooled `wreq-js` 3.2 instead of the native sidecar, with all nine supported bindings pinned and audited, and the applicable platform binding plus native-license evidence included in each release artifact ([#12429](https://github.com/diegosouzapw/OmniRoute/pull/12429), supersedes [#11753](https://github.com/diegosouzapw/OmniRoute/pull/11753)). +- **fix(providers):** Zed Hosted streaming failures now trigger fallback before content and end partial streams with a sanitized structured error instead of fake assistant text and a normal-success stop. ([#12455](https://github.com/diegosouzapw/OmniRoute/pull/12455)) +- **fix(test):** capture hook result after render (direct commit `b85d0abb0b`) +- **fix(sync):** repair the two regressions the v3.8.50 sync-back left on release/v3.8.51 (direct commit `5ade9e0851`) +- **revert(models):** drop the memo reset hook ported from #10055 (direct commit `282de841ae`) +- **fix(typecheck):** guard the optional connectionId before the reactive model sync (direct commit `f2825bad75`) +- **fix(quota-share):** release the winner's reserved in-flight slot (#11371) ([#11408](https://github.com/diegosouzapw/OmniRoute/pull/11408)) — thanks @oyi77 +- **fix(providers):** stop silently dropping reasoning effort on opencode families (#10788) ([#11409](https://github.com/diegosouzapw/OmniRoute/pull/11409)) — thanks @oyi77 +- **fix(combo):** enable genuine quota-aware routing for generic providers (antigravity, claude, etc.) ([#11411](https://github.com/diegosouzapw/OmniRoute/pull/11411)) — thanks @HouMinXi +- **fix(oauth):** allow expired token retry before deactivation and activate expired retry loop (#P0) ([#11414](https://github.com/diegosouzapw/OmniRoute/pull/11414)) — thanks @HouMinXi +- **perf(executors):** lazy-load the executor registry — defer class imports + construction to first use (#11220) ([#11421](https://github.com/diegosouzapw/OmniRoute/pull/11421)) — thanks @oyi77 +- **fix:** make release/v3.8.50 CI gates green (run 32786966560 root causes) ([#11450](https://github.com/diegosouzapw/OmniRoute/pull/11450)) — thanks @jonlwheat2-gif +- **fix(sse):** exact-domain cookie match and origin-equality URL assertions ([#11452](https://github.com/diegosouzapw/OmniRoute/pull/11452)) — thanks @hartmark +- **fix(providers):** send Responses API payload during OpenAI-compatible validation ([#11454](https://github.com/diegosouzapw/OmniRoute/pull/11454)) — thanks @tuandinh0801 +- **fix(command-code):** fallback to /alpha/generate for Go plan without Provider API access ([#11455](https://github.com/diegosouzapw/OmniRoute/pull/11455)) — thanks @Chewji9875 +- **fix(responses-continuation):** fail closed on a log-truncated stored input/output array ([#11473](https://github.com/diegosouzapw/OmniRoute/pull/11473)) — thanks @hartmark +- **fix(cli):** write jcode settings as [providers.omniroute] in config.toml ([#11484](https://github.com/diegosouzapw/OmniRoute/pull/11484)) — thanks @hongnoul +- **fix(logging):** raise chat-log array truncation cap to 1000 and unify duplicate implementations ([#11499](https://github.com/diegosouzapw/OmniRoute/pull/11499)) — thanks @hartmark +- **fix:** restore live websocket URL helpers ([#11509](https://github.com/diegosouzapw/OmniRoute/pull/11509)) — thanks @PauloFH +- **perf(core):** eliminate async waterfalls across home, cache, and db operations (#11396) ([#11512](https://github.com/diegosouzapw/OmniRoute/pull/11512)) — thanks @HouMinXi +- **fix(kiro):** add runtime.us-east-1.kiro.dev as first-attempt endpoint ([#11517](https://github.com/diegosouzapw/OmniRoute/pull/11517)) — thanks @rifqiawl +- **fix(providers):** remove duplicate 128-tool truncation in OpencodeExecutor (#11444) ([#11518](https://github.com/diegosouzapw/OmniRoute/pull/11518)) — thanks @HouMinXi +- **fix(providers):** poll onboardUser LRO before classifying BYOP ([#11519](https://github.com/diegosouzapw/OmniRoute/pull/11519)) — thanks @rifqiawl +- **fix(translator):** preserve exact tool name casing for Claude tool_use payloads (#11487) ([#11520](https://github.com/diegosouzapw/OmniRoute/pull/11520)) — thanks @HouMinXi +- **fix(kimi-web):** align health probe with executor domain ([#11521](https://github.com/diegosouzapw/OmniRoute/pull/11521)) — thanks @pacocartones +- **fix(cli):** preserve existing admin password during setup ([#11522](https://github.com/diegosouzapw/OmniRoute/pull/11522)) — thanks @pacocartones +- **fix(mitm):** keep Windows DNS elevation off the POSIX sudo path (#11430) ([#11542](https://github.com/diegosouzapw/OmniRoute/pull/11542)) — thanks @official-burak +- **fix(sse):** scale chat admission by ingest byte budget instead of a fixed request count ([#11548](https://github.com/diegosouzapw/OmniRoute/pull/11548)) — thanks @TheDemonTuan +- **fix:** accept Gemini Web cookie exports ([#11554](https://github.com/diegosouzapw/OmniRoute/pull/11554)) — thanks @rafacpti23 +- **fix(i18n):** complete Brazilian Portuguese translations ([#11558](https://github.com/diegosouzapw/OmniRoute/pull/11558)) — thanks @rafacpti23 +- **fix(sse):** explicit types for openai-responses pureHelpers — clears last failing core typecheck gate ([#11567](https://github.com/diegosouzapw/OmniRoute/pull/11567)) — thanks @jonlwheat2-gif +- **fix(opencode):** hydrate Proxy Pool references for no-auth connections ([#11584](https://github.com/diegosouzapw/OmniRoute/pull/11584)) — thanks @AStupidBear +- **fix(ci):** resolve inherited release/v3.8.51 base-red CI failures ([#11608](https://github.com/diegosouzapw/OmniRoute/pull/11608)) — thanks @jonlwheat2-gif +- **fix(db):** fallback load STORAGE_ENCRYPTION_KEY from env files ([#11614](https://github.com/diegosouzapw/OmniRoute/pull/11614)) — thanks @benzntech +- **fix(providers):** mark Brave Search as no longer having a free tier ([#11621](https://github.com/diegosouzapw/OmniRoute/pull/11621)) — thanks @turbolego +- **fix:** pass tool result content through as string in OpenAI/Claude -> Gemini translation ([#11624](https://github.com/diegosouzapw/OmniRoute/pull/11624)) — thanks @Hsia97 +- **fix(sse):** scope credential health to exact key ([#11628](https://github.com/diegosouzapw/OmniRoute/pull/11628)) — thanks @KaspaPulse +- **fix(install):** guard prepare script against missing husky ([#11634](https://github.com/diegosouzapw/OmniRoute/pull/11634)) — thanks @Prajeeth-12 +- **fix(security):** redact NVIDIA_API_KEY at the log sink in the ad-hoc diag script ([#11639](https://github.com/diegosouzapw/OmniRoute/pull/11639)) +- **fix(types):** clear the two type regressions red on release/v3.8.51 ([#11640](https://github.com/diegosouzapw/OmniRoute/pull/11640)) — thanks @jonlwheat2-gif +- **fix(build):** resolve workspace: protocol dependencies to concrete semver versions during prepublish (#11540) ([#11641](https://github.com/diegosouzapw/OmniRoute/pull/11641)) — thanks @HouMinXi +- **fix(search):** properly resolve configured search connection in /v1/responses pipeline (#11524) ([#11642](https://github.com/diegosouzapw/OmniRoute/pull/11642)) — thanks @HouMinXi +- **fix(authz):** trust cf-connecting-ip for via-proxy detection when peer is Cloudflare edge (#11514) ([#11643](https://github.com/diegosouzapw/OmniRoute/pull/11643)) — thanks @HouMinXi +- **fix(security):** compare the OIDC state cookie in constant time ([#11646](https://github.com/diegosouzapw/OmniRoute/pull/11646)) +- **fix(security):** namespace the dedup hash by the calling API key ([#11649](https://github.com/diegosouzapw/OmniRoute/pull/11649)) +- **fix(models):** gate the Codex-native catalog loop on prefix mode (#11632) ([#11662](https://github.com/diegosouzapw/OmniRoute/pull/11662)) — thanks @xiaoyaner0201 +- **fix(dashboard):** project Codex routing quota in account pools ([#11666](https://github.com/diegosouzapw/OmniRoute/pull/11666)) — thanks @KaspaPulse +- **fix(ddgw):** normaliza system no upstream da release v3.8.51 ([#11692](https://github.com/diegosouzapw/OmniRoute/pull/11692)) — thanks @RhianB14 +- **fix(memory):** honor category filter in GET /api/memory ([#11699](https://github.com/diegosouzapw/OmniRoute/pull/11699)) — thanks @Sabeekhann +- **fix(ci):** detect stale workspace lockfile entries ([#11714](https://github.com/diegosouzapw/OmniRoute/pull/11714)) — thanks @pacocartones +- **fix(ci):** stamp dist/BUILD_SHA before the npm publish provenance gate ([#11721](https://github.com/diegosouzapw/OmniRoute/pull/11721)) +- **fix(docker):** decouple the best-effort Bun image from the release manifest ([#11724](https://github.com/diegosouzapw/OmniRoute/pull/11724)) +- **perf(compression):** avoid cloning memo misses twice ([#11727](https://github.com/diegosouzapw/OmniRoute/pull/11727)) — thanks @RaviTharuma +- **fix(docker):** let the bun digest artifact be absent, not fatal ([#11740](https://github.com/diegosouzapw/OmniRoute/pull/11740)) +- **fix(shared):** block cloud-metadata hosts under default remote-image guard ([#11755](https://github.com/diegosouzapw/OmniRoute/pull/11755)) — thanks @santosraju99-hub +- **fix(api):** preserve caller-provided X-Correlation-Id on chat completions ([#11760](https://github.com/diegosouzapw/OmniRoute/pull/11760)) — thanks @aniruddhaadak80 +- **fix(resilience):** lock permanently retired models instead of short backoff (Gemini ban prevention) ([#11762](https://github.com/diegosouzapw/OmniRoute/pull/11762)) — thanks @turbolego +- **fix:** mark Vercel AI Gateway as passthroughModels ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **fix(resilience):** stop hammering permanently-moved endpoints and billing-suspended accounts ([#11774](https://github.com/diegosouzapw/OmniRoute/pull/11774)) — thanks @turbolego +- **fix(lease):** remove global static reservation and gate routing on live active lease occupancy ([#11775](https://github.com/diegosouzapw/OmniRoute/pull/11775)) — thanks @TheDemonTuan +- **fix(ci):** size the install-upgrade gate to a measured run, and log the pack cost ([#11776](https://github.com/diegosouzapw/OmniRoute/pull/11776)) +- **fix(pwa):** stop serving the stale shell after deploys ([#11779](https://github.com/diegosouzapw/OmniRoute/pull/11779)) — thanks @HouMinXi +- **fix(combo):** lock GitHub models rejected as "not supported" for future requests ([#11781](https://github.com/diegosouzapw/OmniRoute/pull/11781)) — thanks @turbolego +- **fix(cli):** probe both IPv4 and IPv6 loopback for server readiness (#11766) ([#11794](https://github.com/diegosouzapw/OmniRoute/pull/11794)) — thanks @vermasomesh835 +- **fix(sse):** preserve CLIProxyAPI wrappers per connection ([#11797](https://github.com/diegosouzapw/OmniRoute/pull/11797)) — thanks @RaviTharuma +- **fix(providers):** honor model auto-fetch opt-in on create ([#11798](https://github.com/diegosouzapw/OmniRoute/pull/11798)) — thanks @RaviTharuma +- **fix(dashboard):** make model auto-fetch order independent ([#11805](https://github.com/diegosouzapw/OmniRoute/pull/11805)) — thanks @RaviTharuma +- **fix(docker):** make GHA cache exports best effort ([#11806](https://github.com/diegosouzapw/OmniRoute/pull/11806)) — thanks @RaviTharuma +- **fix(kiro):** do not permanently ban on 'User is not authorized to make this call' ([#11809](https://github.com/diegosouzapw/OmniRoute/pull/11809)) — thanks @Deftera186 +- **fix(sse):** keep cache-write tokens in OpenAI-shaped usage ([#11814](https://github.com/diegosouzapw/OmniRoute/pull/11814)) — thanks @TheDemonTuan +- **fix(docs):** resolve relative markdown and wiki links across Fumadocs and GitHub wiki ([#11834](https://github.com/diegosouzapw/OmniRoute/pull/11834)) — thanks @AndrianBalanescu +- **fix(chat):** preserve unstripped model string for passthrough provider routing ([#11840](https://github.com/diegosouzapw/OmniRoute/pull/11840)) — thanks @raheemuddin786 +- **fix(mcp):** avoid async initialization inside sync esm wrappers (#11569) ([#11849](https://github.com/diegosouzapw/OmniRoute/pull/11849)) — thanks @HouMinXi +- **fix(sse):** preserve coherent markdown token boundaries across stream translation chunks (#11606) ([#11851](https://github.com/diegosouzapw/OmniRoute/pull/11851)) — thanks @HouMinXi +- **fix(ci):** run the install-upgrade gate on disk, not on the /tmp tmpfs ([#11855](https://github.com/diegosouzapw/OmniRoute/pull/11855)) +- **fix(skills):** expand shorthand property types in injected tool schemas ([#11857](https://github.com/diegosouzapw/OmniRoute/pull/11857)) — thanks @alvinveroy +- **fix(ci):** publish npm from a hosted runner so provenance is accepted ([#11877](https://github.com/diegosouzapw/OmniRoute/pull/11877)) +- **fix(lint):** pin react hooks plugin to 7.0.1 ([#11886](https://github.com/diegosouzapw/OmniRoute/pull/11886)) +- **fix(ci):** surface and clear release-green ESLint failure ([#11890](https://github.com/diegosouzapw/OmniRoute/pull/11890)) — thanks @MumuTW +- **fix(guardrails):** prevent duplicate prompt-injection-guard log output ([#11936](https://github.com/diegosouzapw/OmniRoute/pull/11936)) — thanks @patrykkopycinski +- **fix(auth):** downgrade expected transient states from warn to debug ([#11937](https://github.com/diegosouzapw/OmniRoute/pull/11937)) — thanks @patrykkopycinski +- **fix(codex):** fail over image generation for imported free plans ([#11948](https://github.com/diegosouzapw/OmniRoute/pull/11948)) — thanks @Bl0ck154 +- **fix(codex):** normalize non-stream responses ([#11951](https://github.com/diegosouzapw/OmniRoute/pull/11951)) — thanks @Bl0ck154 +- **fix(images):** forward Antigravity image size ([#11952](https://github.com/diegosouzapw/OmniRoute/pull/11952)) — thanks @Bl0ck154 +- **fix(db):** invalidate connection cache after upsert ([#11953](https://github.com/diegosouzapw/OmniRoute/pull/11953)) — thanks @Bl0ck154 +- **fix(codex):** restore imported account state ([#11954](https://github.com/diegosouzapw/OmniRoute/pull/11954)) — thanks @Bl0ck154 +- **fix(sse):** give extended-thinking targets the reasoning readiness budget ([#11959](https://github.com/diegosouzapw/OmniRoute/pull/11959)) — thanks @abhisheksharma2411 +- **fix(dashboard):** use opaque background and readable text color on cost chart tooltips ([#11960](https://github.com/diegosouzapw/OmniRoute/pull/11960)) — thanks @ZaimMarzuki +- **fix(antigravity):** send complete loadCodeAssist metadata (ideType/platform/pluginType as numeric enums) ([#11969](https://github.com/diegosouzapw/OmniRoute/pull/11969)) — thanks @rifqiawl +- **fix(core):** resolve DB init race condition and reasoning translation ([#12003](https://github.com/diegosouzapw/OmniRoute/pull/12003)) — thanks @raheemuddin786 +- **fix(admission):** exclude reclaimable page cache from the cgroup pressure ratio ([#12017](https://github.com/diegosouzapw/OmniRoute/pull/12017)) — thanks @HouMinXi +- **fix(build):** prune non-production trees in NFT trace excludes and tsconfig ([#12028](https://github.com/diegosouzapw/OmniRoute/pull/12028)) — thanks @Chewji9875 +- **fix(migrations):** add renamed migration compatibility for 056/073/077/101 ([#12036](https://github.com/diegosouzapw/OmniRoute/pull/12036)) — thanks @oyi77 +- **fix(sse):** default crash-guard logger to console.warn, not console ([#12042](https://github.com/diegosouzapw/OmniRoute/pull/12042)) — thanks @HouMinXi +- **fix(catalog):** derive vision/modalities for built-in auto combos from effective target pool ([#12046](https://github.com/diegosouzapw/OmniRoute/pull/12046)) — thanks @Prajeeth-12 +- **fix(release):** the packaged-app smoke verifies the database opened, not a driver line the primary path never prints (twin of #12032) ([#12047](https://github.com/diegosouzapw/OmniRoute/pull/12047)) +- **fix(barrel):** migrate src/app/ off the @/lib/localDb barrel import (#11795 Phase 2) ([#12051](https://github.com/diegosouzapw/OmniRoute/pull/12051)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate src/lib/ off the localDb barrel to direct db imports (#59) ([#12052](https://github.com/diegosouzapw/OmniRoute/pull/12052)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate open-sse, src/shared, src/sse, src/models, src/domain off the @/lib/localDb barrel import (#11795 Phase 4) ([#12053](https://github.com/diegosouzapw/OmniRoute/pull/12053)) — thanks @jonlwheat2-gif +- **fix(barrel):** delete the @/lib/localDb barrel — every consumer migrated (#11795 Phase 5) ([#12055](https://github.com/diegosouzapw/OmniRoute/pull/12055)) — thanks @jonlwheat2-gif +- **fix(radar):** restore D12 public boundary ([#12057](https://github.com/diegosouzapw/OmniRoute/pull/12057)) +- **fix(dev):** bound webpack and Tailwind scans ([#12075](https://github.com/diegosouzapw/OmniRoute/pull/12075)) — thanks @backryun +- **fix(dev):** isolate root layout settings reads ([#12076](https://github.com/diegosouzapw/OmniRoute/pull/12076)) — thanks @backryun +- **fix(dev):** reduce instrumentation executor fan-out (phase 3) ([#12078](https://github.com/diegosouzapw/OmniRoute/pull/12078)) — thanks @backryun +- **fix(dev):** make logging resources HMR-singleton ([#12079](https://github.com/diegosouzapw/OmniRoute/pull/12079)) — thanks @backryun +- **fix(sse):** keep unavailable forced connections scoped ([#12080](https://github.com/diegosouzapw/OmniRoute/pull/12080)) — thanks @keeltrace +- **fix(dev):** isolate batch dispatch from instrumentation ([#12081](https://github.com/diegosouzapw/OmniRoute/pull/12081)) — thanks @backryun +- **fix(ci):** reconcile release test contract drift ([#12082](https://github.com/diegosouzapw/OmniRoute/pull/12082)) — thanks @backryun +- **fix(ci):** accept CVE-2025-68121 in the prebuilt tls-client .so, auto-close base-red issues, guard Scorecard on the default branch ([#12085](https://github.com/diegosouzapw/OmniRoute/pull/12085)) +- **fix(db):** drop three consumer-less 1proxy exports — dead-code base-red on release/v3.8.51 after the barrel deletion (#12055) ([#12087](https://github.com/diegosouzapw/OmniRoute/pull/12087)) +- **fix(api):** clear the six API-route TypeScript regressions the new gate landed red on ([#12094](https://github.com/diegosouzapw/OmniRoute/pull/12094)) +- **fix(release):** never let the tag-push Create Release append auto notes to the curated body ([#12096](https://github.com/diegosouzapw/OmniRoute/pull/12096)) +- **fix(sse):** honor CLIProxyAPI environment API key ([#12099](https://github.com/diegosouzapw/OmniRoute/pull/12099)) — thanks @RaviTharuma +- **fix(oauth):** bind Google refresh to the client that issued the token ([#12106](https://github.com/diegosouzapw/OmniRoute/pull/12106)) — thanks @HouMinXi +- **fix(dashboard):** make RequestLoggerDetail loadable outside Next — CSS via globals.css + CJS/ESM interop (#11703 base-reds) ([#12114](https://github.com/diegosouzapw/OmniRoute/pull/12114)) +- **fix(leases):** project status lease row to lease columns so joined connection PII never escapes ([#12115](https://github.com/diegosouzapw/OmniRoute/pull/12115)) — thanks @geek007git +- **fix(plugins):** do not kill the plugin process when a fire-and-forget hook times out ([#12116](https://github.com/diegosouzapw/OmniRoute/pull/12116)) — thanks @geek007git +- **fix(guardrails):** restore injection-guard logging on middleware-only routes ([#12117](https://github.com/diegosouzapw/OmniRoute/pull/12117)) — thanks @geek007git +- **fix(plugins):** refresh stored manifest from disk on activate so new hook fields reach existing installs ([#12120](https://github.com/diegosouzapw/OmniRoute/pull/12120)) — thanks @geek007git +- **fix(codex):** preserve existing provider state when bulk-import upserts a matching connection ([#12122](https://github.com/diegosouzapw/OmniRoute/pull/12122)) — thanks @geek007git +- **fix(ci):** clear the two base-reds the 2026-08-30 merge batch left on release/v3.8.51 (round 3) ([#12123](https://github.com/diegosouzapw/OmniRoute/pull/12123)) +- **fix(sse):** strip type:'custom' from Claude tools on agentrouter dispatch ([#12126](https://github.com/diegosouzapw/OmniRoute/pull/12126)) — thanks @wahidsadik371-coder +- **fix(sse):** stop advertising video providers the dispatcher cannot run ([#12131](https://github.com/diegosouzapw/OmniRoute/pull/12131)) — thanks @quiterunner-commits +- **fix(search):** treat HTTP 432 and plan limit errors as transient cooldown ([#12139](https://github.com/diegosouzapw/OmniRoute/pull/12139)) — thanks @HouMinXi +- **fix(sse):** refuse an AI Horde queue that cannot fit the request budget ([#12143](https://github.com/diegosouzapw/OmniRoute/pull/12143)) — thanks @quiterunner-commits +- **fix(ci):** clear the base-reds the afternoon merge batch left on release/v3.8.51 (round 5: provider count 352, TS2554/TS2677) ([#12144](https://github.com/diegosouzapw/OmniRoute/pull/12144)) +- **fix(translator):** strip neutral tool_choice when tools absent in Responses-to-Chat (#12141) ([#12166](https://github.com/diegosouzapw/OmniRoute/pull/12166)) — thanks @HouMinXi +- **fix(guardrails):** pass providerId to getResolvedModelCapabilities in checkComboVision (#12112) ([#12169](https://github.com/diegosouzapw/OmniRoute/pull/12169)) — thanks @HouMinXi +- **fix(translator):** strip plaintext reasoning content for opaque responses backends (#12128) ([#12171](https://github.com/diegosouzapw/OmniRoute/pull/12171)) — thanks @HouMinXi +- **perf(stream):** compile hot-path regexes once, bound token caches, fix quadratic buffering ([#12179](https://github.com/diegosouzapw/OmniRoute/pull/12179)) — thanks @opensource-elearning +- **fix:** resolve compression worker file using runtime anchors instead of… ([#12183](https://github.com/diegosouzapw/OmniRoute/pull/12183)) — thanks @NightStalker-87 +- **fix:** prevent Claude Code session kills via liveness-aware readiness + auto model echo ([#12189](https://github.com/diegosouzapw/OmniRoute/pull/12189)) — thanks @opensource-elearning +- **fix(translator):** preserve falsy primitive values in Gemini and Antigravity function response results ([#12191](https://github.com/diegosouzapw/OmniRoute/pull/12191)) — thanks @Siva010 +- **fix(oauth):** mark empty Antigravity projectId as degraded and clear stale errors (#11284) ([#12205](https://github.com/diegosouzapw/OmniRoute/pull/12205)) — thanks @HouMinXi +- **fix(providers):** gate the Codex auto-ping usage read on the shared quota throttle ([#12209](https://github.com/diegosouzapw/OmniRoute/pull/12209)) — thanks @abhisheksharma2411 +- **fix(combos):** prioritize SQLite row id over inner JSON id and notify delete errors ([#12213](https://github.com/diegosouzapw/OmniRoute/pull/12213)) — thanks @HouMinXi +- **fix(providers):** mark featherless.ai as no longer having a free tier (`hasFree: false`) ([#12216](https://github.com/diegosouzapw/OmniRoute/pull/12216)) — thanks @turbolego +- **fix(deepseek-web):** stop Turbopack dev panic in the PoW worker path resolver ([#12221](https://github.com/diegosouzapw/OmniRoute/pull/12221)) — thanks @hartmark +- **fix(chat-admission):** clarify local 503 source ([#12223](https://github.com/diegosouzapw/OmniRoute/pull/12223)) — thanks @keeltrace +- **fix(dev):** silence webpack runtime module warnings ([#12228](https://github.com/diegosouzapw/OmniRoute/pull/12228)) — thanks @backryun +- **fix(db):** use module.require for CommonJS runtime driver loading ([#12230](https://github.com/diegosouzapw/OmniRoute/pull/12230)) — thanks @raheemuddin786 +- **fix(memory):** honest probe-driven FTS5 keyword status + memory_id rowid sync ([#12231](https://github.com/diegosouzapw/OmniRoute/pull/12231)) — thanks @raheemuddin786 +- **fix(system):** propagate abort signal to stream reader in HTTP version checks ([#12232](https://github.com/diegosouzapw/OmniRoute/pull/12232)) — thanks @raheemuddin786 +- **fix(resilience):** isolate local host execution errors from provider circuit breakers ([#12233](https://github.com/diegosouzapw/OmniRoute/pull/12233)) — thanks @raheemuddin786 +- **fix(sse):** sort injected tools deterministically for prompt caching ([#12234](https://github.com/diegosouzapw/OmniRoute/pull/12234)) — thanks @raheemuddin786 +- **fix(combo):** always clear the loop-safety timer, not just on the happy path (#11804) ([#12245](https://github.com/diegosouzapw/OmniRoute/pull/12245)) +- **fix(quality):** base-red pair — stryker tap registration + turn-pin suites aligned to the window gate ([#12255](https://github.com/diegosouzapw/OmniRoute/pull/12255)) — thanks @backryun +- **fix(dev):** qualify Turbopack runtime boundaries ([#12258](https://github.com/diegosouzapw/OmniRoute/pull/12258)) — thanks @backryun +- **fix(combo):** fall back to full pool when collapsed sole survivor is context-too-small ([#12278](https://github.com/diegosouzapw/OmniRoute/pull/12278)) — thanks @opensource-elearning +- **fix(combo):** bound the pre-dispatch unavailable skip so a stale label cannot dark a pool (#12168) ([#12285](https://github.com/diegosouzapw/OmniRoute/pull/12285)) +- **fix(security):** strip Qwen/Alibaba console-session cookies from provider API responses ([#12287](https://github.com/diegosouzapw/OmniRoute/pull/12287)) +- **fix(usage):** console-aware Token Plan guidance + subscription hint on bailian 401 ([#12288](https://github.com/diegosouzapw/OmniRoute/pull/12288)) +- **fix(resourcePressure):** log numeric detail on every rejection, recover faster ([#12293](https://github.com/diegosouzapw/OmniRoute/pull/12293)) — thanks @hartmark +- **fix(release):** drain the 2026-09-01 base-red window — passthrough usage regression + radarPage i18n keys ([#12327](https://github.com/diegosouzapw/OmniRoute/pull/12327)) +- **fix(ci):** openapi-security-tiers checker must honor routeGuard patterns + imported prefixes ([#12350](https://github.com/diegosouzapw/OmniRoute/pull/12350)) — thanks @ggiak +- **perf(ci):** cache node_modules in the npm-ci-retry composite (#8084 D3) ([#12408](https://github.com/diegosouzapw/OmniRoute/pull/12408)) +- **revert(ui):** point CTAs back at their real destinations ([#12410](https://github.com/diegosouzapw/OmniRoute/pull/12410)) +- **fix(release):** drain the 2026-09-02 base-red — rerank-providers import + api-typecheck baseline ratchet ([#12414](https://github.com/diegosouzapw/OmniRoute/pull/12414)) +- **fix(types):** make system prompt injection noImplicitAny-safe ([#12416](https://github.com/diegosouzapw/OmniRoute/pull/12416)) +- **fix(quality):** validate typecheck baseline schema ([#12419](https://github.com/diegosouzapw/OmniRoute/pull/12419)) +- **fix(memory):** resolve rerank provider node cache import ([#12421](https://github.com/diegosouzapw/OmniRoute/pull/12421)) +- **fix(ci):** repair release-root regressions — pack dedup, web-session syntax, uc-image ids ([#12423](https://github.com/diegosouzapw/OmniRoute/pull/12423)) — thanks @backryun +- **fix(providers):** separate MaxAI and UC credential contracts ([#12431](https://github.com/diegosouzapw/OmniRoute/pull/12431)) +- **fix(cli):** preserve Claude settings on config set ([#12432](https://github.com/diegosouzapw/OmniRoute/pull/12432)) — thanks @KrzysiekSko +- **fix(providers):** repair the maxai credential block truncated by merge auto-resolve ([#12433](https://github.com/diegosouzapw/OmniRoute/pull/12433)) +- **fix(quality):** record the 2026-09-02 merged growth in the file-size baseline ([#12434](https://github.com/diegosouzapw/OmniRoute/pull/12434)) +- **fix(uc):** route UC error strings through sanitizeErrorMessage; allowlist the retired codex id ([#12437](https://github.com/diegosouzapw/OmniRoute/pull/12437)) +- **fix(adapta):** redact streamed upstream errors ([#12438](https://github.com/diegosouzapw/OmniRoute/pull/12438)) +- **fix(sse):** remap non-contiguous upstream tool_calls index to a gap-free output_index ([#12445](https://github.com/diegosouzapw/OmniRoute/pull/12445)) — thanks @hartmark +- **fix(quality):** detect duplicate tool_calls entries in one response ([#12446](https://github.com/diegosouzapw/OmniRoute/pull/12446)) — thanks @hartmark +- **fix(conversations):** resolve turn content OmniRoute never sends back to the client ([#12447](https://github.com/diegosouzapw/OmniRoute/pull/12447)) — thanks @hartmark +- **fix(combo):** do not treat credits-exhausted 401 as auth skip ([#12449](https://github.com/diegosouzapw/OmniRoute/pull/12449)) — thanks @RaviTharuma +- **fix(auth):** do not park healthy quota accounts as expired ([#12452](https://github.com/diegosouzapw/OmniRoute/pull/12452)) — thanks @RaviTharuma +- **fix(responses-continuation):** fail closed on a collector-truncated, empty output array ([#12460](https://github.com/diegosouzapw/OmniRoute/pull/12460)) — thanks @hartmark +- **fix(logging):** raise the SSE payload collector's default cap ([#12461](https://github.com/diegosouzapw/OmniRoute/pull/12461)) — thanks @hartmark +- **fix(resilience):** surface Responses failed.error.message in 502s ([#12472](https://github.com/diegosouzapw/OmniRoute/pull/12472)) — thanks @RaviTharuma +- **fix(security):** unbiased maxai X-Random nonce + stricter URL/regex assertions ([#12502](https://github.com/diegosouzapw/OmniRoute/pull/12502)) +- **fix(video):** re-anchor transcript log-redaction so PII/credential maskers can't reopen the leak (#12150 P1 follow-up) ([#12503](https://github.com/diegosouzapw/OmniRoute/pull/12503)) +- **fix(monitoring):** serve cached credentialHealth off the request path ([#12533](https://github.com/diegosouzapw/OmniRoute/pull/12533)) — thanks @RaviTharuma +- **fix(quota):** keep Antigravity Gemini usable when Claude weekly is empty ([#12566](https://github.com/diegosouzapw/OmniRoute/pull/12566)) — thanks @HouMinXi +- **fix(authz):** hard-gate every credential export and CLI-config write (GHSA-5926-2w35-7h4q) ([#12600](https://github.com/diegosouzapw/OmniRoute/pull/12600)) +- **fix(docs):** restore the Next build — REMOVED_PROVIDERS.md had no frontmatter (base-red #12581) ([#12610](https://github.com/diegosouzapw/OmniRoute/pull/12610)) +- **fix(quality):** re-point the zcodeProtocol public-creds allowlist to line 313 ([#12615](https://github.com/diegosouzapw/OmniRoute/pull/12615)) +- **fix(security):** close 3 advisories — search baseUrl exfil, sk- in the error sanitizer, bifrost relay header leak ([#12620](https://github.com/diegosouzapw/OmniRoute/pull/12620)) +- **fix(providers):** sanitize boolean required and nested bare maps for Gemini (#12269) ([#12624](https://github.com/diegosouzapw/OmniRoute/pull/12624)) — thanks @HouMinXi +- **fix(resilience):** keep Overloaded STREAM_EARLY_EOF off the provider breaker ([#12626](https://github.com/diegosouzapw/OmniRoute/pull/12626)) — thanks @HouMinXi +- **fix(catalog):** re-audit free-tier quotas against official pages ([#12649](https://github.com/diegosouzapw/OmniRoute/pull/12649)) +- **fix(dashboard):** keep a request's pending-tracking id stable across combo target retries ([#12650](https://github.com/diegosouzapw/OmniRoute/pull/12650)) — thanks @hartmark +- **fix(tests):** retire dead model ids from the chat-pipeline integration suite (base-red #12581) ([#12670](https://github.com/diegosouzapw/OmniRoute/pull/12670)) +- **fix:** resolve SqliteError no such table compression_run_telemetry during cleanup ([#12682](https://github.com/diegosouzapw/OmniRoute/pull/12682)) — thanks @groovecityJO +- **fix(combo):** treat a pin-only step as implicit connection allowlist ([#12697](https://github.com/diegosouzapw/OmniRoute/pull/12697)) — thanks @HouMinXi +- **fix(ci):** ship .npmrc in published package so legacy-peer-deps applies to consumers (#11544) ([#12699](https://github.com/diegosouzapw/OmniRoute/pull/12699)) — thanks @KooshaPari +- **fix(docker):** pin 4 CLI tools to exact versions (#12576) ([#12703](https://github.com/diegosouzapw/OmniRoute/pull/12703)) — thanks @KooshaPari +- **fix(docker):** document and harden cli profile trust boundary (#12570) ([#12706](https://github.com/diegosouzapw/OmniRoute/pull/12706)) — thanks @KooshaPari +- **fix(sse):** redact video transcript in pre-guardrail rejected-request logs (#12150 P2 item 7) ([#12710](https://github.com/diegosouzapw/OmniRoute/pull/12710)) +- **fix(i18n):** quote placeholder in OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES description (#12505) ([#12769](https://github.com/diegosouzapw/OmniRoute/pull/12769)) — thanks @KooshaPari +- **fix(api/pricing):** surface validation error message as string, not raw object (#12494) ([#12771](https://github.com/diegosouzapw/OmniRoute/pull/12771)) — thanks @KooshaPari +- **fix(sse):** re-enable prompt compression for native Codex passthrough ([#12834](https://github.com/diegosouzapw/OmniRoute/pull/12834)) — thanks @thomasmaerz +- **fix(dashboard):** model health tests for a provider node set to the Responses API now call `/v1/responses` with a Responses-shaped body instead of `/v1/chat/completions` — those models were reported as `Provider returned HTTP 200 but no text content` even though the same model answered normally through `/v1/responses` ([#13070](https://github.com/diegosouzapw/OmniRoute/issues/13070)) ([#13087](https://github.com/diegosouzapw/OmniRoute/pull/13087)) — thanks @ntdatt812 +- fix(routing): stop a round-robin combo's "opencode" targets from collapsing onto the opencode-zen connection (#11912) ([#13283](https://github.com/diegosouzapw/OmniRoute/pull/13283)) +- fix(routing): stop the reactive-compaction debug log from lying when compression is globally disabled (#11977) ([#13276](https://github.com/diegosouzapw/OmniRoute/pull/13276)) +- fix(electron): relativize standalone-bundle symlink targets so Stage 8 manifest verification stops failing on Windows (#11979) ([#13251](https://github.com/diegosouzapw/OmniRoute/pull/13251)) +- fix(dashboard): surface a visible error when Compression Studio's combined preview run fails (#12061) ([#13288](https://github.com/diegosouzapw/OmniRoute/pull/13288)) +- fix(dashboard): make the compression "Effective pipeline" preview honor the active profile and warn when the master switch is off (#12063) ([#13287](https://github.com/diegosouzapw/OmniRoute/pull/13287)) +- fix(providers): scope TinyCMS Web signer's DOM shims to each call instead of leaking them for the process lifetime, and surface a clean HTTP status on a non-JSON interception-toggles error (#12072) ([#13254](https://github.com/diegosouzapw/OmniRoute/pull/13254)) +- fix(guardrails): stop Vision Bridge from re-selecting a model locked after a 404 (#12111) ([#13259](https://github.com/diegosouzapw/OmniRoute/pull/13259)) +- fix(sse): require Responses-shaped body before native OpenAI-compatible passthrough (#12129) ([#13278](https://github.com/diegosouzapw/OmniRoute/pull/13278)) +- fix(providers): minimax-m3 now collapses manual thinking.type:"enabled" to adaptive, preventing upstream 400 (2013) (#12132) ([#13249](https://github.com/diegosouzapw/OmniRoute/pull/13249)) +- fix(db): scope model visibility overrides by modality so hiding a Chat model no longer hides an identically-ID'd Image/Embeddings/etc. model (#12172) ([#13258](https://github.com/diegosouzapw/OmniRoute/pull/13258)) +- fix(db): scope local-provider apiKey dedup to matching base URL so LM Studio/Ollama-style connections support multiple accounts (#12173) ([#13250](https://github.com/diegosouzapw/OmniRoute/pull/13250)) +- fix(providers): refresh Trae's stale Referer/Origin and forward user timezone so imported connections stop failing with 401 (#12190) ([#13255](https://github.com/diegosouzapw/OmniRoute/pull/13255)) +- fix(providers): route opencode-go/gpt-5.6-luna to /responses instead of /chat/completions (#12196) ([#13275](https://github.com/diegosouzapw/OmniRoute/pull/13275)) +- fix(dashboard): allow deleting the last extra-upstream-header row even when invalid (#12251) ([#13252](https://github.com/diegosouzapw/OmniRoute/pull/13252)) +- **fix(i18n):** translate pre-existing `__MISSING__:` keys for `combo.sort`, `requestLogger.detail` expand/collapse, `common.profile`, and `settings.resilienceCredentialHealth*` across 39 locales ([#12272](https://github.com/diegosouzapw/OmniRoute/issues/12272)) ([#13034](https://github.com/diegosouzapw/OmniRoute/pull/13034)) — thanks @HouMinXi +- fix(cli): run the Node.js runtime compatibility guard before the heavy `tsx/esm` + Commander import chain so an unsupported runtime gets a clear message instead of a raw `Invalid regular expression flags` crash (#12296) ([#13253](https://github.com/diegosouzapw/OmniRoute/pull/13253)) +- fix(dashboard): refresh the providers list after deleting a compatible provider node (#12298) ([#13256](https://github.com/diegosouzapw/OmniRoute/pull/13256)) +- fix(usage): fail closed on API-key budget enforcement when a provider's `auto` routing alias has no pricing row, instead of silently counting it as $0 (#12341) ([#13257](https://github.com/diegosouzapw/OmniRoute/pull/13257)) +- **fix(providers):** include the submitted Agnes video model when polling by `video_id` ([#12356](https://github.com/diegosouzapw/OmniRoute/pull/12356)) — thanks @pacocartones +- **fix(routing):** custom OpenAI-compatible nodes now honor the saved Chat/Responses API type after edits instead of letting the node's original ID prefix override the live connection setting ([#11884](https://github.com/diegosouzapw/OmniRoute/issues/11884)). ([#12358](https://github.com/diegosouzapw/OmniRoute/pull/12358)) — thanks @pacocartones +- fix(sse): surface an error instead of a silent empty 200 when a Claude stream closes with zero bytes (#12398) ([#13285](https://github.com/diegosouzapw/OmniRoute/pull/13285)) +- fix(oauth): warn before the dead localhost:8080 redirect in antigravity/gemini `oauth start` (#12413) ([#13265](https://github.com/diegosouzapw/OmniRoute/pull/13265)) +- fix(providers): stop devin-cli spawn error from double-closing the SSE controller (#12517) ([#13261](https://github.com/diegosouzapw/OmniRoute/pull/13261)) +- **fix(rerank):** clamp Voyage `top_k` to the documents actually sent after empty-string filtering, and honor `return_documents: false` in the NVIDIA response adapter (#12523 — thanks @pacocartones) +- **fix(audio):** `/v1/audio/translations` now resolves combo names the way `/v1/audio/transcriptions` already does, so a combo that `GET /v1/models` advertises is fanned out to its targets instead of being rejected with `400 Invalid translation model: . Use format: provider/model`; literal `provider/model` ids and unknown bare names behave as before (#12536 — thanks @pacocartones) +- **fix(gemini):** strip the JSON-Schema-2020-12 `prefixItems` keyword from Gemini tool schemas at every nesting level, so Claude Code tool definitions no longer fail with `400 Unknown name "prefixItems"` on Gemini models (#12540 — thanks @pacocartones) +- **fix(api-manager):** Expose an accessible loading status while API keys are fetched instead of an empty accessibility tree (#12541 — thanks @pacocartones) +- **fix(video):** Clamp `estimateJpegFrameBytes` at zero for padding-only payloads and build the three encode-side frame data URIs from `JPEG_FRAME_DATA_URI_PREFIX` instead of a repeated literal (#12543 — thanks @pacocartones) +- **fix(devin):** accept Windows `DEVIN_AGENTIC_HOME` sandbox paths (`C:\...\.sandbox\...`) in the isolated-home check so the Devin Claude Bridge no longer fails closed on Windows ([#12405](https://github.com/diegosouzapw/OmniRoute/issues/12405)) (#12545 — thanks @pacocartones) +- **fix(db):** Add `conversation_turn_nodes` and orphaned `agentic_conversations` to the auto-cleanup cycle under the existing `retention.callLogs` window, so identity nodes whose call-log content has already been purged no longer accumulate without bound in `storage.sqlite` (#12548 — thanks @pacocartones) +- **fix(i18n):** Wrap the `~/.claude/profiles//settings.json` placeholder in ICU single quotes in the `OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES` feature-flag description across all 42 locales and the TypeScript default, so next-intl no longer fails with `INVALID_MESSAGE: UNCLOSED_TAG` and the Feature Flags card shows the description instead of the raw key (#12549 — thanks @pacocartones) +- **fix(orchestration):** `updateCloudAgentTask` now publishes the task's real `status` on `agent.task.updated` when an update only touches `result`, `activities` or `error`, instead of the fabricated `"updated"` state, and stays silent when no row matched the id (#12550 — thanks @pacocartones) +- **fix(i18n):** the home "Recent Requests" panel and the Provider Topology legend are now translated instead of rendering English copies on non-English dashboards; the legend reads its own `home.topologyLegend*` labels with consistent casing rather than borrowing the memory-settings "Recent" and analytics "Error" strings (#12551 — thanks @pacocartones). +- **docs(reference):** bring the `FEATURE_FLAGS.md` catalog back to 1:1 with `featureFlagDefinitions.ts` — 20 missing flags added, the two `*_BLOCK_THRESHOLD` env-only knobs moved out of the flag tables, category/total counts and the Live WS port corrected, guarded by a static test (#12552 — thanks @pacocartones) +- **fix(i18n):** backfill missing `usage.kiloPass*` strings in 39 locales and restore `featureFlagOmnirouteDisableThinkingLevelVariantsDescription` in `pt.json` ([#12561](https://github.com/diegosouzapw/OmniRoute/issues/12561)) ([#13027](https://github.com/diegosouzapw/OmniRoute/pull/13027)) — thanks @HouMinXi +- fix(docker): default docker-compose app ports (dashboard/API/live-WS) to loopback instead of `0.0.0.0`, closing the anonymous `/v1` LAN/WAN exposure gap left open by `REQUIRE_API_KEY=false` (#12568) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): close DNS-rebinding SSRF gap in webhook outbound-URL guard (#12569) ([#13243](https://github.com/diegosouzapw/OmniRoute/pull/13243)) +- fix(docker): require a per-session token on the VNC browser CDP bridge and isolate it on a dedicated Docker network (#12571) ([#13241](https://github.com/diegosouzapw/OmniRoute/pull/13241)) +- fix(open-sse): write Adobe Firefly session tokens and cookie jars with 0700/0600 permissions instead of the process umask (#12572) ([#13242](https://github.com/diegosouzapw/OmniRoute/pull/13242)) +- fix(api): remove hardcoded wildcard CORS in openai-to-gemini-sse.ts so the centralized fail-closed CORS gate is the sole source of `Access-Control-Allow-Origin` (#12573) ([#13239](https://github.com/diegosouzapw/OmniRoute/pull/13239)) +- fix(api): enforce API key policy (budget/rate-limit/schedule/endpoint scoping) on the ElevenLabs speech-to-text, text-to-speech and voices proxy routes (#12574) ([#13244](https://github.com/diegosouzapw/OmniRoute/pull/13244)) +- fix(sse): cap HuggingChat NDJSON body size and bound the read loop with the fetch timeout so a stalled or hostile upstream cannot buffer unbounded memory (#12577) ([#13282](https://github.com/diegosouzapw/OmniRoute/pull/13282)) +- fix(docker): scope the cliproxyapi/qdrant/bifrost sidecars to loopback by default and forward `CLIPROXYAPI_MANAGEMENT_KEY` into the cliproxyapi container so its management API is not left both unauthenticated and LAN-published (#12578) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): create DB export temp paths with `fs.mkdtempSync` instead of predictable timestamps (#12579) ([#13237](https://github.com/diegosouzapw/OmniRoute/pull/13237)) +- Cline 401 bodies that say "re-authenticate your Cline account" classify as a refreshable OAuth token, not a terminal expired key. The cooling panel no longer labels every cooldown as a 429; it shows the recorded last error instead. (#12594) ([#13060](https://github.com/diegosouzapw/OmniRoute/pull/13060)) — thanks @HouMinXi +- **fix(catalog):** degrade unknown combo targets instead of dropping LCD modalities, and persist OpenRouter `architecture.input_modalities` into the capability snapshot ([#12613](https://github.com/diegosouzapw/OmniRoute/issues/12613)) ([#13042](https://github.com/diegosouzapw/OmniRoute/pull/13042)) — thanks @HouMinXi +- fix(providers): send `x-api-key` instead of `Authorization: Bearer` for OpenCode Zen's `/v1/responses` endpoint (Muse Spark Contributor models), fixing a 401 on OmniRoute's auth header (#12633) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(sse): surface the actionable "Auggie CLI not found" message when the shell reports a missing `auggie` binary via exit code instead of a spawn error (#12645) ([#13263](https://github.com/diegosouzapw/OmniRoute/pull/13263)) +- **fix(config):** Persist deletions of built-in background-degradation entries — when a stored settings record exists its `degradationMap` is now authoritative instead of being merged under the defaults, so an entry the user removed in the dashboard no longer reappears on the next apply or restart ([#12424](https://github.com/diegosouzapw/OmniRoute/issues/12424)) ([#12647](https://github.com/diegosouzapw/OmniRoute/pull/12647)) — thanks @pacocartones +- **fix(gamification):** action-count badge milestones (First Token, Token Consumer, Token Machine, Token Whale, and the token-sharing tier) are now backed by a durable `xp_action_counts` counter incremented in `addXp()`, instead of a live `COUNT(*)` over `xp_audit_log`. The audit log is pruned by `retention.xpAuditLog` (default 30 days), so on a default install those "lifetime" milestones were really "actions in the last 30 days" and unlocked badges could stop unlocking once old rows aged out. `getActionCount()` and `checkActionCountBadges()` now read the same durable source, and a migration backfills existing totals from the surviving audit rows ([#12546](https://github.com/diegosouzapw/OmniRoute/issues/12546)) ([#12651](https://github.com/diegosouzapw/OmniRoute/pull/12651)) — thanks @pacocartones +- **fix(images):** `/v1/images/edits` now iterates a combo's targets the same way `/v1/images/generations` does (#9239) instead of flattening a bare combo to its first target. A combo whose first target is not edit-capable — or lacks credentials — now falls through to a later edit-capable target rather than hard-erroring, and missing credentials are skipped (not a hard `401`) to match the generations path. The per-target skip/terminal classification is extracted into a shared `runImageComboTargets` loop, so generations behavior is unchanged ([#12547](https://github.com/diegosouzapw/OmniRoute/issues/12547)). ([#12653](https://github.com/diegosouzapw/OmniRoute/pull/12653)) — thanks @pacocartones +- fix(sse): add first-byte watchdog to the TLS-fingerprint transport so a stalled wreq body falls back instead of hanging for minutes (#12656) ([#13272](https://github.com/diegosouzapw/OmniRoute/pull/13272)) +- fix(sse): exempt tiny-budget reasoning probes from combo quality failure and surface persisted-cooldown skips in ALL_TARGETS_SKIPPED diagnostics (#12659) ([#13269](https://github.com/diegosouzapw/OmniRoute/pull/13269)) +- fix(models): declare the real ~1M contextLength for OpenCode Zen's Muse Spark 1.2 models instead of falling back to the 200000 provider default (#12681) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(oauth): align codebuddy-cn OAuth User-Agent with the chat/usage CLI version to avoid WAF false positives (#12702) ([#13264](https://github.com/diegosouzapw/OmniRoute/pull/13264)) +- fix(dashboard): surface an authentication-required banner instead of silently blanking database settings for a guest session (#12709) ([#13270](https://github.com/diegosouzapw/OmniRoute/pull/13270)) +- **fix(resilience):** stop unbounded queue that hangs 6min until Aborted — gate, provider slot, and Bottleneck queue now share a per-connection `maxWaitMs` budget; `fail-closed` on exhaust (503); execution backstop `executionMaxWaitMs` overridable per connection with upstream clamp ([#12715](https://github.com/diegosouzapw/OmniRoute/pull/12715)) — thanks @maxmad64bis (with thanks to @Tushar49 for surfacing the slow-provider need in #12635) +- **fix(auto-combo):** every mode pack now carries `quality` and `reliability` weights (`quality` 0.02, 0.03 in `quality-first`; `reliability` 0.03, 0.04 in `reliability-first`), so selecting a pack no longer silences either signal; `DEFAULT_WEIGHTS` is unchanged ([#12731](https://github.com/diegosouzapw/OmniRoute/pull/12731)) — thanks @maxmad64bis +- **fix(sse):** the provider execution pipeline keeps the upstream error `code`/`type` again — both were lost when the non-streaming leg was extracted, so a config-class `422` (Antigravity missing project) degraded into a generic account cooldown instead of reaching the gate that recognises it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(db):** a legacy `call_logs` table no longer aborts startup — the provider-stats index is created after the column healing runs, not before it, so an install predating `request_type` opens instead of failing with `no such column` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(catalog):** a malformed row in the operator-writable custom-models blob no longer kills every `auto/*` pool ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(tests):** realign the guards that the same day's merges left asserting the old behavior — the provider-count total after GreenPT and EURouter, the GLM stream buffer slot that `#12925` turned into a declared parameter, and the injection scan bound that `#13104` reshaped from a head-only window into head-plus-tail ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- fix(cache): fold tool_choice/tools/response_format into the semantic cache signature so a cached tool_calls response can no longer be replayed for a request whose tool policy forbids it (#12734) ([#13267](https://github.com/diegosouzapw/OmniRoute/pull/13267)) +- **fix(models):** stop treating provider-supplied `isFree:true`/`:free`/`0/0` flags as trusted unless the provider has a documented free tier — only the shipped free-tier catalog decides otherwise; fetched `isFree:true` trusted only for a free-tier provider, custom `isFree:true` via a trusted path ([#12744](https://github.com/diegosouzapw/OmniRoute/pull/12744)) — thanks @maxmad64bis +- fix(memory): authenticate the internal /v1/rerank loopback call so memory reranking no longer silently degrades to unranked order when REQUIRE_API_KEY=true (#12745) ([#13268](https://github.com/diegosouzapw/OmniRoute/pull/13268)) +- fix(sse): parse Ollama Cloud's current usage markup (`$X of $Y used` aria-label, nested width style) (#12749) ([#13260](https://github.com/diegosouzapw/OmniRoute/pull/13260)) +- fix(cli): setup-opencode no longer sends an active context's management token to `/v1/models` when `--api-key`/`OMNIROUTE_API_KEY` is supplied — an explicit flag or the env var now always outranks the context's token, and the flag itself is no longer swallowed by the parent program's global `--api-key` option (#12783) ([#13246](https://github.com/diegosouzapw/OmniRoute/pull/13246)) +- fix(sse): register Arcee AI in the executor provider registry so requests reach api.arcee.ai instead of silently falling back to OpenAI (#12784) ([#13277](https://github.com/diegosouzapw/OmniRoute/pull/13277)) +- **fix(quota):** share one upstream quota read when identical saturation checks arrive at the same time, so traffic bursts don't multiply provider API calls ([#12787](https://github.com/diegosouzapw/OmniRoute/pull/12787)) — thanks @maxmad64bis +- **fix(db):** ignore expired or invalid rate-limit cooldown writes so a stale timestamp can't lock a connection that should be usable — clearing still works as before ([#12788](https://github.com/diegosouzapw/OmniRoute/pull/12788)) — thanks @maxmad64bis +- **fix(providers):** single source for provider order with `xao` ranking alongside `xai-oauth` ([#12790](https://github.com/diegosouzapw/OmniRoute/pull/12790)) — thanks @maxmad64bis +- **fix(routing):** off-table models check the free-model catalog before inheriting premium prices, latency bootstraps from the observed pool median, async tiers read live database pricing with a 90-day freshness gate, and the tier cache invalidates on every pricing write ([#12792](https://github.com/diegosouzapw/OmniRoute/pull/12792)) — thanks @maxmad64bis +- **fix(auto-combo):** snapshot scoring uses the observed per-connection account tier and per-model quality instead of neutral constants, and circuit-open providers rank lower at build time ([#12794](https://github.com/diegosouzapw/OmniRoute/pull/12794)) — thanks @maxmad64bis +- **fix(pool):** empty `auto/*` pools now say why they are empty, and the models listing uses the same paid check as routing ([#12795](https://github.com/diegosouzapw/OmniRoute/pull/12795)) — thanks @maxmad64bis +- fix(routing): recognize CLIProxyAPI's 'unknown provider for model' 400 as fallback-worthy (#12800) ([#13284](https://github.com/diegosouzapw/OmniRoute/pull/13284)) +- **fix(sse):** translate-mode streams now emit the estimated token counts as a trailing usage-only chunk before `[DONE]` when the upstream stays silent, so metered chat clients see totals instead of nothing ([#12828](https://github.com/diegosouzapw/OmniRoute/pull/12828)) — thanks @maxmad64bis +- **fix(db):** provider stats stay truthful on empty and legacy databases: fallback counts default to `0` instead of `null`, latency averages read `null` (not `0`) when no durations were recorded, failures logged before the error-type column existed group under `pre_migration` instead of `unclassified`, and per-provider queries use two new composite indexes ([#12832](https://github.com/diegosouzapw/OmniRoute/pull/12832)) — thanks @maxmad64bis +- fix(nvidia): fail open when a synced model catalog goes stale instead of gating forever (#12849) ([#13248](https://github.com/diegosouzapw/OmniRoute/pull/13248)) +- **fix(db):** WAL maintenance lives in its own module: a `TRUNCATE` checkpoint that hits a busy database now warns with its streak and retries once via `PASSIVE` instead of logging success, and closing-time checkpoints no longer report success on builds without a database file, with the busy totals visible in the authenticated monitoring health payload ([#12853](https://github.com/diegosouzapw/OmniRoute/pull/12853)) — thanks @maxmad64bis +- **fix(health):** quota with no snapshots reads empty instead of a contradicting 0% ([#12857](https://github.com/diegosouzapw/OmniRoute/pull/12857)) — thanks @maxmad64bis +- fix(a2a): accept the dashboard's own session cookie on /a2a so "Run message/send" no longer fails with "Unauthorized: missing or invalid API key" (#12888) ([#13271](https://github.com/diegosouzapw/OmniRoute/pull/13271)) +- **fix(logs):** the Logs grid's in-memory filter pass no longer discards rows the SQL query already matched — selecting an API key from the dropdown (which sends the key's id) returns its calls again, the Combo tab shows every combo instead of only those whose name contains a "1", and the model filter and search cover the same columns as the query ([#12896](https://github.com/diegosouzapw/OmniRoute/pull/12896)) — fixes [#12873](https://github.com/diegosouzapw/OmniRoute/issues/12873) — thanks @ntdatt812 +- **fix(a2a):** `/api/a2a/status` now builds the agent card from the request that asked for it, so a gateway reached at a non-localhost host no longer advertises `http://localhost:20128` as its A2A URL ([#12918](https://github.com/diegosouzapw/OmniRoute/pull/12918)). — thanks @ntdatt812 +- **fix(compression):** progressive aging now appends its `[COMPRESSED:aging:…]` annotation after a turn's `tool_result` blocks instead of in front of them, so Anthropic no longer rejects aged conversations with "`tool_use` ids were found without `tool_result` blocks immediately after" ([#12920](https://github.com/diegosouzapw/OmniRoute/pull/12920)). — thanks @ntdatt812 +- **fix(bedrock):** model import now resolves context limits for every vendor prefix instead of only `anthropic.*`, so `global.openai.gpt-5.6-*` no longer imports with a null `inputTokenLimit` and gets rejected pre-flight at the 200k default ([#12921](https://github.com/diegosouzapw/OmniRoute/pull/12921)). — thanks @ntdatt812 +- **fix(stream):** the 64 KB stream buffer GLM asks for is honoured instead of dropped, and the type error it caused no longer fails the API Route Typecheck gate on every open PR ([#12925](https://github.com/diegosouzapw/OmniRoute/pull/12925)) — thanks @ntdatt812 +- **fix(guardrails):** mask PII inside a `tool_result`'s nested content array, which the masker walked past while redacting its sibling block ([#12930](https://github.com/diegosouzapw/OmniRoute/pull/12930)) — thanks @ntdatt812 +- **fix(dashboard):** the Radar catalog table no longer leaves absent data unexplained — empty limits, unknown context windows, and unreported capabilities each explain themselves on hover, and a new check keeps it that way ([#12937](https://github.com/diegosouzapw/OmniRoute/pull/12937)) — thanks @maxmad64bis +- **fix(sse):** geo-blocked opencode requests rotate to the next account proxy instead of failing, so one refused egress no longer aborts the whole chain ([#12941](https://github.com/diegosouzapw/OmniRoute/pull/12941)) — thanks @maxmad64bis +- **fix(codex):** dashboard "clear cooldown" and the CAS recovery path now drop nested `codexScopeRateLimitedUntil` maps in the same write that nulls `rate_limited_until`, and fresh quota snapshots with headroom lift fallback-sourced scope cooldowns parked by quota preflight ([#12817](https://github.com/diegosouzapw/OmniRoute/issues/12817), [#12860](https://github.com/diegosouzapw/OmniRoute/issues/12860), [#12951](https://github.com/diegosouzapw/OmniRoute/pull/12951)) The dashboard reset-credit button (`consumeCodexResetCredit`) now exercises that same snapshot path after a successful redeem, so a filled quota bar is enough to unpark a leftover fallback Codex child without another manual clear. — thanks @HouMinXi +- **fix(db):** resolve `sql-wasm.wasm` across global npm install and hoisted layouts, ensuring OmniRoute can boot cleanly on Node 24 when native `better-sqlite3` is uncompiled. ([#13035](https://github.com/diegosouzapw/OmniRoute/pull/13035)) — thanks @HouMinXi +- **fix(api):** `DELETE /v1/batches/delete-completed` now sweeps only the calling API key's own completed batches (batches with no owner stay out of a key-scoped sweep on purpose), with an explicit instance-wide mode reserved for authenticated dashboard sessions, a 401 for a presented key that is unknown, revoked, deactivated, banned or expired (never falling through to the session branch), audit logging of every sweep, a sanitized 500 on failure, an owner-scoped file half (a key-scoped sweep never nulls a file another tenant owns) and an atomic sweep — chunked in 200-batch transactions in instance mode — so a mid-way error never leaves a batch pointing at a nulled file (GHSA-wvxc-jp3v-5mg5) ([#12969](https://github.com/diegosouzapw/OmniRoute/pull/12969)) +- **fix(combo):** quota-weighted routing stops drawing on an out-of-credit connection — a 402 now invalidates the stored quota snapshot instead of leaving its stale remaining percentage in place, and a snapshot older than 10 minutes no longer counts as confident headroom for the primary pool ([#12972](https://github.com/diegosouzapw/OmniRoute/pull/12972)) ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- **fix(vision):** Custom Models with "Vision capable" checked no longer have image requests swapped to `glm/glm-4.6v` when the client sends the advertised alias (`vllm/path/...`) or the bare path-shaped id — Vision Bridge now matches the stored override for all three id forms ([#12758](https://github.com/diegosouzapw/OmniRoute/issues/12758)) ([#12974](https://github.com/diegosouzapw/OmniRoute/pull/12974)) — thanks @HouMinXi +- **fix(sse):** transient opencode upstream failures rotate to the next account proxy instead of failing, so one flapping egress no longer aborts the whole chain ([#12975](https://github.com/diegosouzapw/OmniRoute/pull/12975)) — thanks @maxmad64bis +- **fix(azure):** Deployments from GPT-6 onward now send `max_completion_tokens` instead of `max_tokens`, which Azure rejects with HTTP 400. The rule matched a literal `gpt-5`, so each new generation arrived broken; it now matches the generation range, while `gpt-35-turbo` still keeps `max_tokens`. ([#13007](https://github.com/diegosouzapw/OmniRoute/pull/13007)) — thanks @ntdatt812 +- **fix(db):** add an opt-in self-restart circuit for sustained critical memory pressure, gate post-cleanup VACUUM behind a minimum freed-rows threshold, and checkpoint the SQLite WAL every 5 minutes with a size guard that escalates to TRUNCATE, so a growing WAL can no longer stall the event loop into a full outage. ([#13011](https://github.com/diegosouzapw/OmniRoute/pull/13011)) — thanks @HouMinXi +- **fix(auth):** an explicit connection pin may probe a quota-disabled row once and re-enable it on success ([#12874](https://github.com/diegosouzapw/OmniRoute/issues/12874)) ([#13017](https://github.com/diegosouzapw/OmniRoute/pull/13017)) — thanks @HouMinXi +- **fix(api):** thread `X-OmniRoute-Fallback-Attempts` through combo chat completions so streaming and non-streaming responses report how many prior legs were attempted ([#13038](https://github.com/diegosouzapw/OmniRoute/pull/13038)) — thanks @HouMinXi +- Restore provider failure classification and credential refresh on non-streaming requests: classify non-2xx failures to lock models on per-model quota exhaustion, update connection rate limits from headers and body, and pass credential refresh handlers to pipeline execution so 401 tokens can be refreshed and retried (#13043). ([#13069](https://github.com/diegosouzapw/OmniRoute/pull/13069)) — thanks @HouMinXi +- **fix(responses):** wrap forced-non-streaming web_search fallback JSON as Responses SSE so Codex still sees `response.completed` ([#13050](https://github.com/diegosouzapw/OmniRoute/pull/13050)) — thanks @HouMinXi +- **fix(antigravity):** Preserve upstream thought-token usage in normalized completion and reasoning token counts ([#13055](https://github.com/diegosouzapw/OmniRoute/pull/13055)) — thanks @pacocartones +- Fix a concurrency-slot leak in the DeepSeek PoW solver: a worker that failed to spawn (for example a missing worker script) never released its slot, so `MAX_CONCURRENT_WORKERS` failures disabled the solver until restart. ([#13097](https://github.com/diegosouzapw/OmniRoute/pull/13097)) — thanks @anhtahaylove +- **fix(acp):** bound the ACP session output buffers — `stdoutBuffer` and `stderrBuffer` now cap at 1 MiB keeping the most recent output behind a visible `[...output truncated...]` marker, and `stderrBuffer` is reset per prompt instead of accumulating for the lifetime of the session. ([#13100](https://github.com/diegosouzapw/OmniRoute/pull/13100)) — thanks @anhtahaylove +- **fix(acp):** release the `stdout`/`exit` listeners and the idle timer that a `sendPrompt` timeout used to leave attached to the `acpManager` singleton, and drop sessions that exited on their own from the session map instead of keeping them forever. ([#13096](https://github.com/diegosouzapw/OmniRoute/pull/13096)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection and PII scanners now read the text a `tool_result` block carries on `content` (string or nested block list), in messages and in system blocks, so tool output is judged by the same rules as user text ([#13101](https://github.com/diegosouzapw/OmniRoute/pull/13101)) — thanks @ntdatt812 +- **fix(gamification):** close the badge notification SSE stream when the request signal is already aborted before the stream starts — a client that disconnects while the route is still awaiting auth used to leave both the 2s unlock poll and the 15s heartbeat running for the lifetime of the process. ([#13106](https://github.com/diegosouzapw/OmniRoute/pull/13106)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection scan now spends its 16 KB budget on both ends of the request instead of the first 16 KB only, so `system`, `instructions`, `query`, `documents` and the newest turns are no longer hidden behind one long message ([#13104](https://github.com/diegosouzapw/OmniRoute/pull/13104)) — thanks @ntdatt812 +- **fix(dashboard):** expose the Volcano Ark console cookie on quota scraping and unwrap connect-error objects so the dashboard shows the upstream message ([#13107](https://github.com/diegosouzapw/OmniRoute/pull/13107)) — thanks @HouMinXi +- **fix(db):** release the `beforeExit`/`SIGINT`/`SIGTERM` handlers when a `node:sqlite` adapter closes, so a closed adapter and its database handle are no longer pinned to `process` for the lifetime of the run — the same treatment #7494 gave the sql.js adapter. ([#13109](https://github.com/diegosouzapw/OmniRoute/pull/13109)) — thanks @anhtahaylove +- **fix(translator):** `contentSchema` and `unevaluatedItems` are now treated as subschema positions by the tool-schema sanitizer, so a truncation placeholder in either is replaced with a permissive schema instead of being forwarded as a string ([#13110](https://github.com/diegosouzapw/OmniRoute/pull/13110)) — thanks @ntdatt812 +- **fix(cli-helper):** clear the `createLogStream` timeout on the abort path — `stop()` aborts the in-flight fetch and returned through the `signal.aborted` branch, which skipped `clearTimeout` and left an armed timer per stopped stream. The stream reader is now also cancelled when the read loop exits early. ([#13114](https://github.com/diegosouzapw/OmniRoute/pull/13114)) — thanks @anhtahaylove +- **fix(docs):** drop TLS-impersonation, MITM-decrypt, supply-chain attestation, and XOR-mask writeups from the public `/docs` catalog and Docker image. Files stay in git for engineers; operators who need them open the repo, not the website. ([#13136](https://github.com/diegosouzapw/OmniRoute/pull/13136)) — thanks @HouMinXi +- **fix(combo):** parse numeric-epoch `rate_limited_until` in the combo cooldown read path ([#13141](https://github.com/diegosouzapw/OmniRoute/pull/13141)) — thanks @maxmad64bis +- **fix(opencode):** both OpenCode plugins now reject a gateway address typed without `http://` at configuration time, instead of publishing every model with an api url no client can call, and the v2 plugin no longer publishes a model card whose api url is blank or relative ([#13142](https://github.com/diegosouzapw/OmniRoute/pull/13142)) — thanks @maxmad64bis +- **fix(providers):** lock opencode model on upstream 400 model-unavailable ([#13146](https://github.com/diegosouzapw/OmniRoute/pull/13146)) — thanks @maxmad64bis +- **fix(logging):** keep the provider exchange rather than the raw client bodies when a call log exceeds its size budget, and show that recovered payload in the request-detail panel instead of replacing it with the stored response body ([#13147](https://github.com/diegosouzapw/OmniRoute/pull/13147)) — thanks @maxmad64bis +- **fix(traffic-inspector):** the WebSocket route no longer leaks a traffic-buffer subscriber and a 30s ping timer when the client socket is already closed at handler time — listeners are attached before any resource is acquired, a destroyed socket bails out early, and the ping interval stops on a dead socket where `write()` never throws ([#13155](https://github.com/diegosouzapw/OmniRoute/pull/13155)) — thanks @anhtahaylove +- **fix(telegram):** bound the per-user API key cache in the Telegram chat proxy so a burst of distinct chat ids can no longer grow the process heap without limit ([#13165](https://github.com/diegosouzapw/OmniRoute/issues/13165)) ([#13166](https://github.com/diegosouzapw/OmniRoute/pull/13166)) — thanks @anhtahaylove +- **fix(stream):** cancel the upstream response body when the JSON-to-SSE sniff unwinds on a body timeout, so a stalled upstream no longer pins the connection ([#13169](https://github.com/diegosouzapw/OmniRoute/issues/13169)) ([#13171](https://github.com/diegosouzapw/OmniRoute/pull/13171)) — thanks @anhtahaylove +- **fix(telegram):** authenticate webhook deliveries with Telegram's `secret_token` so an unauthenticated caller can no longer mint API keys or spend upstream quota ([#13172](https://github.com/diegosouzapw/OmniRoute/issues/13172)) ([#13175](https://github.com/diegosouzapw/OmniRoute/pull/13175)) — thanks @anhtahaylove +- **fix(models):** give discoverable Gemini 3.8 Flash ids their own 65536 output spec so Antigravity no longer clamps them to 16384 ([#13195](https://github.com/diegosouzapw/OmniRoute/pull/13195)) — thanks @HouMinXi +- **fix(dashboard):** leftover catalog-removed provider rows (gemini-cli) can be listed and purged from the providers page ([#13067](https://github.com/diegosouzapw/OmniRoute/issues/13067)) ([#13197](https://github.com/diegosouzapw/OmniRoute/pull/13197)) — thanks @HouMinXi +- **fix(auth):** a dashboard session now requires the `authenticated: true` claim that login, OIDC and the session refresh already emit — a JWT merely signed with `JWT_SECRET` (for example the Cursor CLI passthrough token, which any API-key holder can obtain) no longer verifies as the `auth_token` cookie on any route, the WebSocket handshake or the live server; existing sessions keep working ([#13298](https://github.com/diegosouzapw/OmniRoute/issues/13298)) ([#13375](https://github.com/diegosouzapw/OmniRoute/pull/13375)) +- **fix(api):** `DELETE /v1/batches/delete-completed` now applies the caller's API-key policy (endpoint allowlist, schedule, usage cap, rate limit) like every other `/v1` route, commits the key-scoped sweep per 200-batch chunk instead of holding one write lock for the whole sweep, refuses to loop without progress, and rejects a scope that names both a key and `allTenants` ([#13684](https://github.com/diegosouzapw/OmniRoute/pull/13684)) +- Restore the API-route typecheck gate: the non-streaming leg result lost its discriminated-union narrowing after the server-owned tool loop reassignment, producing 13 new TS2339 diagnostics in `chatCore.ts`. ([#12963](https://github.com/diegosouzapw/OmniRoute/pull/12963)) +- Restore four non-streaming behaviours the server-owned tool loop refactor dropped: client aborts map to 499 with the fixed `Request aborted` message, an aborted request no longer logs a synthetic `clientResponse`, Claude prompt-cache telemetry is recorded again, and a body that cannot be canonicalized no longer throws when the tool loop is off. ([#12990](https://github.com/diegosouzapw/OmniRoute/pull/12990)) +- **fix(skills):** The CLI registry parser now reads positionals declared with `.addArgument()`, not only those written inline in `.command()`. `tunnel create [type]` was being published as `tunnel create`, so the agent-skills sync gate reported drift on every branch and regenerating would have deleted the argument. ([#13009](https://github.com/diegosouzapw/OmniRoute/pull/13009)) — thanks @ntdatt812 +- **fix(combos):** dashboard combo test uses a short prompt, serial probes, and a 60s timeout so reasoning models and rate-limited free pools do not fail the health check ([#13001](https://github.com/diegosouzapw/OmniRoute/pull/13001)) — thanks @HouMinXi +- fix(compression): terminate idle worker threads on eviction so long-running instances stop leaking OS threads and MessagePorts ([#13091](https://github.com/diegosouzapw/OmniRoute/pull/13091)) — thanks @anhtahaylove +- Redact Google API keys of any length in error bodies: the pattern required exactly 39 characters, so shorter or longer `AIza…` credentials passed through unredacted. ([#12964](https://github.com/diegosouzapw/OmniRoute/pull/12964)) +- **fix(grok-cli):** a 402 "Grok Build usage balance exhausted" parks that Grok login as out of credit (Grok Build CLI, grok.com cookie, and xAI OAuth share the weekly pool). Combo routing then tries the next login instead of locking the model for every account in the pool ([#13061](https://github.com/diegosouzapw/OmniRoute/pull/13061)) — thanks @HouMinXi +- fix(compression): spawn the LLMLingua worker with a file URL object so compression actually runs instead of silently failing open on Node ([#13093](https://github.com/diegosouzapw/OmniRoute/pull/13093)) — thanks @anhtahaylove +- **fix(test):** run the local `test` and `test:unit` scripts at concurrency 4 so a full-suite run no longer exhausts the machine's commit charge and kills unrelated processes ([#13187](https://github.com/diegosouzapw/OmniRoute/pull/13187)) — thanks @anhtahaylove +- **fix(monitoring):** `GET /api/monitoring/health` `credentialHealth` now includes a bounded `failedConnections` list (`connectionId`, `status`, sanitized `lastError`) when the probe-cache gauge `failed>0`, plus `source: probe-cache` and a cheap `staleDbNonOkCount` for sticky SQLite `test_status` on active rows. Documents that the live gauge is not `provider_connections.test_status`. ([#12876](https://github.com/diegosouzapw/OmniRoute/pull/12876)) — thanks @RaviTharuma +- **fix(resilience):** an openai-compatible multi-upstream gateway no longer marks the whole connection `credits_exhausted` when a single upstream (or the `/models` / representative-model health probe) returns 402. Quota failures stay model-scoped; true connection-wide auth failures are unchanged. Single-credential 402 key disable ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239)) is preserved. ([#12875](https://github.com/diegosouzapw/OmniRoute/pull/12875)) — thanks @RaviTharuma +- **fix(dashboard):** nine fixes on the Orchestration Canvas, all diagnosed in the Phase 2 reviews. A source that fails now keeps the timestamp of its FIRST failure instead of being re-stamped every poll — the stale line said "since the last poll" no matter how long the source had been down, and the churn also defeated the snapshot's stable identity (it serializes the source list), so the canvas re-rendered on every tick while anything was broken. A source that HAD data and only then started failing is flagged too: previously only a source with no node at all got the warning, so a source that went down mid-session kept a healthy-looking node forever. The rest are pointwise: clicking a filter chip cancels the pending search debounce (left armed, it fired ~300ms later and silently reverted the chip); the search input carries an accessible name; `?state=running, failed` parses like the unpadded form instead of dropping the padded value; the CSV toggle helper is defined once in `model/urlParams.ts` rather than twice; the agents tab tells "nothing running" apart from "the filter matched nothing", offering a clear-filters button instead of setup links that would be wrong advice there; edges stop emitting SMIL particles above 40 simultaneously active edges, keeping the colored stroke; and the drawer's error banner clears when a retried action succeeds. ([#12983](https://github.com/diegosouzapw/OmniRoute/pull/12983)) +- **fix(i18n):** the nine locales added with the EU-language batch (Greek, Estonian, Irish, Croatian, Lithuanian, Latvian, Maltese, Slovenian, Serbian) were missing the eleven Orchestration Canvas keys that Phase 3 introduced, so the compare-runs panel and the "no runs match these filters" empty state fell back to English in those languages (`deepMergeFallback` substitutes English for an absent key, so nothing rendered blank — it rendered untranslated). The coverage gate does not catch this: it enforces an 80% floor per locale, and eleven missing keys out of ~13,000 leaves coverage at 99.9%. Translated for real in each language, calibrated against the wording each file already uses for "run", "filter" and "skill". ([#13294](https://github.com/diegosouzapw/OmniRoute/pull/13294)) +- **fix(dashboard):** five follow-ups from the Phase 2 Orchestration Canvas review. The History drawer now shows the memory section for runs that already left the live TTL window: the persisted `memory_hits` event is parsed into `metadata.memoryHits` with the same defensive validation the drawer applies, and — because that event is observability rather than a state transition — it no longer leaks into the timeline, where it had been inheriting the task's state and rendering as a duplicate transition. The A2A memory recall runs against its own 1.5s deadline instead of inheriting the memory backend's 30s one; overshooting degrades exactly like any other recall failure (no hits, task proceeds), and the timer is cleared on both paths. Repeating a Conductor run carries its `requirements.cli`/`requirements.model` forward, so the new run is pinned to the same runner profile and model rather than drifting to whatever the fleet picks. A successful repeat from the Agents tab now focuses the run it created, instead of leaving the operator on the finished one. And the auth test for `POST /api/conductor/tasks` moved into the shared `ROUTES` array rather than restating the pattern. ([#12988](https://github.com/diegosouzapw/OmniRoute/pull/12988)) +- fix(plugins): stop leaking an exit listener per plugin hook timeout, which triggered MaxListenersExceededWarning on plugins that ignore SIGTERM ([#13092](https://github.com/diegosouzapw/OmniRoute/pull/13092)) — thanks @anhtahaylove +- **fix(validation):** Provider node edits no longer fail with a generic "Invalid request" when the optional daily-quota reset fields are left blank. The dashboard sends `dailyQuotaResetTimezone` and `dailyQuotaResetHour` as `null`, and only the hour accepted it. ([#13066](https://github.com/diegosouzapw/OmniRoute/issues/13066)) ([#13083](https://github.com/diegosouzapw/OmniRoute/pull/13083)) — thanks @ntdatt812 +- **fix(combo):** quota-aware expansion drops banned, inactive, missing, and wrong-provider connections before quota fetch or model dispatch; pins and allowlists stay selectors, not a bypass. Antigravity automatic exhaustion now requires a reported zero remaining, so a positive balance below 1% stays eligible. ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- Fix an intermittent failure in the quota-weighted routing test suite: scores are a function of `Date.now()`, so peers with identical quota scored microseconds apart never tied and swapped order. ([#12946](https://github.com/diegosouzapw/OmniRoute/pull/12946)) +- fix(volcengine): resolve quota window mapping gaps, safe multi-connection console binding, and response cookie sanitization ([#12950](https://github.com/diegosouzapw/OmniRoute/pull/12950)) — thanks @HouMinXi +- **fix(test):** resolve the WebDAV handler path with `fileURLToPath` so the suite's 37 WebDAV tests run on Windows instead of failing with a doubled `C:\C:\` drive prefix ([#13196](https://github.com/diegosouzapw/OmniRoute/pull/13196)) — thanks @anhtahaylove +- **fix(ui):** fall back from invalid AnySearch icon alias (direct commit `9587e07b69`) +- **fix(sse):** stop rebuilding a truncated summary from the collector's cap-dropped event array ([#12718](https://github.com/diegosouzapw/OmniRoute/pull/12718)) — thanks @hartmark +- **fix(conversations):** show a pending spinner for unresolved tool nodes instead of "(empty)" ([#12727](https://github.com/diegosouzapw/OmniRoute/pull/12727)) — thanks @hartmark +- **fix(streaming):** fail fast when an upstream stream produces only lifecycle/heartbeat events, never real content ([#12741](https://github.com/diegosouzapw/OmniRoute/pull/12741)) — thanks @hartmark +- **fix(responses-continuation):** bridge the write-in-flight window with an in-memory pending store ([#12854](https://github.com/diegosouzapw/OmniRoute/pull/12854)) — thanks @hartmark +- **fix(translator):** drop or map agent_message on Chat Completions fallback ([#12880](https://github.com/diegosouzapw/OmniRoute/pull/12880)) — thanks @RaviTharuma +- **fix(api):** page and stream-complete GET /v1/models for large catalogs ([#12882](https://github.com/diegosouzapw/OmniRoute/pull/12882)) — thanks @RaviTharuma +- **fix(quota):** align AUTH window labels with usage API and clarify 503 ([#12884](https://github.com/diegosouzapw/OmniRoute/pull/12884)) — thanks @RaviTharuma +- **fix(ci):** clear two base-reds on release/v3.8.51 — mutation-coverage gate + image-only-model guard ([#12945](https://github.com/diegosouzapw/OmniRoute/pull/12945)) +- **fix(api):** scope batch bulk-delete to the calling API key ([#13211](https://github.com/diegosouzapw/OmniRoute/pull/13211)) +- **fix(tests):** align model-sync log assertion to shipped path redaction (base-red) ([#13227](https://github.com/diegosouzapw/OmniRoute/pull/13227)) +- **fix(test):** make the ToS heading guard actually require the parentheses ([#13228](https://github.com/diegosouzapw/OmniRoute/pull/13228)) +- **fix(quality):** register 4 drifted covering tests in stryker tap.testFiles ([#13229](https://github.com/diegosouzapw/OmniRoute/pull/13229)) +- **fix(logging):** mask *-api-key headers in request-log pipeline (#13273) ([#13401](https://github.com/diegosouzapw/OmniRoute/pull/13401)) — thanks @KooshaPari +- **fix(tests):** correct stale Jina catalog prefix in custom-models test ([#13403](https://github.com/diegosouzapw/OmniRoute/pull/13403)) — thanks @KooshaPari +- **fix(backend):** prune old backups after health-check-repair snapshots (#13308) ([#13404](https://github.com/diegosouzapw/OmniRoute/pull/13404)) — thanks @KooshaPari +- **fix(backend):** guard memory_fts_au trigger with WHEN clause to stop FTS bloat (#13326) ([#13405](https://github.com/diegosouzapw/OmniRoute/pull/13405)) — thanks @KooshaPari +- **fix(free-tiers):** remove obsolete Together $25 signup credit (#12526) ([#13407](https://github.com/diegosouzapw/OmniRoute/pull/13407)) — thanks @KooshaPari +- **fix:** skip non-chat generation models in Test all models ([#13409](https://github.com/diegosouzapw/OmniRoute/pull/13409)) — thanks @KooshaPari +- **fix(providers):** de-duplicate agy and antigravity model catalogs ([#13410](https://github.com/diegosouzapw/OmniRoute/pull/13410)) — thanks @KooshaPari +- **fix(dashboard):** send provider in body for mini-playground web search ([#13411](https://github.com/diegosouzapw/OmniRoute/pull/13411)) — thanks @KooshaPari +- **fix(resilience):** add CJK quota-exhaustion patterns to 429 classifier ([#13412](https://github.com/diegosouzapw/OmniRoute/pull/13412)) — thanks @KooshaPari +- **fix(backend):** eval runner: failed calls no longer score as passed + dotAll regex ([#13413](https://github.com/diegosouzapw/OmniRoute/pull/13413)) — thanks @KooshaPari +- **fix(backend):** eval runner opts out of output-style and memory injection ([#13414](https://github.com/diegosouzapw/OmniRoute/pull/13414)) — thanks @KooshaPari +- **fix:** chain serial quarantine step in npm test script (fixes #13286) ([#13418](https://github.com/diegosouzapw/OmniRoute/pull/13418)) — thanks @KooshaPari +- **fix(antigravity):** derive stable session ID from credentials for prompt-cache reuse (#13305) ([#13424](https://github.com/diegosouzapw/OmniRoute/pull/13424)) — thanks @KooshaPari +- **fix(memory):** merge memory into top-level system field instead of unshifting at messages[0] (#13425) ([#13427](https://github.com/diegosouzapw/OmniRoute/pull/13427)) — thanks @KooshaPari +- **fix(compression):** preserve instruction blocks from lossy engines ([#13523](https://github.com/diegosouzapw/OmniRoute/pull/13523)) — thanks @KooshaPari +- **fix(compression):** preserve polarity words and newlines in ultra heuristic ([#13524](https://github.com/diegosouzapw/OmniRoute/pull/13524)) — thanks @KooshaPari +- **fix(compression):** run user preservePatterns before built-in patterns (#13457) ([#13525](https://github.com/diegosouzapw/OmniRoute/pull/13525)) — thanks @KooshaPari +- **fix(reasoning):** expand deepseek-v4 regex to match v4.1+ and add command-code provider (#13430) ([#13528](https://github.com/diegosouzapw/OmniRoute/pull/13528)) — thanks @KooshaPari +- **fix(providers):** add alwaysReasons metadata for reka-flash-3 ([#13531](https://github.com/diegosouzapw/OmniRoute/pull/13531)) — thanks @KooshaPari +- **fix(playground):** allow Compare columns to scroll independently ([#13532](https://github.com/diegosouzapw/OmniRoute/pull/13532)) — thanks @KooshaPari +- **fix(security):** empty whitelist allows through to prevent admin lockout ([#13534](https://github.com/diegosouzapw/OmniRoute/pull/13534)) — thanks @KooshaPari +- **fix(tests):** add DISABLE_IOREG_STRATEGY env var for macOS test compatibility (fixes #13467) ([#13539](https://github.com/diegosouzapw/OmniRoute/pull/13539)) — thanks @KooshaPari +- **fix(zed-hosted):** map developer role and Gemini enums for Zed proxy ([#13543](https://github.com/diegosouzapw/OmniRoute/pull/13543)) — thanks @KooshaPari +- **fix(analytics):** normalize provider aliases to canonical id in usage_history ([#13545](https://github.com/diegosouzapw/OmniRoute/pull/13545)) — thanks @KooshaPari +- **fix(combo):** use traceId for call log id to prevent UNIQUE constraint failure ([#13546](https://github.com/diegosouzapw/OmniRoute/pull/13546)) — thanks @KooshaPari +- **fix(auth):** check model lockout before returning synthetic noauth connection ([#13547](https://github.com/diegosouzapw/OmniRoute/pull/13547)) — thanks @KooshaPari +- **fix(guardrails):** skip credential redaction for base64 image data URLs ([#13550](https://github.com/diegosouzapw/OmniRoute/pull/13550)) — thanks @KooshaPari +- **fix(resilience):** increase requestQueue.maxWaitMs default from 15s to 30s ([#13553](https://github.com/diegosouzapw/OmniRoute/pull/13553)) — thanks @KooshaPari +- **fix(docs):** document OMNIROUTE_READY_TIMEOUT_MS and allowlist the test-only DISABLE_IOREG_STRATEGY ([#13692](https://github.com/diegosouzapw/OmniRoute/pull/13692)) + ### 📝 Maintenance ---- +- **docs(readme):** reconcile live v3.8.50 provider, free-tier, CLI, routing, test, community, sponsor, acknowledgment, and SVG metrics with their audited source denominators, including a deduplicated OmniRoute-in-Action snapshot and distinct contributor rankings for merged pull requests, GitHub-attributed commits, and Git history ([#11356](https://github.com/diegosouzapw/OmniRoute/pull/11356)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **docs:** sync the canonical provider count 354 → 356 across `README.md`, `AGENTS.md`, `llm.txt` (+ 42 i18n mirrors), the four README SVG diagrams, `docs/reference/PROVIDER_REFERENCE.md` (regenerated) and the `package.json` description after Opper (#11629) and 1min.ai (#11631) boarded the catalog — closes the `check:docs-counts-sync` strict drifts that kept `release/v3.8.51` red ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11673](https://github.com/diegosouzapw/OmniRoute/pull/11673)) — thanks @jonlwheat2-gif +- **test(providers):** update count-derived assertions after the v3.8.51 provider additions — `APIKEY_PROVIDERS` 233 → 235 (Opper #11629 + 1min.ai #11631), reserved-prefix REGISTRY walk 395 → 398, `WEB_FETCH_PROVIDERS` now includes `nimble-search` (#11620), and the provider translate-path golden snapshot regenerated ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11674](https://github.com/diegosouzapw/OmniRoute/pull/11674)) — thanks @jonlwheat2-gif +- **test(sse):** bump the hard-lease connection-query inventory for `src/lib/tokenHealthCheck.ts` to 2 — the verify-only web-cookie sweep added by #11495 split the single `getProviderConnections` call into oauth + cookie variants, which the frozen inventory had not tracked ([#11495](https://github.com/diegosouzapw/OmniRoute/pull/11495)). ([#11677](https://github.com/diegosouzapw/OmniRoute/pull/11677)) — thanks @jonlwheat2-gif / @oyi77 +- **chore(release):** clear the `release/v3.8.51` base-red gates — docs migration count (+38 i18n mirrors), stryker `tap.testFiles`, lockfile registry host, stale ESLint suppressions, the 7 lint regressions they were hiding, the stale `APIKEY_PROVIDERS` partition count (231 → 233), the `glm-5.3-max` tier missing from the GLM test table, a dead `modelType === "chat"` comparison in the models catalog, and the `resolveLiveWsUrl` / `sanitizeLiveWsPort` helpers from #11388 that a later merge dropped from `wsPath.ts` (dashboard live WebSocket URL resolution) ([#11502](https://github.com/diegosouzapw/OmniRoute/pull/11502)) — thanks @MumuTW +- **chore(free-models):** the free-tier regime table now declares which totals figure each regime feeds and whether it may take the no-auth shortcut, so the sets that used to repeat those answers by hand are derived from it — a new `freeType` no longer compiles until it has answered every question, instead of silently contributing to no total ([#11537](https://github.com/diegosouzapw/OmniRoute/pull/11537)) — thanks @maxmad64bis +- **docs(routing):** LKGP strategy copy now states provider-level stickiness and the explicit rules fallback instead of promising per-target pinning ([#11555](https://github.com/diegosouzapw/OmniRoute/pull/11555)) — thanks @maxmad64bis +- **test(chatcore):** pin the response tool-name alias resolution against the ordering hazard that broke every Gemini/Antigravity MCP tool call in v3.8.49. `extractRequestToolIdentityMap` deletes `translatedBody._toolNameMap`, so the later read is always undefined and the ledger survives only through the `requestToolIdentityMap` fallback — removing that fallback previously left the entire tool-name suite green. The resolution moves into `resolveResponseToolNameMap()` next to the map it depends on, with a regression guard that fails without the recovery. No behaviour change. ([#11575](https://github.com/diegosouzapw/OmniRoute/pull/11575)) — thanks @amartinawi +- **docs(env):** document `OMNIROUTE_CORPUS_CACHE_SIZE` in `.env.example` and `ENVIRONMENT.md`, so the env/docs contract check stops failing Docs Gates on every branch ([#11576](https://github.com/diegosouzapw/OmniRoute/pull/11576)) — thanks @ntdatt812 +- **chore(stryker):** register `route-guard-tunnel-processes-local-only` and `universal-quota-aware-routing` in `tap.testFiles`, so their mutant kills count for the three modules they cover ([#11577](https://github.com/diegosouzapw/OmniRoute/pull/11577)) — thanks @ntdatt812 +- **test(authz):** pin `LOCAL_ONLY_API_GET_EXEMPTIONS` by exact membership instead of by entry count, so the guard names the offending path and also catches a substitution ([#11580](https://github.com/diegosouzapw/OmniRoute/pull/11580)) — thanks @ntdatt812 +- **test(build):** pin `resolveNextBuildBundlerFlag` to the `OMNIROUTE_USE_TURBOPACK` contract it implements, across runtimes, instead of asserting an unimplemented Bun override ([#11583](https://github.com/diegosouzapw/OmniRoute/pull/11583)) — thanks @ntdatt812 +- **test(claude-web):** the Claude Web transport test's barrel anchors no longer pin the exact construction spelling. #11421 made the executor map lazy, so `/"claude-web": new ClaudeWebExecutor\(\)/` stopped matching and the test went red on `release/v3.8.51` itself — a base-red gate every open PR inherited. The anchors now match the alias key and the class name and tolerate either spelling in between, and were checked to still fail when an alias is genuinely repointed at another executor ([#11585](https://github.com/diegosouzapw/OmniRoute/pull/11585)) — thanks @ntdat812 +- **test(models):** pin `qwen3.8-max` resolution per provider — two of the four catalogs now serve the bare id and no longer carry `-preview` ([#11590](https://github.com/diegosouzapw/OmniRoute/pull/11590)) — thanks @ntdatt812 +- **test(executors):** drop the stale 128-tool truncation assertion that contradicted the #11444 pass-through contract ([#11591](https://github.com/diegosouzapw/OmniRoute/pull/11591)) — thanks @ntdatt812 +- **test(token-health):** pin the current terminal-skip boundary — expired with retry budget is probed, `account_deactivated` stays skipped ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)) — thanks @ntdatt812 +- **test(models):** follow #11503's retarget of the `gemini-3-pro-high` alias to the hyphenated catalog id ([#11593](https://github.com/diegosouzapw/OmniRoute/pull/11593)) — thanks @ntdatt812 +- **refactor(video bridge):** extract per-video acquisition, whole-result caching, description, and metrics/abort/cleanup out of `VideoBridgeGuardrail.preCall` into a `processVideoPart` seam in a new `videoBridgePipeline.ts`, behind explicit `VideoMediaBrokerPort`, `VideoAudioTranscriptionPort`, and `VideoDrilldownPort` boundaries; `preCall` now only handles request traversal, policy, and response aggregation. The Video tab's FFmpeg/ffprobe runtime status is now an explicit `unknown` / `restricted` / `unavailable` / `available` state instead of a nullable boolean pair, fixing a case where an in-flight or failed probe was mislabeled as "install FFmpeg" ([#11657](https://github.com/diegosouzapw/OmniRoute/issues/11657)). ([#12016](https://github.com/diegosouzapw/OmniRoute/pull/12016)) +- **test(opencode-plugin):** add unit test coverage for `formatFreeBudget()` naming helper ([#11660](https://github.com/diegosouzapw/OmniRoute/pull/11660)) — thanks @f9td56dbgh-hub +- **docs(video):** clarify that the Video Bridge transcript `source` field (`client`, `embedded`, `audio-bridge`) is presently caller-declared and not yet server-verified — OmniRoute enforces the enum shape but does not cryptographically confirm that an `embedded`/`audio-bridge` label came from a server-owned extraction ([#11661](https://github.com/diegosouzapw/OmniRoute/issues/11661)). ([#12001](https://github.com/diegosouzapw/OmniRoute/pull/12001)) +- **chore(providers):** retire the distributed Raycast Relay and Hailuo Web integrations after a provenance audit confirmed substantial derivation from GPL-3.0 sources; the official GitHub and MiniMax API providers remain available. Migration 163 disables legacy connections and invalidates their active leases while preserving connection IDs, audit/history rows, quota snapshots, usage records, and fail-closed API-key connection policies. ([#11691](https://github.com/diegosouzapw/OmniRoute/pull/11691)) +- **chore(providers):** retire the Felo Web provider while its GPL-derived provenance remains on licensing HOLD ([#11698](https://github.com/diegosouzapw/OmniRoute/pull/11698)) — stale runtime and database references now fail closed while existing connection, lease, and usage history remains preserved for auditability. +- **fix(sse):** retire the Gemini Web image-generation bridge while provenance and licensing remain on HOLD, preserving Gemini Web chat/auth/tools and legitimate Gemini/Nano Banana image providers ([#11708](https://github.com/diegosouzapw/OmniRoute/pull/11708)) +- **chore(audio):** retire the built-in EdgeTTS provider and its 11-voice catalog while provenance/licensing review remains on HOLD; the generic `/v1/audio/speech` endpoint and the other speech providers remain available ([#11711](https://github.com/diegosouzapw/OmniRoute/pull/11711)) +- **fix(providers):** retire the unofficial Qwen Web integration and its `qwen-web`/`qw` identities while third-party provenance remains on HOLD ([#11713](https://github.com/diegosouzapw/OmniRoute/pull/11713)) — direct requests now fail closed with a sanitized HTTP 410 and combos skip the retired target, while migration guards preserve connection IDs, allowlists, usage/audit history and invalidate active leases. Supported Qwen API, Qwen Code, cloud, token-plan, audio, local and model-name surfaces remain available; restoring Qwen Web requires a qualified provenance/license decision. +- **fix(providers):** retire `microsoft-designer-web` and its `msdesigner` alias from runtime and catalogs while preserving stored credentials, provider history and allowlists as inactive records — source dependency on gpt4free's [`MicrosoftDesigner.py`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/g4f/Provider/needs_auth/MicrosoftDesigner.py) is proven at snapshot `0943d16eef757a44deb60651c07bbe5fa958cafe`, whose [`LICENSE`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/LICENSE) is GPL-3.0; expressive adaptation is probable, which records provenance/licensing risk without asserting a legal conclusion ([#11720](https://github.com/diegosouzapw/OmniRoute/pull/11720)). +- **docs(legal):** centralize pinned MIT notices for the vendored GCF codec and local flag assets ([#11726](https://github.com/diegosouzapw/OmniRoute/pull/11726)) +- Split the 2,952-line `adobeFireflyClient.ts` into nine focused modules (catalog, credentials, payload, ARP session, upload, responses, IMS, account, poll) behind an unchanged barrel — no consumer edits and no public API change (#11728). — thanks @MumuTW +- **fix(dashboard):** replaced the Model Lockout card's unprovenanced notification MP3 with a short, locally synthesized Web Audio chime ([#11731](https://github.com/diegosouzapw/OmniRoute/pull/11731)) — both toggles retain optional audible feedback without shipping replacement media or third-party code, and unsupported, suspended, or rejected audio contexts fail open without blocking the setting change. +- **chore(deepseek-web):** remove the provenance-unresolved DeepSeek PoW WASM binary and its runtime loader/tracing while retaining the existing JavaScript solver (slower at high difficulty) ([#11732](https://github.com/diegosouzapw/OmniRoute/pull/11732)). +- **chore(assets):** remove four unused provider assets whose redistribution provenance could not be verified while retaining the active Freebuff SVG ([#11735](https://github.com/diegosouzapw/OmniRoute/pull/11735)) +- **docs(assets):** retired two raster images whose redistribution provenance could not be verified ([#11736](https://github.com/diegosouzapw/OmniRoute/pull/11736)) — PicoClaw documentation now reuses the existing SVG, while Zoo Code remains a text link without an image. +- **docs(legal):** add pinned notices for six LobeHub-derived provider SVGs and 65 byte-exact theSVG assets, retaining registry claims as unverified metadata with explicit trademark and HOLD boundaries ([#11737](https://github.com/diegosouzapw/OmniRoute/pull/11737)). +- **chore(quality):** added a versioned 225/225 provider-asset provenance manifest and blocking drift gate ([#11743](https://github.com/diegosouzapw/OmniRoute/pull/11743)) — the inventory pins magic MIME and SHA-256 for every physical file, records immutable source matches for 71 assets, keeps 69 probable and 85 unresolved entries explicit but non-blocking, and treats provenance as source evidence rather than copyright or trademark clearance. +- **refactor(mitm):** replace four previously derived Traffic Inspector utilities — conversation normalization, SSE merging, secret masking, and header sanitization — with independent clean-room implementations based on public provider schemas, WHATWG SSE, and RFC 6750, while keeping the two independently classified passthrough surfaces unchanged ([#11748](https://github.com/diegosouzapw/OmniRoute/pull/11748)). +- Added a unit test that fails seven days before any dated pack under `config/` (`validUntil` and sibling keys) lapses, naming the file and key. The Alibaba free-tier pack expired on 2026-08-27 and turned every PR red the next morning with no commit involved; renewal now happens on someone's terms, not the clock's. ([#11891](https://github.com/diegosouzapw/OmniRoute/pull/11891)) +- `check:workflows` now fails (under `--strict`/`--ratchet`) when any job routed to a self-hosted runner publishes with `--provenance` — npm rejects that with `422` at the registry, which in v3.8.50 only surfaced after the tag and Docker images were public. ([#11895](https://github.com/diegosouzapw/OmniRoute/pull/11895)) +- `scripts/ops/runner-janitor.sh` now proves a path is idle with one `lsof` snapshot and removes stale leftovers itself (tmpfs after 3 h — it is RAM — disk after 24 h), kills orphan `next-build` processes, prunes checkouts of stopped runners, and alerts on memory pressure; `--dry-run` shows exactly what it would do. `docs/ops/RUNNER_BOX.md` reconciled to the measured box (31 GB, 10 listeners). ([#11893](https://github.com/diegosouzapw/OmniRoute/pull/11893)) +- The `next-build` artefact (1.3 GB) is now written and read under `$RUNNER_TEMP` (per-runner, on disk) instead of `/tmp`, which on the self-hosted pool is a 12 GB tmpfs in RAM. Landing it there took 27–32 of the publish job's 76 minutes, and the fixed `/tmp/e2e-build.tar.gz` name let E2E jobs on different runners overwrite each other's download. ([#11896](https://github.com/diegosouzapw/OmniRoute/pull/11896)) +- The CI `build` job now runs in two concurrency lanes — `main` and pull requests — so a release build is never queued behind (or OOM-killed beside) PR builds on the self-hosted pool, which holds one `next-build` comfortably and two at the edge. ([#11901](https://github.com/diegosouzapw/OmniRoute/pull/11901)) +- Re-freeze the ESLint suppressions on `release/v3.8.51` from a clean-room measurement (2 stale file entries pruned, 55 pre-existing `no-explicit-any` in six new files frozen under #11924) and drop the dead `GPT_SIZE_MAP` constant orphaned by the Adobe Firefly client split, so `No new ESLint warnings` stops failing every PR with exit 2 (Refs #11924) ([#11955](https://github.com/diegosouzapw/OmniRoute/pull/11955)) +- Type the 55 `no-explicit-any` sites that had been frozen under #11924 — four redundant casts in `socksConnectorWithFamily.ts` (undici/socks types already accept them) and the mocks/fixtures of the socks-timeout and isFree suites — and drop their suppression entries; the ESLint ratchet shrinks from 5487 to 5432 (Closes #11924) ([#11975](https://github.com/diegosouzapw/OmniRoute/pull/11975)) +- Take the two hosted-runner builds off the PR rail: `Build App` (`build.yml`) is `workflow_dispatch`-only and `DAST smoke (PR)` runs only for PRs into `main` — the 7 GB hosted VM cannot build `release/v3.8.51` in any profile (19/30 red, VM shutdown ~8 min into `next build`) and both checks had turned into permanent noise on every release PR; the bundle stays validated by `ci.yml` on `main` and by `nightly-release-green` on `release/**` (Closes #11946) ([#11962](https://github.com/diegosouzapw/OmniRoute/pull/11962)) +- Move the four nightly jobs that build the backend (`nightly-schemathesis`, `nightly-llm-security` promptfoo + garak, `nightly-resilience` axe-a11y) off the hosted 7 GB runner — where they died on `release/v3.8.51` unseen — onto the box's new `omni-light` pool (two listeners), and document the reshaped fleet (4 active OmniRoute listeners: 2 `omni-build` + 2 `omni-light`, janitor ceiling 4) (Closes #11965) ([#11967](https://github.com/diegosouzapw/OmniRoute/pull/11967)) +- Stop painting every fork PR into `release/**` red: `quality.yml` `Build (advisory)` is skipped (GitHub still reports `continue-on-error` failures as check FAILURE). Hosted `ubuntu-latest` cannot finish `npm run build` on this tree — same class as #11962 taking `build.yml` off the PR rail. `docker-publish.yml` amd64 now runs on the `.113` `omni-build` pool (31 GB / 32 cores, two listeners) with Turbopack, shares the `heavy-build-main` lane with `ci.yml` `Build` so it queues instead of becoming a third heavy, and keeps per-ref concurrency (a merge storm was starting 8 concurrent OOM builds). arm64 stays on `ubuntu-24.04-arm` with webpack — there is no ARM box. Fallback when `USE_VPS_RUNNER` is off: hosted amd64 + webpack (#11976). ([#12021](https://github.com/diegosouzapw/OmniRoute/pull/12021)) +- `docker-publish.yml` builds with webpack (`OMNIROUTE_USE_TURBOPACK=0`) on **both** arches, including amd64 on the `.113` `omni-build` pool. Turbopack had the RAM (31 GB, `omniroute-113-6`) and still panicked (`TurbopackInternalError: there must be a path to a root`, run 33253576569, 26 min in). The same tree's arm64 webpack build on hosted `ubuntu-24.04-arm` succeeded (run 33264823398). Dockerfile already documents webpack as the Docker escape hatch (#11976). ([#12050](https://github.com/diegosouzapw/OmniRoute/pull/12050)) +- **docs(free-tier):** declare the counting vs deciding regimes for "is it free?" and guard the deciding path from DB-backed catalog resolution ([#12226](https://github.com/diegosouzapw/OmniRoute/pull/12226)) — thanks @maxmad64bis +- **docs(auto-combo):** The mode pack table in `docs/routing/AUTO-COMBO.md` now lists all six shipped packs with every weight each one sets, replacing a four-pack table whose numbers had also drifted from the source. It states plainly that no pack sets `quality`, so selecting any pack silences the observed-quality signal. Six more documents that quote the scoring factor count joined the `check:docs-counts` gate, which caught five stale claims — including one naming nine factors that do not exist — and two stale mode pack counts. The dashboard routing panel, which offered four of the six packs and labelled the default strategy "6-Factor Scoring", is now covered by a test; the two packs it was missing are `reliability-first` and `chaos-mode`, the latter labelled as the fault-injection profile it is rather than as one more routing preference ([#12316](https://github.com/diegosouzapw/OmniRoute/pull/12316)) — thanks @maxmad64bis +- **chore(quality):** rebaseline `src/lib/db/apiKeys.ts` for the ACL the key-creation path now preserves ([#12352](https://github.com/diegosouzapw/OmniRoute/pull/12352)) ([#12673](https://github.com/diegosouzapw/OmniRoute/pull/12673)) — thanks @KrzysiekSko +- **chore(providers):** bump the Claude Code wire identity and the Devin bridge image pin from `2.1.220` to `2.1.258` ([#12402](https://github.com/diegosouzapw/OmniRoute/pull/12402)) — thanks @ggiak ([#12604](https://github.com/diegosouzapw/OmniRoute/pull/12604)) +- **docs(env):** align `.env.example`, the README Bun section, and the troubleshooting guide with the code: `OMNIROUTE_USE_TURBOPACK` also governs `npm run build` (not dev-only), `bun run build` follows that flag instead of auto-selecting Webpack, `OMNIROUTE_CHAT_MAX_HEAVY_IN_FLIGHT` is unset by default (no request-count cap), and the structural `503 chat_admission_busy` message matches `chatAdmissionResponses.ts` (#12404 — thanks @pacocartones) +- **chore(electron):** upgrade the desktop app to Electron 44 (Chromium 152, Node 24.18.1) ([#12217](https://github.com/diegosouzapw/OmniRoute/pull/12217)). **Requires macOS 13 (Ventura) or later** — Chromium dropped macOS 12 (Monterey), so Monterey users must stay on an earlier OmniRoute desktop build. Windows and Linux are unaffected; the app already shipped only x64/arm64, so Electron 44 dropping 32-bit builds changes nothing. Removes the `openAsHidden`/`wasOpenedAsHidden` login-item fields deleted in Electron 44 — hidden autostart continues to work through the `--hidden` argument registered with the login item ([#12554](https://github.com/diegosouzapw/OmniRoute/pull/12554)) — thanks @app/dependabot +- **chore(quality):** rebaseline `src/sse/handlers/chat.ts` for the effective-input persistence the continuation fix needs ([#12641](https://github.com/diegosouzapw/OmniRoute/pull/12641)) ([#12680](https://github.com/diegosouzapw/OmniRoute/pull/12680)) — thanks @hartmark +- `Coverage` job on `ci.yml`: the informational Codecov upload gets its own 5-minute ceiling and `continue-on-error`, and the job budget grows from 20 to 30 minutes (the 8-shard c8 merge alone takes ~10) — a stalled upload no longer ends the job `cancelled` and drags a fully green `main` run's conclusion down with it ([#11972](https://github.com/diegosouzapw/OmniRoute/pull/11972)) +- CI hardening on the PR rail: the four `Unit Tests fast-path` shards get `timeout-minutes: 30` (a hung shard held a PR for 64 min instead of GitHub's 6 h default) and both ESLint file caches lose their `restore-keys` fallback, so a cache built under another suppressions file or lint config can no longer report stale verdicts (Refs #11600, #11924) ([#11963](https://github.com/diegosouzapw/OmniRoute/pull/11963)) +- Every CI job that runs a `next build` (`build`, the npm `publish`, both release-green validations) now targets the `omni-build` runner label, which only two of the eight self-hosted runners carry. The box holds one build comfortably and two at the edge; a third now queues on GitHub instead of being OOM-killed by the kernel. ([#11932](https://github.com/diegosouzapw/OmniRoute/pull/11932)) +- **refactor(combo):** move `handleRoundRobinCombo` (and `resolveTargetTokenLimit`) into `open-sse/services/combo/roundRobinCombo.ts`. `combo.ts` drops from 2164 to 1014 lines (`split("\n").length`); the leaf is 1199 (under the 1200 new-file cap). The round-robin call site uses a dynamic `import()` so `releaseStickyPinOnFailure` / `clearStaleLKGP` can stay exported from `combo.ts` without a static cycle. Skip / sticky / semaphore / safety-timer behavior is unchanged. ([#12811](https://github.com/diegosouzapw/OmniRoute/pull/12811)) — thanks @HouMinXi +- **chore(quality):** rebaseline the file-size caps the error-boundary campaign grew past (`open-sse/executors/codex.ts`, `open-sse/vendor/codex-chatgpt-web/bridge.ts`, both via [#12444](https://github.com/diegosouzapw/OmniRoute/pull/12444)) ([#12654](https://github.com/diegosouzapw/OmniRoute/pull/12654)) +- **chore(quality):** rebaseline the file-size caps the hartmark batch grew past (`combos/page.tsx` via [#12355](https://github.com/diegosouzapw/OmniRoute/pull/12355), `open-sse/services/combo.ts` via [#12338](https://github.com/diegosouzapw/OmniRoute/pull/12338)) ([#12623](https://github.com/diegosouzapw/OmniRoute/pull/12623)) — thanks @hartmark +- **chore(quality):** rebaseline the file-size caps the HouMinXi batch grew past when its PRs stacked (`providers/page.tsx`, `chatCore.ts`, `accountFallback.ts`) — each PR measured correctly in isolation, none saw the stacking ([#12619](https://github.com/diegosouzapw/OmniRoute/pull/12619)) +- **chore(quality):** rebaseline `open-sse/services/combo.ts` for the reset-aware scoring the HouMinXi batch stacked ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) ([#12678](https://github.com/diegosouzapw/OmniRoute/pull/12678)) — thanks @HouMinXi +- **docs:** sync provider catalog count (direct commit `4f11b2ae3d`) +- **docs(skills):** sync omni-inference search fallback (direct commit `b4e76a7ed9`) +- **chore(lint):** prune two stale base suppressions (direct commit `1325047d56`) +- **test(auto-combo):** tolerate full-suite rotation load (direct commit `ad3e293f9f`) +- **test:** drain inherited quality snapshot reds (direct commit `21ed68d8ac`) +- **chore(quality):** rebaseline measured CLI bundle growth (direct commit `d35b3f9779`) +- **test(ui):** reproduce invalid AnySearch icon alias (direct commit `12cc6ca834`) +- **test(translator):** fix the relative imports of the relocated deferred-finish test (direct commit `777d9d1629`) +- **docs(counts):** bring the documented provider and migration counts to the measured values (direct commit `e70bea30e9`) +- **test(security):** drop the guard-target set left unused by the sync (direct commit `a32ba0e362`) +- **chore(db):** renumber model_capabilities migration 163 → 169 (cross-branch collision with 163_radar_feed_cache_generated_at) (direct commit `70f5d4cbf9`) +- **docs(cli):** document CLI_PRIME_AGENT_BIN and correct the CLI Agents count (direct commit `9058e39b61`) +- **chore(deps):** refresh runtimes and adopt ESLint 10 ([#11259](https://github.com/diegosouzapw/OmniRoute/pull/11259)) — thanks @backryun +- **refactor(local-corpus):** implement dynamic root resolution and LRU cache ([#11491](https://github.com/diegosouzapw/OmniRoute/pull/11491)) — thanks @raheemuddin786 +- **chore(quality):** prune stale eslint-suppressions entries ([#11596](https://github.com/diegosouzapw/OmniRoute/pull/11596)) +- **chore(ci):** unblock quality gates - env-doc sync + stryker tap.testFiles registration (#9282) ([#11609](https://github.com/diegosouzapw/OmniRoute/pull/11609)) — thanks @Meet6338-X +- **chore(ci):** point the circular-deps gate at dpdm's real JS entrypoint ([#11615](https://github.com/diegosouzapw/OmniRoute/pull/11615)) — thanks @benzntech +- **chore(quality):** rebaseline file-size for #11448/#11495/#11561 growth ([#11630](https://github.com/diegosouzapw/OmniRoute/pull/11630)) +- **deps:** realign onnxruntime-node pin to @huggingface/transformers' 1.24.3 lockstep ([#11633](https://github.com/diegosouzapw/OmniRoute/pull/11633)) — thanks @jonlwheat2-gif +- **test(autoCombo):** port models_dev_tier #11508 guard from node:test to vitest ([#11635](https://github.com/diegosouzapw/OmniRoute/pull/11635)) — thanks @jonlwheat2-gif +- **chore(quality):** rebaseline file-size for #11083/#11631/#11628 growth ([#11636](https://github.com/diegosouzapw/OmniRoute/pull/11636)) +- **chore(lint):** prune stale ESLint suppressions (base-red #11449) ([#11644](https://github.com/diegosouzapw/OmniRoute/pull/11644)) — thanks @jonlwheat2-gif +- **docs(changelog):** aggregate the ten v3.8.50 fragments into CHANGELOG.md ([#11683](https://github.com/diegosouzapw/OmniRoute/pull/11683)) +- **chore(search):** drop unreachable credential sweep after #11642 ([#11687](https://github.com/diegosouzapw/OmniRoute/pull/11687)) — thanks @HouMinXi +- **ci:** add API route TypeScript regression gate ([#11705](https://github.com/diegosouzapw/OmniRoute/pull/11705)) — thanks @Sabeekhann +- **docs(dedup):** correct the CodeQL claim on the tenant-namespace comment ([#11712](https://github.com/diegosouzapw/OmniRoute/pull/11712)) +- **docs(changelog):** add consolidated v3.8.50 stats, top-25 ranking and a .mailmap ([#11715](https://github.com/diegosouzapw/OmniRoute/pull/11715)) +- **docs:** recommend gstack for AI-assisted workflows ([#11770](https://github.com/diegosouzapw/OmniRoute/pull/11770)) — thanks @santosraju99-hub +- **chore(quality):** tighten complexity/cognitive-complexity ratchets to the current tip; land the missed gateways.ts rebaseline ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **docs(i18n):** finish Freepik → Magnific rebrand in locale strings and README ([#11772](https://github.com/diegosouzapw/OmniRoute/pull/11772)) — thanks @turbolego +- **refactor(auto-combo):** fix divergent scoring in combo health reporting ([#11854](https://github.com/diegosouzapw/OmniRoute/pull/11854)) — thanks @jacobsparts +- **chore(changelog):** add missing fragments for #11919/#11918/#11916 ([#11938](https://github.com/diegosouzapw/OmniRoute/pull/11938)) +- **test(check):** escape the runs-on fixture with JSON.stringify, not a quote-only replace ([#11942](https://github.com/diegosouzapw/OmniRoute/pull/11942)) (direct commit `fb7445eaa3`) +- **test(release):** align five suites with the contracts #11933, #11919 and #11876 shipped on release/v3.8.51 ([#11944](https://github.com/diegosouzapw/OmniRoute/pull/11944)) +- **chore(quality):** remove unreachable code and restore test discovery ([#11950](https://github.com/diegosouzapw/OmniRoute/pull/11950)) — thanks @backryun +- **docs(agents):** sync-back landings are fast-forward, never squash ([#11964](https://github.com/diegosouzapw/OmniRoute/pull/11964)) +- **test(infra):** retry recursive temp-dir removal instead of failing a shard on ENOTEMPTY (#11966) ([#11968](https://github.com/diegosouzapw/OmniRoute/pull/11968)) +- **docs(ops):** the .113 heavy-build ceiling is one runner, not two ([#12048](https://github.com/diegosouzapw/OmniRoute/pull/12048)) +- **test(ui):** correct inactive auto-fetch expectation ([#12098](https://github.com/diegosouzapw/OmniRoute/pull/12098)) — thanks @RaviTharuma +- **docs:** document native dependency check escape hatch ([#12101](https://github.com/diegosouzapw/OmniRoute/pull/12101)) — thanks @RaviTharuma +- **test(providers):** regenerate the translate-path golden for OrcaRouter (#11923) ([#12118](https://github.com/diegosouzapw/OmniRoute/pull/12118)) +- **chore(quality):** velocity phase — loosen every numeric baseline by 20% until v4.0, monitor headroom nightly ([#12125](https://github.com/diegosouzapw/OmniRoute/pull/12125)) +- **chore(lint):** batch 0 of #12146 — type the call-log-cap sqlite rows instead of 45 as-any casts ([#12157](https://github.com/diegosouzapw/OmniRoute/pull/12157)) +- **chore(lint):** batch 4 of #12146 — shared/components react-hooks violations resolved ([#12159](https://github.com/diegosouzapw/OmniRoute/pull/12159)) +- **chore(lint):** batch 1 of #12146 — dashboard/cli-code react-hooks violations resolved ([#12160](https://github.com/diegosouzapw/OmniRoute/pull/12160)) +- **chore(lint):** batch 3 of #12146 — dashboard/settings react-hooks violations resolved ([#12162](https://github.com/diegosouzapw/OmniRoute/pull/12162)) +- **chore(lint):** batch 2 of #12146 — resolve the react-hooks compiler violations in dashboard/providers ([#12163](https://github.com/diegosouzapw/OmniRoute/pull/12163)) +- **chore(quality):** register search-432 cooldown test in stryker tap.testFiles ([#12170](https://github.com/diegosouzapw/OmniRoute/pull/12170)) +- **chore(lint):** batch 5 of #12146 — combos, endpoint, provider-stats, api-manager and costs react-hooks violations resolved ([#12174](https://github.com/diegosouzapw/OmniRoute/pull/12174)) +- **build:** add contributor fast profile ([#12192](https://github.com/diegosouzapw/OmniRoute/pull/12192)) — thanks @rafacpti23 +- **docs(readme):** README touch-ups ([#12193](https://github.com/diegosouzapw/OmniRoute/pull/12193)) ([#12194](https://github.com/diegosouzapw/OmniRoute/pull/12194)) ([#12202](https://github.com/diegosouzapw/OmniRoute/pull/12202)) — thanks @rafacpti23 +- **build:** force the webpack bundler in the contributor build profile ([#12197](https://github.com/diegosouzapw/OmniRoute/pull/12197)) — thanks @rafacpti23 +- **build:** skip standalone packaging in the contributor profile ([#12198](https://github.com/diegosouzapw/OmniRoute/pull/12198)) — thanks @rafacpti23 +- **docs(audit):** align every published number with the code and harden check:docs-counts ([#12200](https://github.com/diegosouzapw/OmniRoute/pull/12200)) +- **build:** stub the instrumentation entrypoints in the contributor profile ([#12203](https://github.com/diegosouzapw/OmniRoute/pull/12203)) — thanks @rafacpti23 +- **build:** omit the standalone output target for contributor builds ([#12204](https://github.com/diegosouzapw/OmniRoute/pull/12204)) — thanks @rafacpti23 +- **chore(lint):** batch 6 of #12146 — memory, radar, audit, analytics, cache, usage, activity, home and RequestLoggerV2 react-hooks violations resolved ([#12208](https://github.com/diegosouzapw/OmniRoute/pull/12208)) +- **docs(diagrams):** rename number-carrying diagram files to stable names ([#12210](https://github.com/diegosouzapw/OmniRoute/pull/12210)) +- **docs(dashboard):** redraw onboarding tier-flow SVGs for the real 4-tier model ([#12211](https://github.com/diegosouzapw/OmniRoute/pull/12211)) +- **docs(api):** document every implemented route in openapi.yaml (276 → 692 paths) ([#12212](https://github.com/diegosouzapw/OmniRoute/pull/12212)) +- **chore(lint):** batch 7 of #12146 — final src tail: 37 react-hooks violations across 33 files resolved ([#12244](https://github.com/diegosouzapw/OmniRoute/pull/12244)) +- Revert "docs: recommend gstack for AI-assisted workflows (#11770)" ([#12248](https://github.com/diegosouzapw/OmniRoute/pull/12248)) ([#12249](https://github.com/diegosouzapw/OmniRoute/pull/12249)) +- **docs:** Chaos Mode setup guide + weighted strategy semantics ([#12250](https://github.com/diegosouzapw/OmniRoute/pull/12250)) +- **docs(agents):** protected-surface merge rule — operator approval for agent-instruction files ([#12253](https://github.com/diegosouzapw/OmniRoute/pull/12253)) +- **chore(quality):** register native-codex turn-pin tests in stryker tap.testFiles ([#12263](https://github.com/diegosouzapw/OmniRoute/pull/12263)) +- **chore(quality):** dedupe tap.testFiles entries added by racing base-red fixes ([#12265](https://github.com/diegosouzapw/OmniRoute/pull/12265)) +- **docs(readme):** introduce OmniRouteTray — the macOS menu-bar companion ([#12276](https://github.com/diegosouzapw/OmniRoute/pull/12276)) — thanks @ggiak +- **chore(proxy):** purge the legacy 1proxy residue — sync/rotator modules, dead DB exports, dead settings tab and flag, docs (#12091) ([#12290](https://github.com/diegosouzapw/OmniRoute/pull/12290)) +- **chore(quality):** baseline-headroom skips generated and vendored files in the fileSize worst-file signal ([#12291](https://github.com/diegosouzapw/OmniRoute/pull/12291)) +- **refactor(video):** unify the JPEG frame data-URI contract ([#12322](https://github.com/diegosouzapw/OmniRoute/pull/12322)) +- **chore(deps):** freeze onnxruntime-node and eslint-plugin-react-hooks out of dependabot groups ([#12329](https://github.com/diegosouzapw/OmniRoute/pull/12329)) +- **test(executors):** restore the #10986 reasoning-only fallback guards ([#12364](https://github.com/diegosouzapw/OmniRoute/pull/12364)) — thanks @pacocartones +- **chore(quality):** re-tighten the file-size ratchet to the real LOC (plan 3.8.52 task 0) ([#12411](https://github.com/diegosouzapw/OmniRoute/pull/12411)) +- **test(grok):** format web executor suite ([#12412](https://github.com/diegosouzapw/OmniRoute/pull/12412)) +- **chore(quality):** ratchet open-sse typecheck baseline to zero ([#12418](https://github.com/diegosouzapw/OmniRoute/pull/12418)) +- **test(build):** guard the artifact path policy arrays against duplicates ([#12422](https://github.com/diegosouzapw/OmniRoute/pull/12422)) +- **chore(lint):** adopt eslint-plugin-react-hooks 7.1.1 ([#12428](https://github.com/diegosouzapw/OmniRoute/pull/12428)) +- **chore(providers):** remove a keyless provider integration at its operator's request ([#12440](https://github.com/diegosouzapw/OmniRoute/pull/12440)) +- **docs(providers):** register providers removed at their operator's request and guard against reintroduction ([#12478](https://github.com/diegosouzapw/OmniRoute/pull/12478)) +- **chore(quality):** base fixes — stryker tap.testFiles + node_modules cache key ([#12482](https://github.com/diegosouzapw/OmniRoute/pull/12482)) +- **docs(arch):** one-process recipe for tens of long /v1/responses ([#12493](https://github.com/diegosouzapw/OmniRoute/pull/12493)) — thanks @RaviTharuma +- **chore(quality):** tighten the CodeQL ratchet baseline from 11 to 6 ([#12530](https://github.com/diegosouzapw/OmniRoute/pull/12530)) +- **chore(deps):** pin browserslist override to ^4.28.8 ([#12592](https://github.com/diegosouzapw/OmniRoute/pull/12592)) — thanks @KooshaPari +- **chore(deps):** bump fast-uri to 3.1.7 in the electron lockfile ([#12601](https://github.com/diegosouzapw/OmniRoute/pull/12601)) +- **docs(quality):** document how the CodeQL ratchet refreshes and how to tighten it ([#12611](https://github.com/diegosouzapw/OmniRoute/pull/12611)) +- **test(ci):** pin the openapi-security-tiers two-arm contract with an executing gate test (#12581) ([#12652](https://github.com/diegosouzapw/OmniRoute/pull/12652)) +- **docs(gamification):** align XP Rewards table with code (#12501) ([#12667](https://github.com/diegosouzapw/OmniRoute/pull/12667)) — thanks @KooshaPari +- **refactor(combo):** split executeTarget into gates, attempt, and loop ([#12746](https://github.com/diegosouzapw/OmniRoute/pull/12746)) — thanks @HouMinXi +- **docs(security):** document socket.yml scanner config + CI workflow link (#12575) ([#12764](https://github.com/diegosouzapw/OmniRoute/pull/12764)) — thanks @KooshaPari +- **chore(ci):** guard commit identity in pre-commit to stop author misattribution ([#12772](https://github.com/diegosouzapw/OmniRoute/pull/12772)) +- **deps:** 17 Dependabot bumps — bump electron from 43.4.0 to 43.4.1 in /electron ([#11426](https://github.com/diegosouzapw/OmniRoute/pull/11426)); bump the development group across 1 directory with 8 updates ([#11428](https://github.com/diegosouzapw/OmniRoute/pull/11428)); bump the production group across 1 directory with 11 updates ([#11440](https://github.com/diegosouzapw/OmniRoute/pull/11440)); chore(deps): bump github/codeql-action/init from 4.37.7 to 4.37.8 ([#11925](https://github.com/diegosouzapw/OmniRoute/pull/11925)); chore(deps): bump github/codeql-action from 4.37.7 to 4.37.8 ([#11926](https://github.com/diegosouzapw/OmniRoute/pull/11926)); chore(deps): bump github/codeql-action/analyze from 4.37.7 to 4.37.8 ([#11927](https://github.com/diegosouzapw/OmniRoute/pull/11927)); build(deps): bump github/codeql-action/init from 4.37.8 to 4.37.9 ([#12345](https://github.com/diegosouzapw/OmniRoute/pull/12345)); build(deps): bump github/codeql-action/analyze from 4.37.8 to 4.37.9 ([#12346](https://github.com/diegosouzapw/OmniRoute/pull/12346)); bump the development group across 1 directory with 2 updates ([#12347](https://github.com/diegosouzapw/OmniRoute/pull/12347)); build(deps): bump github/codeql-action from 4.37.8 to 4.37.9 ([#12349](https://github.com/diegosouzapw/OmniRoute/pull/12349)); bump browserslist from 4.28.2 to 4.28.8 ([#12396](https://github.com/diegosouzapw/OmniRoute/pull/12396)); bump the production group across 1 directory with 4 updates ([#12399](https://github.com/diegosouzapw/OmniRoute/pull/12399)); bump @xmldom/xmldom ([#12500](https://github.com/diegosouzapw/OmniRoute/pull/12500)); bump qs from 6.15.2 to 6.16.0 ([#12512](https://github.com/diegosouzapw/OmniRoute/pull/12512)); bump @xmldom/xmldom from 0.9.10 to 0.9.12 ([#12513](https://github.com/diegosouzapw/OmniRoute/pull/12513)); bump fast-uri from 3.1.5 to 3.1.7 ([#12514](https://github.com/diegosouzapw/OmniRoute/pull/12514)); bump @humanfs/node from 0.16.7 to 0.16.8 ([#12515](https://github.com/diegosouzapw/OmniRoute/pull/12515)) +- Clear the `release/v3.8.51` `check:agent-skills-sync` base-red: regenerate `skills/cli-tunnel/SKILL.md` so the `tunnel create [type]` positional that #13009 taught the generator to read is reflected in the committed skill. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` typecheck base-red from #12731: the new `Mode pack ranking gates` candidates in `open-sse/services/autoCombo/__tests__/autoCombo.test.ts` omitted the required `provider`, `model` and `errorRate` fields of `ProviderCandidate`, failing `check:open-sse-typecheck` (and with it `Fast Quality Gates`) on every open PR. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` docs-sync base-red: EURouter (#13025) and GreenPT (#13024) took the live provider count to 358, leaving 7 STRICT drifts (`PROVIDER_REFERENCE.md`, 4 diagrams, 2 tier-flow images) plus the `AGENTS.md` / `llm.txt` / `package.json` count claims stale. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- **test(models):** Pin `CATALOG_BUILD_TIMEOUT_MS` in the #12058 canonical-catalog regression so a cold catalog build racing the 8s default bound no longer flakes the suite with a 500 ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Allow `@omniroute/opencode-plugin-v2/` in the npm pack policy — [#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870) shipped the v2 plugin beside its v1 sibling without widening the allowlist, so every packed file under it read as an unexpected artifact and failed `check:pack-policy` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Point the release-green validator's pack gate at the tree it is validating — the build-provenance guard defaulted to `origin/main`, which a release branch tip can never be an ancestor of mid-cycle, so `Package artifact` reported a false red on every run and the tarball boot-smoke cascaded off it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **docs(checks):** keep doc counts honest — headings, rankings, catalog, weights, quality gate and scoring diagram now covered ([#12507](https://github.com/diegosouzapw/OmniRoute/pull/12507)) — thanks @maxmad64bis +- **chore(lifecycle):** `check:model-lifecycle` now also diffs `DEFAULT_DEGRADATION_MAP` (the background-task redirect table) against the vendor lifecycle snapshot, refusing a retired id as source or target, with a table-driven unit test beside it. Three rows whose source the vendor had retired — `claude-sonnet-4-20250514`, `gemini-3-pro-preview` and `gpt-5.1-codex` (whose target `gpt-5.1-codex-mini` is retired too) — were dead code, since `checkLifecycle` answers 410 before the redirect runs; they are dropped (#12535 — thanks @pacocartones) +- **test(compression):** cover idle worker eviction at the resource level — the pool must call `terminate()` and must not retain the worker's `MessagePort`, complementing the `exit`-event assertion added with the fix ([#13371](https://github.com/diegosouzapw/OmniRoute/pull/13371)) +- **chore(quality):** raise the `imageGeneration.ts` file-size ceiling for the image-only-model guard that clears the #12945 base-red ([#13392](https://github.com/diegosouzapw/OmniRoute/pull/13392)) +- **chore(quality):** type the combo-test route's JSON response bodies instead of casting them to `any`, drop the now-empty suppression entry, and remove the `hasPerModelQuota` import `chatCore.ts` stopped using when the failure-classification helper was extracted ([#13379](https://github.com/diegosouzapw/OmniRoute/pull/13379)) +- **chore(electron):** the desktop bundle no longer ships the root-level files of every translated docs mirror (`README.md`, `llm.txt`, `CLAUDE.md`, `GEMINI.md`, `CONTRIBUTING.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`) — the packaged app only reads `docs/i18n//docs/**`, which stays. Saves ~11 MB on top of the translated CHANGELOGs already pruned. (#0000) ([#13235](https://github.com/diegosouzapw/OmniRoute/pull/13235)) +- **chore(quality):** retighten the `src/sse/handlers/chat.ts` unused-vars suppression count to the 8 violations that actually remain ([#13385](https://github.com/diegosouzapw/OmniRoute/pull/13385)) +- Bring the documented counts back in line with the code: 171 migrations (was 169) and 20 routing strategies (was 19), plus the regenerated `cli-tunnel` skill reference. ([#12970](https://github.com/diegosouzapw/OmniRoute/pull/12970)) +- Update the documented migration count to 172 after the call-logs provider-stats indexes landed. ([#13209](https://github.com/diegosouzapw/OmniRoute/pull/13209)) +- **chore(ci):** two gates that close the blind spots behind the exclusions above. `check:vitest-exclusions` requires every Vitest exclusion to name a tracking issue and to appear in `config/quality/vitest-exclusions.json` — the previous list grew to 62 files behind a comment pointing at an issue that had been closed for a month. `check-new-key-coverage` requires a key newly added to `en.json` to reach every locale; the existing coverage gate is a percentage floor per locale, so eleven absent keys out of ~13,000 left it at 99.9% while a whole feature shipped untranslated in nine languages. Both are diff-aware, so pre-existing debt stays frozen and neither needed a migration to turn on. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **build:** root layout no longer loads Inter from `next/font/google`, so a production image build does not need fonts.googleapis.com ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **docs(i18n):** bring the eight new `llm.txt` mirrors (km, kn, ml, my, ne, or, pa, si) up to the current provider and migration counts, so `check-docs-sync` stops rejecting every commit ([#13674](https://github.com/diegosouzapw/OmniRoute/pull/13674)) +- **refactor(ui):** flow surfaces (home topology, combo live studio, compression cockpit/waterfall/nodes, token-health badge) express state through the theme-aware `--orch-status-*` tokens instead of fixed dark-mode hex, so green/red/amber/grey stay legible in the light theme; dark mode is byte-identical. Categorical palettes (routing-strategy hues, compression-layer pills, provider brand colors) deliberately stay hex ([#12378](https://github.com/diegosouzapw/OmniRoute/issues/12378)) ([#13203](https://github.com/diegosouzapw/OmniRoute/pull/13203)) +- **chore(tests):** 51 test files that had been excluded from Vitest are running again, restoring roughly 350 assertions to the blocking suite. Each was measured individually first: of the 62 files parked behind the `// #8618 — pre-existing failure` comment, 51 pass against the current tree with no source change, so the exclusions had outlived the failures they were added for. The 11 that genuinely still fail stay excluded, but now point at a live tracker (#13204) rather than at #8618, which was closed in August while the list it tracked kept growing. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **chore(quality):** drop two ESLint suppression entries whose violations no longer exist, so `eslint --suppressions-location` stops rejecting every commit that touches the surrounding files ([#13384](https://github.com/diegosouzapw/OmniRoute/pull/13384)) +- **chore(release):** bring main's post-tag fixes into release/v3.8.51 — sync-back step 2/2 (direct commit `21c488f210`) +- **chore(deps):** patch toml and esbuild in the opencode plugin lockfiles ([#12965](https://github.com/diegosouzapw/OmniRoute/pull/12965)) +- **docs(changelog):** reconcile the v3.8.51 living section — fold 366 fragments, cover every cycle commit, credit every contributor ([#12971](https://github.com/diegosouzapw/OmniRoute/pull/12971)) +- **test(plugin):** wait for the v2 lazy-refresh tier instead of sleeping past it ([#12984](https://github.com/diegosouzapw/OmniRoute/pull/12984)) +- **chore(quality):** rebaseline chatCore.ts after the non-streaming regression fixes ([#13045](https://github.com/diegosouzapw/OmniRoute/pull/13045)) +- **docs(changelog):** add the missing bullet marker to the DeepSeek PoW fragment ([#13200](https://github.com/diegosouzapw/OmniRoute/pull/13200)) +- **chore(deps):** drain the Dependabot queue — 10 of 13 alerts ([#13213](https://github.com/diegosouzapw/OmniRoute/pull/13213)) +- **chore(ci):** make worktrees carry the hooks that enforce the identity gate ([#13236](https://github.com/diegosouzapw/OmniRoute/pull/13236)) +- **chore(deps):** pin csv-parse to ^7.0.2 via overrides (#13117) ([#13300](https://github.com/diegosouzapw/OmniRoute/pull/13300)) — thanks @KooshaPari +- **chore(deps):** bump hono from ^4.12.34 to ^4.13.7 (#13148) ([#13301](https://github.com/diegosouzapw/OmniRoute/pull/13301)) — thanks @KooshaPari +- **chore(deps):** pin joi to ^18.2.8 via overrides (#13085) ([#13302](https://github.com/diegosouzapw/OmniRoute/pull/13302)) — thanks @KooshaPari +- **docs(providers):** add Microsoft 365 Copilot (BizChat) provider guide (#12779) ([#13340](https://github.com/diegosouzapw/OmniRoute/pull/13340)) — thanks @KooshaPari +### 🙌 Contributors + +Thanks to everyone whose work landed in v3.8.51: + +| Contributor | PRs / Issues | +| --- | --- | +| [@5dive-bot](https://github.com/5dive-bot) | #11852 | +| [@abhisheksharma2411](https://github.com/abhisheksharma2411) | #11547, #11684, #11959, #12209 | +| [@adevwithpurpose](https://github.com/adevwithpurpose) | #11464 | +| [@adivekar-utexas](https://github.com/adivekar-utexas) | #12015, #12043, #12090, #12138 | +| [@AIB1TAL0S](https://github.com/AIB1TAL0S) | #12103 | +| [@alltomatos](https://github.com/alltomatos) | #11448 | +| [@alvinveroy](https://github.com/alvinveroy) | #11857, #12027, #12165 | +| [@amaleta](https://github.com/amaleta) | #11906, #11934 | +| [@amartinawi](https://github.com/amartinawi) | #11575 | +| [@AndrianBalanescu](https://github.com/AndrianBalanescu) | #11083, #11830, #11834, #11844 | +| [@anhtahaylove](https://github.com/anhtahaylove) | #13091, #13092, #13093, #13096, #13097, #13100, #13106, #13109, #13114, #13155, #13166, #13171, #13175, #13187, #13196 | +| [@aniruddhaadak80](https://github.com/aniruddhaadak80) | #11760 | +| [@app](https://github.com/app) | #12554 | +| [@arminanton](https://github.com/arminanton) | #11461, #11513 | +| [@AStupidBear](https://github.com/AStupidBear) | #11584 | +| [@atescivitci-cmd](https://github.com/atescivitci-cmd) | #12105 | +| [@b3nw](https://github.com/b3nw) | #11970, #11971, #12147 | +| [@backryun](https://github.com/backryun) | #11259, #11950, #12075, #12076, #12078, #12079, #12081, #12082, #12181, #12228, #12239, #12255, #12258, #12277, #12367, #12423, #12524, #12538 | +| [@benjaminkitt](https://github.com/benjaminkitt) | #11747 | +| [@benzntech](https://github.com/benzntech) | #11614, #11615 | +| [@Bl0ck154](https://github.com/Bl0ck154) | #11948, #11951, #11952, #11953, #11954 | +| [@brick30llc-ctrl](https://github.com/brick30llc-ctrl) | #11752, #12242, #12262 | +| [@Chewji9875](https://github.com/Chewji9875) | #11455, #11875, #12028 | +| [@cheynetom](https://github.com/cheynetom) | #12058 | +| [@davidebaraldo](https://github.com/davidebaraldo) | #12222, #12304 | +| [@davidlinfr](https://github.com/davidlinfr) | #6390 | +| [@ddarkr](https://github.com/ddarkr) | #12124 | +| [@Deftera186](https://github.com/Deftera186) | #11809 | +| [@dpozimski](https://github.com/dpozimski) | #11945 | +| [@DW-MediaLab](https://github.com/DW-MediaLab) | #12229 | +| [@echel0nn](https://github.com/echel0nn) | #11923 | +| [@f9td56dbgh-hub](https://github.com/f9td56dbgh-hub) | #11660 | +| [@fabioluissilva](https://github.com/fabioluissilva) | #11991 | +| [@foreveryh](https://github.com/foreveryh) | #12177 | +| [@geek007git](https://github.com/geek007git) | #12115, #12116, #12117, #12120, #12122 | +| [@geekyNads](https://github.com/geekyNads) | #11784, #12266 | +| [@ggiak](https://github.com/ggiak) | #11561, #12276, #12350, #12402 | +| [@gonisulaimann](https://github.com/gonisulaimann) | #12368, #12369, #12371 | +| [@Gorillaz322](https://github.com/Gorillaz322) | #12207 | +| [@groovecityJO](https://github.com/groovecityJO) | #12682 | +| [@hartmark](https://github.com/hartmark) | #11434, #11452, #11473, #11499, #11703, #11983, #11984, #11985, #11986, #11988, #11989, #11990, #11994, #12221, #12293, #12445, #12446, #12447, #12448, #12460, #12461, #12623, #12646, #12650, #12680, #12717, #12718, #12727, #12741, #12854 | +| [@hizzt](https://github.com/hizzt) | #11894 | +| [@hongnoul](https://github.com/hongnoul) | #11484 | +| [@HouMinXi](https://github.com/HouMinXi) | #11411, #11414, #11512, #11518, #11520, #11641, #11642, #11643, #11687, #11779, #11849, #11850, #11851, #11915, #11916, #11918, #11919, #11920, #12013, #12017, #12033, #12042, #12106, #12139, #12166, #12169, #12171, #12205, #12213, #12312, #12325, #12487, #12488, #12495, #12504, #12557, #12566, #12590, #12591, #12624, #12626, #12632, #12637, #12678, #12696, #12697, #12711, #12733, #12746, #12767, #12770, #12789, #12803, #12805, #12811, #12866, #12868, #12899, #12926, #12934, #12950, #12951, #12974, #13001, #13006, #13011, #13017, #13026, #13027, #13034, #13035, #13038, #13042, #13050, #13060, #13061, #13069, #13107, #13120, #13136, #13195, #13197 | +| [@Hsia97](https://github.com/Hsia97) | #11624 | +| [@jacobsparts](https://github.com/jacobsparts) | #11854, #12155, #12167 | +| [@joglomedia](https://github.com/joglomedia) | #11980 | +| [@jonlwheat2-gif](https://github.com/jonlwheat2-gif) | #11443, #11450, #11549, #11567, #11608, #11633, #11635, #11640, #11644, #11671, #11672, #11673, #11674, #11675, #11676, #11677, #11706, #11783, #12051, #12052, #12053, #12055 | +| [@kanade-hoshino](https://github.com/kanade-hoshino) | #12180 | +| [@Karan825](https://github.com/Karan825) | #11861 | +| [@KaspaPulse](https://github.com/KaspaPulse) | #11389, #11469, #11628, #11666, #11910 | +| [@keeltrace](https://github.com/keeltrace) | #12080, #12223 | +| [@killer30001000](https://github.com/killer30001000) | #12178 | +| [@KooshaPari](https://github.com/KooshaPari) | #12470, #12592, #12667, #12699, #12703, #12706, #12764, #12769, #12771, #13300, #13301, #13302, #13340, #13401, #13403, #13404, #13405, #13406, #13407, #13409, #13410, #13411, #13412, #13413, #13414, #13418, #13424, #13427, #13433, #13523, #13524, #13525, #13528, #13531, #13532, #13534, #13539, #13543, #13545, #13546, #13547, #13550, #13553 | +| [@kriptoburak](https://github.com/kriptoburak) | #11370 | +| [@KrzysiekSko](https://github.com/KrzysiekSko) | #12425, #12432, #12673 | +| [@marcelokarval](https://github.com/marcelokarval) | direct commit / report | +| [@maxmad64bis](https://github.com/maxmad64bis) | #1622, #11435, #11437, #11441, #11537, #11550, #11553, #11555, #11557, #11812, #11842, #11843, #11903, #12151, #12214, #12215, #12218, #12226, #12314, #12316, #12317, #12318, #12319, #12320, #12321, #12507, #12715, #12731, #12744, #12786, #12787, #12788, #12790, #12792, #12794, #12795, #12828, #12832, #12853, #12857, #12870, #12937, #12941, #12975, #13141, #13142, #13146, #13147 | +| [@mdigitalbh81](https://github.com/mdigitalbh81) | #12240 | +| [@Meet6338-X](https://github.com/Meet6338-X) | #11598, #11609, #12110 | +| [@morpheus9393](https://github.com/morpheus9393) | #11943 | +| [@MumuTW](https://github.com/MumuTW) | #11492, #11502, #11506, #11507, #11626, #11675, #11685, #11728, #11746, #11888, #11889, #11890, #11892 | +| [@Neuron-Mr-White](https://github.com/Neuron-Mr-White) | #11622, #11801, #12256 | +| [@NightStalker-87](https://github.com/NightStalker-87) | #12183 | +| [@NoxzRCW](https://github.com/NoxzRCW) | #11879, #11880, #11881, #11882, #11883, #11935 | +| [@ntdat812](https://github.com/ntdat812) | #11585, #12095 | +| [@ntdatt812](https://github.com/ntdatt812) | #11368, #11573, #11574, #11576, #11577, #11580, #11582, #11583, #11588, #11589, #11590, #11591, #11592, #11593, #11672, #12177, #12180, #12873, #12918, #12920, #12921, #12925, #12930, #13007, #13009, #13024, #13025, #13083, #13087, #13101, #13104, #13110 | +| [@official-burak](https://github.com/official-burak) | #11542 | +| [@opensource-elearning](https://github.com/opensource-elearning) | #12179, #12189, #12278, #12286 | +| [@oyi77](https://github.com/oyi77) | #11408, #11409, #11421, #11505, #11677, #12036, #12110 | +| [@pacocartones](https://github.com/pacocartones) | #11521, #11522, #11527, #11528, #11529, #11530, #11531, #11532, #11533, #11534, #11595, #11599, #11603, #11604, #11605, #11607, #11610, #11676, #11714, #11716, #11718, #11767, #11838, #11860, #11862, #11869, #11871, #11872, #11873, #11903, #11906, #11921, #11934, #12356, #12358, #12359, #12360, #12361, #12362, #12364, #12365, #12373, #12374, #12375, #12376, #12377, #12379, #12380, #12381, #12386, #12387, #12389, #12390, #12394, #12395, #12397, #12401, #12403, #12404, #12522, #12523, #12535, #12536, #12540, #12541, #12543, #12545, #12548, #12549, #12550, #12551, #12552, #12647, #12651, #12653, #13055 | +| [@patrykkopycinski](https://github.com/patrykkopycinski) | #11936, #11937, #12224 | +| [@PauloFH](https://github.com/PauloFH) | #11509 | +| [@PauloHSOliveira](https://github.com/PauloHSOliveira) | #12241 | +| [@ponkcore](https://github.com/ponkcore) | #12054 | +| [@Prajeeth-12](https://github.com/Prajeeth-12) | #11634, #12046 | +| [@quiterunner-commits](https://github.com/quiterunner-commits) | #12131, #12143 | +| [@rafacpti23](https://github.com/rafacpti23) | #11554, #11558, #12192, #12197, #12198, #12202, #12203, #12204 | +| [@ragnar-claude](https://github.com/ragnar-claude) | #11564 | +| [@raheemuddin786](https://github.com/raheemuddin786) | #11491, #11839, #11840, #11841, #12003, #12230, #12231, #12232, #12233, #12234 | +| [@rahilmavani](https://github.com/rahilmavani) | #11761 | +| [@Rahulsharma0810](https://github.com/Rahulsharma0810) | #11771 | +| [@RaviTharuma](https://github.com/RaviTharuma) | #11710, #11727, #11797, #11798, #11802, #11805, #11806, #11811, #12098, #12099, #12101, #12449, #12452, #12472, #12473, #12493, #12533, #12607, #12628, #12631, #12636, #12875, #12876, #12880, #12882, #12884 | +| [@rezjalibd](https://github.com/rezjalibd) | #12186 | +| [@RhianB14](https://github.com/RhianB14) | #11692 | +| [@rifqiawl](https://github.com/rifqiawl) | #11517, #11519, #11969 | +| [@rqzbeh](https://github.com/rqzbeh) | #11390 | +| [@Sabeekhann](https://github.com/Sabeekhann) | #11699, #11705 | +| [@santosraju99-hub](https://github.com/santosraju99-hub) | #11755, #11770 | +| [@Seramicx](https://github.com/Seramicx) | #11568 | +| [@Siva010](https://github.com/Siva010) | #12191 | +| [@solstxce](https://github.com/solstxce) | #11597 | +| [@soroush5](https://github.com/soroush5) | #12691 | +| [@tenshiak](https://github.com/tenshiak) | #12279 | +| [@TheDemonTuan](https://github.com/TheDemonTuan) | #11468, #11470, #11471, #11482, #11548, #11758, #11775, #11814 | +| [@thomasmaerz](https://github.com/thomasmaerz) | #12834 | +| [@tuandinh0801](https://github.com/tuandinh0801) | #11454 | +| [@turbolego](https://github.com/turbolego) | #11621, #11762, #11772, #11774, #11781, #12216 | +| [@Tushar49](https://github.com/Tushar49) | #11392 | +| [@ujjawalkaushik1110](https://github.com/ujjawalkaushik1110) | #11863 | +| [@vermasomesh835](https://github.com/vermasomesh835) | #11794 | +| [@vsd2807](https://github.com/vsd2807) | #11565, #11619 | +| [@wahidsadik371-coder](https://github.com/wahidsadik371-coder) | #12126 | +| [@watchingdogs](https://github.com/watchingdogs) | #12031 | +| [@wildcard](https://github.com/wildcard) | #11620 | +| [@xiaoyaner0201](https://github.com/xiaoyaner0201) | #11460, #11662, #12083 | +| [@Xxx91n](https://github.com/Xxx91n) | #11690 | +| [@yourspraveen](https://github.com/yourspraveen) | #11146 | +| [@yxyxy](https://github.com/yxyxy) | #11671 | +| [@ZaimMarzuki](https://github.com/ZaimMarzuki) | #11960 | +| [@Zartharas](https://github.com/Zartharas) | #11340 | +| [@zero-executioner](https://github.com/zero-executioner) | #11631 | +| [@diegosouzapw](https://github.com/diegosouzapw) | maintainer | + +--- ## [3.8.50] — 2026-08-25 _Living section — regenerated 2026-08-12 from all cycle commits (cycle open `ed2db6cb19` → tip). Bullets carry the merged PR and its author; direct pushes listed separately._ diff --git a/docs/i18n/ml/CHANGELOG.md b/docs/i18n/ml/CHANGELOG.md index 129621337b..70cde97312 100644 --- a/docs/i18n/ml/CHANGELOG.md +++ b/docs/i18n/ml/CHANGELOG.md @@ -96,31 +96,31 @@ ## [3.8.51] — TBD -_Living section — reconciled 2026-09-07 from all cycle commits (`release/v3.8.50` tip `091589089c` → `release/v3.8.51` tip `d6f315018a`, 696 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._ +_Living section — reconciled 2026-09-15 from all cycle commits (`091589089c` → `c0f92ec98a`, 916 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 | **120** | -| 📝 Commits in the cycle | **696** | -| 🔀 Pull requests referenced | **672** | -| 📋 Changelog entries | **685** | -| 🙌 Contributors credited in entries | **118** | +| 👥 People who contributed | **123** | +| 📝 Commits in the cycle | **916** | +| 🔀 Pull requests referenced | **892** | +| 📋 Changelog entries | **919** | +| 🙌 Contributors credited in entries | **121** | | 🤖 Automated dependency commits | 18 | **Entries by type** | Type | Count | | --- | ---: | -| 🐛 Fixes | 403 | -| ✨ Features | 118 | -| 🧹 Chore | 72 | -| 📚 Docs | 36 | -| 🧪 Tests | 29 | +| 🐛 Fixes | 592 | +| ✨ Features | 131 | +| 🧹 Chore | 93 | +| 📚 Docs | 42 | +| 🧪 Tests | 32 | +| ♻️ Refactor | 8 | +| 🏗️ Build | 7 | | ⚡ Performance | 7 | -| ♻️ Refactor | 7 | -| 🏗️ Build | 6 | | ⚙️ CI | 2 | | ⏪ Reverts | 2 | | 📦 Dependencies | 2 | @@ -128,35 +128,35 @@ _Living section — reconciled 2026-09-07 from all cycle commits (`release/v3.8. ### 🏆 Top 25 contributors this cycle -_By commits in `release/v3.8.50..release/v3.8.51` (`091589089c..d6f315018a`), author identities consolidated via `.mailmap` and the merged PR's GitHub login. Bots excluded._ +_By commits in `091589089c..c0f92ec98a`, author identities consolidated via `.mailmap` and the merged PR's GitHub login. Bots excluded._ | # | Contributor | Commits | | ---: | --- | ---: | -| 🥇 | diegosouzapw | 240 | -| 🥈 | Paco Cartones (@pacocartones) | 58 | -| 🥉 | Bob.Hou (@HouMinXi) | 57 | -| 4 | Dizzle (@maxmad64bis) | 27 | -| 5 | Webman (@jonlwheat2-gif) | 22 | -| 6 | Ravi Tharuma (@RaviTharuma) | 21 | -| 7 | backryun | 17 | -| 8 | Markus Hartung (@hartmark) | 17 | -| 9 | Nguyen Thanh Dat (@ntdatt812) | 16 | -| 10 | Rafa Martins (@rafacpti23) | 10 | -| 11 | Syed Raheemuddin (@raheemuddin786) | 10 | -| 12 | Koosha Paridehpour (@KooshaPari) | 9 | -| 13 | Nguyễn Viết Tuấn (@TheDemonTuan) | 8 | -| 14 | MumuTW | 7 | -| 15 | Paijo (@oyi77) | 7 | -| 16 | Tobias Andersen (@turbolego) | 6 | -| 17 | Bl0ck (@Bl0ck154) | 5 | -| 18 | SHANMUGAPRIYAN (@geek007git) | 5 | -| 19 | KaspaPulse | 5 | -| 20 | Abhishek Sharma (@abhisheksharma2411) | 4 | -| 21 | Abhishek Divekar (@adivekar-utexas) | 4 | -| 22 | Andrew B. (@AndrianBalanescu) | 4 | -| 23 | NoxzRCW | 4 | -| 24 | opensource-elearning | 4 | -| 25 | Alvin T. Veroy (@alvinveroy) | 3 | +| 🥇 | diegosouzapw | 323 | +| 🥈 | Bob.Hou (@HouMinXi) | 79 | +| 🥉 | Paco Cartones (@pacocartones) | 77 | +| 4 | Dizzle (@maxmad64bis) | 49 | +| 5 | Koosha Paridehpour (@KooshaPari) | 43 | +| 6 | Nguyen Thanh Dat (@ntdatt812) | 31 | +| 7 | Ravi Tharuma (@RaviTharuma) | 26 | +| 8 | Markus Hartung (@hartmark) | 22 | +| 9 | Webman (@jonlwheat2-gif) | 22 | +| 10 | backryun | 17 | +| 11 | anhtahaylove | 15 | +| 12 | Rafa Martins (@rafacpti23) | 10 | +| 13 | Syed Raheemuddin (@raheemuddin786) | 10 | +| 14 | Nguyễn Viết Tuấn (@TheDemonTuan) | 8 | +| 15 | MumuTW | 7 | +| 16 | Paijo (@oyi77) | 7 | +| 17 | Tobias Andersen (@turbolego) | 6 | +| 18 | Bl0ck (@Bl0ck154) | 5 | +| 19 | SHANMUGAPRIYAN (@geek007git) | 5 | +| 20 | KaspaPulse | 5 | +| 21 | Abhishek Sharma (@abhisheksharma2411) | 4 | +| 22 | Abhishek Divekar (@adivekar-utexas) | 4 | +| 23 | Andrew B. (@AndrianBalanescu) | 4 | +| 24 | NoxzRCW | 4 | +| 25 | opensource-elearning | 4 | ### ✨ New Features @@ -282,6 +282,19 @@ _By commits in `release/v3.8.50..release/v3.8.51` (`091589089c..d6f315018a`), au - **feat(dashboard):** link a log entry's Conversation Context to its owning conversation ([#12646](https://github.com/diegosouzapw/OmniRoute/pull/12646)) — thanks @hartmark - **feat(catalog):** eligibility-gated free-tier bucket ([#12669](https://github.com/diegosouzapw/OmniRoute/pull/12669)) - **feat(db):** fail-closed previous_response_id continuation for redacted video turns (#12150 P2b) ([#12707](https://github.com/diegosouzapw/OmniRoute/pull/12707)) +- **feat(gamification): pay the documented `streak_bonus` and `badge_unlock` XP rewards.** `XP_REWARDS` listed both rewards but the award pipeline never paid them: the private reward table in `events.ts` omitted them, `updateStreak()` did not report when a streak extended, and badge unlocks carried no XP. Every request that extends a daily streak now pays `streak_bonus × streak length` once per UTC day (guarded by a same-day `xp_audit_log` check), and every badge unlocked through the pipeline pays `badge_unlock` once per badge (guarded by the `user_badges` primary key; `unlockBadge()` now reports whether it inserted). Bonus XP flows through the same `addXp` + level sync + global/weekly/monthly leaderboard path as action XP, so level-ups and rankings include it. The Radar supporter recognition unlock stays XP-free. (#12522 — thanks @pacocartones) +- **feat(providers):** advertise a `free-tier` capability in the provider plugin manifest for every provider with documented free models, so sidecars and dashboards can filter free-capable providers without reading the quota catalog ([#12786](https://github.com/diegosouzapw/OmniRoute/pull/12786)) — thanks @maxmad64bis +- **feat(providers):** Added EURouter as an OpenAI-compatible API-key gateway (`https://api.eurouter.ai/v1`), with live model discovery via `passthroughModels`. Its copy states that models are served by third-party upstreams listed per model, so an EU-based router is not read as EU data residency for inference. ([#13025](https://github.com/diegosouzapw/OmniRoute/pull/13025)) — thanks @ntdatt812 +- **feat(providers):** Added GreenPT as an OpenAI-compatible API-key provider (`https://api.greenpt.ai/v1`), with live model discovery via `passthroughModels`. No free-inference badge: the published docs describe a free API subscription billed per token, not a free tier. ([#13024](https://github.com/diegosouzapw/OmniRoute/pull/13024)) — thanks @ntdatt812 +- **feat(i18n):** 9 new locales — Greek (`el`), Croatian (`hr`), Serbian (`sr`), Lithuanian (`lt`), Estonian (`et`), Latvian (`lv`), Slovenian (`sl`), Maltese (`mt`), Irish (`ga`) — across the dashboard, docs mirrors, CLI, README and the site; OmniRoute now ships every official EU language (51 locales). (#13044) +- **feat(i18n):** 8 new locales — Kannada (`kn`), Malayalam (`ml`), Odia (`or`), Punjabi (`pa`), Nepali (`ne`), Sinhala (`si`), Burmese (`my`), Khmer (`km`) — across the dashboard, docs mirrors, CLI, README and the site (59 locales). The translator now restores the ICU literal escape around angle placeholders and splits oversized docs sections before translating. (#0000) ([#13660](https://github.com/diegosouzapw/OmniRoute/pull/13660)) +- feat(providers): **list Agnes 3.0 Flash as the current free chat model, drop retired 1.5 Flash, add Image 2.0/2.5 Flash plus Video 2.5/2.5 Flash, and discover the live `/v1/models` catalog (including the CN host `api.agnes-ai.cn`).** `agnes-1.5-flash` now forwards to `agnes-3.0-flash`. Video 2.5 polls `GET /v1/videos/{id}` (not the V2.0 `/agnesapi` contract). Live `/v1/models` (2026-09-09) no longer serves 1.5; the wiki marks it deprecated. 3.0 Flash is 512K context / 65,536 max output, same window as 2.5. CN-region keys use the existing per-connection base-URL field, default stays `apihub.agnes-ai.com`. ([#13120](https://github.com/diegosouzapw/OmniRoute/pull/13120)) — thanks @HouMinXi +- **feat(sse):** Codex and OpenAI catalogs list GPT-6 Astra with effort aliases (`-low` through `-ultra`); Codex CLI identity pins `@openai/codex@0.153.4` in lockstep with the image ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **feat(dashboard):** the orchestration History tab gained a "Compare runs" mode — toggling it turns each grid cell into a 2-item selection queue (a 3rd click drops the oldest pick), and picking two cells opens a side-by-side comparison panel instead of the usual detail drawer. The panel fetches both runs' detail the same way the drawer does (falling back to persisted history once a run leaves the live TTL window) and shows, per side: identity/source/state, start time, a signed `right - left` delta for duration/cost/event count, the event timeline aligned by index, and any memory hits. One side's fetch failing never blocks the other, and a delta is only ever computed when both sides have a finite value — otherwise it renders "—", never `NaN`. Comparing two runs from different sources or skills still works; a banner marks the deltas as informational rather than hiding them, since the two runs aren't a strict apples-to-apples pair. ([#12677](https://github.com/diegosouzapw/OmniRoute/pull/12677)) +- **feat(dashboard):** badge a conversation that never reached a clean stop ([#12717](https://github.com/diegosouzapw/OmniRoute/pull/12717)) — thanks @hartmark +- **feat(release):** reconcile-changelog tool + version-anchored fragment aggregation ([#12987](https://github.com/diegosouzapw/OmniRoute/pull/12987)) +- **feat(mcp):** carry context_length through MCP list_models_catalog projection (#12776) ([#13406](https://github.com/diegosouzapw/OmniRoute/pull/13406)) — thanks @KooshaPari +- **feat(cli):** make startup readiness budget configurable (#13369) ([#13433](https://github.com/diegosouzapw/OmniRoute/pull/13433)) — thanks @KooshaPari ### 🐛 Bug Fixes @@ -698,6 +711,194 @@ _By commits in `release/v3.8.50..release/v3.8.51` (`091589089c..d6f315018a`), au - **fix(i18n):** quote placeholder in OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES description (#12505) ([#12769](https://github.com/diegosouzapw/OmniRoute/pull/12769)) — thanks @KooshaPari - **fix(api/pricing):** surface validation error message as string, not raw object (#12494) ([#12771](https://github.com/diegosouzapw/OmniRoute/pull/12771)) — thanks @KooshaPari - **fix(sse):** re-enable prompt compression for native Codex passthrough ([#12834](https://github.com/diegosouzapw/OmniRoute/pull/12834)) — thanks @thomasmaerz +- **fix(dashboard):** model health tests for a provider node set to the Responses API now call `/v1/responses` with a Responses-shaped body instead of `/v1/chat/completions` — those models were reported as `Provider returned HTTP 200 but no text content` even though the same model answered normally through `/v1/responses` ([#13070](https://github.com/diegosouzapw/OmniRoute/issues/13070)) ([#13087](https://github.com/diegosouzapw/OmniRoute/pull/13087)) — thanks @ntdatt812 +- fix(routing): stop a round-robin combo's "opencode" targets from collapsing onto the opencode-zen connection (#11912) ([#13283](https://github.com/diegosouzapw/OmniRoute/pull/13283)) +- fix(routing): stop the reactive-compaction debug log from lying when compression is globally disabled (#11977) ([#13276](https://github.com/diegosouzapw/OmniRoute/pull/13276)) +- fix(electron): relativize standalone-bundle symlink targets so Stage 8 manifest verification stops failing on Windows (#11979) ([#13251](https://github.com/diegosouzapw/OmniRoute/pull/13251)) +- fix(dashboard): surface a visible error when Compression Studio's combined preview run fails (#12061) ([#13288](https://github.com/diegosouzapw/OmniRoute/pull/13288)) +- fix(dashboard): make the compression "Effective pipeline" preview honor the active profile and warn when the master switch is off (#12063) ([#13287](https://github.com/diegosouzapw/OmniRoute/pull/13287)) +- fix(providers): scope TinyCMS Web signer's DOM shims to each call instead of leaking them for the process lifetime, and surface a clean HTTP status on a non-JSON interception-toggles error (#12072) ([#13254](https://github.com/diegosouzapw/OmniRoute/pull/13254)) +- fix(guardrails): stop Vision Bridge from re-selecting a model locked after a 404 (#12111) ([#13259](https://github.com/diegosouzapw/OmniRoute/pull/13259)) +- fix(sse): require Responses-shaped body before native OpenAI-compatible passthrough (#12129) ([#13278](https://github.com/diegosouzapw/OmniRoute/pull/13278)) +- fix(providers): minimax-m3 now collapses manual thinking.type:"enabled" to adaptive, preventing upstream 400 (2013) (#12132) ([#13249](https://github.com/diegosouzapw/OmniRoute/pull/13249)) +- fix(db): scope model visibility overrides by modality so hiding a Chat model no longer hides an identically-ID'd Image/Embeddings/etc. model (#12172) ([#13258](https://github.com/diegosouzapw/OmniRoute/pull/13258)) +- fix(db): scope local-provider apiKey dedup to matching base URL so LM Studio/Ollama-style connections support multiple accounts (#12173) ([#13250](https://github.com/diegosouzapw/OmniRoute/pull/13250)) +- fix(providers): refresh Trae's stale Referer/Origin and forward user timezone so imported connections stop failing with 401 (#12190) ([#13255](https://github.com/diegosouzapw/OmniRoute/pull/13255)) +- fix(providers): route opencode-go/gpt-5.6-luna to /responses instead of /chat/completions (#12196) ([#13275](https://github.com/diegosouzapw/OmniRoute/pull/13275)) +- fix(dashboard): allow deleting the last extra-upstream-header row even when invalid (#12251) ([#13252](https://github.com/diegosouzapw/OmniRoute/pull/13252)) +- **fix(i18n):** translate pre-existing `__MISSING__:` keys for `combo.sort`, `requestLogger.detail` expand/collapse, `common.profile`, and `settings.resilienceCredentialHealth*` across 39 locales ([#12272](https://github.com/diegosouzapw/OmniRoute/issues/12272)) ([#13034](https://github.com/diegosouzapw/OmniRoute/pull/13034)) — thanks @HouMinXi +- fix(cli): run the Node.js runtime compatibility guard before the heavy `tsx/esm` + Commander import chain so an unsupported runtime gets a clear message instead of a raw `Invalid regular expression flags` crash (#12296) ([#13253](https://github.com/diegosouzapw/OmniRoute/pull/13253)) +- fix(dashboard): refresh the providers list after deleting a compatible provider node (#12298) ([#13256](https://github.com/diegosouzapw/OmniRoute/pull/13256)) +- fix(usage): fail closed on API-key budget enforcement when a provider's `auto` routing alias has no pricing row, instead of silently counting it as $0 (#12341) ([#13257](https://github.com/diegosouzapw/OmniRoute/pull/13257)) +- **fix(providers):** include the submitted Agnes video model when polling by `video_id` ([#12356](https://github.com/diegosouzapw/OmniRoute/pull/12356)) — thanks @pacocartones +- **fix(routing):** custom OpenAI-compatible nodes now honor the saved Chat/Responses API type after edits instead of letting the node's original ID prefix override the live connection setting ([#11884](https://github.com/diegosouzapw/OmniRoute/issues/11884)). ([#12358](https://github.com/diegosouzapw/OmniRoute/pull/12358)) — thanks @pacocartones +- fix(sse): surface an error instead of a silent empty 200 when a Claude stream closes with zero bytes (#12398) ([#13285](https://github.com/diegosouzapw/OmniRoute/pull/13285)) +- fix(oauth): warn before the dead localhost:8080 redirect in antigravity/gemini `oauth start` (#12413) ([#13265](https://github.com/diegosouzapw/OmniRoute/pull/13265)) +- fix(providers): stop devin-cli spawn error from double-closing the SSE controller (#12517) ([#13261](https://github.com/diegosouzapw/OmniRoute/pull/13261)) +- **fix(rerank):** clamp Voyage `top_k` to the documents actually sent after empty-string filtering, and honor `return_documents: false` in the NVIDIA response adapter (#12523 — thanks @pacocartones) +- **fix(audio):** `/v1/audio/translations` now resolves combo names the way `/v1/audio/transcriptions` already does, so a combo that `GET /v1/models` advertises is fanned out to its targets instead of being rejected with `400 Invalid translation model: . Use format: provider/model`; literal `provider/model` ids and unknown bare names behave as before (#12536 — thanks @pacocartones) +- **fix(gemini):** strip the JSON-Schema-2020-12 `prefixItems` keyword from Gemini tool schemas at every nesting level, so Claude Code tool definitions no longer fail with `400 Unknown name "prefixItems"` on Gemini models (#12540 — thanks @pacocartones) +- **fix(api-manager):** Expose an accessible loading status while API keys are fetched instead of an empty accessibility tree (#12541 — thanks @pacocartones) +- **fix(video):** Clamp `estimateJpegFrameBytes` at zero for padding-only payloads and build the three encode-side frame data URIs from `JPEG_FRAME_DATA_URI_PREFIX` instead of a repeated literal (#12543 — thanks @pacocartones) +- **fix(devin):** accept Windows `DEVIN_AGENTIC_HOME` sandbox paths (`C:\...\.sandbox\...`) in the isolated-home check so the Devin Claude Bridge no longer fails closed on Windows ([#12405](https://github.com/diegosouzapw/OmniRoute/issues/12405)) (#12545 — thanks @pacocartones) +- **fix(db):** Add `conversation_turn_nodes` and orphaned `agentic_conversations` to the auto-cleanup cycle under the existing `retention.callLogs` window, so identity nodes whose call-log content has already been purged no longer accumulate without bound in `storage.sqlite` (#12548 — thanks @pacocartones) +- **fix(i18n):** Wrap the `~/.claude/profiles//settings.json` placeholder in ICU single quotes in the `OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES` feature-flag description across all 42 locales and the TypeScript default, so next-intl no longer fails with `INVALID_MESSAGE: UNCLOSED_TAG` and the Feature Flags card shows the description instead of the raw key (#12549 — thanks @pacocartones) +- **fix(orchestration):** `updateCloudAgentTask` now publishes the task's real `status` on `agent.task.updated` when an update only touches `result`, `activities` or `error`, instead of the fabricated `"updated"` state, and stays silent when no row matched the id (#12550 — thanks @pacocartones) +- **fix(i18n):** the home "Recent Requests" panel and the Provider Topology legend are now translated instead of rendering English copies on non-English dashboards; the legend reads its own `home.topologyLegend*` labels with consistent casing rather than borrowing the memory-settings "Recent" and analytics "Error" strings (#12551 — thanks @pacocartones). +- **docs(reference):** bring the `FEATURE_FLAGS.md` catalog back to 1:1 with `featureFlagDefinitions.ts` — 20 missing flags added, the two `*_BLOCK_THRESHOLD` env-only knobs moved out of the flag tables, category/total counts and the Live WS port corrected, guarded by a static test (#12552 — thanks @pacocartones) +- **fix(i18n):** backfill missing `usage.kiloPass*` strings in 39 locales and restore `featureFlagOmnirouteDisableThinkingLevelVariantsDescription` in `pt.json` ([#12561](https://github.com/diegosouzapw/OmniRoute/issues/12561)) ([#13027](https://github.com/diegosouzapw/OmniRoute/pull/13027)) — thanks @HouMinXi +- fix(docker): default docker-compose app ports (dashboard/API/live-WS) to loopback instead of `0.0.0.0`, closing the anonymous `/v1` LAN/WAN exposure gap left open by `REQUIRE_API_KEY=false` (#12568) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): close DNS-rebinding SSRF gap in webhook outbound-URL guard (#12569) ([#13243](https://github.com/diegosouzapw/OmniRoute/pull/13243)) +- fix(docker): require a per-session token on the VNC browser CDP bridge and isolate it on a dedicated Docker network (#12571) ([#13241](https://github.com/diegosouzapw/OmniRoute/pull/13241)) +- fix(open-sse): write Adobe Firefly session tokens and cookie jars with 0700/0600 permissions instead of the process umask (#12572) ([#13242](https://github.com/diegosouzapw/OmniRoute/pull/13242)) +- fix(api): remove hardcoded wildcard CORS in openai-to-gemini-sse.ts so the centralized fail-closed CORS gate is the sole source of `Access-Control-Allow-Origin` (#12573) ([#13239](https://github.com/diegosouzapw/OmniRoute/pull/13239)) +- fix(api): enforce API key policy (budget/rate-limit/schedule/endpoint scoping) on the ElevenLabs speech-to-text, text-to-speech and voices proxy routes (#12574) ([#13244](https://github.com/diegosouzapw/OmniRoute/pull/13244)) +- fix(sse): cap HuggingChat NDJSON body size and bound the read loop with the fetch timeout so a stalled or hostile upstream cannot buffer unbounded memory (#12577) ([#13282](https://github.com/diegosouzapw/OmniRoute/pull/13282)) +- fix(docker): scope the cliproxyapi/qdrant/bifrost sidecars to loopback by default and forward `CLIPROXYAPI_MANAGEMENT_KEY` into the cliproxyapi container so its management API is not left both unauthenticated and LAN-published (#12578) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): create DB export temp paths with `fs.mkdtempSync` instead of predictable timestamps (#12579) ([#13237](https://github.com/diegosouzapw/OmniRoute/pull/13237)) +- Cline 401 bodies that say "re-authenticate your Cline account" classify as a refreshable OAuth token, not a terminal expired key. The cooling panel no longer labels every cooldown as a 429; it shows the recorded last error instead. (#12594) ([#13060](https://github.com/diegosouzapw/OmniRoute/pull/13060)) — thanks @HouMinXi +- **fix(catalog):** degrade unknown combo targets instead of dropping LCD modalities, and persist OpenRouter `architecture.input_modalities` into the capability snapshot ([#12613](https://github.com/diegosouzapw/OmniRoute/issues/12613)) ([#13042](https://github.com/diegosouzapw/OmniRoute/pull/13042)) — thanks @HouMinXi +- fix(providers): send `x-api-key` instead of `Authorization: Bearer` for OpenCode Zen's `/v1/responses` endpoint (Muse Spark Contributor models), fixing a 401 on OmniRoute's auth header (#12633) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(sse): surface the actionable "Auggie CLI not found" message when the shell reports a missing `auggie` binary via exit code instead of a spawn error (#12645) ([#13263](https://github.com/diegosouzapw/OmniRoute/pull/13263)) +- **fix(config):** Persist deletions of built-in background-degradation entries — when a stored settings record exists its `degradationMap` is now authoritative instead of being merged under the defaults, so an entry the user removed in the dashboard no longer reappears on the next apply or restart ([#12424](https://github.com/diegosouzapw/OmniRoute/issues/12424)) ([#12647](https://github.com/diegosouzapw/OmniRoute/pull/12647)) — thanks @pacocartones +- **fix(gamification):** action-count badge milestones (First Token, Token Consumer, Token Machine, Token Whale, and the token-sharing tier) are now backed by a durable `xp_action_counts` counter incremented in `addXp()`, instead of a live `COUNT(*)` over `xp_audit_log`. The audit log is pruned by `retention.xpAuditLog` (default 30 days), so on a default install those "lifetime" milestones were really "actions in the last 30 days" and unlocked badges could stop unlocking once old rows aged out. `getActionCount()` and `checkActionCountBadges()` now read the same durable source, and a migration backfills existing totals from the surviving audit rows ([#12546](https://github.com/diegosouzapw/OmniRoute/issues/12546)) ([#12651](https://github.com/diegosouzapw/OmniRoute/pull/12651)) — thanks @pacocartones +- **fix(images):** `/v1/images/edits` now iterates a combo's targets the same way `/v1/images/generations` does (#9239) instead of flattening a bare combo to its first target. A combo whose first target is not edit-capable — or lacks credentials — now falls through to a later edit-capable target rather than hard-erroring, and missing credentials are skipped (not a hard `401`) to match the generations path. The per-target skip/terminal classification is extracted into a shared `runImageComboTargets` loop, so generations behavior is unchanged ([#12547](https://github.com/diegosouzapw/OmniRoute/issues/12547)). ([#12653](https://github.com/diegosouzapw/OmniRoute/pull/12653)) — thanks @pacocartones +- fix(sse): add first-byte watchdog to the TLS-fingerprint transport so a stalled wreq body falls back instead of hanging for minutes (#12656) ([#13272](https://github.com/diegosouzapw/OmniRoute/pull/13272)) +- fix(sse): exempt tiny-budget reasoning probes from combo quality failure and surface persisted-cooldown skips in ALL_TARGETS_SKIPPED diagnostics (#12659) ([#13269](https://github.com/diegosouzapw/OmniRoute/pull/13269)) +- fix(models): declare the real ~1M contextLength for OpenCode Zen's Muse Spark 1.2 models instead of falling back to the 200000 provider default (#12681) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(oauth): align codebuddy-cn OAuth User-Agent with the chat/usage CLI version to avoid WAF false positives (#12702) ([#13264](https://github.com/diegosouzapw/OmniRoute/pull/13264)) +- fix(dashboard): surface an authentication-required banner instead of silently blanking database settings for a guest session (#12709) ([#13270](https://github.com/diegosouzapw/OmniRoute/pull/13270)) +- **fix(resilience):** stop unbounded queue that hangs 6min until Aborted — gate, provider slot, and Bottleneck queue now share a per-connection `maxWaitMs` budget; `fail-closed` on exhaust (503); execution backstop `executionMaxWaitMs` overridable per connection with upstream clamp ([#12715](https://github.com/diegosouzapw/OmniRoute/pull/12715)) — thanks @maxmad64bis (with thanks to @Tushar49 for surfacing the slow-provider need in #12635) +- **fix(auto-combo):** every mode pack now carries `quality` and `reliability` weights (`quality` 0.02, 0.03 in `quality-first`; `reliability` 0.03, 0.04 in `reliability-first`), so selecting a pack no longer silences either signal; `DEFAULT_WEIGHTS` is unchanged ([#12731](https://github.com/diegosouzapw/OmniRoute/pull/12731)) — thanks @maxmad64bis +- **fix(sse):** the provider execution pipeline keeps the upstream error `code`/`type` again — both were lost when the non-streaming leg was extracted, so a config-class `422` (Antigravity missing project) degraded into a generic account cooldown instead of reaching the gate that recognises it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(db):** a legacy `call_logs` table no longer aborts startup — the provider-stats index is created after the column healing runs, not before it, so an install predating `request_type` opens instead of failing with `no such column` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(catalog):** a malformed row in the operator-writable custom-models blob no longer kills every `auto/*` pool ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(tests):** realign the guards that the same day's merges left asserting the old behavior — the provider-count total after GreenPT and EURouter, the GLM stream buffer slot that `#12925` turned into a declared parameter, and the injection scan bound that `#13104` reshaped from a head-only window into head-plus-tail ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- fix(cache): fold tool_choice/tools/response_format into the semantic cache signature so a cached tool_calls response can no longer be replayed for a request whose tool policy forbids it (#12734) ([#13267](https://github.com/diegosouzapw/OmniRoute/pull/13267)) +- **fix(models):** stop treating provider-supplied `isFree:true`/`:free`/`0/0` flags as trusted unless the provider has a documented free tier — only the shipped free-tier catalog decides otherwise; fetched `isFree:true` trusted only for a free-tier provider, custom `isFree:true` via a trusted path ([#12744](https://github.com/diegosouzapw/OmniRoute/pull/12744)) — thanks @maxmad64bis +- fix(memory): authenticate the internal /v1/rerank loopback call so memory reranking no longer silently degrades to unranked order when REQUIRE_API_KEY=true (#12745) ([#13268](https://github.com/diegosouzapw/OmniRoute/pull/13268)) +- fix(sse): parse Ollama Cloud's current usage markup (`$X of $Y used` aria-label, nested width style) (#12749) ([#13260](https://github.com/diegosouzapw/OmniRoute/pull/13260)) +- fix(cli): setup-opencode no longer sends an active context's management token to `/v1/models` when `--api-key`/`OMNIROUTE_API_KEY` is supplied — an explicit flag or the env var now always outranks the context's token, and the flag itself is no longer swallowed by the parent program's global `--api-key` option (#12783) ([#13246](https://github.com/diegosouzapw/OmniRoute/pull/13246)) +- fix(sse): register Arcee AI in the executor provider registry so requests reach api.arcee.ai instead of silently falling back to OpenAI (#12784) ([#13277](https://github.com/diegosouzapw/OmniRoute/pull/13277)) +- **fix(quota):** share one upstream quota read when identical saturation checks arrive at the same time, so traffic bursts don't multiply provider API calls ([#12787](https://github.com/diegosouzapw/OmniRoute/pull/12787)) — thanks @maxmad64bis +- **fix(db):** ignore expired or invalid rate-limit cooldown writes so a stale timestamp can't lock a connection that should be usable — clearing still works as before ([#12788](https://github.com/diegosouzapw/OmniRoute/pull/12788)) — thanks @maxmad64bis +- **fix(providers):** single source for provider order with `xao` ranking alongside `xai-oauth` ([#12790](https://github.com/diegosouzapw/OmniRoute/pull/12790)) — thanks @maxmad64bis +- **fix(routing):** off-table models check the free-model catalog before inheriting premium prices, latency bootstraps from the observed pool median, async tiers read live database pricing with a 90-day freshness gate, and the tier cache invalidates on every pricing write ([#12792](https://github.com/diegosouzapw/OmniRoute/pull/12792)) — thanks @maxmad64bis +- **fix(auto-combo):** snapshot scoring uses the observed per-connection account tier and per-model quality instead of neutral constants, and circuit-open providers rank lower at build time ([#12794](https://github.com/diegosouzapw/OmniRoute/pull/12794)) — thanks @maxmad64bis +- **fix(pool):** empty `auto/*` pools now say why they are empty, and the models listing uses the same paid check as routing ([#12795](https://github.com/diegosouzapw/OmniRoute/pull/12795)) — thanks @maxmad64bis +- fix(routing): recognize CLIProxyAPI's 'unknown provider for model' 400 as fallback-worthy (#12800) ([#13284](https://github.com/diegosouzapw/OmniRoute/pull/13284)) +- **fix(sse):** translate-mode streams now emit the estimated token counts as a trailing usage-only chunk before `[DONE]` when the upstream stays silent, so metered chat clients see totals instead of nothing ([#12828](https://github.com/diegosouzapw/OmniRoute/pull/12828)) — thanks @maxmad64bis +- **fix(db):** provider stats stay truthful on empty and legacy databases: fallback counts default to `0` instead of `null`, latency averages read `null` (not `0`) when no durations were recorded, failures logged before the error-type column existed group under `pre_migration` instead of `unclassified`, and per-provider queries use two new composite indexes ([#12832](https://github.com/diegosouzapw/OmniRoute/pull/12832)) — thanks @maxmad64bis +- fix(nvidia): fail open when a synced model catalog goes stale instead of gating forever (#12849) ([#13248](https://github.com/diegosouzapw/OmniRoute/pull/13248)) +- **fix(db):** WAL maintenance lives in its own module: a `TRUNCATE` checkpoint that hits a busy database now warns with its streak and retries once via `PASSIVE` instead of logging success, and closing-time checkpoints no longer report success on builds without a database file, with the busy totals visible in the authenticated monitoring health payload ([#12853](https://github.com/diegosouzapw/OmniRoute/pull/12853)) — thanks @maxmad64bis +- **fix(health):** quota with no snapshots reads empty instead of a contradicting 0% ([#12857](https://github.com/diegosouzapw/OmniRoute/pull/12857)) — thanks @maxmad64bis +- fix(a2a): accept the dashboard's own session cookie on /a2a so "Run message/send" no longer fails with "Unauthorized: missing or invalid API key" (#12888) ([#13271](https://github.com/diegosouzapw/OmniRoute/pull/13271)) +- **fix(logs):** the Logs grid's in-memory filter pass no longer discards rows the SQL query already matched — selecting an API key from the dropdown (which sends the key's id) returns its calls again, the Combo tab shows every combo instead of only those whose name contains a "1", and the model filter and search cover the same columns as the query ([#12896](https://github.com/diegosouzapw/OmniRoute/pull/12896)) — fixes [#12873](https://github.com/diegosouzapw/OmniRoute/issues/12873) — thanks @ntdatt812 +- **fix(a2a):** `/api/a2a/status` now builds the agent card from the request that asked for it, so a gateway reached at a non-localhost host no longer advertises `http://localhost:20128` as its A2A URL ([#12918](https://github.com/diegosouzapw/OmniRoute/pull/12918)). — thanks @ntdatt812 +- **fix(compression):** progressive aging now appends its `[COMPRESSED:aging:…]` annotation after a turn's `tool_result` blocks instead of in front of them, so Anthropic no longer rejects aged conversations with "`tool_use` ids were found without `tool_result` blocks immediately after" ([#12920](https://github.com/diegosouzapw/OmniRoute/pull/12920)). — thanks @ntdatt812 +- **fix(bedrock):** model import now resolves context limits for every vendor prefix instead of only `anthropic.*`, so `global.openai.gpt-5.6-*` no longer imports with a null `inputTokenLimit` and gets rejected pre-flight at the 200k default ([#12921](https://github.com/diegosouzapw/OmniRoute/pull/12921)). — thanks @ntdatt812 +- **fix(stream):** the 64 KB stream buffer GLM asks for is honoured instead of dropped, and the type error it caused no longer fails the API Route Typecheck gate on every open PR ([#12925](https://github.com/diegosouzapw/OmniRoute/pull/12925)) — thanks @ntdatt812 +- **fix(guardrails):** mask PII inside a `tool_result`'s nested content array, which the masker walked past while redacting its sibling block ([#12930](https://github.com/diegosouzapw/OmniRoute/pull/12930)) — thanks @ntdatt812 +- **fix(dashboard):** the Radar catalog table no longer leaves absent data unexplained — empty limits, unknown context windows, and unreported capabilities each explain themselves on hover, and a new check keeps it that way ([#12937](https://github.com/diegosouzapw/OmniRoute/pull/12937)) — thanks @maxmad64bis +- **fix(sse):** geo-blocked opencode requests rotate to the next account proxy instead of failing, so one refused egress no longer aborts the whole chain ([#12941](https://github.com/diegosouzapw/OmniRoute/pull/12941)) — thanks @maxmad64bis +- **fix(codex):** dashboard "clear cooldown" and the CAS recovery path now drop nested `codexScopeRateLimitedUntil` maps in the same write that nulls `rate_limited_until`, and fresh quota snapshots with headroom lift fallback-sourced scope cooldowns parked by quota preflight ([#12817](https://github.com/diegosouzapw/OmniRoute/issues/12817), [#12860](https://github.com/diegosouzapw/OmniRoute/issues/12860), [#12951](https://github.com/diegosouzapw/OmniRoute/pull/12951)) The dashboard reset-credit button (`consumeCodexResetCredit`) now exercises that same snapshot path after a successful redeem, so a filled quota bar is enough to unpark a leftover fallback Codex child without another manual clear. — thanks @HouMinXi +- **fix(db):** resolve `sql-wasm.wasm` across global npm install and hoisted layouts, ensuring OmniRoute can boot cleanly on Node 24 when native `better-sqlite3` is uncompiled. ([#13035](https://github.com/diegosouzapw/OmniRoute/pull/13035)) — thanks @HouMinXi +- **fix(api):** `DELETE /v1/batches/delete-completed` now sweeps only the calling API key's own completed batches (batches with no owner stay out of a key-scoped sweep on purpose), with an explicit instance-wide mode reserved for authenticated dashboard sessions, a 401 for a presented key that is unknown, revoked, deactivated, banned or expired (never falling through to the session branch), audit logging of every sweep, a sanitized 500 on failure, an owner-scoped file half (a key-scoped sweep never nulls a file another tenant owns) and an atomic sweep — chunked in 200-batch transactions in instance mode — so a mid-way error never leaves a batch pointing at a nulled file (GHSA-wvxc-jp3v-5mg5) ([#12969](https://github.com/diegosouzapw/OmniRoute/pull/12969)) +- **fix(combo):** quota-weighted routing stops drawing on an out-of-credit connection — a 402 now invalidates the stored quota snapshot instead of leaving its stale remaining percentage in place, and a snapshot older than 10 minutes no longer counts as confident headroom for the primary pool ([#12972](https://github.com/diegosouzapw/OmniRoute/pull/12972)) ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- **fix(vision):** Custom Models with "Vision capable" checked no longer have image requests swapped to `glm/glm-4.6v` when the client sends the advertised alias (`vllm/path/...`) or the bare path-shaped id — Vision Bridge now matches the stored override for all three id forms ([#12758](https://github.com/diegosouzapw/OmniRoute/issues/12758)) ([#12974](https://github.com/diegosouzapw/OmniRoute/pull/12974)) — thanks @HouMinXi +- **fix(sse):** transient opencode upstream failures rotate to the next account proxy instead of failing, so one flapping egress no longer aborts the whole chain ([#12975](https://github.com/diegosouzapw/OmniRoute/pull/12975)) — thanks @maxmad64bis +- **fix(azure):** Deployments from GPT-6 onward now send `max_completion_tokens` instead of `max_tokens`, which Azure rejects with HTTP 400. The rule matched a literal `gpt-5`, so each new generation arrived broken; it now matches the generation range, while `gpt-35-turbo` still keeps `max_tokens`. ([#13007](https://github.com/diegosouzapw/OmniRoute/pull/13007)) — thanks @ntdatt812 +- **fix(db):** add an opt-in self-restart circuit for sustained critical memory pressure, gate post-cleanup VACUUM behind a minimum freed-rows threshold, and checkpoint the SQLite WAL every 5 minutes with a size guard that escalates to TRUNCATE, so a growing WAL can no longer stall the event loop into a full outage. ([#13011](https://github.com/diegosouzapw/OmniRoute/pull/13011)) — thanks @HouMinXi +- **fix(auth):** an explicit connection pin may probe a quota-disabled row once and re-enable it on success ([#12874](https://github.com/diegosouzapw/OmniRoute/issues/12874)) ([#13017](https://github.com/diegosouzapw/OmniRoute/pull/13017)) — thanks @HouMinXi +- **fix(api):** thread `X-OmniRoute-Fallback-Attempts` through combo chat completions so streaming and non-streaming responses report how many prior legs were attempted ([#13038](https://github.com/diegosouzapw/OmniRoute/pull/13038)) — thanks @HouMinXi +- Restore provider failure classification and credential refresh on non-streaming requests: classify non-2xx failures to lock models on per-model quota exhaustion, update connection rate limits from headers and body, and pass credential refresh handlers to pipeline execution so 401 tokens can be refreshed and retried (#13043). ([#13069](https://github.com/diegosouzapw/OmniRoute/pull/13069)) — thanks @HouMinXi +- **fix(responses):** wrap forced-non-streaming web_search fallback JSON as Responses SSE so Codex still sees `response.completed` ([#13050](https://github.com/diegosouzapw/OmniRoute/pull/13050)) — thanks @HouMinXi +- **fix(antigravity):** Preserve upstream thought-token usage in normalized completion and reasoning token counts ([#13055](https://github.com/diegosouzapw/OmniRoute/pull/13055)) — thanks @pacocartones +- Fix a concurrency-slot leak in the DeepSeek PoW solver: a worker that failed to spawn (for example a missing worker script) never released its slot, so `MAX_CONCURRENT_WORKERS` failures disabled the solver until restart. ([#13097](https://github.com/diegosouzapw/OmniRoute/pull/13097)) — thanks @anhtahaylove +- **fix(acp):** bound the ACP session output buffers — `stdoutBuffer` and `stderrBuffer` now cap at 1 MiB keeping the most recent output behind a visible `[...output truncated...]` marker, and `stderrBuffer` is reset per prompt instead of accumulating for the lifetime of the session. ([#13100](https://github.com/diegosouzapw/OmniRoute/pull/13100)) — thanks @anhtahaylove +- **fix(acp):** release the `stdout`/`exit` listeners and the idle timer that a `sendPrompt` timeout used to leave attached to the `acpManager` singleton, and drop sessions that exited on their own from the session map instead of keeping them forever. ([#13096](https://github.com/diegosouzapw/OmniRoute/pull/13096)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection and PII scanners now read the text a `tool_result` block carries on `content` (string or nested block list), in messages and in system blocks, so tool output is judged by the same rules as user text ([#13101](https://github.com/diegosouzapw/OmniRoute/pull/13101)) — thanks @ntdatt812 +- **fix(gamification):** close the badge notification SSE stream when the request signal is already aborted before the stream starts — a client that disconnects while the route is still awaiting auth used to leave both the 2s unlock poll and the 15s heartbeat running for the lifetime of the process. ([#13106](https://github.com/diegosouzapw/OmniRoute/pull/13106)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection scan now spends its 16 KB budget on both ends of the request instead of the first 16 KB only, so `system`, `instructions`, `query`, `documents` and the newest turns are no longer hidden behind one long message ([#13104](https://github.com/diegosouzapw/OmniRoute/pull/13104)) — thanks @ntdatt812 +- **fix(dashboard):** expose the Volcano Ark console cookie on quota scraping and unwrap connect-error objects so the dashboard shows the upstream message ([#13107](https://github.com/diegosouzapw/OmniRoute/pull/13107)) — thanks @HouMinXi +- **fix(db):** release the `beforeExit`/`SIGINT`/`SIGTERM` handlers when a `node:sqlite` adapter closes, so a closed adapter and its database handle are no longer pinned to `process` for the lifetime of the run — the same treatment #7494 gave the sql.js adapter. ([#13109](https://github.com/diegosouzapw/OmniRoute/pull/13109)) — thanks @anhtahaylove +- **fix(translator):** `contentSchema` and `unevaluatedItems` are now treated as subschema positions by the tool-schema sanitizer, so a truncation placeholder in either is replaced with a permissive schema instead of being forwarded as a string ([#13110](https://github.com/diegosouzapw/OmniRoute/pull/13110)) — thanks @ntdatt812 +- **fix(cli-helper):** clear the `createLogStream` timeout on the abort path — `stop()` aborts the in-flight fetch and returned through the `signal.aborted` branch, which skipped `clearTimeout` and left an armed timer per stopped stream. The stream reader is now also cancelled when the read loop exits early. ([#13114](https://github.com/diegosouzapw/OmniRoute/pull/13114)) — thanks @anhtahaylove +- **fix(docs):** drop TLS-impersonation, MITM-decrypt, supply-chain attestation, and XOR-mask writeups from the public `/docs` catalog and Docker image. Files stay in git for engineers; operators who need them open the repo, not the website. ([#13136](https://github.com/diegosouzapw/OmniRoute/pull/13136)) — thanks @HouMinXi +- **fix(combo):** parse numeric-epoch `rate_limited_until` in the combo cooldown read path ([#13141](https://github.com/diegosouzapw/OmniRoute/pull/13141)) — thanks @maxmad64bis +- **fix(opencode):** both OpenCode plugins now reject a gateway address typed without `http://` at configuration time, instead of publishing every model with an api url no client can call, and the v2 plugin no longer publishes a model card whose api url is blank or relative ([#13142](https://github.com/diegosouzapw/OmniRoute/pull/13142)) — thanks @maxmad64bis +- **fix(providers):** lock opencode model on upstream 400 model-unavailable ([#13146](https://github.com/diegosouzapw/OmniRoute/pull/13146)) — thanks @maxmad64bis +- **fix(logging):** keep the provider exchange rather than the raw client bodies when a call log exceeds its size budget, and show that recovered payload in the request-detail panel instead of replacing it with the stored response body ([#13147](https://github.com/diegosouzapw/OmniRoute/pull/13147)) — thanks @maxmad64bis +- **fix(traffic-inspector):** the WebSocket route no longer leaks a traffic-buffer subscriber and a 30s ping timer when the client socket is already closed at handler time — listeners are attached before any resource is acquired, a destroyed socket bails out early, and the ping interval stops on a dead socket where `write()` never throws ([#13155](https://github.com/diegosouzapw/OmniRoute/pull/13155)) — thanks @anhtahaylove +- **fix(telegram):** bound the per-user API key cache in the Telegram chat proxy so a burst of distinct chat ids can no longer grow the process heap without limit ([#13165](https://github.com/diegosouzapw/OmniRoute/issues/13165)) ([#13166](https://github.com/diegosouzapw/OmniRoute/pull/13166)) — thanks @anhtahaylove +- **fix(stream):** cancel the upstream response body when the JSON-to-SSE sniff unwinds on a body timeout, so a stalled upstream no longer pins the connection ([#13169](https://github.com/diegosouzapw/OmniRoute/issues/13169)) ([#13171](https://github.com/diegosouzapw/OmniRoute/pull/13171)) — thanks @anhtahaylove +- **fix(telegram):** authenticate webhook deliveries with Telegram's `secret_token` so an unauthenticated caller can no longer mint API keys or spend upstream quota ([#13172](https://github.com/diegosouzapw/OmniRoute/issues/13172)) ([#13175](https://github.com/diegosouzapw/OmniRoute/pull/13175)) — thanks @anhtahaylove +- **fix(models):** give discoverable Gemini 3.8 Flash ids their own 65536 output spec so Antigravity no longer clamps them to 16384 ([#13195](https://github.com/diegosouzapw/OmniRoute/pull/13195)) — thanks @HouMinXi +- **fix(dashboard):** leftover catalog-removed provider rows (gemini-cli) can be listed and purged from the providers page ([#13067](https://github.com/diegosouzapw/OmniRoute/issues/13067)) ([#13197](https://github.com/diegosouzapw/OmniRoute/pull/13197)) — thanks @HouMinXi +- **fix(auth):** a dashboard session now requires the `authenticated: true` claim that login, OIDC and the session refresh already emit — a JWT merely signed with `JWT_SECRET` (for example the Cursor CLI passthrough token, which any API-key holder can obtain) no longer verifies as the `auth_token` cookie on any route, the WebSocket handshake or the live server; existing sessions keep working ([#13298](https://github.com/diegosouzapw/OmniRoute/issues/13298)) ([#13375](https://github.com/diegosouzapw/OmniRoute/pull/13375)) +- **fix(api):** `DELETE /v1/batches/delete-completed` now applies the caller's API-key policy (endpoint allowlist, schedule, usage cap, rate limit) like every other `/v1` route, commits the key-scoped sweep per 200-batch chunk instead of holding one write lock for the whole sweep, refuses to loop without progress, and rejects a scope that names both a key and `allTenants` ([#13684](https://github.com/diegosouzapw/OmniRoute/pull/13684)) +- Restore the API-route typecheck gate: the non-streaming leg result lost its discriminated-union narrowing after the server-owned tool loop reassignment, producing 13 new TS2339 diagnostics in `chatCore.ts`. ([#12963](https://github.com/diegosouzapw/OmniRoute/pull/12963)) +- Restore four non-streaming behaviours the server-owned tool loop refactor dropped: client aborts map to 499 with the fixed `Request aborted` message, an aborted request no longer logs a synthetic `clientResponse`, Claude prompt-cache telemetry is recorded again, and a body that cannot be canonicalized no longer throws when the tool loop is off. ([#12990](https://github.com/diegosouzapw/OmniRoute/pull/12990)) +- **fix(skills):** The CLI registry parser now reads positionals declared with `.addArgument()`, not only those written inline in `.command()`. `tunnel create [type]` was being published as `tunnel create`, so the agent-skills sync gate reported drift on every branch and regenerating would have deleted the argument. ([#13009](https://github.com/diegosouzapw/OmniRoute/pull/13009)) — thanks @ntdatt812 +- **fix(combos):** dashboard combo test uses a short prompt, serial probes, and a 60s timeout so reasoning models and rate-limited free pools do not fail the health check ([#13001](https://github.com/diegosouzapw/OmniRoute/pull/13001)) — thanks @HouMinXi +- fix(compression): terminate idle worker threads on eviction so long-running instances stop leaking OS threads and MessagePorts ([#13091](https://github.com/diegosouzapw/OmniRoute/pull/13091)) — thanks @anhtahaylove +- Redact Google API keys of any length in error bodies: the pattern required exactly 39 characters, so shorter or longer `AIza…` credentials passed through unredacted. ([#12964](https://github.com/diegosouzapw/OmniRoute/pull/12964)) +- **fix(grok-cli):** a 402 "Grok Build usage balance exhausted" parks that Grok login as out of credit (Grok Build CLI, grok.com cookie, and xAI OAuth share the weekly pool). Combo routing then tries the next login instead of locking the model for every account in the pool ([#13061](https://github.com/diegosouzapw/OmniRoute/pull/13061)) — thanks @HouMinXi +- fix(compression): spawn the LLMLingua worker with a file URL object so compression actually runs instead of silently failing open on Node ([#13093](https://github.com/diegosouzapw/OmniRoute/pull/13093)) — thanks @anhtahaylove +- **fix(test):** run the local `test` and `test:unit` scripts at concurrency 4 so a full-suite run no longer exhausts the machine's commit charge and kills unrelated processes ([#13187](https://github.com/diegosouzapw/OmniRoute/pull/13187)) — thanks @anhtahaylove +- **fix(monitoring):** `GET /api/monitoring/health` `credentialHealth` now includes a bounded `failedConnections` list (`connectionId`, `status`, sanitized `lastError`) when the probe-cache gauge `failed>0`, plus `source: probe-cache` and a cheap `staleDbNonOkCount` for sticky SQLite `test_status` on active rows. Documents that the live gauge is not `provider_connections.test_status`. ([#12876](https://github.com/diegosouzapw/OmniRoute/pull/12876)) — thanks @RaviTharuma +- **fix(resilience):** an openai-compatible multi-upstream gateway no longer marks the whole connection `credits_exhausted` when a single upstream (or the `/models` / representative-model health probe) returns 402. Quota failures stay model-scoped; true connection-wide auth failures are unchanged. Single-credential 402 key disable ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239)) is preserved. ([#12875](https://github.com/diegosouzapw/OmniRoute/pull/12875)) — thanks @RaviTharuma +- **fix(dashboard):** nine fixes on the Orchestration Canvas, all diagnosed in the Phase 2 reviews. A source that fails now keeps the timestamp of its FIRST failure instead of being re-stamped every poll — the stale line said "since the last poll" no matter how long the source had been down, and the churn also defeated the snapshot's stable identity (it serializes the source list), so the canvas re-rendered on every tick while anything was broken. A source that HAD data and only then started failing is flagged too: previously only a source with no node at all got the warning, so a source that went down mid-session kept a healthy-looking node forever. The rest are pointwise: clicking a filter chip cancels the pending search debounce (left armed, it fired ~300ms later and silently reverted the chip); the search input carries an accessible name; `?state=running, failed` parses like the unpadded form instead of dropping the padded value; the CSV toggle helper is defined once in `model/urlParams.ts` rather than twice; the agents tab tells "nothing running" apart from "the filter matched nothing", offering a clear-filters button instead of setup links that would be wrong advice there; edges stop emitting SMIL particles above 40 simultaneously active edges, keeping the colored stroke; and the drawer's error banner clears when a retried action succeeds. ([#12983](https://github.com/diegosouzapw/OmniRoute/pull/12983)) +- **fix(i18n):** the nine locales added with the EU-language batch (Greek, Estonian, Irish, Croatian, Lithuanian, Latvian, Maltese, Slovenian, Serbian) were missing the eleven Orchestration Canvas keys that Phase 3 introduced, so the compare-runs panel and the "no runs match these filters" empty state fell back to English in those languages (`deepMergeFallback` substitutes English for an absent key, so nothing rendered blank — it rendered untranslated). The coverage gate does not catch this: it enforces an 80% floor per locale, and eleven missing keys out of ~13,000 leaves coverage at 99.9%. Translated for real in each language, calibrated against the wording each file already uses for "run", "filter" and "skill". ([#13294](https://github.com/diegosouzapw/OmniRoute/pull/13294)) +- **fix(dashboard):** five follow-ups from the Phase 2 Orchestration Canvas review. The History drawer now shows the memory section for runs that already left the live TTL window: the persisted `memory_hits` event is parsed into `metadata.memoryHits` with the same defensive validation the drawer applies, and — because that event is observability rather than a state transition — it no longer leaks into the timeline, where it had been inheriting the task's state and rendering as a duplicate transition. The A2A memory recall runs against its own 1.5s deadline instead of inheriting the memory backend's 30s one; overshooting degrades exactly like any other recall failure (no hits, task proceeds), and the timer is cleared on both paths. Repeating a Conductor run carries its `requirements.cli`/`requirements.model` forward, so the new run is pinned to the same runner profile and model rather than drifting to whatever the fleet picks. A successful repeat from the Agents tab now focuses the run it created, instead of leaving the operator on the finished one. And the auth test for `POST /api/conductor/tasks` moved into the shared `ROUTES` array rather than restating the pattern. ([#12988](https://github.com/diegosouzapw/OmniRoute/pull/12988)) +- fix(plugins): stop leaking an exit listener per plugin hook timeout, which triggered MaxListenersExceededWarning on plugins that ignore SIGTERM ([#13092](https://github.com/diegosouzapw/OmniRoute/pull/13092)) — thanks @anhtahaylove +- **fix(validation):** Provider node edits no longer fail with a generic "Invalid request" when the optional daily-quota reset fields are left blank. The dashboard sends `dailyQuotaResetTimezone` and `dailyQuotaResetHour` as `null`, and only the hour accepted it. ([#13066](https://github.com/diegosouzapw/OmniRoute/issues/13066)) ([#13083](https://github.com/diegosouzapw/OmniRoute/pull/13083)) — thanks @ntdatt812 +- **fix(combo):** quota-aware expansion drops banned, inactive, missing, and wrong-provider connections before quota fetch or model dispatch; pins and allowlists stay selectors, not a bypass. Antigravity automatic exhaustion now requires a reported zero remaining, so a positive balance below 1% stays eligible. ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- Fix an intermittent failure in the quota-weighted routing test suite: scores are a function of `Date.now()`, so peers with identical quota scored microseconds apart never tied and swapped order. ([#12946](https://github.com/diegosouzapw/OmniRoute/pull/12946)) +- fix(volcengine): resolve quota window mapping gaps, safe multi-connection console binding, and response cookie sanitization ([#12950](https://github.com/diegosouzapw/OmniRoute/pull/12950)) — thanks @HouMinXi +- **fix(test):** resolve the WebDAV handler path with `fileURLToPath` so the suite's 37 WebDAV tests run on Windows instead of failing with a doubled `C:\C:\` drive prefix ([#13196](https://github.com/diegosouzapw/OmniRoute/pull/13196)) — thanks @anhtahaylove +- **fix(ui):** fall back from invalid AnySearch icon alias (direct commit `9587e07b69`) +- **fix(sse):** stop rebuilding a truncated summary from the collector's cap-dropped event array ([#12718](https://github.com/diegosouzapw/OmniRoute/pull/12718)) — thanks @hartmark +- **fix(conversations):** show a pending spinner for unresolved tool nodes instead of "(empty)" ([#12727](https://github.com/diegosouzapw/OmniRoute/pull/12727)) — thanks @hartmark +- **fix(streaming):** fail fast when an upstream stream produces only lifecycle/heartbeat events, never real content ([#12741](https://github.com/diegosouzapw/OmniRoute/pull/12741)) — thanks @hartmark +- **fix(responses-continuation):** bridge the write-in-flight window with an in-memory pending store ([#12854](https://github.com/diegosouzapw/OmniRoute/pull/12854)) — thanks @hartmark +- **fix(translator):** drop or map agent_message on Chat Completions fallback ([#12880](https://github.com/diegosouzapw/OmniRoute/pull/12880)) — thanks @RaviTharuma +- **fix(api):** page and stream-complete GET /v1/models for large catalogs ([#12882](https://github.com/diegosouzapw/OmniRoute/pull/12882)) — thanks @RaviTharuma +- **fix(quota):** align AUTH window labels with usage API and clarify 503 ([#12884](https://github.com/diegosouzapw/OmniRoute/pull/12884)) — thanks @RaviTharuma +- **fix(ci):** clear two base-reds on release/v3.8.51 — mutation-coverage gate + image-only-model guard ([#12945](https://github.com/diegosouzapw/OmniRoute/pull/12945)) +- **fix(api):** scope batch bulk-delete to the calling API key ([#13211](https://github.com/diegosouzapw/OmniRoute/pull/13211)) +- **fix(tests):** align model-sync log assertion to shipped path redaction (base-red) ([#13227](https://github.com/diegosouzapw/OmniRoute/pull/13227)) +- **fix(test):** make the ToS heading guard actually require the parentheses ([#13228](https://github.com/diegosouzapw/OmniRoute/pull/13228)) +- **fix(quality):** register 4 drifted covering tests in stryker tap.testFiles ([#13229](https://github.com/diegosouzapw/OmniRoute/pull/13229)) +- **fix(logging):** mask *-api-key headers in request-log pipeline (#13273) ([#13401](https://github.com/diegosouzapw/OmniRoute/pull/13401)) — thanks @KooshaPari +- **fix(tests):** correct stale Jina catalog prefix in custom-models test ([#13403](https://github.com/diegosouzapw/OmniRoute/pull/13403)) — thanks @KooshaPari +- **fix(backend):** prune old backups after health-check-repair snapshots (#13308) ([#13404](https://github.com/diegosouzapw/OmniRoute/pull/13404)) — thanks @KooshaPari +- **fix(backend):** guard memory_fts_au trigger with WHEN clause to stop FTS bloat (#13326) ([#13405](https://github.com/diegosouzapw/OmniRoute/pull/13405)) — thanks @KooshaPari +- **fix(free-tiers):** remove obsolete Together $25 signup credit (#12526) ([#13407](https://github.com/diegosouzapw/OmniRoute/pull/13407)) — thanks @KooshaPari +- **fix:** skip non-chat generation models in Test all models ([#13409](https://github.com/diegosouzapw/OmniRoute/pull/13409)) — thanks @KooshaPari +- **fix(providers):** de-duplicate agy and antigravity model catalogs ([#13410](https://github.com/diegosouzapw/OmniRoute/pull/13410)) — thanks @KooshaPari +- **fix(dashboard):** send provider in body for mini-playground web search ([#13411](https://github.com/diegosouzapw/OmniRoute/pull/13411)) — thanks @KooshaPari +- **fix(resilience):** add CJK quota-exhaustion patterns to 429 classifier ([#13412](https://github.com/diegosouzapw/OmniRoute/pull/13412)) — thanks @KooshaPari +- **fix(backend):** eval runner: failed calls no longer score as passed + dotAll regex ([#13413](https://github.com/diegosouzapw/OmniRoute/pull/13413)) — thanks @KooshaPari +- **fix(backend):** eval runner opts out of output-style and memory injection ([#13414](https://github.com/diegosouzapw/OmniRoute/pull/13414)) — thanks @KooshaPari +- **fix:** chain serial quarantine step in npm test script (fixes #13286) ([#13418](https://github.com/diegosouzapw/OmniRoute/pull/13418)) — thanks @KooshaPari +- **fix(antigravity):** derive stable session ID from credentials for prompt-cache reuse (#13305) ([#13424](https://github.com/diegosouzapw/OmniRoute/pull/13424)) — thanks @KooshaPari +- **fix(memory):** merge memory into top-level system field instead of unshifting at messages[0] (#13425) ([#13427](https://github.com/diegosouzapw/OmniRoute/pull/13427)) — thanks @KooshaPari +- **fix(compression):** preserve instruction blocks from lossy engines ([#13523](https://github.com/diegosouzapw/OmniRoute/pull/13523)) — thanks @KooshaPari +- **fix(compression):** preserve polarity words and newlines in ultra heuristic ([#13524](https://github.com/diegosouzapw/OmniRoute/pull/13524)) — thanks @KooshaPari +- **fix(compression):** run user preservePatterns before built-in patterns (#13457) ([#13525](https://github.com/diegosouzapw/OmniRoute/pull/13525)) — thanks @KooshaPari +- **fix(reasoning):** expand deepseek-v4 regex to match v4.1+ and add command-code provider (#13430) ([#13528](https://github.com/diegosouzapw/OmniRoute/pull/13528)) — thanks @KooshaPari +- **fix(providers):** add alwaysReasons metadata for reka-flash-3 ([#13531](https://github.com/diegosouzapw/OmniRoute/pull/13531)) — thanks @KooshaPari +- **fix(playground):** allow Compare columns to scroll independently ([#13532](https://github.com/diegosouzapw/OmniRoute/pull/13532)) — thanks @KooshaPari +- **fix(security):** empty whitelist allows through to prevent admin lockout ([#13534](https://github.com/diegosouzapw/OmniRoute/pull/13534)) — thanks @KooshaPari +- **fix(tests):** add DISABLE_IOREG_STRATEGY env var for macOS test compatibility (fixes #13467) ([#13539](https://github.com/diegosouzapw/OmniRoute/pull/13539)) — thanks @KooshaPari +- **fix(zed-hosted):** map developer role and Gemini enums for Zed proxy ([#13543](https://github.com/diegosouzapw/OmniRoute/pull/13543)) — thanks @KooshaPari +- **fix(analytics):** normalize provider aliases to canonical id in usage_history ([#13545](https://github.com/diegosouzapw/OmniRoute/pull/13545)) — thanks @KooshaPari +- **fix(combo):** use traceId for call log id to prevent UNIQUE constraint failure ([#13546](https://github.com/diegosouzapw/OmniRoute/pull/13546)) — thanks @KooshaPari +- **fix(auth):** check model lockout before returning synthetic noauth connection ([#13547](https://github.com/diegosouzapw/OmniRoute/pull/13547)) — thanks @KooshaPari +- **fix(guardrails):** skip credential redaction for base64 image data URLs ([#13550](https://github.com/diegosouzapw/OmniRoute/pull/13550)) — thanks @KooshaPari +- **fix(resilience):** increase requestQueue.maxWaitMs default from 15s to 30s ([#13553](https://github.com/diegosouzapw/OmniRoute/pull/13553)) — thanks @KooshaPari +- **fix(docs):** document OMNIROUTE_READY_TIMEOUT_MS and allowlist the test-only DISABLE_IOREG_STRATEGY ([#13692](https://github.com/diegosouzapw/OmniRoute/pull/13692)) ### 📝 Maintenance @@ -853,6 +1054,39 @@ _By commits in `release/v3.8.50..release/v3.8.51` (`091589089c..d6f315018a`), au - **docs(security):** document socket.yml scanner config + CI workflow link (#12575) ([#12764](https://github.com/diegosouzapw/OmniRoute/pull/12764)) — thanks @KooshaPari - **chore(ci):** guard commit identity in pre-commit to stop author misattribution ([#12772](https://github.com/diegosouzapw/OmniRoute/pull/12772)) - **deps:** 17 Dependabot bumps — bump electron from 43.4.0 to 43.4.1 in /electron ([#11426](https://github.com/diegosouzapw/OmniRoute/pull/11426)); bump the development group across 1 directory with 8 updates ([#11428](https://github.com/diegosouzapw/OmniRoute/pull/11428)); bump the production group across 1 directory with 11 updates ([#11440](https://github.com/diegosouzapw/OmniRoute/pull/11440)); chore(deps): bump github/codeql-action/init from 4.37.7 to 4.37.8 ([#11925](https://github.com/diegosouzapw/OmniRoute/pull/11925)); chore(deps): bump github/codeql-action from 4.37.7 to 4.37.8 ([#11926](https://github.com/diegosouzapw/OmniRoute/pull/11926)); chore(deps): bump github/codeql-action/analyze from 4.37.7 to 4.37.8 ([#11927](https://github.com/diegosouzapw/OmniRoute/pull/11927)); build(deps): bump github/codeql-action/init from 4.37.8 to 4.37.9 ([#12345](https://github.com/diegosouzapw/OmniRoute/pull/12345)); build(deps): bump github/codeql-action/analyze from 4.37.8 to 4.37.9 ([#12346](https://github.com/diegosouzapw/OmniRoute/pull/12346)); bump the development group across 1 directory with 2 updates ([#12347](https://github.com/diegosouzapw/OmniRoute/pull/12347)); build(deps): bump github/codeql-action from 4.37.8 to 4.37.9 ([#12349](https://github.com/diegosouzapw/OmniRoute/pull/12349)); bump browserslist from 4.28.2 to 4.28.8 ([#12396](https://github.com/diegosouzapw/OmniRoute/pull/12396)); bump the production group across 1 directory with 4 updates ([#12399](https://github.com/diegosouzapw/OmniRoute/pull/12399)); bump @xmldom/xmldom ([#12500](https://github.com/diegosouzapw/OmniRoute/pull/12500)); bump qs from 6.15.2 to 6.16.0 ([#12512](https://github.com/diegosouzapw/OmniRoute/pull/12512)); bump @xmldom/xmldom from 0.9.10 to 0.9.12 ([#12513](https://github.com/diegosouzapw/OmniRoute/pull/12513)); bump fast-uri from 3.1.5 to 3.1.7 ([#12514](https://github.com/diegosouzapw/OmniRoute/pull/12514)); bump @humanfs/node from 0.16.7 to 0.16.8 ([#12515](https://github.com/diegosouzapw/OmniRoute/pull/12515)) +- Clear the `release/v3.8.51` `check:agent-skills-sync` base-red: regenerate `skills/cli-tunnel/SKILL.md` so the `tunnel create [type]` positional that #13009 taught the generator to read is reflected in the committed skill. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` typecheck base-red from #12731: the new `Mode pack ranking gates` candidates in `open-sse/services/autoCombo/__tests__/autoCombo.test.ts` omitted the required `provider`, `model` and `errorRate` fields of `ProviderCandidate`, failing `check:open-sse-typecheck` (and with it `Fast Quality Gates`) on every open PR. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` docs-sync base-red: EURouter (#13025) and GreenPT (#13024) took the live provider count to 358, leaving 7 STRICT drifts (`PROVIDER_REFERENCE.md`, 4 diagrams, 2 tier-flow images) plus the `AGENTS.md` / `llm.txt` / `package.json` count claims stale. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- **test(models):** Pin `CATALOG_BUILD_TIMEOUT_MS` in the #12058 canonical-catalog regression so a cold catalog build racing the 8s default bound no longer flakes the suite with a 500 ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Allow `@omniroute/opencode-plugin-v2/` in the npm pack policy — [#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870) shipped the v2 plugin beside its v1 sibling without widening the allowlist, so every packed file under it read as an unexpected artifact and failed `check:pack-policy` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Point the release-green validator's pack gate at the tree it is validating — the build-provenance guard defaulted to `origin/main`, which a release branch tip can never be an ancestor of mid-cycle, so `Package artifact` reported a false red on every run and the tarball boot-smoke cascaded off it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **docs(checks):** keep doc counts honest — headings, rankings, catalog, weights, quality gate and scoring diagram now covered ([#12507](https://github.com/diegosouzapw/OmniRoute/pull/12507)) — thanks @maxmad64bis +- **chore(lifecycle):** `check:model-lifecycle` now also diffs `DEFAULT_DEGRADATION_MAP` (the background-task redirect table) against the vendor lifecycle snapshot, refusing a retired id as source or target, with a table-driven unit test beside it. Three rows whose source the vendor had retired — `claude-sonnet-4-20250514`, `gemini-3-pro-preview` and `gpt-5.1-codex` (whose target `gpt-5.1-codex-mini` is retired too) — were dead code, since `checkLifecycle` answers 410 before the redirect runs; they are dropped (#12535 — thanks @pacocartones) +- **test(compression):** cover idle worker eviction at the resource level — the pool must call `terminate()` and must not retain the worker's `MessagePort`, complementing the `exit`-event assertion added with the fix ([#13371](https://github.com/diegosouzapw/OmniRoute/pull/13371)) +- **chore(quality):** raise the `imageGeneration.ts` file-size ceiling for the image-only-model guard that clears the #12945 base-red ([#13392](https://github.com/diegosouzapw/OmniRoute/pull/13392)) +- **chore(quality):** type the combo-test route's JSON response bodies instead of casting them to `any`, drop the now-empty suppression entry, and remove the `hasPerModelQuota` import `chatCore.ts` stopped using when the failure-classification helper was extracted ([#13379](https://github.com/diegosouzapw/OmniRoute/pull/13379)) +- **chore(electron):** the desktop bundle no longer ships the root-level files of every translated docs mirror (`README.md`, `llm.txt`, `CLAUDE.md`, `GEMINI.md`, `CONTRIBUTING.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`) — the packaged app only reads `docs/i18n//docs/**`, which stays. Saves ~11 MB on top of the translated CHANGELOGs already pruned. (#0000) ([#13235](https://github.com/diegosouzapw/OmniRoute/pull/13235)) +- **chore(quality):** retighten the `src/sse/handlers/chat.ts` unused-vars suppression count to the 8 violations that actually remain ([#13385](https://github.com/diegosouzapw/OmniRoute/pull/13385)) +- Bring the documented counts back in line with the code: 171 migrations (was 169) and 20 routing strategies (was 19), plus the regenerated `cli-tunnel` skill reference. ([#12970](https://github.com/diegosouzapw/OmniRoute/pull/12970)) +- Update the documented migration count to 172 after the call-logs provider-stats indexes landed. ([#13209](https://github.com/diegosouzapw/OmniRoute/pull/13209)) +- **chore(ci):** two gates that close the blind spots behind the exclusions above. `check:vitest-exclusions` requires every Vitest exclusion to name a tracking issue and to appear in `config/quality/vitest-exclusions.json` — the previous list grew to 62 files behind a comment pointing at an issue that had been closed for a month. `check-new-key-coverage` requires a key newly added to `en.json` to reach every locale; the existing coverage gate is a percentage floor per locale, so eleven absent keys out of ~13,000 left it at 99.9% while a whole feature shipped untranslated in nine languages. Both are diff-aware, so pre-existing debt stays frozen and neither needed a migration to turn on. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **build:** root layout no longer loads Inter from `next/font/google`, so a production image build does not need fonts.googleapis.com ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **docs(i18n):** bring the eight new `llm.txt` mirrors (km, kn, ml, my, ne, or, pa, si) up to the current provider and migration counts, so `check-docs-sync` stops rejecting every commit ([#13674](https://github.com/diegosouzapw/OmniRoute/pull/13674)) +- **refactor(ui):** flow surfaces (home topology, combo live studio, compression cockpit/waterfall/nodes, token-health badge) express state through the theme-aware `--orch-status-*` tokens instead of fixed dark-mode hex, so green/red/amber/grey stay legible in the light theme; dark mode is byte-identical. Categorical palettes (routing-strategy hues, compression-layer pills, provider brand colors) deliberately stay hex ([#12378](https://github.com/diegosouzapw/OmniRoute/issues/12378)) ([#13203](https://github.com/diegosouzapw/OmniRoute/pull/13203)) +- **chore(tests):** 51 test files that had been excluded from Vitest are running again, restoring roughly 350 assertions to the blocking suite. Each was measured individually first: of the 62 files parked behind the `// #8618 — pre-existing failure` comment, 51 pass against the current tree with no source change, so the exclusions had outlived the failures they were added for. The 11 that genuinely still fail stay excluded, but now point at a live tracker (#13204) rather than at #8618, which was closed in August while the list it tracked kept growing. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **chore(quality):** drop two ESLint suppression entries whose violations no longer exist, so `eslint --suppressions-location` stops rejecting every commit that touches the surrounding files ([#13384](https://github.com/diegosouzapw/OmniRoute/pull/13384)) +- **chore(release):** bring main's post-tag fixes into release/v3.8.51 — sync-back step 2/2 (direct commit `21c488f210`) +- **chore(deps):** patch toml and esbuild in the opencode plugin lockfiles ([#12965](https://github.com/diegosouzapw/OmniRoute/pull/12965)) +- **docs(changelog):** reconcile the v3.8.51 living section — fold 366 fragments, cover every cycle commit, credit every contributor ([#12971](https://github.com/diegosouzapw/OmniRoute/pull/12971)) +- **test(plugin):** wait for the v2 lazy-refresh tier instead of sleeping past it ([#12984](https://github.com/diegosouzapw/OmniRoute/pull/12984)) +- **chore(quality):** rebaseline chatCore.ts after the non-streaming regression fixes ([#13045](https://github.com/diegosouzapw/OmniRoute/pull/13045)) +- **docs(changelog):** add the missing bullet marker to the DeepSeek PoW fragment ([#13200](https://github.com/diegosouzapw/OmniRoute/pull/13200)) +- **chore(deps):** drain the Dependabot queue — 10 of 13 alerts ([#13213](https://github.com/diegosouzapw/OmniRoute/pull/13213)) +- **chore(ci):** make worktrees carry the hooks that enforce the identity gate ([#13236](https://github.com/diegosouzapw/OmniRoute/pull/13236)) +- **chore(deps):** pin csv-parse to ^7.0.2 via overrides (#13117) ([#13300](https://github.com/diegosouzapw/OmniRoute/pull/13300)) — thanks @KooshaPari +- **chore(deps):** bump hono from ^4.12.34 to ^4.13.7 (#13148) ([#13301](https://github.com/diegosouzapw/OmniRoute/pull/13301)) — thanks @KooshaPari +- **chore(deps):** pin joi to ^18.2.8 via overrides (#13085) ([#13302](https://github.com/diegosouzapw/OmniRoute/pull/13302)) — thanks @KooshaPari +- **docs(providers):** add Microsoft 365 Copilot (BizChat) provider guide (#12779) ([#13340](https://github.com/diegosouzapw/OmniRoute/pull/13340)) — thanks @KooshaPari ### 🙌 Contributors @@ -870,6 +1104,7 @@ Thanks to everyone whose work landed in v3.8.51: | [@amaleta](https://github.com/amaleta) | #11906, #11934 | | [@amartinawi](https://github.com/amartinawi) | #11575 | | [@AndrianBalanescu](https://github.com/AndrianBalanescu) | #11083, #11830, #11834, #11844 | +| [@anhtahaylove](https://github.com/anhtahaylove) | #13091, #13092, #13093, #13096, #13097, #13100, #13106, #13109, #13114, #13155, #13166, #13171, #13175, #13187, #13196 | | [@aniruddhaadak80](https://github.com/aniruddhaadak80) | #11760 | | [@app](https://github.com/app) | #12554 | | [@arminanton](https://github.com/arminanton) | #11461, #11513 | @@ -899,10 +1134,10 @@ Thanks to everyone whose work landed in v3.8.51: | [@gonisulaimann](https://github.com/gonisulaimann) | #12368, #12369, #12371 | | [@Gorillaz322](https://github.com/Gorillaz322) | #12207 | | [@groovecityJO](https://github.com/groovecityJO) | #12682 | -| [@hartmark](https://github.com/hartmark) | #11434, #11452, #11473, #11499, #11703, #11983, #11984, #11985, #11986, #11988, #11989, #11990, #11994, #12221, #12293, #12445, #12446, #12447, #12448, #12460, #12461, #12623, #12646, #12650, #12680 | +| [@hartmark](https://github.com/hartmark) | #11434, #11452, #11473, #11499, #11703, #11983, #11984, #11985, #11986, #11988, #11989, #11990, #11994, #12221, #12293, #12445, #12446, #12447, #12448, #12460, #12461, #12623, #12646, #12650, #12680, #12717, #12718, #12727, #12741, #12854 | | [@hizzt](https://github.com/hizzt) | #11894 | | [@hongnoul](https://github.com/hongnoul) | #11484 | -| [@HouMinXi](https://github.com/HouMinXi) | #11411, #11414, #11512, #11518, #11520, #11641, #11642, #11643, #11687, #11779, #11849, #11850, #11851, #11915, #11916, #11918, #11919, #11920, #12013, #12017, #12033, #12042, #12106, #12139, #12166, #12169, #12171, #12205, #12213, #12312, #12325, #12487, #12488, #12495, #12504, #12557, #12566, #12590, #12591, #12624, #12626, #12632, #12637, #12678, #12696, #12697, #12711, #12733, #12746, #12767, #12770, #12789, #12803, #12805, #12811, #12866, #12868, #12899, #12926, #12934 | +| [@HouMinXi](https://github.com/HouMinXi) | #11411, #11414, #11512, #11518, #11520, #11641, #11642, #11643, #11687, #11779, #11849, #11850, #11851, #11915, #11916, #11918, #11919, #11920, #12013, #12017, #12033, #12042, #12106, #12139, #12166, #12169, #12171, #12205, #12213, #12312, #12325, #12487, #12488, #12495, #12504, #12557, #12566, #12590, #12591, #12624, #12626, #12632, #12637, #12678, #12696, #12697, #12711, #12733, #12746, #12767, #12770, #12789, #12803, #12805, #12811, #12866, #12868, #12899, #12926, #12934, #12950, #12951, #12974, #13001, #13006, #13011, #13017, #13026, #13027, #13034, #13035, #13038, #13042, #13050, #13060, #13061, #13069, #13107, #13120, #13136, #13195, #13197 | | [@Hsia97](https://github.com/Hsia97) | #11624 | | [@jacobsparts](https://github.com/jacobsparts) | #11854, #12155, #12167 | | [@joglomedia](https://github.com/joglomedia) | #11980 | @@ -912,11 +1147,11 @@ Thanks to everyone whose work landed in v3.8.51: | [@KaspaPulse](https://github.com/KaspaPulse) | #11389, #11469, #11628, #11666, #11910 | | [@keeltrace](https://github.com/keeltrace) | #12080, #12223 | | [@killer30001000](https://github.com/killer30001000) | #12178 | -| [@KooshaPari](https://github.com/KooshaPari) | #12470, #12592, #12667, #12699, #12703, #12706, #12764, #12769, #12771 | +| [@KooshaPari](https://github.com/KooshaPari) | #12470, #12592, #12667, #12699, #12703, #12706, #12764, #12769, #12771, #13300, #13301, #13302, #13340, #13401, #13403, #13404, #13405, #13406, #13407, #13409, #13410, #13411, #13412, #13413, #13414, #13418, #13424, #13427, #13433, #13523, #13524, #13525, #13528, #13531, #13532, #13534, #13539, #13543, #13545, #13546, #13547, #13550, #13553 | | [@kriptoburak](https://github.com/kriptoburak) | #11370 | | [@KrzysiekSko](https://github.com/KrzysiekSko) | #12425, #12432, #12673 | | [@marcelokarval](https://github.com/marcelokarval) | direct commit / report | -| [@maxmad64bis](https://github.com/maxmad64bis) | #1622, #11435, #11437, #11441, #11537, #11550, #11553, #11555, #11557, #11812, #11842, #11843, #11903, #12151, #12214, #12215, #12218, #12226, #12314, #12316, #12317, #12318, #12319, #12320, #12321, #12870 | +| [@maxmad64bis](https://github.com/maxmad64bis) | #1622, #11435, #11437, #11441, #11537, #11550, #11553, #11555, #11557, #11812, #11842, #11843, #11903, #12151, #12214, #12215, #12218, #12226, #12314, #12316, #12317, #12318, #12319, #12320, #12321, #12507, #12715, #12731, #12744, #12786, #12787, #12788, #12790, #12792, #12794, #12795, #12828, #12832, #12853, #12857, #12870, #12937, #12941, #12975, #13141, #13142, #13146, #13147 | | [@mdigitalbh81](https://github.com/mdigitalbh81) | #12240 | | [@Meet6338-X](https://github.com/Meet6338-X) | #11598, #11609, #12110 | | [@morpheus9393](https://github.com/morpheus9393) | #11943 | @@ -925,11 +1160,11 @@ Thanks to everyone whose work landed in v3.8.51: | [@NightStalker-87](https://github.com/NightStalker-87) | #12183 | | [@NoxzRCW](https://github.com/NoxzRCW) | #11879, #11880, #11881, #11882, #11883, #11935 | | [@ntdat812](https://github.com/ntdat812) | #11585, #12095 | -| [@ntdatt812](https://github.com/ntdatt812) | #11368, #11573, #11574, #11576, #11577, #11580, #11582, #11583, #11588, #11589, #11590, #11591, #11592, #11593, #11672, #12177, #12180 | +| [@ntdatt812](https://github.com/ntdatt812) | #11368, #11573, #11574, #11576, #11577, #11580, #11582, #11583, #11588, #11589, #11590, #11591, #11592, #11593, #11672, #12177, #12180, #12873, #12918, #12920, #12921, #12925, #12930, #13007, #13009, #13024, #13025, #13083, #13087, #13101, #13104, #13110 | | [@official-burak](https://github.com/official-burak) | #11542 | | [@opensource-elearning](https://github.com/opensource-elearning) | #12179, #12189, #12278, #12286 | | [@oyi77](https://github.com/oyi77) | #11408, #11409, #11421, #11505, #11677, #12036, #12110 | -| [@pacocartones](https://github.com/pacocartones) | #11521, #11522, #11527, #11528, #11529, #11530, #11531, #11532, #11533, #11534, #11595, #11599, #11603, #11604, #11605, #11607, #11610, #11676, #11714, #11716, #11718, #11767, #11838, #11860, #11862, #11869, #11871, #11872, #11873, #11903, #11906, #11921, #11934, #12359, #12360, #12361, #12362, #12364, #12365, #12373, #12374, #12375, #12376, #12377, #12379, #12380, #12381, #12386, #12387, #12389, #12390, #12394, #12395, #12397, #12401, #12403, #12404 | +| [@pacocartones](https://github.com/pacocartones) | #11521, #11522, #11527, #11528, #11529, #11530, #11531, #11532, #11533, #11534, #11595, #11599, #11603, #11604, #11605, #11607, #11610, #11676, #11714, #11716, #11718, #11767, #11838, #11860, #11862, #11869, #11871, #11872, #11873, #11903, #11906, #11921, #11934, #12356, #12358, #12359, #12360, #12361, #12362, #12364, #12365, #12373, #12374, #12375, #12376, #12377, #12379, #12380, #12381, #12386, #12387, #12389, #12390, #12394, #12395, #12397, #12401, #12403, #12404, #12522, #12523, #12535, #12536, #12540, #12541, #12543, #12545, #12548, #12549, #12550, #12551, #12552, #12647, #12651, #12653, #13055 | | [@patrykkopycinski](https://github.com/patrykkopycinski) | #11936, #11937, #12224 | | [@PauloFH](https://github.com/PauloFH) | #11509 | | [@PauloHSOliveira](https://github.com/PauloHSOliveira) | #12241 | @@ -941,7 +1176,7 @@ Thanks to everyone whose work landed in v3.8.51: | [@raheemuddin786](https://github.com/raheemuddin786) | #11491, #11839, #11840, #11841, #12003, #12230, #12231, #12232, #12233, #12234 | | [@rahilmavani](https://github.com/rahilmavani) | #11761 | | [@Rahulsharma0810](https://github.com/Rahulsharma0810) | #11771 | -| [@RaviTharuma](https://github.com/RaviTharuma) | #11710, #11727, #11797, #11798, #11802, #11805, #11806, #11811, #12098, #12099, #12101, #12449, #12452, #12472, #12473, #12493, #12533, #12607, #12628, #12631, #12636 | +| [@RaviTharuma](https://github.com/RaviTharuma) | #11710, #11727, #11797, #11798, #11802, #11805, #11806, #11811, #12098, #12099, #12101, #12449, #12452, #12472, #12473, #12493, #12533, #12607, #12628, #12631, #12636, #12875, #12876, #12880, #12882, #12884 | | [@rezjalibd](https://github.com/rezjalibd) | #12186 | | [@RhianB14](https://github.com/RhianB14) | #11692 | | [@rifqiawl](https://github.com/rifqiawl) | #11517, #11519, #11969 | diff --git a/docs/i18n/mr/CHANGELOG.md b/docs/i18n/mr/CHANGELOG.md index 6a19da293e..eba3e13e80 100644 --- a/docs/i18n/mr/CHANGELOG.md +++ b/docs/i18n/mr/CHANGELOG.md @@ -8,16 +8,1119 @@ ## [3.8.51] — TBD -_Living section — cycle opened at the v3.8.50 freeze (parallel-cycle model). Bullets are aggregated from `changelog.d/` fragments at each `/generate-release` phase._ +_Living section — reconciled 2026-09-15 from all cycle commits (`091589089c` → `c0f92ec98a`, 916 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 | **123** | +| 📝 Commits in the cycle | **916** | +| 🔀 Pull requests referenced | **892** | +| 📋 Changelog entries | **919** | +| 🙌 Contributors credited in entries | **121** | +| 🤖 Automated dependency commits | 18 | + +**Entries by type** + +| Type | Count | +| --- | ---: | +| 🐛 Fixes | 592 | +| ✨ Features | 131 | +| 🧹 Chore | 93 | +| 📚 Docs | 42 | +| 🧪 Tests | 32 | +| ♻️ Refactor | 8 | +| 🏗️ Build | 7 | +| ⚡ Performance | 7 | +| ⚙️ CI | 2 | +| ⏪ Reverts | 2 | +| 📦 Dependencies | 2 | +| 🔒 Security | 1 | + +### 🏆 Top 25 contributors this cycle + +_By commits in `091589089c..c0f92ec98a`, author identities consolidated via `.mailmap` and the merged PR's GitHub login. Bots excluded._ + +| # | Contributor | Commits | +| ---: | --- | ---: | +| 🥇 | diegosouzapw | 323 | +| 🥈 | Bob.Hou (@HouMinXi) | 79 | +| 🥉 | Paco Cartones (@pacocartones) | 77 | +| 4 | Dizzle (@maxmad64bis) | 49 | +| 5 | Koosha Paridehpour (@KooshaPari) | 43 | +| 6 | Nguyen Thanh Dat (@ntdatt812) | 31 | +| 7 | Ravi Tharuma (@RaviTharuma) | 26 | +| 8 | Markus Hartung (@hartmark) | 22 | +| 9 | Webman (@jonlwheat2-gif) | 22 | +| 10 | backryun | 17 | +| 11 | anhtahaylove | 15 | +| 12 | Rafa Martins (@rafacpti23) | 10 | +| 13 | Syed Raheemuddin (@raheemuddin786) | 10 | +| 14 | Nguyễn Viết Tuấn (@TheDemonTuan) | 8 | +| 15 | MumuTW | 7 | +| 16 | Paijo (@oyi77) | 7 | +| 17 | Tobias Andersen (@turbolego) | 6 | +| 18 | Bl0ck (@Bl0ck154) | 5 | +| 19 | SHANMUGAPRIYAN (@geek007git) | 5 | +| 20 | KaspaPulse | 5 | +| 21 | Abhishek Sharma (@abhisheksharma2411) | 4 | +| 22 | Abhishek Divekar (@adivekar-utexas) | 4 | +| 23 | Andrew B. (@AndrianBalanescu) | 4 | +| 24 | NoxzRCW | 4 | +| 25 | opensource-elearning | 4 | ### ✨ New Features +- **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). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- Added Google AI Studio Gemini batch text-to-speech support through `POST /v1/audio/speech`. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- 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. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(routing):** subscription-first auto groupings — `auto/subscription` routes only through plan-included connections with a documented hard-stop overage and fails closed on exhaustion, while `auto/thrifty` orders the pool `subscription → keyless → free → cheap → premium` and steps up one rung at a time as each is exhausted. Billing class comes from a curated per-connection catalog (uncurated is treated as metered, never plan-included), both reuse STRICT_ZERO_COST's per-connection verification, and a quota reading whose `resetAt` has passed is now refreshed regardless of TTL so routing returns to plan capacity as soon as it resets ([#11146](https://github.com/diegosouzapw/OmniRoute/pull/11146)) — thanks @yourspraveen +- **feat(providers):** publish a management-authenticated versioned web-session credential contract from OmniRoute's canonical browser credential metadata ([#11340](https://github.com/diegosouzapw/OmniRoute/pull/11340)) — thanks @Zartharas +- **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)) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add Xquik X search with typed results, credential validation, REST routing, and MCP selection ([#11370](https://github.com/diegosouzapw/OmniRoute/pull/11370)) — thanks @kriptoburak +- **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)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(dashboard):** surface durable exclusive managed leases in the existing Sessions view, keeping leased clients visible across idle gaps while marking connections with in-flight work as active ([#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389)) — thanks @KaspaPulse +- **build(bun):** allow Turbopack bundler flag on Bun 1.4+ with configurable Webpack fallback ([#11471](https://github.com/diegosouzapw/OmniRoute/pull/11471)) — thanks @TheDemonTuan +- feat(api): add an opt-in `modelVisibilityAllowlist`/`modelVisibilityDenylist` settings pair to curate exactly which models `/v1/models` advertises, mirrored into every `auto/*` combo candidate pool so a denied model cannot be routed to via combo selection either (#11481) ([#11997](https://github.com/diegosouzapw/OmniRoute/pull/11997)) +- **feat(rankings):** the Free Provider Rankings page shows what each provider actually served over the last 24 h. It ranked by ELO alone, which left a provider that answers every call with an error in first place; the usage data was already served by the API but never requested. A provider with too small a sample shows a dash, not a number ([#11546](https://github.com/diegosouzapw/OmniRoute/pull/11546)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **feat(guardrails):** enforce a bounded, deterministic contract for Video Bridge transcripts — 256 cues, 4096 input code units and 4 KiB UTF-8 per cue, 64 KiB total text, malformed-Unicode rejection, focus-window scoping, cross-source reconciliation with contributing-source metadata, and a structural provenance trust boundary so caller JSON can never self-assert `embedded`/`audio-bridge` provenance ([#11652](https://github.com/diegosouzapw/OmniRoute/issues/11652)) ([#12009](https://github.com/diegosouzapw/OmniRoute/pull/12009)) +- **feat(video):** orchestrate optional Video Bridge audio extraction and Audio Bridge STT behind a dual opt-in (operator setting AND per-request signal) — a new loopback-only broker `mode=audio` operation shares the frame path's exact process queue, deadline, AbortSignal, and byte budgets to extract a bounded mono 16 kHz PCM WAV from the same already-downloaded video, then reuses the existing Audio Bridge transcription boundary; provider segment timing is preserved when available and marked coarse otherwise, and every failure degrades to a visual-only-safe partial instead of throwing (#11654). ([#12012](https://github.com/diegosouzapw/OmniRoute/pull/12012)) +- Add a tenant-bound Video Bridge drill-down lifecycle on top of the existing secure cache substrate: opaque hashed handles (never raw session/video identifiers), preview/standard/detail multiresolution variants resampled on read, response pagination capped at 8 frames and 32 MiB, and a new authenticated `/api/v1/video-bridge/drilldown` consumer route that stays disabled for remote access by default and denies cross-key access with the same response as a nonexistent handle (no existence oracle). ([#12006](https://github.com/diegosouzapw/OmniRoute/pull/12006)) +- **test(video):** Add the Video Bridge FU-07/FU-09 promotion-evidence harness (#11656) — a frozen Zod manifest schema covering the 8 required scenario kinds (static scenes, rapid cuts, late facts, fades, blur, small text, close events, visual prompt injection) with a minimum of 3 repetitions per case, deterministic declarative fixture recipes (`videoBridgePromotionFixtures.ts`), a pure medians/p95 metrics aggregator, a pure FU-07/FU-09 promotion-verdict evaluator applying the ticket's exact thresholds (missing token usage always holds), a digest-only persistence layer that never retains raw media or raw model responses, and a versioned per-model promotion allowlist shipped empty with every model defaulting to `hold`. The FU-07/FU-09 promotion verdicts themselves remain HOLD — they require a real evidence run against real models on VPS 192.168.0.15. ([#12008](https://github.com/diegosouzapw/OmniRoute/pull/12008)) +- **feat(video bridge):** "embedded" transcript provenance can now be legitimately earned instead of merely asserted — a bounded, allowlisted (`mov_text`/`subrip`/`webvtt`) subtitle probe runs through the loopback-only Video Bridge broker (at most 2 streams, 10s subdeadline bounded by the request deadline, 256 KiB output, 4096-code-unit lines), normalized through a bounded, ReDoS-safe WebVTT parser and Zod-validated end to end. The adapter always resolves to an explicit `success`/`absent`/`transient_failure` outcome — a subtitle failure never breaks the visual description path, and only a fingerprint-verified broker response (never a caller-declared label) can produce embedded cues (#11659). ([#12011](https://github.com/diegosouzapw/OmniRoute/pull/12011)) +- Default new Antigravity-family connections (agy CLI imports and Antigravity OAuth connects) to model auto-sync, so live model discovery lands in the synced catalog and `/v1/models` picks up freshly released upstream models (e.g. Gemini 3.7 Flash tiers) without code changes. Existing connections keep their current setting; the per-connection dashboard toggle remains the opt-out. (#11685 — thanks @MumuTW) ([#11887](https://github.com/diegosouzapw/OmniRoute/pull/11887)) +- **feat(ui):** enable React Compiler (`reactCompiler: true` + `babel-plugin-react-compiler`) for automatic memoization at build time ([#11783](https://github.com/diegosouzapw/OmniRoute/pull/11783)) — thanks @jonlwheat2-gif +- **feat(providers):** add SeekAi (`seekai.cc`) as an OpenAI-compatible New-API gateway — catalog id `seekai` (alias `ska`), `https://seekai.cc/v1`, live `/v1/models` via `passthroughModels`, aggregator-list membership so New-API balance detection can opt in. No referral/aff codes. ([#11786](https://github.com/diegosouzapw/OmniRoute/issues/11786)) ([#12557](https://github.com/diegosouzapw/OmniRoute/pull/12557)) — thanks @HouMinXi +- **feat(zai):** add GLM-5.3-Flash Coding Plan support (1M context, 128K output, vision, `low|high|max` reasoning) and route `zai` GLM-5.3-family API-key traffic through the OpenAI-compatible Coding Plan endpoint with native thinking defaults ([#11801](https://github.com/diegosouzapw/OmniRoute/pull/11801)) — thanks @Neuron-Mr-White +- **feat(combo):** choose how combo models are ordered — manual, provider, score, or name — via a sort control in the dashboard builder, persisted in `config.modelSort` and re-applied on load and after add ([#11812](https://github.com/diegosouzapw/OmniRoute/pull/11812)) — thanks @maxmad64bis +- **feat(free):** custom models can be marked free-tier via `customModels[].isFree`; `isFreeModel()` is the first door and `hidePaidModels` respects it even for providers outside the free budget ([#11843](https://github.com/diegosouzapw/OmniRoute/pull/11843)) — thanks @maxmad64bis +- **feat(nodejs):** add `5dive` as a `configure` target — `omniroute configure 5dive` / `omniroute setup-5dive` write a 5dive auth profile that points an agent fleet's `claude` seats at OmniRoute, with the root-only write, the loopback-vs-`https` endpoint rule and the per-seat model pin handled explicitly ([#11852](https://github.com/diegosouzapw/OmniRoute/pull/11852)) — thanks @5dive-bot +- **feat(sse):** treat `max` as a first-class reasoning-effort tier and clamp per model family (GLM 5.1+/DeepSeek V4+/Kimi K3+ keep native `max`; o1/MiniMax/Grok/Muse Spark clamp to their upstream ceiling) ([#11875](https://github.com/diegosouzapw/OmniRoute/pull/11875)) — thanks @Chewji9875 +- **feat(providers):** the provider plugin manifest now advertises a `usage-fetch` capability for the 40 providers that have a wired usage/quota fetcher, so external dashboards can read it from `GET /api/v1/provider-plugin-manifest` instead of parsing `open-sse/services/usage.ts` after every release. Discovery only — no new fetcher, no quota change, and the Dashboard quota widget stays gated by `USAGE_SUPPORTED_PROVIDERS`. `USAGE_FETCHER_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/fetcherProviders.ts`) and is re-exported from `services/usage.ts`, keeping the manifest module a light leaf instead of pulling the ~490-module usage dispatcher into the manifest route. ([#11903](https://github.com/diegosouzapw/OmniRoute/pull/11903)) — thanks @maxmad64bis / @pacocartones +- **feat(plugins):** `OMNIROUTE_PLUGINS_DIR` sets the directory the runtime plugin scanner reads — and the root the plugin manager installs into — overriding the `HOME`-derived default, so a Docker/K8s deployment can point straight at its bind-mounted plugin tree instead of moving `HOME` just to relocate the scan path. An image that exports no home no longer scans `/tmp/.omniroute/plugins` in silence: the resolved directory is logged once at startup as `scanner.dir_resolved`, naming the input that won. Unset, behaviour is unchanged. Distinct from the CLI-only `OMNIROUTE_PLUGIN_PATH`, which finds `omniroute-cmd-*` command packages and never reached this scanner ([#11906](https://github.com/diegosouzapw/OmniRoute/pull/11906)) — thanks @amaleta / @pacocartones +- **feat(leases):** add an explicit owner-authenticated status action that returns only the active lease's privacy-safe configured connection and provider labels, with generation fencing and no credential or internal-id disclosure ([#11910](https://github.com/diegosouzapw/OmniRoute/pull/11910)) — thanks @KaspaPulse +- **feat(providers):** import-from-file modal shows per-row API errors and ships a downloadable CSV template ([#12071](https://github.com/diegosouzapw/OmniRoute/issues/12071)) ([#12504](https://github.com/diegosouzapw/OmniRoute/pull/12504)) — thanks @HouMinXi +- **feat(providers):** dashboard search matches connection name and `baseUrl` so imported OpenAI-compat nodes surface on the provider card ([#12108](https://github.com/diegosouzapw/OmniRoute/issues/12108)) ([#12495](https://github.com/diegosouzapw/OmniRoute/pull/12495)) — thanks @HouMinXi +- **feat(routing):** add a `score` Auto router strategy that selects the highest configured weighted score and reuses `explorationRate`. ([#12155](https://github.com/diegosouzapw/OmniRoute/pull/12155)) — thanks @jacobsparts +- Add a runtime feature flag to disable universal context handoffs globally without changing the default behavior. ([#12167](https://github.com/diegosouzapw/OmniRoute/pull/12167)) — thanks @jacobsparts +- **feat(providers):** the provider plugin manifest now also advertises a `usage-supported` capability for the 46 providers whose usage API is accepted by the server and Dashboard routes, so integrators can distinguish "the server will serve quota for this provider" from "a fetcher is wired" without reading TypeScript. Discovery only — no fetcher or quota change. `usage-fetch` resolves on id or alias (the usage dispatcher accepts both); `usage-supported` resolves on id alone, matching the runtime guard `USAGE_SUPPORTED_PROVIDERS.includes(providerId)`. `USAGE_SUPPORTED_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/supportedProviders.ts`) and is re-exported from `providers.ts`, mirroring the `fetcherProviders` leaf from #11903 and keeping the manifest a light module. ([#12214](https://github.com/diegosouzapw/OmniRoute/pull/12214)) — thanks @maxmad64bis +- **feat(rankings):** order Free Provider Rankings by what each provider actually served — `GET /api/free-provider-rankings?sortBy=reliability` and a "Most reliable first" toggle on the page. Providers with too few calls to state a success rate keep their score order below the measured ones; the default order is unchanged ([#12218](https://github.com/diegosouzapw/OmniRoute/pull/12218)). — thanks @maxmad64bis +- **perf(sse):** defer `cloneLogPayload()` in the structured SSE collector until after the `maxEvents`/`maxBytes` cap check, eliminating ~9,800 wasted `structuredClone` calls per streaming response (65–71% faster `push()`). Reducer snapshot isolation restored for OpenAI and Responses summaries ([#12241](https://github.com/diegosouzapw/OmniRoute/pull/12241)) — thanks @PauloHSOliveira ([#12243](https://github.com/diegosouzapw/OmniRoute/pull/12243)) +- **feat(usage):** Devin CLI agentic quota (Codeium seat-management GetUserStatus) and OpenRouter key limits plus account credits now surface in Provider Limits ([#12256](https://github.com/diegosouzapw/OmniRoute/pull/12256) — thanks @Neuron-Mr-White) +- **feat(settings):** persist `headroomUrl` through Settings so status/start use the operator URL instead of only `HEADROOM_URL` ([#12306](https://github.com/diegosouzapw/OmniRoute/issues/12306)) ([#12487](https://github.com/diegosouzapw/OmniRoute/pull/12487)) — thanks @HouMinXi +- **feat(auto-combo):** Auto-Combo scoring can now weigh how often a provider/model has actually succeeded. The engine already carried that number on every candidate — 24 hours of usage history behind a ten-sample floor, real-time metrics otherwise — and the scoring function never read it, while the weight table described `stability` as if it did. `reliability` (`1 - failureRate`, with the same field precedence and the same rate-bounding the speed ranking already uses, so a corrupt reading means "nothing observed" rather than "fails every call") is now a declared factor shipping at weight `0`, so routing is unchanged until an operator gives it one, and the `stability` description now matches what that factor computes ([#12317](https://github.com/diegosouzapw/OmniRoute/pull/12317)) — thanks @maxmad64bis +- **feat(routing):** With `freeAccessPolicy: "strict"`, the read-only candidate listing (`GET /v1/auto-combo/{channel}/candidates`) no longer hides the candidates the zero-cost guard excludes — the same read-only transparency the resilience filter already honours (#9133). Each candidate now carries `freeAccessExclusion` saying why it would be kept out, and it tells an exhausted allowance apart from a quota reading that never arrived or went stale, which used to look identical from the outside. Routing is unchanged: the listing reports, it never enforces. The separate `excludeTosAvoid` guard still drops its candidates without a reason; that gap is now documented rather than closed ([#12319](https://github.com/diegosouzapw/OmniRoute/pull/12319)) — thanks @maxmad64bis +- **feat(radar):** The Radar catalog table now shows two facts it was already receiving from the feed and dropping on the floor: the per-model rate limits (requests and tokens, per minute and per day) in a new column, and a badge when a provider's terms state it may train on the prompts you send. A limit of zero renders as zero rather than "rate-only" — for a ceiling those are opposite facts — and a model with no training statement gets no badge, because an absent statement is not a guarantee ([#12320](https://github.com/diegosouzapw/OmniRoute/pull/12320)) — thanks @maxmad64bis +- **feat(radar):** explain Community, single-use, contributor, supporter, recovery, abuse, offers, and privacy rules before either Radar activation action, and remove the superseded fixed-PR grant promise from every UI locale ([#12342](https://github.com/diegosouzapw/OmniRoute/pull/12342)) +- **feat(gamification):** the dashboard Profile page now shows the real daily streak — `/api/gamification/level` returns `streak: { current, longest }` (per key with `apiKeyId`, operator-wide maximum otherwise) and the streak card reads it instead of a hard-coded 0 (#2403) ([#12377](https://github.com/diegosouzapw/OmniRoute/pull/12377)) — thanks @pacocartones +- **feat(gamification):** the dashboard leaderboard now shows each API key's display name under the Name column instead of a truncated key id; `GET /api/gamification/leaderboard` attaches `name` per entry (name only — no key material), while the shared ranking helper and the federation leaderboard stay id-only — thanks @pacocartones ([#12385](https://github.com/diegosouzapw/OmniRoute/pull/12385)) +- **feat(gamification):** enforce the documented 1000 XP/min per-API-key anti-cheat rate limit on the XP award path; over-limit awards are logged and skipped instead of persisted, and the sliding window now matches the timestamp format stored in `xp_audit_log` ([#2403](https://github.com/diegosouzapw/OmniRoute/issues/2403)) ([#12390](https://github.com/diegosouzapw/OmniRoute/pull/12390)) — thanks @pacocartones +- **feat(admin):** localize the gamification anomalies page — the loading state, the Status column and the Suspicious badge now come from the `common` catalog (new `common.suspicious` key propagated to every locale) — add it to the Gamification sidebar group as `gamification-admin` (`/dashboard/gamification/admin`), and expose the loading and empty states as polite `role="status"` live regions (#12401 — thanks @pacocartones) +- **feat(providers):** skip GitHub combo members missing from the live synced catalog, and drop Copilot models that are policy-disabled or hidden from the model picker ([#12473](https://github.com/diegosouzapw/OmniRoute/pull/12473)) — thanks @RaviTharuma +- **feat(i18n):** locale-expansion tooling — `npm run i18n:add-locale` adds a language to config, dashboard, docs mirrors, CLI, README/indexes and site in one command; browser, CLI and cookie detection resolve `uk`, `fil`/`tl`, `zh-Hant` (and the retired legacy `in` → `id`) via config aliases; `config/i18n.json` now ships in the npm package so the published CLI can read it; new real-translation ratio gate (`npm run i18n:check-ratio`, advisory) with a per-locale ratchet baseline; `run-translation --adopt` restores `.i18n-state.json`; `sync-language-bars` generates 🌐 bars from config; `validate_translation.py` loads its allowlist again. Retires the duplicate `in` locale (Indonesian mislabelled as Hindi) — 42 honest locales; saved `NEXT_LOCALE=in` / `OMNIROUTE_LANG=in` keep working. (#12496) +- **feat(api):** Emit gateway-measured `tokens_per_second` (TTFT excluded) on streaming usage and `X-OmniRoute-Tokens-Per-Second` when first-token latency is known ([#12616](https://github.com/diegosouzapw/OmniRoute/issues/12616)) ([#12631](https://github.com/diegosouzapw/OmniRoute/pull/12631)) — thanks @RaviTharuma +- **feat(opencode-plugin): map gateway cost/usage/tok/s onto OpenCode inference payloads** — the official plugin copies `X-OmniRoute-Response-Cost`, token counts, `X-OmniRoute-Tokens-Per-Second` / `usage.tokens_per_second`, TTFT, and the winning `X-OmniRoute-Model` onto the JSON/SSE body OpenCode already consumes. Missing tok/s is left unset (never `tokens / latency`). (#12636) — thanks @RaviTharuma +- **feat(combo):** add combo strategy `quota-weighted`: skip exhausted accounts, then weighted-draw by leftover / in-flight load; existing conversations stay pinned until the account is empty ([#12789](https://github.com/diegosouzapw/OmniRoute/pull/12789)) — thanks @HouMinXi +- **feat(models):** Account live listings become the chat catalog source for Claude, Codex, Copilot, and AGY; public metadata only fills prices on IDs those accounts already list. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **fix(models):** Union `agy` and `antigravity` live catalogs so `agy/gemini-3.8-flash-high` is not rejected after the prefix folds to `antigravity`. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **feat(opencode):** Add `@omniroute/opencode-plugin-v2`, an opencode v2 plugin that gives a v2 install a route to an OmniRoute gateway: it publishes models, combos and auto-combos into the host catalog, refreshes them lazily behind a 300s TTL, and keeps serving the last known catalog from an on-disk snapshot when the gateway is unreachable. The v1 plugin (`@omniroute/opencode-plugin`) is untouched and keeps working as before. The plugin adapts to the host instead of assuming one shape: it reads the shape the host seeds into the catalog draft and publishes the `api` block, the pre-`api` top-level fields, or both. The gateway key comes from the host credential store when one is connected, so no secret needs to sit in a config file; a source the gateway refuses is named in a warning instead of silently degrading the catalog to raw model ids; and tool schemas bound for a Gemini model are stripped of the JSON-Schema keywords Gemini rejects, so tool calling survives that route ([#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870)) — thanks @maxmad64bis +- feat(services): show sanitized CLIProxyAPI account health from its authenticated management API without exposing credentials, file paths, or raw account metadata (#6342) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add AnySearch free web search + URL extract (webFetch) with typed results, credential validation, REST routing, and MCP selection - fallback-only ([#11690](https://github.com/diegosouzapw/OmniRoute/pull/11690)) — thanks @Xxx91n +- **feat(dashboard):** display clamped `[0, 100]%` cached input token ratio in request logs table ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) — thanks @b3nw +- **feat(catalog):** add `OMNIROUTE_DISABLE_THINKING_LEVEL_VARIANTS` feature flag to optionally filter out thinking level variants from model catalog ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) — thanks @b3nw +- **feat(dashboard):** continuously export call logs to external analytics stores. A pluggable destination registry ships the full Logs-tab record set on an hourly `JobRegistry` cron, with a persisted per-destination cursor, batched inserts, a config UI rendered from each destination's own field descriptors, and a REST layer (`/api/log-export/*`) for CRUD, a connection test, and an on-demand run. A destination can opt into `includeBodies` to also ship the request and response payloads shown in the Logs detail pane, including the client and provider views of each call; this is off by default, and payloads inherit the dashboard's PII sanitisation, secret redaction and `noLog` handling. Google BigQuery is the first destination, using a service-account key stored encrypted at rest and streaming inserts keyed by call-log id, into a table that is day-partitioned on `timestamp` and clustered on `api_key_name`, `provider`, `model` and `status`. ([#11945](https://github.com/diegosouzapw/OmniRoute/pull/11945)) — thanks @dpozimski +- **feat(providers):** add MaxAI as a signed, OpenAI-compatible provider serving its 13 paid chat models (GPT-5.6 / Luna / Thinking, Claude 5 Sonnet, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite, Grok 4.1-fast / 4.5, DeepSeek V3.2 / R1, Llama 3.3 70B) through OmniRoute's `/v1` endpoint, with per-request HMAC-SHA1→SM3→AES request signing, live model + context-window discovery from `/models/get_config`, and prompted tool-calling translated to OpenAI `tool_calls` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI vision input — image_url content parts are forwarded inline in `message_content` to the 6 vision-capable models (GPT-5.6 / Luna / Thinking, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite) ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI image generation — 6 image models (gpt-image-1, dall-e-3, flux-1-schnell/dev/pro, sd3-medium) exposed through `POST /v1/images/generations` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI document RAG — inline base64 file/document attachments are uploaded to MaxAI (content-addressed `doc_id`) and attached to the chat via `doc_list` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** browserless MaxAI onboarding — email device-pair login (`/api/providers/[id]/login`) and signed access-token refresh, so a connection can be created and kept fresh without a real browser or Google OAuth ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** per-provider TLS impersonation profile (MaxAI presents a Windows Firefox-150 client fingerprint) so its bot-sensitive endpoints accept OmniRoute traffic ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** add **Nimble** as a web-search and web-fetch provider (`nimble-search`) — `/v1/search` routes to Nimble's search API at `lite` depth (locale, freshness and include/exclude domain filters mapped onto the shared request contract), and `/v1/web/fetch` routes to Nimble Extract, which covers all four fetch formats (`markdown`, `html`, `links`, `screenshot`) from a single call. One API key serves both surfaces. ([#11620](https://github.com/diegosouzapw/OmniRoute/pull/11620)) — thanks @wildcard +- The npm publish is automatic again, through npm Trusted Publishing (OIDC): the hosted `stage-npm` job publishes with a short-lived credential minted from GitHub's id-token — no `NPM_TOKEN`, no 2FA prompt, provenance attached. `publish_mode=staged` (owner approves with 2FA) and `direct` (token) remain available on `workflow_dispatch`. ([#11931](https://github.com/diegosouzapw/OmniRoute/pull/11931)) +- **feat(providers):** Add **Opper** as an API-key gateway provider — EU-hosted AI gateway with 700+ models from 30+ providers behind one OpenAI-compatible API and one key (`OPPER_API_KEY`); model ids use `provider/model` format (e.g. `anthropic/claude-sonnet-4-6`, `openai/gpt-5`); live model catalog at `https://api.opper.ai/v3/compat/models`; entry mirrors `requesty` (same shape, `passthroughModels: true`, no static seed) ([#11629](https://github.com/diegosouzapw/OmniRoute/pull/11629)) +- **feat(dashboard):** the `/dashboard/orchestration` snapshot hook now subscribes to the `agents` WebSocket channel (`agent.task.updated`) instead of `requests` as its refetch trigger, and relaxes its background poll from 5s to 30s while that WS connection is up — falling back to the tighter 5s cadence, reprogrammed live on any connect/disconnect transition, whenever the socket is down. ([#12409](https://github.com/diegosouzapw/OmniRoute/pull/12409)) +- **feat(dashboard):** Orchestration canvas quick wins — search box plus state/source/provider filter chips with a one-click clear, and per-source collapse/expand, all reflected in the URL so a filtered/collapsed view is shareable and survives a refresh; the detail drawer gained a "copy trace JSON" action and hardened error/empty-state and accessibility handling; the Agents-tab edges now animate traveling particles along active (running) connections; and the canvas node/edge status colors moved off fixed hex values onto theme-aware `--orch-status-*` CSS custom properties, so they adapt correctly to light/dark mode. ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- **chore(dashboard):** Orchestration UI hardening pass and the missing component/model test coverage it called for — `OrchestratorNode`/`ActivityNode`/`OverflowNode` rendering, the `?node=`/overflow-click page routing, the Agents-tab orchestrator-click no-op and `showCompleted` toggle, and the overview kanban's done-column sort order (#12270, #12271). ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- New `/dashboard/orchestration` page: live unified view of everything running — Cloud Agent, A2A and Conductor as a real-time graph (Agents tab), the combo cascade (Routing tab, reusing the Combo Live Studio) and a state kanban (Overview tab), with a detail drawer (trace, cost, approve/cancel). Read-only over existing APIs — no new backend. Canvas concept credit: PR #11815 design ([#12261](https://github.com/diegosouzapw/OmniRoute/pull/12261)) +- **feat(dashboard):** new "History" tab on `/dashboard/orchestration` — an Airflow-style grid of finished runs over a 24h/7d/30d preset window, one row per (source, identity), clicking a cell opens the existing detail drawer. It is backed by real persistence: A2A task lifecycle transitions are now written to the `a2a_tasks` table (purged after 30 days, configurable via `OMNIROUTE_A2A_HISTORY_RETENTION_DAYS`) and served by the new `GET /api/a2a/tasks/history` listing endpoint, with the task-detail route falling back to persisted history once a run leaves the in-memory snapshot. Conductor runs stay remote and are not persisted locally — the tab says so instead of silently omitting them. ([#12479](https://github.com/diegosouzapw/OmniRoute/pull/12479)) +- **feat(dashboard):** the orchestration detail drawer gained a "Repeat" action for Cloud Agent, A2A and Conductor tasks — a two-click confirm (click once to arm, click again within the confirm window to fire) re-submits the original prompt/input as a new run. The button is disabled with an explanatory tooltip whenever the original input can't be recovered from the loaded task detail (e.g. it never carried a prompt, or the detail failed to load). Two limitations of the A2A variant, by design: it targets the `/a2a` JSON-RPC endpoint, which authenticates with an API key only (`REQUIRE_API_KEY=true` or a configured `OMNIROUTE_API_KEY` makes a dashboard-session repeat answer `HTTP 400` — surfaced verbatim in the drawer's error line, never as a success), and the `message/send` call is SYNCHRONOUS: the POST blocks for the whole skill run, so the success confirmation only appears once the repeated task finishes. A dashboard-authenticated A2A creation path is deliberately left to a follow-up — widening the endpoint's auth posture is an operator decision, not a side effect of this feature. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(a2a):** A2A task execution now records which memories were consulted for the task's last user message as `metadata.memoryHits` (id/key/type/content-snippet) plus a `memory_hits` history event, purely for observability — the retrieved memory is never injected into a skill's prompt or behavior. Gated by the `OMNIROUTE_A2A_MEMORY_HITS` kill-switch (default enabled; set to `0` to skip the recall lookup entirely). The drawer's new "Memory used" section lists these hits for a2a tasks and is omitted whenever there are none. Known limitation: recall only resolves under the keyless posture — a keyed caller's task owner is a SHA-256 prefix of the API key, while memory rows are keyed by the database api-key id, and no hash→id lookup exists today, so the hit list stays empty for keyed callers. The recorded hits are also kept out of the task's own `input` (and therefore out of the persisted input and of the "Repeat" request body), so repeating a task never re-sends the previous run's memory snippets. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(providers):** add a Perplexity Agent API provider (`perplexity-agent` / `pplx-agent`) for Perplexity `/v1/responses`, including the documented Anthropic, OpenAI, Google, xAI, DeepSeek, Z.AI, Moonshot/Kimi, NVIDIA, and Perplexity model IDs plus Anthropic-model `max_output_tokens` compatibility. ([#12103](https://github.com/diegosouzapw/OmniRoute/pull/12103)) — thanks @AIB1TAL0S +- **feat(providers):** add RPD (Requests Per Day) limit to provider rate limit overrides across UI, schemas, DB, and i18n ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) — thanks @b3nw +- **feat(providers): add UC Direct (uncensored.com Developer API), the metered OpenAI-compatible surface.** A standard OpenAI-compatible passthrough (default executor) for uncensored.com's official REST API at `https://api.uncensored.com/api/v1`: `X-api-key` auth (never-expiring `uai_sk_live_` key), `POST /chat/completions` with streaming SSE and native tool-calling, and the full live metered catalog (82 models across 15 providers, discovered from the public `GET /v1/models`). Registered as provider `uc-direct` (alias `ucd`). Complements the un-metered `uc` persona provider — same models, metered credits and a plain API key instead of a subscription session. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(providers): add UC (uncensored.com), the un-metered subscription "persona" chat as an OpenAI-compatible provider.** A WebSocket web-app port: a durable Clerk credential mints a short-lived session token per connect (browserless — no API key), driving UC's persona socket. Ships the browserless email-code login (request → verify → harvest), the 19 verified persona models (Claude Opus, Gemini, Grok, GLM, Kimi, DeepSeek, MiniMax, incl. the uncensored variants), prompted `` tool-calling with a per-model code-style dialect + auto-cure retry for guardrailed models, live ``/reasoning split, streaming + non-streaming OpenAI responses, and full quota/auth error surfacing (paywall / message-limit / rate-limit → 429, invalid session → 401 re-login). Full multimodal parity via the persona blob-upload layer: **vision** (image input, 15 vision-capable models), **document RAG** (PDF/doc upload, server-side extraction), **image generation** (22 models), **video generation** (14 models, async signed-url → poll), and **TTS** (streaming MP3). Registered as provider `uc` (alias `ucn`). The metered OpenAI-compatible Developer API is a separate `uc-direct` provider. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(logs):** add copy-all button to request detail modal ([#11083](https://github.com/diegosouzapw/OmniRoute/pull/11083)) — thanks @AndrianBalanescu +- **feat(check):** make serviceKinds required and add the reverse-walk provider consistency gate ([#11392](https://github.com/diegosouzapw/OmniRoute/pull/11392)) — thanks @Tushar49 +- **feat(providers):** only advertise verified-working connections by default ([#11448](https://github.com/diegosouzapw/OmniRoute/pull/11448)) — thanks @alltomatos +- **feat(resilience):** add hierarchical concurrency admission ([#11493](https://github.com/diegosouzapw/OmniRoute/pull/11493)) +- **feat(providers):** derive + surface expiry for JWT-bearing web cookies (#11497) ([#11505](https://github.com/diegosouzapw/OmniRoute/pull/11505)) — thanks @oyi77 +- **feat(tunnels):** support named cloudflared tunnels via CLOUDFLARED_CONFIG ([#11561](https://github.com/diegosouzapw/OmniRoute/pull/11561)) — thanks @ggiak +- **feat(compression):** make proactive context-compression threshold a live setting ([#11564](https://github.com/diegosouzapw/OmniRoute/pull/11564)) — thanks @ragnar-claude +- **feat(resilience):** add provider peak-hour protection ([#11622](https://github.com/diegosouzapw/OmniRoute/pull/11622)) — thanks @Neuron-Mr-White +- **feat(providers):** add 1min.ai provider ([#11631](https://github.com/diegosouzapw/OmniRoute/pull/11631)) — thanks @zero-executioner +- **feat(dashboard):** collapsible JSON tree viewer for request/response payloads ([#11703](https://github.com/diegosouzapw/OmniRoute/pull/11703)) — thanks @hartmark +- **feat(routing):** add LiquidAI LFM2.5-2.6B free tier via OpenRouter ([#11752](https://github.com/diegosouzapw/OmniRoute/pull/11752)) — thanks @brick30llc-ctrl +- **feat(pricing):** add GLM-5.3-Flash pricing, model specs, and catalog registration ([#11830](https://github.com/diegosouzapw/OmniRoute/pull/11830)) — thanks @AndrianBalanescu +- **feat(routing):** add relayMode for schema-locked context handoffs ([#11839](https://github.com/diegosouzapw/OmniRoute/pull/11839)) — thanks @raheemuddin786 +- **feat(config):** add support for runtime system prompt configuration and hot-reloading ([#11841](https://github.com/diegosouzapw/OmniRoute/pull/11841)) — thanks @raheemuddin786 +- **feat(routing):** expand connection-aware quota prefilter across combo strategies (#11682) ([#11850](https://github.com/diegosouzapw/OmniRoute/pull/11850)) — thanks @HouMinXi +- **feat(models):** reactive discovery sync after Antigravity model-not-found 404 ([#11888](https://github.com/diegosouzapw/OmniRoute/pull/11888)) — thanks @MumuTW +- **feat(models):** auto-aliases and token limits from synced Antigravity discovery ([#11889](https://github.com/diegosouzapw/OmniRoute/pull/11889)) — thanks @MumuTW +- **feat(settings):** raise sticky round-robin limit caps to 1000 ([#12015](https://github.com/diegosouzapw/OmniRoute/pull/12015)) — thanks @adivekar-utexas +- **feat(resilience):** operator-configurable global credential health check interval ([#12043](https://github.com/diegosouzapw/OmniRoute/pull/12043)) — thanks @adivekar-utexas +- **feat(quota):** use official OpenCode Go usage API ([#12124](https://github.com/diegosouzapw/OmniRoute/pull/12124)) — thanks @ddarkr +- **feat(resilience):** default the credential health check sweep to 60 minutes ([#12138](https://github.com/diegosouzapw/OmniRoute/pull/12138)) — thanks @adivekar-utexas +- **feat(quality):** new-code mode for the complexity and dead-code ratchets (Clean as You Code) ([#12142](https://github.com/diegosouzapw/OmniRoute/pull/12142)) +- **feat(dashboard):** orchestration canvas — unified model + snapshot hook (part 1/2) ([#12156](https://github.com/diegosouzapw/OmniRoute/pull/12156)) +- **feat(usage):** add Kilo Code balance and Kilo Pass quotas ([#12178](https://github.com/diegosouzapw/OmniRoute/pull/12178)) — thanks @killer30001000 +- **feat(providers):** refresh vendored ChatGPT Web connector to v4.0.7 ([#12181](https://github.com/diegosouzapw/OmniRoute/pull/12181)) — thanks @backryun +- **feat(providers):** manual "Clear cooldown" action in the cooling panel ([#12224](https://github.com/diegosouzapw/OmniRoute/pull/12224)) — thanks @patrykkopycinski +- **feat(providers):** restore ChatGPT Web via clean-room browser transport ([#12239](https://github.com/diegosouzapw/OmniRoute/pull/12239)) — thanks @backryun +- **feat(sse):** wire the PROVIDER_PROFILES window gate into the global provider cooldown ([#12247](https://github.com/diegosouzapw/OmniRoute/pull/12247)) +- **feat(providers):** modernize CLOVA Studio chat and embeddings ([#12277](https://github.com/diegosouzapw/OmniRoute/pull/12277)) — thanks @backryun +- **feat(browser):** adopt Obscura as primary headless browser engine with Chromium fallback ([#12286](https://github.com/diegosouzapw/OmniRoute/pull/12286)) — thanks @opensource-elearning +- **feat(sse):** full language parity for output styles (es/de/fr/it/ru/zh + autoDetect) ([#12289](https://github.com/diegosouzapw/OmniRoute/pull/12289)) +- **feat(quality):** complete test:scoped — --full map rebuild, stdin selection, CI loader parity (#8084 D1) ([#12353](https://github.com/diegosouzapw/OmniRoute/pull/12353)) +- **feat(providers):** refresh Fable, Cursor, and Devin catalogs ([#12367](https://github.com/diegosouzapw/OmniRoute/pull/12367)) — thanks @backryun +- **feat(video):** redact transcript text from logs and durable memory (#12150 P1) ([#12427](https://github.com/diegosouzapw/OmniRoute/pull/12427)) +- **feat(dashboard):** parent-link, genuine-continuation badge, and modal perf fixes ([#12448](https://github.com/diegosouzapw/OmniRoute/pull/12448)) — thanks @hartmark +- **feat(dashboard):** adaptive context-budget dial on compression panel ([#12488](https://github.com/diegosouzapw/OmniRoute/pull/12488)) — thanks @HouMinXi +- **feat(providers):** refresh Z.ai Web models and browser transport ([#12524](https://github.com/diegosouzapw/OmniRoute/pull/12524)) — thanks @backryun +- **feat(video):** redact raw client-snapshot transcript fields in the detailed log (#12150 P2) ([#12528](https://github.com/diegosouzapw/OmniRoute/pull/12528)) +- **feat(providers):** refresh NVIDIA hosted models ([#12538](https://github.com/diegosouzapw/OmniRoute/pull/12538)) — thanks @backryun +- **feat(quota):** Moonshot Open Platform balance and TPD lock for custom nodes ([#12590](https://github.com/diegosouzapw/OmniRoute/pull/12590)) — thanks @HouMinXi +- **feat(video):** redact transcript in the in-memory pending-request snapshot (#12430 item 6) ([#12596](https://github.com/diegosouzapw/OmniRoute/pull/12596)) +- **feat(dashboard):** link a log entry's Conversation Context to its owning conversation ([#12646](https://github.com/diegosouzapw/OmniRoute/pull/12646)) — thanks @hartmark +- **feat(catalog):** eligibility-gated free-tier bucket ([#12669](https://github.com/diegosouzapw/OmniRoute/pull/12669)) +- **feat(db):** fail-closed previous_response_id continuation for redacted video turns (#12150 P2b) ([#12707](https://github.com/diegosouzapw/OmniRoute/pull/12707)) +- **feat(gamification): pay the documented `streak_bonus` and `badge_unlock` XP rewards.** `XP_REWARDS` listed both rewards but the award pipeline never paid them: the private reward table in `events.ts` omitted them, `updateStreak()` did not report when a streak extended, and badge unlocks carried no XP. Every request that extends a daily streak now pays `streak_bonus × streak length` once per UTC day (guarded by a same-day `xp_audit_log` check), and every badge unlocked through the pipeline pays `badge_unlock` once per badge (guarded by the `user_badges` primary key; `unlockBadge()` now reports whether it inserted). Bonus XP flows through the same `addXp` + level sync + global/weekly/monthly leaderboard path as action XP, so level-ups and rankings include it. The Radar supporter recognition unlock stays XP-free. (#12522 — thanks @pacocartones) +- **feat(providers):** advertise a `free-tier` capability in the provider plugin manifest for every provider with documented free models, so sidecars and dashboards can filter free-capable providers without reading the quota catalog ([#12786](https://github.com/diegosouzapw/OmniRoute/pull/12786)) — thanks @maxmad64bis +- **feat(providers):** Added EURouter as an OpenAI-compatible API-key gateway (`https://api.eurouter.ai/v1`), with live model discovery via `passthroughModels`. Its copy states that models are served by third-party upstreams listed per model, so an EU-based router is not read as EU data residency for inference. ([#13025](https://github.com/diegosouzapw/OmniRoute/pull/13025)) — thanks @ntdatt812 +- **feat(providers):** Added GreenPT as an OpenAI-compatible API-key provider (`https://api.greenpt.ai/v1`), with live model discovery via `passthroughModels`. No free-inference badge: the published docs describe a free API subscription billed per token, not a free tier. ([#13024](https://github.com/diegosouzapw/OmniRoute/pull/13024)) — thanks @ntdatt812 +- **feat(i18n):** 9 new locales — Greek (`el`), Croatian (`hr`), Serbian (`sr`), Lithuanian (`lt`), Estonian (`et`), Latvian (`lv`), Slovenian (`sl`), Maltese (`mt`), Irish (`ga`) — across the dashboard, docs mirrors, CLI, README and the site; OmniRoute now ships every official EU language (51 locales). (#13044) +- **feat(i18n):** 8 new locales — Kannada (`kn`), Malayalam (`ml`), Odia (`or`), Punjabi (`pa`), Nepali (`ne`), Sinhala (`si`), Burmese (`my`), Khmer (`km`) — across the dashboard, docs mirrors, CLI, README and the site (59 locales). The translator now restores the ICU literal escape around angle placeholders and splits oversized docs sections before translating. (#0000) ([#13660](https://github.com/diegosouzapw/OmniRoute/pull/13660)) +- feat(providers): **list Agnes 3.0 Flash as the current free chat model, drop retired 1.5 Flash, add Image 2.0/2.5 Flash plus Video 2.5/2.5 Flash, and discover the live `/v1/models` catalog (including the CN host `api.agnes-ai.cn`).** `agnes-1.5-flash` now forwards to `agnes-3.0-flash`. Video 2.5 polls `GET /v1/videos/{id}` (not the V2.0 `/agnesapi` contract). Live `/v1/models` (2026-09-09) no longer serves 1.5; the wiki marks it deprecated. 3.0 Flash is 512K context / 65,536 max output, same window as 2.5. CN-region keys use the existing per-connection base-URL field, default stays `apihub.agnes-ai.com`. ([#13120](https://github.com/diegosouzapw/OmniRoute/pull/13120)) — thanks @HouMinXi +- **feat(sse):** Codex and OpenAI catalogs list GPT-6 Astra with effort aliases (`-low` through `-ultra`); Codex CLI identity pins `@openai/codex@0.153.4` in lockstep with the image ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **feat(dashboard):** the orchestration History tab gained a "Compare runs" mode — toggling it turns each grid cell into a 2-item selection queue (a 3rd click drops the oldest pick), and picking two cells opens a side-by-side comparison panel instead of the usual detail drawer. The panel fetches both runs' detail the same way the drawer does (falling back to persisted history once a run leaves the live TTL window) and shows, per side: identity/source/state, start time, a signed `right - left` delta for duration/cost/event count, the event timeline aligned by index, and any memory hits. One side's fetch failing never blocks the other, and a delta is only ever computed when both sides have a finite value — otherwise it renders "—", never `NaN`. Comparing two runs from different sources or skills still works; a banner marks the deltas as informational rather than hiding them, since the two runs aren't a strict apples-to-apples pair. ([#12677](https://github.com/diegosouzapw/OmniRoute/pull/12677)) +- **feat(dashboard):** badge a conversation that never reached a clean stop ([#12717](https://github.com/diegosouzapw/OmniRoute/pull/12717)) — thanks @hartmark +- **feat(release):** reconcile-changelog tool + version-anchored fragment aggregation ([#12987](https://github.com/diegosouzapw/OmniRoute/pull/12987)) +- **feat(mcp):** carry context_length through MCP list_models_catalog projection (#12776) ([#13406](https://github.com/diegosouzapw/OmniRoute/pull/13406)) — thanks @KooshaPari +- **feat(cli):** make startup readiness budget configurable (#13369) ([#13433](https://github.com/diegosouzapw/OmniRoute/pull/13433)) — thanks @KooshaPari + ### 🐛 Bug Fixes +- **security(streaming):** sanitize generic mid-stream error messages before emitting OpenAI, + Responses, or Claude SSE failure frames and before diagnostic logging, while preserving raw + failures for internal classification and keeping client disconnects out of provider failure state. ([#12457](https://github.com/diegosouzapw/OmniRoute/pull/12457)) +- **fix(claude):** `blockExtraUsage: false` no longer lets 5h quota preflight skip the account; extra usage is billed after the session bar is gone, so the request must reach Anthropic ([#12803](https://github.com/diegosouzapw/OmniRoute/pull/12803)) — thanks @HouMinXi +- Sanitize HuggingChat conversation-creation and message-send transport failures before they reach client error bodies or provider logs. ([#12467](https://github.com/diegosouzapw/OmniRoute/pull/12467)) +- **fix(dashboard):** Keep local and theme-aware provider SVG icons at a definite layout size so Chromium does not collapse them to 0×0 after the v3.8.50 image-rendering change ([#12054](https://github.com/diegosouzapw/OmniRoute/pull/12054)) — thanks @ponkcore +- **fix(security):** Sanitize provider and runtime failures before public API, SSE and MCP responses and before persistent request, proxy and usage logs, preventing credentials, stack traces and host filesystem paths from crossing those boundaries while preserving stable error codes and useful diagnostics. ([#12506](https://github.com/diegosouzapw/OmniRoute/pull/12506)) +- **fix(cli):** `omniroute tunnel create` no longer crashes with `Cannot read properties of undefined (reading optsWithGlobals)` — removed the duplicate positional argument that caused Commander.js to misalign the action callback parameters ([#12295](https://github.com/diegosouzapw/OmniRoute/issues/12295)) ([#12368](https://github.com/diegosouzapw/OmniRoute/pull/12368)) — thanks @gonisulaimann +- Stop advertising Gemini Live-only models as supported audio endpoints until OmniRoute proxies the bidirectional Live protocol. ([#11710](https://github.com/diegosouzapw/OmniRoute/pull/11710)) — thanks @RaviTharuma +- **fix(kie):** reroute `flux/kontext` off the KIE Market `createTask` flow — it is catalogued with `isMarket: true` but has no Market catalog page, so KIE rejected it with "model name not supported"; it now hits the dedicated `POST /api/v1/flux/kontext/generate` / `GET /api/v1/flux/kontext/record-info` endpoints instead (#11296). ([#11985](https://github.com/diegosouzapw/OmniRoute/pull/11985)) — thanks @hartmark +- **fix(kie):** correct 12 more KIE Market catalog ids that were sent to `createTask` unchanged but diverge from KIE's documented upstream `model` values — GPT Image 2 T2I/I2I (drops the `gpt/` prefix), GPT Image 1.5 T2I/I2I (`gpt-image/` namespace), Seedream 5.0 Lite T2I/I2I (drops the `.0`), all 4 Flux 2 variants (`flux-2/` namespace, generic variant renamed `flex`), and Wan 2.7 Image / Image Pro (dash instead of dot) — each verified individually against the literal example request published on docs.kie.ai. `#11326`'s "everything else already matches" claim was wrong a second time (#11296); `z-image/4.0-*`/`z-image/4.5-*` and `flux/kontext` remain open, documented as unresolved in `KIE_MARKET_UPSTREAM_MODEL_IDS`'s comment pending further verification. ([#11667](https://github.com/diegosouzapw/OmniRoute/pull/11667)) +- **fix(dashboard):** `useApiKeySave.handleSaveApiKey` no longer forces a full upstream `/models` catalog sync on every non-curated provider connection save — callers can now pass `skipModelSync: true` to opt out, so a workflow that only wants to add one manual model no longer floods the provider's available-models list with hundreds/thousands of synced entries. The flag is a client-side intent signal only and is stripped before the connection payload is POSTed to `/api/providers`; default behavior (full sync on save) is unchanged when the flag is omitted (#11324) ([#11664](https://github.com/diegosouzapw/OmniRoute/pull/11664)) +- **Provider connections:** keep `tokenExpiresAt` when a connection is created. The create-path allowlist omitted it, so every insert stored NULL and the dashboard token badge could read a fresh connection as expired until its first background refresh ([#11368](https://github.com/diegosouzapw/OmniRoute/pull/11368)). — thanks @ntdatt812 +- **fix(combos):** the combo builder's precision-select, global-model-search, and manual-entry flows now serialize a model step's `model` string using the provider's already-computed routing-alias prefix (e.g. `oc/`) instead of rebuilding it from the raw canonical `providerId`, fixing the no-auth "OpenCode Free" provider (`opencode`) being routed to the unrelated paid "OpenCode Zen" provider (`opencode-zen`) because `opencode` doubles as a manual routing-prefix override ([#11433](https://github.com/diegosouzapw/OmniRoute/issues/11433)). ([#11670](https://github.com/diegosouzapw/OmniRoute/pull/11670)) +- **fix(radar):** the catalog feed cache now keeps `generatedAt`, the date the feed's data was built, next to `fetchedAt`, the date this install downloaded it (#11435). The feed schema requires that date and the sync path validates it, but the cache dropped it — so a feed fetched minutes ago and one carrying weeks-old figures looked identical to everything downstream, including the dashboard's "Last fetched" line. `getRadarCatalog().meta` and `GET /api/radar/status` now report both dates, the latter as its own field rather than folded into `version` — and omitted entirely for the offers and intel caches, which keep no build date, where a `null` would read as "unknown" rather than "never stored". The dashboard still shows only the fetch time; surfacing the build date there needs a new translated label and is left to a follow-up. Rows cached before migration 163 read back as `null`: unknown stays unknown instead of borrowing the fetch time. The referrals cache has persisted the same date since migration 142. — thanks @maxmad64bis +- **fix(cli):** postinstall no longer fills `JWT_SECRET` and `API_KEY_SECRET` in the installed package's `.env` (#11436). `.env.example` ships both blank on purpose: the server restores them from its durable store, or generates and persists them there, in `ensureSecrets()`. Pre-filling them defeated that — the file lives inside the package directory, so `npm i -g` replaced it and postinstall wrote _different_ values, while `ensureSecrets()` (which only acts on an empty variable) never got to restore the real ones. Both secrets rotated silently on every update, invalidating dashboard sessions and API-key CRCs. `STORAGE_ENCRYPTION_KEY` left the same list for the same reason in #1622; its comment pointed at a function that no longer exists and now names the real provisioning path. — thanks @maxmad64bis +- **fix(cli):** the CLI now says when a loaded `.env` lives inside the installed package directory (#11437). It already announces every env file it reads, without distinguishing the ones that survive an update from the one that does not: `npm i -g` replaces the package directory wholesale, so values set there are gone at the next update, silently. The warning names the durable path to move them to, and fires only when that file actually supplied a value — a file entirely shadowed by a durable one supplied nothing. A development checkout stays silent: there the same path is stable and documented in `SETUP_GUIDE.md`. — thanks @maxmad64bis +- **fix(free-models):** the shared `isFreeModel()` predicate no longer reports catalog entries marked `freeType: "discontinued"` as free, so `hidePaidModels` can't route to Pollinations' seven premium models that now require a paid key ([#11441](https://github.com/diegosouzapw/OmniRoute/pull/11441)) — thanks @maxmad64bis +- fix(ui): let AnySearch use the normal provider-icon fallback when LobeHub has no matching icon (#11449) +- Fixed the v3.8.50 Costs and Analytics dashboards so flat-rate Claude Code usage can be shown as an explicitly requested token-price estimate without changing default billed-cost semantics. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed archived usage retention so each request is priced individually instead of pricing a day's summed tokens once, which understated archived cost whenever a day mixed cache-heavy and ordinary requests. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed the Costs dashboard so it discloses when displayed figures include flat-rate token-price estimates instead of labelling them as billed spend, using the flag the analytics API already returns; the month-end projection and the CSV/JSON exports carry the same marker, and billed-cost mode is unchanged. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- **fix(combo):** attach the same combo-diagnostics trace (`poolSize`/`attemptOrder`/`excluded`/`terminalReason`, plus `x-omniroute-combo-*` headers) to the round-robin strategy's and the nested pipeline/fusion runtime-unit loop's "Maximum combo retry limit reached" 503 that the priority-strategy path already attaches for the identical terminal condition — previously those two paths returned a bare, contextless 503 ([#11462](https://github.com/diegosouzapw/OmniRoute/issues/11462)). ([#11665](https://github.com/diegosouzapw/OmniRoute/pull/11665)) +- **fix(bun):** use native `bun:sqlite` in `bootstrap-env` and `sync-env` to avoid loading `better-sqlite3` N-API addon during Bun startup ([#11468](https://github.com/diegosouzapw/OmniRoute/pull/11468)) — thanks @TheDemonTuan +- **fix(docker-bun):** make `Dockerfile.bun` install reliable with pre-install script helpers and native `bun:sqlite` smoke check ([#11470](https://github.com/diegosouzapw/OmniRoute/pull/11470)) — thanks @TheDemonTuan +- **fix(docker-bun):** remove every vendored `better-sqlite3` native addon from the Bun runtime image so startup cannot abort before the `bun:sqlite` fallback ([#11482](https://github.com/diegosouzapw/OmniRoute/pull/11482)) — thanks @TheDemonTuan +- **fix(autoCombo):** effort/alias model variants (`gpt-5.6-sol-xhigh`, `gpt-5.6`, cursor's `claude-4.6-opus-high`) inherit their base model's task fitness instead of falling to the wildcard 0.5 ([#11492](https://github.com/diegosouzapw/OmniRoute/pull/11492)) — thanks @MumuTW +- fix(db): rate-limit repeated Arena ELO leaderboard fetch-failure warnings instead of logging one per sync attempt (#11500) ([#11989](https://github.com/diegosouzapw/OmniRoute/pull/11989)) — thanks @hartmark +- **fix(db):** dedupe the raw `[Encryption] Decryption failed...` log line emitted by the lazy-decrypt views (`createLazyRowProxy`/`createLazyConnectionView`), which power `getProviderConnections()` and were re-triggering that line on every CredentialHealth/model-sync cycle for the same corrupt or stale-key credential — a fresh Proxy over a fresh row on every cycle meant the per-proxy memoization never suppressed it, unlike the dedup `decryptConnectionFields()` already had since [#9927](https://github.com/diegosouzapw/OmniRoute/issues/9927). Now shares that dedupe tracking so the line logs at most once per credential ([#11500](https://github.com/diegosouzapw/OmniRoute/issues/11500)). ([#11678](https://github.com/diegosouzapw/OmniRoute/pull/11678)) +- **fix(intelligence):** Arena ELO sync stores a synthesized base-model row for every effort/harness variant (`claude-opus-5-max` → `claude-opus-5`, `gpt-5.6-sol-xhigh (codex-harness)` → `gpt-5.6-sol`), so bare model requests reach the synced intelligence layer instead of the static fallback table; the hand-maintained `MODEL_ALIAS_MAP` — which collapsed generations (`gpt-5.5` → `gpt-5`) — is gone ([#11506](https://github.com/diegosouzapw/OmniRoute/pull/11506)) — thanks @MumuTW +- **fix(autoCombo,sse):** catalog hygiene — the static task-fitness table no longer ranks retired models above live ones (dead rows dropped, patterns matched on segment boundaries), the 7 deprecation aliases that forwarded to retired or non-existent models now point at the vendors' published replacements, an alias is no longer rewritten when the serving provider offers the id as-is, and a new `check:model-lifecycle` gate diffs both tables against a vendor lifecycle snapshot ([#11507](https://github.com/diegosouzapw/OmniRoute/pull/11507)) — thanks @MumuTW +- **fix(sse):** stop dropping resolved `thoughtSignature` values on parallel (multi tool-call) turns sent to Gemini 3.x — the claude→gemini and openai→gemini translators previously kept the signature only on the *first* function call of a message, causing Gemini to reject subsequent calls in the same turn with HTTP 400 "Function call is missing a thought_signature"; each function call now keeps its own resolved signature ([#11510](https://github.com/diegosouzapw/OmniRoute/issues/11510)). ([#11669](https://github.com/diegosouzapw/OmniRoute/pull/11669)) +- Treat Docker publishing from a non-default release branch as an expected skip instead of a failed workflow, while preserving the guard that prevents it from overwriting the `next` channel. ([#11527](https://github.com/diegosouzapw/OmniRoute/pull/11527)) — thanks @pacocartones +- fix(search): prefer credentialed providers over duckduckgo-free fallback (#11524) — the fallback-only loop ran before the credentialed-providers loop in `executeWebSearch`, making configured providers unreachable when `duckduckgo-free` was available. ([#11565](https://github.com/diegosouzapw/OmniRoute/pull/11565)) — thanks @vsd2807 +- **fix(sse):** cap the upstream headers-wait phase for STREAMING requests to a client-realistic ceiling (110s, under Codex's own ~120s hard client-abort window) instead of the flat 10-minute `FETCH_TIMEOUT_MS` default — that default was 5x longer than the body-phase readiness watchdog's own adaptive bound, so a request whose upstream never returned any response at all (not even headers, e.g. a stalled NVIDIA target behind a tool-heavy Responses→Chat translation) kept the client connection alive on keepalives only, guaranteeing the client's own patience ran out first with an opaque 499 instead of OmniRoute detecting and failing the stall fast. Non-streaming requests are unaffected — they keep the existing flat default (`open-sse/utils/fetchStartTimeoutPolicy.ts`) (#11526) ([#11668](https://github.com/diegosouzapw/OmniRoute/pull/11668)) +- **fix(ci):** protocol-clients E2E harness boots the peer-stamped custom server (`run-next.mjs dev`) instead of the bare `next` CLI, so `LOCAL_ONLY` locality resolves from the real TCP peer stamp and `/api/mcp/audit` answers 200 (open bootstrap preserved via an `OMNIROUTE_E2E_BOOTSTRAP_MODE=open` credential clear in `run-next.mjs`, pinned to loopback `HOST`) instead of a deterministic 403 (#11535) ([#11549](https://github.com/diegosouzapw/OmniRoute/pull/11549)) — thanks @jonlwheat2-gif +- **fix(docs):** the Auto-Combo engine is described with the number of factors it actually declares. Four different counts were in circulation — 6, 9, 13 and 14 across `AGENTS.md`, four guides and six code comments — while `DEFAULT_WEIGHTS` declares 15. `check:docs-counts` now reads that number from the source, so adding a factor without updating the prose fails CI ([#11545](https://github.com/diegosouzapw/OmniRoute/pull/11545)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **fix(quota-share):** in-flight leases are per request, so an aborted request's slot ages out instead of being kept alive by later traffic on the same connection ([#11547](https://github.com/diegosouzapw/OmniRoute/pull/11547)) — thanks @abhisheksharma2411 +- The free-tier summary route now serves the refreshed Radar catalog when the feed is active — the same numbers the dashboard shows — and states which catalog answered plus its real build date, instead of always reporting release-frozen figures with a stale curation date. The supporter-key live feed stays reserved to authenticated callers of the instance. ([#11550](https://github.com/diegosouzapw/OmniRoute/pull/11550)) — thanks @maxmad64bis +- **sse:** fix LiveWS/embed-WS servers crashing at startup under the Node/tsx runtime — `liveServer.ts`, `embedWsProxy.ts` and `apiBridgeServer.ts` imported `@/shared/utils/httpClientAbortGuard` without the `.mjs` extension, so the client-abort crash guard added by [#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556) was unreachable and every dependent test failed with `ERR_MODULE_NOT_FOUND` ([#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556)). ([#11671](https://github.com/diegosouzapw/OmniRoute/pull/11671)) — thanks @jonlwheat2-gif / @yxyxy +- fix(chat): shed any of the matched provider-node's routing identifiers (public prefix or internal id) before model lookup, so composites like `//` no longer reach upstream verbatim (#11557) — thanks @maxmad64bis +- **fix(cache):** a semantic-cache entry now expires on its TTL instead of surviving until the next UTC midnight, and `dbEntries` stops counting expired rows ([#11573](https://github.com/diegosouzapw/OmniRoute/pull/11573)) — thanks @ntdatt812 +- **fix(api):** `/v1/models` schedules its stale-while-revalidate rebuild through Next's `after()` again, so a stale catalog reaches the client before the rebuild blocks the event loop ([#11574](https://github.com/diegosouzapw/OmniRoute/pull/11574)) — thanks @ntdatt812 +- **fix(check):** the `known-symbols` executor conformance gate awaits the now-async `getExecutor()`, so it stops reporting all 142 registered aliases as dead symbols and can detect a lazy import that fails ([#11582](https://github.com/diegosouzapw/OmniRoute/pull/11582)) — thanks @ntdatt812 +- **fix(pack):** register `bin/cli/utils/volatileEnvPath.mjs` in `PACK_ARTIFACT_REQUIRED_PATHS`, so the prune can no longer silently drop a module the published CLI imports on every boot ([#11588](https://github.com/diegosouzapw/OmniRoute/pull/11588)) — thanks @ntdatt812 +- **i18n(vi):** translate the five reliability-column strings on the free-provider rankings page, which rendered as `__MISSING__:` markers in the Vietnamese UI ([#11589](https://github.com/diegosouzapw/OmniRoute/pull/11589)) — thanks @ntdatt812 +- **resilience:** restore the expired-connection retry-budget probe in the token-health sweep — the `!isGitHubAccessTokenOnlyConnection` carve-out reintroduced by #11608 contradicted the boundary pinned by #11592, so a GitHub connection parked at `expired` with retry budget remaining was never probed and could never self-heal ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)). ([#11672](https://github.com/diegosouzapw/OmniRoute/pull/11672)) — thanks @jonlwheat2-gif / @ntdatt812 +- **fix(dashboard):** Provider Stats now displays combo success rates on their native 0–100 percentage scale instead of multiplying them twice ([#11595](https://github.com/diegosouzapw/OmniRoute/pull/11595)) — thanks @pacocartones +- **fix(providers):** route DuckDuckGo Web Chat through the verified `duck.ai` endpoint, refresh its free model catalog, and avoid forwarding unsolved anti-abuse challenges ([#11597](https://github.com/diegosouzapw/OmniRoute/pull/11597)) — thanks @solstxce +- **fix(ui):** Console log Refresh and Copy controls now expose localized accessible names, keep copy actions visible on keyboard focus, and announce copy completion safely ([#11599](https://github.com/diegosouzapw/OmniRoute/pull/11599)) — thanks @pacocartones +- Dropped the stale-`.eslintcache` `restore-keys` fallback from both "Restore ESLint file cache" steps in `ci.yml`, so the blocking `Lint` job can no longer be served per-file verdicts computed under a different lint config, suppressions file or lockfile. `quality.yml` had already dropped it in #11963; `ci.yml` — the workflow that actually gates PRs — had not (#11600). ([#11996](https://github.com/diegosouzapw/OmniRoute/pull/11996)) +- fix(ci): pass `--pass-on-unpruned-suppressions` in `run-eslint-json.mjs` so the CI Lint job no longer fails when a suppression is merely orphaned by a genuine fix, mirroring the identical fix already in `validate-release-green.mjs` (#11600) ([#11983](https://github.com/diegosouzapw/OmniRoute/pull/11983)) — thanks @hartmark +- fix(handoff): enforce provider allowlist for universal handoff (#11602) — universal handoff now skips summarization when the selected summary model's provider is not included in the configured provider allowlist. ([#11619](https://github.com/diegosouzapw/OmniRoute/pull/11619)) — thanks @vsd2807 +- **fix(dashboard):** Show a stable error state when Search Analytics returns an HTTP error or malformed data ([#11603](https://github.com/diegosouzapw/OmniRoute/pull/11603)) — thanks @pacocartones +- **test(gamification):** pin the aggregate profile level to the XP-derived semantics of #11604 — `getAggregateXp()` now derives `currentLevel` from the summed XP (`calculateLevel(sum)`), not `MAX(stored current_level)`, and the #3484 fixture levels are aligned with the XP curve ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)). ([#11676](https://github.com/diegosouzapw/OmniRoute/pull/11676)) — thanks @jonlwheat2-gif / @pacocartones +- **fix(dashboard):** Keep the Profile level and progress aligned with aggregate XP, including bounded handling for invalid totals ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)) — thanks @pacocartones +- **fix(dashboard):** Prevent locked hidden badges from revealing their icon or opening private badge details before they are earned ([#11605](https://github.com/diegosouzapw/OmniRoute/pull/11605)) — thanks @pacocartones +- **fix(dashboard):** Restore keyboard focus after shared modals close and cancel delayed autofocus during cleanup ([#11607](https://github.com/diegosouzapw/OmniRoute/pull/11607)) — thanks @pacocartones +- **fix(dashboard):** Enable Enter and Space activation for clickable data-table rows without hijacking nested controls ([#11610](https://github.com/diegosouzapw/OmniRoute/pull/11610)) — thanks @pacocartones +- **fix(autoCombo,sse):** vendor-retired catalog ids are dropped from the auto-combo candidate pool and no longer win on leftover `arena_elo` / `user_override` rows; `getModelLifecycleDecision` consults `model-lifecycle.json` (prefix-stripped) so aggregator traffic is not `untracked` for ids the snapshot already knows ([#11625](https://github.com/diegosouzapw/OmniRoute/issues/11625)) ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)) — thanks @MumuTW +- **test(chatcore):** move Codex/Claude combo fixtures off models the lifecycle guard now rejects — `gpt-5.1-codex`/`gpt-5-codex` are vendor-retired (snapshot, #11626) and `claude-3-5-sonnet-20241022` is shut down, so native-passthrough and combo-fallback tests switched to `gpt-5.6-sol` and `claude-sonnet-4.6` ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)). ([#11675](https://github.com/diegosouzapw/OmniRoute/pull/11675)) — thanks @jonlwheat2-gif / @MumuTW +- fix(opencode-plugin): stop a free-tier budget that rounds up across a magnitude from rendering as `1000K`/`1000M` in the model picker — `fmtTokens` chose its unit from the raw token count and then rounded with `toFixed(1)`, so 999,950–999,999 printed as `1000K` rather than `1M` and just under a billion printed as `1000M` rather than `1B` ([#11684](https://github.com/diegosouzapw/OmniRoute/pull/11684)) — thanks @abhisheksharma2411 +- Cap prose-derived Antigravity quota resets at the configured model cooldown maximum while preserving authoritative Retry-After headers and Google RetryInfo hints. (#11695) Thanks @pacocartones. ([#11823](https://github.com/diegosouzapw/OmniRoute/pull/11823)) +- Compare `_rebaseline_*` notes structurally in the ratchet-bank verifier so an object-valued note no longer reports itself as rewritten on an unmodified tree — the nightly `bank-ratchet-shrinks` lane had been aborting on every run since 2026-08-11, leaving 41,672 lines of earned file-size shrinks unbanked (#11702). ([#11892](https://github.com/diegosouzapw/OmniRoute/pull/11892)) — thanks @MumuTW +- **fix(build):** `prepublish.ts` bundles the ChatGPT Web (Codex) MCP bridge through `runBuildTool()` instead of spawning `npx.cmd` raw, fixing the build crash on Node ≥ 20/Windows where `.cmd` shims cannot be spawned without a shell (EINVAL) ([#11704](https://github.com/diegosouzapw/OmniRoute/issues/11704)) ([#11706](https://github.com/diegosouzapw/OmniRoute/pull/11706)) — thanks @jonlwheat2-gif +- fix(codex): keep `parallel_tool_calls:false` on the translated Codex Responses Lite path (#11707) ([#11984](https://github.com/diegosouzapw/OmniRoute/pull/11984)) — thanks @hartmark +- **fix(tests):** the new Gemini Web Images retirement test (#11708) called the now-async `getExecutor()` synchronously, throwing `TypeError: getExecutor(...).getProvider is not a function` instead of asserting Gemini Web chat stays available. Awaited to match the R0.3 executor-registry refactor (#11220). ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(search):** Allow REST and MCP web-fetch requests to select the advertised `anysearch-search` provider explicitly ([#11716](https://github.com/diegosouzapw/OmniRoute/pull/11716)) — thanks @pacocartones +- **fix(providers):** Keep overnight peak-hour protection active after midnight according to the window's configured start day ([#11718](https://github.com/diegosouzapw/OmniRoute/pull/11718)) — thanks @pacocartones +- **fix(providers):** correct all five g4f.space gateway descriptions to document proof-of-work credits or member API keys, variable limits, and the remote prompt boundary instead of promising free keyless access at five requests per minute ([#11738](https://github.com/diegosouzapw/OmniRoute/pull/11738)). +- **fix(db):** `model_capabilities` is created by a migration instead of lazily on the first models.dev sync, so a clean install and an upgraded install converge on the same schema regardless of which features have run ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(ci):** `check:install-upgrade` now fails on an `npm` install truncated by ENOSPC (npm reports it as a warning and still exits 0), authenticates its health probe so the version assertion works against the hardened health payload, frees the clean-install tree before the upgrade phase, and no longer reports a schema divergence computed from a boot that never served ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(routing):** harden session-affinity key derivation against oversized explicit IDs and empty structural-payload collisions, while recognizing Gemini and common text request shapes ([#11746](https://github.com/diegosouzapw/OmniRoute/pull/11746)) — thanks @MumuTW +- **fix(packages/browser-pool):** regenerate `package-lock.json` so the `packages/browser-pool` workspace's locked `playwright`/`@types/node` (and transitives) match its `package.json` specs, fixing cache-only/offline installs (`npm ci --offline`, Nix `buildNpmPackage`) that previously failed with `ENOTCACHED` ([#11747](https://github.com/diegosouzapw/OmniRoute/issues/11747)) — thanks @benjaminkitt ([#11784](https://github.com/diegosouzapw/OmniRoute/pull/11784)) — thanks @geekyNads +- **fix(assets):** Stop distributing 79 provider and CLI logos without verified source/license provenance, keep every provider/tool available through the internal generic icon, and prevent those IDs from falling through to an external icon CDN. ([#11750](https://github.com/diegosouzapw/OmniRoute/pull/11750)) +- **fix(providers):** retire the common `chatgpt-web` / `cgpt-web` provider because its pre-key and proof-of-work provenance could not be cleared ([#11754](https://github.com/diegosouzapw/OmniRoute/pull/11754)) — explicit chat and image requests, slash-prefixed models, persisted aliases, proxy overrides, and combo targets now fail closed with a sanitized HTTP `410` / `PROVIDER_RETIRED` before upstream dispatch. Migration 163 tombstones matching connections and invalidates their active leases without deleting history or widening API-key allowlists. The independent `chatgpt-web-codex` / `cgpt-codex` provider and its MIT-noticed vendored implementation remain available. +- **fix(usage):** quota and usage refresh no longer 409 when an exclusive lease reserves the connection ([#11758](https://github.com/diegosouzapw/OmniRoute/pull/11758)) — thanks @TheDemonTuan +- Keep the embedding registry's vector width and `embedding` type on models when a synced model exists for the same id, so `/v1/models` no longer reports registry-described embedding models widthless or untyped (#11761) — thanks @rahilmavani +- Correct `google/gemini-embedding-001` on the OpenRouter route to 3072 dimensions, the width it returns when `dimensions` is not sent (#11761) — thanks @rahilmavani +- **fix(vision-bridge):** Restrict automatic vision routing and fallbacks to models available in authoritative live provider catalogs, including aliases and effort variants ([#11767](https://github.com/diegosouzapw/OmniRoute/issues/11767)) — thanks @pacocartones ([#11813](https://github.com/diegosouzapw/OmniRoute/pull/11813)) +- **fix(providers):** reclassify Cerebras as a one-time $5 signup credit (payment method required, 30-day validity), not a recurring no-card 1M tokens/day trial ([#11773](https://github.com/diegosouzapw/OmniRoute/issues/11773)) ([#12591](https://github.com/diegosouzapw/OmniRoute/pull/12591)) — thanks @HouMinXi +- fix(cli): stop prepublish from re-rebuilding the already-built ESM-only opencode-plugin dist (#11787) ([#11990](https://github.com/diegosouzapw/OmniRoute/pull/11990)) — thanks @hartmark +- **fix(db):** Fresh installs no longer log a non-fatal `no such table: compression_run_telemetry` warning when retention cleanup runs before the lazily-created telemetry table exists ([#11802](https://github.com/diegosouzapw/OmniRoute/pull/11802)) — thanks @RaviTharuma +- fix(sse): set X-OmniRoute-Selected-Connection-Id on successful combo dispatches so downstream consumers stop falling back to an empty connection id (#11810) ([#11986](https://github.com/diegosouzapw/OmniRoute/pull/11986)) — thanks @hartmark +- **fix(services):** embedded CLIProxyAPI lifecycle checks now use public `/healthz`, while model discovery uses the configured dedicated data-plane API key instead of the management password ([#11811](https://github.com/diegosouzapw/OmniRoute/pull/11811)) — thanks @RaviTharuma +- Fix persisted model steps returning an error in the playground route simulator, and warn when structural steps cannot be simulated. ([#11862](https://github.com/diegosouzapw/OmniRoute/pull/11862)) — thanks @pacocartones +- **fix(providers):** Antigravity's dynamic mitmAlias table no longer routes `gemini-3.7-flash-{high,medium,low}` to a literal tier-suffixed upstream id just because one connected account's own discovery listed it directly — those display ids always resolve through the safe `gemini-3.7-flash-tiered` static alias, so one account's Google-provisioned access no longer 404s every sibling account of the provider ([#11824](https://github.com/diegosouzapw/OmniRoute/issues/11824), [#11651](https://github.com/diegosouzapw/OmniRoute/issues/11651)) ([#11988](https://github.com/diegosouzapw/OmniRoute/pull/11988)) — thanks @hartmark +- **fix(dashboard):** Expose Profile loading, errors, page structure, and XP progress to assistive technologies ([#11838](https://github.com/diegosouzapw/OmniRoute/pull/11838)) — thanks @pacocartones +- **fix(socks):** `Agent.connectTimeout` now bounds both SOCKS handshake and TLS connect, and `family === null` no longer falls back to `fetch-socks` ([#11842](https://github.com/diegosouzapw/OmniRoute/pull/11842)) — thanks @maxmad64bis +- **perf(compression):** OOM mitigations for large payload hashing, memoization, and token estimation ([#11844](https://github.com/diegosouzapw/OmniRoute/pull/11844) — thanks @AndrianBalanescu) +- Excluded Next.js Node File Trace manifests (`*.nft.json`) from the published npm tarball. They are build-time metadata and are never read while serving, but had grown to 668.7 MB — 61% of the package — which pushed the upload past the registry limit and made `npm publish` fail with `413 Payload Too Large`. ([#11864](https://github.com/diegosouzapw/OmniRoute/pull/11864)) +- **fix(cli):** keep the global OmniRoute server `--base-url` separate from provider-node `--endpoint` values in add, update, and validate commands ([#11860](https://github.com/diegosouzapw/OmniRoute/pull/11860)) — thanks @pacocartones +- **fix(config):** Nous Research's `Hermes-4-405B` model now displays as "Hermes 4 405B (Nous Research)" in both the provider registry and the free-model catalog, instead of the mislabelled "Hermes 4 7B" ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#11993](https://github.com/diegosouzapw/OmniRoute/pull/11993)) +- **fix(provider/nous):** inject required user= tag into Nous Research inference requests to resolve upstream 400 "missing tags" error ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#12044](https://github.com/diegosouzapw/OmniRoute/pull/12044)) +- **fix(build):** `npm run build` now fails in one second with a named package and a copy-pasteable fix when npm silently drops an externalised optional native dependency, instead of dying four minutes in with `Module not found: Can't resolve 'better-sqlite3'` ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- **fix(install):** `postinstall` no longer throws `ReferenceError: isAndroid is not defined` — failing the whole `npm install` — when the `better-sqlite3` rebuild fallback times out; the manual-fix guidance is reachable again ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- Fixed the Alibaba free-tier allowlist test that went red on its own once the shipped catalog's `validUntil` (2026-08-27) passed, leaving every PR and `main` with a failing `Unit Tests (1/8)`. The test now builds its own packs with dates it controls, and covers the expired-pack fallback that production has actually been serving. ([#11867](https://github.com/diegosouzapw/OmniRoute/pull/11867)) +- Split the npm registry upload into its own GitHub-hosted job. npm refuses `--provenance` from a self-hosted runner (`422 ... Only "github-hosted" runners are supported`), which blocked the v3.8.50 publish; the heavy verification cannot move to a hosted runner, so it now hands the proven tarball over instead. ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(ui):** the shared `DataTable` loading state no longer reads its decorative ⏳ glyph out to assistive technology, and now carries the same `role="status"` / `aria-live="polite"` / `aria-busy="true"` semantics as `PageLoading` ([#11869](https://github.com/diegosouzapw/OmniRoute/pull/11869)) — thanks @pacocartones +- **fix(api):** Bound the `timeout` accepted by `POST /api/providers/[id]/login` to the same 15s–600s window the Adobe Firefly and Conol browser-login paths already enforce, so a request can no longer pin a headful browser and the single active-login slot with an arbitrarily large poll budget. ([#11871](https://github.com/diegosouzapw/OmniRoute/pull/11871)) — thanks @pacocartones +- **fix(api):** Stop the tunnel and MITM routes from returning raw child-process error text, which disclosed host paths, binary install locations and Tailscale `tskey-*` credentials in a body some of these routes serve to non-loopback callers, and return a real 400 instead of a framework 500 when a tunnel request body fails validation. ([#11872](https://github.com/diegosouzapw/OmniRoute/pull/11872)) — thanks @pacocartones +- **fix(open-sse):** stream TTFT/ITL are sampled from a monotonic clock (`performance.now`) instead of `Date.now`, so an NTP correction or wall-clock jump can no longer inflate a genuine TTFT or produce a negative one that the `ttft >= 0` guard silently drops — protecting the router's OTel/EWMA/`usage_history`/speed-ranking signals ([#11873](https://github.com/diegosouzapw/OmniRoute/pull/11873)) — thanks @pacocartones +- **fix(providers):** xAI `reasoning_effort: "xhigh"` now reaches grok-4.6+ instead of being silently clamped to `"high"` ([#11879](https://github.com/diegosouzapw/OmniRoute/pull/11879)) — thanks @NoxzRCW +- **fix(dashboard):** the providers page no longer crashes into the error boundary when a provider id collides with an `Object.prototype` member (`constructor`, `__proto__`); icon lookups are own-property guarded ([#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)) — thanks @NoxzRCW +- **fix(skills):** injected skill tools declared in shorthand (`{"content": "string"}`) now forward valid JSON Schema, unblocking providers that validate tool schemas strictly such as Zhipu GLM on the Console Go tier ([#11881](https://github.com/diegosouzapw/OmniRoute/pull/11881)) — thanks @NoxzRCW +- **fix(api):** `POST /api/playground/simulate-route` now surfaces `combo-ref` and `provider-wildcard` persisted combo steps with a specific warning (naming the referenced combo, or the unresolved `provider/model` wildcard) instead of folding them into a generic "unsupported step" count; a `provider-wildcard` step is also now included as an unresolved target so the operator can see it is in the route (ported from [#11882](https://github.com/diegosouzapw/OmniRoute/pull/11882) — thanks @NoxzRCW). ([#11930](https://github.com/diegosouzapw/OmniRoute/pull/11930)) +- **fix(translator):** the streaming OpenAI→Claude translator keeps upstream usage, including prompt-cache tokens, when it arrives on a trailing `choices: []` chunk (Fireworks and any upstream using `stream_options.include_usage`) ([#11883](https://github.com/diegosouzapw/OmniRoute/pull/11883)) — thanks @NoxzRCW +- **fix(cli):** `omniroute update --apply` now tells you explicitly whether a running server was detected and, if so, that you must run `omniroute restart` to apply the update — it never restarted anything and previously implied the update was already live once files were installed. The dashboard's npm-mode Update flow (`/api/system/version`) now tries OmniRoute's own PID-file-managed supervisor before falling back to pm2, and reports an honest "restart required" step instead of a silent pm2-only "skipped" that read like a completed update. The server-side latest-version lookup backing the dashboard's update banner also gained `--prefer-online`, closing the same stale-npm-cache class already fixed in the CLI's own copy for #4376 ([#11885](https://github.com/diegosouzapw/OmniRoute/issues/11885)). ([#12005](https://github.com/diegosouzapw/OmniRoute/pull/12005)) +- **fix(resilience):** clear persisted LKGP pins when a target suffers connection/provider exhaustion or is skipped before dispatch due to cooldown/exhaustion/unavailability, preventing subsequent requests from repeatedly prioritizing known-dead providers ([#11911](https://github.com/diegosouzapw/OmniRoute/issues/11911)). ([#12013](https://github.com/diegosouzapw/OmniRoute/pull/12013)) — thanks @HouMinXi +- **fix(sse):** the OpenAI→Claude stream translator now defers the terminal `message_delta`/`message_stop` emission until the real usage block has arrived (or a genuine end-of-stream flush forces it) instead of emitting it immediately on `finish_reason` — previously, when the trailing usage-only chunk (`{"choices":[],"usage":{...}}`) arrived *after* the `finish_reason` chunk (the normal order for Fireworks/vLLM/Together and other `stream_options.include_usage` upstreams), the client-visible `message_delta` still carried stale/zero usage even though `state.usage` was internally corrected too late to matter (ported from [#11915](https://github.com/diegosouzapw/OmniRoute/pull/11915) — thanks @HouMinXi). ([#11933](https://github.com/diegosouzapw/OmniRoute/pull/11933)) +- **fix(sse):** `stripStore()` now forces `store=false` for stateless OpenAI-compatible Responses-API targets (unless the connection has explicitly opted in via `providerSpecificData.openaiStoreEnabled`), instead of only handling the `openai`/`agentrouter` cases — a client-supplied `store` value previously passed through untouched to backends that don't actually persist responses server-side ([#11916](https://github.com/diegosouzapw/OmniRoute/pull/11916) — thanks @HouMinXi). +- **fix(models):** custom provider-node models (synced, custom, and alias-backed) now appear under their configured prefix in the unified catalog when the operator's model-id prefix mode is canonical, instead of being dropped whenever alias-inclusion was otherwise disabled ([#11918](https://github.com/diegosouzapw/OmniRoute/pull/11918) — thanks @HouMinXi). +- **fix(models):** the unified model catalog now suppresses stale static registry models (including effort-tier variants) for any provider whose active connection has an authoritative live synced catalog, not only providers already using exclusive-synced-listing — a connection with `providerUsesAuthoritativeLiveCatalog` previously kept serving both the live-synced models and the stale static rows side by side ([#11919](https://github.com/diegosouzapw/OmniRoute/pull/11919) — thanks @HouMinXi). +- **fix(ui):** `ProviderIcon`'s three remaining unguarded lookups (`PROVIDER_ICON_ALIASES`, `LOCAL_SVG_ALIASES`, `THEMED_SVGS`) now use `Object.hasOwn()` own-property checks — a provider id such as `constructor` or `__proto__` previously resolved through the prototype chain instead of falling through to the unknown-provider CDN fallback (`getLobeProviderIcon()` itself was already guarded by [#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)); `ProviderPageHeader`'s `color` field is also now optional, matching the rest of the component's defensive typing (ported from [#11920](https://github.com/diegosouzapw/OmniRoute/pull/11920) — thanks @HouMinXi). ([#11935](https://github.com/diegosouzapw/OmniRoute/pull/11935)) — thanks @NoxzRCW +- fix(ollama): preserve multi-byte UTF-8 content split across stream chunks in the Ollama NDJSON transform, which previously corrupted CJK/emoji into U+FFFD (#11921) — thanks @pacocartones +- **fix(providers):** OrcaRouter chat requests now target `/v1/chat/completions` instead of the bare `/v1` API root, fixing the upstream `404 Invalid URL (POST /v1)` ([#11923](https://github.com/diegosouzapw/OmniRoute/pull/11923)). — thanks @echel0nn +- **fix(plugins):** deliver the `onStreamComplete` event to disk-installed plugins. The event shipped in v3.8.50 (#9669) was emitted internally but had no plugin-facing wiring, so no plugin could ever subscribe: the manifest schema silently dropped `hooks.onStreamComplete`, and the loader/manager only knew the seven legacy hooks. `onStreamComplete` is now a declarable manifest hook, wired through the loader and registered by the manager like the other hooks, and its payload carries a `requestId` so consumers can correlate the stream-completion event with the originating request ([#11934](https://github.com/diegosouzapw/OmniRoute/pull/11934)) — thanks @amaleta / @pacocartones +- **fix(db):** the Qdrant embedding-model dropdown now lists local/self-hosted providers (Ollama, LM Studio, vLLM, etc.) — an active connection is treated as "configured" when the provider allows an optional API key, not only when it has a real key or OAuth, so a running local embedding provider is no longer hidden from the picker ([#11949](https://github.com/diegosouzapw/OmniRoute/issues/11949)) ([#11995](https://github.com/diegosouzapw/OmniRoute/pull/11995)) +- **fix(providers):** Vertex AI Anthropic partner-model discovery now calls the Model Garden `v1beta1` publisher list (`/v1beta1/publishers/anthropic/models`, global) and parses its `publisherModels` envelope, so Claude models auto-synced from Vertex populate the active live catalog and route at request time instead of returning `Model '' is not available in the active live catalog` ([#11991](https://github.com/diegosouzapw/OmniRoute/issues/11991)) — thanks @fabioluissilva ([#11998](https://github.com/diegosouzapw/OmniRoute/pull/11998)) +- **fix(cli):** support `--base-url` alongside `--endpoint` in `omniroute nodes add`, `update`, and `validate` subcommands to prevent global `--base-url` shadowing issues ([#11999](https://github.com/diegosouzapw/OmniRoute/issues/11999)). ([#12033](https://github.com/diegosouzapw/OmniRoute/pull/12033)) — thanks @HouMinXi +- **fix(providers):** `cloudflare-ai` no longer refuses image content parts for every Workers AI model ([#12002](https://github.com/diegosouzapw/OmniRoute/pull/12002)) — the plain-string `content` requirement behind #2539 is carried by the _model_ schema, not by the `/ai/v1/chat/completions` endpoint (measured: an all-text part array returns 200 on `@cf/mistralai/mistral-small-3.1-24b-instruct`, `@cf/meta/llama-4-scout-17b-16e-instruct` and `@cf/meta/llama-3.3-70b-instruct-fp8-fast`, and 400 on the text-only `@cf/qwen/qwen2.5-coder-32b-instruct`). `transformRequest()` flattened every array and threw on the first non-text part (#6390), so image input was refused for vision-capable Cloudflare models that accept it. All-text arrays are still flattened — the one shape every model accepts — while an array carrying a non-text part is passed through untouched, so the attachment is still never silently dropped. Regression guards: `tests/unit/cloudflare-ai-image-parts-6390.test.ts`. — thanks @davidlinfr +- **fix(resilience):** decouple the limiter-managed execution backstop from the queue-wait budget — new `requestQueue.executionMaxWaitMs` (env `RATE_LIMIT_EXECUTION_MAX_WAIT_MS`, default 600000 = 10 min) now feeds Bottleneck's post-dispatch `expiration`, while `requestQueue.maxWaitMs` keeps its documented queue-wait semantics. Previously the queue-wait budget doubled as the execution expiration, so legitimate long-running calls on non-incremental gateways (whole generation buffered before the first upstream byte, e.g. Console Go / Command Code tiers serving GLM models) were killed mid-flight at the queue budget with a false 504 `RATE_LIMIT_EXECUTION_TIMEOUT` — the local limiter undercut the provider-aware upstream fetch-start timeouts. The surfaced 504 message now names `requestQueue.executionMaxWaitMs`; the error keeps the #4165 guarantees (disclaims an upstream timeout, preserves the Bottleneck error as `cause`, branded code + trusted provenance, classified request-scoped so combo falls back). A real queue-wait bound (the `Promise.race` around `limiter.schedule()` sketched in #9533) remains future work. (#12025) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **Call logs:** keep the `error` field when an artifact exceeds the storage cap, instead of replacing it with the omission marker. The error is the only field that says *why* a request failed and is typically ~90 bytes next to the multi-hundred-KB bodies that trip the cap, so dropping it left a size-limited row undiagnosable — a provider outage, a local timeout and an upstream 400 all rendered identically. It is now preserved at every fallback stage, truncated to 4KB if it is itself large ([#12026](https://github.com/diegosouzapw/OmniRoute/issues/12026)). ([#12095](https://github.com/diegosouzapw/OmniRoute/pull/12095)) — thanks @ntdat812 +- **fix(diagnostics):** preserve the error field (truncated to 4KB with a `[truncated: …]` suffix) in every call-log artifact size-limit fallback stage. Previously the minimal fallback replaced the error with `[omitted: call log artifact size limit exceeded]`, so an oversized artifact row showed nothing about WHY the request failed — e.g. 91 of 847 opencode-go 504 rows on one production instance were undiagnosable from the dashboard. Oversized request/response bodies are still omitted exactly as before; the error cap is independent of the payload sizes that tripped the fallback. (#12026) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **fix(sse):** OpenAI Responses clients that declare the native `web_search` tool now receive a spec-shaped `web_search_call` output item with `action.sources` alongside the preserved function-call round-trip, so search results executed through OmniRoute's own search backend are consumable by standard Responses clients (Codex, pi-web-access, …). ([#12031](https://github.com/diegosouzapw/OmniRoute/pull/12031)) — thanks @watchingdogs +- **fix(cli):** use in-thread alias resolver hooks on modern runtimes to avoid deprecation noise and improve Node.js forward compatibility ([#12073](https://github.com/diegosouzapw/OmniRoute/issues/12073)). ([#12083](https://github.com/diegosouzapw/OmniRoute/pull/12083)) — thanks @xiaoyaner0201 +- **fix(combo):** an operator-set **Agent Features → Context length** on a combo is now honored at request time. The value was persisted and advertised through `/v1/models`, but `resolveComboContextLimit()` never consulted it — so a multi-target combo whose members carry no per-model window fell through to the provider's generic `defaultContextLength` (openrouter 128000, command-code 200000) and rejected large requests with `Input exceeds context window … limit 128000` despite the combo being explicitly sized much larger. An identical single-target combo worked, because it collapses to its concrete target before the guard runs. Invalid values (0/negative/NaN/Infinity) are ignored, so the existing target → combo-min → fallback order is unchanged. ([#12090](https://github.com/diegosouzapw/OmniRoute/pull/12090)) — thanks @adivekar-utexas +- **fix(sse):** passthrough streams now estimate usage on finish when upstream closes without usage even with `stream_options.include_usage` — avoids `0 tokens / 0%` for providers that stay silent (and correctly handles trailing empty-choices usage) ([#12151](https://github.com/diegosouzapw/OmniRoute/pull/12151)) — thanks @maxmad64bis +- **fix(combos):** clearing an agent feature in the combos editor now persists — unchecking context cache protection, or emptying the system message or tool filter, sends an explicit `null` instead of dropping the field from the `PUT` body, which the update merge read as "leave unchanged" ([#12177](https://github.com/diegosouzapw/OmniRoute/pull/12177)) — thanks @foreveryh / @ntdatt812 +- **fix(memory):** self-hosted embedding endpoints now vectorize — the vector width is measured from the first embedding that comes back instead of being read from a registry that cannot describe them, so `vec_memories` is created and memories stop piling up unvectorized behind a green health check ([#12180](https://github.com/diegosouzapw/OmniRoute/pull/12180)) — thanks @kanade-hoshino / @ntdatt812 +- **fix(translator):** the leading `system` message now reaches Responses-API upstreams when its `content` is a content-part array — it was read as `typeof content === "string" ? content : ""`, so a prompt-caching client (Anthropic `cache_control`, the shape LiteLLM and the Anthropic SDK emit) had its entire system prompt replaced by an empty `instructions`. The request was still accepted with a normal `prompt_tokens` count, so the model answered with no instructions and nothing in the response said they were missing. Mid-conversation system turns already handled the array shape ([#7056](https://github.com/diegosouzapw/OmniRoute/pull/7056)); only the first one did not ([#12206](https://github.com/diegosouzapw/OmniRoute/issues/12206)). Regression guard: `tests/unit/translator-openai-responses-system-content-parts.test.ts`. ([#12207](https://github.com/diegosouzapw/OmniRoute/pull/12207)) — thanks @Gorillaz322 +- **fix(free-tier):** `/api/free-tier/summary` no longer computes its totals from a Radar feed built before the catalog the running release ships. When the cached feed is older — or carries no build date at all — the route answers from the shipped catalog, resolved through the operator's local model state so disabled and tombstoned models stay out of the numbers ([#12215](https://github.com/diegosouzapw/OmniRoute/pull/12215)). — thanks @maxmad64bis +- **fix(oauth):** Keep a Claude personal workspace and a Team organization as separate connections — they share the same email and `accountUUID`, so the email-only OAuth dedup let the second login overwrite the first account's tokens; `organizationUUID` now disambiguates them, the way `workspaceId` does for Codex ([#12222](https://github.com/diegosouzapw/OmniRoute/pull/12222)) — thanks @davidebaraldo +- **fix(resilience):** a 402 on a single paid model of a passthrough/gateway provider (e.g. `kilo-gateway`, `ollama-cloud`) no longer terminalizes the whole connection with a never-auto-recovered `credits_exhausted` status — only the paid model is locked out, so free models on the same key keep serving. 402 variant of [#3027](https://github.com/diegosouzapw/OmniRoute/issues/3027). Single-credential providers are unaffected — a 402 there is still treated as the key being genuinely out of credit ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239) / [#10616](https://github.com/diegosouzapw/OmniRoute/issues/10616)) ([#12242](https://github.com/diegosouzapw/OmniRoute/issues/12242)) — thanks @brick30llc-ctrl ([#12266](https://github.com/diegosouzapw/OmniRoute/pull/12266)) — thanks @geekyNads +- **fix(sse):** trust `finish_reason: "length"`/`"max_tokens"` over the reasoning-consumed-token ratio in response quality validation, so a reasoning model truncated below the old 90% threshold correctly fails and retries instead of returning empty content as a silent "success" ([#12262](https://github.com/diegosouzapw/OmniRoute/pull/12262)) — thanks @brick30llc-ctrl +- **fix(settings):** `PUT /api/settings/cache-config` now persists `alwaysPreserveClientCache` to the flat general settings the runtime cache-control policy actually reads; previously the value landed in the databaseSettings "cache" section and was silently ignored, so the endpoint had no effect on `cache_control` passthrough ([#12304](https://github.com/diegosouzapw/OmniRoute/pull/12304)) — thanks @davidebaraldo +- **fix(grok-cli):** treat omitted SuperGrokPro `creditUsagePercent` as 0% used so Provider Limits still renders a weekly bar (proto3 zero-elision) ([#12312](https://github.com/diegosouzapw/OmniRoute/pull/12312)) — thanks @HouMinXi +- **fix(combo):** Expose the two Auto-Combo scoring factors nobody could set — the combo validation schema and the dashboard weight sliders both declared 13 of the scorer's 15 factors, so `connectionDensity` (spreads load across a provider's connections) and `quality` were dropped on save and offered nowhere. The sliders also shipped their own default table that differed from the engine's on every non-zero factor and summed to 1.05, so the percentages shown next to them added up to 105%. Both lists now match `DEFAULT_WEIGHTS`, and a test keeps them there. Note that a combo whose stored `weights` omitted the two keys was effectively running with them at zero and the other thirteen renormalized upward; it now runs with the engine's intended distribution, so its routing does shift ([#12314](https://github.com/diegosouzapw/OmniRoute/pull/12314)) — thanks @maxmad64bis +- **fix(docs):** The free-tier reference no longer says its numbers come "confidence tagged per row" — no catalog entry carries a confidence tag and the API serves none, so every figure on that page is an estimate of the same, unstated quality. The page now states what an entry does vouch for: an independently documented hard stop (set by hand with the source in a comment, never defaulted to `true`) and a prompt-training disclosure, both with live counts the `check:docs-counts` gate keeps honest ([#12318](https://github.com/diegosouzapw/OmniRoute/pull/12318)) — thanks @maxmad64bis +- **fix(usage):** `adobe-firefly` and `firefly` have had a working usage fetcher since Adobe Firefly landed, but neither was ever added to the registration list, so the provider-plugin manifest, `genericQuotaFetcher` and the free-access quota cache all reported them as having no usage support — while `USAGE_SUPPORTED_PROVIDERS` said the opposite. Both are now declared, which also means their credit balance is fetched like any other declared provider's: `registerGenericQuotaFetchers` now registers a generic quota fetcher for them, and `resolveFreeAccessState` no longer returns early. A test holds the registration list to the dispatcher's switch in both directions, which is what the module's own docstring already asked for in prose ([#12321](https://github.com/diegosouzapw/OmniRoute/pull/12321)) — thanks @maxmad64bis +- **fix(quota):** drop the generic quota cache (agy / Antigravity / Claude OAuth) on an upstream 429 so reset-aware scoring does not keep a 60s stale snapshot, and force-refresh the next usage fetch so inner provider caches cannot recache the same window ([#12325](https://github.com/diegosouzapw/OmniRoute/pull/12325)) — thanks @HouMinXi +- **fix(combos):** deleting a combo now clears its persisted LKGP pins instead of leaving unreachable `key_value` rows behind ([#12326](https://github.com/diegosouzapw/OmniRoute/issues/12326)) ([#12425](https://github.com/diegosouzapw/OmniRoute/pull/12425)) — thanks @KrzysiekSko +- **fix(sse):** Keep ZWNJ (U+200C) and ZWJ (U+200D) in assistant text, reasoning and tool-call arguments — Persian/Kurdish half-space (`ارائه‌دهنده`), Arabic/Indic shaping and emoji sequences no longer lose them; the response de-obfuscation now removes joiners only between ASCII word characters, where the request side inserts them ([#12186](https://github.com/diegosouzapw/OmniRoute/issues/12186)) — thanks @rezjalibd ([#12359](https://github.com/diegosouzapw/OmniRoute/pull/12359)) — thanks @pacocartones +- **fix(resilience):** count resolved upstream 5xx results against the provider circuit breaker on the chat path — `CircuitBreaker.execute()` no longer reads a resolved `{ success: false, status: 5xx }` as a success that cancels the call-site failure, so a provider answering 503s now trips its breaker instead of staying `CLOSED` at `failureCount: 1`; single-model and combo dispatches are each accounted exactly once ([#12254](https://github.com/diegosouzapw/OmniRoute/issues/12254)) ([#12360](https://github.com/diegosouzapw/OmniRoute/pull/12360)) — thanks @pacocartones +- **fix(providers):** resolve the Codex quota auto-ping model from the live provider catalog and lifecycle registry instead of the retired `gpt-5.1-codex-mini`, and pause the ping with one actionable warning when no selectable Codex model exists rather than retrying a shut-down id every cooldown window ([#11905](https://github.com/diegosouzapw/OmniRoute/issues/11905)) ([#12361](https://github.com/diegosouzapw/OmniRoute/pull/12361)) — thanks @pacocartones +- **fix(api):** keep the `{created, data}` wrapper on combo-routed `/v1/images/generations` responses and default Codex image results to `b64_json` on both `/v1/images/generations` and `/v1/images/edits` so Codex CLI's built-in `image_gen` can decode them ([#12268](https://github.com/diegosouzapw/OmniRoute/issues/12268)) ([#12362](https://github.com/diegosouzapw/OmniRoute/pull/12362)) — thanks @pacocartones +- **fix(sse):** Name the shadowed custom provider node when a built-in provider id/alias (e.g. `openference` → `of`) reserves the prefix of an existing OpenAI/Anthropic-compatible node, so the runtime `No active credentials for provider: ` error explains that the prefix routed to the built-in and never reached the node's healthy connections, instead of contradicting the dashboard ([#11943](https://github.com/diegosouzapw/OmniRoute/issues/11943)) — thanks @morpheus9393 ([#12365](https://github.com/diegosouzapw/OmniRoute/pull/12365)) — thanks @pacocartones +- **fix(i18n):** wrap `ccOnboardingKeyPlaceholder` in ICU single quotes across all 43 locales so angle brackets render literally instead of being parsed as rich-text tags, which crashed the Claude Code onboarding block with `INVALID_MESSAGE: INVALID_TAG` ([#12302](https://github.com/diegosouzapw/OmniRoute/issues/12302)) ([#12369](https://github.com/diegosouzapw/OmniRoute/pull/12369)) — thanks @gonisulaimann +- **fix(models):** publish `effort_tiers` on Kimi K3's synced base-model entries (`kmca/k3`, `kmca/k3-256k`) so catalog-only clients (OpenCode, plain SDK pickers) can see and select the reasoning tiers (`low`/`high`/`max`) the synced metadata already carried — the `isSkippedEffortProvider` gate no longer suppresses tier visibility on those base entries, while synthetic `-` variant generation stays prevented and Codex/GLM base models remain excluded unchanged ([#12299](https://github.com/diegosouzapw/OmniRoute/issues/12299)) ([#12371](https://github.com/diegosouzapw/OmniRoute/pull/12371)) — thanks @gonisulaimann +- **fix(guardrails):** keep `auto`/`auto/*` virtual combos exempt from the Vision Bridge `fixedModel` credential guard so a combo target is passed through instead of silently falling back to global auto-selection ([#12237](https://github.com/diegosouzapw/OmniRoute/issues/12237)) ([#12373](https://github.com/diegosouzapw/OmniRoute/pull/12373)) — thanks @pacocartones +- **fix(combo):** capability-filter exhaustion caused by `max_tokens` above every target's known output limit now reports that reason (requested `max_tokens` vs the pool's highest known ceiling) instead of the unrelated "supports structured output" message ([#12229](https://github.com/diegosouzapw/OmniRoute/issues/12229)) — thanks @DW-MediaLab ([#12374](https://github.com/diegosouzapw/OmniRoute/pull/12374)) — thanks @pacocartones +- **fix(auth):** the `least-used` account strategy now prefers accounts without backoff before falling back to oldest `lastUsedAt`, the same tie-break `round-robin` already applies, so a failover no longer lands on a just-rate-limited account for a single request ([#12279](https://github.com/diegosouzapw/OmniRoute/issues/12279)) — thanks @tenshiak ([#12375](https://github.com/diegosouzapw/OmniRoute/pull/12375)) — thanks @pacocartones +- **fix(docker):** the `chatgpt-web-codex-browser` image now finds the Chrome binary under `chrome-linux64/` (Chrome for Testing layout in `playwright:v1.62.0-noble`) as well as the legacy `chrome-linux/`, so the container no longer crash-loops with `exec: --headless=new: not found` ([#12024](https://github.com/diegosouzapw/OmniRoute/issues/12024)) ([#12376](https://github.com/diegosouzapw/OmniRoute/pull/12376)) — thanks @pacocartones +- **fix(providers):** declare `groq/compound` and `allam-2-7b` as non-reasoning models in the curated Groq registry so `reasoning_effort` / `output_config.effort` / `thinking` from Claude Code are stripped instead of forwarded, which Groq rejected with HTTP 400 ([#12134](https://github.com/diegosouzapw/OmniRoute/issues/12134)) ([#12379](https://github.com/diegosouzapw/OmniRoute/pull/12379)) — thanks @pacocartones +- **fix(executors):** `OpencodeExecutor` no longer forces a direct connection when the connection has a proxy assigned in Proxy Management but no per-account proxies: the single-account fast path used to wrap the upstream dispatch in the direct-egress sentinel, discarding the ambient proxy context the chat handler had pinned from `proxy_assignments`, so API-key `opencode`/`opencode-go` connections egressed from the host IP (and hit geoblocks) despite the assignment. The direct pin is now applied only when no ambient proxy context exists ([#11894](https://github.com/diegosouzapw/OmniRoute/issues/11894) — thanks @hizzt) ([#12380](https://github.com/diegosouzapw/OmniRoute/pull/12380)) — thanks @pacocartones +- **fix(api):** `GET /v1/models` with `MODELS_CATALOG_PREFIX_MODE=canonical` (or `?prefix=canonical`) now lists providers whose registry alias is undefined or equal to their own id (Antigravity, Antigravity CLI and other self-aliased built-ins) — their single `provider/model` id was dropped by the alias/canonical duplicate guard in the static, synced, custom and alias-backed catalog loops ([#12058](https://github.com/diegosouzapw/OmniRoute/issues/12058)) — thanks @cheynetom ([#12381](https://github.com/diegosouzapw/OmniRoute/pull/12381)) — thanks @pacocartones +- **fix(translator):** Drop replayed `thinking` blocks that carry no signature (the shape produced from cross-provider `reasoning_content`) instead of stamping the default Claude signature on them, which Anthropic rejected with `400 Invalid signature in thinking block` on the next turn served by an Anthropic rung ([#12105](https://github.com/diegosouzapw/OmniRoute/issues/12105)) — thanks @atescivitci-cmd ([#12386](https://github.com/diegosouzapw/OmniRoute/pull/12386)) — thanks @pacocartones +- **fix(cli):** Resolve Bun's `--preload` polyfill path against the package root instead of `dist/`, so `omniroute` installed with `bun install -g` no longer crashes at startup with `error: preload not found …/dist/open-sse/utils/setupPolyfill.ts` ([#11980](https://github.com/diegosouzapw/OmniRoute/issues/11980)) — thanks @joglomedia ([#12387](https://github.com/diegosouzapw/OmniRoute/pull/12387)) — thanks @pacocartones +- **fix(providers):** `gemini-business` now publishes its model catalog — `/v1/models` and `/v1/providers/gemini-business/models` list the 12 enterprise Gemini ids the executor understands instead of returning an empty list (#12107) ([#12389](https://github.com/diegosouzapw/OmniRoute/pull/12389)) — thanks @pacocartones +- **fix(db):** install `busy_timeout` before the SQLite connection's first statement so a process opening the database while another one closes its WAL connection waits out the transient EXCLUSIVE lock instead of dying with `database is locked`, and recognise the drivers' real BUSY/PROTOCOL/IOERR errors as transient in the corruption probe so the same lock no longer renames the database away as corrupt; deflakes `cross-process contenders never both acquire the same connection` (#12394 — thanks @pacocartones) +- **fix(chat-admission):** derive the `chat_admission_busy` 503 `Retry-After` from observed heavyweight-lease occupancy — the larger of the exhausted `OMNIROUTE_CHAT_ADMISSION_QUEUE_MS` window and the time since capacity last turned over, capped at 60 s — instead of a fixed 1 s (structural) / 2 s (byte-stage) hint that invited Codex/agent fan-out clients to re-send ~1 MiB `/v1/responses` bodies every second into a gate held for the whole SSE lifetime; an idle gate keeps the historical floors ([#12135](https://github.com/diegosouzapw/OmniRoute/issues/12135)) (#12395 — thanks @pacocartones) +- **fix(api-manager):** the API key permissions modal no longer silently drops `allowedCombos` entries its Combo picker cannot render — routing-rule names such as `rt-*`, which the backend already honours — when "All" is clicked and the key is switched back to "Restrict"; those entries now survive the toggle, are listed read-only under the combo list so the count and the list agree, and are saved back verbatim instead of persisting `[]` (deny-all) (#12397 — thanks @pacocartones) +- **fix(catalog):** write the NUL separator of the catalog connection memo key, the provider serviceKind memo key, the Video Bridge promotion group key and a JSON-exactness test fixture as the `\u0000` escape instead of a raw byte — same runtime value, but the raw byte made git, GitHub and ripgrep treat those files as binary (hidden PR diffs, silently skipped searches); a guard test now keeps raw NUL bytes out of `src/`, `open-sse/` and `tests/` (#12403 — thanks @pacocartones) +- **fix(providers):** add `CLAUDE_CODE_CLIENT_VERSION` and `GITHUB_COPILOT_CLI_VERSION` env overrides so Anthropic/Copilot client-version gates can be unblocked without a rebuild ([#12417](https://github.com/diegosouzapw/OmniRoute/issues/12417)) ([#12632](https://github.com/diegosouzapw/OmniRoute/pull/12632)) — thanks @HouMinXi +- **fix(db):** back-fill `last_ping_at` and `last_pinged_reset_key` on `provider_connections` during schema reconciliation so divergent lineages that skipped `123_quota_auto_ping` still accept quota auto-ping writes ([#12470](https://github.com/diegosouzapw/OmniRoute/pull/12470) — thanks @KooshaPari) +- **fix(ci):** document MIT exceptions for `@eloqnt/{config,format-json,format-po}` (next-intl transitive; locked tarballs omit `license`) and keep the A2A lifecycle vitest off the real SQLite persistence seam ([#12581](https://github.com/diegosouzapw/OmniRoute/issues/12581)) ([#12595](https://github.com/diegosouzapw/OmniRoute/pull/12595)) +- OpenCode plugin `/v1/models` catalog fetch now waits 30s by default and attaches HTTP `statusCode` on 401/5xx so host fallback plugins can hop instead of seeing an untyped AbortError/UnknownError. ([#12607](https://github.com/diegosouzapw/OmniRoute/pull/12607)) — thanks @RaviTharuma +- **CI:** the OpenAPI security-tier gate now mirrors `isAlwaysProtectedPath()` in full — it also reads `ALWAYS_PROTECTED_API_PATTERNS`, so the pattern-gated credential routes (`/api/providers/{id}/{claude,codex}-auth/{export,apply-local}`, GHSA-5926-2w35-7h4q) no longer report as unannotated. (#12605) +- **fix(api):** GET /v1/models no longer waits forever on a hung coalesced catalog rebuild; cold-path waits are bounded (`CATALOG_BUILD_TIMEOUT_MS`, default 8s) and a last-good 200 is served when the rebuild times out ([#12627](https://github.com/diegosouzapw/OmniRoute/issues/12627)). ([#12628](https://github.com/diegosouzapw/OmniRoute/pull/12628)) — thanks @RaviTharuma +- **fix(cursor):** a non-array `tool_calls` on an assistant message no longer crashes the cursor request translator with a `TypeError`; both loops now require an array ([#12689](https://github.com/diegosouzapw/OmniRoute/issues/12689)) ([#12691](https://github.com/diegosouzapw/OmniRoute/pull/12691)) — thanks @soroush5 +- **fix(chat):** Chat Completions no longer return empty `content` after a server-owned memory or skills tool; the first provider send and account/model recovery now share one pipeline so a follow-up round-trip can fill the reply ([#12696](https://github.com/diegosouzapw/OmniRoute/issues/12696)) — thanks @HouMinXi ([#12867](https://github.com/diegosouzapw/OmniRoute/pull/12867)) +- **fix(dashboard):** batch-deleting provider keys no longer toasts failure after a successful delete when the confirm button's click event is forwarded as `onAfter` ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) — thanks @HouMinXi +- **fix(glm):** drop the extra 16th argument to `createSSETransformStreamWithLogger` that TypeScript rejected (TS2554) and that never reached the TransformStream ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) ([#12770](https://github.com/diegosouzapw/OmniRoute/pull/12770)) — thanks @HouMinXi +- **fix(dashboard):** pass `nodeMap` into Runtime `QuotaGroup` so a quota monitor in error/exhausted/alerting no longer throws `ReferenceError: nodeMap is not defined`. ([#12868](https://github.com/diegosouzapw/OmniRoute/pull/12868)) — thanks @HouMinXi +- Restricted API keys whose `allowedModels` lists a combo name no longer skip every combo member at pre-dispatch (`ALL_TARGETS_SKIPPED`). Inner-target filtering still applies when the allow-list is a provider prefix or `disableNonPublicModels` is on ([#12899](https://github.com/diegosouzapw/OmniRoute/pull/12899)). — thanks @HouMinXi +- **fix(combos):** Effort-suffixed combo members inherit the base model's `model_context_overrides` row so priority order is not inverted on large requests ([#12926](https://github.com/diegosouzapw/OmniRoute/pull/12926)) — thanks @HouMinXi +- **fix(catalog):** Picker-added `customModels` enter the dispatch-time live catalog so combo members and bare inference no longer 400 ([#12934](https://github.com/diegosouzapw/OmniRoute/pull/12934)) — thanks @HouMinXi +- **fix(docker):** re-derive the Docker build's worker-pool memory budget from the MEASURED ~4.5 GB per-process RSS (the issue owner's own VPS dmesg OOM-killer reproduction) instead of the stale 2560 MB/worker inference #11419 shipped, and lower `OMNIROUTE_BUILD_WORKERS` 3 → 2 so 1 parent + 1 page-data worker (2 processes × 4.5 GB = 9 GB) fits the 12.288 GB (75%) budget on a 16 GB GitHub Actions runner — the previous default (1 parent + 2 workers = 13.5 GB) still overcommitted the runner and kept "Publish to Docker Hub" failing with `cannot allocate memory` after #11419 merged (#7518). ([#11663](https://github.com/diegosouzapw/OmniRoute/pull/11663)) +- **fix(electron):** the packaged Windows desktop build now passes the #7592 cold-restart smoke end to end. Five defects were found and fixed on the way: (1) optional-pack staging failed on any machine whose `tar` is GNU tar — it reads the drive letter in an absolute `-f C:\...` archive path as a remote rsh host (`Cannot connect to C:`), so staging now passes a bare filename with `cwd` at the tarball directory and surfaces tar stderr on failure; (2) the Electron `files` allowlist was missing `lib/loginHeaderCapture.js`, so the packaged main process crashed at startup with `Cannot find module './lib/loginHeaderCapture'`; (3) electron-builder ≥26 injects an `!**/node_modules/**` ignore into every extraResources pattern list that no positive filter can override, which silently dropped the staged runtime `node_modules` (including the better-sqlite3 N-API prebuild) from `resources/app` — a fresh v3.8.50 desktop build would have shipped with no native SQLite driver and reproduced the #7592 sql.js fallback on every machine; an `afterPack` hook now restores the staged `node_modules` after packing; (4) the packaged-app smoke harness redirected `USERPROFILE` into its temp DATA_DIR, but Electron resolves the Roaming profile from `%USERPROFILE%\AppData\Roaming\` (USERPROFILE takes precedence over the APPDATA env var) and the path service throws instead of creating it, so `requestSingleInstanceLock()` returned false and the app exited(0) silently before `app.whenReady()` — the harness now pre-creates the derived tree, and `ensureSmokeEnvDirs` is exported and unit-tested; (5) the #7592 driver assertion parsed a `[DB] Driver: ...` line that the server's primary DB path never printed (only the unused `openDatabaseAsync()` did) — `getDbInstance()` now emits the same line on open so the guard can actually assert the native driver. Also: the smoke env-allowlist unit test hardcoded POSIX paths and could only pass on Linux/macOS; expectations are now host-agnostic, with new regression tests for the USERPROFILE-derived tree and `tarPack` under absolute Windows-style paths ([#11443](https://github.com/diegosouzapw/OmniRoute/pull/11443)) — thanks @jonlwheat2-gif +- fix(sse): stop the auto-combo candidates inspector from silently dropping model-locked/cooled-down rows (#9133) ([#11994](https://github.com/diegosouzapw/OmniRoute/pull/11994)) — thanks @hartmark +- **fix(providers):** Perplexity Web no longer turns upstream stream failures into successful assistant text; pre-content failures remain eligible for fallback, partial output ends with a structured sanitized error, and failed sessions are not persisted ([#12465](https://github.com/diegosouzapw/OmniRoute/pull/12465)) +- **fix(video-bridge):** preserve chronological video and transcript fusion, source-frame timestamps, transcript provenance, full contact-sheet intervals, and pre-abort callback isolation ([#11681](https://github.com/diegosouzapw/OmniRoute/pull/11681)) +- **Z.ai Web:** HTTP 200 streams carrying an upstream error now terminate with a structured failure instead of assistant text plus a normal stop, preserving partial output while allowing pre-content combo fallback. ([#12454](https://github.com/diegosouzapw/OmniRoute/pull/12454)) +- Absorb `Error [AbortError]: request_signal_aborted` and DOMException AbortError shapes in the process-level client-abort crash guard so routine client disconnects no longer kill the server (exit code 7). ([#12165](https://github.com/diegosouzapw/OmniRoute/pull/12165)) — thanks @alvinveroy +- **fix(sse):** Treat Adapta Web `type:error` SSE events as sanitized non-stream failures instead of empty HTTP 200 completions. ([#12459](https://github.com/diegosouzapw/OmniRoute/pull/12459)) +- **fix(autoCombo):** layer 3 (`models_dev_tier`) no longer scores retired models as premium, merge cross-provider capability rows deterministically, and inherit tier scores through variant ids ([#11508](https://github.com/diegosouzapw/OmniRoute/issues/11508)). `getModelsDevTierFitness` now vetoes ids present in `config/quality/model-lifecycle.json` with status `retired` before any other signal; `loadModelCapabilities()` aggregates per `model_id` (any-non-null-true for booleans, max for `limit_context`) instead of last-write-wins over SQLite's undefined row order; a capabilities miss retries through `resolveScoresAs()`'s base id and reports `models_dev_tier:inherited`, matching layers 1–2 (#11492). Adds `getModelsDevTierFitnessWithSource()` and an `invalidateCapabilitiesCache()` test/ops hook. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) — thanks @Meet6338-X +- fix(sse): accept Claude `content:[]` + `stop_reason: max_tokens`/`tool_use` in `detectMalformedNonStream` (match `isEmptyContentResponse`) so Claude Code `/model` probes with `max_tokens: 1` no longer become a false 502 ([#11568](https://github.com/diegosouzapw/OmniRoute/pull/11568)) — thanks @Seramicx +- **fix(security):** sanitize `request.failed` diagnostics before publishing them to live dashboard listeners and replay history, while keeping status, model, provider, latency, and internal call-log diagnostics intact. ([#12469](https://github.com/diegosouzapw/OmniRoute/pull/12469)) +- **fix(pwa):** do not intercept dashboard navigations so Chrome can fall back from a stale HTTP/3 Alt-Svc advertisement (UDP :20128 is unpublished; F5 on a long-lived tab hung until a new tab opened a fresh TCP connection). ([#12767](https://github.com/diegosouzapw/OmniRoute/pull/12767)) — thanks @HouMinXi +- **fix(executors):** handle DuckDuckGo ERR_BN_LIMIT (418) without retrying — when the upstream returns `418 ERR_BN_LIMIT` (rate-limit/ban), the executor now returns the error immediately instead of burning another VQD acquisition that would only count against the IP limit. The retry logic for `418 ERR_CHALLENGE` (unsolved challenge) remains unchanged. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) ([#12110](https://github.com/diegosouzapw/OmniRoute/pull/12110)) — thanks @oyi77 / @Meet6338-X +- Fixed DuckDuckGo streaming responses losing JSON lines and UTF-8 characters split across network chunks. ([#11528](https://github.com/diegosouzapw/OmniRoute/pull/11528)) — thanks @pacocartones +- Harden generated Cloudflare, Vercel, and Deno relays by stripping hop-by-hop, framing, and proxy authentication headers before forwarding requests upstream. ([#11533](https://github.com/diegosouzapw/OmniRoute/pull/11533)) — thanks @pacocartones +- **feat(grok-cli):** Provider Limits shows grok-cli banked reset credits from `GetRemainingResets` (including a real zero; a failed RPC omits the row) and the existing View credits button now calls `ConsumerUiSvc/RedeemReset` for grok-cli. Live tokens use nested fields 10/20/30 (id + Timestamp), not compact 1/2/3. ([#12805](https://github.com/diegosouzapw/OmniRoute/pull/12805)) — thanks @HouMinXi +- **fix(memory):** Embedding Model Quick select, Embedding Source remote dropdown, and Rerank selector now list every configured provider with embedding/rerank support instead of only chat-catalog text matches plus OpenRouter live discovery; a generic OpenAI-compatible `/embeddings` + Cohere-compatible `/rerank` runtime fallback resolves any configured chat provider's embedding/rerank endpoint, so unlisted providers no longer fail with "Unknown embedding provider"; both memory selectors gained a free-text model override ([#11390](https://github.com/diegosouzapw/OmniRoute/pull/11390)) — thanks @rqzbeh +- Harden SQLite upgrades around the historical migration-074 version collision: missing discovery and inspector tables are replayed atomically, pre-existing databases (including setup-created skeletons) receive reusable content-addressed safety snapshots, and Node test/eval probes without `DATA_DIR` are isolated from the operator database. ([#12435](https://github.com/diegosouzapw/OmniRoute/pull/12435)) +- **fix(dashboard):** Moonshot/Kimi Open Platform voucher and cash leftover percentages follow the bucket balance, so an empty wallet no longer paints those rows as 100% while Available is 0% ([#12733](https://github.com/diegosouzapw/OmniRoute/pull/12733)) — thanks @HouMinXi +- **fix(combo):** return non-retryable HTTP 400 when all candidates for a pinned native Codex turn are unavailable due to model-scoped lockout, terminating the turn cleanly while preserving turn continuity and enabling standard Combo routing on subsequent turns ([#12240](https://github.com/diegosouzapw/OmniRoute/pull/12240)) — thanks @mdigitalbh81 +- Fixed auto-router strategies preserving the selected connection through ranking and dispatch when multiple connections share the same provider and model. ([#11530](https://github.com/diegosouzapw/OmniRoute/pull/11530)) — thanks @pacocartones +- Preserve Chat Completions JSON-object response formats when translating requests to the Responses API. ([#11529](https://github.com/diegosouzapw/OmniRoute/pull/11529)) — thanks @pacocartones +- Fix idempotency fingerprints so Responses API requests include `input` and other semantic generation fields using stable serialization, while excluding credentials and request metadata. ([#11532](https://github.com/diegosouzapw/OmniRoute/pull/11532)) — thanks @pacocartones +- **fix(api):** Generated API CLI commands now enforce required OpenAPI request bodies; Combo test commands forward the required `comboName` body, while API keys created by older writers after migration 149 preserve legacy allow-all Combo access without widening explicit empty allowlists — thanks @marcelokarval ([#12070](https://github.com/diegosouzapw/OmniRoute/pull/12070)) +- **fix(grok-web):** treat upstream streaming failures as failures instead of successful assistant text: error-only streams now fail readiness with HTTP 502, while failures after legitimate content preserve that partial output and terminate through the sanitized stream failure path without a normal `stop` completion. ([#12458](https://github.com/diegosouzapw/OmniRoute/pull/12458)) +- HuggingChat now turns HTTP 200 JSONL generation failures into a sanitized 502 before content, or a fixed public stream failure after partial output, so fallback and request persistence no longer record a false successful stop. ([#12456](https://github.com/diegosouzapw/OmniRoute/pull/12456)) +- **fix(providers):** keep 1min.ai HTTP 200 stream errors out of assistant content, preserve partial output, and expose sanitized terminal errors so pre-content failures can fall back. ([#12466](https://github.com/diegosouzapw/OmniRoute/pull/12466)) +- **fix(sessions):** preserve legacy Sessions data when the additive exclusive-session projection is unavailable, returning an empty projection and warning only once per contiguous outage instead of failing the endpoint; the Sessions badge intentionally reflects the merged legacy and exclusive row count introduced by [#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389) ([#11469](https://github.com/diegosouzapw/OmniRoute/pull/11469)) — thanks @KaspaPulse +- Render Nimble Search with the generic provider icon and serve Opper's proven logo locally. ([#12415](https://github.com/diegosouzapw/OmniRoute/pull/12415)) +- **fix(quality):** `config/quality/provider-assets-provenance.jsonl` was stale against the physical `public/providers/` tree (still listing 225 assets and 5 duplicate-content groups after several provider-retirement PRs removed 85 icon files), which would have made `check:provider-asset-provenance` fail on the next full run. Reconciled the manifest to the real 142-file snapshot (142 asset records, 1 duplicate-content group, 2 newly-registered files marked `unresolved` pending provenance research), repinned `auditedCommit`, and updated the regression test's hardcoded counts to match. ([#11876](https://github.com/diegosouzapw/OmniRoute/pull/11876)) +- fix(ui): wrap quota card footer action buttons to prevent clipping Refresh button on narrow cards — when extra action buttons such as `View credits` are present on Codex cards, allow the footer row to flex-wrap with `shrink-0` buttons so `Refresh now` remains visible and clickable across all responsive card widths ([#11464](https://github.com/diegosouzapw/OmniRoute/pull/11464)) — thanks @adevwithpurpose +- Keep Antigravity Gemini usable when the same connection's Claude weekly quota is empty; generic quota cache stays per-connection for every other provider. ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) — thanks @HouMinXi +- Fixed Tailscale tunnel setup to reject ports outside the valid 1-65535 range and safely install Windows MSI packages from paths containing apostrophes. ([#11534](https://github.com/diegosouzapw/OmniRoute/pull/11534)) — thanks @pacocartones +- Classify Cloudflared and Tailscale process-management routes as local-only and spawn-capable while preserving remote access to read-only tunnel status endpoints. ([#11531](https://github.com/diegosouzapw/OmniRoute/pull/11531)) — thanks @pacocartones +- Electron release workflow: the `publish-npm` job now grants `actions: read` to the reusable `npm-publish.yml` it calls (its `publish` job requests it), which is what made GitHub refuse the whole v3.8.50 run at startup and ship the release with zero desktop assets; a `workflow_dispatch` now builds the requested tag instead of the dispatching branch and can skip the npm leg (`publish_npm=false`) when only re-attaching assets ([#11974](https://github.com/diegosouzapw/OmniRoute/pull/11974)) +- **fix(dashboard):** The Combos page usage guide now reads its dismissal through `useSyncExternalStore` instead of correcting SSR state inside an effect, removing an extra commit of the page tree on every load (and the `react-hooks/set-state-in-effect` error it raised). ([#12671](https://github.com/diegosouzapw/OmniRoute/pull/12671)) +- Electron release: `electron/package-lock.json` regained the optional `electron-builder-squirrel-windows` subtree (13 entries) that `npm ci` had been refusing as out of sync, `electron-release.yml` gained a `build_ref` dispatch input and stops regenerating release notes on a re-attach dispatch, and the npm publish workflow attaches the SBOM to the GitHub Release on dispatch publishes too — so the v3.8.51 tag ships every desktop asset and the SBOM like v3.8.49 did ([#12022](https://github.com/diegosouzapw/OmniRoute/pull/12022)) +- Drained the reds every PR against `release/v3.8.51` was born with: documented the three service-worker build-id variables, registered the six retirement/tunnel tests with the mutation gate, approved `eslint-plugin-react-hooks` in the dependency allowlist, added the six `combo.sort.*` strings to `vi` and `pt-BR`, pointed the ChatGPT Web doc at the real migration-168 test, worded the g4f hint around the member key, and realigned four tests to the retired-provider catalog and the legacy-schema fixtures the retirement migrations touch. ([#11940](https://github.com/diegosouzapw/OmniRoute/pull/11940)) +- **fix(providers):** Claude, Grok, LMArena, Notion, and Perplexity web-cookie transports now use pooled `wreq-js` 3.2 instead of the native sidecar, with all nine supported bindings pinned and audited, and the applicable platform binding plus native-license evidence included in each release artifact ([#12429](https://github.com/diegosouzapw/OmniRoute/pull/12429), supersedes [#11753](https://github.com/diegosouzapw/OmniRoute/pull/11753)). +- **fix(providers):** Zed Hosted streaming failures now trigger fallback before content and end partial streams with a sanitized structured error instead of fake assistant text and a normal-success stop. ([#12455](https://github.com/diegosouzapw/OmniRoute/pull/12455)) +- **fix(test):** capture hook result after render (direct commit `b85d0abb0b`) +- **fix(sync):** repair the two regressions the v3.8.50 sync-back left on release/v3.8.51 (direct commit `5ade9e0851`) +- **revert(models):** drop the memo reset hook ported from #10055 (direct commit `282de841ae`) +- **fix(typecheck):** guard the optional connectionId before the reactive model sync (direct commit `f2825bad75`) +- **fix(quota-share):** release the winner's reserved in-flight slot (#11371) ([#11408](https://github.com/diegosouzapw/OmniRoute/pull/11408)) — thanks @oyi77 +- **fix(providers):** stop silently dropping reasoning effort on opencode families (#10788) ([#11409](https://github.com/diegosouzapw/OmniRoute/pull/11409)) — thanks @oyi77 +- **fix(combo):** enable genuine quota-aware routing for generic providers (antigravity, claude, etc.) ([#11411](https://github.com/diegosouzapw/OmniRoute/pull/11411)) — thanks @HouMinXi +- **fix(oauth):** allow expired token retry before deactivation and activate expired retry loop (#P0) ([#11414](https://github.com/diegosouzapw/OmniRoute/pull/11414)) — thanks @HouMinXi +- **perf(executors):** lazy-load the executor registry — defer class imports + construction to first use (#11220) ([#11421](https://github.com/diegosouzapw/OmniRoute/pull/11421)) — thanks @oyi77 +- **fix:** make release/v3.8.50 CI gates green (run 32786966560 root causes) ([#11450](https://github.com/diegosouzapw/OmniRoute/pull/11450)) — thanks @jonlwheat2-gif +- **fix(sse):** exact-domain cookie match and origin-equality URL assertions ([#11452](https://github.com/diegosouzapw/OmniRoute/pull/11452)) — thanks @hartmark +- **fix(providers):** send Responses API payload during OpenAI-compatible validation ([#11454](https://github.com/diegosouzapw/OmniRoute/pull/11454)) — thanks @tuandinh0801 +- **fix(command-code):** fallback to /alpha/generate for Go plan without Provider API access ([#11455](https://github.com/diegosouzapw/OmniRoute/pull/11455)) — thanks @Chewji9875 +- **fix(responses-continuation):** fail closed on a log-truncated stored input/output array ([#11473](https://github.com/diegosouzapw/OmniRoute/pull/11473)) — thanks @hartmark +- **fix(cli):** write jcode settings as [providers.omniroute] in config.toml ([#11484](https://github.com/diegosouzapw/OmniRoute/pull/11484)) — thanks @hongnoul +- **fix(logging):** raise chat-log array truncation cap to 1000 and unify duplicate implementations ([#11499](https://github.com/diegosouzapw/OmniRoute/pull/11499)) — thanks @hartmark +- **fix:** restore live websocket URL helpers ([#11509](https://github.com/diegosouzapw/OmniRoute/pull/11509)) — thanks @PauloFH +- **perf(core):** eliminate async waterfalls across home, cache, and db operations (#11396) ([#11512](https://github.com/diegosouzapw/OmniRoute/pull/11512)) — thanks @HouMinXi +- **fix(kiro):** add runtime.us-east-1.kiro.dev as first-attempt endpoint ([#11517](https://github.com/diegosouzapw/OmniRoute/pull/11517)) — thanks @rifqiawl +- **fix(providers):** remove duplicate 128-tool truncation in OpencodeExecutor (#11444) ([#11518](https://github.com/diegosouzapw/OmniRoute/pull/11518)) — thanks @HouMinXi +- **fix(providers):** poll onboardUser LRO before classifying BYOP ([#11519](https://github.com/diegosouzapw/OmniRoute/pull/11519)) — thanks @rifqiawl +- **fix(translator):** preserve exact tool name casing for Claude tool_use payloads (#11487) ([#11520](https://github.com/diegosouzapw/OmniRoute/pull/11520)) — thanks @HouMinXi +- **fix(kimi-web):** align health probe with executor domain ([#11521](https://github.com/diegosouzapw/OmniRoute/pull/11521)) — thanks @pacocartones +- **fix(cli):** preserve existing admin password during setup ([#11522](https://github.com/diegosouzapw/OmniRoute/pull/11522)) — thanks @pacocartones +- **fix(mitm):** keep Windows DNS elevation off the POSIX sudo path (#11430) ([#11542](https://github.com/diegosouzapw/OmniRoute/pull/11542)) — thanks @official-burak +- **fix(sse):** scale chat admission by ingest byte budget instead of a fixed request count ([#11548](https://github.com/diegosouzapw/OmniRoute/pull/11548)) — thanks @TheDemonTuan +- **fix:** accept Gemini Web cookie exports ([#11554](https://github.com/diegosouzapw/OmniRoute/pull/11554)) — thanks @rafacpti23 +- **fix(i18n):** complete Brazilian Portuguese translations ([#11558](https://github.com/diegosouzapw/OmniRoute/pull/11558)) — thanks @rafacpti23 +- **fix(sse):** explicit types for openai-responses pureHelpers — clears last failing core typecheck gate ([#11567](https://github.com/diegosouzapw/OmniRoute/pull/11567)) — thanks @jonlwheat2-gif +- **fix(opencode):** hydrate Proxy Pool references for no-auth connections ([#11584](https://github.com/diegosouzapw/OmniRoute/pull/11584)) — thanks @AStupidBear +- **fix(ci):** resolve inherited release/v3.8.51 base-red CI failures ([#11608](https://github.com/diegosouzapw/OmniRoute/pull/11608)) — thanks @jonlwheat2-gif +- **fix(db):** fallback load STORAGE_ENCRYPTION_KEY from env files ([#11614](https://github.com/diegosouzapw/OmniRoute/pull/11614)) — thanks @benzntech +- **fix(providers):** mark Brave Search as no longer having a free tier ([#11621](https://github.com/diegosouzapw/OmniRoute/pull/11621)) — thanks @turbolego +- **fix:** pass tool result content through as string in OpenAI/Claude -> Gemini translation ([#11624](https://github.com/diegosouzapw/OmniRoute/pull/11624)) — thanks @Hsia97 +- **fix(sse):** scope credential health to exact key ([#11628](https://github.com/diegosouzapw/OmniRoute/pull/11628)) — thanks @KaspaPulse +- **fix(install):** guard prepare script against missing husky ([#11634](https://github.com/diegosouzapw/OmniRoute/pull/11634)) — thanks @Prajeeth-12 +- **fix(security):** redact NVIDIA_API_KEY at the log sink in the ad-hoc diag script ([#11639](https://github.com/diegosouzapw/OmniRoute/pull/11639)) +- **fix(types):** clear the two type regressions red on release/v3.8.51 ([#11640](https://github.com/diegosouzapw/OmniRoute/pull/11640)) — thanks @jonlwheat2-gif +- **fix(build):** resolve workspace: protocol dependencies to concrete semver versions during prepublish (#11540) ([#11641](https://github.com/diegosouzapw/OmniRoute/pull/11641)) — thanks @HouMinXi +- **fix(search):** properly resolve configured search connection in /v1/responses pipeline (#11524) ([#11642](https://github.com/diegosouzapw/OmniRoute/pull/11642)) — thanks @HouMinXi +- **fix(authz):** trust cf-connecting-ip for via-proxy detection when peer is Cloudflare edge (#11514) ([#11643](https://github.com/diegosouzapw/OmniRoute/pull/11643)) — thanks @HouMinXi +- **fix(security):** compare the OIDC state cookie in constant time ([#11646](https://github.com/diegosouzapw/OmniRoute/pull/11646)) +- **fix(security):** namespace the dedup hash by the calling API key ([#11649](https://github.com/diegosouzapw/OmniRoute/pull/11649)) +- **fix(models):** gate the Codex-native catalog loop on prefix mode (#11632) ([#11662](https://github.com/diegosouzapw/OmniRoute/pull/11662)) — thanks @xiaoyaner0201 +- **fix(dashboard):** project Codex routing quota in account pools ([#11666](https://github.com/diegosouzapw/OmniRoute/pull/11666)) — thanks @KaspaPulse +- **fix(ddgw):** normaliza system no upstream da release v3.8.51 ([#11692](https://github.com/diegosouzapw/OmniRoute/pull/11692)) — thanks @RhianB14 +- **fix(memory):** honor category filter in GET /api/memory ([#11699](https://github.com/diegosouzapw/OmniRoute/pull/11699)) — thanks @Sabeekhann +- **fix(ci):** detect stale workspace lockfile entries ([#11714](https://github.com/diegosouzapw/OmniRoute/pull/11714)) — thanks @pacocartones +- **fix(ci):** stamp dist/BUILD_SHA before the npm publish provenance gate ([#11721](https://github.com/diegosouzapw/OmniRoute/pull/11721)) +- **fix(docker):** decouple the best-effort Bun image from the release manifest ([#11724](https://github.com/diegosouzapw/OmniRoute/pull/11724)) +- **perf(compression):** avoid cloning memo misses twice ([#11727](https://github.com/diegosouzapw/OmniRoute/pull/11727)) — thanks @RaviTharuma +- **fix(docker):** let the bun digest artifact be absent, not fatal ([#11740](https://github.com/diegosouzapw/OmniRoute/pull/11740)) +- **fix(shared):** block cloud-metadata hosts under default remote-image guard ([#11755](https://github.com/diegosouzapw/OmniRoute/pull/11755)) — thanks @santosraju99-hub +- **fix(api):** preserve caller-provided X-Correlation-Id on chat completions ([#11760](https://github.com/diegosouzapw/OmniRoute/pull/11760)) — thanks @aniruddhaadak80 +- **fix(resilience):** lock permanently retired models instead of short backoff (Gemini ban prevention) ([#11762](https://github.com/diegosouzapw/OmniRoute/pull/11762)) — thanks @turbolego +- **fix:** mark Vercel AI Gateway as passthroughModels ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **fix(resilience):** stop hammering permanently-moved endpoints and billing-suspended accounts ([#11774](https://github.com/diegosouzapw/OmniRoute/pull/11774)) — thanks @turbolego +- **fix(lease):** remove global static reservation and gate routing on live active lease occupancy ([#11775](https://github.com/diegosouzapw/OmniRoute/pull/11775)) — thanks @TheDemonTuan +- **fix(ci):** size the install-upgrade gate to a measured run, and log the pack cost ([#11776](https://github.com/diegosouzapw/OmniRoute/pull/11776)) +- **fix(pwa):** stop serving the stale shell after deploys ([#11779](https://github.com/diegosouzapw/OmniRoute/pull/11779)) — thanks @HouMinXi +- **fix(combo):** lock GitHub models rejected as "not supported" for future requests ([#11781](https://github.com/diegosouzapw/OmniRoute/pull/11781)) — thanks @turbolego +- **fix(cli):** probe both IPv4 and IPv6 loopback for server readiness (#11766) ([#11794](https://github.com/diegosouzapw/OmniRoute/pull/11794)) — thanks @vermasomesh835 +- **fix(sse):** preserve CLIProxyAPI wrappers per connection ([#11797](https://github.com/diegosouzapw/OmniRoute/pull/11797)) — thanks @RaviTharuma +- **fix(providers):** honor model auto-fetch opt-in on create ([#11798](https://github.com/diegosouzapw/OmniRoute/pull/11798)) — thanks @RaviTharuma +- **fix(dashboard):** make model auto-fetch order independent ([#11805](https://github.com/diegosouzapw/OmniRoute/pull/11805)) — thanks @RaviTharuma +- **fix(docker):** make GHA cache exports best effort ([#11806](https://github.com/diegosouzapw/OmniRoute/pull/11806)) — thanks @RaviTharuma +- **fix(kiro):** do not permanently ban on 'User is not authorized to make this call' ([#11809](https://github.com/diegosouzapw/OmniRoute/pull/11809)) — thanks @Deftera186 +- **fix(sse):** keep cache-write tokens in OpenAI-shaped usage ([#11814](https://github.com/diegosouzapw/OmniRoute/pull/11814)) — thanks @TheDemonTuan +- **fix(docs):** resolve relative markdown and wiki links across Fumadocs and GitHub wiki ([#11834](https://github.com/diegosouzapw/OmniRoute/pull/11834)) — thanks @AndrianBalanescu +- **fix(chat):** preserve unstripped model string for passthrough provider routing ([#11840](https://github.com/diegosouzapw/OmniRoute/pull/11840)) — thanks @raheemuddin786 +- **fix(mcp):** avoid async initialization inside sync esm wrappers (#11569) ([#11849](https://github.com/diegosouzapw/OmniRoute/pull/11849)) — thanks @HouMinXi +- **fix(sse):** preserve coherent markdown token boundaries across stream translation chunks (#11606) ([#11851](https://github.com/diegosouzapw/OmniRoute/pull/11851)) — thanks @HouMinXi +- **fix(ci):** run the install-upgrade gate on disk, not on the /tmp tmpfs ([#11855](https://github.com/diegosouzapw/OmniRoute/pull/11855)) +- **fix(skills):** expand shorthand property types in injected tool schemas ([#11857](https://github.com/diegosouzapw/OmniRoute/pull/11857)) — thanks @alvinveroy +- **fix(ci):** publish npm from a hosted runner so provenance is accepted ([#11877](https://github.com/diegosouzapw/OmniRoute/pull/11877)) +- **fix(lint):** pin react hooks plugin to 7.0.1 ([#11886](https://github.com/diegosouzapw/OmniRoute/pull/11886)) +- **fix(ci):** surface and clear release-green ESLint failure ([#11890](https://github.com/diegosouzapw/OmniRoute/pull/11890)) — thanks @MumuTW +- **fix(guardrails):** prevent duplicate prompt-injection-guard log output ([#11936](https://github.com/diegosouzapw/OmniRoute/pull/11936)) — thanks @patrykkopycinski +- **fix(auth):** downgrade expected transient states from warn to debug ([#11937](https://github.com/diegosouzapw/OmniRoute/pull/11937)) — thanks @patrykkopycinski +- **fix(codex):** fail over image generation for imported free plans ([#11948](https://github.com/diegosouzapw/OmniRoute/pull/11948)) — thanks @Bl0ck154 +- **fix(codex):** normalize non-stream responses ([#11951](https://github.com/diegosouzapw/OmniRoute/pull/11951)) — thanks @Bl0ck154 +- **fix(images):** forward Antigravity image size ([#11952](https://github.com/diegosouzapw/OmniRoute/pull/11952)) — thanks @Bl0ck154 +- **fix(db):** invalidate connection cache after upsert ([#11953](https://github.com/diegosouzapw/OmniRoute/pull/11953)) — thanks @Bl0ck154 +- **fix(codex):** restore imported account state ([#11954](https://github.com/diegosouzapw/OmniRoute/pull/11954)) — thanks @Bl0ck154 +- **fix(sse):** give extended-thinking targets the reasoning readiness budget ([#11959](https://github.com/diegosouzapw/OmniRoute/pull/11959)) — thanks @abhisheksharma2411 +- **fix(dashboard):** use opaque background and readable text color on cost chart tooltips ([#11960](https://github.com/diegosouzapw/OmniRoute/pull/11960)) — thanks @ZaimMarzuki +- **fix(antigravity):** send complete loadCodeAssist metadata (ideType/platform/pluginType as numeric enums) ([#11969](https://github.com/diegosouzapw/OmniRoute/pull/11969)) — thanks @rifqiawl +- **fix(core):** resolve DB init race condition and reasoning translation ([#12003](https://github.com/diegosouzapw/OmniRoute/pull/12003)) — thanks @raheemuddin786 +- **fix(admission):** exclude reclaimable page cache from the cgroup pressure ratio ([#12017](https://github.com/diegosouzapw/OmniRoute/pull/12017)) — thanks @HouMinXi +- **fix(build):** prune non-production trees in NFT trace excludes and tsconfig ([#12028](https://github.com/diegosouzapw/OmniRoute/pull/12028)) — thanks @Chewji9875 +- **fix(migrations):** add renamed migration compatibility for 056/073/077/101 ([#12036](https://github.com/diegosouzapw/OmniRoute/pull/12036)) — thanks @oyi77 +- **fix(sse):** default crash-guard logger to console.warn, not console ([#12042](https://github.com/diegosouzapw/OmniRoute/pull/12042)) — thanks @HouMinXi +- **fix(catalog):** derive vision/modalities for built-in auto combos from effective target pool ([#12046](https://github.com/diegosouzapw/OmniRoute/pull/12046)) — thanks @Prajeeth-12 +- **fix(release):** the packaged-app smoke verifies the database opened, not a driver line the primary path never prints (twin of #12032) ([#12047](https://github.com/diegosouzapw/OmniRoute/pull/12047)) +- **fix(barrel):** migrate src/app/ off the @/lib/localDb barrel import (#11795 Phase 2) ([#12051](https://github.com/diegosouzapw/OmniRoute/pull/12051)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate src/lib/ off the localDb barrel to direct db imports (#59) ([#12052](https://github.com/diegosouzapw/OmniRoute/pull/12052)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate open-sse, src/shared, src/sse, src/models, src/domain off the @/lib/localDb barrel import (#11795 Phase 4) ([#12053](https://github.com/diegosouzapw/OmniRoute/pull/12053)) — thanks @jonlwheat2-gif +- **fix(barrel):** delete the @/lib/localDb barrel — every consumer migrated (#11795 Phase 5) ([#12055](https://github.com/diegosouzapw/OmniRoute/pull/12055)) — thanks @jonlwheat2-gif +- **fix(radar):** restore D12 public boundary ([#12057](https://github.com/diegosouzapw/OmniRoute/pull/12057)) +- **fix(dev):** bound webpack and Tailwind scans ([#12075](https://github.com/diegosouzapw/OmniRoute/pull/12075)) — thanks @backryun +- **fix(dev):** isolate root layout settings reads ([#12076](https://github.com/diegosouzapw/OmniRoute/pull/12076)) — thanks @backryun +- **fix(dev):** reduce instrumentation executor fan-out (phase 3) ([#12078](https://github.com/diegosouzapw/OmniRoute/pull/12078)) — thanks @backryun +- **fix(dev):** make logging resources HMR-singleton ([#12079](https://github.com/diegosouzapw/OmniRoute/pull/12079)) — thanks @backryun +- **fix(sse):** keep unavailable forced connections scoped ([#12080](https://github.com/diegosouzapw/OmniRoute/pull/12080)) — thanks @keeltrace +- **fix(dev):** isolate batch dispatch from instrumentation ([#12081](https://github.com/diegosouzapw/OmniRoute/pull/12081)) — thanks @backryun +- **fix(ci):** reconcile release test contract drift ([#12082](https://github.com/diegosouzapw/OmniRoute/pull/12082)) — thanks @backryun +- **fix(ci):** accept CVE-2025-68121 in the prebuilt tls-client .so, auto-close base-red issues, guard Scorecard on the default branch ([#12085](https://github.com/diegosouzapw/OmniRoute/pull/12085)) +- **fix(db):** drop three consumer-less 1proxy exports — dead-code base-red on release/v3.8.51 after the barrel deletion (#12055) ([#12087](https://github.com/diegosouzapw/OmniRoute/pull/12087)) +- **fix(api):** clear the six API-route TypeScript regressions the new gate landed red on ([#12094](https://github.com/diegosouzapw/OmniRoute/pull/12094)) +- **fix(release):** never let the tag-push Create Release append auto notes to the curated body ([#12096](https://github.com/diegosouzapw/OmniRoute/pull/12096)) +- **fix(sse):** honor CLIProxyAPI environment API key ([#12099](https://github.com/diegosouzapw/OmniRoute/pull/12099)) — thanks @RaviTharuma +- **fix(oauth):** bind Google refresh to the client that issued the token ([#12106](https://github.com/diegosouzapw/OmniRoute/pull/12106)) — thanks @HouMinXi +- **fix(dashboard):** make RequestLoggerDetail loadable outside Next — CSS via globals.css + CJS/ESM interop (#11703 base-reds) ([#12114](https://github.com/diegosouzapw/OmniRoute/pull/12114)) +- **fix(leases):** project status lease row to lease columns so joined connection PII never escapes ([#12115](https://github.com/diegosouzapw/OmniRoute/pull/12115)) — thanks @geek007git +- **fix(plugins):** do not kill the plugin process when a fire-and-forget hook times out ([#12116](https://github.com/diegosouzapw/OmniRoute/pull/12116)) — thanks @geek007git +- **fix(guardrails):** restore injection-guard logging on middleware-only routes ([#12117](https://github.com/diegosouzapw/OmniRoute/pull/12117)) — thanks @geek007git +- **fix(plugins):** refresh stored manifest from disk on activate so new hook fields reach existing installs ([#12120](https://github.com/diegosouzapw/OmniRoute/pull/12120)) — thanks @geek007git +- **fix(codex):** preserve existing provider state when bulk-import upserts a matching connection ([#12122](https://github.com/diegosouzapw/OmniRoute/pull/12122)) — thanks @geek007git +- **fix(ci):** clear the two base-reds the 2026-08-30 merge batch left on release/v3.8.51 (round 3) ([#12123](https://github.com/diegosouzapw/OmniRoute/pull/12123)) +- **fix(sse):** strip type:'custom' from Claude tools on agentrouter dispatch ([#12126](https://github.com/diegosouzapw/OmniRoute/pull/12126)) — thanks @wahidsadik371-coder +- **fix(sse):** stop advertising video providers the dispatcher cannot run ([#12131](https://github.com/diegosouzapw/OmniRoute/pull/12131)) — thanks @quiterunner-commits +- **fix(search):** treat HTTP 432 and plan limit errors as transient cooldown ([#12139](https://github.com/diegosouzapw/OmniRoute/pull/12139)) — thanks @HouMinXi +- **fix(sse):** refuse an AI Horde queue that cannot fit the request budget ([#12143](https://github.com/diegosouzapw/OmniRoute/pull/12143)) — thanks @quiterunner-commits +- **fix(ci):** clear the base-reds the afternoon merge batch left on release/v3.8.51 (round 5: provider count 352, TS2554/TS2677) ([#12144](https://github.com/diegosouzapw/OmniRoute/pull/12144)) +- **fix(translator):** strip neutral tool_choice when tools absent in Responses-to-Chat (#12141) ([#12166](https://github.com/diegosouzapw/OmniRoute/pull/12166)) — thanks @HouMinXi +- **fix(guardrails):** pass providerId to getResolvedModelCapabilities in checkComboVision (#12112) ([#12169](https://github.com/diegosouzapw/OmniRoute/pull/12169)) — thanks @HouMinXi +- **fix(translator):** strip plaintext reasoning content for opaque responses backends (#12128) ([#12171](https://github.com/diegosouzapw/OmniRoute/pull/12171)) — thanks @HouMinXi +- **perf(stream):** compile hot-path regexes once, bound token caches, fix quadratic buffering ([#12179](https://github.com/diegosouzapw/OmniRoute/pull/12179)) — thanks @opensource-elearning +- **fix:** resolve compression worker file using runtime anchors instead of… ([#12183](https://github.com/diegosouzapw/OmniRoute/pull/12183)) — thanks @NightStalker-87 +- **fix:** prevent Claude Code session kills via liveness-aware readiness + auto model echo ([#12189](https://github.com/diegosouzapw/OmniRoute/pull/12189)) — thanks @opensource-elearning +- **fix(translator):** preserve falsy primitive values in Gemini and Antigravity function response results ([#12191](https://github.com/diegosouzapw/OmniRoute/pull/12191)) — thanks @Siva010 +- **fix(oauth):** mark empty Antigravity projectId as degraded and clear stale errors (#11284) ([#12205](https://github.com/diegosouzapw/OmniRoute/pull/12205)) — thanks @HouMinXi +- **fix(providers):** gate the Codex auto-ping usage read on the shared quota throttle ([#12209](https://github.com/diegosouzapw/OmniRoute/pull/12209)) — thanks @abhisheksharma2411 +- **fix(combos):** prioritize SQLite row id over inner JSON id and notify delete errors ([#12213](https://github.com/diegosouzapw/OmniRoute/pull/12213)) — thanks @HouMinXi +- **fix(providers):** mark featherless.ai as no longer having a free tier (`hasFree: false`) ([#12216](https://github.com/diegosouzapw/OmniRoute/pull/12216)) — thanks @turbolego +- **fix(deepseek-web):** stop Turbopack dev panic in the PoW worker path resolver ([#12221](https://github.com/diegosouzapw/OmniRoute/pull/12221)) — thanks @hartmark +- **fix(chat-admission):** clarify local 503 source ([#12223](https://github.com/diegosouzapw/OmniRoute/pull/12223)) — thanks @keeltrace +- **fix(dev):** silence webpack runtime module warnings ([#12228](https://github.com/diegosouzapw/OmniRoute/pull/12228)) — thanks @backryun +- **fix(db):** use module.require for CommonJS runtime driver loading ([#12230](https://github.com/diegosouzapw/OmniRoute/pull/12230)) — thanks @raheemuddin786 +- **fix(memory):** honest probe-driven FTS5 keyword status + memory_id rowid sync ([#12231](https://github.com/diegosouzapw/OmniRoute/pull/12231)) — thanks @raheemuddin786 +- **fix(system):** propagate abort signal to stream reader in HTTP version checks ([#12232](https://github.com/diegosouzapw/OmniRoute/pull/12232)) — thanks @raheemuddin786 +- **fix(resilience):** isolate local host execution errors from provider circuit breakers ([#12233](https://github.com/diegosouzapw/OmniRoute/pull/12233)) — thanks @raheemuddin786 +- **fix(sse):** sort injected tools deterministically for prompt caching ([#12234](https://github.com/diegosouzapw/OmniRoute/pull/12234)) — thanks @raheemuddin786 +- **fix(combo):** always clear the loop-safety timer, not just on the happy path (#11804) ([#12245](https://github.com/diegosouzapw/OmniRoute/pull/12245)) +- **fix(quality):** base-red pair — stryker tap registration + turn-pin suites aligned to the window gate ([#12255](https://github.com/diegosouzapw/OmniRoute/pull/12255)) — thanks @backryun +- **fix(dev):** qualify Turbopack runtime boundaries ([#12258](https://github.com/diegosouzapw/OmniRoute/pull/12258)) — thanks @backryun +- **fix(combo):** fall back to full pool when collapsed sole survivor is context-too-small ([#12278](https://github.com/diegosouzapw/OmniRoute/pull/12278)) — thanks @opensource-elearning +- **fix(combo):** bound the pre-dispatch unavailable skip so a stale label cannot dark a pool (#12168) ([#12285](https://github.com/diegosouzapw/OmniRoute/pull/12285)) +- **fix(security):** strip Qwen/Alibaba console-session cookies from provider API responses ([#12287](https://github.com/diegosouzapw/OmniRoute/pull/12287)) +- **fix(usage):** console-aware Token Plan guidance + subscription hint on bailian 401 ([#12288](https://github.com/diegosouzapw/OmniRoute/pull/12288)) +- **fix(resourcePressure):** log numeric detail on every rejection, recover faster ([#12293](https://github.com/diegosouzapw/OmniRoute/pull/12293)) — thanks @hartmark +- **fix(release):** drain the 2026-09-01 base-red window — passthrough usage regression + radarPage i18n keys ([#12327](https://github.com/diegosouzapw/OmniRoute/pull/12327)) +- **fix(ci):** openapi-security-tiers checker must honor routeGuard patterns + imported prefixes ([#12350](https://github.com/diegosouzapw/OmniRoute/pull/12350)) — thanks @ggiak +- **perf(ci):** cache node_modules in the npm-ci-retry composite (#8084 D3) ([#12408](https://github.com/diegosouzapw/OmniRoute/pull/12408)) +- **revert(ui):** point CTAs back at their real destinations ([#12410](https://github.com/diegosouzapw/OmniRoute/pull/12410)) +- **fix(release):** drain the 2026-09-02 base-red — rerank-providers import + api-typecheck baseline ratchet ([#12414](https://github.com/diegosouzapw/OmniRoute/pull/12414)) +- **fix(types):** make system prompt injection noImplicitAny-safe ([#12416](https://github.com/diegosouzapw/OmniRoute/pull/12416)) +- **fix(quality):** validate typecheck baseline schema ([#12419](https://github.com/diegosouzapw/OmniRoute/pull/12419)) +- **fix(memory):** resolve rerank provider node cache import ([#12421](https://github.com/diegosouzapw/OmniRoute/pull/12421)) +- **fix(ci):** repair release-root regressions — pack dedup, web-session syntax, uc-image ids ([#12423](https://github.com/diegosouzapw/OmniRoute/pull/12423)) — thanks @backryun +- **fix(providers):** separate MaxAI and UC credential contracts ([#12431](https://github.com/diegosouzapw/OmniRoute/pull/12431)) +- **fix(cli):** preserve Claude settings on config set ([#12432](https://github.com/diegosouzapw/OmniRoute/pull/12432)) — thanks @KrzysiekSko +- **fix(providers):** repair the maxai credential block truncated by merge auto-resolve ([#12433](https://github.com/diegosouzapw/OmniRoute/pull/12433)) +- **fix(quality):** record the 2026-09-02 merged growth in the file-size baseline ([#12434](https://github.com/diegosouzapw/OmniRoute/pull/12434)) +- **fix(uc):** route UC error strings through sanitizeErrorMessage; allowlist the retired codex id ([#12437](https://github.com/diegosouzapw/OmniRoute/pull/12437)) +- **fix(adapta):** redact streamed upstream errors ([#12438](https://github.com/diegosouzapw/OmniRoute/pull/12438)) +- **fix(sse):** remap non-contiguous upstream tool_calls index to a gap-free output_index ([#12445](https://github.com/diegosouzapw/OmniRoute/pull/12445)) — thanks @hartmark +- **fix(quality):** detect duplicate tool_calls entries in one response ([#12446](https://github.com/diegosouzapw/OmniRoute/pull/12446)) — thanks @hartmark +- **fix(conversations):** resolve turn content OmniRoute never sends back to the client ([#12447](https://github.com/diegosouzapw/OmniRoute/pull/12447)) — thanks @hartmark +- **fix(combo):** do not treat credits-exhausted 401 as auth skip ([#12449](https://github.com/diegosouzapw/OmniRoute/pull/12449)) — thanks @RaviTharuma +- **fix(auth):** do not park healthy quota accounts as expired ([#12452](https://github.com/diegosouzapw/OmniRoute/pull/12452)) — thanks @RaviTharuma +- **fix(responses-continuation):** fail closed on a collector-truncated, empty output array ([#12460](https://github.com/diegosouzapw/OmniRoute/pull/12460)) — thanks @hartmark +- **fix(logging):** raise the SSE payload collector's default cap ([#12461](https://github.com/diegosouzapw/OmniRoute/pull/12461)) — thanks @hartmark +- **fix(resilience):** surface Responses failed.error.message in 502s ([#12472](https://github.com/diegosouzapw/OmniRoute/pull/12472)) — thanks @RaviTharuma +- **fix(security):** unbiased maxai X-Random nonce + stricter URL/regex assertions ([#12502](https://github.com/diegosouzapw/OmniRoute/pull/12502)) +- **fix(video):** re-anchor transcript log-redaction so PII/credential maskers can't reopen the leak (#12150 P1 follow-up) ([#12503](https://github.com/diegosouzapw/OmniRoute/pull/12503)) +- **fix(monitoring):** serve cached credentialHealth off the request path ([#12533](https://github.com/diegosouzapw/OmniRoute/pull/12533)) — thanks @RaviTharuma +- **fix(quota):** keep Antigravity Gemini usable when Claude weekly is empty ([#12566](https://github.com/diegosouzapw/OmniRoute/pull/12566)) — thanks @HouMinXi +- **fix(authz):** hard-gate every credential export and CLI-config write (GHSA-5926-2w35-7h4q) ([#12600](https://github.com/diegosouzapw/OmniRoute/pull/12600)) +- **fix(docs):** restore the Next build — REMOVED_PROVIDERS.md had no frontmatter (base-red #12581) ([#12610](https://github.com/diegosouzapw/OmniRoute/pull/12610)) +- **fix(quality):** re-point the zcodeProtocol public-creds allowlist to line 313 ([#12615](https://github.com/diegosouzapw/OmniRoute/pull/12615)) +- **fix(security):** close 3 advisories — search baseUrl exfil, sk- in the error sanitizer, bifrost relay header leak ([#12620](https://github.com/diegosouzapw/OmniRoute/pull/12620)) +- **fix(providers):** sanitize boolean required and nested bare maps for Gemini (#12269) ([#12624](https://github.com/diegosouzapw/OmniRoute/pull/12624)) — thanks @HouMinXi +- **fix(resilience):** keep Overloaded STREAM_EARLY_EOF off the provider breaker ([#12626](https://github.com/diegosouzapw/OmniRoute/pull/12626)) — thanks @HouMinXi +- **fix(catalog):** re-audit free-tier quotas against official pages ([#12649](https://github.com/diegosouzapw/OmniRoute/pull/12649)) +- **fix(dashboard):** keep a request's pending-tracking id stable across combo target retries ([#12650](https://github.com/diegosouzapw/OmniRoute/pull/12650)) — thanks @hartmark +- **fix(tests):** retire dead model ids from the chat-pipeline integration suite (base-red #12581) ([#12670](https://github.com/diegosouzapw/OmniRoute/pull/12670)) +- **fix:** resolve SqliteError no such table compression_run_telemetry during cleanup ([#12682](https://github.com/diegosouzapw/OmniRoute/pull/12682)) — thanks @groovecityJO +- **fix(combo):** treat a pin-only step as implicit connection allowlist ([#12697](https://github.com/diegosouzapw/OmniRoute/pull/12697)) — thanks @HouMinXi +- **fix(ci):** ship .npmrc in published package so legacy-peer-deps applies to consumers (#11544) ([#12699](https://github.com/diegosouzapw/OmniRoute/pull/12699)) — thanks @KooshaPari +- **fix(docker):** pin 4 CLI tools to exact versions (#12576) ([#12703](https://github.com/diegosouzapw/OmniRoute/pull/12703)) — thanks @KooshaPari +- **fix(docker):** document and harden cli profile trust boundary (#12570) ([#12706](https://github.com/diegosouzapw/OmniRoute/pull/12706)) — thanks @KooshaPari +- **fix(sse):** redact video transcript in pre-guardrail rejected-request logs (#12150 P2 item 7) ([#12710](https://github.com/diegosouzapw/OmniRoute/pull/12710)) +- **fix(i18n):** quote placeholder in OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES description (#12505) ([#12769](https://github.com/diegosouzapw/OmniRoute/pull/12769)) — thanks @KooshaPari +- **fix(api/pricing):** surface validation error message as string, not raw object (#12494) ([#12771](https://github.com/diegosouzapw/OmniRoute/pull/12771)) — thanks @KooshaPari +- **fix(sse):** re-enable prompt compression for native Codex passthrough ([#12834](https://github.com/diegosouzapw/OmniRoute/pull/12834)) — thanks @thomasmaerz +- **fix(dashboard):** model health tests for a provider node set to the Responses API now call `/v1/responses` with a Responses-shaped body instead of `/v1/chat/completions` — those models were reported as `Provider returned HTTP 200 but no text content` even though the same model answered normally through `/v1/responses` ([#13070](https://github.com/diegosouzapw/OmniRoute/issues/13070)) ([#13087](https://github.com/diegosouzapw/OmniRoute/pull/13087)) — thanks @ntdatt812 +- fix(routing): stop a round-robin combo's "opencode" targets from collapsing onto the opencode-zen connection (#11912) ([#13283](https://github.com/diegosouzapw/OmniRoute/pull/13283)) +- fix(routing): stop the reactive-compaction debug log from lying when compression is globally disabled (#11977) ([#13276](https://github.com/diegosouzapw/OmniRoute/pull/13276)) +- fix(electron): relativize standalone-bundle symlink targets so Stage 8 manifest verification stops failing on Windows (#11979) ([#13251](https://github.com/diegosouzapw/OmniRoute/pull/13251)) +- fix(dashboard): surface a visible error when Compression Studio's combined preview run fails (#12061) ([#13288](https://github.com/diegosouzapw/OmniRoute/pull/13288)) +- fix(dashboard): make the compression "Effective pipeline" preview honor the active profile and warn when the master switch is off (#12063) ([#13287](https://github.com/diegosouzapw/OmniRoute/pull/13287)) +- fix(providers): scope TinyCMS Web signer's DOM shims to each call instead of leaking them for the process lifetime, and surface a clean HTTP status on a non-JSON interception-toggles error (#12072) ([#13254](https://github.com/diegosouzapw/OmniRoute/pull/13254)) +- fix(guardrails): stop Vision Bridge from re-selecting a model locked after a 404 (#12111) ([#13259](https://github.com/diegosouzapw/OmniRoute/pull/13259)) +- fix(sse): require Responses-shaped body before native OpenAI-compatible passthrough (#12129) ([#13278](https://github.com/diegosouzapw/OmniRoute/pull/13278)) +- fix(providers): minimax-m3 now collapses manual thinking.type:"enabled" to adaptive, preventing upstream 400 (2013) (#12132) ([#13249](https://github.com/diegosouzapw/OmniRoute/pull/13249)) +- fix(db): scope model visibility overrides by modality so hiding a Chat model no longer hides an identically-ID'd Image/Embeddings/etc. model (#12172) ([#13258](https://github.com/diegosouzapw/OmniRoute/pull/13258)) +- fix(db): scope local-provider apiKey dedup to matching base URL so LM Studio/Ollama-style connections support multiple accounts (#12173) ([#13250](https://github.com/diegosouzapw/OmniRoute/pull/13250)) +- fix(providers): refresh Trae's stale Referer/Origin and forward user timezone so imported connections stop failing with 401 (#12190) ([#13255](https://github.com/diegosouzapw/OmniRoute/pull/13255)) +- fix(providers): route opencode-go/gpt-5.6-luna to /responses instead of /chat/completions (#12196) ([#13275](https://github.com/diegosouzapw/OmniRoute/pull/13275)) +- fix(dashboard): allow deleting the last extra-upstream-header row even when invalid (#12251) ([#13252](https://github.com/diegosouzapw/OmniRoute/pull/13252)) +- **fix(i18n):** translate pre-existing `__MISSING__:` keys for `combo.sort`, `requestLogger.detail` expand/collapse, `common.profile`, and `settings.resilienceCredentialHealth*` across 39 locales ([#12272](https://github.com/diegosouzapw/OmniRoute/issues/12272)) ([#13034](https://github.com/diegosouzapw/OmniRoute/pull/13034)) — thanks @HouMinXi +- fix(cli): run the Node.js runtime compatibility guard before the heavy `tsx/esm` + Commander import chain so an unsupported runtime gets a clear message instead of a raw `Invalid regular expression flags` crash (#12296) ([#13253](https://github.com/diegosouzapw/OmniRoute/pull/13253)) +- fix(dashboard): refresh the providers list after deleting a compatible provider node (#12298) ([#13256](https://github.com/diegosouzapw/OmniRoute/pull/13256)) +- fix(usage): fail closed on API-key budget enforcement when a provider's `auto` routing alias has no pricing row, instead of silently counting it as $0 (#12341) ([#13257](https://github.com/diegosouzapw/OmniRoute/pull/13257)) +- **fix(providers):** include the submitted Agnes video model when polling by `video_id` ([#12356](https://github.com/diegosouzapw/OmniRoute/pull/12356)) — thanks @pacocartones +- **fix(routing):** custom OpenAI-compatible nodes now honor the saved Chat/Responses API type after edits instead of letting the node's original ID prefix override the live connection setting ([#11884](https://github.com/diegosouzapw/OmniRoute/issues/11884)). ([#12358](https://github.com/diegosouzapw/OmniRoute/pull/12358)) — thanks @pacocartones +- fix(sse): surface an error instead of a silent empty 200 when a Claude stream closes with zero bytes (#12398) ([#13285](https://github.com/diegosouzapw/OmniRoute/pull/13285)) +- fix(oauth): warn before the dead localhost:8080 redirect in antigravity/gemini `oauth start` (#12413) ([#13265](https://github.com/diegosouzapw/OmniRoute/pull/13265)) +- fix(providers): stop devin-cli spawn error from double-closing the SSE controller (#12517) ([#13261](https://github.com/diegosouzapw/OmniRoute/pull/13261)) +- **fix(rerank):** clamp Voyage `top_k` to the documents actually sent after empty-string filtering, and honor `return_documents: false` in the NVIDIA response adapter (#12523 — thanks @pacocartones) +- **fix(audio):** `/v1/audio/translations` now resolves combo names the way `/v1/audio/transcriptions` already does, so a combo that `GET /v1/models` advertises is fanned out to its targets instead of being rejected with `400 Invalid translation model: . Use format: provider/model`; literal `provider/model` ids and unknown bare names behave as before (#12536 — thanks @pacocartones) +- **fix(gemini):** strip the JSON-Schema-2020-12 `prefixItems` keyword from Gemini tool schemas at every nesting level, so Claude Code tool definitions no longer fail with `400 Unknown name "prefixItems"` on Gemini models (#12540 — thanks @pacocartones) +- **fix(api-manager):** Expose an accessible loading status while API keys are fetched instead of an empty accessibility tree (#12541 — thanks @pacocartones) +- **fix(video):** Clamp `estimateJpegFrameBytes` at zero for padding-only payloads and build the three encode-side frame data URIs from `JPEG_FRAME_DATA_URI_PREFIX` instead of a repeated literal (#12543 — thanks @pacocartones) +- **fix(devin):** accept Windows `DEVIN_AGENTIC_HOME` sandbox paths (`C:\...\.sandbox\...`) in the isolated-home check so the Devin Claude Bridge no longer fails closed on Windows ([#12405](https://github.com/diegosouzapw/OmniRoute/issues/12405)) (#12545 — thanks @pacocartones) +- **fix(db):** Add `conversation_turn_nodes` and orphaned `agentic_conversations` to the auto-cleanup cycle under the existing `retention.callLogs` window, so identity nodes whose call-log content has already been purged no longer accumulate without bound in `storage.sqlite` (#12548 — thanks @pacocartones) +- **fix(i18n):** Wrap the `~/.claude/profiles//settings.json` placeholder in ICU single quotes in the `OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES` feature-flag description across all 42 locales and the TypeScript default, so next-intl no longer fails with `INVALID_MESSAGE: UNCLOSED_TAG` and the Feature Flags card shows the description instead of the raw key (#12549 — thanks @pacocartones) +- **fix(orchestration):** `updateCloudAgentTask` now publishes the task's real `status` on `agent.task.updated` when an update only touches `result`, `activities` or `error`, instead of the fabricated `"updated"` state, and stays silent when no row matched the id (#12550 — thanks @pacocartones) +- **fix(i18n):** the home "Recent Requests" panel and the Provider Topology legend are now translated instead of rendering English copies on non-English dashboards; the legend reads its own `home.topologyLegend*` labels with consistent casing rather than borrowing the memory-settings "Recent" and analytics "Error" strings (#12551 — thanks @pacocartones). +- **docs(reference):** bring the `FEATURE_FLAGS.md` catalog back to 1:1 with `featureFlagDefinitions.ts` — 20 missing flags added, the two `*_BLOCK_THRESHOLD` env-only knobs moved out of the flag tables, category/total counts and the Live WS port corrected, guarded by a static test (#12552 — thanks @pacocartones) +- **fix(i18n):** backfill missing `usage.kiloPass*` strings in 39 locales and restore `featureFlagOmnirouteDisableThinkingLevelVariantsDescription` in `pt.json` ([#12561](https://github.com/diegosouzapw/OmniRoute/issues/12561)) ([#13027](https://github.com/diegosouzapw/OmniRoute/pull/13027)) — thanks @HouMinXi +- fix(docker): default docker-compose app ports (dashboard/API/live-WS) to loopback instead of `0.0.0.0`, closing the anonymous `/v1` LAN/WAN exposure gap left open by `REQUIRE_API_KEY=false` (#12568) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): close DNS-rebinding SSRF gap in webhook outbound-URL guard (#12569) ([#13243](https://github.com/diegosouzapw/OmniRoute/pull/13243)) +- fix(docker): require a per-session token on the VNC browser CDP bridge and isolate it on a dedicated Docker network (#12571) ([#13241](https://github.com/diegosouzapw/OmniRoute/pull/13241)) +- fix(open-sse): write Adobe Firefly session tokens and cookie jars with 0700/0600 permissions instead of the process umask (#12572) ([#13242](https://github.com/diegosouzapw/OmniRoute/pull/13242)) +- fix(api): remove hardcoded wildcard CORS in openai-to-gemini-sse.ts so the centralized fail-closed CORS gate is the sole source of `Access-Control-Allow-Origin` (#12573) ([#13239](https://github.com/diegosouzapw/OmniRoute/pull/13239)) +- fix(api): enforce API key policy (budget/rate-limit/schedule/endpoint scoping) on the ElevenLabs speech-to-text, text-to-speech and voices proxy routes (#12574) ([#13244](https://github.com/diegosouzapw/OmniRoute/pull/13244)) +- fix(sse): cap HuggingChat NDJSON body size and bound the read loop with the fetch timeout so a stalled or hostile upstream cannot buffer unbounded memory (#12577) ([#13282](https://github.com/diegosouzapw/OmniRoute/pull/13282)) +- fix(docker): scope the cliproxyapi/qdrant/bifrost sidecars to loopback by default and forward `CLIPROXYAPI_MANAGEMENT_KEY` into the cliproxyapi container so its management API is not left both unauthenticated and LAN-published (#12578) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): create DB export temp paths with `fs.mkdtempSync` instead of predictable timestamps (#12579) ([#13237](https://github.com/diegosouzapw/OmniRoute/pull/13237)) +- Cline 401 bodies that say "re-authenticate your Cline account" classify as a refreshable OAuth token, not a terminal expired key. The cooling panel no longer labels every cooldown as a 429; it shows the recorded last error instead. (#12594) ([#13060](https://github.com/diegosouzapw/OmniRoute/pull/13060)) — thanks @HouMinXi +- **fix(catalog):** degrade unknown combo targets instead of dropping LCD modalities, and persist OpenRouter `architecture.input_modalities` into the capability snapshot ([#12613](https://github.com/diegosouzapw/OmniRoute/issues/12613)) ([#13042](https://github.com/diegosouzapw/OmniRoute/pull/13042)) — thanks @HouMinXi +- fix(providers): send `x-api-key` instead of `Authorization: Bearer` for OpenCode Zen's `/v1/responses` endpoint (Muse Spark Contributor models), fixing a 401 on OmniRoute's auth header (#12633) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(sse): surface the actionable "Auggie CLI not found" message when the shell reports a missing `auggie` binary via exit code instead of a spawn error (#12645) ([#13263](https://github.com/diegosouzapw/OmniRoute/pull/13263)) +- **fix(config):** Persist deletions of built-in background-degradation entries — when a stored settings record exists its `degradationMap` is now authoritative instead of being merged under the defaults, so an entry the user removed in the dashboard no longer reappears on the next apply or restart ([#12424](https://github.com/diegosouzapw/OmniRoute/issues/12424)) ([#12647](https://github.com/diegosouzapw/OmniRoute/pull/12647)) — thanks @pacocartones +- **fix(gamification):** action-count badge milestones (First Token, Token Consumer, Token Machine, Token Whale, and the token-sharing tier) are now backed by a durable `xp_action_counts` counter incremented in `addXp()`, instead of a live `COUNT(*)` over `xp_audit_log`. The audit log is pruned by `retention.xpAuditLog` (default 30 days), so on a default install those "lifetime" milestones were really "actions in the last 30 days" and unlocked badges could stop unlocking once old rows aged out. `getActionCount()` and `checkActionCountBadges()` now read the same durable source, and a migration backfills existing totals from the surviving audit rows ([#12546](https://github.com/diegosouzapw/OmniRoute/issues/12546)) ([#12651](https://github.com/diegosouzapw/OmniRoute/pull/12651)) — thanks @pacocartones +- **fix(images):** `/v1/images/edits` now iterates a combo's targets the same way `/v1/images/generations` does (#9239) instead of flattening a bare combo to its first target. A combo whose first target is not edit-capable — or lacks credentials — now falls through to a later edit-capable target rather than hard-erroring, and missing credentials are skipped (not a hard `401`) to match the generations path. The per-target skip/terminal classification is extracted into a shared `runImageComboTargets` loop, so generations behavior is unchanged ([#12547](https://github.com/diegosouzapw/OmniRoute/issues/12547)). ([#12653](https://github.com/diegosouzapw/OmniRoute/pull/12653)) — thanks @pacocartones +- fix(sse): add first-byte watchdog to the TLS-fingerprint transport so a stalled wreq body falls back instead of hanging for minutes (#12656) ([#13272](https://github.com/diegosouzapw/OmniRoute/pull/13272)) +- fix(sse): exempt tiny-budget reasoning probes from combo quality failure and surface persisted-cooldown skips in ALL_TARGETS_SKIPPED diagnostics (#12659) ([#13269](https://github.com/diegosouzapw/OmniRoute/pull/13269)) +- fix(models): declare the real ~1M contextLength for OpenCode Zen's Muse Spark 1.2 models instead of falling back to the 200000 provider default (#12681) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(oauth): align codebuddy-cn OAuth User-Agent with the chat/usage CLI version to avoid WAF false positives (#12702) ([#13264](https://github.com/diegosouzapw/OmniRoute/pull/13264)) +- fix(dashboard): surface an authentication-required banner instead of silently blanking database settings for a guest session (#12709) ([#13270](https://github.com/diegosouzapw/OmniRoute/pull/13270)) +- **fix(resilience):** stop unbounded queue that hangs 6min until Aborted — gate, provider slot, and Bottleneck queue now share a per-connection `maxWaitMs` budget; `fail-closed` on exhaust (503); execution backstop `executionMaxWaitMs` overridable per connection with upstream clamp ([#12715](https://github.com/diegosouzapw/OmniRoute/pull/12715)) — thanks @maxmad64bis (with thanks to @Tushar49 for surfacing the slow-provider need in #12635) +- **fix(auto-combo):** every mode pack now carries `quality` and `reliability` weights (`quality` 0.02, 0.03 in `quality-first`; `reliability` 0.03, 0.04 in `reliability-first`), so selecting a pack no longer silences either signal; `DEFAULT_WEIGHTS` is unchanged ([#12731](https://github.com/diegosouzapw/OmniRoute/pull/12731)) — thanks @maxmad64bis +- **fix(sse):** the provider execution pipeline keeps the upstream error `code`/`type` again — both were lost when the non-streaming leg was extracted, so a config-class `422` (Antigravity missing project) degraded into a generic account cooldown instead of reaching the gate that recognises it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(db):** a legacy `call_logs` table no longer aborts startup — the provider-stats index is created after the column healing runs, not before it, so an install predating `request_type` opens instead of failing with `no such column` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(catalog):** a malformed row in the operator-writable custom-models blob no longer kills every `auto/*` pool ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(tests):** realign the guards that the same day's merges left asserting the old behavior — the provider-count total after GreenPT and EURouter, the GLM stream buffer slot that `#12925` turned into a declared parameter, and the injection scan bound that `#13104` reshaped from a head-only window into head-plus-tail ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- fix(cache): fold tool_choice/tools/response_format into the semantic cache signature so a cached tool_calls response can no longer be replayed for a request whose tool policy forbids it (#12734) ([#13267](https://github.com/diegosouzapw/OmniRoute/pull/13267)) +- **fix(models):** stop treating provider-supplied `isFree:true`/`:free`/`0/0` flags as trusted unless the provider has a documented free tier — only the shipped free-tier catalog decides otherwise; fetched `isFree:true` trusted only for a free-tier provider, custom `isFree:true` via a trusted path ([#12744](https://github.com/diegosouzapw/OmniRoute/pull/12744)) — thanks @maxmad64bis +- fix(memory): authenticate the internal /v1/rerank loopback call so memory reranking no longer silently degrades to unranked order when REQUIRE_API_KEY=true (#12745) ([#13268](https://github.com/diegosouzapw/OmniRoute/pull/13268)) +- fix(sse): parse Ollama Cloud's current usage markup (`$X of $Y used` aria-label, nested width style) (#12749) ([#13260](https://github.com/diegosouzapw/OmniRoute/pull/13260)) +- fix(cli): setup-opencode no longer sends an active context's management token to `/v1/models` when `--api-key`/`OMNIROUTE_API_KEY` is supplied — an explicit flag or the env var now always outranks the context's token, and the flag itself is no longer swallowed by the parent program's global `--api-key` option (#12783) ([#13246](https://github.com/diegosouzapw/OmniRoute/pull/13246)) +- fix(sse): register Arcee AI in the executor provider registry so requests reach api.arcee.ai instead of silently falling back to OpenAI (#12784) ([#13277](https://github.com/diegosouzapw/OmniRoute/pull/13277)) +- **fix(quota):** share one upstream quota read when identical saturation checks arrive at the same time, so traffic bursts don't multiply provider API calls ([#12787](https://github.com/diegosouzapw/OmniRoute/pull/12787)) — thanks @maxmad64bis +- **fix(db):** ignore expired or invalid rate-limit cooldown writes so a stale timestamp can't lock a connection that should be usable — clearing still works as before ([#12788](https://github.com/diegosouzapw/OmniRoute/pull/12788)) — thanks @maxmad64bis +- **fix(providers):** single source for provider order with `xao` ranking alongside `xai-oauth` ([#12790](https://github.com/diegosouzapw/OmniRoute/pull/12790)) — thanks @maxmad64bis +- **fix(routing):** off-table models check the free-model catalog before inheriting premium prices, latency bootstraps from the observed pool median, async tiers read live database pricing with a 90-day freshness gate, and the tier cache invalidates on every pricing write ([#12792](https://github.com/diegosouzapw/OmniRoute/pull/12792)) — thanks @maxmad64bis +- **fix(auto-combo):** snapshot scoring uses the observed per-connection account tier and per-model quality instead of neutral constants, and circuit-open providers rank lower at build time ([#12794](https://github.com/diegosouzapw/OmniRoute/pull/12794)) — thanks @maxmad64bis +- **fix(pool):** empty `auto/*` pools now say why they are empty, and the models listing uses the same paid check as routing ([#12795](https://github.com/diegosouzapw/OmniRoute/pull/12795)) — thanks @maxmad64bis +- fix(routing): recognize CLIProxyAPI's 'unknown provider for model' 400 as fallback-worthy (#12800) ([#13284](https://github.com/diegosouzapw/OmniRoute/pull/13284)) +- **fix(sse):** translate-mode streams now emit the estimated token counts as a trailing usage-only chunk before `[DONE]` when the upstream stays silent, so metered chat clients see totals instead of nothing ([#12828](https://github.com/diegosouzapw/OmniRoute/pull/12828)) — thanks @maxmad64bis +- **fix(db):** provider stats stay truthful on empty and legacy databases: fallback counts default to `0` instead of `null`, latency averages read `null` (not `0`) when no durations were recorded, failures logged before the error-type column existed group under `pre_migration` instead of `unclassified`, and per-provider queries use two new composite indexes ([#12832](https://github.com/diegosouzapw/OmniRoute/pull/12832)) — thanks @maxmad64bis +- fix(nvidia): fail open when a synced model catalog goes stale instead of gating forever (#12849) ([#13248](https://github.com/diegosouzapw/OmniRoute/pull/13248)) +- **fix(db):** WAL maintenance lives in its own module: a `TRUNCATE` checkpoint that hits a busy database now warns with its streak and retries once via `PASSIVE` instead of logging success, and closing-time checkpoints no longer report success on builds without a database file, with the busy totals visible in the authenticated monitoring health payload ([#12853](https://github.com/diegosouzapw/OmniRoute/pull/12853)) — thanks @maxmad64bis +- **fix(health):** quota with no snapshots reads empty instead of a contradicting 0% ([#12857](https://github.com/diegosouzapw/OmniRoute/pull/12857)) — thanks @maxmad64bis +- fix(a2a): accept the dashboard's own session cookie on /a2a so "Run message/send" no longer fails with "Unauthorized: missing or invalid API key" (#12888) ([#13271](https://github.com/diegosouzapw/OmniRoute/pull/13271)) +- **fix(logs):** the Logs grid's in-memory filter pass no longer discards rows the SQL query already matched — selecting an API key from the dropdown (which sends the key's id) returns its calls again, the Combo tab shows every combo instead of only those whose name contains a "1", and the model filter and search cover the same columns as the query ([#12896](https://github.com/diegosouzapw/OmniRoute/pull/12896)) — fixes [#12873](https://github.com/diegosouzapw/OmniRoute/issues/12873) — thanks @ntdatt812 +- **fix(a2a):** `/api/a2a/status` now builds the agent card from the request that asked for it, so a gateway reached at a non-localhost host no longer advertises `http://localhost:20128` as its A2A URL ([#12918](https://github.com/diegosouzapw/OmniRoute/pull/12918)). — thanks @ntdatt812 +- **fix(compression):** progressive aging now appends its `[COMPRESSED:aging:…]` annotation after a turn's `tool_result` blocks instead of in front of them, so Anthropic no longer rejects aged conversations with "`tool_use` ids were found without `tool_result` blocks immediately after" ([#12920](https://github.com/diegosouzapw/OmniRoute/pull/12920)). — thanks @ntdatt812 +- **fix(bedrock):** model import now resolves context limits for every vendor prefix instead of only `anthropic.*`, so `global.openai.gpt-5.6-*` no longer imports with a null `inputTokenLimit` and gets rejected pre-flight at the 200k default ([#12921](https://github.com/diegosouzapw/OmniRoute/pull/12921)). — thanks @ntdatt812 +- **fix(stream):** the 64 KB stream buffer GLM asks for is honoured instead of dropped, and the type error it caused no longer fails the API Route Typecheck gate on every open PR ([#12925](https://github.com/diegosouzapw/OmniRoute/pull/12925)) — thanks @ntdatt812 +- **fix(guardrails):** mask PII inside a `tool_result`'s nested content array, which the masker walked past while redacting its sibling block ([#12930](https://github.com/diegosouzapw/OmniRoute/pull/12930)) — thanks @ntdatt812 +- **fix(dashboard):** the Radar catalog table no longer leaves absent data unexplained — empty limits, unknown context windows, and unreported capabilities each explain themselves on hover, and a new check keeps it that way ([#12937](https://github.com/diegosouzapw/OmniRoute/pull/12937)) — thanks @maxmad64bis +- **fix(sse):** geo-blocked opencode requests rotate to the next account proxy instead of failing, so one refused egress no longer aborts the whole chain ([#12941](https://github.com/diegosouzapw/OmniRoute/pull/12941)) — thanks @maxmad64bis +- **fix(codex):** dashboard "clear cooldown" and the CAS recovery path now drop nested `codexScopeRateLimitedUntil` maps in the same write that nulls `rate_limited_until`, and fresh quota snapshots with headroom lift fallback-sourced scope cooldowns parked by quota preflight ([#12817](https://github.com/diegosouzapw/OmniRoute/issues/12817), [#12860](https://github.com/diegosouzapw/OmniRoute/issues/12860), [#12951](https://github.com/diegosouzapw/OmniRoute/pull/12951)) The dashboard reset-credit button (`consumeCodexResetCredit`) now exercises that same snapshot path after a successful redeem, so a filled quota bar is enough to unpark a leftover fallback Codex child without another manual clear. — thanks @HouMinXi +- **fix(db):** resolve `sql-wasm.wasm` across global npm install and hoisted layouts, ensuring OmniRoute can boot cleanly on Node 24 when native `better-sqlite3` is uncompiled. ([#13035](https://github.com/diegosouzapw/OmniRoute/pull/13035)) — thanks @HouMinXi +- **fix(api):** `DELETE /v1/batches/delete-completed` now sweeps only the calling API key's own completed batches (batches with no owner stay out of a key-scoped sweep on purpose), with an explicit instance-wide mode reserved for authenticated dashboard sessions, a 401 for a presented key that is unknown, revoked, deactivated, banned or expired (never falling through to the session branch), audit logging of every sweep, a sanitized 500 on failure, an owner-scoped file half (a key-scoped sweep never nulls a file another tenant owns) and an atomic sweep — chunked in 200-batch transactions in instance mode — so a mid-way error never leaves a batch pointing at a nulled file (GHSA-wvxc-jp3v-5mg5) ([#12969](https://github.com/diegosouzapw/OmniRoute/pull/12969)) +- **fix(combo):** quota-weighted routing stops drawing on an out-of-credit connection — a 402 now invalidates the stored quota snapshot instead of leaving its stale remaining percentage in place, and a snapshot older than 10 minutes no longer counts as confident headroom for the primary pool ([#12972](https://github.com/diegosouzapw/OmniRoute/pull/12972)) ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- **fix(vision):** Custom Models with "Vision capable" checked no longer have image requests swapped to `glm/glm-4.6v` when the client sends the advertised alias (`vllm/path/...`) or the bare path-shaped id — Vision Bridge now matches the stored override for all three id forms ([#12758](https://github.com/diegosouzapw/OmniRoute/issues/12758)) ([#12974](https://github.com/diegosouzapw/OmniRoute/pull/12974)) — thanks @HouMinXi +- **fix(sse):** transient opencode upstream failures rotate to the next account proxy instead of failing, so one flapping egress no longer aborts the whole chain ([#12975](https://github.com/diegosouzapw/OmniRoute/pull/12975)) — thanks @maxmad64bis +- **fix(azure):** Deployments from GPT-6 onward now send `max_completion_tokens` instead of `max_tokens`, which Azure rejects with HTTP 400. The rule matched a literal `gpt-5`, so each new generation arrived broken; it now matches the generation range, while `gpt-35-turbo` still keeps `max_tokens`. ([#13007](https://github.com/diegosouzapw/OmniRoute/pull/13007)) — thanks @ntdatt812 +- **fix(db):** add an opt-in self-restart circuit for sustained critical memory pressure, gate post-cleanup VACUUM behind a minimum freed-rows threshold, and checkpoint the SQLite WAL every 5 minutes with a size guard that escalates to TRUNCATE, so a growing WAL can no longer stall the event loop into a full outage. ([#13011](https://github.com/diegosouzapw/OmniRoute/pull/13011)) — thanks @HouMinXi +- **fix(auth):** an explicit connection pin may probe a quota-disabled row once and re-enable it on success ([#12874](https://github.com/diegosouzapw/OmniRoute/issues/12874)) ([#13017](https://github.com/diegosouzapw/OmniRoute/pull/13017)) — thanks @HouMinXi +- **fix(api):** thread `X-OmniRoute-Fallback-Attempts` through combo chat completions so streaming and non-streaming responses report how many prior legs were attempted ([#13038](https://github.com/diegosouzapw/OmniRoute/pull/13038)) — thanks @HouMinXi +- Restore provider failure classification and credential refresh on non-streaming requests: classify non-2xx failures to lock models on per-model quota exhaustion, update connection rate limits from headers and body, and pass credential refresh handlers to pipeline execution so 401 tokens can be refreshed and retried (#13043). ([#13069](https://github.com/diegosouzapw/OmniRoute/pull/13069)) — thanks @HouMinXi +- **fix(responses):** wrap forced-non-streaming web_search fallback JSON as Responses SSE so Codex still sees `response.completed` ([#13050](https://github.com/diegosouzapw/OmniRoute/pull/13050)) — thanks @HouMinXi +- **fix(antigravity):** Preserve upstream thought-token usage in normalized completion and reasoning token counts ([#13055](https://github.com/diegosouzapw/OmniRoute/pull/13055)) — thanks @pacocartones +- Fix a concurrency-slot leak in the DeepSeek PoW solver: a worker that failed to spawn (for example a missing worker script) never released its slot, so `MAX_CONCURRENT_WORKERS` failures disabled the solver until restart. ([#13097](https://github.com/diegosouzapw/OmniRoute/pull/13097)) — thanks @anhtahaylove +- **fix(acp):** bound the ACP session output buffers — `stdoutBuffer` and `stderrBuffer` now cap at 1 MiB keeping the most recent output behind a visible `[...output truncated...]` marker, and `stderrBuffer` is reset per prompt instead of accumulating for the lifetime of the session. ([#13100](https://github.com/diegosouzapw/OmniRoute/pull/13100)) — thanks @anhtahaylove +- **fix(acp):** release the `stdout`/`exit` listeners and the idle timer that a `sendPrompt` timeout used to leave attached to the `acpManager` singleton, and drop sessions that exited on their own from the session map instead of keeping them forever. ([#13096](https://github.com/diegosouzapw/OmniRoute/pull/13096)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection and PII scanners now read the text a `tool_result` block carries on `content` (string or nested block list), in messages and in system blocks, so tool output is judged by the same rules as user text ([#13101](https://github.com/diegosouzapw/OmniRoute/pull/13101)) — thanks @ntdatt812 +- **fix(gamification):** close the badge notification SSE stream when the request signal is already aborted before the stream starts — a client that disconnects while the route is still awaiting auth used to leave both the 2s unlock poll and the 15s heartbeat running for the lifetime of the process. ([#13106](https://github.com/diegosouzapw/OmniRoute/pull/13106)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection scan now spends its 16 KB budget on both ends of the request instead of the first 16 KB only, so `system`, `instructions`, `query`, `documents` and the newest turns are no longer hidden behind one long message ([#13104](https://github.com/diegosouzapw/OmniRoute/pull/13104)) — thanks @ntdatt812 +- **fix(dashboard):** expose the Volcano Ark console cookie on quota scraping and unwrap connect-error objects so the dashboard shows the upstream message ([#13107](https://github.com/diegosouzapw/OmniRoute/pull/13107)) — thanks @HouMinXi +- **fix(db):** release the `beforeExit`/`SIGINT`/`SIGTERM` handlers when a `node:sqlite` adapter closes, so a closed adapter and its database handle are no longer pinned to `process` for the lifetime of the run — the same treatment #7494 gave the sql.js adapter. ([#13109](https://github.com/diegosouzapw/OmniRoute/pull/13109)) — thanks @anhtahaylove +- **fix(translator):** `contentSchema` and `unevaluatedItems` are now treated as subschema positions by the tool-schema sanitizer, so a truncation placeholder in either is replaced with a permissive schema instead of being forwarded as a string ([#13110](https://github.com/diegosouzapw/OmniRoute/pull/13110)) — thanks @ntdatt812 +- **fix(cli-helper):** clear the `createLogStream` timeout on the abort path — `stop()` aborts the in-flight fetch and returned through the `signal.aborted` branch, which skipped `clearTimeout` and left an armed timer per stopped stream. The stream reader is now also cancelled when the read loop exits early. ([#13114](https://github.com/diegosouzapw/OmniRoute/pull/13114)) — thanks @anhtahaylove +- **fix(docs):** drop TLS-impersonation, MITM-decrypt, supply-chain attestation, and XOR-mask writeups from the public `/docs` catalog and Docker image. Files stay in git for engineers; operators who need them open the repo, not the website. ([#13136](https://github.com/diegosouzapw/OmniRoute/pull/13136)) — thanks @HouMinXi +- **fix(combo):** parse numeric-epoch `rate_limited_until` in the combo cooldown read path ([#13141](https://github.com/diegosouzapw/OmniRoute/pull/13141)) — thanks @maxmad64bis +- **fix(opencode):** both OpenCode plugins now reject a gateway address typed without `http://` at configuration time, instead of publishing every model with an api url no client can call, and the v2 plugin no longer publishes a model card whose api url is blank or relative ([#13142](https://github.com/diegosouzapw/OmniRoute/pull/13142)) — thanks @maxmad64bis +- **fix(providers):** lock opencode model on upstream 400 model-unavailable ([#13146](https://github.com/diegosouzapw/OmniRoute/pull/13146)) — thanks @maxmad64bis +- **fix(logging):** keep the provider exchange rather than the raw client bodies when a call log exceeds its size budget, and show that recovered payload in the request-detail panel instead of replacing it with the stored response body ([#13147](https://github.com/diegosouzapw/OmniRoute/pull/13147)) — thanks @maxmad64bis +- **fix(traffic-inspector):** the WebSocket route no longer leaks a traffic-buffer subscriber and a 30s ping timer when the client socket is already closed at handler time — listeners are attached before any resource is acquired, a destroyed socket bails out early, and the ping interval stops on a dead socket where `write()` never throws ([#13155](https://github.com/diegosouzapw/OmniRoute/pull/13155)) — thanks @anhtahaylove +- **fix(telegram):** bound the per-user API key cache in the Telegram chat proxy so a burst of distinct chat ids can no longer grow the process heap without limit ([#13165](https://github.com/diegosouzapw/OmniRoute/issues/13165)) ([#13166](https://github.com/diegosouzapw/OmniRoute/pull/13166)) — thanks @anhtahaylove +- **fix(stream):** cancel the upstream response body when the JSON-to-SSE sniff unwinds on a body timeout, so a stalled upstream no longer pins the connection ([#13169](https://github.com/diegosouzapw/OmniRoute/issues/13169)) ([#13171](https://github.com/diegosouzapw/OmniRoute/pull/13171)) — thanks @anhtahaylove +- **fix(telegram):** authenticate webhook deliveries with Telegram's `secret_token` so an unauthenticated caller can no longer mint API keys or spend upstream quota ([#13172](https://github.com/diegosouzapw/OmniRoute/issues/13172)) ([#13175](https://github.com/diegosouzapw/OmniRoute/pull/13175)) — thanks @anhtahaylove +- **fix(models):** give discoverable Gemini 3.8 Flash ids their own 65536 output spec so Antigravity no longer clamps them to 16384 ([#13195](https://github.com/diegosouzapw/OmniRoute/pull/13195)) — thanks @HouMinXi +- **fix(dashboard):** leftover catalog-removed provider rows (gemini-cli) can be listed and purged from the providers page ([#13067](https://github.com/diegosouzapw/OmniRoute/issues/13067)) ([#13197](https://github.com/diegosouzapw/OmniRoute/pull/13197)) — thanks @HouMinXi +- **fix(auth):** a dashboard session now requires the `authenticated: true` claim that login, OIDC and the session refresh already emit — a JWT merely signed with `JWT_SECRET` (for example the Cursor CLI passthrough token, which any API-key holder can obtain) no longer verifies as the `auth_token` cookie on any route, the WebSocket handshake or the live server; existing sessions keep working ([#13298](https://github.com/diegosouzapw/OmniRoute/issues/13298)) ([#13375](https://github.com/diegosouzapw/OmniRoute/pull/13375)) +- **fix(api):** `DELETE /v1/batches/delete-completed` now applies the caller's API-key policy (endpoint allowlist, schedule, usage cap, rate limit) like every other `/v1` route, commits the key-scoped sweep per 200-batch chunk instead of holding one write lock for the whole sweep, refuses to loop without progress, and rejects a scope that names both a key and `allTenants` ([#13684](https://github.com/diegosouzapw/OmniRoute/pull/13684)) +- Restore the API-route typecheck gate: the non-streaming leg result lost its discriminated-union narrowing after the server-owned tool loop reassignment, producing 13 new TS2339 diagnostics in `chatCore.ts`. ([#12963](https://github.com/diegosouzapw/OmniRoute/pull/12963)) +- Restore four non-streaming behaviours the server-owned tool loop refactor dropped: client aborts map to 499 with the fixed `Request aborted` message, an aborted request no longer logs a synthetic `clientResponse`, Claude prompt-cache telemetry is recorded again, and a body that cannot be canonicalized no longer throws when the tool loop is off. ([#12990](https://github.com/diegosouzapw/OmniRoute/pull/12990)) +- **fix(skills):** The CLI registry parser now reads positionals declared with `.addArgument()`, not only those written inline in `.command()`. `tunnel create [type]` was being published as `tunnel create`, so the agent-skills sync gate reported drift on every branch and regenerating would have deleted the argument. ([#13009](https://github.com/diegosouzapw/OmniRoute/pull/13009)) — thanks @ntdatt812 +- **fix(combos):** dashboard combo test uses a short prompt, serial probes, and a 60s timeout so reasoning models and rate-limited free pools do not fail the health check ([#13001](https://github.com/diegosouzapw/OmniRoute/pull/13001)) — thanks @HouMinXi +- fix(compression): terminate idle worker threads on eviction so long-running instances stop leaking OS threads and MessagePorts ([#13091](https://github.com/diegosouzapw/OmniRoute/pull/13091)) — thanks @anhtahaylove +- Redact Google API keys of any length in error bodies: the pattern required exactly 39 characters, so shorter or longer `AIza…` credentials passed through unredacted. ([#12964](https://github.com/diegosouzapw/OmniRoute/pull/12964)) +- **fix(grok-cli):** a 402 "Grok Build usage balance exhausted" parks that Grok login as out of credit (Grok Build CLI, grok.com cookie, and xAI OAuth share the weekly pool). Combo routing then tries the next login instead of locking the model for every account in the pool ([#13061](https://github.com/diegosouzapw/OmniRoute/pull/13061)) — thanks @HouMinXi +- fix(compression): spawn the LLMLingua worker with a file URL object so compression actually runs instead of silently failing open on Node ([#13093](https://github.com/diegosouzapw/OmniRoute/pull/13093)) — thanks @anhtahaylove +- **fix(test):** run the local `test` and `test:unit` scripts at concurrency 4 so a full-suite run no longer exhausts the machine's commit charge and kills unrelated processes ([#13187](https://github.com/diegosouzapw/OmniRoute/pull/13187)) — thanks @anhtahaylove +- **fix(monitoring):** `GET /api/monitoring/health` `credentialHealth` now includes a bounded `failedConnections` list (`connectionId`, `status`, sanitized `lastError`) when the probe-cache gauge `failed>0`, plus `source: probe-cache` and a cheap `staleDbNonOkCount` for sticky SQLite `test_status` on active rows. Documents that the live gauge is not `provider_connections.test_status`. ([#12876](https://github.com/diegosouzapw/OmniRoute/pull/12876)) — thanks @RaviTharuma +- **fix(resilience):** an openai-compatible multi-upstream gateway no longer marks the whole connection `credits_exhausted` when a single upstream (or the `/models` / representative-model health probe) returns 402. Quota failures stay model-scoped; true connection-wide auth failures are unchanged. Single-credential 402 key disable ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239)) is preserved. ([#12875](https://github.com/diegosouzapw/OmniRoute/pull/12875)) — thanks @RaviTharuma +- **fix(dashboard):** nine fixes on the Orchestration Canvas, all diagnosed in the Phase 2 reviews. A source that fails now keeps the timestamp of its FIRST failure instead of being re-stamped every poll — the stale line said "since the last poll" no matter how long the source had been down, and the churn also defeated the snapshot's stable identity (it serializes the source list), so the canvas re-rendered on every tick while anything was broken. A source that HAD data and only then started failing is flagged too: previously only a source with no node at all got the warning, so a source that went down mid-session kept a healthy-looking node forever. The rest are pointwise: clicking a filter chip cancels the pending search debounce (left armed, it fired ~300ms later and silently reverted the chip); the search input carries an accessible name; `?state=running, failed` parses like the unpadded form instead of dropping the padded value; the CSV toggle helper is defined once in `model/urlParams.ts` rather than twice; the agents tab tells "nothing running" apart from "the filter matched nothing", offering a clear-filters button instead of setup links that would be wrong advice there; edges stop emitting SMIL particles above 40 simultaneously active edges, keeping the colored stroke; and the drawer's error banner clears when a retried action succeeds. ([#12983](https://github.com/diegosouzapw/OmniRoute/pull/12983)) +- **fix(i18n):** the nine locales added with the EU-language batch (Greek, Estonian, Irish, Croatian, Lithuanian, Latvian, Maltese, Slovenian, Serbian) were missing the eleven Orchestration Canvas keys that Phase 3 introduced, so the compare-runs panel and the "no runs match these filters" empty state fell back to English in those languages (`deepMergeFallback` substitutes English for an absent key, so nothing rendered blank — it rendered untranslated). The coverage gate does not catch this: it enforces an 80% floor per locale, and eleven missing keys out of ~13,000 leaves coverage at 99.9%. Translated for real in each language, calibrated against the wording each file already uses for "run", "filter" and "skill". ([#13294](https://github.com/diegosouzapw/OmniRoute/pull/13294)) +- **fix(dashboard):** five follow-ups from the Phase 2 Orchestration Canvas review. The History drawer now shows the memory section for runs that already left the live TTL window: the persisted `memory_hits` event is parsed into `metadata.memoryHits` with the same defensive validation the drawer applies, and — because that event is observability rather than a state transition — it no longer leaks into the timeline, where it had been inheriting the task's state and rendering as a duplicate transition. The A2A memory recall runs against its own 1.5s deadline instead of inheriting the memory backend's 30s one; overshooting degrades exactly like any other recall failure (no hits, task proceeds), and the timer is cleared on both paths. Repeating a Conductor run carries its `requirements.cli`/`requirements.model` forward, so the new run is pinned to the same runner profile and model rather than drifting to whatever the fleet picks. A successful repeat from the Agents tab now focuses the run it created, instead of leaving the operator on the finished one. And the auth test for `POST /api/conductor/tasks` moved into the shared `ROUTES` array rather than restating the pattern. ([#12988](https://github.com/diegosouzapw/OmniRoute/pull/12988)) +- fix(plugins): stop leaking an exit listener per plugin hook timeout, which triggered MaxListenersExceededWarning on plugins that ignore SIGTERM ([#13092](https://github.com/diegosouzapw/OmniRoute/pull/13092)) — thanks @anhtahaylove +- **fix(validation):** Provider node edits no longer fail with a generic "Invalid request" when the optional daily-quota reset fields are left blank. The dashboard sends `dailyQuotaResetTimezone` and `dailyQuotaResetHour` as `null`, and only the hour accepted it. ([#13066](https://github.com/diegosouzapw/OmniRoute/issues/13066)) ([#13083](https://github.com/diegosouzapw/OmniRoute/pull/13083)) — thanks @ntdatt812 +- **fix(combo):** quota-aware expansion drops banned, inactive, missing, and wrong-provider connections before quota fetch or model dispatch; pins and allowlists stay selectors, not a bypass. Antigravity automatic exhaustion now requires a reported zero remaining, so a positive balance below 1% stays eligible. ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- Fix an intermittent failure in the quota-weighted routing test suite: scores are a function of `Date.now()`, so peers with identical quota scored microseconds apart never tied and swapped order. ([#12946](https://github.com/diegosouzapw/OmniRoute/pull/12946)) +- fix(volcengine): resolve quota window mapping gaps, safe multi-connection console binding, and response cookie sanitization ([#12950](https://github.com/diegosouzapw/OmniRoute/pull/12950)) — thanks @HouMinXi +- **fix(test):** resolve the WebDAV handler path with `fileURLToPath` so the suite's 37 WebDAV tests run on Windows instead of failing with a doubled `C:\C:\` drive prefix ([#13196](https://github.com/diegosouzapw/OmniRoute/pull/13196)) — thanks @anhtahaylove +- **fix(ui):** fall back from invalid AnySearch icon alias (direct commit `9587e07b69`) +- **fix(sse):** stop rebuilding a truncated summary from the collector's cap-dropped event array ([#12718](https://github.com/diegosouzapw/OmniRoute/pull/12718)) — thanks @hartmark +- **fix(conversations):** show a pending spinner for unresolved tool nodes instead of "(empty)" ([#12727](https://github.com/diegosouzapw/OmniRoute/pull/12727)) — thanks @hartmark +- **fix(streaming):** fail fast when an upstream stream produces only lifecycle/heartbeat events, never real content ([#12741](https://github.com/diegosouzapw/OmniRoute/pull/12741)) — thanks @hartmark +- **fix(responses-continuation):** bridge the write-in-flight window with an in-memory pending store ([#12854](https://github.com/diegosouzapw/OmniRoute/pull/12854)) — thanks @hartmark +- **fix(translator):** drop or map agent_message on Chat Completions fallback ([#12880](https://github.com/diegosouzapw/OmniRoute/pull/12880)) — thanks @RaviTharuma +- **fix(api):** page and stream-complete GET /v1/models for large catalogs ([#12882](https://github.com/diegosouzapw/OmniRoute/pull/12882)) — thanks @RaviTharuma +- **fix(quota):** align AUTH window labels with usage API and clarify 503 ([#12884](https://github.com/diegosouzapw/OmniRoute/pull/12884)) — thanks @RaviTharuma +- **fix(ci):** clear two base-reds on release/v3.8.51 — mutation-coverage gate + image-only-model guard ([#12945](https://github.com/diegosouzapw/OmniRoute/pull/12945)) +- **fix(api):** scope batch bulk-delete to the calling API key ([#13211](https://github.com/diegosouzapw/OmniRoute/pull/13211)) +- **fix(tests):** align model-sync log assertion to shipped path redaction (base-red) ([#13227](https://github.com/diegosouzapw/OmniRoute/pull/13227)) +- **fix(test):** make the ToS heading guard actually require the parentheses ([#13228](https://github.com/diegosouzapw/OmniRoute/pull/13228)) +- **fix(quality):** register 4 drifted covering tests in stryker tap.testFiles ([#13229](https://github.com/diegosouzapw/OmniRoute/pull/13229)) +- **fix(logging):** mask *-api-key headers in request-log pipeline (#13273) ([#13401](https://github.com/diegosouzapw/OmniRoute/pull/13401)) — thanks @KooshaPari +- **fix(tests):** correct stale Jina catalog prefix in custom-models test ([#13403](https://github.com/diegosouzapw/OmniRoute/pull/13403)) — thanks @KooshaPari +- **fix(backend):** prune old backups after health-check-repair snapshots (#13308) ([#13404](https://github.com/diegosouzapw/OmniRoute/pull/13404)) — thanks @KooshaPari +- **fix(backend):** guard memory_fts_au trigger with WHEN clause to stop FTS bloat (#13326) ([#13405](https://github.com/diegosouzapw/OmniRoute/pull/13405)) — thanks @KooshaPari +- **fix(free-tiers):** remove obsolete Together $25 signup credit (#12526) ([#13407](https://github.com/diegosouzapw/OmniRoute/pull/13407)) — thanks @KooshaPari +- **fix:** skip non-chat generation models in Test all models ([#13409](https://github.com/diegosouzapw/OmniRoute/pull/13409)) — thanks @KooshaPari +- **fix(providers):** de-duplicate agy and antigravity model catalogs ([#13410](https://github.com/diegosouzapw/OmniRoute/pull/13410)) — thanks @KooshaPari +- **fix(dashboard):** send provider in body for mini-playground web search ([#13411](https://github.com/diegosouzapw/OmniRoute/pull/13411)) — thanks @KooshaPari +- **fix(resilience):** add CJK quota-exhaustion patterns to 429 classifier ([#13412](https://github.com/diegosouzapw/OmniRoute/pull/13412)) — thanks @KooshaPari +- **fix(backend):** eval runner: failed calls no longer score as passed + dotAll regex ([#13413](https://github.com/diegosouzapw/OmniRoute/pull/13413)) — thanks @KooshaPari +- **fix(backend):** eval runner opts out of output-style and memory injection ([#13414](https://github.com/diegosouzapw/OmniRoute/pull/13414)) — thanks @KooshaPari +- **fix:** chain serial quarantine step in npm test script (fixes #13286) ([#13418](https://github.com/diegosouzapw/OmniRoute/pull/13418)) — thanks @KooshaPari +- **fix(antigravity):** derive stable session ID from credentials for prompt-cache reuse (#13305) ([#13424](https://github.com/diegosouzapw/OmniRoute/pull/13424)) — thanks @KooshaPari +- **fix(memory):** merge memory into top-level system field instead of unshifting at messages[0] (#13425) ([#13427](https://github.com/diegosouzapw/OmniRoute/pull/13427)) — thanks @KooshaPari +- **fix(compression):** preserve instruction blocks from lossy engines ([#13523](https://github.com/diegosouzapw/OmniRoute/pull/13523)) — thanks @KooshaPari +- **fix(compression):** preserve polarity words and newlines in ultra heuristic ([#13524](https://github.com/diegosouzapw/OmniRoute/pull/13524)) — thanks @KooshaPari +- **fix(compression):** run user preservePatterns before built-in patterns (#13457) ([#13525](https://github.com/diegosouzapw/OmniRoute/pull/13525)) — thanks @KooshaPari +- **fix(reasoning):** expand deepseek-v4 regex to match v4.1+ and add command-code provider (#13430) ([#13528](https://github.com/diegosouzapw/OmniRoute/pull/13528)) — thanks @KooshaPari +- **fix(providers):** add alwaysReasons metadata for reka-flash-3 ([#13531](https://github.com/diegosouzapw/OmniRoute/pull/13531)) — thanks @KooshaPari +- **fix(playground):** allow Compare columns to scroll independently ([#13532](https://github.com/diegosouzapw/OmniRoute/pull/13532)) — thanks @KooshaPari +- **fix(security):** empty whitelist allows through to prevent admin lockout ([#13534](https://github.com/diegosouzapw/OmniRoute/pull/13534)) — thanks @KooshaPari +- **fix(tests):** add DISABLE_IOREG_STRATEGY env var for macOS test compatibility (fixes #13467) ([#13539](https://github.com/diegosouzapw/OmniRoute/pull/13539)) — thanks @KooshaPari +- **fix(zed-hosted):** map developer role and Gemini enums for Zed proxy ([#13543](https://github.com/diegosouzapw/OmniRoute/pull/13543)) — thanks @KooshaPari +- **fix(analytics):** normalize provider aliases to canonical id in usage_history ([#13545](https://github.com/diegosouzapw/OmniRoute/pull/13545)) — thanks @KooshaPari +- **fix(combo):** use traceId for call log id to prevent UNIQUE constraint failure ([#13546](https://github.com/diegosouzapw/OmniRoute/pull/13546)) — thanks @KooshaPari +- **fix(auth):** check model lockout before returning synthetic noauth connection ([#13547](https://github.com/diegosouzapw/OmniRoute/pull/13547)) — thanks @KooshaPari +- **fix(guardrails):** skip credential redaction for base64 image data URLs ([#13550](https://github.com/diegosouzapw/OmniRoute/pull/13550)) — thanks @KooshaPari +- **fix(resilience):** increase requestQueue.maxWaitMs default from 15s to 30s ([#13553](https://github.com/diegosouzapw/OmniRoute/pull/13553)) — thanks @KooshaPari +- **fix(docs):** document OMNIROUTE_READY_TIMEOUT_MS and allowlist the test-only DISABLE_IOREG_STRATEGY ([#13692](https://github.com/diegosouzapw/OmniRoute/pull/13692)) + ### 📝 Maintenance ---- +- **docs(readme):** reconcile live v3.8.50 provider, free-tier, CLI, routing, test, community, sponsor, acknowledgment, and SVG metrics with their audited source denominators, including a deduplicated OmniRoute-in-Action snapshot and distinct contributor rankings for merged pull requests, GitHub-attributed commits, and Git history ([#11356](https://github.com/diegosouzapw/OmniRoute/pull/11356)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **docs:** sync the canonical provider count 354 → 356 across `README.md`, `AGENTS.md`, `llm.txt` (+ 42 i18n mirrors), the four README SVG diagrams, `docs/reference/PROVIDER_REFERENCE.md` (regenerated) and the `package.json` description after Opper (#11629) and 1min.ai (#11631) boarded the catalog — closes the `check:docs-counts-sync` strict drifts that kept `release/v3.8.51` red ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11673](https://github.com/diegosouzapw/OmniRoute/pull/11673)) — thanks @jonlwheat2-gif +- **test(providers):** update count-derived assertions after the v3.8.51 provider additions — `APIKEY_PROVIDERS` 233 → 235 (Opper #11629 + 1min.ai #11631), reserved-prefix REGISTRY walk 395 → 398, `WEB_FETCH_PROVIDERS` now includes `nimble-search` (#11620), and the provider translate-path golden snapshot regenerated ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11674](https://github.com/diegosouzapw/OmniRoute/pull/11674)) — thanks @jonlwheat2-gif +- **test(sse):** bump the hard-lease connection-query inventory for `src/lib/tokenHealthCheck.ts` to 2 — the verify-only web-cookie sweep added by #11495 split the single `getProviderConnections` call into oauth + cookie variants, which the frozen inventory had not tracked ([#11495](https://github.com/diegosouzapw/OmniRoute/pull/11495)). ([#11677](https://github.com/diegosouzapw/OmniRoute/pull/11677)) — thanks @jonlwheat2-gif / @oyi77 +- **chore(release):** clear the `release/v3.8.51` base-red gates — docs migration count (+38 i18n mirrors), stryker `tap.testFiles`, lockfile registry host, stale ESLint suppressions, the 7 lint regressions they were hiding, the stale `APIKEY_PROVIDERS` partition count (231 → 233), the `glm-5.3-max` tier missing from the GLM test table, a dead `modelType === "chat"` comparison in the models catalog, and the `resolveLiveWsUrl` / `sanitizeLiveWsPort` helpers from #11388 that a later merge dropped from `wsPath.ts` (dashboard live WebSocket URL resolution) ([#11502](https://github.com/diegosouzapw/OmniRoute/pull/11502)) — thanks @MumuTW +- **chore(free-models):** the free-tier regime table now declares which totals figure each regime feeds and whether it may take the no-auth shortcut, so the sets that used to repeat those answers by hand are derived from it — a new `freeType` no longer compiles until it has answered every question, instead of silently contributing to no total ([#11537](https://github.com/diegosouzapw/OmniRoute/pull/11537)) — thanks @maxmad64bis +- **docs(routing):** LKGP strategy copy now states provider-level stickiness and the explicit rules fallback instead of promising per-target pinning ([#11555](https://github.com/diegosouzapw/OmniRoute/pull/11555)) — thanks @maxmad64bis +- **test(chatcore):** pin the response tool-name alias resolution against the ordering hazard that broke every Gemini/Antigravity MCP tool call in v3.8.49. `extractRequestToolIdentityMap` deletes `translatedBody._toolNameMap`, so the later read is always undefined and the ledger survives only through the `requestToolIdentityMap` fallback — removing that fallback previously left the entire tool-name suite green. The resolution moves into `resolveResponseToolNameMap()` next to the map it depends on, with a regression guard that fails without the recovery. No behaviour change. ([#11575](https://github.com/diegosouzapw/OmniRoute/pull/11575)) — thanks @amartinawi +- **docs(env):** document `OMNIROUTE_CORPUS_CACHE_SIZE` in `.env.example` and `ENVIRONMENT.md`, so the env/docs contract check stops failing Docs Gates on every branch ([#11576](https://github.com/diegosouzapw/OmniRoute/pull/11576)) — thanks @ntdatt812 +- **chore(stryker):** register `route-guard-tunnel-processes-local-only` and `universal-quota-aware-routing` in `tap.testFiles`, so their mutant kills count for the three modules they cover ([#11577](https://github.com/diegosouzapw/OmniRoute/pull/11577)) — thanks @ntdatt812 +- **test(authz):** pin `LOCAL_ONLY_API_GET_EXEMPTIONS` by exact membership instead of by entry count, so the guard names the offending path and also catches a substitution ([#11580](https://github.com/diegosouzapw/OmniRoute/pull/11580)) — thanks @ntdatt812 +- **test(build):** pin `resolveNextBuildBundlerFlag` to the `OMNIROUTE_USE_TURBOPACK` contract it implements, across runtimes, instead of asserting an unimplemented Bun override ([#11583](https://github.com/diegosouzapw/OmniRoute/pull/11583)) — thanks @ntdatt812 +- **test(claude-web):** the Claude Web transport test's barrel anchors no longer pin the exact construction spelling. #11421 made the executor map lazy, so `/"claude-web": new ClaudeWebExecutor\(\)/` stopped matching and the test went red on `release/v3.8.51` itself — a base-red gate every open PR inherited. The anchors now match the alias key and the class name and tolerate either spelling in between, and were checked to still fail when an alias is genuinely repointed at another executor ([#11585](https://github.com/diegosouzapw/OmniRoute/pull/11585)) — thanks @ntdat812 +- **test(models):** pin `qwen3.8-max` resolution per provider — two of the four catalogs now serve the bare id and no longer carry `-preview` ([#11590](https://github.com/diegosouzapw/OmniRoute/pull/11590)) — thanks @ntdatt812 +- **test(executors):** drop the stale 128-tool truncation assertion that contradicted the #11444 pass-through contract ([#11591](https://github.com/diegosouzapw/OmniRoute/pull/11591)) — thanks @ntdatt812 +- **test(token-health):** pin the current terminal-skip boundary — expired with retry budget is probed, `account_deactivated` stays skipped ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)) — thanks @ntdatt812 +- **test(models):** follow #11503's retarget of the `gemini-3-pro-high` alias to the hyphenated catalog id ([#11593](https://github.com/diegosouzapw/OmniRoute/pull/11593)) — thanks @ntdatt812 +- **refactor(video bridge):** extract per-video acquisition, whole-result caching, description, and metrics/abort/cleanup out of `VideoBridgeGuardrail.preCall` into a `processVideoPart` seam in a new `videoBridgePipeline.ts`, behind explicit `VideoMediaBrokerPort`, `VideoAudioTranscriptionPort`, and `VideoDrilldownPort` boundaries; `preCall` now only handles request traversal, policy, and response aggregation. The Video tab's FFmpeg/ffprobe runtime status is now an explicit `unknown` / `restricted` / `unavailable` / `available` state instead of a nullable boolean pair, fixing a case where an in-flight or failed probe was mislabeled as "install FFmpeg" ([#11657](https://github.com/diegosouzapw/OmniRoute/issues/11657)). ([#12016](https://github.com/diegosouzapw/OmniRoute/pull/12016)) +- **test(opencode-plugin):** add unit test coverage for `formatFreeBudget()` naming helper ([#11660](https://github.com/diegosouzapw/OmniRoute/pull/11660)) — thanks @f9td56dbgh-hub +- **docs(video):** clarify that the Video Bridge transcript `source` field (`client`, `embedded`, `audio-bridge`) is presently caller-declared and not yet server-verified — OmniRoute enforces the enum shape but does not cryptographically confirm that an `embedded`/`audio-bridge` label came from a server-owned extraction ([#11661](https://github.com/diegosouzapw/OmniRoute/issues/11661)). ([#12001](https://github.com/diegosouzapw/OmniRoute/pull/12001)) +- **chore(providers):** retire the distributed Raycast Relay and Hailuo Web integrations after a provenance audit confirmed substantial derivation from GPL-3.0 sources; the official GitHub and MiniMax API providers remain available. Migration 163 disables legacy connections and invalidates their active leases while preserving connection IDs, audit/history rows, quota snapshots, usage records, and fail-closed API-key connection policies. ([#11691](https://github.com/diegosouzapw/OmniRoute/pull/11691)) +- **chore(providers):** retire the Felo Web provider while its GPL-derived provenance remains on licensing HOLD ([#11698](https://github.com/diegosouzapw/OmniRoute/pull/11698)) — stale runtime and database references now fail closed while existing connection, lease, and usage history remains preserved for auditability. +- **fix(sse):** retire the Gemini Web image-generation bridge while provenance and licensing remain on HOLD, preserving Gemini Web chat/auth/tools and legitimate Gemini/Nano Banana image providers ([#11708](https://github.com/diegosouzapw/OmniRoute/pull/11708)) +- **chore(audio):** retire the built-in EdgeTTS provider and its 11-voice catalog while provenance/licensing review remains on HOLD; the generic `/v1/audio/speech` endpoint and the other speech providers remain available ([#11711](https://github.com/diegosouzapw/OmniRoute/pull/11711)) +- **fix(providers):** retire the unofficial Qwen Web integration and its `qwen-web`/`qw` identities while third-party provenance remains on HOLD ([#11713](https://github.com/diegosouzapw/OmniRoute/pull/11713)) — direct requests now fail closed with a sanitized HTTP 410 and combos skip the retired target, while migration guards preserve connection IDs, allowlists, usage/audit history and invalidate active leases. Supported Qwen API, Qwen Code, cloud, token-plan, audio, local and model-name surfaces remain available; restoring Qwen Web requires a qualified provenance/license decision. +- **fix(providers):** retire `microsoft-designer-web` and its `msdesigner` alias from runtime and catalogs while preserving stored credentials, provider history and allowlists as inactive records — source dependency on gpt4free's [`MicrosoftDesigner.py`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/g4f/Provider/needs_auth/MicrosoftDesigner.py) is proven at snapshot `0943d16eef757a44deb60651c07bbe5fa958cafe`, whose [`LICENSE`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/LICENSE) is GPL-3.0; expressive adaptation is probable, which records provenance/licensing risk without asserting a legal conclusion ([#11720](https://github.com/diegosouzapw/OmniRoute/pull/11720)). +- **docs(legal):** centralize pinned MIT notices for the vendored GCF codec and local flag assets ([#11726](https://github.com/diegosouzapw/OmniRoute/pull/11726)) +- Split the 2,952-line `adobeFireflyClient.ts` into nine focused modules (catalog, credentials, payload, ARP session, upload, responses, IMS, account, poll) behind an unchanged barrel — no consumer edits and no public API change (#11728). — thanks @MumuTW +- **fix(dashboard):** replaced the Model Lockout card's unprovenanced notification MP3 with a short, locally synthesized Web Audio chime ([#11731](https://github.com/diegosouzapw/OmniRoute/pull/11731)) — both toggles retain optional audible feedback without shipping replacement media or third-party code, and unsupported, suspended, or rejected audio contexts fail open without blocking the setting change. +- **chore(deepseek-web):** remove the provenance-unresolved DeepSeek PoW WASM binary and its runtime loader/tracing while retaining the existing JavaScript solver (slower at high difficulty) ([#11732](https://github.com/diegosouzapw/OmniRoute/pull/11732)). +- **chore(assets):** remove four unused provider assets whose redistribution provenance could not be verified while retaining the active Freebuff SVG ([#11735](https://github.com/diegosouzapw/OmniRoute/pull/11735)) +- **docs(assets):** retired two raster images whose redistribution provenance could not be verified ([#11736](https://github.com/diegosouzapw/OmniRoute/pull/11736)) — PicoClaw documentation now reuses the existing SVG, while Zoo Code remains a text link without an image. +- **docs(legal):** add pinned notices for six LobeHub-derived provider SVGs and 65 byte-exact theSVG assets, retaining registry claims as unverified metadata with explicit trademark and HOLD boundaries ([#11737](https://github.com/diegosouzapw/OmniRoute/pull/11737)). +- **chore(quality):** added a versioned 225/225 provider-asset provenance manifest and blocking drift gate ([#11743](https://github.com/diegosouzapw/OmniRoute/pull/11743)) — the inventory pins magic MIME and SHA-256 for every physical file, records immutable source matches for 71 assets, keeps 69 probable and 85 unresolved entries explicit but non-blocking, and treats provenance as source evidence rather than copyright or trademark clearance. +- **refactor(mitm):** replace four previously derived Traffic Inspector utilities — conversation normalization, SSE merging, secret masking, and header sanitization — with independent clean-room implementations based on public provider schemas, WHATWG SSE, and RFC 6750, while keeping the two independently classified passthrough surfaces unchanged ([#11748](https://github.com/diegosouzapw/OmniRoute/pull/11748)). +- Added a unit test that fails seven days before any dated pack under `config/` (`validUntil` and sibling keys) lapses, naming the file and key. The Alibaba free-tier pack expired on 2026-08-27 and turned every PR red the next morning with no commit involved; renewal now happens on someone's terms, not the clock's. ([#11891](https://github.com/diegosouzapw/OmniRoute/pull/11891)) +- `check:workflows` now fails (under `--strict`/`--ratchet`) when any job routed to a self-hosted runner publishes with `--provenance` — npm rejects that with `422` at the registry, which in v3.8.50 only surfaced after the tag and Docker images were public. ([#11895](https://github.com/diegosouzapw/OmniRoute/pull/11895)) +- `scripts/ops/runner-janitor.sh` now proves a path is idle with one `lsof` snapshot and removes stale leftovers itself (tmpfs after 3 h — it is RAM — disk after 24 h), kills orphan `next-build` processes, prunes checkouts of stopped runners, and alerts on memory pressure; `--dry-run` shows exactly what it would do. `docs/ops/RUNNER_BOX.md` reconciled to the measured box (31 GB, 10 listeners). ([#11893](https://github.com/diegosouzapw/OmniRoute/pull/11893)) +- The `next-build` artefact (1.3 GB) is now written and read under `$RUNNER_TEMP` (per-runner, on disk) instead of `/tmp`, which on the self-hosted pool is a 12 GB tmpfs in RAM. Landing it there took 27–32 of the publish job's 76 minutes, and the fixed `/tmp/e2e-build.tar.gz` name let E2E jobs on different runners overwrite each other's download. ([#11896](https://github.com/diegosouzapw/OmniRoute/pull/11896)) +- The CI `build` job now runs in two concurrency lanes — `main` and pull requests — so a release build is never queued behind (or OOM-killed beside) PR builds on the self-hosted pool, which holds one `next-build` comfortably and two at the edge. ([#11901](https://github.com/diegosouzapw/OmniRoute/pull/11901)) +- Re-freeze the ESLint suppressions on `release/v3.8.51` from a clean-room measurement (2 stale file entries pruned, 55 pre-existing `no-explicit-any` in six new files frozen under #11924) and drop the dead `GPT_SIZE_MAP` constant orphaned by the Adobe Firefly client split, so `No new ESLint warnings` stops failing every PR with exit 2 (Refs #11924) ([#11955](https://github.com/diegosouzapw/OmniRoute/pull/11955)) +- Type the 55 `no-explicit-any` sites that had been frozen under #11924 — four redundant casts in `socksConnectorWithFamily.ts` (undici/socks types already accept them) and the mocks/fixtures of the socks-timeout and isFree suites — and drop their suppression entries; the ESLint ratchet shrinks from 5487 to 5432 (Closes #11924) ([#11975](https://github.com/diegosouzapw/OmniRoute/pull/11975)) +- Take the two hosted-runner builds off the PR rail: `Build App` (`build.yml`) is `workflow_dispatch`-only and `DAST smoke (PR)` runs only for PRs into `main` — the 7 GB hosted VM cannot build `release/v3.8.51` in any profile (19/30 red, VM shutdown ~8 min into `next build`) and both checks had turned into permanent noise on every release PR; the bundle stays validated by `ci.yml` on `main` and by `nightly-release-green` on `release/**` (Closes #11946) ([#11962](https://github.com/diegosouzapw/OmniRoute/pull/11962)) +- Move the four nightly jobs that build the backend (`nightly-schemathesis`, `nightly-llm-security` promptfoo + garak, `nightly-resilience` axe-a11y) off the hosted 7 GB runner — where they died on `release/v3.8.51` unseen — onto the box's new `omni-light` pool (two listeners), and document the reshaped fleet (4 active OmniRoute listeners: 2 `omni-build` + 2 `omni-light`, janitor ceiling 4) (Closes #11965) ([#11967](https://github.com/diegosouzapw/OmniRoute/pull/11967)) +- Stop painting every fork PR into `release/**` red: `quality.yml` `Build (advisory)` is skipped (GitHub still reports `continue-on-error` failures as check FAILURE). Hosted `ubuntu-latest` cannot finish `npm run build` on this tree — same class as #11962 taking `build.yml` off the PR rail. `docker-publish.yml` amd64 now runs on the `.113` `omni-build` pool (31 GB / 32 cores, two listeners) with Turbopack, shares the `heavy-build-main` lane with `ci.yml` `Build` so it queues instead of becoming a third heavy, and keeps per-ref concurrency (a merge storm was starting 8 concurrent OOM builds). arm64 stays on `ubuntu-24.04-arm` with webpack — there is no ARM box. Fallback when `USE_VPS_RUNNER` is off: hosted amd64 + webpack (#11976). ([#12021](https://github.com/diegosouzapw/OmniRoute/pull/12021)) +- `docker-publish.yml` builds with webpack (`OMNIROUTE_USE_TURBOPACK=0`) on **both** arches, including amd64 on the `.113` `omni-build` pool. Turbopack had the RAM (31 GB, `omniroute-113-6`) and still panicked (`TurbopackInternalError: there must be a path to a root`, run 33253576569, 26 min in). The same tree's arm64 webpack build on hosted `ubuntu-24.04-arm` succeeded (run 33264823398). Dockerfile already documents webpack as the Docker escape hatch (#11976). ([#12050](https://github.com/diegosouzapw/OmniRoute/pull/12050)) +- **docs(free-tier):** declare the counting vs deciding regimes for "is it free?" and guard the deciding path from DB-backed catalog resolution ([#12226](https://github.com/diegosouzapw/OmniRoute/pull/12226)) — thanks @maxmad64bis +- **docs(auto-combo):** The mode pack table in `docs/routing/AUTO-COMBO.md` now lists all six shipped packs with every weight each one sets, replacing a four-pack table whose numbers had also drifted from the source. It states plainly that no pack sets `quality`, so selecting any pack silences the observed-quality signal. Six more documents that quote the scoring factor count joined the `check:docs-counts` gate, which caught five stale claims — including one naming nine factors that do not exist — and two stale mode pack counts. The dashboard routing panel, which offered four of the six packs and labelled the default strategy "6-Factor Scoring", is now covered by a test; the two packs it was missing are `reliability-first` and `chaos-mode`, the latter labelled as the fault-injection profile it is rather than as one more routing preference ([#12316](https://github.com/diegosouzapw/OmniRoute/pull/12316)) — thanks @maxmad64bis +- **chore(quality):** rebaseline `src/lib/db/apiKeys.ts` for the ACL the key-creation path now preserves ([#12352](https://github.com/diegosouzapw/OmniRoute/pull/12352)) ([#12673](https://github.com/diegosouzapw/OmniRoute/pull/12673)) — thanks @KrzysiekSko +- **chore(providers):** bump the Claude Code wire identity and the Devin bridge image pin from `2.1.220` to `2.1.258` ([#12402](https://github.com/diegosouzapw/OmniRoute/pull/12402)) — thanks @ggiak ([#12604](https://github.com/diegosouzapw/OmniRoute/pull/12604)) +- **docs(env):** align `.env.example`, the README Bun section, and the troubleshooting guide with the code: `OMNIROUTE_USE_TURBOPACK` also governs `npm run build` (not dev-only), `bun run build` follows that flag instead of auto-selecting Webpack, `OMNIROUTE_CHAT_MAX_HEAVY_IN_FLIGHT` is unset by default (no request-count cap), and the structural `503 chat_admission_busy` message matches `chatAdmissionResponses.ts` (#12404 — thanks @pacocartones) +- **chore(electron):** upgrade the desktop app to Electron 44 (Chromium 152, Node 24.18.1) ([#12217](https://github.com/diegosouzapw/OmniRoute/pull/12217)). **Requires macOS 13 (Ventura) or later** — Chromium dropped macOS 12 (Monterey), so Monterey users must stay on an earlier OmniRoute desktop build. Windows and Linux are unaffected; the app already shipped only x64/arm64, so Electron 44 dropping 32-bit builds changes nothing. Removes the `openAsHidden`/`wasOpenedAsHidden` login-item fields deleted in Electron 44 — hidden autostart continues to work through the `--hidden` argument registered with the login item ([#12554](https://github.com/diegosouzapw/OmniRoute/pull/12554)) — thanks @app/dependabot +- **chore(quality):** rebaseline `src/sse/handlers/chat.ts` for the effective-input persistence the continuation fix needs ([#12641](https://github.com/diegosouzapw/OmniRoute/pull/12641)) ([#12680](https://github.com/diegosouzapw/OmniRoute/pull/12680)) — thanks @hartmark +- `Coverage` job on `ci.yml`: the informational Codecov upload gets its own 5-minute ceiling and `continue-on-error`, and the job budget grows from 20 to 30 minutes (the 8-shard c8 merge alone takes ~10) — a stalled upload no longer ends the job `cancelled` and drags a fully green `main` run's conclusion down with it ([#11972](https://github.com/diegosouzapw/OmniRoute/pull/11972)) +- CI hardening on the PR rail: the four `Unit Tests fast-path` shards get `timeout-minutes: 30` (a hung shard held a PR for 64 min instead of GitHub's 6 h default) and both ESLint file caches lose their `restore-keys` fallback, so a cache built under another suppressions file or lint config can no longer report stale verdicts (Refs #11600, #11924) ([#11963](https://github.com/diegosouzapw/OmniRoute/pull/11963)) +- Every CI job that runs a `next build` (`build`, the npm `publish`, both release-green validations) now targets the `omni-build` runner label, which only two of the eight self-hosted runners carry. The box holds one build comfortably and two at the edge; a third now queues on GitHub instead of being OOM-killed by the kernel. ([#11932](https://github.com/diegosouzapw/OmniRoute/pull/11932)) +- **refactor(combo):** move `handleRoundRobinCombo` (and `resolveTargetTokenLimit`) into `open-sse/services/combo/roundRobinCombo.ts`. `combo.ts` drops from 2164 to 1014 lines (`split("\n").length`); the leaf is 1199 (under the 1200 new-file cap). The round-robin call site uses a dynamic `import()` so `releaseStickyPinOnFailure` / `clearStaleLKGP` can stay exported from `combo.ts` without a static cycle. Skip / sticky / semaphore / safety-timer behavior is unchanged. ([#12811](https://github.com/diegosouzapw/OmniRoute/pull/12811)) — thanks @HouMinXi +- **chore(quality):** rebaseline the file-size caps the error-boundary campaign grew past (`open-sse/executors/codex.ts`, `open-sse/vendor/codex-chatgpt-web/bridge.ts`, both via [#12444](https://github.com/diegosouzapw/OmniRoute/pull/12444)) ([#12654](https://github.com/diegosouzapw/OmniRoute/pull/12654)) +- **chore(quality):** rebaseline the file-size caps the hartmark batch grew past (`combos/page.tsx` via [#12355](https://github.com/diegosouzapw/OmniRoute/pull/12355), `open-sse/services/combo.ts` via [#12338](https://github.com/diegosouzapw/OmniRoute/pull/12338)) ([#12623](https://github.com/diegosouzapw/OmniRoute/pull/12623)) — thanks @hartmark +- **chore(quality):** rebaseline the file-size caps the HouMinXi batch grew past when its PRs stacked (`providers/page.tsx`, `chatCore.ts`, `accountFallback.ts`) — each PR measured correctly in isolation, none saw the stacking ([#12619](https://github.com/diegosouzapw/OmniRoute/pull/12619)) +- **chore(quality):** rebaseline `open-sse/services/combo.ts` for the reset-aware scoring the HouMinXi batch stacked ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) ([#12678](https://github.com/diegosouzapw/OmniRoute/pull/12678)) — thanks @HouMinXi +- **docs:** sync provider catalog count (direct commit `4f11b2ae3d`) +- **docs(skills):** sync omni-inference search fallback (direct commit `b4e76a7ed9`) +- **chore(lint):** prune two stale base suppressions (direct commit `1325047d56`) +- **test(auto-combo):** tolerate full-suite rotation load (direct commit `ad3e293f9f`) +- **test:** drain inherited quality snapshot reds (direct commit `21ed68d8ac`) +- **chore(quality):** rebaseline measured CLI bundle growth (direct commit `d35b3f9779`) +- **test(ui):** reproduce invalid AnySearch icon alias (direct commit `12cc6ca834`) +- **test(translator):** fix the relative imports of the relocated deferred-finish test (direct commit `777d9d1629`) +- **docs(counts):** bring the documented provider and migration counts to the measured values (direct commit `e70bea30e9`) +- **test(security):** drop the guard-target set left unused by the sync (direct commit `a32ba0e362`) +- **chore(db):** renumber model_capabilities migration 163 → 169 (cross-branch collision with 163_radar_feed_cache_generated_at) (direct commit `70f5d4cbf9`) +- **docs(cli):** document CLI_PRIME_AGENT_BIN and correct the CLI Agents count (direct commit `9058e39b61`) +- **chore(deps):** refresh runtimes and adopt ESLint 10 ([#11259](https://github.com/diegosouzapw/OmniRoute/pull/11259)) — thanks @backryun +- **refactor(local-corpus):** implement dynamic root resolution and LRU cache ([#11491](https://github.com/diegosouzapw/OmniRoute/pull/11491)) — thanks @raheemuddin786 +- **chore(quality):** prune stale eslint-suppressions entries ([#11596](https://github.com/diegosouzapw/OmniRoute/pull/11596)) +- **chore(ci):** unblock quality gates - env-doc sync + stryker tap.testFiles registration (#9282) ([#11609](https://github.com/diegosouzapw/OmniRoute/pull/11609)) — thanks @Meet6338-X +- **chore(ci):** point the circular-deps gate at dpdm's real JS entrypoint ([#11615](https://github.com/diegosouzapw/OmniRoute/pull/11615)) — thanks @benzntech +- **chore(quality):** rebaseline file-size for #11448/#11495/#11561 growth ([#11630](https://github.com/diegosouzapw/OmniRoute/pull/11630)) +- **deps:** realign onnxruntime-node pin to @huggingface/transformers' 1.24.3 lockstep ([#11633](https://github.com/diegosouzapw/OmniRoute/pull/11633)) — thanks @jonlwheat2-gif +- **test(autoCombo):** port models_dev_tier #11508 guard from node:test to vitest ([#11635](https://github.com/diegosouzapw/OmniRoute/pull/11635)) — thanks @jonlwheat2-gif +- **chore(quality):** rebaseline file-size for #11083/#11631/#11628 growth ([#11636](https://github.com/diegosouzapw/OmniRoute/pull/11636)) +- **chore(lint):** prune stale ESLint suppressions (base-red #11449) ([#11644](https://github.com/diegosouzapw/OmniRoute/pull/11644)) — thanks @jonlwheat2-gif +- **docs(changelog):** aggregate the ten v3.8.50 fragments into CHANGELOG.md ([#11683](https://github.com/diegosouzapw/OmniRoute/pull/11683)) +- **chore(search):** drop unreachable credential sweep after #11642 ([#11687](https://github.com/diegosouzapw/OmniRoute/pull/11687)) — thanks @HouMinXi +- **ci:** add API route TypeScript regression gate ([#11705](https://github.com/diegosouzapw/OmniRoute/pull/11705)) — thanks @Sabeekhann +- **docs(dedup):** correct the CodeQL claim on the tenant-namespace comment ([#11712](https://github.com/diegosouzapw/OmniRoute/pull/11712)) +- **docs(changelog):** add consolidated v3.8.50 stats, top-25 ranking and a .mailmap ([#11715](https://github.com/diegosouzapw/OmniRoute/pull/11715)) +- **docs:** recommend gstack for AI-assisted workflows ([#11770](https://github.com/diegosouzapw/OmniRoute/pull/11770)) — thanks @santosraju99-hub +- **chore(quality):** tighten complexity/cognitive-complexity ratchets to the current tip; land the missed gateways.ts rebaseline ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **docs(i18n):** finish Freepik → Magnific rebrand in locale strings and README ([#11772](https://github.com/diegosouzapw/OmniRoute/pull/11772)) — thanks @turbolego +- **refactor(auto-combo):** fix divergent scoring in combo health reporting ([#11854](https://github.com/diegosouzapw/OmniRoute/pull/11854)) — thanks @jacobsparts +- **chore(changelog):** add missing fragments for #11919/#11918/#11916 ([#11938](https://github.com/diegosouzapw/OmniRoute/pull/11938)) +- **test(check):** escape the runs-on fixture with JSON.stringify, not a quote-only replace ([#11942](https://github.com/diegosouzapw/OmniRoute/pull/11942)) (direct commit `fb7445eaa3`) +- **test(release):** align five suites with the contracts #11933, #11919 and #11876 shipped on release/v3.8.51 ([#11944](https://github.com/diegosouzapw/OmniRoute/pull/11944)) +- **chore(quality):** remove unreachable code and restore test discovery ([#11950](https://github.com/diegosouzapw/OmniRoute/pull/11950)) — thanks @backryun +- **docs(agents):** sync-back landings are fast-forward, never squash ([#11964](https://github.com/diegosouzapw/OmniRoute/pull/11964)) +- **test(infra):** retry recursive temp-dir removal instead of failing a shard on ENOTEMPTY (#11966) ([#11968](https://github.com/diegosouzapw/OmniRoute/pull/11968)) +- **docs(ops):** the .113 heavy-build ceiling is one runner, not two ([#12048](https://github.com/diegosouzapw/OmniRoute/pull/12048)) +- **test(ui):** correct inactive auto-fetch expectation ([#12098](https://github.com/diegosouzapw/OmniRoute/pull/12098)) — thanks @RaviTharuma +- **docs:** document native dependency check escape hatch ([#12101](https://github.com/diegosouzapw/OmniRoute/pull/12101)) — thanks @RaviTharuma +- **test(providers):** regenerate the translate-path golden for OrcaRouter (#11923) ([#12118](https://github.com/diegosouzapw/OmniRoute/pull/12118)) +- **chore(quality):** velocity phase — loosen every numeric baseline by 20% until v4.0, monitor headroom nightly ([#12125](https://github.com/diegosouzapw/OmniRoute/pull/12125)) +- **chore(lint):** batch 0 of #12146 — type the call-log-cap sqlite rows instead of 45 as-any casts ([#12157](https://github.com/diegosouzapw/OmniRoute/pull/12157)) +- **chore(lint):** batch 4 of #12146 — shared/components react-hooks violations resolved ([#12159](https://github.com/diegosouzapw/OmniRoute/pull/12159)) +- **chore(lint):** batch 1 of #12146 — dashboard/cli-code react-hooks violations resolved ([#12160](https://github.com/diegosouzapw/OmniRoute/pull/12160)) +- **chore(lint):** batch 3 of #12146 — dashboard/settings react-hooks violations resolved ([#12162](https://github.com/diegosouzapw/OmniRoute/pull/12162)) +- **chore(lint):** batch 2 of #12146 — resolve the react-hooks compiler violations in dashboard/providers ([#12163](https://github.com/diegosouzapw/OmniRoute/pull/12163)) +- **chore(quality):** register search-432 cooldown test in stryker tap.testFiles ([#12170](https://github.com/diegosouzapw/OmniRoute/pull/12170)) +- **chore(lint):** batch 5 of #12146 — combos, endpoint, provider-stats, api-manager and costs react-hooks violations resolved ([#12174](https://github.com/diegosouzapw/OmniRoute/pull/12174)) +- **build:** add contributor fast profile ([#12192](https://github.com/diegosouzapw/OmniRoute/pull/12192)) — thanks @rafacpti23 +- **docs(readme):** README touch-ups ([#12193](https://github.com/diegosouzapw/OmniRoute/pull/12193)) ([#12194](https://github.com/diegosouzapw/OmniRoute/pull/12194)) ([#12202](https://github.com/diegosouzapw/OmniRoute/pull/12202)) — thanks @rafacpti23 +- **build:** force the webpack bundler in the contributor build profile ([#12197](https://github.com/diegosouzapw/OmniRoute/pull/12197)) — thanks @rafacpti23 +- **build:** skip standalone packaging in the contributor profile ([#12198](https://github.com/diegosouzapw/OmniRoute/pull/12198)) — thanks @rafacpti23 +- **docs(audit):** align every published number with the code and harden check:docs-counts ([#12200](https://github.com/diegosouzapw/OmniRoute/pull/12200)) +- **build:** stub the instrumentation entrypoints in the contributor profile ([#12203](https://github.com/diegosouzapw/OmniRoute/pull/12203)) — thanks @rafacpti23 +- **build:** omit the standalone output target for contributor builds ([#12204](https://github.com/diegosouzapw/OmniRoute/pull/12204)) — thanks @rafacpti23 +- **chore(lint):** batch 6 of #12146 — memory, radar, audit, analytics, cache, usage, activity, home and RequestLoggerV2 react-hooks violations resolved ([#12208](https://github.com/diegosouzapw/OmniRoute/pull/12208)) +- **docs(diagrams):** rename number-carrying diagram files to stable names ([#12210](https://github.com/diegosouzapw/OmniRoute/pull/12210)) +- **docs(dashboard):** redraw onboarding tier-flow SVGs for the real 4-tier model ([#12211](https://github.com/diegosouzapw/OmniRoute/pull/12211)) +- **docs(api):** document every implemented route in openapi.yaml (276 → 692 paths) ([#12212](https://github.com/diegosouzapw/OmniRoute/pull/12212)) +- **chore(lint):** batch 7 of #12146 — final src tail: 37 react-hooks violations across 33 files resolved ([#12244](https://github.com/diegosouzapw/OmniRoute/pull/12244)) +- Revert "docs: recommend gstack for AI-assisted workflows (#11770)" ([#12248](https://github.com/diegosouzapw/OmniRoute/pull/12248)) ([#12249](https://github.com/diegosouzapw/OmniRoute/pull/12249)) +- **docs:** Chaos Mode setup guide + weighted strategy semantics ([#12250](https://github.com/diegosouzapw/OmniRoute/pull/12250)) +- **docs(agents):** protected-surface merge rule — operator approval for agent-instruction files ([#12253](https://github.com/diegosouzapw/OmniRoute/pull/12253)) +- **chore(quality):** register native-codex turn-pin tests in stryker tap.testFiles ([#12263](https://github.com/diegosouzapw/OmniRoute/pull/12263)) +- **chore(quality):** dedupe tap.testFiles entries added by racing base-red fixes ([#12265](https://github.com/diegosouzapw/OmniRoute/pull/12265)) +- **docs(readme):** introduce OmniRouteTray — the macOS menu-bar companion ([#12276](https://github.com/diegosouzapw/OmniRoute/pull/12276)) — thanks @ggiak +- **chore(proxy):** purge the legacy 1proxy residue — sync/rotator modules, dead DB exports, dead settings tab and flag, docs (#12091) ([#12290](https://github.com/diegosouzapw/OmniRoute/pull/12290)) +- **chore(quality):** baseline-headroom skips generated and vendored files in the fileSize worst-file signal ([#12291](https://github.com/diegosouzapw/OmniRoute/pull/12291)) +- **refactor(video):** unify the JPEG frame data-URI contract ([#12322](https://github.com/diegosouzapw/OmniRoute/pull/12322)) +- **chore(deps):** freeze onnxruntime-node and eslint-plugin-react-hooks out of dependabot groups ([#12329](https://github.com/diegosouzapw/OmniRoute/pull/12329)) +- **test(executors):** restore the #10986 reasoning-only fallback guards ([#12364](https://github.com/diegosouzapw/OmniRoute/pull/12364)) — thanks @pacocartones +- **chore(quality):** re-tighten the file-size ratchet to the real LOC (plan 3.8.52 task 0) ([#12411](https://github.com/diegosouzapw/OmniRoute/pull/12411)) +- **test(grok):** format web executor suite ([#12412](https://github.com/diegosouzapw/OmniRoute/pull/12412)) +- **chore(quality):** ratchet open-sse typecheck baseline to zero ([#12418](https://github.com/diegosouzapw/OmniRoute/pull/12418)) +- **test(build):** guard the artifact path policy arrays against duplicates ([#12422](https://github.com/diegosouzapw/OmniRoute/pull/12422)) +- **chore(lint):** adopt eslint-plugin-react-hooks 7.1.1 ([#12428](https://github.com/diegosouzapw/OmniRoute/pull/12428)) +- **chore(providers):** remove a keyless provider integration at its operator's request ([#12440](https://github.com/diegosouzapw/OmniRoute/pull/12440)) +- **docs(providers):** register providers removed at their operator's request and guard against reintroduction ([#12478](https://github.com/diegosouzapw/OmniRoute/pull/12478)) +- **chore(quality):** base fixes — stryker tap.testFiles + node_modules cache key ([#12482](https://github.com/diegosouzapw/OmniRoute/pull/12482)) +- **docs(arch):** one-process recipe for tens of long /v1/responses ([#12493](https://github.com/diegosouzapw/OmniRoute/pull/12493)) — thanks @RaviTharuma +- **chore(quality):** tighten the CodeQL ratchet baseline from 11 to 6 ([#12530](https://github.com/diegosouzapw/OmniRoute/pull/12530)) +- **chore(deps):** pin browserslist override to ^4.28.8 ([#12592](https://github.com/diegosouzapw/OmniRoute/pull/12592)) — thanks @KooshaPari +- **chore(deps):** bump fast-uri to 3.1.7 in the electron lockfile ([#12601](https://github.com/diegosouzapw/OmniRoute/pull/12601)) +- **docs(quality):** document how the CodeQL ratchet refreshes and how to tighten it ([#12611](https://github.com/diegosouzapw/OmniRoute/pull/12611)) +- **test(ci):** pin the openapi-security-tiers two-arm contract with an executing gate test (#12581) ([#12652](https://github.com/diegosouzapw/OmniRoute/pull/12652)) +- **docs(gamification):** align XP Rewards table with code (#12501) ([#12667](https://github.com/diegosouzapw/OmniRoute/pull/12667)) — thanks @KooshaPari +- **refactor(combo):** split executeTarget into gates, attempt, and loop ([#12746](https://github.com/diegosouzapw/OmniRoute/pull/12746)) — thanks @HouMinXi +- **docs(security):** document socket.yml scanner config + CI workflow link (#12575) ([#12764](https://github.com/diegosouzapw/OmniRoute/pull/12764)) — thanks @KooshaPari +- **chore(ci):** guard commit identity in pre-commit to stop author misattribution ([#12772](https://github.com/diegosouzapw/OmniRoute/pull/12772)) +- **deps:** 17 Dependabot bumps — bump electron from 43.4.0 to 43.4.1 in /electron ([#11426](https://github.com/diegosouzapw/OmniRoute/pull/11426)); bump the development group across 1 directory with 8 updates ([#11428](https://github.com/diegosouzapw/OmniRoute/pull/11428)); bump the production group across 1 directory with 11 updates ([#11440](https://github.com/diegosouzapw/OmniRoute/pull/11440)); chore(deps): bump github/codeql-action/init from 4.37.7 to 4.37.8 ([#11925](https://github.com/diegosouzapw/OmniRoute/pull/11925)); chore(deps): bump github/codeql-action from 4.37.7 to 4.37.8 ([#11926](https://github.com/diegosouzapw/OmniRoute/pull/11926)); chore(deps): bump github/codeql-action/analyze from 4.37.7 to 4.37.8 ([#11927](https://github.com/diegosouzapw/OmniRoute/pull/11927)); build(deps): bump github/codeql-action/init from 4.37.8 to 4.37.9 ([#12345](https://github.com/diegosouzapw/OmniRoute/pull/12345)); build(deps): bump github/codeql-action/analyze from 4.37.8 to 4.37.9 ([#12346](https://github.com/diegosouzapw/OmniRoute/pull/12346)); bump the development group across 1 directory with 2 updates ([#12347](https://github.com/diegosouzapw/OmniRoute/pull/12347)); build(deps): bump github/codeql-action from 4.37.8 to 4.37.9 ([#12349](https://github.com/diegosouzapw/OmniRoute/pull/12349)); bump browserslist from 4.28.2 to 4.28.8 ([#12396](https://github.com/diegosouzapw/OmniRoute/pull/12396)); bump the production group across 1 directory with 4 updates ([#12399](https://github.com/diegosouzapw/OmniRoute/pull/12399)); bump @xmldom/xmldom ([#12500](https://github.com/diegosouzapw/OmniRoute/pull/12500)); bump qs from 6.15.2 to 6.16.0 ([#12512](https://github.com/diegosouzapw/OmniRoute/pull/12512)); bump @xmldom/xmldom from 0.9.10 to 0.9.12 ([#12513](https://github.com/diegosouzapw/OmniRoute/pull/12513)); bump fast-uri from 3.1.5 to 3.1.7 ([#12514](https://github.com/diegosouzapw/OmniRoute/pull/12514)); bump @humanfs/node from 0.16.7 to 0.16.8 ([#12515](https://github.com/diegosouzapw/OmniRoute/pull/12515)) +- Clear the `release/v3.8.51` `check:agent-skills-sync` base-red: regenerate `skills/cli-tunnel/SKILL.md` so the `tunnel create [type]` positional that #13009 taught the generator to read is reflected in the committed skill. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` typecheck base-red from #12731: the new `Mode pack ranking gates` candidates in `open-sse/services/autoCombo/__tests__/autoCombo.test.ts` omitted the required `provider`, `model` and `errorRate` fields of `ProviderCandidate`, failing `check:open-sse-typecheck` (and with it `Fast Quality Gates`) on every open PR. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` docs-sync base-red: EURouter (#13025) and GreenPT (#13024) took the live provider count to 358, leaving 7 STRICT drifts (`PROVIDER_REFERENCE.md`, 4 diagrams, 2 tier-flow images) plus the `AGENTS.md` / `llm.txt` / `package.json` count claims stale. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- **test(models):** Pin `CATALOG_BUILD_TIMEOUT_MS` in the #12058 canonical-catalog regression so a cold catalog build racing the 8s default bound no longer flakes the suite with a 500 ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Allow `@omniroute/opencode-plugin-v2/` in the npm pack policy — [#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870) shipped the v2 plugin beside its v1 sibling without widening the allowlist, so every packed file under it read as an unexpected artifact and failed `check:pack-policy` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Point the release-green validator's pack gate at the tree it is validating — the build-provenance guard defaulted to `origin/main`, which a release branch tip can never be an ancestor of mid-cycle, so `Package artifact` reported a false red on every run and the tarball boot-smoke cascaded off it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **docs(checks):** keep doc counts honest — headings, rankings, catalog, weights, quality gate and scoring diagram now covered ([#12507](https://github.com/diegosouzapw/OmniRoute/pull/12507)) — thanks @maxmad64bis +- **chore(lifecycle):** `check:model-lifecycle` now also diffs `DEFAULT_DEGRADATION_MAP` (the background-task redirect table) against the vendor lifecycle snapshot, refusing a retired id as source or target, with a table-driven unit test beside it. Three rows whose source the vendor had retired — `claude-sonnet-4-20250514`, `gemini-3-pro-preview` and `gpt-5.1-codex` (whose target `gpt-5.1-codex-mini` is retired too) — were dead code, since `checkLifecycle` answers 410 before the redirect runs; they are dropped (#12535 — thanks @pacocartones) +- **test(compression):** cover idle worker eviction at the resource level — the pool must call `terminate()` and must not retain the worker's `MessagePort`, complementing the `exit`-event assertion added with the fix ([#13371](https://github.com/diegosouzapw/OmniRoute/pull/13371)) +- **chore(quality):** raise the `imageGeneration.ts` file-size ceiling for the image-only-model guard that clears the #12945 base-red ([#13392](https://github.com/diegosouzapw/OmniRoute/pull/13392)) +- **chore(quality):** type the combo-test route's JSON response bodies instead of casting them to `any`, drop the now-empty suppression entry, and remove the `hasPerModelQuota` import `chatCore.ts` stopped using when the failure-classification helper was extracted ([#13379](https://github.com/diegosouzapw/OmniRoute/pull/13379)) +- **chore(electron):** the desktop bundle no longer ships the root-level files of every translated docs mirror (`README.md`, `llm.txt`, `CLAUDE.md`, `GEMINI.md`, `CONTRIBUTING.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`) — the packaged app only reads `docs/i18n//docs/**`, which stays. Saves ~11 MB on top of the translated CHANGELOGs already pruned. (#0000) ([#13235](https://github.com/diegosouzapw/OmniRoute/pull/13235)) +- **chore(quality):** retighten the `src/sse/handlers/chat.ts` unused-vars suppression count to the 8 violations that actually remain ([#13385](https://github.com/diegosouzapw/OmniRoute/pull/13385)) +- Bring the documented counts back in line with the code: 171 migrations (was 169) and 20 routing strategies (was 19), plus the regenerated `cli-tunnel` skill reference. ([#12970](https://github.com/diegosouzapw/OmniRoute/pull/12970)) +- Update the documented migration count to 172 after the call-logs provider-stats indexes landed. ([#13209](https://github.com/diegosouzapw/OmniRoute/pull/13209)) +- **chore(ci):** two gates that close the blind spots behind the exclusions above. `check:vitest-exclusions` requires every Vitest exclusion to name a tracking issue and to appear in `config/quality/vitest-exclusions.json` — the previous list grew to 62 files behind a comment pointing at an issue that had been closed for a month. `check-new-key-coverage` requires a key newly added to `en.json` to reach every locale; the existing coverage gate is a percentage floor per locale, so eleven absent keys out of ~13,000 left it at 99.9% while a whole feature shipped untranslated in nine languages. Both are diff-aware, so pre-existing debt stays frozen and neither needed a migration to turn on. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **build:** root layout no longer loads Inter from `next/font/google`, so a production image build does not need fonts.googleapis.com ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **docs(i18n):** bring the eight new `llm.txt` mirrors (km, kn, ml, my, ne, or, pa, si) up to the current provider and migration counts, so `check-docs-sync` stops rejecting every commit ([#13674](https://github.com/diegosouzapw/OmniRoute/pull/13674)) +- **refactor(ui):** flow surfaces (home topology, combo live studio, compression cockpit/waterfall/nodes, token-health badge) express state through the theme-aware `--orch-status-*` tokens instead of fixed dark-mode hex, so green/red/amber/grey stay legible in the light theme; dark mode is byte-identical. Categorical palettes (routing-strategy hues, compression-layer pills, provider brand colors) deliberately stay hex ([#12378](https://github.com/diegosouzapw/OmniRoute/issues/12378)) ([#13203](https://github.com/diegosouzapw/OmniRoute/pull/13203)) +- **chore(tests):** 51 test files that had been excluded from Vitest are running again, restoring roughly 350 assertions to the blocking suite. Each was measured individually first: of the 62 files parked behind the `// #8618 — pre-existing failure` comment, 51 pass against the current tree with no source change, so the exclusions had outlived the failures they were added for. The 11 that genuinely still fail stay excluded, but now point at a live tracker (#13204) rather than at #8618, which was closed in August while the list it tracked kept growing. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **chore(quality):** drop two ESLint suppression entries whose violations no longer exist, so `eslint --suppressions-location` stops rejecting every commit that touches the surrounding files ([#13384](https://github.com/diegosouzapw/OmniRoute/pull/13384)) +- **chore(release):** bring main's post-tag fixes into release/v3.8.51 — sync-back step 2/2 (direct commit `21c488f210`) +- **chore(deps):** patch toml and esbuild in the opencode plugin lockfiles ([#12965](https://github.com/diegosouzapw/OmniRoute/pull/12965)) +- **docs(changelog):** reconcile the v3.8.51 living section — fold 366 fragments, cover every cycle commit, credit every contributor ([#12971](https://github.com/diegosouzapw/OmniRoute/pull/12971)) +- **test(plugin):** wait for the v2 lazy-refresh tier instead of sleeping past it ([#12984](https://github.com/diegosouzapw/OmniRoute/pull/12984)) +- **chore(quality):** rebaseline chatCore.ts after the non-streaming regression fixes ([#13045](https://github.com/diegosouzapw/OmniRoute/pull/13045)) +- **docs(changelog):** add the missing bullet marker to the DeepSeek PoW fragment ([#13200](https://github.com/diegosouzapw/OmniRoute/pull/13200)) +- **chore(deps):** drain the Dependabot queue — 10 of 13 alerts ([#13213](https://github.com/diegosouzapw/OmniRoute/pull/13213)) +- **chore(ci):** make worktrees carry the hooks that enforce the identity gate ([#13236](https://github.com/diegosouzapw/OmniRoute/pull/13236)) +- **chore(deps):** pin csv-parse to ^7.0.2 via overrides (#13117) ([#13300](https://github.com/diegosouzapw/OmniRoute/pull/13300)) — thanks @KooshaPari +- **chore(deps):** bump hono from ^4.12.34 to ^4.13.7 (#13148) ([#13301](https://github.com/diegosouzapw/OmniRoute/pull/13301)) — thanks @KooshaPari +- **chore(deps):** pin joi to ^18.2.8 via overrides (#13085) ([#13302](https://github.com/diegosouzapw/OmniRoute/pull/13302)) — thanks @KooshaPari +- **docs(providers):** add Microsoft 365 Copilot (BizChat) provider guide (#12779) ([#13340](https://github.com/diegosouzapw/OmniRoute/pull/13340)) — thanks @KooshaPari +### 🙌 Contributors + +Thanks to everyone whose work landed in v3.8.51: + +| Contributor | PRs / Issues | +| --- | --- | +| [@5dive-bot](https://github.com/5dive-bot) | #11852 | +| [@abhisheksharma2411](https://github.com/abhisheksharma2411) | #11547, #11684, #11959, #12209 | +| [@adevwithpurpose](https://github.com/adevwithpurpose) | #11464 | +| [@adivekar-utexas](https://github.com/adivekar-utexas) | #12015, #12043, #12090, #12138 | +| [@AIB1TAL0S](https://github.com/AIB1TAL0S) | #12103 | +| [@alltomatos](https://github.com/alltomatos) | #11448 | +| [@alvinveroy](https://github.com/alvinveroy) | #11857, #12027, #12165 | +| [@amaleta](https://github.com/amaleta) | #11906, #11934 | +| [@amartinawi](https://github.com/amartinawi) | #11575 | +| [@AndrianBalanescu](https://github.com/AndrianBalanescu) | #11083, #11830, #11834, #11844 | +| [@anhtahaylove](https://github.com/anhtahaylove) | #13091, #13092, #13093, #13096, #13097, #13100, #13106, #13109, #13114, #13155, #13166, #13171, #13175, #13187, #13196 | +| [@aniruddhaadak80](https://github.com/aniruddhaadak80) | #11760 | +| [@app](https://github.com/app) | #12554 | +| [@arminanton](https://github.com/arminanton) | #11461, #11513 | +| [@AStupidBear](https://github.com/AStupidBear) | #11584 | +| [@atescivitci-cmd](https://github.com/atescivitci-cmd) | #12105 | +| [@b3nw](https://github.com/b3nw) | #11970, #11971, #12147 | +| [@backryun](https://github.com/backryun) | #11259, #11950, #12075, #12076, #12078, #12079, #12081, #12082, #12181, #12228, #12239, #12255, #12258, #12277, #12367, #12423, #12524, #12538 | +| [@benjaminkitt](https://github.com/benjaminkitt) | #11747 | +| [@benzntech](https://github.com/benzntech) | #11614, #11615 | +| [@Bl0ck154](https://github.com/Bl0ck154) | #11948, #11951, #11952, #11953, #11954 | +| [@brick30llc-ctrl](https://github.com/brick30llc-ctrl) | #11752, #12242, #12262 | +| [@Chewji9875](https://github.com/Chewji9875) | #11455, #11875, #12028 | +| [@cheynetom](https://github.com/cheynetom) | #12058 | +| [@davidebaraldo](https://github.com/davidebaraldo) | #12222, #12304 | +| [@davidlinfr](https://github.com/davidlinfr) | #6390 | +| [@ddarkr](https://github.com/ddarkr) | #12124 | +| [@Deftera186](https://github.com/Deftera186) | #11809 | +| [@dpozimski](https://github.com/dpozimski) | #11945 | +| [@DW-MediaLab](https://github.com/DW-MediaLab) | #12229 | +| [@echel0nn](https://github.com/echel0nn) | #11923 | +| [@f9td56dbgh-hub](https://github.com/f9td56dbgh-hub) | #11660 | +| [@fabioluissilva](https://github.com/fabioluissilva) | #11991 | +| [@foreveryh](https://github.com/foreveryh) | #12177 | +| [@geek007git](https://github.com/geek007git) | #12115, #12116, #12117, #12120, #12122 | +| [@geekyNads](https://github.com/geekyNads) | #11784, #12266 | +| [@ggiak](https://github.com/ggiak) | #11561, #12276, #12350, #12402 | +| [@gonisulaimann](https://github.com/gonisulaimann) | #12368, #12369, #12371 | +| [@Gorillaz322](https://github.com/Gorillaz322) | #12207 | +| [@groovecityJO](https://github.com/groovecityJO) | #12682 | +| [@hartmark](https://github.com/hartmark) | #11434, #11452, #11473, #11499, #11703, #11983, #11984, #11985, #11986, #11988, #11989, #11990, #11994, #12221, #12293, #12445, #12446, #12447, #12448, #12460, #12461, #12623, #12646, #12650, #12680, #12717, #12718, #12727, #12741, #12854 | +| [@hizzt](https://github.com/hizzt) | #11894 | +| [@hongnoul](https://github.com/hongnoul) | #11484 | +| [@HouMinXi](https://github.com/HouMinXi) | #11411, #11414, #11512, #11518, #11520, #11641, #11642, #11643, #11687, #11779, #11849, #11850, #11851, #11915, #11916, #11918, #11919, #11920, #12013, #12017, #12033, #12042, #12106, #12139, #12166, #12169, #12171, #12205, #12213, #12312, #12325, #12487, #12488, #12495, #12504, #12557, #12566, #12590, #12591, #12624, #12626, #12632, #12637, #12678, #12696, #12697, #12711, #12733, #12746, #12767, #12770, #12789, #12803, #12805, #12811, #12866, #12868, #12899, #12926, #12934, #12950, #12951, #12974, #13001, #13006, #13011, #13017, #13026, #13027, #13034, #13035, #13038, #13042, #13050, #13060, #13061, #13069, #13107, #13120, #13136, #13195, #13197 | +| [@Hsia97](https://github.com/Hsia97) | #11624 | +| [@jacobsparts](https://github.com/jacobsparts) | #11854, #12155, #12167 | +| [@joglomedia](https://github.com/joglomedia) | #11980 | +| [@jonlwheat2-gif](https://github.com/jonlwheat2-gif) | #11443, #11450, #11549, #11567, #11608, #11633, #11635, #11640, #11644, #11671, #11672, #11673, #11674, #11675, #11676, #11677, #11706, #11783, #12051, #12052, #12053, #12055 | +| [@kanade-hoshino](https://github.com/kanade-hoshino) | #12180 | +| [@Karan825](https://github.com/Karan825) | #11861 | +| [@KaspaPulse](https://github.com/KaspaPulse) | #11389, #11469, #11628, #11666, #11910 | +| [@keeltrace](https://github.com/keeltrace) | #12080, #12223 | +| [@killer30001000](https://github.com/killer30001000) | #12178 | +| [@KooshaPari](https://github.com/KooshaPari) | #12470, #12592, #12667, #12699, #12703, #12706, #12764, #12769, #12771, #13300, #13301, #13302, #13340, #13401, #13403, #13404, #13405, #13406, #13407, #13409, #13410, #13411, #13412, #13413, #13414, #13418, #13424, #13427, #13433, #13523, #13524, #13525, #13528, #13531, #13532, #13534, #13539, #13543, #13545, #13546, #13547, #13550, #13553 | +| [@kriptoburak](https://github.com/kriptoburak) | #11370 | +| [@KrzysiekSko](https://github.com/KrzysiekSko) | #12425, #12432, #12673 | +| [@marcelokarval](https://github.com/marcelokarval) | direct commit / report | +| [@maxmad64bis](https://github.com/maxmad64bis) | #1622, #11435, #11437, #11441, #11537, #11550, #11553, #11555, #11557, #11812, #11842, #11843, #11903, #12151, #12214, #12215, #12218, #12226, #12314, #12316, #12317, #12318, #12319, #12320, #12321, #12507, #12715, #12731, #12744, #12786, #12787, #12788, #12790, #12792, #12794, #12795, #12828, #12832, #12853, #12857, #12870, #12937, #12941, #12975, #13141, #13142, #13146, #13147 | +| [@mdigitalbh81](https://github.com/mdigitalbh81) | #12240 | +| [@Meet6338-X](https://github.com/Meet6338-X) | #11598, #11609, #12110 | +| [@morpheus9393](https://github.com/morpheus9393) | #11943 | +| [@MumuTW](https://github.com/MumuTW) | #11492, #11502, #11506, #11507, #11626, #11675, #11685, #11728, #11746, #11888, #11889, #11890, #11892 | +| [@Neuron-Mr-White](https://github.com/Neuron-Mr-White) | #11622, #11801, #12256 | +| [@NightStalker-87](https://github.com/NightStalker-87) | #12183 | +| [@NoxzRCW](https://github.com/NoxzRCW) | #11879, #11880, #11881, #11882, #11883, #11935 | +| [@ntdat812](https://github.com/ntdat812) | #11585, #12095 | +| [@ntdatt812](https://github.com/ntdatt812) | #11368, #11573, #11574, #11576, #11577, #11580, #11582, #11583, #11588, #11589, #11590, #11591, #11592, #11593, #11672, #12177, #12180, #12873, #12918, #12920, #12921, #12925, #12930, #13007, #13009, #13024, #13025, #13083, #13087, #13101, #13104, #13110 | +| [@official-burak](https://github.com/official-burak) | #11542 | +| [@opensource-elearning](https://github.com/opensource-elearning) | #12179, #12189, #12278, #12286 | +| [@oyi77](https://github.com/oyi77) | #11408, #11409, #11421, #11505, #11677, #12036, #12110 | +| [@pacocartones](https://github.com/pacocartones) | #11521, #11522, #11527, #11528, #11529, #11530, #11531, #11532, #11533, #11534, #11595, #11599, #11603, #11604, #11605, #11607, #11610, #11676, #11714, #11716, #11718, #11767, #11838, #11860, #11862, #11869, #11871, #11872, #11873, #11903, #11906, #11921, #11934, #12356, #12358, #12359, #12360, #12361, #12362, #12364, #12365, #12373, #12374, #12375, #12376, #12377, #12379, #12380, #12381, #12386, #12387, #12389, #12390, #12394, #12395, #12397, #12401, #12403, #12404, #12522, #12523, #12535, #12536, #12540, #12541, #12543, #12545, #12548, #12549, #12550, #12551, #12552, #12647, #12651, #12653, #13055 | +| [@patrykkopycinski](https://github.com/patrykkopycinski) | #11936, #11937, #12224 | +| [@PauloFH](https://github.com/PauloFH) | #11509 | +| [@PauloHSOliveira](https://github.com/PauloHSOliveira) | #12241 | +| [@ponkcore](https://github.com/ponkcore) | #12054 | +| [@Prajeeth-12](https://github.com/Prajeeth-12) | #11634, #12046 | +| [@quiterunner-commits](https://github.com/quiterunner-commits) | #12131, #12143 | +| [@rafacpti23](https://github.com/rafacpti23) | #11554, #11558, #12192, #12197, #12198, #12202, #12203, #12204 | +| [@ragnar-claude](https://github.com/ragnar-claude) | #11564 | +| [@raheemuddin786](https://github.com/raheemuddin786) | #11491, #11839, #11840, #11841, #12003, #12230, #12231, #12232, #12233, #12234 | +| [@rahilmavani](https://github.com/rahilmavani) | #11761 | +| [@Rahulsharma0810](https://github.com/Rahulsharma0810) | #11771 | +| [@RaviTharuma](https://github.com/RaviTharuma) | #11710, #11727, #11797, #11798, #11802, #11805, #11806, #11811, #12098, #12099, #12101, #12449, #12452, #12472, #12473, #12493, #12533, #12607, #12628, #12631, #12636, #12875, #12876, #12880, #12882, #12884 | +| [@rezjalibd](https://github.com/rezjalibd) | #12186 | +| [@RhianB14](https://github.com/RhianB14) | #11692 | +| [@rifqiawl](https://github.com/rifqiawl) | #11517, #11519, #11969 | +| [@rqzbeh](https://github.com/rqzbeh) | #11390 | +| [@Sabeekhann](https://github.com/Sabeekhann) | #11699, #11705 | +| [@santosraju99-hub](https://github.com/santosraju99-hub) | #11755, #11770 | +| [@Seramicx](https://github.com/Seramicx) | #11568 | +| [@Siva010](https://github.com/Siva010) | #12191 | +| [@solstxce](https://github.com/solstxce) | #11597 | +| [@soroush5](https://github.com/soroush5) | #12691 | +| [@tenshiak](https://github.com/tenshiak) | #12279 | +| [@TheDemonTuan](https://github.com/TheDemonTuan) | #11468, #11470, #11471, #11482, #11548, #11758, #11775, #11814 | +| [@thomasmaerz](https://github.com/thomasmaerz) | #12834 | +| [@tuandinh0801](https://github.com/tuandinh0801) | #11454 | +| [@turbolego](https://github.com/turbolego) | #11621, #11762, #11772, #11774, #11781, #12216 | +| [@Tushar49](https://github.com/Tushar49) | #11392 | +| [@ujjawalkaushik1110](https://github.com/ujjawalkaushik1110) | #11863 | +| [@vermasomesh835](https://github.com/vermasomesh835) | #11794 | +| [@vsd2807](https://github.com/vsd2807) | #11565, #11619 | +| [@wahidsadik371-coder](https://github.com/wahidsadik371-coder) | #12126 | +| [@watchingdogs](https://github.com/watchingdogs) | #12031 | +| [@wildcard](https://github.com/wildcard) | #11620 | +| [@xiaoyaner0201](https://github.com/xiaoyaner0201) | #11460, #11662, #12083 | +| [@Xxx91n](https://github.com/Xxx91n) | #11690 | +| [@yourspraveen](https://github.com/yourspraveen) | #11146 | +| [@yxyxy](https://github.com/yxyxy) | #11671 | +| [@ZaimMarzuki](https://github.com/ZaimMarzuki) | #11960 | +| [@Zartharas](https://github.com/Zartharas) | #11340 | +| [@zero-executioner](https://github.com/zero-executioner) | #11631 | +| [@diegosouzapw](https://github.com/diegosouzapw) | maintainer | + +--- ## [3.8.50] — 2026-08-25 _Living section — regenerated 2026-08-12 from all cycle commits (cycle open `ed2db6cb19` → tip). Bullets carry the merged PR and its author; direct pushes listed separately._ diff --git a/docs/i18n/ms/CHANGELOG.md b/docs/i18n/ms/CHANGELOG.md index d61cf1ae57..90f20b15f0 100644 --- a/docs/i18n/ms/CHANGELOG.md +++ b/docs/i18n/ms/CHANGELOG.md @@ -8,16 +8,1119 @@ ## [3.8.51] — TBD -_Living section — cycle opened at the v3.8.50 freeze (parallel-cycle model). Bullets are aggregated from `changelog.d/` fragments at each `/generate-release` phase._ +_Living section — reconciled 2026-09-15 from all cycle commits (`091589089c` → `c0f92ec98a`, 916 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 | **123** | +| 📝 Commits in the cycle | **916** | +| 🔀 Pull requests referenced | **892** | +| 📋 Changelog entries | **919** | +| 🙌 Contributors credited in entries | **121** | +| 🤖 Automated dependency commits | 18 | + +**Entries by type** + +| Type | Count | +| --- | ---: | +| 🐛 Fixes | 592 | +| ✨ Features | 131 | +| 🧹 Chore | 93 | +| 📚 Docs | 42 | +| 🧪 Tests | 32 | +| ♻️ Refactor | 8 | +| 🏗️ Build | 7 | +| ⚡ Performance | 7 | +| ⚙️ CI | 2 | +| ⏪ Reverts | 2 | +| 📦 Dependencies | 2 | +| 🔒 Security | 1 | + +### 🏆 Top 25 contributors this cycle + +_By commits in `091589089c..c0f92ec98a`, author identities consolidated via `.mailmap` and the merged PR's GitHub login. Bots excluded._ + +| # | Contributor | Commits | +| ---: | --- | ---: | +| 🥇 | diegosouzapw | 323 | +| 🥈 | Bob.Hou (@HouMinXi) | 79 | +| 🥉 | Paco Cartones (@pacocartones) | 77 | +| 4 | Dizzle (@maxmad64bis) | 49 | +| 5 | Koosha Paridehpour (@KooshaPari) | 43 | +| 6 | Nguyen Thanh Dat (@ntdatt812) | 31 | +| 7 | Ravi Tharuma (@RaviTharuma) | 26 | +| 8 | Markus Hartung (@hartmark) | 22 | +| 9 | Webman (@jonlwheat2-gif) | 22 | +| 10 | backryun | 17 | +| 11 | anhtahaylove | 15 | +| 12 | Rafa Martins (@rafacpti23) | 10 | +| 13 | Syed Raheemuddin (@raheemuddin786) | 10 | +| 14 | Nguyễn Viết Tuấn (@TheDemonTuan) | 8 | +| 15 | MumuTW | 7 | +| 16 | Paijo (@oyi77) | 7 | +| 17 | Tobias Andersen (@turbolego) | 6 | +| 18 | Bl0ck (@Bl0ck154) | 5 | +| 19 | SHANMUGAPRIYAN (@geek007git) | 5 | +| 20 | KaspaPulse | 5 | +| 21 | Abhishek Sharma (@abhisheksharma2411) | 4 | +| 22 | Abhishek Divekar (@adivekar-utexas) | 4 | +| 23 | Andrew B. (@AndrianBalanescu) | 4 | +| 24 | NoxzRCW | 4 | +| 25 | opensource-elearning | 4 | ### ✨ New Features +- **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). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- Added Google AI Studio Gemini batch text-to-speech support through `POST /v1/audio/speech`. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- 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. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(routing):** subscription-first auto groupings — `auto/subscription` routes only through plan-included connections with a documented hard-stop overage and fails closed on exhaustion, while `auto/thrifty` orders the pool `subscription → keyless → free → cheap → premium` and steps up one rung at a time as each is exhausted. Billing class comes from a curated per-connection catalog (uncurated is treated as metered, never plan-included), both reuse STRICT_ZERO_COST's per-connection verification, and a quota reading whose `resetAt` has passed is now refreshed regardless of TTL so routing returns to plan capacity as soon as it resets ([#11146](https://github.com/diegosouzapw/OmniRoute/pull/11146)) — thanks @yourspraveen +- **feat(providers):** publish a management-authenticated versioned web-session credential contract from OmniRoute's canonical browser credential metadata ([#11340](https://github.com/diegosouzapw/OmniRoute/pull/11340)) — thanks @Zartharas +- **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)) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add Xquik X search with typed results, credential validation, REST routing, and MCP selection ([#11370](https://github.com/diegosouzapw/OmniRoute/pull/11370)) — thanks @kriptoburak +- **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)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(dashboard):** surface durable exclusive managed leases in the existing Sessions view, keeping leased clients visible across idle gaps while marking connections with in-flight work as active ([#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389)) — thanks @KaspaPulse +- **build(bun):** allow Turbopack bundler flag on Bun 1.4+ with configurable Webpack fallback ([#11471](https://github.com/diegosouzapw/OmniRoute/pull/11471)) — thanks @TheDemonTuan +- feat(api): add an opt-in `modelVisibilityAllowlist`/`modelVisibilityDenylist` settings pair to curate exactly which models `/v1/models` advertises, mirrored into every `auto/*` combo candidate pool so a denied model cannot be routed to via combo selection either (#11481) ([#11997](https://github.com/diegosouzapw/OmniRoute/pull/11997)) +- **feat(rankings):** the Free Provider Rankings page shows what each provider actually served over the last 24 h. It ranked by ELO alone, which left a provider that answers every call with an error in first place; the usage data was already served by the API but never requested. A provider with too small a sample shows a dash, not a number ([#11546](https://github.com/diegosouzapw/OmniRoute/pull/11546)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **feat(guardrails):** enforce a bounded, deterministic contract for Video Bridge transcripts — 256 cues, 4096 input code units and 4 KiB UTF-8 per cue, 64 KiB total text, malformed-Unicode rejection, focus-window scoping, cross-source reconciliation with contributing-source metadata, and a structural provenance trust boundary so caller JSON can never self-assert `embedded`/`audio-bridge` provenance ([#11652](https://github.com/diegosouzapw/OmniRoute/issues/11652)) ([#12009](https://github.com/diegosouzapw/OmniRoute/pull/12009)) +- **feat(video):** orchestrate optional Video Bridge audio extraction and Audio Bridge STT behind a dual opt-in (operator setting AND per-request signal) — a new loopback-only broker `mode=audio` operation shares the frame path's exact process queue, deadline, AbortSignal, and byte budgets to extract a bounded mono 16 kHz PCM WAV from the same already-downloaded video, then reuses the existing Audio Bridge transcription boundary; provider segment timing is preserved when available and marked coarse otherwise, and every failure degrades to a visual-only-safe partial instead of throwing (#11654). ([#12012](https://github.com/diegosouzapw/OmniRoute/pull/12012)) +- Add a tenant-bound Video Bridge drill-down lifecycle on top of the existing secure cache substrate: opaque hashed handles (never raw session/video identifiers), preview/standard/detail multiresolution variants resampled on read, response pagination capped at 8 frames and 32 MiB, and a new authenticated `/api/v1/video-bridge/drilldown` consumer route that stays disabled for remote access by default and denies cross-key access with the same response as a nonexistent handle (no existence oracle). ([#12006](https://github.com/diegosouzapw/OmniRoute/pull/12006)) +- **test(video):** Add the Video Bridge FU-07/FU-09 promotion-evidence harness (#11656) — a frozen Zod manifest schema covering the 8 required scenario kinds (static scenes, rapid cuts, late facts, fades, blur, small text, close events, visual prompt injection) with a minimum of 3 repetitions per case, deterministic declarative fixture recipes (`videoBridgePromotionFixtures.ts`), a pure medians/p95 metrics aggregator, a pure FU-07/FU-09 promotion-verdict evaluator applying the ticket's exact thresholds (missing token usage always holds), a digest-only persistence layer that never retains raw media or raw model responses, and a versioned per-model promotion allowlist shipped empty with every model defaulting to `hold`. The FU-07/FU-09 promotion verdicts themselves remain HOLD — they require a real evidence run against real models on VPS 192.168.0.15. ([#12008](https://github.com/diegosouzapw/OmniRoute/pull/12008)) +- **feat(video bridge):** "embedded" transcript provenance can now be legitimately earned instead of merely asserted — a bounded, allowlisted (`mov_text`/`subrip`/`webvtt`) subtitle probe runs through the loopback-only Video Bridge broker (at most 2 streams, 10s subdeadline bounded by the request deadline, 256 KiB output, 4096-code-unit lines), normalized through a bounded, ReDoS-safe WebVTT parser and Zod-validated end to end. The adapter always resolves to an explicit `success`/`absent`/`transient_failure` outcome — a subtitle failure never breaks the visual description path, and only a fingerprint-verified broker response (never a caller-declared label) can produce embedded cues (#11659). ([#12011](https://github.com/diegosouzapw/OmniRoute/pull/12011)) +- Default new Antigravity-family connections (agy CLI imports and Antigravity OAuth connects) to model auto-sync, so live model discovery lands in the synced catalog and `/v1/models` picks up freshly released upstream models (e.g. Gemini 3.7 Flash tiers) without code changes. Existing connections keep their current setting; the per-connection dashboard toggle remains the opt-out. (#11685 — thanks @MumuTW) ([#11887](https://github.com/diegosouzapw/OmniRoute/pull/11887)) +- **feat(ui):** enable React Compiler (`reactCompiler: true` + `babel-plugin-react-compiler`) for automatic memoization at build time ([#11783](https://github.com/diegosouzapw/OmniRoute/pull/11783)) — thanks @jonlwheat2-gif +- **feat(providers):** add SeekAi (`seekai.cc`) as an OpenAI-compatible New-API gateway — catalog id `seekai` (alias `ska`), `https://seekai.cc/v1`, live `/v1/models` via `passthroughModels`, aggregator-list membership so New-API balance detection can opt in. No referral/aff codes. ([#11786](https://github.com/diegosouzapw/OmniRoute/issues/11786)) ([#12557](https://github.com/diegosouzapw/OmniRoute/pull/12557)) — thanks @HouMinXi +- **feat(zai):** add GLM-5.3-Flash Coding Plan support (1M context, 128K output, vision, `low|high|max` reasoning) and route `zai` GLM-5.3-family API-key traffic through the OpenAI-compatible Coding Plan endpoint with native thinking defaults ([#11801](https://github.com/diegosouzapw/OmniRoute/pull/11801)) — thanks @Neuron-Mr-White +- **feat(combo):** choose how combo models are ordered — manual, provider, score, or name — via a sort control in the dashboard builder, persisted in `config.modelSort` and re-applied on load and after add ([#11812](https://github.com/diegosouzapw/OmniRoute/pull/11812)) — thanks @maxmad64bis +- **feat(free):** custom models can be marked free-tier via `customModels[].isFree`; `isFreeModel()` is the first door and `hidePaidModels` respects it even for providers outside the free budget ([#11843](https://github.com/diegosouzapw/OmniRoute/pull/11843)) — thanks @maxmad64bis +- **feat(nodejs):** add `5dive` as a `configure` target — `omniroute configure 5dive` / `omniroute setup-5dive` write a 5dive auth profile that points an agent fleet's `claude` seats at OmniRoute, with the root-only write, the loopback-vs-`https` endpoint rule and the per-seat model pin handled explicitly ([#11852](https://github.com/diegosouzapw/OmniRoute/pull/11852)) — thanks @5dive-bot +- **feat(sse):** treat `max` as a first-class reasoning-effort tier and clamp per model family (GLM 5.1+/DeepSeek V4+/Kimi K3+ keep native `max`; o1/MiniMax/Grok/Muse Spark clamp to their upstream ceiling) ([#11875](https://github.com/diegosouzapw/OmniRoute/pull/11875)) — thanks @Chewji9875 +- **feat(providers):** the provider plugin manifest now advertises a `usage-fetch` capability for the 40 providers that have a wired usage/quota fetcher, so external dashboards can read it from `GET /api/v1/provider-plugin-manifest` instead of parsing `open-sse/services/usage.ts` after every release. Discovery only — no new fetcher, no quota change, and the Dashboard quota widget stays gated by `USAGE_SUPPORTED_PROVIDERS`. `USAGE_FETCHER_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/fetcherProviders.ts`) and is re-exported from `services/usage.ts`, keeping the manifest module a light leaf instead of pulling the ~490-module usage dispatcher into the manifest route. ([#11903](https://github.com/diegosouzapw/OmniRoute/pull/11903)) — thanks @maxmad64bis / @pacocartones +- **feat(plugins):** `OMNIROUTE_PLUGINS_DIR` sets the directory the runtime plugin scanner reads — and the root the plugin manager installs into — overriding the `HOME`-derived default, so a Docker/K8s deployment can point straight at its bind-mounted plugin tree instead of moving `HOME` just to relocate the scan path. An image that exports no home no longer scans `/tmp/.omniroute/plugins` in silence: the resolved directory is logged once at startup as `scanner.dir_resolved`, naming the input that won. Unset, behaviour is unchanged. Distinct from the CLI-only `OMNIROUTE_PLUGIN_PATH`, which finds `omniroute-cmd-*` command packages and never reached this scanner ([#11906](https://github.com/diegosouzapw/OmniRoute/pull/11906)) — thanks @amaleta / @pacocartones +- **feat(leases):** add an explicit owner-authenticated status action that returns only the active lease's privacy-safe configured connection and provider labels, with generation fencing and no credential or internal-id disclosure ([#11910](https://github.com/diegosouzapw/OmniRoute/pull/11910)) — thanks @KaspaPulse +- **feat(providers):** import-from-file modal shows per-row API errors and ships a downloadable CSV template ([#12071](https://github.com/diegosouzapw/OmniRoute/issues/12071)) ([#12504](https://github.com/diegosouzapw/OmniRoute/pull/12504)) — thanks @HouMinXi +- **feat(providers):** dashboard search matches connection name and `baseUrl` so imported OpenAI-compat nodes surface on the provider card ([#12108](https://github.com/diegosouzapw/OmniRoute/issues/12108)) ([#12495](https://github.com/diegosouzapw/OmniRoute/pull/12495)) — thanks @HouMinXi +- **feat(routing):** add a `score` Auto router strategy that selects the highest configured weighted score and reuses `explorationRate`. ([#12155](https://github.com/diegosouzapw/OmniRoute/pull/12155)) — thanks @jacobsparts +- Add a runtime feature flag to disable universal context handoffs globally without changing the default behavior. ([#12167](https://github.com/diegosouzapw/OmniRoute/pull/12167)) — thanks @jacobsparts +- **feat(providers):** the provider plugin manifest now also advertises a `usage-supported` capability for the 46 providers whose usage API is accepted by the server and Dashboard routes, so integrators can distinguish "the server will serve quota for this provider" from "a fetcher is wired" without reading TypeScript. Discovery only — no fetcher or quota change. `usage-fetch` resolves on id or alias (the usage dispatcher accepts both); `usage-supported` resolves on id alone, matching the runtime guard `USAGE_SUPPORTED_PROVIDERS.includes(providerId)`. `USAGE_SUPPORTED_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/supportedProviders.ts`) and is re-exported from `providers.ts`, mirroring the `fetcherProviders` leaf from #11903 and keeping the manifest a light module. ([#12214](https://github.com/diegosouzapw/OmniRoute/pull/12214)) — thanks @maxmad64bis +- **feat(rankings):** order Free Provider Rankings by what each provider actually served — `GET /api/free-provider-rankings?sortBy=reliability` and a "Most reliable first" toggle on the page. Providers with too few calls to state a success rate keep their score order below the measured ones; the default order is unchanged ([#12218](https://github.com/diegosouzapw/OmniRoute/pull/12218)). — thanks @maxmad64bis +- **perf(sse):** defer `cloneLogPayload()` in the structured SSE collector until after the `maxEvents`/`maxBytes` cap check, eliminating ~9,800 wasted `structuredClone` calls per streaming response (65–71% faster `push()`). Reducer snapshot isolation restored for OpenAI and Responses summaries ([#12241](https://github.com/diegosouzapw/OmniRoute/pull/12241)) — thanks @PauloHSOliveira ([#12243](https://github.com/diegosouzapw/OmniRoute/pull/12243)) +- **feat(usage):** Devin CLI agentic quota (Codeium seat-management GetUserStatus) and OpenRouter key limits plus account credits now surface in Provider Limits ([#12256](https://github.com/diegosouzapw/OmniRoute/pull/12256) — thanks @Neuron-Mr-White) +- **feat(settings):** persist `headroomUrl` through Settings so status/start use the operator URL instead of only `HEADROOM_URL` ([#12306](https://github.com/diegosouzapw/OmniRoute/issues/12306)) ([#12487](https://github.com/diegosouzapw/OmniRoute/pull/12487)) — thanks @HouMinXi +- **feat(auto-combo):** Auto-Combo scoring can now weigh how often a provider/model has actually succeeded. The engine already carried that number on every candidate — 24 hours of usage history behind a ten-sample floor, real-time metrics otherwise — and the scoring function never read it, while the weight table described `stability` as if it did. `reliability` (`1 - failureRate`, with the same field precedence and the same rate-bounding the speed ranking already uses, so a corrupt reading means "nothing observed" rather than "fails every call") is now a declared factor shipping at weight `0`, so routing is unchanged until an operator gives it one, and the `stability` description now matches what that factor computes ([#12317](https://github.com/diegosouzapw/OmniRoute/pull/12317)) — thanks @maxmad64bis +- **feat(routing):** With `freeAccessPolicy: "strict"`, the read-only candidate listing (`GET /v1/auto-combo/{channel}/candidates`) no longer hides the candidates the zero-cost guard excludes — the same read-only transparency the resilience filter already honours (#9133). Each candidate now carries `freeAccessExclusion` saying why it would be kept out, and it tells an exhausted allowance apart from a quota reading that never arrived or went stale, which used to look identical from the outside. Routing is unchanged: the listing reports, it never enforces. The separate `excludeTosAvoid` guard still drops its candidates without a reason; that gap is now documented rather than closed ([#12319](https://github.com/diegosouzapw/OmniRoute/pull/12319)) — thanks @maxmad64bis +- **feat(radar):** The Radar catalog table now shows two facts it was already receiving from the feed and dropping on the floor: the per-model rate limits (requests and tokens, per minute and per day) in a new column, and a badge when a provider's terms state it may train on the prompts you send. A limit of zero renders as zero rather than "rate-only" — for a ceiling those are opposite facts — and a model with no training statement gets no badge, because an absent statement is not a guarantee ([#12320](https://github.com/diegosouzapw/OmniRoute/pull/12320)) — thanks @maxmad64bis +- **feat(radar):** explain Community, single-use, contributor, supporter, recovery, abuse, offers, and privacy rules before either Radar activation action, and remove the superseded fixed-PR grant promise from every UI locale ([#12342](https://github.com/diegosouzapw/OmniRoute/pull/12342)) +- **feat(gamification):** the dashboard Profile page now shows the real daily streak — `/api/gamification/level` returns `streak: { current, longest }` (per key with `apiKeyId`, operator-wide maximum otherwise) and the streak card reads it instead of a hard-coded 0 (#2403) ([#12377](https://github.com/diegosouzapw/OmniRoute/pull/12377)) — thanks @pacocartones +- **feat(gamification):** the dashboard leaderboard now shows each API key's display name under the Name column instead of a truncated key id; `GET /api/gamification/leaderboard` attaches `name` per entry (name only — no key material), while the shared ranking helper and the federation leaderboard stay id-only — thanks @pacocartones ([#12385](https://github.com/diegosouzapw/OmniRoute/pull/12385)) +- **feat(gamification):** enforce the documented 1000 XP/min per-API-key anti-cheat rate limit on the XP award path; over-limit awards are logged and skipped instead of persisted, and the sliding window now matches the timestamp format stored in `xp_audit_log` ([#2403](https://github.com/diegosouzapw/OmniRoute/issues/2403)) ([#12390](https://github.com/diegosouzapw/OmniRoute/pull/12390)) — thanks @pacocartones +- **feat(admin):** localize the gamification anomalies page — the loading state, the Status column and the Suspicious badge now come from the `common` catalog (new `common.suspicious` key propagated to every locale) — add it to the Gamification sidebar group as `gamification-admin` (`/dashboard/gamification/admin`), and expose the loading and empty states as polite `role="status"` live regions (#12401 — thanks @pacocartones) +- **feat(providers):** skip GitHub combo members missing from the live synced catalog, and drop Copilot models that are policy-disabled or hidden from the model picker ([#12473](https://github.com/diegosouzapw/OmniRoute/pull/12473)) — thanks @RaviTharuma +- **feat(i18n):** locale-expansion tooling — `npm run i18n:add-locale` adds a language to config, dashboard, docs mirrors, CLI, README/indexes and site in one command; browser, CLI and cookie detection resolve `uk`, `fil`/`tl`, `zh-Hant` (and the retired legacy `in` → `id`) via config aliases; `config/i18n.json` now ships in the npm package so the published CLI can read it; new real-translation ratio gate (`npm run i18n:check-ratio`, advisory) with a per-locale ratchet baseline; `run-translation --adopt` restores `.i18n-state.json`; `sync-language-bars` generates 🌐 bars from config; `validate_translation.py` loads its allowlist again. Retires the duplicate `in` locale (Indonesian mislabelled as Hindi) — 42 honest locales; saved `NEXT_LOCALE=in` / `OMNIROUTE_LANG=in` keep working. (#12496) +- **feat(api):** Emit gateway-measured `tokens_per_second` (TTFT excluded) on streaming usage and `X-OmniRoute-Tokens-Per-Second` when first-token latency is known ([#12616](https://github.com/diegosouzapw/OmniRoute/issues/12616)) ([#12631](https://github.com/diegosouzapw/OmniRoute/pull/12631)) — thanks @RaviTharuma +- **feat(opencode-plugin): map gateway cost/usage/tok/s onto OpenCode inference payloads** — the official plugin copies `X-OmniRoute-Response-Cost`, token counts, `X-OmniRoute-Tokens-Per-Second` / `usage.tokens_per_second`, TTFT, and the winning `X-OmniRoute-Model` onto the JSON/SSE body OpenCode already consumes. Missing tok/s is left unset (never `tokens / latency`). (#12636) — thanks @RaviTharuma +- **feat(combo):** add combo strategy `quota-weighted`: skip exhausted accounts, then weighted-draw by leftover / in-flight load; existing conversations stay pinned until the account is empty ([#12789](https://github.com/diegosouzapw/OmniRoute/pull/12789)) — thanks @HouMinXi +- **feat(models):** Account live listings become the chat catalog source for Claude, Codex, Copilot, and AGY; public metadata only fills prices on IDs those accounts already list. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **fix(models):** Union `agy` and `antigravity` live catalogs so `agy/gemini-3.8-flash-high` is not rejected after the prefix folds to `antigravity`. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **feat(opencode):** Add `@omniroute/opencode-plugin-v2`, an opencode v2 plugin that gives a v2 install a route to an OmniRoute gateway: it publishes models, combos and auto-combos into the host catalog, refreshes them lazily behind a 300s TTL, and keeps serving the last known catalog from an on-disk snapshot when the gateway is unreachable. The v1 plugin (`@omniroute/opencode-plugin`) is untouched and keeps working as before. The plugin adapts to the host instead of assuming one shape: it reads the shape the host seeds into the catalog draft and publishes the `api` block, the pre-`api` top-level fields, or both. The gateway key comes from the host credential store when one is connected, so no secret needs to sit in a config file; a source the gateway refuses is named in a warning instead of silently degrading the catalog to raw model ids; and tool schemas bound for a Gemini model are stripped of the JSON-Schema keywords Gemini rejects, so tool calling survives that route ([#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870)) — thanks @maxmad64bis +- feat(services): show sanitized CLIProxyAPI account health from its authenticated management API without exposing credentials, file paths, or raw account metadata (#6342) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add AnySearch free web search + URL extract (webFetch) with typed results, credential validation, REST routing, and MCP selection - fallback-only ([#11690](https://github.com/diegosouzapw/OmniRoute/pull/11690)) — thanks @Xxx91n +- **feat(dashboard):** display clamped `[0, 100]%` cached input token ratio in request logs table ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) — thanks @b3nw +- **feat(catalog):** add `OMNIROUTE_DISABLE_THINKING_LEVEL_VARIANTS` feature flag to optionally filter out thinking level variants from model catalog ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) — thanks @b3nw +- **feat(dashboard):** continuously export call logs to external analytics stores. A pluggable destination registry ships the full Logs-tab record set on an hourly `JobRegistry` cron, with a persisted per-destination cursor, batched inserts, a config UI rendered from each destination's own field descriptors, and a REST layer (`/api/log-export/*`) for CRUD, a connection test, and an on-demand run. A destination can opt into `includeBodies` to also ship the request and response payloads shown in the Logs detail pane, including the client and provider views of each call; this is off by default, and payloads inherit the dashboard's PII sanitisation, secret redaction and `noLog` handling. Google BigQuery is the first destination, using a service-account key stored encrypted at rest and streaming inserts keyed by call-log id, into a table that is day-partitioned on `timestamp` and clustered on `api_key_name`, `provider`, `model` and `status`. ([#11945](https://github.com/diegosouzapw/OmniRoute/pull/11945)) — thanks @dpozimski +- **feat(providers):** add MaxAI as a signed, OpenAI-compatible provider serving its 13 paid chat models (GPT-5.6 / Luna / Thinking, Claude 5 Sonnet, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite, Grok 4.1-fast / 4.5, DeepSeek V3.2 / R1, Llama 3.3 70B) through OmniRoute's `/v1` endpoint, with per-request HMAC-SHA1→SM3→AES request signing, live model + context-window discovery from `/models/get_config`, and prompted tool-calling translated to OpenAI `tool_calls` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI vision input — image_url content parts are forwarded inline in `message_content` to the 6 vision-capable models (GPT-5.6 / Luna / Thinking, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite) ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI image generation — 6 image models (gpt-image-1, dall-e-3, flux-1-schnell/dev/pro, sd3-medium) exposed through `POST /v1/images/generations` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI document RAG — inline base64 file/document attachments are uploaded to MaxAI (content-addressed `doc_id`) and attached to the chat via `doc_list` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** browserless MaxAI onboarding — email device-pair login (`/api/providers/[id]/login`) and signed access-token refresh, so a connection can be created and kept fresh without a real browser or Google OAuth ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** per-provider TLS impersonation profile (MaxAI presents a Windows Firefox-150 client fingerprint) so its bot-sensitive endpoints accept OmniRoute traffic ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** add **Nimble** as a web-search and web-fetch provider (`nimble-search`) — `/v1/search` routes to Nimble's search API at `lite` depth (locale, freshness and include/exclude domain filters mapped onto the shared request contract), and `/v1/web/fetch` routes to Nimble Extract, which covers all four fetch formats (`markdown`, `html`, `links`, `screenshot`) from a single call. One API key serves both surfaces. ([#11620](https://github.com/diegosouzapw/OmniRoute/pull/11620)) — thanks @wildcard +- The npm publish is automatic again, through npm Trusted Publishing (OIDC): the hosted `stage-npm` job publishes with a short-lived credential minted from GitHub's id-token — no `NPM_TOKEN`, no 2FA prompt, provenance attached. `publish_mode=staged` (owner approves with 2FA) and `direct` (token) remain available on `workflow_dispatch`. ([#11931](https://github.com/diegosouzapw/OmniRoute/pull/11931)) +- **feat(providers):** Add **Opper** as an API-key gateway provider — EU-hosted AI gateway with 700+ models from 30+ providers behind one OpenAI-compatible API and one key (`OPPER_API_KEY`); model ids use `provider/model` format (e.g. `anthropic/claude-sonnet-4-6`, `openai/gpt-5`); live model catalog at `https://api.opper.ai/v3/compat/models`; entry mirrors `requesty` (same shape, `passthroughModels: true`, no static seed) ([#11629](https://github.com/diegosouzapw/OmniRoute/pull/11629)) +- **feat(dashboard):** the `/dashboard/orchestration` snapshot hook now subscribes to the `agents` WebSocket channel (`agent.task.updated`) instead of `requests` as its refetch trigger, and relaxes its background poll from 5s to 30s while that WS connection is up — falling back to the tighter 5s cadence, reprogrammed live on any connect/disconnect transition, whenever the socket is down. ([#12409](https://github.com/diegosouzapw/OmniRoute/pull/12409)) +- **feat(dashboard):** Orchestration canvas quick wins — search box plus state/source/provider filter chips with a one-click clear, and per-source collapse/expand, all reflected in the URL so a filtered/collapsed view is shareable and survives a refresh; the detail drawer gained a "copy trace JSON" action and hardened error/empty-state and accessibility handling; the Agents-tab edges now animate traveling particles along active (running) connections; and the canvas node/edge status colors moved off fixed hex values onto theme-aware `--orch-status-*` CSS custom properties, so they adapt correctly to light/dark mode. ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- **chore(dashboard):** Orchestration UI hardening pass and the missing component/model test coverage it called for — `OrchestratorNode`/`ActivityNode`/`OverflowNode` rendering, the `?node=`/overflow-click page routing, the Agents-tab orchestrator-click no-op and `showCompleted` toggle, and the overview kanban's done-column sort order (#12270, #12271). ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- New `/dashboard/orchestration` page: live unified view of everything running — Cloud Agent, A2A and Conductor as a real-time graph (Agents tab), the combo cascade (Routing tab, reusing the Combo Live Studio) and a state kanban (Overview tab), with a detail drawer (trace, cost, approve/cancel). Read-only over existing APIs — no new backend. Canvas concept credit: PR #11815 design ([#12261](https://github.com/diegosouzapw/OmniRoute/pull/12261)) +- **feat(dashboard):** new "History" tab on `/dashboard/orchestration` — an Airflow-style grid of finished runs over a 24h/7d/30d preset window, one row per (source, identity), clicking a cell opens the existing detail drawer. It is backed by real persistence: A2A task lifecycle transitions are now written to the `a2a_tasks` table (purged after 30 days, configurable via `OMNIROUTE_A2A_HISTORY_RETENTION_DAYS`) and served by the new `GET /api/a2a/tasks/history` listing endpoint, with the task-detail route falling back to persisted history once a run leaves the in-memory snapshot. Conductor runs stay remote and are not persisted locally — the tab says so instead of silently omitting them. ([#12479](https://github.com/diegosouzapw/OmniRoute/pull/12479)) +- **feat(dashboard):** the orchestration detail drawer gained a "Repeat" action for Cloud Agent, A2A and Conductor tasks — a two-click confirm (click once to arm, click again within the confirm window to fire) re-submits the original prompt/input as a new run. The button is disabled with an explanatory tooltip whenever the original input can't be recovered from the loaded task detail (e.g. it never carried a prompt, or the detail failed to load). Two limitations of the A2A variant, by design: it targets the `/a2a` JSON-RPC endpoint, which authenticates with an API key only (`REQUIRE_API_KEY=true` or a configured `OMNIROUTE_API_KEY` makes a dashboard-session repeat answer `HTTP 400` — surfaced verbatim in the drawer's error line, never as a success), and the `message/send` call is SYNCHRONOUS: the POST blocks for the whole skill run, so the success confirmation only appears once the repeated task finishes. A dashboard-authenticated A2A creation path is deliberately left to a follow-up — widening the endpoint's auth posture is an operator decision, not a side effect of this feature. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(a2a):** A2A task execution now records which memories were consulted for the task's last user message as `metadata.memoryHits` (id/key/type/content-snippet) plus a `memory_hits` history event, purely for observability — the retrieved memory is never injected into a skill's prompt or behavior. Gated by the `OMNIROUTE_A2A_MEMORY_HITS` kill-switch (default enabled; set to `0` to skip the recall lookup entirely). The drawer's new "Memory used" section lists these hits for a2a tasks and is omitted whenever there are none. Known limitation: recall only resolves under the keyless posture — a keyed caller's task owner is a SHA-256 prefix of the API key, while memory rows are keyed by the database api-key id, and no hash→id lookup exists today, so the hit list stays empty for keyed callers. The recorded hits are also kept out of the task's own `input` (and therefore out of the persisted input and of the "Repeat" request body), so repeating a task never re-sends the previous run's memory snippets. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(providers):** add a Perplexity Agent API provider (`perplexity-agent` / `pplx-agent`) for Perplexity `/v1/responses`, including the documented Anthropic, OpenAI, Google, xAI, DeepSeek, Z.AI, Moonshot/Kimi, NVIDIA, and Perplexity model IDs plus Anthropic-model `max_output_tokens` compatibility. ([#12103](https://github.com/diegosouzapw/OmniRoute/pull/12103)) — thanks @AIB1TAL0S +- **feat(providers):** add RPD (Requests Per Day) limit to provider rate limit overrides across UI, schemas, DB, and i18n ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) — thanks @b3nw +- **feat(providers): add UC Direct (uncensored.com Developer API), the metered OpenAI-compatible surface.** A standard OpenAI-compatible passthrough (default executor) for uncensored.com's official REST API at `https://api.uncensored.com/api/v1`: `X-api-key` auth (never-expiring `uai_sk_live_` key), `POST /chat/completions` with streaming SSE and native tool-calling, and the full live metered catalog (82 models across 15 providers, discovered from the public `GET /v1/models`). Registered as provider `uc-direct` (alias `ucd`). Complements the un-metered `uc` persona provider — same models, metered credits and a plain API key instead of a subscription session. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(providers): add UC (uncensored.com), the un-metered subscription "persona" chat as an OpenAI-compatible provider.** A WebSocket web-app port: a durable Clerk credential mints a short-lived session token per connect (browserless — no API key), driving UC's persona socket. Ships the browserless email-code login (request → verify → harvest), the 19 verified persona models (Claude Opus, Gemini, Grok, GLM, Kimi, DeepSeek, MiniMax, incl. the uncensored variants), prompted `` tool-calling with a per-model code-style dialect + auto-cure retry for guardrailed models, live ``/reasoning split, streaming + non-streaming OpenAI responses, and full quota/auth error surfacing (paywall / message-limit / rate-limit → 429, invalid session → 401 re-login). Full multimodal parity via the persona blob-upload layer: **vision** (image input, 15 vision-capable models), **document RAG** (PDF/doc upload, server-side extraction), **image generation** (22 models), **video generation** (14 models, async signed-url → poll), and **TTS** (streaming MP3). Registered as provider `uc` (alias `ucn`). The metered OpenAI-compatible Developer API is a separate `uc-direct` provider. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(logs):** add copy-all button to request detail modal ([#11083](https://github.com/diegosouzapw/OmniRoute/pull/11083)) — thanks @AndrianBalanescu +- **feat(check):** make serviceKinds required and add the reverse-walk provider consistency gate ([#11392](https://github.com/diegosouzapw/OmniRoute/pull/11392)) — thanks @Tushar49 +- **feat(providers):** only advertise verified-working connections by default ([#11448](https://github.com/diegosouzapw/OmniRoute/pull/11448)) — thanks @alltomatos +- **feat(resilience):** add hierarchical concurrency admission ([#11493](https://github.com/diegosouzapw/OmniRoute/pull/11493)) +- **feat(providers):** derive + surface expiry for JWT-bearing web cookies (#11497) ([#11505](https://github.com/diegosouzapw/OmniRoute/pull/11505)) — thanks @oyi77 +- **feat(tunnels):** support named cloudflared tunnels via CLOUDFLARED_CONFIG ([#11561](https://github.com/diegosouzapw/OmniRoute/pull/11561)) — thanks @ggiak +- **feat(compression):** make proactive context-compression threshold a live setting ([#11564](https://github.com/diegosouzapw/OmniRoute/pull/11564)) — thanks @ragnar-claude +- **feat(resilience):** add provider peak-hour protection ([#11622](https://github.com/diegosouzapw/OmniRoute/pull/11622)) — thanks @Neuron-Mr-White +- **feat(providers):** add 1min.ai provider ([#11631](https://github.com/diegosouzapw/OmniRoute/pull/11631)) — thanks @zero-executioner +- **feat(dashboard):** collapsible JSON tree viewer for request/response payloads ([#11703](https://github.com/diegosouzapw/OmniRoute/pull/11703)) — thanks @hartmark +- **feat(routing):** add LiquidAI LFM2.5-2.6B free tier via OpenRouter ([#11752](https://github.com/diegosouzapw/OmniRoute/pull/11752)) — thanks @brick30llc-ctrl +- **feat(pricing):** add GLM-5.3-Flash pricing, model specs, and catalog registration ([#11830](https://github.com/diegosouzapw/OmniRoute/pull/11830)) — thanks @AndrianBalanescu +- **feat(routing):** add relayMode for schema-locked context handoffs ([#11839](https://github.com/diegosouzapw/OmniRoute/pull/11839)) — thanks @raheemuddin786 +- **feat(config):** add support for runtime system prompt configuration and hot-reloading ([#11841](https://github.com/diegosouzapw/OmniRoute/pull/11841)) — thanks @raheemuddin786 +- **feat(routing):** expand connection-aware quota prefilter across combo strategies (#11682) ([#11850](https://github.com/diegosouzapw/OmniRoute/pull/11850)) — thanks @HouMinXi +- **feat(models):** reactive discovery sync after Antigravity model-not-found 404 ([#11888](https://github.com/diegosouzapw/OmniRoute/pull/11888)) — thanks @MumuTW +- **feat(models):** auto-aliases and token limits from synced Antigravity discovery ([#11889](https://github.com/diegosouzapw/OmniRoute/pull/11889)) — thanks @MumuTW +- **feat(settings):** raise sticky round-robin limit caps to 1000 ([#12015](https://github.com/diegosouzapw/OmniRoute/pull/12015)) — thanks @adivekar-utexas +- **feat(resilience):** operator-configurable global credential health check interval ([#12043](https://github.com/diegosouzapw/OmniRoute/pull/12043)) — thanks @adivekar-utexas +- **feat(quota):** use official OpenCode Go usage API ([#12124](https://github.com/diegosouzapw/OmniRoute/pull/12124)) — thanks @ddarkr +- **feat(resilience):** default the credential health check sweep to 60 minutes ([#12138](https://github.com/diegosouzapw/OmniRoute/pull/12138)) — thanks @adivekar-utexas +- **feat(quality):** new-code mode for the complexity and dead-code ratchets (Clean as You Code) ([#12142](https://github.com/diegosouzapw/OmniRoute/pull/12142)) +- **feat(dashboard):** orchestration canvas — unified model + snapshot hook (part 1/2) ([#12156](https://github.com/diegosouzapw/OmniRoute/pull/12156)) +- **feat(usage):** add Kilo Code balance and Kilo Pass quotas ([#12178](https://github.com/diegosouzapw/OmniRoute/pull/12178)) — thanks @killer30001000 +- **feat(providers):** refresh vendored ChatGPT Web connector to v4.0.7 ([#12181](https://github.com/diegosouzapw/OmniRoute/pull/12181)) — thanks @backryun +- **feat(providers):** manual "Clear cooldown" action in the cooling panel ([#12224](https://github.com/diegosouzapw/OmniRoute/pull/12224)) — thanks @patrykkopycinski +- **feat(providers):** restore ChatGPT Web via clean-room browser transport ([#12239](https://github.com/diegosouzapw/OmniRoute/pull/12239)) — thanks @backryun +- **feat(sse):** wire the PROVIDER_PROFILES window gate into the global provider cooldown ([#12247](https://github.com/diegosouzapw/OmniRoute/pull/12247)) +- **feat(providers):** modernize CLOVA Studio chat and embeddings ([#12277](https://github.com/diegosouzapw/OmniRoute/pull/12277)) — thanks @backryun +- **feat(browser):** adopt Obscura as primary headless browser engine with Chromium fallback ([#12286](https://github.com/diegosouzapw/OmniRoute/pull/12286)) — thanks @opensource-elearning +- **feat(sse):** full language parity for output styles (es/de/fr/it/ru/zh + autoDetect) ([#12289](https://github.com/diegosouzapw/OmniRoute/pull/12289)) +- **feat(quality):** complete test:scoped — --full map rebuild, stdin selection, CI loader parity (#8084 D1) ([#12353](https://github.com/diegosouzapw/OmniRoute/pull/12353)) +- **feat(providers):** refresh Fable, Cursor, and Devin catalogs ([#12367](https://github.com/diegosouzapw/OmniRoute/pull/12367)) — thanks @backryun +- **feat(video):** redact transcript text from logs and durable memory (#12150 P1) ([#12427](https://github.com/diegosouzapw/OmniRoute/pull/12427)) +- **feat(dashboard):** parent-link, genuine-continuation badge, and modal perf fixes ([#12448](https://github.com/diegosouzapw/OmniRoute/pull/12448)) — thanks @hartmark +- **feat(dashboard):** adaptive context-budget dial on compression panel ([#12488](https://github.com/diegosouzapw/OmniRoute/pull/12488)) — thanks @HouMinXi +- **feat(providers):** refresh Z.ai Web models and browser transport ([#12524](https://github.com/diegosouzapw/OmniRoute/pull/12524)) — thanks @backryun +- **feat(video):** redact raw client-snapshot transcript fields in the detailed log (#12150 P2) ([#12528](https://github.com/diegosouzapw/OmniRoute/pull/12528)) +- **feat(providers):** refresh NVIDIA hosted models ([#12538](https://github.com/diegosouzapw/OmniRoute/pull/12538)) — thanks @backryun +- **feat(quota):** Moonshot Open Platform balance and TPD lock for custom nodes ([#12590](https://github.com/diegosouzapw/OmniRoute/pull/12590)) — thanks @HouMinXi +- **feat(video):** redact transcript in the in-memory pending-request snapshot (#12430 item 6) ([#12596](https://github.com/diegosouzapw/OmniRoute/pull/12596)) +- **feat(dashboard):** link a log entry's Conversation Context to its owning conversation ([#12646](https://github.com/diegosouzapw/OmniRoute/pull/12646)) — thanks @hartmark +- **feat(catalog):** eligibility-gated free-tier bucket ([#12669](https://github.com/diegosouzapw/OmniRoute/pull/12669)) +- **feat(db):** fail-closed previous_response_id continuation for redacted video turns (#12150 P2b) ([#12707](https://github.com/diegosouzapw/OmniRoute/pull/12707)) +- **feat(gamification): pay the documented `streak_bonus` and `badge_unlock` XP rewards.** `XP_REWARDS` listed both rewards but the award pipeline never paid them: the private reward table in `events.ts` omitted them, `updateStreak()` did not report when a streak extended, and badge unlocks carried no XP. Every request that extends a daily streak now pays `streak_bonus × streak length` once per UTC day (guarded by a same-day `xp_audit_log` check), and every badge unlocked through the pipeline pays `badge_unlock` once per badge (guarded by the `user_badges` primary key; `unlockBadge()` now reports whether it inserted). Bonus XP flows through the same `addXp` + level sync + global/weekly/monthly leaderboard path as action XP, so level-ups and rankings include it. The Radar supporter recognition unlock stays XP-free. (#12522 — thanks @pacocartones) +- **feat(providers):** advertise a `free-tier` capability in the provider plugin manifest for every provider with documented free models, so sidecars and dashboards can filter free-capable providers without reading the quota catalog ([#12786](https://github.com/diegosouzapw/OmniRoute/pull/12786)) — thanks @maxmad64bis +- **feat(providers):** Added EURouter as an OpenAI-compatible API-key gateway (`https://api.eurouter.ai/v1`), with live model discovery via `passthroughModels`. Its copy states that models are served by third-party upstreams listed per model, so an EU-based router is not read as EU data residency for inference. ([#13025](https://github.com/diegosouzapw/OmniRoute/pull/13025)) — thanks @ntdatt812 +- **feat(providers):** Added GreenPT as an OpenAI-compatible API-key provider (`https://api.greenpt.ai/v1`), with live model discovery via `passthroughModels`. No free-inference badge: the published docs describe a free API subscription billed per token, not a free tier. ([#13024](https://github.com/diegosouzapw/OmniRoute/pull/13024)) — thanks @ntdatt812 +- **feat(i18n):** 9 new locales — Greek (`el`), Croatian (`hr`), Serbian (`sr`), Lithuanian (`lt`), Estonian (`et`), Latvian (`lv`), Slovenian (`sl`), Maltese (`mt`), Irish (`ga`) — across the dashboard, docs mirrors, CLI, README and the site; OmniRoute now ships every official EU language (51 locales). (#13044) +- **feat(i18n):** 8 new locales — Kannada (`kn`), Malayalam (`ml`), Odia (`or`), Punjabi (`pa`), Nepali (`ne`), Sinhala (`si`), Burmese (`my`), Khmer (`km`) — across the dashboard, docs mirrors, CLI, README and the site (59 locales). The translator now restores the ICU literal escape around angle placeholders and splits oversized docs sections before translating. (#0000) ([#13660](https://github.com/diegosouzapw/OmniRoute/pull/13660)) +- feat(providers): **list Agnes 3.0 Flash as the current free chat model, drop retired 1.5 Flash, add Image 2.0/2.5 Flash plus Video 2.5/2.5 Flash, and discover the live `/v1/models` catalog (including the CN host `api.agnes-ai.cn`).** `agnes-1.5-flash` now forwards to `agnes-3.0-flash`. Video 2.5 polls `GET /v1/videos/{id}` (not the V2.0 `/agnesapi` contract). Live `/v1/models` (2026-09-09) no longer serves 1.5; the wiki marks it deprecated. 3.0 Flash is 512K context / 65,536 max output, same window as 2.5. CN-region keys use the existing per-connection base-URL field, default stays `apihub.agnes-ai.com`. ([#13120](https://github.com/diegosouzapw/OmniRoute/pull/13120)) — thanks @HouMinXi +- **feat(sse):** Codex and OpenAI catalogs list GPT-6 Astra with effort aliases (`-low` through `-ultra`); Codex CLI identity pins `@openai/codex@0.153.4` in lockstep with the image ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **feat(dashboard):** the orchestration History tab gained a "Compare runs" mode — toggling it turns each grid cell into a 2-item selection queue (a 3rd click drops the oldest pick), and picking two cells opens a side-by-side comparison panel instead of the usual detail drawer. The panel fetches both runs' detail the same way the drawer does (falling back to persisted history once a run leaves the live TTL window) and shows, per side: identity/source/state, start time, a signed `right - left` delta for duration/cost/event count, the event timeline aligned by index, and any memory hits. One side's fetch failing never blocks the other, and a delta is only ever computed when both sides have a finite value — otherwise it renders "—", never `NaN`. Comparing two runs from different sources or skills still works; a banner marks the deltas as informational rather than hiding them, since the two runs aren't a strict apples-to-apples pair. ([#12677](https://github.com/diegosouzapw/OmniRoute/pull/12677)) +- **feat(dashboard):** badge a conversation that never reached a clean stop ([#12717](https://github.com/diegosouzapw/OmniRoute/pull/12717)) — thanks @hartmark +- **feat(release):** reconcile-changelog tool + version-anchored fragment aggregation ([#12987](https://github.com/diegosouzapw/OmniRoute/pull/12987)) +- **feat(mcp):** carry context_length through MCP list_models_catalog projection (#12776) ([#13406](https://github.com/diegosouzapw/OmniRoute/pull/13406)) — thanks @KooshaPari +- **feat(cli):** make startup readiness budget configurable (#13369) ([#13433](https://github.com/diegosouzapw/OmniRoute/pull/13433)) — thanks @KooshaPari + ### 🐛 Bug Fixes +- **security(streaming):** sanitize generic mid-stream error messages before emitting OpenAI, + Responses, or Claude SSE failure frames and before diagnostic logging, while preserving raw + failures for internal classification and keeping client disconnects out of provider failure state. ([#12457](https://github.com/diegosouzapw/OmniRoute/pull/12457)) +- **fix(claude):** `blockExtraUsage: false` no longer lets 5h quota preflight skip the account; extra usage is billed after the session bar is gone, so the request must reach Anthropic ([#12803](https://github.com/diegosouzapw/OmniRoute/pull/12803)) — thanks @HouMinXi +- Sanitize HuggingChat conversation-creation and message-send transport failures before they reach client error bodies or provider logs. ([#12467](https://github.com/diegosouzapw/OmniRoute/pull/12467)) +- **fix(dashboard):** Keep local and theme-aware provider SVG icons at a definite layout size so Chromium does not collapse them to 0×0 after the v3.8.50 image-rendering change ([#12054](https://github.com/diegosouzapw/OmniRoute/pull/12054)) — thanks @ponkcore +- **fix(security):** Sanitize provider and runtime failures before public API, SSE and MCP responses and before persistent request, proxy and usage logs, preventing credentials, stack traces and host filesystem paths from crossing those boundaries while preserving stable error codes and useful diagnostics. ([#12506](https://github.com/diegosouzapw/OmniRoute/pull/12506)) +- **fix(cli):** `omniroute tunnel create` no longer crashes with `Cannot read properties of undefined (reading optsWithGlobals)` — removed the duplicate positional argument that caused Commander.js to misalign the action callback parameters ([#12295](https://github.com/diegosouzapw/OmniRoute/issues/12295)) ([#12368](https://github.com/diegosouzapw/OmniRoute/pull/12368)) — thanks @gonisulaimann +- Stop advertising Gemini Live-only models as supported audio endpoints until OmniRoute proxies the bidirectional Live protocol. ([#11710](https://github.com/diegosouzapw/OmniRoute/pull/11710)) — thanks @RaviTharuma +- **fix(kie):** reroute `flux/kontext` off the KIE Market `createTask` flow — it is catalogued with `isMarket: true` but has no Market catalog page, so KIE rejected it with "model name not supported"; it now hits the dedicated `POST /api/v1/flux/kontext/generate` / `GET /api/v1/flux/kontext/record-info` endpoints instead (#11296). ([#11985](https://github.com/diegosouzapw/OmniRoute/pull/11985)) — thanks @hartmark +- **fix(kie):** correct 12 more KIE Market catalog ids that were sent to `createTask` unchanged but diverge from KIE's documented upstream `model` values — GPT Image 2 T2I/I2I (drops the `gpt/` prefix), GPT Image 1.5 T2I/I2I (`gpt-image/` namespace), Seedream 5.0 Lite T2I/I2I (drops the `.0`), all 4 Flux 2 variants (`flux-2/` namespace, generic variant renamed `flex`), and Wan 2.7 Image / Image Pro (dash instead of dot) — each verified individually against the literal example request published on docs.kie.ai. `#11326`'s "everything else already matches" claim was wrong a second time (#11296); `z-image/4.0-*`/`z-image/4.5-*` and `flux/kontext` remain open, documented as unresolved in `KIE_MARKET_UPSTREAM_MODEL_IDS`'s comment pending further verification. ([#11667](https://github.com/diegosouzapw/OmniRoute/pull/11667)) +- **fix(dashboard):** `useApiKeySave.handleSaveApiKey` no longer forces a full upstream `/models` catalog sync on every non-curated provider connection save — callers can now pass `skipModelSync: true` to opt out, so a workflow that only wants to add one manual model no longer floods the provider's available-models list with hundreds/thousands of synced entries. The flag is a client-side intent signal only and is stripped before the connection payload is POSTed to `/api/providers`; default behavior (full sync on save) is unchanged when the flag is omitted (#11324) ([#11664](https://github.com/diegosouzapw/OmniRoute/pull/11664)) +- **Provider connections:** keep `tokenExpiresAt` when a connection is created. The create-path allowlist omitted it, so every insert stored NULL and the dashboard token badge could read a fresh connection as expired until its first background refresh ([#11368](https://github.com/diegosouzapw/OmniRoute/pull/11368)). — thanks @ntdatt812 +- **fix(combos):** the combo builder's precision-select, global-model-search, and manual-entry flows now serialize a model step's `model` string using the provider's already-computed routing-alias prefix (e.g. `oc/`) instead of rebuilding it from the raw canonical `providerId`, fixing the no-auth "OpenCode Free" provider (`opencode`) being routed to the unrelated paid "OpenCode Zen" provider (`opencode-zen`) because `opencode` doubles as a manual routing-prefix override ([#11433](https://github.com/diegosouzapw/OmniRoute/issues/11433)). ([#11670](https://github.com/diegosouzapw/OmniRoute/pull/11670)) +- **fix(radar):** the catalog feed cache now keeps `generatedAt`, the date the feed's data was built, next to `fetchedAt`, the date this install downloaded it (#11435). The feed schema requires that date and the sync path validates it, but the cache dropped it — so a feed fetched minutes ago and one carrying weeks-old figures looked identical to everything downstream, including the dashboard's "Last fetched" line. `getRadarCatalog().meta` and `GET /api/radar/status` now report both dates, the latter as its own field rather than folded into `version` — and omitted entirely for the offers and intel caches, which keep no build date, where a `null` would read as "unknown" rather than "never stored". The dashboard still shows only the fetch time; surfacing the build date there needs a new translated label and is left to a follow-up. Rows cached before migration 163 read back as `null`: unknown stays unknown instead of borrowing the fetch time. The referrals cache has persisted the same date since migration 142. — thanks @maxmad64bis +- **fix(cli):** postinstall no longer fills `JWT_SECRET` and `API_KEY_SECRET` in the installed package's `.env` (#11436). `.env.example` ships both blank on purpose: the server restores them from its durable store, or generates and persists them there, in `ensureSecrets()`. Pre-filling them defeated that — the file lives inside the package directory, so `npm i -g` replaced it and postinstall wrote _different_ values, while `ensureSecrets()` (which only acts on an empty variable) never got to restore the real ones. Both secrets rotated silently on every update, invalidating dashboard sessions and API-key CRCs. `STORAGE_ENCRYPTION_KEY` left the same list for the same reason in #1622; its comment pointed at a function that no longer exists and now names the real provisioning path. — thanks @maxmad64bis +- **fix(cli):** the CLI now says when a loaded `.env` lives inside the installed package directory (#11437). It already announces every env file it reads, without distinguishing the ones that survive an update from the one that does not: `npm i -g` replaces the package directory wholesale, so values set there are gone at the next update, silently. The warning names the durable path to move them to, and fires only when that file actually supplied a value — a file entirely shadowed by a durable one supplied nothing. A development checkout stays silent: there the same path is stable and documented in `SETUP_GUIDE.md`. — thanks @maxmad64bis +- **fix(free-models):** the shared `isFreeModel()` predicate no longer reports catalog entries marked `freeType: "discontinued"` as free, so `hidePaidModels` can't route to Pollinations' seven premium models that now require a paid key ([#11441](https://github.com/diegosouzapw/OmniRoute/pull/11441)) — thanks @maxmad64bis +- fix(ui): let AnySearch use the normal provider-icon fallback when LobeHub has no matching icon (#11449) +- Fixed the v3.8.50 Costs and Analytics dashboards so flat-rate Claude Code usage can be shown as an explicitly requested token-price estimate without changing default billed-cost semantics. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed archived usage retention so each request is priced individually instead of pricing a day's summed tokens once, which understated archived cost whenever a day mixed cache-heavy and ordinary requests. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed the Costs dashboard so it discloses when displayed figures include flat-rate token-price estimates instead of labelling them as billed spend, using the flag the analytics API already returns; the month-end projection and the CSV/JSON exports carry the same marker, and billed-cost mode is unchanged. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- **fix(combo):** attach the same combo-diagnostics trace (`poolSize`/`attemptOrder`/`excluded`/`terminalReason`, plus `x-omniroute-combo-*` headers) to the round-robin strategy's and the nested pipeline/fusion runtime-unit loop's "Maximum combo retry limit reached" 503 that the priority-strategy path already attaches for the identical terminal condition — previously those two paths returned a bare, contextless 503 ([#11462](https://github.com/diegosouzapw/OmniRoute/issues/11462)). ([#11665](https://github.com/diegosouzapw/OmniRoute/pull/11665)) +- **fix(bun):** use native `bun:sqlite` in `bootstrap-env` and `sync-env` to avoid loading `better-sqlite3` N-API addon during Bun startup ([#11468](https://github.com/diegosouzapw/OmniRoute/pull/11468)) — thanks @TheDemonTuan +- **fix(docker-bun):** make `Dockerfile.bun` install reliable with pre-install script helpers and native `bun:sqlite` smoke check ([#11470](https://github.com/diegosouzapw/OmniRoute/pull/11470)) — thanks @TheDemonTuan +- **fix(docker-bun):** remove every vendored `better-sqlite3` native addon from the Bun runtime image so startup cannot abort before the `bun:sqlite` fallback ([#11482](https://github.com/diegosouzapw/OmniRoute/pull/11482)) — thanks @TheDemonTuan +- **fix(autoCombo):** effort/alias model variants (`gpt-5.6-sol-xhigh`, `gpt-5.6`, cursor's `claude-4.6-opus-high`) inherit their base model's task fitness instead of falling to the wildcard 0.5 ([#11492](https://github.com/diegosouzapw/OmniRoute/pull/11492)) — thanks @MumuTW +- fix(db): rate-limit repeated Arena ELO leaderboard fetch-failure warnings instead of logging one per sync attempt (#11500) ([#11989](https://github.com/diegosouzapw/OmniRoute/pull/11989)) — thanks @hartmark +- **fix(db):** dedupe the raw `[Encryption] Decryption failed...` log line emitted by the lazy-decrypt views (`createLazyRowProxy`/`createLazyConnectionView`), which power `getProviderConnections()` and were re-triggering that line on every CredentialHealth/model-sync cycle for the same corrupt or stale-key credential — a fresh Proxy over a fresh row on every cycle meant the per-proxy memoization never suppressed it, unlike the dedup `decryptConnectionFields()` already had since [#9927](https://github.com/diegosouzapw/OmniRoute/issues/9927). Now shares that dedupe tracking so the line logs at most once per credential ([#11500](https://github.com/diegosouzapw/OmniRoute/issues/11500)). ([#11678](https://github.com/diegosouzapw/OmniRoute/pull/11678)) +- **fix(intelligence):** Arena ELO sync stores a synthesized base-model row for every effort/harness variant (`claude-opus-5-max` → `claude-opus-5`, `gpt-5.6-sol-xhigh (codex-harness)` → `gpt-5.6-sol`), so bare model requests reach the synced intelligence layer instead of the static fallback table; the hand-maintained `MODEL_ALIAS_MAP` — which collapsed generations (`gpt-5.5` → `gpt-5`) — is gone ([#11506](https://github.com/diegosouzapw/OmniRoute/pull/11506)) — thanks @MumuTW +- **fix(autoCombo,sse):** catalog hygiene — the static task-fitness table no longer ranks retired models above live ones (dead rows dropped, patterns matched on segment boundaries), the 7 deprecation aliases that forwarded to retired or non-existent models now point at the vendors' published replacements, an alias is no longer rewritten when the serving provider offers the id as-is, and a new `check:model-lifecycle` gate diffs both tables against a vendor lifecycle snapshot ([#11507](https://github.com/diegosouzapw/OmniRoute/pull/11507)) — thanks @MumuTW +- **fix(sse):** stop dropping resolved `thoughtSignature` values on parallel (multi tool-call) turns sent to Gemini 3.x — the claude→gemini and openai→gemini translators previously kept the signature only on the *first* function call of a message, causing Gemini to reject subsequent calls in the same turn with HTTP 400 "Function call is missing a thought_signature"; each function call now keeps its own resolved signature ([#11510](https://github.com/diegosouzapw/OmniRoute/issues/11510)). ([#11669](https://github.com/diegosouzapw/OmniRoute/pull/11669)) +- Treat Docker publishing from a non-default release branch as an expected skip instead of a failed workflow, while preserving the guard that prevents it from overwriting the `next` channel. ([#11527](https://github.com/diegosouzapw/OmniRoute/pull/11527)) — thanks @pacocartones +- fix(search): prefer credentialed providers over duckduckgo-free fallback (#11524) — the fallback-only loop ran before the credentialed-providers loop in `executeWebSearch`, making configured providers unreachable when `duckduckgo-free` was available. ([#11565](https://github.com/diegosouzapw/OmniRoute/pull/11565)) — thanks @vsd2807 +- **fix(sse):** cap the upstream headers-wait phase for STREAMING requests to a client-realistic ceiling (110s, under Codex's own ~120s hard client-abort window) instead of the flat 10-minute `FETCH_TIMEOUT_MS` default — that default was 5x longer than the body-phase readiness watchdog's own adaptive bound, so a request whose upstream never returned any response at all (not even headers, e.g. a stalled NVIDIA target behind a tool-heavy Responses→Chat translation) kept the client connection alive on keepalives only, guaranteeing the client's own patience ran out first with an opaque 499 instead of OmniRoute detecting and failing the stall fast. Non-streaming requests are unaffected — they keep the existing flat default (`open-sse/utils/fetchStartTimeoutPolicy.ts`) (#11526) ([#11668](https://github.com/diegosouzapw/OmniRoute/pull/11668)) +- **fix(ci):** protocol-clients E2E harness boots the peer-stamped custom server (`run-next.mjs dev`) instead of the bare `next` CLI, so `LOCAL_ONLY` locality resolves from the real TCP peer stamp and `/api/mcp/audit` answers 200 (open bootstrap preserved via an `OMNIROUTE_E2E_BOOTSTRAP_MODE=open` credential clear in `run-next.mjs`, pinned to loopback `HOST`) instead of a deterministic 403 (#11535) ([#11549](https://github.com/diegosouzapw/OmniRoute/pull/11549)) — thanks @jonlwheat2-gif +- **fix(docs):** the Auto-Combo engine is described with the number of factors it actually declares. Four different counts were in circulation — 6, 9, 13 and 14 across `AGENTS.md`, four guides and six code comments — while `DEFAULT_WEIGHTS` declares 15. `check:docs-counts` now reads that number from the source, so adding a factor without updating the prose fails CI ([#11545](https://github.com/diegosouzapw/OmniRoute/pull/11545)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **fix(quota-share):** in-flight leases are per request, so an aborted request's slot ages out instead of being kept alive by later traffic on the same connection ([#11547](https://github.com/diegosouzapw/OmniRoute/pull/11547)) — thanks @abhisheksharma2411 +- The free-tier summary route now serves the refreshed Radar catalog when the feed is active — the same numbers the dashboard shows — and states which catalog answered plus its real build date, instead of always reporting release-frozen figures with a stale curation date. The supporter-key live feed stays reserved to authenticated callers of the instance. ([#11550](https://github.com/diegosouzapw/OmniRoute/pull/11550)) — thanks @maxmad64bis +- **sse:** fix LiveWS/embed-WS servers crashing at startup under the Node/tsx runtime — `liveServer.ts`, `embedWsProxy.ts` and `apiBridgeServer.ts` imported `@/shared/utils/httpClientAbortGuard` without the `.mjs` extension, so the client-abort crash guard added by [#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556) was unreachable and every dependent test failed with `ERR_MODULE_NOT_FOUND` ([#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556)). ([#11671](https://github.com/diegosouzapw/OmniRoute/pull/11671)) — thanks @jonlwheat2-gif / @yxyxy +- fix(chat): shed any of the matched provider-node's routing identifiers (public prefix or internal id) before model lookup, so composites like `//` no longer reach upstream verbatim (#11557) — thanks @maxmad64bis +- **fix(cache):** a semantic-cache entry now expires on its TTL instead of surviving until the next UTC midnight, and `dbEntries` stops counting expired rows ([#11573](https://github.com/diegosouzapw/OmniRoute/pull/11573)) — thanks @ntdatt812 +- **fix(api):** `/v1/models` schedules its stale-while-revalidate rebuild through Next's `after()` again, so a stale catalog reaches the client before the rebuild blocks the event loop ([#11574](https://github.com/diegosouzapw/OmniRoute/pull/11574)) — thanks @ntdatt812 +- **fix(check):** the `known-symbols` executor conformance gate awaits the now-async `getExecutor()`, so it stops reporting all 142 registered aliases as dead symbols and can detect a lazy import that fails ([#11582](https://github.com/diegosouzapw/OmniRoute/pull/11582)) — thanks @ntdatt812 +- **fix(pack):** register `bin/cli/utils/volatileEnvPath.mjs` in `PACK_ARTIFACT_REQUIRED_PATHS`, so the prune can no longer silently drop a module the published CLI imports on every boot ([#11588](https://github.com/diegosouzapw/OmniRoute/pull/11588)) — thanks @ntdatt812 +- **i18n(vi):** translate the five reliability-column strings on the free-provider rankings page, which rendered as `__MISSING__:` markers in the Vietnamese UI ([#11589](https://github.com/diegosouzapw/OmniRoute/pull/11589)) — thanks @ntdatt812 +- **resilience:** restore the expired-connection retry-budget probe in the token-health sweep — the `!isGitHubAccessTokenOnlyConnection` carve-out reintroduced by #11608 contradicted the boundary pinned by #11592, so a GitHub connection parked at `expired` with retry budget remaining was never probed and could never self-heal ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)). ([#11672](https://github.com/diegosouzapw/OmniRoute/pull/11672)) — thanks @jonlwheat2-gif / @ntdatt812 +- **fix(dashboard):** Provider Stats now displays combo success rates on their native 0–100 percentage scale instead of multiplying them twice ([#11595](https://github.com/diegosouzapw/OmniRoute/pull/11595)) — thanks @pacocartones +- **fix(providers):** route DuckDuckGo Web Chat through the verified `duck.ai` endpoint, refresh its free model catalog, and avoid forwarding unsolved anti-abuse challenges ([#11597](https://github.com/diegosouzapw/OmniRoute/pull/11597)) — thanks @solstxce +- **fix(ui):** Console log Refresh and Copy controls now expose localized accessible names, keep copy actions visible on keyboard focus, and announce copy completion safely ([#11599](https://github.com/diegosouzapw/OmniRoute/pull/11599)) — thanks @pacocartones +- Dropped the stale-`.eslintcache` `restore-keys` fallback from both "Restore ESLint file cache" steps in `ci.yml`, so the blocking `Lint` job can no longer be served per-file verdicts computed under a different lint config, suppressions file or lockfile. `quality.yml` had already dropped it in #11963; `ci.yml` — the workflow that actually gates PRs — had not (#11600). ([#11996](https://github.com/diegosouzapw/OmniRoute/pull/11996)) +- fix(ci): pass `--pass-on-unpruned-suppressions` in `run-eslint-json.mjs` so the CI Lint job no longer fails when a suppression is merely orphaned by a genuine fix, mirroring the identical fix already in `validate-release-green.mjs` (#11600) ([#11983](https://github.com/diegosouzapw/OmniRoute/pull/11983)) — thanks @hartmark +- fix(handoff): enforce provider allowlist for universal handoff (#11602) — universal handoff now skips summarization when the selected summary model's provider is not included in the configured provider allowlist. ([#11619](https://github.com/diegosouzapw/OmniRoute/pull/11619)) — thanks @vsd2807 +- **fix(dashboard):** Show a stable error state when Search Analytics returns an HTTP error or malformed data ([#11603](https://github.com/diegosouzapw/OmniRoute/pull/11603)) — thanks @pacocartones +- **test(gamification):** pin the aggregate profile level to the XP-derived semantics of #11604 — `getAggregateXp()` now derives `currentLevel` from the summed XP (`calculateLevel(sum)`), not `MAX(stored current_level)`, and the #3484 fixture levels are aligned with the XP curve ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)). ([#11676](https://github.com/diegosouzapw/OmniRoute/pull/11676)) — thanks @jonlwheat2-gif / @pacocartones +- **fix(dashboard):** Keep the Profile level and progress aligned with aggregate XP, including bounded handling for invalid totals ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)) — thanks @pacocartones +- **fix(dashboard):** Prevent locked hidden badges from revealing their icon or opening private badge details before they are earned ([#11605](https://github.com/diegosouzapw/OmniRoute/pull/11605)) — thanks @pacocartones +- **fix(dashboard):** Restore keyboard focus after shared modals close and cancel delayed autofocus during cleanup ([#11607](https://github.com/diegosouzapw/OmniRoute/pull/11607)) — thanks @pacocartones +- **fix(dashboard):** Enable Enter and Space activation for clickable data-table rows without hijacking nested controls ([#11610](https://github.com/diegosouzapw/OmniRoute/pull/11610)) — thanks @pacocartones +- **fix(autoCombo,sse):** vendor-retired catalog ids are dropped from the auto-combo candidate pool and no longer win on leftover `arena_elo` / `user_override` rows; `getModelLifecycleDecision` consults `model-lifecycle.json` (prefix-stripped) so aggregator traffic is not `untracked` for ids the snapshot already knows ([#11625](https://github.com/diegosouzapw/OmniRoute/issues/11625)) ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)) — thanks @MumuTW +- **test(chatcore):** move Codex/Claude combo fixtures off models the lifecycle guard now rejects — `gpt-5.1-codex`/`gpt-5-codex` are vendor-retired (snapshot, #11626) and `claude-3-5-sonnet-20241022` is shut down, so native-passthrough and combo-fallback tests switched to `gpt-5.6-sol` and `claude-sonnet-4.6` ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)). ([#11675](https://github.com/diegosouzapw/OmniRoute/pull/11675)) — thanks @jonlwheat2-gif / @MumuTW +- fix(opencode-plugin): stop a free-tier budget that rounds up across a magnitude from rendering as `1000K`/`1000M` in the model picker — `fmtTokens` chose its unit from the raw token count and then rounded with `toFixed(1)`, so 999,950–999,999 printed as `1000K` rather than `1M` and just under a billion printed as `1000M` rather than `1B` ([#11684](https://github.com/diegosouzapw/OmniRoute/pull/11684)) — thanks @abhisheksharma2411 +- Cap prose-derived Antigravity quota resets at the configured model cooldown maximum while preserving authoritative Retry-After headers and Google RetryInfo hints. (#11695) Thanks @pacocartones. ([#11823](https://github.com/diegosouzapw/OmniRoute/pull/11823)) +- Compare `_rebaseline_*` notes structurally in the ratchet-bank verifier so an object-valued note no longer reports itself as rewritten on an unmodified tree — the nightly `bank-ratchet-shrinks` lane had been aborting on every run since 2026-08-11, leaving 41,672 lines of earned file-size shrinks unbanked (#11702). ([#11892](https://github.com/diegosouzapw/OmniRoute/pull/11892)) — thanks @MumuTW +- **fix(build):** `prepublish.ts` bundles the ChatGPT Web (Codex) MCP bridge through `runBuildTool()` instead of spawning `npx.cmd` raw, fixing the build crash on Node ≥ 20/Windows where `.cmd` shims cannot be spawned without a shell (EINVAL) ([#11704](https://github.com/diegosouzapw/OmniRoute/issues/11704)) ([#11706](https://github.com/diegosouzapw/OmniRoute/pull/11706)) — thanks @jonlwheat2-gif +- fix(codex): keep `parallel_tool_calls:false` on the translated Codex Responses Lite path (#11707) ([#11984](https://github.com/diegosouzapw/OmniRoute/pull/11984)) — thanks @hartmark +- **fix(tests):** the new Gemini Web Images retirement test (#11708) called the now-async `getExecutor()` synchronously, throwing `TypeError: getExecutor(...).getProvider is not a function` instead of asserting Gemini Web chat stays available. Awaited to match the R0.3 executor-registry refactor (#11220). ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(search):** Allow REST and MCP web-fetch requests to select the advertised `anysearch-search` provider explicitly ([#11716](https://github.com/diegosouzapw/OmniRoute/pull/11716)) — thanks @pacocartones +- **fix(providers):** Keep overnight peak-hour protection active after midnight according to the window's configured start day ([#11718](https://github.com/diegosouzapw/OmniRoute/pull/11718)) — thanks @pacocartones +- **fix(providers):** correct all five g4f.space gateway descriptions to document proof-of-work credits or member API keys, variable limits, and the remote prompt boundary instead of promising free keyless access at five requests per minute ([#11738](https://github.com/diegosouzapw/OmniRoute/pull/11738)). +- **fix(db):** `model_capabilities` is created by a migration instead of lazily on the first models.dev sync, so a clean install and an upgraded install converge on the same schema regardless of which features have run ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(ci):** `check:install-upgrade` now fails on an `npm` install truncated by ENOSPC (npm reports it as a warning and still exits 0), authenticates its health probe so the version assertion works against the hardened health payload, frees the clean-install tree before the upgrade phase, and no longer reports a schema divergence computed from a boot that never served ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(routing):** harden session-affinity key derivation against oversized explicit IDs and empty structural-payload collisions, while recognizing Gemini and common text request shapes ([#11746](https://github.com/diegosouzapw/OmniRoute/pull/11746)) — thanks @MumuTW +- **fix(packages/browser-pool):** regenerate `package-lock.json` so the `packages/browser-pool` workspace's locked `playwright`/`@types/node` (and transitives) match its `package.json` specs, fixing cache-only/offline installs (`npm ci --offline`, Nix `buildNpmPackage`) that previously failed with `ENOTCACHED` ([#11747](https://github.com/diegosouzapw/OmniRoute/issues/11747)) — thanks @benjaminkitt ([#11784](https://github.com/diegosouzapw/OmniRoute/pull/11784)) — thanks @geekyNads +- **fix(assets):** Stop distributing 79 provider and CLI logos without verified source/license provenance, keep every provider/tool available through the internal generic icon, and prevent those IDs from falling through to an external icon CDN. ([#11750](https://github.com/diegosouzapw/OmniRoute/pull/11750)) +- **fix(providers):** retire the common `chatgpt-web` / `cgpt-web` provider because its pre-key and proof-of-work provenance could not be cleared ([#11754](https://github.com/diegosouzapw/OmniRoute/pull/11754)) — explicit chat and image requests, slash-prefixed models, persisted aliases, proxy overrides, and combo targets now fail closed with a sanitized HTTP `410` / `PROVIDER_RETIRED` before upstream dispatch. Migration 163 tombstones matching connections and invalidates their active leases without deleting history or widening API-key allowlists. The independent `chatgpt-web-codex` / `cgpt-codex` provider and its MIT-noticed vendored implementation remain available. +- **fix(usage):** quota and usage refresh no longer 409 when an exclusive lease reserves the connection ([#11758](https://github.com/diegosouzapw/OmniRoute/pull/11758)) — thanks @TheDemonTuan +- Keep the embedding registry's vector width and `embedding` type on models when a synced model exists for the same id, so `/v1/models` no longer reports registry-described embedding models widthless or untyped (#11761) — thanks @rahilmavani +- Correct `google/gemini-embedding-001` on the OpenRouter route to 3072 dimensions, the width it returns when `dimensions` is not sent (#11761) — thanks @rahilmavani +- **fix(vision-bridge):** Restrict automatic vision routing and fallbacks to models available in authoritative live provider catalogs, including aliases and effort variants ([#11767](https://github.com/diegosouzapw/OmniRoute/issues/11767)) — thanks @pacocartones ([#11813](https://github.com/diegosouzapw/OmniRoute/pull/11813)) +- **fix(providers):** reclassify Cerebras as a one-time $5 signup credit (payment method required, 30-day validity), not a recurring no-card 1M tokens/day trial ([#11773](https://github.com/diegosouzapw/OmniRoute/issues/11773)) ([#12591](https://github.com/diegosouzapw/OmniRoute/pull/12591)) — thanks @HouMinXi +- fix(cli): stop prepublish from re-rebuilding the already-built ESM-only opencode-plugin dist (#11787) ([#11990](https://github.com/diegosouzapw/OmniRoute/pull/11990)) — thanks @hartmark +- **fix(db):** Fresh installs no longer log a non-fatal `no such table: compression_run_telemetry` warning when retention cleanup runs before the lazily-created telemetry table exists ([#11802](https://github.com/diegosouzapw/OmniRoute/pull/11802)) — thanks @RaviTharuma +- fix(sse): set X-OmniRoute-Selected-Connection-Id on successful combo dispatches so downstream consumers stop falling back to an empty connection id (#11810) ([#11986](https://github.com/diegosouzapw/OmniRoute/pull/11986)) — thanks @hartmark +- **fix(services):** embedded CLIProxyAPI lifecycle checks now use public `/healthz`, while model discovery uses the configured dedicated data-plane API key instead of the management password ([#11811](https://github.com/diegosouzapw/OmniRoute/pull/11811)) — thanks @RaviTharuma +- Fix persisted model steps returning an error in the playground route simulator, and warn when structural steps cannot be simulated. ([#11862](https://github.com/diegosouzapw/OmniRoute/pull/11862)) — thanks @pacocartones +- **fix(providers):** Antigravity's dynamic mitmAlias table no longer routes `gemini-3.7-flash-{high,medium,low}` to a literal tier-suffixed upstream id just because one connected account's own discovery listed it directly — those display ids always resolve through the safe `gemini-3.7-flash-tiered` static alias, so one account's Google-provisioned access no longer 404s every sibling account of the provider ([#11824](https://github.com/diegosouzapw/OmniRoute/issues/11824), [#11651](https://github.com/diegosouzapw/OmniRoute/issues/11651)) ([#11988](https://github.com/diegosouzapw/OmniRoute/pull/11988)) — thanks @hartmark +- **fix(dashboard):** Expose Profile loading, errors, page structure, and XP progress to assistive technologies ([#11838](https://github.com/diegosouzapw/OmniRoute/pull/11838)) — thanks @pacocartones +- **fix(socks):** `Agent.connectTimeout` now bounds both SOCKS handshake and TLS connect, and `family === null` no longer falls back to `fetch-socks` ([#11842](https://github.com/diegosouzapw/OmniRoute/pull/11842)) — thanks @maxmad64bis +- **perf(compression):** OOM mitigations for large payload hashing, memoization, and token estimation ([#11844](https://github.com/diegosouzapw/OmniRoute/pull/11844) — thanks @AndrianBalanescu) +- Excluded Next.js Node File Trace manifests (`*.nft.json`) from the published npm tarball. They are build-time metadata and are never read while serving, but had grown to 668.7 MB — 61% of the package — which pushed the upload past the registry limit and made `npm publish` fail with `413 Payload Too Large`. ([#11864](https://github.com/diegosouzapw/OmniRoute/pull/11864)) +- **fix(cli):** keep the global OmniRoute server `--base-url` separate from provider-node `--endpoint` values in add, update, and validate commands ([#11860](https://github.com/diegosouzapw/OmniRoute/pull/11860)) — thanks @pacocartones +- **fix(config):** Nous Research's `Hermes-4-405B` model now displays as "Hermes 4 405B (Nous Research)" in both the provider registry and the free-model catalog, instead of the mislabelled "Hermes 4 7B" ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#11993](https://github.com/diegosouzapw/OmniRoute/pull/11993)) +- **fix(provider/nous):** inject required user= tag into Nous Research inference requests to resolve upstream 400 "missing tags" error ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#12044](https://github.com/diegosouzapw/OmniRoute/pull/12044)) +- **fix(build):** `npm run build` now fails in one second with a named package and a copy-pasteable fix when npm silently drops an externalised optional native dependency, instead of dying four minutes in with `Module not found: Can't resolve 'better-sqlite3'` ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- **fix(install):** `postinstall` no longer throws `ReferenceError: isAndroid is not defined` — failing the whole `npm install` — when the `better-sqlite3` rebuild fallback times out; the manual-fix guidance is reachable again ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- Fixed the Alibaba free-tier allowlist test that went red on its own once the shipped catalog's `validUntil` (2026-08-27) passed, leaving every PR and `main` with a failing `Unit Tests (1/8)`. The test now builds its own packs with dates it controls, and covers the expired-pack fallback that production has actually been serving. ([#11867](https://github.com/diegosouzapw/OmniRoute/pull/11867)) +- Split the npm registry upload into its own GitHub-hosted job. npm refuses `--provenance` from a self-hosted runner (`422 ... Only "github-hosted" runners are supported`), which blocked the v3.8.50 publish; the heavy verification cannot move to a hosted runner, so it now hands the proven tarball over instead. ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(ui):** the shared `DataTable` loading state no longer reads its decorative ⏳ glyph out to assistive technology, and now carries the same `role="status"` / `aria-live="polite"` / `aria-busy="true"` semantics as `PageLoading` ([#11869](https://github.com/diegosouzapw/OmniRoute/pull/11869)) — thanks @pacocartones +- **fix(api):** Bound the `timeout` accepted by `POST /api/providers/[id]/login` to the same 15s–600s window the Adobe Firefly and Conol browser-login paths already enforce, so a request can no longer pin a headful browser and the single active-login slot with an arbitrarily large poll budget. ([#11871](https://github.com/diegosouzapw/OmniRoute/pull/11871)) — thanks @pacocartones +- **fix(api):** Stop the tunnel and MITM routes from returning raw child-process error text, which disclosed host paths, binary install locations and Tailscale `tskey-*` credentials in a body some of these routes serve to non-loopback callers, and return a real 400 instead of a framework 500 when a tunnel request body fails validation. ([#11872](https://github.com/diegosouzapw/OmniRoute/pull/11872)) — thanks @pacocartones +- **fix(open-sse):** stream TTFT/ITL are sampled from a monotonic clock (`performance.now`) instead of `Date.now`, so an NTP correction or wall-clock jump can no longer inflate a genuine TTFT or produce a negative one that the `ttft >= 0` guard silently drops — protecting the router's OTel/EWMA/`usage_history`/speed-ranking signals ([#11873](https://github.com/diegosouzapw/OmniRoute/pull/11873)) — thanks @pacocartones +- **fix(providers):** xAI `reasoning_effort: "xhigh"` now reaches grok-4.6+ instead of being silently clamped to `"high"` ([#11879](https://github.com/diegosouzapw/OmniRoute/pull/11879)) — thanks @NoxzRCW +- **fix(dashboard):** the providers page no longer crashes into the error boundary when a provider id collides with an `Object.prototype` member (`constructor`, `__proto__`); icon lookups are own-property guarded ([#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)) — thanks @NoxzRCW +- **fix(skills):** injected skill tools declared in shorthand (`{"content": "string"}`) now forward valid JSON Schema, unblocking providers that validate tool schemas strictly such as Zhipu GLM on the Console Go tier ([#11881](https://github.com/diegosouzapw/OmniRoute/pull/11881)) — thanks @NoxzRCW +- **fix(api):** `POST /api/playground/simulate-route` now surfaces `combo-ref` and `provider-wildcard` persisted combo steps with a specific warning (naming the referenced combo, or the unresolved `provider/model` wildcard) instead of folding them into a generic "unsupported step" count; a `provider-wildcard` step is also now included as an unresolved target so the operator can see it is in the route (ported from [#11882](https://github.com/diegosouzapw/OmniRoute/pull/11882) — thanks @NoxzRCW). ([#11930](https://github.com/diegosouzapw/OmniRoute/pull/11930)) +- **fix(translator):** the streaming OpenAI→Claude translator keeps upstream usage, including prompt-cache tokens, when it arrives on a trailing `choices: []` chunk (Fireworks and any upstream using `stream_options.include_usage`) ([#11883](https://github.com/diegosouzapw/OmniRoute/pull/11883)) — thanks @NoxzRCW +- **fix(cli):** `omniroute update --apply` now tells you explicitly whether a running server was detected and, if so, that you must run `omniroute restart` to apply the update — it never restarted anything and previously implied the update was already live once files were installed. The dashboard's npm-mode Update flow (`/api/system/version`) now tries OmniRoute's own PID-file-managed supervisor before falling back to pm2, and reports an honest "restart required" step instead of a silent pm2-only "skipped" that read like a completed update. The server-side latest-version lookup backing the dashboard's update banner also gained `--prefer-online`, closing the same stale-npm-cache class already fixed in the CLI's own copy for #4376 ([#11885](https://github.com/diegosouzapw/OmniRoute/issues/11885)). ([#12005](https://github.com/diegosouzapw/OmniRoute/pull/12005)) +- **fix(resilience):** clear persisted LKGP pins when a target suffers connection/provider exhaustion or is skipped before dispatch due to cooldown/exhaustion/unavailability, preventing subsequent requests from repeatedly prioritizing known-dead providers ([#11911](https://github.com/diegosouzapw/OmniRoute/issues/11911)). ([#12013](https://github.com/diegosouzapw/OmniRoute/pull/12013)) — thanks @HouMinXi +- **fix(sse):** the OpenAI→Claude stream translator now defers the terminal `message_delta`/`message_stop` emission until the real usage block has arrived (or a genuine end-of-stream flush forces it) instead of emitting it immediately on `finish_reason` — previously, when the trailing usage-only chunk (`{"choices":[],"usage":{...}}`) arrived *after* the `finish_reason` chunk (the normal order for Fireworks/vLLM/Together and other `stream_options.include_usage` upstreams), the client-visible `message_delta` still carried stale/zero usage even though `state.usage` was internally corrected too late to matter (ported from [#11915](https://github.com/diegosouzapw/OmniRoute/pull/11915) — thanks @HouMinXi). ([#11933](https://github.com/diegosouzapw/OmniRoute/pull/11933)) +- **fix(sse):** `stripStore()` now forces `store=false` for stateless OpenAI-compatible Responses-API targets (unless the connection has explicitly opted in via `providerSpecificData.openaiStoreEnabled`), instead of only handling the `openai`/`agentrouter` cases — a client-supplied `store` value previously passed through untouched to backends that don't actually persist responses server-side ([#11916](https://github.com/diegosouzapw/OmniRoute/pull/11916) — thanks @HouMinXi). +- **fix(models):** custom provider-node models (synced, custom, and alias-backed) now appear under their configured prefix in the unified catalog when the operator's model-id prefix mode is canonical, instead of being dropped whenever alias-inclusion was otherwise disabled ([#11918](https://github.com/diegosouzapw/OmniRoute/pull/11918) — thanks @HouMinXi). +- **fix(models):** the unified model catalog now suppresses stale static registry models (including effort-tier variants) for any provider whose active connection has an authoritative live synced catalog, not only providers already using exclusive-synced-listing — a connection with `providerUsesAuthoritativeLiveCatalog` previously kept serving both the live-synced models and the stale static rows side by side ([#11919](https://github.com/diegosouzapw/OmniRoute/pull/11919) — thanks @HouMinXi). +- **fix(ui):** `ProviderIcon`'s three remaining unguarded lookups (`PROVIDER_ICON_ALIASES`, `LOCAL_SVG_ALIASES`, `THEMED_SVGS`) now use `Object.hasOwn()` own-property checks — a provider id such as `constructor` or `__proto__` previously resolved through the prototype chain instead of falling through to the unknown-provider CDN fallback (`getLobeProviderIcon()` itself was already guarded by [#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)); `ProviderPageHeader`'s `color` field is also now optional, matching the rest of the component's defensive typing (ported from [#11920](https://github.com/diegosouzapw/OmniRoute/pull/11920) — thanks @HouMinXi). ([#11935](https://github.com/diegosouzapw/OmniRoute/pull/11935)) — thanks @NoxzRCW +- fix(ollama): preserve multi-byte UTF-8 content split across stream chunks in the Ollama NDJSON transform, which previously corrupted CJK/emoji into U+FFFD (#11921) — thanks @pacocartones +- **fix(providers):** OrcaRouter chat requests now target `/v1/chat/completions` instead of the bare `/v1` API root, fixing the upstream `404 Invalid URL (POST /v1)` ([#11923](https://github.com/diegosouzapw/OmniRoute/pull/11923)). — thanks @echel0nn +- **fix(plugins):** deliver the `onStreamComplete` event to disk-installed plugins. The event shipped in v3.8.50 (#9669) was emitted internally but had no plugin-facing wiring, so no plugin could ever subscribe: the manifest schema silently dropped `hooks.onStreamComplete`, and the loader/manager only knew the seven legacy hooks. `onStreamComplete` is now a declarable manifest hook, wired through the loader and registered by the manager like the other hooks, and its payload carries a `requestId` so consumers can correlate the stream-completion event with the originating request ([#11934](https://github.com/diegosouzapw/OmniRoute/pull/11934)) — thanks @amaleta / @pacocartones +- **fix(db):** the Qdrant embedding-model dropdown now lists local/self-hosted providers (Ollama, LM Studio, vLLM, etc.) — an active connection is treated as "configured" when the provider allows an optional API key, not only when it has a real key or OAuth, so a running local embedding provider is no longer hidden from the picker ([#11949](https://github.com/diegosouzapw/OmniRoute/issues/11949)) ([#11995](https://github.com/diegosouzapw/OmniRoute/pull/11995)) +- **fix(providers):** Vertex AI Anthropic partner-model discovery now calls the Model Garden `v1beta1` publisher list (`/v1beta1/publishers/anthropic/models`, global) and parses its `publisherModels` envelope, so Claude models auto-synced from Vertex populate the active live catalog and route at request time instead of returning `Model '' is not available in the active live catalog` ([#11991](https://github.com/diegosouzapw/OmniRoute/issues/11991)) — thanks @fabioluissilva ([#11998](https://github.com/diegosouzapw/OmniRoute/pull/11998)) +- **fix(cli):** support `--base-url` alongside `--endpoint` in `omniroute nodes add`, `update`, and `validate` subcommands to prevent global `--base-url` shadowing issues ([#11999](https://github.com/diegosouzapw/OmniRoute/issues/11999)). ([#12033](https://github.com/diegosouzapw/OmniRoute/pull/12033)) — thanks @HouMinXi +- **fix(providers):** `cloudflare-ai` no longer refuses image content parts for every Workers AI model ([#12002](https://github.com/diegosouzapw/OmniRoute/pull/12002)) — the plain-string `content` requirement behind #2539 is carried by the _model_ schema, not by the `/ai/v1/chat/completions` endpoint (measured: an all-text part array returns 200 on `@cf/mistralai/mistral-small-3.1-24b-instruct`, `@cf/meta/llama-4-scout-17b-16e-instruct` and `@cf/meta/llama-3.3-70b-instruct-fp8-fast`, and 400 on the text-only `@cf/qwen/qwen2.5-coder-32b-instruct`). `transformRequest()` flattened every array and threw on the first non-text part (#6390), so image input was refused for vision-capable Cloudflare models that accept it. All-text arrays are still flattened — the one shape every model accepts — while an array carrying a non-text part is passed through untouched, so the attachment is still never silently dropped. Regression guards: `tests/unit/cloudflare-ai-image-parts-6390.test.ts`. — thanks @davidlinfr +- **fix(resilience):** decouple the limiter-managed execution backstop from the queue-wait budget — new `requestQueue.executionMaxWaitMs` (env `RATE_LIMIT_EXECUTION_MAX_WAIT_MS`, default 600000 = 10 min) now feeds Bottleneck's post-dispatch `expiration`, while `requestQueue.maxWaitMs` keeps its documented queue-wait semantics. Previously the queue-wait budget doubled as the execution expiration, so legitimate long-running calls on non-incremental gateways (whole generation buffered before the first upstream byte, e.g. Console Go / Command Code tiers serving GLM models) were killed mid-flight at the queue budget with a false 504 `RATE_LIMIT_EXECUTION_TIMEOUT` — the local limiter undercut the provider-aware upstream fetch-start timeouts. The surfaced 504 message now names `requestQueue.executionMaxWaitMs`; the error keeps the #4165 guarantees (disclaims an upstream timeout, preserves the Bottleneck error as `cause`, branded code + trusted provenance, classified request-scoped so combo falls back). A real queue-wait bound (the `Promise.race` around `limiter.schedule()` sketched in #9533) remains future work. (#12025) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **Call logs:** keep the `error` field when an artifact exceeds the storage cap, instead of replacing it with the omission marker. The error is the only field that says *why* a request failed and is typically ~90 bytes next to the multi-hundred-KB bodies that trip the cap, so dropping it left a size-limited row undiagnosable — a provider outage, a local timeout and an upstream 400 all rendered identically. It is now preserved at every fallback stage, truncated to 4KB if it is itself large ([#12026](https://github.com/diegosouzapw/OmniRoute/issues/12026)). ([#12095](https://github.com/diegosouzapw/OmniRoute/pull/12095)) — thanks @ntdat812 +- **fix(diagnostics):** preserve the error field (truncated to 4KB with a `[truncated: …]` suffix) in every call-log artifact size-limit fallback stage. Previously the minimal fallback replaced the error with `[omitted: call log artifact size limit exceeded]`, so an oversized artifact row showed nothing about WHY the request failed — e.g. 91 of 847 opencode-go 504 rows on one production instance were undiagnosable from the dashboard. Oversized request/response bodies are still omitted exactly as before; the error cap is independent of the payload sizes that tripped the fallback. (#12026) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **fix(sse):** OpenAI Responses clients that declare the native `web_search` tool now receive a spec-shaped `web_search_call` output item with `action.sources` alongside the preserved function-call round-trip, so search results executed through OmniRoute's own search backend are consumable by standard Responses clients (Codex, pi-web-access, …). ([#12031](https://github.com/diegosouzapw/OmniRoute/pull/12031)) — thanks @watchingdogs +- **fix(cli):** use in-thread alias resolver hooks on modern runtimes to avoid deprecation noise and improve Node.js forward compatibility ([#12073](https://github.com/diegosouzapw/OmniRoute/issues/12073)). ([#12083](https://github.com/diegosouzapw/OmniRoute/pull/12083)) — thanks @xiaoyaner0201 +- **fix(combo):** an operator-set **Agent Features → Context length** on a combo is now honored at request time. The value was persisted and advertised through `/v1/models`, but `resolveComboContextLimit()` never consulted it — so a multi-target combo whose members carry no per-model window fell through to the provider's generic `defaultContextLength` (openrouter 128000, command-code 200000) and rejected large requests with `Input exceeds context window … limit 128000` despite the combo being explicitly sized much larger. An identical single-target combo worked, because it collapses to its concrete target before the guard runs. Invalid values (0/negative/NaN/Infinity) are ignored, so the existing target → combo-min → fallback order is unchanged. ([#12090](https://github.com/diegosouzapw/OmniRoute/pull/12090)) — thanks @adivekar-utexas +- **fix(sse):** passthrough streams now estimate usage on finish when upstream closes without usage even with `stream_options.include_usage` — avoids `0 tokens / 0%` for providers that stay silent (and correctly handles trailing empty-choices usage) ([#12151](https://github.com/diegosouzapw/OmniRoute/pull/12151)) — thanks @maxmad64bis +- **fix(combos):** clearing an agent feature in the combos editor now persists — unchecking context cache protection, or emptying the system message or tool filter, sends an explicit `null` instead of dropping the field from the `PUT` body, which the update merge read as "leave unchanged" ([#12177](https://github.com/diegosouzapw/OmniRoute/pull/12177)) — thanks @foreveryh / @ntdatt812 +- **fix(memory):** self-hosted embedding endpoints now vectorize — the vector width is measured from the first embedding that comes back instead of being read from a registry that cannot describe them, so `vec_memories` is created and memories stop piling up unvectorized behind a green health check ([#12180](https://github.com/diegosouzapw/OmniRoute/pull/12180)) — thanks @kanade-hoshino / @ntdatt812 +- **fix(translator):** the leading `system` message now reaches Responses-API upstreams when its `content` is a content-part array — it was read as `typeof content === "string" ? content : ""`, so a prompt-caching client (Anthropic `cache_control`, the shape LiteLLM and the Anthropic SDK emit) had its entire system prompt replaced by an empty `instructions`. The request was still accepted with a normal `prompt_tokens` count, so the model answered with no instructions and nothing in the response said they were missing. Mid-conversation system turns already handled the array shape ([#7056](https://github.com/diegosouzapw/OmniRoute/pull/7056)); only the first one did not ([#12206](https://github.com/diegosouzapw/OmniRoute/issues/12206)). Regression guard: `tests/unit/translator-openai-responses-system-content-parts.test.ts`. ([#12207](https://github.com/diegosouzapw/OmniRoute/pull/12207)) — thanks @Gorillaz322 +- **fix(free-tier):** `/api/free-tier/summary` no longer computes its totals from a Radar feed built before the catalog the running release ships. When the cached feed is older — or carries no build date at all — the route answers from the shipped catalog, resolved through the operator's local model state so disabled and tombstoned models stay out of the numbers ([#12215](https://github.com/diegosouzapw/OmniRoute/pull/12215)). — thanks @maxmad64bis +- **fix(oauth):** Keep a Claude personal workspace and a Team organization as separate connections — they share the same email and `accountUUID`, so the email-only OAuth dedup let the second login overwrite the first account's tokens; `organizationUUID` now disambiguates them, the way `workspaceId` does for Codex ([#12222](https://github.com/diegosouzapw/OmniRoute/pull/12222)) — thanks @davidebaraldo +- **fix(resilience):** a 402 on a single paid model of a passthrough/gateway provider (e.g. `kilo-gateway`, `ollama-cloud`) no longer terminalizes the whole connection with a never-auto-recovered `credits_exhausted` status — only the paid model is locked out, so free models on the same key keep serving. 402 variant of [#3027](https://github.com/diegosouzapw/OmniRoute/issues/3027). Single-credential providers are unaffected — a 402 there is still treated as the key being genuinely out of credit ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239) / [#10616](https://github.com/diegosouzapw/OmniRoute/issues/10616)) ([#12242](https://github.com/diegosouzapw/OmniRoute/issues/12242)) — thanks @brick30llc-ctrl ([#12266](https://github.com/diegosouzapw/OmniRoute/pull/12266)) — thanks @geekyNads +- **fix(sse):** trust `finish_reason: "length"`/`"max_tokens"` over the reasoning-consumed-token ratio in response quality validation, so a reasoning model truncated below the old 90% threshold correctly fails and retries instead of returning empty content as a silent "success" ([#12262](https://github.com/diegosouzapw/OmniRoute/pull/12262)) — thanks @brick30llc-ctrl +- **fix(settings):** `PUT /api/settings/cache-config` now persists `alwaysPreserveClientCache` to the flat general settings the runtime cache-control policy actually reads; previously the value landed in the databaseSettings "cache" section and was silently ignored, so the endpoint had no effect on `cache_control` passthrough ([#12304](https://github.com/diegosouzapw/OmniRoute/pull/12304)) — thanks @davidebaraldo +- **fix(grok-cli):** treat omitted SuperGrokPro `creditUsagePercent` as 0% used so Provider Limits still renders a weekly bar (proto3 zero-elision) ([#12312](https://github.com/diegosouzapw/OmniRoute/pull/12312)) — thanks @HouMinXi +- **fix(combo):** Expose the two Auto-Combo scoring factors nobody could set — the combo validation schema and the dashboard weight sliders both declared 13 of the scorer's 15 factors, so `connectionDensity` (spreads load across a provider's connections) and `quality` were dropped on save and offered nowhere. The sliders also shipped their own default table that differed from the engine's on every non-zero factor and summed to 1.05, so the percentages shown next to them added up to 105%. Both lists now match `DEFAULT_WEIGHTS`, and a test keeps them there. Note that a combo whose stored `weights` omitted the two keys was effectively running with them at zero and the other thirteen renormalized upward; it now runs with the engine's intended distribution, so its routing does shift ([#12314](https://github.com/diegosouzapw/OmniRoute/pull/12314)) — thanks @maxmad64bis +- **fix(docs):** The free-tier reference no longer says its numbers come "confidence tagged per row" — no catalog entry carries a confidence tag and the API serves none, so every figure on that page is an estimate of the same, unstated quality. The page now states what an entry does vouch for: an independently documented hard stop (set by hand with the source in a comment, never defaulted to `true`) and a prompt-training disclosure, both with live counts the `check:docs-counts` gate keeps honest ([#12318](https://github.com/diegosouzapw/OmniRoute/pull/12318)) — thanks @maxmad64bis +- **fix(usage):** `adobe-firefly` and `firefly` have had a working usage fetcher since Adobe Firefly landed, but neither was ever added to the registration list, so the provider-plugin manifest, `genericQuotaFetcher` and the free-access quota cache all reported them as having no usage support — while `USAGE_SUPPORTED_PROVIDERS` said the opposite. Both are now declared, which also means their credit balance is fetched like any other declared provider's: `registerGenericQuotaFetchers` now registers a generic quota fetcher for them, and `resolveFreeAccessState` no longer returns early. A test holds the registration list to the dispatcher's switch in both directions, which is what the module's own docstring already asked for in prose ([#12321](https://github.com/diegosouzapw/OmniRoute/pull/12321)) — thanks @maxmad64bis +- **fix(quota):** drop the generic quota cache (agy / Antigravity / Claude OAuth) on an upstream 429 so reset-aware scoring does not keep a 60s stale snapshot, and force-refresh the next usage fetch so inner provider caches cannot recache the same window ([#12325](https://github.com/diegosouzapw/OmniRoute/pull/12325)) — thanks @HouMinXi +- **fix(combos):** deleting a combo now clears its persisted LKGP pins instead of leaving unreachable `key_value` rows behind ([#12326](https://github.com/diegosouzapw/OmniRoute/issues/12326)) ([#12425](https://github.com/diegosouzapw/OmniRoute/pull/12425)) — thanks @KrzysiekSko +- **fix(sse):** Keep ZWNJ (U+200C) and ZWJ (U+200D) in assistant text, reasoning and tool-call arguments — Persian/Kurdish half-space (`ارائه‌دهنده`), Arabic/Indic shaping and emoji sequences no longer lose them; the response de-obfuscation now removes joiners only between ASCII word characters, where the request side inserts them ([#12186](https://github.com/diegosouzapw/OmniRoute/issues/12186)) — thanks @rezjalibd ([#12359](https://github.com/diegosouzapw/OmniRoute/pull/12359)) — thanks @pacocartones +- **fix(resilience):** count resolved upstream 5xx results against the provider circuit breaker on the chat path — `CircuitBreaker.execute()` no longer reads a resolved `{ success: false, status: 5xx }` as a success that cancels the call-site failure, so a provider answering 503s now trips its breaker instead of staying `CLOSED` at `failureCount: 1`; single-model and combo dispatches are each accounted exactly once ([#12254](https://github.com/diegosouzapw/OmniRoute/issues/12254)) ([#12360](https://github.com/diegosouzapw/OmniRoute/pull/12360)) — thanks @pacocartones +- **fix(providers):** resolve the Codex quota auto-ping model from the live provider catalog and lifecycle registry instead of the retired `gpt-5.1-codex-mini`, and pause the ping with one actionable warning when no selectable Codex model exists rather than retrying a shut-down id every cooldown window ([#11905](https://github.com/diegosouzapw/OmniRoute/issues/11905)) ([#12361](https://github.com/diegosouzapw/OmniRoute/pull/12361)) — thanks @pacocartones +- **fix(api):** keep the `{created, data}` wrapper on combo-routed `/v1/images/generations` responses and default Codex image results to `b64_json` on both `/v1/images/generations` and `/v1/images/edits` so Codex CLI's built-in `image_gen` can decode them ([#12268](https://github.com/diegosouzapw/OmniRoute/issues/12268)) ([#12362](https://github.com/diegosouzapw/OmniRoute/pull/12362)) — thanks @pacocartones +- **fix(sse):** Name the shadowed custom provider node when a built-in provider id/alias (e.g. `openference` → `of`) reserves the prefix of an existing OpenAI/Anthropic-compatible node, so the runtime `No active credentials for provider: ` error explains that the prefix routed to the built-in and never reached the node's healthy connections, instead of contradicting the dashboard ([#11943](https://github.com/diegosouzapw/OmniRoute/issues/11943)) — thanks @morpheus9393 ([#12365](https://github.com/diegosouzapw/OmniRoute/pull/12365)) — thanks @pacocartones +- **fix(i18n):** wrap `ccOnboardingKeyPlaceholder` in ICU single quotes across all 43 locales so angle brackets render literally instead of being parsed as rich-text tags, which crashed the Claude Code onboarding block with `INVALID_MESSAGE: INVALID_TAG` ([#12302](https://github.com/diegosouzapw/OmniRoute/issues/12302)) ([#12369](https://github.com/diegosouzapw/OmniRoute/pull/12369)) — thanks @gonisulaimann +- **fix(models):** publish `effort_tiers` on Kimi K3's synced base-model entries (`kmca/k3`, `kmca/k3-256k`) so catalog-only clients (OpenCode, plain SDK pickers) can see and select the reasoning tiers (`low`/`high`/`max`) the synced metadata already carried — the `isSkippedEffortProvider` gate no longer suppresses tier visibility on those base entries, while synthetic `-` variant generation stays prevented and Codex/GLM base models remain excluded unchanged ([#12299](https://github.com/diegosouzapw/OmniRoute/issues/12299)) ([#12371](https://github.com/diegosouzapw/OmniRoute/pull/12371)) — thanks @gonisulaimann +- **fix(guardrails):** keep `auto`/`auto/*` virtual combos exempt from the Vision Bridge `fixedModel` credential guard so a combo target is passed through instead of silently falling back to global auto-selection ([#12237](https://github.com/diegosouzapw/OmniRoute/issues/12237)) ([#12373](https://github.com/diegosouzapw/OmniRoute/pull/12373)) — thanks @pacocartones +- **fix(combo):** capability-filter exhaustion caused by `max_tokens` above every target's known output limit now reports that reason (requested `max_tokens` vs the pool's highest known ceiling) instead of the unrelated "supports structured output" message ([#12229](https://github.com/diegosouzapw/OmniRoute/issues/12229)) — thanks @DW-MediaLab ([#12374](https://github.com/diegosouzapw/OmniRoute/pull/12374)) — thanks @pacocartones +- **fix(auth):** the `least-used` account strategy now prefers accounts without backoff before falling back to oldest `lastUsedAt`, the same tie-break `round-robin` already applies, so a failover no longer lands on a just-rate-limited account for a single request ([#12279](https://github.com/diegosouzapw/OmniRoute/issues/12279)) — thanks @tenshiak ([#12375](https://github.com/diegosouzapw/OmniRoute/pull/12375)) — thanks @pacocartones +- **fix(docker):** the `chatgpt-web-codex-browser` image now finds the Chrome binary under `chrome-linux64/` (Chrome for Testing layout in `playwright:v1.62.0-noble`) as well as the legacy `chrome-linux/`, so the container no longer crash-loops with `exec: --headless=new: not found` ([#12024](https://github.com/diegosouzapw/OmniRoute/issues/12024)) ([#12376](https://github.com/diegosouzapw/OmniRoute/pull/12376)) — thanks @pacocartones +- **fix(providers):** declare `groq/compound` and `allam-2-7b` as non-reasoning models in the curated Groq registry so `reasoning_effort` / `output_config.effort` / `thinking` from Claude Code are stripped instead of forwarded, which Groq rejected with HTTP 400 ([#12134](https://github.com/diegosouzapw/OmniRoute/issues/12134)) ([#12379](https://github.com/diegosouzapw/OmniRoute/pull/12379)) — thanks @pacocartones +- **fix(executors):** `OpencodeExecutor` no longer forces a direct connection when the connection has a proxy assigned in Proxy Management but no per-account proxies: the single-account fast path used to wrap the upstream dispatch in the direct-egress sentinel, discarding the ambient proxy context the chat handler had pinned from `proxy_assignments`, so API-key `opencode`/`opencode-go` connections egressed from the host IP (and hit geoblocks) despite the assignment. The direct pin is now applied only when no ambient proxy context exists ([#11894](https://github.com/diegosouzapw/OmniRoute/issues/11894) — thanks @hizzt) ([#12380](https://github.com/diegosouzapw/OmniRoute/pull/12380)) — thanks @pacocartones +- **fix(api):** `GET /v1/models` with `MODELS_CATALOG_PREFIX_MODE=canonical` (or `?prefix=canonical`) now lists providers whose registry alias is undefined or equal to their own id (Antigravity, Antigravity CLI and other self-aliased built-ins) — their single `provider/model` id was dropped by the alias/canonical duplicate guard in the static, synced, custom and alias-backed catalog loops ([#12058](https://github.com/diegosouzapw/OmniRoute/issues/12058)) — thanks @cheynetom ([#12381](https://github.com/diegosouzapw/OmniRoute/pull/12381)) — thanks @pacocartones +- **fix(translator):** Drop replayed `thinking` blocks that carry no signature (the shape produced from cross-provider `reasoning_content`) instead of stamping the default Claude signature on them, which Anthropic rejected with `400 Invalid signature in thinking block` on the next turn served by an Anthropic rung ([#12105](https://github.com/diegosouzapw/OmniRoute/issues/12105)) — thanks @atescivitci-cmd ([#12386](https://github.com/diegosouzapw/OmniRoute/pull/12386)) — thanks @pacocartones +- **fix(cli):** Resolve Bun's `--preload` polyfill path against the package root instead of `dist/`, so `omniroute` installed with `bun install -g` no longer crashes at startup with `error: preload not found …/dist/open-sse/utils/setupPolyfill.ts` ([#11980](https://github.com/diegosouzapw/OmniRoute/issues/11980)) — thanks @joglomedia ([#12387](https://github.com/diegosouzapw/OmniRoute/pull/12387)) — thanks @pacocartones +- **fix(providers):** `gemini-business` now publishes its model catalog — `/v1/models` and `/v1/providers/gemini-business/models` list the 12 enterprise Gemini ids the executor understands instead of returning an empty list (#12107) ([#12389](https://github.com/diegosouzapw/OmniRoute/pull/12389)) — thanks @pacocartones +- **fix(db):** install `busy_timeout` before the SQLite connection's first statement so a process opening the database while another one closes its WAL connection waits out the transient EXCLUSIVE lock instead of dying with `database is locked`, and recognise the drivers' real BUSY/PROTOCOL/IOERR errors as transient in the corruption probe so the same lock no longer renames the database away as corrupt; deflakes `cross-process contenders never both acquire the same connection` (#12394 — thanks @pacocartones) +- **fix(chat-admission):** derive the `chat_admission_busy` 503 `Retry-After` from observed heavyweight-lease occupancy — the larger of the exhausted `OMNIROUTE_CHAT_ADMISSION_QUEUE_MS` window and the time since capacity last turned over, capped at 60 s — instead of a fixed 1 s (structural) / 2 s (byte-stage) hint that invited Codex/agent fan-out clients to re-send ~1 MiB `/v1/responses` bodies every second into a gate held for the whole SSE lifetime; an idle gate keeps the historical floors ([#12135](https://github.com/diegosouzapw/OmniRoute/issues/12135)) (#12395 — thanks @pacocartones) +- **fix(api-manager):** the API key permissions modal no longer silently drops `allowedCombos` entries its Combo picker cannot render — routing-rule names such as `rt-*`, which the backend already honours — when "All" is clicked and the key is switched back to "Restrict"; those entries now survive the toggle, are listed read-only under the combo list so the count and the list agree, and are saved back verbatim instead of persisting `[]` (deny-all) (#12397 — thanks @pacocartones) +- **fix(catalog):** write the NUL separator of the catalog connection memo key, the provider serviceKind memo key, the Video Bridge promotion group key and a JSON-exactness test fixture as the `\u0000` escape instead of a raw byte — same runtime value, but the raw byte made git, GitHub and ripgrep treat those files as binary (hidden PR diffs, silently skipped searches); a guard test now keeps raw NUL bytes out of `src/`, `open-sse/` and `tests/` (#12403 — thanks @pacocartones) +- **fix(providers):** add `CLAUDE_CODE_CLIENT_VERSION` and `GITHUB_COPILOT_CLI_VERSION` env overrides so Anthropic/Copilot client-version gates can be unblocked without a rebuild ([#12417](https://github.com/diegosouzapw/OmniRoute/issues/12417)) ([#12632](https://github.com/diegosouzapw/OmniRoute/pull/12632)) — thanks @HouMinXi +- **fix(db):** back-fill `last_ping_at` and `last_pinged_reset_key` on `provider_connections` during schema reconciliation so divergent lineages that skipped `123_quota_auto_ping` still accept quota auto-ping writes ([#12470](https://github.com/diegosouzapw/OmniRoute/pull/12470) — thanks @KooshaPari) +- **fix(ci):** document MIT exceptions for `@eloqnt/{config,format-json,format-po}` (next-intl transitive; locked tarballs omit `license`) and keep the A2A lifecycle vitest off the real SQLite persistence seam ([#12581](https://github.com/diegosouzapw/OmniRoute/issues/12581)) ([#12595](https://github.com/diegosouzapw/OmniRoute/pull/12595)) +- OpenCode plugin `/v1/models` catalog fetch now waits 30s by default and attaches HTTP `statusCode` on 401/5xx so host fallback plugins can hop instead of seeing an untyped AbortError/UnknownError. ([#12607](https://github.com/diegosouzapw/OmniRoute/pull/12607)) — thanks @RaviTharuma +- **CI:** the OpenAPI security-tier gate now mirrors `isAlwaysProtectedPath()` in full — it also reads `ALWAYS_PROTECTED_API_PATTERNS`, so the pattern-gated credential routes (`/api/providers/{id}/{claude,codex}-auth/{export,apply-local}`, GHSA-5926-2w35-7h4q) no longer report as unannotated. (#12605) +- **fix(api):** GET /v1/models no longer waits forever on a hung coalesced catalog rebuild; cold-path waits are bounded (`CATALOG_BUILD_TIMEOUT_MS`, default 8s) and a last-good 200 is served when the rebuild times out ([#12627](https://github.com/diegosouzapw/OmniRoute/issues/12627)). ([#12628](https://github.com/diegosouzapw/OmniRoute/pull/12628)) — thanks @RaviTharuma +- **fix(cursor):** a non-array `tool_calls` on an assistant message no longer crashes the cursor request translator with a `TypeError`; both loops now require an array ([#12689](https://github.com/diegosouzapw/OmniRoute/issues/12689)) ([#12691](https://github.com/diegosouzapw/OmniRoute/pull/12691)) — thanks @soroush5 +- **fix(chat):** Chat Completions no longer return empty `content` after a server-owned memory or skills tool; the first provider send and account/model recovery now share one pipeline so a follow-up round-trip can fill the reply ([#12696](https://github.com/diegosouzapw/OmniRoute/issues/12696)) — thanks @HouMinXi ([#12867](https://github.com/diegosouzapw/OmniRoute/pull/12867)) +- **fix(dashboard):** batch-deleting provider keys no longer toasts failure after a successful delete when the confirm button's click event is forwarded as `onAfter` ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) — thanks @HouMinXi +- **fix(glm):** drop the extra 16th argument to `createSSETransformStreamWithLogger` that TypeScript rejected (TS2554) and that never reached the TransformStream ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) ([#12770](https://github.com/diegosouzapw/OmniRoute/pull/12770)) — thanks @HouMinXi +- **fix(dashboard):** pass `nodeMap` into Runtime `QuotaGroup` so a quota monitor in error/exhausted/alerting no longer throws `ReferenceError: nodeMap is not defined`. ([#12868](https://github.com/diegosouzapw/OmniRoute/pull/12868)) — thanks @HouMinXi +- Restricted API keys whose `allowedModels` lists a combo name no longer skip every combo member at pre-dispatch (`ALL_TARGETS_SKIPPED`). Inner-target filtering still applies when the allow-list is a provider prefix or `disableNonPublicModels` is on ([#12899](https://github.com/diegosouzapw/OmniRoute/pull/12899)). — thanks @HouMinXi +- **fix(combos):** Effort-suffixed combo members inherit the base model's `model_context_overrides` row so priority order is not inverted on large requests ([#12926](https://github.com/diegosouzapw/OmniRoute/pull/12926)) — thanks @HouMinXi +- **fix(catalog):** Picker-added `customModels` enter the dispatch-time live catalog so combo members and bare inference no longer 400 ([#12934](https://github.com/diegosouzapw/OmniRoute/pull/12934)) — thanks @HouMinXi +- **fix(docker):** re-derive the Docker build's worker-pool memory budget from the MEASURED ~4.5 GB per-process RSS (the issue owner's own VPS dmesg OOM-killer reproduction) instead of the stale 2560 MB/worker inference #11419 shipped, and lower `OMNIROUTE_BUILD_WORKERS` 3 → 2 so 1 parent + 1 page-data worker (2 processes × 4.5 GB = 9 GB) fits the 12.288 GB (75%) budget on a 16 GB GitHub Actions runner — the previous default (1 parent + 2 workers = 13.5 GB) still overcommitted the runner and kept "Publish to Docker Hub" failing with `cannot allocate memory` after #11419 merged (#7518). ([#11663](https://github.com/diegosouzapw/OmniRoute/pull/11663)) +- **fix(electron):** the packaged Windows desktop build now passes the #7592 cold-restart smoke end to end. Five defects were found and fixed on the way: (1) optional-pack staging failed on any machine whose `tar` is GNU tar — it reads the drive letter in an absolute `-f C:\...` archive path as a remote rsh host (`Cannot connect to C:`), so staging now passes a bare filename with `cwd` at the tarball directory and surfaces tar stderr on failure; (2) the Electron `files` allowlist was missing `lib/loginHeaderCapture.js`, so the packaged main process crashed at startup with `Cannot find module './lib/loginHeaderCapture'`; (3) electron-builder ≥26 injects an `!**/node_modules/**` ignore into every extraResources pattern list that no positive filter can override, which silently dropped the staged runtime `node_modules` (including the better-sqlite3 N-API prebuild) from `resources/app` — a fresh v3.8.50 desktop build would have shipped with no native SQLite driver and reproduced the #7592 sql.js fallback on every machine; an `afterPack` hook now restores the staged `node_modules` after packing; (4) the packaged-app smoke harness redirected `USERPROFILE` into its temp DATA_DIR, but Electron resolves the Roaming profile from `%USERPROFILE%\AppData\Roaming\` (USERPROFILE takes precedence over the APPDATA env var) and the path service throws instead of creating it, so `requestSingleInstanceLock()` returned false and the app exited(0) silently before `app.whenReady()` — the harness now pre-creates the derived tree, and `ensureSmokeEnvDirs` is exported and unit-tested; (5) the #7592 driver assertion parsed a `[DB] Driver: ...` line that the server's primary DB path never printed (only the unused `openDatabaseAsync()` did) — `getDbInstance()` now emits the same line on open so the guard can actually assert the native driver. Also: the smoke env-allowlist unit test hardcoded POSIX paths and could only pass on Linux/macOS; expectations are now host-agnostic, with new regression tests for the USERPROFILE-derived tree and `tarPack` under absolute Windows-style paths ([#11443](https://github.com/diegosouzapw/OmniRoute/pull/11443)) — thanks @jonlwheat2-gif +- fix(sse): stop the auto-combo candidates inspector from silently dropping model-locked/cooled-down rows (#9133) ([#11994](https://github.com/diegosouzapw/OmniRoute/pull/11994)) — thanks @hartmark +- **fix(providers):** Perplexity Web no longer turns upstream stream failures into successful assistant text; pre-content failures remain eligible for fallback, partial output ends with a structured sanitized error, and failed sessions are not persisted ([#12465](https://github.com/diegosouzapw/OmniRoute/pull/12465)) +- **fix(video-bridge):** preserve chronological video and transcript fusion, source-frame timestamps, transcript provenance, full contact-sheet intervals, and pre-abort callback isolation ([#11681](https://github.com/diegosouzapw/OmniRoute/pull/11681)) +- **Z.ai Web:** HTTP 200 streams carrying an upstream error now terminate with a structured failure instead of assistant text plus a normal stop, preserving partial output while allowing pre-content combo fallback. ([#12454](https://github.com/diegosouzapw/OmniRoute/pull/12454)) +- Absorb `Error [AbortError]: request_signal_aborted` and DOMException AbortError shapes in the process-level client-abort crash guard so routine client disconnects no longer kill the server (exit code 7). ([#12165](https://github.com/diegosouzapw/OmniRoute/pull/12165)) — thanks @alvinveroy +- **fix(sse):** Treat Adapta Web `type:error` SSE events as sanitized non-stream failures instead of empty HTTP 200 completions. ([#12459](https://github.com/diegosouzapw/OmniRoute/pull/12459)) +- **fix(autoCombo):** layer 3 (`models_dev_tier`) no longer scores retired models as premium, merge cross-provider capability rows deterministically, and inherit tier scores through variant ids ([#11508](https://github.com/diegosouzapw/OmniRoute/issues/11508)). `getModelsDevTierFitness` now vetoes ids present in `config/quality/model-lifecycle.json` with status `retired` before any other signal; `loadModelCapabilities()` aggregates per `model_id` (any-non-null-true for booleans, max for `limit_context`) instead of last-write-wins over SQLite's undefined row order; a capabilities miss retries through `resolveScoresAs()`'s base id and reports `models_dev_tier:inherited`, matching layers 1–2 (#11492). Adds `getModelsDevTierFitnessWithSource()` and an `invalidateCapabilitiesCache()` test/ops hook. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) — thanks @Meet6338-X +- fix(sse): accept Claude `content:[]` + `stop_reason: max_tokens`/`tool_use` in `detectMalformedNonStream` (match `isEmptyContentResponse`) so Claude Code `/model` probes with `max_tokens: 1` no longer become a false 502 ([#11568](https://github.com/diegosouzapw/OmniRoute/pull/11568)) — thanks @Seramicx +- **fix(security):** sanitize `request.failed` diagnostics before publishing them to live dashboard listeners and replay history, while keeping status, model, provider, latency, and internal call-log diagnostics intact. ([#12469](https://github.com/diegosouzapw/OmniRoute/pull/12469)) +- **fix(pwa):** do not intercept dashboard navigations so Chrome can fall back from a stale HTTP/3 Alt-Svc advertisement (UDP :20128 is unpublished; F5 on a long-lived tab hung until a new tab opened a fresh TCP connection). ([#12767](https://github.com/diegosouzapw/OmniRoute/pull/12767)) — thanks @HouMinXi +- **fix(executors):** handle DuckDuckGo ERR_BN_LIMIT (418) without retrying — when the upstream returns `418 ERR_BN_LIMIT` (rate-limit/ban), the executor now returns the error immediately instead of burning another VQD acquisition that would only count against the IP limit. The retry logic for `418 ERR_CHALLENGE` (unsolved challenge) remains unchanged. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) ([#12110](https://github.com/diegosouzapw/OmniRoute/pull/12110)) — thanks @oyi77 / @Meet6338-X +- Fixed DuckDuckGo streaming responses losing JSON lines and UTF-8 characters split across network chunks. ([#11528](https://github.com/diegosouzapw/OmniRoute/pull/11528)) — thanks @pacocartones +- Harden generated Cloudflare, Vercel, and Deno relays by stripping hop-by-hop, framing, and proxy authentication headers before forwarding requests upstream. ([#11533](https://github.com/diegosouzapw/OmniRoute/pull/11533)) — thanks @pacocartones +- **feat(grok-cli):** Provider Limits shows grok-cli banked reset credits from `GetRemainingResets` (including a real zero; a failed RPC omits the row) and the existing View credits button now calls `ConsumerUiSvc/RedeemReset` for grok-cli. Live tokens use nested fields 10/20/30 (id + Timestamp), not compact 1/2/3. ([#12805](https://github.com/diegosouzapw/OmniRoute/pull/12805)) — thanks @HouMinXi +- **fix(memory):** Embedding Model Quick select, Embedding Source remote dropdown, and Rerank selector now list every configured provider with embedding/rerank support instead of only chat-catalog text matches plus OpenRouter live discovery; a generic OpenAI-compatible `/embeddings` + Cohere-compatible `/rerank` runtime fallback resolves any configured chat provider's embedding/rerank endpoint, so unlisted providers no longer fail with "Unknown embedding provider"; both memory selectors gained a free-text model override ([#11390](https://github.com/diegosouzapw/OmniRoute/pull/11390)) — thanks @rqzbeh +- Harden SQLite upgrades around the historical migration-074 version collision: missing discovery and inspector tables are replayed atomically, pre-existing databases (including setup-created skeletons) receive reusable content-addressed safety snapshots, and Node test/eval probes without `DATA_DIR` are isolated from the operator database. ([#12435](https://github.com/diegosouzapw/OmniRoute/pull/12435)) +- **fix(dashboard):** Moonshot/Kimi Open Platform voucher and cash leftover percentages follow the bucket balance, so an empty wallet no longer paints those rows as 100% while Available is 0% ([#12733](https://github.com/diegosouzapw/OmniRoute/pull/12733)) — thanks @HouMinXi +- **fix(combo):** return non-retryable HTTP 400 when all candidates for a pinned native Codex turn are unavailable due to model-scoped lockout, terminating the turn cleanly while preserving turn continuity and enabling standard Combo routing on subsequent turns ([#12240](https://github.com/diegosouzapw/OmniRoute/pull/12240)) — thanks @mdigitalbh81 +- Fixed auto-router strategies preserving the selected connection through ranking and dispatch when multiple connections share the same provider and model. ([#11530](https://github.com/diegosouzapw/OmniRoute/pull/11530)) — thanks @pacocartones +- Preserve Chat Completions JSON-object response formats when translating requests to the Responses API. ([#11529](https://github.com/diegosouzapw/OmniRoute/pull/11529)) — thanks @pacocartones +- Fix idempotency fingerprints so Responses API requests include `input` and other semantic generation fields using stable serialization, while excluding credentials and request metadata. ([#11532](https://github.com/diegosouzapw/OmniRoute/pull/11532)) — thanks @pacocartones +- **fix(api):** Generated API CLI commands now enforce required OpenAPI request bodies; Combo test commands forward the required `comboName` body, while API keys created by older writers after migration 149 preserve legacy allow-all Combo access without widening explicit empty allowlists — thanks @marcelokarval ([#12070](https://github.com/diegosouzapw/OmniRoute/pull/12070)) +- **fix(grok-web):** treat upstream streaming failures as failures instead of successful assistant text: error-only streams now fail readiness with HTTP 502, while failures after legitimate content preserve that partial output and terminate through the sanitized stream failure path without a normal `stop` completion. ([#12458](https://github.com/diegosouzapw/OmniRoute/pull/12458)) +- HuggingChat now turns HTTP 200 JSONL generation failures into a sanitized 502 before content, or a fixed public stream failure after partial output, so fallback and request persistence no longer record a false successful stop. ([#12456](https://github.com/diegosouzapw/OmniRoute/pull/12456)) +- **fix(providers):** keep 1min.ai HTTP 200 stream errors out of assistant content, preserve partial output, and expose sanitized terminal errors so pre-content failures can fall back. ([#12466](https://github.com/diegosouzapw/OmniRoute/pull/12466)) +- **fix(sessions):** preserve legacy Sessions data when the additive exclusive-session projection is unavailable, returning an empty projection and warning only once per contiguous outage instead of failing the endpoint; the Sessions badge intentionally reflects the merged legacy and exclusive row count introduced by [#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389) ([#11469](https://github.com/diegosouzapw/OmniRoute/pull/11469)) — thanks @KaspaPulse +- Render Nimble Search with the generic provider icon and serve Opper's proven logo locally. ([#12415](https://github.com/diegosouzapw/OmniRoute/pull/12415)) +- **fix(quality):** `config/quality/provider-assets-provenance.jsonl` was stale against the physical `public/providers/` tree (still listing 225 assets and 5 duplicate-content groups after several provider-retirement PRs removed 85 icon files), which would have made `check:provider-asset-provenance` fail on the next full run. Reconciled the manifest to the real 142-file snapshot (142 asset records, 1 duplicate-content group, 2 newly-registered files marked `unresolved` pending provenance research), repinned `auditedCommit`, and updated the regression test's hardcoded counts to match. ([#11876](https://github.com/diegosouzapw/OmniRoute/pull/11876)) +- fix(ui): wrap quota card footer action buttons to prevent clipping Refresh button on narrow cards — when extra action buttons such as `View credits` are present on Codex cards, allow the footer row to flex-wrap with `shrink-0` buttons so `Refresh now` remains visible and clickable across all responsive card widths ([#11464](https://github.com/diegosouzapw/OmniRoute/pull/11464)) — thanks @adevwithpurpose +- Keep Antigravity Gemini usable when the same connection's Claude weekly quota is empty; generic quota cache stays per-connection for every other provider. ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) — thanks @HouMinXi +- Fixed Tailscale tunnel setup to reject ports outside the valid 1-65535 range and safely install Windows MSI packages from paths containing apostrophes. ([#11534](https://github.com/diegosouzapw/OmniRoute/pull/11534)) — thanks @pacocartones +- Classify Cloudflared and Tailscale process-management routes as local-only and spawn-capable while preserving remote access to read-only tunnel status endpoints. ([#11531](https://github.com/diegosouzapw/OmniRoute/pull/11531)) — thanks @pacocartones +- Electron release workflow: the `publish-npm` job now grants `actions: read` to the reusable `npm-publish.yml` it calls (its `publish` job requests it), which is what made GitHub refuse the whole v3.8.50 run at startup and ship the release with zero desktop assets; a `workflow_dispatch` now builds the requested tag instead of the dispatching branch and can skip the npm leg (`publish_npm=false`) when only re-attaching assets ([#11974](https://github.com/diegosouzapw/OmniRoute/pull/11974)) +- **fix(dashboard):** The Combos page usage guide now reads its dismissal through `useSyncExternalStore` instead of correcting SSR state inside an effect, removing an extra commit of the page tree on every load (and the `react-hooks/set-state-in-effect` error it raised). ([#12671](https://github.com/diegosouzapw/OmniRoute/pull/12671)) +- Electron release: `electron/package-lock.json` regained the optional `electron-builder-squirrel-windows` subtree (13 entries) that `npm ci` had been refusing as out of sync, `electron-release.yml` gained a `build_ref` dispatch input and stops regenerating release notes on a re-attach dispatch, and the npm publish workflow attaches the SBOM to the GitHub Release on dispatch publishes too — so the v3.8.51 tag ships every desktop asset and the SBOM like v3.8.49 did ([#12022](https://github.com/diegosouzapw/OmniRoute/pull/12022)) +- Drained the reds every PR against `release/v3.8.51` was born with: documented the three service-worker build-id variables, registered the six retirement/tunnel tests with the mutation gate, approved `eslint-plugin-react-hooks` in the dependency allowlist, added the six `combo.sort.*` strings to `vi` and `pt-BR`, pointed the ChatGPT Web doc at the real migration-168 test, worded the g4f hint around the member key, and realigned four tests to the retired-provider catalog and the legacy-schema fixtures the retirement migrations touch. ([#11940](https://github.com/diegosouzapw/OmniRoute/pull/11940)) +- **fix(providers):** Claude, Grok, LMArena, Notion, and Perplexity web-cookie transports now use pooled `wreq-js` 3.2 instead of the native sidecar, with all nine supported bindings pinned and audited, and the applicable platform binding plus native-license evidence included in each release artifact ([#12429](https://github.com/diegosouzapw/OmniRoute/pull/12429), supersedes [#11753](https://github.com/diegosouzapw/OmniRoute/pull/11753)). +- **fix(providers):** Zed Hosted streaming failures now trigger fallback before content and end partial streams with a sanitized structured error instead of fake assistant text and a normal-success stop. ([#12455](https://github.com/diegosouzapw/OmniRoute/pull/12455)) +- **fix(test):** capture hook result after render (direct commit `b85d0abb0b`) +- **fix(sync):** repair the two regressions the v3.8.50 sync-back left on release/v3.8.51 (direct commit `5ade9e0851`) +- **revert(models):** drop the memo reset hook ported from #10055 (direct commit `282de841ae`) +- **fix(typecheck):** guard the optional connectionId before the reactive model sync (direct commit `f2825bad75`) +- **fix(quota-share):** release the winner's reserved in-flight slot (#11371) ([#11408](https://github.com/diegosouzapw/OmniRoute/pull/11408)) — thanks @oyi77 +- **fix(providers):** stop silently dropping reasoning effort on opencode families (#10788) ([#11409](https://github.com/diegosouzapw/OmniRoute/pull/11409)) — thanks @oyi77 +- **fix(combo):** enable genuine quota-aware routing for generic providers (antigravity, claude, etc.) ([#11411](https://github.com/diegosouzapw/OmniRoute/pull/11411)) — thanks @HouMinXi +- **fix(oauth):** allow expired token retry before deactivation and activate expired retry loop (#P0) ([#11414](https://github.com/diegosouzapw/OmniRoute/pull/11414)) — thanks @HouMinXi +- **perf(executors):** lazy-load the executor registry — defer class imports + construction to first use (#11220) ([#11421](https://github.com/diegosouzapw/OmniRoute/pull/11421)) — thanks @oyi77 +- **fix:** make release/v3.8.50 CI gates green (run 32786966560 root causes) ([#11450](https://github.com/diegosouzapw/OmniRoute/pull/11450)) — thanks @jonlwheat2-gif +- **fix(sse):** exact-domain cookie match and origin-equality URL assertions ([#11452](https://github.com/diegosouzapw/OmniRoute/pull/11452)) — thanks @hartmark +- **fix(providers):** send Responses API payload during OpenAI-compatible validation ([#11454](https://github.com/diegosouzapw/OmniRoute/pull/11454)) — thanks @tuandinh0801 +- **fix(command-code):** fallback to /alpha/generate for Go plan without Provider API access ([#11455](https://github.com/diegosouzapw/OmniRoute/pull/11455)) — thanks @Chewji9875 +- **fix(responses-continuation):** fail closed on a log-truncated stored input/output array ([#11473](https://github.com/diegosouzapw/OmniRoute/pull/11473)) — thanks @hartmark +- **fix(cli):** write jcode settings as [providers.omniroute] in config.toml ([#11484](https://github.com/diegosouzapw/OmniRoute/pull/11484)) — thanks @hongnoul +- **fix(logging):** raise chat-log array truncation cap to 1000 and unify duplicate implementations ([#11499](https://github.com/diegosouzapw/OmniRoute/pull/11499)) — thanks @hartmark +- **fix:** restore live websocket URL helpers ([#11509](https://github.com/diegosouzapw/OmniRoute/pull/11509)) — thanks @PauloFH +- **perf(core):** eliminate async waterfalls across home, cache, and db operations (#11396) ([#11512](https://github.com/diegosouzapw/OmniRoute/pull/11512)) — thanks @HouMinXi +- **fix(kiro):** add runtime.us-east-1.kiro.dev as first-attempt endpoint ([#11517](https://github.com/diegosouzapw/OmniRoute/pull/11517)) — thanks @rifqiawl +- **fix(providers):** remove duplicate 128-tool truncation in OpencodeExecutor (#11444) ([#11518](https://github.com/diegosouzapw/OmniRoute/pull/11518)) — thanks @HouMinXi +- **fix(providers):** poll onboardUser LRO before classifying BYOP ([#11519](https://github.com/diegosouzapw/OmniRoute/pull/11519)) — thanks @rifqiawl +- **fix(translator):** preserve exact tool name casing for Claude tool_use payloads (#11487) ([#11520](https://github.com/diegosouzapw/OmniRoute/pull/11520)) — thanks @HouMinXi +- **fix(kimi-web):** align health probe with executor domain ([#11521](https://github.com/diegosouzapw/OmniRoute/pull/11521)) — thanks @pacocartones +- **fix(cli):** preserve existing admin password during setup ([#11522](https://github.com/diegosouzapw/OmniRoute/pull/11522)) — thanks @pacocartones +- **fix(mitm):** keep Windows DNS elevation off the POSIX sudo path (#11430) ([#11542](https://github.com/diegosouzapw/OmniRoute/pull/11542)) — thanks @official-burak +- **fix(sse):** scale chat admission by ingest byte budget instead of a fixed request count ([#11548](https://github.com/diegosouzapw/OmniRoute/pull/11548)) — thanks @TheDemonTuan +- **fix:** accept Gemini Web cookie exports ([#11554](https://github.com/diegosouzapw/OmniRoute/pull/11554)) — thanks @rafacpti23 +- **fix(i18n):** complete Brazilian Portuguese translations ([#11558](https://github.com/diegosouzapw/OmniRoute/pull/11558)) — thanks @rafacpti23 +- **fix(sse):** explicit types for openai-responses pureHelpers — clears last failing core typecheck gate ([#11567](https://github.com/diegosouzapw/OmniRoute/pull/11567)) — thanks @jonlwheat2-gif +- **fix(opencode):** hydrate Proxy Pool references for no-auth connections ([#11584](https://github.com/diegosouzapw/OmniRoute/pull/11584)) — thanks @AStupidBear +- **fix(ci):** resolve inherited release/v3.8.51 base-red CI failures ([#11608](https://github.com/diegosouzapw/OmniRoute/pull/11608)) — thanks @jonlwheat2-gif +- **fix(db):** fallback load STORAGE_ENCRYPTION_KEY from env files ([#11614](https://github.com/diegosouzapw/OmniRoute/pull/11614)) — thanks @benzntech +- **fix(providers):** mark Brave Search as no longer having a free tier ([#11621](https://github.com/diegosouzapw/OmniRoute/pull/11621)) — thanks @turbolego +- **fix:** pass tool result content through as string in OpenAI/Claude -> Gemini translation ([#11624](https://github.com/diegosouzapw/OmniRoute/pull/11624)) — thanks @Hsia97 +- **fix(sse):** scope credential health to exact key ([#11628](https://github.com/diegosouzapw/OmniRoute/pull/11628)) — thanks @KaspaPulse +- **fix(install):** guard prepare script against missing husky ([#11634](https://github.com/diegosouzapw/OmniRoute/pull/11634)) — thanks @Prajeeth-12 +- **fix(security):** redact NVIDIA_API_KEY at the log sink in the ad-hoc diag script ([#11639](https://github.com/diegosouzapw/OmniRoute/pull/11639)) +- **fix(types):** clear the two type regressions red on release/v3.8.51 ([#11640](https://github.com/diegosouzapw/OmniRoute/pull/11640)) — thanks @jonlwheat2-gif +- **fix(build):** resolve workspace: protocol dependencies to concrete semver versions during prepublish (#11540) ([#11641](https://github.com/diegosouzapw/OmniRoute/pull/11641)) — thanks @HouMinXi +- **fix(search):** properly resolve configured search connection in /v1/responses pipeline (#11524) ([#11642](https://github.com/diegosouzapw/OmniRoute/pull/11642)) — thanks @HouMinXi +- **fix(authz):** trust cf-connecting-ip for via-proxy detection when peer is Cloudflare edge (#11514) ([#11643](https://github.com/diegosouzapw/OmniRoute/pull/11643)) — thanks @HouMinXi +- **fix(security):** compare the OIDC state cookie in constant time ([#11646](https://github.com/diegosouzapw/OmniRoute/pull/11646)) +- **fix(security):** namespace the dedup hash by the calling API key ([#11649](https://github.com/diegosouzapw/OmniRoute/pull/11649)) +- **fix(models):** gate the Codex-native catalog loop on prefix mode (#11632) ([#11662](https://github.com/diegosouzapw/OmniRoute/pull/11662)) — thanks @xiaoyaner0201 +- **fix(dashboard):** project Codex routing quota in account pools ([#11666](https://github.com/diegosouzapw/OmniRoute/pull/11666)) — thanks @KaspaPulse +- **fix(ddgw):** normaliza system no upstream da release v3.8.51 ([#11692](https://github.com/diegosouzapw/OmniRoute/pull/11692)) — thanks @RhianB14 +- **fix(memory):** honor category filter in GET /api/memory ([#11699](https://github.com/diegosouzapw/OmniRoute/pull/11699)) — thanks @Sabeekhann +- **fix(ci):** detect stale workspace lockfile entries ([#11714](https://github.com/diegosouzapw/OmniRoute/pull/11714)) — thanks @pacocartones +- **fix(ci):** stamp dist/BUILD_SHA before the npm publish provenance gate ([#11721](https://github.com/diegosouzapw/OmniRoute/pull/11721)) +- **fix(docker):** decouple the best-effort Bun image from the release manifest ([#11724](https://github.com/diegosouzapw/OmniRoute/pull/11724)) +- **perf(compression):** avoid cloning memo misses twice ([#11727](https://github.com/diegosouzapw/OmniRoute/pull/11727)) — thanks @RaviTharuma +- **fix(docker):** let the bun digest artifact be absent, not fatal ([#11740](https://github.com/diegosouzapw/OmniRoute/pull/11740)) +- **fix(shared):** block cloud-metadata hosts under default remote-image guard ([#11755](https://github.com/diegosouzapw/OmniRoute/pull/11755)) — thanks @santosraju99-hub +- **fix(api):** preserve caller-provided X-Correlation-Id on chat completions ([#11760](https://github.com/diegosouzapw/OmniRoute/pull/11760)) — thanks @aniruddhaadak80 +- **fix(resilience):** lock permanently retired models instead of short backoff (Gemini ban prevention) ([#11762](https://github.com/diegosouzapw/OmniRoute/pull/11762)) — thanks @turbolego +- **fix:** mark Vercel AI Gateway as passthroughModels ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **fix(resilience):** stop hammering permanently-moved endpoints and billing-suspended accounts ([#11774](https://github.com/diegosouzapw/OmniRoute/pull/11774)) — thanks @turbolego +- **fix(lease):** remove global static reservation and gate routing on live active lease occupancy ([#11775](https://github.com/diegosouzapw/OmniRoute/pull/11775)) — thanks @TheDemonTuan +- **fix(ci):** size the install-upgrade gate to a measured run, and log the pack cost ([#11776](https://github.com/diegosouzapw/OmniRoute/pull/11776)) +- **fix(pwa):** stop serving the stale shell after deploys ([#11779](https://github.com/diegosouzapw/OmniRoute/pull/11779)) — thanks @HouMinXi +- **fix(combo):** lock GitHub models rejected as "not supported" for future requests ([#11781](https://github.com/diegosouzapw/OmniRoute/pull/11781)) — thanks @turbolego +- **fix(cli):** probe both IPv4 and IPv6 loopback for server readiness (#11766) ([#11794](https://github.com/diegosouzapw/OmniRoute/pull/11794)) — thanks @vermasomesh835 +- **fix(sse):** preserve CLIProxyAPI wrappers per connection ([#11797](https://github.com/diegosouzapw/OmniRoute/pull/11797)) — thanks @RaviTharuma +- **fix(providers):** honor model auto-fetch opt-in on create ([#11798](https://github.com/diegosouzapw/OmniRoute/pull/11798)) — thanks @RaviTharuma +- **fix(dashboard):** make model auto-fetch order independent ([#11805](https://github.com/diegosouzapw/OmniRoute/pull/11805)) — thanks @RaviTharuma +- **fix(docker):** make GHA cache exports best effort ([#11806](https://github.com/diegosouzapw/OmniRoute/pull/11806)) — thanks @RaviTharuma +- **fix(kiro):** do not permanently ban on 'User is not authorized to make this call' ([#11809](https://github.com/diegosouzapw/OmniRoute/pull/11809)) — thanks @Deftera186 +- **fix(sse):** keep cache-write tokens in OpenAI-shaped usage ([#11814](https://github.com/diegosouzapw/OmniRoute/pull/11814)) — thanks @TheDemonTuan +- **fix(docs):** resolve relative markdown and wiki links across Fumadocs and GitHub wiki ([#11834](https://github.com/diegosouzapw/OmniRoute/pull/11834)) — thanks @AndrianBalanescu +- **fix(chat):** preserve unstripped model string for passthrough provider routing ([#11840](https://github.com/diegosouzapw/OmniRoute/pull/11840)) — thanks @raheemuddin786 +- **fix(mcp):** avoid async initialization inside sync esm wrappers (#11569) ([#11849](https://github.com/diegosouzapw/OmniRoute/pull/11849)) — thanks @HouMinXi +- **fix(sse):** preserve coherent markdown token boundaries across stream translation chunks (#11606) ([#11851](https://github.com/diegosouzapw/OmniRoute/pull/11851)) — thanks @HouMinXi +- **fix(ci):** run the install-upgrade gate on disk, not on the /tmp tmpfs ([#11855](https://github.com/diegosouzapw/OmniRoute/pull/11855)) +- **fix(skills):** expand shorthand property types in injected tool schemas ([#11857](https://github.com/diegosouzapw/OmniRoute/pull/11857)) — thanks @alvinveroy +- **fix(ci):** publish npm from a hosted runner so provenance is accepted ([#11877](https://github.com/diegosouzapw/OmniRoute/pull/11877)) +- **fix(lint):** pin react hooks plugin to 7.0.1 ([#11886](https://github.com/diegosouzapw/OmniRoute/pull/11886)) +- **fix(ci):** surface and clear release-green ESLint failure ([#11890](https://github.com/diegosouzapw/OmniRoute/pull/11890)) — thanks @MumuTW +- **fix(guardrails):** prevent duplicate prompt-injection-guard log output ([#11936](https://github.com/diegosouzapw/OmniRoute/pull/11936)) — thanks @patrykkopycinski +- **fix(auth):** downgrade expected transient states from warn to debug ([#11937](https://github.com/diegosouzapw/OmniRoute/pull/11937)) — thanks @patrykkopycinski +- **fix(codex):** fail over image generation for imported free plans ([#11948](https://github.com/diegosouzapw/OmniRoute/pull/11948)) — thanks @Bl0ck154 +- **fix(codex):** normalize non-stream responses ([#11951](https://github.com/diegosouzapw/OmniRoute/pull/11951)) — thanks @Bl0ck154 +- **fix(images):** forward Antigravity image size ([#11952](https://github.com/diegosouzapw/OmniRoute/pull/11952)) — thanks @Bl0ck154 +- **fix(db):** invalidate connection cache after upsert ([#11953](https://github.com/diegosouzapw/OmniRoute/pull/11953)) — thanks @Bl0ck154 +- **fix(codex):** restore imported account state ([#11954](https://github.com/diegosouzapw/OmniRoute/pull/11954)) — thanks @Bl0ck154 +- **fix(sse):** give extended-thinking targets the reasoning readiness budget ([#11959](https://github.com/diegosouzapw/OmniRoute/pull/11959)) — thanks @abhisheksharma2411 +- **fix(dashboard):** use opaque background and readable text color on cost chart tooltips ([#11960](https://github.com/diegosouzapw/OmniRoute/pull/11960)) — thanks @ZaimMarzuki +- **fix(antigravity):** send complete loadCodeAssist metadata (ideType/platform/pluginType as numeric enums) ([#11969](https://github.com/diegosouzapw/OmniRoute/pull/11969)) — thanks @rifqiawl +- **fix(core):** resolve DB init race condition and reasoning translation ([#12003](https://github.com/diegosouzapw/OmniRoute/pull/12003)) — thanks @raheemuddin786 +- **fix(admission):** exclude reclaimable page cache from the cgroup pressure ratio ([#12017](https://github.com/diegosouzapw/OmniRoute/pull/12017)) — thanks @HouMinXi +- **fix(build):** prune non-production trees in NFT trace excludes and tsconfig ([#12028](https://github.com/diegosouzapw/OmniRoute/pull/12028)) — thanks @Chewji9875 +- **fix(migrations):** add renamed migration compatibility for 056/073/077/101 ([#12036](https://github.com/diegosouzapw/OmniRoute/pull/12036)) — thanks @oyi77 +- **fix(sse):** default crash-guard logger to console.warn, not console ([#12042](https://github.com/diegosouzapw/OmniRoute/pull/12042)) — thanks @HouMinXi +- **fix(catalog):** derive vision/modalities for built-in auto combos from effective target pool ([#12046](https://github.com/diegosouzapw/OmniRoute/pull/12046)) — thanks @Prajeeth-12 +- **fix(release):** the packaged-app smoke verifies the database opened, not a driver line the primary path never prints (twin of #12032) ([#12047](https://github.com/diegosouzapw/OmniRoute/pull/12047)) +- **fix(barrel):** migrate src/app/ off the @/lib/localDb barrel import (#11795 Phase 2) ([#12051](https://github.com/diegosouzapw/OmniRoute/pull/12051)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate src/lib/ off the localDb barrel to direct db imports (#59) ([#12052](https://github.com/diegosouzapw/OmniRoute/pull/12052)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate open-sse, src/shared, src/sse, src/models, src/domain off the @/lib/localDb barrel import (#11795 Phase 4) ([#12053](https://github.com/diegosouzapw/OmniRoute/pull/12053)) — thanks @jonlwheat2-gif +- **fix(barrel):** delete the @/lib/localDb barrel — every consumer migrated (#11795 Phase 5) ([#12055](https://github.com/diegosouzapw/OmniRoute/pull/12055)) — thanks @jonlwheat2-gif +- **fix(radar):** restore D12 public boundary ([#12057](https://github.com/diegosouzapw/OmniRoute/pull/12057)) +- **fix(dev):** bound webpack and Tailwind scans ([#12075](https://github.com/diegosouzapw/OmniRoute/pull/12075)) — thanks @backryun +- **fix(dev):** isolate root layout settings reads ([#12076](https://github.com/diegosouzapw/OmniRoute/pull/12076)) — thanks @backryun +- **fix(dev):** reduce instrumentation executor fan-out (phase 3) ([#12078](https://github.com/diegosouzapw/OmniRoute/pull/12078)) — thanks @backryun +- **fix(dev):** make logging resources HMR-singleton ([#12079](https://github.com/diegosouzapw/OmniRoute/pull/12079)) — thanks @backryun +- **fix(sse):** keep unavailable forced connections scoped ([#12080](https://github.com/diegosouzapw/OmniRoute/pull/12080)) — thanks @keeltrace +- **fix(dev):** isolate batch dispatch from instrumentation ([#12081](https://github.com/diegosouzapw/OmniRoute/pull/12081)) — thanks @backryun +- **fix(ci):** reconcile release test contract drift ([#12082](https://github.com/diegosouzapw/OmniRoute/pull/12082)) — thanks @backryun +- **fix(ci):** accept CVE-2025-68121 in the prebuilt tls-client .so, auto-close base-red issues, guard Scorecard on the default branch ([#12085](https://github.com/diegosouzapw/OmniRoute/pull/12085)) +- **fix(db):** drop three consumer-less 1proxy exports — dead-code base-red on release/v3.8.51 after the barrel deletion (#12055) ([#12087](https://github.com/diegosouzapw/OmniRoute/pull/12087)) +- **fix(api):** clear the six API-route TypeScript regressions the new gate landed red on ([#12094](https://github.com/diegosouzapw/OmniRoute/pull/12094)) +- **fix(release):** never let the tag-push Create Release append auto notes to the curated body ([#12096](https://github.com/diegosouzapw/OmniRoute/pull/12096)) +- **fix(sse):** honor CLIProxyAPI environment API key ([#12099](https://github.com/diegosouzapw/OmniRoute/pull/12099)) — thanks @RaviTharuma +- **fix(oauth):** bind Google refresh to the client that issued the token ([#12106](https://github.com/diegosouzapw/OmniRoute/pull/12106)) — thanks @HouMinXi +- **fix(dashboard):** make RequestLoggerDetail loadable outside Next — CSS via globals.css + CJS/ESM interop (#11703 base-reds) ([#12114](https://github.com/diegosouzapw/OmniRoute/pull/12114)) +- **fix(leases):** project status lease row to lease columns so joined connection PII never escapes ([#12115](https://github.com/diegosouzapw/OmniRoute/pull/12115)) — thanks @geek007git +- **fix(plugins):** do not kill the plugin process when a fire-and-forget hook times out ([#12116](https://github.com/diegosouzapw/OmniRoute/pull/12116)) — thanks @geek007git +- **fix(guardrails):** restore injection-guard logging on middleware-only routes ([#12117](https://github.com/diegosouzapw/OmniRoute/pull/12117)) — thanks @geek007git +- **fix(plugins):** refresh stored manifest from disk on activate so new hook fields reach existing installs ([#12120](https://github.com/diegosouzapw/OmniRoute/pull/12120)) — thanks @geek007git +- **fix(codex):** preserve existing provider state when bulk-import upserts a matching connection ([#12122](https://github.com/diegosouzapw/OmniRoute/pull/12122)) — thanks @geek007git +- **fix(ci):** clear the two base-reds the 2026-08-30 merge batch left on release/v3.8.51 (round 3) ([#12123](https://github.com/diegosouzapw/OmniRoute/pull/12123)) +- **fix(sse):** strip type:'custom' from Claude tools on agentrouter dispatch ([#12126](https://github.com/diegosouzapw/OmniRoute/pull/12126)) — thanks @wahidsadik371-coder +- **fix(sse):** stop advertising video providers the dispatcher cannot run ([#12131](https://github.com/diegosouzapw/OmniRoute/pull/12131)) — thanks @quiterunner-commits +- **fix(search):** treat HTTP 432 and plan limit errors as transient cooldown ([#12139](https://github.com/diegosouzapw/OmniRoute/pull/12139)) — thanks @HouMinXi +- **fix(sse):** refuse an AI Horde queue that cannot fit the request budget ([#12143](https://github.com/diegosouzapw/OmniRoute/pull/12143)) — thanks @quiterunner-commits +- **fix(ci):** clear the base-reds the afternoon merge batch left on release/v3.8.51 (round 5: provider count 352, TS2554/TS2677) ([#12144](https://github.com/diegosouzapw/OmniRoute/pull/12144)) +- **fix(translator):** strip neutral tool_choice when tools absent in Responses-to-Chat (#12141) ([#12166](https://github.com/diegosouzapw/OmniRoute/pull/12166)) — thanks @HouMinXi +- **fix(guardrails):** pass providerId to getResolvedModelCapabilities in checkComboVision (#12112) ([#12169](https://github.com/diegosouzapw/OmniRoute/pull/12169)) — thanks @HouMinXi +- **fix(translator):** strip plaintext reasoning content for opaque responses backends (#12128) ([#12171](https://github.com/diegosouzapw/OmniRoute/pull/12171)) — thanks @HouMinXi +- **perf(stream):** compile hot-path regexes once, bound token caches, fix quadratic buffering ([#12179](https://github.com/diegosouzapw/OmniRoute/pull/12179)) — thanks @opensource-elearning +- **fix:** resolve compression worker file using runtime anchors instead of… ([#12183](https://github.com/diegosouzapw/OmniRoute/pull/12183)) — thanks @NightStalker-87 +- **fix:** prevent Claude Code session kills via liveness-aware readiness + auto model echo ([#12189](https://github.com/diegosouzapw/OmniRoute/pull/12189)) — thanks @opensource-elearning +- **fix(translator):** preserve falsy primitive values in Gemini and Antigravity function response results ([#12191](https://github.com/diegosouzapw/OmniRoute/pull/12191)) — thanks @Siva010 +- **fix(oauth):** mark empty Antigravity projectId as degraded and clear stale errors (#11284) ([#12205](https://github.com/diegosouzapw/OmniRoute/pull/12205)) — thanks @HouMinXi +- **fix(providers):** gate the Codex auto-ping usage read on the shared quota throttle ([#12209](https://github.com/diegosouzapw/OmniRoute/pull/12209)) — thanks @abhisheksharma2411 +- **fix(combos):** prioritize SQLite row id over inner JSON id and notify delete errors ([#12213](https://github.com/diegosouzapw/OmniRoute/pull/12213)) — thanks @HouMinXi +- **fix(providers):** mark featherless.ai as no longer having a free tier (`hasFree: false`) ([#12216](https://github.com/diegosouzapw/OmniRoute/pull/12216)) — thanks @turbolego +- **fix(deepseek-web):** stop Turbopack dev panic in the PoW worker path resolver ([#12221](https://github.com/diegosouzapw/OmniRoute/pull/12221)) — thanks @hartmark +- **fix(chat-admission):** clarify local 503 source ([#12223](https://github.com/diegosouzapw/OmniRoute/pull/12223)) — thanks @keeltrace +- **fix(dev):** silence webpack runtime module warnings ([#12228](https://github.com/diegosouzapw/OmniRoute/pull/12228)) — thanks @backryun +- **fix(db):** use module.require for CommonJS runtime driver loading ([#12230](https://github.com/diegosouzapw/OmniRoute/pull/12230)) — thanks @raheemuddin786 +- **fix(memory):** honest probe-driven FTS5 keyword status + memory_id rowid sync ([#12231](https://github.com/diegosouzapw/OmniRoute/pull/12231)) — thanks @raheemuddin786 +- **fix(system):** propagate abort signal to stream reader in HTTP version checks ([#12232](https://github.com/diegosouzapw/OmniRoute/pull/12232)) — thanks @raheemuddin786 +- **fix(resilience):** isolate local host execution errors from provider circuit breakers ([#12233](https://github.com/diegosouzapw/OmniRoute/pull/12233)) — thanks @raheemuddin786 +- **fix(sse):** sort injected tools deterministically for prompt caching ([#12234](https://github.com/diegosouzapw/OmniRoute/pull/12234)) — thanks @raheemuddin786 +- **fix(combo):** always clear the loop-safety timer, not just on the happy path (#11804) ([#12245](https://github.com/diegosouzapw/OmniRoute/pull/12245)) +- **fix(quality):** base-red pair — stryker tap registration + turn-pin suites aligned to the window gate ([#12255](https://github.com/diegosouzapw/OmniRoute/pull/12255)) — thanks @backryun +- **fix(dev):** qualify Turbopack runtime boundaries ([#12258](https://github.com/diegosouzapw/OmniRoute/pull/12258)) — thanks @backryun +- **fix(combo):** fall back to full pool when collapsed sole survivor is context-too-small ([#12278](https://github.com/diegosouzapw/OmniRoute/pull/12278)) — thanks @opensource-elearning +- **fix(combo):** bound the pre-dispatch unavailable skip so a stale label cannot dark a pool (#12168) ([#12285](https://github.com/diegosouzapw/OmniRoute/pull/12285)) +- **fix(security):** strip Qwen/Alibaba console-session cookies from provider API responses ([#12287](https://github.com/diegosouzapw/OmniRoute/pull/12287)) +- **fix(usage):** console-aware Token Plan guidance + subscription hint on bailian 401 ([#12288](https://github.com/diegosouzapw/OmniRoute/pull/12288)) +- **fix(resourcePressure):** log numeric detail on every rejection, recover faster ([#12293](https://github.com/diegosouzapw/OmniRoute/pull/12293)) — thanks @hartmark +- **fix(release):** drain the 2026-09-01 base-red window — passthrough usage regression + radarPage i18n keys ([#12327](https://github.com/diegosouzapw/OmniRoute/pull/12327)) +- **fix(ci):** openapi-security-tiers checker must honor routeGuard patterns + imported prefixes ([#12350](https://github.com/diegosouzapw/OmniRoute/pull/12350)) — thanks @ggiak +- **perf(ci):** cache node_modules in the npm-ci-retry composite (#8084 D3) ([#12408](https://github.com/diegosouzapw/OmniRoute/pull/12408)) +- **revert(ui):** point CTAs back at their real destinations ([#12410](https://github.com/diegosouzapw/OmniRoute/pull/12410)) +- **fix(release):** drain the 2026-09-02 base-red — rerank-providers import + api-typecheck baseline ratchet ([#12414](https://github.com/diegosouzapw/OmniRoute/pull/12414)) +- **fix(types):** make system prompt injection noImplicitAny-safe ([#12416](https://github.com/diegosouzapw/OmniRoute/pull/12416)) +- **fix(quality):** validate typecheck baseline schema ([#12419](https://github.com/diegosouzapw/OmniRoute/pull/12419)) +- **fix(memory):** resolve rerank provider node cache import ([#12421](https://github.com/diegosouzapw/OmniRoute/pull/12421)) +- **fix(ci):** repair release-root regressions — pack dedup, web-session syntax, uc-image ids ([#12423](https://github.com/diegosouzapw/OmniRoute/pull/12423)) — thanks @backryun +- **fix(providers):** separate MaxAI and UC credential contracts ([#12431](https://github.com/diegosouzapw/OmniRoute/pull/12431)) +- **fix(cli):** preserve Claude settings on config set ([#12432](https://github.com/diegosouzapw/OmniRoute/pull/12432)) — thanks @KrzysiekSko +- **fix(providers):** repair the maxai credential block truncated by merge auto-resolve ([#12433](https://github.com/diegosouzapw/OmniRoute/pull/12433)) +- **fix(quality):** record the 2026-09-02 merged growth in the file-size baseline ([#12434](https://github.com/diegosouzapw/OmniRoute/pull/12434)) +- **fix(uc):** route UC error strings through sanitizeErrorMessage; allowlist the retired codex id ([#12437](https://github.com/diegosouzapw/OmniRoute/pull/12437)) +- **fix(adapta):** redact streamed upstream errors ([#12438](https://github.com/diegosouzapw/OmniRoute/pull/12438)) +- **fix(sse):** remap non-contiguous upstream tool_calls index to a gap-free output_index ([#12445](https://github.com/diegosouzapw/OmniRoute/pull/12445)) — thanks @hartmark +- **fix(quality):** detect duplicate tool_calls entries in one response ([#12446](https://github.com/diegosouzapw/OmniRoute/pull/12446)) — thanks @hartmark +- **fix(conversations):** resolve turn content OmniRoute never sends back to the client ([#12447](https://github.com/diegosouzapw/OmniRoute/pull/12447)) — thanks @hartmark +- **fix(combo):** do not treat credits-exhausted 401 as auth skip ([#12449](https://github.com/diegosouzapw/OmniRoute/pull/12449)) — thanks @RaviTharuma +- **fix(auth):** do not park healthy quota accounts as expired ([#12452](https://github.com/diegosouzapw/OmniRoute/pull/12452)) — thanks @RaviTharuma +- **fix(responses-continuation):** fail closed on a collector-truncated, empty output array ([#12460](https://github.com/diegosouzapw/OmniRoute/pull/12460)) — thanks @hartmark +- **fix(logging):** raise the SSE payload collector's default cap ([#12461](https://github.com/diegosouzapw/OmniRoute/pull/12461)) — thanks @hartmark +- **fix(resilience):** surface Responses failed.error.message in 502s ([#12472](https://github.com/diegosouzapw/OmniRoute/pull/12472)) — thanks @RaviTharuma +- **fix(security):** unbiased maxai X-Random nonce + stricter URL/regex assertions ([#12502](https://github.com/diegosouzapw/OmniRoute/pull/12502)) +- **fix(video):** re-anchor transcript log-redaction so PII/credential maskers can't reopen the leak (#12150 P1 follow-up) ([#12503](https://github.com/diegosouzapw/OmniRoute/pull/12503)) +- **fix(monitoring):** serve cached credentialHealth off the request path ([#12533](https://github.com/diegosouzapw/OmniRoute/pull/12533)) — thanks @RaviTharuma +- **fix(quota):** keep Antigravity Gemini usable when Claude weekly is empty ([#12566](https://github.com/diegosouzapw/OmniRoute/pull/12566)) — thanks @HouMinXi +- **fix(authz):** hard-gate every credential export and CLI-config write (GHSA-5926-2w35-7h4q) ([#12600](https://github.com/diegosouzapw/OmniRoute/pull/12600)) +- **fix(docs):** restore the Next build — REMOVED_PROVIDERS.md had no frontmatter (base-red #12581) ([#12610](https://github.com/diegosouzapw/OmniRoute/pull/12610)) +- **fix(quality):** re-point the zcodeProtocol public-creds allowlist to line 313 ([#12615](https://github.com/diegosouzapw/OmniRoute/pull/12615)) +- **fix(security):** close 3 advisories — search baseUrl exfil, sk- in the error sanitizer, bifrost relay header leak ([#12620](https://github.com/diegosouzapw/OmniRoute/pull/12620)) +- **fix(providers):** sanitize boolean required and nested bare maps for Gemini (#12269) ([#12624](https://github.com/diegosouzapw/OmniRoute/pull/12624)) — thanks @HouMinXi +- **fix(resilience):** keep Overloaded STREAM_EARLY_EOF off the provider breaker ([#12626](https://github.com/diegosouzapw/OmniRoute/pull/12626)) — thanks @HouMinXi +- **fix(catalog):** re-audit free-tier quotas against official pages ([#12649](https://github.com/diegosouzapw/OmniRoute/pull/12649)) +- **fix(dashboard):** keep a request's pending-tracking id stable across combo target retries ([#12650](https://github.com/diegosouzapw/OmniRoute/pull/12650)) — thanks @hartmark +- **fix(tests):** retire dead model ids from the chat-pipeline integration suite (base-red #12581) ([#12670](https://github.com/diegosouzapw/OmniRoute/pull/12670)) +- **fix:** resolve SqliteError no such table compression_run_telemetry during cleanup ([#12682](https://github.com/diegosouzapw/OmniRoute/pull/12682)) — thanks @groovecityJO +- **fix(combo):** treat a pin-only step as implicit connection allowlist ([#12697](https://github.com/diegosouzapw/OmniRoute/pull/12697)) — thanks @HouMinXi +- **fix(ci):** ship .npmrc in published package so legacy-peer-deps applies to consumers (#11544) ([#12699](https://github.com/diegosouzapw/OmniRoute/pull/12699)) — thanks @KooshaPari +- **fix(docker):** pin 4 CLI tools to exact versions (#12576) ([#12703](https://github.com/diegosouzapw/OmniRoute/pull/12703)) — thanks @KooshaPari +- **fix(docker):** document and harden cli profile trust boundary (#12570) ([#12706](https://github.com/diegosouzapw/OmniRoute/pull/12706)) — thanks @KooshaPari +- **fix(sse):** redact video transcript in pre-guardrail rejected-request logs (#12150 P2 item 7) ([#12710](https://github.com/diegosouzapw/OmniRoute/pull/12710)) +- **fix(i18n):** quote placeholder in OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES description (#12505) ([#12769](https://github.com/diegosouzapw/OmniRoute/pull/12769)) — thanks @KooshaPari +- **fix(api/pricing):** surface validation error message as string, not raw object (#12494) ([#12771](https://github.com/diegosouzapw/OmniRoute/pull/12771)) — thanks @KooshaPari +- **fix(sse):** re-enable prompt compression for native Codex passthrough ([#12834](https://github.com/diegosouzapw/OmniRoute/pull/12834)) — thanks @thomasmaerz +- **fix(dashboard):** model health tests for a provider node set to the Responses API now call `/v1/responses` with a Responses-shaped body instead of `/v1/chat/completions` — those models were reported as `Provider returned HTTP 200 but no text content` even though the same model answered normally through `/v1/responses` ([#13070](https://github.com/diegosouzapw/OmniRoute/issues/13070)) ([#13087](https://github.com/diegosouzapw/OmniRoute/pull/13087)) — thanks @ntdatt812 +- fix(routing): stop a round-robin combo's "opencode" targets from collapsing onto the opencode-zen connection (#11912) ([#13283](https://github.com/diegosouzapw/OmniRoute/pull/13283)) +- fix(routing): stop the reactive-compaction debug log from lying when compression is globally disabled (#11977) ([#13276](https://github.com/diegosouzapw/OmniRoute/pull/13276)) +- fix(electron): relativize standalone-bundle symlink targets so Stage 8 manifest verification stops failing on Windows (#11979) ([#13251](https://github.com/diegosouzapw/OmniRoute/pull/13251)) +- fix(dashboard): surface a visible error when Compression Studio's combined preview run fails (#12061) ([#13288](https://github.com/diegosouzapw/OmniRoute/pull/13288)) +- fix(dashboard): make the compression "Effective pipeline" preview honor the active profile and warn when the master switch is off (#12063) ([#13287](https://github.com/diegosouzapw/OmniRoute/pull/13287)) +- fix(providers): scope TinyCMS Web signer's DOM shims to each call instead of leaking them for the process lifetime, and surface a clean HTTP status on a non-JSON interception-toggles error (#12072) ([#13254](https://github.com/diegosouzapw/OmniRoute/pull/13254)) +- fix(guardrails): stop Vision Bridge from re-selecting a model locked after a 404 (#12111) ([#13259](https://github.com/diegosouzapw/OmniRoute/pull/13259)) +- fix(sse): require Responses-shaped body before native OpenAI-compatible passthrough (#12129) ([#13278](https://github.com/diegosouzapw/OmniRoute/pull/13278)) +- fix(providers): minimax-m3 now collapses manual thinking.type:"enabled" to adaptive, preventing upstream 400 (2013) (#12132) ([#13249](https://github.com/diegosouzapw/OmniRoute/pull/13249)) +- fix(db): scope model visibility overrides by modality so hiding a Chat model no longer hides an identically-ID'd Image/Embeddings/etc. model (#12172) ([#13258](https://github.com/diegosouzapw/OmniRoute/pull/13258)) +- fix(db): scope local-provider apiKey dedup to matching base URL so LM Studio/Ollama-style connections support multiple accounts (#12173) ([#13250](https://github.com/diegosouzapw/OmniRoute/pull/13250)) +- fix(providers): refresh Trae's stale Referer/Origin and forward user timezone so imported connections stop failing with 401 (#12190) ([#13255](https://github.com/diegosouzapw/OmniRoute/pull/13255)) +- fix(providers): route opencode-go/gpt-5.6-luna to /responses instead of /chat/completions (#12196) ([#13275](https://github.com/diegosouzapw/OmniRoute/pull/13275)) +- fix(dashboard): allow deleting the last extra-upstream-header row even when invalid (#12251) ([#13252](https://github.com/diegosouzapw/OmniRoute/pull/13252)) +- **fix(i18n):** translate pre-existing `__MISSING__:` keys for `combo.sort`, `requestLogger.detail` expand/collapse, `common.profile`, and `settings.resilienceCredentialHealth*` across 39 locales ([#12272](https://github.com/diegosouzapw/OmniRoute/issues/12272)) ([#13034](https://github.com/diegosouzapw/OmniRoute/pull/13034)) — thanks @HouMinXi +- fix(cli): run the Node.js runtime compatibility guard before the heavy `tsx/esm` + Commander import chain so an unsupported runtime gets a clear message instead of a raw `Invalid regular expression flags` crash (#12296) ([#13253](https://github.com/diegosouzapw/OmniRoute/pull/13253)) +- fix(dashboard): refresh the providers list after deleting a compatible provider node (#12298) ([#13256](https://github.com/diegosouzapw/OmniRoute/pull/13256)) +- fix(usage): fail closed on API-key budget enforcement when a provider's `auto` routing alias has no pricing row, instead of silently counting it as $0 (#12341) ([#13257](https://github.com/diegosouzapw/OmniRoute/pull/13257)) +- **fix(providers):** include the submitted Agnes video model when polling by `video_id` ([#12356](https://github.com/diegosouzapw/OmniRoute/pull/12356)) — thanks @pacocartones +- **fix(routing):** custom OpenAI-compatible nodes now honor the saved Chat/Responses API type after edits instead of letting the node's original ID prefix override the live connection setting ([#11884](https://github.com/diegosouzapw/OmniRoute/issues/11884)). ([#12358](https://github.com/diegosouzapw/OmniRoute/pull/12358)) — thanks @pacocartones +- fix(sse): surface an error instead of a silent empty 200 when a Claude stream closes with zero bytes (#12398) ([#13285](https://github.com/diegosouzapw/OmniRoute/pull/13285)) +- fix(oauth): warn before the dead localhost:8080 redirect in antigravity/gemini `oauth start` (#12413) ([#13265](https://github.com/diegosouzapw/OmniRoute/pull/13265)) +- fix(providers): stop devin-cli spawn error from double-closing the SSE controller (#12517) ([#13261](https://github.com/diegosouzapw/OmniRoute/pull/13261)) +- **fix(rerank):** clamp Voyage `top_k` to the documents actually sent after empty-string filtering, and honor `return_documents: false` in the NVIDIA response adapter (#12523 — thanks @pacocartones) +- **fix(audio):** `/v1/audio/translations` now resolves combo names the way `/v1/audio/transcriptions` already does, so a combo that `GET /v1/models` advertises is fanned out to its targets instead of being rejected with `400 Invalid translation model: . Use format: provider/model`; literal `provider/model` ids and unknown bare names behave as before (#12536 — thanks @pacocartones) +- **fix(gemini):** strip the JSON-Schema-2020-12 `prefixItems` keyword from Gemini tool schemas at every nesting level, so Claude Code tool definitions no longer fail with `400 Unknown name "prefixItems"` on Gemini models (#12540 — thanks @pacocartones) +- **fix(api-manager):** Expose an accessible loading status while API keys are fetched instead of an empty accessibility tree (#12541 — thanks @pacocartones) +- **fix(video):** Clamp `estimateJpegFrameBytes` at zero for padding-only payloads and build the three encode-side frame data URIs from `JPEG_FRAME_DATA_URI_PREFIX` instead of a repeated literal (#12543 — thanks @pacocartones) +- **fix(devin):** accept Windows `DEVIN_AGENTIC_HOME` sandbox paths (`C:\...\.sandbox\...`) in the isolated-home check so the Devin Claude Bridge no longer fails closed on Windows ([#12405](https://github.com/diegosouzapw/OmniRoute/issues/12405)) (#12545 — thanks @pacocartones) +- **fix(db):** Add `conversation_turn_nodes` and orphaned `agentic_conversations` to the auto-cleanup cycle under the existing `retention.callLogs` window, so identity nodes whose call-log content has already been purged no longer accumulate without bound in `storage.sqlite` (#12548 — thanks @pacocartones) +- **fix(i18n):** Wrap the `~/.claude/profiles//settings.json` placeholder in ICU single quotes in the `OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES` feature-flag description across all 42 locales and the TypeScript default, so next-intl no longer fails with `INVALID_MESSAGE: UNCLOSED_TAG` and the Feature Flags card shows the description instead of the raw key (#12549 — thanks @pacocartones) +- **fix(orchestration):** `updateCloudAgentTask` now publishes the task's real `status` on `agent.task.updated` when an update only touches `result`, `activities` or `error`, instead of the fabricated `"updated"` state, and stays silent when no row matched the id (#12550 — thanks @pacocartones) +- **fix(i18n):** the home "Recent Requests" panel and the Provider Topology legend are now translated instead of rendering English copies on non-English dashboards; the legend reads its own `home.topologyLegend*` labels with consistent casing rather than borrowing the memory-settings "Recent" and analytics "Error" strings (#12551 — thanks @pacocartones). +- **docs(reference):** bring the `FEATURE_FLAGS.md` catalog back to 1:1 with `featureFlagDefinitions.ts` — 20 missing flags added, the two `*_BLOCK_THRESHOLD` env-only knobs moved out of the flag tables, category/total counts and the Live WS port corrected, guarded by a static test (#12552 — thanks @pacocartones) +- **fix(i18n):** backfill missing `usage.kiloPass*` strings in 39 locales and restore `featureFlagOmnirouteDisableThinkingLevelVariantsDescription` in `pt.json` ([#12561](https://github.com/diegosouzapw/OmniRoute/issues/12561)) ([#13027](https://github.com/diegosouzapw/OmniRoute/pull/13027)) — thanks @HouMinXi +- fix(docker): default docker-compose app ports (dashboard/API/live-WS) to loopback instead of `0.0.0.0`, closing the anonymous `/v1` LAN/WAN exposure gap left open by `REQUIRE_API_KEY=false` (#12568) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): close DNS-rebinding SSRF gap in webhook outbound-URL guard (#12569) ([#13243](https://github.com/diegosouzapw/OmniRoute/pull/13243)) +- fix(docker): require a per-session token on the VNC browser CDP bridge and isolate it on a dedicated Docker network (#12571) ([#13241](https://github.com/diegosouzapw/OmniRoute/pull/13241)) +- fix(open-sse): write Adobe Firefly session tokens and cookie jars with 0700/0600 permissions instead of the process umask (#12572) ([#13242](https://github.com/diegosouzapw/OmniRoute/pull/13242)) +- fix(api): remove hardcoded wildcard CORS in openai-to-gemini-sse.ts so the centralized fail-closed CORS gate is the sole source of `Access-Control-Allow-Origin` (#12573) ([#13239](https://github.com/diegosouzapw/OmniRoute/pull/13239)) +- fix(api): enforce API key policy (budget/rate-limit/schedule/endpoint scoping) on the ElevenLabs speech-to-text, text-to-speech and voices proxy routes (#12574) ([#13244](https://github.com/diegosouzapw/OmniRoute/pull/13244)) +- fix(sse): cap HuggingChat NDJSON body size and bound the read loop with the fetch timeout so a stalled or hostile upstream cannot buffer unbounded memory (#12577) ([#13282](https://github.com/diegosouzapw/OmniRoute/pull/13282)) +- fix(docker): scope the cliproxyapi/qdrant/bifrost sidecars to loopback by default and forward `CLIPROXYAPI_MANAGEMENT_KEY` into the cliproxyapi container so its management API is not left both unauthenticated and LAN-published (#12578) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): create DB export temp paths with `fs.mkdtempSync` instead of predictable timestamps (#12579) ([#13237](https://github.com/diegosouzapw/OmniRoute/pull/13237)) +- Cline 401 bodies that say "re-authenticate your Cline account" classify as a refreshable OAuth token, not a terminal expired key. The cooling panel no longer labels every cooldown as a 429; it shows the recorded last error instead. (#12594) ([#13060](https://github.com/diegosouzapw/OmniRoute/pull/13060)) — thanks @HouMinXi +- **fix(catalog):** degrade unknown combo targets instead of dropping LCD modalities, and persist OpenRouter `architecture.input_modalities` into the capability snapshot ([#12613](https://github.com/diegosouzapw/OmniRoute/issues/12613)) ([#13042](https://github.com/diegosouzapw/OmniRoute/pull/13042)) — thanks @HouMinXi +- fix(providers): send `x-api-key` instead of `Authorization: Bearer` for OpenCode Zen's `/v1/responses` endpoint (Muse Spark Contributor models), fixing a 401 on OmniRoute's auth header (#12633) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(sse): surface the actionable "Auggie CLI not found" message when the shell reports a missing `auggie` binary via exit code instead of a spawn error (#12645) ([#13263](https://github.com/diegosouzapw/OmniRoute/pull/13263)) +- **fix(config):** Persist deletions of built-in background-degradation entries — when a stored settings record exists its `degradationMap` is now authoritative instead of being merged under the defaults, so an entry the user removed in the dashboard no longer reappears on the next apply or restart ([#12424](https://github.com/diegosouzapw/OmniRoute/issues/12424)) ([#12647](https://github.com/diegosouzapw/OmniRoute/pull/12647)) — thanks @pacocartones +- **fix(gamification):** action-count badge milestones (First Token, Token Consumer, Token Machine, Token Whale, and the token-sharing tier) are now backed by a durable `xp_action_counts` counter incremented in `addXp()`, instead of a live `COUNT(*)` over `xp_audit_log`. The audit log is pruned by `retention.xpAuditLog` (default 30 days), so on a default install those "lifetime" milestones were really "actions in the last 30 days" and unlocked badges could stop unlocking once old rows aged out. `getActionCount()` and `checkActionCountBadges()` now read the same durable source, and a migration backfills existing totals from the surviving audit rows ([#12546](https://github.com/diegosouzapw/OmniRoute/issues/12546)) ([#12651](https://github.com/diegosouzapw/OmniRoute/pull/12651)) — thanks @pacocartones +- **fix(images):** `/v1/images/edits` now iterates a combo's targets the same way `/v1/images/generations` does (#9239) instead of flattening a bare combo to its first target. A combo whose first target is not edit-capable — or lacks credentials — now falls through to a later edit-capable target rather than hard-erroring, and missing credentials are skipped (not a hard `401`) to match the generations path. The per-target skip/terminal classification is extracted into a shared `runImageComboTargets` loop, so generations behavior is unchanged ([#12547](https://github.com/diegosouzapw/OmniRoute/issues/12547)). ([#12653](https://github.com/diegosouzapw/OmniRoute/pull/12653)) — thanks @pacocartones +- fix(sse): add first-byte watchdog to the TLS-fingerprint transport so a stalled wreq body falls back instead of hanging for minutes (#12656) ([#13272](https://github.com/diegosouzapw/OmniRoute/pull/13272)) +- fix(sse): exempt tiny-budget reasoning probes from combo quality failure and surface persisted-cooldown skips in ALL_TARGETS_SKIPPED diagnostics (#12659) ([#13269](https://github.com/diegosouzapw/OmniRoute/pull/13269)) +- fix(models): declare the real ~1M contextLength for OpenCode Zen's Muse Spark 1.2 models instead of falling back to the 200000 provider default (#12681) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(oauth): align codebuddy-cn OAuth User-Agent with the chat/usage CLI version to avoid WAF false positives (#12702) ([#13264](https://github.com/diegosouzapw/OmniRoute/pull/13264)) +- fix(dashboard): surface an authentication-required banner instead of silently blanking database settings for a guest session (#12709) ([#13270](https://github.com/diegosouzapw/OmniRoute/pull/13270)) +- **fix(resilience):** stop unbounded queue that hangs 6min until Aborted — gate, provider slot, and Bottleneck queue now share a per-connection `maxWaitMs` budget; `fail-closed` on exhaust (503); execution backstop `executionMaxWaitMs` overridable per connection with upstream clamp ([#12715](https://github.com/diegosouzapw/OmniRoute/pull/12715)) — thanks @maxmad64bis (with thanks to @Tushar49 for surfacing the slow-provider need in #12635) +- **fix(auto-combo):** every mode pack now carries `quality` and `reliability` weights (`quality` 0.02, 0.03 in `quality-first`; `reliability` 0.03, 0.04 in `reliability-first`), so selecting a pack no longer silences either signal; `DEFAULT_WEIGHTS` is unchanged ([#12731](https://github.com/diegosouzapw/OmniRoute/pull/12731)) — thanks @maxmad64bis +- **fix(sse):** the provider execution pipeline keeps the upstream error `code`/`type` again — both were lost when the non-streaming leg was extracted, so a config-class `422` (Antigravity missing project) degraded into a generic account cooldown instead of reaching the gate that recognises it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(db):** a legacy `call_logs` table no longer aborts startup — the provider-stats index is created after the column healing runs, not before it, so an install predating `request_type` opens instead of failing with `no such column` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(catalog):** a malformed row in the operator-writable custom-models blob no longer kills every `auto/*` pool ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(tests):** realign the guards that the same day's merges left asserting the old behavior — the provider-count total after GreenPT and EURouter, the GLM stream buffer slot that `#12925` turned into a declared parameter, and the injection scan bound that `#13104` reshaped from a head-only window into head-plus-tail ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- fix(cache): fold tool_choice/tools/response_format into the semantic cache signature so a cached tool_calls response can no longer be replayed for a request whose tool policy forbids it (#12734) ([#13267](https://github.com/diegosouzapw/OmniRoute/pull/13267)) +- **fix(models):** stop treating provider-supplied `isFree:true`/`:free`/`0/0` flags as trusted unless the provider has a documented free tier — only the shipped free-tier catalog decides otherwise; fetched `isFree:true` trusted only for a free-tier provider, custom `isFree:true` via a trusted path ([#12744](https://github.com/diegosouzapw/OmniRoute/pull/12744)) — thanks @maxmad64bis +- fix(memory): authenticate the internal /v1/rerank loopback call so memory reranking no longer silently degrades to unranked order when REQUIRE_API_KEY=true (#12745) ([#13268](https://github.com/diegosouzapw/OmniRoute/pull/13268)) +- fix(sse): parse Ollama Cloud's current usage markup (`$X of $Y used` aria-label, nested width style) (#12749) ([#13260](https://github.com/diegosouzapw/OmniRoute/pull/13260)) +- fix(cli): setup-opencode no longer sends an active context's management token to `/v1/models` when `--api-key`/`OMNIROUTE_API_KEY` is supplied — an explicit flag or the env var now always outranks the context's token, and the flag itself is no longer swallowed by the parent program's global `--api-key` option (#12783) ([#13246](https://github.com/diegosouzapw/OmniRoute/pull/13246)) +- fix(sse): register Arcee AI in the executor provider registry so requests reach api.arcee.ai instead of silently falling back to OpenAI (#12784) ([#13277](https://github.com/diegosouzapw/OmniRoute/pull/13277)) +- **fix(quota):** share one upstream quota read when identical saturation checks arrive at the same time, so traffic bursts don't multiply provider API calls ([#12787](https://github.com/diegosouzapw/OmniRoute/pull/12787)) — thanks @maxmad64bis +- **fix(db):** ignore expired or invalid rate-limit cooldown writes so a stale timestamp can't lock a connection that should be usable — clearing still works as before ([#12788](https://github.com/diegosouzapw/OmniRoute/pull/12788)) — thanks @maxmad64bis +- **fix(providers):** single source for provider order with `xao` ranking alongside `xai-oauth` ([#12790](https://github.com/diegosouzapw/OmniRoute/pull/12790)) — thanks @maxmad64bis +- **fix(routing):** off-table models check the free-model catalog before inheriting premium prices, latency bootstraps from the observed pool median, async tiers read live database pricing with a 90-day freshness gate, and the tier cache invalidates on every pricing write ([#12792](https://github.com/diegosouzapw/OmniRoute/pull/12792)) — thanks @maxmad64bis +- **fix(auto-combo):** snapshot scoring uses the observed per-connection account tier and per-model quality instead of neutral constants, and circuit-open providers rank lower at build time ([#12794](https://github.com/diegosouzapw/OmniRoute/pull/12794)) — thanks @maxmad64bis +- **fix(pool):** empty `auto/*` pools now say why they are empty, and the models listing uses the same paid check as routing ([#12795](https://github.com/diegosouzapw/OmniRoute/pull/12795)) — thanks @maxmad64bis +- fix(routing): recognize CLIProxyAPI's 'unknown provider for model' 400 as fallback-worthy (#12800) ([#13284](https://github.com/diegosouzapw/OmniRoute/pull/13284)) +- **fix(sse):** translate-mode streams now emit the estimated token counts as a trailing usage-only chunk before `[DONE]` when the upstream stays silent, so metered chat clients see totals instead of nothing ([#12828](https://github.com/diegosouzapw/OmniRoute/pull/12828)) — thanks @maxmad64bis +- **fix(db):** provider stats stay truthful on empty and legacy databases: fallback counts default to `0` instead of `null`, latency averages read `null` (not `0`) when no durations were recorded, failures logged before the error-type column existed group under `pre_migration` instead of `unclassified`, and per-provider queries use two new composite indexes ([#12832](https://github.com/diegosouzapw/OmniRoute/pull/12832)) — thanks @maxmad64bis +- fix(nvidia): fail open when a synced model catalog goes stale instead of gating forever (#12849) ([#13248](https://github.com/diegosouzapw/OmniRoute/pull/13248)) +- **fix(db):** WAL maintenance lives in its own module: a `TRUNCATE` checkpoint that hits a busy database now warns with its streak and retries once via `PASSIVE` instead of logging success, and closing-time checkpoints no longer report success on builds without a database file, with the busy totals visible in the authenticated monitoring health payload ([#12853](https://github.com/diegosouzapw/OmniRoute/pull/12853)) — thanks @maxmad64bis +- **fix(health):** quota with no snapshots reads empty instead of a contradicting 0% ([#12857](https://github.com/diegosouzapw/OmniRoute/pull/12857)) — thanks @maxmad64bis +- fix(a2a): accept the dashboard's own session cookie on /a2a so "Run message/send" no longer fails with "Unauthorized: missing or invalid API key" (#12888) ([#13271](https://github.com/diegosouzapw/OmniRoute/pull/13271)) +- **fix(logs):** the Logs grid's in-memory filter pass no longer discards rows the SQL query already matched — selecting an API key from the dropdown (which sends the key's id) returns its calls again, the Combo tab shows every combo instead of only those whose name contains a "1", and the model filter and search cover the same columns as the query ([#12896](https://github.com/diegosouzapw/OmniRoute/pull/12896)) — fixes [#12873](https://github.com/diegosouzapw/OmniRoute/issues/12873) — thanks @ntdatt812 +- **fix(a2a):** `/api/a2a/status` now builds the agent card from the request that asked for it, so a gateway reached at a non-localhost host no longer advertises `http://localhost:20128` as its A2A URL ([#12918](https://github.com/diegosouzapw/OmniRoute/pull/12918)). — thanks @ntdatt812 +- **fix(compression):** progressive aging now appends its `[COMPRESSED:aging:…]` annotation after a turn's `tool_result` blocks instead of in front of them, so Anthropic no longer rejects aged conversations with "`tool_use` ids were found without `tool_result` blocks immediately after" ([#12920](https://github.com/diegosouzapw/OmniRoute/pull/12920)). — thanks @ntdatt812 +- **fix(bedrock):** model import now resolves context limits for every vendor prefix instead of only `anthropic.*`, so `global.openai.gpt-5.6-*` no longer imports with a null `inputTokenLimit` and gets rejected pre-flight at the 200k default ([#12921](https://github.com/diegosouzapw/OmniRoute/pull/12921)). — thanks @ntdatt812 +- **fix(stream):** the 64 KB stream buffer GLM asks for is honoured instead of dropped, and the type error it caused no longer fails the API Route Typecheck gate on every open PR ([#12925](https://github.com/diegosouzapw/OmniRoute/pull/12925)) — thanks @ntdatt812 +- **fix(guardrails):** mask PII inside a `tool_result`'s nested content array, which the masker walked past while redacting its sibling block ([#12930](https://github.com/diegosouzapw/OmniRoute/pull/12930)) — thanks @ntdatt812 +- **fix(dashboard):** the Radar catalog table no longer leaves absent data unexplained — empty limits, unknown context windows, and unreported capabilities each explain themselves on hover, and a new check keeps it that way ([#12937](https://github.com/diegosouzapw/OmniRoute/pull/12937)) — thanks @maxmad64bis +- **fix(sse):** geo-blocked opencode requests rotate to the next account proxy instead of failing, so one refused egress no longer aborts the whole chain ([#12941](https://github.com/diegosouzapw/OmniRoute/pull/12941)) — thanks @maxmad64bis +- **fix(codex):** dashboard "clear cooldown" and the CAS recovery path now drop nested `codexScopeRateLimitedUntil` maps in the same write that nulls `rate_limited_until`, and fresh quota snapshots with headroom lift fallback-sourced scope cooldowns parked by quota preflight ([#12817](https://github.com/diegosouzapw/OmniRoute/issues/12817), [#12860](https://github.com/diegosouzapw/OmniRoute/issues/12860), [#12951](https://github.com/diegosouzapw/OmniRoute/pull/12951)) The dashboard reset-credit button (`consumeCodexResetCredit`) now exercises that same snapshot path after a successful redeem, so a filled quota bar is enough to unpark a leftover fallback Codex child without another manual clear. — thanks @HouMinXi +- **fix(db):** resolve `sql-wasm.wasm` across global npm install and hoisted layouts, ensuring OmniRoute can boot cleanly on Node 24 when native `better-sqlite3` is uncompiled. ([#13035](https://github.com/diegosouzapw/OmniRoute/pull/13035)) — thanks @HouMinXi +- **fix(api):** `DELETE /v1/batches/delete-completed` now sweeps only the calling API key's own completed batches (batches with no owner stay out of a key-scoped sweep on purpose), with an explicit instance-wide mode reserved for authenticated dashboard sessions, a 401 for a presented key that is unknown, revoked, deactivated, banned or expired (never falling through to the session branch), audit logging of every sweep, a sanitized 500 on failure, an owner-scoped file half (a key-scoped sweep never nulls a file another tenant owns) and an atomic sweep — chunked in 200-batch transactions in instance mode — so a mid-way error never leaves a batch pointing at a nulled file (GHSA-wvxc-jp3v-5mg5) ([#12969](https://github.com/diegosouzapw/OmniRoute/pull/12969)) +- **fix(combo):** quota-weighted routing stops drawing on an out-of-credit connection — a 402 now invalidates the stored quota snapshot instead of leaving its stale remaining percentage in place, and a snapshot older than 10 minutes no longer counts as confident headroom for the primary pool ([#12972](https://github.com/diegosouzapw/OmniRoute/pull/12972)) ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- **fix(vision):** Custom Models with "Vision capable" checked no longer have image requests swapped to `glm/glm-4.6v` when the client sends the advertised alias (`vllm/path/...`) or the bare path-shaped id — Vision Bridge now matches the stored override for all three id forms ([#12758](https://github.com/diegosouzapw/OmniRoute/issues/12758)) ([#12974](https://github.com/diegosouzapw/OmniRoute/pull/12974)) — thanks @HouMinXi +- **fix(sse):** transient opencode upstream failures rotate to the next account proxy instead of failing, so one flapping egress no longer aborts the whole chain ([#12975](https://github.com/diegosouzapw/OmniRoute/pull/12975)) — thanks @maxmad64bis +- **fix(azure):** Deployments from GPT-6 onward now send `max_completion_tokens` instead of `max_tokens`, which Azure rejects with HTTP 400. The rule matched a literal `gpt-5`, so each new generation arrived broken; it now matches the generation range, while `gpt-35-turbo` still keeps `max_tokens`. ([#13007](https://github.com/diegosouzapw/OmniRoute/pull/13007)) — thanks @ntdatt812 +- **fix(db):** add an opt-in self-restart circuit for sustained critical memory pressure, gate post-cleanup VACUUM behind a minimum freed-rows threshold, and checkpoint the SQLite WAL every 5 minutes with a size guard that escalates to TRUNCATE, so a growing WAL can no longer stall the event loop into a full outage. ([#13011](https://github.com/diegosouzapw/OmniRoute/pull/13011)) — thanks @HouMinXi +- **fix(auth):** an explicit connection pin may probe a quota-disabled row once and re-enable it on success ([#12874](https://github.com/diegosouzapw/OmniRoute/issues/12874)) ([#13017](https://github.com/diegosouzapw/OmniRoute/pull/13017)) — thanks @HouMinXi +- **fix(api):** thread `X-OmniRoute-Fallback-Attempts` through combo chat completions so streaming and non-streaming responses report how many prior legs were attempted ([#13038](https://github.com/diegosouzapw/OmniRoute/pull/13038)) — thanks @HouMinXi +- Restore provider failure classification and credential refresh on non-streaming requests: classify non-2xx failures to lock models on per-model quota exhaustion, update connection rate limits from headers and body, and pass credential refresh handlers to pipeline execution so 401 tokens can be refreshed and retried (#13043). ([#13069](https://github.com/diegosouzapw/OmniRoute/pull/13069)) — thanks @HouMinXi +- **fix(responses):** wrap forced-non-streaming web_search fallback JSON as Responses SSE so Codex still sees `response.completed` ([#13050](https://github.com/diegosouzapw/OmniRoute/pull/13050)) — thanks @HouMinXi +- **fix(antigravity):** Preserve upstream thought-token usage in normalized completion and reasoning token counts ([#13055](https://github.com/diegosouzapw/OmniRoute/pull/13055)) — thanks @pacocartones +- Fix a concurrency-slot leak in the DeepSeek PoW solver: a worker that failed to spawn (for example a missing worker script) never released its slot, so `MAX_CONCURRENT_WORKERS` failures disabled the solver until restart. ([#13097](https://github.com/diegosouzapw/OmniRoute/pull/13097)) — thanks @anhtahaylove +- **fix(acp):** bound the ACP session output buffers — `stdoutBuffer` and `stderrBuffer` now cap at 1 MiB keeping the most recent output behind a visible `[...output truncated...]` marker, and `stderrBuffer` is reset per prompt instead of accumulating for the lifetime of the session. ([#13100](https://github.com/diegosouzapw/OmniRoute/pull/13100)) — thanks @anhtahaylove +- **fix(acp):** release the `stdout`/`exit` listeners and the idle timer that a `sendPrompt` timeout used to leave attached to the `acpManager` singleton, and drop sessions that exited on their own from the session map instead of keeping them forever. ([#13096](https://github.com/diegosouzapw/OmniRoute/pull/13096)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection and PII scanners now read the text a `tool_result` block carries on `content` (string or nested block list), in messages and in system blocks, so tool output is judged by the same rules as user text ([#13101](https://github.com/diegosouzapw/OmniRoute/pull/13101)) — thanks @ntdatt812 +- **fix(gamification):** close the badge notification SSE stream when the request signal is already aborted before the stream starts — a client that disconnects while the route is still awaiting auth used to leave both the 2s unlock poll and the 15s heartbeat running for the lifetime of the process. ([#13106](https://github.com/diegosouzapw/OmniRoute/pull/13106)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection scan now spends its 16 KB budget on both ends of the request instead of the first 16 KB only, so `system`, `instructions`, `query`, `documents` and the newest turns are no longer hidden behind one long message ([#13104](https://github.com/diegosouzapw/OmniRoute/pull/13104)) — thanks @ntdatt812 +- **fix(dashboard):** expose the Volcano Ark console cookie on quota scraping and unwrap connect-error objects so the dashboard shows the upstream message ([#13107](https://github.com/diegosouzapw/OmniRoute/pull/13107)) — thanks @HouMinXi +- **fix(db):** release the `beforeExit`/`SIGINT`/`SIGTERM` handlers when a `node:sqlite` adapter closes, so a closed adapter and its database handle are no longer pinned to `process` for the lifetime of the run — the same treatment #7494 gave the sql.js adapter. ([#13109](https://github.com/diegosouzapw/OmniRoute/pull/13109)) — thanks @anhtahaylove +- **fix(translator):** `contentSchema` and `unevaluatedItems` are now treated as subschema positions by the tool-schema sanitizer, so a truncation placeholder in either is replaced with a permissive schema instead of being forwarded as a string ([#13110](https://github.com/diegosouzapw/OmniRoute/pull/13110)) — thanks @ntdatt812 +- **fix(cli-helper):** clear the `createLogStream` timeout on the abort path — `stop()` aborts the in-flight fetch and returned through the `signal.aborted` branch, which skipped `clearTimeout` and left an armed timer per stopped stream. The stream reader is now also cancelled when the read loop exits early. ([#13114](https://github.com/diegosouzapw/OmniRoute/pull/13114)) — thanks @anhtahaylove +- **fix(docs):** drop TLS-impersonation, MITM-decrypt, supply-chain attestation, and XOR-mask writeups from the public `/docs` catalog and Docker image. Files stay in git for engineers; operators who need them open the repo, not the website. ([#13136](https://github.com/diegosouzapw/OmniRoute/pull/13136)) — thanks @HouMinXi +- **fix(combo):** parse numeric-epoch `rate_limited_until` in the combo cooldown read path ([#13141](https://github.com/diegosouzapw/OmniRoute/pull/13141)) — thanks @maxmad64bis +- **fix(opencode):** both OpenCode plugins now reject a gateway address typed without `http://` at configuration time, instead of publishing every model with an api url no client can call, and the v2 plugin no longer publishes a model card whose api url is blank or relative ([#13142](https://github.com/diegosouzapw/OmniRoute/pull/13142)) — thanks @maxmad64bis +- **fix(providers):** lock opencode model on upstream 400 model-unavailable ([#13146](https://github.com/diegosouzapw/OmniRoute/pull/13146)) — thanks @maxmad64bis +- **fix(logging):** keep the provider exchange rather than the raw client bodies when a call log exceeds its size budget, and show that recovered payload in the request-detail panel instead of replacing it with the stored response body ([#13147](https://github.com/diegosouzapw/OmniRoute/pull/13147)) — thanks @maxmad64bis +- **fix(traffic-inspector):** the WebSocket route no longer leaks a traffic-buffer subscriber and a 30s ping timer when the client socket is already closed at handler time — listeners are attached before any resource is acquired, a destroyed socket bails out early, and the ping interval stops on a dead socket where `write()` never throws ([#13155](https://github.com/diegosouzapw/OmniRoute/pull/13155)) — thanks @anhtahaylove +- **fix(telegram):** bound the per-user API key cache in the Telegram chat proxy so a burst of distinct chat ids can no longer grow the process heap without limit ([#13165](https://github.com/diegosouzapw/OmniRoute/issues/13165)) ([#13166](https://github.com/diegosouzapw/OmniRoute/pull/13166)) — thanks @anhtahaylove +- **fix(stream):** cancel the upstream response body when the JSON-to-SSE sniff unwinds on a body timeout, so a stalled upstream no longer pins the connection ([#13169](https://github.com/diegosouzapw/OmniRoute/issues/13169)) ([#13171](https://github.com/diegosouzapw/OmniRoute/pull/13171)) — thanks @anhtahaylove +- **fix(telegram):** authenticate webhook deliveries with Telegram's `secret_token` so an unauthenticated caller can no longer mint API keys or spend upstream quota ([#13172](https://github.com/diegosouzapw/OmniRoute/issues/13172)) ([#13175](https://github.com/diegosouzapw/OmniRoute/pull/13175)) — thanks @anhtahaylove +- **fix(models):** give discoverable Gemini 3.8 Flash ids their own 65536 output spec so Antigravity no longer clamps them to 16384 ([#13195](https://github.com/diegosouzapw/OmniRoute/pull/13195)) — thanks @HouMinXi +- **fix(dashboard):** leftover catalog-removed provider rows (gemini-cli) can be listed and purged from the providers page ([#13067](https://github.com/diegosouzapw/OmniRoute/issues/13067)) ([#13197](https://github.com/diegosouzapw/OmniRoute/pull/13197)) — thanks @HouMinXi +- **fix(auth):** a dashboard session now requires the `authenticated: true` claim that login, OIDC and the session refresh already emit — a JWT merely signed with `JWT_SECRET` (for example the Cursor CLI passthrough token, which any API-key holder can obtain) no longer verifies as the `auth_token` cookie on any route, the WebSocket handshake or the live server; existing sessions keep working ([#13298](https://github.com/diegosouzapw/OmniRoute/issues/13298)) ([#13375](https://github.com/diegosouzapw/OmniRoute/pull/13375)) +- **fix(api):** `DELETE /v1/batches/delete-completed` now applies the caller's API-key policy (endpoint allowlist, schedule, usage cap, rate limit) like every other `/v1` route, commits the key-scoped sweep per 200-batch chunk instead of holding one write lock for the whole sweep, refuses to loop without progress, and rejects a scope that names both a key and `allTenants` ([#13684](https://github.com/diegosouzapw/OmniRoute/pull/13684)) +- Restore the API-route typecheck gate: the non-streaming leg result lost its discriminated-union narrowing after the server-owned tool loop reassignment, producing 13 new TS2339 diagnostics in `chatCore.ts`. ([#12963](https://github.com/diegosouzapw/OmniRoute/pull/12963)) +- Restore four non-streaming behaviours the server-owned tool loop refactor dropped: client aborts map to 499 with the fixed `Request aborted` message, an aborted request no longer logs a synthetic `clientResponse`, Claude prompt-cache telemetry is recorded again, and a body that cannot be canonicalized no longer throws when the tool loop is off. ([#12990](https://github.com/diegosouzapw/OmniRoute/pull/12990)) +- **fix(skills):** The CLI registry parser now reads positionals declared with `.addArgument()`, not only those written inline in `.command()`. `tunnel create [type]` was being published as `tunnel create`, so the agent-skills sync gate reported drift on every branch and regenerating would have deleted the argument. ([#13009](https://github.com/diegosouzapw/OmniRoute/pull/13009)) — thanks @ntdatt812 +- **fix(combos):** dashboard combo test uses a short prompt, serial probes, and a 60s timeout so reasoning models and rate-limited free pools do not fail the health check ([#13001](https://github.com/diegosouzapw/OmniRoute/pull/13001)) — thanks @HouMinXi +- fix(compression): terminate idle worker threads on eviction so long-running instances stop leaking OS threads and MessagePorts ([#13091](https://github.com/diegosouzapw/OmniRoute/pull/13091)) — thanks @anhtahaylove +- Redact Google API keys of any length in error bodies: the pattern required exactly 39 characters, so shorter or longer `AIza…` credentials passed through unredacted. ([#12964](https://github.com/diegosouzapw/OmniRoute/pull/12964)) +- **fix(grok-cli):** a 402 "Grok Build usage balance exhausted" parks that Grok login as out of credit (Grok Build CLI, grok.com cookie, and xAI OAuth share the weekly pool). Combo routing then tries the next login instead of locking the model for every account in the pool ([#13061](https://github.com/diegosouzapw/OmniRoute/pull/13061)) — thanks @HouMinXi +- fix(compression): spawn the LLMLingua worker with a file URL object so compression actually runs instead of silently failing open on Node ([#13093](https://github.com/diegosouzapw/OmniRoute/pull/13093)) — thanks @anhtahaylove +- **fix(test):** run the local `test` and `test:unit` scripts at concurrency 4 so a full-suite run no longer exhausts the machine's commit charge and kills unrelated processes ([#13187](https://github.com/diegosouzapw/OmniRoute/pull/13187)) — thanks @anhtahaylove +- **fix(monitoring):** `GET /api/monitoring/health` `credentialHealth` now includes a bounded `failedConnections` list (`connectionId`, `status`, sanitized `lastError`) when the probe-cache gauge `failed>0`, plus `source: probe-cache` and a cheap `staleDbNonOkCount` for sticky SQLite `test_status` on active rows. Documents that the live gauge is not `provider_connections.test_status`. ([#12876](https://github.com/diegosouzapw/OmniRoute/pull/12876)) — thanks @RaviTharuma +- **fix(resilience):** an openai-compatible multi-upstream gateway no longer marks the whole connection `credits_exhausted` when a single upstream (or the `/models` / representative-model health probe) returns 402. Quota failures stay model-scoped; true connection-wide auth failures are unchanged. Single-credential 402 key disable ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239)) is preserved. ([#12875](https://github.com/diegosouzapw/OmniRoute/pull/12875)) — thanks @RaviTharuma +- **fix(dashboard):** nine fixes on the Orchestration Canvas, all diagnosed in the Phase 2 reviews. A source that fails now keeps the timestamp of its FIRST failure instead of being re-stamped every poll — the stale line said "since the last poll" no matter how long the source had been down, and the churn also defeated the snapshot's stable identity (it serializes the source list), so the canvas re-rendered on every tick while anything was broken. A source that HAD data and only then started failing is flagged too: previously only a source with no node at all got the warning, so a source that went down mid-session kept a healthy-looking node forever. The rest are pointwise: clicking a filter chip cancels the pending search debounce (left armed, it fired ~300ms later and silently reverted the chip); the search input carries an accessible name; `?state=running, failed` parses like the unpadded form instead of dropping the padded value; the CSV toggle helper is defined once in `model/urlParams.ts` rather than twice; the agents tab tells "nothing running" apart from "the filter matched nothing", offering a clear-filters button instead of setup links that would be wrong advice there; edges stop emitting SMIL particles above 40 simultaneously active edges, keeping the colored stroke; and the drawer's error banner clears when a retried action succeeds. ([#12983](https://github.com/diegosouzapw/OmniRoute/pull/12983)) +- **fix(i18n):** the nine locales added with the EU-language batch (Greek, Estonian, Irish, Croatian, Lithuanian, Latvian, Maltese, Slovenian, Serbian) were missing the eleven Orchestration Canvas keys that Phase 3 introduced, so the compare-runs panel and the "no runs match these filters" empty state fell back to English in those languages (`deepMergeFallback` substitutes English for an absent key, so nothing rendered blank — it rendered untranslated). The coverage gate does not catch this: it enforces an 80% floor per locale, and eleven missing keys out of ~13,000 leaves coverage at 99.9%. Translated for real in each language, calibrated against the wording each file already uses for "run", "filter" and "skill". ([#13294](https://github.com/diegosouzapw/OmniRoute/pull/13294)) +- **fix(dashboard):** five follow-ups from the Phase 2 Orchestration Canvas review. The History drawer now shows the memory section for runs that already left the live TTL window: the persisted `memory_hits` event is parsed into `metadata.memoryHits` with the same defensive validation the drawer applies, and — because that event is observability rather than a state transition — it no longer leaks into the timeline, where it had been inheriting the task's state and rendering as a duplicate transition. The A2A memory recall runs against its own 1.5s deadline instead of inheriting the memory backend's 30s one; overshooting degrades exactly like any other recall failure (no hits, task proceeds), and the timer is cleared on both paths. Repeating a Conductor run carries its `requirements.cli`/`requirements.model` forward, so the new run is pinned to the same runner profile and model rather than drifting to whatever the fleet picks. A successful repeat from the Agents tab now focuses the run it created, instead of leaving the operator on the finished one. And the auth test for `POST /api/conductor/tasks` moved into the shared `ROUTES` array rather than restating the pattern. ([#12988](https://github.com/diegosouzapw/OmniRoute/pull/12988)) +- fix(plugins): stop leaking an exit listener per plugin hook timeout, which triggered MaxListenersExceededWarning on plugins that ignore SIGTERM ([#13092](https://github.com/diegosouzapw/OmniRoute/pull/13092)) — thanks @anhtahaylove +- **fix(validation):** Provider node edits no longer fail with a generic "Invalid request" when the optional daily-quota reset fields are left blank. The dashboard sends `dailyQuotaResetTimezone` and `dailyQuotaResetHour` as `null`, and only the hour accepted it. ([#13066](https://github.com/diegosouzapw/OmniRoute/issues/13066)) ([#13083](https://github.com/diegosouzapw/OmniRoute/pull/13083)) — thanks @ntdatt812 +- **fix(combo):** quota-aware expansion drops banned, inactive, missing, and wrong-provider connections before quota fetch or model dispatch; pins and allowlists stay selectors, not a bypass. Antigravity automatic exhaustion now requires a reported zero remaining, so a positive balance below 1% stays eligible. ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- Fix an intermittent failure in the quota-weighted routing test suite: scores are a function of `Date.now()`, so peers with identical quota scored microseconds apart never tied and swapped order. ([#12946](https://github.com/diegosouzapw/OmniRoute/pull/12946)) +- fix(volcengine): resolve quota window mapping gaps, safe multi-connection console binding, and response cookie sanitization ([#12950](https://github.com/diegosouzapw/OmniRoute/pull/12950)) — thanks @HouMinXi +- **fix(test):** resolve the WebDAV handler path with `fileURLToPath` so the suite's 37 WebDAV tests run on Windows instead of failing with a doubled `C:\C:\` drive prefix ([#13196](https://github.com/diegosouzapw/OmniRoute/pull/13196)) — thanks @anhtahaylove +- **fix(ui):** fall back from invalid AnySearch icon alias (direct commit `9587e07b69`) +- **fix(sse):** stop rebuilding a truncated summary from the collector's cap-dropped event array ([#12718](https://github.com/diegosouzapw/OmniRoute/pull/12718)) — thanks @hartmark +- **fix(conversations):** show a pending spinner for unresolved tool nodes instead of "(empty)" ([#12727](https://github.com/diegosouzapw/OmniRoute/pull/12727)) — thanks @hartmark +- **fix(streaming):** fail fast when an upstream stream produces only lifecycle/heartbeat events, never real content ([#12741](https://github.com/diegosouzapw/OmniRoute/pull/12741)) — thanks @hartmark +- **fix(responses-continuation):** bridge the write-in-flight window with an in-memory pending store ([#12854](https://github.com/diegosouzapw/OmniRoute/pull/12854)) — thanks @hartmark +- **fix(translator):** drop or map agent_message on Chat Completions fallback ([#12880](https://github.com/diegosouzapw/OmniRoute/pull/12880)) — thanks @RaviTharuma +- **fix(api):** page and stream-complete GET /v1/models for large catalogs ([#12882](https://github.com/diegosouzapw/OmniRoute/pull/12882)) — thanks @RaviTharuma +- **fix(quota):** align AUTH window labels with usage API and clarify 503 ([#12884](https://github.com/diegosouzapw/OmniRoute/pull/12884)) — thanks @RaviTharuma +- **fix(ci):** clear two base-reds on release/v3.8.51 — mutation-coverage gate + image-only-model guard ([#12945](https://github.com/diegosouzapw/OmniRoute/pull/12945)) +- **fix(api):** scope batch bulk-delete to the calling API key ([#13211](https://github.com/diegosouzapw/OmniRoute/pull/13211)) +- **fix(tests):** align model-sync log assertion to shipped path redaction (base-red) ([#13227](https://github.com/diegosouzapw/OmniRoute/pull/13227)) +- **fix(test):** make the ToS heading guard actually require the parentheses ([#13228](https://github.com/diegosouzapw/OmniRoute/pull/13228)) +- **fix(quality):** register 4 drifted covering tests in stryker tap.testFiles ([#13229](https://github.com/diegosouzapw/OmniRoute/pull/13229)) +- **fix(logging):** mask *-api-key headers in request-log pipeline (#13273) ([#13401](https://github.com/diegosouzapw/OmniRoute/pull/13401)) — thanks @KooshaPari +- **fix(tests):** correct stale Jina catalog prefix in custom-models test ([#13403](https://github.com/diegosouzapw/OmniRoute/pull/13403)) — thanks @KooshaPari +- **fix(backend):** prune old backups after health-check-repair snapshots (#13308) ([#13404](https://github.com/diegosouzapw/OmniRoute/pull/13404)) — thanks @KooshaPari +- **fix(backend):** guard memory_fts_au trigger with WHEN clause to stop FTS bloat (#13326) ([#13405](https://github.com/diegosouzapw/OmniRoute/pull/13405)) — thanks @KooshaPari +- **fix(free-tiers):** remove obsolete Together $25 signup credit (#12526) ([#13407](https://github.com/diegosouzapw/OmniRoute/pull/13407)) — thanks @KooshaPari +- **fix:** skip non-chat generation models in Test all models ([#13409](https://github.com/diegosouzapw/OmniRoute/pull/13409)) — thanks @KooshaPari +- **fix(providers):** de-duplicate agy and antigravity model catalogs ([#13410](https://github.com/diegosouzapw/OmniRoute/pull/13410)) — thanks @KooshaPari +- **fix(dashboard):** send provider in body for mini-playground web search ([#13411](https://github.com/diegosouzapw/OmniRoute/pull/13411)) — thanks @KooshaPari +- **fix(resilience):** add CJK quota-exhaustion patterns to 429 classifier ([#13412](https://github.com/diegosouzapw/OmniRoute/pull/13412)) — thanks @KooshaPari +- **fix(backend):** eval runner: failed calls no longer score as passed + dotAll regex ([#13413](https://github.com/diegosouzapw/OmniRoute/pull/13413)) — thanks @KooshaPari +- **fix(backend):** eval runner opts out of output-style and memory injection ([#13414](https://github.com/diegosouzapw/OmniRoute/pull/13414)) — thanks @KooshaPari +- **fix:** chain serial quarantine step in npm test script (fixes #13286) ([#13418](https://github.com/diegosouzapw/OmniRoute/pull/13418)) — thanks @KooshaPari +- **fix(antigravity):** derive stable session ID from credentials for prompt-cache reuse (#13305) ([#13424](https://github.com/diegosouzapw/OmniRoute/pull/13424)) — thanks @KooshaPari +- **fix(memory):** merge memory into top-level system field instead of unshifting at messages[0] (#13425) ([#13427](https://github.com/diegosouzapw/OmniRoute/pull/13427)) — thanks @KooshaPari +- **fix(compression):** preserve instruction blocks from lossy engines ([#13523](https://github.com/diegosouzapw/OmniRoute/pull/13523)) — thanks @KooshaPari +- **fix(compression):** preserve polarity words and newlines in ultra heuristic ([#13524](https://github.com/diegosouzapw/OmniRoute/pull/13524)) — thanks @KooshaPari +- **fix(compression):** run user preservePatterns before built-in patterns (#13457) ([#13525](https://github.com/diegosouzapw/OmniRoute/pull/13525)) — thanks @KooshaPari +- **fix(reasoning):** expand deepseek-v4 regex to match v4.1+ and add command-code provider (#13430) ([#13528](https://github.com/diegosouzapw/OmniRoute/pull/13528)) — thanks @KooshaPari +- **fix(providers):** add alwaysReasons metadata for reka-flash-3 ([#13531](https://github.com/diegosouzapw/OmniRoute/pull/13531)) — thanks @KooshaPari +- **fix(playground):** allow Compare columns to scroll independently ([#13532](https://github.com/diegosouzapw/OmniRoute/pull/13532)) — thanks @KooshaPari +- **fix(security):** empty whitelist allows through to prevent admin lockout ([#13534](https://github.com/diegosouzapw/OmniRoute/pull/13534)) — thanks @KooshaPari +- **fix(tests):** add DISABLE_IOREG_STRATEGY env var for macOS test compatibility (fixes #13467) ([#13539](https://github.com/diegosouzapw/OmniRoute/pull/13539)) — thanks @KooshaPari +- **fix(zed-hosted):** map developer role and Gemini enums for Zed proxy ([#13543](https://github.com/diegosouzapw/OmniRoute/pull/13543)) — thanks @KooshaPari +- **fix(analytics):** normalize provider aliases to canonical id in usage_history ([#13545](https://github.com/diegosouzapw/OmniRoute/pull/13545)) — thanks @KooshaPari +- **fix(combo):** use traceId for call log id to prevent UNIQUE constraint failure ([#13546](https://github.com/diegosouzapw/OmniRoute/pull/13546)) — thanks @KooshaPari +- **fix(auth):** check model lockout before returning synthetic noauth connection ([#13547](https://github.com/diegosouzapw/OmniRoute/pull/13547)) — thanks @KooshaPari +- **fix(guardrails):** skip credential redaction for base64 image data URLs ([#13550](https://github.com/diegosouzapw/OmniRoute/pull/13550)) — thanks @KooshaPari +- **fix(resilience):** increase requestQueue.maxWaitMs default from 15s to 30s ([#13553](https://github.com/diegosouzapw/OmniRoute/pull/13553)) — thanks @KooshaPari +- **fix(docs):** document OMNIROUTE_READY_TIMEOUT_MS and allowlist the test-only DISABLE_IOREG_STRATEGY ([#13692](https://github.com/diegosouzapw/OmniRoute/pull/13692)) + ### 📝 Maintenance ---- +- **docs(readme):** reconcile live v3.8.50 provider, free-tier, CLI, routing, test, community, sponsor, acknowledgment, and SVG metrics with their audited source denominators, including a deduplicated OmniRoute-in-Action snapshot and distinct contributor rankings for merged pull requests, GitHub-attributed commits, and Git history ([#11356](https://github.com/diegosouzapw/OmniRoute/pull/11356)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **docs:** sync the canonical provider count 354 → 356 across `README.md`, `AGENTS.md`, `llm.txt` (+ 42 i18n mirrors), the four README SVG diagrams, `docs/reference/PROVIDER_REFERENCE.md` (regenerated) and the `package.json` description after Opper (#11629) and 1min.ai (#11631) boarded the catalog — closes the `check:docs-counts-sync` strict drifts that kept `release/v3.8.51` red ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11673](https://github.com/diegosouzapw/OmniRoute/pull/11673)) — thanks @jonlwheat2-gif +- **test(providers):** update count-derived assertions after the v3.8.51 provider additions — `APIKEY_PROVIDERS` 233 → 235 (Opper #11629 + 1min.ai #11631), reserved-prefix REGISTRY walk 395 → 398, `WEB_FETCH_PROVIDERS` now includes `nimble-search` (#11620), and the provider translate-path golden snapshot regenerated ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11674](https://github.com/diegosouzapw/OmniRoute/pull/11674)) — thanks @jonlwheat2-gif +- **test(sse):** bump the hard-lease connection-query inventory for `src/lib/tokenHealthCheck.ts` to 2 — the verify-only web-cookie sweep added by #11495 split the single `getProviderConnections` call into oauth + cookie variants, which the frozen inventory had not tracked ([#11495](https://github.com/diegosouzapw/OmniRoute/pull/11495)). ([#11677](https://github.com/diegosouzapw/OmniRoute/pull/11677)) — thanks @jonlwheat2-gif / @oyi77 +- **chore(release):** clear the `release/v3.8.51` base-red gates — docs migration count (+38 i18n mirrors), stryker `tap.testFiles`, lockfile registry host, stale ESLint suppressions, the 7 lint regressions they were hiding, the stale `APIKEY_PROVIDERS` partition count (231 → 233), the `glm-5.3-max` tier missing from the GLM test table, a dead `modelType === "chat"` comparison in the models catalog, and the `resolveLiveWsUrl` / `sanitizeLiveWsPort` helpers from #11388 that a later merge dropped from `wsPath.ts` (dashboard live WebSocket URL resolution) ([#11502](https://github.com/diegosouzapw/OmniRoute/pull/11502)) — thanks @MumuTW +- **chore(free-models):** the free-tier regime table now declares which totals figure each regime feeds and whether it may take the no-auth shortcut, so the sets that used to repeat those answers by hand are derived from it — a new `freeType` no longer compiles until it has answered every question, instead of silently contributing to no total ([#11537](https://github.com/diegosouzapw/OmniRoute/pull/11537)) — thanks @maxmad64bis +- **docs(routing):** LKGP strategy copy now states provider-level stickiness and the explicit rules fallback instead of promising per-target pinning ([#11555](https://github.com/diegosouzapw/OmniRoute/pull/11555)) — thanks @maxmad64bis +- **test(chatcore):** pin the response tool-name alias resolution against the ordering hazard that broke every Gemini/Antigravity MCP tool call in v3.8.49. `extractRequestToolIdentityMap` deletes `translatedBody._toolNameMap`, so the later read is always undefined and the ledger survives only through the `requestToolIdentityMap` fallback — removing that fallback previously left the entire tool-name suite green. The resolution moves into `resolveResponseToolNameMap()` next to the map it depends on, with a regression guard that fails without the recovery. No behaviour change. ([#11575](https://github.com/diegosouzapw/OmniRoute/pull/11575)) — thanks @amartinawi +- **docs(env):** document `OMNIROUTE_CORPUS_CACHE_SIZE` in `.env.example` and `ENVIRONMENT.md`, so the env/docs contract check stops failing Docs Gates on every branch ([#11576](https://github.com/diegosouzapw/OmniRoute/pull/11576)) — thanks @ntdatt812 +- **chore(stryker):** register `route-guard-tunnel-processes-local-only` and `universal-quota-aware-routing` in `tap.testFiles`, so their mutant kills count for the three modules they cover ([#11577](https://github.com/diegosouzapw/OmniRoute/pull/11577)) — thanks @ntdatt812 +- **test(authz):** pin `LOCAL_ONLY_API_GET_EXEMPTIONS` by exact membership instead of by entry count, so the guard names the offending path and also catches a substitution ([#11580](https://github.com/diegosouzapw/OmniRoute/pull/11580)) — thanks @ntdatt812 +- **test(build):** pin `resolveNextBuildBundlerFlag` to the `OMNIROUTE_USE_TURBOPACK` contract it implements, across runtimes, instead of asserting an unimplemented Bun override ([#11583](https://github.com/diegosouzapw/OmniRoute/pull/11583)) — thanks @ntdatt812 +- **test(claude-web):** the Claude Web transport test's barrel anchors no longer pin the exact construction spelling. #11421 made the executor map lazy, so `/"claude-web": new ClaudeWebExecutor\(\)/` stopped matching and the test went red on `release/v3.8.51` itself — a base-red gate every open PR inherited. The anchors now match the alias key and the class name and tolerate either spelling in between, and were checked to still fail when an alias is genuinely repointed at another executor ([#11585](https://github.com/diegosouzapw/OmniRoute/pull/11585)) — thanks @ntdat812 +- **test(models):** pin `qwen3.8-max` resolution per provider — two of the four catalogs now serve the bare id and no longer carry `-preview` ([#11590](https://github.com/diegosouzapw/OmniRoute/pull/11590)) — thanks @ntdatt812 +- **test(executors):** drop the stale 128-tool truncation assertion that contradicted the #11444 pass-through contract ([#11591](https://github.com/diegosouzapw/OmniRoute/pull/11591)) — thanks @ntdatt812 +- **test(token-health):** pin the current terminal-skip boundary — expired with retry budget is probed, `account_deactivated` stays skipped ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)) — thanks @ntdatt812 +- **test(models):** follow #11503's retarget of the `gemini-3-pro-high` alias to the hyphenated catalog id ([#11593](https://github.com/diegosouzapw/OmniRoute/pull/11593)) — thanks @ntdatt812 +- **refactor(video bridge):** extract per-video acquisition, whole-result caching, description, and metrics/abort/cleanup out of `VideoBridgeGuardrail.preCall` into a `processVideoPart` seam in a new `videoBridgePipeline.ts`, behind explicit `VideoMediaBrokerPort`, `VideoAudioTranscriptionPort`, and `VideoDrilldownPort` boundaries; `preCall` now only handles request traversal, policy, and response aggregation. The Video tab's FFmpeg/ffprobe runtime status is now an explicit `unknown` / `restricted` / `unavailable` / `available` state instead of a nullable boolean pair, fixing a case where an in-flight or failed probe was mislabeled as "install FFmpeg" ([#11657](https://github.com/diegosouzapw/OmniRoute/issues/11657)). ([#12016](https://github.com/diegosouzapw/OmniRoute/pull/12016)) +- **test(opencode-plugin):** add unit test coverage for `formatFreeBudget()` naming helper ([#11660](https://github.com/diegosouzapw/OmniRoute/pull/11660)) — thanks @f9td56dbgh-hub +- **docs(video):** clarify that the Video Bridge transcript `source` field (`client`, `embedded`, `audio-bridge`) is presently caller-declared and not yet server-verified — OmniRoute enforces the enum shape but does not cryptographically confirm that an `embedded`/`audio-bridge` label came from a server-owned extraction ([#11661](https://github.com/diegosouzapw/OmniRoute/issues/11661)). ([#12001](https://github.com/diegosouzapw/OmniRoute/pull/12001)) +- **chore(providers):** retire the distributed Raycast Relay and Hailuo Web integrations after a provenance audit confirmed substantial derivation from GPL-3.0 sources; the official GitHub and MiniMax API providers remain available. Migration 163 disables legacy connections and invalidates their active leases while preserving connection IDs, audit/history rows, quota snapshots, usage records, and fail-closed API-key connection policies. ([#11691](https://github.com/diegosouzapw/OmniRoute/pull/11691)) +- **chore(providers):** retire the Felo Web provider while its GPL-derived provenance remains on licensing HOLD ([#11698](https://github.com/diegosouzapw/OmniRoute/pull/11698)) — stale runtime and database references now fail closed while existing connection, lease, and usage history remains preserved for auditability. +- **fix(sse):** retire the Gemini Web image-generation bridge while provenance and licensing remain on HOLD, preserving Gemini Web chat/auth/tools and legitimate Gemini/Nano Banana image providers ([#11708](https://github.com/diegosouzapw/OmniRoute/pull/11708)) +- **chore(audio):** retire the built-in EdgeTTS provider and its 11-voice catalog while provenance/licensing review remains on HOLD; the generic `/v1/audio/speech` endpoint and the other speech providers remain available ([#11711](https://github.com/diegosouzapw/OmniRoute/pull/11711)) +- **fix(providers):** retire the unofficial Qwen Web integration and its `qwen-web`/`qw` identities while third-party provenance remains on HOLD ([#11713](https://github.com/diegosouzapw/OmniRoute/pull/11713)) — direct requests now fail closed with a sanitized HTTP 410 and combos skip the retired target, while migration guards preserve connection IDs, allowlists, usage/audit history and invalidate active leases. Supported Qwen API, Qwen Code, cloud, token-plan, audio, local and model-name surfaces remain available; restoring Qwen Web requires a qualified provenance/license decision. +- **fix(providers):** retire `microsoft-designer-web` and its `msdesigner` alias from runtime and catalogs while preserving stored credentials, provider history and allowlists as inactive records — source dependency on gpt4free's [`MicrosoftDesigner.py`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/g4f/Provider/needs_auth/MicrosoftDesigner.py) is proven at snapshot `0943d16eef757a44deb60651c07bbe5fa958cafe`, whose [`LICENSE`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/LICENSE) is GPL-3.0; expressive adaptation is probable, which records provenance/licensing risk without asserting a legal conclusion ([#11720](https://github.com/diegosouzapw/OmniRoute/pull/11720)). +- **docs(legal):** centralize pinned MIT notices for the vendored GCF codec and local flag assets ([#11726](https://github.com/diegosouzapw/OmniRoute/pull/11726)) +- Split the 2,952-line `adobeFireflyClient.ts` into nine focused modules (catalog, credentials, payload, ARP session, upload, responses, IMS, account, poll) behind an unchanged barrel — no consumer edits and no public API change (#11728). — thanks @MumuTW +- **fix(dashboard):** replaced the Model Lockout card's unprovenanced notification MP3 with a short, locally synthesized Web Audio chime ([#11731](https://github.com/diegosouzapw/OmniRoute/pull/11731)) — both toggles retain optional audible feedback without shipping replacement media or third-party code, and unsupported, suspended, or rejected audio contexts fail open without blocking the setting change. +- **chore(deepseek-web):** remove the provenance-unresolved DeepSeek PoW WASM binary and its runtime loader/tracing while retaining the existing JavaScript solver (slower at high difficulty) ([#11732](https://github.com/diegosouzapw/OmniRoute/pull/11732)). +- **chore(assets):** remove four unused provider assets whose redistribution provenance could not be verified while retaining the active Freebuff SVG ([#11735](https://github.com/diegosouzapw/OmniRoute/pull/11735)) +- **docs(assets):** retired two raster images whose redistribution provenance could not be verified ([#11736](https://github.com/diegosouzapw/OmniRoute/pull/11736)) — PicoClaw documentation now reuses the existing SVG, while Zoo Code remains a text link without an image. +- **docs(legal):** add pinned notices for six LobeHub-derived provider SVGs and 65 byte-exact theSVG assets, retaining registry claims as unverified metadata with explicit trademark and HOLD boundaries ([#11737](https://github.com/diegosouzapw/OmniRoute/pull/11737)). +- **chore(quality):** added a versioned 225/225 provider-asset provenance manifest and blocking drift gate ([#11743](https://github.com/diegosouzapw/OmniRoute/pull/11743)) — the inventory pins magic MIME and SHA-256 for every physical file, records immutable source matches for 71 assets, keeps 69 probable and 85 unresolved entries explicit but non-blocking, and treats provenance as source evidence rather than copyright or trademark clearance. +- **refactor(mitm):** replace four previously derived Traffic Inspector utilities — conversation normalization, SSE merging, secret masking, and header sanitization — with independent clean-room implementations based on public provider schemas, WHATWG SSE, and RFC 6750, while keeping the two independently classified passthrough surfaces unchanged ([#11748](https://github.com/diegosouzapw/OmniRoute/pull/11748)). +- Added a unit test that fails seven days before any dated pack under `config/` (`validUntil` and sibling keys) lapses, naming the file and key. The Alibaba free-tier pack expired on 2026-08-27 and turned every PR red the next morning with no commit involved; renewal now happens on someone's terms, not the clock's. ([#11891](https://github.com/diegosouzapw/OmniRoute/pull/11891)) +- `check:workflows` now fails (under `--strict`/`--ratchet`) when any job routed to a self-hosted runner publishes with `--provenance` — npm rejects that with `422` at the registry, which in v3.8.50 only surfaced after the tag and Docker images were public. ([#11895](https://github.com/diegosouzapw/OmniRoute/pull/11895)) +- `scripts/ops/runner-janitor.sh` now proves a path is idle with one `lsof` snapshot and removes stale leftovers itself (tmpfs after 3 h — it is RAM — disk after 24 h), kills orphan `next-build` processes, prunes checkouts of stopped runners, and alerts on memory pressure; `--dry-run` shows exactly what it would do. `docs/ops/RUNNER_BOX.md` reconciled to the measured box (31 GB, 10 listeners). ([#11893](https://github.com/diegosouzapw/OmniRoute/pull/11893)) +- The `next-build` artefact (1.3 GB) is now written and read under `$RUNNER_TEMP` (per-runner, on disk) instead of `/tmp`, which on the self-hosted pool is a 12 GB tmpfs in RAM. Landing it there took 27–32 of the publish job's 76 minutes, and the fixed `/tmp/e2e-build.tar.gz` name let E2E jobs on different runners overwrite each other's download. ([#11896](https://github.com/diegosouzapw/OmniRoute/pull/11896)) +- The CI `build` job now runs in two concurrency lanes — `main` and pull requests — so a release build is never queued behind (or OOM-killed beside) PR builds on the self-hosted pool, which holds one `next-build` comfortably and two at the edge. ([#11901](https://github.com/diegosouzapw/OmniRoute/pull/11901)) +- Re-freeze the ESLint suppressions on `release/v3.8.51` from a clean-room measurement (2 stale file entries pruned, 55 pre-existing `no-explicit-any` in six new files frozen under #11924) and drop the dead `GPT_SIZE_MAP` constant orphaned by the Adobe Firefly client split, so `No new ESLint warnings` stops failing every PR with exit 2 (Refs #11924) ([#11955](https://github.com/diegosouzapw/OmniRoute/pull/11955)) +- Type the 55 `no-explicit-any` sites that had been frozen under #11924 — four redundant casts in `socksConnectorWithFamily.ts` (undici/socks types already accept them) and the mocks/fixtures of the socks-timeout and isFree suites — and drop their suppression entries; the ESLint ratchet shrinks from 5487 to 5432 (Closes #11924) ([#11975](https://github.com/diegosouzapw/OmniRoute/pull/11975)) +- Take the two hosted-runner builds off the PR rail: `Build App` (`build.yml`) is `workflow_dispatch`-only and `DAST smoke (PR)` runs only for PRs into `main` — the 7 GB hosted VM cannot build `release/v3.8.51` in any profile (19/30 red, VM shutdown ~8 min into `next build`) and both checks had turned into permanent noise on every release PR; the bundle stays validated by `ci.yml` on `main` and by `nightly-release-green` on `release/**` (Closes #11946) ([#11962](https://github.com/diegosouzapw/OmniRoute/pull/11962)) +- Move the four nightly jobs that build the backend (`nightly-schemathesis`, `nightly-llm-security` promptfoo + garak, `nightly-resilience` axe-a11y) off the hosted 7 GB runner — where they died on `release/v3.8.51` unseen — onto the box's new `omni-light` pool (two listeners), and document the reshaped fleet (4 active OmniRoute listeners: 2 `omni-build` + 2 `omni-light`, janitor ceiling 4) (Closes #11965) ([#11967](https://github.com/diegosouzapw/OmniRoute/pull/11967)) +- Stop painting every fork PR into `release/**` red: `quality.yml` `Build (advisory)` is skipped (GitHub still reports `continue-on-error` failures as check FAILURE). Hosted `ubuntu-latest` cannot finish `npm run build` on this tree — same class as #11962 taking `build.yml` off the PR rail. `docker-publish.yml` amd64 now runs on the `.113` `omni-build` pool (31 GB / 32 cores, two listeners) with Turbopack, shares the `heavy-build-main` lane with `ci.yml` `Build` so it queues instead of becoming a third heavy, and keeps per-ref concurrency (a merge storm was starting 8 concurrent OOM builds). arm64 stays on `ubuntu-24.04-arm` with webpack — there is no ARM box. Fallback when `USE_VPS_RUNNER` is off: hosted amd64 + webpack (#11976). ([#12021](https://github.com/diegosouzapw/OmniRoute/pull/12021)) +- `docker-publish.yml` builds with webpack (`OMNIROUTE_USE_TURBOPACK=0`) on **both** arches, including amd64 on the `.113` `omni-build` pool. Turbopack had the RAM (31 GB, `omniroute-113-6`) and still panicked (`TurbopackInternalError: there must be a path to a root`, run 33253576569, 26 min in). The same tree's arm64 webpack build on hosted `ubuntu-24.04-arm` succeeded (run 33264823398). Dockerfile already documents webpack as the Docker escape hatch (#11976). ([#12050](https://github.com/diegosouzapw/OmniRoute/pull/12050)) +- **docs(free-tier):** declare the counting vs deciding regimes for "is it free?" and guard the deciding path from DB-backed catalog resolution ([#12226](https://github.com/diegosouzapw/OmniRoute/pull/12226)) — thanks @maxmad64bis +- **docs(auto-combo):** The mode pack table in `docs/routing/AUTO-COMBO.md` now lists all six shipped packs with every weight each one sets, replacing a four-pack table whose numbers had also drifted from the source. It states plainly that no pack sets `quality`, so selecting any pack silences the observed-quality signal. Six more documents that quote the scoring factor count joined the `check:docs-counts` gate, which caught five stale claims — including one naming nine factors that do not exist — and two stale mode pack counts. The dashboard routing panel, which offered four of the six packs and labelled the default strategy "6-Factor Scoring", is now covered by a test; the two packs it was missing are `reliability-first` and `chaos-mode`, the latter labelled as the fault-injection profile it is rather than as one more routing preference ([#12316](https://github.com/diegosouzapw/OmniRoute/pull/12316)) — thanks @maxmad64bis +- **chore(quality):** rebaseline `src/lib/db/apiKeys.ts` for the ACL the key-creation path now preserves ([#12352](https://github.com/diegosouzapw/OmniRoute/pull/12352)) ([#12673](https://github.com/diegosouzapw/OmniRoute/pull/12673)) — thanks @KrzysiekSko +- **chore(providers):** bump the Claude Code wire identity and the Devin bridge image pin from `2.1.220` to `2.1.258` ([#12402](https://github.com/diegosouzapw/OmniRoute/pull/12402)) — thanks @ggiak ([#12604](https://github.com/diegosouzapw/OmniRoute/pull/12604)) +- **docs(env):** align `.env.example`, the README Bun section, and the troubleshooting guide with the code: `OMNIROUTE_USE_TURBOPACK` also governs `npm run build` (not dev-only), `bun run build` follows that flag instead of auto-selecting Webpack, `OMNIROUTE_CHAT_MAX_HEAVY_IN_FLIGHT` is unset by default (no request-count cap), and the structural `503 chat_admission_busy` message matches `chatAdmissionResponses.ts` (#12404 — thanks @pacocartones) +- **chore(electron):** upgrade the desktop app to Electron 44 (Chromium 152, Node 24.18.1) ([#12217](https://github.com/diegosouzapw/OmniRoute/pull/12217)). **Requires macOS 13 (Ventura) or later** — Chromium dropped macOS 12 (Monterey), so Monterey users must stay on an earlier OmniRoute desktop build. Windows and Linux are unaffected; the app already shipped only x64/arm64, so Electron 44 dropping 32-bit builds changes nothing. Removes the `openAsHidden`/`wasOpenedAsHidden` login-item fields deleted in Electron 44 — hidden autostart continues to work through the `--hidden` argument registered with the login item ([#12554](https://github.com/diegosouzapw/OmniRoute/pull/12554)) — thanks @app/dependabot +- **chore(quality):** rebaseline `src/sse/handlers/chat.ts` for the effective-input persistence the continuation fix needs ([#12641](https://github.com/diegosouzapw/OmniRoute/pull/12641)) ([#12680](https://github.com/diegosouzapw/OmniRoute/pull/12680)) — thanks @hartmark +- `Coverage` job on `ci.yml`: the informational Codecov upload gets its own 5-minute ceiling and `continue-on-error`, and the job budget grows from 20 to 30 minutes (the 8-shard c8 merge alone takes ~10) — a stalled upload no longer ends the job `cancelled` and drags a fully green `main` run's conclusion down with it ([#11972](https://github.com/diegosouzapw/OmniRoute/pull/11972)) +- CI hardening on the PR rail: the four `Unit Tests fast-path` shards get `timeout-minutes: 30` (a hung shard held a PR for 64 min instead of GitHub's 6 h default) and both ESLint file caches lose their `restore-keys` fallback, so a cache built under another suppressions file or lint config can no longer report stale verdicts (Refs #11600, #11924) ([#11963](https://github.com/diegosouzapw/OmniRoute/pull/11963)) +- Every CI job that runs a `next build` (`build`, the npm `publish`, both release-green validations) now targets the `omni-build` runner label, which only two of the eight self-hosted runners carry. The box holds one build comfortably and two at the edge; a third now queues on GitHub instead of being OOM-killed by the kernel. ([#11932](https://github.com/diegosouzapw/OmniRoute/pull/11932)) +- **refactor(combo):** move `handleRoundRobinCombo` (and `resolveTargetTokenLimit`) into `open-sse/services/combo/roundRobinCombo.ts`. `combo.ts` drops from 2164 to 1014 lines (`split("\n").length`); the leaf is 1199 (under the 1200 new-file cap). The round-robin call site uses a dynamic `import()` so `releaseStickyPinOnFailure` / `clearStaleLKGP` can stay exported from `combo.ts` without a static cycle. Skip / sticky / semaphore / safety-timer behavior is unchanged. ([#12811](https://github.com/diegosouzapw/OmniRoute/pull/12811)) — thanks @HouMinXi +- **chore(quality):** rebaseline the file-size caps the error-boundary campaign grew past (`open-sse/executors/codex.ts`, `open-sse/vendor/codex-chatgpt-web/bridge.ts`, both via [#12444](https://github.com/diegosouzapw/OmniRoute/pull/12444)) ([#12654](https://github.com/diegosouzapw/OmniRoute/pull/12654)) +- **chore(quality):** rebaseline the file-size caps the hartmark batch grew past (`combos/page.tsx` via [#12355](https://github.com/diegosouzapw/OmniRoute/pull/12355), `open-sse/services/combo.ts` via [#12338](https://github.com/diegosouzapw/OmniRoute/pull/12338)) ([#12623](https://github.com/diegosouzapw/OmniRoute/pull/12623)) — thanks @hartmark +- **chore(quality):** rebaseline the file-size caps the HouMinXi batch grew past when its PRs stacked (`providers/page.tsx`, `chatCore.ts`, `accountFallback.ts`) — each PR measured correctly in isolation, none saw the stacking ([#12619](https://github.com/diegosouzapw/OmniRoute/pull/12619)) +- **chore(quality):** rebaseline `open-sse/services/combo.ts` for the reset-aware scoring the HouMinXi batch stacked ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) ([#12678](https://github.com/diegosouzapw/OmniRoute/pull/12678)) — thanks @HouMinXi +- **docs:** sync provider catalog count (direct commit `4f11b2ae3d`) +- **docs(skills):** sync omni-inference search fallback (direct commit `b4e76a7ed9`) +- **chore(lint):** prune two stale base suppressions (direct commit `1325047d56`) +- **test(auto-combo):** tolerate full-suite rotation load (direct commit `ad3e293f9f`) +- **test:** drain inherited quality snapshot reds (direct commit `21ed68d8ac`) +- **chore(quality):** rebaseline measured CLI bundle growth (direct commit `d35b3f9779`) +- **test(ui):** reproduce invalid AnySearch icon alias (direct commit `12cc6ca834`) +- **test(translator):** fix the relative imports of the relocated deferred-finish test (direct commit `777d9d1629`) +- **docs(counts):** bring the documented provider and migration counts to the measured values (direct commit `e70bea30e9`) +- **test(security):** drop the guard-target set left unused by the sync (direct commit `a32ba0e362`) +- **chore(db):** renumber model_capabilities migration 163 → 169 (cross-branch collision with 163_radar_feed_cache_generated_at) (direct commit `70f5d4cbf9`) +- **docs(cli):** document CLI_PRIME_AGENT_BIN and correct the CLI Agents count (direct commit `9058e39b61`) +- **chore(deps):** refresh runtimes and adopt ESLint 10 ([#11259](https://github.com/diegosouzapw/OmniRoute/pull/11259)) — thanks @backryun +- **refactor(local-corpus):** implement dynamic root resolution and LRU cache ([#11491](https://github.com/diegosouzapw/OmniRoute/pull/11491)) — thanks @raheemuddin786 +- **chore(quality):** prune stale eslint-suppressions entries ([#11596](https://github.com/diegosouzapw/OmniRoute/pull/11596)) +- **chore(ci):** unblock quality gates - env-doc sync + stryker tap.testFiles registration (#9282) ([#11609](https://github.com/diegosouzapw/OmniRoute/pull/11609)) — thanks @Meet6338-X +- **chore(ci):** point the circular-deps gate at dpdm's real JS entrypoint ([#11615](https://github.com/diegosouzapw/OmniRoute/pull/11615)) — thanks @benzntech +- **chore(quality):** rebaseline file-size for #11448/#11495/#11561 growth ([#11630](https://github.com/diegosouzapw/OmniRoute/pull/11630)) +- **deps:** realign onnxruntime-node pin to @huggingface/transformers' 1.24.3 lockstep ([#11633](https://github.com/diegosouzapw/OmniRoute/pull/11633)) — thanks @jonlwheat2-gif +- **test(autoCombo):** port models_dev_tier #11508 guard from node:test to vitest ([#11635](https://github.com/diegosouzapw/OmniRoute/pull/11635)) — thanks @jonlwheat2-gif +- **chore(quality):** rebaseline file-size for #11083/#11631/#11628 growth ([#11636](https://github.com/diegosouzapw/OmniRoute/pull/11636)) +- **chore(lint):** prune stale ESLint suppressions (base-red #11449) ([#11644](https://github.com/diegosouzapw/OmniRoute/pull/11644)) — thanks @jonlwheat2-gif +- **docs(changelog):** aggregate the ten v3.8.50 fragments into CHANGELOG.md ([#11683](https://github.com/diegosouzapw/OmniRoute/pull/11683)) +- **chore(search):** drop unreachable credential sweep after #11642 ([#11687](https://github.com/diegosouzapw/OmniRoute/pull/11687)) — thanks @HouMinXi +- **ci:** add API route TypeScript regression gate ([#11705](https://github.com/diegosouzapw/OmniRoute/pull/11705)) — thanks @Sabeekhann +- **docs(dedup):** correct the CodeQL claim on the tenant-namespace comment ([#11712](https://github.com/diegosouzapw/OmniRoute/pull/11712)) +- **docs(changelog):** add consolidated v3.8.50 stats, top-25 ranking and a .mailmap ([#11715](https://github.com/diegosouzapw/OmniRoute/pull/11715)) +- **docs:** recommend gstack for AI-assisted workflows ([#11770](https://github.com/diegosouzapw/OmniRoute/pull/11770)) — thanks @santosraju99-hub +- **chore(quality):** tighten complexity/cognitive-complexity ratchets to the current tip; land the missed gateways.ts rebaseline ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **docs(i18n):** finish Freepik → Magnific rebrand in locale strings and README ([#11772](https://github.com/diegosouzapw/OmniRoute/pull/11772)) — thanks @turbolego +- **refactor(auto-combo):** fix divergent scoring in combo health reporting ([#11854](https://github.com/diegosouzapw/OmniRoute/pull/11854)) — thanks @jacobsparts +- **chore(changelog):** add missing fragments for #11919/#11918/#11916 ([#11938](https://github.com/diegosouzapw/OmniRoute/pull/11938)) +- **test(check):** escape the runs-on fixture with JSON.stringify, not a quote-only replace ([#11942](https://github.com/diegosouzapw/OmniRoute/pull/11942)) (direct commit `fb7445eaa3`) +- **test(release):** align five suites with the contracts #11933, #11919 and #11876 shipped on release/v3.8.51 ([#11944](https://github.com/diegosouzapw/OmniRoute/pull/11944)) +- **chore(quality):** remove unreachable code and restore test discovery ([#11950](https://github.com/diegosouzapw/OmniRoute/pull/11950)) — thanks @backryun +- **docs(agents):** sync-back landings are fast-forward, never squash ([#11964](https://github.com/diegosouzapw/OmniRoute/pull/11964)) +- **test(infra):** retry recursive temp-dir removal instead of failing a shard on ENOTEMPTY (#11966) ([#11968](https://github.com/diegosouzapw/OmniRoute/pull/11968)) +- **docs(ops):** the .113 heavy-build ceiling is one runner, not two ([#12048](https://github.com/diegosouzapw/OmniRoute/pull/12048)) +- **test(ui):** correct inactive auto-fetch expectation ([#12098](https://github.com/diegosouzapw/OmniRoute/pull/12098)) — thanks @RaviTharuma +- **docs:** document native dependency check escape hatch ([#12101](https://github.com/diegosouzapw/OmniRoute/pull/12101)) — thanks @RaviTharuma +- **test(providers):** regenerate the translate-path golden for OrcaRouter (#11923) ([#12118](https://github.com/diegosouzapw/OmniRoute/pull/12118)) +- **chore(quality):** velocity phase — loosen every numeric baseline by 20% until v4.0, monitor headroom nightly ([#12125](https://github.com/diegosouzapw/OmniRoute/pull/12125)) +- **chore(lint):** batch 0 of #12146 — type the call-log-cap sqlite rows instead of 45 as-any casts ([#12157](https://github.com/diegosouzapw/OmniRoute/pull/12157)) +- **chore(lint):** batch 4 of #12146 — shared/components react-hooks violations resolved ([#12159](https://github.com/diegosouzapw/OmniRoute/pull/12159)) +- **chore(lint):** batch 1 of #12146 — dashboard/cli-code react-hooks violations resolved ([#12160](https://github.com/diegosouzapw/OmniRoute/pull/12160)) +- **chore(lint):** batch 3 of #12146 — dashboard/settings react-hooks violations resolved ([#12162](https://github.com/diegosouzapw/OmniRoute/pull/12162)) +- **chore(lint):** batch 2 of #12146 — resolve the react-hooks compiler violations in dashboard/providers ([#12163](https://github.com/diegosouzapw/OmniRoute/pull/12163)) +- **chore(quality):** register search-432 cooldown test in stryker tap.testFiles ([#12170](https://github.com/diegosouzapw/OmniRoute/pull/12170)) +- **chore(lint):** batch 5 of #12146 — combos, endpoint, provider-stats, api-manager and costs react-hooks violations resolved ([#12174](https://github.com/diegosouzapw/OmniRoute/pull/12174)) +- **build:** add contributor fast profile ([#12192](https://github.com/diegosouzapw/OmniRoute/pull/12192)) — thanks @rafacpti23 +- **docs(readme):** README touch-ups ([#12193](https://github.com/diegosouzapw/OmniRoute/pull/12193)) ([#12194](https://github.com/diegosouzapw/OmniRoute/pull/12194)) ([#12202](https://github.com/diegosouzapw/OmniRoute/pull/12202)) — thanks @rafacpti23 +- **build:** force the webpack bundler in the contributor build profile ([#12197](https://github.com/diegosouzapw/OmniRoute/pull/12197)) — thanks @rafacpti23 +- **build:** skip standalone packaging in the contributor profile ([#12198](https://github.com/diegosouzapw/OmniRoute/pull/12198)) — thanks @rafacpti23 +- **docs(audit):** align every published number with the code and harden check:docs-counts ([#12200](https://github.com/diegosouzapw/OmniRoute/pull/12200)) +- **build:** stub the instrumentation entrypoints in the contributor profile ([#12203](https://github.com/diegosouzapw/OmniRoute/pull/12203)) — thanks @rafacpti23 +- **build:** omit the standalone output target for contributor builds ([#12204](https://github.com/diegosouzapw/OmniRoute/pull/12204)) — thanks @rafacpti23 +- **chore(lint):** batch 6 of #12146 — memory, radar, audit, analytics, cache, usage, activity, home and RequestLoggerV2 react-hooks violations resolved ([#12208](https://github.com/diegosouzapw/OmniRoute/pull/12208)) +- **docs(diagrams):** rename number-carrying diagram files to stable names ([#12210](https://github.com/diegosouzapw/OmniRoute/pull/12210)) +- **docs(dashboard):** redraw onboarding tier-flow SVGs for the real 4-tier model ([#12211](https://github.com/diegosouzapw/OmniRoute/pull/12211)) +- **docs(api):** document every implemented route in openapi.yaml (276 → 692 paths) ([#12212](https://github.com/diegosouzapw/OmniRoute/pull/12212)) +- **chore(lint):** batch 7 of #12146 — final src tail: 37 react-hooks violations across 33 files resolved ([#12244](https://github.com/diegosouzapw/OmniRoute/pull/12244)) +- Revert "docs: recommend gstack for AI-assisted workflows (#11770)" ([#12248](https://github.com/diegosouzapw/OmniRoute/pull/12248)) ([#12249](https://github.com/diegosouzapw/OmniRoute/pull/12249)) +- **docs:** Chaos Mode setup guide + weighted strategy semantics ([#12250](https://github.com/diegosouzapw/OmniRoute/pull/12250)) +- **docs(agents):** protected-surface merge rule — operator approval for agent-instruction files ([#12253](https://github.com/diegosouzapw/OmniRoute/pull/12253)) +- **chore(quality):** register native-codex turn-pin tests in stryker tap.testFiles ([#12263](https://github.com/diegosouzapw/OmniRoute/pull/12263)) +- **chore(quality):** dedupe tap.testFiles entries added by racing base-red fixes ([#12265](https://github.com/diegosouzapw/OmniRoute/pull/12265)) +- **docs(readme):** introduce OmniRouteTray — the macOS menu-bar companion ([#12276](https://github.com/diegosouzapw/OmniRoute/pull/12276)) — thanks @ggiak +- **chore(proxy):** purge the legacy 1proxy residue — sync/rotator modules, dead DB exports, dead settings tab and flag, docs (#12091) ([#12290](https://github.com/diegosouzapw/OmniRoute/pull/12290)) +- **chore(quality):** baseline-headroom skips generated and vendored files in the fileSize worst-file signal ([#12291](https://github.com/diegosouzapw/OmniRoute/pull/12291)) +- **refactor(video):** unify the JPEG frame data-URI contract ([#12322](https://github.com/diegosouzapw/OmniRoute/pull/12322)) +- **chore(deps):** freeze onnxruntime-node and eslint-plugin-react-hooks out of dependabot groups ([#12329](https://github.com/diegosouzapw/OmniRoute/pull/12329)) +- **test(executors):** restore the #10986 reasoning-only fallback guards ([#12364](https://github.com/diegosouzapw/OmniRoute/pull/12364)) — thanks @pacocartones +- **chore(quality):** re-tighten the file-size ratchet to the real LOC (plan 3.8.52 task 0) ([#12411](https://github.com/diegosouzapw/OmniRoute/pull/12411)) +- **test(grok):** format web executor suite ([#12412](https://github.com/diegosouzapw/OmniRoute/pull/12412)) +- **chore(quality):** ratchet open-sse typecheck baseline to zero ([#12418](https://github.com/diegosouzapw/OmniRoute/pull/12418)) +- **test(build):** guard the artifact path policy arrays against duplicates ([#12422](https://github.com/diegosouzapw/OmniRoute/pull/12422)) +- **chore(lint):** adopt eslint-plugin-react-hooks 7.1.1 ([#12428](https://github.com/diegosouzapw/OmniRoute/pull/12428)) +- **chore(providers):** remove a keyless provider integration at its operator's request ([#12440](https://github.com/diegosouzapw/OmniRoute/pull/12440)) +- **docs(providers):** register providers removed at their operator's request and guard against reintroduction ([#12478](https://github.com/diegosouzapw/OmniRoute/pull/12478)) +- **chore(quality):** base fixes — stryker tap.testFiles + node_modules cache key ([#12482](https://github.com/diegosouzapw/OmniRoute/pull/12482)) +- **docs(arch):** one-process recipe for tens of long /v1/responses ([#12493](https://github.com/diegosouzapw/OmniRoute/pull/12493)) — thanks @RaviTharuma +- **chore(quality):** tighten the CodeQL ratchet baseline from 11 to 6 ([#12530](https://github.com/diegosouzapw/OmniRoute/pull/12530)) +- **chore(deps):** pin browserslist override to ^4.28.8 ([#12592](https://github.com/diegosouzapw/OmniRoute/pull/12592)) — thanks @KooshaPari +- **chore(deps):** bump fast-uri to 3.1.7 in the electron lockfile ([#12601](https://github.com/diegosouzapw/OmniRoute/pull/12601)) +- **docs(quality):** document how the CodeQL ratchet refreshes and how to tighten it ([#12611](https://github.com/diegosouzapw/OmniRoute/pull/12611)) +- **test(ci):** pin the openapi-security-tiers two-arm contract with an executing gate test (#12581) ([#12652](https://github.com/diegosouzapw/OmniRoute/pull/12652)) +- **docs(gamification):** align XP Rewards table with code (#12501) ([#12667](https://github.com/diegosouzapw/OmniRoute/pull/12667)) — thanks @KooshaPari +- **refactor(combo):** split executeTarget into gates, attempt, and loop ([#12746](https://github.com/diegosouzapw/OmniRoute/pull/12746)) — thanks @HouMinXi +- **docs(security):** document socket.yml scanner config + CI workflow link (#12575) ([#12764](https://github.com/diegosouzapw/OmniRoute/pull/12764)) — thanks @KooshaPari +- **chore(ci):** guard commit identity in pre-commit to stop author misattribution ([#12772](https://github.com/diegosouzapw/OmniRoute/pull/12772)) +- **deps:** 17 Dependabot bumps — bump electron from 43.4.0 to 43.4.1 in /electron ([#11426](https://github.com/diegosouzapw/OmniRoute/pull/11426)); bump the development group across 1 directory with 8 updates ([#11428](https://github.com/diegosouzapw/OmniRoute/pull/11428)); bump the production group across 1 directory with 11 updates ([#11440](https://github.com/diegosouzapw/OmniRoute/pull/11440)); chore(deps): bump github/codeql-action/init from 4.37.7 to 4.37.8 ([#11925](https://github.com/diegosouzapw/OmniRoute/pull/11925)); chore(deps): bump github/codeql-action from 4.37.7 to 4.37.8 ([#11926](https://github.com/diegosouzapw/OmniRoute/pull/11926)); chore(deps): bump github/codeql-action/analyze from 4.37.7 to 4.37.8 ([#11927](https://github.com/diegosouzapw/OmniRoute/pull/11927)); build(deps): bump github/codeql-action/init from 4.37.8 to 4.37.9 ([#12345](https://github.com/diegosouzapw/OmniRoute/pull/12345)); build(deps): bump github/codeql-action/analyze from 4.37.8 to 4.37.9 ([#12346](https://github.com/diegosouzapw/OmniRoute/pull/12346)); bump the development group across 1 directory with 2 updates ([#12347](https://github.com/diegosouzapw/OmniRoute/pull/12347)); build(deps): bump github/codeql-action from 4.37.8 to 4.37.9 ([#12349](https://github.com/diegosouzapw/OmniRoute/pull/12349)); bump browserslist from 4.28.2 to 4.28.8 ([#12396](https://github.com/diegosouzapw/OmniRoute/pull/12396)); bump the production group across 1 directory with 4 updates ([#12399](https://github.com/diegosouzapw/OmniRoute/pull/12399)); bump @xmldom/xmldom ([#12500](https://github.com/diegosouzapw/OmniRoute/pull/12500)); bump qs from 6.15.2 to 6.16.0 ([#12512](https://github.com/diegosouzapw/OmniRoute/pull/12512)); bump @xmldom/xmldom from 0.9.10 to 0.9.12 ([#12513](https://github.com/diegosouzapw/OmniRoute/pull/12513)); bump fast-uri from 3.1.5 to 3.1.7 ([#12514](https://github.com/diegosouzapw/OmniRoute/pull/12514)); bump @humanfs/node from 0.16.7 to 0.16.8 ([#12515](https://github.com/diegosouzapw/OmniRoute/pull/12515)) +- Clear the `release/v3.8.51` `check:agent-skills-sync` base-red: regenerate `skills/cli-tunnel/SKILL.md` so the `tunnel create [type]` positional that #13009 taught the generator to read is reflected in the committed skill. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` typecheck base-red from #12731: the new `Mode pack ranking gates` candidates in `open-sse/services/autoCombo/__tests__/autoCombo.test.ts` omitted the required `provider`, `model` and `errorRate` fields of `ProviderCandidate`, failing `check:open-sse-typecheck` (and with it `Fast Quality Gates`) on every open PR. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` docs-sync base-red: EURouter (#13025) and GreenPT (#13024) took the live provider count to 358, leaving 7 STRICT drifts (`PROVIDER_REFERENCE.md`, 4 diagrams, 2 tier-flow images) plus the `AGENTS.md` / `llm.txt` / `package.json` count claims stale. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- **test(models):** Pin `CATALOG_BUILD_TIMEOUT_MS` in the #12058 canonical-catalog regression so a cold catalog build racing the 8s default bound no longer flakes the suite with a 500 ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Allow `@omniroute/opencode-plugin-v2/` in the npm pack policy — [#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870) shipped the v2 plugin beside its v1 sibling without widening the allowlist, so every packed file under it read as an unexpected artifact and failed `check:pack-policy` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Point the release-green validator's pack gate at the tree it is validating — the build-provenance guard defaulted to `origin/main`, which a release branch tip can never be an ancestor of mid-cycle, so `Package artifact` reported a false red on every run and the tarball boot-smoke cascaded off it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **docs(checks):** keep doc counts honest — headings, rankings, catalog, weights, quality gate and scoring diagram now covered ([#12507](https://github.com/diegosouzapw/OmniRoute/pull/12507)) — thanks @maxmad64bis +- **chore(lifecycle):** `check:model-lifecycle` now also diffs `DEFAULT_DEGRADATION_MAP` (the background-task redirect table) against the vendor lifecycle snapshot, refusing a retired id as source or target, with a table-driven unit test beside it. Three rows whose source the vendor had retired — `claude-sonnet-4-20250514`, `gemini-3-pro-preview` and `gpt-5.1-codex` (whose target `gpt-5.1-codex-mini` is retired too) — were dead code, since `checkLifecycle` answers 410 before the redirect runs; they are dropped (#12535 — thanks @pacocartones) +- **test(compression):** cover idle worker eviction at the resource level — the pool must call `terminate()` and must not retain the worker's `MessagePort`, complementing the `exit`-event assertion added with the fix ([#13371](https://github.com/diegosouzapw/OmniRoute/pull/13371)) +- **chore(quality):** raise the `imageGeneration.ts` file-size ceiling for the image-only-model guard that clears the #12945 base-red ([#13392](https://github.com/diegosouzapw/OmniRoute/pull/13392)) +- **chore(quality):** type the combo-test route's JSON response bodies instead of casting them to `any`, drop the now-empty suppression entry, and remove the `hasPerModelQuota` import `chatCore.ts` stopped using when the failure-classification helper was extracted ([#13379](https://github.com/diegosouzapw/OmniRoute/pull/13379)) +- **chore(electron):** the desktop bundle no longer ships the root-level files of every translated docs mirror (`README.md`, `llm.txt`, `CLAUDE.md`, `GEMINI.md`, `CONTRIBUTING.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`) — the packaged app only reads `docs/i18n//docs/**`, which stays. Saves ~11 MB on top of the translated CHANGELOGs already pruned. (#0000) ([#13235](https://github.com/diegosouzapw/OmniRoute/pull/13235)) +- **chore(quality):** retighten the `src/sse/handlers/chat.ts` unused-vars suppression count to the 8 violations that actually remain ([#13385](https://github.com/diegosouzapw/OmniRoute/pull/13385)) +- Bring the documented counts back in line with the code: 171 migrations (was 169) and 20 routing strategies (was 19), plus the regenerated `cli-tunnel` skill reference. ([#12970](https://github.com/diegosouzapw/OmniRoute/pull/12970)) +- Update the documented migration count to 172 after the call-logs provider-stats indexes landed. ([#13209](https://github.com/diegosouzapw/OmniRoute/pull/13209)) +- **chore(ci):** two gates that close the blind spots behind the exclusions above. `check:vitest-exclusions` requires every Vitest exclusion to name a tracking issue and to appear in `config/quality/vitest-exclusions.json` — the previous list grew to 62 files behind a comment pointing at an issue that had been closed for a month. `check-new-key-coverage` requires a key newly added to `en.json` to reach every locale; the existing coverage gate is a percentage floor per locale, so eleven absent keys out of ~13,000 left it at 99.9% while a whole feature shipped untranslated in nine languages. Both are diff-aware, so pre-existing debt stays frozen and neither needed a migration to turn on. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **build:** root layout no longer loads Inter from `next/font/google`, so a production image build does not need fonts.googleapis.com ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **docs(i18n):** bring the eight new `llm.txt` mirrors (km, kn, ml, my, ne, or, pa, si) up to the current provider and migration counts, so `check-docs-sync` stops rejecting every commit ([#13674](https://github.com/diegosouzapw/OmniRoute/pull/13674)) +- **refactor(ui):** flow surfaces (home topology, combo live studio, compression cockpit/waterfall/nodes, token-health badge) express state through the theme-aware `--orch-status-*` tokens instead of fixed dark-mode hex, so green/red/amber/grey stay legible in the light theme; dark mode is byte-identical. Categorical palettes (routing-strategy hues, compression-layer pills, provider brand colors) deliberately stay hex ([#12378](https://github.com/diegosouzapw/OmniRoute/issues/12378)) ([#13203](https://github.com/diegosouzapw/OmniRoute/pull/13203)) +- **chore(tests):** 51 test files that had been excluded from Vitest are running again, restoring roughly 350 assertions to the blocking suite. Each was measured individually first: of the 62 files parked behind the `// #8618 — pre-existing failure` comment, 51 pass against the current tree with no source change, so the exclusions had outlived the failures they were added for. The 11 that genuinely still fail stay excluded, but now point at a live tracker (#13204) rather than at #8618, which was closed in August while the list it tracked kept growing. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **chore(quality):** drop two ESLint suppression entries whose violations no longer exist, so `eslint --suppressions-location` stops rejecting every commit that touches the surrounding files ([#13384](https://github.com/diegosouzapw/OmniRoute/pull/13384)) +- **chore(release):** bring main's post-tag fixes into release/v3.8.51 — sync-back step 2/2 (direct commit `21c488f210`) +- **chore(deps):** patch toml and esbuild in the opencode plugin lockfiles ([#12965](https://github.com/diegosouzapw/OmniRoute/pull/12965)) +- **docs(changelog):** reconcile the v3.8.51 living section — fold 366 fragments, cover every cycle commit, credit every contributor ([#12971](https://github.com/diegosouzapw/OmniRoute/pull/12971)) +- **test(plugin):** wait for the v2 lazy-refresh tier instead of sleeping past it ([#12984](https://github.com/diegosouzapw/OmniRoute/pull/12984)) +- **chore(quality):** rebaseline chatCore.ts after the non-streaming regression fixes ([#13045](https://github.com/diegosouzapw/OmniRoute/pull/13045)) +- **docs(changelog):** add the missing bullet marker to the DeepSeek PoW fragment ([#13200](https://github.com/diegosouzapw/OmniRoute/pull/13200)) +- **chore(deps):** drain the Dependabot queue — 10 of 13 alerts ([#13213](https://github.com/diegosouzapw/OmniRoute/pull/13213)) +- **chore(ci):** make worktrees carry the hooks that enforce the identity gate ([#13236](https://github.com/diegosouzapw/OmniRoute/pull/13236)) +- **chore(deps):** pin csv-parse to ^7.0.2 via overrides (#13117) ([#13300](https://github.com/diegosouzapw/OmniRoute/pull/13300)) — thanks @KooshaPari +- **chore(deps):** bump hono from ^4.12.34 to ^4.13.7 (#13148) ([#13301](https://github.com/diegosouzapw/OmniRoute/pull/13301)) — thanks @KooshaPari +- **chore(deps):** pin joi to ^18.2.8 via overrides (#13085) ([#13302](https://github.com/diegosouzapw/OmniRoute/pull/13302)) — thanks @KooshaPari +- **docs(providers):** add Microsoft 365 Copilot (BizChat) provider guide (#12779) ([#13340](https://github.com/diegosouzapw/OmniRoute/pull/13340)) — thanks @KooshaPari +### 🙌 Contributors + +Thanks to everyone whose work landed in v3.8.51: + +| Contributor | PRs / Issues | +| --- | --- | +| [@5dive-bot](https://github.com/5dive-bot) | #11852 | +| [@abhisheksharma2411](https://github.com/abhisheksharma2411) | #11547, #11684, #11959, #12209 | +| [@adevwithpurpose](https://github.com/adevwithpurpose) | #11464 | +| [@adivekar-utexas](https://github.com/adivekar-utexas) | #12015, #12043, #12090, #12138 | +| [@AIB1TAL0S](https://github.com/AIB1TAL0S) | #12103 | +| [@alltomatos](https://github.com/alltomatos) | #11448 | +| [@alvinveroy](https://github.com/alvinveroy) | #11857, #12027, #12165 | +| [@amaleta](https://github.com/amaleta) | #11906, #11934 | +| [@amartinawi](https://github.com/amartinawi) | #11575 | +| [@AndrianBalanescu](https://github.com/AndrianBalanescu) | #11083, #11830, #11834, #11844 | +| [@anhtahaylove](https://github.com/anhtahaylove) | #13091, #13092, #13093, #13096, #13097, #13100, #13106, #13109, #13114, #13155, #13166, #13171, #13175, #13187, #13196 | +| [@aniruddhaadak80](https://github.com/aniruddhaadak80) | #11760 | +| [@app](https://github.com/app) | #12554 | +| [@arminanton](https://github.com/arminanton) | #11461, #11513 | +| [@AStupidBear](https://github.com/AStupidBear) | #11584 | +| [@atescivitci-cmd](https://github.com/atescivitci-cmd) | #12105 | +| [@b3nw](https://github.com/b3nw) | #11970, #11971, #12147 | +| [@backryun](https://github.com/backryun) | #11259, #11950, #12075, #12076, #12078, #12079, #12081, #12082, #12181, #12228, #12239, #12255, #12258, #12277, #12367, #12423, #12524, #12538 | +| [@benjaminkitt](https://github.com/benjaminkitt) | #11747 | +| [@benzntech](https://github.com/benzntech) | #11614, #11615 | +| [@Bl0ck154](https://github.com/Bl0ck154) | #11948, #11951, #11952, #11953, #11954 | +| [@brick30llc-ctrl](https://github.com/brick30llc-ctrl) | #11752, #12242, #12262 | +| [@Chewji9875](https://github.com/Chewji9875) | #11455, #11875, #12028 | +| [@cheynetom](https://github.com/cheynetom) | #12058 | +| [@davidebaraldo](https://github.com/davidebaraldo) | #12222, #12304 | +| [@davidlinfr](https://github.com/davidlinfr) | #6390 | +| [@ddarkr](https://github.com/ddarkr) | #12124 | +| [@Deftera186](https://github.com/Deftera186) | #11809 | +| [@dpozimski](https://github.com/dpozimski) | #11945 | +| [@DW-MediaLab](https://github.com/DW-MediaLab) | #12229 | +| [@echel0nn](https://github.com/echel0nn) | #11923 | +| [@f9td56dbgh-hub](https://github.com/f9td56dbgh-hub) | #11660 | +| [@fabioluissilva](https://github.com/fabioluissilva) | #11991 | +| [@foreveryh](https://github.com/foreveryh) | #12177 | +| [@geek007git](https://github.com/geek007git) | #12115, #12116, #12117, #12120, #12122 | +| [@geekyNads](https://github.com/geekyNads) | #11784, #12266 | +| [@ggiak](https://github.com/ggiak) | #11561, #12276, #12350, #12402 | +| [@gonisulaimann](https://github.com/gonisulaimann) | #12368, #12369, #12371 | +| [@Gorillaz322](https://github.com/Gorillaz322) | #12207 | +| [@groovecityJO](https://github.com/groovecityJO) | #12682 | +| [@hartmark](https://github.com/hartmark) | #11434, #11452, #11473, #11499, #11703, #11983, #11984, #11985, #11986, #11988, #11989, #11990, #11994, #12221, #12293, #12445, #12446, #12447, #12448, #12460, #12461, #12623, #12646, #12650, #12680, #12717, #12718, #12727, #12741, #12854 | +| [@hizzt](https://github.com/hizzt) | #11894 | +| [@hongnoul](https://github.com/hongnoul) | #11484 | +| [@HouMinXi](https://github.com/HouMinXi) | #11411, #11414, #11512, #11518, #11520, #11641, #11642, #11643, #11687, #11779, #11849, #11850, #11851, #11915, #11916, #11918, #11919, #11920, #12013, #12017, #12033, #12042, #12106, #12139, #12166, #12169, #12171, #12205, #12213, #12312, #12325, #12487, #12488, #12495, #12504, #12557, #12566, #12590, #12591, #12624, #12626, #12632, #12637, #12678, #12696, #12697, #12711, #12733, #12746, #12767, #12770, #12789, #12803, #12805, #12811, #12866, #12868, #12899, #12926, #12934, #12950, #12951, #12974, #13001, #13006, #13011, #13017, #13026, #13027, #13034, #13035, #13038, #13042, #13050, #13060, #13061, #13069, #13107, #13120, #13136, #13195, #13197 | +| [@Hsia97](https://github.com/Hsia97) | #11624 | +| [@jacobsparts](https://github.com/jacobsparts) | #11854, #12155, #12167 | +| [@joglomedia](https://github.com/joglomedia) | #11980 | +| [@jonlwheat2-gif](https://github.com/jonlwheat2-gif) | #11443, #11450, #11549, #11567, #11608, #11633, #11635, #11640, #11644, #11671, #11672, #11673, #11674, #11675, #11676, #11677, #11706, #11783, #12051, #12052, #12053, #12055 | +| [@kanade-hoshino](https://github.com/kanade-hoshino) | #12180 | +| [@Karan825](https://github.com/Karan825) | #11861 | +| [@KaspaPulse](https://github.com/KaspaPulse) | #11389, #11469, #11628, #11666, #11910 | +| [@keeltrace](https://github.com/keeltrace) | #12080, #12223 | +| [@killer30001000](https://github.com/killer30001000) | #12178 | +| [@KooshaPari](https://github.com/KooshaPari) | #12470, #12592, #12667, #12699, #12703, #12706, #12764, #12769, #12771, #13300, #13301, #13302, #13340, #13401, #13403, #13404, #13405, #13406, #13407, #13409, #13410, #13411, #13412, #13413, #13414, #13418, #13424, #13427, #13433, #13523, #13524, #13525, #13528, #13531, #13532, #13534, #13539, #13543, #13545, #13546, #13547, #13550, #13553 | +| [@kriptoburak](https://github.com/kriptoburak) | #11370 | +| [@KrzysiekSko](https://github.com/KrzysiekSko) | #12425, #12432, #12673 | +| [@marcelokarval](https://github.com/marcelokarval) | direct commit / report | +| [@maxmad64bis](https://github.com/maxmad64bis) | #1622, #11435, #11437, #11441, #11537, #11550, #11553, #11555, #11557, #11812, #11842, #11843, #11903, #12151, #12214, #12215, #12218, #12226, #12314, #12316, #12317, #12318, #12319, #12320, #12321, #12507, #12715, #12731, #12744, #12786, #12787, #12788, #12790, #12792, #12794, #12795, #12828, #12832, #12853, #12857, #12870, #12937, #12941, #12975, #13141, #13142, #13146, #13147 | +| [@mdigitalbh81](https://github.com/mdigitalbh81) | #12240 | +| [@Meet6338-X](https://github.com/Meet6338-X) | #11598, #11609, #12110 | +| [@morpheus9393](https://github.com/morpheus9393) | #11943 | +| [@MumuTW](https://github.com/MumuTW) | #11492, #11502, #11506, #11507, #11626, #11675, #11685, #11728, #11746, #11888, #11889, #11890, #11892 | +| [@Neuron-Mr-White](https://github.com/Neuron-Mr-White) | #11622, #11801, #12256 | +| [@NightStalker-87](https://github.com/NightStalker-87) | #12183 | +| [@NoxzRCW](https://github.com/NoxzRCW) | #11879, #11880, #11881, #11882, #11883, #11935 | +| [@ntdat812](https://github.com/ntdat812) | #11585, #12095 | +| [@ntdatt812](https://github.com/ntdatt812) | #11368, #11573, #11574, #11576, #11577, #11580, #11582, #11583, #11588, #11589, #11590, #11591, #11592, #11593, #11672, #12177, #12180, #12873, #12918, #12920, #12921, #12925, #12930, #13007, #13009, #13024, #13025, #13083, #13087, #13101, #13104, #13110 | +| [@official-burak](https://github.com/official-burak) | #11542 | +| [@opensource-elearning](https://github.com/opensource-elearning) | #12179, #12189, #12278, #12286 | +| [@oyi77](https://github.com/oyi77) | #11408, #11409, #11421, #11505, #11677, #12036, #12110 | +| [@pacocartones](https://github.com/pacocartones) | #11521, #11522, #11527, #11528, #11529, #11530, #11531, #11532, #11533, #11534, #11595, #11599, #11603, #11604, #11605, #11607, #11610, #11676, #11714, #11716, #11718, #11767, #11838, #11860, #11862, #11869, #11871, #11872, #11873, #11903, #11906, #11921, #11934, #12356, #12358, #12359, #12360, #12361, #12362, #12364, #12365, #12373, #12374, #12375, #12376, #12377, #12379, #12380, #12381, #12386, #12387, #12389, #12390, #12394, #12395, #12397, #12401, #12403, #12404, #12522, #12523, #12535, #12536, #12540, #12541, #12543, #12545, #12548, #12549, #12550, #12551, #12552, #12647, #12651, #12653, #13055 | +| [@patrykkopycinski](https://github.com/patrykkopycinski) | #11936, #11937, #12224 | +| [@PauloFH](https://github.com/PauloFH) | #11509 | +| [@PauloHSOliveira](https://github.com/PauloHSOliveira) | #12241 | +| [@ponkcore](https://github.com/ponkcore) | #12054 | +| [@Prajeeth-12](https://github.com/Prajeeth-12) | #11634, #12046 | +| [@quiterunner-commits](https://github.com/quiterunner-commits) | #12131, #12143 | +| [@rafacpti23](https://github.com/rafacpti23) | #11554, #11558, #12192, #12197, #12198, #12202, #12203, #12204 | +| [@ragnar-claude](https://github.com/ragnar-claude) | #11564 | +| [@raheemuddin786](https://github.com/raheemuddin786) | #11491, #11839, #11840, #11841, #12003, #12230, #12231, #12232, #12233, #12234 | +| [@rahilmavani](https://github.com/rahilmavani) | #11761 | +| [@Rahulsharma0810](https://github.com/Rahulsharma0810) | #11771 | +| [@RaviTharuma](https://github.com/RaviTharuma) | #11710, #11727, #11797, #11798, #11802, #11805, #11806, #11811, #12098, #12099, #12101, #12449, #12452, #12472, #12473, #12493, #12533, #12607, #12628, #12631, #12636, #12875, #12876, #12880, #12882, #12884 | +| [@rezjalibd](https://github.com/rezjalibd) | #12186 | +| [@RhianB14](https://github.com/RhianB14) | #11692 | +| [@rifqiawl](https://github.com/rifqiawl) | #11517, #11519, #11969 | +| [@rqzbeh](https://github.com/rqzbeh) | #11390 | +| [@Sabeekhann](https://github.com/Sabeekhann) | #11699, #11705 | +| [@santosraju99-hub](https://github.com/santosraju99-hub) | #11755, #11770 | +| [@Seramicx](https://github.com/Seramicx) | #11568 | +| [@Siva010](https://github.com/Siva010) | #12191 | +| [@solstxce](https://github.com/solstxce) | #11597 | +| [@soroush5](https://github.com/soroush5) | #12691 | +| [@tenshiak](https://github.com/tenshiak) | #12279 | +| [@TheDemonTuan](https://github.com/TheDemonTuan) | #11468, #11470, #11471, #11482, #11548, #11758, #11775, #11814 | +| [@thomasmaerz](https://github.com/thomasmaerz) | #12834 | +| [@tuandinh0801](https://github.com/tuandinh0801) | #11454 | +| [@turbolego](https://github.com/turbolego) | #11621, #11762, #11772, #11774, #11781, #12216 | +| [@Tushar49](https://github.com/Tushar49) | #11392 | +| [@ujjawalkaushik1110](https://github.com/ujjawalkaushik1110) | #11863 | +| [@vermasomesh835](https://github.com/vermasomesh835) | #11794 | +| [@vsd2807](https://github.com/vsd2807) | #11565, #11619 | +| [@wahidsadik371-coder](https://github.com/wahidsadik371-coder) | #12126 | +| [@watchingdogs](https://github.com/watchingdogs) | #12031 | +| [@wildcard](https://github.com/wildcard) | #11620 | +| [@xiaoyaner0201](https://github.com/xiaoyaner0201) | #11460, #11662, #12083 | +| [@Xxx91n](https://github.com/Xxx91n) | #11690 | +| [@yourspraveen](https://github.com/yourspraveen) | #11146 | +| [@yxyxy](https://github.com/yxyxy) | #11671 | +| [@ZaimMarzuki](https://github.com/ZaimMarzuki) | #11960 | +| [@Zartharas](https://github.com/Zartharas) | #11340 | +| [@zero-executioner](https://github.com/zero-executioner) | #11631 | +| [@diegosouzapw](https://github.com/diegosouzapw) | maintainer | + +--- ## [3.8.50] — 2026-08-25 _Living section — regenerated 2026-08-12 from all cycle commits (cycle open `ed2db6cb19` → tip). Bullets carry the merged PR and its author; direct pushes listed separately._ diff --git a/docs/i18n/mt/CHANGELOG.md b/docs/i18n/mt/CHANGELOG.md index 7559251e01..05c96d2361 100644 --- a/docs/i18n/mt/CHANGELOG.md +++ b/docs/i18n/mt/CHANGELOG.md @@ -95,16 +95,1119 @@ ## [3.8.51] — TBD -_Living section — cycle opened at the v3.8.50 freeze (parallel-cycle model). Bullets are aggregated from `changelog.d/` fragments at each `/generate-release` phase._ +_Living section — reconciled 2026-09-15 from all cycle commits (`091589089c` → `c0f92ec98a`, 916 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 | **123** | +| 📝 Commits in the cycle | **916** | +| 🔀 Pull requests referenced | **892** | +| 📋 Changelog entries | **919** | +| 🙌 Contributors credited in entries | **121** | +| 🤖 Automated dependency commits | 18 | + +**Entries by type** + +| Type | Count | +| --- | ---: | +| 🐛 Fixes | 592 | +| ✨ Features | 131 | +| 🧹 Chore | 93 | +| 📚 Docs | 42 | +| 🧪 Tests | 32 | +| ♻️ Refactor | 8 | +| 🏗️ Build | 7 | +| ⚡ Performance | 7 | +| ⚙️ CI | 2 | +| ⏪ Reverts | 2 | +| 📦 Dependencies | 2 | +| 🔒 Security | 1 | + +### 🏆 Top 25 contributors this cycle + +_By commits in `091589089c..c0f92ec98a`, author identities consolidated via `.mailmap` and the merged PR's GitHub login. Bots excluded._ + +| # | Contributor | Commits | +| ---: | --- | ---: | +| 🥇 | diegosouzapw | 323 | +| 🥈 | Bob.Hou (@HouMinXi) | 79 | +| 🥉 | Paco Cartones (@pacocartones) | 77 | +| 4 | Dizzle (@maxmad64bis) | 49 | +| 5 | Koosha Paridehpour (@KooshaPari) | 43 | +| 6 | Nguyen Thanh Dat (@ntdatt812) | 31 | +| 7 | Ravi Tharuma (@RaviTharuma) | 26 | +| 8 | Markus Hartung (@hartmark) | 22 | +| 9 | Webman (@jonlwheat2-gif) | 22 | +| 10 | backryun | 17 | +| 11 | anhtahaylove | 15 | +| 12 | Rafa Martins (@rafacpti23) | 10 | +| 13 | Syed Raheemuddin (@raheemuddin786) | 10 | +| 14 | Nguyễn Viết Tuấn (@TheDemonTuan) | 8 | +| 15 | MumuTW | 7 | +| 16 | Paijo (@oyi77) | 7 | +| 17 | Tobias Andersen (@turbolego) | 6 | +| 18 | Bl0ck (@Bl0ck154) | 5 | +| 19 | SHANMUGAPRIYAN (@geek007git) | 5 | +| 20 | KaspaPulse | 5 | +| 21 | Abhishek Sharma (@abhisheksharma2411) | 4 | +| 22 | Abhishek Divekar (@adivekar-utexas) | 4 | +| 23 | Andrew B. (@AndrianBalanescu) | 4 | +| 24 | NoxzRCW | 4 | +| 25 | opensource-elearning | 4 | ### ✨ New Features +- **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). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- Added Google AI Studio Gemini batch text-to-speech support through `POST /v1/audio/speech`. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- 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. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(routing):** subscription-first auto groupings — `auto/subscription` routes only through plan-included connections with a documented hard-stop overage and fails closed on exhaustion, while `auto/thrifty` orders the pool `subscription → keyless → free → cheap → premium` and steps up one rung at a time as each is exhausted. Billing class comes from a curated per-connection catalog (uncurated is treated as metered, never plan-included), both reuse STRICT_ZERO_COST's per-connection verification, and a quota reading whose `resetAt` has passed is now refreshed regardless of TTL so routing returns to plan capacity as soon as it resets ([#11146](https://github.com/diegosouzapw/OmniRoute/pull/11146)) — thanks @yourspraveen +- **feat(providers):** publish a management-authenticated versioned web-session credential contract from OmniRoute's canonical browser credential metadata ([#11340](https://github.com/diegosouzapw/OmniRoute/pull/11340)) — thanks @Zartharas +- **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)) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add Xquik X search with typed results, credential validation, REST routing, and MCP selection ([#11370](https://github.com/diegosouzapw/OmniRoute/pull/11370)) — thanks @kriptoburak +- **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)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(dashboard):** surface durable exclusive managed leases in the existing Sessions view, keeping leased clients visible across idle gaps while marking connections with in-flight work as active ([#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389)) — thanks @KaspaPulse +- **build(bun):** allow Turbopack bundler flag on Bun 1.4+ with configurable Webpack fallback ([#11471](https://github.com/diegosouzapw/OmniRoute/pull/11471)) — thanks @TheDemonTuan +- feat(api): add an opt-in `modelVisibilityAllowlist`/`modelVisibilityDenylist` settings pair to curate exactly which models `/v1/models` advertises, mirrored into every `auto/*` combo candidate pool so a denied model cannot be routed to via combo selection either (#11481) ([#11997](https://github.com/diegosouzapw/OmniRoute/pull/11997)) +- **feat(rankings):** the Free Provider Rankings page shows what each provider actually served over the last 24 h. It ranked by ELO alone, which left a provider that answers every call with an error in first place; the usage data was already served by the API but never requested. A provider with too small a sample shows a dash, not a number ([#11546](https://github.com/diegosouzapw/OmniRoute/pull/11546)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **feat(guardrails):** enforce a bounded, deterministic contract for Video Bridge transcripts — 256 cues, 4096 input code units and 4 KiB UTF-8 per cue, 64 KiB total text, malformed-Unicode rejection, focus-window scoping, cross-source reconciliation with contributing-source metadata, and a structural provenance trust boundary so caller JSON can never self-assert `embedded`/`audio-bridge` provenance ([#11652](https://github.com/diegosouzapw/OmniRoute/issues/11652)) ([#12009](https://github.com/diegosouzapw/OmniRoute/pull/12009)) +- **feat(video):** orchestrate optional Video Bridge audio extraction and Audio Bridge STT behind a dual opt-in (operator setting AND per-request signal) — a new loopback-only broker `mode=audio` operation shares the frame path's exact process queue, deadline, AbortSignal, and byte budgets to extract a bounded mono 16 kHz PCM WAV from the same already-downloaded video, then reuses the existing Audio Bridge transcription boundary; provider segment timing is preserved when available and marked coarse otherwise, and every failure degrades to a visual-only-safe partial instead of throwing (#11654). ([#12012](https://github.com/diegosouzapw/OmniRoute/pull/12012)) +- Add a tenant-bound Video Bridge drill-down lifecycle on top of the existing secure cache substrate: opaque hashed handles (never raw session/video identifiers), preview/standard/detail multiresolution variants resampled on read, response pagination capped at 8 frames and 32 MiB, and a new authenticated `/api/v1/video-bridge/drilldown` consumer route that stays disabled for remote access by default and denies cross-key access with the same response as a nonexistent handle (no existence oracle). ([#12006](https://github.com/diegosouzapw/OmniRoute/pull/12006)) +- **test(video):** Add the Video Bridge FU-07/FU-09 promotion-evidence harness (#11656) — a frozen Zod manifest schema covering the 8 required scenario kinds (static scenes, rapid cuts, late facts, fades, blur, small text, close events, visual prompt injection) with a minimum of 3 repetitions per case, deterministic declarative fixture recipes (`videoBridgePromotionFixtures.ts`), a pure medians/p95 metrics aggregator, a pure FU-07/FU-09 promotion-verdict evaluator applying the ticket's exact thresholds (missing token usage always holds), a digest-only persistence layer that never retains raw media or raw model responses, and a versioned per-model promotion allowlist shipped empty with every model defaulting to `hold`. The FU-07/FU-09 promotion verdicts themselves remain HOLD — they require a real evidence run against real models on VPS 192.168.0.15. ([#12008](https://github.com/diegosouzapw/OmniRoute/pull/12008)) +- **feat(video bridge):** "embedded" transcript provenance can now be legitimately earned instead of merely asserted — a bounded, allowlisted (`mov_text`/`subrip`/`webvtt`) subtitle probe runs through the loopback-only Video Bridge broker (at most 2 streams, 10s subdeadline bounded by the request deadline, 256 KiB output, 4096-code-unit lines), normalized through a bounded, ReDoS-safe WebVTT parser and Zod-validated end to end. The adapter always resolves to an explicit `success`/`absent`/`transient_failure` outcome — a subtitle failure never breaks the visual description path, and only a fingerprint-verified broker response (never a caller-declared label) can produce embedded cues (#11659). ([#12011](https://github.com/diegosouzapw/OmniRoute/pull/12011)) +- Default new Antigravity-family connections (agy CLI imports and Antigravity OAuth connects) to model auto-sync, so live model discovery lands in the synced catalog and `/v1/models` picks up freshly released upstream models (e.g. Gemini 3.7 Flash tiers) without code changes. Existing connections keep their current setting; the per-connection dashboard toggle remains the opt-out. (#11685 — thanks @MumuTW) ([#11887](https://github.com/diegosouzapw/OmniRoute/pull/11887)) +- **feat(ui):** enable React Compiler (`reactCompiler: true` + `babel-plugin-react-compiler`) for automatic memoization at build time ([#11783](https://github.com/diegosouzapw/OmniRoute/pull/11783)) — thanks @jonlwheat2-gif +- **feat(providers):** add SeekAi (`seekai.cc`) as an OpenAI-compatible New-API gateway — catalog id `seekai` (alias `ska`), `https://seekai.cc/v1`, live `/v1/models` via `passthroughModels`, aggregator-list membership so New-API balance detection can opt in. No referral/aff codes. ([#11786](https://github.com/diegosouzapw/OmniRoute/issues/11786)) ([#12557](https://github.com/diegosouzapw/OmniRoute/pull/12557)) — thanks @HouMinXi +- **feat(zai):** add GLM-5.3-Flash Coding Plan support (1M context, 128K output, vision, `low|high|max` reasoning) and route `zai` GLM-5.3-family API-key traffic through the OpenAI-compatible Coding Plan endpoint with native thinking defaults ([#11801](https://github.com/diegosouzapw/OmniRoute/pull/11801)) — thanks @Neuron-Mr-White +- **feat(combo):** choose how combo models are ordered — manual, provider, score, or name — via a sort control in the dashboard builder, persisted in `config.modelSort` and re-applied on load and after add ([#11812](https://github.com/diegosouzapw/OmniRoute/pull/11812)) — thanks @maxmad64bis +- **feat(free):** custom models can be marked free-tier via `customModels[].isFree`; `isFreeModel()` is the first door and `hidePaidModels` respects it even for providers outside the free budget ([#11843](https://github.com/diegosouzapw/OmniRoute/pull/11843)) — thanks @maxmad64bis +- **feat(nodejs):** add `5dive` as a `configure` target — `omniroute configure 5dive` / `omniroute setup-5dive` write a 5dive auth profile that points an agent fleet's `claude` seats at OmniRoute, with the root-only write, the loopback-vs-`https` endpoint rule and the per-seat model pin handled explicitly ([#11852](https://github.com/diegosouzapw/OmniRoute/pull/11852)) — thanks @5dive-bot +- **feat(sse):** treat `max` as a first-class reasoning-effort tier and clamp per model family (GLM 5.1+/DeepSeek V4+/Kimi K3+ keep native `max`; o1/MiniMax/Grok/Muse Spark clamp to their upstream ceiling) ([#11875](https://github.com/diegosouzapw/OmniRoute/pull/11875)) — thanks @Chewji9875 +- **feat(providers):** the provider plugin manifest now advertises a `usage-fetch` capability for the 40 providers that have a wired usage/quota fetcher, so external dashboards can read it from `GET /api/v1/provider-plugin-manifest` instead of parsing `open-sse/services/usage.ts` after every release. Discovery only — no new fetcher, no quota change, and the Dashboard quota widget stays gated by `USAGE_SUPPORTED_PROVIDERS`. `USAGE_FETCHER_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/fetcherProviders.ts`) and is re-exported from `services/usage.ts`, keeping the manifest module a light leaf instead of pulling the ~490-module usage dispatcher into the manifest route. ([#11903](https://github.com/diegosouzapw/OmniRoute/pull/11903)) — thanks @maxmad64bis / @pacocartones +- **feat(plugins):** `OMNIROUTE_PLUGINS_DIR` sets the directory the runtime plugin scanner reads — and the root the plugin manager installs into — overriding the `HOME`-derived default, so a Docker/K8s deployment can point straight at its bind-mounted plugin tree instead of moving `HOME` just to relocate the scan path. An image that exports no home no longer scans `/tmp/.omniroute/plugins` in silence: the resolved directory is logged once at startup as `scanner.dir_resolved`, naming the input that won. Unset, behaviour is unchanged. Distinct from the CLI-only `OMNIROUTE_PLUGIN_PATH`, which finds `omniroute-cmd-*` command packages and never reached this scanner ([#11906](https://github.com/diegosouzapw/OmniRoute/pull/11906)) — thanks @amaleta / @pacocartones +- **feat(leases):** add an explicit owner-authenticated status action that returns only the active lease's privacy-safe configured connection and provider labels, with generation fencing and no credential or internal-id disclosure ([#11910](https://github.com/diegosouzapw/OmniRoute/pull/11910)) — thanks @KaspaPulse +- **feat(providers):** import-from-file modal shows per-row API errors and ships a downloadable CSV template ([#12071](https://github.com/diegosouzapw/OmniRoute/issues/12071)) ([#12504](https://github.com/diegosouzapw/OmniRoute/pull/12504)) — thanks @HouMinXi +- **feat(providers):** dashboard search matches connection name and `baseUrl` so imported OpenAI-compat nodes surface on the provider card ([#12108](https://github.com/diegosouzapw/OmniRoute/issues/12108)) ([#12495](https://github.com/diegosouzapw/OmniRoute/pull/12495)) — thanks @HouMinXi +- **feat(routing):** add a `score` Auto router strategy that selects the highest configured weighted score and reuses `explorationRate`. ([#12155](https://github.com/diegosouzapw/OmniRoute/pull/12155)) — thanks @jacobsparts +- Add a runtime feature flag to disable universal context handoffs globally without changing the default behavior. ([#12167](https://github.com/diegosouzapw/OmniRoute/pull/12167)) — thanks @jacobsparts +- **feat(providers):** the provider plugin manifest now also advertises a `usage-supported` capability for the 46 providers whose usage API is accepted by the server and Dashboard routes, so integrators can distinguish "the server will serve quota for this provider" from "a fetcher is wired" without reading TypeScript. Discovery only — no fetcher or quota change. `usage-fetch` resolves on id or alias (the usage dispatcher accepts both); `usage-supported` resolves on id alone, matching the runtime guard `USAGE_SUPPORTED_PROVIDERS.includes(providerId)`. `USAGE_SUPPORTED_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/supportedProviders.ts`) and is re-exported from `providers.ts`, mirroring the `fetcherProviders` leaf from #11903 and keeping the manifest a light module. ([#12214](https://github.com/diegosouzapw/OmniRoute/pull/12214)) — thanks @maxmad64bis +- **feat(rankings):** order Free Provider Rankings by what each provider actually served — `GET /api/free-provider-rankings?sortBy=reliability` and a "Most reliable first" toggle on the page. Providers with too few calls to state a success rate keep their score order below the measured ones; the default order is unchanged ([#12218](https://github.com/diegosouzapw/OmniRoute/pull/12218)). — thanks @maxmad64bis +- **perf(sse):** defer `cloneLogPayload()` in the structured SSE collector until after the `maxEvents`/`maxBytes` cap check, eliminating ~9,800 wasted `structuredClone` calls per streaming response (65–71% faster `push()`). Reducer snapshot isolation restored for OpenAI and Responses summaries ([#12241](https://github.com/diegosouzapw/OmniRoute/pull/12241)) — thanks @PauloHSOliveira ([#12243](https://github.com/diegosouzapw/OmniRoute/pull/12243)) +- **feat(usage):** Devin CLI agentic quota (Codeium seat-management GetUserStatus) and OpenRouter key limits plus account credits now surface in Provider Limits ([#12256](https://github.com/diegosouzapw/OmniRoute/pull/12256) — thanks @Neuron-Mr-White) +- **feat(settings):** persist `headroomUrl` through Settings so status/start use the operator URL instead of only `HEADROOM_URL` ([#12306](https://github.com/diegosouzapw/OmniRoute/issues/12306)) ([#12487](https://github.com/diegosouzapw/OmniRoute/pull/12487)) — thanks @HouMinXi +- **feat(auto-combo):** Auto-Combo scoring can now weigh how often a provider/model has actually succeeded. The engine already carried that number on every candidate — 24 hours of usage history behind a ten-sample floor, real-time metrics otherwise — and the scoring function never read it, while the weight table described `stability` as if it did. `reliability` (`1 - failureRate`, with the same field precedence and the same rate-bounding the speed ranking already uses, so a corrupt reading means "nothing observed" rather than "fails every call") is now a declared factor shipping at weight `0`, so routing is unchanged until an operator gives it one, and the `stability` description now matches what that factor computes ([#12317](https://github.com/diegosouzapw/OmniRoute/pull/12317)) — thanks @maxmad64bis +- **feat(routing):** With `freeAccessPolicy: "strict"`, the read-only candidate listing (`GET /v1/auto-combo/{channel}/candidates`) no longer hides the candidates the zero-cost guard excludes — the same read-only transparency the resilience filter already honours (#9133). Each candidate now carries `freeAccessExclusion` saying why it would be kept out, and it tells an exhausted allowance apart from a quota reading that never arrived or went stale, which used to look identical from the outside. Routing is unchanged: the listing reports, it never enforces. The separate `excludeTosAvoid` guard still drops its candidates without a reason; that gap is now documented rather than closed ([#12319](https://github.com/diegosouzapw/OmniRoute/pull/12319)) — thanks @maxmad64bis +- **feat(radar):** The Radar catalog table now shows two facts it was already receiving from the feed and dropping on the floor: the per-model rate limits (requests and tokens, per minute and per day) in a new column, and a badge when a provider's terms state it may train on the prompts you send. A limit of zero renders as zero rather than "rate-only" — for a ceiling those are opposite facts — and a model with no training statement gets no badge, because an absent statement is not a guarantee ([#12320](https://github.com/diegosouzapw/OmniRoute/pull/12320)) — thanks @maxmad64bis +- **feat(radar):** explain Community, single-use, contributor, supporter, recovery, abuse, offers, and privacy rules before either Radar activation action, and remove the superseded fixed-PR grant promise from every UI locale ([#12342](https://github.com/diegosouzapw/OmniRoute/pull/12342)) +- **feat(gamification):** the dashboard Profile page now shows the real daily streak — `/api/gamification/level` returns `streak: { current, longest }` (per key with `apiKeyId`, operator-wide maximum otherwise) and the streak card reads it instead of a hard-coded 0 (#2403) ([#12377](https://github.com/diegosouzapw/OmniRoute/pull/12377)) — thanks @pacocartones +- **feat(gamification):** the dashboard leaderboard now shows each API key's display name under the Name column instead of a truncated key id; `GET /api/gamification/leaderboard` attaches `name` per entry (name only — no key material), while the shared ranking helper and the federation leaderboard stay id-only — thanks @pacocartones ([#12385](https://github.com/diegosouzapw/OmniRoute/pull/12385)) +- **feat(gamification):** enforce the documented 1000 XP/min per-API-key anti-cheat rate limit on the XP award path; over-limit awards are logged and skipped instead of persisted, and the sliding window now matches the timestamp format stored in `xp_audit_log` ([#2403](https://github.com/diegosouzapw/OmniRoute/issues/2403)) ([#12390](https://github.com/diegosouzapw/OmniRoute/pull/12390)) — thanks @pacocartones +- **feat(admin):** localize the gamification anomalies page — the loading state, the Status column and the Suspicious badge now come from the `common` catalog (new `common.suspicious` key propagated to every locale) — add it to the Gamification sidebar group as `gamification-admin` (`/dashboard/gamification/admin`), and expose the loading and empty states as polite `role="status"` live regions (#12401 — thanks @pacocartones) +- **feat(providers):** skip GitHub combo members missing from the live synced catalog, and drop Copilot models that are policy-disabled or hidden from the model picker ([#12473](https://github.com/diegosouzapw/OmniRoute/pull/12473)) — thanks @RaviTharuma +- **feat(i18n):** locale-expansion tooling — `npm run i18n:add-locale` adds a language to config, dashboard, docs mirrors, CLI, README/indexes and site in one command; browser, CLI and cookie detection resolve `uk`, `fil`/`tl`, `zh-Hant` (and the retired legacy `in` → `id`) via config aliases; `config/i18n.json` now ships in the npm package so the published CLI can read it; new real-translation ratio gate (`npm run i18n:check-ratio`, advisory) with a per-locale ratchet baseline; `run-translation --adopt` restores `.i18n-state.json`; `sync-language-bars` generates 🌐 bars from config; `validate_translation.py` loads its allowlist again. Retires the duplicate `in` locale (Indonesian mislabelled as Hindi) — 42 honest locales; saved `NEXT_LOCALE=in` / `OMNIROUTE_LANG=in` keep working. (#12496) +- **feat(api):** Emit gateway-measured `tokens_per_second` (TTFT excluded) on streaming usage and `X-OmniRoute-Tokens-Per-Second` when first-token latency is known ([#12616](https://github.com/diegosouzapw/OmniRoute/issues/12616)) ([#12631](https://github.com/diegosouzapw/OmniRoute/pull/12631)) — thanks @RaviTharuma +- **feat(opencode-plugin): map gateway cost/usage/tok/s onto OpenCode inference payloads** — the official plugin copies `X-OmniRoute-Response-Cost`, token counts, `X-OmniRoute-Tokens-Per-Second` / `usage.tokens_per_second`, TTFT, and the winning `X-OmniRoute-Model` onto the JSON/SSE body OpenCode already consumes. Missing tok/s is left unset (never `tokens / latency`). (#12636) — thanks @RaviTharuma +- **feat(combo):** add combo strategy `quota-weighted`: skip exhausted accounts, then weighted-draw by leftover / in-flight load; existing conversations stay pinned until the account is empty ([#12789](https://github.com/diegosouzapw/OmniRoute/pull/12789)) — thanks @HouMinXi +- **feat(models):** Account live listings become the chat catalog source for Claude, Codex, Copilot, and AGY; public metadata only fills prices on IDs those accounts already list. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **fix(models):** Union `agy` and `antigravity` live catalogs so `agy/gemini-3.8-flash-high` is not rejected after the prefix folds to `antigravity`. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **feat(opencode):** Add `@omniroute/opencode-plugin-v2`, an opencode v2 plugin that gives a v2 install a route to an OmniRoute gateway: it publishes models, combos and auto-combos into the host catalog, refreshes them lazily behind a 300s TTL, and keeps serving the last known catalog from an on-disk snapshot when the gateway is unreachable. The v1 plugin (`@omniroute/opencode-plugin`) is untouched and keeps working as before. The plugin adapts to the host instead of assuming one shape: it reads the shape the host seeds into the catalog draft and publishes the `api` block, the pre-`api` top-level fields, or both. The gateway key comes from the host credential store when one is connected, so no secret needs to sit in a config file; a source the gateway refuses is named in a warning instead of silently degrading the catalog to raw model ids; and tool schemas bound for a Gemini model are stripped of the JSON-Schema keywords Gemini rejects, so tool calling survives that route ([#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870)) — thanks @maxmad64bis +- feat(services): show sanitized CLIProxyAPI account health from its authenticated management API without exposing credentials, file paths, or raw account metadata (#6342) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add AnySearch free web search + URL extract (webFetch) with typed results, credential validation, REST routing, and MCP selection - fallback-only ([#11690](https://github.com/diegosouzapw/OmniRoute/pull/11690)) — thanks @Xxx91n +- **feat(dashboard):** display clamped `[0, 100]%` cached input token ratio in request logs table ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) — thanks @b3nw +- **feat(catalog):** add `OMNIROUTE_DISABLE_THINKING_LEVEL_VARIANTS` feature flag to optionally filter out thinking level variants from model catalog ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) — thanks @b3nw +- **feat(dashboard):** continuously export call logs to external analytics stores. A pluggable destination registry ships the full Logs-tab record set on an hourly `JobRegistry` cron, with a persisted per-destination cursor, batched inserts, a config UI rendered from each destination's own field descriptors, and a REST layer (`/api/log-export/*`) for CRUD, a connection test, and an on-demand run. A destination can opt into `includeBodies` to also ship the request and response payloads shown in the Logs detail pane, including the client and provider views of each call; this is off by default, and payloads inherit the dashboard's PII sanitisation, secret redaction and `noLog` handling. Google BigQuery is the first destination, using a service-account key stored encrypted at rest and streaming inserts keyed by call-log id, into a table that is day-partitioned on `timestamp` and clustered on `api_key_name`, `provider`, `model` and `status`. ([#11945](https://github.com/diegosouzapw/OmniRoute/pull/11945)) — thanks @dpozimski +- **feat(providers):** add MaxAI as a signed, OpenAI-compatible provider serving its 13 paid chat models (GPT-5.6 / Luna / Thinking, Claude 5 Sonnet, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite, Grok 4.1-fast / 4.5, DeepSeek V3.2 / R1, Llama 3.3 70B) through OmniRoute's `/v1` endpoint, with per-request HMAC-SHA1→SM3→AES request signing, live model + context-window discovery from `/models/get_config`, and prompted tool-calling translated to OpenAI `tool_calls` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI vision input — image_url content parts are forwarded inline in `message_content` to the 6 vision-capable models (GPT-5.6 / Luna / Thinking, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite) ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI image generation — 6 image models (gpt-image-1, dall-e-3, flux-1-schnell/dev/pro, sd3-medium) exposed through `POST /v1/images/generations` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI document RAG — inline base64 file/document attachments are uploaded to MaxAI (content-addressed `doc_id`) and attached to the chat via `doc_list` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** browserless MaxAI onboarding — email device-pair login (`/api/providers/[id]/login`) and signed access-token refresh, so a connection can be created and kept fresh without a real browser or Google OAuth ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** per-provider TLS impersonation profile (MaxAI presents a Windows Firefox-150 client fingerprint) so its bot-sensitive endpoints accept OmniRoute traffic ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** add **Nimble** as a web-search and web-fetch provider (`nimble-search`) — `/v1/search` routes to Nimble's search API at `lite` depth (locale, freshness and include/exclude domain filters mapped onto the shared request contract), and `/v1/web/fetch` routes to Nimble Extract, which covers all four fetch formats (`markdown`, `html`, `links`, `screenshot`) from a single call. One API key serves both surfaces. ([#11620](https://github.com/diegosouzapw/OmniRoute/pull/11620)) — thanks @wildcard +- The npm publish is automatic again, through npm Trusted Publishing (OIDC): the hosted `stage-npm` job publishes with a short-lived credential minted from GitHub's id-token — no `NPM_TOKEN`, no 2FA prompt, provenance attached. `publish_mode=staged` (owner approves with 2FA) and `direct` (token) remain available on `workflow_dispatch`. ([#11931](https://github.com/diegosouzapw/OmniRoute/pull/11931)) +- **feat(providers):** Add **Opper** as an API-key gateway provider — EU-hosted AI gateway with 700+ models from 30+ providers behind one OpenAI-compatible API and one key (`OPPER_API_KEY`); model ids use `provider/model` format (e.g. `anthropic/claude-sonnet-4-6`, `openai/gpt-5`); live model catalog at `https://api.opper.ai/v3/compat/models`; entry mirrors `requesty` (same shape, `passthroughModels: true`, no static seed) ([#11629](https://github.com/diegosouzapw/OmniRoute/pull/11629)) +- **feat(dashboard):** the `/dashboard/orchestration` snapshot hook now subscribes to the `agents` WebSocket channel (`agent.task.updated`) instead of `requests` as its refetch trigger, and relaxes its background poll from 5s to 30s while that WS connection is up — falling back to the tighter 5s cadence, reprogrammed live on any connect/disconnect transition, whenever the socket is down. ([#12409](https://github.com/diegosouzapw/OmniRoute/pull/12409)) +- **feat(dashboard):** Orchestration canvas quick wins — search box plus state/source/provider filter chips with a one-click clear, and per-source collapse/expand, all reflected in the URL so a filtered/collapsed view is shareable and survives a refresh; the detail drawer gained a "copy trace JSON" action and hardened error/empty-state and accessibility handling; the Agents-tab edges now animate traveling particles along active (running) connections; and the canvas node/edge status colors moved off fixed hex values onto theme-aware `--orch-status-*` CSS custom properties, so they adapt correctly to light/dark mode. ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- **chore(dashboard):** Orchestration UI hardening pass and the missing component/model test coverage it called for — `OrchestratorNode`/`ActivityNode`/`OverflowNode` rendering, the `?node=`/overflow-click page routing, the Agents-tab orchestrator-click no-op and `showCompleted` toggle, and the overview kanban's done-column sort order (#12270, #12271). ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- New `/dashboard/orchestration` page: live unified view of everything running — Cloud Agent, A2A and Conductor as a real-time graph (Agents tab), the combo cascade (Routing tab, reusing the Combo Live Studio) and a state kanban (Overview tab), with a detail drawer (trace, cost, approve/cancel). Read-only over existing APIs — no new backend. Canvas concept credit: PR #11815 design ([#12261](https://github.com/diegosouzapw/OmniRoute/pull/12261)) +- **feat(dashboard):** new "History" tab on `/dashboard/orchestration` — an Airflow-style grid of finished runs over a 24h/7d/30d preset window, one row per (source, identity), clicking a cell opens the existing detail drawer. It is backed by real persistence: A2A task lifecycle transitions are now written to the `a2a_tasks` table (purged after 30 days, configurable via `OMNIROUTE_A2A_HISTORY_RETENTION_DAYS`) and served by the new `GET /api/a2a/tasks/history` listing endpoint, with the task-detail route falling back to persisted history once a run leaves the in-memory snapshot. Conductor runs stay remote and are not persisted locally — the tab says so instead of silently omitting them. ([#12479](https://github.com/diegosouzapw/OmniRoute/pull/12479)) +- **feat(dashboard):** the orchestration detail drawer gained a "Repeat" action for Cloud Agent, A2A and Conductor tasks — a two-click confirm (click once to arm, click again within the confirm window to fire) re-submits the original prompt/input as a new run. The button is disabled with an explanatory tooltip whenever the original input can't be recovered from the loaded task detail (e.g. it never carried a prompt, or the detail failed to load). Two limitations of the A2A variant, by design: it targets the `/a2a` JSON-RPC endpoint, which authenticates with an API key only (`REQUIRE_API_KEY=true` or a configured `OMNIROUTE_API_KEY` makes a dashboard-session repeat answer `HTTP 400` — surfaced verbatim in the drawer's error line, never as a success), and the `message/send` call is SYNCHRONOUS: the POST blocks for the whole skill run, so the success confirmation only appears once the repeated task finishes. A dashboard-authenticated A2A creation path is deliberately left to a follow-up — widening the endpoint's auth posture is an operator decision, not a side effect of this feature. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(a2a):** A2A task execution now records which memories were consulted for the task's last user message as `metadata.memoryHits` (id/key/type/content-snippet) plus a `memory_hits` history event, purely for observability — the retrieved memory is never injected into a skill's prompt or behavior. Gated by the `OMNIROUTE_A2A_MEMORY_HITS` kill-switch (default enabled; set to `0` to skip the recall lookup entirely). The drawer's new "Memory used" section lists these hits for a2a tasks and is omitted whenever there are none. Known limitation: recall only resolves under the keyless posture — a keyed caller's task owner is a SHA-256 prefix of the API key, while memory rows are keyed by the database api-key id, and no hash→id lookup exists today, so the hit list stays empty for keyed callers. The recorded hits are also kept out of the task's own `input` (and therefore out of the persisted input and of the "Repeat" request body), so repeating a task never re-sends the previous run's memory snippets. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(providers):** add a Perplexity Agent API provider (`perplexity-agent` / `pplx-agent`) for Perplexity `/v1/responses`, including the documented Anthropic, OpenAI, Google, xAI, DeepSeek, Z.AI, Moonshot/Kimi, NVIDIA, and Perplexity model IDs plus Anthropic-model `max_output_tokens` compatibility. ([#12103](https://github.com/diegosouzapw/OmniRoute/pull/12103)) — thanks @AIB1TAL0S +- **feat(providers):** add RPD (Requests Per Day) limit to provider rate limit overrides across UI, schemas, DB, and i18n ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) — thanks @b3nw +- **feat(providers): add UC Direct (uncensored.com Developer API), the metered OpenAI-compatible surface.** A standard OpenAI-compatible passthrough (default executor) for uncensored.com's official REST API at `https://api.uncensored.com/api/v1`: `X-api-key` auth (never-expiring `uai_sk_live_` key), `POST /chat/completions` with streaming SSE and native tool-calling, and the full live metered catalog (82 models across 15 providers, discovered from the public `GET /v1/models`). Registered as provider `uc-direct` (alias `ucd`). Complements the un-metered `uc` persona provider — same models, metered credits and a plain API key instead of a subscription session. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(providers): add UC (uncensored.com), the un-metered subscription "persona" chat as an OpenAI-compatible provider.** A WebSocket web-app port: a durable Clerk credential mints a short-lived session token per connect (browserless — no API key), driving UC's persona socket. Ships the browserless email-code login (request → verify → harvest), the 19 verified persona models (Claude Opus, Gemini, Grok, GLM, Kimi, DeepSeek, MiniMax, incl. the uncensored variants), prompted `` tool-calling with a per-model code-style dialect + auto-cure retry for guardrailed models, live ``/reasoning split, streaming + non-streaming OpenAI responses, and full quota/auth error surfacing (paywall / message-limit / rate-limit → 429, invalid session → 401 re-login). Full multimodal parity via the persona blob-upload layer: **vision** (image input, 15 vision-capable models), **document RAG** (PDF/doc upload, server-side extraction), **image generation** (22 models), **video generation** (14 models, async signed-url → poll), and **TTS** (streaming MP3). Registered as provider `uc` (alias `ucn`). The metered OpenAI-compatible Developer API is a separate `uc-direct` provider. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(logs):** add copy-all button to request detail modal ([#11083](https://github.com/diegosouzapw/OmniRoute/pull/11083)) — thanks @AndrianBalanescu +- **feat(check):** make serviceKinds required and add the reverse-walk provider consistency gate ([#11392](https://github.com/diegosouzapw/OmniRoute/pull/11392)) — thanks @Tushar49 +- **feat(providers):** only advertise verified-working connections by default ([#11448](https://github.com/diegosouzapw/OmniRoute/pull/11448)) — thanks @alltomatos +- **feat(resilience):** add hierarchical concurrency admission ([#11493](https://github.com/diegosouzapw/OmniRoute/pull/11493)) +- **feat(providers):** derive + surface expiry for JWT-bearing web cookies (#11497) ([#11505](https://github.com/diegosouzapw/OmniRoute/pull/11505)) — thanks @oyi77 +- **feat(tunnels):** support named cloudflared tunnels via CLOUDFLARED_CONFIG ([#11561](https://github.com/diegosouzapw/OmniRoute/pull/11561)) — thanks @ggiak +- **feat(compression):** make proactive context-compression threshold a live setting ([#11564](https://github.com/diegosouzapw/OmniRoute/pull/11564)) — thanks @ragnar-claude +- **feat(resilience):** add provider peak-hour protection ([#11622](https://github.com/diegosouzapw/OmniRoute/pull/11622)) — thanks @Neuron-Mr-White +- **feat(providers):** add 1min.ai provider ([#11631](https://github.com/diegosouzapw/OmniRoute/pull/11631)) — thanks @zero-executioner +- **feat(dashboard):** collapsible JSON tree viewer for request/response payloads ([#11703](https://github.com/diegosouzapw/OmniRoute/pull/11703)) — thanks @hartmark +- **feat(routing):** add LiquidAI LFM2.5-2.6B free tier via OpenRouter ([#11752](https://github.com/diegosouzapw/OmniRoute/pull/11752)) — thanks @brick30llc-ctrl +- **feat(pricing):** add GLM-5.3-Flash pricing, model specs, and catalog registration ([#11830](https://github.com/diegosouzapw/OmniRoute/pull/11830)) — thanks @AndrianBalanescu +- **feat(routing):** add relayMode for schema-locked context handoffs ([#11839](https://github.com/diegosouzapw/OmniRoute/pull/11839)) — thanks @raheemuddin786 +- **feat(config):** add support for runtime system prompt configuration and hot-reloading ([#11841](https://github.com/diegosouzapw/OmniRoute/pull/11841)) — thanks @raheemuddin786 +- **feat(routing):** expand connection-aware quota prefilter across combo strategies (#11682) ([#11850](https://github.com/diegosouzapw/OmniRoute/pull/11850)) — thanks @HouMinXi +- **feat(models):** reactive discovery sync after Antigravity model-not-found 404 ([#11888](https://github.com/diegosouzapw/OmniRoute/pull/11888)) — thanks @MumuTW +- **feat(models):** auto-aliases and token limits from synced Antigravity discovery ([#11889](https://github.com/diegosouzapw/OmniRoute/pull/11889)) — thanks @MumuTW +- **feat(settings):** raise sticky round-robin limit caps to 1000 ([#12015](https://github.com/diegosouzapw/OmniRoute/pull/12015)) — thanks @adivekar-utexas +- **feat(resilience):** operator-configurable global credential health check interval ([#12043](https://github.com/diegosouzapw/OmniRoute/pull/12043)) — thanks @adivekar-utexas +- **feat(quota):** use official OpenCode Go usage API ([#12124](https://github.com/diegosouzapw/OmniRoute/pull/12124)) — thanks @ddarkr +- **feat(resilience):** default the credential health check sweep to 60 minutes ([#12138](https://github.com/diegosouzapw/OmniRoute/pull/12138)) — thanks @adivekar-utexas +- **feat(quality):** new-code mode for the complexity and dead-code ratchets (Clean as You Code) ([#12142](https://github.com/diegosouzapw/OmniRoute/pull/12142)) +- **feat(dashboard):** orchestration canvas — unified model + snapshot hook (part 1/2) ([#12156](https://github.com/diegosouzapw/OmniRoute/pull/12156)) +- **feat(usage):** add Kilo Code balance and Kilo Pass quotas ([#12178](https://github.com/diegosouzapw/OmniRoute/pull/12178)) — thanks @killer30001000 +- **feat(providers):** refresh vendored ChatGPT Web connector to v4.0.7 ([#12181](https://github.com/diegosouzapw/OmniRoute/pull/12181)) — thanks @backryun +- **feat(providers):** manual "Clear cooldown" action in the cooling panel ([#12224](https://github.com/diegosouzapw/OmniRoute/pull/12224)) — thanks @patrykkopycinski +- **feat(providers):** restore ChatGPT Web via clean-room browser transport ([#12239](https://github.com/diegosouzapw/OmniRoute/pull/12239)) — thanks @backryun +- **feat(sse):** wire the PROVIDER_PROFILES window gate into the global provider cooldown ([#12247](https://github.com/diegosouzapw/OmniRoute/pull/12247)) +- **feat(providers):** modernize CLOVA Studio chat and embeddings ([#12277](https://github.com/diegosouzapw/OmniRoute/pull/12277)) — thanks @backryun +- **feat(browser):** adopt Obscura as primary headless browser engine with Chromium fallback ([#12286](https://github.com/diegosouzapw/OmniRoute/pull/12286)) — thanks @opensource-elearning +- **feat(sse):** full language parity for output styles (es/de/fr/it/ru/zh + autoDetect) ([#12289](https://github.com/diegosouzapw/OmniRoute/pull/12289)) +- **feat(quality):** complete test:scoped — --full map rebuild, stdin selection, CI loader parity (#8084 D1) ([#12353](https://github.com/diegosouzapw/OmniRoute/pull/12353)) +- **feat(providers):** refresh Fable, Cursor, and Devin catalogs ([#12367](https://github.com/diegosouzapw/OmniRoute/pull/12367)) — thanks @backryun +- **feat(video):** redact transcript text from logs and durable memory (#12150 P1) ([#12427](https://github.com/diegosouzapw/OmniRoute/pull/12427)) +- **feat(dashboard):** parent-link, genuine-continuation badge, and modal perf fixes ([#12448](https://github.com/diegosouzapw/OmniRoute/pull/12448)) — thanks @hartmark +- **feat(dashboard):** adaptive context-budget dial on compression panel ([#12488](https://github.com/diegosouzapw/OmniRoute/pull/12488)) — thanks @HouMinXi +- **feat(providers):** refresh Z.ai Web models and browser transport ([#12524](https://github.com/diegosouzapw/OmniRoute/pull/12524)) — thanks @backryun +- **feat(video):** redact raw client-snapshot transcript fields in the detailed log (#12150 P2) ([#12528](https://github.com/diegosouzapw/OmniRoute/pull/12528)) +- **feat(providers):** refresh NVIDIA hosted models ([#12538](https://github.com/diegosouzapw/OmniRoute/pull/12538)) — thanks @backryun +- **feat(quota):** Moonshot Open Platform balance and TPD lock for custom nodes ([#12590](https://github.com/diegosouzapw/OmniRoute/pull/12590)) — thanks @HouMinXi +- **feat(video):** redact transcript in the in-memory pending-request snapshot (#12430 item 6) ([#12596](https://github.com/diegosouzapw/OmniRoute/pull/12596)) +- **feat(dashboard):** link a log entry's Conversation Context to its owning conversation ([#12646](https://github.com/diegosouzapw/OmniRoute/pull/12646)) — thanks @hartmark +- **feat(catalog):** eligibility-gated free-tier bucket ([#12669](https://github.com/diegosouzapw/OmniRoute/pull/12669)) +- **feat(db):** fail-closed previous_response_id continuation for redacted video turns (#12150 P2b) ([#12707](https://github.com/diegosouzapw/OmniRoute/pull/12707)) +- **feat(gamification): pay the documented `streak_bonus` and `badge_unlock` XP rewards.** `XP_REWARDS` listed both rewards but the award pipeline never paid them: the private reward table in `events.ts` omitted them, `updateStreak()` did not report when a streak extended, and badge unlocks carried no XP. Every request that extends a daily streak now pays `streak_bonus × streak length` once per UTC day (guarded by a same-day `xp_audit_log` check), and every badge unlocked through the pipeline pays `badge_unlock` once per badge (guarded by the `user_badges` primary key; `unlockBadge()` now reports whether it inserted). Bonus XP flows through the same `addXp` + level sync + global/weekly/monthly leaderboard path as action XP, so level-ups and rankings include it. The Radar supporter recognition unlock stays XP-free. (#12522 — thanks @pacocartones) +- **feat(providers):** advertise a `free-tier` capability in the provider plugin manifest for every provider with documented free models, so sidecars and dashboards can filter free-capable providers without reading the quota catalog ([#12786](https://github.com/diegosouzapw/OmniRoute/pull/12786)) — thanks @maxmad64bis +- **feat(providers):** Added EURouter as an OpenAI-compatible API-key gateway (`https://api.eurouter.ai/v1`), with live model discovery via `passthroughModels`. Its copy states that models are served by third-party upstreams listed per model, so an EU-based router is not read as EU data residency for inference. ([#13025](https://github.com/diegosouzapw/OmniRoute/pull/13025)) — thanks @ntdatt812 +- **feat(providers):** Added GreenPT as an OpenAI-compatible API-key provider (`https://api.greenpt.ai/v1`), with live model discovery via `passthroughModels`. No free-inference badge: the published docs describe a free API subscription billed per token, not a free tier. ([#13024](https://github.com/diegosouzapw/OmniRoute/pull/13024)) — thanks @ntdatt812 +- **feat(i18n):** 9 new locales — Greek (`el`), Croatian (`hr`), Serbian (`sr`), Lithuanian (`lt`), Estonian (`et`), Latvian (`lv`), Slovenian (`sl`), Maltese (`mt`), Irish (`ga`) — across the dashboard, docs mirrors, CLI, README and the site; OmniRoute now ships every official EU language (51 locales). (#13044) +- **feat(i18n):** 8 new locales — Kannada (`kn`), Malayalam (`ml`), Odia (`or`), Punjabi (`pa`), Nepali (`ne`), Sinhala (`si`), Burmese (`my`), Khmer (`km`) — across the dashboard, docs mirrors, CLI, README and the site (59 locales). The translator now restores the ICU literal escape around angle placeholders and splits oversized docs sections before translating. (#0000) ([#13660](https://github.com/diegosouzapw/OmniRoute/pull/13660)) +- feat(providers): **list Agnes 3.0 Flash as the current free chat model, drop retired 1.5 Flash, add Image 2.0/2.5 Flash plus Video 2.5/2.5 Flash, and discover the live `/v1/models` catalog (including the CN host `api.agnes-ai.cn`).** `agnes-1.5-flash` now forwards to `agnes-3.0-flash`. Video 2.5 polls `GET /v1/videos/{id}` (not the V2.0 `/agnesapi` contract). Live `/v1/models` (2026-09-09) no longer serves 1.5; the wiki marks it deprecated. 3.0 Flash is 512K context / 65,536 max output, same window as 2.5. CN-region keys use the existing per-connection base-URL field, default stays `apihub.agnes-ai.com`. ([#13120](https://github.com/diegosouzapw/OmniRoute/pull/13120)) — thanks @HouMinXi +- **feat(sse):** Codex and OpenAI catalogs list GPT-6 Astra with effort aliases (`-low` through `-ultra`); Codex CLI identity pins `@openai/codex@0.153.4` in lockstep with the image ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **feat(dashboard):** the orchestration History tab gained a "Compare runs" mode — toggling it turns each grid cell into a 2-item selection queue (a 3rd click drops the oldest pick), and picking two cells opens a side-by-side comparison panel instead of the usual detail drawer. The panel fetches both runs' detail the same way the drawer does (falling back to persisted history once a run leaves the live TTL window) and shows, per side: identity/source/state, start time, a signed `right - left` delta for duration/cost/event count, the event timeline aligned by index, and any memory hits. One side's fetch failing never blocks the other, and a delta is only ever computed when both sides have a finite value — otherwise it renders "—", never `NaN`. Comparing two runs from different sources or skills still works; a banner marks the deltas as informational rather than hiding them, since the two runs aren't a strict apples-to-apples pair. ([#12677](https://github.com/diegosouzapw/OmniRoute/pull/12677)) +- **feat(dashboard):** badge a conversation that never reached a clean stop ([#12717](https://github.com/diegosouzapw/OmniRoute/pull/12717)) — thanks @hartmark +- **feat(release):** reconcile-changelog tool + version-anchored fragment aggregation ([#12987](https://github.com/diegosouzapw/OmniRoute/pull/12987)) +- **feat(mcp):** carry context_length through MCP list_models_catalog projection (#12776) ([#13406](https://github.com/diegosouzapw/OmniRoute/pull/13406)) — thanks @KooshaPari +- **feat(cli):** make startup readiness budget configurable (#13369) ([#13433](https://github.com/diegosouzapw/OmniRoute/pull/13433)) — thanks @KooshaPari + ### 🐛 Bug Fixes +- **security(streaming):** sanitize generic mid-stream error messages before emitting OpenAI, + Responses, or Claude SSE failure frames and before diagnostic logging, while preserving raw + failures for internal classification and keeping client disconnects out of provider failure state. ([#12457](https://github.com/diegosouzapw/OmniRoute/pull/12457)) +- **fix(claude):** `blockExtraUsage: false` no longer lets 5h quota preflight skip the account; extra usage is billed after the session bar is gone, so the request must reach Anthropic ([#12803](https://github.com/diegosouzapw/OmniRoute/pull/12803)) — thanks @HouMinXi +- Sanitize HuggingChat conversation-creation and message-send transport failures before they reach client error bodies or provider logs. ([#12467](https://github.com/diegosouzapw/OmniRoute/pull/12467)) +- **fix(dashboard):** Keep local and theme-aware provider SVG icons at a definite layout size so Chromium does not collapse them to 0×0 after the v3.8.50 image-rendering change ([#12054](https://github.com/diegosouzapw/OmniRoute/pull/12054)) — thanks @ponkcore +- **fix(security):** Sanitize provider and runtime failures before public API, SSE and MCP responses and before persistent request, proxy and usage logs, preventing credentials, stack traces and host filesystem paths from crossing those boundaries while preserving stable error codes and useful diagnostics. ([#12506](https://github.com/diegosouzapw/OmniRoute/pull/12506)) +- **fix(cli):** `omniroute tunnel create` no longer crashes with `Cannot read properties of undefined (reading optsWithGlobals)` — removed the duplicate positional argument that caused Commander.js to misalign the action callback parameters ([#12295](https://github.com/diegosouzapw/OmniRoute/issues/12295)) ([#12368](https://github.com/diegosouzapw/OmniRoute/pull/12368)) — thanks @gonisulaimann +- Stop advertising Gemini Live-only models as supported audio endpoints until OmniRoute proxies the bidirectional Live protocol. ([#11710](https://github.com/diegosouzapw/OmniRoute/pull/11710)) — thanks @RaviTharuma +- **fix(kie):** reroute `flux/kontext` off the KIE Market `createTask` flow — it is catalogued with `isMarket: true` but has no Market catalog page, so KIE rejected it with "model name not supported"; it now hits the dedicated `POST /api/v1/flux/kontext/generate` / `GET /api/v1/flux/kontext/record-info` endpoints instead (#11296). ([#11985](https://github.com/diegosouzapw/OmniRoute/pull/11985)) — thanks @hartmark +- **fix(kie):** correct 12 more KIE Market catalog ids that were sent to `createTask` unchanged but diverge from KIE's documented upstream `model` values — GPT Image 2 T2I/I2I (drops the `gpt/` prefix), GPT Image 1.5 T2I/I2I (`gpt-image/` namespace), Seedream 5.0 Lite T2I/I2I (drops the `.0`), all 4 Flux 2 variants (`flux-2/` namespace, generic variant renamed `flex`), and Wan 2.7 Image / Image Pro (dash instead of dot) — each verified individually against the literal example request published on docs.kie.ai. `#11326`'s "everything else already matches" claim was wrong a second time (#11296); `z-image/4.0-*`/`z-image/4.5-*` and `flux/kontext` remain open, documented as unresolved in `KIE_MARKET_UPSTREAM_MODEL_IDS`'s comment pending further verification. ([#11667](https://github.com/diegosouzapw/OmniRoute/pull/11667)) +- **fix(dashboard):** `useApiKeySave.handleSaveApiKey` no longer forces a full upstream `/models` catalog sync on every non-curated provider connection save — callers can now pass `skipModelSync: true` to opt out, so a workflow that only wants to add one manual model no longer floods the provider's available-models list with hundreds/thousands of synced entries. The flag is a client-side intent signal only and is stripped before the connection payload is POSTed to `/api/providers`; default behavior (full sync on save) is unchanged when the flag is omitted (#11324) ([#11664](https://github.com/diegosouzapw/OmniRoute/pull/11664)) +- **Provider connections:** keep `tokenExpiresAt` when a connection is created. The create-path allowlist omitted it, so every insert stored NULL and the dashboard token badge could read a fresh connection as expired until its first background refresh ([#11368](https://github.com/diegosouzapw/OmniRoute/pull/11368)). — thanks @ntdatt812 +- **fix(combos):** the combo builder's precision-select, global-model-search, and manual-entry flows now serialize a model step's `model` string using the provider's already-computed routing-alias prefix (e.g. `oc/`) instead of rebuilding it from the raw canonical `providerId`, fixing the no-auth "OpenCode Free" provider (`opencode`) being routed to the unrelated paid "OpenCode Zen" provider (`opencode-zen`) because `opencode` doubles as a manual routing-prefix override ([#11433](https://github.com/diegosouzapw/OmniRoute/issues/11433)). ([#11670](https://github.com/diegosouzapw/OmniRoute/pull/11670)) +- **fix(radar):** the catalog feed cache now keeps `generatedAt`, the date the feed's data was built, next to `fetchedAt`, the date this install downloaded it (#11435). The feed schema requires that date and the sync path validates it, but the cache dropped it — so a feed fetched minutes ago and one carrying weeks-old figures looked identical to everything downstream, including the dashboard's "Last fetched" line. `getRadarCatalog().meta` and `GET /api/radar/status` now report both dates, the latter as its own field rather than folded into `version` — and omitted entirely for the offers and intel caches, which keep no build date, where a `null` would read as "unknown" rather than "never stored". The dashboard still shows only the fetch time; surfacing the build date there needs a new translated label and is left to a follow-up. Rows cached before migration 163 read back as `null`: unknown stays unknown instead of borrowing the fetch time. The referrals cache has persisted the same date since migration 142. — thanks @maxmad64bis +- **fix(cli):** postinstall no longer fills `JWT_SECRET` and `API_KEY_SECRET` in the installed package's `.env` (#11436). `.env.example` ships both blank on purpose: the server restores them from its durable store, or generates and persists them there, in `ensureSecrets()`. Pre-filling them defeated that — the file lives inside the package directory, so `npm i -g` replaced it and postinstall wrote _different_ values, while `ensureSecrets()` (which only acts on an empty variable) never got to restore the real ones. Both secrets rotated silently on every update, invalidating dashboard sessions and API-key CRCs. `STORAGE_ENCRYPTION_KEY` left the same list for the same reason in #1622; its comment pointed at a function that no longer exists and now names the real provisioning path. — thanks @maxmad64bis +- **fix(cli):** the CLI now says when a loaded `.env` lives inside the installed package directory (#11437). It already announces every env file it reads, without distinguishing the ones that survive an update from the one that does not: `npm i -g` replaces the package directory wholesale, so values set there are gone at the next update, silently. The warning names the durable path to move them to, and fires only when that file actually supplied a value — a file entirely shadowed by a durable one supplied nothing. A development checkout stays silent: there the same path is stable and documented in `SETUP_GUIDE.md`. — thanks @maxmad64bis +- **fix(free-models):** the shared `isFreeModel()` predicate no longer reports catalog entries marked `freeType: "discontinued"` as free, so `hidePaidModels` can't route to Pollinations' seven premium models that now require a paid key ([#11441](https://github.com/diegosouzapw/OmniRoute/pull/11441)) — thanks @maxmad64bis +- fix(ui): let AnySearch use the normal provider-icon fallback when LobeHub has no matching icon (#11449) +- Fixed the v3.8.50 Costs and Analytics dashboards so flat-rate Claude Code usage can be shown as an explicitly requested token-price estimate without changing default billed-cost semantics. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed archived usage retention so each request is priced individually instead of pricing a day's summed tokens once, which understated archived cost whenever a day mixed cache-heavy and ordinary requests. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed the Costs dashboard so it discloses when displayed figures include flat-rate token-price estimates instead of labelling them as billed spend, using the flag the analytics API already returns; the month-end projection and the CSV/JSON exports carry the same marker, and billed-cost mode is unchanged. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- **fix(combo):** attach the same combo-diagnostics trace (`poolSize`/`attemptOrder`/`excluded`/`terminalReason`, plus `x-omniroute-combo-*` headers) to the round-robin strategy's and the nested pipeline/fusion runtime-unit loop's "Maximum combo retry limit reached" 503 that the priority-strategy path already attaches for the identical terminal condition — previously those two paths returned a bare, contextless 503 ([#11462](https://github.com/diegosouzapw/OmniRoute/issues/11462)). ([#11665](https://github.com/diegosouzapw/OmniRoute/pull/11665)) +- **fix(bun):** use native `bun:sqlite` in `bootstrap-env` and `sync-env` to avoid loading `better-sqlite3` N-API addon during Bun startup ([#11468](https://github.com/diegosouzapw/OmniRoute/pull/11468)) — thanks @TheDemonTuan +- **fix(docker-bun):** make `Dockerfile.bun` install reliable with pre-install script helpers and native `bun:sqlite` smoke check ([#11470](https://github.com/diegosouzapw/OmniRoute/pull/11470)) — thanks @TheDemonTuan +- **fix(docker-bun):** remove every vendored `better-sqlite3` native addon from the Bun runtime image so startup cannot abort before the `bun:sqlite` fallback ([#11482](https://github.com/diegosouzapw/OmniRoute/pull/11482)) — thanks @TheDemonTuan +- **fix(autoCombo):** effort/alias model variants (`gpt-5.6-sol-xhigh`, `gpt-5.6`, cursor's `claude-4.6-opus-high`) inherit their base model's task fitness instead of falling to the wildcard 0.5 ([#11492](https://github.com/diegosouzapw/OmniRoute/pull/11492)) — thanks @MumuTW +- fix(db): rate-limit repeated Arena ELO leaderboard fetch-failure warnings instead of logging one per sync attempt (#11500) ([#11989](https://github.com/diegosouzapw/OmniRoute/pull/11989)) — thanks @hartmark +- **fix(db):** dedupe the raw `[Encryption] Decryption failed...` log line emitted by the lazy-decrypt views (`createLazyRowProxy`/`createLazyConnectionView`), which power `getProviderConnections()` and were re-triggering that line on every CredentialHealth/model-sync cycle for the same corrupt or stale-key credential — a fresh Proxy over a fresh row on every cycle meant the per-proxy memoization never suppressed it, unlike the dedup `decryptConnectionFields()` already had since [#9927](https://github.com/diegosouzapw/OmniRoute/issues/9927). Now shares that dedupe tracking so the line logs at most once per credential ([#11500](https://github.com/diegosouzapw/OmniRoute/issues/11500)). ([#11678](https://github.com/diegosouzapw/OmniRoute/pull/11678)) +- **fix(intelligence):** Arena ELO sync stores a synthesized base-model row for every effort/harness variant (`claude-opus-5-max` → `claude-opus-5`, `gpt-5.6-sol-xhigh (codex-harness)` → `gpt-5.6-sol`), so bare model requests reach the synced intelligence layer instead of the static fallback table; the hand-maintained `MODEL_ALIAS_MAP` — which collapsed generations (`gpt-5.5` → `gpt-5`) — is gone ([#11506](https://github.com/diegosouzapw/OmniRoute/pull/11506)) — thanks @MumuTW +- **fix(autoCombo,sse):** catalog hygiene — the static task-fitness table no longer ranks retired models above live ones (dead rows dropped, patterns matched on segment boundaries), the 7 deprecation aliases that forwarded to retired or non-existent models now point at the vendors' published replacements, an alias is no longer rewritten when the serving provider offers the id as-is, and a new `check:model-lifecycle` gate diffs both tables against a vendor lifecycle snapshot ([#11507](https://github.com/diegosouzapw/OmniRoute/pull/11507)) — thanks @MumuTW +- **fix(sse):** stop dropping resolved `thoughtSignature` values on parallel (multi tool-call) turns sent to Gemini 3.x — the claude→gemini and openai→gemini translators previously kept the signature only on the *first* function call of a message, causing Gemini to reject subsequent calls in the same turn with HTTP 400 "Function call is missing a thought_signature"; each function call now keeps its own resolved signature ([#11510](https://github.com/diegosouzapw/OmniRoute/issues/11510)). ([#11669](https://github.com/diegosouzapw/OmniRoute/pull/11669)) +- Treat Docker publishing from a non-default release branch as an expected skip instead of a failed workflow, while preserving the guard that prevents it from overwriting the `next` channel. ([#11527](https://github.com/diegosouzapw/OmniRoute/pull/11527)) — thanks @pacocartones +- fix(search): prefer credentialed providers over duckduckgo-free fallback (#11524) — the fallback-only loop ran before the credentialed-providers loop in `executeWebSearch`, making configured providers unreachable when `duckduckgo-free` was available. ([#11565](https://github.com/diegosouzapw/OmniRoute/pull/11565)) — thanks @vsd2807 +- **fix(sse):** cap the upstream headers-wait phase for STREAMING requests to a client-realistic ceiling (110s, under Codex's own ~120s hard client-abort window) instead of the flat 10-minute `FETCH_TIMEOUT_MS` default — that default was 5x longer than the body-phase readiness watchdog's own adaptive bound, so a request whose upstream never returned any response at all (not even headers, e.g. a stalled NVIDIA target behind a tool-heavy Responses→Chat translation) kept the client connection alive on keepalives only, guaranteeing the client's own patience ran out first with an opaque 499 instead of OmniRoute detecting and failing the stall fast. Non-streaming requests are unaffected — they keep the existing flat default (`open-sse/utils/fetchStartTimeoutPolicy.ts`) (#11526) ([#11668](https://github.com/diegosouzapw/OmniRoute/pull/11668)) +- **fix(ci):** protocol-clients E2E harness boots the peer-stamped custom server (`run-next.mjs dev`) instead of the bare `next` CLI, so `LOCAL_ONLY` locality resolves from the real TCP peer stamp and `/api/mcp/audit` answers 200 (open bootstrap preserved via an `OMNIROUTE_E2E_BOOTSTRAP_MODE=open` credential clear in `run-next.mjs`, pinned to loopback `HOST`) instead of a deterministic 403 (#11535) ([#11549](https://github.com/diegosouzapw/OmniRoute/pull/11549)) — thanks @jonlwheat2-gif +- **fix(docs):** the Auto-Combo engine is described with the number of factors it actually declares. Four different counts were in circulation — 6, 9, 13 and 14 across `AGENTS.md`, four guides and six code comments — while `DEFAULT_WEIGHTS` declares 15. `check:docs-counts` now reads that number from the source, so adding a factor without updating the prose fails CI ([#11545](https://github.com/diegosouzapw/OmniRoute/pull/11545)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **fix(quota-share):** in-flight leases are per request, so an aborted request's slot ages out instead of being kept alive by later traffic on the same connection ([#11547](https://github.com/diegosouzapw/OmniRoute/pull/11547)) — thanks @abhisheksharma2411 +- The free-tier summary route now serves the refreshed Radar catalog when the feed is active — the same numbers the dashboard shows — and states which catalog answered plus its real build date, instead of always reporting release-frozen figures with a stale curation date. The supporter-key live feed stays reserved to authenticated callers of the instance. ([#11550](https://github.com/diegosouzapw/OmniRoute/pull/11550)) — thanks @maxmad64bis +- **sse:** fix LiveWS/embed-WS servers crashing at startup under the Node/tsx runtime — `liveServer.ts`, `embedWsProxy.ts` and `apiBridgeServer.ts` imported `@/shared/utils/httpClientAbortGuard` without the `.mjs` extension, so the client-abort crash guard added by [#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556) was unreachable and every dependent test failed with `ERR_MODULE_NOT_FOUND` ([#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556)). ([#11671](https://github.com/diegosouzapw/OmniRoute/pull/11671)) — thanks @jonlwheat2-gif / @yxyxy +- fix(chat): shed any of the matched provider-node's routing identifiers (public prefix or internal id) before model lookup, so composites like `//` no longer reach upstream verbatim (#11557) — thanks @maxmad64bis +- **fix(cache):** a semantic-cache entry now expires on its TTL instead of surviving until the next UTC midnight, and `dbEntries` stops counting expired rows ([#11573](https://github.com/diegosouzapw/OmniRoute/pull/11573)) — thanks @ntdatt812 +- **fix(api):** `/v1/models` schedules its stale-while-revalidate rebuild through Next's `after()` again, so a stale catalog reaches the client before the rebuild blocks the event loop ([#11574](https://github.com/diegosouzapw/OmniRoute/pull/11574)) — thanks @ntdatt812 +- **fix(check):** the `known-symbols` executor conformance gate awaits the now-async `getExecutor()`, so it stops reporting all 142 registered aliases as dead symbols and can detect a lazy import that fails ([#11582](https://github.com/diegosouzapw/OmniRoute/pull/11582)) — thanks @ntdatt812 +- **fix(pack):** register `bin/cli/utils/volatileEnvPath.mjs` in `PACK_ARTIFACT_REQUIRED_PATHS`, so the prune can no longer silently drop a module the published CLI imports on every boot ([#11588](https://github.com/diegosouzapw/OmniRoute/pull/11588)) — thanks @ntdatt812 +- **i18n(vi):** translate the five reliability-column strings on the free-provider rankings page, which rendered as `__MISSING__:` markers in the Vietnamese UI ([#11589](https://github.com/diegosouzapw/OmniRoute/pull/11589)) — thanks @ntdatt812 +- **resilience:** restore the expired-connection retry-budget probe in the token-health sweep — the `!isGitHubAccessTokenOnlyConnection` carve-out reintroduced by #11608 contradicted the boundary pinned by #11592, so a GitHub connection parked at `expired` with retry budget remaining was never probed and could never self-heal ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)). ([#11672](https://github.com/diegosouzapw/OmniRoute/pull/11672)) — thanks @jonlwheat2-gif / @ntdatt812 +- **fix(dashboard):** Provider Stats now displays combo success rates on their native 0–100 percentage scale instead of multiplying them twice ([#11595](https://github.com/diegosouzapw/OmniRoute/pull/11595)) — thanks @pacocartones +- **fix(providers):** route DuckDuckGo Web Chat through the verified `duck.ai` endpoint, refresh its free model catalog, and avoid forwarding unsolved anti-abuse challenges ([#11597](https://github.com/diegosouzapw/OmniRoute/pull/11597)) — thanks @solstxce +- **fix(ui):** Console log Refresh and Copy controls now expose localized accessible names, keep copy actions visible on keyboard focus, and announce copy completion safely ([#11599](https://github.com/diegosouzapw/OmniRoute/pull/11599)) — thanks @pacocartones +- Dropped the stale-`.eslintcache` `restore-keys` fallback from both "Restore ESLint file cache" steps in `ci.yml`, so the blocking `Lint` job can no longer be served per-file verdicts computed under a different lint config, suppressions file or lockfile. `quality.yml` had already dropped it in #11963; `ci.yml` — the workflow that actually gates PRs — had not (#11600). ([#11996](https://github.com/diegosouzapw/OmniRoute/pull/11996)) +- fix(ci): pass `--pass-on-unpruned-suppressions` in `run-eslint-json.mjs` so the CI Lint job no longer fails when a suppression is merely orphaned by a genuine fix, mirroring the identical fix already in `validate-release-green.mjs` (#11600) ([#11983](https://github.com/diegosouzapw/OmniRoute/pull/11983)) — thanks @hartmark +- fix(handoff): enforce provider allowlist for universal handoff (#11602) — universal handoff now skips summarization when the selected summary model's provider is not included in the configured provider allowlist. ([#11619](https://github.com/diegosouzapw/OmniRoute/pull/11619)) — thanks @vsd2807 +- **fix(dashboard):** Show a stable error state when Search Analytics returns an HTTP error or malformed data ([#11603](https://github.com/diegosouzapw/OmniRoute/pull/11603)) — thanks @pacocartones +- **test(gamification):** pin the aggregate profile level to the XP-derived semantics of #11604 — `getAggregateXp()` now derives `currentLevel` from the summed XP (`calculateLevel(sum)`), not `MAX(stored current_level)`, and the #3484 fixture levels are aligned with the XP curve ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)). ([#11676](https://github.com/diegosouzapw/OmniRoute/pull/11676)) — thanks @jonlwheat2-gif / @pacocartones +- **fix(dashboard):** Keep the Profile level and progress aligned with aggregate XP, including bounded handling for invalid totals ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)) — thanks @pacocartones +- **fix(dashboard):** Prevent locked hidden badges from revealing their icon or opening private badge details before they are earned ([#11605](https://github.com/diegosouzapw/OmniRoute/pull/11605)) — thanks @pacocartones +- **fix(dashboard):** Restore keyboard focus after shared modals close and cancel delayed autofocus during cleanup ([#11607](https://github.com/diegosouzapw/OmniRoute/pull/11607)) — thanks @pacocartones +- **fix(dashboard):** Enable Enter and Space activation for clickable data-table rows without hijacking nested controls ([#11610](https://github.com/diegosouzapw/OmniRoute/pull/11610)) — thanks @pacocartones +- **fix(autoCombo,sse):** vendor-retired catalog ids are dropped from the auto-combo candidate pool and no longer win on leftover `arena_elo` / `user_override` rows; `getModelLifecycleDecision` consults `model-lifecycle.json` (prefix-stripped) so aggregator traffic is not `untracked` for ids the snapshot already knows ([#11625](https://github.com/diegosouzapw/OmniRoute/issues/11625)) ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)) — thanks @MumuTW +- **test(chatcore):** move Codex/Claude combo fixtures off models the lifecycle guard now rejects — `gpt-5.1-codex`/`gpt-5-codex` are vendor-retired (snapshot, #11626) and `claude-3-5-sonnet-20241022` is shut down, so native-passthrough and combo-fallback tests switched to `gpt-5.6-sol` and `claude-sonnet-4.6` ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)). ([#11675](https://github.com/diegosouzapw/OmniRoute/pull/11675)) — thanks @jonlwheat2-gif / @MumuTW +- fix(opencode-plugin): stop a free-tier budget that rounds up across a magnitude from rendering as `1000K`/`1000M` in the model picker — `fmtTokens` chose its unit from the raw token count and then rounded with `toFixed(1)`, so 999,950–999,999 printed as `1000K` rather than `1M` and just under a billion printed as `1000M` rather than `1B` ([#11684](https://github.com/diegosouzapw/OmniRoute/pull/11684)) — thanks @abhisheksharma2411 +- Cap prose-derived Antigravity quota resets at the configured model cooldown maximum while preserving authoritative Retry-After headers and Google RetryInfo hints. (#11695) Thanks @pacocartones. ([#11823](https://github.com/diegosouzapw/OmniRoute/pull/11823)) +- Compare `_rebaseline_*` notes structurally in the ratchet-bank verifier so an object-valued note no longer reports itself as rewritten on an unmodified tree — the nightly `bank-ratchet-shrinks` lane had been aborting on every run since 2026-08-11, leaving 41,672 lines of earned file-size shrinks unbanked (#11702). ([#11892](https://github.com/diegosouzapw/OmniRoute/pull/11892)) — thanks @MumuTW +- **fix(build):** `prepublish.ts` bundles the ChatGPT Web (Codex) MCP bridge through `runBuildTool()` instead of spawning `npx.cmd` raw, fixing the build crash on Node ≥ 20/Windows where `.cmd` shims cannot be spawned without a shell (EINVAL) ([#11704](https://github.com/diegosouzapw/OmniRoute/issues/11704)) ([#11706](https://github.com/diegosouzapw/OmniRoute/pull/11706)) — thanks @jonlwheat2-gif +- fix(codex): keep `parallel_tool_calls:false` on the translated Codex Responses Lite path (#11707) ([#11984](https://github.com/diegosouzapw/OmniRoute/pull/11984)) — thanks @hartmark +- **fix(tests):** the new Gemini Web Images retirement test (#11708) called the now-async `getExecutor()` synchronously, throwing `TypeError: getExecutor(...).getProvider is not a function` instead of asserting Gemini Web chat stays available. Awaited to match the R0.3 executor-registry refactor (#11220). ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(search):** Allow REST and MCP web-fetch requests to select the advertised `anysearch-search` provider explicitly ([#11716](https://github.com/diegosouzapw/OmniRoute/pull/11716)) — thanks @pacocartones +- **fix(providers):** Keep overnight peak-hour protection active after midnight according to the window's configured start day ([#11718](https://github.com/diegosouzapw/OmniRoute/pull/11718)) — thanks @pacocartones +- **fix(providers):** correct all five g4f.space gateway descriptions to document proof-of-work credits or member API keys, variable limits, and the remote prompt boundary instead of promising free keyless access at five requests per minute ([#11738](https://github.com/diegosouzapw/OmniRoute/pull/11738)). +- **fix(db):** `model_capabilities` is created by a migration instead of lazily on the first models.dev sync, so a clean install and an upgraded install converge on the same schema regardless of which features have run ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(ci):** `check:install-upgrade` now fails on an `npm` install truncated by ENOSPC (npm reports it as a warning and still exits 0), authenticates its health probe so the version assertion works against the hardened health payload, frees the clean-install tree before the upgrade phase, and no longer reports a schema divergence computed from a boot that never served ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(routing):** harden session-affinity key derivation against oversized explicit IDs and empty structural-payload collisions, while recognizing Gemini and common text request shapes ([#11746](https://github.com/diegosouzapw/OmniRoute/pull/11746)) — thanks @MumuTW +- **fix(packages/browser-pool):** regenerate `package-lock.json` so the `packages/browser-pool` workspace's locked `playwright`/`@types/node` (and transitives) match its `package.json` specs, fixing cache-only/offline installs (`npm ci --offline`, Nix `buildNpmPackage`) that previously failed with `ENOTCACHED` ([#11747](https://github.com/diegosouzapw/OmniRoute/issues/11747)) — thanks @benjaminkitt ([#11784](https://github.com/diegosouzapw/OmniRoute/pull/11784)) — thanks @geekyNads +- **fix(assets):** Stop distributing 79 provider and CLI logos without verified source/license provenance, keep every provider/tool available through the internal generic icon, and prevent those IDs from falling through to an external icon CDN. ([#11750](https://github.com/diegosouzapw/OmniRoute/pull/11750)) +- **fix(providers):** retire the common `chatgpt-web` / `cgpt-web` provider because its pre-key and proof-of-work provenance could not be cleared ([#11754](https://github.com/diegosouzapw/OmniRoute/pull/11754)) — explicit chat and image requests, slash-prefixed models, persisted aliases, proxy overrides, and combo targets now fail closed with a sanitized HTTP `410` / `PROVIDER_RETIRED` before upstream dispatch. Migration 163 tombstones matching connections and invalidates their active leases without deleting history or widening API-key allowlists. The independent `chatgpt-web-codex` / `cgpt-codex` provider and its MIT-noticed vendored implementation remain available. +- **fix(usage):** quota and usage refresh no longer 409 when an exclusive lease reserves the connection ([#11758](https://github.com/diegosouzapw/OmniRoute/pull/11758)) — thanks @TheDemonTuan +- Keep the embedding registry's vector width and `embedding` type on models when a synced model exists for the same id, so `/v1/models` no longer reports registry-described embedding models widthless or untyped (#11761) — thanks @rahilmavani +- Correct `google/gemini-embedding-001` on the OpenRouter route to 3072 dimensions, the width it returns when `dimensions` is not sent (#11761) — thanks @rahilmavani +- **fix(vision-bridge):** Restrict automatic vision routing and fallbacks to models available in authoritative live provider catalogs, including aliases and effort variants ([#11767](https://github.com/diegosouzapw/OmniRoute/issues/11767)) — thanks @pacocartones ([#11813](https://github.com/diegosouzapw/OmniRoute/pull/11813)) +- **fix(providers):** reclassify Cerebras as a one-time $5 signup credit (payment method required, 30-day validity), not a recurring no-card 1M tokens/day trial ([#11773](https://github.com/diegosouzapw/OmniRoute/issues/11773)) ([#12591](https://github.com/diegosouzapw/OmniRoute/pull/12591)) — thanks @HouMinXi +- fix(cli): stop prepublish from re-rebuilding the already-built ESM-only opencode-plugin dist (#11787) ([#11990](https://github.com/diegosouzapw/OmniRoute/pull/11990)) — thanks @hartmark +- **fix(db):** Fresh installs no longer log a non-fatal `no such table: compression_run_telemetry` warning when retention cleanup runs before the lazily-created telemetry table exists ([#11802](https://github.com/diegosouzapw/OmniRoute/pull/11802)) — thanks @RaviTharuma +- fix(sse): set X-OmniRoute-Selected-Connection-Id on successful combo dispatches so downstream consumers stop falling back to an empty connection id (#11810) ([#11986](https://github.com/diegosouzapw/OmniRoute/pull/11986)) — thanks @hartmark +- **fix(services):** embedded CLIProxyAPI lifecycle checks now use public `/healthz`, while model discovery uses the configured dedicated data-plane API key instead of the management password ([#11811](https://github.com/diegosouzapw/OmniRoute/pull/11811)) — thanks @RaviTharuma +- Fix persisted model steps returning an error in the playground route simulator, and warn when structural steps cannot be simulated. ([#11862](https://github.com/diegosouzapw/OmniRoute/pull/11862)) — thanks @pacocartones +- **fix(providers):** Antigravity's dynamic mitmAlias table no longer routes `gemini-3.7-flash-{high,medium,low}` to a literal tier-suffixed upstream id just because one connected account's own discovery listed it directly — those display ids always resolve through the safe `gemini-3.7-flash-tiered` static alias, so one account's Google-provisioned access no longer 404s every sibling account of the provider ([#11824](https://github.com/diegosouzapw/OmniRoute/issues/11824), [#11651](https://github.com/diegosouzapw/OmniRoute/issues/11651)) ([#11988](https://github.com/diegosouzapw/OmniRoute/pull/11988)) — thanks @hartmark +- **fix(dashboard):** Expose Profile loading, errors, page structure, and XP progress to assistive technologies ([#11838](https://github.com/diegosouzapw/OmniRoute/pull/11838)) — thanks @pacocartones +- **fix(socks):** `Agent.connectTimeout` now bounds both SOCKS handshake and TLS connect, and `family === null` no longer falls back to `fetch-socks` ([#11842](https://github.com/diegosouzapw/OmniRoute/pull/11842)) — thanks @maxmad64bis +- **perf(compression):** OOM mitigations for large payload hashing, memoization, and token estimation ([#11844](https://github.com/diegosouzapw/OmniRoute/pull/11844) — thanks @AndrianBalanescu) +- Excluded Next.js Node File Trace manifests (`*.nft.json`) from the published npm tarball. They are build-time metadata and are never read while serving, but had grown to 668.7 MB — 61% of the package — which pushed the upload past the registry limit and made `npm publish` fail with `413 Payload Too Large`. ([#11864](https://github.com/diegosouzapw/OmniRoute/pull/11864)) +- **fix(cli):** keep the global OmniRoute server `--base-url` separate from provider-node `--endpoint` values in add, update, and validate commands ([#11860](https://github.com/diegosouzapw/OmniRoute/pull/11860)) — thanks @pacocartones +- **fix(config):** Nous Research's `Hermes-4-405B` model now displays as "Hermes 4 405B (Nous Research)" in both the provider registry and the free-model catalog, instead of the mislabelled "Hermes 4 7B" ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#11993](https://github.com/diegosouzapw/OmniRoute/pull/11993)) +- **fix(provider/nous):** inject required user= tag into Nous Research inference requests to resolve upstream 400 "missing tags" error ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#12044](https://github.com/diegosouzapw/OmniRoute/pull/12044)) +- **fix(build):** `npm run build` now fails in one second with a named package and a copy-pasteable fix when npm silently drops an externalised optional native dependency, instead of dying four minutes in with `Module not found: Can't resolve 'better-sqlite3'` ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- **fix(install):** `postinstall` no longer throws `ReferenceError: isAndroid is not defined` — failing the whole `npm install` — when the `better-sqlite3` rebuild fallback times out; the manual-fix guidance is reachable again ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- Fixed the Alibaba free-tier allowlist test that went red on its own once the shipped catalog's `validUntil` (2026-08-27) passed, leaving every PR and `main` with a failing `Unit Tests (1/8)`. The test now builds its own packs with dates it controls, and covers the expired-pack fallback that production has actually been serving. ([#11867](https://github.com/diegosouzapw/OmniRoute/pull/11867)) +- Split the npm registry upload into its own GitHub-hosted job. npm refuses `--provenance` from a self-hosted runner (`422 ... Only "github-hosted" runners are supported`), which blocked the v3.8.50 publish; the heavy verification cannot move to a hosted runner, so it now hands the proven tarball over instead. ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(ui):** the shared `DataTable` loading state no longer reads its decorative ⏳ glyph out to assistive technology, and now carries the same `role="status"` / `aria-live="polite"` / `aria-busy="true"` semantics as `PageLoading` ([#11869](https://github.com/diegosouzapw/OmniRoute/pull/11869)) — thanks @pacocartones +- **fix(api):** Bound the `timeout` accepted by `POST /api/providers/[id]/login` to the same 15s–600s window the Adobe Firefly and Conol browser-login paths already enforce, so a request can no longer pin a headful browser and the single active-login slot with an arbitrarily large poll budget. ([#11871](https://github.com/diegosouzapw/OmniRoute/pull/11871)) — thanks @pacocartones +- **fix(api):** Stop the tunnel and MITM routes from returning raw child-process error text, which disclosed host paths, binary install locations and Tailscale `tskey-*` credentials in a body some of these routes serve to non-loopback callers, and return a real 400 instead of a framework 500 when a tunnel request body fails validation. ([#11872](https://github.com/diegosouzapw/OmniRoute/pull/11872)) — thanks @pacocartones +- **fix(open-sse):** stream TTFT/ITL are sampled from a monotonic clock (`performance.now`) instead of `Date.now`, so an NTP correction or wall-clock jump can no longer inflate a genuine TTFT or produce a negative one that the `ttft >= 0` guard silently drops — protecting the router's OTel/EWMA/`usage_history`/speed-ranking signals ([#11873](https://github.com/diegosouzapw/OmniRoute/pull/11873)) — thanks @pacocartones +- **fix(providers):** xAI `reasoning_effort: "xhigh"` now reaches grok-4.6+ instead of being silently clamped to `"high"` ([#11879](https://github.com/diegosouzapw/OmniRoute/pull/11879)) — thanks @NoxzRCW +- **fix(dashboard):** the providers page no longer crashes into the error boundary when a provider id collides with an `Object.prototype` member (`constructor`, `__proto__`); icon lookups are own-property guarded ([#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)) — thanks @NoxzRCW +- **fix(skills):** injected skill tools declared in shorthand (`{"content": "string"}`) now forward valid JSON Schema, unblocking providers that validate tool schemas strictly such as Zhipu GLM on the Console Go tier ([#11881](https://github.com/diegosouzapw/OmniRoute/pull/11881)) — thanks @NoxzRCW +- **fix(api):** `POST /api/playground/simulate-route` now surfaces `combo-ref` and `provider-wildcard` persisted combo steps with a specific warning (naming the referenced combo, or the unresolved `provider/model` wildcard) instead of folding them into a generic "unsupported step" count; a `provider-wildcard` step is also now included as an unresolved target so the operator can see it is in the route (ported from [#11882](https://github.com/diegosouzapw/OmniRoute/pull/11882) — thanks @NoxzRCW). ([#11930](https://github.com/diegosouzapw/OmniRoute/pull/11930)) +- **fix(translator):** the streaming OpenAI→Claude translator keeps upstream usage, including prompt-cache tokens, when it arrives on a trailing `choices: []` chunk (Fireworks and any upstream using `stream_options.include_usage`) ([#11883](https://github.com/diegosouzapw/OmniRoute/pull/11883)) — thanks @NoxzRCW +- **fix(cli):** `omniroute update --apply` now tells you explicitly whether a running server was detected and, if so, that you must run `omniroute restart` to apply the update — it never restarted anything and previously implied the update was already live once files were installed. The dashboard's npm-mode Update flow (`/api/system/version`) now tries OmniRoute's own PID-file-managed supervisor before falling back to pm2, and reports an honest "restart required" step instead of a silent pm2-only "skipped" that read like a completed update. The server-side latest-version lookup backing the dashboard's update banner also gained `--prefer-online`, closing the same stale-npm-cache class already fixed in the CLI's own copy for #4376 ([#11885](https://github.com/diegosouzapw/OmniRoute/issues/11885)). ([#12005](https://github.com/diegosouzapw/OmniRoute/pull/12005)) +- **fix(resilience):** clear persisted LKGP pins when a target suffers connection/provider exhaustion or is skipped before dispatch due to cooldown/exhaustion/unavailability, preventing subsequent requests from repeatedly prioritizing known-dead providers ([#11911](https://github.com/diegosouzapw/OmniRoute/issues/11911)). ([#12013](https://github.com/diegosouzapw/OmniRoute/pull/12013)) — thanks @HouMinXi +- **fix(sse):** the OpenAI→Claude stream translator now defers the terminal `message_delta`/`message_stop` emission until the real usage block has arrived (or a genuine end-of-stream flush forces it) instead of emitting it immediately on `finish_reason` — previously, when the trailing usage-only chunk (`{"choices":[],"usage":{...}}`) arrived *after* the `finish_reason` chunk (the normal order for Fireworks/vLLM/Together and other `stream_options.include_usage` upstreams), the client-visible `message_delta` still carried stale/zero usage even though `state.usage` was internally corrected too late to matter (ported from [#11915](https://github.com/diegosouzapw/OmniRoute/pull/11915) — thanks @HouMinXi). ([#11933](https://github.com/diegosouzapw/OmniRoute/pull/11933)) +- **fix(sse):** `stripStore()` now forces `store=false` for stateless OpenAI-compatible Responses-API targets (unless the connection has explicitly opted in via `providerSpecificData.openaiStoreEnabled`), instead of only handling the `openai`/`agentrouter` cases — a client-supplied `store` value previously passed through untouched to backends that don't actually persist responses server-side ([#11916](https://github.com/diegosouzapw/OmniRoute/pull/11916) — thanks @HouMinXi). +- **fix(models):** custom provider-node models (synced, custom, and alias-backed) now appear under their configured prefix in the unified catalog when the operator's model-id prefix mode is canonical, instead of being dropped whenever alias-inclusion was otherwise disabled ([#11918](https://github.com/diegosouzapw/OmniRoute/pull/11918) — thanks @HouMinXi). +- **fix(models):** the unified model catalog now suppresses stale static registry models (including effort-tier variants) for any provider whose active connection has an authoritative live synced catalog, not only providers already using exclusive-synced-listing — a connection with `providerUsesAuthoritativeLiveCatalog` previously kept serving both the live-synced models and the stale static rows side by side ([#11919](https://github.com/diegosouzapw/OmniRoute/pull/11919) — thanks @HouMinXi). +- **fix(ui):** `ProviderIcon`'s three remaining unguarded lookups (`PROVIDER_ICON_ALIASES`, `LOCAL_SVG_ALIASES`, `THEMED_SVGS`) now use `Object.hasOwn()` own-property checks — a provider id such as `constructor` or `__proto__` previously resolved through the prototype chain instead of falling through to the unknown-provider CDN fallback (`getLobeProviderIcon()` itself was already guarded by [#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)); `ProviderPageHeader`'s `color` field is also now optional, matching the rest of the component's defensive typing (ported from [#11920](https://github.com/diegosouzapw/OmniRoute/pull/11920) — thanks @HouMinXi). ([#11935](https://github.com/diegosouzapw/OmniRoute/pull/11935)) — thanks @NoxzRCW +- fix(ollama): preserve multi-byte UTF-8 content split across stream chunks in the Ollama NDJSON transform, which previously corrupted CJK/emoji into U+FFFD (#11921) — thanks @pacocartones +- **fix(providers):** OrcaRouter chat requests now target `/v1/chat/completions` instead of the bare `/v1` API root, fixing the upstream `404 Invalid URL (POST /v1)` ([#11923](https://github.com/diegosouzapw/OmniRoute/pull/11923)). — thanks @echel0nn +- **fix(plugins):** deliver the `onStreamComplete` event to disk-installed plugins. The event shipped in v3.8.50 (#9669) was emitted internally but had no plugin-facing wiring, so no plugin could ever subscribe: the manifest schema silently dropped `hooks.onStreamComplete`, and the loader/manager only knew the seven legacy hooks. `onStreamComplete` is now a declarable manifest hook, wired through the loader and registered by the manager like the other hooks, and its payload carries a `requestId` so consumers can correlate the stream-completion event with the originating request ([#11934](https://github.com/diegosouzapw/OmniRoute/pull/11934)) — thanks @amaleta / @pacocartones +- **fix(db):** the Qdrant embedding-model dropdown now lists local/self-hosted providers (Ollama, LM Studio, vLLM, etc.) — an active connection is treated as "configured" when the provider allows an optional API key, not only when it has a real key or OAuth, so a running local embedding provider is no longer hidden from the picker ([#11949](https://github.com/diegosouzapw/OmniRoute/issues/11949)) ([#11995](https://github.com/diegosouzapw/OmniRoute/pull/11995)) +- **fix(providers):** Vertex AI Anthropic partner-model discovery now calls the Model Garden `v1beta1` publisher list (`/v1beta1/publishers/anthropic/models`, global) and parses its `publisherModels` envelope, so Claude models auto-synced from Vertex populate the active live catalog and route at request time instead of returning `Model '' is not available in the active live catalog` ([#11991](https://github.com/diegosouzapw/OmniRoute/issues/11991)) — thanks @fabioluissilva ([#11998](https://github.com/diegosouzapw/OmniRoute/pull/11998)) +- **fix(cli):** support `--base-url` alongside `--endpoint` in `omniroute nodes add`, `update`, and `validate` subcommands to prevent global `--base-url` shadowing issues ([#11999](https://github.com/diegosouzapw/OmniRoute/issues/11999)). ([#12033](https://github.com/diegosouzapw/OmniRoute/pull/12033)) — thanks @HouMinXi +- **fix(providers):** `cloudflare-ai` no longer refuses image content parts for every Workers AI model ([#12002](https://github.com/diegosouzapw/OmniRoute/pull/12002)) — the plain-string `content` requirement behind #2539 is carried by the _model_ schema, not by the `/ai/v1/chat/completions` endpoint (measured: an all-text part array returns 200 on `@cf/mistralai/mistral-small-3.1-24b-instruct`, `@cf/meta/llama-4-scout-17b-16e-instruct` and `@cf/meta/llama-3.3-70b-instruct-fp8-fast`, and 400 on the text-only `@cf/qwen/qwen2.5-coder-32b-instruct`). `transformRequest()` flattened every array and threw on the first non-text part (#6390), so image input was refused for vision-capable Cloudflare models that accept it. All-text arrays are still flattened — the one shape every model accepts — while an array carrying a non-text part is passed through untouched, so the attachment is still never silently dropped. Regression guards: `tests/unit/cloudflare-ai-image-parts-6390.test.ts`. — thanks @davidlinfr +- **fix(resilience):** decouple the limiter-managed execution backstop from the queue-wait budget — new `requestQueue.executionMaxWaitMs` (env `RATE_LIMIT_EXECUTION_MAX_WAIT_MS`, default 600000 = 10 min) now feeds Bottleneck's post-dispatch `expiration`, while `requestQueue.maxWaitMs` keeps its documented queue-wait semantics. Previously the queue-wait budget doubled as the execution expiration, so legitimate long-running calls on non-incremental gateways (whole generation buffered before the first upstream byte, e.g. Console Go / Command Code tiers serving GLM models) were killed mid-flight at the queue budget with a false 504 `RATE_LIMIT_EXECUTION_TIMEOUT` — the local limiter undercut the provider-aware upstream fetch-start timeouts. The surfaced 504 message now names `requestQueue.executionMaxWaitMs`; the error keeps the #4165 guarantees (disclaims an upstream timeout, preserves the Bottleneck error as `cause`, branded code + trusted provenance, classified request-scoped so combo falls back). A real queue-wait bound (the `Promise.race` around `limiter.schedule()` sketched in #9533) remains future work. (#12025) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **Call logs:** keep the `error` field when an artifact exceeds the storage cap, instead of replacing it with the omission marker. The error is the only field that says *why* a request failed and is typically ~90 bytes next to the multi-hundred-KB bodies that trip the cap, so dropping it left a size-limited row undiagnosable — a provider outage, a local timeout and an upstream 400 all rendered identically. It is now preserved at every fallback stage, truncated to 4KB if it is itself large ([#12026](https://github.com/diegosouzapw/OmniRoute/issues/12026)). ([#12095](https://github.com/diegosouzapw/OmniRoute/pull/12095)) — thanks @ntdat812 +- **fix(diagnostics):** preserve the error field (truncated to 4KB with a `[truncated: …]` suffix) in every call-log artifact size-limit fallback stage. Previously the minimal fallback replaced the error with `[omitted: call log artifact size limit exceeded]`, so an oversized artifact row showed nothing about WHY the request failed — e.g. 91 of 847 opencode-go 504 rows on one production instance were undiagnosable from the dashboard. Oversized request/response bodies are still omitted exactly as before; the error cap is independent of the payload sizes that tripped the fallback. (#12026) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **fix(sse):** OpenAI Responses clients that declare the native `web_search` tool now receive a spec-shaped `web_search_call` output item with `action.sources` alongside the preserved function-call round-trip, so search results executed through OmniRoute's own search backend are consumable by standard Responses clients (Codex, pi-web-access, …). ([#12031](https://github.com/diegosouzapw/OmniRoute/pull/12031)) — thanks @watchingdogs +- **fix(cli):** use in-thread alias resolver hooks on modern runtimes to avoid deprecation noise and improve Node.js forward compatibility ([#12073](https://github.com/diegosouzapw/OmniRoute/issues/12073)). ([#12083](https://github.com/diegosouzapw/OmniRoute/pull/12083)) — thanks @xiaoyaner0201 +- **fix(combo):** an operator-set **Agent Features → Context length** on a combo is now honored at request time. The value was persisted and advertised through `/v1/models`, but `resolveComboContextLimit()` never consulted it — so a multi-target combo whose members carry no per-model window fell through to the provider's generic `defaultContextLength` (openrouter 128000, command-code 200000) and rejected large requests with `Input exceeds context window … limit 128000` despite the combo being explicitly sized much larger. An identical single-target combo worked, because it collapses to its concrete target before the guard runs. Invalid values (0/negative/NaN/Infinity) are ignored, so the existing target → combo-min → fallback order is unchanged. ([#12090](https://github.com/diegosouzapw/OmniRoute/pull/12090)) — thanks @adivekar-utexas +- **fix(sse):** passthrough streams now estimate usage on finish when upstream closes without usage even with `stream_options.include_usage` — avoids `0 tokens / 0%` for providers that stay silent (and correctly handles trailing empty-choices usage) ([#12151](https://github.com/diegosouzapw/OmniRoute/pull/12151)) — thanks @maxmad64bis +- **fix(combos):** clearing an agent feature in the combos editor now persists — unchecking context cache protection, or emptying the system message or tool filter, sends an explicit `null` instead of dropping the field from the `PUT` body, which the update merge read as "leave unchanged" ([#12177](https://github.com/diegosouzapw/OmniRoute/pull/12177)) — thanks @foreveryh / @ntdatt812 +- **fix(memory):** self-hosted embedding endpoints now vectorize — the vector width is measured from the first embedding that comes back instead of being read from a registry that cannot describe them, so `vec_memories` is created and memories stop piling up unvectorized behind a green health check ([#12180](https://github.com/diegosouzapw/OmniRoute/pull/12180)) — thanks @kanade-hoshino / @ntdatt812 +- **fix(translator):** the leading `system` message now reaches Responses-API upstreams when its `content` is a content-part array — it was read as `typeof content === "string" ? content : ""`, so a prompt-caching client (Anthropic `cache_control`, the shape LiteLLM and the Anthropic SDK emit) had its entire system prompt replaced by an empty `instructions`. The request was still accepted with a normal `prompt_tokens` count, so the model answered with no instructions and nothing in the response said they were missing. Mid-conversation system turns already handled the array shape ([#7056](https://github.com/diegosouzapw/OmniRoute/pull/7056)); only the first one did not ([#12206](https://github.com/diegosouzapw/OmniRoute/issues/12206)). Regression guard: `tests/unit/translator-openai-responses-system-content-parts.test.ts`. ([#12207](https://github.com/diegosouzapw/OmniRoute/pull/12207)) — thanks @Gorillaz322 +- **fix(free-tier):** `/api/free-tier/summary` no longer computes its totals from a Radar feed built before the catalog the running release ships. When the cached feed is older — or carries no build date at all — the route answers from the shipped catalog, resolved through the operator's local model state so disabled and tombstoned models stay out of the numbers ([#12215](https://github.com/diegosouzapw/OmniRoute/pull/12215)). — thanks @maxmad64bis +- **fix(oauth):** Keep a Claude personal workspace and a Team organization as separate connections — they share the same email and `accountUUID`, so the email-only OAuth dedup let the second login overwrite the first account's tokens; `organizationUUID` now disambiguates them, the way `workspaceId` does for Codex ([#12222](https://github.com/diegosouzapw/OmniRoute/pull/12222)) — thanks @davidebaraldo +- **fix(resilience):** a 402 on a single paid model of a passthrough/gateway provider (e.g. `kilo-gateway`, `ollama-cloud`) no longer terminalizes the whole connection with a never-auto-recovered `credits_exhausted` status — only the paid model is locked out, so free models on the same key keep serving. 402 variant of [#3027](https://github.com/diegosouzapw/OmniRoute/issues/3027). Single-credential providers are unaffected — a 402 there is still treated as the key being genuinely out of credit ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239) / [#10616](https://github.com/diegosouzapw/OmniRoute/issues/10616)) ([#12242](https://github.com/diegosouzapw/OmniRoute/issues/12242)) — thanks @brick30llc-ctrl ([#12266](https://github.com/diegosouzapw/OmniRoute/pull/12266)) — thanks @geekyNads +- **fix(sse):** trust `finish_reason: "length"`/`"max_tokens"` over the reasoning-consumed-token ratio in response quality validation, so a reasoning model truncated below the old 90% threshold correctly fails and retries instead of returning empty content as a silent "success" ([#12262](https://github.com/diegosouzapw/OmniRoute/pull/12262)) — thanks @brick30llc-ctrl +- **fix(settings):** `PUT /api/settings/cache-config` now persists `alwaysPreserveClientCache` to the flat general settings the runtime cache-control policy actually reads; previously the value landed in the databaseSettings "cache" section and was silently ignored, so the endpoint had no effect on `cache_control` passthrough ([#12304](https://github.com/diegosouzapw/OmniRoute/pull/12304)) — thanks @davidebaraldo +- **fix(grok-cli):** treat omitted SuperGrokPro `creditUsagePercent` as 0% used so Provider Limits still renders a weekly bar (proto3 zero-elision) ([#12312](https://github.com/diegosouzapw/OmniRoute/pull/12312)) — thanks @HouMinXi +- **fix(combo):** Expose the two Auto-Combo scoring factors nobody could set — the combo validation schema and the dashboard weight sliders both declared 13 of the scorer's 15 factors, so `connectionDensity` (spreads load across a provider's connections) and `quality` were dropped on save and offered nowhere. The sliders also shipped their own default table that differed from the engine's on every non-zero factor and summed to 1.05, so the percentages shown next to them added up to 105%. Both lists now match `DEFAULT_WEIGHTS`, and a test keeps them there. Note that a combo whose stored `weights` omitted the two keys was effectively running with them at zero and the other thirteen renormalized upward; it now runs with the engine's intended distribution, so its routing does shift ([#12314](https://github.com/diegosouzapw/OmniRoute/pull/12314)) — thanks @maxmad64bis +- **fix(docs):** The free-tier reference no longer says its numbers come "confidence tagged per row" — no catalog entry carries a confidence tag and the API serves none, so every figure on that page is an estimate of the same, unstated quality. The page now states what an entry does vouch for: an independently documented hard stop (set by hand with the source in a comment, never defaulted to `true`) and a prompt-training disclosure, both with live counts the `check:docs-counts` gate keeps honest ([#12318](https://github.com/diegosouzapw/OmniRoute/pull/12318)) — thanks @maxmad64bis +- **fix(usage):** `adobe-firefly` and `firefly` have had a working usage fetcher since Adobe Firefly landed, but neither was ever added to the registration list, so the provider-plugin manifest, `genericQuotaFetcher` and the free-access quota cache all reported them as having no usage support — while `USAGE_SUPPORTED_PROVIDERS` said the opposite. Both are now declared, which also means their credit balance is fetched like any other declared provider's: `registerGenericQuotaFetchers` now registers a generic quota fetcher for them, and `resolveFreeAccessState` no longer returns early. A test holds the registration list to the dispatcher's switch in both directions, which is what the module's own docstring already asked for in prose ([#12321](https://github.com/diegosouzapw/OmniRoute/pull/12321)) — thanks @maxmad64bis +- **fix(quota):** drop the generic quota cache (agy / Antigravity / Claude OAuth) on an upstream 429 so reset-aware scoring does not keep a 60s stale snapshot, and force-refresh the next usage fetch so inner provider caches cannot recache the same window ([#12325](https://github.com/diegosouzapw/OmniRoute/pull/12325)) — thanks @HouMinXi +- **fix(combos):** deleting a combo now clears its persisted LKGP pins instead of leaving unreachable `key_value` rows behind ([#12326](https://github.com/diegosouzapw/OmniRoute/issues/12326)) ([#12425](https://github.com/diegosouzapw/OmniRoute/pull/12425)) — thanks @KrzysiekSko +- **fix(sse):** Keep ZWNJ (U+200C) and ZWJ (U+200D) in assistant text, reasoning and tool-call arguments — Persian/Kurdish half-space (`ارائه‌دهنده`), Arabic/Indic shaping and emoji sequences no longer lose them; the response de-obfuscation now removes joiners only between ASCII word characters, where the request side inserts them ([#12186](https://github.com/diegosouzapw/OmniRoute/issues/12186)) — thanks @rezjalibd ([#12359](https://github.com/diegosouzapw/OmniRoute/pull/12359)) — thanks @pacocartones +- **fix(resilience):** count resolved upstream 5xx results against the provider circuit breaker on the chat path — `CircuitBreaker.execute()` no longer reads a resolved `{ success: false, status: 5xx }` as a success that cancels the call-site failure, so a provider answering 503s now trips its breaker instead of staying `CLOSED` at `failureCount: 1`; single-model and combo dispatches are each accounted exactly once ([#12254](https://github.com/diegosouzapw/OmniRoute/issues/12254)) ([#12360](https://github.com/diegosouzapw/OmniRoute/pull/12360)) — thanks @pacocartones +- **fix(providers):** resolve the Codex quota auto-ping model from the live provider catalog and lifecycle registry instead of the retired `gpt-5.1-codex-mini`, and pause the ping with one actionable warning when no selectable Codex model exists rather than retrying a shut-down id every cooldown window ([#11905](https://github.com/diegosouzapw/OmniRoute/issues/11905)) ([#12361](https://github.com/diegosouzapw/OmniRoute/pull/12361)) — thanks @pacocartones +- **fix(api):** keep the `{created, data}` wrapper on combo-routed `/v1/images/generations` responses and default Codex image results to `b64_json` on both `/v1/images/generations` and `/v1/images/edits` so Codex CLI's built-in `image_gen` can decode them ([#12268](https://github.com/diegosouzapw/OmniRoute/issues/12268)) ([#12362](https://github.com/diegosouzapw/OmniRoute/pull/12362)) — thanks @pacocartones +- **fix(sse):** Name the shadowed custom provider node when a built-in provider id/alias (e.g. `openference` → `of`) reserves the prefix of an existing OpenAI/Anthropic-compatible node, so the runtime `No active credentials for provider: ` error explains that the prefix routed to the built-in and never reached the node's healthy connections, instead of contradicting the dashboard ([#11943](https://github.com/diegosouzapw/OmniRoute/issues/11943)) — thanks @morpheus9393 ([#12365](https://github.com/diegosouzapw/OmniRoute/pull/12365)) — thanks @pacocartones +- **fix(i18n):** wrap `ccOnboardingKeyPlaceholder` in ICU single quotes across all 43 locales so angle brackets render literally instead of being parsed as rich-text tags, which crashed the Claude Code onboarding block with `INVALID_MESSAGE: INVALID_TAG` ([#12302](https://github.com/diegosouzapw/OmniRoute/issues/12302)) ([#12369](https://github.com/diegosouzapw/OmniRoute/pull/12369)) — thanks @gonisulaimann +- **fix(models):** publish `effort_tiers` on Kimi K3's synced base-model entries (`kmca/k3`, `kmca/k3-256k`) so catalog-only clients (OpenCode, plain SDK pickers) can see and select the reasoning tiers (`low`/`high`/`max`) the synced metadata already carried — the `isSkippedEffortProvider` gate no longer suppresses tier visibility on those base entries, while synthetic `-` variant generation stays prevented and Codex/GLM base models remain excluded unchanged ([#12299](https://github.com/diegosouzapw/OmniRoute/issues/12299)) ([#12371](https://github.com/diegosouzapw/OmniRoute/pull/12371)) — thanks @gonisulaimann +- **fix(guardrails):** keep `auto`/`auto/*` virtual combos exempt from the Vision Bridge `fixedModel` credential guard so a combo target is passed through instead of silently falling back to global auto-selection ([#12237](https://github.com/diegosouzapw/OmniRoute/issues/12237)) ([#12373](https://github.com/diegosouzapw/OmniRoute/pull/12373)) — thanks @pacocartones +- **fix(combo):** capability-filter exhaustion caused by `max_tokens` above every target's known output limit now reports that reason (requested `max_tokens` vs the pool's highest known ceiling) instead of the unrelated "supports structured output" message ([#12229](https://github.com/diegosouzapw/OmniRoute/issues/12229)) — thanks @DW-MediaLab ([#12374](https://github.com/diegosouzapw/OmniRoute/pull/12374)) — thanks @pacocartones +- **fix(auth):** the `least-used` account strategy now prefers accounts without backoff before falling back to oldest `lastUsedAt`, the same tie-break `round-robin` already applies, so a failover no longer lands on a just-rate-limited account for a single request ([#12279](https://github.com/diegosouzapw/OmniRoute/issues/12279)) — thanks @tenshiak ([#12375](https://github.com/diegosouzapw/OmniRoute/pull/12375)) — thanks @pacocartones +- **fix(docker):** the `chatgpt-web-codex-browser` image now finds the Chrome binary under `chrome-linux64/` (Chrome for Testing layout in `playwright:v1.62.0-noble`) as well as the legacy `chrome-linux/`, so the container no longer crash-loops with `exec: --headless=new: not found` ([#12024](https://github.com/diegosouzapw/OmniRoute/issues/12024)) ([#12376](https://github.com/diegosouzapw/OmniRoute/pull/12376)) — thanks @pacocartones +- **fix(providers):** declare `groq/compound` and `allam-2-7b` as non-reasoning models in the curated Groq registry so `reasoning_effort` / `output_config.effort` / `thinking` from Claude Code are stripped instead of forwarded, which Groq rejected with HTTP 400 ([#12134](https://github.com/diegosouzapw/OmniRoute/issues/12134)) ([#12379](https://github.com/diegosouzapw/OmniRoute/pull/12379)) — thanks @pacocartones +- **fix(executors):** `OpencodeExecutor` no longer forces a direct connection when the connection has a proxy assigned in Proxy Management but no per-account proxies: the single-account fast path used to wrap the upstream dispatch in the direct-egress sentinel, discarding the ambient proxy context the chat handler had pinned from `proxy_assignments`, so API-key `opencode`/`opencode-go` connections egressed from the host IP (and hit geoblocks) despite the assignment. The direct pin is now applied only when no ambient proxy context exists ([#11894](https://github.com/diegosouzapw/OmniRoute/issues/11894) — thanks @hizzt) ([#12380](https://github.com/diegosouzapw/OmniRoute/pull/12380)) — thanks @pacocartones +- **fix(api):** `GET /v1/models` with `MODELS_CATALOG_PREFIX_MODE=canonical` (or `?prefix=canonical`) now lists providers whose registry alias is undefined or equal to their own id (Antigravity, Antigravity CLI and other self-aliased built-ins) — their single `provider/model` id was dropped by the alias/canonical duplicate guard in the static, synced, custom and alias-backed catalog loops ([#12058](https://github.com/diegosouzapw/OmniRoute/issues/12058)) — thanks @cheynetom ([#12381](https://github.com/diegosouzapw/OmniRoute/pull/12381)) — thanks @pacocartones +- **fix(translator):** Drop replayed `thinking` blocks that carry no signature (the shape produced from cross-provider `reasoning_content`) instead of stamping the default Claude signature on them, which Anthropic rejected with `400 Invalid signature in thinking block` on the next turn served by an Anthropic rung ([#12105](https://github.com/diegosouzapw/OmniRoute/issues/12105)) — thanks @atescivitci-cmd ([#12386](https://github.com/diegosouzapw/OmniRoute/pull/12386)) — thanks @pacocartones +- **fix(cli):** Resolve Bun's `--preload` polyfill path against the package root instead of `dist/`, so `omniroute` installed with `bun install -g` no longer crashes at startup with `error: preload not found …/dist/open-sse/utils/setupPolyfill.ts` ([#11980](https://github.com/diegosouzapw/OmniRoute/issues/11980)) — thanks @joglomedia ([#12387](https://github.com/diegosouzapw/OmniRoute/pull/12387)) — thanks @pacocartones +- **fix(providers):** `gemini-business` now publishes its model catalog — `/v1/models` and `/v1/providers/gemini-business/models` list the 12 enterprise Gemini ids the executor understands instead of returning an empty list (#12107) ([#12389](https://github.com/diegosouzapw/OmniRoute/pull/12389)) — thanks @pacocartones +- **fix(db):** install `busy_timeout` before the SQLite connection's first statement so a process opening the database while another one closes its WAL connection waits out the transient EXCLUSIVE lock instead of dying with `database is locked`, and recognise the drivers' real BUSY/PROTOCOL/IOERR errors as transient in the corruption probe so the same lock no longer renames the database away as corrupt; deflakes `cross-process contenders never both acquire the same connection` (#12394 — thanks @pacocartones) +- **fix(chat-admission):** derive the `chat_admission_busy` 503 `Retry-After` from observed heavyweight-lease occupancy — the larger of the exhausted `OMNIROUTE_CHAT_ADMISSION_QUEUE_MS` window and the time since capacity last turned over, capped at 60 s — instead of a fixed 1 s (structural) / 2 s (byte-stage) hint that invited Codex/agent fan-out clients to re-send ~1 MiB `/v1/responses` bodies every second into a gate held for the whole SSE lifetime; an idle gate keeps the historical floors ([#12135](https://github.com/diegosouzapw/OmniRoute/issues/12135)) (#12395 — thanks @pacocartones) +- **fix(api-manager):** the API key permissions modal no longer silently drops `allowedCombos` entries its Combo picker cannot render — routing-rule names such as `rt-*`, which the backend already honours — when "All" is clicked and the key is switched back to "Restrict"; those entries now survive the toggle, are listed read-only under the combo list so the count and the list agree, and are saved back verbatim instead of persisting `[]` (deny-all) (#12397 — thanks @pacocartones) +- **fix(catalog):** write the NUL separator of the catalog connection memo key, the provider serviceKind memo key, the Video Bridge promotion group key and a JSON-exactness test fixture as the `\u0000` escape instead of a raw byte — same runtime value, but the raw byte made git, GitHub and ripgrep treat those files as binary (hidden PR diffs, silently skipped searches); a guard test now keeps raw NUL bytes out of `src/`, `open-sse/` and `tests/` (#12403 — thanks @pacocartones) +- **fix(providers):** add `CLAUDE_CODE_CLIENT_VERSION` and `GITHUB_COPILOT_CLI_VERSION` env overrides so Anthropic/Copilot client-version gates can be unblocked without a rebuild ([#12417](https://github.com/diegosouzapw/OmniRoute/issues/12417)) ([#12632](https://github.com/diegosouzapw/OmniRoute/pull/12632)) — thanks @HouMinXi +- **fix(db):** back-fill `last_ping_at` and `last_pinged_reset_key` on `provider_connections` during schema reconciliation so divergent lineages that skipped `123_quota_auto_ping` still accept quota auto-ping writes ([#12470](https://github.com/diegosouzapw/OmniRoute/pull/12470) — thanks @KooshaPari) +- **fix(ci):** document MIT exceptions for `@eloqnt/{config,format-json,format-po}` (next-intl transitive; locked tarballs omit `license`) and keep the A2A lifecycle vitest off the real SQLite persistence seam ([#12581](https://github.com/diegosouzapw/OmniRoute/issues/12581)) ([#12595](https://github.com/diegosouzapw/OmniRoute/pull/12595)) +- OpenCode plugin `/v1/models` catalog fetch now waits 30s by default and attaches HTTP `statusCode` on 401/5xx so host fallback plugins can hop instead of seeing an untyped AbortError/UnknownError. ([#12607](https://github.com/diegosouzapw/OmniRoute/pull/12607)) — thanks @RaviTharuma +- **CI:** the OpenAPI security-tier gate now mirrors `isAlwaysProtectedPath()` in full — it also reads `ALWAYS_PROTECTED_API_PATTERNS`, so the pattern-gated credential routes (`/api/providers/{id}/{claude,codex}-auth/{export,apply-local}`, GHSA-5926-2w35-7h4q) no longer report as unannotated. (#12605) +- **fix(api):** GET /v1/models no longer waits forever on a hung coalesced catalog rebuild; cold-path waits are bounded (`CATALOG_BUILD_TIMEOUT_MS`, default 8s) and a last-good 200 is served when the rebuild times out ([#12627](https://github.com/diegosouzapw/OmniRoute/issues/12627)). ([#12628](https://github.com/diegosouzapw/OmniRoute/pull/12628)) — thanks @RaviTharuma +- **fix(cursor):** a non-array `tool_calls` on an assistant message no longer crashes the cursor request translator with a `TypeError`; both loops now require an array ([#12689](https://github.com/diegosouzapw/OmniRoute/issues/12689)) ([#12691](https://github.com/diegosouzapw/OmniRoute/pull/12691)) — thanks @soroush5 +- **fix(chat):** Chat Completions no longer return empty `content` after a server-owned memory or skills tool; the first provider send and account/model recovery now share one pipeline so a follow-up round-trip can fill the reply ([#12696](https://github.com/diegosouzapw/OmniRoute/issues/12696)) — thanks @HouMinXi ([#12867](https://github.com/diegosouzapw/OmniRoute/pull/12867)) +- **fix(dashboard):** batch-deleting provider keys no longer toasts failure after a successful delete when the confirm button's click event is forwarded as `onAfter` ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) — thanks @HouMinXi +- **fix(glm):** drop the extra 16th argument to `createSSETransformStreamWithLogger` that TypeScript rejected (TS2554) and that never reached the TransformStream ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) ([#12770](https://github.com/diegosouzapw/OmniRoute/pull/12770)) — thanks @HouMinXi +- **fix(dashboard):** pass `nodeMap` into Runtime `QuotaGroup` so a quota monitor in error/exhausted/alerting no longer throws `ReferenceError: nodeMap is not defined`. ([#12868](https://github.com/diegosouzapw/OmniRoute/pull/12868)) — thanks @HouMinXi +- Restricted API keys whose `allowedModels` lists a combo name no longer skip every combo member at pre-dispatch (`ALL_TARGETS_SKIPPED`). Inner-target filtering still applies when the allow-list is a provider prefix or `disableNonPublicModels` is on ([#12899](https://github.com/diegosouzapw/OmniRoute/pull/12899)). — thanks @HouMinXi +- **fix(combos):** Effort-suffixed combo members inherit the base model's `model_context_overrides` row so priority order is not inverted on large requests ([#12926](https://github.com/diegosouzapw/OmniRoute/pull/12926)) — thanks @HouMinXi +- **fix(catalog):** Picker-added `customModels` enter the dispatch-time live catalog so combo members and bare inference no longer 400 ([#12934](https://github.com/diegosouzapw/OmniRoute/pull/12934)) — thanks @HouMinXi +- **fix(docker):** re-derive the Docker build's worker-pool memory budget from the MEASURED ~4.5 GB per-process RSS (the issue owner's own VPS dmesg OOM-killer reproduction) instead of the stale 2560 MB/worker inference #11419 shipped, and lower `OMNIROUTE_BUILD_WORKERS` 3 → 2 so 1 parent + 1 page-data worker (2 processes × 4.5 GB = 9 GB) fits the 12.288 GB (75%) budget on a 16 GB GitHub Actions runner — the previous default (1 parent + 2 workers = 13.5 GB) still overcommitted the runner and kept "Publish to Docker Hub" failing with `cannot allocate memory` after #11419 merged (#7518). ([#11663](https://github.com/diegosouzapw/OmniRoute/pull/11663)) +- **fix(electron):** the packaged Windows desktop build now passes the #7592 cold-restart smoke end to end. Five defects were found and fixed on the way: (1) optional-pack staging failed on any machine whose `tar` is GNU tar — it reads the drive letter in an absolute `-f C:\...` archive path as a remote rsh host (`Cannot connect to C:`), so staging now passes a bare filename with `cwd` at the tarball directory and surfaces tar stderr on failure; (2) the Electron `files` allowlist was missing `lib/loginHeaderCapture.js`, so the packaged main process crashed at startup with `Cannot find module './lib/loginHeaderCapture'`; (3) electron-builder ≥26 injects an `!**/node_modules/**` ignore into every extraResources pattern list that no positive filter can override, which silently dropped the staged runtime `node_modules` (including the better-sqlite3 N-API prebuild) from `resources/app` — a fresh v3.8.50 desktop build would have shipped with no native SQLite driver and reproduced the #7592 sql.js fallback on every machine; an `afterPack` hook now restores the staged `node_modules` after packing; (4) the packaged-app smoke harness redirected `USERPROFILE` into its temp DATA_DIR, but Electron resolves the Roaming profile from `%USERPROFILE%\AppData\Roaming\` (USERPROFILE takes precedence over the APPDATA env var) and the path service throws instead of creating it, so `requestSingleInstanceLock()` returned false and the app exited(0) silently before `app.whenReady()` — the harness now pre-creates the derived tree, and `ensureSmokeEnvDirs` is exported and unit-tested; (5) the #7592 driver assertion parsed a `[DB] Driver: ...` line that the server's primary DB path never printed (only the unused `openDatabaseAsync()` did) — `getDbInstance()` now emits the same line on open so the guard can actually assert the native driver. Also: the smoke env-allowlist unit test hardcoded POSIX paths and could only pass on Linux/macOS; expectations are now host-agnostic, with new regression tests for the USERPROFILE-derived tree and `tarPack` under absolute Windows-style paths ([#11443](https://github.com/diegosouzapw/OmniRoute/pull/11443)) — thanks @jonlwheat2-gif +- fix(sse): stop the auto-combo candidates inspector from silently dropping model-locked/cooled-down rows (#9133) ([#11994](https://github.com/diegosouzapw/OmniRoute/pull/11994)) — thanks @hartmark +- **fix(providers):** Perplexity Web no longer turns upstream stream failures into successful assistant text; pre-content failures remain eligible for fallback, partial output ends with a structured sanitized error, and failed sessions are not persisted ([#12465](https://github.com/diegosouzapw/OmniRoute/pull/12465)) +- **fix(video-bridge):** preserve chronological video and transcript fusion, source-frame timestamps, transcript provenance, full contact-sheet intervals, and pre-abort callback isolation ([#11681](https://github.com/diegosouzapw/OmniRoute/pull/11681)) +- **Z.ai Web:** HTTP 200 streams carrying an upstream error now terminate with a structured failure instead of assistant text plus a normal stop, preserving partial output while allowing pre-content combo fallback. ([#12454](https://github.com/diegosouzapw/OmniRoute/pull/12454)) +- Absorb `Error [AbortError]: request_signal_aborted` and DOMException AbortError shapes in the process-level client-abort crash guard so routine client disconnects no longer kill the server (exit code 7). ([#12165](https://github.com/diegosouzapw/OmniRoute/pull/12165)) — thanks @alvinveroy +- **fix(sse):** Treat Adapta Web `type:error` SSE events as sanitized non-stream failures instead of empty HTTP 200 completions. ([#12459](https://github.com/diegosouzapw/OmniRoute/pull/12459)) +- **fix(autoCombo):** layer 3 (`models_dev_tier`) no longer scores retired models as premium, merge cross-provider capability rows deterministically, and inherit tier scores through variant ids ([#11508](https://github.com/diegosouzapw/OmniRoute/issues/11508)). `getModelsDevTierFitness` now vetoes ids present in `config/quality/model-lifecycle.json` with status `retired` before any other signal; `loadModelCapabilities()` aggregates per `model_id` (any-non-null-true for booleans, max for `limit_context`) instead of last-write-wins over SQLite's undefined row order; a capabilities miss retries through `resolveScoresAs()`'s base id and reports `models_dev_tier:inherited`, matching layers 1–2 (#11492). Adds `getModelsDevTierFitnessWithSource()` and an `invalidateCapabilitiesCache()` test/ops hook. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) — thanks @Meet6338-X +- fix(sse): accept Claude `content:[]` + `stop_reason: max_tokens`/`tool_use` in `detectMalformedNonStream` (match `isEmptyContentResponse`) so Claude Code `/model` probes with `max_tokens: 1` no longer become a false 502 ([#11568](https://github.com/diegosouzapw/OmniRoute/pull/11568)) — thanks @Seramicx +- **fix(security):** sanitize `request.failed` diagnostics before publishing them to live dashboard listeners and replay history, while keeping status, model, provider, latency, and internal call-log diagnostics intact. ([#12469](https://github.com/diegosouzapw/OmniRoute/pull/12469)) +- **fix(pwa):** do not intercept dashboard navigations so Chrome can fall back from a stale HTTP/3 Alt-Svc advertisement (UDP :20128 is unpublished; F5 on a long-lived tab hung until a new tab opened a fresh TCP connection). ([#12767](https://github.com/diegosouzapw/OmniRoute/pull/12767)) — thanks @HouMinXi +- **fix(executors):** handle DuckDuckGo ERR_BN_LIMIT (418) without retrying — when the upstream returns `418 ERR_BN_LIMIT` (rate-limit/ban), the executor now returns the error immediately instead of burning another VQD acquisition that would only count against the IP limit. The retry logic for `418 ERR_CHALLENGE` (unsolved challenge) remains unchanged. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) ([#12110](https://github.com/diegosouzapw/OmniRoute/pull/12110)) — thanks @oyi77 / @Meet6338-X +- Fixed DuckDuckGo streaming responses losing JSON lines and UTF-8 characters split across network chunks. ([#11528](https://github.com/diegosouzapw/OmniRoute/pull/11528)) — thanks @pacocartones +- Harden generated Cloudflare, Vercel, and Deno relays by stripping hop-by-hop, framing, and proxy authentication headers before forwarding requests upstream. ([#11533](https://github.com/diegosouzapw/OmniRoute/pull/11533)) — thanks @pacocartones +- **feat(grok-cli):** Provider Limits shows grok-cli banked reset credits from `GetRemainingResets` (including a real zero; a failed RPC omits the row) and the existing View credits button now calls `ConsumerUiSvc/RedeemReset` for grok-cli. Live tokens use nested fields 10/20/30 (id + Timestamp), not compact 1/2/3. ([#12805](https://github.com/diegosouzapw/OmniRoute/pull/12805)) — thanks @HouMinXi +- **fix(memory):** Embedding Model Quick select, Embedding Source remote dropdown, and Rerank selector now list every configured provider with embedding/rerank support instead of only chat-catalog text matches plus OpenRouter live discovery; a generic OpenAI-compatible `/embeddings` + Cohere-compatible `/rerank` runtime fallback resolves any configured chat provider's embedding/rerank endpoint, so unlisted providers no longer fail with "Unknown embedding provider"; both memory selectors gained a free-text model override ([#11390](https://github.com/diegosouzapw/OmniRoute/pull/11390)) — thanks @rqzbeh +- Harden SQLite upgrades around the historical migration-074 version collision: missing discovery and inspector tables are replayed atomically, pre-existing databases (including setup-created skeletons) receive reusable content-addressed safety snapshots, and Node test/eval probes without `DATA_DIR` are isolated from the operator database. ([#12435](https://github.com/diegosouzapw/OmniRoute/pull/12435)) +- **fix(dashboard):** Moonshot/Kimi Open Platform voucher and cash leftover percentages follow the bucket balance, so an empty wallet no longer paints those rows as 100% while Available is 0% ([#12733](https://github.com/diegosouzapw/OmniRoute/pull/12733)) — thanks @HouMinXi +- **fix(combo):** return non-retryable HTTP 400 when all candidates for a pinned native Codex turn are unavailable due to model-scoped lockout, terminating the turn cleanly while preserving turn continuity and enabling standard Combo routing on subsequent turns ([#12240](https://github.com/diegosouzapw/OmniRoute/pull/12240)) — thanks @mdigitalbh81 +- Fixed auto-router strategies preserving the selected connection through ranking and dispatch when multiple connections share the same provider and model. ([#11530](https://github.com/diegosouzapw/OmniRoute/pull/11530)) — thanks @pacocartones +- Preserve Chat Completions JSON-object response formats when translating requests to the Responses API. ([#11529](https://github.com/diegosouzapw/OmniRoute/pull/11529)) — thanks @pacocartones +- Fix idempotency fingerprints so Responses API requests include `input` and other semantic generation fields using stable serialization, while excluding credentials and request metadata. ([#11532](https://github.com/diegosouzapw/OmniRoute/pull/11532)) — thanks @pacocartones +- **fix(api):** Generated API CLI commands now enforce required OpenAPI request bodies; Combo test commands forward the required `comboName` body, while API keys created by older writers after migration 149 preserve legacy allow-all Combo access without widening explicit empty allowlists — thanks @marcelokarval ([#12070](https://github.com/diegosouzapw/OmniRoute/pull/12070)) +- **fix(grok-web):** treat upstream streaming failures as failures instead of successful assistant text: error-only streams now fail readiness with HTTP 502, while failures after legitimate content preserve that partial output and terminate through the sanitized stream failure path without a normal `stop` completion. ([#12458](https://github.com/diegosouzapw/OmniRoute/pull/12458)) +- HuggingChat now turns HTTP 200 JSONL generation failures into a sanitized 502 before content, or a fixed public stream failure after partial output, so fallback and request persistence no longer record a false successful stop. ([#12456](https://github.com/diegosouzapw/OmniRoute/pull/12456)) +- **fix(providers):** keep 1min.ai HTTP 200 stream errors out of assistant content, preserve partial output, and expose sanitized terminal errors so pre-content failures can fall back. ([#12466](https://github.com/diegosouzapw/OmniRoute/pull/12466)) +- **fix(sessions):** preserve legacy Sessions data when the additive exclusive-session projection is unavailable, returning an empty projection and warning only once per contiguous outage instead of failing the endpoint; the Sessions badge intentionally reflects the merged legacy and exclusive row count introduced by [#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389) ([#11469](https://github.com/diegosouzapw/OmniRoute/pull/11469)) — thanks @KaspaPulse +- Render Nimble Search with the generic provider icon and serve Opper's proven logo locally. ([#12415](https://github.com/diegosouzapw/OmniRoute/pull/12415)) +- **fix(quality):** `config/quality/provider-assets-provenance.jsonl` was stale against the physical `public/providers/` tree (still listing 225 assets and 5 duplicate-content groups after several provider-retirement PRs removed 85 icon files), which would have made `check:provider-asset-provenance` fail on the next full run. Reconciled the manifest to the real 142-file snapshot (142 asset records, 1 duplicate-content group, 2 newly-registered files marked `unresolved` pending provenance research), repinned `auditedCommit`, and updated the regression test's hardcoded counts to match. ([#11876](https://github.com/diegosouzapw/OmniRoute/pull/11876)) +- fix(ui): wrap quota card footer action buttons to prevent clipping Refresh button on narrow cards — when extra action buttons such as `View credits` are present on Codex cards, allow the footer row to flex-wrap with `shrink-0` buttons so `Refresh now` remains visible and clickable across all responsive card widths ([#11464](https://github.com/diegosouzapw/OmniRoute/pull/11464)) — thanks @adevwithpurpose +- Keep Antigravity Gemini usable when the same connection's Claude weekly quota is empty; generic quota cache stays per-connection for every other provider. ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) — thanks @HouMinXi +- Fixed Tailscale tunnel setup to reject ports outside the valid 1-65535 range and safely install Windows MSI packages from paths containing apostrophes. ([#11534](https://github.com/diegosouzapw/OmniRoute/pull/11534)) — thanks @pacocartones +- Classify Cloudflared and Tailscale process-management routes as local-only and spawn-capable while preserving remote access to read-only tunnel status endpoints. ([#11531](https://github.com/diegosouzapw/OmniRoute/pull/11531)) — thanks @pacocartones +- Electron release workflow: the `publish-npm` job now grants `actions: read` to the reusable `npm-publish.yml` it calls (its `publish` job requests it), which is what made GitHub refuse the whole v3.8.50 run at startup and ship the release with zero desktop assets; a `workflow_dispatch` now builds the requested tag instead of the dispatching branch and can skip the npm leg (`publish_npm=false`) when only re-attaching assets ([#11974](https://github.com/diegosouzapw/OmniRoute/pull/11974)) +- **fix(dashboard):** The Combos page usage guide now reads its dismissal through `useSyncExternalStore` instead of correcting SSR state inside an effect, removing an extra commit of the page tree on every load (and the `react-hooks/set-state-in-effect` error it raised). ([#12671](https://github.com/diegosouzapw/OmniRoute/pull/12671)) +- Electron release: `electron/package-lock.json` regained the optional `electron-builder-squirrel-windows` subtree (13 entries) that `npm ci` had been refusing as out of sync, `electron-release.yml` gained a `build_ref` dispatch input and stops regenerating release notes on a re-attach dispatch, and the npm publish workflow attaches the SBOM to the GitHub Release on dispatch publishes too — so the v3.8.51 tag ships every desktop asset and the SBOM like v3.8.49 did ([#12022](https://github.com/diegosouzapw/OmniRoute/pull/12022)) +- Drained the reds every PR against `release/v3.8.51` was born with: documented the three service-worker build-id variables, registered the six retirement/tunnel tests with the mutation gate, approved `eslint-plugin-react-hooks` in the dependency allowlist, added the six `combo.sort.*` strings to `vi` and `pt-BR`, pointed the ChatGPT Web doc at the real migration-168 test, worded the g4f hint around the member key, and realigned four tests to the retired-provider catalog and the legacy-schema fixtures the retirement migrations touch. ([#11940](https://github.com/diegosouzapw/OmniRoute/pull/11940)) +- **fix(providers):** Claude, Grok, LMArena, Notion, and Perplexity web-cookie transports now use pooled `wreq-js` 3.2 instead of the native sidecar, with all nine supported bindings pinned and audited, and the applicable platform binding plus native-license evidence included in each release artifact ([#12429](https://github.com/diegosouzapw/OmniRoute/pull/12429), supersedes [#11753](https://github.com/diegosouzapw/OmniRoute/pull/11753)). +- **fix(providers):** Zed Hosted streaming failures now trigger fallback before content and end partial streams with a sanitized structured error instead of fake assistant text and a normal-success stop. ([#12455](https://github.com/diegosouzapw/OmniRoute/pull/12455)) +- **fix(test):** capture hook result after render (direct commit `b85d0abb0b`) +- **fix(sync):** repair the two regressions the v3.8.50 sync-back left on release/v3.8.51 (direct commit `5ade9e0851`) +- **revert(models):** drop the memo reset hook ported from #10055 (direct commit `282de841ae`) +- **fix(typecheck):** guard the optional connectionId before the reactive model sync (direct commit `f2825bad75`) +- **fix(quota-share):** release the winner's reserved in-flight slot (#11371) ([#11408](https://github.com/diegosouzapw/OmniRoute/pull/11408)) — thanks @oyi77 +- **fix(providers):** stop silently dropping reasoning effort on opencode families (#10788) ([#11409](https://github.com/diegosouzapw/OmniRoute/pull/11409)) — thanks @oyi77 +- **fix(combo):** enable genuine quota-aware routing for generic providers (antigravity, claude, etc.) ([#11411](https://github.com/diegosouzapw/OmniRoute/pull/11411)) — thanks @HouMinXi +- **fix(oauth):** allow expired token retry before deactivation and activate expired retry loop (#P0) ([#11414](https://github.com/diegosouzapw/OmniRoute/pull/11414)) — thanks @HouMinXi +- **perf(executors):** lazy-load the executor registry — defer class imports + construction to first use (#11220) ([#11421](https://github.com/diegosouzapw/OmniRoute/pull/11421)) — thanks @oyi77 +- **fix:** make release/v3.8.50 CI gates green (run 32786966560 root causes) ([#11450](https://github.com/diegosouzapw/OmniRoute/pull/11450)) — thanks @jonlwheat2-gif +- **fix(sse):** exact-domain cookie match and origin-equality URL assertions ([#11452](https://github.com/diegosouzapw/OmniRoute/pull/11452)) — thanks @hartmark +- **fix(providers):** send Responses API payload during OpenAI-compatible validation ([#11454](https://github.com/diegosouzapw/OmniRoute/pull/11454)) — thanks @tuandinh0801 +- **fix(command-code):** fallback to /alpha/generate for Go plan without Provider API access ([#11455](https://github.com/diegosouzapw/OmniRoute/pull/11455)) — thanks @Chewji9875 +- **fix(responses-continuation):** fail closed on a log-truncated stored input/output array ([#11473](https://github.com/diegosouzapw/OmniRoute/pull/11473)) — thanks @hartmark +- **fix(cli):** write jcode settings as [providers.omniroute] in config.toml ([#11484](https://github.com/diegosouzapw/OmniRoute/pull/11484)) — thanks @hongnoul +- **fix(logging):** raise chat-log array truncation cap to 1000 and unify duplicate implementations ([#11499](https://github.com/diegosouzapw/OmniRoute/pull/11499)) — thanks @hartmark +- **fix:** restore live websocket URL helpers ([#11509](https://github.com/diegosouzapw/OmniRoute/pull/11509)) — thanks @PauloFH +- **perf(core):** eliminate async waterfalls across home, cache, and db operations (#11396) ([#11512](https://github.com/diegosouzapw/OmniRoute/pull/11512)) — thanks @HouMinXi +- **fix(kiro):** add runtime.us-east-1.kiro.dev as first-attempt endpoint ([#11517](https://github.com/diegosouzapw/OmniRoute/pull/11517)) — thanks @rifqiawl +- **fix(providers):** remove duplicate 128-tool truncation in OpencodeExecutor (#11444) ([#11518](https://github.com/diegosouzapw/OmniRoute/pull/11518)) — thanks @HouMinXi +- **fix(providers):** poll onboardUser LRO before classifying BYOP ([#11519](https://github.com/diegosouzapw/OmniRoute/pull/11519)) — thanks @rifqiawl +- **fix(translator):** preserve exact tool name casing for Claude tool_use payloads (#11487) ([#11520](https://github.com/diegosouzapw/OmniRoute/pull/11520)) — thanks @HouMinXi +- **fix(kimi-web):** align health probe with executor domain ([#11521](https://github.com/diegosouzapw/OmniRoute/pull/11521)) — thanks @pacocartones +- **fix(cli):** preserve existing admin password during setup ([#11522](https://github.com/diegosouzapw/OmniRoute/pull/11522)) — thanks @pacocartones +- **fix(mitm):** keep Windows DNS elevation off the POSIX sudo path (#11430) ([#11542](https://github.com/diegosouzapw/OmniRoute/pull/11542)) — thanks @official-burak +- **fix(sse):** scale chat admission by ingest byte budget instead of a fixed request count ([#11548](https://github.com/diegosouzapw/OmniRoute/pull/11548)) — thanks @TheDemonTuan +- **fix:** accept Gemini Web cookie exports ([#11554](https://github.com/diegosouzapw/OmniRoute/pull/11554)) — thanks @rafacpti23 +- **fix(i18n):** complete Brazilian Portuguese translations ([#11558](https://github.com/diegosouzapw/OmniRoute/pull/11558)) — thanks @rafacpti23 +- **fix(sse):** explicit types for openai-responses pureHelpers — clears last failing core typecheck gate ([#11567](https://github.com/diegosouzapw/OmniRoute/pull/11567)) — thanks @jonlwheat2-gif +- **fix(opencode):** hydrate Proxy Pool references for no-auth connections ([#11584](https://github.com/diegosouzapw/OmniRoute/pull/11584)) — thanks @AStupidBear +- **fix(ci):** resolve inherited release/v3.8.51 base-red CI failures ([#11608](https://github.com/diegosouzapw/OmniRoute/pull/11608)) — thanks @jonlwheat2-gif +- **fix(db):** fallback load STORAGE_ENCRYPTION_KEY from env files ([#11614](https://github.com/diegosouzapw/OmniRoute/pull/11614)) — thanks @benzntech +- **fix(providers):** mark Brave Search as no longer having a free tier ([#11621](https://github.com/diegosouzapw/OmniRoute/pull/11621)) — thanks @turbolego +- **fix:** pass tool result content through as string in OpenAI/Claude -> Gemini translation ([#11624](https://github.com/diegosouzapw/OmniRoute/pull/11624)) — thanks @Hsia97 +- **fix(sse):** scope credential health to exact key ([#11628](https://github.com/diegosouzapw/OmniRoute/pull/11628)) — thanks @KaspaPulse +- **fix(install):** guard prepare script against missing husky ([#11634](https://github.com/diegosouzapw/OmniRoute/pull/11634)) — thanks @Prajeeth-12 +- **fix(security):** redact NVIDIA_API_KEY at the log sink in the ad-hoc diag script ([#11639](https://github.com/diegosouzapw/OmniRoute/pull/11639)) +- **fix(types):** clear the two type regressions red on release/v3.8.51 ([#11640](https://github.com/diegosouzapw/OmniRoute/pull/11640)) — thanks @jonlwheat2-gif +- **fix(build):** resolve workspace: protocol dependencies to concrete semver versions during prepublish (#11540) ([#11641](https://github.com/diegosouzapw/OmniRoute/pull/11641)) — thanks @HouMinXi +- **fix(search):** properly resolve configured search connection in /v1/responses pipeline (#11524) ([#11642](https://github.com/diegosouzapw/OmniRoute/pull/11642)) — thanks @HouMinXi +- **fix(authz):** trust cf-connecting-ip for via-proxy detection when peer is Cloudflare edge (#11514) ([#11643](https://github.com/diegosouzapw/OmniRoute/pull/11643)) — thanks @HouMinXi +- **fix(security):** compare the OIDC state cookie in constant time ([#11646](https://github.com/diegosouzapw/OmniRoute/pull/11646)) +- **fix(security):** namespace the dedup hash by the calling API key ([#11649](https://github.com/diegosouzapw/OmniRoute/pull/11649)) +- **fix(models):** gate the Codex-native catalog loop on prefix mode (#11632) ([#11662](https://github.com/diegosouzapw/OmniRoute/pull/11662)) — thanks @xiaoyaner0201 +- **fix(dashboard):** project Codex routing quota in account pools ([#11666](https://github.com/diegosouzapw/OmniRoute/pull/11666)) — thanks @KaspaPulse +- **fix(ddgw):** normaliza system no upstream da release v3.8.51 ([#11692](https://github.com/diegosouzapw/OmniRoute/pull/11692)) — thanks @RhianB14 +- **fix(memory):** honor category filter in GET /api/memory ([#11699](https://github.com/diegosouzapw/OmniRoute/pull/11699)) — thanks @Sabeekhann +- **fix(ci):** detect stale workspace lockfile entries ([#11714](https://github.com/diegosouzapw/OmniRoute/pull/11714)) — thanks @pacocartones +- **fix(ci):** stamp dist/BUILD_SHA before the npm publish provenance gate ([#11721](https://github.com/diegosouzapw/OmniRoute/pull/11721)) +- **fix(docker):** decouple the best-effort Bun image from the release manifest ([#11724](https://github.com/diegosouzapw/OmniRoute/pull/11724)) +- **perf(compression):** avoid cloning memo misses twice ([#11727](https://github.com/diegosouzapw/OmniRoute/pull/11727)) — thanks @RaviTharuma +- **fix(docker):** let the bun digest artifact be absent, not fatal ([#11740](https://github.com/diegosouzapw/OmniRoute/pull/11740)) +- **fix(shared):** block cloud-metadata hosts under default remote-image guard ([#11755](https://github.com/diegosouzapw/OmniRoute/pull/11755)) — thanks @santosraju99-hub +- **fix(api):** preserve caller-provided X-Correlation-Id on chat completions ([#11760](https://github.com/diegosouzapw/OmniRoute/pull/11760)) — thanks @aniruddhaadak80 +- **fix(resilience):** lock permanently retired models instead of short backoff (Gemini ban prevention) ([#11762](https://github.com/diegosouzapw/OmniRoute/pull/11762)) — thanks @turbolego +- **fix:** mark Vercel AI Gateway as passthroughModels ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **fix(resilience):** stop hammering permanently-moved endpoints and billing-suspended accounts ([#11774](https://github.com/diegosouzapw/OmniRoute/pull/11774)) — thanks @turbolego +- **fix(lease):** remove global static reservation and gate routing on live active lease occupancy ([#11775](https://github.com/diegosouzapw/OmniRoute/pull/11775)) — thanks @TheDemonTuan +- **fix(ci):** size the install-upgrade gate to a measured run, and log the pack cost ([#11776](https://github.com/diegosouzapw/OmniRoute/pull/11776)) +- **fix(pwa):** stop serving the stale shell after deploys ([#11779](https://github.com/diegosouzapw/OmniRoute/pull/11779)) — thanks @HouMinXi +- **fix(combo):** lock GitHub models rejected as "not supported" for future requests ([#11781](https://github.com/diegosouzapw/OmniRoute/pull/11781)) — thanks @turbolego +- **fix(cli):** probe both IPv4 and IPv6 loopback for server readiness (#11766) ([#11794](https://github.com/diegosouzapw/OmniRoute/pull/11794)) — thanks @vermasomesh835 +- **fix(sse):** preserve CLIProxyAPI wrappers per connection ([#11797](https://github.com/diegosouzapw/OmniRoute/pull/11797)) — thanks @RaviTharuma +- **fix(providers):** honor model auto-fetch opt-in on create ([#11798](https://github.com/diegosouzapw/OmniRoute/pull/11798)) — thanks @RaviTharuma +- **fix(dashboard):** make model auto-fetch order independent ([#11805](https://github.com/diegosouzapw/OmniRoute/pull/11805)) — thanks @RaviTharuma +- **fix(docker):** make GHA cache exports best effort ([#11806](https://github.com/diegosouzapw/OmniRoute/pull/11806)) — thanks @RaviTharuma +- **fix(kiro):** do not permanently ban on 'User is not authorized to make this call' ([#11809](https://github.com/diegosouzapw/OmniRoute/pull/11809)) — thanks @Deftera186 +- **fix(sse):** keep cache-write tokens in OpenAI-shaped usage ([#11814](https://github.com/diegosouzapw/OmniRoute/pull/11814)) — thanks @TheDemonTuan +- **fix(docs):** resolve relative markdown and wiki links across Fumadocs and GitHub wiki ([#11834](https://github.com/diegosouzapw/OmniRoute/pull/11834)) — thanks @AndrianBalanescu +- **fix(chat):** preserve unstripped model string for passthrough provider routing ([#11840](https://github.com/diegosouzapw/OmniRoute/pull/11840)) — thanks @raheemuddin786 +- **fix(mcp):** avoid async initialization inside sync esm wrappers (#11569) ([#11849](https://github.com/diegosouzapw/OmniRoute/pull/11849)) — thanks @HouMinXi +- **fix(sse):** preserve coherent markdown token boundaries across stream translation chunks (#11606) ([#11851](https://github.com/diegosouzapw/OmniRoute/pull/11851)) — thanks @HouMinXi +- **fix(ci):** run the install-upgrade gate on disk, not on the /tmp tmpfs ([#11855](https://github.com/diegosouzapw/OmniRoute/pull/11855)) +- **fix(skills):** expand shorthand property types in injected tool schemas ([#11857](https://github.com/diegosouzapw/OmniRoute/pull/11857)) — thanks @alvinveroy +- **fix(ci):** publish npm from a hosted runner so provenance is accepted ([#11877](https://github.com/diegosouzapw/OmniRoute/pull/11877)) +- **fix(lint):** pin react hooks plugin to 7.0.1 ([#11886](https://github.com/diegosouzapw/OmniRoute/pull/11886)) +- **fix(ci):** surface and clear release-green ESLint failure ([#11890](https://github.com/diegosouzapw/OmniRoute/pull/11890)) — thanks @MumuTW +- **fix(guardrails):** prevent duplicate prompt-injection-guard log output ([#11936](https://github.com/diegosouzapw/OmniRoute/pull/11936)) — thanks @patrykkopycinski +- **fix(auth):** downgrade expected transient states from warn to debug ([#11937](https://github.com/diegosouzapw/OmniRoute/pull/11937)) — thanks @patrykkopycinski +- **fix(codex):** fail over image generation for imported free plans ([#11948](https://github.com/diegosouzapw/OmniRoute/pull/11948)) — thanks @Bl0ck154 +- **fix(codex):** normalize non-stream responses ([#11951](https://github.com/diegosouzapw/OmniRoute/pull/11951)) — thanks @Bl0ck154 +- **fix(images):** forward Antigravity image size ([#11952](https://github.com/diegosouzapw/OmniRoute/pull/11952)) — thanks @Bl0ck154 +- **fix(db):** invalidate connection cache after upsert ([#11953](https://github.com/diegosouzapw/OmniRoute/pull/11953)) — thanks @Bl0ck154 +- **fix(codex):** restore imported account state ([#11954](https://github.com/diegosouzapw/OmniRoute/pull/11954)) — thanks @Bl0ck154 +- **fix(sse):** give extended-thinking targets the reasoning readiness budget ([#11959](https://github.com/diegosouzapw/OmniRoute/pull/11959)) — thanks @abhisheksharma2411 +- **fix(dashboard):** use opaque background and readable text color on cost chart tooltips ([#11960](https://github.com/diegosouzapw/OmniRoute/pull/11960)) — thanks @ZaimMarzuki +- **fix(antigravity):** send complete loadCodeAssist metadata (ideType/platform/pluginType as numeric enums) ([#11969](https://github.com/diegosouzapw/OmniRoute/pull/11969)) — thanks @rifqiawl +- **fix(core):** resolve DB init race condition and reasoning translation ([#12003](https://github.com/diegosouzapw/OmniRoute/pull/12003)) — thanks @raheemuddin786 +- **fix(admission):** exclude reclaimable page cache from the cgroup pressure ratio ([#12017](https://github.com/diegosouzapw/OmniRoute/pull/12017)) — thanks @HouMinXi +- **fix(build):** prune non-production trees in NFT trace excludes and tsconfig ([#12028](https://github.com/diegosouzapw/OmniRoute/pull/12028)) — thanks @Chewji9875 +- **fix(migrations):** add renamed migration compatibility for 056/073/077/101 ([#12036](https://github.com/diegosouzapw/OmniRoute/pull/12036)) — thanks @oyi77 +- **fix(sse):** default crash-guard logger to console.warn, not console ([#12042](https://github.com/diegosouzapw/OmniRoute/pull/12042)) — thanks @HouMinXi +- **fix(catalog):** derive vision/modalities for built-in auto combos from effective target pool ([#12046](https://github.com/diegosouzapw/OmniRoute/pull/12046)) — thanks @Prajeeth-12 +- **fix(release):** the packaged-app smoke verifies the database opened, not a driver line the primary path never prints (twin of #12032) ([#12047](https://github.com/diegosouzapw/OmniRoute/pull/12047)) +- **fix(barrel):** migrate src/app/ off the @/lib/localDb barrel import (#11795 Phase 2) ([#12051](https://github.com/diegosouzapw/OmniRoute/pull/12051)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate src/lib/ off the localDb barrel to direct db imports (#59) ([#12052](https://github.com/diegosouzapw/OmniRoute/pull/12052)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate open-sse, src/shared, src/sse, src/models, src/domain off the @/lib/localDb barrel import (#11795 Phase 4) ([#12053](https://github.com/diegosouzapw/OmniRoute/pull/12053)) — thanks @jonlwheat2-gif +- **fix(barrel):** delete the @/lib/localDb barrel — every consumer migrated (#11795 Phase 5) ([#12055](https://github.com/diegosouzapw/OmniRoute/pull/12055)) — thanks @jonlwheat2-gif +- **fix(radar):** restore D12 public boundary ([#12057](https://github.com/diegosouzapw/OmniRoute/pull/12057)) +- **fix(dev):** bound webpack and Tailwind scans ([#12075](https://github.com/diegosouzapw/OmniRoute/pull/12075)) — thanks @backryun +- **fix(dev):** isolate root layout settings reads ([#12076](https://github.com/diegosouzapw/OmniRoute/pull/12076)) — thanks @backryun +- **fix(dev):** reduce instrumentation executor fan-out (phase 3) ([#12078](https://github.com/diegosouzapw/OmniRoute/pull/12078)) — thanks @backryun +- **fix(dev):** make logging resources HMR-singleton ([#12079](https://github.com/diegosouzapw/OmniRoute/pull/12079)) — thanks @backryun +- **fix(sse):** keep unavailable forced connections scoped ([#12080](https://github.com/diegosouzapw/OmniRoute/pull/12080)) — thanks @keeltrace +- **fix(dev):** isolate batch dispatch from instrumentation ([#12081](https://github.com/diegosouzapw/OmniRoute/pull/12081)) — thanks @backryun +- **fix(ci):** reconcile release test contract drift ([#12082](https://github.com/diegosouzapw/OmniRoute/pull/12082)) — thanks @backryun +- **fix(ci):** accept CVE-2025-68121 in the prebuilt tls-client .so, auto-close base-red issues, guard Scorecard on the default branch ([#12085](https://github.com/diegosouzapw/OmniRoute/pull/12085)) +- **fix(db):** drop three consumer-less 1proxy exports — dead-code base-red on release/v3.8.51 after the barrel deletion (#12055) ([#12087](https://github.com/diegosouzapw/OmniRoute/pull/12087)) +- **fix(api):** clear the six API-route TypeScript regressions the new gate landed red on ([#12094](https://github.com/diegosouzapw/OmniRoute/pull/12094)) +- **fix(release):** never let the tag-push Create Release append auto notes to the curated body ([#12096](https://github.com/diegosouzapw/OmniRoute/pull/12096)) +- **fix(sse):** honor CLIProxyAPI environment API key ([#12099](https://github.com/diegosouzapw/OmniRoute/pull/12099)) — thanks @RaviTharuma +- **fix(oauth):** bind Google refresh to the client that issued the token ([#12106](https://github.com/diegosouzapw/OmniRoute/pull/12106)) — thanks @HouMinXi +- **fix(dashboard):** make RequestLoggerDetail loadable outside Next — CSS via globals.css + CJS/ESM interop (#11703 base-reds) ([#12114](https://github.com/diegosouzapw/OmniRoute/pull/12114)) +- **fix(leases):** project status lease row to lease columns so joined connection PII never escapes ([#12115](https://github.com/diegosouzapw/OmniRoute/pull/12115)) — thanks @geek007git +- **fix(plugins):** do not kill the plugin process when a fire-and-forget hook times out ([#12116](https://github.com/diegosouzapw/OmniRoute/pull/12116)) — thanks @geek007git +- **fix(guardrails):** restore injection-guard logging on middleware-only routes ([#12117](https://github.com/diegosouzapw/OmniRoute/pull/12117)) — thanks @geek007git +- **fix(plugins):** refresh stored manifest from disk on activate so new hook fields reach existing installs ([#12120](https://github.com/diegosouzapw/OmniRoute/pull/12120)) — thanks @geek007git +- **fix(codex):** preserve existing provider state when bulk-import upserts a matching connection ([#12122](https://github.com/diegosouzapw/OmniRoute/pull/12122)) — thanks @geek007git +- **fix(ci):** clear the two base-reds the 2026-08-30 merge batch left on release/v3.8.51 (round 3) ([#12123](https://github.com/diegosouzapw/OmniRoute/pull/12123)) +- **fix(sse):** strip type:'custom' from Claude tools on agentrouter dispatch ([#12126](https://github.com/diegosouzapw/OmniRoute/pull/12126)) — thanks @wahidsadik371-coder +- **fix(sse):** stop advertising video providers the dispatcher cannot run ([#12131](https://github.com/diegosouzapw/OmniRoute/pull/12131)) — thanks @quiterunner-commits +- **fix(search):** treat HTTP 432 and plan limit errors as transient cooldown ([#12139](https://github.com/diegosouzapw/OmniRoute/pull/12139)) — thanks @HouMinXi +- **fix(sse):** refuse an AI Horde queue that cannot fit the request budget ([#12143](https://github.com/diegosouzapw/OmniRoute/pull/12143)) — thanks @quiterunner-commits +- **fix(ci):** clear the base-reds the afternoon merge batch left on release/v3.8.51 (round 5: provider count 352, TS2554/TS2677) ([#12144](https://github.com/diegosouzapw/OmniRoute/pull/12144)) +- **fix(translator):** strip neutral tool_choice when tools absent in Responses-to-Chat (#12141) ([#12166](https://github.com/diegosouzapw/OmniRoute/pull/12166)) — thanks @HouMinXi +- **fix(guardrails):** pass providerId to getResolvedModelCapabilities in checkComboVision (#12112) ([#12169](https://github.com/diegosouzapw/OmniRoute/pull/12169)) — thanks @HouMinXi +- **fix(translator):** strip plaintext reasoning content for opaque responses backends (#12128) ([#12171](https://github.com/diegosouzapw/OmniRoute/pull/12171)) — thanks @HouMinXi +- **perf(stream):** compile hot-path regexes once, bound token caches, fix quadratic buffering ([#12179](https://github.com/diegosouzapw/OmniRoute/pull/12179)) — thanks @opensource-elearning +- **fix:** resolve compression worker file using runtime anchors instead of… ([#12183](https://github.com/diegosouzapw/OmniRoute/pull/12183)) — thanks @NightStalker-87 +- **fix:** prevent Claude Code session kills via liveness-aware readiness + auto model echo ([#12189](https://github.com/diegosouzapw/OmniRoute/pull/12189)) — thanks @opensource-elearning +- **fix(translator):** preserve falsy primitive values in Gemini and Antigravity function response results ([#12191](https://github.com/diegosouzapw/OmniRoute/pull/12191)) — thanks @Siva010 +- **fix(oauth):** mark empty Antigravity projectId as degraded and clear stale errors (#11284) ([#12205](https://github.com/diegosouzapw/OmniRoute/pull/12205)) — thanks @HouMinXi +- **fix(providers):** gate the Codex auto-ping usage read on the shared quota throttle ([#12209](https://github.com/diegosouzapw/OmniRoute/pull/12209)) — thanks @abhisheksharma2411 +- **fix(combos):** prioritize SQLite row id over inner JSON id and notify delete errors ([#12213](https://github.com/diegosouzapw/OmniRoute/pull/12213)) — thanks @HouMinXi +- **fix(providers):** mark featherless.ai as no longer having a free tier (`hasFree: false`) ([#12216](https://github.com/diegosouzapw/OmniRoute/pull/12216)) — thanks @turbolego +- **fix(deepseek-web):** stop Turbopack dev panic in the PoW worker path resolver ([#12221](https://github.com/diegosouzapw/OmniRoute/pull/12221)) — thanks @hartmark +- **fix(chat-admission):** clarify local 503 source ([#12223](https://github.com/diegosouzapw/OmniRoute/pull/12223)) — thanks @keeltrace +- **fix(dev):** silence webpack runtime module warnings ([#12228](https://github.com/diegosouzapw/OmniRoute/pull/12228)) — thanks @backryun +- **fix(db):** use module.require for CommonJS runtime driver loading ([#12230](https://github.com/diegosouzapw/OmniRoute/pull/12230)) — thanks @raheemuddin786 +- **fix(memory):** honest probe-driven FTS5 keyword status + memory_id rowid sync ([#12231](https://github.com/diegosouzapw/OmniRoute/pull/12231)) — thanks @raheemuddin786 +- **fix(system):** propagate abort signal to stream reader in HTTP version checks ([#12232](https://github.com/diegosouzapw/OmniRoute/pull/12232)) — thanks @raheemuddin786 +- **fix(resilience):** isolate local host execution errors from provider circuit breakers ([#12233](https://github.com/diegosouzapw/OmniRoute/pull/12233)) — thanks @raheemuddin786 +- **fix(sse):** sort injected tools deterministically for prompt caching ([#12234](https://github.com/diegosouzapw/OmniRoute/pull/12234)) — thanks @raheemuddin786 +- **fix(combo):** always clear the loop-safety timer, not just on the happy path (#11804) ([#12245](https://github.com/diegosouzapw/OmniRoute/pull/12245)) +- **fix(quality):** base-red pair — stryker tap registration + turn-pin suites aligned to the window gate ([#12255](https://github.com/diegosouzapw/OmniRoute/pull/12255)) — thanks @backryun +- **fix(dev):** qualify Turbopack runtime boundaries ([#12258](https://github.com/diegosouzapw/OmniRoute/pull/12258)) — thanks @backryun +- **fix(combo):** fall back to full pool when collapsed sole survivor is context-too-small ([#12278](https://github.com/diegosouzapw/OmniRoute/pull/12278)) — thanks @opensource-elearning +- **fix(combo):** bound the pre-dispatch unavailable skip so a stale label cannot dark a pool (#12168) ([#12285](https://github.com/diegosouzapw/OmniRoute/pull/12285)) +- **fix(security):** strip Qwen/Alibaba console-session cookies from provider API responses ([#12287](https://github.com/diegosouzapw/OmniRoute/pull/12287)) +- **fix(usage):** console-aware Token Plan guidance + subscription hint on bailian 401 ([#12288](https://github.com/diegosouzapw/OmniRoute/pull/12288)) +- **fix(resourcePressure):** log numeric detail on every rejection, recover faster ([#12293](https://github.com/diegosouzapw/OmniRoute/pull/12293)) — thanks @hartmark +- **fix(release):** drain the 2026-09-01 base-red window — passthrough usage regression + radarPage i18n keys ([#12327](https://github.com/diegosouzapw/OmniRoute/pull/12327)) +- **fix(ci):** openapi-security-tiers checker must honor routeGuard patterns + imported prefixes ([#12350](https://github.com/diegosouzapw/OmniRoute/pull/12350)) — thanks @ggiak +- **perf(ci):** cache node_modules in the npm-ci-retry composite (#8084 D3) ([#12408](https://github.com/diegosouzapw/OmniRoute/pull/12408)) +- **revert(ui):** point CTAs back at their real destinations ([#12410](https://github.com/diegosouzapw/OmniRoute/pull/12410)) +- **fix(release):** drain the 2026-09-02 base-red — rerank-providers import + api-typecheck baseline ratchet ([#12414](https://github.com/diegosouzapw/OmniRoute/pull/12414)) +- **fix(types):** make system prompt injection noImplicitAny-safe ([#12416](https://github.com/diegosouzapw/OmniRoute/pull/12416)) +- **fix(quality):** validate typecheck baseline schema ([#12419](https://github.com/diegosouzapw/OmniRoute/pull/12419)) +- **fix(memory):** resolve rerank provider node cache import ([#12421](https://github.com/diegosouzapw/OmniRoute/pull/12421)) +- **fix(ci):** repair release-root regressions — pack dedup, web-session syntax, uc-image ids ([#12423](https://github.com/diegosouzapw/OmniRoute/pull/12423)) — thanks @backryun +- **fix(providers):** separate MaxAI and UC credential contracts ([#12431](https://github.com/diegosouzapw/OmniRoute/pull/12431)) +- **fix(cli):** preserve Claude settings on config set ([#12432](https://github.com/diegosouzapw/OmniRoute/pull/12432)) — thanks @KrzysiekSko +- **fix(providers):** repair the maxai credential block truncated by merge auto-resolve ([#12433](https://github.com/diegosouzapw/OmniRoute/pull/12433)) +- **fix(quality):** record the 2026-09-02 merged growth in the file-size baseline ([#12434](https://github.com/diegosouzapw/OmniRoute/pull/12434)) +- **fix(uc):** route UC error strings through sanitizeErrorMessage; allowlist the retired codex id ([#12437](https://github.com/diegosouzapw/OmniRoute/pull/12437)) +- **fix(adapta):** redact streamed upstream errors ([#12438](https://github.com/diegosouzapw/OmniRoute/pull/12438)) +- **fix(sse):** remap non-contiguous upstream tool_calls index to a gap-free output_index ([#12445](https://github.com/diegosouzapw/OmniRoute/pull/12445)) — thanks @hartmark +- **fix(quality):** detect duplicate tool_calls entries in one response ([#12446](https://github.com/diegosouzapw/OmniRoute/pull/12446)) — thanks @hartmark +- **fix(conversations):** resolve turn content OmniRoute never sends back to the client ([#12447](https://github.com/diegosouzapw/OmniRoute/pull/12447)) — thanks @hartmark +- **fix(combo):** do not treat credits-exhausted 401 as auth skip ([#12449](https://github.com/diegosouzapw/OmniRoute/pull/12449)) — thanks @RaviTharuma +- **fix(auth):** do not park healthy quota accounts as expired ([#12452](https://github.com/diegosouzapw/OmniRoute/pull/12452)) — thanks @RaviTharuma +- **fix(responses-continuation):** fail closed on a collector-truncated, empty output array ([#12460](https://github.com/diegosouzapw/OmniRoute/pull/12460)) — thanks @hartmark +- **fix(logging):** raise the SSE payload collector's default cap ([#12461](https://github.com/diegosouzapw/OmniRoute/pull/12461)) — thanks @hartmark +- **fix(resilience):** surface Responses failed.error.message in 502s ([#12472](https://github.com/diegosouzapw/OmniRoute/pull/12472)) — thanks @RaviTharuma +- **fix(security):** unbiased maxai X-Random nonce + stricter URL/regex assertions ([#12502](https://github.com/diegosouzapw/OmniRoute/pull/12502)) +- **fix(video):** re-anchor transcript log-redaction so PII/credential maskers can't reopen the leak (#12150 P1 follow-up) ([#12503](https://github.com/diegosouzapw/OmniRoute/pull/12503)) +- **fix(monitoring):** serve cached credentialHealth off the request path ([#12533](https://github.com/diegosouzapw/OmniRoute/pull/12533)) — thanks @RaviTharuma +- **fix(quota):** keep Antigravity Gemini usable when Claude weekly is empty ([#12566](https://github.com/diegosouzapw/OmniRoute/pull/12566)) — thanks @HouMinXi +- **fix(authz):** hard-gate every credential export and CLI-config write (GHSA-5926-2w35-7h4q) ([#12600](https://github.com/diegosouzapw/OmniRoute/pull/12600)) +- **fix(docs):** restore the Next build — REMOVED_PROVIDERS.md had no frontmatter (base-red #12581) ([#12610](https://github.com/diegosouzapw/OmniRoute/pull/12610)) +- **fix(quality):** re-point the zcodeProtocol public-creds allowlist to line 313 ([#12615](https://github.com/diegosouzapw/OmniRoute/pull/12615)) +- **fix(security):** close 3 advisories — search baseUrl exfil, sk- in the error sanitizer, bifrost relay header leak ([#12620](https://github.com/diegosouzapw/OmniRoute/pull/12620)) +- **fix(providers):** sanitize boolean required and nested bare maps for Gemini (#12269) ([#12624](https://github.com/diegosouzapw/OmniRoute/pull/12624)) — thanks @HouMinXi +- **fix(resilience):** keep Overloaded STREAM_EARLY_EOF off the provider breaker ([#12626](https://github.com/diegosouzapw/OmniRoute/pull/12626)) — thanks @HouMinXi +- **fix(catalog):** re-audit free-tier quotas against official pages ([#12649](https://github.com/diegosouzapw/OmniRoute/pull/12649)) +- **fix(dashboard):** keep a request's pending-tracking id stable across combo target retries ([#12650](https://github.com/diegosouzapw/OmniRoute/pull/12650)) — thanks @hartmark +- **fix(tests):** retire dead model ids from the chat-pipeline integration suite (base-red #12581) ([#12670](https://github.com/diegosouzapw/OmniRoute/pull/12670)) +- **fix:** resolve SqliteError no such table compression_run_telemetry during cleanup ([#12682](https://github.com/diegosouzapw/OmniRoute/pull/12682)) — thanks @groovecityJO +- **fix(combo):** treat a pin-only step as implicit connection allowlist ([#12697](https://github.com/diegosouzapw/OmniRoute/pull/12697)) — thanks @HouMinXi +- **fix(ci):** ship .npmrc in published package so legacy-peer-deps applies to consumers (#11544) ([#12699](https://github.com/diegosouzapw/OmniRoute/pull/12699)) — thanks @KooshaPari +- **fix(docker):** pin 4 CLI tools to exact versions (#12576) ([#12703](https://github.com/diegosouzapw/OmniRoute/pull/12703)) — thanks @KooshaPari +- **fix(docker):** document and harden cli profile trust boundary (#12570) ([#12706](https://github.com/diegosouzapw/OmniRoute/pull/12706)) — thanks @KooshaPari +- **fix(sse):** redact video transcript in pre-guardrail rejected-request logs (#12150 P2 item 7) ([#12710](https://github.com/diegosouzapw/OmniRoute/pull/12710)) +- **fix(i18n):** quote placeholder in OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES description (#12505) ([#12769](https://github.com/diegosouzapw/OmniRoute/pull/12769)) — thanks @KooshaPari +- **fix(api/pricing):** surface validation error message as string, not raw object (#12494) ([#12771](https://github.com/diegosouzapw/OmniRoute/pull/12771)) — thanks @KooshaPari +- **fix(sse):** re-enable prompt compression for native Codex passthrough ([#12834](https://github.com/diegosouzapw/OmniRoute/pull/12834)) — thanks @thomasmaerz +- **fix(dashboard):** model health tests for a provider node set to the Responses API now call `/v1/responses` with a Responses-shaped body instead of `/v1/chat/completions` — those models were reported as `Provider returned HTTP 200 but no text content` even though the same model answered normally through `/v1/responses` ([#13070](https://github.com/diegosouzapw/OmniRoute/issues/13070)) ([#13087](https://github.com/diegosouzapw/OmniRoute/pull/13087)) — thanks @ntdatt812 +- fix(routing): stop a round-robin combo's "opencode" targets from collapsing onto the opencode-zen connection (#11912) ([#13283](https://github.com/diegosouzapw/OmniRoute/pull/13283)) +- fix(routing): stop the reactive-compaction debug log from lying when compression is globally disabled (#11977) ([#13276](https://github.com/diegosouzapw/OmniRoute/pull/13276)) +- fix(electron): relativize standalone-bundle symlink targets so Stage 8 manifest verification stops failing on Windows (#11979) ([#13251](https://github.com/diegosouzapw/OmniRoute/pull/13251)) +- fix(dashboard): surface a visible error when Compression Studio's combined preview run fails (#12061) ([#13288](https://github.com/diegosouzapw/OmniRoute/pull/13288)) +- fix(dashboard): make the compression "Effective pipeline" preview honor the active profile and warn when the master switch is off (#12063) ([#13287](https://github.com/diegosouzapw/OmniRoute/pull/13287)) +- fix(providers): scope TinyCMS Web signer's DOM shims to each call instead of leaking them for the process lifetime, and surface a clean HTTP status on a non-JSON interception-toggles error (#12072) ([#13254](https://github.com/diegosouzapw/OmniRoute/pull/13254)) +- fix(guardrails): stop Vision Bridge from re-selecting a model locked after a 404 (#12111) ([#13259](https://github.com/diegosouzapw/OmniRoute/pull/13259)) +- fix(sse): require Responses-shaped body before native OpenAI-compatible passthrough (#12129) ([#13278](https://github.com/diegosouzapw/OmniRoute/pull/13278)) +- fix(providers): minimax-m3 now collapses manual thinking.type:"enabled" to adaptive, preventing upstream 400 (2013) (#12132) ([#13249](https://github.com/diegosouzapw/OmniRoute/pull/13249)) +- fix(db): scope model visibility overrides by modality so hiding a Chat model no longer hides an identically-ID'd Image/Embeddings/etc. model (#12172) ([#13258](https://github.com/diegosouzapw/OmniRoute/pull/13258)) +- fix(db): scope local-provider apiKey dedup to matching base URL so LM Studio/Ollama-style connections support multiple accounts (#12173) ([#13250](https://github.com/diegosouzapw/OmniRoute/pull/13250)) +- fix(providers): refresh Trae's stale Referer/Origin and forward user timezone so imported connections stop failing with 401 (#12190) ([#13255](https://github.com/diegosouzapw/OmniRoute/pull/13255)) +- fix(providers): route opencode-go/gpt-5.6-luna to /responses instead of /chat/completions (#12196) ([#13275](https://github.com/diegosouzapw/OmniRoute/pull/13275)) +- fix(dashboard): allow deleting the last extra-upstream-header row even when invalid (#12251) ([#13252](https://github.com/diegosouzapw/OmniRoute/pull/13252)) +- **fix(i18n):** translate pre-existing `__MISSING__:` keys for `combo.sort`, `requestLogger.detail` expand/collapse, `common.profile`, and `settings.resilienceCredentialHealth*` across 39 locales ([#12272](https://github.com/diegosouzapw/OmniRoute/issues/12272)) ([#13034](https://github.com/diegosouzapw/OmniRoute/pull/13034)) — thanks @HouMinXi +- fix(cli): run the Node.js runtime compatibility guard before the heavy `tsx/esm` + Commander import chain so an unsupported runtime gets a clear message instead of a raw `Invalid regular expression flags` crash (#12296) ([#13253](https://github.com/diegosouzapw/OmniRoute/pull/13253)) +- fix(dashboard): refresh the providers list after deleting a compatible provider node (#12298) ([#13256](https://github.com/diegosouzapw/OmniRoute/pull/13256)) +- fix(usage): fail closed on API-key budget enforcement when a provider's `auto` routing alias has no pricing row, instead of silently counting it as $0 (#12341) ([#13257](https://github.com/diegosouzapw/OmniRoute/pull/13257)) +- **fix(providers):** include the submitted Agnes video model when polling by `video_id` ([#12356](https://github.com/diegosouzapw/OmniRoute/pull/12356)) — thanks @pacocartones +- **fix(routing):** custom OpenAI-compatible nodes now honor the saved Chat/Responses API type after edits instead of letting the node's original ID prefix override the live connection setting ([#11884](https://github.com/diegosouzapw/OmniRoute/issues/11884)). ([#12358](https://github.com/diegosouzapw/OmniRoute/pull/12358)) — thanks @pacocartones +- fix(sse): surface an error instead of a silent empty 200 when a Claude stream closes with zero bytes (#12398) ([#13285](https://github.com/diegosouzapw/OmniRoute/pull/13285)) +- fix(oauth): warn before the dead localhost:8080 redirect in antigravity/gemini `oauth start` (#12413) ([#13265](https://github.com/diegosouzapw/OmniRoute/pull/13265)) +- fix(providers): stop devin-cli spawn error from double-closing the SSE controller (#12517) ([#13261](https://github.com/diegosouzapw/OmniRoute/pull/13261)) +- **fix(rerank):** clamp Voyage `top_k` to the documents actually sent after empty-string filtering, and honor `return_documents: false` in the NVIDIA response adapter (#12523 — thanks @pacocartones) +- **fix(audio):** `/v1/audio/translations` now resolves combo names the way `/v1/audio/transcriptions` already does, so a combo that `GET /v1/models` advertises is fanned out to its targets instead of being rejected with `400 Invalid translation model: . Use format: provider/model`; literal `provider/model` ids and unknown bare names behave as before (#12536 — thanks @pacocartones) +- **fix(gemini):** strip the JSON-Schema-2020-12 `prefixItems` keyword from Gemini tool schemas at every nesting level, so Claude Code tool definitions no longer fail with `400 Unknown name "prefixItems"` on Gemini models (#12540 — thanks @pacocartones) +- **fix(api-manager):** Expose an accessible loading status while API keys are fetched instead of an empty accessibility tree (#12541 — thanks @pacocartones) +- **fix(video):** Clamp `estimateJpegFrameBytes` at zero for padding-only payloads and build the three encode-side frame data URIs from `JPEG_FRAME_DATA_URI_PREFIX` instead of a repeated literal (#12543 — thanks @pacocartones) +- **fix(devin):** accept Windows `DEVIN_AGENTIC_HOME` sandbox paths (`C:\...\.sandbox\...`) in the isolated-home check so the Devin Claude Bridge no longer fails closed on Windows ([#12405](https://github.com/diegosouzapw/OmniRoute/issues/12405)) (#12545 — thanks @pacocartones) +- **fix(db):** Add `conversation_turn_nodes` and orphaned `agentic_conversations` to the auto-cleanup cycle under the existing `retention.callLogs` window, so identity nodes whose call-log content has already been purged no longer accumulate without bound in `storage.sqlite` (#12548 — thanks @pacocartones) +- **fix(i18n):** Wrap the `~/.claude/profiles//settings.json` placeholder in ICU single quotes in the `OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES` feature-flag description across all 42 locales and the TypeScript default, so next-intl no longer fails with `INVALID_MESSAGE: UNCLOSED_TAG` and the Feature Flags card shows the description instead of the raw key (#12549 — thanks @pacocartones) +- **fix(orchestration):** `updateCloudAgentTask` now publishes the task's real `status` on `agent.task.updated` when an update only touches `result`, `activities` or `error`, instead of the fabricated `"updated"` state, and stays silent when no row matched the id (#12550 — thanks @pacocartones) +- **fix(i18n):** the home "Recent Requests" panel and the Provider Topology legend are now translated instead of rendering English copies on non-English dashboards; the legend reads its own `home.topologyLegend*` labels with consistent casing rather than borrowing the memory-settings "Recent" and analytics "Error" strings (#12551 — thanks @pacocartones). +- **docs(reference):** bring the `FEATURE_FLAGS.md` catalog back to 1:1 with `featureFlagDefinitions.ts` — 20 missing flags added, the two `*_BLOCK_THRESHOLD` env-only knobs moved out of the flag tables, category/total counts and the Live WS port corrected, guarded by a static test (#12552 — thanks @pacocartones) +- **fix(i18n):** backfill missing `usage.kiloPass*` strings in 39 locales and restore `featureFlagOmnirouteDisableThinkingLevelVariantsDescription` in `pt.json` ([#12561](https://github.com/diegosouzapw/OmniRoute/issues/12561)) ([#13027](https://github.com/diegosouzapw/OmniRoute/pull/13027)) — thanks @HouMinXi +- fix(docker): default docker-compose app ports (dashboard/API/live-WS) to loopback instead of `0.0.0.0`, closing the anonymous `/v1` LAN/WAN exposure gap left open by `REQUIRE_API_KEY=false` (#12568) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): close DNS-rebinding SSRF gap in webhook outbound-URL guard (#12569) ([#13243](https://github.com/diegosouzapw/OmniRoute/pull/13243)) +- fix(docker): require a per-session token on the VNC browser CDP bridge and isolate it on a dedicated Docker network (#12571) ([#13241](https://github.com/diegosouzapw/OmniRoute/pull/13241)) +- fix(open-sse): write Adobe Firefly session tokens and cookie jars with 0700/0600 permissions instead of the process umask (#12572) ([#13242](https://github.com/diegosouzapw/OmniRoute/pull/13242)) +- fix(api): remove hardcoded wildcard CORS in openai-to-gemini-sse.ts so the centralized fail-closed CORS gate is the sole source of `Access-Control-Allow-Origin` (#12573) ([#13239](https://github.com/diegosouzapw/OmniRoute/pull/13239)) +- fix(api): enforce API key policy (budget/rate-limit/schedule/endpoint scoping) on the ElevenLabs speech-to-text, text-to-speech and voices proxy routes (#12574) ([#13244](https://github.com/diegosouzapw/OmniRoute/pull/13244)) +- fix(sse): cap HuggingChat NDJSON body size and bound the read loop with the fetch timeout so a stalled or hostile upstream cannot buffer unbounded memory (#12577) ([#13282](https://github.com/diegosouzapw/OmniRoute/pull/13282)) +- fix(docker): scope the cliproxyapi/qdrant/bifrost sidecars to loopback by default and forward `CLIPROXYAPI_MANAGEMENT_KEY` into the cliproxyapi container so its management API is not left both unauthenticated and LAN-published (#12578) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): create DB export temp paths with `fs.mkdtempSync` instead of predictable timestamps (#12579) ([#13237](https://github.com/diegosouzapw/OmniRoute/pull/13237)) +- Cline 401 bodies that say "re-authenticate your Cline account" classify as a refreshable OAuth token, not a terminal expired key. The cooling panel no longer labels every cooldown as a 429; it shows the recorded last error instead. (#12594) ([#13060](https://github.com/diegosouzapw/OmniRoute/pull/13060)) — thanks @HouMinXi +- **fix(catalog):** degrade unknown combo targets instead of dropping LCD modalities, and persist OpenRouter `architecture.input_modalities` into the capability snapshot ([#12613](https://github.com/diegosouzapw/OmniRoute/issues/12613)) ([#13042](https://github.com/diegosouzapw/OmniRoute/pull/13042)) — thanks @HouMinXi +- fix(providers): send `x-api-key` instead of `Authorization: Bearer` for OpenCode Zen's `/v1/responses` endpoint (Muse Spark Contributor models), fixing a 401 on OmniRoute's auth header (#12633) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(sse): surface the actionable "Auggie CLI not found" message when the shell reports a missing `auggie` binary via exit code instead of a spawn error (#12645) ([#13263](https://github.com/diegosouzapw/OmniRoute/pull/13263)) +- **fix(config):** Persist deletions of built-in background-degradation entries — when a stored settings record exists its `degradationMap` is now authoritative instead of being merged under the defaults, so an entry the user removed in the dashboard no longer reappears on the next apply or restart ([#12424](https://github.com/diegosouzapw/OmniRoute/issues/12424)) ([#12647](https://github.com/diegosouzapw/OmniRoute/pull/12647)) — thanks @pacocartones +- **fix(gamification):** action-count badge milestones (First Token, Token Consumer, Token Machine, Token Whale, and the token-sharing tier) are now backed by a durable `xp_action_counts` counter incremented in `addXp()`, instead of a live `COUNT(*)` over `xp_audit_log`. The audit log is pruned by `retention.xpAuditLog` (default 30 days), so on a default install those "lifetime" milestones were really "actions in the last 30 days" and unlocked badges could stop unlocking once old rows aged out. `getActionCount()` and `checkActionCountBadges()` now read the same durable source, and a migration backfills existing totals from the surviving audit rows ([#12546](https://github.com/diegosouzapw/OmniRoute/issues/12546)) ([#12651](https://github.com/diegosouzapw/OmniRoute/pull/12651)) — thanks @pacocartones +- **fix(images):** `/v1/images/edits` now iterates a combo's targets the same way `/v1/images/generations` does (#9239) instead of flattening a bare combo to its first target. A combo whose first target is not edit-capable — or lacks credentials — now falls through to a later edit-capable target rather than hard-erroring, and missing credentials are skipped (not a hard `401`) to match the generations path. The per-target skip/terminal classification is extracted into a shared `runImageComboTargets` loop, so generations behavior is unchanged ([#12547](https://github.com/diegosouzapw/OmniRoute/issues/12547)). ([#12653](https://github.com/diegosouzapw/OmniRoute/pull/12653)) — thanks @pacocartones +- fix(sse): add first-byte watchdog to the TLS-fingerprint transport so a stalled wreq body falls back instead of hanging for minutes (#12656) ([#13272](https://github.com/diegosouzapw/OmniRoute/pull/13272)) +- fix(sse): exempt tiny-budget reasoning probes from combo quality failure and surface persisted-cooldown skips in ALL_TARGETS_SKIPPED diagnostics (#12659) ([#13269](https://github.com/diegosouzapw/OmniRoute/pull/13269)) +- fix(models): declare the real ~1M contextLength for OpenCode Zen's Muse Spark 1.2 models instead of falling back to the 200000 provider default (#12681) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(oauth): align codebuddy-cn OAuth User-Agent with the chat/usage CLI version to avoid WAF false positives (#12702) ([#13264](https://github.com/diegosouzapw/OmniRoute/pull/13264)) +- fix(dashboard): surface an authentication-required banner instead of silently blanking database settings for a guest session (#12709) ([#13270](https://github.com/diegosouzapw/OmniRoute/pull/13270)) +- **fix(resilience):** stop unbounded queue that hangs 6min until Aborted — gate, provider slot, and Bottleneck queue now share a per-connection `maxWaitMs` budget; `fail-closed` on exhaust (503); execution backstop `executionMaxWaitMs` overridable per connection with upstream clamp ([#12715](https://github.com/diegosouzapw/OmniRoute/pull/12715)) — thanks @maxmad64bis (with thanks to @Tushar49 for surfacing the slow-provider need in #12635) +- **fix(auto-combo):** every mode pack now carries `quality` and `reliability` weights (`quality` 0.02, 0.03 in `quality-first`; `reliability` 0.03, 0.04 in `reliability-first`), so selecting a pack no longer silences either signal; `DEFAULT_WEIGHTS` is unchanged ([#12731](https://github.com/diegosouzapw/OmniRoute/pull/12731)) — thanks @maxmad64bis +- **fix(sse):** the provider execution pipeline keeps the upstream error `code`/`type` again — both were lost when the non-streaming leg was extracted, so a config-class `422` (Antigravity missing project) degraded into a generic account cooldown instead of reaching the gate that recognises it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(db):** a legacy `call_logs` table no longer aborts startup — the provider-stats index is created after the column healing runs, not before it, so an install predating `request_type` opens instead of failing with `no such column` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(catalog):** a malformed row in the operator-writable custom-models blob no longer kills every `auto/*` pool ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(tests):** realign the guards that the same day's merges left asserting the old behavior — the provider-count total after GreenPT and EURouter, the GLM stream buffer slot that `#12925` turned into a declared parameter, and the injection scan bound that `#13104` reshaped from a head-only window into head-plus-tail ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- fix(cache): fold tool_choice/tools/response_format into the semantic cache signature so a cached tool_calls response can no longer be replayed for a request whose tool policy forbids it (#12734) ([#13267](https://github.com/diegosouzapw/OmniRoute/pull/13267)) +- **fix(models):** stop treating provider-supplied `isFree:true`/`:free`/`0/0` flags as trusted unless the provider has a documented free tier — only the shipped free-tier catalog decides otherwise; fetched `isFree:true` trusted only for a free-tier provider, custom `isFree:true` via a trusted path ([#12744](https://github.com/diegosouzapw/OmniRoute/pull/12744)) — thanks @maxmad64bis +- fix(memory): authenticate the internal /v1/rerank loopback call so memory reranking no longer silently degrades to unranked order when REQUIRE_API_KEY=true (#12745) ([#13268](https://github.com/diegosouzapw/OmniRoute/pull/13268)) +- fix(sse): parse Ollama Cloud's current usage markup (`$X of $Y used` aria-label, nested width style) (#12749) ([#13260](https://github.com/diegosouzapw/OmniRoute/pull/13260)) +- fix(cli): setup-opencode no longer sends an active context's management token to `/v1/models` when `--api-key`/`OMNIROUTE_API_KEY` is supplied — an explicit flag or the env var now always outranks the context's token, and the flag itself is no longer swallowed by the parent program's global `--api-key` option (#12783) ([#13246](https://github.com/diegosouzapw/OmniRoute/pull/13246)) +- fix(sse): register Arcee AI in the executor provider registry so requests reach api.arcee.ai instead of silently falling back to OpenAI (#12784) ([#13277](https://github.com/diegosouzapw/OmniRoute/pull/13277)) +- **fix(quota):** share one upstream quota read when identical saturation checks arrive at the same time, so traffic bursts don't multiply provider API calls ([#12787](https://github.com/diegosouzapw/OmniRoute/pull/12787)) — thanks @maxmad64bis +- **fix(db):** ignore expired or invalid rate-limit cooldown writes so a stale timestamp can't lock a connection that should be usable — clearing still works as before ([#12788](https://github.com/diegosouzapw/OmniRoute/pull/12788)) — thanks @maxmad64bis +- **fix(providers):** single source for provider order with `xao` ranking alongside `xai-oauth` ([#12790](https://github.com/diegosouzapw/OmniRoute/pull/12790)) — thanks @maxmad64bis +- **fix(routing):** off-table models check the free-model catalog before inheriting premium prices, latency bootstraps from the observed pool median, async tiers read live database pricing with a 90-day freshness gate, and the tier cache invalidates on every pricing write ([#12792](https://github.com/diegosouzapw/OmniRoute/pull/12792)) — thanks @maxmad64bis +- **fix(auto-combo):** snapshot scoring uses the observed per-connection account tier and per-model quality instead of neutral constants, and circuit-open providers rank lower at build time ([#12794](https://github.com/diegosouzapw/OmniRoute/pull/12794)) — thanks @maxmad64bis +- **fix(pool):** empty `auto/*` pools now say why they are empty, and the models listing uses the same paid check as routing ([#12795](https://github.com/diegosouzapw/OmniRoute/pull/12795)) — thanks @maxmad64bis +- fix(routing): recognize CLIProxyAPI's 'unknown provider for model' 400 as fallback-worthy (#12800) ([#13284](https://github.com/diegosouzapw/OmniRoute/pull/13284)) +- **fix(sse):** translate-mode streams now emit the estimated token counts as a trailing usage-only chunk before `[DONE]` when the upstream stays silent, so metered chat clients see totals instead of nothing ([#12828](https://github.com/diegosouzapw/OmniRoute/pull/12828)) — thanks @maxmad64bis +- **fix(db):** provider stats stay truthful on empty and legacy databases: fallback counts default to `0` instead of `null`, latency averages read `null` (not `0`) when no durations were recorded, failures logged before the error-type column existed group under `pre_migration` instead of `unclassified`, and per-provider queries use two new composite indexes ([#12832](https://github.com/diegosouzapw/OmniRoute/pull/12832)) — thanks @maxmad64bis +- fix(nvidia): fail open when a synced model catalog goes stale instead of gating forever (#12849) ([#13248](https://github.com/diegosouzapw/OmniRoute/pull/13248)) +- **fix(db):** WAL maintenance lives in its own module: a `TRUNCATE` checkpoint that hits a busy database now warns with its streak and retries once via `PASSIVE` instead of logging success, and closing-time checkpoints no longer report success on builds without a database file, with the busy totals visible in the authenticated monitoring health payload ([#12853](https://github.com/diegosouzapw/OmniRoute/pull/12853)) — thanks @maxmad64bis +- **fix(health):** quota with no snapshots reads empty instead of a contradicting 0% ([#12857](https://github.com/diegosouzapw/OmniRoute/pull/12857)) — thanks @maxmad64bis +- fix(a2a): accept the dashboard's own session cookie on /a2a so "Run message/send" no longer fails with "Unauthorized: missing or invalid API key" (#12888) ([#13271](https://github.com/diegosouzapw/OmniRoute/pull/13271)) +- **fix(logs):** the Logs grid's in-memory filter pass no longer discards rows the SQL query already matched — selecting an API key from the dropdown (which sends the key's id) returns its calls again, the Combo tab shows every combo instead of only those whose name contains a "1", and the model filter and search cover the same columns as the query ([#12896](https://github.com/diegosouzapw/OmniRoute/pull/12896)) — fixes [#12873](https://github.com/diegosouzapw/OmniRoute/issues/12873) — thanks @ntdatt812 +- **fix(a2a):** `/api/a2a/status` now builds the agent card from the request that asked for it, so a gateway reached at a non-localhost host no longer advertises `http://localhost:20128` as its A2A URL ([#12918](https://github.com/diegosouzapw/OmniRoute/pull/12918)). — thanks @ntdatt812 +- **fix(compression):** progressive aging now appends its `[COMPRESSED:aging:…]` annotation after a turn's `tool_result` blocks instead of in front of them, so Anthropic no longer rejects aged conversations with "`tool_use` ids were found without `tool_result` blocks immediately after" ([#12920](https://github.com/diegosouzapw/OmniRoute/pull/12920)). — thanks @ntdatt812 +- **fix(bedrock):** model import now resolves context limits for every vendor prefix instead of only `anthropic.*`, so `global.openai.gpt-5.6-*` no longer imports with a null `inputTokenLimit` and gets rejected pre-flight at the 200k default ([#12921](https://github.com/diegosouzapw/OmniRoute/pull/12921)). — thanks @ntdatt812 +- **fix(stream):** the 64 KB stream buffer GLM asks for is honoured instead of dropped, and the type error it caused no longer fails the API Route Typecheck gate on every open PR ([#12925](https://github.com/diegosouzapw/OmniRoute/pull/12925)) — thanks @ntdatt812 +- **fix(guardrails):** mask PII inside a `tool_result`'s nested content array, which the masker walked past while redacting its sibling block ([#12930](https://github.com/diegosouzapw/OmniRoute/pull/12930)) — thanks @ntdatt812 +- **fix(dashboard):** the Radar catalog table no longer leaves absent data unexplained — empty limits, unknown context windows, and unreported capabilities each explain themselves on hover, and a new check keeps it that way ([#12937](https://github.com/diegosouzapw/OmniRoute/pull/12937)) — thanks @maxmad64bis +- **fix(sse):** geo-blocked opencode requests rotate to the next account proxy instead of failing, so one refused egress no longer aborts the whole chain ([#12941](https://github.com/diegosouzapw/OmniRoute/pull/12941)) — thanks @maxmad64bis +- **fix(codex):** dashboard "clear cooldown" and the CAS recovery path now drop nested `codexScopeRateLimitedUntil` maps in the same write that nulls `rate_limited_until`, and fresh quota snapshots with headroom lift fallback-sourced scope cooldowns parked by quota preflight ([#12817](https://github.com/diegosouzapw/OmniRoute/issues/12817), [#12860](https://github.com/diegosouzapw/OmniRoute/issues/12860), [#12951](https://github.com/diegosouzapw/OmniRoute/pull/12951)) The dashboard reset-credit button (`consumeCodexResetCredit`) now exercises that same snapshot path after a successful redeem, so a filled quota bar is enough to unpark a leftover fallback Codex child without another manual clear. — thanks @HouMinXi +- **fix(db):** resolve `sql-wasm.wasm` across global npm install and hoisted layouts, ensuring OmniRoute can boot cleanly on Node 24 when native `better-sqlite3` is uncompiled. ([#13035](https://github.com/diegosouzapw/OmniRoute/pull/13035)) — thanks @HouMinXi +- **fix(api):** `DELETE /v1/batches/delete-completed` now sweeps only the calling API key's own completed batches (batches with no owner stay out of a key-scoped sweep on purpose), with an explicit instance-wide mode reserved for authenticated dashboard sessions, a 401 for a presented key that is unknown, revoked, deactivated, banned or expired (never falling through to the session branch), audit logging of every sweep, a sanitized 500 on failure, an owner-scoped file half (a key-scoped sweep never nulls a file another tenant owns) and an atomic sweep — chunked in 200-batch transactions in instance mode — so a mid-way error never leaves a batch pointing at a nulled file (GHSA-wvxc-jp3v-5mg5) ([#12969](https://github.com/diegosouzapw/OmniRoute/pull/12969)) +- **fix(combo):** quota-weighted routing stops drawing on an out-of-credit connection — a 402 now invalidates the stored quota snapshot instead of leaving its stale remaining percentage in place, and a snapshot older than 10 minutes no longer counts as confident headroom for the primary pool ([#12972](https://github.com/diegosouzapw/OmniRoute/pull/12972)) ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- **fix(vision):** Custom Models with "Vision capable" checked no longer have image requests swapped to `glm/glm-4.6v` when the client sends the advertised alias (`vllm/path/...`) or the bare path-shaped id — Vision Bridge now matches the stored override for all three id forms ([#12758](https://github.com/diegosouzapw/OmniRoute/issues/12758)) ([#12974](https://github.com/diegosouzapw/OmniRoute/pull/12974)) — thanks @HouMinXi +- **fix(sse):** transient opencode upstream failures rotate to the next account proxy instead of failing, so one flapping egress no longer aborts the whole chain ([#12975](https://github.com/diegosouzapw/OmniRoute/pull/12975)) — thanks @maxmad64bis +- **fix(azure):** Deployments from GPT-6 onward now send `max_completion_tokens` instead of `max_tokens`, which Azure rejects with HTTP 400. The rule matched a literal `gpt-5`, so each new generation arrived broken; it now matches the generation range, while `gpt-35-turbo` still keeps `max_tokens`. ([#13007](https://github.com/diegosouzapw/OmniRoute/pull/13007)) — thanks @ntdatt812 +- **fix(db):** add an opt-in self-restart circuit for sustained critical memory pressure, gate post-cleanup VACUUM behind a minimum freed-rows threshold, and checkpoint the SQLite WAL every 5 minutes with a size guard that escalates to TRUNCATE, so a growing WAL can no longer stall the event loop into a full outage. ([#13011](https://github.com/diegosouzapw/OmniRoute/pull/13011)) — thanks @HouMinXi +- **fix(auth):** an explicit connection pin may probe a quota-disabled row once and re-enable it on success ([#12874](https://github.com/diegosouzapw/OmniRoute/issues/12874)) ([#13017](https://github.com/diegosouzapw/OmniRoute/pull/13017)) — thanks @HouMinXi +- **fix(api):** thread `X-OmniRoute-Fallback-Attempts` through combo chat completions so streaming and non-streaming responses report how many prior legs were attempted ([#13038](https://github.com/diegosouzapw/OmniRoute/pull/13038)) — thanks @HouMinXi +- Restore provider failure classification and credential refresh on non-streaming requests: classify non-2xx failures to lock models on per-model quota exhaustion, update connection rate limits from headers and body, and pass credential refresh handlers to pipeline execution so 401 tokens can be refreshed and retried (#13043). ([#13069](https://github.com/diegosouzapw/OmniRoute/pull/13069)) — thanks @HouMinXi +- **fix(responses):** wrap forced-non-streaming web_search fallback JSON as Responses SSE so Codex still sees `response.completed` ([#13050](https://github.com/diegosouzapw/OmniRoute/pull/13050)) — thanks @HouMinXi +- **fix(antigravity):** Preserve upstream thought-token usage in normalized completion and reasoning token counts ([#13055](https://github.com/diegosouzapw/OmniRoute/pull/13055)) — thanks @pacocartones +- Fix a concurrency-slot leak in the DeepSeek PoW solver: a worker that failed to spawn (for example a missing worker script) never released its slot, so `MAX_CONCURRENT_WORKERS` failures disabled the solver until restart. ([#13097](https://github.com/diegosouzapw/OmniRoute/pull/13097)) — thanks @anhtahaylove +- **fix(acp):** bound the ACP session output buffers — `stdoutBuffer` and `stderrBuffer` now cap at 1 MiB keeping the most recent output behind a visible `[...output truncated...]` marker, and `stderrBuffer` is reset per prompt instead of accumulating for the lifetime of the session. ([#13100](https://github.com/diegosouzapw/OmniRoute/pull/13100)) — thanks @anhtahaylove +- **fix(acp):** release the `stdout`/`exit` listeners and the idle timer that a `sendPrompt` timeout used to leave attached to the `acpManager` singleton, and drop sessions that exited on their own from the session map instead of keeping them forever. ([#13096](https://github.com/diegosouzapw/OmniRoute/pull/13096)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection and PII scanners now read the text a `tool_result` block carries on `content` (string or nested block list), in messages and in system blocks, so tool output is judged by the same rules as user text ([#13101](https://github.com/diegosouzapw/OmniRoute/pull/13101)) — thanks @ntdatt812 +- **fix(gamification):** close the badge notification SSE stream when the request signal is already aborted before the stream starts — a client that disconnects while the route is still awaiting auth used to leave both the 2s unlock poll and the 15s heartbeat running for the lifetime of the process. ([#13106](https://github.com/diegosouzapw/OmniRoute/pull/13106)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection scan now spends its 16 KB budget on both ends of the request instead of the first 16 KB only, so `system`, `instructions`, `query`, `documents` and the newest turns are no longer hidden behind one long message ([#13104](https://github.com/diegosouzapw/OmniRoute/pull/13104)) — thanks @ntdatt812 +- **fix(dashboard):** expose the Volcano Ark console cookie on quota scraping and unwrap connect-error objects so the dashboard shows the upstream message ([#13107](https://github.com/diegosouzapw/OmniRoute/pull/13107)) — thanks @HouMinXi +- **fix(db):** release the `beforeExit`/`SIGINT`/`SIGTERM` handlers when a `node:sqlite` adapter closes, so a closed adapter and its database handle are no longer pinned to `process` for the lifetime of the run — the same treatment #7494 gave the sql.js adapter. ([#13109](https://github.com/diegosouzapw/OmniRoute/pull/13109)) — thanks @anhtahaylove +- **fix(translator):** `contentSchema` and `unevaluatedItems` are now treated as subschema positions by the tool-schema sanitizer, so a truncation placeholder in either is replaced with a permissive schema instead of being forwarded as a string ([#13110](https://github.com/diegosouzapw/OmniRoute/pull/13110)) — thanks @ntdatt812 +- **fix(cli-helper):** clear the `createLogStream` timeout on the abort path — `stop()` aborts the in-flight fetch and returned through the `signal.aborted` branch, which skipped `clearTimeout` and left an armed timer per stopped stream. The stream reader is now also cancelled when the read loop exits early. ([#13114](https://github.com/diegosouzapw/OmniRoute/pull/13114)) — thanks @anhtahaylove +- **fix(docs):** drop TLS-impersonation, MITM-decrypt, supply-chain attestation, and XOR-mask writeups from the public `/docs` catalog and Docker image. Files stay in git for engineers; operators who need them open the repo, not the website. ([#13136](https://github.com/diegosouzapw/OmniRoute/pull/13136)) — thanks @HouMinXi +- **fix(combo):** parse numeric-epoch `rate_limited_until` in the combo cooldown read path ([#13141](https://github.com/diegosouzapw/OmniRoute/pull/13141)) — thanks @maxmad64bis +- **fix(opencode):** both OpenCode plugins now reject a gateway address typed without `http://` at configuration time, instead of publishing every model with an api url no client can call, and the v2 plugin no longer publishes a model card whose api url is blank or relative ([#13142](https://github.com/diegosouzapw/OmniRoute/pull/13142)) — thanks @maxmad64bis +- **fix(providers):** lock opencode model on upstream 400 model-unavailable ([#13146](https://github.com/diegosouzapw/OmniRoute/pull/13146)) — thanks @maxmad64bis +- **fix(logging):** keep the provider exchange rather than the raw client bodies when a call log exceeds its size budget, and show that recovered payload in the request-detail panel instead of replacing it with the stored response body ([#13147](https://github.com/diegosouzapw/OmniRoute/pull/13147)) — thanks @maxmad64bis +- **fix(traffic-inspector):** the WebSocket route no longer leaks a traffic-buffer subscriber and a 30s ping timer when the client socket is already closed at handler time — listeners are attached before any resource is acquired, a destroyed socket bails out early, and the ping interval stops on a dead socket where `write()` never throws ([#13155](https://github.com/diegosouzapw/OmniRoute/pull/13155)) — thanks @anhtahaylove +- **fix(telegram):** bound the per-user API key cache in the Telegram chat proxy so a burst of distinct chat ids can no longer grow the process heap without limit ([#13165](https://github.com/diegosouzapw/OmniRoute/issues/13165)) ([#13166](https://github.com/diegosouzapw/OmniRoute/pull/13166)) — thanks @anhtahaylove +- **fix(stream):** cancel the upstream response body when the JSON-to-SSE sniff unwinds on a body timeout, so a stalled upstream no longer pins the connection ([#13169](https://github.com/diegosouzapw/OmniRoute/issues/13169)) ([#13171](https://github.com/diegosouzapw/OmniRoute/pull/13171)) — thanks @anhtahaylove +- **fix(telegram):** authenticate webhook deliveries with Telegram's `secret_token` so an unauthenticated caller can no longer mint API keys or spend upstream quota ([#13172](https://github.com/diegosouzapw/OmniRoute/issues/13172)) ([#13175](https://github.com/diegosouzapw/OmniRoute/pull/13175)) — thanks @anhtahaylove +- **fix(models):** give discoverable Gemini 3.8 Flash ids their own 65536 output spec so Antigravity no longer clamps them to 16384 ([#13195](https://github.com/diegosouzapw/OmniRoute/pull/13195)) — thanks @HouMinXi +- **fix(dashboard):** leftover catalog-removed provider rows (gemini-cli) can be listed and purged from the providers page ([#13067](https://github.com/diegosouzapw/OmniRoute/issues/13067)) ([#13197](https://github.com/diegosouzapw/OmniRoute/pull/13197)) — thanks @HouMinXi +- **fix(auth):** a dashboard session now requires the `authenticated: true` claim that login, OIDC and the session refresh already emit — a JWT merely signed with `JWT_SECRET` (for example the Cursor CLI passthrough token, which any API-key holder can obtain) no longer verifies as the `auth_token` cookie on any route, the WebSocket handshake or the live server; existing sessions keep working ([#13298](https://github.com/diegosouzapw/OmniRoute/issues/13298)) ([#13375](https://github.com/diegosouzapw/OmniRoute/pull/13375)) +- **fix(api):** `DELETE /v1/batches/delete-completed` now applies the caller's API-key policy (endpoint allowlist, schedule, usage cap, rate limit) like every other `/v1` route, commits the key-scoped sweep per 200-batch chunk instead of holding one write lock for the whole sweep, refuses to loop without progress, and rejects a scope that names both a key and `allTenants` ([#13684](https://github.com/diegosouzapw/OmniRoute/pull/13684)) +- Restore the API-route typecheck gate: the non-streaming leg result lost its discriminated-union narrowing after the server-owned tool loop reassignment, producing 13 new TS2339 diagnostics in `chatCore.ts`. ([#12963](https://github.com/diegosouzapw/OmniRoute/pull/12963)) +- Restore four non-streaming behaviours the server-owned tool loop refactor dropped: client aborts map to 499 with the fixed `Request aborted` message, an aborted request no longer logs a synthetic `clientResponse`, Claude prompt-cache telemetry is recorded again, and a body that cannot be canonicalized no longer throws when the tool loop is off. ([#12990](https://github.com/diegosouzapw/OmniRoute/pull/12990)) +- **fix(skills):** The CLI registry parser now reads positionals declared with `.addArgument()`, not only those written inline in `.command()`. `tunnel create [type]` was being published as `tunnel create`, so the agent-skills sync gate reported drift on every branch and regenerating would have deleted the argument. ([#13009](https://github.com/diegosouzapw/OmniRoute/pull/13009)) — thanks @ntdatt812 +- **fix(combos):** dashboard combo test uses a short prompt, serial probes, and a 60s timeout so reasoning models and rate-limited free pools do not fail the health check ([#13001](https://github.com/diegosouzapw/OmniRoute/pull/13001)) — thanks @HouMinXi +- fix(compression): terminate idle worker threads on eviction so long-running instances stop leaking OS threads and MessagePorts ([#13091](https://github.com/diegosouzapw/OmniRoute/pull/13091)) — thanks @anhtahaylove +- Redact Google API keys of any length in error bodies: the pattern required exactly 39 characters, so shorter or longer `AIza…` credentials passed through unredacted. ([#12964](https://github.com/diegosouzapw/OmniRoute/pull/12964)) +- **fix(grok-cli):** a 402 "Grok Build usage balance exhausted" parks that Grok login as out of credit (Grok Build CLI, grok.com cookie, and xAI OAuth share the weekly pool). Combo routing then tries the next login instead of locking the model for every account in the pool ([#13061](https://github.com/diegosouzapw/OmniRoute/pull/13061)) — thanks @HouMinXi +- fix(compression): spawn the LLMLingua worker with a file URL object so compression actually runs instead of silently failing open on Node ([#13093](https://github.com/diegosouzapw/OmniRoute/pull/13093)) — thanks @anhtahaylove +- **fix(test):** run the local `test` and `test:unit` scripts at concurrency 4 so a full-suite run no longer exhausts the machine's commit charge and kills unrelated processes ([#13187](https://github.com/diegosouzapw/OmniRoute/pull/13187)) — thanks @anhtahaylove +- **fix(monitoring):** `GET /api/monitoring/health` `credentialHealth` now includes a bounded `failedConnections` list (`connectionId`, `status`, sanitized `lastError`) when the probe-cache gauge `failed>0`, plus `source: probe-cache` and a cheap `staleDbNonOkCount` for sticky SQLite `test_status` on active rows. Documents that the live gauge is not `provider_connections.test_status`. ([#12876](https://github.com/diegosouzapw/OmniRoute/pull/12876)) — thanks @RaviTharuma +- **fix(resilience):** an openai-compatible multi-upstream gateway no longer marks the whole connection `credits_exhausted` when a single upstream (or the `/models` / representative-model health probe) returns 402. Quota failures stay model-scoped; true connection-wide auth failures are unchanged. Single-credential 402 key disable ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239)) is preserved. ([#12875](https://github.com/diegosouzapw/OmniRoute/pull/12875)) — thanks @RaviTharuma +- **fix(dashboard):** nine fixes on the Orchestration Canvas, all diagnosed in the Phase 2 reviews. A source that fails now keeps the timestamp of its FIRST failure instead of being re-stamped every poll — the stale line said "since the last poll" no matter how long the source had been down, and the churn also defeated the snapshot's stable identity (it serializes the source list), so the canvas re-rendered on every tick while anything was broken. A source that HAD data and only then started failing is flagged too: previously only a source with no node at all got the warning, so a source that went down mid-session kept a healthy-looking node forever. The rest are pointwise: clicking a filter chip cancels the pending search debounce (left armed, it fired ~300ms later and silently reverted the chip); the search input carries an accessible name; `?state=running, failed` parses like the unpadded form instead of dropping the padded value; the CSV toggle helper is defined once in `model/urlParams.ts` rather than twice; the agents tab tells "nothing running" apart from "the filter matched nothing", offering a clear-filters button instead of setup links that would be wrong advice there; edges stop emitting SMIL particles above 40 simultaneously active edges, keeping the colored stroke; and the drawer's error banner clears when a retried action succeeds. ([#12983](https://github.com/diegosouzapw/OmniRoute/pull/12983)) +- **fix(i18n):** the nine locales added with the EU-language batch (Greek, Estonian, Irish, Croatian, Lithuanian, Latvian, Maltese, Slovenian, Serbian) were missing the eleven Orchestration Canvas keys that Phase 3 introduced, so the compare-runs panel and the "no runs match these filters" empty state fell back to English in those languages (`deepMergeFallback` substitutes English for an absent key, so nothing rendered blank — it rendered untranslated). The coverage gate does not catch this: it enforces an 80% floor per locale, and eleven missing keys out of ~13,000 leaves coverage at 99.9%. Translated for real in each language, calibrated against the wording each file already uses for "run", "filter" and "skill". ([#13294](https://github.com/diegosouzapw/OmniRoute/pull/13294)) +- **fix(dashboard):** five follow-ups from the Phase 2 Orchestration Canvas review. The History drawer now shows the memory section for runs that already left the live TTL window: the persisted `memory_hits` event is parsed into `metadata.memoryHits` with the same defensive validation the drawer applies, and — because that event is observability rather than a state transition — it no longer leaks into the timeline, where it had been inheriting the task's state and rendering as a duplicate transition. The A2A memory recall runs against its own 1.5s deadline instead of inheriting the memory backend's 30s one; overshooting degrades exactly like any other recall failure (no hits, task proceeds), and the timer is cleared on both paths. Repeating a Conductor run carries its `requirements.cli`/`requirements.model` forward, so the new run is pinned to the same runner profile and model rather than drifting to whatever the fleet picks. A successful repeat from the Agents tab now focuses the run it created, instead of leaving the operator on the finished one. And the auth test for `POST /api/conductor/tasks` moved into the shared `ROUTES` array rather than restating the pattern. ([#12988](https://github.com/diegosouzapw/OmniRoute/pull/12988)) +- fix(plugins): stop leaking an exit listener per plugin hook timeout, which triggered MaxListenersExceededWarning on plugins that ignore SIGTERM ([#13092](https://github.com/diegosouzapw/OmniRoute/pull/13092)) — thanks @anhtahaylove +- **fix(validation):** Provider node edits no longer fail with a generic "Invalid request" when the optional daily-quota reset fields are left blank. The dashboard sends `dailyQuotaResetTimezone` and `dailyQuotaResetHour` as `null`, and only the hour accepted it. ([#13066](https://github.com/diegosouzapw/OmniRoute/issues/13066)) ([#13083](https://github.com/diegosouzapw/OmniRoute/pull/13083)) — thanks @ntdatt812 +- **fix(combo):** quota-aware expansion drops banned, inactive, missing, and wrong-provider connections before quota fetch or model dispatch; pins and allowlists stay selectors, not a bypass. Antigravity automatic exhaustion now requires a reported zero remaining, so a positive balance below 1% stays eligible. ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- Fix an intermittent failure in the quota-weighted routing test suite: scores are a function of `Date.now()`, so peers with identical quota scored microseconds apart never tied and swapped order. ([#12946](https://github.com/diegosouzapw/OmniRoute/pull/12946)) +- fix(volcengine): resolve quota window mapping gaps, safe multi-connection console binding, and response cookie sanitization ([#12950](https://github.com/diegosouzapw/OmniRoute/pull/12950)) — thanks @HouMinXi +- **fix(test):** resolve the WebDAV handler path with `fileURLToPath` so the suite's 37 WebDAV tests run on Windows instead of failing with a doubled `C:\C:\` drive prefix ([#13196](https://github.com/diegosouzapw/OmniRoute/pull/13196)) — thanks @anhtahaylove +- **fix(ui):** fall back from invalid AnySearch icon alias (direct commit `9587e07b69`) +- **fix(sse):** stop rebuilding a truncated summary from the collector's cap-dropped event array ([#12718](https://github.com/diegosouzapw/OmniRoute/pull/12718)) — thanks @hartmark +- **fix(conversations):** show a pending spinner for unresolved tool nodes instead of "(empty)" ([#12727](https://github.com/diegosouzapw/OmniRoute/pull/12727)) — thanks @hartmark +- **fix(streaming):** fail fast when an upstream stream produces only lifecycle/heartbeat events, never real content ([#12741](https://github.com/diegosouzapw/OmniRoute/pull/12741)) — thanks @hartmark +- **fix(responses-continuation):** bridge the write-in-flight window with an in-memory pending store ([#12854](https://github.com/diegosouzapw/OmniRoute/pull/12854)) — thanks @hartmark +- **fix(translator):** drop or map agent_message on Chat Completions fallback ([#12880](https://github.com/diegosouzapw/OmniRoute/pull/12880)) — thanks @RaviTharuma +- **fix(api):** page and stream-complete GET /v1/models for large catalogs ([#12882](https://github.com/diegosouzapw/OmniRoute/pull/12882)) — thanks @RaviTharuma +- **fix(quota):** align AUTH window labels with usage API and clarify 503 ([#12884](https://github.com/diegosouzapw/OmniRoute/pull/12884)) — thanks @RaviTharuma +- **fix(ci):** clear two base-reds on release/v3.8.51 — mutation-coverage gate + image-only-model guard ([#12945](https://github.com/diegosouzapw/OmniRoute/pull/12945)) +- **fix(api):** scope batch bulk-delete to the calling API key ([#13211](https://github.com/diegosouzapw/OmniRoute/pull/13211)) +- **fix(tests):** align model-sync log assertion to shipped path redaction (base-red) ([#13227](https://github.com/diegosouzapw/OmniRoute/pull/13227)) +- **fix(test):** make the ToS heading guard actually require the parentheses ([#13228](https://github.com/diegosouzapw/OmniRoute/pull/13228)) +- **fix(quality):** register 4 drifted covering tests in stryker tap.testFiles ([#13229](https://github.com/diegosouzapw/OmniRoute/pull/13229)) +- **fix(logging):** mask *-api-key headers in request-log pipeline (#13273) ([#13401](https://github.com/diegosouzapw/OmniRoute/pull/13401)) — thanks @KooshaPari +- **fix(tests):** correct stale Jina catalog prefix in custom-models test ([#13403](https://github.com/diegosouzapw/OmniRoute/pull/13403)) — thanks @KooshaPari +- **fix(backend):** prune old backups after health-check-repair snapshots (#13308) ([#13404](https://github.com/diegosouzapw/OmniRoute/pull/13404)) — thanks @KooshaPari +- **fix(backend):** guard memory_fts_au trigger with WHEN clause to stop FTS bloat (#13326) ([#13405](https://github.com/diegosouzapw/OmniRoute/pull/13405)) — thanks @KooshaPari +- **fix(free-tiers):** remove obsolete Together $25 signup credit (#12526) ([#13407](https://github.com/diegosouzapw/OmniRoute/pull/13407)) — thanks @KooshaPari +- **fix:** skip non-chat generation models in Test all models ([#13409](https://github.com/diegosouzapw/OmniRoute/pull/13409)) — thanks @KooshaPari +- **fix(providers):** de-duplicate agy and antigravity model catalogs ([#13410](https://github.com/diegosouzapw/OmniRoute/pull/13410)) — thanks @KooshaPari +- **fix(dashboard):** send provider in body for mini-playground web search ([#13411](https://github.com/diegosouzapw/OmniRoute/pull/13411)) — thanks @KooshaPari +- **fix(resilience):** add CJK quota-exhaustion patterns to 429 classifier ([#13412](https://github.com/diegosouzapw/OmniRoute/pull/13412)) — thanks @KooshaPari +- **fix(backend):** eval runner: failed calls no longer score as passed + dotAll regex ([#13413](https://github.com/diegosouzapw/OmniRoute/pull/13413)) — thanks @KooshaPari +- **fix(backend):** eval runner opts out of output-style and memory injection ([#13414](https://github.com/diegosouzapw/OmniRoute/pull/13414)) — thanks @KooshaPari +- **fix:** chain serial quarantine step in npm test script (fixes #13286) ([#13418](https://github.com/diegosouzapw/OmniRoute/pull/13418)) — thanks @KooshaPari +- **fix(antigravity):** derive stable session ID from credentials for prompt-cache reuse (#13305) ([#13424](https://github.com/diegosouzapw/OmniRoute/pull/13424)) — thanks @KooshaPari +- **fix(memory):** merge memory into top-level system field instead of unshifting at messages[0] (#13425) ([#13427](https://github.com/diegosouzapw/OmniRoute/pull/13427)) — thanks @KooshaPari +- **fix(compression):** preserve instruction blocks from lossy engines ([#13523](https://github.com/diegosouzapw/OmniRoute/pull/13523)) — thanks @KooshaPari +- **fix(compression):** preserve polarity words and newlines in ultra heuristic ([#13524](https://github.com/diegosouzapw/OmniRoute/pull/13524)) — thanks @KooshaPari +- **fix(compression):** run user preservePatterns before built-in patterns (#13457) ([#13525](https://github.com/diegosouzapw/OmniRoute/pull/13525)) — thanks @KooshaPari +- **fix(reasoning):** expand deepseek-v4 regex to match v4.1+ and add command-code provider (#13430) ([#13528](https://github.com/diegosouzapw/OmniRoute/pull/13528)) — thanks @KooshaPari +- **fix(providers):** add alwaysReasons metadata for reka-flash-3 ([#13531](https://github.com/diegosouzapw/OmniRoute/pull/13531)) — thanks @KooshaPari +- **fix(playground):** allow Compare columns to scroll independently ([#13532](https://github.com/diegosouzapw/OmniRoute/pull/13532)) — thanks @KooshaPari +- **fix(security):** empty whitelist allows through to prevent admin lockout ([#13534](https://github.com/diegosouzapw/OmniRoute/pull/13534)) — thanks @KooshaPari +- **fix(tests):** add DISABLE_IOREG_STRATEGY env var for macOS test compatibility (fixes #13467) ([#13539](https://github.com/diegosouzapw/OmniRoute/pull/13539)) — thanks @KooshaPari +- **fix(zed-hosted):** map developer role and Gemini enums for Zed proxy ([#13543](https://github.com/diegosouzapw/OmniRoute/pull/13543)) — thanks @KooshaPari +- **fix(analytics):** normalize provider aliases to canonical id in usage_history ([#13545](https://github.com/diegosouzapw/OmniRoute/pull/13545)) — thanks @KooshaPari +- **fix(combo):** use traceId for call log id to prevent UNIQUE constraint failure ([#13546](https://github.com/diegosouzapw/OmniRoute/pull/13546)) — thanks @KooshaPari +- **fix(auth):** check model lockout before returning synthetic noauth connection ([#13547](https://github.com/diegosouzapw/OmniRoute/pull/13547)) — thanks @KooshaPari +- **fix(guardrails):** skip credential redaction for base64 image data URLs ([#13550](https://github.com/diegosouzapw/OmniRoute/pull/13550)) — thanks @KooshaPari +- **fix(resilience):** increase requestQueue.maxWaitMs default from 15s to 30s ([#13553](https://github.com/diegosouzapw/OmniRoute/pull/13553)) — thanks @KooshaPari +- **fix(docs):** document OMNIROUTE_READY_TIMEOUT_MS and allowlist the test-only DISABLE_IOREG_STRATEGY ([#13692](https://github.com/diegosouzapw/OmniRoute/pull/13692)) + ### 📝 Maintenance ---- +- **docs(readme):** reconcile live v3.8.50 provider, free-tier, CLI, routing, test, community, sponsor, acknowledgment, and SVG metrics with their audited source denominators, including a deduplicated OmniRoute-in-Action snapshot and distinct contributor rankings for merged pull requests, GitHub-attributed commits, and Git history ([#11356](https://github.com/diegosouzapw/OmniRoute/pull/11356)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **docs:** sync the canonical provider count 354 → 356 across `README.md`, `AGENTS.md`, `llm.txt` (+ 42 i18n mirrors), the four README SVG diagrams, `docs/reference/PROVIDER_REFERENCE.md` (regenerated) and the `package.json` description after Opper (#11629) and 1min.ai (#11631) boarded the catalog — closes the `check:docs-counts-sync` strict drifts that kept `release/v3.8.51` red ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11673](https://github.com/diegosouzapw/OmniRoute/pull/11673)) — thanks @jonlwheat2-gif +- **test(providers):** update count-derived assertions after the v3.8.51 provider additions — `APIKEY_PROVIDERS` 233 → 235 (Opper #11629 + 1min.ai #11631), reserved-prefix REGISTRY walk 395 → 398, `WEB_FETCH_PROVIDERS` now includes `nimble-search` (#11620), and the provider translate-path golden snapshot regenerated ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11674](https://github.com/diegosouzapw/OmniRoute/pull/11674)) — thanks @jonlwheat2-gif +- **test(sse):** bump the hard-lease connection-query inventory for `src/lib/tokenHealthCheck.ts` to 2 — the verify-only web-cookie sweep added by #11495 split the single `getProviderConnections` call into oauth + cookie variants, which the frozen inventory had not tracked ([#11495](https://github.com/diegosouzapw/OmniRoute/pull/11495)). ([#11677](https://github.com/diegosouzapw/OmniRoute/pull/11677)) — thanks @jonlwheat2-gif / @oyi77 +- **chore(release):** clear the `release/v3.8.51` base-red gates — docs migration count (+38 i18n mirrors), stryker `tap.testFiles`, lockfile registry host, stale ESLint suppressions, the 7 lint regressions they were hiding, the stale `APIKEY_PROVIDERS` partition count (231 → 233), the `glm-5.3-max` tier missing from the GLM test table, a dead `modelType === "chat"` comparison in the models catalog, and the `resolveLiveWsUrl` / `sanitizeLiveWsPort` helpers from #11388 that a later merge dropped from `wsPath.ts` (dashboard live WebSocket URL resolution) ([#11502](https://github.com/diegosouzapw/OmniRoute/pull/11502)) — thanks @MumuTW +- **chore(free-models):** the free-tier regime table now declares which totals figure each regime feeds and whether it may take the no-auth shortcut, so the sets that used to repeat those answers by hand are derived from it — a new `freeType` no longer compiles until it has answered every question, instead of silently contributing to no total ([#11537](https://github.com/diegosouzapw/OmniRoute/pull/11537)) — thanks @maxmad64bis +- **docs(routing):** LKGP strategy copy now states provider-level stickiness and the explicit rules fallback instead of promising per-target pinning ([#11555](https://github.com/diegosouzapw/OmniRoute/pull/11555)) — thanks @maxmad64bis +- **test(chatcore):** pin the response tool-name alias resolution against the ordering hazard that broke every Gemini/Antigravity MCP tool call in v3.8.49. `extractRequestToolIdentityMap` deletes `translatedBody._toolNameMap`, so the later read is always undefined and the ledger survives only through the `requestToolIdentityMap` fallback — removing that fallback previously left the entire tool-name suite green. The resolution moves into `resolveResponseToolNameMap()` next to the map it depends on, with a regression guard that fails without the recovery. No behaviour change. ([#11575](https://github.com/diegosouzapw/OmniRoute/pull/11575)) — thanks @amartinawi +- **docs(env):** document `OMNIROUTE_CORPUS_CACHE_SIZE` in `.env.example` and `ENVIRONMENT.md`, so the env/docs contract check stops failing Docs Gates on every branch ([#11576](https://github.com/diegosouzapw/OmniRoute/pull/11576)) — thanks @ntdatt812 +- **chore(stryker):** register `route-guard-tunnel-processes-local-only` and `universal-quota-aware-routing` in `tap.testFiles`, so their mutant kills count for the three modules they cover ([#11577](https://github.com/diegosouzapw/OmniRoute/pull/11577)) — thanks @ntdatt812 +- **test(authz):** pin `LOCAL_ONLY_API_GET_EXEMPTIONS` by exact membership instead of by entry count, so the guard names the offending path and also catches a substitution ([#11580](https://github.com/diegosouzapw/OmniRoute/pull/11580)) — thanks @ntdatt812 +- **test(build):** pin `resolveNextBuildBundlerFlag` to the `OMNIROUTE_USE_TURBOPACK` contract it implements, across runtimes, instead of asserting an unimplemented Bun override ([#11583](https://github.com/diegosouzapw/OmniRoute/pull/11583)) — thanks @ntdatt812 +- **test(claude-web):** the Claude Web transport test's barrel anchors no longer pin the exact construction spelling. #11421 made the executor map lazy, so `/"claude-web": new ClaudeWebExecutor\(\)/` stopped matching and the test went red on `release/v3.8.51` itself — a base-red gate every open PR inherited. The anchors now match the alias key and the class name and tolerate either spelling in between, and were checked to still fail when an alias is genuinely repointed at another executor ([#11585](https://github.com/diegosouzapw/OmniRoute/pull/11585)) — thanks @ntdat812 +- **test(models):** pin `qwen3.8-max` resolution per provider — two of the four catalogs now serve the bare id and no longer carry `-preview` ([#11590](https://github.com/diegosouzapw/OmniRoute/pull/11590)) — thanks @ntdatt812 +- **test(executors):** drop the stale 128-tool truncation assertion that contradicted the #11444 pass-through contract ([#11591](https://github.com/diegosouzapw/OmniRoute/pull/11591)) — thanks @ntdatt812 +- **test(token-health):** pin the current terminal-skip boundary — expired with retry budget is probed, `account_deactivated` stays skipped ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)) — thanks @ntdatt812 +- **test(models):** follow #11503's retarget of the `gemini-3-pro-high` alias to the hyphenated catalog id ([#11593](https://github.com/diegosouzapw/OmniRoute/pull/11593)) — thanks @ntdatt812 +- **refactor(video bridge):** extract per-video acquisition, whole-result caching, description, and metrics/abort/cleanup out of `VideoBridgeGuardrail.preCall` into a `processVideoPart` seam in a new `videoBridgePipeline.ts`, behind explicit `VideoMediaBrokerPort`, `VideoAudioTranscriptionPort`, and `VideoDrilldownPort` boundaries; `preCall` now only handles request traversal, policy, and response aggregation. The Video tab's FFmpeg/ffprobe runtime status is now an explicit `unknown` / `restricted` / `unavailable` / `available` state instead of a nullable boolean pair, fixing a case where an in-flight or failed probe was mislabeled as "install FFmpeg" ([#11657](https://github.com/diegosouzapw/OmniRoute/issues/11657)). ([#12016](https://github.com/diegosouzapw/OmniRoute/pull/12016)) +- **test(opencode-plugin):** add unit test coverage for `formatFreeBudget()` naming helper ([#11660](https://github.com/diegosouzapw/OmniRoute/pull/11660)) — thanks @f9td56dbgh-hub +- **docs(video):** clarify that the Video Bridge transcript `source` field (`client`, `embedded`, `audio-bridge`) is presently caller-declared and not yet server-verified — OmniRoute enforces the enum shape but does not cryptographically confirm that an `embedded`/`audio-bridge` label came from a server-owned extraction ([#11661](https://github.com/diegosouzapw/OmniRoute/issues/11661)). ([#12001](https://github.com/diegosouzapw/OmniRoute/pull/12001)) +- **chore(providers):** retire the distributed Raycast Relay and Hailuo Web integrations after a provenance audit confirmed substantial derivation from GPL-3.0 sources; the official GitHub and MiniMax API providers remain available. Migration 163 disables legacy connections and invalidates their active leases while preserving connection IDs, audit/history rows, quota snapshots, usage records, and fail-closed API-key connection policies. ([#11691](https://github.com/diegosouzapw/OmniRoute/pull/11691)) +- **chore(providers):** retire the Felo Web provider while its GPL-derived provenance remains on licensing HOLD ([#11698](https://github.com/diegosouzapw/OmniRoute/pull/11698)) — stale runtime and database references now fail closed while existing connection, lease, and usage history remains preserved for auditability. +- **fix(sse):** retire the Gemini Web image-generation bridge while provenance and licensing remain on HOLD, preserving Gemini Web chat/auth/tools and legitimate Gemini/Nano Banana image providers ([#11708](https://github.com/diegosouzapw/OmniRoute/pull/11708)) +- **chore(audio):** retire the built-in EdgeTTS provider and its 11-voice catalog while provenance/licensing review remains on HOLD; the generic `/v1/audio/speech` endpoint and the other speech providers remain available ([#11711](https://github.com/diegosouzapw/OmniRoute/pull/11711)) +- **fix(providers):** retire the unofficial Qwen Web integration and its `qwen-web`/`qw` identities while third-party provenance remains on HOLD ([#11713](https://github.com/diegosouzapw/OmniRoute/pull/11713)) — direct requests now fail closed with a sanitized HTTP 410 and combos skip the retired target, while migration guards preserve connection IDs, allowlists, usage/audit history and invalidate active leases. Supported Qwen API, Qwen Code, cloud, token-plan, audio, local and model-name surfaces remain available; restoring Qwen Web requires a qualified provenance/license decision. +- **fix(providers):** retire `microsoft-designer-web` and its `msdesigner` alias from runtime and catalogs while preserving stored credentials, provider history and allowlists as inactive records — source dependency on gpt4free's [`MicrosoftDesigner.py`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/g4f/Provider/needs_auth/MicrosoftDesigner.py) is proven at snapshot `0943d16eef757a44deb60651c07bbe5fa958cafe`, whose [`LICENSE`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/LICENSE) is GPL-3.0; expressive adaptation is probable, which records provenance/licensing risk without asserting a legal conclusion ([#11720](https://github.com/diegosouzapw/OmniRoute/pull/11720)). +- **docs(legal):** centralize pinned MIT notices for the vendored GCF codec and local flag assets ([#11726](https://github.com/diegosouzapw/OmniRoute/pull/11726)) +- Split the 2,952-line `adobeFireflyClient.ts` into nine focused modules (catalog, credentials, payload, ARP session, upload, responses, IMS, account, poll) behind an unchanged barrel — no consumer edits and no public API change (#11728). — thanks @MumuTW +- **fix(dashboard):** replaced the Model Lockout card's unprovenanced notification MP3 with a short, locally synthesized Web Audio chime ([#11731](https://github.com/diegosouzapw/OmniRoute/pull/11731)) — both toggles retain optional audible feedback without shipping replacement media or third-party code, and unsupported, suspended, or rejected audio contexts fail open without blocking the setting change. +- **chore(deepseek-web):** remove the provenance-unresolved DeepSeek PoW WASM binary and its runtime loader/tracing while retaining the existing JavaScript solver (slower at high difficulty) ([#11732](https://github.com/diegosouzapw/OmniRoute/pull/11732)). +- **chore(assets):** remove four unused provider assets whose redistribution provenance could not be verified while retaining the active Freebuff SVG ([#11735](https://github.com/diegosouzapw/OmniRoute/pull/11735)) +- **docs(assets):** retired two raster images whose redistribution provenance could not be verified ([#11736](https://github.com/diegosouzapw/OmniRoute/pull/11736)) — PicoClaw documentation now reuses the existing SVG, while Zoo Code remains a text link without an image. +- **docs(legal):** add pinned notices for six LobeHub-derived provider SVGs and 65 byte-exact theSVG assets, retaining registry claims as unverified metadata with explicit trademark and HOLD boundaries ([#11737](https://github.com/diegosouzapw/OmniRoute/pull/11737)). +- **chore(quality):** added a versioned 225/225 provider-asset provenance manifest and blocking drift gate ([#11743](https://github.com/diegosouzapw/OmniRoute/pull/11743)) — the inventory pins magic MIME and SHA-256 for every physical file, records immutable source matches for 71 assets, keeps 69 probable and 85 unresolved entries explicit but non-blocking, and treats provenance as source evidence rather than copyright or trademark clearance. +- **refactor(mitm):** replace four previously derived Traffic Inspector utilities — conversation normalization, SSE merging, secret masking, and header sanitization — with independent clean-room implementations based on public provider schemas, WHATWG SSE, and RFC 6750, while keeping the two independently classified passthrough surfaces unchanged ([#11748](https://github.com/diegosouzapw/OmniRoute/pull/11748)). +- Added a unit test that fails seven days before any dated pack under `config/` (`validUntil` and sibling keys) lapses, naming the file and key. The Alibaba free-tier pack expired on 2026-08-27 and turned every PR red the next morning with no commit involved; renewal now happens on someone's terms, not the clock's. ([#11891](https://github.com/diegosouzapw/OmniRoute/pull/11891)) +- `check:workflows` now fails (under `--strict`/`--ratchet`) when any job routed to a self-hosted runner publishes with `--provenance` — npm rejects that with `422` at the registry, which in v3.8.50 only surfaced after the tag and Docker images were public. ([#11895](https://github.com/diegosouzapw/OmniRoute/pull/11895)) +- `scripts/ops/runner-janitor.sh` now proves a path is idle with one `lsof` snapshot and removes stale leftovers itself (tmpfs after 3 h — it is RAM — disk after 24 h), kills orphan `next-build` processes, prunes checkouts of stopped runners, and alerts on memory pressure; `--dry-run` shows exactly what it would do. `docs/ops/RUNNER_BOX.md` reconciled to the measured box (31 GB, 10 listeners). ([#11893](https://github.com/diegosouzapw/OmniRoute/pull/11893)) +- The `next-build` artefact (1.3 GB) is now written and read under `$RUNNER_TEMP` (per-runner, on disk) instead of `/tmp`, which on the self-hosted pool is a 12 GB tmpfs in RAM. Landing it there took 27–32 of the publish job's 76 minutes, and the fixed `/tmp/e2e-build.tar.gz` name let E2E jobs on different runners overwrite each other's download. ([#11896](https://github.com/diegosouzapw/OmniRoute/pull/11896)) +- The CI `build` job now runs in two concurrency lanes — `main` and pull requests — so a release build is never queued behind (or OOM-killed beside) PR builds on the self-hosted pool, which holds one `next-build` comfortably and two at the edge. ([#11901](https://github.com/diegosouzapw/OmniRoute/pull/11901)) +- Re-freeze the ESLint suppressions on `release/v3.8.51` from a clean-room measurement (2 stale file entries pruned, 55 pre-existing `no-explicit-any` in six new files frozen under #11924) and drop the dead `GPT_SIZE_MAP` constant orphaned by the Adobe Firefly client split, so `No new ESLint warnings` stops failing every PR with exit 2 (Refs #11924) ([#11955](https://github.com/diegosouzapw/OmniRoute/pull/11955)) +- Type the 55 `no-explicit-any` sites that had been frozen under #11924 — four redundant casts in `socksConnectorWithFamily.ts` (undici/socks types already accept them) and the mocks/fixtures of the socks-timeout and isFree suites — and drop their suppression entries; the ESLint ratchet shrinks from 5487 to 5432 (Closes #11924) ([#11975](https://github.com/diegosouzapw/OmniRoute/pull/11975)) +- Take the two hosted-runner builds off the PR rail: `Build App` (`build.yml`) is `workflow_dispatch`-only and `DAST smoke (PR)` runs only for PRs into `main` — the 7 GB hosted VM cannot build `release/v3.8.51` in any profile (19/30 red, VM shutdown ~8 min into `next build`) and both checks had turned into permanent noise on every release PR; the bundle stays validated by `ci.yml` on `main` and by `nightly-release-green` on `release/**` (Closes #11946) ([#11962](https://github.com/diegosouzapw/OmniRoute/pull/11962)) +- Move the four nightly jobs that build the backend (`nightly-schemathesis`, `nightly-llm-security` promptfoo + garak, `nightly-resilience` axe-a11y) off the hosted 7 GB runner — where they died on `release/v3.8.51` unseen — onto the box's new `omni-light` pool (two listeners), and document the reshaped fleet (4 active OmniRoute listeners: 2 `omni-build` + 2 `omni-light`, janitor ceiling 4) (Closes #11965) ([#11967](https://github.com/diegosouzapw/OmniRoute/pull/11967)) +- Stop painting every fork PR into `release/**` red: `quality.yml` `Build (advisory)` is skipped (GitHub still reports `continue-on-error` failures as check FAILURE). Hosted `ubuntu-latest` cannot finish `npm run build` on this tree — same class as #11962 taking `build.yml` off the PR rail. `docker-publish.yml` amd64 now runs on the `.113` `omni-build` pool (31 GB / 32 cores, two listeners) with Turbopack, shares the `heavy-build-main` lane with `ci.yml` `Build` so it queues instead of becoming a third heavy, and keeps per-ref concurrency (a merge storm was starting 8 concurrent OOM builds). arm64 stays on `ubuntu-24.04-arm` with webpack — there is no ARM box. Fallback when `USE_VPS_RUNNER` is off: hosted amd64 + webpack (#11976). ([#12021](https://github.com/diegosouzapw/OmniRoute/pull/12021)) +- `docker-publish.yml` builds with webpack (`OMNIROUTE_USE_TURBOPACK=0`) on **both** arches, including amd64 on the `.113` `omni-build` pool. Turbopack had the RAM (31 GB, `omniroute-113-6`) and still panicked (`TurbopackInternalError: there must be a path to a root`, run 33253576569, 26 min in). The same tree's arm64 webpack build on hosted `ubuntu-24.04-arm` succeeded (run 33264823398). Dockerfile already documents webpack as the Docker escape hatch (#11976). ([#12050](https://github.com/diegosouzapw/OmniRoute/pull/12050)) +- **docs(free-tier):** declare the counting vs deciding regimes for "is it free?" and guard the deciding path from DB-backed catalog resolution ([#12226](https://github.com/diegosouzapw/OmniRoute/pull/12226)) — thanks @maxmad64bis +- **docs(auto-combo):** The mode pack table in `docs/routing/AUTO-COMBO.md` now lists all six shipped packs with every weight each one sets, replacing a four-pack table whose numbers had also drifted from the source. It states plainly that no pack sets `quality`, so selecting any pack silences the observed-quality signal. Six more documents that quote the scoring factor count joined the `check:docs-counts` gate, which caught five stale claims — including one naming nine factors that do not exist — and two stale mode pack counts. The dashboard routing panel, which offered four of the six packs and labelled the default strategy "6-Factor Scoring", is now covered by a test; the two packs it was missing are `reliability-first` and `chaos-mode`, the latter labelled as the fault-injection profile it is rather than as one more routing preference ([#12316](https://github.com/diegosouzapw/OmniRoute/pull/12316)) — thanks @maxmad64bis +- **chore(quality):** rebaseline `src/lib/db/apiKeys.ts` for the ACL the key-creation path now preserves ([#12352](https://github.com/diegosouzapw/OmniRoute/pull/12352)) ([#12673](https://github.com/diegosouzapw/OmniRoute/pull/12673)) — thanks @KrzysiekSko +- **chore(providers):** bump the Claude Code wire identity and the Devin bridge image pin from `2.1.220` to `2.1.258` ([#12402](https://github.com/diegosouzapw/OmniRoute/pull/12402)) — thanks @ggiak ([#12604](https://github.com/diegosouzapw/OmniRoute/pull/12604)) +- **docs(env):** align `.env.example`, the README Bun section, and the troubleshooting guide with the code: `OMNIROUTE_USE_TURBOPACK` also governs `npm run build` (not dev-only), `bun run build` follows that flag instead of auto-selecting Webpack, `OMNIROUTE_CHAT_MAX_HEAVY_IN_FLIGHT` is unset by default (no request-count cap), and the structural `503 chat_admission_busy` message matches `chatAdmissionResponses.ts` (#12404 — thanks @pacocartones) +- **chore(electron):** upgrade the desktop app to Electron 44 (Chromium 152, Node 24.18.1) ([#12217](https://github.com/diegosouzapw/OmniRoute/pull/12217)). **Requires macOS 13 (Ventura) or later** — Chromium dropped macOS 12 (Monterey), so Monterey users must stay on an earlier OmniRoute desktop build. Windows and Linux are unaffected; the app already shipped only x64/arm64, so Electron 44 dropping 32-bit builds changes nothing. Removes the `openAsHidden`/`wasOpenedAsHidden` login-item fields deleted in Electron 44 — hidden autostart continues to work through the `--hidden` argument registered with the login item ([#12554](https://github.com/diegosouzapw/OmniRoute/pull/12554)) — thanks @app/dependabot +- **chore(quality):** rebaseline `src/sse/handlers/chat.ts` for the effective-input persistence the continuation fix needs ([#12641](https://github.com/diegosouzapw/OmniRoute/pull/12641)) ([#12680](https://github.com/diegosouzapw/OmniRoute/pull/12680)) — thanks @hartmark +- `Coverage` job on `ci.yml`: the informational Codecov upload gets its own 5-minute ceiling and `continue-on-error`, and the job budget grows from 20 to 30 minutes (the 8-shard c8 merge alone takes ~10) — a stalled upload no longer ends the job `cancelled` and drags a fully green `main` run's conclusion down with it ([#11972](https://github.com/diegosouzapw/OmniRoute/pull/11972)) +- CI hardening on the PR rail: the four `Unit Tests fast-path` shards get `timeout-minutes: 30` (a hung shard held a PR for 64 min instead of GitHub's 6 h default) and both ESLint file caches lose their `restore-keys` fallback, so a cache built under another suppressions file or lint config can no longer report stale verdicts (Refs #11600, #11924) ([#11963](https://github.com/diegosouzapw/OmniRoute/pull/11963)) +- Every CI job that runs a `next build` (`build`, the npm `publish`, both release-green validations) now targets the `omni-build` runner label, which only two of the eight self-hosted runners carry. The box holds one build comfortably and two at the edge; a third now queues on GitHub instead of being OOM-killed by the kernel. ([#11932](https://github.com/diegosouzapw/OmniRoute/pull/11932)) +- **refactor(combo):** move `handleRoundRobinCombo` (and `resolveTargetTokenLimit`) into `open-sse/services/combo/roundRobinCombo.ts`. `combo.ts` drops from 2164 to 1014 lines (`split("\n").length`); the leaf is 1199 (under the 1200 new-file cap). The round-robin call site uses a dynamic `import()` so `releaseStickyPinOnFailure` / `clearStaleLKGP` can stay exported from `combo.ts` without a static cycle. Skip / sticky / semaphore / safety-timer behavior is unchanged. ([#12811](https://github.com/diegosouzapw/OmniRoute/pull/12811)) — thanks @HouMinXi +- **chore(quality):** rebaseline the file-size caps the error-boundary campaign grew past (`open-sse/executors/codex.ts`, `open-sse/vendor/codex-chatgpt-web/bridge.ts`, both via [#12444](https://github.com/diegosouzapw/OmniRoute/pull/12444)) ([#12654](https://github.com/diegosouzapw/OmniRoute/pull/12654)) +- **chore(quality):** rebaseline the file-size caps the hartmark batch grew past (`combos/page.tsx` via [#12355](https://github.com/diegosouzapw/OmniRoute/pull/12355), `open-sse/services/combo.ts` via [#12338](https://github.com/diegosouzapw/OmniRoute/pull/12338)) ([#12623](https://github.com/diegosouzapw/OmniRoute/pull/12623)) — thanks @hartmark +- **chore(quality):** rebaseline the file-size caps the HouMinXi batch grew past when its PRs stacked (`providers/page.tsx`, `chatCore.ts`, `accountFallback.ts`) — each PR measured correctly in isolation, none saw the stacking ([#12619](https://github.com/diegosouzapw/OmniRoute/pull/12619)) +- **chore(quality):** rebaseline `open-sse/services/combo.ts` for the reset-aware scoring the HouMinXi batch stacked ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) ([#12678](https://github.com/diegosouzapw/OmniRoute/pull/12678)) — thanks @HouMinXi +- **docs:** sync provider catalog count (direct commit `4f11b2ae3d`) +- **docs(skills):** sync omni-inference search fallback (direct commit `b4e76a7ed9`) +- **chore(lint):** prune two stale base suppressions (direct commit `1325047d56`) +- **test(auto-combo):** tolerate full-suite rotation load (direct commit `ad3e293f9f`) +- **test:** drain inherited quality snapshot reds (direct commit `21ed68d8ac`) +- **chore(quality):** rebaseline measured CLI bundle growth (direct commit `d35b3f9779`) +- **test(ui):** reproduce invalid AnySearch icon alias (direct commit `12cc6ca834`) +- **test(translator):** fix the relative imports of the relocated deferred-finish test (direct commit `777d9d1629`) +- **docs(counts):** bring the documented provider and migration counts to the measured values (direct commit `e70bea30e9`) +- **test(security):** drop the guard-target set left unused by the sync (direct commit `a32ba0e362`) +- **chore(db):** renumber model_capabilities migration 163 → 169 (cross-branch collision with 163_radar_feed_cache_generated_at) (direct commit `70f5d4cbf9`) +- **docs(cli):** document CLI_PRIME_AGENT_BIN and correct the CLI Agents count (direct commit `9058e39b61`) +- **chore(deps):** refresh runtimes and adopt ESLint 10 ([#11259](https://github.com/diegosouzapw/OmniRoute/pull/11259)) — thanks @backryun +- **refactor(local-corpus):** implement dynamic root resolution and LRU cache ([#11491](https://github.com/diegosouzapw/OmniRoute/pull/11491)) — thanks @raheemuddin786 +- **chore(quality):** prune stale eslint-suppressions entries ([#11596](https://github.com/diegosouzapw/OmniRoute/pull/11596)) +- **chore(ci):** unblock quality gates - env-doc sync + stryker tap.testFiles registration (#9282) ([#11609](https://github.com/diegosouzapw/OmniRoute/pull/11609)) — thanks @Meet6338-X +- **chore(ci):** point the circular-deps gate at dpdm's real JS entrypoint ([#11615](https://github.com/diegosouzapw/OmniRoute/pull/11615)) — thanks @benzntech +- **chore(quality):** rebaseline file-size for #11448/#11495/#11561 growth ([#11630](https://github.com/diegosouzapw/OmniRoute/pull/11630)) +- **deps:** realign onnxruntime-node pin to @huggingface/transformers' 1.24.3 lockstep ([#11633](https://github.com/diegosouzapw/OmniRoute/pull/11633)) — thanks @jonlwheat2-gif +- **test(autoCombo):** port models_dev_tier #11508 guard from node:test to vitest ([#11635](https://github.com/diegosouzapw/OmniRoute/pull/11635)) — thanks @jonlwheat2-gif +- **chore(quality):** rebaseline file-size for #11083/#11631/#11628 growth ([#11636](https://github.com/diegosouzapw/OmniRoute/pull/11636)) +- **chore(lint):** prune stale ESLint suppressions (base-red #11449) ([#11644](https://github.com/diegosouzapw/OmniRoute/pull/11644)) — thanks @jonlwheat2-gif +- **docs(changelog):** aggregate the ten v3.8.50 fragments into CHANGELOG.md ([#11683](https://github.com/diegosouzapw/OmniRoute/pull/11683)) +- **chore(search):** drop unreachable credential sweep after #11642 ([#11687](https://github.com/diegosouzapw/OmniRoute/pull/11687)) — thanks @HouMinXi +- **ci:** add API route TypeScript regression gate ([#11705](https://github.com/diegosouzapw/OmniRoute/pull/11705)) — thanks @Sabeekhann +- **docs(dedup):** correct the CodeQL claim on the tenant-namespace comment ([#11712](https://github.com/diegosouzapw/OmniRoute/pull/11712)) +- **docs(changelog):** add consolidated v3.8.50 stats, top-25 ranking and a .mailmap ([#11715](https://github.com/diegosouzapw/OmniRoute/pull/11715)) +- **docs:** recommend gstack for AI-assisted workflows ([#11770](https://github.com/diegosouzapw/OmniRoute/pull/11770)) — thanks @santosraju99-hub +- **chore(quality):** tighten complexity/cognitive-complexity ratchets to the current tip; land the missed gateways.ts rebaseline ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **docs(i18n):** finish Freepik → Magnific rebrand in locale strings and README ([#11772](https://github.com/diegosouzapw/OmniRoute/pull/11772)) — thanks @turbolego +- **refactor(auto-combo):** fix divergent scoring in combo health reporting ([#11854](https://github.com/diegosouzapw/OmniRoute/pull/11854)) — thanks @jacobsparts +- **chore(changelog):** add missing fragments for #11919/#11918/#11916 ([#11938](https://github.com/diegosouzapw/OmniRoute/pull/11938)) +- **test(check):** escape the runs-on fixture with JSON.stringify, not a quote-only replace ([#11942](https://github.com/diegosouzapw/OmniRoute/pull/11942)) (direct commit `fb7445eaa3`) +- **test(release):** align five suites with the contracts #11933, #11919 and #11876 shipped on release/v3.8.51 ([#11944](https://github.com/diegosouzapw/OmniRoute/pull/11944)) +- **chore(quality):** remove unreachable code and restore test discovery ([#11950](https://github.com/diegosouzapw/OmniRoute/pull/11950)) — thanks @backryun +- **docs(agents):** sync-back landings are fast-forward, never squash ([#11964](https://github.com/diegosouzapw/OmniRoute/pull/11964)) +- **test(infra):** retry recursive temp-dir removal instead of failing a shard on ENOTEMPTY (#11966) ([#11968](https://github.com/diegosouzapw/OmniRoute/pull/11968)) +- **docs(ops):** the .113 heavy-build ceiling is one runner, not two ([#12048](https://github.com/diegosouzapw/OmniRoute/pull/12048)) +- **test(ui):** correct inactive auto-fetch expectation ([#12098](https://github.com/diegosouzapw/OmniRoute/pull/12098)) — thanks @RaviTharuma +- **docs:** document native dependency check escape hatch ([#12101](https://github.com/diegosouzapw/OmniRoute/pull/12101)) — thanks @RaviTharuma +- **test(providers):** regenerate the translate-path golden for OrcaRouter (#11923) ([#12118](https://github.com/diegosouzapw/OmniRoute/pull/12118)) +- **chore(quality):** velocity phase — loosen every numeric baseline by 20% until v4.0, monitor headroom nightly ([#12125](https://github.com/diegosouzapw/OmniRoute/pull/12125)) +- **chore(lint):** batch 0 of #12146 — type the call-log-cap sqlite rows instead of 45 as-any casts ([#12157](https://github.com/diegosouzapw/OmniRoute/pull/12157)) +- **chore(lint):** batch 4 of #12146 — shared/components react-hooks violations resolved ([#12159](https://github.com/diegosouzapw/OmniRoute/pull/12159)) +- **chore(lint):** batch 1 of #12146 — dashboard/cli-code react-hooks violations resolved ([#12160](https://github.com/diegosouzapw/OmniRoute/pull/12160)) +- **chore(lint):** batch 3 of #12146 — dashboard/settings react-hooks violations resolved ([#12162](https://github.com/diegosouzapw/OmniRoute/pull/12162)) +- **chore(lint):** batch 2 of #12146 — resolve the react-hooks compiler violations in dashboard/providers ([#12163](https://github.com/diegosouzapw/OmniRoute/pull/12163)) +- **chore(quality):** register search-432 cooldown test in stryker tap.testFiles ([#12170](https://github.com/diegosouzapw/OmniRoute/pull/12170)) +- **chore(lint):** batch 5 of #12146 — combos, endpoint, provider-stats, api-manager and costs react-hooks violations resolved ([#12174](https://github.com/diegosouzapw/OmniRoute/pull/12174)) +- **build:** add contributor fast profile ([#12192](https://github.com/diegosouzapw/OmniRoute/pull/12192)) — thanks @rafacpti23 +- **docs(readme):** README touch-ups ([#12193](https://github.com/diegosouzapw/OmniRoute/pull/12193)) ([#12194](https://github.com/diegosouzapw/OmniRoute/pull/12194)) ([#12202](https://github.com/diegosouzapw/OmniRoute/pull/12202)) — thanks @rafacpti23 +- **build:** force the webpack bundler in the contributor build profile ([#12197](https://github.com/diegosouzapw/OmniRoute/pull/12197)) — thanks @rafacpti23 +- **build:** skip standalone packaging in the contributor profile ([#12198](https://github.com/diegosouzapw/OmniRoute/pull/12198)) — thanks @rafacpti23 +- **docs(audit):** align every published number with the code and harden check:docs-counts ([#12200](https://github.com/diegosouzapw/OmniRoute/pull/12200)) +- **build:** stub the instrumentation entrypoints in the contributor profile ([#12203](https://github.com/diegosouzapw/OmniRoute/pull/12203)) — thanks @rafacpti23 +- **build:** omit the standalone output target for contributor builds ([#12204](https://github.com/diegosouzapw/OmniRoute/pull/12204)) — thanks @rafacpti23 +- **chore(lint):** batch 6 of #12146 — memory, radar, audit, analytics, cache, usage, activity, home and RequestLoggerV2 react-hooks violations resolved ([#12208](https://github.com/diegosouzapw/OmniRoute/pull/12208)) +- **docs(diagrams):** rename number-carrying diagram files to stable names ([#12210](https://github.com/diegosouzapw/OmniRoute/pull/12210)) +- **docs(dashboard):** redraw onboarding tier-flow SVGs for the real 4-tier model ([#12211](https://github.com/diegosouzapw/OmniRoute/pull/12211)) +- **docs(api):** document every implemented route in openapi.yaml (276 → 692 paths) ([#12212](https://github.com/diegosouzapw/OmniRoute/pull/12212)) +- **chore(lint):** batch 7 of #12146 — final src tail: 37 react-hooks violations across 33 files resolved ([#12244](https://github.com/diegosouzapw/OmniRoute/pull/12244)) +- Revert "docs: recommend gstack for AI-assisted workflows (#11770)" ([#12248](https://github.com/diegosouzapw/OmniRoute/pull/12248)) ([#12249](https://github.com/diegosouzapw/OmniRoute/pull/12249)) +- **docs:** Chaos Mode setup guide + weighted strategy semantics ([#12250](https://github.com/diegosouzapw/OmniRoute/pull/12250)) +- **docs(agents):** protected-surface merge rule — operator approval for agent-instruction files ([#12253](https://github.com/diegosouzapw/OmniRoute/pull/12253)) +- **chore(quality):** register native-codex turn-pin tests in stryker tap.testFiles ([#12263](https://github.com/diegosouzapw/OmniRoute/pull/12263)) +- **chore(quality):** dedupe tap.testFiles entries added by racing base-red fixes ([#12265](https://github.com/diegosouzapw/OmniRoute/pull/12265)) +- **docs(readme):** introduce OmniRouteTray — the macOS menu-bar companion ([#12276](https://github.com/diegosouzapw/OmniRoute/pull/12276)) — thanks @ggiak +- **chore(proxy):** purge the legacy 1proxy residue — sync/rotator modules, dead DB exports, dead settings tab and flag, docs (#12091) ([#12290](https://github.com/diegosouzapw/OmniRoute/pull/12290)) +- **chore(quality):** baseline-headroom skips generated and vendored files in the fileSize worst-file signal ([#12291](https://github.com/diegosouzapw/OmniRoute/pull/12291)) +- **refactor(video):** unify the JPEG frame data-URI contract ([#12322](https://github.com/diegosouzapw/OmniRoute/pull/12322)) +- **chore(deps):** freeze onnxruntime-node and eslint-plugin-react-hooks out of dependabot groups ([#12329](https://github.com/diegosouzapw/OmniRoute/pull/12329)) +- **test(executors):** restore the #10986 reasoning-only fallback guards ([#12364](https://github.com/diegosouzapw/OmniRoute/pull/12364)) — thanks @pacocartones +- **chore(quality):** re-tighten the file-size ratchet to the real LOC (plan 3.8.52 task 0) ([#12411](https://github.com/diegosouzapw/OmniRoute/pull/12411)) +- **test(grok):** format web executor suite ([#12412](https://github.com/diegosouzapw/OmniRoute/pull/12412)) +- **chore(quality):** ratchet open-sse typecheck baseline to zero ([#12418](https://github.com/diegosouzapw/OmniRoute/pull/12418)) +- **test(build):** guard the artifact path policy arrays against duplicates ([#12422](https://github.com/diegosouzapw/OmniRoute/pull/12422)) +- **chore(lint):** adopt eslint-plugin-react-hooks 7.1.1 ([#12428](https://github.com/diegosouzapw/OmniRoute/pull/12428)) +- **chore(providers):** remove a keyless provider integration at its operator's request ([#12440](https://github.com/diegosouzapw/OmniRoute/pull/12440)) +- **docs(providers):** register providers removed at their operator's request and guard against reintroduction ([#12478](https://github.com/diegosouzapw/OmniRoute/pull/12478)) +- **chore(quality):** base fixes — stryker tap.testFiles + node_modules cache key ([#12482](https://github.com/diegosouzapw/OmniRoute/pull/12482)) +- **docs(arch):** one-process recipe for tens of long /v1/responses ([#12493](https://github.com/diegosouzapw/OmniRoute/pull/12493)) — thanks @RaviTharuma +- **chore(quality):** tighten the CodeQL ratchet baseline from 11 to 6 ([#12530](https://github.com/diegosouzapw/OmniRoute/pull/12530)) +- **chore(deps):** pin browserslist override to ^4.28.8 ([#12592](https://github.com/diegosouzapw/OmniRoute/pull/12592)) — thanks @KooshaPari +- **chore(deps):** bump fast-uri to 3.1.7 in the electron lockfile ([#12601](https://github.com/diegosouzapw/OmniRoute/pull/12601)) +- **docs(quality):** document how the CodeQL ratchet refreshes and how to tighten it ([#12611](https://github.com/diegosouzapw/OmniRoute/pull/12611)) +- **test(ci):** pin the openapi-security-tiers two-arm contract with an executing gate test (#12581) ([#12652](https://github.com/diegosouzapw/OmniRoute/pull/12652)) +- **docs(gamification):** align XP Rewards table with code (#12501) ([#12667](https://github.com/diegosouzapw/OmniRoute/pull/12667)) — thanks @KooshaPari +- **refactor(combo):** split executeTarget into gates, attempt, and loop ([#12746](https://github.com/diegosouzapw/OmniRoute/pull/12746)) — thanks @HouMinXi +- **docs(security):** document socket.yml scanner config + CI workflow link (#12575) ([#12764](https://github.com/diegosouzapw/OmniRoute/pull/12764)) — thanks @KooshaPari +- **chore(ci):** guard commit identity in pre-commit to stop author misattribution ([#12772](https://github.com/diegosouzapw/OmniRoute/pull/12772)) +- **deps:** 17 Dependabot bumps — bump electron from 43.4.0 to 43.4.1 in /electron ([#11426](https://github.com/diegosouzapw/OmniRoute/pull/11426)); bump the development group across 1 directory with 8 updates ([#11428](https://github.com/diegosouzapw/OmniRoute/pull/11428)); bump the production group across 1 directory with 11 updates ([#11440](https://github.com/diegosouzapw/OmniRoute/pull/11440)); chore(deps): bump github/codeql-action/init from 4.37.7 to 4.37.8 ([#11925](https://github.com/diegosouzapw/OmniRoute/pull/11925)); chore(deps): bump github/codeql-action from 4.37.7 to 4.37.8 ([#11926](https://github.com/diegosouzapw/OmniRoute/pull/11926)); chore(deps): bump github/codeql-action/analyze from 4.37.7 to 4.37.8 ([#11927](https://github.com/diegosouzapw/OmniRoute/pull/11927)); build(deps): bump github/codeql-action/init from 4.37.8 to 4.37.9 ([#12345](https://github.com/diegosouzapw/OmniRoute/pull/12345)); build(deps): bump github/codeql-action/analyze from 4.37.8 to 4.37.9 ([#12346](https://github.com/diegosouzapw/OmniRoute/pull/12346)); bump the development group across 1 directory with 2 updates ([#12347](https://github.com/diegosouzapw/OmniRoute/pull/12347)); build(deps): bump github/codeql-action from 4.37.8 to 4.37.9 ([#12349](https://github.com/diegosouzapw/OmniRoute/pull/12349)); bump browserslist from 4.28.2 to 4.28.8 ([#12396](https://github.com/diegosouzapw/OmniRoute/pull/12396)); bump the production group across 1 directory with 4 updates ([#12399](https://github.com/diegosouzapw/OmniRoute/pull/12399)); bump @xmldom/xmldom ([#12500](https://github.com/diegosouzapw/OmniRoute/pull/12500)); bump qs from 6.15.2 to 6.16.0 ([#12512](https://github.com/diegosouzapw/OmniRoute/pull/12512)); bump @xmldom/xmldom from 0.9.10 to 0.9.12 ([#12513](https://github.com/diegosouzapw/OmniRoute/pull/12513)); bump fast-uri from 3.1.5 to 3.1.7 ([#12514](https://github.com/diegosouzapw/OmniRoute/pull/12514)); bump @humanfs/node from 0.16.7 to 0.16.8 ([#12515](https://github.com/diegosouzapw/OmniRoute/pull/12515)) +- Clear the `release/v3.8.51` `check:agent-skills-sync` base-red: regenerate `skills/cli-tunnel/SKILL.md` so the `tunnel create [type]` positional that #13009 taught the generator to read is reflected in the committed skill. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` typecheck base-red from #12731: the new `Mode pack ranking gates` candidates in `open-sse/services/autoCombo/__tests__/autoCombo.test.ts` omitted the required `provider`, `model` and `errorRate` fields of `ProviderCandidate`, failing `check:open-sse-typecheck` (and with it `Fast Quality Gates`) on every open PR. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` docs-sync base-red: EURouter (#13025) and GreenPT (#13024) took the live provider count to 358, leaving 7 STRICT drifts (`PROVIDER_REFERENCE.md`, 4 diagrams, 2 tier-flow images) plus the `AGENTS.md` / `llm.txt` / `package.json` count claims stale. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- **test(models):** Pin `CATALOG_BUILD_TIMEOUT_MS` in the #12058 canonical-catalog regression so a cold catalog build racing the 8s default bound no longer flakes the suite with a 500 ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Allow `@omniroute/opencode-plugin-v2/` in the npm pack policy — [#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870) shipped the v2 plugin beside its v1 sibling without widening the allowlist, so every packed file under it read as an unexpected artifact and failed `check:pack-policy` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Point the release-green validator's pack gate at the tree it is validating — the build-provenance guard defaulted to `origin/main`, which a release branch tip can never be an ancestor of mid-cycle, so `Package artifact` reported a false red on every run and the tarball boot-smoke cascaded off it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **docs(checks):** keep doc counts honest — headings, rankings, catalog, weights, quality gate and scoring diagram now covered ([#12507](https://github.com/diegosouzapw/OmniRoute/pull/12507)) — thanks @maxmad64bis +- **chore(lifecycle):** `check:model-lifecycle` now also diffs `DEFAULT_DEGRADATION_MAP` (the background-task redirect table) against the vendor lifecycle snapshot, refusing a retired id as source or target, with a table-driven unit test beside it. Three rows whose source the vendor had retired — `claude-sonnet-4-20250514`, `gemini-3-pro-preview` and `gpt-5.1-codex` (whose target `gpt-5.1-codex-mini` is retired too) — were dead code, since `checkLifecycle` answers 410 before the redirect runs; they are dropped (#12535 — thanks @pacocartones) +- **test(compression):** cover idle worker eviction at the resource level — the pool must call `terminate()` and must not retain the worker's `MessagePort`, complementing the `exit`-event assertion added with the fix ([#13371](https://github.com/diegosouzapw/OmniRoute/pull/13371)) +- **chore(quality):** raise the `imageGeneration.ts` file-size ceiling for the image-only-model guard that clears the #12945 base-red ([#13392](https://github.com/diegosouzapw/OmniRoute/pull/13392)) +- **chore(quality):** type the combo-test route's JSON response bodies instead of casting them to `any`, drop the now-empty suppression entry, and remove the `hasPerModelQuota` import `chatCore.ts` stopped using when the failure-classification helper was extracted ([#13379](https://github.com/diegosouzapw/OmniRoute/pull/13379)) +- **chore(electron):** the desktop bundle no longer ships the root-level files of every translated docs mirror (`README.md`, `llm.txt`, `CLAUDE.md`, `GEMINI.md`, `CONTRIBUTING.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`) — the packaged app only reads `docs/i18n//docs/**`, which stays. Saves ~11 MB on top of the translated CHANGELOGs already pruned. (#0000) ([#13235](https://github.com/diegosouzapw/OmniRoute/pull/13235)) +- **chore(quality):** retighten the `src/sse/handlers/chat.ts` unused-vars suppression count to the 8 violations that actually remain ([#13385](https://github.com/diegosouzapw/OmniRoute/pull/13385)) +- Bring the documented counts back in line with the code: 171 migrations (was 169) and 20 routing strategies (was 19), plus the regenerated `cli-tunnel` skill reference. ([#12970](https://github.com/diegosouzapw/OmniRoute/pull/12970)) +- Update the documented migration count to 172 after the call-logs provider-stats indexes landed. ([#13209](https://github.com/diegosouzapw/OmniRoute/pull/13209)) +- **chore(ci):** two gates that close the blind spots behind the exclusions above. `check:vitest-exclusions` requires every Vitest exclusion to name a tracking issue and to appear in `config/quality/vitest-exclusions.json` — the previous list grew to 62 files behind a comment pointing at an issue that had been closed for a month. `check-new-key-coverage` requires a key newly added to `en.json` to reach every locale; the existing coverage gate is a percentage floor per locale, so eleven absent keys out of ~13,000 left it at 99.9% while a whole feature shipped untranslated in nine languages. Both are diff-aware, so pre-existing debt stays frozen and neither needed a migration to turn on. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **build:** root layout no longer loads Inter from `next/font/google`, so a production image build does not need fonts.googleapis.com ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **docs(i18n):** bring the eight new `llm.txt` mirrors (km, kn, ml, my, ne, or, pa, si) up to the current provider and migration counts, so `check-docs-sync` stops rejecting every commit ([#13674](https://github.com/diegosouzapw/OmniRoute/pull/13674)) +- **refactor(ui):** flow surfaces (home topology, combo live studio, compression cockpit/waterfall/nodes, token-health badge) express state through the theme-aware `--orch-status-*` tokens instead of fixed dark-mode hex, so green/red/amber/grey stay legible in the light theme; dark mode is byte-identical. Categorical palettes (routing-strategy hues, compression-layer pills, provider brand colors) deliberately stay hex ([#12378](https://github.com/diegosouzapw/OmniRoute/issues/12378)) ([#13203](https://github.com/diegosouzapw/OmniRoute/pull/13203)) +- **chore(tests):** 51 test files that had been excluded from Vitest are running again, restoring roughly 350 assertions to the blocking suite. Each was measured individually first: of the 62 files parked behind the `// #8618 — pre-existing failure` comment, 51 pass against the current tree with no source change, so the exclusions had outlived the failures they were added for. The 11 that genuinely still fail stay excluded, but now point at a live tracker (#13204) rather than at #8618, which was closed in August while the list it tracked kept growing. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **chore(quality):** drop two ESLint suppression entries whose violations no longer exist, so `eslint --suppressions-location` stops rejecting every commit that touches the surrounding files ([#13384](https://github.com/diegosouzapw/OmniRoute/pull/13384)) +- **chore(release):** bring main's post-tag fixes into release/v3.8.51 — sync-back step 2/2 (direct commit `21c488f210`) +- **chore(deps):** patch toml and esbuild in the opencode plugin lockfiles ([#12965](https://github.com/diegosouzapw/OmniRoute/pull/12965)) +- **docs(changelog):** reconcile the v3.8.51 living section — fold 366 fragments, cover every cycle commit, credit every contributor ([#12971](https://github.com/diegosouzapw/OmniRoute/pull/12971)) +- **test(plugin):** wait for the v2 lazy-refresh tier instead of sleeping past it ([#12984](https://github.com/diegosouzapw/OmniRoute/pull/12984)) +- **chore(quality):** rebaseline chatCore.ts after the non-streaming regression fixes ([#13045](https://github.com/diegosouzapw/OmniRoute/pull/13045)) +- **docs(changelog):** add the missing bullet marker to the DeepSeek PoW fragment ([#13200](https://github.com/diegosouzapw/OmniRoute/pull/13200)) +- **chore(deps):** drain the Dependabot queue — 10 of 13 alerts ([#13213](https://github.com/diegosouzapw/OmniRoute/pull/13213)) +- **chore(ci):** make worktrees carry the hooks that enforce the identity gate ([#13236](https://github.com/diegosouzapw/OmniRoute/pull/13236)) +- **chore(deps):** pin csv-parse to ^7.0.2 via overrides (#13117) ([#13300](https://github.com/diegosouzapw/OmniRoute/pull/13300)) — thanks @KooshaPari +- **chore(deps):** bump hono from ^4.12.34 to ^4.13.7 (#13148) ([#13301](https://github.com/diegosouzapw/OmniRoute/pull/13301)) — thanks @KooshaPari +- **chore(deps):** pin joi to ^18.2.8 via overrides (#13085) ([#13302](https://github.com/diegosouzapw/OmniRoute/pull/13302)) — thanks @KooshaPari +- **docs(providers):** add Microsoft 365 Copilot (BizChat) provider guide (#12779) ([#13340](https://github.com/diegosouzapw/OmniRoute/pull/13340)) — thanks @KooshaPari +### 🙌 Contributors + +Thanks to everyone whose work landed in v3.8.51: + +| Contributor | PRs / Issues | +| --- | --- | +| [@5dive-bot](https://github.com/5dive-bot) | #11852 | +| [@abhisheksharma2411](https://github.com/abhisheksharma2411) | #11547, #11684, #11959, #12209 | +| [@adevwithpurpose](https://github.com/adevwithpurpose) | #11464 | +| [@adivekar-utexas](https://github.com/adivekar-utexas) | #12015, #12043, #12090, #12138 | +| [@AIB1TAL0S](https://github.com/AIB1TAL0S) | #12103 | +| [@alltomatos](https://github.com/alltomatos) | #11448 | +| [@alvinveroy](https://github.com/alvinveroy) | #11857, #12027, #12165 | +| [@amaleta](https://github.com/amaleta) | #11906, #11934 | +| [@amartinawi](https://github.com/amartinawi) | #11575 | +| [@AndrianBalanescu](https://github.com/AndrianBalanescu) | #11083, #11830, #11834, #11844 | +| [@anhtahaylove](https://github.com/anhtahaylove) | #13091, #13092, #13093, #13096, #13097, #13100, #13106, #13109, #13114, #13155, #13166, #13171, #13175, #13187, #13196 | +| [@aniruddhaadak80](https://github.com/aniruddhaadak80) | #11760 | +| [@app](https://github.com/app) | #12554 | +| [@arminanton](https://github.com/arminanton) | #11461, #11513 | +| [@AStupidBear](https://github.com/AStupidBear) | #11584 | +| [@atescivitci-cmd](https://github.com/atescivitci-cmd) | #12105 | +| [@b3nw](https://github.com/b3nw) | #11970, #11971, #12147 | +| [@backryun](https://github.com/backryun) | #11259, #11950, #12075, #12076, #12078, #12079, #12081, #12082, #12181, #12228, #12239, #12255, #12258, #12277, #12367, #12423, #12524, #12538 | +| [@benjaminkitt](https://github.com/benjaminkitt) | #11747 | +| [@benzntech](https://github.com/benzntech) | #11614, #11615 | +| [@Bl0ck154](https://github.com/Bl0ck154) | #11948, #11951, #11952, #11953, #11954 | +| [@brick30llc-ctrl](https://github.com/brick30llc-ctrl) | #11752, #12242, #12262 | +| [@Chewji9875](https://github.com/Chewji9875) | #11455, #11875, #12028 | +| [@cheynetom](https://github.com/cheynetom) | #12058 | +| [@davidebaraldo](https://github.com/davidebaraldo) | #12222, #12304 | +| [@davidlinfr](https://github.com/davidlinfr) | #6390 | +| [@ddarkr](https://github.com/ddarkr) | #12124 | +| [@Deftera186](https://github.com/Deftera186) | #11809 | +| [@dpozimski](https://github.com/dpozimski) | #11945 | +| [@DW-MediaLab](https://github.com/DW-MediaLab) | #12229 | +| [@echel0nn](https://github.com/echel0nn) | #11923 | +| [@f9td56dbgh-hub](https://github.com/f9td56dbgh-hub) | #11660 | +| [@fabioluissilva](https://github.com/fabioluissilva) | #11991 | +| [@foreveryh](https://github.com/foreveryh) | #12177 | +| [@geek007git](https://github.com/geek007git) | #12115, #12116, #12117, #12120, #12122 | +| [@geekyNads](https://github.com/geekyNads) | #11784, #12266 | +| [@ggiak](https://github.com/ggiak) | #11561, #12276, #12350, #12402 | +| [@gonisulaimann](https://github.com/gonisulaimann) | #12368, #12369, #12371 | +| [@Gorillaz322](https://github.com/Gorillaz322) | #12207 | +| [@groovecityJO](https://github.com/groovecityJO) | #12682 | +| [@hartmark](https://github.com/hartmark) | #11434, #11452, #11473, #11499, #11703, #11983, #11984, #11985, #11986, #11988, #11989, #11990, #11994, #12221, #12293, #12445, #12446, #12447, #12448, #12460, #12461, #12623, #12646, #12650, #12680, #12717, #12718, #12727, #12741, #12854 | +| [@hizzt](https://github.com/hizzt) | #11894 | +| [@hongnoul](https://github.com/hongnoul) | #11484 | +| [@HouMinXi](https://github.com/HouMinXi) | #11411, #11414, #11512, #11518, #11520, #11641, #11642, #11643, #11687, #11779, #11849, #11850, #11851, #11915, #11916, #11918, #11919, #11920, #12013, #12017, #12033, #12042, #12106, #12139, #12166, #12169, #12171, #12205, #12213, #12312, #12325, #12487, #12488, #12495, #12504, #12557, #12566, #12590, #12591, #12624, #12626, #12632, #12637, #12678, #12696, #12697, #12711, #12733, #12746, #12767, #12770, #12789, #12803, #12805, #12811, #12866, #12868, #12899, #12926, #12934, #12950, #12951, #12974, #13001, #13006, #13011, #13017, #13026, #13027, #13034, #13035, #13038, #13042, #13050, #13060, #13061, #13069, #13107, #13120, #13136, #13195, #13197 | +| [@Hsia97](https://github.com/Hsia97) | #11624 | +| [@jacobsparts](https://github.com/jacobsparts) | #11854, #12155, #12167 | +| [@joglomedia](https://github.com/joglomedia) | #11980 | +| [@jonlwheat2-gif](https://github.com/jonlwheat2-gif) | #11443, #11450, #11549, #11567, #11608, #11633, #11635, #11640, #11644, #11671, #11672, #11673, #11674, #11675, #11676, #11677, #11706, #11783, #12051, #12052, #12053, #12055 | +| [@kanade-hoshino](https://github.com/kanade-hoshino) | #12180 | +| [@Karan825](https://github.com/Karan825) | #11861 | +| [@KaspaPulse](https://github.com/KaspaPulse) | #11389, #11469, #11628, #11666, #11910 | +| [@keeltrace](https://github.com/keeltrace) | #12080, #12223 | +| [@killer30001000](https://github.com/killer30001000) | #12178 | +| [@KooshaPari](https://github.com/KooshaPari) | #12470, #12592, #12667, #12699, #12703, #12706, #12764, #12769, #12771, #13300, #13301, #13302, #13340, #13401, #13403, #13404, #13405, #13406, #13407, #13409, #13410, #13411, #13412, #13413, #13414, #13418, #13424, #13427, #13433, #13523, #13524, #13525, #13528, #13531, #13532, #13534, #13539, #13543, #13545, #13546, #13547, #13550, #13553 | +| [@kriptoburak](https://github.com/kriptoburak) | #11370 | +| [@KrzysiekSko](https://github.com/KrzysiekSko) | #12425, #12432, #12673 | +| [@marcelokarval](https://github.com/marcelokarval) | direct commit / report | +| [@maxmad64bis](https://github.com/maxmad64bis) | #1622, #11435, #11437, #11441, #11537, #11550, #11553, #11555, #11557, #11812, #11842, #11843, #11903, #12151, #12214, #12215, #12218, #12226, #12314, #12316, #12317, #12318, #12319, #12320, #12321, #12507, #12715, #12731, #12744, #12786, #12787, #12788, #12790, #12792, #12794, #12795, #12828, #12832, #12853, #12857, #12870, #12937, #12941, #12975, #13141, #13142, #13146, #13147 | +| [@mdigitalbh81](https://github.com/mdigitalbh81) | #12240 | +| [@Meet6338-X](https://github.com/Meet6338-X) | #11598, #11609, #12110 | +| [@morpheus9393](https://github.com/morpheus9393) | #11943 | +| [@MumuTW](https://github.com/MumuTW) | #11492, #11502, #11506, #11507, #11626, #11675, #11685, #11728, #11746, #11888, #11889, #11890, #11892 | +| [@Neuron-Mr-White](https://github.com/Neuron-Mr-White) | #11622, #11801, #12256 | +| [@NightStalker-87](https://github.com/NightStalker-87) | #12183 | +| [@NoxzRCW](https://github.com/NoxzRCW) | #11879, #11880, #11881, #11882, #11883, #11935 | +| [@ntdat812](https://github.com/ntdat812) | #11585, #12095 | +| [@ntdatt812](https://github.com/ntdatt812) | #11368, #11573, #11574, #11576, #11577, #11580, #11582, #11583, #11588, #11589, #11590, #11591, #11592, #11593, #11672, #12177, #12180, #12873, #12918, #12920, #12921, #12925, #12930, #13007, #13009, #13024, #13025, #13083, #13087, #13101, #13104, #13110 | +| [@official-burak](https://github.com/official-burak) | #11542 | +| [@opensource-elearning](https://github.com/opensource-elearning) | #12179, #12189, #12278, #12286 | +| [@oyi77](https://github.com/oyi77) | #11408, #11409, #11421, #11505, #11677, #12036, #12110 | +| [@pacocartones](https://github.com/pacocartones) | #11521, #11522, #11527, #11528, #11529, #11530, #11531, #11532, #11533, #11534, #11595, #11599, #11603, #11604, #11605, #11607, #11610, #11676, #11714, #11716, #11718, #11767, #11838, #11860, #11862, #11869, #11871, #11872, #11873, #11903, #11906, #11921, #11934, #12356, #12358, #12359, #12360, #12361, #12362, #12364, #12365, #12373, #12374, #12375, #12376, #12377, #12379, #12380, #12381, #12386, #12387, #12389, #12390, #12394, #12395, #12397, #12401, #12403, #12404, #12522, #12523, #12535, #12536, #12540, #12541, #12543, #12545, #12548, #12549, #12550, #12551, #12552, #12647, #12651, #12653, #13055 | +| [@patrykkopycinski](https://github.com/patrykkopycinski) | #11936, #11937, #12224 | +| [@PauloFH](https://github.com/PauloFH) | #11509 | +| [@PauloHSOliveira](https://github.com/PauloHSOliveira) | #12241 | +| [@ponkcore](https://github.com/ponkcore) | #12054 | +| [@Prajeeth-12](https://github.com/Prajeeth-12) | #11634, #12046 | +| [@quiterunner-commits](https://github.com/quiterunner-commits) | #12131, #12143 | +| [@rafacpti23](https://github.com/rafacpti23) | #11554, #11558, #12192, #12197, #12198, #12202, #12203, #12204 | +| [@ragnar-claude](https://github.com/ragnar-claude) | #11564 | +| [@raheemuddin786](https://github.com/raheemuddin786) | #11491, #11839, #11840, #11841, #12003, #12230, #12231, #12232, #12233, #12234 | +| [@rahilmavani](https://github.com/rahilmavani) | #11761 | +| [@Rahulsharma0810](https://github.com/Rahulsharma0810) | #11771 | +| [@RaviTharuma](https://github.com/RaviTharuma) | #11710, #11727, #11797, #11798, #11802, #11805, #11806, #11811, #12098, #12099, #12101, #12449, #12452, #12472, #12473, #12493, #12533, #12607, #12628, #12631, #12636, #12875, #12876, #12880, #12882, #12884 | +| [@rezjalibd](https://github.com/rezjalibd) | #12186 | +| [@RhianB14](https://github.com/RhianB14) | #11692 | +| [@rifqiawl](https://github.com/rifqiawl) | #11517, #11519, #11969 | +| [@rqzbeh](https://github.com/rqzbeh) | #11390 | +| [@Sabeekhann](https://github.com/Sabeekhann) | #11699, #11705 | +| [@santosraju99-hub](https://github.com/santosraju99-hub) | #11755, #11770 | +| [@Seramicx](https://github.com/Seramicx) | #11568 | +| [@Siva010](https://github.com/Siva010) | #12191 | +| [@solstxce](https://github.com/solstxce) | #11597 | +| [@soroush5](https://github.com/soroush5) | #12691 | +| [@tenshiak](https://github.com/tenshiak) | #12279 | +| [@TheDemonTuan](https://github.com/TheDemonTuan) | #11468, #11470, #11471, #11482, #11548, #11758, #11775, #11814 | +| [@thomasmaerz](https://github.com/thomasmaerz) | #12834 | +| [@tuandinh0801](https://github.com/tuandinh0801) | #11454 | +| [@turbolego](https://github.com/turbolego) | #11621, #11762, #11772, #11774, #11781, #12216 | +| [@Tushar49](https://github.com/Tushar49) | #11392 | +| [@ujjawalkaushik1110](https://github.com/ujjawalkaushik1110) | #11863 | +| [@vermasomesh835](https://github.com/vermasomesh835) | #11794 | +| [@vsd2807](https://github.com/vsd2807) | #11565, #11619 | +| [@wahidsadik371-coder](https://github.com/wahidsadik371-coder) | #12126 | +| [@watchingdogs](https://github.com/watchingdogs) | #12031 | +| [@wildcard](https://github.com/wildcard) | #11620 | +| [@xiaoyaner0201](https://github.com/xiaoyaner0201) | #11460, #11662, #12083 | +| [@Xxx91n](https://github.com/Xxx91n) | #11690 | +| [@yourspraveen](https://github.com/yourspraveen) | #11146 | +| [@yxyxy](https://github.com/yxyxy) | #11671 | +| [@ZaimMarzuki](https://github.com/ZaimMarzuki) | #11960 | +| [@Zartharas](https://github.com/Zartharas) | #11340 | +| [@zero-executioner](https://github.com/zero-executioner) | #11631 | +| [@diegosouzapw](https://github.com/diegosouzapw) | maintainer | + +--- ## [3.8.50] — 2026-08-25 _Living section — regenerated 2026-08-12 from all cycle commits (cycle open `ed2db6cb19` → tip). Bullets carry the merged PR and its author; direct pushes listed separately._ diff --git a/docs/i18n/my/CHANGELOG.md b/docs/i18n/my/CHANGELOG.md index 26550937fe..fcd6f95637 100644 --- a/docs/i18n/my/CHANGELOG.md +++ b/docs/i18n/my/CHANGELOG.md @@ -96,31 +96,31 @@ ## [3.8.51] — TBD -_Living section — reconciled 2026-09-07 from all cycle commits (`release/v3.8.50` tip `091589089c` → `release/v3.8.51` tip `d6f315018a`, 696 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._ +_Living section — reconciled 2026-09-15 from all cycle commits (`091589089c` → `c0f92ec98a`, 916 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 | **120** | -| 📝 Commits in the cycle | **696** | -| 🔀 Pull requests referenced | **672** | -| 📋 Changelog entries | **685** | -| 🙌 Contributors credited in entries | **118** | +| 👥 People who contributed | **123** | +| 📝 Commits in the cycle | **916** | +| 🔀 Pull requests referenced | **892** | +| 📋 Changelog entries | **919** | +| 🙌 Contributors credited in entries | **121** | | 🤖 Automated dependency commits | 18 | **Entries by type** | Type | Count | | --- | ---: | -| 🐛 Fixes | 403 | -| ✨ Features | 118 | -| 🧹 Chore | 72 | -| 📚 Docs | 36 | -| 🧪 Tests | 29 | +| 🐛 Fixes | 592 | +| ✨ Features | 131 | +| 🧹 Chore | 93 | +| 📚 Docs | 42 | +| 🧪 Tests | 32 | +| ♻️ Refactor | 8 | +| 🏗️ Build | 7 | | ⚡ Performance | 7 | -| ♻️ Refactor | 7 | -| 🏗️ Build | 6 | | ⚙️ CI | 2 | | ⏪ Reverts | 2 | | 📦 Dependencies | 2 | @@ -128,35 +128,35 @@ _Living section — reconciled 2026-09-07 from all cycle commits (`release/v3.8. ### 🏆 Top 25 contributors this cycle -_By commits in `release/v3.8.50..release/v3.8.51` (`091589089c..d6f315018a`), author identities consolidated via `.mailmap` and the merged PR's GitHub login. Bots excluded._ +_By commits in `091589089c..c0f92ec98a`, author identities consolidated via `.mailmap` and the merged PR's GitHub login. Bots excluded._ | # | Contributor | Commits | | ---: | --- | ---: | -| 🥇 | diegosouzapw | 240 | -| 🥈 | Paco Cartones (@pacocartones) | 58 | -| 🥉 | Bob.Hou (@HouMinXi) | 57 | -| 4 | Dizzle (@maxmad64bis) | 27 | -| 5 | Webman (@jonlwheat2-gif) | 22 | -| 6 | Ravi Tharuma (@RaviTharuma) | 21 | -| 7 | backryun | 17 | -| 8 | Markus Hartung (@hartmark) | 17 | -| 9 | Nguyen Thanh Dat (@ntdatt812) | 16 | -| 10 | Rafa Martins (@rafacpti23) | 10 | -| 11 | Syed Raheemuddin (@raheemuddin786) | 10 | -| 12 | Koosha Paridehpour (@KooshaPari) | 9 | -| 13 | Nguyễn Viết Tuấn (@TheDemonTuan) | 8 | -| 14 | MumuTW | 7 | -| 15 | Paijo (@oyi77) | 7 | -| 16 | Tobias Andersen (@turbolego) | 6 | -| 17 | Bl0ck (@Bl0ck154) | 5 | -| 18 | SHANMUGAPRIYAN (@geek007git) | 5 | -| 19 | KaspaPulse | 5 | -| 20 | Abhishek Sharma (@abhisheksharma2411) | 4 | -| 21 | Abhishek Divekar (@adivekar-utexas) | 4 | -| 22 | Andrew B. (@AndrianBalanescu) | 4 | -| 23 | NoxzRCW | 4 | -| 24 | opensource-elearning | 4 | -| 25 | Alvin T. Veroy (@alvinveroy) | 3 | +| 🥇 | diegosouzapw | 323 | +| 🥈 | Bob.Hou (@HouMinXi) | 79 | +| 🥉 | Paco Cartones (@pacocartones) | 77 | +| 4 | Dizzle (@maxmad64bis) | 49 | +| 5 | Koosha Paridehpour (@KooshaPari) | 43 | +| 6 | Nguyen Thanh Dat (@ntdatt812) | 31 | +| 7 | Ravi Tharuma (@RaviTharuma) | 26 | +| 8 | Markus Hartung (@hartmark) | 22 | +| 9 | Webman (@jonlwheat2-gif) | 22 | +| 10 | backryun | 17 | +| 11 | anhtahaylove | 15 | +| 12 | Rafa Martins (@rafacpti23) | 10 | +| 13 | Syed Raheemuddin (@raheemuddin786) | 10 | +| 14 | Nguyễn Viết Tuấn (@TheDemonTuan) | 8 | +| 15 | MumuTW | 7 | +| 16 | Paijo (@oyi77) | 7 | +| 17 | Tobias Andersen (@turbolego) | 6 | +| 18 | Bl0ck (@Bl0ck154) | 5 | +| 19 | SHANMUGAPRIYAN (@geek007git) | 5 | +| 20 | KaspaPulse | 5 | +| 21 | Abhishek Sharma (@abhisheksharma2411) | 4 | +| 22 | Abhishek Divekar (@adivekar-utexas) | 4 | +| 23 | Andrew B. (@AndrianBalanescu) | 4 | +| 24 | NoxzRCW | 4 | +| 25 | opensource-elearning | 4 | ### ✨ New Features @@ -282,6 +282,19 @@ _By commits in `release/v3.8.50..release/v3.8.51` (`091589089c..d6f315018a`), au - **feat(dashboard):** link a log entry's Conversation Context to its owning conversation ([#12646](https://github.com/diegosouzapw/OmniRoute/pull/12646)) — thanks @hartmark - **feat(catalog):** eligibility-gated free-tier bucket ([#12669](https://github.com/diegosouzapw/OmniRoute/pull/12669)) - **feat(db):** fail-closed previous_response_id continuation for redacted video turns (#12150 P2b) ([#12707](https://github.com/diegosouzapw/OmniRoute/pull/12707)) +- **feat(gamification): pay the documented `streak_bonus` and `badge_unlock` XP rewards.** `XP_REWARDS` listed both rewards but the award pipeline never paid them: the private reward table in `events.ts` omitted them, `updateStreak()` did not report when a streak extended, and badge unlocks carried no XP. Every request that extends a daily streak now pays `streak_bonus × streak length` once per UTC day (guarded by a same-day `xp_audit_log` check), and every badge unlocked through the pipeline pays `badge_unlock` once per badge (guarded by the `user_badges` primary key; `unlockBadge()` now reports whether it inserted). Bonus XP flows through the same `addXp` + level sync + global/weekly/monthly leaderboard path as action XP, so level-ups and rankings include it. The Radar supporter recognition unlock stays XP-free. (#12522 — thanks @pacocartones) +- **feat(providers):** advertise a `free-tier` capability in the provider plugin manifest for every provider with documented free models, so sidecars and dashboards can filter free-capable providers without reading the quota catalog ([#12786](https://github.com/diegosouzapw/OmniRoute/pull/12786)) — thanks @maxmad64bis +- **feat(providers):** Added EURouter as an OpenAI-compatible API-key gateway (`https://api.eurouter.ai/v1`), with live model discovery via `passthroughModels`. Its copy states that models are served by third-party upstreams listed per model, so an EU-based router is not read as EU data residency for inference. ([#13025](https://github.com/diegosouzapw/OmniRoute/pull/13025)) — thanks @ntdatt812 +- **feat(providers):** Added GreenPT as an OpenAI-compatible API-key provider (`https://api.greenpt.ai/v1`), with live model discovery via `passthroughModels`. No free-inference badge: the published docs describe a free API subscription billed per token, not a free tier. ([#13024](https://github.com/diegosouzapw/OmniRoute/pull/13024)) — thanks @ntdatt812 +- **feat(i18n):** 9 new locales — Greek (`el`), Croatian (`hr`), Serbian (`sr`), Lithuanian (`lt`), Estonian (`et`), Latvian (`lv`), Slovenian (`sl`), Maltese (`mt`), Irish (`ga`) — across the dashboard, docs mirrors, CLI, README and the site; OmniRoute now ships every official EU language (51 locales). (#13044) +- **feat(i18n):** 8 new locales — Kannada (`kn`), Malayalam (`ml`), Odia (`or`), Punjabi (`pa`), Nepali (`ne`), Sinhala (`si`), Burmese (`my`), Khmer (`km`) — across the dashboard, docs mirrors, CLI, README and the site (59 locales). The translator now restores the ICU literal escape around angle placeholders and splits oversized docs sections before translating. (#0000) ([#13660](https://github.com/diegosouzapw/OmniRoute/pull/13660)) +- feat(providers): **list Agnes 3.0 Flash as the current free chat model, drop retired 1.5 Flash, add Image 2.0/2.5 Flash plus Video 2.5/2.5 Flash, and discover the live `/v1/models` catalog (including the CN host `api.agnes-ai.cn`).** `agnes-1.5-flash` now forwards to `agnes-3.0-flash`. Video 2.5 polls `GET /v1/videos/{id}` (not the V2.0 `/agnesapi` contract). Live `/v1/models` (2026-09-09) no longer serves 1.5; the wiki marks it deprecated. 3.0 Flash is 512K context / 65,536 max output, same window as 2.5. CN-region keys use the existing per-connection base-URL field, default stays `apihub.agnes-ai.com`. ([#13120](https://github.com/diegosouzapw/OmniRoute/pull/13120)) — thanks @HouMinXi +- **feat(sse):** Codex and OpenAI catalogs list GPT-6 Astra with effort aliases (`-low` through `-ultra`); Codex CLI identity pins `@openai/codex@0.153.4` in lockstep with the image ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **feat(dashboard):** the orchestration History tab gained a "Compare runs" mode — toggling it turns each grid cell into a 2-item selection queue (a 3rd click drops the oldest pick), and picking two cells opens a side-by-side comparison panel instead of the usual detail drawer. The panel fetches both runs' detail the same way the drawer does (falling back to persisted history once a run leaves the live TTL window) and shows, per side: identity/source/state, start time, a signed `right - left` delta for duration/cost/event count, the event timeline aligned by index, and any memory hits. One side's fetch failing never blocks the other, and a delta is only ever computed when both sides have a finite value — otherwise it renders "—", never `NaN`. Comparing two runs from different sources or skills still works; a banner marks the deltas as informational rather than hiding them, since the two runs aren't a strict apples-to-apples pair. ([#12677](https://github.com/diegosouzapw/OmniRoute/pull/12677)) +- **feat(dashboard):** badge a conversation that never reached a clean stop ([#12717](https://github.com/diegosouzapw/OmniRoute/pull/12717)) — thanks @hartmark +- **feat(release):** reconcile-changelog tool + version-anchored fragment aggregation ([#12987](https://github.com/diegosouzapw/OmniRoute/pull/12987)) +- **feat(mcp):** carry context_length through MCP list_models_catalog projection (#12776) ([#13406](https://github.com/diegosouzapw/OmniRoute/pull/13406)) — thanks @KooshaPari +- **feat(cli):** make startup readiness budget configurable (#13369) ([#13433](https://github.com/diegosouzapw/OmniRoute/pull/13433)) — thanks @KooshaPari ### 🐛 Bug Fixes @@ -698,6 +711,194 @@ _By commits in `release/v3.8.50..release/v3.8.51` (`091589089c..d6f315018a`), au - **fix(i18n):** quote placeholder in OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES description (#12505) ([#12769](https://github.com/diegosouzapw/OmniRoute/pull/12769)) — thanks @KooshaPari - **fix(api/pricing):** surface validation error message as string, not raw object (#12494) ([#12771](https://github.com/diegosouzapw/OmniRoute/pull/12771)) — thanks @KooshaPari - **fix(sse):** re-enable prompt compression for native Codex passthrough ([#12834](https://github.com/diegosouzapw/OmniRoute/pull/12834)) — thanks @thomasmaerz +- **fix(dashboard):** model health tests for a provider node set to the Responses API now call `/v1/responses` with a Responses-shaped body instead of `/v1/chat/completions` — those models were reported as `Provider returned HTTP 200 but no text content` even though the same model answered normally through `/v1/responses` ([#13070](https://github.com/diegosouzapw/OmniRoute/issues/13070)) ([#13087](https://github.com/diegosouzapw/OmniRoute/pull/13087)) — thanks @ntdatt812 +- fix(routing): stop a round-robin combo's "opencode" targets from collapsing onto the opencode-zen connection (#11912) ([#13283](https://github.com/diegosouzapw/OmniRoute/pull/13283)) +- fix(routing): stop the reactive-compaction debug log from lying when compression is globally disabled (#11977) ([#13276](https://github.com/diegosouzapw/OmniRoute/pull/13276)) +- fix(electron): relativize standalone-bundle symlink targets so Stage 8 manifest verification stops failing on Windows (#11979) ([#13251](https://github.com/diegosouzapw/OmniRoute/pull/13251)) +- fix(dashboard): surface a visible error when Compression Studio's combined preview run fails (#12061) ([#13288](https://github.com/diegosouzapw/OmniRoute/pull/13288)) +- fix(dashboard): make the compression "Effective pipeline" preview honor the active profile and warn when the master switch is off (#12063) ([#13287](https://github.com/diegosouzapw/OmniRoute/pull/13287)) +- fix(providers): scope TinyCMS Web signer's DOM shims to each call instead of leaking them for the process lifetime, and surface a clean HTTP status on a non-JSON interception-toggles error (#12072) ([#13254](https://github.com/diegosouzapw/OmniRoute/pull/13254)) +- fix(guardrails): stop Vision Bridge from re-selecting a model locked after a 404 (#12111) ([#13259](https://github.com/diegosouzapw/OmniRoute/pull/13259)) +- fix(sse): require Responses-shaped body before native OpenAI-compatible passthrough (#12129) ([#13278](https://github.com/diegosouzapw/OmniRoute/pull/13278)) +- fix(providers): minimax-m3 now collapses manual thinking.type:"enabled" to adaptive, preventing upstream 400 (2013) (#12132) ([#13249](https://github.com/diegosouzapw/OmniRoute/pull/13249)) +- fix(db): scope model visibility overrides by modality so hiding a Chat model no longer hides an identically-ID'd Image/Embeddings/etc. model (#12172) ([#13258](https://github.com/diegosouzapw/OmniRoute/pull/13258)) +- fix(db): scope local-provider apiKey dedup to matching base URL so LM Studio/Ollama-style connections support multiple accounts (#12173) ([#13250](https://github.com/diegosouzapw/OmniRoute/pull/13250)) +- fix(providers): refresh Trae's stale Referer/Origin and forward user timezone so imported connections stop failing with 401 (#12190) ([#13255](https://github.com/diegosouzapw/OmniRoute/pull/13255)) +- fix(providers): route opencode-go/gpt-5.6-luna to /responses instead of /chat/completions (#12196) ([#13275](https://github.com/diegosouzapw/OmniRoute/pull/13275)) +- fix(dashboard): allow deleting the last extra-upstream-header row even when invalid (#12251) ([#13252](https://github.com/diegosouzapw/OmniRoute/pull/13252)) +- **fix(i18n):** translate pre-existing `__MISSING__:` keys for `combo.sort`, `requestLogger.detail` expand/collapse, `common.profile`, and `settings.resilienceCredentialHealth*` across 39 locales ([#12272](https://github.com/diegosouzapw/OmniRoute/issues/12272)) ([#13034](https://github.com/diegosouzapw/OmniRoute/pull/13034)) — thanks @HouMinXi +- fix(cli): run the Node.js runtime compatibility guard before the heavy `tsx/esm` + Commander import chain so an unsupported runtime gets a clear message instead of a raw `Invalid regular expression flags` crash (#12296) ([#13253](https://github.com/diegosouzapw/OmniRoute/pull/13253)) +- fix(dashboard): refresh the providers list after deleting a compatible provider node (#12298) ([#13256](https://github.com/diegosouzapw/OmniRoute/pull/13256)) +- fix(usage): fail closed on API-key budget enforcement when a provider's `auto` routing alias has no pricing row, instead of silently counting it as $0 (#12341) ([#13257](https://github.com/diegosouzapw/OmniRoute/pull/13257)) +- **fix(providers):** include the submitted Agnes video model when polling by `video_id` ([#12356](https://github.com/diegosouzapw/OmniRoute/pull/12356)) — thanks @pacocartones +- **fix(routing):** custom OpenAI-compatible nodes now honor the saved Chat/Responses API type after edits instead of letting the node's original ID prefix override the live connection setting ([#11884](https://github.com/diegosouzapw/OmniRoute/issues/11884)). ([#12358](https://github.com/diegosouzapw/OmniRoute/pull/12358)) — thanks @pacocartones +- fix(sse): surface an error instead of a silent empty 200 when a Claude stream closes with zero bytes (#12398) ([#13285](https://github.com/diegosouzapw/OmniRoute/pull/13285)) +- fix(oauth): warn before the dead localhost:8080 redirect in antigravity/gemini `oauth start` (#12413) ([#13265](https://github.com/diegosouzapw/OmniRoute/pull/13265)) +- fix(providers): stop devin-cli spawn error from double-closing the SSE controller (#12517) ([#13261](https://github.com/diegosouzapw/OmniRoute/pull/13261)) +- **fix(rerank):** clamp Voyage `top_k` to the documents actually sent after empty-string filtering, and honor `return_documents: false` in the NVIDIA response adapter (#12523 — thanks @pacocartones) +- **fix(audio):** `/v1/audio/translations` now resolves combo names the way `/v1/audio/transcriptions` already does, so a combo that `GET /v1/models` advertises is fanned out to its targets instead of being rejected with `400 Invalid translation model: . Use format: provider/model`; literal `provider/model` ids and unknown bare names behave as before (#12536 — thanks @pacocartones) +- **fix(gemini):** strip the JSON-Schema-2020-12 `prefixItems` keyword from Gemini tool schemas at every nesting level, so Claude Code tool definitions no longer fail with `400 Unknown name "prefixItems"` on Gemini models (#12540 — thanks @pacocartones) +- **fix(api-manager):** Expose an accessible loading status while API keys are fetched instead of an empty accessibility tree (#12541 — thanks @pacocartones) +- **fix(video):** Clamp `estimateJpegFrameBytes` at zero for padding-only payloads and build the three encode-side frame data URIs from `JPEG_FRAME_DATA_URI_PREFIX` instead of a repeated literal (#12543 — thanks @pacocartones) +- **fix(devin):** accept Windows `DEVIN_AGENTIC_HOME` sandbox paths (`C:\...\.sandbox\...`) in the isolated-home check so the Devin Claude Bridge no longer fails closed on Windows ([#12405](https://github.com/diegosouzapw/OmniRoute/issues/12405)) (#12545 — thanks @pacocartones) +- **fix(db):** Add `conversation_turn_nodes` and orphaned `agentic_conversations` to the auto-cleanup cycle under the existing `retention.callLogs` window, so identity nodes whose call-log content has already been purged no longer accumulate without bound in `storage.sqlite` (#12548 — thanks @pacocartones) +- **fix(i18n):** Wrap the `~/.claude/profiles//settings.json` placeholder in ICU single quotes in the `OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES` feature-flag description across all 42 locales and the TypeScript default, so next-intl no longer fails with `INVALID_MESSAGE: UNCLOSED_TAG` and the Feature Flags card shows the description instead of the raw key (#12549 — thanks @pacocartones) +- **fix(orchestration):** `updateCloudAgentTask` now publishes the task's real `status` on `agent.task.updated` when an update only touches `result`, `activities` or `error`, instead of the fabricated `"updated"` state, and stays silent when no row matched the id (#12550 — thanks @pacocartones) +- **fix(i18n):** the home "Recent Requests" panel and the Provider Topology legend are now translated instead of rendering English copies on non-English dashboards; the legend reads its own `home.topologyLegend*` labels with consistent casing rather than borrowing the memory-settings "Recent" and analytics "Error" strings (#12551 — thanks @pacocartones). +- **docs(reference):** bring the `FEATURE_FLAGS.md` catalog back to 1:1 with `featureFlagDefinitions.ts` — 20 missing flags added, the two `*_BLOCK_THRESHOLD` env-only knobs moved out of the flag tables, category/total counts and the Live WS port corrected, guarded by a static test (#12552 — thanks @pacocartones) +- **fix(i18n):** backfill missing `usage.kiloPass*` strings in 39 locales and restore `featureFlagOmnirouteDisableThinkingLevelVariantsDescription` in `pt.json` ([#12561](https://github.com/diegosouzapw/OmniRoute/issues/12561)) ([#13027](https://github.com/diegosouzapw/OmniRoute/pull/13027)) — thanks @HouMinXi +- fix(docker): default docker-compose app ports (dashboard/API/live-WS) to loopback instead of `0.0.0.0`, closing the anonymous `/v1` LAN/WAN exposure gap left open by `REQUIRE_API_KEY=false` (#12568) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): close DNS-rebinding SSRF gap in webhook outbound-URL guard (#12569) ([#13243](https://github.com/diegosouzapw/OmniRoute/pull/13243)) +- fix(docker): require a per-session token on the VNC browser CDP bridge and isolate it on a dedicated Docker network (#12571) ([#13241](https://github.com/diegosouzapw/OmniRoute/pull/13241)) +- fix(open-sse): write Adobe Firefly session tokens and cookie jars with 0700/0600 permissions instead of the process umask (#12572) ([#13242](https://github.com/diegosouzapw/OmniRoute/pull/13242)) +- fix(api): remove hardcoded wildcard CORS in openai-to-gemini-sse.ts so the centralized fail-closed CORS gate is the sole source of `Access-Control-Allow-Origin` (#12573) ([#13239](https://github.com/diegosouzapw/OmniRoute/pull/13239)) +- fix(api): enforce API key policy (budget/rate-limit/schedule/endpoint scoping) on the ElevenLabs speech-to-text, text-to-speech and voices proxy routes (#12574) ([#13244](https://github.com/diegosouzapw/OmniRoute/pull/13244)) +- fix(sse): cap HuggingChat NDJSON body size and bound the read loop with the fetch timeout so a stalled or hostile upstream cannot buffer unbounded memory (#12577) ([#13282](https://github.com/diegosouzapw/OmniRoute/pull/13282)) +- fix(docker): scope the cliproxyapi/qdrant/bifrost sidecars to loopback by default and forward `CLIPROXYAPI_MANAGEMENT_KEY` into the cliproxyapi container so its management API is not left both unauthenticated and LAN-published (#12578) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): create DB export temp paths with `fs.mkdtempSync` instead of predictable timestamps (#12579) ([#13237](https://github.com/diegosouzapw/OmniRoute/pull/13237)) +- Cline 401 bodies that say "re-authenticate your Cline account" classify as a refreshable OAuth token, not a terminal expired key. The cooling panel no longer labels every cooldown as a 429; it shows the recorded last error instead. (#12594) ([#13060](https://github.com/diegosouzapw/OmniRoute/pull/13060)) — thanks @HouMinXi +- **fix(catalog):** degrade unknown combo targets instead of dropping LCD modalities, and persist OpenRouter `architecture.input_modalities` into the capability snapshot ([#12613](https://github.com/diegosouzapw/OmniRoute/issues/12613)) ([#13042](https://github.com/diegosouzapw/OmniRoute/pull/13042)) — thanks @HouMinXi +- fix(providers): send `x-api-key` instead of `Authorization: Bearer` for OpenCode Zen's `/v1/responses` endpoint (Muse Spark Contributor models), fixing a 401 on OmniRoute's auth header (#12633) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(sse): surface the actionable "Auggie CLI not found" message when the shell reports a missing `auggie` binary via exit code instead of a spawn error (#12645) ([#13263](https://github.com/diegosouzapw/OmniRoute/pull/13263)) +- **fix(config):** Persist deletions of built-in background-degradation entries — when a stored settings record exists its `degradationMap` is now authoritative instead of being merged under the defaults, so an entry the user removed in the dashboard no longer reappears on the next apply or restart ([#12424](https://github.com/diegosouzapw/OmniRoute/issues/12424)) ([#12647](https://github.com/diegosouzapw/OmniRoute/pull/12647)) — thanks @pacocartones +- **fix(gamification):** action-count badge milestones (First Token, Token Consumer, Token Machine, Token Whale, and the token-sharing tier) are now backed by a durable `xp_action_counts` counter incremented in `addXp()`, instead of a live `COUNT(*)` over `xp_audit_log`. The audit log is pruned by `retention.xpAuditLog` (default 30 days), so on a default install those "lifetime" milestones were really "actions in the last 30 days" and unlocked badges could stop unlocking once old rows aged out. `getActionCount()` and `checkActionCountBadges()` now read the same durable source, and a migration backfills existing totals from the surviving audit rows ([#12546](https://github.com/diegosouzapw/OmniRoute/issues/12546)) ([#12651](https://github.com/diegosouzapw/OmniRoute/pull/12651)) — thanks @pacocartones +- **fix(images):** `/v1/images/edits` now iterates a combo's targets the same way `/v1/images/generations` does (#9239) instead of flattening a bare combo to its first target. A combo whose first target is not edit-capable — or lacks credentials — now falls through to a later edit-capable target rather than hard-erroring, and missing credentials are skipped (not a hard `401`) to match the generations path. The per-target skip/terminal classification is extracted into a shared `runImageComboTargets` loop, so generations behavior is unchanged ([#12547](https://github.com/diegosouzapw/OmniRoute/issues/12547)). ([#12653](https://github.com/diegosouzapw/OmniRoute/pull/12653)) — thanks @pacocartones +- fix(sse): add first-byte watchdog to the TLS-fingerprint transport so a stalled wreq body falls back instead of hanging for minutes (#12656) ([#13272](https://github.com/diegosouzapw/OmniRoute/pull/13272)) +- fix(sse): exempt tiny-budget reasoning probes from combo quality failure and surface persisted-cooldown skips in ALL_TARGETS_SKIPPED diagnostics (#12659) ([#13269](https://github.com/diegosouzapw/OmniRoute/pull/13269)) +- fix(models): declare the real ~1M contextLength for OpenCode Zen's Muse Spark 1.2 models instead of falling back to the 200000 provider default (#12681) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(oauth): align codebuddy-cn OAuth User-Agent with the chat/usage CLI version to avoid WAF false positives (#12702) ([#13264](https://github.com/diegosouzapw/OmniRoute/pull/13264)) +- fix(dashboard): surface an authentication-required banner instead of silently blanking database settings for a guest session (#12709) ([#13270](https://github.com/diegosouzapw/OmniRoute/pull/13270)) +- **fix(resilience):** stop unbounded queue that hangs 6min until Aborted — gate, provider slot, and Bottleneck queue now share a per-connection `maxWaitMs` budget; `fail-closed` on exhaust (503); execution backstop `executionMaxWaitMs` overridable per connection with upstream clamp ([#12715](https://github.com/diegosouzapw/OmniRoute/pull/12715)) — thanks @maxmad64bis (with thanks to @Tushar49 for surfacing the slow-provider need in #12635) +- **fix(auto-combo):** every mode pack now carries `quality` and `reliability` weights (`quality` 0.02, 0.03 in `quality-first`; `reliability` 0.03, 0.04 in `reliability-first`), so selecting a pack no longer silences either signal; `DEFAULT_WEIGHTS` is unchanged ([#12731](https://github.com/diegosouzapw/OmniRoute/pull/12731)) — thanks @maxmad64bis +- **fix(sse):** the provider execution pipeline keeps the upstream error `code`/`type` again — both were lost when the non-streaming leg was extracted, so a config-class `422` (Antigravity missing project) degraded into a generic account cooldown instead of reaching the gate that recognises it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(db):** a legacy `call_logs` table no longer aborts startup — the provider-stats index is created after the column healing runs, not before it, so an install predating `request_type` opens instead of failing with `no such column` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(catalog):** a malformed row in the operator-writable custom-models blob no longer kills every `auto/*` pool ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(tests):** realign the guards that the same day's merges left asserting the old behavior — the provider-count total after GreenPT and EURouter, the GLM stream buffer slot that `#12925` turned into a declared parameter, and the injection scan bound that `#13104` reshaped from a head-only window into head-plus-tail ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- fix(cache): fold tool_choice/tools/response_format into the semantic cache signature so a cached tool_calls response can no longer be replayed for a request whose tool policy forbids it (#12734) ([#13267](https://github.com/diegosouzapw/OmniRoute/pull/13267)) +- **fix(models):** stop treating provider-supplied `isFree:true`/`:free`/`0/0` flags as trusted unless the provider has a documented free tier — only the shipped free-tier catalog decides otherwise; fetched `isFree:true` trusted only for a free-tier provider, custom `isFree:true` via a trusted path ([#12744](https://github.com/diegosouzapw/OmniRoute/pull/12744)) — thanks @maxmad64bis +- fix(memory): authenticate the internal /v1/rerank loopback call so memory reranking no longer silently degrades to unranked order when REQUIRE_API_KEY=true (#12745) ([#13268](https://github.com/diegosouzapw/OmniRoute/pull/13268)) +- fix(sse): parse Ollama Cloud's current usage markup (`$X of $Y used` aria-label, nested width style) (#12749) ([#13260](https://github.com/diegosouzapw/OmniRoute/pull/13260)) +- fix(cli): setup-opencode no longer sends an active context's management token to `/v1/models` when `--api-key`/`OMNIROUTE_API_KEY` is supplied — an explicit flag or the env var now always outranks the context's token, and the flag itself is no longer swallowed by the parent program's global `--api-key` option (#12783) ([#13246](https://github.com/diegosouzapw/OmniRoute/pull/13246)) +- fix(sse): register Arcee AI in the executor provider registry so requests reach api.arcee.ai instead of silently falling back to OpenAI (#12784) ([#13277](https://github.com/diegosouzapw/OmniRoute/pull/13277)) +- **fix(quota):** share one upstream quota read when identical saturation checks arrive at the same time, so traffic bursts don't multiply provider API calls ([#12787](https://github.com/diegosouzapw/OmniRoute/pull/12787)) — thanks @maxmad64bis +- **fix(db):** ignore expired or invalid rate-limit cooldown writes so a stale timestamp can't lock a connection that should be usable — clearing still works as before ([#12788](https://github.com/diegosouzapw/OmniRoute/pull/12788)) — thanks @maxmad64bis +- **fix(providers):** single source for provider order with `xao` ranking alongside `xai-oauth` ([#12790](https://github.com/diegosouzapw/OmniRoute/pull/12790)) — thanks @maxmad64bis +- **fix(routing):** off-table models check the free-model catalog before inheriting premium prices, latency bootstraps from the observed pool median, async tiers read live database pricing with a 90-day freshness gate, and the tier cache invalidates on every pricing write ([#12792](https://github.com/diegosouzapw/OmniRoute/pull/12792)) — thanks @maxmad64bis +- **fix(auto-combo):** snapshot scoring uses the observed per-connection account tier and per-model quality instead of neutral constants, and circuit-open providers rank lower at build time ([#12794](https://github.com/diegosouzapw/OmniRoute/pull/12794)) — thanks @maxmad64bis +- **fix(pool):** empty `auto/*` pools now say why they are empty, and the models listing uses the same paid check as routing ([#12795](https://github.com/diegosouzapw/OmniRoute/pull/12795)) — thanks @maxmad64bis +- fix(routing): recognize CLIProxyAPI's 'unknown provider for model' 400 as fallback-worthy (#12800) ([#13284](https://github.com/diegosouzapw/OmniRoute/pull/13284)) +- **fix(sse):** translate-mode streams now emit the estimated token counts as a trailing usage-only chunk before `[DONE]` when the upstream stays silent, so metered chat clients see totals instead of nothing ([#12828](https://github.com/diegosouzapw/OmniRoute/pull/12828)) — thanks @maxmad64bis +- **fix(db):** provider stats stay truthful on empty and legacy databases: fallback counts default to `0` instead of `null`, latency averages read `null` (not `0`) when no durations were recorded, failures logged before the error-type column existed group under `pre_migration` instead of `unclassified`, and per-provider queries use two new composite indexes ([#12832](https://github.com/diegosouzapw/OmniRoute/pull/12832)) — thanks @maxmad64bis +- fix(nvidia): fail open when a synced model catalog goes stale instead of gating forever (#12849) ([#13248](https://github.com/diegosouzapw/OmniRoute/pull/13248)) +- **fix(db):** WAL maintenance lives in its own module: a `TRUNCATE` checkpoint that hits a busy database now warns with its streak and retries once via `PASSIVE` instead of logging success, and closing-time checkpoints no longer report success on builds without a database file, with the busy totals visible in the authenticated monitoring health payload ([#12853](https://github.com/diegosouzapw/OmniRoute/pull/12853)) — thanks @maxmad64bis +- **fix(health):** quota with no snapshots reads empty instead of a contradicting 0% ([#12857](https://github.com/diegosouzapw/OmniRoute/pull/12857)) — thanks @maxmad64bis +- fix(a2a): accept the dashboard's own session cookie on /a2a so "Run message/send" no longer fails with "Unauthorized: missing or invalid API key" (#12888) ([#13271](https://github.com/diegosouzapw/OmniRoute/pull/13271)) +- **fix(logs):** the Logs grid's in-memory filter pass no longer discards rows the SQL query already matched — selecting an API key from the dropdown (which sends the key's id) returns its calls again, the Combo tab shows every combo instead of only those whose name contains a "1", and the model filter and search cover the same columns as the query ([#12896](https://github.com/diegosouzapw/OmniRoute/pull/12896)) — fixes [#12873](https://github.com/diegosouzapw/OmniRoute/issues/12873) — thanks @ntdatt812 +- **fix(a2a):** `/api/a2a/status` now builds the agent card from the request that asked for it, so a gateway reached at a non-localhost host no longer advertises `http://localhost:20128` as its A2A URL ([#12918](https://github.com/diegosouzapw/OmniRoute/pull/12918)). — thanks @ntdatt812 +- **fix(compression):** progressive aging now appends its `[COMPRESSED:aging:…]` annotation after a turn's `tool_result` blocks instead of in front of them, so Anthropic no longer rejects aged conversations with "`tool_use` ids were found without `tool_result` blocks immediately after" ([#12920](https://github.com/diegosouzapw/OmniRoute/pull/12920)). — thanks @ntdatt812 +- **fix(bedrock):** model import now resolves context limits for every vendor prefix instead of only `anthropic.*`, so `global.openai.gpt-5.6-*` no longer imports with a null `inputTokenLimit` and gets rejected pre-flight at the 200k default ([#12921](https://github.com/diegosouzapw/OmniRoute/pull/12921)). — thanks @ntdatt812 +- **fix(stream):** the 64 KB stream buffer GLM asks for is honoured instead of dropped, and the type error it caused no longer fails the API Route Typecheck gate on every open PR ([#12925](https://github.com/diegosouzapw/OmniRoute/pull/12925)) — thanks @ntdatt812 +- **fix(guardrails):** mask PII inside a `tool_result`'s nested content array, which the masker walked past while redacting its sibling block ([#12930](https://github.com/diegosouzapw/OmniRoute/pull/12930)) — thanks @ntdatt812 +- **fix(dashboard):** the Radar catalog table no longer leaves absent data unexplained — empty limits, unknown context windows, and unreported capabilities each explain themselves on hover, and a new check keeps it that way ([#12937](https://github.com/diegosouzapw/OmniRoute/pull/12937)) — thanks @maxmad64bis +- **fix(sse):** geo-blocked opencode requests rotate to the next account proxy instead of failing, so one refused egress no longer aborts the whole chain ([#12941](https://github.com/diegosouzapw/OmniRoute/pull/12941)) — thanks @maxmad64bis +- **fix(codex):** dashboard "clear cooldown" and the CAS recovery path now drop nested `codexScopeRateLimitedUntil` maps in the same write that nulls `rate_limited_until`, and fresh quota snapshots with headroom lift fallback-sourced scope cooldowns parked by quota preflight ([#12817](https://github.com/diegosouzapw/OmniRoute/issues/12817), [#12860](https://github.com/diegosouzapw/OmniRoute/issues/12860), [#12951](https://github.com/diegosouzapw/OmniRoute/pull/12951)) The dashboard reset-credit button (`consumeCodexResetCredit`) now exercises that same snapshot path after a successful redeem, so a filled quota bar is enough to unpark a leftover fallback Codex child without another manual clear. — thanks @HouMinXi +- **fix(db):** resolve `sql-wasm.wasm` across global npm install and hoisted layouts, ensuring OmniRoute can boot cleanly on Node 24 when native `better-sqlite3` is uncompiled. ([#13035](https://github.com/diegosouzapw/OmniRoute/pull/13035)) — thanks @HouMinXi +- **fix(api):** `DELETE /v1/batches/delete-completed` now sweeps only the calling API key's own completed batches (batches with no owner stay out of a key-scoped sweep on purpose), with an explicit instance-wide mode reserved for authenticated dashboard sessions, a 401 for a presented key that is unknown, revoked, deactivated, banned or expired (never falling through to the session branch), audit logging of every sweep, a sanitized 500 on failure, an owner-scoped file half (a key-scoped sweep never nulls a file another tenant owns) and an atomic sweep — chunked in 200-batch transactions in instance mode — so a mid-way error never leaves a batch pointing at a nulled file (GHSA-wvxc-jp3v-5mg5) ([#12969](https://github.com/diegosouzapw/OmniRoute/pull/12969)) +- **fix(combo):** quota-weighted routing stops drawing on an out-of-credit connection — a 402 now invalidates the stored quota snapshot instead of leaving its stale remaining percentage in place, and a snapshot older than 10 minutes no longer counts as confident headroom for the primary pool ([#12972](https://github.com/diegosouzapw/OmniRoute/pull/12972)) ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- **fix(vision):** Custom Models with "Vision capable" checked no longer have image requests swapped to `glm/glm-4.6v` when the client sends the advertised alias (`vllm/path/...`) or the bare path-shaped id — Vision Bridge now matches the stored override for all three id forms ([#12758](https://github.com/diegosouzapw/OmniRoute/issues/12758)) ([#12974](https://github.com/diegosouzapw/OmniRoute/pull/12974)) — thanks @HouMinXi +- **fix(sse):** transient opencode upstream failures rotate to the next account proxy instead of failing, so one flapping egress no longer aborts the whole chain ([#12975](https://github.com/diegosouzapw/OmniRoute/pull/12975)) — thanks @maxmad64bis +- **fix(azure):** Deployments from GPT-6 onward now send `max_completion_tokens` instead of `max_tokens`, which Azure rejects with HTTP 400. The rule matched a literal `gpt-5`, so each new generation arrived broken; it now matches the generation range, while `gpt-35-turbo` still keeps `max_tokens`. ([#13007](https://github.com/diegosouzapw/OmniRoute/pull/13007)) — thanks @ntdatt812 +- **fix(db):** add an opt-in self-restart circuit for sustained critical memory pressure, gate post-cleanup VACUUM behind a minimum freed-rows threshold, and checkpoint the SQLite WAL every 5 minutes with a size guard that escalates to TRUNCATE, so a growing WAL can no longer stall the event loop into a full outage. ([#13011](https://github.com/diegosouzapw/OmniRoute/pull/13011)) — thanks @HouMinXi +- **fix(auth):** an explicit connection pin may probe a quota-disabled row once and re-enable it on success ([#12874](https://github.com/diegosouzapw/OmniRoute/issues/12874)) ([#13017](https://github.com/diegosouzapw/OmniRoute/pull/13017)) — thanks @HouMinXi +- **fix(api):** thread `X-OmniRoute-Fallback-Attempts` through combo chat completions so streaming and non-streaming responses report how many prior legs were attempted ([#13038](https://github.com/diegosouzapw/OmniRoute/pull/13038)) — thanks @HouMinXi +- Restore provider failure classification and credential refresh on non-streaming requests: classify non-2xx failures to lock models on per-model quota exhaustion, update connection rate limits from headers and body, and pass credential refresh handlers to pipeline execution so 401 tokens can be refreshed and retried (#13043). ([#13069](https://github.com/diegosouzapw/OmniRoute/pull/13069)) — thanks @HouMinXi +- **fix(responses):** wrap forced-non-streaming web_search fallback JSON as Responses SSE so Codex still sees `response.completed` ([#13050](https://github.com/diegosouzapw/OmniRoute/pull/13050)) — thanks @HouMinXi +- **fix(antigravity):** Preserve upstream thought-token usage in normalized completion and reasoning token counts ([#13055](https://github.com/diegosouzapw/OmniRoute/pull/13055)) — thanks @pacocartones +- Fix a concurrency-slot leak in the DeepSeek PoW solver: a worker that failed to spawn (for example a missing worker script) never released its slot, so `MAX_CONCURRENT_WORKERS` failures disabled the solver until restart. ([#13097](https://github.com/diegosouzapw/OmniRoute/pull/13097)) — thanks @anhtahaylove +- **fix(acp):** bound the ACP session output buffers — `stdoutBuffer` and `stderrBuffer` now cap at 1 MiB keeping the most recent output behind a visible `[...output truncated...]` marker, and `stderrBuffer` is reset per prompt instead of accumulating for the lifetime of the session. ([#13100](https://github.com/diegosouzapw/OmniRoute/pull/13100)) — thanks @anhtahaylove +- **fix(acp):** release the `stdout`/`exit` listeners and the idle timer that a `sendPrompt` timeout used to leave attached to the `acpManager` singleton, and drop sessions that exited on their own from the session map instead of keeping them forever. ([#13096](https://github.com/diegosouzapw/OmniRoute/pull/13096)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection and PII scanners now read the text a `tool_result` block carries on `content` (string or nested block list), in messages and in system blocks, so tool output is judged by the same rules as user text ([#13101](https://github.com/diegosouzapw/OmniRoute/pull/13101)) — thanks @ntdatt812 +- **fix(gamification):** close the badge notification SSE stream when the request signal is already aborted before the stream starts — a client that disconnects while the route is still awaiting auth used to leave both the 2s unlock poll and the 15s heartbeat running for the lifetime of the process. ([#13106](https://github.com/diegosouzapw/OmniRoute/pull/13106)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection scan now spends its 16 KB budget on both ends of the request instead of the first 16 KB only, so `system`, `instructions`, `query`, `documents` and the newest turns are no longer hidden behind one long message ([#13104](https://github.com/diegosouzapw/OmniRoute/pull/13104)) — thanks @ntdatt812 +- **fix(dashboard):** expose the Volcano Ark console cookie on quota scraping and unwrap connect-error objects so the dashboard shows the upstream message ([#13107](https://github.com/diegosouzapw/OmniRoute/pull/13107)) — thanks @HouMinXi +- **fix(db):** release the `beforeExit`/`SIGINT`/`SIGTERM` handlers when a `node:sqlite` adapter closes, so a closed adapter and its database handle are no longer pinned to `process` for the lifetime of the run — the same treatment #7494 gave the sql.js adapter. ([#13109](https://github.com/diegosouzapw/OmniRoute/pull/13109)) — thanks @anhtahaylove +- **fix(translator):** `contentSchema` and `unevaluatedItems` are now treated as subschema positions by the tool-schema sanitizer, so a truncation placeholder in either is replaced with a permissive schema instead of being forwarded as a string ([#13110](https://github.com/diegosouzapw/OmniRoute/pull/13110)) — thanks @ntdatt812 +- **fix(cli-helper):** clear the `createLogStream` timeout on the abort path — `stop()` aborts the in-flight fetch and returned through the `signal.aborted` branch, which skipped `clearTimeout` and left an armed timer per stopped stream. The stream reader is now also cancelled when the read loop exits early. ([#13114](https://github.com/diegosouzapw/OmniRoute/pull/13114)) — thanks @anhtahaylove +- **fix(docs):** drop TLS-impersonation, MITM-decrypt, supply-chain attestation, and XOR-mask writeups from the public `/docs` catalog and Docker image. Files stay in git for engineers; operators who need them open the repo, not the website. ([#13136](https://github.com/diegosouzapw/OmniRoute/pull/13136)) — thanks @HouMinXi +- **fix(combo):** parse numeric-epoch `rate_limited_until` in the combo cooldown read path ([#13141](https://github.com/diegosouzapw/OmniRoute/pull/13141)) — thanks @maxmad64bis +- **fix(opencode):** both OpenCode plugins now reject a gateway address typed without `http://` at configuration time, instead of publishing every model with an api url no client can call, and the v2 plugin no longer publishes a model card whose api url is blank or relative ([#13142](https://github.com/diegosouzapw/OmniRoute/pull/13142)) — thanks @maxmad64bis +- **fix(providers):** lock opencode model on upstream 400 model-unavailable ([#13146](https://github.com/diegosouzapw/OmniRoute/pull/13146)) — thanks @maxmad64bis +- **fix(logging):** keep the provider exchange rather than the raw client bodies when a call log exceeds its size budget, and show that recovered payload in the request-detail panel instead of replacing it with the stored response body ([#13147](https://github.com/diegosouzapw/OmniRoute/pull/13147)) — thanks @maxmad64bis +- **fix(traffic-inspector):** the WebSocket route no longer leaks a traffic-buffer subscriber and a 30s ping timer when the client socket is already closed at handler time — listeners are attached before any resource is acquired, a destroyed socket bails out early, and the ping interval stops on a dead socket where `write()` never throws ([#13155](https://github.com/diegosouzapw/OmniRoute/pull/13155)) — thanks @anhtahaylove +- **fix(telegram):** bound the per-user API key cache in the Telegram chat proxy so a burst of distinct chat ids can no longer grow the process heap without limit ([#13165](https://github.com/diegosouzapw/OmniRoute/issues/13165)) ([#13166](https://github.com/diegosouzapw/OmniRoute/pull/13166)) — thanks @anhtahaylove +- **fix(stream):** cancel the upstream response body when the JSON-to-SSE sniff unwinds on a body timeout, so a stalled upstream no longer pins the connection ([#13169](https://github.com/diegosouzapw/OmniRoute/issues/13169)) ([#13171](https://github.com/diegosouzapw/OmniRoute/pull/13171)) — thanks @anhtahaylove +- **fix(telegram):** authenticate webhook deliveries with Telegram's `secret_token` so an unauthenticated caller can no longer mint API keys or spend upstream quota ([#13172](https://github.com/diegosouzapw/OmniRoute/issues/13172)) ([#13175](https://github.com/diegosouzapw/OmniRoute/pull/13175)) — thanks @anhtahaylove +- **fix(models):** give discoverable Gemini 3.8 Flash ids their own 65536 output spec so Antigravity no longer clamps them to 16384 ([#13195](https://github.com/diegosouzapw/OmniRoute/pull/13195)) — thanks @HouMinXi +- **fix(dashboard):** leftover catalog-removed provider rows (gemini-cli) can be listed and purged from the providers page ([#13067](https://github.com/diegosouzapw/OmniRoute/issues/13067)) ([#13197](https://github.com/diegosouzapw/OmniRoute/pull/13197)) — thanks @HouMinXi +- **fix(auth):** a dashboard session now requires the `authenticated: true` claim that login, OIDC and the session refresh already emit — a JWT merely signed with `JWT_SECRET` (for example the Cursor CLI passthrough token, which any API-key holder can obtain) no longer verifies as the `auth_token` cookie on any route, the WebSocket handshake or the live server; existing sessions keep working ([#13298](https://github.com/diegosouzapw/OmniRoute/issues/13298)) ([#13375](https://github.com/diegosouzapw/OmniRoute/pull/13375)) +- **fix(api):** `DELETE /v1/batches/delete-completed` now applies the caller's API-key policy (endpoint allowlist, schedule, usage cap, rate limit) like every other `/v1` route, commits the key-scoped sweep per 200-batch chunk instead of holding one write lock for the whole sweep, refuses to loop without progress, and rejects a scope that names both a key and `allTenants` ([#13684](https://github.com/diegosouzapw/OmniRoute/pull/13684)) +- Restore the API-route typecheck gate: the non-streaming leg result lost its discriminated-union narrowing after the server-owned tool loop reassignment, producing 13 new TS2339 diagnostics in `chatCore.ts`. ([#12963](https://github.com/diegosouzapw/OmniRoute/pull/12963)) +- Restore four non-streaming behaviours the server-owned tool loop refactor dropped: client aborts map to 499 with the fixed `Request aborted` message, an aborted request no longer logs a synthetic `clientResponse`, Claude prompt-cache telemetry is recorded again, and a body that cannot be canonicalized no longer throws when the tool loop is off. ([#12990](https://github.com/diegosouzapw/OmniRoute/pull/12990)) +- **fix(skills):** The CLI registry parser now reads positionals declared with `.addArgument()`, not only those written inline in `.command()`. `tunnel create [type]` was being published as `tunnel create`, so the agent-skills sync gate reported drift on every branch and regenerating would have deleted the argument. ([#13009](https://github.com/diegosouzapw/OmniRoute/pull/13009)) — thanks @ntdatt812 +- **fix(combos):** dashboard combo test uses a short prompt, serial probes, and a 60s timeout so reasoning models and rate-limited free pools do not fail the health check ([#13001](https://github.com/diegosouzapw/OmniRoute/pull/13001)) — thanks @HouMinXi +- fix(compression): terminate idle worker threads on eviction so long-running instances stop leaking OS threads and MessagePorts ([#13091](https://github.com/diegosouzapw/OmniRoute/pull/13091)) — thanks @anhtahaylove +- Redact Google API keys of any length in error bodies: the pattern required exactly 39 characters, so shorter or longer `AIza…` credentials passed through unredacted. ([#12964](https://github.com/diegosouzapw/OmniRoute/pull/12964)) +- **fix(grok-cli):** a 402 "Grok Build usage balance exhausted" parks that Grok login as out of credit (Grok Build CLI, grok.com cookie, and xAI OAuth share the weekly pool). Combo routing then tries the next login instead of locking the model for every account in the pool ([#13061](https://github.com/diegosouzapw/OmniRoute/pull/13061)) — thanks @HouMinXi +- fix(compression): spawn the LLMLingua worker with a file URL object so compression actually runs instead of silently failing open on Node ([#13093](https://github.com/diegosouzapw/OmniRoute/pull/13093)) — thanks @anhtahaylove +- **fix(test):** run the local `test` and `test:unit` scripts at concurrency 4 so a full-suite run no longer exhausts the machine's commit charge and kills unrelated processes ([#13187](https://github.com/diegosouzapw/OmniRoute/pull/13187)) — thanks @anhtahaylove +- **fix(monitoring):** `GET /api/monitoring/health` `credentialHealth` now includes a bounded `failedConnections` list (`connectionId`, `status`, sanitized `lastError`) when the probe-cache gauge `failed>0`, plus `source: probe-cache` and a cheap `staleDbNonOkCount` for sticky SQLite `test_status` on active rows. Documents that the live gauge is not `provider_connections.test_status`. ([#12876](https://github.com/diegosouzapw/OmniRoute/pull/12876)) — thanks @RaviTharuma +- **fix(resilience):** an openai-compatible multi-upstream gateway no longer marks the whole connection `credits_exhausted` when a single upstream (or the `/models` / representative-model health probe) returns 402. Quota failures stay model-scoped; true connection-wide auth failures are unchanged. Single-credential 402 key disable ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239)) is preserved. ([#12875](https://github.com/diegosouzapw/OmniRoute/pull/12875)) — thanks @RaviTharuma +- **fix(dashboard):** nine fixes on the Orchestration Canvas, all diagnosed in the Phase 2 reviews. A source that fails now keeps the timestamp of its FIRST failure instead of being re-stamped every poll — the stale line said "since the last poll" no matter how long the source had been down, and the churn also defeated the snapshot's stable identity (it serializes the source list), so the canvas re-rendered on every tick while anything was broken. A source that HAD data and only then started failing is flagged too: previously only a source with no node at all got the warning, so a source that went down mid-session kept a healthy-looking node forever. The rest are pointwise: clicking a filter chip cancels the pending search debounce (left armed, it fired ~300ms later and silently reverted the chip); the search input carries an accessible name; `?state=running, failed` parses like the unpadded form instead of dropping the padded value; the CSV toggle helper is defined once in `model/urlParams.ts` rather than twice; the agents tab tells "nothing running" apart from "the filter matched nothing", offering a clear-filters button instead of setup links that would be wrong advice there; edges stop emitting SMIL particles above 40 simultaneously active edges, keeping the colored stroke; and the drawer's error banner clears when a retried action succeeds. ([#12983](https://github.com/diegosouzapw/OmniRoute/pull/12983)) +- **fix(i18n):** the nine locales added with the EU-language batch (Greek, Estonian, Irish, Croatian, Lithuanian, Latvian, Maltese, Slovenian, Serbian) were missing the eleven Orchestration Canvas keys that Phase 3 introduced, so the compare-runs panel and the "no runs match these filters" empty state fell back to English in those languages (`deepMergeFallback` substitutes English for an absent key, so nothing rendered blank — it rendered untranslated). The coverage gate does not catch this: it enforces an 80% floor per locale, and eleven missing keys out of ~13,000 leaves coverage at 99.9%. Translated for real in each language, calibrated against the wording each file already uses for "run", "filter" and "skill". ([#13294](https://github.com/diegosouzapw/OmniRoute/pull/13294)) +- **fix(dashboard):** five follow-ups from the Phase 2 Orchestration Canvas review. The History drawer now shows the memory section for runs that already left the live TTL window: the persisted `memory_hits` event is parsed into `metadata.memoryHits` with the same defensive validation the drawer applies, and — because that event is observability rather than a state transition — it no longer leaks into the timeline, where it had been inheriting the task's state and rendering as a duplicate transition. The A2A memory recall runs against its own 1.5s deadline instead of inheriting the memory backend's 30s one; overshooting degrades exactly like any other recall failure (no hits, task proceeds), and the timer is cleared on both paths. Repeating a Conductor run carries its `requirements.cli`/`requirements.model` forward, so the new run is pinned to the same runner profile and model rather than drifting to whatever the fleet picks. A successful repeat from the Agents tab now focuses the run it created, instead of leaving the operator on the finished one. And the auth test for `POST /api/conductor/tasks` moved into the shared `ROUTES` array rather than restating the pattern. ([#12988](https://github.com/diegosouzapw/OmniRoute/pull/12988)) +- fix(plugins): stop leaking an exit listener per plugin hook timeout, which triggered MaxListenersExceededWarning on plugins that ignore SIGTERM ([#13092](https://github.com/diegosouzapw/OmniRoute/pull/13092)) — thanks @anhtahaylove +- **fix(validation):** Provider node edits no longer fail with a generic "Invalid request" when the optional daily-quota reset fields are left blank. The dashboard sends `dailyQuotaResetTimezone` and `dailyQuotaResetHour` as `null`, and only the hour accepted it. ([#13066](https://github.com/diegosouzapw/OmniRoute/issues/13066)) ([#13083](https://github.com/diegosouzapw/OmniRoute/pull/13083)) — thanks @ntdatt812 +- **fix(combo):** quota-aware expansion drops banned, inactive, missing, and wrong-provider connections before quota fetch or model dispatch; pins and allowlists stay selectors, not a bypass. Antigravity automatic exhaustion now requires a reported zero remaining, so a positive balance below 1% stays eligible. ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- Fix an intermittent failure in the quota-weighted routing test suite: scores are a function of `Date.now()`, so peers with identical quota scored microseconds apart never tied and swapped order. ([#12946](https://github.com/diegosouzapw/OmniRoute/pull/12946)) +- fix(volcengine): resolve quota window mapping gaps, safe multi-connection console binding, and response cookie sanitization ([#12950](https://github.com/diegosouzapw/OmniRoute/pull/12950)) — thanks @HouMinXi +- **fix(test):** resolve the WebDAV handler path with `fileURLToPath` so the suite's 37 WebDAV tests run on Windows instead of failing with a doubled `C:\C:\` drive prefix ([#13196](https://github.com/diegosouzapw/OmniRoute/pull/13196)) — thanks @anhtahaylove +- **fix(ui):** fall back from invalid AnySearch icon alias (direct commit `9587e07b69`) +- **fix(sse):** stop rebuilding a truncated summary from the collector's cap-dropped event array ([#12718](https://github.com/diegosouzapw/OmniRoute/pull/12718)) — thanks @hartmark +- **fix(conversations):** show a pending spinner for unresolved tool nodes instead of "(empty)" ([#12727](https://github.com/diegosouzapw/OmniRoute/pull/12727)) — thanks @hartmark +- **fix(streaming):** fail fast when an upstream stream produces only lifecycle/heartbeat events, never real content ([#12741](https://github.com/diegosouzapw/OmniRoute/pull/12741)) — thanks @hartmark +- **fix(responses-continuation):** bridge the write-in-flight window with an in-memory pending store ([#12854](https://github.com/diegosouzapw/OmniRoute/pull/12854)) — thanks @hartmark +- **fix(translator):** drop or map agent_message on Chat Completions fallback ([#12880](https://github.com/diegosouzapw/OmniRoute/pull/12880)) — thanks @RaviTharuma +- **fix(api):** page and stream-complete GET /v1/models for large catalogs ([#12882](https://github.com/diegosouzapw/OmniRoute/pull/12882)) — thanks @RaviTharuma +- **fix(quota):** align AUTH window labels with usage API and clarify 503 ([#12884](https://github.com/diegosouzapw/OmniRoute/pull/12884)) — thanks @RaviTharuma +- **fix(ci):** clear two base-reds on release/v3.8.51 — mutation-coverage gate + image-only-model guard ([#12945](https://github.com/diegosouzapw/OmniRoute/pull/12945)) +- **fix(api):** scope batch bulk-delete to the calling API key ([#13211](https://github.com/diegosouzapw/OmniRoute/pull/13211)) +- **fix(tests):** align model-sync log assertion to shipped path redaction (base-red) ([#13227](https://github.com/diegosouzapw/OmniRoute/pull/13227)) +- **fix(test):** make the ToS heading guard actually require the parentheses ([#13228](https://github.com/diegosouzapw/OmniRoute/pull/13228)) +- **fix(quality):** register 4 drifted covering tests in stryker tap.testFiles ([#13229](https://github.com/diegosouzapw/OmniRoute/pull/13229)) +- **fix(logging):** mask *-api-key headers in request-log pipeline (#13273) ([#13401](https://github.com/diegosouzapw/OmniRoute/pull/13401)) — thanks @KooshaPari +- **fix(tests):** correct stale Jina catalog prefix in custom-models test ([#13403](https://github.com/diegosouzapw/OmniRoute/pull/13403)) — thanks @KooshaPari +- **fix(backend):** prune old backups after health-check-repair snapshots (#13308) ([#13404](https://github.com/diegosouzapw/OmniRoute/pull/13404)) — thanks @KooshaPari +- **fix(backend):** guard memory_fts_au trigger with WHEN clause to stop FTS bloat (#13326) ([#13405](https://github.com/diegosouzapw/OmniRoute/pull/13405)) — thanks @KooshaPari +- **fix(free-tiers):** remove obsolete Together $25 signup credit (#12526) ([#13407](https://github.com/diegosouzapw/OmniRoute/pull/13407)) — thanks @KooshaPari +- **fix:** skip non-chat generation models in Test all models ([#13409](https://github.com/diegosouzapw/OmniRoute/pull/13409)) — thanks @KooshaPari +- **fix(providers):** de-duplicate agy and antigravity model catalogs ([#13410](https://github.com/diegosouzapw/OmniRoute/pull/13410)) — thanks @KooshaPari +- **fix(dashboard):** send provider in body for mini-playground web search ([#13411](https://github.com/diegosouzapw/OmniRoute/pull/13411)) — thanks @KooshaPari +- **fix(resilience):** add CJK quota-exhaustion patterns to 429 classifier ([#13412](https://github.com/diegosouzapw/OmniRoute/pull/13412)) — thanks @KooshaPari +- **fix(backend):** eval runner: failed calls no longer score as passed + dotAll regex ([#13413](https://github.com/diegosouzapw/OmniRoute/pull/13413)) — thanks @KooshaPari +- **fix(backend):** eval runner opts out of output-style and memory injection ([#13414](https://github.com/diegosouzapw/OmniRoute/pull/13414)) — thanks @KooshaPari +- **fix:** chain serial quarantine step in npm test script (fixes #13286) ([#13418](https://github.com/diegosouzapw/OmniRoute/pull/13418)) — thanks @KooshaPari +- **fix(antigravity):** derive stable session ID from credentials for prompt-cache reuse (#13305) ([#13424](https://github.com/diegosouzapw/OmniRoute/pull/13424)) — thanks @KooshaPari +- **fix(memory):** merge memory into top-level system field instead of unshifting at messages[0] (#13425) ([#13427](https://github.com/diegosouzapw/OmniRoute/pull/13427)) — thanks @KooshaPari +- **fix(compression):** preserve instruction blocks from lossy engines ([#13523](https://github.com/diegosouzapw/OmniRoute/pull/13523)) — thanks @KooshaPari +- **fix(compression):** preserve polarity words and newlines in ultra heuristic ([#13524](https://github.com/diegosouzapw/OmniRoute/pull/13524)) — thanks @KooshaPari +- **fix(compression):** run user preservePatterns before built-in patterns (#13457) ([#13525](https://github.com/diegosouzapw/OmniRoute/pull/13525)) — thanks @KooshaPari +- **fix(reasoning):** expand deepseek-v4 regex to match v4.1+ and add command-code provider (#13430) ([#13528](https://github.com/diegosouzapw/OmniRoute/pull/13528)) — thanks @KooshaPari +- **fix(providers):** add alwaysReasons metadata for reka-flash-3 ([#13531](https://github.com/diegosouzapw/OmniRoute/pull/13531)) — thanks @KooshaPari +- **fix(playground):** allow Compare columns to scroll independently ([#13532](https://github.com/diegosouzapw/OmniRoute/pull/13532)) — thanks @KooshaPari +- **fix(security):** empty whitelist allows through to prevent admin lockout ([#13534](https://github.com/diegosouzapw/OmniRoute/pull/13534)) — thanks @KooshaPari +- **fix(tests):** add DISABLE_IOREG_STRATEGY env var for macOS test compatibility (fixes #13467) ([#13539](https://github.com/diegosouzapw/OmniRoute/pull/13539)) — thanks @KooshaPari +- **fix(zed-hosted):** map developer role and Gemini enums for Zed proxy ([#13543](https://github.com/diegosouzapw/OmniRoute/pull/13543)) — thanks @KooshaPari +- **fix(analytics):** normalize provider aliases to canonical id in usage_history ([#13545](https://github.com/diegosouzapw/OmniRoute/pull/13545)) — thanks @KooshaPari +- **fix(combo):** use traceId for call log id to prevent UNIQUE constraint failure ([#13546](https://github.com/diegosouzapw/OmniRoute/pull/13546)) — thanks @KooshaPari +- **fix(auth):** check model lockout before returning synthetic noauth connection ([#13547](https://github.com/diegosouzapw/OmniRoute/pull/13547)) — thanks @KooshaPari +- **fix(guardrails):** skip credential redaction for base64 image data URLs ([#13550](https://github.com/diegosouzapw/OmniRoute/pull/13550)) — thanks @KooshaPari +- **fix(resilience):** increase requestQueue.maxWaitMs default from 15s to 30s ([#13553](https://github.com/diegosouzapw/OmniRoute/pull/13553)) — thanks @KooshaPari +- **fix(docs):** document OMNIROUTE_READY_TIMEOUT_MS and allowlist the test-only DISABLE_IOREG_STRATEGY ([#13692](https://github.com/diegosouzapw/OmniRoute/pull/13692)) ### 📝 Maintenance @@ -853,6 +1054,39 @@ _By commits in `release/v3.8.50..release/v3.8.51` (`091589089c..d6f315018a`), au - **docs(security):** document socket.yml scanner config + CI workflow link (#12575) ([#12764](https://github.com/diegosouzapw/OmniRoute/pull/12764)) — thanks @KooshaPari - **chore(ci):** guard commit identity in pre-commit to stop author misattribution ([#12772](https://github.com/diegosouzapw/OmniRoute/pull/12772)) - **deps:** 17 Dependabot bumps — bump electron from 43.4.0 to 43.4.1 in /electron ([#11426](https://github.com/diegosouzapw/OmniRoute/pull/11426)); bump the development group across 1 directory with 8 updates ([#11428](https://github.com/diegosouzapw/OmniRoute/pull/11428)); bump the production group across 1 directory with 11 updates ([#11440](https://github.com/diegosouzapw/OmniRoute/pull/11440)); chore(deps): bump github/codeql-action/init from 4.37.7 to 4.37.8 ([#11925](https://github.com/diegosouzapw/OmniRoute/pull/11925)); chore(deps): bump github/codeql-action from 4.37.7 to 4.37.8 ([#11926](https://github.com/diegosouzapw/OmniRoute/pull/11926)); chore(deps): bump github/codeql-action/analyze from 4.37.7 to 4.37.8 ([#11927](https://github.com/diegosouzapw/OmniRoute/pull/11927)); build(deps): bump github/codeql-action/init from 4.37.8 to 4.37.9 ([#12345](https://github.com/diegosouzapw/OmniRoute/pull/12345)); build(deps): bump github/codeql-action/analyze from 4.37.8 to 4.37.9 ([#12346](https://github.com/diegosouzapw/OmniRoute/pull/12346)); bump the development group across 1 directory with 2 updates ([#12347](https://github.com/diegosouzapw/OmniRoute/pull/12347)); build(deps): bump github/codeql-action from 4.37.8 to 4.37.9 ([#12349](https://github.com/diegosouzapw/OmniRoute/pull/12349)); bump browserslist from 4.28.2 to 4.28.8 ([#12396](https://github.com/diegosouzapw/OmniRoute/pull/12396)); bump the production group across 1 directory with 4 updates ([#12399](https://github.com/diegosouzapw/OmniRoute/pull/12399)); bump @xmldom/xmldom ([#12500](https://github.com/diegosouzapw/OmniRoute/pull/12500)); bump qs from 6.15.2 to 6.16.0 ([#12512](https://github.com/diegosouzapw/OmniRoute/pull/12512)); bump @xmldom/xmldom from 0.9.10 to 0.9.12 ([#12513](https://github.com/diegosouzapw/OmniRoute/pull/12513)); bump fast-uri from 3.1.5 to 3.1.7 ([#12514](https://github.com/diegosouzapw/OmniRoute/pull/12514)); bump @humanfs/node from 0.16.7 to 0.16.8 ([#12515](https://github.com/diegosouzapw/OmniRoute/pull/12515)) +- Clear the `release/v3.8.51` `check:agent-skills-sync` base-red: regenerate `skills/cli-tunnel/SKILL.md` so the `tunnel create [type]` positional that #13009 taught the generator to read is reflected in the committed skill. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` typecheck base-red from #12731: the new `Mode pack ranking gates` candidates in `open-sse/services/autoCombo/__tests__/autoCombo.test.ts` omitted the required `provider`, `model` and `errorRate` fields of `ProviderCandidate`, failing `check:open-sse-typecheck` (and with it `Fast Quality Gates`) on every open PR. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` docs-sync base-red: EURouter (#13025) and GreenPT (#13024) took the live provider count to 358, leaving 7 STRICT drifts (`PROVIDER_REFERENCE.md`, 4 diagrams, 2 tier-flow images) plus the `AGENTS.md` / `llm.txt` / `package.json` count claims stale. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- **test(models):** Pin `CATALOG_BUILD_TIMEOUT_MS` in the #12058 canonical-catalog regression so a cold catalog build racing the 8s default bound no longer flakes the suite with a 500 ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Allow `@omniroute/opencode-plugin-v2/` in the npm pack policy — [#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870) shipped the v2 plugin beside its v1 sibling without widening the allowlist, so every packed file under it read as an unexpected artifact and failed `check:pack-policy` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Point the release-green validator's pack gate at the tree it is validating — the build-provenance guard defaulted to `origin/main`, which a release branch tip can never be an ancestor of mid-cycle, so `Package artifact` reported a false red on every run and the tarball boot-smoke cascaded off it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **docs(checks):** keep doc counts honest — headings, rankings, catalog, weights, quality gate and scoring diagram now covered ([#12507](https://github.com/diegosouzapw/OmniRoute/pull/12507)) — thanks @maxmad64bis +- **chore(lifecycle):** `check:model-lifecycle` now also diffs `DEFAULT_DEGRADATION_MAP` (the background-task redirect table) against the vendor lifecycle snapshot, refusing a retired id as source or target, with a table-driven unit test beside it. Three rows whose source the vendor had retired — `claude-sonnet-4-20250514`, `gemini-3-pro-preview` and `gpt-5.1-codex` (whose target `gpt-5.1-codex-mini` is retired too) — were dead code, since `checkLifecycle` answers 410 before the redirect runs; they are dropped (#12535 — thanks @pacocartones) +- **test(compression):** cover idle worker eviction at the resource level — the pool must call `terminate()` and must not retain the worker's `MessagePort`, complementing the `exit`-event assertion added with the fix ([#13371](https://github.com/diegosouzapw/OmniRoute/pull/13371)) +- **chore(quality):** raise the `imageGeneration.ts` file-size ceiling for the image-only-model guard that clears the #12945 base-red ([#13392](https://github.com/diegosouzapw/OmniRoute/pull/13392)) +- **chore(quality):** type the combo-test route's JSON response bodies instead of casting them to `any`, drop the now-empty suppression entry, and remove the `hasPerModelQuota` import `chatCore.ts` stopped using when the failure-classification helper was extracted ([#13379](https://github.com/diegosouzapw/OmniRoute/pull/13379)) +- **chore(electron):** the desktop bundle no longer ships the root-level files of every translated docs mirror (`README.md`, `llm.txt`, `CLAUDE.md`, `GEMINI.md`, `CONTRIBUTING.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`) — the packaged app only reads `docs/i18n//docs/**`, which stays. Saves ~11 MB on top of the translated CHANGELOGs already pruned. (#0000) ([#13235](https://github.com/diegosouzapw/OmniRoute/pull/13235)) +- **chore(quality):** retighten the `src/sse/handlers/chat.ts` unused-vars suppression count to the 8 violations that actually remain ([#13385](https://github.com/diegosouzapw/OmniRoute/pull/13385)) +- Bring the documented counts back in line with the code: 171 migrations (was 169) and 20 routing strategies (was 19), plus the regenerated `cli-tunnel` skill reference. ([#12970](https://github.com/diegosouzapw/OmniRoute/pull/12970)) +- Update the documented migration count to 172 after the call-logs provider-stats indexes landed. ([#13209](https://github.com/diegosouzapw/OmniRoute/pull/13209)) +- **chore(ci):** two gates that close the blind spots behind the exclusions above. `check:vitest-exclusions` requires every Vitest exclusion to name a tracking issue and to appear in `config/quality/vitest-exclusions.json` — the previous list grew to 62 files behind a comment pointing at an issue that had been closed for a month. `check-new-key-coverage` requires a key newly added to `en.json` to reach every locale; the existing coverage gate is a percentage floor per locale, so eleven absent keys out of ~13,000 left it at 99.9% while a whole feature shipped untranslated in nine languages. Both are diff-aware, so pre-existing debt stays frozen and neither needed a migration to turn on. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **build:** root layout no longer loads Inter from `next/font/google`, so a production image build does not need fonts.googleapis.com ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **docs(i18n):** bring the eight new `llm.txt` mirrors (km, kn, ml, my, ne, or, pa, si) up to the current provider and migration counts, so `check-docs-sync` stops rejecting every commit ([#13674](https://github.com/diegosouzapw/OmniRoute/pull/13674)) +- **refactor(ui):** flow surfaces (home topology, combo live studio, compression cockpit/waterfall/nodes, token-health badge) express state through the theme-aware `--orch-status-*` tokens instead of fixed dark-mode hex, so green/red/amber/grey stay legible in the light theme; dark mode is byte-identical. Categorical palettes (routing-strategy hues, compression-layer pills, provider brand colors) deliberately stay hex ([#12378](https://github.com/diegosouzapw/OmniRoute/issues/12378)) ([#13203](https://github.com/diegosouzapw/OmniRoute/pull/13203)) +- **chore(tests):** 51 test files that had been excluded from Vitest are running again, restoring roughly 350 assertions to the blocking suite. Each was measured individually first: of the 62 files parked behind the `// #8618 — pre-existing failure` comment, 51 pass against the current tree with no source change, so the exclusions had outlived the failures they were added for. The 11 that genuinely still fail stay excluded, but now point at a live tracker (#13204) rather than at #8618, which was closed in August while the list it tracked kept growing. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **chore(quality):** drop two ESLint suppression entries whose violations no longer exist, so `eslint --suppressions-location` stops rejecting every commit that touches the surrounding files ([#13384](https://github.com/diegosouzapw/OmniRoute/pull/13384)) +- **chore(release):** bring main's post-tag fixes into release/v3.8.51 — sync-back step 2/2 (direct commit `21c488f210`) +- **chore(deps):** patch toml and esbuild in the opencode plugin lockfiles ([#12965](https://github.com/diegosouzapw/OmniRoute/pull/12965)) +- **docs(changelog):** reconcile the v3.8.51 living section — fold 366 fragments, cover every cycle commit, credit every contributor ([#12971](https://github.com/diegosouzapw/OmniRoute/pull/12971)) +- **test(plugin):** wait for the v2 lazy-refresh tier instead of sleeping past it ([#12984](https://github.com/diegosouzapw/OmniRoute/pull/12984)) +- **chore(quality):** rebaseline chatCore.ts after the non-streaming regression fixes ([#13045](https://github.com/diegosouzapw/OmniRoute/pull/13045)) +- **docs(changelog):** add the missing bullet marker to the DeepSeek PoW fragment ([#13200](https://github.com/diegosouzapw/OmniRoute/pull/13200)) +- **chore(deps):** drain the Dependabot queue — 10 of 13 alerts ([#13213](https://github.com/diegosouzapw/OmniRoute/pull/13213)) +- **chore(ci):** make worktrees carry the hooks that enforce the identity gate ([#13236](https://github.com/diegosouzapw/OmniRoute/pull/13236)) +- **chore(deps):** pin csv-parse to ^7.0.2 via overrides (#13117) ([#13300](https://github.com/diegosouzapw/OmniRoute/pull/13300)) — thanks @KooshaPari +- **chore(deps):** bump hono from ^4.12.34 to ^4.13.7 (#13148) ([#13301](https://github.com/diegosouzapw/OmniRoute/pull/13301)) — thanks @KooshaPari +- **chore(deps):** pin joi to ^18.2.8 via overrides (#13085) ([#13302](https://github.com/diegosouzapw/OmniRoute/pull/13302)) — thanks @KooshaPari +- **docs(providers):** add Microsoft 365 Copilot (BizChat) provider guide (#12779) ([#13340](https://github.com/diegosouzapw/OmniRoute/pull/13340)) — thanks @KooshaPari ### 🙌 Contributors @@ -870,6 +1104,7 @@ Thanks to everyone whose work landed in v3.8.51: | [@amaleta](https://github.com/amaleta) | #11906, #11934 | | [@amartinawi](https://github.com/amartinawi) | #11575 | | [@AndrianBalanescu](https://github.com/AndrianBalanescu) | #11083, #11830, #11834, #11844 | +| [@anhtahaylove](https://github.com/anhtahaylove) | #13091, #13092, #13093, #13096, #13097, #13100, #13106, #13109, #13114, #13155, #13166, #13171, #13175, #13187, #13196 | | [@aniruddhaadak80](https://github.com/aniruddhaadak80) | #11760 | | [@app](https://github.com/app) | #12554 | | [@arminanton](https://github.com/arminanton) | #11461, #11513 | @@ -899,10 +1134,10 @@ Thanks to everyone whose work landed in v3.8.51: | [@gonisulaimann](https://github.com/gonisulaimann) | #12368, #12369, #12371 | | [@Gorillaz322](https://github.com/Gorillaz322) | #12207 | | [@groovecityJO](https://github.com/groovecityJO) | #12682 | -| [@hartmark](https://github.com/hartmark) | #11434, #11452, #11473, #11499, #11703, #11983, #11984, #11985, #11986, #11988, #11989, #11990, #11994, #12221, #12293, #12445, #12446, #12447, #12448, #12460, #12461, #12623, #12646, #12650, #12680 | +| [@hartmark](https://github.com/hartmark) | #11434, #11452, #11473, #11499, #11703, #11983, #11984, #11985, #11986, #11988, #11989, #11990, #11994, #12221, #12293, #12445, #12446, #12447, #12448, #12460, #12461, #12623, #12646, #12650, #12680, #12717, #12718, #12727, #12741, #12854 | | [@hizzt](https://github.com/hizzt) | #11894 | | [@hongnoul](https://github.com/hongnoul) | #11484 | -| [@HouMinXi](https://github.com/HouMinXi) | #11411, #11414, #11512, #11518, #11520, #11641, #11642, #11643, #11687, #11779, #11849, #11850, #11851, #11915, #11916, #11918, #11919, #11920, #12013, #12017, #12033, #12042, #12106, #12139, #12166, #12169, #12171, #12205, #12213, #12312, #12325, #12487, #12488, #12495, #12504, #12557, #12566, #12590, #12591, #12624, #12626, #12632, #12637, #12678, #12696, #12697, #12711, #12733, #12746, #12767, #12770, #12789, #12803, #12805, #12811, #12866, #12868, #12899, #12926, #12934 | +| [@HouMinXi](https://github.com/HouMinXi) | #11411, #11414, #11512, #11518, #11520, #11641, #11642, #11643, #11687, #11779, #11849, #11850, #11851, #11915, #11916, #11918, #11919, #11920, #12013, #12017, #12033, #12042, #12106, #12139, #12166, #12169, #12171, #12205, #12213, #12312, #12325, #12487, #12488, #12495, #12504, #12557, #12566, #12590, #12591, #12624, #12626, #12632, #12637, #12678, #12696, #12697, #12711, #12733, #12746, #12767, #12770, #12789, #12803, #12805, #12811, #12866, #12868, #12899, #12926, #12934, #12950, #12951, #12974, #13001, #13006, #13011, #13017, #13026, #13027, #13034, #13035, #13038, #13042, #13050, #13060, #13061, #13069, #13107, #13120, #13136, #13195, #13197 | | [@Hsia97](https://github.com/Hsia97) | #11624 | | [@jacobsparts](https://github.com/jacobsparts) | #11854, #12155, #12167 | | [@joglomedia](https://github.com/joglomedia) | #11980 | @@ -912,11 +1147,11 @@ Thanks to everyone whose work landed in v3.8.51: | [@KaspaPulse](https://github.com/KaspaPulse) | #11389, #11469, #11628, #11666, #11910 | | [@keeltrace](https://github.com/keeltrace) | #12080, #12223 | | [@killer30001000](https://github.com/killer30001000) | #12178 | -| [@KooshaPari](https://github.com/KooshaPari) | #12470, #12592, #12667, #12699, #12703, #12706, #12764, #12769, #12771 | +| [@KooshaPari](https://github.com/KooshaPari) | #12470, #12592, #12667, #12699, #12703, #12706, #12764, #12769, #12771, #13300, #13301, #13302, #13340, #13401, #13403, #13404, #13405, #13406, #13407, #13409, #13410, #13411, #13412, #13413, #13414, #13418, #13424, #13427, #13433, #13523, #13524, #13525, #13528, #13531, #13532, #13534, #13539, #13543, #13545, #13546, #13547, #13550, #13553 | | [@kriptoburak](https://github.com/kriptoburak) | #11370 | | [@KrzysiekSko](https://github.com/KrzysiekSko) | #12425, #12432, #12673 | | [@marcelokarval](https://github.com/marcelokarval) | direct commit / report | -| [@maxmad64bis](https://github.com/maxmad64bis) | #1622, #11435, #11437, #11441, #11537, #11550, #11553, #11555, #11557, #11812, #11842, #11843, #11903, #12151, #12214, #12215, #12218, #12226, #12314, #12316, #12317, #12318, #12319, #12320, #12321, #12870 | +| [@maxmad64bis](https://github.com/maxmad64bis) | #1622, #11435, #11437, #11441, #11537, #11550, #11553, #11555, #11557, #11812, #11842, #11843, #11903, #12151, #12214, #12215, #12218, #12226, #12314, #12316, #12317, #12318, #12319, #12320, #12321, #12507, #12715, #12731, #12744, #12786, #12787, #12788, #12790, #12792, #12794, #12795, #12828, #12832, #12853, #12857, #12870, #12937, #12941, #12975, #13141, #13142, #13146, #13147 | | [@mdigitalbh81](https://github.com/mdigitalbh81) | #12240 | | [@Meet6338-X](https://github.com/Meet6338-X) | #11598, #11609, #12110 | | [@morpheus9393](https://github.com/morpheus9393) | #11943 | @@ -925,11 +1160,11 @@ Thanks to everyone whose work landed in v3.8.51: | [@NightStalker-87](https://github.com/NightStalker-87) | #12183 | | [@NoxzRCW](https://github.com/NoxzRCW) | #11879, #11880, #11881, #11882, #11883, #11935 | | [@ntdat812](https://github.com/ntdat812) | #11585, #12095 | -| [@ntdatt812](https://github.com/ntdatt812) | #11368, #11573, #11574, #11576, #11577, #11580, #11582, #11583, #11588, #11589, #11590, #11591, #11592, #11593, #11672, #12177, #12180 | +| [@ntdatt812](https://github.com/ntdatt812) | #11368, #11573, #11574, #11576, #11577, #11580, #11582, #11583, #11588, #11589, #11590, #11591, #11592, #11593, #11672, #12177, #12180, #12873, #12918, #12920, #12921, #12925, #12930, #13007, #13009, #13024, #13025, #13083, #13087, #13101, #13104, #13110 | | [@official-burak](https://github.com/official-burak) | #11542 | | [@opensource-elearning](https://github.com/opensource-elearning) | #12179, #12189, #12278, #12286 | | [@oyi77](https://github.com/oyi77) | #11408, #11409, #11421, #11505, #11677, #12036, #12110 | -| [@pacocartones](https://github.com/pacocartones) | #11521, #11522, #11527, #11528, #11529, #11530, #11531, #11532, #11533, #11534, #11595, #11599, #11603, #11604, #11605, #11607, #11610, #11676, #11714, #11716, #11718, #11767, #11838, #11860, #11862, #11869, #11871, #11872, #11873, #11903, #11906, #11921, #11934, #12359, #12360, #12361, #12362, #12364, #12365, #12373, #12374, #12375, #12376, #12377, #12379, #12380, #12381, #12386, #12387, #12389, #12390, #12394, #12395, #12397, #12401, #12403, #12404 | +| [@pacocartones](https://github.com/pacocartones) | #11521, #11522, #11527, #11528, #11529, #11530, #11531, #11532, #11533, #11534, #11595, #11599, #11603, #11604, #11605, #11607, #11610, #11676, #11714, #11716, #11718, #11767, #11838, #11860, #11862, #11869, #11871, #11872, #11873, #11903, #11906, #11921, #11934, #12356, #12358, #12359, #12360, #12361, #12362, #12364, #12365, #12373, #12374, #12375, #12376, #12377, #12379, #12380, #12381, #12386, #12387, #12389, #12390, #12394, #12395, #12397, #12401, #12403, #12404, #12522, #12523, #12535, #12536, #12540, #12541, #12543, #12545, #12548, #12549, #12550, #12551, #12552, #12647, #12651, #12653, #13055 | | [@patrykkopycinski](https://github.com/patrykkopycinski) | #11936, #11937, #12224 | | [@PauloFH](https://github.com/PauloFH) | #11509 | | [@PauloHSOliveira](https://github.com/PauloHSOliveira) | #12241 | @@ -941,7 +1176,7 @@ Thanks to everyone whose work landed in v3.8.51: | [@raheemuddin786](https://github.com/raheemuddin786) | #11491, #11839, #11840, #11841, #12003, #12230, #12231, #12232, #12233, #12234 | | [@rahilmavani](https://github.com/rahilmavani) | #11761 | | [@Rahulsharma0810](https://github.com/Rahulsharma0810) | #11771 | -| [@RaviTharuma](https://github.com/RaviTharuma) | #11710, #11727, #11797, #11798, #11802, #11805, #11806, #11811, #12098, #12099, #12101, #12449, #12452, #12472, #12473, #12493, #12533, #12607, #12628, #12631, #12636 | +| [@RaviTharuma](https://github.com/RaviTharuma) | #11710, #11727, #11797, #11798, #11802, #11805, #11806, #11811, #12098, #12099, #12101, #12449, #12452, #12472, #12473, #12493, #12533, #12607, #12628, #12631, #12636, #12875, #12876, #12880, #12882, #12884 | | [@rezjalibd](https://github.com/rezjalibd) | #12186 | | [@RhianB14](https://github.com/RhianB14) | #11692 | | [@rifqiawl](https://github.com/rifqiawl) | #11517, #11519, #11969 | diff --git a/docs/i18n/ne/CHANGELOG.md b/docs/i18n/ne/CHANGELOG.md index e936b3f9fe..79f48e8b49 100644 --- a/docs/i18n/ne/CHANGELOG.md +++ b/docs/i18n/ne/CHANGELOG.md @@ -96,31 +96,31 @@ ## [3.8.51] — TBD -_Living section — reconciled 2026-09-07 from all cycle commits (`release/v3.8.50` tip `091589089c` → `release/v3.8.51` tip `d6f315018a`, 696 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._ +_Living section — reconciled 2026-09-15 from all cycle commits (`091589089c` → `c0f92ec98a`, 916 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 | **120** | -| 📝 Commits in the cycle | **696** | -| 🔀 Pull requests referenced | **672** | -| 📋 Changelog entries | **685** | -| 🙌 Contributors credited in entries | **118** | +| 👥 People who contributed | **123** | +| 📝 Commits in the cycle | **916** | +| 🔀 Pull requests referenced | **892** | +| 📋 Changelog entries | **919** | +| 🙌 Contributors credited in entries | **121** | | 🤖 Automated dependency commits | 18 | **Entries by type** | Type | Count | | --- | ---: | -| 🐛 Fixes | 403 | -| ✨ Features | 118 | -| 🧹 Chore | 72 | -| 📚 Docs | 36 | -| 🧪 Tests | 29 | +| 🐛 Fixes | 592 | +| ✨ Features | 131 | +| 🧹 Chore | 93 | +| 📚 Docs | 42 | +| 🧪 Tests | 32 | +| ♻️ Refactor | 8 | +| 🏗️ Build | 7 | | ⚡ Performance | 7 | -| ♻️ Refactor | 7 | -| 🏗️ Build | 6 | | ⚙️ CI | 2 | | ⏪ Reverts | 2 | | 📦 Dependencies | 2 | @@ -128,35 +128,35 @@ _Living section — reconciled 2026-09-07 from all cycle commits (`release/v3.8. ### 🏆 Top 25 contributors this cycle -_By commits in `release/v3.8.50..release/v3.8.51` (`091589089c..d6f315018a`), author identities consolidated via `.mailmap` and the merged PR's GitHub login. Bots excluded._ +_By commits in `091589089c..c0f92ec98a`, author identities consolidated via `.mailmap` and the merged PR's GitHub login. Bots excluded._ | # | Contributor | Commits | | ---: | --- | ---: | -| 🥇 | diegosouzapw | 240 | -| 🥈 | Paco Cartones (@pacocartones) | 58 | -| 🥉 | Bob.Hou (@HouMinXi) | 57 | -| 4 | Dizzle (@maxmad64bis) | 27 | -| 5 | Webman (@jonlwheat2-gif) | 22 | -| 6 | Ravi Tharuma (@RaviTharuma) | 21 | -| 7 | backryun | 17 | -| 8 | Markus Hartung (@hartmark) | 17 | -| 9 | Nguyen Thanh Dat (@ntdatt812) | 16 | -| 10 | Rafa Martins (@rafacpti23) | 10 | -| 11 | Syed Raheemuddin (@raheemuddin786) | 10 | -| 12 | Koosha Paridehpour (@KooshaPari) | 9 | -| 13 | Nguyễn Viết Tuấn (@TheDemonTuan) | 8 | -| 14 | MumuTW | 7 | -| 15 | Paijo (@oyi77) | 7 | -| 16 | Tobias Andersen (@turbolego) | 6 | -| 17 | Bl0ck (@Bl0ck154) | 5 | -| 18 | SHANMUGAPRIYAN (@geek007git) | 5 | -| 19 | KaspaPulse | 5 | -| 20 | Abhishek Sharma (@abhisheksharma2411) | 4 | -| 21 | Abhishek Divekar (@adivekar-utexas) | 4 | -| 22 | Andrew B. (@AndrianBalanescu) | 4 | -| 23 | NoxzRCW | 4 | -| 24 | opensource-elearning | 4 | -| 25 | Alvin T. Veroy (@alvinveroy) | 3 | +| 🥇 | diegosouzapw | 323 | +| 🥈 | Bob.Hou (@HouMinXi) | 79 | +| 🥉 | Paco Cartones (@pacocartones) | 77 | +| 4 | Dizzle (@maxmad64bis) | 49 | +| 5 | Koosha Paridehpour (@KooshaPari) | 43 | +| 6 | Nguyen Thanh Dat (@ntdatt812) | 31 | +| 7 | Ravi Tharuma (@RaviTharuma) | 26 | +| 8 | Markus Hartung (@hartmark) | 22 | +| 9 | Webman (@jonlwheat2-gif) | 22 | +| 10 | backryun | 17 | +| 11 | anhtahaylove | 15 | +| 12 | Rafa Martins (@rafacpti23) | 10 | +| 13 | Syed Raheemuddin (@raheemuddin786) | 10 | +| 14 | Nguyễn Viết Tuấn (@TheDemonTuan) | 8 | +| 15 | MumuTW | 7 | +| 16 | Paijo (@oyi77) | 7 | +| 17 | Tobias Andersen (@turbolego) | 6 | +| 18 | Bl0ck (@Bl0ck154) | 5 | +| 19 | SHANMUGAPRIYAN (@geek007git) | 5 | +| 20 | KaspaPulse | 5 | +| 21 | Abhishek Sharma (@abhisheksharma2411) | 4 | +| 22 | Abhishek Divekar (@adivekar-utexas) | 4 | +| 23 | Andrew B. (@AndrianBalanescu) | 4 | +| 24 | NoxzRCW | 4 | +| 25 | opensource-elearning | 4 | ### ✨ New Features @@ -282,6 +282,19 @@ _By commits in `release/v3.8.50..release/v3.8.51` (`091589089c..d6f315018a`), au - **feat(dashboard):** link a log entry's Conversation Context to its owning conversation ([#12646](https://github.com/diegosouzapw/OmniRoute/pull/12646)) — thanks @hartmark - **feat(catalog):** eligibility-gated free-tier bucket ([#12669](https://github.com/diegosouzapw/OmniRoute/pull/12669)) - **feat(db):** fail-closed previous_response_id continuation for redacted video turns (#12150 P2b) ([#12707](https://github.com/diegosouzapw/OmniRoute/pull/12707)) +- **feat(gamification): pay the documented `streak_bonus` and `badge_unlock` XP rewards.** `XP_REWARDS` listed both rewards but the award pipeline never paid them: the private reward table in `events.ts` omitted them, `updateStreak()` did not report when a streak extended, and badge unlocks carried no XP. Every request that extends a daily streak now pays `streak_bonus × streak length` once per UTC day (guarded by a same-day `xp_audit_log` check), and every badge unlocked through the pipeline pays `badge_unlock` once per badge (guarded by the `user_badges` primary key; `unlockBadge()` now reports whether it inserted). Bonus XP flows through the same `addXp` + level sync + global/weekly/monthly leaderboard path as action XP, so level-ups and rankings include it. The Radar supporter recognition unlock stays XP-free. (#12522 — thanks @pacocartones) +- **feat(providers):** advertise a `free-tier` capability in the provider plugin manifest for every provider with documented free models, so sidecars and dashboards can filter free-capable providers without reading the quota catalog ([#12786](https://github.com/diegosouzapw/OmniRoute/pull/12786)) — thanks @maxmad64bis +- **feat(providers):** Added EURouter as an OpenAI-compatible API-key gateway (`https://api.eurouter.ai/v1`), with live model discovery via `passthroughModels`. Its copy states that models are served by third-party upstreams listed per model, so an EU-based router is not read as EU data residency for inference. ([#13025](https://github.com/diegosouzapw/OmniRoute/pull/13025)) — thanks @ntdatt812 +- **feat(providers):** Added GreenPT as an OpenAI-compatible API-key provider (`https://api.greenpt.ai/v1`), with live model discovery via `passthroughModels`. No free-inference badge: the published docs describe a free API subscription billed per token, not a free tier. ([#13024](https://github.com/diegosouzapw/OmniRoute/pull/13024)) — thanks @ntdatt812 +- **feat(i18n):** 9 new locales — Greek (`el`), Croatian (`hr`), Serbian (`sr`), Lithuanian (`lt`), Estonian (`et`), Latvian (`lv`), Slovenian (`sl`), Maltese (`mt`), Irish (`ga`) — across the dashboard, docs mirrors, CLI, README and the site; OmniRoute now ships every official EU language (51 locales). (#13044) +- **feat(i18n):** 8 new locales — Kannada (`kn`), Malayalam (`ml`), Odia (`or`), Punjabi (`pa`), Nepali (`ne`), Sinhala (`si`), Burmese (`my`), Khmer (`km`) — across the dashboard, docs mirrors, CLI, README and the site (59 locales). The translator now restores the ICU literal escape around angle placeholders and splits oversized docs sections before translating. (#0000) ([#13660](https://github.com/diegosouzapw/OmniRoute/pull/13660)) +- feat(providers): **list Agnes 3.0 Flash as the current free chat model, drop retired 1.5 Flash, add Image 2.0/2.5 Flash plus Video 2.5/2.5 Flash, and discover the live `/v1/models` catalog (including the CN host `api.agnes-ai.cn`).** `agnes-1.5-flash` now forwards to `agnes-3.0-flash`. Video 2.5 polls `GET /v1/videos/{id}` (not the V2.0 `/agnesapi` contract). Live `/v1/models` (2026-09-09) no longer serves 1.5; the wiki marks it deprecated. 3.0 Flash is 512K context / 65,536 max output, same window as 2.5. CN-region keys use the existing per-connection base-URL field, default stays `apihub.agnes-ai.com`. ([#13120](https://github.com/diegosouzapw/OmniRoute/pull/13120)) — thanks @HouMinXi +- **feat(sse):** Codex and OpenAI catalogs list GPT-6 Astra with effort aliases (`-low` through `-ultra`); Codex CLI identity pins `@openai/codex@0.153.4` in lockstep with the image ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **feat(dashboard):** the orchestration History tab gained a "Compare runs" mode — toggling it turns each grid cell into a 2-item selection queue (a 3rd click drops the oldest pick), and picking two cells opens a side-by-side comparison panel instead of the usual detail drawer. The panel fetches both runs' detail the same way the drawer does (falling back to persisted history once a run leaves the live TTL window) and shows, per side: identity/source/state, start time, a signed `right - left` delta for duration/cost/event count, the event timeline aligned by index, and any memory hits. One side's fetch failing never blocks the other, and a delta is only ever computed when both sides have a finite value — otherwise it renders "—", never `NaN`. Comparing two runs from different sources or skills still works; a banner marks the deltas as informational rather than hiding them, since the two runs aren't a strict apples-to-apples pair. ([#12677](https://github.com/diegosouzapw/OmniRoute/pull/12677)) +- **feat(dashboard):** badge a conversation that never reached a clean stop ([#12717](https://github.com/diegosouzapw/OmniRoute/pull/12717)) — thanks @hartmark +- **feat(release):** reconcile-changelog tool + version-anchored fragment aggregation ([#12987](https://github.com/diegosouzapw/OmniRoute/pull/12987)) +- **feat(mcp):** carry context_length through MCP list_models_catalog projection (#12776) ([#13406](https://github.com/diegosouzapw/OmniRoute/pull/13406)) — thanks @KooshaPari +- **feat(cli):** make startup readiness budget configurable (#13369) ([#13433](https://github.com/diegosouzapw/OmniRoute/pull/13433)) — thanks @KooshaPari ### 🐛 Bug Fixes @@ -698,6 +711,194 @@ _By commits in `release/v3.8.50..release/v3.8.51` (`091589089c..d6f315018a`), au - **fix(i18n):** quote placeholder in OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES description (#12505) ([#12769](https://github.com/diegosouzapw/OmniRoute/pull/12769)) — thanks @KooshaPari - **fix(api/pricing):** surface validation error message as string, not raw object (#12494) ([#12771](https://github.com/diegosouzapw/OmniRoute/pull/12771)) — thanks @KooshaPari - **fix(sse):** re-enable prompt compression for native Codex passthrough ([#12834](https://github.com/diegosouzapw/OmniRoute/pull/12834)) — thanks @thomasmaerz +- **fix(dashboard):** model health tests for a provider node set to the Responses API now call `/v1/responses` with a Responses-shaped body instead of `/v1/chat/completions` — those models were reported as `Provider returned HTTP 200 but no text content` even though the same model answered normally through `/v1/responses` ([#13070](https://github.com/diegosouzapw/OmniRoute/issues/13070)) ([#13087](https://github.com/diegosouzapw/OmniRoute/pull/13087)) — thanks @ntdatt812 +- fix(routing): stop a round-robin combo's "opencode" targets from collapsing onto the opencode-zen connection (#11912) ([#13283](https://github.com/diegosouzapw/OmniRoute/pull/13283)) +- fix(routing): stop the reactive-compaction debug log from lying when compression is globally disabled (#11977) ([#13276](https://github.com/diegosouzapw/OmniRoute/pull/13276)) +- fix(electron): relativize standalone-bundle symlink targets so Stage 8 manifest verification stops failing on Windows (#11979) ([#13251](https://github.com/diegosouzapw/OmniRoute/pull/13251)) +- fix(dashboard): surface a visible error when Compression Studio's combined preview run fails (#12061) ([#13288](https://github.com/diegosouzapw/OmniRoute/pull/13288)) +- fix(dashboard): make the compression "Effective pipeline" preview honor the active profile and warn when the master switch is off (#12063) ([#13287](https://github.com/diegosouzapw/OmniRoute/pull/13287)) +- fix(providers): scope TinyCMS Web signer's DOM shims to each call instead of leaking them for the process lifetime, and surface a clean HTTP status on a non-JSON interception-toggles error (#12072) ([#13254](https://github.com/diegosouzapw/OmniRoute/pull/13254)) +- fix(guardrails): stop Vision Bridge from re-selecting a model locked after a 404 (#12111) ([#13259](https://github.com/diegosouzapw/OmniRoute/pull/13259)) +- fix(sse): require Responses-shaped body before native OpenAI-compatible passthrough (#12129) ([#13278](https://github.com/diegosouzapw/OmniRoute/pull/13278)) +- fix(providers): minimax-m3 now collapses manual thinking.type:"enabled" to adaptive, preventing upstream 400 (2013) (#12132) ([#13249](https://github.com/diegosouzapw/OmniRoute/pull/13249)) +- fix(db): scope model visibility overrides by modality so hiding a Chat model no longer hides an identically-ID'd Image/Embeddings/etc. model (#12172) ([#13258](https://github.com/diegosouzapw/OmniRoute/pull/13258)) +- fix(db): scope local-provider apiKey dedup to matching base URL so LM Studio/Ollama-style connections support multiple accounts (#12173) ([#13250](https://github.com/diegosouzapw/OmniRoute/pull/13250)) +- fix(providers): refresh Trae's stale Referer/Origin and forward user timezone so imported connections stop failing with 401 (#12190) ([#13255](https://github.com/diegosouzapw/OmniRoute/pull/13255)) +- fix(providers): route opencode-go/gpt-5.6-luna to /responses instead of /chat/completions (#12196) ([#13275](https://github.com/diegosouzapw/OmniRoute/pull/13275)) +- fix(dashboard): allow deleting the last extra-upstream-header row even when invalid (#12251) ([#13252](https://github.com/diegosouzapw/OmniRoute/pull/13252)) +- **fix(i18n):** translate pre-existing `__MISSING__:` keys for `combo.sort`, `requestLogger.detail` expand/collapse, `common.profile`, and `settings.resilienceCredentialHealth*` across 39 locales ([#12272](https://github.com/diegosouzapw/OmniRoute/issues/12272)) ([#13034](https://github.com/diegosouzapw/OmniRoute/pull/13034)) — thanks @HouMinXi +- fix(cli): run the Node.js runtime compatibility guard before the heavy `tsx/esm` + Commander import chain so an unsupported runtime gets a clear message instead of a raw `Invalid regular expression flags` crash (#12296) ([#13253](https://github.com/diegosouzapw/OmniRoute/pull/13253)) +- fix(dashboard): refresh the providers list after deleting a compatible provider node (#12298) ([#13256](https://github.com/diegosouzapw/OmniRoute/pull/13256)) +- fix(usage): fail closed on API-key budget enforcement when a provider's `auto` routing alias has no pricing row, instead of silently counting it as $0 (#12341) ([#13257](https://github.com/diegosouzapw/OmniRoute/pull/13257)) +- **fix(providers):** include the submitted Agnes video model when polling by `video_id` ([#12356](https://github.com/diegosouzapw/OmniRoute/pull/12356)) — thanks @pacocartones +- **fix(routing):** custom OpenAI-compatible nodes now honor the saved Chat/Responses API type after edits instead of letting the node's original ID prefix override the live connection setting ([#11884](https://github.com/diegosouzapw/OmniRoute/issues/11884)). ([#12358](https://github.com/diegosouzapw/OmniRoute/pull/12358)) — thanks @pacocartones +- fix(sse): surface an error instead of a silent empty 200 when a Claude stream closes with zero bytes (#12398) ([#13285](https://github.com/diegosouzapw/OmniRoute/pull/13285)) +- fix(oauth): warn before the dead localhost:8080 redirect in antigravity/gemini `oauth start` (#12413) ([#13265](https://github.com/diegosouzapw/OmniRoute/pull/13265)) +- fix(providers): stop devin-cli spawn error from double-closing the SSE controller (#12517) ([#13261](https://github.com/diegosouzapw/OmniRoute/pull/13261)) +- **fix(rerank):** clamp Voyage `top_k` to the documents actually sent after empty-string filtering, and honor `return_documents: false` in the NVIDIA response adapter (#12523 — thanks @pacocartones) +- **fix(audio):** `/v1/audio/translations` now resolves combo names the way `/v1/audio/transcriptions` already does, so a combo that `GET /v1/models` advertises is fanned out to its targets instead of being rejected with `400 Invalid translation model: . Use format: provider/model`; literal `provider/model` ids and unknown bare names behave as before (#12536 — thanks @pacocartones) +- **fix(gemini):** strip the JSON-Schema-2020-12 `prefixItems` keyword from Gemini tool schemas at every nesting level, so Claude Code tool definitions no longer fail with `400 Unknown name "prefixItems"` on Gemini models (#12540 — thanks @pacocartones) +- **fix(api-manager):** Expose an accessible loading status while API keys are fetched instead of an empty accessibility tree (#12541 — thanks @pacocartones) +- **fix(video):** Clamp `estimateJpegFrameBytes` at zero for padding-only payloads and build the three encode-side frame data URIs from `JPEG_FRAME_DATA_URI_PREFIX` instead of a repeated literal (#12543 — thanks @pacocartones) +- **fix(devin):** accept Windows `DEVIN_AGENTIC_HOME` sandbox paths (`C:\...\.sandbox\...`) in the isolated-home check so the Devin Claude Bridge no longer fails closed on Windows ([#12405](https://github.com/diegosouzapw/OmniRoute/issues/12405)) (#12545 — thanks @pacocartones) +- **fix(db):** Add `conversation_turn_nodes` and orphaned `agentic_conversations` to the auto-cleanup cycle under the existing `retention.callLogs` window, so identity nodes whose call-log content has already been purged no longer accumulate without bound in `storage.sqlite` (#12548 — thanks @pacocartones) +- **fix(i18n):** Wrap the `~/.claude/profiles//settings.json` placeholder in ICU single quotes in the `OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES` feature-flag description across all 42 locales and the TypeScript default, so next-intl no longer fails with `INVALID_MESSAGE: UNCLOSED_TAG` and the Feature Flags card shows the description instead of the raw key (#12549 — thanks @pacocartones) +- **fix(orchestration):** `updateCloudAgentTask` now publishes the task's real `status` on `agent.task.updated` when an update only touches `result`, `activities` or `error`, instead of the fabricated `"updated"` state, and stays silent when no row matched the id (#12550 — thanks @pacocartones) +- **fix(i18n):** the home "Recent Requests" panel and the Provider Topology legend are now translated instead of rendering English copies on non-English dashboards; the legend reads its own `home.topologyLegend*` labels with consistent casing rather than borrowing the memory-settings "Recent" and analytics "Error" strings (#12551 — thanks @pacocartones). +- **docs(reference):** bring the `FEATURE_FLAGS.md` catalog back to 1:1 with `featureFlagDefinitions.ts` — 20 missing flags added, the two `*_BLOCK_THRESHOLD` env-only knobs moved out of the flag tables, category/total counts and the Live WS port corrected, guarded by a static test (#12552 — thanks @pacocartones) +- **fix(i18n):** backfill missing `usage.kiloPass*` strings in 39 locales and restore `featureFlagOmnirouteDisableThinkingLevelVariantsDescription` in `pt.json` ([#12561](https://github.com/diegosouzapw/OmniRoute/issues/12561)) ([#13027](https://github.com/diegosouzapw/OmniRoute/pull/13027)) — thanks @HouMinXi +- fix(docker): default docker-compose app ports (dashboard/API/live-WS) to loopback instead of `0.0.0.0`, closing the anonymous `/v1` LAN/WAN exposure gap left open by `REQUIRE_API_KEY=false` (#12568) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): close DNS-rebinding SSRF gap in webhook outbound-URL guard (#12569) ([#13243](https://github.com/diegosouzapw/OmniRoute/pull/13243)) +- fix(docker): require a per-session token on the VNC browser CDP bridge and isolate it on a dedicated Docker network (#12571) ([#13241](https://github.com/diegosouzapw/OmniRoute/pull/13241)) +- fix(open-sse): write Adobe Firefly session tokens and cookie jars with 0700/0600 permissions instead of the process umask (#12572) ([#13242](https://github.com/diegosouzapw/OmniRoute/pull/13242)) +- fix(api): remove hardcoded wildcard CORS in openai-to-gemini-sse.ts so the centralized fail-closed CORS gate is the sole source of `Access-Control-Allow-Origin` (#12573) ([#13239](https://github.com/diegosouzapw/OmniRoute/pull/13239)) +- fix(api): enforce API key policy (budget/rate-limit/schedule/endpoint scoping) on the ElevenLabs speech-to-text, text-to-speech and voices proxy routes (#12574) ([#13244](https://github.com/diegosouzapw/OmniRoute/pull/13244)) +- fix(sse): cap HuggingChat NDJSON body size and bound the read loop with the fetch timeout so a stalled or hostile upstream cannot buffer unbounded memory (#12577) ([#13282](https://github.com/diegosouzapw/OmniRoute/pull/13282)) +- fix(docker): scope the cliproxyapi/qdrant/bifrost sidecars to loopback by default and forward `CLIPROXYAPI_MANAGEMENT_KEY` into the cliproxyapi container so its management API is not left both unauthenticated and LAN-published (#12578) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): create DB export temp paths with `fs.mkdtempSync` instead of predictable timestamps (#12579) ([#13237](https://github.com/diegosouzapw/OmniRoute/pull/13237)) +- Cline 401 bodies that say "re-authenticate your Cline account" classify as a refreshable OAuth token, not a terminal expired key. The cooling panel no longer labels every cooldown as a 429; it shows the recorded last error instead. (#12594) ([#13060](https://github.com/diegosouzapw/OmniRoute/pull/13060)) — thanks @HouMinXi +- **fix(catalog):** degrade unknown combo targets instead of dropping LCD modalities, and persist OpenRouter `architecture.input_modalities` into the capability snapshot ([#12613](https://github.com/diegosouzapw/OmniRoute/issues/12613)) ([#13042](https://github.com/diegosouzapw/OmniRoute/pull/13042)) — thanks @HouMinXi +- fix(providers): send `x-api-key` instead of `Authorization: Bearer` for OpenCode Zen's `/v1/responses` endpoint (Muse Spark Contributor models), fixing a 401 on OmniRoute's auth header (#12633) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(sse): surface the actionable "Auggie CLI not found" message when the shell reports a missing `auggie` binary via exit code instead of a spawn error (#12645) ([#13263](https://github.com/diegosouzapw/OmniRoute/pull/13263)) +- **fix(config):** Persist deletions of built-in background-degradation entries — when a stored settings record exists its `degradationMap` is now authoritative instead of being merged under the defaults, so an entry the user removed in the dashboard no longer reappears on the next apply or restart ([#12424](https://github.com/diegosouzapw/OmniRoute/issues/12424)) ([#12647](https://github.com/diegosouzapw/OmniRoute/pull/12647)) — thanks @pacocartones +- **fix(gamification):** action-count badge milestones (First Token, Token Consumer, Token Machine, Token Whale, and the token-sharing tier) are now backed by a durable `xp_action_counts` counter incremented in `addXp()`, instead of a live `COUNT(*)` over `xp_audit_log`. The audit log is pruned by `retention.xpAuditLog` (default 30 days), so on a default install those "lifetime" milestones were really "actions in the last 30 days" and unlocked badges could stop unlocking once old rows aged out. `getActionCount()` and `checkActionCountBadges()` now read the same durable source, and a migration backfills existing totals from the surviving audit rows ([#12546](https://github.com/diegosouzapw/OmniRoute/issues/12546)) ([#12651](https://github.com/diegosouzapw/OmniRoute/pull/12651)) — thanks @pacocartones +- **fix(images):** `/v1/images/edits` now iterates a combo's targets the same way `/v1/images/generations` does (#9239) instead of flattening a bare combo to its first target. A combo whose first target is not edit-capable — or lacks credentials — now falls through to a later edit-capable target rather than hard-erroring, and missing credentials are skipped (not a hard `401`) to match the generations path. The per-target skip/terminal classification is extracted into a shared `runImageComboTargets` loop, so generations behavior is unchanged ([#12547](https://github.com/diegosouzapw/OmniRoute/issues/12547)). ([#12653](https://github.com/diegosouzapw/OmniRoute/pull/12653)) — thanks @pacocartones +- fix(sse): add first-byte watchdog to the TLS-fingerprint transport so a stalled wreq body falls back instead of hanging for minutes (#12656) ([#13272](https://github.com/diegosouzapw/OmniRoute/pull/13272)) +- fix(sse): exempt tiny-budget reasoning probes from combo quality failure and surface persisted-cooldown skips in ALL_TARGETS_SKIPPED diagnostics (#12659) ([#13269](https://github.com/diegosouzapw/OmniRoute/pull/13269)) +- fix(models): declare the real ~1M contextLength for OpenCode Zen's Muse Spark 1.2 models instead of falling back to the 200000 provider default (#12681) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(oauth): align codebuddy-cn OAuth User-Agent with the chat/usage CLI version to avoid WAF false positives (#12702) ([#13264](https://github.com/diegosouzapw/OmniRoute/pull/13264)) +- fix(dashboard): surface an authentication-required banner instead of silently blanking database settings for a guest session (#12709) ([#13270](https://github.com/diegosouzapw/OmniRoute/pull/13270)) +- **fix(resilience):** stop unbounded queue that hangs 6min until Aborted — gate, provider slot, and Bottleneck queue now share a per-connection `maxWaitMs` budget; `fail-closed` on exhaust (503); execution backstop `executionMaxWaitMs` overridable per connection with upstream clamp ([#12715](https://github.com/diegosouzapw/OmniRoute/pull/12715)) — thanks @maxmad64bis (with thanks to @Tushar49 for surfacing the slow-provider need in #12635) +- **fix(auto-combo):** every mode pack now carries `quality` and `reliability` weights (`quality` 0.02, 0.03 in `quality-first`; `reliability` 0.03, 0.04 in `reliability-first`), so selecting a pack no longer silences either signal; `DEFAULT_WEIGHTS` is unchanged ([#12731](https://github.com/diegosouzapw/OmniRoute/pull/12731)) — thanks @maxmad64bis +- **fix(sse):** the provider execution pipeline keeps the upstream error `code`/`type` again — both were lost when the non-streaming leg was extracted, so a config-class `422` (Antigravity missing project) degraded into a generic account cooldown instead of reaching the gate that recognises it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(db):** a legacy `call_logs` table no longer aborts startup — the provider-stats index is created after the column healing runs, not before it, so an install predating `request_type` opens instead of failing with `no such column` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(catalog):** a malformed row in the operator-writable custom-models blob no longer kills every `auto/*` pool ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(tests):** realign the guards that the same day's merges left asserting the old behavior — the provider-count total after GreenPT and EURouter, the GLM stream buffer slot that `#12925` turned into a declared parameter, and the injection scan bound that `#13104` reshaped from a head-only window into head-plus-tail ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- fix(cache): fold tool_choice/tools/response_format into the semantic cache signature so a cached tool_calls response can no longer be replayed for a request whose tool policy forbids it (#12734) ([#13267](https://github.com/diegosouzapw/OmniRoute/pull/13267)) +- **fix(models):** stop treating provider-supplied `isFree:true`/`:free`/`0/0` flags as trusted unless the provider has a documented free tier — only the shipped free-tier catalog decides otherwise; fetched `isFree:true` trusted only for a free-tier provider, custom `isFree:true` via a trusted path ([#12744](https://github.com/diegosouzapw/OmniRoute/pull/12744)) — thanks @maxmad64bis +- fix(memory): authenticate the internal /v1/rerank loopback call so memory reranking no longer silently degrades to unranked order when REQUIRE_API_KEY=true (#12745) ([#13268](https://github.com/diegosouzapw/OmniRoute/pull/13268)) +- fix(sse): parse Ollama Cloud's current usage markup (`$X of $Y used` aria-label, nested width style) (#12749) ([#13260](https://github.com/diegosouzapw/OmniRoute/pull/13260)) +- fix(cli): setup-opencode no longer sends an active context's management token to `/v1/models` when `--api-key`/`OMNIROUTE_API_KEY` is supplied — an explicit flag or the env var now always outranks the context's token, and the flag itself is no longer swallowed by the parent program's global `--api-key` option (#12783) ([#13246](https://github.com/diegosouzapw/OmniRoute/pull/13246)) +- fix(sse): register Arcee AI in the executor provider registry so requests reach api.arcee.ai instead of silently falling back to OpenAI (#12784) ([#13277](https://github.com/diegosouzapw/OmniRoute/pull/13277)) +- **fix(quota):** share one upstream quota read when identical saturation checks arrive at the same time, so traffic bursts don't multiply provider API calls ([#12787](https://github.com/diegosouzapw/OmniRoute/pull/12787)) — thanks @maxmad64bis +- **fix(db):** ignore expired or invalid rate-limit cooldown writes so a stale timestamp can't lock a connection that should be usable — clearing still works as before ([#12788](https://github.com/diegosouzapw/OmniRoute/pull/12788)) — thanks @maxmad64bis +- **fix(providers):** single source for provider order with `xao` ranking alongside `xai-oauth` ([#12790](https://github.com/diegosouzapw/OmniRoute/pull/12790)) — thanks @maxmad64bis +- **fix(routing):** off-table models check the free-model catalog before inheriting premium prices, latency bootstraps from the observed pool median, async tiers read live database pricing with a 90-day freshness gate, and the tier cache invalidates on every pricing write ([#12792](https://github.com/diegosouzapw/OmniRoute/pull/12792)) — thanks @maxmad64bis +- **fix(auto-combo):** snapshot scoring uses the observed per-connection account tier and per-model quality instead of neutral constants, and circuit-open providers rank lower at build time ([#12794](https://github.com/diegosouzapw/OmniRoute/pull/12794)) — thanks @maxmad64bis +- **fix(pool):** empty `auto/*` pools now say why they are empty, and the models listing uses the same paid check as routing ([#12795](https://github.com/diegosouzapw/OmniRoute/pull/12795)) — thanks @maxmad64bis +- fix(routing): recognize CLIProxyAPI's 'unknown provider for model' 400 as fallback-worthy (#12800) ([#13284](https://github.com/diegosouzapw/OmniRoute/pull/13284)) +- **fix(sse):** translate-mode streams now emit the estimated token counts as a trailing usage-only chunk before `[DONE]` when the upstream stays silent, so metered chat clients see totals instead of nothing ([#12828](https://github.com/diegosouzapw/OmniRoute/pull/12828)) — thanks @maxmad64bis +- **fix(db):** provider stats stay truthful on empty and legacy databases: fallback counts default to `0` instead of `null`, latency averages read `null` (not `0`) when no durations were recorded, failures logged before the error-type column existed group under `pre_migration` instead of `unclassified`, and per-provider queries use two new composite indexes ([#12832](https://github.com/diegosouzapw/OmniRoute/pull/12832)) — thanks @maxmad64bis +- fix(nvidia): fail open when a synced model catalog goes stale instead of gating forever (#12849) ([#13248](https://github.com/diegosouzapw/OmniRoute/pull/13248)) +- **fix(db):** WAL maintenance lives in its own module: a `TRUNCATE` checkpoint that hits a busy database now warns with its streak and retries once via `PASSIVE` instead of logging success, and closing-time checkpoints no longer report success on builds without a database file, with the busy totals visible in the authenticated monitoring health payload ([#12853](https://github.com/diegosouzapw/OmniRoute/pull/12853)) — thanks @maxmad64bis +- **fix(health):** quota with no snapshots reads empty instead of a contradicting 0% ([#12857](https://github.com/diegosouzapw/OmniRoute/pull/12857)) — thanks @maxmad64bis +- fix(a2a): accept the dashboard's own session cookie on /a2a so "Run message/send" no longer fails with "Unauthorized: missing or invalid API key" (#12888) ([#13271](https://github.com/diegosouzapw/OmniRoute/pull/13271)) +- **fix(logs):** the Logs grid's in-memory filter pass no longer discards rows the SQL query already matched — selecting an API key from the dropdown (which sends the key's id) returns its calls again, the Combo tab shows every combo instead of only those whose name contains a "1", and the model filter and search cover the same columns as the query ([#12896](https://github.com/diegosouzapw/OmniRoute/pull/12896)) — fixes [#12873](https://github.com/diegosouzapw/OmniRoute/issues/12873) — thanks @ntdatt812 +- **fix(a2a):** `/api/a2a/status` now builds the agent card from the request that asked for it, so a gateway reached at a non-localhost host no longer advertises `http://localhost:20128` as its A2A URL ([#12918](https://github.com/diegosouzapw/OmniRoute/pull/12918)). — thanks @ntdatt812 +- **fix(compression):** progressive aging now appends its `[COMPRESSED:aging:…]` annotation after a turn's `tool_result` blocks instead of in front of them, so Anthropic no longer rejects aged conversations with "`tool_use` ids were found without `tool_result` blocks immediately after" ([#12920](https://github.com/diegosouzapw/OmniRoute/pull/12920)). — thanks @ntdatt812 +- **fix(bedrock):** model import now resolves context limits for every vendor prefix instead of only `anthropic.*`, so `global.openai.gpt-5.6-*` no longer imports with a null `inputTokenLimit` and gets rejected pre-flight at the 200k default ([#12921](https://github.com/diegosouzapw/OmniRoute/pull/12921)). — thanks @ntdatt812 +- **fix(stream):** the 64 KB stream buffer GLM asks for is honoured instead of dropped, and the type error it caused no longer fails the API Route Typecheck gate on every open PR ([#12925](https://github.com/diegosouzapw/OmniRoute/pull/12925)) — thanks @ntdatt812 +- **fix(guardrails):** mask PII inside a `tool_result`'s nested content array, which the masker walked past while redacting its sibling block ([#12930](https://github.com/diegosouzapw/OmniRoute/pull/12930)) — thanks @ntdatt812 +- **fix(dashboard):** the Radar catalog table no longer leaves absent data unexplained — empty limits, unknown context windows, and unreported capabilities each explain themselves on hover, and a new check keeps it that way ([#12937](https://github.com/diegosouzapw/OmniRoute/pull/12937)) — thanks @maxmad64bis +- **fix(sse):** geo-blocked opencode requests rotate to the next account proxy instead of failing, so one refused egress no longer aborts the whole chain ([#12941](https://github.com/diegosouzapw/OmniRoute/pull/12941)) — thanks @maxmad64bis +- **fix(codex):** dashboard "clear cooldown" and the CAS recovery path now drop nested `codexScopeRateLimitedUntil` maps in the same write that nulls `rate_limited_until`, and fresh quota snapshots with headroom lift fallback-sourced scope cooldowns parked by quota preflight ([#12817](https://github.com/diegosouzapw/OmniRoute/issues/12817), [#12860](https://github.com/diegosouzapw/OmniRoute/issues/12860), [#12951](https://github.com/diegosouzapw/OmniRoute/pull/12951)) The dashboard reset-credit button (`consumeCodexResetCredit`) now exercises that same snapshot path after a successful redeem, so a filled quota bar is enough to unpark a leftover fallback Codex child without another manual clear. — thanks @HouMinXi +- **fix(db):** resolve `sql-wasm.wasm` across global npm install and hoisted layouts, ensuring OmniRoute can boot cleanly on Node 24 when native `better-sqlite3` is uncompiled. ([#13035](https://github.com/diegosouzapw/OmniRoute/pull/13035)) — thanks @HouMinXi +- **fix(api):** `DELETE /v1/batches/delete-completed` now sweeps only the calling API key's own completed batches (batches with no owner stay out of a key-scoped sweep on purpose), with an explicit instance-wide mode reserved for authenticated dashboard sessions, a 401 for a presented key that is unknown, revoked, deactivated, banned or expired (never falling through to the session branch), audit logging of every sweep, a sanitized 500 on failure, an owner-scoped file half (a key-scoped sweep never nulls a file another tenant owns) and an atomic sweep — chunked in 200-batch transactions in instance mode — so a mid-way error never leaves a batch pointing at a nulled file (GHSA-wvxc-jp3v-5mg5) ([#12969](https://github.com/diegosouzapw/OmniRoute/pull/12969)) +- **fix(combo):** quota-weighted routing stops drawing on an out-of-credit connection — a 402 now invalidates the stored quota snapshot instead of leaving its stale remaining percentage in place, and a snapshot older than 10 minutes no longer counts as confident headroom for the primary pool ([#12972](https://github.com/diegosouzapw/OmniRoute/pull/12972)) ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- **fix(vision):** Custom Models with "Vision capable" checked no longer have image requests swapped to `glm/glm-4.6v` when the client sends the advertised alias (`vllm/path/...`) or the bare path-shaped id — Vision Bridge now matches the stored override for all three id forms ([#12758](https://github.com/diegosouzapw/OmniRoute/issues/12758)) ([#12974](https://github.com/diegosouzapw/OmniRoute/pull/12974)) — thanks @HouMinXi +- **fix(sse):** transient opencode upstream failures rotate to the next account proxy instead of failing, so one flapping egress no longer aborts the whole chain ([#12975](https://github.com/diegosouzapw/OmniRoute/pull/12975)) — thanks @maxmad64bis +- **fix(azure):** Deployments from GPT-6 onward now send `max_completion_tokens` instead of `max_tokens`, which Azure rejects with HTTP 400. The rule matched a literal `gpt-5`, so each new generation arrived broken; it now matches the generation range, while `gpt-35-turbo` still keeps `max_tokens`. ([#13007](https://github.com/diegosouzapw/OmniRoute/pull/13007)) — thanks @ntdatt812 +- **fix(db):** add an opt-in self-restart circuit for sustained critical memory pressure, gate post-cleanup VACUUM behind a minimum freed-rows threshold, and checkpoint the SQLite WAL every 5 minutes with a size guard that escalates to TRUNCATE, so a growing WAL can no longer stall the event loop into a full outage. ([#13011](https://github.com/diegosouzapw/OmniRoute/pull/13011)) — thanks @HouMinXi +- **fix(auth):** an explicit connection pin may probe a quota-disabled row once and re-enable it on success ([#12874](https://github.com/diegosouzapw/OmniRoute/issues/12874)) ([#13017](https://github.com/diegosouzapw/OmniRoute/pull/13017)) — thanks @HouMinXi +- **fix(api):** thread `X-OmniRoute-Fallback-Attempts` through combo chat completions so streaming and non-streaming responses report how many prior legs were attempted ([#13038](https://github.com/diegosouzapw/OmniRoute/pull/13038)) — thanks @HouMinXi +- Restore provider failure classification and credential refresh on non-streaming requests: classify non-2xx failures to lock models on per-model quota exhaustion, update connection rate limits from headers and body, and pass credential refresh handlers to pipeline execution so 401 tokens can be refreshed and retried (#13043). ([#13069](https://github.com/diegosouzapw/OmniRoute/pull/13069)) — thanks @HouMinXi +- **fix(responses):** wrap forced-non-streaming web_search fallback JSON as Responses SSE so Codex still sees `response.completed` ([#13050](https://github.com/diegosouzapw/OmniRoute/pull/13050)) — thanks @HouMinXi +- **fix(antigravity):** Preserve upstream thought-token usage in normalized completion and reasoning token counts ([#13055](https://github.com/diegosouzapw/OmniRoute/pull/13055)) — thanks @pacocartones +- Fix a concurrency-slot leak in the DeepSeek PoW solver: a worker that failed to spawn (for example a missing worker script) never released its slot, so `MAX_CONCURRENT_WORKERS` failures disabled the solver until restart. ([#13097](https://github.com/diegosouzapw/OmniRoute/pull/13097)) — thanks @anhtahaylove +- **fix(acp):** bound the ACP session output buffers — `stdoutBuffer` and `stderrBuffer` now cap at 1 MiB keeping the most recent output behind a visible `[...output truncated...]` marker, and `stderrBuffer` is reset per prompt instead of accumulating for the lifetime of the session. ([#13100](https://github.com/diegosouzapw/OmniRoute/pull/13100)) — thanks @anhtahaylove +- **fix(acp):** release the `stdout`/`exit` listeners and the idle timer that a `sendPrompt` timeout used to leave attached to the `acpManager` singleton, and drop sessions that exited on their own from the session map instead of keeping them forever. ([#13096](https://github.com/diegosouzapw/OmniRoute/pull/13096)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection and PII scanners now read the text a `tool_result` block carries on `content` (string or nested block list), in messages and in system blocks, so tool output is judged by the same rules as user text ([#13101](https://github.com/diegosouzapw/OmniRoute/pull/13101)) — thanks @ntdatt812 +- **fix(gamification):** close the badge notification SSE stream when the request signal is already aborted before the stream starts — a client that disconnects while the route is still awaiting auth used to leave both the 2s unlock poll and the 15s heartbeat running for the lifetime of the process. ([#13106](https://github.com/diegosouzapw/OmniRoute/pull/13106)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection scan now spends its 16 KB budget on both ends of the request instead of the first 16 KB only, so `system`, `instructions`, `query`, `documents` and the newest turns are no longer hidden behind one long message ([#13104](https://github.com/diegosouzapw/OmniRoute/pull/13104)) — thanks @ntdatt812 +- **fix(dashboard):** expose the Volcano Ark console cookie on quota scraping and unwrap connect-error objects so the dashboard shows the upstream message ([#13107](https://github.com/diegosouzapw/OmniRoute/pull/13107)) — thanks @HouMinXi +- **fix(db):** release the `beforeExit`/`SIGINT`/`SIGTERM` handlers when a `node:sqlite` adapter closes, so a closed adapter and its database handle are no longer pinned to `process` for the lifetime of the run — the same treatment #7494 gave the sql.js adapter. ([#13109](https://github.com/diegosouzapw/OmniRoute/pull/13109)) — thanks @anhtahaylove +- **fix(translator):** `contentSchema` and `unevaluatedItems` are now treated as subschema positions by the tool-schema sanitizer, so a truncation placeholder in either is replaced with a permissive schema instead of being forwarded as a string ([#13110](https://github.com/diegosouzapw/OmniRoute/pull/13110)) — thanks @ntdatt812 +- **fix(cli-helper):** clear the `createLogStream` timeout on the abort path — `stop()` aborts the in-flight fetch and returned through the `signal.aborted` branch, which skipped `clearTimeout` and left an armed timer per stopped stream. The stream reader is now also cancelled when the read loop exits early. ([#13114](https://github.com/diegosouzapw/OmniRoute/pull/13114)) — thanks @anhtahaylove +- **fix(docs):** drop TLS-impersonation, MITM-decrypt, supply-chain attestation, and XOR-mask writeups from the public `/docs` catalog and Docker image. Files stay in git for engineers; operators who need them open the repo, not the website. ([#13136](https://github.com/diegosouzapw/OmniRoute/pull/13136)) — thanks @HouMinXi +- **fix(combo):** parse numeric-epoch `rate_limited_until` in the combo cooldown read path ([#13141](https://github.com/diegosouzapw/OmniRoute/pull/13141)) — thanks @maxmad64bis +- **fix(opencode):** both OpenCode plugins now reject a gateway address typed without `http://` at configuration time, instead of publishing every model with an api url no client can call, and the v2 plugin no longer publishes a model card whose api url is blank or relative ([#13142](https://github.com/diegosouzapw/OmniRoute/pull/13142)) — thanks @maxmad64bis +- **fix(providers):** lock opencode model on upstream 400 model-unavailable ([#13146](https://github.com/diegosouzapw/OmniRoute/pull/13146)) — thanks @maxmad64bis +- **fix(logging):** keep the provider exchange rather than the raw client bodies when a call log exceeds its size budget, and show that recovered payload in the request-detail panel instead of replacing it with the stored response body ([#13147](https://github.com/diegosouzapw/OmniRoute/pull/13147)) — thanks @maxmad64bis +- **fix(traffic-inspector):** the WebSocket route no longer leaks a traffic-buffer subscriber and a 30s ping timer when the client socket is already closed at handler time — listeners are attached before any resource is acquired, a destroyed socket bails out early, and the ping interval stops on a dead socket where `write()` never throws ([#13155](https://github.com/diegosouzapw/OmniRoute/pull/13155)) — thanks @anhtahaylove +- **fix(telegram):** bound the per-user API key cache in the Telegram chat proxy so a burst of distinct chat ids can no longer grow the process heap without limit ([#13165](https://github.com/diegosouzapw/OmniRoute/issues/13165)) ([#13166](https://github.com/diegosouzapw/OmniRoute/pull/13166)) — thanks @anhtahaylove +- **fix(stream):** cancel the upstream response body when the JSON-to-SSE sniff unwinds on a body timeout, so a stalled upstream no longer pins the connection ([#13169](https://github.com/diegosouzapw/OmniRoute/issues/13169)) ([#13171](https://github.com/diegosouzapw/OmniRoute/pull/13171)) — thanks @anhtahaylove +- **fix(telegram):** authenticate webhook deliveries with Telegram's `secret_token` so an unauthenticated caller can no longer mint API keys or spend upstream quota ([#13172](https://github.com/diegosouzapw/OmniRoute/issues/13172)) ([#13175](https://github.com/diegosouzapw/OmniRoute/pull/13175)) — thanks @anhtahaylove +- **fix(models):** give discoverable Gemini 3.8 Flash ids their own 65536 output spec so Antigravity no longer clamps them to 16384 ([#13195](https://github.com/diegosouzapw/OmniRoute/pull/13195)) — thanks @HouMinXi +- **fix(dashboard):** leftover catalog-removed provider rows (gemini-cli) can be listed and purged from the providers page ([#13067](https://github.com/diegosouzapw/OmniRoute/issues/13067)) ([#13197](https://github.com/diegosouzapw/OmniRoute/pull/13197)) — thanks @HouMinXi +- **fix(auth):** a dashboard session now requires the `authenticated: true` claim that login, OIDC and the session refresh already emit — a JWT merely signed with `JWT_SECRET` (for example the Cursor CLI passthrough token, which any API-key holder can obtain) no longer verifies as the `auth_token` cookie on any route, the WebSocket handshake or the live server; existing sessions keep working ([#13298](https://github.com/diegosouzapw/OmniRoute/issues/13298)) ([#13375](https://github.com/diegosouzapw/OmniRoute/pull/13375)) +- **fix(api):** `DELETE /v1/batches/delete-completed` now applies the caller's API-key policy (endpoint allowlist, schedule, usage cap, rate limit) like every other `/v1` route, commits the key-scoped sweep per 200-batch chunk instead of holding one write lock for the whole sweep, refuses to loop without progress, and rejects a scope that names both a key and `allTenants` ([#13684](https://github.com/diegosouzapw/OmniRoute/pull/13684)) +- Restore the API-route typecheck gate: the non-streaming leg result lost its discriminated-union narrowing after the server-owned tool loop reassignment, producing 13 new TS2339 diagnostics in `chatCore.ts`. ([#12963](https://github.com/diegosouzapw/OmniRoute/pull/12963)) +- Restore four non-streaming behaviours the server-owned tool loop refactor dropped: client aborts map to 499 with the fixed `Request aborted` message, an aborted request no longer logs a synthetic `clientResponse`, Claude prompt-cache telemetry is recorded again, and a body that cannot be canonicalized no longer throws when the tool loop is off. ([#12990](https://github.com/diegosouzapw/OmniRoute/pull/12990)) +- **fix(skills):** The CLI registry parser now reads positionals declared with `.addArgument()`, not only those written inline in `.command()`. `tunnel create [type]` was being published as `tunnel create`, so the agent-skills sync gate reported drift on every branch and regenerating would have deleted the argument. ([#13009](https://github.com/diegosouzapw/OmniRoute/pull/13009)) — thanks @ntdatt812 +- **fix(combos):** dashboard combo test uses a short prompt, serial probes, and a 60s timeout so reasoning models and rate-limited free pools do not fail the health check ([#13001](https://github.com/diegosouzapw/OmniRoute/pull/13001)) — thanks @HouMinXi +- fix(compression): terminate idle worker threads on eviction so long-running instances stop leaking OS threads and MessagePorts ([#13091](https://github.com/diegosouzapw/OmniRoute/pull/13091)) — thanks @anhtahaylove +- Redact Google API keys of any length in error bodies: the pattern required exactly 39 characters, so shorter or longer `AIza…` credentials passed through unredacted. ([#12964](https://github.com/diegosouzapw/OmniRoute/pull/12964)) +- **fix(grok-cli):** a 402 "Grok Build usage balance exhausted" parks that Grok login as out of credit (Grok Build CLI, grok.com cookie, and xAI OAuth share the weekly pool). Combo routing then tries the next login instead of locking the model for every account in the pool ([#13061](https://github.com/diegosouzapw/OmniRoute/pull/13061)) — thanks @HouMinXi +- fix(compression): spawn the LLMLingua worker with a file URL object so compression actually runs instead of silently failing open on Node ([#13093](https://github.com/diegosouzapw/OmniRoute/pull/13093)) — thanks @anhtahaylove +- **fix(test):** run the local `test` and `test:unit` scripts at concurrency 4 so a full-suite run no longer exhausts the machine's commit charge and kills unrelated processes ([#13187](https://github.com/diegosouzapw/OmniRoute/pull/13187)) — thanks @anhtahaylove +- **fix(monitoring):** `GET /api/monitoring/health` `credentialHealth` now includes a bounded `failedConnections` list (`connectionId`, `status`, sanitized `lastError`) when the probe-cache gauge `failed>0`, plus `source: probe-cache` and a cheap `staleDbNonOkCount` for sticky SQLite `test_status` on active rows. Documents that the live gauge is not `provider_connections.test_status`. ([#12876](https://github.com/diegosouzapw/OmniRoute/pull/12876)) — thanks @RaviTharuma +- **fix(resilience):** an openai-compatible multi-upstream gateway no longer marks the whole connection `credits_exhausted` when a single upstream (or the `/models` / representative-model health probe) returns 402. Quota failures stay model-scoped; true connection-wide auth failures are unchanged. Single-credential 402 key disable ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239)) is preserved. ([#12875](https://github.com/diegosouzapw/OmniRoute/pull/12875)) — thanks @RaviTharuma +- **fix(dashboard):** nine fixes on the Orchestration Canvas, all diagnosed in the Phase 2 reviews. A source that fails now keeps the timestamp of its FIRST failure instead of being re-stamped every poll — the stale line said "since the last poll" no matter how long the source had been down, and the churn also defeated the snapshot's stable identity (it serializes the source list), so the canvas re-rendered on every tick while anything was broken. A source that HAD data and only then started failing is flagged too: previously only a source with no node at all got the warning, so a source that went down mid-session kept a healthy-looking node forever. The rest are pointwise: clicking a filter chip cancels the pending search debounce (left armed, it fired ~300ms later and silently reverted the chip); the search input carries an accessible name; `?state=running, failed` parses like the unpadded form instead of dropping the padded value; the CSV toggle helper is defined once in `model/urlParams.ts` rather than twice; the agents tab tells "nothing running" apart from "the filter matched nothing", offering a clear-filters button instead of setup links that would be wrong advice there; edges stop emitting SMIL particles above 40 simultaneously active edges, keeping the colored stroke; and the drawer's error banner clears when a retried action succeeds. ([#12983](https://github.com/diegosouzapw/OmniRoute/pull/12983)) +- **fix(i18n):** the nine locales added with the EU-language batch (Greek, Estonian, Irish, Croatian, Lithuanian, Latvian, Maltese, Slovenian, Serbian) were missing the eleven Orchestration Canvas keys that Phase 3 introduced, so the compare-runs panel and the "no runs match these filters" empty state fell back to English in those languages (`deepMergeFallback` substitutes English for an absent key, so nothing rendered blank — it rendered untranslated). The coverage gate does not catch this: it enforces an 80% floor per locale, and eleven missing keys out of ~13,000 leaves coverage at 99.9%. Translated for real in each language, calibrated against the wording each file already uses for "run", "filter" and "skill". ([#13294](https://github.com/diegosouzapw/OmniRoute/pull/13294)) +- **fix(dashboard):** five follow-ups from the Phase 2 Orchestration Canvas review. The History drawer now shows the memory section for runs that already left the live TTL window: the persisted `memory_hits` event is parsed into `metadata.memoryHits` with the same defensive validation the drawer applies, and — because that event is observability rather than a state transition — it no longer leaks into the timeline, where it had been inheriting the task's state and rendering as a duplicate transition. The A2A memory recall runs against its own 1.5s deadline instead of inheriting the memory backend's 30s one; overshooting degrades exactly like any other recall failure (no hits, task proceeds), and the timer is cleared on both paths. Repeating a Conductor run carries its `requirements.cli`/`requirements.model` forward, so the new run is pinned to the same runner profile and model rather than drifting to whatever the fleet picks. A successful repeat from the Agents tab now focuses the run it created, instead of leaving the operator on the finished one. And the auth test for `POST /api/conductor/tasks` moved into the shared `ROUTES` array rather than restating the pattern. ([#12988](https://github.com/diegosouzapw/OmniRoute/pull/12988)) +- fix(plugins): stop leaking an exit listener per plugin hook timeout, which triggered MaxListenersExceededWarning on plugins that ignore SIGTERM ([#13092](https://github.com/diegosouzapw/OmniRoute/pull/13092)) — thanks @anhtahaylove +- **fix(validation):** Provider node edits no longer fail with a generic "Invalid request" when the optional daily-quota reset fields are left blank. The dashboard sends `dailyQuotaResetTimezone` and `dailyQuotaResetHour` as `null`, and only the hour accepted it. ([#13066](https://github.com/diegosouzapw/OmniRoute/issues/13066)) ([#13083](https://github.com/diegosouzapw/OmniRoute/pull/13083)) — thanks @ntdatt812 +- **fix(combo):** quota-aware expansion drops banned, inactive, missing, and wrong-provider connections before quota fetch or model dispatch; pins and allowlists stay selectors, not a bypass. Antigravity automatic exhaustion now requires a reported zero remaining, so a positive balance below 1% stays eligible. ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- Fix an intermittent failure in the quota-weighted routing test suite: scores are a function of `Date.now()`, so peers with identical quota scored microseconds apart never tied and swapped order. ([#12946](https://github.com/diegosouzapw/OmniRoute/pull/12946)) +- fix(volcengine): resolve quota window mapping gaps, safe multi-connection console binding, and response cookie sanitization ([#12950](https://github.com/diegosouzapw/OmniRoute/pull/12950)) — thanks @HouMinXi +- **fix(test):** resolve the WebDAV handler path with `fileURLToPath` so the suite's 37 WebDAV tests run on Windows instead of failing with a doubled `C:\C:\` drive prefix ([#13196](https://github.com/diegosouzapw/OmniRoute/pull/13196)) — thanks @anhtahaylove +- **fix(ui):** fall back from invalid AnySearch icon alias (direct commit `9587e07b69`) +- **fix(sse):** stop rebuilding a truncated summary from the collector's cap-dropped event array ([#12718](https://github.com/diegosouzapw/OmniRoute/pull/12718)) — thanks @hartmark +- **fix(conversations):** show a pending spinner for unresolved tool nodes instead of "(empty)" ([#12727](https://github.com/diegosouzapw/OmniRoute/pull/12727)) — thanks @hartmark +- **fix(streaming):** fail fast when an upstream stream produces only lifecycle/heartbeat events, never real content ([#12741](https://github.com/diegosouzapw/OmniRoute/pull/12741)) — thanks @hartmark +- **fix(responses-continuation):** bridge the write-in-flight window with an in-memory pending store ([#12854](https://github.com/diegosouzapw/OmniRoute/pull/12854)) — thanks @hartmark +- **fix(translator):** drop or map agent_message on Chat Completions fallback ([#12880](https://github.com/diegosouzapw/OmniRoute/pull/12880)) — thanks @RaviTharuma +- **fix(api):** page and stream-complete GET /v1/models for large catalogs ([#12882](https://github.com/diegosouzapw/OmniRoute/pull/12882)) — thanks @RaviTharuma +- **fix(quota):** align AUTH window labels with usage API and clarify 503 ([#12884](https://github.com/diegosouzapw/OmniRoute/pull/12884)) — thanks @RaviTharuma +- **fix(ci):** clear two base-reds on release/v3.8.51 — mutation-coverage gate + image-only-model guard ([#12945](https://github.com/diegosouzapw/OmniRoute/pull/12945)) +- **fix(api):** scope batch bulk-delete to the calling API key ([#13211](https://github.com/diegosouzapw/OmniRoute/pull/13211)) +- **fix(tests):** align model-sync log assertion to shipped path redaction (base-red) ([#13227](https://github.com/diegosouzapw/OmniRoute/pull/13227)) +- **fix(test):** make the ToS heading guard actually require the parentheses ([#13228](https://github.com/diegosouzapw/OmniRoute/pull/13228)) +- **fix(quality):** register 4 drifted covering tests in stryker tap.testFiles ([#13229](https://github.com/diegosouzapw/OmniRoute/pull/13229)) +- **fix(logging):** mask *-api-key headers in request-log pipeline (#13273) ([#13401](https://github.com/diegosouzapw/OmniRoute/pull/13401)) — thanks @KooshaPari +- **fix(tests):** correct stale Jina catalog prefix in custom-models test ([#13403](https://github.com/diegosouzapw/OmniRoute/pull/13403)) — thanks @KooshaPari +- **fix(backend):** prune old backups after health-check-repair snapshots (#13308) ([#13404](https://github.com/diegosouzapw/OmniRoute/pull/13404)) — thanks @KooshaPari +- **fix(backend):** guard memory_fts_au trigger with WHEN clause to stop FTS bloat (#13326) ([#13405](https://github.com/diegosouzapw/OmniRoute/pull/13405)) — thanks @KooshaPari +- **fix(free-tiers):** remove obsolete Together $25 signup credit (#12526) ([#13407](https://github.com/diegosouzapw/OmniRoute/pull/13407)) — thanks @KooshaPari +- **fix:** skip non-chat generation models in Test all models ([#13409](https://github.com/diegosouzapw/OmniRoute/pull/13409)) — thanks @KooshaPari +- **fix(providers):** de-duplicate agy and antigravity model catalogs ([#13410](https://github.com/diegosouzapw/OmniRoute/pull/13410)) — thanks @KooshaPari +- **fix(dashboard):** send provider in body for mini-playground web search ([#13411](https://github.com/diegosouzapw/OmniRoute/pull/13411)) — thanks @KooshaPari +- **fix(resilience):** add CJK quota-exhaustion patterns to 429 classifier ([#13412](https://github.com/diegosouzapw/OmniRoute/pull/13412)) — thanks @KooshaPari +- **fix(backend):** eval runner: failed calls no longer score as passed + dotAll regex ([#13413](https://github.com/diegosouzapw/OmniRoute/pull/13413)) — thanks @KooshaPari +- **fix(backend):** eval runner opts out of output-style and memory injection ([#13414](https://github.com/diegosouzapw/OmniRoute/pull/13414)) — thanks @KooshaPari +- **fix:** chain serial quarantine step in npm test script (fixes #13286) ([#13418](https://github.com/diegosouzapw/OmniRoute/pull/13418)) — thanks @KooshaPari +- **fix(antigravity):** derive stable session ID from credentials for prompt-cache reuse (#13305) ([#13424](https://github.com/diegosouzapw/OmniRoute/pull/13424)) — thanks @KooshaPari +- **fix(memory):** merge memory into top-level system field instead of unshifting at messages[0] (#13425) ([#13427](https://github.com/diegosouzapw/OmniRoute/pull/13427)) — thanks @KooshaPari +- **fix(compression):** preserve instruction blocks from lossy engines ([#13523](https://github.com/diegosouzapw/OmniRoute/pull/13523)) — thanks @KooshaPari +- **fix(compression):** preserve polarity words and newlines in ultra heuristic ([#13524](https://github.com/diegosouzapw/OmniRoute/pull/13524)) — thanks @KooshaPari +- **fix(compression):** run user preservePatterns before built-in patterns (#13457) ([#13525](https://github.com/diegosouzapw/OmniRoute/pull/13525)) — thanks @KooshaPari +- **fix(reasoning):** expand deepseek-v4 regex to match v4.1+ and add command-code provider (#13430) ([#13528](https://github.com/diegosouzapw/OmniRoute/pull/13528)) — thanks @KooshaPari +- **fix(providers):** add alwaysReasons metadata for reka-flash-3 ([#13531](https://github.com/diegosouzapw/OmniRoute/pull/13531)) — thanks @KooshaPari +- **fix(playground):** allow Compare columns to scroll independently ([#13532](https://github.com/diegosouzapw/OmniRoute/pull/13532)) — thanks @KooshaPari +- **fix(security):** empty whitelist allows through to prevent admin lockout ([#13534](https://github.com/diegosouzapw/OmniRoute/pull/13534)) — thanks @KooshaPari +- **fix(tests):** add DISABLE_IOREG_STRATEGY env var for macOS test compatibility (fixes #13467) ([#13539](https://github.com/diegosouzapw/OmniRoute/pull/13539)) — thanks @KooshaPari +- **fix(zed-hosted):** map developer role and Gemini enums for Zed proxy ([#13543](https://github.com/diegosouzapw/OmniRoute/pull/13543)) — thanks @KooshaPari +- **fix(analytics):** normalize provider aliases to canonical id in usage_history ([#13545](https://github.com/diegosouzapw/OmniRoute/pull/13545)) — thanks @KooshaPari +- **fix(combo):** use traceId for call log id to prevent UNIQUE constraint failure ([#13546](https://github.com/diegosouzapw/OmniRoute/pull/13546)) — thanks @KooshaPari +- **fix(auth):** check model lockout before returning synthetic noauth connection ([#13547](https://github.com/diegosouzapw/OmniRoute/pull/13547)) — thanks @KooshaPari +- **fix(guardrails):** skip credential redaction for base64 image data URLs ([#13550](https://github.com/diegosouzapw/OmniRoute/pull/13550)) — thanks @KooshaPari +- **fix(resilience):** increase requestQueue.maxWaitMs default from 15s to 30s ([#13553](https://github.com/diegosouzapw/OmniRoute/pull/13553)) — thanks @KooshaPari +- **fix(docs):** document OMNIROUTE_READY_TIMEOUT_MS and allowlist the test-only DISABLE_IOREG_STRATEGY ([#13692](https://github.com/diegosouzapw/OmniRoute/pull/13692)) ### 📝 Maintenance @@ -853,6 +1054,39 @@ _By commits in `release/v3.8.50..release/v3.8.51` (`091589089c..d6f315018a`), au - **docs(security):** document socket.yml scanner config + CI workflow link (#12575) ([#12764](https://github.com/diegosouzapw/OmniRoute/pull/12764)) — thanks @KooshaPari - **chore(ci):** guard commit identity in pre-commit to stop author misattribution ([#12772](https://github.com/diegosouzapw/OmniRoute/pull/12772)) - **deps:** 17 Dependabot bumps — bump electron from 43.4.0 to 43.4.1 in /electron ([#11426](https://github.com/diegosouzapw/OmniRoute/pull/11426)); bump the development group across 1 directory with 8 updates ([#11428](https://github.com/diegosouzapw/OmniRoute/pull/11428)); bump the production group across 1 directory with 11 updates ([#11440](https://github.com/diegosouzapw/OmniRoute/pull/11440)); chore(deps): bump github/codeql-action/init from 4.37.7 to 4.37.8 ([#11925](https://github.com/diegosouzapw/OmniRoute/pull/11925)); chore(deps): bump github/codeql-action from 4.37.7 to 4.37.8 ([#11926](https://github.com/diegosouzapw/OmniRoute/pull/11926)); chore(deps): bump github/codeql-action/analyze from 4.37.7 to 4.37.8 ([#11927](https://github.com/diegosouzapw/OmniRoute/pull/11927)); build(deps): bump github/codeql-action/init from 4.37.8 to 4.37.9 ([#12345](https://github.com/diegosouzapw/OmniRoute/pull/12345)); build(deps): bump github/codeql-action/analyze from 4.37.8 to 4.37.9 ([#12346](https://github.com/diegosouzapw/OmniRoute/pull/12346)); bump the development group across 1 directory with 2 updates ([#12347](https://github.com/diegosouzapw/OmniRoute/pull/12347)); build(deps): bump github/codeql-action from 4.37.8 to 4.37.9 ([#12349](https://github.com/diegosouzapw/OmniRoute/pull/12349)); bump browserslist from 4.28.2 to 4.28.8 ([#12396](https://github.com/diegosouzapw/OmniRoute/pull/12396)); bump the production group across 1 directory with 4 updates ([#12399](https://github.com/diegosouzapw/OmniRoute/pull/12399)); bump @xmldom/xmldom ([#12500](https://github.com/diegosouzapw/OmniRoute/pull/12500)); bump qs from 6.15.2 to 6.16.0 ([#12512](https://github.com/diegosouzapw/OmniRoute/pull/12512)); bump @xmldom/xmldom from 0.9.10 to 0.9.12 ([#12513](https://github.com/diegosouzapw/OmniRoute/pull/12513)); bump fast-uri from 3.1.5 to 3.1.7 ([#12514](https://github.com/diegosouzapw/OmniRoute/pull/12514)); bump @humanfs/node from 0.16.7 to 0.16.8 ([#12515](https://github.com/diegosouzapw/OmniRoute/pull/12515)) +- Clear the `release/v3.8.51` `check:agent-skills-sync` base-red: regenerate `skills/cli-tunnel/SKILL.md` so the `tunnel create [type]` positional that #13009 taught the generator to read is reflected in the committed skill. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` typecheck base-red from #12731: the new `Mode pack ranking gates` candidates in `open-sse/services/autoCombo/__tests__/autoCombo.test.ts` omitted the required `provider`, `model` and `errorRate` fields of `ProviderCandidate`, failing `check:open-sse-typecheck` (and with it `Fast Quality Gates`) on every open PR. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` docs-sync base-red: EURouter (#13025) and GreenPT (#13024) took the live provider count to 358, leaving 7 STRICT drifts (`PROVIDER_REFERENCE.md`, 4 diagrams, 2 tier-flow images) plus the `AGENTS.md` / `llm.txt` / `package.json` count claims stale. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- **test(models):** Pin `CATALOG_BUILD_TIMEOUT_MS` in the #12058 canonical-catalog regression so a cold catalog build racing the 8s default bound no longer flakes the suite with a 500 ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Allow `@omniroute/opencode-plugin-v2/` in the npm pack policy — [#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870) shipped the v2 plugin beside its v1 sibling without widening the allowlist, so every packed file under it read as an unexpected artifact and failed `check:pack-policy` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Point the release-green validator's pack gate at the tree it is validating — the build-provenance guard defaulted to `origin/main`, which a release branch tip can never be an ancestor of mid-cycle, so `Package artifact` reported a false red on every run and the tarball boot-smoke cascaded off it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **docs(checks):** keep doc counts honest — headings, rankings, catalog, weights, quality gate and scoring diagram now covered ([#12507](https://github.com/diegosouzapw/OmniRoute/pull/12507)) — thanks @maxmad64bis +- **chore(lifecycle):** `check:model-lifecycle` now also diffs `DEFAULT_DEGRADATION_MAP` (the background-task redirect table) against the vendor lifecycle snapshot, refusing a retired id as source or target, with a table-driven unit test beside it. Three rows whose source the vendor had retired — `claude-sonnet-4-20250514`, `gemini-3-pro-preview` and `gpt-5.1-codex` (whose target `gpt-5.1-codex-mini` is retired too) — were dead code, since `checkLifecycle` answers 410 before the redirect runs; they are dropped (#12535 — thanks @pacocartones) +- **test(compression):** cover idle worker eviction at the resource level — the pool must call `terminate()` and must not retain the worker's `MessagePort`, complementing the `exit`-event assertion added with the fix ([#13371](https://github.com/diegosouzapw/OmniRoute/pull/13371)) +- **chore(quality):** raise the `imageGeneration.ts` file-size ceiling for the image-only-model guard that clears the #12945 base-red ([#13392](https://github.com/diegosouzapw/OmniRoute/pull/13392)) +- **chore(quality):** type the combo-test route's JSON response bodies instead of casting them to `any`, drop the now-empty suppression entry, and remove the `hasPerModelQuota` import `chatCore.ts` stopped using when the failure-classification helper was extracted ([#13379](https://github.com/diegosouzapw/OmniRoute/pull/13379)) +- **chore(electron):** the desktop bundle no longer ships the root-level files of every translated docs mirror (`README.md`, `llm.txt`, `CLAUDE.md`, `GEMINI.md`, `CONTRIBUTING.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`) — the packaged app only reads `docs/i18n//docs/**`, which stays. Saves ~11 MB on top of the translated CHANGELOGs already pruned. (#0000) ([#13235](https://github.com/diegosouzapw/OmniRoute/pull/13235)) +- **chore(quality):** retighten the `src/sse/handlers/chat.ts` unused-vars suppression count to the 8 violations that actually remain ([#13385](https://github.com/diegosouzapw/OmniRoute/pull/13385)) +- Bring the documented counts back in line with the code: 171 migrations (was 169) and 20 routing strategies (was 19), plus the regenerated `cli-tunnel` skill reference. ([#12970](https://github.com/diegosouzapw/OmniRoute/pull/12970)) +- Update the documented migration count to 172 after the call-logs provider-stats indexes landed. ([#13209](https://github.com/diegosouzapw/OmniRoute/pull/13209)) +- **chore(ci):** two gates that close the blind spots behind the exclusions above. `check:vitest-exclusions` requires every Vitest exclusion to name a tracking issue and to appear in `config/quality/vitest-exclusions.json` — the previous list grew to 62 files behind a comment pointing at an issue that had been closed for a month. `check-new-key-coverage` requires a key newly added to `en.json` to reach every locale; the existing coverage gate is a percentage floor per locale, so eleven absent keys out of ~13,000 left it at 99.9% while a whole feature shipped untranslated in nine languages. Both are diff-aware, so pre-existing debt stays frozen and neither needed a migration to turn on. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **build:** root layout no longer loads Inter from `next/font/google`, so a production image build does not need fonts.googleapis.com ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **docs(i18n):** bring the eight new `llm.txt` mirrors (km, kn, ml, my, ne, or, pa, si) up to the current provider and migration counts, so `check-docs-sync` stops rejecting every commit ([#13674](https://github.com/diegosouzapw/OmniRoute/pull/13674)) +- **refactor(ui):** flow surfaces (home topology, combo live studio, compression cockpit/waterfall/nodes, token-health badge) express state through the theme-aware `--orch-status-*` tokens instead of fixed dark-mode hex, so green/red/amber/grey stay legible in the light theme; dark mode is byte-identical. Categorical palettes (routing-strategy hues, compression-layer pills, provider brand colors) deliberately stay hex ([#12378](https://github.com/diegosouzapw/OmniRoute/issues/12378)) ([#13203](https://github.com/diegosouzapw/OmniRoute/pull/13203)) +- **chore(tests):** 51 test files that had been excluded from Vitest are running again, restoring roughly 350 assertions to the blocking suite. Each was measured individually first: of the 62 files parked behind the `// #8618 — pre-existing failure` comment, 51 pass against the current tree with no source change, so the exclusions had outlived the failures they were added for. The 11 that genuinely still fail stay excluded, but now point at a live tracker (#13204) rather than at #8618, which was closed in August while the list it tracked kept growing. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **chore(quality):** drop two ESLint suppression entries whose violations no longer exist, so `eslint --suppressions-location` stops rejecting every commit that touches the surrounding files ([#13384](https://github.com/diegosouzapw/OmniRoute/pull/13384)) +- **chore(release):** bring main's post-tag fixes into release/v3.8.51 — sync-back step 2/2 (direct commit `21c488f210`) +- **chore(deps):** patch toml and esbuild in the opencode plugin lockfiles ([#12965](https://github.com/diegosouzapw/OmniRoute/pull/12965)) +- **docs(changelog):** reconcile the v3.8.51 living section — fold 366 fragments, cover every cycle commit, credit every contributor ([#12971](https://github.com/diegosouzapw/OmniRoute/pull/12971)) +- **test(plugin):** wait for the v2 lazy-refresh tier instead of sleeping past it ([#12984](https://github.com/diegosouzapw/OmniRoute/pull/12984)) +- **chore(quality):** rebaseline chatCore.ts after the non-streaming regression fixes ([#13045](https://github.com/diegosouzapw/OmniRoute/pull/13045)) +- **docs(changelog):** add the missing bullet marker to the DeepSeek PoW fragment ([#13200](https://github.com/diegosouzapw/OmniRoute/pull/13200)) +- **chore(deps):** drain the Dependabot queue — 10 of 13 alerts ([#13213](https://github.com/diegosouzapw/OmniRoute/pull/13213)) +- **chore(ci):** make worktrees carry the hooks that enforce the identity gate ([#13236](https://github.com/diegosouzapw/OmniRoute/pull/13236)) +- **chore(deps):** pin csv-parse to ^7.0.2 via overrides (#13117) ([#13300](https://github.com/diegosouzapw/OmniRoute/pull/13300)) — thanks @KooshaPari +- **chore(deps):** bump hono from ^4.12.34 to ^4.13.7 (#13148) ([#13301](https://github.com/diegosouzapw/OmniRoute/pull/13301)) — thanks @KooshaPari +- **chore(deps):** pin joi to ^18.2.8 via overrides (#13085) ([#13302](https://github.com/diegosouzapw/OmniRoute/pull/13302)) — thanks @KooshaPari +- **docs(providers):** add Microsoft 365 Copilot (BizChat) provider guide (#12779) ([#13340](https://github.com/diegosouzapw/OmniRoute/pull/13340)) — thanks @KooshaPari ### 🙌 Contributors @@ -870,6 +1104,7 @@ Thanks to everyone whose work landed in v3.8.51: | [@amaleta](https://github.com/amaleta) | #11906, #11934 | | [@amartinawi](https://github.com/amartinawi) | #11575 | | [@AndrianBalanescu](https://github.com/AndrianBalanescu) | #11083, #11830, #11834, #11844 | +| [@anhtahaylove](https://github.com/anhtahaylove) | #13091, #13092, #13093, #13096, #13097, #13100, #13106, #13109, #13114, #13155, #13166, #13171, #13175, #13187, #13196 | | [@aniruddhaadak80](https://github.com/aniruddhaadak80) | #11760 | | [@app](https://github.com/app) | #12554 | | [@arminanton](https://github.com/arminanton) | #11461, #11513 | @@ -899,10 +1134,10 @@ Thanks to everyone whose work landed in v3.8.51: | [@gonisulaimann](https://github.com/gonisulaimann) | #12368, #12369, #12371 | | [@Gorillaz322](https://github.com/Gorillaz322) | #12207 | | [@groovecityJO](https://github.com/groovecityJO) | #12682 | -| [@hartmark](https://github.com/hartmark) | #11434, #11452, #11473, #11499, #11703, #11983, #11984, #11985, #11986, #11988, #11989, #11990, #11994, #12221, #12293, #12445, #12446, #12447, #12448, #12460, #12461, #12623, #12646, #12650, #12680 | +| [@hartmark](https://github.com/hartmark) | #11434, #11452, #11473, #11499, #11703, #11983, #11984, #11985, #11986, #11988, #11989, #11990, #11994, #12221, #12293, #12445, #12446, #12447, #12448, #12460, #12461, #12623, #12646, #12650, #12680, #12717, #12718, #12727, #12741, #12854 | | [@hizzt](https://github.com/hizzt) | #11894 | | [@hongnoul](https://github.com/hongnoul) | #11484 | -| [@HouMinXi](https://github.com/HouMinXi) | #11411, #11414, #11512, #11518, #11520, #11641, #11642, #11643, #11687, #11779, #11849, #11850, #11851, #11915, #11916, #11918, #11919, #11920, #12013, #12017, #12033, #12042, #12106, #12139, #12166, #12169, #12171, #12205, #12213, #12312, #12325, #12487, #12488, #12495, #12504, #12557, #12566, #12590, #12591, #12624, #12626, #12632, #12637, #12678, #12696, #12697, #12711, #12733, #12746, #12767, #12770, #12789, #12803, #12805, #12811, #12866, #12868, #12899, #12926, #12934 | +| [@HouMinXi](https://github.com/HouMinXi) | #11411, #11414, #11512, #11518, #11520, #11641, #11642, #11643, #11687, #11779, #11849, #11850, #11851, #11915, #11916, #11918, #11919, #11920, #12013, #12017, #12033, #12042, #12106, #12139, #12166, #12169, #12171, #12205, #12213, #12312, #12325, #12487, #12488, #12495, #12504, #12557, #12566, #12590, #12591, #12624, #12626, #12632, #12637, #12678, #12696, #12697, #12711, #12733, #12746, #12767, #12770, #12789, #12803, #12805, #12811, #12866, #12868, #12899, #12926, #12934, #12950, #12951, #12974, #13001, #13006, #13011, #13017, #13026, #13027, #13034, #13035, #13038, #13042, #13050, #13060, #13061, #13069, #13107, #13120, #13136, #13195, #13197 | | [@Hsia97](https://github.com/Hsia97) | #11624 | | [@jacobsparts](https://github.com/jacobsparts) | #11854, #12155, #12167 | | [@joglomedia](https://github.com/joglomedia) | #11980 | @@ -912,11 +1147,11 @@ Thanks to everyone whose work landed in v3.8.51: | [@KaspaPulse](https://github.com/KaspaPulse) | #11389, #11469, #11628, #11666, #11910 | | [@keeltrace](https://github.com/keeltrace) | #12080, #12223 | | [@killer30001000](https://github.com/killer30001000) | #12178 | -| [@KooshaPari](https://github.com/KooshaPari) | #12470, #12592, #12667, #12699, #12703, #12706, #12764, #12769, #12771 | +| [@KooshaPari](https://github.com/KooshaPari) | #12470, #12592, #12667, #12699, #12703, #12706, #12764, #12769, #12771, #13300, #13301, #13302, #13340, #13401, #13403, #13404, #13405, #13406, #13407, #13409, #13410, #13411, #13412, #13413, #13414, #13418, #13424, #13427, #13433, #13523, #13524, #13525, #13528, #13531, #13532, #13534, #13539, #13543, #13545, #13546, #13547, #13550, #13553 | | [@kriptoburak](https://github.com/kriptoburak) | #11370 | | [@KrzysiekSko](https://github.com/KrzysiekSko) | #12425, #12432, #12673 | | [@marcelokarval](https://github.com/marcelokarval) | direct commit / report | -| [@maxmad64bis](https://github.com/maxmad64bis) | #1622, #11435, #11437, #11441, #11537, #11550, #11553, #11555, #11557, #11812, #11842, #11843, #11903, #12151, #12214, #12215, #12218, #12226, #12314, #12316, #12317, #12318, #12319, #12320, #12321, #12870 | +| [@maxmad64bis](https://github.com/maxmad64bis) | #1622, #11435, #11437, #11441, #11537, #11550, #11553, #11555, #11557, #11812, #11842, #11843, #11903, #12151, #12214, #12215, #12218, #12226, #12314, #12316, #12317, #12318, #12319, #12320, #12321, #12507, #12715, #12731, #12744, #12786, #12787, #12788, #12790, #12792, #12794, #12795, #12828, #12832, #12853, #12857, #12870, #12937, #12941, #12975, #13141, #13142, #13146, #13147 | | [@mdigitalbh81](https://github.com/mdigitalbh81) | #12240 | | [@Meet6338-X](https://github.com/Meet6338-X) | #11598, #11609, #12110 | | [@morpheus9393](https://github.com/morpheus9393) | #11943 | @@ -925,11 +1160,11 @@ Thanks to everyone whose work landed in v3.8.51: | [@NightStalker-87](https://github.com/NightStalker-87) | #12183 | | [@NoxzRCW](https://github.com/NoxzRCW) | #11879, #11880, #11881, #11882, #11883, #11935 | | [@ntdat812](https://github.com/ntdat812) | #11585, #12095 | -| [@ntdatt812](https://github.com/ntdatt812) | #11368, #11573, #11574, #11576, #11577, #11580, #11582, #11583, #11588, #11589, #11590, #11591, #11592, #11593, #11672, #12177, #12180 | +| [@ntdatt812](https://github.com/ntdatt812) | #11368, #11573, #11574, #11576, #11577, #11580, #11582, #11583, #11588, #11589, #11590, #11591, #11592, #11593, #11672, #12177, #12180, #12873, #12918, #12920, #12921, #12925, #12930, #13007, #13009, #13024, #13025, #13083, #13087, #13101, #13104, #13110 | | [@official-burak](https://github.com/official-burak) | #11542 | | [@opensource-elearning](https://github.com/opensource-elearning) | #12179, #12189, #12278, #12286 | | [@oyi77](https://github.com/oyi77) | #11408, #11409, #11421, #11505, #11677, #12036, #12110 | -| [@pacocartones](https://github.com/pacocartones) | #11521, #11522, #11527, #11528, #11529, #11530, #11531, #11532, #11533, #11534, #11595, #11599, #11603, #11604, #11605, #11607, #11610, #11676, #11714, #11716, #11718, #11767, #11838, #11860, #11862, #11869, #11871, #11872, #11873, #11903, #11906, #11921, #11934, #12359, #12360, #12361, #12362, #12364, #12365, #12373, #12374, #12375, #12376, #12377, #12379, #12380, #12381, #12386, #12387, #12389, #12390, #12394, #12395, #12397, #12401, #12403, #12404 | +| [@pacocartones](https://github.com/pacocartones) | #11521, #11522, #11527, #11528, #11529, #11530, #11531, #11532, #11533, #11534, #11595, #11599, #11603, #11604, #11605, #11607, #11610, #11676, #11714, #11716, #11718, #11767, #11838, #11860, #11862, #11869, #11871, #11872, #11873, #11903, #11906, #11921, #11934, #12356, #12358, #12359, #12360, #12361, #12362, #12364, #12365, #12373, #12374, #12375, #12376, #12377, #12379, #12380, #12381, #12386, #12387, #12389, #12390, #12394, #12395, #12397, #12401, #12403, #12404, #12522, #12523, #12535, #12536, #12540, #12541, #12543, #12545, #12548, #12549, #12550, #12551, #12552, #12647, #12651, #12653, #13055 | | [@patrykkopycinski](https://github.com/patrykkopycinski) | #11936, #11937, #12224 | | [@PauloFH](https://github.com/PauloFH) | #11509 | | [@PauloHSOliveira](https://github.com/PauloHSOliveira) | #12241 | @@ -941,7 +1176,7 @@ Thanks to everyone whose work landed in v3.8.51: | [@raheemuddin786](https://github.com/raheemuddin786) | #11491, #11839, #11840, #11841, #12003, #12230, #12231, #12232, #12233, #12234 | | [@rahilmavani](https://github.com/rahilmavani) | #11761 | | [@Rahulsharma0810](https://github.com/Rahulsharma0810) | #11771 | -| [@RaviTharuma](https://github.com/RaviTharuma) | #11710, #11727, #11797, #11798, #11802, #11805, #11806, #11811, #12098, #12099, #12101, #12449, #12452, #12472, #12473, #12493, #12533, #12607, #12628, #12631, #12636 | +| [@RaviTharuma](https://github.com/RaviTharuma) | #11710, #11727, #11797, #11798, #11802, #11805, #11806, #11811, #12098, #12099, #12101, #12449, #12452, #12472, #12473, #12493, #12533, #12607, #12628, #12631, #12636, #12875, #12876, #12880, #12882, #12884 | | [@rezjalibd](https://github.com/rezjalibd) | #12186 | | [@RhianB14](https://github.com/RhianB14) | #11692 | | [@rifqiawl](https://github.com/rifqiawl) | #11517, #11519, #11969 | diff --git a/docs/i18n/nl/CHANGELOG.md b/docs/i18n/nl/CHANGELOG.md index f181e26763..e80e1ad942 100644 --- a/docs/i18n/nl/CHANGELOG.md +++ b/docs/i18n/nl/CHANGELOG.md @@ -8,16 +8,1119 @@ ## [3.8.51] — TBD -_Living section — cycle opened at the v3.8.50 freeze (parallel-cycle model). Bullets are aggregated from `changelog.d/` fragments at each `/generate-release` phase._ +_Living section — reconciled 2026-09-15 from all cycle commits (`091589089c` → `c0f92ec98a`, 916 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 | **123** | +| 📝 Commits in the cycle | **916** | +| 🔀 Pull requests referenced | **892** | +| 📋 Changelog entries | **919** | +| 🙌 Contributors credited in entries | **121** | +| 🤖 Automated dependency commits | 18 | + +**Entries by type** + +| Type | Count | +| --- | ---: | +| 🐛 Fixes | 592 | +| ✨ Features | 131 | +| 🧹 Chore | 93 | +| 📚 Docs | 42 | +| 🧪 Tests | 32 | +| ♻️ Refactor | 8 | +| 🏗️ Build | 7 | +| ⚡ Performance | 7 | +| ⚙️ CI | 2 | +| ⏪ Reverts | 2 | +| 📦 Dependencies | 2 | +| 🔒 Security | 1 | + +### 🏆 Top 25 contributors this cycle + +_By commits in `091589089c..c0f92ec98a`, author identities consolidated via `.mailmap` and the merged PR's GitHub login. Bots excluded._ + +| # | Contributor | Commits | +| ---: | --- | ---: | +| 🥇 | diegosouzapw | 323 | +| 🥈 | Bob.Hou (@HouMinXi) | 79 | +| 🥉 | Paco Cartones (@pacocartones) | 77 | +| 4 | Dizzle (@maxmad64bis) | 49 | +| 5 | Koosha Paridehpour (@KooshaPari) | 43 | +| 6 | Nguyen Thanh Dat (@ntdatt812) | 31 | +| 7 | Ravi Tharuma (@RaviTharuma) | 26 | +| 8 | Markus Hartung (@hartmark) | 22 | +| 9 | Webman (@jonlwheat2-gif) | 22 | +| 10 | backryun | 17 | +| 11 | anhtahaylove | 15 | +| 12 | Rafa Martins (@rafacpti23) | 10 | +| 13 | Syed Raheemuddin (@raheemuddin786) | 10 | +| 14 | Nguyễn Viết Tuấn (@TheDemonTuan) | 8 | +| 15 | MumuTW | 7 | +| 16 | Paijo (@oyi77) | 7 | +| 17 | Tobias Andersen (@turbolego) | 6 | +| 18 | Bl0ck (@Bl0ck154) | 5 | +| 19 | SHANMUGAPRIYAN (@geek007git) | 5 | +| 20 | KaspaPulse | 5 | +| 21 | Abhishek Sharma (@abhisheksharma2411) | 4 | +| 22 | Abhishek Divekar (@adivekar-utexas) | 4 | +| 23 | Andrew B. (@AndrianBalanescu) | 4 | +| 24 | NoxzRCW | 4 | +| 25 | opensource-elearning | 4 | ### ✨ New Features +- **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). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- Added Google AI Studio Gemini batch text-to-speech support through `POST /v1/audio/speech`. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- 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. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(routing):** subscription-first auto groupings — `auto/subscription` routes only through plan-included connections with a documented hard-stop overage and fails closed on exhaustion, while `auto/thrifty` orders the pool `subscription → keyless → free → cheap → premium` and steps up one rung at a time as each is exhausted. Billing class comes from a curated per-connection catalog (uncurated is treated as metered, never plan-included), both reuse STRICT_ZERO_COST's per-connection verification, and a quota reading whose `resetAt` has passed is now refreshed regardless of TTL so routing returns to plan capacity as soon as it resets ([#11146](https://github.com/diegosouzapw/OmniRoute/pull/11146)) — thanks @yourspraveen +- **feat(providers):** publish a management-authenticated versioned web-session credential contract from OmniRoute's canonical browser credential metadata ([#11340](https://github.com/diegosouzapw/OmniRoute/pull/11340)) — thanks @Zartharas +- **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)) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add Xquik X search with typed results, credential validation, REST routing, and MCP selection ([#11370](https://github.com/diegosouzapw/OmniRoute/pull/11370)) — thanks @kriptoburak +- **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)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(dashboard):** surface durable exclusive managed leases in the existing Sessions view, keeping leased clients visible across idle gaps while marking connections with in-flight work as active ([#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389)) — thanks @KaspaPulse +- **build(bun):** allow Turbopack bundler flag on Bun 1.4+ with configurable Webpack fallback ([#11471](https://github.com/diegosouzapw/OmniRoute/pull/11471)) — thanks @TheDemonTuan +- feat(api): add an opt-in `modelVisibilityAllowlist`/`modelVisibilityDenylist` settings pair to curate exactly which models `/v1/models` advertises, mirrored into every `auto/*` combo candidate pool so a denied model cannot be routed to via combo selection either (#11481) ([#11997](https://github.com/diegosouzapw/OmniRoute/pull/11997)) +- **feat(rankings):** the Free Provider Rankings page shows what each provider actually served over the last 24 h. It ranked by ELO alone, which left a provider that answers every call with an error in first place; the usage data was already served by the API but never requested. A provider with too small a sample shows a dash, not a number ([#11546](https://github.com/diegosouzapw/OmniRoute/pull/11546)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **feat(guardrails):** enforce a bounded, deterministic contract for Video Bridge transcripts — 256 cues, 4096 input code units and 4 KiB UTF-8 per cue, 64 KiB total text, malformed-Unicode rejection, focus-window scoping, cross-source reconciliation with contributing-source metadata, and a structural provenance trust boundary so caller JSON can never self-assert `embedded`/`audio-bridge` provenance ([#11652](https://github.com/diegosouzapw/OmniRoute/issues/11652)) ([#12009](https://github.com/diegosouzapw/OmniRoute/pull/12009)) +- **feat(video):** orchestrate optional Video Bridge audio extraction and Audio Bridge STT behind a dual opt-in (operator setting AND per-request signal) — a new loopback-only broker `mode=audio` operation shares the frame path's exact process queue, deadline, AbortSignal, and byte budgets to extract a bounded mono 16 kHz PCM WAV from the same already-downloaded video, then reuses the existing Audio Bridge transcription boundary; provider segment timing is preserved when available and marked coarse otherwise, and every failure degrades to a visual-only-safe partial instead of throwing (#11654). ([#12012](https://github.com/diegosouzapw/OmniRoute/pull/12012)) +- Add a tenant-bound Video Bridge drill-down lifecycle on top of the existing secure cache substrate: opaque hashed handles (never raw session/video identifiers), preview/standard/detail multiresolution variants resampled on read, response pagination capped at 8 frames and 32 MiB, and a new authenticated `/api/v1/video-bridge/drilldown` consumer route that stays disabled for remote access by default and denies cross-key access with the same response as a nonexistent handle (no existence oracle). ([#12006](https://github.com/diegosouzapw/OmniRoute/pull/12006)) +- **test(video):** Add the Video Bridge FU-07/FU-09 promotion-evidence harness (#11656) — a frozen Zod manifest schema covering the 8 required scenario kinds (static scenes, rapid cuts, late facts, fades, blur, small text, close events, visual prompt injection) with a minimum of 3 repetitions per case, deterministic declarative fixture recipes (`videoBridgePromotionFixtures.ts`), a pure medians/p95 metrics aggregator, a pure FU-07/FU-09 promotion-verdict evaluator applying the ticket's exact thresholds (missing token usage always holds), a digest-only persistence layer that never retains raw media or raw model responses, and a versioned per-model promotion allowlist shipped empty with every model defaulting to `hold`. The FU-07/FU-09 promotion verdicts themselves remain HOLD — they require a real evidence run against real models on VPS 192.168.0.15. ([#12008](https://github.com/diegosouzapw/OmniRoute/pull/12008)) +- **feat(video bridge):** "embedded" transcript provenance can now be legitimately earned instead of merely asserted — a bounded, allowlisted (`mov_text`/`subrip`/`webvtt`) subtitle probe runs through the loopback-only Video Bridge broker (at most 2 streams, 10s subdeadline bounded by the request deadline, 256 KiB output, 4096-code-unit lines), normalized through a bounded, ReDoS-safe WebVTT parser and Zod-validated end to end. The adapter always resolves to an explicit `success`/`absent`/`transient_failure` outcome — a subtitle failure never breaks the visual description path, and only a fingerprint-verified broker response (never a caller-declared label) can produce embedded cues (#11659). ([#12011](https://github.com/diegosouzapw/OmniRoute/pull/12011)) +- Default new Antigravity-family connections (agy CLI imports and Antigravity OAuth connects) to model auto-sync, so live model discovery lands in the synced catalog and `/v1/models` picks up freshly released upstream models (e.g. Gemini 3.7 Flash tiers) without code changes. Existing connections keep their current setting; the per-connection dashboard toggle remains the opt-out. (#11685 — thanks @MumuTW) ([#11887](https://github.com/diegosouzapw/OmniRoute/pull/11887)) +- **feat(ui):** enable React Compiler (`reactCompiler: true` + `babel-plugin-react-compiler`) for automatic memoization at build time ([#11783](https://github.com/diegosouzapw/OmniRoute/pull/11783)) — thanks @jonlwheat2-gif +- **feat(providers):** add SeekAi (`seekai.cc`) as an OpenAI-compatible New-API gateway — catalog id `seekai` (alias `ska`), `https://seekai.cc/v1`, live `/v1/models` via `passthroughModels`, aggregator-list membership so New-API balance detection can opt in. No referral/aff codes. ([#11786](https://github.com/diegosouzapw/OmniRoute/issues/11786)) ([#12557](https://github.com/diegosouzapw/OmniRoute/pull/12557)) — thanks @HouMinXi +- **feat(zai):** add GLM-5.3-Flash Coding Plan support (1M context, 128K output, vision, `low|high|max` reasoning) and route `zai` GLM-5.3-family API-key traffic through the OpenAI-compatible Coding Plan endpoint with native thinking defaults ([#11801](https://github.com/diegosouzapw/OmniRoute/pull/11801)) — thanks @Neuron-Mr-White +- **feat(combo):** choose how combo models are ordered — manual, provider, score, or name — via a sort control in the dashboard builder, persisted in `config.modelSort` and re-applied on load and after add ([#11812](https://github.com/diegosouzapw/OmniRoute/pull/11812)) — thanks @maxmad64bis +- **feat(free):** custom models can be marked free-tier via `customModels[].isFree`; `isFreeModel()` is the first door and `hidePaidModels` respects it even for providers outside the free budget ([#11843](https://github.com/diegosouzapw/OmniRoute/pull/11843)) — thanks @maxmad64bis +- **feat(nodejs):** add `5dive` as a `configure` target — `omniroute configure 5dive` / `omniroute setup-5dive` write a 5dive auth profile that points an agent fleet's `claude` seats at OmniRoute, with the root-only write, the loopback-vs-`https` endpoint rule and the per-seat model pin handled explicitly ([#11852](https://github.com/diegosouzapw/OmniRoute/pull/11852)) — thanks @5dive-bot +- **feat(sse):** treat `max` as a first-class reasoning-effort tier and clamp per model family (GLM 5.1+/DeepSeek V4+/Kimi K3+ keep native `max`; o1/MiniMax/Grok/Muse Spark clamp to their upstream ceiling) ([#11875](https://github.com/diegosouzapw/OmniRoute/pull/11875)) — thanks @Chewji9875 +- **feat(providers):** the provider plugin manifest now advertises a `usage-fetch` capability for the 40 providers that have a wired usage/quota fetcher, so external dashboards can read it from `GET /api/v1/provider-plugin-manifest` instead of parsing `open-sse/services/usage.ts` after every release. Discovery only — no new fetcher, no quota change, and the Dashboard quota widget stays gated by `USAGE_SUPPORTED_PROVIDERS`. `USAGE_FETCHER_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/fetcherProviders.ts`) and is re-exported from `services/usage.ts`, keeping the manifest module a light leaf instead of pulling the ~490-module usage dispatcher into the manifest route. ([#11903](https://github.com/diegosouzapw/OmniRoute/pull/11903)) — thanks @maxmad64bis / @pacocartones +- **feat(plugins):** `OMNIROUTE_PLUGINS_DIR` sets the directory the runtime plugin scanner reads — and the root the plugin manager installs into — overriding the `HOME`-derived default, so a Docker/K8s deployment can point straight at its bind-mounted plugin tree instead of moving `HOME` just to relocate the scan path. An image that exports no home no longer scans `/tmp/.omniroute/plugins` in silence: the resolved directory is logged once at startup as `scanner.dir_resolved`, naming the input that won. Unset, behaviour is unchanged. Distinct from the CLI-only `OMNIROUTE_PLUGIN_PATH`, which finds `omniroute-cmd-*` command packages and never reached this scanner ([#11906](https://github.com/diegosouzapw/OmniRoute/pull/11906)) — thanks @amaleta / @pacocartones +- **feat(leases):** add an explicit owner-authenticated status action that returns only the active lease's privacy-safe configured connection and provider labels, with generation fencing and no credential or internal-id disclosure ([#11910](https://github.com/diegosouzapw/OmniRoute/pull/11910)) — thanks @KaspaPulse +- **feat(providers):** import-from-file modal shows per-row API errors and ships a downloadable CSV template ([#12071](https://github.com/diegosouzapw/OmniRoute/issues/12071)) ([#12504](https://github.com/diegosouzapw/OmniRoute/pull/12504)) — thanks @HouMinXi +- **feat(providers):** dashboard search matches connection name and `baseUrl` so imported OpenAI-compat nodes surface on the provider card ([#12108](https://github.com/diegosouzapw/OmniRoute/issues/12108)) ([#12495](https://github.com/diegosouzapw/OmniRoute/pull/12495)) — thanks @HouMinXi +- **feat(routing):** add a `score` Auto router strategy that selects the highest configured weighted score and reuses `explorationRate`. ([#12155](https://github.com/diegosouzapw/OmniRoute/pull/12155)) — thanks @jacobsparts +- Add a runtime feature flag to disable universal context handoffs globally without changing the default behavior. ([#12167](https://github.com/diegosouzapw/OmniRoute/pull/12167)) — thanks @jacobsparts +- **feat(providers):** the provider plugin manifest now also advertises a `usage-supported` capability for the 46 providers whose usage API is accepted by the server and Dashboard routes, so integrators can distinguish "the server will serve quota for this provider" from "a fetcher is wired" without reading TypeScript. Discovery only — no fetcher or quota change. `usage-fetch` resolves on id or alias (the usage dispatcher accepts both); `usage-supported` resolves on id alone, matching the runtime guard `USAGE_SUPPORTED_PROVIDERS.includes(providerId)`. `USAGE_SUPPORTED_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/supportedProviders.ts`) and is re-exported from `providers.ts`, mirroring the `fetcherProviders` leaf from #11903 and keeping the manifest a light module. ([#12214](https://github.com/diegosouzapw/OmniRoute/pull/12214)) — thanks @maxmad64bis +- **feat(rankings):** order Free Provider Rankings by what each provider actually served — `GET /api/free-provider-rankings?sortBy=reliability` and a "Most reliable first" toggle on the page. Providers with too few calls to state a success rate keep their score order below the measured ones; the default order is unchanged ([#12218](https://github.com/diegosouzapw/OmniRoute/pull/12218)). — thanks @maxmad64bis +- **perf(sse):** defer `cloneLogPayload()` in the structured SSE collector until after the `maxEvents`/`maxBytes` cap check, eliminating ~9,800 wasted `structuredClone` calls per streaming response (65–71% faster `push()`). Reducer snapshot isolation restored for OpenAI and Responses summaries ([#12241](https://github.com/diegosouzapw/OmniRoute/pull/12241)) — thanks @PauloHSOliveira ([#12243](https://github.com/diegosouzapw/OmniRoute/pull/12243)) +- **feat(usage):** Devin CLI agentic quota (Codeium seat-management GetUserStatus) and OpenRouter key limits plus account credits now surface in Provider Limits ([#12256](https://github.com/diegosouzapw/OmniRoute/pull/12256) — thanks @Neuron-Mr-White) +- **feat(settings):** persist `headroomUrl` through Settings so status/start use the operator URL instead of only `HEADROOM_URL` ([#12306](https://github.com/diegosouzapw/OmniRoute/issues/12306)) ([#12487](https://github.com/diegosouzapw/OmniRoute/pull/12487)) — thanks @HouMinXi +- **feat(auto-combo):** Auto-Combo scoring can now weigh how often a provider/model has actually succeeded. The engine already carried that number on every candidate — 24 hours of usage history behind a ten-sample floor, real-time metrics otherwise — and the scoring function never read it, while the weight table described `stability` as if it did. `reliability` (`1 - failureRate`, with the same field precedence and the same rate-bounding the speed ranking already uses, so a corrupt reading means "nothing observed" rather than "fails every call") is now a declared factor shipping at weight `0`, so routing is unchanged until an operator gives it one, and the `stability` description now matches what that factor computes ([#12317](https://github.com/diegosouzapw/OmniRoute/pull/12317)) — thanks @maxmad64bis +- **feat(routing):** With `freeAccessPolicy: "strict"`, the read-only candidate listing (`GET /v1/auto-combo/{channel}/candidates`) no longer hides the candidates the zero-cost guard excludes — the same read-only transparency the resilience filter already honours (#9133). Each candidate now carries `freeAccessExclusion` saying why it would be kept out, and it tells an exhausted allowance apart from a quota reading that never arrived or went stale, which used to look identical from the outside. Routing is unchanged: the listing reports, it never enforces. The separate `excludeTosAvoid` guard still drops its candidates without a reason; that gap is now documented rather than closed ([#12319](https://github.com/diegosouzapw/OmniRoute/pull/12319)) — thanks @maxmad64bis +- **feat(radar):** The Radar catalog table now shows two facts it was already receiving from the feed and dropping on the floor: the per-model rate limits (requests and tokens, per minute and per day) in a new column, and a badge when a provider's terms state it may train on the prompts you send. A limit of zero renders as zero rather than "rate-only" — for a ceiling those are opposite facts — and a model with no training statement gets no badge, because an absent statement is not a guarantee ([#12320](https://github.com/diegosouzapw/OmniRoute/pull/12320)) — thanks @maxmad64bis +- **feat(radar):** explain Community, single-use, contributor, supporter, recovery, abuse, offers, and privacy rules before either Radar activation action, and remove the superseded fixed-PR grant promise from every UI locale ([#12342](https://github.com/diegosouzapw/OmniRoute/pull/12342)) +- **feat(gamification):** the dashboard Profile page now shows the real daily streak — `/api/gamification/level` returns `streak: { current, longest }` (per key with `apiKeyId`, operator-wide maximum otherwise) and the streak card reads it instead of a hard-coded 0 (#2403) ([#12377](https://github.com/diegosouzapw/OmniRoute/pull/12377)) — thanks @pacocartones +- **feat(gamification):** the dashboard leaderboard now shows each API key's display name under the Name column instead of a truncated key id; `GET /api/gamification/leaderboard` attaches `name` per entry (name only — no key material), while the shared ranking helper and the federation leaderboard stay id-only — thanks @pacocartones ([#12385](https://github.com/diegosouzapw/OmniRoute/pull/12385)) +- **feat(gamification):** enforce the documented 1000 XP/min per-API-key anti-cheat rate limit on the XP award path; over-limit awards are logged and skipped instead of persisted, and the sliding window now matches the timestamp format stored in `xp_audit_log` ([#2403](https://github.com/diegosouzapw/OmniRoute/issues/2403)) ([#12390](https://github.com/diegosouzapw/OmniRoute/pull/12390)) — thanks @pacocartones +- **feat(admin):** localize the gamification anomalies page — the loading state, the Status column and the Suspicious badge now come from the `common` catalog (new `common.suspicious` key propagated to every locale) — add it to the Gamification sidebar group as `gamification-admin` (`/dashboard/gamification/admin`), and expose the loading and empty states as polite `role="status"` live regions (#12401 — thanks @pacocartones) +- **feat(providers):** skip GitHub combo members missing from the live synced catalog, and drop Copilot models that are policy-disabled or hidden from the model picker ([#12473](https://github.com/diegosouzapw/OmniRoute/pull/12473)) — thanks @RaviTharuma +- **feat(i18n):** locale-expansion tooling — `npm run i18n:add-locale` adds a language to config, dashboard, docs mirrors, CLI, README/indexes and site in one command; browser, CLI and cookie detection resolve `uk`, `fil`/`tl`, `zh-Hant` (and the retired legacy `in` → `id`) via config aliases; `config/i18n.json` now ships in the npm package so the published CLI can read it; new real-translation ratio gate (`npm run i18n:check-ratio`, advisory) with a per-locale ratchet baseline; `run-translation --adopt` restores `.i18n-state.json`; `sync-language-bars` generates 🌐 bars from config; `validate_translation.py` loads its allowlist again. Retires the duplicate `in` locale (Indonesian mislabelled as Hindi) — 42 honest locales; saved `NEXT_LOCALE=in` / `OMNIROUTE_LANG=in` keep working. (#12496) +- **feat(api):** Emit gateway-measured `tokens_per_second` (TTFT excluded) on streaming usage and `X-OmniRoute-Tokens-Per-Second` when first-token latency is known ([#12616](https://github.com/diegosouzapw/OmniRoute/issues/12616)) ([#12631](https://github.com/diegosouzapw/OmniRoute/pull/12631)) — thanks @RaviTharuma +- **feat(opencode-plugin): map gateway cost/usage/tok/s onto OpenCode inference payloads** — the official plugin copies `X-OmniRoute-Response-Cost`, token counts, `X-OmniRoute-Tokens-Per-Second` / `usage.tokens_per_second`, TTFT, and the winning `X-OmniRoute-Model` onto the JSON/SSE body OpenCode already consumes. Missing tok/s is left unset (never `tokens / latency`). (#12636) — thanks @RaviTharuma +- **feat(combo):** add combo strategy `quota-weighted`: skip exhausted accounts, then weighted-draw by leftover / in-flight load; existing conversations stay pinned until the account is empty ([#12789](https://github.com/diegosouzapw/OmniRoute/pull/12789)) — thanks @HouMinXi +- **feat(models):** Account live listings become the chat catalog source for Claude, Codex, Copilot, and AGY; public metadata only fills prices on IDs those accounts already list. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **fix(models):** Union `agy` and `antigravity` live catalogs so `agy/gemini-3.8-flash-high` is not rejected after the prefix folds to `antigravity`. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **feat(opencode):** Add `@omniroute/opencode-plugin-v2`, an opencode v2 plugin that gives a v2 install a route to an OmniRoute gateway: it publishes models, combos and auto-combos into the host catalog, refreshes them lazily behind a 300s TTL, and keeps serving the last known catalog from an on-disk snapshot when the gateway is unreachable. The v1 plugin (`@omniroute/opencode-plugin`) is untouched and keeps working as before. The plugin adapts to the host instead of assuming one shape: it reads the shape the host seeds into the catalog draft and publishes the `api` block, the pre-`api` top-level fields, or both. The gateway key comes from the host credential store when one is connected, so no secret needs to sit in a config file; a source the gateway refuses is named in a warning instead of silently degrading the catalog to raw model ids; and tool schemas bound for a Gemini model are stripped of the JSON-Schema keywords Gemini rejects, so tool calling survives that route ([#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870)) — thanks @maxmad64bis +- feat(services): show sanitized CLIProxyAPI account health from its authenticated management API without exposing credentials, file paths, or raw account metadata (#6342) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add AnySearch free web search + URL extract (webFetch) with typed results, credential validation, REST routing, and MCP selection - fallback-only ([#11690](https://github.com/diegosouzapw/OmniRoute/pull/11690)) — thanks @Xxx91n +- **feat(dashboard):** display clamped `[0, 100]%` cached input token ratio in request logs table ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) — thanks @b3nw +- **feat(catalog):** add `OMNIROUTE_DISABLE_THINKING_LEVEL_VARIANTS` feature flag to optionally filter out thinking level variants from model catalog ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) — thanks @b3nw +- **feat(dashboard):** continuously export call logs to external analytics stores. A pluggable destination registry ships the full Logs-tab record set on an hourly `JobRegistry` cron, with a persisted per-destination cursor, batched inserts, a config UI rendered from each destination's own field descriptors, and a REST layer (`/api/log-export/*`) for CRUD, a connection test, and an on-demand run. A destination can opt into `includeBodies` to also ship the request and response payloads shown in the Logs detail pane, including the client and provider views of each call; this is off by default, and payloads inherit the dashboard's PII sanitisation, secret redaction and `noLog` handling. Google BigQuery is the first destination, using a service-account key stored encrypted at rest and streaming inserts keyed by call-log id, into a table that is day-partitioned on `timestamp` and clustered on `api_key_name`, `provider`, `model` and `status`. ([#11945](https://github.com/diegosouzapw/OmniRoute/pull/11945)) — thanks @dpozimski +- **feat(providers):** add MaxAI as a signed, OpenAI-compatible provider serving its 13 paid chat models (GPT-5.6 / Luna / Thinking, Claude 5 Sonnet, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite, Grok 4.1-fast / 4.5, DeepSeek V3.2 / R1, Llama 3.3 70B) through OmniRoute's `/v1` endpoint, with per-request HMAC-SHA1→SM3→AES request signing, live model + context-window discovery from `/models/get_config`, and prompted tool-calling translated to OpenAI `tool_calls` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI vision input — image_url content parts are forwarded inline in `message_content` to the 6 vision-capable models (GPT-5.6 / Luna / Thinking, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite) ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI image generation — 6 image models (gpt-image-1, dall-e-3, flux-1-schnell/dev/pro, sd3-medium) exposed through `POST /v1/images/generations` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI document RAG — inline base64 file/document attachments are uploaded to MaxAI (content-addressed `doc_id`) and attached to the chat via `doc_list` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** browserless MaxAI onboarding — email device-pair login (`/api/providers/[id]/login`) and signed access-token refresh, so a connection can be created and kept fresh without a real browser or Google OAuth ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** per-provider TLS impersonation profile (MaxAI presents a Windows Firefox-150 client fingerprint) so its bot-sensitive endpoints accept OmniRoute traffic ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** add **Nimble** as a web-search and web-fetch provider (`nimble-search`) — `/v1/search` routes to Nimble's search API at `lite` depth (locale, freshness and include/exclude domain filters mapped onto the shared request contract), and `/v1/web/fetch` routes to Nimble Extract, which covers all four fetch formats (`markdown`, `html`, `links`, `screenshot`) from a single call. One API key serves both surfaces. ([#11620](https://github.com/diegosouzapw/OmniRoute/pull/11620)) — thanks @wildcard +- The npm publish is automatic again, through npm Trusted Publishing (OIDC): the hosted `stage-npm` job publishes with a short-lived credential minted from GitHub's id-token — no `NPM_TOKEN`, no 2FA prompt, provenance attached. `publish_mode=staged` (owner approves with 2FA) and `direct` (token) remain available on `workflow_dispatch`. ([#11931](https://github.com/diegosouzapw/OmniRoute/pull/11931)) +- **feat(providers):** Add **Opper** as an API-key gateway provider — EU-hosted AI gateway with 700+ models from 30+ providers behind one OpenAI-compatible API and one key (`OPPER_API_KEY`); model ids use `provider/model` format (e.g. `anthropic/claude-sonnet-4-6`, `openai/gpt-5`); live model catalog at `https://api.opper.ai/v3/compat/models`; entry mirrors `requesty` (same shape, `passthroughModels: true`, no static seed) ([#11629](https://github.com/diegosouzapw/OmniRoute/pull/11629)) +- **feat(dashboard):** the `/dashboard/orchestration` snapshot hook now subscribes to the `agents` WebSocket channel (`agent.task.updated`) instead of `requests` as its refetch trigger, and relaxes its background poll from 5s to 30s while that WS connection is up — falling back to the tighter 5s cadence, reprogrammed live on any connect/disconnect transition, whenever the socket is down. ([#12409](https://github.com/diegosouzapw/OmniRoute/pull/12409)) +- **feat(dashboard):** Orchestration canvas quick wins — search box plus state/source/provider filter chips with a one-click clear, and per-source collapse/expand, all reflected in the URL so a filtered/collapsed view is shareable and survives a refresh; the detail drawer gained a "copy trace JSON" action and hardened error/empty-state and accessibility handling; the Agents-tab edges now animate traveling particles along active (running) connections; and the canvas node/edge status colors moved off fixed hex values onto theme-aware `--orch-status-*` CSS custom properties, so they adapt correctly to light/dark mode. ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- **chore(dashboard):** Orchestration UI hardening pass and the missing component/model test coverage it called for — `OrchestratorNode`/`ActivityNode`/`OverflowNode` rendering, the `?node=`/overflow-click page routing, the Agents-tab orchestrator-click no-op and `showCompleted` toggle, and the overview kanban's done-column sort order (#12270, #12271). ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- New `/dashboard/orchestration` page: live unified view of everything running — Cloud Agent, A2A and Conductor as a real-time graph (Agents tab), the combo cascade (Routing tab, reusing the Combo Live Studio) and a state kanban (Overview tab), with a detail drawer (trace, cost, approve/cancel). Read-only over existing APIs — no new backend. Canvas concept credit: PR #11815 design ([#12261](https://github.com/diegosouzapw/OmniRoute/pull/12261)) +- **feat(dashboard):** new "History" tab on `/dashboard/orchestration` — an Airflow-style grid of finished runs over a 24h/7d/30d preset window, one row per (source, identity), clicking a cell opens the existing detail drawer. It is backed by real persistence: A2A task lifecycle transitions are now written to the `a2a_tasks` table (purged after 30 days, configurable via `OMNIROUTE_A2A_HISTORY_RETENTION_DAYS`) and served by the new `GET /api/a2a/tasks/history` listing endpoint, with the task-detail route falling back to persisted history once a run leaves the in-memory snapshot. Conductor runs stay remote and are not persisted locally — the tab says so instead of silently omitting them. ([#12479](https://github.com/diegosouzapw/OmniRoute/pull/12479)) +- **feat(dashboard):** the orchestration detail drawer gained a "Repeat" action for Cloud Agent, A2A and Conductor tasks — a two-click confirm (click once to arm, click again within the confirm window to fire) re-submits the original prompt/input as a new run. The button is disabled with an explanatory tooltip whenever the original input can't be recovered from the loaded task detail (e.g. it never carried a prompt, or the detail failed to load). Two limitations of the A2A variant, by design: it targets the `/a2a` JSON-RPC endpoint, which authenticates with an API key only (`REQUIRE_API_KEY=true` or a configured `OMNIROUTE_API_KEY` makes a dashboard-session repeat answer `HTTP 400` — surfaced verbatim in the drawer's error line, never as a success), and the `message/send` call is SYNCHRONOUS: the POST blocks for the whole skill run, so the success confirmation only appears once the repeated task finishes. A dashboard-authenticated A2A creation path is deliberately left to a follow-up — widening the endpoint's auth posture is an operator decision, not a side effect of this feature. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(a2a):** A2A task execution now records which memories were consulted for the task's last user message as `metadata.memoryHits` (id/key/type/content-snippet) plus a `memory_hits` history event, purely for observability — the retrieved memory is never injected into a skill's prompt or behavior. Gated by the `OMNIROUTE_A2A_MEMORY_HITS` kill-switch (default enabled; set to `0` to skip the recall lookup entirely). The drawer's new "Memory used" section lists these hits for a2a tasks and is omitted whenever there are none. Known limitation: recall only resolves under the keyless posture — a keyed caller's task owner is a SHA-256 prefix of the API key, while memory rows are keyed by the database api-key id, and no hash→id lookup exists today, so the hit list stays empty for keyed callers. The recorded hits are also kept out of the task's own `input` (and therefore out of the persisted input and of the "Repeat" request body), so repeating a task never re-sends the previous run's memory snippets. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(providers):** add a Perplexity Agent API provider (`perplexity-agent` / `pplx-agent`) for Perplexity `/v1/responses`, including the documented Anthropic, OpenAI, Google, xAI, DeepSeek, Z.AI, Moonshot/Kimi, NVIDIA, and Perplexity model IDs plus Anthropic-model `max_output_tokens` compatibility. ([#12103](https://github.com/diegosouzapw/OmniRoute/pull/12103)) — thanks @AIB1TAL0S +- **feat(providers):** add RPD (Requests Per Day) limit to provider rate limit overrides across UI, schemas, DB, and i18n ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) — thanks @b3nw +- **feat(providers): add UC Direct (uncensored.com Developer API), the metered OpenAI-compatible surface.** A standard OpenAI-compatible passthrough (default executor) for uncensored.com's official REST API at `https://api.uncensored.com/api/v1`: `X-api-key` auth (never-expiring `uai_sk_live_` key), `POST /chat/completions` with streaming SSE and native tool-calling, and the full live metered catalog (82 models across 15 providers, discovered from the public `GET /v1/models`). Registered as provider `uc-direct` (alias `ucd`). Complements the un-metered `uc` persona provider — same models, metered credits and a plain API key instead of a subscription session. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(providers): add UC (uncensored.com), the un-metered subscription "persona" chat as an OpenAI-compatible provider.** A WebSocket web-app port: a durable Clerk credential mints a short-lived session token per connect (browserless — no API key), driving UC's persona socket. Ships the browserless email-code login (request → verify → harvest), the 19 verified persona models (Claude Opus, Gemini, Grok, GLM, Kimi, DeepSeek, MiniMax, incl. the uncensored variants), prompted `` tool-calling with a per-model code-style dialect + auto-cure retry for guardrailed models, live ``/reasoning split, streaming + non-streaming OpenAI responses, and full quota/auth error surfacing (paywall / message-limit / rate-limit → 429, invalid session → 401 re-login). Full multimodal parity via the persona blob-upload layer: **vision** (image input, 15 vision-capable models), **document RAG** (PDF/doc upload, server-side extraction), **image generation** (22 models), **video generation** (14 models, async signed-url → poll), and **TTS** (streaming MP3). Registered as provider `uc` (alias `ucn`). The metered OpenAI-compatible Developer API is a separate `uc-direct` provider. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(logs):** add copy-all button to request detail modal ([#11083](https://github.com/diegosouzapw/OmniRoute/pull/11083)) — thanks @AndrianBalanescu +- **feat(check):** make serviceKinds required and add the reverse-walk provider consistency gate ([#11392](https://github.com/diegosouzapw/OmniRoute/pull/11392)) — thanks @Tushar49 +- **feat(providers):** only advertise verified-working connections by default ([#11448](https://github.com/diegosouzapw/OmniRoute/pull/11448)) — thanks @alltomatos +- **feat(resilience):** add hierarchical concurrency admission ([#11493](https://github.com/diegosouzapw/OmniRoute/pull/11493)) +- **feat(providers):** derive + surface expiry for JWT-bearing web cookies (#11497) ([#11505](https://github.com/diegosouzapw/OmniRoute/pull/11505)) — thanks @oyi77 +- **feat(tunnels):** support named cloudflared tunnels via CLOUDFLARED_CONFIG ([#11561](https://github.com/diegosouzapw/OmniRoute/pull/11561)) — thanks @ggiak +- **feat(compression):** make proactive context-compression threshold a live setting ([#11564](https://github.com/diegosouzapw/OmniRoute/pull/11564)) — thanks @ragnar-claude +- **feat(resilience):** add provider peak-hour protection ([#11622](https://github.com/diegosouzapw/OmniRoute/pull/11622)) — thanks @Neuron-Mr-White +- **feat(providers):** add 1min.ai provider ([#11631](https://github.com/diegosouzapw/OmniRoute/pull/11631)) — thanks @zero-executioner +- **feat(dashboard):** collapsible JSON tree viewer for request/response payloads ([#11703](https://github.com/diegosouzapw/OmniRoute/pull/11703)) — thanks @hartmark +- **feat(routing):** add LiquidAI LFM2.5-2.6B free tier via OpenRouter ([#11752](https://github.com/diegosouzapw/OmniRoute/pull/11752)) — thanks @brick30llc-ctrl +- **feat(pricing):** add GLM-5.3-Flash pricing, model specs, and catalog registration ([#11830](https://github.com/diegosouzapw/OmniRoute/pull/11830)) — thanks @AndrianBalanescu +- **feat(routing):** add relayMode for schema-locked context handoffs ([#11839](https://github.com/diegosouzapw/OmniRoute/pull/11839)) — thanks @raheemuddin786 +- **feat(config):** add support for runtime system prompt configuration and hot-reloading ([#11841](https://github.com/diegosouzapw/OmniRoute/pull/11841)) — thanks @raheemuddin786 +- **feat(routing):** expand connection-aware quota prefilter across combo strategies (#11682) ([#11850](https://github.com/diegosouzapw/OmniRoute/pull/11850)) — thanks @HouMinXi +- **feat(models):** reactive discovery sync after Antigravity model-not-found 404 ([#11888](https://github.com/diegosouzapw/OmniRoute/pull/11888)) — thanks @MumuTW +- **feat(models):** auto-aliases and token limits from synced Antigravity discovery ([#11889](https://github.com/diegosouzapw/OmniRoute/pull/11889)) — thanks @MumuTW +- **feat(settings):** raise sticky round-robin limit caps to 1000 ([#12015](https://github.com/diegosouzapw/OmniRoute/pull/12015)) — thanks @adivekar-utexas +- **feat(resilience):** operator-configurable global credential health check interval ([#12043](https://github.com/diegosouzapw/OmniRoute/pull/12043)) — thanks @adivekar-utexas +- **feat(quota):** use official OpenCode Go usage API ([#12124](https://github.com/diegosouzapw/OmniRoute/pull/12124)) — thanks @ddarkr +- **feat(resilience):** default the credential health check sweep to 60 minutes ([#12138](https://github.com/diegosouzapw/OmniRoute/pull/12138)) — thanks @adivekar-utexas +- **feat(quality):** new-code mode for the complexity and dead-code ratchets (Clean as You Code) ([#12142](https://github.com/diegosouzapw/OmniRoute/pull/12142)) +- **feat(dashboard):** orchestration canvas — unified model + snapshot hook (part 1/2) ([#12156](https://github.com/diegosouzapw/OmniRoute/pull/12156)) +- **feat(usage):** add Kilo Code balance and Kilo Pass quotas ([#12178](https://github.com/diegosouzapw/OmniRoute/pull/12178)) — thanks @killer30001000 +- **feat(providers):** refresh vendored ChatGPT Web connector to v4.0.7 ([#12181](https://github.com/diegosouzapw/OmniRoute/pull/12181)) — thanks @backryun +- **feat(providers):** manual "Clear cooldown" action in the cooling panel ([#12224](https://github.com/diegosouzapw/OmniRoute/pull/12224)) — thanks @patrykkopycinski +- **feat(providers):** restore ChatGPT Web via clean-room browser transport ([#12239](https://github.com/diegosouzapw/OmniRoute/pull/12239)) — thanks @backryun +- **feat(sse):** wire the PROVIDER_PROFILES window gate into the global provider cooldown ([#12247](https://github.com/diegosouzapw/OmniRoute/pull/12247)) +- **feat(providers):** modernize CLOVA Studio chat and embeddings ([#12277](https://github.com/diegosouzapw/OmniRoute/pull/12277)) — thanks @backryun +- **feat(browser):** adopt Obscura as primary headless browser engine with Chromium fallback ([#12286](https://github.com/diegosouzapw/OmniRoute/pull/12286)) — thanks @opensource-elearning +- **feat(sse):** full language parity for output styles (es/de/fr/it/ru/zh + autoDetect) ([#12289](https://github.com/diegosouzapw/OmniRoute/pull/12289)) +- **feat(quality):** complete test:scoped — --full map rebuild, stdin selection, CI loader parity (#8084 D1) ([#12353](https://github.com/diegosouzapw/OmniRoute/pull/12353)) +- **feat(providers):** refresh Fable, Cursor, and Devin catalogs ([#12367](https://github.com/diegosouzapw/OmniRoute/pull/12367)) — thanks @backryun +- **feat(video):** redact transcript text from logs and durable memory (#12150 P1) ([#12427](https://github.com/diegosouzapw/OmniRoute/pull/12427)) +- **feat(dashboard):** parent-link, genuine-continuation badge, and modal perf fixes ([#12448](https://github.com/diegosouzapw/OmniRoute/pull/12448)) — thanks @hartmark +- **feat(dashboard):** adaptive context-budget dial on compression panel ([#12488](https://github.com/diegosouzapw/OmniRoute/pull/12488)) — thanks @HouMinXi +- **feat(providers):** refresh Z.ai Web models and browser transport ([#12524](https://github.com/diegosouzapw/OmniRoute/pull/12524)) — thanks @backryun +- **feat(video):** redact raw client-snapshot transcript fields in the detailed log (#12150 P2) ([#12528](https://github.com/diegosouzapw/OmniRoute/pull/12528)) +- **feat(providers):** refresh NVIDIA hosted models ([#12538](https://github.com/diegosouzapw/OmniRoute/pull/12538)) — thanks @backryun +- **feat(quota):** Moonshot Open Platform balance and TPD lock for custom nodes ([#12590](https://github.com/diegosouzapw/OmniRoute/pull/12590)) — thanks @HouMinXi +- **feat(video):** redact transcript in the in-memory pending-request snapshot (#12430 item 6) ([#12596](https://github.com/diegosouzapw/OmniRoute/pull/12596)) +- **feat(dashboard):** link a log entry's Conversation Context to its owning conversation ([#12646](https://github.com/diegosouzapw/OmniRoute/pull/12646)) — thanks @hartmark +- **feat(catalog):** eligibility-gated free-tier bucket ([#12669](https://github.com/diegosouzapw/OmniRoute/pull/12669)) +- **feat(db):** fail-closed previous_response_id continuation for redacted video turns (#12150 P2b) ([#12707](https://github.com/diegosouzapw/OmniRoute/pull/12707)) +- **feat(gamification): pay the documented `streak_bonus` and `badge_unlock` XP rewards.** `XP_REWARDS` listed both rewards but the award pipeline never paid them: the private reward table in `events.ts` omitted them, `updateStreak()` did not report when a streak extended, and badge unlocks carried no XP. Every request that extends a daily streak now pays `streak_bonus × streak length` once per UTC day (guarded by a same-day `xp_audit_log` check), and every badge unlocked through the pipeline pays `badge_unlock` once per badge (guarded by the `user_badges` primary key; `unlockBadge()` now reports whether it inserted). Bonus XP flows through the same `addXp` + level sync + global/weekly/monthly leaderboard path as action XP, so level-ups and rankings include it. The Radar supporter recognition unlock stays XP-free. (#12522 — thanks @pacocartones) +- **feat(providers):** advertise a `free-tier` capability in the provider plugin manifest for every provider with documented free models, so sidecars and dashboards can filter free-capable providers without reading the quota catalog ([#12786](https://github.com/diegosouzapw/OmniRoute/pull/12786)) — thanks @maxmad64bis +- **feat(providers):** Added EURouter as an OpenAI-compatible API-key gateway (`https://api.eurouter.ai/v1`), with live model discovery via `passthroughModels`. Its copy states that models are served by third-party upstreams listed per model, so an EU-based router is not read as EU data residency for inference. ([#13025](https://github.com/diegosouzapw/OmniRoute/pull/13025)) — thanks @ntdatt812 +- **feat(providers):** Added GreenPT as an OpenAI-compatible API-key provider (`https://api.greenpt.ai/v1`), with live model discovery via `passthroughModels`. No free-inference badge: the published docs describe a free API subscription billed per token, not a free tier. ([#13024](https://github.com/diegosouzapw/OmniRoute/pull/13024)) — thanks @ntdatt812 +- **feat(i18n):** 9 new locales — Greek (`el`), Croatian (`hr`), Serbian (`sr`), Lithuanian (`lt`), Estonian (`et`), Latvian (`lv`), Slovenian (`sl`), Maltese (`mt`), Irish (`ga`) — across the dashboard, docs mirrors, CLI, README and the site; OmniRoute now ships every official EU language (51 locales). (#13044) +- **feat(i18n):** 8 new locales — Kannada (`kn`), Malayalam (`ml`), Odia (`or`), Punjabi (`pa`), Nepali (`ne`), Sinhala (`si`), Burmese (`my`), Khmer (`km`) — across the dashboard, docs mirrors, CLI, README and the site (59 locales). The translator now restores the ICU literal escape around angle placeholders and splits oversized docs sections before translating. (#0000) ([#13660](https://github.com/diegosouzapw/OmniRoute/pull/13660)) +- feat(providers): **list Agnes 3.0 Flash as the current free chat model, drop retired 1.5 Flash, add Image 2.0/2.5 Flash plus Video 2.5/2.5 Flash, and discover the live `/v1/models` catalog (including the CN host `api.agnes-ai.cn`).** `agnes-1.5-flash` now forwards to `agnes-3.0-flash`. Video 2.5 polls `GET /v1/videos/{id}` (not the V2.0 `/agnesapi` contract). Live `/v1/models` (2026-09-09) no longer serves 1.5; the wiki marks it deprecated. 3.0 Flash is 512K context / 65,536 max output, same window as 2.5. CN-region keys use the existing per-connection base-URL field, default stays `apihub.agnes-ai.com`. ([#13120](https://github.com/diegosouzapw/OmniRoute/pull/13120)) — thanks @HouMinXi +- **feat(sse):** Codex and OpenAI catalogs list GPT-6 Astra with effort aliases (`-low` through `-ultra`); Codex CLI identity pins `@openai/codex@0.153.4` in lockstep with the image ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **feat(dashboard):** the orchestration History tab gained a "Compare runs" mode — toggling it turns each grid cell into a 2-item selection queue (a 3rd click drops the oldest pick), and picking two cells opens a side-by-side comparison panel instead of the usual detail drawer. The panel fetches both runs' detail the same way the drawer does (falling back to persisted history once a run leaves the live TTL window) and shows, per side: identity/source/state, start time, a signed `right - left` delta for duration/cost/event count, the event timeline aligned by index, and any memory hits. One side's fetch failing never blocks the other, and a delta is only ever computed when both sides have a finite value — otherwise it renders "—", never `NaN`. Comparing two runs from different sources or skills still works; a banner marks the deltas as informational rather than hiding them, since the two runs aren't a strict apples-to-apples pair. ([#12677](https://github.com/diegosouzapw/OmniRoute/pull/12677)) +- **feat(dashboard):** badge a conversation that never reached a clean stop ([#12717](https://github.com/diegosouzapw/OmniRoute/pull/12717)) — thanks @hartmark +- **feat(release):** reconcile-changelog tool + version-anchored fragment aggregation ([#12987](https://github.com/diegosouzapw/OmniRoute/pull/12987)) +- **feat(mcp):** carry context_length through MCP list_models_catalog projection (#12776) ([#13406](https://github.com/diegosouzapw/OmniRoute/pull/13406)) — thanks @KooshaPari +- **feat(cli):** make startup readiness budget configurable (#13369) ([#13433](https://github.com/diegosouzapw/OmniRoute/pull/13433)) — thanks @KooshaPari + ### 🐛 Bug Fixes +- **security(streaming):** sanitize generic mid-stream error messages before emitting OpenAI, + Responses, or Claude SSE failure frames and before diagnostic logging, while preserving raw + failures for internal classification and keeping client disconnects out of provider failure state. ([#12457](https://github.com/diegosouzapw/OmniRoute/pull/12457)) +- **fix(claude):** `blockExtraUsage: false` no longer lets 5h quota preflight skip the account; extra usage is billed after the session bar is gone, so the request must reach Anthropic ([#12803](https://github.com/diegosouzapw/OmniRoute/pull/12803)) — thanks @HouMinXi +- Sanitize HuggingChat conversation-creation and message-send transport failures before they reach client error bodies or provider logs. ([#12467](https://github.com/diegosouzapw/OmniRoute/pull/12467)) +- **fix(dashboard):** Keep local and theme-aware provider SVG icons at a definite layout size so Chromium does not collapse them to 0×0 after the v3.8.50 image-rendering change ([#12054](https://github.com/diegosouzapw/OmniRoute/pull/12054)) — thanks @ponkcore +- **fix(security):** Sanitize provider and runtime failures before public API, SSE and MCP responses and before persistent request, proxy and usage logs, preventing credentials, stack traces and host filesystem paths from crossing those boundaries while preserving stable error codes and useful diagnostics. ([#12506](https://github.com/diegosouzapw/OmniRoute/pull/12506)) +- **fix(cli):** `omniroute tunnel create` no longer crashes with `Cannot read properties of undefined (reading optsWithGlobals)` — removed the duplicate positional argument that caused Commander.js to misalign the action callback parameters ([#12295](https://github.com/diegosouzapw/OmniRoute/issues/12295)) ([#12368](https://github.com/diegosouzapw/OmniRoute/pull/12368)) — thanks @gonisulaimann +- Stop advertising Gemini Live-only models as supported audio endpoints until OmniRoute proxies the bidirectional Live protocol. ([#11710](https://github.com/diegosouzapw/OmniRoute/pull/11710)) — thanks @RaviTharuma +- **fix(kie):** reroute `flux/kontext` off the KIE Market `createTask` flow — it is catalogued with `isMarket: true` but has no Market catalog page, so KIE rejected it with "model name not supported"; it now hits the dedicated `POST /api/v1/flux/kontext/generate` / `GET /api/v1/flux/kontext/record-info` endpoints instead (#11296). ([#11985](https://github.com/diegosouzapw/OmniRoute/pull/11985)) — thanks @hartmark +- **fix(kie):** correct 12 more KIE Market catalog ids that were sent to `createTask` unchanged but diverge from KIE's documented upstream `model` values — GPT Image 2 T2I/I2I (drops the `gpt/` prefix), GPT Image 1.5 T2I/I2I (`gpt-image/` namespace), Seedream 5.0 Lite T2I/I2I (drops the `.0`), all 4 Flux 2 variants (`flux-2/` namespace, generic variant renamed `flex`), and Wan 2.7 Image / Image Pro (dash instead of dot) — each verified individually against the literal example request published on docs.kie.ai. `#11326`'s "everything else already matches" claim was wrong a second time (#11296); `z-image/4.0-*`/`z-image/4.5-*` and `flux/kontext` remain open, documented as unresolved in `KIE_MARKET_UPSTREAM_MODEL_IDS`'s comment pending further verification. ([#11667](https://github.com/diegosouzapw/OmniRoute/pull/11667)) +- **fix(dashboard):** `useApiKeySave.handleSaveApiKey` no longer forces a full upstream `/models` catalog sync on every non-curated provider connection save — callers can now pass `skipModelSync: true` to opt out, so a workflow that only wants to add one manual model no longer floods the provider's available-models list with hundreds/thousands of synced entries. The flag is a client-side intent signal only and is stripped before the connection payload is POSTed to `/api/providers`; default behavior (full sync on save) is unchanged when the flag is omitted (#11324) ([#11664](https://github.com/diegosouzapw/OmniRoute/pull/11664)) +- **Provider connections:** keep `tokenExpiresAt` when a connection is created. The create-path allowlist omitted it, so every insert stored NULL and the dashboard token badge could read a fresh connection as expired until its first background refresh ([#11368](https://github.com/diegosouzapw/OmniRoute/pull/11368)). — thanks @ntdatt812 +- **fix(combos):** the combo builder's precision-select, global-model-search, and manual-entry flows now serialize a model step's `model` string using the provider's already-computed routing-alias prefix (e.g. `oc/`) instead of rebuilding it from the raw canonical `providerId`, fixing the no-auth "OpenCode Free" provider (`opencode`) being routed to the unrelated paid "OpenCode Zen" provider (`opencode-zen`) because `opencode` doubles as a manual routing-prefix override ([#11433](https://github.com/diegosouzapw/OmniRoute/issues/11433)). ([#11670](https://github.com/diegosouzapw/OmniRoute/pull/11670)) +- **fix(radar):** the catalog feed cache now keeps `generatedAt`, the date the feed's data was built, next to `fetchedAt`, the date this install downloaded it (#11435). The feed schema requires that date and the sync path validates it, but the cache dropped it — so a feed fetched minutes ago and one carrying weeks-old figures looked identical to everything downstream, including the dashboard's "Last fetched" line. `getRadarCatalog().meta` and `GET /api/radar/status` now report both dates, the latter as its own field rather than folded into `version` — and omitted entirely for the offers and intel caches, which keep no build date, where a `null` would read as "unknown" rather than "never stored". The dashboard still shows only the fetch time; surfacing the build date there needs a new translated label and is left to a follow-up. Rows cached before migration 163 read back as `null`: unknown stays unknown instead of borrowing the fetch time. The referrals cache has persisted the same date since migration 142. — thanks @maxmad64bis +- **fix(cli):** postinstall no longer fills `JWT_SECRET` and `API_KEY_SECRET` in the installed package's `.env` (#11436). `.env.example` ships both blank on purpose: the server restores them from its durable store, or generates and persists them there, in `ensureSecrets()`. Pre-filling them defeated that — the file lives inside the package directory, so `npm i -g` replaced it and postinstall wrote _different_ values, while `ensureSecrets()` (which only acts on an empty variable) never got to restore the real ones. Both secrets rotated silently on every update, invalidating dashboard sessions and API-key CRCs. `STORAGE_ENCRYPTION_KEY` left the same list for the same reason in #1622; its comment pointed at a function that no longer exists and now names the real provisioning path. — thanks @maxmad64bis +- **fix(cli):** the CLI now says when a loaded `.env` lives inside the installed package directory (#11437). It already announces every env file it reads, without distinguishing the ones that survive an update from the one that does not: `npm i -g` replaces the package directory wholesale, so values set there are gone at the next update, silently. The warning names the durable path to move them to, and fires only when that file actually supplied a value — a file entirely shadowed by a durable one supplied nothing. A development checkout stays silent: there the same path is stable and documented in `SETUP_GUIDE.md`. — thanks @maxmad64bis +- **fix(free-models):** the shared `isFreeModel()` predicate no longer reports catalog entries marked `freeType: "discontinued"` as free, so `hidePaidModels` can't route to Pollinations' seven premium models that now require a paid key ([#11441](https://github.com/diegosouzapw/OmniRoute/pull/11441)) — thanks @maxmad64bis +- fix(ui): let AnySearch use the normal provider-icon fallback when LobeHub has no matching icon (#11449) +- Fixed the v3.8.50 Costs and Analytics dashboards so flat-rate Claude Code usage can be shown as an explicitly requested token-price estimate without changing default billed-cost semantics. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed archived usage retention so each request is priced individually instead of pricing a day's summed tokens once, which understated archived cost whenever a day mixed cache-heavy and ordinary requests. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed the Costs dashboard so it discloses when displayed figures include flat-rate token-price estimates instead of labelling them as billed spend, using the flag the analytics API already returns; the month-end projection and the CSV/JSON exports carry the same marker, and billed-cost mode is unchanged. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- **fix(combo):** attach the same combo-diagnostics trace (`poolSize`/`attemptOrder`/`excluded`/`terminalReason`, plus `x-omniroute-combo-*` headers) to the round-robin strategy's and the nested pipeline/fusion runtime-unit loop's "Maximum combo retry limit reached" 503 that the priority-strategy path already attaches for the identical terminal condition — previously those two paths returned a bare, contextless 503 ([#11462](https://github.com/diegosouzapw/OmniRoute/issues/11462)). ([#11665](https://github.com/diegosouzapw/OmniRoute/pull/11665)) +- **fix(bun):** use native `bun:sqlite` in `bootstrap-env` and `sync-env` to avoid loading `better-sqlite3` N-API addon during Bun startup ([#11468](https://github.com/diegosouzapw/OmniRoute/pull/11468)) — thanks @TheDemonTuan +- **fix(docker-bun):** make `Dockerfile.bun` install reliable with pre-install script helpers and native `bun:sqlite` smoke check ([#11470](https://github.com/diegosouzapw/OmniRoute/pull/11470)) — thanks @TheDemonTuan +- **fix(docker-bun):** remove every vendored `better-sqlite3` native addon from the Bun runtime image so startup cannot abort before the `bun:sqlite` fallback ([#11482](https://github.com/diegosouzapw/OmniRoute/pull/11482)) — thanks @TheDemonTuan +- **fix(autoCombo):** effort/alias model variants (`gpt-5.6-sol-xhigh`, `gpt-5.6`, cursor's `claude-4.6-opus-high`) inherit their base model's task fitness instead of falling to the wildcard 0.5 ([#11492](https://github.com/diegosouzapw/OmniRoute/pull/11492)) — thanks @MumuTW +- fix(db): rate-limit repeated Arena ELO leaderboard fetch-failure warnings instead of logging one per sync attempt (#11500) ([#11989](https://github.com/diegosouzapw/OmniRoute/pull/11989)) — thanks @hartmark +- **fix(db):** dedupe the raw `[Encryption] Decryption failed...` log line emitted by the lazy-decrypt views (`createLazyRowProxy`/`createLazyConnectionView`), which power `getProviderConnections()` and were re-triggering that line on every CredentialHealth/model-sync cycle for the same corrupt or stale-key credential — a fresh Proxy over a fresh row on every cycle meant the per-proxy memoization never suppressed it, unlike the dedup `decryptConnectionFields()` already had since [#9927](https://github.com/diegosouzapw/OmniRoute/issues/9927). Now shares that dedupe tracking so the line logs at most once per credential ([#11500](https://github.com/diegosouzapw/OmniRoute/issues/11500)). ([#11678](https://github.com/diegosouzapw/OmniRoute/pull/11678)) +- **fix(intelligence):** Arena ELO sync stores a synthesized base-model row for every effort/harness variant (`claude-opus-5-max` → `claude-opus-5`, `gpt-5.6-sol-xhigh (codex-harness)` → `gpt-5.6-sol`), so bare model requests reach the synced intelligence layer instead of the static fallback table; the hand-maintained `MODEL_ALIAS_MAP` — which collapsed generations (`gpt-5.5` → `gpt-5`) — is gone ([#11506](https://github.com/diegosouzapw/OmniRoute/pull/11506)) — thanks @MumuTW +- **fix(autoCombo,sse):** catalog hygiene — the static task-fitness table no longer ranks retired models above live ones (dead rows dropped, patterns matched on segment boundaries), the 7 deprecation aliases that forwarded to retired or non-existent models now point at the vendors' published replacements, an alias is no longer rewritten when the serving provider offers the id as-is, and a new `check:model-lifecycle` gate diffs both tables against a vendor lifecycle snapshot ([#11507](https://github.com/diegosouzapw/OmniRoute/pull/11507)) — thanks @MumuTW +- **fix(sse):** stop dropping resolved `thoughtSignature` values on parallel (multi tool-call) turns sent to Gemini 3.x — the claude→gemini and openai→gemini translators previously kept the signature only on the *first* function call of a message, causing Gemini to reject subsequent calls in the same turn with HTTP 400 "Function call is missing a thought_signature"; each function call now keeps its own resolved signature ([#11510](https://github.com/diegosouzapw/OmniRoute/issues/11510)). ([#11669](https://github.com/diegosouzapw/OmniRoute/pull/11669)) +- Treat Docker publishing from a non-default release branch as an expected skip instead of a failed workflow, while preserving the guard that prevents it from overwriting the `next` channel. ([#11527](https://github.com/diegosouzapw/OmniRoute/pull/11527)) — thanks @pacocartones +- fix(search): prefer credentialed providers over duckduckgo-free fallback (#11524) — the fallback-only loop ran before the credentialed-providers loop in `executeWebSearch`, making configured providers unreachable when `duckduckgo-free` was available. ([#11565](https://github.com/diegosouzapw/OmniRoute/pull/11565)) — thanks @vsd2807 +- **fix(sse):** cap the upstream headers-wait phase for STREAMING requests to a client-realistic ceiling (110s, under Codex's own ~120s hard client-abort window) instead of the flat 10-minute `FETCH_TIMEOUT_MS` default — that default was 5x longer than the body-phase readiness watchdog's own adaptive bound, so a request whose upstream never returned any response at all (not even headers, e.g. a stalled NVIDIA target behind a tool-heavy Responses→Chat translation) kept the client connection alive on keepalives only, guaranteeing the client's own patience ran out first with an opaque 499 instead of OmniRoute detecting and failing the stall fast. Non-streaming requests are unaffected — they keep the existing flat default (`open-sse/utils/fetchStartTimeoutPolicy.ts`) (#11526) ([#11668](https://github.com/diegosouzapw/OmniRoute/pull/11668)) +- **fix(ci):** protocol-clients E2E harness boots the peer-stamped custom server (`run-next.mjs dev`) instead of the bare `next` CLI, so `LOCAL_ONLY` locality resolves from the real TCP peer stamp and `/api/mcp/audit` answers 200 (open bootstrap preserved via an `OMNIROUTE_E2E_BOOTSTRAP_MODE=open` credential clear in `run-next.mjs`, pinned to loopback `HOST`) instead of a deterministic 403 (#11535) ([#11549](https://github.com/diegosouzapw/OmniRoute/pull/11549)) — thanks @jonlwheat2-gif +- **fix(docs):** the Auto-Combo engine is described with the number of factors it actually declares. Four different counts were in circulation — 6, 9, 13 and 14 across `AGENTS.md`, four guides and six code comments — while `DEFAULT_WEIGHTS` declares 15. `check:docs-counts` now reads that number from the source, so adding a factor without updating the prose fails CI ([#11545](https://github.com/diegosouzapw/OmniRoute/pull/11545)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **fix(quota-share):** in-flight leases are per request, so an aborted request's slot ages out instead of being kept alive by later traffic on the same connection ([#11547](https://github.com/diegosouzapw/OmniRoute/pull/11547)) — thanks @abhisheksharma2411 +- The free-tier summary route now serves the refreshed Radar catalog when the feed is active — the same numbers the dashboard shows — and states which catalog answered plus its real build date, instead of always reporting release-frozen figures with a stale curation date. The supporter-key live feed stays reserved to authenticated callers of the instance. ([#11550](https://github.com/diegosouzapw/OmniRoute/pull/11550)) — thanks @maxmad64bis +- **sse:** fix LiveWS/embed-WS servers crashing at startup under the Node/tsx runtime — `liveServer.ts`, `embedWsProxy.ts` and `apiBridgeServer.ts` imported `@/shared/utils/httpClientAbortGuard` without the `.mjs` extension, so the client-abort crash guard added by [#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556) was unreachable and every dependent test failed with `ERR_MODULE_NOT_FOUND` ([#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556)). ([#11671](https://github.com/diegosouzapw/OmniRoute/pull/11671)) — thanks @jonlwheat2-gif / @yxyxy +- fix(chat): shed any of the matched provider-node's routing identifiers (public prefix or internal id) before model lookup, so composites like `//` no longer reach upstream verbatim (#11557) — thanks @maxmad64bis +- **fix(cache):** a semantic-cache entry now expires on its TTL instead of surviving until the next UTC midnight, and `dbEntries` stops counting expired rows ([#11573](https://github.com/diegosouzapw/OmniRoute/pull/11573)) — thanks @ntdatt812 +- **fix(api):** `/v1/models` schedules its stale-while-revalidate rebuild through Next's `after()` again, so a stale catalog reaches the client before the rebuild blocks the event loop ([#11574](https://github.com/diegosouzapw/OmniRoute/pull/11574)) — thanks @ntdatt812 +- **fix(check):** the `known-symbols` executor conformance gate awaits the now-async `getExecutor()`, so it stops reporting all 142 registered aliases as dead symbols and can detect a lazy import that fails ([#11582](https://github.com/diegosouzapw/OmniRoute/pull/11582)) — thanks @ntdatt812 +- **fix(pack):** register `bin/cli/utils/volatileEnvPath.mjs` in `PACK_ARTIFACT_REQUIRED_PATHS`, so the prune can no longer silently drop a module the published CLI imports on every boot ([#11588](https://github.com/diegosouzapw/OmniRoute/pull/11588)) — thanks @ntdatt812 +- **i18n(vi):** translate the five reliability-column strings on the free-provider rankings page, which rendered as `__MISSING__:` markers in the Vietnamese UI ([#11589](https://github.com/diegosouzapw/OmniRoute/pull/11589)) — thanks @ntdatt812 +- **resilience:** restore the expired-connection retry-budget probe in the token-health sweep — the `!isGitHubAccessTokenOnlyConnection` carve-out reintroduced by #11608 contradicted the boundary pinned by #11592, so a GitHub connection parked at `expired` with retry budget remaining was never probed and could never self-heal ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)). ([#11672](https://github.com/diegosouzapw/OmniRoute/pull/11672)) — thanks @jonlwheat2-gif / @ntdatt812 +- **fix(dashboard):** Provider Stats now displays combo success rates on their native 0–100 percentage scale instead of multiplying them twice ([#11595](https://github.com/diegosouzapw/OmniRoute/pull/11595)) — thanks @pacocartones +- **fix(providers):** route DuckDuckGo Web Chat through the verified `duck.ai` endpoint, refresh its free model catalog, and avoid forwarding unsolved anti-abuse challenges ([#11597](https://github.com/diegosouzapw/OmniRoute/pull/11597)) — thanks @solstxce +- **fix(ui):** Console log Refresh and Copy controls now expose localized accessible names, keep copy actions visible on keyboard focus, and announce copy completion safely ([#11599](https://github.com/diegosouzapw/OmniRoute/pull/11599)) — thanks @pacocartones +- Dropped the stale-`.eslintcache` `restore-keys` fallback from both "Restore ESLint file cache" steps in `ci.yml`, so the blocking `Lint` job can no longer be served per-file verdicts computed under a different lint config, suppressions file or lockfile. `quality.yml` had already dropped it in #11963; `ci.yml` — the workflow that actually gates PRs — had not (#11600). ([#11996](https://github.com/diegosouzapw/OmniRoute/pull/11996)) +- fix(ci): pass `--pass-on-unpruned-suppressions` in `run-eslint-json.mjs` so the CI Lint job no longer fails when a suppression is merely orphaned by a genuine fix, mirroring the identical fix already in `validate-release-green.mjs` (#11600) ([#11983](https://github.com/diegosouzapw/OmniRoute/pull/11983)) — thanks @hartmark +- fix(handoff): enforce provider allowlist for universal handoff (#11602) — universal handoff now skips summarization when the selected summary model's provider is not included in the configured provider allowlist. ([#11619](https://github.com/diegosouzapw/OmniRoute/pull/11619)) — thanks @vsd2807 +- **fix(dashboard):** Show a stable error state when Search Analytics returns an HTTP error or malformed data ([#11603](https://github.com/diegosouzapw/OmniRoute/pull/11603)) — thanks @pacocartones +- **test(gamification):** pin the aggregate profile level to the XP-derived semantics of #11604 — `getAggregateXp()` now derives `currentLevel` from the summed XP (`calculateLevel(sum)`), not `MAX(stored current_level)`, and the #3484 fixture levels are aligned with the XP curve ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)). ([#11676](https://github.com/diegosouzapw/OmniRoute/pull/11676)) — thanks @jonlwheat2-gif / @pacocartones +- **fix(dashboard):** Keep the Profile level and progress aligned with aggregate XP, including bounded handling for invalid totals ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)) — thanks @pacocartones +- **fix(dashboard):** Prevent locked hidden badges from revealing their icon or opening private badge details before they are earned ([#11605](https://github.com/diegosouzapw/OmniRoute/pull/11605)) — thanks @pacocartones +- **fix(dashboard):** Restore keyboard focus after shared modals close and cancel delayed autofocus during cleanup ([#11607](https://github.com/diegosouzapw/OmniRoute/pull/11607)) — thanks @pacocartones +- **fix(dashboard):** Enable Enter and Space activation for clickable data-table rows without hijacking nested controls ([#11610](https://github.com/diegosouzapw/OmniRoute/pull/11610)) — thanks @pacocartones +- **fix(autoCombo,sse):** vendor-retired catalog ids are dropped from the auto-combo candidate pool and no longer win on leftover `arena_elo` / `user_override` rows; `getModelLifecycleDecision` consults `model-lifecycle.json` (prefix-stripped) so aggregator traffic is not `untracked` for ids the snapshot already knows ([#11625](https://github.com/diegosouzapw/OmniRoute/issues/11625)) ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)) — thanks @MumuTW +- **test(chatcore):** move Codex/Claude combo fixtures off models the lifecycle guard now rejects — `gpt-5.1-codex`/`gpt-5-codex` are vendor-retired (snapshot, #11626) and `claude-3-5-sonnet-20241022` is shut down, so native-passthrough and combo-fallback tests switched to `gpt-5.6-sol` and `claude-sonnet-4.6` ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)). ([#11675](https://github.com/diegosouzapw/OmniRoute/pull/11675)) — thanks @jonlwheat2-gif / @MumuTW +- fix(opencode-plugin): stop a free-tier budget that rounds up across a magnitude from rendering as `1000K`/`1000M` in the model picker — `fmtTokens` chose its unit from the raw token count and then rounded with `toFixed(1)`, so 999,950–999,999 printed as `1000K` rather than `1M` and just under a billion printed as `1000M` rather than `1B` ([#11684](https://github.com/diegosouzapw/OmniRoute/pull/11684)) — thanks @abhisheksharma2411 +- Cap prose-derived Antigravity quota resets at the configured model cooldown maximum while preserving authoritative Retry-After headers and Google RetryInfo hints. (#11695) Thanks @pacocartones. ([#11823](https://github.com/diegosouzapw/OmniRoute/pull/11823)) +- Compare `_rebaseline_*` notes structurally in the ratchet-bank verifier so an object-valued note no longer reports itself as rewritten on an unmodified tree — the nightly `bank-ratchet-shrinks` lane had been aborting on every run since 2026-08-11, leaving 41,672 lines of earned file-size shrinks unbanked (#11702). ([#11892](https://github.com/diegosouzapw/OmniRoute/pull/11892)) — thanks @MumuTW +- **fix(build):** `prepublish.ts` bundles the ChatGPT Web (Codex) MCP bridge through `runBuildTool()` instead of spawning `npx.cmd` raw, fixing the build crash on Node ≥ 20/Windows where `.cmd` shims cannot be spawned without a shell (EINVAL) ([#11704](https://github.com/diegosouzapw/OmniRoute/issues/11704)) ([#11706](https://github.com/diegosouzapw/OmniRoute/pull/11706)) — thanks @jonlwheat2-gif +- fix(codex): keep `parallel_tool_calls:false` on the translated Codex Responses Lite path (#11707) ([#11984](https://github.com/diegosouzapw/OmniRoute/pull/11984)) — thanks @hartmark +- **fix(tests):** the new Gemini Web Images retirement test (#11708) called the now-async `getExecutor()` synchronously, throwing `TypeError: getExecutor(...).getProvider is not a function` instead of asserting Gemini Web chat stays available. Awaited to match the R0.3 executor-registry refactor (#11220). ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(search):** Allow REST and MCP web-fetch requests to select the advertised `anysearch-search` provider explicitly ([#11716](https://github.com/diegosouzapw/OmniRoute/pull/11716)) — thanks @pacocartones +- **fix(providers):** Keep overnight peak-hour protection active after midnight according to the window's configured start day ([#11718](https://github.com/diegosouzapw/OmniRoute/pull/11718)) — thanks @pacocartones +- **fix(providers):** correct all five g4f.space gateway descriptions to document proof-of-work credits or member API keys, variable limits, and the remote prompt boundary instead of promising free keyless access at five requests per minute ([#11738](https://github.com/diegosouzapw/OmniRoute/pull/11738)). +- **fix(db):** `model_capabilities` is created by a migration instead of lazily on the first models.dev sync, so a clean install and an upgraded install converge on the same schema regardless of which features have run ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(ci):** `check:install-upgrade` now fails on an `npm` install truncated by ENOSPC (npm reports it as a warning and still exits 0), authenticates its health probe so the version assertion works against the hardened health payload, frees the clean-install tree before the upgrade phase, and no longer reports a schema divergence computed from a boot that never served ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(routing):** harden session-affinity key derivation against oversized explicit IDs and empty structural-payload collisions, while recognizing Gemini and common text request shapes ([#11746](https://github.com/diegosouzapw/OmniRoute/pull/11746)) — thanks @MumuTW +- **fix(packages/browser-pool):** regenerate `package-lock.json` so the `packages/browser-pool` workspace's locked `playwright`/`@types/node` (and transitives) match its `package.json` specs, fixing cache-only/offline installs (`npm ci --offline`, Nix `buildNpmPackage`) that previously failed with `ENOTCACHED` ([#11747](https://github.com/diegosouzapw/OmniRoute/issues/11747)) — thanks @benjaminkitt ([#11784](https://github.com/diegosouzapw/OmniRoute/pull/11784)) — thanks @geekyNads +- **fix(assets):** Stop distributing 79 provider and CLI logos without verified source/license provenance, keep every provider/tool available through the internal generic icon, and prevent those IDs from falling through to an external icon CDN. ([#11750](https://github.com/diegosouzapw/OmniRoute/pull/11750)) +- **fix(providers):** retire the common `chatgpt-web` / `cgpt-web` provider because its pre-key and proof-of-work provenance could not be cleared ([#11754](https://github.com/diegosouzapw/OmniRoute/pull/11754)) — explicit chat and image requests, slash-prefixed models, persisted aliases, proxy overrides, and combo targets now fail closed with a sanitized HTTP `410` / `PROVIDER_RETIRED` before upstream dispatch. Migration 163 tombstones matching connections and invalidates their active leases without deleting history or widening API-key allowlists. The independent `chatgpt-web-codex` / `cgpt-codex` provider and its MIT-noticed vendored implementation remain available. +- **fix(usage):** quota and usage refresh no longer 409 when an exclusive lease reserves the connection ([#11758](https://github.com/diegosouzapw/OmniRoute/pull/11758)) — thanks @TheDemonTuan +- Keep the embedding registry's vector width and `embedding` type on models when a synced model exists for the same id, so `/v1/models` no longer reports registry-described embedding models widthless or untyped (#11761) — thanks @rahilmavani +- Correct `google/gemini-embedding-001` on the OpenRouter route to 3072 dimensions, the width it returns when `dimensions` is not sent (#11761) — thanks @rahilmavani +- **fix(vision-bridge):** Restrict automatic vision routing and fallbacks to models available in authoritative live provider catalogs, including aliases and effort variants ([#11767](https://github.com/diegosouzapw/OmniRoute/issues/11767)) — thanks @pacocartones ([#11813](https://github.com/diegosouzapw/OmniRoute/pull/11813)) +- **fix(providers):** reclassify Cerebras as a one-time $5 signup credit (payment method required, 30-day validity), not a recurring no-card 1M tokens/day trial ([#11773](https://github.com/diegosouzapw/OmniRoute/issues/11773)) ([#12591](https://github.com/diegosouzapw/OmniRoute/pull/12591)) — thanks @HouMinXi +- fix(cli): stop prepublish from re-rebuilding the already-built ESM-only opencode-plugin dist (#11787) ([#11990](https://github.com/diegosouzapw/OmniRoute/pull/11990)) — thanks @hartmark +- **fix(db):** Fresh installs no longer log a non-fatal `no such table: compression_run_telemetry` warning when retention cleanup runs before the lazily-created telemetry table exists ([#11802](https://github.com/diegosouzapw/OmniRoute/pull/11802)) — thanks @RaviTharuma +- fix(sse): set X-OmniRoute-Selected-Connection-Id on successful combo dispatches so downstream consumers stop falling back to an empty connection id (#11810) ([#11986](https://github.com/diegosouzapw/OmniRoute/pull/11986)) — thanks @hartmark +- **fix(services):** embedded CLIProxyAPI lifecycle checks now use public `/healthz`, while model discovery uses the configured dedicated data-plane API key instead of the management password ([#11811](https://github.com/diegosouzapw/OmniRoute/pull/11811)) — thanks @RaviTharuma +- Fix persisted model steps returning an error in the playground route simulator, and warn when structural steps cannot be simulated. ([#11862](https://github.com/diegosouzapw/OmniRoute/pull/11862)) — thanks @pacocartones +- **fix(providers):** Antigravity's dynamic mitmAlias table no longer routes `gemini-3.7-flash-{high,medium,low}` to a literal tier-suffixed upstream id just because one connected account's own discovery listed it directly — those display ids always resolve through the safe `gemini-3.7-flash-tiered` static alias, so one account's Google-provisioned access no longer 404s every sibling account of the provider ([#11824](https://github.com/diegosouzapw/OmniRoute/issues/11824), [#11651](https://github.com/diegosouzapw/OmniRoute/issues/11651)) ([#11988](https://github.com/diegosouzapw/OmniRoute/pull/11988)) — thanks @hartmark +- **fix(dashboard):** Expose Profile loading, errors, page structure, and XP progress to assistive technologies ([#11838](https://github.com/diegosouzapw/OmniRoute/pull/11838)) — thanks @pacocartones +- **fix(socks):** `Agent.connectTimeout` now bounds both SOCKS handshake and TLS connect, and `family === null` no longer falls back to `fetch-socks` ([#11842](https://github.com/diegosouzapw/OmniRoute/pull/11842)) — thanks @maxmad64bis +- **perf(compression):** OOM mitigations for large payload hashing, memoization, and token estimation ([#11844](https://github.com/diegosouzapw/OmniRoute/pull/11844) — thanks @AndrianBalanescu) +- Excluded Next.js Node File Trace manifests (`*.nft.json`) from the published npm tarball. They are build-time metadata and are never read while serving, but had grown to 668.7 MB — 61% of the package — which pushed the upload past the registry limit and made `npm publish` fail with `413 Payload Too Large`. ([#11864](https://github.com/diegosouzapw/OmniRoute/pull/11864)) +- **fix(cli):** keep the global OmniRoute server `--base-url` separate from provider-node `--endpoint` values in add, update, and validate commands ([#11860](https://github.com/diegosouzapw/OmniRoute/pull/11860)) — thanks @pacocartones +- **fix(config):** Nous Research's `Hermes-4-405B` model now displays as "Hermes 4 405B (Nous Research)" in both the provider registry and the free-model catalog, instead of the mislabelled "Hermes 4 7B" ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#11993](https://github.com/diegosouzapw/OmniRoute/pull/11993)) +- **fix(provider/nous):** inject required user= tag into Nous Research inference requests to resolve upstream 400 "missing tags" error ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#12044](https://github.com/diegosouzapw/OmniRoute/pull/12044)) +- **fix(build):** `npm run build` now fails in one second with a named package and a copy-pasteable fix when npm silently drops an externalised optional native dependency, instead of dying four minutes in with `Module not found: Can't resolve 'better-sqlite3'` ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- **fix(install):** `postinstall` no longer throws `ReferenceError: isAndroid is not defined` — failing the whole `npm install` — when the `better-sqlite3` rebuild fallback times out; the manual-fix guidance is reachable again ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- Fixed the Alibaba free-tier allowlist test that went red on its own once the shipped catalog's `validUntil` (2026-08-27) passed, leaving every PR and `main` with a failing `Unit Tests (1/8)`. The test now builds its own packs with dates it controls, and covers the expired-pack fallback that production has actually been serving. ([#11867](https://github.com/diegosouzapw/OmniRoute/pull/11867)) +- Split the npm registry upload into its own GitHub-hosted job. npm refuses `--provenance` from a self-hosted runner (`422 ... Only "github-hosted" runners are supported`), which blocked the v3.8.50 publish; the heavy verification cannot move to a hosted runner, so it now hands the proven tarball over instead. ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(ui):** the shared `DataTable` loading state no longer reads its decorative ⏳ glyph out to assistive technology, and now carries the same `role="status"` / `aria-live="polite"` / `aria-busy="true"` semantics as `PageLoading` ([#11869](https://github.com/diegosouzapw/OmniRoute/pull/11869)) — thanks @pacocartones +- **fix(api):** Bound the `timeout` accepted by `POST /api/providers/[id]/login` to the same 15s–600s window the Adobe Firefly and Conol browser-login paths already enforce, so a request can no longer pin a headful browser and the single active-login slot with an arbitrarily large poll budget. ([#11871](https://github.com/diegosouzapw/OmniRoute/pull/11871)) — thanks @pacocartones +- **fix(api):** Stop the tunnel and MITM routes from returning raw child-process error text, which disclosed host paths, binary install locations and Tailscale `tskey-*` credentials in a body some of these routes serve to non-loopback callers, and return a real 400 instead of a framework 500 when a tunnel request body fails validation. ([#11872](https://github.com/diegosouzapw/OmniRoute/pull/11872)) — thanks @pacocartones +- **fix(open-sse):** stream TTFT/ITL are sampled from a monotonic clock (`performance.now`) instead of `Date.now`, so an NTP correction or wall-clock jump can no longer inflate a genuine TTFT or produce a negative one that the `ttft >= 0` guard silently drops — protecting the router's OTel/EWMA/`usage_history`/speed-ranking signals ([#11873](https://github.com/diegosouzapw/OmniRoute/pull/11873)) — thanks @pacocartones +- **fix(providers):** xAI `reasoning_effort: "xhigh"` now reaches grok-4.6+ instead of being silently clamped to `"high"` ([#11879](https://github.com/diegosouzapw/OmniRoute/pull/11879)) — thanks @NoxzRCW +- **fix(dashboard):** the providers page no longer crashes into the error boundary when a provider id collides with an `Object.prototype` member (`constructor`, `__proto__`); icon lookups are own-property guarded ([#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)) — thanks @NoxzRCW +- **fix(skills):** injected skill tools declared in shorthand (`{"content": "string"}`) now forward valid JSON Schema, unblocking providers that validate tool schemas strictly such as Zhipu GLM on the Console Go tier ([#11881](https://github.com/diegosouzapw/OmniRoute/pull/11881)) — thanks @NoxzRCW +- **fix(api):** `POST /api/playground/simulate-route` now surfaces `combo-ref` and `provider-wildcard` persisted combo steps with a specific warning (naming the referenced combo, or the unresolved `provider/model` wildcard) instead of folding them into a generic "unsupported step" count; a `provider-wildcard` step is also now included as an unresolved target so the operator can see it is in the route (ported from [#11882](https://github.com/diegosouzapw/OmniRoute/pull/11882) — thanks @NoxzRCW). ([#11930](https://github.com/diegosouzapw/OmniRoute/pull/11930)) +- **fix(translator):** the streaming OpenAI→Claude translator keeps upstream usage, including prompt-cache tokens, when it arrives on a trailing `choices: []` chunk (Fireworks and any upstream using `stream_options.include_usage`) ([#11883](https://github.com/diegosouzapw/OmniRoute/pull/11883)) — thanks @NoxzRCW +- **fix(cli):** `omniroute update --apply` now tells you explicitly whether a running server was detected and, if so, that you must run `omniroute restart` to apply the update — it never restarted anything and previously implied the update was already live once files were installed. The dashboard's npm-mode Update flow (`/api/system/version`) now tries OmniRoute's own PID-file-managed supervisor before falling back to pm2, and reports an honest "restart required" step instead of a silent pm2-only "skipped" that read like a completed update. The server-side latest-version lookup backing the dashboard's update banner also gained `--prefer-online`, closing the same stale-npm-cache class already fixed in the CLI's own copy for #4376 ([#11885](https://github.com/diegosouzapw/OmniRoute/issues/11885)). ([#12005](https://github.com/diegosouzapw/OmniRoute/pull/12005)) +- **fix(resilience):** clear persisted LKGP pins when a target suffers connection/provider exhaustion or is skipped before dispatch due to cooldown/exhaustion/unavailability, preventing subsequent requests from repeatedly prioritizing known-dead providers ([#11911](https://github.com/diegosouzapw/OmniRoute/issues/11911)). ([#12013](https://github.com/diegosouzapw/OmniRoute/pull/12013)) — thanks @HouMinXi +- **fix(sse):** the OpenAI→Claude stream translator now defers the terminal `message_delta`/`message_stop` emission until the real usage block has arrived (or a genuine end-of-stream flush forces it) instead of emitting it immediately on `finish_reason` — previously, when the trailing usage-only chunk (`{"choices":[],"usage":{...}}`) arrived *after* the `finish_reason` chunk (the normal order for Fireworks/vLLM/Together and other `stream_options.include_usage` upstreams), the client-visible `message_delta` still carried stale/zero usage even though `state.usage` was internally corrected too late to matter (ported from [#11915](https://github.com/diegosouzapw/OmniRoute/pull/11915) — thanks @HouMinXi). ([#11933](https://github.com/diegosouzapw/OmniRoute/pull/11933)) +- **fix(sse):** `stripStore()` now forces `store=false` for stateless OpenAI-compatible Responses-API targets (unless the connection has explicitly opted in via `providerSpecificData.openaiStoreEnabled`), instead of only handling the `openai`/`agentrouter` cases — a client-supplied `store` value previously passed through untouched to backends that don't actually persist responses server-side ([#11916](https://github.com/diegosouzapw/OmniRoute/pull/11916) — thanks @HouMinXi). +- **fix(models):** custom provider-node models (synced, custom, and alias-backed) now appear under their configured prefix in the unified catalog when the operator's model-id prefix mode is canonical, instead of being dropped whenever alias-inclusion was otherwise disabled ([#11918](https://github.com/diegosouzapw/OmniRoute/pull/11918) — thanks @HouMinXi). +- **fix(models):** the unified model catalog now suppresses stale static registry models (including effort-tier variants) for any provider whose active connection has an authoritative live synced catalog, not only providers already using exclusive-synced-listing — a connection with `providerUsesAuthoritativeLiveCatalog` previously kept serving both the live-synced models and the stale static rows side by side ([#11919](https://github.com/diegosouzapw/OmniRoute/pull/11919) — thanks @HouMinXi). +- **fix(ui):** `ProviderIcon`'s three remaining unguarded lookups (`PROVIDER_ICON_ALIASES`, `LOCAL_SVG_ALIASES`, `THEMED_SVGS`) now use `Object.hasOwn()` own-property checks — a provider id such as `constructor` or `__proto__` previously resolved through the prototype chain instead of falling through to the unknown-provider CDN fallback (`getLobeProviderIcon()` itself was already guarded by [#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)); `ProviderPageHeader`'s `color` field is also now optional, matching the rest of the component's defensive typing (ported from [#11920](https://github.com/diegosouzapw/OmniRoute/pull/11920) — thanks @HouMinXi). ([#11935](https://github.com/diegosouzapw/OmniRoute/pull/11935)) — thanks @NoxzRCW +- fix(ollama): preserve multi-byte UTF-8 content split across stream chunks in the Ollama NDJSON transform, which previously corrupted CJK/emoji into U+FFFD (#11921) — thanks @pacocartones +- **fix(providers):** OrcaRouter chat requests now target `/v1/chat/completions` instead of the bare `/v1` API root, fixing the upstream `404 Invalid URL (POST /v1)` ([#11923](https://github.com/diegosouzapw/OmniRoute/pull/11923)). — thanks @echel0nn +- **fix(plugins):** deliver the `onStreamComplete` event to disk-installed plugins. The event shipped in v3.8.50 (#9669) was emitted internally but had no plugin-facing wiring, so no plugin could ever subscribe: the manifest schema silently dropped `hooks.onStreamComplete`, and the loader/manager only knew the seven legacy hooks. `onStreamComplete` is now a declarable manifest hook, wired through the loader and registered by the manager like the other hooks, and its payload carries a `requestId` so consumers can correlate the stream-completion event with the originating request ([#11934](https://github.com/diegosouzapw/OmniRoute/pull/11934)) — thanks @amaleta / @pacocartones +- **fix(db):** the Qdrant embedding-model dropdown now lists local/self-hosted providers (Ollama, LM Studio, vLLM, etc.) — an active connection is treated as "configured" when the provider allows an optional API key, not only when it has a real key or OAuth, so a running local embedding provider is no longer hidden from the picker ([#11949](https://github.com/diegosouzapw/OmniRoute/issues/11949)) ([#11995](https://github.com/diegosouzapw/OmniRoute/pull/11995)) +- **fix(providers):** Vertex AI Anthropic partner-model discovery now calls the Model Garden `v1beta1` publisher list (`/v1beta1/publishers/anthropic/models`, global) and parses its `publisherModels` envelope, so Claude models auto-synced from Vertex populate the active live catalog and route at request time instead of returning `Model '' is not available in the active live catalog` ([#11991](https://github.com/diegosouzapw/OmniRoute/issues/11991)) — thanks @fabioluissilva ([#11998](https://github.com/diegosouzapw/OmniRoute/pull/11998)) +- **fix(cli):** support `--base-url` alongside `--endpoint` in `omniroute nodes add`, `update`, and `validate` subcommands to prevent global `--base-url` shadowing issues ([#11999](https://github.com/diegosouzapw/OmniRoute/issues/11999)). ([#12033](https://github.com/diegosouzapw/OmniRoute/pull/12033)) — thanks @HouMinXi +- **fix(providers):** `cloudflare-ai` no longer refuses image content parts for every Workers AI model ([#12002](https://github.com/diegosouzapw/OmniRoute/pull/12002)) — the plain-string `content` requirement behind #2539 is carried by the _model_ schema, not by the `/ai/v1/chat/completions` endpoint (measured: an all-text part array returns 200 on `@cf/mistralai/mistral-small-3.1-24b-instruct`, `@cf/meta/llama-4-scout-17b-16e-instruct` and `@cf/meta/llama-3.3-70b-instruct-fp8-fast`, and 400 on the text-only `@cf/qwen/qwen2.5-coder-32b-instruct`). `transformRequest()` flattened every array and threw on the first non-text part (#6390), so image input was refused for vision-capable Cloudflare models that accept it. All-text arrays are still flattened — the one shape every model accepts — while an array carrying a non-text part is passed through untouched, so the attachment is still never silently dropped. Regression guards: `tests/unit/cloudflare-ai-image-parts-6390.test.ts`. — thanks @davidlinfr +- **fix(resilience):** decouple the limiter-managed execution backstop from the queue-wait budget — new `requestQueue.executionMaxWaitMs` (env `RATE_LIMIT_EXECUTION_MAX_WAIT_MS`, default 600000 = 10 min) now feeds Bottleneck's post-dispatch `expiration`, while `requestQueue.maxWaitMs` keeps its documented queue-wait semantics. Previously the queue-wait budget doubled as the execution expiration, so legitimate long-running calls on non-incremental gateways (whole generation buffered before the first upstream byte, e.g. Console Go / Command Code tiers serving GLM models) were killed mid-flight at the queue budget with a false 504 `RATE_LIMIT_EXECUTION_TIMEOUT` — the local limiter undercut the provider-aware upstream fetch-start timeouts. The surfaced 504 message now names `requestQueue.executionMaxWaitMs`; the error keeps the #4165 guarantees (disclaims an upstream timeout, preserves the Bottleneck error as `cause`, branded code + trusted provenance, classified request-scoped so combo falls back). A real queue-wait bound (the `Promise.race` around `limiter.schedule()` sketched in #9533) remains future work. (#12025) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **Call logs:** keep the `error` field when an artifact exceeds the storage cap, instead of replacing it with the omission marker. The error is the only field that says *why* a request failed and is typically ~90 bytes next to the multi-hundred-KB bodies that trip the cap, so dropping it left a size-limited row undiagnosable — a provider outage, a local timeout and an upstream 400 all rendered identically. It is now preserved at every fallback stage, truncated to 4KB if it is itself large ([#12026](https://github.com/diegosouzapw/OmniRoute/issues/12026)). ([#12095](https://github.com/diegosouzapw/OmniRoute/pull/12095)) — thanks @ntdat812 +- **fix(diagnostics):** preserve the error field (truncated to 4KB with a `[truncated: …]` suffix) in every call-log artifact size-limit fallback stage. Previously the minimal fallback replaced the error with `[omitted: call log artifact size limit exceeded]`, so an oversized artifact row showed nothing about WHY the request failed — e.g. 91 of 847 opencode-go 504 rows on one production instance were undiagnosable from the dashboard. Oversized request/response bodies are still omitted exactly as before; the error cap is independent of the payload sizes that tripped the fallback. (#12026) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **fix(sse):** OpenAI Responses clients that declare the native `web_search` tool now receive a spec-shaped `web_search_call` output item with `action.sources` alongside the preserved function-call round-trip, so search results executed through OmniRoute's own search backend are consumable by standard Responses clients (Codex, pi-web-access, …). ([#12031](https://github.com/diegosouzapw/OmniRoute/pull/12031)) — thanks @watchingdogs +- **fix(cli):** use in-thread alias resolver hooks on modern runtimes to avoid deprecation noise and improve Node.js forward compatibility ([#12073](https://github.com/diegosouzapw/OmniRoute/issues/12073)). ([#12083](https://github.com/diegosouzapw/OmniRoute/pull/12083)) — thanks @xiaoyaner0201 +- **fix(combo):** an operator-set **Agent Features → Context length** on a combo is now honored at request time. The value was persisted and advertised through `/v1/models`, but `resolveComboContextLimit()` never consulted it — so a multi-target combo whose members carry no per-model window fell through to the provider's generic `defaultContextLength` (openrouter 128000, command-code 200000) and rejected large requests with `Input exceeds context window … limit 128000` despite the combo being explicitly sized much larger. An identical single-target combo worked, because it collapses to its concrete target before the guard runs. Invalid values (0/negative/NaN/Infinity) are ignored, so the existing target → combo-min → fallback order is unchanged. ([#12090](https://github.com/diegosouzapw/OmniRoute/pull/12090)) — thanks @adivekar-utexas +- **fix(sse):** passthrough streams now estimate usage on finish when upstream closes without usage even with `stream_options.include_usage` — avoids `0 tokens / 0%` for providers that stay silent (and correctly handles trailing empty-choices usage) ([#12151](https://github.com/diegosouzapw/OmniRoute/pull/12151)) — thanks @maxmad64bis +- **fix(combos):** clearing an agent feature in the combos editor now persists — unchecking context cache protection, or emptying the system message or tool filter, sends an explicit `null` instead of dropping the field from the `PUT` body, which the update merge read as "leave unchanged" ([#12177](https://github.com/diegosouzapw/OmniRoute/pull/12177)) — thanks @foreveryh / @ntdatt812 +- **fix(memory):** self-hosted embedding endpoints now vectorize — the vector width is measured from the first embedding that comes back instead of being read from a registry that cannot describe them, so `vec_memories` is created and memories stop piling up unvectorized behind a green health check ([#12180](https://github.com/diegosouzapw/OmniRoute/pull/12180)) — thanks @kanade-hoshino / @ntdatt812 +- **fix(translator):** the leading `system` message now reaches Responses-API upstreams when its `content` is a content-part array — it was read as `typeof content === "string" ? content : ""`, so a prompt-caching client (Anthropic `cache_control`, the shape LiteLLM and the Anthropic SDK emit) had its entire system prompt replaced by an empty `instructions`. The request was still accepted with a normal `prompt_tokens` count, so the model answered with no instructions and nothing in the response said they were missing. Mid-conversation system turns already handled the array shape ([#7056](https://github.com/diegosouzapw/OmniRoute/pull/7056)); only the first one did not ([#12206](https://github.com/diegosouzapw/OmniRoute/issues/12206)). Regression guard: `tests/unit/translator-openai-responses-system-content-parts.test.ts`. ([#12207](https://github.com/diegosouzapw/OmniRoute/pull/12207)) — thanks @Gorillaz322 +- **fix(free-tier):** `/api/free-tier/summary` no longer computes its totals from a Radar feed built before the catalog the running release ships. When the cached feed is older — or carries no build date at all — the route answers from the shipped catalog, resolved through the operator's local model state so disabled and tombstoned models stay out of the numbers ([#12215](https://github.com/diegosouzapw/OmniRoute/pull/12215)). — thanks @maxmad64bis +- **fix(oauth):** Keep a Claude personal workspace and a Team organization as separate connections — they share the same email and `accountUUID`, so the email-only OAuth dedup let the second login overwrite the first account's tokens; `organizationUUID` now disambiguates them, the way `workspaceId` does for Codex ([#12222](https://github.com/diegosouzapw/OmniRoute/pull/12222)) — thanks @davidebaraldo +- **fix(resilience):** a 402 on a single paid model of a passthrough/gateway provider (e.g. `kilo-gateway`, `ollama-cloud`) no longer terminalizes the whole connection with a never-auto-recovered `credits_exhausted` status — only the paid model is locked out, so free models on the same key keep serving. 402 variant of [#3027](https://github.com/diegosouzapw/OmniRoute/issues/3027). Single-credential providers are unaffected — a 402 there is still treated as the key being genuinely out of credit ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239) / [#10616](https://github.com/diegosouzapw/OmniRoute/issues/10616)) ([#12242](https://github.com/diegosouzapw/OmniRoute/issues/12242)) — thanks @brick30llc-ctrl ([#12266](https://github.com/diegosouzapw/OmniRoute/pull/12266)) — thanks @geekyNads +- **fix(sse):** trust `finish_reason: "length"`/`"max_tokens"` over the reasoning-consumed-token ratio in response quality validation, so a reasoning model truncated below the old 90% threshold correctly fails and retries instead of returning empty content as a silent "success" ([#12262](https://github.com/diegosouzapw/OmniRoute/pull/12262)) — thanks @brick30llc-ctrl +- **fix(settings):** `PUT /api/settings/cache-config` now persists `alwaysPreserveClientCache` to the flat general settings the runtime cache-control policy actually reads; previously the value landed in the databaseSettings "cache" section and was silently ignored, so the endpoint had no effect on `cache_control` passthrough ([#12304](https://github.com/diegosouzapw/OmniRoute/pull/12304)) — thanks @davidebaraldo +- **fix(grok-cli):** treat omitted SuperGrokPro `creditUsagePercent` as 0% used so Provider Limits still renders a weekly bar (proto3 zero-elision) ([#12312](https://github.com/diegosouzapw/OmniRoute/pull/12312)) — thanks @HouMinXi +- **fix(combo):** Expose the two Auto-Combo scoring factors nobody could set — the combo validation schema and the dashboard weight sliders both declared 13 of the scorer's 15 factors, so `connectionDensity` (spreads load across a provider's connections) and `quality` were dropped on save and offered nowhere. The sliders also shipped their own default table that differed from the engine's on every non-zero factor and summed to 1.05, so the percentages shown next to them added up to 105%. Both lists now match `DEFAULT_WEIGHTS`, and a test keeps them there. Note that a combo whose stored `weights` omitted the two keys was effectively running with them at zero and the other thirteen renormalized upward; it now runs with the engine's intended distribution, so its routing does shift ([#12314](https://github.com/diegosouzapw/OmniRoute/pull/12314)) — thanks @maxmad64bis +- **fix(docs):** The free-tier reference no longer says its numbers come "confidence tagged per row" — no catalog entry carries a confidence tag and the API serves none, so every figure on that page is an estimate of the same, unstated quality. The page now states what an entry does vouch for: an independently documented hard stop (set by hand with the source in a comment, never defaulted to `true`) and a prompt-training disclosure, both with live counts the `check:docs-counts` gate keeps honest ([#12318](https://github.com/diegosouzapw/OmniRoute/pull/12318)) — thanks @maxmad64bis +- **fix(usage):** `adobe-firefly` and `firefly` have had a working usage fetcher since Adobe Firefly landed, but neither was ever added to the registration list, so the provider-plugin manifest, `genericQuotaFetcher` and the free-access quota cache all reported them as having no usage support — while `USAGE_SUPPORTED_PROVIDERS` said the opposite. Both are now declared, which also means their credit balance is fetched like any other declared provider's: `registerGenericQuotaFetchers` now registers a generic quota fetcher for them, and `resolveFreeAccessState` no longer returns early. A test holds the registration list to the dispatcher's switch in both directions, which is what the module's own docstring already asked for in prose ([#12321](https://github.com/diegosouzapw/OmniRoute/pull/12321)) — thanks @maxmad64bis +- **fix(quota):** drop the generic quota cache (agy / Antigravity / Claude OAuth) on an upstream 429 so reset-aware scoring does not keep a 60s stale snapshot, and force-refresh the next usage fetch so inner provider caches cannot recache the same window ([#12325](https://github.com/diegosouzapw/OmniRoute/pull/12325)) — thanks @HouMinXi +- **fix(combos):** deleting a combo now clears its persisted LKGP pins instead of leaving unreachable `key_value` rows behind ([#12326](https://github.com/diegosouzapw/OmniRoute/issues/12326)) ([#12425](https://github.com/diegosouzapw/OmniRoute/pull/12425)) — thanks @KrzysiekSko +- **fix(sse):** Keep ZWNJ (U+200C) and ZWJ (U+200D) in assistant text, reasoning and tool-call arguments — Persian/Kurdish half-space (`ارائه‌دهنده`), Arabic/Indic shaping and emoji sequences no longer lose them; the response de-obfuscation now removes joiners only between ASCII word characters, where the request side inserts them ([#12186](https://github.com/diegosouzapw/OmniRoute/issues/12186)) — thanks @rezjalibd ([#12359](https://github.com/diegosouzapw/OmniRoute/pull/12359)) — thanks @pacocartones +- **fix(resilience):** count resolved upstream 5xx results against the provider circuit breaker on the chat path — `CircuitBreaker.execute()` no longer reads a resolved `{ success: false, status: 5xx }` as a success that cancels the call-site failure, so a provider answering 503s now trips its breaker instead of staying `CLOSED` at `failureCount: 1`; single-model and combo dispatches are each accounted exactly once ([#12254](https://github.com/diegosouzapw/OmniRoute/issues/12254)) ([#12360](https://github.com/diegosouzapw/OmniRoute/pull/12360)) — thanks @pacocartones +- **fix(providers):** resolve the Codex quota auto-ping model from the live provider catalog and lifecycle registry instead of the retired `gpt-5.1-codex-mini`, and pause the ping with one actionable warning when no selectable Codex model exists rather than retrying a shut-down id every cooldown window ([#11905](https://github.com/diegosouzapw/OmniRoute/issues/11905)) ([#12361](https://github.com/diegosouzapw/OmniRoute/pull/12361)) — thanks @pacocartones +- **fix(api):** keep the `{created, data}` wrapper on combo-routed `/v1/images/generations` responses and default Codex image results to `b64_json` on both `/v1/images/generations` and `/v1/images/edits` so Codex CLI's built-in `image_gen` can decode them ([#12268](https://github.com/diegosouzapw/OmniRoute/issues/12268)) ([#12362](https://github.com/diegosouzapw/OmniRoute/pull/12362)) — thanks @pacocartones +- **fix(sse):** Name the shadowed custom provider node when a built-in provider id/alias (e.g. `openference` → `of`) reserves the prefix of an existing OpenAI/Anthropic-compatible node, so the runtime `No active credentials for provider: ` error explains that the prefix routed to the built-in and never reached the node's healthy connections, instead of contradicting the dashboard ([#11943](https://github.com/diegosouzapw/OmniRoute/issues/11943)) — thanks @morpheus9393 ([#12365](https://github.com/diegosouzapw/OmniRoute/pull/12365)) — thanks @pacocartones +- **fix(i18n):** wrap `ccOnboardingKeyPlaceholder` in ICU single quotes across all 43 locales so angle brackets render literally instead of being parsed as rich-text tags, which crashed the Claude Code onboarding block with `INVALID_MESSAGE: INVALID_TAG` ([#12302](https://github.com/diegosouzapw/OmniRoute/issues/12302)) ([#12369](https://github.com/diegosouzapw/OmniRoute/pull/12369)) — thanks @gonisulaimann +- **fix(models):** publish `effort_tiers` on Kimi K3's synced base-model entries (`kmca/k3`, `kmca/k3-256k`) so catalog-only clients (OpenCode, plain SDK pickers) can see and select the reasoning tiers (`low`/`high`/`max`) the synced metadata already carried — the `isSkippedEffortProvider` gate no longer suppresses tier visibility on those base entries, while synthetic `-` variant generation stays prevented and Codex/GLM base models remain excluded unchanged ([#12299](https://github.com/diegosouzapw/OmniRoute/issues/12299)) ([#12371](https://github.com/diegosouzapw/OmniRoute/pull/12371)) — thanks @gonisulaimann +- **fix(guardrails):** keep `auto`/`auto/*` virtual combos exempt from the Vision Bridge `fixedModel` credential guard so a combo target is passed through instead of silently falling back to global auto-selection ([#12237](https://github.com/diegosouzapw/OmniRoute/issues/12237)) ([#12373](https://github.com/diegosouzapw/OmniRoute/pull/12373)) — thanks @pacocartones +- **fix(combo):** capability-filter exhaustion caused by `max_tokens` above every target's known output limit now reports that reason (requested `max_tokens` vs the pool's highest known ceiling) instead of the unrelated "supports structured output" message ([#12229](https://github.com/diegosouzapw/OmniRoute/issues/12229)) — thanks @DW-MediaLab ([#12374](https://github.com/diegosouzapw/OmniRoute/pull/12374)) — thanks @pacocartones +- **fix(auth):** the `least-used` account strategy now prefers accounts without backoff before falling back to oldest `lastUsedAt`, the same tie-break `round-robin` already applies, so a failover no longer lands on a just-rate-limited account for a single request ([#12279](https://github.com/diegosouzapw/OmniRoute/issues/12279)) — thanks @tenshiak ([#12375](https://github.com/diegosouzapw/OmniRoute/pull/12375)) — thanks @pacocartones +- **fix(docker):** the `chatgpt-web-codex-browser` image now finds the Chrome binary under `chrome-linux64/` (Chrome for Testing layout in `playwright:v1.62.0-noble`) as well as the legacy `chrome-linux/`, so the container no longer crash-loops with `exec: --headless=new: not found` ([#12024](https://github.com/diegosouzapw/OmniRoute/issues/12024)) ([#12376](https://github.com/diegosouzapw/OmniRoute/pull/12376)) — thanks @pacocartones +- **fix(providers):** declare `groq/compound` and `allam-2-7b` as non-reasoning models in the curated Groq registry so `reasoning_effort` / `output_config.effort` / `thinking` from Claude Code are stripped instead of forwarded, which Groq rejected with HTTP 400 ([#12134](https://github.com/diegosouzapw/OmniRoute/issues/12134)) ([#12379](https://github.com/diegosouzapw/OmniRoute/pull/12379)) — thanks @pacocartones +- **fix(executors):** `OpencodeExecutor` no longer forces a direct connection when the connection has a proxy assigned in Proxy Management but no per-account proxies: the single-account fast path used to wrap the upstream dispatch in the direct-egress sentinel, discarding the ambient proxy context the chat handler had pinned from `proxy_assignments`, so API-key `opencode`/`opencode-go` connections egressed from the host IP (and hit geoblocks) despite the assignment. The direct pin is now applied only when no ambient proxy context exists ([#11894](https://github.com/diegosouzapw/OmniRoute/issues/11894) — thanks @hizzt) ([#12380](https://github.com/diegosouzapw/OmniRoute/pull/12380)) — thanks @pacocartones +- **fix(api):** `GET /v1/models` with `MODELS_CATALOG_PREFIX_MODE=canonical` (or `?prefix=canonical`) now lists providers whose registry alias is undefined or equal to their own id (Antigravity, Antigravity CLI and other self-aliased built-ins) — their single `provider/model` id was dropped by the alias/canonical duplicate guard in the static, synced, custom and alias-backed catalog loops ([#12058](https://github.com/diegosouzapw/OmniRoute/issues/12058)) — thanks @cheynetom ([#12381](https://github.com/diegosouzapw/OmniRoute/pull/12381)) — thanks @pacocartones +- **fix(translator):** Drop replayed `thinking` blocks that carry no signature (the shape produced from cross-provider `reasoning_content`) instead of stamping the default Claude signature on them, which Anthropic rejected with `400 Invalid signature in thinking block` on the next turn served by an Anthropic rung ([#12105](https://github.com/diegosouzapw/OmniRoute/issues/12105)) — thanks @atescivitci-cmd ([#12386](https://github.com/diegosouzapw/OmniRoute/pull/12386)) — thanks @pacocartones +- **fix(cli):** Resolve Bun's `--preload` polyfill path against the package root instead of `dist/`, so `omniroute` installed with `bun install -g` no longer crashes at startup with `error: preload not found …/dist/open-sse/utils/setupPolyfill.ts` ([#11980](https://github.com/diegosouzapw/OmniRoute/issues/11980)) — thanks @joglomedia ([#12387](https://github.com/diegosouzapw/OmniRoute/pull/12387)) — thanks @pacocartones +- **fix(providers):** `gemini-business` now publishes its model catalog — `/v1/models` and `/v1/providers/gemini-business/models` list the 12 enterprise Gemini ids the executor understands instead of returning an empty list (#12107) ([#12389](https://github.com/diegosouzapw/OmniRoute/pull/12389)) — thanks @pacocartones +- **fix(db):** install `busy_timeout` before the SQLite connection's first statement so a process opening the database while another one closes its WAL connection waits out the transient EXCLUSIVE lock instead of dying with `database is locked`, and recognise the drivers' real BUSY/PROTOCOL/IOERR errors as transient in the corruption probe so the same lock no longer renames the database away as corrupt; deflakes `cross-process contenders never both acquire the same connection` (#12394 — thanks @pacocartones) +- **fix(chat-admission):** derive the `chat_admission_busy` 503 `Retry-After` from observed heavyweight-lease occupancy — the larger of the exhausted `OMNIROUTE_CHAT_ADMISSION_QUEUE_MS` window and the time since capacity last turned over, capped at 60 s — instead of a fixed 1 s (structural) / 2 s (byte-stage) hint that invited Codex/agent fan-out clients to re-send ~1 MiB `/v1/responses` bodies every second into a gate held for the whole SSE lifetime; an idle gate keeps the historical floors ([#12135](https://github.com/diegosouzapw/OmniRoute/issues/12135)) (#12395 — thanks @pacocartones) +- **fix(api-manager):** the API key permissions modal no longer silently drops `allowedCombos` entries its Combo picker cannot render — routing-rule names such as `rt-*`, which the backend already honours — when "All" is clicked and the key is switched back to "Restrict"; those entries now survive the toggle, are listed read-only under the combo list so the count and the list agree, and are saved back verbatim instead of persisting `[]` (deny-all) (#12397 — thanks @pacocartones) +- **fix(catalog):** write the NUL separator of the catalog connection memo key, the provider serviceKind memo key, the Video Bridge promotion group key and a JSON-exactness test fixture as the `\u0000` escape instead of a raw byte — same runtime value, but the raw byte made git, GitHub and ripgrep treat those files as binary (hidden PR diffs, silently skipped searches); a guard test now keeps raw NUL bytes out of `src/`, `open-sse/` and `tests/` (#12403 — thanks @pacocartones) +- **fix(providers):** add `CLAUDE_CODE_CLIENT_VERSION` and `GITHUB_COPILOT_CLI_VERSION` env overrides so Anthropic/Copilot client-version gates can be unblocked without a rebuild ([#12417](https://github.com/diegosouzapw/OmniRoute/issues/12417)) ([#12632](https://github.com/diegosouzapw/OmniRoute/pull/12632)) — thanks @HouMinXi +- **fix(db):** back-fill `last_ping_at` and `last_pinged_reset_key` on `provider_connections` during schema reconciliation so divergent lineages that skipped `123_quota_auto_ping` still accept quota auto-ping writes ([#12470](https://github.com/diegosouzapw/OmniRoute/pull/12470) — thanks @KooshaPari) +- **fix(ci):** document MIT exceptions for `@eloqnt/{config,format-json,format-po}` (next-intl transitive; locked tarballs omit `license`) and keep the A2A lifecycle vitest off the real SQLite persistence seam ([#12581](https://github.com/diegosouzapw/OmniRoute/issues/12581)) ([#12595](https://github.com/diegosouzapw/OmniRoute/pull/12595)) +- OpenCode plugin `/v1/models` catalog fetch now waits 30s by default and attaches HTTP `statusCode` on 401/5xx so host fallback plugins can hop instead of seeing an untyped AbortError/UnknownError. ([#12607](https://github.com/diegosouzapw/OmniRoute/pull/12607)) — thanks @RaviTharuma +- **CI:** the OpenAPI security-tier gate now mirrors `isAlwaysProtectedPath()` in full — it also reads `ALWAYS_PROTECTED_API_PATTERNS`, so the pattern-gated credential routes (`/api/providers/{id}/{claude,codex}-auth/{export,apply-local}`, GHSA-5926-2w35-7h4q) no longer report as unannotated. (#12605) +- **fix(api):** GET /v1/models no longer waits forever on a hung coalesced catalog rebuild; cold-path waits are bounded (`CATALOG_BUILD_TIMEOUT_MS`, default 8s) and a last-good 200 is served when the rebuild times out ([#12627](https://github.com/diegosouzapw/OmniRoute/issues/12627)). ([#12628](https://github.com/diegosouzapw/OmniRoute/pull/12628)) — thanks @RaviTharuma +- **fix(cursor):** a non-array `tool_calls` on an assistant message no longer crashes the cursor request translator with a `TypeError`; both loops now require an array ([#12689](https://github.com/diegosouzapw/OmniRoute/issues/12689)) ([#12691](https://github.com/diegosouzapw/OmniRoute/pull/12691)) — thanks @soroush5 +- **fix(chat):** Chat Completions no longer return empty `content` after a server-owned memory or skills tool; the first provider send and account/model recovery now share one pipeline so a follow-up round-trip can fill the reply ([#12696](https://github.com/diegosouzapw/OmniRoute/issues/12696)) — thanks @HouMinXi ([#12867](https://github.com/diegosouzapw/OmniRoute/pull/12867)) +- **fix(dashboard):** batch-deleting provider keys no longer toasts failure after a successful delete when the confirm button's click event is forwarded as `onAfter` ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) — thanks @HouMinXi +- **fix(glm):** drop the extra 16th argument to `createSSETransformStreamWithLogger` that TypeScript rejected (TS2554) and that never reached the TransformStream ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) ([#12770](https://github.com/diegosouzapw/OmniRoute/pull/12770)) — thanks @HouMinXi +- **fix(dashboard):** pass `nodeMap` into Runtime `QuotaGroup` so a quota monitor in error/exhausted/alerting no longer throws `ReferenceError: nodeMap is not defined`. ([#12868](https://github.com/diegosouzapw/OmniRoute/pull/12868)) — thanks @HouMinXi +- Restricted API keys whose `allowedModels` lists a combo name no longer skip every combo member at pre-dispatch (`ALL_TARGETS_SKIPPED`). Inner-target filtering still applies when the allow-list is a provider prefix or `disableNonPublicModels` is on ([#12899](https://github.com/diegosouzapw/OmniRoute/pull/12899)). — thanks @HouMinXi +- **fix(combos):** Effort-suffixed combo members inherit the base model's `model_context_overrides` row so priority order is not inverted on large requests ([#12926](https://github.com/diegosouzapw/OmniRoute/pull/12926)) — thanks @HouMinXi +- **fix(catalog):** Picker-added `customModels` enter the dispatch-time live catalog so combo members and bare inference no longer 400 ([#12934](https://github.com/diegosouzapw/OmniRoute/pull/12934)) — thanks @HouMinXi +- **fix(docker):** re-derive the Docker build's worker-pool memory budget from the MEASURED ~4.5 GB per-process RSS (the issue owner's own VPS dmesg OOM-killer reproduction) instead of the stale 2560 MB/worker inference #11419 shipped, and lower `OMNIROUTE_BUILD_WORKERS` 3 → 2 so 1 parent + 1 page-data worker (2 processes × 4.5 GB = 9 GB) fits the 12.288 GB (75%) budget on a 16 GB GitHub Actions runner — the previous default (1 parent + 2 workers = 13.5 GB) still overcommitted the runner and kept "Publish to Docker Hub" failing with `cannot allocate memory` after #11419 merged (#7518). ([#11663](https://github.com/diegosouzapw/OmniRoute/pull/11663)) +- **fix(electron):** the packaged Windows desktop build now passes the #7592 cold-restart smoke end to end. Five defects were found and fixed on the way: (1) optional-pack staging failed on any machine whose `tar` is GNU tar — it reads the drive letter in an absolute `-f C:\...` archive path as a remote rsh host (`Cannot connect to C:`), so staging now passes a bare filename with `cwd` at the tarball directory and surfaces tar stderr on failure; (2) the Electron `files` allowlist was missing `lib/loginHeaderCapture.js`, so the packaged main process crashed at startup with `Cannot find module './lib/loginHeaderCapture'`; (3) electron-builder ≥26 injects an `!**/node_modules/**` ignore into every extraResources pattern list that no positive filter can override, which silently dropped the staged runtime `node_modules` (including the better-sqlite3 N-API prebuild) from `resources/app` — a fresh v3.8.50 desktop build would have shipped with no native SQLite driver and reproduced the #7592 sql.js fallback on every machine; an `afterPack` hook now restores the staged `node_modules` after packing; (4) the packaged-app smoke harness redirected `USERPROFILE` into its temp DATA_DIR, but Electron resolves the Roaming profile from `%USERPROFILE%\AppData\Roaming\` (USERPROFILE takes precedence over the APPDATA env var) and the path service throws instead of creating it, so `requestSingleInstanceLock()` returned false and the app exited(0) silently before `app.whenReady()` — the harness now pre-creates the derived tree, and `ensureSmokeEnvDirs` is exported and unit-tested; (5) the #7592 driver assertion parsed a `[DB] Driver: ...` line that the server's primary DB path never printed (only the unused `openDatabaseAsync()` did) — `getDbInstance()` now emits the same line on open so the guard can actually assert the native driver. Also: the smoke env-allowlist unit test hardcoded POSIX paths and could only pass on Linux/macOS; expectations are now host-agnostic, with new regression tests for the USERPROFILE-derived tree and `tarPack` under absolute Windows-style paths ([#11443](https://github.com/diegosouzapw/OmniRoute/pull/11443)) — thanks @jonlwheat2-gif +- fix(sse): stop the auto-combo candidates inspector from silently dropping model-locked/cooled-down rows (#9133) ([#11994](https://github.com/diegosouzapw/OmniRoute/pull/11994)) — thanks @hartmark +- **fix(providers):** Perplexity Web no longer turns upstream stream failures into successful assistant text; pre-content failures remain eligible for fallback, partial output ends with a structured sanitized error, and failed sessions are not persisted ([#12465](https://github.com/diegosouzapw/OmniRoute/pull/12465)) +- **fix(video-bridge):** preserve chronological video and transcript fusion, source-frame timestamps, transcript provenance, full contact-sheet intervals, and pre-abort callback isolation ([#11681](https://github.com/diegosouzapw/OmniRoute/pull/11681)) +- **Z.ai Web:** HTTP 200 streams carrying an upstream error now terminate with a structured failure instead of assistant text plus a normal stop, preserving partial output while allowing pre-content combo fallback. ([#12454](https://github.com/diegosouzapw/OmniRoute/pull/12454)) +- Absorb `Error [AbortError]: request_signal_aborted` and DOMException AbortError shapes in the process-level client-abort crash guard so routine client disconnects no longer kill the server (exit code 7). ([#12165](https://github.com/diegosouzapw/OmniRoute/pull/12165)) — thanks @alvinveroy +- **fix(sse):** Treat Adapta Web `type:error` SSE events as sanitized non-stream failures instead of empty HTTP 200 completions. ([#12459](https://github.com/diegosouzapw/OmniRoute/pull/12459)) +- **fix(autoCombo):** layer 3 (`models_dev_tier`) no longer scores retired models as premium, merge cross-provider capability rows deterministically, and inherit tier scores through variant ids ([#11508](https://github.com/diegosouzapw/OmniRoute/issues/11508)). `getModelsDevTierFitness` now vetoes ids present in `config/quality/model-lifecycle.json` with status `retired` before any other signal; `loadModelCapabilities()` aggregates per `model_id` (any-non-null-true for booleans, max for `limit_context`) instead of last-write-wins over SQLite's undefined row order; a capabilities miss retries through `resolveScoresAs()`'s base id and reports `models_dev_tier:inherited`, matching layers 1–2 (#11492). Adds `getModelsDevTierFitnessWithSource()` and an `invalidateCapabilitiesCache()` test/ops hook. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) — thanks @Meet6338-X +- fix(sse): accept Claude `content:[]` + `stop_reason: max_tokens`/`tool_use` in `detectMalformedNonStream` (match `isEmptyContentResponse`) so Claude Code `/model` probes with `max_tokens: 1` no longer become a false 502 ([#11568](https://github.com/diegosouzapw/OmniRoute/pull/11568)) — thanks @Seramicx +- **fix(security):** sanitize `request.failed` diagnostics before publishing them to live dashboard listeners and replay history, while keeping status, model, provider, latency, and internal call-log diagnostics intact. ([#12469](https://github.com/diegosouzapw/OmniRoute/pull/12469)) +- **fix(pwa):** do not intercept dashboard navigations so Chrome can fall back from a stale HTTP/3 Alt-Svc advertisement (UDP :20128 is unpublished; F5 on a long-lived tab hung until a new tab opened a fresh TCP connection). ([#12767](https://github.com/diegosouzapw/OmniRoute/pull/12767)) — thanks @HouMinXi +- **fix(executors):** handle DuckDuckGo ERR_BN_LIMIT (418) without retrying — when the upstream returns `418 ERR_BN_LIMIT` (rate-limit/ban), the executor now returns the error immediately instead of burning another VQD acquisition that would only count against the IP limit. The retry logic for `418 ERR_CHALLENGE` (unsolved challenge) remains unchanged. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) ([#12110](https://github.com/diegosouzapw/OmniRoute/pull/12110)) — thanks @oyi77 / @Meet6338-X +- Fixed DuckDuckGo streaming responses losing JSON lines and UTF-8 characters split across network chunks. ([#11528](https://github.com/diegosouzapw/OmniRoute/pull/11528)) — thanks @pacocartones +- Harden generated Cloudflare, Vercel, and Deno relays by stripping hop-by-hop, framing, and proxy authentication headers before forwarding requests upstream. ([#11533](https://github.com/diegosouzapw/OmniRoute/pull/11533)) — thanks @pacocartones +- **feat(grok-cli):** Provider Limits shows grok-cli banked reset credits from `GetRemainingResets` (including a real zero; a failed RPC omits the row) and the existing View credits button now calls `ConsumerUiSvc/RedeemReset` for grok-cli. Live tokens use nested fields 10/20/30 (id + Timestamp), not compact 1/2/3. ([#12805](https://github.com/diegosouzapw/OmniRoute/pull/12805)) — thanks @HouMinXi +- **fix(memory):** Embedding Model Quick select, Embedding Source remote dropdown, and Rerank selector now list every configured provider with embedding/rerank support instead of only chat-catalog text matches plus OpenRouter live discovery; a generic OpenAI-compatible `/embeddings` + Cohere-compatible `/rerank` runtime fallback resolves any configured chat provider's embedding/rerank endpoint, so unlisted providers no longer fail with "Unknown embedding provider"; both memory selectors gained a free-text model override ([#11390](https://github.com/diegosouzapw/OmniRoute/pull/11390)) — thanks @rqzbeh +- Harden SQLite upgrades around the historical migration-074 version collision: missing discovery and inspector tables are replayed atomically, pre-existing databases (including setup-created skeletons) receive reusable content-addressed safety snapshots, and Node test/eval probes without `DATA_DIR` are isolated from the operator database. ([#12435](https://github.com/diegosouzapw/OmniRoute/pull/12435)) +- **fix(dashboard):** Moonshot/Kimi Open Platform voucher and cash leftover percentages follow the bucket balance, so an empty wallet no longer paints those rows as 100% while Available is 0% ([#12733](https://github.com/diegosouzapw/OmniRoute/pull/12733)) — thanks @HouMinXi +- **fix(combo):** return non-retryable HTTP 400 when all candidates for a pinned native Codex turn are unavailable due to model-scoped lockout, terminating the turn cleanly while preserving turn continuity and enabling standard Combo routing on subsequent turns ([#12240](https://github.com/diegosouzapw/OmniRoute/pull/12240)) — thanks @mdigitalbh81 +- Fixed auto-router strategies preserving the selected connection through ranking and dispatch when multiple connections share the same provider and model. ([#11530](https://github.com/diegosouzapw/OmniRoute/pull/11530)) — thanks @pacocartones +- Preserve Chat Completions JSON-object response formats when translating requests to the Responses API. ([#11529](https://github.com/diegosouzapw/OmniRoute/pull/11529)) — thanks @pacocartones +- Fix idempotency fingerprints so Responses API requests include `input` and other semantic generation fields using stable serialization, while excluding credentials and request metadata. ([#11532](https://github.com/diegosouzapw/OmniRoute/pull/11532)) — thanks @pacocartones +- **fix(api):** Generated API CLI commands now enforce required OpenAPI request bodies; Combo test commands forward the required `comboName` body, while API keys created by older writers after migration 149 preserve legacy allow-all Combo access without widening explicit empty allowlists — thanks @marcelokarval ([#12070](https://github.com/diegosouzapw/OmniRoute/pull/12070)) +- **fix(grok-web):** treat upstream streaming failures as failures instead of successful assistant text: error-only streams now fail readiness with HTTP 502, while failures after legitimate content preserve that partial output and terminate through the sanitized stream failure path without a normal `stop` completion. ([#12458](https://github.com/diegosouzapw/OmniRoute/pull/12458)) +- HuggingChat now turns HTTP 200 JSONL generation failures into a sanitized 502 before content, or a fixed public stream failure after partial output, so fallback and request persistence no longer record a false successful stop. ([#12456](https://github.com/diegosouzapw/OmniRoute/pull/12456)) +- **fix(providers):** keep 1min.ai HTTP 200 stream errors out of assistant content, preserve partial output, and expose sanitized terminal errors so pre-content failures can fall back. ([#12466](https://github.com/diegosouzapw/OmniRoute/pull/12466)) +- **fix(sessions):** preserve legacy Sessions data when the additive exclusive-session projection is unavailable, returning an empty projection and warning only once per contiguous outage instead of failing the endpoint; the Sessions badge intentionally reflects the merged legacy and exclusive row count introduced by [#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389) ([#11469](https://github.com/diegosouzapw/OmniRoute/pull/11469)) — thanks @KaspaPulse +- Render Nimble Search with the generic provider icon and serve Opper's proven logo locally. ([#12415](https://github.com/diegosouzapw/OmniRoute/pull/12415)) +- **fix(quality):** `config/quality/provider-assets-provenance.jsonl` was stale against the physical `public/providers/` tree (still listing 225 assets and 5 duplicate-content groups after several provider-retirement PRs removed 85 icon files), which would have made `check:provider-asset-provenance` fail on the next full run. Reconciled the manifest to the real 142-file snapshot (142 asset records, 1 duplicate-content group, 2 newly-registered files marked `unresolved` pending provenance research), repinned `auditedCommit`, and updated the regression test's hardcoded counts to match. ([#11876](https://github.com/diegosouzapw/OmniRoute/pull/11876)) +- fix(ui): wrap quota card footer action buttons to prevent clipping Refresh button on narrow cards — when extra action buttons such as `View credits` are present on Codex cards, allow the footer row to flex-wrap with `shrink-0` buttons so `Refresh now` remains visible and clickable across all responsive card widths ([#11464](https://github.com/diegosouzapw/OmniRoute/pull/11464)) — thanks @adevwithpurpose +- Keep Antigravity Gemini usable when the same connection's Claude weekly quota is empty; generic quota cache stays per-connection for every other provider. ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) — thanks @HouMinXi +- Fixed Tailscale tunnel setup to reject ports outside the valid 1-65535 range and safely install Windows MSI packages from paths containing apostrophes. ([#11534](https://github.com/diegosouzapw/OmniRoute/pull/11534)) — thanks @pacocartones +- Classify Cloudflared and Tailscale process-management routes as local-only and spawn-capable while preserving remote access to read-only tunnel status endpoints. ([#11531](https://github.com/diegosouzapw/OmniRoute/pull/11531)) — thanks @pacocartones +- Electron release workflow: the `publish-npm` job now grants `actions: read` to the reusable `npm-publish.yml` it calls (its `publish` job requests it), which is what made GitHub refuse the whole v3.8.50 run at startup and ship the release with zero desktop assets; a `workflow_dispatch` now builds the requested tag instead of the dispatching branch and can skip the npm leg (`publish_npm=false`) when only re-attaching assets ([#11974](https://github.com/diegosouzapw/OmniRoute/pull/11974)) +- **fix(dashboard):** The Combos page usage guide now reads its dismissal through `useSyncExternalStore` instead of correcting SSR state inside an effect, removing an extra commit of the page tree on every load (and the `react-hooks/set-state-in-effect` error it raised). ([#12671](https://github.com/diegosouzapw/OmniRoute/pull/12671)) +- Electron release: `electron/package-lock.json` regained the optional `electron-builder-squirrel-windows` subtree (13 entries) that `npm ci` had been refusing as out of sync, `electron-release.yml` gained a `build_ref` dispatch input and stops regenerating release notes on a re-attach dispatch, and the npm publish workflow attaches the SBOM to the GitHub Release on dispatch publishes too — so the v3.8.51 tag ships every desktop asset and the SBOM like v3.8.49 did ([#12022](https://github.com/diegosouzapw/OmniRoute/pull/12022)) +- Drained the reds every PR against `release/v3.8.51` was born with: documented the three service-worker build-id variables, registered the six retirement/tunnel tests with the mutation gate, approved `eslint-plugin-react-hooks` in the dependency allowlist, added the six `combo.sort.*` strings to `vi` and `pt-BR`, pointed the ChatGPT Web doc at the real migration-168 test, worded the g4f hint around the member key, and realigned four tests to the retired-provider catalog and the legacy-schema fixtures the retirement migrations touch. ([#11940](https://github.com/diegosouzapw/OmniRoute/pull/11940)) +- **fix(providers):** Claude, Grok, LMArena, Notion, and Perplexity web-cookie transports now use pooled `wreq-js` 3.2 instead of the native sidecar, with all nine supported bindings pinned and audited, and the applicable platform binding plus native-license evidence included in each release artifact ([#12429](https://github.com/diegosouzapw/OmniRoute/pull/12429), supersedes [#11753](https://github.com/diegosouzapw/OmniRoute/pull/11753)). +- **fix(providers):** Zed Hosted streaming failures now trigger fallback before content and end partial streams with a sanitized structured error instead of fake assistant text and a normal-success stop. ([#12455](https://github.com/diegosouzapw/OmniRoute/pull/12455)) +- **fix(test):** capture hook result after render (direct commit `b85d0abb0b`) +- **fix(sync):** repair the two regressions the v3.8.50 sync-back left on release/v3.8.51 (direct commit `5ade9e0851`) +- **revert(models):** drop the memo reset hook ported from #10055 (direct commit `282de841ae`) +- **fix(typecheck):** guard the optional connectionId before the reactive model sync (direct commit `f2825bad75`) +- **fix(quota-share):** release the winner's reserved in-flight slot (#11371) ([#11408](https://github.com/diegosouzapw/OmniRoute/pull/11408)) — thanks @oyi77 +- **fix(providers):** stop silently dropping reasoning effort on opencode families (#10788) ([#11409](https://github.com/diegosouzapw/OmniRoute/pull/11409)) — thanks @oyi77 +- **fix(combo):** enable genuine quota-aware routing for generic providers (antigravity, claude, etc.) ([#11411](https://github.com/diegosouzapw/OmniRoute/pull/11411)) — thanks @HouMinXi +- **fix(oauth):** allow expired token retry before deactivation and activate expired retry loop (#P0) ([#11414](https://github.com/diegosouzapw/OmniRoute/pull/11414)) — thanks @HouMinXi +- **perf(executors):** lazy-load the executor registry — defer class imports + construction to first use (#11220) ([#11421](https://github.com/diegosouzapw/OmniRoute/pull/11421)) — thanks @oyi77 +- **fix:** make release/v3.8.50 CI gates green (run 32786966560 root causes) ([#11450](https://github.com/diegosouzapw/OmniRoute/pull/11450)) — thanks @jonlwheat2-gif +- **fix(sse):** exact-domain cookie match and origin-equality URL assertions ([#11452](https://github.com/diegosouzapw/OmniRoute/pull/11452)) — thanks @hartmark +- **fix(providers):** send Responses API payload during OpenAI-compatible validation ([#11454](https://github.com/diegosouzapw/OmniRoute/pull/11454)) — thanks @tuandinh0801 +- **fix(command-code):** fallback to /alpha/generate for Go plan without Provider API access ([#11455](https://github.com/diegosouzapw/OmniRoute/pull/11455)) — thanks @Chewji9875 +- **fix(responses-continuation):** fail closed on a log-truncated stored input/output array ([#11473](https://github.com/diegosouzapw/OmniRoute/pull/11473)) — thanks @hartmark +- **fix(cli):** write jcode settings as [providers.omniroute] in config.toml ([#11484](https://github.com/diegosouzapw/OmniRoute/pull/11484)) — thanks @hongnoul +- **fix(logging):** raise chat-log array truncation cap to 1000 and unify duplicate implementations ([#11499](https://github.com/diegosouzapw/OmniRoute/pull/11499)) — thanks @hartmark +- **fix:** restore live websocket URL helpers ([#11509](https://github.com/diegosouzapw/OmniRoute/pull/11509)) — thanks @PauloFH +- **perf(core):** eliminate async waterfalls across home, cache, and db operations (#11396) ([#11512](https://github.com/diegosouzapw/OmniRoute/pull/11512)) — thanks @HouMinXi +- **fix(kiro):** add runtime.us-east-1.kiro.dev as first-attempt endpoint ([#11517](https://github.com/diegosouzapw/OmniRoute/pull/11517)) — thanks @rifqiawl +- **fix(providers):** remove duplicate 128-tool truncation in OpencodeExecutor (#11444) ([#11518](https://github.com/diegosouzapw/OmniRoute/pull/11518)) — thanks @HouMinXi +- **fix(providers):** poll onboardUser LRO before classifying BYOP ([#11519](https://github.com/diegosouzapw/OmniRoute/pull/11519)) — thanks @rifqiawl +- **fix(translator):** preserve exact tool name casing for Claude tool_use payloads (#11487) ([#11520](https://github.com/diegosouzapw/OmniRoute/pull/11520)) — thanks @HouMinXi +- **fix(kimi-web):** align health probe with executor domain ([#11521](https://github.com/diegosouzapw/OmniRoute/pull/11521)) — thanks @pacocartones +- **fix(cli):** preserve existing admin password during setup ([#11522](https://github.com/diegosouzapw/OmniRoute/pull/11522)) — thanks @pacocartones +- **fix(mitm):** keep Windows DNS elevation off the POSIX sudo path (#11430) ([#11542](https://github.com/diegosouzapw/OmniRoute/pull/11542)) — thanks @official-burak +- **fix(sse):** scale chat admission by ingest byte budget instead of a fixed request count ([#11548](https://github.com/diegosouzapw/OmniRoute/pull/11548)) — thanks @TheDemonTuan +- **fix:** accept Gemini Web cookie exports ([#11554](https://github.com/diegosouzapw/OmniRoute/pull/11554)) — thanks @rafacpti23 +- **fix(i18n):** complete Brazilian Portuguese translations ([#11558](https://github.com/diegosouzapw/OmniRoute/pull/11558)) — thanks @rafacpti23 +- **fix(sse):** explicit types for openai-responses pureHelpers — clears last failing core typecheck gate ([#11567](https://github.com/diegosouzapw/OmniRoute/pull/11567)) — thanks @jonlwheat2-gif +- **fix(opencode):** hydrate Proxy Pool references for no-auth connections ([#11584](https://github.com/diegosouzapw/OmniRoute/pull/11584)) — thanks @AStupidBear +- **fix(ci):** resolve inherited release/v3.8.51 base-red CI failures ([#11608](https://github.com/diegosouzapw/OmniRoute/pull/11608)) — thanks @jonlwheat2-gif +- **fix(db):** fallback load STORAGE_ENCRYPTION_KEY from env files ([#11614](https://github.com/diegosouzapw/OmniRoute/pull/11614)) — thanks @benzntech +- **fix(providers):** mark Brave Search as no longer having a free tier ([#11621](https://github.com/diegosouzapw/OmniRoute/pull/11621)) — thanks @turbolego +- **fix:** pass tool result content through as string in OpenAI/Claude -> Gemini translation ([#11624](https://github.com/diegosouzapw/OmniRoute/pull/11624)) — thanks @Hsia97 +- **fix(sse):** scope credential health to exact key ([#11628](https://github.com/diegosouzapw/OmniRoute/pull/11628)) — thanks @KaspaPulse +- **fix(install):** guard prepare script against missing husky ([#11634](https://github.com/diegosouzapw/OmniRoute/pull/11634)) — thanks @Prajeeth-12 +- **fix(security):** redact NVIDIA_API_KEY at the log sink in the ad-hoc diag script ([#11639](https://github.com/diegosouzapw/OmniRoute/pull/11639)) +- **fix(types):** clear the two type regressions red on release/v3.8.51 ([#11640](https://github.com/diegosouzapw/OmniRoute/pull/11640)) — thanks @jonlwheat2-gif +- **fix(build):** resolve workspace: protocol dependencies to concrete semver versions during prepublish (#11540) ([#11641](https://github.com/diegosouzapw/OmniRoute/pull/11641)) — thanks @HouMinXi +- **fix(search):** properly resolve configured search connection in /v1/responses pipeline (#11524) ([#11642](https://github.com/diegosouzapw/OmniRoute/pull/11642)) — thanks @HouMinXi +- **fix(authz):** trust cf-connecting-ip for via-proxy detection when peer is Cloudflare edge (#11514) ([#11643](https://github.com/diegosouzapw/OmniRoute/pull/11643)) — thanks @HouMinXi +- **fix(security):** compare the OIDC state cookie in constant time ([#11646](https://github.com/diegosouzapw/OmniRoute/pull/11646)) +- **fix(security):** namespace the dedup hash by the calling API key ([#11649](https://github.com/diegosouzapw/OmniRoute/pull/11649)) +- **fix(models):** gate the Codex-native catalog loop on prefix mode (#11632) ([#11662](https://github.com/diegosouzapw/OmniRoute/pull/11662)) — thanks @xiaoyaner0201 +- **fix(dashboard):** project Codex routing quota in account pools ([#11666](https://github.com/diegosouzapw/OmniRoute/pull/11666)) — thanks @KaspaPulse +- **fix(ddgw):** normaliza system no upstream da release v3.8.51 ([#11692](https://github.com/diegosouzapw/OmniRoute/pull/11692)) — thanks @RhianB14 +- **fix(memory):** honor category filter in GET /api/memory ([#11699](https://github.com/diegosouzapw/OmniRoute/pull/11699)) — thanks @Sabeekhann +- **fix(ci):** detect stale workspace lockfile entries ([#11714](https://github.com/diegosouzapw/OmniRoute/pull/11714)) — thanks @pacocartones +- **fix(ci):** stamp dist/BUILD_SHA before the npm publish provenance gate ([#11721](https://github.com/diegosouzapw/OmniRoute/pull/11721)) +- **fix(docker):** decouple the best-effort Bun image from the release manifest ([#11724](https://github.com/diegosouzapw/OmniRoute/pull/11724)) +- **perf(compression):** avoid cloning memo misses twice ([#11727](https://github.com/diegosouzapw/OmniRoute/pull/11727)) — thanks @RaviTharuma +- **fix(docker):** let the bun digest artifact be absent, not fatal ([#11740](https://github.com/diegosouzapw/OmniRoute/pull/11740)) +- **fix(shared):** block cloud-metadata hosts under default remote-image guard ([#11755](https://github.com/diegosouzapw/OmniRoute/pull/11755)) — thanks @santosraju99-hub +- **fix(api):** preserve caller-provided X-Correlation-Id on chat completions ([#11760](https://github.com/diegosouzapw/OmniRoute/pull/11760)) — thanks @aniruddhaadak80 +- **fix(resilience):** lock permanently retired models instead of short backoff (Gemini ban prevention) ([#11762](https://github.com/diegosouzapw/OmniRoute/pull/11762)) — thanks @turbolego +- **fix:** mark Vercel AI Gateway as passthroughModels ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **fix(resilience):** stop hammering permanently-moved endpoints and billing-suspended accounts ([#11774](https://github.com/diegosouzapw/OmniRoute/pull/11774)) — thanks @turbolego +- **fix(lease):** remove global static reservation and gate routing on live active lease occupancy ([#11775](https://github.com/diegosouzapw/OmniRoute/pull/11775)) — thanks @TheDemonTuan +- **fix(ci):** size the install-upgrade gate to a measured run, and log the pack cost ([#11776](https://github.com/diegosouzapw/OmniRoute/pull/11776)) +- **fix(pwa):** stop serving the stale shell after deploys ([#11779](https://github.com/diegosouzapw/OmniRoute/pull/11779)) — thanks @HouMinXi +- **fix(combo):** lock GitHub models rejected as "not supported" for future requests ([#11781](https://github.com/diegosouzapw/OmniRoute/pull/11781)) — thanks @turbolego +- **fix(cli):** probe both IPv4 and IPv6 loopback for server readiness (#11766) ([#11794](https://github.com/diegosouzapw/OmniRoute/pull/11794)) — thanks @vermasomesh835 +- **fix(sse):** preserve CLIProxyAPI wrappers per connection ([#11797](https://github.com/diegosouzapw/OmniRoute/pull/11797)) — thanks @RaviTharuma +- **fix(providers):** honor model auto-fetch opt-in on create ([#11798](https://github.com/diegosouzapw/OmniRoute/pull/11798)) — thanks @RaviTharuma +- **fix(dashboard):** make model auto-fetch order independent ([#11805](https://github.com/diegosouzapw/OmniRoute/pull/11805)) — thanks @RaviTharuma +- **fix(docker):** make GHA cache exports best effort ([#11806](https://github.com/diegosouzapw/OmniRoute/pull/11806)) — thanks @RaviTharuma +- **fix(kiro):** do not permanently ban on 'User is not authorized to make this call' ([#11809](https://github.com/diegosouzapw/OmniRoute/pull/11809)) — thanks @Deftera186 +- **fix(sse):** keep cache-write tokens in OpenAI-shaped usage ([#11814](https://github.com/diegosouzapw/OmniRoute/pull/11814)) — thanks @TheDemonTuan +- **fix(docs):** resolve relative markdown and wiki links across Fumadocs and GitHub wiki ([#11834](https://github.com/diegosouzapw/OmniRoute/pull/11834)) — thanks @AndrianBalanescu +- **fix(chat):** preserve unstripped model string for passthrough provider routing ([#11840](https://github.com/diegosouzapw/OmniRoute/pull/11840)) — thanks @raheemuddin786 +- **fix(mcp):** avoid async initialization inside sync esm wrappers (#11569) ([#11849](https://github.com/diegosouzapw/OmniRoute/pull/11849)) — thanks @HouMinXi +- **fix(sse):** preserve coherent markdown token boundaries across stream translation chunks (#11606) ([#11851](https://github.com/diegosouzapw/OmniRoute/pull/11851)) — thanks @HouMinXi +- **fix(ci):** run the install-upgrade gate on disk, not on the /tmp tmpfs ([#11855](https://github.com/diegosouzapw/OmniRoute/pull/11855)) +- **fix(skills):** expand shorthand property types in injected tool schemas ([#11857](https://github.com/diegosouzapw/OmniRoute/pull/11857)) — thanks @alvinveroy +- **fix(ci):** publish npm from a hosted runner so provenance is accepted ([#11877](https://github.com/diegosouzapw/OmniRoute/pull/11877)) +- **fix(lint):** pin react hooks plugin to 7.0.1 ([#11886](https://github.com/diegosouzapw/OmniRoute/pull/11886)) +- **fix(ci):** surface and clear release-green ESLint failure ([#11890](https://github.com/diegosouzapw/OmniRoute/pull/11890)) — thanks @MumuTW +- **fix(guardrails):** prevent duplicate prompt-injection-guard log output ([#11936](https://github.com/diegosouzapw/OmniRoute/pull/11936)) — thanks @patrykkopycinski +- **fix(auth):** downgrade expected transient states from warn to debug ([#11937](https://github.com/diegosouzapw/OmniRoute/pull/11937)) — thanks @patrykkopycinski +- **fix(codex):** fail over image generation for imported free plans ([#11948](https://github.com/diegosouzapw/OmniRoute/pull/11948)) — thanks @Bl0ck154 +- **fix(codex):** normalize non-stream responses ([#11951](https://github.com/diegosouzapw/OmniRoute/pull/11951)) — thanks @Bl0ck154 +- **fix(images):** forward Antigravity image size ([#11952](https://github.com/diegosouzapw/OmniRoute/pull/11952)) — thanks @Bl0ck154 +- **fix(db):** invalidate connection cache after upsert ([#11953](https://github.com/diegosouzapw/OmniRoute/pull/11953)) — thanks @Bl0ck154 +- **fix(codex):** restore imported account state ([#11954](https://github.com/diegosouzapw/OmniRoute/pull/11954)) — thanks @Bl0ck154 +- **fix(sse):** give extended-thinking targets the reasoning readiness budget ([#11959](https://github.com/diegosouzapw/OmniRoute/pull/11959)) — thanks @abhisheksharma2411 +- **fix(dashboard):** use opaque background and readable text color on cost chart tooltips ([#11960](https://github.com/diegosouzapw/OmniRoute/pull/11960)) — thanks @ZaimMarzuki +- **fix(antigravity):** send complete loadCodeAssist metadata (ideType/platform/pluginType as numeric enums) ([#11969](https://github.com/diegosouzapw/OmniRoute/pull/11969)) — thanks @rifqiawl +- **fix(core):** resolve DB init race condition and reasoning translation ([#12003](https://github.com/diegosouzapw/OmniRoute/pull/12003)) — thanks @raheemuddin786 +- **fix(admission):** exclude reclaimable page cache from the cgroup pressure ratio ([#12017](https://github.com/diegosouzapw/OmniRoute/pull/12017)) — thanks @HouMinXi +- **fix(build):** prune non-production trees in NFT trace excludes and tsconfig ([#12028](https://github.com/diegosouzapw/OmniRoute/pull/12028)) — thanks @Chewji9875 +- **fix(migrations):** add renamed migration compatibility for 056/073/077/101 ([#12036](https://github.com/diegosouzapw/OmniRoute/pull/12036)) — thanks @oyi77 +- **fix(sse):** default crash-guard logger to console.warn, not console ([#12042](https://github.com/diegosouzapw/OmniRoute/pull/12042)) — thanks @HouMinXi +- **fix(catalog):** derive vision/modalities for built-in auto combos from effective target pool ([#12046](https://github.com/diegosouzapw/OmniRoute/pull/12046)) — thanks @Prajeeth-12 +- **fix(release):** the packaged-app smoke verifies the database opened, not a driver line the primary path never prints (twin of #12032) ([#12047](https://github.com/diegosouzapw/OmniRoute/pull/12047)) +- **fix(barrel):** migrate src/app/ off the @/lib/localDb barrel import (#11795 Phase 2) ([#12051](https://github.com/diegosouzapw/OmniRoute/pull/12051)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate src/lib/ off the localDb barrel to direct db imports (#59) ([#12052](https://github.com/diegosouzapw/OmniRoute/pull/12052)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate open-sse, src/shared, src/sse, src/models, src/domain off the @/lib/localDb barrel import (#11795 Phase 4) ([#12053](https://github.com/diegosouzapw/OmniRoute/pull/12053)) — thanks @jonlwheat2-gif +- **fix(barrel):** delete the @/lib/localDb barrel — every consumer migrated (#11795 Phase 5) ([#12055](https://github.com/diegosouzapw/OmniRoute/pull/12055)) — thanks @jonlwheat2-gif +- **fix(radar):** restore D12 public boundary ([#12057](https://github.com/diegosouzapw/OmniRoute/pull/12057)) +- **fix(dev):** bound webpack and Tailwind scans ([#12075](https://github.com/diegosouzapw/OmniRoute/pull/12075)) — thanks @backryun +- **fix(dev):** isolate root layout settings reads ([#12076](https://github.com/diegosouzapw/OmniRoute/pull/12076)) — thanks @backryun +- **fix(dev):** reduce instrumentation executor fan-out (phase 3) ([#12078](https://github.com/diegosouzapw/OmniRoute/pull/12078)) — thanks @backryun +- **fix(dev):** make logging resources HMR-singleton ([#12079](https://github.com/diegosouzapw/OmniRoute/pull/12079)) — thanks @backryun +- **fix(sse):** keep unavailable forced connections scoped ([#12080](https://github.com/diegosouzapw/OmniRoute/pull/12080)) — thanks @keeltrace +- **fix(dev):** isolate batch dispatch from instrumentation ([#12081](https://github.com/diegosouzapw/OmniRoute/pull/12081)) — thanks @backryun +- **fix(ci):** reconcile release test contract drift ([#12082](https://github.com/diegosouzapw/OmniRoute/pull/12082)) — thanks @backryun +- **fix(ci):** accept CVE-2025-68121 in the prebuilt tls-client .so, auto-close base-red issues, guard Scorecard on the default branch ([#12085](https://github.com/diegosouzapw/OmniRoute/pull/12085)) +- **fix(db):** drop three consumer-less 1proxy exports — dead-code base-red on release/v3.8.51 after the barrel deletion (#12055) ([#12087](https://github.com/diegosouzapw/OmniRoute/pull/12087)) +- **fix(api):** clear the six API-route TypeScript regressions the new gate landed red on ([#12094](https://github.com/diegosouzapw/OmniRoute/pull/12094)) +- **fix(release):** never let the tag-push Create Release append auto notes to the curated body ([#12096](https://github.com/diegosouzapw/OmniRoute/pull/12096)) +- **fix(sse):** honor CLIProxyAPI environment API key ([#12099](https://github.com/diegosouzapw/OmniRoute/pull/12099)) — thanks @RaviTharuma +- **fix(oauth):** bind Google refresh to the client that issued the token ([#12106](https://github.com/diegosouzapw/OmniRoute/pull/12106)) — thanks @HouMinXi +- **fix(dashboard):** make RequestLoggerDetail loadable outside Next — CSS via globals.css + CJS/ESM interop (#11703 base-reds) ([#12114](https://github.com/diegosouzapw/OmniRoute/pull/12114)) +- **fix(leases):** project status lease row to lease columns so joined connection PII never escapes ([#12115](https://github.com/diegosouzapw/OmniRoute/pull/12115)) — thanks @geek007git +- **fix(plugins):** do not kill the plugin process when a fire-and-forget hook times out ([#12116](https://github.com/diegosouzapw/OmniRoute/pull/12116)) — thanks @geek007git +- **fix(guardrails):** restore injection-guard logging on middleware-only routes ([#12117](https://github.com/diegosouzapw/OmniRoute/pull/12117)) — thanks @geek007git +- **fix(plugins):** refresh stored manifest from disk on activate so new hook fields reach existing installs ([#12120](https://github.com/diegosouzapw/OmniRoute/pull/12120)) — thanks @geek007git +- **fix(codex):** preserve existing provider state when bulk-import upserts a matching connection ([#12122](https://github.com/diegosouzapw/OmniRoute/pull/12122)) — thanks @geek007git +- **fix(ci):** clear the two base-reds the 2026-08-30 merge batch left on release/v3.8.51 (round 3) ([#12123](https://github.com/diegosouzapw/OmniRoute/pull/12123)) +- **fix(sse):** strip type:'custom' from Claude tools on agentrouter dispatch ([#12126](https://github.com/diegosouzapw/OmniRoute/pull/12126)) — thanks @wahidsadik371-coder +- **fix(sse):** stop advertising video providers the dispatcher cannot run ([#12131](https://github.com/diegosouzapw/OmniRoute/pull/12131)) — thanks @quiterunner-commits +- **fix(search):** treat HTTP 432 and plan limit errors as transient cooldown ([#12139](https://github.com/diegosouzapw/OmniRoute/pull/12139)) — thanks @HouMinXi +- **fix(sse):** refuse an AI Horde queue that cannot fit the request budget ([#12143](https://github.com/diegosouzapw/OmniRoute/pull/12143)) — thanks @quiterunner-commits +- **fix(ci):** clear the base-reds the afternoon merge batch left on release/v3.8.51 (round 5: provider count 352, TS2554/TS2677) ([#12144](https://github.com/diegosouzapw/OmniRoute/pull/12144)) +- **fix(translator):** strip neutral tool_choice when tools absent in Responses-to-Chat (#12141) ([#12166](https://github.com/diegosouzapw/OmniRoute/pull/12166)) — thanks @HouMinXi +- **fix(guardrails):** pass providerId to getResolvedModelCapabilities in checkComboVision (#12112) ([#12169](https://github.com/diegosouzapw/OmniRoute/pull/12169)) — thanks @HouMinXi +- **fix(translator):** strip plaintext reasoning content for opaque responses backends (#12128) ([#12171](https://github.com/diegosouzapw/OmniRoute/pull/12171)) — thanks @HouMinXi +- **perf(stream):** compile hot-path regexes once, bound token caches, fix quadratic buffering ([#12179](https://github.com/diegosouzapw/OmniRoute/pull/12179)) — thanks @opensource-elearning +- **fix:** resolve compression worker file using runtime anchors instead of… ([#12183](https://github.com/diegosouzapw/OmniRoute/pull/12183)) — thanks @NightStalker-87 +- **fix:** prevent Claude Code session kills via liveness-aware readiness + auto model echo ([#12189](https://github.com/diegosouzapw/OmniRoute/pull/12189)) — thanks @opensource-elearning +- **fix(translator):** preserve falsy primitive values in Gemini and Antigravity function response results ([#12191](https://github.com/diegosouzapw/OmniRoute/pull/12191)) — thanks @Siva010 +- **fix(oauth):** mark empty Antigravity projectId as degraded and clear stale errors (#11284) ([#12205](https://github.com/diegosouzapw/OmniRoute/pull/12205)) — thanks @HouMinXi +- **fix(providers):** gate the Codex auto-ping usage read on the shared quota throttle ([#12209](https://github.com/diegosouzapw/OmniRoute/pull/12209)) — thanks @abhisheksharma2411 +- **fix(combos):** prioritize SQLite row id over inner JSON id and notify delete errors ([#12213](https://github.com/diegosouzapw/OmniRoute/pull/12213)) — thanks @HouMinXi +- **fix(providers):** mark featherless.ai as no longer having a free tier (`hasFree: false`) ([#12216](https://github.com/diegosouzapw/OmniRoute/pull/12216)) — thanks @turbolego +- **fix(deepseek-web):** stop Turbopack dev panic in the PoW worker path resolver ([#12221](https://github.com/diegosouzapw/OmniRoute/pull/12221)) — thanks @hartmark +- **fix(chat-admission):** clarify local 503 source ([#12223](https://github.com/diegosouzapw/OmniRoute/pull/12223)) — thanks @keeltrace +- **fix(dev):** silence webpack runtime module warnings ([#12228](https://github.com/diegosouzapw/OmniRoute/pull/12228)) — thanks @backryun +- **fix(db):** use module.require for CommonJS runtime driver loading ([#12230](https://github.com/diegosouzapw/OmniRoute/pull/12230)) — thanks @raheemuddin786 +- **fix(memory):** honest probe-driven FTS5 keyword status + memory_id rowid sync ([#12231](https://github.com/diegosouzapw/OmniRoute/pull/12231)) — thanks @raheemuddin786 +- **fix(system):** propagate abort signal to stream reader in HTTP version checks ([#12232](https://github.com/diegosouzapw/OmniRoute/pull/12232)) — thanks @raheemuddin786 +- **fix(resilience):** isolate local host execution errors from provider circuit breakers ([#12233](https://github.com/diegosouzapw/OmniRoute/pull/12233)) — thanks @raheemuddin786 +- **fix(sse):** sort injected tools deterministically for prompt caching ([#12234](https://github.com/diegosouzapw/OmniRoute/pull/12234)) — thanks @raheemuddin786 +- **fix(combo):** always clear the loop-safety timer, not just on the happy path (#11804) ([#12245](https://github.com/diegosouzapw/OmniRoute/pull/12245)) +- **fix(quality):** base-red pair — stryker tap registration + turn-pin suites aligned to the window gate ([#12255](https://github.com/diegosouzapw/OmniRoute/pull/12255)) — thanks @backryun +- **fix(dev):** qualify Turbopack runtime boundaries ([#12258](https://github.com/diegosouzapw/OmniRoute/pull/12258)) — thanks @backryun +- **fix(combo):** fall back to full pool when collapsed sole survivor is context-too-small ([#12278](https://github.com/diegosouzapw/OmniRoute/pull/12278)) — thanks @opensource-elearning +- **fix(combo):** bound the pre-dispatch unavailable skip so a stale label cannot dark a pool (#12168) ([#12285](https://github.com/diegosouzapw/OmniRoute/pull/12285)) +- **fix(security):** strip Qwen/Alibaba console-session cookies from provider API responses ([#12287](https://github.com/diegosouzapw/OmniRoute/pull/12287)) +- **fix(usage):** console-aware Token Plan guidance + subscription hint on bailian 401 ([#12288](https://github.com/diegosouzapw/OmniRoute/pull/12288)) +- **fix(resourcePressure):** log numeric detail on every rejection, recover faster ([#12293](https://github.com/diegosouzapw/OmniRoute/pull/12293)) — thanks @hartmark +- **fix(release):** drain the 2026-09-01 base-red window — passthrough usage regression + radarPage i18n keys ([#12327](https://github.com/diegosouzapw/OmniRoute/pull/12327)) +- **fix(ci):** openapi-security-tiers checker must honor routeGuard patterns + imported prefixes ([#12350](https://github.com/diegosouzapw/OmniRoute/pull/12350)) — thanks @ggiak +- **perf(ci):** cache node_modules in the npm-ci-retry composite (#8084 D3) ([#12408](https://github.com/diegosouzapw/OmniRoute/pull/12408)) +- **revert(ui):** point CTAs back at their real destinations ([#12410](https://github.com/diegosouzapw/OmniRoute/pull/12410)) +- **fix(release):** drain the 2026-09-02 base-red — rerank-providers import + api-typecheck baseline ratchet ([#12414](https://github.com/diegosouzapw/OmniRoute/pull/12414)) +- **fix(types):** make system prompt injection noImplicitAny-safe ([#12416](https://github.com/diegosouzapw/OmniRoute/pull/12416)) +- **fix(quality):** validate typecheck baseline schema ([#12419](https://github.com/diegosouzapw/OmniRoute/pull/12419)) +- **fix(memory):** resolve rerank provider node cache import ([#12421](https://github.com/diegosouzapw/OmniRoute/pull/12421)) +- **fix(ci):** repair release-root regressions — pack dedup, web-session syntax, uc-image ids ([#12423](https://github.com/diegosouzapw/OmniRoute/pull/12423)) — thanks @backryun +- **fix(providers):** separate MaxAI and UC credential contracts ([#12431](https://github.com/diegosouzapw/OmniRoute/pull/12431)) +- **fix(cli):** preserve Claude settings on config set ([#12432](https://github.com/diegosouzapw/OmniRoute/pull/12432)) — thanks @KrzysiekSko +- **fix(providers):** repair the maxai credential block truncated by merge auto-resolve ([#12433](https://github.com/diegosouzapw/OmniRoute/pull/12433)) +- **fix(quality):** record the 2026-09-02 merged growth in the file-size baseline ([#12434](https://github.com/diegosouzapw/OmniRoute/pull/12434)) +- **fix(uc):** route UC error strings through sanitizeErrorMessage; allowlist the retired codex id ([#12437](https://github.com/diegosouzapw/OmniRoute/pull/12437)) +- **fix(adapta):** redact streamed upstream errors ([#12438](https://github.com/diegosouzapw/OmniRoute/pull/12438)) +- **fix(sse):** remap non-contiguous upstream tool_calls index to a gap-free output_index ([#12445](https://github.com/diegosouzapw/OmniRoute/pull/12445)) — thanks @hartmark +- **fix(quality):** detect duplicate tool_calls entries in one response ([#12446](https://github.com/diegosouzapw/OmniRoute/pull/12446)) — thanks @hartmark +- **fix(conversations):** resolve turn content OmniRoute never sends back to the client ([#12447](https://github.com/diegosouzapw/OmniRoute/pull/12447)) — thanks @hartmark +- **fix(combo):** do not treat credits-exhausted 401 as auth skip ([#12449](https://github.com/diegosouzapw/OmniRoute/pull/12449)) — thanks @RaviTharuma +- **fix(auth):** do not park healthy quota accounts as expired ([#12452](https://github.com/diegosouzapw/OmniRoute/pull/12452)) — thanks @RaviTharuma +- **fix(responses-continuation):** fail closed on a collector-truncated, empty output array ([#12460](https://github.com/diegosouzapw/OmniRoute/pull/12460)) — thanks @hartmark +- **fix(logging):** raise the SSE payload collector's default cap ([#12461](https://github.com/diegosouzapw/OmniRoute/pull/12461)) — thanks @hartmark +- **fix(resilience):** surface Responses failed.error.message in 502s ([#12472](https://github.com/diegosouzapw/OmniRoute/pull/12472)) — thanks @RaviTharuma +- **fix(security):** unbiased maxai X-Random nonce + stricter URL/regex assertions ([#12502](https://github.com/diegosouzapw/OmniRoute/pull/12502)) +- **fix(video):** re-anchor transcript log-redaction so PII/credential maskers can't reopen the leak (#12150 P1 follow-up) ([#12503](https://github.com/diegosouzapw/OmniRoute/pull/12503)) +- **fix(monitoring):** serve cached credentialHealth off the request path ([#12533](https://github.com/diegosouzapw/OmniRoute/pull/12533)) — thanks @RaviTharuma +- **fix(quota):** keep Antigravity Gemini usable when Claude weekly is empty ([#12566](https://github.com/diegosouzapw/OmniRoute/pull/12566)) — thanks @HouMinXi +- **fix(authz):** hard-gate every credential export and CLI-config write (GHSA-5926-2w35-7h4q) ([#12600](https://github.com/diegosouzapw/OmniRoute/pull/12600)) +- **fix(docs):** restore the Next build — REMOVED_PROVIDERS.md had no frontmatter (base-red #12581) ([#12610](https://github.com/diegosouzapw/OmniRoute/pull/12610)) +- **fix(quality):** re-point the zcodeProtocol public-creds allowlist to line 313 ([#12615](https://github.com/diegosouzapw/OmniRoute/pull/12615)) +- **fix(security):** close 3 advisories — search baseUrl exfil, sk- in the error sanitizer, bifrost relay header leak ([#12620](https://github.com/diegosouzapw/OmniRoute/pull/12620)) +- **fix(providers):** sanitize boolean required and nested bare maps for Gemini (#12269) ([#12624](https://github.com/diegosouzapw/OmniRoute/pull/12624)) — thanks @HouMinXi +- **fix(resilience):** keep Overloaded STREAM_EARLY_EOF off the provider breaker ([#12626](https://github.com/diegosouzapw/OmniRoute/pull/12626)) — thanks @HouMinXi +- **fix(catalog):** re-audit free-tier quotas against official pages ([#12649](https://github.com/diegosouzapw/OmniRoute/pull/12649)) +- **fix(dashboard):** keep a request's pending-tracking id stable across combo target retries ([#12650](https://github.com/diegosouzapw/OmniRoute/pull/12650)) — thanks @hartmark +- **fix(tests):** retire dead model ids from the chat-pipeline integration suite (base-red #12581) ([#12670](https://github.com/diegosouzapw/OmniRoute/pull/12670)) +- **fix:** resolve SqliteError no such table compression_run_telemetry during cleanup ([#12682](https://github.com/diegosouzapw/OmniRoute/pull/12682)) — thanks @groovecityJO +- **fix(combo):** treat a pin-only step as implicit connection allowlist ([#12697](https://github.com/diegosouzapw/OmniRoute/pull/12697)) — thanks @HouMinXi +- **fix(ci):** ship .npmrc in published package so legacy-peer-deps applies to consumers (#11544) ([#12699](https://github.com/diegosouzapw/OmniRoute/pull/12699)) — thanks @KooshaPari +- **fix(docker):** pin 4 CLI tools to exact versions (#12576) ([#12703](https://github.com/diegosouzapw/OmniRoute/pull/12703)) — thanks @KooshaPari +- **fix(docker):** document and harden cli profile trust boundary (#12570) ([#12706](https://github.com/diegosouzapw/OmniRoute/pull/12706)) — thanks @KooshaPari +- **fix(sse):** redact video transcript in pre-guardrail rejected-request logs (#12150 P2 item 7) ([#12710](https://github.com/diegosouzapw/OmniRoute/pull/12710)) +- **fix(i18n):** quote placeholder in OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES description (#12505) ([#12769](https://github.com/diegosouzapw/OmniRoute/pull/12769)) — thanks @KooshaPari +- **fix(api/pricing):** surface validation error message as string, not raw object (#12494) ([#12771](https://github.com/diegosouzapw/OmniRoute/pull/12771)) — thanks @KooshaPari +- **fix(sse):** re-enable prompt compression for native Codex passthrough ([#12834](https://github.com/diegosouzapw/OmniRoute/pull/12834)) — thanks @thomasmaerz +- **fix(dashboard):** model health tests for a provider node set to the Responses API now call `/v1/responses` with a Responses-shaped body instead of `/v1/chat/completions` — those models were reported as `Provider returned HTTP 200 but no text content` even though the same model answered normally through `/v1/responses` ([#13070](https://github.com/diegosouzapw/OmniRoute/issues/13070)) ([#13087](https://github.com/diegosouzapw/OmniRoute/pull/13087)) — thanks @ntdatt812 +- fix(routing): stop a round-robin combo's "opencode" targets from collapsing onto the opencode-zen connection (#11912) ([#13283](https://github.com/diegosouzapw/OmniRoute/pull/13283)) +- fix(routing): stop the reactive-compaction debug log from lying when compression is globally disabled (#11977) ([#13276](https://github.com/diegosouzapw/OmniRoute/pull/13276)) +- fix(electron): relativize standalone-bundle symlink targets so Stage 8 manifest verification stops failing on Windows (#11979) ([#13251](https://github.com/diegosouzapw/OmniRoute/pull/13251)) +- fix(dashboard): surface a visible error when Compression Studio's combined preview run fails (#12061) ([#13288](https://github.com/diegosouzapw/OmniRoute/pull/13288)) +- fix(dashboard): make the compression "Effective pipeline" preview honor the active profile and warn when the master switch is off (#12063) ([#13287](https://github.com/diegosouzapw/OmniRoute/pull/13287)) +- fix(providers): scope TinyCMS Web signer's DOM shims to each call instead of leaking them for the process lifetime, and surface a clean HTTP status on a non-JSON interception-toggles error (#12072) ([#13254](https://github.com/diegosouzapw/OmniRoute/pull/13254)) +- fix(guardrails): stop Vision Bridge from re-selecting a model locked after a 404 (#12111) ([#13259](https://github.com/diegosouzapw/OmniRoute/pull/13259)) +- fix(sse): require Responses-shaped body before native OpenAI-compatible passthrough (#12129) ([#13278](https://github.com/diegosouzapw/OmniRoute/pull/13278)) +- fix(providers): minimax-m3 now collapses manual thinking.type:"enabled" to adaptive, preventing upstream 400 (2013) (#12132) ([#13249](https://github.com/diegosouzapw/OmniRoute/pull/13249)) +- fix(db): scope model visibility overrides by modality so hiding a Chat model no longer hides an identically-ID'd Image/Embeddings/etc. model (#12172) ([#13258](https://github.com/diegosouzapw/OmniRoute/pull/13258)) +- fix(db): scope local-provider apiKey dedup to matching base URL so LM Studio/Ollama-style connections support multiple accounts (#12173) ([#13250](https://github.com/diegosouzapw/OmniRoute/pull/13250)) +- fix(providers): refresh Trae's stale Referer/Origin and forward user timezone so imported connections stop failing with 401 (#12190) ([#13255](https://github.com/diegosouzapw/OmniRoute/pull/13255)) +- fix(providers): route opencode-go/gpt-5.6-luna to /responses instead of /chat/completions (#12196) ([#13275](https://github.com/diegosouzapw/OmniRoute/pull/13275)) +- fix(dashboard): allow deleting the last extra-upstream-header row even when invalid (#12251) ([#13252](https://github.com/diegosouzapw/OmniRoute/pull/13252)) +- **fix(i18n):** translate pre-existing `__MISSING__:` keys for `combo.sort`, `requestLogger.detail` expand/collapse, `common.profile`, and `settings.resilienceCredentialHealth*` across 39 locales ([#12272](https://github.com/diegosouzapw/OmniRoute/issues/12272)) ([#13034](https://github.com/diegosouzapw/OmniRoute/pull/13034)) — thanks @HouMinXi +- fix(cli): run the Node.js runtime compatibility guard before the heavy `tsx/esm` + Commander import chain so an unsupported runtime gets a clear message instead of a raw `Invalid regular expression flags` crash (#12296) ([#13253](https://github.com/diegosouzapw/OmniRoute/pull/13253)) +- fix(dashboard): refresh the providers list after deleting a compatible provider node (#12298) ([#13256](https://github.com/diegosouzapw/OmniRoute/pull/13256)) +- fix(usage): fail closed on API-key budget enforcement when a provider's `auto` routing alias has no pricing row, instead of silently counting it as $0 (#12341) ([#13257](https://github.com/diegosouzapw/OmniRoute/pull/13257)) +- **fix(providers):** include the submitted Agnes video model when polling by `video_id` ([#12356](https://github.com/diegosouzapw/OmniRoute/pull/12356)) — thanks @pacocartones +- **fix(routing):** custom OpenAI-compatible nodes now honor the saved Chat/Responses API type after edits instead of letting the node's original ID prefix override the live connection setting ([#11884](https://github.com/diegosouzapw/OmniRoute/issues/11884)). ([#12358](https://github.com/diegosouzapw/OmniRoute/pull/12358)) — thanks @pacocartones +- fix(sse): surface an error instead of a silent empty 200 when a Claude stream closes with zero bytes (#12398) ([#13285](https://github.com/diegosouzapw/OmniRoute/pull/13285)) +- fix(oauth): warn before the dead localhost:8080 redirect in antigravity/gemini `oauth start` (#12413) ([#13265](https://github.com/diegosouzapw/OmniRoute/pull/13265)) +- fix(providers): stop devin-cli spawn error from double-closing the SSE controller (#12517) ([#13261](https://github.com/diegosouzapw/OmniRoute/pull/13261)) +- **fix(rerank):** clamp Voyage `top_k` to the documents actually sent after empty-string filtering, and honor `return_documents: false` in the NVIDIA response adapter (#12523 — thanks @pacocartones) +- **fix(audio):** `/v1/audio/translations` now resolves combo names the way `/v1/audio/transcriptions` already does, so a combo that `GET /v1/models` advertises is fanned out to its targets instead of being rejected with `400 Invalid translation model: . Use format: provider/model`; literal `provider/model` ids and unknown bare names behave as before (#12536 — thanks @pacocartones) +- **fix(gemini):** strip the JSON-Schema-2020-12 `prefixItems` keyword from Gemini tool schemas at every nesting level, so Claude Code tool definitions no longer fail with `400 Unknown name "prefixItems"` on Gemini models (#12540 — thanks @pacocartones) +- **fix(api-manager):** Expose an accessible loading status while API keys are fetched instead of an empty accessibility tree (#12541 — thanks @pacocartones) +- **fix(video):** Clamp `estimateJpegFrameBytes` at zero for padding-only payloads and build the three encode-side frame data URIs from `JPEG_FRAME_DATA_URI_PREFIX` instead of a repeated literal (#12543 — thanks @pacocartones) +- **fix(devin):** accept Windows `DEVIN_AGENTIC_HOME` sandbox paths (`C:\...\.sandbox\...`) in the isolated-home check so the Devin Claude Bridge no longer fails closed on Windows ([#12405](https://github.com/diegosouzapw/OmniRoute/issues/12405)) (#12545 — thanks @pacocartones) +- **fix(db):** Add `conversation_turn_nodes` and orphaned `agentic_conversations` to the auto-cleanup cycle under the existing `retention.callLogs` window, so identity nodes whose call-log content has already been purged no longer accumulate without bound in `storage.sqlite` (#12548 — thanks @pacocartones) +- **fix(i18n):** Wrap the `~/.claude/profiles//settings.json` placeholder in ICU single quotes in the `OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES` feature-flag description across all 42 locales and the TypeScript default, so next-intl no longer fails with `INVALID_MESSAGE: UNCLOSED_TAG` and the Feature Flags card shows the description instead of the raw key (#12549 — thanks @pacocartones) +- **fix(orchestration):** `updateCloudAgentTask` now publishes the task's real `status` on `agent.task.updated` when an update only touches `result`, `activities` or `error`, instead of the fabricated `"updated"` state, and stays silent when no row matched the id (#12550 — thanks @pacocartones) +- **fix(i18n):** the home "Recent Requests" panel and the Provider Topology legend are now translated instead of rendering English copies on non-English dashboards; the legend reads its own `home.topologyLegend*` labels with consistent casing rather than borrowing the memory-settings "Recent" and analytics "Error" strings (#12551 — thanks @pacocartones). +- **docs(reference):** bring the `FEATURE_FLAGS.md` catalog back to 1:1 with `featureFlagDefinitions.ts` — 20 missing flags added, the two `*_BLOCK_THRESHOLD` env-only knobs moved out of the flag tables, category/total counts and the Live WS port corrected, guarded by a static test (#12552 — thanks @pacocartones) +- **fix(i18n):** backfill missing `usage.kiloPass*` strings in 39 locales and restore `featureFlagOmnirouteDisableThinkingLevelVariantsDescription` in `pt.json` ([#12561](https://github.com/diegosouzapw/OmniRoute/issues/12561)) ([#13027](https://github.com/diegosouzapw/OmniRoute/pull/13027)) — thanks @HouMinXi +- fix(docker): default docker-compose app ports (dashboard/API/live-WS) to loopback instead of `0.0.0.0`, closing the anonymous `/v1` LAN/WAN exposure gap left open by `REQUIRE_API_KEY=false` (#12568) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): close DNS-rebinding SSRF gap in webhook outbound-URL guard (#12569) ([#13243](https://github.com/diegosouzapw/OmniRoute/pull/13243)) +- fix(docker): require a per-session token on the VNC browser CDP bridge and isolate it on a dedicated Docker network (#12571) ([#13241](https://github.com/diegosouzapw/OmniRoute/pull/13241)) +- fix(open-sse): write Adobe Firefly session tokens and cookie jars with 0700/0600 permissions instead of the process umask (#12572) ([#13242](https://github.com/diegosouzapw/OmniRoute/pull/13242)) +- fix(api): remove hardcoded wildcard CORS in openai-to-gemini-sse.ts so the centralized fail-closed CORS gate is the sole source of `Access-Control-Allow-Origin` (#12573) ([#13239](https://github.com/diegosouzapw/OmniRoute/pull/13239)) +- fix(api): enforce API key policy (budget/rate-limit/schedule/endpoint scoping) on the ElevenLabs speech-to-text, text-to-speech and voices proxy routes (#12574) ([#13244](https://github.com/diegosouzapw/OmniRoute/pull/13244)) +- fix(sse): cap HuggingChat NDJSON body size and bound the read loop with the fetch timeout so a stalled or hostile upstream cannot buffer unbounded memory (#12577) ([#13282](https://github.com/diegosouzapw/OmniRoute/pull/13282)) +- fix(docker): scope the cliproxyapi/qdrant/bifrost sidecars to loopback by default and forward `CLIPROXYAPI_MANAGEMENT_KEY` into the cliproxyapi container so its management API is not left both unauthenticated and LAN-published (#12578) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): create DB export temp paths with `fs.mkdtempSync` instead of predictable timestamps (#12579) ([#13237](https://github.com/diegosouzapw/OmniRoute/pull/13237)) +- Cline 401 bodies that say "re-authenticate your Cline account" classify as a refreshable OAuth token, not a terminal expired key. The cooling panel no longer labels every cooldown as a 429; it shows the recorded last error instead. (#12594) ([#13060](https://github.com/diegosouzapw/OmniRoute/pull/13060)) — thanks @HouMinXi +- **fix(catalog):** degrade unknown combo targets instead of dropping LCD modalities, and persist OpenRouter `architecture.input_modalities` into the capability snapshot ([#12613](https://github.com/diegosouzapw/OmniRoute/issues/12613)) ([#13042](https://github.com/diegosouzapw/OmniRoute/pull/13042)) — thanks @HouMinXi +- fix(providers): send `x-api-key` instead of `Authorization: Bearer` for OpenCode Zen's `/v1/responses` endpoint (Muse Spark Contributor models), fixing a 401 on OmniRoute's auth header (#12633) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(sse): surface the actionable "Auggie CLI not found" message when the shell reports a missing `auggie` binary via exit code instead of a spawn error (#12645) ([#13263](https://github.com/diegosouzapw/OmniRoute/pull/13263)) +- **fix(config):** Persist deletions of built-in background-degradation entries — when a stored settings record exists its `degradationMap` is now authoritative instead of being merged under the defaults, so an entry the user removed in the dashboard no longer reappears on the next apply or restart ([#12424](https://github.com/diegosouzapw/OmniRoute/issues/12424)) ([#12647](https://github.com/diegosouzapw/OmniRoute/pull/12647)) — thanks @pacocartones +- **fix(gamification):** action-count badge milestones (First Token, Token Consumer, Token Machine, Token Whale, and the token-sharing tier) are now backed by a durable `xp_action_counts` counter incremented in `addXp()`, instead of a live `COUNT(*)` over `xp_audit_log`. The audit log is pruned by `retention.xpAuditLog` (default 30 days), so on a default install those "lifetime" milestones were really "actions in the last 30 days" and unlocked badges could stop unlocking once old rows aged out. `getActionCount()` and `checkActionCountBadges()` now read the same durable source, and a migration backfills existing totals from the surviving audit rows ([#12546](https://github.com/diegosouzapw/OmniRoute/issues/12546)) ([#12651](https://github.com/diegosouzapw/OmniRoute/pull/12651)) — thanks @pacocartones +- **fix(images):** `/v1/images/edits` now iterates a combo's targets the same way `/v1/images/generations` does (#9239) instead of flattening a bare combo to its first target. A combo whose first target is not edit-capable — or lacks credentials — now falls through to a later edit-capable target rather than hard-erroring, and missing credentials are skipped (not a hard `401`) to match the generations path. The per-target skip/terminal classification is extracted into a shared `runImageComboTargets` loop, so generations behavior is unchanged ([#12547](https://github.com/diegosouzapw/OmniRoute/issues/12547)). ([#12653](https://github.com/diegosouzapw/OmniRoute/pull/12653)) — thanks @pacocartones +- fix(sse): add first-byte watchdog to the TLS-fingerprint transport so a stalled wreq body falls back instead of hanging for minutes (#12656) ([#13272](https://github.com/diegosouzapw/OmniRoute/pull/13272)) +- fix(sse): exempt tiny-budget reasoning probes from combo quality failure and surface persisted-cooldown skips in ALL_TARGETS_SKIPPED diagnostics (#12659) ([#13269](https://github.com/diegosouzapw/OmniRoute/pull/13269)) +- fix(models): declare the real ~1M contextLength for OpenCode Zen's Muse Spark 1.2 models instead of falling back to the 200000 provider default (#12681) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(oauth): align codebuddy-cn OAuth User-Agent with the chat/usage CLI version to avoid WAF false positives (#12702) ([#13264](https://github.com/diegosouzapw/OmniRoute/pull/13264)) +- fix(dashboard): surface an authentication-required banner instead of silently blanking database settings for a guest session (#12709) ([#13270](https://github.com/diegosouzapw/OmniRoute/pull/13270)) +- **fix(resilience):** stop unbounded queue that hangs 6min until Aborted — gate, provider slot, and Bottleneck queue now share a per-connection `maxWaitMs` budget; `fail-closed` on exhaust (503); execution backstop `executionMaxWaitMs` overridable per connection with upstream clamp ([#12715](https://github.com/diegosouzapw/OmniRoute/pull/12715)) — thanks @maxmad64bis (with thanks to @Tushar49 for surfacing the slow-provider need in #12635) +- **fix(auto-combo):** every mode pack now carries `quality` and `reliability` weights (`quality` 0.02, 0.03 in `quality-first`; `reliability` 0.03, 0.04 in `reliability-first`), so selecting a pack no longer silences either signal; `DEFAULT_WEIGHTS` is unchanged ([#12731](https://github.com/diegosouzapw/OmniRoute/pull/12731)) — thanks @maxmad64bis +- **fix(sse):** the provider execution pipeline keeps the upstream error `code`/`type` again — both were lost when the non-streaming leg was extracted, so a config-class `422` (Antigravity missing project) degraded into a generic account cooldown instead of reaching the gate that recognises it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(db):** a legacy `call_logs` table no longer aborts startup — the provider-stats index is created after the column healing runs, not before it, so an install predating `request_type` opens instead of failing with `no such column` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(catalog):** a malformed row in the operator-writable custom-models blob no longer kills every `auto/*` pool ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(tests):** realign the guards that the same day's merges left asserting the old behavior — the provider-count total after GreenPT and EURouter, the GLM stream buffer slot that `#12925` turned into a declared parameter, and the injection scan bound that `#13104` reshaped from a head-only window into head-plus-tail ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- fix(cache): fold tool_choice/tools/response_format into the semantic cache signature so a cached tool_calls response can no longer be replayed for a request whose tool policy forbids it (#12734) ([#13267](https://github.com/diegosouzapw/OmniRoute/pull/13267)) +- **fix(models):** stop treating provider-supplied `isFree:true`/`:free`/`0/0` flags as trusted unless the provider has a documented free tier — only the shipped free-tier catalog decides otherwise; fetched `isFree:true` trusted only for a free-tier provider, custom `isFree:true` via a trusted path ([#12744](https://github.com/diegosouzapw/OmniRoute/pull/12744)) — thanks @maxmad64bis +- fix(memory): authenticate the internal /v1/rerank loopback call so memory reranking no longer silently degrades to unranked order when REQUIRE_API_KEY=true (#12745) ([#13268](https://github.com/diegosouzapw/OmniRoute/pull/13268)) +- fix(sse): parse Ollama Cloud's current usage markup (`$X of $Y used` aria-label, nested width style) (#12749) ([#13260](https://github.com/diegosouzapw/OmniRoute/pull/13260)) +- fix(cli): setup-opencode no longer sends an active context's management token to `/v1/models` when `--api-key`/`OMNIROUTE_API_KEY` is supplied — an explicit flag or the env var now always outranks the context's token, and the flag itself is no longer swallowed by the parent program's global `--api-key` option (#12783) ([#13246](https://github.com/diegosouzapw/OmniRoute/pull/13246)) +- fix(sse): register Arcee AI in the executor provider registry so requests reach api.arcee.ai instead of silently falling back to OpenAI (#12784) ([#13277](https://github.com/diegosouzapw/OmniRoute/pull/13277)) +- **fix(quota):** share one upstream quota read when identical saturation checks arrive at the same time, so traffic bursts don't multiply provider API calls ([#12787](https://github.com/diegosouzapw/OmniRoute/pull/12787)) — thanks @maxmad64bis +- **fix(db):** ignore expired or invalid rate-limit cooldown writes so a stale timestamp can't lock a connection that should be usable — clearing still works as before ([#12788](https://github.com/diegosouzapw/OmniRoute/pull/12788)) — thanks @maxmad64bis +- **fix(providers):** single source for provider order with `xao` ranking alongside `xai-oauth` ([#12790](https://github.com/diegosouzapw/OmniRoute/pull/12790)) — thanks @maxmad64bis +- **fix(routing):** off-table models check the free-model catalog before inheriting premium prices, latency bootstraps from the observed pool median, async tiers read live database pricing with a 90-day freshness gate, and the tier cache invalidates on every pricing write ([#12792](https://github.com/diegosouzapw/OmniRoute/pull/12792)) — thanks @maxmad64bis +- **fix(auto-combo):** snapshot scoring uses the observed per-connection account tier and per-model quality instead of neutral constants, and circuit-open providers rank lower at build time ([#12794](https://github.com/diegosouzapw/OmniRoute/pull/12794)) — thanks @maxmad64bis +- **fix(pool):** empty `auto/*` pools now say why they are empty, and the models listing uses the same paid check as routing ([#12795](https://github.com/diegosouzapw/OmniRoute/pull/12795)) — thanks @maxmad64bis +- fix(routing): recognize CLIProxyAPI's 'unknown provider for model' 400 as fallback-worthy (#12800) ([#13284](https://github.com/diegosouzapw/OmniRoute/pull/13284)) +- **fix(sse):** translate-mode streams now emit the estimated token counts as a trailing usage-only chunk before `[DONE]` when the upstream stays silent, so metered chat clients see totals instead of nothing ([#12828](https://github.com/diegosouzapw/OmniRoute/pull/12828)) — thanks @maxmad64bis +- **fix(db):** provider stats stay truthful on empty and legacy databases: fallback counts default to `0` instead of `null`, latency averages read `null` (not `0`) when no durations were recorded, failures logged before the error-type column existed group under `pre_migration` instead of `unclassified`, and per-provider queries use two new composite indexes ([#12832](https://github.com/diegosouzapw/OmniRoute/pull/12832)) — thanks @maxmad64bis +- fix(nvidia): fail open when a synced model catalog goes stale instead of gating forever (#12849) ([#13248](https://github.com/diegosouzapw/OmniRoute/pull/13248)) +- **fix(db):** WAL maintenance lives in its own module: a `TRUNCATE` checkpoint that hits a busy database now warns with its streak and retries once via `PASSIVE` instead of logging success, and closing-time checkpoints no longer report success on builds without a database file, with the busy totals visible in the authenticated monitoring health payload ([#12853](https://github.com/diegosouzapw/OmniRoute/pull/12853)) — thanks @maxmad64bis +- **fix(health):** quota with no snapshots reads empty instead of a contradicting 0% ([#12857](https://github.com/diegosouzapw/OmniRoute/pull/12857)) — thanks @maxmad64bis +- fix(a2a): accept the dashboard's own session cookie on /a2a so "Run message/send" no longer fails with "Unauthorized: missing or invalid API key" (#12888) ([#13271](https://github.com/diegosouzapw/OmniRoute/pull/13271)) +- **fix(logs):** the Logs grid's in-memory filter pass no longer discards rows the SQL query already matched — selecting an API key from the dropdown (which sends the key's id) returns its calls again, the Combo tab shows every combo instead of only those whose name contains a "1", and the model filter and search cover the same columns as the query ([#12896](https://github.com/diegosouzapw/OmniRoute/pull/12896)) — fixes [#12873](https://github.com/diegosouzapw/OmniRoute/issues/12873) — thanks @ntdatt812 +- **fix(a2a):** `/api/a2a/status` now builds the agent card from the request that asked for it, so a gateway reached at a non-localhost host no longer advertises `http://localhost:20128` as its A2A URL ([#12918](https://github.com/diegosouzapw/OmniRoute/pull/12918)). — thanks @ntdatt812 +- **fix(compression):** progressive aging now appends its `[COMPRESSED:aging:…]` annotation after a turn's `tool_result` blocks instead of in front of them, so Anthropic no longer rejects aged conversations with "`tool_use` ids were found without `tool_result` blocks immediately after" ([#12920](https://github.com/diegosouzapw/OmniRoute/pull/12920)). — thanks @ntdatt812 +- **fix(bedrock):** model import now resolves context limits for every vendor prefix instead of only `anthropic.*`, so `global.openai.gpt-5.6-*` no longer imports with a null `inputTokenLimit` and gets rejected pre-flight at the 200k default ([#12921](https://github.com/diegosouzapw/OmniRoute/pull/12921)). — thanks @ntdatt812 +- **fix(stream):** the 64 KB stream buffer GLM asks for is honoured instead of dropped, and the type error it caused no longer fails the API Route Typecheck gate on every open PR ([#12925](https://github.com/diegosouzapw/OmniRoute/pull/12925)) — thanks @ntdatt812 +- **fix(guardrails):** mask PII inside a `tool_result`'s nested content array, which the masker walked past while redacting its sibling block ([#12930](https://github.com/diegosouzapw/OmniRoute/pull/12930)) — thanks @ntdatt812 +- **fix(dashboard):** the Radar catalog table no longer leaves absent data unexplained — empty limits, unknown context windows, and unreported capabilities each explain themselves on hover, and a new check keeps it that way ([#12937](https://github.com/diegosouzapw/OmniRoute/pull/12937)) — thanks @maxmad64bis +- **fix(sse):** geo-blocked opencode requests rotate to the next account proxy instead of failing, so one refused egress no longer aborts the whole chain ([#12941](https://github.com/diegosouzapw/OmniRoute/pull/12941)) — thanks @maxmad64bis +- **fix(codex):** dashboard "clear cooldown" and the CAS recovery path now drop nested `codexScopeRateLimitedUntil` maps in the same write that nulls `rate_limited_until`, and fresh quota snapshots with headroom lift fallback-sourced scope cooldowns parked by quota preflight ([#12817](https://github.com/diegosouzapw/OmniRoute/issues/12817), [#12860](https://github.com/diegosouzapw/OmniRoute/issues/12860), [#12951](https://github.com/diegosouzapw/OmniRoute/pull/12951)) The dashboard reset-credit button (`consumeCodexResetCredit`) now exercises that same snapshot path after a successful redeem, so a filled quota bar is enough to unpark a leftover fallback Codex child without another manual clear. — thanks @HouMinXi +- **fix(db):** resolve `sql-wasm.wasm` across global npm install and hoisted layouts, ensuring OmniRoute can boot cleanly on Node 24 when native `better-sqlite3` is uncompiled. ([#13035](https://github.com/diegosouzapw/OmniRoute/pull/13035)) — thanks @HouMinXi +- **fix(api):** `DELETE /v1/batches/delete-completed` now sweeps only the calling API key's own completed batches (batches with no owner stay out of a key-scoped sweep on purpose), with an explicit instance-wide mode reserved for authenticated dashboard sessions, a 401 for a presented key that is unknown, revoked, deactivated, banned or expired (never falling through to the session branch), audit logging of every sweep, a sanitized 500 on failure, an owner-scoped file half (a key-scoped sweep never nulls a file another tenant owns) and an atomic sweep — chunked in 200-batch transactions in instance mode — so a mid-way error never leaves a batch pointing at a nulled file (GHSA-wvxc-jp3v-5mg5) ([#12969](https://github.com/diegosouzapw/OmniRoute/pull/12969)) +- **fix(combo):** quota-weighted routing stops drawing on an out-of-credit connection — a 402 now invalidates the stored quota snapshot instead of leaving its stale remaining percentage in place, and a snapshot older than 10 minutes no longer counts as confident headroom for the primary pool ([#12972](https://github.com/diegosouzapw/OmniRoute/pull/12972)) ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- **fix(vision):** Custom Models with "Vision capable" checked no longer have image requests swapped to `glm/glm-4.6v` when the client sends the advertised alias (`vllm/path/...`) or the bare path-shaped id — Vision Bridge now matches the stored override for all three id forms ([#12758](https://github.com/diegosouzapw/OmniRoute/issues/12758)) ([#12974](https://github.com/diegosouzapw/OmniRoute/pull/12974)) — thanks @HouMinXi +- **fix(sse):** transient opencode upstream failures rotate to the next account proxy instead of failing, so one flapping egress no longer aborts the whole chain ([#12975](https://github.com/diegosouzapw/OmniRoute/pull/12975)) — thanks @maxmad64bis +- **fix(azure):** Deployments from GPT-6 onward now send `max_completion_tokens` instead of `max_tokens`, which Azure rejects with HTTP 400. The rule matched a literal `gpt-5`, so each new generation arrived broken; it now matches the generation range, while `gpt-35-turbo` still keeps `max_tokens`. ([#13007](https://github.com/diegosouzapw/OmniRoute/pull/13007)) — thanks @ntdatt812 +- **fix(db):** add an opt-in self-restart circuit for sustained critical memory pressure, gate post-cleanup VACUUM behind a minimum freed-rows threshold, and checkpoint the SQLite WAL every 5 minutes with a size guard that escalates to TRUNCATE, so a growing WAL can no longer stall the event loop into a full outage. ([#13011](https://github.com/diegosouzapw/OmniRoute/pull/13011)) — thanks @HouMinXi +- **fix(auth):** an explicit connection pin may probe a quota-disabled row once and re-enable it on success ([#12874](https://github.com/diegosouzapw/OmniRoute/issues/12874)) ([#13017](https://github.com/diegosouzapw/OmniRoute/pull/13017)) — thanks @HouMinXi +- **fix(api):** thread `X-OmniRoute-Fallback-Attempts` through combo chat completions so streaming and non-streaming responses report how many prior legs were attempted ([#13038](https://github.com/diegosouzapw/OmniRoute/pull/13038)) — thanks @HouMinXi +- Restore provider failure classification and credential refresh on non-streaming requests: classify non-2xx failures to lock models on per-model quota exhaustion, update connection rate limits from headers and body, and pass credential refresh handlers to pipeline execution so 401 tokens can be refreshed and retried (#13043). ([#13069](https://github.com/diegosouzapw/OmniRoute/pull/13069)) — thanks @HouMinXi +- **fix(responses):** wrap forced-non-streaming web_search fallback JSON as Responses SSE so Codex still sees `response.completed` ([#13050](https://github.com/diegosouzapw/OmniRoute/pull/13050)) — thanks @HouMinXi +- **fix(antigravity):** Preserve upstream thought-token usage in normalized completion and reasoning token counts ([#13055](https://github.com/diegosouzapw/OmniRoute/pull/13055)) — thanks @pacocartones +- Fix a concurrency-slot leak in the DeepSeek PoW solver: a worker that failed to spawn (for example a missing worker script) never released its slot, so `MAX_CONCURRENT_WORKERS` failures disabled the solver until restart. ([#13097](https://github.com/diegosouzapw/OmniRoute/pull/13097)) — thanks @anhtahaylove +- **fix(acp):** bound the ACP session output buffers — `stdoutBuffer` and `stderrBuffer` now cap at 1 MiB keeping the most recent output behind a visible `[...output truncated...]` marker, and `stderrBuffer` is reset per prompt instead of accumulating for the lifetime of the session. ([#13100](https://github.com/diegosouzapw/OmniRoute/pull/13100)) — thanks @anhtahaylove +- **fix(acp):** release the `stdout`/`exit` listeners and the idle timer that a `sendPrompt` timeout used to leave attached to the `acpManager` singleton, and drop sessions that exited on their own from the session map instead of keeping them forever. ([#13096](https://github.com/diegosouzapw/OmniRoute/pull/13096)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection and PII scanners now read the text a `tool_result` block carries on `content` (string or nested block list), in messages and in system blocks, so tool output is judged by the same rules as user text ([#13101](https://github.com/diegosouzapw/OmniRoute/pull/13101)) — thanks @ntdatt812 +- **fix(gamification):** close the badge notification SSE stream when the request signal is already aborted before the stream starts — a client that disconnects while the route is still awaiting auth used to leave both the 2s unlock poll and the 15s heartbeat running for the lifetime of the process. ([#13106](https://github.com/diegosouzapw/OmniRoute/pull/13106)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection scan now spends its 16 KB budget on both ends of the request instead of the first 16 KB only, so `system`, `instructions`, `query`, `documents` and the newest turns are no longer hidden behind one long message ([#13104](https://github.com/diegosouzapw/OmniRoute/pull/13104)) — thanks @ntdatt812 +- **fix(dashboard):** expose the Volcano Ark console cookie on quota scraping and unwrap connect-error objects so the dashboard shows the upstream message ([#13107](https://github.com/diegosouzapw/OmniRoute/pull/13107)) — thanks @HouMinXi +- **fix(db):** release the `beforeExit`/`SIGINT`/`SIGTERM` handlers when a `node:sqlite` adapter closes, so a closed adapter and its database handle are no longer pinned to `process` for the lifetime of the run — the same treatment #7494 gave the sql.js adapter. ([#13109](https://github.com/diegosouzapw/OmniRoute/pull/13109)) — thanks @anhtahaylove +- **fix(translator):** `contentSchema` and `unevaluatedItems` are now treated as subschema positions by the tool-schema sanitizer, so a truncation placeholder in either is replaced with a permissive schema instead of being forwarded as a string ([#13110](https://github.com/diegosouzapw/OmniRoute/pull/13110)) — thanks @ntdatt812 +- **fix(cli-helper):** clear the `createLogStream` timeout on the abort path — `stop()` aborts the in-flight fetch and returned through the `signal.aborted` branch, which skipped `clearTimeout` and left an armed timer per stopped stream. The stream reader is now also cancelled when the read loop exits early. ([#13114](https://github.com/diegosouzapw/OmniRoute/pull/13114)) — thanks @anhtahaylove +- **fix(docs):** drop TLS-impersonation, MITM-decrypt, supply-chain attestation, and XOR-mask writeups from the public `/docs` catalog and Docker image. Files stay in git for engineers; operators who need them open the repo, not the website. ([#13136](https://github.com/diegosouzapw/OmniRoute/pull/13136)) — thanks @HouMinXi +- **fix(combo):** parse numeric-epoch `rate_limited_until` in the combo cooldown read path ([#13141](https://github.com/diegosouzapw/OmniRoute/pull/13141)) — thanks @maxmad64bis +- **fix(opencode):** both OpenCode plugins now reject a gateway address typed without `http://` at configuration time, instead of publishing every model with an api url no client can call, and the v2 plugin no longer publishes a model card whose api url is blank or relative ([#13142](https://github.com/diegosouzapw/OmniRoute/pull/13142)) — thanks @maxmad64bis +- **fix(providers):** lock opencode model on upstream 400 model-unavailable ([#13146](https://github.com/diegosouzapw/OmniRoute/pull/13146)) — thanks @maxmad64bis +- **fix(logging):** keep the provider exchange rather than the raw client bodies when a call log exceeds its size budget, and show that recovered payload in the request-detail panel instead of replacing it with the stored response body ([#13147](https://github.com/diegosouzapw/OmniRoute/pull/13147)) — thanks @maxmad64bis +- **fix(traffic-inspector):** the WebSocket route no longer leaks a traffic-buffer subscriber and a 30s ping timer when the client socket is already closed at handler time — listeners are attached before any resource is acquired, a destroyed socket bails out early, and the ping interval stops on a dead socket where `write()` never throws ([#13155](https://github.com/diegosouzapw/OmniRoute/pull/13155)) — thanks @anhtahaylove +- **fix(telegram):** bound the per-user API key cache in the Telegram chat proxy so a burst of distinct chat ids can no longer grow the process heap without limit ([#13165](https://github.com/diegosouzapw/OmniRoute/issues/13165)) ([#13166](https://github.com/diegosouzapw/OmniRoute/pull/13166)) — thanks @anhtahaylove +- **fix(stream):** cancel the upstream response body when the JSON-to-SSE sniff unwinds on a body timeout, so a stalled upstream no longer pins the connection ([#13169](https://github.com/diegosouzapw/OmniRoute/issues/13169)) ([#13171](https://github.com/diegosouzapw/OmniRoute/pull/13171)) — thanks @anhtahaylove +- **fix(telegram):** authenticate webhook deliveries with Telegram's `secret_token` so an unauthenticated caller can no longer mint API keys or spend upstream quota ([#13172](https://github.com/diegosouzapw/OmniRoute/issues/13172)) ([#13175](https://github.com/diegosouzapw/OmniRoute/pull/13175)) — thanks @anhtahaylove +- **fix(models):** give discoverable Gemini 3.8 Flash ids their own 65536 output spec so Antigravity no longer clamps them to 16384 ([#13195](https://github.com/diegosouzapw/OmniRoute/pull/13195)) — thanks @HouMinXi +- **fix(dashboard):** leftover catalog-removed provider rows (gemini-cli) can be listed and purged from the providers page ([#13067](https://github.com/diegosouzapw/OmniRoute/issues/13067)) ([#13197](https://github.com/diegosouzapw/OmniRoute/pull/13197)) — thanks @HouMinXi +- **fix(auth):** a dashboard session now requires the `authenticated: true` claim that login, OIDC and the session refresh already emit — a JWT merely signed with `JWT_SECRET` (for example the Cursor CLI passthrough token, which any API-key holder can obtain) no longer verifies as the `auth_token` cookie on any route, the WebSocket handshake or the live server; existing sessions keep working ([#13298](https://github.com/diegosouzapw/OmniRoute/issues/13298)) ([#13375](https://github.com/diegosouzapw/OmniRoute/pull/13375)) +- **fix(api):** `DELETE /v1/batches/delete-completed` now applies the caller's API-key policy (endpoint allowlist, schedule, usage cap, rate limit) like every other `/v1` route, commits the key-scoped sweep per 200-batch chunk instead of holding one write lock for the whole sweep, refuses to loop without progress, and rejects a scope that names both a key and `allTenants` ([#13684](https://github.com/diegosouzapw/OmniRoute/pull/13684)) +- Restore the API-route typecheck gate: the non-streaming leg result lost its discriminated-union narrowing after the server-owned tool loop reassignment, producing 13 new TS2339 diagnostics in `chatCore.ts`. ([#12963](https://github.com/diegosouzapw/OmniRoute/pull/12963)) +- Restore four non-streaming behaviours the server-owned tool loop refactor dropped: client aborts map to 499 with the fixed `Request aborted` message, an aborted request no longer logs a synthetic `clientResponse`, Claude prompt-cache telemetry is recorded again, and a body that cannot be canonicalized no longer throws when the tool loop is off. ([#12990](https://github.com/diegosouzapw/OmniRoute/pull/12990)) +- **fix(skills):** The CLI registry parser now reads positionals declared with `.addArgument()`, not only those written inline in `.command()`. `tunnel create [type]` was being published as `tunnel create`, so the agent-skills sync gate reported drift on every branch and regenerating would have deleted the argument. ([#13009](https://github.com/diegosouzapw/OmniRoute/pull/13009)) — thanks @ntdatt812 +- **fix(combos):** dashboard combo test uses a short prompt, serial probes, and a 60s timeout so reasoning models and rate-limited free pools do not fail the health check ([#13001](https://github.com/diegosouzapw/OmniRoute/pull/13001)) — thanks @HouMinXi +- fix(compression): terminate idle worker threads on eviction so long-running instances stop leaking OS threads and MessagePorts ([#13091](https://github.com/diegosouzapw/OmniRoute/pull/13091)) — thanks @anhtahaylove +- Redact Google API keys of any length in error bodies: the pattern required exactly 39 characters, so shorter or longer `AIza…` credentials passed through unredacted. ([#12964](https://github.com/diegosouzapw/OmniRoute/pull/12964)) +- **fix(grok-cli):** a 402 "Grok Build usage balance exhausted" parks that Grok login as out of credit (Grok Build CLI, grok.com cookie, and xAI OAuth share the weekly pool). Combo routing then tries the next login instead of locking the model for every account in the pool ([#13061](https://github.com/diegosouzapw/OmniRoute/pull/13061)) — thanks @HouMinXi +- fix(compression): spawn the LLMLingua worker with a file URL object so compression actually runs instead of silently failing open on Node ([#13093](https://github.com/diegosouzapw/OmniRoute/pull/13093)) — thanks @anhtahaylove +- **fix(test):** run the local `test` and `test:unit` scripts at concurrency 4 so a full-suite run no longer exhausts the machine's commit charge and kills unrelated processes ([#13187](https://github.com/diegosouzapw/OmniRoute/pull/13187)) — thanks @anhtahaylove +- **fix(monitoring):** `GET /api/monitoring/health` `credentialHealth` now includes a bounded `failedConnections` list (`connectionId`, `status`, sanitized `lastError`) when the probe-cache gauge `failed>0`, plus `source: probe-cache` and a cheap `staleDbNonOkCount` for sticky SQLite `test_status` on active rows. Documents that the live gauge is not `provider_connections.test_status`. ([#12876](https://github.com/diegosouzapw/OmniRoute/pull/12876)) — thanks @RaviTharuma +- **fix(resilience):** an openai-compatible multi-upstream gateway no longer marks the whole connection `credits_exhausted` when a single upstream (or the `/models` / representative-model health probe) returns 402. Quota failures stay model-scoped; true connection-wide auth failures are unchanged. Single-credential 402 key disable ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239)) is preserved. ([#12875](https://github.com/diegosouzapw/OmniRoute/pull/12875)) — thanks @RaviTharuma +- **fix(dashboard):** nine fixes on the Orchestration Canvas, all diagnosed in the Phase 2 reviews. A source that fails now keeps the timestamp of its FIRST failure instead of being re-stamped every poll — the stale line said "since the last poll" no matter how long the source had been down, and the churn also defeated the snapshot's stable identity (it serializes the source list), so the canvas re-rendered on every tick while anything was broken. A source that HAD data and only then started failing is flagged too: previously only a source with no node at all got the warning, so a source that went down mid-session kept a healthy-looking node forever. The rest are pointwise: clicking a filter chip cancels the pending search debounce (left armed, it fired ~300ms later and silently reverted the chip); the search input carries an accessible name; `?state=running, failed` parses like the unpadded form instead of dropping the padded value; the CSV toggle helper is defined once in `model/urlParams.ts` rather than twice; the agents tab tells "nothing running" apart from "the filter matched nothing", offering a clear-filters button instead of setup links that would be wrong advice there; edges stop emitting SMIL particles above 40 simultaneously active edges, keeping the colored stroke; and the drawer's error banner clears when a retried action succeeds. ([#12983](https://github.com/diegosouzapw/OmniRoute/pull/12983)) +- **fix(i18n):** the nine locales added with the EU-language batch (Greek, Estonian, Irish, Croatian, Lithuanian, Latvian, Maltese, Slovenian, Serbian) were missing the eleven Orchestration Canvas keys that Phase 3 introduced, so the compare-runs panel and the "no runs match these filters" empty state fell back to English in those languages (`deepMergeFallback` substitutes English for an absent key, so nothing rendered blank — it rendered untranslated). The coverage gate does not catch this: it enforces an 80% floor per locale, and eleven missing keys out of ~13,000 leaves coverage at 99.9%. Translated for real in each language, calibrated against the wording each file already uses for "run", "filter" and "skill". ([#13294](https://github.com/diegosouzapw/OmniRoute/pull/13294)) +- **fix(dashboard):** five follow-ups from the Phase 2 Orchestration Canvas review. The History drawer now shows the memory section for runs that already left the live TTL window: the persisted `memory_hits` event is parsed into `metadata.memoryHits` with the same defensive validation the drawer applies, and — because that event is observability rather than a state transition — it no longer leaks into the timeline, where it had been inheriting the task's state and rendering as a duplicate transition. The A2A memory recall runs against its own 1.5s deadline instead of inheriting the memory backend's 30s one; overshooting degrades exactly like any other recall failure (no hits, task proceeds), and the timer is cleared on both paths. Repeating a Conductor run carries its `requirements.cli`/`requirements.model` forward, so the new run is pinned to the same runner profile and model rather than drifting to whatever the fleet picks. A successful repeat from the Agents tab now focuses the run it created, instead of leaving the operator on the finished one. And the auth test for `POST /api/conductor/tasks` moved into the shared `ROUTES` array rather than restating the pattern. ([#12988](https://github.com/diegosouzapw/OmniRoute/pull/12988)) +- fix(plugins): stop leaking an exit listener per plugin hook timeout, which triggered MaxListenersExceededWarning on plugins that ignore SIGTERM ([#13092](https://github.com/diegosouzapw/OmniRoute/pull/13092)) — thanks @anhtahaylove +- **fix(validation):** Provider node edits no longer fail with a generic "Invalid request" when the optional daily-quota reset fields are left blank. The dashboard sends `dailyQuotaResetTimezone` and `dailyQuotaResetHour` as `null`, and only the hour accepted it. ([#13066](https://github.com/diegosouzapw/OmniRoute/issues/13066)) ([#13083](https://github.com/diegosouzapw/OmniRoute/pull/13083)) — thanks @ntdatt812 +- **fix(combo):** quota-aware expansion drops banned, inactive, missing, and wrong-provider connections before quota fetch or model dispatch; pins and allowlists stay selectors, not a bypass. Antigravity automatic exhaustion now requires a reported zero remaining, so a positive balance below 1% stays eligible. ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- Fix an intermittent failure in the quota-weighted routing test suite: scores are a function of `Date.now()`, so peers with identical quota scored microseconds apart never tied and swapped order. ([#12946](https://github.com/diegosouzapw/OmniRoute/pull/12946)) +- fix(volcengine): resolve quota window mapping gaps, safe multi-connection console binding, and response cookie sanitization ([#12950](https://github.com/diegosouzapw/OmniRoute/pull/12950)) — thanks @HouMinXi +- **fix(test):** resolve the WebDAV handler path with `fileURLToPath` so the suite's 37 WebDAV tests run on Windows instead of failing with a doubled `C:\C:\` drive prefix ([#13196](https://github.com/diegosouzapw/OmniRoute/pull/13196)) — thanks @anhtahaylove +- **fix(ui):** fall back from invalid AnySearch icon alias (direct commit `9587e07b69`) +- **fix(sse):** stop rebuilding a truncated summary from the collector's cap-dropped event array ([#12718](https://github.com/diegosouzapw/OmniRoute/pull/12718)) — thanks @hartmark +- **fix(conversations):** show a pending spinner for unresolved tool nodes instead of "(empty)" ([#12727](https://github.com/diegosouzapw/OmniRoute/pull/12727)) — thanks @hartmark +- **fix(streaming):** fail fast when an upstream stream produces only lifecycle/heartbeat events, never real content ([#12741](https://github.com/diegosouzapw/OmniRoute/pull/12741)) — thanks @hartmark +- **fix(responses-continuation):** bridge the write-in-flight window with an in-memory pending store ([#12854](https://github.com/diegosouzapw/OmniRoute/pull/12854)) — thanks @hartmark +- **fix(translator):** drop or map agent_message on Chat Completions fallback ([#12880](https://github.com/diegosouzapw/OmniRoute/pull/12880)) — thanks @RaviTharuma +- **fix(api):** page and stream-complete GET /v1/models for large catalogs ([#12882](https://github.com/diegosouzapw/OmniRoute/pull/12882)) — thanks @RaviTharuma +- **fix(quota):** align AUTH window labels with usage API and clarify 503 ([#12884](https://github.com/diegosouzapw/OmniRoute/pull/12884)) — thanks @RaviTharuma +- **fix(ci):** clear two base-reds on release/v3.8.51 — mutation-coverage gate + image-only-model guard ([#12945](https://github.com/diegosouzapw/OmniRoute/pull/12945)) +- **fix(api):** scope batch bulk-delete to the calling API key ([#13211](https://github.com/diegosouzapw/OmniRoute/pull/13211)) +- **fix(tests):** align model-sync log assertion to shipped path redaction (base-red) ([#13227](https://github.com/diegosouzapw/OmniRoute/pull/13227)) +- **fix(test):** make the ToS heading guard actually require the parentheses ([#13228](https://github.com/diegosouzapw/OmniRoute/pull/13228)) +- **fix(quality):** register 4 drifted covering tests in stryker tap.testFiles ([#13229](https://github.com/diegosouzapw/OmniRoute/pull/13229)) +- **fix(logging):** mask *-api-key headers in request-log pipeline (#13273) ([#13401](https://github.com/diegosouzapw/OmniRoute/pull/13401)) — thanks @KooshaPari +- **fix(tests):** correct stale Jina catalog prefix in custom-models test ([#13403](https://github.com/diegosouzapw/OmniRoute/pull/13403)) — thanks @KooshaPari +- **fix(backend):** prune old backups after health-check-repair snapshots (#13308) ([#13404](https://github.com/diegosouzapw/OmniRoute/pull/13404)) — thanks @KooshaPari +- **fix(backend):** guard memory_fts_au trigger with WHEN clause to stop FTS bloat (#13326) ([#13405](https://github.com/diegosouzapw/OmniRoute/pull/13405)) — thanks @KooshaPari +- **fix(free-tiers):** remove obsolete Together $25 signup credit (#12526) ([#13407](https://github.com/diegosouzapw/OmniRoute/pull/13407)) — thanks @KooshaPari +- **fix:** skip non-chat generation models in Test all models ([#13409](https://github.com/diegosouzapw/OmniRoute/pull/13409)) — thanks @KooshaPari +- **fix(providers):** de-duplicate agy and antigravity model catalogs ([#13410](https://github.com/diegosouzapw/OmniRoute/pull/13410)) — thanks @KooshaPari +- **fix(dashboard):** send provider in body for mini-playground web search ([#13411](https://github.com/diegosouzapw/OmniRoute/pull/13411)) — thanks @KooshaPari +- **fix(resilience):** add CJK quota-exhaustion patterns to 429 classifier ([#13412](https://github.com/diegosouzapw/OmniRoute/pull/13412)) — thanks @KooshaPari +- **fix(backend):** eval runner: failed calls no longer score as passed + dotAll regex ([#13413](https://github.com/diegosouzapw/OmniRoute/pull/13413)) — thanks @KooshaPari +- **fix(backend):** eval runner opts out of output-style and memory injection ([#13414](https://github.com/diegosouzapw/OmniRoute/pull/13414)) — thanks @KooshaPari +- **fix:** chain serial quarantine step in npm test script (fixes #13286) ([#13418](https://github.com/diegosouzapw/OmniRoute/pull/13418)) — thanks @KooshaPari +- **fix(antigravity):** derive stable session ID from credentials for prompt-cache reuse (#13305) ([#13424](https://github.com/diegosouzapw/OmniRoute/pull/13424)) — thanks @KooshaPari +- **fix(memory):** merge memory into top-level system field instead of unshifting at messages[0] (#13425) ([#13427](https://github.com/diegosouzapw/OmniRoute/pull/13427)) — thanks @KooshaPari +- **fix(compression):** preserve instruction blocks from lossy engines ([#13523](https://github.com/diegosouzapw/OmniRoute/pull/13523)) — thanks @KooshaPari +- **fix(compression):** preserve polarity words and newlines in ultra heuristic ([#13524](https://github.com/diegosouzapw/OmniRoute/pull/13524)) — thanks @KooshaPari +- **fix(compression):** run user preservePatterns before built-in patterns (#13457) ([#13525](https://github.com/diegosouzapw/OmniRoute/pull/13525)) — thanks @KooshaPari +- **fix(reasoning):** expand deepseek-v4 regex to match v4.1+ and add command-code provider (#13430) ([#13528](https://github.com/diegosouzapw/OmniRoute/pull/13528)) — thanks @KooshaPari +- **fix(providers):** add alwaysReasons metadata for reka-flash-3 ([#13531](https://github.com/diegosouzapw/OmniRoute/pull/13531)) — thanks @KooshaPari +- **fix(playground):** allow Compare columns to scroll independently ([#13532](https://github.com/diegosouzapw/OmniRoute/pull/13532)) — thanks @KooshaPari +- **fix(security):** empty whitelist allows through to prevent admin lockout ([#13534](https://github.com/diegosouzapw/OmniRoute/pull/13534)) — thanks @KooshaPari +- **fix(tests):** add DISABLE_IOREG_STRATEGY env var for macOS test compatibility (fixes #13467) ([#13539](https://github.com/diegosouzapw/OmniRoute/pull/13539)) — thanks @KooshaPari +- **fix(zed-hosted):** map developer role and Gemini enums for Zed proxy ([#13543](https://github.com/diegosouzapw/OmniRoute/pull/13543)) — thanks @KooshaPari +- **fix(analytics):** normalize provider aliases to canonical id in usage_history ([#13545](https://github.com/diegosouzapw/OmniRoute/pull/13545)) — thanks @KooshaPari +- **fix(combo):** use traceId for call log id to prevent UNIQUE constraint failure ([#13546](https://github.com/diegosouzapw/OmniRoute/pull/13546)) — thanks @KooshaPari +- **fix(auth):** check model lockout before returning synthetic noauth connection ([#13547](https://github.com/diegosouzapw/OmniRoute/pull/13547)) — thanks @KooshaPari +- **fix(guardrails):** skip credential redaction for base64 image data URLs ([#13550](https://github.com/diegosouzapw/OmniRoute/pull/13550)) — thanks @KooshaPari +- **fix(resilience):** increase requestQueue.maxWaitMs default from 15s to 30s ([#13553](https://github.com/diegosouzapw/OmniRoute/pull/13553)) — thanks @KooshaPari +- **fix(docs):** document OMNIROUTE_READY_TIMEOUT_MS and allowlist the test-only DISABLE_IOREG_STRATEGY ([#13692](https://github.com/diegosouzapw/OmniRoute/pull/13692)) + ### 📝 Maintenance ---- +- **docs(readme):** reconcile live v3.8.50 provider, free-tier, CLI, routing, test, community, sponsor, acknowledgment, and SVG metrics with their audited source denominators, including a deduplicated OmniRoute-in-Action snapshot and distinct contributor rankings for merged pull requests, GitHub-attributed commits, and Git history ([#11356](https://github.com/diegosouzapw/OmniRoute/pull/11356)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **docs:** sync the canonical provider count 354 → 356 across `README.md`, `AGENTS.md`, `llm.txt` (+ 42 i18n mirrors), the four README SVG diagrams, `docs/reference/PROVIDER_REFERENCE.md` (regenerated) and the `package.json` description after Opper (#11629) and 1min.ai (#11631) boarded the catalog — closes the `check:docs-counts-sync` strict drifts that kept `release/v3.8.51` red ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11673](https://github.com/diegosouzapw/OmniRoute/pull/11673)) — thanks @jonlwheat2-gif +- **test(providers):** update count-derived assertions after the v3.8.51 provider additions — `APIKEY_PROVIDERS` 233 → 235 (Opper #11629 + 1min.ai #11631), reserved-prefix REGISTRY walk 395 → 398, `WEB_FETCH_PROVIDERS` now includes `nimble-search` (#11620), and the provider translate-path golden snapshot regenerated ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11674](https://github.com/diegosouzapw/OmniRoute/pull/11674)) — thanks @jonlwheat2-gif +- **test(sse):** bump the hard-lease connection-query inventory for `src/lib/tokenHealthCheck.ts` to 2 — the verify-only web-cookie sweep added by #11495 split the single `getProviderConnections` call into oauth + cookie variants, which the frozen inventory had not tracked ([#11495](https://github.com/diegosouzapw/OmniRoute/pull/11495)). ([#11677](https://github.com/diegosouzapw/OmniRoute/pull/11677)) — thanks @jonlwheat2-gif / @oyi77 +- **chore(release):** clear the `release/v3.8.51` base-red gates — docs migration count (+38 i18n mirrors), stryker `tap.testFiles`, lockfile registry host, stale ESLint suppressions, the 7 lint regressions they were hiding, the stale `APIKEY_PROVIDERS` partition count (231 → 233), the `glm-5.3-max` tier missing from the GLM test table, a dead `modelType === "chat"` comparison in the models catalog, and the `resolveLiveWsUrl` / `sanitizeLiveWsPort` helpers from #11388 that a later merge dropped from `wsPath.ts` (dashboard live WebSocket URL resolution) ([#11502](https://github.com/diegosouzapw/OmniRoute/pull/11502)) — thanks @MumuTW +- **chore(free-models):** the free-tier regime table now declares which totals figure each regime feeds and whether it may take the no-auth shortcut, so the sets that used to repeat those answers by hand are derived from it — a new `freeType` no longer compiles until it has answered every question, instead of silently contributing to no total ([#11537](https://github.com/diegosouzapw/OmniRoute/pull/11537)) — thanks @maxmad64bis +- **docs(routing):** LKGP strategy copy now states provider-level stickiness and the explicit rules fallback instead of promising per-target pinning ([#11555](https://github.com/diegosouzapw/OmniRoute/pull/11555)) — thanks @maxmad64bis +- **test(chatcore):** pin the response tool-name alias resolution against the ordering hazard that broke every Gemini/Antigravity MCP tool call in v3.8.49. `extractRequestToolIdentityMap` deletes `translatedBody._toolNameMap`, so the later read is always undefined and the ledger survives only through the `requestToolIdentityMap` fallback — removing that fallback previously left the entire tool-name suite green. The resolution moves into `resolveResponseToolNameMap()` next to the map it depends on, with a regression guard that fails without the recovery. No behaviour change. ([#11575](https://github.com/diegosouzapw/OmniRoute/pull/11575)) — thanks @amartinawi +- **docs(env):** document `OMNIROUTE_CORPUS_CACHE_SIZE` in `.env.example` and `ENVIRONMENT.md`, so the env/docs contract check stops failing Docs Gates on every branch ([#11576](https://github.com/diegosouzapw/OmniRoute/pull/11576)) — thanks @ntdatt812 +- **chore(stryker):** register `route-guard-tunnel-processes-local-only` and `universal-quota-aware-routing` in `tap.testFiles`, so their mutant kills count for the three modules they cover ([#11577](https://github.com/diegosouzapw/OmniRoute/pull/11577)) — thanks @ntdatt812 +- **test(authz):** pin `LOCAL_ONLY_API_GET_EXEMPTIONS` by exact membership instead of by entry count, so the guard names the offending path and also catches a substitution ([#11580](https://github.com/diegosouzapw/OmniRoute/pull/11580)) — thanks @ntdatt812 +- **test(build):** pin `resolveNextBuildBundlerFlag` to the `OMNIROUTE_USE_TURBOPACK` contract it implements, across runtimes, instead of asserting an unimplemented Bun override ([#11583](https://github.com/diegosouzapw/OmniRoute/pull/11583)) — thanks @ntdatt812 +- **test(claude-web):** the Claude Web transport test's barrel anchors no longer pin the exact construction spelling. #11421 made the executor map lazy, so `/"claude-web": new ClaudeWebExecutor\(\)/` stopped matching and the test went red on `release/v3.8.51` itself — a base-red gate every open PR inherited. The anchors now match the alias key and the class name and tolerate either spelling in between, and were checked to still fail when an alias is genuinely repointed at another executor ([#11585](https://github.com/diegosouzapw/OmniRoute/pull/11585)) — thanks @ntdat812 +- **test(models):** pin `qwen3.8-max` resolution per provider — two of the four catalogs now serve the bare id and no longer carry `-preview` ([#11590](https://github.com/diegosouzapw/OmniRoute/pull/11590)) — thanks @ntdatt812 +- **test(executors):** drop the stale 128-tool truncation assertion that contradicted the #11444 pass-through contract ([#11591](https://github.com/diegosouzapw/OmniRoute/pull/11591)) — thanks @ntdatt812 +- **test(token-health):** pin the current terminal-skip boundary — expired with retry budget is probed, `account_deactivated` stays skipped ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)) — thanks @ntdatt812 +- **test(models):** follow #11503's retarget of the `gemini-3-pro-high` alias to the hyphenated catalog id ([#11593](https://github.com/diegosouzapw/OmniRoute/pull/11593)) — thanks @ntdatt812 +- **refactor(video bridge):** extract per-video acquisition, whole-result caching, description, and metrics/abort/cleanup out of `VideoBridgeGuardrail.preCall` into a `processVideoPart` seam in a new `videoBridgePipeline.ts`, behind explicit `VideoMediaBrokerPort`, `VideoAudioTranscriptionPort`, and `VideoDrilldownPort` boundaries; `preCall` now only handles request traversal, policy, and response aggregation. The Video tab's FFmpeg/ffprobe runtime status is now an explicit `unknown` / `restricted` / `unavailable` / `available` state instead of a nullable boolean pair, fixing a case where an in-flight or failed probe was mislabeled as "install FFmpeg" ([#11657](https://github.com/diegosouzapw/OmniRoute/issues/11657)). ([#12016](https://github.com/diegosouzapw/OmniRoute/pull/12016)) +- **test(opencode-plugin):** add unit test coverage for `formatFreeBudget()` naming helper ([#11660](https://github.com/diegosouzapw/OmniRoute/pull/11660)) — thanks @f9td56dbgh-hub +- **docs(video):** clarify that the Video Bridge transcript `source` field (`client`, `embedded`, `audio-bridge`) is presently caller-declared and not yet server-verified — OmniRoute enforces the enum shape but does not cryptographically confirm that an `embedded`/`audio-bridge` label came from a server-owned extraction ([#11661](https://github.com/diegosouzapw/OmniRoute/issues/11661)). ([#12001](https://github.com/diegosouzapw/OmniRoute/pull/12001)) +- **chore(providers):** retire the distributed Raycast Relay and Hailuo Web integrations after a provenance audit confirmed substantial derivation from GPL-3.0 sources; the official GitHub and MiniMax API providers remain available. Migration 163 disables legacy connections and invalidates their active leases while preserving connection IDs, audit/history rows, quota snapshots, usage records, and fail-closed API-key connection policies. ([#11691](https://github.com/diegosouzapw/OmniRoute/pull/11691)) +- **chore(providers):** retire the Felo Web provider while its GPL-derived provenance remains on licensing HOLD ([#11698](https://github.com/diegosouzapw/OmniRoute/pull/11698)) — stale runtime and database references now fail closed while existing connection, lease, and usage history remains preserved for auditability. +- **fix(sse):** retire the Gemini Web image-generation bridge while provenance and licensing remain on HOLD, preserving Gemini Web chat/auth/tools and legitimate Gemini/Nano Banana image providers ([#11708](https://github.com/diegosouzapw/OmniRoute/pull/11708)) +- **chore(audio):** retire the built-in EdgeTTS provider and its 11-voice catalog while provenance/licensing review remains on HOLD; the generic `/v1/audio/speech` endpoint and the other speech providers remain available ([#11711](https://github.com/diegosouzapw/OmniRoute/pull/11711)) +- **fix(providers):** retire the unofficial Qwen Web integration and its `qwen-web`/`qw` identities while third-party provenance remains on HOLD ([#11713](https://github.com/diegosouzapw/OmniRoute/pull/11713)) — direct requests now fail closed with a sanitized HTTP 410 and combos skip the retired target, while migration guards preserve connection IDs, allowlists, usage/audit history and invalidate active leases. Supported Qwen API, Qwen Code, cloud, token-plan, audio, local and model-name surfaces remain available; restoring Qwen Web requires a qualified provenance/license decision. +- **fix(providers):** retire `microsoft-designer-web` and its `msdesigner` alias from runtime and catalogs while preserving stored credentials, provider history and allowlists as inactive records — source dependency on gpt4free's [`MicrosoftDesigner.py`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/g4f/Provider/needs_auth/MicrosoftDesigner.py) is proven at snapshot `0943d16eef757a44deb60651c07bbe5fa958cafe`, whose [`LICENSE`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/LICENSE) is GPL-3.0; expressive adaptation is probable, which records provenance/licensing risk without asserting a legal conclusion ([#11720](https://github.com/diegosouzapw/OmniRoute/pull/11720)). +- **docs(legal):** centralize pinned MIT notices for the vendored GCF codec and local flag assets ([#11726](https://github.com/diegosouzapw/OmniRoute/pull/11726)) +- Split the 2,952-line `adobeFireflyClient.ts` into nine focused modules (catalog, credentials, payload, ARP session, upload, responses, IMS, account, poll) behind an unchanged barrel — no consumer edits and no public API change (#11728). — thanks @MumuTW +- **fix(dashboard):** replaced the Model Lockout card's unprovenanced notification MP3 with a short, locally synthesized Web Audio chime ([#11731](https://github.com/diegosouzapw/OmniRoute/pull/11731)) — both toggles retain optional audible feedback without shipping replacement media or third-party code, and unsupported, suspended, or rejected audio contexts fail open without blocking the setting change. +- **chore(deepseek-web):** remove the provenance-unresolved DeepSeek PoW WASM binary and its runtime loader/tracing while retaining the existing JavaScript solver (slower at high difficulty) ([#11732](https://github.com/diegosouzapw/OmniRoute/pull/11732)). +- **chore(assets):** remove four unused provider assets whose redistribution provenance could not be verified while retaining the active Freebuff SVG ([#11735](https://github.com/diegosouzapw/OmniRoute/pull/11735)) +- **docs(assets):** retired two raster images whose redistribution provenance could not be verified ([#11736](https://github.com/diegosouzapw/OmniRoute/pull/11736)) — PicoClaw documentation now reuses the existing SVG, while Zoo Code remains a text link without an image. +- **docs(legal):** add pinned notices for six LobeHub-derived provider SVGs and 65 byte-exact theSVG assets, retaining registry claims as unverified metadata with explicit trademark and HOLD boundaries ([#11737](https://github.com/diegosouzapw/OmniRoute/pull/11737)). +- **chore(quality):** added a versioned 225/225 provider-asset provenance manifest and blocking drift gate ([#11743](https://github.com/diegosouzapw/OmniRoute/pull/11743)) — the inventory pins magic MIME and SHA-256 for every physical file, records immutable source matches for 71 assets, keeps 69 probable and 85 unresolved entries explicit but non-blocking, and treats provenance as source evidence rather than copyright or trademark clearance. +- **refactor(mitm):** replace four previously derived Traffic Inspector utilities — conversation normalization, SSE merging, secret masking, and header sanitization — with independent clean-room implementations based on public provider schemas, WHATWG SSE, and RFC 6750, while keeping the two independently classified passthrough surfaces unchanged ([#11748](https://github.com/diegosouzapw/OmniRoute/pull/11748)). +- Added a unit test that fails seven days before any dated pack under `config/` (`validUntil` and sibling keys) lapses, naming the file and key. The Alibaba free-tier pack expired on 2026-08-27 and turned every PR red the next morning with no commit involved; renewal now happens on someone's terms, not the clock's. ([#11891](https://github.com/diegosouzapw/OmniRoute/pull/11891)) +- `check:workflows` now fails (under `--strict`/`--ratchet`) when any job routed to a self-hosted runner publishes with `--provenance` — npm rejects that with `422` at the registry, which in v3.8.50 only surfaced after the tag and Docker images were public. ([#11895](https://github.com/diegosouzapw/OmniRoute/pull/11895)) +- `scripts/ops/runner-janitor.sh` now proves a path is idle with one `lsof` snapshot and removes stale leftovers itself (tmpfs after 3 h — it is RAM — disk after 24 h), kills orphan `next-build` processes, prunes checkouts of stopped runners, and alerts on memory pressure; `--dry-run` shows exactly what it would do. `docs/ops/RUNNER_BOX.md` reconciled to the measured box (31 GB, 10 listeners). ([#11893](https://github.com/diegosouzapw/OmniRoute/pull/11893)) +- The `next-build` artefact (1.3 GB) is now written and read under `$RUNNER_TEMP` (per-runner, on disk) instead of `/tmp`, which on the self-hosted pool is a 12 GB tmpfs in RAM. Landing it there took 27–32 of the publish job's 76 minutes, and the fixed `/tmp/e2e-build.tar.gz` name let E2E jobs on different runners overwrite each other's download. ([#11896](https://github.com/diegosouzapw/OmniRoute/pull/11896)) +- The CI `build` job now runs in two concurrency lanes — `main` and pull requests — so a release build is never queued behind (or OOM-killed beside) PR builds on the self-hosted pool, which holds one `next-build` comfortably and two at the edge. ([#11901](https://github.com/diegosouzapw/OmniRoute/pull/11901)) +- Re-freeze the ESLint suppressions on `release/v3.8.51` from a clean-room measurement (2 stale file entries pruned, 55 pre-existing `no-explicit-any` in six new files frozen under #11924) and drop the dead `GPT_SIZE_MAP` constant orphaned by the Adobe Firefly client split, so `No new ESLint warnings` stops failing every PR with exit 2 (Refs #11924) ([#11955](https://github.com/diegosouzapw/OmniRoute/pull/11955)) +- Type the 55 `no-explicit-any` sites that had been frozen under #11924 — four redundant casts in `socksConnectorWithFamily.ts` (undici/socks types already accept them) and the mocks/fixtures of the socks-timeout and isFree suites — and drop their suppression entries; the ESLint ratchet shrinks from 5487 to 5432 (Closes #11924) ([#11975](https://github.com/diegosouzapw/OmniRoute/pull/11975)) +- Take the two hosted-runner builds off the PR rail: `Build App` (`build.yml`) is `workflow_dispatch`-only and `DAST smoke (PR)` runs only for PRs into `main` — the 7 GB hosted VM cannot build `release/v3.8.51` in any profile (19/30 red, VM shutdown ~8 min into `next build`) and both checks had turned into permanent noise on every release PR; the bundle stays validated by `ci.yml` on `main` and by `nightly-release-green` on `release/**` (Closes #11946) ([#11962](https://github.com/diegosouzapw/OmniRoute/pull/11962)) +- Move the four nightly jobs that build the backend (`nightly-schemathesis`, `nightly-llm-security` promptfoo + garak, `nightly-resilience` axe-a11y) off the hosted 7 GB runner — where they died on `release/v3.8.51` unseen — onto the box's new `omni-light` pool (two listeners), and document the reshaped fleet (4 active OmniRoute listeners: 2 `omni-build` + 2 `omni-light`, janitor ceiling 4) (Closes #11965) ([#11967](https://github.com/diegosouzapw/OmniRoute/pull/11967)) +- Stop painting every fork PR into `release/**` red: `quality.yml` `Build (advisory)` is skipped (GitHub still reports `continue-on-error` failures as check FAILURE). Hosted `ubuntu-latest` cannot finish `npm run build` on this tree — same class as #11962 taking `build.yml` off the PR rail. `docker-publish.yml` amd64 now runs on the `.113` `omni-build` pool (31 GB / 32 cores, two listeners) with Turbopack, shares the `heavy-build-main` lane with `ci.yml` `Build` so it queues instead of becoming a third heavy, and keeps per-ref concurrency (a merge storm was starting 8 concurrent OOM builds). arm64 stays on `ubuntu-24.04-arm` with webpack — there is no ARM box. Fallback when `USE_VPS_RUNNER` is off: hosted amd64 + webpack (#11976). ([#12021](https://github.com/diegosouzapw/OmniRoute/pull/12021)) +- `docker-publish.yml` builds with webpack (`OMNIROUTE_USE_TURBOPACK=0`) on **both** arches, including amd64 on the `.113` `omni-build` pool. Turbopack had the RAM (31 GB, `omniroute-113-6`) and still panicked (`TurbopackInternalError: there must be a path to a root`, run 33253576569, 26 min in). The same tree's arm64 webpack build on hosted `ubuntu-24.04-arm` succeeded (run 33264823398). Dockerfile already documents webpack as the Docker escape hatch (#11976). ([#12050](https://github.com/diegosouzapw/OmniRoute/pull/12050)) +- **docs(free-tier):** declare the counting vs deciding regimes for "is it free?" and guard the deciding path from DB-backed catalog resolution ([#12226](https://github.com/diegosouzapw/OmniRoute/pull/12226)) — thanks @maxmad64bis +- **docs(auto-combo):** The mode pack table in `docs/routing/AUTO-COMBO.md` now lists all six shipped packs with every weight each one sets, replacing a four-pack table whose numbers had also drifted from the source. It states plainly that no pack sets `quality`, so selecting any pack silences the observed-quality signal. Six more documents that quote the scoring factor count joined the `check:docs-counts` gate, which caught five stale claims — including one naming nine factors that do not exist — and two stale mode pack counts. The dashboard routing panel, which offered four of the six packs and labelled the default strategy "6-Factor Scoring", is now covered by a test; the two packs it was missing are `reliability-first` and `chaos-mode`, the latter labelled as the fault-injection profile it is rather than as one more routing preference ([#12316](https://github.com/diegosouzapw/OmniRoute/pull/12316)) — thanks @maxmad64bis +- **chore(quality):** rebaseline `src/lib/db/apiKeys.ts` for the ACL the key-creation path now preserves ([#12352](https://github.com/diegosouzapw/OmniRoute/pull/12352)) ([#12673](https://github.com/diegosouzapw/OmniRoute/pull/12673)) — thanks @KrzysiekSko +- **chore(providers):** bump the Claude Code wire identity and the Devin bridge image pin from `2.1.220` to `2.1.258` ([#12402](https://github.com/diegosouzapw/OmniRoute/pull/12402)) — thanks @ggiak ([#12604](https://github.com/diegosouzapw/OmniRoute/pull/12604)) +- **docs(env):** align `.env.example`, the README Bun section, and the troubleshooting guide with the code: `OMNIROUTE_USE_TURBOPACK` also governs `npm run build` (not dev-only), `bun run build` follows that flag instead of auto-selecting Webpack, `OMNIROUTE_CHAT_MAX_HEAVY_IN_FLIGHT` is unset by default (no request-count cap), and the structural `503 chat_admission_busy` message matches `chatAdmissionResponses.ts` (#12404 — thanks @pacocartones) +- **chore(electron):** upgrade the desktop app to Electron 44 (Chromium 152, Node 24.18.1) ([#12217](https://github.com/diegosouzapw/OmniRoute/pull/12217)). **Requires macOS 13 (Ventura) or later** — Chromium dropped macOS 12 (Monterey), so Monterey users must stay on an earlier OmniRoute desktop build. Windows and Linux are unaffected; the app already shipped only x64/arm64, so Electron 44 dropping 32-bit builds changes nothing. Removes the `openAsHidden`/`wasOpenedAsHidden` login-item fields deleted in Electron 44 — hidden autostart continues to work through the `--hidden` argument registered with the login item ([#12554](https://github.com/diegosouzapw/OmniRoute/pull/12554)) — thanks @app/dependabot +- **chore(quality):** rebaseline `src/sse/handlers/chat.ts` for the effective-input persistence the continuation fix needs ([#12641](https://github.com/diegosouzapw/OmniRoute/pull/12641)) ([#12680](https://github.com/diegosouzapw/OmniRoute/pull/12680)) — thanks @hartmark +- `Coverage` job on `ci.yml`: the informational Codecov upload gets its own 5-minute ceiling and `continue-on-error`, and the job budget grows from 20 to 30 minutes (the 8-shard c8 merge alone takes ~10) — a stalled upload no longer ends the job `cancelled` and drags a fully green `main` run's conclusion down with it ([#11972](https://github.com/diegosouzapw/OmniRoute/pull/11972)) +- CI hardening on the PR rail: the four `Unit Tests fast-path` shards get `timeout-minutes: 30` (a hung shard held a PR for 64 min instead of GitHub's 6 h default) and both ESLint file caches lose their `restore-keys` fallback, so a cache built under another suppressions file or lint config can no longer report stale verdicts (Refs #11600, #11924) ([#11963](https://github.com/diegosouzapw/OmniRoute/pull/11963)) +- Every CI job that runs a `next build` (`build`, the npm `publish`, both release-green validations) now targets the `omni-build` runner label, which only two of the eight self-hosted runners carry. The box holds one build comfortably and two at the edge; a third now queues on GitHub instead of being OOM-killed by the kernel. ([#11932](https://github.com/diegosouzapw/OmniRoute/pull/11932)) +- **refactor(combo):** move `handleRoundRobinCombo` (and `resolveTargetTokenLimit`) into `open-sse/services/combo/roundRobinCombo.ts`. `combo.ts` drops from 2164 to 1014 lines (`split("\n").length`); the leaf is 1199 (under the 1200 new-file cap). The round-robin call site uses a dynamic `import()` so `releaseStickyPinOnFailure` / `clearStaleLKGP` can stay exported from `combo.ts` without a static cycle. Skip / sticky / semaphore / safety-timer behavior is unchanged. ([#12811](https://github.com/diegosouzapw/OmniRoute/pull/12811)) — thanks @HouMinXi +- **chore(quality):** rebaseline the file-size caps the error-boundary campaign grew past (`open-sse/executors/codex.ts`, `open-sse/vendor/codex-chatgpt-web/bridge.ts`, both via [#12444](https://github.com/diegosouzapw/OmniRoute/pull/12444)) ([#12654](https://github.com/diegosouzapw/OmniRoute/pull/12654)) +- **chore(quality):** rebaseline the file-size caps the hartmark batch grew past (`combos/page.tsx` via [#12355](https://github.com/diegosouzapw/OmniRoute/pull/12355), `open-sse/services/combo.ts` via [#12338](https://github.com/diegosouzapw/OmniRoute/pull/12338)) ([#12623](https://github.com/diegosouzapw/OmniRoute/pull/12623)) — thanks @hartmark +- **chore(quality):** rebaseline the file-size caps the HouMinXi batch grew past when its PRs stacked (`providers/page.tsx`, `chatCore.ts`, `accountFallback.ts`) — each PR measured correctly in isolation, none saw the stacking ([#12619](https://github.com/diegosouzapw/OmniRoute/pull/12619)) +- **chore(quality):** rebaseline `open-sse/services/combo.ts` for the reset-aware scoring the HouMinXi batch stacked ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) ([#12678](https://github.com/diegosouzapw/OmniRoute/pull/12678)) — thanks @HouMinXi +- **docs:** sync provider catalog count (direct commit `4f11b2ae3d`) +- **docs(skills):** sync omni-inference search fallback (direct commit `b4e76a7ed9`) +- **chore(lint):** prune two stale base suppressions (direct commit `1325047d56`) +- **test(auto-combo):** tolerate full-suite rotation load (direct commit `ad3e293f9f`) +- **test:** drain inherited quality snapshot reds (direct commit `21ed68d8ac`) +- **chore(quality):** rebaseline measured CLI bundle growth (direct commit `d35b3f9779`) +- **test(ui):** reproduce invalid AnySearch icon alias (direct commit `12cc6ca834`) +- **test(translator):** fix the relative imports of the relocated deferred-finish test (direct commit `777d9d1629`) +- **docs(counts):** bring the documented provider and migration counts to the measured values (direct commit `e70bea30e9`) +- **test(security):** drop the guard-target set left unused by the sync (direct commit `a32ba0e362`) +- **chore(db):** renumber model_capabilities migration 163 → 169 (cross-branch collision with 163_radar_feed_cache_generated_at) (direct commit `70f5d4cbf9`) +- **docs(cli):** document CLI_PRIME_AGENT_BIN and correct the CLI Agents count (direct commit `9058e39b61`) +- **chore(deps):** refresh runtimes and adopt ESLint 10 ([#11259](https://github.com/diegosouzapw/OmniRoute/pull/11259)) — thanks @backryun +- **refactor(local-corpus):** implement dynamic root resolution and LRU cache ([#11491](https://github.com/diegosouzapw/OmniRoute/pull/11491)) — thanks @raheemuddin786 +- **chore(quality):** prune stale eslint-suppressions entries ([#11596](https://github.com/diegosouzapw/OmniRoute/pull/11596)) +- **chore(ci):** unblock quality gates - env-doc sync + stryker tap.testFiles registration (#9282) ([#11609](https://github.com/diegosouzapw/OmniRoute/pull/11609)) — thanks @Meet6338-X +- **chore(ci):** point the circular-deps gate at dpdm's real JS entrypoint ([#11615](https://github.com/diegosouzapw/OmniRoute/pull/11615)) — thanks @benzntech +- **chore(quality):** rebaseline file-size for #11448/#11495/#11561 growth ([#11630](https://github.com/diegosouzapw/OmniRoute/pull/11630)) +- **deps:** realign onnxruntime-node pin to @huggingface/transformers' 1.24.3 lockstep ([#11633](https://github.com/diegosouzapw/OmniRoute/pull/11633)) — thanks @jonlwheat2-gif +- **test(autoCombo):** port models_dev_tier #11508 guard from node:test to vitest ([#11635](https://github.com/diegosouzapw/OmniRoute/pull/11635)) — thanks @jonlwheat2-gif +- **chore(quality):** rebaseline file-size for #11083/#11631/#11628 growth ([#11636](https://github.com/diegosouzapw/OmniRoute/pull/11636)) +- **chore(lint):** prune stale ESLint suppressions (base-red #11449) ([#11644](https://github.com/diegosouzapw/OmniRoute/pull/11644)) — thanks @jonlwheat2-gif +- **docs(changelog):** aggregate the ten v3.8.50 fragments into CHANGELOG.md ([#11683](https://github.com/diegosouzapw/OmniRoute/pull/11683)) +- **chore(search):** drop unreachable credential sweep after #11642 ([#11687](https://github.com/diegosouzapw/OmniRoute/pull/11687)) — thanks @HouMinXi +- **ci:** add API route TypeScript regression gate ([#11705](https://github.com/diegosouzapw/OmniRoute/pull/11705)) — thanks @Sabeekhann +- **docs(dedup):** correct the CodeQL claim on the tenant-namespace comment ([#11712](https://github.com/diegosouzapw/OmniRoute/pull/11712)) +- **docs(changelog):** add consolidated v3.8.50 stats, top-25 ranking and a .mailmap ([#11715](https://github.com/diegosouzapw/OmniRoute/pull/11715)) +- **docs:** recommend gstack for AI-assisted workflows ([#11770](https://github.com/diegosouzapw/OmniRoute/pull/11770)) — thanks @santosraju99-hub +- **chore(quality):** tighten complexity/cognitive-complexity ratchets to the current tip; land the missed gateways.ts rebaseline ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **docs(i18n):** finish Freepik → Magnific rebrand in locale strings and README ([#11772](https://github.com/diegosouzapw/OmniRoute/pull/11772)) — thanks @turbolego +- **refactor(auto-combo):** fix divergent scoring in combo health reporting ([#11854](https://github.com/diegosouzapw/OmniRoute/pull/11854)) — thanks @jacobsparts +- **chore(changelog):** add missing fragments for #11919/#11918/#11916 ([#11938](https://github.com/diegosouzapw/OmniRoute/pull/11938)) +- **test(check):** escape the runs-on fixture with JSON.stringify, not a quote-only replace ([#11942](https://github.com/diegosouzapw/OmniRoute/pull/11942)) (direct commit `fb7445eaa3`) +- **test(release):** align five suites with the contracts #11933, #11919 and #11876 shipped on release/v3.8.51 ([#11944](https://github.com/diegosouzapw/OmniRoute/pull/11944)) +- **chore(quality):** remove unreachable code and restore test discovery ([#11950](https://github.com/diegosouzapw/OmniRoute/pull/11950)) — thanks @backryun +- **docs(agents):** sync-back landings are fast-forward, never squash ([#11964](https://github.com/diegosouzapw/OmniRoute/pull/11964)) +- **test(infra):** retry recursive temp-dir removal instead of failing a shard on ENOTEMPTY (#11966) ([#11968](https://github.com/diegosouzapw/OmniRoute/pull/11968)) +- **docs(ops):** the .113 heavy-build ceiling is one runner, not two ([#12048](https://github.com/diegosouzapw/OmniRoute/pull/12048)) +- **test(ui):** correct inactive auto-fetch expectation ([#12098](https://github.com/diegosouzapw/OmniRoute/pull/12098)) — thanks @RaviTharuma +- **docs:** document native dependency check escape hatch ([#12101](https://github.com/diegosouzapw/OmniRoute/pull/12101)) — thanks @RaviTharuma +- **test(providers):** regenerate the translate-path golden for OrcaRouter (#11923) ([#12118](https://github.com/diegosouzapw/OmniRoute/pull/12118)) +- **chore(quality):** velocity phase — loosen every numeric baseline by 20% until v4.0, monitor headroom nightly ([#12125](https://github.com/diegosouzapw/OmniRoute/pull/12125)) +- **chore(lint):** batch 0 of #12146 — type the call-log-cap sqlite rows instead of 45 as-any casts ([#12157](https://github.com/diegosouzapw/OmniRoute/pull/12157)) +- **chore(lint):** batch 4 of #12146 — shared/components react-hooks violations resolved ([#12159](https://github.com/diegosouzapw/OmniRoute/pull/12159)) +- **chore(lint):** batch 1 of #12146 — dashboard/cli-code react-hooks violations resolved ([#12160](https://github.com/diegosouzapw/OmniRoute/pull/12160)) +- **chore(lint):** batch 3 of #12146 — dashboard/settings react-hooks violations resolved ([#12162](https://github.com/diegosouzapw/OmniRoute/pull/12162)) +- **chore(lint):** batch 2 of #12146 — resolve the react-hooks compiler violations in dashboard/providers ([#12163](https://github.com/diegosouzapw/OmniRoute/pull/12163)) +- **chore(quality):** register search-432 cooldown test in stryker tap.testFiles ([#12170](https://github.com/diegosouzapw/OmniRoute/pull/12170)) +- **chore(lint):** batch 5 of #12146 — combos, endpoint, provider-stats, api-manager and costs react-hooks violations resolved ([#12174](https://github.com/diegosouzapw/OmniRoute/pull/12174)) +- **build:** add contributor fast profile ([#12192](https://github.com/diegosouzapw/OmniRoute/pull/12192)) — thanks @rafacpti23 +- **docs(readme):** README touch-ups ([#12193](https://github.com/diegosouzapw/OmniRoute/pull/12193)) ([#12194](https://github.com/diegosouzapw/OmniRoute/pull/12194)) ([#12202](https://github.com/diegosouzapw/OmniRoute/pull/12202)) — thanks @rafacpti23 +- **build:** force the webpack bundler in the contributor build profile ([#12197](https://github.com/diegosouzapw/OmniRoute/pull/12197)) — thanks @rafacpti23 +- **build:** skip standalone packaging in the contributor profile ([#12198](https://github.com/diegosouzapw/OmniRoute/pull/12198)) — thanks @rafacpti23 +- **docs(audit):** align every published number with the code and harden check:docs-counts ([#12200](https://github.com/diegosouzapw/OmniRoute/pull/12200)) +- **build:** stub the instrumentation entrypoints in the contributor profile ([#12203](https://github.com/diegosouzapw/OmniRoute/pull/12203)) — thanks @rafacpti23 +- **build:** omit the standalone output target for contributor builds ([#12204](https://github.com/diegosouzapw/OmniRoute/pull/12204)) — thanks @rafacpti23 +- **chore(lint):** batch 6 of #12146 — memory, radar, audit, analytics, cache, usage, activity, home and RequestLoggerV2 react-hooks violations resolved ([#12208](https://github.com/diegosouzapw/OmniRoute/pull/12208)) +- **docs(diagrams):** rename number-carrying diagram files to stable names ([#12210](https://github.com/diegosouzapw/OmniRoute/pull/12210)) +- **docs(dashboard):** redraw onboarding tier-flow SVGs for the real 4-tier model ([#12211](https://github.com/diegosouzapw/OmniRoute/pull/12211)) +- **docs(api):** document every implemented route in openapi.yaml (276 → 692 paths) ([#12212](https://github.com/diegosouzapw/OmniRoute/pull/12212)) +- **chore(lint):** batch 7 of #12146 — final src tail: 37 react-hooks violations across 33 files resolved ([#12244](https://github.com/diegosouzapw/OmniRoute/pull/12244)) +- Revert "docs: recommend gstack for AI-assisted workflows (#11770)" ([#12248](https://github.com/diegosouzapw/OmniRoute/pull/12248)) ([#12249](https://github.com/diegosouzapw/OmniRoute/pull/12249)) +- **docs:** Chaos Mode setup guide + weighted strategy semantics ([#12250](https://github.com/diegosouzapw/OmniRoute/pull/12250)) +- **docs(agents):** protected-surface merge rule — operator approval for agent-instruction files ([#12253](https://github.com/diegosouzapw/OmniRoute/pull/12253)) +- **chore(quality):** register native-codex turn-pin tests in stryker tap.testFiles ([#12263](https://github.com/diegosouzapw/OmniRoute/pull/12263)) +- **chore(quality):** dedupe tap.testFiles entries added by racing base-red fixes ([#12265](https://github.com/diegosouzapw/OmniRoute/pull/12265)) +- **docs(readme):** introduce OmniRouteTray — the macOS menu-bar companion ([#12276](https://github.com/diegosouzapw/OmniRoute/pull/12276)) — thanks @ggiak +- **chore(proxy):** purge the legacy 1proxy residue — sync/rotator modules, dead DB exports, dead settings tab and flag, docs (#12091) ([#12290](https://github.com/diegosouzapw/OmniRoute/pull/12290)) +- **chore(quality):** baseline-headroom skips generated and vendored files in the fileSize worst-file signal ([#12291](https://github.com/diegosouzapw/OmniRoute/pull/12291)) +- **refactor(video):** unify the JPEG frame data-URI contract ([#12322](https://github.com/diegosouzapw/OmniRoute/pull/12322)) +- **chore(deps):** freeze onnxruntime-node and eslint-plugin-react-hooks out of dependabot groups ([#12329](https://github.com/diegosouzapw/OmniRoute/pull/12329)) +- **test(executors):** restore the #10986 reasoning-only fallback guards ([#12364](https://github.com/diegosouzapw/OmniRoute/pull/12364)) — thanks @pacocartones +- **chore(quality):** re-tighten the file-size ratchet to the real LOC (plan 3.8.52 task 0) ([#12411](https://github.com/diegosouzapw/OmniRoute/pull/12411)) +- **test(grok):** format web executor suite ([#12412](https://github.com/diegosouzapw/OmniRoute/pull/12412)) +- **chore(quality):** ratchet open-sse typecheck baseline to zero ([#12418](https://github.com/diegosouzapw/OmniRoute/pull/12418)) +- **test(build):** guard the artifact path policy arrays against duplicates ([#12422](https://github.com/diegosouzapw/OmniRoute/pull/12422)) +- **chore(lint):** adopt eslint-plugin-react-hooks 7.1.1 ([#12428](https://github.com/diegosouzapw/OmniRoute/pull/12428)) +- **chore(providers):** remove a keyless provider integration at its operator's request ([#12440](https://github.com/diegosouzapw/OmniRoute/pull/12440)) +- **docs(providers):** register providers removed at their operator's request and guard against reintroduction ([#12478](https://github.com/diegosouzapw/OmniRoute/pull/12478)) +- **chore(quality):** base fixes — stryker tap.testFiles + node_modules cache key ([#12482](https://github.com/diegosouzapw/OmniRoute/pull/12482)) +- **docs(arch):** one-process recipe for tens of long /v1/responses ([#12493](https://github.com/diegosouzapw/OmniRoute/pull/12493)) — thanks @RaviTharuma +- **chore(quality):** tighten the CodeQL ratchet baseline from 11 to 6 ([#12530](https://github.com/diegosouzapw/OmniRoute/pull/12530)) +- **chore(deps):** pin browserslist override to ^4.28.8 ([#12592](https://github.com/diegosouzapw/OmniRoute/pull/12592)) — thanks @KooshaPari +- **chore(deps):** bump fast-uri to 3.1.7 in the electron lockfile ([#12601](https://github.com/diegosouzapw/OmniRoute/pull/12601)) +- **docs(quality):** document how the CodeQL ratchet refreshes and how to tighten it ([#12611](https://github.com/diegosouzapw/OmniRoute/pull/12611)) +- **test(ci):** pin the openapi-security-tiers two-arm contract with an executing gate test (#12581) ([#12652](https://github.com/diegosouzapw/OmniRoute/pull/12652)) +- **docs(gamification):** align XP Rewards table with code (#12501) ([#12667](https://github.com/diegosouzapw/OmniRoute/pull/12667)) — thanks @KooshaPari +- **refactor(combo):** split executeTarget into gates, attempt, and loop ([#12746](https://github.com/diegosouzapw/OmniRoute/pull/12746)) — thanks @HouMinXi +- **docs(security):** document socket.yml scanner config + CI workflow link (#12575) ([#12764](https://github.com/diegosouzapw/OmniRoute/pull/12764)) — thanks @KooshaPari +- **chore(ci):** guard commit identity in pre-commit to stop author misattribution ([#12772](https://github.com/diegosouzapw/OmniRoute/pull/12772)) +- **deps:** 17 Dependabot bumps — bump electron from 43.4.0 to 43.4.1 in /electron ([#11426](https://github.com/diegosouzapw/OmniRoute/pull/11426)); bump the development group across 1 directory with 8 updates ([#11428](https://github.com/diegosouzapw/OmniRoute/pull/11428)); bump the production group across 1 directory with 11 updates ([#11440](https://github.com/diegosouzapw/OmniRoute/pull/11440)); chore(deps): bump github/codeql-action/init from 4.37.7 to 4.37.8 ([#11925](https://github.com/diegosouzapw/OmniRoute/pull/11925)); chore(deps): bump github/codeql-action from 4.37.7 to 4.37.8 ([#11926](https://github.com/diegosouzapw/OmniRoute/pull/11926)); chore(deps): bump github/codeql-action/analyze from 4.37.7 to 4.37.8 ([#11927](https://github.com/diegosouzapw/OmniRoute/pull/11927)); build(deps): bump github/codeql-action/init from 4.37.8 to 4.37.9 ([#12345](https://github.com/diegosouzapw/OmniRoute/pull/12345)); build(deps): bump github/codeql-action/analyze from 4.37.8 to 4.37.9 ([#12346](https://github.com/diegosouzapw/OmniRoute/pull/12346)); bump the development group across 1 directory with 2 updates ([#12347](https://github.com/diegosouzapw/OmniRoute/pull/12347)); build(deps): bump github/codeql-action from 4.37.8 to 4.37.9 ([#12349](https://github.com/diegosouzapw/OmniRoute/pull/12349)); bump browserslist from 4.28.2 to 4.28.8 ([#12396](https://github.com/diegosouzapw/OmniRoute/pull/12396)); bump the production group across 1 directory with 4 updates ([#12399](https://github.com/diegosouzapw/OmniRoute/pull/12399)); bump @xmldom/xmldom ([#12500](https://github.com/diegosouzapw/OmniRoute/pull/12500)); bump qs from 6.15.2 to 6.16.0 ([#12512](https://github.com/diegosouzapw/OmniRoute/pull/12512)); bump @xmldom/xmldom from 0.9.10 to 0.9.12 ([#12513](https://github.com/diegosouzapw/OmniRoute/pull/12513)); bump fast-uri from 3.1.5 to 3.1.7 ([#12514](https://github.com/diegosouzapw/OmniRoute/pull/12514)); bump @humanfs/node from 0.16.7 to 0.16.8 ([#12515](https://github.com/diegosouzapw/OmniRoute/pull/12515)) +- Clear the `release/v3.8.51` `check:agent-skills-sync` base-red: regenerate `skills/cli-tunnel/SKILL.md` so the `tunnel create [type]` positional that #13009 taught the generator to read is reflected in the committed skill. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` typecheck base-red from #12731: the new `Mode pack ranking gates` candidates in `open-sse/services/autoCombo/__tests__/autoCombo.test.ts` omitted the required `provider`, `model` and `errorRate` fields of `ProviderCandidate`, failing `check:open-sse-typecheck` (and with it `Fast Quality Gates`) on every open PR. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` docs-sync base-red: EURouter (#13025) and GreenPT (#13024) took the live provider count to 358, leaving 7 STRICT drifts (`PROVIDER_REFERENCE.md`, 4 diagrams, 2 tier-flow images) plus the `AGENTS.md` / `llm.txt` / `package.json` count claims stale. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- **test(models):** Pin `CATALOG_BUILD_TIMEOUT_MS` in the #12058 canonical-catalog regression so a cold catalog build racing the 8s default bound no longer flakes the suite with a 500 ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Allow `@omniroute/opencode-plugin-v2/` in the npm pack policy — [#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870) shipped the v2 plugin beside its v1 sibling without widening the allowlist, so every packed file under it read as an unexpected artifact and failed `check:pack-policy` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Point the release-green validator's pack gate at the tree it is validating — the build-provenance guard defaulted to `origin/main`, which a release branch tip can never be an ancestor of mid-cycle, so `Package artifact` reported a false red on every run and the tarball boot-smoke cascaded off it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **docs(checks):** keep doc counts honest — headings, rankings, catalog, weights, quality gate and scoring diagram now covered ([#12507](https://github.com/diegosouzapw/OmniRoute/pull/12507)) — thanks @maxmad64bis +- **chore(lifecycle):** `check:model-lifecycle` now also diffs `DEFAULT_DEGRADATION_MAP` (the background-task redirect table) against the vendor lifecycle snapshot, refusing a retired id as source or target, with a table-driven unit test beside it. Three rows whose source the vendor had retired — `claude-sonnet-4-20250514`, `gemini-3-pro-preview` and `gpt-5.1-codex` (whose target `gpt-5.1-codex-mini` is retired too) — were dead code, since `checkLifecycle` answers 410 before the redirect runs; they are dropped (#12535 — thanks @pacocartones) +- **test(compression):** cover idle worker eviction at the resource level — the pool must call `terminate()` and must not retain the worker's `MessagePort`, complementing the `exit`-event assertion added with the fix ([#13371](https://github.com/diegosouzapw/OmniRoute/pull/13371)) +- **chore(quality):** raise the `imageGeneration.ts` file-size ceiling for the image-only-model guard that clears the #12945 base-red ([#13392](https://github.com/diegosouzapw/OmniRoute/pull/13392)) +- **chore(quality):** type the combo-test route's JSON response bodies instead of casting them to `any`, drop the now-empty suppression entry, and remove the `hasPerModelQuota` import `chatCore.ts` stopped using when the failure-classification helper was extracted ([#13379](https://github.com/diegosouzapw/OmniRoute/pull/13379)) +- **chore(electron):** the desktop bundle no longer ships the root-level files of every translated docs mirror (`README.md`, `llm.txt`, `CLAUDE.md`, `GEMINI.md`, `CONTRIBUTING.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`) — the packaged app only reads `docs/i18n//docs/**`, which stays. Saves ~11 MB on top of the translated CHANGELOGs already pruned. (#0000) ([#13235](https://github.com/diegosouzapw/OmniRoute/pull/13235)) +- **chore(quality):** retighten the `src/sse/handlers/chat.ts` unused-vars suppression count to the 8 violations that actually remain ([#13385](https://github.com/diegosouzapw/OmniRoute/pull/13385)) +- Bring the documented counts back in line with the code: 171 migrations (was 169) and 20 routing strategies (was 19), plus the regenerated `cli-tunnel` skill reference. ([#12970](https://github.com/diegosouzapw/OmniRoute/pull/12970)) +- Update the documented migration count to 172 after the call-logs provider-stats indexes landed. ([#13209](https://github.com/diegosouzapw/OmniRoute/pull/13209)) +- **chore(ci):** two gates that close the blind spots behind the exclusions above. `check:vitest-exclusions` requires every Vitest exclusion to name a tracking issue and to appear in `config/quality/vitest-exclusions.json` — the previous list grew to 62 files behind a comment pointing at an issue that had been closed for a month. `check-new-key-coverage` requires a key newly added to `en.json` to reach every locale; the existing coverage gate is a percentage floor per locale, so eleven absent keys out of ~13,000 left it at 99.9% while a whole feature shipped untranslated in nine languages. Both are diff-aware, so pre-existing debt stays frozen and neither needed a migration to turn on. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **build:** root layout no longer loads Inter from `next/font/google`, so a production image build does not need fonts.googleapis.com ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **docs(i18n):** bring the eight new `llm.txt` mirrors (km, kn, ml, my, ne, or, pa, si) up to the current provider and migration counts, so `check-docs-sync` stops rejecting every commit ([#13674](https://github.com/diegosouzapw/OmniRoute/pull/13674)) +- **refactor(ui):** flow surfaces (home topology, combo live studio, compression cockpit/waterfall/nodes, token-health badge) express state through the theme-aware `--orch-status-*` tokens instead of fixed dark-mode hex, so green/red/amber/grey stay legible in the light theme; dark mode is byte-identical. Categorical palettes (routing-strategy hues, compression-layer pills, provider brand colors) deliberately stay hex ([#12378](https://github.com/diegosouzapw/OmniRoute/issues/12378)) ([#13203](https://github.com/diegosouzapw/OmniRoute/pull/13203)) +- **chore(tests):** 51 test files that had been excluded from Vitest are running again, restoring roughly 350 assertions to the blocking suite. Each was measured individually first: of the 62 files parked behind the `// #8618 — pre-existing failure` comment, 51 pass against the current tree with no source change, so the exclusions had outlived the failures they were added for. The 11 that genuinely still fail stay excluded, but now point at a live tracker (#13204) rather than at #8618, which was closed in August while the list it tracked kept growing. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **chore(quality):** drop two ESLint suppression entries whose violations no longer exist, so `eslint --suppressions-location` stops rejecting every commit that touches the surrounding files ([#13384](https://github.com/diegosouzapw/OmniRoute/pull/13384)) +- **chore(release):** bring main's post-tag fixes into release/v3.8.51 — sync-back step 2/2 (direct commit `21c488f210`) +- **chore(deps):** patch toml and esbuild in the opencode plugin lockfiles ([#12965](https://github.com/diegosouzapw/OmniRoute/pull/12965)) +- **docs(changelog):** reconcile the v3.8.51 living section — fold 366 fragments, cover every cycle commit, credit every contributor ([#12971](https://github.com/diegosouzapw/OmniRoute/pull/12971)) +- **test(plugin):** wait for the v2 lazy-refresh tier instead of sleeping past it ([#12984](https://github.com/diegosouzapw/OmniRoute/pull/12984)) +- **chore(quality):** rebaseline chatCore.ts after the non-streaming regression fixes ([#13045](https://github.com/diegosouzapw/OmniRoute/pull/13045)) +- **docs(changelog):** add the missing bullet marker to the DeepSeek PoW fragment ([#13200](https://github.com/diegosouzapw/OmniRoute/pull/13200)) +- **chore(deps):** drain the Dependabot queue — 10 of 13 alerts ([#13213](https://github.com/diegosouzapw/OmniRoute/pull/13213)) +- **chore(ci):** make worktrees carry the hooks that enforce the identity gate ([#13236](https://github.com/diegosouzapw/OmniRoute/pull/13236)) +- **chore(deps):** pin csv-parse to ^7.0.2 via overrides (#13117) ([#13300](https://github.com/diegosouzapw/OmniRoute/pull/13300)) — thanks @KooshaPari +- **chore(deps):** bump hono from ^4.12.34 to ^4.13.7 (#13148) ([#13301](https://github.com/diegosouzapw/OmniRoute/pull/13301)) — thanks @KooshaPari +- **chore(deps):** pin joi to ^18.2.8 via overrides (#13085) ([#13302](https://github.com/diegosouzapw/OmniRoute/pull/13302)) — thanks @KooshaPari +- **docs(providers):** add Microsoft 365 Copilot (BizChat) provider guide (#12779) ([#13340](https://github.com/diegosouzapw/OmniRoute/pull/13340)) — thanks @KooshaPari +### 🙌 Contributors + +Thanks to everyone whose work landed in v3.8.51: + +| Contributor | PRs / Issues | +| --- | --- | +| [@5dive-bot](https://github.com/5dive-bot) | #11852 | +| [@abhisheksharma2411](https://github.com/abhisheksharma2411) | #11547, #11684, #11959, #12209 | +| [@adevwithpurpose](https://github.com/adevwithpurpose) | #11464 | +| [@adivekar-utexas](https://github.com/adivekar-utexas) | #12015, #12043, #12090, #12138 | +| [@AIB1TAL0S](https://github.com/AIB1TAL0S) | #12103 | +| [@alltomatos](https://github.com/alltomatos) | #11448 | +| [@alvinveroy](https://github.com/alvinveroy) | #11857, #12027, #12165 | +| [@amaleta](https://github.com/amaleta) | #11906, #11934 | +| [@amartinawi](https://github.com/amartinawi) | #11575 | +| [@AndrianBalanescu](https://github.com/AndrianBalanescu) | #11083, #11830, #11834, #11844 | +| [@anhtahaylove](https://github.com/anhtahaylove) | #13091, #13092, #13093, #13096, #13097, #13100, #13106, #13109, #13114, #13155, #13166, #13171, #13175, #13187, #13196 | +| [@aniruddhaadak80](https://github.com/aniruddhaadak80) | #11760 | +| [@app](https://github.com/app) | #12554 | +| [@arminanton](https://github.com/arminanton) | #11461, #11513 | +| [@AStupidBear](https://github.com/AStupidBear) | #11584 | +| [@atescivitci-cmd](https://github.com/atescivitci-cmd) | #12105 | +| [@b3nw](https://github.com/b3nw) | #11970, #11971, #12147 | +| [@backryun](https://github.com/backryun) | #11259, #11950, #12075, #12076, #12078, #12079, #12081, #12082, #12181, #12228, #12239, #12255, #12258, #12277, #12367, #12423, #12524, #12538 | +| [@benjaminkitt](https://github.com/benjaminkitt) | #11747 | +| [@benzntech](https://github.com/benzntech) | #11614, #11615 | +| [@Bl0ck154](https://github.com/Bl0ck154) | #11948, #11951, #11952, #11953, #11954 | +| [@brick30llc-ctrl](https://github.com/brick30llc-ctrl) | #11752, #12242, #12262 | +| [@Chewji9875](https://github.com/Chewji9875) | #11455, #11875, #12028 | +| [@cheynetom](https://github.com/cheynetom) | #12058 | +| [@davidebaraldo](https://github.com/davidebaraldo) | #12222, #12304 | +| [@davidlinfr](https://github.com/davidlinfr) | #6390 | +| [@ddarkr](https://github.com/ddarkr) | #12124 | +| [@Deftera186](https://github.com/Deftera186) | #11809 | +| [@dpozimski](https://github.com/dpozimski) | #11945 | +| [@DW-MediaLab](https://github.com/DW-MediaLab) | #12229 | +| [@echel0nn](https://github.com/echel0nn) | #11923 | +| [@f9td56dbgh-hub](https://github.com/f9td56dbgh-hub) | #11660 | +| [@fabioluissilva](https://github.com/fabioluissilva) | #11991 | +| [@foreveryh](https://github.com/foreveryh) | #12177 | +| [@geek007git](https://github.com/geek007git) | #12115, #12116, #12117, #12120, #12122 | +| [@geekyNads](https://github.com/geekyNads) | #11784, #12266 | +| [@ggiak](https://github.com/ggiak) | #11561, #12276, #12350, #12402 | +| [@gonisulaimann](https://github.com/gonisulaimann) | #12368, #12369, #12371 | +| [@Gorillaz322](https://github.com/Gorillaz322) | #12207 | +| [@groovecityJO](https://github.com/groovecityJO) | #12682 | +| [@hartmark](https://github.com/hartmark) | #11434, #11452, #11473, #11499, #11703, #11983, #11984, #11985, #11986, #11988, #11989, #11990, #11994, #12221, #12293, #12445, #12446, #12447, #12448, #12460, #12461, #12623, #12646, #12650, #12680, #12717, #12718, #12727, #12741, #12854 | +| [@hizzt](https://github.com/hizzt) | #11894 | +| [@hongnoul](https://github.com/hongnoul) | #11484 | +| [@HouMinXi](https://github.com/HouMinXi) | #11411, #11414, #11512, #11518, #11520, #11641, #11642, #11643, #11687, #11779, #11849, #11850, #11851, #11915, #11916, #11918, #11919, #11920, #12013, #12017, #12033, #12042, #12106, #12139, #12166, #12169, #12171, #12205, #12213, #12312, #12325, #12487, #12488, #12495, #12504, #12557, #12566, #12590, #12591, #12624, #12626, #12632, #12637, #12678, #12696, #12697, #12711, #12733, #12746, #12767, #12770, #12789, #12803, #12805, #12811, #12866, #12868, #12899, #12926, #12934, #12950, #12951, #12974, #13001, #13006, #13011, #13017, #13026, #13027, #13034, #13035, #13038, #13042, #13050, #13060, #13061, #13069, #13107, #13120, #13136, #13195, #13197 | +| [@Hsia97](https://github.com/Hsia97) | #11624 | +| [@jacobsparts](https://github.com/jacobsparts) | #11854, #12155, #12167 | +| [@joglomedia](https://github.com/joglomedia) | #11980 | +| [@jonlwheat2-gif](https://github.com/jonlwheat2-gif) | #11443, #11450, #11549, #11567, #11608, #11633, #11635, #11640, #11644, #11671, #11672, #11673, #11674, #11675, #11676, #11677, #11706, #11783, #12051, #12052, #12053, #12055 | +| [@kanade-hoshino](https://github.com/kanade-hoshino) | #12180 | +| [@Karan825](https://github.com/Karan825) | #11861 | +| [@KaspaPulse](https://github.com/KaspaPulse) | #11389, #11469, #11628, #11666, #11910 | +| [@keeltrace](https://github.com/keeltrace) | #12080, #12223 | +| [@killer30001000](https://github.com/killer30001000) | #12178 | +| [@KooshaPari](https://github.com/KooshaPari) | #12470, #12592, #12667, #12699, #12703, #12706, #12764, #12769, #12771, #13300, #13301, #13302, #13340, #13401, #13403, #13404, #13405, #13406, #13407, #13409, #13410, #13411, #13412, #13413, #13414, #13418, #13424, #13427, #13433, #13523, #13524, #13525, #13528, #13531, #13532, #13534, #13539, #13543, #13545, #13546, #13547, #13550, #13553 | +| [@kriptoburak](https://github.com/kriptoburak) | #11370 | +| [@KrzysiekSko](https://github.com/KrzysiekSko) | #12425, #12432, #12673 | +| [@marcelokarval](https://github.com/marcelokarval) | direct commit / report | +| [@maxmad64bis](https://github.com/maxmad64bis) | #1622, #11435, #11437, #11441, #11537, #11550, #11553, #11555, #11557, #11812, #11842, #11843, #11903, #12151, #12214, #12215, #12218, #12226, #12314, #12316, #12317, #12318, #12319, #12320, #12321, #12507, #12715, #12731, #12744, #12786, #12787, #12788, #12790, #12792, #12794, #12795, #12828, #12832, #12853, #12857, #12870, #12937, #12941, #12975, #13141, #13142, #13146, #13147 | +| [@mdigitalbh81](https://github.com/mdigitalbh81) | #12240 | +| [@Meet6338-X](https://github.com/Meet6338-X) | #11598, #11609, #12110 | +| [@morpheus9393](https://github.com/morpheus9393) | #11943 | +| [@MumuTW](https://github.com/MumuTW) | #11492, #11502, #11506, #11507, #11626, #11675, #11685, #11728, #11746, #11888, #11889, #11890, #11892 | +| [@Neuron-Mr-White](https://github.com/Neuron-Mr-White) | #11622, #11801, #12256 | +| [@NightStalker-87](https://github.com/NightStalker-87) | #12183 | +| [@NoxzRCW](https://github.com/NoxzRCW) | #11879, #11880, #11881, #11882, #11883, #11935 | +| [@ntdat812](https://github.com/ntdat812) | #11585, #12095 | +| [@ntdatt812](https://github.com/ntdatt812) | #11368, #11573, #11574, #11576, #11577, #11580, #11582, #11583, #11588, #11589, #11590, #11591, #11592, #11593, #11672, #12177, #12180, #12873, #12918, #12920, #12921, #12925, #12930, #13007, #13009, #13024, #13025, #13083, #13087, #13101, #13104, #13110 | +| [@official-burak](https://github.com/official-burak) | #11542 | +| [@opensource-elearning](https://github.com/opensource-elearning) | #12179, #12189, #12278, #12286 | +| [@oyi77](https://github.com/oyi77) | #11408, #11409, #11421, #11505, #11677, #12036, #12110 | +| [@pacocartones](https://github.com/pacocartones) | #11521, #11522, #11527, #11528, #11529, #11530, #11531, #11532, #11533, #11534, #11595, #11599, #11603, #11604, #11605, #11607, #11610, #11676, #11714, #11716, #11718, #11767, #11838, #11860, #11862, #11869, #11871, #11872, #11873, #11903, #11906, #11921, #11934, #12356, #12358, #12359, #12360, #12361, #12362, #12364, #12365, #12373, #12374, #12375, #12376, #12377, #12379, #12380, #12381, #12386, #12387, #12389, #12390, #12394, #12395, #12397, #12401, #12403, #12404, #12522, #12523, #12535, #12536, #12540, #12541, #12543, #12545, #12548, #12549, #12550, #12551, #12552, #12647, #12651, #12653, #13055 | +| [@patrykkopycinski](https://github.com/patrykkopycinski) | #11936, #11937, #12224 | +| [@PauloFH](https://github.com/PauloFH) | #11509 | +| [@PauloHSOliveira](https://github.com/PauloHSOliveira) | #12241 | +| [@ponkcore](https://github.com/ponkcore) | #12054 | +| [@Prajeeth-12](https://github.com/Prajeeth-12) | #11634, #12046 | +| [@quiterunner-commits](https://github.com/quiterunner-commits) | #12131, #12143 | +| [@rafacpti23](https://github.com/rafacpti23) | #11554, #11558, #12192, #12197, #12198, #12202, #12203, #12204 | +| [@ragnar-claude](https://github.com/ragnar-claude) | #11564 | +| [@raheemuddin786](https://github.com/raheemuddin786) | #11491, #11839, #11840, #11841, #12003, #12230, #12231, #12232, #12233, #12234 | +| [@rahilmavani](https://github.com/rahilmavani) | #11761 | +| [@Rahulsharma0810](https://github.com/Rahulsharma0810) | #11771 | +| [@RaviTharuma](https://github.com/RaviTharuma) | #11710, #11727, #11797, #11798, #11802, #11805, #11806, #11811, #12098, #12099, #12101, #12449, #12452, #12472, #12473, #12493, #12533, #12607, #12628, #12631, #12636, #12875, #12876, #12880, #12882, #12884 | +| [@rezjalibd](https://github.com/rezjalibd) | #12186 | +| [@RhianB14](https://github.com/RhianB14) | #11692 | +| [@rifqiawl](https://github.com/rifqiawl) | #11517, #11519, #11969 | +| [@rqzbeh](https://github.com/rqzbeh) | #11390 | +| [@Sabeekhann](https://github.com/Sabeekhann) | #11699, #11705 | +| [@santosraju99-hub](https://github.com/santosraju99-hub) | #11755, #11770 | +| [@Seramicx](https://github.com/Seramicx) | #11568 | +| [@Siva010](https://github.com/Siva010) | #12191 | +| [@solstxce](https://github.com/solstxce) | #11597 | +| [@soroush5](https://github.com/soroush5) | #12691 | +| [@tenshiak](https://github.com/tenshiak) | #12279 | +| [@TheDemonTuan](https://github.com/TheDemonTuan) | #11468, #11470, #11471, #11482, #11548, #11758, #11775, #11814 | +| [@thomasmaerz](https://github.com/thomasmaerz) | #12834 | +| [@tuandinh0801](https://github.com/tuandinh0801) | #11454 | +| [@turbolego](https://github.com/turbolego) | #11621, #11762, #11772, #11774, #11781, #12216 | +| [@Tushar49](https://github.com/Tushar49) | #11392 | +| [@ujjawalkaushik1110](https://github.com/ujjawalkaushik1110) | #11863 | +| [@vermasomesh835](https://github.com/vermasomesh835) | #11794 | +| [@vsd2807](https://github.com/vsd2807) | #11565, #11619 | +| [@wahidsadik371-coder](https://github.com/wahidsadik371-coder) | #12126 | +| [@watchingdogs](https://github.com/watchingdogs) | #12031 | +| [@wildcard](https://github.com/wildcard) | #11620 | +| [@xiaoyaner0201](https://github.com/xiaoyaner0201) | #11460, #11662, #12083 | +| [@Xxx91n](https://github.com/Xxx91n) | #11690 | +| [@yourspraveen](https://github.com/yourspraveen) | #11146 | +| [@yxyxy](https://github.com/yxyxy) | #11671 | +| [@ZaimMarzuki](https://github.com/ZaimMarzuki) | #11960 | +| [@Zartharas](https://github.com/Zartharas) | #11340 | +| [@zero-executioner](https://github.com/zero-executioner) | #11631 | +| [@diegosouzapw](https://github.com/diegosouzapw) | maintainer | + +--- ## [3.8.50] — 2026-08-25 _Living section — regenerated 2026-08-12 from all cycle commits (cycle open `ed2db6cb19` → tip). Bullets carry the merged PR and its author; direct pushes listed separately._ diff --git a/docs/i18n/no/CHANGELOG.md b/docs/i18n/no/CHANGELOG.md index 316bebb9d8..749a6e0774 100644 --- a/docs/i18n/no/CHANGELOG.md +++ b/docs/i18n/no/CHANGELOG.md @@ -8,16 +8,1119 @@ ## [3.8.51] — TBD -_Living section — cycle opened at the v3.8.50 freeze (parallel-cycle model). Bullets are aggregated from `changelog.d/` fragments at each `/generate-release` phase._ +_Living section — reconciled 2026-09-15 from all cycle commits (`091589089c` → `c0f92ec98a`, 916 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 | **123** | +| 📝 Commits in the cycle | **916** | +| 🔀 Pull requests referenced | **892** | +| 📋 Changelog entries | **919** | +| 🙌 Contributors credited in entries | **121** | +| 🤖 Automated dependency commits | 18 | + +**Entries by type** + +| Type | Count | +| --- | ---: | +| 🐛 Fixes | 592 | +| ✨ Features | 131 | +| 🧹 Chore | 93 | +| 📚 Docs | 42 | +| 🧪 Tests | 32 | +| ♻️ Refactor | 8 | +| 🏗️ Build | 7 | +| ⚡ Performance | 7 | +| ⚙️ CI | 2 | +| ⏪ Reverts | 2 | +| 📦 Dependencies | 2 | +| 🔒 Security | 1 | + +### 🏆 Top 25 contributors this cycle + +_By commits in `091589089c..c0f92ec98a`, author identities consolidated via `.mailmap` and the merged PR's GitHub login. Bots excluded._ + +| # | Contributor | Commits | +| ---: | --- | ---: | +| 🥇 | diegosouzapw | 323 | +| 🥈 | Bob.Hou (@HouMinXi) | 79 | +| 🥉 | Paco Cartones (@pacocartones) | 77 | +| 4 | Dizzle (@maxmad64bis) | 49 | +| 5 | Koosha Paridehpour (@KooshaPari) | 43 | +| 6 | Nguyen Thanh Dat (@ntdatt812) | 31 | +| 7 | Ravi Tharuma (@RaviTharuma) | 26 | +| 8 | Markus Hartung (@hartmark) | 22 | +| 9 | Webman (@jonlwheat2-gif) | 22 | +| 10 | backryun | 17 | +| 11 | anhtahaylove | 15 | +| 12 | Rafa Martins (@rafacpti23) | 10 | +| 13 | Syed Raheemuddin (@raheemuddin786) | 10 | +| 14 | Nguyễn Viết Tuấn (@TheDemonTuan) | 8 | +| 15 | MumuTW | 7 | +| 16 | Paijo (@oyi77) | 7 | +| 17 | Tobias Andersen (@turbolego) | 6 | +| 18 | Bl0ck (@Bl0ck154) | 5 | +| 19 | SHANMUGAPRIYAN (@geek007git) | 5 | +| 20 | KaspaPulse | 5 | +| 21 | Abhishek Sharma (@abhisheksharma2411) | 4 | +| 22 | Abhishek Divekar (@adivekar-utexas) | 4 | +| 23 | Andrew B. (@AndrianBalanescu) | 4 | +| 24 | NoxzRCW | 4 | +| 25 | opensource-elearning | 4 | ### ✨ New Features +- **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). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- Added Google AI Studio Gemini batch text-to-speech support through `POST /v1/audio/speech`. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- 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. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(routing):** subscription-first auto groupings — `auto/subscription` routes only through plan-included connections with a documented hard-stop overage and fails closed on exhaustion, while `auto/thrifty` orders the pool `subscription → keyless → free → cheap → premium` and steps up one rung at a time as each is exhausted. Billing class comes from a curated per-connection catalog (uncurated is treated as metered, never plan-included), both reuse STRICT_ZERO_COST's per-connection verification, and a quota reading whose `resetAt` has passed is now refreshed regardless of TTL so routing returns to plan capacity as soon as it resets ([#11146](https://github.com/diegosouzapw/OmniRoute/pull/11146)) — thanks @yourspraveen +- **feat(providers):** publish a management-authenticated versioned web-session credential contract from OmniRoute's canonical browser credential metadata ([#11340](https://github.com/diegosouzapw/OmniRoute/pull/11340)) — thanks @Zartharas +- **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)) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add Xquik X search with typed results, credential validation, REST routing, and MCP selection ([#11370](https://github.com/diegosouzapw/OmniRoute/pull/11370)) — thanks @kriptoburak +- **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)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(dashboard):** surface durable exclusive managed leases in the existing Sessions view, keeping leased clients visible across idle gaps while marking connections with in-flight work as active ([#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389)) — thanks @KaspaPulse +- **build(bun):** allow Turbopack bundler flag on Bun 1.4+ with configurable Webpack fallback ([#11471](https://github.com/diegosouzapw/OmniRoute/pull/11471)) — thanks @TheDemonTuan +- feat(api): add an opt-in `modelVisibilityAllowlist`/`modelVisibilityDenylist` settings pair to curate exactly which models `/v1/models` advertises, mirrored into every `auto/*` combo candidate pool so a denied model cannot be routed to via combo selection either (#11481) ([#11997](https://github.com/diegosouzapw/OmniRoute/pull/11997)) +- **feat(rankings):** the Free Provider Rankings page shows what each provider actually served over the last 24 h. It ranked by ELO alone, which left a provider that answers every call with an error in first place; the usage data was already served by the API but never requested. A provider with too small a sample shows a dash, not a number ([#11546](https://github.com/diegosouzapw/OmniRoute/pull/11546)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **feat(guardrails):** enforce a bounded, deterministic contract for Video Bridge transcripts — 256 cues, 4096 input code units and 4 KiB UTF-8 per cue, 64 KiB total text, malformed-Unicode rejection, focus-window scoping, cross-source reconciliation with contributing-source metadata, and a structural provenance trust boundary so caller JSON can never self-assert `embedded`/`audio-bridge` provenance ([#11652](https://github.com/diegosouzapw/OmniRoute/issues/11652)) ([#12009](https://github.com/diegosouzapw/OmniRoute/pull/12009)) +- **feat(video):** orchestrate optional Video Bridge audio extraction and Audio Bridge STT behind a dual opt-in (operator setting AND per-request signal) — a new loopback-only broker `mode=audio` operation shares the frame path's exact process queue, deadline, AbortSignal, and byte budgets to extract a bounded mono 16 kHz PCM WAV from the same already-downloaded video, then reuses the existing Audio Bridge transcription boundary; provider segment timing is preserved when available and marked coarse otherwise, and every failure degrades to a visual-only-safe partial instead of throwing (#11654). ([#12012](https://github.com/diegosouzapw/OmniRoute/pull/12012)) +- Add a tenant-bound Video Bridge drill-down lifecycle on top of the existing secure cache substrate: opaque hashed handles (never raw session/video identifiers), preview/standard/detail multiresolution variants resampled on read, response pagination capped at 8 frames and 32 MiB, and a new authenticated `/api/v1/video-bridge/drilldown` consumer route that stays disabled for remote access by default and denies cross-key access with the same response as a nonexistent handle (no existence oracle). ([#12006](https://github.com/diegosouzapw/OmniRoute/pull/12006)) +- **test(video):** Add the Video Bridge FU-07/FU-09 promotion-evidence harness (#11656) — a frozen Zod manifest schema covering the 8 required scenario kinds (static scenes, rapid cuts, late facts, fades, blur, small text, close events, visual prompt injection) with a minimum of 3 repetitions per case, deterministic declarative fixture recipes (`videoBridgePromotionFixtures.ts`), a pure medians/p95 metrics aggregator, a pure FU-07/FU-09 promotion-verdict evaluator applying the ticket's exact thresholds (missing token usage always holds), a digest-only persistence layer that never retains raw media or raw model responses, and a versioned per-model promotion allowlist shipped empty with every model defaulting to `hold`. The FU-07/FU-09 promotion verdicts themselves remain HOLD — they require a real evidence run against real models on VPS 192.168.0.15. ([#12008](https://github.com/diegosouzapw/OmniRoute/pull/12008)) +- **feat(video bridge):** "embedded" transcript provenance can now be legitimately earned instead of merely asserted — a bounded, allowlisted (`mov_text`/`subrip`/`webvtt`) subtitle probe runs through the loopback-only Video Bridge broker (at most 2 streams, 10s subdeadline bounded by the request deadline, 256 KiB output, 4096-code-unit lines), normalized through a bounded, ReDoS-safe WebVTT parser and Zod-validated end to end. The adapter always resolves to an explicit `success`/`absent`/`transient_failure` outcome — a subtitle failure never breaks the visual description path, and only a fingerprint-verified broker response (never a caller-declared label) can produce embedded cues (#11659). ([#12011](https://github.com/diegosouzapw/OmniRoute/pull/12011)) +- Default new Antigravity-family connections (agy CLI imports and Antigravity OAuth connects) to model auto-sync, so live model discovery lands in the synced catalog and `/v1/models` picks up freshly released upstream models (e.g. Gemini 3.7 Flash tiers) without code changes. Existing connections keep their current setting; the per-connection dashboard toggle remains the opt-out. (#11685 — thanks @MumuTW) ([#11887](https://github.com/diegosouzapw/OmniRoute/pull/11887)) +- **feat(ui):** enable React Compiler (`reactCompiler: true` + `babel-plugin-react-compiler`) for automatic memoization at build time ([#11783](https://github.com/diegosouzapw/OmniRoute/pull/11783)) — thanks @jonlwheat2-gif +- **feat(providers):** add SeekAi (`seekai.cc`) as an OpenAI-compatible New-API gateway — catalog id `seekai` (alias `ska`), `https://seekai.cc/v1`, live `/v1/models` via `passthroughModels`, aggregator-list membership so New-API balance detection can opt in. No referral/aff codes. ([#11786](https://github.com/diegosouzapw/OmniRoute/issues/11786)) ([#12557](https://github.com/diegosouzapw/OmniRoute/pull/12557)) — thanks @HouMinXi +- **feat(zai):** add GLM-5.3-Flash Coding Plan support (1M context, 128K output, vision, `low|high|max` reasoning) and route `zai` GLM-5.3-family API-key traffic through the OpenAI-compatible Coding Plan endpoint with native thinking defaults ([#11801](https://github.com/diegosouzapw/OmniRoute/pull/11801)) — thanks @Neuron-Mr-White +- **feat(combo):** choose how combo models are ordered — manual, provider, score, or name — via a sort control in the dashboard builder, persisted in `config.modelSort` and re-applied on load and after add ([#11812](https://github.com/diegosouzapw/OmniRoute/pull/11812)) — thanks @maxmad64bis +- **feat(free):** custom models can be marked free-tier via `customModels[].isFree`; `isFreeModel()` is the first door and `hidePaidModels` respects it even for providers outside the free budget ([#11843](https://github.com/diegosouzapw/OmniRoute/pull/11843)) — thanks @maxmad64bis +- **feat(nodejs):** add `5dive` as a `configure` target — `omniroute configure 5dive` / `omniroute setup-5dive` write a 5dive auth profile that points an agent fleet's `claude` seats at OmniRoute, with the root-only write, the loopback-vs-`https` endpoint rule and the per-seat model pin handled explicitly ([#11852](https://github.com/diegosouzapw/OmniRoute/pull/11852)) — thanks @5dive-bot +- **feat(sse):** treat `max` as a first-class reasoning-effort tier and clamp per model family (GLM 5.1+/DeepSeek V4+/Kimi K3+ keep native `max`; o1/MiniMax/Grok/Muse Spark clamp to their upstream ceiling) ([#11875](https://github.com/diegosouzapw/OmniRoute/pull/11875)) — thanks @Chewji9875 +- **feat(providers):** the provider plugin manifest now advertises a `usage-fetch` capability for the 40 providers that have a wired usage/quota fetcher, so external dashboards can read it from `GET /api/v1/provider-plugin-manifest` instead of parsing `open-sse/services/usage.ts` after every release. Discovery only — no new fetcher, no quota change, and the Dashboard quota widget stays gated by `USAGE_SUPPORTED_PROVIDERS`. `USAGE_FETCHER_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/fetcherProviders.ts`) and is re-exported from `services/usage.ts`, keeping the manifest module a light leaf instead of pulling the ~490-module usage dispatcher into the manifest route. ([#11903](https://github.com/diegosouzapw/OmniRoute/pull/11903)) — thanks @maxmad64bis / @pacocartones +- **feat(plugins):** `OMNIROUTE_PLUGINS_DIR` sets the directory the runtime plugin scanner reads — and the root the plugin manager installs into — overriding the `HOME`-derived default, so a Docker/K8s deployment can point straight at its bind-mounted plugin tree instead of moving `HOME` just to relocate the scan path. An image that exports no home no longer scans `/tmp/.omniroute/plugins` in silence: the resolved directory is logged once at startup as `scanner.dir_resolved`, naming the input that won. Unset, behaviour is unchanged. Distinct from the CLI-only `OMNIROUTE_PLUGIN_PATH`, which finds `omniroute-cmd-*` command packages and never reached this scanner ([#11906](https://github.com/diegosouzapw/OmniRoute/pull/11906)) — thanks @amaleta / @pacocartones +- **feat(leases):** add an explicit owner-authenticated status action that returns only the active lease's privacy-safe configured connection and provider labels, with generation fencing and no credential or internal-id disclosure ([#11910](https://github.com/diegosouzapw/OmniRoute/pull/11910)) — thanks @KaspaPulse +- **feat(providers):** import-from-file modal shows per-row API errors and ships a downloadable CSV template ([#12071](https://github.com/diegosouzapw/OmniRoute/issues/12071)) ([#12504](https://github.com/diegosouzapw/OmniRoute/pull/12504)) — thanks @HouMinXi +- **feat(providers):** dashboard search matches connection name and `baseUrl` so imported OpenAI-compat nodes surface on the provider card ([#12108](https://github.com/diegosouzapw/OmniRoute/issues/12108)) ([#12495](https://github.com/diegosouzapw/OmniRoute/pull/12495)) — thanks @HouMinXi +- **feat(routing):** add a `score` Auto router strategy that selects the highest configured weighted score and reuses `explorationRate`. ([#12155](https://github.com/diegosouzapw/OmniRoute/pull/12155)) — thanks @jacobsparts +- Add a runtime feature flag to disable universal context handoffs globally without changing the default behavior. ([#12167](https://github.com/diegosouzapw/OmniRoute/pull/12167)) — thanks @jacobsparts +- **feat(providers):** the provider plugin manifest now also advertises a `usage-supported` capability for the 46 providers whose usage API is accepted by the server and Dashboard routes, so integrators can distinguish "the server will serve quota for this provider" from "a fetcher is wired" without reading TypeScript. Discovery only — no fetcher or quota change. `usage-fetch` resolves on id or alias (the usage dispatcher accepts both); `usage-supported` resolves on id alone, matching the runtime guard `USAGE_SUPPORTED_PROVIDERS.includes(providerId)`. `USAGE_SUPPORTED_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/supportedProviders.ts`) and is re-exported from `providers.ts`, mirroring the `fetcherProviders` leaf from #11903 and keeping the manifest a light module. ([#12214](https://github.com/diegosouzapw/OmniRoute/pull/12214)) — thanks @maxmad64bis +- **feat(rankings):** order Free Provider Rankings by what each provider actually served — `GET /api/free-provider-rankings?sortBy=reliability` and a "Most reliable first" toggle on the page. Providers with too few calls to state a success rate keep their score order below the measured ones; the default order is unchanged ([#12218](https://github.com/diegosouzapw/OmniRoute/pull/12218)). — thanks @maxmad64bis +- **perf(sse):** defer `cloneLogPayload()` in the structured SSE collector until after the `maxEvents`/`maxBytes` cap check, eliminating ~9,800 wasted `structuredClone` calls per streaming response (65–71% faster `push()`). Reducer snapshot isolation restored for OpenAI and Responses summaries ([#12241](https://github.com/diegosouzapw/OmniRoute/pull/12241)) — thanks @PauloHSOliveira ([#12243](https://github.com/diegosouzapw/OmniRoute/pull/12243)) +- **feat(usage):** Devin CLI agentic quota (Codeium seat-management GetUserStatus) and OpenRouter key limits plus account credits now surface in Provider Limits ([#12256](https://github.com/diegosouzapw/OmniRoute/pull/12256) — thanks @Neuron-Mr-White) +- **feat(settings):** persist `headroomUrl` through Settings so status/start use the operator URL instead of only `HEADROOM_URL` ([#12306](https://github.com/diegosouzapw/OmniRoute/issues/12306)) ([#12487](https://github.com/diegosouzapw/OmniRoute/pull/12487)) — thanks @HouMinXi +- **feat(auto-combo):** Auto-Combo scoring can now weigh how often a provider/model has actually succeeded. The engine already carried that number on every candidate — 24 hours of usage history behind a ten-sample floor, real-time metrics otherwise — and the scoring function never read it, while the weight table described `stability` as if it did. `reliability` (`1 - failureRate`, with the same field precedence and the same rate-bounding the speed ranking already uses, so a corrupt reading means "nothing observed" rather than "fails every call") is now a declared factor shipping at weight `0`, so routing is unchanged until an operator gives it one, and the `stability` description now matches what that factor computes ([#12317](https://github.com/diegosouzapw/OmniRoute/pull/12317)) — thanks @maxmad64bis +- **feat(routing):** With `freeAccessPolicy: "strict"`, the read-only candidate listing (`GET /v1/auto-combo/{channel}/candidates`) no longer hides the candidates the zero-cost guard excludes — the same read-only transparency the resilience filter already honours (#9133). Each candidate now carries `freeAccessExclusion` saying why it would be kept out, and it tells an exhausted allowance apart from a quota reading that never arrived or went stale, which used to look identical from the outside. Routing is unchanged: the listing reports, it never enforces. The separate `excludeTosAvoid` guard still drops its candidates without a reason; that gap is now documented rather than closed ([#12319](https://github.com/diegosouzapw/OmniRoute/pull/12319)) — thanks @maxmad64bis +- **feat(radar):** The Radar catalog table now shows two facts it was already receiving from the feed and dropping on the floor: the per-model rate limits (requests and tokens, per minute and per day) in a new column, and a badge when a provider's terms state it may train on the prompts you send. A limit of zero renders as zero rather than "rate-only" — for a ceiling those are opposite facts — and a model with no training statement gets no badge, because an absent statement is not a guarantee ([#12320](https://github.com/diegosouzapw/OmniRoute/pull/12320)) — thanks @maxmad64bis +- **feat(radar):** explain Community, single-use, contributor, supporter, recovery, abuse, offers, and privacy rules before either Radar activation action, and remove the superseded fixed-PR grant promise from every UI locale ([#12342](https://github.com/diegosouzapw/OmniRoute/pull/12342)) +- **feat(gamification):** the dashboard Profile page now shows the real daily streak — `/api/gamification/level` returns `streak: { current, longest }` (per key with `apiKeyId`, operator-wide maximum otherwise) and the streak card reads it instead of a hard-coded 0 (#2403) ([#12377](https://github.com/diegosouzapw/OmniRoute/pull/12377)) — thanks @pacocartones +- **feat(gamification):** the dashboard leaderboard now shows each API key's display name under the Name column instead of a truncated key id; `GET /api/gamification/leaderboard` attaches `name` per entry (name only — no key material), while the shared ranking helper and the federation leaderboard stay id-only — thanks @pacocartones ([#12385](https://github.com/diegosouzapw/OmniRoute/pull/12385)) +- **feat(gamification):** enforce the documented 1000 XP/min per-API-key anti-cheat rate limit on the XP award path; over-limit awards are logged and skipped instead of persisted, and the sliding window now matches the timestamp format stored in `xp_audit_log` ([#2403](https://github.com/diegosouzapw/OmniRoute/issues/2403)) ([#12390](https://github.com/diegosouzapw/OmniRoute/pull/12390)) — thanks @pacocartones +- **feat(admin):** localize the gamification anomalies page — the loading state, the Status column and the Suspicious badge now come from the `common` catalog (new `common.suspicious` key propagated to every locale) — add it to the Gamification sidebar group as `gamification-admin` (`/dashboard/gamification/admin`), and expose the loading and empty states as polite `role="status"` live regions (#12401 — thanks @pacocartones) +- **feat(providers):** skip GitHub combo members missing from the live synced catalog, and drop Copilot models that are policy-disabled or hidden from the model picker ([#12473](https://github.com/diegosouzapw/OmniRoute/pull/12473)) — thanks @RaviTharuma +- **feat(i18n):** locale-expansion tooling — `npm run i18n:add-locale` adds a language to config, dashboard, docs mirrors, CLI, README/indexes and site in one command; browser, CLI and cookie detection resolve `uk`, `fil`/`tl`, `zh-Hant` (and the retired legacy `in` → `id`) via config aliases; `config/i18n.json` now ships in the npm package so the published CLI can read it; new real-translation ratio gate (`npm run i18n:check-ratio`, advisory) with a per-locale ratchet baseline; `run-translation --adopt` restores `.i18n-state.json`; `sync-language-bars` generates 🌐 bars from config; `validate_translation.py` loads its allowlist again. Retires the duplicate `in` locale (Indonesian mislabelled as Hindi) — 42 honest locales; saved `NEXT_LOCALE=in` / `OMNIROUTE_LANG=in` keep working. (#12496) +- **feat(api):** Emit gateway-measured `tokens_per_second` (TTFT excluded) on streaming usage and `X-OmniRoute-Tokens-Per-Second` when first-token latency is known ([#12616](https://github.com/diegosouzapw/OmniRoute/issues/12616)) ([#12631](https://github.com/diegosouzapw/OmniRoute/pull/12631)) — thanks @RaviTharuma +- **feat(opencode-plugin): map gateway cost/usage/tok/s onto OpenCode inference payloads** — the official plugin copies `X-OmniRoute-Response-Cost`, token counts, `X-OmniRoute-Tokens-Per-Second` / `usage.tokens_per_second`, TTFT, and the winning `X-OmniRoute-Model` onto the JSON/SSE body OpenCode already consumes. Missing tok/s is left unset (never `tokens / latency`). (#12636) — thanks @RaviTharuma +- **feat(combo):** add combo strategy `quota-weighted`: skip exhausted accounts, then weighted-draw by leftover / in-flight load; existing conversations stay pinned until the account is empty ([#12789](https://github.com/diegosouzapw/OmniRoute/pull/12789)) — thanks @HouMinXi +- **feat(models):** Account live listings become the chat catalog source for Claude, Codex, Copilot, and AGY; public metadata only fills prices on IDs those accounts already list. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **fix(models):** Union `agy` and `antigravity` live catalogs so `agy/gemini-3.8-flash-high` is not rejected after the prefix folds to `antigravity`. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **feat(opencode):** Add `@omniroute/opencode-plugin-v2`, an opencode v2 plugin that gives a v2 install a route to an OmniRoute gateway: it publishes models, combos and auto-combos into the host catalog, refreshes them lazily behind a 300s TTL, and keeps serving the last known catalog from an on-disk snapshot when the gateway is unreachable. The v1 plugin (`@omniroute/opencode-plugin`) is untouched and keeps working as before. The plugin adapts to the host instead of assuming one shape: it reads the shape the host seeds into the catalog draft and publishes the `api` block, the pre-`api` top-level fields, or both. The gateway key comes from the host credential store when one is connected, so no secret needs to sit in a config file; a source the gateway refuses is named in a warning instead of silently degrading the catalog to raw model ids; and tool schemas bound for a Gemini model are stripped of the JSON-Schema keywords Gemini rejects, so tool calling survives that route ([#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870)) — thanks @maxmad64bis +- feat(services): show sanitized CLIProxyAPI account health from its authenticated management API without exposing credentials, file paths, or raw account metadata (#6342) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add AnySearch free web search + URL extract (webFetch) with typed results, credential validation, REST routing, and MCP selection - fallback-only ([#11690](https://github.com/diegosouzapw/OmniRoute/pull/11690)) — thanks @Xxx91n +- **feat(dashboard):** display clamped `[0, 100]%` cached input token ratio in request logs table ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) — thanks @b3nw +- **feat(catalog):** add `OMNIROUTE_DISABLE_THINKING_LEVEL_VARIANTS` feature flag to optionally filter out thinking level variants from model catalog ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) — thanks @b3nw +- **feat(dashboard):** continuously export call logs to external analytics stores. A pluggable destination registry ships the full Logs-tab record set on an hourly `JobRegistry` cron, with a persisted per-destination cursor, batched inserts, a config UI rendered from each destination's own field descriptors, and a REST layer (`/api/log-export/*`) for CRUD, a connection test, and an on-demand run. A destination can opt into `includeBodies` to also ship the request and response payloads shown in the Logs detail pane, including the client and provider views of each call; this is off by default, and payloads inherit the dashboard's PII sanitisation, secret redaction and `noLog` handling. Google BigQuery is the first destination, using a service-account key stored encrypted at rest and streaming inserts keyed by call-log id, into a table that is day-partitioned on `timestamp` and clustered on `api_key_name`, `provider`, `model` and `status`. ([#11945](https://github.com/diegosouzapw/OmniRoute/pull/11945)) — thanks @dpozimski +- **feat(providers):** add MaxAI as a signed, OpenAI-compatible provider serving its 13 paid chat models (GPT-5.6 / Luna / Thinking, Claude 5 Sonnet, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite, Grok 4.1-fast / 4.5, DeepSeek V3.2 / R1, Llama 3.3 70B) through OmniRoute's `/v1` endpoint, with per-request HMAC-SHA1→SM3→AES request signing, live model + context-window discovery from `/models/get_config`, and prompted tool-calling translated to OpenAI `tool_calls` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI vision input — image_url content parts are forwarded inline in `message_content` to the 6 vision-capable models (GPT-5.6 / Luna / Thinking, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite) ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI image generation — 6 image models (gpt-image-1, dall-e-3, flux-1-schnell/dev/pro, sd3-medium) exposed through `POST /v1/images/generations` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI document RAG — inline base64 file/document attachments are uploaded to MaxAI (content-addressed `doc_id`) and attached to the chat via `doc_list` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** browserless MaxAI onboarding — email device-pair login (`/api/providers/[id]/login`) and signed access-token refresh, so a connection can be created and kept fresh without a real browser or Google OAuth ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** per-provider TLS impersonation profile (MaxAI presents a Windows Firefox-150 client fingerprint) so its bot-sensitive endpoints accept OmniRoute traffic ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** add **Nimble** as a web-search and web-fetch provider (`nimble-search`) — `/v1/search` routes to Nimble's search API at `lite` depth (locale, freshness and include/exclude domain filters mapped onto the shared request contract), and `/v1/web/fetch` routes to Nimble Extract, which covers all four fetch formats (`markdown`, `html`, `links`, `screenshot`) from a single call. One API key serves both surfaces. ([#11620](https://github.com/diegosouzapw/OmniRoute/pull/11620)) — thanks @wildcard +- The npm publish is automatic again, through npm Trusted Publishing (OIDC): the hosted `stage-npm` job publishes with a short-lived credential minted from GitHub's id-token — no `NPM_TOKEN`, no 2FA prompt, provenance attached. `publish_mode=staged` (owner approves with 2FA) and `direct` (token) remain available on `workflow_dispatch`. ([#11931](https://github.com/diegosouzapw/OmniRoute/pull/11931)) +- **feat(providers):** Add **Opper** as an API-key gateway provider — EU-hosted AI gateway with 700+ models from 30+ providers behind one OpenAI-compatible API and one key (`OPPER_API_KEY`); model ids use `provider/model` format (e.g. `anthropic/claude-sonnet-4-6`, `openai/gpt-5`); live model catalog at `https://api.opper.ai/v3/compat/models`; entry mirrors `requesty` (same shape, `passthroughModels: true`, no static seed) ([#11629](https://github.com/diegosouzapw/OmniRoute/pull/11629)) +- **feat(dashboard):** the `/dashboard/orchestration` snapshot hook now subscribes to the `agents` WebSocket channel (`agent.task.updated`) instead of `requests` as its refetch trigger, and relaxes its background poll from 5s to 30s while that WS connection is up — falling back to the tighter 5s cadence, reprogrammed live on any connect/disconnect transition, whenever the socket is down. ([#12409](https://github.com/diegosouzapw/OmniRoute/pull/12409)) +- **feat(dashboard):** Orchestration canvas quick wins — search box plus state/source/provider filter chips with a one-click clear, and per-source collapse/expand, all reflected in the URL so a filtered/collapsed view is shareable and survives a refresh; the detail drawer gained a "copy trace JSON" action and hardened error/empty-state and accessibility handling; the Agents-tab edges now animate traveling particles along active (running) connections; and the canvas node/edge status colors moved off fixed hex values onto theme-aware `--orch-status-*` CSS custom properties, so they adapt correctly to light/dark mode. ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- **chore(dashboard):** Orchestration UI hardening pass and the missing component/model test coverage it called for — `OrchestratorNode`/`ActivityNode`/`OverflowNode` rendering, the `?node=`/overflow-click page routing, the Agents-tab orchestrator-click no-op and `showCompleted` toggle, and the overview kanban's done-column sort order (#12270, #12271). ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- New `/dashboard/orchestration` page: live unified view of everything running — Cloud Agent, A2A and Conductor as a real-time graph (Agents tab), the combo cascade (Routing tab, reusing the Combo Live Studio) and a state kanban (Overview tab), with a detail drawer (trace, cost, approve/cancel). Read-only over existing APIs — no new backend. Canvas concept credit: PR #11815 design ([#12261](https://github.com/diegosouzapw/OmniRoute/pull/12261)) +- **feat(dashboard):** new "History" tab on `/dashboard/orchestration` — an Airflow-style grid of finished runs over a 24h/7d/30d preset window, one row per (source, identity), clicking a cell opens the existing detail drawer. It is backed by real persistence: A2A task lifecycle transitions are now written to the `a2a_tasks` table (purged after 30 days, configurable via `OMNIROUTE_A2A_HISTORY_RETENTION_DAYS`) and served by the new `GET /api/a2a/tasks/history` listing endpoint, with the task-detail route falling back to persisted history once a run leaves the in-memory snapshot. Conductor runs stay remote and are not persisted locally — the tab says so instead of silently omitting them. ([#12479](https://github.com/diegosouzapw/OmniRoute/pull/12479)) +- **feat(dashboard):** the orchestration detail drawer gained a "Repeat" action for Cloud Agent, A2A and Conductor tasks — a two-click confirm (click once to arm, click again within the confirm window to fire) re-submits the original prompt/input as a new run. The button is disabled with an explanatory tooltip whenever the original input can't be recovered from the loaded task detail (e.g. it never carried a prompt, or the detail failed to load). Two limitations of the A2A variant, by design: it targets the `/a2a` JSON-RPC endpoint, which authenticates with an API key only (`REQUIRE_API_KEY=true` or a configured `OMNIROUTE_API_KEY` makes a dashboard-session repeat answer `HTTP 400` — surfaced verbatim in the drawer's error line, never as a success), and the `message/send` call is SYNCHRONOUS: the POST blocks for the whole skill run, so the success confirmation only appears once the repeated task finishes. A dashboard-authenticated A2A creation path is deliberately left to a follow-up — widening the endpoint's auth posture is an operator decision, not a side effect of this feature. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(a2a):** A2A task execution now records which memories were consulted for the task's last user message as `metadata.memoryHits` (id/key/type/content-snippet) plus a `memory_hits` history event, purely for observability — the retrieved memory is never injected into a skill's prompt or behavior. Gated by the `OMNIROUTE_A2A_MEMORY_HITS` kill-switch (default enabled; set to `0` to skip the recall lookup entirely). The drawer's new "Memory used" section lists these hits for a2a tasks and is omitted whenever there are none. Known limitation: recall only resolves under the keyless posture — a keyed caller's task owner is a SHA-256 prefix of the API key, while memory rows are keyed by the database api-key id, and no hash→id lookup exists today, so the hit list stays empty for keyed callers. The recorded hits are also kept out of the task's own `input` (and therefore out of the persisted input and of the "Repeat" request body), so repeating a task never re-sends the previous run's memory snippets. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(providers):** add a Perplexity Agent API provider (`perplexity-agent` / `pplx-agent`) for Perplexity `/v1/responses`, including the documented Anthropic, OpenAI, Google, xAI, DeepSeek, Z.AI, Moonshot/Kimi, NVIDIA, and Perplexity model IDs plus Anthropic-model `max_output_tokens` compatibility. ([#12103](https://github.com/diegosouzapw/OmniRoute/pull/12103)) — thanks @AIB1TAL0S +- **feat(providers):** add RPD (Requests Per Day) limit to provider rate limit overrides across UI, schemas, DB, and i18n ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) — thanks @b3nw +- **feat(providers): add UC Direct (uncensored.com Developer API), the metered OpenAI-compatible surface.** A standard OpenAI-compatible passthrough (default executor) for uncensored.com's official REST API at `https://api.uncensored.com/api/v1`: `X-api-key` auth (never-expiring `uai_sk_live_` key), `POST /chat/completions` with streaming SSE and native tool-calling, and the full live metered catalog (82 models across 15 providers, discovered from the public `GET /v1/models`). Registered as provider `uc-direct` (alias `ucd`). Complements the un-metered `uc` persona provider — same models, metered credits and a plain API key instead of a subscription session. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(providers): add UC (uncensored.com), the un-metered subscription "persona" chat as an OpenAI-compatible provider.** A WebSocket web-app port: a durable Clerk credential mints a short-lived session token per connect (browserless — no API key), driving UC's persona socket. Ships the browserless email-code login (request → verify → harvest), the 19 verified persona models (Claude Opus, Gemini, Grok, GLM, Kimi, DeepSeek, MiniMax, incl. the uncensored variants), prompted `` tool-calling with a per-model code-style dialect + auto-cure retry for guardrailed models, live ``/reasoning split, streaming + non-streaming OpenAI responses, and full quota/auth error surfacing (paywall / message-limit / rate-limit → 429, invalid session → 401 re-login). Full multimodal parity via the persona blob-upload layer: **vision** (image input, 15 vision-capable models), **document RAG** (PDF/doc upload, server-side extraction), **image generation** (22 models), **video generation** (14 models, async signed-url → poll), and **TTS** (streaming MP3). Registered as provider `uc` (alias `ucn`). The metered OpenAI-compatible Developer API is a separate `uc-direct` provider. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(logs):** add copy-all button to request detail modal ([#11083](https://github.com/diegosouzapw/OmniRoute/pull/11083)) — thanks @AndrianBalanescu +- **feat(check):** make serviceKinds required and add the reverse-walk provider consistency gate ([#11392](https://github.com/diegosouzapw/OmniRoute/pull/11392)) — thanks @Tushar49 +- **feat(providers):** only advertise verified-working connections by default ([#11448](https://github.com/diegosouzapw/OmniRoute/pull/11448)) — thanks @alltomatos +- **feat(resilience):** add hierarchical concurrency admission ([#11493](https://github.com/diegosouzapw/OmniRoute/pull/11493)) +- **feat(providers):** derive + surface expiry for JWT-bearing web cookies (#11497) ([#11505](https://github.com/diegosouzapw/OmniRoute/pull/11505)) — thanks @oyi77 +- **feat(tunnels):** support named cloudflared tunnels via CLOUDFLARED_CONFIG ([#11561](https://github.com/diegosouzapw/OmniRoute/pull/11561)) — thanks @ggiak +- **feat(compression):** make proactive context-compression threshold a live setting ([#11564](https://github.com/diegosouzapw/OmniRoute/pull/11564)) — thanks @ragnar-claude +- **feat(resilience):** add provider peak-hour protection ([#11622](https://github.com/diegosouzapw/OmniRoute/pull/11622)) — thanks @Neuron-Mr-White +- **feat(providers):** add 1min.ai provider ([#11631](https://github.com/diegosouzapw/OmniRoute/pull/11631)) — thanks @zero-executioner +- **feat(dashboard):** collapsible JSON tree viewer for request/response payloads ([#11703](https://github.com/diegosouzapw/OmniRoute/pull/11703)) — thanks @hartmark +- **feat(routing):** add LiquidAI LFM2.5-2.6B free tier via OpenRouter ([#11752](https://github.com/diegosouzapw/OmniRoute/pull/11752)) — thanks @brick30llc-ctrl +- **feat(pricing):** add GLM-5.3-Flash pricing, model specs, and catalog registration ([#11830](https://github.com/diegosouzapw/OmniRoute/pull/11830)) — thanks @AndrianBalanescu +- **feat(routing):** add relayMode for schema-locked context handoffs ([#11839](https://github.com/diegosouzapw/OmniRoute/pull/11839)) — thanks @raheemuddin786 +- **feat(config):** add support for runtime system prompt configuration and hot-reloading ([#11841](https://github.com/diegosouzapw/OmniRoute/pull/11841)) — thanks @raheemuddin786 +- **feat(routing):** expand connection-aware quota prefilter across combo strategies (#11682) ([#11850](https://github.com/diegosouzapw/OmniRoute/pull/11850)) — thanks @HouMinXi +- **feat(models):** reactive discovery sync after Antigravity model-not-found 404 ([#11888](https://github.com/diegosouzapw/OmniRoute/pull/11888)) — thanks @MumuTW +- **feat(models):** auto-aliases and token limits from synced Antigravity discovery ([#11889](https://github.com/diegosouzapw/OmniRoute/pull/11889)) — thanks @MumuTW +- **feat(settings):** raise sticky round-robin limit caps to 1000 ([#12015](https://github.com/diegosouzapw/OmniRoute/pull/12015)) — thanks @adivekar-utexas +- **feat(resilience):** operator-configurable global credential health check interval ([#12043](https://github.com/diegosouzapw/OmniRoute/pull/12043)) — thanks @adivekar-utexas +- **feat(quota):** use official OpenCode Go usage API ([#12124](https://github.com/diegosouzapw/OmniRoute/pull/12124)) — thanks @ddarkr +- **feat(resilience):** default the credential health check sweep to 60 minutes ([#12138](https://github.com/diegosouzapw/OmniRoute/pull/12138)) — thanks @adivekar-utexas +- **feat(quality):** new-code mode for the complexity and dead-code ratchets (Clean as You Code) ([#12142](https://github.com/diegosouzapw/OmniRoute/pull/12142)) +- **feat(dashboard):** orchestration canvas — unified model + snapshot hook (part 1/2) ([#12156](https://github.com/diegosouzapw/OmniRoute/pull/12156)) +- **feat(usage):** add Kilo Code balance and Kilo Pass quotas ([#12178](https://github.com/diegosouzapw/OmniRoute/pull/12178)) — thanks @killer30001000 +- **feat(providers):** refresh vendored ChatGPT Web connector to v4.0.7 ([#12181](https://github.com/diegosouzapw/OmniRoute/pull/12181)) — thanks @backryun +- **feat(providers):** manual "Clear cooldown" action in the cooling panel ([#12224](https://github.com/diegosouzapw/OmniRoute/pull/12224)) — thanks @patrykkopycinski +- **feat(providers):** restore ChatGPT Web via clean-room browser transport ([#12239](https://github.com/diegosouzapw/OmniRoute/pull/12239)) — thanks @backryun +- **feat(sse):** wire the PROVIDER_PROFILES window gate into the global provider cooldown ([#12247](https://github.com/diegosouzapw/OmniRoute/pull/12247)) +- **feat(providers):** modernize CLOVA Studio chat and embeddings ([#12277](https://github.com/diegosouzapw/OmniRoute/pull/12277)) — thanks @backryun +- **feat(browser):** adopt Obscura as primary headless browser engine with Chromium fallback ([#12286](https://github.com/diegosouzapw/OmniRoute/pull/12286)) — thanks @opensource-elearning +- **feat(sse):** full language parity for output styles (es/de/fr/it/ru/zh + autoDetect) ([#12289](https://github.com/diegosouzapw/OmniRoute/pull/12289)) +- **feat(quality):** complete test:scoped — --full map rebuild, stdin selection, CI loader parity (#8084 D1) ([#12353](https://github.com/diegosouzapw/OmniRoute/pull/12353)) +- **feat(providers):** refresh Fable, Cursor, and Devin catalogs ([#12367](https://github.com/diegosouzapw/OmniRoute/pull/12367)) — thanks @backryun +- **feat(video):** redact transcript text from logs and durable memory (#12150 P1) ([#12427](https://github.com/diegosouzapw/OmniRoute/pull/12427)) +- **feat(dashboard):** parent-link, genuine-continuation badge, and modal perf fixes ([#12448](https://github.com/diegosouzapw/OmniRoute/pull/12448)) — thanks @hartmark +- **feat(dashboard):** adaptive context-budget dial on compression panel ([#12488](https://github.com/diegosouzapw/OmniRoute/pull/12488)) — thanks @HouMinXi +- **feat(providers):** refresh Z.ai Web models and browser transport ([#12524](https://github.com/diegosouzapw/OmniRoute/pull/12524)) — thanks @backryun +- **feat(video):** redact raw client-snapshot transcript fields in the detailed log (#12150 P2) ([#12528](https://github.com/diegosouzapw/OmniRoute/pull/12528)) +- **feat(providers):** refresh NVIDIA hosted models ([#12538](https://github.com/diegosouzapw/OmniRoute/pull/12538)) — thanks @backryun +- **feat(quota):** Moonshot Open Platform balance and TPD lock for custom nodes ([#12590](https://github.com/diegosouzapw/OmniRoute/pull/12590)) — thanks @HouMinXi +- **feat(video):** redact transcript in the in-memory pending-request snapshot (#12430 item 6) ([#12596](https://github.com/diegosouzapw/OmniRoute/pull/12596)) +- **feat(dashboard):** link a log entry's Conversation Context to its owning conversation ([#12646](https://github.com/diegosouzapw/OmniRoute/pull/12646)) — thanks @hartmark +- **feat(catalog):** eligibility-gated free-tier bucket ([#12669](https://github.com/diegosouzapw/OmniRoute/pull/12669)) +- **feat(db):** fail-closed previous_response_id continuation for redacted video turns (#12150 P2b) ([#12707](https://github.com/diegosouzapw/OmniRoute/pull/12707)) +- **feat(gamification): pay the documented `streak_bonus` and `badge_unlock` XP rewards.** `XP_REWARDS` listed both rewards but the award pipeline never paid them: the private reward table in `events.ts` omitted them, `updateStreak()` did not report when a streak extended, and badge unlocks carried no XP. Every request that extends a daily streak now pays `streak_bonus × streak length` once per UTC day (guarded by a same-day `xp_audit_log` check), and every badge unlocked through the pipeline pays `badge_unlock` once per badge (guarded by the `user_badges` primary key; `unlockBadge()` now reports whether it inserted). Bonus XP flows through the same `addXp` + level sync + global/weekly/monthly leaderboard path as action XP, so level-ups and rankings include it. The Radar supporter recognition unlock stays XP-free. (#12522 — thanks @pacocartones) +- **feat(providers):** advertise a `free-tier` capability in the provider plugin manifest for every provider with documented free models, so sidecars and dashboards can filter free-capable providers without reading the quota catalog ([#12786](https://github.com/diegosouzapw/OmniRoute/pull/12786)) — thanks @maxmad64bis +- **feat(providers):** Added EURouter as an OpenAI-compatible API-key gateway (`https://api.eurouter.ai/v1`), with live model discovery via `passthroughModels`. Its copy states that models are served by third-party upstreams listed per model, so an EU-based router is not read as EU data residency for inference. ([#13025](https://github.com/diegosouzapw/OmniRoute/pull/13025)) — thanks @ntdatt812 +- **feat(providers):** Added GreenPT as an OpenAI-compatible API-key provider (`https://api.greenpt.ai/v1`), with live model discovery via `passthroughModels`. No free-inference badge: the published docs describe a free API subscription billed per token, not a free tier. ([#13024](https://github.com/diegosouzapw/OmniRoute/pull/13024)) — thanks @ntdatt812 +- **feat(i18n):** 9 new locales — Greek (`el`), Croatian (`hr`), Serbian (`sr`), Lithuanian (`lt`), Estonian (`et`), Latvian (`lv`), Slovenian (`sl`), Maltese (`mt`), Irish (`ga`) — across the dashboard, docs mirrors, CLI, README and the site; OmniRoute now ships every official EU language (51 locales). (#13044) +- **feat(i18n):** 8 new locales — Kannada (`kn`), Malayalam (`ml`), Odia (`or`), Punjabi (`pa`), Nepali (`ne`), Sinhala (`si`), Burmese (`my`), Khmer (`km`) — across the dashboard, docs mirrors, CLI, README and the site (59 locales). The translator now restores the ICU literal escape around angle placeholders and splits oversized docs sections before translating. (#0000) ([#13660](https://github.com/diegosouzapw/OmniRoute/pull/13660)) +- feat(providers): **list Agnes 3.0 Flash as the current free chat model, drop retired 1.5 Flash, add Image 2.0/2.5 Flash plus Video 2.5/2.5 Flash, and discover the live `/v1/models` catalog (including the CN host `api.agnes-ai.cn`).** `agnes-1.5-flash` now forwards to `agnes-3.0-flash`. Video 2.5 polls `GET /v1/videos/{id}` (not the V2.0 `/agnesapi` contract). Live `/v1/models` (2026-09-09) no longer serves 1.5; the wiki marks it deprecated. 3.0 Flash is 512K context / 65,536 max output, same window as 2.5. CN-region keys use the existing per-connection base-URL field, default stays `apihub.agnes-ai.com`. ([#13120](https://github.com/diegosouzapw/OmniRoute/pull/13120)) — thanks @HouMinXi +- **feat(sse):** Codex and OpenAI catalogs list GPT-6 Astra with effort aliases (`-low` through `-ultra`); Codex CLI identity pins `@openai/codex@0.153.4` in lockstep with the image ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **feat(dashboard):** the orchestration History tab gained a "Compare runs" mode — toggling it turns each grid cell into a 2-item selection queue (a 3rd click drops the oldest pick), and picking two cells opens a side-by-side comparison panel instead of the usual detail drawer. The panel fetches both runs' detail the same way the drawer does (falling back to persisted history once a run leaves the live TTL window) and shows, per side: identity/source/state, start time, a signed `right - left` delta for duration/cost/event count, the event timeline aligned by index, and any memory hits. One side's fetch failing never blocks the other, and a delta is only ever computed when both sides have a finite value — otherwise it renders "—", never `NaN`. Comparing two runs from different sources or skills still works; a banner marks the deltas as informational rather than hiding them, since the two runs aren't a strict apples-to-apples pair. ([#12677](https://github.com/diegosouzapw/OmniRoute/pull/12677)) +- **feat(dashboard):** badge a conversation that never reached a clean stop ([#12717](https://github.com/diegosouzapw/OmniRoute/pull/12717)) — thanks @hartmark +- **feat(release):** reconcile-changelog tool + version-anchored fragment aggregation ([#12987](https://github.com/diegosouzapw/OmniRoute/pull/12987)) +- **feat(mcp):** carry context_length through MCP list_models_catalog projection (#12776) ([#13406](https://github.com/diegosouzapw/OmniRoute/pull/13406)) — thanks @KooshaPari +- **feat(cli):** make startup readiness budget configurable (#13369) ([#13433](https://github.com/diegosouzapw/OmniRoute/pull/13433)) — thanks @KooshaPari + ### 🐛 Bug Fixes +- **security(streaming):** sanitize generic mid-stream error messages before emitting OpenAI, + Responses, or Claude SSE failure frames and before diagnostic logging, while preserving raw + failures for internal classification and keeping client disconnects out of provider failure state. ([#12457](https://github.com/diegosouzapw/OmniRoute/pull/12457)) +- **fix(claude):** `blockExtraUsage: false` no longer lets 5h quota preflight skip the account; extra usage is billed after the session bar is gone, so the request must reach Anthropic ([#12803](https://github.com/diegosouzapw/OmniRoute/pull/12803)) — thanks @HouMinXi +- Sanitize HuggingChat conversation-creation and message-send transport failures before they reach client error bodies or provider logs. ([#12467](https://github.com/diegosouzapw/OmniRoute/pull/12467)) +- **fix(dashboard):** Keep local and theme-aware provider SVG icons at a definite layout size so Chromium does not collapse them to 0×0 after the v3.8.50 image-rendering change ([#12054](https://github.com/diegosouzapw/OmniRoute/pull/12054)) — thanks @ponkcore +- **fix(security):** Sanitize provider and runtime failures before public API, SSE and MCP responses and before persistent request, proxy and usage logs, preventing credentials, stack traces and host filesystem paths from crossing those boundaries while preserving stable error codes and useful diagnostics. ([#12506](https://github.com/diegosouzapw/OmniRoute/pull/12506)) +- **fix(cli):** `omniroute tunnel create` no longer crashes with `Cannot read properties of undefined (reading optsWithGlobals)` — removed the duplicate positional argument that caused Commander.js to misalign the action callback parameters ([#12295](https://github.com/diegosouzapw/OmniRoute/issues/12295)) ([#12368](https://github.com/diegosouzapw/OmniRoute/pull/12368)) — thanks @gonisulaimann +- Stop advertising Gemini Live-only models as supported audio endpoints until OmniRoute proxies the bidirectional Live protocol. ([#11710](https://github.com/diegosouzapw/OmniRoute/pull/11710)) — thanks @RaviTharuma +- **fix(kie):** reroute `flux/kontext` off the KIE Market `createTask` flow — it is catalogued with `isMarket: true` but has no Market catalog page, so KIE rejected it with "model name not supported"; it now hits the dedicated `POST /api/v1/flux/kontext/generate` / `GET /api/v1/flux/kontext/record-info` endpoints instead (#11296). ([#11985](https://github.com/diegosouzapw/OmniRoute/pull/11985)) — thanks @hartmark +- **fix(kie):** correct 12 more KIE Market catalog ids that were sent to `createTask` unchanged but diverge from KIE's documented upstream `model` values — GPT Image 2 T2I/I2I (drops the `gpt/` prefix), GPT Image 1.5 T2I/I2I (`gpt-image/` namespace), Seedream 5.0 Lite T2I/I2I (drops the `.0`), all 4 Flux 2 variants (`flux-2/` namespace, generic variant renamed `flex`), and Wan 2.7 Image / Image Pro (dash instead of dot) — each verified individually against the literal example request published on docs.kie.ai. `#11326`'s "everything else already matches" claim was wrong a second time (#11296); `z-image/4.0-*`/`z-image/4.5-*` and `flux/kontext` remain open, documented as unresolved in `KIE_MARKET_UPSTREAM_MODEL_IDS`'s comment pending further verification. ([#11667](https://github.com/diegosouzapw/OmniRoute/pull/11667)) +- **fix(dashboard):** `useApiKeySave.handleSaveApiKey` no longer forces a full upstream `/models` catalog sync on every non-curated provider connection save — callers can now pass `skipModelSync: true` to opt out, so a workflow that only wants to add one manual model no longer floods the provider's available-models list with hundreds/thousands of synced entries. The flag is a client-side intent signal only and is stripped before the connection payload is POSTed to `/api/providers`; default behavior (full sync on save) is unchanged when the flag is omitted (#11324) ([#11664](https://github.com/diegosouzapw/OmniRoute/pull/11664)) +- **Provider connections:** keep `tokenExpiresAt` when a connection is created. The create-path allowlist omitted it, so every insert stored NULL and the dashboard token badge could read a fresh connection as expired until its first background refresh ([#11368](https://github.com/diegosouzapw/OmniRoute/pull/11368)). — thanks @ntdatt812 +- **fix(combos):** the combo builder's precision-select, global-model-search, and manual-entry flows now serialize a model step's `model` string using the provider's already-computed routing-alias prefix (e.g. `oc/`) instead of rebuilding it from the raw canonical `providerId`, fixing the no-auth "OpenCode Free" provider (`opencode`) being routed to the unrelated paid "OpenCode Zen" provider (`opencode-zen`) because `opencode` doubles as a manual routing-prefix override ([#11433](https://github.com/diegosouzapw/OmniRoute/issues/11433)). ([#11670](https://github.com/diegosouzapw/OmniRoute/pull/11670)) +- **fix(radar):** the catalog feed cache now keeps `generatedAt`, the date the feed's data was built, next to `fetchedAt`, the date this install downloaded it (#11435). The feed schema requires that date and the sync path validates it, but the cache dropped it — so a feed fetched minutes ago and one carrying weeks-old figures looked identical to everything downstream, including the dashboard's "Last fetched" line. `getRadarCatalog().meta` and `GET /api/radar/status` now report both dates, the latter as its own field rather than folded into `version` — and omitted entirely for the offers and intel caches, which keep no build date, where a `null` would read as "unknown" rather than "never stored". The dashboard still shows only the fetch time; surfacing the build date there needs a new translated label and is left to a follow-up. Rows cached before migration 163 read back as `null`: unknown stays unknown instead of borrowing the fetch time. The referrals cache has persisted the same date since migration 142. — thanks @maxmad64bis +- **fix(cli):** postinstall no longer fills `JWT_SECRET` and `API_KEY_SECRET` in the installed package's `.env` (#11436). `.env.example` ships both blank on purpose: the server restores them from its durable store, or generates and persists them there, in `ensureSecrets()`. Pre-filling them defeated that — the file lives inside the package directory, so `npm i -g` replaced it and postinstall wrote _different_ values, while `ensureSecrets()` (which only acts on an empty variable) never got to restore the real ones. Both secrets rotated silently on every update, invalidating dashboard sessions and API-key CRCs. `STORAGE_ENCRYPTION_KEY` left the same list for the same reason in #1622; its comment pointed at a function that no longer exists and now names the real provisioning path. — thanks @maxmad64bis +- **fix(cli):** the CLI now says when a loaded `.env` lives inside the installed package directory (#11437). It already announces every env file it reads, without distinguishing the ones that survive an update from the one that does not: `npm i -g` replaces the package directory wholesale, so values set there are gone at the next update, silently. The warning names the durable path to move them to, and fires only when that file actually supplied a value — a file entirely shadowed by a durable one supplied nothing. A development checkout stays silent: there the same path is stable and documented in `SETUP_GUIDE.md`. — thanks @maxmad64bis +- **fix(free-models):** the shared `isFreeModel()` predicate no longer reports catalog entries marked `freeType: "discontinued"` as free, so `hidePaidModels` can't route to Pollinations' seven premium models that now require a paid key ([#11441](https://github.com/diegosouzapw/OmniRoute/pull/11441)) — thanks @maxmad64bis +- fix(ui): let AnySearch use the normal provider-icon fallback when LobeHub has no matching icon (#11449) +- Fixed the v3.8.50 Costs and Analytics dashboards so flat-rate Claude Code usage can be shown as an explicitly requested token-price estimate without changing default billed-cost semantics. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed archived usage retention so each request is priced individually instead of pricing a day's summed tokens once, which understated archived cost whenever a day mixed cache-heavy and ordinary requests. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed the Costs dashboard so it discloses when displayed figures include flat-rate token-price estimates instead of labelling them as billed spend, using the flag the analytics API already returns; the month-end projection and the CSV/JSON exports carry the same marker, and billed-cost mode is unchanged. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- **fix(combo):** attach the same combo-diagnostics trace (`poolSize`/`attemptOrder`/`excluded`/`terminalReason`, plus `x-omniroute-combo-*` headers) to the round-robin strategy's and the nested pipeline/fusion runtime-unit loop's "Maximum combo retry limit reached" 503 that the priority-strategy path already attaches for the identical terminal condition — previously those two paths returned a bare, contextless 503 ([#11462](https://github.com/diegosouzapw/OmniRoute/issues/11462)). ([#11665](https://github.com/diegosouzapw/OmniRoute/pull/11665)) +- **fix(bun):** use native `bun:sqlite` in `bootstrap-env` and `sync-env` to avoid loading `better-sqlite3` N-API addon during Bun startup ([#11468](https://github.com/diegosouzapw/OmniRoute/pull/11468)) — thanks @TheDemonTuan +- **fix(docker-bun):** make `Dockerfile.bun` install reliable with pre-install script helpers and native `bun:sqlite` smoke check ([#11470](https://github.com/diegosouzapw/OmniRoute/pull/11470)) — thanks @TheDemonTuan +- **fix(docker-bun):** remove every vendored `better-sqlite3` native addon from the Bun runtime image so startup cannot abort before the `bun:sqlite` fallback ([#11482](https://github.com/diegosouzapw/OmniRoute/pull/11482)) — thanks @TheDemonTuan +- **fix(autoCombo):** effort/alias model variants (`gpt-5.6-sol-xhigh`, `gpt-5.6`, cursor's `claude-4.6-opus-high`) inherit their base model's task fitness instead of falling to the wildcard 0.5 ([#11492](https://github.com/diegosouzapw/OmniRoute/pull/11492)) — thanks @MumuTW +- fix(db): rate-limit repeated Arena ELO leaderboard fetch-failure warnings instead of logging one per sync attempt (#11500) ([#11989](https://github.com/diegosouzapw/OmniRoute/pull/11989)) — thanks @hartmark +- **fix(db):** dedupe the raw `[Encryption] Decryption failed...` log line emitted by the lazy-decrypt views (`createLazyRowProxy`/`createLazyConnectionView`), which power `getProviderConnections()` and were re-triggering that line on every CredentialHealth/model-sync cycle for the same corrupt or stale-key credential — a fresh Proxy over a fresh row on every cycle meant the per-proxy memoization never suppressed it, unlike the dedup `decryptConnectionFields()` already had since [#9927](https://github.com/diegosouzapw/OmniRoute/issues/9927). Now shares that dedupe tracking so the line logs at most once per credential ([#11500](https://github.com/diegosouzapw/OmniRoute/issues/11500)). ([#11678](https://github.com/diegosouzapw/OmniRoute/pull/11678)) +- **fix(intelligence):** Arena ELO sync stores a synthesized base-model row for every effort/harness variant (`claude-opus-5-max` → `claude-opus-5`, `gpt-5.6-sol-xhigh (codex-harness)` → `gpt-5.6-sol`), so bare model requests reach the synced intelligence layer instead of the static fallback table; the hand-maintained `MODEL_ALIAS_MAP` — which collapsed generations (`gpt-5.5` → `gpt-5`) — is gone ([#11506](https://github.com/diegosouzapw/OmniRoute/pull/11506)) — thanks @MumuTW +- **fix(autoCombo,sse):** catalog hygiene — the static task-fitness table no longer ranks retired models above live ones (dead rows dropped, patterns matched on segment boundaries), the 7 deprecation aliases that forwarded to retired or non-existent models now point at the vendors' published replacements, an alias is no longer rewritten when the serving provider offers the id as-is, and a new `check:model-lifecycle` gate diffs both tables against a vendor lifecycle snapshot ([#11507](https://github.com/diegosouzapw/OmniRoute/pull/11507)) — thanks @MumuTW +- **fix(sse):** stop dropping resolved `thoughtSignature` values on parallel (multi tool-call) turns sent to Gemini 3.x — the claude→gemini and openai→gemini translators previously kept the signature only on the *first* function call of a message, causing Gemini to reject subsequent calls in the same turn with HTTP 400 "Function call is missing a thought_signature"; each function call now keeps its own resolved signature ([#11510](https://github.com/diegosouzapw/OmniRoute/issues/11510)). ([#11669](https://github.com/diegosouzapw/OmniRoute/pull/11669)) +- Treat Docker publishing from a non-default release branch as an expected skip instead of a failed workflow, while preserving the guard that prevents it from overwriting the `next` channel. ([#11527](https://github.com/diegosouzapw/OmniRoute/pull/11527)) — thanks @pacocartones +- fix(search): prefer credentialed providers over duckduckgo-free fallback (#11524) — the fallback-only loop ran before the credentialed-providers loop in `executeWebSearch`, making configured providers unreachable when `duckduckgo-free` was available. ([#11565](https://github.com/diegosouzapw/OmniRoute/pull/11565)) — thanks @vsd2807 +- **fix(sse):** cap the upstream headers-wait phase for STREAMING requests to a client-realistic ceiling (110s, under Codex's own ~120s hard client-abort window) instead of the flat 10-minute `FETCH_TIMEOUT_MS` default — that default was 5x longer than the body-phase readiness watchdog's own adaptive bound, so a request whose upstream never returned any response at all (not even headers, e.g. a stalled NVIDIA target behind a tool-heavy Responses→Chat translation) kept the client connection alive on keepalives only, guaranteeing the client's own patience ran out first with an opaque 499 instead of OmniRoute detecting and failing the stall fast. Non-streaming requests are unaffected — they keep the existing flat default (`open-sse/utils/fetchStartTimeoutPolicy.ts`) (#11526) ([#11668](https://github.com/diegosouzapw/OmniRoute/pull/11668)) +- **fix(ci):** protocol-clients E2E harness boots the peer-stamped custom server (`run-next.mjs dev`) instead of the bare `next` CLI, so `LOCAL_ONLY` locality resolves from the real TCP peer stamp and `/api/mcp/audit` answers 200 (open bootstrap preserved via an `OMNIROUTE_E2E_BOOTSTRAP_MODE=open` credential clear in `run-next.mjs`, pinned to loopback `HOST`) instead of a deterministic 403 (#11535) ([#11549](https://github.com/diegosouzapw/OmniRoute/pull/11549)) — thanks @jonlwheat2-gif +- **fix(docs):** the Auto-Combo engine is described with the number of factors it actually declares. Four different counts were in circulation — 6, 9, 13 and 14 across `AGENTS.md`, four guides and six code comments — while `DEFAULT_WEIGHTS` declares 15. `check:docs-counts` now reads that number from the source, so adding a factor without updating the prose fails CI ([#11545](https://github.com/diegosouzapw/OmniRoute/pull/11545)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **fix(quota-share):** in-flight leases are per request, so an aborted request's slot ages out instead of being kept alive by later traffic on the same connection ([#11547](https://github.com/diegosouzapw/OmniRoute/pull/11547)) — thanks @abhisheksharma2411 +- The free-tier summary route now serves the refreshed Radar catalog when the feed is active — the same numbers the dashboard shows — and states which catalog answered plus its real build date, instead of always reporting release-frozen figures with a stale curation date. The supporter-key live feed stays reserved to authenticated callers of the instance. ([#11550](https://github.com/diegosouzapw/OmniRoute/pull/11550)) — thanks @maxmad64bis +- **sse:** fix LiveWS/embed-WS servers crashing at startup under the Node/tsx runtime — `liveServer.ts`, `embedWsProxy.ts` and `apiBridgeServer.ts` imported `@/shared/utils/httpClientAbortGuard` without the `.mjs` extension, so the client-abort crash guard added by [#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556) was unreachable and every dependent test failed with `ERR_MODULE_NOT_FOUND` ([#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556)). ([#11671](https://github.com/diegosouzapw/OmniRoute/pull/11671)) — thanks @jonlwheat2-gif / @yxyxy +- fix(chat): shed any of the matched provider-node's routing identifiers (public prefix or internal id) before model lookup, so composites like `//` no longer reach upstream verbatim (#11557) — thanks @maxmad64bis +- **fix(cache):** a semantic-cache entry now expires on its TTL instead of surviving until the next UTC midnight, and `dbEntries` stops counting expired rows ([#11573](https://github.com/diegosouzapw/OmniRoute/pull/11573)) — thanks @ntdatt812 +- **fix(api):** `/v1/models` schedules its stale-while-revalidate rebuild through Next's `after()` again, so a stale catalog reaches the client before the rebuild blocks the event loop ([#11574](https://github.com/diegosouzapw/OmniRoute/pull/11574)) — thanks @ntdatt812 +- **fix(check):** the `known-symbols` executor conformance gate awaits the now-async `getExecutor()`, so it stops reporting all 142 registered aliases as dead symbols and can detect a lazy import that fails ([#11582](https://github.com/diegosouzapw/OmniRoute/pull/11582)) — thanks @ntdatt812 +- **fix(pack):** register `bin/cli/utils/volatileEnvPath.mjs` in `PACK_ARTIFACT_REQUIRED_PATHS`, so the prune can no longer silently drop a module the published CLI imports on every boot ([#11588](https://github.com/diegosouzapw/OmniRoute/pull/11588)) — thanks @ntdatt812 +- **i18n(vi):** translate the five reliability-column strings on the free-provider rankings page, which rendered as `__MISSING__:` markers in the Vietnamese UI ([#11589](https://github.com/diegosouzapw/OmniRoute/pull/11589)) — thanks @ntdatt812 +- **resilience:** restore the expired-connection retry-budget probe in the token-health sweep — the `!isGitHubAccessTokenOnlyConnection` carve-out reintroduced by #11608 contradicted the boundary pinned by #11592, so a GitHub connection parked at `expired` with retry budget remaining was never probed and could never self-heal ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)). ([#11672](https://github.com/diegosouzapw/OmniRoute/pull/11672)) — thanks @jonlwheat2-gif / @ntdatt812 +- **fix(dashboard):** Provider Stats now displays combo success rates on their native 0–100 percentage scale instead of multiplying them twice ([#11595](https://github.com/diegosouzapw/OmniRoute/pull/11595)) — thanks @pacocartones +- **fix(providers):** route DuckDuckGo Web Chat through the verified `duck.ai` endpoint, refresh its free model catalog, and avoid forwarding unsolved anti-abuse challenges ([#11597](https://github.com/diegosouzapw/OmniRoute/pull/11597)) — thanks @solstxce +- **fix(ui):** Console log Refresh and Copy controls now expose localized accessible names, keep copy actions visible on keyboard focus, and announce copy completion safely ([#11599](https://github.com/diegosouzapw/OmniRoute/pull/11599)) — thanks @pacocartones +- Dropped the stale-`.eslintcache` `restore-keys` fallback from both "Restore ESLint file cache" steps in `ci.yml`, so the blocking `Lint` job can no longer be served per-file verdicts computed under a different lint config, suppressions file or lockfile. `quality.yml` had already dropped it in #11963; `ci.yml` — the workflow that actually gates PRs — had not (#11600). ([#11996](https://github.com/diegosouzapw/OmniRoute/pull/11996)) +- fix(ci): pass `--pass-on-unpruned-suppressions` in `run-eslint-json.mjs` so the CI Lint job no longer fails when a suppression is merely orphaned by a genuine fix, mirroring the identical fix already in `validate-release-green.mjs` (#11600) ([#11983](https://github.com/diegosouzapw/OmniRoute/pull/11983)) — thanks @hartmark +- fix(handoff): enforce provider allowlist for universal handoff (#11602) — universal handoff now skips summarization when the selected summary model's provider is not included in the configured provider allowlist. ([#11619](https://github.com/diegosouzapw/OmniRoute/pull/11619)) — thanks @vsd2807 +- **fix(dashboard):** Show a stable error state when Search Analytics returns an HTTP error or malformed data ([#11603](https://github.com/diegosouzapw/OmniRoute/pull/11603)) — thanks @pacocartones +- **test(gamification):** pin the aggregate profile level to the XP-derived semantics of #11604 — `getAggregateXp()` now derives `currentLevel` from the summed XP (`calculateLevel(sum)`), not `MAX(stored current_level)`, and the #3484 fixture levels are aligned with the XP curve ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)). ([#11676](https://github.com/diegosouzapw/OmniRoute/pull/11676)) — thanks @jonlwheat2-gif / @pacocartones +- **fix(dashboard):** Keep the Profile level and progress aligned with aggregate XP, including bounded handling for invalid totals ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)) — thanks @pacocartones +- **fix(dashboard):** Prevent locked hidden badges from revealing their icon or opening private badge details before they are earned ([#11605](https://github.com/diegosouzapw/OmniRoute/pull/11605)) — thanks @pacocartones +- **fix(dashboard):** Restore keyboard focus after shared modals close and cancel delayed autofocus during cleanup ([#11607](https://github.com/diegosouzapw/OmniRoute/pull/11607)) — thanks @pacocartones +- **fix(dashboard):** Enable Enter and Space activation for clickable data-table rows without hijacking nested controls ([#11610](https://github.com/diegosouzapw/OmniRoute/pull/11610)) — thanks @pacocartones +- **fix(autoCombo,sse):** vendor-retired catalog ids are dropped from the auto-combo candidate pool and no longer win on leftover `arena_elo` / `user_override` rows; `getModelLifecycleDecision` consults `model-lifecycle.json` (prefix-stripped) so aggregator traffic is not `untracked` for ids the snapshot already knows ([#11625](https://github.com/diegosouzapw/OmniRoute/issues/11625)) ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)) — thanks @MumuTW +- **test(chatcore):** move Codex/Claude combo fixtures off models the lifecycle guard now rejects — `gpt-5.1-codex`/`gpt-5-codex` are vendor-retired (snapshot, #11626) and `claude-3-5-sonnet-20241022` is shut down, so native-passthrough and combo-fallback tests switched to `gpt-5.6-sol` and `claude-sonnet-4.6` ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)). ([#11675](https://github.com/diegosouzapw/OmniRoute/pull/11675)) — thanks @jonlwheat2-gif / @MumuTW +- fix(opencode-plugin): stop a free-tier budget that rounds up across a magnitude from rendering as `1000K`/`1000M` in the model picker — `fmtTokens` chose its unit from the raw token count and then rounded with `toFixed(1)`, so 999,950–999,999 printed as `1000K` rather than `1M` and just under a billion printed as `1000M` rather than `1B` ([#11684](https://github.com/diegosouzapw/OmniRoute/pull/11684)) — thanks @abhisheksharma2411 +- Cap prose-derived Antigravity quota resets at the configured model cooldown maximum while preserving authoritative Retry-After headers and Google RetryInfo hints. (#11695) Thanks @pacocartones. ([#11823](https://github.com/diegosouzapw/OmniRoute/pull/11823)) +- Compare `_rebaseline_*` notes structurally in the ratchet-bank verifier so an object-valued note no longer reports itself as rewritten on an unmodified tree — the nightly `bank-ratchet-shrinks` lane had been aborting on every run since 2026-08-11, leaving 41,672 lines of earned file-size shrinks unbanked (#11702). ([#11892](https://github.com/diegosouzapw/OmniRoute/pull/11892)) — thanks @MumuTW +- **fix(build):** `prepublish.ts` bundles the ChatGPT Web (Codex) MCP bridge through `runBuildTool()` instead of spawning `npx.cmd` raw, fixing the build crash on Node ≥ 20/Windows where `.cmd` shims cannot be spawned without a shell (EINVAL) ([#11704](https://github.com/diegosouzapw/OmniRoute/issues/11704)) ([#11706](https://github.com/diegosouzapw/OmniRoute/pull/11706)) — thanks @jonlwheat2-gif +- fix(codex): keep `parallel_tool_calls:false` on the translated Codex Responses Lite path (#11707) ([#11984](https://github.com/diegosouzapw/OmniRoute/pull/11984)) — thanks @hartmark +- **fix(tests):** the new Gemini Web Images retirement test (#11708) called the now-async `getExecutor()` synchronously, throwing `TypeError: getExecutor(...).getProvider is not a function` instead of asserting Gemini Web chat stays available. Awaited to match the R0.3 executor-registry refactor (#11220). ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(search):** Allow REST and MCP web-fetch requests to select the advertised `anysearch-search` provider explicitly ([#11716](https://github.com/diegosouzapw/OmniRoute/pull/11716)) — thanks @pacocartones +- **fix(providers):** Keep overnight peak-hour protection active after midnight according to the window's configured start day ([#11718](https://github.com/diegosouzapw/OmniRoute/pull/11718)) — thanks @pacocartones +- **fix(providers):** correct all five g4f.space gateway descriptions to document proof-of-work credits or member API keys, variable limits, and the remote prompt boundary instead of promising free keyless access at five requests per minute ([#11738](https://github.com/diegosouzapw/OmniRoute/pull/11738)). +- **fix(db):** `model_capabilities` is created by a migration instead of lazily on the first models.dev sync, so a clean install and an upgraded install converge on the same schema regardless of which features have run ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(ci):** `check:install-upgrade` now fails on an `npm` install truncated by ENOSPC (npm reports it as a warning and still exits 0), authenticates its health probe so the version assertion works against the hardened health payload, frees the clean-install tree before the upgrade phase, and no longer reports a schema divergence computed from a boot that never served ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(routing):** harden session-affinity key derivation against oversized explicit IDs and empty structural-payload collisions, while recognizing Gemini and common text request shapes ([#11746](https://github.com/diegosouzapw/OmniRoute/pull/11746)) — thanks @MumuTW +- **fix(packages/browser-pool):** regenerate `package-lock.json` so the `packages/browser-pool` workspace's locked `playwright`/`@types/node` (and transitives) match its `package.json` specs, fixing cache-only/offline installs (`npm ci --offline`, Nix `buildNpmPackage`) that previously failed with `ENOTCACHED` ([#11747](https://github.com/diegosouzapw/OmniRoute/issues/11747)) — thanks @benjaminkitt ([#11784](https://github.com/diegosouzapw/OmniRoute/pull/11784)) — thanks @geekyNads +- **fix(assets):** Stop distributing 79 provider and CLI logos without verified source/license provenance, keep every provider/tool available through the internal generic icon, and prevent those IDs from falling through to an external icon CDN. ([#11750](https://github.com/diegosouzapw/OmniRoute/pull/11750)) +- **fix(providers):** retire the common `chatgpt-web` / `cgpt-web` provider because its pre-key and proof-of-work provenance could not be cleared ([#11754](https://github.com/diegosouzapw/OmniRoute/pull/11754)) — explicit chat and image requests, slash-prefixed models, persisted aliases, proxy overrides, and combo targets now fail closed with a sanitized HTTP `410` / `PROVIDER_RETIRED` before upstream dispatch. Migration 163 tombstones matching connections and invalidates their active leases without deleting history or widening API-key allowlists. The independent `chatgpt-web-codex` / `cgpt-codex` provider and its MIT-noticed vendored implementation remain available. +- **fix(usage):** quota and usage refresh no longer 409 when an exclusive lease reserves the connection ([#11758](https://github.com/diegosouzapw/OmniRoute/pull/11758)) — thanks @TheDemonTuan +- Keep the embedding registry's vector width and `embedding` type on models when a synced model exists for the same id, so `/v1/models` no longer reports registry-described embedding models widthless or untyped (#11761) — thanks @rahilmavani +- Correct `google/gemini-embedding-001` on the OpenRouter route to 3072 dimensions, the width it returns when `dimensions` is not sent (#11761) — thanks @rahilmavani +- **fix(vision-bridge):** Restrict automatic vision routing and fallbacks to models available in authoritative live provider catalogs, including aliases and effort variants ([#11767](https://github.com/diegosouzapw/OmniRoute/issues/11767)) — thanks @pacocartones ([#11813](https://github.com/diegosouzapw/OmniRoute/pull/11813)) +- **fix(providers):** reclassify Cerebras as a one-time $5 signup credit (payment method required, 30-day validity), not a recurring no-card 1M tokens/day trial ([#11773](https://github.com/diegosouzapw/OmniRoute/issues/11773)) ([#12591](https://github.com/diegosouzapw/OmniRoute/pull/12591)) — thanks @HouMinXi +- fix(cli): stop prepublish from re-rebuilding the already-built ESM-only opencode-plugin dist (#11787) ([#11990](https://github.com/diegosouzapw/OmniRoute/pull/11990)) — thanks @hartmark +- **fix(db):** Fresh installs no longer log a non-fatal `no such table: compression_run_telemetry` warning when retention cleanup runs before the lazily-created telemetry table exists ([#11802](https://github.com/diegosouzapw/OmniRoute/pull/11802)) — thanks @RaviTharuma +- fix(sse): set X-OmniRoute-Selected-Connection-Id on successful combo dispatches so downstream consumers stop falling back to an empty connection id (#11810) ([#11986](https://github.com/diegosouzapw/OmniRoute/pull/11986)) — thanks @hartmark +- **fix(services):** embedded CLIProxyAPI lifecycle checks now use public `/healthz`, while model discovery uses the configured dedicated data-plane API key instead of the management password ([#11811](https://github.com/diegosouzapw/OmniRoute/pull/11811)) — thanks @RaviTharuma +- Fix persisted model steps returning an error in the playground route simulator, and warn when structural steps cannot be simulated. ([#11862](https://github.com/diegosouzapw/OmniRoute/pull/11862)) — thanks @pacocartones +- **fix(providers):** Antigravity's dynamic mitmAlias table no longer routes `gemini-3.7-flash-{high,medium,low}` to a literal tier-suffixed upstream id just because one connected account's own discovery listed it directly — those display ids always resolve through the safe `gemini-3.7-flash-tiered` static alias, so one account's Google-provisioned access no longer 404s every sibling account of the provider ([#11824](https://github.com/diegosouzapw/OmniRoute/issues/11824), [#11651](https://github.com/diegosouzapw/OmniRoute/issues/11651)) ([#11988](https://github.com/diegosouzapw/OmniRoute/pull/11988)) — thanks @hartmark +- **fix(dashboard):** Expose Profile loading, errors, page structure, and XP progress to assistive technologies ([#11838](https://github.com/diegosouzapw/OmniRoute/pull/11838)) — thanks @pacocartones +- **fix(socks):** `Agent.connectTimeout` now bounds both SOCKS handshake and TLS connect, and `family === null` no longer falls back to `fetch-socks` ([#11842](https://github.com/diegosouzapw/OmniRoute/pull/11842)) — thanks @maxmad64bis +- **perf(compression):** OOM mitigations for large payload hashing, memoization, and token estimation ([#11844](https://github.com/diegosouzapw/OmniRoute/pull/11844) — thanks @AndrianBalanescu) +- Excluded Next.js Node File Trace manifests (`*.nft.json`) from the published npm tarball. They are build-time metadata and are never read while serving, but had grown to 668.7 MB — 61% of the package — which pushed the upload past the registry limit and made `npm publish` fail with `413 Payload Too Large`. ([#11864](https://github.com/diegosouzapw/OmniRoute/pull/11864)) +- **fix(cli):** keep the global OmniRoute server `--base-url` separate from provider-node `--endpoint` values in add, update, and validate commands ([#11860](https://github.com/diegosouzapw/OmniRoute/pull/11860)) — thanks @pacocartones +- **fix(config):** Nous Research's `Hermes-4-405B` model now displays as "Hermes 4 405B (Nous Research)" in both the provider registry and the free-model catalog, instead of the mislabelled "Hermes 4 7B" ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#11993](https://github.com/diegosouzapw/OmniRoute/pull/11993)) +- **fix(provider/nous):** inject required user= tag into Nous Research inference requests to resolve upstream 400 "missing tags" error ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#12044](https://github.com/diegosouzapw/OmniRoute/pull/12044)) +- **fix(build):** `npm run build` now fails in one second with a named package and a copy-pasteable fix when npm silently drops an externalised optional native dependency, instead of dying four minutes in with `Module not found: Can't resolve 'better-sqlite3'` ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- **fix(install):** `postinstall` no longer throws `ReferenceError: isAndroid is not defined` — failing the whole `npm install` — when the `better-sqlite3` rebuild fallback times out; the manual-fix guidance is reachable again ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- Fixed the Alibaba free-tier allowlist test that went red on its own once the shipped catalog's `validUntil` (2026-08-27) passed, leaving every PR and `main` with a failing `Unit Tests (1/8)`. The test now builds its own packs with dates it controls, and covers the expired-pack fallback that production has actually been serving. ([#11867](https://github.com/diegosouzapw/OmniRoute/pull/11867)) +- Split the npm registry upload into its own GitHub-hosted job. npm refuses `--provenance` from a self-hosted runner (`422 ... Only "github-hosted" runners are supported`), which blocked the v3.8.50 publish; the heavy verification cannot move to a hosted runner, so it now hands the proven tarball over instead. ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(ui):** the shared `DataTable` loading state no longer reads its decorative ⏳ glyph out to assistive technology, and now carries the same `role="status"` / `aria-live="polite"` / `aria-busy="true"` semantics as `PageLoading` ([#11869](https://github.com/diegosouzapw/OmniRoute/pull/11869)) — thanks @pacocartones +- **fix(api):** Bound the `timeout` accepted by `POST /api/providers/[id]/login` to the same 15s–600s window the Adobe Firefly and Conol browser-login paths already enforce, so a request can no longer pin a headful browser and the single active-login slot with an arbitrarily large poll budget. ([#11871](https://github.com/diegosouzapw/OmniRoute/pull/11871)) — thanks @pacocartones +- **fix(api):** Stop the tunnel and MITM routes from returning raw child-process error text, which disclosed host paths, binary install locations and Tailscale `tskey-*` credentials in a body some of these routes serve to non-loopback callers, and return a real 400 instead of a framework 500 when a tunnel request body fails validation. ([#11872](https://github.com/diegosouzapw/OmniRoute/pull/11872)) — thanks @pacocartones +- **fix(open-sse):** stream TTFT/ITL are sampled from a monotonic clock (`performance.now`) instead of `Date.now`, so an NTP correction or wall-clock jump can no longer inflate a genuine TTFT or produce a negative one that the `ttft >= 0` guard silently drops — protecting the router's OTel/EWMA/`usage_history`/speed-ranking signals ([#11873](https://github.com/diegosouzapw/OmniRoute/pull/11873)) — thanks @pacocartones +- **fix(providers):** xAI `reasoning_effort: "xhigh"` now reaches grok-4.6+ instead of being silently clamped to `"high"` ([#11879](https://github.com/diegosouzapw/OmniRoute/pull/11879)) — thanks @NoxzRCW +- **fix(dashboard):** the providers page no longer crashes into the error boundary when a provider id collides with an `Object.prototype` member (`constructor`, `__proto__`); icon lookups are own-property guarded ([#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)) — thanks @NoxzRCW +- **fix(skills):** injected skill tools declared in shorthand (`{"content": "string"}`) now forward valid JSON Schema, unblocking providers that validate tool schemas strictly such as Zhipu GLM on the Console Go tier ([#11881](https://github.com/diegosouzapw/OmniRoute/pull/11881)) — thanks @NoxzRCW +- **fix(api):** `POST /api/playground/simulate-route` now surfaces `combo-ref` and `provider-wildcard` persisted combo steps with a specific warning (naming the referenced combo, or the unresolved `provider/model` wildcard) instead of folding them into a generic "unsupported step" count; a `provider-wildcard` step is also now included as an unresolved target so the operator can see it is in the route (ported from [#11882](https://github.com/diegosouzapw/OmniRoute/pull/11882) — thanks @NoxzRCW). ([#11930](https://github.com/diegosouzapw/OmniRoute/pull/11930)) +- **fix(translator):** the streaming OpenAI→Claude translator keeps upstream usage, including prompt-cache tokens, when it arrives on a trailing `choices: []` chunk (Fireworks and any upstream using `stream_options.include_usage`) ([#11883](https://github.com/diegosouzapw/OmniRoute/pull/11883)) — thanks @NoxzRCW +- **fix(cli):** `omniroute update --apply` now tells you explicitly whether a running server was detected and, if so, that you must run `omniroute restart` to apply the update — it never restarted anything and previously implied the update was already live once files were installed. The dashboard's npm-mode Update flow (`/api/system/version`) now tries OmniRoute's own PID-file-managed supervisor before falling back to pm2, and reports an honest "restart required" step instead of a silent pm2-only "skipped" that read like a completed update. The server-side latest-version lookup backing the dashboard's update banner also gained `--prefer-online`, closing the same stale-npm-cache class already fixed in the CLI's own copy for #4376 ([#11885](https://github.com/diegosouzapw/OmniRoute/issues/11885)). ([#12005](https://github.com/diegosouzapw/OmniRoute/pull/12005)) +- **fix(resilience):** clear persisted LKGP pins when a target suffers connection/provider exhaustion or is skipped before dispatch due to cooldown/exhaustion/unavailability, preventing subsequent requests from repeatedly prioritizing known-dead providers ([#11911](https://github.com/diegosouzapw/OmniRoute/issues/11911)). ([#12013](https://github.com/diegosouzapw/OmniRoute/pull/12013)) — thanks @HouMinXi +- **fix(sse):** the OpenAI→Claude stream translator now defers the terminal `message_delta`/`message_stop` emission until the real usage block has arrived (or a genuine end-of-stream flush forces it) instead of emitting it immediately on `finish_reason` — previously, when the trailing usage-only chunk (`{"choices":[],"usage":{...}}`) arrived *after* the `finish_reason` chunk (the normal order for Fireworks/vLLM/Together and other `stream_options.include_usage` upstreams), the client-visible `message_delta` still carried stale/zero usage even though `state.usage` was internally corrected too late to matter (ported from [#11915](https://github.com/diegosouzapw/OmniRoute/pull/11915) — thanks @HouMinXi). ([#11933](https://github.com/diegosouzapw/OmniRoute/pull/11933)) +- **fix(sse):** `stripStore()` now forces `store=false` for stateless OpenAI-compatible Responses-API targets (unless the connection has explicitly opted in via `providerSpecificData.openaiStoreEnabled`), instead of only handling the `openai`/`agentrouter` cases — a client-supplied `store` value previously passed through untouched to backends that don't actually persist responses server-side ([#11916](https://github.com/diegosouzapw/OmniRoute/pull/11916) — thanks @HouMinXi). +- **fix(models):** custom provider-node models (synced, custom, and alias-backed) now appear under their configured prefix in the unified catalog when the operator's model-id prefix mode is canonical, instead of being dropped whenever alias-inclusion was otherwise disabled ([#11918](https://github.com/diegosouzapw/OmniRoute/pull/11918) — thanks @HouMinXi). +- **fix(models):** the unified model catalog now suppresses stale static registry models (including effort-tier variants) for any provider whose active connection has an authoritative live synced catalog, not only providers already using exclusive-synced-listing — a connection with `providerUsesAuthoritativeLiveCatalog` previously kept serving both the live-synced models and the stale static rows side by side ([#11919](https://github.com/diegosouzapw/OmniRoute/pull/11919) — thanks @HouMinXi). +- **fix(ui):** `ProviderIcon`'s three remaining unguarded lookups (`PROVIDER_ICON_ALIASES`, `LOCAL_SVG_ALIASES`, `THEMED_SVGS`) now use `Object.hasOwn()` own-property checks — a provider id such as `constructor` or `__proto__` previously resolved through the prototype chain instead of falling through to the unknown-provider CDN fallback (`getLobeProviderIcon()` itself was already guarded by [#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)); `ProviderPageHeader`'s `color` field is also now optional, matching the rest of the component's defensive typing (ported from [#11920](https://github.com/diegosouzapw/OmniRoute/pull/11920) — thanks @HouMinXi). ([#11935](https://github.com/diegosouzapw/OmniRoute/pull/11935)) — thanks @NoxzRCW +- fix(ollama): preserve multi-byte UTF-8 content split across stream chunks in the Ollama NDJSON transform, which previously corrupted CJK/emoji into U+FFFD (#11921) — thanks @pacocartones +- **fix(providers):** OrcaRouter chat requests now target `/v1/chat/completions` instead of the bare `/v1` API root, fixing the upstream `404 Invalid URL (POST /v1)` ([#11923](https://github.com/diegosouzapw/OmniRoute/pull/11923)). — thanks @echel0nn +- **fix(plugins):** deliver the `onStreamComplete` event to disk-installed plugins. The event shipped in v3.8.50 (#9669) was emitted internally but had no plugin-facing wiring, so no plugin could ever subscribe: the manifest schema silently dropped `hooks.onStreamComplete`, and the loader/manager only knew the seven legacy hooks. `onStreamComplete` is now a declarable manifest hook, wired through the loader and registered by the manager like the other hooks, and its payload carries a `requestId` so consumers can correlate the stream-completion event with the originating request ([#11934](https://github.com/diegosouzapw/OmniRoute/pull/11934)) — thanks @amaleta / @pacocartones +- **fix(db):** the Qdrant embedding-model dropdown now lists local/self-hosted providers (Ollama, LM Studio, vLLM, etc.) — an active connection is treated as "configured" when the provider allows an optional API key, not only when it has a real key or OAuth, so a running local embedding provider is no longer hidden from the picker ([#11949](https://github.com/diegosouzapw/OmniRoute/issues/11949)) ([#11995](https://github.com/diegosouzapw/OmniRoute/pull/11995)) +- **fix(providers):** Vertex AI Anthropic partner-model discovery now calls the Model Garden `v1beta1` publisher list (`/v1beta1/publishers/anthropic/models`, global) and parses its `publisherModels` envelope, so Claude models auto-synced from Vertex populate the active live catalog and route at request time instead of returning `Model '' is not available in the active live catalog` ([#11991](https://github.com/diegosouzapw/OmniRoute/issues/11991)) — thanks @fabioluissilva ([#11998](https://github.com/diegosouzapw/OmniRoute/pull/11998)) +- **fix(cli):** support `--base-url` alongside `--endpoint` in `omniroute nodes add`, `update`, and `validate` subcommands to prevent global `--base-url` shadowing issues ([#11999](https://github.com/diegosouzapw/OmniRoute/issues/11999)). ([#12033](https://github.com/diegosouzapw/OmniRoute/pull/12033)) — thanks @HouMinXi +- **fix(providers):** `cloudflare-ai` no longer refuses image content parts for every Workers AI model ([#12002](https://github.com/diegosouzapw/OmniRoute/pull/12002)) — the plain-string `content` requirement behind #2539 is carried by the _model_ schema, not by the `/ai/v1/chat/completions` endpoint (measured: an all-text part array returns 200 on `@cf/mistralai/mistral-small-3.1-24b-instruct`, `@cf/meta/llama-4-scout-17b-16e-instruct` and `@cf/meta/llama-3.3-70b-instruct-fp8-fast`, and 400 on the text-only `@cf/qwen/qwen2.5-coder-32b-instruct`). `transformRequest()` flattened every array and threw on the first non-text part (#6390), so image input was refused for vision-capable Cloudflare models that accept it. All-text arrays are still flattened — the one shape every model accepts — while an array carrying a non-text part is passed through untouched, so the attachment is still never silently dropped. Regression guards: `tests/unit/cloudflare-ai-image-parts-6390.test.ts`. — thanks @davidlinfr +- **fix(resilience):** decouple the limiter-managed execution backstop from the queue-wait budget — new `requestQueue.executionMaxWaitMs` (env `RATE_LIMIT_EXECUTION_MAX_WAIT_MS`, default 600000 = 10 min) now feeds Bottleneck's post-dispatch `expiration`, while `requestQueue.maxWaitMs` keeps its documented queue-wait semantics. Previously the queue-wait budget doubled as the execution expiration, so legitimate long-running calls on non-incremental gateways (whole generation buffered before the first upstream byte, e.g. Console Go / Command Code tiers serving GLM models) were killed mid-flight at the queue budget with a false 504 `RATE_LIMIT_EXECUTION_TIMEOUT` — the local limiter undercut the provider-aware upstream fetch-start timeouts. The surfaced 504 message now names `requestQueue.executionMaxWaitMs`; the error keeps the #4165 guarantees (disclaims an upstream timeout, preserves the Bottleneck error as `cause`, branded code + trusted provenance, classified request-scoped so combo falls back). A real queue-wait bound (the `Promise.race` around `limiter.schedule()` sketched in #9533) remains future work. (#12025) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **Call logs:** keep the `error` field when an artifact exceeds the storage cap, instead of replacing it with the omission marker. The error is the only field that says *why* a request failed and is typically ~90 bytes next to the multi-hundred-KB bodies that trip the cap, so dropping it left a size-limited row undiagnosable — a provider outage, a local timeout and an upstream 400 all rendered identically. It is now preserved at every fallback stage, truncated to 4KB if it is itself large ([#12026](https://github.com/diegosouzapw/OmniRoute/issues/12026)). ([#12095](https://github.com/diegosouzapw/OmniRoute/pull/12095)) — thanks @ntdat812 +- **fix(diagnostics):** preserve the error field (truncated to 4KB with a `[truncated: …]` suffix) in every call-log artifact size-limit fallback stage. Previously the minimal fallback replaced the error with `[omitted: call log artifact size limit exceeded]`, so an oversized artifact row showed nothing about WHY the request failed — e.g. 91 of 847 opencode-go 504 rows on one production instance were undiagnosable from the dashboard. Oversized request/response bodies are still omitted exactly as before; the error cap is independent of the payload sizes that tripped the fallback. (#12026) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **fix(sse):** OpenAI Responses clients that declare the native `web_search` tool now receive a spec-shaped `web_search_call` output item with `action.sources` alongside the preserved function-call round-trip, so search results executed through OmniRoute's own search backend are consumable by standard Responses clients (Codex, pi-web-access, …). ([#12031](https://github.com/diegosouzapw/OmniRoute/pull/12031)) — thanks @watchingdogs +- **fix(cli):** use in-thread alias resolver hooks on modern runtimes to avoid deprecation noise and improve Node.js forward compatibility ([#12073](https://github.com/diegosouzapw/OmniRoute/issues/12073)). ([#12083](https://github.com/diegosouzapw/OmniRoute/pull/12083)) — thanks @xiaoyaner0201 +- **fix(combo):** an operator-set **Agent Features → Context length** on a combo is now honored at request time. The value was persisted and advertised through `/v1/models`, but `resolveComboContextLimit()` never consulted it — so a multi-target combo whose members carry no per-model window fell through to the provider's generic `defaultContextLength` (openrouter 128000, command-code 200000) and rejected large requests with `Input exceeds context window … limit 128000` despite the combo being explicitly sized much larger. An identical single-target combo worked, because it collapses to its concrete target before the guard runs. Invalid values (0/negative/NaN/Infinity) are ignored, so the existing target → combo-min → fallback order is unchanged. ([#12090](https://github.com/diegosouzapw/OmniRoute/pull/12090)) — thanks @adivekar-utexas +- **fix(sse):** passthrough streams now estimate usage on finish when upstream closes without usage even with `stream_options.include_usage` — avoids `0 tokens / 0%` for providers that stay silent (and correctly handles trailing empty-choices usage) ([#12151](https://github.com/diegosouzapw/OmniRoute/pull/12151)) — thanks @maxmad64bis +- **fix(combos):** clearing an agent feature in the combos editor now persists — unchecking context cache protection, or emptying the system message or tool filter, sends an explicit `null` instead of dropping the field from the `PUT` body, which the update merge read as "leave unchanged" ([#12177](https://github.com/diegosouzapw/OmniRoute/pull/12177)) — thanks @foreveryh / @ntdatt812 +- **fix(memory):** self-hosted embedding endpoints now vectorize — the vector width is measured from the first embedding that comes back instead of being read from a registry that cannot describe them, so `vec_memories` is created and memories stop piling up unvectorized behind a green health check ([#12180](https://github.com/diegosouzapw/OmniRoute/pull/12180)) — thanks @kanade-hoshino / @ntdatt812 +- **fix(translator):** the leading `system` message now reaches Responses-API upstreams when its `content` is a content-part array — it was read as `typeof content === "string" ? content : ""`, so a prompt-caching client (Anthropic `cache_control`, the shape LiteLLM and the Anthropic SDK emit) had its entire system prompt replaced by an empty `instructions`. The request was still accepted with a normal `prompt_tokens` count, so the model answered with no instructions and nothing in the response said they were missing. Mid-conversation system turns already handled the array shape ([#7056](https://github.com/diegosouzapw/OmniRoute/pull/7056)); only the first one did not ([#12206](https://github.com/diegosouzapw/OmniRoute/issues/12206)). Regression guard: `tests/unit/translator-openai-responses-system-content-parts.test.ts`. ([#12207](https://github.com/diegosouzapw/OmniRoute/pull/12207)) — thanks @Gorillaz322 +- **fix(free-tier):** `/api/free-tier/summary` no longer computes its totals from a Radar feed built before the catalog the running release ships. When the cached feed is older — or carries no build date at all — the route answers from the shipped catalog, resolved through the operator's local model state so disabled and tombstoned models stay out of the numbers ([#12215](https://github.com/diegosouzapw/OmniRoute/pull/12215)). — thanks @maxmad64bis +- **fix(oauth):** Keep a Claude personal workspace and a Team organization as separate connections — they share the same email and `accountUUID`, so the email-only OAuth dedup let the second login overwrite the first account's tokens; `organizationUUID` now disambiguates them, the way `workspaceId` does for Codex ([#12222](https://github.com/diegosouzapw/OmniRoute/pull/12222)) — thanks @davidebaraldo +- **fix(resilience):** a 402 on a single paid model of a passthrough/gateway provider (e.g. `kilo-gateway`, `ollama-cloud`) no longer terminalizes the whole connection with a never-auto-recovered `credits_exhausted` status — only the paid model is locked out, so free models on the same key keep serving. 402 variant of [#3027](https://github.com/diegosouzapw/OmniRoute/issues/3027). Single-credential providers are unaffected — a 402 there is still treated as the key being genuinely out of credit ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239) / [#10616](https://github.com/diegosouzapw/OmniRoute/issues/10616)) ([#12242](https://github.com/diegosouzapw/OmniRoute/issues/12242)) — thanks @brick30llc-ctrl ([#12266](https://github.com/diegosouzapw/OmniRoute/pull/12266)) — thanks @geekyNads +- **fix(sse):** trust `finish_reason: "length"`/`"max_tokens"` over the reasoning-consumed-token ratio in response quality validation, so a reasoning model truncated below the old 90% threshold correctly fails and retries instead of returning empty content as a silent "success" ([#12262](https://github.com/diegosouzapw/OmniRoute/pull/12262)) — thanks @brick30llc-ctrl +- **fix(settings):** `PUT /api/settings/cache-config` now persists `alwaysPreserveClientCache` to the flat general settings the runtime cache-control policy actually reads; previously the value landed in the databaseSettings "cache" section and was silently ignored, so the endpoint had no effect on `cache_control` passthrough ([#12304](https://github.com/diegosouzapw/OmniRoute/pull/12304)) — thanks @davidebaraldo +- **fix(grok-cli):** treat omitted SuperGrokPro `creditUsagePercent` as 0% used so Provider Limits still renders a weekly bar (proto3 zero-elision) ([#12312](https://github.com/diegosouzapw/OmniRoute/pull/12312)) — thanks @HouMinXi +- **fix(combo):** Expose the two Auto-Combo scoring factors nobody could set — the combo validation schema and the dashboard weight sliders both declared 13 of the scorer's 15 factors, so `connectionDensity` (spreads load across a provider's connections) and `quality` were dropped on save and offered nowhere. The sliders also shipped their own default table that differed from the engine's on every non-zero factor and summed to 1.05, so the percentages shown next to them added up to 105%. Both lists now match `DEFAULT_WEIGHTS`, and a test keeps them there. Note that a combo whose stored `weights` omitted the two keys was effectively running with them at zero and the other thirteen renormalized upward; it now runs with the engine's intended distribution, so its routing does shift ([#12314](https://github.com/diegosouzapw/OmniRoute/pull/12314)) — thanks @maxmad64bis +- **fix(docs):** The free-tier reference no longer says its numbers come "confidence tagged per row" — no catalog entry carries a confidence tag and the API serves none, so every figure on that page is an estimate of the same, unstated quality. The page now states what an entry does vouch for: an independently documented hard stop (set by hand with the source in a comment, never defaulted to `true`) and a prompt-training disclosure, both with live counts the `check:docs-counts` gate keeps honest ([#12318](https://github.com/diegosouzapw/OmniRoute/pull/12318)) — thanks @maxmad64bis +- **fix(usage):** `adobe-firefly` and `firefly` have had a working usage fetcher since Adobe Firefly landed, but neither was ever added to the registration list, so the provider-plugin manifest, `genericQuotaFetcher` and the free-access quota cache all reported them as having no usage support — while `USAGE_SUPPORTED_PROVIDERS` said the opposite. Both are now declared, which also means their credit balance is fetched like any other declared provider's: `registerGenericQuotaFetchers` now registers a generic quota fetcher for them, and `resolveFreeAccessState` no longer returns early. A test holds the registration list to the dispatcher's switch in both directions, which is what the module's own docstring already asked for in prose ([#12321](https://github.com/diegosouzapw/OmniRoute/pull/12321)) — thanks @maxmad64bis +- **fix(quota):** drop the generic quota cache (agy / Antigravity / Claude OAuth) on an upstream 429 so reset-aware scoring does not keep a 60s stale snapshot, and force-refresh the next usage fetch so inner provider caches cannot recache the same window ([#12325](https://github.com/diegosouzapw/OmniRoute/pull/12325)) — thanks @HouMinXi +- **fix(combos):** deleting a combo now clears its persisted LKGP pins instead of leaving unreachable `key_value` rows behind ([#12326](https://github.com/diegosouzapw/OmniRoute/issues/12326)) ([#12425](https://github.com/diegosouzapw/OmniRoute/pull/12425)) — thanks @KrzysiekSko +- **fix(sse):** Keep ZWNJ (U+200C) and ZWJ (U+200D) in assistant text, reasoning and tool-call arguments — Persian/Kurdish half-space (`ارائه‌دهنده`), Arabic/Indic shaping and emoji sequences no longer lose them; the response de-obfuscation now removes joiners only between ASCII word characters, where the request side inserts them ([#12186](https://github.com/diegosouzapw/OmniRoute/issues/12186)) — thanks @rezjalibd ([#12359](https://github.com/diegosouzapw/OmniRoute/pull/12359)) — thanks @pacocartones +- **fix(resilience):** count resolved upstream 5xx results against the provider circuit breaker on the chat path — `CircuitBreaker.execute()` no longer reads a resolved `{ success: false, status: 5xx }` as a success that cancels the call-site failure, so a provider answering 503s now trips its breaker instead of staying `CLOSED` at `failureCount: 1`; single-model and combo dispatches are each accounted exactly once ([#12254](https://github.com/diegosouzapw/OmniRoute/issues/12254)) ([#12360](https://github.com/diegosouzapw/OmniRoute/pull/12360)) — thanks @pacocartones +- **fix(providers):** resolve the Codex quota auto-ping model from the live provider catalog and lifecycle registry instead of the retired `gpt-5.1-codex-mini`, and pause the ping with one actionable warning when no selectable Codex model exists rather than retrying a shut-down id every cooldown window ([#11905](https://github.com/diegosouzapw/OmniRoute/issues/11905)) ([#12361](https://github.com/diegosouzapw/OmniRoute/pull/12361)) — thanks @pacocartones +- **fix(api):** keep the `{created, data}` wrapper on combo-routed `/v1/images/generations` responses and default Codex image results to `b64_json` on both `/v1/images/generations` and `/v1/images/edits` so Codex CLI's built-in `image_gen` can decode them ([#12268](https://github.com/diegosouzapw/OmniRoute/issues/12268)) ([#12362](https://github.com/diegosouzapw/OmniRoute/pull/12362)) — thanks @pacocartones +- **fix(sse):** Name the shadowed custom provider node when a built-in provider id/alias (e.g. `openference` → `of`) reserves the prefix of an existing OpenAI/Anthropic-compatible node, so the runtime `No active credentials for provider: ` error explains that the prefix routed to the built-in and never reached the node's healthy connections, instead of contradicting the dashboard ([#11943](https://github.com/diegosouzapw/OmniRoute/issues/11943)) — thanks @morpheus9393 ([#12365](https://github.com/diegosouzapw/OmniRoute/pull/12365)) — thanks @pacocartones +- **fix(i18n):** wrap `ccOnboardingKeyPlaceholder` in ICU single quotes across all 43 locales so angle brackets render literally instead of being parsed as rich-text tags, which crashed the Claude Code onboarding block with `INVALID_MESSAGE: INVALID_TAG` ([#12302](https://github.com/diegosouzapw/OmniRoute/issues/12302)) ([#12369](https://github.com/diegosouzapw/OmniRoute/pull/12369)) — thanks @gonisulaimann +- **fix(models):** publish `effort_tiers` on Kimi K3's synced base-model entries (`kmca/k3`, `kmca/k3-256k`) so catalog-only clients (OpenCode, plain SDK pickers) can see and select the reasoning tiers (`low`/`high`/`max`) the synced metadata already carried — the `isSkippedEffortProvider` gate no longer suppresses tier visibility on those base entries, while synthetic `-` variant generation stays prevented and Codex/GLM base models remain excluded unchanged ([#12299](https://github.com/diegosouzapw/OmniRoute/issues/12299)) ([#12371](https://github.com/diegosouzapw/OmniRoute/pull/12371)) — thanks @gonisulaimann +- **fix(guardrails):** keep `auto`/`auto/*` virtual combos exempt from the Vision Bridge `fixedModel` credential guard so a combo target is passed through instead of silently falling back to global auto-selection ([#12237](https://github.com/diegosouzapw/OmniRoute/issues/12237)) ([#12373](https://github.com/diegosouzapw/OmniRoute/pull/12373)) — thanks @pacocartones +- **fix(combo):** capability-filter exhaustion caused by `max_tokens` above every target's known output limit now reports that reason (requested `max_tokens` vs the pool's highest known ceiling) instead of the unrelated "supports structured output" message ([#12229](https://github.com/diegosouzapw/OmniRoute/issues/12229)) — thanks @DW-MediaLab ([#12374](https://github.com/diegosouzapw/OmniRoute/pull/12374)) — thanks @pacocartones +- **fix(auth):** the `least-used` account strategy now prefers accounts without backoff before falling back to oldest `lastUsedAt`, the same tie-break `round-robin` already applies, so a failover no longer lands on a just-rate-limited account for a single request ([#12279](https://github.com/diegosouzapw/OmniRoute/issues/12279)) — thanks @tenshiak ([#12375](https://github.com/diegosouzapw/OmniRoute/pull/12375)) — thanks @pacocartones +- **fix(docker):** the `chatgpt-web-codex-browser` image now finds the Chrome binary under `chrome-linux64/` (Chrome for Testing layout in `playwright:v1.62.0-noble`) as well as the legacy `chrome-linux/`, so the container no longer crash-loops with `exec: --headless=new: not found` ([#12024](https://github.com/diegosouzapw/OmniRoute/issues/12024)) ([#12376](https://github.com/diegosouzapw/OmniRoute/pull/12376)) — thanks @pacocartones +- **fix(providers):** declare `groq/compound` and `allam-2-7b` as non-reasoning models in the curated Groq registry so `reasoning_effort` / `output_config.effort` / `thinking` from Claude Code are stripped instead of forwarded, which Groq rejected with HTTP 400 ([#12134](https://github.com/diegosouzapw/OmniRoute/issues/12134)) ([#12379](https://github.com/diegosouzapw/OmniRoute/pull/12379)) — thanks @pacocartones +- **fix(executors):** `OpencodeExecutor` no longer forces a direct connection when the connection has a proxy assigned in Proxy Management but no per-account proxies: the single-account fast path used to wrap the upstream dispatch in the direct-egress sentinel, discarding the ambient proxy context the chat handler had pinned from `proxy_assignments`, so API-key `opencode`/`opencode-go` connections egressed from the host IP (and hit geoblocks) despite the assignment. The direct pin is now applied only when no ambient proxy context exists ([#11894](https://github.com/diegosouzapw/OmniRoute/issues/11894) — thanks @hizzt) ([#12380](https://github.com/diegosouzapw/OmniRoute/pull/12380)) — thanks @pacocartones +- **fix(api):** `GET /v1/models` with `MODELS_CATALOG_PREFIX_MODE=canonical` (or `?prefix=canonical`) now lists providers whose registry alias is undefined or equal to their own id (Antigravity, Antigravity CLI and other self-aliased built-ins) — their single `provider/model` id was dropped by the alias/canonical duplicate guard in the static, synced, custom and alias-backed catalog loops ([#12058](https://github.com/diegosouzapw/OmniRoute/issues/12058)) — thanks @cheynetom ([#12381](https://github.com/diegosouzapw/OmniRoute/pull/12381)) — thanks @pacocartones +- **fix(translator):** Drop replayed `thinking` blocks that carry no signature (the shape produced from cross-provider `reasoning_content`) instead of stamping the default Claude signature on them, which Anthropic rejected with `400 Invalid signature in thinking block` on the next turn served by an Anthropic rung ([#12105](https://github.com/diegosouzapw/OmniRoute/issues/12105)) — thanks @atescivitci-cmd ([#12386](https://github.com/diegosouzapw/OmniRoute/pull/12386)) — thanks @pacocartones +- **fix(cli):** Resolve Bun's `--preload` polyfill path against the package root instead of `dist/`, so `omniroute` installed with `bun install -g` no longer crashes at startup with `error: preload not found …/dist/open-sse/utils/setupPolyfill.ts` ([#11980](https://github.com/diegosouzapw/OmniRoute/issues/11980)) — thanks @joglomedia ([#12387](https://github.com/diegosouzapw/OmniRoute/pull/12387)) — thanks @pacocartones +- **fix(providers):** `gemini-business` now publishes its model catalog — `/v1/models` and `/v1/providers/gemini-business/models` list the 12 enterprise Gemini ids the executor understands instead of returning an empty list (#12107) ([#12389](https://github.com/diegosouzapw/OmniRoute/pull/12389)) — thanks @pacocartones +- **fix(db):** install `busy_timeout` before the SQLite connection's first statement so a process opening the database while another one closes its WAL connection waits out the transient EXCLUSIVE lock instead of dying with `database is locked`, and recognise the drivers' real BUSY/PROTOCOL/IOERR errors as transient in the corruption probe so the same lock no longer renames the database away as corrupt; deflakes `cross-process contenders never both acquire the same connection` (#12394 — thanks @pacocartones) +- **fix(chat-admission):** derive the `chat_admission_busy` 503 `Retry-After` from observed heavyweight-lease occupancy — the larger of the exhausted `OMNIROUTE_CHAT_ADMISSION_QUEUE_MS` window and the time since capacity last turned over, capped at 60 s — instead of a fixed 1 s (structural) / 2 s (byte-stage) hint that invited Codex/agent fan-out clients to re-send ~1 MiB `/v1/responses` bodies every second into a gate held for the whole SSE lifetime; an idle gate keeps the historical floors ([#12135](https://github.com/diegosouzapw/OmniRoute/issues/12135)) (#12395 — thanks @pacocartones) +- **fix(api-manager):** the API key permissions modal no longer silently drops `allowedCombos` entries its Combo picker cannot render — routing-rule names such as `rt-*`, which the backend already honours — when "All" is clicked and the key is switched back to "Restrict"; those entries now survive the toggle, are listed read-only under the combo list so the count and the list agree, and are saved back verbatim instead of persisting `[]` (deny-all) (#12397 — thanks @pacocartones) +- **fix(catalog):** write the NUL separator of the catalog connection memo key, the provider serviceKind memo key, the Video Bridge promotion group key and a JSON-exactness test fixture as the `\u0000` escape instead of a raw byte — same runtime value, but the raw byte made git, GitHub and ripgrep treat those files as binary (hidden PR diffs, silently skipped searches); a guard test now keeps raw NUL bytes out of `src/`, `open-sse/` and `tests/` (#12403 — thanks @pacocartones) +- **fix(providers):** add `CLAUDE_CODE_CLIENT_VERSION` and `GITHUB_COPILOT_CLI_VERSION` env overrides so Anthropic/Copilot client-version gates can be unblocked without a rebuild ([#12417](https://github.com/diegosouzapw/OmniRoute/issues/12417)) ([#12632](https://github.com/diegosouzapw/OmniRoute/pull/12632)) — thanks @HouMinXi +- **fix(db):** back-fill `last_ping_at` and `last_pinged_reset_key` on `provider_connections` during schema reconciliation so divergent lineages that skipped `123_quota_auto_ping` still accept quota auto-ping writes ([#12470](https://github.com/diegosouzapw/OmniRoute/pull/12470) — thanks @KooshaPari) +- **fix(ci):** document MIT exceptions for `@eloqnt/{config,format-json,format-po}` (next-intl transitive; locked tarballs omit `license`) and keep the A2A lifecycle vitest off the real SQLite persistence seam ([#12581](https://github.com/diegosouzapw/OmniRoute/issues/12581)) ([#12595](https://github.com/diegosouzapw/OmniRoute/pull/12595)) +- OpenCode plugin `/v1/models` catalog fetch now waits 30s by default and attaches HTTP `statusCode` on 401/5xx so host fallback plugins can hop instead of seeing an untyped AbortError/UnknownError. ([#12607](https://github.com/diegosouzapw/OmniRoute/pull/12607)) — thanks @RaviTharuma +- **CI:** the OpenAPI security-tier gate now mirrors `isAlwaysProtectedPath()` in full — it also reads `ALWAYS_PROTECTED_API_PATTERNS`, so the pattern-gated credential routes (`/api/providers/{id}/{claude,codex}-auth/{export,apply-local}`, GHSA-5926-2w35-7h4q) no longer report as unannotated. (#12605) +- **fix(api):** GET /v1/models no longer waits forever on a hung coalesced catalog rebuild; cold-path waits are bounded (`CATALOG_BUILD_TIMEOUT_MS`, default 8s) and a last-good 200 is served when the rebuild times out ([#12627](https://github.com/diegosouzapw/OmniRoute/issues/12627)). ([#12628](https://github.com/diegosouzapw/OmniRoute/pull/12628)) — thanks @RaviTharuma +- **fix(cursor):** a non-array `tool_calls` on an assistant message no longer crashes the cursor request translator with a `TypeError`; both loops now require an array ([#12689](https://github.com/diegosouzapw/OmniRoute/issues/12689)) ([#12691](https://github.com/diegosouzapw/OmniRoute/pull/12691)) — thanks @soroush5 +- **fix(chat):** Chat Completions no longer return empty `content` after a server-owned memory or skills tool; the first provider send and account/model recovery now share one pipeline so a follow-up round-trip can fill the reply ([#12696](https://github.com/diegosouzapw/OmniRoute/issues/12696)) — thanks @HouMinXi ([#12867](https://github.com/diegosouzapw/OmniRoute/pull/12867)) +- **fix(dashboard):** batch-deleting provider keys no longer toasts failure after a successful delete when the confirm button's click event is forwarded as `onAfter` ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) — thanks @HouMinXi +- **fix(glm):** drop the extra 16th argument to `createSSETransformStreamWithLogger` that TypeScript rejected (TS2554) and that never reached the TransformStream ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) ([#12770](https://github.com/diegosouzapw/OmniRoute/pull/12770)) — thanks @HouMinXi +- **fix(dashboard):** pass `nodeMap` into Runtime `QuotaGroup` so a quota monitor in error/exhausted/alerting no longer throws `ReferenceError: nodeMap is not defined`. ([#12868](https://github.com/diegosouzapw/OmniRoute/pull/12868)) — thanks @HouMinXi +- Restricted API keys whose `allowedModels` lists a combo name no longer skip every combo member at pre-dispatch (`ALL_TARGETS_SKIPPED`). Inner-target filtering still applies when the allow-list is a provider prefix or `disableNonPublicModels` is on ([#12899](https://github.com/diegosouzapw/OmniRoute/pull/12899)). — thanks @HouMinXi +- **fix(combos):** Effort-suffixed combo members inherit the base model's `model_context_overrides` row so priority order is not inverted on large requests ([#12926](https://github.com/diegosouzapw/OmniRoute/pull/12926)) — thanks @HouMinXi +- **fix(catalog):** Picker-added `customModels` enter the dispatch-time live catalog so combo members and bare inference no longer 400 ([#12934](https://github.com/diegosouzapw/OmniRoute/pull/12934)) — thanks @HouMinXi +- **fix(docker):** re-derive the Docker build's worker-pool memory budget from the MEASURED ~4.5 GB per-process RSS (the issue owner's own VPS dmesg OOM-killer reproduction) instead of the stale 2560 MB/worker inference #11419 shipped, and lower `OMNIROUTE_BUILD_WORKERS` 3 → 2 so 1 parent + 1 page-data worker (2 processes × 4.5 GB = 9 GB) fits the 12.288 GB (75%) budget on a 16 GB GitHub Actions runner — the previous default (1 parent + 2 workers = 13.5 GB) still overcommitted the runner and kept "Publish to Docker Hub" failing with `cannot allocate memory` after #11419 merged (#7518). ([#11663](https://github.com/diegosouzapw/OmniRoute/pull/11663)) +- **fix(electron):** the packaged Windows desktop build now passes the #7592 cold-restart smoke end to end. Five defects were found and fixed on the way: (1) optional-pack staging failed on any machine whose `tar` is GNU tar — it reads the drive letter in an absolute `-f C:\...` archive path as a remote rsh host (`Cannot connect to C:`), so staging now passes a bare filename with `cwd` at the tarball directory and surfaces tar stderr on failure; (2) the Electron `files` allowlist was missing `lib/loginHeaderCapture.js`, so the packaged main process crashed at startup with `Cannot find module './lib/loginHeaderCapture'`; (3) electron-builder ≥26 injects an `!**/node_modules/**` ignore into every extraResources pattern list that no positive filter can override, which silently dropped the staged runtime `node_modules` (including the better-sqlite3 N-API prebuild) from `resources/app` — a fresh v3.8.50 desktop build would have shipped with no native SQLite driver and reproduced the #7592 sql.js fallback on every machine; an `afterPack` hook now restores the staged `node_modules` after packing; (4) the packaged-app smoke harness redirected `USERPROFILE` into its temp DATA_DIR, but Electron resolves the Roaming profile from `%USERPROFILE%\AppData\Roaming\` (USERPROFILE takes precedence over the APPDATA env var) and the path service throws instead of creating it, so `requestSingleInstanceLock()` returned false and the app exited(0) silently before `app.whenReady()` — the harness now pre-creates the derived tree, and `ensureSmokeEnvDirs` is exported and unit-tested; (5) the #7592 driver assertion parsed a `[DB] Driver: ...` line that the server's primary DB path never printed (only the unused `openDatabaseAsync()` did) — `getDbInstance()` now emits the same line on open so the guard can actually assert the native driver. Also: the smoke env-allowlist unit test hardcoded POSIX paths and could only pass on Linux/macOS; expectations are now host-agnostic, with new regression tests for the USERPROFILE-derived tree and `tarPack` under absolute Windows-style paths ([#11443](https://github.com/diegosouzapw/OmniRoute/pull/11443)) — thanks @jonlwheat2-gif +- fix(sse): stop the auto-combo candidates inspector from silently dropping model-locked/cooled-down rows (#9133) ([#11994](https://github.com/diegosouzapw/OmniRoute/pull/11994)) — thanks @hartmark +- **fix(providers):** Perplexity Web no longer turns upstream stream failures into successful assistant text; pre-content failures remain eligible for fallback, partial output ends with a structured sanitized error, and failed sessions are not persisted ([#12465](https://github.com/diegosouzapw/OmniRoute/pull/12465)) +- **fix(video-bridge):** preserve chronological video and transcript fusion, source-frame timestamps, transcript provenance, full contact-sheet intervals, and pre-abort callback isolation ([#11681](https://github.com/diegosouzapw/OmniRoute/pull/11681)) +- **Z.ai Web:** HTTP 200 streams carrying an upstream error now terminate with a structured failure instead of assistant text plus a normal stop, preserving partial output while allowing pre-content combo fallback. ([#12454](https://github.com/diegosouzapw/OmniRoute/pull/12454)) +- Absorb `Error [AbortError]: request_signal_aborted` and DOMException AbortError shapes in the process-level client-abort crash guard so routine client disconnects no longer kill the server (exit code 7). ([#12165](https://github.com/diegosouzapw/OmniRoute/pull/12165)) — thanks @alvinveroy +- **fix(sse):** Treat Adapta Web `type:error` SSE events as sanitized non-stream failures instead of empty HTTP 200 completions. ([#12459](https://github.com/diegosouzapw/OmniRoute/pull/12459)) +- **fix(autoCombo):** layer 3 (`models_dev_tier`) no longer scores retired models as premium, merge cross-provider capability rows deterministically, and inherit tier scores through variant ids ([#11508](https://github.com/diegosouzapw/OmniRoute/issues/11508)). `getModelsDevTierFitness` now vetoes ids present in `config/quality/model-lifecycle.json` with status `retired` before any other signal; `loadModelCapabilities()` aggregates per `model_id` (any-non-null-true for booleans, max for `limit_context`) instead of last-write-wins over SQLite's undefined row order; a capabilities miss retries through `resolveScoresAs()`'s base id and reports `models_dev_tier:inherited`, matching layers 1–2 (#11492). Adds `getModelsDevTierFitnessWithSource()` and an `invalidateCapabilitiesCache()` test/ops hook. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) — thanks @Meet6338-X +- fix(sse): accept Claude `content:[]` + `stop_reason: max_tokens`/`tool_use` in `detectMalformedNonStream` (match `isEmptyContentResponse`) so Claude Code `/model` probes with `max_tokens: 1` no longer become a false 502 ([#11568](https://github.com/diegosouzapw/OmniRoute/pull/11568)) — thanks @Seramicx +- **fix(security):** sanitize `request.failed` diagnostics before publishing them to live dashboard listeners and replay history, while keeping status, model, provider, latency, and internal call-log diagnostics intact. ([#12469](https://github.com/diegosouzapw/OmniRoute/pull/12469)) +- **fix(pwa):** do not intercept dashboard navigations so Chrome can fall back from a stale HTTP/3 Alt-Svc advertisement (UDP :20128 is unpublished; F5 on a long-lived tab hung until a new tab opened a fresh TCP connection). ([#12767](https://github.com/diegosouzapw/OmniRoute/pull/12767)) — thanks @HouMinXi +- **fix(executors):** handle DuckDuckGo ERR_BN_LIMIT (418) without retrying — when the upstream returns `418 ERR_BN_LIMIT` (rate-limit/ban), the executor now returns the error immediately instead of burning another VQD acquisition that would only count against the IP limit. The retry logic for `418 ERR_CHALLENGE` (unsolved challenge) remains unchanged. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) ([#12110](https://github.com/diegosouzapw/OmniRoute/pull/12110)) — thanks @oyi77 / @Meet6338-X +- Fixed DuckDuckGo streaming responses losing JSON lines and UTF-8 characters split across network chunks. ([#11528](https://github.com/diegosouzapw/OmniRoute/pull/11528)) — thanks @pacocartones +- Harden generated Cloudflare, Vercel, and Deno relays by stripping hop-by-hop, framing, and proxy authentication headers before forwarding requests upstream. ([#11533](https://github.com/diegosouzapw/OmniRoute/pull/11533)) — thanks @pacocartones +- **feat(grok-cli):** Provider Limits shows grok-cli banked reset credits from `GetRemainingResets` (including a real zero; a failed RPC omits the row) and the existing View credits button now calls `ConsumerUiSvc/RedeemReset` for grok-cli. Live tokens use nested fields 10/20/30 (id + Timestamp), not compact 1/2/3. ([#12805](https://github.com/diegosouzapw/OmniRoute/pull/12805)) — thanks @HouMinXi +- **fix(memory):** Embedding Model Quick select, Embedding Source remote dropdown, and Rerank selector now list every configured provider with embedding/rerank support instead of only chat-catalog text matches plus OpenRouter live discovery; a generic OpenAI-compatible `/embeddings` + Cohere-compatible `/rerank` runtime fallback resolves any configured chat provider's embedding/rerank endpoint, so unlisted providers no longer fail with "Unknown embedding provider"; both memory selectors gained a free-text model override ([#11390](https://github.com/diegosouzapw/OmniRoute/pull/11390)) — thanks @rqzbeh +- Harden SQLite upgrades around the historical migration-074 version collision: missing discovery and inspector tables are replayed atomically, pre-existing databases (including setup-created skeletons) receive reusable content-addressed safety snapshots, and Node test/eval probes without `DATA_DIR` are isolated from the operator database. ([#12435](https://github.com/diegosouzapw/OmniRoute/pull/12435)) +- **fix(dashboard):** Moonshot/Kimi Open Platform voucher and cash leftover percentages follow the bucket balance, so an empty wallet no longer paints those rows as 100% while Available is 0% ([#12733](https://github.com/diegosouzapw/OmniRoute/pull/12733)) — thanks @HouMinXi +- **fix(combo):** return non-retryable HTTP 400 when all candidates for a pinned native Codex turn are unavailable due to model-scoped lockout, terminating the turn cleanly while preserving turn continuity and enabling standard Combo routing on subsequent turns ([#12240](https://github.com/diegosouzapw/OmniRoute/pull/12240)) — thanks @mdigitalbh81 +- Fixed auto-router strategies preserving the selected connection through ranking and dispatch when multiple connections share the same provider and model. ([#11530](https://github.com/diegosouzapw/OmniRoute/pull/11530)) — thanks @pacocartones +- Preserve Chat Completions JSON-object response formats when translating requests to the Responses API. ([#11529](https://github.com/diegosouzapw/OmniRoute/pull/11529)) — thanks @pacocartones +- Fix idempotency fingerprints so Responses API requests include `input` and other semantic generation fields using stable serialization, while excluding credentials and request metadata. ([#11532](https://github.com/diegosouzapw/OmniRoute/pull/11532)) — thanks @pacocartones +- **fix(api):** Generated API CLI commands now enforce required OpenAPI request bodies; Combo test commands forward the required `comboName` body, while API keys created by older writers after migration 149 preserve legacy allow-all Combo access without widening explicit empty allowlists — thanks @marcelokarval ([#12070](https://github.com/diegosouzapw/OmniRoute/pull/12070)) +- **fix(grok-web):** treat upstream streaming failures as failures instead of successful assistant text: error-only streams now fail readiness with HTTP 502, while failures after legitimate content preserve that partial output and terminate through the sanitized stream failure path without a normal `stop` completion. ([#12458](https://github.com/diegosouzapw/OmniRoute/pull/12458)) +- HuggingChat now turns HTTP 200 JSONL generation failures into a sanitized 502 before content, or a fixed public stream failure after partial output, so fallback and request persistence no longer record a false successful stop. ([#12456](https://github.com/diegosouzapw/OmniRoute/pull/12456)) +- **fix(providers):** keep 1min.ai HTTP 200 stream errors out of assistant content, preserve partial output, and expose sanitized terminal errors so pre-content failures can fall back. ([#12466](https://github.com/diegosouzapw/OmniRoute/pull/12466)) +- **fix(sessions):** preserve legacy Sessions data when the additive exclusive-session projection is unavailable, returning an empty projection and warning only once per contiguous outage instead of failing the endpoint; the Sessions badge intentionally reflects the merged legacy and exclusive row count introduced by [#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389) ([#11469](https://github.com/diegosouzapw/OmniRoute/pull/11469)) — thanks @KaspaPulse +- Render Nimble Search with the generic provider icon and serve Opper's proven logo locally. ([#12415](https://github.com/diegosouzapw/OmniRoute/pull/12415)) +- **fix(quality):** `config/quality/provider-assets-provenance.jsonl` was stale against the physical `public/providers/` tree (still listing 225 assets and 5 duplicate-content groups after several provider-retirement PRs removed 85 icon files), which would have made `check:provider-asset-provenance` fail on the next full run. Reconciled the manifest to the real 142-file snapshot (142 asset records, 1 duplicate-content group, 2 newly-registered files marked `unresolved` pending provenance research), repinned `auditedCommit`, and updated the regression test's hardcoded counts to match. ([#11876](https://github.com/diegosouzapw/OmniRoute/pull/11876)) +- fix(ui): wrap quota card footer action buttons to prevent clipping Refresh button on narrow cards — when extra action buttons such as `View credits` are present on Codex cards, allow the footer row to flex-wrap with `shrink-0` buttons so `Refresh now` remains visible and clickable across all responsive card widths ([#11464](https://github.com/diegosouzapw/OmniRoute/pull/11464)) — thanks @adevwithpurpose +- Keep Antigravity Gemini usable when the same connection's Claude weekly quota is empty; generic quota cache stays per-connection for every other provider. ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) — thanks @HouMinXi +- Fixed Tailscale tunnel setup to reject ports outside the valid 1-65535 range and safely install Windows MSI packages from paths containing apostrophes. ([#11534](https://github.com/diegosouzapw/OmniRoute/pull/11534)) — thanks @pacocartones +- Classify Cloudflared and Tailscale process-management routes as local-only and spawn-capable while preserving remote access to read-only tunnel status endpoints. ([#11531](https://github.com/diegosouzapw/OmniRoute/pull/11531)) — thanks @pacocartones +- Electron release workflow: the `publish-npm` job now grants `actions: read` to the reusable `npm-publish.yml` it calls (its `publish` job requests it), which is what made GitHub refuse the whole v3.8.50 run at startup and ship the release with zero desktop assets; a `workflow_dispatch` now builds the requested tag instead of the dispatching branch and can skip the npm leg (`publish_npm=false`) when only re-attaching assets ([#11974](https://github.com/diegosouzapw/OmniRoute/pull/11974)) +- **fix(dashboard):** The Combos page usage guide now reads its dismissal through `useSyncExternalStore` instead of correcting SSR state inside an effect, removing an extra commit of the page tree on every load (and the `react-hooks/set-state-in-effect` error it raised). ([#12671](https://github.com/diegosouzapw/OmniRoute/pull/12671)) +- Electron release: `electron/package-lock.json` regained the optional `electron-builder-squirrel-windows` subtree (13 entries) that `npm ci` had been refusing as out of sync, `electron-release.yml` gained a `build_ref` dispatch input and stops regenerating release notes on a re-attach dispatch, and the npm publish workflow attaches the SBOM to the GitHub Release on dispatch publishes too — so the v3.8.51 tag ships every desktop asset and the SBOM like v3.8.49 did ([#12022](https://github.com/diegosouzapw/OmniRoute/pull/12022)) +- Drained the reds every PR against `release/v3.8.51` was born with: documented the three service-worker build-id variables, registered the six retirement/tunnel tests with the mutation gate, approved `eslint-plugin-react-hooks` in the dependency allowlist, added the six `combo.sort.*` strings to `vi` and `pt-BR`, pointed the ChatGPT Web doc at the real migration-168 test, worded the g4f hint around the member key, and realigned four tests to the retired-provider catalog and the legacy-schema fixtures the retirement migrations touch. ([#11940](https://github.com/diegosouzapw/OmniRoute/pull/11940)) +- **fix(providers):** Claude, Grok, LMArena, Notion, and Perplexity web-cookie transports now use pooled `wreq-js` 3.2 instead of the native sidecar, with all nine supported bindings pinned and audited, and the applicable platform binding plus native-license evidence included in each release artifact ([#12429](https://github.com/diegosouzapw/OmniRoute/pull/12429), supersedes [#11753](https://github.com/diegosouzapw/OmniRoute/pull/11753)). +- **fix(providers):** Zed Hosted streaming failures now trigger fallback before content and end partial streams with a sanitized structured error instead of fake assistant text and a normal-success stop. ([#12455](https://github.com/diegosouzapw/OmniRoute/pull/12455)) +- **fix(test):** capture hook result after render (direct commit `b85d0abb0b`) +- **fix(sync):** repair the two regressions the v3.8.50 sync-back left on release/v3.8.51 (direct commit `5ade9e0851`) +- **revert(models):** drop the memo reset hook ported from #10055 (direct commit `282de841ae`) +- **fix(typecheck):** guard the optional connectionId before the reactive model sync (direct commit `f2825bad75`) +- **fix(quota-share):** release the winner's reserved in-flight slot (#11371) ([#11408](https://github.com/diegosouzapw/OmniRoute/pull/11408)) — thanks @oyi77 +- **fix(providers):** stop silently dropping reasoning effort on opencode families (#10788) ([#11409](https://github.com/diegosouzapw/OmniRoute/pull/11409)) — thanks @oyi77 +- **fix(combo):** enable genuine quota-aware routing for generic providers (antigravity, claude, etc.) ([#11411](https://github.com/diegosouzapw/OmniRoute/pull/11411)) — thanks @HouMinXi +- **fix(oauth):** allow expired token retry before deactivation and activate expired retry loop (#P0) ([#11414](https://github.com/diegosouzapw/OmniRoute/pull/11414)) — thanks @HouMinXi +- **perf(executors):** lazy-load the executor registry — defer class imports + construction to first use (#11220) ([#11421](https://github.com/diegosouzapw/OmniRoute/pull/11421)) — thanks @oyi77 +- **fix:** make release/v3.8.50 CI gates green (run 32786966560 root causes) ([#11450](https://github.com/diegosouzapw/OmniRoute/pull/11450)) — thanks @jonlwheat2-gif +- **fix(sse):** exact-domain cookie match and origin-equality URL assertions ([#11452](https://github.com/diegosouzapw/OmniRoute/pull/11452)) — thanks @hartmark +- **fix(providers):** send Responses API payload during OpenAI-compatible validation ([#11454](https://github.com/diegosouzapw/OmniRoute/pull/11454)) — thanks @tuandinh0801 +- **fix(command-code):** fallback to /alpha/generate for Go plan without Provider API access ([#11455](https://github.com/diegosouzapw/OmniRoute/pull/11455)) — thanks @Chewji9875 +- **fix(responses-continuation):** fail closed on a log-truncated stored input/output array ([#11473](https://github.com/diegosouzapw/OmniRoute/pull/11473)) — thanks @hartmark +- **fix(cli):** write jcode settings as [providers.omniroute] in config.toml ([#11484](https://github.com/diegosouzapw/OmniRoute/pull/11484)) — thanks @hongnoul +- **fix(logging):** raise chat-log array truncation cap to 1000 and unify duplicate implementations ([#11499](https://github.com/diegosouzapw/OmniRoute/pull/11499)) — thanks @hartmark +- **fix:** restore live websocket URL helpers ([#11509](https://github.com/diegosouzapw/OmniRoute/pull/11509)) — thanks @PauloFH +- **perf(core):** eliminate async waterfalls across home, cache, and db operations (#11396) ([#11512](https://github.com/diegosouzapw/OmniRoute/pull/11512)) — thanks @HouMinXi +- **fix(kiro):** add runtime.us-east-1.kiro.dev as first-attempt endpoint ([#11517](https://github.com/diegosouzapw/OmniRoute/pull/11517)) — thanks @rifqiawl +- **fix(providers):** remove duplicate 128-tool truncation in OpencodeExecutor (#11444) ([#11518](https://github.com/diegosouzapw/OmniRoute/pull/11518)) — thanks @HouMinXi +- **fix(providers):** poll onboardUser LRO before classifying BYOP ([#11519](https://github.com/diegosouzapw/OmniRoute/pull/11519)) — thanks @rifqiawl +- **fix(translator):** preserve exact tool name casing for Claude tool_use payloads (#11487) ([#11520](https://github.com/diegosouzapw/OmniRoute/pull/11520)) — thanks @HouMinXi +- **fix(kimi-web):** align health probe with executor domain ([#11521](https://github.com/diegosouzapw/OmniRoute/pull/11521)) — thanks @pacocartones +- **fix(cli):** preserve existing admin password during setup ([#11522](https://github.com/diegosouzapw/OmniRoute/pull/11522)) — thanks @pacocartones +- **fix(mitm):** keep Windows DNS elevation off the POSIX sudo path (#11430) ([#11542](https://github.com/diegosouzapw/OmniRoute/pull/11542)) — thanks @official-burak +- **fix(sse):** scale chat admission by ingest byte budget instead of a fixed request count ([#11548](https://github.com/diegosouzapw/OmniRoute/pull/11548)) — thanks @TheDemonTuan +- **fix:** accept Gemini Web cookie exports ([#11554](https://github.com/diegosouzapw/OmniRoute/pull/11554)) — thanks @rafacpti23 +- **fix(i18n):** complete Brazilian Portuguese translations ([#11558](https://github.com/diegosouzapw/OmniRoute/pull/11558)) — thanks @rafacpti23 +- **fix(sse):** explicit types for openai-responses pureHelpers — clears last failing core typecheck gate ([#11567](https://github.com/diegosouzapw/OmniRoute/pull/11567)) — thanks @jonlwheat2-gif +- **fix(opencode):** hydrate Proxy Pool references for no-auth connections ([#11584](https://github.com/diegosouzapw/OmniRoute/pull/11584)) — thanks @AStupidBear +- **fix(ci):** resolve inherited release/v3.8.51 base-red CI failures ([#11608](https://github.com/diegosouzapw/OmniRoute/pull/11608)) — thanks @jonlwheat2-gif +- **fix(db):** fallback load STORAGE_ENCRYPTION_KEY from env files ([#11614](https://github.com/diegosouzapw/OmniRoute/pull/11614)) — thanks @benzntech +- **fix(providers):** mark Brave Search as no longer having a free tier ([#11621](https://github.com/diegosouzapw/OmniRoute/pull/11621)) — thanks @turbolego +- **fix:** pass tool result content through as string in OpenAI/Claude -> Gemini translation ([#11624](https://github.com/diegosouzapw/OmniRoute/pull/11624)) — thanks @Hsia97 +- **fix(sse):** scope credential health to exact key ([#11628](https://github.com/diegosouzapw/OmniRoute/pull/11628)) — thanks @KaspaPulse +- **fix(install):** guard prepare script against missing husky ([#11634](https://github.com/diegosouzapw/OmniRoute/pull/11634)) — thanks @Prajeeth-12 +- **fix(security):** redact NVIDIA_API_KEY at the log sink in the ad-hoc diag script ([#11639](https://github.com/diegosouzapw/OmniRoute/pull/11639)) +- **fix(types):** clear the two type regressions red on release/v3.8.51 ([#11640](https://github.com/diegosouzapw/OmniRoute/pull/11640)) — thanks @jonlwheat2-gif +- **fix(build):** resolve workspace: protocol dependencies to concrete semver versions during prepublish (#11540) ([#11641](https://github.com/diegosouzapw/OmniRoute/pull/11641)) — thanks @HouMinXi +- **fix(search):** properly resolve configured search connection in /v1/responses pipeline (#11524) ([#11642](https://github.com/diegosouzapw/OmniRoute/pull/11642)) — thanks @HouMinXi +- **fix(authz):** trust cf-connecting-ip for via-proxy detection when peer is Cloudflare edge (#11514) ([#11643](https://github.com/diegosouzapw/OmniRoute/pull/11643)) — thanks @HouMinXi +- **fix(security):** compare the OIDC state cookie in constant time ([#11646](https://github.com/diegosouzapw/OmniRoute/pull/11646)) +- **fix(security):** namespace the dedup hash by the calling API key ([#11649](https://github.com/diegosouzapw/OmniRoute/pull/11649)) +- **fix(models):** gate the Codex-native catalog loop on prefix mode (#11632) ([#11662](https://github.com/diegosouzapw/OmniRoute/pull/11662)) — thanks @xiaoyaner0201 +- **fix(dashboard):** project Codex routing quota in account pools ([#11666](https://github.com/diegosouzapw/OmniRoute/pull/11666)) — thanks @KaspaPulse +- **fix(ddgw):** normaliza system no upstream da release v3.8.51 ([#11692](https://github.com/diegosouzapw/OmniRoute/pull/11692)) — thanks @RhianB14 +- **fix(memory):** honor category filter in GET /api/memory ([#11699](https://github.com/diegosouzapw/OmniRoute/pull/11699)) — thanks @Sabeekhann +- **fix(ci):** detect stale workspace lockfile entries ([#11714](https://github.com/diegosouzapw/OmniRoute/pull/11714)) — thanks @pacocartones +- **fix(ci):** stamp dist/BUILD_SHA before the npm publish provenance gate ([#11721](https://github.com/diegosouzapw/OmniRoute/pull/11721)) +- **fix(docker):** decouple the best-effort Bun image from the release manifest ([#11724](https://github.com/diegosouzapw/OmniRoute/pull/11724)) +- **perf(compression):** avoid cloning memo misses twice ([#11727](https://github.com/diegosouzapw/OmniRoute/pull/11727)) — thanks @RaviTharuma +- **fix(docker):** let the bun digest artifact be absent, not fatal ([#11740](https://github.com/diegosouzapw/OmniRoute/pull/11740)) +- **fix(shared):** block cloud-metadata hosts under default remote-image guard ([#11755](https://github.com/diegosouzapw/OmniRoute/pull/11755)) — thanks @santosraju99-hub +- **fix(api):** preserve caller-provided X-Correlation-Id on chat completions ([#11760](https://github.com/diegosouzapw/OmniRoute/pull/11760)) — thanks @aniruddhaadak80 +- **fix(resilience):** lock permanently retired models instead of short backoff (Gemini ban prevention) ([#11762](https://github.com/diegosouzapw/OmniRoute/pull/11762)) — thanks @turbolego +- **fix:** mark Vercel AI Gateway as passthroughModels ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **fix(resilience):** stop hammering permanently-moved endpoints and billing-suspended accounts ([#11774](https://github.com/diegosouzapw/OmniRoute/pull/11774)) — thanks @turbolego +- **fix(lease):** remove global static reservation and gate routing on live active lease occupancy ([#11775](https://github.com/diegosouzapw/OmniRoute/pull/11775)) — thanks @TheDemonTuan +- **fix(ci):** size the install-upgrade gate to a measured run, and log the pack cost ([#11776](https://github.com/diegosouzapw/OmniRoute/pull/11776)) +- **fix(pwa):** stop serving the stale shell after deploys ([#11779](https://github.com/diegosouzapw/OmniRoute/pull/11779)) — thanks @HouMinXi +- **fix(combo):** lock GitHub models rejected as "not supported" for future requests ([#11781](https://github.com/diegosouzapw/OmniRoute/pull/11781)) — thanks @turbolego +- **fix(cli):** probe both IPv4 and IPv6 loopback for server readiness (#11766) ([#11794](https://github.com/diegosouzapw/OmniRoute/pull/11794)) — thanks @vermasomesh835 +- **fix(sse):** preserve CLIProxyAPI wrappers per connection ([#11797](https://github.com/diegosouzapw/OmniRoute/pull/11797)) — thanks @RaviTharuma +- **fix(providers):** honor model auto-fetch opt-in on create ([#11798](https://github.com/diegosouzapw/OmniRoute/pull/11798)) — thanks @RaviTharuma +- **fix(dashboard):** make model auto-fetch order independent ([#11805](https://github.com/diegosouzapw/OmniRoute/pull/11805)) — thanks @RaviTharuma +- **fix(docker):** make GHA cache exports best effort ([#11806](https://github.com/diegosouzapw/OmniRoute/pull/11806)) — thanks @RaviTharuma +- **fix(kiro):** do not permanently ban on 'User is not authorized to make this call' ([#11809](https://github.com/diegosouzapw/OmniRoute/pull/11809)) — thanks @Deftera186 +- **fix(sse):** keep cache-write tokens in OpenAI-shaped usage ([#11814](https://github.com/diegosouzapw/OmniRoute/pull/11814)) — thanks @TheDemonTuan +- **fix(docs):** resolve relative markdown and wiki links across Fumadocs and GitHub wiki ([#11834](https://github.com/diegosouzapw/OmniRoute/pull/11834)) — thanks @AndrianBalanescu +- **fix(chat):** preserve unstripped model string for passthrough provider routing ([#11840](https://github.com/diegosouzapw/OmniRoute/pull/11840)) — thanks @raheemuddin786 +- **fix(mcp):** avoid async initialization inside sync esm wrappers (#11569) ([#11849](https://github.com/diegosouzapw/OmniRoute/pull/11849)) — thanks @HouMinXi +- **fix(sse):** preserve coherent markdown token boundaries across stream translation chunks (#11606) ([#11851](https://github.com/diegosouzapw/OmniRoute/pull/11851)) — thanks @HouMinXi +- **fix(ci):** run the install-upgrade gate on disk, not on the /tmp tmpfs ([#11855](https://github.com/diegosouzapw/OmniRoute/pull/11855)) +- **fix(skills):** expand shorthand property types in injected tool schemas ([#11857](https://github.com/diegosouzapw/OmniRoute/pull/11857)) — thanks @alvinveroy +- **fix(ci):** publish npm from a hosted runner so provenance is accepted ([#11877](https://github.com/diegosouzapw/OmniRoute/pull/11877)) +- **fix(lint):** pin react hooks plugin to 7.0.1 ([#11886](https://github.com/diegosouzapw/OmniRoute/pull/11886)) +- **fix(ci):** surface and clear release-green ESLint failure ([#11890](https://github.com/diegosouzapw/OmniRoute/pull/11890)) — thanks @MumuTW +- **fix(guardrails):** prevent duplicate prompt-injection-guard log output ([#11936](https://github.com/diegosouzapw/OmniRoute/pull/11936)) — thanks @patrykkopycinski +- **fix(auth):** downgrade expected transient states from warn to debug ([#11937](https://github.com/diegosouzapw/OmniRoute/pull/11937)) — thanks @patrykkopycinski +- **fix(codex):** fail over image generation for imported free plans ([#11948](https://github.com/diegosouzapw/OmniRoute/pull/11948)) — thanks @Bl0ck154 +- **fix(codex):** normalize non-stream responses ([#11951](https://github.com/diegosouzapw/OmniRoute/pull/11951)) — thanks @Bl0ck154 +- **fix(images):** forward Antigravity image size ([#11952](https://github.com/diegosouzapw/OmniRoute/pull/11952)) — thanks @Bl0ck154 +- **fix(db):** invalidate connection cache after upsert ([#11953](https://github.com/diegosouzapw/OmniRoute/pull/11953)) — thanks @Bl0ck154 +- **fix(codex):** restore imported account state ([#11954](https://github.com/diegosouzapw/OmniRoute/pull/11954)) — thanks @Bl0ck154 +- **fix(sse):** give extended-thinking targets the reasoning readiness budget ([#11959](https://github.com/diegosouzapw/OmniRoute/pull/11959)) — thanks @abhisheksharma2411 +- **fix(dashboard):** use opaque background and readable text color on cost chart tooltips ([#11960](https://github.com/diegosouzapw/OmniRoute/pull/11960)) — thanks @ZaimMarzuki +- **fix(antigravity):** send complete loadCodeAssist metadata (ideType/platform/pluginType as numeric enums) ([#11969](https://github.com/diegosouzapw/OmniRoute/pull/11969)) — thanks @rifqiawl +- **fix(core):** resolve DB init race condition and reasoning translation ([#12003](https://github.com/diegosouzapw/OmniRoute/pull/12003)) — thanks @raheemuddin786 +- **fix(admission):** exclude reclaimable page cache from the cgroup pressure ratio ([#12017](https://github.com/diegosouzapw/OmniRoute/pull/12017)) — thanks @HouMinXi +- **fix(build):** prune non-production trees in NFT trace excludes and tsconfig ([#12028](https://github.com/diegosouzapw/OmniRoute/pull/12028)) — thanks @Chewji9875 +- **fix(migrations):** add renamed migration compatibility for 056/073/077/101 ([#12036](https://github.com/diegosouzapw/OmniRoute/pull/12036)) — thanks @oyi77 +- **fix(sse):** default crash-guard logger to console.warn, not console ([#12042](https://github.com/diegosouzapw/OmniRoute/pull/12042)) — thanks @HouMinXi +- **fix(catalog):** derive vision/modalities for built-in auto combos from effective target pool ([#12046](https://github.com/diegosouzapw/OmniRoute/pull/12046)) — thanks @Prajeeth-12 +- **fix(release):** the packaged-app smoke verifies the database opened, not a driver line the primary path never prints (twin of #12032) ([#12047](https://github.com/diegosouzapw/OmniRoute/pull/12047)) +- **fix(barrel):** migrate src/app/ off the @/lib/localDb barrel import (#11795 Phase 2) ([#12051](https://github.com/diegosouzapw/OmniRoute/pull/12051)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate src/lib/ off the localDb barrel to direct db imports (#59) ([#12052](https://github.com/diegosouzapw/OmniRoute/pull/12052)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate open-sse, src/shared, src/sse, src/models, src/domain off the @/lib/localDb barrel import (#11795 Phase 4) ([#12053](https://github.com/diegosouzapw/OmniRoute/pull/12053)) — thanks @jonlwheat2-gif +- **fix(barrel):** delete the @/lib/localDb barrel — every consumer migrated (#11795 Phase 5) ([#12055](https://github.com/diegosouzapw/OmniRoute/pull/12055)) — thanks @jonlwheat2-gif +- **fix(radar):** restore D12 public boundary ([#12057](https://github.com/diegosouzapw/OmniRoute/pull/12057)) +- **fix(dev):** bound webpack and Tailwind scans ([#12075](https://github.com/diegosouzapw/OmniRoute/pull/12075)) — thanks @backryun +- **fix(dev):** isolate root layout settings reads ([#12076](https://github.com/diegosouzapw/OmniRoute/pull/12076)) — thanks @backryun +- **fix(dev):** reduce instrumentation executor fan-out (phase 3) ([#12078](https://github.com/diegosouzapw/OmniRoute/pull/12078)) — thanks @backryun +- **fix(dev):** make logging resources HMR-singleton ([#12079](https://github.com/diegosouzapw/OmniRoute/pull/12079)) — thanks @backryun +- **fix(sse):** keep unavailable forced connections scoped ([#12080](https://github.com/diegosouzapw/OmniRoute/pull/12080)) — thanks @keeltrace +- **fix(dev):** isolate batch dispatch from instrumentation ([#12081](https://github.com/diegosouzapw/OmniRoute/pull/12081)) — thanks @backryun +- **fix(ci):** reconcile release test contract drift ([#12082](https://github.com/diegosouzapw/OmniRoute/pull/12082)) — thanks @backryun +- **fix(ci):** accept CVE-2025-68121 in the prebuilt tls-client .so, auto-close base-red issues, guard Scorecard on the default branch ([#12085](https://github.com/diegosouzapw/OmniRoute/pull/12085)) +- **fix(db):** drop three consumer-less 1proxy exports — dead-code base-red on release/v3.8.51 after the barrel deletion (#12055) ([#12087](https://github.com/diegosouzapw/OmniRoute/pull/12087)) +- **fix(api):** clear the six API-route TypeScript regressions the new gate landed red on ([#12094](https://github.com/diegosouzapw/OmniRoute/pull/12094)) +- **fix(release):** never let the tag-push Create Release append auto notes to the curated body ([#12096](https://github.com/diegosouzapw/OmniRoute/pull/12096)) +- **fix(sse):** honor CLIProxyAPI environment API key ([#12099](https://github.com/diegosouzapw/OmniRoute/pull/12099)) — thanks @RaviTharuma +- **fix(oauth):** bind Google refresh to the client that issued the token ([#12106](https://github.com/diegosouzapw/OmniRoute/pull/12106)) — thanks @HouMinXi +- **fix(dashboard):** make RequestLoggerDetail loadable outside Next — CSS via globals.css + CJS/ESM interop (#11703 base-reds) ([#12114](https://github.com/diegosouzapw/OmniRoute/pull/12114)) +- **fix(leases):** project status lease row to lease columns so joined connection PII never escapes ([#12115](https://github.com/diegosouzapw/OmniRoute/pull/12115)) — thanks @geek007git +- **fix(plugins):** do not kill the plugin process when a fire-and-forget hook times out ([#12116](https://github.com/diegosouzapw/OmniRoute/pull/12116)) — thanks @geek007git +- **fix(guardrails):** restore injection-guard logging on middleware-only routes ([#12117](https://github.com/diegosouzapw/OmniRoute/pull/12117)) — thanks @geek007git +- **fix(plugins):** refresh stored manifest from disk on activate so new hook fields reach existing installs ([#12120](https://github.com/diegosouzapw/OmniRoute/pull/12120)) — thanks @geek007git +- **fix(codex):** preserve existing provider state when bulk-import upserts a matching connection ([#12122](https://github.com/diegosouzapw/OmniRoute/pull/12122)) — thanks @geek007git +- **fix(ci):** clear the two base-reds the 2026-08-30 merge batch left on release/v3.8.51 (round 3) ([#12123](https://github.com/diegosouzapw/OmniRoute/pull/12123)) +- **fix(sse):** strip type:'custom' from Claude tools on agentrouter dispatch ([#12126](https://github.com/diegosouzapw/OmniRoute/pull/12126)) — thanks @wahidsadik371-coder +- **fix(sse):** stop advertising video providers the dispatcher cannot run ([#12131](https://github.com/diegosouzapw/OmniRoute/pull/12131)) — thanks @quiterunner-commits +- **fix(search):** treat HTTP 432 and plan limit errors as transient cooldown ([#12139](https://github.com/diegosouzapw/OmniRoute/pull/12139)) — thanks @HouMinXi +- **fix(sse):** refuse an AI Horde queue that cannot fit the request budget ([#12143](https://github.com/diegosouzapw/OmniRoute/pull/12143)) — thanks @quiterunner-commits +- **fix(ci):** clear the base-reds the afternoon merge batch left on release/v3.8.51 (round 5: provider count 352, TS2554/TS2677) ([#12144](https://github.com/diegosouzapw/OmniRoute/pull/12144)) +- **fix(translator):** strip neutral tool_choice when tools absent in Responses-to-Chat (#12141) ([#12166](https://github.com/diegosouzapw/OmniRoute/pull/12166)) — thanks @HouMinXi +- **fix(guardrails):** pass providerId to getResolvedModelCapabilities in checkComboVision (#12112) ([#12169](https://github.com/diegosouzapw/OmniRoute/pull/12169)) — thanks @HouMinXi +- **fix(translator):** strip plaintext reasoning content for opaque responses backends (#12128) ([#12171](https://github.com/diegosouzapw/OmniRoute/pull/12171)) — thanks @HouMinXi +- **perf(stream):** compile hot-path regexes once, bound token caches, fix quadratic buffering ([#12179](https://github.com/diegosouzapw/OmniRoute/pull/12179)) — thanks @opensource-elearning +- **fix:** resolve compression worker file using runtime anchors instead of… ([#12183](https://github.com/diegosouzapw/OmniRoute/pull/12183)) — thanks @NightStalker-87 +- **fix:** prevent Claude Code session kills via liveness-aware readiness + auto model echo ([#12189](https://github.com/diegosouzapw/OmniRoute/pull/12189)) — thanks @opensource-elearning +- **fix(translator):** preserve falsy primitive values in Gemini and Antigravity function response results ([#12191](https://github.com/diegosouzapw/OmniRoute/pull/12191)) — thanks @Siva010 +- **fix(oauth):** mark empty Antigravity projectId as degraded and clear stale errors (#11284) ([#12205](https://github.com/diegosouzapw/OmniRoute/pull/12205)) — thanks @HouMinXi +- **fix(providers):** gate the Codex auto-ping usage read on the shared quota throttle ([#12209](https://github.com/diegosouzapw/OmniRoute/pull/12209)) — thanks @abhisheksharma2411 +- **fix(combos):** prioritize SQLite row id over inner JSON id and notify delete errors ([#12213](https://github.com/diegosouzapw/OmniRoute/pull/12213)) — thanks @HouMinXi +- **fix(providers):** mark featherless.ai as no longer having a free tier (`hasFree: false`) ([#12216](https://github.com/diegosouzapw/OmniRoute/pull/12216)) — thanks @turbolego +- **fix(deepseek-web):** stop Turbopack dev panic in the PoW worker path resolver ([#12221](https://github.com/diegosouzapw/OmniRoute/pull/12221)) — thanks @hartmark +- **fix(chat-admission):** clarify local 503 source ([#12223](https://github.com/diegosouzapw/OmniRoute/pull/12223)) — thanks @keeltrace +- **fix(dev):** silence webpack runtime module warnings ([#12228](https://github.com/diegosouzapw/OmniRoute/pull/12228)) — thanks @backryun +- **fix(db):** use module.require for CommonJS runtime driver loading ([#12230](https://github.com/diegosouzapw/OmniRoute/pull/12230)) — thanks @raheemuddin786 +- **fix(memory):** honest probe-driven FTS5 keyword status + memory_id rowid sync ([#12231](https://github.com/diegosouzapw/OmniRoute/pull/12231)) — thanks @raheemuddin786 +- **fix(system):** propagate abort signal to stream reader in HTTP version checks ([#12232](https://github.com/diegosouzapw/OmniRoute/pull/12232)) — thanks @raheemuddin786 +- **fix(resilience):** isolate local host execution errors from provider circuit breakers ([#12233](https://github.com/diegosouzapw/OmniRoute/pull/12233)) — thanks @raheemuddin786 +- **fix(sse):** sort injected tools deterministically for prompt caching ([#12234](https://github.com/diegosouzapw/OmniRoute/pull/12234)) — thanks @raheemuddin786 +- **fix(combo):** always clear the loop-safety timer, not just on the happy path (#11804) ([#12245](https://github.com/diegosouzapw/OmniRoute/pull/12245)) +- **fix(quality):** base-red pair — stryker tap registration + turn-pin suites aligned to the window gate ([#12255](https://github.com/diegosouzapw/OmniRoute/pull/12255)) — thanks @backryun +- **fix(dev):** qualify Turbopack runtime boundaries ([#12258](https://github.com/diegosouzapw/OmniRoute/pull/12258)) — thanks @backryun +- **fix(combo):** fall back to full pool when collapsed sole survivor is context-too-small ([#12278](https://github.com/diegosouzapw/OmniRoute/pull/12278)) — thanks @opensource-elearning +- **fix(combo):** bound the pre-dispatch unavailable skip so a stale label cannot dark a pool (#12168) ([#12285](https://github.com/diegosouzapw/OmniRoute/pull/12285)) +- **fix(security):** strip Qwen/Alibaba console-session cookies from provider API responses ([#12287](https://github.com/diegosouzapw/OmniRoute/pull/12287)) +- **fix(usage):** console-aware Token Plan guidance + subscription hint on bailian 401 ([#12288](https://github.com/diegosouzapw/OmniRoute/pull/12288)) +- **fix(resourcePressure):** log numeric detail on every rejection, recover faster ([#12293](https://github.com/diegosouzapw/OmniRoute/pull/12293)) — thanks @hartmark +- **fix(release):** drain the 2026-09-01 base-red window — passthrough usage regression + radarPage i18n keys ([#12327](https://github.com/diegosouzapw/OmniRoute/pull/12327)) +- **fix(ci):** openapi-security-tiers checker must honor routeGuard patterns + imported prefixes ([#12350](https://github.com/diegosouzapw/OmniRoute/pull/12350)) — thanks @ggiak +- **perf(ci):** cache node_modules in the npm-ci-retry composite (#8084 D3) ([#12408](https://github.com/diegosouzapw/OmniRoute/pull/12408)) +- **revert(ui):** point CTAs back at their real destinations ([#12410](https://github.com/diegosouzapw/OmniRoute/pull/12410)) +- **fix(release):** drain the 2026-09-02 base-red — rerank-providers import + api-typecheck baseline ratchet ([#12414](https://github.com/diegosouzapw/OmniRoute/pull/12414)) +- **fix(types):** make system prompt injection noImplicitAny-safe ([#12416](https://github.com/diegosouzapw/OmniRoute/pull/12416)) +- **fix(quality):** validate typecheck baseline schema ([#12419](https://github.com/diegosouzapw/OmniRoute/pull/12419)) +- **fix(memory):** resolve rerank provider node cache import ([#12421](https://github.com/diegosouzapw/OmniRoute/pull/12421)) +- **fix(ci):** repair release-root regressions — pack dedup, web-session syntax, uc-image ids ([#12423](https://github.com/diegosouzapw/OmniRoute/pull/12423)) — thanks @backryun +- **fix(providers):** separate MaxAI and UC credential contracts ([#12431](https://github.com/diegosouzapw/OmniRoute/pull/12431)) +- **fix(cli):** preserve Claude settings on config set ([#12432](https://github.com/diegosouzapw/OmniRoute/pull/12432)) — thanks @KrzysiekSko +- **fix(providers):** repair the maxai credential block truncated by merge auto-resolve ([#12433](https://github.com/diegosouzapw/OmniRoute/pull/12433)) +- **fix(quality):** record the 2026-09-02 merged growth in the file-size baseline ([#12434](https://github.com/diegosouzapw/OmniRoute/pull/12434)) +- **fix(uc):** route UC error strings through sanitizeErrorMessage; allowlist the retired codex id ([#12437](https://github.com/diegosouzapw/OmniRoute/pull/12437)) +- **fix(adapta):** redact streamed upstream errors ([#12438](https://github.com/diegosouzapw/OmniRoute/pull/12438)) +- **fix(sse):** remap non-contiguous upstream tool_calls index to a gap-free output_index ([#12445](https://github.com/diegosouzapw/OmniRoute/pull/12445)) — thanks @hartmark +- **fix(quality):** detect duplicate tool_calls entries in one response ([#12446](https://github.com/diegosouzapw/OmniRoute/pull/12446)) — thanks @hartmark +- **fix(conversations):** resolve turn content OmniRoute never sends back to the client ([#12447](https://github.com/diegosouzapw/OmniRoute/pull/12447)) — thanks @hartmark +- **fix(combo):** do not treat credits-exhausted 401 as auth skip ([#12449](https://github.com/diegosouzapw/OmniRoute/pull/12449)) — thanks @RaviTharuma +- **fix(auth):** do not park healthy quota accounts as expired ([#12452](https://github.com/diegosouzapw/OmniRoute/pull/12452)) — thanks @RaviTharuma +- **fix(responses-continuation):** fail closed on a collector-truncated, empty output array ([#12460](https://github.com/diegosouzapw/OmniRoute/pull/12460)) — thanks @hartmark +- **fix(logging):** raise the SSE payload collector's default cap ([#12461](https://github.com/diegosouzapw/OmniRoute/pull/12461)) — thanks @hartmark +- **fix(resilience):** surface Responses failed.error.message in 502s ([#12472](https://github.com/diegosouzapw/OmniRoute/pull/12472)) — thanks @RaviTharuma +- **fix(security):** unbiased maxai X-Random nonce + stricter URL/regex assertions ([#12502](https://github.com/diegosouzapw/OmniRoute/pull/12502)) +- **fix(video):** re-anchor transcript log-redaction so PII/credential maskers can't reopen the leak (#12150 P1 follow-up) ([#12503](https://github.com/diegosouzapw/OmniRoute/pull/12503)) +- **fix(monitoring):** serve cached credentialHealth off the request path ([#12533](https://github.com/diegosouzapw/OmniRoute/pull/12533)) — thanks @RaviTharuma +- **fix(quota):** keep Antigravity Gemini usable when Claude weekly is empty ([#12566](https://github.com/diegosouzapw/OmniRoute/pull/12566)) — thanks @HouMinXi +- **fix(authz):** hard-gate every credential export and CLI-config write (GHSA-5926-2w35-7h4q) ([#12600](https://github.com/diegosouzapw/OmniRoute/pull/12600)) +- **fix(docs):** restore the Next build — REMOVED_PROVIDERS.md had no frontmatter (base-red #12581) ([#12610](https://github.com/diegosouzapw/OmniRoute/pull/12610)) +- **fix(quality):** re-point the zcodeProtocol public-creds allowlist to line 313 ([#12615](https://github.com/diegosouzapw/OmniRoute/pull/12615)) +- **fix(security):** close 3 advisories — search baseUrl exfil, sk- in the error sanitizer, bifrost relay header leak ([#12620](https://github.com/diegosouzapw/OmniRoute/pull/12620)) +- **fix(providers):** sanitize boolean required and nested bare maps for Gemini (#12269) ([#12624](https://github.com/diegosouzapw/OmniRoute/pull/12624)) — thanks @HouMinXi +- **fix(resilience):** keep Overloaded STREAM_EARLY_EOF off the provider breaker ([#12626](https://github.com/diegosouzapw/OmniRoute/pull/12626)) — thanks @HouMinXi +- **fix(catalog):** re-audit free-tier quotas against official pages ([#12649](https://github.com/diegosouzapw/OmniRoute/pull/12649)) +- **fix(dashboard):** keep a request's pending-tracking id stable across combo target retries ([#12650](https://github.com/diegosouzapw/OmniRoute/pull/12650)) — thanks @hartmark +- **fix(tests):** retire dead model ids from the chat-pipeline integration suite (base-red #12581) ([#12670](https://github.com/diegosouzapw/OmniRoute/pull/12670)) +- **fix:** resolve SqliteError no such table compression_run_telemetry during cleanup ([#12682](https://github.com/diegosouzapw/OmniRoute/pull/12682)) — thanks @groovecityJO +- **fix(combo):** treat a pin-only step as implicit connection allowlist ([#12697](https://github.com/diegosouzapw/OmniRoute/pull/12697)) — thanks @HouMinXi +- **fix(ci):** ship .npmrc in published package so legacy-peer-deps applies to consumers (#11544) ([#12699](https://github.com/diegosouzapw/OmniRoute/pull/12699)) — thanks @KooshaPari +- **fix(docker):** pin 4 CLI tools to exact versions (#12576) ([#12703](https://github.com/diegosouzapw/OmniRoute/pull/12703)) — thanks @KooshaPari +- **fix(docker):** document and harden cli profile trust boundary (#12570) ([#12706](https://github.com/diegosouzapw/OmniRoute/pull/12706)) — thanks @KooshaPari +- **fix(sse):** redact video transcript in pre-guardrail rejected-request logs (#12150 P2 item 7) ([#12710](https://github.com/diegosouzapw/OmniRoute/pull/12710)) +- **fix(i18n):** quote placeholder in OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES description (#12505) ([#12769](https://github.com/diegosouzapw/OmniRoute/pull/12769)) — thanks @KooshaPari +- **fix(api/pricing):** surface validation error message as string, not raw object (#12494) ([#12771](https://github.com/diegosouzapw/OmniRoute/pull/12771)) — thanks @KooshaPari +- **fix(sse):** re-enable prompt compression for native Codex passthrough ([#12834](https://github.com/diegosouzapw/OmniRoute/pull/12834)) — thanks @thomasmaerz +- **fix(dashboard):** model health tests for a provider node set to the Responses API now call `/v1/responses` with a Responses-shaped body instead of `/v1/chat/completions` — those models were reported as `Provider returned HTTP 200 but no text content` even though the same model answered normally through `/v1/responses` ([#13070](https://github.com/diegosouzapw/OmniRoute/issues/13070)) ([#13087](https://github.com/diegosouzapw/OmniRoute/pull/13087)) — thanks @ntdatt812 +- fix(routing): stop a round-robin combo's "opencode" targets from collapsing onto the opencode-zen connection (#11912) ([#13283](https://github.com/diegosouzapw/OmniRoute/pull/13283)) +- fix(routing): stop the reactive-compaction debug log from lying when compression is globally disabled (#11977) ([#13276](https://github.com/diegosouzapw/OmniRoute/pull/13276)) +- fix(electron): relativize standalone-bundle symlink targets so Stage 8 manifest verification stops failing on Windows (#11979) ([#13251](https://github.com/diegosouzapw/OmniRoute/pull/13251)) +- fix(dashboard): surface a visible error when Compression Studio's combined preview run fails (#12061) ([#13288](https://github.com/diegosouzapw/OmniRoute/pull/13288)) +- fix(dashboard): make the compression "Effective pipeline" preview honor the active profile and warn when the master switch is off (#12063) ([#13287](https://github.com/diegosouzapw/OmniRoute/pull/13287)) +- fix(providers): scope TinyCMS Web signer's DOM shims to each call instead of leaking them for the process lifetime, and surface a clean HTTP status on a non-JSON interception-toggles error (#12072) ([#13254](https://github.com/diegosouzapw/OmniRoute/pull/13254)) +- fix(guardrails): stop Vision Bridge from re-selecting a model locked after a 404 (#12111) ([#13259](https://github.com/diegosouzapw/OmniRoute/pull/13259)) +- fix(sse): require Responses-shaped body before native OpenAI-compatible passthrough (#12129) ([#13278](https://github.com/diegosouzapw/OmniRoute/pull/13278)) +- fix(providers): minimax-m3 now collapses manual thinking.type:"enabled" to adaptive, preventing upstream 400 (2013) (#12132) ([#13249](https://github.com/diegosouzapw/OmniRoute/pull/13249)) +- fix(db): scope model visibility overrides by modality so hiding a Chat model no longer hides an identically-ID'd Image/Embeddings/etc. model (#12172) ([#13258](https://github.com/diegosouzapw/OmniRoute/pull/13258)) +- fix(db): scope local-provider apiKey dedup to matching base URL so LM Studio/Ollama-style connections support multiple accounts (#12173) ([#13250](https://github.com/diegosouzapw/OmniRoute/pull/13250)) +- fix(providers): refresh Trae's stale Referer/Origin and forward user timezone so imported connections stop failing with 401 (#12190) ([#13255](https://github.com/diegosouzapw/OmniRoute/pull/13255)) +- fix(providers): route opencode-go/gpt-5.6-luna to /responses instead of /chat/completions (#12196) ([#13275](https://github.com/diegosouzapw/OmniRoute/pull/13275)) +- fix(dashboard): allow deleting the last extra-upstream-header row even when invalid (#12251) ([#13252](https://github.com/diegosouzapw/OmniRoute/pull/13252)) +- **fix(i18n):** translate pre-existing `__MISSING__:` keys for `combo.sort`, `requestLogger.detail` expand/collapse, `common.profile`, and `settings.resilienceCredentialHealth*` across 39 locales ([#12272](https://github.com/diegosouzapw/OmniRoute/issues/12272)) ([#13034](https://github.com/diegosouzapw/OmniRoute/pull/13034)) — thanks @HouMinXi +- fix(cli): run the Node.js runtime compatibility guard before the heavy `tsx/esm` + Commander import chain so an unsupported runtime gets a clear message instead of a raw `Invalid regular expression flags` crash (#12296) ([#13253](https://github.com/diegosouzapw/OmniRoute/pull/13253)) +- fix(dashboard): refresh the providers list after deleting a compatible provider node (#12298) ([#13256](https://github.com/diegosouzapw/OmniRoute/pull/13256)) +- fix(usage): fail closed on API-key budget enforcement when a provider's `auto` routing alias has no pricing row, instead of silently counting it as $0 (#12341) ([#13257](https://github.com/diegosouzapw/OmniRoute/pull/13257)) +- **fix(providers):** include the submitted Agnes video model when polling by `video_id` ([#12356](https://github.com/diegosouzapw/OmniRoute/pull/12356)) — thanks @pacocartones +- **fix(routing):** custom OpenAI-compatible nodes now honor the saved Chat/Responses API type after edits instead of letting the node's original ID prefix override the live connection setting ([#11884](https://github.com/diegosouzapw/OmniRoute/issues/11884)). ([#12358](https://github.com/diegosouzapw/OmniRoute/pull/12358)) — thanks @pacocartones +- fix(sse): surface an error instead of a silent empty 200 when a Claude stream closes with zero bytes (#12398) ([#13285](https://github.com/diegosouzapw/OmniRoute/pull/13285)) +- fix(oauth): warn before the dead localhost:8080 redirect in antigravity/gemini `oauth start` (#12413) ([#13265](https://github.com/diegosouzapw/OmniRoute/pull/13265)) +- fix(providers): stop devin-cli spawn error from double-closing the SSE controller (#12517) ([#13261](https://github.com/diegosouzapw/OmniRoute/pull/13261)) +- **fix(rerank):** clamp Voyage `top_k` to the documents actually sent after empty-string filtering, and honor `return_documents: false` in the NVIDIA response adapter (#12523 — thanks @pacocartones) +- **fix(audio):** `/v1/audio/translations` now resolves combo names the way `/v1/audio/transcriptions` already does, so a combo that `GET /v1/models` advertises is fanned out to its targets instead of being rejected with `400 Invalid translation model: . Use format: provider/model`; literal `provider/model` ids and unknown bare names behave as before (#12536 — thanks @pacocartones) +- **fix(gemini):** strip the JSON-Schema-2020-12 `prefixItems` keyword from Gemini tool schemas at every nesting level, so Claude Code tool definitions no longer fail with `400 Unknown name "prefixItems"` on Gemini models (#12540 — thanks @pacocartones) +- **fix(api-manager):** Expose an accessible loading status while API keys are fetched instead of an empty accessibility tree (#12541 — thanks @pacocartones) +- **fix(video):** Clamp `estimateJpegFrameBytes` at zero for padding-only payloads and build the three encode-side frame data URIs from `JPEG_FRAME_DATA_URI_PREFIX` instead of a repeated literal (#12543 — thanks @pacocartones) +- **fix(devin):** accept Windows `DEVIN_AGENTIC_HOME` sandbox paths (`C:\...\.sandbox\...`) in the isolated-home check so the Devin Claude Bridge no longer fails closed on Windows ([#12405](https://github.com/diegosouzapw/OmniRoute/issues/12405)) (#12545 — thanks @pacocartones) +- **fix(db):** Add `conversation_turn_nodes` and orphaned `agentic_conversations` to the auto-cleanup cycle under the existing `retention.callLogs` window, so identity nodes whose call-log content has already been purged no longer accumulate without bound in `storage.sqlite` (#12548 — thanks @pacocartones) +- **fix(i18n):** Wrap the `~/.claude/profiles//settings.json` placeholder in ICU single quotes in the `OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES` feature-flag description across all 42 locales and the TypeScript default, so next-intl no longer fails with `INVALID_MESSAGE: UNCLOSED_TAG` and the Feature Flags card shows the description instead of the raw key (#12549 — thanks @pacocartones) +- **fix(orchestration):** `updateCloudAgentTask` now publishes the task's real `status` on `agent.task.updated` when an update only touches `result`, `activities` or `error`, instead of the fabricated `"updated"` state, and stays silent when no row matched the id (#12550 — thanks @pacocartones) +- **fix(i18n):** the home "Recent Requests" panel and the Provider Topology legend are now translated instead of rendering English copies on non-English dashboards; the legend reads its own `home.topologyLegend*` labels with consistent casing rather than borrowing the memory-settings "Recent" and analytics "Error" strings (#12551 — thanks @pacocartones). +- **docs(reference):** bring the `FEATURE_FLAGS.md` catalog back to 1:1 with `featureFlagDefinitions.ts` — 20 missing flags added, the two `*_BLOCK_THRESHOLD` env-only knobs moved out of the flag tables, category/total counts and the Live WS port corrected, guarded by a static test (#12552 — thanks @pacocartones) +- **fix(i18n):** backfill missing `usage.kiloPass*` strings in 39 locales and restore `featureFlagOmnirouteDisableThinkingLevelVariantsDescription` in `pt.json` ([#12561](https://github.com/diegosouzapw/OmniRoute/issues/12561)) ([#13027](https://github.com/diegosouzapw/OmniRoute/pull/13027)) — thanks @HouMinXi +- fix(docker): default docker-compose app ports (dashboard/API/live-WS) to loopback instead of `0.0.0.0`, closing the anonymous `/v1` LAN/WAN exposure gap left open by `REQUIRE_API_KEY=false` (#12568) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): close DNS-rebinding SSRF gap in webhook outbound-URL guard (#12569) ([#13243](https://github.com/diegosouzapw/OmniRoute/pull/13243)) +- fix(docker): require a per-session token on the VNC browser CDP bridge and isolate it on a dedicated Docker network (#12571) ([#13241](https://github.com/diegosouzapw/OmniRoute/pull/13241)) +- fix(open-sse): write Adobe Firefly session tokens and cookie jars with 0700/0600 permissions instead of the process umask (#12572) ([#13242](https://github.com/diegosouzapw/OmniRoute/pull/13242)) +- fix(api): remove hardcoded wildcard CORS in openai-to-gemini-sse.ts so the centralized fail-closed CORS gate is the sole source of `Access-Control-Allow-Origin` (#12573) ([#13239](https://github.com/diegosouzapw/OmniRoute/pull/13239)) +- fix(api): enforce API key policy (budget/rate-limit/schedule/endpoint scoping) on the ElevenLabs speech-to-text, text-to-speech and voices proxy routes (#12574) ([#13244](https://github.com/diegosouzapw/OmniRoute/pull/13244)) +- fix(sse): cap HuggingChat NDJSON body size and bound the read loop with the fetch timeout so a stalled or hostile upstream cannot buffer unbounded memory (#12577) ([#13282](https://github.com/diegosouzapw/OmniRoute/pull/13282)) +- fix(docker): scope the cliproxyapi/qdrant/bifrost sidecars to loopback by default and forward `CLIPROXYAPI_MANAGEMENT_KEY` into the cliproxyapi container so its management API is not left both unauthenticated and LAN-published (#12578) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): create DB export temp paths with `fs.mkdtempSync` instead of predictable timestamps (#12579) ([#13237](https://github.com/diegosouzapw/OmniRoute/pull/13237)) +- Cline 401 bodies that say "re-authenticate your Cline account" classify as a refreshable OAuth token, not a terminal expired key. The cooling panel no longer labels every cooldown as a 429; it shows the recorded last error instead. (#12594) ([#13060](https://github.com/diegosouzapw/OmniRoute/pull/13060)) — thanks @HouMinXi +- **fix(catalog):** degrade unknown combo targets instead of dropping LCD modalities, and persist OpenRouter `architecture.input_modalities` into the capability snapshot ([#12613](https://github.com/diegosouzapw/OmniRoute/issues/12613)) ([#13042](https://github.com/diegosouzapw/OmniRoute/pull/13042)) — thanks @HouMinXi +- fix(providers): send `x-api-key` instead of `Authorization: Bearer` for OpenCode Zen's `/v1/responses` endpoint (Muse Spark Contributor models), fixing a 401 on OmniRoute's auth header (#12633) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(sse): surface the actionable "Auggie CLI not found" message when the shell reports a missing `auggie` binary via exit code instead of a spawn error (#12645) ([#13263](https://github.com/diegosouzapw/OmniRoute/pull/13263)) +- **fix(config):** Persist deletions of built-in background-degradation entries — when a stored settings record exists its `degradationMap` is now authoritative instead of being merged under the defaults, so an entry the user removed in the dashboard no longer reappears on the next apply or restart ([#12424](https://github.com/diegosouzapw/OmniRoute/issues/12424)) ([#12647](https://github.com/diegosouzapw/OmniRoute/pull/12647)) — thanks @pacocartones +- **fix(gamification):** action-count badge milestones (First Token, Token Consumer, Token Machine, Token Whale, and the token-sharing tier) are now backed by a durable `xp_action_counts` counter incremented in `addXp()`, instead of a live `COUNT(*)` over `xp_audit_log`. The audit log is pruned by `retention.xpAuditLog` (default 30 days), so on a default install those "lifetime" milestones were really "actions in the last 30 days" and unlocked badges could stop unlocking once old rows aged out. `getActionCount()` and `checkActionCountBadges()` now read the same durable source, and a migration backfills existing totals from the surviving audit rows ([#12546](https://github.com/diegosouzapw/OmniRoute/issues/12546)) ([#12651](https://github.com/diegosouzapw/OmniRoute/pull/12651)) — thanks @pacocartones +- **fix(images):** `/v1/images/edits` now iterates a combo's targets the same way `/v1/images/generations` does (#9239) instead of flattening a bare combo to its first target. A combo whose first target is not edit-capable — or lacks credentials — now falls through to a later edit-capable target rather than hard-erroring, and missing credentials are skipped (not a hard `401`) to match the generations path. The per-target skip/terminal classification is extracted into a shared `runImageComboTargets` loop, so generations behavior is unchanged ([#12547](https://github.com/diegosouzapw/OmniRoute/issues/12547)). ([#12653](https://github.com/diegosouzapw/OmniRoute/pull/12653)) — thanks @pacocartones +- fix(sse): add first-byte watchdog to the TLS-fingerprint transport so a stalled wreq body falls back instead of hanging for minutes (#12656) ([#13272](https://github.com/diegosouzapw/OmniRoute/pull/13272)) +- fix(sse): exempt tiny-budget reasoning probes from combo quality failure and surface persisted-cooldown skips in ALL_TARGETS_SKIPPED diagnostics (#12659) ([#13269](https://github.com/diegosouzapw/OmniRoute/pull/13269)) +- fix(models): declare the real ~1M contextLength for OpenCode Zen's Muse Spark 1.2 models instead of falling back to the 200000 provider default (#12681) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(oauth): align codebuddy-cn OAuth User-Agent with the chat/usage CLI version to avoid WAF false positives (#12702) ([#13264](https://github.com/diegosouzapw/OmniRoute/pull/13264)) +- fix(dashboard): surface an authentication-required banner instead of silently blanking database settings for a guest session (#12709) ([#13270](https://github.com/diegosouzapw/OmniRoute/pull/13270)) +- **fix(resilience):** stop unbounded queue that hangs 6min until Aborted — gate, provider slot, and Bottleneck queue now share a per-connection `maxWaitMs` budget; `fail-closed` on exhaust (503); execution backstop `executionMaxWaitMs` overridable per connection with upstream clamp ([#12715](https://github.com/diegosouzapw/OmniRoute/pull/12715)) — thanks @maxmad64bis (with thanks to @Tushar49 for surfacing the slow-provider need in #12635) +- **fix(auto-combo):** every mode pack now carries `quality` and `reliability` weights (`quality` 0.02, 0.03 in `quality-first`; `reliability` 0.03, 0.04 in `reliability-first`), so selecting a pack no longer silences either signal; `DEFAULT_WEIGHTS` is unchanged ([#12731](https://github.com/diegosouzapw/OmniRoute/pull/12731)) — thanks @maxmad64bis +- **fix(sse):** the provider execution pipeline keeps the upstream error `code`/`type` again — both were lost when the non-streaming leg was extracted, so a config-class `422` (Antigravity missing project) degraded into a generic account cooldown instead of reaching the gate that recognises it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(db):** a legacy `call_logs` table no longer aborts startup — the provider-stats index is created after the column healing runs, not before it, so an install predating `request_type` opens instead of failing with `no such column` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(catalog):** a malformed row in the operator-writable custom-models blob no longer kills every `auto/*` pool ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(tests):** realign the guards that the same day's merges left asserting the old behavior — the provider-count total after GreenPT and EURouter, the GLM stream buffer slot that `#12925` turned into a declared parameter, and the injection scan bound that `#13104` reshaped from a head-only window into head-plus-tail ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- fix(cache): fold tool_choice/tools/response_format into the semantic cache signature so a cached tool_calls response can no longer be replayed for a request whose tool policy forbids it (#12734) ([#13267](https://github.com/diegosouzapw/OmniRoute/pull/13267)) +- **fix(models):** stop treating provider-supplied `isFree:true`/`:free`/`0/0` flags as trusted unless the provider has a documented free tier — only the shipped free-tier catalog decides otherwise; fetched `isFree:true` trusted only for a free-tier provider, custom `isFree:true` via a trusted path ([#12744](https://github.com/diegosouzapw/OmniRoute/pull/12744)) — thanks @maxmad64bis +- fix(memory): authenticate the internal /v1/rerank loopback call so memory reranking no longer silently degrades to unranked order when REQUIRE_API_KEY=true (#12745) ([#13268](https://github.com/diegosouzapw/OmniRoute/pull/13268)) +- fix(sse): parse Ollama Cloud's current usage markup (`$X of $Y used` aria-label, nested width style) (#12749) ([#13260](https://github.com/diegosouzapw/OmniRoute/pull/13260)) +- fix(cli): setup-opencode no longer sends an active context's management token to `/v1/models` when `--api-key`/`OMNIROUTE_API_KEY` is supplied — an explicit flag or the env var now always outranks the context's token, and the flag itself is no longer swallowed by the parent program's global `--api-key` option (#12783) ([#13246](https://github.com/diegosouzapw/OmniRoute/pull/13246)) +- fix(sse): register Arcee AI in the executor provider registry so requests reach api.arcee.ai instead of silently falling back to OpenAI (#12784) ([#13277](https://github.com/diegosouzapw/OmniRoute/pull/13277)) +- **fix(quota):** share one upstream quota read when identical saturation checks arrive at the same time, so traffic bursts don't multiply provider API calls ([#12787](https://github.com/diegosouzapw/OmniRoute/pull/12787)) — thanks @maxmad64bis +- **fix(db):** ignore expired or invalid rate-limit cooldown writes so a stale timestamp can't lock a connection that should be usable — clearing still works as before ([#12788](https://github.com/diegosouzapw/OmniRoute/pull/12788)) — thanks @maxmad64bis +- **fix(providers):** single source for provider order with `xao` ranking alongside `xai-oauth` ([#12790](https://github.com/diegosouzapw/OmniRoute/pull/12790)) — thanks @maxmad64bis +- **fix(routing):** off-table models check the free-model catalog before inheriting premium prices, latency bootstraps from the observed pool median, async tiers read live database pricing with a 90-day freshness gate, and the tier cache invalidates on every pricing write ([#12792](https://github.com/diegosouzapw/OmniRoute/pull/12792)) — thanks @maxmad64bis +- **fix(auto-combo):** snapshot scoring uses the observed per-connection account tier and per-model quality instead of neutral constants, and circuit-open providers rank lower at build time ([#12794](https://github.com/diegosouzapw/OmniRoute/pull/12794)) — thanks @maxmad64bis +- **fix(pool):** empty `auto/*` pools now say why they are empty, and the models listing uses the same paid check as routing ([#12795](https://github.com/diegosouzapw/OmniRoute/pull/12795)) — thanks @maxmad64bis +- fix(routing): recognize CLIProxyAPI's 'unknown provider for model' 400 as fallback-worthy (#12800) ([#13284](https://github.com/diegosouzapw/OmniRoute/pull/13284)) +- **fix(sse):** translate-mode streams now emit the estimated token counts as a trailing usage-only chunk before `[DONE]` when the upstream stays silent, so metered chat clients see totals instead of nothing ([#12828](https://github.com/diegosouzapw/OmniRoute/pull/12828)) — thanks @maxmad64bis +- **fix(db):** provider stats stay truthful on empty and legacy databases: fallback counts default to `0` instead of `null`, latency averages read `null` (not `0`) when no durations were recorded, failures logged before the error-type column existed group under `pre_migration` instead of `unclassified`, and per-provider queries use two new composite indexes ([#12832](https://github.com/diegosouzapw/OmniRoute/pull/12832)) — thanks @maxmad64bis +- fix(nvidia): fail open when a synced model catalog goes stale instead of gating forever (#12849) ([#13248](https://github.com/diegosouzapw/OmniRoute/pull/13248)) +- **fix(db):** WAL maintenance lives in its own module: a `TRUNCATE` checkpoint that hits a busy database now warns with its streak and retries once via `PASSIVE` instead of logging success, and closing-time checkpoints no longer report success on builds without a database file, with the busy totals visible in the authenticated monitoring health payload ([#12853](https://github.com/diegosouzapw/OmniRoute/pull/12853)) — thanks @maxmad64bis +- **fix(health):** quota with no snapshots reads empty instead of a contradicting 0% ([#12857](https://github.com/diegosouzapw/OmniRoute/pull/12857)) — thanks @maxmad64bis +- fix(a2a): accept the dashboard's own session cookie on /a2a so "Run message/send" no longer fails with "Unauthorized: missing or invalid API key" (#12888) ([#13271](https://github.com/diegosouzapw/OmniRoute/pull/13271)) +- **fix(logs):** the Logs grid's in-memory filter pass no longer discards rows the SQL query already matched — selecting an API key from the dropdown (which sends the key's id) returns its calls again, the Combo tab shows every combo instead of only those whose name contains a "1", and the model filter and search cover the same columns as the query ([#12896](https://github.com/diegosouzapw/OmniRoute/pull/12896)) — fixes [#12873](https://github.com/diegosouzapw/OmniRoute/issues/12873) — thanks @ntdatt812 +- **fix(a2a):** `/api/a2a/status` now builds the agent card from the request that asked for it, so a gateway reached at a non-localhost host no longer advertises `http://localhost:20128` as its A2A URL ([#12918](https://github.com/diegosouzapw/OmniRoute/pull/12918)). — thanks @ntdatt812 +- **fix(compression):** progressive aging now appends its `[COMPRESSED:aging:…]` annotation after a turn's `tool_result` blocks instead of in front of them, so Anthropic no longer rejects aged conversations with "`tool_use` ids were found without `tool_result` blocks immediately after" ([#12920](https://github.com/diegosouzapw/OmniRoute/pull/12920)). — thanks @ntdatt812 +- **fix(bedrock):** model import now resolves context limits for every vendor prefix instead of only `anthropic.*`, so `global.openai.gpt-5.6-*` no longer imports with a null `inputTokenLimit` and gets rejected pre-flight at the 200k default ([#12921](https://github.com/diegosouzapw/OmniRoute/pull/12921)). — thanks @ntdatt812 +- **fix(stream):** the 64 KB stream buffer GLM asks for is honoured instead of dropped, and the type error it caused no longer fails the API Route Typecheck gate on every open PR ([#12925](https://github.com/diegosouzapw/OmniRoute/pull/12925)) — thanks @ntdatt812 +- **fix(guardrails):** mask PII inside a `tool_result`'s nested content array, which the masker walked past while redacting its sibling block ([#12930](https://github.com/diegosouzapw/OmniRoute/pull/12930)) — thanks @ntdatt812 +- **fix(dashboard):** the Radar catalog table no longer leaves absent data unexplained — empty limits, unknown context windows, and unreported capabilities each explain themselves on hover, and a new check keeps it that way ([#12937](https://github.com/diegosouzapw/OmniRoute/pull/12937)) — thanks @maxmad64bis +- **fix(sse):** geo-blocked opencode requests rotate to the next account proxy instead of failing, so one refused egress no longer aborts the whole chain ([#12941](https://github.com/diegosouzapw/OmniRoute/pull/12941)) — thanks @maxmad64bis +- **fix(codex):** dashboard "clear cooldown" and the CAS recovery path now drop nested `codexScopeRateLimitedUntil` maps in the same write that nulls `rate_limited_until`, and fresh quota snapshots with headroom lift fallback-sourced scope cooldowns parked by quota preflight ([#12817](https://github.com/diegosouzapw/OmniRoute/issues/12817), [#12860](https://github.com/diegosouzapw/OmniRoute/issues/12860), [#12951](https://github.com/diegosouzapw/OmniRoute/pull/12951)) The dashboard reset-credit button (`consumeCodexResetCredit`) now exercises that same snapshot path after a successful redeem, so a filled quota bar is enough to unpark a leftover fallback Codex child without another manual clear. — thanks @HouMinXi +- **fix(db):** resolve `sql-wasm.wasm` across global npm install and hoisted layouts, ensuring OmniRoute can boot cleanly on Node 24 when native `better-sqlite3` is uncompiled. ([#13035](https://github.com/diegosouzapw/OmniRoute/pull/13035)) — thanks @HouMinXi +- **fix(api):** `DELETE /v1/batches/delete-completed` now sweeps only the calling API key's own completed batches (batches with no owner stay out of a key-scoped sweep on purpose), with an explicit instance-wide mode reserved for authenticated dashboard sessions, a 401 for a presented key that is unknown, revoked, deactivated, banned or expired (never falling through to the session branch), audit logging of every sweep, a sanitized 500 on failure, an owner-scoped file half (a key-scoped sweep never nulls a file another tenant owns) and an atomic sweep — chunked in 200-batch transactions in instance mode — so a mid-way error never leaves a batch pointing at a nulled file (GHSA-wvxc-jp3v-5mg5) ([#12969](https://github.com/diegosouzapw/OmniRoute/pull/12969)) +- **fix(combo):** quota-weighted routing stops drawing on an out-of-credit connection — a 402 now invalidates the stored quota snapshot instead of leaving its stale remaining percentage in place, and a snapshot older than 10 minutes no longer counts as confident headroom for the primary pool ([#12972](https://github.com/diegosouzapw/OmniRoute/pull/12972)) ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- **fix(vision):** Custom Models with "Vision capable" checked no longer have image requests swapped to `glm/glm-4.6v` when the client sends the advertised alias (`vllm/path/...`) or the bare path-shaped id — Vision Bridge now matches the stored override for all three id forms ([#12758](https://github.com/diegosouzapw/OmniRoute/issues/12758)) ([#12974](https://github.com/diegosouzapw/OmniRoute/pull/12974)) — thanks @HouMinXi +- **fix(sse):** transient opencode upstream failures rotate to the next account proxy instead of failing, so one flapping egress no longer aborts the whole chain ([#12975](https://github.com/diegosouzapw/OmniRoute/pull/12975)) — thanks @maxmad64bis +- **fix(azure):** Deployments from GPT-6 onward now send `max_completion_tokens` instead of `max_tokens`, which Azure rejects with HTTP 400. The rule matched a literal `gpt-5`, so each new generation arrived broken; it now matches the generation range, while `gpt-35-turbo` still keeps `max_tokens`. ([#13007](https://github.com/diegosouzapw/OmniRoute/pull/13007)) — thanks @ntdatt812 +- **fix(db):** add an opt-in self-restart circuit for sustained critical memory pressure, gate post-cleanup VACUUM behind a minimum freed-rows threshold, and checkpoint the SQLite WAL every 5 minutes with a size guard that escalates to TRUNCATE, so a growing WAL can no longer stall the event loop into a full outage. ([#13011](https://github.com/diegosouzapw/OmniRoute/pull/13011)) — thanks @HouMinXi +- **fix(auth):** an explicit connection pin may probe a quota-disabled row once and re-enable it on success ([#12874](https://github.com/diegosouzapw/OmniRoute/issues/12874)) ([#13017](https://github.com/diegosouzapw/OmniRoute/pull/13017)) — thanks @HouMinXi +- **fix(api):** thread `X-OmniRoute-Fallback-Attempts` through combo chat completions so streaming and non-streaming responses report how many prior legs were attempted ([#13038](https://github.com/diegosouzapw/OmniRoute/pull/13038)) — thanks @HouMinXi +- Restore provider failure classification and credential refresh on non-streaming requests: classify non-2xx failures to lock models on per-model quota exhaustion, update connection rate limits from headers and body, and pass credential refresh handlers to pipeline execution so 401 tokens can be refreshed and retried (#13043). ([#13069](https://github.com/diegosouzapw/OmniRoute/pull/13069)) — thanks @HouMinXi +- **fix(responses):** wrap forced-non-streaming web_search fallback JSON as Responses SSE so Codex still sees `response.completed` ([#13050](https://github.com/diegosouzapw/OmniRoute/pull/13050)) — thanks @HouMinXi +- **fix(antigravity):** Preserve upstream thought-token usage in normalized completion and reasoning token counts ([#13055](https://github.com/diegosouzapw/OmniRoute/pull/13055)) — thanks @pacocartones +- Fix a concurrency-slot leak in the DeepSeek PoW solver: a worker that failed to spawn (for example a missing worker script) never released its slot, so `MAX_CONCURRENT_WORKERS` failures disabled the solver until restart. ([#13097](https://github.com/diegosouzapw/OmniRoute/pull/13097)) — thanks @anhtahaylove +- **fix(acp):** bound the ACP session output buffers — `stdoutBuffer` and `stderrBuffer` now cap at 1 MiB keeping the most recent output behind a visible `[...output truncated...]` marker, and `stderrBuffer` is reset per prompt instead of accumulating for the lifetime of the session. ([#13100](https://github.com/diegosouzapw/OmniRoute/pull/13100)) — thanks @anhtahaylove +- **fix(acp):** release the `stdout`/`exit` listeners and the idle timer that a `sendPrompt` timeout used to leave attached to the `acpManager` singleton, and drop sessions that exited on their own from the session map instead of keeping them forever. ([#13096](https://github.com/diegosouzapw/OmniRoute/pull/13096)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection and PII scanners now read the text a `tool_result` block carries on `content` (string or nested block list), in messages and in system blocks, so tool output is judged by the same rules as user text ([#13101](https://github.com/diegosouzapw/OmniRoute/pull/13101)) — thanks @ntdatt812 +- **fix(gamification):** close the badge notification SSE stream when the request signal is already aborted before the stream starts — a client that disconnects while the route is still awaiting auth used to leave both the 2s unlock poll and the 15s heartbeat running for the lifetime of the process. ([#13106](https://github.com/diegosouzapw/OmniRoute/pull/13106)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection scan now spends its 16 KB budget on both ends of the request instead of the first 16 KB only, so `system`, `instructions`, `query`, `documents` and the newest turns are no longer hidden behind one long message ([#13104](https://github.com/diegosouzapw/OmniRoute/pull/13104)) — thanks @ntdatt812 +- **fix(dashboard):** expose the Volcano Ark console cookie on quota scraping and unwrap connect-error objects so the dashboard shows the upstream message ([#13107](https://github.com/diegosouzapw/OmniRoute/pull/13107)) — thanks @HouMinXi +- **fix(db):** release the `beforeExit`/`SIGINT`/`SIGTERM` handlers when a `node:sqlite` adapter closes, so a closed adapter and its database handle are no longer pinned to `process` for the lifetime of the run — the same treatment #7494 gave the sql.js adapter. ([#13109](https://github.com/diegosouzapw/OmniRoute/pull/13109)) — thanks @anhtahaylove +- **fix(translator):** `contentSchema` and `unevaluatedItems` are now treated as subschema positions by the tool-schema sanitizer, so a truncation placeholder in either is replaced with a permissive schema instead of being forwarded as a string ([#13110](https://github.com/diegosouzapw/OmniRoute/pull/13110)) — thanks @ntdatt812 +- **fix(cli-helper):** clear the `createLogStream` timeout on the abort path — `stop()` aborts the in-flight fetch and returned through the `signal.aborted` branch, which skipped `clearTimeout` and left an armed timer per stopped stream. The stream reader is now also cancelled when the read loop exits early. ([#13114](https://github.com/diegosouzapw/OmniRoute/pull/13114)) — thanks @anhtahaylove +- **fix(docs):** drop TLS-impersonation, MITM-decrypt, supply-chain attestation, and XOR-mask writeups from the public `/docs` catalog and Docker image. Files stay in git for engineers; operators who need them open the repo, not the website. ([#13136](https://github.com/diegosouzapw/OmniRoute/pull/13136)) — thanks @HouMinXi +- **fix(combo):** parse numeric-epoch `rate_limited_until` in the combo cooldown read path ([#13141](https://github.com/diegosouzapw/OmniRoute/pull/13141)) — thanks @maxmad64bis +- **fix(opencode):** both OpenCode plugins now reject a gateway address typed without `http://` at configuration time, instead of publishing every model with an api url no client can call, and the v2 plugin no longer publishes a model card whose api url is blank or relative ([#13142](https://github.com/diegosouzapw/OmniRoute/pull/13142)) — thanks @maxmad64bis +- **fix(providers):** lock opencode model on upstream 400 model-unavailable ([#13146](https://github.com/diegosouzapw/OmniRoute/pull/13146)) — thanks @maxmad64bis +- **fix(logging):** keep the provider exchange rather than the raw client bodies when a call log exceeds its size budget, and show that recovered payload in the request-detail panel instead of replacing it with the stored response body ([#13147](https://github.com/diegosouzapw/OmniRoute/pull/13147)) — thanks @maxmad64bis +- **fix(traffic-inspector):** the WebSocket route no longer leaks a traffic-buffer subscriber and a 30s ping timer when the client socket is already closed at handler time — listeners are attached before any resource is acquired, a destroyed socket bails out early, and the ping interval stops on a dead socket where `write()` never throws ([#13155](https://github.com/diegosouzapw/OmniRoute/pull/13155)) — thanks @anhtahaylove +- **fix(telegram):** bound the per-user API key cache in the Telegram chat proxy so a burst of distinct chat ids can no longer grow the process heap without limit ([#13165](https://github.com/diegosouzapw/OmniRoute/issues/13165)) ([#13166](https://github.com/diegosouzapw/OmniRoute/pull/13166)) — thanks @anhtahaylove +- **fix(stream):** cancel the upstream response body when the JSON-to-SSE sniff unwinds on a body timeout, so a stalled upstream no longer pins the connection ([#13169](https://github.com/diegosouzapw/OmniRoute/issues/13169)) ([#13171](https://github.com/diegosouzapw/OmniRoute/pull/13171)) — thanks @anhtahaylove +- **fix(telegram):** authenticate webhook deliveries with Telegram's `secret_token` so an unauthenticated caller can no longer mint API keys or spend upstream quota ([#13172](https://github.com/diegosouzapw/OmniRoute/issues/13172)) ([#13175](https://github.com/diegosouzapw/OmniRoute/pull/13175)) — thanks @anhtahaylove +- **fix(models):** give discoverable Gemini 3.8 Flash ids their own 65536 output spec so Antigravity no longer clamps them to 16384 ([#13195](https://github.com/diegosouzapw/OmniRoute/pull/13195)) — thanks @HouMinXi +- **fix(dashboard):** leftover catalog-removed provider rows (gemini-cli) can be listed and purged from the providers page ([#13067](https://github.com/diegosouzapw/OmniRoute/issues/13067)) ([#13197](https://github.com/diegosouzapw/OmniRoute/pull/13197)) — thanks @HouMinXi +- **fix(auth):** a dashboard session now requires the `authenticated: true` claim that login, OIDC and the session refresh already emit — a JWT merely signed with `JWT_SECRET` (for example the Cursor CLI passthrough token, which any API-key holder can obtain) no longer verifies as the `auth_token` cookie on any route, the WebSocket handshake or the live server; existing sessions keep working ([#13298](https://github.com/diegosouzapw/OmniRoute/issues/13298)) ([#13375](https://github.com/diegosouzapw/OmniRoute/pull/13375)) +- **fix(api):** `DELETE /v1/batches/delete-completed` now applies the caller's API-key policy (endpoint allowlist, schedule, usage cap, rate limit) like every other `/v1` route, commits the key-scoped sweep per 200-batch chunk instead of holding one write lock for the whole sweep, refuses to loop without progress, and rejects a scope that names both a key and `allTenants` ([#13684](https://github.com/diegosouzapw/OmniRoute/pull/13684)) +- Restore the API-route typecheck gate: the non-streaming leg result lost its discriminated-union narrowing after the server-owned tool loop reassignment, producing 13 new TS2339 diagnostics in `chatCore.ts`. ([#12963](https://github.com/diegosouzapw/OmniRoute/pull/12963)) +- Restore four non-streaming behaviours the server-owned tool loop refactor dropped: client aborts map to 499 with the fixed `Request aborted` message, an aborted request no longer logs a synthetic `clientResponse`, Claude prompt-cache telemetry is recorded again, and a body that cannot be canonicalized no longer throws when the tool loop is off. ([#12990](https://github.com/diegosouzapw/OmniRoute/pull/12990)) +- **fix(skills):** The CLI registry parser now reads positionals declared with `.addArgument()`, not only those written inline in `.command()`. `tunnel create [type]` was being published as `tunnel create`, so the agent-skills sync gate reported drift on every branch and regenerating would have deleted the argument. ([#13009](https://github.com/diegosouzapw/OmniRoute/pull/13009)) — thanks @ntdatt812 +- **fix(combos):** dashboard combo test uses a short prompt, serial probes, and a 60s timeout so reasoning models and rate-limited free pools do not fail the health check ([#13001](https://github.com/diegosouzapw/OmniRoute/pull/13001)) — thanks @HouMinXi +- fix(compression): terminate idle worker threads on eviction so long-running instances stop leaking OS threads and MessagePorts ([#13091](https://github.com/diegosouzapw/OmniRoute/pull/13091)) — thanks @anhtahaylove +- Redact Google API keys of any length in error bodies: the pattern required exactly 39 characters, so shorter or longer `AIza…` credentials passed through unredacted. ([#12964](https://github.com/diegosouzapw/OmniRoute/pull/12964)) +- **fix(grok-cli):** a 402 "Grok Build usage balance exhausted" parks that Grok login as out of credit (Grok Build CLI, grok.com cookie, and xAI OAuth share the weekly pool). Combo routing then tries the next login instead of locking the model for every account in the pool ([#13061](https://github.com/diegosouzapw/OmniRoute/pull/13061)) — thanks @HouMinXi +- fix(compression): spawn the LLMLingua worker with a file URL object so compression actually runs instead of silently failing open on Node ([#13093](https://github.com/diegosouzapw/OmniRoute/pull/13093)) — thanks @anhtahaylove +- **fix(test):** run the local `test` and `test:unit` scripts at concurrency 4 so a full-suite run no longer exhausts the machine's commit charge and kills unrelated processes ([#13187](https://github.com/diegosouzapw/OmniRoute/pull/13187)) — thanks @anhtahaylove +- **fix(monitoring):** `GET /api/monitoring/health` `credentialHealth` now includes a bounded `failedConnections` list (`connectionId`, `status`, sanitized `lastError`) when the probe-cache gauge `failed>0`, plus `source: probe-cache` and a cheap `staleDbNonOkCount` for sticky SQLite `test_status` on active rows. Documents that the live gauge is not `provider_connections.test_status`. ([#12876](https://github.com/diegosouzapw/OmniRoute/pull/12876)) — thanks @RaviTharuma +- **fix(resilience):** an openai-compatible multi-upstream gateway no longer marks the whole connection `credits_exhausted` when a single upstream (or the `/models` / representative-model health probe) returns 402. Quota failures stay model-scoped; true connection-wide auth failures are unchanged. Single-credential 402 key disable ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239)) is preserved. ([#12875](https://github.com/diegosouzapw/OmniRoute/pull/12875)) — thanks @RaviTharuma +- **fix(dashboard):** nine fixes on the Orchestration Canvas, all diagnosed in the Phase 2 reviews. A source that fails now keeps the timestamp of its FIRST failure instead of being re-stamped every poll — the stale line said "since the last poll" no matter how long the source had been down, and the churn also defeated the snapshot's stable identity (it serializes the source list), so the canvas re-rendered on every tick while anything was broken. A source that HAD data and only then started failing is flagged too: previously only a source with no node at all got the warning, so a source that went down mid-session kept a healthy-looking node forever. The rest are pointwise: clicking a filter chip cancels the pending search debounce (left armed, it fired ~300ms later and silently reverted the chip); the search input carries an accessible name; `?state=running, failed` parses like the unpadded form instead of dropping the padded value; the CSV toggle helper is defined once in `model/urlParams.ts` rather than twice; the agents tab tells "nothing running" apart from "the filter matched nothing", offering a clear-filters button instead of setup links that would be wrong advice there; edges stop emitting SMIL particles above 40 simultaneously active edges, keeping the colored stroke; and the drawer's error banner clears when a retried action succeeds. ([#12983](https://github.com/diegosouzapw/OmniRoute/pull/12983)) +- **fix(i18n):** the nine locales added with the EU-language batch (Greek, Estonian, Irish, Croatian, Lithuanian, Latvian, Maltese, Slovenian, Serbian) were missing the eleven Orchestration Canvas keys that Phase 3 introduced, so the compare-runs panel and the "no runs match these filters" empty state fell back to English in those languages (`deepMergeFallback` substitutes English for an absent key, so nothing rendered blank — it rendered untranslated). The coverage gate does not catch this: it enforces an 80% floor per locale, and eleven missing keys out of ~13,000 leaves coverage at 99.9%. Translated for real in each language, calibrated against the wording each file already uses for "run", "filter" and "skill". ([#13294](https://github.com/diegosouzapw/OmniRoute/pull/13294)) +- **fix(dashboard):** five follow-ups from the Phase 2 Orchestration Canvas review. The History drawer now shows the memory section for runs that already left the live TTL window: the persisted `memory_hits` event is parsed into `metadata.memoryHits` with the same defensive validation the drawer applies, and — because that event is observability rather than a state transition — it no longer leaks into the timeline, where it had been inheriting the task's state and rendering as a duplicate transition. The A2A memory recall runs against its own 1.5s deadline instead of inheriting the memory backend's 30s one; overshooting degrades exactly like any other recall failure (no hits, task proceeds), and the timer is cleared on both paths. Repeating a Conductor run carries its `requirements.cli`/`requirements.model` forward, so the new run is pinned to the same runner profile and model rather than drifting to whatever the fleet picks. A successful repeat from the Agents tab now focuses the run it created, instead of leaving the operator on the finished one. And the auth test for `POST /api/conductor/tasks` moved into the shared `ROUTES` array rather than restating the pattern. ([#12988](https://github.com/diegosouzapw/OmniRoute/pull/12988)) +- fix(plugins): stop leaking an exit listener per plugin hook timeout, which triggered MaxListenersExceededWarning on plugins that ignore SIGTERM ([#13092](https://github.com/diegosouzapw/OmniRoute/pull/13092)) — thanks @anhtahaylove +- **fix(validation):** Provider node edits no longer fail with a generic "Invalid request" when the optional daily-quota reset fields are left blank. The dashboard sends `dailyQuotaResetTimezone` and `dailyQuotaResetHour` as `null`, and only the hour accepted it. ([#13066](https://github.com/diegosouzapw/OmniRoute/issues/13066)) ([#13083](https://github.com/diegosouzapw/OmniRoute/pull/13083)) — thanks @ntdatt812 +- **fix(combo):** quota-aware expansion drops banned, inactive, missing, and wrong-provider connections before quota fetch or model dispatch; pins and allowlists stay selectors, not a bypass. Antigravity automatic exhaustion now requires a reported zero remaining, so a positive balance below 1% stays eligible. ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- Fix an intermittent failure in the quota-weighted routing test suite: scores are a function of `Date.now()`, so peers with identical quota scored microseconds apart never tied and swapped order. ([#12946](https://github.com/diegosouzapw/OmniRoute/pull/12946)) +- fix(volcengine): resolve quota window mapping gaps, safe multi-connection console binding, and response cookie sanitization ([#12950](https://github.com/diegosouzapw/OmniRoute/pull/12950)) — thanks @HouMinXi +- **fix(test):** resolve the WebDAV handler path with `fileURLToPath` so the suite's 37 WebDAV tests run on Windows instead of failing with a doubled `C:\C:\` drive prefix ([#13196](https://github.com/diegosouzapw/OmniRoute/pull/13196)) — thanks @anhtahaylove +- **fix(ui):** fall back from invalid AnySearch icon alias (direct commit `9587e07b69`) +- **fix(sse):** stop rebuilding a truncated summary from the collector's cap-dropped event array ([#12718](https://github.com/diegosouzapw/OmniRoute/pull/12718)) — thanks @hartmark +- **fix(conversations):** show a pending spinner for unresolved tool nodes instead of "(empty)" ([#12727](https://github.com/diegosouzapw/OmniRoute/pull/12727)) — thanks @hartmark +- **fix(streaming):** fail fast when an upstream stream produces only lifecycle/heartbeat events, never real content ([#12741](https://github.com/diegosouzapw/OmniRoute/pull/12741)) — thanks @hartmark +- **fix(responses-continuation):** bridge the write-in-flight window with an in-memory pending store ([#12854](https://github.com/diegosouzapw/OmniRoute/pull/12854)) — thanks @hartmark +- **fix(translator):** drop or map agent_message on Chat Completions fallback ([#12880](https://github.com/diegosouzapw/OmniRoute/pull/12880)) — thanks @RaviTharuma +- **fix(api):** page and stream-complete GET /v1/models for large catalogs ([#12882](https://github.com/diegosouzapw/OmniRoute/pull/12882)) — thanks @RaviTharuma +- **fix(quota):** align AUTH window labels with usage API and clarify 503 ([#12884](https://github.com/diegosouzapw/OmniRoute/pull/12884)) — thanks @RaviTharuma +- **fix(ci):** clear two base-reds on release/v3.8.51 — mutation-coverage gate + image-only-model guard ([#12945](https://github.com/diegosouzapw/OmniRoute/pull/12945)) +- **fix(api):** scope batch bulk-delete to the calling API key ([#13211](https://github.com/diegosouzapw/OmniRoute/pull/13211)) +- **fix(tests):** align model-sync log assertion to shipped path redaction (base-red) ([#13227](https://github.com/diegosouzapw/OmniRoute/pull/13227)) +- **fix(test):** make the ToS heading guard actually require the parentheses ([#13228](https://github.com/diegosouzapw/OmniRoute/pull/13228)) +- **fix(quality):** register 4 drifted covering tests in stryker tap.testFiles ([#13229](https://github.com/diegosouzapw/OmniRoute/pull/13229)) +- **fix(logging):** mask *-api-key headers in request-log pipeline (#13273) ([#13401](https://github.com/diegosouzapw/OmniRoute/pull/13401)) — thanks @KooshaPari +- **fix(tests):** correct stale Jina catalog prefix in custom-models test ([#13403](https://github.com/diegosouzapw/OmniRoute/pull/13403)) — thanks @KooshaPari +- **fix(backend):** prune old backups after health-check-repair snapshots (#13308) ([#13404](https://github.com/diegosouzapw/OmniRoute/pull/13404)) — thanks @KooshaPari +- **fix(backend):** guard memory_fts_au trigger with WHEN clause to stop FTS bloat (#13326) ([#13405](https://github.com/diegosouzapw/OmniRoute/pull/13405)) — thanks @KooshaPari +- **fix(free-tiers):** remove obsolete Together $25 signup credit (#12526) ([#13407](https://github.com/diegosouzapw/OmniRoute/pull/13407)) — thanks @KooshaPari +- **fix:** skip non-chat generation models in Test all models ([#13409](https://github.com/diegosouzapw/OmniRoute/pull/13409)) — thanks @KooshaPari +- **fix(providers):** de-duplicate agy and antigravity model catalogs ([#13410](https://github.com/diegosouzapw/OmniRoute/pull/13410)) — thanks @KooshaPari +- **fix(dashboard):** send provider in body for mini-playground web search ([#13411](https://github.com/diegosouzapw/OmniRoute/pull/13411)) — thanks @KooshaPari +- **fix(resilience):** add CJK quota-exhaustion patterns to 429 classifier ([#13412](https://github.com/diegosouzapw/OmniRoute/pull/13412)) — thanks @KooshaPari +- **fix(backend):** eval runner: failed calls no longer score as passed + dotAll regex ([#13413](https://github.com/diegosouzapw/OmniRoute/pull/13413)) — thanks @KooshaPari +- **fix(backend):** eval runner opts out of output-style and memory injection ([#13414](https://github.com/diegosouzapw/OmniRoute/pull/13414)) — thanks @KooshaPari +- **fix:** chain serial quarantine step in npm test script (fixes #13286) ([#13418](https://github.com/diegosouzapw/OmniRoute/pull/13418)) — thanks @KooshaPari +- **fix(antigravity):** derive stable session ID from credentials for prompt-cache reuse (#13305) ([#13424](https://github.com/diegosouzapw/OmniRoute/pull/13424)) — thanks @KooshaPari +- **fix(memory):** merge memory into top-level system field instead of unshifting at messages[0] (#13425) ([#13427](https://github.com/diegosouzapw/OmniRoute/pull/13427)) — thanks @KooshaPari +- **fix(compression):** preserve instruction blocks from lossy engines ([#13523](https://github.com/diegosouzapw/OmniRoute/pull/13523)) — thanks @KooshaPari +- **fix(compression):** preserve polarity words and newlines in ultra heuristic ([#13524](https://github.com/diegosouzapw/OmniRoute/pull/13524)) — thanks @KooshaPari +- **fix(compression):** run user preservePatterns before built-in patterns (#13457) ([#13525](https://github.com/diegosouzapw/OmniRoute/pull/13525)) — thanks @KooshaPari +- **fix(reasoning):** expand deepseek-v4 regex to match v4.1+ and add command-code provider (#13430) ([#13528](https://github.com/diegosouzapw/OmniRoute/pull/13528)) — thanks @KooshaPari +- **fix(providers):** add alwaysReasons metadata for reka-flash-3 ([#13531](https://github.com/diegosouzapw/OmniRoute/pull/13531)) — thanks @KooshaPari +- **fix(playground):** allow Compare columns to scroll independently ([#13532](https://github.com/diegosouzapw/OmniRoute/pull/13532)) — thanks @KooshaPari +- **fix(security):** empty whitelist allows through to prevent admin lockout ([#13534](https://github.com/diegosouzapw/OmniRoute/pull/13534)) — thanks @KooshaPari +- **fix(tests):** add DISABLE_IOREG_STRATEGY env var for macOS test compatibility (fixes #13467) ([#13539](https://github.com/diegosouzapw/OmniRoute/pull/13539)) — thanks @KooshaPari +- **fix(zed-hosted):** map developer role and Gemini enums for Zed proxy ([#13543](https://github.com/diegosouzapw/OmniRoute/pull/13543)) — thanks @KooshaPari +- **fix(analytics):** normalize provider aliases to canonical id in usage_history ([#13545](https://github.com/diegosouzapw/OmniRoute/pull/13545)) — thanks @KooshaPari +- **fix(combo):** use traceId for call log id to prevent UNIQUE constraint failure ([#13546](https://github.com/diegosouzapw/OmniRoute/pull/13546)) — thanks @KooshaPari +- **fix(auth):** check model lockout before returning synthetic noauth connection ([#13547](https://github.com/diegosouzapw/OmniRoute/pull/13547)) — thanks @KooshaPari +- **fix(guardrails):** skip credential redaction for base64 image data URLs ([#13550](https://github.com/diegosouzapw/OmniRoute/pull/13550)) — thanks @KooshaPari +- **fix(resilience):** increase requestQueue.maxWaitMs default from 15s to 30s ([#13553](https://github.com/diegosouzapw/OmniRoute/pull/13553)) — thanks @KooshaPari +- **fix(docs):** document OMNIROUTE_READY_TIMEOUT_MS and allowlist the test-only DISABLE_IOREG_STRATEGY ([#13692](https://github.com/diegosouzapw/OmniRoute/pull/13692)) + ### 📝 Maintenance ---- +- **docs(readme):** reconcile live v3.8.50 provider, free-tier, CLI, routing, test, community, sponsor, acknowledgment, and SVG metrics with their audited source denominators, including a deduplicated OmniRoute-in-Action snapshot and distinct contributor rankings for merged pull requests, GitHub-attributed commits, and Git history ([#11356](https://github.com/diegosouzapw/OmniRoute/pull/11356)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **docs:** sync the canonical provider count 354 → 356 across `README.md`, `AGENTS.md`, `llm.txt` (+ 42 i18n mirrors), the four README SVG diagrams, `docs/reference/PROVIDER_REFERENCE.md` (regenerated) and the `package.json` description after Opper (#11629) and 1min.ai (#11631) boarded the catalog — closes the `check:docs-counts-sync` strict drifts that kept `release/v3.8.51` red ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11673](https://github.com/diegosouzapw/OmniRoute/pull/11673)) — thanks @jonlwheat2-gif +- **test(providers):** update count-derived assertions after the v3.8.51 provider additions — `APIKEY_PROVIDERS` 233 → 235 (Opper #11629 + 1min.ai #11631), reserved-prefix REGISTRY walk 395 → 398, `WEB_FETCH_PROVIDERS` now includes `nimble-search` (#11620), and the provider translate-path golden snapshot regenerated ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11674](https://github.com/diegosouzapw/OmniRoute/pull/11674)) — thanks @jonlwheat2-gif +- **test(sse):** bump the hard-lease connection-query inventory for `src/lib/tokenHealthCheck.ts` to 2 — the verify-only web-cookie sweep added by #11495 split the single `getProviderConnections` call into oauth + cookie variants, which the frozen inventory had not tracked ([#11495](https://github.com/diegosouzapw/OmniRoute/pull/11495)). ([#11677](https://github.com/diegosouzapw/OmniRoute/pull/11677)) — thanks @jonlwheat2-gif / @oyi77 +- **chore(release):** clear the `release/v3.8.51` base-red gates — docs migration count (+38 i18n mirrors), stryker `tap.testFiles`, lockfile registry host, stale ESLint suppressions, the 7 lint regressions they were hiding, the stale `APIKEY_PROVIDERS` partition count (231 → 233), the `glm-5.3-max` tier missing from the GLM test table, a dead `modelType === "chat"` comparison in the models catalog, and the `resolveLiveWsUrl` / `sanitizeLiveWsPort` helpers from #11388 that a later merge dropped from `wsPath.ts` (dashboard live WebSocket URL resolution) ([#11502](https://github.com/diegosouzapw/OmniRoute/pull/11502)) — thanks @MumuTW +- **chore(free-models):** the free-tier regime table now declares which totals figure each regime feeds and whether it may take the no-auth shortcut, so the sets that used to repeat those answers by hand are derived from it — a new `freeType` no longer compiles until it has answered every question, instead of silently contributing to no total ([#11537](https://github.com/diegosouzapw/OmniRoute/pull/11537)) — thanks @maxmad64bis +- **docs(routing):** LKGP strategy copy now states provider-level stickiness and the explicit rules fallback instead of promising per-target pinning ([#11555](https://github.com/diegosouzapw/OmniRoute/pull/11555)) — thanks @maxmad64bis +- **test(chatcore):** pin the response tool-name alias resolution against the ordering hazard that broke every Gemini/Antigravity MCP tool call in v3.8.49. `extractRequestToolIdentityMap` deletes `translatedBody._toolNameMap`, so the later read is always undefined and the ledger survives only through the `requestToolIdentityMap` fallback — removing that fallback previously left the entire tool-name suite green. The resolution moves into `resolveResponseToolNameMap()` next to the map it depends on, with a regression guard that fails without the recovery. No behaviour change. ([#11575](https://github.com/diegosouzapw/OmniRoute/pull/11575)) — thanks @amartinawi +- **docs(env):** document `OMNIROUTE_CORPUS_CACHE_SIZE` in `.env.example` and `ENVIRONMENT.md`, so the env/docs contract check stops failing Docs Gates on every branch ([#11576](https://github.com/diegosouzapw/OmniRoute/pull/11576)) — thanks @ntdatt812 +- **chore(stryker):** register `route-guard-tunnel-processes-local-only` and `universal-quota-aware-routing` in `tap.testFiles`, so their mutant kills count for the three modules they cover ([#11577](https://github.com/diegosouzapw/OmniRoute/pull/11577)) — thanks @ntdatt812 +- **test(authz):** pin `LOCAL_ONLY_API_GET_EXEMPTIONS` by exact membership instead of by entry count, so the guard names the offending path and also catches a substitution ([#11580](https://github.com/diegosouzapw/OmniRoute/pull/11580)) — thanks @ntdatt812 +- **test(build):** pin `resolveNextBuildBundlerFlag` to the `OMNIROUTE_USE_TURBOPACK` contract it implements, across runtimes, instead of asserting an unimplemented Bun override ([#11583](https://github.com/diegosouzapw/OmniRoute/pull/11583)) — thanks @ntdatt812 +- **test(claude-web):** the Claude Web transport test's barrel anchors no longer pin the exact construction spelling. #11421 made the executor map lazy, so `/"claude-web": new ClaudeWebExecutor\(\)/` stopped matching and the test went red on `release/v3.8.51` itself — a base-red gate every open PR inherited. The anchors now match the alias key and the class name and tolerate either spelling in between, and were checked to still fail when an alias is genuinely repointed at another executor ([#11585](https://github.com/diegosouzapw/OmniRoute/pull/11585)) — thanks @ntdat812 +- **test(models):** pin `qwen3.8-max` resolution per provider — two of the four catalogs now serve the bare id and no longer carry `-preview` ([#11590](https://github.com/diegosouzapw/OmniRoute/pull/11590)) — thanks @ntdatt812 +- **test(executors):** drop the stale 128-tool truncation assertion that contradicted the #11444 pass-through contract ([#11591](https://github.com/diegosouzapw/OmniRoute/pull/11591)) — thanks @ntdatt812 +- **test(token-health):** pin the current terminal-skip boundary — expired with retry budget is probed, `account_deactivated` stays skipped ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)) — thanks @ntdatt812 +- **test(models):** follow #11503's retarget of the `gemini-3-pro-high` alias to the hyphenated catalog id ([#11593](https://github.com/diegosouzapw/OmniRoute/pull/11593)) — thanks @ntdatt812 +- **refactor(video bridge):** extract per-video acquisition, whole-result caching, description, and metrics/abort/cleanup out of `VideoBridgeGuardrail.preCall` into a `processVideoPart` seam in a new `videoBridgePipeline.ts`, behind explicit `VideoMediaBrokerPort`, `VideoAudioTranscriptionPort`, and `VideoDrilldownPort` boundaries; `preCall` now only handles request traversal, policy, and response aggregation. The Video tab's FFmpeg/ffprobe runtime status is now an explicit `unknown` / `restricted` / `unavailable` / `available` state instead of a nullable boolean pair, fixing a case where an in-flight or failed probe was mislabeled as "install FFmpeg" ([#11657](https://github.com/diegosouzapw/OmniRoute/issues/11657)). ([#12016](https://github.com/diegosouzapw/OmniRoute/pull/12016)) +- **test(opencode-plugin):** add unit test coverage for `formatFreeBudget()` naming helper ([#11660](https://github.com/diegosouzapw/OmniRoute/pull/11660)) — thanks @f9td56dbgh-hub +- **docs(video):** clarify that the Video Bridge transcript `source` field (`client`, `embedded`, `audio-bridge`) is presently caller-declared and not yet server-verified — OmniRoute enforces the enum shape but does not cryptographically confirm that an `embedded`/`audio-bridge` label came from a server-owned extraction ([#11661](https://github.com/diegosouzapw/OmniRoute/issues/11661)). ([#12001](https://github.com/diegosouzapw/OmniRoute/pull/12001)) +- **chore(providers):** retire the distributed Raycast Relay and Hailuo Web integrations after a provenance audit confirmed substantial derivation from GPL-3.0 sources; the official GitHub and MiniMax API providers remain available. Migration 163 disables legacy connections and invalidates their active leases while preserving connection IDs, audit/history rows, quota snapshots, usage records, and fail-closed API-key connection policies. ([#11691](https://github.com/diegosouzapw/OmniRoute/pull/11691)) +- **chore(providers):** retire the Felo Web provider while its GPL-derived provenance remains on licensing HOLD ([#11698](https://github.com/diegosouzapw/OmniRoute/pull/11698)) — stale runtime and database references now fail closed while existing connection, lease, and usage history remains preserved for auditability. +- **fix(sse):** retire the Gemini Web image-generation bridge while provenance and licensing remain on HOLD, preserving Gemini Web chat/auth/tools and legitimate Gemini/Nano Banana image providers ([#11708](https://github.com/diegosouzapw/OmniRoute/pull/11708)) +- **chore(audio):** retire the built-in EdgeTTS provider and its 11-voice catalog while provenance/licensing review remains on HOLD; the generic `/v1/audio/speech` endpoint and the other speech providers remain available ([#11711](https://github.com/diegosouzapw/OmniRoute/pull/11711)) +- **fix(providers):** retire the unofficial Qwen Web integration and its `qwen-web`/`qw` identities while third-party provenance remains on HOLD ([#11713](https://github.com/diegosouzapw/OmniRoute/pull/11713)) — direct requests now fail closed with a sanitized HTTP 410 and combos skip the retired target, while migration guards preserve connection IDs, allowlists, usage/audit history and invalidate active leases. Supported Qwen API, Qwen Code, cloud, token-plan, audio, local and model-name surfaces remain available; restoring Qwen Web requires a qualified provenance/license decision. +- **fix(providers):** retire `microsoft-designer-web` and its `msdesigner` alias from runtime and catalogs while preserving stored credentials, provider history and allowlists as inactive records — source dependency on gpt4free's [`MicrosoftDesigner.py`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/g4f/Provider/needs_auth/MicrosoftDesigner.py) is proven at snapshot `0943d16eef757a44deb60651c07bbe5fa958cafe`, whose [`LICENSE`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/LICENSE) is GPL-3.0; expressive adaptation is probable, which records provenance/licensing risk without asserting a legal conclusion ([#11720](https://github.com/diegosouzapw/OmniRoute/pull/11720)). +- **docs(legal):** centralize pinned MIT notices for the vendored GCF codec and local flag assets ([#11726](https://github.com/diegosouzapw/OmniRoute/pull/11726)) +- Split the 2,952-line `adobeFireflyClient.ts` into nine focused modules (catalog, credentials, payload, ARP session, upload, responses, IMS, account, poll) behind an unchanged barrel — no consumer edits and no public API change (#11728). — thanks @MumuTW +- **fix(dashboard):** replaced the Model Lockout card's unprovenanced notification MP3 with a short, locally synthesized Web Audio chime ([#11731](https://github.com/diegosouzapw/OmniRoute/pull/11731)) — both toggles retain optional audible feedback without shipping replacement media or third-party code, and unsupported, suspended, or rejected audio contexts fail open without blocking the setting change. +- **chore(deepseek-web):** remove the provenance-unresolved DeepSeek PoW WASM binary and its runtime loader/tracing while retaining the existing JavaScript solver (slower at high difficulty) ([#11732](https://github.com/diegosouzapw/OmniRoute/pull/11732)). +- **chore(assets):** remove four unused provider assets whose redistribution provenance could not be verified while retaining the active Freebuff SVG ([#11735](https://github.com/diegosouzapw/OmniRoute/pull/11735)) +- **docs(assets):** retired two raster images whose redistribution provenance could not be verified ([#11736](https://github.com/diegosouzapw/OmniRoute/pull/11736)) — PicoClaw documentation now reuses the existing SVG, while Zoo Code remains a text link without an image. +- **docs(legal):** add pinned notices for six LobeHub-derived provider SVGs and 65 byte-exact theSVG assets, retaining registry claims as unverified metadata with explicit trademark and HOLD boundaries ([#11737](https://github.com/diegosouzapw/OmniRoute/pull/11737)). +- **chore(quality):** added a versioned 225/225 provider-asset provenance manifest and blocking drift gate ([#11743](https://github.com/diegosouzapw/OmniRoute/pull/11743)) — the inventory pins magic MIME and SHA-256 for every physical file, records immutable source matches for 71 assets, keeps 69 probable and 85 unresolved entries explicit but non-blocking, and treats provenance as source evidence rather than copyright or trademark clearance. +- **refactor(mitm):** replace four previously derived Traffic Inspector utilities — conversation normalization, SSE merging, secret masking, and header sanitization — with independent clean-room implementations based on public provider schemas, WHATWG SSE, and RFC 6750, while keeping the two independently classified passthrough surfaces unchanged ([#11748](https://github.com/diegosouzapw/OmniRoute/pull/11748)). +- Added a unit test that fails seven days before any dated pack under `config/` (`validUntil` and sibling keys) lapses, naming the file and key. The Alibaba free-tier pack expired on 2026-08-27 and turned every PR red the next morning with no commit involved; renewal now happens on someone's terms, not the clock's. ([#11891](https://github.com/diegosouzapw/OmniRoute/pull/11891)) +- `check:workflows` now fails (under `--strict`/`--ratchet`) when any job routed to a self-hosted runner publishes with `--provenance` — npm rejects that with `422` at the registry, which in v3.8.50 only surfaced after the tag and Docker images were public. ([#11895](https://github.com/diegosouzapw/OmniRoute/pull/11895)) +- `scripts/ops/runner-janitor.sh` now proves a path is idle with one `lsof` snapshot and removes stale leftovers itself (tmpfs after 3 h — it is RAM — disk after 24 h), kills orphan `next-build` processes, prunes checkouts of stopped runners, and alerts on memory pressure; `--dry-run` shows exactly what it would do. `docs/ops/RUNNER_BOX.md` reconciled to the measured box (31 GB, 10 listeners). ([#11893](https://github.com/diegosouzapw/OmniRoute/pull/11893)) +- The `next-build` artefact (1.3 GB) is now written and read under `$RUNNER_TEMP` (per-runner, on disk) instead of `/tmp`, which on the self-hosted pool is a 12 GB tmpfs in RAM. Landing it there took 27–32 of the publish job's 76 minutes, and the fixed `/tmp/e2e-build.tar.gz` name let E2E jobs on different runners overwrite each other's download. ([#11896](https://github.com/diegosouzapw/OmniRoute/pull/11896)) +- The CI `build` job now runs in two concurrency lanes — `main` and pull requests — so a release build is never queued behind (or OOM-killed beside) PR builds on the self-hosted pool, which holds one `next-build` comfortably and two at the edge. ([#11901](https://github.com/diegosouzapw/OmniRoute/pull/11901)) +- Re-freeze the ESLint suppressions on `release/v3.8.51` from a clean-room measurement (2 stale file entries pruned, 55 pre-existing `no-explicit-any` in six new files frozen under #11924) and drop the dead `GPT_SIZE_MAP` constant orphaned by the Adobe Firefly client split, so `No new ESLint warnings` stops failing every PR with exit 2 (Refs #11924) ([#11955](https://github.com/diegosouzapw/OmniRoute/pull/11955)) +- Type the 55 `no-explicit-any` sites that had been frozen under #11924 — four redundant casts in `socksConnectorWithFamily.ts` (undici/socks types already accept them) and the mocks/fixtures of the socks-timeout and isFree suites — and drop their suppression entries; the ESLint ratchet shrinks from 5487 to 5432 (Closes #11924) ([#11975](https://github.com/diegosouzapw/OmniRoute/pull/11975)) +- Take the two hosted-runner builds off the PR rail: `Build App` (`build.yml`) is `workflow_dispatch`-only and `DAST smoke (PR)` runs only for PRs into `main` — the 7 GB hosted VM cannot build `release/v3.8.51` in any profile (19/30 red, VM shutdown ~8 min into `next build`) and both checks had turned into permanent noise on every release PR; the bundle stays validated by `ci.yml` on `main` and by `nightly-release-green` on `release/**` (Closes #11946) ([#11962](https://github.com/diegosouzapw/OmniRoute/pull/11962)) +- Move the four nightly jobs that build the backend (`nightly-schemathesis`, `nightly-llm-security` promptfoo + garak, `nightly-resilience` axe-a11y) off the hosted 7 GB runner — where they died on `release/v3.8.51` unseen — onto the box's new `omni-light` pool (two listeners), and document the reshaped fleet (4 active OmniRoute listeners: 2 `omni-build` + 2 `omni-light`, janitor ceiling 4) (Closes #11965) ([#11967](https://github.com/diegosouzapw/OmniRoute/pull/11967)) +- Stop painting every fork PR into `release/**` red: `quality.yml` `Build (advisory)` is skipped (GitHub still reports `continue-on-error` failures as check FAILURE). Hosted `ubuntu-latest` cannot finish `npm run build` on this tree — same class as #11962 taking `build.yml` off the PR rail. `docker-publish.yml` amd64 now runs on the `.113` `omni-build` pool (31 GB / 32 cores, two listeners) with Turbopack, shares the `heavy-build-main` lane with `ci.yml` `Build` so it queues instead of becoming a third heavy, and keeps per-ref concurrency (a merge storm was starting 8 concurrent OOM builds). arm64 stays on `ubuntu-24.04-arm` with webpack — there is no ARM box. Fallback when `USE_VPS_RUNNER` is off: hosted amd64 + webpack (#11976). ([#12021](https://github.com/diegosouzapw/OmniRoute/pull/12021)) +- `docker-publish.yml` builds with webpack (`OMNIROUTE_USE_TURBOPACK=0`) on **both** arches, including amd64 on the `.113` `omni-build` pool. Turbopack had the RAM (31 GB, `omniroute-113-6`) and still panicked (`TurbopackInternalError: there must be a path to a root`, run 33253576569, 26 min in). The same tree's arm64 webpack build on hosted `ubuntu-24.04-arm` succeeded (run 33264823398). Dockerfile already documents webpack as the Docker escape hatch (#11976). ([#12050](https://github.com/diegosouzapw/OmniRoute/pull/12050)) +- **docs(free-tier):** declare the counting vs deciding regimes for "is it free?" and guard the deciding path from DB-backed catalog resolution ([#12226](https://github.com/diegosouzapw/OmniRoute/pull/12226)) — thanks @maxmad64bis +- **docs(auto-combo):** The mode pack table in `docs/routing/AUTO-COMBO.md` now lists all six shipped packs with every weight each one sets, replacing a four-pack table whose numbers had also drifted from the source. It states plainly that no pack sets `quality`, so selecting any pack silences the observed-quality signal. Six more documents that quote the scoring factor count joined the `check:docs-counts` gate, which caught five stale claims — including one naming nine factors that do not exist — and two stale mode pack counts. The dashboard routing panel, which offered four of the six packs and labelled the default strategy "6-Factor Scoring", is now covered by a test; the two packs it was missing are `reliability-first` and `chaos-mode`, the latter labelled as the fault-injection profile it is rather than as one more routing preference ([#12316](https://github.com/diegosouzapw/OmniRoute/pull/12316)) — thanks @maxmad64bis +- **chore(quality):** rebaseline `src/lib/db/apiKeys.ts` for the ACL the key-creation path now preserves ([#12352](https://github.com/diegosouzapw/OmniRoute/pull/12352)) ([#12673](https://github.com/diegosouzapw/OmniRoute/pull/12673)) — thanks @KrzysiekSko +- **chore(providers):** bump the Claude Code wire identity and the Devin bridge image pin from `2.1.220` to `2.1.258` ([#12402](https://github.com/diegosouzapw/OmniRoute/pull/12402)) — thanks @ggiak ([#12604](https://github.com/diegosouzapw/OmniRoute/pull/12604)) +- **docs(env):** align `.env.example`, the README Bun section, and the troubleshooting guide with the code: `OMNIROUTE_USE_TURBOPACK` also governs `npm run build` (not dev-only), `bun run build` follows that flag instead of auto-selecting Webpack, `OMNIROUTE_CHAT_MAX_HEAVY_IN_FLIGHT` is unset by default (no request-count cap), and the structural `503 chat_admission_busy` message matches `chatAdmissionResponses.ts` (#12404 — thanks @pacocartones) +- **chore(electron):** upgrade the desktop app to Electron 44 (Chromium 152, Node 24.18.1) ([#12217](https://github.com/diegosouzapw/OmniRoute/pull/12217)). **Requires macOS 13 (Ventura) or later** — Chromium dropped macOS 12 (Monterey), so Monterey users must stay on an earlier OmniRoute desktop build. Windows and Linux are unaffected; the app already shipped only x64/arm64, so Electron 44 dropping 32-bit builds changes nothing. Removes the `openAsHidden`/`wasOpenedAsHidden` login-item fields deleted in Electron 44 — hidden autostart continues to work through the `--hidden` argument registered with the login item ([#12554](https://github.com/diegosouzapw/OmniRoute/pull/12554)) — thanks @app/dependabot +- **chore(quality):** rebaseline `src/sse/handlers/chat.ts` for the effective-input persistence the continuation fix needs ([#12641](https://github.com/diegosouzapw/OmniRoute/pull/12641)) ([#12680](https://github.com/diegosouzapw/OmniRoute/pull/12680)) — thanks @hartmark +- `Coverage` job on `ci.yml`: the informational Codecov upload gets its own 5-minute ceiling and `continue-on-error`, and the job budget grows from 20 to 30 minutes (the 8-shard c8 merge alone takes ~10) — a stalled upload no longer ends the job `cancelled` and drags a fully green `main` run's conclusion down with it ([#11972](https://github.com/diegosouzapw/OmniRoute/pull/11972)) +- CI hardening on the PR rail: the four `Unit Tests fast-path` shards get `timeout-minutes: 30` (a hung shard held a PR for 64 min instead of GitHub's 6 h default) and both ESLint file caches lose their `restore-keys` fallback, so a cache built under another suppressions file or lint config can no longer report stale verdicts (Refs #11600, #11924) ([#11963](https://github.com/diegosouzapw/OmniRoute/pull/11963)) +- Every CI job that runs a `next build` (`build`, the npm `publish`, both release-green validations) now targets the `omni-build` runner label, which only two of the eight self-hosted runners carry. The box holds one build comfortably and two at the edge; a third now queues on GitHub instead of being OOM-killed by the kernel. ([#11932](https://github.com/diegosouzapw/OmniRoute/pull/11932)) +- **refactor(combo):** move `handleRoundRobinCombo` (and `resolveTargetTokenLimit`) into `open-sse/services/combo/roundRobinCombo.ts`. `combo.ts` drops from 2164 to 1014 lines (`split("\n").length`); the leaf is 1199 (under the 1200 new-file cap). The round-robin call site uses a dynamic `import()` so `releaseStickyPinOnFailure` / `clearStaleLKGP` can stay exported from `combo.ts` without a static cycle. Skip / sticky / semaphore / safety-timer behavior is unchanged. ([#12811](https://github.com/diegosouzapw/OmniRoute/pull/12811)) — thanks @HouMinXi +- **chore(quality):** rebaseline the file-size caps the error-boundary campaign grew past (`open-sse/executors/codex.ts`, `open-sse/vendor/codex-chatgpt-web/bridge.ts`, both via [#12444](https://github.com/diegosouzapw/OmniRoute/pull/12444)) ([#12654](https://github.com/diegosouzapw/OmniRoute/pull/12654)) +- **chore(quality):** rebaseline the file-size caps the hartmark batch grew past (`combos/page.tsx` via [#12355](https://github.com/diegosouzapw/OmniRoute/pull/12355), `open-sse/services/combo.ts` via [#12338](https://github.com/diegosouzapw/OmniRoute/pull/12338)) ([#12623](https://github.com/diegosouzapw/OmniRoute/pull/12623)) — thanks @hartmark +- **chore(quality):** rebaseline the file-size caps the HouMinXi batch grew past when its PRs stacked (`providers/page.tsx`, `chatCore.ts`, `accountFallback.ts`) — each PR measured correctly in isolation, none saw the stacking ([#12619](https://github.com/diegosouzapw/OmniRoute/pull/12619)) +- **chore(quality):** rebaseline `open-sse/services/combo.ts` for the reset-aware scoring the HouMinXi batch stacked ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) ([#12678](https://github.com/diegosouzapw/OmniRoute/pull/12678)) — thanks @HouMinXi +- **docs:** sync provider catalog count (direct commit `4f11b2ae3d`) +- **docs(skills):** sync omni-inference search fallback (direct commit `b4e76a7ed9`) +- **chore(lint):** prune two stale base suppressions (direct commit `1325047d56`) +- **test(auto-combo):** tolerate full-suite rotation load (direct commit `ad3e293f9f`) +- **test:** drain inherited quality snapshot reds (direct commit `21ed68d8ac`) +- **chore(quality):** rebaseline measured CLI bundle growth (direct commit `d35b3f9779`) +- **test(ui):** reproduce invalid AnySearch icon alias (direct commit `12cc6ca834`) +- **test(translator):** fix the relative imports of the relocated deferred-finish test (direct commit `777d9d1629`) +- **docs(counts):** bring the documented provider and migration counts to the measured values (direct commit `e70bea30e9`) +- **test(security):** drop the guard-target set left unused by the sync (direct commit `a32ba0e362`) +- **chore(db):** renumber model_capabilities migration 163 → 169 (cross-branch collision with 163_radar_feed_cache_generated_at) (direct commit `70f5d4cbf9`) +- **docs(cli):** document CLI_PRIME_AGENT_BIN and correct the CLI Agents count (direct commit `9058e39b61`) +- **chore(deps):** refresh runtimes and adopt ESLint 10 ([#11259](https://github.com/diegosouzapw/OmniRoute/pull/11259)) — thanks @backryun +- **refactor(local-corpus):** implement dynamic root resolution and LRU cache ([#11491](https://github.com/diegosouzapw/OmniRoute/pull/11491)) — thanks @raheemuddin786 +- **chore(quality):** prune stale eslint-suppressions entries ([#11596](https://github.com/diegosouzapw/OmniRoute/pull/11596)) +- **chore(ci):** unblock quality gates - env-doc sync + stryker tap.testFiles registration (#9282) ([#11609](https://github.com/diegosouzapw/OmniRoute/pull/11609)) — thanks @Meet6338-X +- **chore(ci):** point the circular-deps gate at dpdm's real JS entrypoint ([#11615](https://github.com/diegosouzapw/OmniRoute/pull/11615)) — thanks @benzntech +- **chore(quality):** rebaseline file-size for #11448/#11495/#11561 growth ([#11630](https://github.com/diegosouzapw/OmniRoute/pull/11630)) +- **deps:** realign onnxruntime-node pin to @huggingface/transformers' 1.24.3 lockstep ([#11633](https://github.com/diegosouzapw/OmniRoute/pull/11633)) — thanks @jonlwheat2-gif +- **test(autoCombo):** port models_dev_tier #11508 guard from node:test to vitest ([#11635](https://github.com/diegosouzapw/OmniRoute/pull/11635)) — thanks @jonlwheat2-gif +- **chore(quality):** rebaseline file-size for #11083/#11631/#11628 growth ([#11636](https://github.com/diegosouzapw/OmniRoute/pull/11636)) +- **chore(lint):** prune stale ESLint suppressions (base-red #11449) ([#11644](https://github.com/diegosouzapw/OmniRoute/pull/11644)) — thanks @jonlwheat2-gif +- **docs(changelog):** aggregate the ten v3.8.50 fragments into CHANGELOG.md ([#11683](https://github.com/diegosouzapw/OmniRoute/pull/11683)) +- **chore(search):** drop unreachable credential sweep after #11642 ([#11687](https://github.com/diegosouzapw/OmniRoute/pull/11687)) — thanks @HouMinXi +- **ci:** add API route TypeScript regression gate ([#11705](https://github.com/diegosouzapw/OmniRoute/pull/11705)) — thanks @Sabeekhann +- **docs(dedup):** correct the CodeQL claim on the tenant-namespace comment ([#11712](https://github.com/diegosouzapw/OmniRoute/pull/11712)) +- **docs(changelog):** add consolidated v3.8.50 stats, top-25 ranking and a .mailmap ([#11715](https://github.com/diegosouzapw/OmniRoute/pull/11715)) +- **docs:** recommend gstack for AI-assisted workflows ([#11770](https://github.com/diegosouzapw/OmniRoute/pull/11770)) — thanks @santosraju99-hub +- **chore(quality):** tighten complexity/cognitive-complexity ratchets to the current tip; land the missed gateways.ts rebaseline ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **docs(i18n):** finish Freepik → Magnific rebrand in locale strings and README ([#11772](https://github.com/diegosouzapw/OmniRoute/pull/11772)) — thanks @turbolego +- **refactor(auto-combo):** fix divergent scoring in combo health reporting ([#11854](https://github.com/diegosouzapw/OmniRoute/pull/11854)) — thanks @jacobsparts +- **chore(changelog):** add missing fragments for #11919/#11918/#11916 ([#11938](https://github.com/diegosouzapw/OmniRoute/pull/11938)) +- **test(check):** escape the runs-on fixture with JSON.stringify, not a quote-only replace ([#11942](https://github.com/diegosouzapw/OmniRoute/pull/11942)) (direct commit `fb7445eaa3`) +- **test(release):** align five suites with the contracts #11933, #11919 and #11876 shipped on release/v3.8.51 ([#11944](https://github.com/diegosouzapw/OmniRoute/pull/11944)) +- **chore(quality):** remove unreachable code and restore test discovery ([#11950](https://github.com/diegosouzapw/OmniRoute/pull/11950)) — thanks @backryun +- **docs(agents):** sync-back landings are fast-forward, never squash ([#11964](https://github.com/diegosouzapw/OmniRoute/pull/11964)) +- **test(infra):** retry recursive temp-dir removal instead of failing a shard on ENOTEMPTY (#11966) ([#11968](https://github.com/diegosouzapw/OmniRoute/pull/11968)) +- **docs(ops):** the .113 heavy-build ceiling is one runner, not two ([#12048](https://github.com/diegosouzapw/OmniRoute/pull/12048)) +- **test(ui):** correct inactive auto-fetch expectation ([#12098](https://github.com/diegosouzapw/OmniRoute/pull/12098)) — thanks @RaviTharuma +- **docs:** document native dependency check escape hatch ([#12101](https://github.com/diegosouzapw/OmniRoute/pull/12101)) — thanks @RaviTharuma +- **test(providers):** regenerate the translate-path golden for OrcaRouter (#11923) ([#12118](https://github.com/diegosouzapw/OmniRoute/pull/12118)) +- **chore(quality):** velocity phase — loosen every numeric baseline by 20% until v4.0, monitor headroom nightly ([#12125](https://github.com/diegosouzapw/OmniRoute/pull/12125)) +- **chore(lint):** batch 0 of #12146 — type the call-log-cap sqlite rows instead of 45 as-any casts ([#12157](https://github.com/diegosouzapw/OmniRoute/pull/12157)) +- **chore(lint):** batch 4 of #12146 — shared/components react-hooks violations resolved ([#12159](https://github.com/diegosouzapw/OmniRoute/pull/12159)) +- **chore(lint):** batch 1 of #12146 — dashboard/cli-code react-hooks violations resolved ([#12160](https://github.com/diegosouzapw/OmniRoute/pull/12160)) +- **chore(lint):** batch 3 of #12146 — dashboard/settings react-hooks violations resolved ([#12162](https://github.com/diegosouzapw/OmniRoute/pull/12162)) +- **chore(lint):** batch 2 of #12146 — resolve the react-hooks compiler violations in dashboard/providers ([#12163](https://github.com/diegosouzapw/OmniRoute/pull/12163)) +- **chore(quality):** register search-432 cooldown test in stryker tap.testFiles ([#12170](https://github.com/diegosouzapw/OmniRoute/pull/12170)) +- **chore(lint):** batch 5 of #12146 — combos, endpoint, provider-stats, api-manager and costs react-hooks violations resolved ([#12174](https://github.com/diegosouzapw/OmniRoute/pull/12174)) +- **build:** add contributor fast profile ([#12192](https://github.com/diegosouzapw/OmniRoute/pull/12192)) — thanks @rafacpti23 +- **docs(readme):** README touch-ups ([#12193](https://github.com/diegosouzapw/OmniRoute/pull/12193)) ([#12194](https://github.com/diegosouzapw/OmniRoute/pull/12194)) ([#12202](https://github.com/diegosouzapw/OmniRoute/pull/12202)) — thanks @rafacpti23 +- **build:** force the webpack bundler in the contributor build profile ([#12197](https://github.com/diegosouzapw/OmniRoute/pull/12197)) — thanks @rafacpti23 +- **build:** skip standalone packaging in the contributor profile ([#12198](https://github.com/diegosouzapw/OmniRoute/pull/12198)) — thanks @rafacpti23 +- **docs(audit):** align every published number with the code and harden check:docs-counts ([#12200](https://github.com/diegosouzapw/OmniRoute/pull/12200)) +- **build:** stub the instrumentation entrypoints in the contributor profile ([#12203](https://github.com/diegosouzapw/OmniRoute/pull/12203)) — thanks @rafacpti23 +- **build:** omit the standalone output target for contributor builds ([#12204](https://github.com/diegosouzapw/OmniRoute/pull/12204)) — thanks @rafacpti23 +- **chore(lint):** batch 6 of #12146 — memory, radar, audit, analytics, cache, usage, activity, home and RequestLoggerV2 react-hooks violations resolved ([#12208](https://github.com/diegosouzapw/OmniRoute/pull/12208)) +- **docs(diagrams):** rename number-carrying diagram files to stable names ([#12210](https://github.com/diegosouzapw/OmniRoute/pull/12210)) +- **docs(dashboard):** redraw onboarding tier-flow SVGs for the real 4-tier model ([#12211](https://github.com/diegosouzapw/OmniRoute/pull/12211)) +- **docs(api):** document every implemented route in openapi.yaml (276 → 692 paths) ([#12212](https://github.com/diegosouzapw/OmniRoute/pull/12212)) +- **chore(lint):** batch 7 of #12146 — final src tail: 37 react-hooks violations across 33 files resolved ([#12244](https://github.com/diegosouzapw/OmniRoute/pull/12244)) +- Revert "docs: recommend gstack for AI-assisted workflows (#11770)" ([#12248](https://github.com/diegosouzapw/OmniRoute/pull/12248)) ([#12249](https://github.com/diegosouzapw/OmniRoute/pull/12249)) +- **docs:** Chaos Mode setup guide + weighted strategy semantics ([#12250](https://github.com/diegosouzapw/OmniRoute/pull/12250)) +- **docs(agents):** protected-surface merge rule — operator approval for agent-instruction files ([#12253](https://github.com/diegosouzapw/OmniRoute/pull/12253)) +- **chore(quality):** register native-codex turn-pin tests in stryker tap.testFiles ([#12263](https://github.com/diegosouzapw/OmniRoute/pull/12263)) +- **chore(quality):** dedupe tap.testFiles entries added by racing base-red fixes ([#12265](https://github.com/diegosouzapw/OmniRoute/pull/12265)) +- **docs(readme):** introduce OmniRouteTray — the macOS menu-bar companion ([#12276](https://github.com/diegosouzapw/OmniRoute/pull/12276)) — thanks @ggiak +- **chore(proxy):** purge the legacy 1proxy residue — sync/rotator modules, dead DB exports, dead settings tab and flag, docs (#12091) ([#12290](https://github.com/diegosouzapw/OmniRoute/pull/12290)) +- **chore(quality):** baseline-headroom skips generated and vendored files in the fileSize worst-file signal ([#12291](https://github.com/diegosouzapw/OmniRoute/pull/12291)) +- **refactor(video):** unify the JPEG frame data-URI contract ([#12322](https://github.com/diegosouzapw/OmniRoute/pull/12322)) +- **chore(deps):** freeze onnxruntime-node and eslint-plugin-react-hooks out of dependabot groups ([#12329](https://github.com/diegosouzapw/OmniRoute/pull/12329)) +- **test(executors):** restore the #10986 reasoning-only fallback guards ([#12364](https://github.com/diegosouzapw/OmniRoute/pull/12364)) — thanks @pacocartones +- **chore(quality):** re-tighten the file-size ratchet to the real LOC (plan 3.8.52 task 0) ([#12411](https://github.com/diegosouzapw/OmniRoute/pull/12411)) +- **test(grok):** format web executor suite ([#12412](https://github.com/diegosouzapw/OmniRoute/pull/12412)) +- **chore(quality):** ratchet open-sse typecheck baseline to zero ([#12418](https://github.com/diegosouzapw/OmniRoute/pull/12418)) +- **test(build):** guard the artifact path policy arrays against duplicates ([#12422](https://github.com/diegosouzapw/OmniRoute/pull/12422)) +- **chore(lint):** adopt eslint-plugin-react-hooks 7.1.1 ([#12428](https://github.com/diegosouzapw/OmniRoute/pull/12428)) +- **chore(providers):** remove a keyless provider integration at its operator's request ([#12440](https://github.com/diegosouzapw/OmniRoute/pull/12440)) +- **docs(providers):** register providers removed at their operator's request and guard against reintroduction ([#12478](https://github.com/diegosouzapw/OmniRoute/pull/12478)) +- **chore(quality):** base fixes — stryker tap.testFiles + node_modules cache key ([#12482](https://github.com/diegosouzapw/OmniRoute/pull/12482)) +- **docs(arch):** one-process recipe for tens of long /v1/responses ([#12493](https://github.com/diegosouzapw/OmniRoute/pull/12493)) — thanks @RaviTharuma +- **chore(quality):** tighten the CodeQL ratchet baseline from 11 to 6 ([#12530](https://github.com/diegosouzapw/OmniRoute/pull/12530)) +- **chore(deps):** pin browserslist override to ^4.28.8 ([#12592](https://github.com/diegosouzapw/OmniRoute/pull/12592)) — thanks @KooshaPari +- **chore(deps):** bump fast-uri to 3.1.7 in the electron lockfile ([#12601](https://github.com/diegosouzapw/OmniRoute/pull/12601)) +- **docs(quality):** document how the CodeQL ratchet refreshes and how to tighten it ([#12611](https://github.com/diegosouzapw/OmniRoute/pull/12611)) +- **test(ci):** pin the openapi-security-tiers two-arm contract with an executing gate test (#12581) ([#12652](https://github.com/diegosouzapw/OmniRoute/pull/12652)) +- **docs(gamification):** align XP Rewards table with code (#12501) ([#12667](https://github.com/diegosouzapw/OmniRoute/pull/12667)) — thanks @KooshaPari +- **refactor(combo):** split executeTarget into gates, attempt, and loop ([#12746](https://github.com/diegosouzapw/OmniRoute/pull/12746)) — thanks @HouMinXi +- **docs(security):** document socket.yml scanner config + CI workflow link (#12575) ([#12764](https://github.com/diegosouzapw/OmniRoute/pull/12764)) — thanks @KooshaPari +- **chore(ci):** guard commit identity in pre-commit to stop author misattribution ([#12772](https://github.com/diegosouzapw/OmniRoute/pull/12772)) +- **deps:** 17 Dependabot bumps — bump electron from 43.4.0 to 43.4.1 in /electron ([#11426](https://github.com/diegosouzapw/OmniRoute/pull/11426)); bump the development group across 1 directory with 8 updates ([#11428](https://github.com/diegosouzapw/OmniRoute/pull/11428)); bump the production group across 1 directory with 11 updates ([#11440](https://github.com/diegosouzapw/OmniRoute/pull/11440)); chore(deps): bump github/codeql-action/init from 4.37.7 to 4.37.8 ([#11925](https://github.com/diegosouzapw/OmniRoute/pull/11925)); chore(deps): bump github/codeql-action from 4.37.7 to 4.37.8 ([#11926](https://github.com/diegosouzapw/OmniRoute/pull/11926)); chore(deps): bump github/codeql-action/analyze from 4.37.7 to 4.37.8 ([#11927](https://github.com/diegosouzapw/OmniRoute/pull/11927)); build(deps): bump github/codeql-action/init from 4.37.8 to 4.37.9 ([#12345](https://github.com/diegosouzapw/OmniRoute/pull/12345)); build(deps): bump github/codeql-action/analyze from 4.37.8 to 4.37.9 ([#12346](https://github.com/diegosouzapw/OmniRoute/pull/12346)); bump the development group across 1 directory with 2 updates ([#12347](https://github.com/diegosouzapw/OmniRoute/pull/12347)); build(deps): bump github/codeql-action from 4.37.8 to 4.37.9 ([#12349](https://github.com/diegosouzapw/OmniRoute/pull/12349)); bump browserslist from 4.28.2 to 4.28.8 ([#12396](https://github.com/diegosouzapw/OmniRoute/pull/12396)); bump the production group across 1 directory with 4 updates ([#12399](https://github.com/diegosouzapw/OmniRoute/pull/12399)); bump @xmldom/xmldom ([#12500](https://github.com/diegosouzapw/OmniRoute/pull/12500)); bump qs from 6.15.2 to 6.16.0 ([#12512](https://github.com/diegosouzapw/OmniRoute/pull/12512)); bump @xmldom/xmldom from 0.9.10 to 0.9.12 ([#12513](https://github.com/diegosouzapw/OmniRoute/pull/12513)); bump fast-uri from 3.1.5 to 3.1.7 ([#12514](https://github.com/diegosouzapw/OmniRoute/pull/12514)); bump @humanfs/node from 0.16.7 to 0.16.8 ([#12515](https://github.com/diegosouzapw/OmniRoute/pull/12515)) +- Clear the `release/v3.8.51` `check:agent-skills-sync` base-red: regenerate `skills/cli-tunnel/SKILL.md` so the `tunnel create [type]` positional that #13009 taught the generator to read is reflected in the committed skill. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` typecheck base-red from #12731: the new `Mode pack ranking gates` candidates in `open-sse/services/autoCombo/__tests__/autoCombo.test.ts` omitted the required `provider`, `model` and `errorRate` fields of `ProviderCandidate`, failing `check:open-sse-typecheck` (and with it `Fast Quality Gates`) on every open PR. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` docs-sync base-red: EURouter (#13025) and GreenPT (#13024) took the live provider count to 358, leaving 7 STRICT drifts (`PROVIDER_REFERENCE.md`, 4 diagrams, 2 tier-flow images) plus the `AGENTS.md` / `llm.txt` / `package.json` count claims stale. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- **test(models):** Pin `CATALOG_BUILD_TIMEOUT_MS` in the #12058 canonical-catalog regression so a cold catalog build racing the 8s default bound no longer flakes the suite with a 500 ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Allow `@omniroute/opencode-plugin-v2/` in the npm pack policy — [#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870) shipped the v2 plugin beside its v1 sibling without widening the allowlist, so every packed file under it read as an unexpected artifact and failed `check:pack-policy` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Point the release-green validator's pack gate at the tree it is validating — the build-provenance guard defaulted to `origin/main`, which a release branch tip can never be an ancestor of mid-cycle, so `Package artifact` reported a false red on every run and the tarball boot-smoke cascaded off it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **docs(checks):** keep doc counts honest — headings, rankings, catalog, weights, quality gate and scoring diagram now covered ([#12507](https://github.com/diegosouzapw/OmniRoute/pull/12507)) — thanks @maxmad64bis +- **chore(lifecycle):** `check:model-lifecycle` now also diffs `DEFAULT_DEGRADATION_MAP` (the background-task redirect table) against the vendor lifecycle snapshot, refusing a retired id as source or target, with a table-driven unit test beside it. Three rows whose source the vendor had retired — `claude-sonnet-4-20250514`, `gemini-3-pro-preview` and `gpt-5.1-codex` (whose target `gpt-5.1-codex-mini` is retired too) — were dead code, since `checkLifecycle` answers 410 before the redirect runs; they are dropped (#12535 — thanks @pacocartones) +- **test(compression):** cover idle worker eviction at the resource level — the pool must call `terminate()` and must not retain the worker's `MessagePort`, complementing the `exit`-event assertion added with the fix ([#13371](https://github.com/diegosouzapw/OmniRoute/pull/13371)) +- **chore(quality):** raise the `imageGeneration.ts` file-size ceiling for the image-only-model guard that clears the #12945 base-red ([#13392](https://github.com/diegosouzapw/OmniRoute/pull/13392)) +- **chore(quality):** type the combo-test route's JSON response bodies instead of casting them to `any`, drop the now-empty suppression entry, and remove the `hasPerModelQuota` import `chatCore.ts` stopped using when the failure-classification helper was extracted ([#13379](https://github.com/diegosouzapw/OmniRoute/pull/13379)) +- **chore(electron):** the desktop bundle no longer ships the root-level files of every translated docs mirror (`README.md`, `llm.txt`, `CLAUDE.md`, `GEMINI.md`, `CONTRIBUTING.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`) — the packaged app only reads `docs/i18n//docs/**`, which stays. Saves ~11 MB on top of the translated CHANGELOGs already pruned. (#0000) ([#13235](https://github.com/diegosouzapw/OmniRoute/pull/13235)) +- **chore(quality):** retighten the `src/sse/handlers/chat.ts` unused-vars suppression count to the 8 violations that actually remain ([#13385](https://github.com/diegosouzapw/OmniRoute/pull/13385)) +- Bring the documented counts back in line with the code: 171 migrations (was 169) and 20 routing strategies (was 19), plus the regenerated `cli-tunnel` skill reference. ([#12970](https://github.com/diegosouzapw/OmniRoute/pull/12970)) +- Update the documented migration count to 172 after the call-logs provider-stats indexes landed. ([#13209](https://github.com/diegosouzapw/OmniRoute/pull/13209)) +- **chore(ci):** two gates that close the blind spots behind the exclusions above. `check:vitest-exclusions` requires every Vitest exclusion to name a tracking issue and to appear in `config/quality/vitest-exclusions.json` — the previous list grew to 62 files behind a comment pointing at an issue that had been closed for a month. `check-new-key-coverage` requires a key newly added to `en.json` to reach every locale; the existing coverage gate is a percentage floor per locale, so eleven absent keys out of ~13,000 left it at 99.9% while a whole feature shipped untranslated in nine languages. Both are diff-aware, so pre-existing debt stays frozen and neither needed a migration to turn on. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **build:** root layout no longer loads Inter from `next/font/google`, so a production image build does not need fonts.googleapis.com ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **docs(i18n):** bring the eight new `llm.txt` mirrors (km, kn, ml, my, ne, or, pa, si) up to the current provider and migration counts, so `check-docs-sync` stops rejecting every commit ([#13674](https://github.com/diegosouzapw/OmniRoute/pull/13674)) +- **refactor(ui):** flow surfaces (home topology, combo live studio, compression cockpit/waterfall/nodes, token-health badge) express state through the theme-aware `--orch-status-*` tokens instead of fixed dark-mode hex, so green/red/amber/grey stay legible in the light theme; dark mode is byte-identical. Categorical palettes (routing-strategy hues, compression-layer pills, provider brand colors) deliberately stay hex ([#12378](https://github.com/diegosouzapw/OmniRoute/issues/12378)) ([#13203](https://github.com/diegosouzapw/OmniRoute/pull/13203)) +- **chore(tests):** 51 test files that had been excluded from Vitest are running again, restoring roughly 350 assertions to the blocking suite. Each was measured individually first: of the 62 files parked behind the `// #8618 — pre-existing failure` comment, 51 pass against the current tree with no source change, so the exclusions had outlived the failures they were added for. The 11 that genuinely still fail stay excluded, but now point at a live tracker (#13204) rather than at #8618, which was closed in August while the list it tracked kept growing. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **chore(quality):** drop two ESLint suppression entries whose violations no longer exist, so `eslint --suppressions-location` stops rejecting every commit that touches the surrounding files ([#13384](https://github.com/diegosouzapw/OmniRoute/pull/13384)) +- **chore(release):** bring main's post-tag fixes into release/v3.8.51 — sync-back step 2/2 (direct commit `21c488f210`) +- **chore(deps):** patch toml and esbuild in the opencode plugin lockfiles ([#12965](https://github.com/diegosouzapw/OmniRoute/pull/12965)) +- **docs(changelog):** reconcile the v3.8.51 living section — fold 366 fragments, cover every cycle commit, credit every contributor ([#12971](https://github.com/diegosouzapw/OmniRoute/pull/12971)) +- **test(plugin):** wait for the v2 lazy-refresh tier instead of sleeping past it ([#12984](https://github.com/diegosouzapw/OmniRoute/pull/12984)) +- **chore(quality):** rebaseline chatCore.ts after the non-streaming regression fixes ([#13045](https://github.com/diegosouzapw/OmniRoute/pull/13045)) +- **docs(changelog):** add the missing bullet marker to the DeepSeek PoW fragment ([#13200](https://github.com/diegosouzapw/OmniRoute/pull/13200)) +- **chore(deps):** drain the Dependabot queue — 10 of 13 alerts ([#13213](https://github.com/diegosouzapw/OmniRoute/pull/13213)) +- **chore(ci):** make worktrees carry the hooks that enforce the identity gate ([#13236](https://github.com/diegosouzapw/OmniRoute/pull/13236)) +- **chore(deps):** pin csv-parse to ^7.0.2 via overrides (#13117) ([#13300](https://github.com/diegosouzapw/OmniRoute/pull/13300)) — thanks @KooshaPari +- **chore(deps):** bump hono from ^4.12.34 to ^4.13.7 (#13148) ([#13301](https://github.com/diegosouzapw/OmniRoute/pull/13301)) — thanks @KooshaPari +- **chore(deps):** pin joi to ^18.2.8 via overrides (#13085) ([#13302](https://github.com/diegosouzapw/OmniRoute/pull/13302)) — thanks @KooshaPari +- **docs(providers):** add Microsoft 365 Copilot (BizChat) provider guide (#12779) ([#13340](https://github.com/diegosouzapw/OmniRoute/pull/13340)) — thanks @KooshaPari +### 🙌 Contributors + +Thanks to everyone whose work landed in v3.8.51: + +| Contributor | PRs / Issues | +| --- | --- | +| [@5dive-bot](https://github.com/5dive-bot) | #11852 | +| [@abhisheksharma2411](https://github.com/abhisheksharma2411) | #11547, #11684, #11959, #12209 | +| [@adevwithpurpose](https://github.com/adevwithpurpose) | #11464 | +| [@adivekar-utexas](https://github.com/adivekar-utexas) | #12015, #12043, #12090, #12138 | +| [@AIB1TAL0S](https://github.com/AIB1TAL0S) | #12103 | +| [@alltomatos](https://github.com/alltomatos) | #11448 | +| [@alvinveroy](https://github.com/alvinveroy) | #11857, #12027, #12165 | +| [@amaleta](https://github.com/amaleta) | #11906, #11934 | +| [@amartinawi](https://github.com/amartinawi) | #11575 | +| [@AndrianBalanescu](https://github.com/AndrianBalanescu) | #11083, #11830, #11834, #11844 | +| [@anhtahaylove](https://github.com/anhtahaylove) | #13091, #13092, #13093, #13096, #13097, #13100, #13106, #13109, #13114, #13155, #13166, #13171, #13175, #13187, #13196 | +| [@aniruddhaadak80](https://github.com/aniruddhaadak80) | #11760 | +| [@app](https://github.com/app) | #12554 | +| [@arminanton](https://github.com/arminanton) | #11461, #11513 | +| [@AStupidBear](https://github.com/AStupidBear) | #11584 | +| [@atescivitci-cmd](https://github.com/atescivitci-cmd) | #12105 | +| [@b3nw](https://github.com/b3nw) | #11970, #11971, #12147 | +| [@backryun](https://github.com/backryun) | #11259, #11950, #12075, #12076, #12078, #12079, #12081, #12082, #12181, #12228, #12239, #12255, #12258, #12277, #12367, #12423, #12524, #12538 | +| [@benjaminkitt](https://github.com/benjaminkitt) | #11747 | +| [@benzntech](https://github.com/benzntech) | #11614, #11615 | +| [@Bl0ck154](https://github.com/Bl0ck154) | #11948, #11951, #11952, #11953, #11954 | +| [@brick30llc-ctrl](https://github.com/brick30llc-ctrl) | #11752, #12242, #12262 | +| [@Chewji9875](https://github.com/Chewji9875) | #11455, #11875, #12028 | +| [@cheynetom](https://github.com/cheynetom) | #12058 | +| [@davidebaraldo](https://github.com/davidebaraldo) | #12222, #12304 | +| [@davidlinfr](https://github.com/davidlinfr) | #6390 | +| [@ddarkr](https://github.com/ddarkr) | #12124 | +| [@Deftera186](https://github.com/Deftera186) | #11809 | +| [@dpozimski](https://github.com/dpozimski) | #11945 | +| [@DW-MediaLab](https://github.com/DW-MediaLab) | #12229 | +| [@echel0nn](https://github.com/echel0nn) | #11923 | +| [@f9td56dbgh-hub](https://github.com/f9td56dbgh-hub) | #11660 | +| [@fabioluissilva](https://github.com/fabioluissilva) | #11991 | +| [@foreveryh](https://github.com/foreveryh) | #12177 | +| [@geek007git](https://github.com/geek007git) | #12115, #12116, #12117, #12120, #12122 | +| [@geekyNads](https://github.com/geekyNads) | #11784, #12266 | +| [@ggiak](https://github.com/ggiak) | #11561, #12276, #12350, #12402 | +| [@gonisulaimann](https://github.com/gonisulaimann) | #12368, #12369, #12371 | +| [@Gorillaz322](https://github.com/Gorillaz322) | #12207 | +| [@groovecityJO](https://github.com/groovecityJO) | #12682 | +| [@hartmark](https://github.com/hartmark) | #11434, #11452, #11473, #11499, #11703, #11983, #11984, #11985, #11986, #11988, #11989, #11990, #11994, #12221, #12293, #12445, #12446, #12447, #12448, #12460, #12461, #12623, #12646, #12650, #12680, #12717, #12718, #12727, #12741, #12854 | +| [@hizzt](https://github.com/hizzt) | #11894 | +| [@hongnoul](https://github.com/hongnoul) | #11484 | +| [@HouMinXi](https://github.com/HouMinXi) | #11411, #11414, #11512, #11518, #11520, #11641, #11642, #11643, #11687, #11779, #11849, #11850, #11851, #11915, #11916, #11918, #11919, #11920, #12013, #12017, #12033, #12042, #12106, #12139, #12166, #12169, #12171, #12205, #12213, #12312, #12325, #12487, #12488, #12495, #12504, #12557, #12566, #12590, #12591, #12624, #12626, #12632, #12637, #12678, #12696, #12697, #12711, #12733, #12746, #12767, #12770, #12789, #12803, #12805, #12811, #12866, #12868, #12899, #12926, #12934, #12950, #12951, #12974, #13001, #13006, #13011, #13017, #13026, #13027, #13034, #13035, #13038, #13042, #13050, #13060, #13061, #13069, #13107, #13120, #13136, #13195, #13197 | +| [@Hsia97](https://github.com/Hsia97) | #11624 | +| [@jacobsparts](https://github.com/jacobsparts) | #11854, #12155, #12167 | +| [@joglomedia](https://github.com/joglomedia) | #11980 | +| [@jonlwheat2-gif](https://github.com/jonlwheat2-gif) | #11443, #11450, #11549, #11567, #11608, #11633, #11635, #11640, #11644, #11671, #11672, #11673, #11674, #11675, #11676, #11677, #11706, #11783, #12051, #12052, #12053, #12055 | +| [@kanade-hoshino](https://github.com/kanade-hoshino) | #12180 | +| [@Karan825](https://github.com/Karan825) | #11861 | +| [@KaspaPulse](https://github.com/KaspaPulse) | #11389, #11469, #11628, #11666, #11910 | +| [@keeltrace](https://github.com/keeltrace) | #12080, #12223 | +| [@killer30001000](https://github.com/killer30001000) | #12178 | +| [@KooshaPari](https://github.com/KooshaPari) | #12470, #12592, #12667, #12699, #12703, #12706, #12764, #12769, #12771, #13300, #13301, #13302, #13340, #13401, #13403, #13404, #13405, #13406, #13407, #13409, #13410, #13411, #13412, #13413, #13414, #13418, #13424, #13427, #13433, #13523, #13524, #13525, #13528, #13531, #13532, #13534, #13539, #13543, #13545, #13546, #13547, #13550, #13553 | +| [@kriptoburak](https://github.com/kriptoburak) | #11370 | +| [@KrzysiekSko](https://github.com/KrzysiekSko) | #12425, #12432, #12673 | +| [@marcelokarval](https://github.com/marcelokarval) | direct commit / report | +| [@maxmad64bis](https://github.com/maxmad64bis) | #1622, #11435, #11437, #11441, #11537, #11550, #11553, #11555, #11557, #11812, #11842, #11843, #11903, #12151, #12214, #12215, #12218, #12226, #12314, #12316, #12317, #12318, #12319, #12320, #12321, #12507, #12715, #12731, #12744, #12786, #12787, #12788, #12790, #12792, #12794, #12795, #12828, #12832, #12853, #12857, #12870, #12937, #12941, #12975, #13141, #13142, #13146, #13147 | +| [@mdigitalbh81](https://github.com/mdigitalbh81) | #12240 | +| [@Meet6338-X](https://github.com/Meet6338-X) | #11598, #11609, #12110 | +| [@morpheus9393](https://github.com/morpheus9393) | #11943 | +| [@MumuTW](https://github.com/MumuTW) | #11492, #11502, #11506, #11507, #11626, #11675, #11685, #11728, #11746, #11888, #11889, #11890, #11892 | +| [@Neuron-Mr-White](https://github.com/Neuron-Mr-White) | #11622, #11801, #12256 | +| [@NightStalker-87](https://github.com/NightStalker-87) | #12183 | +| [@NoxzRCW](https://github.com/NoxzRCW) | #11879, #11880, #11881, #11882, #11883, #11935 | +| [@ntdat812](https://github.com/ntdat812) | #11585, #12095 | +| [@ntdatt812](https://github.com/ntdatt812) | #11368, #11573, #11574, #11576, #11577, #11580, #11582, #11583, #11588, #11589, #11590, #11591, #11592, #11593, #11672, #12177, #12180, #12873, #12918, #12920, #12921, #12925, #12930, #13007, #13009, #13024, #13025, #13083, #13087, #13101, #13104, #13110 | +| [@official-burak](https://github.com/official-burak) | #11542 | +| [@opensource-elearning](https://github.com/opensource-elearning) | #12179, #12189, #12278, #12286 | +| [@oyi77](https://github.com/oyi77) | #11408, #11409, #11421, #11505, #11677, #12036, #12110 | +| [@pacocartones](https://github.com/pacocartones) | #11521, #11522, #11527, #11528, #11529, #11530, #11531, #11532, #11533, #11534, #11595, #11599, #11603, #11604, #11605, #11607, #11610, #11676, #11714, #11716, #11718, #11767, #11838, #11860, #11862, #11869, #11871, #11872, #11873, #11903, #11906, #11921, #11934, #12356, #12358, #12359, #12360, #12361, #12362, #12364, #12365, #12373, #12374, #12375, #12376, #12377, #12379, #12380, #12381, #12386, #12387, #12389, #12390, #12394, #12395, #12397, #12401, #12403, #12404, #12522, #12523, #12535, #12536, #12540, #12541, #12543, #12545, #12548, #12549, #12550, #12551, #12552, #12647, #12651, #12653, #13055 | +| [@patrykkopycinski](https://github.com/patrykkopycinski) | #11936, #11937, #12224 | +| [@PauloFH](https://github.com/PauloFH) | #11509 | +| [@PauloHSOliveira](https://github.com/PauloHSOliveira) | #12241 | +| [@ponkcore](https://github.com/ponkcore) | #12054 | +| [@Prajeeth-12](https://github.com/Prajeeth-12) | #11634, #12046 | +| [@quiterunner-commits](https://github.com/quiterunner-commits) | #12131, #12143 | +| [@rafacpti23](https://github.com/rafacpti23) | #11554, #11558, #12192, #12197, #12198, #12202, #12203, #12204 | +| [@ragnar-claude](https://github.com/ragnar-claude) | #11564 | +| [@raheemuddin786](https://github.com/raheemuddin786) | #11491, #11839, #11840, #11841, #12003, #12230, #12231, #12232, #12233, #12234 | +| [@rahilmavani](https://github.com/rahilmavani) | #11761 | +| [@Rahulsharma0810](https://github.com/Rahulsharma0810) | #11771 | +| [@RaviTharuma](https://github.com/RaviTharuma) | #11710, #11727, #11797, #11798, #11802, #11805, #11806, #11811, #12098, #12099, #12101, #12449, #12452, #12472, #12473, #12493, #12533, #12607, #12628, #12631, #12636, #12875, #12876, #12880, #12882, #12884 | +| [@rezjalibd](https://github.com/rezjalibd) | #12186 | +| [@RhianB14](https://github.com/RhianB14) | #11692 | +| [@rifqiawl](https://github.com/rifqiawl) | #11517, #11519, #11969 | +| [@rqzbeh](https://github.com/rqzbeh) | #11390 | +| [@Sabeekhann](https://github.com/Sabeekhann) | #11699, #11705 | +| [@santosraju99-hub](https://github.com/santosraju99-hub) | #11755, #11770 | +| [@Seramicx](https://github.com/Seramicx) | #11568 | +| [@Siva010](https://github.com/Siva010) | #12191 | +| [@solstxce](https://github.com/solstxce) | #11597 | +| [@soroush5](https://github.com/soroush5) | #12691 | +| [@tenshiak](https://github.com/tenshiak) | #12279 | +| [@TheDemonTuan](https://github.com/TheDemonTuan) | #11468, #11470, #11471, #11482, #11548, #11758, #11775, #11814 | +| [@thomasmaerz](https://github.com/thomasmaerz) | #12834 | +| [@tuandinh0801](https://github.com/tuandinh0801) | #11454 | +| [@turbolego](https://github.com/turbolego) | #11621, #11762, #11772, #11774, #11781, #12216 | +| [@Tushar49](https://github.com/Tushar49) | #11392 | +| [@ujjawalkaushik1110](https://github.com/ujjawalkaushik1110) | #11863 | +| [@vermasomesh835](https://github.com/vermasomesh835) | #11794 | +| [@vsd2807](https://github.com/vsd2807) | #11565, #11619 | +| [@wahidsadik371-coder](https://github.com/wahidsadik371-coder) | #12126 | +| [@watchingdogs](https://github.com/watchingdogs) | #12031 | +| [@wildcard](https://github.com/wildcard) | #11620 | +| [@xiaoyaner0201](https://github.com/xiaoyaner0201) | #11460, #11662, #12083 | +| [@Xxx91n](https://github.com/Xxx91n) | #11690 | +| [@yourspraveen](https://github.com/yourspraveen) | #11146 | +| [@yxyxy](https://github.com/yxyxy) | #11671 | +| [@ZaimMarzuki](https://github.com/ZaimMarzuki) | #11960 | +| [@Zartharas](https://github.com/Zartharas) | #11340 | +| [@zero-executioner](https://github.com/zero-executioner) | #11631 | +| [@diegosouzapw](https://github.com/diegosouzapw) | maintainer | + +--- ## [3.8.50] — 2026-08-25 _Living section — regenerated 2026-08-12 from all cycle commits (cycle open `ed2db6cb19` → tip). Bullets carry the merged PR and its author; direct pushes listed separately._ diff --git a/docs/i18n/or/CHANGELOG.md b/docs/i18n/or/CHANGELOG.md index 9fdbe92e7b..64e1edbbcd 100644 --- a/docs/i18n/or/CHANGELOG.md +++ b/docs/i18n/or/CHANGELOG.md @@ -96,31 +96,31 @@ ## [3.8.51] — TBD -_Living section — reconciled 2026-09-07 from all cycle commits (`release/v3.8.50` tip `091589089c` → `release/v3.8.51` tip `d6f315018a`, 696 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._ +_Living section — reconciled 2026-09-15 from all cycle commits (`091589089c` → `c0f92ec98a`, 916 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 | **120** | -| 📝 Commits in the cycle | **696** | -| 🔀 Pull requests referenced | **672** | -| 📋 Changelog entries | **685** | -| 🙌 Contributors credited in entries | **118** | +| 👥 People who contributed | **123** | +| 📝 Commits in the cycle | **916** | +| 🔀 Pull requests referenced | **892** | +| 📋 Changelog entries | **919** | +| 🙌 Contributors credited in entries | **121** | | 🤖 Automated dependency commits | 18 | **Entries by type** | Type | Count | | --- | ---: | -| 🐛 Fixes | 403 | -| ✨ Features | 118 | -| 🧹 Chore | 72 | -| 📚 Docs | 36 | -| 🧪 Tests | 29 | +| 🐛 Fixes | 592 | +| ✨ Features | 131 | +| 🧹 Chore | 93 | +| 📚 Docs | 42 | +| 🧪 Tests | 32 | +| ♻️ Refactor | 8 | +| 🏗️ Build | 7 | | ⚡ Performance | 7 | -| ♻️ Refactor | 7 | -| 🏗️ Build | 6 | | ⚙️ CI | 2 | | ⏪ Reverts | 2 | | 📦 Dependencies | 2 | @@ -128,35 +128,35 @@ _Living section — reconciled 2026-09-07 from all cycle commits (`release/v3.8. ### 🏆 Top 25 contributors this cycle -_By commits in `release/v3.8.50..release/v3.8.51` (`091589089c..d6f315018a`), author identities consolidated via `.mailmap` and the merged PR's GitHub login. Bots excluded._ +_By commits in `091589089c..c0f92ec98a`, author identities consolidated via `.mailmap` and the merged PR's GitHub login. Bots excluded._ | # | Contributor | Commits | | ---: | --- | ---: | -| 🥇 | diegosouzapw | 240 | -| 🥈 | Paco Cartones (@pacocartones) | 58 | -| 🥉 | Bob.Hou (@HouMinXi) | 57 | -| 4 | Dizzle (@maxmad64bis) | 27 | -| 5 | Webman (@jonlwheat2-gif) | 22 | -| 6 | Ravi Tharuma (@RaviTharuma) | 21 | -| 7 | backryun | 17 | -| 8 | Markus Hartung (@hartmark) | 17 | -| 9 | Nguyen Thanh Dat (@ntdatt812) | 16 | -| 10 | Rafa Martins (@rafacpti23) | 10 | -| 11 | Syed Raheemuddin (@raheemuddin786) | 10 | -| 12 | Koosha Paridehpour (@KooshaPari) | 9 | -| 13 | Nguyễn Viết Tuấn (@TheDemonTuan) | 8 | -| 14 | MumuTW | 7 | -| 15 | Paijo (@oyi77) | 7 | -| 16 | Tobias Andersen (@turbolego) | 6 | -| 17 | Bl0ck (@Bl0ck154) | 5 | -| 18 | SHANMUGAPRIYAN (@geek007git) | 5 | -| 19 | KaspaPulse | 5 | -| 20 | Abhishek Sharma (@abhisheksharma2411) | 4 | -| 21 | Abhishek Divekar (@adivekar-utexas) | 4 | -| 22 | Andrew B. (@AndrianBalanescu) | 4 | -| 23 | NoxzRCW | 4 | -| 24 | opensource-elearning | 4 | -| 25 | Alvin T. Veroy (@alvinveroy) | 3 | +| 🥇 | diegosouzapw | 323 | +| 🥈 | Bob.Hou (@HouMinXi) | 79 | +| 🥉 | Paco Cartones (@pacocartones) | 77 | +| 4 | Dizzle (@maxmad64bis) | 49 | +| 5 | Koosha Paridehpour (@KooshaPari) | 43 | +| 6 | Nguyen Thanh Dat (@ntdatt812) | 31 | +| 7 | Ravi Tharuma (@RaviTharuma) | 26 | +| 8 | Markus Hartung (@hartmark) | 22 | +| 9 | Webman (@jonlwheat2-gif) | 22 | +| 10 | backryun | 17 | +| 11 | anhtahaylove | 15 | +| 12 | Rafa Martins (@rafacpti23) | 10 | +| 13 | Syed Raheemuddin (@raheemuddin786) | 10 | +| 14 | Nguyễn Viết Tuấn (@TheDemonTuan) | 8 | +| 15 | MumuTW | 7 | +| 16 | Paijo (@oyi77) | 7 | +| 17 | Tobias Andersen (@turbolego) | 6 | +| 18 | Bl0ck (@Bl0ck154) | 5 | +| 19 | SHANMUGAPRIYAN (@geek007git) | 5 | +| 20 | KaspaPulse | 5 | +| 21 | Abhishek Sharma (@abhisheksharma2411) | 4 | +| 22 | Abhishek Divekar (@adivekar-utexas) | 4 | +| 23 | Andrew B. (@AndrianBalanescu) | 4 | +| 24 | NoxzRCW | 4 | +| 25 | opensource-elearning | 4 | ### ✨ New Features @@ -282,6 +282,19 @@ _By commits in `release/v3.8.50..release/v3.8.51` (`091589089c..d6f315018a`), au - **feat(dashboard):** link a log entry's Conversation Context to its owning conversation ([#12646](https://github.com/diegosouzapw/OmniRoute/pull/12646)) — thanks @hartmark - **feat(catalog):** eligibility-gated free-tier bucket ([#12669](https://github.com/diegosouzapw/OmniRoute/pull/12669)) - **feat(db):** fail-closed previous_response_id continuation for redacted video turns (#12150 P2b) ([#12707](https://github.com/diegosouzapw/OmniRoute/pull/12707)) +- **feat(gamification): pay the documented `streak_bonus` and `badge_unlock` XP rewards.** `XP_REWARDS` listed both rewards but the award pipeline never paid them: the private reward table in `events.ts` omitted them, `updateStreak()` did not report when a streak extended, and badge unlocks carried no XP. Every request that extends a daily streak now pays `streak_bonus × streak length` once per UTC day (guarded by a same-day `xp_audit_log` check), and every badge unlocked through the pipeline pays `badge_unlock` once per badge (guarded by the `user_badges` primary key; `unlockBadge()` now reports whether it inserted). Bonus XP flows through the same `addXp` + level sync + global/weekly/monthly leaderboard path as action XP, so level-ups and rankings include it. The Radar supporter recognition unlock stays XP-free. (#12522 — thanks @pacocartones) +- **feat(providers):** advertise a `free-tier` capability in the provider plugin manifest for every provider with documented free models, so sidecars and dashboards can filter free-capable providers without reading the quota catalog ([#12786](https://github.com/diegosouzapw/OmniRoute/pull/12786)) — thanks @maxmad64bis +- **feat(providers):** Added EURouter as an OpenAI-compatible API-key gateway (`https://api.eurouter.ai/v1`), with live model discovery via `passthroughModels`. Its copy states that models are served by third-party upstreams listed per model, so an EU-based router is not read as EU data residency for inference. ([#13025](https://github.com/diegosouzapw/OmniRoute/pull/13025)) — thanks @ntdatt812 +- **feat(providers):** Added GreenPT as an OpenAI-compatible API-key provider (`https://api.greenpt.ai/v1`), with live model discovery via `passthroughModels`. No free-inference badge: the published docs describe a free API subscription billed per token, not a free tier. ([#13024](https://github.com/diegosouzapw/OmniRoute/pull/13024)) — thanks @ntdatt812 +- **feat(i18n):** 9 new locales — Greek (`el`), Croatian (`hr`), Serbian (`sr`), Lithuanian (`lt`), Estonian (`et`), Latvian (`lv`), Slovenian (`sl`), Maltese (`mt`), Irish (`ga`) — across the dashboard, docs mirrors, CLI, README and the site; OmniRoute now ships every official EU language (51 locales). (#13044) +- **feat(i18n):** 8 new locales — Kannada (`kn`), Malayalam (`ml`), Odia (`or`), Punjabi (`pa`), Nepali (`ne`), Sinhala (`si`), Burmese (`my`), Khmer (`km`) — across the dashboard, docs mirrors, CLI, README and the site (59 locales). The translator now restores the ICU literal escape around angle placeholders and splits oversized docs sections before translating. (#0000) ([#13660](https://github.com/diegosouzapw/OmniRoute/pull/13660)) +- feat(providers): **list Agnes 3.0 Flash as the current free chat model, drop retired 1.5 Flash, add Image 2.0/2.5 Flash plus Video 2.5/2.5 Flash, and discover the live `/v1/models` catalog (including the CN host `api.agnes-ai.cn`).** `agnes-1.5-flash` now forwards to `agnes-3.0-flash`. Video 2.5 polls `GET /v1/videos/{id}` (not the V2.0 `/agnesapi` contract). Live `/v1/models` (2026-09-09) no longer serves 1.5; the wiki marks it deprecated. 3.0 Flash is 512K context / 65,536 max output, same window as 2.5. CN-region keys use the existing per-connection base-URL field, default stays `apihub.agnes-ai.com`. ([#13120](https://github.com/diegosouzapw/OmniRoute/pull/13120)) — thanks @HouMinXi +- **feat(sse):** Codex and OpenAI catalogs list GPT-6 Astra with effort aliases (`-low` through `-ultra`); Codex CLI identity pins `@openai/codex@0.153.4` in lockstep with the image ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **feat(dashboard):** the orchestration History tab gained a "Compare runs" mode — toggling it turns each grid cell into a 2-item selection queue (a 3rd click drops the oldest pick), and picking two cells opens a side-by-side comparison panel instead of the usual detail drawer. The panel fetches both runs' detail the same way the drawer does (falling back to persisted history once a run leaves the live TTL window) and shows, per side: identity/source/state, start time, a signed `right - left` delta for duration/cost/event count, the event timeline aligned by index, and any memory hits. One side's fetch failing never blocks the other, and a delta is only ever computed when both sides have a finite value — otherwise it renders "—", never `NaN`. Comparing two runs from different sources or skills still works; a banner marks the deltas as informational rather than hiding them, since the two runs aren't a strict apples-to-apples pair. ([#12677](https://github.com/diegosouzapw/OmniRoute/pull/12677)) +- **feat(dashboard):** badge a conversation that never reached a clean stop ([#12717](https://github.com/diegosouzapw/OmniRoute/pull/12717)) — thanks @hartmark +- **feat(release):** reconcile-changelog tool + version-anchored fragment aggregation ([#12987](https://github.com/diegosouzapw/OmniRoute/pull/12987)) +- **feat(mcp):** carry context_length through MCP list_models_catalog projection (#12776) ([#13406](https://github.com/diegosouzapw/OmniRoute/pull/13406)) — thanks @KooshaPari +- **feat(cli):** make startup readiness budget configurable (#13369) ([#13433](https://github.com/diegosouzapw/OmniRoute/pull/13433)) — thanks @KooshaPari ### 🐛 Bug Fixes @@ -698,6 +711,194 @@ _By commits in `release/v3.8.50..release/v3.8.51` (`091589089c..d6f315018a`), au - **fix(i18n):** quote placeholder in OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES description (#12505) ([#12769](https://github.com/diegosouzapw/OmniRoute/pull/12769)) — thanks @KooshaPari - **fix(api/pricing):** surface validation error message as string, not raw object (#12494) ([#12771](https://github.com/diegosouzapw/OmniRoute/pull/12771)) — thanks @KooshaPari - **fix(sse):** re-enable prompt compression for native Codex passthrough ([#12834](https://github.com/diegosouzapw/OmniRoute/pull/12834)) — thanks @thomasmaerz +- **fix(dashboard):** model health tests for a provider node set to the Responses API now call `/v1/responses` with a Responses-shaped body instead of `/v1/chat/completions` — those models were reported as `Provider returned HTTP 200 but no text content` even though the same model answered normally through `/v1/responses` ([#13070](https://github.com/diegosouzapw/OmniRoute/issues/13070)) ([#13087](https://github.com/diegosouzapw/OmniRoute/pull/13087)) — thanks @ntdatt812 +- fix(routing): stop a round-robin combo's "opencode" targets from collapsing onto the opencode-zen connection (#11912) ([#13283](https://github.com/diegosouzapw/OmniRoute/pull/13283)) +- fix(routing): stop the reactive-compaction debug log from lying when compression is globally disabled (#11977) ([#13276](https://github.com/diegosouzapw/OmniRoute/pull/13276)) +- fix(electron): relativize standalone-bundle symlink targets so Stage 8 manifest verification stops failing on Windows (#11979) ([#13251](https://github.com/diegosouzapw/OmniRoute/pull/13251)) +- fix(dashboard): surface a visible error when Compression Studio's combined preview run fails (#12061) ([#13288](https://github.com/diegosouzapw/OmniRoute/pull/13288)) +- fix(dashboard): make the compression "Effective pipeline" preview honor the active profile and warn when the master switch is off (#12063) ([#13287](https://github.com/diegosouzapw/OmniRoute/pull/13287)) +- fix(providers): scope TinyCMS Web signer's DOM shims to each call instead of leaking them for the process lifetime, and surface a clean HTTP status on a non-JSON interception-toggles error (#12072) ([#13254](https://github.com/diegosouzapw/OmniRoute/pull/13254)) +- fix(guardrails): stop Vision Bridge from re-selecting a model locked after a 404 (#12111) ([#13259](https://github.com/diegosouzapw/OmniRoute/pull/13259)) +- fix(sse): require Responses-shaped body before native OpenAI-compatible passthrough (#12129) ([#13278](https://github.com/diegosouzapw/OmniRoute/pull/13278)) +- fix(providers): minimax-m3 now collapses manual thinking.type:"enabled" to adaptive, preventing upstream 400 (2013) (#12132) ([#13249](https://github.com/diegosouzapw/OmniRoute/pull/13249)) +- fix(db): scope model visibility overrides by modality so hiding a Chat model no longer hides an identically-ID'd Image/Embeddings/etc. model (#12172) ([#13258](https://github.com/diegosouzapw/OmniRoute/pull/13258)) +- fix(db): scope local-provider apiKey dedup to matching base URL so LM Studio/Ollama-style connections support multiple accounts (#12173) ([#13250](https://github.com/diegosouzapw/OmniRoute/pull/13250)) +- fix(providers): refresh Trae's stale Referer/Origin and forward user timezone so imported connections stop failing with 401 (#12190) ([#13255](https://github.com/diegosouzapw/OmniRoute/pull/13255)) +- fix(providers): route opencode-go/gpt-5.6-luna to /responses instead of /chat/completions (#12196) ([#13275](https://github.com/diegosouzapw/OmniRoute/pull/13275)) +- fix(dashboard): allow deleting the last extra-upstream-header row even when invalid (#12251) ([#13252](https://github.com/diegosouzapw/OmniRoute/pull/13252)) +- **fix(i18n):** translate pre-existing `__MISSING__:` keys for `combo.sort`, `requestLogger.detail` expand/collapse, `common.profile`, and `settings.resilienceCredentialHealth*` across 39 locales ([#12272](https://github.com/diegosouzapw/OmniRoute/issues/12272)) ([#13034](https://github.com/diegosouzapw/OmniRoute/pull/13034)) — thanks @HouMinXi +- fix(cli): run the Node.js runtime compatibility guard before the heavy `tsx/esm` + Commander import chain so an unsupported runtime gets a clear message instead of a raw `Invalid regular expression flags` crash (#12296) ([#13253](https://github.com/diegosouzapw/OmniRoute/pull/13253)) +- fix(dashboard): refresh the providers list after deleting a compatible provider node (#12298) ([#13256](https://github.com/diegosouzapw/OmniRoute/pull/13256)) +- fix(usage): fail closed on API-key budget enforcement when a provider's `auto` routing alias has no pricing row, instead of silently counting it as $0 (#12341) ([#13257](https://github.com/diegosouzapw/OmniRoute/pull/13257)) +- **fix(providers):** include the submitted Agnes video model when polling by `video_id` ([#12356](https://github.com/diegosouzapw/OmniRoute/pull/12356)) — thanks @pacocartones +- **fix(routing):** custom OpenAI-compatible nodes now honor the saved Chat/Responses API type after edits instead of letting the node's original ID prefix override the live connection setting ([#11884](https://github.com/diegosouzapw/OmniRoute/issues/11884)). ([#12358](https://github.com/diegosouzapw/OmniRoute/pull/12358)) — thanks @pacocartones +- fix(sse): surface an error instead of a silent empty 200 when a Claude stream closes with zero bytes (#12398) ([#13285](https://github.com/diegosouzapw/OmniRoute/pull/13285)) +- fix(oauth): warn before the dead localhost:8080 redirect in antigravity/gemini `oauth start` (#12413) ([#13265](https://github.com/diegosouzapw/OmniRoute/pull/13265)) +- fix(providers): stop devin-cli spawn error from double-closing the SSE controller (#12517) ([#13261](https://github.com/diegosouzapw/OmniRoute/pull/13261)) +- **fix(rerank):** clamp Voyage `top_k` to the documents actually sent after empty-string filtering, and honor `return_documents: false` in the NVIDIA response adapter (#12523 — thanks @pacocartones) +- **fix(audio):** `/v1/audio/translations` now resolves combo names the way `/v1/audio/transcriptions` already does, so a combo that `GET /v1/models` advertises is fanned out to its targets instead of being rejected with `400 Invalid translation model: . Use format: provider/model`; literal `provider/model` ids and unknown bare names behave as before (#12536 — thanks @pacocartones) +- **fix(gemini):** strip the JSON-Schema-2020-12 `prefixItems` keyword from Gemini tool schemas at every nesting level, so Claude Code tool definitions no longer fail with `400 Unknown name "prefixItems"` on Gemini models (#12540 — thanks @pacocartones) +- **fix(api-manager):** Expose an accessible loading status while API keys are fetched instead of an empty accessibility tree (#12541 — thanks @pacocartones) +- **fix(video):** Clamp `estimateJpegFrameBytes` at zero for padding-only payloads and build the three encode-side frame data URIs from `JPEG_FRAME_DATA_URI_PREFIX` instead of a repeated literal (#12543 — thanks @pacocartones) +- **fix(devin):** accept Windows `DEVIN_AGENTIC_HOME` sandbox paths (`C:\...\.sandbox\...`) in the isolated-home check so the Devin Claude Bridge no longer fails closed on Windows ([#12405](https://github.com/diegosouzapw/OmniRoute/issues/12405)) (#12545 — thanks @pacocartones) +- **fix(db):** Add `conversation_turn_nodes` and orphaned `agentic_conversations` to the auto-cleanup cycle under the existing `retention.callLogs` window, so identity nodes whose call-log content has already been purged no longer accumulate without bound in `storage.sqlite` (#12548 — thanks @pacocartones) +- **fix(i18n):** Wrap the `~/.claude/profiles//settings.json` placeholder in ICU single quotes in the `OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES` feature-flag description across all 42 locales and the TypeScript default, so next-intl no longer fails with `INVALID_MESSAGE: UNCLOSED_TAG` and the Feature Flags card shows the description instead of the raw key (#12549 — thanks @pacocartones) +- **fix(orchestration):** `updateCloudAgentTask` now publishes the task's real `status` on `agent.task.updated` when an update only touches `result`, `activities` or `error`, instead of the fabricated `"updated"` state, and stays silent when no row matched the id (#12550 — thanks @pacocartones) +- **fix(i18n):** the home "Recent Requests" panel and the Provider Topology legend are now translated instead of rendering English copies on non-English dashboards; the legend reads its own `home.topologyLegend*` labels with consistent casing rather than borrowing the memory-settings "Recent" and analytics "Error" strings (#12551 — thanks @pacocartones). +- **docs(reference):** bring the `FEATURE_FLAGS.md` catalog back to 1:1 with `featureFlagDefinitions.ts` — 20 missing flags added, the two `*_BLOCK_THRESHOLD` env-only knobs moved out of the flag tables, category/total counts and the Live WS port corrected, guarded by a static test (#12552 — thanks @pacocartones) +- **fix(i18n):** backfill missing `usage.kiloPass*` strings in 39 locales and restore `featureFlagOmnirouteDisableThinkingLevelVariantsDescription` in `pt.json` ([#12561](https://github.com/diegosouzapw/OmniRoute/issues/12561)) ([#13027](https://github.com/diegosouzapw/OmniRoute/pull/13027)) — thanks @HouMinXi +- fix(docker): default docker-compose app ports (dashboard/API/live-WS) to loopback instead of `0.0.0.0`, closing the anonymous `/v1` LAN/WAN exposure gap left open by `REQUIRE_API_KEY=false` (#12568) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): close DNS-rebinding SSRF gap in webhook outbound-URL guard (#12569) ([#13243](https://github.com/diegosouzapw/OmniRoute/pull/13243)) +- fix(docker): require a per-session token on the VNC browser CDP bridge and isolate it on a dedicated Docker network (#12571) ([#13241](https://github.com/diegosouzapw/OmniRoute/pull/13241)) +- fix(open-sse): write Adobe Firefly session tokens and cookie jars with 0700/0600 permissions instead of the process umask (#12572) ([#13242](https://github.com/diegosouzapw/OmniRoute/pull/13242)) +- fix(api): remove hardcoded wildcard CORS in openai-to-gemini-sse.ts so the centralized fail-closed CORS gate is the sole source of `Access-Control-Allow-Origin` (#12573) ([#13239](https://github.com/diegosouzapw/OmniRoute/pull/13239)) +- fix(api): enforce API key policy (budget/rate-limit/schedule/endpoint scoping) on the ElevenLabs speech-to-text, text-to-speech and voices proxy routes (#12574) ([#13244](https://github.com/diegosouzapw/OmniRoute/pull/13244)) +- fix(sse): cap HuggingChat NDJSON body size and bound the read loop with the fetch timeout so a stalled or hostile upstream cannot buffer unbounded memory (#12577) ([#13282](https://github.com/diegosouzapw/OmniRoute/pull/13282)) +- fix(docker): scope the cliproxyapi/qdrant/bifrost sidecars to loopback by default and forward `CLIPROXYAPI_MANAGEMENT_KEY` into the cliproxyapi container so its management API is not left both unauthenticated and LAN-published (#12578) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): create DB export temp paths with `fs.mkdtempSync` instead of predictable timestamps (#12579) ([#13237](https://github.com/diegosouzapw/OmniRoute/pull/13237)) +- Cline 401 bodies that say "re-authenticate your Cline account" classify as a refreshable OAuth token, not a terminal expired key. The cooling panel no longer labels every cooldown as a 429; it shows the recorded last error instead. (#12594) ([#13060](https://github.com/diegosouzapw/OmniRoute/pull/13060)) — thanks @HouMinXi +- **fix(catalog):** degrade unknown combo targets instead of dropping LCD modalities, and persist OpenRouter `architecture.input_modalities` into the capability snapshot ([#12613](https://github.com/diegosouzapw/OmniRoute/issues/12613)) ([#13042](https://github.com/diegosouzapw/OmniRoute/pull/13042)) — thanks @HouMinXi +- fix(providers): send `x-api-key` instead of `Authorization: Bearer` for OpenCode Zen's `/v1/responses` endpoint (Muse Spark Contributor models), fixing a 401 on OmniRoute's auth header (#12633) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(sse): surface the actionable "Auggie CLI not found" message when the shell reports a missing `auggie` binary via exit code instead of a spawn error (#12645) ([#13263](https://github.com/diegosouzapw/OmniRoute/pull/13263)) +- **fix(config):** Persist deletions of built-in background-degradation entries — when a stored settings record exists its `degradationMap` is now authoritative instead of being merged under the defaults, so an entry the user removed in the dashboard no longer reappears on the next apply or restart ([#12424](https://github.com/diegosouzapw/OmniRoute/issues/12424)) ([#12647](https://github.com/diegosouzapw/OmniRoute/pull/12647)) — thanks @pacocartones +- **fix(gamification):** action-count badge milestones (First Token, Token Consumer, Token Machine, Token Whale, and the token-sharing tier) are now backed by a durable `xp_action_counts` counter incremented in `addXp()`, instead of a live `COUNT(*)` over `xp_audit_log`. The audit log is pruned by `retention.xpAuditLog` (default 30 days), so on a default install those "lifetime" milestones were really "actions in the last 30 days" and unlocked badges could stop unlocking once old rows aged out. `getActionCount()` and `checkActionCountBadges()` now read the same durable source, and a migration backfills existing totals from the surviving audit rows ([#12546](https://github.com/diegosouzapw/OmniRoute/issues/12546)) ([#12651](https://github.com/diegosouzapw/OmniRoute/pull/12651)) — thanks @pacocartones +- **fix(images):** `/v1/images/edits` now iterates a combo's targets the same way `/v1/images/generations` does (#9239) instead of flattening a bare combo to its first target. A combo whose first target is not edit-capable — or lacks credentials — now falls through to a later edit-capable target rather than hard-erroring, and missing credentials are skipped (not a hard `401`) to match the generations path. The per-target skip/terminal classification is extracted into a shared `runImageComboTargets` loop, so generations behavior is unchanged ([#12547](https://github.com/diegosouzapw/OmniRoute/issues/12547)). ([#12653](https://github.com/diegosouzapw/OmniRoute/pull/12653)) — thanks @pacocartones +- fix(sse): add first-byte watchdog to the TLS-fingerprint transport so a stalled wreq body falls back instead of hanging for minutes (#12656) ([#13272](https://github.com/diegosouzapw/OmniRoute/pull/13272)) +- fix(sse): exempt tiny-budget reasoning probes from combo quality failure and surface persisted-cooldown skips in ALL_TARGETS_SKIPPED diagnostics (#12659) ([#13269](https://github.com/diegosouzapw/OmniRoute/pull/13269)) +- fix(models): declare the real ~1M contextLength for OpenCode Zen's Muse Spark 1.2 models instead of falling back to the 200000 provider default (#12681) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(oauth): align codebuddy-cn OAuth User-Agent with the chat/usage CLI version to avoid WAF false positives (#12702) ([#13264](https://github.com/diegosouzapw/OmniRoute/pull/13264)) +- fix(dashboard): surface an authentication-required banner instead of silently blanking database settings for a guest session (#12709) ([#13270](https://github.com/diegosouzapw/OmniRoute/pull/13270)) +- **fix(resilience):** stop unbounded queue that hangs 6min until Aborted — gate, provider slot, and Bottleneck queue now share a per-connection `maxWaitMs` budget; `fail-closed` on exhaust (503); execution backstop `executionMaxWaitMs` overridable per connection with upstream clamp ([#12715](https://github.com/diegosouzapw/OmniRoute/pull/12715)) — thanks @maxmad64bis (with thanks to @Tushar49 for surfacing the slow-provider need in #12635) +- **fix(auto-combo):** every mode pack now carries `quality` and `reliability` weights (`quality` 0.02, 0.03 in `quality-first`; `reliability` 0.03, 0.04 in `reliability-first`), so selecting a pack no longer silences either signal; `DEFAULT_WEIGHTS` is unchanged ([#12731](https://github.com/diegosouzapw/OmniRoute/pull/12731)) — thanks @maxmad64bis +- **fix(sse):** the provider execution pipeline keeps the upstream error `code`/`type` again — both were lost when the non-streaming leg was extracted, so a config-class `422` (Antigravity missing project) degraded into a generic account cooldown instead of reaching the gate that recognises it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(db):** a legacy `call_logs` table no longer aborts startup — the provider-stats index is created after the column healing runs, not before it, so an install predating `request_type` opens instead of failing with `no such column` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(catalog):** a malformed row in the operator-writable custom-models blob no longer kills every `auto/*` pool ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(tests):** realign the guards that the same day's merges left asserting the old behavior — the provider-count total after GreenPT and EURouter, the GLM stream buffer slot that `#12925` turned into a declared parameter, and the injection scan bound that `#13104` reshaped from a head-only window into head-plus-tail ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- fix(cache): fold tool_choice/tools/response_format into the semantic cache signature so a cached tool_calls response can no longer be replayed for a request whose tool policy forbids it (#12734) ([#13267](https://github.com/diegosouzapw/OmniRoute/pull/13267)) +- **fix(models):** stop treating provider-supplied `isFree:true`/`:free`/`0/0` flags as trusted unless the provider has a documented free tier — only the shipped free-tier catalog decides otherwise; fetched `isFree:true` trusted only for a free-tier provider, custom `isFree:true` via a trusted path ([#12744](https://github.com/diegosouzapw/OmniRoute/pull/12744)) — thanks @maxmad64bis +- fix(memory): authenticate the internal /v1/rerank loopback call so memory reranking no longer silently degrades to unranked order when REQUIRE_API_KEY=true (#12745) ([#13268](https://github.com/diegosouzapw/OmniRoute/pull/13268)) +- fix(sse): parse Ollama Cloud's current usage markup (`$X of $Y used` aria-label, nested width style) (#12749) ([#13260](https://github.com/diegosouzapw/OmniRoute/pull/13260)) +- fix(cli): setup-opencode no longer sends an active context's management token to `/v1/models` when `--api-key`/`OMNIROUTE_API_KEY` is supplied — an explicit flag or the env var now always outranks the context's token, and the flag itself is no longer swallowed by the parent program's global `--api-key` option (#12783) ([#13246](https://github.com/diegosouzapw/OmniRoute/pull/13246)) +- fix(sse): register Arcee AI in the executor provider registry so requests reach api.arcee.ai instead of silently falling back to OpenAI (#12784) ([#13277](https://github.com/diegosouzapw/OmniRoute/pull/13277)) +- **fix(quota):** share one upstream quota read when identical saturation checks arrive at the same time, so traffic bursts don't multiply provider API calls ([#12787](https://github.com/diegosouzapw/OmniRoute/pull/12787)) — thanks @maxmad64bis +- **fix(db):** ignore expired or invalid rate-limit cooldown writes so a stale timestamp can't lock a connection that should be usable — clearing still works as before ([#12788](https://github.com/diegosouzapw/OmniRoute/pull/12788)) — thanks @maxmad64bis +- **fix(providers):** single source for provider order with `xao` ranking alongside `xai-oauth` ([#12790](https://github.com/diegosouzapw/OmniRoute/pull/12790)) — thanks @maxmad64bis +- **fix(routing):** off-table models check the free-model catalog before inheriting premium prices, latency bootstraps from the observed pool median, async tiers read live database pricing with a 90-day freshness gate, and the tier cache invalidates on every pricing write ([#12792](https://github.com/diegosouzapw/OmniRoute/pull/12792)) — thanks @maxmad64bis +- **fix(auto-combo):** snapshot scoring uses the observed per-connection account tier and per-model quality instead of neutral constants, and circuit-open providers rank lower at build time ([#12794](https://github.com/diegosouzapw/OmniRoute/pull/12794)) — thanks @maxmad64bis +- **fix(pool):** empty `auto/*` pools now say why they are empty, and the models listing uses the same paid check as routing ([#12795](https://github.com/diegosouzapw/OmniRoute/pull/12795)) — thanks @maxmad64bis +- fix(routing): recognize CLIProxyAPI's 'unknown provider for model' 400 as fallback-worthy (#12800) ([#13284](https://github.com/diegosouzapw/OmniRoute/pull/13284)) +- **fix(sse):** translate-mode streams now emit the estimated token counts as a trailing usage-only chunk before `[DONE]` when the upstream stays silent, so metered chat clients see totals instead of nothing ([#12828](https://github.com/diegosouzapw/OmniRoute/pull/12828)) — thanks @maxmad64bis +- **fix(db):** provider stats stay truthful on empty and legacy databases: fallback counts default to `0` instead of `null`, latency averages read `null` (not `0`) when no durations were recorded, failures logged before the error-type column existed group under `pre_migration` instead of `unclassified`, and per-provider queries use two new composite indexes ([#12832](https://github.com/diegosouzapw/OmniRoute/pull/12832)) — thanks @maxmad64bis +- fix(nvidia): fail open when a synced model catalog goes stale instead of gating forever (#12849) ([#13248](https://github.com/diegosouzapw/OmniRoute/pull/13248)) +- **fix(db):** WAL maintenance lives in its own module: a `TRUNCATE` checkpoint that hits a busy database now warns with its streak and retries once via `PASSIVE` instead of logging success, and closing-time checkpoints no longer report success on builds without a database file, with the busy totals visible in the authenticated monitoring health payload ([#12853](https://github.com/diegosouzapw/OmniRoute/pull/12853)) — thanks @maxmad64bis +- **fix(health):** quota with no snapshots reads empty instead of a contradicting 0% ([#12857](https://github.com/diegosouzapw/OmniRoute/pull/12857)) — thanks @maxmad64bis +- fix(a2a): accept the dashboard's own session cookie on /a2a so "Run message/send" no longer fails with "Unauthorized: missing or invalid API key" (#12888) ([#13271](https://github.com/diegosouzapw/OmniRoute/pull/13271)) +- **fix(logs):** the Logs grid's in-memory filter pass no longer discards rows the SQL query already matched — selecting an API key from the dropdown (which sends the key's id) returns its calls again, the Combo tab shows every combo instead of only those whose name contains a "1", and the model filter and search cover the same columns as the query ([#12896](https://github.com/diegosouzapw/OmniRoute/pull/12896)) — fixes [#12873](https://github.com/diegosouzapw/OmniRoute/issues/12873) — thanks @ntdatt812 +- **fix(a2a):** `/api/a2a/status` now builds the agent card from the request that asked for it, so a gateway reached at a non-localhost host no longer advertises `http://localhost:20128` as its A2A URL ([#12918](https://github.com/diegosouzapw/OmniRoute/pull/12918)). — thanks @ntdatt812 +- **fix(compression):** progressive aging now appends its `[COMPRESSED:aging:…]` annotation after a turn's `tool_result` blocks instead of in front of them, so Anthropic no longer rejects aged conversations with "`tool_use` ids were found without `tool_result` blocks immediately after" ([#12920](https://github.com/diegosouzapw/OmniRoute/pull/12920)). — thanks @ntdatt812 +- **fix(bedrock):** model import now resolves context limits for every vendor prefix instead of only `anthropic.*`, so `global.openai.gpt-5.6-*` no longer imports with a null `inputTokenLimit` and gets rejected pre-flight at the 200k default ([#12921](https://github.com/diegosouzapw/OmniRoute/pull/12921)). — thanks @ntdatt812 +- **fix(stream):** the 64 KB stream buffer GLM asks for is honoured instead of dropped, and the type error it caused no longer fails the API Route Typecheck gate on every open PR ([#12925](https://github.com/diegosouzapw/OmniRoute/pull/12925)) — thanks @ntdatt812 +- **fix(guardrails):** mask PII inside a `tool_result`'s nested content array, which the masker walked past while redacting its sibling block ([#12930](https://github.com/diegosouzapw/OmniRoute/pull/12930)) — thanks @ntdatt812 +- **fix(dashboard):** the Radar catalog table no longer leaves absent data unexplained — empty limits, unknown context windows, and unreported capabilities each explain themselves on hover, and a new check keeps it that way ([#12937](https://github.com/diegosouzapw/OmniRoute/pull/12937)) — thanks @maxmad64bis +- **fix(sse):** geo-blocked opencode requests rotate to the next account proxy instead of failing, so one refused egress no longer aborts the whole chain ([#12941](https://github.com/diegosouzapw/OmniRoute/pull/12941)) — thanks @maxmad64bis +- **fix(codex):** dashboard "clear cooldown" and the CAS recovery path now drop nested `codexScopeRateLimitedUntil` maps in the same write that nulls `rate_limited_until`, and fresh quota snapshots with headroom lift fallback-sourced scope cooldowns parked by quota preflight ([#12817](https://github.com/diegosouzapw/OmniRoute/issues/12817), [#12860](https://github.com/diegosouzapw/OmniRoute/issues/12860), [#12951](https://github.com/diegosouzapw/OmniRoute/pull/12951)) The dashboard reset-credit button (`consumeCodexResetCredit`) now exercises that same snapshot path after a successful redeem, so a filled quota bar is enough to unpark a leftover fallback Codex child without another manual clear. — thanks @HouMinXi +- **fix(db):** resolve `sql-wasm.wasm` across global npm install and hoisted layouts, ensuring OmniRoute can boot cleanly on Node 24 when native `better-sqlite3` is uncompiled. ([#13035](https://github.com/diegosouzapw/OmniRoute/pull/13035)) — thanks @HouMinXi +- **fix(api):** `DELETE /v1/batches/delete-completed` now sweeps only the calling API key's own completed batches (batches with no owner stay out of a key-scoped sweep on purpose), with an explicit instance-wide mode reserved for authenticated dashboard sessions, a 401 for a presented key that is unknown, revoked, deactivated, banned or expired (never falling through to the session branch), audit logging of every sweep, a sanitized 500 on failure, an owner-scoped file half (a key-scoped sweep never nulls a file another tenant owns) and an atomic sweep — chunked in 200-batch transactions in instance mode — so a mid-way error never leaves a batch pointing at a nulled file (GHSA-wvxc-jp3v-5mg5) ([#12969](https://github.com/diegosouzapw/OmniRoute/pull/12969)) +- **fix(combo):** quota-weighted routing stops drawing on an out-of-credit connection — a 402 now invalidates the stored quota snapshot instead of leaving its stale remaining percentage in place, and a snapshot older than 10 minutes no longer counts as confident headroom for the primary pool ([#12972](https://github.com/diegosouzapw/OmniRoute/pull/12972)) ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- **fix(vision):** Custom Models with "Vision capable" checked no longer have image requests swapped to `glm/glm-4.6v` when the client sends the advertised alias (`vllm/path/...`) or the bare path-shaped id — Vision Bridge now matches the stored override for all three id forms ([#12758](https://github.com/diegosouzapw/OmniRoute/issues/12758)) ([#12974](https://github.com/diegosouzapw/OmniRoute/pull/12974)) — thanks @HouMinXi +- **fix(sse):** transient opencode upstream failures rotate to the next account proxy instead of failing, so one flapping egress no longer aborts the whole chain ([#12975](https://github.com/diegosouzapw/OmniRoute/pull/12975)) — thanks @maxmad64bis +- **fix(azure):** Deployments from GPT-6 onward now send `max_completion_tokens` instead of `max_tokens`, which Azure rejects with HTTP 400. The rule matched a literal `gpt-5`, so each new generation arrived broken; it now matches the generation range, while `gpt-35-turbo` still keeps `max_tokens`. ([#13007](https://github.com/diegosouzapw/OmniRoute/pull/13007)) — thanks @ntdatt812 +- **fix(db):** add an opt-in self-restart circuit for sustained critical memory pressure, gate post-cleanup VACUUM behind a minimum freed-rows threshold, and checkpoint the SQLite WAL every 5 minutes with a size guard that escalates to TRUNCATE, so a growing WAL can no longer stall the event loop into a full outage. ([#13011](https://github.com/diegosouzapw/OmniRoute/pull/13011)) — thanks @HouMinXi +- **fix(auth):** an explicit connection pin may probe a quota-disabled row once and re-enable it on success ([#12874](https://github.com/diegosouzapw/OmniRoute/issues/12874)) ([#13017](https://github.com/diegosouzapw/OmniRoute/pull/13017)) — thanks @HouMinXi +- **fix(api):** thread `X-OmniRoute-Fallback-Attempts` through combo chat completions so streaming and non-streaming responses report how many prior legs were attempted ([#13038](https://github.com/diegosouzapw/OmniRoute/pull/13038)) — thanks @HouMinXi +- Restore provider failure classification and credential refresh on non-streaming requests: classify non-2xx failures to lock models on per-model quota exhaustion, update connection rate limits from headers and body, and pass credential refresh handlers to pipeline execution so 401 tokens can be refreshed and retried (#13043). ([#13069](https://github.com/diegosouzapw/OmniRoute/pull/13069)) — thanks @HouMinXi +- **fix(responses):** wrap forced-non-streaming web_search fallback JSON as Responses SSE so Codex still sees `response.completed` ([#13050](https://github.com/diegosouzapw/OmniRoute/pull/13050)) — thanks @HouMinXi +- **fix(antigravity):** Preserve upstream thought-token usage in normalized completion and reasoning token counts ([#13055](https://github.com/diegosouzapw/OmniRoute/pull/13055)) — thanks @pacocartones +- Fix a concurrency-slot leak in the DeepSeek PoW solver: a worker that failed to spawn (for example a missing worker script) never released its slot, so `MAX_CONCURRENT_WORKERS` failures disabled the solver until restart. ([#13097](https://github.com/diegosouzapw/OmniRoute/pull/13097)) — thanks @anhtahaylove +- **fix(acp):** bound the ACP session output buffers — `stdoutBuffer` and `stderrBuffer` now cap at 1 MiB keeping the most recent output behind a visible `[...output truncated...]` marker, and `stderrBuffer` is reset per prompt instead of accumulating for the lifetime of the session. ([#13100](https://github.com/diegosouzapw/OmniRoute/pull/13100)) — thanks @anhtahaylove +- **fix(acp):** release the `stdout`/`exit` listeners and the idle timer that a `sendPrompt` timeout used to leave attached to the `acpManager` singleton, and drop sessions that exited on their own from the session map instead of keeping them forever. ([#13096](https://github.com/diegosouzapw/OmniRoute/pull/13096)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection and PII scanners now read the text a `tool_result` block carries on `content` (string or nested block list), in messages and in system blocks, so tool output is judged by the same rules as user text ([#13101](https://github.com/diegosouzapw/OmniRoute/pull/13101)) — thanks @ntdatt812 +- **fix(gamification):** close the badge notification SSE stream when the request signal is already aborted before the stream starts — a client that disconnects while the route is still awaiting auth used to leave both the 2s unlock poll and the 15s heartbeat running for the lifetime of the process. ([#13106](https://github.com/diegosouzapw/OmniRoute/pull/13106)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection scan now spends its 16 KB budget on both ends of the request instead of the first 16 KB only, so `system`, `instructions`, `query`, `documents` and the newest turns are no longer hidden behind one long message ([#13104](https://github.com/diegosouzapw/OmniRoute/pull/13104)) — thanks @ntdatt812 +- **fix(dashboard):** expose the Volcano Ark console cookie on quota scraping and unwrap connect-error objects so the dashboard shows the upstream message ([#13107](https://github.com/diegosouzapw/OmniRoute/pull/13107)) — thanks @HouMinXi +- **fix(db):** release the `beforeExit`/`SIGINT`/`SIGTERM` handlers when a `node:sqlite` adapter closes, so a closed adapter and its database handle are no longer pinned to `process` for the lifetime of the run — the same treatment #7494 gave the sql.js adapter. ([#13109](https://github.com/diegosouzapw/OmniRoute/pull/13109)) — thanks @anhtahaylove +- **fix(translator):** `contentSchema` and `unevaluatedItems` are now treated as subschema positions by the tool-schema sanitizer, so a truncation placeholder in either is replaced with a permissive schema instead of being forwarded as a string ([#13110](https://github.com/diegosouzapw/OmniRoute/pull/13110)) — thanks @ntdatt812 +- **fix(cli-helper):** clear the `createLogStream` timeout on the abort path — `stop()` aborts the in-flight fetch and returned through the `signal.aborted` branch, which skipped `clearTimeout` and left an armed timer per stopped stream. The stream reader is now also cancelled when the read loop exits early. ([#13114](https://github.com/diegosouzapw/OmniRoute/pull/13114)) — thanks @anhtahaylove +- **fix(docs):** drop TLS-impersonation, MITM-decrypt, supply-chain attestation, and XOR-mask writeups from the public `/docs` catalog and Docker image. Files stay in git for engineers; operators who need them open the repo, not the website. ([#13136](https://github.com/diegosouzapw/OmniRoute/pull/13136)) — thanks @HouMinXi +- **fix(combo):** parse numeric-epoch `rate_limited_until` in the combo cooldown read path ([#13141](https://github.com/diegosouzapw/OmniRoute/pull/13141)) — thanks @maxmad64bis +- **fix(opencode):** both OpenCode plugins now reject a gateway address typed without `http://` at configuration time, instead of publishing every model with an api url no client can call, and the v2 plugin no longer publishes a model card whose api url is blank or relative ([#13142](https://github.com/diegosouzapw/OmniRoute/pull/13142)) — thanks @maxmad64bis +- **fix(providers):** lock opencode model on upstream 400 model-unavailable ([#13146](https://github.com/diegosouzapw/OmniRoute/pull/13146)) — thanks @maxmad64bis +- **fix(logging):** keep the provider exchange rather than the raw client bodies when a call log exceeds its size budget, and show that recovered payload in the request-detail panel instead of replacing it with the stored response body ([#13147](https://github.com/diegosouzapw/OmniRoute/pull/13147)) — thanks @maxmad64bis +- **fix(traffic-inspector):** the WebSocket route no longer leaks a traffic-buffer subscriber and a 30s ping timer when the client socket is already closed at handler time — listeners are attached before any resource is acquired, a destroyed socket bails out early, and the ping interval stops on a dead socket where `write()` never throws ([#13155](https://github.com/diegosouzapw/OmniRoute/pull/13155)) — thanks @anhtahaylove +- **fix(telegram):** bound the per-user API key cache in the Telegram chat proxy so a burst of distinct chat ids can no longer grow the process heap without limit ([#13165](https://github.com/diegosouzapw/OmniRoute/issues/13165)) ([#13166](https://github.com/diegosouzapw/OmniRoute/pull/13166)) — thanks @anhtahaylove +- **fix(stream):** cancel the upstream response body when the JSON-to-SSE sniff unwinds on a body timeout, so a stalled upstream no longer pins the connection ([#13169](https://github.com/diegosouzapw/OmniRoute/issues/13169)) ([#13171](https://github.com/diegosouzapw/OmniRoute/pull/13171)) — thanks @anhtahaylove +- **fix(telegram):** authenticate webhook deliveries with Telegram's `secret_token` so an unauthenticated caller can no longer mint API keys or spend upstream quota ([#13172](https://github.com/diegosouzapw/OmniRoute/issues/13172)) ([#13175](https://github.com/diegosouzapw/OmniRoute/pull/13175)) — thanks @anhtahaylove +- **fix(models):** give discoverable Gemini 3.8 Flash ids their own 65536 output spec so Antigravity no longer clamps them to 16384 ([#13195](https://github.com/diegosouzapw/OmniRoute/pull/13195)) — thanks @HouMinXi +- **fix(dashboard):** leftover catalog-removed provider rows (gemini-cli) can be listed and purged from the providers page ([#13067](https://github.com/diegosouzapw/OmniRoute/issues/13067)) ([#13197](https://github.com/diegosouzapw/OmniRoute/pull/13197)) — thanks @HouMinXi +- **fix(auth):** a dashboard session now requires the `authenticated: true` claim that login, OIDC and the session refresh already emit — a JWT merely signed with `JWT_SECRET` (for example the Cursor CLI passthrough token, which any API-key holder can obtain) no longer verifies as the `auth_token` cookie on any route, the WebSocket handshake or the live server; existing sessions keep working ([#13298](https://github.com/diegosouzapw/OmniRoute/issues/13298)) ([#13375](https://github.com/diegosouzapw/OmniRoute/pull/13375)) +- **fix(api):** `DELETE /v1/batches/delete-completed` now applies the caller's API-key policy (endpoint allowlist, schedule, usage cap, rate limit) like every other `/v1` route, commits the key-scoped sweep per 200-batch chunk instead of holding one write lock for the whole sweep, refuses to loop without progress, and rejects a scope that names both a key and `allTenants` ([#13684](https://github.com/diegosouzapw/OmniRoute/pull/13684)) +- Restore the API-route typecheck gate: the non-streaming leg result lost its discriminated-union narrowing after the server-owned tool loop reassignment, producing 13 new TS2339 diagnostics in `chatCore.ts`. ([#12963](https://github.com/diegosouzapw/OmniRoute/pull/12963)) +- Restore four non-streaming behaviours the server-owned tool loop refactor dropped: client aborts map to 499 with the fixed `Request aborted` message, an aborted request no longer logs a synthetic `clientResponse`, Claude prompt-cache telemetry is recorded again, and a body that cannot be canonicalized no longer throws when the tool loop is off. ([#12990](https://github.com/diegosouzapw/OmniRoute/pull/12990)) +- **fix(skills):** The CLI registry parser now reads positionals declared with `.addArgument()`, not only those written inline in `.command()`. `tunnel create [type]` was being published as `tunnel create`, so the agent-skills sync gate reported drift on every branch and regenerating would have deleted the argument. ([#13009](https://github.com/diegosouzapw/OmniRoute/pull/13009)) — thanks @ntdatt812 +- **fix(combos):** dashboard combo test uses a short prompt, serial probes, and a 60s timeout so reasoning models and rate-limited free pools do not fail the health check ([#13001](https://github.com/diegosouzapw/OmniRoute/pull/13001)) — thanks @HouMinXi +- fix(compression): terminate idle worker threads on eviction so long-running instances stop leaking OS threads and MessagePorts ([#13091](https://github.com/diegosouzapw/OmniRoute/pull/13091)) — thanks @anhtahaylove +- Redact Google API keys of any length in error bodies: the pattern required exactly 39 characters, so shorter or longer `AIza…` credentials passed through unredacted. ([#12964](https://github.com/diegosouzapw/OmniRoute/pull/12964)) +- **fix(grok-cli):** a 402 "Grok Build usage balance exhausted" parks that Grok login as out of credit (Grok Build CLI, grok.com cookie, and xAI OAuth share the weekly pool). Combo routing then tries the next login instead of locking the model for every account in the pool ([#13061](https://github.com/diegosouzapw/OmniRoute/pull/13061)) — thanks @HouMinXi +- fix(compression): spawn the LLMLingua worker with a file URL object so compression actually runs instead of silently failing open on Node ([#13093](https://github.com/diegosouzapw/OmniRoute/pull/13093)) — thanks @anhtahaylove +- **fix(test):** run the local `test` and `test:unit` scripts at concurrency 4 so a full-suite run no longer exhausts the machine's commit charge and kills unrelated processes ([#13187](https://github.com/diegosouzapw/OmniRoute/pull/13187)) — thanks @anhtahaylove +- **fix(monitoring):** `GET /api/monitoring/health` `credentialHealth` now includes a bounded `failedConnections` list (`connectionId`, `status`, sanitized `lastError`) when the probe-cache gauge `failed>0`, plus `source: probe-cache` and a cheap `staleDbNonOkCount` for sticky SQLite `test_status` on active rows. Documents that the live gauge is not `provider_connections.test_status`. ([#12876](https://github.com/diegosouzapw/OmniRoute/pull/12876)) — thanks @RaviTharuma +- **fix(resilience):** an openai-compatible multi-upstream gateway no longer marks the whole connection `credits_exhausted` when a single upstream (or the `/models` / representative-model health probe) returns 402. Quota failures stay model-scoped; true connection-wide auth failures are unchanged. Single-credential 402 key disable ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239)) is preserved. ([#12875](https://github.com/diegosouzapw/OmniRoute/pull/12875)) — thanks @RaviTharuma +- **fix(dashboard):** nine fixes on the Orchestration Canvas, all diagnosed in the Phase 2 reviews. A source that fails now keeps the timestamp of its FIRST failure instead of being re-stamped every poll — the stale line said "since the last poll" no matter how long the source had been down, and the churn also defeated the snapshot's stable identity (it serializes the source list), so the canvas re-rendered on every tick while anything was broken. A source that HAD data and only then started failing is flagged too: previously only a source with no node at all got the warning, so a source that went down mid-session kept a healthy-looking node forever. The rest are pointwise: clicking a filter chip cancels the pending search debounce (left armed, it fired ~300ms later and silently reverted the chip); the search input carries an accessible name; `?state=running, failed` parses like the unpadded form instead of dropping the padded value; the CSV toggle helper is defined once in `model/urlParams.ts` rather than twice; the agents tab tells "nothing running" apart from "the filter matched nothing", offering a clear-filters button instead of setup links that would be wrong advice there; edges stop emitting SMIL particles above 40 simultaneously active edges, keeping the colored stroke; and the drawer's error banner clears when a retried action succeeds. ([#12983](https://github.com/diegosouzapw/OmniRoute/pull/12983)) +- **fix(i18n):** the nine locales added with the EU-language batch (Greek, Estonian, Irish, Croatian, Lithuanian, Latvian, Maltese, Slovenian, Serbian) were missing the eleven Orchestration Canvas keys that Phase 3 introduced, so the compare-runs panel and the "no runs match these filters" empty state fell back to English in those languages (`deepMergeFallback` substitutes English for an absent key, so nothing rendered blank — it rendered untranslated). The coverage gate does not catch this: it enforces an 80% floor per locale, and eleven missing keys out of ~13,000 leaves coverage at 99.9%. Translated for real in each language, calibrated against the wording each file already uses for "run", "filter" and "skill". ([#13294](https://github.com/diegosouzapw/OmniRoute/pull/13294)) +- **fix(dashboard):** five follow-ups from the Phase 2 Orchestration Canvas review. The History drawer now shows the memory section for runs that already left the live TTL window: the persisted `memory_hits` event is parsed into `metadata.memoryHits` with the same defensive validation the drawer applies, and — because that event is observability rather than a state transition — it no longer leaks into the timeline, where it had been inheriting the task's state and rendering as a duplicate transition. The A2A memory recall runs against its own 1.5s deadline instead of inheriting the memory backend's 30s one; overshooting degrades exactly like any other recall failure (no hits, task proceeds), and the timer is cleared on both paths. Repeating a Conductor run carries its `requirements.cli`/`requirements.model` forward, so the new run is pinned to the same runner profile and model rather than drifting to whatever the fleet picks. A successful repeat from the Agents tab now focuses the run it created, instead of leaving the operator on the finished one. And the auth test for `POST /api/conductor/tasks` moved into the shared `ROUTES` array rather than restating the pattern. ([#12988](https://github.com/diegosouzapw/OmniRoute/pull/12988)) +- fix(plugins): stop leaking an exit listener per plugin hook timeout, which triggered MaxListenersExceededWarning on plugins that ignore SIGTERM ([#13092](https://github.com/diegosouzapw/OmniRoute/pull/13092)) — thanks @anhtahaylove +- **fix(validation):** Provider node edits no longer fail with a generic "Invalid request" when the optional daily-quota reset fields are left blank. The dashboard sends `dailyQuotaResetTimezone` and `dailyQuotaResetHour` as `null`, and only the hour accepted it. ([#13066](https://github.com/diegosouzapw/OmniRoute/issues/13066)) ([#13083](https://github.com/diegosouzapw/OmniRoute/pull/13083)) — thanks @ntdatt812 +- **fix(combo):** quota-aware expansion drops banned, inactive, missing, and wrong-provider connections before quota fetch or model dispatch; pins and allowlists stay selectors, not a bypass. Antigravity automatic exhaustion now requires a reported zero remaining, so a positive balance below 1% stays eligible. ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- Fix an intermittent failure in the quota-weighted routing test suite: scores are a function of `Date.now()`, so peers with identical quota scored microseconds apart never tied and swapped order. ([#12946](https://github.com/diegosouzapw/OmniRoute/pull/12946)) +- fix(volcengine): resolve quota window mapping gaps, safe multi-connection console binding, and response cookie sanitization ([#12950](https://github.com/diegosouzapw/OmniRoute/pull/12950)) — thanks @HouMinXi +- **fix(test):** resolve the WebDAV handler path with `fileURLToPath` so the suite's 37 WebDAV tests run on Windows instead of failing with a doubled `C:\C:\` drive prefix ([#13196](https://github.com/diegosouzapw/OmniRoute/pull/13196)) — thanks @anhtahaylove +- **fix(ui):** fall back from invalid AnySearch icon alias (direct commit `9587e07b69`) +- **fix(sse):** stop rebuilding a truncated summary from the collector's cap-dropped event array ([#12718](https://github.com/diegosouzapw/OmniRoute/pull/12718)) — thanks @hartmark +- **fix(conversations):** show a pending spinner for unresolved tool nodes instead of "(empty)" ([#12727](https://github.com/diegosouzapw/OmniRoute/pull/12727)) — thanks @hartmark +- **fix(streaming):** fail fast when an upstream stream produces only lifecycle/heartbeat events, never real content ([#12741](https://github.com/diegosouzapw/OmniRoute/pull/12741)) — thanks @hartmark +- **fix(responses-continuation):** bridge the write-in-flight window with an in-memory pending store ([#12854](https://github.com/diegosouzapw/OmniRoute/pull/12854)) — thanks @hartmark +- **fix(translator):** drop or map agent_message on Chat Completions fallback ([#12880](https://github.com/diegosouzapw/OmniRoute/pull/12880)) — thanks @RaviTharuma +- **fix(api):** page and stream-complete GET /v1/models for large catalogs ([#12882](https://github.com/diegosouzapw/OmniRoute/pull/12882)) — thanks @RaviTharuma +- **fix(quota):** align AUTH window labels with usage API and clarify 503 ([#12884](https://github.com/diegosouzapw/OmniRoute/pull/12884)) — thanks @RaviTharuma +- **fix(ci):** clear two base-reds on release/v3.8.51 — mutation-coverage gate + image-only-model guard ([#12945](https://github.com/diegosouzapw/OmniRoute/pull/12945)) +- **fix(api):** scope batch bulk-delete to the calling API key ([#13211](https://github.com/diegosouzapw/OmniRoute/pull/13211)) +- **fix(tests):** align model-sync log assertion to shipped path redaction (base-red) ([#13227](https://github.com/diegosouzapw/OmniRoute/pull/13227)) +- **fix(test):** make the ToS heading guard actually require the parentheses ([#13228](https://github.com/diegosouzapw/OmniRoute/pull/13228)) +- **fix(quality):** register 4 drifted covering tests in stryker tap.testFiles ([#13229](https://github.com/diegosouzapw/OmniRoute/pull/13229)) +- **fix(logging):** mask *-api-key headers in request-log pipeline (#13273) ([#13401](https://github.com/diegosouzapw/OmniRoute/pull/13401)) — thanks @KooshaPari +- **fix(tests):** correct stale Jina catalog prefix in custom-models test ([#13403](https://github.com/diegosouzapw/OmniRoute/pull/13403)) — thanks @KooshaPari +- **fix(backend):** prune old backups after health-check-repair snapshots (#13308) ([#13404](https://github.com/diegosouzapw/OmniRoute/pull/13404)) — thanks @KooshaPari +- **fix(backend):** guard memory_fts_au trigger with WHEN clause to stop FTS bloat (#13326) ([#13405](https://github.com/diegosouzapw/OmniRoute/pull/13405)) — thanks @KooshaPari +- **fix(free-tiers):** remove obsolete Together $25 signup credit (#12526) ([#13407](https://github.com/diegosouzapw/OmniRoute/pull/13407)) — thanks @KooshaPari +- **fix:** skip non-chat generation models in Test all models ([#13409](https://github.com/diegosouzapw/OmniRoute/pull/13409)) — thanks @KooshaPari +- **fix(providers):** de-duplicate agy and antigravity model catalogs ([#13410](https://github.com/diegosouzapw/OmniRoute/pull/13410)) — thanks @KooshaPari +- **fix(dashboard):** send provider in body for mini-playground web search ([#13411](https://github.com/diegosouzapw/OmniRoute/pull/13411)) — thanks @KooshaPari +- **fix(resilience):** add CJK quota-exhaustion patterns to 429 classifier ([#13412](https://github.com/diegosouzapw/OmniRoute/pull/13412)) — thanks @KooshaPari +- **fix(backend):** eval runner: failed calls no longer score as passed + dotAll regex ([#13413](https://github.com/diegosouzapw/OmniRoute/pull/13413)) — thanks @KooshaPari +- **fix(backend):** eval runner opts out of output-style and memory injection ([#13414](https://github.com/diegosouzapw/OmniRoute/pull/13414)) — thanks @KooshaPari +- **fix:** chain serial quarantine step in npm test script (fixes #13286) ([#13418](https://github.com/diegosouzapw/OmniRoute/pull/13418)) — thanks @KooshaPari +- **fix(antigravity):** derive stable session ID from credentials for prompt-cache reuse (#13305) ([#13424](https://github.com/diegosouzapw/OmniRoute/pull/13424)) — thanks @KooshaPari +- **fix(memory):** merge memory into top-level system field instead of unshifting at messages[0] (#13425) ([#13427](https://github.com/diegosouzapw/OmniRoute/pull/13427)) — thanks @KooshaPari +- **fix(compression):** preserve instruction blocks from lossy engines ([#13523](https://github.com/diegosouzapw/OmniRoute/pull/13523)) — thanks @KooshaPari +- **fix(compression):** preserve polarity words and newlines in ultra heuristic ([#13524](https://github.com/diegosouzapw/OmniRoute/pull/13524)) — thanks @KooshaPari +- **fix(compression):** run user preservePatterns before built-in patterns (#13457) ([#13525](https://github.com/diegosouzapw/OmniRoute/pull/13525)) — thanks @KooshaPari +- **fix(reasoning):** expand deepseek-v4 regex to match v4.1+ and add command-code provider (#13430) ([#13528](https://github.com/diegosouzapw/OmniRoute/pull/13528)) — thanks @KooshaPari +- **fix(providers):** add alwaysReasons metadata for reka-flash-3 ([#13531](https://github.com/diegosouzapw/OmniRoute/pull/13531)) — thanks @KooshaPari +- **fix(playground):** allow Compare columns to scroll independently ([#13532](https://github.com/diegosouzapw/OmniRoute/pull/13532)) — thanks @KooshaPari +- **fix(security):** empty whitelist allows through to prevent admin lockout ([#13534](https://github.com/diegosouzapw/OmniRoute/pull/13534)) — thanks @KooshaPari +- **fix(tests):** add DISABLE_IOREG_STRATEGY env var for macOS test compatibility (fixes #13467) ([#13539](https://github.com/diegosouzapw/OmniRoute/pull/13539)) — thanks @KooshaPari +- **fix(zed-hosted):** map developer role and Gemini enums for Zed proxy ([#13543](https://github.com/diegosouzapw/OmniRoute/pull/13543)) — thanks @KooshaPari +- **fix(analytics):** normalize provider aliases to canonical id in usage_history ([#13545](https://github.com/diegosouzapw/OmniRoute/pull/13545)) — thanks @KooshaPari +- **fix(combo):** use traceId for call log id to prevent UNIQUE constraint failure ([#13546](https://github.com/diegosouzapw/OmniRoute/pull/13546)) — thanks @KooshaPari +- **fix(auth):** check model lockout before returning synthetic noauth connection ([#13547](https://github.com/diegosouzapw/OmniRoute/pull/13547)) — thanks @KooshaPari +- **fix(guardrails):** skip credential redaction for base64 image data URLs ([#13550](https://github.com/diegosouzapw/OmniRoute/pull/13550)) — thanks @KooshaPari +- **fix(resilience):** increase requestQueue.maxWaitMs default from 15s to 30s ([#13553](https://github.com/diegosouzapw/OmniRoute/pull/13553)) — thanks @KooshaPari +- **fix(docs):** document OMNIROUTE_READY_TIMEOUT_MS and allowlist the test-only DISABLE_IOREG_STRATEGY ([#13692](https://github.com/diegosouzapw/OmniRoute/pull/13692)) ### 📝 Maintenance @@ -853,6 +1054,39 @@ _By commits in `release/v3.8.50..release/v3.8.51` (`091589089c..d6f315018a`), au - **docs(security):** document socket.yml scanner config + CI workflow link (#12575) ([#12764](https://github.com/diegosouzapw/OmniRoute/pull/12764)) — thanks @KooshaPari - **chore(ci):** guard commit identity in pre-commit to stop author misattribution ([#12772](https://github.com/diegosouzapw/OmniRoute/pull/12772)) - **deps:** 17 Dependabot bumps — bump electron from 43.4.0 to 43.4.1 in /electron ([#11426](https://github.com/diegosouzapw/OmniRoute/pull/11426)); bump the development group across 1 directory with 8 updates ([#11428](https://github.com/diegosouzapw/OmniRoute/pull/11428)); bump the production group across 1 directory with 11 updates ([#11440](https://github.com/diegosouzapw/OmniRoute/pull/11440)); chore(deps): bump github/codeql-action/init from 4.37.7 to 4.37.8 ([#11925](https://github.com/diegosouzapw/OmniRoute/pull/11925)); chore(deps): bump github/codeql-action from 4.37.7 to 4.37.8 ([#11926](https://github.com/diegosouzapw/OmniRoute/pull/11926)); chore(deps): bump github/codeql-action/analyze from 4.37.7 to 4.37.8 ([#11927](https://github.com/diegosouzapw/OmniRoute/pull/11927)); build(deps): bump github/codeql-action/init from 4.37.8 to 4.37.9 ([#12345](https://github.com/diegosouzapw/OmniRoute/pull/12345)); build(deps): bump github/codeql-action/analyze from 4.37.8 to 4.37.9 ([#12346](https://github.com/diegosouzapw/OmniRoute/pull/12346)); bump the development group across 1 directory with 2 updates ([#12347](https://github.com/diegosouzapw/OmniRoute/pull/12347)); build(deps): bump github/codeql-action from 4.37.8 to 4.37.9 ([#12349](https://github.com/diegosouzapw/OmniRoute/pull/12349)); bump browserslist from 4.28.2 to 4.28.8 ([#12396](https://github.com/diegosouzapw/OmniRoute/pull/12396)); bump the production group across 1 directory with 4 updates ([#12399](https://github.com/diegosouzapw/OmniRoute/pull/12399)); bump @xmldom/xmldom ([#12500](https://github.com/diegosouzapw/OmniRoute/pull/12500)); bump qs from 6.15.2 to 6.16.0 ([#12512](https://github.com/diegosouzapw/OmniRoute/pull/12512)); bump @xmldom/xmldom from 0.9.10 to 0.9.12 ([#12513](https://github.com/diegosouzapw/OmniRoute/pull/12513)); bump fast-uri from 3.1.5 to 3.1.7 ([#12514](https://github.com/diegosouzapw/OmniRoute/pull/12514)); bump @humanfs/node from 0.16.7 to 0.16.8 ([#12515](https://github.com/diegosouzapw/OmniRoute/pull/12515)) +- Clear the `release/v3.8.51` `check:agent-skills-sync` base-red: regenerate `skills/cli-tunnel/SKILL.md` so the `tunnel create [type]` positional that #13009 taught the generator to read is reflected in the committed skill. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` typecheck base-red from #12731: the new `Mode pack ranking gates` candidates in `open-sse/services/autoCombo/__tests__/autoCombo.test.ts` omitted the required `provider`, `model` and `errorRate` fields of `ProviderCandidate`, failing `check:open-sse-typecheck` (and with it `Fast Quality Gates`) on every open PR. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` docs-sync base-red: EURouter (#13025) and GreenPT (#13024) took the live provider count to 358, leaving 7 STRICT drifts (`PROVIDER_REFERENCE.md`, 4 diagrams, 2 tier-flow images) plus the `AGENTS.md` / `llm.txt` / `package.json` count claims stale. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- **test(models):** Pin `CATALOG_BUILD_TIMEOUT_MS` in the #12058 canonical-catalog regression so a cold catalog build racing the 8s default bound no longer flakes the suite with a 500 ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Allow `@omniroute/opencode-plugin-v2/` in the npm pack policy — [#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870) shipped the v2 plugin beside its v1 sibling without widening the allowlist, so every packed file under it read as an unexpected artifact and failed `check:pack-policy` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Point the release-green validator's pack gate at the tree it is validating — the build-provenance guard defaulted to `origin/main`, which a release branch tip can never be an ancestor of mid-cycle, so `Package artifact` reported a false red on every run and the tarball boot-smoke cascaded off it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **docs(checks):** keep doc counts honest — headings, rankings, catalog, weights, quality gate and scoring diagram now covered ([#12507](https://github.com/diegosouzapw/OmniRoute/pull/12507)) — thanks @maxmad64bis +- **chore(lifecycle):** `check:model-lifecycle` now also diffs `DEFAULT_DEGRADATION_MAP` (the background-task redirect table) against the vendor lifecycle snapshot, refusing a retired id as source or target, with a table-driven unit test beside it. Three rows whose source the vendor had retired — `claude-sonnet-4-20250514`, `gemini-3-pro-preview` and `gpt-5.1-codex` (whose target `gpt-5.1-codex-mini` is retired too) — were dead code, since `checkLifecycle` answers 410 before the redirect runs; they are dropped (#12535 — thanks @pacocartones) +- **test(compression):** cover idle worker eviction at the resource level — the pool must call `terminate()` and must not retain the worker's `MessagePort`, complementing the `exit`-event assertion added with the fix ([#13371](https://github.com/diegosouzapw/OmniRoute/pull/13371)) +- **chore(quality):** raise the `imageGeneration.ts` file-size ceiling for the image-only-model guard that clears the #12945 base-red ([#13392](https://github.com/diegosouzapw/OmniRoute/pull/13392)) +- **chore(quality):** type the combo-test route's JSON response bodies instead of casting them to `any`, drop the now-empty suppression entry, and remove the `hasPerModelQuota` import `chatCore.ts` stopped using when the failure-classification helper was extracted ([#13379](https://github.com/diegosouzapw/OmniRoute/pull/13379)) +- **chore(electron):** the desktop bundle no longer ships the root-level files of every translated docs mirror (`README.md`, `llm.txt`, `CLAUDE.md`, `GEMINI.md`, `CONTRIBUTING.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`) — the packaged app only reads `docs/i18n//docs/**`, which stays. Saves ~11 MB on top of the translated CHANGELOGs already pruned. (#0000) ([#13235](https://github.com/diegosouzapw/OmniRoute/pull/13235)) +- **chore(quality):** retighten the `src/sse/handlers/chat.ts` unused-vars suppression count to the 8 violations that actually remain ([#13385](https://github.com/diegosouzapw/OmniRoute/pull/13385)) +- Bring the documented counts back in line with the code: 171 migrations (was 169) and 20 routing strategies (was 19), plus the regenerated `cli-tunnel` skill reference. ([#12970](https://github.com/diegosouzapw/OmniRoute/pull/12970)) +- Update the documented migration count to 172 after the call-logs provider-stats indexes landed. ([#13209](https://github.com/diegosouzapw/OmniRoute/pull/13209)) +- **chore(ci):** two gates that close the blind spots behind the exclusions above. `check:vitest-exclusions` requires every Vitest exclusion to name a tracking issue and to appear in `config/quality/vitest-exclusions.json` — the previous list grew to 62 files behind a comment pointing at an issue that had been closed for a month. `check-new-key-coverage` requires a key newly added to `en.json` to reach every locale; the existing coverage gate is a percentage floor per locale, so eleven absent keys out of ~13,000 left it at 99.9% while a whole feature shipped untranslated in nine languages. Both are diff-aware, so pre-existing debt stays frozen and neither needed a migration to turn on. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **build:** root layout no longer loads Inter from `next/font/google`, so a production image build does not need fonts.googleapis.com ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **docs(i18n):** bring the eight new `llm.txt` mirrors (km, kn, ml, my, ne, or, pa, si) up to the current provider and migration counts, so `check-docs-sync` stops rejecting every commit ([#13674](https://github.com/diegosouzapw/OmniRoute/pull/13674)) +- **refactor(ui):** flow surfaces (home topology, combo live studio, compression cockpit/waterfall/nodes, token-health badge) express state through the theme-aware `--orch-status-*` tokens instead of fixed dark-mode hex, so green/red/amber/grey stay legible in the light theme; dark mode is byte-identical. Categorical palettes (routing-strategy hues, compression-layer pills, provider brand colors) deliberately stay hex ([#12378](https://github.com/diegosouzapw/OmniRoute/issues/12378)) ([#13203](https://github.com/diegosouzapw/OmniRoute/pull/13203)) +- **chore(tests):** 51 test files that had been excluded from Vitest are running again, restoring roughly 350 assertions to the blocking suite. Each was measured individually first: of the 62 files parked behind the `// #8618 — pre-existing failure` comment, 51 pass against the current tree with no source change, so the exclusions had outlived the failures they were added for. The 11 that genuinely still fail stay excluded, but now point at a live tracker (#13204) rather than at #8618, which was closed in August while the list it tracked kept growing. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **chore(quality):** drop two ESLint suppression entries whose violations no longer exist, so `eslint --suppressions-location` stops rejecting every commit that touches the surrounding files ([#13384](https://github.com/diegosouzapw/OmniRoute/pull/13384)) +- **chore(release):** bring main's post-tag fixes into release/v3.8.51 — sync-back step 2/2 (direct commit `21c488f210`) +- **chore(deps):** patch toml and esbuild in the opencode plugin lockfiles ([#12965](https://github.com/diegosouzapw/OmniRoute/pull/12965)) +- **docs(changelog):** reconcile the v3.8.51 living section — fold 366 fragments, cover every cycle commit, credit every contributor ([#12971](https://github.com/diegosouzapw/OmniRoute/pull/12971)) +- **test(plugin):** wait for the v2 lazy-refresh tier instead of sleeping past it ([#12984](https://github.com/diegosouzapw/OmniRoute/pull/12984)) +- **chore(quality):** rebaseline chatCore.ts after the non-streaming regression fixes ([#13045](https://github.com/diegosouzapw/OmniRoute/pull/13045)) +- **docs(changelog):** add the missing bullet marker to the DeepSeek PoW fragment ([#13200](https://github.com/diegosouzapw/OmniRoute/pull/13200)) +- **chore(deps):** drain the Dependabot queue — 10 of 13 alerts ([#13213](https://github.com/diegosouzapw/OmniRoute/pull/13213)) +- **chore(ci):** make worktrees carry the hooks that enforce the identity gate ([#13236](https://github.com/diegosouzapw/OmniRoute/pull/13236)) +- **chore(deps):** pin csv-parse to ^7.0.2 via overrides (#13117) ([#13300](https://github.com/diegosouzapw/OmniRoute/pull/13300)) — thanks @KooshaPari +- **chore(deps):** bump hono from ^4.12.34 to ^4.13.7 (#13148) ([#13301](https://github.com/diegosouzapw/OmniRoute/pull/13301)) — thanks @KooshaPari +- **chore(deps):** pin joi to ^18.2.8 via overrides (#13085) ([#13302](https://github.com/diegosouzapw/OmniRoute/pull/13302)) — thanks @KooshaPari +- **docs(providers):** add Microsoft 365 Copilot (BizChat) provider guide (#12779) ([#13340](https://github.com/diegosouzapw/OmniRoute/pull/13340)) — thanks @KooshaPari ### 🙌 Contributors @@ -870,6 +1104,7 @@ Thanks to everyone whose work landed in v3.8.51: | [@amaleta](https://github.com/amaleta) | #11906, #11934 | | [@amartinawi](https://github.com/amartinawi) | #11575 | | [@AndrianBalanescu](https://github.com/AndrianBalanescu) | #11083, #11830, #11834, #11844 | +| [@anhtahaylove](https://github.com/anhtahaylove) | #13091, #13092, #13093, #13096, #13097, #13100, #13106, #13109, #13114, #13155, #13166, #13171, #13175, #13187, #13196 | | [@aniruddhaadak80](https://github.com/aniruddhaadak80) | #11760 | | [@app](https://github.com/app) | #12554 | | [@arminanton](https://github.com/arminanton) | #11461, #11513 | @@ -899,10 +1134,10 @@ Thanks to everyone whose work landed in v3.8.51: | [@gonisulaimann](https://github.com/gonisulaimann) | #12368, #12369, #12371 | | [@Gorillaz322](https://github.com/Gorillaz322) | #12207 | | [@groovecityJO](https://github.com/groovecityJO) | #12682 | -| [@hartmark](https://github.com/hartmark) | #11434, #11452, #11473, #11499, #11703, #11983, #11984, #11985, #11986, #11988, #11989, #11990, #11994, #12221, #12293, #12445, #12446, #12447, #12448, #12460, #12461, #12623, #12646, #12650, #12680 | +| [@hartmark](https://github.com/hartmark) | #11434, #11452, #11473, #11499, #11703, #11983, #11984, #11985, #11986, #11988, #11989, #11990, #11994, #12221, #12293, #12445, #12446, #12447, #12448, #12460, #12461, #12623, #12646, #12650, #12680, #12717, #12718, #12727, #12741, #12854 | | [@hizzt](https://github.com/hizzt) | #11894 | | [@hongnoul](https://github.com/hongnoul) | #11484 | -| [@HouMinXi](https://github.com/HouMinXi) | #11411, #11414, #11512, #11518, #11520, #11641, #11642, #11643, #11687, #11779, #11849, #11850, #11851, #11915, #11916, #11918, #11919, #11920, #12013, #12017, #12033, #12042, #12106, #12139, #12166, #12169, #12171, #12205, #12213, #12312, #12325, #12487, #12488, #12495, #12504, #12557, #12566, #12590, #12591, #12624, #12626, #12632, #12637, #12678, #12696, #12697, #12711, #12733, #12746, #12767, #12770, #12789, #12803, #12805, #12811, #12866, #12868, #12899, #12926, #12934 | +| [@HouMinXi](https://github.com/HouMinXi) | #11411, #11414, #11512, #11518, #11520, #11641, #11642, #11643, #11687, #11779, #11849, #11850, #11851, #11915, #11916, #11918, #11919, #11920, #12013, #12017, #12033, #12042, #12106, #12139, #12166, #12169, #12171, #12205, #12213, #12312, #12325, #12487, #12488, #12495, #12504, #12557, #12566, #12590, #12591, #12624, #12626, #12632, #12637, #12678, #12696, #12697, #12711, #12733, #12746, #12767, #12770, #12789, #12803, #12805, #12811, #12866, #12868, #12899, #12926, #12934, #12950, #12951, #12974, #13001, #13006, #13011, #13017, #13026, #13027, #13034, #13035, #13038, #13042, #13050, #13060, #13061, #13069, #13107, #13120, #13136, #13195, #13197 | | [@Hsia97](https://github.com/Hsia97) | #11624 | | [@jacobsparts](https://github.com/jacobsparts) | #11854, #12155, #12167 | | [@joglomedia](https://github.com/joglomedia) | #11980 | @@ -912,11 +1147,11 @@ Thanks to everyone whose work landed in v3.8.51: | [@KaspaPulse](https://github.com/KaspaPulse) | #11389, #11469, #11628, #11666, #11910 | | [@keeltrace](https://github.com/keeltrace) | #12080, #12223 | | [@killer30001000](https://github.com/killer30001000) | #12178 | -| [@KooshaPari](https://github.com/KooshaPari) | #12470, #12592, #12667, #12699, #12703, #12706, #12764, #12769, #12771 | +| [@KooshaPari](https://github.com/KooshaPari) | #12470, #12592, #12667, #12699, #12703, #12706, #12764, #12769, #12771, #13300, #13301, #13302, #13340, #13401, #13403, #13404, #13405, #13406, #13407, #13409, #13410, #13411, #13412, #13413, #13414, #13418, #13424, #13427, #13433, #13523, #13524, #13525, #13528, #13531, #13532, #13534, #13539, #13543, #13545, #13546, #13547, #13550, #13553 | | [@kriptoburak](https://github.com/kriptoburak) | #11370 | | [@KrzysiekSko](https://github.com/KrzysiekSko) | #12425, #12432, #12673 | | [@marcelokarval](https://github.com/marcelokarval) | direct commit / report | -| [@maxmad64bis](https://github.com/maxmad64bis) | #1622, #11435, #11437, #11441, #11537, #11550, #11553, #11555, #11557, #11812, #11842, #11843, #11903, #12151, #12214, #12215, #12218, #12226, #12314, #12316, #12317, #12318, #12319, #12320, #12321, #12870 | +| [@maxmad64bis](https://github.com/maxmad64bis) | #1622, #11435, #11437, #11441, #11537, #11550, #11553, #11555, #11557, #11812, #11842, #11843, #11903, #12151, #12214, #12215, #12218, #12226, #12314, #12316, #12317, #12318, #12319, #12320, #12321, #12507, #12715, #12731, #12744, #12786, #12787, #12788, #12790, #12792, #12794, #12795, #12828, #12832, #12853, #12857, #12870, #12937, #12941, #12975, #13141, #13142, #13146, #13147 | | [@mdigitalbh81](https://github.com/mdigitalbh81) | #12240 | | [@Meet6338-X](https://github.com/Meet6338-X) | #11598, #11609, #12110 | | [@morpheus9393](https://github.com/morpheus9393) | #11943 | @@ -925,11 +1160,11 @@ Thanks to everyone whose work landed in v3.8.51: | [@NightStalker-87](https://github.com/NightStalker-87) | #12183 | | [@NoxzRCW](https://github.com/NoxzRCW) | #11879, #11880, #11881, #11882, #11883, #11935 | | [@ntdat812](https://github.com/ntdat812) | #11585, #12095 | -| [@ntdatt812](https://github.com/ntdatt812) | #11368, #11573, #11574, #11576, #11577, #11580, #11582, #11583, #11588, #11589, #11590, #11591, #11592, #11593, #11672, #12177, #12180 | +| [@ntdatt812](https://github.com/ntdatt812) | #11368, #11573, #11574, #11576, #11577, #11580, #11582, #11583, #11588, #11589, #11590, #11591, #11592, #11593, #11672, #12177, #12180, #12873, #12918, #12920, #12921, #12925, #12930, #13007, #13009, #13024, #13025, #13083, #13087, #13101, #13104, #13110 | | [@official-burak](https://github.com/official-burak) | #11542 | | [@opensource-elearning](https://github.com/opensource-elearning) | #12179, #12189, #12278, #12286 | | [@oyi77](https://github.com/oyi77) | #11408, #11409, #11421, #11505, #11677, #12036, #12110 | -| [@pacocartones](https://github.com/pacocartones) | #11521, #11522, #11527, #11528, #11529, #11530, #11531, #11532, #11533, #11534, #11595, #11599, #11603, #11604, #11605, #11607, #11610, #11676, #11714, #11716, #11718, #11767, #11838, #11860, #11862, #11869, #11871, #11872, #11873, #11903, #11906, #11921, #11934, #12359, #12360, #12361, #12362, #12364, #12365, #12373, #12374, #12375, #12376, #12377, #12379, #12380, #12381, #12386, #12387, #12389, #12390, #12394, #12395, #12397, #12401, #12403, #12404 | +| [@pacocartones](https://github.com/pacocartones) | #11521, #11522, #11527, #11528, #11529, #11530, #11531, #11532, #11533, #11534, #11595, #11599, #11603, #11604, #11605, #11607, #11610, #11676, #11714, #11716, #11718, #11767, #11838, #11860, #11862, #11869, #11871, #11872, #11873, #11903, #11906, #11921, #11934, #12356, #12358, #12359, #12360, #12361, #12362, #12364, #12365, #12373, #12374, #12375, #12376, #12377, #12379, #12380, #12381, #12386, #12387, #12389, #12390, #12394, #12395, #12397, #12401, #12403, #12404, #12522, #12523, #12535, #12536, #12540, #12541, #12543, #12545, #12548, #12549, #12550, #12551, #12552, #12647, #12651, #12653, #13055 | | [@patrykkopycinski](https://github.com/patrykkopycinski) | #11936, #11937, #12224 | | [@PauloFH](https://github.com/PauloFH) | #11509 | | [@PauloHSOliveira](https://github.com/PauloHSOliveira) | #12241 | @@ -941,7 +1176,7 @@ Thanks to everyone whose work landed in v3.8.51: | [@raheemuddin786](https://github.com/raheemuddin786) | #11491, #11839, #11840, #11841, #12003, #12230, #12231, #12232, #12233, #12234 | | [@rahilmavani](https://github.com/rahilmavani) | #11761 | | [@Rahulsharma0810](https://github.com/Rahulsharma0810) | #11771 | -| [@RaviTharuma](https://github.com/RaviTharuma) | #11710, #11727, #11797, #11798, #11802, #11805, #11806, #11811, #12098, #12099, #12101, #12449, #12452, #12472, #12473, #12493, #12533, #12607, #12628, #12631, #12636 | +| [@RaviTharuma](https://github.com/RaviTharuma) | #11710, #11727, #11797, #11798, #11802, #11805, #11806, #11811, #12098, #12099, #12101, #12449, #12452, #12472, #12473, #12493, #12533, #12607, #12628, #12631, #12636, #12875, #12876, #12880, #12882, #12884 | | [@rezjalibd](https://github.com/rezjalibd) | #12186 | | [@RhianB14](https://github.com/RhianB14) | #11692 | | [@rifqiawl](https://github.com/rifqiawl) | #11517, #11519, #11969 | diff --git a/docs/i18n/pa/CHANGELOG.md b/docs/i18n/pa/CHANGELOG.md index 310fcffcc9..d6f11549a0 100644 --- a/docs/i18n/pa/CHANGELOG.md +++ b/docs/i18n/pa/CHANGELOG.md @@ -96,31 +96,31 @@ ## [3.8.51] — TBD -_Living section — reconciled 2026-09-07 from all cycle commits (`release/v3.8.50` tip `091589089c` → `release/v3.8.51` tip `d6f315018a`, 696 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._ +_Living section — reconciled 2026-09-15 from all cycle commits (`091589089c` → `c0f92ec98a`, 916 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 | **120** | -| 📝 Commits in the cycle | **696** | -| 🔀 Pull requests referenced | **672** | -| 📋 Changelog entries | **685** | -| 🙌 Contributors credited in entries | **118** | +| 👥 People who contributed | **123** | +| 📝 Commits in the cycle | **916** | +| 🔀 Pull requests referenced | **892** | +| 📋 Changelog entries | **919** | +| 🙌 Contributors credited in entries | **121** | | 🤖 Automated dependency commits | 18 | **Entries by type** | Type | Count | | --- | ---: | -| 🐛 Fixes | 403 | -| ✨ Features | 118 | -| 🧹 Chore | 72 | -| 📚 Docs | 36 | -| 🧪 Tests | 29 | +| 🐛 Fixes | 592 | +| ✨ Features | 131 | +| 🧹 Chore | 93 | +| 📚 Docs | 42 | +| 🧪 Tests | 32 | +| ♻️ Refactor | 8 | +| 🏗️ Build | 7 | | ⚡ Performance | 7 | -| ♻️ Refactor | 7 | -| 🏗️ Build | 6 | | ⚙️ CI | 2 | | ⏪ Reverts | 2 | | 📦 Dependencies | 2 | @@ -128,35 +128,35 @@ _Living section — reconciled 2026-09-07 from all cycle commits (`release/v3.8. ### 🏆 Top 25 contributors this cycle -_By commits in `release/v3.8.50..release/v3.8.51` (`091589089c..d6f315018a`), author identities consolidated via `.mailmap` and the merged PR's GitHub login. Bots excluded._ +_By commits in `091589089c..c0f92ec98a`, author identities consolidated via `.mailmap` and the merged PR's GitHub login. Bots excluded._ | # | Contributor | Commits | | ---: | --- | ---: | -| 🥇 | diegosouzapw | 240 | -| 🥈 | Paco Cartones (@pacocartones) | 58 | -| 🥉 | Bob.Hou (@HouMinXi) | 57 | -| 4 | Dizzle (@maxmad64bis) | 27 | -| 5 | Webman (@jonlwheat2-gif) | 22 | -| 6 | Ravi Tharuma (@RaviTharuma) | 21 | -| 7 | backryun | 17 | -| 8 | Markus Hartung (@hartmark) | 17 | -| 9 | Nguyen Thanh Dat (@ntdatt812) | 16 | -| 10 | Rafa Martins (@rafacpti23) | 10 | -| 11 | Syed Raheemuddin (@raheemuddin786) | 10 | -| 12 | Koosha Paridehpour (@KooshaPari) | 9 | -| 13 | Nguyễn Viết Tuấn (@TheDemonTuan) | 8 | -| 14 | MumuTW | 7 | -| 15 | Paijo (@oyi77) | 7 | -| 16 | Tobias Andersen (@turbolego) | 6 | -| 17 | Bl0ck (@Bl0ck154) | 5 | -| 18 | SHANMUGAPRIYAN (@geek007git) | 5 | -| 19 | KaspaPulse | 5 | -| 20 | Abhishek Sharma (@abhisheksharma2411) | 4 | -| 21 | Abhishek Divekar (@adivekar-utexas) | 4 | -| 22 | Andrew B. (@AndrianBalanescu) | 4 | -| 23 | NoxzRCW | 4 | -| 24 | opensource-elearning | 4 | -| 25 | Alvin T. Veroy (@alvinveroy) | 3 | +| 🥇 | diegosouzapw | 323 | +| 🥈 | Bob.Hou (@HouMinXi) | 79 | +| 🥉 | Paco Cartones (@pacocartones) | 77 | +| 4 | Dizzle (@maxmad64bis) | 49 | +| 5 | Koosha Paridehpour (@KooshaPari) | 43 | +| 6 | Nguyen Thanh Dat (@ntdatt812) | 31 | +| 7 | Ravi Tharuma (@RaviTharuma) | 26 | +| 8 | Markus Hartung (@hartmark) | 22 | +| 9 | Webman (@jonlwheat2-gif) | 22 | +| 10 | backryun | 17 | +| 11 | anhtahaylove | 15 | +| 12 | Rafa Martins (@rafacpti23) | 10 | +| 13 | Syed Raheemuddin (@raheemuddin786) | 10 | +| 14 | Nguyễn Viết Tuấn (@TheDemonTuan) | 8 | +| 15 | MumuTW | 7 | +| 16 | Paijo (@oyi77) | 7 | +| 17 | Tobias Andersen (@turbolego) | 6 | +| 18 | Bl0ck (@Bl0ck154) | 5 | +| 19 | SHANMUGAPRIYAN (@geek007git) | 5 | +| 20 | KaspaPulse | 5 | +| 21 | Abhishek Sharma (@abhisheksharma2411) | 4 | +| 22 | Abhishek Divekar (@adivekar-utexas) | 4 | +| 23 | Andrew B. (@AndrianBalanescu) | 4 | +| 24 | NoxzRCW | 4 | +| 25 | opensource-elearning | 4 | ### ✨ New Features @@ -282,6 +282,19 @@ _By commits in `release/v3.8.50..release/v3.8.51` (`091589089c..d6f315018a`), au - **feat(dashboard):** link a log entry's Conversation Context to its owning conversation ([#12646](https://github.com/diegosouzapw/OmniRoute/pull/12646)) — thanks @hartmark - **feat(catalog):** eligibility-gated free-tier bucket ([#12669](https://github.com/diegosouzapw/OmniRoute/pull/12669)) - **feat(db):** fail-closed previous_response_id continuation for redacted video turns (#12150 P2b) ([#12707](https://github.com/diegosouzapw/OmniRoute/pull/12707)) +- **feat(gamification): pay the documented `streak_bonus` and `badge_unlock` XP rewards.** `XP_REWARDS` listed both rewards but the award pipeline never paid them: the private reward table in `events.ts` omitted them, `updateStreak()` did not report when a streak extended, and badge unlocks carried no XP. Every request that extends a daily streak now pays `streak_bonus × streak length` once per UTC day (guarded by a same-day `xp_audit_log` check), and every badge unlocked through the pipeline pays `badge_unlock` once per badge (guarded by the `user_badges` primary key; `unlockBadge()` now reports whether it inserted). Bonus XP flows through the same `addXp` + level sync + global/weekly/monthly leaderboard path as action XP, so level-ups and rankings include it. The Radar supporter recognition unlock stays XP-free. (#12522 — thanks @pacocartones) +- **feat(providers):** advertise a `free-tier` capability in the provider plugin manifest for every provider with documented free models, so sidecars and dashboards can filter free-capable providers without reading the quota catalog ([#12786](https://github.com/diegosouzapw/OmniRoute/pull/12786)) — thanks @maxmad64bis +- **feat(providers):** Added EURouter as an OpenAI-compatible API-key gateway (`https://api.eurouter.ai/v1`), with live model discovery via `passthroughModels`. Its copy states that models are served by third-party upstreams listed per model, so an EU-based router is not read as EU data residency for inference. ([#13025](https://github.com/diegosouzapw/OmniRoute/pull/13025)) — thanks @ntdatt812 +- **feat(providers):** Added GreenPT as an OpenAI-compatible API-key provider (`https://api.greenpt.ai/v1`), with live model discovery via `passthroughModels`. No free-inference badge: the published docs describe a free API subscription billed per token, not a free tier. ([#13024](https://github.com/diegosouzapw/OmniRoute/pull/13024)) — thanks @ntdatt812 +- **feat(i18n):** 9 new locales — Greek (`el`), Croatian (`hr`), Serbian (`sr`), Lithuanian (`lt`), Estonian (`et`), Latvian (`lv`), Slovenian (`sl`), Maltese (`mt`), Irish (`ga`) — across the dashboard, docs mirrors, CLI, README and the site; OmniRoute now ships every official EU language (51 locales). (#13044) +- **feat(i18n):** 8 new locales — Kannada (`kn`), Malayalam (`ml`), Odia (`or`), Punjabi (`pa`), Nepali (`ne`), Sinhala (`si`), Burmese (`my`), Khmer (`km`) — across the dashboard, docs mirrors, CLI, README and the site (59 locales). The translator now restores the ICU literal escape around angle placeholders and splits oversized docs sections before translating. (#0000) ([#13660](https://github.com/diegosouzapw/OmniRoute/pull/13660)) +- feat(providers): **list Agnes 3.0 Flash as the current free chat model, drop retired 1.5 Flash, add Image 2.0/2.5 Flash plus Video 2.5/2.5 Flash, and discover the live `/v1/models` catalog (including the CN host `api.agnes-ai.cn`).** `agnes-1.5-flash` now forwards to `agnes-3.0-flash`. Video 2.5 polls `GET /v1/videos/{id}` (not the V2.0 `/agnesapi` contract). Live `/v1/models` (2026-09-09) no longer serves 1.5; the wiki marks it deprecated. 3.0 Flash is 512K context / 65,536 max output, same window as 2.5. CN-region keys use the existing per-connection base-URL field, default stays `apihub.agnes-ai.com`. ([#13120](https://github.com/diegosouzapw/OmniRoute/pull/13120)) — thanks @HouMinXi +- **feat(sse):** Codex and OpenAI catalogs list GPT-6 Astra with effort aliases (`-low` through `-ultra`); Codex CLI identity pins `@openai/codex@0.153.4` in lockstep with the image ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **feat(dashboard):** the orchestration History tab gained a "Compare runs" mode — toggling it turns each grid cell into a 2-item selection queue (a 3rd click drops the oldest pick), and picking two cells opens a side-by-side comparison panel instead of the usual detail drawer. The panel fetches both runs' detail the same way the drawer does (falling back to persisted history once a run leaves the live TTL window) and shows, per side: identity/source/state, start time, a signed `right - left` delta for duration/cost/event count, the event timeline aligned by index, and any memory hits. One side's fetch failing never blocks the other, and a delta is only ever computed when both sides have a finite value — otherwise it renders "—", never `NaN`. Comparing two runs from different sources or skills still works; a banner marks the deltas as informational rather than hiding them, since the two runs aren't a strict apples-to-apples pair. ([#12677](https://github.com/diegosouzapw/OmniRoute/pull/12677)) +- **feat(dashboard):** badge a conversation that never reached a clean stop ([#12717](https://github.com/diegosouzapw/OmniRoute/pull/12717)) — thanks @hartmark +- **feat(release):** reconcile-changelog tool + version-anchored fragment aggregation ([#12987](https://github.com/diegosouzapw/OmniRoute/pull/12987)) +- **feat(mcp):** carry context_length through MCP list_models_catalog projection (#12776) ([#13406](https://github.com/diegosouzapw/OmniRoute/pull/13406)) — thanks @KooshaPari +- **feat(cli):** make startup readiness budget configurable (#13369) ([#13433](https://github.com/diegosouzapw/OmniRoute/pull/13433)) — thanks @KooshaPari ### 🐛 Bug Fixes @@ -698,6 +711,194 @@ _By commits in `release/v3.8.50..release/v3.8.51` (`091589089c..d6f315018a`), au - **fix(i18n):** quote placeholder in OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES description (#12505) ([#12769](https://github.com/diegosouzapw/OmniRoute/pull/12769)) — thanks @KooshaPari - **fix(api/pricing):** surface validation error message as string, not raw object (#12494) ([#12771](https://github.com/diegosouzapw/OmniRoute/pull/12771)) — thanks @KooshaPari - **fix(sse):** re-enable prompt compression for native Codex passthrough ([#12834](https://github.com/diegosouzapw/OmniRoute/pull/12834)) — thanks @thomasmaerz +- **fix(dashboard):** model health tests for a provider node set to the Responses API now call `/v1/responses` with a Responses-shaped body instead of `/v1/chat/completions` — those models were reported as `Provider returned HTTP 200 but no text content` even though the same model answered normally through `/v1/responses` ([#13070](https://github.com/diegosouzapw/OmniRoute/issues/13070)) ([#13087](https://github.com/diegosouzapw/OmniRoute/pull/13087)) — thanks @ntdatt812 +- fix(routing): stop a round-robin combo's "opencode" targets from collapsing onto the opencode-zen connection (#11912) ([#13283](https://github.com/diegosouzapw/OmniRoute/pull/13283)) +- fix(routing): stop the reactive-compaction debug log from lying when compression is globally disabled (#11977) ([#13276](https://github.com/diegosouzapw/OmniRoute/pull/13276)) +- fix(electron): relativize standalone-bundle symlink targets so Stage 8 manifest verification stops failing on Windows (#11979) ([#13251](https://github.com/diegosouzapw/OmniRoute/pull/13251)) +- fix(dashboard): surface a visible error when Compression Studio's combined preview run fails (#12061) ([#13288](https://github.com/diegosouzapw/OmniRoute/pull/13288)) +- fix(dashboard): make the compression "Effective pipeline" preview honor the active profile and warn when the master switch is off (#12063) ([#13287](https://github.com/diegosouzapw/OmniRoute/pull/13287)) +- fix(providers): scope TinyCMS Web signer's DOM shims to each call instead of leaking them for the process lifetime, and surface a clean HTTP status on a non-JSON interception-toggles error (#12072) ([#13254](https://github.com/diegosouzapw/OmniRoute/pull/13254)) +- fix(guardrails): stop Vision Bridge from re-selecting a model locked after a 404 (#12111) ([#13259](https://github.com/diegosouzapw/OmniRoute/pull/13259)) +- fix(sse): require Responses-shaped body before native OpenAI-compatible passthrough (#12129) ([#13278](https://github.com/diegosouzapw/OmniRoute/pull/13278)) +- fix(providers): minimax-m3 now collapses manual thinking.type:"enabled" to adaptive, preventing upstream 400 (2013) (#12132) ([#13249](https://github.com/diegosouzapw/OmniRoute/pull/13249)) +- fix(db): scope model visibility overrides by modality so hiding a Chat model no longer hides an identically-ID'd Image/Embeddings/etc. model (#12172) ([#13258](https://github.com/diegosouzapw/OmniRoute/pull/13258)) +- fix(db): scope local-provider apiKey dedup to matching base URL so LM Studio/Ollama-style connections support multiple accounts (#12173) ([#13250](https://github.com/diegosouzapw/OmniRoute/pull/13250)) +- fix(providers): refresh Trae's stale Referer/Origin and forward user timezone so imported connections stop failing with 401 (#12190) ([#13255](https://github.com/diegosouzapw/OmniRoute/pull/13255)) +- fix(providers): route opencode-go/gpt-5.6-luna to /responses instead of /chat/completions (#12196) ([#13275](https://github.com/diegosouzapw/OmniRoute/pull/13275)) +- fix(dashboard): allow deleting the last extra-upstream-header row even when invalid (#12251) ([#13252](https://github.com/diegosouzapw/OmniRoute/pull/13252)) +- **fix(i18n):** translate pre-existing `__MISSING__:` keys for `combo.sort`, `requestLogger.detail` expand/collapse, `common.profile`, and `settings.resilienceCredentialHealth*` across 39 locales ([#12272](https://github.com/diegosouzapw/OmniRoute/issues/12272)) ([#13034](https://github.com/diegosouzapw/OmniRoute/pull/13034)) — thanks @HouMinXi +- fix(cli): run the Node.js runtime compatibility guard before the heavy `tsx/esm` + Commander import chain so an unsupported runtime gets a clear message instead of a raw `Invalid regular expression flags` crash (#12296) ([#13253](https://github.com/diegosouzapw/OmniRoute/pull/13253)) +- fix(dashboard): refresh the providers list after deleting a compatible provider node (#12298) ([#13256](https://github.com/diegosouzapw/OmniRoute/pull/13256)) +- fix(usage): fail closed on API-key budget enforcement when a provider's `auto` routing alias has no pricing row, instead of silently counting it as $0 (#12341) ([#13257](https://github.com/diegosouzapw/OmniRoute/pull/13257)) +- **fix(providers):** include the submitted Agnes video model when polling by `video_id` ([#12356](https://github.com/diegosouzapw/OmniRoute/pull/12356)) — thanks @pacocartones +- **fix(routing):** custom OpenAI-compatible nodes now honor the saved Chat/Responses API type after edits instead of letting the node's original ID prefix override the live connection setting ([#11884](https://github.com/diegosouzapw/OmniRoute/issues/11884)). ([#12358](https://github.com/diegosouzapw/OmniRoute/pull/12358)) — thanks @pacocartones +- fix(sse): surface an error instead of a silent empty 200 when a Claude stream closes with zero bytes (#12398) ([#13285](https://github.com/diegosouzapw/OmniRoute/pull/13285)) +- fix(oauth): warn before the dead localhost:8080 redirect in antigravity/gemini `oauth start` (#12413) ([#13265](https://github.com/diegosouzapw/OmniRoute/pull/13265)) +- fix(providers): stop devin-cli spawn error from double-closing the SSE controller (#12517) ([#13261](https://github.com/diegosouzapw/OmniRoute/pull/13261)) +- **fix(rerank):** clamp Voyage `top_k` to the documents actually sent after empty-string filtering, and honor `return_documents: false` in the NVIDIA response adapter (#12523 — thanks @pacocartones) +- **fix(audio):** `/v1/audio/translations` now resolves combo names the way `/v1/audio/transcriptions` already does, so a combo that `GET /v1/models` advertises is fanned out to its targets instead of being rejected with `400 Invalid translation model: . Use format: provider/model`; literal `provider/model` ids and unknown bare names behave as before (#12536 — thanks @pacocartones) +- **fix(gemini):** strip the JSON-Schema-2020-12 `prefixItems` keyword from Gemini tool schemas at every nesting level, so Claude Code tool definitions no longer fail with `400 Unknown name "prefixItems"` on Gemini models (#12540 — thanks @pacocartones) +- **fix(api-manager):** Expose an accessible loading status while API keys are fetched instead of an empty accessibility tree (#12541 — thanks @pacocartones) +- **fix(video):** Clamp `estimateJpegFrameBytes` at zero for padding-only payloads and build the three encode-side frame data URIs from `JPEG_FRAME_DATA_URI_PREFIX` instead of a repeated literal (#12543 — thanks @pacocartones) +- **fix(devin):** accept Windows `DEVIN_AGENTIC_HOME` sandbox paths (`C:\...\.sandbox\...`) in the isolated-home check so the Devin Claude Bridge no longer fails closed on Windows ([#12405](https://github.com/diegosouzapw/OmniRoute/issues/12405)) (#12545 — thanks @pacocartones) +- **fix(db):** Add `conversation_turn_nodes` and orphaned `agentic_conversations` to the auto-cleanup cycle under the existing `retention.callLogs` window, so identity nodes whose call-log content has already been purged no longer accumulate without bound in `storage.sqlite` (#12548 — thanks @pacocartones) +- **fix(i18n):** Wrap the `~/.claude/profiles//settings.json` placeholder in ICU single quotes in the `OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES` feature-flag description across all 42 locales and the TypeScript default, so next-intl no longer fails with `INVALID_MESSAGE: UNCLOSED_TAG` and the Feature Flags card shows the description instead of the raw key (#12549 — thanks @pacocartones) +- **fix(orchestration):** `updateCloudAgentTask` now publishes the task's real `status` on `agent.task.updated` when an update only touches `result`, `activities` or `error`, instead of the fabricated `"updated"` state, and stays silent when no row matched the id (#12550 — thanks @pacocartones) +- **fix(i18n):** the home "Recent Requests" panel and the Provider Topology legend are now translated instead of rendering English copies on non-English dashboards; the legend reads its own `home.topologyLegend*` labels with consistent casing rather than borrowing the memory-settings "Recent" and analytics "Error" strings (#12551 — thanks @pacocartones). +- **docs(reference):** bring the `FEATURE_FLAGS.md` catalog back to 1:1 with `featureFlagDefinitions.ts` — 20 missing flags added, the two `*_BLOCK_THRESHOLD` env-only knobs moved out of the flag tables, category/total counts and the Live WS port corrected, guarded by a static test (#12552 — thanks @pacocartones) +- **fix(i18n):** backfill missing `usage.kiloPass*` strings in 39 locales and restore `featureFlagOmnirouteDisableThinkingLevelVariantsDescription` in `pt.json` ([#12561](https://github.com/diegosouzapw/OmniRoute/issues/12561)) ([#13027](https://github.com/diegosouzapw/OmniRoute/pull/13027)) — thanks @HouMinXi +- fix(docker): default docker-compose app ports (dashboard/API/live-WS) to loopback instead of `0.0.0.0`, closing the anonymous `/v1` LAN/WAN exposure gap left open by `REQUIRE_API_KEY=false` (#12568) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): close DNS-rebinding SSRF gap in webhook outbound-URL guard (#12569) ([#13243](https://github.com/diegosouzapw/OmniRoute/pull/13243)) +- fix(docker): require a per-session token on the VNC browser CDP bridge and isolate it on a dedicated Docker network (#12571) ([#13241](https://github.com/diegosouzapw/OmniRoute/pull/13241)) +- fix(open-sse): write Adobe Firefly session tokens and cookie jars with 0700/0600 permissions instead of the process umask (#12572) ([#13242](https://github.com/diegosouzapw/OmniRoute/pull/13242)) +- fix(api): remove hardcoded wildcard CORS in openai-to-gemini-sse.ts so the centralized fail-closed CORS gate is the sole source of `Access-Control-Allow-Origin` (#12573) ([#13239](https://github.com/diegosouzapw/OmniRoute/pull/13239)) +- fix(api): enforce API key policy (budget/rate-limit/schedule/endpoint scoping) on the ElevenLabs speech-to-text, text-to-speech and voices proxy routes (#12574) ([#13244](https://github.com/diegosouzapw/OmniRoute/pull/13244)) +- fix(sse): cap HuggingChat NDJSON body size and bound the read loop with the fetch timeout so a stalled or hostile upstream cannot buffer unbounded memory (#12577) ([#13282](https://github.com/diegosouzapw/OmniRoute/pull/13282)) +- fix(docker): scope the cliproxyapi/qdrant/bifrost sidecars to loopback by default and forward `CLIPROXYAPI_MANAGEMENT_KEY` into the cliproxyapi container so its management API is not left both unauthenticated and LAN-published (#12578) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): create DB export temp paths with `fs.mkdtempSync` instead of predictable timestamps (#12579) ([#13237](https://github.com/diegosouzapw/OmniRoute/pull/13237)) +- Cline 401 bodies that say "re-authenticate your Cline account" classify as a refreshable OAuth token, not a terminal expired key. The cooling panel no longer labels every cooldown as a 429; it shows the recorded last error instead. (#12594) ([#13060](https://github.com/diegosouzapw/OmniRoute/pull/13060)) — thanks @HouMinXi +- **fix(catalog):** degrade unknown combo targets instead of dropping LCD modalities, and persist OpenRouter `architecture.input_modalities` into the capability snapshot ([#12613](https://github.com/diegosouzapw/OmniRoute/issues/12613)) ([#13042](https://github.com/diegosouzapw/OmniRoute/pull/13042)) — thanks @HouMinXi +- fix(providers): send `x-api-key` instead of `Authorization: Bearer` for OpenCode Zen's `/v1/responses` endpoint (Muse Spark Contributor models), fixing a 401 on OmniRoute's auth header (#12633) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(sse): surface the actionable "Auggie CLI not found" message when the shell reports a missing `auggie` binary via exit code instead of a spawn error (#12645) ([#13263](https://github.com/diegosouzapw/OmniRoute/pull/13263)) +- **fix(config):** Persist deletions of built-in background-degradation entries — when a stored settings record exists its `degradationMap` is now authoritative instead of being merged under the defaults, so an entry the user removed in the dashboard no longer reappears on the next apply or restart ([#12424](https://github.com/diegosouzapw/OmniRoute/issues/12424)) ([#12647](https://github.com/diegosouzapw/OmniRoute/pull/12647)) — thanks @pacocartones +- **fix(gamification):** action-count badge milestones (First Token, Token Consumer, Token Machine, Token Whale, and the token-sharing tier) are now backed by a durable `xp_action_counts` counter incremented in `addXp()`, instead of a live `COUNT(*)` over `xp_audit_log`. The audit log is pruned by `retention.xpAuditLog` (default 30 days), so on a default install those "lifetime" milestones were really "actions in the last 30 days" and unlocked badges could stop unlocking once old rows aged out. `getActionCount()` and `checkActionCountBadges()` now read the same durable source, and a migration backfills existing totals from the surviving audit rows ([#12546](https://github.com/diegosouzapw/OmniRoute/issues/12546)) ([#12651](https://github.com/diegosouzapw/OmniRoute/pull/12651)) — thanks @pacocartones +- **fix(images):** `/v1/images/edits` now iterates a combo's targets the same way `/v1/images/generations` does (#9239) instead of flattening a bare combo to its first target. A combo whose first target is not edit-capable — or lacks credentials — now falls through to a later edit-capable target rather than hard-erroring, and missing credentials are skipped (not a hard `401`) to match the generations path. The per-target skip/terminal classification is extracted into a shared `runImageComboTargets` loop, so generations behavior is unchanged ([#12547](https://github.com/diegosouzapw/OmniRoute/issues/12547)). ([#12653](https://github.com/diegosouzapw/OmniRoute/pull/12653)) — thanks @pacocartones +- fix(sse): add first-byte watchdog to the TLS-fingerprint transport so a stalled wreq body falls back instead of hanging for minutes (#12656) ([#13272](https://github.com/diegosouzapw/OmniRoute/pull/13272)) +- fix(sse): exempt tiny-budget reasoning probes from combo quality failure and surface persisted-cooldown skips in ALL_TARGETS_SKIPPED diagnostics (#12659) ([#13269](https://github.com/diegosouzapw/OmniRoute/pull/13269)) +- fix(models): declare the real ~1M contextLength for OpenCode Zen's Muse Spark 1.2 models instead of falling back to the 200000 provider default (#12681) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(oauth): align codebuddy-cn OAuth User-Agent with the chat/usage CLI version to avoid WAF false positives (#12702) ([#13264](https://github.com/diegosouzapw/OmniRoute/pull/13264)) +- fix(dashboard): surface an authentication-required banner instead of silently blanking database settings for a guest session (#12709) ([#13270](https://github.com/diegosouzapw/OmniRoute/pull/13270)) +- **fix(resilience):** stop unbounded queue that hangs 6min until Aborted — gate, provider slot, and Bottleneck queue now share a per-connection `maxWaitMs` budget; `fail-closed` on exhaust (503); execution backstop `executionMaxWaitMs` overridable per connection with upstream clamp ([#12715](https://github.com/diegosouzapw/OmniRoute/pull/12715)) — thanks @maxmad64bis (with thanks to @Tushar49 for surfacing the slow-provider need in #12635) +- **fix(auto-combo):** every mode pack now carries `quality` and `reliability` weights (`quality` 0.02, 0.03 in `quality-first`; `reliability` 0.03, 0.04 in `reliability-first`), so selecting a pack no longer silences either signal; `DEFAULT_WEIGHTS` is unchanged ([#12731](https://github.com/diegosouzapw/OmniRoute/pull/12731)) — thanks @maxmad64bis +- **fix(sse):** the provider execution pipeline keeps the upstream error `code`/`type` again — both were lost when the non-streaming leg was extracted, so a config-class `422` (Antigravity missing project) degraded into a generic account cooldown instead of reaching the gate that recognises it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(db):** a legacy `call_logs` table no longer aborts startup — the provider-stats index is created after the column healing runs, not before it, so an install predating `request_type` opens instead of failing with `no such column` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(catalog):** a malformed row in the operator-writable custom-models blob no longer kills every `auto/*` pool ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(tests):** realign the guards that the same day's merges left asserting the old behavior — the provider-count total after GreenPT and EURouter, the GLM stream buffer slot that `#12925` turned into a declared parameter, and the injection scan bound that `#13104` reshaped from a head-only window into head-plus-tail ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- fix(cache): fold tool_choice/tools/response_format into the semantic cache signature so a cached tool_calls response can no longer be replayed for a request whose tool policy forbids it (#12734) ([#13267](https://github.com/diegosouzapw/OmniRoute/pull/13267)) +- **fix(models):** stop treating provider-supplied `isFree:true`/`:free`/`0/0` flags as trusted unless the provider has a documented free tier — only the shipped free-tier catalog decides otherwise; fetched `isFree:true` trusted only for a free-tier provider, custom `isFree:true` via a trusted path ([#12744](https://github.com/diegosouzapw/OmniRoute/pull/12744)) — thanks @maxmad64bis +- fix(memory): authenticate the internal /v1/rerank loopback call so memory reranking no longer silently degrades to unranked order when REQUIRE_API_KEY=true (#12745) ([#13268](https://github.com/diegosouzapw/OmniRoute/pull/13268)) +- fix(sse): parse Ollama Cloud's current usage markup (`$X of $Y used` aria-label, nested width style) (#12749) ([#13260](https://github.com/diegosouzapw/OmniRoute/pull/13260)) +- fix(cli): setup-opencode no longer sends an active context's management token to `/v1/models` when `--api-key`/`OMNIROUTE_API_KEY` is supplied — an explicit flag or the env var now always outranks the context's token, and the flag itself is no longer swallowed by the parent program's global `--api-key` option (#12783) ([#13246](https://github.com/diegosouzapw/OmniRoute/pull/13246)) +- fix(sse): register Arcee AI in the executor provider registry so requests reach api.arcee.ai instead of silently falling back to OpenAI (#12784) ([#13277](https://github.com/diegosouzapw/OmniRoute/pull/13277)) +- **fix(quota):** share one upstream quota read when identical saturation checks arrive at the same time, so traffic bursts don't multiply provider API calls ([#12787](https://github.com/diegosouzapw/OmniRoute/pull/12787)) — thanks @maxmad64bis +- **fix(db):** ignore expired or invalid rate-limit cooldown writes so a stale timestamp can't lock a connection that should be usable — clearing still works as before ([#12788](https://github.com/diegosouzapw/OmniRoute/pull/12788)) — thanks @maxmad64bis +- **fix(providers):** single source for provider order with `xao` ranking alongside `xai-oauth` ([#12790](https://github.com/diegosouzapw/OmniRoute/pull/12790)) — thanks @maxmad64bis +- **fix(routing):** off-table models check the free-model catalog before inheriting premium prices, latency bootstraps from the observed pool median, async tiers read live database pricing with a 90-day freshness gate, and the tier cache invalidates on every pricing write ([#12792](https://github.com/diegosouzapw/OmniRoute/pull/12792)) — thanks @maxmad64bis +- **fix(auto-combo):** snapshot scoring uses the observed per-connection account tier and per-model quality instead of neutral constants, and circuit-open providers rank lower at build time ([#12794](https://github.com/diegosouzapw/OmniRoute/pull/12794)) — thanks @maxmad64bis +- **fix(pool):** empty `auto/*` pools now say why they are empty, and the models listing uses the same paid check as routing ([#12795](https://github.com/diegosouzapw/OmniRoute/pull/12795)) — thanks @maxmad64bis +- fix(routing): recognize CLIProxyAPI's 'unknown provider for model' 400 as fallback-worthy (#12800) ([#13284](https://github.com/diegosouzapw/OmniRoute/pull/13284)) +- **fix(sse):** translate-mode streams now emit the estimated token counts as a trailing usage-only chunk before `[DONE]` when the upstream stays silent, so metered chat clients see totals instead of nothing ([#12828](https://github.com/diegosouzapw/OmniRoute/pull/12828)) — thanks @maxmad64bis +- **fix(db):** provider stats stay truthful on empty and legacy databases: fallback counts default to `0` instead of `null`, latency averages read `null` (not `0`) when no durations were recorded, failures logged before the error-type column existed group under `pre_migration` instead of `unclassified`, and per-provider queries use two new composite indexes ([#12832](https://github.com/diegosouzapw/OmniRoute/pull/12832)) — thanks @maxmad64bis +- fix(nvidia): fail open when a synced model catalog goes stale instead of gating forever (#12849) ([#13248](https://github.com/diegosouzapw/OmniRoute/pull/13248)) +- **fix(db):** WAL maintenance lives in its own module: a `TRUNCATE` checkpoint that hits a busy database now warns with its streak and retries once via `PASSIVE` instead of logging success, and closing-time checkpoints no longer report success on builds without a database file, with the busy totals visible in the authenticated monitoring health payload ([#12853](https://github.com/diegosouzapw/OmniRoute/pull/12853)) — thanks @maxmad64bis +- **fix(health):** quota with no snapshots reads empty instead of a contradicting 0% ([#12857](https://github.com/diegosouzapw/OmniRoute/pull/12857)) — thanks @maxmad64bis +- fix(a2a): accept the dashboard's own session cookie on /a2a so "Run message/send" no longer fails with "Unauthorized: missing or invalid API key" (#12888) ([#13271](https://github.com/diegosouzapw/OmniRoute/pull/13271)) +- **fix(logs):** the Logs grid's in-memory filter pass no longer discards rows the SQL query already matched — selecting an API key from the dropdown (which sends the key's id) returns its calls again, the Combo tab shows every combo instead of only those whose name contains a "1", and the model filter and search cover the same columns as the query ([#12896](https://github.com/diegosouzapw/OmniRoute/pull/12896)) — fixes [#12873](https://github.com/diegosouzapw/OmniRoute/issues/12873) — thanks @ntdatt812 +- **fix(a2a):** `/api/a2a/status` now builds the agent card from the request that asked for it, so a gateway reached at a non-localhost host no longer advertises `http://localhost:20128` as its A2A URL ([#12918](https://github.com/diegosouzapw/OmniRoute/pull/12918)). — thanks @ntdatt812 +- **fix(compression):** progressive aging now appends its `[COMPRESSED:aging:…]` annotation after a turn's `tool_result` blocks instead of in front of them, so Anthropic no longer rejects aged conversations with "`tool_use` ids were found without `tool_result` blocks immediately after" ([#12920](https://github.com/diegosouzapw/OmniRoute/pull/12920)). — thanks @ntdatt812 +- **fix(bedrock):** model import now resolves context limits for every vendor prefix instead of only `anthropic.*`, so `global.openai.gpt-5.6-*` no longer imports with a null `inputTokenLimit` and gets rejected pre-flight at the 200k default ([#12921](https://github.com/diegosouzapw/OmniRoute/pull/12921)). — thanks @ntdatt812 +- **fix(stream):** the 64 KB stream buffer GLM asks for is honoured instead of dropped, and the type error it caused no longer fails the API Route Typecheck gate on every open PR ([#12925](https://github.com/diegosouzapw/OmniRoute/pull/12925)) — thanks @ntdatt812 +- **fix(guardrails):** mask PII inside a `tool_result`'s nested content array, which the masker walked past while redacting its sibling block ([#12930](https://github.com/diegosouzapw/OmniRoute/pull/12930)) — thanks @ntdatt812 +- **fix(dashboard):** the Radar catalog table no longer leaves absent data unexplained — empty limits, unknown context windows, and unreported capabilities each explain themselves on hover, and a new check keeps it that way ([#12937](https://github.com/diegosouzapw/OmniRoute/pull/12937)) — thanks @maxmad64bis +- **fix(sse):** geo-blocked opencode requests rotate to the next account proxy instead of failing, so one refused egress no longer aborts the whole chain ([#12941](https://github.com/diegosouzapw/OmniRoute/pull/12941)) — thanks @maxmad64bis +- **fix(codex):** dashboard "clear cooldown" and the CAS recovery path now drop nested `codexScopeRateLimitedUntil` maps in the same write that nulls `rate_limited_until`, and fresh quota snapshots with headroom lift fallback-sourced scope cooldowns parked by quota preflight ([#12817](https://github.com/diegosouzapw/OmniRoute/issues/12817), [#12860](https://github.com/diegosouzapw/OmniRoute/issues/12860), [#12951](https://github.com/diegosouzapw/OmniRoute/pull/12951)) The dashboard reset-credit button (`consumeCodexResetCredit`) now exercises that same snapshot path after a successful redeem, so a filled quota bar is enough to unpark a leftover fallback Codex child without another manual clear. — thanks @HouMinXi +- **fix(db):** resolve `sql-wasm.wasm` across global npm install and hoisted layouts, ensuring OmniRoute can boot cleanly on Node 24 when native `better-sqlite3` is uncompiled. ([#13035](https://github.com/diegosouzapw/OmniRoute/pull/13035)) — thanks @HouMinXi +- **fix(api):** `DELETE /v1/batches/delete-completed` now sweeps only the calling API key's own completed batches (batches with no owner stay out of a key-scoped sweep on purpose), with an explicit instance-wide mode reserved for authenticated dashboard sessions, a 401 for a presented key that is unknown, revoked, deactivated, banned or expired (never falling through to the session branch), audit logging of every sweep, a sanitized 500 on failure, an owner-scoped file half (a key-scoped sweep never nulls a file another tenant owns) and an atomic sweep — chunked in 200-batch transactions in instance mode — so a mid-way error never leaves a batch pointing at a nulled file (GHSA-wvxc-jp3v-5mg5) ([#12969](https://github.com/diegosouzapw/OmniRoute/pull/12969)) +- **fix(combo):** quota-weighted routing stops drawing on an out-of-credit connection — a 402 now invalidates the stored quota snapshot instead of leaving its stale remaining percentage in place, and a snapshot older than 10 minutes no longer counts as confident headroom for the primary pool ([#12972](https://github.com/diegosouzapw/OmniRoute/pull/12972)) ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- **fix(vision):** Custom Models with "Vision capable" checked no longer have image requests swapped to `glm/glm-4.6v` when the client sends the advertised alias (`vllm/path/...`) or the bare path-shaped id — Vision Bridge now matches the stored override for all three id forms ([#12758](https://github.com/diegosouzapw/OmniRoute/issues/12758)) ([#12974](https://github.com/diegosouzapw/OmniRoute/pull/12974)) — thanks @HouMinXi +- **fix(sse):** transient opencode upstream failures rotate to the next account proxy instead of failing, so one flapping egress no longer aborts the whole chain ([#12975](https://github.com/diegosouzapw/OmniRoute/pull/12975)) — thanks @maxmad64bis +- **fix(azure):** Deployments from GPT-6 onward now send `max_completion_tokens` instead of `max_tokens`, which Azure rejects with HTTP 400. The rule matched a literal `gpt-5`, so each new generation arrived broken; it now matches the generation range, while `gpt-35-turbo` still keeps `max_tokens`. ([#13007](https://github.com/diegosouzapw/OmniRoute/pull/13007)) — thanks @ntdatt812 +- **fix(db):** add an opt-in self-restart circuit for sustained critical memory pressure, gate post-cleanup VACUUM behind a minimum freed-rows threshold, and checkpoint the SQLite WAL every 5 minutes with a size guard that escalates to TRUNCATE, so a growing WAL can no longer stall the event loop into a full outage. ([#13011](https://github.com/diegosouzapw/OmniRoute/pull/13011)) — thanks @HouMinXi +- **fix(auth):** an explicit connection pin may probe a quota-disabled row once and re-enable it on success ([#12874](https://github.com/diegosouzapw/OmniRoute/issues/12874)) ([#13017](https://github.com/diegosouzapw/OmniRoute/pull/13017)) — thanks @HouMinXi +- **fix(api):** thread `X-OmniRoute-Fallback-Attempts` through combo chat completions so streaming and non-streaming responses report how many prior legs were attempted ([#13038](https://github.com/diegosouzapw/OmniRoute/pull/13038)) — thanks @HouMinXi +- Restore provider failure classification and credential refresh on non-streaming requests: classify non-2xx failures to lock models on per-model quota exhaustion, update connection rate limits from headers and body, and pass credential refresh handlers to pipeline execution so 401 tokens can be refreshed and retried (#13043). ([#13069](https://github.com/diegosouzapw/OmniRoute/pull/13069)) — thanks @HouMinXi +- **fix(responses):** wrap forced-non-streaming web_search fallback JSON as Responses SSE so Codex still sees `response.completed` ([#13050](https://github.com/diegosouzapw/OmniRoute/pull/13050)) — thanks @HouMinXi +- **fix(antigravity):** Preserve upstream thought-token usage in normalized completion and reasoning token counts ([#13055](https://github.com/diegosouzapw/OmniRoute/pull/13055)) — thanks @pacocartones +- Fix a concurrency-slot leak in the DeepSeek PoW solver: a worker that failed to spawn (for example a missing worker script) never released its slot, so `MAX_CONCURRENT_WORKERS` failures disabled the solver until restart. ([#13097](https://github.com/diegosouzapw/OmniRoute/pull/13097)) — thanks @anhtahaylove +- **fix(acp):** bound the ACP session output buffers — `stdoutBuffer` and `stderrBuffer` now cap at 1 MiB keeping the most recent output behind a visible `[...output truncated...]` marker, and `stderrBuffer` is reset per prompt instead of accumulating for the lifetime of the session. ([#13100](https://github.com/diegosouzapw/OmniRoute/pull/13100)) — thanks @anhtahaylove +- **fix(acp):** release the `stdout`/`exit` listeners and the idle timer that a `sendPrompt` timeout used to leave attached to the `acpManager` singleton, and drop sessions that exited on their own from the session map instead of keeping them forever. ([#13096](https://github.com/diegosouzapw/OmniRoute/pull/13096)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection and PII scanners now read the text a `tool_result` block carries on `content` (string or nested block list), in messages and in system blocks, so tool output is judged by the same rules as user text ([#13101](https://github.com/diegosouzapw/OmniRoute/pull/13101)) — thanks @ntdatt812 +- **fix(gamification):** close the badge notification SSE stream when the request signal is already aborted before the stream starts — a client that disconnects while the route is still awaiting auth used to leave both the 2s unlock poll and the 15s heartbeat running for the lifetime of the process. ([#13106](https://github.com/diegosouzapw/OmniRoute/pull/13106)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection scan now spends its 16 KB budget on both ends of the request instead of the first 16 KB only, so `system`, `instructions`, `query`, `documents` and the newest turns are no longer hidden behind one long message ([#13104](https://github.com/diegosouzapw/OmniRoute/pull/13104)) — thanks @ntdatt812 +- **fix(dashboard):** expose the Volcano Ark console cookie on quota scraping and unwrap connect-error objects so the dashboard shows the upstream message ([#13107](https://github.com/diegosouzapw/OmniRoute/pull/13107)) — thanks @HouMinXi +- **fix(db):** release the `beforeExit`/`SIGINT`/`SIGTERM` handlers when a `node:sqlite` adapter closes, so a closed adapter and its database handle are no longer pinned to `process` for the lifetime of the run — the same treatment #7494 gave the sql.js adapter. ([#13109](https://github.com/diegosouzapw/OmniRoute/pull/13109)) — thanks @anhtahaylove +- **fix(translator):** `contentSchema` and `unevaluatedItems` are now treated as subschema positions by the tool-schema sanitizer, so a truncation placeholder in either is replaced with a permissive schema instead of being forwarded as a string ([#13110](https://github.com/diegosouzapw/OmniRoute/pull/13110)) — thanks @ntdatt812 +- **fix(cli-helper):** clear the `createLogStream` timeout on the abort path — `stop()` aborts the in-flight fetch and returned through the `signal.aborted` branch, which skipped `clearTimeout` and left an armed timer per stopped stream. The stream reader is now also cancelled when the read loop exits early. ([#13114](https://github.com/diegosouzapw/OmniRoute/pull/13114)) — thanks @anhtahaylove +- **fix(docs):** drop TLS-impersonation, MITM-decrypt, supply-chain attestation, and XOR-mask writeups from the public `/docs` catalog and Docker image. Files stay in git for engineers; operators who need them open the repo, not the website. ([#13136](https://github.com/diegosouzapw/OmniRoute/pull/13136)) — thanks @HouMinXi +- **fix(combo):** parse numeric-epoch `rate_limited_until` in the combo cooldown read path ([#13141](https://github.com/diegosouzapw/OmniRoute/pull/13141)) — thanks @maxmad64bis +- **fix(opencode):** both OpenCode plugins now reject a gateway address typed without `http://` at configuration time, instead of publishing every model with an api url no client can call, and the v2 plugin no longer publishes a model card whose api url is blank or relative ([#13142](https://github.com/diegosouzapw/OmniRoute/pull/13142)) — thanks @maxmad64bis +- **fix(providers):** lock opencode model on upstream 400 model-unavailable ([#13146](https://github.com/diegosouzapw/OmniRoute/pull/13146)) — thanks @maxmad64bis +- **fix(logging):** keep the provider exchange rather than the raw client bodies when a call log exceeds its size budget, and show that recovered payload in the request-detail panel instead of replacing it with the stored response body ([#13147](https://github.com/diegosouzapw/OmniRoute/pull/13147)) — thanks @maxmad64bis +- **fix(traffic-inspector):** the WebSocket route no longer leaks a traffic-buffer subscriber and a 30s ping timer when the client socket is already closed at handler time — listeners are attached before any resource is acquired, a destroyed socket bails out early, and the ping interval stops on a dead socket where `write()` never throws ([#13155](https://github.com/diegosouzapw/OmniRoute/pull/13155)) — thanks @anhtahaylove +- **fix(telegram):** bound the per-user API key cache in the Telegram chat proxy so a burst of distinct chat ids can no longer grow the process heap without limit ([#13165](https://github.com/diegosouzapw/OmniRoute/issues/13165)) ([#13166](https://github.com/diegosouzapw/OmniRoute/pull/13166)) — thanks @anhtahaylove +- **fix(stream):** cancel the upstream response body when the JSON-to-SSE sniff unwinds on a body timeout, so a stalled upstream no longer pins the connection ([#13169](https://github.com/diegosouzapw/OmniRoute/issues/13169)) ([#13171](https://github.com/diegosouzapw/OmniRoute/pull/13171)) — thanks @anhtahaylove +- **fix(telegram):** authenticate webhook deliveries with Telegram's `secret_token` so an unauthenticated caller can no longer mint API keys or spend upstream quota ([#13172](https://github.com/diegosouzapw/OmniRoute/issues/13172)) ([#13175](https://github.com/diegosouzapw/OmniRoute/pull/13175)) — thanks @anhtahaylove +- **fix(models):** give discoverable Gemini 3.8 Flash ids their own 65536 output spec so Antigravity no longer clamps them to 16384 ([#13195](https://github.com/diegosouzapw/OmniRoute/pull/13195)) — thanks @HouMinXi +- **fix(dashboard):** leftover catalog-removed provider rows (gemini-cli) can be listed and purged from the providers page ([#13067](https://github.com/diegosouzapw/OmniRoute/issues/13067)) ([#13197](https://github.com/diegosouzapw/OmniRoute/pull/13197)) — thanks @HouMinXi +- **fix(auth):** a dashboard session now requires the `authenticated: true` claim that login, OIDC and the session refresh already emit — a JWT merely signed with `JWT_SECRET` (for example the Cursor CLI passthrough token, which any API-key holder can obtain) no longer verifies as the `auth_token` cookie on any route, the WebSocket handshake or the live server; existing sessions keep working ([#13298](https://github.com/diegosouzapw/OmniRoute/issues/13298)) ([#13375](https://github.com/diegosouzapw/OmniRoute/pull/13375)) +- **fix(api):** `DELETE /v1/batches/delete-completed` now applies the caller's API-key policy (endpoint allowlist, schedule, usage cap, rate limit) like every other `/v1` route, commits the key-scoped sweep per 200-batch chunk instead of holding one write lock for the whole sweep, refuses to loop without progress, and rejects a scope that names both a key and `allTenants` ([#13684](https://github.com/diegosouzapw/OmniRoute/pull/13684)) +- Restore the API-route typecheck gate: the non-streaming leg result lost its discriminated-union narrowing after the server-owned tool loop reassignment, producing 13 new TS2339 diagnostics in `chatCore.ts`. ([#12963](https://github.com/diegosouzapw/OmniRoute/pull/12963)) +- Restore four non-streaming behaviours the server-owned tool loop refactor dropped: client aborts map to 499 with the fixed `Request aborted` message, an aborted request no longer logs a synthetic `clientResponse`, Claude prompt-cache telemetry is recorded again, and a body that cannot be canonicalized no longer throws when the tool loop is off. ([#12990](https://github.com/diegosouzapw/OmniRoute/pull/12990)) +- **fix(skills):** The CLI registry parser now reads positionals declared with `.addArgument()`, not only those written inline in `.command()`. `tunnel create [type]` was being published as `tunnel create`, so the agent-skills sync gate reported drift on every branch and regenerating would have deleted the argument. ([#13009](https://github.com/diegosouzapw/OmniRoute/pull/13009)) — thanks @ntdatt812 +- **fix(combos):** dashboard combo test uses a short prompt, serial probes, and a 60s timeout so reasoning models and rate-limited free pools do not fail the health check ([#13001](https://github.com/diegosouzapw/OmniRoute/pull/13001)) — thanks @HouMinXi +- fix(compression): terminate idle worker threads on eviction so long-running instances stop leaking OS threads and MessagePorts ([#13091](https://github.com/diegosouzapw/OmniRoute/pull/13091)) — thanks @anhtahaylove +- Redact Google API keys of any length in error bodies: the pattern required exactly 39 characters, so shorter or longer `AIza…` credentials passed through unredacted. ([#12964](https://github.com/diegosouzapw/OmniRoute/pull/12964)) +- **fix(grok-cli):** a 402 "Grok Build usage balance exhausted" parks that Grok login as out of credit (Grok Build CLI, grok.com cookie, and xAI OAuth share the weekly pool). Combo routing then tries the next login instead of locking the model for every account in the pool ([#13061](https://github.com/diegosouzapw/OmniRoute/pull/13061)) — thanks @HouMinXi +- fix(compression): spawn the LLMLingua worker with a file URL object so compression actually runs instead of silently failing open on Node ([#13093](https://github.com/diegosouzapw/OmniRoute/pull/13093)) — thanks @anhtahaylove +- **fix(test):** run the local `test` and `test:unit` scripts at concurrency 4 so a full-suite run no longer exhausts the machine's commit charge and kills unrelated processes ([#13187](https://github.com/diegosouzapw/OmniRoute/pull/13187)) — thanks @anhtahaylove +- **fix(monitoring):** `GET /api/monitoring/health` `credentialHealth` now includes a bounded `failedConnections` list (`connectionId`, `status`, sanitized `lastError`) when the probe-cache gauge `failed>0`, plus `source: probe-cache` and a cheap `staleDbNonOkCount` for sticky SQLite `test_status` on active rows. Documents that the live gauge is not `provider_connections.test_status`. ([#12876](https://github.com/diegosouzapw/OmniRoute/pull/12876)) — thanks @RaviTharuma +- **fix(resilience):** an openai-compatible multi-upstream gateway no longer marks the whole connection `credits_exhausted` when a single upstream (or the `/models` / representative-model health probe) returns 402. Quota failures stay model-scoped; true connection-wide auth failures are unchanged. Single-credential 402 key disable ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239)) is preserved. ([#12875](https://github.com/diegosouzapw/OmniRoute/pull/12875)) — thanks @RaviTharuma +- **fix(dashboard):** nine fixes on the Orchestration Canvas, all diagnosed in the Phase 2 reviews. A source that fails now keeps the timestamp of its FIRST failure instead of being re-stamped every poll — the stale line said "since the last poll" no matter how long the source had been down, and the churn also defeated the snapshot's stable identity (it serializes the source list), so the canvas re-rendered on every tick while anything was broken. A source that HAD data and only then started failing is flagged too: previously only a source with no node at all got the warning, so a source that went down mid-session kept a healthy-looking node forever. The rest are pointwise: clicking a filter chip cancels the pending search debounce (left armed, it fired ~300ms later and silently reverted the chip); the search input carries an accessible name; `?state=running, failed` parses like the unpadded form instead of dropping the padded value; the CSV toggle helper is defined once in `model/urlParams.ts` rather than twice; the agents tab tells "nothing running" apart from "the filter matched nothing", offering a clear-filters button instead of setup links that would be wrong advice there; edges stop emitting SMIL particles above 40 simultaneously active edges, keeping the colored stroke; and the drawer's error banner clears when a retried action succeeds. ([#12983](https://github.com/diegosouzapw/OmniRoute/pull/12983)) +- **fix(i18n):** the nine locales added with the EU-language batch (Greek, Estonian, Irish, Croatian, Lithuanian, Latvian, Maltese, Slovenian, Serbian) were missing the eleven Orchestration Canvas keys that Phase 3 introduced, so the compare-runs panel and the "no runs match these filters" empty state fell back to English in those languages (`deepMergeFallback` substitutes English for an absent key, so nothing rendered blank — it rendered untranslated). The coverage gate does not catch this: it enforces an 80% floor per locale, and eleven missing keys out of ~13,000 leaves coverage at 99.9%. Translated for real in each language, calibrated against the wording each file already uses for "run", "filter" and "skill". ([#13294](https://github.com/diegosouzapw/OmniRoute/pull/13294)) +- **fix(dashboard):** five follow-ups from the Phase 2 Orchestration Canvas review. The History drawer now shows the memory section for runs that already left the live TTL window: the persisted `memory_hits` event is parsed into `metadata.memoryHits` with the same defensive validation the drawer applies, and — because that event is observability rather than a state transition — it no longer leaks into the timeline, where it had been inheriting the task's state and rendering as a duplicate transition. The A2A memory recall runs against its own 1.5s deadline instead of inheriting the memory backend's 30s one; overshooting degrades exactly like any other recall failure (no hits, task proceeds), and the timer is cleared on both paths. Repeating a Conductor run carries its `requirements.cli`/`requirements.model` forward, so the new run is pinned to the same runner profile and model rather than drifting to whatever the fleet picks. A successful repeat from the Agents tab now focuses the run it created, instead of leaving the operator on the finished one. And the auth test for `POST /api/conductor/tasks` moved into the shared `ROUTES` array rather than restating the pattern. ([#12988](https://github.com/diegosouzapw/OmniRoute/pull/12988)) +- fix(plugins): stop leaking an exit listener per plugin hook timeout, which triggered MaxListenersExceededWarning on plugins that ignore SIGTERM ([#13092](https://github.com/diegosouzapw/OmniRoute/pull/13092)) — thanks @anhtahaylove +- **fix(validation):** Provider node edits no longer fail with a generic "Invalid request" when the optional daily-quota reset fields are left blank. The dashboard sends `dailyQuotaResetTimezone` and `dailyQuotaResetHour` as `null`, and only the hour accepted it. ([#13066](https://github.com/diegosouzapw/OmniRoute/issues/13066)) ([#13083](https://github.com/diegosouzapw/OmniRoute/pull/13083)) — thanks @ntdatt812 +- **fix(combo):** quota-aware expansion drops banned, inactive, missing, and wrong-provider connections before quota fetch or model dispatch; pins and allowlists stay selectors, not a bypass. Antigravity automatic exhaustion now requires a reported zero remaining, so a positive balance below 1% stays eligible. ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- Fix an intermittent failure in the quota-weighted routing test suite: scores are a function of `Date.now()`, so peers with identical quota scored microseconds apart never tied and swapped order. ([#12946](https://github.com/diegosouzapw/OmniRoute/pull/12946)) +- fix(volcengine): resolve quota window mapping gaps, safe multi-connection console binding, and response cookie sanitization ([#12950](https://github.com/diegosouzapw/OmniRoute/pull/12950)) — thanks @HouMinXi +- **fix(test):** resolve the WebDAV handler path with `fileURLToPath` so the suite's 37 WebDAV tests run on Windows instead of failing with a doubled `C:\C:\` drive prefix ([#13196](https://github.com/diegosouzapw/OmniRoute/pull/13196)) — thanks @anhtahaylove +- **fix(ui):** fall back from invalid AnySearch icon alias (direct commit `9587e07b69`) +- **fix(sse):** stop rebuilding a truncated summary from the collector's cap-dropped event array ([#12718](https://github.com/diegosouzapw/OmniRoute/pull/12718)) — thanks @hartmark +- **fix(conversations):** show a pending spinner for unresolved tool nodes instead of "(empty)" ([#12727](https://github.com/diegosouzapw/OmniRoute/pull/12727)) — thanks @hartmark +- **fix(streaming):** fail fast when an upstream stream produces only lifecycle/heartbeat events, never real content ([#12741](https://github.com/diegosouzapw/OmniRoute/pull/12741)) — thanks @hartmark +- **fix(responses-continuation):** bridge the write-in-flight window with an in-memory pending store ([#12854](https://github.com/diegosouzapw/OmniRoute/pull/12854)) — thanks @hartmark +- **fix(translator):** drop or map agent_message on Chat Completions fallback ([#12880](https://github.com/diegosouzapw/OmniRoute/pull/12880)) — thanks @RaviTharuma +- **fix(api):** page and stream-complete GET /v1/models for large catalogs ([#12882](https://github.com/diegosouzapw/OmniRoute/pull/12882)) — thanks @RaviTharuma +- **fix(quota):** align AUTH window labels with usage API and clarify 503 ([#12884](https://github.com/diegosouzapw/OmniRoute/pull/12884)) — thanks @RaviTharuma +- **fix(ci):** clear two base-reds on release/v3.8.51 — mutation-coverage gate + image-only-model guard ([#12945](https://github.com/diegosouzapw/OmniRoute/pull/12945)) +- **fix(api):** scope batch bulk-delete to the calling API key ([#13211](https://github.com/diegosouzapw/OmniRoute/pull/13211)) +- **fix(tests):** align model-sync log assertion to shipped path redaction (base-red) ([#13227](https://github.com/diegosouzapw/OmniRoute/pull/13227)) +- **fix(test):** make the ToS heading guard actually require the parentheses ([#13228](https://github.com/diegosouzapw/OmniRoute/pull/13228)) +- **fix(quality):** register 4 drifted covering tests in stryker tap.testFiles ([#13229](https://github.com/diegosouzapw/OmniRoute/pull/13229)) +- **fix(logging):** mask *-api-key headers in request-log pipeline (#13273) ([#13401](https://github.com/diegosouzapw/OmniRoute/pull/13401)) — thanks @KooshaPari +- **fix(tests):** correct stale Jina catalog prefix in custom-models test ([#13403](https://github.com/diegosouzapw/OmniRoute/pull/13403)) — thanks @KooshaPari +- **fix(backend):** prune old backups after health-check-repair snapshots (#13308) ([#13404](https://github.com/diegosouzapw/OmniRoute/pull/13404)) — thanks @KooshaPari +- **fix(backend):** guard memory_fts_au trigger with WHEN clause to stop FTS bloat (#13326) ([#13405](https://github.com/diegosouzapw/OmniRoute/pull/13405)) — thanks @KooshaPari +- **fix(free-tiers):** remove obsolete Together $25 signup credit (#12526) ([#13407](https://github.com/diegosouzapw/OmniRoute/pull/13407)) — thanks @KooshaPari +- **fix:** skip non-chat generation models in Test all models ([#13409](https://github.com/diegosouzapw/OmniRoute/pull/13409)) — thanks @KooshaPari +- **fix(providers):** de-duplicate agy and antigravity model catalogs ([#13410](https://github.com/diegosouzapw/OmniRoute/pull/13410)) — thanks @KooshaPari +- **fix(dashboard):** send provider in body for mini-playground web search ([#13411](https://github.com/diegosouzapw/OmniRoute/pull/13411)) — thanks @KooshaPari +- **fix(resilience):** add CJK quota-exhaustion patterns to 429 classifier ([#13412](https://github.com/diegosouzapw/OmniRoute/pull/13412)) — thanks @KooshaPari +- **fix(backend):** eval runner: failed calls no longer score as passed + dotAll regex ([#13413](https://github.com/diegosouzapw/OmniRoute/pull/13413)) — thanks @KooshaPari +- **fix(backend):** eval runner opts out of output-style and memory injection ([#13414](https://github.com/diegosouzapw/OmniRoute/pull/13414)) — thanks @KooshaPari +- **fix:** chain serial quarantine step in npm test script (fixes #13286) ([#13418](https://github.com/diegosouzapw/OmniRoute/pull/13418)) — thanks @KooshaPari +- **fix(antigravity):** derive stable session ID from credentials for prompt-cache reuse (#13305) ([#13424](https://github.com/diegosouzapw/OmniRoute/pull/13424)) — thanks @KooshaPari +- **fix(memory):** merge memory into top-level system field instead of unshifting at messages[0] (#13425) ([#13427](https://github.com/diegosouzapw/OmniRoute/pull/13427)) — thanks @KooshaPari +- **fix(compression):** preserve instruction blocks from lossy engines ([#13523](https://github.com/diegosouzapw/OmniRoute/pull/13523)) — thanks @KooshaPari +- **fix(compression):** preserve polarity words and newlines in ultra heuristic ([#13524](https://github.com/diegosouzapw/OmniRoute/pull/13524)) — thanks @KooshaPari +- **fix(compression):** run user preservePatterns before built-in patterns (#13457) ([#13525](https://github.com/diegosouzapw/OmniRoute/pull/13525)) — thanks @KooshaPari +- **fix(reasoning):** expand deepseek-v4 regex to match v4.1+ and add command-code provider (#13430) ([#13528](https://github.com/diegosouzapw/OmniRoute/pull/13528)) — thanks @KooshaPari +- **fix(providers):** add alwaysReasons metadata for reka-flash-3 ([#13531](https://github.com/diegosouzapw/OmniRoute/pull/13531)) — thanks @KooshaPari +- **fix(playground):** allow Compare columns to scroll independently ([#13532](https://github.com/diegosouzapw/OmniRoute/pull/13532)) — thanks @KooshaPari +- **fix(security):** empty whitelist allows through to prevent admin lockout ([#13534](https://github.com/diegosouzapw/OmniRoute/pull/13534)) — thanks @KooshaPari +- **fix(tests):** add DISABLE_IOREG_STRATEGY env var for macOS test compatibility (fixes #13467) ([#13539](https://github.com/diegosouzapw/OmniRoute/pull/13539)) — thanks @KooshaPari +- **fix(zed-hosted):** map developer role and Gemini enums for Zed proxy ([#13543](https://github.com/diegosouzapw/OmniRoute/pull/13543)) — thanks @KooshaPari +- **fix(analytics):** normalize provider aliases to canonical id in usage_history ([#13545](https://github.com/diegosouzapw/OmniRoute/pull/13545)) — thanks @KooshaPari +- **fix(combo):** use traceId for call log id to prevent UNIQUE constraint failure ([#13546](https://github.com/diegosouzapw/OmniRoute/pull/13546)) — thanks @KooshaPari +- **fix(auth):** check model lockout before returning synthetic noauth connection ([#13547](https://github.com/diegosouzapw/OmniRoute/pull/13547)) — thanks @KooshaPari +- **fix(guardrails):** skip credential redaction for base64 image data URLs ([#13550](https://github.com/diegosouzapw/OmniRoute/pull/13550)) — thanks @KooshaPari +- **fix(resilience):** increase requestQueue.maxWaitMs default from 15s to 30s ([#13553](https://github.com/diegosouzapw/OmniRoute/pull/13553)) — thanks @KooshaPari +- **fix(docs):** document OMNIROUTE_READY_TIMEOUT_MS and allowlist the test-only DISABLE_IOREG_STRATEGY ([#13692](https://github.com/diegosouzapw/OmniRoute/pull/13692)) ### 📝 Maintenance @@ -853,6 +1054,39 @@ _By commits in `release/v3.8.50..release/v3.8.51` (`091589089c..d6f315018a`), au - **docs(security):** document socket.yml scanner config + CI workflow link (#12575) ([#12764](https://github.com/diegosouzapw/OmniRoute/pull/12764)) — thanks @KooshaPari - **chore(ci):** guard commit identity in pre-commit to stop author misattribution ([#12772](https://github.com/diegosouzapw/OmniRoute/pull/12772)) - **deps:** 17 Dependabot bumps — bump electron from 43.4.0 to 43.4.1 in /electron ([#11426](https://github.com/diegosouzapw/OmniRoute/pull/11426)); bump the development group across 1 directory with 8 updates ([#11428](https://github.com/diegosouzapw/OmniRoute/pull/11428)); bump the production group across 1 directory with 11 updates ([#11440](https://github.com/diegosouzapw/OmniRoute/pull/11440)); chore(deps): bump github/codeql-action/init from 4.37.7 to 4.37.8 ([#11925](https://github.com/diegosouzapw/OmniRoute/pull/11925)); chore(deps): bump github/codeql-action from 4.37.7 to 4.37.8 ([#11926](https://github.com/diegosouzapw/OmniRoute/pull/11926)); chore(deps): bump github/codeql-action/analyze from 4.37.7 to 4.37.8 ([#11927](https://github.com/diegosouzapw/OmniRoute/pull/11927)); build(deps): bump github/codeql-action/init from 4.37.8 to 4.37.9 ([#12345](https://github.com/diegosouzapw/OmniRoute/pull/12345)); build(deps): bump github/codeql-action/analyze from 4.37.8 to 4.37.9 ([#12346](https://github.com/diegosouzapw/OmniRoute/pull/12346)); bump the development group across 1 directory with 2 updates ([#12347](https://github.com/diegosouzapw/OmniRoute/pull/12347)); build(deps): bump github/codeql-action from 4.37.8 to 4.37.9 ([#12349](https://github.com/diegosouzapw/OmniRoute/pull/12349)); bump browserslist from 4.28.2 to 4.28.8 ([#12396](https://github.com/diegosouzapw/OmniRoute/pull/12396)); bump the production group across 1 directory with 4 updates ([#12399](https://github.com/diegosouzapw/OmniRoute/pull/12399)); bump @xmldom/xmldom ([#12500](https://github.com/diegosouzapw/OmniRoute/pull/12500)); bump qs from 6.15.2 to 6.16.0 ([#12512](https://github.com/diegosouzapw/OmniRoute/pull/12512)); bump @xmldom/xmldom from 0.9.10 to 0.9.12 ([#12513](https://github.com/diegosouzapw/OmniRoute/pull/12513)); bump fast-uri from 3.1.5 to 3.1.7 ([#12514](https://github.com/diegosouzapw/OmniRoute/pull/12514)); bump @humanfs/node from 0.16.7 to 0.16.8 ([#12515](https://github.com/diegosouzapw/OmniRoute/pull/12515)) +- Clear the `release/v3.8.51` `check:agent-skills-sync` base-red: regenerate `skills/cli-tunnel/SKILL.md` so the `tunnel create [type]` positional that #13009 taught the generator to read is reflected in the committed skill. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` typecheck base-red from #12731: the new `Mode pack ranking gates` candidates in `open-sse/services/autoCombo/__tests__/autoCombo.test.ts` omitted the required `provider`, `model` and `errorRate` fields of `ProviderCandidate`, failing `check:open-sse-typecheck` (and with it `Fast Quality Gates`) on every open PR. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` docs-sync base-red: EURouter (#13025) and GreenPT (#13024) took the live provider count to 358, leaving 7 STRICT drifts (`PROVIDER_REFERENCE.md`, 4 diagrams, 2 tier-flow images) plus the `AGENTS.md` / `llm.txt` / `package.json` count claims stale. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- **test(models):** Pin `CATALOG_BUILD_TIMEOUT_MS` in the #12058 canonical-catalog regression so a cold catalog build racing the 8s default bound no longer flakes the suite with a 500 ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Allow `@omniroute/opencode-plugin-v2/` in the npm pack policy — [#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870) shipped the v2 plugin beside its v1 sibling without widening the allowlist, so every packed file under it read as an unexpected artifact and failed `check:pack-policy` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Point the release-green validator's pack gate at the tree it is validating — the build-provenance guard defaulted to `origin/main`, which a release branch tip can never be an ancestor of mid-cycle, so `Package artifact` reported a false red on every run and the tarball boot-smoke cascaded off it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **docs(checks):** keep doc counts honest — headings, rankings, catalog, weights, quality gate and scoring diagram now covered ([#12507](https://github.com/diegosouzapw/OmniRoute/pull/12507)) — thanks @maxmad64bis +- **chore(lifecycle):** `check:model-lifecycle` now also diffs `DEFAULT_DEGRADATION_MAP` (the background-task redirect table) against the vendor lifecycle snapshot, refusing a retired id as source or target, with a table-driven unit test beside it. Three rows whose source the vendor had retired — `claude-sonnet-4-20250514`, `gemini-3-pro-preview` and `gpt-5.1-codex` (whose target `gpt-5.1-codex-mini` is retired too) — were dead code, since `checkLifecycle` answers 410 before the redirect runs; they are dropped (#12535 — thanks @pacocartones) +- **test(compression):** cover idle worker eviction at the resource level — the pool must call `terminate()` and must not retain the worker's `MessagePort`, complementing the `exit`-event assertion added with the fix ([#13371](https://github.com/diegosouzapw/OmniRoute/pull/13371)) +- **chore(quality):** raise the `imageGeneration.ts` file-size ceiling for the image-only-model guard that clears the #12945 base-red ([#13392](https://github.com/diegosouzapw/OmniRoute/pull/13392)) +- **chore(quality):** type the combo-test route's JSON response bodies instead of casting them to `any`, drop the now-empty suppression entry, and remove the `hasPerModelQuota` import `chatCore.ts` stopped using when the failure-classification helper was extracted ([#13379](https://github.com/diegosouzapw/OmniRoute/pull/13379)) +- **chore(electron):** the desktop bundle no longer ships the root-level files of every translated docs mirror (`README.md`, `llm.txt`, `CLAUDE.md`, `GEMINI.md`, `CONTRIBUTING.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`) — the packaged app only reads `docs/i18n//docs/**`, which stays. Saves ~11 MB on top of the translated CHANGELOGs already pruned. (#0000) ([#13235](https://github.com/diegosouzapw/OmniRoute/pull/13235)) +- **chore(quality):** retighten the `src/sse/handlers/chat.ts` unused-vars suppression count to the 8 violations that actually remain ([#13385](https://github.com/diegosouzapw/OmniRoute/pull/13385)) +- Bring the documented counts back in line with the code: 171 migrations (was 169) and 20 routing strategies (was 19), plus the regenerated `cli-tunnel` skill reference. ([#12970](https://github.com/diegosouzapw/OmniRoute/pull/12970)) +- Update the documented migration count to 172 after the call-logs provider-stats indexes landed. ([#13209](https://github.com/diegosouzapw/OmniRoute/pull/13209)) +- **chore(ci):** two gates that close the blind spots behind the exclusions above. `check:vitest-exclusions` requires every Vitest exclusion to name a tracking issue and to appear in `config/quality/vitest-exclusions.json` — the previous list grew to 62 files behind a comment pointing at an issue that had been closed for a month. `check-new-key-coverage` requires a key newly added to `en.json` to reach every locale; the existing coverage gate is a percentage floor per locale, so eleven absent keys out of ~13,000 left it at 99.9% while a whole feature shipped untranslated in nine languages. Both are diff-aware, so pre-existing debt stays frozen and neither needed a migration to turn on. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **build:** root layout no longer loads Inter from `next/font/google`, so a production image build does not need fonts.googleapis.com ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **docs(i18n):** bring the eight new `llm.txt` mirrors (km, kn, ml, my, ne, or, pa, si) up to the current provider and migration counts, so `check-docs-sync` stops rejecting every commit ([#13674](https://github.com/diegosouzapw/OmniRoute/pull/13674)) +- **refactor(ui):** flow surfaces (home topology, combo live studio, compression cockpit/waterfall/nodes, token-health badge) express state through the theme-aware `--orch-status-*` tokens instead of fixed dark-mode hex, so green/red/amber/grey stay legible in the light theme; dark mode is byte-identical. Categorical palettes (routing-strategy hues, compression-layer pills, provider brand colors) deliberately stay hex ([#12378](https://github.com/diegosouzapw/OmniRoute/issues/12378)) ([#13203](https://github.com/diegosouzapw/OmniRoute/pull/13203)) +- **chore(tests):** 51 test files that had been excluded from Vitest are running again, restoring roughly 350 assertions to the blocking suite. Each was measured individually first: of the 62 files parked behind the `// #8618 — pre-existing failure` comment, 51 pass against the current tree with no source change, so the exclusions had outlived the failures they were added for. The 11 that genuinely still fail stay excluded, but now point at a live tracker (#13204) rather than at #8618, which was closed in August while the list it tracked kept growing. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **chore(quality):** drop two ESLint suppression entries whose violations no longer exist, so `eslint --suppressions-location` stops rejecting every commit that touches the surrounding files ([#13384](https://github.com/diegosouzapw/OmniRoute/pull/13384)) +- **chore(release):** bring main's post-tag fixes into release/v3.8.51 — sync-back step 2/2 (direct commit `21c488f210`) +- **chore(deps):** patch toml and esbuild in the opencode plugin lockfiles ([#12965](https://github.com/diegosouzapw/OmniRoute/pull/12965)) +- **docs(changelog):** reconcile the v3.8.51 living section — fold 366 fragments, cover every cycle commit, credit every contributor ([#12971](https://github.com/diegosouzapw/OmniRoute/pull/12971)) +- **test(plugin):** wait for the v2 lazy-refresh tier instead of sleeping past it ([#12984](https://github.com/diegosouzapw/OmniRoute/pull/12984)) +- **chore(quality):** rebaseline chatCore.ts after the non-streaming regression fixes ([#13045](https://github.com/diegosouzapw/OmniRoute/pull/13045)) +- **docs(changelog):** add the missing bullet marker to the DeepSeek PoW fragment ([#13200](https://github.com/diegosouzapw/OmniRoute/pull/13200)) +- **chore(deps):** drain the Dependabot queue — 10 of 13 alerts ([#13213](https://github.com/diegosouzapw/OmniRoute/pull/13213)) +- **chore(ci):** make worktrees carry the hooks that enforce the identity gate ([#13236](https://github.com/diegosouzapw/OmniRoute/pull/13236)) +- **chore(deps):** pin csv-parse to ^7.0.2 via overrides (#13117) ([#13300](https://github.com/diegosouzapw/OmniRoute/pull/13300)) — thanks @KooshaPari +- **chore(deps):** bump hono from ^4.12.34 to ^4.13.7 (#13148) ([#13301](https://github.com/diegosouzapw/OmniRoute/pull/13301)) — thanks @KooshaPari +- **chore(deps):** pin joi to ^18.2.8 via overrides (#13085) ([#13302](https://github.com/diegosouzapw/OmniRoute/pull/13302)) — thanks @KooshaPari +- **docs(providers):** add Microsoft 365 Copilot (BizChat) provider guide (#12779) ([#13340](https://github.com/diegosouzapw/OmniRoute/pull/13340)) — thanks @KooshaPari ### 🙌 Contributors @@ -870,6 +1104,7 @@ Thanks to everyone whose work landed in v3.8.51: | [@amaleta](https://github.com/amaleta) | #11906, #11934 | | [@amartinawi](https://github.com/amartinawi) | #11575 | | [@AndrianBalanescu](https://github.com/AndrianBalanescu) | #11083, #11830, #11834, #11844 | +| [@anhtahaylove](https://github.com/anhtahaylove) | #13091, #13092, #13093, #13096, #13097, #13100, #13106, #13109, #13114, #13155, #13166, #13171, #13175, #13187, #13196 | | [@aniruddhaadak80](https://github.com/aniruddhaadak80) | #11760 | | [@app](https://github.com/app) | #12554 | | [@arminanton](https://github.com/arminanton) | #11461, #11513 | @@ -899,10 +1134,10 @@ Thanks to everyone whose work landed in v3.8.51: | [@gonisulaimann](https://github.com/gonisulaimann) | #12368, #12369, #12371 | | [@Gorillaz322](https://github.com/Gorillaz322) | #12207 | | [@groovecityJO](https://github.com/groovecityJO) | #12682 | -| [@hartmark](https://github.com/hartmark) | #11434, #11452, #11473, #11499, #11703, #11983, #11984, #11985, #11986, #11988, #11989, #11990, #11994, #12221, #12293, #12445, #12446, #12447, #12448, #12460, #12461, #12623, #12646, #12650, #12680 | +| [@hartmark](https://github.com/hartmark) | #11434, #11452, #11473, #11499, #11703, #11983, #11984, #11985, #11986, #11988, #11989, #11990, #11994, #12221, #12293, #12445, #12446, #12447, #12448, #12460, #12461, #12623, #12646, #12650, #12680, #12717, #12718, #12727, #12741, #12854 | | [@hizzt](https://github.com/hizzt) | #11894 | | [@hongnoul](https://github.com/hongnoul) | #11484 | -| [@HouMinXi](https://github.com/HouMinXi) | #11411, #11414, #11512, #11518, #11520, #11641, #11642, #11643, #11687, #11779, #11849, #11850, #11851, #11915, #11916, #11918, #11919, #11920, #12013, #12017, #12033, #12042, #12106, #12139, #12166, #12169, #12171, #12205, #12213, #12312, #12325, #12487, #12488, #12495, #12504, #12557, #12566, #12590, #12591, #12624, #12626, #12632, #12637, #12678, #12696, #12697, #12711, #12733, #12746, #12767, #12770, #12789, #12803, #12805, #12811, #12866, #12868, #12899, #12926, #12934 | +| [@HouMinXi](https://github.com/HouMinXi) | #11411, #11414, #11512, #11518, #11520, #11641, #11642, #11643, #11687, #11779, #11849, #11850, #11851, #11915, #11916, #11918, #11919, #11920, #12013, #12017, #12033, #12042, #12106, #12139, #12166, #12169, #12171, #12205, #12213, #12312, #12325, #12487, #12488, #12495, #12504, #12557, #12566, #12590, #12591, #12624, #12626, #12632, #12637, #12678, #12696, #12697, #12711, #12733, #12746, #12767, #12770, #12789, #12803, #12805, #12811, #12866, #12868, #12899, #12926, #12934, #12950, #12951, #12974, #13001, #13006, #13011, #13017, #13026, #13027, #13034, #13035, #13038, #13042, #13050, #13060, #13061, #13069, #13107, #13120, #13136, #13195, #13197 | | [@Hsia97](https://github.com/Hsia97) | #11624 | | [@jacobsparts](https://github.com/jacobsparts) | #11854, #12155, #12167 | | [@joglomedia](https://github.com/joglomedia) | #11980 | @@ -912,11 +1147,11 @@ Thanks to everyone whose work landed in v3.8.51: | [@KaspaPulse](https://github.com/KaspaPulse) | #11389, #11469, #11628, #11666, #11910 | | [@keeltrace](https://github.com/keeltrace) | #12080, #12223 | | [@killer30001000](https://github.com/killer30001000) | #12178 | -| [@KooshaPari](https://github.com/KooshaPari) | #12470, #12592, #12667, #12699, #12703, #12706, #12764, #12769, #12771 | +| [@KooshaPari](https://github.com/KooshaPari) | #12470, #12592, #12667, #12699, #12703, #12706, #12764, #12769, #12771, #13300, #13301, #13302, #13340, #13401, #13403, #13404, #13405, #13406, #13407, #13409, #13410, #13411, #13412, #13413, #13414, #13418, #13424, #13427, #13433, #13523, #13524, #13525, #13528, #13531, #13532, #13534, #13539, #13543, #13545, #13546, #13547, #13550, #13553 | | [@kriptoburak](https://github.com/kriptoburak) | #11370 | | [@KrzysiekSko](https://github.com/KrzysiekSko) | #12425, #12432, #12673 | | [@marcelokarval](https://github.com/marcelokarval) | direct commit / report | -| [@maxmad64bis](https://github.com/maxmad64bis) | #1622, #11435, #11437, #11441, #11537, #11550, #11553, #11555, #11557, #11812, #11842, #11843, #11903, #12151, #12214, #12215, #12218, #12226, #12314, #12316, #12317, #12318, #12319, #12320, #12321, #12870 | +| [@maxmad64bis](https://github.com/maxmad64bis) | #1622, #11435, #11437, #11441, #11537, #11550, #11553, #11555, #11557, #11812, #11842, #11843, #11903, #12151, #12214, #12215, #12218, #12226, #12314, #12316, #12317, #12318, #12319, #12320, #12321, #12507, #12715, #12731, #12744, #12786, #12787, #12788, #12790, #12792, #12794, #12795, #12828, #12832, #12853, #12857, #12870, #12937, #12941, #12975, #13141, #13142, #13146, #13147 | | [@mdigitalbh81](https://github.com/mdigitalbh81) | #12240 | | [@Meet6338-X](https://github.com/Meet6338-X) | #11598, #11609, #12110 | | [@morpheus9393](https://github.com/morpheus9393) | #11943 | @@ -925,11 +1160,11 @@ Thanks to everyone whose work landed in v3.8.51: | [@NightStalker-87](https://github.com/NightStalker-87) | #12183 | | [@NoxzRCW](https://github.com/NoxzRCW) | #11879, #11880, #11881, #11882, #11883, #11935 | | [@ntdat812](https://github.com/ntdat812) | #11585, #12095 | -| [@ntdatt812](https://github.com/ntdatt812) | #11368, #11573, #11574, #11576, #11577, #11580, #11582, #11583, #11588, #11589, #11590, #11591, #11592, #11593, #11672, #12177, #12180 | +| [@ntdatt812](https://github.com/ntdatt812) | #11368, #11573, #11574, #11576, #11577, #11580, #11582, #11583, #11588, #11589, #11590, #11591, #11592, #11593, #11672, #12177, #12180, #12873, #12918, #12920, #12921, #12925, #12930, #13007, #13009, #13024, #13025, #13083, #13087, #13101, #13104, #13110 | | [@official-burak](https://github.com/official-burak) | #11542 | | [@opensource-elearning](https://github.com/opensource-elearning) | #12179, #12189, #12278, #12286 | | [@oyi77](https://github.com/oyi77) | #11408, #11409, #11421, #11505, #11677, #12036, #12110 | -| [@pacocartones](https://github.com/pacocartones) | #11521, #11522, #11527, #11528, #11529, #11530, #11531, #11532, #11533, #11534, #11595, #11599, #11603, #11604, #11605, #11607, #11610, #11676, #11714, #11716, #11718, #11767, #11838, #11860, #11862, #11869, #11871, #11872, #11873, #11903, #11906, #11921, #11934, #12359, #12360, #12361, #12362, #12364, #12365, #12373, #12374, #12375, #12376, #12377, #12379, #12380, #12381, #12386, #12387, #12389, #12390, #12394, #12395, #12397, #12401, #12403, #12404 | +| [@pacocartones](https://github.com/pacocartones) | #11521, #11522, #11527, #11528, #11529, #11530, #11531, #11532, #11533, #11534, #11595, #11599, #11603, #11604, #11605, #11607, #11610, #11676, #11714, #11716, #11718, #11767, #11838, #11860, #11862, #11869, #11871, #11872, #11873, #11903, #11906, #11921, #11934, #12356, #12358, #12359, #12360, #12361, #12362, #12364, #12365, #12373, #12374, #12375, #12376, #12377, #12379, #12380, #12381, #12386, #12387, #12389, #12390, #12394, #12395, #12397, #12401, #12403, #12404, #12522, #12523, #12535, #12536, #12540, #12541, #12543, #12545, #12548, #12549, #12550, #12551, #12552, #12647, #12651, #12653, #13055 | | [@patrykkopycinski](https://github.com/patrykkopycinski) | #11936, #11937, #12224 | | [@PauloFH](https://github.com/PauloFH) | #11509 | | [@PauloHSOliveira](https://github.com/PauloHSOliveira) | #12241 | @@ -941,7 +1176,7 @@ Thanks to everyone whose work landed in v3.8.51: | [@raheemuddin786](https://github.com/raheemuddin786) | #11491, #11839, #11840, #11841, #12003, #12230, #12231, #12232, #12233, #12234 | | [@rahilmavani](https://github.com/rahilmavani) | #11761 | | [@Rahulsharma0810](https://github.com/Rahulsharma0810) | #11771 | -| [@RaviTharuma](https://github.com/RaviTharuma) | #11710, #11727, #11797, #11798, #11802, #11805, #11806, #11811, #12098, #12099, #12101, #12449, #12452, #12472, #12473, #12493, #12533, #12607, #12628, #12631, #12636 | +| [@RaviTharuma](https://github.com/RaviTharuma) | #11710, #11727, #11797, #11798, #11802, #11805, #11806, #11811, #12098, #12099, #12101, #12449, #12452, #12472, #12473, #12493, #12533, #12607, #12628, #12631, #12636, #12875, #12876, #12880, #12882, #12884 | | [@rezjalibd](https://github.com/rezjalibd) | #12186 | | [@RhianB14](https://github.com/RhianB14) | #11692 | | [@rifqiawl](https://github.com/rifqiawl) | #11517, #11519, #11969 | diff --git a/docs/i18n/phi/CHANGELOG.md b/docs/i18n/phi/CHANGELOG.md index 797d1cb812..ce37408077 100644 --- a/docs/i18n/phi/CHANGELOG.md +++ b/docs/i18n/phi/CHANGELOG.md @@ -8,16 +8,1119 @@ ## [3.8.51] — TBD -_Living section — cycle opened at the v3.8.50 freeze (parallel-cycle model). Bullets are aggregated from `changelog.d/` fragments at each `/generate-release` phase._ +_Living section — reconciled 2026-09-15 from all cycle commits (`091589089c` → `c0f92ec98a`, 916 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 | **123** | +| 📝 Commits in the cycle | **916** | +| 🔀 Pull requests referenced | **892** | +| 📋 Changelog entries | **919** | +| 🙌 Contributors credited in entries | **121** | +| 🤖 Automated dependency commits | 18 | + +**Entries by type** + +| Type | Count | +| --- | ---: | +| 🐛 Fixes | 592 | +| ✨ Features | 131 | +| 🧹 Chore | 93 | +| 📚 Docs | 42 | +| 🧪 Tests | 32 | +| ♻️ Refactor | 8 | +| 🏗️ Build | 7 | +| ⚡ Performance | 7 | +| ⚙️ CI | 2 | +| ⏪ Reverts | 2 | +| 📦 Dependencies | 2 | +| 🔒 Security | 1 | + +### 🏆 Top 25 contributors this cycle + +_By commits in `091589089c..c0f92ec98a`, author identities consolidated via `.mailmap` and the merged PR's GitHub login. Bots excluded._ + +| # | Contributor | Commits | +| ---: | --- | ---: | +| 🥇 | diegosouzapw | 323 | +| 🥈 | Bob.Hou (@HouMinXi) | 79 | +| 🥉 | Paco Cartones (@pacocartones) | 77 | +| 4 | Dizzle (@maxmad64bis) | 49 | +| 5 | Koosha Paridehpour (@KooshaPari) | 43 | +| 6 | Nguyen Thanh Dat (@ntdatt812) | 31 | +| 7 | Ravi Tharuma (@RaviTharuma) | 26 | +| 8 | Markus Hartung (@hartmark) | 22 | +| 9 | Webman (@jonlwheat2-gif) | 22 | +| 10 | backryun | 17 | +| 11 | anhtahaylove | 15 | +| 12 | Rafa Martins (@rafacpti23) | 10 | +| 13 | Syed Raheemuddin (@raheemuddin786) | 10 | +| 14 | Nguyễn Viết Tuấn (@TheDemonTuan) | 8 | +| 15 | MumuTW | 7 | +| 16 | Paijo (@oyi77) | 7 | +| 17 | Tobias Andersen (@turbolego) | 6 | +| 18 | Bl0ck (@Bl0ck154) | 5 | +| 19 | SHANMUGAPRIYAN (@geek007git) | 5 | +| 20 | KaspaPulse | 5 | +| 21 | Abhishek Sharma (@abhisheksharma2411) | 4 | +| 22 | Abhishek Divekar (@adivekar-utexas) | 4 | +| 23 | Andrew B. (@AndrianBalanescu) | 4 | +| 24 | NoxzRCW | 4 | +| 25 | opensource-elearning | 4 | ### ✨ New Features +- **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). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- Added Google AI Studio Gemini batch text-to-speech support through `POST /v1/audio/speech`. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- 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. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(routing):** subscription-first auto groupings — `auto/subscription` routes only through plan-included connections with a documented hard-stop overage and fails closed on exhaustion, while `auto/thrifty` orders the pool `subscription → keyless → free → cheap → premium` and steps up one rung at a time as each is exhausted. Billing class comes from a curated per-connection catalog (uncurated is treated as metered, never plan-included), both reuse STRICT_ZERO_COST's per-connection verification, and a quota reading whose `resetAt` has passed is now refreshed regardless of TTL so routing returns to plan capacity as soon as it resets ([#11146](https://github.com/diegosouzapw/OmniRoute/pull/11146)) — thanks @yourspraveen +- **feat(providers):** publish a management-authenticated versioned web-session credential contract from OmniRoute's canonical browser credential metadata ([#11340](https://github.com/diegosouzapw/OmniRoute/pull/11340)) — thanks @Zartharas +- **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)) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add Xquik X search with typed results, credential validation, REST routing, and MCP selection ([#11370](https://github.com/diegosouzapw/OmniRoute/pull/11370)) — thanks @kriptoburak +- **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)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(dashboard):** surface durable exclusive managed leases in the existing Sessions view, keeping leased clients visible across idle gaps while marking connections with in-flight work as active ([#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389)) — thanks @KaspaPulse +- **build(bun):** allow Turbopack bundler flag on Bun 1.4+ with configurable Webpack fallback ([#11471](https://github.com/diegosouzapw/OmniRoute/pull/11471)) — thanks @TheDemonTuan +- feat(api): add an opt-in `modelVisibilityAllowlist`/`modelVisibilityDenylist` settings pair to curate exactly which models `/v1/models` advertises, mirrored into every `auto/*` combo candidate pool so a denied model cannot be routed to via combo selection either (#11481) ([#11997](https://github.com/diegosouzapw/OmniRoute/pull/11997)) +- **feat(rankings):** the Free Provider Rankings page shows what each provider actually served over the last 24 h. It ranked by ELO alone, which left a provider that answers every call with an error in first place; the usage data was already served by the API but never requested. A provider with too small a sample shows a dash, not a number ([#11546](https://github.com/diegosouzapw/OmniRoute/pull/11546)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **feat(guardrails):** enforce a bounded, deterministic contract for Video Bridge transcripts — 256 cues, 4096 input code units and 4 KiB UTF-8 per cue, 64 KiB total text, malformed-Unicode rejection, focus-window scoping, cross-source reconciliation with contributing-source metadata, and a structural provenance trust boundary so caller JSON can never self-assert `embedded`/`audio-bridge` provenance ([#11652](https://github.com/diegosouzapw/OmniRoute/issues/11652)) ([#12009](https://github.com/diegosouzapw/OmniRoute/pull/12009)) +- **feat(video):** orchestrate optional Video Bridge audio extraction and Audio Bridge STT behind a dual opt-in (operator setting AND per-request signal) — a new loopback-only broker `mode=audio` operation shares the frame path's exact process queue, deadline, AbortSignal, and byte budgets to extract a bounded mono 16 kHz PCM WAV from the same already-downloaded video, then reuses the existing Audio Bridge transcription boundary; provider segment timing is preserved when available and marked coarse otherwise, and every failure degrades to a visual-only-safe partial instead of throwing (#11654). ([#12012](https://github.com/diegosouzapw/OmniRoute/pull/12012)) +- Add a tenant-bound Video Bridge drill-down lifecycle on top of the existing secure cache substrate: opaque hashed handles (never raw session/video identifiers), preview/standard/detail multiresolution variants resampled on read, response pagination capped at 8 frames and 32 MiB, and a new authenticated `/api/v1/video-bridge/drilldown` consumer route that stays disabled for remote access by default and denies cross-key access with the same response as a nonexistent handle (no existence oracle). ([#12006](https://github.com/diegosouzapw/OmniRoute/pull/12006)) +- **test(video):** Add the Video Bridge FU-07/FU-09 promotion-evidence harness (#11656) — a frozen Zod manifest schema covering the 8 required scenario kinds (static scenes, rapid cuts, late facts, fades, blur, small text, close events, visual prompt injection) with a minimum of 3 repetitions per case, deterministic declarative fixture recipes (`videoBridgePromotionFixtures.ts`), a pure medians/p95 metrics aggregator, a pure FU-07/FU-09 promotion-verdict evaluator applying the ticket's exact thresholds (missing token usage always holds), a digest-only persistence layer that never retains raw media or raw model responses, and a versioned per-model promotion allowlist shipped empty with every model defaulting to `hold`. The FU-07/FU-09 promotion verdicts themselves remain HOLD — they require a real evidence run against real models on VPS 192.168.0.15. ([#12008](https://github.com/diegosouzapw/OmniRoute/pull/12008)) +- **feat(video bridge):** "embedded" transcript provenance can now be legitimately earned instead of merely asserted — a bounded, allowlisted (`mov_text`/`subrip`/`webvtt`) subtitle probe runs through the loopback-only Video Bridge broker (at most 2 streams, 10s subdeadline bounded by the request deadline, 256 KiB output, 4096-code-unit lines), normalized through a bounded, ReDoS-safe WebVTT parser and Zod-validated end to end. The adapter always resolves to an explicit `success`/`absent`/`transient_failure` outcome — a subtitle failure never breaks the visual description path, and only a fingerprint-verified broker response (never a caller-declared label) can produce embedded cues (#11659). ([#12011](https://github.com/diegosouzapw/OmniRoute/pull/12011)) +- Default new Antigravity-family connections (agy CLI imports and Antigravity OAuth connects) to model auto-sync, so live model discovery lands in the synced catalog and `/v1/models` picks up freshly released upstream models (e.g. Gemini 3.7 Flash tiers) without code changes. Existing connections keep their current setting; the per-connection dashboard toggle remains the opt-out. (#11685 — thanks @MumuTW) ([#11887](https://github.com/diegosouzapw/OmniRoute/pull/11887)) +- **feat(ui):** enable React Compiler (`reactCompiler: true` + `babel-plugin-react-compiler`) for automatic memoization at build time ([#11783](https://github.com/diegosouzapw/OmniRoute/pull/11783)) — thanks @jonlwheat2-gif +- **feat(providers):** add SeekAi (`seekai.cc`) as an OpenAI-compatible New-API gateway — catalog id `seekai` (alias `ska`), `https://seekai.cc/v1`, live `/v1/models` via `passthroughModels`, aggregator-list membership so New-API balance detection can opt in. No referral/aff codes. ([#11786](https://github.com/diegosouzapw/OmniRoute/issues/11786)) ([#12557](https://github.com/diegosouzapw/OmniRoute/pull/12557)) — thanks @HouMinXi +- **feat(zai):** add GLM-5.3-Flash Coding Plan support (1M context, 128K output, vision, `low|high|max` reasoning) and route `zai` GLM-5.3-family API-key traffic through the OpenAI-compatible Coding Plan endpoint with native thinking defaults ([#11801](https://github.com/diegosouzapw/OmniRoute/pull/11801)) — thanks @Neuron-Mr-White +- **feat(combo):** choose how combo models are ordered — manual, provider, score, or name — via a sort control in the dashboard builder, persisted in `config.modelSort` and re-applied on load and after add ([#11812](https://github.com/diegosouzapw/OmniRoute/pull/11812)) — thanks @maxmad64bis +- **feat(free):** custom models can be marked free-tier via `customModels[].isFree`; `isFreeModel()` is the first door and `hidePaidModels` respects it even for providers outside the free budget ([#11843](https://github.com/diegosouzapw/OmniRoute/pull/11843)) — thanks @maxmad64bis +- **feat(nodejs):** add `5dive` as a `configure` target — `omniroute configure 5dive` / `omniroute setup-5dive` write a 5dive auth profile that points an agent fleet's `claude` seats at OmniRoute, with the root-only write, the loopback-vs-`https` endpoint rule and the per-seat model pin handled explicitly ([#11852](https://github.com/diegosouzapw/OmniRoute/pull/11852)) — thanks @5dive-bot +- **feat(sse):** treat `max` as a first-class reasoning-effort tier and clamp per model family (GLM 5.1+/DeepSeek V4+/Kimi K3+ keep native `max`; o1/MiniMax/Grok/Muse Spark clamp to their upstream ceiling) ([#11875](https://github.com/diegosouzapw/OmniRoute/pull/11875)) — thanks @Chewji9875 +- **feat(providers):** the provider plugin manifest now advertises a `usage-fetch` capability for the 40 providers that have a wired usage/quota fetcher, so external dashboards can read it from `GET /api/v1/provider-plugin-manifest` instead of parsing `open-sse/services/usage.ts` after every release. Discovery only — no new fetcher, no quota change, and the Dashboard quota widget stays gated by `USAGE_SUPPORTED_PROVIDERS`. `USAGE_FETCHER_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/fetcherProviders.ts`) and is re-exported from `services/usage.ts`, keeping the manifest module a light leaf instead of pulling the ~490-module usage dispatcher into the manifest route. ([#11903](https://github.com/diegosouzapw/OmniRoute/pull/11903)) — thanks @maxmad64bis / @pacocartones +- **feat(plugins):** `OMNIROUTE_PLUGINS_DIR` sets the directory the runtime plugin scanner reads — and the root the plugin manager installs into — overriding the `HOME`-derived default, so a Docker/K8s deployment can point straight at its bind-mounted plugin tree instead of moving `HOME` just to relocate the scan path. An image that exports no home no longer scans `/tmp/.omniroute/plugins` in silence: the resolved directory is logged once at startup as `scanner.dir_resolved`, naming the input that won. Unset, behaviour is unchanged. Distinct from the CLI-only `OMNIROUTE_PLUGIN_PATH`, which finds `omniroute-cmd-*` command packages and never reached this scanner ([#11906](https://github.com/diegosouzapw/OmniRoute/pull/11906)) — thanks @amaleta / @pacocartones +- **feat(leases):** add an explicit owner-authenticated status action that returns only the active lease's privacy-safe configured connection and provider labels, with generation fencing and no credential or internal-id disclosure ([#11910](https://github.com/diegosouzapw/OmniRoute/pull/11910)) — thanks @KaspaPulse +- **feat(providers):** import-from-file modal shows per-row API errors and ships a downloadable CSV template ([#12071](https://github.com/diegosouzapw/OmniRoute/issues/12071)) ([#12504](https://github.com/diegosouzapw/OmniRoute/pull/12504)) — thanks @HouMinXi +- **feat(providers):** dashboard search matches connection name and `baseUrl` so imported OpenAI-compat nodes surface on the provider card ([#12108](https://github.com/diegosouzapw/OmniRoute/issues/12108)) ([#12495](https://github.com/diegosouzapw/OmniRoute/pull/12495)) — thanks @HouMinXi +- **feat(routing):** add a `score` Auto router strategy that selects the highest configured weighted score and reuses `explorationRate`. ([#12155](https://github.com/diegosouzapw/OmniRoute/pull/12155)) — thanks @jacobsparts +- Add a runtime feature flag to disable universal context handoffs globally without changing the default behavior. ([#12167](https://github.com/diegosouzapw/OmniRoute/pull/12167)) — thanks @jacobsparts +- **feat(providers):** the provider plugin manifest now also advertises a `usage-supported` capability for the 46 providers whose usage API is accepted by the server and Dashboard routes, so integrators can distinguish "the server will serve quota for this provider" from "a fetcher is wired" without reading TypeScript. Discovery only — no fetcher or quota change. `usage-fetch` resolves on id or alias (the usage dispatcher accepts both); `usage-supported` resolves on id alone, matching the runtime guard `USAGE_SUPPORTED_PROVIDERS.includes(providerId)`. `USAGE_SUPPORTED_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/supportedProviders.ts`) and is re-exported from `providers.ts`, mirroring the `fetcherProviders` leaf from #11903 and keeping the manifest a light module. ([#12214](https://github.com/diegosouzapw/OmniRoute/pull/12214)) — thanks @maxmad64bis +- **feat(rankings):** order Free Provider Rankings by what each provider actually served — `GET /api/free-provider-rankings?sortBy=reliability` and a "Most reliable first" toggle on the page. Providers with too few calls to state a success rate keep their score order below the measured ones; the default order is unchanged ([#12218](https://github.com/diegosouzapw/OmniRoute/pull/12218)). — thanks @maxmad64bis +- **perf(sse):** defer `cloneLogPayload()` in the structured SSE collector until after the `maxEvents`/`maxBytes` cap check, eliminating ~9,800 wasted `structuredClone` calls per streaming response (65–71% faster `push()`). Reducer snapshot isolation restored for OpenAI and Responses summaries ([#12241](https://github.com/diegosouzapw/OmniRoute/pull/12241)) — thanks @PauloHSOliveira ([#12243](https://github.com/diegosouzapw/OmniRoute/pull/12243)) +- **feat(usage):** Devin CLI agentic quota (Codeium seat-management GetUserStatus) and OpenRouter key limits plus account credits now surface in Provider Limits ([#12256](https://github.com/diegosouzapw/OmniRoute/pull/12256) — thanks @Neuron-Mr-White) +- **feat(settings):** persist `headroomUrl` through Settings so status/start use the operator URL instead of only `HEADROOM_URL` ([#12306](https://github.com/diegosouzapw/OmniRoute/issues/12306)) ([#12487](https://github.com/diegosouzapw/OmniRoute/pull/12487)) — thanks @HouMinXi +- **feat(auto-combo):** Auto-Combo scoring can now weigh how often a provider/model has actually succeeded. The engine already carried that number on every candidate — 24 hours of usage history behind a ten-sample floor, real-time metrics otherwise — and the scoring function never read it, while the weight table described `stability` as if it did. `reliability` (`1 - failureRate`, with the same field precedence and the same rate-bounding the speed ranking already uses, so a corrupt reading means "nothing observed" rather than "fails every call") is now a declared factor shipping at weight `0`, so routing is unchanged until an operator gives it one, and the `stability` description now matches what that factor computes ([#12317](https://github.com/diegosouzapw/OmniRoute/pull/12317)) — thanks @maxmad64bis +- **feat(routing):** With `freeAccessPolicy: "strict"`, the read-only candidate listing (`GET /v1/auto-combo/{channel}/candidates`) no longer hides the candidates the zero-cost guard excludes — the same read-only transparency the resilience filter already honours (#9133). Each candidate now carries `freeAccessExclusion` saying why it would be kept out, and it tells an exhausted allowance apart from a quota reading that never arrived or went stale, which used to look identical from the outside. Routing is unchanged: the listing reports, it never enforces. The separate `excludeTosAvoid` guard still drops its candidates without a reason; that gap is now documented rather than closed ([#12319](https://github.com/diegosouzapw/OmniRoute/pull/12319)) — thanks @maxmad64bis +- **feat(radar):** The Radar catalog table now shows two facts it was already receiving from the feed and dropping on the floor: the per-model rate limits (requests and tokens, per minute and per day) in a new column, and a badge when a provider's terms state it may train on the prompts you send. A limit of zero renders as zero rather than "rate-only" — for a ceiling those are opposite facts — and a model with no training statement gets no badge, because an absent statement is not a guarantee ([#12320](https://github.com/diegosouzapw/OmniRoute/pull/12320)) — thanks @maxmad64bis +- **feat(radar):** explain Community, single-use, contributor, supporter, recovery, abuse, offers, and privacy rules before either Radar activation action, and remove the superseded fixed-PR grant promise from every UI locale ([#12342](https://github.com/diegosouzapw/OmniRoute/pull/12342)) +- **feat(gamification):** the dashboard Profile page now shows the real daily streak — `/api/gamification/level` returns `streak: { current, longest }` (per key with `apiKeyId`, operator-wide maximum otherwise) and the streak card reads it instead of a hard-coded 0 (#2403) ([#12377](https://github.com/diegosouzapw/OmniRoute/pull/12377)) — thanks @pacocartones +- **feat(gamification):** the dashboard leaderboard now shows each API key's display name under the Name column instead of a truncated key id; `GET /api/gamification/leaderboard` attaches `name` per entry (name only — no key material), while the shared ranking helper and the federation leaderboard stay id-only — thanks @pacocartones ([#12385](https://github.com/diegosouzapw/OmniRoute/pull/12385)) +- **feat(gamification):** enforce the documented 1000 XP/min per-API-key anti-cheat rate limit on the XP award path; over-limit awards are logged and skipped instead of persisted, and the sliding window now matches the timestamp format stored in `xp_audit_log` ([#2403](https://github.com/diegosouzapw/OmniRoute/issues/2403)) ([#12390](https://github.com/diegosouzapw/OmniRoute/pull/12390)) — thanks @pacocartones +- **feat(admin):** localize the gamification anomalies page — the loading state, the Status column and the Suspicious badge now come from the `common` catalog (new `common.suspicious` key propagated to every locale) — add it to the Gamification sidebar group as `gamification-admin` (`/dashboard/gamification/admin`), and expose the loading and empty states as polite `role="status"` live regions (#12401 — thanks @pacocartones) +- **feat(providers):** skip GitHub combo members missing from the live synced catalog, and drop Copilot models that are policy-disabled or hidden from the model picker ([#12473](https://github.com/diegosouzapw/OmniRoute/pull/12473)) — thanks @RaviTharuma +- **feat(i18n):** locale-expansion tooling — `npm run i18n:add-locale` adds a language to config, dashboard, docs mirrors, CLI, README/indexes and site in one command; browser, CLI and cookie detection resolve `uk`, `fil`/`tl`, `zh-Hant` (and the retired legacy `in` → `id`) via config aliases; `config/i18n.json` now ships in the npm package so the published CLI can read it; new real-translation ratio gate (`npm run i18n:check-ratio`, advisory) with a per-locale ratchet baseline; `run-translation --adopt` restores `.i18n-state.json`; `sync-language-bars` generates 🌐 bars from config; `validate_translation.py` loads its allowlist again. Retires the duplicate `in` locale (Indonesian mislabelled as Hindi) — 42 honest locales; saved `NEXT_LOCALE=in` / `OMNIROUTE_LANG=in` keep working. (#12496) +- **feat(api):** Emit gateway-measured `tokens_per_second` (TTFT excluded) on streaming usage and `X-OmniRoute-Tokens-Per-Second` when first-token latency is known ([#12616](https://github.com/diegosouzapw/OmniRoute/issues/12616)) ([#12631](https://github.com/diegosouzapw/OmniRoute/pull/12631)) — thanks @RaviTharuma +- **feat(opencode-plugin): map gateway cost/usage/tok/s onto OpenCode inference payloads** — the official plugin copies `X-OmniRoute-Response-Cost`, token counts, `X-OmniRoute-Tokens-Per-Second` / `usage.tokens_per_second`, TTFT, and the winning `X-OmniRoute-Model` onto the JSON/SSE body OpenCode already consumes. Missing tok/s is left unset (never `tokens / latency`). (#12636) — thanks @RaviTharuma +- **feat(combo):** add combo strategy `quota-weighted`: skip exhausted accounts, then weighted-draw by leftover / in-flight load; existing conversations stay pinned until the account is empty ([#12789](https://github.com/diegosouzapw/OmniRoute/pull/12789)) — thanks @HouMinXi +- **feat(models):** Account live listings become the chat catalog source for Claude, Codex, Copilot, and AGY; public metadata only fills prices on IDs those accounts already list. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **fix(models):** Union `agy` and `antigravity` live catalogs so `agy/gemini-3.8-flash-high` is not rejected after the prefix folds to `antigravity`. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **feat(opencode):** Add `@omniroute/opencode-plugin-v2`, an opencode v2 plugin that gives a v2 install a route to an OmniRoute gateway: it publishes models, combos and auto-combos into the host catalog, refreshes them lazily behind a 300s TTL, and keeps serving the last known catalog from an on-disk snapshot when the gateway is unreachable. The v1 plugin (`@omniroute/opencode-plugin`) is untouched and keeps working as before. The plugin adapts to the host instead of assuming one shape: it reads the shape the host seeds into the catalog draft and publishes the `api` block, the pre-`api` top-level fields, or both. The gateway key comes from the host credential store when one is connected, so no secret needs to sit in a config file; a source the gateway refuses is named in a warning instead of silently degrading the catalog to raw model ids; and tool schemas bound for a Gemini model are stripped of the JSON-Schema keywords Gemini rejects, so tool calling survives that route ([#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870)) — thanks @maxmad64bis +- feat(services): show sanitized CLIProxyAPI account health from its authenticated management API without exposing credentials, file paths, or raw account metadata (#6342) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add AnySearch free web search + URL extract (webFetch) with typed results, credential validation, REST routing, and MCP selection - fallback-only ([#11690](https://github.com/diegosouzapw/OmniRoute/pull/11690)) — thanks @Xxx91n +- **feat(dashboard):** display clamped `[0, 100]%` cached input token ratio in request logs table ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) — thanks @b3nw +- **feat(catalog):** add `OMNIROUTE_DISABLE_THINKING_LEVEL_VARIANTS` feature flag to optionally filter out thinking level variants from model catalog ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) — thanks @b3nw +- **feat(dashboard):** continuously export call logs to external analytics stores. A pluggable destination registry ships the full Logs-tab record set on an hourly `JobRegistry` cron, with a persisted per-destination cursor, batched inserts, a config UI rendered from each destination's own field descriptors, and a REST layer (`/api/log-export/*`) for CRUD, a connection test, and an on-demand run. A destination can opt into `includeBodies` to also ship the request and response payloads shown in the Logs detail pane, including the client and provider views of each call; this is off by default, and payloads inherit the dashboard's PII sanitisation, secret redaction and `noLog` handling. Google BigQuery is the first destination, using a service-account key stored encrypted at rest and streaming inserts keyed by call-log id, into a table that is day-partitioned on `timestamp` and clustered on `api_key_name`, `provider`, `model` and `status`. ([#11945](https://github.com/diegosouzapw/OmniRoute/pull/11945)) — thanks @dpozimski +- **feat(providers):** add MaxAI as a signed, OpenAI-compatible provider serving its 13 paid chat models (GPT-5.6 / Luna / Thinking, Claude 5 Sonnet, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite, Grok 4.1-fast / 4.5, DeepSeek V3.2 / R1, Llama 3.3 70B) through OmniRoute's `/v1` endpoint, with per-request HMAC-SHA1→SM3→AES request signing, live model + context-window discovery from `/models/get_config`, and prompted tool-calling translated to OpenAI `tool_calls` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI vision input — image_url content parts are forwarded inline in `message_content` to the 6 vision-capable models (GPT-5.6 / Luna / Thinking, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite) ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI image generation — 6 image models (gpt-image-1, dall-e-3, flux-1-schnell/dev/pro, sd3-medium) exposed through `POST /v1/images/generations` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI document RAG — inline base64 file/document attachments are uploaded to MaxAI (content-addressed `doc_id`) and attached to the chat via `doc_list` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** browserless MaxAI onboarding — email device-pair login (`/api/providers/[id]/login`) and signed access-token refresh, so a connection can be created and kept fresh without a real browser or Google OAuth ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** per-provider TLS impersonation profile (MaxAI presents a Windows Firefox-150 client fingerprint) so its bot-sensitive endpoints accept OmniRoute traffic ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** add **Nimble** as a web-search and web-fetch provider (`nimble-search`) — `/v1/search` routes to Nimble's search API at `lite` depth (locale, freshness and include/exclude domain filters mapped onto the shared request contract), and `/v1/web/fetch` routes to Nimble Extract, which covers all four fetch formats (`markdown`, `html`, `links`, `screenshot`) from a single call. One API key serves both surfaces. ([#11620](https://github.com/diegosouzapw/OmniRoute/pull/11620)) — thanks @wildcard +- The npm publish is automatic again, through npm Trusted Publishing (OIDC): the hosted `stage-npm` job publishes with a short-lived credential minted from GitHub's id-token — no `NPM_TOKEN`, no 2FA prompt, provenance attached. `publish_mode=staged` (owner approves with 2FA) and `direct` (token) remain available on `workflow_dispatch`. ([#11931](https://github.com/diegosouzapw/OmniRoute/pull/11931)) +- **feat(providers):** Add **Opper** as an API-key gateway provider — EU-hosted AI gateway with 700+ models from 30+ providers behind one OpenAI-compatible API and one key (`OPPER_API_KEY`); model ids use `provider/model` format (e.g. `anthropic/claude-sonnet-4-6`, `openai/gpt-5`); live model catalog at `https://api.opper.ai/v3/compat/models`; entry mirrors `requesty` (same shape, `passthroughModels: true`, no static seed) ([#11629](https://github.com/diegosouzapw/OmniRoute/pull/11629)) +- **feat(dashboard):** the `/dashboard/orchestration` snapshot hook now subscribes to the `agents` WebSocket channel (`agent.task.updated`) instead of `requests` as its refetch trigger, and relaxes its background poll from 5s to 30s while that WS connection is up — falling back to the tighter 5s cadence, reprogrammed live on any connect/disconnect transition, whenever the socket is down. ([#12409](https://github.com/diegosouzapw/OmniRoute/pull/12409)) +- **feat(dashboard):** Orchestration canvas quick wins — search box plus state/source/provider filter chips with a one-click clear, and per-source collapse/expand, all reflected in the URL so a filtered/collapsed view is shareable and survives a refresh; the detail drawer gained a "copy trace JSON" action and hardened error/empty-state and accessibility handling; the Agents-tab edges now animate traveling particles along active (running) connections; and the canvas node/edge status colors moved off fixed hex values onto theme-aware `--orch-status-*` CSS custom properties, so they adapt correctly to light/dark mode. ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- **chore(dashboard):** Orchestration UI hardening pass and the missing component/model test coverage it called for — `OrchestratorNode`/`ActivityNode`/`OverflowNode` rendering, the `?node=`/overflow-click page routing, the Agents-tab orchestrator-click no-op and `showCompleted` toggle, and the overview kanban's done-column sort order (#12270, #12271). ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- New `/dashboard/orchestration` page: live unified view of everything running — Cloud Agent, A2A and Conductor as a real-time graph (Agents tab), the combo cascade (Routing tab, reusing the Combo Live Studio) and a state kanban (Overview tab), with a detail drawer (trace, cost, approve/cancel). Read-only over existing APIs — no new backend. Canvas concept credit: PR #11815 design ([#12261](https://github.com/diegosouzapw/OmniRoute/pull/12261)) +- **feat(dashboard):** new "History" tab on `/dashboard/orchestration` — an Airflow-style grid of finished runs over a 24h/7d/30d preset window, one row per (source, identity), clicking a cell opens the existing detail drawer. It is backed by real persistence: A2A task lifecycle transitions are now written to the `a2a_tasks` table (purged after 30 days, configurable via `OMNIROUTE_A2A_HISTORY_RETENTION_DAYS`) and served by the new `GET /api/a2a/tasks/history` listing endpoint, with the task-detail route falling back to persisted history once a run leaves the in-memory snapshot. Conductor runs stay remote and are not persisted locally — the tab says so instead of silently omitting them. ([#12479](https://github.com/diegosouzapw/OmniRoute/pull/12479)) +- **feat(dashboard):** the orchestration detail drawer gained a "Repeat" action for Cloud Agent, A2A and Conductor tasks — a two-click confirm (click once to arm, click again within the confirm window to fire) re-submits the original prompt/input as a new run. The button is disabled with an explanatory tooltip whenever the original input can't be recovered from the loaded task detail (e.g. it never carried a prompt, or the detail failed to load). Two limitations of the A2A variant, by design: it targets the `/a2a` JSON-RPC endpoint, which authenticates with an API key only (`REQUIRE_API_KEY=true` or a configured `OMNIROUTE_API_KEY` makes a dashboard-session repeat answer `HTTP 400` — surfaced verbatim in the drawer's error line, never as a success), and the `message/send` call is SYNCHRONOUS: the POST blocks for the whole skill run, so the success confirmation only appears once the repeated task finishes. A dashboard-authenticated A2A creation path is deliberately left to a follow-up — widening the endpoint's auth posture is an operator decision, not a side effect of this feature. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(a2a):** A2A task execution now records which memories were consulted for the task's last user message as `metadata.memoryHits` (id/key/type/content-snippet) plus a `memory_hits` history event, purely for observability — the retrieved memory is never injected into a skill's prompt or behavior. Gated by the `OMNIROUTE_A2A_MEMORY_HITS` kill-switch (default enabled; set to `0` to skip the recall lookup entirely). The drawer's new "Memory used" section lists these hits for a2a tasks and is omitted whenever there are none. Known limitation: recall only resolves under the keyless posture — a keyed caller's task owner is a SHA-256 prefix of the API key, while memory rows are keyed by the database api-key id, and no hash→id lookup exists today, so the hit list stays empty for keyed callers. The recorded hits are also kept out of the task's own `input` (and therefore out of the persisted input and of the "Repeat" request body), so repeating a task never re-sends the previous run's memory snippets. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(providers):** add a Perplexity Agent API provider (`perplexity-agent` / `pplx-agent`) for Perplexity `/v1/responses`, including the documented Anthropic, OpenAI, Google, xAI, DeepSeek, Z.AI, Moonshot/Kimi, NVIDIA, and Perplexity model IDs plus Anthropic-model `max_output_tokens` compatibility. ([#12103](https://github.com/diegosouzapw/OmniRoute/pull/12103)) — thanks @AIB1TAL0S +- **feat(providers):** add RPD (Requests Per Day) limit to provider rate limit overrides across UI, schemas, DB, and i18n ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) — thanks @b3nw +- **feat(providers): add UC Direct (uncensored.com Developer API), the metered OpenAI-compatible surface.** A standard OpenAI-compatible passthrough (default executor) for uncensored.com's official REST API at `https://api.uncensored.com/api/v1`: `X-api-key` auth (never-expiring `uai_sk_live_` key), `POST /chat/completions` with streaming SSE and native tool-calling, and the full live metered catalog (82 models across 15 providers, discovered from the public `GET /v1/models`). Registered as provider `uc-direct` (alias `ucd`). Complements the un-metered `uc` persona provider — same models, metered credits and a plain API key instead of a subscription session. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(providers): add UC (uncensored.com), the un-metered subscription "persona" chat as an OpenAI-compatible provider.** A WebSocket web-app port: a durable Clerk credential mints a short-lived session token per connect (browserless — no API key), driving UC's persona socket. Ships the browserless email-code login (request → verify → harvest), the 19 verified persona models (Claude Opus, Gemini, Grok, GLM, Kimi, DeepSeek, MiniMax, incl. the uncensored variants), prompted `` tool-calling with a per-model code-style dialect + auto-cure retry for guardrailed models, live ``/reasoning split, streaming + non-streaming OpenAI responses, and full quota/auth error surfacing (paywall / message-limit / rate-limit → 429, invalid session → 401 re-login). Full multimodal parity via the persona blob-upload layer: **vision** (image input, 15 vision-capable models), **document RAG** (PDF/doc upload, server-side extraction), **image generation** (22 models), **video generation** (14 models, async signed-url → poll), and **TTS** (streaming MP3). Registered as provider `uc` (alias `ucn`). The metered OpenAI-compatible Developer API is a separate `uc-direct` provider. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(logs):** add copy-all button to request detail modal ([#11083](https://github.com/diegosouzapw/OmniRoute/pull/11083)) — thanks @AndrianBalanescu +- **feat(check):** make serviceKinds required and add the reverse-walk provider consistency gate ([#11392](https://github.com/diegosouzapw/OmniRoute/pull/11392)) — thanks @Tushar49 +- **feat(providers):** only advertise verified-working connections by default ([#11448](https://github.com/diegosouzapw/OmniRoute/pull/11448)) — thanks @alltomatos +- **feat(resilience):** add hierarchical concurrency admission ([#11493](https://github.com/diegosouzapw/OmniRoute/pull/11493)) +- **feat(providers):** derive + surface expiry for JWT-bearing web cookies (#11497) ([#11505](https://github.com/diegosouzapw/OmniRoute/pull/11505)) — thanks @oyi77 +- **feat(tunnels):** support named cloudflared tunnels via CLOUDFLARED_CONFIG ([#11561](https://github.com/diegosouzapw/OmniRoute/pull/11561)) — thanks @ggiak +- **feat(compression):** make proactive context-compression threshold a live setting ([#11564](https://github.com/diegosouzapw/OmniRoute/pull/11564)) — thanks @ragnar-claude +- **feat(resilience):** add provider peak-hour protection ([#11622](https://github.com/diegosouzapw/OmniRoute/pull/11622)) — thanks @Neuron-Mr-White +- **feat(providers):** add 1min.ai provider ([#11631](https://github.com/diegosouzapw/OmniRoute/pull/11631)) — thanks @zero-executioner +- **feat(dashboard):** collapsible JSON tree viewer for request/response payloads ([#11703](https://github.com/diegosouzapw/OmniRoute/pull/11703)) — thanks @hartmark +- **feat(routing):** add LiquidAI LFM2.5-2.6B free tier via OpenRouter ([#11752](https://github.com/diegosouzapw/OmniRoute/pull/11752)) — thanks @brick30llc-ctrl +- **feat(pricing):** add GLM-5.3-Flash pricing, model specs, and catalog registration ([#11830](https://github.com/diegosouzapw/OmniRoute/pull/11830)) — thanks @AndrianBalanescu +- **feat(routing):** add relayMode for schema-locked context handoffs ([#11839](https://github.com/diegosouzapw/OmniRoute/pull/11839)) — thanks @raheemuddin786 +- **feat(config):** add support for runtime system prompt configuration and hot-reloading ([#11841](https://github.com/diegosouzapw/OmniRoute/pull/11841)) — thanks @raheemuddin786 +- **feat(routing):** expand connection-aware quota prefilter across combo strategies (#11682) ([#11850](https://github.com/diegosouzapw/OmniRoute/pull/11850)) — thanks @HouMinXi +- **feat(models):** reactive discovery sync after Antigravity model-not-found 404 ([#11888](https://github.com/diegosouzapw/OmniRoute/pull/11888)) — thanks @MumuTW +- **feat(models):** auto-aliases and token limits from synced Antigravity discovery ([#11889](https://github.com/diegosouzapw/OmniRoute/pull/11889)) — thanks @MumuTW +- **feat(settings):** raise sticky round-robin limit caps to 1000 ([#12015](https://github.com/diegosouzapw/OmniRoute/pull/12015)) — thanks @adivekar-utexas +- **feat(resilience):** operator-configurable global credential health check interval ([#12043](https://github.com/diegosouzapw/OmniRoute/pull/12043)) — thanks @adivekar-utexas +- **feat(quota):** use official OpenCode Go usage API ([#12124](https://github.com/diegosouzapw/OmniRoute/pull/12124)) — thanks @ddarkr +- **feat(resilience):** default the credential health check sweep to 60 minutes ([#12138](https://github.com/diegosouzapw/OmniRoute/pull/12138)) — thanks @adivekar-utexas +- **feat(quality):** new-code mode for the complexity and dead-code ratchets (Clean as You Code) ([#12142](https://github.com/diegosouzapw/OmniRoute/pull/12142)) +- **feat(dashboard):** orchestration canvas — unified model + snapshot hook (part 1/2) ([#12156](https://github.com/diegosouzapw/OmniRoute/pull/12156)) +- **feat(usage):** add Kilo Code balance and Kilo Pass quotas ([#12178](https://github.com/diegosouzapw/OmniRoute/pull/12178)) — thanks @killer30001000 +- **feat(providers):** refresh vendored ChatGPT Web connector to v4.0.7 ([#12181](https://github.com/diegosouzapw/OmniRoute/pull/12181)) — thanks @backryun +- **feat(providers):** manual "Clear cooldown" action in the cooling panel ([#12224](https://github.com/diegosouzapw/OmniRoute/pull/12224)) — thanks @patrykkopycinski +- **feat(providers):** restore ChatGPT Web via clean-room browser transport ([#12239](https://github.com/diegosouzapw/OmniRoute/pull/12239)) — thanks @backryun +- **feat(sse):** wire the PROVIDER_PROFILES window gate into the global provider cooldown ([#12247](https://github.com/diegosouzapw/OmniRoute/pull/12247)) +- **feat(providers):** modernize CLOVA Studio chat and embeddings ([#12277](https://github.com/diegosouzapw/OmniRoute/pull/12277)) — thanks @backryun +- **feat(browser):** adopt Obscura as primary headless browser engine with Chromium fallback ([#12286](https://github.com/diegosouzapw/OmniRoute/pull/12286)) — thanks @opensource-elearning +- **feat(sse):** full language parity for output styles (es/de/fr/it/ru/zh + autoDetect) ([#12289](https://github.com/diegosouzapw/OmniRoute/pull/12289)) +- **feat(quality):** complete test:scoped — --full map rebuild, stdin selection, CI loader parity (#8084 D1) ([#12353](https://github.com/diegosouzapw/OmniRoute/pull/12353)) +- **feat(providers):** refresh Fable, Cursor, and Devin catalogs ([#12367](https://github.com/diegosouzapw/OmniRoute/pull/12367)) — thanks @backryun +- **feat(video):** redact transcript text from logs and durable memory (#12150 P1) ([#12427](https://github.com/diegosouzapw/OmniRoute/pull/12427)) +- **feat(dashboard):** parent-link, genuine-continuation badge, and modal perf fixes ([#12448](https://github.com/diegosouzapw/OmniRoute/pull/12448)) — thanks @hartmark +- **feat(dashboard):** adaptive context-budget dial on compression panel ([#12488](https://github.com/diegosouzapw/OmniRoute/pull/12488)) — thanks @HouMinXi +- **feat(providers):** refresh Z.ai Web models and browser transport ([#12524](https://github.com/diegosouzapw/OmniRoute/pull/12524)) — thanks @backryun +- **feat(video):** redact raw client-snapshot transcript fields in the detailed log (#12150 P2) ([#12528](https://github.com/diegosouzapw/OmniRoute/pull/12528)) +- **feat(providers):** refresh NVIDIA hosted models ([#12538](https://github.com/diegosouzapw/OmniRoute/pull/12538)) — thanks @backryun +- **feat(quota):** Moonshot Open Platform balance and TPD lock for custom nodes ([#12590](https://github.com/diegosouzapw/OmniRoute/pull/12590)) — thanks @HouMinXi +- **feat(video):** redact transcript in the in-memory pending-request snapshot (#12430 item 6) ([#12596](https://github.com/diegosouzapw/OmniRoute/pull/12596)) +- **feat(dashboard):** link a log entry's Conversation Context to its owning conversation ([#12646](https://github.com/diegosouzapw/OmniRoute/pull/12646)) — thanks @hartmark +- **feat(catalog):** eligibility-gated free-tier bucket ([#12669](https://github.com/diegosouzapw/OmniRoute/pull/12669)) +- **feat(db):** fail-closed previous_response_id continuation for redacted video turns (#12150 P2b) ([#12707](https://github.com/diegosouzapw/OmniRoute/pull/12707)) +- **feat(gamification): pay the documented `streak_bonus` and `badge_unlock` XP rewards.** `XP_REWARDS` listed both rewards but the award pipeline never paid them: the private reward table in `events.ts` omitted them, `updateStreak()` did not report when a streak extended, and badge unlocks carried no XP. Every request that extends a daily streak now pays `streak_bonus × streak length` once per UTC day (guarded by a same-day `xp_audit_log` check), and every badge unlocked through the pipeline pays `badge_unlock` once per badge (guarded by the `user_badges` primary key; `unlockBadge()` now reports whether it inserted). Bonus XP flows through the same `addXp` + level sync + global/weekly/monthly leaderboard path as action XP, so level-ups and rankings include it. The Radar supporter recognition unlock stays XP-free. (#12522 — thanks @pacocartones) +- **feat(providers):** advertise a `free-tier` capability in the provider plugin manifest for every provider with documented free models, so sidecars and dashboards can filter free-capable providers without reading the quota catalog ([#12786](https://github.com/diegosouzapw/OmniRoute/pull/12786)) — thanks @maxmad64bis +- **feat(providers):** Added EURouter as an OpenAI-compatible API-key gateway (`https://api.eurouter.ai/v1`), with live model discovery via `passthroughModels`. Its copy states that models are served by third-party upstreams listed per model, so an EU-based router is not read as EU data residency for inference. ([#13025](https://github.com/diegosouzapw/OmniRoute/pull/13025)) — thanks @ntdatt812 +- **feat(providers):** Added GreenPT as an OpenAI-compatible API-key provider (`https://api.greenpt.ai/v1`), with live model discovery via `passthroughModels`. No free-inference badge: the published docs describe a free API subscription billed per token, not a free tier. ([#13024](https://github.com/diegosouzapw/OmniRoute/pull/13024)) — thanks @ntdatt812 +- **feat(i18n):** 9 new locales — Greek (`el`), Croatian (`hr`), Serbian (`sr`), Lithuanian (`lt`), Estonian (`et`), Latvian (`lv`), Slovenian (`sl`), Maltese (`mt`), Irish (`ga`) — across the dashboard, docs mirrors, CLI, README and the site; OmniRoute now ships every official EU language (51 locales). (#13044) +- **feat(i18n):** 8 new locales — Kannada (`kn`), Malayalam (`ml`), Odia (`or`), Punjabi (`pa`), Nepali (`ne`), Sinhala (`si`), Burmese (`my`), Khmer (`km`) — across the dashboard, docs mirrors, CLI, README and the site (59 locales). The translator now restores the ICU literal escape around angle placeholders and splits oversized docs sections before translating. (#0000) ([#13660](https://github.com/diegosouzapw/OmniRoute/pull/13660)) +- feat(providers): **list Agnes 3.0 Flash as the current free chat model, drop retired 1.5 Flash, add Image 2.0/2.5 Flash plus Video 2.5/2.5 Flash, and discover the live `/v1/models` catalog (including the CN host `api.agnes-ai.cn`).** `agnes-1.5-flash` now forwards to `agnes-3.0-flash`. Video 2.5 polls `GET /v1/videos/{id}` (not the V2.0 `/agnesapi` contract). Live `/v1/models` (2026-09-09) no longer serves 1.5; the wiki marks it deprecated. 3.0 Flash is 512K context / 65,536 max output, same window as 2.5. CN-region keys use the existing per-connection base-URL field, default stays `apihub.agnes-ai.com`. ([#13120](https://github.com/diegosouzapw/OmniRoute/pull/13120)) — thanks @HouMinXi +- **feat(sse):** Codex and OpenAI catalogs list GPT-6 Astra with effort aliases (`-low` through `-ultra`); Codex CLI identity pins `@openai/codex@0.153.4` in lockstep with the image ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **feat(dashboard):** the orchestration History tab gained a "Compare runs" mode — toggling it turns each grid cell into a 2-item selection queue (a 3rd click drops the oldest pick), and picking two cells opens a side-by-side comparison panel instead of the usual detail drawer. The panel fetches both runs' detail the same way the drawer does (falling back to persisted history once a run leaves the live TTL window) and shows, per side: identity/source/state, start time, a signed `right - left` delta for duration/cost/event count, the event timeline aligned by index, and any memory hits. One side's fetch failing never blocks the other, and a delta is only ever computed when both sides have a finite value — otherwise it renders "—", never `NaN`. Comparing two runs from different sources or skills still works; a banner marks the deltas as informational rather than hiding them, since the two runs aren't a strict apples-to-apples pair. ([#12677](https://github.com/diegosouzapw/OmniRoute/pull/12677)) +- **feat(dashboard):** badge a conversation that never reached a clean stop ([#12717](https://github.com/diegosouzapw/OmniRoute/pull/12717)) — thanks @hartmark +- **feat(release):** reconcile-changelog tool + version-anchored fragment aggregation ([#12987](https://github.com/diegosouzapw/OmniRoute/pull/12987)) +- **feat(mcp):** carry context_length through MCP list_models_catalog projection (#12776) ([#13406](https://github.com/diegosouzapw/OmniRoute/pull/13406)) — thanks @KooshaPari +- **feat(cli):** make startup readiness budget configurable (#13369) ([#13433](https://github.com/diegosouzapw/OmniRoute/pull/13433)) — thanks @KooshaPari + ### 🐛 Bug Fixes +- **security(streaming):** sanitize generic mid-stream error messages before emitting OpenAI, + Responses, or Claude SSE failure frames and before diagnostic logging, while preserving raw + failures for internal classification and keeping client disconnects out of provider failure state. ([#12457](https://github.com/diegosouzapw/OmniRoute/pull/12457)) +- **fix(claude):** `blockExtraUsage: false` no longer lets 5h quota preflight skip the account; extra usage is billed after the session bar is gone, so the request must reach Anthropic ([#12803](https://github.com/diegosouzapw/OmniRoute/pull/12803)) — thanks @HouMinXi +- Sanitize HuggingChat conversation-creation and message-send transport failures before they reach client error bodies or provider logs. ([#12467](https://github.com/diegosouzapw/OmniRoute/pull/12467)) +- **fix(dashboard):** Keep local and theme-aware provider SVG icons at a definite layout size so Chromium does not collapse them to 0×0 after the v3.8.50 image-rendering change ([#12054](https://github.com/diegosouzapw/OmniRoute/pull/12054)) — thanks @ponkcore +- **fix(security):** Sanitize provider and runtime failures before public API, SSE and MCP responses and before persistent request, proxy and usage logs, preventing credentials, stack traces and host filesystem paths from crossing those boundaries while preserving stable error codes and useful diagnostics. ([#12506](https://github.com/diegosouzapw/OmniRoute/pull/12506)) +- **fix(cli):** `omniroute tunnel create` no longer crashes with `Cannot read properties of undefined (reading optsWithGlobals)` — removed the duplicate positional argument that caused Commander.js to misalign the action callback parameters ([#12295](https://github.com/diegosouzapw/OmniRoute/issues/12295)) ([#12368](https://github.com/diegosouzapw/OmniRoute/pull/12368)) — thanks @gonisulaimann +- Stop advertising Gemini Live-only models as supported audio endpoints until OmniRoute proxies the bidirectional Live protocol. ([#11710](https://github.com/diegosouzapw/OmniRoute/pull/11710)) — thanks @RaviTharuma +- **fix(kie):** reroute `flux/kontext` off the KIE Market `createTask` flow — it is catalogued with `isMarket: true` but has no Market catalog page, so KIE rejected it with "model name not supported"; it now hits the dedicated `POST /api/v1/flux/kontext/generate` / `GET /api/v1/flux/kontext/record-info` endpoints instead (#11296). ([#11985](https://github.com/diegosouzapw/OmniRoute/pull/11985)) — thanks @hartmark +- **fix(kie):** correct 12 more KIE Market catalog ids that were sent to `createTask` unchanged but diverge from KIE's documented upstream `model` values — GPT Image 2 T2I/I2I (drops the `gpt/` prefix), GPT Image 1.5 T2I/I2I (`gpt-image/` namespace), Seedream 5.0 Lite T2I/I2I (drops the `.0`), all 4 Flux 2 variants (`flux-2/` namespace, generic variant renamed `flex`), and Wan 2.7 Image / Image Pro (dash instead of dot) — each verified individually against the literal example request published on docs.kie.ai. `#11326`'s "everything else already matches" claim was wrong a second time (#11296); `z-image/4.0-*`/`z-image/4.5-*` and `flux/kontext` remain open, documented as unresolved in `KIE_MARKET_UPSTREAM_MODEL_IDS`'s comment pending further verification. ([#11667](https://github.com/diegosouzapw/OmniRoute/pull/11667)) +- **fix(dashboard):** `useApiKeySave.handleSaveApiKey` no longer forces a full upstream `/models` catalog sync on every non-curated provider connection save — callers can now pass `skipModelSync: true` to opt out, so a workflow that only wants to add one manual model no longer floods the provider's available-models list with hundreds/thousands of synced entries. The flag is a client-side intent signal only and is stripped before the connection payload is POSTed to `/api/providers`; default behavior (full sync on save) is unchanged when the flag is omitted (#11324) ([#11664](https://github.com/diegosouzapw/OmniRoute/pull/11664)) +- **Provider connections:** keep `tokenExpiresAt` when a connection is created. The create-path allowlist omitted it, so every insert stored NULL and the dashboard token badge could read a fresh connection as expired until its first background refresh ([#11368](https://github.com/diegosouzapw/OmniRoute/pull/11368)). — thanks @ntdatt812 +- **fix(combos):** the combo builder's precision-select, global-model-search, and manual-entry flows now serialize a model step's `model` string using the provider's already-computed routing-alias prefix (e.g. `oc/`) instead of rebuilding it from the raw canonical `providerId`, fixing the no-auth "OpenCode Free" provider (`opencode`) being routed to the unrelated paid "OpenCode Zen" provider (`opencode-zen`) because `opencode` doubles as a manual routing-prefix override ([#11433](https://github.com/diegosouzapw/OmniRoute/issues/11433)). ([#11670](https://github.com/diegosouzapw/OmniRoute/pull/11670)) +- **fix(radar):** the catalog feed cache now keeps `generatedAt`, the date the feed's data was built, next to `fetchedAt`, the date this install downloaded it (#11435). The feed schema requires that date and the sync path validates it, but the cache dropped it — so a feed fetched minutes ago and one carrying weeks-old figures looked identical to everything downstream, including the dashboard's "Last fetched" line. `getRadarCatalog().meta` and `GET /api/radar/status` now report both dates, the latter as its own field rather than folded into `version` — and omitted entirely for the offers and intel caches, which keep no build date, where a `null` would read as "unknown" rather than "never stored". The dashboard still shows only the fetch time; surfacing the build date there needs a new translated label and is left to a follow-up. Rows cached before migration 163 read back as `null`: unknown stays unknown instead of borrowing the fetch time. The referrals cache has persisted the same date since migration 142. — thanks @maxmad64bis +- **fix(cli):** postinstall no longer fills `JWT_SECRET` and `API_KEY_SECRET` in the installed package's `.env` (#11436). `.env.example` ships both blank on purpose: the server restores them from its durable store, or generates and persists them there, in `ensureSecrets()`. Pre-filling them defeated that — the file lives inside the package directory, so `npm i -g` replaced it and postinstall wrote _different_ values, while `ensureSecrets()` (which only acts on an empty variable) never got to restore the real ones. Both secrets rotated silently on every update, invalidating dashboard sessions and API-key CRCs. `STORAGE_ENCRYPTION_KEY` left the same list for the same reason in #1622; its comment pointed at a function that no longer exists and now names the real provisioning path. — thanks @maxmad64bis +- **fix(cli):** the CLI now says when a loaded `.env` lives inside the installed package directory (#11437). It already announces every env file it reads, without distinguishing the ones that survive an update from the one that does not: `npm i -g` replaces the package directory wholesale, so values set there are gone at the next update, silently. The warning names the durable path to move them to, and fires only when that file actually supplied a value — a file entirely shadowed by a durable one supplied nothing. A development checkout stays silent: there the same path is stable and documented in `SETUP_GUIDE.md`. — thanks @maxmad64bis +- **fix(free-models):** the shared `isFreeModel()` predicate no longer reports catalog entries marked `freeType: "discontinued"` as free, so `hidePaidModels` can't route to Pollinations' seven premium models that now require a paid key ([#11441](https://github.com/diegosouzapw/OmniRoute/pull/11441)) — thanks @maxmad64bis +- fix(ui): let AnySearch use the normal provider-icon fallback when LobeHub has no matching icon (#11449) +- Fixed the v3.8.50 Costs and Analytics dashboards so flat-rate Claude Code usage can be shown as an explicitly requested token-price estimate without changing default billed-cost semantics. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed archived usage retention so each request is priced individually instead of pricing a day's summed tokens once, which understated archived cost whenever a day mixed cache-heavy and ordinary requests. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed the Costs dashboard so it discloses when displayed figures include flat-rate token-price estimates instead of labelling them as billed spend, using the flag the analytics API already returns; the month-end projection and the CSV/JSON exports carry the same marker, and billed-cost mode is unchanged. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- **fix(combo):** attach the same combo-diagnostics trace (`poolSize`/`attemptOrder`/`excluded`/`terminalReason`, plus `x-omniroute-combo-*` headers) to the round-robin strategy's and the nested pipeline/fusion runtime-unit loop's "Maximum combo retry limit reached" 503 that the priority-strategy path already attaches for the identical terminal condition — previously those two paths returned a bare, contextless 503 ([#11462](https://github.com/diegosouzapw/OmniRoute/issues/11462)). ([#11665](https://github.com/diegosouzapw/OmniRoute/pull/11665)) +- **fix(bun):** use native `bun:sqlite` in `bootstrap-env` and `sync-env` to avoid loading `better-sqlite3` N-API addon during Bun startup ([#11468](https://github.com/diegosouzapw/OmniRoute/pull/11468)) — thanks @TheDemonTuan +- **fix(docker-bun):** make `Dockerfile.bun` install reliable with pre-install script helpers and native `bun:sqlite` smoke check ([#11470](https://github.com/diegosouzapw/OmniRoute/pull/11470)) — thanks @TheDemonTuan +- **fix(docker-bun):** remove every vendored `better-sqlite3` native addon from the Bun runtime image so startup cannot abort before the `bun:sqlite` fallback ([#11482](https://github.com/diegosouzapw/OmniRoute/pull/11482)) — thanks @TheDemonTuan +- **fix(autoCombo):** effort/alias model variants (`gpt-5.6-sol-xhigh`, `gpt-5.6`, cursor's `claude-4.6-opus-high`) inherit their base model's task fitness instead of falling to the wildcard 0.5 ([#11492](https://github.com/diegosouzapw/OmniRoute/pull/11492)) — thanks @MumuTW +- fix(db): rate-limit repeated Arena ELO leaderboard fetch-failure warnings instead of logging one per sync attempt (#11500) ([#11989](https://github.com/diegosouzapw/OmniRoute/pull/11989)) — thanks @hartmark +- **fix(db):** dedupe the raw `[Encryption] Decryption failed...` log line emitted by the lazy-decrypt views (`createLazyRowProxy`/`createLazyConnectionView`), which power `getProviderConnections()` and were re-triggering that line on every CredentialHealth/model-sync cycle for the same corrupt or stale-key credential — a fresh Proxy over a fresh row on every cycle meant the per-proxy memoization never suppressed it, unlike the dedup `decryptConnectionFields()` already had since [#9927](https://github.com/diegosouzapw/OmniRoute/issues/9927). Now shares that dedupe tracking so the line logs at most once per credential ([#11500](https://github.com/diegosouzapw/OmniRoute/issues/11500)). ([#11678](https://github.com/diegosouzapw/OmniRoute/pull/11678)) +- **fix(intelligence):** Arena ELO sync stores a synthesized base-model row for every effort/harness variant (`claude-opus-5-max` → `claude-opus-5`, `gpt-5.6-sol-xhigh (codex-harness)` → `gpt-5.6-sol`), so bare model requests reach the synced intelligence layer instead of the static fallback table; the hand-maintained `MODEL_ALIAS_MAP` — which collapsed generations (`gpt-5.5` → `gpt-5`) — is gone ([#11506](https://github.com/diegosouzapw/OmniRoute/pull/11506)) — thanks @MumuTW +- **fix(autoCombo,sse):** catalog hygiene — the static task-fitness table no longer ranks retired models above live ones (dead rows dropped, patterns matched on segment boundaries), the 7 deprecation aliases that forwarded to retired or non-existent models now point at the vendors' published replacements, an alias is no longer rewritten when the serving provider offers the id as-is, and a new `check:model-lifecycle` gate diffs both tables against a vendor lifecycle snapshot ([#11507](https://github.com/diegosouzapw/OmniRoute/pull/11507)) — thanks @MumuTW +- **fix(sse):** stop dropping resolved `thoughtSignature` values on parallel (multi tool-call) turns sent to Gemini 3.x — the claude→gemini and openai→gemini translators previously kept the signature only on the *first* function call of a message, causing Gemini to reject subsequent calls in the same turn with HTTP 400 "Function call is missing a thought_signature"; each function call now keeps its own resolved signature ([#11510](https://github.com/diegosouzapw/OmniRoute/issues/11510)). ([#11669](https://github.com/diegosouzapw/OmniRoute/pull/11669)) +- Treat Docker publishing from a non-default release branch as an expected skip instead of a failed workflow, while preserving the guard that prevents it from overwriting the `next` channel. ([#11527](https://github.com/diegosouzapw/OmniRoute/pull/11527)) — thanks @pacocartones +- fix(search): prefer credentialed providers over duckduckgo-free fallback (#11524) — the fallback-only loop ran before the credentialed-providers loop in `executeWebSearch`, making configured providers unreachable when `duckduckgo-free` was available. ([#11565](https://github.com/diegosouzapw/OmniRoute/pull/11565)) — thanks @vsd2807 +- **fix(sse):** cap the upstream headers-wait phase for STREAMING requests to a client-realistic ceiling (110s, under Codex's own ~120s hard client-abort window) instead of the flat 10-minute `FETCH_TIMEOUT_MS` default — that default was 5x longer than the body-phase readiness watchdog's own adaptive bound, so a request whose upstream never returned any response at all (not even headers, e.g. a stalled NVIDIA target behind a tool-heavy Responses→Chat translation) kept the client connection alive on keepalives only, guaranteeing the client's own patience ran out first with an opaque 499 instead of OmniRoute detecting and failing the stall fast. Non-streaming requests are unaffected — they keep the existing flat default (`open-sse/utils/fetchStartTimeoutPolicy.ts`) (#11526) ([#11668](https://github.com/diegosouzapw/OmniRoute/pull/11668)) +- **fix(ci):** protocol-clients E2E harness boots the peer-stamped custom server (`run-next.mjs dev`) instead of the bare `next` CLI, so `LOCAL_ONLY` locality resolves from the real TCP peer stamp and `/api/mcp/audit` answers 200 (open bootstrap preserved via an `OMNIROUTE_E2E_BOOTSTRAP_MODE=open` credential clear in `run-next.mjs`, pinned to loopback `HOST`) instead of a deterministic 403 (#11535) ([#11549](https://github.com/diegosouzapw/OmniRoute/pull/11549)) — thanks @jonlwheat2-gif +- **fix(docs):** the Auto-Combo engine is described with the number of factors it actually declares. Four different counts were in circulation — 6, 9, 13 and 14 across `AGENTS.md`, four guides and six code comments — while `DEFAULT_WEIGHTS` declares 15. `check:docs-counts` now reads that number from the source, so adding a factor without updating the prose fails CI ([#11545](https://github.com/diegosouzapw/OmniRoute/pull/11545)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **fix(quota-share):** in-flight leases are per request, so an aborted request's slot ages out instead of being kept alive by later traffic on the same connection ([#11547](https://github.com/diegosouzapw/OmniRoute/pull/11547)) — thanks @abhisheksharma2411 +- The free-tier summary route now serves the refreshed Radar catalog when the feed is active — the same numbers the dashboard shows — and states which catalog answered plus its real build date, instead of always reporting release-frozen figures with a stale curation date. The supporter-key live feed stays reserved to authenticated callers of the instance. ([#11550](https://github.com/diegosouzapw/OmniRoute/pull/11550)) — thanks @maxmad64bis +- **sse:** fix LiveWS/embed-WS servers crashing at startup under the Node/tsx runtime — `liveServer.ts`, `embedWsProxy.ts` and `apiBridgeServer.ts` imported `@/shared/utils/httpClientAbortGuard` without the `.mjs` extension, so the client-abort crash guard added by [#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556) was unreachable and every dependent test failed with `ERR_MODULE_NOT_FOUND` ([#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556)). ([#11671](https://github.com/diegosouzapw/OmniRoute/pull/11671)) — thanks @jonlwheat2-gif / @yxyxy +- fix(chat): shed any of the matched provider-node's routing identifiers (public prefix or internal id) before model lookup, so composites like `//` no longer reach upstream verbatim (#11557) — thanks @maxmad64bis +- **fix(cache):** a semantic-cache entry now expires on its TTL instead of surviving until the next UTC midnight, and `dbEntries` stops counting expired rows ([#11573](https://github.com/diegosouzapw/OmniRoute/pull/11573)) — thanks @ntdatt812 +- **fix(api):** `/v1/models` schedules its stale-while-revalidate rebuild through Next's `after()` again, so a stale catalog reaches the client before the rebuild blocks the event loop ([#11574](https://github.com/diegosouzapw/OmniRoute/pull/11574)) — thanks @ntdatt812 +- **fix(check):** the `known-symbols` executor conformance gate awaits the now-async `getExecutor()`, so it stops reporting all 142 registered aliases as dead symbols and can detect a lazy import that fails ([#11582](https://github.com/diegosouzapw/OmniRoute/pull/11582)) — thanks @ntdatt812 +- **fix(pack):** register `bin/cli/utils/volatileEnvPath.mjs` in `PACK_ARTIFACT_REQUIRED_PATHS`, so the prune can no longer silently drop a module the published CLI imports on every boot ([#11588](https://github.com/diegosouzapw/OmniRoute/pull/11588)) — thanks @ntdatt812 +- **i18n(vi):** translate the five reliability-column strings on the free-provider rankings page, which rendered as `__MISSING__:` markers in the Vietnamese UI ([#11589](https://github.com/diegosouzapw/OmniRoute/pull/11589)) — thanks @ntdatt812 +- **resilience:** restore the expired-connection retry-budget probe in the token-health sweep — the `!isGitHubAccessTokenOnlyConnection` carve-out reintroduced by #11608 contradicted the boundary pinned by #11592, so a GitHub connection parked at `expired` with retry budget remaining was never probed and could never self-heal ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)). ([#11672](https://github.com/diegosouzapw/OmniRoute/pull/11672)) — thanks @jonlwheat2-gif / @ntdatt812 +- **fix(dashboard):** Provider Stats now displays combo success rates on their native 0–100 percentage scale instead of multiplying them twice ([#11595](https://github.com/diegosouzapw/OmniRoute/pull/11595)) — thanks @pacocartones +- **fix(providers):** route DuckDuckGo Web Chat through the verified `duck.ai` endpoint, refresh its free model catalog, and avoid forwarding unsolved anti-abuse challenges ([#11597](https://github.com/diegosouzapw/OmniRoute/pull/11597)) — thanks @solstxce +- **fix(ui):** Console log Refresh and Copy controls now expose localized accessible names, keep copy actions visible on keyboard focus, and announce copy completion safely ([#11599](https://github.com/diegosouzapw/OmniRoute/pull/11599)) — thanks @pacocartones +- Dropped the stale-`.eslintcache` `restore-keys` fallback from both "Restore ESLint file cache" steps in `ci.yml`, so the blocking `Lint` job can no longer be served per-file verdicts computed under a different lint config, suppressions file or lockfile. `quality.yml` had already dropped it in #11963; `ci.yml` — the workflow that actually gates PRs — had not (#11600). ([#11996](https://github.com/diegosouzapw/OmniRoute/pull/11996)) +- fix(ci): pass `--pass-on-unpruned-suppressions` in `run-eslint-json.mjs` so the CI Lint job no longer fails when a suppression is merely orphaned by a genuine fix, mirroring the identical fix already in `validate-release-green.mjs` (#11600) ([#11983](https://github.com/diegosouzapw/OmniRoute/pull/11983)) — thanks @hartmark +- fix(handoff): enforce provider allowlist for universal handoff (#11602) — universal handoff now skips summarization when the selected summary model's provider is not included in the configured provider allowlist. ([#11619](https://github.com/diegosouzapw/OmniRoute/pull/11619)) — thanks @vsd2807 +- **fix(dashboard):** Show a stable error state when Search Analytics returns an HTTP error or malformed data ([#11603](https://github.com/diegosouzapw/OmniRoute/pull/11603)) — thanks @pacocartones +- **test(gamification):** pin the aggregate profile level to the XP-derived semantics of #11604 — `getAggregateXp()` now derives `currentLevel` from the summed XP (`calculateLevel(sum)`), not `MAX(stored current_level)`, and the #3484 fixture levels are aligned with the XP curve ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)). ([#11676](https://github.com/diegosouzapw/OmniRoute/pull/11676)) — thanks @jonlwheat2-gif / @pacocartones +- **fix(dashboard):** Keep the Profile level and progress aligned with aggregate XP, including bounded handling for invalid totals ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)) — thanks @pacocartones +- **fix(dashboard):** Prevent locked hidden badges from revealing their icon or opening private badge details before they are earned ([#11605](https://github.com/diegosouzapw/OmniRoute/pull/11605)) — thanks @pacocartones +- **fix(dashboard):** Restore keyboard focus after shared modals close and cancel delayed autofocus during cleanup ([#11607](https://github.com/diegosouzapw/OmniRoute/pull/11607)) — thanks @pacocartones +- **fix(dashboard):** Enable Enter and Space activation for clickable data-table rows without hijacking nested controls ([#11610](https://github.com/diegosouzapw/OmniRoute/pull/11610)) — thanks @pacocartones +- **fix(autoCombo,sse):** vendor-retired catalog ids are dropped from the auto-combo candidate pool and no longer win on leftover `arena_elo` / `user_override` rows; `getModelLifecycleDecision` consults `model-lifecycle.json` (prefix-stripped) so aggregator traffic is not `untracked` for ids the snapshot already knows ([#11625](https://github.com/diegosouzapw/OmniRoute/issues/11625)) ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)) — thanks @MumuTW +- **test(chatcore):** move Codex/Claude combo fixtures off models the lifecycle guard now rejects — `gpt-5.1-codex`/`gpt-5-codex` are vendor-retired (snapshot, #11626) and `claude-3-5-sonnet-20241022` is shut down, so native-passthrough and combo-fallback tests switched to `gpt-5.6-sol` and `claude-sonnet-4.6` ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)). ([#11675](https://github.com/diegosouzapw/OmniRoute/pull/11675)) — thanks @jonlwheat2-gif / @MumuTW +- fix(opencode-plugin): stop a free-tier budget that rounds up across a magnitude from rendering as `1000K`/`1000M` in the model picker — `fmtTokens` chose its unit from the raw token count and then rounded with `toFixed(1)`, so 999,950–999,999 printed as `1000K` rather than `1M` and just under a billion printed as `1000M` rather than `1B` ([#11684](https://github.com/diegosouzapw/OmniRoute/pull/11684)) — thanks @abhisheksharma2411 +- Cap prose-derived Antigravity quota resets at the configured model cooldown maximum while preserving authoritative Retry-After headers and Google RetryInfo hints. (#11695) Thanks @pacocartones. ([#11823](https://github.com/diegosouzapw/OmniRoute/pull/11823)) +- Compare `_rebaseline_*` notes structurally in the ratchet-bank verifier so an object-valued note no longer reports itself as rewritten on an unmodified tree — the nightly `bank-ratchet-shrinks` lane had been aborting on every run since 2026-08-11, leaving 41,672 lines of earned file-size shrinks unbanked (#11702). ([#11892](https://github.com/diegosouzapw/OmniRoute/pull/11892)) — thanks @MumuTW +- **fix(build):** `prepublish.ts` bundles the ChatGPT Web (Codex) MCP bridge through `runBuildTool()` instead of spawning `npx.cmd` raw, fixing the build crash on Node ≥ 20/Windows where `.cmd` shims cannot be spawned without a shell (EINVAL) ([#11704](https://github.com/diegosouzapw/OmniRoute/issues/11704)) ([#11706](https://github.com/diegosouzapw/OmniRoute/pull/11706)) — thanks @jonlwheat2-gif +- fix(codex): keep `parallel_tool_calls:false` on the translated Codex Responses Lite path (#11707) ([#11984](https://github.com/diegosouzapw/OmniRoute/pull/11984)) — thanks @hartmark +- **fix(tests):** the new Gemini Web Images retirement test (#11708) called the now-async `getExecutor()` synchronously, throwing `TypeError: getExecutor(...).getProvider is not a function` instead of asserting Gemini Web chat stays available. Awaited to match the R0.3 executor-registry refactor (#11220). ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(search):** Allow REST and MCP web-fetch requests to select the advertised `anysearch-search` provider explicitly ([#11716](https://github.com/diegosouzapw/OmniRoute/pull/11716)) — thanks @pacocartones +- **fix(providers):** Keep overnight peak-hour protection active after midnight according to the window's configured start day ([#11718](https://github.com/diegosouzapw/OmniRoute/pull/11718)) — thanks @pacocartones +- **fix(providers):** correct all five g4f.space gateway descriptions to document proof-of-work credits or member API keys, variable limits, and the remote prompt boundary instead of promising free keyless access at five requests per minute ([#11738](https://github.com/diegosouzapw/OmniRoute/pull/11738)). +- **fix(db):** `model_capabilities` is created by a migration instead of lazily on the first models.dev sync, so a clean install and an upgraded install converge on the same schema regardless of which features have run ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(ci):** `check:install-upgrade` now fails on an `npm` install truncated by ENOSPC (npm reports it as a warning and still exits 0), authenticates its health probe so the version assertion works against the hardened health payload, frees the clean-install tree before the upgrade phase, and no longer reports a schema divergence computed from a boot that never served ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(routing):** harden session-affinity key derivation against oversized explicit IDs and empty structural-payload collisions, while recognizing Gemini and common text request shapes ([#11746](https://github.com/diegosouzapw/OmniRoute/pull/11746)) — thanks @MumuTW +- **fix(packages/browser-pool):** regenerate `package-lock.json` so the `packages/browser-pool` workspace's locked `playwright`/`@types/node` (and transitives) match its `package.json` specs, fixing cache-only/offline installs (`npm ci --offline`, Nix `buildNpmPackage`) that previously failed with `ENOTCACHED` ([#11747](https://github.com/diegosouzapw/OmniRoute/issues/11747)) — thanks @benjaminkitt ([#11784](https://github.com/diegosouzapw/OmniRoute/pull/11784)) — thanks @geekyNads +- **fix(assets):** Stop distributing 79 provider and CLI logos without verified source/license provenance, keep every provider/tool available through the internal generic icon, and prevent those IDs from falling through to an external icon CDN. ([#11750](https://github.com/diegosouzapw/OmniRoute/pull/11750)) +- **fix(providers):** retire the common `chatgpt-web` / `cgpt-web` provider because its pre-key and proof-of-work provenance could not be cleared ([#11754](https://github.com/diegosouzapw/OmniRoute/pull/11754)) — explicit chat and image requests, slash-prefixed models, persisted aliases, proxy overrides, and combo targets now fail closed with a sanitized HTTP `410` / `PROVIDER_RETIRED` before upstream dispatch. Migration 163 tombstones matching connections and invalidates their active leases without deleting history or widening API-key allowlists. The independent `chatgpt-web-codex` / `cgpt-codex` provider and its MIT-noticed vendored implementation remain available. +- **fix(usage):** quota and usage refresh no longer 409 when an exclusive lease reserves the connection ([#11758](https://github.com/diegosouzapw/OmniRoute/pull/11758)) — thanks @TheDemonTuan +- Keep the embedding registry's vector width and `embedding` type on models when a synced model exists for the same id, so `/v1/models` no longer reports registry-described embedding models widthless or untyped (#11761) — thanks @rahilmavani +- Correct `google/gemini-embedding-001` on the OpenRouter route to 3072 dimensions, the width it returns when `dimensions` is not sent (#11761) — thanks @rahilmavani +- **fix(vision-bridge):** Restrict automatic vision routing and fallbacks to models available in authoritative live provider catalogs, including aliases and effort variants ([#11767](https://github.com/diegosouzapw/OmniRoute/issues/11767)) — thanks @pacocartones ([#11813](https://github.com/diegosouzapw/OmniRoute/pull/11813)) +- **fix(providers):** reclassify Cerebras as a one-time $5 signup credit (payment method required, 30-day validity), not a recurring no-card 1M tokens/day trial ([#11773](https://github.com/diegosouzapw/OmniRoute/issues/11773)) ([#12591](https://github.com/diegosouzapw/OmniRoute/pull/12591)) — thanks @HouMinXi +- fix(cli): stop prepublish from re-rebuilding the already-built ESM-only opencode-plugin dist (#11787) ([#11990](https://github.com/diegosouzapw/OmniRoute/pull/11990)) — thanks @hartmark +- **fix(db):** Fresh installs no longer log a non-fatal `no such table: compression_run_telemetry` warning when retention cleanup runs before the lazily-created telemetry table exists ([#11802](https://github.com/diegosouzapw/OmniRoute/pull/11802)) — thanks @RaviTharuma +- fix(sse): set X-OmniRoute-Selected-Connection-Id on successful combo dispatches so downstream consumers stop falling back to an empty connection id (#11810) ([#11986](https://github.com/diegosouzapw/OmniRoute/pull/11986)) — thanks @hartmark +- **fix(services):** embedded CLIProxyAPI lifecycle checks now use public `/healthz`, while model discovery uses the configured dedicated data-plane API key instead of the management password ([#11811](https://github.com/diegosouzapw/OmniRoute/pull/11811)) — thanks @RaviTharuma +- Fix persisted model steps returning an error in the playground route simulator, and warn when structural steps cannot be simulated. ([#11862](https://github.com/diegosouzapw/OmniRoute/pull/11862)) — thanks @pacocartones +- **fix(providers):** Antigravity's dynamic mitmAlias table no longer routes `gemini-3.7-flash-{high,medium,low}` to a literal tier-suffixed upstream id just because one connected account's own discovery listed it directly — those display ids always resolve through the safe `gemini-3.7-flash-tiered` static alias, so one account's Google-provisioned access no longer 404s every sibling account of the provider ([#11824](https://github.com/diegosouzapw/OmniRoute/issues/11824), [#11651](https://github.com/diegosouzapw/OmniRoute/issues/11651)) ([#11988](https://github.com/diegosouzapw/OmniRoute/pull/11988)) — thanks @hartmark +- **fix(dashboard):** Expose Profile loading, errors, page structure, and XP progress to assistive technologies ([#11838](https://github.com/diegosouzapw/OmniRoute/pull/11838)) — thanks @pacocartones +- **fix(socks):** `Agent.connectTimeout` now bounds both SOCKS handshake and TLS connect, and `family === null` no longer falls back to `fetch-socks` ([#11842](https://github.com/diegosouzapw/OmniRoute/pull/11842)) — thanks @maxmad64bis +- **perf(compression):** OOM mitigations for large payload hashing, memoization, and token estimation ([#11844](https://github.com/diegosouzapw/OmniRoute/pull/11844) — thanks @AndrianBalanescu) +- Excluded Next.js Node File Trace manifests (`*.nft.json`) from the published npm tarball. They are build-time metadata and are never read while serving, but had grown to 668.7 MB — 61% of the package — which pushed the upload past the registry limit and made `npm publish` fail with `413 Payload Too Large`. ([#11864](https://github.com/diegosouzapw/OmniRoute/pull/11864)) +- **fix(cli):** keep the global OmniRoute server `--base-url` separate from provider-node `--endpoint` values in add, update, and validate commands ([#11860](https://github.com/diegosouzapw/OmniRoute/pull/11860)) — thanks @pacocartones +- **fix(config):** Nous Research's `Hermes-4-405B` model now displays as "Hermes 4 405B (Nous Research)" in both the provider registry and the free-model catalog, instead of the mislabelled "Hermes 4 7B" ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#11993](https://github.com/diegosouzapw/OmniRoute/pull/11993)) +- **fix(provider/nous):** inject required user= tag into Nous Research inference requests to resolve upstream 400 "missing tags" error ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#12044](https://github.com/diegosouzapw/OmniRoute/pull/12044)) +- **fix(build):** `npm run build` now fails in one second with a named package and a copy-pasteable fix when npm silently drops an externalised optional native dependency, instead of dying four minutes in with `Module not found: Can't resolve 'better-sqlite3'` ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- **fix(install):** `postinstall` no longer throws `ReferenceError: isAndroid is not defined` — failing the whole `npm install` — when the `better-sqlite3` rebuild fallback times out; the manual-fix guidance is reachable again ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- Fixed the Alibaba free-tier allowlist test that went red on its own once the shipped catalog's `validUntil` (2026-08-27) passed, leaving every PR and `main` with a failing `Unit Tests (1/8)`. The test now builds its own packs with dates it controls, and covers the expired-pack fallback that production has actually been serving. ([#11867](https://github.com/diegosouzapw/OmniRoute/pull/11867)) +- Split the npm registry upload into its own GitHub-hosted job. npm refuses `--provenance` from a self-hosted runner (`422 ... Only "github-hosted" runners are supported`), which blocked the v3.8.50 publish; the heavy verification cannot move to a hosted runner, so it now hands the proven tarball over instead. ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(ui):** the shared `DataTable` loading state no longer reads its decorative ⏳ glyph out to assistive technology, and now carries the same `role="status"` / `aria-live="polite"` / `aria-busy="true"` semantics as `PageLoading` ([#11869](https://github.com/diegosouzapw/OmniRoute/pull/11869)) — thanks @pacocartones +- **fix(api):** Bound the `timeout` accepted by `POST /api/providers/[id]/login` to the same 15s–600s window the Adobe Firefly and Conol browser-login paths already enforce, so a request can no longer pin a headful browser and the single active-login slot with an arbitrarily large poll budget. ([#11871](https://github.com/diegosouzapw/OmniRoute/pull/11871)) — thanks @pacocartones +- **fix(api):** Stop the tunnel and MITM routes from returning raw child-process error text, which disclosed host paths, binary install locations and Tailscale `tskey-*` credentials in a body some of these routes serve to non-loopback callers, and return a real 400 instead of a framework 500 when a tunnel request body fails validation. ([#11872](https://github.com/diegosouzapw/OmniRoute/pull/11872)) — thanks @pacocartones +- **fix(open-sse):** stream TTFT/ITL are sampled from a monotonic clock (`performance.now`) instead of `Date.now`, so an NTP correction or wall-clock jump can no longer inflate a genuine TTFT or produce a negative one that the `ttft >= 0` guard silently drops — protecting the router's OTel/EWMA/`usage_history`/speed-ranking signals ([#11873](https://github.com/diegosouzapw/OmniRoute/pull/11873)) — thanks @pacocartones +- **fix(providers):** xAI `reasoning_effort: "xhigh"` now reaches grok-4.6+ instead of being silently clamped to `"high"` ([#11879](https://github.com/diegosouzapw/OmniRoute/pull/11879)) — thanks @NoxzRCW +- **fix(dashboard):** the providers page no longer crashes into the error boundary when a provider id collides with an `Object.prototype` member (`constructor`, `__proto__`); icon lookups are own-property guarded ([#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)) — thanks @NoxzRCW +- **fix(skills):** injected skill tools declared in shorthand (`{"content": "string"}`) now forward valid JSON Schema, unblocking providers that validate tool schemas strictly such as Zhipu GLM on the Console Go tier ([#11881](https://github.com/diegosouzapw/OmniRoute/pull/11881)) — thanks @NoxzRCW +- **fix(api):** `POST /api/playground/simulate-route` now surfaces `combo-ref` and `provider-wildcard` persisted combo steps with a specific warning (naming the referenced combo, or the unresolved `provider/model` wildcard) instead of folding them into a generic "unsupported step" count; a `provider-wildcard` step is also now included as an unresolved target so the operator can see it is in the route (ported from [#11882](https://github.com/diegosouzapw/OmniRoute/pull/11882) — thanks @NoxzRCW). ([#11930](https://github.com/diegosouzapw/OmniRoute/pull/11930)) +- **fix(translator):** the streaming OpenAI→Claude translator keeps upstream usage, including prompt-cache tokens, when it arrives on a trailing `choices: []` chunk (Fireworks and any upstream using `stream_options.include_usage`) ([#11883](https://github.com/diegosouzapw/OmniRoute/pull/11883)) — thanks @NoxzRCW +- **fix(cli):** `omniroute update --apply` now tells you explicitly whether a running server was detected and, if so, that you must run `omniroute restart` to apply the update — it never restarted anything and previously implied the update was already live once files were installed. The dashboard's npm-mode Update flow (`/api/system/version`) now tries OmniRoute's own PID-file-managed supervisor before falling back to pm2, and reports an honest "restart required" step instead of a silent pm2-only "skipped" that read like a completed update. The server-side latest-version lookup backing the dashboard's update banner also gained `--prefer-online`, closing the same stale-npm-cache class already fixed in the CLI's own copy for #4376 ([#11885](https://github.com/diegosouzapw/OmniRoute/issues/11885)). ([#12005](https://github.com/diegosouzapw/OmniRoute/pull/12005)) +- **fix(resilience):** clear persisted LKGP pins when a target suffers connection/provider exhaustion or is skipped before dispatch due to cooldown/exhaustion/unavailability, preventing subsequent requests from repeatedly prioritizing known-dead providers ([#11911](https://github.com/diegosouzapw/OmniRoute/issues/11911)). ([#12013](https://github.com/diegosouzapw/OmniRoute/pull/12013)) — thanks @HouMinXi +- **fix(sse):** the OpenAI→Claude stream translator now defers the terminal `message_delta`/`message_stop` emission until the real usage block has arrived (or a genuine end-of-stream flush forces it) instead of emitting it immediately on `finish_reason` — previously, when the trailing usage-only chunk (`{"choices":[],"usage":{...}}`) arrived *after* the `finish_reason` chunk (the normal order for Fireworks/vLLM/Together and other `stream_options.include_usage` upstreams), the client-visible `message_delta` still carried stale/zero usage even though `state.usage` was internally corrected too late to matter (ported from [#11915](https://github.com/diegosouzapw/OmniRoute/pull/11915) — thanks @HouMinXi). ([#11933](https://github.com/diegosouzapw/OmniRoute/pull/11933)) +- **fix(sse):** `stripStore()` now forces `store=false` for stateless OpenAI-compatible Responses-API targets (unless the connection has explicitly opted in via `providerSpecificData.openaiStoreEnabled`), instead of only handling the `openai`/`agentrouter` cases — a client-supplied `store` value previously passed through untouched to backends that don't actually persist responses server-side ([#11916](https://github.com/diegosouzapw/OmniRoute/pull/11916) — thanks @HouMinXi). +- **fix(models):** custom provider-node models (synced, custom, and alias-backed) now appear under their configured prefix in the unified catalog when the operator's model-id prefix mode is canonical, instead of being dropped whenever alias-inclusion was otherwise disabled ([#11918](https://github.com/diegosouzapw/OmniRoute/pull/11918) — thanks @HouMinXi). +- **fix(models):** the unified model catalog now suppresses stale static registry models (including effort-tier variants) for any provider whose active connection has an authoritative live synced catalog, not only providers already using exclusive-synced-listing — a connection with `providerUsesAuthoritativeLiveCatalog` previously kept serving both the live-synced models and the stale static rows side by side ([#11919](https://github.com/diegosouzapw/OmniRoute/pull/11919) — thanks @HouMinXi). +- **fix(ui):** `ProviderIcon`'s three remaining unguarded lookups (`PROVIDER_ICON_ALIASES`, `LOCAL_SVG_ALIASES`, `THEMED_SVGS`) now use `Object.hasOwn()` own-property checks — a provider id such as `constructor` or `__proto__` previously resolved through the prototype chain instead of falling through to the unknown-provider CDN fallback (`getLobeProviderIcon()` itself was already guarded by [#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)); `ProviderPageHeader`'s `color` field is also now optional, matching the rest of the component's defensive typing (ported from [#11920](https://github.com/diegosouzapw/OmniRoute/pull/11920) — thanks @HouMinXi). ([#11935](https://github.com/diegosouzapw/OmniRoute/pull/11935)) — thanks @NoxzRCW +- fix(ollama): preserve multi-byte UTF-8 content split across stream chunks in the Ollama NDJSON transform, which previously corrupted CJK/emoji into U+FFFD (#11921) — thanks @pacocartones +- **fix(providers):** OrcaRouter chat requests now target `/v1/chat/completions` instead of the bare `/v1` API root, fixing the upstream `404 Invalid URL (POST /v1)` ([#11923](https://github.com/diegosouzapw/OmniRoute/pull/11923)). — thanks @echel0nn +- **fix(plugins):** deliver the `onStreamComplete` event to disk-installed plugins. The event shipped in v3.8.50 (#9669) was emitted internally but had no plugin-facing wiring, so no plugin could ever subscribe: the manifest schema silently dropped `hooks.onStreamComplete`, and the loader/manager only knew the seven legacy hooks. `onStreamComplete` is now a declarable manifest hook, wired through the loader and registered by the manager like the other hooks, and its payload carries a `requestId` so consumers can correlate the stream-completion event with the originating request ([#11934](https://github.com/diegosouzapw/OmniRoute/pull/11934)) — thanks @amaleta / @pacocartones +- **fix(db):** the Qdrant embedding-model dropdown now lists local/self-hosted providers (Ollama, LM Studio, vLLM, etc.) — an active connection is treated as "configured" when the provider allows an optional API key, not only when it has a real key or OAuth, so a running local embedding provider is no longer hidden from the picker ([#11949](https://github.com/diegosouzapw/OmniRoute/issues/11949)) ([#11995](https://github.com/diegosouzapw/OmniRoute/pull/11995)) +- **fix(providers):** Vertex AI Anthropic partner-model discovery now calls the Model Garden `v1beta1` publisher list (`/v1beta1/publishers/anthropic/models`, global) and parses its `publisherModels` envelope, so Claude models auto-synced from Vertex populate the active live catalog and route at request time instead of returning `Model '' is not available in the active live catalog` ([#11991](https://github.com/diegosouzapw/OmniRoute/issues/11991)) — thanks @fabioluissilva ([#11998](https://github.com/diegosouzapw/OmniRoute/pull/11998)) +- **fix(cli):** support `--base-url` alongside `--endpoint` in `omniroute nodes add`, `update`, and `validate` subcommands to prevent global `--base-url` shadowing issues ([#11999](https://github.com/diegosouzapw/OmniRoute/issues/11999)). ([#12033](https://github.com/diegosouzapw/OmniRoute/pull/12033)) — thanks @HouMinXi +- **fix(providers):** `cloudflare-ai` no longer refuses image content parts for every Workers AI model ([#12002](https://github.com/diegosouzapw/OmniRoute/pull/12002)) — the plain-string `content` requirement behind #2539 is carried by the _model_ schema, not by the `/ai/v1/chat/completions` endpoint (measured: an all-text part array returns 200 on `@cf/mistralai/mistral-small-3.1-24b-instruct`, `@cf/meta/llama-4-scout-17b-16e-instruct` and `@cf/meta/llama-3.3-70b-instruct-fp8-fast`, and 400 on the text-only `@cf/qwen/qwen2.5-coder-32b-instruct`). `transformRequest()` flattened every array and threw on the first non-text part (#6390), so image input was refused for vision-capable Cloudflare models that accept it. All-text arrays are still flattened — the one shape every model accepts — while an array carrying a non-text part is passed through untouched, so the attachment is still never silently dropped. Regression guards: `tests/unit/cloudflare-ai-image-parts-6390.test.ts`. — thanks @davidlinfr +- **fix(resilience):** decouple the limiter-managed execution backstop from the queue-wait budget — new `requestQueue.executionMaxWaitMs` (env `RATE_LIMIT_EXECUTION_MAX_WAIT_MS`, default 600000 = 10 min) now feeds Bottleneck's post-dispatch `expiration`, while `requestQueue.maxWaitMs` keeps its documented queue-wait semantics. Previously the queue-wait budget doubled as the execution expiration, so legitimate long-running calls on non-incremental gateways (whole generation buffered before the first upstream byte, e.g. Console Go / Command Code tiers serving GLM models) were killed mid-flight at the queue budget with a false 504 `RATE_LIMIT_EXECUTION_TIMEOUT` — the local limiter undercut the provider-aware upstream fetch-start timeouts. The surfaced 504 message now names `requestQueue.executionMaxWaitMs`; the error keeps the #4165 guarantees (disclaims an upstream timeout, preserves the Bottleneck error as `cause`, branded code + trusted provenance, classified request-scoped so combo falls back). A real queue-wait bound (the `Promise.race` around `limiter.schedule()` sketched in #9533) remains future work. (#12025) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **Call logs:** keep the `error` field when an artifact exceeds the storage cap, instead of replacing it with the omission marker. The error is the only field that says *why* a request failed and is typically ~90 bytes next to the multi-hundred-KB bodies that trip the cap, so dropping it left a size-limited row undiagnosable — a provider outage, a local timeout and an upstream 400 all rendered identically. It is now preserved at every fallback stage, truncated to 4KB if it is itself large ([#12026](https://github.com/diegosouzapw/OmniRoute/issues/12026)). ([#12095](https://github.com/diegosouzapw/OmniRoute/pull/12095)) — thanks @ntdat812 +- **fix(diagnostics):** preserve the error field (truncated to 4KB with a `[truncated: …]` suffix) in every call-log artifact size-limit fallback stage. Previously the minimal fallback replaced the error with `[omitted: call log artifact size limit exceeded]`, so an oversized artifact row showed nothing about WHY the request failed — e.g. 91 of 847 opencode-go 504 rows on one production instance were undiagnosable from the dashboard. Oversized request/response bodies are still omitted exactly as before; the error cap is independent of the payload sizes that tripped the fallback. (#12026) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **fix(sse):** OpenAI Responses clients that declare the native `web_search` tool now receive a spec-shaped `web_search_call` output item with `action.sources` alongside the preserved function-call round-trip, so search results executed through OmniRoute's own search backend are consumable by standard Responses clients (Codex, pi-web-access, …). ([#12031](https://github.com/diegosouzapw/OmniRoute/pull/12031)) — thanks @watchingdogs +- **fix(cli):** use in-thread alias resolver hooks on modern runtimes to avoid deprecation noise and improve Node.js forward compatibility ([#12073](https://github.com/diegosouzapw/OmniRoute/issues/12073)). ([#12083](https://github.com/diegosouzapw/OmniRoute/pull/12083)) — thanks @xiaoyaner0201 +- **fix(combo):** an operator-set **Agent Features → Context length** on a combo is now honored at request time. The value was persisted and advertised through `/v1/models`, but `resolveComboContextLimit()` never consulted it — so a multi-target combo whose members carry no per-model window fell through to the provider's generic `defaultContextLength` (openrouter 128000, command-code 200000) and rejected large requests with `Input exceeds context window … limit 128000` despite the combo being explicitly sized much larger. An identical single-target combo worked, because it collapses to its concrete target before the guard runs. Invalid values (0/negative/NaN/Infinity) are ignored, so the existing target → combo-min → fallback order is unchanged. ([#12090](https://github.com/diegosouzapw/OmniRoute/pull/12090)) — thanks @adivekar-utexas +- **fix(sse):** passthrough streams now estimate usage on finish when upstream closes without usage even with `stream_options.include_usage` — avoids `0 tokens / 0%` for providers that stay silent (and correctly handles trailing empty-choices usage) ([#12151](https://github.com/diegosouzapw/OmniRoute/pull/12151)) — thanks @maxmad64bis +- **fix(combos):** clearing an agent feature in the combos editor now persists — unchecking context cache protection, or emptying the system message or tool filter, sends an explicit `null` instead of dropping the field from the `PUT` body, which the update merge read as "leave unchanged" ([#12177](https://github.com/diegosouzapw/OmniRoute/pull/12177)) — thanks @foreveryh / @ntdatt812 +- **fix(memory):** self-hosted embedding endpoints now vectorize — the vector width is measured from the first embedding that comes back instead of being read from a registry that cannot describe them, so `vec_memories` is created and memories stop piling up unvectorized behind a green health check ([#12180](https://github.com/diegosouzapw/OmniRoute/pull/12180)) — thanks @kanade-hoshino / @ntdatt812 +- **fix(translator):** the leading `system` message now reaches Responses-API upstreams when its `content` is a content-part array — it was read as `typeof content === "string" ? content : ""`, so a prompt-caching client (Anthropic `cache_control`, the shape LiteLLM and the Anthropic SDK emit) had its entire system prompt replaced by an empty `instructions`. The request was still accepted with a normal `prompt_tokens` count, so the model answered with no instructions and nothing in the response said they were missing. Mid-conversation system turns already handled the array shape ([#7056](https://github.com/diegosouzapw/OmniRoute/pull/7056)); only the first one did not ([#12206](https://github.com/diegosouzapw/OmniRoute/issues/12206)). Regression guard: `tests/unit/translator-openai-responses-system-content-parts.test.ts`. ([#12207](https://github.com/diegosouzapw/OmniRoute/pull/12207)) — thanks @Gorillaz322 +- **fix(free-tier):** `/api/free-tier/summary` no longer computes its totals from a Radar feed built before the catalog the running release ships. When the cached feed is older — or carries no build date at all — the route answers from the shipped catalog, resolved through the operator's local model state so disabled and tombstoned models stay out of the numbers ([#12215](https://github.com/diegosouzapw/OmniRoute/pull/12215)). — thanks @maxmad64bis +- **fix(oauth):** Keep a Claude personal workspace and a Team organization as separate connections — they share the same email and `accountUUID`, so the email-only OAuth dedup let the second login overwrite the first account's tokens; `organizationUUID` now disambiguates them, the way `workspaceId` does for Codex ([#12222](https://github.com/diegosouzapw/OmniRoute/pull/12222)) — thanks @davidebaraldo +- **fix(resilience):** a 402 on a single paid model of a passthrough/gateway provider (e.g. `kilo-gateway`, `ollama-cloud`) no longer terminalizes the whole connection with a never-auto-recovered `credits_exhausted` status — only the paid model is locked out, so free models on the same key keep serving. 402 variant of [#3027](https://github.com/diegosouzapw/OmniRoute/issues/3027). Single-credential providers are unaffected — a 402 there is still treated as the key being genuinely out of credit ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239) / [#10616](https://github.com/diegosouzapw/OmniRoute/issues/10616)) ([#12242](https://github.com/diegosouzapw/OmniRoute/issues/12242)) — thanks @brick30llc-ctrl ([#12266](https://github.com/diegosouzapw/OmniRoute/pull/12266)) — thanks @geekyNads +- **fix(sse):** trust `finish_reason: "length"`/`"max_tokens"` over the reasoning-consumed-token ratio in response quality validation, so a reasoning model truncated below the old 90% threshold correctly fails and retries instead of returning empty content as a silent "success" ([#12262](https://github.com/diegosouzapw/OmniRoute/pull/12262)) — thanks @brick30llc-ctrl +- **fix(settings):** `PUT /api/settings/cache-config` now persists `alwaysPreserveClientCache` to the flat general settings the runtime cache-control policy actually reads; previously the value landed in the databaseSettings "cache" section and was silently ignored, so the endpoint had no effect on `cache_control` passthrough ([#12304](https://github.com/diegosouzapw/OmniRoute/pull/12304)) — thanks @davidebaraldo +- **fix(grok-cli):** treat omitted SuperGrokPro `creditUsagePercent` as 0% used so Provider Limits still renders a weekly bar (proto3 zero-elision) ([#12312](https://github.com/diegosouzapw/OmniRoute/pull/12312)) — thanks @HouMinXi +- **fix(combo):** Expose the two Auto-Combo scoring factors nobody could set — the combo validation schema and the dashboard weight sliders both declared 13 of the scorer's 15 factors, so `connectionDensity` (spreads load across a provider's connections) and `quality` were dropped on save and offered nowhere. The sliders also shipped their own default table that differed from the engine's on every non-zero factor and summed to 1.05, so the percentages shown next to them added up to 105%. Both lists now match `DEFAULT_WEIGHTS`, and a test keeps them there. Note that a combo whose stored `weights` omitted the two keys was effectively running with them at zero and the other thirteen renormalized upward; it now runs with the engine's intended distribution, so its routing does shift ([#12314](https://github.com/diegosouzapw/OmniRoute/pull/12314)) — thanks @maxmad64bis +- **fix(docs):** The free-tier reference no longer says its numbers come "confidence tagged per row" — no catalog entry carries a confidence tag and the API serves none, so every figure on that page is an estimate of the same, unstated quality. The page now states what an entry does vouch for: an independently documented hard stop (set by hand with the source in a comment, never defaulted to `true`) and a prompt-training disclosure, both with live counts the `check:docs-counts` gate keeps honest ([#12318](https://github.com/diegosouzapw/OmniRoute/pull/12318)) — thanks @maxmad64bis +- **fix(usage):** `adobe-firefly` and `firefly` have had a working usage fetcher since Adobe Firefly landed, but neither was ever added to the registration list, so the provider-plugin manifest, `genericQuotaFetcher` and the free-access quota cache all reported them as having no usage support — while `USAGE_SUPPORTED_PROVIDERS` said the opposite. Both are now declared, which also means their credit balance is fetched like any other declared provider's: `registerGenericQuotaFetchers` now registers a generic quota fetcher for them, and `resolveFreeAccessState` no longer returns early. A test holds the registration list to the dispatcher's switch in both directions, which is what the module's own docstring already asked for in prose ([#12321](https://github.com/diegosouzapw/OmniRoute/pull/12321)) — thanks @maxmad64bis +- **fix(quota):** drop the generic quota cache (agy / Antigravity / Claude OAuth) on an upstream 429 so reset-aware scoring does not keep a 60s stale snapshot, and force-refresh the next usage fetch so inner provider caches cannot recache the same window ([#12325](https://github.com/diegosouzapw/OmniRoute/pull/12325)) — thanks @HouMinXi +- **fix(combos):** deleting a combo now clears its persisted LKGP pins instead of leaving unreachable `key_value` rows behind ([#12326](https://github.com/diegosouzapw/OmniRoute/issues/12326)) ([#12425](https://github.com/diegosouzapw/OmniRoute/pull/12425)) — thanks @KrzysiekSko +- **fix(sse):** Keep ZWNJ (U+200C) and ZWJ (U+200D) in assistant text, reasoning and tool-call arguments — Persian/Kurdish half-space (`ارائه‌دهنده`), Arabic/Indic shaping and emoji sequences no longer lose them; the response de-obfuscation now removes joiners only between ASCII word characters, where the request side inserts them ([#12186](https://github.com/diegosouzapw/OmniRoute/issues/12186)) — thanks @rezjalibd ([#12359](https://github.com/diegosouzapw/OmniRoute/pull/12359)) — thanks @pacocartones +- **fix(resilience):** count resolved upstream 5xx results against the provider circuit breaker on the chat path — `CircuitBreaker.execute()` no longer reads a resolved `{ success: false, status: 5xx }` as a success that cancels the call-site failure, so a provider answering 503s now trips its breaker instead of staying `CLOSED` at `failureCount: 1`; single-model and combo dispatches are each accounted exactly once ([#12254](https://github.com/diegosouzapw/OmniRoute/issues/12254)) ([#12360](https://github.com/diegosouzapw/OmniRoute/pull/12360)) — thanks @pacocartones +- **fix(providers):** resolve the Codex quota auto-ping model from the live provider catalog and lifecycle registry instead of the retired `gpt-5.1-codex-mini`, and pause the ping with one actionable warning when no selectable Codex model exists rather than retrying a shut-down id every cooldown window ([#11905](https://github.com/diegosouzapw/OmniRoute/issues/11905)) ([#12361](https://github.com/diegosouzapw/OmniRoute/pull/12361)) — thanks @pacocartones +- **fix(api):** keep the `{created, data}` wrapper on combo-routed `/v1/images/generations` responses and default Codex image results to `b64_json` on both `/v1/images/generations` and `/v1/images/edits` so Codex CLI's built-in `image_gen` can decode them ([#12268](https://github.com/diegosouzapw/OmniRoute/issues/12268)) ([#12362](https://github.com/diegosouzapw/OmniRoute/pull/12362)) — thanks @pacocartones +- **fix(sse):** Name the shadowed custom provider node when a built-in provider id/alias (e.g. `openference` → `of`) reserves the prefix of an existing OpenAI/Anthropic-compatible node, so the runtime `No active credentials for provider: ` error explains that the prefix routed to the built-in and never reached the node's healthy connections, instead of contradicting the dashboard ([#11943](https://github.com/diegosouzapw/OmniRoute/issues/11943)) — thanks @morpheus9393 ([#12365](https://github.com/diegosouzapw/OmniRoute/pull/12365)) — thanks @pacocartones +- **fix(i18n):** wrap `ccOnboardingKeyPlaceholder` in ICU single quotes across all 43 locales so angle brackets render literally instead of being parsed as rich-text tags, which crashed the Claude Code onboarding block with `INVALID_MESSAGE: INVALID_TAG` ([#12302](https://github.com/diegosouzapw/OmniRoute/issues/12302)) ([#12369](https://github.com/diegosouzapw/OmniRoute/pull/12369)) — thanks @gonisulaimann +- **fix(models):** publish `effort_tiers` on Kimi K3's synced base-model entries (`kmca/k3`, `kmca/k3-256k`) so catalog-only clients (OpenCode, plain SDK pickers) can see and select the reasoning tiers (`low`/`high`/`max`) the synced metadata already carried — the `isSkippedEffortProvider` gate no longer suppresses tier visibility on those base entries, while synthetic `-` variant generation stays prevented and Codex/GLM base models remain excluded unchanged ([#12299](https://github.com/diegosouzapw/OmniRoute/issues/12299)) ([#12371](https://github.com/diegosouzapw/OmniRoute/pull/12371)) — thanks @gonisulaimann +- **fix(guardrails):** keep `auto`/`auto/*` virtual combos exempt from the Vision Bridge `fixedModel` credential guard so a combo target is passed through instead of silently falling back to global auto-selection ([#12237](https://github.com/diegosouzapw/OmniRoute/issues/12237)) ([#12373](https://github.com/diegosouzapw/OmniRoute/pull/12373)) — thanks @pacocartones +- **fix(combo):** capability-filter exhaustion caused by `max_tokens` above every target's known output limit now reports that reason (requested `max_tokens` vs the pool's highest known ceiling) instead of the unrelated "supports structured output" message ([#12229](https://github.com/diegosouzapw/OmniRoute/issues/12229)) — thanks @DW-MediaLab ([#12374](https://github.com/diegosouzapw/OmniRoute/pull/12374)) — thanks @pacocartones +- **fix(auth):** the `least-used` account strategy now prefers accounts without backoff before falling back to oldest `lastUsedAt`, the same tie-break `round-robin` already applies, so a failover no longer lands on a just-rate-limited account for a single request ([#12279](https://github.com/diegosouzapw/OmniRoute/issues/12279)) — thanks @tenshiak ([#12375](https://github.com/diegosouzapw/OmniRoute/pull/12375)) — thanks @pacocartones +- **fix(docker):** the `chatgpt-web-codex-browser` image now finds the Chrome binary under `chrome-linux64/` (Chrome for Testing layout in `playwright:v1.62.0-noble`) as well as the legacy `chrome-linux/`, so the container no longer crash-loops with `exec: --headless=new: not found` ([#12024](https://github.com/diegosouzapw/OmniRoute/issues/12024)) ([#12376](https://github.com/diegosouzapw/OmniRoute/pull/12376)) — thanks @pacocartones +- **fix(providers):** declare `groq/compound` and `allam-2-7b` as non-reasoning models in the curated Groq registry so `reasoning_effort` / `output_config.effort` / `thinking` from Claude Code are stripped instead of forwarded, which Groq rejected with HTTP 400 ([#12134](https://github.com/diegosouzapw/OmniRoute/issues/12134)) ([#12379](https://github.com/diegosouzapw/OmniRoute/pull/12379)) — thanks @pacocartones +- **fix(executors):** `OpencodeExecutor` no longer forces a direct connection when the connection has a proxy assigned in Proxy Management but no per-account proxies: the single-account fast path used to wrap the upstream dispatch in the direct-egress sentinel, discarding the ambient proxy context the chat handler had pinned from `proxy_assignments`, so API-key `opencode`/`opencode-go` connections egressed from the host IP (and hit geoblocks) despite the assignment. The direct pin is now applied only when no ambient proxy context exists ([#11894](https://github.com/diegosouzapw/OmniRoute/issues/11894) — thanks @hizzt) ([#12380](https://github.com/diegosouzapw/OmniRoute/pull/12380)) — thanks @pacocartones +- **fix(api):** `GET /v1/models` with `MODELS_CATALOG_PREFIX_MODE=canonical` (or `?prefix=canonical`) now lists providers whose registry alias is undefined or equal to their own id (Antigravity, Antigravity CLI and other self-aliased built-ins) — their single `provider/model` id was dropped by the alias/canonical duplicate guard in the static, synced, custom and alias-backed catalog loops ([#12058](https://github.com/diegosouzapw/OmniRoute/issues/12058)) — thanks @cheynetom ([#12381](https://github.com/diegosouzapw/OmniRoute/pull/12381)) — thanks @pacocartones +- **fix(translator):** Drop replayed `thinking` blocks that carry no signature (the shape produced from cross-provider `reasoning_content`) instead of stamping the default Claude signature on them, which Anthropic rejected with `400 Invalid signature in thinking block` on the next turn served by an Anthropic rung ([#12105](https://github.com/diegosouzapw/OmniRoute/issues/12105)) — thanks @atescivitci-cmd ([#12386](https://github.com/diegosouzapw/OmniRoute/pull/12386)) — thanks @pacocartones +- **fix(cli):** Resolve Bun's `--preload` polyfill path against the package root instead of `dist/`, so `omniroute` installed with `bun install -g` no longer crashes at startup with `error: preload not found …/dist/open-sse/utils/setupPolyfill.ts` ([#11980](https://github.com/diegosouzapw/OmniRoute/issues/11980)) — thanks @joglomedia ([#12387](https://github.com/diegosouzapw/OmniRoute/pull/12387)) — thanks @pacocartones +- **fix(providers):** `gemini-business` now publishes its model catalog — `/v1/models` and `/v1/providers/gemini-business/models` list the 12 enterprise Gemini ids the executor understands instead of returning an empty list (#12107) ([#12389](https://github.com/diegosouzapw/OmniRoute/pull/12389)) — thanks @pacocartones +- **fix(db):** install `busy_timeout` before the SQLite connection's first statement so a process opening the database while another one closes its WAL connection waits out the transient EXCLUSIVE lock instead of dying with `database is locked`, and recognise the drivers' real BUSY/PROTOCOL/IOERR errors as transient in the corruption probe so the same lock no longer renames the database away as corrupt; deflakes `cross-process contenders never both acquire the same connection` (#12394 — thanks @pacocartones) +- **fix(chat-admission):** derive the `chat_admission_busy` 503 `Retry-After` from observed heavyweight-lease occupancy — the larger of the exhausted `OMNIROUTE_CHAT_ADMISSION_QUEUE_MS` window and the time since capacity last turned over, capped at 60 s — instead of a fixed 1 s (structural) / 2 s (byte-stage) hint that invited Codex/agent fan-out clients to re-send ~1 MiB `/v1/responses` bodies every second into a gate held for the whole SSE lifetime; an idle gate keeps the historical floors ([#12135](https://github.com/diegosouzapw/OmniRoute/issues/12135)) (#12395 — thanks @pacocartones) +- **fix(api-manager):** the API key permissions modal no longer silently drops `allowedCombos` entries its Combo picker cannot render — routing-rule names such as `rt-*`, which the backend already honours — when "All" is clicked and the key is switched back to "Restrict"; those entries now survive the toggle, are listed read-only under the combo list so the count and the list agree, and are saved back verbatim instead of persisting `[]` (deny-all) (#12397 — thanks @pacocartones) +- **fix(catalog):** write the NUL separator of the catalog connection memo key, the provider serviceKind memo key, the Video Bridge promotion group key and a JSON-exactness test fixture as the `\u0000` escape instead of a raw byte — same runtime value, but the raw byte made git, GitHub and ripgrep treat those files as binary (hidden PR diffs, silently skipped searches); a guard test now keeps raw NUL bytes out of `src/`, `open-sse/` and `tests/` (#12403 — thanks @pacocartones) +- **fix(providers):** add `CLAUDE_CODE_CLIENT_VERSION` and `GITHUB_COPILOT_CLI_VERSION` env overrides so Anthropic/Copilot client-version gates can be unblocked without a rebuild ([#12417](https://github.com/diegosouzapw/OmniRoute/issues/12417)) ([#12632](https://github.com/diegosouzapw/OmniRoute/pull/12632)) — thanks @HouMinXi +- **fix(db):** back-fill `last_ping_at` and `last_pinged_reset_key` on `provider_connections` during schema reconciliation so divergent lineages that skipped `123_quota_auto_ping` still accept quota auto-ping writes ([#12470](https://github.com/diegosouzapw/OmniRoute/pull/12470) — thanks @KooshaPari) +- **fix(ci):** document MIT exceptions for `@eloqnt/{config,format-json,format-po}` (next-intl transitive; locked tarballs omit `license`) and keep the A2A lifecycle vitest off the real SQLite persistence seam ([#12581](https://github.com/diegosouzapw/OmniRoute/issues/12581)) ([#12595](https://github.com/diegosouzapw/OmniRoute/pull/12595)) +- OpenCode plugin `/v1/models` catalog fetch now waits 30s by default and attaches HTTP `statusCode` on 401/5xx so host fallback plugins can hop instead of seeing an untyped AbortError/UnknownError. ([#12607](https://github.com/diegosouzapw/OmniRoute/pull/12607)) — thanks @RaviTharuma +- **CI:** the OpenAPI security-tier gate now mirrors `isAlwaysProtectedPath()` in full — it also reads `ALWAYS_PROTECTED_API_PATTERNS`, so the pattern-gated credential routes (`/api/providers/{id}/{claude,codex}-auth/{export,apply-local}`, GHSA-5926-2w35-7h4q) no longer report as unannotated. (#12605) +- **fix(api):** GET /v1/models no longer waits forever on a hung coalesced catalog rebuild; cold-path waits are bounded (`CATALOG_BUILD_TIMEOUT_MS`, default 8s) and a last-good 200 is served when the rebuild times out ([#12627](https://github.com/diegosouzapw/OmniRoute/issues/12627)). ([#12628](https://github.com/diegosouzapw/OmniRoute/pull/12628)) — thanks @RaviTharuma +- **fix(cursor):** a non-array `tool_calls` on an assistant message no longer crashes the cursor request translator with a `TypeError`; both loops now require an array ([#12689](https://github.com/diegosouzapw/OmniRoute/issues/12689)) ([#12691](https://github.com/diegosouzapw/OmniRoute/pull/12691)) — thanks @soroush5 +- **fix(chat):** Chat Completions no longer return empty `content` after a server-owned memory or skills tool; the first provider send and account/model recovery now share one pipeline so a follow-up round-trip can fill the reply ([#12696](https://github.com/diegosouzapw/OmniRoute/issues/12696)) — thanks @HouMinXi ([#12867](https://github.com/diegosouzapw/OmniRoute/pull/12867)) +- **fix(dashboard):** batch-deleting provider keys no longer toasts failure after a successful delete when the confirm button's click event is forwarded as `onAfter` ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) — thanks @HouMinXi +- **fix(glm):** drop the extra 16th argument to `createSSETransformStreamWithLogger` that TypeScript rejected (TS2554) and that never reached the TransformStream ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) ([#12770](https://github.com/diegosouzapw/OmniRoute/pull/12770)) — thanks @HouMinXi +- **fix(dashboard):** pass `nodeMap` into Runtime `QuotaGroup` so a quota monitor in error/exhausted/alerting no longer throws `ReferenceError: nodeMap is not defined`. ([#12868](https://github.com/diegosouzapw/OmniRoute/pull/12868)) — thanks @HouMinXi +- Restricted API keys whose `allowedModels` lists a combo name no longer skip every combo member at pre-dispatch (`ALL_TARGETS_SKIPPED`). Inner-target filtering still applies when the allow-list is a provider prefix or `disableNonPublicModels` is on ([#12899](https://github.com/diegosouzapw/OmniRoute/pull/12899)). — thanks @HouMinXi +- **fix(combos):** Effort-suffixed combo members inherit the base model's `model_context_overrides` row so priority order is not inverted on large requests ([#12926](https://github.com/diegosouzapw/OmniRoute/pull/12926)) — thanks @HouMinXi +- **fix(catalog):** Picker-added `customModels` enter the dispatch-time live catalog so combo members and bare inference no longer 400 ([#12934](https://github.com/diegosouzapw/OmniRoute/pull/12934)) — thanks @HouMinXi +- **fix(docker):** re-derive the Docker build's worker-pool memory budget from the MEASURED ~4.5 GB per-process RSS (the issue owner's own VPS dmesg OOM-killer reproduction) instead of the stale 2560 MB/worker inference #11419 shipped, and lower `OMNIROUTE_BUILD_WORKERS` 3 → 2 so 1 parent + 1 page-data worker (2 processes × 4.5 GB = 9 GB) fits the 12.288 GB (75%) budget on a 16 GB GitHub Actions runner — the previous default (1 parent + 2 workers = 13.5 GB) still overcommitted the runner and kept "Publish to Docker Hub" failing with `cannot allocate memory` after #11419 merged (#7518). ([#11663](https://github.com/diegosouzapw/OmniRoute/pull/11663)) +- **fix(electron):** the packaged Windows desktop build now passes the #7592 cold-restart smoke end to end. Five defects were found and fixed on the way: (1) optional-pack staging failed on any machine whose `tar` is GNU tar — it reads the drive letter in an absolute `-f C:\...` archive path as a remote rsh host (`Cannot connect to C:`), so staging now passes a bare filename with `cwd` at the tarball directory and surfaces tar stderr on failure; (2) the Electron `files` allowlist was missing `lib/loginHeaderCapture.js`, so the packaged main process crashed at startup with `Cannot find module './lib/loginHeaderCapture'`; (3) electron-builder ≥26 injects an `!**/node_modules/**` ignore into every extraResources pattern list that no positive filter can override, which silently dropped the staged runtime `node_modules` (including the better-sqlite3 N-API prebuild) from `resources/app` — a fresh v3.8.50 desktop build would have shipped with no native SQLite driver and reproduced the #7592 sql.js fallback on every machine; an `afterPack` hook now restores the staged `node_modules` after packing; (4) the packaged-app smoke harness redirected `USERPROFILE` into its temp DATA_DIR, but Electron resolves the Roaming profile from `%USERPROFILE%\AppData\Roaming\` (USERPROFILE takes precedence over the APPDATA env var) and the path service throws instead of creating it, so `requestSingleInstanceLock()` returned false and the app exited(0) silently before `app.whenReady()` — the harness now pre-creates the derived tree, and `ensureSmokeEnvDirs` is exported and unit-tested; (5) the #7592 driver assertion parsed a `[DB] Driver: ...` line that the server's primary DB path never printed (only the unused `openDatabaseAsync()` did) — `getDbInstance()` now emits the same line on open so the guard can actually assert the native driver. Also: the smoke env-allowlist unit test hardcoded POSIX paths and could only pass on Linux/macOS; expectations are now host-agnostic, with new regression tests for the USERPROFILE-derived tree and `tarPack` under absolute Windows-style paths ([#11443](https://github.com/diegosouzapw/OmniRoute/pull/11443)) — thanks @jonlwheat2-gif +- fix(sse): stop the auto-combo candidates inspector from silently dropping model-locked/cooled-down rows (#9133) ([#11994](https://github.com/diegosouzapw/OmniRoute/pull/11994)) — thanks @hartmark +- **fix(providers):** Perplexity Web no longer turns upstream stream failures into successful assistant text; pre-content failures remain eligible for fallback, partial output ends with a structured sanitized error, and failed sessions are not persisted ([#12465](https://github.com/diegosouzapw/OmniRoute/pull/12465)) +- **fix(video-bridge):** preserve chronological video and transcript fusion, source-frame timestamps, transcript provenance, full contact-sheet intervals, and pre-abort callback isolation ([#11681](https://github.com/diegosouzapw/OmniRoute/pull/11681)) +- **Z.ai Web:** HTTP 200 streams carrying an upstream error now terminate with a structured failure instead of assistant text plus a normal stop, preserving partial output while allowing pre-content combo fallback. ([#12454](https://github.com/diegosouzapw/OmniRoute/pull/12454)) +- Absorb `Error [AbortError]: request_signal_aborted` and DOMException AbortError shapes in the process-level client-abort crash guard so routine client disconnects no longer kill the server (exit code 7). ([#12165](https://github.com/diegosouzapw/OmniRoute/pull/12165)) — thanks @alvinveroy +- **fix(sse):** Treat Adapta Web `type:error` SSE events as sanitized non-stream failures instead of empty HTTP 200 completions. ([#12459](https://github.com/diegosouzapw/OmniRoute/pull/12459)) +- **fix(autoCombo):** layer 3 (`models_dev_tier`) no longer scores retired models as premium, merge cross-provider capability rows deterministically, and inherit tier scores through variant ids ([#11508](https://github.com/diegosouzapw/OmniRoute/issues/11508)). `getModelsDevTierFitness` now vetoes ids present in `config/quality/model-lifecycle.json` with status `retired` before any other signal; `loadModelCapabilities()` aggregates per `model_id` (any-non-null-true for booleans, max for `limit_context`) instead of last-write-wins over SQLite's undefined row order; a capabilities miss retries through `resolveScoresAs()`'s base id and reports `models_dev_tier:inherited`, matching layers 1–2 (#11492). Adds `getModelsDevTierFitnessWithSource()` and an `invalidateCapabilitiesCache()` test/ops hook. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) — thanks @Meet6338-X +- fix(sse): accept Claude `content:[]` + `stop_reason: max_tokens`/`tool_use` in `detectMalformedNonStream` (match `isEmptyContentResponse`) so Claude Code `/model` probes with `max_tokens: 1` no longer become a false 502 ([#11568](https://github.com/diegosouzapw/OmniRoute/pull/11568)) — thanks @Seramicx +- **fix(security):** sanitize `request.failed` diagnostics before publishing them to live dashboard listeners and replay history, while keeping status, model, provider, latency, and internal call-log diagnostics intact. ([#12469](https://github.com/diegosouzapw/OmniRoute/pull/12469)) +- **fix(pwa):** do not intercept dashboard navigations so Chrome can fall back from a stale HTTP/3 Alt-Svc advertisement (UDP :20128 is unpublished; F5 on a long-lived tab hung until a new tab opened a fresh TCP connection). ([#12767](https://github.com/diegosouzapw/OmniRoute/pull/12767)) — thanks @HouMinXi +- **fix(executors):** handle DuckDuckGo ERR_BN_LIMIT (418) without retrying — when the upstream returns `418 ERR_BN_LIMIT` (rate-limit/ban), the executor now returns the error immediately instead of burning another VQD acquisition that would only count against the IP limit. The retry logic for `418 ERR_CHALLENGE` (unsolved challenge) remains unchanged. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) ([#12110](https://github.com/diegosouzapw/OmniRoute/pull/12110)) — thanks @oyi77 / @Meet6338-X +- Fixed DuckDuckGo streaming responses losing JSON lines and UTF-8 characters split across network chunks. ([#11528](https://github.com/diegosouzapw/OmniRoute/pull/11528)) — thanks @pacocartones +- Harden generated Cloudflare, Vercel, and Deno relays by stripping hop-by-hop, framing, and proxy authentication headers before forwarding requests upstream. ([#11533](https://github.com/diegosouzapw/OmniRoute/pull/11533)) — thanks @pacocartones +- **feat(grok-cli):** Provider Limits shows grok-cli banked reset credits from `GetRemainingResets` (including a real zero; a failed RPC omits the row) and the existing View credits button now calls `ConsumerUiSvc/RedeemReset` for grok-cli. Live tokens use nested fields 10/20/30 (id + Timestamp), not compact 1/2/3. ([#12805](https://github.com/diegosouzapw/OmniRoute/pull/12805)) — thanks @HouMinXi +- **fix(memory):** Embedding Model Quick select, Embedding Source remote dropdown, and Rerank selector now list every configured provider with embedding/rerank support instead of only chat-catalog text matches plus OpenRouter live discovery; a generic OpenAI-compatible `/embeddings` + Cohere-compatible `/rerank` runtime fallback resolves any configured chat provider's embedding/rerank endpoint, so unlisted providers no longer fail with "Unknown embedding provider"; both memory selectors gained a free-text model override ([#11390](https://github.com/diegosouzapw/OmniRoute/pull/11390)) — thanks @rqzbeh +- Harden SQLite upgrades around the historical migration-074 version collision: missing discovery and inspector tables are replayed atomically, pre-existing databases (including setup-created skeletons) receive reusable content-addressed safety snapshots, and Node test/eval probes without `DATA_DIR` are isolated from the operator database. ([#12435](https://github.com/diegosouzapw/OmniRoute/pull/12435)) +- **fix(dashboard):** Moonshot/Kimi Open Platform voucher and cash leftover percentages follow the bucket balance, so an empty wallet no longer paints those rows as 100% while Available is 0% ([#12733](https://github.com/diegosouzapw/OmniRoute/pull/12733)) — thanks @HouMinXi +- **fix(combo):** return non-retryable HTTP 400 when all candidates for a pinned native Codex turn are unavailable due to model-scoped lockout, terminating the turn cleanly while preserving turn continuity and enabling standard Combo routing on subsequent turns ([#12240](https://github.com/diegosouzapw/OmniRoute/pull/12240)) — thanks @mdigitalbh81 +- Fixed auto-router strategies preserving the selected connection through ranking and dispatch when multiple connections share the same provider and model. ([#11530](https://github.com/diegosouzapw/OmniRoute/pull/11530)) — thanks @pacocartones +- Preserve Chat Completions JSON-object response formats when translating requests to the Responses API. ([#11529](https://github.com/diegosouzapw/OmniRoute/pull/11529)) — thanks @pacocartones +- Fix idempotency fingerprints so Responses API requests include `input` and other semantic generation fields using stable serialization, while excluding credentials and request metadata. ([#11532](https://github.com/diegosouzapw/OmniRoute/pull/11532)) — thanks @pacocartones +- **fix(api):** Generated API CLI commands now enforce required OpenAPI request bodies; Combo test commands forward the required `comboName` body, while API keys created by older writers after migration 149 preserve legacy allow-all Combo access without widening explicit empty allowlists — thanks @marcelokarval ([#12070](https://github.com/diegosouzapw/OmniRoute/pull/12070)) +- **fix(grok-web):** treat upstream streaming failures as failures instead of successful assistant text: error-only streams now fail readiness with HTTP 502, while failures after legitimate content preserve that partial output and terminate through the sanitized stream failure path without a normal `stop` completion. ([#12458](https://github.com/diegosouzapw/OmniRoute/pull/12458)) +- HuggingChat now turns HTTP 200 JSONL generation failures into a sanitized 502 before content, or a fixed public stream failure after partial output, so fallback and request persistence no longer record a false successful stop. ([#12456](https://github.com/diegosouzapw/OmniRoute/pull/12456)) +- **fix(providers):** keep 1min.ai HTTP 200 stream errors out of assistant content, preserve partial output, and expose sanitized terminal errors so pre-content failures can fall back. ([#12466](https://github.com/diegosouzapw/OmniRoute/pull/12466)) +- **fix(sessions):** preserve legacy Sessions data when the additive exclusive-session projection is unavailable, returning an empty projection and warning only once per contiguous outage instead of failing the endpoint; the Sessions badge intentionally reflects the merged legacy and exclusive row count introduced by [#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389) ([#11469](https://github.com/diegosouzapw/OmniRoute/pull/11469)) — thanks @KaspaPulse +- Render Nimble Search with the generic provider icon and serve Opper's proven logo locally. ([#12415](https://github.com/diegosouzapw/OmniRoute/pull/12415)) +- **fix(quality):** `config/quality/provider-assets-provenance.jsonl` was stale against the physical `public/providers/` tree (still listing 225 assets and 5 duplicate-content groups after several provider-retirement PRs removed 85 icon files), which would have made `check:provider-asset-provenance` fail on the next full run. Reconciled the manifest to the real 142-file snapshot (142 asset records, 1 duplicate-content group, 2 newly-registered files marked `unresolved` pending provenance research), repinned `auditedCommit`, and updated the regression test's hardcoded counts to match. ([#11876](https://github.com/diegosouzapw/OmniRoute/pull/11876)) +- fix(ui): wrap quota card footer action buttons to prevent clipping Refresh button on narrow cards — when extra action buttons such as `View credits` are present on Codex cards, allow the footer row to flex-wrap with `shrink-0` buttons so `Refresh now` remains visible and clickable across all responsive card widths ([#11464](https://github.com/diegosouzapw/OmniRoute/pull/11464)) — thanks @adevwithpurpose +- Keep Antigravity Gemini usable when the same connection's Claude weekly quota is empty; generic quota cache stays per-connection for every other provider. ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) — thanks @HouMinXi +- Fixed Tailscale tunnel setup to reject ports outside the valid 1-65535 range and safely install Windows MSI packages from paths containing apostrophes. ([#11534](https://github.com/diegosouzapw/OmniRoute/pull/11534)) — thanks @pacocartones +- Classify Cloudflared and Tailscale process-management routes as local-only and spawn-capable while preserving remote access to read-only tunnel status endpoints. ([#11531](https://github.com/diegosouzapw/OmniRoute/pull/11531)) — thanks @pacocartones +- Electron release workflow: the `publish-npm` job now grants `actions: read` to the reusable `npm-publish.yml` it calls (its `publish` job requests it), which is what made GitHub refuse the whole v3.8.50 run at startup and ship the release with zero desktop assets; a `workflow_dispatch` now builds the requested tag instead of the dispatching branch and can skip the npm leg (`publish_npm=false`) when only re-attaching assets ([#11974](https://github.com/diegosouzapw/OmniRoute/pull/11974)) +- **fix(dashboard):** The Combos page usage guide now reads its dismissal through `useSyncExternalStore` instead of correcting SSR state inside an effect, removing an extra commit of the page tree on every load (and the `react-hooks/set-state-in-effect` error it raised). ([#12671](https://github.com/diegosouzapw/OmniRoute/pull/12671)) +- Electron release: `electron/package-lock.json` regained the optional `electron-builder-squirrel-windows` subtree (13 entries) that `npm ci` had been refusing as out of sync, `electron-release.yml` gained a `build_ref` dispatch input and stops regenerating release notes on a re-attach dispatch, and the npm publish workflow attaches the SBOM to the GitHub Release on dispatch publishes too — so the v3.8.51 tag ships every desktop asset and the SBOM like v3.8.49 did ([#12022](https://github.com/diegosouzapw/OmniRoute/pull/12022)) +- Drained the reds every PR against `release/v3.8.51` was born with: documented the three service-worker build-id variables, registered the six retirement/tunnel tests with the mutation gate, approved `eslint-plugin-react-hooks` in the dependency allowlist, added the six `combo.sort.*` strings to `vi` and `pt-BR`, pointed the ChatGPT Web doc at the real migration-168 test, worded the g4f hint around the member key, and realigned four tests to the retired-provider catalog and the legacy-schema fixtures the retirement migrations touch. ([#11940](https://github.com/diegosouzapw/OmniRoute/pull/11940)) +- **fix(providers):** Claude, Grok, LMArena, Notion, and Perplexity web-cookie transports now use pooled `wreq-js` 3.2 instead of the native sidecar, with all nine supported bindings pinned and audited, and the applicable platform binding plus native-license evidence included in each release artifact ([#12429](https://github.com/diegosouzapw/OmniRoute/pull/12429), supersedes [#11753](https://github.com/diegosouzapw/OmniRoute/pull/11753)). +- **fix(providers):** Zed Hosted streaming failures now trigger fallback before content and end partial streams with a sanitized structured error instead of fake assistant text and a normal-success stop. ([#12455](https://github.com/diegosouzapw/OmniRoute/pull/12455)) +- **fix(test):** capture hook result after render (direct commit `b85d0abb0b`) +- **fix(sync):** repair the two regressions the v3.8.50 sync-back left on release/v3.8.51 (direct commit `5ade9e0851`) +- **revert(models):** drop the memo reset hook ported from #10055 (direct commit `282de841ae`) +- **fix(typecheck):** guard the optional connectionId before the reactive model sync (direct commit `f2825bad75`) +- **fix(quota-share):** release the winner's reserved in-flight slot (#11371) ([#11408](https://github.com/diegosouzapw/OmniRoute/pull/11408)) — thanks @oyi77 +- **fix(providers):** stop silently dropping reasoning effort on opencode families (#10788) ([#11409](https://github.com/diegosouzapw/OmniRoute/pull/11409)) — thanks @oyi77 +- **fix(combo):** enable genuine quota-aware routing for generic providers (antigravity, claude, etc.) ([#11411](https://github.com/diegosouzapw/OmniRoute/pull/11411)) — thanks @HouMinXi +- **fix(oauth):** allow expired token retry before deactivation and activate expired retry loop (#P0) ([#11414](https://github.com/diegosouzapw/OmniRoute/pull/11414)) — thanks @HouMinXi +- **perf(executors):** lazy-load the executor registry — defer class imports + construction to first use (#11220) ([#11421](https://github.com/diegosouzapw/OmniRoute/pull/11421)) — thanks @oyi77 +- **fix:** make release/v3.8.50 CI gates green (run 32786966560 root causes) ([#11450](https://github.com/diegosouzapw/OmniRoute/pull/11450)) — thanks @jonlwheat2-gif +- **fix(sse):** exact-domain cookie match and origin-equality URL assertions ([#11452](https://github.com/diegosouzapw/OmniRoute/pull/11452)) — thanks @hartmark +- **fix(providers):** send Responses API payload during OpenAI-compatible validation ([#11454](https://github.com/diegosouzapw/OmniRoute/pull/11454)) — thanks @tuandinh0801 +- **fix(command-code):** fallback to /alpha/generate for Go plan without Provider API access ([#11455](https://github.com/diegosouzapw/OmniRoute/pull/11455)) — thanks @Chewji9875 +- **fix(responses-continuation):** fail closed on a log-truncated stored input/output array ([#11473](https://github.com/diegosouzapw/OmniRoute/pull/11473)) — thanks @hartmark +- **fix(cli):** write jcode settings as [providers.omniroute] in config.toml ([#11484](https://github.com/diegosouzapw/OmniRoute/pull/11484)) — thanks @hongnoul +- **fix(logging):** raise chat-log array truncation cap to 1000 and unify duplicate implementations ([#11499](https://github.com/diegosouzapw/OmniRoute/pull/11499)) — thanks @hartmark +- **fix:** restore live websocket URL helpers ([#11509](https://github.com/diegosouzapw/OmniRoute/pull/11509)) — thanks @PauloFH +- **perf(core):** eliminate async waterfalls across home, cache, and db operations (#11396) ([#11512](https://github.com/diegosouzapw/OmniRoute/pull/11512)) — thanks @HouMinXi +- **fix(kiro):** add runtime.us-east-1.kiro.dev as first-attempt endpoint ([#11517](https://github.com/diegosouzapw/OmniRoute/pull/11517)) — thanks @rifqiawl +- **fix(providers):** remove duplicate 128-tool truncation in OpencodeExecutor (#11444) ([#11518](https://github.com/diegosouzapw/OmniRoute/pull/11518)) — thanks @HouMinXi +- **fix(providers):** poll onboardUser LRO before classifying BYOP ([#11519](https://github.com/diegosouzapw/OmniRoute/pull/11519)) — thanks @rifqiawl +- **fix(translator):** preserve exact tool name casing for Claude tool_use payloads (#11487) ([#11520](https://github.com/diegosouzapw/OmniRoute/pull/11520)) — thanks @HouMinXi +- **fix(kimi-web):** align health probe with executor domain ([#11521](https://github.com/diegosouzapw/OmniRoute/pull/11521)) — thanks @pacocartones +- **fix(cli):** preserve existing admin password during setup ([#11522](https://github.com/diegosouzapw/OmniRoute/pull/11522)) — thanks @pacocartones +- **fix(mitm):** keep Windows DNS elevation off the POSIX sudo path (#11430) ([#11542](https://github.com/diegosouzapw/OmniRoute/pull/11542)) — thanks @official-burak +- **fix(sse):** scale chat admission by ingest byte budget instead of a fixed request count ([#11548](https://github.com/diegosouzapw/OmniRoute/pull/11548)) — thanks @TheDemonTuan +- **fix:** accept Gemini Web cookie exports ([#11554](https://github.com/diegosouzapw/OmniRoute/pull/11554)) — thanks @rafacpti23 +- **fix(i18n):** complete Brazilian Portuguese translations ([#11558](https://github.com/diegosouzapw/OmniRoute/pull/11558)) — thanks @rafacpti23 +- **fix(sse):** explicit types for openai-responses pureHelpers — clears last failing core typecheck gate ([#11567](https://github.com/diegosouzapw/OmniRoute/pull/11567)) — thanks @jonlwheat2-gif +- **fix(opencode):** hydrate Proxy Pool references for no-auth connections ([#11584](https://github.com/diegosouzapw/OmniRoute/pull/11584)) — thanks @AStupidBear +- **fix(ci):** resolve inherited release/v3.8.51 base-red CI failures ([#11608](https://github.com/diegosouzapw/OmniRoute/pull/11608)) — thanks @jonlwheat2-gif +- **fix(db):** fallback load STORAGE_ENCRYPTION_KEY from env files ([#11614](https://github.com/diegosouzapw/OmniRoute/pull/11614)) — thanks @benzntech +- **fix(providers):** mark Brave Search as no longer having a free tier ([#11621](https://github.com/diegosouzapw/OmniRoute/pull/11621)) — thanks @turbolego +- **fix:** pass tool result content through as string in OpenAI/Claude -> Gemini translation ([#11624](https://github.com/diegosouzapw/OmniRoute/pull/11624)) — thanks @Hsia97 +- **fix(sse):** scope credential health to exact key ([#11628](https://github.com/diegosouzapw/OmniRoute/pull/11628)) — thanks @KaspaPulse +- **fix(install):** guard prepare script against missing husky ([#11634](https://github.com/diegosouzapw/OmniRoute/pull/11634)) — thanks @Prajeeth-12 +- **fix(security):** redact NVIDIA_API_KEY at the log sink in the ad-hoc diag script ([#11639](https://github.com/diegosouzapw/OmniRoute/pull/11639)) +- **fix(types):** clear the two type regressions red on release/v3.8.51 ([#11640](https://github.com/diegosouzapw/OmniRoute/pull/11640)) — thanks @jonlwheat2-gif +- **fix(build):** resolve workspace: protocol dependencies to concrete semver versions during prepublish (#11540) ([#11641](https://github.com/diegosouzapw/OmniRoute/pull/11641)) — thanks @HouMinXi +- **fix(search):** properly resolve configured search connection in /v1/responses pipeline (#11524) ([#11642](https://github.com/diegosouzapw/OmniRoute/pull/11642)) — thanks @HouMinXi +- **fix(authz):** trust cf-connecting-ip for via-proxy detection when peer is Cloudflare edge (#11514) ([#11643](https://github.com/diegosouzapw/OmniRoute/pull/11643)) — thanks @HouMinXi +- **fix(security):** compare the OIDC state cookie in constant time ([#11646](https://github.com/diegosouzapw/OmniRoute/pull/11646)) +- **fix(security):** namespace the dedup hash by the calling API key ([#11649](https://github.com/diegosouzapw/OmniRoute/pull/11649)) +- **fix(models):** gate the Codex-native catalog loop on prefix mode (#11632) ([#11662](https://github.com/diegosouzapw/OmniRoute/pull/11662)) — thanks @xiaoyaner0201 +- **fix(dashboard):** project Codex routing quota in account pools ([#11666](https://github.com/diegosouzapw/OmniRoute/pull/11666)) — thanks @KaspaPulse +- **fix(ddgw):** normaliza system no upstream da release v3.8.51 ([#11692](https://github.com/diegosouzapw/OmniRoute/pull/11692)) — thanks @RhianB14 +- **fix(memory):** honor category filter in GET /api/memory ([#11699](https://github.com/diegosouzapw/OmniRoute/pull/11699)) — thanks @Sabeekhann +- **fix(ci):** detect stale workspace lockfile entries ([#11714](https://github.com/diegosouzapw/OmniRoute/pull/11714)) — thanks @pacocartones +- **fix(ci):** stamp dist/BUILD_SHA before the npm publish provenance gate ([#11721](https://github.com/diegosouzapw/OmniRoute/pull/11721)) +- **fix(docker):** decouple the best-effort Bun image from the release manifest ([#11724](https://github.com/diegosouzapw/OmniRoute/pull/11724)) +- **perf(compression):** avoid cloning memo misses twice ([#11727](https://github.com/diegosouzapw/OmniRoute/pull/11727)) — thanks @RaviTharuma +- **fix(docker):** let the bun digest artifact be absent, not fatal ([#11740](https://github.com/diegosouzapw/OmniRoute/pull/11740)) +- **fix(shared):** block cloud-metadata hosts under default remote-image guard ([#11755](https://github.com/diegosouzapw/OmniRoute/pull/11755)) — thanks @santosraju99-hub +- **fix(api):** preserve caller-provided X-Correlation-Id on chat completions ([#11760](https://github.com/diegosouzapw/OmniRoute/pull/11760)) — thanks @aniruddhaadak80 +- **fix(resilience):** lock permanently retired models instead of short backoff (Gemini ban prevention) ([#11762](https://github.com/diegosouzapw/OmniRoute/pull/11762)) — thanks @turbolego +- **fix:** mark Vercel AI Gateway as passthroughModels ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **fix(resilience):** stop hammering permanently-moved endpoints and billing-suspended accounts ([#11774](https://github.com/diegosouzapw/OmniRoute/pull/11774)) — thanks @turbolego +- **fix(lease):** remove global static reservation and gate routing on live active lease occupancy ([#11775](https://github.com/diegosouzapw/OmniRoute/pull/11775)) — thanks @TheDemonTuan +- **fix(ci):** size the install-upgrade gate to a measured run, and log the pack cost ([#11776](https://github.com/diegosouzapw/OmniRoute/pull/11776)) +- **fix(pwa):** stop serving the stale shell after deploys ([#11779](https://github.com/diegosouzapw/OmniRoute/pull/11779)) — thanks @HouMinXi +- **fix(combo):** lock GitHub models rejected as "not supported" for future requests ([#11781](https://github.com/diegosouzapw/OmniRoute/pull/11781)) — thanks @turbolego +- **fix(cli):** probe both IPv4 and IPv6 loopback for server readiness (#11766) ([#11794](https://github.com/diegosouzapw/OmniRoute/pull/11794)) — thanks @vermasomesh835 +- **fix(sse):** preserve CLIProxyAPI wrappers per connection ([#11797](https://github.com/diegosouzapw/OmniRoute/pull/11797)) — thanks @RaviTharuma +- **fix(providers):** honor model auto-fetch opt-in on create ([#11798](https://github.com/diegosouzapw/OmniRoute/pull/11798)) — thanks @RaviTharuma +- **fix(dashboard):** make model auto-fetch order independent ([#11805](https://github.com/diegosouzapw/OmniRoute/pull/11805)) — thanks @RaviTharuma +- **fix(docker):** make GHA cache exports best effort ([#11806](https://github.com/diegosouzapw/OmniRoute/pull/11806)) — thanks @RaviTharuma +- **fix(kiro):** do not permanently ban on 'User is not authorized to make this call' ([#11809](https://github.com/diegosouzapw/OmniRoute/pull/11809)) — thanks @Deftera186 +- **fix(sse):** keep cache-write tokens in OpenAI-shaped usage ([#11814](https://github.com/diegosouzapw/OmniRoute/pull/11814)) — thanks @TheDemonTuan +- **fix(docs):** resolve relative markdown and wiki links across Fumadocs and GitHub wiki ([#11834](https://github.com/diegosouzapw/OmniRoute/pull/11834)) — thanks @AndrianBalanescu +- **fix(chat):** preserve unstripped model string for passthrough provider routing ([#11840](https://github.com/diegosouzapw/OmniRoute/pull/11840)) — thanks @raheemuddin786 +- **fix(mcp):** avoid async initialization inside sync esm wrappers (#11569) ([#11849](https://github.com/diegosouzapw/OmniRoute/pull/11849)) — thanks @HouMinXi +- **fix(sse):** preserve coherent markdown token boundaries across stream translation chunks (#11606) ([#11851](https://github.com/diegosouzapw/OmniRoute/pull/11851)) — thanks @HouMinXi +- **fix(ci):** run the install-upgrade gate on disk, not on the /tmp tmpfs ([#11855](https://github.com/diegosouzapw/OmniRoute/pull/11855)) +- **fix(skills):** expand shorthand property types in injected tool schemas ([#11857](https://github.com/diegosouzapw/OmniRoute/pull/11857)) — thanks @alvinveroy +- **fix(ci):** publish npm from a hosted runner so provenance is accepted ([#11877](https://github.com/diegosouzapw/OmniRoute/pull/11877)) +- **fix(lint):** pin react hooks plugin to 7.0.1 ([#11886](https://github.com/diegosouzapw/OmniRoute/pull/11886)) +- **fix(ci):** surface and clear release-green ESLint failure ([#11890](https://github.com/diegosouzapw/OmniRoute/pull/11890)) — thanks @MumuTW +- **fix(guardrails):** prevent duplicate prompt-injection-guard log output ([#11936](https://github.com/diegosouzapw/OmniRoute/pull/11936)) — thanks @patrykkopycinski +- **fix(auth):** downgrade expected transient states from warn to debug ([#11937](https://github.com/diegosouzapw/OmniRoute/pull/11937)) — thanks @patrykkopycinski +- **fix(codex):** fail over image generation for imported free plans ([#11948](https://github.com/diegosouzapw/OmniRoute/pull/11948)) — thanks @Bl0ck154 +- **fix(codex):** normalize non-stream responses ([#11951](https://github.com/diegosouzapw/OmniRoute/pull/11951)) — thanks @Bl0ck154 +- **fix(images):** forward Antigravity image size ([#11952](https://github.com/diegosouzapw/OmniRoute/pull/11952)) — thanks @Bl0ck154 +- **fix(db):** invalidate connection cache after upsert ([#11953](https://github.com/diegosouzapw/OmniRoute/pull/11953)) — thanks @Bl0ck154 +- **fix(codex):** restore imported account state ([#11954](https://github.com/diegosouzapw/OmniRoute/pull/11954)) — thanks @Bl0ck154 +- **fix(sse):** give extended-thinking targets the reasoning readiness budget ([#11959](https://github.com/diegosouzapw/OmniRoute/pull/11959)) — thanks @abhisheksharma2411 +- **fix(dashboard):** use opaque background and readable text color on cost chart tooltips ([#11960](https://github.com/diegosouzapw/OmniRoute/pull/11960)) — thanks @ZaimMarzuki +- **fix(antigravity):** send complete loadCodeAssist metadata (ideType/platform/pluginType as numeric enums) ([#11969](https://github.com/diegosouzapw/OmniRoute/pull/11969)) — thanks @rifqiawl +- **fix(core):** resolve DB init race condition and reasoning translation ([#12003](https://github.com/diegosouzapw/OmniRoute/pull/12003)) — thanks @raheemuddin786 +- **fix(admission):** exclude reclaimable page cache from the cgroup pressure ratio ([#12017](https://github.com/diegosouzapw/OmniRoute/pull/12017)) — thanks @HouMinXi +- **fix(build):** prune non-production trees in NFT trace excludes and tsconfig ([#12028](https://github.com/diegosouzapw/OmniRoute/pull/12028)) — thanks @Chewji9875 +- **fix(migrations):** add renamed migration compatibility for 056/073/077/101 ([#12036](https://github.com/diegosouzapw/OmniRoute/pull/12036)) — thanks @oyi77 +- **fix(sse):** default crash-guard logger to console.warn, not console ([#12042](https://github.com/diegosouzapw/OmniRoute/pull/12042)) — thanks @HouMinXi +- **fix(catalog):** derive vision/modalities for built-in auto combos from effective target pool ([#12046](https://github.com/diegosouzapw/OmniRoute/pull/12046)) — thanks @Prajeeth-12 +- **fix(release):** the packaged-app smoke verifies the database opened, not a driver line the primary path never prints (twin of #12032) ([#12047](https://github.com/diegosouzapw/OmniRoute/pull/12047)) +- **fix(barrel):** migrate src/app/ off the @/lib/localDb barrel import (#11795 Phase 2) ([#12051](https://github.com/diegosouzapw/OmniRoute/pull/12051)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate src/lib/ off the localDb barrel to direct db imports (#59) ([#12052](https://github.com/diegosouzapw/OmniRoute/pull/12052)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate open-sse, src/shared, src/sse, src/models, src/domain off the @/lib/localDb barrel import (#11795 Phase 4) ([#12053](https://github.com/diegosouzapw/OmniRoute/pull/12053)) — thanks @jonlwheat2-gif +- **fix(barrel):** delete the @/lib/localDb barrel — every consumer migrated (#11795 Phase 5) ([#12055](https://github.com/diegosouzapw/OmniRoute/pull/12055)) — thanks @jonlwheat2-gif +- **fix(radar):** restore D12 public boundary ([#12057](https://github.com/diegosouzapw/OmniRoute/pull/12057)) +- **fix(dev):** bound webpack and Tailwind scans ([#12075](https://github.com/diegosouzapw/OmniRoute/pull/12075)) — thanks @backryun +- **fix(dev):** isolate root layout settings reads ([#12076](https://github.com/diegosouzapw/OmniRoute/pull/12076)) — thanks @backryun +- **fix(dev):** reduce instrumentation executor fan-out (phase 3) ([#12078](https://github.com/diegosouzapw/OmniRoute/pull/12078)) — thanks @backryun +- **fix(dev):** make logging resources HMR-singleton ([#12079](https://github.com/diegosouzapw/OmniRoute/pull/12079)) — thanks @backryun +- **fix(sse):** keep unavailable forced connections scoped ([#12080](https://github.com/diegosouzapw/OmniRoute/pull/12080)) — thanks @keeltrace +- **fix(dev):** isolate batch dispatch from instrumentation ([#12081](https://github.com/diegosouzapw/OmniRoute/pull/12081)) — thanks @backryun +- **fix(ci):** reconcile release test contract drift ([#12082](https://github.com/diegosouzapw/OmniRoute/pull/12082)) — thanks @backryun +- **fix(ci):** accept CVE-2025-68121 in the prebuilt tls-client .so, auto-close base-red issues, guard Scorecard on the default branch ([#12085](https://github.com/diegosouzapw/OmniRoute/pull/12085)) +- **fix(db):** drop three consumer-less 1proxy exports — dead-code base-red on release/v3.8.51 after the barrel deletion (#12055) ([#12087](https://github.com/diegosouzapw/OmniRoute/pull/12087)) +- **fix(api):** clear the six API-route TypeScript regressions the new gate landed red on ([#12094](https://github.com/diegosouzapw/OmniRoute/pull/12094)) +- **fix(release):** never let the tag-push Create Release append auto notes to the curated body ([#12096](https://github.com/diegosouzapw/OmniRoute/pull/12096)) +- **fix(sse):** honor CLIProxyAPI environment API key ([#12099](https://github.com/diegosouzapw/OmniRoute/pull/12099)) — thanks @RaviTharuma +- **fix(oauth):** bind Google refresh to the client that issued the token ([#12106](https://github.com/diegosouzapw/OmniRoute/pull/12106)) — thanks @HouMinXi +- **fix(dashboard):** make RequestLoggerDetail loadable outside Next — CSS via globals.css + CJS/ESM interop (#11703 base-reds) ([#12114](https://github.com/diegosouzapw/OmniRoute/pull/12114)) +- **fix(leases):** project status lease row to lease columns so joined connection PII never escapes ([#12115](https://github.com/diegosouzapw/OmniRoute/pull/12115)) — thanks @geek007git +- **fix(plugins):** do not kill the plugin process when a fire-and-forget hook times out ([#12116](https://github.com/diegosouzapw/OmniRoute/pull/12116)) — thanks @geek007git +- **fix(guardrails):** restore injection-guard logging on middleware-only routes ([#12117](https://github.com/diegosouzapw/OmniRoute/pull/12117)) — thanks @geek007git +- **fix(plugins):** refresh stored manifest from disk on activate so new hook fields reach existing installs ([#12120](https://github.com/diegosouzapw/OmniRoute/pull/12120)) — thanks @geek007git +- **fix(codex):** preserve existing provider state when bulk-import upserts a matching connection ([#12122](https://github.com/diegosouzapw/OmniRoute/pull/12122)) — thanks @geek007git +- **fix(ci):** clear the two base-reds the 2026-08-30 merge batch left on release/v3.8.51 (round 3) ([#12123](https://github.com/diegosouzapw/OmniRoute/pull/12123)) +- **fix(sse):** strip type:'custom' from Claude tools on agentrouter dispatch ([#12126](https://github.com/diegosouzapw/OmniRoute/pull/12126)) — thanks @wahidsadik371-coder +- **fix(sse):** stop advertising video providers the dispatcher cannot run ([#12131](https://github.com/diegosouzapw/OmniRoute/pull/12131)) — thanks @quiterunner-commits +- **fix(search):** treat HTTP 432 and plan limit errors as transient cooldown ([#12139](https://github.com/diegosouzapw/OmniRoute/pull/12139)) — thanks @HouMinXi +- **fix(sse):** refuse an AI Horde queue that cannot fit the request budget ([#12143](https://github.com/diegosouzapw/OmniRoute/pull/12143)) — thanks @quiterunner-commits +- **fix(ci):** clear the base-reds the afternoon merge batch left on release/v3.8.51 (round 5: provider count 352, TS2554/TS2677) ([#12144](https://github.com/diegosouzapw/OmniRoute/pull/12144)) +- **fix(translator):** strip neutral tool_choice when tools absent in Responses-to-Chat (#12141) ([#12166](https://github.com/diegosouzapw/OmniRoute/pull/12166)) — thanks @HouMinXi +- **fix(guardrails):** pass providerId to getResolvedModelCapabilities in checkComboVision (#12112) ([#12169](https://github.com/diegosouzapw/OmniRoute/pull/12169)) — thanks @HouMinXi +- **fix(translator):** strip plaintext reasoning content for opaque responses backends (#12128) ([#12171](https://github.com/diegosouzapw/OmniRoute/pull/12171)) — thanks @HouMinXi +- **perf(stream):** compile hot-path regexes once, bound token caches, fix quadratic buffering ([#12179](https://github.com/diegosouzapw/OmniRoute/pull/12179)) — thanks @opensource-elearning +- **fix:** resolve compression worker file using runtime anchors instead of… ([#12183](https://github.com/diegosouzapw/OmniRoute/pull/12183)) — thanks @NightStalker-87 +- **fix:** prevent Claude Code session kills via liveness-aware readiness + auto model echo ([#12189](https://github.com/diegosouzapw/OmniRoute/pull/12189)) — thanks @opensource-elearning +- **fix(translator):** preserve falsy primitive values in Gemini and Antigravity function response results ([#12191](https://github.com/diegosouzapw/OmniRoute/pull/12191)) — thanks @Siva010 +- **fix(oauth):** mark empty Antigravity projectId as degraded and clear stale errors (#11284) ([#12205](https://github.com/diegosouzapw/OmniRoute/pull/12205)) — thanks @HouMinXi +- **fix(providers):** gate the Codex auto-ping usage read on the shared quota throttle ([#12209](https://github.com/diegosouzapw/OmniRoute/pull/12209)) — thanks @abhisheksharma2411 +- **fix(combos):** prioritize SQLite row id over inner JSON id and notify delete errors ([#12213](https://github.com/diegosouzapw/OmniRoute/pull/12213)) — thanks @HouMinXi +- **fix(providers):** mark featherless.ai as no longer having a free tier (`hasFree: false`) ([#12216](https://github.com/diegosouzapw/OmniRoute/pull/12216)) — thanks @turbolego +- **fix(deepseek-web):** stop Turbopack dev panic in the PoW worker path resolver ([#12221](https://github.com/diegosouzapw/OmniRoute/pull/12221)) — thanks @hartmark +- **fix(chat-admission):** clarify local 503 source ([#12223](https://github.com/diegosouzapw/OmniRoute/pull/12223)) — thanks @keeltrace +- **fix(dev):** silence webpack runtime module warnings ([#12228](https://github.com/diegosouzapw/OmniRoute/pull/12228)) — thanks @backryun +- **fix(db):** use module.require for CommonJS runtime driver loading ([#12230](https://github.com/diegosouzapw/OmniRoute/pull/12230)) — thanks @raheemuddin786 +- **fix(memory):** honest probe-driven FTS5 keyword status + memory_id rowid sync ([#12231](https://github.com/diegosouzapw/OmniRoute/pull/12231)) — thanks @raheemuddin786 +- **fix(system):** propagate abort signal to stream reader in HTTP version checks ([#12232](https://github.com/diegosouzapw/OmniRoute/pull/12232)) — thanks @raheemuddin786 +- **fix(resilience):** isolate local host execution errors from provider circuit breakers ([#12233](https://github.com/diegosouzapw/OmniRoute/pull/12233)) — thanks @raheemuddin786 +- **fix(sse):** sort injected tools deterministically for prompt caching ([#12234](https://github.com/diegosouzapw/OmniRoute/pull/12234)) — thanks @raheemuddin786 +- **fix(combo):** always clear the loop-safety timer, not just on the happy path (#11804) ([#12245](https://github.com/diegosouzapw/OmniRoute/pull/12245)) +- **fix(quality):** base-red pair — stryker tap registration + turn-pin suites aligned to the window gate ([#12255](https://github.com/diegosouzapw/OmniRoute/pull/12255)) — thanks @backryun +- **fix(dev):** qualify Turbopack runtime boundaries ([#12258](https://github.com/diegosouzapw/OmniRoute/pull/12258)) — thanks @backryun +- **fix(combo):** fall back to full pool when collapsed sole survivor is context-too-small ([#12278](https://github.com/diegosouzapw/OmniRoute/pull/12278)) — thanks @opensource-elearning +- **fix(combo):** bound the pre-dispatch unavailable skip so a stale label cannot dark a pool (#12168) ([#12285](https://github.com/diegosouzapw/OmniRoute/pull/12285)) +- **fix(security):** strip Qwen/Alibaba console-session cookies from provider API responses ([#12287](https://github.com/diegosouzapw/OmniRoute/pull/12287)) +- **fix(usage):** console-aware Token Plan guidance + subscription hint on bailian 401 ([#12288](https://github.com/diegosouzapw/OmniRoute/pull/12288)) +- **fix(resourcePressure):** log numeric detail on every rejection, recover faster ([#12293](https://github.com/diegosouzapw/OmniRoute/pull/12293)) — thanks @hartmark +- **fix(release):** drain the 2026-09-01 base-red window — passthrough usage regression + radarPage i18n keys ([#12327](https://github.com/diegosouzapw/OmniRoute/pull/12327)) +- **fix(ci):** openapi-security-tiers checker must honor routeGuard patterns + imported prefixes ([#12350](https://github.com/diegosouzapw/OmniRoute/pull/12350)) — thanks @ggiak +- **perf(ci):** cache node_modules in the npm-ci-retry composite (#8084 D3) ([#12408](https://github.com/diegosouzapw/OmniRoute/pull/12408)) +- **revert(ui):** point CTAs back at their real destinations ([#12410](https://github.com/diegosouzapw/OmniRoute/pull/12410)) +- **fix(release):** drain the 2026-09-02 base-red — rerank-providers import + api-typecheck baseline ratchet ([#12414](https://github.com/diegosouzapw/OmniRoute/pull/12414)) +- **fix(types):** make system prompt injection noImplicitAny-safe ([#12416](https://github.com/diegosouzapw/OmniRoute/pull/12416)) +- **fix(quality):** validate typecheck baseline schema ([#12419](https://github.com/diegosouzapw/OmniRoute/pull/12419)) +- **fix(memory):** resolve rerank provider node cache import ([#12421](https://github.com/diegosouzapw/OmniRoute/pull/12421)) +- **fix(ci):** repair release-root regressions — pack dedup, web-session syntax, uc-image ids ([#12423](https://github.com/diegosouzapw/OmniRoute/pull/12423)) — thanks @backryun +- **fix(providers):** separate MaxAI and UC credential contracts ([#12431](https://github.com/diegosouzapw/OmniRoute/pull/12431)) +- **fix(cli):** preserve Claude settings on config set ([#12432](https://github.com/diegosouzapw/OmniRoute/pull/12432)) — thanks @KrzysiekSko +- **fix(providers):** repair the maxai credential block truncated by merge auto-resolve ([#12433](https://github.com/diegosouzapw/OmniRoute/pull/12433)) +- **fix(quality):** record the 2026-09-02 merged growth in the file-size baseline ([#12434](https://github.com/diegosouzapw/OmniRoute/pull/12434)) +- **fix(uc):** route UC error strings through sanitizeErrorMessage; allowlist the retired codex id ([#12437](https://github.com/diegosouzapw/OmniRoute/pull/12437)) +- **fix(adapta):** redact streamed upstream errors ([#12438](https://github.com/diegosouzapw/OmniRoute/pull/12438)) +- **fix(sse):** remap non-contiguous upstream tool_calls index to a gap-free output_index ([#12445](https://github.com/diegosouzapw/OmniRoute/pull/12445)) — thanks @hartmark +- **fix(quality):** detect duplicate tool_calls entries in one response ([#12446](https://github.com/diegosouzapw/OmniRoute/pull/12446)) — thanks @hartmark +- **fix(conversations):** resolve turn content OmniRoute never sends back to the client ([#12447](https://github.com/diegosouzapw/OmniRoute/pull/12447)) — thanks @hartmark +- **fix(combo):** do not treat credits-exhausted 401 as auth skip ([#12449](https://github.com/diegosouzapw/OmniRoute/pull/12449)) — thanks @RaviTharuma +- **fix(auth):** do not park healthy quota accounts as expired ([#12452](https://github.com/diegosouzapw/OmniRoute/pull/12452)) — thanks @RaviTharuma +- **fix(responses-continuation):** fail closed on a collector-truncated, empty output array ([#12460](https://github.com/diegosouzapw/OmniRoute/pull/12460)) — thanks @hartmark +- **fix(logging):** raise the SSE payload collector's default cap ([#12461](https://github.com/diegosouzapw/OmniRoute/pull/12461)) — thanks @hartmark +- **fix(resilience):** surface Responses failed.error.message in 502s ([#12472](https://github.com/diegosouzapw/OmniRoute/pull/12472)) — thanks @RaviTharuma +- **fix(security):** unbiased maxai X-Random nonce + stricter URL/regex assertions ([#12502](https://github.com/diegosouzapw/OmniRoute/pull/12502)) +- **fix(video):** re-anchor transcript log-redaction so PII/credential maskers can't reopen the leak (#12150 P1 follow-up) ([#12503](https://github.com/diegosouzapw/OmniRoute/pull/12503)) +- **fix(monitoring):** serve cached credentialHealth off the request path ([#12533](https://github.com/diegosouzapw/OmniRoute/pull/12533)) — thanks @RaviTharuma +- **fix(quota):** keep Antigravity Gemini usable when Claude weekly is empty ([#12566](https://github.com/diegosouzapw/OmniRoute/pull/12566)) — thanks @HouMinXi +- **fix(authz):** hard-gate every credential export and CLI-config write (GHSA-5926-2w35-7h4q) ([#12600](https://github.com/diegosouzapw/OmniRoute/pull/12600)) +- **fix(docs):** restore the Next build — REMOVED_PROVIDERS.md had no frontmatter (base-red #12581) ([#12610](https://github.com/diegosouzapw/OmniRoute/pull/12610)) +- **fix(quality):** re-point the zcodeProtocol public-creds allowlist to line 313 ([#12615](https://github.com/diegosouzapw/OmniRoute/pull/12615)) +- **fix(security):** close 3 advisories — search baseUrl exfil, sk- in the error sanitizer, bifrost relay header leak ([#12620](https://github.com/diegosouzapw/OmniRoute/pull/12620)) +- **fix(providers):** sanitize boolean required and nested bare maps for Gemini (#12269) ([#12624](https://github.com/diegosouzapw/OmniRoute/pull/12624)) — thanks @HouMinXi +- **fix(resilience):** keep Overloaded STREAM_EARLY_EOF off the provider breaker ([#12626](https://github.com/diegosouzapw/OmniRoute/pull/12626)) — thanks @HouMinXi +- **fix(catalog):** re-audit free-tier quotas against official pages ([#12649](https://github.com/diegosouzapw/OmniRoute/pull/12649)) +- **fix(dashboard):** keep a request's pending-tracking id stable across combo target retries ([#12650](https://github.com/diegosouzapw/OmniRoute/pull/12650)) — thanks @hartmark +- **fix(tests):** retire dead model ids from the chat-pipeline integration suite (base-red #12581) ([#12670](https://github.com/diegosouzapw/OmniRoute/pull/12670)) +- **fix:** resolve SqliteError no such table compression_run_telemetry during cleanup ([#12682](https://github.com/diegosouzapw/OmniRoute/pull/12682)) — thanks @groovecityJO +- **fix(combo):** treat a pin-only step as implicit connection allowlist ([#12697](https://github.com/diegosouzapw/OmniRoute/pull/12697)) — thanks @HouMinXi +- **fix(ci):** ship .npmrc in published package so legacy-peer-deps applies to consumers (#11544) ([#12699](https://github.com/diegosouzapw/OmniRoute/pull/12699)) — thanks @KooshaPari +- **fix(docker):** pin 4 CLI tools to exact versions (#12576) ([#12703](https://github.com/diegosouzapw/OmniRoute/pull/12703)) — thanks @KooshaPari +- **fix(docker):** document and harden cli profile trust boundary (#12570) ([#12706](https://github.com/diegosouzapw/OmniRoute/pull/12706)) — thanks @KooshaPari +- **fix(sse):** redact video transcript in pre-guardrail rejected-request logs (#12150 P2 item 7) ([#12710](https://github.com/diegosouzapw/OmniRoute/pull/12710)) +- **fix(i18n):** quote placeholder in OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES description (#12505) ([#12769](https://github.com/diegosouzapw/OmniRoute/pull/12769)) — thanks @KooshaPari +- **fix(api/pricing):** surface validation error message as string, not raw object (#12494) ([#12771](https://github.com/diegosouzapw/OmniRoute/pull/12771)) — thanks @KooshaPari +- **fix(sse):** re-enable prompt compression for native Codex passthrough ([#12834](https://github.com/diegosouzapw/OmniRoute/pull/12834)) — thanks @thomasmaerz +- **fix(dashboard):** model health tests for a provider node set to the Responses API now call `/v1/responses` with a Responses-shaped body instead of `/v1/chat/completions` — those models were reported as `Provider returned HTTP 200 but no text content` even though the same model answered normally through `/v1/responses` ([#13070](https://github.com/diegosouzapw/OmniRoute/issues/13070)) ([#13087](https://github.com/diegosouzapw/OmniRoute/pull/13087)) — thanks @ntdatt812 +- fix(routing): stop a round-robin combo's "opencode" targets from collapsing onto the opencode-zen connection (#11912) ([#13283](https://github.com/diegosouzapw/OmniRoute/pull/13283)) +- fix(routing): stop the reactive-compaction debug log from lying when compression is globally disabled (#11977) ([#13276](https://github.com/diegosouzapw/OmniRoute/pull/13276)) +- fix(electron): relativize standalone-bundle symlink targets so Stage 8 manifest verification stops failing on Windows (#11979) ([#13251](https://github.com/diegosouzapw/OmniRoute/pull/13251)) +- fix(dashboard): surface a visible error when Compression Studio's combined preview run fails (#12061) ([#13288](https://github.com/diegosouzapw/OmniRoute/pull/13288)) +- fix(dashboard): make the compression "Effective pipeline" preview honor the active profile and warn when the master switch is off (#12063) ([#13287](https://github.com/diegosouzapw/OmniRoute/pull/13287)) +- fix(providers): scope TinyCMS Web signer's DOM shims to each call instead of leaking them for the process lifetime, and surface a clean HTTP status on a non-JSON interception-toggles error (#12072) ([#13254](https://github.com/diegosouzapw/OmniRoute/pull/13254)) +- fix(guardrails): stop Vision Bridge from re-selecting a model locked after a 404 (#12111) ([#13259](https://github.com/diegosouzapw/OmniRoute/pull/13259)) +- fix(sse): require Responses-shaped body before native OpenAI-compatible passthrough (#12129) ([#13278](https://github.com/diegosouzapw/OmniRoute/pull/13278)) +- fix(providers): minimax-m3 now collapses manual thinking.type:"enabled" to adaptive, preventing upstream 400 (2013) (#12132) ([#13249](https://github.com/diegosouzapw/OmniRoute/pull/13249)) +- fix(db): scope model visibility overrides by modality so hiding a Chat model no longer hides an identically-ID'd Image/Embeddings/etc. model (#12172) ([#13258](https://github.com/diegosouzapw/OmniRoute/pull/13258)) +- fix(db): scope local-provider apiKey dedup to matching base URL so LM Studio/Ollama-style connections support multiple accounts (#12173) ([#13250](https://github.com/diegosouzapw/OmniRoute/pull/13250)) +- fix(providers): refresh Trae's stale Referer/Origin and forward user timezone so imported connections stop failing with 401 (#12190) ([#13255](https://github.com/diegosouzapw/OmniRoute/pull/13255)) +- fix(providers): route opencode-go/gpt-5.6-luna to /responses instead of /chat/completions (#12196) ([#13275](https://github.com/diegosouzapw/OmniRoute/pull/13275)) +- fix(dashboard): allow deleting the last extra-upstream-header row even when invalid (#12251) ([#13252](https://github.com/diegosouzapw/OmniRoute/pull/13252)) +- **fix(i18n):** translate pre-existing `__MISSING__:` keys for `combo.sort`, `requestLogger.detail` expand/collapse, `common.profile`, and `settings.resilienceCredentialHealth*` across 39 locales ([#12272](https://github.com/diegosouzapw/OmniRoute/issues/12272)) ([#13034](https://github.com/diegosouzapw/OmniRoute/pull/13034)) — thanks @HouMinXi +- fix(cli): run the Node.js runtime compatibility guard before the heavy `tsx/esm` + Commander import chain so an unsupported runtime gets a clear message instead of a raw `Invalid regular expression flags` crash (#12296) ([#13253](https://github.com/diegosouzapw/OmniRoute/pull/13253)) +- fix(dashboard): refresh the providers list after deleting a compatible provider node (#12298) ([#13256](https://github.com/diegosouzapw/OmniRoute/pull/13256)) +- fix(usage): fail closed on API-key budget enforcement when a provider's `auto` routing alias has no pricing row, instead of silently counting it as $0 (#12341) ([#13257](https://github.com/diegosouzapw/OmniRoute/pull/13257)) +- **fix(providers):** include the submitted Agnes video model when polling by `video_id` ([#12356](https://github.com/diegosouzapw/OmniRoute/pull/12356)) — thanks @pacocartones +- **fix(routing):** custom OpenAI-compatible nodes now honor the saved Chat/Responses API type after edits instead of letting the node's original ID prefix override the live connection setting ([#11884](https://github.com/diegosouzapw/OmniRoute/issues/11884)). ([#12358](https://github.com/diegosouzapw/OmniRoute/pull/12358)) — thanks @pacocartones +- fix(sse): surface an error instead of a silent empty 200 when a Claude stream closes with zero bytes (#12398) ([#13285](https://github.com/diegosouzapw/OmniRoute/pull/13285)) +- fix(oauth): warn before the dead localhost:8080 redirect in antigravity/gemini `oauth start` (#12413) ([#13265](https://github.com/diegosouzapw/OmniRoute/pull/13265)) +- fix(providers): stop devin-cli spawn error from double-closing the SSE controller (#12517) ([#13261](https://github.com/diegosouzapw/OmniRoute/pull/13261)) +- **fix(rerank):** clamp Voyage `top_k` to the documents actually sent after empty-string filtering, and honor `return_documents: false` in the NVIDIA response adapter (#12523 — thanks @pacocartones) +- **fix(audio):** `/v1/audio/translations` now resolves combo names the way `/v1/audio/transcriptions` already does, so a combo that `GET /v1/models` advertises is fanned out to its targets instead of being rejected with `400 Invalid translation model: . Use format: provider/model`; literal `provider/model` ids and unknown bare names behave as before (#12536 — thanks @pacocartones) +- **fix(gemini):** strip the JSON-Schema-2020-12 `prefixItems` keyword from Gemini tool schemas at every nesting level, so Claude Code tool definitions no longer fail with `400 Unknown name "prefixItems"` on Gemini models (#12540 — thanks @pacocartones) +- **fix(api-manager):** Expose an accessible loading status while API keys are fetched instead of an empty accessibility tree (#12541 — thanks @pacocartones) +- **fix(video):** Clamp `estimateJpegFrameBytes` at zero for padding-only payloads and build the three encode-side frame data URIs from `JPEG_FRAME_DATA_URI_PREFIX` instead of a repeated literal (#12543 — thanks @pacocartones) +- **fix(devin):** accept Windows `DEVIN_AGENTIC_HOME` sandbox paths (`C:\...\.sandbox\...`) in the isolated-home check so the Devin Claude Bridge no longer fails closed on Windows ([#12405](https://github.com/diegosouzapw/OmniRoute/issues/12405)) (#12545 — thanks @pacocartones) +- **fix(db):** Add `conversation_turn_nodes` and orphaned `agentic_conversations` to the auto-cleanup cycle under the existing `retention.callLogs` window, so identity nodes whose call-log content has already been purged no longer accumulate without bound in `storage.sqlite` (#12548 — thanks @pacocartones) +- **fix(i18n):** Wrap the `~/.claude/profiles//settings.json` placeholder in ICU single quotes in the `OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES` feature-flag description across all 42 locales and the TypeScript default, so next-intl no longer fails with `INVALID_MESSAGE: UNCLOSED_TAG` and the Feature Flags card shows the description instead of the raw key (#12549 — thanks @pacocartones) +- **fix(orchestration):** `updateCloudAgentTask` now publishes the task's real `status` on `agent.task.updated` when an update only touches `result`, `activities` or `error`, instead of the fabricated `"updated"` state, and stays silent when no row matched the id (#12550 — thanks @pacocartones) +- **fix(i18n):** the home "Recent Requests" panel and the Provider Topology legend are now translated instead of rendering English copies on non-English dashboards; the legend reads its own `home.topologyLegend*` labels with consistent casing rather than borrowing the memory-settings "Recent" and analytics "Error" strings (#12551 — thanks @pacocartones). +- **docs(reference):** bring the `FEATURE_FLAGS.md` catalog back to 1:1 with `featureFlagDefinitions.ts` — 20 missing flags added, the two `*_BLOCK_THRESHOLD` env-only knobs moved out of the flag tables, category/total counts and the Live WS port corrected, guarded by a static test (#12552 — thanks @pacocartones) +- **fix(i18n):** backfill missing `usage.kiloPass*` strings in 39 locales and restore `featureFlagOmnirouteDisableThinkingLevelVariantsDescription` in `pt.json` ([#12561](https://github.com/diegosouzapw/OmniRoute/issues/12561)) ([#13027](https://github.com/diegosouzapw/OmniRoute/pull/13027)) — thanks @HouMinXi +- fix(docker): default docker-compose app ports (dashboard/API/live-WS) to loopback instead of `0.0.0.0`, closing the anonymous `/v1` LAN/WAN exposure gap left open by `REQUIRE_API_KEY=false` (#12568) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): close DNS-rebinding SSRF gap in webhook outbound-URL guard (#12569) ([#13243](https://github.com/diegosouzapw/OmniRoute/pull/13243)) +- fix(docker): require a per-session token on the VNC browser CDP bridge and isolate it on a dedicated Docker network (#12571) ([#13241](https://github.com/diegosouzapw/OmniRoute/pull/13241)) +- fix(open-sse): write Adobe Firefly session tokens and cookie jars with 0700/0600 permissions instead of the process umask (#12572) ([#13242](https://github.com/diegosouzapw/OmniRoute/pull/13242)) +- fix(api): remove hardcoded wildcard CORS in openai-to-gemini-sse.ts so the centralized fail-closed CORS gate is the sole source of `Access-Control-Allow-Origin` (#12573) ([#13239](https://github.com/diegosouzapw/OmniRoute/pull/13239)) +- fix(api): enforce API key policy (budget/rate-limit/schedule/endpoint scoping) on the ElevenLabs speech-to-text, text-to-speech and voices proxy routes (#12574) ([#13244](https://github.com/diegosouzapw/OmniRoute/pull/13244)) +- fix(sse): cap HuggingChat NDJSON body size and bound the read loop with the fetch timeout so a stalled or hostile upstream cannot buffer unbounded memory (#12577) ([#13282](https://github.com/diegosouzapw/OmniRoute/pull/13282)) +- fix(docker): scope the cliproxyapi/qdrant/bifrost sidecars to loopback by default and forward `CLIPROXYAPI_MANAGEMENT_KEY` into the cliproxyapi container so its management API is not left both unauthenticated and LAN-published (#12578) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): create DB export temp paths with `fs.mkdtempSync` instead of predictable timestamps (#12579) ([#13237](https://github.com/diegosouzapw/OmniRoute/pull/13237)) +- Cline 401 bodies that say "re-authenticate your Cline account" classify as a refreshable OAuth token, not a terminal expired key. The cooling panel no longer labels every cooldown as a 429; it shows the recorded last error instead. (#12594) ([#13060](https://github.com/diegosouzapw/OmniRoute/pull/13060)) — thanks @HouMinXi +- **fix(catalog):** degrade unknown combo targets instead of dropping LCD modalities, and persist OpenRouter `architecture.input_modalities` into the capability snapshot ([#12613](https://github.com/diegosouzapw/OmniRoute/issues/12613)) ([#13042](https://github.com/diegosouzapw/OmniRoute/pull/13042)) — thanks @HouMinXi +- fix(providers): send `x-api-key` instead of `Authorization: Bearer` for OpenCode Zen's `/v1/responses` endpoint (Muse Spark Contributor models), fixing a 401 on OmniRoute's auth header (#12633) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(sse): surface the actionable "Auggie CLI not found" message when the shell reports a missing `auggie` binary via exit code instead of a spawn error (#12645) ([#13263](https://github.com/diegosouzapw/OmniRoute/pull/13263)) +- **fix(config):** Persist deletions of built-in background-degradation entries — when a stored settings record exists its `degradationMap` is now authoritative instead of being merged under the defaults, so an entry the user removed in the dashboard no longer reappears on the next apply or restart ([#12424](https://github.com/diegosouzapw/OmniRoute/issues/12424)) ([#12647](https://github.com/diegosouzapw/OmniRoute/pull/12647)) — thanks @pacocartones +- **fix(gamification):** action-count badge milestones (First Token, Token Consumer, Token Machine, Token Whale, and the token-sharing tier) are now backed by a durable `xp_action_counts` counter incremented in `addXp()`, instead of a live `COUNT(*)` over `xp_audit_log`. The audit log is pruned by `retention.xpAuditLog` (default 30 days), so on a default install those "lifetime" milestones were really "actions in the last 30 days" and unlocked badges could stop unlocking once old rows aged out. `getActionCount()` and `checkActionCountBadges()` now read the same durable source, and a migration backfills existing totals from the surviving audit rows ([#12546](https://github.com/diegosouzapw/OmniRoute/issues/12546)) ([#12651](https://github.com/diegosouzapw/OmniRoute/pull/12651)) — thanks @pacocartones +- **fix(images):** `/v1/images/edits` now iterates a combo's targets the same way `/v1/images/generations` does (#9239) instead of flattening a bare combo to its first target. A combo whose first target is not edit-capable — or lacks credentials — now falls through to a later edit-capable target rather than hard-erroring, and missing credentials are skipped (not a hard `401`) to match the generations path. The per-target skip/terminal classification is extracted into a shared `runImageComboTargets` loop, so generations behavior is unchanged ([#12547](https://github.com/diegosouzapw/OmniRoute/issues/12547)). ([#12653](https://github.com/diegosouzapw/OmniRoute/pull/12653)) — thanks @pacocartones +- fix(sse): add first-byte watchdog to the TLS-fingerprint transport so a stalled wreq body falls back instead of hanging for minutes (#12656) ([#13272](https://github.com/diegosouzapw/OmniRoute/pull/13272)) +- fix(sse): exempt tiny-budget reasoning probes from combo quality failure and surface persisted-cooldown skips in ALL_TARGETS_SKIPPED diagnostics (#12659) ([#13269](https://github.com/diegosouzapw/OmniRoute/pull/13269)) +- fix(models): declare the real ~1M contextLength for OpenCode Zen's Muse Spark 1.2 models instead of falling back to the 200000 provider default (#12681) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(oauth): align codebuddy-cn OAuth User-Agent with the chat/usage CLI version to avoid WAF false positives (#12702) ([#13264](https://github.com/diegosouzapw/OmniRoute/pull/13264)) +- fix(dashboard): surface an authentication-required banner instead of silently blanking database settings for a guest session (#12709) ([#13270](https://github.com/diegosouzapw/OmniRoute/pull/13270)) +- **fix(resilience):** stop unbounded queue that hangs 6min until Aborted — gate, provider slot, and Bottleneck queue now share a per-connection `maxWaitMs` budget; `fail-closed` on exhaust (503); execution backstop `executionMaxWaitMs` overridable per connection with upstream clamp ([#12715](https://github.com/diegosouzapw/OmniRoute/pull/12715)) — thanks @maxmad64bis (with thanks to @Tushar49 for surfacing the slow-provider need in #12635) +- **fix(auto-combo):** every mode pack now carries `quality` and `reliability` weights (`quality` 0.02, 0.03 in `quality-first`; `reliability` 0.03, 0.04 in `reliability-first`), so selecting a pack no longer silences either signal; `DEFAULT_WEIGHTS` is unchanged ([#12731](https://github.com/diegosouzapw/OmniRoute/pull/12731)) — thanks @maxmad64bis +- **fix(sse):** the provider execution pipeline keeps the upstream error `code`/`type` again — both were lost when the non-streaming leg was extracted, so a config-class `422` (Antigravity missing project) degraded into a generic account cooldown instead of reaching the gate that recognises it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(db):** a legacy `call_logs` table no longer aborts startup — the provider-stats index is created after the column healing runs, not before it, so an install predating `request_type` opens instead of failing with `no such column` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(catalog):** a malformed row in the operator-writable custom-models blob no longer kills every `auto/*` pool ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(tests):** realign the guards that the same day's merges left asserting the old behavior — the provider-count total after GreenPT and EURouter, the GLM stream buffer slot that `#12925` turned into a declared parameter, and the injection scan bound that `#13104` reshaped from a head-only window into head-plus-tail ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- fix(cache): fold tool_choice/tools/response_format into the semantic cache signature so a cached tool_calls response can no longer be replayed for a request whose tool policy forbids it (#12734) ([#13267](https://github.com/diegosouzapw/OmniRoute/pull/13267)) +- **fix(models):** stop treating provider-supplied `isFree:true`/`:free`/`0/0` flags as trusted unless the provider has a documented free tier — only the shipped free-tier catalog decides otherwise; fetched `isFree:true` trusted only for a free-tier provider, custom `isFree:true` via a trusted path ([#12744](https://github.com/diegosouzapw/OmniRoute/pull/12744)) — thanks @maxmad64bis +- fix(memory): authenticate the internal /v1/rerank loopback call so memory reranking no longer silently degrades to unranked order when REQUIRE_API_KEY=true (#12745) ([#13268](https://github.com/diegosouzapw/OmniRoute/pull/13268)) +- fix(sse): parse Ollama Cloud's current usage markup (`$X of $Y used` aria-label, nested width style) (#12749) ([#13260](https://github.com/diegosouzapw/OmniRoute/pull/13260)) +- fix(cli): setup-opencode no longer sends an active context's management token to `/v1/models` when `--api-key`/`OMNIROUTE_API_KEY` is supplied — an explicit flag or the env var now always outranks the context's token, and the flag itself is no longer swallowed by the parent program's global `--api-key` option (#12783) ([#13246](https://github.com/diegosouzapw/OmniRoute/pull/13246)) +- fix(sse): register Arcee AI in the executor provider registry so requests reach api.arcee.ai instead of silently falling back to OpenAI (#12784) ([#13277](https://github.com/diegosouzapw/OmniRoute/pull/13277)) +- **fix(quota):** share one upstream quota read when identical saturation checks arrive at the same time, so traffic bursts don't multiply provider API calls ([#12787](https://github.com/diegosouzapw/OmniRoute/pull/12787)) — thanks @maxmad64bis +- **fix(db):** ignore expired or invalid rate-limit cooldown writes so a stale timestamp can't lock a connection that should be usable — clearing still works as before ([#12788](https://github.com/diegosouzapw/OmniRoute/pull/12788)) — thanks @maxmad64bis +- **fix(providers):** single source for provider order with `xao` ranking alongside `xai-oauth` ([#12790](https://github.com/diegosouzapw/OmniRoute/pull/12790)) — thanks @maxmad64bis +- **fix(routing):** off-table models check the free-model catalog before inheriting premium prices, latency bootstraps from the observed pool median, async tiers read live database pricing with a 90-day freshness gate, and the tier cache invalidates on every pricing write ([#12792](https://github.com/diegosouzapw/OmniRoute/pull/12792)) — thanks @maxmad64bis +- **fix(auto-combo):** snapshot scoring uses the observed per-connection account tier and per-model quality instead of neutral constants, and circuit-open providers rank lower at build time ([#12794](https://github.com/diegosouzapw/OmniRoute/pull/12794)) — thanks @maxmad64bis +- **fix(pool):** empty `auto/*` pools now say why they are empty, and the models listing uses the same paid check as routing ([#12795](https://github.com/diegosouzapw/OmniRoute/pull/12795)) — thanks @maxmad64bis +- fix(routing): recognize CLIProxyAPI's 'unknown provider for model' 400 as fallback-worthy (#12800) ([#13284](https://github.com/diegosouzapw/OmniRoute/pull/13284)) +- **fix(sse):** translate-mode streams now emit the estimated token counts as a trailing usage-only chunk before `[DONE]` when the upstream stays silent, so metered chat clients see totals instead of nothing ([#12828](https://github.com/diegosouzapw/OmniRoute/pull/12828)) — thanks @maxmad64bis +- **fix(db):** provider stats stay truthful on empty and legacy databases: fallback counts default to `0` instead of `null`, latency averages read `null` (not `0`) when no durations were recorded, failures logged before the error-type column existed group under `pre_migration` instead of `unclassified`, and per-provider queries use two new composite indexes ([#12832](https://github.com/diegosouzapw/OmniRoute/pull/12832)) — thanks @maxmad64bis +- fix(nvidia): fail open when a synced model catalog goes stale instead of gating forever (#12849) ([#13248](https://github.com/diegosouzapw/OmniRoute/pull/13248)) +- **fix(db):** WAL maintenance lives in its own module: a `TRUNCATE` checkpoint that hits a busy database now warns with its streak and retries once via `PASSIVE` instead of logging success, and closing-time checkpoints no longer report success on builds without a database file, with the busy totals visible in the authenticated monitoring health payload ([#12853](https://github.com/diegosouzapw/OmniRoute/pull/12853)) — thanks @maxmad64bis +- **fix(health):** quota with no snapshots reads empty instead of a contradicting 0% ([#12857](https://github.com/diegosouzapw/OmniRoute/pull/12857)) — thanks @maxmad64bis +- fix(a2a): accept the dashboard's own session cookie on /a2a so "Run message/send" no longer fails with "Unauthorized: missing or invalid API key" (#12888) ([#13271](https://github.com/diegosouzapw/OmniRoute/pull/13271)) +- **fix(logs):** the Logs grid's in-memory filter pass no longer discards rows the SQL query already matched — selecting an API key from the dropdown (which sends the key's id) returns its calls again, the Combo tab shows every combo instead of only those whose name contains a "1", and the model filter and search cover the same columns as the query ([#12896](https://github.com/diegosouzapw/OmniRoute/pull/12896)) — fixes [#12873](https://github.com/diegosouzapw/OmniRoute/issues/12873) — thanks @ntdatt812 +- **fix(a2a):** `/api/a2a/status` now builds the agent card from the request that asked for it, so a gateway reached at a non-localhost host no longer advertises `http://localhost:20128` as its A2A URL ([#12918](https://github.com/diegosouzapw/OmniRoute/pull/12918)). — thanks @ntdatt812 +- **fix(compression):** progressive aging now appends its `[COMPRESSED:aging:…]` annotation after a turn's `tool_result` blocks instead of in front of them, so Anthropic no longer rejects aged conversations with "`tool_use` ids were found without `tool_result` blocks immediately after" ([#12920](https://github.com/diegosouzapw/OmniRoute/pull/12920)). — thanks @ntdatt812 +- **fix(bedrock):** model import now resolves context limits for every vendor prefix instead of only `anthropic.*`, so `global.openai.gpt-5.6-*` no longer imports with a null `inputTokenLimit` and gets rejected pre-flight at the 200k default ([#12921](https://github.com/diegosouzapw/OmniRoute/pull/12921)). — thanks @ntdatt812 +- **fix(stream):** the 64 KB stream buffer GLM asks for is honoured instead of dropped, and the type error it caused no longer fails the API Route Typecheck gate on every open PR ([#12925](https://github.com/diegosouzapw/OmniRoute/pull/12925)) — thanks @ntdatt812 +- **fix(guardrails):** mask PII inside a `tool_result`'s nested content array, which the masker walked past while redacting its sibling block ([#12930](https://github.com/diegosouzapw/OmniRoute/pull/12930)) — thanks @ntdatt812 +- **fix(dashboard):** the Radar catalog table no longer leaves absent data unexplained — empty limits, unknown context windows, and unreported capabilities each explain themselves on hover, and a new check keeps it that way ([#12937](https://github.com/diegosouzapw/OmniRoute/pull/12937)) — thanks @maxmad64bis +- **fix(sse):** geo-blocked opencode requests rotate to the next account proxy instead of failing, so one refused egress no longer aborts the whole chain ([#12941](https://github.com/diegosouzapw/OmniRoute/pull/12941)) — thanks @maxmad64bis +- **fix(codex):** dashboard "clear cooldown" and the CAS recovery path now drop nested `codexScopeRateLimitedUntil` maps in the same write that nulls `rate_limited_until`, and fresh quota snapshots with headroom lift fallback-sourced scope cooldowns parked by quota preflight ([#12817](https://github.com/diegosouzapw/OmniRoute/issues/12817), [#12860](https://github.com/diegosouzapw/OmniRoute/issues/12860), [#12951](https://github.com/diegosouzapw/OmniRoute/pull/12951)) The dashboard reset-credit button (`consumeCodexResetCredit`) now exercises that same snapshot path after a successful redeem, so a filled quota bar is enough to unpark a leftover fallback Codex child without another manual clear. — thanks @HouMinXi +- **fix(db):** resolve `sql-wasm.wasm` across global npm install and hoisted layouts, ensuring OmniRoute can boot cleanly on Node 24 when native `better-sqlite3` is uncompiled. ([#13035](https://github.com/diegosouzapw/OmniRoute/pull/13035)) — thanks @HouMinXi +- **fix(api):** `DELETE /v1/batches/delete-completed` now sweeps only the calling API key's own completed batches (batches with no owner stay out of a key-scoped sweep on purpose), with an explicit instance-wide mode reserved for authenticated dashboard sessions, a 401 for a presented key that is unknown, revoked, deactivated, banned or expired (never falling through to the session branch), audit logging of every sweep, a sanitized 500 on failure, an owner-scoped file half (a key-scoped sweep never nulls a file another tenant owns) and an atomic sweep — chunked in 200-batch transactions in instance mode — so a mid-way error never leaves a batch pointing at a nulled file (GHSA-wvxc-jp3v-5mg5) ([#12969](https://github.com/diegosouzapw/OmniRoute/pull/12969)) +- **fix(combo):** quota-weighted routing stops drawing on an out-of-credit connection — a 402 now invalidates the stored quota snapshot instead of leaving its stale remaining percentage in place, and a snapshot older than 10 minutes no longer counts as confident headroom for the primary pool ([#12972](https://github.com/diegosouzapw/OmniRoute/pull/12972)) ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- **fix(vision):** Custom Models with "Vision capable" checked no longer have image requests swapped to `glm/glm-4.6v` when the client sends the advertised alias (`vllm/path/...`) or the bare path-shaped id — Vision Bridge now matches the stored override for all three id forms ([#12758](https://github.com/diegosouzapw/OmniRoute/issues/12758)) ([#12974](https://github.com/diegosouzapw/OmniRoute/pull/12974)) — thanks @HouMinXi +- **fix(sse):** transient opencode upstream failures rotate to the next account proxy instead of failing, so one flapping egress no longer aborts the whole chain ([#12975](https://github.com/diegosouzapw/OmniRoute/pull/12975)) — thanks @maxmad64bis +- **fix(azure):** Deployments from GPT-6 onward now send `max_completion_tokens` instead of `max_tokens`, which Azure rejects with HTTP 400. The rule matched a literal `gpt-5`, so each new generation arrived broken; it now matches the generation range, while `gpt-35-turbo` still keeps `max_tokens`. ([#13007](https://github.com/diegosouzapw/OmniRoute/pull/13007)) — thanks @ntdatt812 +- **fix(db):** add an opt-in self-restart circuit for sustained critical memory pressure, gate post-cleanup VACUUM behind a minimum freed-rows threshold, and checkpoint the SQLite WAL every 5 minutes with a size guard that escalates to TRUNCATE, so a growing WAL can no longer stall the event loop into a full outage. ([#13011](https://github.com/diegosouzapw/OmniRoute/pull/13011)) — thanks @HouMinXi +- **fix(auth):** an explicit connection pin may probe a quota-disabled row once and re-enable it on success ([#12874](https://github.com/diegosouzapw/OmniRoute/issues/12874)) ([#13017](https://github.com/diegosouzapw/OmniRoute/pull/13017)) — thanks @HouMinXi +- **fix(api):** thread `X-OmniRoute-Fallback-Attempts` through combo chat completions so streaming and non-streaming responses report how many prior legs were attempted ([#13038](https://github.com/diegosouzapw/OmniRoute/pull/13038)) — thanks @HouMinXi +- Restore provider failure classification and credential refresh on non-streaming requests: classify non-2xx failures to lock models on per-model quota exhaustion, update connection rate limits from headers and body, and pass credential refresh handlers to pipeline execution so 401 tokens can be refreshed and retried (#13043). ([#13069](https://github.com/diegosouzapw/OmniRoute/pull/13069)) — thanks @HouMinXi +- **fix(responses):** wrap forced-non-streaming web_search fallback JSON as Responses SSE so Codex still sees `response.completed` ([#13050](https://github.com/diegosouzapw/OmniRoute/pull/13050)) — thanks @HouMinXi +- **fix(antigravity):** Preserve upstream thought-token usage in normalized completion and reasoning token counts ([#13055](https://github.com/diegosouzapw/OmniRoute/pull/13055)) — thanks @pacocartones +- Fix a concurrency-slot leak in the DeepSeek PoW solver: a worker that failed to spawn (for example a missing worker script) never released its slot, so `MAX_CONCURRENT_WORKERS` failures disabled the solver until restart. ([#13097](https://github.com/diegosouzapw/OmniRoute/pull/13097)) — thanks @anhtahaylove +- **fix(acp):** bound the ACP session output buffers — `stdoutBuffer` and `stderrBuffer` now cap at 1 MiB keeping the most recent output behind a visible `[...output truncated...]` marker, and `stderrBuffer` is reset per prompt instead of accumulating for the lifetime of the session. ([#13100](https://github.com/diegosouzapw/OmniRoute/pull/13100)) — thanks @anhtahaylove +- **fix(acp):** release the `stdout`/`exit` listeners and the idle timer that a `sendPrompt` timeout used to leave attached to the `acpManager` singleton, and drop sessions that exited on their own from the session map instead of keeping them forever. ([#13096](https://github.com/diegosouzapw/OmniRoute/pull/13096)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection and PII scanners now read the text a `tool_result` block carries on `content` (string or nested block list), in messages and in system blocks, so tool output is judged by the same rules as user text ([#13101](https://github.com/diegosouzapw/OmniRoute/pull/13101)) — thanks @ntdatt812 +- **fix(gamification):** close the badge notification SSE stream when the request signal is already aborted before the stream starts — a client that disconnects while the route is still awaiting auth used to leave both the 2s unlock poll and the 15s heartbeat running for the lifetime of the process. ([#13106](https://github.com/diegosouzapw/OmniRoute/pull/13106)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection scan now spends its 16 KB budget on both ends of the request instead of the first 16 KB only, so `system`, `instructions`, `query`, `documents` and the newest turns are no longer hidden behind one long message ([#13104](https://github.com/diegosouzapw/OmniRoute/pull/13104)) — thanks @ntdatt812 +- **fix(dashboard):** expose the Volcano Ark console cookie on quota scraping and unwrap connect-error objects so the dashboard shows the upstream message ([#13107](https://github.com/diegosouzapw/OmniRoute/pull/13107)) — thanks @HouMinXi +- **fix(db):** release the `beforeExit`/`SIGINT`/`SIGTERM` handlers when a `node:sqlite` adapter closes, so a closed adapter and its database handle are no longer pinned to `process` for the lifetime of the run — the same treatment #7494 gave the sql.js adapter. ([#13109](https://github.com/diegosouzapw/OmniRoute/pull/13109)) — thanks @anhtahaylove +- **fix(translator):** `contentSchema` and `unevaluatedItems` are now treated as subschema positions by the tool-schema sanitizer, so a truncation placeholder in either is replaced with a permissive schema instead of being forwarded as a string ([#13110](https://github.com/diegosouzapw/OmniRoute/pull/13110)) — thanks @ntdatt812 +- **fix(cli-helper):** clear the `createLogStream` timeout on the abort path — `stop()` aborts the in-flight fetch and returned through the `signal.aborted` branch, which skipped `clearTimeout` and left an armed timer per stopped stream. The stream reader is now also cancelled when the read loop exits early. ([#13114](https://github.com/diegosouzapw/OmniRoute/pull/13114)) — thanks @anhtahaylove +- **fix(docs):** drop TLS-impersonation, MITM-decrypt, supply-chain attestation, and XOR-mask writeups from the public `/docs` catalog and Docker image. Files stay in git for engineers; operators who need them open the repo, not the website. ([#13136](https://github.com/diegosouzapw/OmniRoute/pull/13136)) — thanks @HouMinXi +- **fix(combo):** parse numeric-epoch `rate_limited_until` in the combo cooldown read path ([#13141](https://github.com/diegosouzapw/OmniRoute/pull/13141)) — thanks @maxmad64bis +- **fix(opencode):** both OpenCode plugins now reject a gateway address typed without `http://` at configuration time, instead of publishing every model with an api url no client can call, and the v2 plugin no longer publishes a model card whose api url is blank or relative ([#13142](https://github.com/diegosouzapw/OmniRoute/pull/13142)) — thanks @maxmad64bis +- **fix(providers):** lock opencode model on upstream 400 model-unavailable ([#13146](https://github.com/diegosouzapw/OmniRoute/pull/13146)) — thanks @maxmad64bis +- **fix(logging):** keep the provider exchange rather than the raw client bodies when a call log exceeds its size budget, and show that recovered payload in the request-detail panel instead of replacing it with the stored response body ([#13147](https://github.com/diegosouzapw/OmniRoute/pull/13147)) — thanks @maxmad64bis +- **fix(traffic-inspector):** the WebSocket route no longer leaks a traffic-buffer subscriber and a 30s ping timer when the client socket is already closed at handler time — listeners are attached before any resource is acquired, a destroyed socket bails out early, and the ping interval stops on a dead socket where `write()` never throws ([#13155](https://github.com/diegosouzapw/OmniRoute/pull/13155)) — thanks @anhtahaylove +- **fix(telegram):** bound the per-user API key cache in the Telegram chat proxy so a burst of distinct chat ids can no longer grow the process heap without limit ([#13165](https://github.com/diegosouzapw/OmniRoute/issues/13165)) ([#13166](https://github.com/diegosouzapw/OmniRoute/pull/13166)) — thanks @anhtahaylove +- **fix(stream):** cancel the upstream response body when the JSON-to-SSE sniff unwinds on a body timeout, so a stalled upstream no longer pins the connection ([#13169](https://github.com/diegosouzapw/OmniRoute/issues/13169)) ([#13171](https://github.com/diegosouzapw/OmniRoute/pull/13171)) — thanks @anhtahaylove +- **fix(telegram):** authenticate webhook deliveries with Telegram's `secret_token` so an unauthenticated caller can no longer mint API keys or spend upstream quota ([#13172](https://github.com/diegosouzapw/OmniRoute/issues/13172)) ([#13175](https://github.com/diegosouzapw/OmniRoute/pull/13175)) — thanks @anhtahaylove +- **fix(models):** give discoverable Gemini 3.8 Flash ids their own 65536 output spec so Antigravity no longer clamps them to 16384 ([#13195](https://github.com/diegosouzapw/OmniRoute/pull/13195)) — thanks @HouMinXi +- **fix(dashboard):** leftover catalog-removed provider rows (gemini-cli) can be listed and purged from the providers page ([#13067](https://github.com/diegosouzapw/OmniRoute/issues/13067)) ([#13197](https://github.com/diegosouzapw/OmniRoute/pull/13197)) — thanks @HouMinXi +- **fix(auth):** a dashboard session now requires the `authenticated: true` claim that login, OIDC and the session refresh already emit — a JWT merely signed with `JWT_SECRET` (for example the Cursor CLI passthrough token, which any API-key holder can obtain) no longer verifies as the `auth_token` cookie on any route, the WebSocket handshake or the live server; existing sessions keep working ([#13298](https://github.com/diegosouzapw/OmniRoute/issues/13298)) ([#13375](https://github.com/diegosouzapw/OmniRoute/pull/13375)) +- **fix(api):** `DELETE /v1/batches/delete-completed` now applies the caller's API-key policy (endpoint allowlist, schedule, usage cap, rate limit) like every other `/v1` route, commits the key-scoped sweep per 200-batch chunk instead of holding one write lock for the whole sweep, refuses to loop without progress, and rejects a scope that names both a key and `allTenants` ([#13684](https://github.com/diegosouzapw/OmniRoute/pull/13684)) +- Restore the API-route typecheck gate: the non-streaming leg result lost its discriminated-union narrowing after the server-owned tool loop reassignment, producing 13 new TS2339 diagnostics in `chatCore.ts`. ([#12963](https://github.com/diegosouzapw/OmniRoute/pull/12963)) +- Restore four non-streaming behaviours the server-owned tool loop refactor dropped: client aborts map to 499 with the fixed `Request aborted` message, an aborted request no longer logs a synthetic `clientResponse`, Claude prompt-cache telemetry is recorded again, and a body that cannot be canonicalized no longer throws when the tool loop is off. ([#12990](https://github.com/diegosouzapw/OmniRoute/pull/12990)) +- **fix(skills):** The CLI registry parser now reads positionals declared with `.addArgument()`, not only those written inline in `.command()`. `tunnel create [type]` was being published as `tunnel create`, so the agent-skills sync gate reported drift on every branch and regenerating would have deleted the argument. ([#13009](https://github.com/diegosouzapw/OmniRoute/pull/13009)) — thanks @ntdatt812 +- **fix(combos):** dashboard combo test uses a short prompt, serial probes, and a 60s timeout so reasoning models and rate-limited free pools do not fail the health check ([#13001](https://github.com/diegosouzapw/OmniRoute/pull/13001)) — thanks @HouMinXi +- fix(compression): terminate idle worker threads on eviction so long-running instances stop leaking OS threads and MessagePorts ([#13091](https://github.com/diegosouzapw/OmniRoute/pull/13091)) — thanks @anhtahaylove +- Redact Google API keys of any length in error bodies: the pattern required exactly 39 characters, so shorter or longer `AIza…` credentials passed through unredacted. ([#12964](https://github.com/diegosouzapw/OmniRoute/pull/12964)) +- **fix(grok-cli):** a 402 "Grok Build usage balance exhausted" parks that Grok login as out of credit (Grok Build CLI, grok.com cookie, and xAI OAuth share the weekly pool). Combo routing then tries the next login instead of locking the model for every account in the pool ([#13061](https://github.com/diegosouzapw/OmniRoute/pull/13061)) — thanks @HouMinXi +- fix(compression): spawn the LLMLingua worker with a file URL object so compression actually runs instead of silently failing open on Node ([#13093](https://github.com/diegosouzapw/OmniRoute/pull/13093)) — thanks @anhtahaylove +- **fix(test):** run the local `test` and `test:unit` scripts at concurrency 4 so a full-suite run no longer exhausts the machine's commit charge and kills unrelated processes ([#13187](https://github.com/diegosouzapw/OmniRoute/pull/13187)) — thanks @anhtahaylove +- **fix(monitoring):** `GET /api/monitoring/health` `credentialHealth` now includes a bounded `failedConnections` list (`connectionId`, `status`, sanitized `lastError`) when the probe-cache gauge `failed>0`, plus `source: probe-cache` and a cheap `staleDbNonOkCount` for sticky SQLite `test_status` on active rows. Documents that the live gauge is not `provider_connections.test_status`. ([#12876](https://github.com/diegosouzapw/OmniRoute/pull/12876)) — thanks @RaviTharuma +- **fix(resilience):** an openai-compatible multi-upstream gateway no longer marks the whole connection `credits_exhausted` when a single upstream (or the `/models` / representative-model health probe) returns 402. Quota failures stay model-scoped; true connection-wide auth failures are unchanged. Single-credential 402 key disable ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239)) is preserved. ([#12875](https://github.com/diegosouzapw/OmniRoute/pull/12875)) — thanks @RaviTharuma +- **fix(dashboard):** nine fixes on the Orchestration Canvas, all diagnosed in the Phase 2 reviews. A source that fails now keeps the timestamp of its FIRST failure instead of being re-stamped every poll — the stale line said "since the last poll" no matter how long the source had been down, and the churn also defeated the snapshot's stable identity (it serializes the source list), so the canvas re-rendered on every tick while anything was broken. A source that HAD data and only then started failing is flagged too: previously only a source with no node at all got the warning, so a source that went down mid-session kept a healthy-looking node forever. The rest are pointwise: clicking a filter chip cancels the pending search debounce (left armed, it fired ~300ms later and silently reverted the chip); the search input carries an accessible name; `?state=running, failed` parses like the unpadded form instead of dropping the padded value; the CSV toggle helper is defined once in `model/urlParams.ts` rather than twice; the agents tab tells "nothing running" apart from "the filter matched nothing", offering a clear-filters button instead of setup links that would be wrong advice there; edges stop emitting SMIL particles above 40 simultaneously active edges, keeping the colored stroke; and the drawer's error banner clears when a retried action succeeds. ([#12983](https://github.com/diegosouzapw/OmniRoute/pull/12983)) +- **fix(i18n):** the nine locales added with the EU-language batch (Greek, Estonian, Irish, Croatian, Lithuanian, Latvian, Maltese, Slovenian, Serbian) were missing the eleven Orchestration Canvas keys that Phase 3 introduced, so the compare-runs panel and the "no runs match these filters" empty state fell back to English in those languages (`deepMergeFallback` substitutes English for an absent key, so nothing rendered blank — it rendered untranslated). The coverage gate does not catch this: it enforces an 80% floor per locale, and eleven missing keys out of ~13,000 leaves coverage at 99.9%. Translated for real in each language, calibrated against the wording each file already uses for "run", "filter" and "skill". ([#13294](https://github.com/diegosouzapw/OmniRoute/pull/13294)) +- **fix(dashboard):** five follow-ups from the Phase 2 Orchestration Canvas review. The History drawer now shows the memory section for runs that already left the live TTL window: the persisted `memory_hits` event is parsed into `metadata.memoryHits` with the same defensive validation the drawer applies, and — because that event is observability rather than a state transition — it no longer leaks into the timeline, where it had been inheriting the task's state and rendering as a duplicate transition. The A2A memory recall runs against its own 1.5s deadline instead of inheriting the memory backend's 30s one; overshooting degrades exactly like any other recall failure (no hits, task proceeds), and the timer is cleared on both paths. Repeating a Conductor run carries its `requirements.cli`/`requirements.model` forward, so the new run is pinned to the same runner profile and model rather than drifting to whatever the fleet picks. A successful repeat from the Agents tab now focuses the run it created, instead of leaving the operator on the finished one. And the auth test for `POST /api/conductor/tasks` moved into the shared `ROUTES` array rather than restating the pattern. ([#12988](https://github.com/diegosouzapw/OmniRoute/pull/12988)) +- fix(plugins): stop leaking an exit listener per plugin hook timeout, which triggered MaxListenersExceededWarning on plugins that ignore SIGTERM ([#13092](https://github.com/diegosouzapw/OmniRoute/pull/13092)) — thanks @anhtahaylove +- **fix(validation):** Provider node edits no longer fail with a generic "Invalid request" when the optional daily-quota reset fields are left blank. The dashboard sends `dailyQuotaResetTimezone` and `dailyQuotaResetHour` as `null`, and only the hour accepted it. ([#13066](https://github.com/diegosouzapw/OmniRoute/issues/13066)) ([#13083](https://github.com/diegosouzapw/OmniRoute/pull/13083)) — thanks @ntdatt812 +- **fix(combo):** quota-aware expansion drops banned, inactive, missing, and wrong-provider connections before quota fetch or model dispatch; pins and allowlists stay selectors, not a bypass. Antigravity automatic exhaustion now requires a reported zero remaining, so a positive balance below 1% stays eligible. ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- Fix an intermittent failure in the quota-weighted routing test suite: scores are a function of `Date.now()`, so peers with identical quota scored microseconds apart never tied and swapped order. ([#12946](https://github.com/diegosouzapw/OmniRoute/pull/12946)) +- fix(volcengine): resolve quota window mapping gaps, safe multi-connection console binding, and response cookie sanitization ([#12950](https://github.com/diegosouzapw/OmniRoute/pull/12950)) — thanks @HouMinXi +- **fix(test):** resolve the WebDAV handler path with `fileURLToPath` so the suite's 37 WebDAV tests run on Windows instead of failing with a doubled `C:\C:\` drive prefix ([#13196](https://github.com/diegosouzapw/OmniRoute/pull/13196)) — thanks @anhtahaylove +- **fix(ui):** fall back from invalid AnySearch icon alias (direct commit `9587e07b69`) +- **fix(sse):** stop rebuilding a truncated summary from the collector's cap-dropped event array ([#12718](https://github.com/diegosouzapw/OmniRoute/pull/12718)) — thanks @hartmark +- **fix(conversations):** show a pending spinner for unresolved tool nodes instead of "(empty)" ([#12727](https://github.com/diegosouzapw/OmniRoute/pull/12727)) — thanks @hartmark +- **fix(streaming):** fail fast when an upstream stream produces only lifecycle/heartbeat events, never real content ([#12741](https://github.com/diegosouzapw/OmniRoute/pull/12741)) — thanks @hartmark +- **fix(responses-continuation):** bridge the write-in-flight window with an in-memory pending store ([#12854](https://github.com/diegosouzapw/OmniRoute/pull/12854)) — thanks @hartmark +- **fix(translator):** drop or map agent_message on Chat Completions fallback ([#12880](https://github.com/diegosouzapw/OmniRoute/pull/12880)) — thanks @RaviTharuma +- **fix(api):** page and stream-complete GET /v1/models for large catalogs ([#12882](https://github.com/diegosouzapw/OmniRoute/pull/12882)) — thanks @RaviTharuma +- **fix(quota):** align AUTH window labels with usage API and clarify 503 ([#12884](https://github.com/diegosouzapw/OmniRoute/pull/12884)) — thanks @RaviTharuma +- **fix(ci):** clear two base-reds on release/v3.8.51 — mutation-coverage gate + image-only-model guard ([#12945](https://github.com/diegosouzapw/OmniRoute/pull/12945)) +- **fix(api):** scope batch bulk-delete to the calling API key ([#13211](https://github.com/diegosouzapw/OmniRoute/pull/13211)) +- **fix(tests):** align model-sync log assertion to shipped path redaction (base-red) ([#13227](https://github.com/diegosouzapw/OmniRoute/pull/13227)) +- **fix(test):** make the ToS heading guard actually require the parentheses ([#13228](https://github.com/diegosouzapw/OmniRoute/pull/13228)) +- **fix(quality):** register 4 drifted covering tests in stryker tap.testFiles ([#13229](https://github.com/diegosouzapw/OmniRoute/pull/13229)) +- **fix(logging):** mask *-api-key headers in request-log pipeline (#13273) ([#13401](https://github.com/diegosouzapw/OmniRoute/pull/13401)) — thanks @KooshaPari +- **fix(tests):** correct stale Jina catalog prefix in custom-models test ([#13403](https://github.com/diegosouzapw/OmniRoute/pull/13403)) — thanks @KooshaPari +- **fix(backend):** prune old backups after health-check-repair snapshots (#13308) ([#13404](https://github.com/diegosouzapw/OmniRoute/pull/13404)) — thanks @KooshaPari +- **fix(backend):** guard memory_fts_au trigger with WHEN clause to stop FTS bloat (#13326) ([#13405](https://github.com/diegosouzapw/OmniRoute/pull/13405)) — thanks @KooshaPari +- **fix(free-tiers):** remove obsolete Together $25 signup credit (#12526) ([#13407](https://github.com/diegosouzapw/OmniRoute/pull/13407)) — thanks @KooshaPari +- **fix:** skip non-chat generation models in Test all models ([#13409](https://github.com/diegosouzapw/OmniRoute/pull/13409)) — thanks @KooshaPari +- **fix(providers):** de-duplicate agy and antigravity model catalogs ([#13410](https://github.com/diegosouzapw/OmniRoute/pull/13410)) — thanks @KooshaPari +- **fix(dashboard):** send provider in body for mini-playground web search ([#13411](https://github.com/diegosouzapw/OmniRoute/pull/13411)) — thanks @KooshaPari +- **fix(resilience):** add CJK quota-exhaustion patterns to 429 classifier ([#13412](https://github.com/diegosouzapw/OmniRoute/pull/13412)) — thanks @KooshaPari +- **fix(backend):** eval runner: failed calls no longer score as passed + dotAll regex ([#13413](https://github.com/diegosouzapw/OmniRoute/pull/13413)) — thanks @KooshaPari +- **fix(backend):** eval runner opts out of output-style and memory injection ([#13414](https://github.com/diegosouzapw/OmniRoute/pull/13414)) — thanks @KooshaPari +- **fix:** chain serial quarantine step in npm test script (fixes #13286) ([#13418](https://github.com/diegosouzapw/OmniRoute/pull/13418)) — thanks @KooshaPari +- **fix(antigravity):** derive stable session ID from credentials for prompt-cache reuse (#13305) ([#13424](https://github.com/diegosouzapw/OmniRoute/pull/13424)) — thanks @KooshaPari +- **fix(memory):** merge memory into top-level system field instead of unshifting at messages[0] (#13425) ([#13427](https://github.com/diegosouzapw/OmniRoute/pull/13427)) — thanks @KooshaPari +- **fix(compression):** preserve instruction blocks from lossy engines ([#13523](https://github.com/diegosouzapw/OmniRoute/pull/13523)) — thanks @KooshaPari +- **fix(compression):** preserve polarity words and newlines in ultra heuristic ([#13524](https://github.com/diegosouzapw/OmniRoute/pull/13524)) — thanks @KooshaPari +- **fix(compression):** run user preservePatterns before built-in patterns (#13457) ([#13525](https://github.com/diegosouzapw/OmniRoute/pull/13525)) — thanks @KooshaPari +- **fix(reasoning):** expand deepseek-v4 regex to match v4.1+ and add command-code provider (#13430) ([#13528](https://github.com/diegosouzapw/OmniRoute/pull/13528)) — thanks @KooshaPari +- **fix(providers):** add alwaysReasons metadata for reka-flash-3 ([#13531](https://github.com/diegosouzapw/OmniRoute/pull/13531)) — thanks @KooshaPari +- **fix(playground):** allow Compare columns to scroll independently ([#13532](https://github.com/diegosouzapw/OmniRoute/pull/13532)) — thanks @KooshaPari +- **fix(security):** empty whitelist allows through to prevent admin lockout ([#13534](https://github.com/diegosouzapw/OmniRoute/pull/13534)) — thanks @KooshaPari +- **fix(tests):** add DISABLE_IOREG_STRATEGY env var for macOS test compatibility (fixes #13467) ([#13539](https://github.com/diegosouzapw/OmniRoute/pull/13539)) — thanks @KooshaPari +- **fix(zed-hosted):** map developer role and Gemini enums for Zed proxy ([#13543](https://github.com/diegosouzapw/OmniRoute/pull/13543)) — thanks @KooshaPari +- **fix(analytics):** normalize provider aliases to canonical id in usage_history ([#13545](https://github.com/diegosouzapw/OmniRoute/pull/13545)) — thanks @KooshaPari +- **fix(combo):** use traceId for call log id to prevent UNIQUE constraint failure ([#13546](https://github.com/diegosouzapw/OmniRoute/pull/13546)) — thanks @KooshaPari +- **fix(auth):** check model lockout before returning synthetic noauth connection ([#13547](https://github.com/diegosouzapw/OmniRoute/pull/13547)) — thanks @KooshaPari +- **fix(guardrails):** skip credential redaction for base64 image data URLs ([#13550](https://github.com/diegosouzapw/OmniRoute/pull/13550)) — thanks @KooshaPari +- **fix(resilience):** increase requestQueue.maxWaitMs default from 15s to 30s ([#13553](https://github.com/diegosouzapw/OmniRoute/pull/13553)) — thanks @KooshaPari +- **fix(docs):** document OMNIROUTE_READY_TIMEOUT_MS and allowlist the test-only DISABLE_IOREG_STRATEGY ([#13692](https://github.com/diegosouzapw/OmniRoute/pull/13692)) + ### 📝 Maintenance ---- +- **docs(readme):** reconcile live v3.8.50 provider, free-tier, CLI, routing, test, community, sponsor, acknowledgment, and SVG metrics with their audited source denominators, including a deduplicated OmniRoute-in-Action snapshot and distinct contributor rankings for merged pull requests, GitHub-attributed commits, and Git history ([#11356](https://github.com/diegosouzapw/OmniRoute/pull/11356)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **docs:** sync the canonical provider count 354 → 356 across `README.md`, `AGENTS.md`, `llm.txt` (+ 42 i18n mirrors), the four README SVG diagrams, `docs/reference/PROVIDER_REFERENCE.md` (regenerated) and the `package.json` description after Opper (#11629) and 1min.ai (#11631) boarded the catalog — closes the `check:docs-counts-sync` strict drifts that kept `release/v3.8.51` red ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11673](https://github.com/diegosouzapw/OmniRoute/pull/11673)) — thanks @jonlwheat2-gif +- **test(providers):** update count-derived assertions after the v3.8.51 provider additions — `APIKEY_PROVIDERS` 233 → 235 (Opper #11629 + 1min.ai #11631), reserved-prefix REGISTRY walk 395 → 398, `WEB_FETCH_PROVIDERS` now includes `nimble-search` (#11620), and the provider translate-path golden snapshot regenerated ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11674](https://github.com/diegosouzapw/OmniRoute/pull/11674)) — thanks @jonlwheat2-gif +- **test(sse):** bump the hard-lease connection-query inventory for `src/lib/tokenHealthCheck.ts` to 2 — the verify-only web-cookie sweep added by #11495 split the single `getProviderConnections` call into oauth + cookie variants, which the frozen inventory had not tracked ([#11495](https://github.com/diegosouzapw/OmniRoute/pull/11495)). ([#11677](https://github.com/diegosouzapw/OmniRoute/pull/11677)) — thanks @jonlwheat2-gif / @oyi77 +- **chore(release):** clear the `release/v3.8.51` base-red gates — docs migration count (+38 i18n mirrors), stryker `tap.testFiles`, lockfile registry host, stale ESLint suppressions, the 7 lint regressions they were hiding, the stale `APIKEY_PROVIDERS` partition count (231 → 233), the `glm-5.3-max` tier missing from the GLM test table, a dead `modelType === "chat"` comparison in the models catalog, and the `resolveLiveWsUrl` / `sanitizeLiveWsPort` helpers from #11388 that a later merge dropped from `wsPath.ts` (dashboard live WebSocket URL resolution) ([#11502](https://github.com/diegosouzapw/OmniRoute/pull/11502)) — thanks @MumuTW +- **chore(free-models):** the free-tier regime table now declares which totals figure each regime feeds and whether it may take the no-auth shortcut, so the sets that used to repeat those answers by hand are derived from it — a new `freeType` no longer compiles until it has answered every question, instead of silently contributing to no total ([#11537](https://github.com/diegosouzapw/OmniRoute/pull/11537)) — thanks @maxmad64bis +- **docs(routing):** LKGP strategy copy now states provider-level stickiness and the explicit rules fallback instead of promising per-target pinning ([#11555](https://github.com/diegosouzapw/OmniRoute/pull/11555)) — thanks @maxmad64bis +- **test(chatcore):** pin the response tool-name alias resolution against the ordering hazard that broke every Gemini/Antigravity MCP tool call in v3.8.49. `extractRequestToolIdentityMap` deletes `translatedBody._toolNameMap`, so the later read is always undefined and the ledger survives only through the `requestToolIdentityMap` fallback — removing that fallback previously left the entire tool-name suite green. The resolution moves into `resolveResponseToolNameMap()` next to the map it depends on, with a regression guard that fails without the recovery. No behaviour change. ([#11575](https://github.com/diegosouzapw/OmniRoute/pull/11575)) — thanks @amartinawi +- **docs(env):** document `OMNIROUTE_CORPUS_CACHE_SIZE` in `.env.example` and `ENVIRONMENT.md`, so the env/docs contract check stops failing Docs Gates on every branch ([#11576](https://github.com/diegosouzapw/OmniRoute/pull/11576)) — thanks @ntdatt812 +- **chore(stryker):** register `route-guard-tunnel-processes-local-only` and `universal-quota-aware-routing` in `tap.testFiles`, so their mutant kills count for the three modules they cover ([#11577](https://github.com/diegosouzapw/OmniRoute/pull/11577)) — thanks @ntdatt812 +- **test(authz):** pin `LOCAL_ONLY_API_GET_EXEMPTIONS` by exact membership instead of by entry count, so the guard names the offending path and also catches a substitution ([#11580](https://github.com/diegosouzapw/OmniRoute/pull/11580)) — thanks @ntdatt812 +- **test(build):** pin `resolveNextBuildBundlerFlag` to the `OMNIROUTE_USE_TURBOPACK` contract it implements, across runtimes, instead of asserting an unimplemented Bun override ([#11583](https://github.com/diegosouzapw/OmniRoute/pull/11583)) — thanks @ntdatt812 +- **test(claude-web):** the Claude Web transport test's barrel anchors no longer pin the exact construction spelling. #11421 made the executor map lazy, so `/"claude-web": new ClaudeWebExecutor\(\)/` stopped matching and the test went red on `release/v3.8.51` itself — a base-red gate every open PR inherited. The anchors now match the alias key and the class name and tolerate either spelling in between, and were checked to still fail when an alias is genuinely repointed at another executor ([#11585](https://github.com/diegosouzapw/OmniRoute/pull/11585)) — thanks @ntdat812 +- **test(models):** pin `qwen3.8-max` resolution per provider — two of the four catalogs now serve the bare id and no longer carry `-preview` ([#11590](https://github.com/diegosouzapw/OmniRoute/pull/11590)) — thanks @ntdatt812 +- **test(executors):** drop the stale 128-tool truncation assertion that contradicted the #11444 pass-through contract ([#11591](https://github.com/diegosouzapw/OmniRoute/pull/11591)) — thanks @ntdatt812 +- **test(token-health):** pin the current terminal-skip boundary — expired with retry budget is probed, `account_deactivated` stays skipped ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)) — thanks @ntdatt812 +- **test(models):** follow #11503's retarget of the `gemini-3-pro-high` alias to the hyphenated catalog id ([#11593](https://github.com/diegosouzapw/OmniRoute/pull/11593)) — thanks @ntdatt812 +- **refactor(video bridge):** extract per-video acquisition, whole-result caching, description, and metrics/abort/cleanup out of `VideoBridgeGuardrail.preCall` into a `processVideoPart` seam in a new `videoBridgePipeline.ts`, behind explicit `VideoMediaBrokerPort`, `VideoAudioTranscriptionPort`, and `VideoDrilldownPort` boundaries; `preCall` now only handles request traversal, policy, and response aggregation. The Video tab's FFmpeg/ffprobe runtime status is now an explicit `unknown` / `restricted` / `unavailable` / `available` state instead of a nullable boolean pair, fixing a case where an in-flight or failed probe was mislabeled as "install FFmpeg" ([#11657](https://github.com/diegosouzapw/OmniRoute/issues/11657)). ([#12016](https://github.com/diegosouzapw/OmniRoute/pull/12016)) +- **test(opencode-plugin):** add unit test coverage for `formatFreeBudget()` naming helper ([#11660](https://github.com/diegosouzapw/OmniRoute/pull/11660)) — thanks @f9td56dbgh-hub +- **docs(video):** clarify that the Video Bridge transcript `source` field (`client`, `embedded`, `audio-bridge`) is presently caller-declared and not yet server-verified — OmniRoute enforces the enum shape but does not cryptographically confirm that an `embedded`/`audio-bridge` label came from a server-owned extraction ([#11661](https://github.com/diegosouzapw/OmniRoute/issues/11661)). ([#12001](https://github.com/diegosouzapw/OmniRoute/pull/12001)) +- **chore(providers):** retire the distributed Raycast Relay and Hailuo Web integrations after a provenance audit confirmed substantial derivation from GPL-3.0 sources; the official GitHub and MiniMax API providers remain available. Migration 163 disables legacy connections and invalidates their active leases while preserving connection IDs, audit/history rows, quota snapshots, usage records, and fail-closed API-key connection policies. ([#11691](https://github.com/diegosouzapw/OmniRoute/pull/11691)) +- **chore(providers):** retire the Felo Web provider while its GPL-derived provenance remains on licensing HOLD ([#11698](https://github.com/diegosouzapw/OmniRoute/pull/11698)) — stale runtime and database references now fail closed while existing connection, lease, and usage history remains preserved for auditability. +- **fix(sse):** retire the Gemini Web image-generation bridge while provenance and licensing remain on HOLD, preserving Gemini Web chat/auth/tools and legitimate Gemini/Nano Banana image providers ([#11708](https://github.com/diegosouzapw/OmniRoute/pull/11708)) +- **chore(audio):** retire the built-in EdgeTTS provider and its 11-voice catalog while provenance/licensing review remains on HOLD; the generic `/v1/audio/speech` endpoint and the other speech providers remain available ([#11711](https://github.com/diegosouzapw/OmniRoute/pull/11711)) +- **fix(providers):** retire the unofficial Qwen Web integration and its `qwen-web`/`qw` identities while third-party provenance remains on HOLD ([#11713](https://github.com/diegosouzapw/OmniRoute/pull/11713)) — direct requests now fail closed with a sanitized HTTP 410 and combos skip the retired target, while migration guards preserve connection IDs, allowlists, usage/audit history and invalidate active leases. Supported Qwen API, Qwen Code, cloud, token-plan, audio, local and model-name surfaces remain available; restoring Qwen Web requires a qualified provenance/license decision. +- **fix(providers):** retire `microsoft-designer-web` and its `msdesigner` alias from runtime and catalogs while preserving stored credentials, provider history and allowlists as inactive records — source dependency on gpt4free's [`MicrosoftDesigner.py`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/g4f/Provider/needs_auth/MicrosoftDesigner.py) is proven at snapshot `0943d16eef757a44deb60651c07bbe5fa958cafe`, whose [`LICENSE`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/LICENSE) is GPL-3.0; expressive adaptation is probable, which records provenance/licensing risk without asserting a legal conclusion ([#11720](https://github.com/diegosouzapw/OmniRoute/pull/11720)). +- **docs(legal):** centralize pinned MIT notices for the vendored GCF codec and local flag assets ([#11726](https://github.com/diegosouzapw/OmniRoute/pull/11726)) +- Split the 2,952-line `adobeFireflyClient.ts` into nine focused modules (catalog, credentials, payload, ARP session, upload, responses, IMS, account, poll) behind an unchanged barrel — no consumer edits and no public API change (#11728). — thanks @MumuTW +- **fix(dashboard):** replaced the Model Lockout card's unprovenanced notification MP3 with a short, locally synthesized Web Audio chime ([#11731](https://github.com/diegosouzapw/OmniRoute/pull/11731)) — both toggles retain optional audible feedback without shipping replacement media or third-party code, and unsupported, suspended, or rejected audio contexts fail open without blocking the setting change. +- **chore(deepseek-web):** remove the provenance-unresolved DeepSeek PoW WASM binary and its runtime loader/tracing while retaining the existing JavaScript solver (slower at high difficulty) ([#11732](https://github.com/diegosouzapw/OmniRoute/pull/11732)). +- **chore(assets):** remove four unused provider assets whose redistribution provenance could not be verified while retaining the active Freebuff SVG ([#11735](https://github.com/diegosouzapw/OmniRoute/pull/11735)) +- **docs(assets):** retired two raster images whose redistribution provenance could not be verified ([#11736](https://github.com/diegosouzapw/OmniRoute/pull/11736)) — PicoClaw documentation now reuses the existing SVG, while Zoo Code remains a text link without an image. +- **docs(legal):** add pinned notices for six LobeHub-derived provider SVGs and 65 byte-exact theSVG assets, retaining registry claims as unverified metadata with explicit trademark and HOLD boundaries ([#11737](https://github.com/diegosouzapw/OmniRoute/pull/11737)). +- **chore(quality):** added a versioned 225/225 provider-asset provenance manifest and blocking drift gate ([#11743](https://github.com/diegosouzapw/OmniRoute/pull/11743)) — the inventory pins magic MIME and SHA-256 for every physical file, records immutable source matches for 71 assets, keeps 69 probable and 85 unresolved entries explicit but non-blocking, and treats provenance as source evidence rather than copyright or trademark clearance. +- **refactor(mitm):** replace four previously derived Traffic Inspector utilities — conversation normalization, SSE merging, secret masking, and header sanitization — with independent clean-room implementations based on public provider schemas, WHATWG SSE, and RFC 6750, while keeping the two independently classified passthrough surfaces unchanged ([#11748](https://github.com/diegosouzapw/OmniRoute/pull/11748)). +- Added a unit test that fails seven days before any dated pack under `config/` (`validUntil` and sibling keys) lapses, naming the file and key. The Alibaba free-tier pack expired on 2026-08-27 and turned every PR red the next morning with no commit involved; renewal now happens on someone's terms, not the clock's. ([#11891](https://github.com/diegosouzapw/OmniRoute/pull/11891)) +- `check:workflows` now fails (under `--strict`/`--ratchet`) when any job routed to a self-hosted runner publishes with `--provenance` — npm rejects that with `422` at the registry, which in v3.8.50 only surfaced after the tag and Docker images were public. ([#11895](https://github.com/diegosouzapw/OmniRoute/pull/11895)) +- `scripts/ops/runner-janitor.sh` now proves a path is idle with one `lsof` snapshot and removes stale leftovers itself (tmpfs after 3 h — it is RAM — disk after 24 h), kills orphan `next-build` processes, prunes checkouts of stopped runners, and alerts on memory pressure; `--dry-run` shows exactly what it would do. `docs/ops/RUNNER_BOX.md` reconciled to the measured box (31 GB, 10 listeners). ([#11893](https://github.com/diegosouzapw/OmniRoute/pull/11893)) +- The `next-build` artefact (1.3 GB) is now written and read under `$RUNNER_TEMP` (per-runner, on disk) instead of `/tmp`, which on the self-hosted pool is a 12 GB tmpfs in RAM. Landing it there took 27–32 of the publish job's 76 minutes, and the fixed `/tmp/e2e-build.tar.gz` name let E2E jobs on different runners overwrite each other's download. ([#11896](https://github.com/diegosouzapw/OmniRoute/pull/11896)) +- The CI `build` job now runs in two concurrency lanes — `main` and pull requests — so a release build is never queued behind (or OOM-killed beside) PR builds on the self-hosted pool, which holds one `next-build` comfortably and two at the edge. ([#11901](https://github.com/diegosouzapw/OmniRoute/pull/11901)) +- Re-freeze the ESLint suppressions on `release/v3.8.51` from a clean-room measurement (2 stale file entries pruned, 55 pre-existing `no-explicit-any` in six new files frozen under #11924) and drop the dead `GPT_SIZE_MAP` constant orphaned by the Adobe Firefly client split, so `No new ESLint warnings` stops failing every PR with exit 2 (Refs #11924) ([#11955](https://github.com/diegosouzapw/OmniRoute/pull/11955)) +- Type the 55 `no-explicit-any` sites that had been frozen under #11924 — four redundant casts in `socksConnectorWithFamily.ts` (undici/socks types already accept them) and the mocks/fixtures of the socks-timeout and isFree suites — and drop their suppression entries; the ESLint ratchet shrinks from 5487 to 5432 (Closes #11924) ([#11975](https://github.com/diegosouzapw/OmniRoute/pull/11975)) +- Take the two hosted-runner builds off the PR rail: `Build App` (`build.yml`) is `workflow_dispatch`-only and `DAST smoke (PR)` runs only for PRs into `main` — the 7 GB hosted VM cannot build `release/v3.8.51` in any profile (19/30 red, VM shutdown ~8 min into `next build`) and both checks had turned into permanent noise on every release PR; the bundle stays validated by `ci.yml` on `main` and by `nightly-release-green` on `release/**` (Closes #11946) ([#11962](https://github.com/diegosouzapw/OmniRoute/pull/11962)) +- Move the four nightly jobs that build the backend (`nightly-schemathesis`, `nightly-llm-security` promptfoo + garak, `nightly-resilience` axe-a11y) off the hosted 7 GB runner — where they died on `release/v3.8.51` unseen — onto the box's new `omni-light` pool (two listeners), and document the reshaped fleet (4 active OmniRoute listeners: 2 `omni-build` + 2 `omni-light`, janitor ceiling 4) (Closes #11965) ([#11967](https://github.com/diegosouzapw/OmniRoute/pull/11967)) +- Stop painting every fork PR into `release/**` red: `quality.yml` `Build (advisory)` is skipped (GitHub still reports `continue-on-error` failures as check FAILURE). Hosted `ubuntu-latest` cannot finish `npm run build` on this tree — same class as #11962 taking `build.yml` off the PR rail. `docker-publish.yml` amd64 now runs on the `.113` `omni-build` pool (31 GB / 32 cores, two listeners) with Turbopack, shares the `heavy-build-main` lane with `ci.yml` `Build` so it queues instead of becoming a third heavy, and keeps per-ref concurrency (a merge storm was starting 8 concurrent OOM builds). arm64 stays on `ubuntu-24.04-arm` with webpack — there is no ARM box. Fallback when `USE_VPS_RUNNER` is off: hosted amd64 + webpack (#11976). ([#12021](https://github.com/diegosouzapw/OmniRoute/pull/12021)) +- `docker-publish.yml` builds with webpack (`OMNIROUTE_USE_TURBOPACK=0`) on **both** arches, including amd64 on the `.113` `omni-build` pool. Turbopack had the RAM (31 GB, `omniroute-113-6`) and still panicked (`TurbopackInternalError: there must be a path to a root`, run 33253576569, 26 min in). The same tree's arm64 webpack build on hosted `ubuntu-24.04-arm` succeeded (run 33264823398). Dockerfile already documents webpack as the Docker escape hatch (#11976). ([#12050](https://github.com/diegosouzapw/OmniRoute/pull/12050)) +- **docs(free-tier):** declare the counting vs deciding regimes for "is it free?" and guard the deciding path from DB-backed catalog resolution ([#12226](https://github.com/diegosouzapw/OmniRoute/pull/12226)) — thanks @maxmad64bis +- **docs(auto-combo):** The mode pack table in `docs/routing/AUTO-COMBO.md` now lists all six shipped packs with every weight each one sets, replacing a four-pack table whose numbers had also drifted from the source. It states plainly that no pack sets `quality`, so selecting any pack silences the observed-quality signal. Six more documents that quote the scoring factor count joined the `check:docs-counts` gate, which caught five stale claims — including one naming nine factors that do not exist — and two stale mode pack counts. The dashboard routing panel, which offered four of the six packs and labelled the default strategy "6-Factor Scoring", is now covered by a test; the two packs it was missing are `reliability-first` and `chaos-mode`, the latter labelled as the fault-injection profile it is rather than as one more routing preference ([#12316](https://github.com/diegosouzapw/OmniRoute/pull/12316)) — thanks @maxmad64bis +- **chore(quality):** rebaseline `src/lib/db/apiKeys.ts` for the ACL the key-creation path now preserves ([#12352](https://github.com/diegosouzapw/OmniRoute/pull/12352)) ([#12673](https://github.com/diegosouzapw/OmniRoute/pull/12673)) — thanks @KrzysiekSko +- **chore(providers):** bump the Claude Code wire identity and the Devin bridge image pin from `2.1.220` to `2.1.258` ([#12402](https://github.com/diegosouzapw/OmniRoute/pull/12402)) — thanks @ggiak ([#12604](https://github.com/diegosouzapw/OmniRoute/pull/12604)) +- **docs(env):** align `.env.example`, the README Bun section, and the troubleshooting guide with the code: `OMNIROUTE_USE_TURBOPACK` also governs `npm run build` (not dev-only), `bun run build` follows that flag instead of auto-selecting Webpack, `OMNIROUTE_CHAT_MAX_HEAVY_IN_FLIGHT` is unset by default (no request-count cap), and the structural `503 chat_admission_busy` message matches `chatAdmissionResponses.ts` (#12404 — thanks @pacocartones) +- **chore(electron):** upgrade the desktop app to Electron 44 (Chromium 152, Node 24.18.1) ([#12217](https://github.com/diegosouzapw/OmniRoute/pull/12217)). **Requires macOS 13 (Ventura) or later** — Chromium dropped macOS 12 (Monterey), so Monterey users must stay on an earlier OmniRoute desktop build. Windows and Linux are unaffected; the app already shipped only x64/arm64, so Electron 44 dropping 32-bit builds changes nothing. Removes the `openAsHidden`/`wasOpenedAsHidden` login-item fields deleted in Electron 44 — hidden autostart continues to work through the `--hidden` argument registered with the login item ([#12554](https://github.com/diegosouzapw/OmniRoute/pull/12554)) — thanks @app/dependabot +- **chore(quality):** rebaseline `src/sse/handlers/chat.ts` for the effective-input persistence the continuation fix needs ([#12641](https://github.com/diegosouzapw/OmniRoute/pull/12641)) ([#12680](https://github.com/diegosouzapw/OmniRoute/pull/12680)) — thanks @hartmark +- `Coverage` job on `ci.yml`: the informational Codecov upload gets its own 5-minute ceiling and `continue-on-error`, and the job budget grows from 20 to 30 minutes (the 8-shard c8 merge alone takes ~10) — a stalled upload no longer ends the job `cancelled` and drags a fully green `main` run's conclusion down with it ([#11972](https://github.com/diegosouzapw/OmniRoute/pull/11972)) +- CI hardening on the PR rail: the four `Unit Tests fast-path` shards get `timeout-minutes: 30` (a hung shard held a PR for 64 min instead of GitHub's 6 h default) and both ESLint file caches lose their `restore-keys` fallback, so a cache built under another suppressions file or lint config can no longer report stale verdicts (Refs #11600, #11924) ([#11963](https://github.com/diegosouzapw/OmniRoute/pull/11963)) +- Every CI job that runs a `next build` (`build`, the npm `publish`, both release-green validations) now targets the `omni-build` runner label, which only two of the eight self-hosted runners carry. The box holds one build comfortably and two at the edge; a third now queues on GitHub instead of being OOM-killed by the kernel. ([#11932](https://github.com/diegosouzapw/OmniRoute/pull/11932)) +- **refactor(combo):** move `handleRoundRobinCombo` (and `resolveTargetTokenLimit`) into `open-sse/services/combo/roundRobinCombo.ts`. `combo.ts` drops from 2164 to 1014 lines (`split("\n").length`); the leaf is 1199 (under the 1200 new-file cap). The round-robin call site uses a dynamic `import()` so `releaseStickyPinOnFailure` / `clearStaleLKGP` can stay exported from `combo.ts` without a static cycle. Skip / sticky / semaphore / safety-timer behavior is unchanged. ([#12811](https://github.com/diegosouzapw/OmniRoute/pull/12811)) — thanks @HouMinXi +- **chore(quality):** rebaseline the file-size caps the error-boundary campaign grew past (`open-sse/executors/codex.ts`, `open-sse/vendor/codex-chatgpt-web/bridge.ts`, both via [#12444](https://github.com/diegosouzapw/OmniRoute/pull/12444)) ([#12654](https://github.com/diegosouzapw/OmniRoute/pull/12654)) +- **chore(quality):** rebaseline the file-size caps the hartmark batch grew past (`combos/page.tsx` via [#12355](https://github.com/diegosouzapw/OmniRoute/pull/12355), `open-sse/services/combo.ts` via [#12338](https://github.com/diegosouzapw/OmniRoute/pull/12338)) ([#12623](https://github.com/diegosouzapw/OmniRoute/pull/12623)) — thanks @hartmark +- **chore(quality):** rebaseline the file-size caps the HouMinXi batch grew past when its PRs stacked (`providers/page.tsx`, `chatCore.ts`, `accountFallback.ts`) — each PR measured correctly in isolation, none saw the stacking ([#12619](https://github.com/diegosouzapw/OmniRoute/pull/12619)) +- **chore(quality):** rebaseline `open-sse/services/combo.ts` for the reset-aware scoring the HouMinXi batch stacked ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) ([#12678](https://github.com/diegosouzapw/OmniRoute/pull/12678)) — thanks @HouMinXi +- **docs:** sync provider catalog count (direct commit `4f11b2ae3d`) +- **docs(skills):** sync omni-inference search fallback (direct commit `b4e76a7ed9`) +- **chore(lint):** prune two stale base suppressions (direct commit `1325047d56`) +- **test(auto-combo):** tolerate full-suite rotation load (direct commit `ad3e293f9f`) +- **test:** drain inherited quality snapshot reds (direct commit `21ed68d8ac`) +- **chore(quality):** rebaseline measured CLI bundle growth (direct commit `d35b3f9779`) +- **test(ui):** reproduce invalid AnySearch icon alias (direct commit `12cc6ca834`) +- **test(translator):** fix the relative imports of the relocated deferred-finish test (direct commit `777d9d1629`) +- **docs(counts):** bring the documented provider and migration counts to the measured values (direct commit `e70bea30e9`) +- **test(security):** drop the guard-target set left unused by the sync (direct commit `a32ba0e362`) +- **chore(db):** renumber model_capabilities migration 163 → 169 (cross-branch collision with 163_radar_feed_cache_generated_at) (direct commit `70f5d4cbf9`) +- **docs(cli):** document CLI_PRIME_AGENT_BIN and correct the CLI Agents count (direct commit `9058e39b61`) +- **chore(deps):** refresh runtimes and adopt ESLint 10 ([#11259](https://github.com/diegosouzapw/OmniRoute/pull/11259)) — thanks @backryun +- **refactor(local-corpus):** implement dynamic root resolution and LRU cache ([#11491](https://github.com/diegosouzapw/OmniRoute/pull/11491)) — thanks @raheemuddin786 +- **chore(quality):** prune stale eslint-suppressions entries ([#11596](https://github.com/diegosouzapw/OmniRoute/pull/11596)) +- **chore(ci):** unblock quality gates - env-doc sync + stryker tap.testFiles registration (#9282) ([#11609](https://github.com/diegosouzapw/OmniRoute/pull/11609)) — thanks @Meet6338-X +- **chore(ci):** point the circular-deps gate at dpdm's real JS entrypoint ([#11615](https://github.com/diegosouzapw/OmniRoute/pull/11615)) — thanks @benzntech +- **chore(quality):** rebaseline file-size for #11448/#11495/#11561 growth ([#11630](https://github.com/diegosouzapw/OmniRoute/pull/11630)) +- **deps:** realign onnxruntime-node pin to @huggingface/transformers' 1.24.3 lockstep ([#11633](https://github.com/diegosouzapw/OmniRoute/pull/11633)) — thanks @jonlwheat2-gif +- **test(autoCombo):** port models_dev_tier #11508 guard from node:test to vitest ([#11635](https://github.com/diegosouzapw/OmniRoute/pull/11635)) — thanks @jonlwheat2-gif +- **chore(quality):** rebaseline file-size for #11083/#11631/#11628 growth ([#11636](https://github.com/diegosouzapw/OmniRoute/pull/11636)) +- **chore(lint):** prune stale ESLint suppressions (base-red #11449) ([#11644](https://github.com/diegosouzapw/OmniRoute/pull/11644)) — thanks @jonlwheat2-gif +- **docs(changelog):** aggregate the ten v3.8.50 fragments into CHANGELOG.md ([#11683](https://github.com/diegosouzapw/OmniRoute/pull/11683)) +- **chore(search):** drop unreachable credential sweep after #11642 ([#11687](https://github.com/diegosouzapw/OmniRoute/pull/11687)) — thanks @HouMinXi +- **ci:** add API route TypeScript regression gate ([#11705](https://github.com/diegosouzapw/OmniRoute/pull/11705)) — thanks @Sabeekhann +- **docs(dedup):** correct the CodeQL claim on the tenant-namespace comment ([#11712](https://github.com/diegosouzapw/OmniRoute/pull/11712)) +- **docs(changelog):** add consolidated v3.8.50 stats, top-25 ranking and a .mailmap ([#11715](https://github.com/diegosouzapw/OmniRoute/pull/11715)) +- **docs:** recommend gstack for AI-assisted workflows ([#11770](https://github.com/diegosouzapw/OmniRoute/pull/11770)) — thanks @santosraju99-hub +- **chore(quality):** tighten complexity/cognitive-complexity ratchets to the current tip; land the missed gateways.ts rebaseline ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **docs(i18n):** finish Freepik → Magnific rebrand in locale strings and README ([#11772](https://github.com/diegosouzapw/OmniRoute/pull/11772)) — thanks @turbolego +- **refactor(auto-combo):** fix divergent scoring in combo health reporting ([#11854](https://github.com/diegosouzapw/OmniRoute/pull/11854)) — thanks @jacobsparts +- **chore(changelog):** add missing fragments for #11919/#11918/#11916 ([#11938](https://github.com/diegosouzapw/OmniRoute/pull/11938)) +- **test(check):** escape the runs-on fixture with JSON.stringify, not a quote-only replace ([#11942](https://github.com/diegosouzapw/OmniRoute/pull/11942)) (direct commit `fb7445eaa3`) +- **test(release):** align five suites with the contracts #11933, #11919 and #11876 shipped on release/v3.8.51 ([#11944](https://github.com/diegosouzapw/OmniRoute/pull/11944)) +- **chore(quality):** remove unreachable code and restore test discovery ([#11950](https://github.com/diegosouzapw/OmniRoute/pull/11950)) — thanks @backryun +- **docs(agents):** sync-back landings are fast-forward, never squash ([#11964](https://github.com/diegosouzapw/OmniRoute/pull/11964)) +- **test(infra):** retry recursive temp-dir removal instead of failing a shard on ENOTEMPTY (#11966) ([#11968](https://github.com/diegosouzapw/OmniRoute/pull/11968)) +- **docs(ops):** the .113 heavy-build ceiling is one runner, not two ([#12048](https://github.com/diegosouzapw/OmniRoute/pull/12048)) +- **test(ui):** correct inactive auto-fetch expectation ([#12098](https://github.com/diegosouzapw/OmniRoute/pull/12098)) — thanks @RaviTharuma +- **docs:** document native dependency check escape hatch ([#12101](https://github.com/diegosouzapw/OmniRoute/pull/12101)) — thanks @RaviTharuma +- **test(providers):** regenerate the translate-path golden for OrcaRouter (#11923) ([#12118](https://github.com/diegosouzapw/OmniRoute/pull/12118)) +- **chore(quality):** velocity phase — loosen every numeric baseline by 20% until v4.0, monitor headroom nightly ([#12125](https://github.com/diegosouzapw/OmniRoute/pull/12125)) +- **chore(lint):** batch 0 of #12146 — type the call-log-cap sqlite rows instead of 45 as-any casts ([#12157](https://github.com/diegosouzapw/OmniRoute/pull/12157)) +- **chore(lint):** batch 4 of #12146 — shared/components react-hooks violations resolved ([#12159](https://github.com/diegosouzapw/OmniRoute/pull/12159)) +- **chore(lint):** batch 1 of #12146 — dashboard/cli-code react-hooks violations resolved ([#12160](https://github.com/diegosouzapw/OmniRoute/pull/12160)) +- **chore(lint):** batch 3 of #12146 — dashboard/settings react-hooks violations resolved ([#12162](https://github.com/diegosouzapw/OmniRoute/pull/12162)) +- **chore(lint):** batch 2 of #12146 — resolve the react-hooks compiler violations in dashboard/providers ([#12163](https://github.com/diegosouzapw/OmniRoute/pull/12163)) +- **chore(quality):** register search-432 cooldown test in stryker tap.testFiles ([#12170](https://github.com/diegosouzapw/OmniRoute/pull/12170)) +- **chore(lint):** batch 5 of #12146 — combos, endpoint, provider-stats, api-manager and costs react-hooks violations resolved ([#12174](https://github.com/diegosouzapw/OmniRoute/pull/12174)) +- **build:** add contributor fast profile ([#12192](https://github.com/diegosouzapw/OmniRoute/pull/12192)) — thanks @rafacpti23 +- **docs(readme):** README touch-ups ([#12193](https://github.com/diegosouzapw/OmniRoute/pull/12193)) ([#12194](https://github.com/diegosouzapw/OmniRoute/pull/12194)) ([#12202](https://github.com/diegosouzapw/OmniRoute/pull/12202)) — thanks @rafacpti23 +- **build:** force the webpack bundler in the contributor build profile ([#12197](https://github.com/diegosouzapw/OmniRoute/pull/12197)) — thanks @rafacpti23 +- **build:** skip standalone packaging in the contributor profile ([#12198](https://github.com/diegosouzapw/OmniRoute/pull/12198)) — thanks @rafacpti23 +- **docs(audit):** align every published number with the code and harden check:docs-counts ([#12200](https://github.com/diegosouzapw/OmniRoute/pull/12200)) +- **build:** stub the instrumentation entrypoints in the contributor profile ([#12203](https://github.com/diegosouzapw/OmniRoute/pull/12203)) — thanks @rafacpti23 +- **build:** omit the standalone output target for contributor builds ([#12204](https://github.com/diegosouzapw/OmniRoute/pull/12204)) — thanks @rafacpti23 +- **chore(lint):** batch 6 of #12146 — memory, radar, audit, analytics, cache, usage, activity, home and RequestLoggerV2 react-hooks violations resolved ([#12208](https://github.com/diegosouzapw/OmniRoute/pull/12208)) +- **docs(diagrams):** rename number-carrying diagram files to stable names ([#12210](https://github.com/diegosouzapw/OmniRoute/pull/12210)) +- **docs(dashboard):** redraw onboarding tier-flow SVGs for the real 4-tier model ([#12211](https://github.com/diegosouzapw/OmniRoute/pull/12211)) +- **docs(api):** document every implemented route in openapi.yaml (276 → 692 paths) ([#12212](https://github.com/diegosouzapw/OmniRoute/pull/12212)) +- **chore(lint):** batch 7 of #12146 — final src tail: 37 react-hooks violations across 33 files resolved ([#12244](https://github.com/diegosouzapw/OmniRoute/pull/12244)) +- Revert "docs: recommend gstack for AI-assisted workflows (#11770)" ([#12248](https://github.com/diegosouzapw/OmniRoute/pull/12248)) ([#12249](https://github.com/diegosouzapw/OmniRoute/pull/12249)) +- **docs:** Chaos Mode setup guide + weighted strategy semantics ([#12250](https://github.com/diegosouzapw/OmniRoute/pull/12250)) +- **docs(agents):** protected-surface merge rule — operator approval for agent-instruction files ([#12253](https://github.com/diegosouzapw/OmniRoute/pull/12253)) +- **chore(quality):** register native-codex turn-pin tests in stryker tap.testFiles ([#12263](https://github.com/diegosouzapw/OmniRoute/pull/12263)) +- **chore(quality):** dedupe tap.testFiles entries added by racing base-red fixes ([#12265](https://github.com/diegosouzapw/OmniRoute/pull/12265)) +- **docs(readme):** introduce OmniRouteTray — the macOS menu-bar companion ([#12276](https://github.com/diegosouzapw/OmniRoute/pull/12276)) — thanks @ggiak +- **chore(proxy):** purge the legacy 1proxy residue — sync/rotator modules, dead DB exports, dead settings tab and flag, docs (#12091) ([#12290](https://github.com/diegosouzapw/OmniRoute/pull/12290)) +- **chore(quality):** baseline-headroom skips generated and vendored files in the fileSize worst-file signal ([#12291](https://github.com/diegosouzapw/OmniRoute/pull/12291)) +- **refactor(video):** unify the JPEG frame data-URI contract ([#12322](https://github.com/diegosouzapw/OmniRoute/pull/12322)) +- **chore(deps):** freeze onnxruntime-node and eslint-plugin-react-hooks out of dependabot groups ([#12329](https://github.com/diegosouzapw/OmniRoute/pull/12329)) +- **test(executors):** restore the #10986 reasoning-only fallback guards ([#12364](https://github.com/diegosouzapw/OmniRoute/pull/12364)) — thanks @pacocartones +- **chore(quality):** re-tighten the file-size ratchet to the real LOC (plan 3.8.52 task 0) ([#12411](https://github.com/diegosouzapw/OmniRoute/pull/12411)) +- **test(grok):** format web executor suite ([#12412](https://github.com/diegosouzapw/OmniRoute/pull/12412)) +- **chore(quality):** ratchet open-sse typecheck baseline to zero ([#12418](https://github.com/diegosouzapw/OmniRoute/pull/12418)) +- **test(build):** guard the artifact path policy arrays against duplicates ([#12422](https://github.com/diegosouzapw/OmniRoute/pull/12422)) +- **chore(lint):** adopt eslint-plugin-react-hooks 7.1.1 ([#12428](https://github.com/diegosouzapw/OmniRoute/pull/12428)) +- **chore(providers):** remove a keyless provider integration at its operator's request ([#12440](https://github.com/diegosouzapw/OmniRoute/pull/12440)) +- **docs(providers):** register providers removed at their operator's request and guard against reintroduction ([#12478](https://github.com/diegosouzapw/OmniRoute/pull/12478)) +- **chore(quality):** base fixes — stryker tap.testFiles + node_modules cache key ([#12482](https://github.com/diegosouzapw/OmniRoute/pull/12482)) +- **docs(arch):** one-process recipe for tens of long /v1/responses ([#12493](https://github.com/diegosouzapw/OmniRoute/pull/12493)) — thanks @RaviTharuma +- **chore(quality):** tighten the CodeQL ratchet baseline from 11 to 6 ([#12530](https://github.com/diegosouzapw/OmniRoute/pull/12530)) +- **chore(deps):** pin browserslist override to ^4.28.8 ([#12592](https://github.com/diegosouzapw/OmniRoute/pull/12592)) — thanks @KooshaPari +- **chore(deps):** bump fast-uri to 3.1.7 in the electron lockfile ([#12601](https://github.com/diegosouzapw/OmniRoute/pull/12601)) +- **docs(quality):** document how the CodeQL ratchet refreshes and how to tighten it ([#12611](https://github.com/diegosouzapw/OmniRoute/pull/12611)) +- **test(ci):** pin the openapi-security-tiers two-arm contract with an executing gate test (#12581) ([#12652](https://github.com/diegosouzapw/OmniRoute/pull/12652)) +- **docs(gamification):** align XP Rewards table with code (#12501) ([#12667](https://github.com/diegosouzapw/OmniRoute/pull/12667)) — thanks @KooshaPari +- **refactor(combo):** split executeTarget into gates, attempt, and loop ([#12746](https://github.com/diegosouzapw/OmniRoute/pull/12746)) — thanks @HouMinXi +- **docs(security):** document socket.yml scanner config + CI workflow link (#12575) ([#12764](https://github.com/diegosouzapw/OmniRoute/pull/12764)) — thanks @KooshaPari +- **chore(ci):** guard commit identity in pre-commit to stop author misattribution ([#12772](https://github.com/diegosouzapw/OmniRoute/pull/12772)) +- **deps:** 17 Dependabot bumps — bump electron from 43.4.0 to 43.4.1 in /electron ([#11426](https://github.com/diegosouzapw/OmniRoute/pull/11426)); bump the development group across 1 directory with 8 updates ([#11428](https://github.com/diegosouzapw/OmniRoute/pull/11428)); bump the production group across 1 directory with 11 updates ([#11440](https://github.com/diegosouzapw/OmniRoute/pull/11440)); chore(deps): bump github/codeql-action/init from 4.37.7 to 4.37.8 ([#11925](https://github.com/diegosouzapw/OmniRoute/pull/11925)); chore(deps): bump github/codeql-action from 4.37.7 to 4.37.8 ([#11926](https://github.com/diegosouzapw/OmniRoute/pull/11926)); chore(deps): bump github/codeql-action/analyze from 4.37.7 to 4.37.8 ([#11927](https://github.com/diegosouzapw/OmniRoute/pull/11927)); build(deps): bump github/codeql-action/init from 4.37.8 to 4.37.9 ([#12345](https://github.com/diegosouzapw/OmniRoute/pull/12345)); build(deps): bump github/codeql-action/analyze from 4.37.8 to 4.37.9 ([#12346](https://github.com/diegosouzapw/OmniRoute/pull/12346)); bump the development group across 1 directory with 2 updates ([#12347](https://github.com/diegosouzapw/OmniRoute/pull/12347)); build(deps): bump github/codeql-action from 4.37.8 to 4.37.9 ([#12349](https://github.com/diegosouzapw/OmniRoute/pull/12349)); bump browserslist from 4.28.2 to 4.28.8 ([#12396](https://github.com/diegosouzapw/OmniRoute/pull/12396)); bump the production group across 1 directory with 4 updates ([#12399](https://github.com/diegosouzapw/OmniRoute/pull/12399)); bump @xmldom/xmldom ([#12500](https://github.com/diegosouzapw/OmniRoute/pull/12500)); bump qs from 6.15.2 to 6.16.0 ([#12512](https://github.com/diegosouzapw/OmniRoute/pull/12512)); bump @xmldom/xmldom from 0.9.10 to 0.9.12 ([#12513](https://github.com/diegosouzapw/OmniRoute/pull/12513)); bump fast-uri from 3.1.5 to 3.1.7 ([#12514](https://github.com/diegosouzapw/OmniRoute/pull/12514)); bump @humanfs/node from 0.16.7 to 0.16.8 ([#12515](https://github.com/diegosouzapw/OmniRoute/pull/12515)) +- Clear the `release/v3.8.51` `check:agent-skills-sync` base-red: regenerate `skills/cli-tunnel/SKILL.md` so the `tunnel create [type]` positional that #13009 taught the generator to read is reflected in the committed skill. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` typecheck base-red from #12731: the new `Mode pack ranking gates` candidates in `open-sse/services/autoCombo/__tests__/autoCombo.test.ts` omitted the required `provider`, `model` and `errorRate` fields of `ProviderCandidate`, failing `check:open-sse-typecheck` (and with it `Fast Quality Gates`) on every open PR. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` docs-sync base-red: EURouter (#13025) and GreenPT (#13024) took the live provider count to 358, leaving 7 STRICT drifts (`PROVIDER_REFERENCE.md`, 4 diagrams, 2 tier-flow images) plus the `AGENTS.md` / `llm.txt` / `package.json` count claims stale. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- **test(models):** Pin `CATALOG_BUILD_TIMEOUT_MS` in the #12058 canonical-catalog regression so a cold catalog build racing the 8s default bound no longer flakes the suite with a 500 ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Allow `@omniroute/opencode-plugin-v2/` in the npm pack policy — [#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870) shipped the v2 plugin beside its v1 sibling without widening the allowlist, so every packed file under it read as an unexpected artifact and failed `check:pack-policy` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Point the release-green validator's pack gate at the tree it is validating — the build-provenance guard defaulted to `origin/main`, which a release branch tip can never be an ancestor of mid-cycle, so `Package artifact` reported a false red on every run and the tarball boot-smoke cascaded off it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **docs(checks):** keep doc counts honest — headings, rankings, catalog, weights, quality gate and scoring diagram now covered ([#12507](https://github.com/diegosouzapw/OmniRoute/pull/12507)) — thanks @maxmad64bis +- **chore(lifecycle):** `check:model-lifecycle` now also diffs `DEFAULT_DEGRADATION_MAP` (the background-task redirect table) against the vendor lifecycle snapshot, refusing a retired id as source or target, with a table-driven unit test beside it. Three rows whose source the vendor had retired — `claude-sonnet-4-20250514`, `gemini-3-pro-preview` and `gpt-5.1-codex` (whose target `gpt-5.1-codex-mini` is retired too) — were dead code, since `checkLifecycle` answers 410 before the redirect runs; they are dropped (#12535 — thanks @pacocartones) +- **test(compression):** cover idle worker eviction at the resource level — the pool must call `terminate()` and must not retain the worker's `MessagePort`, complementing the `exit`-event assertion added with the fix ([#13371](https://github.com/diegosouzapw/OmniRoute/pull/13371)) +- **chore(quality):** raise the `imageGeneration.ts` file-size ceiling for the image-only-model guard that clears the #12945 base-red ([#13392](https://github.com/diegosouzapw/OmniRoute/pull/13392)) +- **chore(quality):** type the combo-test route's JSON response bodies instead of casting them to `any`, drop the now-empty suppression entry, and remove the `hasPerModelQuota` import `chatCore.ts` stopped using when the failure-classification helper was extracted ([#13379](https://github.com/diegosouzapw/OmniRoute/pull/13379)) +- **chore(electron):** the desktop bundle no longer ships the root-level files of every translated docs mirror (`README.md`, `llm.txt`, `CLAUDE.md`, `GEMINI.md`, `CONTRIBUTING.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`) — the packaged app only reads `docs/i18n//docs/**`, which stays. Saves ~11 MB on top of the translated CHANGELOGs already pruned. (#0000) ([#13235](https://github.com/diegosouzapw/OmniRoute/pull/13235)) +- **chore(quality):** retighten the `src/sse/handlers/chat.ts` unused-vars suppression count to the 8 violations that actually remain ([#13385](https://github.com/diegosouzapw/OmniRoute/pull/13385)) +- Bring the documented counts back in line with the code: 171 migrations (was 169) and 20 routing strategies (was 19), plus the regenerated `cli-tunnel` skill reference. ([#12970](https://github.com/diegosouzapw/OmniRoute/pull/12970)) +- Update the documented migration count to 172 after the call-logs provider-stats indexes landed. ([#13209](https://github.com/diegosouzapw/OmniRoute/pull/13209)) +- **chore(ci):** two gates that close the blind spots behind the exclusions above. `check:vitest-exclusions` requires every Vitest exclusion to name a tracking issue and to appear in `config/quality/vitest-exclusions.json` — the previous list grew to 62 files behind a comment pointing at an issue that had been closed for a month. `check-new-key-coverage` requires a key newly added to `en.json` to reach every locale; the existing coverage gate is a percentage floor per locale, so eleven absent keys out of ~13,000 left it at 99.9% while a whole feature shipped untranslated in nine languages. Both are diff-aware, so pre-existing debt stays frozen and neither needed a migration to turn on. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **build:** root layout no longer loads Inter from `next/font/google`, so a production image build does not need fonts.googleapis.com ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **docs(i18n):** bring the eight new `llm.txt` mirrors (km, kn, ml, my, ne, or, pa, si) up to the current provider and migration counts, so `check-docs-sync` stops rejecting every commit ([#13674](https://github.com/diegosouzapw/OmniRoute/pull/13674)) +- **refactor(ui):** flow surfaces (home topology, combo live studio, compression cockpit/waterfall/nodes, token-health badge) express state through the theme-aware `--orch-status-*` tokens instead of fixed dark-mode hex, so green/red/amber/grey stay legible in the light theme; dark mode is byte-identical. Categorical palettes (routing-strategy hues, compression-layer pills, provider brand colors) deliberately stay hex ([#12378](https://github.com/diegosouzapw/OmniRoute/issues/12378)) ([#13203](https://github.com/diegosouzapw/OmniRoute/pull/13203)) +- **chore(tests):** 51 test files that had been excluded from Vitest are running again, restoring roughly 350 assertions to the blocking suite. Each was measured individually first: of the 62 files parked behind the `// #8618 — pre-existing failure` comment, 51 pass against the current tree with no source change, so the exclusions had outlived the failures they were added for. The 11 that genuinely still fail stay excluded, but now point at a live tracker (#13204) rather than at #8618, which was closed in August while the list it tracked kept growing. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **chore(quality):** drop two ESLint suppression entries whose violations no longer exist, so `eslint --suppressions-location` stops rejecting every commit that touches the surrounding files ([#13384](https://github.com/diegosouzapw/OmniRoute/pull/13384)) +- **chore(release):** bring main's post-tag fixes into release/v3.8.51 — sync-back step 2/2 (direct commit `21c488f210`) +- **chore(deps):** patch toml and esbuild in the opencode plugin lockfiles ([#12965](https://github.com/diegosouzapw/OmniRoute/pull/12965)) +- **docs(changelog):** reconcile the v3.8.51 living section — fold 366 fragments, cover every cycle commit, credit every contributor ([#12971](https://github.com/diegosouzapw/OmniRoute/pull/12971)) +- **test(plugin):** wait for the v2 lazy-refresh tier instead of sleeping past it ([#12984](https://github.com/diegosouzapw/OmniRoute/pull/12984)) +- **chore(quality):** rebaseline chatCore.ts after the non-streaming regression fixes ([#13045](https://github.com/diegosouzapw/OmniRoute/pull/13045)) +- **docs(changelog):** add the missing bullet marker to the DeepSeek PoW fragment ([#13200](https://github.com/diegosouzapw/OmniRoute/pull/13200)) +- **chore(deps):** drain the Dependabot queue — 10 of 13 alerts ([#13213](https://github.com/diegosouzapw/OmniRoute/pull/13213)) +- **chore(ci):** make worktrees carry the hooks that enforce the identity gate ([#13236](https://github.com/diegosouzapw/OmniRoute/pull/13236)) +- **chore(deps):** pin csv-parse to ^7.0.2 via overrides (#13117) ([#13300](https://github.com/diegosouzapw/OmniRoute/pull/13300)) — thanks @KooshaPari +- **chore(deps):** bump hono from ^4.12.34 to ^4.13.7 (#13148) ([#13301](https://github.com/diegosouzapw/OmniRoute/pull/13301)) — thanks @KooshaPari +- **chore(deps):** pin joi to ^18.2.8 via overrides (#13085) ([#13302](https://github.com/diegosouzapw/OmniRoute/pull/13302)) — thanks @KooshaPari +- **docs(providers):** add Microsoft 365 Copilot (BizChat) provider guide (#12779) ([#13340](https://github.com/diegosouzapw/OmniRoute/pull/13340)) — thanks @KooshaPari +### 🙌 Contributors + +Thanks to everyone whose work landed in v3.8.51: + +| Contributor | PRs / Issues | +| --- | --- | +| [@5dive-bot](https://github.com/5dive-bot) | #11852 | +| [@abhisheksharma2411](https://github.com/abhisheksharma2411) | #11547, #11684, #11959, #12209 | +| [@adevwithpurpose](https://github.com/adevwithpurpose) | #11464 | +| [@adivekar-utexas](https://github.com/adivekar-utexas) | #12015, #12043, #12090, #12138 | +| [@AIB1TAL0S](https://github.com/AIB1TAL0S) | #12103 | +| [@alltomatos](https://github.com/alltomatos) | #11448 | +| [@alvinveroy](https://github.com/alvinveroy) | #11857, #12027, #12165 | +| [@amaleta](https://github.com/amaleta) | #11906, #11934 | +| [@amartinawi](https://github.com/amartinawi) | #11575 | +| [@AndrianBalanescu](https://github.com/AndrianBalanescu) | #11083, #11830, #11834, #11844 | +| [@anhtahaylove](https://github.com/anhtahaylove) | #13091, #13092, #13093, #13096, #13097, #13100, #13106, #13109, #13114, #13155, #13166, #13171, #13175, #13187, #13196 | +| [@aniruddhaadak80](https://github.com/aniruddhaadak80) | #11760 | +| [@app](https://github.com/app) | #12554 | +| [@arminanton](https://github.com/arminanton) | #11461, #11513 | +| [@AStupidBear](https://github.com/AStupidBear) | #11584 | +| [@atescivitci-cmd](https://github.com/atescivitci-cmd) | #12105 | +| [@b3nw](https://github.com/b3nw) | #11970, #11971, #12147 | +| [@backryun](https://github.com/backryun) | #11259, #11950, #12075, #12076, #12078, #12079, #12081, #12082, #12181, #12228, #12239, #12255, #12258, #12277, #12367, #12423, #12524, #12538 | +| [@benjaminkitt](https://github.com/benjaminkitt) | #11747 | +| [@benzntech](https://github.com/benzntech) | #11614, #11615 | +| [@Bl0ck154](https://github.com/Bl0ck154) | #11948, #11951, #11952, #11953, #11954 | +| [@brick30llc-ctrl](https://github.com/brick30llc-ctrl) | #11752, #12242, #12262 | +| [@Chewji9875](https://github.com/Chewji9875) | #11455, #11875, #12028 | +| [@cheynetom](https://github.com/cheynetom) | #12058 | +| [@davidebaraldo](https://github.com/davidebaraldo) | #12222, #12304 | +| [@davidlinfr](https://github.com/davidlinfr) | #6390 | +| [@ddarkr](https://github.com/ddarkr) | #12124 | +| [@Deftera186](https://github.com/Deftera186) | #11809 | +| [@dpozimski](https://github.com/dpozimski) | #11945 | +| [@DW-MediaLab](https://github.com/DW-MediaLab) | #12229 | +| [@echel0nn](https://github.com/echel0nn) | #11923 | +| [@f9td56dbgh-hub](https://github.com/f9td56dbgh-hub) | #11660 | +| [@fabioluissilva](https://github.com/fabioluissilva) | #11991 | +| [@foreveryh](https://github.com/foreveryh) | #12177 | +| [@geek007git](https://github.com/geek007git) | #12115, #12116, #12117, #12120, #12122 | +| [@geekyNads](https://github.com/geekyNads) | #11784, #12266 | +| [@ggiak](https://github.com/ggiak) | #11561, #12276, #12350, #12402 | +| [@gonisulaimann](https://github.com/gonisulaimann) | #12368, #12369, #12371 | +| [@Gorillaz322](https://github.com/Gorillaz322) | #12207 | +| [@groovecityJO](https://github.com/groovecityJO) | #12682 | +| [@hartmark](https://github.com/hartmark) | #11434, #11452, #11473, #11499, #11703, #11983, #11984, #11985, #11986, #11988, #11989, #11990, #11994, #12221, #12293, #12445, #12446, #12447, #12448, #12460, #12461, #12623, #12646, #12650, #12680, #12717, #12718, #12727, #12741, #12854 | +| [@hizzt](https://github.com/hizzt) | #11894 | +| [@hongnoul](https://github.com/hongnoul) | #11484 | +| [@HouMinXi](https://github.com/HouMinXi) | #11411, #11414, #11512, #11518, #11520, #11641, #11642, #11643, #11687, #11779, #11849, #11850, #11851, #11915, #11916, #11918, #11919, #11920, #12013, #12017, #12033, #12042, #12106, #12139, #12166, #12169, #12171, #12205, #12213, #12312, #12325, #12487, #12488, #12495, #12504, #12557, #12566, #12590, #12591, #12624, #12626, #12632, #12637, #12678, #12696, #12697, #12711, #12733, #12746, #12767, #12770, #12789, #12803, #12805, #12811, #12866, #12868, #12899, #12926, #12934, #12950, #12951, #12974, #13001, #13006, #13011, #13017, #13026, #13027, #13034, #13035, #13038, #13042, #13050, #13060, #13061, #13069, #13107, #13120, #13136, #13195, #13197 | +| [@Hsia97](https://github.com/Hsia97) | #11624 | +| [@jacobsparts](https://github.com/jacobsparts) | #11854, #12155, #12167 | +| [@joglomedia](https://github.com/joglomedia) | #11980 | +| [@jonlwheat2-gif](https://github.com/jonlwheat2-gif) | #11443, #11450, #11549, #11567, #11608, #11633, #11635, #11640, #11644, #11671, #11672, #11673, #11674, #11675, #11676, #11677, #11706, #11783, #12051, #12052, #12053, #12055 | +| [@kanade-hoshino](https://github.com/kanade-hoshino) | #12180 | +| [@Karan825](https://github.com/Karan825) | #11861 | +| [@KaspaPulse](https://github.com/KaspaPulse) | #11389, #11469, #11628, #11666, #11910 | +| [@keeltrace](https://github.com/keeltrace) | #12080, #12223 | +| [@killer30001000](https://github.com/killer30001000) | #12178 | +| [@KooshaPari](https://github.com/KooshaPari) | #12470, #12592, #12667, #12699, #12703, #12706, #12764, #12769, #12771, #13300, #13301, #13302, #13340, #13401, #13403, #13404, #13405, #13406, #13407, #13409, #13410, #13411, #13412, #13413, #13414, #13418, #13424, #13427, #13433, #13523, #13524, #13525, #13528, #13531, #13532, #13534, #13539, #13543, #13545, #13546, #13547, #13550, #13553 | +| [@kriptoburak](https://github.com/kriptoburak) | #11370 | +| [@KrzysiekSko](https://github.com/KrzysiekSko) | #12425, #12432, #12673 | +| [@marcelokarval](https://github.com/marcelokarval) | direct commit / report | +| [@maxmad64bis](https://github.com/maxmad64bis) | #1622, #11435, #11437, #11441, #11537, #11550, #11553, #11555, #11557, #11812, #11842, #11843, #11903, #12151, #12214, #12215, #12218, #12226, #12314, #12316, #12317, #12318, #12319, #12320, #12321, #12507, #12715, #12731, #12744, #12786, #12787, #12788, #12790, #12792, #12794, #12795, #12828, #12832, #12853, #12857, #12870, #12937, #12941, #12975, #13141, #13142, #13146, #13147 | +| [@mdigitalbh81](https://github.com/mdigitalbh81) | #12240 | +| [@Meet6338-X](https://github.com/Meet6338-X) | #11598, #11609, #12110 | +| [@morpheus9393](https://github.com/morpheus9393) | #11943 | +| [@MumuTW](https://github.com/MumuTW) | #11492, #11502, #11506, #11507, #11626, #11675, #11685, #11728, #11746, #11888, #11889, #11890, #11892 | +| [@Neuron-Mr-White](https://github.com/Neuron-Mr-White) | #11622, #11801, #12256 | +| [@NightStalker-87](https://github.com/NightStalker-87) | #12183 | +| [@NoxzRCW](https://github.com/NoxzRCW) | #11879, #11880, #11881, #11882, #11883, #11935 | +| [@ntdat812](https://github.com/ntdat812) | #11585, #12095 | +| [@ntdatt812](https://github.com/ntdatt812) | #11368, #11573, #11574, #11576, #11577, #11580, #11582, #11583, #11588, #11589, #11590, #11591, #11592, #11593, #11672, #12177, #12180, #12873, #12918, #12920, #12921, #12925, #12930, #13007, #13009, #13024, #13025, #13083, #13087, #13101, #13104, #13110 | +| [@official-burak](https://github.com/official-burak) | #11542 | +| [@opensource-elearning](https://github.com/opensource-elearning) | #12179, #12189, #12278, #12286 | +| [@oyi77](https://github.com/oyi77) | #11408, #11409, #11421, #11505, #11677, #12036, #12110 | +| [@pacocartones](https://github.com/pacocartones) | #11521, #11522, #11527, #11528, #11529, #11530, #11531, #11532, #11533, #11534, #11595, #11599, #11603, #11604, #11605, #11607, #11610, #11676, #11714, #11716, #11718, #11767, #11838, #11860, #11862, #11869, #11871, #11872, #11873, #11903, #11906, #11921, #11934, #12356, #12358, #12359, #12360, #12361, #12362, #12364, #12365, #12373, #12374, #12375, #12376, #12377, #12379, #12380, #12381, #12386, #12387, #12389, #12390, #12394, #12395, #12397, #12401, #12403, #12404, #12522, #12523, #12535, #12536, #12540, #12541, #12543, #12545, #12548, #12549, #12550, #12551, #12552, #12647, #12651, #12653, #13055 | +| [@patrykkopycinski](https://github.com/patrykkopycinski) | #11936, #11937, #12224 | +| [@PauloFH](https://github.com/PauloFH) | #11509 | +| [@PauloHSOliveira](https://github.com/PauloHSOliveira) | #12241 | +| [@ponkcore](https://github.com/ponkcore) | #12054 | +| [@Prajeeth-12](https://github.com/Prajeeth-12) | #11634, #12046 | +| [@quiterunner-commits](https://github.com/quiterunner-commits) | #12131, #12143 | +| [@rafacpti23](https://github.com/rafacpti23) | #11554, #11558, #12192, #12197, #12198, #12202, #12203, #12204 | +| [@ragnar-claude](https://github.com/ragnar-claude) | #11564 | +| [@raheemuddin786](https://github.com/raheemuddin786) | #11491, #11839, #11840, #11841, #12003, #12230, #12231, #12232, #12233, #12234 | +| [@rahilmavani](https://github.com/rahilmavani) | #11761 | +| [@Rahulsharma0810](https://github.com/Rahulsharma0810) | #11771 | +| [@RaviTharuma](https://github.com/RaviTharuma) | #11710, #11727, #11797, #11798, #11802, #11805, #11806, #11811, #12098, #12099, #12101, #12449, #12452, #12472, #12473, #12493, #12533, #12607, #12628, #12631, #12636, #12875, #12876, #12880, #12882, #12884 | +| [@rezjalibd](https://github.com/rezjalibd) | #12186 | +| [@RhianB14](https://github.com/RhianB14) | #11692 | +| [@rifqiawl](https://github.com/rifqiawl) | #11517, #11519, #11969 | +| [@rqzbeh](https://github.com/rqzbeh) | #11390 | +| [@Sabeekhann](https://github.com/Sabeekhann) | #11699, #11705 | +| [@santosraju99-hub](https://github.com/santosraju99-hub) | #11755, #11770 | +| [@Seramicx](https://github.com/Seramicx) | #11568 | +| [@Siva010](https://github.com/Siva010) | #12191 | +| [@solstxce](https://github.com/solstxce) | #11597 | +| [@soroush5](https://github.com/soroush5) | #12691 | +| [@tenshiak](https://github.com/tenshiak) | #12279 | +| [@TheDemonTuan](https://github.com/TheDemonTuan) | #11468, #11470, #11471, #11482, #11548, #11758, #11775, #11814 | +| [@thomasmaerz](https://github.com/thomasmaerz) | #12834 | +| [@tuandinh0801](https://github.com/tuandinh0801) | #11454 | +| [@turbolego](https://github.com/turbolego) | #11621, #11762, #11772, #11774, #11781, #12216 | +| [@Tushar49](https://github.com/Tushar49) | #11392 | +| [@ujjawalkaushik1110](https://github.com/ujjawalkaushik1110) | #11863 | +| [@vermasomesh835](https://github.com/vermasomesh835) | #11794 | +| [@vsd2807](https://github.com/vsd2807) | #11565, #11619 | +| [@wahidsadik371-coder](https://github.com/wahidsadik371-coder) | #12126 | +| [@watchingdogs](https://github.com/watchingdogs) | #12031 | +| [@wildcard](https://github.com/wildcard) | #11620 | +| [@xiaoyaner0201](https://github.com/xiaoyaner0201) | #11460, #11662, #12083 | +| [@Xxx91n](https://github.com/Xxx91n) | #11690 | +| [@yourspraveen](https://github.com/yourspraveen) | #11146 | +| [@yxyxy](https://github.com/yxyxy) | #11671 | +| [@ZaimMarzuki](https://github.com/ZaimMarzuki) | #11960 | +| [@Zartharas](https://github.com/Zartharas) | #11340 | +| [@zero-executioner](https://github.com/zero-executioner) | #11631 | +| [@diegosouzapw](https://github.com/diegosouzapw) | maintainer | + +--- ## [3.8.50] — 2026-08-25 _Living section — regenerated 2026-08-12 from all cycle commits (cycle open `ed2db6cb19` → tip). Bullets carry the merged PR and its author; direct pushes listed separately._ diff --git a/docs/i18n/pl/CHANGELOG.md b/docs/i18n/pl/CHANGELOG.md index 3182ce00ac..11c4d99e84 100644 --- a/docs/i18n/pl/CHANGELOG.md +++ b/docs/i18n/pl/CHANGELOG.md @@ -10,31 +10,31 @@ ## [3.8.51] — TBD -_Living section — reconciled 2026-09-07 from all cycle commits (`release/v3.8.50` tip `091589089c` → `release/v3.8.51` tip `d6f315018a`, 696 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._ +_Living section — reconciled 2026-09-15 from all cycle commits (`091589089c` → `c0f92ec98a`, 916 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 | **120** | -| 📝 Commits in the cycle | **696** | -| 🔀 Pull requests referenced | **672** | -| 📋 Changelog entries | **685** | -| 🙌 Contributors credited in entries | **118** | +| 👥 People who contributed | **123** | +| 📝 Commits in the cycle | **916** | +| 🔀 Pull requests referenced | **892** | +| 📋 Changelog entries | **919** | +| 🙌 Contributors credited in entries | **121** | | 🤖 Automated dependency commits | 18 | **Entries by type** | Type | Count | | --- | ---: | -| 🐛 Fixes | 403 | -| ✨ Features | 118 | -| 🧹 Chore | 72 | -| 📚 Docs | 36 | -| 🧪 Tests | 29 | +| 🐛 Fixes | 592 | +| ✨ Features | 131 | +| 🧹 Chore | 93 | +| 📚 Docs | 42 | +| 🧪 Tests | 32 | +| ♻️ Refactor | 8 | +| 🏗️ Build | 7 | | ⚡ Performance | 7 | -| ♻️ Refactor | 7 | -| 🏗️ Build | 6 | | ⚙️ CI | 2 | | ⏪ Reverts | 2 | | 📦 Dependencies | 2 | @@ -42,35 +42,35 @@ _Living section — reconciled 2026-09-07 from all cycle commits (`release/v3.8. ### 🏆 Top 25 contributors this cycle -_By commits in `release/v3.8.50..release/v3.8.51` (`091589089c..d6f315018a`), author identities consolidated via `.mailmap` and the merged PR's GitHub login. Bots excluded._ +_By commits in `091589089c..c0f92ec98a`, author identities consolidated via `.mailmap` and the merged PR's GitHub login. Bots excluded._ | # | Contributor | Commits | | ---: | --- | ---: | -| 🥇 | diegosouzapw | 240 | -| 🥈 | Paco Cartones (@pacocartones) | 58 | -| 🥉 | Bob.Hou (@HouMinXi) | 57 | -| 4 | Dizzle (@maxmad64bis) | 27 | -| 5 | Webman (@jonlwheat2-gif) | 22 | -| 6 | Ravi Tharuma (@RaviTharuma) | 21 | -| 7 | backryun | 17 | -| 8 | Markus Hartung (@hartmark) | 17 | -| 9 | Nguyen Thanh Dat (@ntdatt812) | 16 | -| 10 | Rafa Martins (@rafacpti23) | 10 | -| 11 | Syed Raheemuddin (@raheemuddin786) | 10 | -| 12 | Koosha Paridehpour (@KooshaPari) | 9 | -| 13 | Nguyễn Viết Tuấn (@TheDemonTuan) | 8 | -| 14 | MumuTW | 7 | -| 15 | Paijo (@oyi77) | 7 | -| 16 | Tobias Andersen (@turbolego) | 6 | -| 17 | Bl0ck (@Bl0ck154) | 5 | -| 18 | SHANMUGAPRIYAN (@geek007git) | 5 | -| 19 | KaspaPulse | 5 | -| 20 | Abhishek Sharma (@abhisheksharma2411) | 4 | -| 21 | Abhishek Divekar (@adivekar-utexas) | 4 | -| 22 | Andrew B. (@AndrianBalanescu) | 4 | -| 23 | NoxzRCW | 4 | -| 24 | opensource-elearning | 4 | -| 25 | Alvin T. Veroy (@alvinveroy) | 3 | +| 🥇 | diegosouzapw | 323 | +| 🥈 | Bob.Hou (@HouMinXi) | 79 | +| 🥉 | Paco Cartones (@pacocartones) | 77 | +| 4 | Dizzle (@maxmad64bis) | 49 | +| 5 | Koosha Paridehpour (@KooshaPari) | 43 | +| 6 | Nguyen Thanh Dat (@ntdatt812) | 31 | +| 7 | Ravi Tharuma (@RaviTharuma) | 26 | +| 8 | Markus Hartung (@hartmark) | 22 | +| 9 | Webman (@jonlwheat2-gif) | 22 | +| 10 | backryun | 17 | +| 11 | anhtahaylove | 15 | +| 12 | Rafa Martins (@rafacpti23) | 10 | +| 13 | Syed Raheemuddin (@raheemuddin786) | 10 | +| 14 | Nguyễn Viết Tuấn (@TheDemonTuan) | 8 | +| 15 | MumuTW | 7 | +| 16 | Paijo (@oyi77) | 7 | +| 17 | Tobias Andersen (@turbolego) | 6 | +| 18 | Bl0ck (@Bl0ck154) | 5 | +| 19 | SHANMUGAPRIYAN (@geek007git) | 5 | +| 20 | KaspaPulse | 5 | +| 21 | Abhishek Sharma (@abhisheksharma2411) | 4 | +| 22 | Abhishek Divekar (@adivekar-utexas) | 4 | +| 23 | Andrew B. (@AndrianBalanescu) | 4 | +| 24 | NoxzRCW | 4 | +| 25 | opensource-elearning | 4 | ### ✨ New Features @@ -196,6 +196,19 @@ _By commits in `release/v3.8.50..release/v3.8.51` (`091589089c..d6f315018a`), au - **feat(dashboard):** link a log entry's Conversation Context to its owning conversation ([#12646](https://github.com/diegosouzapw/OmniRoute/pull/12646)) — thanks @hartmark - **feat(catalog):** eligibility-gated free-tier bucket ([#12669](https://github.com/diegosouzapw/OmniRoute/pull/12669)) - **feat(db):** fail-closed previous_response_id continuation for redacted video turns (#12150 P2b) ([#12707](https://github.com/diegosouzapw/OmniRoute/pull/12707)) +- **feat(gamification): pay the documented `streak_bonus` and `badge_unlock` XP rewards.** `XP_REWARDS` listed both rewards but the award pipeline never paid them: the private reward table in `events.ts` omitted them, `updateStreak()` did not report when a streak extended, and badge unlocks carried no XP. Every request that extends a daily streak now pays `streak_bonus × streak length` once per UTC day (guarded by a same-day `xp_audit_log` check), and every badge unlocked through the pipeline pays `badge_unlock` once per badge (guarded by the `user_badges` primary key; `unlockBadge()` now reports whether it inserted). Bonus XP flows through the same `addXp` + level sync + global/weekly/monthly leaderboard path as action XP, so level-ups and rankings include it. The Radar supporter recognition unlock stays XP-free. (#12522 — thanks @pacocartones) +- **feat(providers):** advertise a `free-tier` capability in the provider plugin manifest for every provider with documented free models, so sidecars and dashboards can filter free-capable providers without reading the quota catalog ([#12786](https://github.com/diegosouzapw/OmniRoute/pull/12786)) — thanks @maxmad64bis +- **feat(providers):** Added EURouter as an OpenAI-compatible API-key gateway (`https://api.eurouter.ai/v1`), with live model discovery via `passthroughModels`. Its copy states that models are served by third-party upstreams listed per model, so an EU-based router is not read as EU data residency for inference. ([#13025](https://github.com/diegosouzapw/OmniRoute/pull/13025)) — thanks @ntdatt812 +- **feat(providers):** Added GreenPT as an OpenAI-compatible API-key provider (`https://api.greenpt.ai/v1`), with live model discovery via `passthroughModels`. No free-inference badge: the published docs describe a free API subscription billed per token, not a free tier. ([#13024](https://github.com/diegosouzapw/OmniRoute/pull/13024)) — thanks @ntdatt812 +- **feat(i18n):** 9 new locales — Greek (`el`), Croatian (`hr`), Serbian (`sr`), Lithuanian (`lt`), Estonian (`et`), Latvian (`lv`), Slovenian (`sl`), Maltese (`mt`), Irish (`ga`) — across the dashboard, docs mirrors, CLI, README and the site; OmniRoute now ships every official EU language (51 locales). (#13044) +- **feat(i18n):** 8 new locales — Kannada (`kn`), Malayalam (`ml`), Odia (`or`), Punjabi (`pa`), Nepali (`ne`), Sinhala (`si`), Burmese (`my`), Khmer (`km`) — across the dashboard, docs mirrors, CLI, README and the site (59 locales). The translator now restores the ICU literal escape around angle placeholders and splits oversized docs sections before translating. (#0000) ([#13660](https://github.com/diegosouzapw/OmniRoute/pull/13660)) +- feat(providers): **list Agnes 3.0 Flash as the current free chat model, drop retired 1.5 Flash, add Image 2.0/2.5 Flash plus Video 2.5/2.5 Flash, and discover the live `/v1/models` catalog (including the CN host `api.agnes-ai.cn`).** `agnes-1.5-flash` now forwards to `agnes-3.0-flash`. Video 2.5 polls `GET /v1/videos/{id}` (not the V2.0 `/agnesapi` contract). Live `/v1/models` (2026-09-09) no longer serves 1.5; the wiki marks it deprecated. 3.0 Flash is 512K context / 65,536 max output, same window as 2.5. CN-region keys use the existing per-connection base-URL field, default stays `apihub.agnes-ai.com`. ([#13120](https://github.com/diegosouzapw/OmniRoute/pull/13120)) — thanks @HouMinXi +- **feat(sse):** Codex and OpenAI catalogs list GPT-6 Astra with effort aliases (`-low` through `-ultra`); Codex CLI identity pins `@openai/codex@0.153.4` in lockstep with the image ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **feat(dashboard):** the orchestration History tab gained a "Compare runs" mode — toggling it turns each grid cell into a 2-item selection queue (a 3rd click drops the oldest pick), and picking two cells opens a side-by-side comparison panel instead of the usual detail drawer. The panel fetches both runs' detail the same way the drawer does (falling back to persisted history once a run leaves the live TTL window) and shows, per side: identity/source/state, start time, a signed `right - left` delta for duration/cost/event count, the event timeline aligned by index, and any memory hits. One side's fetch failing never blocks the other, and a delta is only ever computed when both sides have a finite value — otherwise it renders "—", never `NaN`. Comparing two runs from different sources or skills still works; a banner marks the deltas as informational rather than hiding them, since the two runs aren't a strict apples-to-apples pair. ([#12677](https://github.com/diegosouzapw/OmniRoute/pull/12677)) +- **feat(dashboard):** badge a conversation that never reached a clean stop ([#12717](https://github.com/diegosouzapw/OmniRoute/pull/12717)) — thanks @hartmark +- **feat(release):** reconcile-changelog tool + version-anchored fragment aggregation ([#12987](https://github.com/diegosouzapw/OmniRoute/pull/12987)) +- **feat(mcp):** carry context_length through MCP list_models_catalog projection (#12776) ([#13406](https://github.com/diegosouzapw/OmniRoute/pull/13406)) — thanks @KooshaPari +- **feat(cli):** make startup readiness budget configurable (#13369) ([#13433](https://github.com/diegosouzapw/OmniRoute/pull/13433)) — thanks @KooshaPari ### 🐛 Bug Fixes @@ -612,6 +625,194 @@ _By commits in `release/v3.8.50..release/v3.8.51` (`091589089c..d6f315018a`), au - **fix(i18n):** quote placeholder in OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES description (#12505) ([#12769](https://github.com/diegosouzapw/OmniRoute/pull/12769)) — thanks @KooshaPari - **fix(api/pricing):** surface validation error message as string, not raw object (#12494) ([#12771](https://github.com/diegosouzapw/OmniRoute/pull/12771)) — thanks @KooshaPari - **fix(sse):** re-enable prompt compression for native Codex passthrough ([#12834](https://github.com/diegosouzapw/OmniRoute/pull/12834)) — thanks @thomasmaerz +- **fix(dashboard):** model health tests for a provider node set to the Responses API now call `/v1/responses` with a Responses-shaped body instead of `/v1/chat/completions` — those models were reported as `Provider returned HTTP 200 but no text content` even though the same model answered normally through `/v1/responses` ([#13070](https://github.com/diegosouzapw/OmniRoute/issues/13070)) ([#13087](https://github.com/diegosouzapw/OmniRoute/pull/13087)) — thanks @ntdatt812 +- fix(routing): stop a round-robin combo's "opencode" targets from collapsing onto the opencode-zen connection (#11912) ([#13283](https://github.com/diegosouzapw/OmniRoute/pull/13283)) +- fix(routing): stop the reactive-compaction debug log from lying when compression is globally disabled (#11977) ([#13276](https://github.com/diegosouzapw/OmniRoute/pull/13276)) +- fix(electron): relativize standalone-bundle symlink targets so Stage 8 manifest verification stops failing on Windows (#11979) ([#13251](https://github.com/diegosouzapw/OmniRoute/pull/13251)) +- fix(dashboard): surface a visible error when Compression Studio's combined preview run fails (#12061) ([#13288](https://github.com/diegosouzapw/OmniRoute/pull/13288)) +- fix(dashboard): make the compression "Effective pipeline" preview honor the active profile and warn when the master switch is off (#12063) ([#13287](https://github.com/diegosouzapw/OmniRoute/pull/13287)) +- fix(providers): scope TinyCMS Web signer's DOM shims to each call instead of leaking them for the process lifetime, and surface a clean HTTP status on a non-JSON interception-toggles error (#12072) ([#13254](https://github.com/diegosouzapw/OmniRoute/pull/13254)) +- fix(guardrails): stop Vision Bridge from re-selecting a model locked after a 404 (#12111) ([#13259](https://github.com/diegosouzapw/OmniRoute/pull/13259)) +- fix(sse): require Responses-shaped body before native OpenAI-compatible passthrough (#12129) ([#13278](https://github.com/diegosouzapw/OmniRoute/pull/13278)) +- fix(providers): minimax-m3 now collapses manual thinking.type:"enabled" to adaptive, preventing upstream 400 (2013) (#12132) ([#13249](https://github.com/diegosouzapw/OmniRoute/pull/13249)) +- fix(db): scope model visibility overrides by modality so hiding a Chat model no longer hides an identically-ID'd Image/Embeddings/etc. model (#12172) ([#13258](https://github.com/diegosouzapw/OmniRoute/pull/13258)) +- fix(db): scope local-provider apiKey dedup to matching base URL so LM Studio/Ollama-style connections support multiple accounts (#12173) ([#13250](https://github.com/diegosouzapw/OmniRoute/pull/13250)) +- fix(providers): refresh Trae's stale Referer/Origin and forward user timezone so imported connections stop failing with 401 (#12190) ([#13255](https://github.com/diegosouzapw/OmniRoute/pull/13255)) +- fix(providers): route opencode-go/gpt-5.6-luna to /responses instead of /chat/completions (#12196) ([#13275](https://github.com/diegosouzapw/OmniRoute/pull/13275)) +- fix(dashboard): allow deleting the last extra-upstream-header row even when invalid (#12251) ([#13252](https://github.com/diegosouzapw/OmniRoute/pull/13252)) +- **fix(i18n):** translate pre-existing `__MISSING__:` keys for `combo.sort`, `requestLogger.detail` expand/collapse, `common.profile`, and `settings.resilienceCredentialHealth*` across 39 locales ([#12272](https://github.com/diegosouzapw/OmniRoute/issues/12272)) ([#13034](https://github.com/diegosouzapw/OmniRoute/pull/13034)) — thanks @HouMinXi +- fix(cli): run the Node.js runtime compatibility guard before the heavy `tsx/esm` + Commander import chain so an unsupported runtime gets a clear message instead of a raw `Invalid regular expression flags` crash (#12296) ([#13253](https://github.com/diegosouzapw/OmniRoute/pull/13253)) +- fix(dashboard): refresh the providers list after deleting a compatible provider node (#12298) ([#13256](https://github.com/diegosouzapw/OmniRoute/pull/13256)) +- fix(usage): fail closed on API-key budget enforcement when a provider's `auto` routing alias has no pricing row, instead of silently counting it as $0 (#12341) ([#13257](https://github.com/diegosouzapw/OmniRoute/pull/13257)) +- **fix(providers):** include the submitted Agnes video model when polling by `video_id` ([#12356](https://github.com/diegosouzapw/OmniRoute/pull/12356)) — thanks @pacocartones +- **fix(routing):** custom OpenAI-compatible nodes now honor the saved Chat/Responses API type after edits instead of letting the node's original ID prefix override the live connection setting ([#11884](https://github.com/diegosouzapw/OmniRoute/issues/11884)). ([#12358](https://github.com/diegosouzapw/OmniRoute/pull/12358)) — thanks @pacocartones +- fix(sse): surface an error instead of a silent empty 200 when a Claude stream closes with zero bytes (#12398) ([#13285](https://github.com/diegosouzapw/OmniRoute/pull/13285)) +- fix(oauth): warn before the dead localhost:8080 redirect in antigravity/gemini `oauth start` (#12413) ([#13265](https://github.com/diegosouzapw/OmniRoute/pull/13265)) +- fix(providers): stop devin-cli spawn error from double-closing the SSE controller (#12517) ([#13261](https://github.com/diegosouzapw/OmniRoute/pull/13261)) +- **fix(rerank):** clamp Voyage `top_k` to the documents actually sent after empty-string filtering, and honor `return_documents: false` in the NVIDIA response adapter (#12523 — thanks @pacocartones) +- **fix(audio):** `/v1/audio/translations` now resolves combo names the way `/v1/audio/transcriptions` already does, so a combo that `GET /v1/models` advertises is fanned out to its targets instead of being rejected with `400 Invalid translation model: . Use format: provider/model`; literal `provider/model` ids and unknown bare names behave as before (#12536 — thanks @pacocartones) +- **fix(gemini):** strip the JSON-Schema-2020-12 `prefixItems` keyword from Gemini tool schemas at every nesting level, so Claude Code tool definitions no longer fail with `400 Unknown name "prefixItems"` on Gemini models (#12540 — thanks @pacocartones) +- **fix(api-manager):** Expose an accessible loading status while API keys are fetched instead of an empty accessibility tree (#12541 — thanks @pacocartones) +- **fix(video):** Clamp `estimateJpegFrameBytes` at zero for padding-only payloads and build the three encode-side frame data URIs from `JPEG_FRAME_DATA_URI_PREFIX` instead of a repeated literal (#12543 — thanks @pacocartones) +- **fix(devin):** accept Windows `DEVIN_AGENTIC_HOME` sandbox paths (`C:\...\.sandbox\...`) in the isolated-home check so the Devin Claude Bridge no longer fails closed on Windows ([#12405](https://github.com/diegosouzapw/OmniRoute/issues/12405)) (#12545 — thanks @pacocartones) +- **fix(db):** Add `conversation_turn_nodes` and orphaned `agentic_conversations` to the auto-cleanup cycle under the existing `retention.callLogs` window, so identity nodes whose call-log content has already been purged no longer accumulate without bound in `storage.sqlite` (#12548 — thanks @pacocartones) +- **fix(i18n):** Wrap the `~/.claude/profiles//settings.json` placeholder in ICU single quotes in the `OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES` feature-flag description across all 42 locales and the TypeScript default, so next-intl no longer fails with `INVALID_MESSAGE: UNCLOSED_TAG` and the Feature Flags card shows the description instead of the raw key (#12549 — thanks @pacocartones) +- **fix(orchestration):** `updateCloudAgentTask` now publishes the task's real `status` on `agent.task.updated` when an update only touches `result`, `activities` or `error`, instead of the fabricated `"updated"` state, and stays silent when no row matched the id (#12550 — thanks @pacocartones) +- **fix(i18n):** the home "Recent Requests" panel and the Provider Topology legend are now translated instead of rendering English copies on non-English dashboards; the legend reads its own `home.topologyLegend*` labels with consistent casing rather than borrowing the memory-settings "Recent" and analytics "Error" strings (#12551 — thanks @pacocartones). +- **docs(reference):** bring the `FEATURE_FLAGS.md` catalog back to 1:1 with `featureFlagDefinitions.ts` — 20 missing flags added, the two `*_BLOCK_THRESHOLD` env-only knobs moved out of the flag tables, category/total counts and the Live WS port corrected, guarded by a static test (#12552 — thanks @pacocartones) +- **fix(i18n):** backfill missing `usage.kiloPass*` strings in 39 locales and restore `featureFlagOmnirouteDisableThinkingLevelVariantsDescription` in `pt.json` ([#12561](https://github.com/diegosouzapw/OmniRoute/issues/12561)) ([#13027](https://github.com/diegosouzapw/OmniRoute/pull/13027)) — thanks @HouMinXi +- fix(docker): default docker-compose app ports (dashboard/API/live-WS) to loopback instead of `0.0.0.0`, closing the anonymous `/v1` LAN/WAN exposure gap left open by `REQUIRE_API_KEY=false` (#12568) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): close DNS-rebinding SSRF gap in webhook outbound-URL guard (#12569) ([#13243](https://github.com/diegosouzapw/OmniRoute/pull/13243)) +- fix(docker): require a per-session token on the VNC browser CDP bridge and isolate it on a dedicated Docker network (#12571) ([#13241](https://github.com/diegosouzapw/OmniRoute/pull/13241)) +- fix(open-sse): write Adobe Firefly session tokens and cookie jars with 0700/0600 permissions instead of the process umask (#12572) ([#13242](https://github.com/diegosouzapw/OmniRoute/pull/13242)) +- fix(api): remove hardcoded wildcard CORS in openai-to-gemini-sse.ts so the centralized fail-closed CORS gate is the sole source of `Access-Control-Allow-Origin` (#12573) ([#13239](https://github.com/diegosouzapw/OmniRoute/pull/13239)) +- fix(api): enforce API key policy (budget/rate-limit/schedule/endpoint scoping) on the ElevenLabs speech-to-text, text-to-speech and voices proxy routes (#12574) ([#13244](https://github.com/diegosouzapw/OmniRoute/pull/13244)) +- fix(sse): cap HuggingChat NDJSON body size and bound the read loop with the fetch timeout so a stalled or hostile upstream cannot buffer unbounded memory (#12577) ([#13282](https://github.com/diegosouzapw/OmniRoute/pull/13282)) +- fix(docker): scope the cliproxyapi/qdrant/bifrost sidecars to loopback by default and forward `CLIPROXYAPI_MANAGEMENT_KEY` into the cliproxyapi container so its management API is not left both unauthenticated and LAN-published (#12578) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): create DB export temp paths with `fs.mkdtempSync` instead of predictable timestamps (#12579) ([#13237](https://github.com/diegosouzapw/OmniRoute/pull/13237)) +- Cline 401 bodies that say "re-authenticate your Cline account" classify as a refreshable OAuth token, not a terminal expired key. The cooling panel no longer labels every cooldown as a 429; it shows the recorded last error instead. (#12594) ([#13060](https://github.com/diegosouzapw/OmniRoute/pull/13060)) — thanks @HouMinXi +- **fix(catalog):** degrade unknown combo targets instead of dropping LCD modalities, and persist OpenRouter `architecture.input_modalities` into the capability snapshot ([#12613](https://github.com/diegosouzapw/OmniRoute/issues/12613)) ([#13042](https://github.com/diegosouzapw/OmniRoute/pull/13042)) — thanks @HouMinXi +- fix(providers): send `x-api-key` instead of `Authorization: Bearer` for OpenCode Zen's `/v1/responses` endpoint (Muse Spark Contributor models), fixing a 401 on OmniRoute's auth header (#12633) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(sse): surface the actionable "Auggie CLI not found" message when the shell reports a missing `auggie` binary via exit code instead of a spawn error (#12645) ([#13263](https://github.com/diegosouzapw/OmniRoute/pull/13263)) +- **fix(config):** Persist deletions of built-in background-degradation entries — when a stored settings record exists its `degradationMap` is now authoritative instead of being merged under the defaults, so an entry the user removed in the dashboard no longer reappears on the next apply or restart ([#12424](https://github.com/diegosouzapw/OmniRoute/issues/12424)) ([#12647](https://github.com/diegosouzapw/OmniRoute/pull/12647)) — thanks @pacocartones +- **fix(gamification):** action-count badge milestones (First Token, Token Consumer, Token Machine, Token Whale, and the token-sharing tier) are now backed by a durable `xp_action_counts` counter incremented in `addXp()`, instead of a live `COUNT(*)` over `xp_audit_log`. The audit log is pruned by `retention.xpAuditLog` (default 30 days), so on a default install those "lifetime" milestones were really "actions in the last 30 days" and unlocked badges could stop unlocking once old rows aged out. `getActionCount()` and `checkActionCountBadges()` now read the same durable source, and a migration backfills existing totals from the surviving audit rows ([#12546](https://github.com/diegosouzapw/OmniRoute/issues/12546)) ([#12651](https://github.com/diegosouzapw/OmniRoute/pull/12651)) — thanks @pacocartones +- **fix(images):** `/v1/images/edits` now iterates a combo's targets the same way `/v1/images/generations` does (#9239) instead of flattening a bare combo to its first target. A combo whose first target is not edit-capable — or lacks credentials — now falls through to a later edit-capable target rather than hard-erroring, and missing credentials are skipped (not a hard `401`) to match the generations path. The per-target skip/terminal classification is extracted into a shared `runImageComboTargets` loop, so generations behavior is unchanged ([#12547](https://github.com/diegosouzapw/OmniRoute/issues/12547)). ([#12653](https://github.com/diegosouzapw/OmniRoute/pull/12653)) — thanks @pacocartones +- fix(sse): add first-byte watchdog to the TLS-fingerprint transport so a stalled wreq body falls back instead of hanging for minutes (#12656) ([#13272](https://github.com/diegosouzapw/OmniRoute/pull/13272)) +- fix(sse): exempt tiny-budget reasoning probes from combo quality failure and surface persisted-cooldown skips in ALL_TARGETS_SKIPPED diagnostics (#12659) ([#13269](https://github.com/diegosouzapw/OmniRoute/pull/13269)) +- fix(models): declare the real ~1M contextLength for OpenCode Zen's Muse Spark 1.2 models instead of falling back to the 200000 provider default (#12681) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(oauth): align codebuddy-cn OAuth User-Agent with the chat/usage CLI version to avoid WAF false positives (#12702) ([#13264](https://github.com/diegosouzapw/OmniRoute/pull/13264)) +- fix(dashboard): surface an authentication-required banner instead of silently blanking database settings for a guest session (#12709) ([#13270](https://github.com/diegosouzapw/OmniRoute/pull/13270)) +- **fix(resilience):** stop unbounded queue that hangs 6min until Aborted — gate, provider slot, and Bottleneck queue now share a per-connection `maxWaitMs` budget; `fail-closed` on exhaust (503); execution backstop `executionMaxWaitMs` overridable per connection with upstream clamp ([#12715](https://github.com/diegosouzapw/OmniRoute/pull/12715)) — thanks @maxmad64bis (with thanks to @Tushar49 for surfacing the slow-provider need in #12635) +- **fix(auto-combo):** every mode pack now carries `quality` and `reliability` weights (`quality` 0.02, 0.03 in `quality-first`; `reliability` 0.03, 0.04 in `reliability-first`), so selecting a pack no longer silences either signal; `DEFAULT_WEIGHTS` is unchanged ([#12731](https://github.com/diegosouzapw/OmniRoute/pull/12731)) — thanks @maxmad64bis +- **fix(sse):** the provider execution pipeline keeps the upstream error `code`/`type` again — both were lost when the non-streaming leg was extracted, so a config-class `422` (Antigravity missing project) degraded into a generic account cooldown instead of reaching the gate that recognises it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(db):** a legacy `call_logs` table no longer aborts startup — the provider-stats index is created after the column healing runs, not before it, so an install predating `request_type` opens instead of failing with `no such column` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(catalog):** a malformed row in the operator-writable custom-models blob no longer kills every `auto/*` pool ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(tests):** realign the guards that the same day's merges left asserting the old behavior — the provider-count total after GreenPT and EURouter, the GLM stream buffer slot that `#12925` turned into a declared parameter, and the injection scan bound that `#13104` reshaped from a head-only window into head-plus-tail ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- fix(cache): fold tool_choice/tools/response_format into the semantic cache signature so a cached tool_calls response can no longer be replayed for a request whose tool policy forbids it (#12734) ([#13267](https://github.com/diegosouzapw/OmniRoute/pull/13267)) +- **fix(models):** stop treating provider-supplied `isFree:true`/`:free`/`0/0` flags as trusted unless the provider has a documented free tier — only the shipped free-tier catalog decides otherwise; fetched `isFree:true` trusted only for a free-tier provider, custom `isFree:true` via a trusted path ([#12744](https://github.com/diegosouzapw/OmniRoute/pull/12744)) — thanks @maxmad64bis +- fix(memory): authenticate the internal /v1/rerank loopback call so memory reranking no longer silently degrades to unranked order when REQUIRE_API_KEY=true (#12745) ([#13268](https://github.com/diegosouzapw/OmniRoute/pull/13268)) +- fix(sse): parse Ollama Cloud's current usage markup (`$X of $Y used` aria-label, nested width style) (#12749) ([#13260](https://github.com/diegosouzapw/OmniRoute/pull/13260)) +- fix(cli): setup-opencode no longer sends an active context's management token to `/v1/models` when `--api-key`/`OMNIROUTE_API_KEY` is supplied — an explicit flag or the env var now always outranks the context's token, and the flag itself is no longer swallowed by the parent program's global `--api-key` option (#12783) ([#13246](https://github.com/diegosouzapw/OmniRoute/pull/13246)) +- fix(sse): register Arcee AI in the executor provider registry so requests reach api.arcee.ai instead of silently falling back to OpenAI (#12784) ([#13277](https://github.com/diegosouzapw/OmniRoute/pull/13277)) +- **fix(quota):** share one upstream quota read when identical saturation checks arrive at the same time, so traffic bursts don't multiply provider API calls ([#12787](https://github.com/diegosouzapw/OmniRoute/pull/12787)) — thanks @maxmad64bis +- **fix(db):** ignore expired or invalid rate-limit cooldown writes so a stale timestamp can't lock a connection that should be usable — clearing still works as before ([#12788](https://github.com/diegosouzapw/OmniRoute/pull/12788)) — thanks @maxmad64bis +- **fix(providers):** single source for provider order with `xao` ranking alongside `xai-oauth` ([#12790](https://github.com/diegosouzapw/OmniRoute/pull/12790)) — thanks @maxmad64bis +- **fix(routing):** off-table models check the free-model catalog before inheriting premium prices, latency bootstraps from the observed pool median, async tiers read live database pricing with a 90-day freshness gate, and the tier cache invalidates on every pricing write ([#12792](https://github.com/diegosouzapw/OmniRoute/pull/12792)) — thanks @maxmad64bis +- **fix(auto-combo):** snapshot scoring uses the observed per-connection account tier and per-model quality instead of neutral constants, and circuit-open providers rank lower at build time ([#12794](https://github.com/diegosouzapw/OmniRoute/pull/12794)) — thanks @maxmad64bis +- **fix(pool):** empty `auto/*` pools now say why they are empty, and the models listing uses the same paid check as routing ([#12795](https://github.com/diegosouzapw/OmniRoute/pull/12795)) — thanks @maxmad64bis +- fix(routing): recognize CLIProxyAPI's 'unknown provider for model' 400 as fallback-worthy (#12800) ([#13284](https://github.com/diegosouzapw/OmniRoute/pull/13284)) +- **fix(sse):** translate-mode streams now emit the estimated token counts as a trailing usage-only chunk before `[DONE]` when the upstream stays silent, so metered chat clients see totals instead of nothing ([#12828](https://github.com/diegosouzapw/OmniRoute/pull/12828)) — thanks @maxmad64bis +- **fix(db):** provider stats stay truthful on empty and legacy databases: fallback counts default to `0` instead of `null`, latency averages read `null` (not `0`) when no durations were recorded, failures logged before the error-type column existed group under `pre_migration` instead of `unclassified`, and per-provider queries use two new composite indexes ([#12832](https://github.com/diegosouzapw/OmniRoute/pull/12832)) — thanks @maxmad64bis +- fix(nvidia): fail open when a synced model catalog goes stale instead of gating forever (#12849) ([#13248](https://github.com/diegosouzapw/OmniRoute/pull/13248)) +- **fix(db):** WAL maintenance lives in its own module: a `TRUNCATE` checkpoint that hits a busy database now warns with its streak and retries once via `PASSIVE` instead of logging success, and closing-time checkpoints no longer report success on builds without a database file, with the busy totals visible in the authenticated monitoring health payload ([#12853](https://github.com/diegosouzapw/OmniRoute/pull/12853)) — thanks @maxmad64bis +- **fix(health):** quota with no snapshots reads empty instead of a contradicting 0% ([#12857](https://github.com/diegosouzapw/OmniRoute/pull/12857)) — thanks @maxmad64bis +- fix(a2a): accept the dashboard's own session cookie on /a2a so "Run message/send" no longer fails with "Unauthorized: missing or invalid API key" (#12888) ([#13271](https://github.com/diegosouzapw/OmniRoute/pull/13271)) +- **fix(logs):** the Logs grid's in-memory filter pass no longer discards rows the SQL query already matched — selecting an API key from the dropdown (which sends the key's id) returns its calls again, the Combo tab shows every combo instead of only those whose name contains a "1", and the model filter and search cover the same columns as the query ([#12896](https://github.com/diegosouzapw/OmniRoute/pull/12896)) — fixes [#12873](https://github.com/diegosouzapw/OmniRoute/issues/12873) — thanks @ntdatt812 +- **fix(a2a):** `/api/a2a/status` now builds the agent card from the request that asked for it, so a gateway reached at a non-localhost host no longer advertises `http://localhost:20128` as its A2A URL ([#12918](https://github.com/diegosouzapw/OmniRoute/pull/12918)). — thanks @ntdatt812 +- **fix(compression):** progressive aging now appends its `[COMPRESSED:aging:…]` annotation after a turn's `tool_result` blocks instead of in front of them, so Anthropic no longer rejects aged conversations with "`tool_use` ids were found without `tool_result` blocks immediately after" ([#12920](https://github.com/diegosouzapw/OmniRoute/pull/12920)). — thanks @ntdatt812 +- **fix(bedrock):** model import now resolves context limits for every vendor prefix instead of only `anthropic.*`, so `global.openai.gpt-5.6-*` no longer imports with a null `inputTokenLimit` and gets rejected pre-flight at the 200k default ([#12921](https://github.com/diegosouzapw/OmniRoute/pull/12921)). — thanks @ntdatt812 +- **fix(stream):** the 64 KB stream buffer GLM asks for is honoured instead of dropped, and the type error it caused no longer fails the API Route Typecheck gate on every open PR ([#12925](https://github.com/diegosouzapw/OmniRoute/pull/12925)) — thanks @ntdatt812 +- **fix(guardrails):** mask PII inside a `tool_result`'s nested content array, which the masker walked past while redacting its sibling block ([#12930](https://github.com/diegosouzapw/OmniRoute/pull/12930)) — thanks @ntdatt812 +- **fix(dashboard):** the Radar catalog table no longer leaves absent data unexplained — empty limits, unknown context windows, and unreported capabilities each explain themselves on hover, and a new check keeps it that way ([#12937](https://github.com/diegosouzapw/OmniRoute/pull/12937)) — thanks @maxmad64bis +- **fix(sse):** geo-blocked opencode requests rotate to the next account proxy instead of failing, so one refused egress no longer aborts the whole chain ([#12941](https://github.com/diegosouzapw/OmniRoute/pull/12941)) — thanks @maxmad64bis +- **fix(codex):** dashboard "clear cooldown" and the CAS recovery path now drop nested `codexScopeRateLimitedUntil` maps in the same write that nulls `rate_limited_until`, and fresh quota snapshots with headroom lift fallback-sourced scope cooldowns parked by quota preflight ([#12817](https://github.com/diegosouzapw/OmniRoute/issues/12817), [#12860](https://github.com/diegosouzapw/OmniRoute/issues/12860), [#12951](https://github.com/diegosouzapw/OmniRoute/pull/12951)) The dashboard reset-credit button (`consumeCodexResetCredit`) now exercises that same snapshot path after a successful redeem, so a filled quota bar is enough to unpark a leftover fallback Codex child without another manual clear. — thanks @HouMinXi +- **fix(db):** resolve `sql-wasm.wasm` across global npm install and hoisted layouts, ensuring OmniRoute can boot cleanly on Node 24 when native `better-sqlite3` is uncompiled. ([#13035](https://github.com/diegosouzapw/OmniRoute/pull/13035)) — thanks @HouMinXi +- **fix(api):** `DELETE /v1/batches/delete-completed` now sweeps only the calling API key's own completed batches (batches with no owner stay out of a key-scoped sweep on purpose), with an explicit instance-wide mode reserved for authenticated dashboard sessions, a 401 for a presented key that is unknown, revoked, deactivated, banned or expired (never falling through to the session branch), audit logging of every sweep, a sanitized 500 on failure, an owner-scoped file half (a key-scoped sweep never nulls a file another tenant owns) and an atomic sweep — chunked in 200-batch transactions in instance mode — so a mid-way error never leaves a batch pointing at a nulled file (GHSA-wvxc-jp3v-5mg5) ([#12969](https://github.com/diegosouzapw/OmniRoute/pull/12969)) +- **fix(combo):** quota-weighted routing stops drawing on an out-of-credit connection — a 402 now invalidates the stored quota snapshot instead of leaving its stale remaining percentage in place, and a snapshot older than 10 minutes no longer counts as confident headroom for the primary pool ([#12972](https://github.com/diegosouzapw/OmniRoute/pull/12972)) ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- **fix(vision):** Custom Models with "Vision capable" checked no longer have image requests swapped to `glm/glm-4.6v` when the client sends the advertised alias (`vllm/path/...`) or the bare path-shaped id — Vision Bridge now matches the stored override for all three id forms ([#12758](https://github.com/diegosouzapw/OmniRoute/issues/12758)) ([#12974](https://github.com/diegosouzapw/OmniRoute/pull/12974)) — thanks @HouMinXi +- **fix(sse):** transient opencode upstream failures rotate to the next account proxy instead of failing, so one flapping egress no longer aborts the whole chain ([#12975](https://github.com/diegosouzapw/OmniRoute/pull/12975)) — thanks @maxmad64bis +- **fix(azure):** Deployments from GPT-6 onward now send `max_completion_tokens` instead of `max_tokens`, which Azure rejects with HTTP 400. The rule matched a literal `gpt-5`, so each new generation arrived broken; it now matches the generation range, while `gpt-35-turbo` still keeps `max_tokens`. ([#13007](https://github.com/diegosouzapw/OmniRoute/pull/13007)) — thanks @ntdatt812 +- **fix(db):** add an opt-in self-restart circuit for sustained critical memory pressure, gate post-cleanup VACUUM behind a minimum freed-rows threshold, and checkpoint the SQLite WAL every 5 minutes with a size guard that escalates to TRUNCATE, so a growing WAL can no longer stall the event loop into a full outage. ([#13011](https://github.com/diegosouzapw/OmniRoute/pull/13011)) — thanks @HouMinXi +- **fix(auth):** an explicit connection pin may probe a quota-disabled row once and re-enable it on success ([#12874](https://github.com/diegosouzapw/OmniRoute/issues/12874)) ([#13017](https://github.com/diegosouzapw/OmniRoute/pull/13017)) — thanks @HouMinXi +- **fix(api):** thread `X-OmniRoute-Fallback-Attempts` through combo chat completions so streaming and non-streaming responses report how many prior legs were attempted ([#13038](https://github.com/diegosouzapw/OmniRoute/pull/13038)) — thanks @HouMinXi +- Restore provider failure classification and credential refresh on non-streaming requests: classify non-2xx failures to lock models on per-model quota exhaustion, update connection rate limits from headers and body, and pass credential refresh handlers to pipeline execution so 401 tokens can be refreshed and retried (#13043). ([#13069](https://github.com/diegosouzapw/OmniRoute/pull/13069)) — thanks @HouMinXi +- **fix(responses):** wrap forced-non-streaming web_search fallback JSON as Responses SSE so Codex still sees `response.completed` ([#13050](https://github.com/diegosouzapw/OmniRoute/pull/13050)) — thanks @HouMinXi +- **fix(antigravity):** Preserve upstream thought-token usage in normalized completion and reasoning token counts ([#13055](https://github.com/diegosouzapw/OmniRoute/pull/13055)) — thanks @pacocartones +- Fix a concurrency-slot leak in the DeepSeek PoW solver: a worker that failed to spawn (for example a missing worker script) never released its slot, so `MAX_CONCURRENT_WORKERS` failures disabled the solver until restart. ([#13097](https://github.com/diegosouzapw/OmniRoute/pull/13097)) — thanks @anhtahaylove +- **fix(acp):** bound the ACP session output buffers — `stdoutBuffer` and `stderrBuffer` now cap at 1 MiB keeping the most recent output behind a visible `[...output truncated...]` marker, and `stderrBuffer` is reset per prompt instead of accumulating for the lifetime of the session. ([#13100](https://github.com/diegosouzapw/OmniRoute/pull/13100)) — thanks @anhtahaylove +- **fix(acp):** release the `stdout`/`exit` listeners and the idle timer that a `sendPrompt` timeout used to leave attached to the `acpManager` singleton, and drop sessions that exited on their own from the session map instead of keeping them forever. ([#13096](https://github.com/diegosouzapw/OmniRoute/pull/13096)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection and PII scanners now read the text a `tool_result` block carries on `content` (string or nested block list), in messages and in system blocks, so tool output is judged by the same rules as user text ([#13101](https://github.com/diegosouzapw/OmniRoute/pull/13101)) — thanks @ntdatt812 +- **fix(gamification):** close the badge notification SSE stream when the request signal is already aborted before the stream starts — a client that disconnects while the route is still awaiting auth used to leave both the 2s unlock poll and the 15s heartbeat running for the lifetime of the process. ([#13106](https://github.com/diegosouzapw/OmniRoute/pull/13106)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection scan now spends its 16 KB budget on both ends of the request instead of the first 16 KB only, so `system`, `instructions`, `query`, `documents` and the newest turns are no longer hidden behind one long message ([#13104](https://github.com/diegosouzapw/OmniRoute/pull/13104)) — thanks @ntdatt812 +- **fix(dashboard):** expose the Volcano Ark console cookie on quota scraping and unwrap connect-error objects so the dashboard shows the upstream message ([#13107](https://github.com/diegosouzapw/OmniRoute/pull/13107)) — thanks @HouMinXi +- **fix(db):** release the `beforeExit`/`SIGINT`/`SIGTERM` handlers when a `node:sqlite` adapter closes, so a closed adapter and its database handle are no longer pinned to `process` for the lifetime of the run — the same treatment #7494 gave the sql.js adapter. ([#13109](https://github.com/diegosouzapw/OmniRoute/pull/13109)) — thanks @anhtahaylove +- **fix(translator):** `contentSchema` and `unevaluatedItems` are now treated as subschema positions by the tool-schema sanitizer, so a truncation placeholder in either is replaced with a permissive schema instead of being forwarded as a string ([#13110](https://github.com/diegosouzapw/OmniRoute/pull/13110)) — thanks @ntdatt812 +- **fix(cli-helper):** clear the `createLogStream` timeout on the abort path — `stop()` aborts the in-flight fetch and returned through the `signal.aborted` branch, which skipped `clearTimeout` and left an armed timer per stopped stream. The stream reader is now also cancelled when the read loop exits early. ([#13114](https://github.com/diegosouzapw/OmniRoute/pull/13114)) — thanks @anhtahaylove +- **fix(docs):** drop TLS-impersonation, MITM-decrypt, supply-chain attestation, and XOR-mask writeups from the public `/docs` catalog and Docker image. Files stay in git for engineers; operators who need them open the repo, not the website. ([#13136](https://github.com/diegosouzapw/OmniRoute/pull/13136)) — thanks @HouMinXi +- **fix(combo):** parse numeric-epoch `rate_limited_until` in the combo cooldown read path ([#13141](https://github.com/diegosouzapw/OmniRoute/pull/13141)) — thanks @maxmad64bis +- **fix(opencode):** both OpenCode plugins now reject a gateway address typed without `http://` at configuration time, instead of publishing every model with an api url no client can call, and the v2 plugin no longer publishes a model card whose api url is blank or relative ([#13142](https://github.com/diegosouzapw/OmniRoute/pull/13142)) — thanks @maxmad64bis +- **fix(providers):** lock opencode model on upstream 400 model-unavailable ([#13146](https://github.com/diegosouzapw/OmniRoute/pull/13146)) — thanks @maxmad64bis +- **fix(logging):** keep the provider exchange rather than the raw client bodies when a call log exceeds its size budget, and show that recovered payload in the request-detail panel instead of replacing it with the stored response body ([#13147](https://github.com/diegosouzapw/OmniRoute/pull/13147)) — thanks @maxmad64bis +- **fix(traffic-inspector):** the WebSocket route no longer leaks a traffic-buffer subscriber and a 30s ping timer when the client socket is already closed at handler time — listeners are attached before any resource is acquired, a destroyed socket bails out early, and the ping interval stops on a dead socket where `write()` never throws ([#13155](https://github.com/diegosouzapw/OmniRoute/pull/13155)) — thanks @anhtahaylove +- **fix(telegram):** bound the per-user API key cache in the Telegram chat proxy so a burst of distinct chat ids can no longer grow the process heap without limit ([#13165](https://github.com/diegosouzapw/OmniRoute/issues/13165)) ([#13166](https://github.com/diegosouzapw/OmniRoute/pull/13166)) — thanks @anhtahaylove +- **fix(stream):** cancel the upstream response body when the JSON-to-SSE sniff unwinds on a body timeout, so a stalled upstream no longer pins the connection ([#13169](https://github.com/diegosouzapw/OmniRoute/issues/13169)) ([#13171](https://github.com/diegosouzapw/OmniRoute/pull/13171)) — thanks @anhtahaylove +- **fix(telegram):** authenticate webhook deliveries with Telegram's `secret_token` so an unauthenticated caller can no longer mint API keys or spend upstream quota ([#13172](https://github.com/diegosouzapw/OmniRoute/issues/13172)) ([#13175](https://github.com/diegosouzapw/OmniRoute/pull/13175)) — thanks @anhtahaylove +- **fix(models):** give discoverable Gemini 3.8 Flash ids their own 65536 output spec so Antigravity no longer clamps them to 16384 ([#13195](https://github.com/diegosouzapw/OmniRoute/pull/13195)) — thanks @HouMinXi +- **fix(dashboard):** leftover catalog-removed provider rows (gemini-cli) can be listed and purged from the providers page ([#13067](https://github.com/diegosouzapw/OmniRoute/issues/13067)) ([#13197](https://github.com/diegosouzapw/OmniRoute/pull/13197)) — thanks @HouMinXi +- **fix(auth):** a dashboard session now requires the `authenticated: true` claim that login, OIDC and the session refresh already emit — a JWT merely signed with `JWT_SECRET` (for example the Cursor CLI passthrough token, which any API-key holder can obtain) no longer verifies as the `auth_token` cookie on any route, the WebSocket handshake or the live server; existing sessions keep working ([#13298](https://github.com/diegosouzapw/OmniRoute/issues/13298)) ([#13375](https://github.com/diegosouzapw/OmniRoute/pull/13375)) +- **fix(api):** `DELETE /v1/batches/delete-completed` now applies the caller's API-key policy (endpoint allowlist, schedule, usage cap, rate limit) like every other `/v1` route, commits the key-scoped sweep per 200-batch chunk instead of holding one write lock for the whole sweep, refuses to loop without progress, and rejects a scope that names both a key and `allTenants` ([#13684](https://github.com/diegosouzapw/OmniRoute/pull/13684)) +- Restore the API-route typecheck gate: the non-streaming leg result lost its discriminated-union narrowing after the server-owned tool loop reassignment, producing 13 new TS2339 diagnostics in `chatCore.ts`. ([#12963](https://github.com/diegosouzapw/OmniRoute/pull/12963)) +- Restore four non-streaming behaviours the server-owned tool loop refactor dropped: client aborts map to 499 with the fixed `Request aborted` message, an aborted request no longer logs a synthetic `clientResponse`, Claude prompt-cache telemetry is recorded again, and a body that cannot be canonicalized no longer throws when the tool loop is off. ([#12990](https://github.com/diegosouzapw/OmniRoute/pull/12990)) +- **fix(skills):** The CLI registry parser now reads positionals declared with `.addArgument()`, not only those written inline in `.command()`. `tunnel create [type]` was being published as `tunnel create`, so the agent-skills sync gate reported drift on every branch and regenerating would have deleted the argument. ([#13009](https://github.com/diegosouzapw/OmniRoute/pull/13009)) — thanks @ntdatt812 +- **fix(combos):** dashboard combo test uses a short prompt, serial probes, and a 60s timeout so reasoning models and rate-limited free pools do not fail the health check ([#13001](https://github.com/diegosouzapw/OmniRoute/pull/13001)) — thanks @HouMinXi +- fix(compression): terminate idle worker threads on eviction so long-running instances stop leaking OS threads and MessagePorts ([#13091](https://github.com/diegosouzapw/OmniRoute/pull/13091)) — thanks @anhtahaylove +- Redact Google API keys of any length in error bodies: the pattern required exactly 39 characters, so shorter or longer `AIza…` credentials passed through unredacted. ([#12964](https://github.com/diegosouzapw/OmniRoute/pull/12964)) +- **fix(grok-cli):** a 402 "Grok Build usage balance exhausted" parks that Grok login as out of credit (Grok Build CLI, grok.com cookie, and xAI OAuth share the weekly pool). Combo routing then tries the next login instead of locking the model for every account in the pool ([#13061](https://github.com/diegosouzapw/OmniRoute/pull/13061)) — thanks @HouMinXi +- fix(compression): spawn the LLMLingua worker with a file URL object so compression actually runs instead of silently failing open on Node ([#13093](https://github.com/diegosouzapw/OmniRoute/pull/13093)) — thanks @anhtahaylove +- **fix(test):** run the local `test` and `test:unit` scripts at concurrency 4 so a full-suite run no longer exhausts the machine's commit charge and kills unrelated processes ([#13187](https://github.com/diegosouzapw/OmniRoute/pull/13187)) — thanks @anhtahaylove +- **fix(monitoring):** `GET /api/monitoring/health` `credentialHealth` now includes a bounded `failedConnections` list (`connectionId`, `status`, sanitized `lastError`) when the probe-cache gauge `failed>0`, plus `source: probe-cache` and a cheap `staleDbNonOkCount` for sticky SQLite `test_status` on active rows. Documents that the live gauge is not `provider_connections.test_status`. ([#12876](https://github.com/diegosouzapw/OmniRoute/pull/12876)) — thanks @RaviTharuma +- **fix(resilience):** an openai-compatible multi-upstream gateway no longer marks the whole connection `credits_exhausted` when a single upstream (or the `/models` / representative-model health probe) returns 402. Quota failures stay model-scoped; true connection-wide auth failures are unchanged. Single-credential 402 key disable ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239)) is preserved. ([#12875](https://github.com/diegosouzapw/OmniRoute/pull/12875)) — thanks @RaviTharuma +- **fix(dashboard):** nine fixes on the Orchestration Canvas, all diagnosed in the Phase 2 reviews. A source that fails now keeps the timestamp of its FIRST failure instead of being re-stamped every poll — the stale line said "since the last poll" no matter how long the source had been down, and the churn also defeated the snapshot's stable identity (it serializes the source list), so the canvas re-rendered on every tick while anything was broken. A source that HAD data and only then started failing is flagged too: previously only a source with no node at all got the warning, so a source that went down mid-session kept a healthy-looking node forever. The rest are pointwise: clicking a filter chip cancels the pending search debounce (left armed, it fired ~300ms later and silently reverted the chip); the search input carries an accessible name; `?state=running, failed` parses like the unpadded form instead of dropping the padded value; the CSV toggle helper is defined once in `model/urlParams.ts` rather than twice; the agents tab tells "nothing running" apart from "the filter matched nothing", offering a clear-filters button instead of setup links that would be wrong advice there; edges stop emitting SMIL particles above 40 simultaneously active edges, keeping the colored stroke; and the drawer's error banner clears when a retried action succeeds. ([#12983](https://github.com/diegosouzapw/OmniRoute/pull/12983)) +- **fix(i18n):** the nine locales added with the EU-language batch (Greek, Estonian, Irish, Croatian, Lithuanian, Latvian, Maltese, Slovenian, Serbian) were missing the eleven Orchestration Canvas keys that Phase 3 introduced, so the compare-runs panel and the "no runs match these filters" empty state fell back to English in those languages (`deepMergeFallback` substitutes English for an absent key, so nothing rendered blank — it rendered untranslated). The coverage gate does not catch this: it enforces an 80% floor per locale, and eleven missing keys out of ~13,000 leaves coverage at 99.9%. Translated for real in each language, calibrated against the wording each file already uses for "run", "filter" and "skill". ([#13294](https://github.com/diegosouzapw/OmniRoute/pull/13294)) +- **fix(dashboard):** five follow-ups from the Phase 2 Orchestration Canvas review. The History drawer now shows the memory section for runs that already left the live TTL window: the persisted `memory_hits` event is parsed into `metadata.memoryHits` with the same defensive validation the drawer applies, and — because that event is observability rather than a state transition — it no longer leaks into the timeline, where it had been inheriting the task's state and rendering as a duplicate transition. The A2A memory recall runs against its own 1.5s deadline instead of inheriting the memory backend's 30s one; overshooting degrades exactly like any other recall failure (no hits, task proceeds), and the timer is cleared on both paths. Repeating a Conductor run carries its `requirements.cli`/`requirements.model` forward, so the new run is pinned to the same runner profile and model rather than drifting to whatever the fleet picks. A successful repeat from the Agents tab now focuses the run it created, instead of leaving the operator on the finished one. And the auth test for `POST /api/conductor/tasks` moved into the shared `ROUTES` array rather than restating the pattern. ([#12988](https://github.com/diegosouzapw/OmniRoute/pull/12988)) +- fix(plugins): stop leaking an exit listener per plugin hook timeout, which triggered MaxListenersExceededWarning on plugins that ignore SIGTERM ([#13092](https://github.com/diegosouzapw/OmniRoute/pull/13092)) — thanks @anhtahaylove +- **fix(validation):** Provider node edits no longer fail with a generic "Invalid request" when the optional daily-quota reset fields are left blank. The dashboard sends `dailyQuotaResetTimezone` and `dailyQuotaResetHour` as `null`, and only the hour accepted it. ([#13066](https://github.com/diegosouzapw/OmniRoute/issues/13066)) ([#13083](https://github.com/diegosouzapw/OmniRoute/pull/13083)) — thanks @ntdatt812 +- **fix(combo):** quota-aware expansion drops banned, inactive, missing, and wrong-provider connections before quota fetch or model dispatch; pins and allowlists stay selectors, not a bypass. Antigravity automatic exhaustion now requires a reported zero remaining, so a positive balance below 1% stays eligible. ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- Fix an intermittent failure in the quota-weighted routing test suite: scores are a function of `Date.now()`, so peers with identical quota scored microseconds apart never tied and swapped order. ([#12946](https://github.com/diegosouzapw/OmniRoute/pull/12946)) +- fix(volcengine): resolve quota window mapping gaps, safe multi-connection console binding, and response cookie sanitization ([#12950](https://github.com/diegosouzapw/OmniRoute/pull/12950)) — thanks @HouMinXi +- **fix(test):** resolve the WebDAV handler path with `fileURLToPath` so the suite's 37 WebDAV tests run on Windows instead of failing with a doubled `C:\C:\` drive prefix ([#13196](https://github.com/diegosouzapw/OmniRoute/pull/13196)) — thanks @anhtahaylove +- **fix(ui):** fall back from invalid AnySearch icon alias (direct commit `9587e07b69`) +- **fix(sse):** stop rebuilding a truncated summary from the collector's cap-dropped event array ([#12718](https://github.com/diegosouzapw/OmniRoute/pull/12718)) — thanks @hartmark +- **fix(conversations):** show a pending spinner for unresolved tool nodes instead of "(empty)" ([#12727](https://github.com/diegosouzapw/OmniRoute/pull/12727)) — thanks @hartmark +- **fix(streaming):** fail fast when an upstream stream produces only lifecycle/heartbeat events, never real content ([#12741](https://github.com/diegosouzapw/OmniRoute/pull/12741)) — thanks @hartmark +- **fix(responses-continuation):** bridge the write-in-flight window with an in-memory pending store ([#12854](https://github.com/diegosouzapw/OmniRoute/pull/12854)) — thanks @hartmark +- **fix(translator):** drop or map agent_message on Chat Completions fallback ([#12880](https://github.com/diegosouzapw/OmniRoute/pull/12880)) — thanks @RaviTharuma +- **fix(api):** page and stream-complete GET /v1/models for large catalogs ([#12882](https://github.com/diegosouzapw/OmniRoute/pull/12882)) — thanks @RaviTharuma +- **fix(quota):** align AUTH window labels with usage API and clarify 503 ([#12884](https://github.com/diegosouzapw/OmniRoute/pull/12884)) — thanks @RaviTharuma +- **fix(ci):** clear two base-reds on release/v3.8.51 — mutation-coverage gate + image-only-model guard ([#12945](https://github.com/diegosouzapw/OmniRoute/pull/12945)) +- **fix(api):** scope batch bulk-delete to the calling API key ([#13211](https://github.com/diegosouzapw/OmniRoute/pull/13211)) +- **fix(tests):** align model-sync log assertion to shipped path redaction (base-red) ([#13227](https://github.com/diegosouzapw/OmniRoute/pull/13227)) +- **fix(test):** make the ToS heading guard actually require the parentheses ([#13228](https://github.com/diegosouzapw/OmniRoute/pull/13228)) +- **fix(quality):** register 4 drifted covering tests in stryker tap.testFiles ([#13229](https://github.com/diegosouzapw/OmniRoute/pull/13229)) +- **fix(logging):** mask *-api-key headers in request-log pipeline (#13273) ([#13401](https://github.com/diegosouzapw/OmniRoute/pull/13401)) — thanks @KooshaPari +- **fix(tests):** correct stale Jina catalog prefix in custom-models test ([#13403](https://github.com/diegosouzapw/OmniRoute/pull/13403)) — thanks @KooshaPari +- **fix(backend):** prune old backups after health-check-repair snapshots (#13308) ([#13404](https://github.com/diegosouzapw/OmniRoute/pull/13404)) — thanks @KooshaPari +- **fix(backend):** guard memory_fts_au trigger with WHEN clause to stop FTS bloat (#13326) ([#13405](https://github.com/diegosouzapw/OmniRoute/pull/13405)) — thanks @KooshaPari +- **fix(free-tiers):** remove obsolete Together $25 signup credit (#12526) ([#13407](https://github.com/diegosouzapw/OmniRoute/pull/13407)) — thanks @KooshaPari +- **fix:** skip non-chat generation models in Test all models ([#13409](https://github.com/diegosouzapw/OmniRoute/pull/13409)) — thanks @KooshaPari +- **fix(providers):** de-duplicate agy and antigravity model catalogs ([#13410](https://github.com/diegosouzapw/OmniRoute/pull/13410)) — thanks @KooshaPari +- **fix(dashboard):** send provider in body for mini-playground web search ([#13411](https://github.com/diegosouzapw/OmniRoute/pull/13411)) — thanks @KooshaPari +- **fix(resilience):** add CJK quota-exhaustion patterns to 429 classifier ([#13412](https://github.com/diegosouzapw/OmniRoute/pull/13412)) — thanks @KooshaPari +- **fix(backend):** eval runner: failed calls no longer score as passed + dotAll regex ([#13413](https://github.com/diegosouzapw/OmniRoute/pull/13413)) — thanks @KooshaPari +- **fix(backend):** eval runner opts out of output-style and memory injection ([#13414](https://github.com/diegosouzapw/OmniRoute/pull/13414)) — thanks @KooshaPari +- **fix:** chain serial quarantine step in npm test script (fixes #13286) ([#13418](https://github.com/diegosouzapw/OmniRoute/pull/13418)) — thanks @KooshaPari +- **fix(antigravity):** derive stable session ID from credentials for prompt-cache reuse (#13305) ([#13424](https://github.com/diegosouzapw/OmniRoute/pull/13424)) — thanks @KooshaPari +- **fix(memory):** merge memory into top-level system field instead of unshifting at messages[0] (#13425) ([#13427](https://github.com/diegosouzapw/OmniRoute/pull/13427)) — thanks @KooshaPari +- **fix(compression):** preserve instruction blocks from lossy engines ([#13523](https://github.com/diegosouzapw/OmniRoute/pull/13523)) — thanks @KooshaPari +- **fix(compression):** preserve polarity words and newlines in ultra heuristic ([#13524](https://github.com/diegosouzapw/OmniRoute/pull/13524)) — thanks @KooshaPari +- **fix(compression):** run user preservePatterns before built-in patterns (#13457) ([#13525](https://github.com/diegosouzapw/OmniRoute/pull/13525)) — thanks @KooshaPari +- **fix(reasoning):** expand deepseek-v4 regex to match v4.1+ and add command-code provider (#13430) ([#13528](https://github.com/diegosouzapw/OmniRoute/pull/13528)) — thanks @KooshaPari +- **fix(providers):** add alwaysReasons metadata for reka-flash-3 ([#13531](https://github.com/diegosouzapw/OmniRoute/pull/13531)) — thanks @KooshaPari +- **fix(playground):** allow Compare columns to scroll independently ([#13532](https://github.com/diegosouzapw/OmniRoute/pull/13532)) — thanks @KooshaPari +- **fix(security):** empty whitelist allows through to prevent admin lockout ([#13534](https://github.com/diegosouzapw/OmniRoute/pull/13534)) — thanks @KooshaPari +- **fix(tests):** add DISABLE_IOREG_STRATEGY env var for macOS test compatibility (fixes #13467) ([#13539](https://github.com/diegosouzapw/OmniRoute/pull/13539)) — thanks @KooshaPari +- **fix(zed-hosted):** map developer role and Gemini enums for Zed proxy ([#13543](https://github.com/diegosouzapw/OmniRoute/pull/13543)) — thanks @KooshaPari +- **fix(analytics):** normalize provider aliases to canonical id in usage_history ([#13545](https://github.com/diegosouzapw/OmniRoute/pull/13545)) — thanks @KooshaPari +- **fix(combo):** use traceId for call log id to prevent UNIQUE constraint failure ([#13546](https://github.com/diegosouzapw/OmniRoute/pull/13546)) — thanks @KooshaPari +- **fix(auth):** check model lockout before returning synthetic noauth connection ([#13547](https://github.com/diegosouzapw/OmniRoute/pull/13547)) — thanks @KooshaPari +- **fix(guardrails):** skip credential redaction for base64 image data URLs ([#13550](https://github.com/diegosouzapw/OmniRoute/pull/13550)) — thanks @KooshaPari +- **fix(resilience):** increase requestQueue.maxWaitMs default from 15s to 30s ([#13553](https://github.com/diegosouzapw/OmniRoute/pull/13553)) — thanks @KooshaPari +- **fix(docs):** document OMNIROUTE_READY_TIMEOUT_MS and allowlist the test-only DISABLE_IOREG_STRATEGY ([#13692](https://github.com/diegosouzapw/OmniRoute/pull/13692)) ### 📝 Maintenance @@ -767,6 +968,39 @@ _By commits in `release/v3.8.50..release/v3.8.51` (`091589089c..d6f315018a`), au - **docs(security):** document socket.yml scanner config + CI workflow link (#12575) ([#12764](https://github.com/diegosouzapw/OmniRoute/pull/12764)) — thanks @KooshaPari - **chore(ci):** guard commit identity in pre-commit to stop author misattribution ([#12772](https://github.com/diegosouzapw/OmniRoute/pull/12772)) - **deps:** 17 Dependabot bumps — bump electron from 43.4.0 to 43.4.1 in /electron ([#11426](https://github.com/diegosouzapw/OmniRoute/pull/11426)); bump the development group across 1 directory with 8 updates ([#11428](https://github.com/diegosouzapw/OmniRoute/pull/11428)); bump the production group across 1 directory with 11 updates ([#11440](https://github.com/diegosouzapw/OmniRoute/pull/11440)); chore(deps): bump github/codeql-action/init from 4.37.7 to 4.37.8 ([#11925](https://github.com/diegosouzapw/OmniRoute/pull/11925)); chore(deps): bump github/codeql-action from 4.37.7 to 4.37.8 ([#11926](https://github.com/diegosouzapw/OmniRoute/pull/11926)); chore(deps): bump github/codeql-action/analyze from 4.37.7 to 4.37.8 ([#11927](https://github.com/diegosouzapw/OmniRoute/pull/11927)); build(deps): bump github/codeql-action/init from 4.37.8 to 4.37.9 ([#12345](https://github.com/diegosouzapw/OmniRoute/pull/12345)); build(deps): bump github/codeql-action/analyze from 4.37.8 to 4.37.9 ([#12346](https://github.com/diegosouzapw/OmniRoute/pull/12346)); bump the development group across 1 directory with 2 updates ([#12347](https://github.com/diegosouzapw/OmniRoute/pull/12347)); build(deps): bump github/codeql-action from 4.37.8 to 4.37.9 ([#12349](https://github.com/diegosouzapw/OmniRoute/pull/12349)); bump browserslist from 4.28.2 to 4.28.8 ([#12396](https://github.com/diegosouzapw/OmniRoute/pull/12396)); bump the production group across 1 directory with 4 updates ([#12399](https://github.com/diegosouzapw/OmniRoute/pull/12399)); bump @xmldom/xmldom ([#12500](https://github.com/diegosouzapw/OmniRoute/pull/12500)); bump qs from 6.15.2 to 6.16.0 ([#12512](https://github.com/diegosouzapw/OmniRoute/pull/12512)); bump @xmldom/xmldom from 0.9.10 to 0.9.12 ([#12513](https://github.com/diegosouzapw/OmniRoute/pull/12513)); bump fast-uri from 3.1.5 to 3.1.7 ([#12514](https://github.com/diegosouzapw/OmniRoute/pull/12514)); bump @humanfs/node from 0.16.7 to 0.16.8 ([#12515](https://github.com/diegosouzapw/OmniRoute/pull/12515)) +- Clear the `release/v3.8.51` `check:agent-skills-sync` base-red: regenerate `skills/cli-tunnel/SKILL.md` so the `tunnel create [type]` positional that #13009 taught the generator to read is reflected in the committed skill. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` typecheck base-red from #12731: the new `Mode pack ranking gates` candidates in `open-sse/services/autoCombo/__tests__/autoCombo.test.ts` omitted the required `provider`, `model` and `errorRate` fields of `ProviderCandidate`, failing `check:open-sse-typecheck` (and with it `Fast Quality Gates`) on every open PR. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` docs-sync base-red: EURouter (#13025) and GreenPT (#13024) took the live provider count to 358, leaving 7 STRICT drifts (`PROVIDER_REFERENCE.md`, 4 diagrams, 2 tier-flow images) plus the `AGENTS.md` / `llm.txt` / `package.json` count claims stale. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- **test(models):** Pin `CATALOG_BUILD_TIMEOUT_MS` in the #12058 canonical-catalog regression so a cold catalog build racing the 8s default bound no longer flakes the suite with a 500 ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Allow `@omniroute/opencode-plugin-v2/` in the npm pack policy — [#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870) shipped the v2 plugin beside its v1 sibling without widening the allowlist, so every packed file under it read as an unexpected artifact and failed `check:pack-policy` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Point the release-green validator's pack gate at the tree it is validating — the build-provenance guard defaulted to `origin/main`, which a release branch tip can never be an ancestor of mid-cycle, so `Package artifact` reported a false red on every run and the tarball boot-smoke cascaded off it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **docs(checks):** keep doc counts honest — headings, rankings, catalog, weights, quality gate and scoring diagram now covered ([#12507](https://github.com/diegosouzapw/OmniRoute/pull/12507)) — thanks @maxmad64bis +- **chore(lifecycle):** `check:model-lifecycle` now also diffs `DEFAULT_DEGRADATION_MAP` (the background-task redirect table) against the vendor lifecycle snapshot, refusing a retired id as source or target, with a table-driven unit test beside it. Three rows whose source the vendor had retired — `claude-sonnet-4-20250514`, `gemini-3-pro-preview` and `gpt-5.1-codex` (whose target `gpt-5.1-codex-mini` is retired too) — were dead code, since `checkLifecycle` answers 410 before the redirect runs; they are dropped (#12535 — thanks @pacocartones) +- **test(compression):** cover idle worker eviction at the resource level — the pool must call `terminate()` and must not retain the worker's `MessagePort`, complementing the `exit`-event assertion added with the fix ([#13371](https://github.com/diegosouzapw/OmniRoute/pull/13371)) +- **chore(quality):** raise the `imageGeneration.ts` file-size ceiling for the image-only-model guard that clears the #12945 base-red ([#13392](https://github.com/diegosouzapw/OmniRoute/pull/13392)) +- **chore(quality):** type the combo-test route's JSON response bodies instead of casting them to `any`, drop the now-empty suppression entry, and remove the `hasPerModelQuota` import `chatCore.ts` stopped using when the failure-classification helper was extracted ([#13379](https://github.com/diegosouzapw/OmniRoute/pull/13379)) +- **chore(electron):** the desktop bundle no longer ships the root-level files of every translated docs mirror (`README.md`, `llm.txt`, `CLAUDE.md`, `GEMINI.md`, `CONTRIBUTING.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`) — the packaged app only reads `docs/i18n//docs/**`, which stays. Saves ~11 MB on top of the translated CHANGELOGs already pruned. (#0000) ([#13235](https://github.com/diegosouzapw/OmniRoute/pull/13235)) +- **chore(quality):** retighten the `src/sse/handlers/chat.ts` unused-vars suppression count to the 8 violations that actually remain ([#13385](https://github.com/diegosouzapw/OmniRoute/pull/13385)) +- Bring the documented counts back in line with the code: 171 migrations (was 169) and 20 routing strategies (was 19), plus the regenerated `cli-tunnel` skill reference. ([#12970](https://github.com/diegosouzapw/OmniRoute/pull/12970)) +- Update the documented migration count to 172 after the call-logs provider-stats indexes landed. ([#13209](https://github.com/diegosouzapw/OmniRoute/pull/13209)) +- **chore(ci):** two gates that close the blind spots behind the exclusions above. `check:vitest-exclusions` requires every Vitest exclusion to name a tracking issue and to appear in `config/quality/vitest-exclusions.json` — the previous list grew to 62 files behind a comment pointing at an issue that had been closed for a month. `check-new-key-coverage` requires a key newly added to `en.json` to reach every locale; the existing coverage gate is a percentage floor per locale, so eleven absent keys out of ~13,000 left it at 99.9% while a whole feature shipped untranslated in nine languages. Both are diff-aware, so pre-existing debt stays frozen and neither needed a migration to turn on. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **build:** root layout no longer loads Inter from `next/font/google`, so a production image build does not need fonts.googleapis.com ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **docs(i18n):** bring the eight new `llm.txt` mirrors (km, kn, ml, my, ne, or, pa, si) up to the current provider and migration counts, so `check-docs-sync` stops rejecting every commit ([#13674](https://github.com/diegosouzapw/OmniRoute/pull/13674)) +- **refactor(ui):** flow surfaces (home topology, combo live studio, compression cockpit/waterfall/nodes, token-health badge) express state through the theme-aware `--orch-status-*` tokens instead of fixed dark-mode hex, so green/red/amber/grey stay legible in the light theme; dark mode is byte-identical. Categorical palettes (routing-strategy hues, compression-layer pills, provider brand colors) deliberately stay hex ([#12378](https://github.com/diegosouzapw/OmniRoute/issues/12378)) ([#13203](https://github.com/diegosouzapw/OmniRoute/pull/13203)) +- **chore(tests):** 51 test files that had been excluded from Vitest are running again, restoring roughly 350 assertions to the blocking suite. Each was measured individually first: of the 62 files parked behind the `// #8618 — pre-existing failure` comment, 51 pass against the current tree with no source change, so the exclusions had outlived the failures they were added for. The 11 that genuinely still fail stay excluded, but now point at a live tracker (#13204) rather than at #8618, which was closed in August while the list it tracked kept growing. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **chore(quality):** drop two ESLint suppression entries whose violations no longer exist, so `eslint --suppressions-location` stops rejecting every commit that touches the surrounding files ([#13384](https://github.com/diegosouzapw/OmniRoute/pull/13384)) +- **chore(release):** bring main's post-tag fixes into release/v3.8.51 — sync-back step 2/2 (direct commit `21c488f210`) +- **chore(deps):** patch toml and esbuild in the opencode plugin lockfiles ([#12965](https://github.com/diegosouzapw/OmniRoute/pull/12965)) +- **docs(changelog):** reconcile the v3.8.51 living section — fold 366 fragments, cover every cycle commit, credit every contributor ([#12971](https://github.com/diegosouzapw/OmniRoute/pull/12971)) +- **test(plugin):** wait for the v2 lazy-refresh tier instead of sleeping past it ([#12984](https://github.com/diegosouzapw/OmniRoute/pull/12984)) +- **chore(quality):** rebaseline chatCore.ts after the non-streaming regression fixes ([#13045](https://github.com/diegosouzapw/OmniRoute/pull/13045)) +- **docs(changelog):** add the missing bullet marker to the DeepSeek PoW fragment ([#13200](https://github.com/diegosouzapw/OmniRoute/pull/13200)) +- **chore(deps):** drain the Dependabot queue — 10 of 13 alerts ([#13213](https://github.com/diegosouzapw/OmniRoute/pull/13213)) +- **chore(ci):** make worktrees carry the hooks that enforce the identity gate ([#13236](https://github.com/diegosouzapw/OmniRoute/pull/13236)) +- **chore(deps):** pin csv-parse to ^7.0.2 via overrides (#13117) ([#13300](https://github.com/diegosouzapw/OmniRoute/pull/13300)) — thanks @KooshaPari +- **chore(deps):** bump hono from ^4.12.34 to ^4.13.7 (#13148) ([#13301](https://github.com/diegosouzapw/OmniRoute/pull/13301)) — thanks @KooshaPari +- **chore(deps):** pin joi to ^18.2.8 via overrides (#13085) ([#13302](https://github.com/diegosouzapw/OmniRoute/pull/13302)) — thanks @KooshaPari +- **docs(providers):** add Microsoft 365 Copilot (BizChat) provider guide (#12779) ([#13340](https://github.com/diegosouzapw/OmniRoute/pull/13340)) — thanks @KooshaPari ### 🙌 Contributors @@ -784,6 +1018,7 @@ Thanks to everyone whose work landed in v3.8.51: | [@amaleta](https://github.com/amaleta) | #11906, #11934 | | [@amartinawi](https://github.com/amartinawi) | #11575 | | [@AndrianBalanescu](https://github.com/AndrianBalanescu) | #11083, #11830, #11834, #11844 | +| [@anhtahaylove](https://github.com/anhtahaylove) | #13091, #13092, #13093, #13096, #13097, #13100, #13106, #13109, #13114, #13155, #13166, #13171, #13175, #13187, #13196 | | [@aniruddhaadak80](https://github.com/aniruddhaadak80) | #11760 | | [@app](https://github.com/app) | #12554 | | [@arminanton](https://github.com/arminanton) | #11461, #11513 | @@ -813,10 +1048,10 @@ Thanks to everyone whose work landed in v3.8.51: | [@gonisulaimann](https://github.com/gonisulaimann) | #12368, #12369, #12371 | | [@Gorillaz322](https://github.com/Gorillaz322) | #12207 | | [@groovecityJO](https://github.com/groovecityJO) | #12682 | -| [@hartmark](https://github.com/hartmark) | #11434, #11452, #11473, #11499, #11703, #11983, #11984, #11985, #11986, #11988, #11989, #11990, #11994, #12221, #12293, #12445, #12446, #12447, #12448, #12460, #12461, #12623, #12646, #12650, #12680 | +| [@hartmark](https://github.com/hartmark) | #11434, #11452, #11473, #11499, #11703, #11983, #11984, #11985, #11986, #11988, #11989, #11990, #11994, #12221, #12293, #12445, #12446, #12447, #12448, #12460, #12461, #12623, #12646, #12650, #12680, #12717, #12718, #12727, #12741, #12854 | | [@hizzt](https://github.com/hizzt) | #11894 | | [@hongnoul](https://github.com/hongnoul) | #11484 | -| [@HouMinXi](https://github.com/HouMinXi) | #11411, #11414, #11512, #11518, #11520, #11641, #11642, #11643, #11687, #11779, #11849, #11850, #11851, #11915, #11916, #11918, #11919, #11920, #12013, #12017, #12033, #12042, #12106, #12139, #12166, #12169, #12171, #12205, #12213, #12312, #12325, #12487, #12488, #12495, #12504, #12557, #12566, #12590, #12591, #12624, #12626, #12632, #12637, #12678, #12696, #12697, #12711, #12733, #12746, #12767, #12770, #12789, #12803, #12805, #12811, #12866, #12868, #12899, #12926, #12934 | +| [@HouMinXi](https://github.com/HouMinXi) | #11411, #11414, #11512, #11518, #11520, #11641, #11642, #11643, #11687, #11779, #11849, #11850, #11851, #11915, #11916, #11918, #11919, #11920, #12013, #12017, #12033, #12042, #12106, #12139, #12166, #12169, #12171, #12205, #12213, #12312, #12325, #12487, #12488, #12495, #12504, #12557, #12566, #12590, #12591, #12624, #12626, #12632, #12637, #12678, #12696, #12697, #12711, #12733, #12746, #12767, #12770, #12789, #12803, #12805, #12811, #12866, #12868, #12899, #12926, #12934, #12950, #12951, #12974, #13001, #13006, #13011, #13017, #13026, #13027, #13034, #13035, #13038, #13042, #13050, #13060, #13061, #13069, #13107, #13120, #13136, #13195, #13197 | | [@Hsia97](https://github.com/Hsia97) | #11624 | | [@jacobsparts](https://github.com/jacobsparts) | #11854, #12155, #12167 | | [@joglomedia](https://github.com/joglomedia) | #11980 | @@ -826,11 +1061,11 @@ Thanks to everyone whose work landed in v3.8.51: | [@KaspaPulse](https://github.com/KaspaPulse) | #11389, #11469, #11628, #11666, #11910 | | [@keeltrace](https://github.com/keeltrace) | #12080, #12223 | | [@killer30001000](https://github.com/killer30001000) | #12178 | -| [@KooshaPari](https://github.com/KooshaPari) | #12470, #12592, #12667, #12699, #12703, #12706, #12764, #12769, #12771 | +| [@KooshaPari](https://github.com/KooshaPari) | #12470, #12592, #12667, #12699, #12703, #12706, #12764, #12769, #12771, #13300, #13301, #13302, #13340, #13401, #13403, #13404, #13405, #13406, #13407, #13409, #13410, #13411, #13412, #13413, #13414, #13418, #13424, #13427, #13433, #13523, #13524, #13525, #13528, #13531, #13532, #13534, #13539, #13543, #13545, #13546, #13547, #13550, #13553 | | [@kriptoburak](https://github.com/kriptoburak) | #11370 | | [@KrzysiekSko](https://github.com/KrzysiekSko) | #12425, #12432, #12673 | | [@marcelokarval](https://github.com/marcelokarval) | direct commit / report | -| [@maxmad64bis](https://github.com/maxmad64bis) | #1622, #11435, #11437, #11441, #11537, #11550, #11553, #11555, #11557, #11812, #11842, #11843, #11903, #12151, #12214, #12215, #12218, #12226, #12314, #12316, #12317, #12318, #12319, #12320, #12321, #12870 | +| [@maxmad64bis](https://github.com/maxmad64bis) | #1622, #11435, #11437, #11441, #11537, #11550, #11553, #11555, #11557, #11812, #11842, #11843, #11903, #12151, #12214, #12215, #12218, #12226, #12314, #12316, #12317, #12318, #12319, #12320, #12321, #12507, #12715, #12731, #12744, #12786, #12787, #12788, #12790, #12792, #12794, #12795, #12828, #12832, #12853, #12857, #12870, #12937, #12941, #12975, #13141, #13142, #13146, #13147 | | [@mdigitalbh81](https://github.com/mdigitalbh81) | #12240 | | [@Meet6338-X](https://github.com/Meet6338-X) | #11598, #11609, #12110 | | [@morpheus9393](https://github.com/morpheus9393) | #11943 | @@ -839,11 +1074,11 @@ Thanks to everyone whose work landed in v3.8.51: | [@NightStalker-87](https://github.com/NightStalker-87) | #12183 | | [@NoxzRCW](https://github.com/NoxzRCW) | #11879, #11880, #11881, #11882, #11883, #11935 | | [@ntdat812](https://github.com/ntdat812) | #11585, #12095 | -| [@ntdatt812](https://github.com/ntdatt812) | #11368, #11573, #11574, #11576, #11577, #11580, #11582, #11583, #11588, #11589, #11590, #11591, #11592, #11593, #11672, #12177, #12180 | +| [@ntdatt812](https://github.com/ntdatt812) | #11368, #11573, #11574, #11576, #11577, #11580, #11582, #11583, #11588, #11589, #11590, #11591, #11592, #11593, #11672, #12177, #12180, #12873, #12918, #12920, #12921, #12925, #12930, #13007, #13009, #13024, #13025, #13083, #13087, #13101, #13104, #13110 | | [@official-burak](https://github.com/official-burak) | #11542 | | [@opensource-elearning](https://github.com/opensource-elearning) | #12179, #12189, #12278, #12286 | | [@oyi77](https://github.com/oyi77) | #11408, #11409, #11421, #11505, #11677, #12036, #12110 | -| [@pacocartones](https://github.com/pacocartones) | #11521, #11522, #11527, #11528, #11529, #11530, #11531, #11532, #11533, #11534, #11595, #11599, #11603, #11604, #11605, #11607, #11610, #11676, #11714, #11716, #11718, #11767, #11838, #11860, #11862, #11869, #11871, #11872, #11873, #11903, #11906, #11921, #11934, #12359, #12360, #12361, #12362, #12364, #12365, #12373, #12374, #12375, #12376, #12377, #12379, #12380, #12381, #12386, #12387, #12389, #12390, #12394, #12395, #12397, #12401, #12403, #12404 | +| [@pacocartones](https://github.com/pacocartones) | #11521, #11522, #11527, #11528, #11529, #11530, #11531, #11532, #11533, #11534, #11595, #11599, #11603, #11604, #11605, #11607, #11610, #11676, #11714, #11716, #11718, #11767, #11838, #11860, #11862, #11869, #11871, #11872, #11873, #11903, #11906, #11921, #11934, #12356, #12358, #12359, #12360, #12361, #12362, #12364, #12365, #12373, #12374, #12375, #12376, #12377, #12379, #12380, #12381, #12386, #12387, #12389, #12390, #12394, #12395, #12397, #12401, #12403, #12404, #12522, #12523, #12535, #12536, #12540, #12541, #12543, #12545, #12548, #12549, #12550, #12551, #12552, #12647, #12651, #12653, #13055 | | [@patrykkopycinski](https://github.com/patrykkopycinski) | #11936, #11937, #12224 | | [@PauloFH](https://github.com/PauloFH) | #11509 | | [@PauloHSOliveira](https://github.com/PauloHSOliveira) | #12241 | @@ -855,7 +1090,7 @@ Thanks to everyone whose work landed in v3.8.51: | [@raheemuddin786](https://github.com/raheemuddin786) | #11491, #11839, #11840, #11841, #12003, #12230, #12231, #12232, #12233, #12234 | | [@rahilmavani](https://github.com/rahilmavani) | #11761 | | [@Rahulsharma0810](https://github.com/Rahulsharma0810) | #11771 | -| [@RaviTharuma](https://github.com/RaviTharuma) | #11710, #11727, #11797, #11798, #11802, #11805, #11806, #11811, #12098, #12099, #12101, #12449, #12452, #12472, #12473, #12493, #12533, #12607, #12628, #12631, #12636 | +| [@RaviTharuma](https://github.com/RaviTharuma) | #11710, #11727, #11797, #11798, #11802, #11805, #11806, #11811, #12098, #12099, #12101, #12449, #12452, #12472, #12473, #12493, #12533, #12607, #12628, #12631, #12636, #12875, #12876, #12880, #12882, #12884 | | [@rezjalibd](https://github.com/rezjalibd) | #12186 | | [@RhianB14](https://github.com/RhianB14) | #11692 | | [@rifqiawl](https://github.com/rifqiawl) | #11517, #11519, #11969 | diff --git a/docs/i18n/pt-BR/CHANGELOG.md b/docs/i18n/pt-BR/CHANGELOG.md index 43392bb1f3..d68e804ec3 100644 --- a/docs/i18n/pt-BR/CHANGELOG.md +++ b/docs/i18n/pt-BR/CHANGELOG.md @@ -8,16 +8,1119 @@ ## [3.8.51] — TBD -_Living section — cycle opened at the v3.8.50 freeze (parallel-cycle model). Bullets are aggregated from `changelog.d/` fragments at each `/generate-release` phase._ +_Living section — reconciled 2026-09-15 from all cycle commits (`091589089c` → `c0f92ec98a`, 916 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 | **123** | +| 📝 Commits in the cycle | **916** | +| 🔀 Pull requests referenced | **892** | +| 📋 Changelog entries | **919** | +| 🙌 Contributors credited in entries | **121** | +| 🤖 Automated dependency commits | 18 | + +**Entries by type** + +| Type | Count | +| --- | ---: | +| 🐛 Fixes | 592 | +| ✨ Features | 131 | +| 🧹 Chore | 93 | +| 📚 Docs | 42 | +| 🧪 Tests | 32 | +| ♻️ Refactor | 8 | +| 🏗️ Build | 7 | +| ⚡ Performance | 7 | +| ⚙️ CI | 2 | +| ⏪ Reverts | 2 | +| 📦 Dependencies | 2 | +| 🔒 Security | 1 | + +### 🏆 Top 25 contributors this cycle + +_By commits in `091589089c..c0f92ec98a`, author identities consolidated via `.mailmap` and the merged PR's GitHub login. Bots excluded._ + +| # | Contributor | Commits | +| ---: | --- | ---: | +| 🥇 | diegosouzapw | 323 | +| 🥈 | Bob.Hou (@HouMinXi) | 79 | +| 🥉 | Paco Cartones (@pacocartones) | 77 | +| 4 | Dizzle (@maxmad64bis) | 49 | +| 5 | Koosha Paridehpour (@KooshaPari) | 43 | +| 6 | Nguyen Thanh Dat (@ntdatt812) | 31 | +| 7 | Ravi Tharuma (@RaviTharuma) | 26 | +| 8 | Markus Hartung (@hartmark) | 22 | +| 9 | Webman (@jonlwheat2-gif) | 22 | +| 10 | backryun | 17 | +| 11 | anhtahaylove | 15 | +| 12 | Rafa Martins (@rafacpti23) | 10 | +| 13 | Syed Raheemuddin (@raheemuddin786) | 10 | +| 14 | Nguyễn Viết Tuấn (@TheDemonTuan) | 8 | +| 15 | MumuTW | 7 | +| 16 | Paijo (@oyi77) | 7 | +| 17 | Tobias Andersen (@turbolego) | 6 | +| 18 | Bl0ck (@Bl0ck154) | 5 | +| 19 | SHANMUGAPRIYAN (@geek007git) | 5 | +| 20 | KaspaPulse | 5 | +| 21 | Abhishek Sharma (@abhisheksharma2411) | 4 | +| 22 | Abhishek Divekar (@adivekar-utexas) | 4 | +| 23 | Andrew B. (@AndrianBalanescu) | 4 | +| 24 | NoxzRCW | 4 | +| 25 | opensource-elearning | 4 | ### ✨ New Features +- **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). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- Added Google AI Studio Gemini batch text-to-speech support through `POST /v1/audio/speech`. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- 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. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(routing):** subscription-first auto groupings — `auto/subscription` routes only through plan-included connections with a documented hard-stop overage and fails closed on exhaustion, while `auto/thrifty` orders the pool `subscription → keyless → free → cheap → premium` and steps up one rung at a time as each is exhausted. Billing class comes from a curated per-connection catalog (uncurated is treated as metered, never plan-included), both reuse STRICT_ZERO_COST's per-connection verification, and a quota reading whose `resetAt` has passed is now refreshed regardless of TTL so routing returns to plan capacity as soon as it resets ([#11146](https://github.com/diegosouzapw/OmniRoute/pull/11146)) — thanks @yourspraveen +- **feat(providers):** publish a management-authenticated versioned web-session credential contract from OmniRoute's canonical browser credential metadata ([#11340](https://github.com/diegosouzapw/OmniRoute/pull/11340)) — thanks @Zartharas +- **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)) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add Xquik X search with typed results, credential validation, REST routing, and MCP selection ([#11370](https://github.com/diegosouzapw/OmniRoute/pull/11370)) — thanks @kriptoburak +- **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)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(dashboard):** surface durable exclusive managed leases in the existing Sessions view, keeping leased clients visible across idle gaps while marking connections with in-flight work as active ([#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389)) — thanks @KaspaPulse +- **build(bun):** allow Turbopack bundler flag on Bun 1.4+ with configurable Webpack fallback ([#11471](https://github.com/diegosouzapw/OmniRoute/pull/11471)) — thanks @TheDemonTuan +- feat(api): add an opt-in `modelVisibilityAllowlist`/`modelVisibilityDenylist` settings pair to curate exactly which models `/v1/models` advertises, mirrored into every `auto/*` combo candidate pool so a denied model cannot be routed to via combo selection either (#11481) ([#11997](https://github.com/diegosouzapw/OmniRoute/pull/11997)) +- **feat(rankings):** the Free Provider Rankings page shows what each provider actually served over the last 24 h. It ranked by ELO alone, which left a provider that answers every call with an error in first place; the usage data was already served by the API but never requested. A provider with too small a sample shows a dash, not a number ([#11546](https://github.com/diegosouzapw/OmniRoute/pull/11546)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **feat(guardrails):** enforce a bounded, deterministic contract for Video Bridge transcripts — 256 cues, 4096 input code units and 4 KiB UTF-8 per cue, 64 KiB total text, malformed-Unicode rejection, focus-window scoping, cross-source reconciliation with contributing-source metadata, and a structural provenance trust boundary so caller JSON can never self-assert `embedded`/`audio-bridge` provenance ([#11652](https://github.com/diegosouzapw/OmniRoute/issues/11652)) ([#12009](https://github.com/diegosouzapw/OmniRoute/pull/12009)) +- **feat(video):** orchestrate optional Video Bridge audio extraction and Audio Bridge STT behind a dual opt-in (operator setting AND per-request signal) — a new loopback-only broker `mode=audio` operation shares the frame path's exact process queue, deadline, AbortSignal, and byte budgets to extract a bounded mono 16 kHz PCM WAV from the same already-downloaded video, then reuses the existing Audio Bridge transcription boundary; provider segment timing is preserved when available and marked coarse otherwise, and every failure degrades to a visual-only-safe partial instead of throwing (#11654). ([#12012](https://github.com/diegosouzapw/OmniRoute/pull/12012)) +- Add a tenant-bound Video Bridge drill-down lifecycle on top of the existing secure cache substrate: opaque hashed handles (never raw session/video identifiers), preview/standard/detail multiresolution variants resampled on read, response pagination capped at 8 frames and 32 MiB, and a new authenticated `/api/v1/video-bridge/drilldown` consumer route that stays disabled for remote access by default and denies cross-key access with the same response as a nonexistent handle (no existence oracle). ([#12006](https://github.com/diegosouzapw/OmniRoute/pull/12006)) +- **test(video):** Add the Video Bridge FU-07/FU-09 promotion-evidence harness (#11656) — a frozen Zod manifest schema covering the 8 required scenario kinds (static scenes, rapid cuts, late facts, fades, blur, small text, close events, visual prompt injection) with a minimum of 3 repetitions per case, deterministic declarative fixture recipes (`videoBridgePromotionFixtures.ts`), a pure medians/p95 metrics aggregator, a pure FU-07/FU-09 promotion-verdict evaluator applying the ticket's exact thresholds (missing token usage always holds), a digest-only persistence layer that never retains raw media or raw model responses, and a versioned per-model promotion allowlist shipped empty with every model defaulting to `hold`. The FU-07/FU-09 promotion verdicts themselves remain HOLD — they require a real evidence run against real models on VPS 192.168.0.15. ([#12008](https://github.com/diegosouzapw/OmniRoute/pull/12008)) +- **feat(video bridge):** "embedded" transcript provenance can now be legitimately earned instead of merely asserted — a bounded, allowlisted (`mov_text`/`subrip`/`webvtt`) subtitle probe runs through the loopback-only Video Bridge broker (at most 2 streams, 10s subdeadline bounded by the request deadline, 256 KiB output, 4096-code-unit lines), normalized through a bounded, ReDoS-safe WebVTT parser and Zod-validated end to end. The adapter always resolves to an explicit `success`/`absent`/`transient_failure` outcome — a subtitle failure never breaks the visual description path, and only a fingerprint-verified broker response (never a caller-declared label) can produce embedded cues (#11659). ([#12011](https://github.com/diegosouzapw/OmniRoute/pull/12011)) +- Default new Antigravity-family connections (agy CLI imports and Antigravity OAuth connects) to model auto-sync, so live model discovery lands in the synced catalog and `/v1/models` picks up freshly released upstream models (e.g. Gemini 3.7 Flash tiers) without code changes. Existing connections keep their current setting; the per-connection dashboard toggle remains the opt-out. (#11685 — thanks @MumuTW) ([#11887](https://github.com/diegosouzapw/OmniRoute/pull/11887)) +- **feat(ui):** enable React Compiler (`reactCompiler: true` + `babel-plugin-react-compiler`) for automatic memoization at build time ([#11783](https://github.com/diegosouzapw/OmniRoute/pull/11783)) — thanks @jonlwheat2-gif +- **feat(providers):** add SeekAi (`seekai.cc`) as an OpenAI-compatible New-API gateway — catalog id `seekai` (alias `ska`), `https://seekai.cc/v1`, live `/v1/models` via `passthroughModels`, aggregator-list membership so New-API balance detection can opt in. No referral/aff codes. ([#11786](https://github.com/diegosouzapw/OmniRoute/issues/11786)) ([#12557](https://github.com/diegosouzapw/OmniRoute/pull/12557)) — thanks @HouMinXi +- **feat(zai):** add GLM-5.3-Flash Coding Plan support (1M context, 128K output, vision, `low|high|max` reasoning) and route `zai` GLM-5.3-family API-key traffic through the OpenAI-compatible Coding Plan endpoint with native thinking defaults ([#11801](https://github.com/diegosouzapw/OmniRoute/pull/11801)) — thanks @Neuron-Mr-White +- **feat(combo):** choose how combo models are ordered — manual, provider, score, or name — via a sort control in the dashboard builder, persisted in `config.modelSort` and re-applied on load and after add ([#11812](https://github.com/diegosouzapw/OmniRoute/pull/11812)) — thanks @maxmad64bis +- **feat(free):** custom models can be marked free-tier via `customModels[].isFree`; `isFreeModel()` is the first door and `hidePaidModels` respects it even for providers outside the free budget ([#11843](https://github.com/diegosouzapw/OmniRoute/pull/11843)) — thanks @maxmad64bis +- **feat(nodejs):** add `5dive` as a `configure` target — `omniroute configure 5dive` / `omniroute setup-5dive` write a 5dive auth profile that points an agent fleet's `claude` seats at OmniRoute, with the root-only write, the loopback-vs-`https` endpoint rule and the per-seat model pin handled explicitly ([#11852](https://github.com/diegosouzapw/OmniRoute/pull/11852)) — thanks @5dive-bot +- **feat(sse):** treat `max` as a first-class reasoning-effort tier and clamp per model family (GLM 5.1+/DeepSeek V4+/Kimi K3+ keep native `max`; o1/MiniMax/Grok/Muse Spark clamp to their upstream ceiling) ([#11875](https://github.com/diegosouzapw/OmniRoute/pull/11875)) — thanks @Chewji9875 +- **feat(providers):** the provider plugin manifest now advertises a `usage-fetch` capability for the 40 providers that have a wired usage/quota fetcher, so external dashboards can read it from `GET /api/v1/provider-plugin-manifest` instead of parsing `open-sse/services/usage.ts` after every release. Discovery only — no new fetcher, no quota change, and the Dashboard quota widget stays gated by `USAGE_SUPPORTED_PROVIDERS`. `USAGE_FETCHER_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/fetcherProviders.ts`) and is re-exported from `services/usage.ts`, keeping the manifest module a light leaf instead of pulling the ~490-module usage dispatcher into the manifest route. ([#11903](https://github.com/diegosouzapw/OmniRoute/pull/11903)) — thanks @maxmad64bis / @pacocartones +- **feat(plugins):** `OMNIROUTE_PLUGINS_DIR` sets the directory the runtime plugin scanner reads — and the root the plugin manager installs into — overriding the `HOME`-derived default, so a Docker/K8s deployment can point straight at its bind-mounted plugin tree instead of moving `HOME` just to relocate the scan path. An image that exports no home no longer scans `/tmp/.omniroute/plugins` in silence: the resolved directory is logged once at startup as `scanner.dir_resolved`, naming the input that won. Unset, behaviour is unchanged. Distinct from the CLI-only `OMNIROUTE_PLUGIN_PATH`, which finds `omniroute-cmd-*` command packages and never reached this scanner ([#11906](https://github.com/diegosouzapw/OmniRoute/pull/11906)) — thanks @amaleta / @pacocartones +- **feat(leases):** add an explicit owner-authenticated status action that returns only the active lease's privacy-safe configured connection and provider labels, with generation fencing and no credential or internal-id disclosure ([#11910](https://github.com/diegosouzapw/OmniRoute/pull/11910)) — thanks @KaspaPulse +- **feat(providers):** import-from-file modal shows per-row API errors and ships a downloadable CSV template ([#12071](https://github.com/diegosouzapw/OmniRoute/issues/12071)) ([#12504](https://github.com/diegosouzapw/OmniRoute/pull/12504)) — thanks @HouMinXi +- **feat(providers):** dashboard search matches connection name and `baseUrl` so imported OpenAI-compat nodes surface on the provider card ([#12108](https://github.com/diegosouzapw/OmniRoute/issues/12108)) ([#12495](https://github.com/diegosouzapw/OmniRoute/pull/12495)) — thanks @HouMinXi +- **feat(routing):** add a `score` Auto router strategy that selects the highest configured weighted score and reuses `explorationRate`. ([#12155](https://github.com/diegosouzapw/OmniRoute/pull/12155)) — thanks @jacobsparts +- Add a runtime feature flag to disable universal context handoffs globally without changing the default behavior. ([#12167](https://github.com/diegosouzapw/OmniRoute/pull/12167)) — thanks @jacobsparts +- **feat(providers):** the provider plugin manifest now also advertises a `usage-supported` capability for the 46 providers whose usage API is accepted by the server and Dashboard routes, so integrators can distinguish "the server will serve quota for this provider" from "a fetcher is wired" without reading TypeScript. Discovery only — no fetcher or quota change. `usage-fetch` resolves on id or alias (the usage dispatcher accepts both); `usage-supported` resolves on id alone, matching the runtime guard `USAGE_SUPPORTED_PROVIDERS.includes(providerId)`. `USAGE_SUPPORTED_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/supportedProviders.ts`) and is re-exported from `providers.ts`, mirroring the `fetcherProviders` leaf from #11903 and keeping the manifest a light module. ([#12214](https://github.com/diegosouzapw/OmniRoute/pull/12214)) — thanks @maxmad64bis +- **feat(rankings):** order Free Provider Rankings by what each provider actually served — `GET /api/free-provider-rankings?sortBy=reliability` and a "Most reliable first" toggle on the page. Providers with too few calls to state a success rate keep their score order below the measured ones; the default order is unchanged ([#12218](https://github.com/diegosouzapw/OmniRoute/pull/12218)). — thanks @maxmad64bis +- **perf(sse):** defer `cloneLogPayload()` in the structured SSE collector until after the `maxEvents`/`maxBytes` cap check, eliminating ~9,800 wasted `structuredClone` calls per streaming response (65–71% faster `push()`). Reducer snapshot isolation restored for OpenAI and Responses summaries ([#12241](https://github.com/diegosouzapw/OmniRoute/pull/12241)) — thanks @PauloHSOliveira ([#12243](https://github.com/diegosouzapw/OmniRoute/pull/12243)) +- **feat(usage):** Devin CLI agentic quota (Codeium seat-management GetUserStatus) and OpenRouter key limits plus account credits now surface in Provider Limits ([#12256](https://github.com/diegosouzapw/OmniRoute/pull/12256) — thanks @Neuron-Mr-White) +- **feat(settings):** persist `headroomUrl` through Settings so status/start use the operator URL instead of only `HEADROOM_URL` ([#12306](https://github.com/diegosouzapw/OmniRoute/issues/12306)) ([#12487](https://github.com/diegosouzapw/OmniRoute/pull/12487)) — thanks @HouMinXi +- **feat(auto-combo):** Auto-Combo scoring can now weigh how often a provider/model has actually succeeded. The engine already carried that number on every candidate — 24 hours of usage history behind a ten-sample floor, real-time metrics otherwise — and the scoring function never read it, while the weight table described `stability` as if it did. `reliability` (`1 - failureRate`, with the same field precedence and the same rate-bounding the speed ranking already uses, so a corrupt reading means "nothing observed" rather than "fails every call") is now a declared factor shipping at weight `0`, so routing is unchanged until an operator gives it one, and the `stability` description now matches what that factor computes ([#12317](https://github.com/diegosouzapw/OmniRoute/pull/12317)) — thanks @maxmad64bis +- **feat(routing):** With `freeAccessPolicy: "strict"`, the read-only candidate listing (`GET /v1/auto-combo/{channel}/candidates`) no longer hides the candidates the zero-cost guard excludes — the same read-only transparency the resilience filter already honours (#9133). Each candidate now carries `freeAccessExclusion` saying why it would be kept out, and it tells an exhausted allowance apart from a quota reading that never arrived or went stale, which used to look identical from the outside. Routing is unchanged: the listing reports, it never enforces. The separate `excludeTosAvoid` guard still drops its candidates without a reason; that gap is now documented rather than closed ([#12319](https://github.com/diegosouzapw/OmniRoute/pull/12319)) — thanks @maxmad64bis +- **feat(radar):** The Radar catalog table now shows two facts it was already receiving from the feed and dropping on the floor: the per-model rate limits (requests and tokens, per minute and per day) in a new column, and a badge when a provider's terms state it may train on the prompts you send. A limit of zero renders as zero rather than "rate-only" — for a ceiling those are opposite facts — and a model with no training statement gets no badge, because an absent statement is not a guarantee ([#12320](https://github.com/diegosouzapw/OmniRoute/pull/12320)) — thanks @maxmad64bis +- **feat(radar):** explain Community, single-use, contributor, supporter, recovery, abuse, offers, and privacy rules before either Radar activation action, and remove the superseded fixed-PR grant promise from every UI locale ([#12342](https://github.com/diegosouzapw/OmniRoute/pull/12342)) +- **feat(gamification):** the dashboard Profile page now shows the real daily streak — `/api/gamification/level` returns `streak: { current, longest }` (per key with `apiKeyId`, operator-wide maximum otherwise) and the streak card reads it instead of a hard-coded 0 (#2403) ([#12377](https://github.com/diegosouzapw/OmniRoute/pull/12377)) — thanks @pacocartones +- **feat(gamification):** the dashboard leaderboard now shows each API key's display name under the Name column instead of a truncated key id; `GET /api/gamification/leaderboard` attaches `name` per entry (name only — no key material), while the shared ranking helper and the federation leaderboard stay id-only — thanks @pacocartones ([#12385](https://github.com/diegosouzapw/OmniRoute/pull/12385)) +- **feat(gamification):** enforce the documented 1000 XP/min per-API-key anti-cheat rate limit on the XP award path; over-limit awards are logged and skipped instead of persisted, and the sliding window now matches the timestamp format stored in `xp_audit_log` ([#2403](https://github.com/diegosouzapw/OmniRoute/issues/2403)) ([#12390](https://github.com/diegosouzapw/OmniRoute/pull/12390)) — thanks @pacocartones +- **feat(admin):** localize the gamification anomalies page — the loading state, the Status column and the Suspicious badge now come from the `common` catalog (new `common.suspicious` key propagated to every locale) — add it to the Gamification sidebar group as `gamification-admin` (`/dashboard/gamification/admin`), and expose the loading and empty states as polite `role="status"` live regions (#12401 — thanks @pacocartones) +- **feat(providers):** skip GitHub combo members missing from the live synced catalog, and drop Copilot models that are policy-disabled or hidden from the model picker ([#12473](https://github.com/diegosouzapw/OmniRoute/pull/12473)) — thanks @RaviTharuma +- **feat(i18n):** locale-expansion tooling — `npm run i18n:add-locale` adds a language to config, dashboard, docs mirrors, CLI, README/indexes and site in one command; browser, CLI and cookie detection resolve `uk`, `fil`/`tl`, `zh-Hant` (and the retired legacy `in` → `id`) via config aliases; `config/i18n.json` now ships in the npm package so the published CLI can read it; new real-translation ratio gate (`npm run i18n:check-ratio`, advisory) with a per-locale ratchet baseline; `run-translation --adopt` restores `.i18n-state.json`; `sync-language-bars` generates 🌐 bars from config; `validate_translation.py` loads its allowlist again. Retires the duplicate `in` locale (Indonesian mislabelled as Hindi) — 42 honest locales; saved `NEXT_LOCALE=in` / `OMNIROUTE_LANG=in` keep working. (#12496) +- **feat(api):** Emit gateway-measured `tokens_per_second` (TTFT excluded) on streaming usage and `X-OmniRoute-Tokens-Per-Second` when first-token latency is known ([#12616](https://github.com/diegosouzapw/OmniRoute/issues/12616)) ([#12631](https://github.com/diegosouzapw/OmniRoute/pull/12631)) — thanks @RaviTharuma +- **feat(opencode-plugin): map gateway cost/usage/tok/s onto OpenCode inference payloads** — the official plugin copies `X-OmniRoute-Response-Cost`, token counts, `X-OmniRoute-Tokens-Per-Second` / `usage.tokens_per_second`, TTFT, and the winning `X-OmniRoute-Model` onto the JSON/SSE body OpenCode already consumes. Missing tok/s is left unset (never `tokens / latency`). (#12636) — thanks @RaviTharuma +- **feat(combo):** add combo strategy `quota-weighted`: skip exhausted accounts, then weighted-draw by leftover / in-flight load; existing conversations stay pinned until the account is empty ([#12789](https://github.com/diegosouzapw/OmniRoute/pull/12789)) — thanks @HouMinXi +- **feat(models):** Account live listings become the chat catalog source for Claude, Codex, Copilot, and AGY; public metadata only fills prices on IDs those accounts already list. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **fix(models):** Union `agy` and `antigravity` live catalogs so `agy/gemini-3.8-flash-high` is not rejected after the prefix folds to `antigravity`. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **feat(opencode):** Add `@omniroute/opencode-plugin-v2`, an opencode v2 plugin that gives a v2 install a route to an OmniRoute gateway: it publishes models, combos and auto-combos into the host catalog, refreshes them lazily behind a 300s TTL, and keeps serving the last known catalog from an on-disk snapshot when the gateway is unreachable. The v1 plugin (`@omniroute/opencode-plugin`) is untouched and keeps working as before. The plugin adapts to the host instead of assuming one shape: it reads the shape the host seeds into the catalog draft and publishes the `api` block, the pre-`api` top-level fields, or both. The gateway key comes from the host credential store when one is connected, so no secret needs to sit in a config file; a source the gateway refuses is named in a warning instead of silently degrading the catalog to raw model ids; and tool schemas bound for a Gemini model are stripped of the JSON-Schema keywords Gemini rejects, so tool calling survives that route ([#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870)) — thanks @maxmad64bis +- feat(services): show sanitized CLIProxyAPI account health from its authenticated management API without exposing credentials, file paths, or raw account metadata (#6342) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add AnySearch free web search + URL extract (webFetch) with typed results, credential validation, REST routing, and MCP selection - fallback-only ([#11690](https://github.com/diegosouzapw/OmniRoute/pull/11690)) — thanks @Xxx91n +- **feat(dashboard):** display clamped `[0, 100]%` cached input token ratio in request logs table ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) — thanks @b3nw +- **feat(catalog):** add `OMNIROUTE_DISABLE_THINKING_LEVEL_VARIANTS` feature flag to optionally filter out thinking level variants from model catalog ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) — thanks @b3nw +- **feat(dashboard):** continuously export call logs to external analytics stores. A pluggable destination registry ships the full Logs-tab record set on an hourly `JobRegistry` cron, with a persisted per-destination cursor, batched inserts, a config UI rendered from each destination's own field descriptors, and a REST layer (`/api/log-export/*`) for CRUD, a connection test, and an on-demand run. A destination can opt into `includeBodies` to also ship the request and response payloads shown in the Logs detail pane, including the client and provider views of each call; this is off by default, and payloads inherit the dashboard's PII sanitisation, secret redaction and `noLog` handling. Google BigQuery is the first destination, using a service-account key stored encrypted at rest and streaming inserts keyed by call-log id, into a table that is day-partitioned on `timestamp` and clustered on `api_key_name`, `provider`, `model` and `status`. ([#11945](https://github.com/diegosouzapw/OmniRoute/pull/11945)) — thanks @dpozimski +- **feat(providers):** add MaxAI as a signed, OpenAI-compatible provider serving its 13 paid chat models (GPT-5.6 / Luna / Thinking, Claude 5 Sonnet, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite, Grok 4.1-fast / 4.5, DeepSeek V3.2 / R1, Llama 3.3 70B) through OmniRoute's `/v1` endpoint, with per-request HMAC-SHA1→SM3→AES request signing, live model + context-window discovery from `/models/get_config`, and prompted tool-calling translated to OpenAI `tool_calls` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI vision input — image_url content parts are forwarded inline in `message_content` to the 6 vision-capable models (GPT-5.6 / Luna / Thinking, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite) ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI image generation — 6 image models (gpt-image-1, dall-e-3, flux-1-schnell/dev/pro, sd3-medium) exposed through `POST /v1/images/generations` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI document RAG — inline base64 file/document attachments are uploaded to MaxAI (content-addressed `doc_id`) and attached to the chat via `doc_list` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** browserless MaxAI onboarding — email device-pair login (`/api/providers/[id]/login`) and signed access-token refresh, so a connection can be created and kept fresh without a real browser or Google OAuth ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** per-provider TLS impersonation profile (MaxAI presents a Windows Firefox-150 client fingerprint) so its bot-sensitive endpoints accept OmniRoute traffic ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** add **Nimble** as a web-search and web-fetch provider (`nimble-search`) — `/v1/search` routes to Nimble's search API at `lite` depth (locale, freshness and include/exclude domain filters mapped onto the shared request contract), and `/v1/web/fetch` routes to Nimble Extract, which covers all four fetch formats (`markdown`, `html`, `links`, `screenshot`) from a single call. One API key serves both surfaces. ([#11620](https://github.com/diegosouzapw/OmniRoute/pull/11620)) — thanks @wildcard +- The npm publish is automatic again, through npm Trusted Publishing (OIDC): the hosted `stage-npm` job publishes with a short-lived credential minted from GitHub's id-token — no `NPM_TOKEN`, no 2FA prompt, provenance attached. `publish_mode=staged` (owner approves with 2FA) and `direct` (token) remain available on `workflow_dispatch`. ([#11931](https://github.com/diegosouzapw/OmniRoute/pull/11931)) +- **feat(providers):** Add **Opper** as an API-key gateway provider — EU-hosted AI gateway with 700+ models from 30+ providers behind one OpenAI-compatible API and one key (`OPPER_API_KEY`); model ids use `provider/model` format (e.g. `anthropic/claude-sonnet-4-6`, `openai/gpt-5`); live model catalog at `https://api.opper.ai/v3/compat/models`; entry mirrors `requesty` (same shape, `passthroughModels: true`, no static seed) ([#11629](https://github.com/diegosouzapw/OmniRoute/pull/11629)) +- **feat(dashboard):** the `/dashboard/orchestration` snapshot hook now subscribes to the `agents` WebSocket channel (`agent.task.updated`) instead of `requests` as its refetch trigger, and relaxes its background poll from 5s to 30s while that WS connection is up — falling back to the tighter 5s cadence, reprogrammed live on any connect/disconnect transition, whenever the socket is down. ([#12409](https://github.com/diegosouzapw/OmniRoute/pull/12409)) +- **feat(dashboard):** Orchestration canvas quick wins — search box plus state/source/provider filter chips with a one-click clear, and per-source collapse/expand, all reflected in the URL so a filtered/collapsed view is shareable and survives a refresh; the detail drawer gained a "copy trace JSON" action and hardened error/empty-state and accessibility handling; the Agents-tab edges now animate traveling particles along active (running) connections; and the canvas node/edge status colors moved off fixed hex values onto theme-aware `--orch-status-*` CSS custom properties, so they adapt correctly to light/dark mode. ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- **chore(dashboard):** Orchestration UI hardening pass and the missing component/model test coverage it called for — `OrchestratorNode`/`ActivityNode`/`OverflowNode` rendering, the `?node=`/overflow-click page routing, the Agents-tab orchestrator-click no-op and `showCompleted` toggle, and the overview kanban's done-column sort order (#12270, #12271). ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- New `/dashboard/orchestration` page: live unified view of everything running — Cloud Agent, A2A and Conductor as a real-time graph (Agents tab), the combo cascade (Routing tab, reusing the Combo Live Studio) and a state kanban (Overview tab), with a detail drawer (trace, cost, approve/cancel). Read-only over existing APIs — no new backend. Canvas concept credit: PR #11815 design ([#12261](https://github.com/diegosouzapw/OmniRoute/pull/12261)) +- **feat(dashboard):** new "History" tab on `/dashboard/orchestration` — an Airflow-style grid of finished runs over a 24h/7d/30d preset window, one row per (source, identity), clicking a cell opens the existing detail drawer. It is backed by real persistence: A2A task lifecycle transitions are now written to the `a2a_tasks` table (purged after 30 days, configurable via `OMNIROUTE_A2A_HISTORY_RETENTION_DAYS`) and served by the new `GET /api/a2a/tasks/history` listing endpoint, with the task-detail route falling back to persisted history once a run leaves the in-memory snapshot. Conductor runs stay remote and are not persisted locally — the tab says so instead of silently omitting them. ([#12479](https://github.com/diegosouzapw/OmniRoute/pull/12479)) +- **feat(dashboard):** the orchestration detail drawer gained a "Repeat" action for Cloud Agent, A2A and Conductor tasks — a two-click confirm (click once to arm, click again within the confirm window to fire) re-submits the original prompt/input as a new run. The button is disabled with an explanatory tooltip whenever the original input can't be recovered from the loaded task detail (e.g. it never carried a prompt, or the detail failed to load). Two limitations of the A2A variant, by design: it targets the `/a2a` JSON-RPC endpoint, which authenticates with an API key only (`REQUIRE_API_KEY=true` or a configured `OMNIROUTE_API_KEY` makes a dashboard-session repeat answer `HTTP 400` — surfaced verbatim in the drawer's error line, never as a success), and the `message/send` call is SYNCHRONOUS: the POST blocks for the whole skill run, so the success confirmation only appears once the repeated task finishes. A dashboard-authenticated A2A creation path is deliberately left to a follow-up — widening the endpoint's auth posture is an operator decision, not a side effect of this feature. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(a2a):** A2A task execution now records which memories were consulted for the task's last user message as `metadata.memoryHits` (id/key/type/content-snippet) plus a `memory_hits` history event, purely for observability — the retrieved memory is never injected into a skill's prompt or behavior. Gated by the `OMNIROUTE_A2A_MEMORY_HITS` kill-switch (default enabled; set to `0` to skip the recall lookup entirely). The drawer's new "Memory used" section lists these hits for a2a tasks and is omitted whenever there are none. Known limitation: recall only resolves under the keyless posture — a keyed caller's task owner is a SHA-256 prefix of the API key, while memory rows are keyed by the database api-key id, and no hash→id lookup exists today, so the hit list stays empty for keyed callers. The recorded hits are also kept out of the task's own `input` (and therefore out of the persisted input and of the "Repeat" request body), so repeating a task never re-sends the previous run's memory snippets. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(providers):** add a Perplexity Agent API provider (`perplexity-agent` / `pplx-agent`) for Perplexity `/v1/responses`, including the documented Anthropic, OpenAI, Google, xAI, DeepSeek, Z.AI, Moonshot/Kimi, NVIDIA, and Perplexity model IDs plus Anthropic-model `max_output_tokens` compatibility. ([#12103](https://github.com/diegosouzapw/OmniRoute/pull/12103)) — thanks @AIB1TAL0S +- **feat(providers):** add RPD (Requests Per Day) limit to provider rate limit overrides across UI, schemas, DB, and i18n ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) — thanks @b3nw +- **feat(providers): add UC Direct (uncensored.com Developer API), the metered OpenAI-compatible surface.** A standard OpenAI-compatible passthrough (default executor) for uncensored.com's official REST API at `https://api.uncensored.com/api/v1`: `X-api-key` auth (never-expiring `uai_sk_live_` key), `POST /chat/completions` with streaming SSE and native tool-calling, and the full live metered catalog (82 models across 15 providers, discovered from the public `GET /v1/models`). Registered as provider `uc-direct` (alias `ucd`). Complements the un-metered `uc` persona provider — same models, metered credits and a plain API key instead of a subscription session. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(providers): add UC (uncensored.com), the un-metered subscription "persona" chat as an OpenAI-compatible provider.** A WebSocket web-app port: a durable Clerk credential mints a short-lived session token per connect (browserless — no API key), driving UC's persona socket. Ships the browserless email-code login (request → verify → harvest), the 19 verified persona models (Claude Opus, Gemini, Grok, GLM, Kimi, DeepSeek, MiniMax, incl. the uncensored variants), prompted `` tool-calling with a per-model code-style dialect + auto-cure retry for guardrailed models, live ``/reasoning split, streaming + non-streaming OpenAI responses, and full quota/auth error surfacing (paywall / message-limit / rate-limit → 429, invalid session → 401 re-login). Full multimodal parity via the persona blob-upload layer: **vision** (image input, 15 vision-capable models), **document RAG** (PDF/doc upload, server-side extraction), **image generation** (22 models), **video generation** (14 models, async signed-url → poll), and **TTS** (streaming MP3). Registered as provider `uc` (alias `ucn`). The metered OpenAI-compatible Developer API is a separate `uc-direct` provider. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(logs):** add copy-all button to request detail modal ([#11083](https://github.com/diegosouzapw/OmniRoute/pull/11083)) — thanks @AndrianBalanescu +- **feat(check):** make serviceKinds required and add the reverse-walk provider consistency gate ([#11392](https://github.com/diegosouzapw/OmniRoute/pull/11392)) — thanks @Tushar49 +- **feat(providers):** only advertise verified-working connections by default ([#11448](https://github.com/diegosouzapw/OmniRoute/pull/11448)) — thanks @alltomatos +- **feat(resilience):** add hierarchical concurrency admission ([#11493](https://github.com/diegosouzapw/OmniRoute/pull/11493)) +- **feat(providers):** derive + surface expiry for JWT-bearing web cookies (#11497) ([#11505](https://github.com/diegosouzapw/OmniRoute/pull/11505)) — thanks @oyi77 +- **feat(tunnels):** support named cloudflared tunnels via CLOUDFLARED_CONFIG ([#11561](https://github.com/diegosouzapw/OmniRoute/pull/11561)) — thanks @ggiak +- **feat(compression):** make proactive context-compression threshold a live setting ([#11564](https://github.com/diegosouzapw/OmniRoute/pull/11564)) — thanks @ragnar-claude +- **feat(resilience):** add provider peak-hour protection ([#11622](https://github.com/diegosouzapw/OmniRoute/pull/11622)) — thanks @Neuron-Mr-White +- **feat(providers):** add 1min.ai provider ([#11631](https://github.com/diegosouzapw/OmniRoute/pull/11631)) — thanks @zero-executioner +- **feat(dashboard):** collapsible JSON tree viewer for request/response payloads ([#11703](https://github.com/diegosouzapw/OmniRoute/pull/11703)) — thanks @hartmark +- **feat(routing):** add LiquidAI LFM2.5-2.6B free tier via OpenRouter ([#11752](https://github.com/diegosouzapw/OmniRoute/pull/11752)) — thanks @brick30llc-ctrl +- **feat(pricing):** add GLM-5.3-Flash pricing, model specs, and catalog registration ([#11830](https://github.com/diegosouzapw/OmniRoute/pull/11830)) — thanks @AndrianBalanescu +- **feat(routing):** add relayMode for schema-locked context handoffs ([#11839](https://github.com/diegosouzapw/OmniRoute/pull/11839)) — thanks @raheemuddin786 +- **feat(config):** add support for runtime system prompt configuration and hot-reloading ([#11841](https://github.com/diegosouzapw/OmniRoute/pull/11841)) — thanks @raheemuddin786 +- **feat(routing):** expand connection-aware quota prefilter across combo strategies (#11682) ([#11850](https://github.com/diegosouzapw/OmniRoute/pull/11850)) — thanks @HouMinXi +- **feat(models):** reactive discovery sync after Antigravity model-not-found 404 ([#11888](https://github.com/diegosouzapw/OmniRoute/pull/11888)) — thanks @MumuTW +- **feat(models):** auto-aliases and token limits from synced Antigravity discovery ([#11889](https://github.com/diegosouzapw/OmniRoute/pull/11889)) — thanks @MumuTW +- **feat(settings):** raise sticky round-robin limit caps to 1000 ([#12015](https://github.com/diegosouzapw/OmniRoute/pull/12015)) — thanks @adivekar-utexas +- **feat(resilience):** operator-configurable global credential health check interval ([#12043](https://github.com/diegosouzapw/OmniRoute/pull/12043)) — thanks @adivekar-utexas +- **feat(quota):** use official OpenCode Go usage API ([#12124](https://github.com/diegosouzapw/OmniRoute/pull/12124)) — thanks @ddarkr +- **feat(resilience):** default the credential health check sweep to 60 minutes ([#12138](https://github.com/diegosouzapw/OmniRoute/pull/12138)) — thanks @adivekar-utexas +- **feat(quality):** new-code mode for the complexity and dead-code ratchets (Clean as You Code) ([#12142](https://github.com/diegosouzapw/OmniRoute/pull/12142)) +- **feat(dashboard):** orchestration canvas — unified model + snapshot hook (part 1/2) ([#12156](https://github.com/diegosouzapw/OmniRoute/pull/12156)) +- **feat(usage):** add Kilo Code balance and Kilo Pass quotas ([#12178](https://github.com/diegosouzapw/OmniRoute/pull/12178)) — thanks @killer30001000 +- **feat(providers):** refresh vendored ChatGPT Web connector to v4.0.7 ([#12181](https://github.com/diegosouzapw/OmniRoute/pull/12181)) — thanks @backryun +- **feat(providers):** manual "Clear cooldown" action in the cooling panel ([#12224](https://github.com/diegosouzapw/OmniRoute/pull/12224)) — thanks @patrykkopycinski +- **feat(providers):** restore ChatGPT Web via clean-room browser transport ([#12239](https://github.com/diegosouzapw/OmniRoute/pull/12239)) — thanks @backryun +- **feat(sse):** wire the PROVIDER_PROFILES window gate into the global provider cooldown ([#12247](https://github.com/diegosouzapw/OmniRoute/pull/12247)) +- **feat(providers):** modernize CLOVA Studio chat and embeddings ([#12277](https://github.com/diegosouzapw/OmniRoute/pull/12277)) — thanks @backryun +- **feat(browser):** adopt Obscura as primary headless browser engine with Chromium fallback ([#12286](https://github.com/diegosouzapw/OmniRoute/pull/12286)) — thanks @opensource-elearning +- **feat(sse):** full language parity for output styles (es/de/fr/it/ru/zh + autoDetect) ([#12289](https://github.com/diegosouzapw/OmniRoute/pull/12289)) +- **feat(quality):** complete test:scoped — --full map rebuild, stdin selection, CI loader parity (#8084 D1) ([#12353](https://github.com/diegosouzapw/OmniRoute/pull/12353)) +- **feat(providers):** refresh Fable, Cursor, and Devin catalogs ([#12367](https://github.com/diegosouzapw/OmniRoute/pull/12367)) — thanks @backryun +- **feat(video):** redact transcript text from logs and durable memory (#12150 P1) ([#12427](https://github.com/diegosouzapw/OmniRoute/pull/12427)) +- **feat(dashboard):** parent-link, genuine-continuation badge, and modal perf fixes ([#12448](https://github.com/diegosouzapw/OmniRoute/pull/12448)) — thanks @hartmark +- **feat(dashboard):** adaptive context-budget dial on compression panel ([#12488](https://github.com/diegosouzapw/OmniRoute/pull/12488)) — thanks @HouMinXi +- **feat(providers):** refresh Z.ai Web models and browser transport ([#12524](https://github.com/diegosouzapw/OmniRoute/pull/12524)) — thanks @backryun +- **feat(video):** redact raw client-snapshot transcript fields in the detailed log (#12150 P2) ([#12528](https://github.com/diegosouzapw/OmniRoute/pull/12528)) +- **feat(providers):** refresh NVIDIA hosted models ([#12538](https://github.com/diegosouzapw/OmniRoute/pull/12538)) — thanks @backryun +- **feat(quota):** Moonshot Open Platform balance and TPD lock for custom nodes ([#12590](https://github.com/diegosouzapw/OmniRoute/pull/12590)) — thanks @HouMinXi +- **feat(video):** redact transcript in the in-memory pending-request snapshot (#12430 item 6) ([#12596](https://github.com/diegosouzapw/OmniRoute/pull/12596)) +- **feat(dashboard):** link a log entry's Conversation Context to its owning conversation ([#12646](https://github.com/diegosouzapw/OmniRoute/pull/12646)) — thanks @hartmark +- **feat(catalog):** eligibility-gated free-tier bucket ([#12669](https://github.com/diegosouzapw/OmniRoute/pull/12669)) +- **feat(db):** fail-closed previous_response_id continuation for redacted video turns (#12150 P2b) ([#12707](https://github.com/diegosouzapw/OmniRoute/pull/12707)) +- **feat(gamification): pay the documented `streak_bonus` and `badge_unlock` XP rewards.** `XP_REWARDS` listed both rewards but the award pipeline never paid them: the private reward table in `events.ts` omitted them, `updateStreak()` did not report when a streak extended, and badge unlocks carried no XP. Every request that extends a daily streak now pays `streak_bonus × streak length` once per UTC day (guarded by a same-day `xp_audit_log` check), and every badge unlocked through the pipeline pays `badge_unlock` once per badge (guarded by the `user_badges` primary key; `unlockBadge()` now reports whether it inserted). Bonus XP flows through the same `addXp` + level sync + global/weekly/monthly leaderboard path as action XP, so level-ups and rankings include it. The Radar supporter recognition unlock stays XP-free. (#12522 — thanks @pacocartones) +- **feat(providers):** advertise a `free-tier` capability in the provider plugin manifest for every provider with documented free models, so sidecars and dashboards can filter free-capable providers without reading the quota catalog ([#12786](https://github.com/diegosouzapw/OmniRoute/pull/12786)) — thanks @maxmad64bis +- **feat(providers):** Added EURouter as an OpenAI-compatible API-key gateway (`https://api.eurouter.ai/v1`), with live model discovery via `passthroughModels`. Its copy states that models are served by third-party upstreams listed per model, so an EU-based router is not read as EU data residency for inference. ([#13025](https://github.com/diegosouzapw/OmniRoute/pull/13025)) — thanks @ntdatt812 +- **feat(providers):** Added GreenPT as an OpenAI-compatible API-key provider (`https://api.greenpt.ai/v1`), with live model discovery via `passthroughModels`. No free-inference badge: the published docs describe a free API subscription billed per token, not a free tier. ([#13024](https://github.com/diegosouzapw/OmniRoute/pull/13024)) — thanks @ntdatt812 +- **feat(i18n):** 9 new locales — Greek (`el`), Croatian (`hr`), Serbian (`sr`), Lithuanian (`lt`), Estonian (`et`), Latvian (`lv`), Slovenian (`sl`), Maltese (`mt`), Irish (`ga`) — across the dashboard, docs mirrors, CLI, README and the site; OmniRoute now ships every official EU language (51 locales). (#13044) +- **feat(i18n):** 8 new locales — Kannada (`kn`), Malayalam (`ml`), Odia (`or`), Punjabi (`pa`), Nepali (`ne`), Sinhala (`si`), Burmese (`my`), Khmer (`km`) — across the dashboard, docs mirrors, CLI, README and the site (59 locales). The translator now restores the ICU literal escape around angle placeholders and splits oversized docs sections before translating. (#0000) ([#13660](https://github.com/diegosouzapw/OmniRoute/pull/13660)) +- feat(providers): **list Agnes 3.0 Flash as the current free chat model, drop retired 1.5 Flash, add Image 2.0/2.5 Flash plus Video 2.5/2.5 Flash, and discover the live `/v1/models` catalog (including the CN host `api.agnes-ai.cn`).** `agnes-1.5-flash` now forwards to `agnes-3.0-flash`. Video 2.5 polls `GET /v1/videos/{id}` (not the V2.0 `/agnesapi` contract). Live `/v1/models` (2026-09-09) no longer serves 1.5; the wiki marks it deprecated. 3.0 Flash is 512K context / 65,536 max output, same window as 2.5. CN-region keys use the existing per-connection base-URL field, default stays `apihub.agnes-ai.com`. ([#13120](https://github.com/diegosouzapw/OmniRoute/pull/13120)) — thanks @HouMinXi +- **feat(sse):** Codex and OpenAI catalogs list GPT-6 Astra with effort aliases (`-low` through `-ultra`); Codex CLI identity pins `@openai/codex@0.153.4` in lockstep with the image ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **feat(dashboard):** the orchestration History tab gained a "Compare runs" mode — toggling it turns each grid cell into a 2-item selection queue (a 3rd click drops the oldest pick), and picking two cells opens a side-by-side comparison panel instead of the usual detail drawer. The panel fetches both runs' detail the same way the drawer does (falling back to persisted history once a run leaves the live TTL window) and shows, per side: identity/source/state, start time, a signed `right - left` delta for duration/cost/event count, the event timeline aligned by index, and any memory hits. One side's fetch failing never blocks the other, and a delta is only ever computed when both sides have a finite value — otherwise it renders "—", never `NaN`. Comparing two runs from different sources or skills still works; a banner marks the deltas as informational rather than hiding them, since the two runs aren't a strict apples-to-apples pair. ([#12677](https://github.com/diegosouzapw/OmniRoute/pull/12677)) +- **feat(dashboard):** badge a conversation that never reached a clean stop ([#12717](https://github.com/diegosouzapw/OmniRoute/pull/12717)) — thanks @hartmark +- **feat(release):** reconcile-changelog tool + version-anchored fragment aggregation ([#12987](https://github.com/diegosouzapw/OmniRoute/pull/12987)) +- **feat(mcp):** carry context_length through MCP list_models_catalog projection (#12776) ([#13406](https://github.com/diegosouzapw/OmniRoute/pull/13406)) — thanks @KooshaPari +- **feat(cli):** make startup readiness budget configurable (#13369) ([#13433](https://github.com/diegosouzapw/OmniRoute/pull/13433)) — thanks @KooshaPari + ### 🐛 Bug Fixes +- **security(streaming):** sanitize generic mid-stream error messages before emitting OpenAI, + Responses, or Claude SSE failure frames and before diagnostic logging, while preserving raw + failures for internal classification and keeping client disconnects out of provider failure state. ([#12457](https://github.com/diegosouzapw/OmniRoute/pull/12457)) +- **fix(claude):** `blockExtraUsage: false` no longer lets 5h quota preflight skip the account; extra usage is billed after the session bar is gone, so the request must reach Anthropic ([#12803](https://github.com/diegosouzapw/OmniRoute/pull/12803)) — thanks @HouMinXi +- Sanitize HuggingChat conversation-creation and message-send transport failures before they reach client error bodies or provider logs. ([#12467](https://github.com/diegosouzapw/OmniRoute/pull/12467)) +- **fix(dashboard):** Keep local and theme-aware provider SVG icons at a definite layout size so Chromium does not collapse them to 0×0 after the v3.8.50 image-rendering change ([#12054](https://github.com/diegosouzapw/OmniRoute/pull/12054)) — thanks @ponkcore +- **fix(security):** Sanitize provider and runtime failures before public API, SSE and MCP responses and before persistent request, proxy and usage logs, preventing credentials, stack traces and host filesystem paths from crossing those boundaries while preserving stable error codes and useful diagnostics. ([#12506](https://github.com/diegosouzapw/OmniRoute/pull/12506)) +- **fix(cli):** `omniroute tunnel create` no longer crashes with `Cannot read properties of undefined (reading optsWithGlobals)` — removed the duplicate positional argument that caused Commander.js to misalign the action callback parameters ([#12295](https://github.com/diegosouzapw/OmniRoute/issues/12295)) ([#12368](https://github.com/diegosouzapw/OmniRoute/pull/12368)) — thanks @gonisulaimann +- Stop advertising Gemini Live-only models as supported audio endpoints until OmniRoute proxies the bidirectional Live protocol. ([#11710](https://github.com/diegosouzapw/OmniRoute/pull/11710)) — thanks @RaviTharuma +- **fix(kie):** reroute `flux/kontext` off the KIE Market `createTask` flow — it is catalogued with `isMarket: true` but has no Market catalog page, so KIE rejected it with "model name not supported"; it now hits the dedicated `POST /api/v1/flux/kontext/generate` / `GET /api/v1/flux/kontext/record-info` endpoints instead (#11296). ([#11985](https://github.com/diegosouzapw/OmniRoute/pull/11985)) — thanks @hartmark +- **fix(kie):** correct 12 more KIE Market catalog ids that were sent to `createTask` unchanged but diverge from KIE's documented upstream `model` values — GPT Image 2 T2I/I2I (drops the `gpt/` prefix), GPT Image 1.5 T2I/I2I (`gpt-image/` namespace), Seedream 5.0 Lite T2I/I2I (drops the `.0`), all 4 Flux 2 variants (`flux-2/` namespace, generic variant renamed `flex`), and Wan 2.7 Image / Image Pro (dash instead of dot) — each verified individually against the literal example request published on docs.kie.ai. `#11326`'s "everything else already matches" claim was wrong a second time (#11296); `z-image/4.0-*`/`z-image/4.5-*` and `flux/kontext` remain open, documented as unresolved in `KIE_MARKET_UPSTREAM_MODEL_IDS`'s comment pending further verification. ([#11667](https://github.com/diegosouzapw/OmniRoute/pull/11667)) +- **fix(dashboard):** `useApiKeySave.handleSaveApiKey` no longer forces a full upstream `/models` catalog sync on every non-curated provider connection save — callers can now pass `skipModelSync: true` to opt out, so a workflow that only wants to add one manual model no longer floods the provider's available-models list with hundreds/thousands of synced entries. The flag is a client-side intent signal only and is stripped before the connection payload is POSTed to `/api/providers`; default behavior (full sync on save) is unchanged when the flag is omitted (#11324) ([#11664](https://github.com/diegosouzapw/OmniRoute/pull/11664)) +- **Provider connections:** keep `tokenExpiresAt` when a connection is created. The create-path allowlist omitted it, so every insert stored NULL and the dashboard token badge could read a fresh connection as expired until its first background refresh ([#11368](https://github.com/diegosouzapw/OmniRoute/pull/11368)). — thanks @ntdatt812 +- **fix(combos):** the combo builder's precision-select, global-model-search, and manual-entry flows now serialize a model step's `model` string using the provider's already-computed routing-alias prefix (e.g. `oc/`) instead of rebuilding it from the raw canonical `providerId`, fixing the no-auth "OpenCode Free" provider (`opencode`) being routed to the unrelated paid "OpenCode Zen" provider (`opencode-zen`) because `opencode` doubles as a manual routing-prefix override ([#11433](https://github.com/diegosouzapw/OmniRoute/issues/11433)). ([#11670](https://github.com/diegosouzapw/OmniRoute/pull/11670)) +- **fix(radar):** the catalog feed cache now keeps `generatedAt`, the date the feed's data was built, next to `fetchedAt`, the date this install downloaded it (#11435). The feed schema requires that date and the sync path validates it, but the cache dropped it — so a feed fetched minutes ago and one carrying weeks-old figures looked identical to everything downstream, including the dashboard's "Last fetched" line. `getRadarCatalog().meta` and `GET /api/radar/status` now report both dates, the latter as its own field rather than folded into `version` — and omitted entirely for the offers and intel caches, which keep no build date, where a `null` would read as "unknown" rather than "never stored". The dashboard still shows only the fetch time; surfacing the build date there needs a new translated label and is left to a follow-up. Rows cached before migration 163 read back as `null`: unknown stays unknown instead of borrowing the fetch time. The referrals cache has persisted the same date since migration 142. — thanks @maxmad64bis +- **fix(cli):** postinstall no longer fills `JWT_SECRET` and `API_KEY_SECRET` in the installed package's `.env` (#11436). `.env.example` ships both blank on purpose: the server restores them from its durable store, or generates and persists them there, in `ensureSecrets()`. Pre-filling them defeated that — the file lives inside the package directory, so `npm i -g` replaced it and postinstall wrote _different_ values, while `ensureSecrets()` (which only acts on an empty variable) never got to restore the real ones. Both secrets rotated silently on every update, invalidating dashboard sessions and API-key CRCs. `STORAGE_ENCRYPTION_KEY` left the same list for the same reason in #1622; its comment pointed at a function that no longer exists and now names the real provisioning path. — thanks @maxmad64bis +- **fix(cli):** the CLI now says when a loaded `.env` lives inside the installed package directory (#11437). It already announces every env file it reads, without distinguishing the ones that survive an update from the one that does not: `npm i -g` replaces the package directory wholesale, so values set there are gone at the next update, silently. The warning names the durable path to move them to, and fires only when that file actually supplied a value — a file entirely shadowed by a durable one supplied nothing. A development checkout stays silent: there the same path is stable and documented in `SETUP_GUIDE.md`. — thanks @maxmad64bis +- **fix(free-models):** the shared `isFreeModel()` predicate no longer reports catalog entries marked `freeType: "discontinued"` as free, so `hidePaidModels` can't route to Pollinations' seven premium models that now require a paid key ([#11441](https://github.com/diegosouzapw/OmniRoute/pull/11441)) — thanks @maxmad64bis +- fix(ui): let AnySearch use the normal provider-icon fallback when LobeHub has no matching icon (#11449) +- Fixed the v3.8.50 Costs and Analytics dashboards so flat-rate Claude Code usage can be shown as an explicitly requested token-price estimate without changing default billed-cost semantics. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed archived usage retention so each request is priced individually instead of pricing a day's summed tokens once, which understated archived cost whenever a day mixed cache-heavy and ordinary requests. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed the Costs dashboard so it discloses when displayed figures include flat-rate token-price estimates instead of labelling them as billed spend, using the flag the analytics API already returns; the month-end projection and the CSV/JSON exports carry the same marker, and billed-cost mode is unchanged. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- **fix(combo):** attach the same combo-diagnostics trace (`poolSize`/`attemptOrder`/`excluded`/`terminalReason`, plus `x-omniroute-combo-*` headers) to the round-robin strategy's and the nested pipeline/fusion runtime-unit loop's "Maximum combo retry limit reached" 503 that the priority-strategy path already attaches for the identical terminal condition — previously those two paths returned a bare, contextless 503 ([#11462](https://github.com/diegosouzapw/OmniRoute/issues/11462)). ([#11665](https://github.com/diegosouzapw/OmniRoute/pull/11665)) +- **fix(bun):** use native `bun:sqlite` in `bootstrap-env` and `sync-env` to avoid loading `better-sqlite3` N-API addon during Bun startup ([#11468](https://github.com/diegosouzapw/OmniRoute/pull/11468)) — thanks @TheDemonTuan +- **fix(docker-bun):** make `Dockerfile.bun` install reliable with pre-install script helpers and native `bun:sqlite` smoke check ([#11470](https://github.com/diegosouzapw/OmniRoute/pull/11470)) — thanks @TheDemonTuan +- **fix(docker-bun):** remove every vendored `better-sqlite3` native addon from the Bun runtime image so startup cannot abort before the `bun:sqlite` fallback ([#11482](https://github.com/diegosouzapw/OmniRoute/pull/11482)) — thanks @TheDemonTuan +- **fix(autoCombo):** effort/alias model variants (`gpt-5.6-sol-xhigh`, `gpt-5.6`, cursor's `claude-4.6-opus-high`) inherit their base model's task fitness instead of falling to the wildcard 0.5 ([#11492](https://github.com/diegosouzapw/OmniRoute/pull/11492)) — thanks @MumuTW +- fix(db): rate-limit repeated Arena ELO leaderboard fetch-failure warnings instead of logging one per sync attempt (#11500) ([#11989](https://github.com/diegosouzapw/OmniRoute/pull/11989)) — thanks @hartmark +- **fix(db):** dedupe the raw `[Encryption] Decryption failed...` log line emitted by the lazy-decrypt views (`createLazyRowProxy`/`createLazyConnectionView`), which power `getProviderConnections()` and were re-triggering that line on every CredentialHealth/model-sync cycle for the same corrupt or stale-key credential — a fresh Proxy over a fresh row on every cycle meant the per-proxy memoization never suppressed it, unlike the dedup `decryptConnectionFields()` already had since [#9927](https://github.com/diegosouzapw/OmniRoute/issues/9927). Now shares that dedupe tracking so the line logs at most once per credential ([#11500](https://github.com/diegosouzapw/OmniRoute/issues/11500)). ([#11678](https://github.com/diegosouzapw/OmniRoute/pull/11678)) +- **fix(intelligence):** Arena ELO sync stores a synthesized base-model row for every effort/harness variant (`claude-opus-5-max` → `claude-opus-5`, `gpt-5.6-sol-xhigh (codex-harness)` → `gpt-5.6-sol`), so bare model requests reach the synced intelligence layer instead of the static fallback table; the hand-maintained `MODEL_ALIAS_MAP` — which collapsed generations (`gpt-5.5` → `gpt-5`) — is gone ([#11506](https://github.com/diegosouzapw/OmniRoute/pull/11506)) — thanks @MumuTW +- **fix(autoCombo,sse):** catalog hygiene — the static task-fitness table no longer ranks retired models above live ones (dead rows dropped, patterns matched on segment boundaries), the 7 deprecation aliases that forwarded to retired or non-existent models now point at the vendors' published replacements, an alias is no longer rewritten when the serving provider offers the id as-is, and a new `check:model-lifecycle` gate diffs both tables against a vendor lifecycle snapshot ([#11507](https://github.com/diegosouzapw/OmniRoute/pull/11507)) — thanks @MumuTW +- **fix(sse):** stop dropping resolved `thoughtSignature` values on parallel (multi tool-call) turns sent to Gemini 3.x — the claude→gemini and openai→gemini translators previously kept the signature only on the *first* function call of a message, causing Gemini to reject subsequent calls in the same turn with HTTP 400 "Function call is missing a thought_signature"; each function call now keeps its own resolved signature ([#11510](https://github.com/diegosouzapw/OmniRoute/issues/11510)). ([#11669](https://github.com/diegosouzapw/OmniRoute/pull/11669)) +- Treat Docker publishing from a non-default release branch as an expected skip instead of a failed workflow, while preserving the guard that prevents it from overwriting the `next` channel. ([#11527](https://github.com/diegosouzapw/OmniRoute/pull/11527)) — thanks @pacocartones +- fix(search): prefer credentialed providers over duckduckgo-free fallback (#11524) — the fallback-only loop ran before the credentialed-providers loop in `executeWebSearch`, making configured providers unreachable when `duckduckgo-free` was available. ([#11565](https://github.com/diegosouzapw/OmniRoute/pull/11565)) — thanks @vsd2807 +- **fix(sse):** cap the upstream headers-wait phase for STREAMING requests to a client-realistic ceiling (110s, under Codex's own ~120s hard client-abort window) instead of the flat 10-minute `FETCH_TIMEOUT_MS` default — that default was 5x longer than the body-phase readiness watchdog's own adaptive bound, so a request whose upstream never returned any response at all (not even headers, e.g. a stalled NVIDIA target behind a tool-heavy Responses→Chat translation) kept the client connection alive on keepalives only, guaranteeing the client's own patience ran out first with an opaque 499 instead of OmniRoute detecting and failing the stall fast. Non-streaming requests are unaffected — they keep the existing flat default (`open-sse/utils/fetchStartTimeoutPolicy.ts`) (#11526) ([#11668](https://github.com/diegosouzapw/OmniRoute/pull/11668)) +- **fix(ci):** protocol-clients E2E harness boots the peer-stamped custom server (`run-next.mjs dev`) instead of the bare `next` CLI, so `LOCAL_ONLY` locality resolves from the real TCP peer stamp and `/api/mcp/audit` answers 200 (open bootstrap preserved via an `OMNIROUTE_E2E_BOOTSTRAP_MODE=open` credential clear in `run-next.mjs`, pinned to loopback `HOST`) instead of a deterministic 403 (#11535) ([#11549](https://github.com/diegosouzapw/OmniRoute/pull/11549)) — thanks @jonlwheat2-gif +- **fix(docs):** the Auto-Combo engine is described with the number of factors it actually declares. Four different counts were in circulation — 6, 9, 13 and 14 across `AGENTS.md`, four guides and six code comments — while `DEFAULT_WEIGHTS` declares 15. `check:docs-counts` now reads that number from the source, so adding a factor without updating the prose fails CI ([#11545](https://github.com/diegosouzapw/OmniRoute/pull/11545)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **fix(quota-share):** in-flight leases are per request, so an aborted request's slot ages out instead of being kept alive by later traffic on the same connection ([#11547](https://github.com/diegosouzapw/OmniRoute/pull/11547)) — thanks @abhisheksharma2411 +- The free-tier summary route now serves the refreshed Radar catalog when the feed is active — the same numbers the dashboard shows — and states which catalog answered plus its real build date, instead of always reporting release-frozen figures with a stale curation date. The supporter-key live feed stays reserved to authenticated callers of the instance. ([#11550](https://github.com/diegosouzapw/OmniRoute/pull/11550)) — thanks @maxmad64bis +- **sse:** fix LiveWS/embed-WS servers crashing at startup under the Node/tsx runtime — `liveServer.ts`, `embedWsProxy.ts` and `apiBridgeServer.ts` imported `@/shared/utils/httpClientAbortGuard` without the `.mjs` extension, so the client-abort crash guard added by [#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556) was unreachable and every dependent test failed with `ERR_MODULE_NOT_FOUND` ([#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556)). ([#11671](https://github.com/diegosouzapw/OmniRoute/pull/11671)) — thanks @jonlwheat2-gif / @yxyxy +- fix(chat): shed any of the matched provider-node's routing identifiers (public prefix or internal id) before model lookup, so composites like `//` no longer reach upstream verbatim (#11557) — thanks @maxmad64bis +- **fix(cache):** a semantic-cache entry now expires on its TTL instead of surviving until the next UTC midnight, and `dbEntries` stops counting expired rows ([#11573](https://github.com/diegosouzapw/OmniRoute/pull/11573)) — thanks @ntdatt812 +- **fix(api):** `/v1/models` schedules its stale-while-revalidate rebuild through Next's `after()` again, so a stale catalog reaches the client before the rebuild blocks the event loop ([#11574](https://github.com/diegosouzapw/OmniRoute/pull/11574)) — thanks @ntdatt812 +- **fix(check):** the `known-symbols` executor conformance gate awaits the now-async `getExecutor()`, so it stops reporting all 142 registered aliases as dead symbols and can detect a lazy import that fails ([#11582](https://github.com/diegosouzapw/OmniRoute/pull/11582)) — thanks @ntdatt812 +- **fix(pack):** register `bin/cli/utils/volatileEnvPath.mjs` in `PACK_ARTIFACT_REQUIRED_PATHS`, so the prune can no longer silently drop a module the published CLI imports on every boot ([#11588](https://github.com/diegosouzapw/OmniRoute/pull/11588)) — thanks @ntdatt812 +- **i18n(vi):** translate the five reliability-column strings on the free-provider rankings page, which rendered as `__MISSING__:` markers in the Vietnamese UI ([#11589](https://github.com/diegosouzapw/OmniRoute/pull/11589)) — thanks @ntdatt812 +- **resilience:** restore the expired-connection retry-budget probe in the token-health sweep — the `!isGitHubAccessTokenOnlyConnection` carve-out reintroduced by #11608 contradicted the boundary pinned by #11592, so a GitHub connection parked at `expired` with retry budget remaining was never probed and could never self-heal ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)). ([#11672](https://github.com/diegosouzapw/OmniRoute/pull/11672)) — thanks @jonlwheat2-gif / @ntdatt812 +- **fix(dashboard):** Provider Stats now displays combo success rates on their native 0–100 percentage scale instead of multiplying them twice ([#11595](https://github.com/diegosouzapw/OmniRoute/pull/11595)) — thanks @pacocartones +- **fix(providers):** route DuckDuckGo Web Chat through the verified `duck.ai` endpoint, refresh its free model catalog, and avoid forwarding unsolved anti-abuse challenges ([#11597](https://github.com/diegosouzapw/OmniRoute/pull/11597)) — thanks @solstxce +- **fix(ui):** Console log Refresh and Copy controls now expose localized accessible names, keep copy actions visible on keyboard focus, and announce copy completion safely ([#11599](https://github.com/diegosouzapw/OmniRoute/pull/11599)) — thanks @pacocartones +- Dropped the stale-`.eslintcache` `restore-keys` fallback from both "Restore ESLint file cache" steps in `ci.yml`, so the blocking `Lint` job can no longer be served per-file verdicts computed under a different lint config, suppressions file or lockfile. `quality.yml` had already dropped it in #11963; `ci.yml` — the workflow that actually gates PRs — had not (#11600). ([#11996](https://github.com/diegosouzapw/OmniRoute/pull/11996)) +- fix(ci): pass `--pass-on-unpruned-suppressions` in `run-eslint-json.mjs` so the CI Lint job no longer fails when a suppression is merely orphaned by a genuine fix, mirroring the identical fix already in `validate-release-green.mjs` (#11600) ([#11983](https://github.com/diegosouzapw/OmniRoute/pull/11983)) — thanks @hartmark +- fix(handoff): enforce provider allowlist for universal handoff (#11602) — universal handoff now skips summarization when the selected summary model's provider is not included in the configured provider allowlist. ([#11619](https://github.com/diegosouzapw/OmniRoute/pull/11619)) — thanks @vsd2807 +- **fix(dashboard):** Show a stable error state when Search Analytics returns an HTTP error or malformed data ([#11603](https://github.com/diegosouzapw/OmniRoute/pull/11603)) — thanks @pacocartones +- **test(gamification):** pin the aggregate profile level to the XP-derived semantics of #11604 — `getAggregateXp()` now derives `currentLevel` from the summed XP (`calculateLevel(sum)`), not `MAX(stored current_level)`, and the #3484 fixture levels are aligned with the XP curve ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)). ([#11676](https://github.com/diegosouzapw/OmniRoute/pull/11676)) — thanks @jonlwheat2-gif / @pacocartones +- **fix(dashboard):** Keep the Profile level and progress aligned with aggregate XP, including bounded handling for invalid totals ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)) — thanks @pacocartones +- **fix(dashboard):** Prevent locked hidden badges from revealing their icon or opening private badge details before they are earned ([#11605](https://github.com/diegosouzapw/OmniRoute/pull/11605)) — thanks @pacocartones +- **fix(dashboard):** Restore keyboard focus after shared modals close and cancel delayed autofocus during cleanup ([#11607](https://github.com/diegosouzapw/OmniRoute/pull/11607)) — thanks @pacocartones +- **fix(dashboard):** Enable Enter and Space activation for clickable data-table rows without hijacking nested controls ([#11610](https://github.com/diegosouzapw/OmniRoute/pull/11610)) — thanks @pacocartones +- **fix(autoCombo,sse):** vendor-retired catalog ids are dropped from the auto-combo candidate pool and no longer win on leftover `arena_elo` / `user_override` rows; `getModelLifecycleDecision` consults `model-lifecycle.json` (prefix-stripped) so aggregator traffic is not `untracked` for ids the snapshot already knows ([#11625](https://github.com/diegosouzapw/OmniRoute/issues/11625)) ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)) — thanks @MumuTW +- **test(chatcore):** move Codex/Claude combo fixtures off models the lifecycle guard now rejects — `gpt-5.1-codex`/`gpt-5-codex` are vendor-retired (snapshot, #11626) and `claude-3-5-sonnet-20241022` is shut down, so native-passthrough and combo-fallback tests switched to `gpt-5.6-sol` and `claude-sonnet-4.6` ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)). ([#11675](https://github.com/diegosouzapw/OmniRoute/pull/11675)) — thanks @jonlwheat2-gif / @MumuTW +- fix(opencode-plugin): stop a free-tier budget that rounds up across a magnitude from rendering as `1000K`/`1000M` in the model picker — `fmtTokens` chose its unit from the raw token count and then rounded with `toFixed(1)`, so 999,950–999,999 printed as `1000K` rather than `1M` and just under a billion printed as `1000M` rather than `1B` ([#11684](https://github.com/diegosouzapw/OmniRoute/pull/11684)) — thanks @abhisheksharma2411 +- Cap prose-derived Antigravity quota resets at the configured model cooldown maximum while preserving authoritative Retry-After headers and Google RetryInfo hints. (#11695) Thanks @pacocartones. ([#11823](https://github.com/diegosouzapw/OmniRoute/pull/11823)) +- Compare `_rebaseline_*` notes structurally in the ratchet-bank verifier so an object-valued note no longer reports itself as rewritten on an unmodified tree — the nightly `bank-ratchet-shrinks` lane had been aborting on every run since 2026-08-11, leaving 41,672 lines of earned file-size shrinks unbanked (#11702). ([#11892](https://github.com/diegosouzapw/OmniRoute/pull/11892)) — thanks @MumuTW +- **fix(build):** `prepublish.ts` bundles the ChatGPT Web (Codex) MCP bridge through `runBuildTool()` instead of spawning `npx.cmd` raw, fixing the build crash on Node ≥ 20/Windows where `.cmd` shims cannot be spawned without a shell (EINVAL) ([#11704](https://github.com/diegosouzapw/OmniRoute/issues/11704)) ([#11706](https://github.com/diegosouzapw/OmniRoute/pull/11706)) — thanks @jonlwheat2-gif +- fix(codex): keep `parallel_tool_calls:false` on the translated Codex Responses Lite path (#11707) ([#11984](https://github.com/diegosouzapw/OmniRoute/pull/11984)) — thanks @hartmark +- **fix(tests):** the new Gemini Web Images retirement test (#11708) called the now-async `getExecutor()` synchronously, throwing `TypeError: getExecutor(...).getProvider is not a function` instead of asserting Gemini Web chat stays available. Awaited to match the R0.3 executor-registry refactor (#11220). ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(search):** Allow REST and MCP web-fetch requests to select the advertised `anysearch-search` provider explicitly ([#11716](https://github.com/diegosouzapw/OmniRoute/pull/11716)) — thanks @pacocartones +- **fix(providers):** Keep overnight peak-hour protection active after midnight according to the window's configured start day ([#11718](https://github.com/diegosouzapw/OmniRoute/pull/11718)) — thanks @pacocartones +- **fix(providers):** correct all five g4f.space gateway descriptions to document proof-of-work credits or member API keys, variable limits, and the remote prompt boundary instead of promising free keyless access at five requests per minute ([#11738](https://github.com/diegosouzapw/OmniRoute/pull/11738)). +- **fix(db):** `model_capabilities` is created by a migration instead of lazily on the first models.dev sync, so a clean install and an upgraded install converge on the same schema regardless of which features have run ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(ci):** `check:install-upgrade` now fails on an `npm` install truncated by ENOSPC (npm reports it as a warning and still exits 0), authenticates its health probe so the version assertion works against the hardened health payload, frees the clean-install tree before the upgrade phase, and no longer reports a schema divergence computed from a boot that never served ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(routing):** harden session-affinity key derivation against oversized explicit IDs and empty structural-payload collisions, while recognizing Gemini and common text request shapes ([#11746](https://github.com/diegosouzapw/OmniRoute/pull/11746)) — thanks @MumuTW +- **fix(packages/browser-pool):** regenerate `package-lock.json` so the `packages/browser-pool` workspace's locked `playwright`/`@types/node` (and transitives) match its `package.json` specs, fixing cache-only/offline installs (`npm ci --offline`, Nix `buildNpmPackage`) that previously failed with `ENOTCACHED` ([#11747](https://github.com/diegosouzapw/OmniRoute/issues/11747)) — thanks @benjaminkitt ([#11784](https://github.com/diegosouzapw/OmniRoute/pull/11784)) — thanks @geekyNads +- **fix(assets):** Stop distributing 79 provider and CLI logos without verified source/license provenance, keep every provider/tool available through the internal generic icon, and prevent those IDs from falling through to an external icon CDN. ([#11750](https://github.com/diegosouzapw/OmniRoute/pull/11750)) +- **fix(providers):** retire the common `chatgpt-web` / `cgpt-web` provider because its pre-key and proof-of-work provenance could not be cleared ([#11754](https://github.com/diegosouzapw/OmniRoute/pull/11754)) — explicit chat and image requests, slash-prefixed models, persisted aliases, proxy overrides, and combo targets now fail closed with a sanitized HTTP `410` / `PROVIDER_RETIRED` before upstream dispatch. Migration 163 tombstones matching connections and invalidates their active leases without deleting history or widening API-key allowlists. The independent `chatgpt-web-codex` / `cgpt-codex` provider and its MIT-noticed vendored implementation remain available. +- **fix(usage):** quota and usage refresh no longer 409 when an exclusive lease reserves the connection ([#11758](https://github.com/diegosouzapw/OmniRoute/pull/11758)) — thanks @TheDemonTuan +- Keep the embedding registry's vector width and `embedding` type on models when a synced model exists for the same id, so `/v1/models` no longer reports registry-described embedding models widthless or untyped (#11761) — thanks @rahilmavani +- Correct `google/gemini-embedding-001` on the OpenRouter route to 3072 dimensions, the width it returns when `dimensions` is not sent (#11761) — thanks @rahilmavani +- **fix(vision-bridge):** Restrict automatic vision routing and fallbacks to models available in authoritative live provider catalogs, including aliases and effort variants ([#11767](https://github.com/diegosouzapw/OmniRoute/issues/11767)) — thanks @pacocartones ([#11813](https://github.com/diegosouzapw/OmniRoute/pull/11813)) +- **fix(providers):** reclassify Cerebras as a one-time $5 signup credit (payment method required, 30-day validity), not a recurring no-card 1M tokens/day trial ([#11773](https://github.com/diegosouzapw/OmniRoute/issues/11773)) ([#12591](https://github.com/diegosouzapw/OmniRoute/pull/12591)) — thanks @HouMinXi +- fix(cli): stop prepublish from re-rebuilding the already-built ESM-only opencode-plugin dist (#11787) ([#11990](https://github.com/diegosouzapw/OmniRoute/pull/11990)) — thanks @hartmark +- **fix(db):** Fresh installs no longer log a non-fatal `no such table: compression_run_telemetry` warning when retention cleanup runs before the lazily-created telemetry table exists ([#11802](https://github.com/diegosouzapw/OmniRoute/pull/11802)) — thanks @RaviTharuma +- fix(sse): set X-OmniRoute-Selected-Connection-Id on successful combo dispatches so downstream consumers stop falling back to an empty connection id (#11810) ([#11986](https://github.com/diegosouzapw/OmniRoute/pull/11986)) — thanks @hartmark +- **fix(services):** embedded CLIProxyAPI lifecycle checks now use public `/healthz`, while model discovery uses the configured dedicated data-plane API key instead of the management password ([#11811](https://github.com/diegosouzapw/OmniRoute/pull/11811)) — thanks @RaviTharuma +- Fix persisted model steps returning an error in the playground route simulator, and warn when structural steps cannot be simulated. ([#11862](https://github.com/diegosouzapw/OmniRoute/pull/11862)) — thanks @pacocartones +- **fix(providers):** Antigravity's dynamic mitmAlias table no longer routes `gemini-3.7-flash-{high,medium,low}` to a literal tier-suffixed upstream id just because one connected account's own discovery listed it directly — those display ids always resolve through the safe `gemini-3.7-flash-tiered` static alias, so one account's Google-provisioned access no longer 404s every sibling account of the provider ([#11824](https://github.com/diegosouzapw/OmniRoute/issues/11824), [#11651](https://github.com/diegosouzapw/OmniRoute/issues/11651)) ([#11988](https://github.com/diegosouzapw/OmniRoute/pull/11988)) — thanks @hartmark +- **fix(dashboard):** Expose Profile loading, errors, page structure, and XP progress to assistive technologies ([#11838](https://github.com/diegosouzapw/OmniRoute/pull/11838)) — thanks @pacocartones +- **fix(socks):** `Agent.connectTimeout` now bounds both SOCKS handshake and TLS connect, and `family === null` no longer falls back to `fetch-socks` ([#11842](https://github.com/diegosouzapw/OmniRoute/pull/11842)) — thanks @maxmad64bis +- **perf(compression):** OOM mitigations for large payload hashing, memoization, and token estimation ([#11844](https://github.com/diegosouzapw/OmniRoute/pull/11844) — thanks @AndrianBalanescu) +- Excluded Next.js Node File Trace manifests (`*.nft.json`) from the published npm tarball. They are build-time metadata and are never read while serving, but had grown to 668.7 MB — 61% of the package — which pushed the upload past the registry limit and made `npm publish` fail with `413 Payload Too Large`. ([#11864](https://github.com/diegosouzapw/OmniRoute/pull/11864)) +- **fix(cli):** keep the global OmniRoute server `--base-url` separate from provider-node `--endpoint` values in add, update, and validate commands ([#11860](https://github.com/diegosouzapw/OmniRoute/pull/11860)) — thanks @pacocartones +- **fix(config):** Nous Research's `Hermes-4-405B` model now displays as "Hermes 4 405B (Nous Research)" in both the provider registry and the free-model catalog, instead of the mislabelled "Hermes 4 7B" ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#11993](https://github.com/diegosouzapw/OmniRoute/pull/11993)) +- **fix(provider/nous):** inject required user= tag into Nous Research inference requests to resolve upstream 400 "missing tags" error ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#12044](https://github.com/diegosouzapw/OmniRoute/pull/12044)) +- **fix(build):** `npm run build` now fails in one second with a named package and a copy-pasteable fix when npm silently drops an externalised optional native dependency, instead of dying four minutes in with `Module not found: Can't resolve 'better-sqlite3'` ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- **fix(install):** `postinstall` no longer throws `ReferenceError: isAndroid is not defined` — failing the whole `npm install` — when the `better-sqlite3` rebuild fallback times out; the manual-fix guidance is reachable again ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- Fixed the Alibaba free-tier allowlist test that went red on its own once the shipped catalog's `validUntil` (2026-08-27) passed, leaving every PR and `main` with a failing `Unit Tests (1/8)`. The test now builds its own packs with dates it controls, and covers the expired-pack fallback that production has actually been serving. ([#11867](https://github.com/diegosouzapw/OmniRoute/pull/11867)) +- Split the npm registry upload into its own GitHub-hosted job. npm refuses `--provenance` from a self-hosted runner (`422 ... Only "github-hosted" runners are supported`), which blocked the v3.8.50 publish; the heavy verification cannot move to a hosted runner, so it now hands the proven tarball over instead. ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(ui):** the shared `DataTable` loading state no longer reads its decorative ⏳ glyph out to assistive technology, and now carries the same `role="status"` / `aria-live="polite"` / `aria-busy="true"` semantics as `PageLoading` ([#11869](https://github.com/diegosouzapw/OmniRoute/pull/11869)) — thanks @pacocartones +- **fix(api):** Bound the `timeout` accepted by `POST /api/providers/[id]/login` to the same 15s–600s window the Adobe Firefly and Conol browser-login paths already enforce, so a request can no longer pin a headful browser and the single active-login slot with an arbitrarily large poll budget. ([#11871](https://github.com/diegosouzapw/OmniRoute/pull/11871)) — thanks @pacocartones +- **fix(api):** Stop the tunnel and MITM routes from returning raw child-process error text, which disclosed host paths, binary install locations and Tailscale `tskey-*` credentials in a body some of these routes serve to non-loopback callers, and return a real 400 instead of a framework 500 when a tunnel request body fails validation. ([#11872](https://github.com/diegosouzapw/OmniRoute/pull/11872)) — thanks @pacocartones +- **fix(open-sse):** stream TTFT/ITL are sampled from a monotonic clock (`performance.now`) instead of `Date.now`, so an NTP correction or wall-clock jump can no longer inflate a genuine TTFT or produce a negative one that the `ttft >= 0` guard silently drops — protecting the router's OTel/EWMA/`usage_history`/speed-ranking signals ([#11873](https://github.com/diegosouzapw/OmniRoute/pull/11873)) — thanks @pacocartones +- **fix(providers):** xAI `reasoning_effort: "xhigh"` now reaches grok-4.6+ instead of being silently clamped to `"high"` ([#11879](https://github.com/diegosouzapw/OmniRoute/pull/11879)) — thanks @NoxzRCW +- **fix(dashboard):** the providers page no longer crashes into the error boundary when a provider id collides with an `Object.prototype` member (`constructor`, `__proto__`); icon lookups are own-property guarded ([#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)) — thanks @NoxzRCW +- **fix(skills):** injected skill tools declared in shorthand (`{"content": "string"}`) now forward valid JSON Schema, unblocking providers that validate tool schemas strictly such as Zhipu GLM on the Console Go tier ([#11881](https://github.com/diegosouzapw/OmniRoute/pull/11881)) — thanks @NoxzRCW +- **fix(api):** `POST /api/playground/simulate-route` now surfaces `combo-ref` and `provider-wildcard` persisted combo steps with a specific warning (naming the referenced combo, or the unresolved `provider/model` wildcard) instead of folding them into a generic "unsupported step" count; a `provider-wildcard` step is also now included as an unresolved target so the operator can see it is in the route (ported from [#11882](https://github.com/diegosouzapw/OmniRoute/pull/11882) — thanks @NoxzRCW). ([#11930](https://github.com/diegosouzapw/OmniRoute/pull/11930)) +- **fix(translator):** the streaming OpenAI→Claude translator keeps upstream usage, including prompt-cache tokens, when it arrives on a trailing `choices: []` chunk (Fireworks and any upstream using `stream_options.include_usage`) ([#11883](https://github.com/diegosouzapw/OmniRoute/pull/11883)) — thanks @NoxzRCW +- **fix(cli):** `omniroute update --apply` now tells you explicitly whether a running server was detected and, if so, that you must run `omniroute restart` to apply the update — it never restarted anything and previously implied the update was already live once files were installed. The dashboard's npm-mode Update flow (`/api/system/version`) now tries OmniRoute's own PID-file-managed supervisor before falling back to pm2, and reports an honest "restart required" step instead of a silent pm2-only "skipped" that read like a completed update. The server-side latest-version lookup backing the dashboard's update banner also gained `--prefer-online`, closing the same stale-npm-cache class already fixed in the CLI's own copy for #4376 ([#11885](https://github.com/diegosouzapw/OmniRoute/issues/11885)). ([#12005](https://github.com/diegosouzapw/OmniRoute/pull/12005)) +- **fix(resilience):** clear persisted LKGP pins when a target suffers connection/provider exhaustion or is skipped before dispatch due to cooldown/exhaustion/unavailability, preventing subsequent requests from repeatedly prioritizing known-dead providers ([#11911](https://github.com/diegosouzapw/OmniRoute/issues/11911)). ([#12013](https://github.com/diegosouzapw/OmniRoute/pull/12013)) — thanks @HouMinXi +- **fix(sse):** the OpenAI→Claude stream translator now defers the terminal `message_delta`/`message_stop` emission until the real usage block has arrived (or a genuine end-of-stream flush forces it) instead of emitting it immediately on `finish_reason` — previously, when the trailing usage-only chunk (`{"choices":[],"usage":{...}}`) arrived *after* the `finish_reason` chunk (the normal order for Fireworks/vLLM/Together and other `stream_options.include_usage` upstreams), the client-visible `message_delta` still carried stale/zero usage even though `state.usage` was internally corrected too late to matter (ported from [#11915](https://github.com/diegosouzapw/OmniRoute/pull/11915) — thanks @HouMinXi). ([#11933](https://github.com/diegosouzapw/OmniRoute/pull/11933)) +- **fix(sse):** `stripStore()` now forces `store=false` for stateless OpenAI-compatible Responses-API targets (unless the connection has explicitly opted in via `providerSpecificData.openaiStoreEnabled`), instead of only handling the `openai`/`agentrouter` cases — a client-supplied `store` value previously passed through untouched to backends that don't actually persist responses server-side ([#11916](https://github.com/diegosouzapw/OmniRoute/pull/11916) — thanks @HouMinXi). +- **fix(models):** custom provider-node models (synced, custom, and alias-backed) now appear under their configured prefix in the unified catalog when the operator's model-id prefix mode is canonical, instead of being dropped whenever alias-inclusion was otherwise disabled ([#11918](https://github.com/diegosouzapw/OmniRoute/pull/11918) — thanks @HouMinXi). +- **fix(models):** the unified model catalog now suppresses stale static registry models (including effort-tier variants) for any provider whose active connection has an authoritative live synced catalog, not only providers already using exclusive-synced-listing — a connection with `providerUsesAuthoritativeLiveCatalog` previously kept serving both the live-synced models and the stale static rows side by side ([#11919](https://github.com/diegosouzapw/OmniRoute/pull/11919) — thanks @HouMinXi). +- **fix(ui):** `ProviderIcon`'s three remaining unguarded lookups (`PROVIDER_ICON_ALIASES`, `LOCAL_SVG_ALIASES`, `THEMED_SVGS`) now use `Object.hasOwn()` own-property checks — a provider id such as `constructor` or `__proto__` previously resolved through the prototype chain instead of falling through to the unknown-provider CDN fallback (`getLobeProviderIcon()` itself was already guarded by [#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)); `ProviderPageHeader`'s `color` field is also now optional, matching the rest of the component's defensive typing (ported from [#11920](https://github.com/diegosouzapw/OmniRoute/pull/11920) — thanks @HouMinXi). ([#11935](https://github.com/diegosouzapw/OmniRoute/pull/11935)) — thanks @NoxzRCW +- fix(ollama): preserve multi-byte UTF-8 content split across stream chunks in the Ollama NDJSON transform, which previously corrupted CJK/emoji into U+FFFD (#11921) — thanks @pacocartones +- **fix(providers):** OrcaRouter chat requests now target `/v1/chat/completions` instead of the bare `/v1` API root, fixing the upstream `404 Invalid URL (POST /v1)` ([#11923](https://github.com/diegosouzapw/OmniRoute/pull/11923)). — thanks @echel0nn +- **fix(plugins):** deliver the `onStreamComplete` event to disk-installed plugins. The event shipped in v3.8.50 (#9669) was emitted internally but had no plugin-facing wiring, so no plugin could ever subscribe: the manifest schema silently dropped `hooks.onStreamComplete`, and the loader/manager only knew the seven legacy hooks. `onStreamComplete` is now a declarable manifest hook, wired through the loader and registered by the manager like the other hooks, and its payload carries a `requestId` so consumers can correlate the stream-completion event with the originating request ([#11934](https://github.com/diegosouzapw/OmniRoute/pull/11934)) — thanks @amaleta / @pacocartones +- **fix(db):** the Qdrant embedding-model dropdown now lists local/self-hosted providers (Ollama, LM Studio, vLLM, etc.) — an active connection is treated as "configured" when the provider allows an optional API key, not only when it has a real key or OAuth, so a running local embedding provider is no longer hidden from the picker ([#11949](https://github.com/diegosouzapw/OmniRoute/issues/11949)) ([#11995](https://github.com/diegosouzapw/OmniRoute/pull/11995)) +- **fix(providers):** Vertex AI Anthropic partner-model discovery now calls the Model Garden `v1beta1` publisher list (`/v1beta1/publishers/anthropic/models`, global) and parses its `publisherModels` envelope, so Claude models auto-synced from Vertex populate the active live catalog and route at request time instead of returning `Model '' is not available in the active live catalog` ([#11991](https://github.com/diegosouzapw/OmniRoute/issues/11991)) — thanks @fabioluissilva ([#11998](https://github.com/diegosouzapw/OmniRoute/pull/11998)) +- **fix(cli):** support `--base-url` alongside `--endpoint` in `omniroute nodes add`, `update`, and `validate` subcommands to prevent global `--base-url` shadowing issues ([#11999](https://github.com/diegosouzapw/OmniRoute/issues/11999)). ([#12033](https://github.com/diegosouzapw/OmniRoute/pull/12033)) — thanks @HouMinXi +- **fix(providers):** `cloudflare-ai` no longer refuses image content parts for every Workers AI model ([#12002](https://github.com/diegosouzapw/OmniRoute/pull/12002)) — the plain-string `content` requirement behind #2539 is carried by the _model_ schema, not by the `/ai/v1/chat/completions` endpoint (measured: an all-text part array returns 200 on `@cf/mistralai/mistral-small-3.1-24b-instruct`, `@cf/meta/llama-4-scout-17b-16e-instruct` and `@cf/meta/llama-3.3-70b-instruct-fp8-fast`, and 400 on the text-only `@cf/qwen/qwen2.5-coder-32b-instruct`). `transformRequest()` flattened every array and threw on the first non-text part (#6390), so image input was refused for vision-capable Cloudflare models that accept it. All-text arrays are still flattened — the one shape every model accepts — while an array carrying a non-text part is passed through untouched, so the attachment is still never silently dropped. Regression guards: `tests/unit/cloudflare-ai-image-parts-6390.test.ts`. — thanks @davidlinfr +- **fix(resilience):** decouple the limiter-managed execution backstop from the queue-wait budget — new `requestQueue.executionMaxWaitMs` (env `RATE_LIMIT_EXECUTION_MAX_WAIT_MS`, default 600000 = 10 min) now feeds Bottleneck's post-dispatch `expiration`, while `requestQueue.maxWaitMs` keeps its documented queue-wait semantics. Previously the queue-wait budget doubled as the execution expiration, so legitimate long-running calls on non-incremental gateways (whole generation buffered before the first upstream byte, e.g. Console Go / Command Code tiers serving GLM models) were killed mid-flight at the queue budget with a false 504 `RATE_LIMIT_EXECUTION_TIMEOUT` — the local limiter undercut the provider-aware upstream fetch-start timeouts. The surfaced 504 message now names `requestQueue.executionMaxWaitMs`; the error keeps the #4165 guarantees (disclaims an upstream timeout, preserves the Bottleneck error as `cause`, branded code + trusted provenance, classified request-scoped so combo falls back). A real queue-wait bound (the `Promise.race` around `limiter.schedule()` sketched in #9533) remains future work. (#12025) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **Call logs:** keep the `error` field when an artifact exceeds the storage cap, instead of replacing it with the omission marker. The error is the only field that says *why* a request failed and is typically ~90 bytes next to the multi-hundred-KB bodies that trip the cap, so dropping it left a size-limited row undiagnosable — a provider outage, a local timeout and an upstream 400 all rendered identically. It is now preserved at every fallback stage, truncated to 4KB if it is itself large ([#12026](https://github.com/diegosouzapw/OmniRoute/issues/12026)). ([#12095](https://github.com/diegosouzapw/OmniRoute/pull/12095)) — thanks @ntdat812 +- **fix(diagnostics):** preserve the error field (truncated to 4KB with a `[truncated: …]` suffix) in every call-log artifact size-limit fallback stage. Previously the minimal fallback replaced the error with `[omitted: call log artifact size limit exceeded]`, so an oversized artifact row showed nothing about WHY the request failed — e.g. 91 of 847 opencode-go 504 rows on one production instance were undiagnosable from the dashboard. Oversized request/response bodies are still omitted exactly as before; the error cap is independent of the payload sizes that tripped the fallback. (#12026) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **fix(sse):** OpenAI Responses clients that declare the native `web_search` tool now receive a spec-shaped `web_search_call` output item with `action.sources` alongside the preserved function-call round-trip, so search results executed through OmniRoute's own search backend are consumable by standard Responses clients (Codex, pi-web-access, …). ([#12031](https://github.com/diegosouzapw/OmniRoute/pull/12031)) — thanks @watchingdogs +- **fix(cli):** use in-thread alias resolver hooks on modern runtimes to avoid deprecation noise and improve Node.js forward compatibility ([#12073](https://github.com/diegosouzapw/OmniRoute/issues/12073)). ([#12083](https://github.com/diegosouzapw/OmniRoute/pull/12083)) — thanks @xiaoyaner0201 +- **fix(combo):** an operator-set **Agent Features → Context length** on a combo is now honored at request time. The value was persisted and advertised through `/v1/models`, but `resolveComboContextLimit()` never consulted it — so a multi-target combo whose members carry no per-model window fell through to the provider's generic `defaultContextLength` (openrouter 128000, command-code 200000) and rejected large requests with `Input exceeds context window … limit 128000` despite the combo being explicitly sized much larger. An identical single-target combo worked, because it collapses to its concrete target before the guard runs. Invalid values (0/negative/NaN/Infinity) are ignored, so the existing target → combo-min → fallback order is unchanged. ([#12090](https://github.com/diegosouzapw/OmniRoute/pull/12090)) — thanks @adivekar-utexas +- **fix(sse):** passthrough streams now estimate usage on finish when upstream closes without usage even with `stream_options.include_usage` — avoids `0 tokens / 0%` for providers that stay silent (and correctly handles trailing empty-choices usage) ([#12151](https://github.com/diegosouzapw/OmniRoute/pull/12151)) — thanks @maxmad64bis +- **fix(combos):** clearing an agent feature in the combos editor now persists — unchecking context cache protection, or emptying the system message or tool filter, sends an explicit `null` instead of dropping the field from the `PUT` body, which the update merge read as "leave unchanged" ([#12177](https://github.com/diegosouzapw/OmniRoute/pull/12177)) — thanks @foreveryh / @ntdatt812 +- **fix(memory):** self-hosted embedding endpoints now vectorize — the vector width is measured from the first embedding that comes back instead of being read from a registry that cannot describe them, so `vec_memories` is created and memories stop piling up unvectorized behind a green health check ([#12180](https://github.com/diegosouzapw/OmniRoute/pull/12180)) — thanks @kanade-hoshino / @ntdatt812 +- **fix(translator):** the leading `system` message now reaches Responses-API upstreams when its `content` is a content-part array — it was read as `typeof content === "string" ? content : ""`, so a prompt-caching client (Anthropic `cache_control`, the shape LiteLLM and the Anthropic SDK emit) had its entire system prompt replaced by an empty `instructions`. The request was still accepted with a normal `prompt_tokens` count, so the model answered with no instructions and nothing in the response said they were missing. Mid-conversation system turns already handled the array shape ([#7056](https://github.com/diegosouzapw/OmniRoute/pull/7056)); only the first one did not ([#12206](https://github.com/diegosouzapw/OmniRoute/issues/12206)). Regression guard: `tests/unit/translator-openai-responses-system-content-parts.test.ts`. ([#12207](https://github.com/diegosouzapw/OmniRoute/pull/12207)) — thanks @Gorillaz322 +- **fix(free-tier):** `/api/free-tier/summary` no longer computes its totals from a Radar feed built before the catalog the running release ships. When the cached feed is older — or carries no build date at all — the route answers from the shipped catalog, resolved through the operator's local model state so disabled and tombstoned models stay out of the numbers ([#12215](https://github.com/diegosouzapw/OmniRoute/pull/12215)). — thanks @maxmad64bis +- **fix(oauth):** Keep a Claude personal workspace and a Team organization as separate connections — they share the same email and `accountUUID`, so the email-only OAuth dedup let the second login overwrite the first account's tokens; `organizationUUID` now disambiguates them, the way `workspaceId` does for Codex ([#12222](https://github.com/diegosouzapw/OmniRoute/pull/12222)) — thanks @davidebaraldo +- **fix(resilience):** a 402 on a single paid model of a passthrough/gateway provider (e.g. `kilo-gateway`, `ollama-cloud`) no longer terminalizes the whole connection with a never-auto-recovered `credits_exhausted` status — only the paid model is locked out, so free models on the same key keep serving. 402 variant of [#3027](https://github.com/diegosouzapw/OmniRoute/issues/3027). Single-credential providers are unaffected — a 402 there is still treated as the key being genuinely out of credit ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239) / [#10616](https://github.com/diegosouzapw/OmniRoute/issues/10616)) ([#12242](https://github.com/diegosouzapw/OmniRoute/issues/12242)) — thanks @brick30llc-ctrl ([#12266](https://github.com/diegosouzapw/OmniRoute/pull/12266)) — thanks @geekyNads +- **fix(sse):** trust `finish_reason: "length"`/`"max_tokens"` over the reasoning-consumed-token ratio in response quality validation, so a reasoning model truncated below the old 90% threshold correctly fails and retries instead of returning empty content as a silent "success" ([#12262](https://github.com/diegosouzapw/OmniRoute/pull/12262)) — thanks @brick30llc-ctrl +- **fix(settings):** `PUT /api/settings/cache-config` now persists `alwaysPreserveClientCache` to the flat general settings the runtime cache-control policy actually reads; previously the value landed in the databaseSettings "cache" section and was silently ignored, so the endpoint had no effect on `cache_control` passthrough ([#12304](https://github.com/diegosouzapw/OmniRoute/pull/12304)) — thanks @davidebaraldo +- **fix(grok-cli):** treat omitted SuperGrokPro `creditUsagePercent` as 0% used so Provider Limits still renders a weekly bar (proto3 zero-elision) ([#12312](https://github.com/diegosouzapw/OmniRoute/pull/12312)) — thanks @HouMinXi +- **fix(combo):** Expose the two Auto-Combo scoring factors nobody could set — the combo validation schema and the dashboard weight sliders both declared 13 of the scorer's 15 factors, so `connectionDensity` (spreads load across a provider's connections) and `quality` were dropped on save and offered nowhere. The sliders also shipped their own default table that differed from the engine's on every non-zero factor and summed to 1.05, so the percentages shown next to them added up to 105%. Both lists now match `DEFAULT_WEIGHTS`, and a test keeps them there. Note that a combo whose stored `weights` omitted the two keys was effectively running with them at zero and the other thirteen renormalized upward; it now runs with the engine's intended distribution, so its routing does shift ([#12314](https://github.com/diegosouzapw/OmniRoute/pull/12314)) — thanks @maxmad64bis +- **fix(docs):** The free-tier reference no longer says its numbers come "confidence tagged per row" — no catalog entry carries a confidence tag and the API serves none, so every figure on that page is an estimate of the same, unstated quality. The page now states what an entry does vouch for: an independently documented hard stop (set by hand with the source in a comment, never defaulted to `true`) and a prompt-training disclosure, both with live counts the `check:docs-counts` gate keeps honest ([#12318](https://github.com/diegosouzapw/OmniRoute/pull/12318)) — thanks @maxmad64bis +- **fix(usage):** `adobe-firefly` and `firefly` have had a working usage fetcher since Adobe Firefly landed, but neither was ever added to the registration list, so the provider-plugin manifest, `genericQuotaFetcher` and the free-access quota cache all reported them as having no usage support — while `USAGE_SUPPORTED_PROVIDERS` said the opposite. Both are now declared, which also means their credit balance is fetched like any other declared provider's: `registerGenericQuotaFetchers` now registers a generic quota fetcher for them, and `resolveFreeAccessState` no longer returns early. A test holds the registration list to the dispatcher's switch in both directions, which is what the module's own docstring already asked for in prose ([#12321](https://github.com/diegosouzapw/OmniRoute/pull/12321)) — thanks @maxmad64bis +- **fix(quota):** drop the generic quota cache (agy / Antigravity / Claude OAuth) on an upstream 429 so reset-aware scoring does not keep a 60s stale snapshot, and force-refresh the next usage fetch so inner provider caches cannot recache the same window ([#12325](https://github.com/diegosouzapw/OmniRoute/pull/12325)) — thanks @HouMinXi +- **fix(combos):** deleting a combo now clears its persisted LKGP pins instead of leaving unreachable `key_value` rows behind ([#12326](https://github.com/diegosouzapw/OmniRoute/issues/12326)) ([#12425](https://github.com/diegosouzapw/OmniRoute/pull/12425)) — thanks @KrzysiekSko +- **fix(sse):** Keep ZWNJ (U+200C) and ZWJ (U+200D) in assistant text, reasoning and tool-call arguments — Persian/Kurdish half-space (`ارائه‌دهنده`), Arabic/Indic shaping and emoji sequences no longer lose them; the response de-obfuscation now removes joiners only between ASCII word characters, where the request side inserts them ([#12186](https://github.com/diegosouzapw/OmniRoute/issues/12186)) — thanks @rezjalibd ([#12359](https://github.com/diegosouzapw/OmniRoute/pull/12359)) — thanks @pacocartones +- **fix(resilience):** count resolved upstream 5xx results against the provider circuit breaker on the chat path — `CircuitBreaker.execute()` no longer reads a resolved `{ success: false, status: 5xx }` as a success that cancels the call-site failure, so a provider answering 503s now trips its breaker instead of staying `CLOSED` at `failureCount: 1`; single-model and combo dispatches are each accounted exactly once ([#12254](https://github.com/diegosouzapw/OmniRoute/issues/12254)) ([#12360](https://github.com/diegosouzapw/OmniRoute/pull/12360)) — thanks @pacocartones +- **fix(providers):** resolve the Codex quota auto-ping model from the live provider catalog and lifecycle registry instead of the retired `gpt-5.1-codex-mini`, and pause the ping with one actionable warning when no selectable Codex model exists rather than retrying a shut-down id every cooldown window ([#11905](https://github.com/diegosouzapw/OmniRoute/issues/11905)) ([#12361](https://github.com/diegosouzapw/OmniRoute/pull/12361)) — thanks @pacocartones +- **fix(api):** keep the `{created, data}` wrapper on combo-routed `/v1/images/generations` responses and default Codex image results to `b64_json` on both `/v1/images/generations` and `/v1/images/edits` so Codex CLI's built-in `image_gen` can decode them ([#12268](https://github.com/diegosouzapw/OmniRoute/issues/12268)) ([#12362](https://github.com/diegosouzapw/OmniRoute/pull/12362)) — thanks @pacocartones +- **fix(sse):** Name the shadowed custom provider node when a built-in provider id/alias (e.g. `openference` → `of`) reserves the prefix of an existing OpenAI/Anthropic-compatible node, so the runtime `No active credentials for provider: ` error explains that the prefix routed to the built-in and never reached the node's healthy connections, instead of contradicting the dashboard ([#11943](https://github.com/diegosouzapw/OmniRoute/issues/11943)) — thanks @morpheus9393 ([#12365](https://github.com/diegosouzapw/OmniRoute/pull/12365)) — thanks @pacocartones +- **fix(i18n):** wrap `ccOnboardingKeyPlaceholder` in ICU single quotes across all 43 locales so angle brackets render literally instead of being parsed as rich-text tags, which crashed the Claude Code onboarding block with `INVALID_MESSAGE: INVALID_TAG` ([#12302](https://github.com/diegosouzapw/OmniRoute/issues/12302)) ([#12369](https://github.com/diegosouzapw/OmniRoute/pull/12369)) — thanks @gonisulaimann +- **fix(models):** publish `effort_tiers` on Kimi K3's synced base-model entries (`kmca/k3`, `kmca/k3-256k`) so catalog-only clients (OpenCode, plain SDK pickers) can see and select the reasoning tiers (`low`/`high`/`max`) the synced metadata already carried — the `isSkippedEffortProvider` gate no longer suppresses tier visibility on those base entries, while synthetic `-` variant generation stays prevented and Codex/GLM base models remain excluded unchanged ([#12299](https://github.com/diegosouzapw/OmniRoute/issues/12299)) ([#12371](https://github.com/diegosouzapw/OmniRoute/pull/12371)) — thanks @gonisulaimann +- **fix(guardrails):** keep `auto`/`auto/*` virtual combos exempt from the Vision Bridge `fixedModel` credential guard so a combo target is passed through instead of silently falling back to global auto-selection ([#12237](https://github.com/diegosouzapw/OmniRoute/issues/12237)) ([#12373](https://github.com/diegosouzapw/OmniRoute/pull/12373)) — thanks @pacocartones +- **fix(combo):** capability-filter exhaustion caused by `max_tokens` above every target's known output limit now reports that reason (requested `max_tokens` vs the pool's highest known ceiling) instead of the unrelated "supports structured output" message ([#12229](https://github.com/diegosouzapw/OmniRoute/issues/12229)) — thanks @DW-MediaLab ([#12374](https://github.com/diegosouzapw/OmniRoute/pull/12374)) — thanks @pacocartones +- **fix(auth):** the `least-used` account strategy now prefers accounts without backoff before falling back to oldest `lastUsedAt`, the same tie-break `round-robin` already applies, so a failover no longer lands on a just-rate-limited account for a single request ([#12279](https://github.com/diegosouzapw/OmniRoute/issues/12279)) — thanks @tenshiak ([#12375](https://github.com/diegosouzapw/OmniRoute/pull/12375)) — thanks @pacocartones +- **fix(docker):** the `chatgpt-web-codex-browser` image now finds the Chrome binary under `chrome-linux64/` (Chrome for Testing layout in `playwright:v1.62.0-noble`) as well as the legacy `chrome-linux/`, so the container no longer crash-loops with `exec: --headless=new: not found` ([#12024](https://github.com/diegosouzapw/OmniRoute/issues/12024)) ([#12376](https://github.com/diegosouzapw/OmniRoute/pull/12376)) — thanks @pacocartones +- **fix(providers):** declare `groq/compound` and `allam-2-7b` as non-reasoning models in the curated Groq registry so `reasoning_effort` / `output_config.effort` / `thinking` from Claude Code are stripped instead of forwarded, which Groq rejected with HTTP 400 ([#12134](https://github.com/diegosouzapw/OmniRoute/issues/12134)) ([#12379](https://github.com/diegosouzapw/OmniRoute/pull/12379)) — thanks @pacocartones +- **fix(executors):** `OpencodeExecutor` no longer forces a direct connection when the connection has a proxy assigned in Proxy Management but no per-account proxies: the single-account fast path used to wrap the upstream dispatch in the direct-egress sentinel, discarding the ambient proxy context the chat handler had pinned from `proxy_assignments`, so API-key `opencode`/`opencode-go` connections egressed from the host IP (and hit geoblocks) despite the assignment. The direct pin is now applied only when no ambient proxy context exists ([#11894](https://github.com/diegosouzapw/OmniRoute/issues/11894) — thanks @hizzt) ([#12380](https://github.com/diegosouzapw/OmniRoute/pull/12380)) — thanks @pacocartones +- **fix(api):** `GET /v1/models` with `MODELS_CATALOG_PREFIX_MODE=canonical` (or `?prefix=canonical`) now lists providers whose registry alias is undefined or equal to their own id (Antigravity, Antigravity CLI and other self-aliased built-ins) — their single `provider/model` id was dropped by the alias/canonical duplicate guard in the static, synced, custom and alias-backed catalog loops ([#12058](https://github.com/diegosouzapw/OmniRoute/issues/12058)) — thanks @cheynetom ([#12381](https://github.com/diegosouzapw/OmniRoute/pull/12381)) — thanks @pacocartones +- **fix(translator):** Drop replayed `thinking` blocks that carry no signature (the shape produced from cross-provider `reasoning_content`) instead of stamping the default Claude signature on them, which Anthropic rejected with `400 Invalid signature in thinking block` on the next turn served by an Anthropic rung ([#12105](https://github.com/diegosouzapw/OmniRoute/issues/12105)) — thanks @atescivitci-cmd ([#12386](https://github.com/diegosouzapw/OmniRoute/pull/12386)) — thanks @pacocartones +- **fix(cli):** Resolve Bun's `--preload` polyfill path against the package root instead of `dist/`, so `omniroute` installed with `bun install -g` no longer crashes at startup with `error: preload not found …/dist/open-sse/utils/setupPolyfill.ts` ([#11980](https://github.com/diegosouzapw/OmniRoute/issues/11980)) — thanks @joglomedia ([#12387](https://github.com/diegosouzapw/OmniRoute/pull/12387)) — thanks @pacocartones +- **fix(providers):** `gemini-business` now publishes its model catalog — `/v1/models` and `/v1/providers/gemini-business/models` list the 12 enterprise Gemini ids the executor understands instead of returning an empty list (#12107) ([#12389](https://github.com/diegosouzapw/OmniRoute/pull/12389)) — thanks @pacocartones +- **fix(db):** install `busy_timeout` before the SQLite connection's first statement so a process opening the database while another one closes its WAL connection waits out the transient EXCLUSIVE lock instead of dying with `database is locked`, and recognise the drivers' real BUSY/PROTOCOL/IOERR errors as transient in the corruption probe so the same lock no longer renames the database away as corrupt; deflakes `cross-process contenders never both acquire the same connection` (#12394 — thanks @pacocartones) +- **fix(chat-admission):** derive the `chat_admission_busy` 503 `Retry-After` from observed heavyweight-lease occupancy — the larger of the exhausted `OMNIROUTE_CHAT_ADMISSION_QUEUE_MS` window and the time since capacity last turned over, capped at 60 s — instead of a fixed 1 s (structural) / 2 s (byte-stage) hint that invited Codex/agent fan-out clients to re-send ~1 MiB `/v1/responses` bodies every second into a gate held for the whole SSE lifetime; an idle gate keeps the historical floors ([#12135](https://github.com/diegosouzapw/OmniRoute/issues/12135)) (#12395 — thanks @pacocartones) +- **fix(api-manager):** the API key permissions modal no longer silently drops `allowedCombos` entries its Combo picker cannot render — routing-rule names such as `rt-*`, which the backend already honours — when "All" is clicked and the key is switched back to "Restrict"; those entries now survive the toggle, are listed read-only under the combo list so the count and the list agree, and are saved back verbatim instead of persisting `[]` (deny-all) (#12397 — thanks @pacocartones) +- **fix(catalog):** write the NUL separator of the catalog connection memo key, the provider serviceKind memo key, the Video Bridge promotion group key and a JSON-exactness test fixture as the `\u0000` escape instead of a raw byte — same runtime value, but the raw byte made git, GitHub and ripgrep treat those files as binary (hidden PR diffs, silently skipped searches); a guard test now keeps raw NUL bytes out of `src/`, `open-sse/` and `tests/` (#12403 — thanks @pacocartones) +- **fix(providers):** add `CLAUDE_CODE_CLIENT_VERSION` and `GITHUB_COPILOT_CLI_VERSION` env overrides so Anthropic/Copilot client-version gates can be unblocked without a rebuild ([#12417](https://github.com/diegosouzapw/OmniRoute/issues/12417)) ([#12632](https://github.com/diegosouzapw/OmniRoute/pull/12632)) — thanks @HouMinXi +- **fix(db):** back-fill `last_ping_at` and `last_pinged_reset_key` on `provider_connections` during schema reconciliation so divergent lineages that skipped `123_quota_auto_ping` still accept quota auto-ping writes ([#12470](https://github.com/diegosouzapw/OmniRoute/pull/12470) — thanks @KooshaPari) +- **fix(ci):** document MIT exceptions for `@eloqnt/{config,format-json,format-po}` (next-intl transitive; locked tarballs omit `license`) and keep the A2A lifecycle vitest off the real SQLite persistence seam ([#12581](https://github.com/diegosouzapw/OmniRoute/issues/12581)) ([#12595](https://github.com/diegosouzapw/OmniRoute/pull/12595)) +- OpenCode plugin `/v1/models` catalog fetch now waits 30s by default and attaches HTTP `statusCode` on 401/5xx so host fallback plugins can hop instead of seeing an untyped AbortError/UnknownError. ([#12607](https://github.com/diegosouzapw/OmniRoute/pull/12607)) — thanks @RaviTharuma +- **CI:** the OpenAPI security-tier gate now mirrors `isAlwaysProtectedPath()` in full — it also reads `ALWAYS_PROTECTED_API_PATTERNS`, so the pattern-gated credential routes (`/api/providers/{id}/{claude,codex}-auth/{export,apply-local}`, GHSA-5926-2w35-7h4q) no longer report as unannotated. (#12605) +- **fix(api):** GET /v1/models no longer waits forever on a hung coalesced catalog rebuild; cold-path waits are bounded (`CATALOG_BUILD_TIMEOUT_MS`, default 8s) and a last-good 200 is served when the rebuild times out ([#12627](https://github.com/diegosouzapw/OmniRoute/issues/12627)). ([#12628](https://github.com/diegosouzapw/OmniRoute/pull/12628)) — thanks @RaviTharuma +- **fix(cursor):** a non-array `tool_calls` on an assistant message no longer crashes the cursor request translator with a `TypeError`; both loops now require an array ([#12689](https://github.com/diegosouzapw/OmniRoute/issues/12689)) ([#12691](https://github.com/diegosouzapw/OmniRoute/pull/12691)) — thanks @soroush5 +- **fix(chat):** Chat Completions no longer return empty `content` after a server-owned memory or skills tool; the first provider send and account/model recovery now share one pipeline so a follow-up round-trip can fill the reply ([#12696](https://github.com/diegosouzapw/OmniRoute/issues/12696)) — thanks @HouMinXi ([#12867](https://github.com/diegosouzapw/OmniRoute/pull/12867)) +- **fix(dashboard):** batch-deleting provider keys no longer toasts failure after a successful delete when the confirm button's click event is forwarded as `onAfter` ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) — thanks @HouMinXi +- **fix(glm):** drop the extra 16th argument to `createSSETransformStreamWithLogger` that TypeScript rejected (TS2554) and that never reached the TransformStream ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) ([#12770](https://github.com/diegosouzapw/OmniRoute/pull/12770)) — thanks @HouMinXi +- **fix(dashboard):** pass `nodeMap` into Runtime `QuotaGroup` so a quota monitor in error/exhausted/alerting no longer throws `ReferenceError: nodeMap is not defined`. ([#12868](https://github.com/diegosouzapw/OmniRoute/pull/12868)) — thanks @HouMinXi +- Restricted API keys whose `allowedModels` lists a combo name no longer skip every combo member at pre-dispatch (`ALL_TARGETS_SKIPPED`). Inner-target filtering still applies when the allow-list is a provider prefix or `disableNonPublicModels` is on ([#12899](https://github.com/diegosouzapw/OmniRoute/pull/12899)). — thanks @HouMinXi +- **fix(combos):** Effort-suffixed combo members inherit the base model's `model_context_overrides` row so priority order is not inverted on large requests ([#12926](https://github.com/diegosouzapw/OmniRoute/pull/12926)) — thanks @HouMinXi +- **fix(catalog):** Picker-added `customModels` enter the dispatch-time live catalog so combo members and bare inference no longer 400 ([#12934](https://github.com/diegosouzapw/OmniRoute/pull/12934)) — thanks @HouMinXi +- **fix(docker):** re-derive the Docker build's worker-pool memory budget from the MEASURED ~4.5 GB per-process RSS (the issue owner's own VPS dmesg OOM-killer reproduction) instead of the stale 2560 MB/worker inference #11419 shipped, and lower `OMNIROUTE_BUILD_WORKERS` 3 → 2 so 1 parent + 1 page-data worker (2 processes × 4.5 GB = 9 GB) fits the 12.288 GB (75%) budget on a 16 GB GitHub Actions runner — the previous default (1 parent + 2 workers = 13.5 GB) still overcommitted the runner and kept "Publish to Docker Hub" failing with `cannot allocate memory` after #11419 merged (#7518). ([#11663](https://github.com/diegosouzapw/OmniRoute/pull/11663)) +- **fix(electron):** the packaged Windows desktop build now passes the #7592 cold-restart smoke end to end. Five defects were found and fixed on the way: (1) optional-pack staging failed on any machine whose `tar` is GNU tar — it reads the drive letter in an absolute `-f C:\...` archive path as a remote rsh host (`Cannot connect to C:`), so staging now passes a bare filename with `cwd` at the tarball directory and surfaces tar stderr on failure; (2) the Electron `files` allowlist was missing `lib/loginHeaderCapture.js`, so the packaged main process crashed at startup with `Cannot find module './lib/loginHeaderCapture'`; (3) electron-builder ≥26 injects an `!**/node_modules/**` ignore into every extraResources pattern list that no positive filter can override, which silently dropped the staged runtime `node_modules` (including the better-sqlite3 N-API prebuild) from `resources/app` — a fresh v3.8.50 desktop build would have shipped with no native SQLite driver and reproduced the #7592 sql.js fallback on every machine; an `afterPack` hook now restores the staged `node_modules` after packing; (4) the packaged-app smoke harness redirected `USERPROFILE` into its temp DATA_DIR, but Electron resolves the Roaming profile from `%USERPROFILE%\AppData\Roaming\` (USERPROFILE takes precedence over the APPDATA env var) and the path service throws instead of creating it, so `requestSingleInstanceLock()` returned false and the app exited(0) silently before `app.whenReady()` — the harness now pre-creates the derived tree, and `ensureSmokeEnvDirs` is exported and unit-tested; (5) the #7592 driver assertion parsed a `[DB] Driver: ...` line that the server's primary DB path never printed (only the unused `openDatabaseAsync()` did) — `getDbInstance()` now emits the same line on open so the guard can actually assert the native driver. Also: the smoke env-allowlist unit test hardcoded POSIX paths and could only pass on Linux/macOS; expectations are now host-agnostic, with new regression tests for the USERPROFILE-derived tree and `tarPack` under absolute Windows-style paths ([#11443](https://github.com/diegosouzapw/OmniRoute/pull/11443)) — thanks @jonlwheat2-gif +- fix(sse): stop the auto-combo candidates inspector from silently dropping model-locked/cooled-down rows (#9133) ([#11994](https://github.com/diegosouzapw/OmniRoute/pull/11994)) — thanks @hartmark +- **fix(providers):** Perplexity Web no longer turns upstream stream failures into successful assistant text; pre-content failures remain eligible for fallback, partial output ends with a structured sanitized error, and failed sessions are not persisted ([#12465](https://github.com/diegosouzapw/OmniRoute/pull/12465)) +- **fix(video-bridge):** preserve chronological video and transcript fusion, source-frame timestamps, transcript provenance, full contact-sheet intervals, and pre-abort callback isolation ([#11681](https://github.com/diegosouzapw/OmniRoute/pull/11681)) +- **Z.ai Web:** HTTP 200 streams carrying an upstream error now terminate with a structured failure instead of assistant text plus a normal stop, preserving partial output while allowing pre-content combo fallback. ([#12454](https://github.com/diegosouzapw/OmniRoute/pull/12454)) +- Absorb `Error [AbortError]: request_signal_aborted` and DOMException AbortError shapes in the process-level client-abort crash guard so routine client disconnects no longer kill the server (exit code 7). ([#12165](https://github.com/diegosouzapw/OmniRoute/pull/12165)) — thanks @alvinveroy +- **fix(sse):** Treat Adapta Web `type:error` SSE events as sanitized non-stream failures instead of empty HTTP 200 completions. ([#12459](https://github.com/diegosouzapw/OmniRoute/pull/12459)) +- **fix(autoCombo):** layer 3 (`models_dev_tier`) no longer scores retired models as premium, merge cross-provider capability rows deterministically, and inherit tier scores through variant ids ([#11508](https://github.com/diegosouzapw/OmniRoute/issues/11508)). `getModelsDevTierFitness` now vetoes ids present in `config/quality/model-lifecycle.json` with status `retired` before any other signal; `loadModelCapabilities()` aggregates per `model_id` (any-non-null-true for booleans, max for `limit_context`) instead of last-write-wins over SQLite's undefined row order; a capabilities miss retries through `resolveScoresAs()`'s base id and reports `models_dev_tier:inherited`, matching layers 1–2 (#11492). Adds `getModelsDevTierFitnessWithSource()` and an `invalidateCapabilitiesCache()` test/ops hook. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) — thanks @Meet6338-X +- fix(sse): accept Claude `content:[]` + `stop_reason: max_tokens`/`tool_use` in `detectMalformedNonStream` (match `isEmptyContentResponse`) so Claude Code `/model` probes with `max_tokens: 1` no longer become a false 502 ([#11568](https://github.com/diegosouzapw/OmniRoute/pull/11568)) — thanks @Seramicx +- **fix(security):** sanitize `request.failed` diagnostics before publishing them to live dashboard listeners and replay history, while keeping status, model, provider, latency, and internal call-log diagnostics intact. ([#12469](https://github.com/diegosouzapw/OmniRoute/pull/12469)) +- **fix(pwa):** do not intercept dashboard navigations so Chrome can fall back from a stale HTTP/3 Alt-Svc advertisement (UDP :20128 is unpublished; F5 on a long-lived tab hung until a new tab opened a fresh TCP connection). ([#12767](https://github.com/diegosouzapw/OmniRoute/pull/12767)) — thanks @HouMinXi +- **fix(executors):** handle DuckDuckGo ERR_BN_LIMIT (418) without retrying — when the upstream returns `418 ERR_BN_LIMIT` (rate-limit/ban), the executor now returns the error immediately instead of burning another VQD acquisition that would only count against the IP limit. The retry logic for `418 ERR_CHALLENGE` (unsolved challenge) remains unchanged. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) ([#12110](https://github.com/diegosouzapw/OmniRoute/pull/12110)) — thanks @oyi77 / @Meet6338-X +- Fixed DuckDuckGo streaming responses losing JSON lines and UTF-8 characters split across network chunks. ([#11528](https://github.com/diegosouzapw/OmniRoute/pull/11528)) — thanks @pacocartones +- Harden generated Cloudflare, Vercel, and Deno relays by stripping hop-by-hop, framing, and proxy authentication headers before forwarding requests upstream. ([#11533](https://github.com/diegosouzapw/OmniRoute/pull/11533)) — thanks @pacocartones +- **feat(grok-cli):** Provider Limits shows grok-cli banked reset credits from `GetRemainingResets` (including a real zero; a failed RPC omits the row) and the existing View credits button now calls `ConsumerUiSvc/RedeemReset` for grok-cli. Live tokens use nested fields 10/20/30 (id + Timestamp), not compact 1/2/3. ([#12805](https://github.com/diegosouzapw/OmniRoute/pull/12805)) — thanks @HouMinXi +- **fix(memory):** Embedding Model Quick select, Embedding Source remote dropdown, and Rerank selector now list every configured provider with embedding/rerank support instead of only chat-catalog text matches plus OpenRouter live discovery; a generic OpenAI-compatible `/embeddings` + Cohere-compatible `/rerank` runtime fallback resolves any configured chat provider's embedding/rerank endpoint, so unlisted providers no longer fail with "Unknown embedding provider"; both memory selectors gained a free-text model override ([#11390](https://github.com/diegosouzapw/OmniRoute/pull/11390)) — thanks @rqzbeh +- Harden SQLite upgrades around the historical migration-074 version collision: missing discovery and inspector tables are replayed atomically, pre-existing databases (including setup-created skeletons) receive reusable content-addressed safety snapshots, and Node test/eval probes without `DATA_DIR` are isolated from the operator database. ([#12435](https://github.com/diegosouzapw/OmniRoute/pull/12435)) +- **fix(dashboard):** Moonshot/Kimi Open Platform voucher and cash leftover percentages follow the bucket balance, so an empty wallet no longer paints those rows as 100% while Available is 0% ([#12733](https://github.com/diegosouzapw/OmniRoute/pull/12733)) — thanks @HouMinXi +- **fix(combo):** return non-retryable HTTP 400 when all candidates for a pinned native Codex turn are unavailable due to model-scoped lockout, terminating the turn cleanly while preserving turn continuity and enabling standard Combo routing on subsequent turns ([#12240](https://github.com/diegosouzapw/OmniRoute/pull/12240)) — thanks @mdigitalbh81 +- Fixed auto-router strategies preserving the selected connection through ranking and dispatch when multiple connections share the same provider and model. ([#11530](https://github.com/diegosouzapw/OmniRoute/pull/11530)) — thanks @pacocartones +- Preserve Chat Completions JSON-object response formats when translating requests to the Responses API. ([#11529](https://github.com/diegosouzapw/OmniRoute/pull/11529)) — thanks @pacocartones +- Fix idempotency fingerprints so Responses API requests include `input` and other semantic generation fields using stable serialization, while excluding credentials and request metadata. ([#11532](https://github.com/diegosouzapw/OmniRoute/pull/11532)) — thanks @pacocartones +- **fix(api):** Generated API CLI commands now enforce required OpenAPI request bodies; Combo test commands forward the required `comboName` body, while API keys created by older writers after migration 149 preserve legacy allow-all Combo access without widening explicit empty allowlists — thanks @marcelokarval ([#12070](https://github.com/diegosouzapw/OmniRoute/pull/12070)) +- **fix(grok-web):** treat upstream streaming failures as failures instead of successful assistant text: error-only streams now fail readiness with HTTP 502, while failures after legitimate content preserve that partial output and terminate through the sanitized stream failure path without a normal `stop` completion. ([#12458](https://github.com/diegosouzapw/OmniRoute/pull/12458)) +- HuggingChat now turns HTTP 200 JSONL generation failures into a sanitized 502 before content, or a fixed public stream failure after partial output, so fallback and request persistence no longer record a false successful stop. ([#12456](https://github.com/diegosouzapw/OmniRoute/pull/12456)) +- **fix(providers):** keep 1min.ai HTTP 200 stream errors out of assistant content, preserve partial output, and expose sanitized terminal errors so pre-content failures can fall back. ([#12466](https://github.com/diegosouzapw/OmniRoute/pull/12466)) +- **fix(sessions):** preserve legacy Sessions data when the additive exclusive-session projection is unavailable, returning an empty projection and warning only once per contiguous outage instead of failing the endpoint; the Sessions badge intentionally reflects the merged legacy and exclusive row count introduced by [#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389) ([#11469](https://github.com/diegosouzapw/OmniRoute/pull/11469)) — thanks @KaspaPulse +- Render Nimble Search with the generic provider icon and serve Opper's proven logo locally. ([#12415](https://github.com/diegosouzapw/OmniRoute/pull/12415)) +- **fix(quality):** `config/quality/provider-assets-provenance.jsonl` was stale against the physical `public/providers/` tree (still listing 225 assets and 5 duplicate-content groups after several provider-retirement PRs removed 85 icon files), which would have made `check:provider-asset-provenance` fail on the next full run. Reconciled the manifest to the real 142-file snapshot (142 asset records, 1 duplicate-content group, 2 newly-registered files marked `unresolved` pending provenance research), repinned `auditedCommit`, and updated the regression test's hardcoded counts to match. ([#11876](https://github.com/diegosouzapw/OmniRoute/pull/11876)) +- fix(ui): wrap quota card footer action buttons to prevent clipping Refresh button on narrow cards — when extra action buttons such as `View credits` are present on Codex cards, allow the footer row to flex-wrap with `shrink-0` buttons so `Refresh now` remains visible and clickable across all responsive card widths ([#11464](https://github.com/diegosouzapw/OmniRoute/pull/11464)) — thanks @adevwithpurpose +- Keep Antigravity Gemini usable when the same connection's Claude weekly quota is empty; generic quota cache stays per-connection for every other provider. ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) — thanks @HouMinXi +- Fixed Tailscale tunnel setup to reject ports outside the valid 1-65535 range and safely install Windows MSI packages from paths containing apostrophes. ([#11534](https://github.com/diegosouzapw/OmniRoute/pull/11534)) — thanks @pacocartones +- Classify Cloudflared and Tailscale process-management routes as local-only and spawn-capable while preserving remote access to read-only tunnel status endpoints. ([#11531](https://github.com/diegosouzapw/OmniRoute/pull/11531)) — thanks @pacocartones +- Electron release workflow: the `publish-npm` job now grants `actions: read` to the reusable `npm-publish.yml` it calls (its `publish` job requests it), which is what made GitHub refuse the whole v3.8.50 run at startup and ship the release with zero desktop assets; a `workflow_dispatch` now builds the requested tag instead of the dispatching branch and can skip the npm leg (`publish_npm=false`) when only re-attaching assets ([#11974](https://github.com/diegosouzapw/OmniRoute/pull/11974)) +- **fix(dashboard):** The Combos page usage guide now reads its dismissal through `useSyncExternalStore` instead of correcting SSR state inside an effect, removing an extra commit of the page tree on every load (and the `react-hooks/set-state-in-effect` error it raised). ([#12671](https://github.com/diegosouzapw/OmniRoute/pull/12671)) +- Electron release: `electron/package-lock.json` regained the optional `electron-builder-squirrel-windows` subtree (13 entries) that `npm ci` had been refusing as out of sync, `electron-release.yml` gained a `build_ref` dispatch input and stops regenerating release notes on a re-attach dispatch, and the npm publish workflow attaches the SBOM to the GitHub Release on dispatch publishes too — so the v3.8.51 tag ships every desktop asset and the SBOM like v3.8.49 did ([#12022](https://github.com/diegosouzapw/OmniRoute/pull/12022)) +- Drained the reds every PR against `release/v3.8.51` was born with: documented the three service-worker build-id variables, registered the six retirement/tunnel tests with the mutation gate, approved `eslint-plugin-react-hooks` in the dependency allowlist, added the six `combo.sort.*` strings to `vi` and `pt-BR`, pointed the ChatGPT Web doc at the real migration-168 test, worded the g4f hint around the member key, and realigned four tests to the retired-provider catalog and the legacy-schema fixtures the retirement migrations touch. ([#11940](https://github.com/diegosouzapw/OmniRoute/pull/11940)) +- **fix(providers):** Claude, Grok, LMArena, Notion, and Perplexity web-cookie transports now use pooled `wreq-js` 3.2 instead of the native sidecar, with all nine supported bindings pinned and audited, and the applicable platform binding plus native-license evidence included in each release artifact ([#12429](https://github.com/diegosouzapw/OmniRoute/pull/12429), supersedes [#11753](https://github.com/diegosouzapw/OmniRoute/pull/11753)). +- **fix(providers):** Zed Hosted streaming failures now trigger fallback before content and end partial streams with a sanitized structured error instead of fake assistant text and a normal-success stop. ([#12455](https://github.com/diegosouzapw/OmniRoute/pull/12455)) +- **fix(test):** capture hook result after render (direct commit `b85d0abb0b`) +- **fix(sync):** repair the two regressions the v3.8.50 sync-back left on release/v3.8.51 (direct commit `5ade9e0851`) +- **revert(models):** drop the memo reset hook ported from #10055 (direct commit `282de841ae`) +- **fix(typecheck):** guard the optional connectionId before the reactive model sync (direct commit `f2825bad75`) +- **fix(quota-share):** release the winner's reserved in-flight slot (#11371) ([#11408](https://github.com/diegosouzapw/OmniRoute/pull/11408)) — thanks @oyi77 +- **fix(providers):** stop silently dropping reasoning effort on opencode families (#10788) ([#11409](https://github.com/diegosouzapw/OmniRoute/pull/11409)) — thanks @oyi77 +- **fix(combo):** enable genuine quota-aware routing for generic providers (antigravity, claude, etc.) ([#11411](https://github.com/diegosouzapw/OmniRoute/pull/11411)) — thanks @HouMinXi +- **fix(oauth):** allow expired token retry before deactivation and activate expired retry loop (#P0) ([#11414](https://github.com/diegosouzapw/OmniRoute/pull/11414)) — thanks @HouMinXi +- **perf(executors):** lazy-load the executor registry — defer class imports + construction to first use (#11220) ([#11421](https://github.com/diegosouzapw/OmniRoute/pull/11421)) — thanks @oyi77 +- **fix:** make release/v3.8.50 CI gates green (run 32786966560 root causes) ([#11450](https://github.com/diegosouzapw/OmniRoute/pull/11450)) — thanks @jonlwheat2-gif +- **fix(sse):** exact-domain cookie match and origin-equality URL assertions ([#11452](https://github.com/diegosouzapw/OmniRoute/pull/11452)) — thanks @hartmark +- **fix(providers):** send Responses API payload during OpenAI-compatible validation ([#11454](https://github.com/diegosouzapw/OmniRoute/pull/11454)) — thanks @tuandinh0801 +- **fix(command-code):** fallback to /alpha/generate for Go plan without Provider API access ([#11455](https://github.com/diegosouzapw/OmniRoute/pull/11455)) — thanks @Chewji9875 +- **fix(responses-continuation):** fail closed on a log-truncated stored input/output array ([#11473](https://github.com/diegosouzapw/OmniRoute/pull/11473)) — thanks @hartmark +- **fix(cli):** write jcode settings as [providers.omniroute] in config.toml ([#11484](https://github.com/diegosouzapw/OmniRoute/pull/11484)) — thanks @hongnoul +- **fix(logging):** raise chat-log array truncation cap to 1000 and unify duplicate implementations ([#11499](https://github.com/diegosouzapw/OmniRoute/pull/11499)) — thanks @hartmark +- **fix:** restore live websocket URL helpers ([#11509](https://github.com/diegosouzapw/OmniRoute/pull/11509)) — thanks @PauloFH +- **perf(core):** eliminate async waterfalls across home, cache, and db operations (#11396) ([#11512](https://github.com/diegosouzapw/OmniRoute/pull/11512)) — thanks @HouMinXi +- **fix(kiro):** add runtime.us-east-1.kiro.dev as first-attempt endpoint ([#11517](https://github.com/diegosouzapw/OmniRoute/pull/11517)) — thanks @rifqiawl +- **fix(providers):** remove duplicate 128-tool truncation in OpencodeExecutor (#11444) ([#11518](https://github.com/diegosouzapw/OmniRoute/pull/11518)) — thanks @HouMinXi +- **fix(providers):** poll onboardUser LRO before classifying BYOP ([#11519](https://github.com/diegosouzapw/OmniRoute/pull/11519)) — thanks @rifqiawl +- **fix(translator):** preserve exact tool name casing for Claude tool_use payloads (#11487) ([#11520](https://github.com/diegosouzapw/OmniRoute/pull/11520)) — thanks @HouMinXi +- **fix(kimi-web):** align health probe with executor domain ([#11521](https://github.com/diegosouzapw/OmniRoute/pull/11521)) — thanks @pacocartones +- **fix(cli):** preserve existing admin password during setup ([#11522](https://github.com/diegosouzapw/OmniRoute/pull/11522)) — thanks @pacocartones +- **fix(mitm):** keep Windows DNS elevation off the POSIX sudo path (#11430) ([#11542](https://github.com/diegosouzapw/OmniRoute/pull/11542)) — thanks @official-burak +- **fix(sse):** scale chat admission by ingest byte budget instead of a fixed request count ([#11548](https://github.com/diegosouzapw/OmniRoute/pull/11548)) — thanks @TheDemonTuan +- **fix:** accept Gemini Web cookie exports ([#11554](https://github.com/diegosouzapw/OmniRoute/pull/11554)) — thanks @rafacpti23 +- **fix(i18n):** complete Brazilian Portuguese translations ([#11558](https://github.com/diegosouzapw/OmniRoute/pull/11558)) — thanks @rafacpti23 +- **fix(sse):** explicit types for openai-responses pureHelpers — clears last failing core typecheck gate ([#11567](https://github.com/diegosouzapw/OmniRoute/pull/11567)) — thanks @jonlwheat2-gif +- **fix(opencode):** hydrate Proxy Pool references for no-auth connections ([#11584](https://github.com/diegosouzapw/OmniRoute/pull/11584)) — thanks @AStupidBear +- **fix(ci):** resolve inherited release/v3.8.51 base-red CI failures ([#11608](https://github.com/diegosouzapw/OmniRoute/pull/11608)) — thanks @jonlwheat2-gif +- **fix(db):** fallback load STORAGE_ENCRYPTION_KEY from env files ([#11614](https://github.com/diegosouzapw/OmniRoute/pull/11614)) — thanks @benzntech +- **fix(providers):** mark Brave Search as no longer having a free tier ([#11621](https://github.com/diegosouzapw/OmniRoute/pull/11621)) — thanks @turbolego +- **fix:** pass tool result content through as string in OpenAI/Claude -> Gemini translation ([#11624](https://github.com/diegosouzapw/OmniRoute/pull/11624)) — thanks @Hsia97 +- **fix(sse):** scope credential health to exact key ([#11628](https://github.com/diegosouzapw/OmniRoute/pull/11628)) — thanks @KaspaPulse +- **fix(install):** guard prepare script against missing husky ([#11634](https://github.com/diegosouzapw/OmniRoute/pull/11634)) — thanks @Prajeeth-12 +- **fix(security):** redact NVIDIA_API_KEY at the log sink in the ad-hoc diag script ([#11639](https://github.com/diegosouzapw/OmniRoute/pull/11639)) +- **fix(types):** clear the two type regressions red on release/v3.8.51 ([#11640](https://github.com/diegosouzapw/OmniRoute/pull/11640)) — thanks @jonlwheat2-gif +- **fix(build):** resolve workspace: protocol dependencies to concrete semver versions during prepublish (#11540) ([#11641](https://github.com/diegosouzapw/OmniRoute/pull/11641)) — thanks @HouMinXi +- **fix(search):** properly resolve configured search connection in /v1/responses pipeline (#11524) ([#11642](https://github.com/diegosouzapw/OmniRoute/pull/11642)) — thanks @HouMinXi +- **fix(authz):** trust cf-connecting-ip for via-proxy detection when peer is Cloudflare edge (#11514) ([#11643](https://github.com/diegosouzapw/OmniRoute/pull/11643)) — thanks @HouMinXi +- **fix(security):** compare the OIDC state cookie in constant time ([#11646](https://github.com/diegosouzapw/OmniRoute/pull/11646)) +- **fix(security):** namespace the dedup hash by the calling API key ([#11649](https://github.com/diegosouzapw/OmniRoute/pull/11649)) +- **fix(models):** gate the Codex-native catalog loop on prefix mode (#11632) ([#11662](https://github.com/diegosouzapw/OmniRoute/pull/11662)) — thanks @xiaoyaner0201 +- **fix(dashboard):** project Codex routing quota in account pools ([#11666](https://github.com/diegosouzapw/OmniRoute/pull/11666)) — thanks @KaspaPulse +- **fix(ddgw):** normaliza system no upstream da release v3.8.51 ([#11692](https://github.com/diegosouzapw/OmniRoute/pull/11692)) — thanks @RhianB14 +- **fix(memory):** honor category filter in GET /api/memory ([#11699](https://github.com/diegosouzapw/OmniRoute/pull/11699)) — thanks @Sabeekhann +- **fix(ci):** detect stale workspace lockfile entries ([#11714](https://github.com/diegosouzapw/OmniRoute/pull/11714)) — thanks @pacocartones +- **fix(ci):** stamp dist/BUILD_SHA before the npm publish provenance gate ([#11721](https://github.com/diegosouzapw/OmniRoute/pull/11721)) +- **fix(docker):** decouple the best-effort Bun image from the release manifest ([#11724](https://github.com/diegosouzapw/OmniRoute/pull/11724)) +- **perf(compression):** avoid cloning memo misses twice ([#11727](https://github.com/diegosouzapw/OmniRoute/pull/11727)) — thanks @RaviTharuma +- **fix(docker):** let the bun digest artifact be absent, not fatal ([#11740](https://github.com/diegosouzapw/OmniRoute/pull/11740)) +- **fix(shared):** block cloud-metadata hosts under default remote-image guard ([#11755](https://github.com/diegosouzapw/OmniRoute/pull/11755)) — thanks @santosraju99-hub +- **fix(api):** preserve caller-provided X-Correlation-Id on chat completions ([#11760](https://github.com/diegosouzapw/OmniRoute/pull/11760)) — thanks @aniruddhaadak80 +- **fix(resilience):** lock permanently retired models instead of short backoff (Gemini ban prevention) ([#11762](https://github.com/diegosouzapw/OmniRoute/pull/11762)) — thanks @turbolego +- **fix:** mark Vercel AI Gateway as passthroughModels ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **fix(resilience):** stop hammering permanently-moved endpoints and billing-suspended accounts ([#11774](https://github.com/diegosouzapw/OmniRoute/pull/11774)) — thanks @turbolego +- **fix(lease):** remove global static reservation and gate routing on live active lease occupancy ([#11775](https://github.com/diegosouzapw/OmniRoute/pull/11775)) — thanks @TheDemonTuan +- **fix(ci):** size the install-upgrade gate to a measured run, and log the pack cost ([#11776](https://github.com/diegosouzapw/OmniRoute/pull/11776)) +- **fix(pwa):** stop serving the stale shell after deploys ([#11779](https://github.com/diegosouzapw/OmniRoute/pull/11779)) — thanks @HouMinXi +- **fix(combo):** lock GitHub models rejected as "not supported" for future requests ([#11781](https://github.com/diegosouzapw/OmniRoute/pull/11781)) — thanks @turbolego +- **fix(cli):** probe both IPv4 and IPv6 loopback for server readiness (#11766) ([#11794](https://github.com/diegosouzapw/OmniRoute/pull/11794)) — thanks @vermasomesh835 +- **fix(sse):** preserve CLIProxyAPI wrappers per connection ([#11797](https://github.com/diegosouzapw/OmniRoute/pull/11797)) — thanks @RaviTharuma +- **fix(providers):** honor model auto-fetch opt-in on create ([#11798](https://github.com/diegosouzapw/OmniRoute/pull/11798)) — thanks @RaviTharuma +- **fix(dashboard):** make model auto-fetch order independent ([#11805](https://github.com/diegosouzapw/OmniRoute/pull/11805)) — thanks @RaviTharuma +- **fix(docker):** make GHA cache exports best effort ([#11806](https://github.com/diegosouzapw/OmniRoute/pull/11806)) — thanks @RaviTharuma +- **fix(kiro):** do not permanently ban on 'User is not authorized to make this call' ([#11809](https://github.com/diegosouzapw/OmniRoute/pull/11809)) — thanks @Deftera186 +- **fix(sse):** keep cache-write tokens in OpenAI-shaped usage ([#11814](https://github.com/diegosouzapw/OmniRoute/pull/11814)) — thanks @TheDemonTuan +- **fix(docs):** resolve relative markdown and wiki links across Fumadocs and GitHub wiki ([#11834](https://github.com/diegosouzapw/OmniRoute/pull/11834)) — thanks @AndrianBalanescu +- **fix(chat):** preserve unstripped model string for passthrough provider routing ([#11840](https://github.com/diegosouzapw/OmniRoute/pull/11840)) — thanks @raheemuddin786 +- **fix(mcp):** avoid async initialization inside sync esm wrappers (#11569) ([#11849](https://github.com/diegosouzapw/OmniRoute/pull/11849)) — thanks @HouMinXi +- **fix(sse):** preserve coherent markdown token boundaries across stream translation chunks (#11606) ([#11851](https://github.com/diegosouzapw/OmniRoute/pull/11851)) — thanks @HouMinXi +- **fix(ci):** run the install-upgrade gate on disk, not on the /tmp tmpfs ([#11855](https://github.com/diegosouzapw/OmniRoute/pull/11855)) +- **fix(skills):** expand shorthand property types in injected tool schemas ([#11857](https://github.com/diegosouzapw/OmniRoute/pull/11857)) — thanks @alvinveroy +- **fix(ci):** publish npm from a hosted runner so provenance is accepted ([#11877](https://github.com/diegosouzapw/OmniRoute/pull/11877)) +- **fix(lint):** pin react hooks plugin to 7.0.1 ([#11886](https://github.com/diegosouzapw/OmniRoute/pull/11886)) +- **fix(ci):** surface and clear release-green ESLint failure ([#11890](https://github.com/diegosouzapw/OmniRoute/pull/11890)) — thanks @MumuTW +- **fix(guardrails):** prevent duplicate prompt-injection-guard log output ([#11936](https://github.com/diegosouzapw/OmniRoute/pull/11936)) — thanks @patrykkopycinski +- **fix(auth):** downgrade expected transient states from warn to debug ([#11937](https://github.com/diegosouzapw/OmniRoute/pull/11937)) — thanks @patrykkopycinski +- **fix(codex):** fail over image generation for imported free plans ([#11948](https://github.com/diegosouzapw/OmniRoute/pull/11948)) — thanks @Bl0ck154 +- **fix(codex):** normalize non-stream responses ([#11951](https://github.com/diegosouzapw/OmniRoute/pull/11951)) — thanks @Bl0ck154 +- **fix(images):** forward Antigravity image size ([#11952](https://github.com/diegosouzapw/OmniRoute/pull/11952)) — thanks @Bl0ck154 +- **fix(db):** invalidate connection cache after upsert ([#11953](https://github.com/diegosouzapw/OmniRoute/pull/11953)) — thanks @Bl0ck154 +- **fix(codex):** restore imported account state ([#11954](https://github.com/diegosouzapw/OmniRoute/pull/11954)) — thanks @Bl0ck154 +- **fix(sse):** give extended-thinking targets the reasoning readiness budget ([#11959](https://github.com/diegosouzapw/OmniRoute/pull/11959)) — thanks @abhisheksharma2411 +- **fix(dashboard):** use opaque background and readable text color on cost chart tooltips ([#11960](https://github.com/diegosouzapw/OmniRoute/pull/11960)) — thanks @ZaimMarzuki +- **fix(antigravity):** send complete loadCodeAssist metadata (ideType/platform/pluginType as numeric enums) ([#11969](https://github.com/diegosouzapw/OmniRoute/pull/11969)) — thanks @rifqiawl +- **fix(core):** resolve DB init race condition and reasoning translation ([#12003](https://github.com/diegosouzapw/OmniRoute/pull/12003)) — thanks @raheemuddin786 +- **fix(admission):** exclude reclaimable page cache from the cgroup pressure ratio ([#12017](https://github.com/diegosouzapw/OmniRoute/pull/12017)) — thanks @HouMinXi +- **fix(build):** prune non-production trees in NFT trace excludes and tsconfig ([#12028](https://github.com/diegosouzapw/OmniRoute/pull/12028)) — thanks @Chewji9875 +- **fix(migrations):** add renamed migration compatibility for 056/073/077/101 ([#12036](https://github.com/diegosouzapw/OmniRoute/pull/12036)) — thanks @oyi77 +- **fix(sse):** default crash-guard logger to console.warn, not console ([#12042](https://github.com/diegosouzapw/OmniRoute/pull/12042)) — thanks @HouMinXi +- **fix(catalog):** derive vision/modalities for built-in auto combos from effective target pool ([#12046](https://github.com/diegosouzapw/OmniRoute/pull/12046)) — thanks @Prajeeth-12 +- **fix(release):** the packaged-app smoke verifies the database opened, not a driver line the primary path never prints (twin of #12032) ([#12047](https://github.com/diegosouzapw/OmniRoute/pull/12047)) +- **fix(barrel):** migrate src/app/ off the @/lib/localDb barrel import (#11795 Phase 2) ([#12051](https://github.com/diegosouzapw/OmniRoute/pull/12051)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate src/lib/ off the localDb barrel to direct db imports (#59) ([#12052](https://github.com/diegosouzapw/OmniRoute/pull/12052)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate open-sse, src/shared, src/sse, src/models, src/domain off the @/lib/localDb barrel import (#11795 Phase 4) ([#12053](https://github.com/diegosouzapw/OmniRoute/pull/12053)) — thanks @jonlwheat2-gif +- **fix(barrel):** delete the @/lib/localDb barrel — every consumer migrated (#11795 Phase 5) ([#12055](https://github.com/diegosouzapw/OmniRoute/pull/12055)) — thanks @jonlwheat2-gif +- **fix(radar):** restore D12 public boundary ([#12057](https://github.com/diegosouzapw/OmniRoute/pull/12057)) +- **fix(dev):** bound webpack and Tailwind scans ([#12075](https://github.com/diegosouzapw/OmniRoute/pull/12075)) — thanks @backryun +- **fix(dev):** isolate root layout settings reads ([#12076](https://github.com/diegosouzapw/OmniRoute/pull/12076)) — thanks @backryun +- **fix(dev):** reduce instrumentation executor fan-out (phase 3) ([#12078](https://github.com/diegosouzapw/OmniRoute/pull/12078)) — thanks @backryun +- **fix(dev):** make logging resources HMR-singleton ([#12079](https://github.com/diegosouzapw/OmniRoute/pull/12079)) — thanks @backryun +- **fix(sse):** keep unavailable forced connections scoped ([#12080](https://github.com/diegosouzapw/OmniRoute/pull/12080)) — thanks @keeltrace +- **fix(dev):** isolate batch dispatch from instrumentation ([#12081](https://github.com/diegosouzapw/OmniRoute/pull/12081)) — thanks @backryun +- **fix(ci):** reconcile release test contract drift ([#12082](https://github.com/diegosouzapw/OmniRoute/pull/12082)) — thanks @backryun +- **fix(ci):** accept CVE-2025-68121 in the prebuilt tls-client .so, auto-close base-red issues, guard Scorecard on the default branch ([#12085](https://github.com/diegosouzapw/OmniRoute/pull/12085)) +- **fix(db):** drop three consumer-less 1proxy exports — dead-code base-red on release/v3.8.51 after the barrel deletion (#12055) ([#12087](https://github.com/diegosouzapw/OmniRoute/pull/12087)) +- **fix(api):** clear the six API-route TypeScript regressions the new gate landed red on ([#12094](https://github.com/diegosouzapw/OmniRoute/pull/12094)) +- **fix(release):** never let the tag-push Create Release append auto notes to the curated body ([#12096](https://github.com/diegosouzapw/OmniRoute/pull/12096)) +- **fix(sse):** honor CLIProxyAPI environment API key ([#12099](https://github.com/diegosouzapw/OmniRoute/pull/12099)) — thanks @RaviTharuma +- **fix(oauth):** bind Google refresh to the client that issued the token ([#12106](https://github.com/diegosouzapw/OmniRoute/pull/12106)) — thanks @HouMinXi +- **fix(dashboard):** make RequestLoggerDetail loadable outside Next — CSS via globals.css + CJS/ESM interop (#11703 base-reds) ([#12114](https://github.com/diegosouzapw/OmniRoute/pull/12114)) +- **fix(leases):** project status lease row to lease columns so joined connection PII never escapes ([#12115](https://github.com/diegosouzapw/OmniRoute/pull/12115)) — thanks @geek007git +- **fix(plugins):** do not kill the plugin process when a fire-and-forget hook times out ([#12116](https://github.com/diegosouzapw/OmniRoute/pull/12116)) — thanks @geek007git +- **fix(guardrails):** restore injection-guard logging on middleware-only routes ([#12117](https://github.com/diegosouzapw/OmniRoute/pull/12117)) — thanks @geek007git +- **fix(plugins):** refresh stored manifest from disk on activate so new hook fields reach existing installs ([#12120](https://github.com/diegosouzapw/OmniRoute/pull/12120)) — thanks @geek007git +- **fix(codex):** preserve existing provider state when bulk-import upserts a matching connection ([#12122](https://github.com/diegosouzapw/OmniRoute/pull/12122)) — thanks @geek007git +- **fix(ci):** clear the two base-reds the 2026-08-30 merge batch left on release/v3.8.51 (round 3) ([#12123](https://github.com/diegosouzapw/OmniRoute/pull/12123)) +- **fix(sse):** strip type:'custom' from Claude tools on agentrouter dispatch ([#12126](https://github.com/diegosouzapw/OmniRoute/pull/12126)) — thanks @wahidsadik371-coder +- **fix(sse):** stop advertising video providers the dispatcher cannot run ([#12131](https://github.com/diegosouzapw/OmniRoute/pull/12131)) — thanks @quiterunner-commits +- **fix(search):** treat HTTP 432 and plan limit errors as transient cooldown ([#12139](https://github.com/diegosouzapw/OmniRoute/pull/12139)) — thanks @HouMinXi +- **fix(sse):** refuse an AI Horde queue that cannot fit the request budget ([#12143](https://github.com/diegosouzapw/OmniRoute/pull/12143)) — thanks @quiterunner-commits +- **fix(ci):** clear the base-reds the afternoon merge batch left on release/v3.8.51 (round 5: provider count 352, TS2554/TS2677) ([#12144](https://github.com/diegosouzapw/OmniRoute/pull/12144)) +- **fix(translator):** strip neutral tool_choice when tools absent in Responses-to-Chat (#12141) ([#12166](https://github.com/diegosouzapw/OmniRoute/pull/12166)) — thanks @HouMinXi +- **fix(guardrails):** pass providerId to getResolvedModelCapabilities in checkComboVision (#12112) ([#12169](https://github.com/diegosouzapw/OmniRoute/pull/12169)) — thanks @HouMinXi +- **fix(translator):** strip plaintext reasoning content for opaque responses backends (#12128) ([#12171](https://github.com/diegosouzapw/OmniRoute/pull/12171)) — thanks @HouMinXi +- **perf(stream):** compile hot-path regexes once, bound token caches, fix quadratic buffering ([#12179](https://github.com/diegosouzapw/OmniRoute/pull/12179)) — thanks @opensource-elearning +- **fix:** resolve compression worker file using runtime anchors instead of… ([#12183](https://github.com/diegosouzapw/OmniRoute/pull/12183)) — thanks @NightStalker-87 +- **fix:** prevent Claude Code session kills via liveness-aware readiness + auto model echo ([#12189](https://github.com/diegosouzapw/OmniRoute/pull/12189)) — thanks @opensource-elearning +- **fix(translator):** preserve falsy primitive values in Gemini and Antigravity function response results ([#12191](https://github.com/diegosouzapw/OmniRoute/pull/12191)) — thanks @Siva010 +- **fix(oauth):** mark empty Antigravity projectId as degraded and clear stale errors (#11284) ([#12205](https://github.com/diegosouzapw/OmniRoute/pull/12205)) — thanks @HouMinXi +- **fix(providers):** gate the Codex auto-ping usage read on the shared quota throttle ([#12209](https://github.com/diegosouzapw/OmniRoute/pull/12209)) — thanks @abhisheksharma2411 +- **fix(combos):** prioritize SQLite row id over inner JSON id and notify delete errors ([#12213](https://github.com/diegosouzapw/OmniRoute/pull/12213)) — thanks @HouMinXi +- **fix(providers):** mark featherless.ai as no longer having a free tier (`hasFree: false`) ([#12216](https://github.com/diegosouzapw/OmniRoute/pull/12216)) — thanks @turbolego +- **fix(deepseek-web):** stop Turbopack dev panic in the PoW worker path resolver ([#12221](https://github.com/diegosouzapw/OmniRoute/pull/12221)) — thanks @hartmark +- **fix(chat-admission):** clarify local 503 source ([#12223](https://github.com/diegosouzapw/OmniRoute/pull/12223)) — thanks @keeltrace +- **fix(dev):** silence webpack runtime module warnings ([#12228](https://github.com/diegosouzapw/OmniRoute/pull/12228)) — thanks @backryun +- **fix(db):** use module.require for CommonJS runtime driver loading ([#12230](https://github.com/diegosouzapw/OmniRoute/pull/12230)) — thanks @raheemuddin786 +- **fix(memory):** honest probe-driven FTS5 keyword status + memory_id rowid sync ([#12231](https://github.com/diegosouzapw/OmniRoute/pull/12231)) — thanks @raheemuddin786 +- **fix(system):** propagate abort signal to stream reader in HTTP version checks ([#12232](https://github.com/diegosouzapw/OmniRoute/pull/12232)) — thanks @raheemuddin786 +- **fix(resilience):** isolate local host execution errors from provider circuit breakers ([#12233](https://github.com/diegosouzapw/OmniRoute/pull/12233)) — thanks @raheemuddin786 +- **fix(sse):** sort injected tools deterministically for prompt caching ([#12234](https://github.com/diegosouzapw/OmniRoute/pull/12234)) — thanks @raheemuddin786 +- **fix(combo):** always clear the loop-safety timer, not just on the happy path (#11804) ([#12245](https://github.com/diegosouzapw/OmniRoute/pull/12245)) +- **fix(quality):** base-red pair — stryker tap registration + turn-pin suites aligned to the window gate ([#12255](https://github.com/diegosouzapw/OmniRoute/pull/12255)) — thanks @backryun +- **fix(dev):** qualify Turbopack runtime boundaries ([#12258](https://github.com/diegosouzapw/OmniRoute/pull/12258)) — thanks @backryun +- **fix(combo):** fall back to full pool when collapsed sole survivor is context-too-small ([#12278](https://github.com/diegosouzapw/OmniRoute/pull/12278)) — thanks @opensource-elearning +- **fix(combo):** bound the pre-dispatch unavailable skip so a stale label cannot dark a pool (#12168) ([#12285](https://github.com/diegosouzapw/OmniRoute/pull/12285)) +- **fix(security):** strip Qwen/Alibaba console-session cookies from provider API responses ([#12287](https://github.com/diegosouzapw/OmniRoute/pull/12287)) +- **fix(usage):** console-aware Token Plan guidance + subscription hint on bailian 401 ([#12288](https://github.com/diegosouzapw/OmniRoute/pull/12288)) +- **fix(resourcePressure):** log numeric detail on every rejection, recover faster ([#12293](https://github.com/diegosouzapw/OmniRoute/pull/12293)) — thanks @hartmark +- **fix(release):** drain the 2026-09-01 base-red window — passthrough usage regression + radarPage i18n keys ([#12327](https://github.com/diegosouzapw/OmniRoute/pull/12327)) +- **fix(ci):** openapi-security-tiers checker must honor routeGuard patterns + imported prefixes ([#12350](https://github.com/diegosouzapw/OmniRoute/pull/12350)) — thanks @ggiak +- **perf(ci):** cache node_modules in the npm-ci-retry composite (#8084 D3) ([#12408](https://github.com/diegosouzapw/OmniRoute/pull/12408)) +- **revert(ui):** point CTAs back at their real destinations ([#12410](https://github.com/diegosouzapw/OmniRoute/pull/12410)) +- **fix(release):** drain the 2026-09-02 base-red — rerank-providers import + api-typecheck baseline ratchet ([#12414](https://github.com/diegosouzapw/OmniRoute/pull/12414)) +- **fix(types):** make system prompt injection noImplicitAny-safe ([#12416](https://github.com/diegosouzapw/OmniRoute/pull/12416)) +- **fix(quality):** validate typecheck baseline schema ([#12419](https://github.com/diegosouzapw/OmniRoute/pull/12419)) +- **fix(memory):** resolve rerank provider node cache import ([#12421](https://github.com/diegosouzapw/OmniRoute/pull/12421)) +- **fix(ci):** repair release-root regressions — pack dedup, web-session syntax, uc-image ids ([#12423](https://github.com/diegosouzapw/OmniRoute/pull/12423)) — thanks @backryun +- **fix(providers):** separate MaxAI and UC credential contracts ([#12431](https://github.com/diegosouzapw/OmniRoute/pull/12431)) +- **fix(cli):** preserve Claude settings on config set ([#12432](https://github.com/diegosouzapw/OmniRoute/pull/12432)) — thanks @KrzysiekSko +- **fix(providers):** repair the maxai credential block truncated by merge auto-resolve ([#12433](https://github.com/diegosouzapw/OmniRoute/pull/12433)) +- **fix(quality):** record the 2026-09-02 merged growth in the file-size baseline ([#12434](https://github.com/diegosouzapw/OmniRoute/pull/12434)) +- **fix(uc):** route UC error strings through sanitizeErrorMessage; allowlist the retired codex id ([#12437](https://github.com/diegosouzapw/OmniRoute/pull/12437)) +- **fix(adapta):** redact streamed upstream errors ([#12438](https://github.com/diegosouzapw/OmniRoute/pull/12438)) +- **fix(sse):** remap non-contiguous upstream tool_calls index to a gap-free output_index ([#12445](https://github.com/diegosouzapw/OmniRoute/pull/12445)) — thanks @hartmark +- **fix(quality):** detect duplicate tool_calls entries in one response ([#12446](https://github.com/diegosouzapw/OmniRoute/pull/12446)) — thanks @hartmark +- **fix(conversations):** resolve turn content OmniRoute never sends back to the client ([#12447](https://github.com/diegosouzapw/OmniRoute/pull/12447)) — thanks @hartmark +- **fix(combo):** do not treat credits-exhausted 401 as auth skip ([#12449](https://github.com/diegosouzapw/OmniRoute/pull/12449)) — thanks @RaviTharuma +- **fix(auth):** do not park healthy quota accounts as expired ([#12452](https://github.com/diegosouzapw/OmniRoute/pull/12452)) — thanks @RaviTharuma +- **fix(responses-continuation):** fail closed on a collector-truncated, empty output array ([#12460](https://github.com/diegosouzapw/OmniRoute/pull/12460)) — thanks @hartmark +- **fix(logging):** raise the SSE payload collector's default cap ([#12461](https://github.com/diegosouzapw/OmniRoute/pull/12461)) — thanks @hartmark +- **fix(resilience):** surface Responses failed.error.message in 502s ([#12472](https://github.com/diegosouzapw/OmniRoute/pull/12472)) — thanks @RaviTharuma +- **fix(security):** unbiased maxai X-Random nonce + stricter URL/regex assertions ([#12502](https://github.com/diegosouzapw/OmniRoute/pull/12502)) +- **fix(video):** re-anchor transcript log-redaction so PII/credential maskers can't reopen the leak (#12150 P1 follow-up) ([#12503](https://github.com/diegosouzapw/OmniRoute/pull/12503)) +- **fix(monitoring):** serve cached credentialHealth off the request path ([#12533](https://github.com/diegosouzapw/OmniRoute/pull/12533)) — thanks @RaviTharuma +- **fix(quota):** keep Antigravity Gemini usable when Claude weekly is empty ([#12566](https://github.com/diegosouzapw/OmniRoute/pull/12566)) — thanks @HouMinXi +- **fix(authz):** hard-gate every credential export and CLI-config write (GHSA-5926-2w35-7h4q) ([#12600](https://github.com/diegosouzapw/OmniRoute/pull/12600)) +- **fix(docs):** restore the Next build — REMOVED_PROVIDERS.md had no frontmatter (base-red #12581) ([#12610](https://github.com/diegosouzapw/OmniRoute/pull/12610)) +- **fix(quality):** re-point the zcodeProtocol public-creds allowlist to line 313 ([#12615](https://github.com/diegosouzapw/OmniRoute/pull/12615)) +- **fix(security):** close 3 advisories — search baseUrl exfil, sk- in the error sanitizer, bifrost relay header leak ([#12620](https://github.com/diegosouzapw/OmniRoute/pull/12620)) +- **fix(providers):** sanitize boolean required and nested bare maps for Gemini (#12269) ([#12624](https://github.com/diegosouzapw/OmniRoute/pull/12624)) — thanks @HouMinXi +- **fix(resilience):** keep Overloaded STREAM_EARLY_EOF off the provider breaker ([#12626](https://github.com/diegosouzapw/OmniRoute/pull/12626)) — thanks @HouMinXi +- **fix(catalog):** re-audit free-tier quotas against official pages ([#12649](https://github.com/diegosouzapw/OmniRoute/pull/12649)) +- **fix(dashboard):** keep a request's pending-tracking id stable across combo target retries ([#12650](https://github.com/diegosouzapw/OmniRoute/pull/12650)) — thanks @hartmark +- **fix(tests):** retire dead model ids from the chat-pipeline integration suite (base-red #12581) ([#12670](https://github.com/diegosouzapw/OmniRoute/pull/12670)) +- **fix:** resolve SqliteError no such table compression_run_telemetry during cleanup ([#12682](https://github.com/diegosouzapw/OmniRoute/pull/12682)) — thanks @groovecityJO +- **fix(combo):** treat a pin-only step as implicit connection allowlist ([#12697](https://github.com/diegosouzapw/OmniRoute/pull/12697)) — thanks @HouMinXi +- **fix(ci):** ship .npmrc in published package so legacy-peer-deps applies to consumers (#11544) ([#12699](https://github.com/diegosouzapw/OmniRoute/pull/12699)) — thanks @KooshaPari +- **fix(docker):** pin 4 CLI tools to exact versions (#12576) ([#12703](https://github.com/diegosouzapw/OmniRoute/pull/12703)) — thanks @KooshaPari +- **fix(docker):** document and harden cli profile trust boundary (#12570) ([#12706](https://github.com/diegosouzapw/OmniRoute/pull/12706)) — thanks @KooshaPari +- **fix(sse):** redact video transcript in pre-guardrail rejected-request logs (#12150 P2 item 7) ([#12710](https://github.com/diegosouzapw/OmniRoute/pull/12710)) +- **fix(i18n):** quote placeholder in OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES description (#12505) ([#12769](https://github.com/diegosouzapw/OmniRoute/pull/12769)) — thanks @KooshaPari +- **fix(api/pricing):** surface validation error message as string, not raw object (#12494) ([#12771](https://github.com/diegosouzapw/OmniRoute/pull/12771)) — thanks @KooshaPari +- **fix(sse):** re-enable prompt compression for native Codex passthrough ([#12834](https://github.com/diegosouzapw/OmniRoute/pull/12834)) — thanks @thomasmaerz +- **fix(dashboard):** model health tests for a provider node set to the Responses API now call `/v1/responses` with a Responses-shaped body instead of `/v1/chat/completions` — those models were reported as `Provider returned HTTP 200 but no text content` even though the same model answered normally through `/v1/responses` ([#13070](https://github.com/diegosouzapw/OmniRoute/issues/13070)) ([#13087](https://github.com/diegosouzapw/OmniRoute/pull/13087)) — thanks @ntdatt812 +- fix(routing): stop a round-robin combo's "opencode" targets from collapsing onto the opencode-zen connection (#11912) ([#13283](https://github.com/diegosouzapw/OmniRoute/pull/13283)) +- fix(routing): stop the reactive-compaction debug log from lying when compression is globally disabled (#11977) ([#13276](https://github.com/diegosouzapw/OmniRoute/pull/13276)) +- fix(electron): relativize standalone-bundle symlink targets so Stage 8 manifest verification stops failing on Windows (#11979) ([#13251](https://github.com/diegosouzapw/OmniRoute/pull/13251)) +- fix(dashboard): surface a visible error when Compression Studio's combined preview run fails (#12061) ([#13288](https://github.com/diegosouzapw/OmniRoute/pull/13288)) +- fix(dashboard): make the compression "Effective pipeline" preview honor the active profile and warn when the master switch is off (#12063) ([#13287](https://github.com/diegosouzapw/OmniRoute/pull/13287)) +- fix(providers): scope TinyCMS Web signer's DOM shims to each call instead of leaking them for the process lifetime, and surface a clean HTTP status on a non-JSON interception-toggles error (#12072) ([#13254](https://github.com/diegosouzapw/OmniRoute/pull/13254)) +- fix(guardrails): stop Vision Bridge from re-selecting a model locked after a 404 (#12111) ([#13259](https://github.com/diegosouzapw/OmniRoute/pull/13259)) +- fix(sse): require Responses-shaped body before native OpenAI-compatible passthrough (#12129) ([#13278](https://github.com/diegosouzapw/OmniRoute/pull/13278)) +- fix(providers): minimax-m3 now collapses manual thinking.type:"enabled" to adaptive, preventing upstream 400 (2013) (#12132) ([#13249](https://github.com/diegosouzapw/OmniRoute/pull/13249)) +- fix(db): scope model visibility overrides by modality so hiding a Chat model no longer hides an identically-ID'd Image/Embeddings/etc. model (#12172) ([#13258](https://github.com/diegosouzapw/OmniRoute/pull/13258)) +- fix(db): scope local-provider apiKey dedup to matching base URL so LM Studio/Ollama-style connections support multiple accounts (#12173) ([#13250](https://github.com/diegosouzapw/OmniRoute/pull/13250)) +- fix(providers): refresh Trae's stale Referer/Origin and forward user timezone so imported connections stop failing with 401 (#12190) ([#13255](https://github.com/diegosouzapw/OmniRoute/pull/13255)) +- fix(providers): route opencode-go/gpt-5.6-luna to /responses instead of /chat/completions (#12196) ([#13275](https://github.com/diegosouzapw/OmniRoute/pull/13275)) +- fix(dashboard): allow deleting the last extra-upstream-header row even when invalid (#12251) ([#13252](https://github.com/diegosouzapw/OmniRoute/pull/13252)) +- **fix(i18n):** translate pre-existing `__MISSING__:` keys for `combo.sort`, `requestLogger.detail` expand/collapse, `common.profile`, and `settings.resilienceCredentialHealth*` across 39 locales ([#12272](https://github.com/diegosouzapw/OmniRoute/issues/12272)) ([#13034](https://github.com/diegosouzapw/OmniRoute/pull/13034)) — thanks @HouMinXi +- fix(cli): run the Node.js runtime compatibility guard before the heavy `tsx/esm` + Commander import chain so an unsupported runtime gets a clear message instead of a raw `Invalid regular expression flags` crash (#12296) ([#13253](https://github.com/diegosouzapw/OmniRoute/pull/13253)) +- fix(dashboard): refresh the providers list after deleting a compatible provider node (#12298) ([#13256](https://github.com/diegosouzapw/OmniRoute/pull/13256)) +- fix(usage): fail closed on API-key budget enforcement when a provider's `auto` routing alias has no pricing row, instead of silently counting it as $0 (#12341) ([#13257](https://github.com/diegosouzapw/OmniRoute/pull/13257)) +- **fix(providers):** include the submitted Agnes video model when polling by `video_id` ([#12356](https://github.com/diegosouzapw/OmniRoute/pull/12356)) — thanks @pacocartones +- **fix(routing):** custom OpenAI-compatible nodes now honor the saved Chat/Responses API type after edits instead of letting the node's original ID prefix override the live connection setting ([#11884](https://github.com/diegosouzapw/OmniRoute/issues/11884)). ([#12358](https://github.com/diegosouzapw/OmniRoute/pull/12358)) — thanks @pacocartones +- fix(sse): surface an error instead of a silent empty 200 when a Claude stream closes with zero bytes (#12398) ([#13285](https://github.com/diegosouzapw/OmniRoute/pull/13285)) +- fix(oauth): warn before the dead localhost:8080 redirect in antigravity/gemini `oauth start` (#12413) ([#13265](https://github.com/diegosouzapw/OmniRoute/pull/13265)) +- fix(providers): stop devin-cli spawn error from double-closing the SSE controller (#12517) ([#13261](https://github.com/diegosouzapw/OmniRoute/pull/13261)) +- **fix(rerank):** clamp Voyage `top_k` to the documents actually sent after empty-string filtering, and honor `return_documents: false` in the NVIDIA response adapter (#12523 — thanks @pacocartones) +- **fix(audio):** `/v1/audio/translations` now resolves combo names the way `/v1/audio/transcriptions` already does, so a combo that `GET /v1/models` advertises is fanned out to its targets instead of being rejected with `400 Invalid translation model: . Use format: provider/model`; literal `provider/model` ids and unknown bare names behave as before (#12536 — thanks @pacocartones) +- **fix(gemini):** strip the JSON-Schema-2020-12 `prefixItems` keyword from Gemini tool schemas at every nesting level, so Claude Code tool definitions no longer fail with `400 Unknown name "prefixItems"` on Gemini models (#12540 — thanks @pacocartones) +- **fix(api-manager):** Expose an accessible loading status while API keys are fetched instead of an empty accessibility tree (#12541 — thanks @pacocartones) +- **fix(video):** Clamp `estimateJpegFrameBytes` at zero for padding-only payloads and build the three encode-side frame data URIs from `JPEG_FRAME_DATA_URI_PREFIX` instead of a repeated literal (#12543 — thanks @pacocartones) +- **fix(devin):** accept Windows `DEVIN_AGENTIC_HOME` sandbox paths (`C:\...\.sandbox\...`) in the isolated-home check so the Devin Claude Bridge no longer fails closed on Windows ([#12405](https://github.com/diegosouzapw/OmniRoute/issues/12405)) (#12545 — thanks @pacocartones) +- **fix(db):** Add `conversation_turn_nodes` and orphaned `agentic_conversations` to the auto-cleanup cycle under the existing `retention.callLogs` window, so identity nodes whose call-log content has already been purged no longer accumulate without bound in `storage.sqlite` (#12548 — thanks @pacocartones) +- **fix(i18n):** Wrap the `~/.claude/profiles//settings.json` placeholder in ICU single quotes in the `OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES` feature-flag description across all 42 locales and the TypeScript default, so next-intl no longer fails with `INVALID_MESSAGE: UNCLOSED_TAG` and the Feature Flags card shows the description instead of the raw key (#12549 — thanks @pacocartones) +- **fix(orchestration):** `updateCloudAgentTask` now publishes the task's real `status` on `agent.task.updated` when an update only touches `result`, `activities` or `error`, instead of the fabricated `"updated"` state, and stays silent when no row matched the id (#12550 — thanks @pacocartones) +- **fix(i18n):** the home "Recent Requests" panel and the Provider Topology legend are now translated instead of rendering English copies on non-English dashboards; the legend reads its own `home.topologyLegend*` labels with consistent casing rather than borrowing the memory-settings "Recent" and analytics "Error" strings (#12551 — thanks @pacocartones). +- **docs(reference):** bring the `FEATURE_FLAGS.md` catalog back to 1:1 with `featureFlagDefinitions.ts` — 20 missing flags added, the two `*_BLOCK_THRESHOLD` env-only knobs moved out of the flag tables, category/total counts and the Live WS port corrected, guarded by a static test (#12552 — thanks @pacocartones) +- **fix(i18n):** backfill missing `usage.kiloPass*` strings in 39 locales and restore `featureFlagOmnirouteDisableThinkingLevelVariantsDescription` in `pt.json` ([#12561](https://github.com/diegosouzapw/OmniRoute/issues/12561)) ([#13027](https://github.com/diegosouzapw/OmniRoute/pull/13027)) — thanks @HouMinXi +- fix(docker): default docker-compose app ports (dashboard/API/live-WS) to loopback instead of `0.0.0.0`, closing the anonymous `/v1` LAN/WAN exposure gap left open by `REQUIRE_API_KEY=false` (#12568) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): close DNS-rebinding SSRF gap in webhook outbound-URL guard (#12569) ([#13243](https://github.com/diegosouzapw/OmniRoute/pull/13243)) +- fix(docker): require a per-session token on the VNC browser CDP bridge and isolate it on a dedicated Docker network (#12571) ([#13241](https://github.com/diegosouzapw/OmniRoute/pull/13241)) +- fix(open-sse): write Adobe Firefly session tokens and cookie jars with 0700/0600 permissions instead of the process umask (#12572) ([#13242](https://github.com/diegosouzapw/OmniRoute/pull/13242)) +- fix(api): remove hardcoded wildcard CORS in openai-to-gemini-sse.ts so the centralized fail-closed CORS gate is the sole source of `Access-Control-Allow-Origin` (#12573) ([#13239](https://github.com/diegosouzapw/OmniRoute/pull/13239)) +- fix(api): enforce API key policy (budget/rate-limit/schedule/endpoint scoping) on the ElevenLabs speech-to-text, text-to-speech and voices proxy routes (#12574) ([#13244](https://github.com/diegosouzapw/OmniRoute/pull/13244)) +- fix(sse): cap HuggingChat NDJSON body size and bound the read loop with the fetch timeout so a stalled or hostile upstream cannot buffer unbounded memory (#12577) ([#13282](https://github.com/diegosouzapw/OmniRoute/pull/13282)) +- fix(docker): scope the cliproxyapi/qdrant/bifrost sidecars to loopback by default and forward `CLIPROXYAPI_MANAGEMENT_KEY` into the cliproxyapi container so its management API is not left both unauthenticated and LAN-published (#12578) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): create DB export temp paths with `fs.mkdtempSync` instead of predictable timestamps (#12579) ([#13237](https://github.com/diegosouzapw/OmniRoute/pull/13237)) +- Cline 401 bodies that say "re-authenticate your Cline account" classify as a refreshable OAuth token, not a terminal expired key. The cooling panel no longer labels every cooldown as a 429; it shows the recorded last error instead. (#12594) ([#13060](https://github.com/diegosouzapw/OmniRoute/pull/13060)) — thanks @HouMinXi +- **fix(catalog):** degrade unknown combo targets instead of dropping LCD modalities, and persist OpenRouter `architecture.input_modalities` into the capability snapshot ([#12613](https://github.com/diegosouzapw/OmniRoute/issues/12613)) ([#13042](https://github.com/diegosouzapw/OmniRoute/pull/13042)) — thanks @HouMinXi +- fix(providers): send `x-api-key` instead of `Authorization: Bearer` for OpenCode Zen's `/v1/responses` endpoint (Muse Spark Contributor models), fixing a 401 on OmniRoute's auth header (#12633) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(sse): surface the actionable "Auggie CLI not found" message when the shell reports a missing `auggie` binary via exit code instead of a spawn error (#12645) ([#13263](https://github.com/diegosouzapw/OmniRoute/pull/13263)) +- **fix(config):** Persist deletions of built-in background-degradation entries — when a stored settings record exists its `degradationMap` is now authoritative instead of being merged under the defaults, so an entry the user removed in the dashboard no longer reappears on the next apply or restart ([#12424](https://github.com/diegosouzapw/OmniRoute/issues/12424)) ([#12647](https://github.com/diegosouzapw/OmniRoute/pull/12647)) — thanks @pacocartones +- **fix(gamification):** action-count badge milestones (First Token, Token Consumer, Token Machine, Token Whale, and the token-sharing tier) are now backed by a durable `xp_action_counts` counter incremented in `addXp()`, instead of a live `COUNT(*)` over `xp_audit_log`. The audit log is pruned by `retention.xpAuditLog` (default 30 days), so on a default install those "lifetime" milestones were really "actions in the last 30 days" and unlocked badges could stop unlocking once old rows aged out. `getActionCount()` and `checkActionCountBadges()` now read the same durable source, and a migration backfills existing totals from the surviving audit rows ([#12546](https://github.com/diegosouzapw/OmniRoute/issues/12546)) ([#12651](https://github.com/diegosouzapw/OmniRoute/pull/12651)) — thanks @pacocartones +- **fix(images):** `/v1/images/edits` now iterates a combo's targets the same way `/v1/images/generations` does (#9239) instead of flattening a bare combo to its first target. A combo whose first target is not edit-capable — or lacks credentials — now falls through to a later edit-capable target rather than hard-erroring, and missing credentials are skipped (not a hard `401`) to match the generations path. The per-target skip/terminal classification is extracted into a shared `runImageComboTargets` loop, so generations behavior is unchanged ([#12547](https://github.com/diegosouzapw/OmniRoute/issues/12547)). ([#12653](https://github.com/diegosouzapw/OmniRoute/pull/12653)) — thanks @pacocartones +- fix(sse): add first-byte watchdog to the TLS-fingerprint transport so a stalled wreq body falls back instead of hanging for minutes (#12656) ([#13272](https://github.com/diegosouzapw/OmniRoute/pull/13272)) +- fix(sse): exempt tiny-budget reasoning probes from combo quality failure and surface persisted-cooldown skips in ALL_TARGETS_SKIPPED diagnostics (#12659) ([#13269](https://github.com/diegosouzapw/OmniRoute/pull/13269)) +- fix(models): declare the real ~1M contextLength for OpenCode Zen's Muse Spark 1.2 models instead of falling back to the 200000 provider default (#12681) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(oauth): align codebuddy-cn OAuth User-Agent with the chat/usage CLI version to avoid WAF false positives (#12702) ([#13264](https://github.com/diegosouzapw/OmniRoute/pull/13264)) +- fix(dashboard): surface an authentication-required banner instead of silently blanking database settings for a guest session (#12709) ([#13270](https://github.com/diegosouzapw/OmniRoute/pull/13270)) +- **fix(resilience):** stop unbounded queue that hangs 6min until Aborted — gate, provider slot, and Bottleneck queue now share a per-connection `maxWaitMs` budget; `fail-closed` on exhaust (503); execution backstop `executionMaxWaitMs` overridable per connection with upstream clamp ([#12715](https://github.com/diegosouzapw/OmniRoute/pull/12715)) — thanks @maxmad64bis (with thanks to @Tushar49 for surfacing the slow-provider need in #12635) +- **fix(auto-combo):** every mode pack now carries `quality` and `reliability` weights (`quality` 0.02, 0.03 in `quality-first`; `reliability` 0.03, 0.04 in `reliability-first`), so selecting a pack no longer silences either signal; `DEFAULT_WEIGHTS` is unchanged ([#12731](https://github.com/diegosouzapw/OmniRoute/pull/12731)) — thanks @maxmad64bis +- **fix(sse):** the provider execution pipeline keeps the upstream error `code`/`type` again — both were lost when the non-streaming leg was extracted, so a config-class `422` (Antigravity missing project) degraded into a generic account cooldown instead of reaching the gate that recognises it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(db):** a legacy `call_logs` table no longer aborts startup — the provider-stats index is created after the column healing runs, not before it, so an install predating `request_type` opens instead of failing with `no such column` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(catalog):** a malformed row in the operator-writable custom-models blob no longer kills every `auto/*` pool ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(tests):** realign the guards that the same day's merges left asserting the old behavior — the provider-count total after GreenPT and EURouter, the GLM stream buffer slot that `#12925` turned into a declared parameter, and the injection scan bound that `#13104` reshaped from a head-only window into head-plus-tail ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- fix(cache): fold tool_choice/tools/response_format into the semantic cache signature so a cached tool_calls response can no longer be replayed for a request whose tool policy forbids it (#12734) ([#13267](https://github.com/diegosouzapw/OmniRoute/pull/13267)) +- **fix(models):** stop treating provider-supplied `isFree:true`/`:free`/`0/0` flags as trusted unless the provider has a documented free tier — only the shipped free-tier catalog decides otherwise; fetched `isFree:true` trusted only for a free-tier provider, custom `isFree:true` via a trusted path ([#12744](https://github.com/diegosouzapw/OmniRoute/pull/12744)) — thanks @maxmad64bis +- fix(memory): authenticate the internal /v1/rerank loopback call so memory reranking no longer silently degrades to unranked order when REQUIRE_API_KEY=true (#12745) ([#13268](https://github.com/diegosouzapw/OmniRoute/pull/13268)) +- fix(sse): parse Ollama Cloud's current usage markup (`$X of $Y used` aria-label, nested width style) (#12749) ([#13260](https://github.com/diegosouzapw/OmniRoute/pull/13260)) +- fix(cli): setup-opencode no longer sends an active context's management token to `/v1/models` when `--api-key`/`OMNIROUTE_API_KEY` is supplied — an explicit flag or the env var now always outranks the context's token, and the flag itself is no longer swallowed by the parent program's global `--api-key` option (#12783) ([#13246](https://github.com/diegosouzapw/OmniRoute/pull/13246)) +- fix(sse): register Arcee AI in the executor provider registry so requests reach api.arcee.ai instead of silently falling back to OpenAI (#12784) ([#13277](https://github.com/diegosouzapw/OmniRoute/pull/13277)) +- **fix(quota):** share one upstream quota read when identical saturation checks arrive at the same time, so traffic bursts don't multiply provider API calls ([#12787](https://github.com/diegosouzapw/OmniRoute/pull/12787)) — thanks @maxmad64bis +- **fix(db):** ignore expired or invalid rate-limit cooldown writes so a stale timestamp can't lock a connection that should be usable — clearing still works as before ([#12788](https://github.com/diegosouzapw/OmniRoute/pull/12788)) — thanks @maxmad64bis +- **fix(providers):** single source for provider order with `xao` ranking alongside `xai-oauth` ([#12790](https://github.com/diegosouzapw/OmniRoute/pull/12790)) — thanks @maxmad64bis +- **fix(routing):** off-table models check the free-model catalog before inheriting premium prices, latency bootstraps from the observed pool median, async tiers read live database pricing with a 90-day freshness gate, and the tier cache invalidates on every pricing write ([#12792](https://github.com/diegosouzapw/OmniRoute/pull/12792)) — thanks @maxmad64bis +- **fix(auto-combo):** snapshot scoring uses the observed per-connection account tier and per-model quality instead of neutral constants, and circuit-open providers rank lower at build time ([#12794](https://github.com/diegosouzapw/OmniRoute/pull/12794)) — thanks @maxmad64bis +- **fix(pool):** empty `auto/*` pools now say why they are empty, and the models listing uses the same paid check as routing ([#12795](https://github.com/diegosouzapw/OmniRoute/pull/12795)) — thanks @maxmad64bis +- fix(routing): recognize CLIProxyAPI's 'unknown provider for model' 400 as fallback-worthy (#12800) ([#13284](https://github.com/diegosouzapw/OmniRoute/pull/13284)) +- **fix(sse):** translate-mode streams now emit the estimated token counts as a trailing usage-only chunk before `[DONE]` when the upstream stays silent, so metered chat clients see totals instead of nothing ([#12828](https://github.com/diegosouzapw/OmniRoute/pull/12828)) — thanks @maxmad64bis +- **fix(db):** provider stats stay truthful on empty and legacy databases: fallback counts default to `0` instead of `null`, latency averages read `null` (not `0`) when no durations were recorded, failures logged before the error-type column existed group under `pre_migration` instead of `unclassified`, and per-provider queries use two new composite indexes ([#12832](https://github.com/diegosouzapw/OmniRoute/pull/12832)) — thanks @maxmad64bis +- fix(nvidia): fail open when a synced model catalog goes stale instead of gating forever (#12849) ([#13248](https://github.com/diegosouzapw/OmniRoute/pull/13248)) +- **fix(db):** WAL maintenance lives in its own module: a `TRUNCATE` checkpoint that hits a busy database now warns with its streak and retries once via `PASSIVE` instead of logging success, and closing-time checkpoints no longer report success on builds without a database file, with the busy totals visible in the authenticated monitoring health payload ([#12853](https://github.com/diegosouzapw/OmniRoute/pull/12853)) — thanks @maxmad64bis +- **fix(health):** quota with no snapshots reads empty instead of a contradicting 0% ([#12857](https://github.com/diegosouzapw/OmniRoute/pull/12857)) — thanks @maxmad64bis +- fix(a2a): accept the dashboard's own session cookie on /a2a so "Run message/send" no longer fails with "Unauthorized: missing or invalid API key" (#12888) ([#13271](https://github.com/diegosouzapw/OmniRoute/pull/13271)) +- **fix(logs):** the Logs grid's in-memory filter pass no longer discards rows the SQL query already matched — selecting an API key from the dropdown (which sends the key's id) returns its calls again, the Combo tab shows every combo instead of only those whose name contains a "1", and the model filter and search cover the same columns as the query ([#12896](https://github.com/diegosouzapw/OmniRoute/pull/12896)) — fixes [#12873](https://github.com/diegosouzapw/OmniRoute/issues/12873) — thanks @ntdatt812 +- **fix(a2a):** `/api/a2a/status` now builds the agent card from the request that asked for it, so a gateway reached at a non-localhost host no longer advertises `http://localhost:20128` as its A2A URL ([#12918](https://github.com/diegosouzapw/OmniRoute/pull/12918)). — thanks @ntdatt812 +- **fix(compression):** progressive aging now appends its `[COMPRESSED:aging:…]` annotation after a turn's `tool_result` blocks instead of in front of them, so Anthropic no longer rejects aged conversations with "`tool_use` ids were found without `tool_result` blocks immediately after" ([#12920](https://github.com/diegosouzapw/OmniRoute/pull/12920)). — thanks @ntdatt812 +- **fix(bedrock):** model import now resolves context limits for every vendor prefix instead of only `anthropic.*`, so `global.openai.gpt-5.6-*` no longer imports with a null `inputTokenLimit` and gets rejected pre-flight at the 200k default ([#12921](https://github.com/diegosouzapw/OmniRoute/pull/12921)). — thanks @ntdatt812 +- **fix(stream):** the 64 KB stream buffer GLM asks for is honoured instead of dropped, and the type error it caused no longer fails the API Route Typecheck gate on every open PR ([#12925](https://github.com/diegosouzapw/OmniRoute/pull/12925)) — thanks @ntdatt812 +- **fix(guardrails):** mask PII inside a `tool_result`'s nested content array, which the masker walked past while redacting its sibling block ([#12930](https://github.com/diegosouzapw/OmniRoute/pull/12930)) — thanks @ntdatt812 +- **fix(dashboard):** the Radar catalog table no longer leaves absent data unexplained — empty limits, unknown context windows, and unreported capabilities each explain themselves on hover, and a new check keeps it that way ([#12937](https://github.com/diegosouzapw/OmniRoute/pull/12937)) — thanks @maxmad64bis +- **fix(sse):** geo-blocked opencode requests rotate to the next account proxy instead of failing, so one refused egress no longer aborts the whole chain ([#12941](https://github.com/diegosouzapw/OmniRoute/pull/12941)) — thanks @maxmad64bis +- **fix(codex):** dashboard "clear cooldown" and the CAS recovery path now drop nested `codexScopeRateLimitedUntil` maps in the same write that nulls `rate_limited_until`, and fresh quota snapshots with headroom lift fallback-sourced scope cooldowns parked by quota preflight ([#12817](https://github.com/diegosouzapw/OmniRoute/issues/12817), [#12860](https://github.com/diegosouzapw/OmniRoute/issues/12860), [#12951](https://github.com/diegosouzapw/OmniRoute/pull/12951)) The dashboard reset-credit button (`consumeCodexResetCredit`) now exercises that same snapshot path after a successful redeem, so a filled quota bar is enough to unpark a leftover fallback Codex child without another manual clear. — thanks @HouMinXi +- **fix(db):** resolve `sql-wasm.wasm` across global npm install and hoisted layouts, ensuring OmniRoute can boot cleanly on Node 24 when native `better-sqlite3` is uncompiled. ([#13035](https://github.com/diegosouzapw/OmniRoute/pull/13035)) — thanks @HouMinXi +- **fix(api):** `DELETE /v1/batches/delete-completed` now sweeps only the calling API key's own completed batches (batches with no owner stay out of a key-scoped sweep on purpose), with an explicit instance-wide mode reserved for authenticated dashboard sessions, a 401 for a presented key that is unknown, revoked, deactivated, banned or expired (never falling through to the session branch), audit logging of every sweep, a sanitized 500 on failure, an owner-scoped file half (a key-scoped sweep never nulls a file another tenant owns) and an atomic sweep — chunked in 200-batch transactions in instance mode — so a mid-way error never leaves a batch pointing at a nulled file (GHSA-wvxc-jp3v-5mg5) ([#12969](https://github.com/diegosouzapw/OmniRoute/pull/12969)) +- **fix(combo):** quota-weighted routing stops drawing on an out-of-credit connection — a 402 now invalidates the stored quota snapshot instead of leaving its stale remaining percentage in place, and a snapshot older than 10 minutes no longer counts as confident headroom for the primary pool ([#12972](https://github.com/diegosouzapw/OmniRoute/pull/12972)) ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- **fix(vision):** Custom Models with "Vision capable" checked no longer have image requests swapped to `glm/glm-4.6v` when the client sends the advertised alias (`vllm/path/...`) or the bare path-shaped id — Vision Bridge now matches the stored override for all three id forms ([#12758](https://github.com/diegosouzapw/OmniRoute/issues/12758)) ([#12974](https://github.com/diegosouzapw/OmniRoute/pull/12974)) — thanks @HouMinXi +- **fix(sse):** transient opencode upstream failures rotate to the next account proxy instead of failing, so one flapping egress no longer aborts the whole chain ([#12975](https://github.com/diegosouzapw/OmniRoute/pull/12975)) — thanks @maxmad64bis +- **fix(azure):** Deployments from GPT-6 onward now send `max_completion_tokens` instead of `max_tokens`, which Azure rejects with HTTP 400. The rule matched a literal `gpt-5`, so each new generation arrived broken; it now matches the generation range, while `gpt-35-turbo` still keeps `max_tokens`. ([#13007](https://github.com/diegosouzapw/OmniRoute/pull/13007)) — thanks @ntdatt812 +- **fix(db):** add an opt-in self-restart circuit for sustained critical memory pressure, gate post-cleanup VACUUM behind a minimum freed-rows threshold, and checkpoint the SQLite WAL every 5 minutes with a size guard that escalates to TRUNCATE, so a growing WAL can no longer stall the event loop into a full outage. ([#13011](https://github.com/diegosouzapw/OmniRoute/pull/13011)) — thanks @HouMinXi +- **fix(auth):** an explicit connection pin may probe a quota-disabled row once and re-enable it on success ([#12874](https://github.com/diegosouzapw/OmniRoute/issues/12874)) ([#13017](https://github.com/diegosouzapw/OmniRoute/pull/13017)) — thanks @HouMinXi +- **fix(api):** thread `X-OmniRoute-Fallback-Attempts` through combo chat completions so streaming and non-streaming responses report how many prior legs were attempted ([#13038](https://github.com/diegosouzapw/OmniRoute/pull/13038)) — thanks @HouMinXi +- Restore provider failure classification and credential refresh on non-streaming requests: classify non-2xx failures to lock models on per-model quota exhaustion, update connection rate limits from headers and body, and pass credential refresh handlers to pipeline execution so 401 tokens can be refreshed and retried (#13043). ([#13069](https://github.com/diegosouzapw/OmniRoute/pull/13069)) — thanks @HouMinXi +- **fix(responses):** wrap forced-non-streaming web_search fallback JSON as Responses SSE so Codex still sees `response.completed` ([#13050](https://github.com/diegosouzapw/OmniRoute/pull/13050)) — thanks @HouMinXi +- **fix(antigravity):** Preserve upstream thought-token usage in normalized completion and reasoning token counts ([#13055](https://github.com/diegosouzapw/OmniRoute/pull/13055)) — thanks @pacocartones +- Fix a concurrency-slot leak in the DeepSeek PoW solver: a worker that failed to spawn (for example a missing worker script) never released its slot, so `MAX_CONCURRENT_WORKERS` failures disabled the solver until restart. ([#13097](https://github.com/diegosouzapw/OmniRoute/pull/13097)) — thanks @anhtahaylove +- **fix(acp):** bound the ACP session output buffers — `stdoutBuffer` and `stderrBuffer` now cap at 1 MiB keeping the most recent output behind a visible `[...output truncated...]` marker, and `stderrBuffer` is reset per prompt instead of accumulating for the lifetime of the session. ([#13100](https://github.com/diegosouzapw/OmniRoute/pull/13100)) — thanks @anhtahaylove +- **fix(acp):** release the `stdout`/`exit` listeners and the idle timer that a `sendPrompt` timeout used to leave attached to the `acpManager` singleton, and drop sessions that exited on their own from the session map instead of keeping them forever. ([#13096](https://github.com/diegosouzapw/OmniRoute/pull/13096)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection and PII scanners now read the text a `tool_result` block carries on `content` (string or nested block list), in messages and in system blocks, so tool output is judged by the same rules as user text ([#13101](https://github.com/diegosouzapw/OmniRoute/pull/13101)) — thanks @ntdatt812 +- **fix(gamification):** close the badge notification SSE stream when the request signal is already aborted before the stream starts — a client that disconnects while the route is still awaiting auth used to leave both the 2s unlock poll and the 15s heartbeat running for the lifetime of the process. ([#13106](https://github.com/diegosouzapw/OmniRoute/pull/13106)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection scan now spends its 16 KB budget on both ends of the request instead of the first 16 KB only, so `system`, `instructions`, `query`, `documents` and the newest turns are no longer hidden behind one long message ([#13104](https://github.com/diegosouzapw/OmniRoute/pull/13104)) — thanks @ntdatt812 +- **fix(dashboard):** expose the Volcano Ark console cookie on quota scraping and unwrap connect-error objects so the dashboard shows the upstream message ([#13107](https://github.com/diegosouzapw/OmniRoute/pull/13107)) — thanks @HouMinXi +- **fix(db):** release the `beforeExit`/`SIGINT`/`SIGTERM` handlers when a `node:sqlite` adapter closes, so a closed adapter and its database handle are no longer pinned to `process` for the lifetime of the run — the same treatment #7494 gave the sql.js adapter. ([#13109](https://github.com/diegosouzapw/OmniRoute/pull/13109)) — thanks @anhtahaylove +- **fix(translator):** `contentSchema` and `unevaluatedItems` are now treated as subschema positions by the tool-schema sanitizer, so a truncation placeholder in either is replaced with a permissive schema instead of being forwarded as a string ([#13110](https://github.com/diegosouzapw/OmniRoute/pull/13110)) — thanks @ntdatt812 +- **fix(cli-helper):** clear the `createLogStream` timeout on the abort path — `stop()` aborts the in-flight fetch and returned through the `signal.aborted` branch, which skipped `clearTimeout` and left an armed timer per stopped stream. The stream reader is now also cancelled when the read loop exits early. ([#13114](https://github.com/diegosouzapw/OmniRoute/pull/13114)) — thanks @anhtahaylove +- **fix(docs):** drop TLS-impersonation, MITM-decrypt, supply-chain attestation, and XOR-mask writeups from the public `/docs` catalog and Docker image. Files stay in git for engineers; operators who need them open the repo, not the website. ([#13136](https://github.com/diegosouzapw/OmniRoute/pull/13136)) — thanks @HouMinXi +- **fix(combo):** parse numeric-epoch `rate_limited_until` in the combo cooldown read path ([#13141](https://github.com/diegosouzapw/OmniRoute/pull/13141)) — thanks @maxmad64bis +- **fix(opencode):** both OpenCode plugins now reject a gateway address typed without `http://` at configuration time, instead of publishing every model with an api url no client can call, and the v2 plugin no longer publishes a model card whose api url is blank or relative ([#13142](https://github.com/diegosouzapw/OmniRoute/pull/13142)) — thanks @maxmad64bis +- **fix(providers):** lock opencode model on upstream 400 model-unavailable ([#13146](https://github.com/diegosouzapw/OmniRoute/pull/13146)) — thanks @maxmad64bis +- **fix(logging):** keep the provider exchange rather than the raw client bodies when a call log exceeds its size budget, and show that recovered payload in the request-detail panel instead of replacing it with the stored response body ([#13147](https://github.com/diegosouzapw/OmniRoute/pull/13147)) — thanks @maxmad64bis +- **fix(traffic-inspector):** the WebSocket route no longer leaks a traffic-buffer subscriber and a 30s ping timer when the client socket is already closed at handler time — listeners are attached before any resource is acquired, a destroyed socket bails out early, and the ping interval stops on a dead socket where `write()` never throws ([#13155](https://github.com/diegosouzapw/OmniRoute/pull/13155)) — thanks @anhtahaylove +- **fix(telegram):** bound the per-user API key cache in the Telegram chat proxy so a burst of distinct chat ids can no longer grow the process heap without limit ([#13165](https://github.com/diegosouzapw/OmniRoute/issues/13165)) ([#13166](https://github.com/diegosouzapw/OmniRoute/pull/13166)) — thanks @anhtahaylove +- **fix(stream):** cancel the upstream response body when the JSON-to-SSE sniff unwinds on a body timeout, so a stalled upstream no longer pins the connection ([#13169](https://github.com/diegosouzapw/OmniRoute/issues/13169)) ([#13171](https://github.com/diegosouzapw/OmniRoute/pull/13171)) — thanks @anhtahaylove +- **fix(telegram):** authenticate webhook deliveries with Telegram's `secret_token` so an unauthenticated caller can no longer mint API keys or spend upstream quota ([#13172](https://github.com/diegosouzapw/OmniRoute/issues/13172)) ([#13175](https://github.com/diegosouzapw/OmniRoute/pull/13175)) — thanks @anhtahaylove +- **fix(models):** give discoverable Gemini 3.8 Flash ids their own 65536 output spec so Antigravity no longer clamps them to 16384 ([#13195](https://github.com/diegosouzapw/OmniRoute/pull/13195)) — thanks @HouMinXi +- **fix(dashboard):** leftover catalog-removed provider rows (gemini-cli) can be listed and purged from the providers page ([#13067](https://github.com/diegosouzapw/OmniRoute/issues/13067)) ([#13197](https://github.com/diegosouzapw/OmniRoute/pull/13197)) — thanks @HouMinXi +- **fix(auth):** a dashboard session now requires the `authenticated: true` claim that login, OIDC and the session refresh already emit — a JWT merely signed with `JWT_SECRET` (for example the Cursor CLI passthrough token, which any API-key holder can obtain) no longer verifies as the `auth_token` cookie on any route, the WebSocket handshake or the live server; existing sessions keep working ([#13298](https://github.com/diegosouzapw/OmniRoute/issues/13298)) ([#13375](https://github.com/diegosouzapw/OmniRoute/pull/13375)) +- **fix(api):** `DELETE /v1/batches/delete-completed` now applies the caller's API-key policy (endpoint allowlist, schedule, usage cap, rate limit) like every other `/v1` route, commits the key-scoped sweep per 200-batch chunk instead of holding one write lock for the whole sweep, refuses to loop without progress, and rejects a scope that names both a key and `allTenants` ([#13684](https://github.com/diegosouzapw/OmniRoute/pull/13684)) +- Restore the API-route typecheck gate: the non-streaming leg result lost its discriminated-union narrowing after the server-owned tool loop reassignment, producing 13 new TS2339 diagnostics in `chatCore.ts`. ([#12963](https://github.com/diegosouzapw/OmniRoute/pull/12963)) +- Restore four non-streaming behaviours the server-owned tool loop refactor dropped: client aborts map to 499 with the fixed `Request aborted` message, an aborted request no longer logs a synthetic `clientResponse`, Claude prompt-cache telemetry is recorded again, and a body that cannot be canonicalized no longer throws when the tool loop is off. ([#12990](https://github.com/diegosouzapw/OmniRoute/pull/12990)) +- **fix(skills):** The CLI registry parser now reads positionals declared with `.addArgument()`, not only those written inline in `.command()`. `tunnel create [type]` was being published as `tunnel create`, so the agent-skills sync gate reported drift on every branch and regenerating would have deleted the argument. ([#13009](https://github.com/diegosouzapw/OmniRoute/pull/13009)) — thanks @ntdatt812 +- **fix(combos):** dashboard combo test uses a short prompt, serial probes, and a 60s timeout so reasoning models and rate-limited free pools do not fail the health check ([#13001](https://github.com/diegosouzapw/OmniRoute/pull/13001)) — thanks @HouMinXi +- fix(compression): terminate idle worker threads on eviction so long-running instances stop leaking OS threads and MessagePorts ([#13091](https://github.com/diegosouzapw/OmniRoute/pull/13091)) — thanks @anhtahaylove +- Redact Google API keys of any length in error bodies: the pattern required exactly 39 characters, so shorter or longer `AIza…` credentials passed through unredacted. ([#12964](https://github.com/diegosouzapw/OmniRoute/pull/12964)) +- **fix(grok-cli):** a 402 "Grok Build usage balance exhausted" parks that Grok login as out of credit (Grok Build CLI, grok.com cookie, and xAI OAuth share the weekly pool). Combo routing then tries the next login instead of locking the model for every account in the pool ([#13061](https://github.com/diegosouzapw/OmniRoute/pull/13061)) — thanks @HouMinXi +- fix(compression): spawn the LLMLingua worker with a file URL object so compression actually runs instead of silently failing open on Node ([#13093](https://github.com/diegosouzapw/OmniRoute/pull/13093)) — thanks @anhtahaylove +- **fix(test):** run the local `test` and `test:unit` scripts at concurrency 4 so a full-suite run no longer exhausts the machine's commit charge and kills unrelated processes ([#13187](https://github.com/diegosouzapw/OmniRoute/pull/13187)) — thanks @anhtahaylove +- **fix(monitoring):** `GET /api/monitoring/health` `credentialHealth` now includes a bounded `failedConnections` list (`connectionId`, `status`, sanitized `lastError`) when the probe-cache gauge `failed>0`, plus `source: probe-cache` and a cheap `staleDbNonOkCount` for sticky SQLite `test_status` on active rows. Documents that the live gauge is not `provider_connections.test_status`. ([#12876](https://github.com/diegosouzapw/OmniRoute/pull/12876)) — thanks @RaviTharuma +- **fix(resilience):** an openai-compatible multi-upstream gateway no longer marks the whole connection `credits_exhausted` when a single upstream (or the `/models` / representative-model health probe) returns 402. Quota failures stay model-scoped; true connection-wide auth failures are unchanged. Single-credential 402 key disable ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239)) is preserved. ([#12875](https://github.com/diegosouzapw/OmniRoute/pull/12875)) — thanks @RaviTharuma +- **fix(dashboard):** nine fixes on the Orchestration Canvas, all diagnosed in the Phase 2 reviews. A source that fails now keeps the timestamp of its FIRST failure instead of being re-stamped every poll — the stale line said "since the last poll" no matter how long the source had been down, and the churn also defeated the snapshot's stable identity (it serializes the source list), so the canvas re-rendered on every tick while anything was broken. A source that HAD data and only then started failing is flagged too: previously only a source with no node at all got the warning, so a source that went down mid-session kept a healthy-looking node forever. The rest are pointwise: clicking a filter chip cancels the pending search debounce (left armed, it fired ~300ms later and silently reverted the chip); the search input carries an accessible name; `?state=running, failed` parses like the unpadded form instead of dropping the padded value; the CSV toggle helper is defined once in `model/urlParams.ts` rather than twice; the agents tab tells "nothing running" apart from "the filter matched nothing", offering a clear-filters button instead of setup links that would be wrong advice there; edges stop emitting SMIL particles above 40 simultaneously active edges, keeping the colored stroke; and the drawer's error banner clears when a retried action succeeds. ([#12983](https://github.com/diegosouzapw/OmniRoute/pull/12983)) +- **fix(i18n):** the nine locales added with the EU-language batch (Greek, Estonian, Irish, Croatian, Lithuanian, Latvian, Maltese, Slovenian, Serbian) were missing the eleven Orchestration Canvas keys that Phase 3 introduced, so the compare-runs panel and the "no runs match these filters" empty state fell back to English in those languages (`deepMergeFallback` substitutes English for an absent key, so nothing rendered blank — it rendered untranslated). The coverage gate does not catch this: it enforces an 80% floor per locale, and eleven missing keys out of ~13,000 leaves coverage at 99.9%. Translated for real in each language, calibrated against the wording each file already uses for "run", "filter" and "skill". ([#13294](https://github.com/diegosouzapw/OmniRoute/pull/13294)) +- **fix(dashboard):** five follow-ups from the Phase 2 Orchestration Canvas review. The History drawer now shows the memory section for runs that already left the live TTL window: the persisted `memory_hits` event is parsed into `metadata.memoryHits` with the same defensive validation the drawer applies, and — because that event is observability rather than a state transition — it no longer leaks into the timeline, where it had been inheriting the task's state and rendering as a duplicate transition. The A2A memory recall runs against its own 1.5s deadline instead of inheriting the memory backend's 30s one; overshooting degrades exactly like any other recall failure (no hits, task proceeds), and the timer is cleared on both paths. Repeating a Conductor run carries its `requirements.cli`/`requirements.model` forward, so the new run is pinned to the same runner profile and model rather than drifting to whatever the fleet picks. A successful repeat from the Agents tab now focuses the run it created, instead of leaving the operator on the finished one. And the auth test for `POST /api/conductor/tasks` moved into the shared `ROUTES` array rather than restating the pattern. ([#12988](https://github.com/diegosouzapw/OmniRoute/pull/12988)) +- fix(plugins): stop leaking an exit listener per plugin hook timeout, which triggered MaxListenersExceededWarning on plugins that ignore SIGTERM ([#13092](https://github.com/diegosouzapw/OmniRoute/pull/13092)) — thanks @anhtahaylove +- **fix(validation):** Provider node edits no longer fail with a generic "Invalid request" when the optional daily-quota reset fields are left blank. The dashboard sends `dailyQuotaResetTimezone` and `dailyQuotaResetHour` as `null`, and only the hour accepted it. ([#13066](https://github.com/diegosouzapw/OmniRoute/issues/13066)) ([#13083](https://github.com/diegosouzapw/OmniRoute/pull/13083)) — thanks @ntdatt812 +- **fix(combo):** quota-aware expansion drops banned, inactive, missing, and wrong-provider connections before quota fetch or model dispatch; pins and allowlists stay selectors, not a bypass. Antigravity automatic exhaustion now requires a reported zero remaining, so a positive balance below 1% stays eligible. ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- Fix an intermittent failure in the quota-weighted routing test suite: scores are a function of `Date.now()`, so peers with identical quota scored microseconds apart never tied and swapped order. ([#12946](https://github.com/diegosouzapw/OmniRoute/pull/12946)) +- fix(volcengine): resolve quota window mapping gaps, safe multi-connection console binding, and response cookie sanitization ([#12950](https://github.com/diegosouzapw/OmniRoute/pull/12950)) — thanks @HouMinXi +- **fix(test):** resolve the WebDAV handler path with `fileURLToPath` so the suite's 37 WebDAV tests run on Windows instead of failing with a doubled `C:\C:\` drive prefix ([#13196](https://github.com/diegosouzapw/OmniRoute/pull/13196)) — thanks @anhtahaylove +- **fix(ui):** fall back from invalid AnySearch icon alias (direct commit `9587e07b69`) +- **fix(sse):** stop rebuilding a truncated summary from the collector's cap-dropped event array ([#12718](https://github.com/diegosouzapw/OmniRoute/pull/12718)) — thanks @hartmark +- **fix(conversations):** show a pending spinner for unresolved tool nodes instead of "(empty)" ([#12727](https://github.com/diegosouzapw/OmniRoute/pull/12727)) — thanks @hartmark +- **fix(streaming):** fail fast when an upstream stream produces only lifecycle/heartbeat events, never real content ([#12741](https://github.com/diegosouzapw/OmniRoute/pull/12741)) — thanks @hartmark +- **fix(responses-continuation):** bridge the write-in-flight window with an in-memory pending store ([#12854](https://github.com/diegosouzapw/OmniRoute/pull/12854)) — thanks @hartmark +- **fix(translator):** drop or map agent_message on Chat Completions fallback ([#12880](https://github.com/diegosouzapw/OmniRoute/pull/12880)) — thanks @RaviTharuma +- **fix(api):** page and stream-complete GET /v1/models for large catalogs ([#12882](https://github.com/diegosouzapw/OmniRoute/pull/12882)) — thanks @RaviTharuma +- **fix(quota):** align AUTH window labels with usage API and clarify 503 ([#12884](https://github.com/diegosouzapw/OmniRoute/pull/12884)) — thanks @RaviTharuma +- **fix(ci):** clear two base-reds on release/v3.8.51 — mutation-coverage gate + image-only-model guard ([#12945](https://github.com/diegosouzapw/OmniRoute/pull/12945)) +- **fix(api):** scope batch bulk-delete to the calling API key ([#13211](https://github.com/diegosouzapw/OmniRoute/pull/13211)) +- **fix(tests):** align model-sync log assertion to shipped path redaction (base-red) ([#13227](https://github.com/diegosouzapw/OmniRoute/pull/13227)) +- **fix(test):** make the ToS heading guard actually require the parentheses ([#13228](https://github.com/diegosouzapw/OmniRoute/pull/13228)) +- **fix(quality):** register 4 drifted covering tests in stryker tap.testFiles ([#13229](https://github.com/diegosouzapw/OmniRoute/pull/13229)) +- **fix(logging):** mask *-api-key headers in request-log pipeline (#13273) ([#13401](https://github.com/diegosouzapw/OmniRoute/pull/13401)) — thanks @KooshaPari +- **fix(tests):** correct stale Jina catalog prefix in custom-models test ([#13403](https://github.com/diegosouzapw/OmniRoute/pull/13403)) — thanks @KooshaPari +- **fix(backend):** prune old backups after health-check-repair snapshots (#13308) ([#13404](https://github.com/diegosouzapw/OmniRoute/pull/13404)) — thanks @KooshaPari +- **fix(backend):** guard memory_fts_au trigger with WHEN clause to stop FTS bloat (#13326) ([#13405](https://github.com/diegosouzapw/OmniRoute/pull/13405)) — thanks @KooshaPari +- **fix(free-tiers):** remove obsolete Together $25 signup credit (#12526) ([#13407](https://github.com/diegosouzapw/OmniRoute/pull/13407)) — thanks @KooshaPari +- **fix:** skip non-chat generation models in Test all models ([#13409](https://github.com/diegosouzapw/OmniRoute/pull/13409)) — thanks @KooshaPari +- **fix(providers):** de-duplicate agy and antigravity model catalogs ([#13410](https://github.com/diegosouzapw/OmniRoute/pull/13410)) — thanks @KooshaPari +- **fix(dashboard):** send provider in body for mini-playground web search ([#13411](https://github.com/diegosouzapw/OmniRoute/pull/13411)) — thanks @KooshaPari +- **fix(resilience):** add CJK quota-exhaustion patterns to 429 classifier ([#13412](https://github.com/diegosouzapw/OmniRoute/pull/13412)) — thanks @KooshaPari +- **fix(backend):** eval runner: failed calls no longer score as passed + dotAll regex ([#13413](https://github.com/diegosouzapw/OmniRoute/pull/13413)) — thanks @KooshaPari +- **fix(backend):** eval runner opts out of output-style and memory injection ([#13414](https://github.com/diegosouzapw/OmniRoute/pull/13414)) — thanks @KooshaPari +- **fix:** chain serial quarantine step in npm test script (fixes #13286) ([#13418](https://github.com/diegosouzapw/OmniRoute/pull/13418)) — thanks @KooshaPari +- **fix(antigravity):** derive stable session ID from credentials for prompt-cache reuse (#13305) ([#13424](https://github.com/diegosouzapw/OmniRoute/pull/13424)) — thanks @KooshaPari +- **fix(memory):** merge memory into top-level system field instead of unshifting at messages[0] (#13425) ([#13427](https://github.com/diegosouzapw/OmniRoute/pull/13427)) — thanks @KooshaPari +- **fix(compression):** preserve instruction blocks from lossy engines ([#13523](https://github.com/diegosouzapw/OmniRoute/pull/13523)) — thanks @KooshaPari +- **fix(compression):** preserve polarity words and newlines in ultra heuristic ([#13524](https://github.com/diegosouzapw/OmniRoute/pull/13524)) — thanks @KooshaPari +- **fix(compression):** run user preservePatterns before built-in patterns (#13457) ([#13525](https://github.com/diegosouzapw/OmniRoute/pull/13525)) — thanks @KooshaPari +- **fix(reasoning):** expand deepseek-v4 regex to match v4.1+ and add command-code provider (#13430) ([#13528](https://github.com/diegosouzapw/OmniRoute/pull/13528)) — thanks @KooshaPari +- **fix(providers):** add alwaysReasons metadata for reka-flash-3 ([#13531](https://github.com/diegosouzapw/OmniRoute/pull/13531)) — thanks @KooshaPari +- **fix(playground):** allow Compare columns to scroll independently ([#13532](https://github.com/diegosouzapw/OmniRoute/pull/13532)) — thanks @KooshaPari +- **fix(security):** empty whitelist allows through to prevent admin lockout ([#13534](https://github.com/diegosouzapw/OmniRoute/pull/13534)) — thanks @KooshaPari +- **fix(tests):** add DISABLE_IOREG_STRATEGY env var for macOS test compatibility (fixes #13467) ([#13539](https://github.com/diegosouzapw/OmniRoute/pull/13539)) — thanks @KooshaPari +- **fix(zed-hosted):** map developer role and Gemini enums for Zed proxy ([#13543](https://github.com/diegosouzapw/OmniRoute/pull/13543)) — thanks @KooshaPari +- **fix(analytics):** normalize provider aliases to canonical id in usage_history ([#13545](https://github.com/diegosouzapw/OmniRoute/pull/13545)) — thanks @KooshaPari +- **fix(combo):** use traceId for call log id to prevent UNIQUE constraint failure ([#13546](https://github.com/diegosouzapw/OmniRoute/pull/13546)) — thanks @KooshaPari +- **fix(auth):** check model lockout before returning synthetic noauth connection ([#13547](https://github.com/diegosouzapw/OmniRoute/pull/13547)) — thanks @KooshaPari +- **fix(guardrails):** skip credential redaction for base64 image data URLs ([#13550](https://github.com/diegosouzapw/OmniRoute/pull/13550)) — thanks @KooshaPari +- **fix(resilience):** increase requestQueue.maxWaitMs default from 15s to 30s ([#13553](https://github.com/diegosouzapw/OmniRoute/pull/13553)) — thanks @KooshaPari +- **fix(docs):** document OMNIROUTE_READY_TIMEOUT_MS and allowlist the test-only DISABLE_IOREG_STRATEGY ([#13692](https://github.com/diegosouzapw/OmniRoute/pull/13692)) + ### 📝 Maintenance ---- +- **docs(readme):** reconcile live v3.8.50 provider, free-tier, CLI, routing, test, community, sponsor, acknowledgment, and SVG metrics with their audited source denominators, including a deduplicated OmniRoute-in-Action snapshot and distinct contributor rankings for merged pull requests, GitHub-attributed commits, and Git history ([#11356](https://github.com/diegosouzapw/OmniRoute/pull/11356)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **docs:** sync the canonical provider count 354 → 356 across `README.md`, `AGENTS.md`, `llm.txt` (+ 42 i18n mirrors), the four README SVG diagrams, `docs/reference/PROVIDER_REFERENCE.md` (regenerated) and the `package.json` description after Opper (#11629) and 1min.ai (#11631) boarded the catalog — closes the `check:docs-counts-sync` strict drifts that kept `release/v3.8.51` red ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11673](https://github.com/diegosouzapw/OmniRoute/pull/11673)) — thanks @jonlwheat2-gif +- **test(providers):** update count-derived assertions after the v3.8.51 provider additions — `APIKEY_PROVIDERS` 233 → 235 (Opper #11629 + 1min.ai #11631), reserved-prefix REGISTRY walk 395 → 398, `WEB_FETCH_PROVIDERS` now includes `nimble-search` (#11620), and the provider translate-path golden snapshot regenerated ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11674](https://github.com/diegosouzapw/OmniRoute/pull/11674)) — thanks @jonlwheat2-gif +- **test(sse):** bump the hard-lease connection-query inventory for `src/lib/tokenHealthCheck.ts` to 2 — the verify-only web-cookie sweep added by #11495 split the single `getProviderConnections` call into oauth + cookie variants, which the frozen inventory had not tracked ([#11495](https://github.com/diegosouzapw/OmniRoute/pull/11495)). ([#11677](https://github.com/diegosouzapw/OmniRoute/pull/11677)) — thanks @jonlwheat2-gif / @oyi77 +- **chore(release):** clear the `release/v3.8.51` base-red gates — docs migration count (+38 i18n mirrors), stryker `tap.testFiles`, lockfile registry host, stale ESLint suppressions, the 7 lint regressions they were hiding, the stale `APIKEY_PROVIDERS` partition count (231 → 233), the `glm-5.3-max` tier missing from the GLM test table, a dead `modelType === "chat"` comparison in the models catalog, and the `resolveLiveWsUrl` / `sanitizeLiveWsPort` helpers from #11388 that a later merge dropped from `wsPath.ts` (dashboard live WebSocket URL resolution) ([#11502](https://github.com/diegosouzapw/OmniRoute/pull/11502)) — thanks @MumuTW +- **chore(free-models):** the free-tier regime table now declares which totals figure each regime feeds and whether it may take the no-auth shortcut, so the sets that used to repeat those answers by hand are derived from it — a new `freeType` no longer compiles until it has answered every question, instead of silently contributing to no total ([#11537](https://github.com/diegosouzapw/OmniRoute/pull/11537)) — thanks @maxmad64bis +- **docs(routing):** LKGP strategy copy now states provider-level stickiness and the explicit rules fallback instead of promising per-target pinning ([#11555](https://github.com/diegosouzapw/OmniRoute/pull/11555)) — thanks @maxmad64bis +- **test(chatcore):** pin the response tool-name alias resolution against the ordering hazard that broke every Gemini/Antigravity MCP tool call in v3.8.49. `extractRequestToolIdentityMap` deletes `translatedBody._toolNameMap`, so the later read is always undefined and the ledger survives only through the `requestToolIdentityMap` fallback — removing that fallback previously left the entire tool-name suite green. The resolution moves into `resolveResponseToolNameMap()` next to the map it depends on, with a regression guard that fails without the recovery. No behaviour change. ([#11575](https://github.com/diegosouzapw/OmniRoute/pull/11575)) — thanks @amartinawi +- **docs(env):** document `OMNIROUTE_CORPUS_CACHE_SIZE` in `.env.example` and `ENVIRONMENT.md`, so the env/docs contract check stops failing Docs Gates on every branch ([#11576](https://github.com/diegosouzapw/OmniRoute/pull/11576)) — thanks @ntdatt812 +- **chore(stryker):** register `route-guard-tunnel-processes-local-only` and `universal-quota-aware-routing` in `tap.testFiles`, so their mutant kills count for the three modules they cover ([#11577](https://github.com/diegosouzapw/OmniRoute/pull/11577)) — thanks @ntdatt812 +- **test(authz):** pin `LOCAL_ONLY_API_GET_EXEMPTIONS` by exact membership instead of by entry count, so the guard names the offending path and also catches a substitution ([#11580](https://github.com/diegosouzapw/OmniRoute/pull/11580)) — thanks @ntdatt812 +- **test(build):** pin `resolveNextBuildBundlerFlag` to the `OMNIROUTE_USE_TURBOPACK` contract it implements, across runtimes, instead of asserting an unimplemented Bun override ([#11583](https://github.com/diegosouzapw/OmniRoute/pull/11583)) — thanks @ntdatt812 +- **test(claude-web):** the Claude Web transport test's barrel anchors no longer pin the exact construction spelling. #11421 made the executor map lazy, so `/"claude-web": new ClaudeWebExecutor\(\)/` stopped matching and the test went red on `release/v3.8.51` itself — a base-red gate every open PR inherited. The anchors now match the alias key and the class name and tolerate either spelling in between, and were checked to still fail when an alias is genuinely repointed at another executor ([#11585](https://github.com/diegosouzapw/OmniRoute/pull/11585)) — thanks @ntdat812 +- **test(models):** pin `qwen3.8-max` resolution per provider — two of the four catalogs now serve the bare id and no longer carry `-preview` ([#11590](https://github.com/diegosouzapw/OmniRoute/pull/11590)) — thanks @ntdatt812 +- **test(executors):** drop the stale 128-tool truncation assertion that contradicted the #11444 pass-through contract ([#11591](https://github.com/diegosouzapw/OmniRoute/pull/11591)) — thanks @ntdatt812 +- **test(token-health):** pin the current terminal-skip boundary — expired with retry budget is probed, `account_deactivated` stays skipped ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)) — thanks @ntdatt812 +- **test(models):** follow #11503's retarget of the `gemini-3-pro-high` alias to the hyphenated catalog id ([#11593](https://github.com/diegosouzapw/OmniRoute/pull/11593)) — thanks @ntdatt812 +- **refactor(video bridge):** extract per-video acquisition, whole-result caching, description, and metrics/abort/cleanup out of `VideoBridgeGuardrail.preCall` into a `processVideoPart` seam in a new `videoBridgePipeline.ts`, behind explicit `VideoMediaBrokerPort`, `VideoAudioTranscriptionPort`, and `VideoDrilldownPort` boundaries; `preCall` now only handles request traversal, policy, and response aggregation. The Video tab's FFmpeg/ffprobe runtime status is now an explicit `unknown` / `restricted` / `unavailable` / `available` state instead of a nullable boolean pair, fixing a case where an in-flight or failed probe was mislabeled as "install FFmpeg" ([#11657](https://github.com/diegosouzapw/OmniRoute/issues/11657)). ([#12016](https://github.com/diegosouzapw/OmniRoute/pull/12016)) +- **test(opencode-plugin):** add unit test coverage for `formatFreeBudget()` naming helper ([#11660](https://github.com/diegosouzapw/OmniRoute/pull/11660)) — thanks @f9td56dbgh-hub +- **docs(video):** clarify that the Video Bridge transcript `source` field (`client`, `embedded`, `audio-bridge`) is presently caller-declared and not yet server-verified — OmniRoute enforces the enum shape but does not cryptographically confirm that an `embedded`/`audio-bridge` label came from a server-owned extraction ([#11661](https://github.com/diegosouzapw/OmniRoute/issues/11661)). ([#12001](https://github.com/diegosouzapw/OmniRoute/pull/12001)) +- **chore(providers):** retire the distributed Raycast Relay and Hailuo Web integrations after a provenance audit confirmed substantial derivation from GPL-3.0 sources; the official GitHub and MiniMax API providers remain available. Migration 163 disables legacy connections and invalidates their active leases while preserving connection IDs, audit/history rows, quota snapshots, usage records, and fail-closed API-key connection policies. ([#11691](https://github.com/diegosouzapw/OmniRoute/pull/11691)) +- **chore(providers):** retire the Felo Web provider while its GPL-derived provenance remains on licensing HOLD ([#11698](https://github.com/diegosouzapw/OmniRoute/pull/11698)) — stale runtime and database references now fail closed while existing connection, lease, and usage history remains preserved for auditability. +- **fix(sse):** retire the Gemini Web image-generation bridge while provenance and licensing remain on HOLD, preserving Gemini Web chat/auth/tools and legitimate Gemini/Nano Banana image providers ([#11708](https://github.com/diegosouzapw/OmniRoute/pull/11708)) +- **chore(audio):** retire the built-in EdgeTTS provider and its 11-voice catalog while provenance/licensing review remains on HOLD; the generic `/v1/audio/speech` endpoint and the other speech providers remain available ([#11711](https://github.com/diegosouzapw/OmniRoute/pull/11711)) +- **fix(providers):** retire the unofficial Qwen Web integration and its `qwen-web`/`qw` identities while third-party provenance remains on HOLD ([#11713](https://github.com/diegosouzapw/OmniRoute/pull/11713)) — direct requests now fail closed with a sanitized HTTP 410 and combos skip the retired target, while migration guards preserve connection IDs, allowlists, usage/audit history and invalidate active leases. Supported Qwen API, Qwen Code, cloud, token-plan, audio, local and model-name surfaces remain available; restoring Qwen Web requires a qualified provenance/license decision. +- **fix(providers):** retire `microsoft-designer-web` and its `msdesigner` alias from runtime and catalogs while preserving stored credentials, provider history and allowlists as inactive records — source dependency on gpt4free's [`MicrosoftDesigner.py`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/g4f/Provider/needs_auth/MicrosoftDesigner.py) is proven at snapshot `0943d16eef757a44deb60651c07bbe5fa958cafe`, whose [`LICENSE`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/LICENSE) is GPL-3.0; expressive adaptation is probable, which records provenance/licensing risk without asserting a legal conclusion ([#11720](https://github.com/diegosouzapw/OmniRoute/pull/11720)). +- **docs(legal):** centralize pinned MIT notices for the vendored GCF codec and local flag assets ([#11726](https://github.com/diegosouzapw/OmniRoute/pull/11726)) +- Split the 2,952-line `adobeFireflyClient.ts` into nine focused modules (catalog, credentials, payload, ARP session, upload, responses, IMS, account, poll) behind an unchanged barrel — no consumer edits and no public API change (#11728). — thanks @MumuTW +- **fix(dashboard):** replaced the Model Lockout card's unprovenanced notification MP3 with a short, locally synthesized Web Audio chime ([#11731](https://github.com/diegosouzapw/OmniRoute/pull/11731)) — both toggles retain optional audible feedback without shipping replacement media or third-party code, and unsupported, suspended, or rejected audio contexts fail open without blocking the setting change. +- **chore(deepseek-web):** remove the provenance-unresolved DeepSeek PoW WASM binary and its runtime loader/tracing while retaining the existing JavaScript solver (slower at high difficulty) ([#11732](https://github.com/diegosouzapw/OmniRoute/pull/11732)). +- **chore(assets):** remove four unused provider assets whose redistribution provenance could not be verified while retaining the active Freebuff SVG ([#11735](https://github.com/diegosouzapw/OmniRoute/pull/11735)) +- **docs(assets):** retired two raster images whose redistribution provenance could not be verified ([#11736](https://github.com/diegosouzapw/OmniRoute/pull/11736)) — PicoClaw documentation now reuses the existing SVG, while Zoo Code remains a text link without an image. +- **docs(legal):** add pinned notices for six LobeHub-derived provider SVGs and 65 byte-exact theSVG assets, retaining registry claims as unverified metadata with explicit trademark and HOLD boundaries ([#11737](https://github.com/diegosouzapw/OmniRoute/pull/11737)). +- **chore(quality):** added a versioned 225/225 provider-asset provenance manifest and blocking drift gate ([#11743](https://github.com/diegosouzapw/OmniRoute/pull/11743)) — the inventory pins magic MIME and SHA-256 for every physical file, records immutable source matches for 71 assets, keeps 69 probable and 85 unresolved entries explicit but non-blocking, and treats provenance as source evidence rather than copyright or trademark clearance. +- **refactor(mitm):** replace four previously derived Traffic Inspector utilities — conversation normalization, SSE merging, secret masking, and header sanitization — with independent clean-room implementations based on public provider schemas, WHATWG SSE, and RFC 6750, while keeping the two independently classified passthrough surfaces unchanged ([#11748](https://github.com/diegosouzapw/OmniRoute/pull/11748)). +- Added a unit test that fails seven days before any dated pack under `config/` (`validUntil` and sibling keys) lapses, naming the file and key. The Alibaba free-tier pack expired on 2026-08-27 and turned every PR red the next morning with no commit involved; renewal now happens on someone's terms, not the clock's. ([#11891](https://github.com/diegosouzapw/OmniRoute/pull/11891)) +- `check:workflows` now fails (under `--strict`/`--ratchet`) when any job routed to a self-hosted runner publishes with `--provenance` — npm rejects that with `422` at the registry, which in v3.8.50 only surfaced after the tag and Docker images were public. ([#11895](https://github.com/diegosouzapw/OmniRoute/pull/11895)) +- `scripts/ops/runner-janitor.sh` now proves a path is idle with one `lsof` snapshot and removes stale leftovers itself (tmpfs after 3 h — it is RAM — disk after 24 h), kills orphan `next-build` processes, prunes checkouts of stopped runners, and alerts on memory pressure; `--dry-run` shows exactly what it would do. `docs/ops/RUNNER_BOX.md` reconciled to the measured box (31 GB, 10 listeners). ([#11893](https://github.com/diegosouzapw/OmniRoute/pull/11893)) +- The `next-build` artefact (1.3 GB) is now written and read under `$RUNNER_TEMP` (per-runner, on disk) instead of `/tmp`, which on the self-hosted pool is a 12 GB tmpfs in RAM. Landing it there took 27–32 of the publish job's 76 minutes, and the fixed `/tmp/e2e-build.tar.gz` name let E2E jobs on different runners overwrite each other's download. ([#11896](https://github.com/diegosouzapw/OmniRoute/pull/11896)) +- The CI `build` job now runs in two concurrency lanes — `main` and pull requests — so a release build is never queued behind (or OOM-killed beside) PR builds on the self-hosted pool, which holds one `next-build` comfortably and two at the edge. ([#11901](https://github.com/diegosouzapw/OmniRoute/pull/11901)) +- Re-freeze the ESLint suppressions on `release/v3.8.51` from a clean-room measurement (2 stale file entries pruned, 55 pre-existing `no-explicit-any` in six new files frozen under #11924) and drop the dead `GPT_SIZE_MAP` constant orphaned by the Adobe Firefly client split, so `No new ESLint warnings` stops failing every PR with exit 2 (Refs #11924) ([#11955](https://github.com/diegosouzapw/OmniRoute/pull/11955)) +- Type the 55 `no-explicit-any` sites that had been frozen under #11924 — four redundant casts in `socksConnectorWithFamily.ts` (undici/socks types already accept them) and the mocks/fixtures of the socks-timeout and isFree suites — and drop their suppression entries; the ESLint ratchet shrinks from 5487 to 5432 (Closes #11924) ([#11975](https://github.com/diegosouzapw/OmniRoute/pull/11975)) +- Take the two hosted-runner builds off the PR rail: `Build App` (`build.yml`) is `workflow_dispatch`-only and `DAST smoke (PR)` runs only for PRs into `main` — the 7 GB hosted VM cannot build `release/v3.8.51` in any profile (19/30 red, VM shutdown ~8 min into `next build`) and both checks had turned into permanent noise on every release PR; the bundle stays validated by `ci.yml` on `main` and by `nightly-release-green` on `release/**` (Closes #11946) ([#11962](https://github.com/diegosouzapw/OmniRoute/pull/11962)) +- Move the four nightly jobs that build the backend (`nightly-schemathesis`, `nightly-llm-security` promptfoo + garak, `nightly-resilience` axe-a11y) off the hosted 7 GB runner — where they died on `release/v3.8.51` unseen — onto the box's new `omni-light` pool (two listeners), and document the reshaped fleet (4 active OmniRoute listeners: 2 `omni-build` + 2 `omni-light`, janitor ceiling 4) (Closes #11965) ([#11967](https://github.com/diegosouzapw/OmniRoute/pull/11967)) +- Stop painting every fork PR into `release/**` red: `quality.yml` `Build (advisory)` is skipped (GitHub still reports `continue-on-error` failures as check FAILURE). Hosted `ubuntu-latest` cannot finish `npm run build` on this tree — same class as #11962 taking `build.yml` off the PR rail. `docker-publish.yml` amd64 now runs on the `.113` `omni-build` pool (31 GB / 32 cores, two listeners) with Turbopack, shares the `heavy-build-main` lane with `ci.yml` `Build` so it queues instead of becoming a third heavy, and keeps per-ref concurrency (a merge storm was starting 8 concurrent OOM builds). arm64 stays on `ubuntu-24.04-arm` with webpack — there is no ARM box. Fallback when `USE_VPS_RUNNER` is off: hosted amd64 + webpack (#11976). ([#12021](https://github.com/diegosouzapw/OmniRoute/pull/12021)) +- `docker-publish.yml` builds with webpack (`OMNIROUTE_USE_TURBOPACK=0`) on **both** arches, including amd64 on the `.113` `omni-build` pool. Turbopack had the RAM (31 GB, `omniroute-113-6`) and still panicked (`TurbopackInternalError: there must be a path to a root`, run 33253576569, 26 min in). The same tree's arm64 webpack build on hosted `ubuntu-24.04-arm` succeeded (run 33264823398). Dockerfile already documents webpack as the Docker escape hatch (#11976). ([#12050](https://github.com/diegosouzapw/OmniRoute/pull/12050)) +- **docs(free-tier):** declare the counting vs deciding regimes for "is it free?" and guard the deciding path from DB-backed catalog resolution ([#12226](https://github.com/diegosouzapw/OmniRoute/pull/12226)) — thanks @maxmad64bis +- **docs(auto-combo):** The mode pack table in `docs/routing/AUTO-COMBO.md` now lists all six shipped packs with every weight each one sets, replacing a four-pack table whose numbers had also drifted from the source. It states plainly that no pack sets `quality`, so selecting any pack silences the observed-quality signal. Six more documents that quote the scoring factor count joined the `check:docs-counts` gate, which caught five stale claims — including one naming nine factors that do not exist — and two stale mode pack counts. The dashboard routing panel, which offered four of the six packs and labelled the default strategy "6-Factor Scoring", is now covered by a test; the two packs it was missing are `reliability-first` and `chaos-mode`, the latter labelled as the fault-injection profile it is rather than as one more routing preference ([#12316](https://github.com/diegosouzapw/OmniRoute/pull/12316)) — thanks @maxmad64bis +- **chore(quality):** rebaseline `src/lib/db/apiKeys.ts` for the ACL the key-creation path now preserves ([#12352](https://github.com/diegosouzapw/OmniRoute/pull/12352)) ([#12673](https://github.com/diegosouzapw/OmniRoute/pull/12673)) — thanks @KrzysiekSko +- **chore(providers):** bump the Claude Code wire identity and the Devin bridge image pin from `2.1.220` to `2.1.258` ([#12402](https://github.com/diegosouzapw/OmniRoute/pull/12402)) — thanks @ggiak ([#12604](https://github.com/diegosouzapw/OmniRoute/pull/12604)) +- **docs(env):** align `.env.example`, the README Bun section, and the troubleshooting guide with the code: `OMNIROUTE_USE_TURBOPACK` also governs `npm run build` (not dev-only), `bun run build` follows that flag instead of auto-selecting Webpack, `OMNIROUTE_CHAT_MAX_HEAVY_IN_FLIGHT` is unset by default (no request-count cap), and the structural `503 chat_admission_busy` message matches `chatAdmissionResponses.ts` (#12404 — thanks @pacocartones) +- **chore(electron):** upgrade the desktop app to Electron 44 (Chromium 152, Node 24.18.1) ([#12217](https://github.com/diegosouzapw/OmniRoute/pull/12217)). **Requires macOS 13 (Ventura) or later** — Chromium dropped macOS 12 (Monterey), so Monterey users must stay on an earlier OmniRoute desktop build. Windows and Linux are unaffected; the app already shipped only x64/arm64, so Electron 44 dropping 32-bit builds changes nothing. Removes the `openAsHidden`/`wasOpenedAsHidden` login-item fields deleted in Electron 44 — hidden autostart continues to work through the `--hidden` argument registered with the login item ([#12554](https://github.com/diegosouzapw/OmniRoute/pull/12554)) — thanks @app/dependabot +- **chore(quality):** rebaseline `src/sse/handlers/chat.ts` for the effective-input persistence the continuation fix needs ([#12641](https://github.com/diegosouzapw/OmniRoute/pull/12641)) ([#12680](https://github.com/diegosouzapw/OmniRoute/pull/12680)) — thanks @hartmark +- `Coverage` job on `ci.yml`: the informational Codecov upload gets its own 5-minute ceiling and `continue-on-error`, and the job budget grows from 20 to 30 minutes (the 8-shard c8 merge alone takes ~10) — a stalled upload no longer ends the job `cancelled` and drags a fully green `main` run's conclusion down with it ([#11972](https://github.com/diegosouzapw/OmniRoute/pull/11972)) +- CI hardening on the PR rail: the four `Unit Tests fast-path` shards get `timeout-minutes: 30` (a hung shard held a PR for 64 min instead of GitHub's 6 h default) and both ESLint file caches lose their `restore-keys` fallback, so a cache built under another suppressions file or lint config can no longer report stale verdicts (Refs #11600, #11924) ([#11963](https://github.com/diegosouzapw/OmniRoute/pull/11963)) +- Every CI job that runs a `next build` (`build`, the npm `publish`, both release-green validations) now targets the `omni-build` runner label, which only two of the eight self-hosted runners carry. The box holds one build comfortably and two at the edge; a third now queues on GitHub instead of being OOM-killed by the kernel. ([#11932](https://github.com/diegosouzapw/OmniRoute/pull/11932)) +- **refactor(combo):** move `handleRoundRobinCombo` (and `resolveTargetTokenLimit`) into `open-sse/services/combo/roundRobinCombo.ts`. `combo.ts` drops from 2164 to 1014 lines (`split("\n").length`); the leaf is 1199 (under the 1200 new-file cap). The round-robin call site uses a dynamic `import()` so `releaseStickyPinOnFailure` / `clearStaleLKGP` can stay exported from `combo.ts` without a static cycle. Skip / sticky / semaphore / safety-timer behavior is unchanged. ([#12811](https://github.com/diegosouzapw/OmniRoute/pull/12811)) — thanks @HouMinXi +- **chore(quality):** rebaseline the file-size caps the error-boundary campaign grew past (`open-sse/executors/codex.ts`, `open-sse/vendor/codex-chatgpt-web/bridge.ts`, both via [#12444](https://github.com/diegosouzapw/OmniRoute/pull/12444)) ([#12654](https://github.com/diegosouzapw/OmniRoute/pull/12654)) +- **chore(quality):** rebaseline the file-size caps the hartmark batch grew past (`combos/page.tsx` via [#12355](https://github.com/diegosouzapw/OmniRoute/pull/12355), `open-sse/services/combo.ts` via [#12338](https://github.com/diegosouzapw/OmniRoute/pull/12338)) ([#12623](https://github.com/diegosouzapw/OmniRoute/pull/12623)) — thanks @hartmark +- **chore(quality):** rebaseline the file-size caps the HouMinXi batch grew past when its PRs stacked (`providers/page.tsx`, `chatCore.ts`, `accountFallback.ts`) — each PR measured correctly in isolation, none saw the stacking ([#12619](https://github.com/diegosouzapw/OmniRoute/pull/12619)) +- **chore(quality):** rebaseline `open-sse/services/combo.ts` for the reset-aware scoring the HouMinXi batch stacked ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) ([#12678](https://github.com/diegosouzapw/OmniRoute/pull/12678)) — thanks @HouMinXi +- **docs:** sync provider catalog count (direct commit `4f11b2ae3d`) +- **docs(skills):** sync omni-inference search fallback (direct commit `b4e76a7ed9`) +- **chore(lint):** prune two stale base suppressions (direct commit `1325047d56`) +- **test(auto-combo):** tolerate full-suite rotation load (direct commit `ad3e293f9f`) +- **test:** drain inherited quality snapshot reds (direct commit `21ed68d8ac`) +- **chore(quality):** rebaseline measured CLI bundle growth (direct commit `d35b3f9779`) +- **test(ui):** reproduce invalid AnySearch icon alias (direct commit `12cc6ca834`) +- **test(translator):** fix the relative imports of the relocated deferred-finish test (direct commit `777d9d1629`) +- **docs(counts):** bring the documented provider and migration counts to the measured values (direct commit `e70bea30e9`) +- **test(security):** drop the guard-target set left unused by the sync (direct commit `a32ba0e362`) +- **chore(db):** renumber model_capabilities migration 163 → 169 (cross-branch collision with 163_radar_feed_cache_generated_at) (direct commit `70f5d4cbf9`) +- **docs(cli):** document CLI_PRIME_AGENT_BIN and correct the CLI Agents count (direct commit `9058e39b61`) +- **chore(deps):** refresh runtimes and adopt ESLint 10 ([#11259](https://github.com/diegosouzapw/OmniRoute/pull/11259)) — thanks @backryun +- **refactor(local-corpus):** implement dynamic root resolution and LRU cache ([#11491](https://github.com/diegosouzapw/OmniRoute/pull/11491)) — thanks @raheemuddin786 +- **chore(quality):** prune stale eslint-suppressions entries ([#11596](https://github.com/diegosouzapw/OmniRoute/pull/11596)) +- **chore(ci):** unblock quality gates - env-doc sync + stryker tap.testFiles registration (#9282) ([#11609](https://github.com/diegosouzapw/OmniRoute/pull/11609)) — thanks @Meet6338-X +- **chore(ci):** point the circular-deps gate at dpdm's real JS entrypoint ([#11615](https://github.com/diegosouzapw/OmniRoute/pull/11615)) — thanks @benzntech +- **chore(quality):** rebaseline file-size for #11448/#11495/#11561 growth ([#11630](https://github.com/diegosouzapw/OmniRoute/pull/11630)) +- **deps:** realign onnxruntime-node pin to @huggingface/transformers' 1.24.3 lockstep ([#11633](https://github.com/diegosouzapw/OmniRoute/pull/11633)) — thanks @jonlwheat2-gif +- **test(autoCombo):** port models_dev_tier #11508 guard from node:test to vitest ([#11635](https://github.com/diegosouzapw/OmniRoute/pull/11635)) — thanks @jonlwheat2-gif +- **chore(quality):** rebaseline file-size for #11083/#11631/#11628 growth ([#11636](https://github.com/diegosouzapw/OmniRoute/pull/11636)) +- **chore(lint):** prune stale ESLint suppressions (base-red #11449) ([#11644](https://github.com/diegosouzapw/OmniRoute/pull/11644)) — thanks @jonlwheat2-gif +- **docs(changelog):** aggregate the ten v3.8.50 fragments into CHANGELOG.md ([#11683](https://github.com/diegosouzapw/OmniRoute/pull/11683)) +- **chore(search):** drop unreachable credential sweep after #11642 ([#11687](https://github.com/diegosouzapw/OmniRoute/pull/11687)) — thanks @HouMinXi +- **ci:** add API route TypeScript regression gate ([#11705](https://github.com/diegosouzapw/OmniRoute/pull/11705)) — thanks @Sabeekhann +- **docs(dedup):** correct the CodeQL claim on the tenant-namespace comment ([#11712](https://github.com/diegosouzapw/OmniRoute/pull/11712)) +- **docs(changelog):** add consolidated v3.8.50 stats, top-25 ranking and a .mailmap ([#11715](https://github.com/diegosouzapw/OmniRoute/pull/11715)) +- **docs:** recommend gstack for AI-assisted workflows ([#11770](https://github.com/diegosouzapw/OmniRoute/pull/11770)) — thanks @santosraju99-hub +- **chore(quality):** tighten complexity/cognitive-complexity ratchets to the current tip; land the missed gateways.ts rebaseline ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **docs(i18n):** finish Freepik → Magnific rebrand in locale strings and README ([#11772](https://github.com/diegosouzapw/OmniRoute/pull/11772)) — thanks @turbolego +- **refactor(auto-combo):** fix divergent scoring in combo health reporting ([#11854](https://github.com/diegosouzapw/OmniRoute/pull/11854)) — thanks @jacobsparts +- **chore(changelog):** add missing fragments for #11919/#11918/#11916 ([#11938](https://github.com/diegosouzapw/OmniRoute/pull/11938)) +- **test(check):** escape the runs-on fixture with JSON.stringify, not a quote-only replace ([#11942](https://github.com/diegosouzapw/OmniRoute/pull/11942)) (direct commit `fb7445eaa3`) +- **test(release):** align five suites with the contracts #11933, #11919 and #11876 shipped on release/v3.8.51 ([#11944](https://github.com/diegosouzapw/OmniRoute/pull/11944)) +- **chore(quality):** remove unreachable code and restore test discovery ([#11950](https://github.com/diegosouzapw/OmniRoute/pull/11950)) — thanks @backryun +- **docs(agents):** sync-back landings are fast-forward, never squash ([#11964](https://github.com/diegosouzapw/OmniRoute/pull/11964)) +- **test(infra):** retry recursive temp-dir removal instead of failing a shard on ENOTEMPTY (#11966) ([#11968](https://github.com/diegosouzapw/OmniRoute/pull/11968)) +- **docs(ops):** the .113 heavy-build ceiling is one runner, not two ([#12048](https://github.com/diegosouzapw/OmniRoute/pull/12048)) +- **test(ui):** correct inactive auto-fetch expectation ([#12098](https://github.com/diegosouzapw/OmniRoute/pull/12098)) — thanks @RaviTharuma +- **docs:** document native dependency check escape hatch ([#12101](https://github.com/diegosouzapw/OmniRoute/pull/12101)) — thanks @RaviTharuma +- **test(providers):** regenerate the translate-path golden for OrcaRouter (#11923) ([#12118](https://github.com/diegosouzapw/OmniRoute/pull/12118)) +- **chore(quality):** velocity phase — loosen every numeric baseline by 20% until v4.0, monitor headroom nightly ([#12125](https://github.com/diegosouzapw/OmniRoute/pull/12125)) +- **chore(lint):** batch 0 of #12146 — type the call-log-cap sqlite rows instead of 45 as-any casts ([#12157](https://github.com/diegosouzapw/OmniRoute/pull/12157)) +- **chore(lint):** batch 4 of #12146 — shared/components react-hooks violations resolved ([#12159](https://github.com/diegosouzapw/OmniRoute/pull/12159)) +- **chore(lint):** batch 1 of #12146 — dashboard/cli-code react-hooks violations resolved ([#12160](https://github.com/diegosouzapw/OmniRoute/pull/12160)) +- **chore(lint):** batch 3 of #12146 — dashboard/settings react-hooks violations resolved ([#12162](https://github.com/diegosouzapw/OmniRoute/pull/12162)) +- **chore(lint):** batch 2 of #12146 — resolve the react-hooks compiler violations in dashboard/providers ([#12163](https://github.com/diegosouzapw/OmniRoute/pull/12163)) +- **chore(quality):** register search-432 cooldown test in stryker tap.testFiles ([#12170](https://github.com/diegosouzapw/OmniRoute/pull/12170)) +- **chore(lint):** batch 5 of #12146 — combos, endpoint, provider-stats, api-manager and costs react-hooks violations resolved ([#12174](https://github.com/diegosouzapw/OmniRoute/pull/12174)) +- **build:** add contributor fast profile ([#12192](https://github.com/diegosouzapw/OmniRoute/pull/12192)) — thanks @rafacpti23 +- **docs(readme):** README touch-ups ([#12193](https://github.com/diegosouzapw/OmniRoute/pull/12193)) ([#12194](https://github.com/diegosouzapw/OmniRoute/pull/12194)) ([#12202](https://github.com/diegosouzapw/OmniRoute/pull/12202)) — thanks @rafacpti23 +- **build:** force the webpack bundler in the contributor build profile ([#12197](https://github.com/diegosouzapw/OmniRoute/pull/12197)) — thanks @rafacpti23 +- **build:** skip standalone packaging in the contributor profile ([#12198](https://github.com/diegosouzapw/OmniRoute/pull/12198)) — thanks @rafacpti23 +- **docs(audit):** align every published number with the code and harden check:docs-counts ([#12200](https://github.com/diegosouzapw/OmniRoute/pull/12200)) +- **build:** stub the instrumentation entrypoints in the contributor profile ([#12203](https://github.com/diegosouzapw/OmniRoute/pull/12203)) — thanks @rafacpti23 +- **build:** omit the standalone output target for contributor builds ([#12204](https://github.com/diegosouzapw/OmniRoute/pull/12204)) — thanks @rafacpti23 +- **chore(lint):** batch 6 of #12146 — memory, radar, audit, analytics, cache, usage, activity, home and RequestLoggerV2 react-hooks violations resolved ([#12208](https://github.com/diegosouzapw/OmniRoute/pull/12208)) +- **docs(diagrams):** rename number-carrying diagram files to stable names ([#12210](https://github.com/diegosouzapw/OmniRoute/pull/12210)) +- **docs(dashboard):** redraw onboarding tier-flow SVGs for the real 4-tier model ([#12211](https://github.com/diegosouzapw/OmniRoute/pull/12211)) +- **docs(api):** document every implemented route in openapi.yaml (276 → 692 paths) ([#12212](https://github.com/diegosouzapw/OmniRoute/pull/12212)) +- **chore(lint):** batch 7 of #12146 — final src tail: 37 react-hooks violations across 33 files resolved ([#12244](https://github.com/diegosouzapw/OmniRoute/pull/12244)) +- Revert "docs: recommend gstack for AI-assisted workflows (#11770)" ([#12248](https://github.com/diegosouzapw/OmniRoute/pull/12248)) ([#12249](https://github.com/diegosouzapw/OmniRoute/pull/12249)) +- **docs:** Chaos Mode setup guide + weighted strategy semantics ([#12250](https://github.com/diegosouzapw/OmniRoute/pull/12250)) +- **docs(agents):** protected-surface merge rule — operator approval for agent-instruction files ([#12253](https://github.com/diegosouzapw/OmniRoute/pull/12253)) +- **chore(quality):** register native-codex turn-pin tests in stryker tap.testFiles ([#12263](https://github.com/diegosouzapw/OmniRoute/pull/12263)) +- **chore(quality):** dedupe tap.testFiles entries added by racing base-red fixes ([#12265](https://github.com/diegosouzapw/OmniRoute/pull/12265)) +- **docs(readme):** introduce OmniRouteTray — the macOS menu-bar companion ([#12276](https://github.com/diegosouzapw/OmniRoute/pull/12276)) — thanks @ggiak +- **chore(proxy):** purge the legacy 1proxy residue — sync/rotator modules, dead DB exports, dead settings tab and flag, docs (#12091) ([#12290](https://github.com/diegosouzapw/OmniRoute/pull/12290)) +- **chore(quality):** baseline-headroom skips generated and vendored files in the fileSize worst-file signal ([#12291](https://github.com/diegosouzapw/OmniRoute/pull/12291)) +- **refactor(video):** unify the JPEG frame data-URI contract ([#12322](https://github.com/diegosouzapw/OmniRoute/pull/12322)) +- **chore(deps):** freeze onnxruntime-node and eslint-plugin-react-hooks out of dependabot groups ([#12329](https://github.com/diegosouzapw/OmniRoute/pull/12329)) +- **test(executors):** restore the #10986 reasoning-only fallback guards ([#12364](https://github.com/diegosouzapw/OmniRoute/pull/12364)) — thanks @pacocartones +- **chore(quality):** re-tighten the file-size ratchet to the real LOC (plan 3.8.52 task 0) ([#12411](https://github.com/diegosouzapw/OmniRoute/pull/12411)) +- **test(grok):** format web executor suite ([#12412](https://github.com/diegosouzapw/OmniRoute/pull/12412)) +- **chore(quality):** ratchet open-sse typecheck baseline to zero ([#12418](https://github.com/diegosouzapw/OmniRoute/pull/12418)) +- **test(build):** guard the artifact path policy arrays against duplicates ([#12422](https://github.com/diegosouzapw/OmniRoute/pull/12422)) +- **chore(lint):** adopt eslint-plugin-react-hooks 7.1.1 ([#12428](https://github.com/diegosouzapw/OmniRoute/pull/12428)) +- **chore(providers):** remove a keyless provider integration at its operator's request ([#12440](https://github.com/diegosouzapw/OmniRoute/pull/12440)) +- **docs(providers):** register providers removed at their operator's request and guard against reintroduction ([#12478](https://github.com/diegosouzapw/OmniRoute/pull/12478)) +- **chore(quality):** base fixes — stryker tap.testFiles + node_modules cache key ([#12482](https://github.com/diegosouzapw/OmniRoute/pull/12482)) +- **docs(arch):** one-process recipe for tens of long /v1/responses ([#12493](https://github.com/diegosouzapw/OmniRoute/pull/12493)) — thanks @RaviTharuma +- **chore(quality):** tighten the CodeQL ratchet baseline from 11 to 6 ([#12530](https://github.com/diegosouzapw/OmniRoute/pull/12530)) +- **chore(deps):** pin browserslist override to ^4.28.8 ([#12592](https://github.com/diegosouzapw/OmniRoute/pull/12592)) — thanks @KooshaPari +- **chore(deps):** bump fast-uri to 3.1.7 in the electron lockfile ([#12601](https://github.com/diegosouzapw/OmniRoute/pull/12601)) +- **docs(quality):** document how the CodeQL ratchet refreshes and how to tighten it ([#12611](https://github.com/diegosouzapw/OmniRoute/pull/12611)) +- **test(ci):** pin the openapi-security-tiers two-arm contract with an executing gate test (#12581) ([#12652](https://github.com/diegosouzapw/OmniRoute/pull/12652)) +- **docs(gamification):** align XP Rewards table with code (#12501) ([#12667](https://github.com/diegosouzapw/OmniRoute/pull/12667)) — thanks @KooshaPari +- **refactor(combo):** split executeTarget into gates, attempt, and loop ([#12746](https://github.com/diegosouzapw/OmniRoute/pull/12746)) — thanks @HouMinXi +- **docs(security):** document socket.yml scanner config + CI workflow link (#12575) ([#12764](https://github.com/diegosouzapw/OmniRoute/pull/12764)) — thanks @KooshaPari +- **chore(ci):** guard commit identity in pre-commit to stop author misattribution ([#12772](https://github.com/diegosouzapw/OmniRoute/pull/12772)) +- **deps:** 17 Dependabot bumps — bump electron from 43.4.0 to 43.4.1 in /electron ([#11426](https://github.com/diegosouzapw/OmniRoute/pull/11426)); bump the development group across 1 directory with 8 updates ([#11428](https://github.com/diegosouzapw/OmniRoute/pull/11428)); bump the production group across 1 directory with 11 updates ([#11440](https://github.com/diegosouzapw/OmniRoute/pull/11440)); chore(deps): bump github/codeql-action/init from 4.37.7 to 4.37.8 ([#11925](https://github.com/diegosouzapw/OmniRoute/pull/11925)); chore(deps): bump github/codeql-action from 4.37.7 to 4.37.8 ([#11926](https://github.com/diegosouzapw/OmniRoute/pull/11926)); chore(deps): bump github/codeql-action/analyze from 4.37.7 to 4.37.8 ([#11927](https://github.com/diegosouzapw/OmniRoute/pull/11927)); build(deps): bump github/codeql-action/init from 4.37.8 to 4.37.9 ([#12345](https://github.com/diegosouzapw/OmniRoute/pull/12345)); build(deps): bump github/codeql-action/analyze from 4.37.8 to 4.37.9 ([#12346](https://github.com/diegosouzapw/OmniRoute/pull/12346)); bump the development group across 1 directory with 2 updates ([#12347](https://github.com/diegosouzapw/OmniRoute/pull/12347)); build(deps): bump github/codeql-action from 4.37.8 to 4.37.9 ([#12349](https://github.com/diegosouzapw/OmniRoute/pull/12349)); bump browserslist from 4.28.2 to 4.28.8 ([#12396](https://github.com/diegosouzapw/OmniRoute/pull/12396)); bump the production group across 1 directory with 4 updates ([#12399](https://github.com/diegosouzapw/OmniRoute/pull/12399)); bump @xmldom/xmldom ([#12500](https://github.com/diegosouzapw/OmniRoute/pull/12500)); bump qs from 6.15.2 to 6.16.0 ([#12512](https://github.com/diegosouzapw/OmniRoute/pull/12512)); bump @xmldom/xmldom from 0.9.10 to 0.9.12 ([#12513](https://github.com/diegosouzapw/OmniRoute/pull/12513)); bump fast-uri from 3.1.5 to 3.1.7 ([#12514](https://github.com/diegosouzapw/OmniRoute/pull/12514)); bump @humanfs/node from 0.16.7 to 0.16.8 ([#12515](https://github.com/diegosouzapw/OmniRoute/pull/12515)) +- Clear the `release/v3.8.51` `check:agent-skills-sync` base-red: regenerate `skills/cli-tunnel/SKILL.md` so the `tunnel create [type]` positional that #13009 taught the generator to read is reflected in the committed skill. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` typecheck base-red from #12731: the new `Mode pack ranking gates` candidates in `open-sse/services/autoCombo/__tests__/autoCombo.test.ts` omitted the required `provider`, `model` and `errorRate` fields of `ProviderCandidate`, failing `check:open-sse-typecheck` (and with it `Fast Quality Gates`) on every open PR. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` docs-sync base-red: EURouter (#13025) and GreenPT (#13024) took the live provider count to 358, leaving 7 STRICT drifts (`PROVIDER_REFERENCE.md`, 4 diagrams, 2 tier-flow images) plus the `AGENTS.md` / `llm.txt` / `package.json` count claims stale. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- **test(models):** Pin `CATALOG_BUILD_TIMEOUT_MS` in the #12058 canonical-catalog regression so a cold catalog build racing the 8s default bound no longer flakes the suite with a 500 ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Allow `@omniroute/opencode-plugin-v2/` in the npm pack policy — [#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870) shipped the v2 plugin beside its v1 sibling without widening the allowlist, so every packed file under it read as an unexpected artifact and failed `check:pack-policy` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Point the release-green validator's pack gate at the tree it is validating — the build-provenance guard defaulted to `origin/main`, which a release branch tip can never be an ancestor of mid-cycle, so `Package artifact` reported a false red on every run and the tarball boot-smoke cascaded off it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **docs(checks):** keep doc counts honest — headings, rankings, catalog, weights, quality gate and scoring diagram now covered ([#12507](https://github.com/diegosouzapw/OmniRoute/pull/12507)) — thanks @maxmad64bis +- **chore(lifecycle):** `check:model-lifecycle` now also diffs `DEFAULT_DEGRADATION_MAP` (the background-task redirect table) against the vendor lifecycle snapshot, refusing a retired id as source or target, with a table-driven unit test beside it. Three rows whose source the vendor had retired — `claude-sonnet-4-20250514`, `gemini-3-pro-preview` and `gpt-5.1-codex` (whose target `gpt-5.1-codex-mini` is retired too) — were dead code, since `checkLifecycle` answers 410 before the redirect runs; they are dropped (#12535 — thanks @pacocartones) +- **test(compression):** cover idle worker eviction at the resource level — the pool must call `terminate()` and must not retain the worker's `MessagePort`, complementing the `exit`-event assertion added with the fix ([#13371](https://github.com/diegosouzapw/OmniRoute/pull/13371)) +- **chore(quality):** raise the `imageGeneration.ts` file-size ceiling for the image-only-model guard that clears the #12945 base-red ([#13392](https://github.com/diegosouzapw/OmniRoute/pull/13392)) +- **chore(quality):** type the combo-test route's JSON response bodies instead of casting them to `any`, drop the now-empty suppression entry, and remove the `hasPerModelQuota` import `chatCore.ts` stopped using when the failure-classification helper was extracted ([#13379](https://github.com/diegosouzapw/OmniRoute/pull/13379)) +- **chore(electron):** the desktop bundle no longer ships the root-level files of every translated docs mirror (`README.md`, `llm.txt`, `CLAUDE.md`, `GEMINI.md`, `CONTRIBUTING.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`) — the packaged app only reads `docs/i18n//docs/**`, which stays. Saves ~11 MB on top of the translated CHANGELOGs already pruned. (#0000) ([#13235](https://github.com/diegosouzapw/OmniRoute/pull/13235)) +- **chore(quality):** retighten the `src/sse/handlers/chat.ts` unused-vars suppression count to the 8 violations that actually remain ([#13385](https://github.com/diegosouzapw/OmniRoute/pull/13385)) +- Bring the documented counts back in line with the code: 171 migrations (was 169) and 20 routing strategies (was 19), plus the regenerated `cli-tunnel` skill reference. ([#12970](https://github.com/diegosouzapw/OmniRoute/pull/12970)) +- Update the documented migration count to 172 after the call-logs provider-stats indexes landed. ([#13209](https://github.com/diegosouzapw/OmniRoute/pull/13209)) +- **chore(ci):** two gates that close the blind spots behind the exclusions above. `check:vitest-exclusions` requires every Vitest exclusion to name a tracking issue and to appear in `config/quality/vitest-exclusions.json` — the previous list grew to 62 files behind a comment pointing at an issue that had been closed for a month. `check-new-key-coverage` requires a key newly added to `en.json` to reach every locale; the existing coverage gate is a percentage floor per locale, so eleven absent keys out of ~13,000 left it at 99.9% while a whole feature shipped untranslated in nine languages. Both are diff-aware, so pre-existing debt stays frozen and neither needed a migration to turn on. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **build:** root layout no longer loads Inter from `next/font/google`, so a production image build does not need fonts.googleapis.com ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **docs(i18n):** bring the eight new `llm.txt` mirrors (km, kn, ml, my, ne, or, pa, si) up to the current provider and migration counts, so `check-docs-sync` stops rejecting every commit ([#13674](https://github.com/diegosouzapw/OmniRoute/pull/13674)) +- **refactor(ui):** flow surfaces (home topology, combo live studio, compression cockpit/waterfall/nodes, token-health badge) express state through the theme-aware `--orch-status-*` tokens instead of fixed dark-mode hex, so green/red/amber/grey stay legible in the light theme; dark mode is byte-identical. Categorical palettes (routing-strategy hues, compression-layer pills, provider brand colors) deliberately stay hex ([#12378](https://github.com/diegosouzapw/OmniRoute/issues/12378)) ([#13203](https://github.com/diegosouzapw/OmniRoute/pull/13203)) +- **chore(tests):** 51 test files that had been excluded from Vitest are running again, restoring roughly 350 assertions to the blocking suite. Each was measured individually first: of the 62 files parked behind the `// #8618 — pre-existing failure` comment, 51 pass against the current tree with no source change, so the exclusions had outlived the failures they were added for. The 11 that genuinely still fail stay excluded, but now point at a live tracker (#13204) rather than at #8618, which was closed in August while the list it tracked kept growing. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **chore(quality):** drop two ESLint suppression entries whose violations no longer exist, so `eslint --suppressions-location` stops rejecting every commit that touches the surrounding files ([#13384](https://github.com/diegosouzapw/OmniRoute/pull/13384)) +- **chore(release):** bring main's post-tag fixes into release/v3.8.51 — sync-back step 2/2 (direct commit `21c488f210`) +- **chore(deps):** patch toml and esbuild in the opencode plugin lockfiles ([#12965](https://github.com/diegosouzapw/OmniRoute/pull/12965)) +- **docs(changelog):** reconcile the v3.8.51 living section — fold 366 fragments, cover every cycle commit, credit every contributor ([#12971](https://github.com/diegosouzapw/OmniRoute/pull/12971)) +- **test(plugin):** wait for the v2 lazy-refresh tier instead of sleeping past it ([#12984](https://github.com/diegosouzapw/OmniRoute/pull/12984)) +- **chore(quality):** rebaseline chatCore.ts after the non-streaming regression fixes ([#13045](https://github.com/diegosouzapw/OmniRoute/pull/13045)) +- **docs(changelog):** add the missing bullet marker to the DeepSeek PoW fragment ([#13200](https://github.com/diegosouzapw/OmniRoute/pull/13200)) +- **chore(deps):** drain the Dependabot queue — 10 of 13 alerts ([#13213](https://github.com/diegosouzapw/OmniRoute/pull/13213)) +- **chore(ci):** make worktrees carry the hooks that enforce the identity gate ([#13236](https://github.com/diegosouzapw/OmniRoute/pull/13236)) +- **chore(deps):** pin csv-parse to ^7.0.2 via overrides (#13117) ([#13300](https://github.com/diegosouzapw/OmniRoute/pull/13300)) — thanks @KooshaPari +- **chore(deps):** bump hono from ^4.12.34 to ^4.13.7 (#13148) ([#13301](https://github.com/diegosouzapw/OmniRoute/pull/13301)) — thanks @KooshaPari +- **chore(deps):** pin joi to ^18.2.8 via overrides (#13085) ([#13302](https://github.com/diegosouzapw/OmniRoute/pull/13302)) — thanks @KooshaPari +- **docs(providers):** add Microsoft 365 Copilot (BizChat) provider guide (#12779) ([#13340](https://github.com/diegosouzapw/OmniRoute/pull/13340)) — thanks @KooshaPari +### 🙌 Contributors + +Thanks to everyone whose work landed in v3.8.51: + +| Contributor | PRs / Issues | +| --- | --- | +| [@5dive-bot](https://github.com/5dive-bot) | #11852 | +| [@abhisheksharma2411](https://github.com/abhisheksharma2411) | #11547, #11684, #11959, #12209 | +| [@adevwithpurpose](https://github.com/adevwithpurpose) | #11464 | +| [@adivekar-utexas](https://github.com/adivekar-utexas) | #12015, #12043, #12090, #12138 | +| [@AIB1TAL0S](https://github.com/AIB1TAL0S) | #12103 | +| [@alltomatos](https://github.com/alltomatos) | #11448 | +| [@alvinveroy](https://github.com/alvinveroy) | #11857, #12027, #12165 | +| [@amaleta](https://github.com/amaleta) | #11906, #11934 | +| [@amartinawi](https://github.com/amartinawi) | #11575 | +| [@AndrianBalanescu](https://github.com/AndrianBalanescu) | #11083, #11830, #11834, #11844 | +| [@anhtahaylove](https://github.com/anhtahaylove) | #13091, #13092, #13093, #13096, #13097, #13100, #13106, #13109, #13114, #13155, #13166, #13171, #13175, #13187, #13196 | +| [@aniruddhaadak80](https://github.com/aniruddhaadak80) | #11760 | +| [@app](https://github.com/app) | #12554 | +| [@arminanton](https://github.com/arminanton) | #11461, #11513 | +| [@AStupidBear](https://github.com/AStupidBear) | #11584 | +| [@atescivitci-cmd](https://github.com/atescivitci-cmd) | #12105 | +| [@b3nw](https://github.com/b3nw) | #11970, #11971, #12147 | +| [@backryun](https://github.com/backryun) | #11259, #11950, #12075, #12076, #12078, #12079, #12081, #12082, #12181, #12228, #12239, #12255, #12258, #12277, #12367, #12423, #12524, #12538 | +| [@benjaminkitt](https://github.com/benjaminkitt) | #11747 | +| [@benzntech](https://github.com/benzntech) | #11614, #11615 | +| [@Bl0ck154](https://github.com/Bl0ck154) | #11948, #11951, #11952, #11953, #11954 | +| [@brick30llc-ctrl](https://github.com/brick30llc-ctrl) | #11752, #12242, #12262 | +| [@Chewji9875](https://github.com/Chewji9875) | #11455, #11875, #12028 | +| [@cheynetom](https://github.com/cheynetom) | #12058 | +| [@davidebaraldo](https://github.com/davidebaraldo) | #12222, #12304 | +| [@davidlinfr](https://github.com/davidlinfr) | #6390 | +| [@ddarkr](https://github.com/ddarkr) | #12124 | +| [@Deftera186](https://github.com/Deftera186) | #11809 | +| [@dpozimski](https://github.com/dpozimski) | #11945 | +| [@DW-MediaLab](https://github.com/DW-MediaLab) | #12229 | +| [@echel0nn](https://github.com/echel0nn) | #11923 | +| [@f9td56dbgh-hub](https://github.com/f9td56dbgh-hub) | #11660 | +| [@fabioluissilva](https://github.com/fabioluissilva) | #11991 | +| [@foreveryh](https://github.com/foreveryh) | #12177 | +| [@geek007git](https://github.com/geek007git) | #12115, #12116, #12117, #12120, #12122 | +| [@geekyNads](https://github.com/geekyNads) | #11784, #12266 | +| [@ggiak](https://github.com/ggiak) | #11561, #12276, #12350, #12402 | +| [@gonisulaimann](https://github.com/gonisulaimann) | #12368, #12369, #12371 | +| [@Gorillaz322](https://github.com/Gorillaz322) | #12207 | +| [@groovecityJO](https://github.com/groovecityJO) | #12682 | +| [@hartmark](https://github.com/hartmark) | #11434, #11452, #11473, #11499, #11703, #11983, #11984, #11985, #11986, #11988, #11989, #11990, #11994, #12221, #12293, #12445, #12446, #12447, #12448, #12460, #12461, #12623, #12646, #12650, #12680, #12717, #12718, #12727, #12741, #12854 | +| [@hizzt](https://github.com/hizzt) | #11894 | +| [@hongnoul](https://github.com/hongnoul) | #11484 | +| [@HouMinXi](https://github.com/HouMinXi) | #11411, #11414, #11512, #11518, #11520, #11641, #11642, #11643, #11687, #11779, #11849, #11850, #11851, #11915, #11916, #11918, #11919, #11920, #12013, #12017, #12033, #12042, #12106, #12139, #12166, #12169, #12171, #12205, #12213, #12312, #12325, #12487, #12488, #12495, #12504, #12557, #12566, #12590, #12591, #12624, #12626, #12632, #12637, #12678, #12696, #12697, #12711, #12733, #12746, #12767, #12770, #12789, #12803, #12805, #12811, #12866, #12868, #12899, #12926, #12934, #12950, #12951, #12974, #13001, #13006, #13011, #13017, #13026, #13027, #13034, #13035, #13038, #13042, #13050, #13060, #13061, #13069, #13107, #13120, #13136, #13195, #13197 | +| [@Hsia97](https://github.com/Hsia97) | #11624 | +| [@jacobsparts](https://github.com/jacobsparts) | #11854, #12155, #12167 | +| [@joglomedia](https://github.com/joglomedia) | #11980 | +| [@jonlwheat2-gif](https://github.com/jonlwheat2-gif) | #11443, #11450, #11549, #11567, #11608, #11633, #11635, #11640, #11644, #11671, #11672, #11673, #11674, #11675, #11676, #11677, #11706, #11783, #12051, #12052, #12053, #12055 | +| [@kanade-hoshino](https://github.com/kanade-hoshino) | #12180 | +| [@Karan825](https://github.com/Karan825) | #11861 | +| [@KaspaPulse](https://github.com/KaspaPulse) | #11389, #11469, #11628, #11666, #11910 | +| [@keeltrace](https://github.com/keeltrace) | #12080, #12223 | +| [@killer30001000](https://github.com/killer30001000) | #12178 | +| [@KooshaPari](https://github.com/KooshaPari) | #12470, #12592, #12667, #12699, #12703, #12706, #12764, #12769, #12771, #13300, #13301, #13302, #13340, #13401, #13403, #13404, #13405, #13406, #13407, #13409, #13410, #13411, #13412, #13413, #13414, #13418, #13424, #13427, #13433, #13523, #13524, #13525, #13528, #13531, #13532, #13534, #13539, #13543, #13545, #13546, #13547, #13550, #13553 | +| [@kriptoburak](https://github.com/kriptoburak) | #11370 | +| [@KrzysiekSko](https://github.com/KrzysiekSko) | #12425, #12432, #12673 | +| [@marcelokarval](https://github.com/marcelokarval) | direct commit / report | +| [@maxmad64bis](https://github.com/maxmad64bis) | #1622, #11435, #11437, #11441, #11537, #11550, #11553, #11555, #11557, #11812, #11842, #11843, #11903, #12151, #12214, #12215, #12218, #12226, #12314, #12316, #12317, #12318, #12319, #12320, #12321, #12507, #12715, #12731, #12744, #12786, #12787, #12788, #12790, #12792, #12794, #12795, #12828, #12832, #12853, #12857, #12870, #12937, #12941, #12975, #13141, #13142, #13146, #13147 | +| [@mdigitalbh81](https://github.com/mdigitalbh81) | #12240 | +| [@Meet6338-X](https://github.com/Meet6338-X) | #11598, #11609, #12110 | +| [@morpheus9393](https://github.com/morpheus9393) | #11943 | +| [@MumuTW](https://github.com/MumuTW) | #11492, #11502, #11506, #11507, #11626, #11675, #11685, #11728, #11746, #11888, #11889, #11890, #11892 | +| [@Neuron-Mr-White](https://github.com/Neuron-Mr-White) | #11622, #11801, #12256 | +| [@NightStalker-87](https://github.com/NightStalker-87) | #12183 | +| [@NoxzRCW](https://github.com/NoxzRCW) | #11879, #11880, #11881, #11882, #11883, #11935 | +| [@ntdat812](https://github.com/ntdat812) | #11585, #12095 | +| [@ntdatt812](https://github.com/ntdatt812) | #11368, #11573, #11574, #11576, #11577, #11580, #11582, #11583, #11588, #11589, #11590, #11591, #11592, #11593, #11672, #12177, #12180, #12873, #12918, #12920, #12921, #12925, #12930, #13007, #13009, #13024, #13025, #13083, #13087, #13101, #13104, #13110 | +| [@official-burak](https://github.com/official-burak) | #11542 | +| [@opensource-elearning](https://github.com/opensource-elearning) | #12179, #12189, #12278, #12286 | +| [@oyi77](https://github.com/oyi77) | #11408, #11409, #11421, #11505, #11677, #12036, #12110 | +| [@pacocartones](https://github.com/pacocartones) | #11521, #11522, #11527, #11528, #11529, #11530, #11531, #11532, #11533, #11534, #11595, #11599, #11603, #11604, #11605, #11607, #11610, #11676, #11714, #11716, #11718, #11767, #11838, #11860, #11862, #11869, #11871, #11872, #11873, #11903, #11906, #11921, #11934, #12356, #12358, #12359, #12360, #12361, #12362, #12364, #12365, #12373, #12374, #12375, #12376, #12377, #12379, #12380, #12381, #12386, #12387, #12389, #12390, #12394, #12395, #12397, #12401, #12403, #12404, #12522, #12523, #12535, #12536, #12540, #12541, #12543, #12545, #12548, #12549, #12550, #12551, #12552, #12647, #12651, #12653, #13055 | +| [@patrykkopycinski](https://github.com/patrykkopycinski) | #11936, #11937, #12224 | +| [@PauloFH](https://github.com/PauloFH) | #11509 | +| [@PauloHSOliveira](https://github.com/PauloHSOliveira) | #12241 | +| [@ponkcore](https://github.com/ponkcore) | #12054 | +| [@Prajeeth-12](https://github.com/Prajeeth-12) | #11634, #12046 | +| [@quiterunner-commits](https://github.com/quiterunner-commits) | #12131, #12143 | +| [@rafacpti23](https://github.com/rafacpti23) | #11554, #11558, #12192, #12197, #12198, #12202, #12203, #12204 | +| [@ragnar-claude](https://github.com/ragnar-claude) | #11564 | +| [@raheemuddin786](https://github.com/raheemuddin786) | #11491, #11839, #11840, #11841, #12003, #12230, #12231, #12232, #12233, #12234 | +| [@rahilmavani](https://github.com/rahilmavani) | #11761 | +| [@Rahulsharma0810](https://github.com/Rahulsharma0810) | #11771 | +| [@RaviTharuma](https://github.com/RaviTharuma) | #11710, #11727, #11797, #11798, #11802, #11805, #11806, #11811, #12098, #12099, #12101, #12449, #12452, #12472, #12473, #12493, #12533, #12607, #12628, #12631, #12636, #12875, #12876, #12880, #12882, #12884 | +| [@rezjalibd](https://github.com/rezjalibd) | #12186 | +| [@RhianB14](https://github.com/RhianB14) | #11692 | +| [@rifqiawl](https://github.com/rifqiawl) | #11517, #11519, #11969 | +| [@rqzbeh](https://github.com/rqzbeh) | #11390 | +| [@Sabeekhann](https://github.com/Sabeekhann) | #11699, #11705 | +| [@santosraju99-hub](https://github.com/santosraju99-hub) | #11755, #11770 | +| [@Seramicx](https://github.com/Seramicx) | #11568 | +| [@Siva010](https://github.com/Siva010) | #12191 | +| [@solstxce](https://github.com/solstxce) | #11597 | +| [@soroush5](https://github.com/soroush5) | #12691 | +| [@tenshiak](https://github.com/tenshiak) | #12279 | +| [@TheDemonTuan](https://github.com/TheDemonTuan) | #11468, #11470, #11471, #11482, #11548, #11758, #11775, #11814 | +| [@thomasmaerz](https://github.com/thomasmaerz) | #12834 | +| [@tuandinh0801](https://github.com/tuandinh0801) | #11454 | +| [@turbolego](https://github.com/turbolego) | #11621, #11762, #11772, #11774, #11781, #12216 | +| [@Tushar49](https://github.com/Tushar49) | #11392 | +| [@ujjawalkaushik1110](https://github.com/ujjawalkaushik1110) | #11863 | +| [@vermasomesh835](https://github.com/vermasomesh835) | #11794 | +| [@vsd2807](https://github.com/vsd2807) | #11565, #11619 | +| [@wahidsadik371-coder](https://github.com/wahidsadik371-coder) | #12126 | +| [@watchingdogs](https://github.com/watchingdogs) | #12031 | +| [@wildcard](https://github.com/wildcard) | #11620 | +| [@xiaoyaner0201](https://github.com/xiaoyaner0201) | #11460, #11662, #12083 | +| [@Xxx91n](https://github.com/Xxx91n) | #11690 | +| [@yourspraveen](https://github.com/yourspraveen) | #11146 | +| [@yxyxy](https://github.com/yxyxy) | #11671 | +| [@ZaimMarzuki](https://github.com/ZaimMarzuki) | #11960 | +| [@Zartharas](https://github.com/Zartharas) | #11340 | +| [@zero-executioner](https://github.com/zero-executioner) | #11631 | +| [@diegosouzapw](https://github.com/diegosouzapw) | maintainer | + +--- ## [3.8.50] — 2026-08-25 _Living section — regenerated 2026-08-12 from all cycle commits (cycle open `ed2db6cb19` → tip). Bullets carry the merged PR and its author; direct pushes listed separately._ diff --git a/docs/i18n/pt/CHANGELOG.md b/docs/i18n/pt/CHANGELOG.md index cf55193b9c..729222cc86 100644 --- a/docs/i18n/pt/CHANGELOG.md +++ b/docs/i18n/pt/CHANGELOG.md @@ -8,16 +8,1119 @@ ## [3.8.51] — TBD -_Living section — cycle opened at the v3.8.50 freeze (parallel-cycle model). Bullets are aggregated from `changelog.d/` fragments at each `/generate-release` phase._ +_Living section — reconciled 2026-09-15 from all cycle commits (`091589089c` → `c0f92ec98a`, 916 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 | **123** | +| 📝 Commits in the cycle | **916** | +| 🔀 Pull requests referenced | **892** | +| 📋 Changelog entries | **919** | +| 🙌 Contributors credited in entries | **121** | +| 🤖 Automated dependency commits | 18 | + +**Entries by type** + +| Type | Count | +| --- | ---: | +| 🐛 Fixes | 592 | +| ✨ Features | 131 | +| 🧹 Chore | 93 | +| 📚 Docs | 42 | +| 🧪 Tests | 32 | +| ♻️ Refactor | 8 | +| 🏗️ Build | 7 | +| ⚡ Performance | 7 | +| ⚙️ CI | 2 | +| ⏪ Reverts | 2 | +| 📦 Dependencies | 2 | +| 🔒 Security | 1 | + +### 🏆 Top 25 contributors this cycle + +_By commits in `091589089c..c0f92ec98a`, author identities consolidated via `.mailmap` and the merged PR's GitHub login. Bots excluded._ + +| # | Contributor | Commits | +| ---: | --- | ---: | +| 🥇 | diegosouzapw | 323 | +| 🥈 | Bob.Hou (@HouMinXi) | 79 | +| 🥉 | Paco Cartones (@pacocartones) | 77 | +| 4 | Dizzle (@maxmad64bis) | 49 | +| 5 | Koosha Paridehpour (@KooshaPari) | 43 | +| 6 | Nguyen Thanh Dat (@ntdatt812) | 31 | +| 7 | Ravi Tharuma (@RaviTharuma) | 26 | +| 8 | Markus Hartung (@hartmark) | 22 | +| 9 | Webman (@jonlwheat2-gif) | 22 | +| 10 | backryun | 17 | +| 11 | anhtahaylove | 15 | +| 12 | Rafa Martins (@rafacpti23) | 10 | +| 13 | Syed Raheemuddin (@raheemuddin786) | 10 | +| 14 | Nguyễn Viết Tuấn (@TheDemonTuan) | 8 | +| 15 | MumuTW | 7 | +| 16 | Paijo (@oyi77) | 7 | +| 17 | Tobias Andersen (@turbolego) | 6 | +| 18 | Bl0ck (@Bl0ck154) | 5 | +| 19 | SHANMUGAPRIYAN (@geek007git) | 5 | +| 20 | KaspaPulse | 5 | +| 21 | Abhishek Sharma (@abhisheksharma2411) | 4 | +| 22 | Abhishek Divekar (@adivekar-utexas) | 4 | +| 23 | Andrew B. (@AndrianBalanescu) | 4 | +| 24 | NoxzRCW | 4 | +| 25 | opensource-elearning | 4 | ### ✨ New Features +- **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). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- Added Google AI Studio Gemini batch text-to-speech support through `POST /v1/audio/speech`. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- 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. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(routing):** subscription-first auto groupings — `auto/subscription` routes only through plan-included connections with a documented hard-stop overage and fails closed on exhaustion, while `auto/thrifty` orders the pool `subscription → keyless → free → cheap → premium` and steps up one rung at a time as each is exhausted. Billing class comes from a curated per-connection catalog (uncurated is treated as metered, never plan-included), both reuse STRICT_ZERO_COST's per-connection verification, and a quota reading whose `resetAt` has passed is now refreshed regardless of TTL so routing returns to plan capacity as soon as it resets ([#11146](https://github.com/diegosouzapw/OmniRoute/pull/11146)) — thanks @yourspraveen +- **feat(providers):** publish a management-authenticated versioned web-session credential contract from OmniRoute's canonical browser credential metadata ([#11340](https://github.com/diegosouzapw/OmniRoute/pull/11340)) — thanks @Zartharas +- **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)) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add Xquik X search with typed results, credential validation, REST routing, and MCP selection ([#11370](https://github.com/diegosouzapw/OmniRoute/pull/11370)) — thanks @kriptoburak +- **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)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(dashboard):** surface durable exclusive managed leases in the existing Sessions view, keeping leased clients visible across idle gaps while marking connections with in-flight work as active ([#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389)) — thanks @KaspaPulse +- **build(bun):** allow Turbopack bundler flag on Bun 1.4+ with configurable Webpack fallback ([#11471](https://github.com/diegosouzapw/OmniRoute/pull/11471)) — thanks @TheDemonTuan +- feat(api): add an opt-in `modelVisibilityAllowlist`/`modelVisibilityDenylist` settings pair to curate exactly which models `/v1/models` advertises, mirrored into every `auto/*` combo candidate pool so a denied model cannot be routed to via combo selection either (#11481) ([#11997](https://github.com/diegosouzapw/OmniRoute/pull/11997)) +- **feat(rankings):** the Free Provider Rankings page shows what each provider actually served over the last 24 h. It ranked by ELO alone, which left a provider that answers every call with an error in first place; the usage data was already served by the API but never requested. A provider with too small a sample shows a dash, not a number ([#11546](https://github.com/diegosouzapw/OmniRoute/pull/11546)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **feat(guardrails):** enforce a bounded, deterministic contract for Video Bridge transcripts — 256 cues, 4096 input code units and 4 KiB UTF-8 per cue, 64 KiB total text, malformed-Unicode rejection, focus-window scoping, cross-source reconciliation with contributing-source metadata, and a structural provenance trust boundary so caller JSON can never self-assert `embedded`/`audio-bridge` provenance ([#11652](https://github.com/diegosouzapw/OmniRoute/issues/11652)) ([#12009](https://github.com/diegosouzapw/OmniRoute/pull/12009)) +- **feat(video):** orchestrate optional Video Bridge audio extraction and Audio Bridge STT behind a dual opt-in (operator setting AND per-request signal) — a new loopback-only broker `mode=audio` operation shares the frame path's exact process queue, deadline, AbortSignal, and byte budgets to extract a bounded mono 16 kHz PCM WAV from the same already-downloaded video, then reuses the existing Audio Bridge transcription boundary; provider segment timing is preserved when available and marked coarse otherwise, and every failure degrades to a visual-only-safe partial instead of throwing (#11654). ([#12012](https://github.com/diegosouzapw/OmniRoute/pull/12012)) +- Add a tenant-bound Video Bridge drill-down lifecycle on top of the existing secure cache substrate: opaque hashed handles (never raw session/video identifiers), preview/standard/detail multiresolution variants resampled on read, response pagination capped at 8 frames and 32 MiB, and a new authenticated `/api/v1/video-bridge/drilldown` consumer route that stays disabled for remote access by default and denies cross-key access with the same response as a nonexistent handle (no existence oracle). ([#12006](https://github.com/diegosouzapw/OmniRoute/pull/12006)) +- **test(video):** Add the Video Bridge FU-07/FU-09 promotion-evidence harness (#11656) — a frozen Zod manifest schema covering the 8 required scenario kinds (static scenes, rapid cuts, late facts, fades, blur, small text, close events, visual prompt injection) with a minimum of 3 repetitions per case, deterministic declarative fixture recipes (`videoBridgePromotionFixtures.ts`), a pure medians/p95 metrics aggregator, a pure FU-07/FU-09 promotion-verdict evaluator applying the ticket's exact thresholds (missing token usage always holds), a digest-only persistence layer that never retains raw media or raw model responses, and a versioned per-model promotion allowlist shipped empty with every model defaulting to `hold`. The FU-07/FU-09 promotion verdicts themselves remain HOLD — they require a real evidence run against real models on VPS 192.168.0.15. ([#12008](https://github.com/diegosouzapw/OmniRoute/pull/12008)) +- **feat(video bridge):** "embedded" transcript provenance can now be legitimately earned instead of merely asserted — a bounded, allowlisted (`mov_text`/`subrip`/`webvtt`) subtitle probe runs through the loopback-only Video Bridge broker (at most 2 streams, 10s subdeadline bounded by the request deadline, 256 KiB output, 4096-code-unit lines), normalized through a bounded, ReDoS-safe WebVTT parser and Zod-validated end to end. The adapter always resolves to an explicit `success`/`absent`/`transient_failure` outcome — a subtitle failure never breaks the visual description path, and only a fingerprint-verified broker response (never a caller-declared label) can produce embedded cues (#11659). ([#12011](https://github.com/diegosouzapw/OmniRoute/pull/12011)) +- Default new Antigravity-family connections (agy CLI imports and Antigravity OAuth connects) to model auto-sync, so live model discovery lands in the synced catalog and `/v1/models` picks up freshly released upstream models (e.g. Gemini 3.7 Flash tiers) without code changes. Existing connections keep their current setting; the per-connection dashboard toggle remains the opt-out. (#11685 — thanks @MumuTW) ([#11887](https://github.com/diegosouzapw/OmniRoute/pull/11887)) +- **feat(ui):** enable React Compiler (`reactCompiler: true` + `babel-plugin-react-compiler`) for automatic memoization at build time ([#11783](https://github.com/diegosouzapw/OmniRoute/pull/11783)) — thanks @jonlwheat2-gif +- **feat(providers):** add SeekAi (`seekai.cc`) as an OpenAI-compatible New-API gateway — catalog id `seekai` (alias `ska`), `https://seekai.cc/v1`, live `/v1/models` via `passthroughModels`, aggregator-list membership so New-API balance detection can opt in. No referral/aff codes. ([#11786](https://github.com/diegosouzapw/OmniRoute/issues/11786)) ([#12557](https://github.com/diegosouzapw/OmniRoute/pull/12557)) — thanks @HouMinXi +- **feat(zai):** add GLM-5.3-Flash Coding Plan support (1M context, 128K output, vision, `low|high|max` reasoning) and route `zai` GLM-5.3-family API-key traffic through the OpenAI-compatible Coding Plan endpoint with native thinking defaults ([#11801](https://github.com/diegosouzapw/OmniRoute/pull/11801)) — thanks @Neuron-Mr-White +- **feat(combo):** choose how combo models are ordered — manual, provider, score, or name — via a sort control in the dashboard builder, persisted in `config.modelSort` and re-applied on load and after add ([#11812](https://github.com/diegosouzapw/OmniRoute/pull/11812)) — thanks @maxmad64bis +- **feat(free):** custom models can be marked free-tier via `customModels[].isFree`; `isFreeModel()` is the first door and `hidePaidModels` respects it even for providers outside the free budget ([#11843](https://github.com/diegosouzapw/OmniRoute/pull/11843)) — thanks @maxmad64bis +- **feat(nodejs):** add `5dive` as a `configure` target — `omniroute configure 5dive` / `omniroute setup-5dive` write a 5dive auth profile that points an agent fleet's `claude` seats at OmniRoute, with the root-only write, the loopback-vs-`https` endpoint rule and the per-seat model pin handled explicitly ([#11852](https://github.com/diegosouzapw/OmniRoute/pull/11852)) — thanks @5dive-bot +- **feat(sse):** treat `max` as a first-class reasoning-effort tier and clamp per model family (GLM 5.1+/DeepSeek V4+/Kimi K3+ keep native `max`; o1/MiniMax/Grok/Muse Spark clamp to their upstream ceiling) ([#11875](https://github.com/diegosouzapw/OmniRoute/pull/11875)) — thanks @Chewji9875 +- **feat(providers):** the provider plugin manifest now advertises a `usage-fetch` capability for the 40 providers that have a wired usage/quota fetcher, so external dashboards can read it from `GET /api/v1/provider-plugin-manifest` instead of parsing `open-sse/services/usage.ts` after every release. Discovery only — no new fetcher, no quota change, and the Dashboard quota widget stays gated by `USAGE_SUPPORTED_PROVIDERS`. `USAGE_FETCHER_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/fetcherProviders.ts`) and is re-exported from `services/usage.ts`, keeping the manifest module a light leaf instead of pulling the ~490-module usage dispatcher into the manifest route. ([#11903](https://github.com/diegosouzapw/OmniRoute/pull/11903)) — thanks @maxmad64bis / @pacocartones +- **feat(plugins):** `OMNIROUTE_PLUGINS_DIR` sets the directory the runtime plugin scanner reads — and the root the plugin manager installs into — overriding the `HOME`-derived default, so a Docker/K8s deployment can point straight at its bind-mounted plugin tree instead of moving `HOME` just to relocate the scan path. An image that exports no home no longer scans `/tmp/.omniroute/plugins` in silence: the resolved directory is logged once at startup as `scanner.dir_resolved`, naming the input that won. Unset, behaviour is unchanged. Distinct from the CLI-only `OMNIROUTE_PLUGIN_PATH`, which finds `omniroute-cmd-*` command packages and never reached this scanner ([#11906](https://github.com/diegosouzapw/OmniRoute/pull/11906)) — thanks @amaleta / @pacocartones +- **feat(leases):** add an explicit owner-authenticated status action that returns only the active lease's privacy-safe configured connection and provider labels, with generation fencing and no credential or internal-id disclosure ([#11910](https://github.com/diegosouzapw/OmniRoute/pull/11910)) — thanks @KaspaPulse +- **feat(providers):** import-from-file modal shows per-row API errors and ships a downloadable CSV template ([#12071](https://github.com/diegosouzapw/OmniRoute/issues/12071)) ([#12504](https://github.com/diegosouzapw/OmniRoute/pull/12504)) — thanks @HouMinXi +- **feat(providers):** dashboard search matches connection name and `baseUrl` so imported OpenAI-compat nodes surface on the provider card ([#12108](https://github.com/diegosouzapw/OmniRoute/issues/12108)) ([#12495](https://github.com/diegosouzapw/OmniRoute/pull/12495)) — thanks @HouMinXi +- **feat(routing):** add a `score` Auto router strategy that selects the highest configured weighted score and reuses `explorationRate`. ([#12155](https://github.com/diegosouzapw/OmniRoute/pull/12155)) — thanks @jacobsparts +- Add a runtime feature flag to disable universal context handoffs globally without changing the default behavior. ([#12167](https://github.com/diegosouzapw/OmniRoute/pull/12167)) — thanks @jacobsparts +- **feat(providers):** the provider plugin manifest now also advertises a `usage-supported` capability for the 46 providers whose usage API is accepted by the server and Dashboard routes, so integrators can distinguish "the server will serve quota for this provider" from "a fetcher is wired" without reading TypeScript. Discovery only — no fetcher or quota change. `usage-fetch` resolves on id or alias (the usage dispatcher accepts both); `usage-supported` resolves on id alone, matching the runtime guard `USAGE_SUPPORTED_PROVIDERS.includes(providerId)`. `USAGE_SUPPORTED_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/supportedProviders.ts`) and is re-exported from `providers.ts`, mirroring the `fetcherProviders` leaf from #11903 and keeping the manifest a light module. ([#12214](https://github.com/diegosouzapw/OmniRoute/pull/12214)) — thanks @maxmad64bis +- **feat(rankings):** order Free Provider Rankings by what each provider actually served — `GET /api/free-provider-rankings?sortBy=reliability` and a "Most reliable first" toggle on the page. Providers with too few calls to state a success rate keep their score order below the measured ones; the default order is unchanged ([#12218](https://github.com/diegosouzapw/OmniRoute/pull/12218)). — thanks @maxmad64bis +- **perf(sse):** defer `cloneLogPayload()` in the structured SSE collector until after the `maxEvents`/`maxBytes` cap check, eliminating ~9,800 wasted `structuredClone` calls per streaming response (65–71% faster `push()`). Reducer snapshot isolation restored for OpenAI and Responses summaries ([#12241](https://github.com/diegosouzapw/OmniRoute/pull/12241)) — thanks @PauloHSOliveira ([#12243](https://github.com/diegosouzapw/OmniRoute/pull/12243)) +- **feat(usage):** Devin CLI agentic quota (Codeium seat-management GetUserStatus) and OpenRouter key limits plus account credits now surface in Provider Limits ([#12256](https://github.com/diegosouzapw/OmniRoute/pull/12256) — thanks @Neuron-Mr-White) +- **feat(settings):** persist `headroomUrl` through Settings so status/start use the operator URL instead of only `HEADROOM_URL` ([#12306](https://github.com/diegosouzapw/OmniRoute/issues/12306)) ([#12487](https://github.com/diegosouzapw/OmniRoute/pull/12487)) — thanks @HouMinXi +- **feat(auto-combo):** Auto-Combo scoring can now weigh how often a provider/model has actually succeeded. The engine already carried that number on every candidate — 24 hours of usage history behind a ten-sample floor, real-time metrics otherwise — and the scoring function never read it, while the weight table described `stability` as if it did. `reliability` (`1 - failureRate`, with the same field precedence and the same rate-bounding the speed ranking already uses, so a corrupt reading means "nothing observed" rather than "fails every call") is now a declared factor shipping at weight `0`, so routing is unchanged until an operator gives it one, and the `stability` description now matches what that factor computes ([#12317](https://github.com/diegosouzapw/OmniRoute/pull/12317)) — thanks @maxmad64bis +- **feat(routing):** With `freeAccessPolicy: "strict"`, the read-only candidate listing (`GET /v1/auto-combo/{channel}/candidates`) no longer hides the candidates the zero-cost guard excludes — the same read-only transparency the resilience filter already honours (#9133). Each candidate now carries `freeAccessExclusion` saying why it would be kept out, and it tells an exhausted allowance apart from a quota reading that never arrived or went stale, which used to look identical from the outside. Routing is unchanged: the listing reports, it never enforces. The separate `excludeTosAvoid` guard still drops its candidates without a reason; that gap is now documented rather than closed ([#12319](https://github.com/diegosouzapw/OmniRoute/pull/12319)) — thanks @maxmad64bis +- **feat(radar):** The Radar catalog table now shows two facts it was already receiving from the feed and dropping on the floor: the per-model rate limits (requests and tokens, per minute and per day) in a new column, and a badge when a provider's terms state it may train on the prompts you send. A limit of zero renders as zero rather than "rate-only" — for a ceiling those are opposite facts — and a model with no training statement gets no badge, because an absent statement is not a guarantee ([#12320](https://github.com/diegosouzapw/OmniRoute/pull/12320)) — thanks @maxmad64bis +- **feat(radar):** explain Community, single-use, contributor, supporter, recovery, abuse, offers, and privacy rules before either Radar activation action, and remove the superseded fixed-PR grant promise from every UI locale ([#12342](https://github.com/diegosouzapw/OmniRoute/pull/12342)) +- **feat(gamification):** the dashboard Profile page now shows the real daily streak — `/api/gamification/level` returns `streak: { current, longest }` (per key with `apiKeyId`, operator-wide maximum otherwise) and the streak card reads it instead of a hard-coded 0 (#2403) ([#12377](https://github.com/diegosouzapw/OmniRoute/pull/12377)) — thanks @pacocartones +- **feat(gamification):** the dashboard leaderboard now shows each API key's display name under the Name column instead of a truncated key id; `GET /api/gamification/leaderboard` attaches `name` per entry (name only — no key material), while the shared ranking helper and the federation leaderboard stay id-only — thanks @pacocartones ([#12385](https://github.com/diegosouzapw/OmniRoute/pull/12385)) +- **feat(gamification):** enforce the documented 1000 XP/min per-API-key anti-cheat rate limit on the XP award path; over-limit awards are logged and skipped instead of persisted, and the sliding window now matches the timestamp format stored in `xp_audit_log` ([#2403](https://github.com/diegosouzapw/OmniRoute/issues/2403)) ([#12390](https://github.com/diegosouzapw/OmniRoute/pull/12390)) — thanks @pacocartones +- **feat(admin):** localize the gamification anomalies page — the loading state, the Status column and the Suspicious badge now come from the `common` catalog (new `common.suspicious` key propagated to every locale) — add it to the Gamification sidebar group as `gamification-admin` (`/dashboard/gamification/admin`), and expose the loading and empty states as polite `role="status"` live regions (#12401 — thanks @pacocartones) +- **feat(providers):** skip GitHub combo members missing from the live synced catalog, and drop Copilot models that are policy-disabled or hidden from the model picker ([#12473](https://github.com/diegosouzapw/OmniRoute/pull/12473)) — thanks @RaviTharuma +- **feat(i18n):** locale-expansion tooling — `npm run i18n:add-locale` adds a language to config, dashboard, docs mirrors, CLI, README/indexes and site in one command; browser, CLI and cookie detection resolve `uk`, `fil`/`tl`, `zh-Hant` (and the retired legacy `in` → `id`) via config aliases; `config/i18n.json` now ships in the npm package so the published CLI can read it; new real-translation ratio gate (`npm run i18n:check-ratio`, advisory) with a per-locale ratchet baseline; `run-translation --adopt` restores `.i18n-state.json`; `sync-language-bars` generates 🌐 bars from config; `validate_translation.py` loads its allowlist again. Retires the duplicate `in` locale (Indonesian mislabelled as Hindi) — 42 honest locales; saved `NEXT_LOCALE=in` / `OMNIROUTE_LANG=in` keep working. (#12496) +- **feat(api):** Emit gateway-measured `tokens_per_second` (TTFT excluded) on streaming usage and `X-OmniRoute-Tokens-Per-Second` when first-token latency is known ([#12616](https://github.com/diegosouzapw/OmniRoute/issues/12616)) ([#12631](https://github.com/diegosouzapw/OmniRoute/pull/12631)) — thanks @RaviTharuma +- **feat(opencode-plugin): map gateway cost/usage/tok/s onto OpenCode inference payloads** — the official plugin copies `X-OmniRoute-Response-Cost`, token counts, `X-OmniRoute-Tokens-Per-Second` / `usage.tokens_per_second`, TTFT, and the winning `X-OmniRoute-Model` onto the JSON/SSE body OpenCode already consumes. Missing tok/s is left unset (never `tokens / latency`). (#12636) — thanks @RaviTharuma +- **feat(combo):** add combo strategy `quota-weighted`: skip exhausted accounts, then weighted-draw by leftover / in-flight load; existing conversations stay pinned until the account is empty ([#12789](https://github.com/diegosouzapw/OmniRoute/pull/12789)) — thanks @HouMinXi +- **feat(models):** Account live listings become the chat catalog source for Claude, Codex, Copilot, and AGY; public metadata only fills prices on IDs those accounts already list. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **fix(models):** Union `agy` and `antigravity` live catalogs so `agy/gemini-3.8-flash-high` is not rejected after the prefix folds to `antigravity`. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **feat(opencode):** Add `@omniroute/opencode-plugin-v2`, an opencode v2 plugin that gives a v2 install a route to an OmniRoute gateway: it publishes models, combos and auto-combos into the host catalog, refreshes them lazily behind a 300s TTL, and keeps serving the last known catalog from an on-disk snapshot when the gateway is unreachable. The v1 plugin (`@omniroute/opencode-plugin`) is untouched and keeps working as before. The plugin adapts to the host instead of assuming one shape: it reads the shape the host seeds into the catalog draft and publishes the `api` block, the pre-`api` top-level fields, or both. The gateway key comes from the host credential store when one is connected, so no secret needs to sit in a config file; a source the gateway refuses is named in a warning instead of silently degrading the catalog to raw model ids; and tool schemas bound for a Gemini model are stripped of the JSON-Schema keywords Gemini rejects, so tool calling survives that route ([#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870)) — thanks @maxmad64bis +- feat(services): show sanitized CLIProxyAPI account health from its authenticated management API without exposing credentials, file paths, or raw account metadata (#6342) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add AnySearch free web search + URL extract (webFetch) with typed results, credential validation, REST routing, and MCP selection - fallback-only ([#11690](https://github.com/diegosouzapw/OmniRoute/pull/11690)) — thanks @Xxx91n +- **feat(dashboard):** display clamped `[0, 100]%` cached input token ratio in request logs table ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) — thanks @b3nw +- **feat(catalog):** add `OMNIROUTE_DISABLE_THINKING_LEVEL_VARIANTS` feature flag to optionally filter out thinking level variants from model catalog ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) — thanks @b3nw +- **feat(dashboard):** continuously export call logs to external analytics stores. A pluggable destination registry ships the full Logs-tab record set on an hourly `JobRegistry` cron, with a persisted per-destination cursor, batched inserts, a config UI rendered from each destination's own field descriptors, and a REST layer (`/api/log-export/*`) for CRUD, a connection test, and an on-demand run. A destination can opt into `includeBodies` to also ship the request and response payloads shown in the Logs detail pane, including the client and provider views of each call; this is off by default, and payloads inherit the dashboard's PII sanitisation, secret redaction and `noLog` handling. Google BigQuery is the first destination, using a service-account key stored encrypted at rest and streaming inserts keyed by call-log id, into a table that is day-partitioned on `timestamp` and clustered on `api_key_name`, `provider`, `model` and `status`. ([#11945](https://github.com/diegosouzapw/OmniRoute/pull/11945)) — thanks @dpozimski +- **feat(providers):** add MaxAI as a signed, OpenAI-compatible provider serving its 13 paid chat models (GPT-5.6 / Luna / Thinking, Claude 5 Sonnet, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite, Grok 4.1-fast / 4.5, DeepSeek V3.2 / R1, Llama 3.3 70B) through OmniRoute's `/v1` endpoint, with per-request HMAC-SHA1→SM3→AES request signing, live model + context-window discovery from `/models/get_config`, and prompted tool-calling translated to OpenAI `tool_calls` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI vision input — image_url content parts are forwarded inline in `message_content` to the 6 vision-capable models (GPT-5.6 / Luna / Thinking, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite) ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI image generation — 6 image models (gpt-image-1, dall-e-3, flux-1-schnell/dev/pro, sd3-medium) exposed through `POST /v1/images/generations` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI document RAG — inline base64 file/document attachments are uploaded to MaxAI (content-addressed `doc_id`) and attached to the chat via `doc_list` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** browserless MaxAI onboarding — email device-pair login (`/api/providers/[id]/login`) and signed access-token refresh, so a connection can be created and kept fresh without a real browser or Google OAuth ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** per-provider TLS impersonation profile (MaxAI presents a Windows Firefox-150 client fingerprint) so its bot-sensitive endpoints accept OmniRoute traffic ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** add **Nimble** as a web-search and web-fetch provider (`nimble-search`) — `/v1/search` routes to Nimble's search API at `lite` depth (locale, freshness and include/exclude domain filters mapped onto the shared request contract), and `/v1/web/fetch` routes to Nimble Extract, which covers all four fetch formats (`markdown`, `html`, `links`, `screenshot`) from a single call. One API key serves both surfaces. ([#11620](https://github.com/diegosouzapw/OmniRoute/pull/11620)) — thanks @wildcard +- The npm publish is automatic again, through npm Trusted Publishing (OIDC): the hosted `stage-npm` job publishes with a short-lived credential minted from GitHub's id-token — no `NPM_TOKEN`, no 2FA prompt, provenance attached. `publish_mode=staged` (owner approves with 2FA) and `direct` (token) remain available on `workflow_dispatch`. ([#11931](https://github.com/diegosouzapw/OmniRoute/pull/11931)) +- **feat(providers):** Add **Opper** as an API-key gateway provider — EU-hosted AI gateway with 700+ models from 30+ providers behind one OpenAI-compatible API and one key (`OPPER_API_KEY`); model ids use `provider/model` format (e.g. `anthropic/claude-sonnet-4-6`, `openai/gpt-5`); live model catalog at `https://api.opper.ai/v3/compat/models`; entry mirrors `requesty` (same shape, `passthroughModels: true`, no static seed) ([#11629](https://github.com/diegosouzapw/OmniRoute/pull/11629)) +- **feat(dashboard):** the `/dashboard/orchestration` snapshot hook now subscribes to the `agents` WebSocket channel (`agent.task.updated`) instead of `requests` as its refetch trigger, and relaxes its background poll from 5s to 30s while that WS connection is up — falling back to the tighter 5s cadence, reprogrammed live on any connect/disconnect transition, whenever the socket is down. ([#12409](https://github.com/diegosouzapw/OmniRoute/pull/12409)) +- **feat(dashboard):** Orchestration canvas quick wins — search box plus state/source/provider filter chips with a one-click clear, and per-source collapse/expand, all reflected in the URL so a filtered/collapsed view is shareable and survives a refresh; the detail drawer gained a "copy trace JSON" action and hardened error/empty-state and accessibility handling; the Agents-tab edges now animate traveling particles along active (running) connections; and the canvas node/edge status colors moved off fixed hex values onto theme-aware `--orch-status-*` CSS custom properties, so they adapt correctly to light/dark mode. ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- **chore(dashboard):** Orchestration UI hardening pass and the missing component/model test coverage it called for — `OrchestratorNode`/`ActivityNode`/`OverflowNode` rendering, the `?node=`/overflow-click page routing, the Agents-tab orchestrator-click no-op and `showCompleted` toggle, and the overview kanban's done-column sort order (#12270, #12271). ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- New `/dashboard/orchestration` page: live unified view of everything running — Cloud Agent, A2A and Conductor as a real-time graph (Agents tab), the combo cascade (Routing tab, reusing the Combo Live Studio) and a state kanban (Overview tab), with a detail drawer (trace, cost, approve/cancel). Read-only over existing APIs — no new backend. Canvas concept credit: PR #11815 design ([#12261](https://github.com/diegosouzapw/OmniRoute/pull/12261)) +- **feat(dashboard):** new "History" tab on `/dashboard/orchestration` — an Airflow-style grid of finished runs over a 24h/7d/30d preset window, one row per (source, identity), clicking a cell opens the existing detail drawer. It is backed by real persistence: A2A task lifecycle transitions are now written to the `a2a_tasks` table (purged after 30 days, configurable via `OMNIROUTE_A2A_HISTORY_RETENTION_DAYS`) and served by the new `GET /api/a2a/tasks/history` listing endpoint, with the task-detail route falling back to persisted history once a run leaves the in-memory snapshot. Conductor runs stay remote and are not persisted locally — the tab says so instead of silently omitting them. ([#12479](https://github.com/diegosouzapw/OmniRoute/pull/12479)) +- **feat(dashboard):** the orchestration detail drawer gained a "Repeat" action for Cloud Agent, A2A and Conductor tasks — a two-click confirm (click once to arm, click again within the confirm window to fire) re-submits the original prompt/input as a new run. The button is disabled with an explanatory tooltip whenever the original input can't be recovered from the loaded task detail (e.g. it never carried a prompt, or the detail failed to load). Two limitations of the A2A variant, by design: it targets the `/a2a` JSON-RPC endpoint, which authenticates with an API key only (`REQUIRE_API_KEY=true` or a configured `OMNIROUTE_API_KEY` makes a dashboard-session repeat answer `HTTP 400` — surfaced verbatim in the drawer's error line, never as a success), and the `message/send` call is SYNCHRONOUS: the POST blocks for the whole skill run, so the success confirmation only appears once the repeated task finishes. A dashboard-authenticated A2A creation path is deliberately left to a follow-up — widening the endpoint's auth posture is an operator decision, not a side effect of this feature. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(a2a):** A2A task execution now records which memories were consulted for the task's last user message as `metadata.memoryHits` (id/key/type/content-snippet) plus a `memory_hits` history event, purely for observability — the retrieved memory is never injected into a skill's prompt or behavior. Gated by the `OMNIROUTE_A2A_MEMORY_HITS` kill-switch (default enabled; set to `0` to skip the recall lookup entirely). The drawer's new "Memory used" section lists these hits for a2a tasks and is omitted whenever there are none. Known limitation: recall only resolves under the keyless posture — a keyed caller's task owner is a SHA-256 prefix of the API key, while memory rows are keyed by the database api-key id, and no hash→id lookup exists today, so the hit list stays empty for keyed callers. The recorded hits are also kept out of the task's own `input` (and therefore out of the persisted input and of the "Repeat" request body), so repeating a task never re-sends the previous run's memory snippets. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(providers):** add a Perplexity Agent API provider (`perplexity-agent` / `pplx-agent`) for Perplexity `/v1/responses`, including the documented Anthropic, OpenAI, Google, xAI, DeepSeek, Z.AI, Moonshot/Kimi, NVIDIA, and Perplexity model IDs plus Anthropic-model `max_output_tokens` compatibility. ([#12103](https://github.com/diegosouzapw/OmniRoute/pull/12103)) — thanks @AIB1TAL0S +- **feat(providers):** add RPD (Requests Per Day) limit to provider rate limit overrides across UI, schemas, DB, and i18n ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) — thanks @b3nw +- **feat(providers): add UC Direct (uncensored.com Developer API), the metered OpenAI-compatible surface.** A standard OpenAI-compatible passthrough (default executor) for uncensored.com's official REST API at `https://api.uncensored.com/api/v1`: `X-api-key` auth (never-expiring `uai_sk_live_` key), `POST /chat/completions` with streaming SSE and native tool-calling, and the full live metered catalog (82 models across 15 providers, discovered from the public `GET /v1/models`). Registered as provider `uc-direct` (alias `ucd`). Complements the un-metered `uc` persona provider — same models, metered credits and a plain API key instead of a subscription session. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(providers): add UC (uncensored.com), the un-metered subscription "persona" chat as an OpenAI-compatible provider.** A WebSocket web-app port: a durable Clerk credential mints a short-lived session token per connect (browserless — no API key), driving UC's persona socket. Ships the browserless email-code login (request → verify → harvest), the 19 verified persona models (Claude Opus, Gemini, Grok, GLM, Kimi, DeepSeek, MiniMax, incl. the uncensored variants), prompted `` tool-calling with a per-model code-style dialect + auto-cure retry for guardrailed models, live ``/reasoning split, streaming + non-streaming OpenAI responses, and full quota/auth error surfacing (paywall / message-limit / rate-limit → 429, invalid session → 401 re-login). Full multimodal parity via the persona blob-upload layer: **vision** (image input, 15 vision-capable models), **document RAG** (PDF/doc upload, server-side extraction), **image generation** (22 models), **video generation** (14 models, async signed-url → poll), and **TTS** (streaming MP3). Registered as provider `uc` (alias `ucn`). The metered OpenAI-compatible Developer API is a separate `uc-direct` provider. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(logs):** add copy-all button to request detail modal ([#11083](https://github.com/diegosouzapw/OmniRoute/pull/11083)) — thanks @AndrianBalanescu +- **feat(check):** make serviceKinds required and add the reverse-walk provider consistency gate ([#11392](https://github.com/diegosouzapw/OmniRoute/pull/11392)) — thanks @Tushar49 +- **feat(providers):** only advertise verified-working connections by default ([#11448](https://github.com/diegosouzapw/OmniRoute/pull/11448)) — thanks @alltomatos +- **feat(resilience):** add hierarchical concurrency admission ([#11493](https://github.com/diegosouzapw/OmniRoute/pull/11493)) +- **feat(providers):** derive + surface expiry for JWT-bearing web cookies (#11497) ([#11505](https://github.com/diegosouzapw/OmniRoute/pull/11505)) — thanks @oyi77 +- **feat(tunnels):** support named cloudflared tunnels via CLOUDFLARED_CONFIG ([#11561](https://github.com/diegosouzapw/OmniRoute/pull/11561)) — thanks @ggiak +- **feat(compression):** make proactive context-compression threshold a live setting ([#11564](https://github.com/diegosouzapw/OmniRoute/pull/11564)) — thanks @ragnar-claude +- **feat(resilience):** add provider peak-hour protection ([#11622](https://github.com/diegosouzapw/OmniRoute/pull/11622)) — thanks @Neuron-Mr-White +- **feat(providers):** add 1min.ai provider ([#11631](https://github.com/diegosouzapw/OmniRoute/pull/11631)) — thanks @zero-executioner +- **feat(dashboard):** collapsible JSON tree viewer for request/response payloads ([#11703](https://github.com/diegosouzapw/OmniRoute/pull/11703)) — thanks @hartmark +- **feat(routing):** add LiquidAI LFM2.5-2.6B free tier via OpenRouter ([#11752](https://github.com/diegosouzapw/OmniRoute/pull/11752)) — thanks @brick30llc-ctrl +- **feat(pricing):** add GLM-5.3-Flash pricing, model specs, and catalog registration ([#11830](https://github.com/diegosouzapw/OmniRoute/pull/11830)) — thanks @AndrianBalanescu +- **feat(routing):** add relayMode for schema-locked context handoffs ([#11839](https://github.com/diegosouzapw/OmniRoute/pull/11839)) — thanks @raheemuddin786 +- **feat(config):** add support for runtime system prompt configuration and hot-reloading ([#11841](https://github.com/diegosouzapw/OmniRoute/pull/11841)) — thanks @raheemuddin786 +- **feat(routing):** expand connection-aware quota prefilter across combo strategies (#11682) ([#11850](https://github.com/diegosouzapw/OmniRoute/pull/11850)) — thanks @HouMinXi +- **feat(models):** reactive discovery sync after Antigravity model-not-found 404 ([#11888](https://github.com/diegosouzapw/OmniRoute/pull/11888)) — thanks @MumuTW +- **feat(models):** auto-aliases and token limits from synced Antigravity discovery ([#11889](https://github.com/diegosouzapw/OmniRoute/pull/11889)) — thanks @MumuTW +- **feat(settings):** raise sticky round-robin limit caps to 1000 ([#12015](https://github.com/diegosouzapw/OmniRoute/pull/12015)) — thanks @adivekar-utexas +- **feat(resilience):** operator-configurable global credential health check interval ([#12043](https://github.com/diegosouzapw/OmniRoute/pull/12043)) — thanks @adivekar-utexas +- **feat(quota):** use official OpenCode Go usage API ([#12124](https://github.com/diegosouzapw/OmniRoute/pull/12124)) — thanks @ddarkr +- **feat(resilience):** default the credential health check sweep to 60 minutes ([#12138](https://github.com/diegosouzapw/OmniRoute/pull/12138)) — thanks @adivekar-utexas +- **feat(quality):** new-code mode for the complexity and dead-code ratchets (Clean as You Code) ([#12142](https://github.com/diegosouzapw/OmniRoute/pull/12142)) +- **feat(dashboard):** orchestration canvas — unified model + snapshot hook (part 1/2) ([#12156](https://github.com/diegosouzapw/OmniRoute/pull/12156)) +- **feat(usage):** add Kilo Code balance and Kilo Pass quotas ([#12178](https://github.com/diegosouzapw/OmniRoute/pull/12178)) — thanks @killer30001000 +- **feat(providers):** refresh vendored ChatGPT Web connector to v4.0.7 ([#12181](https://github.com/diegosouzapw/OmniRoute/pull/12181)) — thanks @backryun +- **feat(providers):** manual "Clear cooldown" action in the cooling panel ([#12224](https://github.com/diegosouzapw/OmniRoute/pull/12224)) — thanks @patrykkopycinski +- **feat(providers):** restore ChatGPT Web via clean-room browser transport ([#12239](https://github.com/diegosouzapw/OmniRoute/pull/12239)) — thanks @backryun +- **feat(sse):** wire the PROVIDER_PROFILES window gate into the global provider cooldown ([#12247](https://github.com/diegosouzapw/OmniRoute/pull/12247)) +- **feat(providers):** modernize CLOVA Studio chat and embeddings ([#12277](https://github.com/diegosouzapw/OmniRoute/pull/12277)) — thanks @backryun +- **feat(browser):** adopt Obscura as primary headless browser engine with Chromium fallback ([#12286](https://github.com/diegosouzapw/OmniRoute/pull/12286)) — thanks @opensource-elearning +- **feat(sse):** full language parity for output styles (es/de/fr/it/ru/zh + autoDetect) ([#12289](https://github.com/diegosouzapw/OmniRoute/pull/12289)) +- **feat(quality):** complete test:scoped — --full map rebuild, stdin selection, CI loader parity (#8084 D1) ([#12353](https://github.com/diegosouzapw/OmniRoute/pull/12353)) +- **feat(providers):** refresh Fable, Cursor, and Devin catalogs ([#12367](https://github.com/diegosouzapw/OmniRoute/pull/12367)) — thanks @backryun +- **feat(video):** redact transcript text from logs and durable memory (#12150 P1) ([#12427](https://github.com/diegosouzapw/OmniRoute/pull/12427)) +- **feat(dashboard):** parent-link, genuine-continuation badge, and modal perf fixes ([#12448](https://github.com/diegosouzapw/OmniRoute/pull/12448)) — thanks @hartmark +- **feat(dashboard):** adaptive context-budget dial on compression panel ([#12488](https://github.com/diegosouzapw/OmniRoute/pull/12488)) — thanks @HouMinXi +- **feat(providers):** refresh Z.ai Web models and browser transport ([#12524](https://github.com/diegosouzapw/OmniRoute/pull/12524)) — thanks @backryun +- **feat(video):** redact raw client-snapshot transcript fields in the detailed log (#12150 P2) ([#12528](https://github.com/diegosouzapw/OmniRoute/pull/12528)) +- **feat(providers):** refresh NVIDIA hosted models ([#12538](https://github.com/diegosouzapw/OmniRoute/pull/12538)) — thanks @backryun +- **feat(quota):** Moonshot Open Platform balance and TPD lock for custom nodes ([#12590](https://github.com/diegosouzapw/OmniRoute/pull/12590)) — thanks @HouMinXi +- **feat(video):** redact transcript in the in-memory pending-request snapshot (#12430 item 6) ([#12596](https://github.com/diegosouzapw/OmniRoute/pull/12596)) +- **feat(dashboard):** link a log entry's Conversation Context to its owning conversation ([#12646](https://github.com/diegosouzapw/OmniRoute/pull/12646)) — thanks @hartmark +- **feat(catalog):** eligibility-gated free-tier bucket ([#12669](https://github.com/diegosouzapw/OmniRoute/pull/12669)) +- **feat(db):** fail-closed previous_response_id continuation for redacted video turns (#12150 P2b) ([#12707](https://github.com/diegosouzapw/OmniRoute/pull/12707)) +- **feat(gamification): pay the documented `streak_bonus` and `badge_unlock` XP rewards.** `XP_REWARDS` listed both rewards but the award pipeline never paid them: the private reward table in `events.ts` omitted them, `updateStreak()` did not report when a streak extended, and badge unlocks carried no XP. Every request that extends a daily streak now pays `streak_bonus × streak length` once per UTC day (guarded by a same-day `xp_audit_log` check), and every badge unlocked through the pipeline pays `badge_unlock` once per badge (guarded by the `user_badges` primary key; `unlockBadge()` now reports whether it inserted). Bonus XP flows through the same `addXp` + level sync + global/weekly/monthly leaderboard path as action XP, so level-ups and rankings include it. The Radar supporter recognition unlock stays XP-free. (#12522 — thanks @pacocartones) +- **feat(providers):** advertise a `free-tier` capability in the provider plugin manifest for every provider with documented free models, so sidecars and dashboards can filter free-capable providers without reading the quota catalog ([#12786](https://github.com/diegosouzapw/OmniRoute/pull/12786)) — thanks @maxmad64bis +- **feat(providers):** Added EURouter as an OpenAI-compatible API-key gateway (`https://api.eurouter.ai/v1`), with live model discovery via `passthroughModels`. Its copy states that models are served by third-party upstreams listed per model, so an EU-based router is not read as EU data residency for inference. ([#13025](https://github.com/diegosouzapw/OmniRoute/pull/13025)) — thanks @ntdatt812 +- **feat(providers):** Added GreenPT as an OpenAI-compatible API-key provider (`https://api.greenpt.ai/v1`), with live model discovery via `passthroughModels`. No free-inference badge: the published docs describe a free API subscription billed per token, not a free tier. ([#13024](https://github.com/diegosouzapw/OmniRoute/pull/13024)) — thanks @ntdatt812 +- **feat(i18n):** 9 new locales — Greek (`el`), Croatian (`hr`), Serbian (`sr`), Lithuanian (`lt`), Estonian (`et`), Latvian (`lv`), Slovenian (`sl`), Maltese (`mt`), Irish (`ga`) — across the dashboard, docs mirrors, CLI, README and the site; OmniRoute now ships every official EU language (51 locales). (#13044) +- **feat(i18n):** 8 new locales — Kannada (`kn`), Malayalam (`ml`), Odia (`or`), Punjabi (`pa`), Nepali (`ne`), Sinhala (`si`), Burmese (`my`), Khmer (`km`) — across the dashboard, docs mirrors, CLI, README and the site (59 locales). The translator now restores the ICU literal escape around angle placeholders and splits oversized docs sections before translating. (#0000) ([#13660](https://github.com/diegosouzapw/OmniRoute/pull/13660)) +- feat(providers): **list Agnes 3.0 Flash as the current free chat model, drop retired 1.5 Flash, add Image 2.0/2.5 Flash plus Video 2.5/2.5 Flash, and discover the live `/v1/models` catalog (including the CN host `api.agnes-ai.cn`).** `agnes-1.5-flash` now forwards to `agnes-3.0-flash`. Video 2.5 polls `GET /v1/videos/{id}` (not the V2.0 `/agnesapi` contract). Live `/v1/models` (2026-09-09) no longer serves 1.5; the wiki marks it deprecated. 3.0 Flash is 512K context / 65,536 max output, same window as 2.5. CN-region keys use the existing per-connection base-URL field, default stays `apihub.agnes-ai.com`. ([#13120](https://github.com/diegosouzapw/OmniRoute/pull/13120)) — thanks @HouMinXi +- **feat(sse):** Codex and OpenAI catalogs list GPT-6 Astra with effort aliases (`-low` through `-ultra`); Codex CLI identity pins `@openai/codex@0.153.4` in lockstep with the image ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **feat(dashboard):** the orchestration History tab gained a "Compare runs" mode — toggling it turns each grid cell into a 2-item selection queue (a 3rd click drops the oldest pick), and picking two cells opens a side-by-side comparison panel instead of the usual detail drawer. The panel fetches both runs' detail the same way the drawer does (falling back to persisted history once a run leaves the live TTL window) and shows, per side: identity/source/state, start time, a signed `right - left` delta for duration/cost/event count, the event timeline aligned by index, and any memory hits. One side's fetch failing never blocks the other, and a delta is only ever computed when both sides have a finite value — otherwise it renders "—", never `NaN`. Comparing two runs from different sources or skills still works; a banner marks the deltas as informational rather than hiding them, since the two runs aren't a strict apples-to-apples pair. ([#12677](https://github.com/diegosouzapw/OmniRoute/pull/12677)) +- **feat(dashboard):** badge a conversation that never reached a clean stop ([#12717](https://github.com/diegosouzapw/OmniRoute/pull/12717)) — thanks @hartmark +- **feat(release):** reconcile-changelog tool + version-anchored fragment aggregation ([#12987](https://github.com/diegosouzapw/OmniRoute/pull/12987)) +- **feat(mcp):** carry context_length through MCP list_models_catalog projection (#12776) ([#13406](https://github.com/diegosouzapw/OmniRoute/pull/13406)) — thanks @KooshaPari +- **feat(cli):** make startup readiness budget configurable (#13369) ([#13433](https://github.com/diegosouzapw/OmniRoute/pull/13433)) — thanks @KooshaPari + ### 🐛 Bug Fixes +- **security(streaming):** sanitize generic mid-stream error messages before emitting OpenAI, + Responses, or Claude SSE failure frames and before diagnostic logging, while preserving raw + failures for internal classification and keeping client disconnects out of provider failure state. ([#12457](https://github.com/diegosouzapw/OmniRoute/pull/12457)) +- **fix(claude):** `blockExtraUsage: false` no longer lets 5h quota preflight skip the account; extra usage is billed after the session bar is gone, so the request must reach Anthropic ([#12803](https://github.com/diegosouzapw/OmniRoute/pull/12803)) — thanks @HouMinXi +- Sanitize HuggingChat conversation-creation and message-send transport failures before they reach client error bodies or provider logs. ([#12467](https://github.com/diegosouzapw/OmniRoute/pull/12467)) +- **fix(dashboard):** Keep local and theme-aware provider SVG icons at a definite layout size so Chromium does not collapse them to 0×0 after the v3.8.50 image-rendering change ([#12054](https://github.com/diegosouzapw/OmniRoute/pull/12054)) — thanks @ponkcore +- **fix(security):** Sanitize provider and runtime failures before public API, SSE and MCP responses and before persistent request, proxy and usage logs, preventing credentials, stack traces and host filesystem paths from crossing those boundaries while preserving stable error codes and useful diagnostics. ([#12506](https://github.com/diegosouzapw/OmniRoute/pull/12506)) +- **fix(cli):** `omniroute tunnel create` no longer crashes with `Cannot read properties of undefined (reading optsWithGlobals)` — removed the duplicate positional argument that caused Commander.js to misalign the action callback parameters ([#12295](https://github.com/diegosouzapw/OmniRoute/issues/12295)) ([#12368](https://github.com/diegosouzapw/OmniRoute/pull/12368)) — thanks @gonisulaimann +- Stop advertising Gemini Live-only models as supported audio endpoints until OmniRoute proxies the bidirectional Live protocol. ([#11710](https://github.com/diegosouzapw/OmniRoute/pull/11710)) — thanks @RaviTharuma +- **fix(kie):** reroute `flux/kontext` off the KIE Market `createTask` flow — it is catalogued with `isMarket: true` but has no Market catalog page, so KIE rejected it with "model name not supported"; it now hits the dedicated `POST /api/v1/flux/kontext/generate` / `GET /api/v1/flux/kontext/record-info` endpoints instead (#11296). ([#11985](https://github.com/diegosouzapw/OmniRoute/pull/11985)) — thanks @hartmark +- **fix(kie):** correct 12 more KIE Market catalog ids that were sent to `createTask` unchanged but diverge from KIE's documented upstream `model` values — GPT Image 2 T2I/I2I (drops the `gpt/` prefix), GPT Image 1.5 T2I/I2I (`gpt-image/` namespace), Seedream 5.0 Lite T2I/I2I (drops the `.0`), all 4 Flux 2 variants (`flux-2/` namespace, generic variant renamed `flex`), and Wan 2.7 Image / Image Pro (dash instead of dot) — each verified individually against the literal example request published on docs.kie.ai. `#11326`'s "everything else already matches" claim was wrong a second time (#11296); `z-image/4.0-*`/`z-image/4.5-*` and `flux/kontext` remain open, documented as unresolved in `KIE_MARKET_UPSTREAM_MODEL_IDS`'s comment pending further verification. ([#11667](https://github.com/diegosouzapw/OmniRoute/pull/11667)) +- **fix(dashboard):** `useApiKeySave.handleSaveApiKey` no longer forces a full upstream `/models` catalog sync on every non-curated provider connection save — callers can now pass `skipModelSync: true` to opt out, so a workflow that only wants to add one manual model no longer floods the provider's available-models list with hundreds/thousands of synced entries. The flag is a client-side intent signal only and is stripped before the connection payload is POSTed to `/api/providers`; default behavior (full sync on save) is unchanged when the flag is omitted (#11324) ([#11664](https://github.com/diegosouzapw/OmniRoute/pull/11664)) +- **Provider connections:** keep `tokenExpiresAt` when a connection is created. The create-path allowlist omitted it, so every insert stored NULL and the dashboard token badge could read a fresh connection as expired until its first background refresh ([#11368](https://github.com/diegosouzapw/OmniRoute/pull/11368)). — thanks @ntdatt812 +- **fix(combos):** the combo builder's precision-select, global-model-search, and manual-entry flows now serialize a model step's `model` string using the provider's already-computed routing-alias prefix (e.g. `oc/`) instead of rebuilding it from the raw canonical `providerId`, fixing the no-auth "OpenCode Free" provider (`opencode`) being routed to the unrelated paid "OpenCode Zen" provider (`opencode-zen`) because `opencode` doubles as a manual routing-prefix override ([#11433](https://github.com/diegosouzapw/OmniRoute/issues/11433)). ([#11670](https://github.com/diegosouzapw/OmniRoute/pull/11670)) +- **fix(radar):** the catalog feed cache now keeps `generatedAt`, the date the feed's data was built, next to `fetchedAt`, the date this install downloaded it (#11435). The feed schema requires that date and the sync path validates it, but the cache dropped it — so a feed fetched minutes ago and one carrying weeks-old figures looked identical to everything downstream, including the dashboard's "Last fetched" line. `getRadarCatalog().meta` and `GET /api/radar/status` now report both dates, the latter as its own field rather than folded into `version` — and omitted entirely for the offers and intel caches, which keep no build date, where a `null` would read as "unknown" rather than "never stored". The dashboard still shows only the fetch time; surfacing the build date there needs a new translated label and is left to a follow-up. Rows cached before migration 163 read back as `null`: unknown stays unknown instead of borrowing the fetch time. The referrals cache has persisted the same date since migration 142. — thanks @maxmad64bis +- **fix(cli):** postinstall no longer fills `JWT_SECRET` and `API_KEY_SECRET` in the installed package's `.env` (#11436). `.env.example` ships both blank on purpose: the server restores them from its durable store, or generates and persists them there, in `ensureSecrets()`. Pre-filling them defeated that — the file lives inside the package directory, so `npm i -g` replaced it and postinstall wrote _different_ values, while `ensureSecrets()` (which only acts on an empty variable) never got to restore the real ones. Both secrets rotated silently on every update, invalidating dashboard sessions and API-key CRCs. `STORAGE_ENCRYPTION_KEY` left the same list for the same reason in #1622; its comment pointed at a function that no longer exists and now names the real provisioning path. — thanks @maxmad64bis +- **fix(cli):** the CLI now says when a loaded `.env` lives inside the installed package directory (#11437). It already announces every env file it reads, without distinguishing the ones that survive an update from the one that does not: `npm i -g` replaces the package directory wholesale, so values set there are gone at the next update, silently. The warning names the durable path to move them to, and fires only when that file actually supplied a value — a file entirely shadowed by a durable one supplied nothing. A development checkout stays silent: there the same path is stable and documented in `SETUP_GUIDE.md`. — thanks @maxmad64bis +- **fix(free-models):** the shared `isFreeModel()` predicate no longer reports catalog entries marked `freeType: "discontinued"` as free, so `hidePaidModels` can't route to Pollinations' seven premium models that now require a paid key ([#11441](https://github.com/diegosouzapw/OmniRoute/pull/11441)) — thanks @maxmad64bis +- fix(ui): let AnySearch use the normal provider-icon fallback when LobeHub has no matching icon (#11449) +- Fixed the v3.8.50 Costs and Analytics dashboards so flat-rate Claude Code usage can be shown as an explicitly requested token-price estimate without changing default billed-cost semantics. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed archived usage retention so each request is priced individually instead of pricing a day's summed tokens once, which understated archived cost whenever a day mixed cache-heavy and ordinary requests. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed the Costs dashboard so it discloses when displayed figures include flat-rate token-price estimates instead of labelling them as billed spend, using the flag the analytics API already returns; the month-end projection and the CSV/JSON exports carry the same marker, and billed-cost mode is unchanged. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- **fix(combo):** attach the same combo-diagnostics trace (`poolSize`/`attemptOrder`/`excluded`/`terminalReason`, plus `x-omniroute-combo-*` headers) to the round-robin strategy's and the nested pipeline/fusion runtime-unit loop's "Maximum combo retry limit reached" 503 that the priority-strategy path already attaches for the identical terminal condition — previously those two paths returned a bare, contextless 503 ([#11462](https://github.com/diegosouzapw/OmniRoute/issues/11462)). ([#11665](https://github.com/diegosouzapw/OmniRoute/pull/11665)) +- **fix(bun):** use native `bun:sqlite` in `bootstrap-env` and `sync-env` to avoid loading `better-sqlite3` N-API addon during Bun startup ([#11468](https://github.com/diegosouzapw/OmniRoute/pull/11468)) — thanks @TheDemonTuan +- **fix(docker-bun):** make `Dockerfile.bun` install reliable with pre-install script helpers and native `bun:sqlite` smoke check ([#11470](https://github.com/diegosouzapw/OmniRoute/pull/11470)) — thanks @TheDemonTuan +- **fix(docker-bun):** remove every vendored `better-sqlite3` native addon from the Bun runtime image so startup cannot abort before the `bun:sqlite` fallback ([#11482](https://github.com/diegosouzapw/OmniRoute/pull/11482)) — thanks @TheDemonTuan +- **fix(autoCombo):** effort/alias model variants (`gpt-5.6-sol-xhigh`, `gpt-5.6`, cursor's `claude-4.6-opus-high`) inherit their base model's task fitness instead of falling to the wildcard 0.5 ([#11492](https://github.com/diegosouzapw/OmniRoute/pull/11492)) — thanks @MumuTW +- fix(db): rate-limit repeated Arena ELO leaderboard fetch-failure warnings instead of logging one per sync attempt (#11500) ([#11989](https://github.com/diegosouzapw/OmniRoute/pull/11989)) — thanks @hartmark +- **fix(db):** dedupe the raw `[Encryption] Decryption failed...` log line emitted by the lazy-decrypt views (`createLazyRowProxy`/`createLazyConnectionView`), which power `getProviderConnections()` and were re-triggering that line on every CredentialHealth/model-sync cycle for the same corrupt or stale-key credential — a fresh Proxy over a fresh row on every cycle meant the per-proxy memoization never suppressed it, unlike the dedup `decryptConnectionFields()` already had since [#9927](https://github.com/diegosouzapw/OmniRoute/issues/9927). Now shares that dedupe tracking so the line logs at most once per credential ([#11500](https://github.com/diegosouzapw/OmniRoute/issues/11500)). ([#11678](https://github.com/diegosouzapw/OmniRoute/pull/11678)) +- **fix(intelligence):** Arena ELO sync stores a synthesized base-model row for every effort/harness variant (`claude-opus-5-max` → `claude-opus-5`, `gpt-5.6-sol-xhigh (codex-harness)` → `gpt-5.6-sol`), so bare model requests reach the synced intelligence layer instead of the static fallback table; the hand-maintained `MODEL_ALIAS_MAP` — which collapsed generations (`gpt-5.5` → `gpt-5`) — is gone ([#11506](https://github.com/diegosouzapw/OmniRoute/pull/11506)) — thanks @MumuTW +- **fix(autoCombo,sse):** catalog hygiene — the static task-fitness table no longer ranks retired models above live ones (dead rows dropped, patterns matched on segment boundaries), the 7 deprecation aliases that forwarded to retired or non-existent models now point at the vendors' published replacements, an alias is no longer rewritten when the serving provider offers the id as-is, and a new `check:model-lifecycle` gate diffs both tables against a vendor lifecycle snapshot ([#11507](https://github.com/diegosouzapw/OmniRoute/pull/11507)) — thanks @MumuTW +- **fix(sse):** stop dropping resolved `thoughtSignature` values on parallel (multi tool-call) turns sent to Gemini 3.x — the claude→gemini and openai→gemini translators previously kept the signature only on the *first* function call of a message, causing Gemini to reject subsequent calls in the same turn with HTTP 400 "Function call is missing a thought_signature"; each function call now keeps its own resolved signature ([#11510](https://github.com/diegosouzapw/OmniRoute/issues/11510)). ([#11669](https://github.com/diegosouzapw/OmniRoute/pull/11669)) +- Treat Docker publishing from a non-default release branch as an expected skip instead of a failed workflow, while preserving the guard that prevents it from overwriting the `next` channel. ([#11527](https://github.com/diegosouzapw/OmniRoute/pull/11527)) — thanks @pacocartones +- fix(search): prefer credentialed providers over duckduckgo-free fallback (#11524) — the fallback-only loop ran before the credentialed-providers loop in `executeWebSearch`, making configured providers unreachable when `duckduckgo-free` was available. ([#11565](https://github.com/diegosouzapw/OmniRoute/pull/11565)) — thanks @vsd2807 +- **fix(sse):** cap the upstream headers-wait phase for STREAMING requests to a client-realistic ceiling (110s, under Codex's own ~120s hard client-abort window) instead of the flat 10-minute `FETCH_TIMEOUT_MS` default — that default was 5x longer than the body-phase readiness watchdog's own adaptive bound, so a request whose upstream never returned any response at all (not even headers, e.g. a stalled NVIDIA target behind a tool-heavy Responses→Chat translation) kept the client connection alive on keepalives only, guaranteeing the client's own patience ran out first with an opaque 499 instead of OmniRoute detecting and failing the stall fast. Non-streaming requests are unaffected — they keep the existing flat default (`open-sse/utils/fetchStartTimeoutPolicy.ts`) (#11526) ([#11668](https://github.com/diegosouzapw/OmniRoute/pull/11668)) +- **fix(ci):** protocol-clients E2E harness boots the peer-stamped custom server (`run-next.mjs dev`) instead of the bare `next` CLI, so `LOCAL_ONLY` locality resolves from the real TCP peer stamp and `/api/mcp/audit` answers 200 (open bootstrap preserved via an `OMNIROUTE_E2E_BOOTSTRAP_MODE=open` credential clear in `run-next.mjs`, pinned to loopback `HOST`) instead of a deterministic 403 (#11535) ([#11549](https://github.com/diegosouzapw/OmniRoute/pull/11549)) — thanks @jonlwheat2-gif +- **fix(docs):** the Auto-Combo engine is described with the number of factors it actually declares. Four different counts were in circulation — 6, 9, 13 and 14 across `AGENTS.md`, four guides and six code comments — while `DEFAULT_WEIGHTS` declares 15. `check:docs-counts` now reads that number from the source, so adding a factor without updating the prose fails CI ([#11545](https://github.com/diegosouzapw/OmniRoute/pull/11545)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **fix(quota-share):** in-flight leases are per request, so an aborted request's slot ages out instead of being kept alive by later traffic on the same connection ([#11547](https://github.com/diegosouzapw/OmniRoute/pull/11547)) — thanks @abhisheksharma2411 +- The free-tier summary route now serves the refreshed Radar catalog when the feed is active — the same numbers the dashboard shows — and states which catalog answered plus its real build date, instead of always reporting release-frozen figures with a stale curation date. The supporter-key live feed stays reserved to authenticated callers of the instance. ([#11550](https://github.com/diegosouzapw/OmniRoute/pull/11550)) — thanks @maxmad64bis +- **sse:** fix LiveWS/embed-WS servers crashing at startup under the Node/tsx runtime — `liveServer.ts`, `embedWsProxy.ts` and `apiBridgeServer.ts` imported `@/shared/utils/httpClientAbortGuard` without the `.mjs` extension, so the client-abort crash guard added by [#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556) was unreachable and every dependent test failed with `ERR_MODULE_NOT_FOUND` ([#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556)). ([#11671](https://github.com/diegosouzapw/OmniRoute/pull/11671)) — thanks @jonlwheat2-gif / @yxyxy +- fix(chat): shed any of the matched provider-node's routing identifiers (public prefix or internal id) before model lookup, so composites like `//` no longer reach upstream verbatim (#11557) — thanks @maxmad64bis +- **fix(cache):** a semantic-cache entry now expires on its TTL instead of surviving until the next UTC midnight, and `dbEntries` stops counting expired rows ([#11573](https://github.com/diegosouzapw/OmniRoute/pull/11573)) — thanks @ntdatt812 +- **fix(api):** `/v1/models` schedules its stale-while-revalidate rebuild through Next's `after()` again, so a stale catalog reaches the client before the rebuild blocks the event loop ([#11574](https://github.com/diegosouzapw/OmniRoute/pull/11574)) — thanks @ntdatt812 +- **fix(check):** the `known-symbols` executor conformance gate awaits the now-async `getExecutor()`, so it stops reporting all 142 registered aliases as dead symbols and can detect a lazy import that fails ([#11582](https://github.com/diegosouzapw/OmniRoute/pull/11582)) — thanks @ntdatt812 +- **fix(pack):** register `bin/cli/utils/volatileEnvPath.mjs` in `PACK_ARTIFACT_REQUIRED_PATHS`, so the prune can no longer silently drop a module the published CLI imports on every boot ([#11588](https://github.com/diegosouzapw/OmniRoute/pull/11588)) — thanks @ntdatt812 +- **i18n(vi):** translate the five reliability-column strings on the free-provider rankings page, which rendered as `__MISSING__:` markers in the Vietnamese UI ([#11589](https://github.com/diegosouzapw/OmniRoute/pull/11589)) — thanks @ntdatt812 +- **resilience:** restore the expired-connection retry-budget probe in the token-health sweep — the `!isGitHubAccessTokenOnlyConnection` carve-out reintroduced by #11608 contradicted the boundary pinned by #11592, so a GitHub connection parked at `expired` with retry budget remaining was never probed and could never self-heal ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)). ([#11672](https://github.com/diegosouzapw/OmniRoute/pull/11672)) — thanks @jonlwheat2-gif / @ntdatt812 +- **fix(dashboard):** Provider Stats now displays combo success rates on their native 0–100 percentage scale instead of multiplying them twice ([#11595](https://github.com/diegosouzapw/OmniRoute/pull/11595)) — thanks @pacocartones +- **fix(providers):** route DuckDuckGo Web Chat through the verified `duck.ai` endpoint, refresh its free model catalog, and avoid forwarding unsolved anti-abuse challenges ([#11597](https://github.com/diegosouzapw/OmniRoute/pull/11597)) — thanks @solstxce +- **fix(ui):** Console log Refresh and Copy controls now expose localized accessible names, keep copy actions visible on keyboard focus, and announce copy completion safely ([#11599](https://github.com/diegosouzapw/OmniRoute/pull/11599)) — thanks @pacocartones +- Dropped the stale-`.eslintcache` `restore-keys` fallback from both "Restore ESLint file cache" steps in `ci.yml`, so the blocking `Lint` job can no longer be served per-file verdicts computed under a different lint config, suppressions file or lockfile. `quality.yml` had already dropped it in #11963; `ci.yml` — the workflow that actually gates PRs — had not (#11600). ([#11996](https://github.com/diegosouzapw/OmniRoute/pull/11996)) +- fix(ci): pass `--pass-on-unpruned-suppressions` in `run-eslint-json.mjs` so the CI Lint job no longer fails when a suppression is merely orphaned by a genuine fix, mirroring the identical fix already in `validate-release-green.mjs` (#11600) ([#11983](https://github.com/diegosouzapw/OmniRoute/pull/11983)) — thanks @hartmark +- fix(handoff): enforce provider allowlist for universal handoff (#11602) — universal handoff now skips summarization when the selected summary model's provider is not included in the configured provider allowlist. ([#11619](https://github.com/diegosouzapw/OmniRoute/pull/11619)) — thanks @vsd2807 +- **fix(dashboard):** Show a stable error state when Search Analytics returns an HTTP error or malformed data ([#11603](https://github.com/diegosouzapw/OmniRoute/pull/11603)) — thanks @pacocartones +- **test(gamification):** pin the aggregate profile level to the XP-derived semantics of #11604 — `getAggregateXp()` now derives `currentLevel` from the summed XP (`calculateLevel(sum)`), not `MAX(stored current_level)`, and the #3484 fixture levels are aligned with the XP curve ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)). ([#11676](https://github.com/diegosouzapw/OmniRoute/pull/11676)) — thanks @jonlwheat2-gif / @pacocartones +- **fix(dashboard):** Keep the Profile level and progress aligned with aggregate XP, including bounded handling for invalid totals ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)) — thanks @pacocartones +- **fix(dashboard):** Prevent locked hidden badges from revealing their icon or opening private badge details before they are earned ([#11605](https://github.com/diegosouzapw/OmniRoute/pull/11605)) — thanks @pacocartones +- **fix(dashboard):** Restore keyboard focus after shared modals close and cancel delayed autofocus during cleanup ([#11607](https://github.com/diegosouzapw/OmniRoute/pull/11607)) — thanks @pacocartones +- **fix(dashboard):** Enable Enter and Space activation for clickable data-table rows without hijacking nested controls ([#11610](https://github.com/diegosouzapw/OmniRoute/pull/11610)) — thanks @pacocartones +- **fix(autoCombo,sse):** vendor-retired catalog ids are dropped from the auto-combo candidate pool and no longer win on leftover `arena_elo` / `user_override` rows; `getModelLifecycleDecision` consults `model-lifecycle.json` (prefix-stripped) so aggregator traffic is not `untracked` for ids the snapshot already knows ([#11625](https://github.com/diegosouzapw/OmniRoute/issues/11625)) ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)) — thanks @MumuTW +- **test(chatcore):** move Codex/Claude combo fixtures off models the lifecycle guard now rejects — `gpt-5.1-codex`/`gpt-5-codex` are vendor-retired (snapshot, #11626) and `claude-3-5-sonnet-20241022` is shut down, so native-passthrough and combo-fallback tests switched to `gpt-5.6-sol` and `claude-sonnet-4.6` ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)). ([#11675](https://github.com/diegosouzapw/OmniRoute/pull/11675)) — thanks @jonlwheat2-gif / @MumuTW +- fix(opencode-plugin): stop a free-tier budget that rounds up across a magnitude from rendering as `1000K`/`1000M` in the model picker — `fmtTokens` chose its unit from the raw token count and then rounded with `toFixed(1)`, so 999,950–999,999 printed as `1000K` rather than `1M` and just under a billion printed as `1000M` rather than `1B` ([#11684](https://github.com/diegosouzapw/OmniRoute/pull/11684)) — thanks @abhisheksharma2411 +- Cap prose-derived Antigravity quota resets at the configured model cooldown maximum while preserving authoritative Retry-After headers and Google RetryInfo hints. (#11695) Thanks @pacocartones. ([#11823](https://github.com/diegosouzapw/OmniRoute/pull/11823)) +- Compare `_rebaseline_*` notes structurally in the ratchet-bank verifier so an object-valued note no longer reports itself as rewritten on an unmodified tree — the nightly `bank-ratchet-shrinks` lane had been aborting on every run since 2026-08-11, leaving 41,672 lines of earned file-size shrinks unbanked (#11702). ([#11892](https://github.com/diegosouzapw/OmniRoute/pull/11892)) — thanks @MumuTW +- **fix(build):** `prepublish.ts` bundles the ChatGPT Web (Codex) MCP bridge through `runBuildTool()` instead of spawning `npx.cmd` raw, fixing the build crash on Node ≥ 20/Windows where `.cmd` shims cannot be spawned without a shell (EINVAL) ([#11704](https://github.com/diegosouzapw/OmniRoute/issues/11704)) ([#11706](https://github.com/diegosouzapw/OmniRoute/pull/11706)) — thanks @jonlwheat2-gif +- fix(codex): keep `parallel_tool_calls:false` on the translated Codex Responses Lite path (#11707) ([#11984](https://github.com/diegosouzapw/OmniRoute/pull/11984)) — thanks @hartmark +- **fix(tests):** the new Gemini Web Images retirement test (#11708) called the now-async `getExecutor()` synchronously, throwing `TypeError: getExecutor(...).getProvider is not a function` instead of asserting Gemini Web chat stays available. Awaited to match the R0.3 executor-registry refactor (#11220). ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(search):** Allow REST and MCP web-fetch requests to select the advertised `anysearch-search` provider explicitly ([#11716](https://github.com/diegosouzapw/OmniRoute/pull/11716)) — thanks @pacocartones +- **fix(providers):** Keep overnight peak-hour protection active after midnight according to the window's configured start day ([#11718](https://github.com/diegosouzapw/OmniRoute/pull/11718)) — thanks @pacocartones +- **fix(providers):** correct all five g4f.space gateway descriptions to document proof-of-work credits or member API keys, variable limits, and the remote prompt boundary instead of promising free keyless access at five requests per minute ([#11738](https://github.com/diegosouzapw/OmniRoute/pull/11738)). +- **fix(db):** `model_capabilities` is created by a migration instead of lazily on the first models.dev sync, so a clean install and an upgraded install converge on the same schema regardless of which features have run ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(ci):** `check:install-upgrade` now fails on an `npm` install truncated by ENOSPC (npm reports it as a warning and still exits 0), authenticates its health probe so the version assertion works against the hardened health payload, frees the clean-install tree before the upgrade phase, and no longer reports a schema divergence computed from a boot that never served ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(routing):** harden session-affinity key derivation against oversized explicit IDs and empty structural-payload collisions, while recognizing Gemini and common text request shapes ([#11746](https://github.com/diegosouzapw/OmniRoute/pull/11746)) — thanks @MumuTW +- **fix(packages/browser-pool):** regenerate `package-lock.json` so the `packages/browser-pool` workspace's locked `playwright`/`@types/node` (and transitives) match its `package.json` specs, fixing cache-only/offline installs (`npm ci --offline`, Nix `buildNpmPackage`) that previously failed with `ENOTCACHED` ([#11747](https://github.com/diegosouzapw/OmniRoute/issues/11747)) — thanks @benjaminkitt ([#11784](https://github.com/diegosouzapw/OmniRoute/pull/11784)) — thanks @geekyNads +- **fix(assets):** Stop distributing 79 provider and CLI logos without verified source/license provenance, keep every provider/tool available through the internal generic icon, and prevent those IDs from falling through to an external icon CDN. ([#11750](https://github.com/diegosouzapw/OmniRoute/pull/11750)) +- **fix(providers):** retire the common `chatgpt-web` / `cgpt-web` provider because its pre-key and proof-of-work provenance could not be cleared ([#11754](https://github.com/diegosouzapw/OmniRoute/pull/11754)) — explicit chat and image requests, slash-prefixed models, persisted aliases, proxy overrides, and combo targets now fail closed with a sanitized HTTP `410` / `PROVIDER_RETIRED` before upstream dispatch. Migration 163 tombstones matching connections and invalidates their active leases without deleting history or widening API-key allowlists. The independent `chatgpt-web-codex` / `cgpt-codex` provider and its MIT-noticed vendored implementation remain available. +- **fix(usage):** quota and usage refresh no longer 409 when an exclusive lease reserves the connection ([#11758](https://github.com/diegosouzapw/OmniRoute/pull/11758)) — thanks @TheDemonTuan +- Keep the embedding registry's vector width and `embedding` type on models when a synced model exists for the same id, so `/v1/models` no longer reports registry-described embedding models widthless or untyped (#11761) — thanks @rahilmavani +- Correct `google/gemini-embedding-001` on the OpenRouter route to 3072 dimensions, the width it returns when `dimensions` is not sent (#11761) — thanks @rahilmavani +- **fix(vision-bridge):** Restrict automatic vision routing and fallbacks to models available in authoritative live provider catalogs, including aliases and effort variants ([#11767](https://github.com/diegosouzapw/OmniRoute/issues/11767)) — thanks @pacocartones ([#11813](https://github.com/diegosouzapw/OmniRoute/pull/11813)) +- **fix(providers):** reclassify Cerebras as a one-time $5 signup credit (payment method required, 30-day validity), not a recurring no-card 1M tokens/day trial ([#11773](https://github.com/diegosouzapw/OmniRoute/issues/11773)) ([#12591](https://github.com/diegosouzapw/OmniRoute/pull/12591)) — thanks @HouMinXi +- fix(cli): stop prepublish from re-rebuilding the already-built ESM-only opencode-plugin dist (#11787) ([#11990](https://github.com/diegosouzapw/OmniRoute/pull/11990)) — thanks @hartmark +- **fix(db):** Fresh installs no longer log a non-fatal `no such table: compression_run_telemetry` warning when retention cleanup runs before the lazily-created telemetry table exists ([#11802](https://github.com/diegosouzapw/OmniRoute/pull/11802)) — thanks @RaviTharuma +- fix(sse): set X-OmniRoute-Selected-Connection-Id on successful combo dispatches so downstream consumers stop falling back to an empty connection id (#11810) ([#11986](https://github.com/diegosouzapw/OmniRoute/pull/11986)) — thanks @hartmark +- **fix(services):** embedded CLIProxyAPI lifecycle checks now use public `/healthz`, while model discovery uses the configured dedicated data-plane API key instead of the management password ([#11811](https://github.com/diegosouzapw/OmniRoute/pull/11811)) — thanks @RaviTharuma +- Fix persisted model steps returning an error in the playground route simulator, and warn when structural steps cannot be simulated. ([#11862](https://github.com/diegosouzapw/OmniRoute/pull/11862)) — thanks @pacocartones +- **fix(providers):** Antigravity's dynamic mitmAlias table no longer routes `gemini-3.7-flash-{high,medium,low}` to a literal tier-suffixed upstream id just because one connected account's own discovery listed it directly — those display ids always resolve through the safe `gemini-3.7-flash-tiered` static alias, so one account's Google-provisioned access no longer 404s every sibling account of the provider ([#11824](https://github.com/diegosouzapw/OmniRoute/issues/11824), [#11651](https://github.com/diegosouzapw/OmniRoute/issues/11651)) ([#11988](https://github.com/diegosouzapw/OmniRoute/pull/11988)) — thanks @hartmark +- **fix(dashboard):** Expose Profile loading, errors, page structure, and XP progress to assistive technologies ([#11838](https://github.com/diegosouzapw/OmniRoute/pull/11838)) — thanks @pacocartones +- **fix(socks):** `Agent.connectTimeout` now bounds both SOCKS handshake and TLS connect, and `family === null` no longer falls back to `fetch-socks` ([#11842](https://github.com/diegosouzapw/OmniRoute/pull/11842)) — thanks @maxmad64bis +- **perf(compression):** OOM mitigations for large payload hashing, memoization, and token estimation ([#11844](https://github.com/diegosouzapw/OmniRoute/pull/11844) — thanks @AndrianBalanescu) +- Excluded Next.js Node File Trace manifests (`*.nft.json`) from the published npm tarball. They are build-time metadata and are never read while serving, but had grown to 668.7 MB — 61% of the package — which pushed the upload past the registry limit and made `npm publish` fail with `413 Payload Too Large`. ([#11864](https://github.com/diegosouzapw/OmniRoute/pull/11864)) +- **fix(cli):** keep the global OmniRoute server `--base-url` separate from provider-node `--endpoint` values in add, update, and validate commands ([#11860](https://github.com/diegosouzapw/OmniRoute/pull/11860)) — thanks @pacocartones +- **fix(config):** Nous Research's `Hermes-4-405B` model now displays as "Hermes 4 405B (Nous Research)" in both the provider registry and the free-model catalog, instead of the mislabelled "Hermes 4 7B" ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#11993](https://github.com/diegosouzapw/OmniRoute/pull/11993)) +- **fix(provider/nous):** inject required user= tag into Nous Research inference requests to resolve upstream 400 "missing tags" error ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#12044](https://github.com/diegosouzapw/OmniRoute/pull/12044)) +- **fix(build):** `npm run build` now fails in one second with a named package and a copy-pasteable fix when npm silently drops an externalised optional native dependency, instead of dying four minutes in with `Module not found: Can't resolve 'better-sqlite3'` ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- **fix(install):** `postinstall` no longer throws `ReferenceError: isAndroid is not defined` — failing the whole `npm install` — when the `better-sqlite3` rebuild fallback times out; the manual-fix guidance is reachable again ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- Fixed the Alibaba free-tier allowlist test that went red on its own once the shipped catalog's `validUntil` (2026-08-27) passed, leaving every PR and `main` with a failing `Unit Tests (1/8)`. The test now builds its own packs with dates it controls, and covers the expired-pack fallback that production has actually been serving. ([#11867](https://github.com/diegosouzapw/OmniRoute/pull/11867)) +- Split the npm registry upload into its own GitHub-hosted job. npm refuses `--provenance` from a self-hosted runner (`422 ... Only "github-hosted" runners are supported`), which blocked the v3.8.50 publish; the heavy verification cannot move to a hosted runner, so it now hands the proven tarball over instead. ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(ui):** the shared `DataTable` loading state no longer reads its decorative ⏳ glyph out to assistive technology, and now carries the same `role="status"` / `aria-live="polite"` / `aria-busy="true"` semantics as `PageLoading` ([#11869](https://github.com/diegosouzapw/OmniRoute/pull/11869)) — thanks @pacocartones +- **fix(api):** Bound the `timeout` accepted by `POST /api/providers/[id]/login` to the same 15s–600s window the Adobe Firefly and Conol browser-login paths already enforce, so a request can no longer pin a headful browser and the single active-login slot with an arbitrarily large poll budget. ([#11871](https://github.com/diegosouzapw/OmniRoute/pull/11871)) — thanks @pacocartones +- **fix(api):** Stop the tunnel and MITM routes from returning raw child-process error text, which disclosed host paths, binary install locations and Tailscale `tskey-*` credentials in a body some of these routes serve to non-loopback callers, and return a real 400 instead of a framework 500 when a tunnel request body fails validation. ([#11872](https://github.com/diegosouzapw/OmniRoute/pull/11872)) — thanks @pacocartones +- **fix(open-sse):** stream TTFT/ITL are sampled from a monotonic clock (`performance.now`) instead of `Date.now`, so an NTP correction or wall-clock jump can no longer inflate a genuine TTFT or produce a negative one that the `ttft >= 0` guard silently drops — protecting the router's OTel/EWMA/`usage_history`/speed-ranking signals ([#11873](https://github.com/diegosouzapw/OmniRoute/pull/11873)) — thanks @pacocartones +- **fix(providers):** xAI `reasoning_effort: "xhigh"` now reaches grok-4.6+ instead of being silently clamped to `"high"` ([#11879](https://github.com/diegosouzapw/OmniRoute/pull/11879)) — thanks @NoxzRCW +- **fix(dashboard):** the providers page no longer crashes into the error boundary when a provider id collides with an `Object.prototype` member (`constructor`, `__proto__`); icon lookups are own-property guarded ([#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)) — thanks @NoxzRCW +- **fix(skills):** injected skill tools declared in shorthand (`{"content": "string"}`) now forward valid JSON Schema, unblocking providers that validate tool schemas strictly such as Zhipu GLM on the Console Go tier ([#11881](https://github.com/diegosouzapw/OmniRoute/pull/11881)) — thanks @NoxzRCW +- **fix(api):** `POST /api/playground/simulate-route` now surfaces `combo-ref` and `provider-wildcard` persisted combo steps with a specific warning (naming the referenced combo, or the unresolved `provider/model` wildcard) instead of folding them into a generic "unsupported step" count; a `provider-wildcard` step is also now included as an unresolved target so the operator can see it is in the route (ported from [#11882](https://github.com/diegosouzapw/OmniRoute/pull/11882) — thanks @NoxzRCW). ([#11930](https://github.com/diegosouzapw/OmniRoute/pull/11930)) +- **fix(translator):** the streaming OpenAI→Claude translator keeps upstream usage, including prompt-cache tokens, when it arrives on a trailing `choices: []` chunk (Fireworks and any upstream using `stream_options.include_usage`) ([#11883](https://github.com/diegosouzapw/OmniRoute/pull/11883)) — thanks @NoxzRCW +- **fix(cli):** `omniroute update --apply` now tells you explicitly whether a running server was detected and, if so, that you must run `omniroute restart` to apply the update — it never restarted anything and previously implied the update was already live once files were installed. The dashboard's npm-mode Update flow (`/api/system/version`) now tries OmniRoute's own PID-file-managed supervisor before falling back to pm2, and reports an honest "restart required" step instead of a silent pm2-only "skipped" that read like a completed update. The server-side latest-version lookup backing the dashboard's update banner also gained `--prefer-online`, closing the same stale-npm-cache class already fixed in the CLI's own copy for #4376 ([#11885](https://github.com/diegosouzapw/OmniRoute/issues/11885)). ([#12005](https://github.com/diegosouzapw/OmniRoute/pull/12005)) +- **fix(resilience):** clear persisted LKGP pins when a target suffers connection/provider exhaustion or is skipped before dispatch due to cooldown/exhaustion/unavailability, preventing subsequent requests from repeatedly prioritizing known-dead providers ([#11911](https://github.com/diegosouzapw/OmniRoute/issues/11911)). ([#12013](https://github.com/diegosouzapw/OmniRoute/pull/12013)) — thanks @HouMinXi +- **fix(sse):** the OpenAI→Claude stream translator now defers the terminal `message_delta`/`message_stop` emission until the real usage block has arrived (or a genuine end-of-stream flush forces it) instead of emitting it immediately on `finish_reason` — previously, when the trailing usage-only chunk (`{"choices":[],"usage":{...}}`) arrived *after* the `finish_reason` chunk (the normal order for Fireworks/vLLM/Together and other `stream_options.include_usage` upstreams), the client-visible `message_delta` still carried stale/zero usage even though `state.usage` was internally corrected too late to matter (ported from [#11915](https://github.com/diegosouzapw/OmniRoute/pull/11915) — thanks @HouMinXi). ([#11933](https://github.com/diegosouzapw/OmniRoute/pull/11933)) +- **fix(sse):** `stripStore()` now forces `store=false` for stateless OpenAI-compatible Responses-API targets (unless the connection has explicitly opted in via `providerSpecificData.openaiStoreEnabled`), instead of only handling the `openai`/`agentrouter` cases — a client-supplied `store` value previously passed through untouched to backends that don't actually persist responses server-side ([#11916](https://github.com/diegosouzapw/OmniRoute/pull/11916) — thanks @HouMinXi). +- **fix(models):** custom provider-node models (synced, custom, and alias-backed) now appear under their configured prefix in the unified catalog when the operator's model-id prefix mode is canonical, instead of being dropped whenever alias-inclusion was otherwise disabled ([#11918](https://github.com/diegosouzapw/OmniRoute/pull/11918) — thanks @HouMinXi). +- **fix(models):** the unified model catalog now suppresses stale static registry models (including effort-tier variants) for any provider whose active connection has an authoritative live synced catalog, not only providers already using exclusive-synced-listing — a connection with `providerUsesAuthoritativeLiveCatalog` previously kept serving both the live-synced models and the stale static rows side by side ([#11919](https://github.com/diegosouzapw/OmniRoute/pull/11919) — thanks @HouMinXi). +- **fix(ui):** `ProviderIcon`'s three remaining unguarded lookups (`PROVIDER_ICON_ALIASES`, `LOCAL_SVG_ALIASES`, `THEMED_SVGS`) now use `Object.hasOwn()` own-property checks — a provider id such as `constructor` or `__proto__` previously resolved through the prototype chain instead of falling through to the unknown-provider CDN fallback (`getLobeProviderIcon()` itself was already guarded by [#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)); `ProviderPageHeader`'s `color` field is also now optional, matching the rest of the component's defensive typing (ported from [#11920](https://github.com/diegosouzapw/OmniRoute/pull/11920) — thanks @HouMinXi). ([#11935](https://github.com/diegosouzapw/OmniRoute/pull/11935)) — thanks @NoxzRCW +- fix(ollama): preserve multi-byte UTF-8 content split across stream chunks in the Ollama NDJSON transform, which previously corrupted CJK/emoji into U+FFFD (#11921) — thanks @pacocartones +- **fix(providers):** OrcaRouter chat requests now target `/v1/chat/completions` instead of the bare `/v1` API root, fixing the upstream `404 Invalid URL (POST /v1)` ([#11923](https://github.com/diegosouzapw/OmniRoute/pull/11923)). — thanks @echel0nn +- **fix(plugins):** deliver the `onStreamComplete` event to disk-installed plugins. The event shipped in v3.8.50 (#9669) was emitted internally but had no plugin-facing wiring, so no plugin could ever subscribe: the manifest schema silently dropped `hooks.onStreamComplete`, and the loader/manager only knew the seven legacy hooks. `onStreamComplete` is now a declarable manifest hook, wired through the loader and registered by the manager like the other hooks, and its payload carries a `requestId` so consumers can correlate the stream-completion event with the originating request ([#11934](https://github.com/diegosouzapw/OmniRoute/pull/11934)) — thanks @amaleta / @pacocartones +- **fix(db):** the Qdrant embedding-model dropdown now lists local/self-hosted providers (Ollama, LM Studio, vLLM, etc.) — an active connection is treated as "configured" when the provider allows an optional API key, not only when it has a real key or OAuth, so a running local embedding provider is no longer hidden from the picker ([#11949](https://github.com/diegosouzapw/OmniRoute/issues/11949)) ([#11995](https://github.com/diegosouzapw/OmniRoute/pull/11995)) +- **fix(providers):** Vertex AI Anthropic partner-model discovery now calls the Model Garden `v1beta1` publisher list (`/v1beta1/publishers/anthropic/models`, global) and parses its `publisherModels` envelope, so Claude models auto-synced from Vertex populate the active live catalog and route at request time instead of returning `Model '' is not available in the active live catalog` ([#11991](https://github.com/diegosouzapw/OmniRoute/issues/11991)) — thanks @fabioluissilva ([#11998](https://github.com/diegosouzapw/OmniRoute/pull/11998)) +- **fix(cli):** support `--base-url` alongside `--endpoint` in `omniroute nodes add`, `update`, and `validate` subcommands to prevent global `--base-url` shadowing issues ([#11999](https://github.com/diegosouzapw/OmniRoute/issues/11999)). ([#12033](https://github.com/diegosouzapw/OmniRoute/pull/12033)) — thanks @HouMinXi +- **fix(providers):** `cloudflare-ai` no longer refuses image content parts for every Workers AI model ([#12002](https://github.com/diegosouzapw/OmniRoute/pull/12002)) — the plain-string `content` requirement behind #2539 is carried by the _model_ schema, not by the `/ai/v1/chat/completions` endpoint (measured: an all-text part array returns 200 on `@cf/mistralai/mistral-small-3.1-24b-instruct`, `@cf/meta/llama-4-scout-17b-16e-instruct` and `@cf/meta/llama-3.3-70b-instruct-fp8-fast`, and 400 on the text-only `@cf/qwen/qwen2.5-coder-32b-instruct`). `transformRequest()` flattened every array and threw on the first non-text part (#6390), so image input was refused for vision-capable Cloudflare models that accept it. All-text arrays are still flattened — the one shape every model accepts — while an array carrying a non-text part is passed through untouched, so the attachment is still never silently dropped. Regression guards: `tests/unit/cloudflare-ai-image-parts-6390.test.ts`. — thanks @davidlinfr +- **fix(resilience):** decouple the limiter-managed execution backstop from the queue-wait budget — new `requestQueue.executionMaxWaitMs` (env `RATE_LIMIT_EXECUTION_MAX_WAIT_MS`, default 600000 = 10 min) now feeds Bottleneck's post-dispatch `expiration`, while `requestQueue.maxWaitMs` keeps its documented queue-wait semantics. Previously the queue-wait budget doubled as the execution expiration, so legitimate long-running calls on non-incremental gateways (whole generation buffered before the first upstream byte, e.g. Console Go / Command Code tiers serving GLM models) were killed mid-flight at the queue budget with a false 504 `RATE_LIMIT_EXECUTION_TIMEOUT` — the local limiter undercut the provider-aware upstream fetch-start timeouts. The surfaced 504 message now names `requestQueue.executionMaxWaitMs`; the error keeps the #4165 guarantees (disclaims an upstream timeout, preserves the Bottleneck error as `cause`, branded code + trusted provenance, classified request-scoped so combo falls back). A real queue-wait bound (the `Promise.race` around `limiter.schedule()` sketched in #9533) remains future work. (#12025) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **Call logs:** keep the `error` field when an artifact exceeds the storage cap, instead of replacing it with the omission marker. The error is the only field that says *why* a request failed and is typically ~90 bytes next to the multi-hundred-KB bodies that trip the cap, so dropping it left a size-limited row undiagnosable — a provider outage, a local timeout and an upstream 400 all rendered identically. It is now preserved at every fallback stage, truncated to 4KB if it is itself large ([#12026](https://github.com/diegosouzapw/OmniRoute/issues/12026)). ([#12095](https://github.com/diegosouzapw/OmniRoute/pull/12095)) — thanks @ntdat812 +- **fix(diagnostics):** preserve the error field (truncated to 4KB with a `[truncated: …]` suffix) in every call-log artifact size-limit fallback stage. Previously the minimal fallback replaced the error with `[omitted: call log artifact size limit exceeded]`, so an oversized artifact row showed nothing about WHY the request failed — e.g. 91 of 847 opencode-go 504 rows on one production instance were undiagnosable from the dashboard. Oversized request/response bodies are still omitted exactly as before; the error cap is independent of the payload sizes that tripped the fallback. (#12026) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **fix(sse):** OpenAI Responses clients that declare the native `web_search` tool now receive a spec-shaped `web_search_call` output item with `action.sources` alongside the preserved function-call round-trip, so search results executed through OmniRoute's own search backend are consumable by standard Responses clients (Codex, pi-web-access, …). ([#12031](https://github.com/diegosouzapw/OmniRoute/pull/12031)) — thanks @watchingdogs +- **fix(cli):** use in-thread alias resolver hooks on modern runtimes to avoid deprecation noise and improve Node.js forward compatibility ([#12073](https://github.com/diegosouzapw/OmniRoute/issues/12073)). ([#12083](https://github.com/diegosouzapw/OmniRoute/pull/12083)) — thanks @xiaoyaner0201 +- **fix(combo):** an operator-set **Agent Features → Context length** on a combo is now honored at request time. The value was persisted and advertised through `/v1/models`, but `resolveComboContextLimit()` never consulted it — so a multi-target combo whose members carry no per-model window fell through to the provider's generic `defaultContextLength` (openrouter 128000, command-code 200000) and rejected large requests with `Input exceeds context window … limit 128000` despite the combo being explicitly sized much larger. An identical single-target combo worked, because it collapses to its concrete target before the guard runs. Invalid values (0/negative/NaN/Infinity) are ignored, so the existing target → combo-min → fallback order is unchanged. ([#12090](https://github.com/diegosouzapw/OmniRoute/pull/12090)) — thanks @adivekar-utexas +- **fix(sse):** passthrough streams now estimate usage on finish when upstream closes without usage even with `stream_options.include_usage` — avoids `0 tokens / 0%` for providers that stay silent (and correctly handles trailing empty-choices usage) ([#12151](https://github.com/diegosouzapw/OmniRoute/pull/12151)) — thanks @maxmad64bis +- **fix(combos):** clearing an agent feature in the combos editor now persists — unchecking context cache protection, or emptying the system message or tool filter, sends an explicit `null` instead of dropping the field from the `PUT` body, which the update merge read as "leave unchanged" ([#12177](https://github.com/diegosouzapw/OmniRoute/pull/12177)) — thanks @foreveryh / @ntdatt812 +- **fix(memory):** self-hosted embedding endpoints now vectorize — the vector width is measured from the first embedding that comes back instead of being read from a registry that cannot describe them, so `vec_memories` is created and memories stop piling up unvectorized behind a green health check ([#12180](https://github.com/diegosouzapw/OmniRoute/pull/12180)) — thanks @kanade-hoshino / @ntdatt812 +- **fix(translator):** the leading `system` message now reaches Responses-API upstreams when its `content` is a content-part array — it was read as `typeof content === "string" ? content : ""`, so a prompt-caching client (Anthropic `cache_control`, the shape LiteLLM and the Anthropic SDK emit) had its entire system prompt replaced by an empty `instructions`. The request was still accepted with a normal `prompt_tokens` count, so the model answered with no instructions and nothing in the response said they were missing. Mid-conversation system turns already handled the array shape ([#7056](https://github.com/diegosouzapw/OmniRoute/pull/7056)); only the first one did not ([#12206](https://github.com/diegosouzapw/OmniRoute/issues/12206)). Regression guard: `tests/unit/translator-openai-responses-system-content-parts.test.ts`. ([#12207](https://github.com/diegosouzapw/OmniRoute/pull/12207)) — thanks @Gorillaz322 +- **fix(free-tier):** `/api/free-tier/summary` no longer computes its totals from a Radar feed built before the catalog the running release ships. When the cached feed is older — or carries no build date at all — the route answers from the shipped catalog, resolved through the operator's local model state so disabled and tombstoned models stay out of the numbers ([#12215](https://github.com/diegosouzapw/OmniRoute/pull/12215)). — thanks @maxmad64bis +- **fix(oauth):** Keep a Claude personal workspace and a Team organization as separate connections — they share the same email and `accountUUID`, so the email-only OAuth dedup let the second login overwrite the first account's tokens; `organizationUUID` now disambiguates them, the way `workspaceId` does for Codex ([#12222](https://github.com/diegosouzapw/OmniRoute/pull/12222)) — thanks @davidebaraldo +- **fix(resilience):** a 402 on a single paid model of a passthrough/gateway provider (e.g. `kilo-gateway`, `ollama-cloud`) no longer terminalizes the whole connection with a never-auto-recovered `credits_exhausted` status — only the paid model is locked out, so free models on the same key keep serving. 402 variant of [#3027](https://github.com/diegosouzapw/OmniRoute/issues/3027). Single-credential providers are unaffected — a 402 there is still treated as the key being genuinely out of credit ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239) / [#10616](https://github.com/diegosouzapw/OmniRoute/issues/10616)) ([#12242](https://github.com/diegosouzapw/OmniRoute/issues/12242)) — thanks @brick30llc-ctrl ([#12266](https://github.com/diegosouzapw/OmniRoute/pull/12266)) — thanks @geekyNads +- **fix(sse):** trust `finish_reason: "length"`/`"max_tokens"` over the reasoning-consumed-token ratio in response quality validation, so a reasoning model truncated below the old 90% threshold correctly fails and retries instead of returning empty content as a silent "success" ([#12262](https://github.com/diegosouzapw/OmniRoute/pull/12262)) — thanks @brick30llc-ctrl +- **fix(settings):** `PUT /api/settings/cache-config` now persists `alwaysPreserveClientCache` to the flat general settings the runtime cache-control policy actually reads; previously the value landed in the databaseSettings "cache" section and was silently ignored, so the endpoint had no effect on `cache_control` passthrough ([#12304](https://github.com/diegosouzapw/OmniRoute/pull/12304)) — thanks @davidebaraldo +- **fix(grok-cli):** treat omitted SuperGrokPro `creditUsagePercent` as 0% used so Provider Limits still renders a weekly bar (proto3 zero-elision) ([#12312](https://github.com/diegosouzapw/OmniRoute/pull/12312)) — thanks @HouMinXi +- **fix(combo):** Expose the two Auto-Combo scoring factors nobody could set — the combo validation schema and the dashboard weight sliders both declared 13 of the scorer's 15 factors, so `connectionDensity` (spreads load across a provider's connections) and `quality` were dropped on save and offered nowhere. The sliders also shipped their own default table that differed from the engine's on every non-zero factor and summed to 1.05, so the percentages shown next to them added up to 105%. Both lists now match `DEFAULT_WEIGHTS`, and a test keeps them there. Note that a combo whose stored `weights` omitted the two keys was effectively running with them at zero and the other thirteen renormalized upward; it now runs with the engine's intended distribution, so its routing does shift ([#12314](https://github.com/diegosouzapw/OmniRoute/pull/12314)) — thanks @maxmad64bis +- **fix(docs):** The free-tier reference no longer says its numbers come "confidence tagged per row" — no catalog entry carries a confidence tag and the API serves none, so every figure on that page is an estimate of the same, unstated quality. The page now states what an entry does vouch for: an independently documented hard stop (set by hand with the source in a comment, never defaulted to `true`) and a prompt-training disclosure, both with live counts the `check:docs-counts` gate keeps honest ([#12318](https://github.com/diegosouzapw/OmniRoute/pull/12318)) — thanks @maxmad64bis +- **fix(usage):** `adobe-firefly` and `firefly` have had a working usage fetcher since Adobe Firefly landed, but neither was ever added to the registration list, so the provider-plugin manifest, `genericQuotaFetcher` and the free-access quota cache all reported them as having no usage support — while `USAGE_SUPPORTED_PROVIDERS` said the opposite. Both are now declared, which also means their credit balance is fetched like any other declared provider's: `registerGenericQuotaFetchers` now registers a generic quota fetcher for them, and `resolveFreeAccessState` no longer returns early. A test holds the registration list to the dispatcher's switch in both directions, which is what the module's own docstring already asked for in prose ([#12321](https://github.com/diegosouzapw/OmniRoute/pull/12321)) — thanks @maxmad64bis +- **fix(quota):** drop the generic quota cache (agy / Antigravity / Claude OAuth) on an upstream 429 so reset-aware scoring does not keep a 60s stale snapshot, and force-refresh the next usage fetch so inner provider caches cannot recache the same window ([#12325](https://github.com/diegosouzapw/OmniRoute/pull/12325)) — thanks @HouMinXi +- **fix(combos):** deleting a combo now clears its persisted LKGP pins instead of leaving unreachable `key_value` rows behind ([#12326](https://github.com/diegosouzapw/OmniRoute/issues/12326)) ([#12425](https://github.com/diegosouzapw/OmniRoute/pull/12425)) — thanks @KrzysiekSko +- **fix(sse):** Keep ZWNJ (U+200C) and ZWJ (U+200D) in assistant text, reasoning and tool-call arguments — Persian/Kurdish half-space (`ارائه‌دهنده`), Arabic/Indic shaping and emoji sequences no longer lose them; the response de-obfuscation now removes joiners only between ASCII word characters, where the request side inserts them ([#12186](https://github.com/diegosouzapw/OmniRoute/issues/12186)) — thanks @rezjalibd ([#12359](https://github.com/diegosouzapw/OmniRoute/pull/12359)) — thanks @pacocartones +- **fix(resilience):** count resolved upstream 5xx results against the provider circuit breaker on the chat path — `CircuitBreaker.execute()` no longer reads a resolved `{ success: false, status: 5xx }` as a success that cancels the call-site failure, so a provider answering 503s now trips its breaker instead of staying `CLOSED` at `failureCount: 1`; single-model and combo dispatches are each accounted exactly once ([#12254](https://github.com/diegosouzapw/OmniRoute/issues/12254)) ([#12360](https://github.com/diegosouzapw/OmniRoute/pull/12360)) — thanks @pacocartones +- **fix(providers):** resolve the Codex quota auto-ping model from the live provider catalog and lifecycle registry instead of the retired `gpt-5.1-codex-mini`, and pause the ping with one actionable warning when no selectable Codex model exists rather than retrying a shut-down id every cooldown window ([#11905](https://github.com/diegosouzapw/OmniRoute/issues/11905)) ([#12361](https://github.com/diegosouzapw/OmniRoute/pull/12361)) — thanks @pacocartones +- **fix(api):** keep the `{created, data}` wrapper on combo-routed `/v1/images/generations` responses and default Codex image results to `b64_json` on both `/v1/images/generations` and `/v1/images/edits` so Codex CLI's built-in `image_gen` can decode them ([#12268](https://github.com/diegosouzapw/OmniRoute/issues/12268)) ([#12362](https://github.com/diegosouzapw/OmniRoute/pull/12362)) — thanks @pacocartones +- **fix(sse):** Name the shadowed custom provider node when a built-in provider id/alias (e.g. `openference` → `of`) reserves the prefix of an existing OpenAI/Anthropic-compatible node, so the runtime `No active credentials for provider: ` error explains that the prefix routed to the built-in and never reached the node's healthy connections, instead of contradicting the dashboard ([#11943](https://github.com/diegosouzapw/OmniRoute/issues/11943)) — thanks @morpheus9393 ([#12365](https://github.com/diegosouzapw/OmniRoute/pull/12365)) — thanks @pacocartones +- **fix(i18n):** wrap `ccOnboardingKeyPlaceholder` in ICU single quotes across all 43 locales so angle brackets render literally instead of being parsed as rich-text tags, which crashed the Claude Code onboarding block with `INVALID_MESSAGE: INVALID_TAG` ([#12302](https://github.com/diegosouzapw/OmniRoute/issues/12302)) ([#12369](https://github.com/diegosouzapw/OmniRoute/pull/12369)) — thanks @gonisulaimann +- **fix(models):** publish `effort_tiers` on Kimi K3's synced base-model entries (`kmca/k3`, `kmca/k3-256k`) so catalog-only clients (OpenCode, plain SDK pickers) can see and select the reasoning tiers (`low`/`high`/`max`) the synced metadata already carried — the `isSkippedEffortProvider` gate no longer suppresses tier visibility on those base entries, while synthetic `-` variant generation stays prevented and Codex/GLM base models remain excluded unchanged ([#12299](https://github.com/diegosouzapw/OmniRoute/issues/12299)) ([#12371](https://github.com/diegosouzapw/OmniRoute/pull/12371)) — thanks @gonisulaimann +- **fix(guardrails):** keep `auto`/`auto/*` virtual combos exempt from the Vision Bridge `fixedModel` credential guard so a combo target is passed through instead of silently falling back to global auto-selection ([#12237](https://github.com/diegosouzapw/OmniRoute/issues/12237)) ([#12373](https://github.com/diegosouzapw/OmniRoute/pull/12373)) — thanks @pacocartones +- **fix(combo):** capability-filter exhaustion caused by `max_tokens` above every target's known output limit now reports that reason (requested `max_tokens` vs the pool's highest known ceiling) instead of the unrelated "supports structured output" message ([#12229](https://github.com/diegosouzapw/OmniRoute/issues/12229)) — thanks @DW-MediaLab ([#12374](https://github.com/diegosouzapw/OmniRoute/pull/12374)) — thanks @pacocartones +- **fix(auth):** the `least-used` account strategy now prefers accounts without backoff before falling back to oldest `lastUsedAt`, the same tie-break `round-robin` already applies, so a failover no longer lands on a just-rate-limited account for a single request ([#12279](https://github.com/diegosouzapw/OmniRoute/issues/12279)) — thanks @tenshiak ([#12375](https://github.com/diegosouzapw/OmniRoute/pull/12375)) — thanks @pacocartones +- **fix(docker):** the `chatgpt-web-codex-browser` image now finds the Chrome binary under `chrome-linux64/` (Chrome for Testing layout in `playwright:v1.62.0-noble`) as well as the legacy `chrome-linux/`, so the container no longer crash-loops with `exec: --headless=new: not found` ([#12024](https://github.com/diegosouzapw/OmniRoute/issues/12024)) ([#12376](https://github.com/diegosouzapw/OmniRoute/pull/12376)) — thanks @pacocartones +- **fix(providers):** declare `groq/compound` and `allam-2-7b` as non-reasoning models in the curated Groq registry so `reasoning_effort` / `output_config.effort` / `thinking` from Claude Code are stripped instead of forwarded, which Groq rejected with HTTP 400 ([#12134](https://github.com/diegosouzapw/OmniRoute/issues/12134)) ([#12379](https://github.com/diegosouzapw/OmniRoute/pull/12379)) — thanks @pacocartones +- **fix(executors):** `OpencodeExecutor` no longer forces a direct connection when the connection has a proxy assigned in Proxy Management but no per-account proxies: the single-account fast path used to wrap the upstream dispatch in the direct-egress sentinel, discarding the ambient proxy context the chat handler had pinned from `proxy_assignments`, so API-key `opencode`/`opencode-go` connections egressed from the host IP (and hit geoblocks) despite the assignment. The direct pin is now applied only when no ambient proxy context exists ([#11894](https://github.com/diegosouzapw/OmniRoute/issues/11894) — thanks @hizzt) ([#12380](https://github.com/diegosouzapw/OmniRoute/pull/12380)) — thanks @pacocartones +- **fix(api):** `GET /v1/models` with `MODELS_CATALOG_PREFIX_MODE=canonical` (or `?prefix=canonical`) now lists providers whose registry alias is undefined or equal to their own id (Antigravity, Antigravity CLI and other self-aliased built-ins) — their single `provider/model` id was dropped by the alias/canonical duplicate guard in the static, synced, custom and alias-backed catalog loops ([#12058](https://github.com/diegosouzapw/OmniRoute/issues/12058)) — thanks @cheynetom ([#12381](https://github.com/diegosouzapw/OmniRoute/pull/12381)) — thanks @pacocartones +- **fix(translator):** Drop replayed `thinking` blocks that carry no signature (the shape produced from cross-provider `reasoning_content`) instead of stamping the default Claude signature on them, which Anthropic rejected with `400 Invalid signature in thinking block` on the next turn served by an Anthropic rung ([#12105](https://github.com/diegosouzapw/OmniRoute/issues/12105)) — thanks @atescivitci-cmd ([#12386](https://github.com/diegosouzapw/OmniRoute/pull/12386)) — thanks @pacocartones +- **fix(cli):** Resolve Bun's `--preload` polyfill path against the package root instead of `dist/`, so `omniroute` installed with `bun install -g` no longer crashes at startup with `error: preload not found …/dist/open-sse/utils/setupPolyfill.ts` ([#11980](https://github.com/diegosouzapw/OmniRoute/issues/11980)) — thanks @joglomedia ([#12387](https://github.com/diegosouzapw/OmniRoute/pull/12387)) — thanks @pacocartones +- **fix(providers):** `gemini-business` now publishes its model catalog — `/v1/models` and `/v1/providers/gemini-business/models` list the 12 enterprise Gemini ids the executor understands instead of returning an empty list (#12107) ([#12389](https://github.com/diegosouzapw/OmniRoute/pull/12389)) — thanks @pacocartones +- **fix(db):** install `busy_timeout` before the SQLite connection's first statement so a process opening the database while another one closes its WAL connection waits out the transient EXCLUSIVE lock instead of dying with `database is locked`, and recognise the drivers' real BUSY/PROTOCOL/IOERR errors as transient in the corruption probe so the same lock no longer renames the database away as corrupt; deflakes `cross-process contenders never both acquire the same connection` (#12394 — thanks @pacocartones) +- **fix(chat-admission):** derive the `chat_admission_busy` 503 `Retry-After` from observed heavyweight-lease occupancy — the larger of the exhausted `OMNIROUTE_CHAT_ADMISSION_QUEUE_MS` window and the time since capacity last turned over, capped at 60 s — instead of a fixed 1 s (structural) / 2 s (byte-stage) hint that invited Codex/agent fan-out clients to re-send ~1 MiB `/v1/responses` bodies every second into a gate held for the whole SSE lifetime; an idle gate keeps the historical floors ([#12135](https://github.com/diegosouzapw/OmniRoute/issues/12135)) (#12395 — thanks @pacocartones) +- **fix(api-manager):** the API key permissions modal no longer silently drops `allowedCombos` entries its Combo picker cannot render — routing-rule names such as `rt-*`, which the backend already honours — when "All" is clicked and the key is switched back to "Restrict"; those entries now survive the toggle, are listed read-only under the combo list so the count and the list agree, and are saved back verbatim instead of persisting `[]` (deny-all) (#12397 — thanks @pacocartones) +- **fix(catalog):** write the NUL separator of the catalog connection memo key, the provider serviceKind memo key, the Video Bridge promotion group key and a JSON-exactness test fixture as the `\u0000` escape instead of a raw byte — same runtime value, but the raw byte made git, GitHub and ripgrep treat those files as binary (hidden PR diffs, silently skipped searches); a guard test now keeps raw NUL bytes out of `src/`, `open-sse/` and `tests/` (#12403 — thanks @pacocartones) +- **fix(providers):** add `CLAUDE_CODE_CLIENT_VERSION` and `GITHUB_COPILOT_CLI_VERSION` env overrides so Anthropic/Copilot client-version gates can be unblocked without a rebuild ([#12417](https://github.com/diegosouzapw/OmniRoute/issues/12417)) ([#12632](https://github.com/diegosouzapw/OmniRoute/pull/12632)) — thanks @HouMinXi +- **fix(db):** back-fill `last_ping_at` and `last_pinged_reset_key` on `provider_connections` during schema reconciliation so divergent lineages that skipped `123_quota_auto_ping` still accept quota auto-ping writes ([#12470](https://github.com/diegosouzapw/OmniRoute/pull/12470) — thanks @KooshaPari) +- **fix(ci):** document MIT exceptions for `@eloqnt/{config,format-json,format-po}` (next-intl transitive; locked tarballs omit `license`) and keep the A2A lifecycle vitest off the real SQLite persistence seam ([#12581](https://github.com/diegosouzapw/OmniRoute/issues/12581)) ([#12595](https://github.com/diegosouzapw/OmniRoute/pull/12595)) +- OpenCode plugin `/v1/models` catalog fetch now waits 30s by default and attaches HTTP `statusCode` on 401/5xx so host fallback plugins can hop instead of seeing an untyped AbortError/UnknownError. ([#12607](https://github.com/diegosouzapw/OmniRoute/pull/12607)) — thanks @RaviTharuma +- **CI:** the OpenAPI security-tier gate now mirrors `isAlwaysProtectedPath()` in full — it also reads `ALWAYS_PROTECTED_API_PATTERNS`, so the pattern-gated credential routes (`/api/providers/{id}/{claude,codex}-auth/{export,apply-local}`, GHSA-5926-2w35-7h4q) no longer report as unannotated. (#12605) +- **fix(api):** GET /v1/models no longer waits forever on a hung coalesced catalog rebuild; cold-path waits are bounded (`CATALOG_BUILD_TIMEOUT_MS`, default 8s) and a last-good 200 is served when the rebuild times out ([#12627](https://github.com/diegosouzapw/OmniRoute/issues/12627)). ([#12628](https://github.com/diegosouzapw/OmniRoute/pull/12628)) — thanks @RaviTharuma +- **fix(cursor):** a non-array `tool_calls` on an assistant message no longer crashes the cursor request translator with a `TypeError`; both loops now require an array ([#12689](https://github.com/diegosouzapw/OmniRoute/issues/12689)) ([#12691](https://github.com/diegosouzapw/OmniRoute/pull/12691)) — thanks @soroush5 +- **fix(chat):** Chat Completions no longer return empty `content` after a server-owned memory or skills tool; the first provider send and account/model recovery now share one pipeline so a follow-up round-trip can fill the reply ([#12696](https://github.com/diegosouzapw/OmniRoute/issues/12696)) — thanks @HouMinXi ([#12867](https://github.com/diegosouzapw/OmniRoute/pull/12867)) +- **fix(dashboard):** batch-deleting provider keys no longer toasts failure after a successful delete when the confirm button's click event is forwarded as `onAfter` ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) — thanks @HouMinXi +- **fix(glm):** drop the extra 16th argument to `createSSETransformStreamWithLogger` that TypeScript rejected (TS2554) and that never reached the TransformStream ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) ([#12770](https://github.com/diegosouzapw/OmniRoute/pull/12770)) — thanks @HouMinXi +- **fix(dashboard):** pass `nodeMap` into Runtime `QuotaGroup` so a quota monitor in error/exhausted/alerting no longer throws `ReferenceError: nodeMap is not defined`. ([#12868](https://github.com/diegosouzapw/OmniRoute/pull/12868)) — thanks @HouMinXi +- Restricted API keys whose `allowedModels` lists a combo name no longer skip every combo member at pre-dispatch (`ALL_TARGETS_SKIPPED`). Inner-target filtering still applies when the allow-list is a provider prefix or `disableNonPublicModels` is on ([#12899](https://github.com/diegosouzapw/OmniRoute/pull/12899)). — thanks @HouMinXi +- **fix(combos):** Effort-suffixed combo members inherit the base model's `model_context_overrides` row so priority order is not inverted on large requests ([#12926](https://github.com/diegosouzapw/OmniRoute/pull/12926)) — thanks @HouMinXi +- **fix(catalog):** Picker-added `customModels` enter the dispatch-time live catalog so combo members and bare inference no longer 400 ([#12934](https://github.com/diegosouzapw/OmniRoute/pull/12934)) — thanks @HouMinXi +- **fix(docker):** re-derive the Docker build's worker-pool memory budget from the MEASURED ~4.5 GB per-process RSS (the issue owner's own VPS dmesg OOM-killer reproduction) instead of the stale 2560 MB/worker inference #11419 shipped, and lower `OMNIROUTE_BUILD_WORKERS` 3 → 2 so 1 parent + 1 page-data worker (2 processes × 4.5 GB = 9 GB) fits the 12.288 GB (75%) budget on a 16 GB GitHub Actions runner — the previous default (1 parent + 2 workers = 13.5 GB) still overcommitted the runner and kept "Publish to Docker Hub" failing with `cannot allocate memory` after #11419 merged (#7518). ([#11663](https://github.com/diegosouzapw/OmniRoute/pull/11663)) +- **fix(electron):** the packaged Windows desktop build now passes the #7592 cold-restart smoke end to end. Five defects were found and fixed on the way: (1) optional-pack staging failed on any machine whose `tar` is GNU tar — it reads the drive letter in an absolute `-f C:\...` archive path as a remote rsh host (`Cannot connect to C:`), so staging now passes a bare filename with `cwd` at the tarball directory and surfaces tar stderr on failure; (2) the Electron `files` allowlist was missing `lib/loginHeaderCapture.js`, so the packaged main process crashed at startup with `Cannot find module './lib/loginHeaderCapture'`; (3) electron-builder ≥26 injects an `!**/node_modules/**` ignore into every extraResources pattern list that no positive filter can override, which silently dropped the staged runtime `node_modules` (including the better-sqlite3 N-API prebuild) from `resources/app` — a fresh v3.8.50 desktop build would have shipped with no native SQLite driver and reproduced the #7592 sql.js fallback on every machine; an `afterPack` hook now restores the staged `node_modules` after packing; (4) the packaged-app smoke harness redirected `USERPROFILE` into its temp DATA_DIR, but Electron resolves the Roaming profile from `%USERPROFILE%\AppData\Roaming\` (USERPROFILE takes precedence over the APPDATA env var) and the path service throws instead of creating it, so `requestSingleInstanceLock()` returned false and the app exited(0) silently before `app.whenReady()` — the harness now pre-creates the derived tree, and `ensureSmokeEnvDirs` is exported and unit-tested; (5) the #7592 driver assertion parsed a `[DB] Driver: ...` line that the server's primary DB path never printed (only the unused `openDatabaseAsync()` did) — `getDbInstance()` now emits the same line on open so the guard can actually assert the native driver. Also: the smoke env-allowlist unit test hardcoded POSIX paths and could only pass on Linux/macOS; expectations are now host-agnostic, with new regression tests for the USERPROFILE-derived tree and `tarPack` under absolute Windows-style paths ([#11443](https://github.com/diegosouzapw/OmniRoute/pull/11443)) — thanks @jonlwheat2-gif +- fix(sse): stop the auto-combo candidates inspector from silently dropping model-locked/cooled-down rows (#9133) ([#11994](https://github.com/diegosouzapw/OmniRoute/pull/11994)) — thanks @hartmark +- **fix(providers):** Perplexity Web no longer turns upstream stream failures into successful assistant text; pre-content failures remain eligible for fallback, partial output ends with a structured sanitized error, and failed sessions are not persisted ([#12465](https://github.com/diegosouzapw/OmniRoute/pull/12465)) +- **fix(video-bridge):** preserve chronological video and transcript fusion, source-frame timestamps, transcript provenance, full contact-sheet intervals, and pre-abort callback isolation ([#11681](https://github.com/diegosouzapw/OmniRoute/pull/11681)) +- **Z.ai Web:** HTTP 200 streams carrying an upstream error now terminate with a structured failure instead of assistant text plus a normal stop, preserving partial output while allowing pre-content combo fallback. ([#12454](https://github.com/diegosouzapw/OmniRoute/pull/12454)) +- Absorb `Error [AbortError]: request_signal_aborted` and DOMException AbortError shapes in the process-level client-abort crash guard so routine client disconnects no longer kill the server (exit code 7). ([#12165](https://github.com/diegosouzapw/OmniRoute/pull/12165)) — thanks @alvinveroy +- **fix(sse):** Treat Adapta Web `type:error` SSE events as sanitized non-stream failures instead of empty HTTP 200 completions. ([#12459](https://github.com/diegosouzapw/OmniRoute/pull/12459)) +- **fix(autoCombo):** layer 3 (`models_dev_tier`) no longer scores retired models as premium, merge cross-provider capability rows deterministically, and inherit tier scores through variant ids ([#11508](https://github.com/diegosouzapw/OmniRoute/issues/11508)). `getModelsDevTierFitness` now vetoes ids present in `config/quality/model-lifecycle.json` with status `retired` before any other signal; `loadModelCapabilities()` aggregates per `model_id` (any-non-null-true for booleans, max for `limit_context`) instead of last-write-wins over SQLite's undefined row order; a capabilities miss retries through `resolveScoresAs()`'s base id and reports `models_dev_tier:inherited`, matching layers 1–2 (#11492). Adds `getModelsDevTierFitnessWithSource()` and an `invalidateCapabilitiesCache()` test/ops hook. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) — thanks @Meet6338-X +- fix(sse): accept Claude `content:[]` + `stop_reason: max_tokens`/`tool_use` in `detectMalformedNonStream` (match `isEmptyContentResponse`) so Claude Code `/model` probes with `max_tokens: 1` no longer become a false 502 ([#11568](https://github.com/diegosouzapw/OmniRoute/pull/11568)) — thanks @Seramicx +- **fix(security):** sanitize `request.failed` diagnostics before publishing them to live dashboard listeners and replay history, while keeping status, model, provider, latency, and internal call-log diagnostics intact. ([#12469](https://github.com/diegosouzapw/OmniRoute/pull/12469)) +- **fix(pwa):** do not intercept dashboard navigations so Chrome can fall back from a stale HTTP/3 Alt-Svc advertisement (UDP :20128 is unpublished; F5 on a long-lived tab hung until a new tab opened a fresh TCP connection). ([#12767](https://github.com/diegosouzapw/OmniRoute/pull/12767)) — thanks @HouMinXi +- **fix(executors):** handle DuckDuckGo ERR_BN_LIMIT (418) without retrying — when the upstream returns `418 ERR_BN_LIMIT` (rate-limit/ban), the executor now returns the error immediately instead of burning another VQD acquisition that would only count against the IP limit. The retry logic for `418 ERR_CHALLENGE` (unsolved challenge) remains unchanged. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) ([#12110](https://github.com/diegosouzapw/OmniRoute/pull/12110)) — thanks @oyi77 / @Meet6338-X +- Fixed DuckDuckGo streaming responses losing JSON lines and UTF-8 characters split across network chunks. ([#11528](https://github.com/diegosouzapw/OmniRoute/pull/11528)) — thanks @pacocartones +- Harden generated Cloudflare, Vercel, and Deno relays by stripping hop-by-hop, framing, and proxy authentication headers before forwarding requests upstream. ([#11533](https://github.com/diegosouzapw/OmniRoute/pull/11533)) — thanks @pacocartones +- **feat(grok-cli):** Provider Limits shows grok-cli banked reset credits from `GetRemainingResets` (including a real zero; a failed RPC omits the row) and the existing View credits button now calls `ConsumerUiSvc/RedeemReset` for grok-cli. Live tokens use nested fields 10/20/30 (id + Timestamp), not compact 1/2/3. ([#12805](https://github.com/diegosouzapw/OmniRoute/pull/12805)) — thanks @HouMinXi +- **fix(memory):** Embedding Model Quick select, Embedding Source remote dropdown, and Rerank selector now list every configured provider with embedding/rerank support instead of only chat-catalog text matches plus OpenRouter live discovery; a generic OpenAI-compatible `/embeddings` + Cohere-compatible `/rerank` runtime fallback resolves any configured chat provider's embedding/rerank endpoint, so unlisted providers no longer fail with "Unknown embedding provider"; both memory selectors gained a free-text model override ([#11390](https://github.com/diegosouzapw/OmniRoute/pull/11390)) — thanks @rqzbeh +- Harden SQLite upgrades around the historical migration-074 version collision: missing discovery and inspector tables are replayed atomically, pre-existing databases (including setup-created skeletons) receive reusable content-addressed safety snapshots, and Node test/eval probes without `DATA_DIR` are isolated from the operator database. ([#12435](https://github.com/diegosouzapw/OmniRoute/pull/12435)) +- **fix(dashboard):** Moonshot/Kimi Open Platform voucher and cash leftover percentages follow the bucket balance, so an empty wallet no longer paints those rows as 100% while Available is 0% ([#12733](https://github.com/diegosouzapw/OmniRoute/pull/12733)) — thanks @HouMinXi +- **fix(combo):** return non-retryable HTTP 400 when all candidates for a pinned native Codex turn are unavailable due to model-scoped lockout, terminating the turn cleanly while preserving turn continuity and enabling standard Combo routing on subsequent turns ([#12240](https://github.com/diegosouzapw/OmniRoute/pull/12240)) — thanks @mdigitalbh81 +- Fixed auto-router strategies preserving the selected connection through ranking and dispatch when multiple connections share the same provider and model. ([#11530](https://github.com/diegosouzapw/OmniRoute/pull/11530)) — thanks @pacocartones +- Preserve Chat Completions JSON-object response formats when translating requests to the Responses API. ([#11529](https://github.com/diegosouzapw/OmniRoute/pull/11529)) — thanks @pacocartones +- Fix idempotency fingerprints so Responses API requests include `input` and other semantic generation fields using stable serialization, while excluding credentials and request metadata. ([#11532](https://github.com/diegosouzapw/OmniRoute/pull/11532)) — thanks @pacocartones +- **fix(api):** Generated API CLI commands now enforce required OpenAPI request bodies; Combo test commands forward the required `comboName` body, while API keys created by older writers after migration 149 preserve legacy allow-all Combo access without widening explicit empty allowlists — thanks @marcelokarval ([#12070](https://github.com/diegosouzapw/OmniRoute/pull/12070)) +- **fix(grok-web):** treat upstream streaming failures as failures instead of successful assistant text: error-only streams now fail readiness with HTTP 502, while failures after legitimate content preserve that partial output and terminate through the sanitized stream failure path without a normal `stop` completion. ([#12458](https://github.com/diegosouzapw/OmniRoute/pull/12458)) +- HuggingChat now turns HTTP 200 JSONL generation failures into a sanitized 502 before content, or a fixed public stream failure after partial output, so fallback and request persistence no longer record a false successful stop. ([#12456](https://github.com/diegosouzapw/OmniRoute/pull/12456)) +- **fix(providers):** keep 1min.ai HTTP 200 stream errors out of assistant content, preserve partial output, and expose sanitized terminal errors so pre-content failures can fall back. ([#12466](https://github.com/diegosouzapw/OmniRoute/pull/12466)) +- **fix(sessions):** preserve legacy Sessions data when the additive exclusive-session projection is unavailable, returning an empty projection and warning only once per contiguous outage instead of failing the endpoint; the Sessions badge intentionally reflects the merged legacy and exclusive row count introduced by [#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389) ([#11469](https://github.com/diegosouzapw/OmniRoute/pull/11469)) — thanks @KaspaPulse +- Render Nimble Search with the generic provider icon and serve Opper's proven logo locally. ([#12415](https://github.com/diegosouzapw/OmniRoute/pull/12415)) +- **fix(quality):** `config/quality/provider-assets-provenance.jsonl` was stale against the physical `public/providers/` tree (still listing 225 assets and 5 duplicate-content groups after several provider-retirement PRs removed 85 icon files), which would have made `check:provider-asset-provenance` fail on the next full run. Reconciled the manifest to the real 142-file snapshot (142 asset records, 1 duplicate-content group, 2 newly-registered files marked `unresolved` pending provenance research), repinned `auditedCommit`, and updated the regression test's hardcoded counts to match. ([#11876](https://github.com/diegosouzapw/OmniRoute/pull/11876)) +- fix(ui): wrap quota card footer action buttons to prevent clipping Refresh button on narrow cards — when extra action buttons such as `View credits` are present on Codex cards, allow the footer row to flex-wrap with `shrink-0` buttons so `Refresh now` remains visible and clickable across all responsive card widths ([#11464](https://github.com/diegosouzapw/OmniRoute/pull/11464)) — thanks @adevwithpurpose +- Keep Antigravity Gemini usable when the same connection's Claude weekly quota is empty; generic quota cache stays per-connection for every other provider. ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) — thanks @HouMinXi +- Fixed Tailscale tunnel setup to reject ports outside the valid 1-65535 range and safely install Windows MSI packages from paths containing apostrophes. ([#11534](https://github.com/diegosouzapw/OmniRoute/pull/11534)) — thanks @pacocartones +- Classify Cloudflared and Tailscale process-management routes as local-only and spawn-capable while preserving remote access to read-only tunnel status endpoints. ([#11531](https://github.com/diegosouzapw/OmniRoute/pull/11531)) — thanks @pacocartones +- Electron release workflow: the `publish-npm` job now grants `actions: read` to the reusable `npm-publish.yml` it calls (its `publish` job requests it), which is what made GitHub refuse the whole v3.8.50 run at startup and ship the release with zero desktop assets; a `workflow_dispatch` now builds the requested tag instead of the dispatching branch and can skip the npm leg (`publish_npm=false`) when only re-attaching assets ([#11974](https://github.com/diegosouzapw/OmniRoute/pull/11974)) +- **fix(dashboard):** The Combos page usage guide now reads its dismissal through `useSyncExternalStore` instead of correcting SSR state inside an effect, removing an extra commit of the page tree on every load (and the `react-hooks/set-state-in-effect` error it raised). ([#12671](https://github.com/diegosouzapw/OmniRoute/pull/12671)) +- Electron release: `electron/package-lock.json` regained the optional `electron-builder-squirrel-windows` subtree (13 entries) that `npm ci` had been refusing as out of sync, `electron-release.yml` gained a `build_ref` dispatch input and stops regenerating release notes on a re-attach dispatch, and the npm publish workflow attaches the SBOM to the GitHub Release on dispatch publishes too — so the v3.8.51 tag ships every desktop asset and the SBOM like v3.8.49 did ([#12022](https://github.com/diegosouzapw/OmniRoute/pull/12022)) +- Drained the reds every PR against `release/v3.8.51` was born with: documented the three service-worker build-id variables, registered the six retirement/tunnel tests with the mutation gate, approved `eslint-plugin-react-hooks` in the dependency allowlist, added the six `combo.sort.*` strings to `vi` and `pt-BR`, pointed the ChatGPT Web doc at the real migration-168 test, worded the g4f hint around the member key, and realigned four tests to the retired-provider catalog and the legacy-schema fixtures the retirement migrations touch. ([#11940](https://github.com/diegosouzapw/OmniRoute/pull/11940)) +- **fix(providers):** Claude, Grok, LMArena, Notion, and Perplexity web-cookie transports now use pooled `wreq-js` 3.2 instead of the native sidecar, with all nine supported bindings pinned and audited, and the applicable platform binding plus native-license evidence included in each release artifact ([#12429](https://github.com/diegosouzapw/OmniRoute/pull/12429), supersedes [#11753](https://github.com/diegosouzapw/OmniRoute/pull/11753)). +- **fix(providers):** Zed Hosted streaming failures now trigger fallback before content and end partial streams with a sanitized structured error instead of fake assistant text and a normal-success stop. ([#12455](https://github.com/diegosouzapw/OmniRoute/pull/12455)) +- **fix(test):** capture hook result after render (direct commit `b85d0abb0b`) +- **fix(sync):** repair the two regressions the v3.8.50 sync-back left on release/v3.8.51 (direct commit `5ade9e0851`) +- **revert(models):** drop the memo reset hook ported from #10055 (direct commit `282de841ae`) +- **fix(typecheck):** guard the optional connectionId before the reactive model sync (direct commit `f2825bad75`) +- **fix(quota-share):** release the winner's reserved in-flight slot (#11371) ([#11408](https://github.com/diegosouzapw/OmniRoute/pull/11408)) — thanks @oyi77 +- **fix(providers):** stop silently dropping reasoning effort on opencode families (#10788) ([#11409](https://github.com/diegosouzapw/OmniRoute/pull/11409)) — thanks @oyi77 +- **fix(combo):** enable genuine quota-aware routing for generic providers (antigravity, claude, etc.) ([#11411](https://github.com/diegosouzapw/OmniRoute/pull/11411)) — thanks @HouMinXi +- **fix(oauth):** allow expired token retry before deactivation and activate expired retry loop (#P0) ([#11414](https://github.com/diegosouzapw/OmniRoute/pull/11414)) — thanks @HouMinXi +- **perf(executors):** lazy-load the executor registry — defer class imports + construction to first use (#11220) ([#11421](https://github.com/diegosouzapw/OmniRoute/pull/11421)) — thanks @oyi77 +- **fix:** make release/v3.8.50 CI gates green (run 32786966560 root causes) ([#11450](https://github.com/diegosouzapw/OmniRoute/pull/11450)) — thanks @jonlwheat2-gif +- **fix(sse):** exact-domain cookie match and origin-equality URL assertions ([#11452](https://github.com/diegosouzapw/OmniRoute/pull/11452)) — thanks @hartmark +- **fix(providers):** send Responses API payload during OpenAI-compatible validation ([#11454](https://github.com/diegosouzapw/OmniRoute/pull/11454)) — thanks @tuandinh0801 +- **fix(command-code):** fallback to /alpha/generate for Go plan without Provider API access ([#11455](https://github.com/diegosouzapw/OmniRoute/pull/11455)) — thanks @Chewji9875 +- **fix(responses-continuation):** fail closed on a log-truncated stored input/output array ([#11473](https://github.com/diegosouzapw/OmniRoute/pull/11473)) — thanks @hartmark +- **fix(cli):** write jcode settings as [providers.omniroute] in config.toml ([#11484](https://github.com/diegosouzapw/OmniRoute/pull/11484)) — thanks @hongnoul +- **fix(logging):** raise chat-log array truncation cap to 1000 and unify duplicate implementations ([#11499](https://github.com/diegosouzapw/OmniRoute/pull/11499)) — thanks @hartmark +- **fix:** restore live websocket URL helpers ([#11509](https://github.com/diegosouzapw/OmniRoute/pull/11509)) — thanks @PauloFH +- **perf(core):** eliminate async waterfalls across home, cache, and db operations (#11396) ([#11512](https://github.com/diegosouzapw/OmniRoute/pull/11512)) — thanks @HouMinXi +- **fix(kiro):** add runtime.us-east-1.kiro.dev as first-attempt endpoint ([#11517](https://github.com/diegosouzapw/OmniRoute/pull/11517)) — thanks @rifqiawl +- **fix(providers):** remove duplicate 128-tool truncation in OpencodeExecutor (#11444) ([#11518](https://github.com/diegosouzapw/OmniRoute/pull/11518)) — thanks @HouMinXi +- **fix(providers):** poll onboardUser LRO before classifying BYOP ([#11519](https://github.com/diegosouzapw/OmniRoute/pull/11519)) — thanks @rifqiawl +- **fix(translator):** preserve exact tool name casing for Claude tool_use payloads (#11487) ([#11520](https://github.com/diegosouzapw/OmniRoute/pull/11520)) — thanks @HouMinXi +- **fix(kimi-web):** align health probe with executor domain ([#11521](https://github.com/diegosouzapw/OmniRoute/pull/11521)) — thanks @pacocartones +- **fix(cli):** preserve existing admin password during setup ([#11522](https://github.com/diegosouzapw/OmniRoute/pull/11522)) — thanks @pacocartones +- **fix(mitm):** keep Windows DNS elevation off the POSIX sudo path (#11430) ([#11542](https://github.com/diegosouzapw/OmniRoute/pull/11542)) — thanks @official-burak +- **fix(sse):** scale chat admission by ingest byte budget instead of a fixed request count ([#11548](https://github.com/diegosouzapw/OmniRoute/pull/11548)) — thanks @TheDemonTuan +- **fix:** accept Gemini Web cookie exports ([#11554](https://github.com/diegosouzapw/OmniRoute/pull/11554)) — thanks @rafacpti23 +- **fix(i18n):** complete Brazilian Portuguese translations ([#11558](https://github.com/diegosouzapw/OmniRoute/pull/11558)) — thanks @rafacpti23 +- **fix(sse):** explicit types for openai-responses pureHelpers — clears last failing core typecheck gate ([#11567](https://github.com/diegosouzapw/OmniRoute/pull/11567)) — thanks @jonlwheat2-gif +- **fix(opencode):** hydrate Proxy Pool references for no-auth connections ([#11584](https://github.com/diegosouzapw/OmniRoute/pull/11584)) — thanks @AStupidBear +- **fix(ci):** resolve inherited release/v3.8.51 base-red CI failures ([#11608](https://github.com/diegosouzapw/OmniRoute/pull/11608)) — thanks @jonlwheat2-gif +- **fix(db):** fallback load STORAGE_ENCRYPTION_KEY from env files ([#11614](https://github.com/diegosouzapw/OmniRoute/pull/11614)) — thanks @benzntech +- **fix(providers):** mark Brave Search as no longer having a free tier ([#11621](https://github.com/diegosouzapw/OmniRoute/pull/11621)) — thanks @turbolego +- **fix:** pass tool result content through as string in OpenAI/Claude -> Gemini translation ([#11624](https://github.com/diegosouzapw/OmniRoute/pull/11624)) — thanks @Hsia97 +- **fix(sse):** scope credential health to exact key ([#11628](https://github.com/diegosouzapw/OmniRoute/pull/11628)) — thanks @KaspaPulse +- **fix(install):** guard prepare script against missing husky ([#11634](https://github.com/diegosouzapw/OmniRoute/pull/11634)) — thanks @Prajeeth-12 +- **fix(security):** redact NVIDIA_API_KEY at the log sink in the ad-hoc diag script ([#11639](https://github.com/diegosouzapw/OmniRoute/pull/11639)) +- **fix(types):** clear the two type regressions red on release/v3.8.51 ([#11640](https://github.com/diegosouzapw/OmniRoute/pull/11640)) — thanks @jonlwheat2-gif +- **fix(build):** resolve workspace: protocol dependencies to concrete semver versions during prepublish (#11540) ([#11641](https://github.com/diegosouzapw/OmniRoute/pull/11641)) — thanks @HouMinXi +- **fix(search):** properly resolve configured search connection in /v1/responses pipeline (#11524) ([#11642](https://github.com/diegosouzapw/OmniRoute/pull/11642)) — thanks @HouMinXi +- **fix(authz):** trust cf-connecting-ip for via-proxy detection when peer is Cloudflare edge (#11514) ([#11643](https://github.com/diegosouzapw/OmniRoute/pull/11643)) — thanks @HouMinXi +- **fix(security):** compare the OIDC state cookie in constant time ([#11646](https://github.com/diegosouzapw/OmniRoute/pull/11646)) +- **fix(security):** namespace the dedup hash by the calling API key ([#11649](https://github.com/diegosouzapw/OmniRoute/pull/11649)) +- **fix(models):** gate the Codex-native catalog loop on prefix mode (#11632) ([#11662](https://github.com/diegosouzapw/OmniRoute/pull/11662)) — thanks @xiaoyaner0201 +- **fix(dashboard):** project Codex routing quota in account pools ([#11666](https://github.com/diegosouzapw/OmniRoute/pull/11666)) — thanks @KaspaPulse +- **fix(ddgw):** normaliza system no upstream da release v3.8.51 ([#11692](https://github.com/diegosouzapw/OmniRoute/pull/11692)) — thanks @RhianB14 +- **fix(memory):** honor category filter in GET /api/memory ([#11699](https://github.com/diegosouzapw/OmniRoute/pull/11699)) — thanks @Sabeekhann +- **fix(ci):** detect stale workspace lockfile entries ([#11714](https://github.com/diegosouzapw/OmniRoute/pull/11714)) — thanks @pacocartones +- **fix(ci):** stamp dist/BUILD_SHA before the npm publish provenance gate ([#11721](https://github.com/diegosouzapw/OmniRoute/pull/11721)) +- **fix(docker):** decouple the best-effort Bun image from the release manifest ([#11724](https://github.com/diegosouzapw/OmniRoute/pull/11724)) +- **perf(compression):** avoid cloning memo misses twice ([#11727](https://github.com/diegosouzapw/OmniRoute/pull/11727)) — thanks @RaviTharuma +- **fix(docker):** let the bun digest artifact be absent, not fatal ([#11740](https://github.com/diegosouzapw/OmniRoute/pull/11740)) +- **fix(shared):** block cloud-metadata hosts under default remote-image guard ([#11755](https://github.com/diegosouzapw/OmniRoute/pull/11755)) — thanks @santosraju99-hub +- **fix(api):** preserve caller-provided X-Correlation-Id on chat completions ([#11760](https://github.com/diegosouzapw/OmniRoute/pull/11760)) — thanks @aniruddhaadak80 +- **fix(resilience):** lock permanently retired models instead of short backoff (Gemini ban prevention) ([#11762](https://github.com/diegosouzapw/OmniRoute/pull/11762)) — thanks @turbolego +- **fix:** mark Vercel AI Gateway as passthroughModels ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **fix(resilience):** stop hammering permanently-moved endpoints and billing-suspended accounts ([#11774](https://github.com/diegosouzapw/OmniRoute/pull/11774)) — thanks @turbolego +- **fix(lease):** remove global static reservation and gate routing on live active lease occupancy ([#11775](https://github.com/diegosouzapw/OmniRoute/pull/11775)) — thanks @TheDemonTuan +- **fix(ci):** size the install-upgrade gate to a measured run, and log the pack cost ([#11776](https://github.com/diegosouzapw/OmniRoute/pull/11776)) +- **fix(pwa):** stop serving the stale shell after deploys ([#11779](https://github.com/diegosouzapw/OmniRoute/pull/11779)) — thanks @HouMinXi +- **fix(combo):** lock GitHub models rejected as "not supported" for future requests ([#11781](https://github.com/diegosouzapw/OmniRoute/pull/11781)) — thanks @turbolego +- **fix(cli):** probe both IPv4 and IPv6 loopback for server readiness (#11766) ([#11794](https://github.com/diegosouzapw/OmniRoute/pull/11794)) — thanks @vermasomesh835 +- **fix(sse):** preserve CLIProxyAPI wrappers per connection ([#11797](https://github.com/diegosouzapw/OmniRoute/pull/11797)) — thanks @RaviTharuma +- **fix(providers):** honor model auto-fetch opt-in on create ([#11798](https://github.com/diegosouzapw/OmniRoute/pull/11798)) — thanks @RaviTharuma +- **fix(dashboard):** make model auto-fetch order independent ([#11805](https://github.com/diegosouzapw/OmniRoute/pull/11805)) — thanks @RaviTharuma +- **fix(docker):** make GHA cache exports best effort ([#11806](https://github.com/diegosouzapw/OmniRoute/pull/11806)) — thanks @RaviTharuma +- **fix(kiro):** do not permanently ban on 'User is not authorized to make this call' ([#11809](https://github.com/diegosouzapw/OmniRoute/pull/11809)) — thanks @Deftera186 +- **fix(sse):** keep cache-write tokens in OpenAI-shaped usage ([#11814](https://github.com/diegosouzapw/OmniRoute/pull/11814)) — thanks @TheDemonTuan +- **fix(docs):** resolve relative markdown and wiki links across Fumadocs and GitHub wiki ([#11834](https://github.com/diegosouzapw/OmniRoute/pull/11834)) — thanks @AndrianBalanescu +- **fix(chat):** preserve unstripped model string for passthrough provider routing ([#11840](https://github.com/diegosouzapw/OmniRoute/pull/11840)) — thanks @raheemuddin786 +- **fix(mcp):** avoid async initialization inside sync esm wrappers (#11569) ([#11849](https://github.com/diegosouzapw/OmniRoute/pull/11849)) — thanks @HouMinXi +- **fix(sse):** preserve coherent markdown token boundaries across stream translation chunks (#11606) ([#11851](https://github.com/diegosouzapw/OmniRoute/pull/11851)) — thanks @HouMinXi +- **fix(ci):** run the install-upgrade gate on disk, not on the /tmp tmpfs ([#11855](https://github.com/diegosouzapw/OmniRoute/pull/11855)) +- **fix(skills):** expand shorthand property types in injected tool schemas ([#11857](https://github.com/diegosouzapw/OmniRoute/pull/11857)) — thanks @alvinveroy +- **fix(ci):** publish npm from a hosted runner so provenance is accepted ([#11877](https://github.com/diegosouzapw/OmniRoute/pull/11877)) +- **fix(lint):** pin react hooks plugin to 7.0.1 ([#11886](https://github.com/diegosouzapw/OmniRoute/pull/11886)) +- **fix(ci):** surface and clear release-green ESLint failure ([#11890](https://github.com/diegosouzapw/OmniRoute/pull/11890)) — thanks @MumuTW +- **fix(guardrails):** prevent duplicate prompt-injection-guard log output ([#11936](https://github.com/diegosouzapw/OmniRoute/pull/11936)) — thanks @patrykkopycinski +- **fix(auth):** downgrade expected transient states from warn to debug ([#11937](https://github.com/diegosouzapw/OmniRoute/pull/11937)) — thanks @patrykkopycinski +- **fix(codex):** fail over image generation for imported free plans ([#11948](https://github.com/diegosouzapw/OmniRoute/pull/11948)) — thanks @Bl0ck154 +- **fix(codex):** normalize non-stream responses ([#11951](https://github.com/diegosouzapw/OmniRoute/pull/11951)) — thanks @Bl0ck154 +- **fix(images):** forward Antigravity image size ([#11952](https://github.com/diegosouzapw/OmniRoute/pull/11952)) — thanks @Bl0ck154 +- **fix(db):** invalidate connection cache after upsert ([#11953](https://github.com/diegosouzapw/OmniRoute/pull/11953)) — thanks @Bl0ck154 +- **fix(codex):** restore imported account state ([#11954](https://github.com/diegosouzapw/OmniRoute/pull/11954)) — thanks @Bl0ck154 +- **fix(sse):** give extended-thinking targets the reasoning readiness budget ([#11959](https://github.com/diegosouzapw/OmniRoute/pull/11959)) — thanks @abhisheksharma2411 +- **fix(dashboard):** use opaque background and readable text color on cost chart tooltips ([#11960](https://github.com/diegosouzapw/OmniRoute/pull/11960)) — thanks @ZaimMarzuki +- **fix(antigravity):** send complete loadCodeAssist metadata (ideType/platform/pluginType as numeric enums) ([#11969](https://github.com/diegosouzapw/OmniRoute/pull/11969)) — thanks @rifqiawl +- **fix(core):** resolve DB init race condition and reasoning translation ([#12003](https://github.com/diegosouzapw/OmniRoute/pull/12003)) — thanks @raheemuddin786 +- **fix(admission):** exclude reclaimable page cache from the cgroup pressure ratio ([#12017](https://github.com/diegosouzapw/OmniRoute/pull/12017)) — thanks @HouMinXi +- **fix(build):** prune non-production trees in NFT trace excludes and tsconfig ([#12028](https://github.com/diegosouzapw/OmniRoute/pull/12028)) — thanks @Chewji9875 +- **fix(migrations):** add renamed migration compatibility for 056/073/077/101 ([#12036](https://github.com/diegosouzapw/OmniRoute/pull/12036)) — thanks @oyi77 +- **fix(sse):** default crash-guard logger to console.warn, not console ([#12042](https://github.com/diegosouzapw/OmniRoute/pull/12042)) — thanks @HouMinXi +- **fix(catalog):** derive vision/modalities for built-in auto combos from effective target pool ([#12046](https://github.com/diegosouzapw/OmniRoute/pull/12046)) — thanks @Prajeeth-12 +- **fix(release):** the packaged-app smoke verifies the database opened, not a driver line the primary path never prints (twin of #12032) ([#12047](https://github.com/diegosouzapw/OmniRoute/pull/12047)) +- **fix(barrel):** migrate src/app/ off the @/lib/localDb barrel import (#11795 Phase 2) ([#12051](https://github.com/diegosouzapw/OmniRoute/pull/12051)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate src/lib/ off the localDb barrel to direct db imports (#59) ([#12052](https://github.com/diegosouzapw/OmniRoute/pull/12052)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate open-sse, src/shared, src/sse, src/models, src/domain off the @/lib/localDb barrel import (#11795 Phase 4) ([#12053](https://github.com/diegosouzapw/OmniRoute/pull/12053)) — thanks @jonlwheat2-gif +- **fix(barrel):** delete the @/lib/localDb barrel — every consumer migrated (#11795 Phase 5) ([#12055](https://github.com/diegosouzapw/OmniRoute/pull/12055)) — thanks @jonlwheat2-gif +- **fix(radar):** restore D12 public boundary ([#12057](https://github.com/diegosouzapw/OmniRoute/pull/12057)) +- **fix(dev):** bound webpack and Tailwind scans ([#12075](https://github.com/diegosouzapw/OmniRoute/pull/12075)) — thanks @backryun +- **fix(dev):** isolate root layout settings reads ([#12076](https://github.com/diegosouzapw/OmniRoute/pull/12076)) — thanks @backryun +- **fix(dev):** reduce instrumentation executor fan-out (phase 3) ([#12078](https://github.com/diegosouzapw/OmniRoute/pull/12078)) — thanks @backryun +- **fix(dev):** make logging resources HMR-singleton ([#12079](https://github.com/diegosouzapw/OmniRoute/pull/12079)) — thanks @backryun +- **fix(sse):** keep unavailable forced connections scoped ([#12080](https://github.com/diegosouzapw/OmniRoute/pull/12080)) — thanks @keeltrace +- **fix(dev):** isolate batch dispatch from instrumentation ([#12081](https://github.com/diegosouzapw/OmniRoute/pull/12081)) — thanks @backryun +- **fix(ci):** reconcile release test contract drift ([#12082](https://github.com/diegosouzapw/OmniRoute/pull/12082)) — thanks @backryun +- **fix(ci):** accept CVE-2025-68121 in the prebuilt tls-client .so, auto-close base-red issues, guard Scorecard on the default branch ([#12085](https://github.com/diegosouzapw/OmniRoute/pull/12085)) +- **fix(db):** drop three consumer-less 1proxy exports — dead-code base-red on release/v3.8.51 after the barrel deletion (#12055) ([#12087](https://github.com/diegosouzapw/OmniRoute/pull/12087)) +- **fix(api):** clear the six API-route TypeScript regressions the new gate landed red on ([#12094](https://github.com/diegosouzapw/OmniRoute/pull/12094)) +- **fix(release):** never let the tag-push Create Release append auto notes to the curated body ([#12096](https://github.com/diegosouzapw/OmniRoute/pull/12096)) +- **fix(sse):** honor CLIProxyAPI environment API key ([#12099](https://github.com/diegosouzapw/OmniRoute/pull/12099)) — thanks @RaviTharuma +- **fix(oauth):** bind Google refresh to the client that issued the token ([#12106](https://github.com/diegosouzapw/OmniRoute/pull/12106)) — thanks @HouMinXi +- **fix(dashboard):** make RequestLoggerDetail loadable outside Next — CSS via globals.css + CJS/ESM interop (#11703 base-reds) ([#12114](https://github.com/diegosouzapw/OmniRoute/pull/12114)) +- **fix(leases):** project status lease row to lease columns so joined connection PII never escapes ([#12115](https://github.com/diegosouzapw/OmniRoute/pull/12115)) — thanks @geek007git +- **fix(plugins):** do not kill the plugin process when a fire-and-forget hook times out ([#12116](https://github.com/diegosouzapw/OmniRoute/pull/12116)) — thanks @geek007git +- **fix(guardrails):** restore injection-guard logging on middleware-only routes ([#12117](https://github.com/diegosouzapw/OmniRoute/pull/12117)) — thanks @geek007git +- **fix(plugins):** refresh stored manifest from disk on activate so new hook fields reach existing installs ([#12120](https://github.com/diegosouzapw/OmniRoute/pull/12120)) — thanks @geek007git +- **fix(codex):** preserve existing provider state when bulk-import upserts a matching connection ([#12122](https://github.com/diegosouzapw/OmniRoute/pull/12122)) — thanks @geek007git +- **fix(ci):** clear the two base-reds the 2026-08-30 merge batch left on release/v3.8.51 (round 3) ([#12123](https://github.com/diegosouzapw/OmniRoute/pull/12123)) +- **fix(sse):** strip type:'custom' from Claude tools on agentrouter dispatch ([#12126](https://github.com/diegosouzapw/OmniRoute/pull/12126)) — thanks @wahidsadik371-coder +- **fix(sse):** stop advertising video providers the dispatcher cannot run ([#12131](https://github.com/diegosouzapw/OmniRoute/pull/12131)) — thanks @quiterunner-commits +- **fix(search):** treat HTTP 432 and plan limit errors as transient cooldown ([#12139](https://github.com/diegosouzapw/OmniRoute/pull/12139)) — thanks @HouMinXi +- **fix(sse):** refuse an AI Horde queue that cannot fit the request budget ([#12143](https://github.com/diegosouzapw/OmniRoute/pull/12143)) — thanks @quiterunner-commits +- **fix(ci):** clear the base-reds the afternoon merge batch left on release/v3.8.51 (round 5: provider count 352, TS2554/TS2677) ([#12144](https://github.com/diegosouzapw/OmniRoute/pull/12144)) +- **fix(translator):** strip neutral tool_choice when tools absent in Responses-to-Chat (#12141) ([#12166](https://github.com/diegosouzapw/OmniRoute/pull/12166)) — thanks @HouMinXi +- **fix(guardrails):** pass providerId to getResolvedModelCapabilities in checkComboVision (#12112) ([#12169](https://github.com/diegosouzapw/OmniRoute/pull/12169)) — thanks @HouMinXi +- **fix(translator):** strip plaintext reasoning content for opaque responses backends (#12128) ([#12171](https://github.com/diegosouzapw/OmniRoute/pull/12171)) — thanks @HouMinXi +- **perf(stream):** compile hot-path regexes once, bound token caches, fix quadratic buffering ([#12179](https://github.com/diegosouzapw/OmniRoute/pull/12179)) — thanks @opensource-elearning +- **fix:** resolve compression worker file using runtime anchors instead of… ([#12183](https://github.com/diegosouzapw/OmniRoute/pull/12183)) — thanks @NightStalker-87 +- **fix:** prevent Claude Code session kills via liveness-aware readiness + auto model echo ([#12189](https://github.com/diegosouzapw/OmniRoute/pull/12189)) — thanks @opensource-elearning +- **fix(translator):** preserve falsy primitive values in Gemini and Antigravity function response results ([#12191](https://github.com/diegosouzapw/OmniRoute/pull/12191)) — thanks @Siva010 +- **fix(oauth):** mark empty Antigravity projectId as degraded and clear stale errors (#11284) ([#12205](https://github.com/diegosouzapw/OmniRoute/pull/12205)) — thanks @HouMinXi +- **fix(providers):** gate the Codex auto-ping usage read on the shared quota throttle ([#12209](https://github.com/diegosouzapw/OmniRoute/pull/12209)) — thanks @abhisheksharma2411 +- **fix(combos):** prioritize SQLite row id over inner JSON id and notify delete errors ([#12213](https://github.com/diegosouzapw/OmniRoute/pull/12213)) — thanks @HouMinXi +- **fix(providers):** mark featherless.ai as no longer having a free tier (`hasFree: false`) ([#12216](https://github.com/diegosouzapw/OmniRoute/pull/12216)) — thanks @turbolego +- **fix(deepseek-web):** stop Turbopack dev panic in the PoW worker path resolver ([#12221](https://github.com/diegosouzapw/OmniRoute/pull/12221)) — thanks @hartmark +- **fix(chat-admission):** clarify local 503 source ([#12223](https://github.com/diegosouzapw/OmniRoute/pull/12223)) — thanks @keeltrace +- **fix(dev):** silence webpack runtime module warnings ([#12228](https://github.com/diegosouzapw/OmniRoute/pull/12228)) — thanks @backryun +- **fix(db):** use module.require for CommonJS runtime driver loading ([#12230](https://github.com/diegosouzapw/OmniRoute/pull/12230)) — thanks @raheemuddin786 +- **fix(memory):** honest probe-driven FTS5 keyword status + memory_id rowid sync ([#12231](https://github.com/diegosouzapw/OmniRoute/pull/12231)) — thanks @raheemuddin786 +- **fix(system):** propagate abort signal to stream reader in HTTP version checks ([#12232](https://github.com/diegosouzapw/OmniRoute/pull/12232)) — thanks @raheemuddin786 +- **fix(resilience):** isolate local host execution errors from provider circuit breakers ([#12233](https://github.com/diegosouzapw/OmniRoute/pull/12233)) — thanks @raheemuddin786 +- **fix(sse):** sort injected tools deterministically for prompt caching ([#12234](https://github.com/diegosouzapw/OmniRoute/pull/12234)) — thanks @raheemuddin786 +- **fix(combo):** always clear the loop-safety timer, not just on the happy path (#11804) ([#12245](https://github.com/diegosouzapw/OmniRoute/pull/12245)) +- **fix(quality):** base-red pair — stryker tap registration + turn-pin suites aligned to the window gate ([#12255](https://github.com/diegosouzapw/OmniRoute/pull/12255)) — thanks @backryun +- **fix(dev):** qualify Turbopack runtime boundaries ([#12258](https://github.com/diegosouzapw/OmniRoute/pull/12258)) — thanks @backryun +- **fix(combo):** fall back to full pool when collapsed sole survivor is context-too-small ([#12278](https://github.com/diegosouzapw/OmniRoute/pull/12278)) — thanks @opensource-elearning +- **fix(combo):** bound the pre-dispatch unavailable skip so a stale label cannot dark a pool (#12168) ([#12285](https://github.com/diegosouzapw/OmniRoute/pull/12285)) +- **fix(security):** strip Qwen/Alibaba console-session cookies from provider API responses ([#12287](https://github.com/diegosouzapw/OmniRoute/pull/12287)) +- **fix(usage):** console-aware Token Plan guidance + subscription hint on bailian 401 ([#12288](https://github.com/diegosouzapw/OmniRoute/pull/12288)) +- **fix(resourcePressure):** log numeric detail on every rejection, recover faster ([#12293](https://github.com/diegosouzapw/OmniRoute/pull/12293)) — thanks @hartmark +- **fix(release):** drain the 2026-09-01 base-red window — passthrough usage regression + radarPage i18n keys ([#12327](https://github.com/diegosouzapw/OmniRoute/pull/12327)) +- **fix(ci):** openapi-security-tiers checker must honor routeGuard patterns + imported prefixes ([#12350](https://github.com/diegosouzapw/OmniRoute/pull/12350)) — thanks @ggiak +- **perf(ci):** cache node_modules in the npm-ci-retry composite (#8084 D3) ([#12408](https://github.com/diegosouzapw/OmniRoute/pull/12408)) +- **revert(ui):** point CTAs back at their real destinations ([#12410](https://github.com/diegosouzapw/OmniRoute/pull/12410)) +- **fix(release):** drain the 2026-09-02 base-red — rerank-providers import + api-typecheck baseline ratchet ([#12414](https://github.com/diegosouzapw/OmniRoute/pull/12414)) +- **fix(types):** make system prompt injection noImplicitAny-safe ([#12416](https://github.com/diegosouzapw/OmniRoute/pull/12416)) +- **fix(quality):** validate typecheck baseline schema ([#12419](https://github.com/diegosouzapw/OmniRoute/pull/12419)) +- **fix(memory):** resolve rerank provider node cache import ([#12421](https://github.com/diegosouzapw/OmniRoute/pull/12421)) +- **fix(ci):** repair release-root regressions — pack dedup, web-session syntax, uc-image ids ([#12423](https://github.com/diegosouzapw/OmniRoute/pull/12423)) — thanks @backryun +- **fix(providers):** separate MaxAI and UC credential contracts ([#12431](https://github.com/diegosouzapw/OmniRoute/pull/12431)) +- **fix(cli):** preserve Claude settings on config set ([#12432](https://github.com/diegosouzapw/OmniRoute/pull/12432)) — thanks @KrzysiekSko +- **fix(providers):** repair the maxai credential block truncated by merge auto-resolve ([#12433](https://github.com/diegosouzapw/OmniRoute/pull/12433)) +- **fix(quality):** record the 2026-09-02 merged growth in the file-size baseline ([#12434](https://github.com/diegosouzapw/OmniRoute/pull/12434)) +- **fix(uc):** route UC error strings through sanitizeErrorMessage; allowlist the retired codex id ([#12437](https://github.com/diegosouzapw/OmniRoute/pull/12437)) +- **fix(adapta):** redact streamed upstream errors ([#12438](https://github.com/diegosouzapw/OmniRoute/pull/12438)) +- **fix(sse):** remap non-contiguous upstream tool_calls index to a gap-free output_index ([#12445](https://github.com/diegosouzapw/OmniRoute/pull/12445)) — thanks @hartmark +- **fix(quality):** detect duplicate tool_calls entries in one response ([#12446](https://github.com/diegosouzapw/OmniRoute/pull/12446)) — thanks @hartmark +- **fix(conversations):** resolve turn content OmniRoute never sends back to the client ([#12447](https://github.com/diegosouzapw/OmniRoute/pull/12447)) — thanks @hartmark +- **fix(combo):** do not treat credits-exhausted 401 as auth skip ([#12449](https://github.com/diegosouzapw/OmniRoute/pull/12449)) — thanks @RaviTharuma +- **fix(auth):** do not park healthy quota accounts as expired ([#12452](https://github.com/diegosouzapw/OmniRoute/pull/12452)) — thanks @RaviTharuma +- **fix(responses-continuation):** fail closed on a collector-truncated, empty output array ([#12460](https://github.com/diegosouzapw/OmniRoute/pull/12460)) — thanks @hartmark +- **fix(logging):** raise the SSE payload collector's default cap ([#12461](https://github.com/diegosouzapw/OmniRoute/pull/12461)) — thanks @hartmark +- **fix(resilience):** surface Responses failed.error.message in 502s ([#12472](https://github.com/diegosouzapw/OmniRoute/pull/12472)) — thanks @RaviTharuma +- **fix(security):** unbiased maxai X-Random nonce + stricter URL/regex assertions ([#12502](https://github.com/diegosouzapw/OmniRoute/pull/12502)) +- **fix(video):** re-anchor transcript log-redaction so PII/credential maskers can't reopen the leak (#12150 P1 follow-up) ([#12503](https://github.com/diegosouzapw/OmniRoute/pull/12503)) +- **fix(monitoring):** serve cached credentialHealth off the request path ([#12533](https://github.com/diegosouzapw/OmniRoute/pull/12533)) — thanks @RaviTharuma +- **fix(quota):** keep Antigravity Gemini usable when Claude weekly is empty ([#12566](https://github.com/diegosouzapw/OmniRoute/pull/12566)) — thanks @HouMinXi +- **fix(authz):** hard-gate every credential export and CLI-config write (GHSA-5926-2w35-7h4q) ([#12600](https://github.com/diegosouzapw/OmniRoute/pull/12600)) +- **fix(docs):** restore the Next build — REMOVED_PROVIDERS.md had no frontmatter (base-red #12581) ([#12610](https://github.com/diegosouzapw/OmniRoute/pull/12610)) +- **fix(quality):** re-point the zcodeProtocol public-creds allowlist to line 313 ([#12615](https://github.com/diegosouzapw/OmniRoute/pull/12615)) +- **fix(security):** close 3 advisories — search baseUrl exfil, sk- in the error sanitizer, bifrost relay header leak ([#12620](https://github.com/diegosouzapw/OmniRoute/pull/12620)) +- **fix(providers):** sanitize boolean required and nested bare maps for Gemini (#12269) ([#12624](https://github.com/diegosouzapw/OmniRoute/pull/12624)) — thanks @HouMinXi +- **fix(resilience):** keep Overloaded STREAM_EARLY_EOF off the provider breaker ([#12626](https://github.com/diegosouzapw/OmniRoute/pull/12626)) — thanks @HouMinXi +- **fix(catalog):** re-audit free-tier quotas against official pages ([#12649](https://github.com/diegosouzapw/OmniRoute/pull/12649)) +- **fix(dashboard):** keep a request's pending-tracking id stable across combo target retries ([#12650](https://github.com/diegosouzapw/OmniRoute/pull/12650)) — thanks @hartmark +- **fix(tests):** retire dead model ids from the chat-pipeline integration suite (base-red #12581) ([#12670](https://github.com/diegosouzapw/OmniRoute/pull/12670)) +- **fix:** resolve SqliteError no such table compression_run_telemetry during cleanup ([#12682](https://github.com/diegosouzapw/OmniRoute/pull/12682)) — thanks @groovecityJO +- **fix(combo):** treat a pin-only step as implicit connection allowlist ([#12697](https://github.com/diegosouzapw/OmniRoute/pull/12697)) — thanks @HouMinXi +- **fix(ci):** ship .npmrc in published package so legacy-peer-deps applies to consumers (#11544) ([#12699](https://github.com/diegosouzapw/OmniRoute/pull/12699)) — thanks @KooshaPari +- **fix(docker):** pin 4 CLI tools to exact versions (#12576) ([#12703](https://github.com/diegosouzapw/OmniRoute/pull/12703)) — thanks @KooshaPari +- **fix(docker):** document and harden cli profile trust boundary (#12570) ([#12706](https://github.com/diegosouzapw/OmniRoute/pull/12706)) — thanks @KooshaPari +- **fix(sse):** redact video transcript in pre-guardrail rejected-request logs (#12150 P2 item 7) ([#12710](https://github.com/diegosouzapw/OmniRoute/pull/12710)) +- **fix(i18n):** quote placeholder in OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES description (#12505) ([#12769](https://github.com/diegosouzapw/OmniRoute/pull/12769)) — thanks @KooshaPari +- **fix(api/pricing):** surface validation error message as string, not raw object (#12494) ([#12771](https://github.com/diegosouzapw/OmniRoute/pull/12771)) — thanks @KooshaPari +- **fix(sse):** re-enable prompt compression for native Codex passthrough ([#12834](https://github.com/diegosouzapw/OmniRoute/pull/12834)) — thanks @thomasmaerz +- **fix(dashboard):** model health tests for a provider node set to the Responses API now call `/v1/responses` with a Responses-shaped body instead of `/v1/chat/completions` — those models were reported as `Provider returned HTTP 200 but no text content` even though the same model answered normally through `/v1/responses` ([#13070](https://github.com/diegosouzapw/OmniRoute/issues/13070)) ([#13087](https://github.com/diegosouzapw/OmniRoute/pull/13087)) — thanks @ntdatt812 +- fix(routing): stop a round-robin combo's "opencode" targets from collapsing onto the opencode-zen connection (#11912) ([#13283](https://github.com/diegosouzapw/OmniRoute/pull/13283)) +- fix(routing): stop the reactive-compaction debug log from lying when compression is globally disabled (#11977) ([#13276](https://github.com/diegosouzapw/OmniRoute/pull/13276)) +- fix(electron): relativize standalone-bundle symlink targets so Stage 8 manifest verification stops failing on Windows (#11979) ([#13251](https://github.com/diegosouzapw/OmniRoute/pull/13251)) +- fix(dashboard): surface a visible error when Compression Studio's combined preview run fails (#12061) ([#13288](https://github.com/diegosouzapw/OmniRoute/pull/13288)) +- fix(dashboard): make the compression "Effective pipeline" preview honor the active profile and warn when the master switch is off (#12063) ([#13287](https://github.com/diegosouzapw/OmniRoute/pull/13287)) +- fix(providers): scope TinyCMS Web signer's DOM shims to each call instead of leaking them for the process lifetime, and surface a clean HTTP status on a non-JSON interception-toggles error (#12072) ([#13254](https://github.com/diegosouzapw/OmniRoute/pull/13254)) +- fix(guardrails): stop Vision Bridge from re-selecting a model locked after a 404 (#12111) ([#13259](https://github.com/diegosouzapw/OmniRoute/pull/13259)) +- fix(sse): require Responses-shaped body before native OpenAI-compatible passthrough (#12129) ([#13278](https://github.com/diegosouzapw/OmniRoute/pull/13278)) +- fix(providers): minimax-m3 now collapses manual thinking.type:"enabled" to adaptive, preventing upstream 400 (2013) (#12132) ([#13249](https://github.com/diegosouzapw/OmniRoute/pull/13249)) +- fix(db): scope model visibility overrides by modality so hiding a Chat model no longer hides an identically-ID'd Image/Embeddings/etc. model (#12172) ([#13258](https://github.com/diegosouzapw/OmniRoute/pull/13258)) +- fix(db): scope local-provider apiKey dedup to matching base URL so LM Studio/Ollama-style connections support multiple accounts (#12173) ([#13250](https://github.com/diegosouzapw/OmniRoute/pull/13250)) +- fix(providers): refresh Trae's stale Referer/Origin and forward user timezone so imported connections stop failing with 401 (#12190) ([#13255](https://github.com/diegosouzapw/OmniRoute/pull/13255)) +- fix(providers): route opencode-go/gpt-5.6-luna to /responses instead of /chat/completions (#12196) ([#13275](https://github.com/diegosouzapw/OmniRoute/pull/13275)) +- fix(dashboard): allow deleting the last extra-upstream-header row even when invalid (#12251) ([#13252](https://github.com/diegosouzapw/OmniRoute/pull/13252)) +- **fix(i18n):** translate pre-existing `__MISSING__:` keys for `combo.sort`, `requestLogger.detail` expand/collapse, `common.profile`, and `settings.resilienceCredentialHealth*` across 39 locales ([#12272](https://github.com/diegosouzapw/OmniRoute/issues/12272)) ([#13034](https://github.com/diegosouzapw/OmniRoute/pull/13034)) — thanks @HouMinXi +- fix(cli): run the Node.js runtime compatibility guard before the heavy `tsx/esm` + Commander import chain so an unsupported runtime gets a clear message instead of a raw `Invalid regular expression flags` crash (#12296) ([#13253](https://github.com/diegosouzapw/OmniRoute/pull/13253)) +- fix(dashboard): refresh the providers list after deleting a compatible provider node (#12298) ([#13256](https://github.com/diegosouzapw/OmniRoute/pull/13256)) +- fix(usage): fail closed on API-key budget enforcement when a provider's `auto` routing alias has no pricing row, instead of silently counting it as $0 (#12341) ([#13257](https://github.com/diegosouzapw/OmniRoute/pull/13257)) +- **fix(providers):** include the submitted Agnes video model when polling by `video_id` ([#12356](https://github.com/diegosouzapw/OmniRoute/pull/12356)) — thanks @pacocartones +- **fix(routing):** custom OpenAI-compatible nodes now honor the saved Chat/Responses API type after edits instead of letting the node's original ID prefix override the live connection setting ([#11884](https://github.com/diegosouzapw/OmniRoute/issues/11884)). ([#12358](https://github.com/diegosouzapw/OmniRoute/pull/12358)) — thanks @pacocartones +- fix(sse): surface an error instead of a silent empty 200 when a Claude stream closes with zero bytes (#12398) ([#13285](https://github.com/diegosouzapw/OmniRoute/pull/13285)) +- fix(oauth): warn before the dead localhost:8080 redirect in antigravity/gemini `oauth start` (#12413) ([#13265](https://github.com/diegosouzapw/OmniRoute/pull/13265)) +- fix(providers): stop devin-cli spawn error from double-closing the SSE controller (#12517) ([#13261](https://github.com/diegosouzapw/OmniRoute/pull/13261)) +- **fix(rerank):** clamp Voyage `top_k` to the documents actually sent after empty-string filtering, and honor `return_documents: false` in the NVIDIA response adapter (#12523 — thanks @pacocartones) +- **fix(audio):** `/v1/audio/translations` now resolves combo names the way `/v1/audio/transcriptions` already does, so a combo that `GET /v1/models` advertises is fanned out to its targets instead of being rejected with `400 Invalid translation model: . Use format: provider/model`; literal `provider/model` ids and unknown bare names behave as before (#12536 — thanks @pacocartones) +- **fix(gemini):** strip the JSON-Schema-2020-12 `prefixItems` keyword from Gemini tool schemas at every nesting level, so Claude Code tool definitions no longer fail with `400 Unknown name "prefixItems"` on Gemini models (#12540 — thanks @pacocartones) +- **fix(api-manager):** Expose an accessible loading status while API keys are fetched instead of an empty accessibility tree (#12541 — thanks @pacocartones) +- **fix(video):** Clamp `estimateJpegFrameBytes` at zero for padding-only payloads and build the three encode-side frame data URIs from `JPEG_FRAME_DATA_URI_PREFIX` instead of a repeated literal (#12543 — thanks @pacocartones) +- **fix(devin):** accept Windows `DEVIN_AGENTIC_HOME` sandbox paths (`C:\...\.sandbox\...`) in the isolated-home check so the Devin Claude Bridge no longer fails closed on Windows ([#12405](https://github.com/diegosouzapw/OmniRoute/issues/12405)) (#12545 — thanks @pacocartones) +- **fix(db):** Add `conversation_turn_nodes` and orphaned `agentic_conversations` to the auto-cleanup cycle under the existing `retention.callLogs` window, so identity nodes whose call-log content has already been purged no longer accumulate without bound in `storage.sqlite` (#12548 — thanks @pacocartones) +- **fix(i18n):** Wrap the `~/.claude/profiles//settings.json` placeholder in ICU single quotes in the `OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES` feature-flag description across all 42 locales and the TypeScript default, so next-intl no longer fails with `INVALID_MESSAGE: UNCLOSED_TAG` and the Feature Flags card shows the description instead of the raw key (#12549 — thanks @pacocartones) +- **fix(orchestration):** `updateCloudAgentTask` now publishes the task's real `status` on `agent.task.updated` when an update only touches `result`, `activities` or `error`, instead of the fabricated `"updated"` state, and stays silent when no row matched the id (#12550 — thanks @pacocartones) +- **fix(i18n):** the home "Recent Requests" panel and the Provider Topology legend are now translated instead of rendering English copies on non-English dashboards; the legend reads its own `home.topologyLegend*` labels with consistent casing rather than borrowing the memory-settings "Recent" and analytics "Error" strings (#12551 — thanks @pacocartones). +- **docs(reference):** bring the `FEATURE_FLAGS.md` catalog back to 1:1 with `featureFlagDefinitions.ts` — 20 missing flags added, the two `*_BLOCK_THRESHOLD` env-only knobs moved out of the flag tables, category/total counts and the Live WS port corrected, guarded by a static test (#12552 — thanks @pacocartones) +- **fix(i18n):** backfill missing `usage.kiloPass*` strings in 39 locales and restore `featureFlagOmnirouteDisableThinkingLevelVariantsDescription` in `pt.json` ([#12561](https://github.com/diegosouzapw/OmniRoute/issues/12561)) ([#13027](https://github.com/diegosouzapw/OmniRoute/pull/13027)) — thanks @HouMinXi +- fix(docker): default docker-compose app ports (dashboard/API/live-WS) to loopback instead of `0.0.0.0`, closing the anonymous `/v1` LAN/WAN exposure gap left open by `REQUIRE_API_KEY=false` (#12568) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): close DNS-rebinding SSRF gap in webhook outbound-URL guard (#12569) ([#13243](https://github.com/diegosouzapw/OmniRoute/pull/13243)) +- fix(docker): require a per-session token on the VNC browser CDP bridge and isolate it on a dedicated Docker network (#12571) ([#13241](https://github.com/diegosouzapw/OmniRoute/pull/13241)) +- fix(open-sse): write Adobe Firefly session tokens and cookie jars with 0700/0600 permissions instead of the process umask (#12572) ([#13242](https://github.com/diegosouzapw/OmniRoute/pull/13242)) +- fix(api): remove hardcoded wildcard CORS in openai-to-gemini-sse.ts so the centralized fail-closed CORS gate is the sole source of `Access-Control-Allow-Origin` (#12573) ([#13239](https://github.com/diegosouzapw/OmniRoute/pull/13239)) +- fix(api): enforce API key policy (budget/rate-limit/schedule/endpoint scoping) on the ElevenLabs speech-to-text, text-to-speech and voices proxy routes (#12574) ([#13244](https://github.com/diegosouzapw/OmniRoute/pull/13244)) +- fix(sse): cap HuggingChat NDJSON body size and bound the read loop with the fetch timeout so a stalled or hostile upstream cannot buffer unbounded memory (#12577) ([#13282](https://github.com/diegosouzapw/OmniRoute/pull/13282)) +- fix(docker): scope the cliproxyapi/qdrant/bifrost sidecars to loopback by default and forward `CLIPROXYAPI_MANAGEMENT_KEY` into the cliproxyapi container so its management API is not left both unauthenticated and LAN-published (#12578) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): create DB export temp paths with `fs.mkdtempSync` instead of predictable timestamps (#12579) ([#13237](https://github.com/diegosouzapw/OmniRoute/pull/13237)) +- Cline 401 bodies that say "re-authenticate your Cline account" classify as a refreshable OAuth token, not a terminal expired key. The cooling panel no longer labels every cooldown as a 429; it shows the recorded last error instead. (#12594) ([#13060](https://github.com/diegosouzapw/OmniRoute/pull/13060)) — thanks @HouMinXi +- **fix(catalog):** degrade unknown combo targets instead of dropping LCD modalities, and persist OpenRouter `architecture.input_modalities` into the capability snapshot ([#12613](https://github.com/diegosouzapw/OmniRoute/issues/12613)) ([#13042](https://github.com/diegosouzapw/OmniRoute/pull/13042)) — thanks @HouMinXi +- fix(providers): send `x-api-key` instead of `Authorization: Bearer` for OpenCode Zen's `/v1/responses` endpoint (Muse Spark Contributor models), fixing a 401 on OmniRoute's auth header (#12633) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(sse): surface the actionable "Auggie CLI not found" message when the shell reports a missing `auggie` binary via exit code instead of a spawn error (#12645) ([#13263](https://github.com/diegosouzapw/OmniRoute/pull/13263)) +- **fix(config):** Persist deletions of built-in background-degradation entries — when a stored settings record exists its `degradationMap` is now authoritative instead of being merged under the defaults, so an entry the user removed in the dashboard no longer reappears on the next apply or restart ([#12424](https://github.com/diegosouzapw/OmniRoute/issues/12424)) ([#12647](https://github.com/diegosouzapw/OmniRoute/pull/12647)) — thanks @pacocartones +- **fix(gamification):** action-count badge milestones (First Token, Token Consumer, Token Machine, Token Whale, and the token-sharing tier) are now backed by a durable `xp_action_counts` counter incremented in `addXp()`, instead of a live `COUNT(*)` over `xp_audit_log`. The audit log is pruned by `retention.xpAuditLog` (default 30 days), so on a default install those "lifetime" milestones were really "actions in the last 30 days" and unlocked badges could stop unlocking once old rows aged out. `getActionCount()` and `checkActionCountBadges()` now read the same durable source, and a migration backfills existing totals from the surviving audit rows ([#12546](https://github.com/diegosouzapw/OmniRoute/issues/12546)) ([#12651](https://github.com/diegosouzapw/OmniRoute/pull/12651)) — thanks @pacocartones +- **fix(images):** `/v1/images/edits` now iterates a combo's targets the same way `/v1/images/generations` does (#9239) instead of flattening a bare combo to its first target. A combo whose first target is not edit-capable — or lacks credentials — now falls through to a later edit-capable target rather than hard-erroring, and missing credentials are skipped (not a hard `401`) to match the generations path. The per-target skip/terminal classification is extracted into a shared `runImageComboTargets` loop, so generations behavior is unchanged ([#12547](https://github.com/diegosouzapw/OmniRoute/issues/12547)). ([#12653](https://github.com/diegosouzapw/OmniRoute/pull/12653)) — thanks @pacocartones +- fix(sse): add first-byte watchdog to the TLS-fingerprint transport so a stalled wreq body falls back instead of hanging for minutes (#12656) ([#13272](https://github.com/diegosouzapw/OmniRoute/pull/13272)) +- fix(sse): exempt tiny-budget reasoning probes from combo quality failure and surface persisted-cooldown skips in ALL_TARGETS_SKIPPED diagnostics (#12659) ([#13269](https://github.com/diegosouzapw/OmniRoute/pull/13269)) +- fix(models): declare the real ~1M contextLength for OpenCode Zen's Muse Spark 1.2 models instead of falling back to the 200000 provider default (#12681) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(oauth): align codebuddy-cn OAuth User-Agent with the chat/usage CLI version to avoid WAF false positives (#12702) ([#13264](https://github.com/diegosouzapw/OmniRoute/pull/13264)) +- fix(dashboard): surface an authentication-required banner instead of silently blanking database settings for a guest session (#12709) ([#13270](https://github.com/diegosouzapw/OmniRoute/pull/13270)) +- **fix(resilience):** stop unbounded queue that hangs 6min until Aborted — gate, provider slot, and Bottleneck queue now share a per-connection `maxWaitMs` budget; `fail-closed` on exhaust (503); execution backstop `executionMaxWaitMs` overridable per connection with upstream clamp ([#12715](https://github.com/diegosouzapw/OmniRoute/pull/12715)) — thanks @maxmad64bis (with thanks to @Tushar49 for surfacing the slow-provider need in #12635) +- **fix(auto-combo):** every mode pack now carries `quality` and `reliability` weights (`quality` 0.02, 0.03 in `quality-first`; `reliability` 0.03, 0.04 in `reliability-first`), so selecting a pack no longer silences either signal; `DEFAULT_WEIGHTS` is unchanged ([#12731](https://github.com/diegosouzapw/OmniRoute/pull/12731)) — thanks @maxmad64bis +- **fix(sse):** the provider execution pipeline keeps the upstream error `code`/`type` again — both were lost when the non-streaming leg was extracted, so a config-class `422` (Antigravity missing project) degraded into a generic account cooldown instead of reaching the gate that recognises it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(db):** a legacy `call_logs` table no longer aborts startup — the provider-stats index is created after the column healing runs, not before it, so an install predating `request_type` opens instead of failing with `no such column` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(catalog):** a malformed row in the operator-writable custom-models blob no longer kills every `auto/*` pool ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(tests):** realign the guards that the same day's merges left asserting the old behavior — the provider-count total after GreenPT and EURouter, the GLM stream buffer slot that `#12925` turned into a declared parameter, and the injection scan bound that `#13104` reshaped from a head-only window into head-plus-tail ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- fix(cache): fold tool_choice/tools/response_format into the semantic cache signature so a cached tool_calls response can no longer be replayed for a request whose tool policy forbids it (#12734) ([#13267](https://github.com/diegosouzapw/OmniRoute/pull/13267)) +- **fix(models):** stop treating provider-supplied `isFree:true`/`:free`/`0/0` flags as trusted unless the provider has a documented free tier — only the shipped free-tier catalog decides otherwise; fetched `isFree:true` trusted only for a free-tier provider, custom `isFree:true` via a trusted path ([#12744](https://github.com/diegosouzapw/OmniRoute/pull/12744)) — thanks @maxmad64bis +- fix(memory): authenticate the internal /v1/rerank loopback call so memory reranking no longer silently degrades to unranked order when REQUIRE_API_KEY=true (#12745) ([#13268](https://github.com/diegosouzapw/OmniRoute/pull/13268)) +- fix(sse): parse Ollama Cloud's current usage markup (`$X of $Y used` aria-label, nested width style) (#12749) ([#13260](https://github.com/diegosouzapw/OmniRoute/pull/13260)) +- fix(cli): setup-opencode no longer sends an active context's management token to `/v1/models` when `--api-key`/`OMNIROUTE_API_KEY` is supplied — an explicit flag or the env var now always outranks the context's token, and the flag itself is no longer swallowed by the parent program's global `--api-key` option (#12783) ([#13246](https://github.com/diegosouzapw/OmniRoute/pull/13246)) +- fix(sse): register Arcee AI in the executor provider registry so requests reach api.arcee.ai instead of silently falling back to OpenAI (#12784) ([#13277](https://github.com/diegosouzapw/OmniRoute/pull/13277)) +- **fix(quota):** share one upstream quota read when identical saturation checks arrive at the same time, so traffic bursts don't multiply provider API calls ([#12787](https://github.com/diegosouzapw/OmniRoute/pull/12787)) — thanks @maxmad64bis +- **fix(db):** ignore expired or invalid rate-limit cooldown writes so a stale timestamp can't lock a connection that should be usable — clearing still works as before ([#12788](https://github.com/diegosouzapw/OmniRoute/pull/12788)) — thanks @maxmad64bis +- **fix(providers):** single source for provider order with `xao` ranking alongside `xai-oauth` ([#12790](https://github.com/diegosouzapw/OmniRoute/pull/12790)) — thanks @maxmad64bis +- **fix(routing):** off-table models check the free-model catalog before inheriting premium prices, latency bootstraps from the observed pool median, async tiers read live database pricing with a 90-day freshness gate, and the tier cache invalidates on every pricing write ([#12792](https://github.com/diegosouzapw/OmniRoute/pull/12792)) — thanks @maxmad64bis +- **fix(auto-combo):** snapshot scoring uses the observed per-connection account tier and per-model quality instead of neutral constants, and circuit-open providers rank lower at build time ([#12794](https://github.com/diegosouzapw/OmniRoute/pull/12794)) — thanks @maxmad64bis +- **fix(pool):** empty `auto/*` pools now say why they are empty, and the models listing uses the same paid check as routing ([#12795](https://github.com/diegosouzapw/OmniRoute/pull/12795)) — thanks @maxmad64bis +- fix(routing): recognize CLIProxyAPI's 'unknown provider for model' 400 as fallback-worthy (#12800) ([#13284](https://github.com/diegosouzapw/OmniRoute/pull/13284)) +- **fix(sse):** translate-mode streams now emit the estimated token counts as a trailing usage-only chunk before `[DONE]` when the upstream stays silent, so metered chat clients see totals instead of nothing ([#12828](https://github.com/diegosouzapw/OmniRoute/pull/12828)) — thanks @maxmad64bis +- **fix(db):** provider stats stay truthful on empty and legacy databases: fallback counts default to `0` instead of `null`, latency averages read `null` (not `0`) when no durations were recorded, failures logged before the error-type column existed group under `pre_migration` instead of `unclassified`, and per-provider queries use two new composite indexes ([#12832](https://github.com/diegosouzapw/OmniRoute/pull/12832)) — thanks @maxmad64bis +- fix(nvidia): fail open when a synced model catalog goes stale instead of gating forever (#12849) ([#13248](https://github.com/diegosouzapw/OmniRoute/pull/13248)) +- **fix(db):** WAL maintenance lives in its own module: a `TRUNCATE` checkpoint that hits a busy database now warns with its streak and retries once via `PASSIVE` instead of logging success, and closing-time checkpoints no longer report success on builds without a database file, with the busy totals visible in the authenticated monitoring health payload ([#12853](https://github.com/diegosouzapw/OmniRoute/pull/12853)) — thanks @maxmad64bis +- **fix(health):** quota with no snapshots reads empty instead of a contradicting 0% ([#12857](https://github.com/diegosouzapw/OmniRoute/pull/12857)) — thanks @maxmad64bis +- fix(a2a): accept the dashboard's own session cookie on /a2a so "Run message/send" no longer fails with "Unauthorized: missing or invalid API key" (#12888) ([#13271](https://github.com/diegosouzapw/OmniRoute/pull/13271)) +- **fix(logs):** the Logs grid's in-memory filter pass no longer discards rows the SQL query already matched — selecting an API key from the dropdown (which sends the key's id) returns its calls again, the Combo tab shows every combo instead of only those whose name contains a "1", and the model filter and search cover the same columns as the query ([#12896](https://github.com/diegosouzapw/OmniRoute/pull/12896)) — fixes [#12873](https://github.com/diegosouzapw/OmniRoute/issues/12873) — thanks @ntdatt812 +- **fix(a2a):** `/api/a2a/status` now builds the agent card from the request that asked for it, so a gateway reached at a non-localhost host no longer advertises `http://localhost:20128` as its A2A URL ([#12918](https://github.com/diegosouzapw/OmniRoute/pull/12918)). — thanks @ntdatt812 +- **fix(compression):** progressive aging now appends its `[COMPRESSED:aging:…]` annotation after a turn's `tool_result` blocks instead of in front of them, so Anthropic no longer rejects aged conversations with "`tool_use` ids were found without `tool_result` blocks immediately after" ([#12920](https://github.com/diegosouzapw/OmniRoute/pull/12920)). — thanks @ntdatt812 +- **fix(bedrock):** model import now resolves context limits for every vendor prefix instead of only `anthropic.*`, so `global.openai.gpt-5.6-*` no longer imports with a null `inputTokenLimit` and gets rejected pre-flight at the 200k default ([#12921](https://github.com/diegosouzapw/OmniRoute/pull/12921)). — thanks @ntdatt812 +- **fix(stream):** the 64 KB stream buffer GLM asks for is honoured instead of dropped, and the type error it caused no longer fails the API Route Typecheck gate on every open PR ([#12925](https://github.com/diegosouzapw/OmniRoute/pull/12925)) — thanks @ntdatt812 +- **fix(guardrails):** mask PII inside a `tool_result`'s nested content array, which the masker walked past while redacting its sibling block ([#12930](https://github.com/diegosouzapw/OmniRoute/pull/12930)) — thanks @ntdatt812 +- **fix(dashboard):** the Radar catalog table no longer leaves absent data unexplained — empty limits, unknown context windows, and unreported capabilities each explain themselves on hover, and a new check keeps it that way ([#12937](https://github.com/diegosouzapw/OmniRoute/pull/12937)) — thanks @maxmad64bis +- **fix(sse):** geo-blocked opencode requests rotate to the next account proxy instead of failing, so one refused egress no longer aborts the whole chain ([#12941](https://github.com/diegosouzapw/OmniRoute/pull/12941)) — thanks @maxmad64bis +- **fix(codex):** dashboard "clear cooldown" and the CAS recovery path now drop nested `codexScopeRateLimitedUntil` maps in the same write that nulls `rate_limited_until`, and fresh quota snapshots with headroom lift fallback-sourced scope cooldowns parked by quota preflight ([#12817](https://github.com/diegosouzapw/OmniRoute/issues/12817), [#12860](https://github.com/diegosouzapw/OmniRoute/issues/12860), [#12951](https://github.com/diegosouzapw/OmniRoute/pull/12951)) The dashboard reset-credit button (`consumeCodexResetCredit`) now exercises that same snapshot path after a successful redeem, so a filled quota bar is enough to unpark a leftover fallback Codex child without another manual clear. — thanks @HouMinXi +- **fix(db):** resolve `sql-wasm.wasm` across global npm install and hoisted layouts, ensuring OmniRoute can boot cleanly on Node 24 when native `better-sqlite3` is uncompiled. ([#13035](https://github.com/diegosouzapw/OmniRoute/pull/13035)) — thanks @HouMinXi +- **fix(api):** `DELETE /v1/batches/delete-completed` now sweeps only the calling API key's own completed batches (batches with no owner stay out of a key-scoped sweep on purpose), with an explicit instance-wide mode reserved for authenticated dashboard sessions, a 401 for a presented key that is unknown, revoked, deactivated, banned or expired (never falling through to the session branch), audit logging of every sweep, a sanitized 500 on failure, an owner-scoped file half (a key-scoped sweep never nulls a file another tenant owns) and an atomic sweep — chunked in 200-batch transactions in instance mode — so a mid-way error never leaves a batch pointing at a nulled file (GHSA-wvxc-jp3v-5mg5) ([#12969](https://github.com/diegosouzapw/OmniRoute/pull/12969)) +- **fix(combo):** quota-weighted routing stops drawing on an out-of-credit connection — a 402 now invalidates the stored quota snapshot instead of leaving its stale remaining percentage in place, and a snapshot older than 10 minutes no longer counts as confident headroom for the primary pool ([#12972](https://github.com/diegosouzapw/OmniRoute/pull/12972)) ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- **fix(vision):** Custom Models with "Vision capable" checked no longer have image requests swapped to `glm/glm-4.6v` when the client sends the advertised alias (`vllm/path/...`) or the bare path-shaped id — Vision Bridge now matches the stored override for all three id forms ([#12758](https://github.com/diegosouzapw/OmniRoute/issues/12758)) ([#12974](https://github.com/diegosouzapw/OmniRoute/pull/12974)) — thanks @HouMinXi +- **fix(sse):** transient opencode upstream failures rotate to the next account proxy instead of failing, so one flapping egress no longer aborts the whole chain ([#12975](https://github.com/diegosouzapw/OmniRoute/pull/12975)) — thanks @maxmad64bis +- **fix(azure):** Deployments from GPT-6 onward now send `max_completion_tokens` instead of `max_tokens`, which Azure rejects with HTTP 400. The rule matched a literal `gpt-5`, so each new generation arrived broken; it now matches the generation range, while `gpt-35-turbo` still keeps `max_tokens`. ([#13007](https://github.com/diegosouzapw/OmniRoute/pull/13007)) — thanks @ntdatt812 +- **fix(db):** add an opt-in self-restart circuit for sustained critical memory pressure, gate post-cleanup VACUUM behind a minimum freed-rows threshold, and checkpoint the SQLite WAL every 5 minutes with a size guard that escalates to TRUNCATE, so a growing WAL can no longer stall the event loop into a full outage. ([#13011](https://github.com/diegosouzapw/OmniRoute/pull/13011)) — thanks @HouMinXi +- **fix(auth):** an explicit connection pin may probe a quota-disabled row once and re-enable it on success ([#12874](https://github.com/diegosouzapw/OmniRoute/issues/12874)) ([#13017](https://github.com/diegosouzapw/OmniRoute/pull/13017)) — thanks @HouMinXi +- **fix(api):** thread `X-OmniRoute-Fallback-Attempts` through combo chat completions so streaming and non-streaming responses report how many prior legs were attempted ([#13038](https://github.com/diegosouzapw/OmniRoute/pull/13038)) — thanks @HouMinXi +- Restore provider failure classification and credential refresh on non-streaming requests: classify non-2xx failures to lock models on per-model quota exhaustion, update connection rate limits from headers and body, and pass credential refresh handlers to pipeline execution so 401 tokens can be refreshed and retried (#13043). ([#13069](https://github.com/diegosouzapw/OmniRoute/pull/13069)) — thanks @HouMinXi +- **fix(responses):** wrap forced-non-streaming web_search fallback JSON as Responses SSE so Codex still sees `response.completed` ([#13050](https://github.com/diegosouzapw/OmniRoute/pull/13050)) — thanks @HouMinXi +- **fix(antigravity):** Preserve upstream thought-token usage in normalized completion and reasoning token counts ([#13055](https://github.com/diegosouzapw/OmniRoute/pull/13055)) — thanks @pacocartones +- Fix a concurrency-slot leak in the DeepSeek PoW solver: a worker that failed to spawn (for example a missing worker script) never released its slot, so `MAX_CONCURRENT_WORKERS` failures disabled the solver until restart. ([#13097](https://github.com/diegosouzapw/OmniRoute/pull/13097)) — thanks @anhtahaylove +- **fix(acp):** bound the ACP session output buffers — `stdoutBuffer` and `stderrBuffer` now cap at 1 MiB keeping the most recent output behind a visible `[...output truncated...]` marker, and `stderrBuffer` is reset per prompt instead of accumulating for the lifetime of the session. ([#13100](https://github.com/diegosouzapw/OmniRoute/pull/13100)) — thanks @anhtahaylove +- **fix(acp):** release the `stdout`/`exit` listeners and the idle timer that a `sendPrompt` timeout used to leave attached to the `acpManager` singleton, and drop sessions that exited on their own from the session map instead of keeping them forever. ([#13096](https://github.com/diegosouzapw/OmniRoute/pull/13096)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection and PII scanners now read the text a `tool_result` block carries on `content` (string or nested block list), in messages and in system blocks, so tool output is judged by the same rules as user text ([#13101](https://github.com/diegosouzapw/OmniRoute/pull/13101)) — thanks @ntdatt812 +- **fix(gamification):** close the badge notification SSE stream when the request signal is already aborted before the stream starts — a client that disconnects while the route is still awaiting auth used to leave both the 2s unlock poll and the 15s heartbeat running for the lifetime of the process. ([#13106](https://github.com/diegosouzapw/OmniRoute/pull/13106)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection scan now spends its 16 KB budget on both ends of the request instead of the first 16 KB only, so `system`, `instructions`, `query`, `documents` and the newest turns are no longer hidden behind one long message ([#13104](https://github.com/diegosouzapw/OmniRoute/pull/13104)) — thanks @ntdatt812 +- **fix(dashboard):** expose the Volcano Ark console cookie on quota scraping and unwrap connect-error objects so the dashboard shows the upstream message ([#13107](https://github.com/diegosouzapw/OmniRoute/pull/13107)) — thanks @HouMinXi +- **fix(db):** release the `beforeExit`/`SIGINT`/`SIGTERM` handlers when a `node:sqlite` adapter closes, so a closed adapter and its database handle are no longer pinned to `process` for the lifetime of the run — the same treatment #7494 gave the sql.js adapter. ([#13109](https://github.com/diegosouzapw/OmniRoute/pull/13109)) — thanks @anhtahaylove +- **fix(translator):** `contentSchema` and `unevaluatedItems` are now treated as subschema positions by the tool-schema sanitizer, so a truncation placeholder in either is replaced with a permissive schema instead of being forwarded as a string ([#13110](https://github.com/diegosouzapw/OmniRoute/pull/13110)) — thanks @ntdatt812 +- **fix(cli-helper):** clear the `createLogStream` timeout on the abort path — `stop()` aborts the in-flight fetch and returned through the `signal.aborted` branch, which skipped `clearTimeout` and left an armed timer per stopped stream. The stream reader is now also cancelled when the read loop exits early. ([#13114](https://github.com/diegosouzapw/OmniRoute/pull/13114)) — thanks @anhtahaylove +- **fix(docs):** drop TLS-impersonation, MITM-decrypt, supply-chain attestation, and XOR-mask writeups from the public `/docs` catalog and Docker image. Files stay in git for engineers; operators who need them open the repo, not the website. ([#13136](https://github.com/diegosouzapw/OmniRoute/pull/13136)) — thanks @HouMinXi +- **fix(combo):** parse numeric-epoch `rate_limited_until` in the combo cooldown read path ([#13141](https://github.com/diegosouzapw/OmniRoute/pull/13141)) — thanks @maxmad64bis +- **fix(opencode):** both OpenCode plugins now reject a gateway address typed without `http://` at configuration time, instead of publishing every model with an api url no client can call, and the v2 plugin no longer publishes a model card whose api url is blank or relative ([#13142](https://github.com/diegosouzapw/OmniRoute/pull/13142)) — thanks @maxmad64bis +- **fix(providers):** lock opencode model on upstream 400 model-unavailable ([#13146](https://github.com/diegosouzapw/OmniRoute/pull/13146)) — thanks @maxmad64bis +- **fix(logging):** keep the provider exchange rather than the raw client bodies when a call log exceeds its size budget, and show that recovered payload in the request-detail panel instead of replacing it with the stored response body ([#13147](https://github.com/diegosouzapw/OmniRoute/pull/13147)) — thanks @maxmad64bis +- **fix(traffic-inspector):** the WebSocket route no longer leaks a traffic-buffer subscriber and a 30s ping timer when the client socket is already closed at handler time — listeners are attached before any resource is acquired, a destroyed socket bails out early, and the ping interval stops on a dead socket where `write()` never throws ([#13155](https://github.com/diegosouzapw/OmniRoute/pull/13155)) — thanks @anhtahaylove +- **fix(telegram):** bound the per-user API key cache in the Telegram chat proxy so a burst of distinct chat ids can no longer grow the process heap without limit ([#13165](https://github.com/diegosouzapw/OmniRoute/issues/13165)) ([#13166](https://github.com/diegosouzapw/OmniRoute/pull/13166)) — thanks @anhtahaylove +- **fix(stream):** cancel the upstream response body when the JSON-to-SSE sniff unwinds on a body timeout, so a stalled upstream no longer pins the connection ([#13169](https://github.com/diegosouzapw/OmniRoute/issues/13169)) ([#13171](https://github.com/diegosouzapw/OmniRoute/pull/13171)) — thanks @anhtahaylove +- **fix(telegram):** authenticate webhook deliveries with Telegram's `secret_token` so an unauthenticated caller can no longer mint API keys or spend upstream quota ([#13172](https://github.com/diegosouzapw/OmniRoute/issues/13172)) ([#13175](https://github.com/diegosouzapw/OmniRoute/pull/13175)) — thanks @anhtahaylove +- **fix(models):** give discoverable Gemini 3.8 Flash ids their own 65536 output spec so Antigravity no longer clamps them to 16384 ([#13195](https://github.com/diegosouzapw/OmniRoute/pull/13195)) — thanks @HouMinXi +- **fix(dashboard):** leftover catalog-removed provider rows (gemini-cli) can be listed and purged from the providers page ([#13067](https://github.com/diegosouzapw/OmniRoute/issues/13067)) ([#13197](https://github.com/diegosouzapw/OmniRoute/pull/13197)) — thanks @HouMinXi +- **fix(auth):** a dashboard session now requires the `authenticated: true` claim that login, OIDC and the session refresh already emit — a JWT merely signed with `JWT_SECRET` (for example the Cursor CLI passthrough token, which any API-key holder can obtain) no longer verifies as the `auth_token` cookie on any route, the WebSocket handshake or the live server; existing sessions keep working ([#13298](https://github.com/diegosouzapw/OmniRoute/issues/13298)) ([#13375](https://github.com/diegosouzapw/OmniRoute/pull/13375)) +- **fix(api):** `DELETE /v1/batches/delete-completed` now applies the caller's API-key policy (endpoint allowlist, schedule, usage cap, rate limit) like every other `/v1` route, commits the key-scoped sweep per 200-batch chunk instead of holding one write lock for the whole sweep, refuses to loop without progress, and rejects a scope that names both a key and `allTenants` ([#13684](https://github.com/diegosouzapw/OmniRoute/pull/13684)) +- Restore the API-route typecheck gate: the non-streaming leg result lost its discriminated-union narrowing after the server-owned tool loop reassignment, producing 13 new TS2339 diagnostics in `chatCore.ts`. ([#12963](https://github.com/diegosouzapw/OmniRoute/pull/12963)) +- Restore four non-streaming behaviours the server-owned tool loop refactor dropped: client aborts map to 499 with the fixed `Request aborted` message, an aborted request no longer logs a synthetic `clientResponse`, Claude prompt-cache telemetry is recorded again, and a body that cannot be canonicalized no longer throws when the tool loop is off. ([#12990](https://github.com/diegosouzapw/OmniRoute/pull/12990)) +- **fix(skills):** The CLI registry parser now reads positionals declared with `.addArgument()`, not only those written inline in `.command()`. `tunnel create [type]` was being published as `tunnel create`, so the agent-skills sync gate reported drift on every branch and regenerating would have deleted the argument. ([#13009](https://github.com/diegosouzapw/OmniRoute/pull/13009)) — thanks @ntdatt812 +- **fix(combos):** dashboard combo test uses a short prompt, serial probes, and a 60s timeout so reasoning models and rate-limited free pools do not fail the health check ([#13001](https://github.com/diegosouzapw/OmniRoute/pull/13001)) — thanks @HouMinXi +- fix(compression): terminate idle worker threads on eviction so long-running instances stop leaking OS threads and MessagePorts ([#13091](https://github.com/diegosouzapw/OmniRoute/pull/13091)) — thanks @anhtahaylove +- Redact Google API keys of any length in error bodies: the pattern required exactly 39 characters, so shorter or longer `AIza…` credentials passed through unredacted. ([#12964](https://github.com/diegosouzapw/OmniRoute/pull/12964)) +- **fix(grok-cli):** a 402 "Grok Build usage balance exhausted" parks that Grok login as out of credit (Grok Build CLI, grok.com cookie, and xAI OAuth share the weekly pool). Combo routing then tries the next login instead of locking the model for every account in the pool ([#13061](https://github.com/diegosouzapw/OmniRoute/pull/13061)) — thanks @HouMinXi +- fix(compression): spawn the LLMLingua worker with a file URL object so compression actually runs instead of silently failing open on Node ([#13093](https://github.com/diegosouzapw/OmniRoute/pull/13093)) — thanks @anhtahaylove +- **fix(test):** run the local `test` and `test:unit` scripts at concurrency 4 so a full-suite run no longer exhausts the machine's commit charge and kills unrelated processes ([#13187](https://github.com/diegosouzapw/OmniRoute/pull/13187)) — thanks @anhtahaylove +- **fix(monitoring):** `GET /api/monitoring/health` `credentialHealth` now includes a bounded `failedConnections` list (`connectionId`, `status`, sanitized `lastError`) when the probe-cache gauge `failed>0`, plus `source: probe-cache` and a cheap `staleDbNonOkCount` for sticky SQLite `test_status` on active rows. Documents that the live gauge is not `provider_connections.test_status`. ([#12876](https://github.com/diegosouzapw/OmniRoute/pull/12876)) — thanks @RaviTharuma +- **fix(resilience):** an openai-compatible multi-upstream gateway no longer marks the whole connection `credits_exhausted` when a single upstream (or the `/models` / representative-model health probe) returns 402. Quota failures stay model-scoped; true connection-wide auth failures are unchanged. Single-credential 402 key disable ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239)) is preserved. ([#12875](https://github.com/diegosouzapw/OmniRoute/pull/12875)) — thanks @RaviTharuma +- **fix(dashboard):** nine fixes on the Orchestration Canvas, all diagnosed in the Phase 2 reviews. A source that fails now keeps the timestamp of its FIRST failure instead of being re-stamped every poll — the stale line said "since the last poll" no matter how long the source had been down, and the churn also defeated the snapshot's stable identity (it serializes the source list), so the canvas re-rendered on every tick while anything was broken. A source that HAD data and only then started failing is flagged too: previously only a source with no node at all got the warning, so a source that went down mid-session kept a healthy-looking node forever. The rest are pointwise: clicking a filter chip cancels the pending search debounce (left armed, it fired ~300ms later and silently reverted the chip); the search input carries an accessible name; `?state=running, failed` parses like the unpadded form instead of dropping the padded value; the CSV toggle helper is defined once in `model/urlParams.ts` rather than twice; the agents tab tells "nothing running" apart from "the filter matched nothing", offering a clear-filters button instead of setup links that would be wrong advice there; edges stop emitting SMIL particles above 40 simultaneously active edges, keeping the colored stroke; and the drawer's error banner clears when a retried action succeeds. ([#12983](https://github.com/diegosouzapw/OmniRoute/pull/12983)) +- **fix(i18n):** the nine locales added with the EU-language batch (Greek, Estonian, Irish, Croatian, Lithuanian, Latvian, Maltese, Slovenian, Serbian) were missing the eleven Orchestration Canvas keys that Phase 3 introduced, so the compare-runs panel and the "no runs match these filters" empty state fell back to English in those languages (`deepMergeFallback` substitutes English for an absent key, so nothing rendered blank — it rendered untranslated). The coverage gate does not catch this: it enforces an 80% floor per locale, and eleven missing keys out of ~13,000 leaves coverage at 99.9%. Translated for real in each language, calibrated against the wording each file already uses for "run", "filter" and "skill". ([#13294](https://github.com/diegosouzapw/OmniRoute/pull/13294)) +- **fix(dashboard):** five follow-ups from the Phase 2 Orchestration Canvas review. The History drawer now shows the memory section for runs that already left the live TTL window: the persisted `memory_hits` event is parsed into `metadata.memoryHits` with the same defensive validation the drawer applies, and — because that event is observability rather than a state transition — it no longer leaks into the timeline, where it had been inheriting the task's state and rendering as a duplicate transition. The A2A memory recall runs against its own 1.5s deadline instead of inheriting the memory backend's 30s one; overshooting degrades exactly like any other recall failure (no hits, task proceeds), and the timer is cleared on both paths. Repeating a Conductor run carries its `requirements.cli`/`requirements.model` forward, so the new run is pinned to the same runner profile and model rather than drifting to whatever the fleet picks. A successful repeat from the Agents tab now focuses the run it created, instead of leaving the operator on the finished one. And the auth test for `POST /api/conductor/tasks` moved into the shared `ROUTES` array rather than restating the pattern. ([#12988](https://github.com/diegosouzapw/OmniRoute/pull/12988)) +- fix(plugins): stop leaking an exit listener per plugin hook timeout, which triggered MaxListenersExceededWarning on plugins that ignore SIGTERM ([#13092](https://github.com/diegosouzapw/OmniRoute/pull/13092)) — thanks @anhtahaylove +- **fix(validation):** Provider node edits no longer fail with a generic "Invalid request" when the optional daily-quota reset fields are left blank. The dashboard sends `dailyQuotaResetTimezone` and `dailyQuotaResetHour` as `null`, and only the hour accepted it. ([#13066](https://github.com/diegosouzapw/OmniRoute/issues/13066)) ([#13083](https://github.com/diegosouzapw/OmniRoute/pull/13083)) — thanks @ntdatt812 +- **fix(combo):** quota-aware expansion drops banned, inactive, missing, and wrong-provider connections before quota fetch or model dispatch; pins and allowlists stay selectors, not a bypass. Antigravity automatic exhaustion now requires a reported zero remaining, so a positive balance below 1% stays eligible. ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- Fix an intermittent failure in the quota-weighted routing test suite: scores are a function of `Date.now()`, so peers with identical quota scored microseconds apart never tied and swapped order. ([#12946](https://github.com/diegosouzapw/OmniRoute/pull/12946)) +- fix(volcengine): resolve quota window mapping gaps, safe multi-connection console binding, and response cookie sanitization ([#12950](https://github.com/diegosouzapw/OmniRoute/pull/12950)) — thanks @HouMinXi +- **fix(test):** resolve the WebDAV handler path with `fileURLToPath` so the suite's 37 WebDAV tests run on Windows instead of failing with a doubled `C:\C:\` drive prefix ([#13196](https://github.com/diegosouzapw/OmniRoute/pull/13196)) — thanks @anhtahaylove +- **fix(ui):** fall back from invalid AnySearch icon alias (direct commit `9587e07b69`) +- **fix(sse):** stop rebuilding a truncated summary from the collector's cap-dropped event array ([#12718](https://github.com/diegosouzapw/OmniRoute/pull/12718)) — thanks @hartmark +- **fix(conversations):** show a pending spinner for unresolved tool nodes instead of "(empty)" ([#12727](https://github.com/diegosouzapw/OmniRoute/pull/12727)) — thanks @hartmark +- **fix(streaming):** fail fast when an upstream stream produces only lifecycle/heartbeat events, never real content ([#12741](https://github.com/diegosouzapw/OmniRoute/pull/12741)) — thanks @hartmark +- **fix(responses-continuation):** bridge the write-in-flight window with an in-memory pending store ([#12854](https://github.com/diegosouzapw/OmniRoute/pull/12854)) — thanks @hartmark +- **fix(translator):** drop or map agent_message on Chat Completions fallback ([#12880](https://github.com/diegosouzapw/OmniRoute/pull/12880)) — thanks @RaviTharuma +- **fix(api):** page and stream-complete GET /v1/models for large catalogs ([#12882](https://github.com/diegosouzapw/OmniRoute/pull/12882)) — thanks @RaviTharuma +- **fix(quota):** align AUTH window labels with usage API and clarify 503 ([#12884](https://github.com/diegosouzapw/OmniRoute/pull/12884)) — thanks @RaviTharuma +- **fix(ci):** clear two base-reds on release/v3.8.51 — mutation-coverage gate + image-only-model guard ([#12945](https://github.com/diegosouzapw/OmniRoute/pull/12945)) +- **fix(api):** scope batch bulk-delete to the calling API key ([#13211](https://github.com/diegosouzapw/OmniRoute/pull/13211)) +- **fix(tests):** align model-sync log assertion to shipped path redaction (base-red) ([#13227](https://github.com/diegosouzapw/OmniRoute/pull/13227)) +- **fix(test):** make the ToS heading guard actually require the parentheses ([#13228](https://github.com/diegosouzapw/OmniRoute/pull/13228)) +- **fix(quality):** register 4 drifted covering tests in stryker tap.testFiles ([#13229](https://github.com/diegosouzapw/OmniRoute/pull/13229)) +- **fix(logging):** mask *-api-key headers in request-log pipeline (#13273) ([#13401](https://github.com/diegosouzapw/OmniRoute/pull/13401)) — thanks @KooshaPari +- **fix(tests):** correct stale Jina catalog prefix in custom-models test ([#13403](https://github.com/diegosouzapw/OmniRoute/pull/13403)) — thanks @KooshaPari +- **fix(backend):** prune old backups after health-check-repair snapshots (#13308) ([#13404](https://github.com/diegosouzapw/OmniRoute/pull/13404)) — thanks @KooshaPari +- **fix(backend):** guard memory_fts_au trigger with WHEN clause to stop FTS bloat (#13326) ([#13405](https://github.com/diegosouzapw/OmniRoute/pull/13405)) — thanks @KooshaPari +- **fix(free-tiers):** remove obsolete Together $25 signup credit (#12526) ([#13407](https://github.com/diegosouzapw/OmniRoute/pull/13407)) — thanks @KooshaPari +- **fix:** skip non-chat generation models in Test all models ([#13409](https://github.com/diegosouzapw/OmniRoute/pull/13409)) — thanks @KooshaPari +- **fix(providers):** de-duplicate agy and antigravity model catalogs ([#13410](https://github.com/diegosouzapw/OmniRoute/pull/13410)) — thanks @KooshaPari +- **fix(dashboard):** send provider in body for mini-playground web search ([#13411](https://github.com/diegosouzapw/OmniRoute/pull/13411)) — thanks @KooshaPari +- **fix(resilience):** add CJK quota-exhaustion patterns to 429 classifier ([#13412](https://github.com/diegosouzapw/OmniRoute/pull/13412)) — thanks @KooshaPari +- **fix(backend):** eval runner: failed calls no longer score as passed + dotAll regex ([#13413](https://github.com/diegosouzapw/OmniRoute/pull/13413)) — thanks @KooshaPari +- **fix(backend):** eval runner opts out of output-style and memory injection ([#13414](https://github.com/diegosouzapw/OmniRoute/pull/13414)) — thanks @KooshaPari +- **fix:** chain serial quarantine step in npm test script (fixes #13286) ([#13418](https://github.com/diegosouzapw/OmniRoute/pull/13418)) — thanks @KooshaPari +- **fix(antigravity):** derive stable session ID from credentials for prompt-cache reuse (#13305) ([#13424](https://github.com/diegosouzapw/OmniRoute/pull/13424)) — thanks @KooshaPari +- **fix(memory):** merge memory into top-level system field instead of unshifting at messages[0] (#13425) ([#13427](https://github.com/diegosouzapw/OmniRoute/pull/13427)) — thanks @KooshaPari +- **fix(compression):** preserve instruction blocks from lossy engines ([#13523](https://github.com/diegosouzapw/OmniRoute/pull/13523)) — thanks @KooshaPari +- **fix(compression):** preserve polarity words and newlines in ultra heuristic ([#13524](https://github.com/diegosouzapw/OmniRoute/pull/13524)) — thanks @KooshaPari +- **fix(compression):** run user preservePatterns before built-in patterns (#13457) ([#13525](https://github.com/diegosouzapw/OmniRoute/pull/13525)) — thanks @KooshaPari +- **fix(reasoning):** expand deepseek-v4 regex to match v4.1+ and add command-code provider (#13430) ([#13528](https://github.com/diegosouzapw/OmniRoute/pull/13528)) — thanks @KooshaPari +- **fix(providers):** add alwaysReasons metadata for reka-flash-3 ([#13531](https://github.com/diegosouzapw/OmniRoute/pull/13531)) — thanks @KooshaPari +- **fix(playground):** allow Compare columns to scroll independently ([#13532](https://github.com/diegosouzapw/OmniRoute/pull/13532)) — thanks @KooshaPari +- **fix(security):** empty whitelist allows through to prevent admin lockout ([#13534](https://github.com/diegosouzapw/OmniRoute/pull/13534)) — thanks @KooshaPari +- **fix(tests):** add DISABLE_IOREG_STRATEGY env var for macOS test compatibility (fixes #13467) ([#13539](https://github.com/diegosouzapw/OmniRoute/pull/13539)) — thanks @KooshaPari +- **fix(zed-hosted):** map developer role and Gemini enums for Zed proxy ([#13543](https://github.com/diegosouzapw/OmniRoute/pull/13543)) — thanks @KooshaPari +- **fix(analytics):** normalize provider aliases to canonical id in usage_history ([#13545](https://github.com/diegosouzapw/OmniRoute/pull/13545)) — thanks @KooshaPari +- **fix(combo):** use traceId for call log id to prevent UNIQUE constraint failure ([#13546](https://github.com/diegosouzapw/OmniRoute/pull/13546)) — thanks @KooshaPari +- **fix(auth):** check model lockout before returning synthetic noauth connection ([#13547](https://github.com/diegosouzapw/OmniRoute/pull/13547)) — thanks @KooshaPari +- **fix(guardrails):** skip credential redaction for base64 image data URLs ([#13550](https://github.com/diegosouzapw/OmniRoute/pull/13550)) — thanks @KooshaPari +- **fix(resilience):** increase requestQueue.maxWaitMs default from 15s to 30s ([#13553](https://github.com/diegosouzapw/OmniRoute/pull/13553)) — thanks @KooshaPari +- **fix(docs):** document OMNIROUTE_READY_TIMEOUT_MS and allowlist the test-only DISABLE_IOREG_STRATEGY ([#13692](https://github.com/diegosouzapw/OmniRoute/pull/13692)) + ### 📝 Maintenance ---- +- **docs(readme):** reconcile live v3.8.50 provider, free-tier, CLI, routing, test, community, sponsor, acknowledgment, and SVG metrics with their audited source denominators, including a deduplicated OmniRoute-in-Action snapshot and distinct contributor rankings for merged pull requests, GitHub-attributed commits, and Git history ([#11356](https://github.com/diegosouzapw/OmniRoute/pull/11356)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **docs:** sync the canonical provider count 354 → 356 across `README.md`, `AGENTS.md`, `llm.txt` (+ 42 i18n mirrors), the four README SVG diagrams, `docs/reference/PROVIDER_REFERENCE.md` (regenerated) and the `package.json` description after Opper (#11629) and 1min.ai (#11631) boarded the catalog — closes the `check:docs-counts-sync` strict drifts that kept `release/v3.8.51` red ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11673](https://github.com/diegosouzapw/OmniRoute/pull/11673)) — thanks @jonlwheat2-gif +- **test(providers):** update count-derived assertions after the v3.8.51 provider additions — `APIKEY_PROVIDERS` 233 → 235 (Opper #11629 + 1min.ai #11631), reserved-prefix REGISTRY walk 395 → 398, `WEB_FETCH_PROVIDERS` now includes `nimble-search` (#11620), and the provider translate-path golden snapshot regenerated ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11674](https://github.com/diegosouzapw/OmniRoute/pull/11674)) — thanks @jonlwheat2-gif +- **test(sse):** bump the hard-lease connection-query inventory for `src/lib/tokenHealthCheck.ts` to 2 — the verify-only web-cookie sweep added by #11495 split the single `getProviderConnections` call into oauth + cookie variants, which the frozen inventory had not tracked ([#11495](https://github.com/diegosouzapw/OmniRoute/pull/11495)). ([#11677](https://github.com/diegosouzapw/OmniRoute/pull/11677)) — thanks @jonlwheat2-gif / @oyi77 +- **chore(release):** clear the `release/v3.8.51` base-red gates — docs migration count (+38 i18n mirrors), stryker `tap.testFiles`, lockfile registry host, stale ESLint suppressions, the 7 lint regressions they were hiding, the stale `APIKEY_PROVIDERS` partition count (231 → 233), the `glm-5.3-max` tier missing from the GLM test table, a dead `modelType === "chat"` comparison in the models catalog, and the `resolveLiveWsUrl` / `sanitizeLiveWsPort` helpers from #11388 that a later merge dropped from `wsPath.ts` (dashboard live WebSocket URL resolution) ([#11502](https://github.com/diegosouzapw/OmniRoute/pull/11502)) — thanks @MumuTW +- **chore(free-models):** the free-tier regime table now declares which totals figure each regime feeds and whether it may take the no-auth shortcut, so the sets that used to repeat those answers by hand are derived from it — a new `freeType` no longer compiles until it has answered every question, instead of silently contributing to no total ([#11537](https://github.com/diegosouzapw/OmniRoute/pull/11537)) — thanks @maxmad64bis +- **docs(routing):** LKGP strategy copy now states provider-level stickiness and the explicit rules fallback instead of promising per-target pinning ([#11555](https://github.com/diegosouzapw/OmniRoute/pull/11555)) — thanks @maxmad64bis +- **test(chatcore):** pin the response tool-name alias resolution against the ordering hazard that broke every Gemini/Antigravity MCP tool call in v3.8.49. `extractRequestToolIdentityMap` deletes `translatedBody._toolNameMap`, so the later read is always undefined and the ledger survives only through the `requestToolIdentityMap` fallback — removing that fallback previously left the entire tool-name suite green. The resolution moves into `resolveResponseToolNameMap()` next to the map it depends on, with a regression guard that fails without the recovery. No behaviour change. ([#11575](https://github.com/diegosouzapw/OmniRoute/pull/11575)) — thanks @amartinawi +- **docs(env):** document `OMNIROUTE_CORPUS_CACHE_SIZE` in `.env.example` and `ENVIRONMENT.md`, so the env/docs contract check stops failing Docs Gates on every branch ([#11576](https://github.com/diegosouzapw/OmniRoute/pull/11576)) — thanks @ntdatt812 +- **chore(stryker):** register `route-guard-tunnel-processes-local-only` and `universal-quota-aware-routing` in `tap.testFiles`, so their mutant kills count for the three modules they cover ([#11577](https://github.com/diegosouzapw/OmniRoute/pull/11577)) — thanks @ntdatt812 +- **test(authz):** pin `LOCAL_ONLY_API_GET_EXEMPTIONS` by exact membership instead of by entry count, so the guard names the offending path and also catches a substitution ([#11580](https://github.com/diegosouzapw/OmniRoute/pull/11580)) — thanks @ntdatt812 +- **test(build):** pin `resolveNextBuildBundlerFlag` to the `OMNIROUTE_USE_TURBOPACK` contract it implements, across runtimes, instead of asserting an unimplemented Bun override ([#11583](https://github.com/diegosouzapw/OmniRoute/pull/11583)) — thanks @ntdatt812 +- **test(claude-web):** the Claude Web transport test's barrel anchors no longer pin the exact construction spelling. #11421 made the executor map lazy, so `/"claude-web": new ClaudeWebExecutor\(\)/` stopped matching and the test went red on `release/v3.8.51` itself — a base-red gate every open PR inherited. The anchors now match the alias key and the class name and tolerate either spelling in between, and were checked to still fail when an alias is genuinely repointed at another executor ([#11585](https://github.com/diegosouzapw/OmniRoute/pull/11585)) — thanks @ntdat812 +- **test(models):** pin `qwen3.8-max` resolution per provider — two of the four catalogs now serve the bare id and no longer carry `-preview` ([#11590](https://github.com/diegosouzapw/OmniRoute/pull/11590)) — thanks @ntdatt812 +- **test(executors):** drop the stale 128-tool truncation assertion that contradicted the #11444 pass-through contract ([#11591](https://github.com/diegosouzapw/OmniRoute/pull/11591)) — thanks @ntdatt812 +- **test(token-health):** pin the current terminal-skip boundary — expired with retry budget is probed, `account_deactivated` stays skipped ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)) — thanks @ntdatt812 +- **test(models):** follow #11503's retarget of the `gemini-3-pro-high` alias to the hyphenated catalog id ([#11593](https://github.com/diegosouzapw/OmniRoute/pull/11593)) — thanks @ntdatt812 +- **refactor(video bridge):** extract per-video acquisition, whole-result caching, description, and metrics/abort/cleanup out of `VideoBridgeGuardrail.preCall` into a `processVideoPart` seam in a new `videoBridgePipeline.ts`, behind explicit `VideoMediaBrokerPort`, `VideoAudioTranscriptionPort`, and `VideoDrilldownPort` boundaries; `preCall` now only handles request traversal, policy, and response aggregation. The Video tab's FFmpeg/ffprobe runtime status is now an explicit `unknown` / `restricted` / `unavailable` / `available` state instead of a nullable boolean pair, fixing a case where an in-flight or failed probe was mislabeled as "install FFmpeg" ([#11657](https://github.com/diegosouzapw/OmniRoute/issues/11657)). ([#12016](https://github.com/diegosouzapw/OmniRoute/pull/12016)) +- **test(opencode-plugin):** add unit test coverage for `formatFreeBudget()` naming helper ([#11660](https://github.com/diegosouzapw/OmniRoute/pull/11660)) — thanks @f9td56dbgh-hub +- **docs(video):** clarify that the Video Bridge transcript `source` field (`client`, `embedded`, `audio-bridge`) is presently caller-declared and not yet server-verified — OmniRoute enforces the enum shape but does not cryptographically confirm that an `embedded`/`audio-bridge` label came from a server-owned extraction ([#11661](https://github.com/diegosouzapw/OmniRoute/issues/11661)). ([#12001](https://github.com/diegosouzapw/OmniRoute/pull/12001)) +- **chore(providers):** retire the distributed Raycast Relay and Hailuo Web integrations after a provenance audit confirmed substantial derivation from GPL-3.0 sources; the official GitHub and MiniMax API providers remain available. Migration 163 disables legacy connections and invalidates their active leases while preserving connection IDs, audit/history rows, quota snapshots, usage records, and fail-closed API-key connection policies. ([#11691](https://github.com/diegosouzapw/OmniRoute/pull/11691)) +- **chore(providers):** retire the Felo Web provider while its GPL-derived provenance remains on licensing HOLD ([#11698](https://github.com/diegosouzapw/OmniRoute/pull/11698)) — stale runtime and database references now fail closed while existing connection, lease, and usage history remains preserved for auditability. +- **fix(sse):** retire the Gemini Web image-generation bridge while provenance and licensing remain on HOLD, preserving Gemini Web chat/auth/tools and legitimate Gemini/Nano Banana image providers ([#11708](https://github.com/diegosouzapw/OmniRoute/pull/11708)) +- **chore(audio):** retire the built-in EdgeTTS provider and its 11-voice catalog while provenance/licensing review remains on HOLD; the generic `/v1/audio/speech` endpoint and the other speech providers remain available ([#11711](https://github.com/diegosouzapw/OmniRoute/pull/11711)) +- **fix(providers):** retire the unofficial Qwen Web integration and its `qwen-web`/`qw` identities while third-party provenance remains on HOLD ([#11713](https://github.com/diegosouzapw/OmniRoute/pull/11713)) — direct requests now fail closed with a sanitized HTTP 410 and combos skip the retired target, while migration guards preserve connection IDs, allowlists, usage/audit history and invalidate active leases. Supported Qwen API, Qwen Code, cloud, token-plan, audio, local and model-name surfaces remain available; restoring Qwen Web requires a qualified provenance/license decision. +- **fix(providers):** retire `microsoft-designer-web` and its `msdesigner` alias from runtime and catalogs while preserving stored credentials, provider history and allowlists as inactive records — source dependency on gpt4free's [`MicrosoftDesigner.py`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/g4f/Provider/needs_auth/MicrosoftDesigner.py) is proven at snapshot `0943d16eef757a44deb60651c07bbe5fa958cafe`, whose [`LICENSE`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/LICENSE) is GPL-3.0; expressive adaptation is probable, which records provenance/licensing risk without asserting a legal conclusion ([#11720](https://github.com/diegosouzapw/OmniRoute/pull/11720)). +- **docs(legal):** centralize pinned MIT notices for the vendored GCF codec and local flag assets ([#11726](https://github.com/diegosouzapw/OmniRoute/pull/11726)) +- Split the 2,952-line `adobeFireflyClient.ts` into nine focused modules (catalog, credentials, payload, ARP session, upload, responses, IMS, account, poll) behind an unchanged barrel — no consumer edits and no public API change (#11728). — thanks @MumuTW +- **fix(dashboard):** replaced the Model Lockout card's unprovenanced notification MP3 with a short, locally synthesized Web Audio chime ([#11731](https://github.com/diegosouzapw/OmniRoute/pull/11731)) — both toggles retain optional audible feedback without shipping replacement media or third-party code, and unsupported, suspended, or rejected audio contexts fail open without blocking the setting change. +- **chore(deepseek-web):** remove the provenance-unresolved DeepSeek PoW WASM binary and its runtime loader/tracing while retaining the existing JavaScript solver (slower at high difficulty) ([#11732](https://github.com/diegosouzapw/OmniRoute/pull/11732)). +- **chore(assets):** remove four unused provider assets whose redistribution provenance could not be verified while retaining the active Freebuff SVG ([#11735](https://github.com/diegosouzapw/OmniRoute/pull/11735)) +- **docs(assets):** retired two raster images whose redistribution provenance could not be verified ([#11736](https://github.com/diegosouzapw/OmniRoute/pull/11736)) — PicoClaw documentation now reuses the existing SVG, while Zoo Code remains a text link without an image. +- **docs(legal):** add pinned notices for six LobeHub-derived provider SVGs and 65 byte-exact theSVG assets, retaining registry claims as unverified metadata with explicit trademark and HOLD boundaries ([#11737](https://github.com/diegosouzapw/OmniRoute/pull/11737)). +- **chore(quality):** added a versioned 225/225 provider-asset provenance manifest and blocking drift gate ([#11743](https://github.com/diegosouzapw/OmniRoute/pull/11743)) — the inventory pins magic MIME and SHA-256 for every physical file, records immutable source matches for 71 assets, keeps 69 probable and 85 unresolved entries explicit but non-blocking, and treats provenance as source evidence rather than copyright or trademark clearance. +- **refactor(mitm):** replace four previously derived Traffic Inspector utilities — conversation normalization, SSE merging, secret masking, and header sanitization — with independent clean-room implementations based on public provider schemas, WHATWG SSE, and RFC 6750, while keeping the two independently classified passthrough surfaces unchanged ([#11748](https://github.com/diegosouzapw/OmniRoute/pull/11748)). +- Added a unit test that fails seven days before any dated pack under `config/` (`validUntil` and sibling keys) lapses, naming the file and key. The Alibaba free-tier pack expired on 2026-08-27 and turned every PR red the next morning with no commit involved; renewal now happens on someone's terms, not the clock's. ([#11891](https://github.com/diegosouzapw/OmniRoute/pull/11891)) +- `check:workflows` now fails (under `--strict`/`--ratchet`) when any job routed to a self-hosted runner publishes with `--provenance` — npm rejects that with `422` at the registry, which in v3.8.50 only surfaced after the tag and Docker images were public. ([#11895](https://github.com/diegosouzapw/OmniRoute/pull/11895)) +- `scripts/ops/runner-janitor.sh` now proves a path is idle with one `lsof` snapshot and removes stale leftovers itself (tmpfs after 3 h — it is RAM — disk after 24 h), kills orphan `next-build` processes, prunes checkouts of stopped runners, and alerts on memory pressure; `--dry-run` shows exactly what it would do. `docs/ops/RUNNER_BOX.md` reconciled to the measured box (31 GB, 10 listeners). ([#11893](https://github.com/diegosouzapw/OmniRoute/pull/11893)) +- The `next-build` artefact (1.3 GB) is now written and read under `$RUNNER_TEMP` (per-runner, on disk) instead of `/tmp`, which on the self-hosted pool is a 12 GB tmpfs in RAM. Landing it there took 27–32 of the publish job's 76 minutes, and the fixed `/tmp/e2e-build.tar.gz` name let E2E jobs on different runners overwrite each other's download. ([#11896](https://github.com/diegosouzapw/OmniRoute/pull/11896)) +- The CI `build` job now runs in two concurrency lanes — `main` and pull requests — so a release build is never queued behind (or OOM-killed beside) PR builds on the self-hosted pool, which holds one `next-build` comfortably and two at the edge. ([#11901](https://github.com/diegosouzapw/OmniRoute/pull/11901)) +- Re-freeze the ESLint suppressions on `release/v3.8.51` from a clean-room measurement (2 stale file entries pruned, 55 pre-existing `no-explicit-any` in six new files frozen under #11924) and drop the dead `GPT_SIZE_MAP` constant orphaned by the Adobe Firefly client split, so `No new ESLint warnings` stops failing every PR with exit 2 (Refs #11924) ([#11955](https://github.com/diegosouzapw/OmniRoute/pull/11955)) +- Type the 55 `no-explicit-any` sites that had been frozen under #11924 — four redundant casts in `socksConnectorWithFamily.ts` (undici/socks types already accept them) and the mocks/fixtures of the socks-timeout and isFree suites — and drop their suppression entries; the ESLint ratchet shrinks from 5487 to 5432 (Closes #11924) ([#11975](https://github.com/diegosouzapw/OmniRoute/pull/11975)) +- Take the two hosted-runner builds off the PR rail: `Build App` (`build.yml`) is `workflow_dispatch`-only and `DAST smoke (PR)` runs only for PRs into `main` — the 7 GB hosted VM cannot build `release/v3.8.51` in any profile (19/30 red, VM shutdown ~8 min into `next build`) and both checks had turned into permanent noise on every release PR; the bundle stays validated by `ci.yml` on `main` and by `nightly-release-green` on `release/**` (Closes #11946) ([#11962](https://github.com/diegosouzapw/OmniRoute/pull/11962)) +- Move the four nightly jobs that build the backend (`nightly-schemathesis`, `nightly-llm-security` promptfoo + garak, `nightly-resilience` axe-a11y) off the hosted 7 GB runner — where they died on `release/v3.8.51` unseen — onto the box's new `omni-light` pool (two listeners), and document the reshaped fleet (4 active OmniRoute listeners: 2 `omni-build` + 2 `omni-light`, janitor ceiling 4) (Closes #11965) ([#11967](https://github.com/diegosouzapw/OmniRoute/pull/11967)) +- Stop painting every fork PR into `release/**` red: `quality.yml` `Build (advisory)` is skipped (GitHub still reports `continue-on-error` failures as check FAILURE). Hosted `ubuntu-latest` cannot finish `npm run build` on this tree — same class as #11962 taking `build.yml` off the PR rail. `docker-publish.yml` amd64 now runs on the `.113` `omni-build` pool (31 GB / 32 cores, two listeners) with Turbopack, shares the `heavy-build-main` lane with `ci.yml` `Build` so it queues instead of becoming a third heavy, and keeps per-ref concurrency (a merge storm was starting 8 concurrent OOM builds). arm64 stays on `ubuntu-24.04-arm` with webpack — there is no ARM box. Fallback when `USE_VPS_RUNNER` is off: hosted amd64 + webpack (#11976). ([#12021](https://github.com/diegosouzapw/OmniRoute/pull/12021)) +- `docker-publish.yml` builds with webpack (`OMNIROUTE_USE_TURBOPACK=0`) on **both** arches, including amd64 on the `.113` `omni-build` pool. Turbopack had the RAM (31 GB, `omniroute-113-6`) and still panicked (`TurbopackInternalError: there must be a path to a root`, run 33253576569, 26 min in). The same tree's arm64 webpack build on hosted `ubuntu-24.04-arm` succeeded (run 33264823398). Dockerfile already documents webpack as the Docker escape hatch (#11976). ([#12050](https://github.com/diegosouzapw/OmniRoute/pull/12050)) +- **docs(free-tier):** declare the counting vs deciding regimes for "is it free?" and guard the deciding path from DB-backed catalog resolution ([#12226](https://github.com/diegosouzapw/OmniRoute/pull/12226)) — thanks @maxmad64bis +- **docs(auto-combo):** The mode pack table in `docs/routing/AUTO-COMBO.md` now lists all six shipped packs with every weight each one sets, replacing a four-pack table whose numbers had also drifted from the source. It states plainly that no pack sets `quality`, so selecting any pack silences the observed-quality signal. Six more documents that quote the scoring factor count joined the `check:docs-counts` gate, which caught five stale claims — including one naming nine factors that do not exist — and two stale mode pack counts. The dashboard routing panel, which offered four of the six packs and labelled the default strategy "6-Factor Scoring", is now covered by a test; the two packs it was missing are `reliability-first` and `chaos-mode`, the latter labelled as the fault-injection profile it is rather than as one more routing preference ([#12316](https://github.com/diegosouzapw/OmniRoute/pull/12316)) — thanks @maxmad64bis +- **chore(quality):** rebaseline `src/lib/db/apiKeys.ts` for the ACL the key-creation path now preserves ([#12352](https://github.com/diegosouzapw/OmniRoute/pull/12352)) ([#12673](https://github.com/diegosouzapw/OmniRoute/pull/12673)) — thanks @KrzysiekSko +- **chore(providers):** bump the Claude Code wire identity and the Devin bridge image pin from `2.1.220` to `2.1.258` ([#12402](https://github.com/diegosouzapw/OmniRoute/pull/12402)) — thanks @ggiak ([#12604](https://github.com/diegosouzapw/OmniRoute/pull/12604)) +- **docs(env):** align `.env.example`, the README Bun section, and the troubleshooting guide with the code: `OMNIROUTE_USE_TURBOPACK` also governs `npm run build` (not dev-only), `bun run build` follows that flag instead of auto-selecting Webpack, `OMNIROUTE_CHAT_MAX_HEAVY_IN_FLIGHT` is unset by default (no request-count cap), and the structural `503 chat_admission_busy` message matches `chatAdmissionResponses.ts` (#12404 — thanks @pacocartones) +- **chore(electron):** upgrade the desktop app to Electron 44 (Chromium 152, Node 24.18.1) ([#12217](https://github.com/diegosouzapw/OmniRoute/pull/12217)). **Requires macOS 13 (Ventura) or later** — Chromium dropped macOS 12 (Monterey), so Monterey users must stay on an earlier OmniRoute desktop build. Windows and Linux are unaffected; the app already shipped only x64/arm64, so Electron 44 dropping 32-bit builds changes nothing. Removes the `openAsHidden`/`wasOpenedAsHidden` login-item fields deleted in Electron 44 — hidden autostart continues to work through the `--hidden` argument registered with the login item ([#12554](https://github.com/diegosouzapw/OmniRoute/pull/12554)) — thanks @app/dependabot +- **chore(quality):** rebaseline `src/sse/handlers/chat.ts` for the effective-input persistence the continuation fix needs ([#12641](https://github.com/diegosouzapw/OmniRoute/pull/12641)) ([#12680](https://github.com/diegosouzapw/OmniRoute/pull/12680)) — thanks @hartmark +- `Coverage` job on `ci.yml`: the informational Codecov upload gets its own 5-minute ceiling and `continue-on-error`, and the job budget grows from 20 to 30 minutes (the 8-shard c8 merge alone takes ~10) — a stalled upload no longer ends the job `cancelled` and drags a fully green `main` run's conclusion down with it ([#11972](https://github.com/diegosouzapw/OmniRoute/pull/11972)) +- CI hardening on the PR rail: the four `Unit Tests fast-path` shards get `timeout-minutes: 30` (a hung shard held a PR for 64 min instead of GitHub's 6 h default) and both ESLint file caches lose their `restore-keys` fallback, so a cache built under another suppressions file or lint config can no longer report stale verdicts (Refs #11600, #11924) ([#11963](https://github.com/diegosouzapw/OmniRoute/pull/11963)) +- Every CI job that runs a `next build` (`build`, the npm `publish`, both release-green validations) now targets the `omni-build` runner label, which only two of the eight self-hosted runners carry. The box holds one build comfortably and two at the edge; a third now queues on GitHub instead of being OOM-killed by the kernel. ([#11932](https://github.com/diegosouzapw/OmniRoute/pull/11932)) +- **refactor(combo):** move `handleRoundRobinCombo` (and `resolveTargetTokenLimit`) into `open-sse/services/combo/roundRobinCombo.ts`. `combo.ts` drops from 2164 to 1014 lines (`split("\n").length`); the leaf is 1199 (under the 1200 new-file cap). The round-robin call site uses a dynamic `import()` so `releaseStickyPinOnFailure` / `clearStaleLKGP` can stay exported from `combo.ts` without a static cycle. Skip / sticky / semaphore / safety-timer behavior is unchanged. ([#12811](https://github.com/diegosouzapw/OmniRoute/pull/12811)) — thanks @HouMinXi +- **chore(quality):** rebaseline the file-size caps the error-boundary campaign grew past (`open-sse/executors/codex.ts`, `open-sse/vendor/codex-chatgpt-web/bridge.ts`, both via [#12444](https://github.com/diegosouzapw/OmniRoute/pull/12444)) ([#12654](https://github.com/diegosouzapw/OmniRoute/pull/12654)) +- **chore(quality):** rebaseline the file-size caps the hartmark batch grew past (`combos/page.tsx` via [#12355](https://github.com/diegosouzapw/OmniRoute/pull/12355), `open-sse/services/combo.ts` via [#12338](https://github.com/diegosouzapw/OmniRoute/pull/12338)) ([#12623](https://github.com/diegosouzapw/OmniRoute/pull/12623)) — thanks @hartmark +- **chore(quality):** rebaseline the file-size caps the HouMinXi batch grew past when its PRs stacked (`providers/page.tsx`, `chatCore.ts`, `accountFallback.ts`) — each PR measured correctly in isolation, none saw the stacking ([#12619](https://github.com/diegosouzapw/OmniRoute/pull/12619)) +- **chore(quality):** rebaseline `open-sse/services/combo.ts` for the reset-aware scoring the HouMinXi batch stacked ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) ([#12678](https://github.com/diegosouzapw/OmniRoute/pull/12678)) — thanks @HouMinXi +- **docs:** sync provider catalog count (direct commit `4f11b2ae3d`) +- **docs(skills):** sync omni-inference search fallback (direct commit `b4e76a7ed9`) +- **chore(lint):** prune two stale base suppressions (direct commit `1325047d56`) +- **test(auto-combo):** tolerate full-suite rotation load (direct commit `ad3e293f9f`) +- **test:** drain inherited quality snapshot reds (direct commit `21ed68d8ac`) +- **chore(quality):** rebaseline measured CLI bundle growth (direct commit `d35b3f9779`) +- **test(ui):** reproduce invalid AnySearch icon alias (direct commit `12cc6ca834`) +- **test(translator):** fix the relative imports of the relocated deferred-finish test (direct commit `777d9d1629`) +- **docs(counts):** bring the documented provider and migration counts to the measured values (direct commit `e70bea30e9`) +- **test(security):** drop the guard-target set left unused by the sync (direct commit `a32ba0e362`) +- **chore(db):** renumber model_capabilities migration 163 → 169 (cross-branch collision with 163_radar_feed_cache_generated_at) (direct commit `70f5d4cbf9`) +- **docs(cli):** document CLI_PRIME_AGENT_BIN and correct the CLI Agents count (direct commit `9058e39b61`) +- **chore(deps):** refresh runtimes and adopt ESLint 10 ([#11259](https://github.com/diegosouzapw/OmniRoute/pull/11259)) — thanks @backryun +- **refactor(local-corpus):** implement dynamic root resolution and LRU cache ([#11491](https://github.com/diegosouzapw/OmniRoute/pull/11491)) — thanks @raheemuddin786 +- **chore(quality):** prune stale eslint-suppressions entries ([#11596](https://github.com/diegosouzapw/OmniRoute/pull/11596)) +- **chore(ci):** unblock quality gates - env-doc sync + stryker tap.testFiles registration (#9282) ([#11609](https://github.com/diegosouzapw/OmniRoute/pull/11609)) — thanks @Meet6338-X +- **chore(ci):** point the circular-deps gate at dpdm's real JS entrypoint ([#11615](https://github.com/diegosouzapw/OmniRoute/pull/11615)) — thanks @benzntech +- **chore(quality):** rebaseline file-size for #11448/#11495/#11561 growth ([#11630](https://github.com/diegosouzapw/OmniRoute/pull/11630)) +- **deps:** realign onnxruntime-node pin to @huggingface/transformers' 1.24.3 lockstep ([#11633](https://github.com/diegosouzapw/OmniRoute/pull/11633)) — thanks @jonlwheat2-gif +- **test(autoCombo):** port models_dev_tier #11508 guard from node:test to vitest ([#11635](https://github.com/diegosouzapw/OmniRoute/pull/11635)) — thanks @jonlwheat2-gif +- **chore(quality):** rebaseline file-size for #11083/#11631/#11628 growth ([#11636](https://github.com/diegosouzapw/OmniRoute/pull/11636)) +- **chore(lint):** prune stale ESLint suppressions (base-red #11449) ([#11644](https://github.com/diegosouzapw/OmniRoute/pull/11644)) — thanks @jonlwheat2-gif +- **docs(changelog):** aggregate the ten v3.8.50 fragments into CHANGELOG.md ([#11683](https://github.com/diegosouzapw/OmniRoute/pull/11683)) +- **chore(search):** drop unreachable credential sweep after #11642 ([#11687](https://github.com/diegosouzapw/OmniRoute/pull/11687)) — thanks @HouMinXi +- **ci:** add API route TypeScript regression gate ([#11705](https://github.com/diegosouzapw/OmniRoute/pull/11705)) — thanks @Sabeekhann +- **docs(dedup):** correct the CodeQL claim on the tenant-namespace comment ([#11712](https://github.com/diegosouzapw/OmniRoute/pull/11712)) +- **docs(changelog):** add consolidated v3.8.50 stats, top-25 ranking and a .mailmap ([#11715](https://github.com/diegosouzapw/OmniRoute/pull/11715)) +- **docs:** recommend gstack for AI-assisted workflows ([#11770](https://github.com/diegosouzapw/OmniRoute/pull/11770)) — thanks @santosraju99-hub +- **chore(quality):** tighten complexity/cognitive-complexity ratchets to the current tip; land the missed gateways.ts rebaseline ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **docs(i18n):** finish Freepik → Magnific rebrand in locale strings and README ([#11772](https://github.com/diegosouzapw/OmniRoute/pull/11772)) — thanks @turbolego +- **refactor(auto-combo):** fix divergent scoring in combo health reporting ([#11854](https://github.com/diegosouzapw/OmniRoute/pull/11854)) — thanks @jacobsparts +- **chore(changelog):** add missing fragments for #11919/#11918/#11916 ([#11938](https://github.com/diegosouzapw/OmniRoute/pull/11938)) +- **test(check):** escape the runs-on fixture with JSON.stringify, not a quote-only replace ([#11942](https://github.com/diegosouzapw/OmniRoute/pull/11942)) (direct commit `fb7445eaa3`) +- **test(release):** align five suites with the contracts #11933, #11919 and #11876 shipped on release/v3.8.51 ([#11944](https://github.com/diegosouzapw/OmniRoute/pull/11944)) +- **chore(quality):** remove unreachable code and restore test discovery ([#11950](https://github.com/diegosouzapw/OmniRoute/pull/11950)) — thanks @backryun +- **docs(agents):** sync-back landings are fast-forward, never squash ([#11964](https://github.com/diegosouzapw/OmniRoute/pull/11964)) +- **test(infra):** retry recursive temp-dir removal instead of failing a shard on ENOTEMPTY (#11966) ([#11968](https://github.com/diegosouzapw/OmniRoute/pull/11968)) +- **docs(ops):** the .113 heavy-build ceiling is one runner, not two ([#12048](https://github.com/diegosouzapw/OmniRoute/pull/12048)) +- **test(ui):** correct inactive auto-fetch expectation ([#12098](https://github.com/diegosouzapw/OmniRoute/pull/12098)) — thanks @RaviTharuma +- **docs:** document native dependency check escape hatch ([#12101](https://github.com/diegosouzapw/OmniRoute/pull/12101)) — thanks @RaviTharuma +- **test(providers):** regenerate the translate-path golden for OrcaRouter (#11923) ([#12118](https://github.com/diegosouzapw/OmniRoute/pull/12118)) +- **chore(quality):** velocity phase — loosen every numeric baseline by 20% until v4.0, monitor headroom nightly ([#12125](https://github.com/diegosouzapw/OmniRoute/pull/12125)) +- **chore(lint):** batch 0 of #12146 — type the call-log-cap sqlite rows instead of 45 as-any casts ([#12157](https://github.com/diegosouzapw/OmniRoute/pull/12157)) +- **chore(lint):** batch 4 of #12146 — shared/components react-hooks violations resolved ([#12159](https://github.com/diegosouzapw/OmniRoute/pull/12159)) +- **chore(lint):** batch 1 of #12146 — dashboard/cli-code react-hooks violations resolved ([#12160](https://github.com/diegosouzapw/OmniRoute/pull/12160)) +- **chore(lint):** batch 3 of #12146 — dashboard/settings react-hooks violations resolved ([#12162](https://github.com/diegosouzapw/OmniRoute/pull/12162)) +- **chore(lint):** batch 2 of #12146 — resolve the react-hooks compiler violations in dashboard/providers ([#12163](https://github.com/diegosouzapw/OmniRoute/pull/12163)) +- **chore(quality):** register search-432 cooldown test in stryker tap.testFiles ([#12170](https://github.com/diegosouzapw/OmniRoute/pull/12170)) +- **chore(lint):** batch 5 of #12146 — combos, endpoint, provider-stats, api-manager and costs react-hooks violations resolved ([#12174](https://github.com/diegosouzapw/OmniRoute/pull/12174)) +- **build:** add contributor fast profile ([#12192](https://github.com/diegosouzapw/OmniRoute/pull/12192)) — thanks @rafacpti23 +- **docs(readme):** README touch-ups ([#12193](https://github.com/diegosouzapw/OmniRoute/pull/12193)) ([#12194](https://github.com/diegosouzapw/OmniRoute/pull/12194)) ([#12202](https://github.com/diegosouzapw/OmniRoute/pull/12202)) — thanks @rafacpti23 +- **build:** force the webpack bundler in the contributor build profile ([#12197](https://github.com/diegosouzapw/OmniRoute/pull/12197)) — thanks @rafacpti23 +- **build:** skip standalone packaging in the contributor profile ([#12198](https://github.com/diegosouzapw/OmniRoute/pull/12198)) — thanks @rafacpti23 +- **docs(audit):** align every published number with the code and harden check:docs-counts ([#12200](https://github.com/diegosouzapw/OmniRoute/pull/12200)) +- **build:** stub the instrumentation entrypoints in the contributor profile ([#12203](https://github.com/diegosouzapw/OmniRoute/pull/12203)) — thanks @rafacpti23 +- **build:** omit the standalone output target for contributor builds ([#12204](https://github.com/diegosouzapw/OmniRoute/pull/12204)) — thanks @rafacpti23 +- **chore(lint):** batch 6 of #12146 — memory, radar, audit, analytics, cache, usage, activity, home and RequestLoggerV2 react-hooks violations resolved ([#12208](https://github.com/diegosouzapw/OmniRoute/pull/12208)) +- **docs(diagrams):** rename number-carrying diagram files to stable names ([#12210](https://github.com/diegosouzapw/OmniRoute/pull/12210)) +- **docs(dashboard):** redraw onboarding tier-flow SVGs for the real 4-tier model ([#12211](https://github.com/diegosouzapw/OmniRoute/pull/12211)) +- **docs(api):** document every implemented route in openapi.yaml (276 → 692 paths) ([#12212](https://github.com/diegosouzapw/OmniRoute/pull/12212)) +- **chore(lint):** batch 7 of #12146 — final src tail: 37 react-hooks violations across 33 files resolved ([#12244](https://github.com/diegosouzapw/OmniRoute/pull/12244)) +- Revert "docs: recommend gstack for AI-assisted workflows (#11770)" ([#12248](https://github.com/diegosouzapw/OmniRoute/pull/12248)) ([#12249](https://github.com/diegosouzapw/OmniRoute/pull/12249)) +- **docs:** Chaos Mode setup guide + weighted strategy semantics ([#12250](https://github.com/diegosouzapw/OmniRoute/pull/12250)) +- **docs(agents):** protected-surface merge rule — operator approval for agent-instruction files ([#12253](https://github.com/diegosouzapw/OmniRoute/pull/12253)) +- **chore(quality):** register native-codex turn-pin tests in stryker tap.testFiles ([#12263](https://github.com/diegosouzapw/OmniRoute/pull/12263)) +- **chore(quality):** dedupe tap.testFiles entries added by racing base-red fixes ([#12265](https://github.com/diegosouzapw/OmniRoute/pull/12265)) +- **docs(readme):** introduce OmniRouteTray — the macOS menu-bar companion ([#12276](https://github.com/diegosouzapw/OmniRoute/pull/12276)) — thanks @ggiak +- **chore(proxy):** purge the legacy 1proxy residue — sync/rotator modules, dead DB exports, dead settings tab and flag, docs (#12091) ([#12290](https://github.com/diegosouzapw/OmniRoute/pull/12290)) +- **chore(quality):** baseline-headroom skips generated and vendored files in the fileSize worst-file signal ([#12291](https://github.com/diegosouzapw/OmniRoute/pull/12291)) +- **refactor(video):** unify the JPEG frame data-URI contract ([#12322](https://github.com/diegosouzapw/OmniRoute/pull/12322)) +- **chore(deps):** freeze onnxruntime-node and eslint-plugin-react-hooks out of dependabot groups ([#12329](https://github.com/diegosouzapw/OmniRoute/pull/12329)) +- **test(executors):** restore the #10986 reasoning-only fallback guards ([#12364](https://github.com/diegosouzapw/OmniRoute/pull/12364)) — thanks @pacocartones +- **chore(quality):** re-tighten the file-size ratchet to the real LOC (plan 3.8.52 task 0) ([#12411](https://github.com/diegosouzapw/OmniRoute/pull/12411)) +- **test(grok):** format web executor suite ([#12412](https://github.com/diegosouzapw/OmniRoute/pull/12412)) +- **chore(quality):** ratchet open-sse typecheck baseline to zero ([#12418](https://github.com/diegosouzapw/OmniRoute/pull/12418)) +- **test(build):** guard the artifact path policy arrays against duplicates ([#12422](https://github.com/diegosouzapw/OmniRoute/pull/12422)) +- **chore(lint):** adopt eslint-plugin-react-hooks 7.1.1 ([#12428](https://github.com/diegosouzapw/OmniRoute/pull/12428)) +- **chore(providers):** remove a keyless provider integration at its operator's request ([#12440](https://github.com/diegosouzapw/OmniRoute/pull/12440)) +- **docs(providers):** register providers removed at their operator's request and guard against reintroduction ([#12478](https://github.com/diegosouzapw/OmniRoute/pull/12478)) +- **chore(quality):** base fixes — stryker tap.testFiles + node_modules cache key ([#12482](https://github.com/diegosouzapw/OmniRoute/pull/12482)) +- **docs(arch):** one-process recipe for tens of long /v1/responses ([#12493](https://github.com/diegosouzapw/OmniRoute/pull/12493)) — thanks @RaviTharuma +- **chore(quality):** tighten the CodeQL ratchet baseline from 11 to 6 ([#12530](https://github.com/diegosouzapw/OmniRoute/pull/12530)) +- **chore(deps):** pin browserslist override to ^4.28.8 ([#12592](https://github.com/diegosouzapw/OmniRoute/pull/12592)) — thanks @KooshaPari +- **chore(deps):** bump fast-uri to 3.1.7 in the electron lockfile ([#12601](https://github.com/diegosouzapw/OmniRoute/pull/12601)) +- **docs(quality):** document how the CodeQL ratchet refreshes and how to tighten it ([#12611](https://github.com/diegosouzapw/OmniRoute/pull/12611)) +- **test(ci):** pin the openapi-security-tiers two-arm contract with an executing gate test (#12581) ([#12652](https://github.com/diegosouzapw/OmniRoute/pull/12652)) +- **docs(gamification):** align XP Rewards table with code (#12501) ([#12667](https://github.com/diegosouzapw/OmniRoute/pull/12667)) — thanks @KooshaPari +- **refactor(combo):** split executeTarget into gates, attempt, and loop ([#12746](https://github.com/diegosouzapw/OmniRoute/pull/12746)) — thanks @HouMinXi +- **docs(security):** document socket.yml scanner config + CI workflow link (#12575) ([#12764](https://github.com/diegosouzapw/OmniRoute/pull/12764)) — thanks @KooshaPari +- **chore(ci):** guard commit identity in pre-commit to stop author misattribution ([#12772](https://github.com/diegosouzapw/OmniRoute/pull/12772)) +- **deps:** 17 Dependabot bumps — bump electron from 43.4.0 to 43.4.1 in /electron ([#11426](https://github.com/diegosouzapw/OmniRoute/pull/11426)); bump the development group across 1 directory with 8 updates ([#11428](https://github.com/diegosouzapw/OmniRoute/pull/11428)); bump the production group across 1 directory with 11 updates ([#11440](https://github.com/diegosouzapw/OmniRoute/pull/11440)); chore(deps): bump github/codeql-action/init from 4.37.7 to 4.37.8 ([#11925](https://github.com/diegosouzapw/OmniRoute/pull/11925)); chore(deps): bump github/codeql-action from 4.37.7 to 4.37.8 ([#11926](https://github.com/diegosouzapw/OmniRoute/pull/11926)); chore(deps): bump github/codeql-action/analyze from 4.37.7 to 4.37.8 ([#11927](https://github.com/diegosouzapw/OmniRoute/pull/11927)); build(deps): bump github/codeql-action/init from 4.37.8 to 4.37.9 ([#12345](https://github.com/diegosouzapw/OmniRoute/pull/12345)); build(deps): bump github/codeql-action/analyze from 4.37.8 to 4.37.9 ([#12346](https://github.com/diegosouzapw/OmniRoute/pull/12346)); bump the development group across 1 directory with 2 updates ([#12347](https://github.com/diegosouzapw/OmniRoute/pull/12347)); build(deps): bump github/codeql-action from 4.37.8 to 4.37.9 ([#12349](https://github.com/diegosouzapw/OmniRoute/pull/12349)); bump browserslist from 4.28.2 to 4.28.8 ([#12396](https://github.com/diegosouzapw/OmniRoute/pull/12396)); bump the production group across 1 directory with 4 updates ([#12399](https://github.com/diegosouzapw/OmniRoute/pull/12399)); bump @xmldom/xmldom ([#12500](https://github.com/diegosouzapw/OmniRoute/pull/12500)); bump qs from 6.15.2 to 6.16.0 ([#12512](https://github.com/diegosouzapw/OmniRoute/pull/12512)); bump @xmldom/xmldom from 0.9.10 to 0.9.12 ([#12513](https://github.com/diegosouzapw/OmniRoute/pull/12513)); bump fast-uri from 3.1.5 to 3.1.7 ([#12514](https://github.com/diegosouzapw/OmniRoute/pull/12514)); bump @humanfs/node from 0.16.7 to 0.16.8 ([#12515](https://github.com/diegosouzapw/OmniRoute/pull/12515)) +- Clear the `release/v3.8.51` `check:agent-skills-sync` base-red: regenerate `skills/cli-tunnel/SKILL.md` so the `tunnel create [type]` positional that #13009 taught the generator to read is reflected in the committed skill. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` typecheck base-red from #12731: the new `Mode pack ranking gates` candidates in `open-sse/services/autoCombo/__tests__/autoCombo.test.ts` omitted the required `provider`, `model` and `errorRate` fields of `ProviderCandidate`, failing `check:open-sse-typecheck` (and with it `Fast Quality Gates`) on every open PR. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` docs-sync base-red: EURouter (#13025) and GreenPT (#13024) took the live provider count to 358, leaving 7 STRICT drifts (`PROVIDER_REFERENCE.md`, 4 diagrams, 2 tier-flow images) plus the `AGENTS.md` / `llm.txt` / `package.json` count claims stale. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- **test(models):** Pin `CATALOG_BUILD_TIMEOUT_MS` in the #12058 canonical-catalog regression so a cold catalog build racing the 8s default bound no longer flakes the suite with a 500 ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Allow `@omniroute/opencode-plugin-v2/` in the npm pack policy — [#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870) shipped the v2 plugin beside its v1 sibling without widening the allowlist, so every packed file under it read as an unexpected artifact and failed `check:pack-policy` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Point the release-green validator's pack gate at the tree it is validating — the build-provenance guard defaulted to `origin/main`, which a release branch tip can never be an ancestor of mid-cycle, so `Package artifact` reported a false red on every run and the tarball boot-smoke cascaded off it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **docs(checks):** keep doc counts honest — headings, rankings, catalog, weights, quality gate and scoring diagram now covered ([#12507](https://github.com/diegosouzapw/OmniRoute/pull/12507)) — thanks @maxmad64bis +- **chore(lifecycle):** `check:model-lifecycle` now also diffs `DEFAULT_DEGRADATION_MAP` (the background-task redirect table) against the vendor lifecycle snapshot, refusing a retired id as source or target, with a table-driven unit test beside it. Three rows whose source the vendor had retired — `claude-sonnet-4-20250514`, `gemini-3-pro-preview` and `gpt-5.1-codex` (whose target `gpt-5.1-codex-mini` is retired too) — were dead code, since `checkLifecycle` answers 410 before the redirect runs; they are dropped (#12535 — thanks @pacocartones) +- **test(compression):** cover idle worker eviction at the resource level — the pool must call `terminate()` and must not retain the worker's `MessagePort`, complementing the `exit`-event assertion added with the fix ([#13371](https://github.com/diegosouzapw/OmniRoute/pull/13371)) +- **chore(quality):** raise the `imageGeneration.ts` file-size ceiling for the image-only-model guard that clears the #12945 base-red ([#13392](https://github.com/diegosouzapw/OmniRoute/pull/13392)) +- **chore(quality):** type the combo-test route's JSON response bodies instead of casting them to `any`, drop the now-empty suppression entry, and remove the `hasPerModelQuota` import `chatCore.ts` stopped using when the failure-classification helper was extracted ([#13379](https://github.com/diegosouzapw/OmniRoute/pull/13379)) +- **chore(electron):** the desktop bundle no longer ships the root-level files of every translated docs mirror (`README.md`, `llm.txt`, `CLAUDE.md`, `GEMINI.md`, `CONTRIBUTING.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`) — the packaged app only reads `docs/i18n//docs/**`, which stays. Saves ~11 MB on top of the translated CHANGELOGs already pruned. (#0000) ([#13235](https://github.com/diegosouzapw/OmniRoute/pull/13235)) +- **chore(quality):** retighten the `src/sse/handlers/chat.ts` unused-vars suppression count to the 8 violations that actually remain ([#13385](https://github.com/diegosouzapw/OmniRoute/pull/13385)) +- Bring the documented counts back in line with the code: 171 migrations (was 169) and 20 routing strategies (was 19), plus the regenerated `cli-tunnel` skill reference. ([#12970](https://github.com/diegosouzapw/OmniRoute/pull/12970)) +- Update the documented migration count to 172 after the call-logs provider-stats indexes landed. ([#13209](https://github.com/diegosouzapw/OmniRoute/pull/13209)) +- **chore(ci):** two gates that close the blind spots behind the exclusions above. `check:vitest-exclusions` requires every Vitest exclusion to name a tracking issue and to appear in `config/quality/vitest-exclusions.json` — the previous list grew to 62 files behind a comment pointing at an issue that had been closed for a month. `check-new-key-coverage` requires a key newly added to `en.json` to reach every locale; the existing coverage gate is a percentage floor per locale, so eleven absent keys out of ~13,000 left it at 99.9% while a whole feature shipped untranslated in nine languages. Both are diff-aware, so pre-existing debt stays frozen and neither needed a migration to turn on. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **build:** root layout no longer loads Inter from `next/font/google`, so a production image build does not need fonts.googleapis.com ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **docs(i18n):** bring the eight new `llm.txt` mirrors (km, kn, ml, my, ne, or, pa, si) up to the current provider and migration counts, so `check-docs-sync` stops rejecting every commit ([#13674](https://github.com/diegosouzapw/OmniRoute/pull/13674)) +- **refactor(ui):** flow surfaces (home topology, combo live studio, compression cockpit/waterfall/nodes, token-health badge) express state through the theme-aware `--orch-status-*` tokens instead of fixed dark-mode hex, so green/red/amber/grey stay legible in the light theme; dark mode is byte-identical. Categorical palettes (routing-strategy hues, compression-layer pills, provider brand colors) deliberately stay hex ([#12378](https://github.com/diegosouzapw/OmniRoute/issues/12378)) ([#13203](https://github.com/diegosouzapw/OmniRoute/pull/13203)) +- **chore(tests):** 51 test files that had been excluded from Vitest are running again, restoring roughly 350 assertions to the blocking suite. Each was measured individually first: of the 62 files parked behind the `// #8618 — pre-existing failure` comment, 51 pass against the current tree with no source change, so the exclusions had outlived the failures they were added for. The 11 that genuinely still fail stay excluded, but now point at a live tracker (#13204) rather than at #8618, which was closed in August while the list it tracked kept growing. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **chore(quality):** drop two ESLint suppression entries whose violations no longer exist, so `eslint --suppressions-location` stops rejecting every commit that touches the surrounding files ([#13384](https://github.com/diegosouzapw/OmniRoute/pull/13384)) +- **chore(release):** bring main's post-tag fixes into release/v3.8.51 — sync-back step 2/2 (direct commit `21c488f210`) +- **chore(deps):** patch toml and esbuild in the opencode plugin lockfiles ([#12965](https://github.com/diegosouzapw/OmniRoute/pull/12965)) +- **docs(changelog):** reconcile the v3.8.51 living section — fold 366 fragments, cover every cycle commit, credit every contributor ([#12971](https://github.com/diegosouzapw/OmniRoute/pull/12971)) +- **test(plugin):** wait for the v2 lazy-refresh tier instead of sleeping past it ([#12984](https://github.com/diegosouzapw/OmniRoute/pull/12984)) +- **chore(quality):** rebaseline chatCore.ts after the non-streaming regression fixes ([#13045](https://github.com/diegosouzapw/OmniRoute/pull/13045)) +- **docs(changelog):** add the missing bullet marker to the DeepSeek PoW fragment ([#13200](https://github.com/diegosouzapw/OmniRoute/pull/13200)) +- **chore(deps):** drain the Dependabot queue — 10 of 13 alerts ([#13213](https://github.com/diegosouzapw/OmniRoute/pull/13213)) +- **chore(ci):** make worktrees carry the hooks that enforce the identity gate ([#13236](https://github.com/diegosouzapw/OmniRoute/pull/13236)) +- **chore(deps):** pin csv-parse to ^7.0.2 via overrides (#13117) ([#13300](https://github.com/diegosouzapw/OmniRoute/pull/13300)) — thanks @KooshaPari +- **chore(deps):** bump hono from ^4.12.34 to ^4.13.7 (#13148) ([#13301](https://github.com/diegosouzapw/OmniRoute/pull/13301)) — thanks @KooshaPari +- **chore(deps):** pin joi to ^18.2.8 via overrides (#13085) ([#13302](https://github.com/diegosouzapw/OmniRoute/pull/13302)) — thanks @KooshaPari +- **docs(providers):** add Microsoft 365 Copilot (BizChat) provider guide (#12779) ([#13340](https://github.com/diegosouzapw/OmniRoute/pull/13340)) — thanks @KooshaPari +### 🙌 Contributors + +Thanks to everyone whose work landed in v3.8.51: + +| Contributor | PRs / Issues | +| --- | --- | +| [@5dive-bot](https://github.com/5dive-bot) | #11852 | +| [@abhisheksharma2411](https://github.com/abhisheksharma2411) | #11547, #11684, #11959, #12209 | +| [@adevwithpurpose](https://github.com/adevwithpurpose) | #11464 | +| [@adivekar-utexas](https://github.com/adivekar-utexas) | #12015, #12043, #12090, #12138 | +| [@AIB1TAL0S](https://github.com/AIB1TAL0S) | #12103 | +| [@alltomatos](https://github.com/alltomatos) | #11448 | +| [@alvinveroy](https://github.com/alvinveroy) | #11857, #12027, #12165 | +| [@amaleta](https://github.com/amaleta) | #11906, #11934 | +| [@amartinawi](https://github.com/amartinawi) | #11575 | +| [@AndrianBalanescu](https://github.com/AndrianBalanescu) | #11083, #11830, #11834, #11844 | +| [@anhtahaylove](https://github.com/anhtahaylove) | #13091, #13092, #13093, #13096, #13097, #13100, #13106, #13109, #13114, #13155, #13166, #13171, #13175, #13187, #13196 | +| [@aniruddhaadak80](https://github.com/aniruddhaadak80) | #11760 | +| [@app](https://github.com/app) | #12554 | +| [@arminanton](https://github.com/arminanton) | #11461, #11513 | +| [@AStupidBear](https://github.com/AStupidBear) | #11584 | +| [@atescivitci-cmd](https://github.com/atescivitci-cmd) | #12105 | +| [@b3nw](https://github.com/b3nw) | #11970, #11971, #12147 | +| [@backryun](https://github.com/backryun) | #11259, #11950, #12075, #12076, #12078, #12079, #12081, #12082, #12181, #12228, #12239, #12255, #12258, #12277, #12367, #12423, #12524, #12538 | +| [@benjaminkitt](https://github.com/benjaminkitt) | #11747 | +| [@benzntech](https://github.com/benzntech) | #11614, #11615 | +| [@Bl0ck154](https://github.com/Bl0ck154) | #11948, #11951, #11952, #11953, #11954 | +| [@brick30llc-ctrl](https://github.com/brick30llc-ctrl) | #11752, #12242, #12262 | +| [@Chewji9875](https://github.com/Chewji9875) | #11455, #11875, #12028 | +| [@cheynetom](https://github.com/cheynetom) | #12058 | +| [@davidebaraldo](https://github.com/davidebaraldo) | #12222, #12304 | +| [@davidlinfr](https://github.com/davidlinfr) | #6390 | +| [@ddarkr](https://github.com/ddarkr) | #12124 | +| [@Deftera186](https://github.com/Deftera186) | #11809 | +| [@dpozimski](https://github.com/dpozimski) | #11945 | +| [@DW-MediaLab](https://github.com/DW-MediaLab) | #12229 | +| [@echel0nn](https://github.com/echel0nn) | #11923 | +| [@f9td56dbgh-hub](https://github.com/f9td56dbgh-hub) | #11660 | +| [@fabioluissilva](https://github.com/fabioluissilva) | #11991 | +| [@foreveryh](https://github.com/foreveryh) | #12177 | +| [@geek007git](https://github.com/geek007git) | #12115, #12116, #12117, #12120, #12122 | +| [@geekyNads](https://github.com/geekyNads) | #11784, #12266 | +| [@ggiak](https://github.com/ggiak) | #11561, #12276, #12350, #12402 | +| [@gonisulaimann](https://github.com/gonisulaimann) | #12368, #12369, #12371 | +| [@Gorillaz322](https://github.com/Gorillaz322) | #12207 | +| [@groovecityJO](https://github.com/groovecityJO) | #12682 | +| [@hartmark](https://github.com/hartmark) | #11434, #11452, #11473, #11499, #11703, #11983, #11984, #11985, #11986, #11988, #11989, #11990, #11994, #12221, #12293, #12445, #12446, #12447, #12448, #12460, #12461, #12623, #12646, #12650, #12680, #12717, #12718, #12727, #12741, #12854 | +| [@hizzt](https://github.com/hizzt) | #11894 | +| [@hongnoul](https://github.com/hongnoul) | #11484 | +| [@HouMinXi](https://github.com/HouMinXi) | #11411, #11414, #11512, #11518, #11520, #11641, #11642, #11643, #11687, #11779, #11849, #11850, #11851, #11915, #11916, #11918, #11919, #11920, #12013, #12017, #12033, #12042, #12106, #12139, #12166, #12169, #12171, #12205, #12213, #12312, #12325, #12487, #12488, #12495, #12504, #12557, #12566, #12590, #12591, #12624, #12626, #12632, #12637, #12678, #12696, #12697, #12711, #12733, #12746, #12767, #12770, #12789, #12803, #12805, #12811, #12866, #12868, #12899, #12926, #12934, #12950, #12951, #12974, #13001, #13006, #13011, #13017, #13026, #13027, #13034, #13035, #13038, #13042, #13050, #13060, #13061, #13069, #13107, #13120, #13136, #13195, #13197 | +| [@Hsia97](https://github.com/Hsia97) | #11624 | +| [@jacobsparts](https://github.com/jacobsparts) | #11854, #12155, #12167 | +| [@joglomedia](https://github.com/joglomedia) | #11980 | +| [@jonlwheat2-gif](https://github.com/jonlwheat2-gif) | #11443, #11450, #11549, #11567, #11608, #11633, #11635, #11640, #11644, #11671, #11672, #11673, #11674, #11675, #11676, #11677, #11706, #11783, #12051, #12052, #12053, #12055 | +| [@kanade-hoshino](https://github.com/kanade-hoshino) | #12180 | +| [@Karan825](https://github.com/Karan825) | #11861 | +| [@KaspaPulse](https://github.com/KaspaPulse) | #11389, #11469, #11628, #11666, #11910 | +| [@keeltrace](https://github.com/keeltrace) | #12080, #12223 | +| [@killer30001000](https://github.com/killer30001000) | #12178 | +| [@KooshaPari](https://github.com/KooshaPari) | #12470, #12592, #12667, #12699, #12703, #12706, #12764, #12769, #12771, #13300, #13301, #13302, #13340, #13401, #13403, #13404, #13405, #13406, #13407, #13409, #13410, #13411, #13412, #13413, #13414, #13418, #13424, #13427, #13433, #13523, #13524, #13525, #13528, #13531, #13532, #13534, #13539, #13543, #13545, #13546, #13547, #13550, #13553 | +| [@kriptoburak](https://github.com/kriptoburak) | #11370 | +| [@KrzysiekSko](https://github.com/KrzysiekSko) | #12425, #12432, #12673 | +| [@marcelokarval](https://github.com/marcelokarval) | direct commit / report | +| [@maxmad64bis](https://github.com/maxmad64bis) | #1622, #11435, #11437, #11441, #11537, #11550, #11553, #11555, #11557, #11812, #11842, #11843, #11903, #12151, #12214, #12215, #12218, #12226, #12314, #12316, #12317, #12318, #12319, #12320, #12321, #12507, #12715, #12731, #12744, #12786, #12787, #12788, #12790, #12792, #12794, #12795, #12828, #12832, #12853, #12857, #12870, #12937, #12941, #12975, #13141, #13142, #13146, #13147 | +| [@mdigitalbh81](https://github.com/mdigitalbh81) | #12240 | +| [@Meet6338-X](https://github.com/Meet6338-X) | #11598, #11609, #12110 | +| [@morpheus9393](https://github.com/morpheus9393) | #11943 | +| [@MumuTW](https://github.com/MumuTW) | #11492, #11502, #11506, #11507, #11626, #11675, #11685, #11728, #11746, #11888, #11889, #11890, #11892 | +| [@Neuron-Mr-White](https://github.com/Neuron-Mr-White) | #11622, #11801, #12256 | +| [@NightStalker-87](https://github.com/NightStalker-87) | #12183 | +| [@NoxzRCW](https://github.com/NoxzRCW) | #11879, #11880, #11881, #11882, #11883, #11935 | +| [@ntdat812](https://github.com/ntdat812) | #11585, #12095 | +| [@ntdatt812](https://github.com/ntdatt812) | #11368, #11573, #11574, #11576, #11577, #11580, #11582, #11583, #11588, #11589, #11590, #11591, #11592, #11593, #11672, #12177, #12180, #12873, #12918, #12920, #12921, #12925, #12930, #13007, #13009, #13024, #13025, #13083, #13087, #13101, #13104, #13110 | +| [@official-burak](https://github.com/official-burak) | #11542 | +| [@opensource-elearning](https://github.com/opensource-elearning) | #12179, #12189, #12278, #12286 | +| [@oyi77](https://github.com/oyi77) | #11408, #11409, #11421, #11505, #11677, #12036, #12110 | +| [@pacocartones](https://github.com/pacocartones) | #11521, #11522, #11527, #11528, #11529, #11530, #11531, #11532, #11533, #11534, #11595, #11599, #11603, #11604, #11605, #11607, #11610, #11676, #11714, #11716, #11718, #11767, #11838, #11860, #11862, #11869, #11871, #11872, #11873, #11903, #11906, #11921, #11934, #12356, #12358, #12359, #12360, #12361, #12362, #12364, #12365, #12373, #12374, #12375, #12376, #12377, #12379, #12380, #12381, #12386, #12387, #12389, #12390, #12394, #12395, #12397, #12401, #12403, #12404, #12522, #12523, #12535, #12536, #12540, #12541, #12543, #12545, #12548, #12549, #12550, #12551, #12552, #12647, #12651, #12653, #13055 | +| [@patrykkopycinski](https://github.com/patrykkopycinski) | #11936, #11937, #12224 | +| [@PauloFH](https://github.com/PauloFH) | #11509 | +| [@PauloHSOliveira](https://github.com/PauloHSOliveira) | #12241 | +| [@ponkcore](https://github.com/ponkcore) | #12054 | +| [@Prajeeth-12](https://github.com/Prajeeth-12) | #11634, #12046 | +| [@quiterunner-commits](https://github.com/quiterunner-commits) | #12131, #12143 | +| [@rafacpti23](https://github.com/rafacpti23) | #11554, #11558, #12192, #12197, #12198, #12202, #12203, #12204 | +| [@ragnar-claude](https://github.com/ragnar-claude) | #11564 | +| [@raheemuddin786](https://github.com/raheemuddin786) | #11491, #11839, #11840, #11841, #12003, #12230, #12231, #12232, #12233, #12234 | +| [@rahilmavani](https://github.com/rahilmavani) | #11761 | +| [@Rahulsharma0810](https://github.com/Rahulsharma0810) | #11771 | +| [@RaviTharuma](https://github.com/RaviTharuma) | #11710, #11727, #11797, #11798, #11802, #11805, #11806, #11811, #12098, #12099, #12101, #12449, #12452, #12472, #12473, #12493, #12533, #12607, #12628, #12631, #12636, #12875, #12876, #12880, #12882, #12884 | +| [@rezjalibd](https://github.com/rezjalibd) | #12186 | +| [@RhianB14](https://github.com/RhianB14) | #11692 | +| [@rifqiawl](https://github.com/rifqiawl) | #11517, #11519, #11969 | +| [@rqzbeh](https://github.com/rqzbeh) | #11390 | +| [@Sabeekhann](https://github.com/Sabeekhann) | #11699, #11705 | +| [@santosraju99-hub](https://github.com/santosraju99-hub) | #11755, #11770 | +| [@Seramicx](https://github.com/Seramicx) | #11568 | +| [@Siva010](https://github.com/Siva010) | #12191 | +| [@solstxce](https://github.com/solstxce) | #11597 | +| [@soroush5](https://github.com/soroush5) | #12691 | +| [@tenshiak](https://github.com/tenshiak) | #12279 | +| [@TheDemonTuan](https://github.com/TheDemonTuan) | #11468, #11470, #11471, #11482, #11548, #11758, #11775, #11814 | +| [@thomasmaerz](https://github.com/thomasmaerz) | #12834 | +| [@tuandinh0801](https://github.com/tuandinh0801) | #11454 | +| [@turbolego](https://github.com/turbolego) | #11621, #11762, #11772, #11774, #11781, #12216 | +| [@Tushar49](https://github.com/Tushar49) | #11392 | +| [@ujjawalkaushik1110](https://github.com/ujjawalkaushik1110) | #11863 | +| [@vermasomesh835](https://github.com/vermasomesh835) | #11794 | +| [@vsd2807](https://github.com/vsd2807) | #11565, #11619 | +| [@wahidsadik371-coder](https://github.com/wahidsadik371-coder) | #12126 | +| [@watchingdogs](https://github.com/watchingdogs) | #12031 | +| [@wildcard](https://github.com/wildcard) | #11620 | +| [@xiaoyaner0201](https://github.com/xiaoyaner0201) | #11460, #11662, #12083 | +| [@Xxx91n](https://github.com/Xxx91n) | #11690 | +| [@yourspraveen](https://github.com/yourspraveen) | #11146 | +| [@yxyxy](https://github.com/yxyxy) | #11671 | +| [@ZaimMarzuki](https://github.com/ZaimMarzuki) | #11960 | +| [@Zartharas](https://github.com/Zartharas) | #11340 | +| [@zero-executioner](https://github.com/zero-executioner) | #11631 | +| [@diegosouzapw](https://github.com/diegosouzapw) | maintainer | + +--- ## [3.8.50] — 2026-08-25 _Living section — regenerated 2026-08-12 from all cycle commits (cycle open `ed2db6cb19` → tip). Bullets carry the merged PR and its author; direct pushes listed separately._ diff --git a/docs/i18n/ro/CHANGELOG.md b/docs/i18n/ro/CHANGELOG.md index aa65668c11..0f8357af9b 100644 --- a/docs/i18n/ro/CHANGELOG.md +++ b/docs/i18n/ro/CHANGELOG.md @@ -8,16 +8,1119 @@ ## [3.8.51] — TBD -_Living section — cycle opened at the v3.8.50 freeze (parallel-cycle model). Bullets are aggregated from `changelog.d/` fragments at each `/generate-release` phase._ +_Living section — reconciled 2026-09-15 from all cycle commits (`091589089c` → `c0f92ec98a`, 916 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 | **123** | +| 📝 Commits in the cycle | **916** | +| 🔀 Pull requests referenced | **892** | +| 📋 Changelog entries | **919** | +| 🙌 Contributors credited in entries | **121** | +| 🤖 Automated dependency commits | 18 | + +**Entries by type** + +| Type | Count | +| --- | ---: | +| 🐛 Fixes | 592 | +| ✨ Features | 131 | +| 🧹 Chore | 93 | +| 📚 Docs | 42 | +| 🧪 Tests | 32 | +| ♻️ Refactor | 8 | +| 🏗️ Build | 7 | +| ⚡ Performance | 7 | +| ⚙️ CI | 2 | +| ⏪ Reverts | 2 | +| 📦 Dependencies | 2 | +| 🔒 Security | 1 | + +### 🏆 Top 25 contributors this cycle + +_By commits in `091589089c..c0f92ec98a`, author identities consolidated via `.mailmap` and the merged PR's GitHub login. Bots excluded._ + +| # | Contributor | Commits | +| ---: | --- | ---: | +| 🥇 | diegosouzapw | 323 | +| 🥈 | Bob.Hou (@HouMinXi) | 79 | +| 🥉 | Paco Cartones (@pacocartones) | 77 | +| 4 | Dizzle (@maxmad64bis) | 49 | +| 5 | Koosha Paridehpour (@KooshaPari) | 43 | +| 6 | Nguyen Thanh Dat (@ntdatt812) | 31 | +| 7 | Ravi Tharuma (@RaviTharuma) | 26 | +| 8 | Markus Hartung (@hartmark) | 22 | +| 9 | Webman (@jonlwheat2-gif) | 22 | +| 10 | backryun | 17 | +| 11 | anhtahaylove | 15 | +| 12 | Rafa Martins (@rafacpti23) | 10 | +| 13 | Syed Raheemuddin (@raheemuddin786) | 10 | +| 14 | Nguyễn Viết Tuấn (@TheDemonTuan) | 8 | +| 15 | MumuTW | 7 | +| 16 | Paijo (@oyi77) | 7 | +| 17 | Tobias Andersen (@turbolego) | 6 | +| 18 | Bl0ck (@Bl0ck154) | 5 | +| 19 | SHANMUGAPRIYAN (@geek007git) | 5 | +| 20 | KaspaPulse | 5 | +| 21 | Abhishek Sharma (@abhisheksharma2411) | 4 | +| 22 | Abhishek Divekar (@adivekar-utexas) | 4 | +| 23 | Andrew B. (@AndrianBalanescu) | 4 | +| 24 | NoxzRCW | 4 | +| 25 | opensource-elearning | 4 | ### ✨ New Features +- **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). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- Added Google AI Studio Gemini batch text-to-speech support through `POST /v1/audio/speech`. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- 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. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(routing):** subscription-first auto groupings — `auto/subscription` routes only through plan-included connections with a documented hard-stop overage and fails closed on exhaustion, while `auto/thrifty` orders the pool `subscription → keyless → free → cheap → premium` and steps up one rung at a time as each is exhausted. Billing class comes from a curated per-connection catalog (uncurated is treated as metered, never plan-included), both reuse STRICT_ZERO_COST's per-connection verification, and a quota reading whose `resetAt` has passed is now refreshed regardless of TTL so routing returns to plan capacity as soon as it resets ([#11146](https://github.com/diegosouzapw/OmniRoute/pull/11146)) — thanks @yourspraveen +- **feat(providers):** publish a management-authenticated versioned web-session credential contract from OmniRoute's canonical browser credential metadata ([#11340](https://github.com/diegosouzapw/OmniRoute/pull/11340)) — thanks @Zartharas +- **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)) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add Xquik X search with typed results, credential validation, REST routing, and MCP selection ([#11370](https://github.com/diegosouzapw/OmniRoute/pull/11370)) — thanks @kriptoburak +- **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)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(dashboard):** surface durable exclusive managed leases in the existing Sessions view, keeping leased clients visible across idle gaps while marking connections with in-flight work as active ([#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389)) — thanks @KaspaPulse +- **build(bun):** allow Turbopack bundler flag on Bun 1.4+ with configurable Webpack fallback ([#11471](https://github.com/diegosouzapw/OmniRoute/pull/11471)) — thanks @TheDemonTuan +- feat(api): add an opt-in `modelVisibilityAllowlist`/`modelVisibilityDenylist` settings pair to curate exactly which models `/v1/models` advertises, mirrored into every `auto/*` combo candidate pool so a denied model cannot be routed to via combo selection either (#11481) ([#11997](https://github.com/diegosouzapw/OmniRoute/pull/11997)) +- **feat(rankings):** the Free Provider Rankings page shows what each provider actually served over the last 24 h. It ranked by ELO alone, which left a provider that answers every call with an error in first place; the usage data was already served by the API but never requested. A provider with too small a sample shows a dash, not a number ([#11546](https://github.com/diegosouzapw/OmniRoute/pull/11546)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **feat(guardrails):** enforce a bounded, deterministic contract for Video Bridge transcripts — 256 cues, 4096 input code units and 4 KiB UTF-8 per cue, 64 KiB total text, malformed-Unicode rejection, focus-window scoping, cross-source reconciliation with contributing-source metadata, and a structural provenance trust boundary so caller JSON can never self-assert `embedded`/`audio-bridge` provenance ([#11652](https://github.com/diegosouzapw/OmniRoute/issues/11652)) ([#12009](https://github.com/diegosouzapw/OmniRoute/pull/12009)) +- **feat(video):** orchestrate optional Video Bridge audio extraction and Audio Bridge STT behind a dual opt-in (operator setting AND per-request signal) — a new loopback-only broker `mode=audio` operation shares the frame path's exact process queue, deadline, AbortSignal, and byte budgets to extract a bounded mono 16 kHz PCM WAV from the same already-downloaded video, then reuses the existing Audio Bridge transcription boundary; provider segment timing is preserved when available and marked coarse otherwise, and every failure degrades to a visual-only-safe partial instead of throwing (#11654). ([#12012](https://github.com/diegosouzapw/OmniRoute/pull/12012)) +- Add a tenant-bound Video Bridge drill-down lifecycle on top of the existing secure cache substrate: opaque hashed handles (never raw session/video identifiers), preview/standard/detail multiresolution variants resampled on read, response pagination capped at 8 frames and 32 MiB, and a new authenticated `/api/v1/video-bridge/drilldown` consumer route that stays disabled for remote access by default and denies cross-key access with the same response as a nonexistent handle (no existence oracle). ([#12006](https://github.com/diegosouzapw/OmniRoute/pull/12006)) +- **test(video):** Add the Video Bridge FU-07/FU-09 promotion-evidence harness (#11656) — a frozen Zod manifest schema covering the 8 required scenario kinds (static scenes, rapid cuts, late facts, fades, blur, small text, close events, visual prompt injection) with a minimum of 3 repetitions per case, deterministic declarative fixture recipes (`videoBridgePromotionFixtures.ts`), a pure medians/p95 metrics aggregator, a pure FU-07/FU-09 promotion-verdict evaluator applying the ticket's exact thresholds (missing token usage always holds), a digest-only persistence layer that never retains raw media or raw model responses, and a versioned per-model promotion allowlist shipped empty with every model defaulting to `hold`. The FU-07/FU-09 promotion verdicts themselves remain HOLD — they require a real evidence run against real models on VPS 192.168.0.15. ([#12008](https://github.com/diegosouzapw/OmniRoute/pull/12008)) +- **feat(video bridge):** "embedded" transcript provenance can now be legitimately earned instead of merely asserted — a bounded, allowlisted (`mov_text`/`subrip`/`webvtt`) subtitle probe runs through the loopback-only Video Bridge broker (at most 2 streams, 10s subdeadline bounded by the request deadline, 256 KiB output, 4096-code-unit lines), normalized through a bounded, ReDoS-safe WebVTT parser and Zod-validated end to end. The adapter always resolves to an explicit `success`/`absent`/`transient_failure` outcome — a subtitle failure never breaks the visual description path, and only a fingerprint-verified broker response (never a caller-declared label) can produce embedded cues (#11659). ([#12011](https://github.com/diegosouzapw/OmniRoute/pull/12011)) +- Default new Antigravity-family connections (agy CLI imports and Antigravity OAuth connects) to model auto-sync, so live model discovery lands in the synced catalog and `/v1/models` picks up freshly released upstream models (e.g. Gemini 3.7 Flash tiers) without code changes. Existing connections keep their current setting; the per-connection dashboard toggle remains the opt-out. (#11685 — thanks @MumuTW) ([#11887](https://github.com/diegosouzapw/OmniRoute/pull/11887)) +- **feat(ui):** enable React Compiler (`reactCompiler: true` + `babel-plugin-react-compiler`) for automatic memoization at build time ([#11783](https://github.com/diegosouzapw/OmniRoute/pull/11783)) — thanks @jonlwheat2-gif +- **feat(providers):** add SeekAi (`seekai.cc`) as an OpenAI-compatible New-API gateway — catalog id `seekai` (alias `ska`), `https://seekai.cc/v1`, live `/v1/models` via `passthroughModels`, aggregator-list membership so New-API balance detection can opt in. No referral/aff codes. ([#11786](https://github.com/diegosouzapw/OmniRoute/issues/11786)) ([#12557](https://github.com/diegosouzapw/OmniRoute/pull/12557)) — thanks @HouMinXi +- **feat(zai):** add GLM-5.3-Flash Coding Plan support (1M context, 128K output, vision, `low|high|max` reasoning) and route `zai` GLM-5.3-family API-key traffic through the OpenAI-compatible Coding Plan endpoint with native thinking defaults ([#11801](https://github.com/diegosouzapw/OmniRoute/pull/11801)) — thanks @Neuron-Mr-White +- **feat(combo):** choose how combo models are ordered — manual, provider, score, or name — via a sort control in the dashboard builder, persisted in `config.modelSort` and re-applied on load and after add ([#11812](https://github.com/diegosouzapw/OmniRoute/pull/11812)) — thanks @maxmad64bis +- **feat(free):** custom models can be marked free-tier via `customModels[].isFree`; `isFreeModel()` is the first door and `hidePaidModels` respects it even for providers outside the free budget ([#11843](https://github.com/diegosouzapw/OmniRoute/pull/11843)) — thanks @maxmad64bis +- **feat(nodejs):** add `5dive` as a `configure` target — `omniroute configure 5dive` / `omniroute setup-5dive` write a 5dive auth profile that points an agent fleet's `claude` seats at OmniRoute, with the root-only write, the loopback-vs-`https` endpoint rule and the per-seat model pin handled explicitly ([#11852](https://github.com/diegosouzapw/OmniRoute/pull/11852)) — thanks @5dive-bot +- **feat(sse):** treat `max` as a first-class reasoning-effort tier and clamp per model family (GLM 5.1+/DeepSeek V4+/Kimi K3+ keep native `max`; o1/MiniMax/Grok/Muse Spark clamp to their upstream ceiling) ([#11875](https://github.com/diegosouzapw/OmniRoute/pull/11875)) — thanks @Chewji9875 +- **feat(providers):** the provider plugin manifest now advertises a `usage-fetch` capability for the 40 providers that have a wired usage/quota fetcher, so external dashboards can read it from `GET /api/v1/provider-plugin-manifest` instead of parsing `open-sse/services/usage.ts` after every release. Discovery only — no new fetcher, no quota change, and the Dashboard quota widget stays gated by `USAGE_SUPPORTED_PROVIDERS`. `USAGE_FETCHER_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/fetcherProviders.ts`) and is re-exported from `services/usage.ts`, keeping the manifest module a light leaf instead of pulling the ~490-module usage dispatcher into the manifest route. ([#11903](https://github.com/diegosouzapw/OmniRoute/pull/11903)) — thanks @maxmad64bis / @pacocartones +- **feat(plugins):** `OMNIROUTE_PLUGINS_DIR` sets the directory the runtime plugin scanner reads — and the root the plugin manager installs into — overriding the `HOME`-derived default, so a Docker/K8s deployment can point straight at its bind-mounted plugin tree instead of moving `HOME` just to relocate the scan path. An image that exports no home no longer scans `/tmp/.omniroute/plugins` in silence: the resolved directory is logged once at startup as `scanner.dir_resolved`, naming the input that won. Unset, behaviour is unchanged. Distinct from the CLI-only `OMNIROUTE_PLUGIN_PATH`, which finds `omniroute-cmd-*` command packages and never reached this scanner ([#11906](https://github.com/diegosouzapw/OmniRoute/pull/11906)) — thanks @amaleta / @pacocartones +- **feat(leases):** add an explicit owner-authenticated status action that returns only the active lease's privacy-safe configured connection and provider labels, with generation fencing and no credential or internal-id disclosure ([#11910](https://github.com/diegosouzapw/OmniRoute/pull/11910)) — thanks @KaspaPulse +- **feat(providers):** import-from-file modal shows per-row API errors and ships a downloadable CSV template ([#12071](https://github.com/diegosouzapw/OmniRoute/issues/12071)) ([#12504](https://github.com/diegosouzapw/OmniRoute/pull/12504)) — thanks @HouMinXi +- **feat(providers):** dashboard search matches connection name and `baseUrl` so imported OpenAI-compat nodes surface on the provider card ([#12108](https://github.com/diegosouzapw/OmniRoute/issues/12108)) ([#12495](https://github.com/diegosouzapw/OmniRoute/pull/12495)) — thanks @HouMinXi +- **feat(routing):** add a `score` Auto router strategy that selects the highest configured weighted score and reuses `explorationRate`. ([#12155](https://github.com/diegosouzapw/OmniRoute/pull/12155)) — thanks @jacobsparts +- Add a runtime feature flag to disable universal context handoffs globally without changing the default behavior. ([#12167](https://github.com/diegosouzapw/OmniRoute/pull/12167)) — thanks @jacobsparts +- **feat(providers):** the provider plugin manifest now also advertises a `usage-supported` capability for the 46 providers whose usage API is accepted by the server and Dashboard routes, so integrators can distinguish "the server will serve quota for this provider" from "a fetcher is wired" without reading TypeScript. Discovery only — no fetcher or quota change. `usage-fetch` resolves on id or alias (the usage dispatcher accepts both); `usage-supported` resolves on id alone, matching the runtime guard `USAGE_SUPPORTED_PROVIDERS.includes(providerId)`. `USAGE_SUPPORTED_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/supportedProviders.ts`) and is re-exported from `providers.ts`, mirroring the `fetcherProviders` leaf from #11903 and keeping the manifest a light module. ([#12214](https://github.com/diegosouzapw/OmniRoute/pull/12214)) — thanks @maxmad64bis +- **feat(rankings):** order Free Provider Rankings by what each provider actually served — `GET /api/free-provider-rankings?sortBy=reliability` and a "Most reliable first" toggle on the page. Providers with too few calls to state a success rate keep their score order below the measured ones; the default order is unchanged ([#12218](https://github.com/diegosouzapw/OmniRoute/pull/12218)). — thanks @maxmad64bis +- **perf(sse):** defer `cloneLogPayload()` in the structured SSE collector until after the `maxEvents`/`maxBytes` cap check, eliminating ~9,800 wasted `structuredClone` calls per streaming response (65–71% faster `push()`). Reducer snapshot isolation restored for OpenAI and Responses summaries ([#12241](https://github.com/diegosouzapw/OmniRoute/pull/12241)) — thanks @PauloHSOliveira ([#12243](https://github.com/diegosouzapw/OmniRoute/pull/12243)) +- **feat(usage):** Devin CLI agentic quota (Codeium seat-management GetUserStatus) and OpenRouter key limits plus account credits now surface in Provider Limits ([#12256](https://github.com/diegosouzapw/OmniRoute/pull/12256) — thanks @Neuron-Mr-White) +- **feat(settings):** persist `headroomUrl` through Settings so status/start use the operator URL instead of only `HEADROOM_URL` ([#12306](https://github.com/diegosouzapw/OmniRoute/issues/12306)) ([#12487](https://github.com/diegosouzapw/OmniRoute/pull/12487)) — thanks @HouMinXi +- **feat(auto-combo):** Auto-Combo scoring can now weigh how often a provider/model has actually succeeded. The engine already carried that number on every candidate — 24 hours of usage history behind a ten-sample floor, real-time metrics otherwise — and the scoring function never read it, while the weight table described `stability` as if it did. `reliability` (`1 - failureRate`, with the same field precedence and the same rate-bounding the speed ranking already uses, so a corrupt reading means "nothing observed" rather than "fails every call") is now a declared factor shipping at weight `0`, so routing is unchanged until an operator gives it one, and the `stability` description now matches what that factor computes ([#12317](https://github.com/diegosouzapw/OmniRoute/pull/12317)) — thanks @maxmad64bis +- **feat(routing):** With `freeAccessPolicy: "strict"`, the read-only candidate listing (`GET /v1/auto-combo/{channel}/candidates`) no longer hides the candidates the zero-cost guard excludes — the same read-only transparency the resilience filter already honours (#9133). Each candidate now carries `freeAccessExclusion` saying why it would be kept out, and it tells an exhausted allowance apart from a quota reading that never arrived or went stale, which used to look identical from the outside. Routing is unchanged: the listing reports, it never enforces. The separate `excludeTosAvoid` guard still drops its candidates without a reason; that gap is now documented rather than closed ([#12319](https://github.com/diegosouzapw/OmniRoute/pull/12319)) — thanks @maxmad64bis +- **feat(radar):** The Radar catalog table now shows two facts it was already receiving from the feed and dropping on the floor: the per-model rate limits (requests and tokens, per minute and per day) in a new column, and a badge when a provider's terms state it may train on the prompts you send. A limit of zero renders as zero rather than "rate-only" — for a ceiling those are opposite facts — and a model with no training statement gets no badge, because an absent statement is not a guarantee ([#12320](https://github.com/diegosouzapw/OmniRoute/pull/12320)) — thanks @maxmad64bis +- **feat(radar):** explain Community, single-use, contributor, supporter, recovery, abuse, offers, and privacy rules before either Radar activation action, and remove the superseded fixed-PR grant promise from every UI locale ([#12342](https://github.com/diegosouzapw/OmniRoute/pull/12342)) +- **feat(gamification):** the dashboard Profile page now shows the real daily streak — `/api/gamification/level` returns `streak: { current, longest }` (per key with `apiKeyId`, operator-wide maximum otherwise) and the streak card reads it instead of a hard-coded 0 (#2403) ([#12377](https://github.com/diegosouzapw/OmniRoute/pull/12377)) — thanks @pacocartones +- **feat(gamification):** the dashboard leaderboard now shows each API key's display name under the Name column instead of a truncated key id; `GET /api/gamification/leaderboard` attaches `name` per entry (name only — no key material), while the shared ranking helper and the federation leaderboard stay id-only — thanks @pacocartones ([#12385](https://github.com/diegosouzapw/OmniRoute/pull/12385)) +- **feat(gamification):** enforce the documented 1000 XP/min per-API-key anti-cheat rate limit on the XP award path; over-limit awards are logged and skipped instead of persisted, and the sliding window now matches the timestamp format stored in `xp_audit_log` ([#2403](https://github.com/diegosouzapw/OmniRoute/issues/2403)) ([#12390](https://github.com/diegosouzapw/OmniRoute/pull/12390)) — thanks @pacocartones +- **feat(admin):** localize the gamification anomalies page — the loading state, the Status column and the Suspicious badge now come from the `common` catalog (new `common.suspicious` key propagated to every locale) — add it to the Gamification sidebar group as `gamification-admin` (`/dashboard/gamification/admin`), and expose the loading and empty states as polite `role="status"` live regions (#12401 — thanks @pacocartones) +- **feat(providers):** skip GitHub combo members missing from the live synced catalog, and drop Copilot models that are policy-disabled or hidden from the model picker ([#12473](https://github.com/diegosouzapw/OmniRoute/pull/12473)) — thanks @RaviTharuma +- **feat(i18n):** locale-expansion tooling — `npm run i18n:add-locale` adds a language to config, dashboard, docs mirrors, CLI, README/indexes and site in one command; browser, CLI and cookie detection resolve `uk`, `fil`/`tl`, `zh-Hant` (and the retired legacy `in` → `id`) via config aliases; `config/i18n.json` now ships in the npm package so the published CLI can read it; new real-translation ratio gate (`npm run i18n:check-ratio`, advisory) with a per-locale ratchet baseline; `run-translation --adopt` restores `.i18n-state.json`; `sync-language-bars` generates 🌐 bars from config; `validate_translation.py` loads its allowlist again. Retires the duplicate `in` locale (Indonesian mislabelled as Hindi) — 42 honest locales; saved `NEXT_LOCALE=in` / `OMNIROUTE_LANG=in` keep working. (#12496) +- **feat(api):** Emit gateway-measured `tokens_per_second` (TTFT excluded) on streaming usage and `X-OmniRoute-Tokens-Per-Second` when first-token latency is known ([#12616](https://github.com/diegosouzapw/OmniRoute/issues/12616)) ([#12631](https://github.com/diegosouzapw/OmniRoute/pull/12631)) — thanks @RaviTharuma +- **feat(opencode-plugin): map gateway cost/usage/tok/s onto OpenCode inference payloads** — the official plugin copies `X-OmniRoute-Response-Cost`, token counts, `X-OmniRoute-Tokens-Per-Second` / `usage.tokens_per_second`, TTFT, and the winning `X-OmniRoute-Model` onto the JSON/SSE body OpenCode already consumes. Missing tok/s is left unset (never `tokens / latency`). (#12636) — thanks @RaviTharuma +- **feat(combo):** add combo strategy `quota-weighted`: skip exhausted accounts, then weighted-draw by leftover / in-flight load; existing conversations stay pinned until the account is empty ([#12789](https://github.com/diegosouzapw/OmniRoute/pull/12789)) — thanks @HouMinXi +- **feat(models):** Account live listings become the chat catalog source for Claude, Codex, Copilot, and AGY; public metadata only fills prices on IDs those accounts already list. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **fix(models):** Union `agy` and `antigravity` live catalogs so `agy/gemini-3.8-flash-high` is not rejected after the prefix folds to `antigravity`. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **feat(opencode):** Add `@omniroute/opencode-plugin-v2`, an opencode v2 plugin that gives a v2 install a route to an OmniRoute gateway: it publishes models, combos and auto-combos into the host catalog, refreshes them lazily behind a 300s TTL, and keeps serving the last known catalog from an on-disk snapshot when the gateway is unreachable. The v1 plugin (`@omniroute/opencode-plugin`) is untouched and keeps working as before. The plugin adapts to the host instead of assuming one shape: it reads the shape the host seeds into the catalog draft and publishes the `api` block, the pre-`api` top-level fields, or both. The gateway key comes from the host credential store when one is connected, so no secret needs to sit in a config file; a source the gateway refuses is named in a warning instead of silently degrading the catalog to raw model ids; and tool schemas bound for a Gemini model are stripped of the JSON-Schema keywords Gemini rejects, so tool calling survives that route ([#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870)) — thanks @maxmad64bis +- feat(services): show sanitized CLIProxyAPI account health from its authenticated management API without exposing credentials, file paths, or raw account metadata (#6342) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add AnySearch free web search + URL extract (webFetch) with typed results, credential validation, REST routing, and MCP selection - fallback-only ([#11690](https://github.com/diegosouzapw/OmniRoute/pull/11690)) — thanks @Xxx91n +- **feat(dashboard):** display clamped `[0, 100]%` cached input token ratio in request logs table ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) — thanks @b3nw +- **feat(catalog):** add `OMNIROUTE_DISABLE_THINKING_LEVEL_VARIANTS` feature flag to optionally filter out thinking level variants from model catalog ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) — thanks @b3nw +- **feat(dashboard):** continuously export call logs to external analytics stores. A pluggable destination registry ships the full Logs-tab record set on an hourly `JobRegistry` cron, with a persisted per-destination cursor, batched inserts, a config UI rendered from each destination's own field descriptors, and a REST layer (`/api/log-export/*`) for CRUD, a connection test, and an on-demand run. A destination can opt into `includeBodies` to also ship the request and response payloads shown in the Logs detail pane, including the client and provider views of each call; this is off by default, and payloads inherit the dashboard's PII sanitisation, secret redaction and `noLog` handling. Google BigQuery is the first destination, using a service-account key stored encrypted at rest and streaming inserts keyed by call-log id, into a table that is day-partitioned on `timestamp` and clustered on `api_key_name`, `provider`, `model` and `status`. ([#11945](https://github.com/diegosouzapw/OmniRoute/pull/11945)) — thanks @dpozimski +- **feat(providers):** add MaxAI as a signed, OpenAI-compatible provider serving its 13 paid chat models (GPT-5.6 / Luna / Thinking, Claude 5 Sonnet, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite, Grok 4.1-fast / 4.5, DeepSeek V3.2 / R1, Llama 3.3 70B) through OmniRoute's `/v1` endpoint, with per-request HMAC-SHA1→SM3→AES request signing, live model + context-window discovery from `/models/get_config`, and prompted tool-calling translated to OpenAI `tool_calls` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI vision input — image_url content parts are forwarded inline in `message_content` to the 6 vision-capable models (GPT-5.6 / Luna / Thinking, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite) ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI image generation — 6 image models (gpt-image-1, dall-e-3, flux-1-schnell/dev/pro, sd3-medium) exposed through `POST /v1/images/generations` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI document RAG — inline base64 file/document attachments are uploaded to MaxAI (content-addressed `doc_id`) and attached to the chat via `doc_list` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** browserless MaxAI onboarding — email device-pair login (`/api/providers/[id]/login`) and signed access-token refresh, so a connection can be created and kept fresh without a real browser or Google OAuth ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** per-provider TLS impersonation profile (MaxAI presents a Windows Firefox-150 client fingerprint) so its bot-sensitive endpoints accept OmniRoute traffic ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** add **Nimble** as a web-search and web-fetch provider (`nimble-search`) — `/v1/search` routes to Nimble's search API at `lite` depth (locale, freshness and include/exclude domain filters mapped onto the shared request contract), and `/v1/web/fetch` routes to Nimble Extract, which covers all four fetch formats (`markdown`, `html`, `links`, `screenshot`) from a single call. One API key serves both surfaces. ([#11620](https://github.com/diegosouzapw/OmniRoute/pull/11620)) — thanks @wildcard +- The npm publish is automatic again, through npm Trusted Publishing (OIDC): the hosted `stage-npm` job publishes with a short-lived credential minted from GitHub's id-token — no `NPM_TOKEN`, no 2FA prompt, provenance attached. `publish_mode=staged` (owner approves with 2FA) and `direct` (token) remain available on `workflow_dispatch`. ([#11931](https://github.com/diegosouzapw/OmniRoute/pull/11931)) +- **feat(providers):** Add **Opper** as an API-key gateway provider — EU-hosted AI gateway with 700+ models from 30+ providers behind one OpenAI-compatible API and one key (`OPPER_API_KEY`); model ids use `provider/model` format (e.g. `anthropic/claude-sonnet-4-6`, `openai/gpt-5`); live model catalog at `https://api.opper.ai/v3/compat/models`; entry mirrors `requesty` (same shape, `passthroughModels: true`, no static seed) ([#11629](https://github.com/diegosouzapw/OmniRoute/pull/11629)) +- **feat(dashboard):** the `/dashboard/orchestration` snapshot hook now subscribes to the `agents` WebSocket channel (`agent.task.updated`) instead of `requests` as its refetch trigger, and relaxes its background poll from 5s to 30s while that WS connection is up — falling back to the tighter 5s cadence, reprogrammed live on any connect/disconnect transition, whenever the socket is down. ([#12409](https://github.com/diegosouzapw/OmniRoute/pull/12409)) +- **feat(dashboard):** Orchestration canvas quick wins — search box plus state/source/provider filter chips with a one-click clear, and per-source collapse/expand, all reflected in the URL so a filtered/collapsed view is shareable and survives a refresh; the detail drawer gained a "copy trace JSON" action and hardened error/empty-state and accessibility handling; the Agents-tab edges now animate traveling particles along active (running) connections; and the canvas node/edge status colors moved off fixed hex values onto theme-aware `--orch-status-*` CSS custom properties, so they adapt correctly to light/dark mode. ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- **chore(dashboard):** Orchestration UI hardening pass and the missing component/model test coverage it called for — `OrchestratorNode`/`ActivityNode`/`OverflowNode` rendering, the `?node=`/overflow-click page routing, the Agents-tab orchestrator-click no-op and `showCompleted` toggle, and the overview kanban's done-column sort order (#12270, #12271). ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- New `/dashboard/orchestration` page: live unified view of everything running — Cloud Agent, A2A and Conductor as a real-time graph (Agents tab), the combo cascade (Routing tab, reusing the Combo Live Studio) and a state kanban (Overview tab), with a detail drawer (trace, cost, approve/cancel). Read-only over existing APIs — no new backend. Canvas concept credit: PR #11815 design ([#12261](https://github.com/diegosouzapw/OmniRoute/pull/12261)) +- **feat(dashboard):** new "History" tab on `/dashboard/orchestration` — an Airflow-style grid of finished runs over a 24h/7d/30d preset window, one row per (source, identity), clicking a cell opens the existing detail drawer. It is backed by real persistence: A2A task lifecycle transitions are now written to the `a2a_tasks` table (purged after 30 days, configurable via `OMNIROUTE_A2A_HISTORY_RETENTION_DAYS`) and served by the new `GET /api/a2a/tasks/history` listing endpoint, with the task-detail route falling back to persisted history once a run leaves the in-memory snapshot. Conductor runs stay remote and are not persisted locally — the tab says so instead of silently omitting them. ([#12479](https://github.com/diegosouzapw/OmniRoute/pull/12479)) +- **feat(dashboard):** the orchestration detail drawer gained a "Repeat" action for Cloud Agent, A2A and Conductor tasks — a two-click confirm (click once to arm, click again within the confirm window to fire) re-submits the original prompt/input as a new run. The button is disabled with an explanatory tooltip whenever the original input can't be recovered from the loaded task detail (e.g. it never carried a prompt, or the detail failed to load). Two limitations of the A2A variant, by design: it targets the `/a2a` JSON-RPC endpoint, which authenticates with an API key only (`REQUIRE_API_KEY=true` or a configured `OMNIROUTE_API_KEY` makes a dashboard-session repeat answer `HTTP 400` — surfaced verbatim in the drawer's error line, never as a success), and the `message/send` call is SYNCHRONOUS: the POST blocks for the whole skill run, so the success confirmation only appears once the repeated task finishes. A dashboard-authenticated A2A creation path is deliberately left to a follow-up — widening the endpoint's auth posture is an operator decision, not a side effect of this feature. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(a2a):** A2A task execution now records which memories were consulted for the task's last user message as `metadata.memoryHits` (id/key/type/content-snippet) plus a `memory_hits` history event, purely for observability — the retrieved memory is never injected into a skill's prompt or behavior. Gated by the `OMNIROUTE_A2A_MEMORY_HITS` kill-switch (default enabled; set to `0` to skip the recall lookup entirely). The drawer's new "Memory used" section lists these hits for a2a tasks and is omitted whenever there are none. Known limitation: recall only resolves under the keyless posture — a keyed caller's task owner is a SHA-256 prefix of the API key, while memory rows are keyed by the database api-key id, and no hash→id lookup exists today, so the hit list stays empty for keyed callers. The recorded hits are also kept out of the task's own `input` (and therefore out of the persisted input and of the "Repeat" request body), so repeating a task never re-sends the previous run's memory snippets. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(providers):** add a Perplexity Agent API provider (`perplexity-agent` / `pplx-agent`) for Perplexity `/v1/responses`, including the documented Anthropic, OpenAI, Google, xAI, DeepSeek, Z.AI, Moonshot/Kimi, NVIDIA, and Perplexity model IDs plus Anthropic-model `max_output_tokens` compatibility. ([#12103](https://github.com/diegosouzapw/OmniRoute/pull/12103)) — thanks @AIB1TAL0S +- **feat(providers):** add RPD (Requests Per Day) limit to provider rate limit overrides across UI, schemas, DB, and i18n ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) — thanks @b3nw +- **feat(providers): add UC Direct (uncensored.com Developer API), the metered OpenAI-compatible surface.** A standard OpenAI-compatible passthrough (default executor) for uncensored.com's official REST API at `https://api.uncensored.com/api/v1`: `X-api-key` auth (never-expiring `uai_sk_live_` key), `POST /chat/completions` with streaming SSE and native tool-calling, and the full live metered catalog (82 models across 15 providers, discovered from the public `GET /v1/models`). Registered as provider `uc-direct` (alias `ucd`). Complements the un-metered `uc` persona provider — same models, metered credits and a plain API key instead of a subscription session. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(providers): add UC (uncensored.com), the un-metered subscription "persona" chat as an OpenAI-compatible provider.** A WebSocket web-app port: a durable Clerk credential mints a short-lived session token per connect (browserless — no API key), driving UC's persona socket. Ships the browserless email-code login (request → verify → harvest), the 19 verified persona models (Claude Opus, Gemini, Grok, GLM, Kimi, DeepSeek, MiniMax, incl. the uncensored variants), prompted `` tool-calling with a per-model code-style dialect + auto-cure retry for guardrailed models, live ``/reasoning split, streaming + non-streaming OpenAI responses, and full quota/auth error surfacing (paywall / message-limit / rate-limit → 429, invalid session → 401 re-login). Full multimodal parity via the persona blob-upload layer: **vision** (image input, 15 vision-capable models), **document RAG** (PDF/doc upload, server-side extraction), **image generation** (22 models), **video generation** (14 models, async signed-url → poll), and **TTS** (streaming MP3). Registered as provider `uc` (alias `ucn`). The metered OpenAI-compatible Developer API is a separate `uc-direct` provider. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(logs):** add copy-all button to request detail modal ([#11083](https://github.com/diegosouzapw/OmniRoute/pull/11083)) — thanks @AndrianBalanescu +- **feat(check):** make serviceKinds required and add the reverse-walk provider consistency gate ([#11392](https://github.com/diegosouzapw/OmniRoute/pull/11392)) — thanks @Tushar49 +- **feat(providers):** only advertise verified-working connections by default ([#11448](https://github.com/diegosouzapw/OmniRoute/pull/11448)) — thanks @alltomatos +- **feat(resilience):** add hierarchical concurrency admission ([#11493](https://github.com/diegosouzapw/OmniRoute/pull/11493)) +- **feat(providers):** derive + surface expiry for JWT-bearing web cookies (#11497) ([#11505](https://github.com/diegosouzapw/OmniRoute/pull/11505)) — thanks @oyi77 +- **feat(tunnels):** support named cloudflared tunnels via CLOUDFLARED_CONFIG ([#11561](https://github.com/diegosouzapw/OmniRoute/pull/11561)) — thanks @ggiak +- **feat(compression):** make proactive context-compression threshold a live setting ([#11564](https://github.com/diegosouzapw/OmniRoute/pull/11564)) — thanks @ragnar-claude +- **feat(resilience):** add provider peak-hour protection ([#11622](https://github.com/diegosouzapw/OmniRoute/pull/11622)) — thanks @Neuron-Mr-White +- **feat(providers):** add 1min.ai provider ([#11631](https://github.com/diegosouzapw/OmniRoute/pull/11631)) — thanks @zero-executioner +- **feat(dashboard):** collapsible JSON tree viewer for request/response payloads ([#11703](https://github.com/diegosouzapw/OmniRoute/pull/11703)) — thanks @hartmark +- **feat(routing):** add LiquidAI LFM2.5-2.6B free tier via OpenRouter ([#11752](https://github.com/diegosouzapw/OmniRoute/pull/11752)) — thanks @brick30llc-ctrl +- **feat(pricing):** add GLM-5.3-Flash pricing, model specs, and catalog registration ([#11830](https://github.com/diegosouzapw/OmniRoute/pull/11830)) — thanks @AndrianBalanescu +- **feat(routing):** add relayMode for schema-locked context handoffs ([#11839](https://github.com/diegosouzapw/OmniRoute/pull/11839)) — thanks @raheemuddin786 +- **feat(config):** add support for runtime system prompt configuration and hot-reloading ([#11841](https://github.com/diegosouzapw/OmniRoute/pull/11841)) — thanks @raheemuddin786 +- **feat(routing):** expand connection-aware quota prefilter across combo strategies (#11682) ([#11850](https://github.com/diegosouzapw/OmniRoute/pull/11850)) — thanks @HouMinXi +- **feat(models):** reactive discovery sync after Antigravity model-not-found 404 ([#11888](https://github.com/diegosouzapw/OmniRoute/pull/11888)) — thanks @MumuTW +- **feat(models):** auto-aliases and token limits from synced Antigravity discovery ([#11889](https://github.com/diegosouzapw/OmniRoute/pull/11889)) — thanks @MumuTW +- **feat(settings):** raise sticky round-robin limit caps to 1000 ([#12015](https://github.com/diegosouzapw/OmniRoute/pull/12015)) — thanks @adivekar-utexas +- **feat(resilience):** operator-configurable global credential health check interval ([#12043](https://github.com/diegosouzapw/OmniRoute/pull/12043)) — thanks @adivekar-utexas +- **feat(quota):** use official OpenCode Go usage API ([#12124](https://github.com/diegosouzapw/OmniRoute/pull/12124)) — thanks @ddarkr +- **feat(resilience):** default the credential health check sweep to 60 minutes ([#12138](https://github.com/diegosouzapw/OmniRoute/pull/12138)) — thanks @adivekar-utexas +- **feat(quality):** new-code mode for the complexity and dead-code ratchets (Clean as You Code) ([#12142](https://github.com/diegosouzapw/OmniRoute/pull/12142)) +- **feat(dashboard):** orchestration canvas — unified model + snapshot hook (part 1/2) ([#12156](https://github.com/diegosouzapw/OmniRoute/pull/12156)) +- **feat(usage):** add Kilo Code balance and Kilo Pass quotas ([#12178](https://github.com/diegosouzapw/OmniRoute/pull/12178)) — thanks @killer30001000 +- **feat(providers):** refresh vendored ChatGPT Web connector to v4.0.7 ([#12181](https://github.com/diegosouzapw/OmniRoute/pull/12181)) — thanks @backryun +- **feat(providers):** manual "Clear cooldown" action in the cooling panel ([#12224](https://github.com/diegosouzapw/OmniRoute/pull/12224)) — thanks @patrykkopycinski +- **feat(providers):** restore ChatGPT Web via clean-room browser transport ([#12239](https://github.com/diegosouzapw/OmniRoute/pull/12239)) — thanks @backryun +- **feat(sse):** wire the PROVIDER_PROFILES window gate into the global provider cooldown ([#12247](https://github.com/diegosouzapw/OmniRoute/pull/12247)) +- **feat(providers):** modernize CLOVA Studio chat and embeddings ([#12277](https://github.com/diegosouzapw/OmniRoute/pull/12277)) — thanks @backryun +- **feat(browser):** adopt Obscura as primary headless browser engine with Chromium fallback ([#12286](https://github.com/diegosouzapw/OmniRoute/pull/12286)) — thanks @opensource-elearning +- **feat(sse):** full language parity for output styles (es/de/fr/it/ru/zh + autoDetect) ([#12289](https://github.com/diegosouzapw/OmniRoute/pull/12289)) +- **feat(quality):** complete test:scoped — --full map rebuild, stdin selection, CI loader parity (#8084 D1) ([#12353](https://github.com/diegosouzapw/OmniRoute/pull/12353)) +- **feat(providers):** refresh Fable, Cursor, and Devin catalogs ([#12367](https://github.com/diegosouzapw/OmniRoute/pull/12367)) — thanks @backryun +- **feat(video):** redact transcript text from logs and durable memory (#12150 P1) ([#12427](https://github.com/diegosouzapw/OmniRoute/pull/12427)) +- **feat(dashboard):** parent-link, genuine-continuation badge, and modal perf fixes ([#12448](https://github.com/diegosouzapw/OmniRoute/pull/12448)) — thanks @hartmark +- **feat(dashboard):** adaptive context-budget dial on compression panel ([#12488](https://github.com/diegosouzapw/OmniRoute/pull/12488)) — thanks @HouMinXi +- **feat(providers):** refresh Z.ai Web models and browser transport ([#12524](https://github.com/diegosouzapw/OmniRoute/pull/12524)) — thanks @backryun +- **feat(video):** redact raw client-snapshot transcript fields in the detailed log (#12150 P2) ([#12528](https://github.com/diegosouzapw/OmniRoute/pull/12528)) +- **feat(providers):** refresh NVIDIA hosted models ([#12538](https://github.com/diegosouzapw/OmniRoute/pull/12538)) — thanks @backryun +- **feat(quota):** Moonshot Open Platform balance and TPD lock for custom nodes ([#12590](https://github.com/diegosouzapw/OmniRoute/pull/12590)) — thanks @HouMinXi +- **feat(video):** redact transcript in the in-memory pending-request snapshot (#12430 item 6) ([#12596](https://github.com/diegosouzapw/OmniRoute/pull/12596)) +- **feat(dashboard):** link a log entry's Conversation Context to its owning conversation ([#12646](https://github.com/diegosouzapw/OmniRoute/pull/12646)) — thanks @hartmark +- **feat(catalog):** eligibility-gated free-tier bucket ([#12669](https://github.com/diegosouzapw/OmniRoute/pull/12669)) +- **feat(db):** fail-closed previous_response_id continuation for redacted video turns (#12150 P2b) ([#12707](https://github.com/diegosouzapw/OmniRoute/pull/12707)) +- **feat(gamification): pay the documented `streak_bonus` and `badge_unlock` XP rewards.** `XP_REWARDS` listed both rewards but the award pipeline never paid them: the private reward table in `events.ts` omitted them, `updateStreak()` did not report when a streak extended, and badge unlocks carried no XP. Every request that extends a daily streak now pays `streak_bonus × streak length` once per UTC day (guarded by a same-day `xp_audit_log` check), and every badge unlocked through the pipeline pays `badge_unlock` once per badge (guarded by the `user_badges` primary key; `unlockBadge()` now reports whether it inserted). Bonus XP flows through the same `addXp` + level sync + global/weekly/monthly leaderboard path as action XP, so level-ups and rankings include it. The Radar supporter recognition unlock stays XP-free. (#12522 — thanks @pacocartones) +- **feat(providers):** advertise a `free-tier` capability in the provider plugin manifest for every provider with documented free models, so sidecars and dashboards can filter free-capable providers without reading the quota catalog ([#12786](https://github.com/diegosouzapw/OmniRoute/pull/12786)) — thanks @maxmad64bis +- **feat(providers):** Added EURouter as an OpenAI-compatible API-key gateway (`https://api.eurouter.ai/v1`), with live model discovery via `passthroughModels`. Its copy states that models are served by third-party upstreams listed per model, so an EU-based router is not read as EU data residency for inference. ([#13025](https://github.com/diegosouzapw/OmniRoute/pull/13025)) — thanks @ntdatt812 +- **feat(providers):** Added GreenPT as an OpenAI-compatible API-key provider (`https://api.greenpt.ai/v1`), with live model discovery via `passthroughModels`. No free-inference badge: the published docs describe a free API subscription billed per token, not a free tier. ([#13024](https://github.com/diegosouzapw/OmniRoute/pull/13024)) — thanks @ntdatt812 +- **feat(i18n):** 9 new locales — Greek (`el`), Croatian (`hr`), Serbian (`sr`), Lithuanian (`lt`), Estonian (`et`), Latvian (`lv`), Slovenian (`sl`), Maltese (`mt`), Irish (`ga`) — across the dashboard, docs mirrors, CLI, README and the site; OmniRoute now ships every official EU language (51 locales). (#13044) +- **feat(i18n):** 8 new locales — Kannada (`kn`), Malayalam (`ml`), Odia (`or`), Punjabi (`pa`), Nepali (`ne`), Sinhala (`si`), Burmese (`my`), Khmer (`km`) — across the dashboard, docs mirrors, CLI, README and the site (59 locales). The translator now restores the ICU literal escape around angle placeholders and splits oversized docs sections before translating. (#0000) ([#13660](https://github.com/diegosouzapw/OmniRoute/pull/13660)) +- feat(providers): **list Agnes 3.0 Flash as the current free chat model, drop retired 1.5 Flash, add Image 2.0/2.5 Flash plus Video 2.5/2.5 Flash, and discover the live `/v1/models` catalog (including the CN host `api.agnes-ai.cn`).** `agnes-1.5-flash` now forwards to `agnes-3.0-flash`. Video 2.5 polls `GET /v1/videos/{id}` (not the V2.0 `/agnesapi` contract). Live `/v1/models` (2026-09-09) no longer serves 1.5; the wiki marks it deprecated. 3.0 Flash is 512K context / 65,536 max output, same window as 2.5. CN-region keys use the existing per-connection base-URL field, default stays `apihub.agnes-ai.com`. ([#13120](https://github.com/diegosouzapw/OmniRoute/pull/13120)) — thanks @HouMinXi +- **feat(sse):** Codex and OpenAI catalogs list GPT-6 Astra with effort aliases (`-low` through `-ultra`); Codex CLI identity pins `@openai/codex@0.153.4` in lockstep with the image ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **feat(dashboard):** the orchestration History tab gained a "Compare runs" mode — toggling it turns each grid cell into a 2-item selection queue (a 3rd click drops the oldest pick), and picking two cells opens a side-by-side comparison panel instead of the usual detail drawer. The panel fetches both runs' detail the same way the drawer does (falling back to persisted history once a run leaves the live TTL window) and shows, per side: identity/source/state, start time, a signed `right - left` delta for duration/cost/event count, the event timeline aligned by index, and any memory hits. One side's fetch failing never blocks the other, and a delta is only ever computed when both sides have a finite value — otherwise it renders "—", never `NaN`. Comparing two runs from different sources or skills still works; a banner marks the deltas as informational rather than hiding them, since the two runs aren't a strict apples-to-apples pair. ([#12677](https://github.com/diegosouzapw/OmniRoute/pull/12677)) +- **feat(dashboard):** badge a conversation that never reached a clean stop ([#12717](https://github.com/diegosouzapw/OmniRoute/pull/12717)) — thanks @hartmark +- **feat(release):** reconcile-changelog tool + version-anchored fragment aggregation ([#12987](https://github.com/diegosouzapw/OmniRoute/pull/12987)) +- **feat(mcp):** carry context_length through MCP list_models_catalog projection (#12776) ([#13406](https://github.com/diegosouzapw/OmniRoute/pull/13406)) — thanks @KooshaPari +- **feat(cli):** make startup readiness budget configurable (#13369) ([#13433](https://github.com/diegosouzapw/OmniRoute/pull/13433)) — thanks @KooshaPari + ### 🐛 Bug Fixes +- **security(streaming):** sanitize generic mid-stream error messages before emitting OpenAI, + Responses, or Claude SSE failure frames and before diagnostic logging, while preserving raw + failures for internal classification and keeping client disconnects out of provider failure state. ([#12457](https://github.com/diegosouzapw/OmniRoute/pull/12457)) +- **fix(claude):** `blockExtraUsage: false` no longer lets 5h quota preflight skip the account; extra usage is billed after the session bar is gone, so the request must reach Anthropic ([#12803](https://github.com/diegosouzapw/OmniRoute/pull/12803)) — thanks @HouMinXi +- Sanitize HuggingChat conversation-creation and message-send transport failures before they reach client error bodies or provider logs. ([#12467](https://github.com/diegosouzapw/OmniRoute/pull/12467)) +- **fix(dashboard):** Keep local and theme-aware provider SVG icons at a definite layout size so Chromium does not collapse them to 0×0 after the v3.8.50 image-rendering change ([#12054](https://github.com/diegosouzapw/OmniRoute/pull/12054)) — thanks @ponkcore +- **fix(security):** Sanitize provider and runtime failures before public API, SSE and MCP responses and before persistent request, proxy and usage logs, preventing credentials, stack traces and host filesystem paths from crossing those boundaries while preserving stable error codes and useful diagnostics. ([#12506](https://github.com/diegosouzapw/OmniRoute/pull/12506)) +- **fix(cli):** `omniroute tunnel create` no longer crashes with `Cannot read properties of undefined (reading optsWithGlobals)` — removed the duplicate positional argument that caused Commander.js to misalign the action callback parameters ([#12295](https://github.com/diegosouzapw/OmniRoute/issues/12295)) ([#12368](https://github.com/diegosouzapw/OmniRoute/pull/12368)) — thanks @gonisulaimann +- Stop advertising Gemini Live-only models as supported audio endpoints until OmniRoute proxies the bidirectional Live protocol. ([#11710](https://github.com/diegosouzapw/OmniRoute/pull/11710)) — thanks @RaviTharuma +- **fix(kie):** reroute `flux/kontext` off the KIE Market `createTask` flow — it is catalogued with `isMarket: true` but has no Market catalog page, so KIE rejected it with "model name not supported"; it now hits the dedicated `POST /api/v1/flux/kontext/generate` / `GET /api/v1/flux/kontext/record-info` endpoints instead (#11296). ([#11985](https://github.com/diegosouzapw/OmniRoute/pull/11985)) — thanks @hartmark +- **fix(kie):** correct 12 more KIE Market catalog ids that were sent to `createTask` unchanged but diverge from KIE's documented upstream `model` values — GPT Image 2 T2I/I2I (drops the `gpt/` prefix), GPT Image 1.5 T2I/I2I (`gpt-image/` namespace), Seedream 5.0 Lite T2I/I2I (drops the `.0`), all 4 Flux 2 variants (`flux-2/` namespace, generic variant renamed `flex`), and Wan 2.7 Image / Image Pro (dash instead of dot) — each verified individually against the literal example request published on docs.kie.ai. `#11326`'s "everything else already matches" claim was wrong a second time (#11296); `z-image/4.0-*`/`z-image/4.5-*` and `flux/kontext` remain open, documented as unresolved in `KIE_MARKET_UPSTREAM_MODEL_IDS`'s comment pending further verification. ([#11667](https://github.com/diegosouzapw/OmniRoute/pull/11667)) +- **fix(dashboard):** `useApiKeySave.handleSaveApiKey` no longer forces a full upstream `/models` catalog sync on every non-curated provider connection save — callers can now pass `skipModelSync: true` to opt out, so a workflow that only wants to add one manual model no longer floods the provider's available-models list with hundreds/thousands of synced entries. The flag is a client-side intent signal only and is stripped before the connection payload is POSTed to `/api/providers`; default behavior (full sync on save) is unchanged when the flag is omitted (#11324) ([#11664](https://github.com/diegosouzapw/OmniRoute/pull/11664)) +- **Provider connections:** keep `tokenExpiresAt` when a connection is created. The create-path allowlist omitted it, so every insert stored NULL and the dashboard token badge could read a fresh connection as expired until its first background refresh ([#11368](https://github.com/diegosouzapw/OmniRoute/pull/11368)). — thanks @ntdatt812 +- **fix(combos):** the combo builder's precision-select, global-model-search, and manual-entry flows now serialize a model step's `model` string using the provider's already-computed routing-alias prefix (e.g. `oc/`) instead of rebuilding it from the raw canonical `providerId`, fixing the no-auth "OpenCode Free" provider (`opencode`) being routed to the unrelated paid "OpenCode Zen" provider (`opencode-zen`) because `opencode` doubles as a manual routing-prefix override ([#11433](https://github.com/diegosouzapw/OmniRoute/issues/11433)). ([#11670](https://github.com/diegosouzapw/OmniRoute/pull/11670)) +- **fix(radar):** the catalog feed cache now keeps `generatedAt`, the date the feed's data was built, next to `fetchedAt`, the date this install downloaded it (#11435). The feed schema requires that date and the sync path validates it, but the cache dropped it — so a feed fetched minutes ago and one carrying weeks-old figures looked identical to everything downstream, including the dashboard's "Last fetched" line. `getRadarCatalog().meta` and `GET /api/radar/status` now report both dates, the latter as its own field rather than folded into `version` — and omitted entirely for the offers and intel caches, which keep no build date, where a `null` would read as "unknown" rather than "never stored". The dashboard still shows only the fetch time; surfacing the build date there needs a new translated label and is left to a follow-up. Rows cached before migration 163 read back as `null`: unknown stays unknown instead of borrowing the fetch time. The referrals cache has persisted the same date since migration 142. — thanks @maxmad64bis +- **fix(cli):** postinstall no longer fills `JWT_SECRET` and `API_KEY_SECRET` in the installed package's `.env` (#11436). `.env.example` ships both blank on purpose: the server restores them from its durable store, or generates and persists them there, in `ensureSecrets()`. Pre-filling them defeated that — the file lives inside the package directory, so `npm i -g` replaced it and postinstall wrote _different_ values, while `ensureSecrets()` (which only acts on an empty variable) never got to restore the real ones. Both secrets rotated silently on every update, invalidating dashboard sessions and API-key CRCs. `STORAGE_ENCRYPTION_KEY` left the same list for the same reason in #1622; its comment pointed at a function that no longer exists and now names the real provisioning path. — thanks @maxmad64bis +- **fix(cli):** the CLI now says when a loaded `.env` lives inside the installed package directory (#11437). It already announces every env file it reads, without distinguishing the ones that survive an update from the one that does not: `npm i -g` replaces the package directory wholesale, so values set there are gone at the next update, silently. The warning names the durable path to move them to, and fires only when that file actually supplied a value — a file entirely shadowed by a durable one supplied nothing. A development checkout stays silent: there the same path is stable and documented in `SETUP_GUIDE.md`. — thanks @maxmad64bis +- **fix(free-models):** the shared `isFreeModel()` predicate no longer reports catalog entries marked `freeType: "discontinued"` as free, so `hidePaidModels` can't route to Pollinations' seven premium models that now require a paid key ([#11441](https://github.com/diegosouzapw/OmniRoute/pull/11441)) — thanks @maxmad64bis +- fix(ui): let AnySearch use the normal provider-icon fallback when LobeHub has no matching icon (#11449) +- Fixed the v3.8.50 Costs and Analytics dashboards so flat-rate Claude Code usage can be shown as an explicitly requested token-price estimate without changing default billed-cost semantics. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed archived usage retention so each request is priced individually instead of pricing a day's summed tokens once, which understated archived cost whenever a day mixed cache-heavy and ordinary requests. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed the Costs dashboard so it discloses when displayed figures include flat-rate token-price estimates instead of labelling them as billed spend, using the flag the analytics API already returns; the month-end projection and the CSV/JSON exports carry the same marker, and billed-cost mode is unchanged. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- **fix(combo):** attach the same combo-diagnostics trace (`poolSize`/`attemptOrder`/`excluded`/`terminalReason`, plus `x-omniroute-combo-*` headers) to the round-robin strategy's and the nested pipeline/fusion runtime-unit loop's "Maximum combo retry limit reached" 503 that the priority-strategy path already attaches for the identical terminal condition — previously those two paths returned a bare, contextless 503 ([#11462](https://github.com/diegosouzapw/OmniRoute/issues/11462)). ([#11665](https://github.com/diegosouzapw/OmniRoute/pull/11665)) +- **fix(bun):** use native `bun:sqlite` in `bootstrap-env` and `sync-env` to avoid loading `better-sqlite3` N-API addon during Bun startup ([#11468](https://github.com/diegosouzapw/OmniRoute/pull/11468)) — thanks @TheDemonTuan +- **fix(docker-bun):** make `Dockerfile.bun` install reliable with pre-install script helpers and native `bun:sqlite` smoke check ([#11470](https://github.com/diegosouzapw/OmniRoute/pull/11470)) — thanks @TheDemonTuan +- **fix(docker-bun):** remove every vendored `better-sqlite3` native addon from the Bun runtime image so startup cannot abort before the `bun:sqlite` fallback ([#11482](https://github.com/diegosouzapw/OmniRoute/pull/11482)) — thanks @TheDemonTuan +- **fix(autoCombo):** effort/alias model variants (`gpt-5.6-sol-xhigh`, `gpt-5.6`, cursor's `claude-4.6-opus-high`) inherit their base model's task fitness instead of falling to the wildcard 0.5 ([#11492](https://github.com/diegosouzapw/OmniRoute/pull/11492)) — thanks @MumuTW +- fix(db): rate-limit repeated Arena ELO leaderboard fetch-failure warnings instead of logging one per sync attempt (#11500) ([#11989](https://github.com/diegosouzapw/OmniRoute/pull/11989)) — thanks @hartmark +- **fix(db):** dedupe the raw `[Encryption] Decryption failed...` log line emitted by the lazy-decrypt views (`createLazyRowProxy`/`createLazyConnectionView`), which power `getProviderConnections()` and were re-triggering that line on every CredentialHealth/model-sync cycle for the same corrupt or stale-key credential — a fresh Proxy over a fresh row on every cycle meant the per-proxy memoization never suppressed it, unlike the dedup `decryptConnectionFields()` already had since [#9927](https://github.com/diegosouzapw/OmniRoute/issues/9927). Now shares that dedupe tracking so the line logs at most once per credential ([#11500](https://github.com/diegosouzapw/OmniRoute/issues/11500)). ([#11678](https://github.com/diegosouzapw/OmniRoute/pull/11678)) +- **fix(intelligence):** Arena ELO sync stores a synthesized base-model row for every effort/harness variant (`claude-opus-5-max` → `claude-opus-5`, `gpt-5.6-sol-xhigh (codex-harness)` → `gpt-5.6-sol`), so bare model requests reach the synced intelligence layer instead of the static fallback table; the hand-maintained `MODEL_ALIAS_MAP` — which collapsed generations (`gpt-5.5` → `gpt-5`) — is gone ([#11506](https://github.com/diegosouzapw/OmniRoute/pull/11506)) — thanks @MumuTW +- **fix(autoCombo,sse):** catalog hygiene — the static task-fitness table no longer ranks retired models above live ones (dead rows dropped, patterns matched on segment boundaries), the 7 deprecation aliases that forwarded to retired or non-existent models now point at the vendors' published replacements, an alias is no longer rewritten when the serving provider offers the id as-is, and a new `check:model-lifecycle` gate diffs both tables against a vendor lifecycle snapshot ([#11507](https://github.com/diegosouzapw/OmniRoute/pull/11507)) — thanks @MumuTW +- **fix(sse):** stop dropping resolved `thoughtSignature` values on parallel (multi tool-call) turns sent to Gemini 3.x — the claude→gemini and openai→gemini translators previously kept the signature only on the *first* function call of a message, causing Gemini to reject subsequent calls in the same turn with HTTP 400 "Function call is missing a thought_signature"; each function call now keeps its own resolved signature ([#11510](https://github.com/diegosouzapw/OmniRoute/issues/11510)). ([#11669](https://github.com/diegosouzapw/OmniRoute/pull/11669)) +- Treat Docker publishing from a non-default release branch as an expected skip instead of a failed workflow, while preserving the guard that prevents it from overwriting the `next` channel. ([#11527](https://github.com/diegosouzapw/OmniRoute/pull/11527)) — thanks @pacocartones +- fix(search): prefer credentialed providers over duckduckgo-free fallback (#11524) — the fallback-only loop ran before the credentialed-providers loop in `executeWebSearch`, making configured providers unreachable when `duckduckgo-free` was available. ([#11565](https://github.com/diegosouzapw/OmniRoute/pull/11565)) — thanks @vsd2807 +- **fix(sse):** cap the upstream headers-wait phase for STREAMING requests to a client-realistic ceiling (110s, under Codex's own ~120s hard client-abort window) instead of the flat 10-minute `FETCH_TIMEOUT_MS` default — that default was 5x longer than the body-phase readiness watchdog's own adaptive bound, so a request whose upstream never returned any response at all (not even headers, e.g. a stalled NVIDIA target behind a tool-heavy Responses→Chat translation) kept the client connection alive on keepalives only, guaranteeing the client's own patience ran out first with an opaque 499 instead of OmniRoute detecting and failing the stall fast. Non-streaming requests are unaffected — they keep the existing flat default (`open-sse/utils/fetchStartTimeoutPolicy.ts`) (#11526) ([#11668](https://github.com/diegosouzapw/OmniRoute/pull/11668)) +- **fix(ci):** protocol-clients E2E harness boots the peer-stamped custom server (`run-next.mjs dev`) instead of the bare `next` CLI, so `LOCAL_ONLY` locality resolves from the real TCP peer stamp and `/api/mcp/audit` answers 200 (open bootstrap preserved via an `OMNIROUTE_E2E_BOOTSTRAP_MODE=open` credential clear in `run-next.mjs`, pinned to loopback `HOST`) instead of a deterministic 403 (#11535) ([#11549](https://github.com/diegosouzapw/OmniRoute/pull/11549)) — thanks @jonlwheat2-gif +- **fix(docs):** the Auto-Combo engine is described with the number of factors it actually declares. Four different counts were in circulation — 6, 9, 13 and 14 across `AGENTS.md`, four guides and six code comments — while `DEFAULT_WEIGHTS` declares 15. `check:docs-counts` now reads that number from the source, so adding a factor without updating the prose fails CI ([#11545](https://github.com/diegosouzapw/OmniRoute/pull/11545)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **fix(quota-share):** in-flight leases are per request, so an aborted request's slot ages out instead of being kept alive by later traffic on the same connection ([#11547](https://github.com/diegosouzapw/OmniRoute/pull/11547)) — thanks @abhisheksharma2411 +- The free-tier summary route now serves the refreshed Radar catalog when the feed is active — the same numbers the dashboard shows — and states which catalog answered plus its real build date, instead of always reporting release-frozen figures with a stale curation date. The supporter-key live feed stays reserved to authenticated callers of the instance. ([#11550](https://github.com/diegosouzapw/OmniRoute/pull/11550)) — thanks @maxmad64bis +- **sse:** fix LiveWS/embed-WS servers crashing at startup under the Node/tsx runtime — `liveServer.ts`, `embedWsProxy.ts` and `apiBridgeServer.ts` imported `@/shared/utils/httpClientAbortGuard` without the `.mjs` extension, so the client-abort crash guard added by [#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556) was unreachable and every dependent test failed with `ERR_MODULE_NOT_FOUND` ([#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556)). ([#11671](https://github.com/diegosouzapw/OmniRoute/pull/11671)) — thanks @jonlwheat2-gif / @yxyxy +- fix(chat): shed any of the matched provider-node's routing identifiers (public prefix or internal id) before model lookup, so composites like `//` no longer reach upstream verbatim (#11557) — thanks @maxmad64bis +- **fix(cache):** a semantic-cache entry now expires on its TTL instead of surviving until the next UTC midnight, and `dbEntries` stops counting expired rows ([#11573](https://github.com/diegosouzapw/OmniRoute/pull/11573)) — thanks @ntdatt812 +- **fix(api):** `/v1/models` schedules its stale-while-revalidate rebuild through Next's `after()` again, so a stale catalog reaches the client before the rebuild blocks the event loop ([#11574](https://github.com/diegosouzapw/OmniRoute/pull/11574)) — thanks @ntdatt812 +- **fix(check):** the `known-symbols` executor conformance gate awaits the now-async `getExecutor()`, so it stops reporting all 142 registered aliases as dead symbols and can detect a lazy import that fails ([#11582](https://github.com/diegosouzapw/OmniRoute/pull/11582)) — thanks @ntdatt812 +- **fix(pack):** register `bin/cli/utils/volatileEnvPath.mjs` in `PACK_ARTIFACT_REQUIRED_PATHS`, so the prune can no longer silently drop a module the published CLI imports on every boot ([#11588](https://github.com/diegosouzapw/OmniRoute/pull/11588)) — thanks @ntdatt812 +- **i18n(vi):** translate the five reliability-column strings on the free-provider rankings page, which rendered as `__MISSING__:` markers in the Vietnamese UI ([#11589](https://github.com/diegosouzapw/OmniRoute/pull/11589)) — thanks @ntdatt812 +- **resilience:** restore the expired-connection retry-budget probe in the token-health sweep — the `!isGitHubAccessTokenOnlyConnection` carve-out reintroduced by #11608 contradicted the boundary pinned by #11592, so a GitHub connection parked at `expired` with retry budget remaining was never probed and could never self-heal ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)). ([#11672](https://github.com/diegosouzapw/OmniRoute/pull/11672)) — thanks @jonlwheat2-gif / @ntdatt812 +- **fix(dashboard):** Provider Stats now displays combo success rates on their native 0–100 percentage scale instead of multiplying them twice ([#11595](https://github.com/diegosouzapw/OmniRoute/pull/11595)) — thanks @pacocartones +- **fix(providers):** route DuckDuckGo Web Chat through the verified `duck.ai` endpoint, refresh its free model catalog, and avoid forwarding unsolved anti-abuse challenges ([#11597](https://github.com/diegosouzapw/OmniRoute/pull/11597)) — thanks @solstxce +- **fix(ui):** Console log Refresh and Copy controls now expose localized accessible names, keep copy actions visible on keyboard focus, and announce copy completion safely ([#11599](https://github.com/diegosouzapw/OmniRoute/pull/11599)) — thanks @pacocartones +- Dropped the stale-`.eslintcache` `restore-keys` fallback from both "Restore ESLint file cache" steps in `ci.yml`, so the blocking `Lint` job can no longer be served per-file verdicts computed under a different lint config, suppressions file or lockfile. `quality.yml` had already dropped it in #11963; `ci.yml` — the workflow that actually gates PRs — had not (#11600). ([#11996](https://github.com/diegosouzapw/OmniRoute/pull/11996)) +- fix(ci): pass `--pass-on-unpruned-suppressions` in `run-eslint-json.mjs` so the CI Lint job no longer fails when a suppression is merely orphaned by a genuine fix, mirroring the identical fix already in `validate-release-green.mjs` (#11600) ([#11983](https://github.com/diegosouzapw/OmniRoute/pull/11983)) — thanks @hartmark +- fix(handoff): enforce provider allowlist for universal handoff (#11602) — universal handoff now skips summarization when the selected summary model's provider is not included in the configured provider allowlist. ([#11619](https://github.com/diegosouzapw/OmniRoute/pull/11619)) — thanks @vsd2807 +- **fix(dashboard):** Show a stable error state when Search Analytics returns an HTTP error or malformed data ([#11603](https://github.com/diegosouzapw/OmniRoute/pull/11603)) — thanks @pacocartones +- **test(gamification):** pin the aggregate profile level to the XP-derived semantics of #11604 — `getAggregateXp()` now derives `currentLevel` from the summed XP (`calculateLevel(sum)`), not `MAX(stored current_level)`, and the #3484 fixture levels are aligned with the XP curve ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)). ([#11676](https://github.com/diegosouzapw/OmniRoute/pull/11676)) — thanks @jonlwheat2-gif / @pacocartones +- **fix(dashboard):** Keep the Profile level and progress aligned with aggregate XP, including bounded handling for invalid totals ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)) — thanks @pacocartones +- **fix(dashboard):** Prevent locked hidden badges from revealing their icon or opening private badge details before they are earned ([#11605](https://github.com/diegosouzapw/OmniRoute/pull/11605)) — thanks @pacocartones +- **fix(dashboard):** Restore keyboard focus after shared modals close and cancel delayed autofocus during cleanup ([#11607](https://github.com/diegosouzapw/OmniRoute/pull/11607)) — thanks @pacocartones +- **fix(dashboard):** Enable Enter and Space activation for clickable data-table rows without hijacking nested controls ([#11610](https://github.com/diegosouzapw/OmniRoute/pull/11610)) — thanks @pacocartones +- **fix(autoCombo,sse):** vendor-retired catalog ids are dropped from the auto-combo candidate pool and no longer win on leftover `arena_elo` / `user_override` rows; `getModelLifecycleDecision` consults `model-lifecycle.json` (prefix-stripped) so aggregator traffic is not `untracked` for ids the snapshot already knows ([#11625](https://github.com/diegosouzapw/OmniRoute/issues/11625)) ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)) — thanks @MumuTW +- **test(chatcore):** move Codex/Claude combo fixtures off models the lifecycle guard now rejects — `gpt-5.1-codex`/`gpt-5-codex` are vendor-retired (snapshot, #11626) and `claude-3-5-sonnet-20241022` is shut down, so native-passthrough and combo-fallback tests switched to `gpt-5.6-sol` and `claude-sonnet-4.6` ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)). ([#11675](https://github.com/diegosouzapw/OmniRoute/pull/11675)) — thanks @jonlwheat2-gif / @MumuTW +- fix(opencode-plugin): stop a free-tier budget that rounds up across a magnitude from rendering as `1000K`/`1000M` in the model picker — `fmtTokens` chose its unit from the raw token count and then rounded with `toFixed(1)`, so 999,950–999,999 printed as `1000K` rather than `1M` and just under a billion printed as `1000M` rather than `1B` ([#11684](https://github.com/diegosouzapw/OmniRoute/pull/11684)) — thanks @abhisheksharma2411 +- Cap prose-derived Antigravity quota resets at the configured model cooldown maximum while preserving authoritative Retry-After headers and Google RetryInfo hints. (#11695) Thanks @pacocartones. ([#11823](https://github.com/diegosouzapw/OmniRoute/pull/11823)) +- Compare `_rebaseline_*` notes structurally in the ratchet-bank verifier so an object-valued note no longer reports itself as rewritten on an unmodified tree — the nightly `bank-ratchet-shrinks` lane had been aborting on every run since 2026-08-11, leaving 41,672 lines of earned file-size shrinks unbanked (#11702). ([#11892](https://github.com/diegosouzapw/OmniRoute/pull/11892)) — thanks @MumuTW +- **fix(build):** `prepublish.ts` bundles the ChatGPT Web (Codex) MCP bridge through `runBuildTool()` instead of spawning `npx.cmd` raw, fixing the build crash on Node ≥ 20/Windows where `.cmd` shims cannot be spawned without a shell (EINVAL) ([#11704](https://github.com/diegosouzapw/OmniRoute/issues/11704)) ([#11706](https://github.com/diegosouzapw/OmniRoute/pull/11706)) — thanks @jonlwheat2-gif +- fix(codex): keep `parallel_tool_calls:false` on the translated Codex Responses Lite path (#11707) ([#11984](https://github.com/diegosouzapw/OmniRoute/pull/11984)) — thanks @hartmark +- **fix(tests):** the new Gemini Web Images retirement test (#11708) called the now-async `getExecutor()` synchronously, throwing `TypeError: getExecutor(...).getProvider is not a function` instead of asserting Gemini Web chat stays available. Awaited to match the R0.3 executor-registry refactor (#11220). ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(search):** Allow REST and MCP web-fetch requests to select the advertised `anysearch-search` provider explicitly ([#11716](https://github.com/diegosouzapw/OmniRoute/pull/11716)) — thanks @pacocartones +- **fix(providers):** Keep overnight peak-hour protection active after midnight according to the window's configured start day ([#11718](https://github.com/diegosouzapw/OmniRoute/pull/11718)) — thanks @pacocartones +- **fix(providers):** correct all five g4f.space gateway descriptions to document proof-of-work credits or member API keys, variable limits, and the remote prompt boundary instead of promising free keyless access at five requests per minute ([#11738](https://github.com/diegosouzapw/OmniRoute/pull/11738)). +- **fix(db):** `model_capabilities` is created by a migration instead of lazily on the first models.dev sync, so a clean install and an upgraded install converge on the same schema regardless of which features have run ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(ci):** `check:install-upgrade` now fails on an `npm` install truncated by ENOSPC (npm reports it as a warning and still exits 0), authenticates its health probe so the version assertion works against the hardened health payload, frees the clean-install tree before the upgrade phase, and no longer reports a schema divergence computed from a boot that never served ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(routing):** harden session-affinity key derivation against oversized explicit IDs and empty structural-payload collisions, while recognizing Gemini and common text request shapes ([#11746](https://github.com/diegosouzapw/OmniRoute/pull/11746)) — thanks @MumuTW +- **fix(packages/browser-pool):** regenerate `package-lock.json` so the `packages/browser-pool` workspace's locked `playwright`/`@types/node` (and transitives) match its `package.json` specs, fixing cache-only/offline installs (`npm ci --offline`, Nix `buildNpmPackage`) that previously failed with `ENOTCACHED` ([#11747](https://github.com/diegosouzapw/OmniRoute/issues/11747)) — thanks @benjaminkitt ([#11784](https://github.com/diegosouzapw/OmniRoute/pull/11784)) — thanks @geekyNads +- **fix(assets):** Stop distributing 79 provider and CLI logos without verified source/license provenance, keep every provider/tool available through the internal generic icon, and prevent those IDs from falling through to an external icon CDN. ([#11750](https://github.com/diegosouzapw/OmniRoute/pull/11750)) +- **fix(providers):** retire the common `chatgpt-web` / `cgpt-web` provider because its pre-key and proof-of-work provenance could not be cleared ([#11754](https://github.com/diegosouzapw/OmniRoute/pull/11754)) — explicit chat and image requests, slash-prefixed models, persisted aliases, proxy overrides, and combo targets now fail closed with a sanitized HTTP `410` / `PROVIDER_RETIRED` before upstream dispatch. Migration 163 tombstones matching connections and invalidates their active leases without deleting history or widening API-key allowlists. The independent `chatgpt-web-codex` / `cgpt-codex` provider and its MIT-noticed vendored implementation remain available. +- **fix(usage):** quota and usage refresh no longer 409 when an exclusive lease reserves the connection ([#11758](https://github.com/diegosouzapw/OmniRoute/pull/11758)) — thanks @TheDemonTuan +- Keep the embedding registry's vector width and `embedding` type on models when a synced model exists for the same id, so `/v1/models` no longer reports registry-described embedding models widthless or untyped (#11761) — thanks @rahilmavani +- Correct `google/gemini-embedding-001` on the OpenRouter route to 3072 dimensions, the width it returns when `dimensions` is not sent (#11761) — thanks @rahilmavani +- **fix(vision-bridge):** Restrict automatic vision routing and fallbacks to models available in authoritative live provider catalogs, including aliases and effort variants ([#11767](https://github.com/diegosouzapw/OmniRoute/issues/11767)) — thanks @pacocartones ([#11813](https://github.com/diegosouzapw/OmniRoute/pull/11813)) +- **fix(providers):** reclassify Cerebras as a one-time $5 signup credit (payment method required, 30-day validity), not a recurring no-card 1M tokens/day trial ([#11773](https://github.com/diegosouzapw/OmniRoute/issues/11773)) ([#12591](https://github.com/diegosouzapw/OmniRoute/pull/12591)) — thanks @HouMinXi +- fix(cli): stop prepublish from re-rebuilding the already-built ESM-only opencode-plugin dist (#11787) ([#11990](https://github.com/diegosouzapw/OmniRoute/pull/11990)) — thanks @hartmark +- **fix(db):** Fresh installs no longer log a non-fatal `no such table: compression_run_telemetry` warning when retention cleanup runs before the lazily-created telemetry table exists ([#11802](https://github.com/diegosouzapw/OmniRoute/pull/11802)) — thanks @RaviTharuma +- fix(sse): set X-OmniRoute-Selected-Connection-Id on successful combo dispatches so downstream consumers stop falling back to an empty connection id (#11810) ([#11986](https://github.com/diegosouzapw/OmniRoute/pull/11986)) — thanks @hartmark +- **fix(services):** embedded CLIProxyAPI lifecycle checks now use public `/healthz`, while model discovery uses the configured dedicated data-plane API key instead of the management password ([#11811](https://github.com/diegosouzapw/OmniRoute/pull/11811)) — thanks @RaviTharuma +- Fix persisted model steps returning an error in the playground route simulator, and warn when structural steps cannot be simulated. ([#11862](https://github.com/diegosouzapw/OmniRoute/pull/11862)) — thanks @pacocartones +- **fix(providers):** Antigravity's dynamic mitmAlias table no longer routes `gemini-3.7-flash-{high,medium,low}` to a literal tier-suffixed upstream id just because one connected account's own discovery listed it directly — those display ids always resolve through the safe `gemini-3.7-flash-tiered` static alias, so one account's Google-provisioned access no longer 404s every sibling account of the provider ([#11824](https://github.com/diegosouzapw/OmniRoute/issues/11824), [#11651](https://github.com/diegosouzapw/OmniRoute/issues/11651)) ([#11988](https://github.com/diegosouzapw/OmniRoute/pull/11988)) — thanks @hartmark +- **fix(dashboard):** Expose Profile loading, errors, page structure, and XP progress to assistive technologies ([#11838](https://github.com/diegosouzapw/OmniRoute/pull/11838)) — thanks @pacocartones +- **fix(socks):** `Agent.connectTimeout` now bounds both SOCKS handshake and TLS connect, and `family === null` no longer falls back to `fetch-socks` ([#11842](https://github.com/diegosouzapw/OmniRoute/pull/11842)) — thanks @maxmad64bis +- **perf(compression):** OOM mitigations for large payload hashing, memoization, and token estimation ([#11844](https://github.com/diegosouzapw/OmniRoute/pull/11844) — thanks @AndrianBalanescu) +- Excluded Next.js Node File Trace manifests (`*.nft.json`) from the published npm tarball. They are build-time metadata and are never read while serving, but had grown to 668.7 MB — 61% of the package — which pushed the upload past the registry limit and made `npm publish` fail with `413 Payload Too Large`. ([#11864](https://github.com/diegosouzapw/OmniRoute/pull/11864)) +- **fix(cli):** keep the global OmniRoute server `--base-url` separate from provider-node `--endpoint` values in add, update, and validate commands ([#11860](https://github.com/diegosouzapw/OmniRoute/pull/11860)) — thanks @pacocartones +- **fix(config):** Nous Research's `Hermes-4-405B` model now displays as "Hermes 4 405B (Nous Research)" in both the provider registry and the free-model catalog, instead of the mislabelled "Hermes 4 7B" ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#11993](https://github.com/diegosouzapw/OmniRoute/pull/11993)) +- **fix(provider/nous):** inject required user= tag into Nous Research inference requests to resolve upstream 400 "missing tags" error ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#12044](https://github.com/diegosouzapw/OmniRoute/pull/12044)) +- **fix(build):** `npm run build` now fails in one second with a named package and a copy-pasteable fix when npm silently drops an externalised optional native dependency, instead of dying four minutes in with `Module not found: Can't resolve 'better-sqlite3'` ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- **fix(install):** `postinstall` no longer throws `ReferenceError: isAndroid is not defined` — failing the whole `npm install` — when the `better-sqlite3` rebuild fallback times out; the manual-fix guidance is reachable again ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- Fixed the Alibaba free-tier allowlist test that went red on its own once the shipped catalog's `validUntil` (2026-08-27) passed, leaving every PR and `main` with a failing `Unit Tests (1/8)`. The test now builds its own packs with dates it controls, and covers the expired-pack fallback that production has actually been serving. ([#11867](https://github.com/diegosouzapw/OmniRoute/pull/11867)) +- Split the npm registry upload into its own GitHub-hosted job. npm refuses `--provenance` from a self-hosted runner (`422 ... Only "github-hosted" runners are supported`), which blocked the v3.8.50 publish; the heavy verification cannot move to a hosted runner, so it now hands the proven tarball over instead. ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(ui):** the shared `DataTable` loading state no longer reads its decorative ⏳ glyph out to assistive technology, and now carries the same `role="status"` / `aria-live="polite"` / `aria-busy="true"` semantics as `PageLoading` ([#11869](https://github.com/diegosouzapw/OmniRoute/pull/11869)) — thanks @pacocartones +- **fix(api):** Bound the `timeout` accepted by `POST /api/providers/[id]/login` to the same 15s–600s window the Adobe Firefly and Conol browser-login paths already enforce, so a request can no longer pin a headful browser and the single active-login slot with an arbitrarily large poll budget. ([#11871](https://github.com/diegosouzapw/OmniRoute/pull/11871)) — thanks @pacocartones +- **fix(api):** Stop the tunnel and MITM routes from returning raw child-process error text, which disclosed host paths, binary install locations and Tailscale `tskey-*` credentials in a body some of these routes serve to non-loopback callers, and return a real 400 instead of a framework 500 when a tunnel request body fails validation. ([#11872](https://github.com/diegosouzapw/OmniRoute/pull/11872)) — thanks @pacocartones +- **fix(open-sse):** stream TTFT/ITL are sampled from a monotonic clock (`performance.now`) instead of `Date.now`, so an NTP correction or wall-clock jump can no longer inflate a genuine TTFT or produce a negative one that the `ttft >= 0` guard silently drops — protecting the router's OTel/EWMA/`usage_history`/speed-ranking signals ([#11873](https://github.com/diegosouzapw/OmniRoute/pull/11873)) — thanks @pacocartones +- **fix(providers):** xAI `reasoning_effort: "xhigh"` now reaches grok-4.6+ instead of being silently clamped to `"high"` ([#11879](https://github.com/diegosouzapw/OmniRoute/pull/11879)) — thanks @NoxzRCW +- **fix(dashboard):** the providers page no longer crashes into the error boundary when a provider id collides with an `Object.prototype` member (`constructor`, `__proto__`); icon lookups are own-property guarded ([#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)) — thanks @NoxzRCW +- **fix(skills):** injected skill tools declared in shorthand (`{"content": "string"}`) now forward valid JSON Schema, unblocking providers that validate tool schemas strictly such as Zhipu GLM on the Console Go tier ([#11881](https://github.com/diegosouzapw/OmniRoute/pull/11881)) — thanks @NoxzRCW +- **fix(api):** `POST /api/playground/simulate-route` now surfaces `combo-ref` and `provider-wildcard` persisted combo steps with a specific warning (naming the referenced combo, or the unresolved `provider/model` wildcard) instead of folding them into a generic "unsupported step" count; a `provider-wildcard` step is also now included as an unresolved target so the operator can see it is in the route (ported from [#11882](https://github.com/diegosouzapw/OmniRoute/pull/11882) — thanks @NoxzRCW). ([#11930](https://github.com/diegosouzapw/OmniRoute/pull/11930)) +- **fix(translator):** the streaming OpenAI→Claude translator keeps upstream usage, including prompt-cache tokens, when it arrives on a trailing `choices: []` chunk (Fireworks and any upstream using `stream_options.include_usage`) ([#11883](https://github.com/diegosouzapw/OmniRoute/pull/11883)) — thanks @NoxzRCW +- **fix(cli):** `omniroute update --apply` now tells you explicitly whether a running server was detected and, if so, that you must run `omniroute restart` to apply the update — it never restarted anything and previously implied the update was already live once files were installed. The dashboard's npm-mode Update flow (`/api/system/version`) now tries OmniRoute's own PID-file-managed supervisor before falling back to pm2, and reports an honest "restart required" step instead of a silent pm2-only "skipped" that read like a completed update. The server-side latest-version lookup backing the dashboard's update banner also gained `--prefer-online`, closing the same stale-npm-cache class already fixed in the CLI's own copy for #4376 ([#11885](https://github.com/diegosouzapw/OmniRoute/issues/11885)). ([#12005](https://github.com/diegosouzapw/OmniRoute/pull/12005)) +- **fix(resilience):** clear persisted LKGP pins when a target suffers connection/provider exhaustion or is skipped before dispatch due to cooldown/exhaustion/unavailability, preventing subsequent requests from repeatedly prioritizing known-dead providers ([#11911](https://github.com/diegosouzapw/OmniRoute/issues/11911)). ([#12013](https://github.com/diegosouzapw/OmniRoute/pull/12013)) — thanks @HouMinXi +- **fix(sse):** the OpenAI→Claude stream translator now defers the terminal `message_delta`/`message_stop` emission until the real usage block has arrived (or a genuine end-of-stream flush forces it) instead of emitting it immediately on `finish_reason` — previously, when the trailing usage-only chunk (`{"choices":[],"usage":{...}}`) arrived *after* the `finish_reason` chunk (the normal order for Fireworks/vLLM/Together and other `stream_options.include_usage` upstreams), the client-visible `message_delta` still carried stale/zero usage even though `state.usage` was internally corrected too late to matter (ported from [#11915](https://github.com/diegosouzapw/OmniRoute/pull/11915) — thanks @HouMinXi). ([#11933](https://github.com/diegosouzapw/OmniRoute/pull/11933)) +- **fix(sse):** `stripStore()` now forces `store=false` for stateless OpenAI-compatible Responses-API targets (unless the connection has explicitly opted in via `providerSpecificData.openaiStoreEnabled`), instead of only handling the `openai`/`agentrouter` cases — a client-supplied `store` value previously passed through untouched to backends that don't actually persist responses server-side ([#11916](https://github.com/diegosouzapw/OmniRoute/pull/11916) — thanks @HouMinXi). +- **fix(models):** custom provider-node models (synced, custom, and alias-backed) now appear under their configured prefix in the unified catalog when the operator's model-id prefix mode is canonical, instead of being dropped whenever alias-inclusion was otherwise disabled ([#11918](https://github.com/diegosouzapw/OmniRoute/pull/11918) — thanks @HouMinXi). +- **fix(models):** the unified model catalog now suppresses stale static registry models (including effort-tier variants) for any provider whose active connection has an authoritative live synced catalog, not only providers already using exclusive-synced-listing — a connection with `providerUsesAuthoritativeLiveCatalog` previously kept serving both the live-synced models and the stale static rows side by side ([#11919](https://github.com/diegosouzapw/OmniRoute/pull/11919) — thanks @HouMinXi). +- **fix(ui):** `ProviderIcon`'s three remaining unguarded lookups (`PROVIDER_ICON_ALIASES`, `LOCAL_SVG_ALIASES`, `THEMED_SVGS`) now use `Object.hasOwn()` own-property checks — a provider id such as `constructor` or `__proto__` previously resolved through the prototype chain instead of falling through to the unknown-provider CDN fallback (`getLobeProviderIcon()` itself was already guarded by [#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)); `ProviderPageHeader`'s `color` field is also now optional, matching the rest of the component's defensive typing (ported from [#11920](https://github.com/diegosouzapw/OmniRoute/pull/11920) — thanks @HouMinXi). ([#11935](https://github.com/diegosouzapw/OmniRoute/pull/11935)) — thanks @NoxzRCW +- fix(ollama): preserve multi-byte UTF-8 content split across stream chunks in the Ollama NDJSON transform, which previously corrupted CJK/emoji into U+FFFD (#11921) — thanks @pacocartones +- **fix(providers):** OrcaRouter chat requests now target `/v1/chat/completions` instead of the bare `/v1` API root, fixing the upstream `404 Invalid URL (POST /v1)` ([#11923](https://github.com/diegosouzapw/OmniRoute/pull/11923)). — thanks @echel0nn +- **fix(plugins):** deliver the `onStreamComplete` event to disk-installed plugins. The event shipped in v3.8.50 (#9669) was emitted internally but had no plugin-facing wiring, so no plugin could ever subscribe: the manifest schema silently dropped `hooks.onStreamComplete`, and the loader/manager only knew the seven legacy hooks. `onStreamComplete` is now a declarable manifest hook, wired through the loader and registered by the manager like the other hooks, and its payload carries a `requestId` so consumers can correlate the stream-completion event with the originating request ([#11934](https://github.com/diegosouzapw/OmniRoute/pull/11934)) — thanks @amaleta / @pacocartones +- **fix(db):** the Qdrant embedding-model dropdown now lists local/self-hosted providers (Ollama, LM Studio, vLLM, etc.) — an active connection is treated as "configured" when the provider allows an optional API key, not only when it has a real key or OAuth, so a running local embedding provider is no longer hidden from the picker ([#11949](https://github.com/diegosouzapw/OmniRoute/issues/11949)) ([#11995](https://github.com/diegosouzapw/OmniRoute/pull/11995)) +- **fix(providers):** Vertex AI Anthropic partner-model discovery now calls the Model Garden `v1beta1` publisher list (`/v1beta1/publishers/anthropic/models`, global) and parses its `publisherModels` envelope, so Claude models auto-synced from Vertex populate the active live catalog and route at request time instead of returning `Model '' is not available in the active live catalog` ([#11991](https://github.com/diegosouzapw/OmniRoute/issues/11991)) — thanks @fabioluissilva ([#11998](https://github.com/diegosouzapw/OmniRoute/pull/11998)) +- **fix(cli):** support `--base-url` alongside `--endpoint` in `omniroute nodes add`, `update`, and `validate` subcommands to prevent global `--base-url` shadowing issues ([#11999](https://github.com/diegosouzapw/OmniRoute/issues/11999)). ([#12033](https://github.com/diegosouzapw/OmniRoute/pull/12033)) — thanks @HouMinXi +- **fix(providers):** `cloudflare-ai` no longer refuses image content parts for every Workers AI model ([#12002](https://github.com/diegosouzapw/OmniRoute/pull/12002)) — the plain-string `content` requirement behind #2539 is carried by the _model_ schema, not by the `/ai/v1/chat/completions` endpoint (measured: an all-text part array returns 200 on `@cf/mistralai/mistral-small-3.1-24b-instruct`, `@cf/meta/llama-4-scout-17b-16e-instruct` and `@cf/meta/llama-3.3-70b-instruct-fp8-fast`, and 400 on the text-only `@cf/qwen/qwen2.5-coder-32b-instruct`). `transformRequest()` flattened every array and threw on the first non-text part (#6390), so image input was refused for vision-capable Cloudflare models that accept it. All-text arrays are still flattened — the one shape every model accepts — while an array carrying a non-text part is passed through untouched, so the attachment is still never silently dropped. Regression guards: `tests/unit/cloudflare-ai-image-parts-6390.test.ts`. — thanks @davidlinfr +- **fix(resilience):** decouple the limiter-managed execution backstop from the queue-wait budget — new `requestQueue.executionMaxWaitMs` (env `RATE_LIMIT_EXECUTION_MAX_WAIT_MS`, default 600000 = 10 min) now feeds Bottleneck's post-dispatch `expiration`, while `requestQueue.maxWaitMs` keeps its documented queue-wait semantics. Previously the queue-wait budget doubled as the execution expiration, so legitimate long-running calls on non-incremental gateways (whole generation buffered before the first upstream byte, e.g. Console Go / Command Code tiers serving GLM models) were killed mid-flight at the queue budget with a false 504 `RATE_LIMIT_EXECUTION_TIMEOUT` — the local limiter undercut the provider-aware upstream fetch-start timeouts. The surfaced 504 message now names `requestQueue.executionMaxWaitMs`; the error keeps the #4165 guarantees (disclaims an upstream timeout, preserves the Bottleneck error as `cause`, branded code + trusted provenance, classified request-scoped so combo falls back). A real queue-wait bound (the `Promise.race` around `limiter.schedule()` sketched in #9533) remains future work. (#12025) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **Call logs:** keep the `error` field when an artifact exceeds the storage cap, instead of replacing it with the omission marker. The error is the only field that says *why* a request failed and is typically ~90 bytes next to the multi-hundred-KB bodies that trip the cap, so dropping it left a size-limited row undiagnosable — a provider outage, a local timeout and an upstream 400 all rendered identically. It is now preserved at every fallback stage, truncated to 4KB if it is itself large ([#12026](https://github.com/diegosouzapw/OmniRoute/issues/12026)). ([#12095](https://github.com/diegosouzapw/OmniRoute/pull/12095)) — thanks @ntdat812 +- **fix(diagnostics):** preserve the error field (truncated to 4KB with a `[truncated: …]` suffix) in every call-log artifact size-limit fallback stage. Previously the minimal fallback replaced the error with `[omitted: call log artifact size limit exceeded]`, so an oversized artifact row showed nothing about WHY the request failed — e.g. 91 of 847 opencode-go 504 rows on one production instance were undiagnosable from the dashboard. Oversized request/response bodies are still omitted exactly as before; the error cap is independent of the payload sizes that tripped the fallback. (#12026) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **fix(sse):** OpenAI Responses clients that declare the native `web_search` tool now receive a spec-shaped `web_search_call` output item with `action.sources` alongside the preserved function-call round-trip, so search results executed through OmniRoute's own search backend are consumable by standard Responses clients (Codex, pi-web-access, …). ([#12031](https://github.com/diegosouzapw/OmniRoute/pull/12031)) — thanks @watchingdogs +- **fix(cli):** use in-thread alias resolver hooks on modern runtimes to avoid deprecation noise and improve Node.js forward compatibility ([#12073](https://github.com/diegosouzapw/OmniRoute/issues/12073)). ([#12083](https://github.com/diegosouzapw/OmniRoute/pull/12083)) — thanks @xiaoyaner0201 +- **fix(combo):** an operator-set **Agent Features → Context length** on a combo is now honored at request time. The value was persisted and advertised through `/v1/models`, but `resolveComboContextLimit()` never consulted it — so a multi-target combo whose members carry no per-model window fell through to the provider's generic `defaultContextLength` (openrouter 128000, command-code 200000) and rejected large requests with `Input exceeds context window … limit 128000` despite the combo being explicitly sized much larger. An identical single-target combo worked, because it collapses to its concrete target before the guard runs. Invalid values (0/negative/NaN/Infinity) are ignored, so the existing target → combo-min → fallback order is unchanged. ([#12090](https://github.com/diegosouzapw/OmniRoute/pull/12090)) — thanks @adivekar-utexas +- **fix(sse):** passthrough streams now estimate usage on finish when upstream closes without usage even with `stream_options.include_usage` — avoids `0 tokens / 0%` for providers that stay silent (and correctly handles trailing empty-choices usage) ([#12151](https://github.com/diegosouzapw/OmniRoute/pull/12151)) — thanks @maxmad64bis +- **fix(combos):** clearing an agent feature in the combos editor now persists — unchecking context cache protection, or emptying the system message or tool filter, sends an explicit `null` instead of dropping the field from the `PUT` body, which the update merge read as "leave unchanged" ([#12177](https://github.com/diegosouzapw/OmniRoute/pull/12177)) — thanks @foreveryh / @ntdatt812 +- **fix(memory):** self-hosted embedding endpoints now vectorize — the vector width is measured from the first embedding that comes back instead of being read from a registry that cannot describe them, so `vec_memories` is created and memories stop piling up unvectorized behind a green health check ([#12180](https://github.com/diegosouzapw/OmniRoute/pull/12180)) — thanks @kanade-hoshino / @ntdatt812 +- **fix(translator):** the leading `system` message now reaches Responses-API upstreams when its `content` is a content-part array — it was read as `typeof content === "string" ? content : ""`, so a prompt-caching client (Anthropic `cache_control`, the shape LiteLLM and the Anthropic SDK emit) had its entire system prompt replaced by an empty `instructions`. The request was still accepted with a normal `prompt_tokens` count, so the model answered with no instructions and nothing in the response said they were missing. Mid-conversation system turns already handled the array shape ([#7056](https://github.com/diegosouzapw/OmniRoute/pull/7056)); only the first one did not ([#12206](https://github.com/diegosouzapw/OmniRoute/issues/12206)). Regression guard: `tests/unit/translator-openai-responses-system-content-parts.test.ts`. ([#12207](https://github.com/diegosouzapw/OmniRoute/pull/12207)) — thanks @Gorillaz322 +- **fix(free-tier):** `/api/free-tier/summary` no longer computes its totals from a Radar feed built before the catalog the running release ships. When the cached feed is older — or carries no build date at all — the route answers from the shipped catalog, resolved through the operator's local model state so disabled and tombstoned models stay out of the numbers ([#12215](https://github.com/diegosouzapw/OmniRoute/pull/12215)). — thanks @maxmad64bis +- **fix(oauth):** Keep a Claude personal workspace and a Team organization as separate connections — they share the same email and `accountUUID`, so the email-only OAuth dedup let the second login overwrite the first account's tokens; `organizationUUID` now disambiguates them, the way `workspaceId` does for Codex ([#12222](https://github.com/diegosouzapw/OmniRoute/pull/12222)) — thanks @davidebaraldo +- **fix(resilience):** a 402 on a single paid model of a passthrough/gateway provider (e.g. `kilo-gateway`, `ollama-cloud`) no longer terminalizes the whole connection with a never-auto-recovered `credits_exhausted` status — only the paid model is locked out, so free models on the same key keep serving. 402 variant of [#3027](https://github.com/diegosouzapw/OmniRoute/issues/3027). Single-credential providers are unaffected — a 402 there is still treated as the key being genuinely out of credit ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239) / [#10616](https://github.com/diegosouzapw/OmniRoute/issues/10616)) ([#12242](https://github.com/diegosouzapw/OmniRoute/issues/12242)) — thanks @brick30llc-ctrl ([#12266](https://github.com/diegosouzapw/OmniRoute/pull/12266)) — thanks @geekyNads +- **fix(sse):** trust `finish_reason: "length"`/`"max_tokens"` over the reasoning-consumed-token ratio in response quality validation, so a reasoning model truncated below the old 90% threshold correctly fails and retries instead of returning empty content as a silent "success" ([#12262](https://github.com/diegosouzapw/OmniRoute/pull/12262)) — thanks @brick30llc-ctrl +- **fix(settings):** `PUT /api/settings/cache-config` now persists `alwaysPreserveClientCache` to the flat general settings the runtime cache-control policy actually reads; previously the value landed in the databaseSettings "cache" section and was silently ignored, so the endpoint had no effect on `cache_control` passthrough ([#12304](https://github.com/diegosouzapw/OmniRoute/pull/12304)) — thanks @davidebaraldo +- **fix(grok-cli):** treat omitted SuperGrokPro `creditUsagePercent` as 0% used so Provider Limits still renders a weekly bar (proto3 zero-elision) ([#12312](https://github.com/diegosouzapw/OmniRoute/pull/12312)) — thanks @HouMinXi +- **fix(combo):** Expose the two Auto-Combo scoring factors nobody could set — the combo validation schema and the dashboard weight sliders both declared 13 of the scorer's 15 factors, so `connectionDensity` (spreads load across a provider's connections) and `quality` were dropped on save and offered nowhere. The sliders also shipped their own default table that differed from the engine's on every non-zero factor and summed to 1.05, so the percentages shown next to them added up to 105%. Both lists now match `DEFAULT_WEIGHTS`, and a test keeps them there. Note that a combo whose stored `weights` omitted the two keys was effectively running with them at zero and the other thirteen renormalized upward; it now runs with the engine's intended distribution, so its routing does shift ([#12314](https://github.com/diegosouzapw/OmniRoute/pull/12314)) — thanks @maxmad64bis +- **fix(docs):** The free-tier reference no longer says its numbers come "confidence tagged per row" — no catalog entry carries a confidence tag and the API serves none, so every figure on that page is an estimate of the same, unstated quality. The page now states what an entry does vouch for: an independently documented hard stop (set by hand with the source in a comment, never defaulted to `true`) and a prompt-training disclosure, both with live counts the `check:docs-counts` gate keeps honest ([#12318](https://github.com/diegosouzapw/OmniRoute/pull/12318)) — thanks @maxmad64bis +- **fix(usage):** `adobe-firefly` and `firefly` have had a working usage fetcher since Adobe Firefly landed, but neither was ever added to the registration list, so the provider-plugin manifest, `genericQuotaFetcher` and the free-access quota cache all reported them as having no usage support — while `USAGE_SUPPORTED_PROVIDERS` said the opposite. Both are now declared, which also means their credit balance is fetched like any other declared provider's: `registerGenericQuotaFetchers` now registers a generic quota fetcher for them, and `resolveFreeAccessState` no longer returns early. A test holds the registration list to the dispatcher's switch in both directions, which is what the module's own docstring already asked for in prose ([#12321](https://github.com/diegosouzapw/OmniRoute/pull/12321)) — thanks @maxmad64bis +- **fix(quota):** drop the generic quota cache (agy / Antigravity / Claude OAuth) on an upstream 429 so reset-aware scoring does not keep a 60s stale snapshot, and force-refresh the next usage fetch so inner provider caches cannot recache the same window ([#12325](https://github.com/diegosouzapw/OmniRoute/pull/12325)) — thanks @HouMinXi +- **fix(combos):** deleting a combo now clears its persisted LKGP pins instead of leaving unreachable `key_value` rows behind ([#12326](https://github.com/diegosouzapw/OmniRoute/issues/12326)) ([#12425](https://github.com/diegosouzapw/OmniRoute/pull/12425)) — thanks @KrzysiekSko +- **fix(sse):** Keep ZWNJ (U+200C) and ZWJ (U+200D) in assistant text, reasoning and tool-call arguments — Persian/Kurdish half-space (`ارائه‌دهنده`), Arabic/Indic shaping and emoji sequences no longer lose them; the response de-obfuscation now removes joiners only between ASCII word characters, where the request side inserts them ([#12186](https://github.com/diegosouzapw/OmniRoute/issues/12186)) — thanks @rezjalibd ([#12359](https://github.com/diegosouzapw/OmniRoute/pull/12359)) — thanks @pacocartones +- **fix(resilience):** count resolved upstream 5xx results against the provider circuit breaker on the chat path — `CircuitBreaker.execute()` no longer reads a resolved `{ success: false, status: 5xx }` as a success that cancels the call-site failure, so a provider answering 503s now trips its breaker instead of staying `CLOSED` at `failureCount: 1`; single-model and combo dispatches are each accounted exactly once ([#12254](https://github.com/diegosouzapw/OmniRoute/issues/12254)) ([#12360](https://github.com/diegosouzapw/OmniRoute/pull/12360)) — thanks @pacocartones +- **fix(providers):** resolve the Codex quota auto-ping model from the live provider catalog and lifecycle registry instead of the retired `gpt-5.1-codex-mini`, and pause the ping with one actionable warning when no selectable Codex model exists rather than retrying a shut-down id every cooldown window ([#11905](https://github.com/diegosouzapw/OmniRoute/issues/11905)) ([#12361](https://github.com/diegosouzapw/OmniRoute/pull/12361)) — thanks @pacocartones +- **fix(api):** keep the `{created, data}` wrapper on combo-routed `/v1/images/generations` responses and default Codex image results to `b64_json` on both `/v1/images/generations` and `/v1/images/edits` so Codex CLI's built-in `image_gen` can decode them ([#12268](https://github.com/diegosouzapw/OmniRoute/issues/12268)) ([#12362](https://github.com/diegosouzapw/OmniRoute/pull/12362)) — thanks @pacocartones +- **fix(sse):** Name the shadowed custom provider node when a built-in provider id/alias (e.g. `openference` → `of`) reserves the prefix of an existing OpenAI/Anthropic-compatible node, so the runtime `No active credentials for provider: ` error explains that the prefix routed to the built-in and never reached the node's healthy connections, instead of contradicting the dashboard ([#11943](https://github.com/diegosouzapw/OmniRoute/issues/11943)) — thanks @morpheus9393 ([#12365](https://github.com/diegosouzapw/OmniRoute/pull/12365)) — thanks @pacocartones +- **fix(i18n):** wrap `ccOnboardingKeyPlaceholder` in ICU single quotes across all 43 locales so angle brackets render literally instead of being parsed as rich-text tags, which crashed the Claude Code onboarding block with `INVALID_MESSAGE: INVALID_TAG` ([#12302](https://github.com/diegosouzapw/OmniRoute/issues/12302)) ([#12369](https://github.com/diegosouzapw/OmniRoute/pull/12369)) — thanks @gonisulaimann +- **fix(models):** publish `effort_tiers` on Kimi K3's synced base-model entries (`kmca/k3`, `kmca/k3-256k`) so catalog-only clients (OpenCode, plain SDK pickers) can see and select the reasoning tiers (`low`/`high`/`max`) the synced metadata already carried — the `isSkippedEffortProvider` gate no longer suppresses tier visibility on those base entries, while synthetic `-` variant generation stays prevented and Codex/GLM base models remain excluded unchanged ([#12299](https://github.com/diegosouzapw/OmniRoute/issues/12299)) ([#12371](https://github.com/diegosouzapw/OmniRoute/pull/12371)) — thanks @gonisulaimann +- **fix(guardrails):** keep `auto`/`auto/*` virtual combos exempt from the Vision Bridge `fixedModel` credential guard so a combo target is passed through instead of silently falling back to global auto-selection ([#12237](https://github.com/diegosouzapw/OmniRoute/issues/12237)) ([#12373](https://github.com/diegosouzapw/OmniRoute/pull/12373)) — thanks @pacocartones +- **fix(combo):** capability-filter exhaustion caused by `max_tokens` above every target's known output limit now reports that reason (requested `max_tokens` vs the pool's highest known ceiling) instead of the unrelated "supports structured output" message ([#12229](https://github.com/diegosouzapw/OmniRoute/issues/12229)) — thanks @DW-MediaLab ([#12374](https://github.com/diegosouzapw/OmniRoute/pull/12374)) — thanks @pacocartones +- **fix(auth):** the `least-used` account strategy now prefers accounts without backoff before falling back to oldest `lastUsedAt`, the same tie-break `round-robin` already applies, so a failover no longer lands on a just-rate-limited account for a single request ([#12279](https://github.com/diegosouzapw/OmniRoute/issues/12279)) — thanks @tenshiak ([#12375](https://github.com/diegosouzapw/OmniRoute/pull/12375)) — thanks @pacocartones +- **fix(docker):** the `chatgpt-web-codex-browser` image now finds the Chrome binary under `chrome-linux64/` (Chrome for Testing layout in `playwright:v1.62.0-noble`) as well as the legacy `chrome-linux/`, so the container no longer crash-loops with `exec: --headless=new: not found` ([#12024](https://github.com/diegosouzapw/OmniRoute/issues/12024)) ([#12376](https://github.com/diegosouzapw/OmniRoute/pull/12376)) — thanks @pacocartones +- **fix(providers):** declare `groq/compound` and `allam-2-7b` as non-reasoning models in the curated Groq registry so `reasoning_effort` / `output_config.effort` / `thinking` from Claude Code are stripped instead of forwarded, which Groq rejected with HTTP 400 ([#12134](https://github.com/diegosouzapw/OmniRoute/issues/12134)) ([#12379](https://github.com/diegosouzapw/OmniRoute/pull/12379)) — thanks @pacocartones +- **fix(executors):** `OpencodeExecutor` no longer forces a direct connection when the connection has a proxy assigned in Proxy Management but no per-account proxies: the single-account fast path used to wrap the upstream dispatch in the direct-egress sentinel, discarding the ambient proxy context the chat handler had pinned from `proxy_assignments`, so API-key `opencode`/`opencode-go` connections egressed from the host IP (and hit geoblocks) despite the assignment. The direct pin is now applied only when no ambient proxy context exists ([#11894](https://github.com/diegosouzapw/OmniRoute/issues/11894) — thanks @hizzt) ([#12380](https://github.com/diegosouzapw/OmniRoute/pull/12380)) — thanks @pacocartones +- **fix(api):** `GET /v1/models` with `MODELS_CATALOG_PREFIX_MODE=canonical` (or `?prefix=canonical`) now lists providers whose registry alias is undefined or equal to their own id (Antigravity, Antigravity CLI and other self-aliased built-ins) — their single `provider/model` id was dropped by the alias/canonical duplicate guard in the static, synced, custom and alias-backed catalog loops ([#12058](https://github.com/diegosouzapw/OmniRoute/issues/12058)) — thanks @cheynetom ([#12381](https://github.com/diegosouzapw/OmniRoute/pull/12381)) — thanks @pacocartones +- **fix(translator):** Drop replayed `thinking` blocks that carry no signature (the shape produced from cross-provider `reasoning_content`) instead of stamping the default Claude signature on them, which Anthropic rejected with `400 Invalid signature in thinking block` on the next turn served by an Anthropic rung ([#12105](https://github.com/diegosouzapw/OmniRoute/issues/12105)) — thanks @atescivitci-cmd ([#12386](https://github.com/diegosouzapw/OmniRoute/pull/12386)) — thanks @pacocartones +- **fix(cli):** Resolve Bun's `--preload` polyfill path against the package root instead of `dist/`, so `omniroute` installed with `bun install -g` no longer crashes at startup with `error: preload not found …/dist/open-sse/utils/setupPolyfill.ts` ([#11980](https://github.com/diegosouzapw/OmniRoute/issues/11980)) — thanks @joglomedia ([#12387](https://github.com/diegosouzapw/OmniRoute/pull/12387)) — thanks @pacocartones +- **fix(providers):** `gemini-business` now publishes its model catalog — `/v1/models` and `/v1/providers/gemini-business/models` list the 12 enterprise Gemini ids the executor understands instead of returning an empty list (#12107) ([#12389](https://github.com/diegosouzapw/OmniRoute/pull/12389)) — thanks @pacocartones +- **fix(db):** install `busy_timeout` before the SQLite connection's first statement so a process opening the database while another one closes its WAL connection waits out the transient EXCLUSIVE lock instead of dying with `database is locked`, and recognise the drivers' real BUSY/PROTOCOL/IOERR errors as transient in the corruption probe so the same lock no longer renames the database away as corrupt; deflakes `cross-process contenders never both acquire the same connection` (#12394 — thanks @pacocartones) +- **fix(chat-admission):** derive the `chat_admission_busy` 503 `Retry-After` from observed heavyweight-lease occupancy — the larger of the exhausted `OMNIROUTE_CHAT_ADMISSION_QUEUE_MS` window and the time since capacity last turned over, capped at 60 s — instead of a fixed 1 s (structural) / 2 s (byte-stage) hint that invited Codex/agent fan-out clients to re-send ~1 MiB `/v1/responses` bodies every second into a gate held for the whole SSE lifetime; an idle gate keeps the historical floors ([#12135](https://github.com/diegosouzapw/OmniRoute/issues/12135)) (#12395 — thanks @pacocartones) +- **fix(api-manager):** the API key permissions modal no longer silently drops `allowedCombos` entries its Combo picker cannot render — routing-rule names such as `rt-*`, which the backend already honours — when "All" is clicked and the key is switched back to "Restrict"; those entries now survive the toggle, are listed read-only under the combo list so the count and the list agree, and are saved back verbatim instead of persisting `[]` (deny-all) (#12397 — thanks @pacocartones) +- **fix(catalog):** write the NUL separator of the catalog connection memo key, the provider serviceKind memo key, the Video Bridge promotion group key and a JSON-exactness test fixture as the `\u0000` escape instead of a raw byte — same runtime value, but the raw byte made git, GitHub and ripgrep treat those files as binary (hidden PR diffs, silently skipped searches); a guard test now keeps raw NUL bytes out of `src/`, `open-sse/` and `tests/` (#12403 — thanks @pacocartones) +- **fix(providers):** add `CLAUDE_CODE_CLIENT_VERSION` and `GITHUB_COPILOT_CLI_VERSION` env overrides so Anthropic/Copilot client-version gates can be unblocked without a rebuild ([#12417](https://github.com/diegosouzapw/OmniRoute/issues/12417)) ([#12632](https://github.com/diegosouzapw/OmniRoute/pull/12632)) — thanks @HouMinXi +- **fix(db):** back-fill `last_ping_at` and `last_pinged_reset_key` on `provider_connections` during schema reconciliation so divergent lineages that skipped `123_quota_auto_ping` still accept quota auto-ping writes ([#12470](https://github.com/diegosouzapw/OmniRoute/pull/12470) — thanks @KooshaPari) +- **fix(ci):** document MIT exceptions for `@eloqnt/{config,format-json,format-po}` (next-intl transitive; locked tarballs omit `license`) and keep the A2A lifecycle vitest off the real SQLite persistence seam ([#12581](https://github.com/diegosouzapw/OmniRoute/issues/12581)) ([#12595](https://github.com/diegosouzapw/OmniRoute/pull/12595)) +- OpenCode plugin `/v1/models` catalog fetch now waits 30s by default and attaches HTTP `statusCode` on 401/5xx so host fallback plugins can hop instead of seeing an untyped AbortError/UnknownError. ([#12607](https://github.com/diegosouzapw/OmniRoute/pull/12607)) — thanks @RaviTharuma +- **CI:** the OpenAPI security-tier gate now mirrors `isAlwaysProtectedPath()` in full — it also reads `ALWAYS_PROTECTED_API_PATTERNS`, so the pattern-gated credential routes (`/api/providers/{id}/{claude,codex}-auth/{export,apply-local}`, GHSA-5926-2w35-7h4q) no longer report as unannotated. (#12605) +- **fix(api):** GET /v1/models no longer waits forever on a hung coalesced catalog rebuild; cold-path waits are bounded (`CATALOG_BUILD_TIMEOUT_MS`, default 8s) and a last-good 200 is served when the rebuild times out ([#12627](https://github.com/diegosouzapw/OmniRoute/issues/12627)). ([#12628](https://github.com/diegosouzapw/OmniRoute/pull/12628)) — thanks @RaviTharuma +- **fix(cursor):** a non-array `tool_calls` on an assistant message no longer crashes the cursor request translator with a `TypeError`; both loops now require an array ([#12689](https://github.com/diegosouzapw/OmniRoute/issues/12689)) ([#12691](https://github.com/diegosouzapw/OmniRoute/pull/12691)) — thanks @soroush5 +- **fix(chat):** Chat Completions no longer return empty `content` after a server-owned memory or skills tool; the first provider send and account/model recovery now share one pipeline so a follow-up round-trip can fill the reply ([#12696](https://github.com/diegosouzapw/OmniRoute/issues/12696)) — thanks @HouMinXi ([#12867](https://github.com/diegosouzapw/OmniRoute/pull/12867)) +- **fix(dashboard):** batch-deleting provider keys no longer toasts failure after a successful delete when the confirm button's click event is forwarded as `onAfter` ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) — thanks @HouMinXi +- **fix(glm):** drop the extra 16th argument to `createSSETransformStreamWithLogger` that TypeScript rejected (TS2554) and that never reached the TransformStream ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) ([#12770](https://github.com/diegosouzapw/OmniRoute/pull/12770)) — thanks @HouMinXi +- **fix(dashboard):** pass `nodeMap` into Runtime `QuotaGroup` so a quota monitor in error/exhausted/alerting no longer throws `ReferenceError: nodeMap is not defined`. ([#12868](https://github.com/diegosouzapw/OmniRoute/pull/12868)) — thanks @HouMinXi +- Restricted API keys whose `allowedModels` lists a combo name no longer skip every combo member at pre-dispatch (`ALL_TARGETS_SKIPPED`). Inner-target filtering still applies when the allow-list is a provider prefix or `disableNonPublicModels` is on ([#12899](https://github.com/diegosouzapw/OmniRoute/pull/12899)). — thanks @HouMinXi +- **fix(combos):** Effort-suffixed combo members inherit the base model's `model_context_overrides` row so priority order is not inverted on large requests ([#12926](https://github.com/diegosouzapw/OmniRoute/pull/12926)) — thanks @HouMinXi +- **fix(catalog):** Picker-added `customModels` enter the dispatch-time live catalog so combo members and bare inference no longer 400 ([#12934](https://github.com/diegosouzapw/OmniRoute/pull/12934)) — thanks @HouMinXi +- **fix(docker):** re-derive the Docker build's worker-pool memory budget from the MEASURED ~4.5 GB per-process RSS (the issue owner's own VPS dmesg OOM-killer reproduction) instead of the stale 2560 MB/worker inference #11419 shipped, and lower `OMNIROUTE_BUILD_WORKERS` 3 → 2 so 1 parent + 1 page-data worker (2 processes × 4.5 GB = 9 GB) fits the 12.288 GB (75%) budget on a 16 GB GitHub Actions runner — the previous default (1 parent + 2 workers = 13.5 GB) still overcommitted the runner and kept "Publish to Docker Hub" failing with `cannot allocate memory` after #11419 merged (#7518). ([#11663](https://github.com/diegosouzapw/OmniRoute/pull/11663)) +- **fix(electron):** the packaged Windows desktop build now passes the #7592 cold-restart smoke end to end. Five defects were found and fixed on the way: (1) optional-pack staging failed on any machine whose `tar` is GNU tar — it reads the drive letter in an absolute `-f C:\...` archive path as a remote rsh host (`Cannot connect to C:`), so staging now passes a bare filename with `cwd` at the tarball directory and surfaces tar stderr on failure; (2) the Electron `files` allowlist was missing `lib/loginHeaderCapture.js`, so the packaged main process crashed at startup with `Cannot find module './lib/loginHeaderCapture'`; (3) electron-builder ≥26 injects an `!**/node_modules/**` ignore into every extraResources pattern list that no positive filter can override, which silently dropped the staged runtime `node_modules` (including the better-sqlite3 N-API prebuild) from `resources/app` — a fresh v3.8.50 desktop build would have shipped with no native SQLite driver and reproduced the #7592 sql.js fallback on every machine; an `afterPack` hook now restores the staged `node_modules` after packing; (4) the packaged-app smoke harness redirected `USERPROFILE` into its temp DATA_DIR, but Electron resolves the Roaming profile from `%USERPROFILE%\AppData\Roaming\` (USERPROFILE takes precedence over the APPDATA env var) and the path service throws instead of creating it, so `requestSingleInstanceLock()` returned false and the app exited(0) silently before `app.whenReady()` — the harness now pre-creates the derived tree, and `ensureSmokeEnvDirs` is exported and unit-tested; (5) the #7592 driver assertion parsed a `[DB] Driver: ...` line that the server's primary DB path never printed (only the unused `openDatabaseAsync()` did) — `getDbInstance()` now emits the same line on open so the guard can actually assert the native driver. Also: the smoke env-allowlist unit test hardcoded POSIX paths and could only pass on Linux/macOS; expectations are now host-agnostic, with new regression tests for the USERPROFILE-derived tree and `tarPack` under absolute Windows-style paths ([#11443](https://github.com/diegosouzapw/OmniRoute/pull/11443)) — thanks @jonlwheat2-gif +- fix(sse): stop the auto-combo candidates inspector from silently dropping model-locked/cooled-down rows (#9133) ([#11994](https://github.com/diegosouzapw/OmniRoute/pull/11994)) — thanks @hartmark +- **fix(providers):** Perplexity Web no longer turns upstream stream failures into successful assistant text; pre-content failures remain eligible for fallback, partial output ends with a structured sanitized error, and failed sessions are not persisted ([#12465](https://github.com/diegosouzapw/OmniRoute/pull/12465)) +- **fix(video-bridge):** preserve chronological video and transcript fusion, source-frame timestamps, transcript provenance, full contact-sheet intervals, and pre-abort callback isolation ([#11681](https://github.com/diegosouzapw/OmniRoute/pull/11681)) +- **Z.ai Web:** HTTP 200 streams carrying an upstream error now terminate with a structured failure instead of assistant text plus a normal stop, preserving partial output while allowing pre-content combo fallback. ([#12454](https://github.com/diegosouzapw/OmniRoute/pull/12454)) +- Absorb `Error [AbortError]: request_signal_aborted` and DOMException AbortError shapes in the process-level client-abort crash guard so routine client disconnects no longer kill the server (exit code 7). ([#12165](https://github.com/diegosouzapw/OmniRoute/pull/12165)) — thanks @alvinveroy +- **fix(sse):** Treat Adapta Web `type:error` SSE events as sanitized non-stream failures instead of empty HTTP 200 completions. ([#12459](https://github.com/diegosouzapw/OmniRoute/pull/12459)) +- **fix(autoCombo):** layer 3 (`models_dev_tier`) no longer scores retired models as premium, merge cross-provider capability rows deterministically, and inherit tier scores through variant ids ([#11508](https://github.com/diegosouzapw/OmniRoute/issues/11508)). `getModelsDevTierFitness` now vetoes ids present in `config/quality/model-lifecycle.json` with status `retired` before any other signal; `loadModelCapabilities()` aggregates per `model_id` (any-non-null-true for booleans, max for `limit_context`) instead of last-write-wins over SQLite's undefined row order; a capabilities miss retries through `resolveScoresAs()`'s base id and reports `models_dev_tier:inherited`, matching layers 1–2 (#11492). Adds `getModelsDevTierFitnessWithSource()` and an `invalidateCapabilitiesCache()` test/ops hook. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) — thanks @Meet6338-X +- fix(sse): accept Claude `content:[]` + `stop_reason: max_tokens`/`tool_use` in `detectMalformedNonStream` (match `isEmptyContentResponse`) so Claude Code `/model` probes with `max_tokens: 1` no longer become a false 502 ([#11568](https://github.com/diegosouzapw/OmniRoute/pull/11568)) — thanks @Seramicx +- **fix(security):** sanitize `request.failed` diagnostics before publishing them to live dashboard listeners and replay history, while keeping status, model, provider, latency, and internal call-log diagnostics intact. ([#12469](https://github.com/diegosouzapw/OmniRoute/pull/12469)) +- **fix(pwa):** do not intercept dashboard navigations so Chrome can fall back from a stale HTTP/3 Alt-Svc advertisement (UDP :20128 is unpublished; F5 on a long-lived tab hung until a new tab opened a fresh TCP connection). ([#12767](https://github.com/diegosouzapw/OmniRoute/pull/12767)) — thanks @HouMinXi +- **fix(executors):** handle DuckDuckGo ERR_BN_LIMIT (418) without retrying — when the upstream returns `418 ERR_BN_LIMIT` (rate-limit/ban), the executor now returns the error immediately instead of burning another VQD acquisition that would only count against the IP limit. The retry logic for `418 ERR_CHALLENGE` (unsolved challenge) remains unchanged. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) ([#12110](https://github.com/diegosouzapw/OmniRoute/pull/12110)) — thanks @oyi77 / @Meet6338-X +- Fixed DuckDuckGo streaming responses losing JSON lines and UTF-8 characters split across network chunks. ([#11528](https://github.com/diegosouzapw/OmniRoute/pull/11528)) — thanks @pacocartones +- Harden generated Cloudflare, Vercel, and Deno relays by stripping hop-by-hop, framing, and proxy authentication headers before forwarding requests upstream. ([#11533](https://github.com/diegosouzapw/OmniRoute/pull/11533)) — thanks @pacocartones +- **feat(grok-cli):** Provider Limits shows grok-cli banked reset credits from `GetRemainingResets` (including a real zero; a failed RPC omits the row) and the existing View credits button now calls `ConsumerUiSvc/RedeemReset` for grok-cli. Live tokens use nested fields 10/20/30 (id + Timestamp), not compact 1/2/3. ([#12805](https://github.com/diegosouzapw/OmniRoute/pull/12805)) — thanks @HouMinXi +- **fix(memory):** Embedding Model Quick select, Embedding Source remote dropdown, and Rerank selector now list every configured provider with embedding/rerank support instead of only chat-catalog text matches plus OpenRouter live discovery; a generic OpenAI-compatible `/embeddings` + Cohere-compatible `/rerank` runtime fallback resolves any configured chat provider's embedding/rerank endpoint, so unlisted providers no longer fail with "Unknown embedding provider"; both memory selectors gained a free-text model override ([#11390](https://github.com/diegosouzapw/OmniRoute/pull/11390)) — thanks @rqzbeh +- Harden SQLite upgrades around the historical migration-074 version collision: missing discovery and inspector tables are replayed atomically, pre-existing databases (including setup-created skeletons) receive reusable content-addressed safety snapshots, and Node test/eval probes without `DATA_DIR` are isolated from the operator database. ([#12435](https://github.com/diegosouzapw/OmniRoute/pull/12435)) +- **fix(dashboard):** Moonshot/Kimi Open Platform voucher and cash leftover percentages follow the bucket balance, so an empty wallet no longer paints those rows as 100% while Available is 0% ([#12733](https://github.com/diegosouzapw/OmniRoute/pull/12733)) — thanks @HouMinXi +- **fix(combo):** return non-retryable HTTP 400 when all candidates for a pinned native Codex turn are unavailable due to model-scoped lockout, terminating the turn cleanly while preserving turn continuity and enabling standard Combo routing on subsequent turns ([#12240](https://github.com/diegosouzapw/OmniRoute/pull/12240)) — thanks @mdigitalbh81 +- Fixed auto-router strategies preserving the selected connection through ranking and dispatch when multiple connections share the same provider and model. ([#11530](https://github.com/diegosouzapw/OmniRoute/pull/11530)) — thanks @pacocartones +- Preserve Chat Completions JSON-object response formats when translating requests to the Responses API. ([#11529](https://github.com/diegosouzapw/OmniRoute/pull/11529)) — thanks @pacocartones +- Fix idempotency fingerprints so Responses API requests include `input` and other semantic generation fields using stable serialization, while excluding credentials and request metadata. ([#11532](https://github.com/diegosouzapw/OmniRoute/pull/11532)) — thanks @pacocartones +- **fix(api):** Generated API CLI commands now enforce required OpenAPI request bodies; Combo test commands forward the required `comboName` body, while API keys created by older writers after migration 149 preserve legacy allow-all Combo access without widening explicit empty allowlists — thanks @marcelokarval ([#12070](https://github.com/diegosouzapw/OmniRoute/pull/12070)) +- **fix(grok-web):** treat upstream streaming failures as failures instead of successful assistant text: error-only streams now fail readiness with HTTP 502, while failures after legitimate content preserve that partial output and terminate through the sanitized stream failure path without a normal `stop` completion. ([#12458](https://github.com/diegosouzapw/OmniRoute/pull/12458)) +- HuggingChat now turns HTTP 200 JSONL generation failures into a sanitized 502 before content, or a fixed public stream failure after partial output, so fallback and request persistence no longer record a false successful stop. ([#12456](https://github.com/diegosouzapw/OmniRoute/pull/12456)) +- **fix(providers):** keep 1min.ai HTTP 200 stream errors out of assistant content, preserve partial output, and expose sanitized terminal errors so pre-content failures can fall back. ([#12466](https://github.com/diegosouzapw/OmniRoute/pull/12466)) +- **fix(sessions):** preserve legacy Sessions data when the additive exclusive-session projection is unavailable, returning an empty projection and warning only once per contiguous outage instead of failing the endpoint; the Sessions badge intentionally reflects the merged legacy and exclusive row count introduced by [#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389) ([#11469](https://github.com/diegosouzapw/OmniRoute/pull/11469)) — thanks @KaspaPulse +- Render Nimble Search with the generic provider icon and serve Opper's proven logo locally. ([#12415](https://github.com/diegosouzapw/OmniRoute/pull/12415)) +- **fix(quality):** `config/quality/provider-assets-provenance.jsonl` was stale against the physical `public/providers/` tree (still listing 225 assets and 5 duplicate-content groups after several provider-retirement PRs removed 85 icon files), which would have made `check:provider-asset-provenance` fail on the next full run. Reconciled the manifest to the real 142-file snapshot (142 asset records, 1 duplicate-content group, 2 newly-registered files marked `unresolved` pending provenance research), repinned `auditedCommit`, and updated the regression test's hardcoded counts to match. ([#11876](https://github.com/diegosouzapw/OmniRoute/pull/11876)) +- fix(ui): wrap quota card footer action buttons to prevent clipping Refresh button on narrow cards — when extra action buttons such as `View credits` are present on Codex cards, allow the footer row to flex-wrap with `shrink-0` buttons so `Refresh now` remains visible and clickable across all responsive card widths ([#11464](https://github.com/diegosouzapw/OmniRoute/pull/11464)) — thanks @adevwithpurpose +- Keep Antigravity Gemini usable when the same connection's Claude weekly quota is empty; generic quota cache stays per-connection for every other provider. ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) — thanks @HouMinXi +- Fixed Tailscale tunnel setup to reject ports outside the valid 1-65535 range and safely install Windows MSI packages from paths containing apostrophes. ([#11534](https://github.com/diegosouzapw/OmniRoute/pull/11534)) — thanks @pacocartones +- Classify Cloudflared and Tailscale process-management routes as local-only and spawn-capable while preserving remote access to read-only tunnel status endpoints. ([#11531](https://github.com/diegosouzapw/OmniRoute/pull/11531)) — thanks @pacocartones +- Electron release workflow: the `publish-npm` job now grants `actions: read` to the reusable `npm-publish.yml` it calls (its `publish` job requests it), which is what made GitHub refuse the whole v3.8.50 run at startup and ship the release with zero desktop assets; a `workflow_dispatch` now builds the requested tag instead of the dispatching branch and can skip the npm leg (`publish_npm=false`) when only re-attaching assets ([#11974](https://github.com/diegosouzapw/OmniRoute/pull/11974)) +- **fix(dashboard):** The Combos page usage guide now reads its dismissal through `useSyncExternalStore` instead of correcting SSR state inside an effect, removing an extra commit of the page tree on every load (and the `react-hooks/set-state-in-effect` error it raised). ([#12671](https://github.com/diegosouzapw/OmniRoute/pull/12671)) +- Electron release: `electron/package-lock.json` regained the optional `electron-builder-squirrel-windows` subtree (13 entries) that `npm ci` had been refusing as out of sync, `electron-release.yml` gained a `build_ref` dispatch input and stops regenerating release notes on a re-attach dispatch, and the npm publish workflow attaches the SBOM to the GitHub Release on dispatch publishes too — so the v3.8.51 tag ships every desktop asset and the SBOM like v3.8.49 did ([#12022](https://github.com/diegosouzapw/OmniRoute/pull/12022)) +- Drained the reds every PR against `release/v3.8.51` was born with: documented the three service-worker build-id variables, registered the six retirement/tunnel tests with the mutation gate, approved `eslint-plugin-react-hooks` in the dependency allowlist, added the six `combo.sort.*` strings to `vi` and `pt-BR`, pointed the ChatGPT Web doc at the real migration-168 test, worded the g4f hint around the member key, and realigned four tests to the retired-provider catalog and the legacy-schema fixtures the retirement migrations touch. ([#11940](https://github.com/diegosouzapw/OmniRoute/pull/11940)) +- **fix(providers):** Claude, Grok, LMArena, Notion, and Perplexity web-cookie transports now use pooled `wreq-js` 3.2 instead of the native sidecar, with all nine supported bindings pinned and audited, and the applicable platform binding plus native-license evidence included in each release artifact ([#12429](https://github.com/diegosouzapw/OmniRoute/pull/12429), supersedes [#11753](https://github.com/diegosouzapw/OmniRoute/pull/11753)). +- **fix(providers):** Zed Hosted streaming failures now trigger fallback before content and end partial streams with a sanitized structured error instead of fake assistant text and a normal-success stop. ([#12455](https://github.com/diegosouzapw/OmniRoute/pull/12455)) +- **fix(test):** capture hook result after render (direct commit `b85d0abb0b`) +- **fix(sync):** repair the two regressions the v3.8.50 sync-back left on release/v3.8.51 (direct commit `5ade9e0851`) +- **revert(models):** drop the memo reset hook ported from #10055 (direct commit `282de841ae`) +- **fix(typecheck):** guard the optional connectionId before the reactive model sync (direct commit `f2825bad75`) +- **fix(quota-share):** release the winner's reserved in-flight slot (#11371) ([#11408](https://github.com/diegosouzapw/OmniRoute/pull/11408)) — thanks @oyi77 +- **fix(providers):** stop silently dropping reasoning effort on opencode families (#10788) ([#11409](https://github.com/diegosouzapw/OmniRoute/pull/11409)) — thanks @oyi77 +- **fix(combo):** enable genuine quota-aware routing for generic providers (antigravity, claude, etc.) ([#11411](https://github.com/diegosouzapw/OmniRoute/pull/11411)) — thanks @HouMinXi +- **fix(oauth):** allow expired token retry before deactivation and activate expired retry loop (#P0) ([#11414](https://github.com/diegosouzapw/OmniRoute/pull/11414)) — thanks @HouMinXi +- **perf(executors):** lazy-load the executor registry — defer class imports + construction to first use (#11220) ([#11421](https://github.com/diegosouzapw/OmniRoute/pull/11421)) — thanks @oyi77 +- **fix:** make release/v3.8.50 CI gates green (run 32786966560 root causes) ([#11450](https://github.com/diegosouzapw/OmniRoute/pull/11450)) — thanks @jonlwheat2-gif +- **fix(sse):** exact-domain cookie match and origin-equality URL assertions ([#11452](https://github.com/diegosouzapw/OmniRoute/pull/11452)) — thanks @hartmark +- **fix(providers):** send Responses API payload during OpenAI-compatible validation ([#11454](https://github.com/diegosouzapw/OmniRoute/pull/11454)) — thanks @tuandinh0801 +- **fix(command-code):** fallback to /alpha/generate for Go plan without Provider API access ([#11455](https://github.com/diegosouzapw/OmniRoute/pull/11455)) — thanks @Chewji9875 +- **fix(responses-continuation):** fail closed on a log-truncated stored input/output array ([#11473](https://github.com/diegosouzapw/OmniRoute/pull/11473)) — thanks @hartmark +- **fix(cli):** write jcode settings as [providers.omniroute] in config.toml ([#11484](https://github.com/diegosouzapw/OmniRoute/pull/11484)) — thanks @hongnoul +- **fix(logging):** raise chat-log array truncation cap to 1000 and unify duplicate implementations ([#11499](https://github.com/diegosouzapw/OmniRoute/pull/11499)) — thanks @hartmark +- **fix:** restore live websocket URL helpers ([#11509](https://github.com/diegosouzapw/OmniRoute/pull/11509)) — thanks @PauloFH +- **perf(core):** eliminate async waterfalls across home, cache, and db operations (#11396) ([#11512](https://github.com/diegosouzapw/OmniRoute/pull/11512)) — thanks @HouMinXi +- **fix(kiro):** add runtime.us-east-1.kiro.dev as first-attempt endpoint ([#11517](https://github.com/diegosouzapw/OmniRoute/pull/11517)) — thanks @rifqiawl +- **fix(providers):** remove duplicate 128-tool truncation in OpencodeExecutor (#11444) ([#11518](https://github.com/diegosouzapw/OmniRoute/pull/11518)) — thanks @HouMinXi +- **fix(providers):** poll onboardUser LRO before classifying BYOP ([#11519](https://github.com/diegosouzapw/OmniRoute/pull/11519)) — thanks @rifqiawl +- **fix(translator):** preserve exact tool name casing for Claude tool_use payloads (#11487) ([#11520](https://github.com/diegosouzapw/OmniRoute/pull/11520)) — thanks @HouMinXi +- **fix(kimi-web):** align health probe with executor domain ([#11521](https://github.com/diegosouzapw/OmniRoute/pull/11521)) — thanks @pacocartones +- **fix(cli):** preserve existing admin password during setup ([#11522](https://github.com/diegosouzapw/OmniRoute/pull/11522)) — thanks @pacocartones +- **fix(mitm):** keep Windows DNS elevation off the POSIX sudo path (#11430) ([#11542](https://github.com/diegosouzapw/OmniRoute/pull/11542)) — thanks @official-burak +- **fix(sse):** scale chat admission by ingest byte budget instead of a fixed request count ([#11548](https://github.com/diegosouzapw/OmniRoute/pull/11548)) — thanks @TheDemonTuan +- **fix:** accept Gemini Web cookie exports ([#11554](https://github.com/diegosouzapw/OmniRoute/pull/11554)) — thanks @rafacpti23 +- **fix(i18n):** complete Brazilian Portuguese translations ([#11558](https://github.com/diegosouzapw/OmniRoute/pull/11558)) — thanks @rafacpti23 +- **fix(sse):** explicit types for openai-responses pureHelpers — clears last failing core typecheck gate ([#11567](https://github.com/diegosouzapw/OmniRoute/pull/11567)) — thanks @jonlwheat2-gif +- **fix(opencode):** hydrate Proxy Pool references for no-auth connections ([#11584](https://github.com/diegosouzapw/OmniRoute/pull/11584)) — thanks @AStupidBear +- **fix(ci):** resolve inherited release/v3.8.51 base-red CI failures ([#11608](https://github.com/diegosouzapw/OmniRoute/pull/11608)) — thanks @jonlwheat2-gif +- **fix(db):** fallback load STORAGE_ENCRYPTION_KEY from env files ([#11614](https://github.com/diegosouzapw/OmniRoute/pull/11614)) — thanks @benzntech +- **fix(providers):** mark Brave Search as no longer having a free tier ([#11621](https://github.com/diegosouzapw/OmniRoute/pull/11621)) — thanks @turbolego +- **fix:** pass tool result content through as string in OpenAI/Claude -> Gemini translation ([#11624](https://github.com/diegosouzapw/OmniRoute/pull/11624)) — thanks @Hsia97 +- **fix(sse):** scope credential health to exact key ([#11628](https://github.com/diegosouzapw/OmniRoute/pull/11628)) — thanks @KaspaPulse +- **fix(install):** guard prepare script against missing husky ([#11634](https://github.com/diegosouzapw/OmniRoute/pull/11634)) — thanks @Prajeeth-12 +- **fix(security):** redact NVIDIA_API_KEY at the log sink in the ad-hoc diag script ([#11639](https://github.com/diegosouzapw/OmniRoute/pull/11639)) +- **fix(types):** clear the two type regressions red on release/v3.8.51 ([#11640](https://github.com/diegosouzapw/OmniRoute/pull/11640)) — thanks @jonlwheat2-gif +- **fix(build):** resolve workspace: protocol dependencies to concrete semver versions during prepublish (#11540) ([#11641](https://github.com/diegosouzapw/OmniRoute/pull/11641)) — thanks @HouMinXi +- **fix(search):** properly resolve configured search connection in /v1/responses pipeline (#11524) ([#11642](https://github.com/diegosouzapw/OmniRoute/pull/11642)) — thanks @HouMinXi +- **fix(authz):** trust cf-connecting-ip for via-proxy detection when peer is Cloudflare edge (#11514) ([#11643](https://github.com/diegosouzapw/OmniRoute/pull/11643)) — thanks @HouMinXi +- **fix(security):** compare the OIDC state cookie in constant time ([#11646](https://github.com/diegosouzapw/OmniRoute/pull/11646)) +- **fix(security):** namespace the dedup hash by the calling API key ([#11649](https://github.com/diegosouzapw/OmniRoute/pull/11649)) +- **fix(models):** gate the Codex-native catalog loop on prefix mode (#11632) ([#11662](https://github.com/diegosouzapw/OmniRoute/pull/11662)) — thanks @xiaoyaner0201 +- **fix(dashboard):** project Codex routing quota in account pools ([#11666](https://github.com/diegosouzapw/OmniRoute/pull/11666)) — thanks @KaspaPulse +- **fix(ddgw):** normaliza system no upstream da release v3.8.51 ([#11692](https://github.com/diegosouzapw/OmniRoute/pull/11692)) — thanks @RhianB14 +- **fix(memory):** honor category filter in GET /api/memory ([#11699](https://github.com/diegosouzapw/OmniRoute/pull/11699)) — thanks @Sabeekhann +- **fix(ci):** detect stale workspace lockfile entries ([#11714](https://github.com/diegosouzapw/OmniRoute/pull/11714)) — thanks @pacocartones +- **fix(ci):** stamp dist/BUILD_SHA before the npm publish provenance gate ([#11721](https://github.com/diegosouzapw/OmniRoute/pull/11721)) +- **fix(docker):** decouple the best-effort Bun image from the release manifest ([#11724](https://github.com/diegosouzapw/OmniRoute/pull/11724)) +- **perf(compression):** avoid cloning memo misses twice ([#11727](https://github.com/diegosouzapw/OmniRoute/pull/11727)) — thanks @RaviTharuma +- **fix(docker):** let the bun digest artifact be absent, not fatal ([#11740](https://github.com/diegosouzapw/OmniRoute/pull/11740)) +- **fix(shared):** block cloud-metadata hosts under default remote-image guard ([#11755](https://github.com/diegosouzapw/OmniRoute/pull/11755)) — thanks @santosraju99-hub +- **fix(api):** preserve caller-provided X-Correlation-Id on chat completions ([#11760](https://github.com/diegosouzapw/OmniRoute/pull/11760)) — thanks @aniruddhaadak80 +- **fix(resilience):** lock permanently retired models instead of short backoff (Gemini ban prevention) ([#11762](https://github.com/diegosouzapw/OmniRoute/pull/11762)) — thanks @turbolego +- **fix:** mark Vercel AI Gateway as passthroughModels ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **fix(resilience):** stop hammering permanently-moved endpoints and billing-suspended accounts ([#11774](https://github.com/diegosouzapw/OmniRoute/pull/11774)) — thanks @turbolego +- **fix(lease):** remove global static reservation and gate routing on live active lease occupancy ([#11775](https://github.com/diegosouzapw/OmniRoute/pull/11775)) — thanks @TheDemonTuan +- **fix(ci):** size the install-upgrade gate to a measured run, and log the pack cost ([#11776](https://github.com/diegosouzapw/OmniRoute/pull/11776)) +- **fix(pwa):** stop serving the stale shell after deploys ([#11779](https://github.com/diegosouzapw/OmniRoute/pull/11779)) — thanks @HouMinXi +- **fix(combo):** lock GitHub models rejected as "not supported" for future requests ([#11781](https://github.com/diegosouzapw/OmniRoute/pull/11781)) — thanks @turbolego +- **fix(cli):** probe both IPv4 and IPv6 loopback for server readiness (#11766) ([#11794](https://github.com/diegosouzapw/OmniRoute/pull/11794)) — thanks @vermasomesh835 +- **fix(sse):** preserve CLIProxyAPI wrappers per connection ([#11797](https://github.com/diegosouzapw/OmniRoute/pull/11797)) — thanks @RaviTharuma +- **fix(providers):** honor model auto-fetch opt-in on create ([#11798](https://github.com/diegosouzapw/OmniRoute/pull/11798)) — thanks @RaviTharuma +- **fix(dashboard):** make model auto-fetch order independent ([#11805](https://github.com/diegosouzapw/OmniRoute/pull/11805)) — thanks @RaviTharuma +- **fix(docker):** make GHA cache exports best effort ([#11806](https://github.com/diegosouzapw/OmniRoute/pull/11806)) — thanks @RaviTharuma +- **fix(kiro):** do not permanently ban on 'User is not authorized to make this call' ([#11809](https://github.com/diegosouzapw/OmniRoute/pull/11809)) — thanks @Deftera186 +- **fix(sse):** keep cache-write tokens in OpenAI-shaped usage ([#11814](https://github.com/diegosouzapw/OmniRoute/pull/11814)) — thanks @TheDemonTuan +- **fix(docs):** resolve relative markdown and wiki links across Fumadocs and GitHub wiki ([#11834](https://github.com/diegosouzapw/OmniRoute/pull/11834)) — thanks @AndrianBalanescu +- **fix(chat):** preserve unstripped model string for passthrough provider routing ([#11840](https://github.com/diegosouzapw/OmniRoute/pull/11840)) — thanks @raheemuddin786 +- **fix(mcp):** avoid async initialization inside sync esm wrappers (#11569) ([#11849](https://github.com/diegosouzapw/OmniRoute/pull/11849)) — thanks @HouMinXi +- **fix(sse):** preserve coherent markdown token boundaries across stream translation chunks (#11606) ([#11851](https://github.com/diegosouzapw/OmniRoute/pull/11851)) — thanks @HouMinXi +- **fix(ci):** run the install-upgrade gate on disk, not on the /tmp tmpfs ([#11855](https://github.com/diegosouzapw/OmniRoute/pull/11855)) +- **fix(skills):** expand shorthand property types in injected tool schemas ([#11857](https://github.com/diegosouzapw/OmniRoute/pull/11857)) — thanks @alvinveroy +- **fix(ci):** publish npm from a hosted runner so provenance is accepted ([#11877](https://github.com/diegosouzapw/OmniRoute/pull/11877)) +- **fix(lint):** pin react hooks plugin to 7.0.1 ([#11886](https://github.com/diegosouzapw/OmniRoute/pull/11886)) +- **fix(ci):** surface and clear release-green ESLint failure ([#11890](https://github.com/diegosouzapw/OmniRoute/pull/11890)) — thanks @MumuTW +- **fix(guardrails):** prevent duplicate prompt-injection-guard log output ([#11936](https://github.com/diegosouzapw/OmniRoute/pull/11936)) — thanks @patrykkopycinski +- **fix(auth):** downgrade expected transient states from warn to debug ([#11937](https://github.com/diegosouzapw/OmniRoute/pull/11937)) — thanks @patrykkopycinski +- **fix(codex):** fail over image generation for imported free plans ([#11948](https://github.com/diegosouzapw/OmniRoute/pull/11948)) — thanks @Bl0ck154 +- **fix(codex):** normalize non-stream responses ([#11951](https://github.com/diegosouzapw/OmniRoute/pull/11951)) — thanks @Bl0ck154 +- **fix(images):** forward Antigravity image size ([#11952](https://github.com/diegosouzapw/OmniRoute/pull/11952)) — thanks @Bl0ck154 +- **fix(db):** invalidate connection cache after upsert ([#11953](https://github.com/diegosouzapw/OmniRoute/pull/11953)) — thanks @Bl0ck154 +- **fix(codex):** restore imported account state ([#11954](https://github.com/diegosouzapw/OmniRoute/pull/11954)) — thanks @Bl0ck154 +- **fix(sse):** give extended-thinking targets the reasoning readiness budget ([#11959](https://github.com/diegosouzapw/OmniRoute/pull/11959)) — thanks @abhisheksharma2411 +- **fix(dashboard):** use opaque background and readable text color on cost chart tooltips ([#11960](https://github.com/diegosouzapw/OmniRoute/pull/11960)) — thanks @ZaimMarzuki +- **fix(antigravity):** send complete loadCodeAssist metadata (ideType/platform/pluginType as numeric enums) ([#11969](https://github.com/diegosouzapw/OmniRoute/pull/11969)) — thanks @rifqiawl +- **fix(core):** resolve DB init race condition and reasoning translation ([#12003](https://github.com/diegosouzapw/OmniRoute/pull/12003)) — thanks @raheemuddin786 +- **fix(admission):** exclude reclaimable page cache from the cgroup pressure ratio ([#12017](https://github.com/diegosouzapw/OmniRoute/pull/12017)) — thanks @HouMinXi +- **fix(build):** prune non-production trees in NFT trace excludes and tsconfig ([#12028](https://github.com/diegosouzapw/OmniRoute/pull/12028)) — thanks @Chewji9875 +- **fix(migrations):** add renamed migration compatibility for 056/073/077/101 ([#12036](https://github.com/diegosouzapw/OmniRoute/pull/12036)) — thanks @oyi77 +- **fix(sse):** default crash-guard logger to console.warn, not console ([#12042](https://github.com/diegosouzapw/OmniRoute/pull/12042)) — thanks @HouMinXi +- **fix(catalog):** derive vision/modalities for built-in auto combos from effective target pool ([#12046](https://github.com/diegosouzapw/OmniRoute/pull/12046)) — thanks @Prajeeth-12 +- **fix(release):** the packaged-app smoke verifies the database opened, not a driver line the primary path never prints (twin of #12032) ([#12047](https://github.com/diegosouzapw/OmniRoute/pull/12047)) +- **fix(barrel):** migrate src/app/ off the @/lib/localDb barrel import (#11795 Phase 2) ([#12051](https://github.com/diegosouzapw/OmniRoute/pull/12051)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate src/lib/ off the localDb barrel to direct db imports (#59) ([#12052](https://github.com/diegosouzapw/OmniRoute/pull/12052)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate open-sse, src/shared, src/sse, src/models, src/domain off the @/lib/localDb barrel import (#11795 Phase 4) ([#12053](https://github.com/diegosouzapw/OmniRoute/pull/12053)) — thanks @jonlwheat2-gif +- **fix(barrel):** delete the @/lib/localDb barrel — every consumer migrated (#11795 Phase 5) ([#12055](https://github.com/diegosouzapw/OmniRoute/pull/12055)) — thanks @jonlwheat2-gif +- **fix(radar):** restore D12 public boundary ([#12057](https://github.com/diegosouzapw/OmniRoute/pull/12057)) +- **fix(dev):** bound webpack and Tailwind scans ([#12075](https://github.com/diegosouzapw/OmniRoute/pull/12075)) — thanks @backryun +- **fix(dev):** isolate root layout settings reads ([#12076](https://github.com/diegosouzapw/OmniRoute/pull/12076)) — thanks @backryun +- **fix(dev):** reduce instrumentation executor fan-out (phase 3) ([#12078](https://github.com/diegosouzapw/OmniRoute/pull/12078)) — thanks @backryun +- **fix(dev):** make logging resources HMR-singleton ([#12079](https://github.com/diegosouzapw/OmniRoute/pull/12079)) — thanks @backryun +- **fix(sse):** keep unavailable forced connections scoped ([#12080](https://github.com/diegosouzapw/OmniRoute/pull/12080)) — thanks @keeltrace +- **fix(dev):** isolate batch dispatch from instrumentation ([#12081](https://github.com/diegosouzapw/OmniRoute/pull/12081)) — thanks @backryun +- **fix(ci):** reconcile release test contract drift ([#12082](https://github.com/diegosouzapw/OmniRoute/pull/12082)) — thanks @backryun +- **fix(ci):** accept CVE-2025-68121 in the prebuilt tls-client .so, auto-close base-red issues, guard Scorecard on the default branch ([#12085](https://github.com/diegosouzapw/OmniRoute/pull/12085)) +- **fix(db):** drop three consumer-less 1proxy exports — dead-code base-red on release/v3.8.51 after the barrel deletion (#12055) ([#12087](https://github.com/diegosouzapw/OmniRoute/pull/12087)) +- **fix(api):** clear the six API-route TypeScript regressions the new gate landed red on ([#12094](https://github.com/diegosouzapw/OmniRoute/pull/12094)) +- **fix(release):** never let the tag-push Create Release append auto notes to the curated body ([#12096](https://github.com/diegosouzapw/OmniRoute/pull/12096)) +- **fix(sse):** honor CLIProxyAPI environment API key ([#12099](https://github.com/diegosouzapw/OmniRoute/pull/12099)) — thanks @RaviTharuma +- **fix(oauth):** bind Google refresh to the client that issued the token ([#12106](https://github.com/diegosouzapw/OmniRoute/pull/12106)) — thanks @HouMinXi +- **fix(dashboard):** make RequestLoggerDetail loadable outside Next — CSS via globals.css + CJS/ESM interop (#11703 base-reds) ([#12114](https://github.com/diegosouzapw/OmniRoute/pull/12114)) +- **fix(leases):** project status lease row to lease columns so joined connection PII never escapes ([#12115](https://github.com/diegosouzapw/OmniRoute/pull/12115)) — thanks @geek007git +- **fix(plugins):** do not kill the plugin process when a fire-and-forget hook times out ([#12116](https://github.com/diegosouzapw/OmniRoute/pull/12116)) — thanks @geek007git +- **fix(guardrails):** restore injection-guard logging on middleware-only routes ([#12117](https://github.com/diegosouzapw/OmniRoute/pull/12117)) — thanks @geek007git +- **fix(plugins):** refresh stored manifest from disk on activate so new hook fields reach existing installs ([#12120](https://github.com/diegosouzapw/OmniRoute/pull/12120)) — thanks @geek007git +- **fix(codex):** preserve existing provider state when bulk-import upserts a matching connection ([#12122](https://github.com/diegosouzapw/OmniRoute/pull/12122)) — thanks @geek007git +- **fix(ci):** clear the two base-reds the 2026-08-30 merge batch left on release/v3.8.51 (round 3) ([#12123](https://github.com/diegosouzapw/OmniRoute/pull/12123)) +- **fix(sse):** strip type:'custom' from Claude tools on agentrouter dispatch ([#12126](https://github.com/diegosouzapw/OmniRoute/pull/12126)) — thanks @wahidsadik371-coder +- **fix(sse):** stop advertising video providers the dispatcher cannot run ([#12131](https://github.com/diegosouzapw/OmniRoute/pull/12131)) — thanks @quiterunner-commits +- **fix(search):** treat HTTP 432 and plan limit errors as transient cooldown ([#12139](https://github.com/diegosouzapw/OmniRoute/pull/12139)) — thanks @HouMinXi +- **fix(sse):** refuse an AI Horde queue that cannot fit the request budget ([#12143](https://github.com/diegosouzapw/OmniRoute/pull/12143)) — thanks @quiterunner-commits +- **fix(ci):** clear the base-reds the afternoon merge batch left on release/v3.8.51 (round 5: provider count 352, TS2554/TS2677) ([#12144](https://github.com/diegosouzapw/OmniRoute/pull/12144)) +- **fix(translator):** strip neutral tool_choice when tools absent in Responses-to-Chat (#12141) ([#12166](https://github.com/diegosouzapw/OmniRoute/pull/12166)) — thanks @HouMinXi +- **fix(guardrails):** pass providerId to getResolvedModelCapabilities in checkComboVision (#12112) ([#12169](https://github.com/diegosouzapw/OmniRoute/pull/12169)) — thanks @HouMinXi +- **fix(translator):** strip plaintext reasoning content for opaque responses backends (#12128) ([#12171](https://github.com/diegosouzapw/OmniRoute/pull/12171)) — thanks @HouMinXi +- **perf(stream):** compile hot-path regexes once, bound token caches, fix quadratic buffering ([#12179](https://github.com/diegosouzapw/OmniRoute/pull/12179)) — thanks @opensource-elearning +- **fix:** resolve compression worker file using runtime anchors instead of… ([#12183](https://github.com/diegosouzapw/OmniRoute/pull/12183)) — thanks @NightStalker-87 +- **fix:** prevent Claude Code session kills via liveness-aware readiness + auto model echo ([#12189](https://github.com/diegosouzapw/OmniRoute/pull/12189)) — thanks @opensource-elearning +- **fix(translator):** preserve falsy primitive values in Gemini and Antigravity function response results ([#12191](https://github.com/diegosouzapw/OmniRoute/pull/12191)) — thanks @Siva010 +- **fix(oauth):** mark empty Antigravity projectId as degraded and clear stale errors (#11284) ([#12205](https://github.com/diegosouzapw/OmniRoute/pull/12205)) — thanks @HouMinXi +- **fix(providers):** gate the Codex auto-ping usage read on the shared quota throttle ([#12209](https://github.com/diegosouzapw/OmniRoute/pull/12209)) — thanks @abhisheksharma2411 +- **fix(combos):** prioritize SQLite row id over inner JSON id and notify delete errors ([#12213](https://github.com/diegosouzapw/OmniRoute/pull/12213)) — thanks @HouMinXi +- **fix(providers):** mark featherless.ai as no longer having a free tier (`hasFree: false`) ([#12216](https://github.com/diegosouzapw/OmniRoute/pull/12216)) — thanks @turbolego +- **fix(deepseek-web):** stop Turbopack dev panic in the PoW worker path resolver ([#12221](https://github.com/diegosouzapw/OmniRoute/pull/12221)) — thanks @hartmark +- **fix(chat-admission):** clarify local 503 source ([#12223](https://github.com/diegosouzapw/OmniRoute/pull/12223)) — thanks @keeltrace +- **fix(dev):** silence webpack runtime module warnings ([#12228](https://github.com/diegosouzapw/OmniRoute/pull/12228)) — thanks @backryun +- **fix(db):** use module.require for CommonJS runtime driver loading ([#12230](https://github.com/diegosouzapw/OmniRoute/pull/12230)) — thanks @raheemuddin786 +- **fix(memory):** honest probe-driven FTS5 keyword status + memory_id rowid sync ([#12231](https://github.com/diegosouzapw/OmniRoute/pull/12231)) — thanks @raheemuddin786 +- **fix(system):** propagate abort signal to stream reader in HTTP version checks ([#12232](https://github.com/diegosouzapw/OmniRoute/pull/12232)) — thanks @raheemuddin786 +- **fix(resilience):** isolate local host execution errors from provider circuit breakers ([#12233](https://github.com/diegosouzapw/OmniRoute/pull/12233)) — thanks @raheemuddin786 +- **fix(sse):** sort injected tools deterministically for prompt caching ([#12234](https://github.com/diegosouzapw/OmniRoute/pull/12234)) — thanks @raheemuddin786 +- **fix(combo):** always clear the loop-safety timer, not just on the happy path (#11804) ([#12245](https://github.com/diegosouzapw/OmniRoute/pull/12245)) +- **fix(quality):** base-red pair — stryker tap registration + turn-pin suites aligned to the window gate ([#12255](https://github.com/diegosouzapw/OmniRoute/pull/12255)) — thanks @backryun +- **fix(dev):** qualify Turbopack runtime boundaries ([#12258](https://github.com/diegosouzapw/OmniRoute/pull/12258)) — thanks @backryun +- **fix(combo):** fall back to full pool when collapsed sole survivor is context-too-small ([#12278](https://github.com/diegosouzapw/OmniRoute/pull/12278)) — thanks @opensource-elearning +- **fix(combo):** bound the pre-dispatch unavailable skip so a stale label cannot dark a pool (#12168) ([#12285](https://github.com/diegosouzapw/OmniRoute/pull/12285)) +- **fix(security):** strip Qwen/Alibaba console-session cookies from provider API responses ([#12287](https://github.com/diegosouzapw/OmniRoute/pull/12287)) +- **fix(usage):** console-aware Token Plan guidance + subscription hint on bailian 401 ([#12288](https://github.com/diegosouzapw/OmniRoute/pull/12288)) +- **fix(resourcePressure):** log numeric detail on every rejection, recover faster ([#12293](https://github.com/diegosouzapw/OmniRoute/pull/12293)) — thanks @hartmark +- **fix(release):** drain the 2026-09-01 base-red window — passthrough usage regression + radarPage i18n keys ([#12327](https://github.com/diegosouzapw/OmniRoute/pull/12327)) +- **fix(ci):** openapi-security-tiers checker must honor routeGuard patterns + imported prefixes ([#12350](https://github.com/diegosouzapw/OmniRoute/pull/12350)) — thanks @ggiak +- **perf(ci):** cache node_modules in the npm-ci-retry composite (#8084 D3) ([#12408](https://github.com/diegosouzapw/OmniRoute/pull/12408)) +- **revert(ui):** point CTAs back at their real destinations ([#12410](https://github.com/diegosouzapw/OmniRoute/pull/12410)) +- **fix(release):** drain the 2026-09-02 base-red — rerank-providers import + api-typecheck baseline ratchet ([#12414](https://github.com/diegosouzapw/OmniRoute/pull/12414)) +- **fix(types):** make system prompt injection noImplicitAny-safe ([#12416](https://github.com/diegosouzapw/OmniRoute/pull/12416)) +- **fix(quality):** validate typecheck baseline schema ([#12419](https://github.com/diegosouzapw/OmniRoute/pull/12419)) +- **fix(memory):** resolve rerank provider node cache import ([#12421](https://github.com/diegosouzapw/OmniRoute/pull/12421)) +- **fix(ci):** repair release-root regressions — pack dedup, web-session syntax, uc-image ids ([#12423](https://github.com/diegosouzapw/OmniRoute/pull/12423)) — thanks @backryun +- **fix(providers):** separate MaxAI and UC credential contracts ([#12431](https://github.com/diegosouzapw/OmniRoute/pull/12431)) +- **fix(cli):** preserve Claude settings on config set ([#12432](https://github.com/diegosouzapw/OmniRoute/pull/12432)) — thanks @KrzysiekSko +- **fix(providers):** repair the maxai credential block truncated by merge auto-resolve ([#12433](https://github.com/diegosouzapw/OmniRoute/pull/12433)) +- **fix(quality):** record the 2026-09-02 merged growth in the file-size baseline ([#12434](https://github.com/diegosouzapw/OmniRoute/pull/12434)) +- **fix(uc):** route UC error strings through sanitizeErrorMessage; allowlist the retired codex id ([#12437](https://github.com/diegosouzapw/OmniRoute/pull/12437)) +- **fix(adapta):** redact streamed upstream errors ([#12438](https://github.com/diegosouzapw/OmniRoute/pull/12438)) +- **fix(sse):** remap non-contiguous upstream tool_calls index to a gap-free output_index ([#12445](https://github.com/diegosouzapw/OmniRoute/pull/12445)) — thanks @hartmark +- **fix(quality):** detect duplicate tool_calls entries in one response ([#12446](https://github.com/diegosouzapw/OmniRoute/pull/12446)) — thanks @hartmark +- **fix(conversations):** resolve turn content OmniRoute never sends back to the client ([#12447](https://github.com/diegosouzapw/OmniRoute/pull/12447)) — thanks @hartmark +- **fix(combo):** do not treat credits-exhausted 401 as auth skip ([#12449](https://github.com/diegosouzapw/OmniRoute/pull/12449)) — thanks @RaviTharuma +- **fix(auth):** do not park healthy quota accounts as expired ([#12452](https://github.com/diegosouzapw/OmniRoute/pull/12452)) — thanks @RaviTharuma +- **fix(responses-continuation):** fail closed on a collector-truncated, empty output array ([#12460](https://github.com/diegosouzapw/OmniRoute/pull/12460)) — thanks @hartmark +- **fix(logging):** raise the SSE payload collector's default cap ([#12461](https://github.com/diegosouzapw/OmniRoute/pull/12461)) — thanks @hartmark +- **fix(resilience):** surface Responses failed.error.message in 502s ([#12472](https://github.com/diegosouzapw/OmniRoute/pull/12472)) — thanks @RaviTharuma +- **fix(security):** unbiased maxai X-Random nonce + stricter URL/regex assertions ([#12502](https://github.com/diegosouzapw/OmniRoute/pull/12502)) +- **fix(video):** re-anchor transcript log-redaction so PII/credential maskers can't reopen the leak (#12150 P1 follow-up) ([#12503](https://github.com/diegosouzapw/OmniRoute/pull/12503)) +- **fix(monitoring):** serve cached credentialHealth off the request path ([#12533](https://github.com/diegosouzapw/OmniRoute/pull/12533)) — thanks @RaviTharuma +- **fix(quota):** keep Antigravity Gemini usable when Claude weekly is empty ([#12566](https://github.com/diegosouzapw/OmniRoute/pull/12566)) — thanks @HouMinXi +- **fix(authz):** hard-gate every credential export and CLI-config write (GHSA-5926-2w35-7h4q) ([#12600](https://github.com/diegosouzapw/OmniRoute/pull/12600)) +- **fix(docs):** restore the Next build — REMOVED_PROVIDERS.md had no frontmatter (base-red #12581) ([#12610](https://github.com/diegosouzapw/OmniRoute/pull/12610)) +- **fix(quality):** re-point the zcodeProtocol public-creds allowlist to line 313 ([#12615](https://github.com/diegosouzapw/OmniRoute/pull/12615)) +- **fix(security):** close 3 advisories — search baseUrl exfil, sk- in the error sanitizer, bifrost relay header leak ([#12620](https://github.com/diegosouzapw/OmniRoute/pull/12620)) +- **fix(providers):** sanitize boolean required and nested bare maps for Gemini (#12269) ([#12624](https://github.com/diegosouzapw/OmniRoute/pull/12624)) — thanks @HouMinXi +- **fix(resilience):** keep Overloaded STREAM_EARLY_EOF off the provider breaker ([#12626](https://github.com/diegosouzapw/OmniRoute/pull/12626)) — thanks @HouMinXi +- **fix(catalog):** re-audit free-tier quotas against official pages ([#12649](https://github.com/diegosouzapw/OmniRoute/pull/12649)) +- **fix(dashboard):** keep a request's pending-tracking id stable across combo target retries ([#12650](https://github.com/diegosouzapw/OmniRoute/pull/12650)) — thanks @hartmark +- **fix(tests):** retire dead model ids from the chat-pipeline integration suite (base-red #12581) ([#12670](https://github.com/diegosouzapw/OmniRoute/pull/12670)) +- **fix:** resolve SqliteError no such table compression_run_telemetry during cleanup ([#12682](https://github.com/diegosouzapw/OmniRoute/pull/12682)) — thanks @groovecityJO +- **fix(combo):** treat a pin-only step as implicit connection allowlist ([#12697](https://github.com/diegosouzapw/OmniRoute/pull/12697)) — thanks @HouMinXi +- **fix(ci):** ship .npmrc in published package so legacy-peer-deps applies to consumers (#11544) ([#12699](https://github.com/diegosouzapw/OmniRoute/pull/12699)) — thanks @KooshaPari +- **fix(docker):** pin 4 CLI tools to exact versions (#12576) ([#12703](https://github.com/diegosouzapw/OmniRoute/pull/12703)) — thanks @KooshaPari +- **fix(docker):** document and harden cli profile trust boundary (#12570) ([#12706](https://github.com/diegosouzapw/OmniRoute/pull/12706)) — thanks @KooshaPari +- **fix(sse):** redact video transcript in pre-guardrail rejected-request logs (#12150 P2 item 7) ([#12710](https://github.com/diegosouzapw/OmniRoute/pull/12710)) +- **fix(i18n):** quote placeholder in OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES description (#12505) ([#12769](https://github.com/diegosouzapw/OmniRoute/pull/12769)) — thanks @KooshaPari +- **fix(api/pricing):** surface validation error message as string, not raw object (#12494) ([#12771](https://github.com/diegosouzapw/OmniRoute/pull/12771)) — thanks @KooshaPari +- **fix(sse):** re-enable prompt compression for native Codex passthrough ([#12834](https://github.com/diegosouzapw/OmniRoute/pull/12834)) — thanks @thomasmaerz +- **fix(dashboard):** model health tests for a provider node set to the Responses API now call `/v1/responses` with a Responses-shaped body instead of `/v1/chat/completions` — those models were reported as `Provider returned HTTP 200 but no text content` even though the same model answered normally through `/v1/responses` ([#13070](https://github.com/diegosouzapw/OmniRoute/issues/13070)) ([#13087](https://github.com/diegosouzapw/OmniRoute/pull/13087)) — thanks @ntdatt812 +- fix(routing): stop a round-robin combo's "opencode" targets from collapsing onto the opencode-zen connection (#11912) ([#13283](https://github.com/diegosouzapw/OmniRoute/pull/13283)) +- fix(routing): stop the reactive-compaction debug log from lying when compression is globally disabled (#11977) ([#13276](https://github.com/diegosouzapw/OmniRoute/pull/13276)) +- fix(electron): relativize standalone-bundle symlink targets so Stage 8 manifest verification stops failing on Windows (#11979) ([#13251](https://github.com/diegosouzapw/OmniRoute/pull/13251)) +- fix(dashboard): surface a visible error when Compression Studio's combined preview run fails (#12061) ([#13288](https://github.com/diegosouzapw/OmniRoute/pull/13288)) +- fix(dashboard): make the compression "Effective pipeline" preview honor the active profile and warn when the master switch is off (#12063) ([#13287](https://github.com/diegosouzapw/OmniRoute/pull/13287)) +- fix(providers): scope TinyCMS Web signer's DOM shims to each call instead of leaking them for the process lifetime, and surface a clean HTTP status on a non-JSON interception-toggles error (#12072) ([#13254](https://github.com/diegosouzapw/OmniRoute/pull/13254)) +- fix(guardrails): stop Vision Bridge from re-selecting a model locked after a 404 (#12111) ([#13259](https://github.com/diegosouzapw/OmniRoute/pull/13259)) +- fix(sse): require Responses-shaped body before native OpenAI-compatible passthrough (#12129) ([#13278](https://github.com/diegosouzapw/OmniRoute/pull/13278)) +- fix(providers): minimax-m3 now collapses manual thinking.type:"enabled" to adaptive, preventing upstream 400 (2013) (#12132) ([#13249](https://github.com/diegosouzapw/OmniRoute/pull/13249)) +- fix(db): scope model visibility overrides by modality so hiding a Chat model no longer hides an identically-ID'd Image/Embeddings/etc. model (#12172) ([#13258](https://github.com/diegosouzapw/OmniRoute/pull/13258)) +- fix(db): scope local-provider apiKey dedup to matching base URL so LM Studio/Ollama-style connections support multiple accounts (#12173) ([#13250](https://github.com/diegosouzapw/OmniRoute/pull/13250)) +- fix(providers): refresh Trae's stale Referer/Origin and forward user timezone so imported connections stop failing with 401 (#12190) ([#13255](https://github.com/diegosouzapw/OmniRoute/pull/13255)) +- fix(providers): route opencode-go/gpt-5.6-luna to /responses instead of /chat/completions (#12196) ([#13275](https://github.com/diegosouzapw/OmniRoute/pull/13275)) +- fix(dashboard): allow deleting the last extra-upstream-header row even when invalid (#12251) ([#13252](https://github.com/diegosouzapw/OmniRoute/pull/13252)) +- **fix(i18n):** translate pre-existing `__MISSING__:` keys for `combo.sort`, `requestLogger.detail` expand/collapse, `common.profile`, and `settings.resilienceCredentialHealth*` across 39 locales ([#12272](https://github.com/diegosouzapw/OmniRoute/issues/12272)) ([#13034](https://github.com/diegosouzapw/OmniRoute/pull/13034)) — thanks @HouMinXi +- fix(cli): run the Node.js runtime compatibility guard before the heavy `tsx/esm` + Commander import chain so an unsupported runtime gets a clear message instead of a raw `Invalid regular expression flags` crash (#12296) ([#13253](https://github.com/diegosouzapw/OmniRoute/pull/13253)) +- fix(dashboard): refresh the providers list after deleting a compatible provider node (#12298) ([#13256](https://github.com/diegosouzapw/OmniRoute/pull/13256)) +- fix(usage): fail closed on API-key budget enforcement when a provider's `auto` routing alias has no pricing row, instead of silently counting it as $0 (#12341) ([#13257](https://github.com/diegosouzapw/OmniRoute/pull/13257)) +- **fix(providers):** include the submitted Agnes video model when polling by `video_id` ([#12356](https://github.com/diegosouzapw/OmniRoute/pull/12356)) — thanks @pacocartones +- **fix(routing):** custom OpenAI-compatible nodes now honor the saved Chat/Responses API type after edits instead of letting the node's original ID prefix override the live connection setting ([#11884](https://github.com/diegosouzapw/OmniRoute/issues/11884)). ([#12358](https://github.com/diegosouzapw/OmniRoute/pull/12358)) — thanks @pacocartones +- fix(sse): surface an error instead of a silent empty 200 when a Claude stream closes with zero bytes (#12398) ([#13285](https://github.com/diegosouzapw/OmniRoute/pull/13285)) +- fix(oauth): warn before the dead localhost:8080 redirect in antigravity/gemini `oauth start` (#12413) ([#13265](https://github.com/diegosouzapw/OmniRoute/pull/13265)) +- fix(providers): stop devin-cli spawn error from double-closing the SSE controller (#12517) ([#13261](https://github.com/diegosouzapw/OmniRoute/pull/13261)) +- **fix(rerank):** clamp Voyage `top_k` to the documents actually sent after empty-string filtering, and honor `return_documents: false` in the NVIDIA response adapter (#12523 — thanks @pacocartones) +- **fix(audio):** `/v1/audio/translations` now resolves combo names the way `/v1/audio/transcriptions` already does, so a combo that `GET /v1/models` advertises is fanned out to its targets instead of being rejected with `400 Invalid translation model: . Use format: provider/model`; literal `provider/model` ids and unknown bare names behave as before (#12536 — thanks @pacocartones) +- **fix(gemini):** strip the JSON-Schema-2020-12 `prefixItems` keyword from Gemini tool schemas at every nesting level, so Claude Code tool definitions no longer fail with `400 Unknown name "prefixItems"` on Gemini models (#12540 — thanks @pacocartones) +- **fix(api-manager):** Expose an accessible loading status while API keys are fetched instead of an empty accessibility tree (#12541 — thanks @pacocartones) +- **fix(video):** Clamp `estimateJpegFrameBytes` at zero for padding-only payloads and build the three encode-side frame data URIs from `JPEG_FRAME_DATA_URI_PREFIX` instead of a repeated literal (#12543 — thanks @pacocartones) +- **fix(devin):** accept Windows `DEVIN_AGENTIC_HOME` sandbox paths (`C:\...\.sandbox\...`) in the isolated-home check so the Devin Claude Bridge no longer fails closed on Windows ([#12405](https://github.com/diegosouzapw/OmniRoute/issues/12405)) (#12545 — thanks @pacocartones) +- **fix(db):** Add `conversation_turn_nodes` and orphaned `agentic_conversations` to the auto-cleanup cycle under the existing `retention.callLogs` window, so identity nodes whose call-log content has already been purged no longer accumulate without bound in `storage.sqlite` (#12548 — thanks @pacocartones) +- **fix(i18n):** Wrap the `~/.claude/profiles//settings.json` placeholder in ICU single quotes in the `OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES` feature-flag description across all 42 locales and the TypeScript default, so next-intl no longer fails with `INVALID_MESSAGE: UNCLOSED_TAG` and the Feature Flags card shows the description instead of the raw key (#12549 — thanks @pacocartones) +- **fix(orchestration):** `updateCloudAgentTask` now publishes the task's real `status` on `agent.task.updated` when an update only touches `result`, `activities` or `error`, instead of the fabricated `"updated"` state, and stays silent when no row matched the id (#12550 — thanks @pacocartones) +- **fix(i18n):** the home "Recent Requests" panel and the Provider Topology legend are now translated instead of rendering English copies on non-English dashboards; the legend reads its own `home.topologyLegend*` labels with consistent casing rather than borrowing the memory-settings "Recent" and analytics "Error" strings (#12551 — thanks @pacocartones). +- **docs(reference):** bring the `FEATURE_FLAGS.md` catalog back to 1:1 with `featureFlagDefinitions.ts` — 20 missing flags added, the two `*_BLOCK_THRESHOLD` env-only knobs moved out of the flag tables, category/total counts and the Live WS port corrected, guarded by a static test (#12552 — thanks @pacocartones) +- **fix(i18n):** backfill missing `usage.kiloPass*` strings in 39 locales and restore `featureFlagOmnirouteDisableThinkingLevelVariantsDescription` in `pt.json` ([#12561](https://github.com/diegosouzapw/OmniRoute/issues/12561)) ([#13027](https://github.com/diegosouzapw/OmniRoute/pull/13027)) — thanks @HouMinXi +- fix(docker): default docker-compose app ports (dashboard/API/live-WS) to loopback instead of `0.0.0.0`, closing the anonymous `/v1` LAN/WAN exposure gap left open by `REQUIRE_API_KEY=false` (#12568) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): close DNS-rebinding SSRF gap in webhook outbound-URL guard (#12569) ([#13243](https://github.com/diegosouzapw/OmniRoute/pull/13243)) +- fix(docker): require a per-session token on the VNC browser CDP bridge and isolate it on a dedicated Docker network (#12571) ([#13241](https://github.com/diegosouzapw/OmniRoute/pull/13241)) +- fix(open-sse): write Adobe Firefly session tokens and cookie jars with 0700/0600 permissions instead of the process umask (#12572) ([#13242](https://github.com/diegosouzapw/OmniRoute/pull/13242)) +- fix(api): remove hardcoded wildcard CORS in openai-to-gemini-sse.ts so the centralized fail-closed CORS gate is the sole source of `Access-Control-Allow-Origin` (#12573) ([#13239](https://github.com/diegosouzapw/OmniRoute/pull/13239)) +- fix(api): enforce API key policy (budget/rate-limit/schedule/endpoint scoping) on the ElevenLabs speech-to-text, text-to-speech and voices proxy routes (#12574) ([#13244](https://github.com/diegosouzapw/OmniRoute/pull/13244)) +- fix(sse): cap HuggingChat NDJSON body size and bound the read loop with the fetch timeout so a stalled or hostile upstream cannot buffer unbounded memory (#12577) ([#13282](https://github.com/diegosouzapw/OmniRoute/pull/13282)) +- fix(docker): scope the cliproxyapi/qdrant/bifrost sidecars to loopback by default and forward `CLIPROXYAPI_MANAGEMENT_KEY` into the cliproxyapi container so its management API is not left both unauthenticated and LAN-published (#12578) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): create DB export temp paths with `fs.mkdtempSync` instead of predictable timestamps (#12579) ([#13237](https://github.com/diegosouzapw/OmniRoute/pull/13237)) +- Cline 401 bodies that say "re-authenticate your Cline account" classify as a refreshable OAuth token, not a terminal expired key. The cooling panel no longer labels every cooldown as a 429; it shows the recorded last error instead. (#12594) ([#13060](https://github.com/diegosouzapw/OmniRoute/pull/13060)) — thanks @HouMinXi +- **fix(catalog):** degrade unknown combo targets instead of dropping LCD modalities, and persist OpenRouter `architecture.input_modalities` into the capability snapshot ([#12613](https://github.com/diegosouzapw/OmniRoute/issues/12613)) ([#13042](https://github.com/diegosouzapw/OmniRoute/pull/13042)) — thanks @HouMinXi +- fix(providers): send `x-api-key` instead of `Authorization: Bearer` for OpenCode Zen's `/v1/responses` endpoint (Muse Spark Contributor models), fixing a 401 on OmniRoute's auth header (#12633) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(sse): surface the actionable "Auggie CLI not found" message when the shell reports a missing `auggie` binary via exit code instead of a spawn error (#12645) ([#13263](https://github.com/diegosouzapw/OmniRoute/pull/13263)) +- **fix(config):** Persist deletions of built-in background-degradation entries — when a stored settings record exists its `degradationMap` is now authoritative instead of being merged under the defaults, so an entry the user removed in the dashboard no longer reappears on the next apply or restart ([#12424](https://github.com/diegosouzapw/OmniRoute/issues/12424)) ([#12647](https://github.com/diegosouzapw/OmniRoute/pull/12647)) — thanks @pacocartones +- **fix(gamification):** action-count badge milestones (First Token, Token Consumer, Token Machine, Token Whale, and the token-sharing tier) are now backed by a durable `xp_action_counts` counter incremented in `addXp()`, instead of a live `COUNT(*)` over `xp_audit_log`. The audit log is pruned by `retention.xpAuditLog` (default 30 days), so on a default install those "lifetime" milestones were really "actions in the last 30 days" and unlocked badges could stop unlocking once old rows aged out. `getActionCount()` and `checkActionCountBadges()` now read the same durable source, and a migration backfills existing totals from the surviving audit rows ([#12546](https://github.com/diegosouzapw/OmniRoute/issues/12546)) ([#12651](https://github.com/diegosouzapw/OmniRoute/pull/12651)) — thanks @pacocartones +- **fix(images):** `/v1/images/edits` now iterates a combo's targets the same way `/v1/images/generations` does (#9239) instead of flattening a bare combo to its first target. A combo whose first target is not edit-capable — or lacks credentials — now falls through to a later edit-capable target rather than hard-erroring, and missing credentials are skipped (not a hard `401`) to match the generations path. The per-target skip/terminal classification is extracted into a shared `runImageComboTargets` loop, so generations behavior is unchanged ([#12547](https://github.com/diegosouzapw/OmniRoute/issues/12547)). ([#12653](https://github.com/diegosouzapw/OmniRoute/pull/12653)) — thanks @pacocartones +- fix(sse): add first-byte watchdog to the TLS-fingerprint transport so a stalled wreq body falls back instead of hanging for minutes (#12656) ([#13272](https://github.com/diegosouzapw/OmniRoute/pull/13272)) +- fix(sse): exempt tiny-budget reasoning probes from combo quality failure and surface persisted-cooldown skips in ALL_TARGETS_SKIPPED diagnostics (#12659) ([#13269](https://github.com/diegosouzapw/OmniRoute/pull/13269)) +- fix(models): declare the real ~1M contextLength for OpenCode Zen's Muse Spark 1.2 models instead of falling back to the 200000 provider default (#12681) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(oauth): align codebuddy-cn OAuth User-Agent with the chat/usage CLI version to avoid WAF false positives (#12702) ([#13264](https://github.com/diegosouzapw/OmniRoute/pull/13264)) +- fix(dashboard): surface an authentication-required banner instead of silently blanking database settings for a guest session (#12709) ([#13270](https://github.com/diegosouzapw/OmniRoute/pull/13270)) +- **fix(resilience):** stop unbounded queue that hangs 6min until Aborted — gate, provider slot, and Bottleneck queue now share a per-connection `maxWaitMs` budget; `fail-closed` on exhaust (503); execution backstop `executionMaxWaitMs` overridable per connection with upstream clamp ([#12715](https://github.com/diegosouzapw/OmniRoute/pull/12715)) — thanks @maxmad64bis (with thanks to @Tushar49 for surfacing the slow-provider need in #12635) +- **fix(auto-combo):** every mode pack now carries `quality` and `reliability` weights (`quality` 0.02, 0.03 in `quality-first`; `reliability` 0.03, 0.04 in `reliability-first`), so selecting a pack no longer silences either signal; `DEFAULT_WEIGHTS` is unchanged ([#12731](https://github.com/diegosouzapw/OmniRoute/pull/12731)) — thanks @maxmad64bis +- **fix(sse):** the provider execution pipeline keeps the upstream error `code`/`type` again — both were lost when the non-streaming leg was extracted, so a config-class `422` (Antigravity missing project) degraded into a generic account cooldown instead of reaching the gate that recognises it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(db):** a legacy `call_logs` table no longer aborts startup — the provider-stats index is created after the column healing runs, not before it, so an install predating `request_type` opens instead of failing with `no such column` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(catalog):** a malformed row in the operator-writable custom-models blob no longer kills every `auto/*` pool ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(tests):** realign the guards that the same day's merges left asserting the old behavior — the provider-count total after GreenPT and EURouter, the GLM stream buffer slot that `#12925` turned into a declared parameter, and the injection scan bound that `#13104` reshaped from a head-only window into head-plus-tail ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- fix(cache): fold tool_choice/tools/response_format into the semantic cache signature so a cached tool_calls response can no longer be replayed for a request whose tool policy forbids it (#12734) ([#13267](https://github.com/diegosouzapw/OmniRoute/pull/13267)) +- **fix(models):** stop treating provider-supplied `isFree:true`/`:free`/`0/0` flags as trusted unless the provider has a documented free tier — only the shipped free-tier catalog decides otherwise; fetched `isFree:true` trusted only for a free-tier provider, custom `isFree:true` via a trusted path ([#12744](https://github.com/diegosouzapw/OmniRoute/pull/12744)) — thanks @maxmad64bis +- fix(memory): authenticate the internal /v1/rerank loopback call so memory reranking no longer silently degrades to unranked order when REQUIRE_API_KEY=true (#12745) ([#13268](https://github.com/diegosouzapw/OmniRoute/pull/13268)) +- fix(sse): parse Ollama Cloud's current usage markup (`$X of $Y used` aria-label, nested width style) (#12749) ([#13260](https://github.com/diegosouzapw/OmniRoute/pull/13260)) +- fix(cli): setup-opencode no longer sends an active context's management token to `/v1/models` when `--api-key`/`OMNIROUTE_API_KEY` is supplied — an explicit flag or the env var now always outranks the context's token, and the flag itself is no longer swallowed by the parent program's global `--api-key` option (#12783) ([#13246](https://github.com/diegosouzapw/OmniRoute/pull/13246)) +- fix(sse): register Arcee AI in the executor provider registry so requests reach api.arcee.ai instead of silently falling back to OpenAI (#12784) ([#13277](https://github.com/diegosouzapw/OmniRoute/pull/13277)) +- **fix(quota):** share one upstream quota read when identical saturation checks arrive at the same time, so traffic bursts don't multiply provider API calls ([#12787](https://github.com/diegosouzapw/OmniRoute/pull/12787)) — thanks @maxmad64bis +- **fix(db):** ignore expired or invalid rate-limit cooldown writes so a stale timestamp can't lock a connection that should be usable — clearing still works as before ([#12788](https://github.com/diegosouzapw/OmniRoute/pull/12788)) — thanks @maxmad64bis +- **fix(providers):** single source for provider order with `xao` ranking alongside `xai-oauth` ([#12790](https://github.com/diegosouzapw/OmniRoute/pull/12790)) — thanks @maxmad64bis +- **fix(routing):** off-table models check the free-model catalog before inheriting premium prices, latency bootstraps from the observed pool median, async tiers read live database pricing with a 90-day freshness gate, and the tier cache invalidates on every pricing write ([#12792](https://github.com/diegosouzapw/OmniRoute/pull/12792)) — thanks @maxmad64bis +- **fix(auto-combo):** snapshot scoring uses the observed per-connection account tier and per-model quality instead of neutral constants, and circuit-open providers rank lower at build time ([#12794](https://github.com/diegosouzapw/OmniRoute/pull/12794)) — thanks @maxmad64bis +- **fix(pool):** empty `auto/*` pools now say why they are empty, and the models listing uses the same paid check as routing ([#12795](https://github.com/diegosouzapw/OmniRoute/pull/12795)) — thanks @maxmad64bis +- fix(routing): recognize CLIProxyAPI's 'unknown provider for model' 400 as fallback-worthy (#12800) ([#13284](https://github.com/diegosouzapw/OmniRoute/pull/13284)) +- **fix(sse):** translate-mode streams now emit the estimated token counts as a trailing usage-only chunk before `[DONE]` when the upstream stays silent, so metered chat clients see totals instead of nothing ([#12828](https://github.com/diegosouzapw/OmniRoute/pull/12828)) — thanks @maxmad64bis +- **fix(db):** provider stats stay truthful on empty and legacy databases: fallback counts default to `0` instead of `null`, latency averages read `null` (not `0`) when no durations were recorded, failures logged before the error-type column existed group under `pre_migration` instead of `unclassified`, and per-provider queries use two new composite indexes ([#12832](https://github.com/diegosouzapw/OmniRoute/pull/12832)) — thanks @maxmad64bis +- fix(nvidia): fail open when a synced model catalog goes stale instead of gating forever (#12849) ([#13248](https://github.com/diegosouzapw/OmniRoute/pull/13248)) +- **fix(db):** WAL maintenance lives in its own module: a `TRUNCATE` checkpoint that hits a busy database now warns with its streak and retries once via `PASSIVE` instead of logging success, and closing-time checkpoints no longer report success on builds without a database file, with the busy totals visible in the authenticated monitoring health payload ([#12853](https://github.com/diegosouzapw/OmniRoute/pull/12853)) — thanks @maxmad64bis +- **fix(health):** quota with no snapshots reads empty instead of a contradicting 0% ([#12857](https://github.com/diegosouzapw/OmniRoute/pull/12857)) — thanks @maxmad64bis +- fix(a2a): accept the dashboard's own session cookie on /a2a so "Run message/send" no longer fails with "Unauthorized: missing or invalid API key" (#12888) ([#13271](https://github.com/diegosouzapw/OmniRoute/pull/13271)) +- **fix(logs):** the Logs grid's in-memory filter pass no longer discards rows the SQL query already matched — selecting an API key from the dropdown (which sends the key's id) returns its calls again, the Combo tab shows every combo instead of only those whose name contains a "1", and the model filter and search cover the same columns as the query ([#12896](https://github.com/diegosouzapw/OmniRoute/pull/12896)) — fixes [#12873](https://github.com/diegosouzapw/OmniRoute/issues/12873) — thanks @ntdatt812 +- **fix(a2a):** `/api/a2a/status` now builds the agent card from the request that asked for it, so a gateway reached at a non-localhost host no longer advertises `http://localhost:20128` as its A2A URL ([#12918](https://github.com/diegosouzapw/OmniRoute/pull/12918)). — thanks @ntdatt812 +- **fix(compression):** progressive aging now appends its `[COMPRESSED:aging:…]` annotation after a turn's `tool_result` blocks instead of in front of them, so Anthropic no longer rejects aged conversations with "`tool_use` ids were found without `tool_result` blocks immediately after" ([#12920](https://github.com/diegosouzapw/OmniRoute/pull/12920)). — thanks @ntdatt812 +- **fix(bedrock):** model import now resolves context limits for every vendor prefix instead of only `anthropic.*`, so `global.openai.gpt-5.6-*` no longer imports with a null `inputTokenLimit` and gets rejected pre-flight at the 200k default ([#12921](https://github.com/diegosouzapw/OmniRoute/pull/12921)). — thanks @ntdatt812 +- **fix(stream):** the 64 KB stream buffer GLM asks for is honoured instead of dropped, and the type error it caused no longer fails the API Route Typecheck gate on every open PR ([#12925](https://github.com/diegosouzapw/OmniRoute/pull/12925)) — thanks @ntdatt812 +- **fix(guardrails):** mask PII inside a `tool_result`'s nested content array, which the masker walked past while redacting its sibling block ([#12930](https://github.com/diegosouzapw/OmniRoute/pull/12930)) — thanks @ntdatt812 +- **fix(dashboard):** the Radar catalog table no longer leaves absent data unexplained — empty limits, unknown context windows, and unreported capabilities each explain themselves on hover, and a new check keeps it that way ([#12937](https://github.com/diegosouzapw/OmniRoute/pull/12937)) — thanks @maxmad64bis +- **fix(sse):** geo-blocked opencode requests rotate to the next account proxy instead of failing, so one refused egress no longer aborts the whole chain ([#12941](https://github.com/diegosouzapw/OmniRoute/pull/12941)) — thanks @maxmad64bis +- **fix(codex):** dashboard "clear cooldown" and the CAS recovery path now drop nested `codexScopeRateLimitedUntil` maps in the same write that nulls `rate_limited_until`, and fresh quota snapshots with headroom lift fallback-sourced scope cooldowns parked by quota preflight ([#12817](https://github.com/diegosouzapw/OmniRoute/issues/12817), [#12860](https://github.com/diegosouzapw/OmniRoute/issues/12860), [#12951](https://github.com/diegosouzapw/OmniRoute/pull/12951)) The dashboard reset-credit button (`consumeCodexResetCredit`) now exercises that same snapshot path after a successful redeem, so a filled quota bar is enough to unpark a leftover fallback Codex child without another manual clear. — thanks @HouMinXi +- **fix(db):** resolve `sql-wasm.wasm` across global npm install and hoisted layouts, ensuring OmniRoute can boot cleanly on Node 24 when native `better-sqlite3` is uncompiled. ([#13035](https://github.com/diegosouzapw/OmniRoute/pull/13035)) — thanks @HouMinXi +- **fix(api):** `DELETE /v1/batches/delete-completed` now sweeps only the calling API key's own completed batches (batches with no owner stay out of a key-scoped sweep on purpose), with an explicit instance-wide mode reserved for authenticated dashboard sessions, a 401 for a presented key that is unknown, revoked, deactivated, banned or expired (never falling through to the session branch), audit logging of every sweep, a sanitized 500 on failure, an owner-scoped file half (a key-scoped sweep never nulls a file another tenant owns) and an atomic sweep — chunked in 200-batch transactions in instance mode — so a mid-way error never leaves a batch pointing at a nulled file (GHSA-wvxc-jp3v-5mg5) ([#12969](https://github.com/diegosouzapw/OmniRoute/pull/12969)) +- **fix(combo):** quota-weighted routing stops drawing on an out-of-credit connection — a 402 now invalidates the stored quota snapshot instead of leaving its stale remaining percentage in place, and a snapshot older than 10 minutes no longer counts as confident headroom for the primary pool ([#12972](https://github.com/diegosouzapw/OmniRoute/pull/12972)) ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- **fix(vision):** Custom Models with "Vision capable" checked no longer have image requests swapped to `glm/glm-4.6v` when the client sends the advertised alias (`vllm/path/...`) or the bare path-shaped id — Vision Bridge now matches the stored override for all three id forms ([#12758](https://github.com/diegosouzapw/OmniRoute/issues/12758)) ([#12974](https://github.com/diegosouzapw/OmniRoute/pull/12974)) — thanks @HouMinXi +- **fix(sse):** transient opencode upstream failures rotate to the next account proxy instead of failing, so one flapping egress no longer aborts the whole chain ([#12975](https://github.com/diegosouzapw/OmniRoute/pull/12975)) — thanks @maxmad64bis +- **fix(azure):** Deployments from GPT-6 onward now send `max_completion_tokens` instead of `max_tokens`, which Azure rejects with HTTP 400. The rule matched a literal `gpt-5`, so each new generation arrived broken; it now matches the generation range, while `gpt-35-turbo` still keeps `max_tokens`. ([#13007](https://github.com/diegosouzapw/OmniRoute/pull/13007)) — thanks @ntdatt812 +- **fix(db):** add an opt-in self-restart circuit for sustained critical memory pressure, gate post-cleanup VACUUM behind a minimum freed-rows threshold, and checkpoint the SQLite WAL every 5 minutes with a size guard that escalates to TRUNCATE, so a growing WAL can no longer stall the event loop into a full outage. ([#13011](https://github.com/diegosouzapw/OmniRoute/pull/13011)) — thanks @HouMinXi +- **fix(auth):** an explicit connection pin may probe a quota-disabled row once and re-enable it on success ([#12874](https://github.com/diegosouzapw/OmniRoute/issues/12874)) ([#13017](https://github.com/diegosouzapw/OmniRoute/pull/13017)) — thanks @HouMinXi +- **fix(api):** thread `X-OmniRoute-Fallback-Attempts` through combo chat completions so streaming and non-streaming responses report how many prior legs were attempted ([#13038](https://github.com/diegosouzapw/OmniRoute/pull/13038)) — thanks @HouMinXi +- Restore provider failure classification and credential refresh on non-streaming requests: classify non-2xx failures to lock models on per-model quota exhaustion, update connection rate limits from headers and body, and pass credential refresh handlers to pipeline execution so 401 tokens can be refreshed and retried (#13043). ([#13069](https://github.com/diegosouzapw/OmniRoute/pull/13069)) — thanks @HouMinXi +- **fix(responses):** wrap forced-non-streaming web_search fallback JSON as Responses SSE so Codex still sees `response.completed` ([#13050](https://github.com/diegosouzapw/OmniRoute/pull/13050)) — thanks @HouMinXi +- **fix(antigravity):** Preserve upstream thought-token usage in normalized completion and reasoning token counts ([#13055](https://github.com/diegosouzapw/OmniRoute/pull/13055)) — thanks @pacocartones +- Fix a concurrency-slot leak in the DeepSeek PoW solver: a worker that failed to spawn (for example a missing worker script) never released its slot, so `MAX_CONCURRENT_WORKERS` failures disabled the solver until restart. ([#13097](https://github.com/diegosouzapw/OmniRoute/pull/13097)) — thanks @anhtahaylove +- **fix(acp):** bound the ACP session output buffers — `stdoutBuffer` and `stderrBuffer` now cap at 1 MiB keeping the most recent output behind a visible `[...output truncated...]` marker, and `stderrBuffer` is reset per prompt instead of accumulating for the lifetime of the session. ([#13100](https://github.com/diegosouzapw/OmniRoute/pull/13100)) — thanks @anhtahaylove +- **fix(acp):** release the `stdout`/`exit` listeners and the idle timer that a `sendPrompt` timeout used to leave attached to the `acpManager` singleton, and drop sessions that exited on their own from the session map instead of keeping them forever. ([#13096](https://github.com/diegosouzapw/OmniRoute/pull/13096)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection and PII scanners now read the text a `tool_result` block carries on `content` (string or nested block list), in messages and in system blocks, so tool output is judged by the same rules as user text ([#13101](https://github.com/diegosouzapw/OmniRoute/pull/13101)) — thanks @ntdatt812 +- **fix(gamification):** close the badge notification SSE stream when the request signal is already aborted before the stream starts — a client that disconnects while the route is still awaiting auth used to leave both the 2s unlock poll and the 15s heartbeat running for the lifetime of the process. ([#13106](https://github.com/diegosouzapw/OmniRoute/pull/13106)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection scan now spends its 16 KB budget on both ends of the request instead of the first 16 KB only, so `system`, `instructions`, `query`, `documents` and the newest turns are no longer hidden behind one long message ([#13104](https://github.com/diegosouzapw/OmniRoute/pull/13104)) — thanks @ntdatt812 +- **fix(dashboard):** expose the Volcano Ark console cookie on quota scraping and unwrap connect-error objects so the dashboard shows the upstream message ([#13107](https://github.com/diegosouzapw/OmniRoute/pull/13107)) — thanks @HouMinXi +- **fix(db):** release the `beforeExit`/`SIGINT`/`SIGTERM` handlers when a `node:sqlite` adapter closes, so a closed adapter and its database handle are no longer pinned to `process` for the lifetime of the run — the same treatment #7494 gave the sql.js adapter. ([#13109](https://github.com/diegosouzapw/OmniRoute/pull/13109)) — thanks @anhtahaylove +- **fix(translator):** `contentSchema` and `unevaluatedItems` are now treated as subschema positions by the tool-schema sanitizer, so a truncation placeholder in either is replaced with a permissive schema instead of being forwarded as a string ([#13110](https://github.com/diegosouzapw/OmniRoute/pull/13110)) — thanks @ntdatt812 +- **fix(cli-helper):** clear the `createLogStream` timeout on the abort path — `stop()` aborts the in-flight fetch and returned through the `signal.aborted` branch, which skipped `clearTimeout` and left an armed timer per stopped stream. The stream reader is now also cancelled when the read loop exits early. ([#13114](https://github.com/diegosouzapw/OmniRoute/pull/13114)) — thanks @anhtahaylove +- **fix(docs):** drop TLS-impersonation, MITM-decrypt, supply-chain attestation, and XOR-mask writeups from the public `/docs` catalog and Docker image. Files stay in git for engineers; operators who need them open the repo, not the website. ([#13136](https://github.com/diegosouzapw/OmniRoute/pull/13136)) — thanks @HouMinXi +- **fix(combo):** parse numeric-epoch `rate_limited_until` in the combo cooldown read path ([#13141](https://github.com/diegosouzapw/OmniRoute/pull/13141)) — thanks @maxmad64bis +- **fix(opencode):** both OpenCode plugins now reject a gateway address typed without `http://` at configuration time, instead of publishing every model with an api url no client can call, and the v2 plugin no longer publishes a model card whose api url is blank or relative ([#13142](https://github.com/diegosouzapw/OmniRoute/pull/13142)) — thanks @maxmad64bis +- **fix(providers):** lock opencode model on upstream 400 model-unavailable ([#13146](https://github.com/diegosouzapw/OmniRoute/pull/13146)) — thanks @maxmad64bis +- **fix(logging):** keep the provider exchange rather than the raw client bodies when a call log exceeds its size budget, and show that recovered payload in the request-detail panel instead of replacing it with the stored response body ([#13147](https://github.com/diegosouzapw/OmniRoute/pull/13147)) — thanks @maxmad64bis +- **fix(traffic-inspector):** the WebSocket route no longer leaks a traffic-buffer subscriber and a 30s ping timer when the client socket is already closed at handler time — listeners are attached before any resource is acquired, a destroyed socket bails out early, and the ping interval stops on a dead socket where `write()` never throws ([#13155](https://github.com/diegosouzapw/OmniRoute/pull/13155)) — thanks @anhtahaylove +- **fix(telegram):** bound the per-user API key cache in the Telegram chat proxy so a burst of distinct chat ids can no longer grow the process heap without limit ([#13165](https://github.com/diegosouzapw/OmniRoute/issues/13165)) ([#13166](https://github.com/diegosouzapw/OmniRoute/pull/13166)) — thanks @anhtahaylove +- **fix(stream):** cancel the upstream response body when the JSON-to-SSE sniff unwinds on a body timeout, so a stalled upstream no longer pins the connection ([#13169](https://github.com/diegosouzapw/OmniRoute/issues/13169)) ([#13171](https://github.com/diegosouzapw/OmniRoute/pull/13171)) — thanks @anhtahaylove +- **fix(telegram):** authenticate webhook deliveries with Telegram's `secret_token` so an unauthenticated caller can no longer mint API keys or spend upstream quota ([#13172](https://github.com/diegosouzapw/OmniRoute/issues/13172)) ([#13175](https://github.com/diegosouzapw/OmniRoute/pull/13175)) — thanks @anhtahaylove +- **fix(models):** give discoverable Gemini 3.8 Flash ids their own 65536 output spec so Antigravity no longer clamps them to 16384 ([#13195](https://github.com/diegosouzapw/OmniRoute/pull/13195)) — thanks @HouMinXi +- **fix(dashboard):** leftover catalog-removed provider rows (gemini-cli) can be listed and purged from the providers page ([#13067](https://github.com/diegosouzapw/OmniRoute/issues/13067)) ([#13197](https://github.com/diegosouzapw/OmniRoute/pull/13197)) — thanks @HouMinXi +- **fix(auth):** a dashboard session now requires the `authenticated: true` claim that login, OIDC and the session refresh already emit — a JWT merely signed with `JWT_SECRET` (for example the Cursor CLI passthrough token, which any API-key holder can obtain) no longer verifies as the `auth_token` cookie on any route, the WebSocket handshake or the live server; existing sessions keep working ([#13298](https://github.com/diegosouzapw/OmniRoute/issues/13298)) ([#13375](https://github.com/diegosouzapw/OmniRoute/pull/13375)) +- **fix(api):** `DELETE /v1/batches/delete-completed` now applies the caller's API-key policy (endpoint allowlist, schedule, usage cap, rate limit) like every other `/v1` route, commits the key-scoped sweep per 200-batch chunk instead of holding one write lock for the whole sweep, refuses to loop without progress, and rejects a scope that names both a key and `allTenants` ([#13684](https://github.com/diegosouzapw/OmniRoute/pull/13684)) +- Restore the API-route typecheck gate: the non-streaming leg result lost its discriminated-union narrowing after the server-owned tool loop reassignment, producing 13 new TS2339 diagnostics in `chatCore.ts`. ([#12963](https://github.com/diegosouzapw/OmniRoute/pull/12963)) +- Restore four non-streaming behaviours the server-owned tool loop refactor dropped: client aborts map to 499 with the fixed `Request aborted` message, an aborted request no longer logs a synthetic `clientResponse`, Claude prompt-cache telemetry is recorded again, and a body that cannot be canonicalized no longer throws when the tool loop is off. ([#12990](https://github.com/diegosouzapw/OmniRoute/pull/12990)) +- **fix(skills):** The CLI registry parser now reads positionals declared with `.addArgument()`, not only those written inline in `.command()`. `tunnel create [type]` was being published as `tunnel create`, so the agent-skills sync gate reported drift on every branch and regenerating would have deleted the argument. ([#13009](https://github.com/diegosouzapw/OmniRoute/pull/13009)) — thanks @ntdatt812 +- **fix(combos):** dashboard combo test uses a short prompt, serial probes, and a 60s timeout so reasoning models and rate-limited free pools do not fail the health check ([#13001](https://github.com/diegosouzapw/OmniRoute/pull/13001)) — thanks @HouMinXi +- fix(compression): terminate idle worker threads on eviction so long-running instances stop leaking OS threads and MessagePorts ([#13091](https://github.com/diegosouzapw/OmniRoute/pull/13091)) — thanks @anhtahaylove +- Redact Google API keys of any length in error bodies: the pattern required exactly 39 characters, so shorter or longer `AIza…` credentials passed through unredacted. ([#12964](https://github.com/diegosouzapw/OmniRoute/pull/12964)) +- **fix(grok-cli):** a 402 "Grok Build usage balance exhausted" parks that Grok login as out of credit (Grok Build CLI, grok.com cookie, and xAI OAuth share the weekly pool). Combo routing then tries the next login instead of locking the model for every account in the pool ([#13061](https://github.com/diegosouzapw/OmniRoute/pull/13061)) — thanks @HouMinXi +- fix(compression): spawn the LLMLingua worker with a file URL object so compression actually runs instead of silently failing open on Node ([#13093](https://github.com/diegosouzapw/OmniRoute/pull/13093)) — thanks @anhtahaylove +- **fix(test):** run the local `test` and `test:unit` scripts at concurrency 4 so a full-suite run no longer exhausts the machine's commit charge and kills unrelated processes ([#13187](https://github.com/diegosouzapw/OmniRoute/pull/13187)) — thanks @anhtahaylove +- **fix(monitoring):** `GET /api/monitoring/health` `credentialHealth` now includes a bounded `failedConnections` list (`connectionId`, `status`, sanitized `lastError`) when the probe-cache gauge `failed>0`, plus `source: probe-cache` and a cheap `staleDbNonOkCount` for sticky SQLite `test_status` on active rows. Documents that the live gauge is not `provider_connections.test_status`. ([#12876](https://github.com/diegosouzapw/OmniRoute/pull/12876)) — thanks @RaviTharuma +- **fix(resilience):** an openai-compatible multi-upstream gateway no longer marks the whole connection `credits_exhausted` when a single upstream (or the `/models` / representative-model health probe) returns 402. Quota failures stay model-scoped; true connection-wide auth failures are unchanged. Single-credential 402 key disable ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239)) is preserved. ([#12875](https://github.com/diegosouzapw/OmniRoute/pull/12875)) — thanks @RaviTharuma +- **fix(dashboard):** nine fixes on the Orchestration Canvas, all diagnosed in the Phase 2 reviews. A source that fails now keeps the timestamp of its FIRST failure instead of being re-stamped every poll — the stale line said "since the last poll" no matter how long the source had been down, and the churn also defeated the snapshot's stable identity (it serializes the source list), so the canvas re-rendered on every tick while anything was broken. A source that HAD data and only then started failing is flagged too: previously only a source with no node at all got the warning, so a source that went down mid-session kept a healthy-looking node forever. The rest are pointwise: clicking a filter chip cancels the pending search debounce (left armed, it fired ~300ms later and silently reverted the chip); the search input carries an accessible name; `?state=running, failed` parses like the unpadded form instead of dropping the padded value; the CSV toggle helper is defined once in `model/urlParams.ts` rather than twice; the agents tab tells "nothing running" apart from "the filter matched nothing", offering a clear-filters button instead of setup links that would be wrong advice there; edges stop emitting SMIL particles above 40 simultaneously active edges, keeping the colored stroke; and the drawer's error banner clears when a retried action succeeds. ([#12983](https://github.com/diegosouzapw/OmniRoute/pull/12983)) +- **fix(i18n):** the nine locales added with the EU-language batch (Greek, Estonian, Irish, Croatian, Lithuanian, Latvian, Maltese, Slovenian, Serbian) were missing the eleven Orchestration Canvas keys that Phase 3 introduced, so the compare-runs panel and the "no runs match these filters" empty state fell back to English in those languages (`deepMergeFallback` substitutes English for an absent key, so nothing rendered blank — it rendered untranslated). The coverage gate does not catch this: it enforces an 80% floor per locale, and eleven missing keys out of ~13,000 leaves coverage at 99.9%. Translated for real in each language, calibrated against the wording each file already uses for "run", "filter" and "skill". ([#13294](https://github.com/diegosouzapw/OmniRoute/pull/13294)) +- **fix(dashboard):** five follow-ups from the Phase 2 Orchestration Canvas review. The History drawer now shows the memory section for runs that already left the live TTL window: the persisted `memory_hits` event is parsed into `metadata.memoryHits` with the same defensive validation the drawer applies, and — because that event is observability rather than a state transition — it no longer leaks into the timeline, where it had been inheriting the task's state and rendering as a duplicate transition. The A2A memory recall runs against its own 1.5s deadline instead of inheriting the memory backend's 30s one; overshooting degrades exactly like any other recall failure (no hits, task proceeds), and the timer is cleared on both paths. Repeating a Conductor run carries its `requirements.cli`/`requirements.model` forward, so the new run is pinned to the same runner profile and model rather than drifting to whatever the fleet picks. A successful repeat from the Agents tab now focuses the run it created, instead of leaving the operator on the finished one. And the auth test for `POST /api/conductor/tasks` moved into the shared `ROUTES` array rather than restating the pattern. ([#12988](https://github.com/diegosouzapw/OmniRoute/pull/12988)) +- fix(plugins): stop leaking an exit listener per plugin hook timeout, which triggered MaxListenersExceededWarning on plugins that ignore SIGTERM ([#13092](https://github.com/diegosouzapw/OmniRoute/pull/13092)) — thanks @anhtahaylove +- **fix(validation):** Provider node edits no longer fail with a generic "Invalid request" when the optional daily-quota reset fields are left blank. The dashboard sends `dailyQuotaResetTimezone` and `dailyQuotaResetHour` as `null`, and only the hour accepted it. ([#13066](https://github.com/diegosouzapw/OmniRoute/issues/13066)) ([#13083](https://github.com/diegosouzapw/OmniRoute/pull/13083)) — thanks @ntdatt812 +- **fix(combo):** quota-aware expansion drops banned, inactive, missing, and wrong-provider connections before quota fetch or model dispatch; pins and allowlists stay selectors, not a bypass. Antigravity automatic exhaustion now requires a reported zero remaining, so a positive balance below 1% stays eligible. ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- Fix an intermittent failure in the quota-weighted routing test suite: scores are a function of `Date.now()`, so peers with identical quota scored microseconds apart never tied and swapped order. ([#12946](https://github.com/diegosouzapw/OmniRoute/pull/12946)) +- fix(volcengine): resolve quota window mapping gaps, safe multi-connection console binding, and response cookie sanitization ([#12950](https://github.com/diegosouzapw/OmniRoute/pull/12950)) — thanks @HouMinXi +- **fix(test):** resolve the WebDAV handler path with `fileURLToPath` so the suite's 37 WebDAV tests run on Windows instead of failing with a doubled `C:\C:\` drive prefix ([#13196](https://github.com/diegosouzapw/OmniRoute/pull/13196)) — thanks @anhtahaylove +- **fix(ui):** fall back from invalid AnySearch icon alias (direct commit `9587e07b69`) +- **fix(sse):** stop rebuilding a truncated summary from the collector's cap-dropped event array ([#12718](https://github.com/diegosouzapw/OmniRoute/pull/12718)) — thanks @hartmark +- **fix(conversations):** show a pending spinner for unresolved tool nodes instead of "(empty)" ([#12727](https://github.com/diegosouzapw/OmniRoute/pull/12727)) — thanks @hartmark +- **fix(streaming):** fail fast when an upstream stream produces only lifecycle/heartbeat events, never real content ([#12741](https://github.com/diegosouzapw/OmniRoute/pull/12741)) — thanks @hartmark +- **fix(responses-continuation):** bridge the write-in-flight window with an in-memory pending store ([#12854](https://github.com/diegosouzapw/OmniRoute/pull/12854)) — thanks @hartmark +- **fix(translator):** drop or map agent_message on Chat Completions fallback ([#12880](https://github.com/diegosouzapw/OmniRoute/pull/12880)) — thanks @RaviTharuma +- **fix(api):** page and stream-complete GET /v1/models for large catalogs ([#12882](https://github.com/diegosouzapw/OmniRoute/pull/12882)) — thanks @RaviTharuma +- **fix(quota):** align AUTH window labels with usage API and clarify 503 ([#12884](https://github.com/diegosouzapw/OmniRoute/pull/12884)) — thanks @RaviTharuma +- **fix(ci):** clear two base-reds on release/v3.8.51 — mutation-coverage gate + image-only-model guard ([#12945](https://github.com/diegosouzapw/OmniRoute/pull/12945)) +- **fix(api):** scope batch bulk-delete to the calling API key ([#13211](https://github.com/diegosouzapw/OmniRoute/pull/13211)) +- **fix(tests):** align model-sync log assertion to shipped path redaction (base-red) ([#13227](https://github.com/diegosouzapw/OmniRoute/pull/13227)) +- **fix(test):** make the ToS heading guard actually require the parentheses ([#13228](https://github.com/diegosouzapw/OmniRoute/pull/13228)) +- **fix(quality):** register 4 drifted covering tests in stryker tap.testFiles ([#13229](https://github.com/diegosouzapw/OmniRoute/pull/13229)) +- **fix(logging):** mask *-api-key headers in request-log pipeline (#13273) ([#13401](https://github.com/diegosouzapw/OmniRoute/pull/13401)) — thanks @KooshaPari +- **fix(tests):** correct stale Jina catalog prefix in custom-models test ([#13403](https://github.com/diegosouzapw/OmniRoute/pull/13403)) — thanks @KooshaPari +- **fix(backend):** prune old backups after health-check-repair snapshots (#13308) ([#13404](https://github.com/diegosouzapw/OmniRoute/pull/13404)) — thanks @KooshaPari +- **fix(backend):** guard memory_fts_au trigger with WHEN clause to stop FTS bloat (#13326) ([#13405](https://github.com/diegosouzapw/OmniRoute/pull/13405)) — thanks @KooshaPari +- **fix(free-tiers):** remove obsolete Together $25 signup credit (#12526) ([#13407](https://github.com/diegosouzapw/OmniRoute/pull/13407)) — thanks @KooshaPari +- **fix:** skip non-chat generation models in Test all models ([#13409](https://github.com/diegosouzapw/OmniRoute/pull/13409)) — thanks @KooshaPari +- **fix(providers):** de-duplicate agy and antigravity model catalogs ([#13410](https://github.com/diegosouzapw/OmniRoute/pull/13410)) — thanks @KooshaPari +- **fix(dashboard):** send provider in body for mini-playground web search ([#13411](https://github.com/diegosouzapw/OmniRoute/pull/13411)) — thanks @KooshaPari +- **fix(resilience):** add CJK quota-exhaustion patterns to 429 classifier ([#13412](https://github.com/diegosouzapw/OmniRoute/pull/13412)) — thanks @KooshaPari +- **fix(backend):** eval runner: failed calls no longer score as passed + dotAll regex ([#13413](https://github.com/diegosouzapw/OmniRoute/pull/13413)) — thanks @KooshaPari +- **fix(backend):** eval runner opts out of output-style and memory injection ([#13414](https://github.com/diegosouzapw/OmniRoute/pull/13414)) — thanks @KooshaPari +- **fix:** chain serial quarantine step in npm test script (fixes #13286) ([#13418](https://github.com/diegosouzapw/OmniRoute/pull/13418)) — thanks @KooshaPari +- **fix(antigravity):** derive stable session ID from credentials for prompt-cache reuse (#13305) ([#13424](https://github.com/diegosouzapw/OmniRoute/pull/13424)) — thanks @KooshaPari +- **fix(memory):** merge memory into top-level system field instead of unshifting at messages[0] (#13425) ([#13427](https://github.com/diegosouzapw/OmniRoute/pull/13427)) — thanks @KooshaPari +- **fix(compression):** preserve instruction blocks from lossy engines ([#13523](https://github.com/diegosouzapw/OmniRoute/pull/13523)) — thanks @KooshaPari +- **fix(compression):** preserve polarity words and newlines in ultra heuristic ([#13524](https://github.com/diegosouzapw/OmniRoute/pull/13524)) — thanks @KooshaPari +- **fix(compression):** run user preservePatterns before built-in patterns (#13457) ([#13525](https://github.com/diegosouzapw/OmniRoute/pull/13525)) — thanks @KooshaPari +- **fix(reasoning):** expand deepseek-v4 regex to match v4.1+ and add command-code provider (#13430) ([#13528](https://github.com/diegosouzapw/OmniRoute/pull/13528)) — thanks @KooshaPari +- **fix(providers):** add alwaysReasons metadata for reka-flash-3 ([#13531](https://github.com/diegosouzapw/OmniRoute/pull/13531)) — thanks @KooshaPari +- **fix(playground):** allow Compare columns to scroll independently ([#13532](https://github.com/diegosouzapw/OmniRoute/pull/13532)) — thanks @KooshaPari +- **fix(security):** empty whitelist allows through to prevent admin lockout ([#13534](https://github.com/diegosouzapw/OmniRoute/pull/13534)) — thanks @KooshaPari +- **fix(tests):** add DISABLE_IOREG_STRATEGY env var for macOS test compatibility (fixes #13467) ([#13539](https://github.com/diegosouzapw/OmniRoute/pull/13539)) — thanks @KooshaPari +- **fix(zed-hosted):** map developer role and Gemini enums for Zed proxy ([#13543](https://github.com/diegosouzapw/OmniRoute/pull/13543)) — thanks @KooshaPari +- **fix(analytics):** normalize provider aliases to canonical id in usage_history ([#13545](https://github.com/diegosouzapw/OmniRoute/pull/13545)) — thanks @KooshaPari +- **fix(combo):** use traceId for call log id to prevent UNIQUE constraint failure ([#13546](https://github.com/diegosouzapw/OmniRoute/pull/13546)) — thanks @KooshaPari +- **fix(auth):** check model lockout before returning synthetic noauth connection ([#13547](https://github.com/diegosouzapw/OmniRoute/pull/13547)) — thanks @KooshaPari +- **fix(guardrails):** skip credential redaction for base64 image data URLs ([#13550](https://github.com/diegosouzapw/OmniRoute/pull/13550)) — thanks @KooshaPari +- **fix(resilience):** increase requestQueue.maxWaitMs default from 15s to 30s ([#13553](https://github.com/diegosouzapw/OmniRoute/pull/13553)) — thanks @KooshaPari +- **fix(docs):** document OMNIROUTE_READY_TIMEOUT_MS and allowlist the test-only DISABLE_IOREG_STRATEGY ([#13692](https://github.com/diegosouzapw/OmniRoute/pull/13692)) + ### 📝 Maintenance ---- +- **docs(readme):** reconcile live v3.8.50 provider, free-tier, CLI, routing, test, community, sponsor, acknowledgment, and SVG metrics with their audited source denominators, including a deduplicated OmniRoute-in-Action snapshot and distinct contributor rankings for merged pull requests, GitHub-attributed commits, and Git history ([#11356](https://github.com/diegosouzapw/OmniRoute/pull/11356)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **docs:** sync the canonical provider count 354 → 356 across `README.md`, `AGENTS.md`, `llm.txt` (+ 42 i18n mirrors), the four README SVG diagrams, `docs/reference/PROVIDER_REFERENCE.md` (regenerated) and the `package.json` description after Opper (#11629) and 1min.ai (#11631) boarded the catalog — closes the `check:docs-counts-sync` strict drifts that kept `release/v3.8.51` red ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11673](https://github.com/diegosouzapw/OmniRoute/pull/11673)) — thanks @jonlwheat2-gif +- **test(providers):** update count-derived assertions after the v3.8.51 provider additions — `APIKEY_PROVIDERS` 233 → 235 (Opper #11629 + 1min.ai #11631), reserved-prefix REGISTRY walk 395 → 398, `WEB_FETCH_PROVIDERS` now includes `nimble-search` (#11620), and the provider translate-path golden snapshot regenerated ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11674](https://github.com/diegosouzapw/OmniRoute/pull/11674)) — thanks @jonlwheat2-gif +- **test(sse):** bump the hard-lease connection-query inventory for `src/lib/tokenHealthCheck.ts` to 2 — the verify-only web-cookie sweep added by #11495 split the single `getProviderConnections` call into oauth + cookie variants, which the frozen inventory had not tracked ([#11495](https://github.com/diegosouzapw/OmniRoute/pull/11495)). ([#11677](https://github.com/diegosouzapw/OmniRoute/pull/11677)) — thanks @jonlwheat2-gif / @oyi77 +- **chore(release):** clear the `release/v3.8.51` base-red gates — docs migration count (+38 i18n mirrors), stryker `tap.testFiles`, lockfile registry host, stale ESLint suppressions, the 7 lint regressions they were hiding, the stale `APIKEY_PROVIDERS` partition count (231 → 233), the `glm-5.3-max` tier missing from the GLM test table, a dead `modelType === "chat"` comparison in the models catalog, and the `resolveLiveWsUrl` / `sanitizeLiveWsPort` helpers from #11388 that a later merge dropped from `wsPath.ts` (dashboard live WebSocket URL resolution) ([#11502](https://github.com/diegosouzapw/OmniRoute/pull/11502)) — thanks @MumuTW +- **chore(free-models):** the free-tier regime table now declares which totals figure each regime feeds and whether it may take the no-auth shortcut, so the sets that used to repeat those answers by hand are derived from it — a new `freeType` no longer compiles until it has answered every question, instead of silently contributing to no total ([#11537](https://github.com/diegosouzapw/OmniRoute/pull/11537)) — thanks @maxmad64bis +- **docs(routing):** LKGP strategy copy now states provider-level stickiness and the explicit rules fallback instead of promising per-target pinning ([#11555](https://github.com/diegosouzapw/OmniRoute/pull/11555)) — thanks @maxmad64bis +- **test(chatcore):** pin the response tool-name alias resolution against the ordering hazard that broke every Gemini/Antigravity MCP tool call in v3.8.49. `extractRequestToolIdentityMap` deletes `translatedBody._toolNameMap`, so the later read is always undefined and the ledger survives only through the `requestToolIdentityMap` fallback — removing that fallback previously left the entire tool-name suite green. The resolution moves into `resolveResponseToolNameMap()` next to the map it depends on, with a regression guard that fails without the recovery. No behaviour change. ([#11575](https://github.com/diegosouzapw/OmniRoute/pull/11575)) — thanks @amartinawi +- **docs(env):** document `OMNIROUTE_CORPUS_CACHE_SIZE` in `.env.example` and `ENVIRONMENT.md`, so the env/docs contract check stops failing Docs Gates on every branch ([#11576](https://github.com/diegosouzapw/OmniRoute/pull/11576)) — thanks @ntdatt812 +- **chore(stryker):** register `route-guard-tunnel-processes-local-only` and `universal-quota-aware-routing` in `tap.testFiles`, so their mutant kills count for the three modules they cover ([#11577](https://github.com/diegosouzapw/OmniRoute/pull/11577)) — thanks @ntdatt812 +- **test(authz):** pin `LOCAL_ONLY_API_GET_EXEMPTIONS` by exact membership instead of by entry count, so the guard names the offending path and also catches a substitution ([#11580](https://github.com/diegosouzapw/OmniRoute/pull/11580)) — thanks @ntdatt812 +- **test(build):** pin `resolveNextBuildBundlerFlag` to the `OMNIROUTE_USE_TURBOPACK` contract it implements, across runtimes, instead of asserting an unimplemented Bun override ([#11583](https://github.com/diegosouzapw/OmniRoute/pull/11583)) — thanks @ntdatt812 +- **test(claude-web):** the Claude Web transport test's barrel anchors no longer pin the exact construction spelling. #11421 made the executor map lazy, so `/"claude-web": new ClaudeWebExecutor\(\)/` stopped matching and the test went red on `release/v3.8.51` itself — a base-red gate every open PR inherited. The anchors now match the alias key and the class name and tolerate either spelling in between, and were checked to still fail when an alias is genuinely repointed at another executor ([#11585](https://github.com/diegosouzapw/OmniRoute/pull/11585)) — thanks @ntdat812 +- **test(models):** pin `qwen3.8-max` resolution per provider — two of the four catalogs now serve the bare id and no longer carry `-preview` ([#11590](https://github.com/diegosouzapw/OmniRoute/pull/11590)) — thanks @ntdatt812 +- **test(executors):** drop the stale 128-tool truncation assertion that contradicted the #11444 pass-through contract ([#11591](https://github.com/diegosouzapw/OmniRoute/pull/11591)) — thanks @ntdatt812 +- **test(token-health):** pin the current terminal-skip boundary — expired with retry budget is probed, `account_deactivated` stays skipped ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)) — thanks @ntdatt812 +- **test(models):** follow #11503's retarget of the `gemini-3-pro-high` alias to the hyphenated catalog id ([#11593](https://github.com/diegosouzapw/OmniRoute/pull/11593)) — thanks @ntdatt812 +- **refactor(video bridge):** extract per-video acquisition, whole-result caching, description, and metrics/abort/cleanup out of `VideoBridgeGuardrail.preCall` into a `processVideoPart` seam in a new `videoBridgePipeline.ts`, behind explicit `VideoMediaBrokerPort`, `VideoAudioTranscriptionPort`, and `VideoDrilldownPort` boundaries; `preCall` now only handles request traversal, policy, and response aggregation. The Video tab's FFmpeg/ffprobe runtime status is now an explicit `unknown` / `restricted` / `unavailable` / `available` state instead of a nullable boolean pair, fixing a case where an in-flight or failed probe was mislabeled as "install FFmpeg" ([#11657](https://github.com/diegosouzapw/OmniRoute/issues/11657)). ([#12016](https://github.com/diegosouzapw/OmniRoute/pull/12016)) +- **test(opencode-plugin):** add unit test coverage for `formatFreeBudget()` naming helper ([#11660](https://github.com/diegosouzapw/OmniRoute/pull/11660)) — thanks @f9td56dbgh-hub +- **docs(video):** clarify that the Video Bridge transcript `source` field (`client`, `embedded`, `audio-bridge`) is presently caller-declared and not yet server-verified — OmniRoute enforces the enum shape but does not cryptographically confirm that an `embedded`/`audio-bridge` label came from a server-owned extraction ([#11661](https://github.com/diegosouzapw/OmniRoute/issues/11661)). ([#12001](https://github.com/diegosouzapw/OmniRoute/pull/12001)) +- **chore(providers):** retire the distributed Raycast Relay and Hailuo Web integrations after a provenance audit confirmed substantial derivation from GPL-3.0 sources; the official GitHub and MiniMax API providers remain available. Migration 163 disables legacy connections and invalidates their active leases while preserving connection IDs, audit/history rows, quota snapshots, usage records, and fail-closed API-key connection policies. ([#11691](https://github.com/diegosouzapw/OmniRoute/pull/11691)) +- **chore(providers):** retire the Felo Web provider while its GPL-derived provenance remains on licensing HOLD ([#11698](https://github.com/diegosouzapw/OmniRoute/pull/11698)) — stale runtime and database references now fail closed while existing connection, lease, and usage history remains preserved for auditability. +- **fix(sse):** retire the Gemini Web image-generation bridge while provenance and licensing remain on HOLD, preserving Gemini Web chat/auth/tools and legitimate Gemini/Nano Banana image providers ([#11708](https://github.com/diegosouzapw/OmniRoute/pull/11708)) +- **chore(audio):** retire the built-in EdgeTTS provider and its 11-voice catalog while provenance/licensing review remains on HOLD; the generic `/v1/audio/speech` endpoint and the other speech providers remain available ([#11711](https://github.com/diegosouzapw/OmniRoute/pull/11711)) +- **fix(providers):** retire the unofficial Qwen Web integration and its `qwen-web`/`qw` identities while third-party provenance remains on HOLD ([#11713](https://github.com/diegosouzapw/OmniRoute/pull/11713)) — direct requests now fail closed with a sanitized HTTP 410 and combos skip the retired target, while migration guards preserve connection IDs, allowlists, usage/audit history and invalidate active leases. Supported Qwen API, Qwen Code, cloud, token-plan, audio, local and model-name surfaces remain available; restoring Qwen Web requires a qualified provenance/license decision. +- **fix(providers):** retire `microsoft-designer-web` and its `msdesigner` alias from runtime and catalogs while preserving stored credentials, provider history and allowlists as inactive records — source dependency on gpt4free's [`MicrosoftDesigner.py`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/g4f/Provider/needs_auth/MicrosoftDesigner.py) is proven at snapshot `0943d16eef757a44deb60651c07bbe5fa958cafe`, whose [`LICENSE`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/LICENSE) is GPL-3.0; expressive adaptation is probable, which records provenance/licensing risk without asserting a legal conclusion ([#11720](https://github.com/diegosouzapw/OmniRoute/pull/11720)). +- **docs(legal):** centralize pinned MIT notices for the vendored GCF codec and local flag assets ([#11726](https://github.com/diegosouzapw/OmniRoute/pull/11726)) +- Split the 2,952-line `adobeFireflyClient.ts` into nine focused modules (catalog, credentials, payload, ARP session, upload, responses, IMS, account, poll) behind an unchanged barrel — no consumer edits and no public API change (#11728). — thanks @MumuTW +- **fix(dashboard):** replaced the Model Lockout card's unprovenanced notification MP3 with a short, locally synthesized Web Audio chime ([#11731](https://github.com/diegosouzapw/OmniRoute/pull/11731)) — both toggles retain optional audible feedback without shipping replacement media or third-party code, and unsupported, suspended, or rejected audio contexts fail open without blocking the setting change. +- **chore(deepseek-web):** remove the provenance-unresolved DeepSeek PoW WASM binary and its runtime loader/tracing while retaining the existing JavaScript solver (slower at high difficulty) ([#11732](https://github.com/diegosouzapw/OmniRoute/pull/11732)). +- **chore(assets):** remove four unused provider assets whose redistribution provenance could not be verified while retaining the active Freebuff SVG ([#11735](https://github.com/diegosouzapw/OmniRoute/pull/11735)) +- **docs(assets):** retired two raster images whose redistribution provenance could not be verified ([#11736](https://github.com/diegosouzapw/OmniRoute/pull/11736)) — PicoClaw documentation now reuses the existing SVG, while Zoo Code remains a text link without an image. +- **docs(legal):** add pinned notices for six LobeHub-derived provider SVGs and 65 byte-exact theSVG assets, retaining registry claims as unverified metadata with explicit trademark and HOLD boundaries ([#11737](https://github.com/diegosouzapw/OmniRoute/pull/11737)). +- **chore(quality):** added a versioned 225/225 provider-asset provenance manifest and blocking drift gate ([#11743](https://github.com/diegosouzapw/OmniRoute/pull/11743)) — the inventory pins magic MIME and SHA-256 for every physical file, records immutable source matches for 71 assets, keeps 69 probable and 85 unresolved entries explicit but non-blocking, and treats provenance as source evidence rather than copyright or trademark clearance. +- **refactor(mitm):** replace four previously derived Traffic Inspector utilities — conversation normalization, SSE merging, secret masking, and header sanitization — with independent clean-room implementations based on public provider schemas, WHATWG SSE, and RFC 6750, while keeping the two independently classified passthrough surfaces unchanged ([#11748](https://github.com/diegosouzapw/OmniRoute/pull/11748)). +- Added a unit test that fails seven days before any dated pack under `config/` (`validUntil` and sibling keys) lapses, naming the file and key. The Alibaba free-tier pack expired on 2026-08-27 and turned every PR red the next morning with no commit involved; renewal now happens on someone's terms, not the clock's. ([#11891](https://github.com/diegosouzapw/OmniRoute/pull/11891)) +- `check:workflows` now fails (under `--strict`/`--ratchet`) when any job routed to a self-hosted runner publishes with `--provenance` — npm rejects that with `422` at the registry, which in v3.8.50 only surfaced after the tag and Docker images were public. ([#11895](https://github.com/diegosouzapw/OmniRoute/pull/11895)) +- `scripts/ops/runner-janitor.sh` now proves a path is idle with one `lsof` snapshot and removes stale leftovers itself (tmpfs after 3 h — it is RAM — disk after 24 h), kills orphan `next-build` processes, prunes checkouts of stopped runners, and alerts on memory pressure; `--dry-run` shows exactly what it would do. `docs/ops/RUNNER_BOX.md` reconciled to the measured box (31 GB, 10 listeners). ([#11893](https://github.com/diegosouzapw/OmniRoute/pull/11893)) +- The `next-build` artefact (1.3 GB) is now written and read under `$RUNNER_TEMP` (per-runner, on disk) instead of `/tmp`, which on the self-hosted pool is a 12 GB tmpfs in RAM. Landing it there took 27–32 of the publish job's 76 minutes, and the fixed `/tmp/e2e-build.tar.gz` name let E2E jobs on different runners overwrite each other's download. ([#11896](https://github.com/diegosouzapw/OmniRoute/pull/11896)) +- The CI `build` job now runs in two concurrency lanes — `main` and pull requests — so a release build is never queued behind (or OOM-killed beside) PR builds on the self-hosted pool, which holds one `next-build` comfortably and two at the edge. ([#11901](https://github.com/diegosouzapw/OmniRoute/pull/11901)) +- Re-freeze the ESLint suppressions on `release/v3.8.51` from a clean-room measurement (2 stale file entries pruned, 55 pre-existing `no-explicit-any` in six new files frozen under #11924) and drop the dead `GPT_SIZE_MAP` constant orphaned by the Adobe Firefly client split, so `No new ESLint warnings` stops failing every PR with exit 2 (Refs #11924) ([#11955](https://github.com/diegosouzapw/OmniRoute/pull/11955)) +- Type the 55 `no-explicit-any` sites that had been frozen under #11924 — four redundant casts in `socksConnectorWithFamily.ts` (undici/socks types already accept them) and the mocks/fixtures of the socks-timeout and isFree suites — and drop their suppression entries; the ESLint ratchet shrinks from 5487 to 5432 (Closes #11924) ([#11975](https://github.com/diegosouzapw/OmniRoute/pull/11975)) +- Take the two hosted-runner builds off the PR rail: `Build App` (`build.yml`) is `workflow_dispatch`-only and `DAST smoke (PR)` runs only for PRs into `main` — the 7 GB hosted VM cannot build `release/v3.8.51` in any profile (19/30 red, VM shutdown ~8 min into `next build`) and both checks had turned into permanent noise on every release PR; the bundle stays validated by `ci.yml` on `main` and by `nightly-release-green` on `release/**` (Closes #11946) ([#11962](https://github.com/diegosouzapw/OmniRoute/pull/11962)) +- Move the four nightly jobs that build the backend (`nightly-schemathesis`, `nightly-llm-security` promptfoo + garak, `nightly-resilience` axe-a11y) off the hosted 7 GB runner — where they died on `release/v3.8.51` unseen — onto the box's new `omni-light` pool (two listeners), and document the reshaped fleet (4 active OmniRoute listeners: 2 `omni-build` + 2 `omni-light`, janitor ceiling 4) (Closes #11965) ([#11967](https://github.com/diegosouzapw/OmniRoute/pull/11967)) +- Stop painting every fork PR into `release/**` red: `quality.yml` `Build (advisory)` is skipped (GitHub still reports `continue-on-error` failures as check FAILURE). Hosted `ubuntu-latest` cannot finish `npm run build` on this tree — same class as #11962 taking `build.yml` off the PR rail. `docker-publish.yml` amd64 now runs on the `.113` `omni-build` pool (31 GB / 32 cores, two listeners) with Turbopack, shares the `heavy-build-main` lane with `ci.yml` `Build` so it queues instead of becoming a third heavy, and keeps per-ref concurrency (a merge storm was starting 8 concurrent OOM builds). arm64 stays on `ubuntu-24.04-arm` with webpack — there is no ARM box. Fallback when `USE_VPS_RUNNER` is off: hosted amd64 + webpack (#11976). ([#12021](https://github.com/diegosouzapw/OmniRoute/pull/12021)) +- `docker-publish.yml` builds with webpack (`OMNIROUTE_USE_TURBOPACK=0`) on **both** arches, including amd64 on the `.113` `omni-build` pool. Turbopack had the RAM (31 GB, `omniroute-113-6`) and still panicked (`TurbopackInternalError: there must be a path to a root`, run 33253576569, 26 min in). The same tree's arm64 webpack build on hosted `ubuntu-24.04-arm` succeeded (run 33264823398). Dockerfile already documents webpack as the Docker escape hatch (#11976). ([#12050](https://github.com/diegosouzapw/OmniRoute/pull/12050)) +- **docs(free-tier):** declare the counting vs deciding regimes for "is it free?" and guard the deciding path from DB-backed catalog resolution ([#12226](https://github.com/diegosouzapw/OmniRoute/pull/12226)) — thanks @maxmad64bis +- **docs(auto-combo):** The mode pack table in `docs/routing/AUTO-COMBO.md` now lists all six shipped packs with every weight each one sets, replacing a four-pack table whose numbers had also drifted from the source. It states plainly that no pack sets `quality`, so selecting any pack silences the observed-quality signal. Six more documents that quote the scoring factor count joined the `check:docs-counts` gate, which caught five stale claims — including one naming nine factors that do not exist — and two stale mode pack counts. The dashboard routing panel, which offered four of the six packs and labelled the default strategy "6-Factor Scoring", is now covered by a test; the two packs it was missing are `reliability-first` and `chaos-mode`, the latter labelled as the fault-injection profile it is rather than as one more routing preference ([#12316](https://github.com/diegosouzapw/OmniRoute/pull/12316)) — thanks @maxmad64bis +- **chore(quality):** rebaseline `src/lib/db/apiKeys.ts` for the ACL the key-creation path now preserves ([#12352](https://github.com/diegosouzapw/OmniRoute/pull/12352)) ([#12673](https://github.com/diegosouzapw/OmniRoute/pull/12673)) — thanks @KrzysiekSko +- **chore(providers):** bump the Claude Code wire identity and the Devin bridge image pin from `2.1.220` to `2.1.258` ([#12402](https://github.com/diegosouzapw/OmniRoute/pull/12402)) — thanks @ggiak ([#12604](https://github.com/diegosouzapw/OmniRoute/pull/12604)) +- **docs(env):** align `.env.example`, the README Bun section, and the troubleshooting guide with the code: `OMNIROUTE_USE_TURBOPACK` also governs `npm run build` (not dev-only), `bun run build` follows that flag instead of auto-selecting Webpack, `OMNIROUTE_CHAT_MAX_HEAVY_IN_FLIGHT` is unset by default (no request-count cap), and the structural `503 chat_admission_busy` message matches `chatAdmissionResponses.ts` (#12404 — thanks @pacocartones) +- **chore(electron):** upgrade the desktop app to Electron 44 (Chromium 152, Node 24.18.1) ([#12217](https://github.com/diegosouzapw/OmniRoute/pull/12217)). **Requires macOS 13 (Ventura) or later** — Chromium dropped macOS 12 (Monterey), so Monterey users must stay on an earlier OmniRoute desktop build. Windows and Linux are unaffected; the app already shipped only x64/arm64, so Electron 44 dropping 32-bit builds changes nothing. Removes the `openAsHidden`/`wasOpenedAsHidden` login-item fields deleted in Electron 44 — hidden autostart continues to work through the `--hidden` argument registered with the login item ([#12554](https://github.com/diegosouzapw/OmniRoute/pull/12554)) — thanks @app/dependabot +- **chore(quality):** rebaseline `src/sse/handlers/chat.ts` for the effective-input persistence the continuation fix needs ([#12641](https://github.com/diegosouzapw/OmniRoute/pull/12641)) ([#12680](https://github.com/diegosouzapw/OmniRoute/pull/12680)) — thanks @hartmark +- `Coverage` job on `ci.yml`: the informational Codecov upload gets its own 5-minute ceiling and `continue-on-error`, and the job budget grows from 20 to 30 minutes (the 8-shard c8 merge alone takes ~10) — a stalled upload no longer ends the job `cancelled` and drags a fully green `main` run's conclusion down with it ([#11972](https://github.com/diegosouzapw/OmniRoute/pull/11972)) +- CI hardening on the PR rail: the four `Unit Tests fast-path` shards get `timeout-minutes: 30` (a hung shard held a PR for 64 min instead of GitHub's 6 h default) and both ESLint file caches lose their `restore-keys` fallback, so a cache built under another suppressions file or lint config can no longer report stale verdicts (Refs #11600, #11924) ([#11963](https://github.com/diegosouzapw/OmniRoute/pull/11963)) +- Every CI job that runs a `next build` (`build`, the npm `publish`, both release-green validations) now targets the `omni-build` runner label, which only two of the eight self-hosted runners carry. The box holds one build comfortably and two at the edge; a third now queues on GitHub instead of being OOM-killed by the kernel. ([#11932](https://github.com/diegosouzapw/OmniRoute/pull/11932)) +- **refactor(combo):** move `handleRoundRobinCombo` (and `resolveTargetTokenLimit`) into `open-sse/services/combo/roundRobinCombo.ts`. `combo.ts` drops from 2164 to 1014 lines (`split("\n").length`); the leaf is 1199 (under the 1200 new-file cap). The round-robin call site uses a dynamic `import()` so `releaseStickyPinOnFailure` / `clearStaleLKGP` can stay exported from `combo.ts` without a static cycle. Skip / sticky / semaphore / safety-timer behavior is unchanged. ([#12811](https://github.com/diegosouzapw/OmniRoute/pull/12811)) — thanks @HouMinXi +- **chore(quality):** rebaseline the file-size caps the error-boundary campaign grew past (`open-sse/executors/codex.ts`, `open-sse/vendor/codex-chatgpt-web/bridge.ts`, both via [#12444](https://github.com/diegosouzapw/OmniRoute/pull/12444)) ([#12654](https://github.com/diegosouzapw/OmniRoute/pull/12654)) +- **chore(quality):** rebaseline the file-size caps the hartmark batch grew past (`combos/page.tsx` via [#12355](https://github.com/diegosouzapw/OmniRoute/pull/12355), `open-sse/services/combo.ts` via [#12338](https://github.com/diegosouzapw/OmniRoute/pull/12338)) ([#12623](https://github.com/diegosouzapw/OmniRoute/pull/12623)) — thanks @hartmark +- **chore(quality):** rebaseline the file-size caps the HouMinXi batch grew past when its PRs stacked (`providers/page.tsx`, `chatCore.ts`, `accountFallback.ts`) — each PR measured correctly in isolation, none saw the stacking ([#12619](https://github.com/diegosouzapw/OmniRoute/pull/12619)) +- **chore(quality):** rebaseline `open-sse/services/combo.ts` for the reset-aware scoring the HouMinXi batch stacked ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) ([#12678](https://github.com/diegosouzapw/OmniRoute/pull/12678)) — thanks @HouMinXi +- **docs:** sync provider catalog count (direct commit `4f11b2ae3d`) +- **docs(skills):** sync omni-inference search fallback (direct commit `b4e76a7ed9`) +- **chore(lint):** prune two stale base suppressions (direct commit `1325047d56`) +- **test(auto-combo):** tolerate full-suite rotation load (direct commit `ad3e293f9f`) +- **test:** drain inherited quality snapshot reds (direct commit `21ed68d8ac`) +- **chore(quality):** rebaseline measured CLI bundle growth (direct commit `d35b3f9779`) +- **test(ui):** reproduce invalid AnySearch icon alias (direct commit `12cc6ca834`) +- **test(translator):** fix the relative imports of the relocated deferred-finish test (direct commit `777d9d1629`) +- **docs(counts):** bring the documented provider and migration counts to the measured values (direct commit `e70bea30e9`) +- **test(security):** drop the guard-target set left unused by the sync (direct commit `a32ba0e362`) +- **chore(db):** renumber model_capabilities migration 163 → 169 (cross-branch collision with 163_radar_feed_cache_generated_at) (direct commit `70f5d4cbf9`) +- **docs(cli):** document CLI_PRIME_AGENT_BIN and correct the CLI Agents count (direct commit `9058e39b61`) +- **chore(deps):** refresh runtimes and adopt ESLint 10 ([#11259](https://github.com/diegosouzapw/OmniRoute/pull/11259)) — thanks @backryun +- **refactor(local-corpus):** implement dynamic root resolution and LRU cache ([#11491](https://github.com/diegosouzapw/OmniRoute/pull/11491)) — thanks @raheemuddin786 +- **chore(quality):** prune stale eslint-suppressions entries ([#11596](https://github.com/diegosouzapw/OmniRoute/pull/11596)) +- **chore(ci):** unblock quality gates - env-doc sync + stryker tap.testFiles registration (#9282) ([#11609](https://github.com/diegosouzapw/OmniRoute/pull/11609)) — thanks @Meet6338-X +- **chore(ci):** point the circular-deps gate at dpdm's real JS entrypoint ([#11615](https://github.com/diegosouzapw/OmniRoute/pull/11615)) — thanks @benzntech +- **chore(quality):** rebaseline file-size for #11448/#11495/#11561 growth ([#11630](https://github.com/diegosouzapw/OmniRoute/pull/11630)) +- **deps:** realign onnxruntime-node pin to @huggingface/transformers' 1.24.3 lockstep ([#11633](https://github.com/diegosouzapw/OmniRoute/pull/11633)) — thanks @jonlwheat2-gif +- **test(autoCombo):** port models_dev_tier #11508 guard from node:test to vitest ([#11635](https://github.com/diegosouzapw/OmniRoute/pull/11635)) — thanks @jonlwheat2-gif +- **chore(quality):** rebaseline file-size for #11083/#11631/#11628 growth ([#11636](https://github.com/diegosouzapw/OmniRoute/pull/11636)) +- **chore(lint):** prune stale ESLint suppressions (base-red #11449) ([#11644](https://github.com/diegosouzapw/OmniRoute/pull/11644)) — thanks @jonlwheat2-gif +- **docs(changelog):** aggregate the ten v3.8.50 fragments into CHANGELOG.md ([#11683](https://github.com/diegosouzapw/OmniRoute/pull/11683)) +- **chore(search):** drop unreachable credential sweep after #11642 ([#11687](https://github.com/diegosouzapw/OmniRoute/pull/11687)) — thanks @HouMinXi +- **ci:** add API route TypeScript regression gate ([#11705](https://github.com/diegosouzapw/OmniRoute/pull/11705)) — thanks @Sabeekhann +- **docs(dedup):** correct the CodeQL claim on the tenant-namespace comment ([#11712](https://github.com/diegosouzapw/OmniRoute/pull/11712)) +- **docs(changelog):** add consolidated v3.8.50 stats, top-25 ranking and a .mailmap ([#11715](https://github.com/diegosouzapw/OmniRoute/pull/11715)) +- **docs:** recommend gstack for AI-assisted workflows ([#11770](https://github.com/diegosouzapw/OmniRoute/pull/11770)) — thanks @santosraju99-hub +- **chore(quality):** tighten complexity/cognitive-complexity ratchets to the current tip; land the missed gateways.ts rebaseline ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **docs(i18n):** finish Freepik → Magnific rebrand in locale strings and README ([#11772](https://github.com/diegosouzapw/OmniRoute/pull/11772)) — thanks @turbolego +- **refactor(auto-combo):** fix divergent scoring in combo health reporting ([#11854](https://github.com/diegosouzapw/OmniRoute/pull/11854)) — thanks @jacobsparts +- **chore(changelog):** add missing fragments for #11919/#11918/#11916 ([#11938](https://github.com/diegosouzapw/OmniRoute/pull/11938)) +- **test(check):** escape the runs-on fixture with JSON.stringify, not a quote-only replace ([#11942](https://github.com/diegosouzapw/OmniRoute/pull/11942)) (direct commit `fb7445eaa3`) +- **test(release):** align five suites with the contracts #11933, #11919 and #11876 shipped on release/v3.8.51 ([#11944](https://github.com/diegosouzapw/OmniRoute/pull/11944)) +- **chore(quality):** remove unreachable code and restore test discovery ([#11950](https://github.com/diegosouzapw/OmniRoute/pull/11950)) — thanks @backryun +- **docs(agents):** sync-back landings are fast-forward, never squash ([#11964](https://github.com/diegosouzapw/OmniRoute/pull/11964)) +- **test(infra):** retry recursive temp-dir removal instead of failing a shard on ENOTEMPTY (#11966) ([#11968](https://github.com/diegosouzapw/OmniRoute/pull/11968)) +- **docs(ops):** the .113 heavy-build ceiling is one runner, not two ([#12048](https://github.com/diegosouzapw/OmniRoute/pull/12048)) +- **test(ui):** correct inactive auto-fetch expectation ([#12098](https://github.com/diegosouzapw/OmniRoute/pull/12098)) — thanks @RaviTharuma +- **docs:** document native dependency check escape hatch ([#12101](https://github.com/diegosouzapw/OmniRoute/pull/12101)) — thanks @RaviTharuma +- **test(providers):** regenerate the translate-path golden for OrcaRouter (#11923) ([#12118](https://github.com/diegosouzapw/OmniRoute/pull/12118)) +- **chore(quality):** velocity phase — loosen every numeric baseline by 20% until v4.0, monitor headroom nightly ([#12125](https://github.com/diegosouzapw/OmniRoute/pull/12125)) +- **chore(lint):** batch 0 of #12146 — type the call-log-cap sqlite rows instead of 45 as-any casts ([#12157](https://github.com/diegosouzapw/OmniRoute/pull/12157)) +- **chore(lint):** batch 4 of #12146 — shared/components react-hooks violations resolved ([#12159](https://github.com/diegosouzapw/OmniRoute/pull/12159)) +- **chore(lint):** batch 1 of #12146 — dashboard/cli-code react-hooks violations resolved ([#12160](https://github.com/diegosouzapw/OmniRoute/pull/12160)) +- **chore(lint):** batch 3 of #12146 — dashboard/settings react-hooks violations resolved ([#12162](https://github.com/diegosouzapw/OmniRoute/pull/12162)) +- **chore(lint):** batch 2 of #12146 — resolve the react-hooks compiler violations in dashboard/providers ([#12163](https://github.com/diegosouzapw/OmniRoute/pull/12163)) +- **chore(quality):** register search-432 cooldown test in stryker tap.testFiles ([#12170](https://github.com/diegosouzapw/OmniRoute/pull/12170)) +- **chore(lint):** batch 5 of #12146 — combos, endpoint, provider-stats, api-manager and costs react-hooks violations resolved ([#12174](https://github.com/diegosouzapw/OmniRoute/pull/12174)) +- **build:** add contributor fast profile ([#12192](https://github.com/diegosouzapw/OmniRoute/pull/12192)) — thanks @rafacpti23 +- **docs(readme):** README touch-ups ([#12193](https://github.com/diegosouzapw/OmniRoute/pull/12193)) ([#12194](https://github.com/diegosouzapw/OmniRoute/pull/12194)) ([#12202](https://github.com/diegosouzapw/OmniRoute/pull/12202)) — thanks @rafacpti23 +- **build:** force the webpack bundler in the contributor build profile ([#12197](https://github.com/diegosouzapw/OmniRoute/pull/12197)) — thanks @rafacpti23 +- **build:** skip standalone packaging in the contributor profile ([#12198](https://github.com/diegosouzapw/OmniRoute/pull/12198)) — thanks @rafacpti23 +- **docs(audit):** align every published number with the code and harden check:docs-counts ([#12200](https://github.com/diegosouzapw/OmniRoute/pull/12200)) +- **build:** stub the instrumentation entrypoints in the contributor profile ([#12203](https://github.com/diegosouzapw/OmniRoute/pull/12203)) — thanks @rafacpti23 +- **build:** omit the standalone output target for contributor builds ([#12204](https://github.com/diegosouzapw/OmniRoute/pull/12204)) — thanks @rafacpti23 +- **chore(lint):** batch 6 of #12146 — memory, radar, audit, analytics, cache, usage, activity, home and RequestLoggerV2 react-hooks violations resolved ([#12208](https://github.com/diegosouzapw/OmniRoute/pull/12208)) +- **docs(diagrams):** rename number-carrying diagram files to stable names ([#12210](https://github.com/diegosouzapw/OmniRoute/pull/12210)) +- **docs(dashboard):** redraw onboarding tier-flow SVGs for the real 4-tier model ([#12211](https://github.com/diegosouzapw/OmniRoute/pull/12211)) +- **docs(api):** document every implemented route in openapi.yaml (276 → 692 paths) ([#12212](https://github.com/diegosouzapw/OmniRoute/pull/12212)) +- **chore(lint):** batch 7 of #12146 — final src tail: 37 react-hooks violations across 33 files resolved ([#12244](https://github.com/diegosouzapw/OmniRoute/pull/12244)) +- Revert "docs: recommend gstack for AI-assisted workflows (#11770)" ([#12248](https://github.com/diegosouzapw/OmniRoute/pull/12248)) ([#12249](https://github.com/diegosouzapw/OmniRoute/pull/12249)) +- **docs:** Chaos Mode setup guide + weighted strategy semantics ([#12250](https://github.com/diegosouzapw/OmniRoute/pull/12250)) +- **docs(agents):** protected-surface merge rule — operator approval for agent-instruction files ([#12253](https://github.com/diegosouzapw/OmniRoute/pull/12253)) +- **chore(quality):** register native-codex turn-pin tests in stryker tap.testFiles ([#12263](https://github.com/diegosouzapw/OmniRoute/pull/12263)) +- **chore(quality):** dedupe tap.testFiles entries added by racing base-red fixes ([#12265](https://github.com/diegosouzapw/OmniRoute/pull/12265)) +- **docs(readme):** introduce OmniRouteTray — the macOS menu-bar companion ([#12276](https://github.com/diegosouzapw/OmniRoute/pull/12276)) — thanks @ggiak +- **chore(proxy):** purge the legacy 1proxy residue — sync/rotator modules, dead DB exports, dead settings tab and flag, docs (#12091) ([#12290](https://github.com/diegosouzapw/OmniRoute/pull/12290)) +- **chore(quality):** baseline-headroom skips generated and vendored files in the fileSize worst-file signal ([#12291](https://github.com/diegosouzapw/OmniRoute/pull/12291)) +- **refactor(video):** unify the JPEG frame data-URI contract ([#12322](https://github.com/diegosouzapw/OmniRoute/pull/12322)) +- **chore(deps):** freeze onnxruntime-node and eslint-plugin-react-hooks out of dependabot groups ([#12329](https://github.com/diegosouzapw/OmniRoute/pull/12329)) +- **test(executors):** restore the #10986 reasoning-only fallback guards ([#12364](https://github.com/diegosouzapw/OmniRoute/pull/12364)) — thanks @pacocartones +- **chore(quality):** re-tighten the file-size ratchet to the real LOC (plan 3.8.52 task 0) ([#12411](https://github.com/diegosouzapw/OmniRoute/pull/12411)) +- **test(grok):** format web executor suite ([#12412](https://github.com/diegosouzapw/OmniRoute/pull/12412)) +- **chore(quality):** ratchet open-sse typecheck baseline to zero ([#12418](https://github.com/diegosouzapw/OmniRoute/pull/12418)) +- **test(build):** guard the artifact path policy arrays against duplicates ([#12422](https://github.com/diegosouzapw/OmniRoute/pull/12422)) +- **chore(lint):** adopt eslint-plugin-react-hooks 7.1.1 ([#12428](https://github.com/diegosouzapw/OmniRoute/pull/12428)) +- **chore(providers):** remove a keyless provider integration at its operator's request ([#12440](https://github.com/diegosouzapw/OmniRoute/pull/12440)) +- **docs(providers):** register providers removed at their operator's request and guard against reintroduction ([#12478](https://github.com/diegosouzapw/OmniRoute/pull/12478)) +- **chore(quality):** base fixes — stryker tap.testFiles + node_modules cache key ([#12482](https://github.com/diegosouzapw/OmniRoute/pull/12482)) +- **docs(arch):** one-process recipe for tens of long /v1/responses ([#12493](https://github.com/diegosouzapw/OmniRoute/pull/12493)) — thanks @RaviTharuma +- **chore(quality):** tighten the CodeQL ratchet baseline from 11 to 6 ([#12530](https://github.com/diegosouzapw/OmniRoute/pull/12530)) +- **chore(deps):** pin browserslist override to ^4.28.8 ([#12592](https://github.com/diegosouzapw/OmniRoute/pull/12592)) — thanks @KooshaPari +- **chore(deps):** bump fast-uri to 3.1.7 in the electron lockfile ([#12601](https://github.com/diegosouzapw/OmniRoute/pull/12601)) +- **docs(quality):** document how the CodeQL ratchet refreshes and how to tighten it ([#12611](https://github.com/diegosouzapw/OmniRoute/pull/12611)) +- **test(ci):** pin the openapi-security-tiers two-arm contract with an executing gate test (#12581) ([#12652](https://github.com/diegosouzapw/OmniRoute/pull/12652)) +- **docs(gamification):** align XP Rewards table with code (#12501) ([#12667](https://github.com/diegosouzapw/OmniRoute/pull/12667)) — thanks @KooshaPari +- **refactor(combo):** split executeTarget into gates, attempt, and loop ([#12746](https://github.com/diegosouzapw/OmniRoute/pull/12746)) — thanks @HouMinXi +- **docs(security):** document socket.yml scanner config + CI workflow link (#12575) ([#12764](https://github.com/diegosouzapw/OmniRoute/pull/12764)) — thanks @KooshaPari +- **chore(ci):** guard commit identity in pre-commit to stop author misattribution ([#12772](https://github.com/diegosouzapw/OmniRoute/pull/12772)) +- **deps:** 17 Dependabot bumps — bump electron from 43.4.0 to 43.4.1 in /electron ([#11426](https://github.com/diegosouzapw/OmniRoute/pull/11426)); bump the development group across 1 directory with 8 updates ([#11428](https://github.com/diegosouzapw/OmniRoute/pull/11428)); bump the production group across 1 directory with 11 updates ([#11440](https://github.com/diegosouzapw/OmniRoute/pull/11440)); chore(deps): bump github/codeql-action/init from 4.37.7 to 4.37.8 ([#11925](https://github.com/diegosouzapw/OmniRoute/pull/11925)); chore(deps): bump github/codeql-action from 4.37.7 to 4.37.8 ([#11926](https://github.com/diegosouzapw/OmniRoute/pull/11926)); chore(deps): bump github/codeql-action/analyze from 4.37.7 to 4.37.8 ([#11927](https://github.com/diegosouzapw/OmniRoute/pull/11927)); build(deps): bump github/codeql-action/init from 4.37.8 to 4.37.9 ([#12345](https://github.com/diegosouzapw/OmniRoute/pull/12345)); build(deps): bump github/codeql-action/analyze from 4.37.8 to 4.37.9 ([#12346](https://github.com/diegosouzapw/OmniRoute/pull/12346)); bump the development group across 1 directory with 2 updates ([#12347](https://github.com/diegosouzapw/OmniRoute/pull/12347)); build(deps): bump github/codeql-action from 4.37.8 to 4.37.9 ([#12349](https://github.com/diegosouzapw/OmniRoute/pull/12349)); bump browserslist from 4.28.2 to 4.28.8 ([#12396](https://github.com/diegosouzapw/OmniRoute/pull/12396)); bump the production group across 1 directory with 4 updates ([#12399](https://github.com/diegosouzapw/OmniRoute/pull/12399)); bump @xmldom/xmldom ([#12500](https://github.com/diegosouzapw/OmniRoute/pull/12500)); bump qs from 6.15.2 to 6.16.0 ([#12512](https://github.com/diegosouzapw/OmniRoute/pull/12512)); bump @xmldom/xmldom from 0.9.10 to 0.9.12 ([#12513](https://github.com/diegosouzapw/OmniRoute/pull/12513)); bump fast-uri from 3.1.5 to 3.1.7 ([#12514](https://github.com/diegosouzapw/OmniRoute/pull/12514)); bump @humanfs/node from 0.16.7 to 0.16.8 ([#12515](https://github.com/diegosouzapw/OmniRoute/pull/12515)) +- Clear the `release/v3.8.51` `check:agent-skills-sync` base-red: regenerate `skills/cli-tunnel/SKILL.md` so the `tunnel create [type]` positional that #13009 taught the generator to read is reflected in the committed skill. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` typecheck base-red from #12731: the new `Mode pack ranking gates` candidates in `open-sse/services/autoCombo/__tests__/autoCombo.test.ts` omitted the required `provider`, `model` and `errorRate` fields of `ProviderCandidate`, failing `check:open-sse-typecheck` (and with it `Fast Quality Gates`) on every open PR. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` docs-sync base-red: EURouter (#13025) and GreenPT (#13024) took the live provider count to 358, leaving 7 STRICT drifts (`PROVIDER_REFERENCE.md`, 4 diagrams, 2 tier-flow images) plus the `AGENTS.md` / `llm.txt` / `package.json` count claims stale. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- **test(models):** Pin `CATALOG_BUILD_TIMEOUT_MS` in the #12058 canonical-catalog regression so a cold catalog build racing the 8s default bound no longer flakes the suite with a 500 ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Allow `@omniroute/opencode-plugin-v2/` in the npm pack policy — [#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870) shipped the v2 plugin beside its v1 sibling without widening the allowlist, so every packed file under it read as an unexpected artifact and failed `check:pack-policy` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Point the release-green validator's pack gate at the tree it is validating — the build-provenance guard defaulted to `origin/main`, which a release branch tip can never be an ancestor of mid-cycle, so `Package artifact` reported a false red on every run and the tarball boot-smoke cascaded off it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **docs(checks):** keep doc counts honest — headings, rankings, catalog, weights, quality gate and scoring diagram now covered ([#12507](https://github.com/diegosouzapw/OmniRoute/pull/12507)) — thanks @maxmad64bis +- **chore(lifecycle):** `check:model-lifecycle` now also diffs `DEFAULT_DEGRADATION_MAP` (the background-task redirect table) against the vendor lifecycle snapshot, refusing a retired id as source or target, with a table-driven unit test beside it. Three rows whose source the vendor had retired — `claude-sonnet-4-20250514`, `gemini-3-pro-preview` and `gpt-5.1-codex` (whose target `gpt-5.1-codex-mini` is retired too) — were dead code, since `checkLifecycle` answers 410 before the redirect runs; they are dropped (#12535 — thanks @pacocartones) +- **test(compression):** cover idle worker eviction at the resource level — the pool must call `terminate()` and must not retain the worker's `MessagePort`, complementing the `exit`-event assertion added with the fix ([#13371](https://github.com/diegosouzapw/OmniRoute/pull/13371)) +- **chore(quality):** raise the `imageGeneration.ts` file-size ceiling for the image-only-model guard that clears the #12945 base-red ([#13392](https://github.com/diegosouzapw/OmniRoute/pull/13392)) +- **chore(quality):** type the combo-test route's JSON response bodies instead of casting them to `any`, drop the now-empty suppression entry, and remove the `hasPerModelQuota` import `chatCore.ts` stopped using when the failure-classification helper was extracted ([#13379](https://github.com/diegosouzapw/OmniRoute/pull/13379)) +- **chore(electron):** the desktop bundle no longer ships the root-level files of every translated docs mirror (`README.md`, `llm.txt`, `CLAUDE.md`, `GEMINI.md`, `CONTRIBUTING.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`) — the packaged app only reads `docs/i18n//docs/**`, which stays. Saves ~11 MB on top of the translated CHANGELOGs already pruned. (#0000) ([#13235](https://github.com/diegosouzapw/OmniRoute/pull/13235)) +- **chore(quality):** retighten the `src/sse/handlers/chat.ts` unused-vars suppression count to the 8 violations that actually remain ([#13385](https://github.com/diegosouzapw/OmniRoute/pull/13385)) +- Bring the documented counts back in line with the code: 171 migrations (was 169) and 20 routing strategies (was 19), plus the regenerated `cli-tunnel` skill reference. ([#12970](https://github.com/diegosouzapw/OmniRoute/pull/12970)) +- Update the documented migration count to 172 after the call-logs provider-stats indexes landed. ([#13209](https://github.com/diegosouzapw/OmniRoute/pull/13209)) +- **chore(ci):** two gates that close the blind spots behind the exclusions above. `check:vitest-exclusions` requires every Vitest exclusion to name a tracking issue and to appear in `config/quality/vitest-exclusions.json` — the previous list grew to 62 files behind a comment pointing at an issue that had been closed for a month. `check-new-key-coverage` requires a key newly added to `en.json` to reach every locale; the existing coverage gate is a percentage floor per locale, so eleven absent keys out of ~13,000 left it at 99.9% while a whole feature shipped untranslated in nine languages. Both are diff-aware, so pre-existing debt stays frozen and neither needed a migration to turn on. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **build:** root layout no longer loads Inter from `next/font/google`, so a production image build does not need fonts.googleapis.com ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **docs(i18n):** bring the eight new `llm.txt` mirrors (km, kn, ml, my, ne, or, pa, si) up to the current provider and migration counts, so `check-docs-sync` stops rejecting every commit ([#13674](https://github.com/diegosouzapw/OmniRoute/pull/13674)) +- **refactor(ui):** flow surfaces (home topology, combo live studio, compression cockpit/waterfall/nodes, token-health badge) express state through the theme-aware `--orch-status-*` tokens instead of fixed dark-mode hex, so green/red/amber/grey stay legible in the light theme; dark mode is byte-identical. Categorical palettes (routing-strategy hues, compression-layer pills, provider brand colors) deliberately stay hex ([#12378](https://github.com/diegosouzapw/OmniRoute/issues/12378)) ([#13203](https://github.com/diegosouzapw/OmniRoute/pull/13203)) +- **chore(tests):** 51 test files that had been excluded from Vitest are running again, restoring roughly 350 assertions to the blocking suite. Each was measured individually first: of the 62 files parked behind the `// #8618 — pre-existing failure` comment, 51 pass against the current tree with no source change, so the exclusions had outlived the failures they were added for. The 11 that genuinely still fail stay excluded, but now point at a live tracker (#13204) rather than at #8618, which was closed in August while the list it tracked kept growing. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **chore(quality):** drop two ESLint suppression entries whose violations no longer exist, so `eslint --suppressions-location` stops rejecting every commit that touches the surrounding files ([#13384](https://github.com/diegosouzapw/OmniRoute/pull/13384)) +- **chore(release):** bring main's post-tag fixes into release/v3.8.51 — sync-back step 2/2 (direct commit `21c488f210`) +- **chore(deps):** patch toml and esbuild in the opencode plugin lockfiles ([#12965](https://github.com/diegosouzapw/OmniRoute/pull/12965)) +- **docs(changelog):** reconcile the v3.8.51 living section — fold 366 fragments, cover every cycle commit, credit every contributor ([#12971](https://github.com/diegosouzapw/OmniRoute/pull/12971)) +- **test(plugin):** wait for the v2 lazy-refresh tier instead of sleeping past it ([#12984](https://github.com/diegosouzapw/OmniRoute/pull/12984)) +- **chore(quality):** rebaseline chatCore.ts after the non-streaming regression fixes ([#13045](https://github.com/diegosouzapw/OmniRoute/pull/13045)) +- **docs(changelog):** add the missing bullet marker to the DeepSeek PoW fragment ([#13200](https://github.com/diegosouzapw/OmniRoute/pull/13200)) +- **chore(deps):** drain the Dependabot queue — 10 of 13 alerts ([#13213](https://github.com/diegosouzapw/OmniRoute/pull/13213)) +- **chore(ci):** make worktrees carry the hooks that enforce the identity gate ([#13236](https://github.com/diegosouzapw/OmniRoute/pull/13236)) +- **chore(deps):** pin csv-parse to ^7.0.2 via overrides (#13117) ([#13300](https://github.com/diegosouzapw/OmniRoute/pull/13300)) — thanks @KooshaPari +- **chore(deps):** bump hono from ^4.12.34 to ^4.13.7 (#13148) ([#13301](https://github.com/diegosouzapw/OmniRoute/pull/13301)) — thanks @KooshaPari +- **chore(deps):** pin joi to ^18.2.8 via overrides (#13085) ([#13302](https://github.com/diegosouzapw/OmniRoute/pull/13302)) — thanks @KooshaPari +- **docs(providers):** add Microsoft 365 Copilot (BizChat) provider guide (#12779) ([#13340](https://github.com/diegosouzapw/OmniRoute/pull/13340)) — thanks @KooshaPari +### 🙌 Contributors + +Thanks to everyone whose work landed in v3.8.51: + +| Contributor | PRs / Issues | +| --- | --- | +| [@5dive-bot](https://github.com/5dive-bot) | #11852 | +| [@abhisheksharma2411](https://github.com/abhisheksharma2411) | #11547, #11684, #11959, #12209 | +| [@adevwithpurpose](https://github.com/adevwithpurpose) | #11464 | +| [@adivekar-utexas](https://github.com/adivekar-utexas) | #12015, #12043, #12090, #12138 | +| [@AIB1TAL0S](https://github.com/AIB1TAL0S) | #12103 | +| [@alltomatos](https://github.com/alltomatos) | #11448 | +| [@alvinveroy](https://github.com/alvinveroy) | #11857, #12027, #12165 | +| [@amaleta](https://github.com/amaleta) | #11906, #11934 | +| [@amartinawi](https://github.com/amartinawi) | #11575 | +| [@AndrianBalanescu](https://github.com/AndrianBalanescu) | #11083, #11830, #11834, #11844 | +| [@anhtahaylove](https://github.com/anhtahaylove) | #13091, #13092, #13093, #13096, #13097, #13100, #13106, #13109, #13114, #13155, #13166, #13171, #13175, #13187, #13196 | +| [@aniruddhaadak80](https://github.com/aniruddhaadak80) | #11760 | +| [@app](https://github.com/app) | #12554 | +| [@arminanton](https://github.com/arminanton) | #11461, #11513 | +| [@AStupidBear](https://github.com/AStupidBear) | #11584 | +| [@atescivitci-cmd](https://github.com/atescivitci-cmd) | #12105 | +| [@b3nw](https://github.com/b3nw) | #11970, #11971, #12147 | +| [@backryun](https://github.com/backryun) | #11259, #11950, #12075, #12076, #12078, #12079, #12081, #12082, #12181, #12228, #12239, #12255, #12258, #12277, #12367, #12423, #12524, #12538 | +| [@benjaminkitt](https://github.com/benjaminkitt) | #11747 | +| [@benzntech](https://github.com/benzntech) | #11614, #11615 | +| [@Bl0ck154](https://github.com/Bl0ck154) | #11948, #11951, #11952, #11953, #11954 | +| [@brick30llc-ctrl](https://github.com/brick30llc-ctrl) | #11752, #12242, #12262 | +| [@Chewji9875](https://github.com/Chewji9875) | #11455, #11875, #12028 | +| [@cheynetom](https://github.com/cheynetom) | #12058 | +| [@davidebaraldo](https://github.com/davidebaraldo) | #12222, #12304 | +| [@davidlinfr](https://github.com/davidlinfr) | #6390 | +| [@ddarkr](https://github.com/ddarkr) | #12124 | +| [@Deftera186](https://github.com/Deftera186) | #11809 | +| [@dpozimski](https://github.com/dpozimski) | #11945 | +| [@DW-MediaLab](https://github.com/DW-MediaLab) | #12229 | +| [@echel0nn](https://github.com/echel0nn) | #11923 | +| [@f9td56dbgh-hub](https://github.com/f9td56dbgh-hub) | #11660 | +| [@fabioluissilva](https://github.com/fabioluissilva) | #11991 | +| [@foreveryh](https://github.com/foreveryh) | #12177 | +| [@geek007git](https://github.com/geek007git) | #12115, #12116, #12117, #12120, #12122 | +| [@geekyNads](https://github.com/geekyNads) | #11784, #12266 | +| [@ggiak](https://github.com/ggiak) | #11561, #12276, #12350, #12402 | +| [@gonisulaimann](https://github.com/gonisulaimann) | #12368, #12369, #12371 | +| [@Gorillaz322](https://github.com/Gorillaz322) | #12207 | +| [@groovecityJO](https://github.com/groovecityJO) | #12682 | +| [@hartmark](https://github.com/hartmark) | #11434, #11452, #11473, #11499, #11703, #11983, #11984, #11985, #11986, #11988, #11989, #11990, #11994, #12221, #12293, #12445, #12446, #12447, #12448, #12460, #12461, #12623, #12646, #12650, #12680, #12717, #12718, #12727, #12741, #12854 | +| [@hizzt](https://github.com/hizzt) | #11894 | +| [@hongnoul](https://github.com/hongnoul) | #11484 | +| [@HouMinXi](https://github.com/HouMinXi) | #11411, #11414, #11512, #11518, #11520, #11641, #11642, #11643, #11687, #11779, #11849, #11850, #11851, #11915, #11916, #11918, #11919, #11920, #12013, #12017, #12033, #12042, #12106, #12139, #12166, #12169, #12171, #12205, #12213, #12312, #12325, #12487, #12488, #12495, #12504, #12557, #12566, #12590, #12591, #12624, #12626, #12632, #12637, #12678, #12696, #12697, #12711, #12733, #12746, #12767, #12770, #12789, #12803, #12805, #12811, #12866, #12868, #12899, #12926, #12934, #12950, #12951, #12974, #13001, #13006, #13011, #13017, #13026, #13027, #13034, #13035, #13038, #13042, #13050, #13060, #13061, #13069, #13107, #13120, #13136, #13195, #13197 | +| [@Hsia97](https://github.com/Hsia97) | #11624 | +| [@jacobsparts](https://github.com/jacobsparts) | #11854, #12155, #12167 | +| [@joglomedia](https://github.com/joglomedia) | #11980 | +| [@jonlwheat2-gif](https://github.com/jonlwheat2-gif) | #11443, #11450, #11549, #11567, #11608, #11633, #11635, #11640, #11644, #11671, #11672, #11673, #11674, #11675, #11676, #11677, #11706, #11783, #12051, #12052, #12053, #12055 | +| [@kanade-hoshino](https://github.com/kanade-hoshino) | #12180 | +| [@Karan825](https://github.com/Karan825) | #11861 | +| [@KaspaPulse](https://github.com/KaspaPulse) | #11389, #11469, #11628, #11666, #11910 | +| [@keeltrace](https://github.com/keeltrace) | #12080, #12223 | +| [@killer30001000](https://github.com/killer30001000) | #12178 | +| [@KooshaPari](https://github.com/KooshaPari) | #12470, #12592, #12667, #12699, #12703, #12706, #12764, #12769, #12771, #13300, #13301, #13302, #13340, #13401, #13403, #13404, #13405, #13406, #13407, #13409, #13410, #13411, #13412, #13413, #13414, #13418, #13424, #13427, #13433, #13523, #13524, #13525, #13528, #13531, #13532, #13534, #13539, #13543, #13545, #13546, #13547, #13550, #13553 | +| [@kriptoburak](https://github.com/kriptoburak) | #11370 | +| [@KrzysiekSko](https://github.com/KrzysiekSko) | #12425, #12432, #12673 | +| [@marcelokarval](https://github.com/marcelokarval) | direct commit / report | +| [@maxmad64bis](https://github.com/maxmad64bis) | #1622, #11435, #11437, #11441, #11537, #11550, #11553, #11555, #11557, #11812, #11842, #11843, #11903, #12151, #12214, #12215, #12218, #12226, #12314, #12316, #12317, #12318, #12319, #12320, #12321, #12507, #12715, #12731, #12744, #12786, #12787, #12788, #12790, #12792, #12794, #12795, #12828, #12832, #12853, #12857, #12870, #12937, #12941, #12975, #13141, #13142, #13146, #13147 | +| [@mdigitalbh81](https://github.com/mdigitalbh81) | #12240 | +| [@Meet6338-X](https://github.com/Meet6338-X) | #11598, #11609, #12110 | +| [@morpheus9393](https://github.com/morpheus9393) | #11943 | +| [@MumuTW](https://github.com/MumuTW) | #11492, #11502, #11506, #11507, #11626, #11675, #11685, #11728, #11746, #11888, #11889, #11890, #11892 | +| [@Neuron-Mr-White](https://github.com/Neuron-Mr-White) | #11622, #11801, #12256 | +| [@NightStalker-87](https://github.com/NightStalker-87) | #12183 | +| [@NoxzRCW](https://github.com/NoxzRCW) | #11879, #11880, #11881, #11882, #11883, #11935 | +| [@ntdat812](https://github.com/ntdat812) | #11585, #12095 | +| [@ntdatt812](https://github.com/ntdatt812) | #11368, #11573, #11574, #11576, #11577, #11580, #11582, #11583, #11588, #11589, #11590, #11591, #11592, #11593, #11672, #12177, #12180, #12873, #12918, #12920, #12921, #12925, #12930, #13007, #13009, #13024, #13025, #13083, #13087, #13101, #13104, #13110 | +| [@official-burak](https://github.com/official-burak) | #11542 | +| [@opensource-elearning](https://github.com/opensource-elearning) | #12179, #12189, #12278, #12286 | +| [@oyi77](https://github.com/oyi77) | #11408, #11409, #11421, #11505, #11677, #12036, #12110 | +| [@pacocartones](https://github.com/pacocartones) | #11521, #11522, #11527, #11528, #11529, #11530, #11531, #11532, #11533, #11534, #11595, #11599, #11603, #11604, #11605, #11607, #11610, #11676, #11714, #11716, #11718, #11767, #11838, #11860, #11862, #11869, #11871, #11872, #11873, #11903, #11906, #11921, #11934, #12356, #12358, #12359, #12360, #12361, #12362, #12364, #12365, #12373, #12374, #12375, #12376, #12377, #12379, #12380, #12381, #12386, #12387, #12389, #12390, #12394, #12395, #12397, #12401, #12403, #12404, #12522, #12523, #12535, #12536, #12540, #12541, #12543, #12545, #12548, #12549, #12550, #12551, #12552, #12647, #12651, #12653, #13055 | +| [@patrykkopycinski](https://github.com/patrykkopycinski) | #11936, #11937, #12224 | +| [@PauloFH](https://github.com/PauloFH) | #11509 | +| [@PauloHSOliveira](https://github.com/PauloHSOliveira) | #12241 | +| [@ponkcore](https://github.com/ponkcore) | #12054 | +| [@Prajeeth-12](https://github.com/Prajeeth-12) | #11634, #12046 | +| [@quiterunner-commits](https://github.com/quiterunner-commits) | #12131, #12143 | +| [@rafacpti23](https://github.com/rafacpti23) | #11554, #11558, #12192, #12197, #12198, #12202, #12203, #12204 | +| [@ragnar-claude](https://github.com/ragnar-claude) | #11564 | +| [@raheemuddin786](https://github.com/raheemuddin786) | #11491, #11839, #11840, #11841, #12003, #12230, #12231, #12232, #12233, #12234 | +| [@rahilmavani](https://github.com/rahilmavani) | #11761 | +| [@Rahulsharma0810](https://github.com/Rahulsharma0810) | #11771 | +| [@RaviTharuma](https://github.com/RaviTharuma) | #11710, #11727, #11797, #11798, #11802, #11805, #11806, #11811, #12098, #12099, #12101, #12449, #12452, #12472, #12473, #12493, #12533, #12607, #12628, #12631, #12636, #12875, #12876, #12880, #12882, #12884 | +| [@rezjalibd](https://github.com/rezjalibd) | #12186 | +| [@RhianB14](https://github.com/RhianB14) | #11692 | +| [@rifqiawl](https://github.com/rifqiawl) | #11517, #11519, #11969 | +| [@rqzbeh](https://github.com/rqzbeh) | #11390 | +| [@Sabeekhann](https://github.com/Sabeekhann) | #11699, #11705 | +| [@santosraju99-hub](https://github.com/santosraju99-hub) | #11755, #11770 | +| [@Seramicx](https://github.com/Seramicx) | #11568 | +| [@Siva010](https://github.com/Siva010) | #12191 | +| [@solstxce](https://github.com/solstxce) | #11597 | +| [@soroush5](https://github.com/soroush5) | #12691 | +| [@tenshiak](https://github.com/tenshiak) | #12279 | +| [@TheDemonTuan](https://github.com/TheDemonTuan) | #11468, #11470, #11471, #11482, #11548, #11758, #11775, #11814 | +| [@thomasmaerz](https://github.com/thomasmaerz) | #12834 | +| [@tuandinh0801](https://github.com/tuandinh0801) | #11454 | +| [@turbolego](https://github.com/turbolego) | #11621, #11762, #11772, #11774, #11781, #12216 | +| [@Tushar49](https://github.com/Tushar49) | #11392 | +| [@ujjawalkaushik1110](https://github.com/ujjawalkaushik1110) | #11863 | +| [@vermasomesh835](https://github.com/vermasomesh835) | #11794 | +| [@vsd2807](https://github.com/vsd2807) | #11565, #11619 | +| [@wahidsadik371-coder](https://github.com/wahidsadik371-coder) | #12126 | +| [@watchingdogs](https://github.com/watchingdogs) | #12031 | +| [@wildcard](https://github.com/wildcard) | #11620 | +| [@xiaoyaner0201](https://github.com/xiaoyaner0201) | #11460, #11662, #12083 | +| [@Xxx91n](https://github.com/Xxx91n) | #11690 | +| [@yourspraveen](https://github.com/yourspraveen) | #11146 | +| [@yxyxy](https://github.com/yxyxy) | #11671 | +| [@ZaimMarzuki](https://github.com/ZaimMarzuki) | #11960 | +| [@Zartharas](https://github.com/Zartharas) | #11340 | +| [@zero-executioner](https://github.com/zero-executioner) | #11631 | +| [@diegosouzapw](https://github.com/diegosouzapw) | maintainer | + +--- ## [3.8.50] — 2026-08-25 _Living section — regenerated 2026-08-12 from all cycle commits (cycle open `ed2db6cb19` → tip). Bullets carry the merged PR and its author; direct pushes listed separately._ diff --git a/docs/i18n/ru/CHANGELOG.md b/docs/i18n/ru/CHANGELOG.md index 6ee90b4c3d..a24aacd200 100644 --- a/docs/i18n/ru/CHANGELOG.md +++ b/docs/i18n/ru/CHANGELOG.md @@ -8,16 +8,1119 @@ ## [3.8.51] — TBD -_Living section — cycle opened at the v3.8.50 freeze (parallel-cycle model). Bullets are aggregated from `changelog.d/` fragments at each `/generate-release` phase._ +_Living section — reconciled 2026-09-15 from all cycle commits (`091589089c` → `c0f92ec98a`, 916 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 | **123** | +| 📝 Commits in the cycle | **916** | +| 🔀 Pull requests referenced | **892** | +| 📋 Changelog entries | **919** | +| 🙌 Contributors credited in entries | **121** | +| 🤖 Automated dependency commits | 18 | + +**Entries by type** + +| Type | Count | +| --- | ---: | +| 🐛 Fixes | 592 | +| ✨ Features | 131 | +| 🧹 Chore | 93 | +| 📚 Docs | 42 | +| 🧪 Tests | 32 | +| ♻️ Refactor | 8 | +| 🏗️ Build | 7 | +| ⚡ Performance | 7 | +| ⚙️ CI | 2 | +| ⏪ Reverts | 2 | +| 📦 Dependencies | 2 | +| 🔒 Security | 1 | + +### 🏆 Top 25 contributors this cycle + +_By commits in `091589089c..c0f92ec98a`, author identities consolidated via `.mailmap` and the merged PR's GitHub login. Bots excluded._ + +| # | Contributor | Commits | +| ---: | --- | ---: | +| 🥇 | diegosouzapw | 323 | +| 🥈 | Bob.Hou (@HouMinXi) | 79 | +| 🥉 | Paco Cartones (@pacocartones) | 77 | +| 4 | Dizzle (@maxmad64bis) | 49 | +| 5 | Koosha Paridehpour (@KooshaPari) | 43 | +| 6 | Nguyen Thanh Dat (@ntdatt812) | 31 | +| 7 | Ravi Tharuma (@RaviTharuma) | 26 | +| 8 | Markus Hartung (@hartmark) | 22 | +| 9 | Webman (@jonlwheat2-gif) | 22 | +| 10 | backryun | 17 | +| 11 | anhtahaylove | 15 | +| 12 | Rafa Martins (@rafacpti23) | 10 | +| 13 | Syed Raheemuddin (@raheemuddin786) | 10 | +| 14 | Nguyễn Viết Tuấn (@TheDemonTuan) | 8 | +| 15 | MumuTW | 7 | +| 16 | Paijo (@oyi77) | 7 | +| 17 | Tobias Andersen (@turbolego) | 6 | +| 18 | Bl0ck (@Bl0ck154) | 5 | +| 19 | SHANMUGAPRIYAN (@geek007git) | 5 | +| 20 | KaspaPulse | 5 | +| 21 | Abhishek Sharma (@abhisheksharma2411) | 4 | +| 22 | Abhishek Divekar (@adivekar-utexas) | 4 | +| 23 | Andrew B. (@AndrianBalanescu) | 4 | +| 24 | NoxzRCW | 4 | +| 25 | opensource-elearning | 4 | ### ✨ New Features +- **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). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- Added Google AI Studio Gemini batch text-to-speech support through `POST /v1/audio/speech`. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- 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. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(routing):** subscription-first auto groupings — `auto/subscription` routes only through plan-included connections with a documented hard-stop overage and fails closed on exhaustion, while `auto/thrifty` orders the pool `subscription → keyless → free → cheap → premium` and steps up one rung at a time as each is exhausted. Billing class comes from a curated per-connection catalog (uncurated is treated as metered, never plan-included), both reuse STRICT_ZERO_COST's per-connection verification, and a quota reading whose `resetAt` has passed is now refreshed regardless of TTL so routing returns to plan capacity as soon as it resets ([#11146](https://github.com/diegosouzapw/OmniRoute/pull/11146)) — thanks @yourspraveen +- **feat(providers):** publish a management-authenticated versioned web-session credential contract from OmniRoute's canonical browser credential metadata ([#11340](https://github.com/diegosouzapw/OmniRoute/pull/11340)) — thanks @Zartharas +- **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)) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add Xquik X search with typed results, credential validation, REST routing, and MCP selection ([#11370](https://github.com/diegosouzapw/OmniRoute/pull/11370)) — thanks @kriptoburak +- **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)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(dashboard):** surface durable exclusive managed leases in the existing Sessions view, keeping leased clients visible across idle gaps while marking connections with in-flight work as active ([#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389)) — thanks @KaspaPulse +- **build(bun):** allow Turbopack bundler flag on Bun 1.4+ with configurable Webpack fallback ([#11471](https://github.com/diegosouzapw/OmniRoute/pull/11471)) — thanks @TheDemonTuan +- feat(api): add an opt-in `modelVisibilityAllowlist`/`modelVisibilityDenylist` settings pair to curate exactly which models `/v1/models` advertises, mirrored into every `auto/*` combo candidate pool so a denied model cannot be routed to via combo selection either (#11481) ([#11997](https://github.com/diegosouzapw/OmniRoute/pull/11997)) +- **feat(rankings):** the Free Provider Rankings page shows what each provider actually served over the last 24 h. It ranked by ELO alone, which left a provider that answers every call with an error in first place; the usage data was already served by the API but never requested. A provider with too small a sample shows a dash, not a number ([#11546](https://github.com/diegosouzapw/OmniRoute/pull/11546)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **feat(guardrails):** enforce a bounded, deterministic contract for Video Bridge transcripts — 256 cues, 4096 input code units and 4 KiB UTF-8 per cue, 64 KiB total text, malformed-Unicode rejection, focus-window scoping, cross-source reconciliation with contributing-source metadata, and a structural provenance trust boundary so caller JSON can never self-assert `embedded`/`audio-bridge` provenance ([#11652](https://github.com/diegosouzapw/OmniRoute/issues/11652)) ([#12009](https://github.com/diegosouzapw/OmniRoute/pull/12009)) +- **feat(video):** orchestrate optional Video Bridge audio extraction and Audio Bridge STT behind a dual opt-in (operator setting AND per-request signal) — a new loopback-only broker `mode=audio` operation shares the frame path's exact process queue, deadline, AbortSignal, and byte budgets to extract a bounded mono 16 kHz PCM WAV from the same already-downloaded video, then reuses the existing Audio Bridge transcription boundary; provider segment timing is preserved when available and marked coarse otherwise, and every failure degrades to a visual-only-safe partial instead of throwing (#11654). ([#12012](https://github.com/diegosouzapw/OmniRoute/pull/12012)) +- Add a tenant-bound Video Bridge drill-down lifecycle on top of the existing secure cache substrate: opaque hashed handles (never raw session/video identifiers), preview/standard/detail multiresolution variants resampled on read, response pagination capped at 8 frames and 32 MiB, and a new authenticated `/api/v1/video-bridge/drilldown` consumer route that stays disabled for remote access by default and denies cross-key access with the same response as a nonexistent handle (no existence oracle). ([#12006](https://github.com/diegosouzapw/OmniRoute/pull/12006)) +- **test(video):** Add the Video Bridge FU-07/FU-09 promotion-evidence harness (#11656) — a frozen Zod manifest schema covering the 8 required scenario kinds (static scenes, rapid cuts, late facts, fades, blur, small text, close events, visual prompt injection) with a minimum of 3 repetitions per case, deterministic declarative fixture recipes (`videoBridgePromotionFixtures.ts`), a pure medians/p95 metrics aggregator, a pure FU-07/FU-09 promotion-verdict evaluator applying the ticket's exact thresholds (missing token usage always holds), a digest-only persistence layer that never retains raw media or raw model responses, and a versioned per-model promotion allowlist shipped empty with every model defaulting to `hold`. The FU-07/FU-09 promotion verdicts themselves remain HOLD — they require a real evidence run against real models on VPS 192.168.0.15. ([#12008](https://github.com/diegosouzapw/OmniRoute/pull/12008)) +- **feat(video bridge):** "embedded" transcript provenance can now be legitimately earned instead of merely asserted — a bounded, allowlisted (`mov_text`/`subrip`/`webvtt`) subtitle probe runs through the loopback-only Video Bridge broker (at most 2 streams, 10s subdeadline bounded by the request deadline, 256 KiB output, 4096-code-unit lines), normalized through a bounded, ReDoS-safe WebVTT parser and Zod-validated end to end. The adapter always resolves to an explicit `success`/`absent`/`transient_failure` outcome — a subtitle failure never breaks the visual description path, and only a fingerprint-verified broker response (never a caller-declared label) can produce embedded cues (#11659). ([#12011](https://github.com/diegosouzapw/OmniRoute/pull/12011)) +- Default new Antigravity-family connections (agy CLI imports and Antigravity OAuth connects) to model auto-sync, so live model discovery lands in the synced catalog and `/v1/models` picks up freshly released upstream models (e.g. Gemini 3.7 Flash tiers) without code changes. Existing connections keep their current setting; the per-connection dashboard toggle remains the opt-out. (#11685 — thanks @MumuTW) ([#11887](https://github.com/diegosouzapw/OmniRoute/pull/11887)) +- **feat(ui):** enable React Compiler (`reactCompiler: true` + `babel-plugin-react-compiler`) for automatic memoization at build time ([#11783](https://github.com/diegosouzapw/OmniRoute/pull/11783)) — thanks @jonlwheat2-gif +- **feat(providers):** add SeekAi (`seekai.cc`) as an OpenAI-compatible New-API gateway — catalog id `seekai` (alias `ska`), `https://seekai.cc/v1`, live `/v1/models` via `passthroughModels`, aggregator-list membership so New-API balance detection can opt in. No referral/aff codes. ([#11786](https://github.com/diegosouzapw/OmniRoute/issues/11786)) ([#12557](https://github.com/diegosouzapw/OmniRoute/pull/12557)) — thanks @HouMinXi +- **feat(zai):** add GLM-5.3-Flash Coding Plan support (1M context, 128K output, vision, `low|high|max` reasoning) and route `zai` GLM-5.3-family API-key traffic through the OpenAI-compatible Coding Plan endpoint with native thinking defaults ([#11801](https://github.com/diegosouzapw/OmniRoute/pull/11801)) — thanks @Neuron-Mr-White +- **feat(combo):** choose how combo models are ordered — manual, provider, score, or name — via a sort control in the dashboard builder, persisted in `config.modelSort` and re-applied on load and after add ([#11812](https://github.com/diegosouzapw/OmniRoute/pull/11812)) — thanks @maxmad64bis +- **feat(free):** custom models can be marked free-tier via `customModels[].isFree`; `isFreeModel()` is the first door and `hidePaidModels` respects it even for providers outside the free budget ([#11843](https://github.com/diegosouzapw/OmniRoute/pull/11843)) — thanks @maxmad64bis +- **feat(nodejs):** add `5dive` as a `configure` target — `omniroute configure 5dive` / `omniroute setup-5dive` write a 5dive auth profile that points an agent fleet's `claude` seats at OmniRoute, with the root-only write, the loopback-vs-`https` endpoint rule and the per-seat model pin handled explicitly ([#11852](https://github.com/diegosouzapw/OmniRoute/pull/11852)) — thanks @5dive-bot +- **feat(sse):** treat `max` as a first-class reasoning-effort tier and clamp per model family (GLM 5.1+/DeepSeek V4+/Kimi K3+ keep native `max`; o1/MiniMax/Grok/Muse Spark clamp to their upstream ceiling) ([#11875](https://github.com/diegosouzapw/OmniRoute/pull/11875)) — thanks @Chewji9875 +- **feat(providers):** the provider plugin manifest now advertises a `usage-fetch` capability for the 40 providers that have a wired usage/quota fetcher, so external dashboards can read it from `GET /api/v1/provider-plugin-manifest` instead of parsing `open-sse/services/usage.ts` after every release. Discovery only — no new fetcher, no quota change, and the Dashboard quota widget stays gated by `USAGE_SUPPORTED_PROVIDERS`. `USAGE_FETCHER_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/fetcherProviders.ts`) and is re-exported from `services/usage.ts`, keeping the manifest module a light leaf instead of pulling the ~490-module usage dispatcher into the manifest route. ([#11903](https://github.com/diegosouzapw/OmniRoute/pull/11903)) — thanks @maxmad64bis / @pacocartones +- **feat(plugins):** `OMNIROUTE_PLUGINS_DIR` sets the directory the runtime plugin scanner reads — and the root the plugin manager installs into — overriding the `HOME`-derived default, so a Docker/K8s deployment can point straight at its bind-mounted plugin tree instead of moving `HOME` just to relocate the scan path. An image that exports no home no longer scans `/tmp/.omniroute/plugins` in silence: the resolved directory is logged once at startup as `scanner.dir_resolved`, naming the input that won. Unset, behaviour is unchanged. Distinct from the CLI-only `OMNIROUTE_PLUGIN_PATH`, which finds `omniroute-cmd-*` command packages and never reached this scanner ([#11906](https://github.com/diegosouzapw/OmniRoute/pull/11906)) — thanks @amaleta / @pacocartones +- **feat(leases):** add an explicit owner-authenticated status action that returns only the active lease's privacy-safe configured connection and provider labels, with generation fencing and no credential or internal-id disclosure ([#11910](https://github.com/diegosouzapw/OmniRoute/pull/11910)) — thanks @KaspaPulse +- **feat(providers):** import-from-file modal shows per-row API errors and ships a downloadable CSV template ([#12071](https://github.com/diegosouzapw/OmniRoute/issues/12071)) ([#12504](https://github.com/diegosouzapw/OmniRoute/pull/12504)) — thanks @HouMinXi +- **feat(providers):** dashboard search matches connection name and `baseUrl` so imported OpenAI-compat nodes surface on the provider card ([#12108](https://github.com/diegosouzapw/OmniRoute/issues/12108)) ([#12495](https://github.com/diegosouzapw/OmniRoute/pull/12495)) — thanks @HouMinXi +- **feat(routing):** add a `score` Auto router strategy that selects the highest configured weighted score and reuses `explorationRate`. ([#12155](https://github.com/diegosouzapw/OmniRoute/pull/12155)) — thanks @jacobsparts +- Add a runtime feature flag to disable universal context handoffs globally without changing the default behavior. ([#12167](https://github.com/diegosouzapw/OmniRoute/pull/12167)) — thanks @jacobsparts +- **feat(providers):** the provider plugin manifest now also advertises a `usage-supported` capability for the 46 providers whose usage API is accepted by the server and Dashboard routes, so integrators can distinguish "the server will serve quota for this provider" from "a fetcher is wired" without reading TypeScript. Discovery only — no fetcher or quota change. `usage-fetch` resolves on id or alias (the usage dispatcher accepts both); `usage-supported` resolves on id alone, matching the runtime guard `USAGE_SUPPORTED_PROVIDERS.includes(providerId)`. `USAGE_SUPPORTED_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/supportedProviders.ts`) and is re-exported from `providers.ts`, mirroring the `fetcherProviders` leaf from #11903 and keeping the manifest a light module. ([#12214](https://github.com/diegosouzapw/OmniRoute/pull/12214)) — thanks @maxmad64bis +- **feat(rankings):** order Free Provider Rankings by what each provider actually served — `GET /api/free-provider-rankings?sortBy=reliability` and a "Most reliable first" toggle on the page. Providers with too few calls to state a success rate keep their score order below the measured ones; the default order is unchanged ([#12218](https://github.com/diegosouzapw/OmniRoute/pull/12218)). — thanks @maxmad64bis +- **perf(sse):** defer `cloneLogPayload()` in the structured SSE collector until after the `maxEvents`/`maxBytes` cap check, eliminating ~9,800 wasted `structuredClone` calls per streaming response (65–71% faster `push()`). Reducer snapshot isolation restored for OpenAI and Responses summaries ([#12241](https://github.com/diegosouzapw/OmniRoute/pull/12241)) — thanks @PauloHSOliveira ([#12243](https://github.com/diegosouzapw/OmniRoute/pull/12243)) +- **feat(usage):** Devin CLI agentic quota (Codeium seat-management GetUserStatus) and OpenRouter key limits plus account credits now surface in Provider Limits ([#12256](https://github.com/diegosouzapw/OmniRoute/pull/12256) — thanks @Neuron-Mr-White) +- **feat(settings):** persist `headroomUrl` through Settings so status/start use the operator URL instead of only `HEADROOM_URL` ([#12306](https://github.com/diegosouzapw/OmniRoute/issues/12306)) ([#12487](https://github.com/diegosouzapw/OmniRoute/pull/12487)) — thanks @HouMinXi +- **feat(auto-combo):** Auto-Combo scoring can now weigh how often a provider/model has actually succeeded. The engine already carried that number on every candidate — 24 hours of usage history behind a ten-sample floor, real-time metrics otherwise — and the scoring function never read it, while the weight table described `stability` as if it did. `reliability` (`1 - failureRate`, with the same field precedence and the same rate-bounding the speed ranking already uses, so a corrupt reading means "nothing observed" rather than "fails every call") is now a declared factor shipping at weight `0`, so routing is unchanged until an operator gives it one, and the `stability` description now matches what that factor computes ([#12317](https://github.com/diegosouzapw/OmniRoute/pull/12317)) — thanks @maxmad64bis +- **feat(routing):** With `freeAccessPolicy: "strict"`, the read-only candidate listing (`GET /v1/auto-combo/{channel}/candidates`) no longer hides the candidates the zero-cost guard excludes — the same read-only transparency the resilience filter already honours (#9133). Each candidate now carries `freeAccessExclusion` saying why it would be kept out, and it tells an exhausted allowance apart from a quota reading that never arrived or went stale, which used to look identical from the outside. Routing is unchanged: the listing reports, it never enforces. The separate `excludeTosAvoid` guard still drops its candidates without a reason; that gap is now documented rather than closed ([#12319](https://github.com/diegosouzapw/OmniRoute/pull/12319)) — thanks @maxmad64bis +- **feat(radar):** The Radar catalog table now shows two facts it was already receiving from the feed and dropping on the floor: the per-model rate limits (requests and tokens, per minute and per day) in a new column, and a badge when a provider's terms state it may train on the prompts you send. A limit of zero renders as zero rather than "rate-only" — for a ceiling those are opposite facts — and a model with no training statement gets no badge, because an absent statement is not a guarantee ([#12320](https://github.com/diegosouzapw/OmniRoute/pull/12320)) — thanks @maxmad64bis +- **feat(radar):** explain Community, single-use, contributor, supporter, recovery, abuse, offers, and privacy rules before either Radar activation action, and remove the superseded fixed-PR grant promise from every UI locale ([#12342](https://github.com/diegosouzapw/OmniRoute/pull/12342)) +- **feat(gamification):** the dashboard Profile page now shows the real daily streak — `/api/gamification/level` returns `streak: { current, longest }` (per key with `apiKeyId`, operator-wide maximum otherwise) and the streak card reads it instead of a hard-coded 0 (#2403) ([#12377](https://github.com/diegosouzapw/OmniRoute/pull/12377)) — thanks @pacocartones +- **feat(gamification):** the dashboard leaderboard now shows each API key's display name under the Name column instead of a truncated key id; `GET /api/gamification/leaderboard` attaches `name` per entry (name only — no key material), while the shared ranking helper and the federation leaderboard stay id-only — thanks @pacocartones ([#12385](https://github.com/diegosouzapw/OmniRoute/pull/12385)) +- **feat(gamification):** enforce the documented 1000 XP/min per-API-key anti-cheat rate limit on the XP award path; over-limit awards are logged and skipped instead of persisted, and the sliding window now matches the timestamp format stored in `xp_audit_log` ([#2403](https://github.com/diegosouzapw/OmniRoute/issues/2403)) ([#12390](https://github.com/diegosouzapw/OmniRoute/pull/12390)) — thanks @pacocartones +- **feat(admin):** localize the gamification anomalies page — the loading state, the Status column and the Suspicious badge now come from the `common` catalog (new `common.suspicious` key propagated to every locale) — add it to the Gamification sidebar group as `gamification-admin` (`/dashboard/gamification/admin`), and expose the loading and empty states as polite `role="status"` live regions (#12401 — thanks @pacocartones) +- **feat(providers):** skip GitHub combo members missing from the live synced catalog, and drop Copilot models that are policy-disabled or hidden from the model picker ([#12473](https://github.com/diegosouzapw/OmniRoute/pull/12473)) — thanks @RaviTharuma +- **feat(i18n):** locale-expansion tooling — `npm run i18n:add-locale` adds a language to config, dashboard, docs mirrors, CLI, README/indexes and site in one command; browser, CLI and cookie detection resolve `uk`, `fil`/`tl`, `zh-Hant` (and the retired legacy `in` → `id`) via config aliases; `config/i18n.json` now ships in the npm package so the published CLI can read it; new real-translation ratio gate (`npm run i18n:check-ratio`, advisory) with a per-locale ratchet baseline; `run-translation --adopt` restores `.i18n-state.json`; `sync-language-bars` generates 🌐 bars from config; `validate_translation.py` loads its allowlist again. Retires the duplicate `in` locale (Indonesian mislabelled as Hindi) — 42 honest locales; saved `NEXT_LOCALE=in` / `OMNIROUTE_LANG=in` keep working. (#12496) +- **feat(api):** Emit gateway-measured `tokens_per_second` (TTFT excluded) on streaming usage and `X-OmniRoute-Tokens-Per-Second` when first-token latency is known ([#12616](https://github.com/diegosouzapw/OmniRoute/issues/12616)) ([#12631](https://github.com/diegosouzapw/OmniRoute/pull/12631)) — thanks @RaviTharuma +- **feat(opencode-plugin): map gateway cost/usage/tok/s onto OpenCode inference payloads** — the official plugin copies `X-OmniRoute-Response-Cost`, token counts, `X-OmniRoute-Tokens-Per-Second` / `usage.tokens_per_second`, TTFT, and the winning `X-OmniRoute-Model` onto the JSON/SSE body OpenCode already consumes. Missing tok/s is left unset (never `tokens / latency`). (#12636) — thanks @RaviTharuma +- **feat(combo):** add combo strategy `quota-weighted`: skip exhausted accounts, then weighted-draw by leftover / in-flight load; existing conversations stay pinned until the account is empty ([#12789](https://github.com/diegosouzapw/OmniRoute/pull/12789)) — thanks @HouMinXi +- **feat(models):** Account live listings become the chat catalog source for Claude, Codex, Copilot, and AGY; public metadata only fills prices on IDs those accounts already list. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **fix(models):** Union `agy` and `antigravity` live catalogs so `agy/gemini-3.8-flash-high` is not rejected after the prefix folds to `antigravity`. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **feat(opencode):** Add `@omniroute/opencode-plugin-v2`, an opencode v2 plugin that gives a v2 install a route to an OmniRoute gateway: it publishes models, combos and auto-combos into the host catalog, refreshes them lazily behind a 300s TTL, and keeps serving the last known catalog from an on-disk snapshot when the gateway is unreachable. The v1 plugin (`@omniroute/opencode-plugin`) is untouched and keeps working as before. The plugin adapts to the host instead of assuming one shape: it reads the shape the host seeds into the catalog draft and publishes the `api` block, the pre-`api` top-level fields, or both. The gateway key comes from the host credential store when one is connected, so no secret needs to sit in a config file; a source the gateway refuses is named in a warning instead of silently degrading the catalog to raw model ids; and tool schemas bound for a Gemini model are stripped of the JSON-Schema keywords Gemini rejects, so tool calling survives that route ([#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870)) — thanks @maxmad64bis +- feat(services): show sanitized CLIProxyAPI account health from its authenticated management API without exposing credentials, file paths, or raw account metadata (#6342) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add AnySearch free web search + URL extract (webFetch) with typed results, credential validation, REST routing, and MCP selection - fallback-only ([#11690](https://github.com/diegosouzapw/OmniRoute/pull/11690)) — thanks @Xxx91n +- **feat(dashboard):** display clamped `[0, 100]%` cached input token ratio in request logs table ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) — thanks @b3nw +- **feat(catalog):** add `OMNIROUTE_DISABLE_THINKING_LEVEL_VARIANTS` feature flag to optionally filter out thinking level variants from model catalog ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) — thanks @b3nw +- **feat(dashboard):** continuously export call logs to external analytics stores. A pluggable destination registry ships the full Logs-tab record set on an hourly `JobRegistry` cron, with a persisted per-destination cursor, batched inserts, a config UI rendered from each destination's own field descriptors, and a REST layer (`/api/log-export/*`) for CRUD, a connection test, and an on-demand run. A destination can opt into `includeBodies` to also ship the request and response payloads shown in the Logs detail pane, including the client and provider views of each call; this is off by default, and payloads inherit the dashboard's PII sanitisation, secret redaction and `noLog` handling. Google BigQuery is the first destination, using a service-account key stored encrypted at rest and streaming inserts keyed by call-log id, into a table that is day-partitioned on `timestamp` and clustered on `api_key_name`, `provider`, `model` and `status`. ([#11945](https://github.com/diegosouzapw/OmniRoute/pull/11945)) — thanks @dpozimski +- **feat(providers):** add MaxAI as a signed, OpenAI-compatible provider serving its 13 paid chat models (GPT-5.6 / Luna / Thinking, Claude 5 Sonnet, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite, Grok 4.1-fast / 4.5, DeepSeek V3.2 / R1, Llama 3.3 70B) through OmniRoute's `/v1` endpoint, with per-request HMAC-SHA1→SM3→AES request signing, live model + context-window discovery from `/models/get_config`, and prompted tool-calling translated to OpenAI `tool_calls` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI vision input — image_url content parts are forwarded inline in `message_content` to the 6 vision-capable models (GPT-5.6 / Luna / Thinking, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite) ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI image generation — 6 image models (gpt-image-1, dall-e-3, flux-1-schnell/dev/pro, sd3-medium) exposed through `POST /v1/images/generations` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI document RAG — inline base64 file/document attachments are uploaded to MaxAI (content-addressed `doc_id`) and attached to the chat via `doc_list` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** browserless MaxAI onboarding — email device-pair login (`/api/providers/[id]/login`) and signed access-token refresh, so a connection can be created and kept fresh without a real browser or Google OAuth ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** per-provider TLS impersonation profile (MaxAI presents a Windows Firefox-150 client fingerprint) so its bot-sensitive endpoints accept OmniRoute traffic ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** add **Nimble** as a web-search and web-fetch provider (`nimble-search`) — `/v1/search` routes to Nimble's search API at `lite` depth (locale, freshness and include/exclude domain filters mapped onto the shared request contract), and `/v1/web/fetch` routes to Nimble Extract, which covers all four fetch formats (`markdown`, `html`, `links`, `screenshot`) from a single call. One API key serves both surfaces. ([#11620](https://github.com/diegosouzapw/OmniRoute/pull/11620)) — thanks @wildcard +- The npm publish is automatic again, through npm Trusted Publishing (OIDC): the hosted `stage-npm` job publishes with a short-lived credential minted from GitHub's id-token — no `NPM_TOKEN`, no 2FA prompt, provenance attached. `publish_mode=staged` (owner approves with 2FA) and `direct` (token) remain available on `workflow_dispatch`. ([#11931](https://github.com/diegosouzapw/OmniRoute/pull/11931)) +- **feat(providers):** Add **Opper** as an API-key gateway provider — EU-hosted AI gateway with 700+ models from 30+ providers behind one OpenAI-compatible API and one key (`OPPER_API_KEY`); model ids use `provider/model` format (e.g. `anthropic/claude-sonnet-4-6`, `openai/gpt-5`); live model catalog at `https://api.opper.ai/v3/compat/models`; entry mirrors `requesty` (same shape, `passthroughModels: true`, no static seed) ([#11629](https://github.com/diegosouzapw/OmniRoute/pull/11629)) +- **feat(dashboard):** the `/dashboard/orchestration` snapshot hook now subscribes to the `agents` WebSocket channel (`agent.task.updated`) instead of `requests` as its refetch trigger, and relaxes its background poll from 5s to 30s while that WS connection is up — falling back to the tighter 5s cadence, reprogrammed live on any connect/disconnect transition, whenever the socket is down. ([#12409](https://github.com/diegosouzapw/OmniRoute/pull/12409)) +- **feat(dashboard):** Orchestration canvas quick wins — search box plus state/source/provider filter chips with a one-click clear, and per-source collapse/expand, all reflected in the URL so a filtered/collapsed view is shareable and survives a refresh; the detail drawer gained a "copy trace JSON" action and hardened error/empty-state and accessibility handling; the Agents-tab edges now animate traveling particles along active (running) connections; and the canvas node/edge status colors moved off fixed hex values onto theme-aware `--orch-status-*` CSS custom properties, so they adapt correctly to light/dark mode. ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- **chore(dashboard):** Orchestration UI hardening pass and the missing component/model test coverage it called for — `OrchestratorNode`/`ActivityNode`/`OverflowNode` rendering, the `?node=`/overflow-click page routing, the Agents-tab orchestrator-click no-op and `showCompleted` toggle, and the overview kanban's done-column sort order (#12270, #12271). ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- New `/dashboard/orchestration` page: live unified view of everything running — Cloud Agent, A2A and Conductor as a real-time graph (Agents tab), the combo cascade (Routing tab, reusing the Combo Live Studio) and a state kanban (Overview tab), with a detail drawer (trace, cost, approve/cancel). Read-only over existing APIs — no new backend. Canvas concept credit: PR #11815 design ([#12261](https://github.com/diegosouzapw/OmniRoute/pull/12261)) +- **feat(dashboard):** new "History" tab on `/dashboard/orchestration` — an Airflow-style grid of finished runs over a 24h/7d/30d preset window, one row per (source, identity), clicking a cell opens the existing detail drawer. It is backed by real persistence: A2A task lifecycle transitions are now written to the `a2a_tasks` table (purged after 30 days, configurable via `OMNIROUTE_A2A_HISTORY_RETENTION_DAYS`) and served by the new `GET /api/a2a/tasks/history` listing endpoint, with the task-detail route falling back to persisted history once a run leaves the in-memory snapshot. Conductor runs stay remote and are not persisted locally — the tab says so instead of silently omitting them. ([#12479](https://github.com/diegosouzapw/OmniRoute/pull/12479)) +- **feat(dashboard):** the orchestration detail drawer gained a "Repeat" action for Cloud Agent, A2A and Conductor tasks — a two-click confirm (click once to arm, click again within the confirm window to fire) re-submits the original prompt/input as a new run. The button is disabled with an explanatory tooltip whenever the original input can't be recovered from the loaded task detail (e.g. it never carried a prompt, or the detail failed to load). Two limitations of the A2A variant, by design: it targets the `/a2a` JSON-RPC endpoint, which authenticates with an API key only (`REQUIRE_API_KEY=true` or a configured `OMNIROUTE_API_KEY` makes a dashboard-session repeat answer `HTTP 400` — surfaced verbatim in the drawer's error line, never as a success), and the `message/send` call is SYNCHRONOUS: the POST blocks for the whole skill run, so the success confirmation only appears once the repeated task finishes. A dashboard-authenticated A2A creation path is deliberately left to a follow-up — widening the endpoint's auth posture is an operator decision, not a side effect of this feature. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(a2a):** A2A task execution now records which memories were consulted for the task's last user message as `metadata.memoryHits` (id/key/type/content-snippet) plus a `memory_hits` history event, purely for observability — the retrieved memory is never injected into a skill's prompt or behavior. Gated by the `OMNIROUTE_A2A_MEMORY_HITS` kill-switch (default enabled; set to `0` to skip the recall lookup entirely). The drawer's new "Memory used" section lists these hits for a2a tasks and is omitted whenever there are none. Known limitation: recall only resolves under the keyless posture — a keyed caller's task owner is a SHA-256 prefix of the API key, while memory rows are keyed by the database api-key id, and no hash→id lookup exists today, so the hit list stays empty for keyed callers. The recorded hits are also kept out of the task's own `input` (and therefore out of the persisted input and of the "Repeat" request body), so repeating a task never re-sends the previous run's memory snippets. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(providers):** add a Perplexity Agent API provider (`perplexity-agent` / `pplx-agent`) for Perplexity `/v1/responses`, including the documented Anthropic, OpenAI, Google, xAI, DeepSeek, Z.AI, Moonshot/Kimi, NVIDIA, and Perplexity model IDs plus Anthropic-model `max_output_tokens` compatibility. ([#12103](https://github.com/diegosouzapw/OmniRoute/pull/12103)) — thanks @AIB1TAL0S +- **feat(providers):** add RPD (Requests Per Day) limit to provider rate limit overrides across UI, schemas, DB, and i18n ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) — thanks @b3nw +- **feat(providers): add UC Direct (uncensored.com Developer API), the metered OpenAI-compatible surface.** A standard OpenAI-compatible passthrough (default executor) for uncensored.com's official REST API at `https://api.uncensored.com/api/v1`: `X-api-key` auth (never-expiring `uai_sk_live_` key), `POST /chat/completions` with streaming SSE and native tool-calling, and the full live metered catalog (82 models across 15 providers, discovered from the public `GET /v1/models`). Registered as provider `uc-direct` (alias `ucd`). Complements the un-metered `uc` persona provider — same models, metered credits and a plain API key instead of a subscription session. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(providers): add UC (uncensored.com), the un-metered subscription "persona" chat as an OpenAI-compatible provider.** A WebSocket web-app port: a durable Clerk credential mints a short-lived session token per connect (browserless — no API key), driving UC's persona socket. Ships the browserless email-code login (request → verify → harvest), the 19 verified persona models (Claude Opus, Gemini, Grok, GLM, Kimi, DeepSeek, MiniMax, incl. the uncensored variants), prompted `` tool-calling with a per-model code-style dialect + auto-cure retry for guardrailed models, live ``/reasoning split, streaming + non-streaming OpenAI responses, and full quota/auth error surfacing (paywall / message-limit / rate-limit → 429, invalid session → 401 re-login). Full multimodal parity via the persona blob-upload layer: **vision** (image input, 15 vision-capable models), **document RAG** (PDF/doc upload, server-side extraction), **image generation** (22 models), **video generation** (14 models, async signed-url → poll), and **TTS** (streaming MP3). Registered as provider `uc` (alias `ucn`). The metered OpenAI-compatible Developer API is a separate `uc-direct` provider. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(logs):** add copy-all button to request detail modal ([#11083](https://github.com/diegosouzapw/OmniRoute/pull/11083)) — thanks @AndrianBalanescu +- **feat(check):** make serviceKinds required and add the reverse-walk provider consistency gate ([#11392](https://github.com/diegosouzapw/OmniRoute/pull/11392)) — thanks @Tushar49 +- **feat(providers):** only advertise verified-working connections by default ([#11448](https://github.com/diegosouzapw/OmniRoute/pull/11448)) — thanks @alltomatos +- **feat(resilience):** add hierarchical concurrency admission ([#11493](https://github.com/diegosouzapw/OmniRoute/pull/11493)) +- **feat(providers):** derive + surface expiry for JWT-bearing web cookies (#11497) ([#11505](https://github.com/diegosouzapw/OmniRoute/pull/11505)) — thanks @oyi77 +- **feat(tunnels):** support named cloudflared tunnels via CLOUDFLARED_CONFIG ([#11561](https://github.com/diegosouzapw/OmniRoute/pull/11561)) — thanks @ggiak +- **feat(compression):** make proactive context-compression threshold a live setting ([#11564](https://github.com/diegosouzapw/OmniRoute/pull/11564)) — thanks @ragnar-claude +- **feat(resilience):** add provider peak-hour protection ([#11622](https://github.com/diegosouzapw/OmniRoute/pull/11622)) — thanks @Neuron-Mr-White +- **feat(providers):** add 1min.ai provider ([#11631](https://github.com/diegosouzapw/OmniRoute/pull/11631)) — thanks @zero-executioner +- **feat(dashboard):** collapsible JSON tree viewer for request/response payloads ([#11703](https://github.com/diegosouzapw/OmniRoute/pull/11703)) — thanks @hartmark +- **feat(routing):** add LiquidAI LFM2.5-2.6B free tier via OpenRouter ([#11752](https://github.com/diegosouzapw/OmniRoute/pull/11752)) — thanks @brick30llc-ctrl +- **feat(pricing):** add GLM-5.3-Flash pricing, model specs, and catalog registration ([#11830](https://github.com/diegosouzapw/OmniRoute/pull/11830)) — thanks @AndrianBalanescu +- **feat(routing):** add relayMode for schema-locked context handoffs ([#11839](https://github.com/diegosouzapw/OmniRoute/pull/11839)) — thanks @raheemuddin786 +- **feat(config):** add support for runtime system prompt configuration and hot-reloading ([#11841](https://github.com/diegosouzapw/OmniRoute/pull/11841)) — thanks @raheemuddin786 +- **feat(routing):** expand connection-aware quota prefilter across combo strategies (#11682) ([#11850](https://github.com/diegosouzapw/OmniRoute/pull/11850)) — thanks @HouMinXi +- **feat(models):** reactive discovery sync after Antigravity model-not-found 404 ([#11888](https://github.com/diegosouzapw/OmniRoute/pull/11888)) — thanks @MumuTW +- **feat(models):** auto-aliases and token limits from synced Antigravity discovery ([#11889](https://github.com/diegosouzapw/OmniRoute/pull/11889)) — thanks @MumuTW +- **feat(settings):** raise sticky round-robin limit caps to 1000 ([#12015](https://github.com/diegosouzapw/OmniRoute/pull/12015)) — thanks @adivekar-utexas +- **feat(resilience):** operator-configurable global credential health check interval ([#12043](https://github.com/diegosouzapw/OmniRoute/pull/12043)) — thanks @adivekar-utexas +- **feat(quota):** use official OpenCode Go usage API ([#12124](https://github.com/diegosouzapw/OmniRoute/pull/12124)) — thanks @ddarkr +- **feat(resilience):** default the credential health check sweep to 60 minutes ([#12138](https://github.com/diegosouzapw/OmniRoute/pull/12138)) — thanks @adivekar-utexas +- **feat(quality):** new-code mode for the complexity and dead-code ratchets (Clean as You Code) ([#12142](https://github.com/diegosouzapw/OmniRoute/pull/12142)) +- **feat(dashboard):** orchestration canvas — unified model + snapshot hook (part 1/2) ([#12156](https://github.com/diegosouzapw/OmniRoute/pull/12156)) +- **feat(usage):** add Kilo Code balance and Kilo Pass quotas ([#12178](https://github.com/diegosouzapw/OmniRoute/pull/12178)) — thanks @killer30001000 +- **feat(providers):** refresh vendored ChatGPT Web connector to v4.0.7 ([#12181](https://github.com/diegosouzapw/OmniRoute/pull/12181)) — thanks @backryun +- **feat(providers):** manual "Clear cooldown" action in the cooling panel ([#12224](https://github.com/diegosouzapw/OmniRoute/pull/12224)) — thanks @patrykkopycinski +- **feat(providers):** restore ChatGPT Web via clean-room browser transport ([#12239](https://github.com/diegosouzapw/OmniRoute/pull/12239)) — thanks @backryun +- **feat(sse):** wire the PROVIDER_PROFILES window gate into the global provider cooldown ([#12247](https://github.com/diegosouzapw/OmniRoute/pull/12247)) +- **feat(providers):** modernize CLOVA Studio chat and embeddings ([#12277](https://github.com/diegosouzapw/OmniRoute/pull/12277)) — thanks @backryun +- **feat(browser):** adopt Obscura as primary headless browser engine with Chromium fallback ([#12286](https://github.com/diegosouzapw/OmniRoute/pull/12286)) — thanks @opensource-elearning +- **feat(sse):** full language parity for output styles (es/de/fr/it/ru/zh + autoDetect) ([#12289](https://github.com/diegosouzapw/OmniRoute/pull/12289)) +- **feat(quality):** complete test:scoped — --full map rebuild, stdin selection, CI loader parity (#8084 D1) ([#12353](https://github.com/diegosouzapw/OmniRoute/pull/12353)) +- **feat(providers):** refresh Fable, Cursor, and Devin catalogs ([#12367](https://github.com/diegosouzapw/OmniRoute/pull/12367)) — thanks @backryun +- **feat(video):** redact transcript text from logs and durable memory (#12150 P1) ([#12427](https://github.com/diegosouzapw/OmniRoute/pull/12427)) +- **feat(dashboard):** parent-link, genuine-continuation badge, and modal perf fixes ([#12448](https://github.com/diegosouzapw/OmniRoute/pull/12448)) — thanks @hartmark +- **feat(dashboard):** adaptive context-budget dial on compression panel ([#12488](https://github.com/diegosouzapw/OmniRoute/pull/12488)) — thanks @HouMinXi +- **feat(providers):** refresh Z.ai Web models and browser transport ([#12524](https://github.com/diegosouzapw/OmniRoute/pull/12524)) — thanks @backryun +- **feat(video):** redact raw client-snapshot transcript fields in the detailed log (#12150 P2) ([#12528](https://github.com/diegosouzapw/OmniRoute/pull/12528)) +- **feat(providers):** refresh NVIDIA hosted models ([#12538](https://github.com/diegosouzapw/OmniRoute/pull/12538)) — thanks @backryun +- **feat(quota):** Moonshot Open Platform balance and TPD lock for custom nodes ([#12590](https://github.com/diegosouzapw/OmniRoute/pull/12590)) — thanks @HouMinXi +- **feat(video):** redact transcript in the in-memory pending-request snapshot (#12430 item 6) ([#12596](https://github.com/diegosouzapw/OmniRoute/pull/12596)) +- **feat(dashboard):** link a log entry's Conversation Context to its owning conversation ([#12646](https://github.com/diegosouzapw/OmniRoute/pull/12646)) — thanks @hartmark +- **feat(catalog):** eligibility-gated free-tier bucket ([#12669](https://github.com/diegosouzapw/OmniRoute/pull/12669)) +- **feat(db):** fail-closed previous_response_id continuation for redacted video turns (#12150 P2b) ([#12707](https://github.com/diegosouzapw/OmniRoute/pull/12707)) +- **feat(gamification): pay the documented `streak_bonus` and `badge_unlock` XP rewards.** `XP_REWARDS` listed both rewards but the award pipeline never paid them: the private reward table in `events.ts` omitted them, `updateStreak()` did not report when a streak extended, and badge unlocks carried no XP. Every request that extends a daily streak now pays `streak_bonus × streak length` once per UTC day (guarded by a same-day `xp_audit_log` check), and every badge unlocked through the pipeline pays `badge_unlock` once per badge (guarded by the `user_badges` primary key; `unlockBadge()` now reports whether it inserted). Bonus XP flows through the same `addXp` + level sync + global/weekly/monthly leaderboard path as action XP, so level-ups and rankings include it. The Radar supporter recognition unlock stays XP-free. (#12522 — thanks @pacocartones) +- **feat(providers):** advertise a `free-tier` capability in the provider plugin manifest for every provider with documented free models, so sidecars and dashboards can filter free-capable providers without reading the quota catalog ([#12786](https://github.com/diegosouzapw/OmniRoute/pull/12786)) — thanks @maxmad64bis +- **feat(providers):** Added EURouter as an OpenAI-compatible API-key gateway (`https://api.eurouter.ai/v1`), with live model discovery via `passthroughModels`. Its copy states that models are served by third-party upstreams listed per model, so an EU-based router is not read as EU data residency for inference. ([#13025](https://github.com/diegosouzapw/OmniRoute/pull/13025)) — thanks @ntdatt812 +- **feat(providers):** Added GreenPT as an OpenAI-compatible API-key provider (`https://api.greenpt.ai/v1`), with live model discovery via `passthroughModels`. No free-inference badge: the published docs describe a free API subscription billed per token, not a free tier. ([#13024](https://github.com/diegosouzapw/OmniRoute/pull/13024)) — thanks @ntdatt812 +- **feat(i18n):** 9 new locales — Greek (`el`), Croatian (`hr`), Serbian (`sr`), Lithuanian (`lt`), Estonian (`et`), Latvian (`lv`), Slovenian (`sl`), Maltese (`mt`), Irish (`ga`) — across the dashboard, docs mirrors, CLI, README and the site; OmniRoute now ships every official EU language (51 locales). (#13044) +- **feat(i18n):** 8 new locales — Kannada (`kn`), Malayalam (`ml`), Odia (`or`), Punjabi (`pa`), Nepali (`ne`), Sinhala (`si`), Burmese (`my`), Khmer (`km`) — across the dashboard, docs mirrors, CLI, README and the site (59 locales). The translator now restores the ICU literal escape around angle placeholders and splits oversized docs sections before translating. (#0000) ([#13660](https://github.com/diegosouzapw/OmniRoute/pull/13660)) +- feat(providers): **list Agnes 3.0 Flash as the current free chat model, drop retired 1.5 Flash, add Image 2.0/2.5 Flash plus Video 2.5/2.5 Flash, and discover the live `/v1/models` catalog (including the CN host `api.agnes-ai.cn`).** `agnes-1.5-flash` now forwards to `agnes-3.0-flash`. Video 2.5 polls `GET /v1/videos/{id}` (not the V2.0 `/agnesapi` contract). Live `/v1/models` (2026-09-09) no longer serves 1.5; the wiki marks it deprecated. 3.0 Flash is 512K context / 65,536 max output, same window as 2.5. CN-region keys use the existing per-connection base-URL field, default stays `apihub.agnes-ai.com`. ([#13120](https://github.com/diegosouzapw/OmniRoute/pull/13120)) — thanks @HouMinXi +- **feat(sse):** Codex and OpenAI catalogs list GPT-6 Astra with effort aliases (`-low` through `-ultra`); Codex CLI identity pins `@openai/codex@0.153.4` in lockstep with the image ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **feat(dashboard):** the orchestration History tab gained a "Compare runs" mode — toggling it turns each grid cell into a 2-item selection queue (a 3rd click drops the oldest pick), and picking two cells opens a side-by-side comparison panel instead of the usual detail drawer. The panel fetches both runs' detail the same way the drawer does (falling back to persisted history once a run leaves the live TTL window) and shows, per side: identity/source/state, start time, a signed `right - left` delta for duration/cost/event count, the event timeline aligned by index, and any memory hits. One side's fetch failing never blocks the other, and a delta is only ever computed when both sides have a finite value — otherwise it renders "—", never `NaN`. Comparing two runs from different sources or skills still works; a banner marks the deltas as informational rather than hiding them, since the two runs aren't a strict apples-to-apples pair. ([#12677](https://github.com/diegosouzapw/OmniRoute/pull/12677)) +- **feat(dashboard):** badge a conversation that never reached a clean stop ([#12717](https://github.com/diegosouzapw/OmniRoute/pull/12717)) — thanks @hartmark +- **feat(release):** reconcile-changelog tool + version-anchored fragment aggregation ([#12987](https://github.com/diegosouzapw/OmniRoute/pull/12987)) +- **feat(mcp):** carry context_length through MCP list_models_catalog projection (#12776) ([#13406](https://github.com/diegosouzapw/OmniRoute/pull/13406)) — thanks @KooshaPari +- **feat(cli):** make startup readiness budget configurable (#13369) ([#13433](https://github.com/diegosouzapw/OmniRoute/pull/13433)) — thanks @KooshaPari + ### 🐛 Bug Fixes +- **security(streaming):** sanitize generic mid-stream error messages before emitting OpenAI, + Responses, or Claude SSE failure frames and before diagnostic logging, while preserving raw + failures for internal classification and keeping client disconnects out of provider failure state. ([#12457](https://github.com/diegosouzapw/OmniRoute/pull/12457)) +- **fix(claude):** `blockExtraUsage: false` no longer lets 5h quota preflight skip the account; extra usage is billed after the session bar is gone, so the request must reach Anthropic ([#12803](https://github.com/diegosouzapw/OmniRoute/pull/12803)) — thanks @HouMinXi +- Sanitize HuggingChat conversation-creation and message-send transport failures before they reach client error bodies or provider logs. ([#12467](https://github.com/diegosouzapw/OmniRoute/pull/12467)) +- **fix(dashboard):** Keep local and theme-aware provider SVG icons at a definite layout size so Chromium does not collapse them to 0×0 after the v3.8.50 image-rendering change ([#12054](https://github.com/diegosouzapw/OmniRoute/pull/12054)) — thanks @ponkcore +- **fix(security):** Sanitize provider and runtime failures before public API, SSE and MCP responses and before persistent request, proxy and usage logs, preventing credentials, stack traces and host filesystem paths from crossing those boundaries while preserving stable error codes and useful diagnostics. ([#12506](https://github.com/diegosouzapw/OmniRoute/pull/12506)) +- **fix(cli):** `omniroute tunnel create` no longer crashes with `Cannot read properties of undefined (reading optsWithGlobals)` — removed the duplicate positional argument that caused Commander.js to misalign the action callback parameters ([#12295](https://github.com/diegosouzapw/OmniRoute/issues/12295)) ([#12368](https://github.com/diegosouzapw/OmniRoute/pull/12368)) — thanks @gonisulaimann +- Stop advertising Gemini Live-only models as supported audio endpoints until OmniRoute proxies the bidirectional Live protocol. ([#11710](https://github.com/diegosouzapw/OmniRoute/pull/11710)) — thanks @RaviTharuma +- **fix(kie):** reroute `flux/kontext` off the KIE Market `createTask` flow — it is catalogued with `isMarket: true` but has no Market catalog page, so KIE rejected it with "model name not supported"; it now hits the dedicated `POST /api/v1/flux/kontext/generate` / `GET /api/v1/flux/kontext/record-info` endpoints instead (#11296). ([#11985](https://github.com/diegosouzapw/OmniRoute/pull/11985)) — thanks @hartmark +- **fix(kie):** correct 12 more KIE Market catalog ids that were sent to `createTask` unchanged but diverge from KIE's documented upstream `model` values — GPT Image 2 T2I/I2I (drops the `gpt/` prefix), GPT Image 1.5 T2I/I2I (`gpt-image/` namespace), Seedream 5.0 Lite T2I/I2I (drops the `.0`), all 4 Flux 2 variants (`flux-2/` namespace, generic variant renamed `flex`), and Wan 2.7 Image / Image Pro (dash instead of dot) — each verified individually against the literal example request published on docs.kie.ai. `#11326`'s "everything else already matches" claim was wrong a second time (#11296); `z-image/4.0-*`/`z-image/4.5-*` and `flux/kontext` remain open, documented as unresolved in `KIE_MARKET_UPSTREAM_MODEL_IDS`'s comment pending further verification. ([#11667](https://github.com/diegosouzapw/OmniRoute/pull/11667)) +- **fix(dashboard):** `useApiKeySave.handleSaveApiKey` no longer forces a full upstream `/models` catalog sync on every non-curated provider connection save — callers can now pass `skipModelSync: true` to opt out, so a workflow that only wants to add one manual model no longer floods the provider's available-models list with hundreds/thousands of synced entries. The flag is a client-side intent signal only and is stripped before the connection payload is POSTed to `/api/providers`; default behavior (full sync on save) is unchanged when the flag is omitted (#11324) ([#11664](https://github.com/diegosouzapw/OmniRoute/pull/11664)) +- **Provider connections:** keep `tokenExpiresAt` when a connection is created. The create-path allowlist omitted it, so every insert stored NULL and the dashboard token badge could read a fresh connection as expired until its first background refresh ([#11368](https://github.com/diegosouzapw/OmniRoute/pull/11368)). — thanks @ntdatt812 +- **fix(combos):** the combo builder's precision-select, global-model-search, and manual-entry flows now serialize a model step's `model` string using the provider's already-computed routing-alias prefix (e.g. `oc/`) instead of rebuilding it from the raw canonical `providerId`, fixing the no-auth "OpenCode Free" provider (`opencode`) being routed to the unrelated paid "OpenCode Zen" provider (`opencode-zen`) because `opencode` doubles as a manual routing-prefix override ([#11433](https://github.com/diegosouzapw/OmniRoute/issues/11433)). ([#11670](https://github.com/diegosouzapw/OmniRoute/pull/11670)) +- **fix(radar):** the catalog feed cache now keeps `generatedAt`, the date the feed's data was built, next to `fetchedAt`, the date this install downloaded it (#11435). The feed schema requires that date and the sync path validates it, but the cache dropped it — so a feed fetched minutes ago and one carrying weeks-old figures looked identical to everything downstream, including the dashboard's "Last fetched" line. `getRadarCatalog().meta` and `GET /api/radar/status` now report both dates, the latter as its own field rather than folded into `version` — and omitted entirely for the offers and intel caches, which keep no build date, where a `null` would read as "unknown" rather than "never stored". The dashboard still shows only the fetch time; surfacing the build date there needs a new translated label and is left to a follow-up. Rows cached before migration 163 read back as `null`: unknown stays unknown instead of borrowing the fetch time. The referrals cache has persisted the same date since migration 142. — thanks @maxmad64bis +- **fix(cli):** postinstall no longer fills `JWT_SECRET` and `API_KEY_SECRET` in the installed package's `.env` (#11436). `.env.example` ships both blank on purpose: the server restores them from its durable store, or generates and persists them there, in `ensureSecrets()`. Pre-filling them defeated that — the file lives inside the package directory, so `npm i -g` replaced it and postinstall wrote _different_ values, while `ensureSecrets()` (which only acts on an empty variable) never got to restore the real ones. Both secrets rotated silently on every update, invalidating dashboard sessions and API-key CRCs. `STORAGE_ENCRYPTION_KEY` left the same list for the same reason in #1622; its comment pointed at a function that no longer exists and now names the real provisioning path. — thanks @maxmad64bis +- **fix(cli):** the CLI now says when a loaded `.env` lives inside the installed package directory (#11437). It already announces every env file it reads, without distinguishing the ones that survive an update from the one that does not: `npm i -g` replaces the package directory wholesale, so values set there are gone at the next update, silently. The warning names the durable path to move them to, and fires only when that file actually supplied a value — a file entirely shadowed by a durable one supplied nothing. A development checkout stays silent: there the same path is stable and documented in `SETUP_GUIDE.md`. — thanks @maxmad64bis +- **fix(free-models):** the shared `isFreeModel()` predicate no longer reports catalog entries marked `freeType: "discontinued"` as free, so `hidePaidModels` can't route to Pollinations' seven premium models that now require a paid key ([#11441](https://github.com/diegosouzapw/OmniRoute/pull/11441)) — thanks @maxmad64bis +- fix(ui): let AnySearch use the normal provider-icon fallback when LobeHub has no matching icon (#11449) +- Fixed the v3.8.50 Costs and Analytics dashboards so flat-rate Claude Code usage can be shown as an explicitly requested token-price estimate without changing default billed-cost semantics. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed archived usage retention so each request is priced individually instead of pricing a day's summed tokens once, which understated archived cost whenever a day mixed cache-heavy and ordinary requests. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed the Costs dashboard so it discloses when displayed figures include flat-rate token-price estimates instead of labelling them as billed spend, using the flag the analytics API already returns; the month-end projection and the CSV/JSON exports carry the same marker, and billed-cost mode is unchanged. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- **fix(combo):** attach the same combo-diagnostics trace (`poolSize`/`attemptOrder`/`excluded`/`terminalReason`, plus `x-omniroute-combo-*` headers) to the round-robin strategy's and the nested pipeline/fusion runtime-unit loop's "Maximum combo retry limit reached" 503 that the priority-strategy path already attaches for the identical terminal condition — previously those two paths returned a bare, contextless 503 ([#11462](https://github.com/diegosouzapw/OmniRoute/issues/11462)). ([#11665](https://github.com/diegosouzapw/OmniRoute/pull/11665)) +- **fix(bun):** use native `bun:sqlite` in `bootstrap-env` and `sync-env` to avoid loading `better-sqlite3` N-API addon during Bun startup ([#11468](https://github.com/diegosouzapw/OmniRoute/pull/11468)) — thanks @TheDemonTuan +- **fix(docker-bun):** make `Dockerfile.bun` install reliable with pre-install script helpers and native `bun:sqlite` smoke check ([#11470](https://github.com/diegosouzapw/OmniRoute/pull/11470)) — thanks @TheDemonTuan +- **fix(docker-bun):** remove every vendored `better-sqlite3` native addon from the Bun runtime image so startup cannot abort before the `bun:sqlite` fallback ([#11482](https://github.com/diegosouzapw/OmniRoute/pull/11482)) — thanks @TheDemonTuan +- **fix(autoCombo):** effort/alias model variants (`gpt-5.6-sol-xhigh`, `gpt-5.6`, cursor's `claude-4.6-opus-high`) inherit their base model's task fitness instead of falling to the wildcard 0.5 ([#11492](https://github.com/diegosouzapw/OmniRoute/pull/11492)) — thanks @MumuTW +- fix(db): rate-limit repeated Arena ELO leaderboard fetch-failure warnings instead of logging one per sync attempt (#11500) ([#11989](https://github.com/diegosouzapw/OmniRoute/pull/11989)) — thanks @hartmark +- **fix(db):** dedupe the raw `[Encryption] Decryption failed...` log line emitted by the lazy-decrypt views (`createLazyRowProxy`/`createLazyConnectionView`), which power `getProviderConnections()` and were re-triggering that line on every CredentialHealth/model-sync cycle for the same corrupt or stale-key credential — a fresh Proxy over a fresh row on every cycle meant the per-proxy memoization never suppressed it, unlike the dedup `decryptConnectionFields()` already had since [#9927](https://github.com/diegosouzapw/OmniRoute/issues/9927). Now shares that dedupe tracking so the line logs at most once per credential ([#11500](https://github.com/diegosouzapw/OmniRoute/issues/11500)). ([#11678](https://github.com/diegosouzapw/OmniRoute/pull/11678)) +- **fix(intelligence):** Arena ELO sync stores a synthesized base-model row for every effort/harness variant (`claude-opus-5-max` → `claude-opus-5`, `gpt-5.6-sol-xhigh (codex-harness)` → `gpt-5.6-sol`), so bare model requests reach the synced intelligence layer instead of the static fallback table; the hand-maintained `MODEL_ALIAS_MAP` — which collapsed generations (`gpt-5.5` → `gpt-5`) — is gone ([#11506](https://github.com/diegosouzapw/OmniRoute/pull/11506)) — thanks @MumuTW +- **fix(autoCombo,sse):** catalog hygiene — the static task-fitness table no longer ranks retired models above live ones (dead rows dropped, patterns matched on segment boundaries), the 7 deprecation aliases that forwarded to retired or non-existent models now point at the vendors' published replacements, an alias is no longer rewritten when the serving provider offers the id as-is, and a new `check:model-lifecycle` gate diffs both tables against a vendor lifecycle snapshot ([#11507](https://github.com/diegosouzapw/OmniRoute/pull/11507)) — thanks @MumuTW +- **fix(sse):** stop dropping resolved `thoughtSignature` values on parallel (multi tool-call) turns sent to Gemini 3.x — the claude→gemini and openai→gemini translators previously kept the signature only on the *first* function call of a message, causing Gemini to reject subsequent calls in the same turn with HTTP 400 "Function call is missing a thought_signature"; each function call now keeps its own resolved signature ([#11510](https://github.com/diegosouzapw/OmniRoute/issues/11510)). ([#11669](https://github.com/diegosouzapw/OmniRoute/pull/11669)) +- Treat Docker publishing from a non-default release branch as an expected skip instead of a failed workflow, while preserving the guard that prevents it from overwriting the `next` channel. ([#11527](https://github.com/diegosouzapw/OmniRoute/pull/11527)) — thanks @pacocartones +- fix(search): prefer credentialed providers over duckduckgo-free fallback (#11524) — the fallback-only loop ran before the credentialed-providers loop in `executeWebSearch`, making configured providers unreachable when `duckduckgo-free` was available. ([#11565](https://github.com/diegosouzapw/OmniRoute/pull/11565)) — thanks @vsd2807 +- **fix(sse):** cap the upstream headers-wait phase for STREAMING requests to a client-realistic ceiling (110s, under Codex's own ~120s hard client-abort window) instead of the flat 10-minute `FETCH_TIMEOUT_MS` default — that default was 5x longer than the body-phase readiness watchdog's own adaptive bound, so a request whose upstream never returned any response at all (not even headers, e.g. a stalled NVIDIA target behind a tool-heavy Responses→Chat translation) kept the client connection alive on keepalives only, guaranteeing the client's own patience ran out first with an opaque 499 instead of OmniRoute detecting and failing the stall fast. Non-streaming requests are unaffected — they keep the existing flat default (`open-sse/utils/fetchStartTimeoutPolicy.ts`) (#11526) ([#11668](https://github.com/diegosouzapw/OmniRoute/pull/11668)) +- **fix(ci):** protocol-clients E2E harness boots the peer-stamped custom server (`run-next.mjs dev`) instead of the bare `next` CLI, so `LOCAL_ONLY` locality resolves from the real TCP peer stamp and `/api/mcp/audit` answers 200 (open bootstrap preserved via an `OMNIROUTE_E2E_BOOTSTRAP_MODE=open` credential clear in `run-next.mjs`, pinned to loopback `HOST`) instead of a deterministic 403 (#11535) ([#11549](https://github.com/diegosouzapw/OmniRoute/pull/11549)) — thanks @jonlwheat2-gif +- **fix(docs):** the Auto-Combo engine is described with the number of factors it actually declares. Four different counts were in circulation — 6, 9, 13 and 14 across `AGENTS.md`, four guides and six code comments — while `DEFAULT_WEIGHTS` declares 15. `check:docs-counts` now reads that number from the source, so adding a factor without updating the prose fails CI ([#11545](https://github.com/diegosouzapw/OmniRoute/pull/11545)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **fix(quota-share):** in-flight leases are per request, so an aborted request's slot ages out instead of being kept alive by later traffic on the same connection ([#11547](https://github.com/diegosouzapw/OmniRoute/pull/11547)) — thanks @abhisheksharma2411 +- The free-tier summary route now serves the refreshed Radar catalog when the feed is active — the same numbers the dashboard shows — and states which catalog answered plus its real build date, instead of always reporting release-frozen figures with a stale curation date. The supporter-key live feed stays reserved to authenticated callers of the instance. ([#11550](https://github.com/diegosouzapw/OmniRoute/pull/11550)) — thanks @maxmad64bis +- **sse:** fix LiveWS/embed-WS servers crashing at startup under the Node/tsx runtime — `liveServer.ts`, `embedWsProxy.ts` and `apiBridgeServer.ts` imported `@/shared/utils/httpClientAbortGuard` without the `.mjs` extension, so the client-abort crash guard added by [#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556) was unreachable and every dependent test failed with `ERR_MODULE_NOT_FOUND` ([#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556)). ([#11671](https://github.com/diegosouzapw/OmniRoute/pull/11671)) — thanks @jonlwheat2-gif / @yxyxy +- fix(chat): shed any of the matched provider-node's routing identifiers (public prefix or internal id) before model lookup, so composites like `//` no longer reach upstream verbatim (#11557) — thanks @maxmad64bis +- **fix(cache):** a semantic-cache entry now expires on its TTL instead of surviving until the next UTC midnight, and `dbEntries` stops counting expired rows ([#11573](https://github.com/diegosouzapw/OmniRoute/pull/11573)) — thanks @ntdatt812 +- **fix(api):** `/v1/models` schedules its stale-while-revalidate rebuild through Next's `after()` again, so a stale catalog reaches the client before the rebuild blocks the event loop ([#11574](https://github.com/diegosouzapw/OmniRoute/pull/11574)) — thanks @ntdatt812 +- **fix(check):** the `known-symbols` executor conformance gate awaits the now-async `getExecutor()`, so it stops reporting all 142 registered aliases as dead symbols and can detect a lazy import that fails ([#11582](https://github.com/diegosouzapw/OmniRoute/pull/11582)) — thanks @ntdatt812 +- **fix(pack):** register `bin/cli/utils/volatileEnvPath.mjs` in `PACK_ARTIFACT_REQUIRED_PATHS`, so the prune can no longer silently drop a module the published CLI imports on every boot ([#11588](https://github.com/diegosouzapw/OmniRoute/pull/11588)) — thanks @ntdatt812 +- **i18n(vi):** translate the five reliability-column strings on the free-provider rankings page, which rendered as `__MISSING__:` markers in the Vietnamese UI ([#11589](https://github.com/diegosouzapw/OmniRoute/pull/11589)) — thanks @ntdatt812 +- **resilience:** restore the expired-connection retry-budget probe in the token-health sweep — the `!isGitHubAccessTokenOnlyConnection` carve-out reintroduced by #11608 contradicted the boundary pinned by #11592, so a GitHub connection parked at `expired` with retry budget remaining was never probed and could never self-heal ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)). ([#11672](https://github.com/diegosouzapw/OmniRoute/pull/11672)) — thanks @jonlwheat2-gif / @ntdatt812 +- **fix(dashboard):** Provider Stats now displays combo success rates on their native 0–100 percentage scale instead of multiplying them twice ([#11595](https://github.com/diegosouzapw/OmniRoute/pull/11595)) — thanks @pacocartones +- **fix(providers):** route DuckDuckGo Web Chat through the verified `duck.ai` endpoint, refresh its free model catalog, and avoid forwarding unsolved anti-abuse challenges ([#11597](https://github.com/diegosouzapw/OmniRoute/pull/11597)) — thanks @solstxce +- **fix(ui):** Console log Refresh and Copy controls now expose localized accessible names, keep copy actions visible on keyboard focus, and announce copy completion safely ([#11599](https://github.com/diegosouzapw/OmniRoute/pull/11599)) — thanks @pacocartones +- Dropped the stale-`.eslintcache` `restore-keys` fallback from both "Restore ESLint file cache" steps in `ci.yml`, so the blocking `Lint` job can no longer be served per-file verdicts computed under a different lint config, suppressions file or lockfile. `quality.yml` had already dropped it in #11963; `ci.yml` — the workflow that actually gates PRs — had not (#11600). ([#11996](https://github.com/diegosouzapw/OmniRoute/pull/11996)) +- fix(ci): pass `--pass-on-unpruned-suppressions` in `run-eslint-json.mjs` so the CI Lint job no longer fails when a suppression is merely orphaned by a genuine fix, mirroring the identical fix already in `validate-release-green.mjs` (#11600) ([#11983](https://github.com/diegosouzapw/OmniRoute/pull/11983)) — thanks @hartmark +- fix(handoff): enforce provider allowlist for universal handoff (#11602) — universal handoff now skips summarization when the selected summary model's provider is not included in the configured provider allowlist. ([#11619](https://github.com/diegosouzapw/OmniRoute/pull/11619)) — thanks @vsd2807 +- **fix(dashboard):** Show a stable error state when Search Analytics returns an HTTP error or malformed data ([#11603](https://github.com/diegosouzapw/OmniRoute/pull/11603)) — thanks @pacocartones +- **test(gamification):** pin the aggregate profile level to the XP-derived semantics of #11604 — `getAggregateXp()` now derives `currentLevel` from the summed XP (`calculateLevel(sum)`), not `MAX(stored current_level)`, and the #3484 fixture levels are aligned with the XP curve ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)). ([#11676](https://github.com/diegosouzapw/OmniRoute/pull/11676)) — thanks @jonlwheat2-gif / @pacocartones +- **fix(dashboard):** Keep the Profile level and progress aligned with aggregate XP, including bounded handling for invalid totals ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)) — thanks @pacocartones +- **fix(dashboard):** Prevent locked hidden badges from revealing their icon or opening private badge details before they are earned ([#11605](https://github.com/diegosouzapw/OmniRoute/pull/11605)) — thanks @pacocartones +- **fix(dashboard):** Restore keyboard focus after shared modals close and cancel delayed autofocus during cleanup ([#11607](https://github.com/diegosouzapw/OmniRoute/pull/11607)) — thanks @pacocartones +- **fix(dashboard):** Enable Enter and Space activation for clickable data-table rows without hijacking nested controls ([#11610](https://github.com/diegosouzapw/OmniRoute/pull/11610)) — thanks @pacocartones +- **fix(autoCombo,sse):** vendor-retired catalog ids are dropped from the auto-combo candidate pool and no longer win on leftover `arena_elo` / `user_override` rows; `getModelLifecycleDecision` consults `model-lifecycle.json` (prefix-stripped) so aggregator traffic is not `untracked` for ids the snapshot already knows ([#11625](https://github.com/diegosouzapw/OmniRoute/issues/11625)) ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)) — thanks @MumuTW +- **test(chatcore):** move Codex/Claude combo fixtures off models the lifecycle guard now rejects — `gpt-5.1-codex`/`gpt-5-codex` are vendor-retired (snapshot, #11626) and `claude-3-5-sonnet-20241022` is shut down, so native-passthrough and combo-fallback tests switched to `gpt-5.6-sol` and `claude-sonnet-4.6` ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)). ([#11675](https://github.com/diegosouzapw/OmniRoute/pull/11675)) — thanks @jonlwheat2-gif / @MumuTW +- fix(opencode-plugin): stop a free-tier budget that rounds up across a magnitude from rendering as `1000K`/`1000M` in the model picker — `fmtTokens` chose its unit from the raw token count and then rounded with `toFixed(1)`, so 999,950–999,999 printed as `1000K` rather than `1M` and just under a billion printed as `1000M` rather than `1B` ([#11684](https://github.com/diegosouzapw/OmniRoute/pull/11684)) — thanks @abhisheksharma2411 +- Cap prose-derived Antigravity quota resets at the configured model cooldown maximum while preserving authoritative Retry-After headers and Google RetryInfo hints. (#11695) Thanks @pacocartones. ([#11823](https://github.com/diegosouzapw/OmniRoute/pull/11823)) +- Compare `_rebaseline_*` notes structurally in the ratchet-bank verifier so an object-valued note no longer reports itself as rewritten on an unmodified tree — the nightly `bank-ratchet-shrinks` lane had been aborting on every run since 2026-08-11, leaving 41,672 lines of earned file-size shrinks unbanked (#11702). ([#11892](https://github.com/diegosouzapw/OmniRoute/pull/11892)) — thanks @MumuTW +- **fix(build):** `prepublish.ts` bundles the ChatGPT Web (Codex) MCP bridge through `runBuildTool()` instead of spawning `npx.cmd` raw, fixing the build crash on Node ≥ 20/Windows where `.cmd` shims cannot be spawned without a shell (EINVAL) ([#11704](https://github.com/diegosouzapw/OmniRoute/issues/11704)) ([#11706](https://github.com/diegosouzapw/OmniRoute/pull/11706)) — thanks @jonlwheat2-gif +- fix(codex): keep `parallel_tool_calls:false` on the translated Codex Responses Lite path (#11707) ([#11984](https://github.com/diegosouzapw/OmniRoute/pull/11984)) — thanks @hartmark +- **fix(tests):** the new Gemini Web Images retirement test (#11708) called the now-async `getExecutor()` synchronously, throwing `TypeError: getExecutor(...).getProvider is not a function` instead of asserting Gemini Web chat stays available. Awaited to match the R0.3 executor-registry refactor (#11220). ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(search):** Allow REST and MCP web-fetch requests to select the advertised `anysearch-search` provider explicitly ([#11716](https://github.com/diegosouzapw/OmniRoute/pull/11716)) — thanks @pacocartones +- **fix(providers):** Keep overnight peak-hour protection active after midnight according to the window's configured start day ([#11718](https://github.com/diegosouzapw/OmniRoute/pull/11718)) — thanks @pacocartones +- **fix(providers):** correct all five g4f.space gateway descriptions to document proof-of-work credits or member API keys, variable limits, and the remote prompt boundary instead of promising free keyless access at five requests per minute ([#11738](https://github.com/diegosouzapw/OmniRoute/pull/11738)). +- **fix(db):** `model_capabilities` is created by a migration instead of lazily on the first models.dev sync, so a clean install and an upgraded install converge on the same schema regardless of which features have run ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(ci):** `check:install-upgrade` now fails on an `npm` install truncated by ENOSPC (npm reports it as a warning and still exits 0), authenticates its health probe so the version assertion works against the hardened health payload, frees the clean-install tree before the upgrade phase, and no longer reports a schema divergence computed from a boot that never served ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(routing):** harden session-affinity key derivation against oversized explicit IDs and empty structural-payload collisions, while recognizing Gemini and common text request shapes ([#11746](https://github.com/diegosouzapw/OmniRoute/pull/11746)) — thanks @MumuTW +- **fix(packages/browser-pool):** regenerate `package-lock.json` so the `packages/browser-pool` workspace's locked `playwright`/`@types/node` (and transitives) match its `package.json` specs, fixing cache-only/offline installs (`npm ci --offline`, Nix `buildNpmPackage`) that previously failed with `ENOTCACHED` ([#11747](https://github.com/diegosouzapw/OmniRoute/issues/11747)) — thanks @benjaminkitt ([#11784](https://github.com/diegosouzapw/OmniRoute/pull/11784)) — thanks @geekyNads +- **fix(assets):** Stop distributing 79 provider and CLI logos without verified source/license provenance, keep every provider/tool available through the internal generic icon, and prevent those IDs from falling through to an external icon CDN. ([#11750](https://github.com/diegosouzapw/OmniRoute/pull/11750)) +- **fix(providers):** retire the common `chatgpt-web` / `cgpt-web` provider because its pre-key and proof-of-work provenance could not be cleared ([#11754](https://github.com/diegosouzapw/OmniRoute/pull/11754)) — explicit chat and image requests, slash-prefixed models, persisted aliases, proxy overrides, and combo targets now fail closed with a sanitized HTTP `410` / `PROVIDER_RETIRED` before upstream dispatch. Migration 163 tombstones matching connections and invalidates their active leases without deleting history or widening API-key allowlists. The independent `chatgpt-web-codex` / `cgpt-codex` provider and its MIT-noticed vendored implementation remain available. +- **fix(usage):** quota and usage refresh no longer 409 when an exclusive lease reserves the connection ([#11758](https://github.com/diegosouzapw/OmniRoute/pull/11758)) — thanks @TheDemonTuan +- Keep the embedding registry's vector width and `embedding` type on models when a synced model exists for the same id, so `/v1/models` no longer reports registry-described embedding models widthless or untyped (#11761) — thanks @rahilmavani +- Correct `google/gemini-embedding-001` on the OpenRouter route to 3072 dimensions, the width it returns when `dimensions` is not sent (#11761) — thanks @rahilmavani +- **fix(vision-bridge):** Restrict automatic vision routing and fallbacks to models available in authoritative live provider catalogs, including aliases and effort variants ([#11767](https://github.com/diegosouzapw/OmniRoute/issues/11767)) — thanks @pacocartones ([#11813](https://github.com/diegosouzapw/OmniRoute/pull/11813)) +- **fix(providers):** reclassify Cerebras as a one-time $5 signup credit (payment method required, 30-day validity), not a recurring no-card 1M tokens/day trial ([#11773](https://github.com/diegosouzapw/OmniRoute/issues/11773)) ([#12591](https://github.com/diegosouzapw/OmniRoute/pull/12591)) — thanks @HouMinXi +- fix(cli): stop prepublish from re-rebuilding the already-built ESM-only opencode-plugin dist (#11787) ([#11990](https://github.com/diegosouzapw/OmniRoute/pull/11990)) — thanks @hartmark +- **fix(db):** Fresh installs no longer log a non-fatal `no such table: compression_run_telemetry` warning when retention cleanup runs before the lazily-created telemetry table exists ([#11802](https://github.com/diegosouzapw/OmniRoute/pull/11802)) — thanks @RaviTharuma +- fix(sse): set X-OmniRoute-Selected-Connection-Id on successful combo dispatches so downstream consumers stop falling back to an empty connection id (#11810) ([#11986](https://github.com/diegosouzapw/OmniRoute/pull/11986)) — thanks @hartmark +- **fix(services):** embedded CLIProxyAPI lifecycle checks now use public `/healthz`, while model discovery uses the configured dedicated data-plane API key instead of the management password ([#11811](https://github.com/diegosouzapw/OmniRoute/pull/11811)) — thanks @RaviTharuma +- Fix persisted model steps returning an error in the playground route simulator, and warn when structural steps cannot be simulated. ([#11862](https://github.com/diegosouzapw/OmniRoute/pull/11862)) — thanks @pacocartones +- **fix(providers):** Antigravity's dynamic mitmAlias table no longer routes `gemini-3.7-flash-{high,medium,low}` to a literal tier-suffixed upstream id just because one connected account's own discovery listed it directly — those display ids always resolve through the safe `gemini-3.7-flash-tiered` static alias, so one account's Google-provisioned access no longer 404s every sibling account of the provider ([#11824](https://github.com/diegosouzapw/OmniRoute/issues/11824), [#11651](https://github.com/diegosouzapw/OmniRoute/issues/11651)) ([#11988](https://github.com/diegosouzapw/OmniRoute/pull/11988)) — thanks @hartmark +- **fix(dashboard):** Expose Profile loading, errors, page structure, and XP progress to assistive technologies ([#11838](https://github.com/diegosouzapw/OmniRoute/pull/11838)) — thanks @pacocartones +- **fix(socks):** `Agent.connectTimeout` now bounds both SOCKS handshake and TLS connect, and `family === null` no longer falls back to `fetch-socks` ([#11842](https://github.com/diegosouzapw/OmniRoute/pull/11842)) — thanks @maxmad64bis +- **perf(compression):** OOM mitigations for large payload hashing, memoization, and token estimation ([#11844](https://github.com/diegosouzapw/OmniRoute/pull/11844) — thanks @AndrianBalanescu) +- Excluded Next.js Node File Trace manifests (`*.nft.json`) from the published npm tarball. They are build-time metadata and are never read while serving, but had grown to 668.7 MB — 61% of the package — which pushed the upload past the registry limit and made `npm publish` fail with `413 Payload Too Large`. ([#11864](https://github.com/diegosouzapw/OmniRoute/pull/11864)) +- **fix(cli):** keep the global OmniRoute server `--base-url` separate from provider-node `--endpoint` values in add, update, and validate commands ([#11860](https://github.com/diegosouzapw/OmniRoute/pull/11860)) — thanks @pacocartones +- **fix(config):** Nous Research's `Hermes-4-405B` model now displays as "Hermes 4 405B (Nous Research)" in both the provider registry and the free-model catalog, instead of the mislabelled "Hermes 4 7B" ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#11993](https://github.com/diegosouzapw/OmniRoute/pull/11993)) +- **fix(provider/nous):** inject required user= tag into Nous Research inference requests to resolve upstream 400 "missing tags" error ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#12044](https://github.com/diegosouzapw/OmniRoute/pull/12044)) +- **fix(build):** `npm run build` now fails in one second with a named package and a copy-pasteable fix when npm silently drops an externalised optional native dependency, instead of dying four minutes in with `Module not found: Can't resolve 'better-sqlite3'` ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- **fix(install):** `postinstall` no longer throws `ReferenceError: isAndroid is not defined` — failing the whole `npm install` — when the `better-sqlite3` rebuild fallback times out; the manual-fix guidance is reachable again ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- Fixed the Alibaba free-tier allowlist test that went red on its own once the shipped catalog's `validUntil` (2026-08-27) passed, leaving every PR and `main` with a failing `Unit Tests (1/8)`. The test now builds its own packs with dates it controls, and covers the expired-pack fallback that production has actually been serving. ([#11867](https://github.com/diegosouzapw/OmniRoute/pull/11867)) +- Split the npm registry upload into its own GitHub-hosted job. npm refuses `--provenance` from a self-hosted runner (`422 ... Only "github-hosted" runners are supported`), which blocked the v3.8.50 publish; the heavy verification cannot move to a hosted runner, so it now hands the proven tarball over instead. ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(ui):** the shared `DataTable` loading state no longer reads its decorative ⏳ glyph out to assistive technology, and now carries the same `role="status"` / `aria-live="polite"` / `aria-busy="true"` semantics as `PageLoading` ([#11869](https://github.com/diegosouzapw/OmniRoute/pull/11869)) — thanks @pacocartones +- **fix(api):** Bound the `timeout` accepted by `POST /api/providers/[id]/login` to the same 15s–600s window the Adobe Firefly and Conol browser-login paths already enforce, so a request can no longer pin a headful browser and the single active-login slot with an arbitrarily large poll budget. ([#11871](https://github.com/diegosouzapw/OmniRoute/pull/11871)) — thanks @pacocartones +- **fix(api):** Stop the tunnel and MITM routes from returning raw child-process error text, which disclosed host paths, binary install locations and Tailscale `tskey-*` credentials in a body some of these routes serve to non-loopback callers, and return a real 400 instead of a framework 500 when a tunnel request body fails validation. ([#11872](https://github.com/diegosouzapw/OmniRoute/pull/11872)) — thanks @pacocartones +- **fix(open-sse):** stream TTFT/ITL are sampled from a monotonic clock (`performance.now`) instead of `Date.now`, so an NTP correction or wall-clock jump can no longer inflate a genuine TTFT or produce a negative one that the `ttft >= 0` guard silently drops — protecting the router's OTel/EWMA/`usage_history`/speed-ranking signals ([#11873](https://github.com/diegosouzapw/OmniRoute/pull/11873)) — thanks @pacocartones +- **fix(providers):** xAI `reasoning_effort: "xhigh"` now reaches grok-4.6+ instead of being silently clamped to `"high"` ([#11879](https://github.com/diegosouzapw/OmniRoute/pull/11879)) — thanks @NoxzRCW +- **fix(dashboard):** the providers page no longer crashes into the error boundary when a provider id collides with an `Object.prototype` member (`constructor`, `__proto__`); icon lookups are own-property guarded ([#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)) — thanks @NoxzRCW +- **fix(skills):** injected skill tools declared in shorthand (`{"content": "string"}`) now forward valid JSON Schema, unblocking providers that validate tool schemas strictly such as Zhipu GLM on the Console Go tier ([#11881](https://github.com/diegosouzapw/OmniRoute/pull/11881)) — thanks @NoxzRCW +- **fix(api):** `POST /api/playground/simulate-route` now surfaces `combo-ref` and `provider-wildcard` persisted combo steps with a specific warning (naming the referenced combo, or the unresolved `provider/model` wildcard) instead of folding them into a generic "unsupported step" count; a `provider-wildcard` step is also now included as an unresolved target so the operator can see it is in the route (ported from [#11882](https://github.com/diegosouzapw/OmniRoute/pull/11882) — thanks @NoxzRCW). ([#11930](https://github.com/diegosouzapw/OmniRoute/pull/11930)) +- **fix(translator):** the streaming OpenAI→Claude translator keeps upstream usage, including prompt-cache tokens, when it arrives on a trailing `choices: []` chunk (Fireworks and any upstream using `stream_options.include_usage`) ([#11883](https://github.com/diegosouzapw/OmniRoute/pull/11883)) — thanks @NoxzRCW +- **fix(cli):** `omniroute update --apply` now tells you explicitly whether a running server was detected and, if so, that you must run `omniroute restart` to apply the update — it never restarted anything and previously implied the update was already live once files were installed. The dashboard's npm-mode Update flow (`/api/system/version`) now tries OmniRoute's own PID-file-managed supervisor before falling back to pm2, and reports an honest "restart required" step instead of a silent pm2-only "skipped" that read like a completed update. The server-side latest-version lookup backing the dashboard's update banner also gained `--prefer-online`, closing the same stale-npm-cache class already fixed in the CLI's own copy for #4376 ([#11885](https://github.com/diegosouzapw/OmniRoute/issues/11885)). ([#12005](https://github.com/diegosouzapw/OmniRoute/pull/12005)) +- **fix(resilience):** clear persisted LKGP pins when a target suffers connection/provider exhaustion or is skipped before dispatch due to cooldown/exhaustion/unavailability, preventing subsequent requests from repeatedly prioritizing known-dead providers ([#11911](https://github.com/diegosouzapw/OmniRoute/issues/11911)). ([#12013](https://github.com/diegosouzapw/OmniRoute/pull/12013)) — thanks @HouMinXi +- **fix(sse):** the OpenAI→Claude stream translator now defers the terminal `message_delta`/`message_stop` emission until the real usage block has arrived (or a genuine end-of-stream flush forces it) instead of emitting it immediately on `finish_reason` — previously, when the trailing usage-only chunk (`{"choices":[],"usage":{...}}`) arrived *after* the `finish_reason` chunk (the normal order for Fireworks/vLLM/Together and other `stream_options.include_usage` upstreams), the client-visible `message_delta` still carried stale/zero usage even though `state.usage` was internally corrected too late to matter (ported from [#11915](https://github.com/diegosouzapw/OmniRoute/pull/11915) — thanks @HouMinXi). ([#11933](https://github.com/diegosouzapw/OmniRoute/pull/11933)) +- **fix(sse):** `stripStore()` now forces `store=false` for stateless OpenAI-compatible Responses-API targets (unless the connection has explicitly opted in via `providerSpecificData.openaiStoreEnabled`), instead of only handling the `openai`/`agentrouter` cases — a client-supplied `store` value previously passed through untouched to backends that don't actually persist responses server-side ([#11916](https://github.com/diegosouzapw/OmniRoute/pull/11916) — thanks @HouMinXi). +- **fix(models):** custom provider-node models (synced, custom, and alias-backed) now appear under their configured prefix in the unified catalog when the operator's model-id prefix mode is canonical, instead of being dropped whenever alias-inclusion was otherwise disabled ([#11918](https://github.com/diegosouzapw/OmniRoute/pull/11918) — thanks @HouMinXi). +- **fix(models):** the unified model catalog now suppresses stale static registry models (including effort-tier variants) for any provider whose active connection has an authoritative live synced catalog, not only providers already using exclusive-synced-listing — a connection with `providerUsesAuthoritativeLiveCatalog` previously kept serving both the live-synced models and the stale static rows side by side ([#11919](https://github.com/diegosouzapw/OmniRoute/pull/11919) — thanks @HouMinXi). +- **fix(ui):** `ProviderIcon`'s three remaining unguarded lookups (`PROVIDER_ICON_ALIASES`, `LOCAL_SVG_ALIASES`, `THEMED_SVGS`) now use `Object.hasOwn()` own-property checks — a provider id such as `constructor` or `__proto__` previously resolved through the prototype chain instead of falling through to the unknown-provider CDN fallback (`getLobeProviderIcon()` itself was already guarded by [#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)); `ProviderPageHeader`'s `color` field is also now optional, matching the rest of the component's defensive typing (ported from [#11920](https://github.com/diegosouzapw/OmniRoute/pull/11920) — thanks @HouMinXi). ([#11935](https://github.com/diegosouzapw/OmniRoute/pull/11935)) — thanks @NoxzRCW +- fix(ollama): preserve multi-byte UTF-8 content split across stream chunks in the Ollama NDJSON transform, which previously corrupted CJK/emoji into U+FFFD (#11921) — thanks @pacocartones +- **fix(providers):** OrcaRouter chat requests now target `/v1/chat/completions` instead of the bare `/v1` API root, fixing the upstream `404 Invalid URL (POST /v1)` ([#11923](https://github.com/diegosouzapw/OmniRoute/pull/11923)). — thanks @echel0nn +- **fix(plugins):** deliver the `onStreamComplete` event to disk-installed plugins. The event shipped in v3.8.50 (#9669) was emitted internally but had no plugin-facing wiring, so no plugin could ever subscribe: the manifest schema silently dropped `hooks.onStreamComplete`, and the loader/manager only knew the seven legacy hooks. `onStreamComplete` is now a declarable manifest hook, wired through the loader and registered by the manager like the other hooks, and its payload carries a `requestId` so consumers can correlate the stream-completion event with the originating request ([#11934](https://github.com/diegosouzapw/OmniRoute/pull/11934)) — thanks @amaleta / @pacocartones +- **fix(db):** the Qdrant embedding-model dropdown now lists local/self-hosted providers (Ollama, LM Studio, vLLM, etc.) — an active connection is treated as "configured" when the provider allows an optional API key, not only when it has a real key or OAuth, so a running local embedding provider is no longer hidden from the picker ([#11949](https://github.com/diegosouzapw/OmniRoute/issues/11949)) ([#11995](https://github.com/diegosouzapw/OmniRoute/pull/11995)) +- **fix(providers):** Vertex AI Anthropic partner-model discovery now calls the Model Garden `v1beta1` publisher list (`/v1beta1/publishers/anthropic/models`, global) and parses its `publisherModels` envelope, so Claude models auto-synced from Vertex populate the active live catalog and route at request time instead of returning `Model '' is not available in the active live catalog` ([#11991](https://github.com/diegosouzapw/OmniRoute/issues/11991)) — thanks @fabioluissilva ([#11998](https://github.com/diegosouzapw/OmniRoute/pull/11998)) +- **fix(cli):** support `--base-url` alongside `--endpoint` in `omniroute nodes add`, `update`, and `validate` subcommands to prevent global `--base-url` shadowing issues ([#11999](https://github.com/diegosouzapw/OmniRoute/issues/11999)). ([#12033](https://github.com/diegosouzapw/OmniRoute/pull/12033)) — thanks @HouMinXi +- **fix(providers):** `cloudflare-ai` no longer refuses image content parts for every Workers AI model ([#12002](https://github.com/diegosouzapw/OmniRoute/pull/12002)) — the plain-string `content` requirement behind #2539 is carried by the _model_ schema, not by the `/ai/v1/chat/completions` endpoint (measured: an all-text part array returns 200 on `@cf/mistralai/mistral-small-3.1-24b-instruct`, `@cf/meta/llama-4-scout-17b-16e-instruct` and `@cf/meta/llama-3.3-70b-instruct-fp8-fast`, and 400 on the text-only `@cf/qwen/qwen2.5-coder-32b-instruct`). `transformRequest()` flattened every array and threw on the first non-text part (#6390), so image input was refused for vision-capable Cloudflare models that accept it. All-text arrays are still flattened — the one shape every model accepts — while an array carrying a non-text part is passed through untouched, so the attachment is still never silently dropped. Regression guards: `tests/unit/cloudflare-ai-image-parts-6390.test.ts`. — thanks @davidlinfr +- **fix(resilience):** decouple the limiter-managed execution backstop from the queue-wait budget — new `requestQueue.executionMaxWaitMs` (env `RATE_LIMIT_EXECUTION_MAX_WAIT_MS`, default 600000 = 10 min) now feeds Bottleneck's post-dispatch `expiration`, while `requestQueue.maxWaitMs` keeps its documented queue-wait semantics. Previously the queue-wait budget doubled as the execution expiration, so legitimate long-running calls on non-incremental gateways (whole generation buffered before the first upstream byte, e.g. Console Go / Command Code tiers serving GLM models) were killed mid-flight at the queue budget with a false 504 `RATE_LIMIT_EXECUTION_TIMEOUT` — the local limiter undercut the provider-aware upstream fetch-start timeouts. The surfaced 504 message now names `requestQueue.executionMaxWaitMs`; the error keeps the #4165 guarantees (disclaims an upstream timeout, preserves the Bottleneck error as `cause`, branded code + trusted provenance, classified request-scoped so combo falls back). A real queue-wait bound (the `Promise.race` around `limiter.schedule()` sketched in #9533) remains future work. (#12025) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **Call logs:** keep the `error` field when an artifact exceeds the storage cap, instead of replacing it with the omission marker. The error is the only field that says *why* a request failed and is typically ~90 bytes next to the multi-hundred-KB bodies that trip the cap, so dropping it left a size-limited row undiagnosable — a provider outage, a local timeout and an upstream 400 all rendered identically. It is now preserved at every fallback stage, truncated to 4KB if it is itself large ([#12026](https://github.com/diegosouzapw/OmniRoute/issues/12026)). ([#12095](https://github.com/diegosouzapw/OmniRoute/pull/12095)) — thanks @ntdat812 +- **fix(diagnostics):** preserve the error field (truncated to 4KB with a `[truncated: …]` suffix) in every call-log artifact size-limit fallback stage. Previously the minimal fallback replaced the error with `[omitted: call log artifact size limit exceeded]`, so an oversized artifact row showed nothing about WHY the request failed — e.g. 91 of 847 opencode-go 504 rows on one production instance were undiagnosable from the dashboard. Oversized request/response bodies are still omitted exactly as before; the error cap is independent of the payload sizes that tripped the fallback. (#12026) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **fix(sse):** OpenAI Responses clients that declare the native `web_search` tool now receive a spec-shaped `web_search_call` output item with `action.sources` alongside the preserved function-call round-trip, so search results executed through OmniRoute's own search backend are consumable by standard Responses clients (Codex, pi-web-access, …). ([#12031](https://github.com/diegosouzapw/OmniRoute/pull/12031)) — thanks @watchingdogs +- **fix(cli):** use in-thread alias resolver hooks on modern runtimes to avoid deprecation noise and improve Node.js forward compatibility ([#12073](https://github.com/diegosouzapw/OmniRoute/issues/12073)). ([#12083](https://github.com/diegosouzapw/OmniRoute/pull/12083)) — thanks @xiaoyaner0201 +- **fix(combo):** an operator-set **Agent Features → Context length** on a combo is now honored at request time. The value was persisted and advertised through `/v1/models`, but `resolveComboContextLimit()` never consulted it — so a multi-target combo whose members carry no per-model window fell through to the provider's generic `defaultContextLength` (openrouter 128000, command-code 200000) and rejected large requests with `Input exceeds context window … limit 128000` despite the combo being explicitly sized much larger. An identical single-target combo worked, because it collapses to its concrete target before the guard runs. Invalid values (0/negative/NaN/Infinity) are ignored, so the existing target → combo-min → fallback order is unchanged. ([#12090](https://github.com/diegosouzapw/OmniRoute/pull/12090)) — thanks @adivekar-utexas +- **fix(sse):** passthrough streams now estimate usage on finish when upstream closes without usage even with `stream_options.include_usage` — avoids `0 tokens / 0%` for providers that stay silent (and correctly handles trailing empty-choices usage) ([#12151](https://github.com/diegosouzapw/OmniRoute/pull/12151)) — thanks @maxmad64bis +- **fix(combos):** clearing an agent feature in the combos editor now persists — unchecking context cache protection, or emptying the system message or tool filter, sends an explicit `null` instead of dropping the field from the `PUT` body, which the update merge read as "leave unchanged" ([#12177](https://github.com/diegosouzapw/OmniRoute/pull/12177)) — thanks @foreveryh / @ntdatt812 +- **fix(memory):** self-hosted embedding endpoints now vectorize — the vector width is measured from the first embedding that comes back instead of being read from a registry that cannot describe them, so `vec_memories` is created and memories stop piling up unvectorized behind a green health check ([#12180](https://github.com/diegosouzapw/OmniRoute/pull/12180)) — thanks @kanade-hoshino / @ntdatt812 +- **fix(translator):** the leading `system` message now reaches Responses-API upstreams when its `content` is a content-part array — it was read as `typeof content === "string" ? content : ""`, so a prompt-caching client (Anthropic `cache_control`, the shape LiteLLM and the Anthropic SDK emit) had its entire system prompt replaced by an empty `instructions`. The request was still accepted with a normal `prompt_tokens` count, so the model answered with no instructions and nothing in the response said they were missing. Mid-conversation system turns already handled the array shape ([#7056](https://github.com/diegosouzapw/OmniRoute/pull/7056)); only the first one did not ([#12206](https://github.com/diegosouzapw/OmniRoute/issues/12206)). Regression guard: `tests/unit/translator-openai-responses-system-content-parts.test.ts`. ([#12207](https://github.com/diegosouzapw/OmniRoute/pull/12207)) — thanks @Gorillaz322 +- **fix(free-tier):** `/api/free-tier/summary` no longer computes its totals from a Radar feed built before the catalog the running release ships. When the cached feed is older — or carries no build date at all — the route answers from the shipped catalog, resolved through the operator's local model state so disabled and tombstoned models stay out of the numbers ([#12215](https://github.com/diegosouzapw/OmniRoute/pull/12215)). — thanks @maxmad64bis +- **fix(oauth):** Keep a Claude personal workspace and a Team organization as separate connections — they share the same email and `accountUUID`, so the email-only OAuth dedup let the second login overwrite the first account's tokens; `organizationUUID` now disambiguates them, the way `workspaceId` does for Codex ([#12222](https://github.com/diegosouzapw/OmniRoute/pull/12222)) — thanks @davidebaraldo +- **fix(resilience):** a 402 on a single paid model of a passthrough/gateway provider (e.g. `kilo-gateway`, `ollama-cloud`) no longer terminalizes the whole connection with a never-auto-recovered `credits_exhausted` status — only the paid model is locked out, so free models on the same key keep serving. 402 variant of [#3027](https://github.com/diegosouzapw/OmniRoute/issues/3027). Single-credential providers are unaffected — a 402 there is still treated as the key being genuinely out of credit ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239) / [#10616](https://github.com/diegosouzapw/OmniRoute/issues/10616)) ([#12242](https://github.com/diegosouzapw/OmniRoute/issues/12242)) — thanks @brick30llc-ctrl ([#12266](https://github.com/diegosouzapw/OmniRoute/pull/12266)) — thanks @geekyNads +- **fix(sse):** trust `finish_reason: "length"`/`"max_tokens"` over the reasoning-consumed-token ratio in response quality validation, so a reasoning model truncated below the old 90% threshold correctly fails and retries instead of returning empty content as a silent "success" ([#12262](https://github.com/diegosouzapw/OmniRoute/pull/12262)) — thanks @brick30llc-ctrl +- **fix(settings):** `PUT /api/settings/cache-config` now persists `alwaysPreserveClientCache` to the flat general settings the runtime cache-control policy actually reads; previously the value landed in the databaseSettings "cache" section and was silently ignored, so the endpoint had no effect on `cache_control` passthrough ([#12304](https://github.com/diegosouzapw/OmniRoute/pull/12304)) — thanks @davidebaraldo +- **fix(grok-cli):** treat omitted SuperGrokPro `creditUsagePercent` as 0% used so Provider Limits still renders a weekly bar (proto3 zero-elision) ([#12312](https://github.com/diegosouzapw/OmniRoute/pull/12312)) — thanks @HouMinXi +- **fix(combo):** Expose the two Auto-Combo scoring factors nobody could set — the combo validation schema and the dashboard weight sliders both declared 13 of the scorer's 15 factors, so `connectionDensity` (spreads load across a provider's connections) and `quality` were dropped on save and offered nowhere. The sliders also shipped their own default table that differed from the engine's on every non-zero factor and summed to 1.05, so the percentages shown next to them added up to 105%. Both lists now match `DEFAULT_WEIGHTS`, and a test keeps them there. Note that a combo whose stored `weights` omitted the two keys was effectively running with them at zero and the other thirteen renormalized upward; it now runs with the engine's intended distribution, so its routing does shift ([#12314](https://github.com/diegosouzapw/OmniRoute/pull/12314)) — thanks @maxmad64bis +- **fix(docs):** The free-tier reference no longer says its numbers come "confidence tagged per row" — no catalog entry carries a confidence tag and the API serves none, so every figure on that page is an estimate of the same, unstated quality. The page now states what an entry does vouch for: an independently documented hard stop (set by hand with the source in a comment, never defaulted to `true`) and a prompt-training disclosure, both with live counts the `check:docs-counts` gate keeps honest ([#12318](https://github.com/diegosouzapw/OmniRoute/pull/12318)) — thanks @maxmad64bis +- **fix(usage):** `adobe-firefly` and `firefly` have had a working usage fetcher since Adobe Firefly landed, but neither was ever added to the registration list, so the provider-plugin manifest, `genericQuotaFetcher` and the free-access quota cache all reported them as having no usage support — while `USAGE_SUPPORTED_PROVIDERS` said the opposite. Both are now declared, which also means their credit balance is fetched like any other declared provider's: `registerGenericQuotaFetchers` now registers a generic quota fetcher for them, and `resolveFreeAccessState` no longer returns early. A test holds the registration list to the dispatcher's switch in both directions, which is what the module's own docstring already asked for in prose ([#12321](https://github.com/diegosouzapw/OmniRoute/pull/12321)) — thanks @maxmad64bis +- **fix(quota):** drop the generic quota cache (agy / Antigravity / Claude OAuth) on an upstream 429 so reset-aware scoring does not keep a 60s stale snapshot, and force-refresh the next usage fetch so inner provider caches cannot recache the same window ([#12325](https://github.com/diegosouzapw/OmniRoute/pull/12325)) — thanks @HouMinXi +- **fix(combos):** deleting a combo now clears its persisted LKGP pins instead of leaving unreachable `key_value` rows behind ([#12326](https://github.com/diegosouzapw/OmniRoute/issues/12326)) ([#12425](https://github.com/diegosouzapw/OmniRoute/pull/12425)) — thanks @KrzysiekSko +- **fix(sse):** Keep ZWNJ (U+200C) and ZWJ (U+200D) in assistant text, reasoning and tool-call arguments — Persian/Kurdish half-space (`ارائه‌دهنده`), Arabic/Indic shaping and emoji sequences no longer lose them; the response de-obfuscation now removes joiners only between ASCII word characters, where the request side inserts them ([#12186](https://github.com/diegosouzapw/OmniRoute/issues/12186)) — thanks @rezjalibd ([#12359](https://github.com/diegosouzapw/OmniRoute/pull/12359)) — thanks @pacocartones +- **fix(resilience):** count resolved upstream 5xx results against the provider circuit breaker on the chat path — `CircuitBreaker.execute()` no longer reads a resolved `{ success: false, status: 5xx }` as a success that cancels the call-site failure, so a provider answering 503s now trips its breaker instead of staying `CLOSED` at `failureCount: 1`; single-model and combo dispatches are each accounted exactly once ([#12254](https://github.com/diegosouzapw/OmniRoute/issues/12254)) ([#12360](https://github.com/diegosouzapw/OmniRoute/pull/12360)) — thanks @pacocartones +- **fix(providers):** resolve the Codex quota auto-ping model from the live provider catalog and lifecycle registry instead of the retired `gpt-5.1-codex-mini`, and pause the ping with one actionable warning when no selectable Codex model exists rather than retrying a shut-down id every cooldown window ([#11905](https://github.com/diegosouzapw/OmniRoute/issues/11905)) ([#12361](https://github.com/diegosouzapw/OmniRoute/pull/12361)) — thanks @pacocartones +- **fix(api):** keep the `{created, data}` wrapper on combo-routed `/v1/images/generations` responses and default Codex image results to `b64_json` on both `/v1/images/generations` and `/v1/images/edits` so Codex CLI's built-in `image_gen` can decode them ([#12268](https://github.com/diegosouzapw/OmniRoute/issues/12268)) ([#12362](https://github.com/diegosouzapw/OmniRoute/pull/12362)) — thanks @pacocartones +- **fix(sse):** Name the shadowed custom provider node when a built-in provider id/alias (e.g. `openference` → `of`) reserves the prefix of an existing OpenAI/Anthropic-compatible node, so the runtime `No active credentials for provider: ` error explains that the prefix routed to the built-in and never reached the node's healthy connections, instead of contradicting the dashboard ([#11943](https://github.com/diegosouzapw/OmniRoute/issues/11943)) — thanks @morpheus9393 ([#12365](https://github.com/diegosouzapw/OmniRoute/pull/12365)) — thanks @pacocartones +- **fix(i18n):** wrap `ccOnboardingKeyPlaceholder` in ICU single quotes across all 43 locales so angle brackets render literally instead of being parsed as rich-text tags, which crashed the Claude Code onboarding block with `INVALID_MESSAGE: INVALID_TAG` ([#12302](https://github.com/diegosouzapw/OmniRoute/issues/12302)) ([#12369](https://github.com/diegosouzapw/OmniRoute/pull/12369)) — thanks @gonisulaimann +- **fix(models):** publish `effort_tiers` on Kimi K3's synced base-model entries (`kmca/k3`, `kmca/k3-256k`) so catalog-only clients (OpenCode, plain SDK pickers) can see and select the reasoning tiers (`low`/`high`/`max`) the synced metadata already carried — the `isSkippedEffortProvider` gate no longer suppresses tier visibility on those base entries, while synthetic `-` variant generation stays prevented and Codex/GLM base models remain excluded unchanged ([#12299](https://github.com/diegosouzapw/OmniRoute/issues/12299)) ([#12371](https://github.com/diegosouzapw/OmniRoute/pull/12371)) — thanks @gonisulaimann +- **fix(guardrails):** keep `auto`/`auto/*` virtual combos exempt from the Vision Bridge `fixedModel` credential guard so a combo target is passed through instead of silently falling back to global auto-selection ([#12237](https://github.com/diegosouzapw/OmniRoute/issues/12237)) ([#12373](https://github.com/diegosouzapw/OmniRoute/pull/12373)) — thanks @pacocartones +- **fix(combo):** capability-filter exhaustion caused by `max_tokens` above every target's known output limit now reports that reason (requested `max_tokens` vs the pool's highest known ceiling) instead of the unrelated "supports structured output" message ([#12229](https://github.com/diegosouzapw/OmniRoute/issues/12229)) — thanks @DW-MediaLab ([#12374](https://github.com/diegosouzapw/OmniRoute/pull/12374)) — thanks @pacocartones +- **fix(auth):** the `least-used` account strategy now prefers accounts without backoff before falling back to oldest `lastUsedAt`, the same tie-break `round-robin` already applies, so a failover no longer lands on a just-rate-limited account for a single request ([#12279](https://github.com/diegosouzapw/OmniRoute/issues/12279)) — thanks @tenshiak ([#12375](https://github.com/diegosouzapw/OmniRoute/pull/12375)) — thanks @pacocartones +- **fix(docker):** the `chatgpt-web-codex-browser` image now finds the Chrome binary under `chrome-linux64/` (Chrome for Testing layout in `playwright:v1.62.0-noble`) as well as the legacy `chrome-linux/`, so the container no longer crash-loops with `exec: --headless=new: not found` ([#12024](https://github.com/diegosouzapw/OmniRoute/issues/12024)) ([#12376](https://github.com/diegosouzapw/OmniRoute/pull/12376)) — thanks @pacocartones +- **fix(providers):** declare `groq/compound` and `allam-2-7b` as non-reasoning models in the curated Groq registry so `reasoning_effort` / `output_config.effort` / `thinking` from Claude Code are stripped instead of forwarded, which Groq rejected with HTTP 400 ([#12134](https://github.com/diegosouzapw/OmniRoute/issues/12134)) ([#12379](https://github.com/diegosouzapw/OmniRoute/pull/12379)) — thanks @pacocartones +- **fix(executors):** `OpencodeExecutor` no longer forces a direct connection when the connection has a proxy assigned in Proxy Management but no per-account proxies: the single-account fast path used to wrap the upstream dispatch in the direct-egress sentinel, discarding the ambient proxy context the chat handler had pinned from `proxy_assignments`, so API-key `opencode`/`opencode-go` connections egressed from the host IP (and hit geoblocks) despite the assignment. The direct pin is now applied only when no ambient proxy context exists ([#11894](https://github.com/diegosouzapw/OmniRoute/issues/11894) — thanks @hizzt) ([#12380](https://github.com/diegosouzapw/OmniRoute/pull/12380)) — thanks @pacocartones +- **fix(api):** `GET /v1/models` with `MODELS_CATALOG_PREFIX_MODE=canonical` (or `?prefix=canonical`) now lists providers whose registry alias is undefined or equal to their own id (Antigravity, Antigravity CLI and other self-aliased built-ins) — their single `provider/model` id was dropped by the alias/canonical duplicate guard in the static, synced, custom and alias-backed catalog loops ([#12058](https://github.com/diegosouzapw/OmniRoute/issues/12058)) — thanks @cheynetom ([#12381](https://github.com/diegosouzapw/OmniRoute/pull/12381)) — thanks @pacocartones +- **fix(translator):** Drop replayed `thinking` blocks that carry no signature (the shape produced from cross-provider `reasoning_content`) instead of stamping the default Claude signature on them, which Anthropic rejected with `400 Invalid signature in thinking block` on the next turn served by an Anthropic rung ([#12105](https://github.com/diegosouzapw/OmniRoute/issues/12105)) — thanks @atescivitci-cmd ([#12386](https://github.com/diegosouzapw/OmniRoute/pull/12386)) — thanks @pacocartones +- **fix(cli):** Resolve Bun's `--preload` polyfill path against the package root instead of `dist/`, so `omniroute` installed with `bun install -g` no longer crashes at startup with `error: preload not found …/dist/open-sse/utils/setupPolyfill.ts` ([#11980](https://github.com/diegosouzapw/OmniRoute/issues/11980)) — thanks @joglomedia ([#12387](https://github.com/diegosouzapw/OmniRoute/pull/12387)) — thanks @pacocartones +- **fix(providers):** `gemini-business` now publishes its model catalog — `/v1/models` and `/v1/providers/gemini-business/models` list the 12 enterprise Gemini ids the executor understands instead of returning an empty list (#12107) ([#12389](https://github.com/diegosouzapw/OmniRoute/pull/12389)) — thanks @pacocartones +- **fix(db):** install `busy_timeout` before the SQLite connection's first statement so a process opening the database while another one closes its WAL connection waits out the transient EXCLUSIVE lock instead of dying with `database is locked`, and recognise the drivers' real BUSY/PROTOCOL/IOERR errors as transient in the corruption probe so the same lock no longer renames the database away as corrupt; deflakes `cross-process contenders never both acquire the same connection` (#12394 — thanks @pacocartones) +- **fix(chat-admission):** derive the `chat_admission_busy` 503 `Retry-After` from observed heavyweight-lease occupancy — the larger of the exhausted `OMNIROUTE_CHAT_ADMISSION_QUEUE_MS` window and the time since capacity last turned over, capped at 60 s — instead of a fixed 1 s (structural) / 2 s (byte-stage) hint that invited Codex/agent fan-out clients to re-send ~1 MiB `/v1/responses` bodies every second into a gate held for the whole SSE lifetime; an idle gate keeps the historical floors ([#12135](https://github.com/diegosouzapw/OmniRoute/issues/12135)) (#12395 — thanks @pacocartones) +- **fix(api-manager):** the API key permissions modal no longer silently drops `allowedCombos` entries its Combo picker cannot render — routing-rule names such as `rt-*`, which the backend already honours — when "All" is clicked and the key is switched back to "Restrict"; those entries now survive the toggle, are listed read-only under the combo list so the count and the list agree, and are saved back verbatim instead of persisting `[]` (deny-all) (#12397 — thanks @pacocartones) +- **fix(catalog):** write the NUL separator of the catalog connection memo key, the provider serviceKind memo key, the Video Bridge promotion group key and a JSON-exactness test fixture as the `\u0000` escape instead of a raw byte — same runtime value, but the raw byte made git, GitHub and ripgrep treat those files as binary (hidden PR diffs, silently skipped searches); a guard test now keeps raw NUL bytes out of `src/`, `open-sse/` and `tests/` (#12403 — thanks @pacocartones) +- **fix(providers):** add `CLAUDE_CODE_CLIENT_VERSION` and `GITHUB_COPILOT_CLI_VERSION` env overrides so Anthropic/Copilot client-version gates can be unblocked without a rebuild ([#12417](https://github.com/diegosouzapw/OmniRoute/issues/12417)) ([#12632](https://github.com/diegosouzapw/OmniRoute/pull/12632)) — thanks @HouMinXi +- **fix(db):** back-fill `last_ping_at` and `last_pinged_reset_key` on `provider_connections` during schema reconciliation so divergent lineages that skipped `123_quota_auto_ping` still accept quota auto-ping writes ([#12470](https://github.com/diegosouzapw/OmniRoute/pull/12470) — thanks @KooshaPari) +- **fix(ci):** document MIT exceptions for `@eloqnt/{config,format-json,format-po}` (next-intl transitive; locked tarballs omit `license`) and keep the A2A lifecycle vitest off the real SQLite persistence seam ([#12581](https://github.com/diegosouzapw/OmniRoute/issues/12581)) ([#12595](https://github.com/diegosouzapw/OmniRoute/pull/12595)) +- OpenCode plugin `/v1/models` catalog fetch now waits 30s by default and attaches HTTP `statusCode` on 401/5xx so host fallback plugins can hop instead of seeing an untyped AbortError/UnknownError. ([#12607](https://github.com/diegosouzapw/OmniRoute/pull/12607)) — thanks @RaviTharuma +- **CI:** the OpenAPI security-tier gate now mirrors `isAlwaysProtectedPath()` in full — it also reads `ALWAYS_PROTECTED_API_PATTERNS`, so the pattern-gated credential routes (`/api/providers/{id}/{claude,codex}-auth/{export,apply-local}`, GHSA-5926-2w35-7h4q) no longer report as unannotated. (#12605) +- **fix(api):** GET /v1/models no longer waits forever on a hung coalesced catalog rebuild; cold-path waits are bounded (`CATALOG_BUILD_TIMEOUT_MS`, default 8s) and a last-good 200 is served when the rebuild times out ([#12627](https://github.com/diegosouzapw/OmniRoute/issues/12627)). ([#12628](https://github.com/diegosouzapw/OmniRoute/pull/12628)) — thanks @RaviTharuma +- **fix(cursor):** a non-array `tool_calls` on an assistant message no longer crashes the cursor request translator with a `TypeError`; both loops now require an array ([#12689](https://github.com/diegosouzapw/OmniRoute/issues/12689)) ([#12691](https://github.com/diegosouzapw/OmniRoute/pull/12691)) — thanks @soroush5 +- **fix(chat):** Chat Completions no longer return empty `content` after a server-owned memory or skills tool; the first provider send and account/model recovery now share one pipeline so a follow-up round-trip can fill the reply ([#12696](https://github.com/diegosouzapw/OmniRoute/issues/12696)) — thanks @HouMinXi ([#12867](https://github.com/diegosouzapw/OmniRoute/pull/12867)) +- **fix(dashboard):** batch-deleting provider keys no longer toasts failure after a successful delete when the confirm button's click event is forwarded as `onAfter` ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) — thanks @HouMinXi +- **fix(glm):** drop the extra 16th argument to `createSSETransformStreamWithLogger` that TypeScript rejected (TS2554) and that never reached the TransformStream ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) ([#12770](https://github.com/diegosouzapw/OmniRoute/pull/12770)) — thanks @HouMinXi +- **fix(dashboard):** pass `nodeMap` into Runtime `QuotaGroup` so a quota monitor in error/exhausted/alerting no longer throws `ReferenceError: nodeMap is not defined`. ([#12868](https://github.com/diegosouzapw/OmniRoute/pull/12868)) — thanks @HouMinXi +- Restricted API keys whose `allowedModels` lists a combo name no longer skip every combo member at pre-dispatch (`ALL_TARGETS_SKIPPED`). Inner-target filtering still applies when the allow-list is a provider prefix or `disableNonPublicModels` is on ([#12899](https://github.com/diegosouzapw/OmniRoute/pull/12899)). — thanks @HouMinXi +- **fix(combos):** Effort-suffixed combo members inherit the base model's `model_context_overrides` row so priority order is not inverted on large requests ([#12926](https://github.com/diegosouzapw/OmniRoute/pull/12926)) — thanks @HouMinXi +- **fix(catalog):** Picker-added `customModels` enter the dispatch-time live catalog so combo members and bare inference no longer 400 ([#12934](https://github.com/diegosouzapw/OmniRoute/pull/12934)) — thanks @HouMinXi +- **fix(docker):** re-derive the Docker build's worker-pool memory budget from the MEASURED ~4.5 GB per-process RSS (the issue owner's own VPS dmesg OOM-killer reproduction) instead of the stale 2560 MB/worker inference #11419 shipped, and lower `OMNIROUTE_BUILD_WORKERS` 3 → 2 so 1 parent + 1 page-data worker (2 processes × 4.5 GB = 9 GB) fits the 12.288 GB (75%) budget on a 16 GB GitHub Actions runner — the previous default (1 parent + 2 workers = 13.5 GB) still overcommitted the runner and kept "Publish to Docker Hub" failing with `cannot allocate memory` after #11419 merged (#7518). ([#11663](https://github.com/diegosouzapw/OmniRoute/pull/11663)) +- **fix(electron):** the packaged Windows desktop build now passes the #7592 cold-restart smoke end to end. Five defects were found and fixed on the way: (1) optional-pack staging failed on any machine whose `tar` is GNU tar — it reads the drive letter in an absolute `-f C:\...` archive path as a remote rsh host (`Cannot connect to C:`), so staging now passes a bare filename with `cwd` at the tarball directory and surfaces tar stderr on failure; (2) the Electron `files` allowlist was missing `lib/loginHeaderCapture.js`, so the packaged main process crashed at startup with `Cannot find module './lib/loginHeaderCapture'`; (3) electron-builder ≥26 injects an `!**/node_modules/**` ignore into every extraResources pattern list that no positive filter can override, which silently dropped the staged runtime `node_modules` (including the better-sqlite3 N-API prebuild) from `resources/app` — a fresh v3.8.50 desktop build would have shipped with no native SQLite driver and reproduced the #7592 sql.js fallback on every machine; an `afterPack` hook now restores the staged `node_modules` after packing; (4) the packaged-app smoke harness redirected `USERPROFILE` into its temp DATA_DIR, but Electron resolves the Roaming profile from `%USERPROFILE%\AppData\Roaming\` (USERPROFILE takes precedence over the APPDATA env var) and the path service throws instead of creating it, so `requestSingleInstanceLock()` returned false and the app exited(0) silently before `app.whenReady()` — the harness now pre-creates the derived tree, and `ensureSmokeEnvDirs` is exported and unit-tested; (5) the #7592 driver assertion parsed a `[DB] Driver: ...` line that the server's primary DB path never printed (only the unused `openDatabaseAsync()` did) — `getDbInstance()` now emits the same line on open so the guard can actually assert the native driver. Also: the smoke env-allowlist unit test hardcoded POSIX paths and could only pass on Linux/macOS; expectations are now host-agnostic, with new regression tests for the USERPROFILE-derived tree and `tarPack` under absolute Windows-style paths ([#11443](https://github.com/diegosouzapw/OmniRoute/pull/11443)) — thanks @jonlwheat2-gif +- fix(sse): stop the auto-combo candidates inspector from silently dropping model-locked/cooled-down rows (#9133) ([#11994](https://github.com/diegosouzapw/OmniRoute/pull/11994)) — thanks @hartmark +- **fix(providers):** Perplexity Web no longer turns upstream stream failures into successful assistant text; pre-content failures remain eligible for fallback, partial output ends with a structured sanitized error, and failed sessions are not persisted ([#12465](https://github.com/diegosouzapw/OmniRoute/pull/12465)) +- **fix(video-bridge):** preserve chronological video and transcript fusion, source-frame timestamps, transcript provenance, full contact-sheet intervals, and pre-abort callback isolation ([#11681](https://github.com/diegosouzapw/OmniRoute/pull/11681)) +- **Z.ai Web:** HTTP 200 streams carrying an upstream error now terminate with a structured failure instead of assistant text plus a normal stop, preserving partial output while allowing pre-content combo fallback. ([#12454](https://github.com/diegosouzapw/OmniRoute/pull/12454)) +- Absorb `Error [AbortError]: request_signal_aborted` and DOMException AbortError shapes in the process-level client-abort crash guard so routine client disconnects no longer kill the server (exit code 7). ([#12165](https://github.com/diegosouzapw/OmniRoute/pull/12165)) — thanks @alvinveroy +- **fix(sse):** Treat Adapta Web `type:error` SSE events as sanitized non-stream failures instead of empty HTTP 200 completions. ([#12459](https://github.com/diegosouzapw/OmniRoute/pull/12459)) +- **fix(autoCombo):** layer 3 (`models_dev_tier`) no longer scores retired models as premium, merge cross-provider capability rows deterministically, and inherit tier scores through variant ids ([#11508](https://github.com/diegosouzapw/OmniRoute/issues/11508)). `getModelsDevTierFitness` now vetoes ids present in `config/quality/model-lifecycle.json` with status `retired` before any other signal; `loadModelCapabilities()` aggregates per `model_id` (any-non-null-true for booleans, max for `limit_context`) instead of last-write-wins over SQLite's undefined row order; a capabilities miss retries through `resolveScoresAs()`'s base id and reports `models_dev_tier:inherited`, matching layers 1–2 (#11492). Adds `getModelsDevTierFitnessWithSource()` and an `invalidateCapabilitiesCache()` test/ops hook. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) — thanks @Meet6338-X +- fix(sse): accept Claude `content:[]` + `stop_reason: max_tokens`/`tool_use` in `detectMalformedNonStream` (match `isEmptyContentResponse`) so Claude Code `/model` probes with `max_tokens: 1` no longer become a false 502 ([#11568](https://github.com/diegosouzapw/OmniRoute/pull/11568)) — thanks @Seramicx +- **fix(security):** sanitize `request.failed` diagnostics before publishing them to live dashboard listeners and replay history, while keeping status, model, provider, latency, and internal call-log diagnostics intact. ([#12469](https://github.com/diegosouzapw/OmniRoute/pull/12469)) +- **fix(pwa):** do not intercept dashboard navigations so Chrome can fall back from a stale HTTP/3 Alt-Svc advertisement (UDP :20128 is unpublished; F5 on a long-lived tab hung until a new tab opened a fresh TCP connection). ([#12767](https://github.com/diegosouzapw/OmniRoute/pull/12767)) — thanks @HouMinXi +- **fix(executors):** handle DuckDuckGo ERR_BN_LIMIT (418) without retrying — when the upstream returns `418 ERR_BN_LIMIT` (rate-limit/ban), the executor now returns the error immediately instead of burning another VQD acquisition that would only count against the IP limit. The retry logic for `418 ERR_CHALLENGE` (unsolved challenge) remains unchanged. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) ([#12110](https://github.com/diegosouzapw/OmniRoute/pull/12110)) — thanks @oyi77 / @Meet6338-X +- Fixed DuckDuckGo streaming responses losing JSON lines and UTF-8 characters split across network chunks. ([#11528](https://github.com/diegosouzapw/OmniRoute/pull/11528)) — thanks @pacocartones +- Harden generated Cloudflare, Vercel, and Deno relays by stripping hop-by-hop, framing, and proxy authentication headers before forwarding requests upstream. ([#11533](https://github.com/diegosouzapw/OmniRoute/pull/11533)) — thanks @pacocartones +- **feat(grok-cli):** Provider Limits shows grok-cli banked reset credits from `GetRemainingResets` (including a real zero; a failed RPC omits the row) and the existing View credits button now calls `ConsumerUiSvc/RedeemReset` for grok-cli. Live tokens use nested fields 10/20/30 (id + Timestamp), not compact 1/2/3. ([#12805](https://github.com/diegosouzapw/OmniRoute/pull/12805)) — thanks @HouMinXi +- **fix(memory):** Embedding Model Quick select, Embedding Source remote dropdown, and Rerank selector now list every configured provider with embedding/rerank support instead of only chat-catalog text matches plus OpenRouter live discovery; a generic OpenAI-compatible `/embeddings` + Cohere-compatible `/rerank` runtime fallback resolves any configured chat provider's embedding/rerank endpoint, so unlisted providers no longer fail with "Unknown embedding provider"; both memory selectors gained a free-text model override ([#11390](https://github.com/diegosouzapw/OmniRoute/pull/11390)) — thanks @rqzbeh +- Harden SQLite upgrades around the historical migration-074 version collision: missing discovery and inspector tables are replayed atomically, pre-existing databases (including setup-created skeletons) receive reusable content-addressed safety snapshots, and Node test/eval probes without `DATA_DIR` are isolated from the operator database. ([#12435](https://github.com/diegosouzapw/OmniRoute/pull/12435)) +- **fix(dashboard):** Moonshot/Kimi Open Platform voucher and cash leftover percentages follow the bucket balance, so an empty wallet no longer paints those rows as 100% while Available is 0% ([#12733](https://github.com/diegosouzapw/OmniRoute/pull/12733)) — thanks @HouMinXi +- **fix(combo):** return non-retryable HTTP 400 when all candidates for a pinned native Codex turn are unavailable due to model-scoped lockout, terminating the turn cleanly while preserving turn continuity and enabling standard Combo routing on subsequent turns ([#12240](https://github.com/diegosouzapw/OmniRoute/pull/12240)) — thanks @mdigitalbh81 +- Fixed auto-router strategies preserving the selected connection through ranking and dispatch when multiple connections share the same provider and model. ([#11530](https://github.com/diegosouzapw/OmniRoute/pull/11530)) — thanks @pacocartones +- Preserve Chat Completions JSON-object response formats when translating requests to the Responses API. ([#11529](https://github.com/diegosouzapw/OmniRoute/pull/11529)) — thanks @pacocartones +- Fix idempotency fingerprints so Responses API requests include `input` and other semantic generation fields using stable serialization, while excluding credentials and request metadata. ([#11532](https://github.com/diegosouzapw/OmniRoute/pull/11532)) — thanks @pacocartones +- **fix(api):** Generated API CLI commands now enforce required OpenAPI request bodies; Combo test commands forward the required `comboName` body, while API keys created by older writers after migration 149 preserve legacy allow-all Combo access without widening explicit empty allowlists — thanks @marcelokarval ([#12070](https://github.com/diegosouzapw/OmniRoute/pull/12070)) +- **fix(grok-web):** treat upstream streaming failures as failures instead of successful assistant text: error-only streams now fail readiness with HTTP 502, while failures after legitimate content preserve that partial output and terminate through the sanitized stream failure path without a normal `stop` completion. ([#12458](https://github.com/diegosouzapw/OmniRoute/pull/12458)) +- HuggingChat now turns HTTP 200 JSONL generation failures into a sanitized 502 before content, or a fixed public stream failure after partial output, so fallback and request persistence no longer record a false successful stop. ([#12456](https://github.com/diegosouzapw/OmniRoute/pull/12456)) +- **fix(providers):** keep 1min.ai HTTP 200 stream errors out of assistant content, preserve partial output, and expose sanitized terminal errors so pre-content failures can fall back. ([#12466](https://github.com/diegosouzapw/OmniRoute/pull/12466)) +- **fix(sessions):** preserve legacy Sessions data when the additive exclusive-session projection is unavailable, returning an empty projection and warning only once per contiguous outage instead of failing the endpoint; the Sessions badge intentionally reflects the merged legacy and exclusive row count introduced by [#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389) ([#11469](https://github.com/diegosouzapw/OmniRoute/pull/11469)) — thanks @KaspaPulse +- Render Nimble Search with the generic provider icon and serve Opper's proven logo locally. ([#12415](https://github.com/diegosouzapw/OmniRoute/pull/12415)) +- **fix(quality):** `config/quality/provider-assets-provenance.jsonl` was stale against the physical `public/providers/` tree (still listing 225 assets and 5 duplicate-content groups after several provider-retirement PRs removed 85 icon files), which would have made `check:provider-asset-provenance` fail on the next full run. Reconciled the manifest to the real 142-file snapshot (142 asset records, 1 duplicate-content group, 2 newly-registered files marked `unresolved` pending provenance research), repinned `auditedCommit`, and updated the regression test's hardcoded counts to match. ([#11876](https://github.com/diegosouzapw/OmniRoute/pull/11876)) +- fix(ui): wrap quota card footer action buttons to prevent clipping Refresh button on narrow cards — when extra action buttons such as `View credits` are present on Codex cards, allow the footer row to flex-wrap with `shrink-0` buttons so `Refresh now` remains visible and clickable across all responsive card widths ([#11464](https://github.com/diegosouzapw/OmniRoute/pull/11464)) — thanks @adevwithpurpose +- Keep Antigravity Gemini usable when the same connection's Claude weekly quota is empty; generic quota cache stays per-connection for every other provider. ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) — thanks @HouMinXi +- Fixed Tailscale tunnel setup to reject ports outside the valid 1-65535 range and safely install Windows MSI packages from paths containing apostrophes. ([#11534](https://github.com/diegosouzapw/OmniRoute/pull/11534)) — thanks @pacocartones +- Classify Cloudflared and Tailscale process-management routes as local-only and spawn-capable while preserving remote access to read-only tunnel status endpoints. ([#11531](https://github.com/diegosouzapw/OmniRoute/pull/11531)) — thanks @pacocartones +- Electron release workflow: the `publish-npm` job now grants `actions: read` to the reusable `npm-publish.yml` it calls (its `publish` job requests it), which is what made GitHub refuse the whole v3.8.50 run at startup and ship the release with zero desktop assets; a `workflow_dispatch` now builds the requested tag instead of the dispatching branch and can skip the npm leg (`publish_npm=false`) when only re-attaching assets ([#11974](https://github.com/diegosouzapw/OmniRoute/pull/11974)) +- **fix(dashboard):** The Combos page usage guide now reads its dismissal through `useSyncExternalStore` instead of correcting SSR state inside an effect, removing an extra commit of the page tree on every load (and the `react-hooks/set-state-in-effect` error it raised). ([#12671](https://github.com/diegosouzapw/OmniRoute/pull/12671)) +- Electron release: `electron/package-lock.json` regained the optional `electron-builder-squirrel-windows` subtree (13 entries) that `npm ci` had been refusing as out of sync, `electron-release.yml` gained a `build_ref` dispatch input and stops regenerating release notes on a re-attach dispatch, and the npm publish workflow attaches the SBOM to the GitHub Release on dispatch publishes too — so the v3.8.51 tag ships every desktop asset and the SBOM like v3.8.49 did ([#12022](https://github.com/diegosouzapw/OmniRoute/pull/12022)) +- Drained the reds every PR against `release/v3.8.51` was born with: documented the three service-worker build-id variables, registered the six retirement/tunnel tests with the mutation gate, approved `eslint-plugin-react-hooks` in the dependency allowlist, added the six `combo.sort.*` strings to `vi` and `pt-BR`, pointed the ChatGPT Web doc at the real migration-168 test, worded the g4f hint around the member key, and realigned four tests to the retired-provider catalog and the legacy-schema fixtures the retirement migrations touch. ([#11940](https://github.com/diegosouzapw/OmniRoute/pull/11940)) +- **fix(providers):** Claude, Grok, LMArena, Notion, and Perplexity web-cookie transports now use pooled `wreq-js` 3.2 instead of the native sidecar, with all nine supported bindings pinned and audited, and the applicable platform binding plus native-license evidence included in each release artifact ([#12429](https://github.com/diegosouzapw/OmniRoute/pull/12429), supersedes [#11753](https://github.com/diegosouzapw/OmniRoute/pull/11753)). +- **fix(providers):** Zed Hosted streaming failures now trigger fallback before content and end partial streams with a sanitized structured error instead of fake assistant text and a normal-success stop. ([#12455](https://github.com/diegosouzapw/OmniRoute/pull/12455)) +- **fix(test):** capture hook result after render (direct commit `b85d0abb0b`) +- **fix(sync):** repair the two regressions the v3.8.50 sync-back left on release/v3.8.51 (direct commit `5ade9e0851`) +- **revert(models):** drop the memo reset hook ported from #10055 (direct commit `282de841ae`) +- **fix(typecheck):** guard the optional connectionId before the reactive model sync (direct commit `f2825bad75`) +- **fix(quota-share):** release the winner's reserved in-flight slot (#11371) ([#11408](https://github.com/diegosouzapw/OmniRoute/pull/11408)) — thanks @oyi77 +- **fix(providers):** stop silently dropping reasoning effort on opencode families (#10788) ([#11409](https://github.com/diegosouzapw/OmniRoute/pull/11409)) — thanks @oyi77 +- **fix(combo):** enable genuine quota-aware routing for generic providers (antigravity, claude, etc.) ([#11411](https://github.com/diegosouzapw/OmniRoute/pull/11411)) — thanks @HouMinXi +- **fix(oauth):** allow expired token retry before deactivation and activate expired retry loop (#P0) ([#11414](https://github.com/diegosouzapw/OmniRoute/pull/11414)) — thanks @HouMinXi +- **perf(executors):** lazy-load the executor registry — defer class imports + construction to first use (#11220) ([#11421](https://github.com/diegosouzapw/OmniRoute/pull/11421)) — thanks @oyi77 +- **fix:** make release/v3.8.50 CI gates green (run 32786966560 root causes) ([#11450](https://github.com/diegosouzapw/OmniRoute/pull/11450)) — thanks @jonlwheat2-gif +- **fix(sse):** exact-domain cookie match and origin-equality URL assertions ([#11452](https://github.com/diegosouzapw/OmniRoute/pull/11452)) — thanks @hartmark +- **fix(providers):** send Responses API payload during OpenAI-compatible validation ([#11454](https://github.com/diegosouzapw/OmniRoute/pull/11454)) — thanks @tuandinh0801 +- **fix(command-code):** fallback to /alpha/generate for Go plan without Provider API access ([#11455](https://github.com/diegosouzapw/OmniRoute/pull/11455)) — thanks @Chewji9875 +- **fix(responses-continuation):** fail closed on a log-truncated stored input/output array ([#11473](https://github.com/diegosouzapw/OmniRoute/pull/11473)) — thanks @hartmark +- **fix(cli):** write jcode settings as [providers.omniroute] in config.toml ([#11484](https://github.com/diegosouzapw/OmniRoute/pull/11484)) — thanks @hongnoul +- **fix(logging):** raise chat-log array truncation cap to 1000 and unify duplicate implementations ([#11499](https://github.com/diegosouzapw/OmniRoute/pull/11499)) — thanks @hartmark +- **fix:** restore live websocket URL helpers ([#11509](https://github.com/diegosouzapw/OmniRoute/pull/11509)) — thanks @PauloFH +- **perf(core):** eliminate async waterfalls across home, cache, and db operations (#11396) ([#11512](https://github.com/diegosouzapw/OmniRoute/pull/11512)) — thanks @HouMinXi +- **fix(kiro):** add runtime.us-east-1.kiro.dev as first-attempt endpoint ([#11517](https://github.com/diegosouzapw/OmniRoute/pull/11517)) — thanks @rifqiawl +- **fix(providers):** remove duplicate 128-tool truncation in OpencodeExecutor (#11444) ([#11518](https://github.com/diegosouzapw/OmniRoute/pull/11518)) — thanks @HouMinXi +- **fix(providers):** poll onboardUser LRO before classifying BYOP ([#11519](https://github.com/diegosouzapw/OmniRoute/pull/11519)) — thanks @rifqiawl +- **fix(translator):** preserve exact tool name casing for Claude tool_use payloads (#11487) ([#11520](https://github.com/diegosouzapw/OmniRoute/pull/11520)) — thanks @HouMinXi +- **fix(kimi-web):** align health probe with executor domain ([#11521](https://github.com/diegosouzapw/OmniRoute/pull/11521)) — thanks @pacocartones +- **fix(cli):** preserve existing admin password during setup ([#11522](https://github.com/diegosouzapw/OmniRoute/pull/11522)) — thanks @pacocartones +- **fix(mitm):** keep Windows DNS elevation off the POSIX sudo path (#11430) ([#11542](https://github.com/diegosouzapw/OmniRoute/pull/11542)) — thanks @official-burak +- **fix(sse):** scale chat admission by ingest byte budget instead of a fixed request count ([#11548](https://github.com/diegosouzapw/OmniRoute/pull/11548)) — thanks @TheDemonTuan +- **fix:** accept Gemini Web cookie exports ([#11554](https://github.com/diegosouzapw/OmniRoute/pull/11554)) — thanks @rafacpti23 +- **fix(i18n):** complete Brazilian Portuguese translations ([#11558](https://github.com/diegosouzapw/OmniRoute/pull/11558)) — thanks @rafacpti23 +- **fix(sse):** explicit types for openai-responses pureHelpers — clears last failing core typecheck gate ([#11567](https://github.com/diegosouzapw/OmniRoute/pull/11567)) — thanks @jonlwheat2-gif +- **fix(opencode):** hydrate Proxy Pool references for no-auth connections ([#11584](https://github.com/diegosouzapw/OmniRoute/pull/11584)) — thanks @AStupidBear +- **fix(ci):** resolve inherited release/v3.8.51 base-red CI failures ([#11608](https://github.com/diegosouzapw/OmniRoute/pull/11608)) — thanks @jonlwheat2-gif +- **fix(db):** fallback load STORAGE_ENCRYPTION_KEY from env files ([#11614](https://github.com/diegosouzapw/OmniRoute/pull/11614)) — thanks @benzntech +- **fix(providers):** mark Brave Search as no longer having a free tier ([#11621](https://github.com/diegosouzapw/OmniRoute/pull/11621)) — thanks @turbolego +- **fix:** pass tool result content through as string in OpenAI/Claude -> Gemini translation ([#11624](https://github.com/diegosouzapw/OmniRoute/pull/11624)) — thanks @Hsia97 +- **fix(sse):** scope credential health to exact key ([#11628](https://github.com/diegosouzapw/OmniRoute/pull/11628)) — thanks @KaspaPulse +- **fix(install):** guard prepare script against missing husky ([#11634](https://github.com/diegosouzapw/OmniRoute/pull/11634)) — thanks @Prajeeth-12 +- **fix(security):** redact NVIDIA_API_KEY at the log sink in the ad-hoc diag script ([#11639](https://github.com/diegosouzapw/OmniRoute/pull/11639)) +- **fix(types):** clear the two type regressions red on release/v3.8.51 ([#11640](https://github.com/diegosouzapw/OmniRoute/pull/11640)) — thanks @jonlwheat2-gif +- **fix(build):** resolve workspace: protocol dependencies to concrete semver versions during prepublish (#11540) ([#11641](https://github.com/diegosouzapw/OmniRoute/pull/11641)) — thanks @HouMinXi +- **fix(search):** properly resolve configured search connection in /v1/responses pipeline (#11524) ([#11642](https://github.com/diegosouzapw/OmniRoute/pull/11642)) — thanks @HouMinXi +- **fix(authz):** trust cf-connecting-ip for via-proxy detection when peer is Cloudflare edge (#11514) ([#11643](https://github.com/diegosouzapw/OmniRoute/pull/11643)) — thanks @HouMinXi +- **fix(security):** compare the OIDC state cookie in constant time ([#11646](https://github.com/diegosouzapw/OmniRoute/pull/11646)) +- **fix(security):** namespace the dedup hash by the calling API key ([#11649](https://github.com/diegosouzapw/OmniRoute/pull/11649)) +- **fix(models):** gate the Codex-native catalog loop on prefix mode (#11632) ([#11662](https://github.com/diegosouzapw/OmniRoute/pull/11662)) — thanks @xiaoyaner0201 +- **fix(dashboard):** project Codex routing quota in account pools ([#11666](https://github.com/diegosouzapw/OmniRoute/pull/11666)) — thanks @KaspaPulse +- **fix(ddgw):** normaliza system no upstream da release v3.8.51 ([#11692](https://github.com/diegosouzapw/OmniRoute/pull/11692)) — thanks @RhianB14 +- **fix(memory):** honor category filter in GET /api/memory ([#11699](https://github.com/diegosouzapw/OmniRoute/pull/11699)) — thanks @Sabeekhann +- **fix(ci):** detect stale workspace lockfile entries ([#11714](https://github.com/diegosouzapw/OmniRoute/pull/11714)) — thanks @pacocartones +- **fix(ci):** stamp dist/BUILD_SHA before the npm publish provenance gate ([#11721](https://github.com/diegosouzapw/OmniRoute/pull/11721)) +- **fix(docker):** decouple the best-effort Bun image from the release manifest ([#11724](https://github.com/diegosouzapw/OmniRoute/pull/11724)) +- **perf(compression):** avoid cloning memo misses twice ([#11727](https://github.com/diegosouzapw/OmniRoute/pull/11727)) — thanks @RaviTharuma +- **fix(docker):** let the bun digest artifact be absent, not fatal ([#11740](https://github.com/diegosouzapw/OmniRoute/pull/11740)) +- **fix(shared):** block cloud-metadata hosts under default remote-image guard ([#11755](https://github.com/diegosouzapw/OmniRoute/pull/11755)) — thanks @santosraju99-hub +- **fix(api):** preserve caller-provided X-Correlation-Id on chat completions ([#11760](https://github.com/diegosouzapw/OmniRoute/pull/11760)) — thanks @aniruddhaadak80 +- **fix(resilience):** lock permanently retired models instead of short backoff (Gemini ban prevention) ([#11762](https://github.com/diegosouzapw/OmniRoute/pull/11762)) — thanks @turbolego +- **fix:** mark Vercel AI Gateway as passthroughModels ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **fix(resilience):** stop hammering permanently-moved endpoints and billing-suspended accounts ([#11774](https://github.com/diegosouzapw/OmniRoute/pull/11774)) — thanks @turbolego +- **fix(lease):** remove global static reservation and gate routing on live active lease occupancy ([#11775](https://github.com/diegosouzapw/OmniRoute/pull/11775)) — thanks @TheDemonTuan +- **fix(ci):** size the install-upgrade gate to a measured run, and log the pack cost ([#11776](https://github.com/diegosouzapw/OmniRoute/pull/11776)) +- **fix(pwa):** stop serving the stale shell after deploys ([#11779](https://github.com/diegosouzapw/OmniRoute/pull/11779)) — thanks @HouMinXi +- **fix(combo):** lock GitHub models rejected as "not supported" for future requests ([#11781](https://github.com/diegosouzapw/OmniRoute/pull/11781)) — thanks @turbolego +- **fix(cli):** probe both IPv4 and IPv6 loopback for server readiness (#11766) ([#11794](https://github.com/diegosouzapw/OmniRoute/pull/11794)) — thanks @vermasomesh835 +- **fix(sse):** preserve CLIProxyAPI wrappers per connection ([#11797](https://github.com/diegosouzapw/OmniRoute/pull/11797)) — thanks @RaviTharuma +- **fix(providers):** honor model auto-fetch opt-in on create ([#11798](https://github.com/diegosouzapw/OmniRoute/pull/11798)) — thanks @RaviTharuma +- **fix(dashboard):** make model auto-fetch order independent ([#11805](https://github.com/diegosouzapw/OmniRoute/pull/11805)) — thanks @RaviTharuma +- **fix(docker):** make GHA cache exports best effort ([#11806](https://github.com/diegosouzapw/OmniRoute/pull/11806)) — thanks @RaviTharuma +- **fix(kiro):** do not permanently ban on 'User is not authorized to make this call' ([#11809](https://github.com/diegosouzapw/OmniRoute/pull/11809)) — thanks @Deftera186 +- **fix(sse):** keep cache-write tokens in OpenAI-shaped usage ([#11814](https://github.com/diegosouzapw/OmniRoute/pull/11814)) — thanks @TheDemonTuan +- **fix(docs):** resolve relative markdown and wiki links across Fumadocs and GitHub wiki ([#11834](https://github.com/diegosouzapw/OmniRoute/pull/11834)) — thanks @AndrianBalanescu +- **fix(chat):** preserve unstripped model string for passthrough provider routing ([#11840](https://github.com/diegosouzapw/OmniRoute/pull/11840)) — thanks @raheemuddin786 +- **fix(mcp):** avoid async initialization inside sync esm wrappers (#11569) ([#11849](https://github.com/diegosouzapw/OmniRoute/pull/11849)) — thanks @HouMinXi +- **fix(sse):** preserve coherent markdown token boundaries across stream translation chunks (#11606) ([#11851](https://github.com/diegosouzapw/OmniRoute/pull/11851)) — thanks @HouMinXi +- **fix(ci):** run the install-upgrade gate on disk, not on the /tmp tmpfs ([#11855](https://github.com/diegosouzapw/OmniRoute/pull/11855)) +- **fix(skills):** expand shorthand property types in injected tool schemas ([#11857](https://github.com/diegosouzapw/OmniRoute/pull/11857)) — thanks @alvinveroy +- **fix(ci):** publish npm from a hosted runner so provenance is accepted ([#11877](https://github.com/diegosouzapw/OmniRoute/pull/11877)) +- **fix(lint):** pin react hooks plugin to 7.0.1 ([#11886](https://github.com/diegosouzapw/OmniRoute/pull/11886)) +- **fix(ci):** surface and clear release-green ESLint failure ([#11890](https://github.com/diegosouzapw/OmniRoute/pull/11890)) — thanks @MumuTW +- **fix(guardrails):** prevent duplicate prompt-injection-guard log output ([#11936](https://github.com/diegosouzapw/OmniRoute/pull/11936)) — thanks @patrykkopycinski +- **fix(auth):** downgrade expected transient states from warn to debug ([#11937](https://github.com/diegosouzapw/OmniRoute/pull/11937)) — thanks @patrykkopycinski +- **fix(codex):** fail over image generation for imported free plans ([#11948](https://github.com/diegosouzapw/OmniRoute/pull/11948)) — thanks @Bl0ck154 +- **fix(codex):** normalize non-stream responses ([#11951](https://github.com/diegosouzapw/OmniRoute/pull/11951)) — thanks @Bl0ck154 +- **fix(images):** forward Antigravity image size ([#11952](https://github.com/diegosouzapw/OmniRoute/pull/11952)) — thanks @Bl0ck154 +- **fix(db):** invalidate connection cache after upsert ([#11953](https://github.com/diegosouzapw/OmniRoute/pull/11953)) — thanks @Bl0ck154 +- **fix(codex):** restore imported account state ([#11954](https://github.com/diegosouzapw/OmniRoute/pull/11954)) — thanks @Bl0ck154 +- **fix(sse):** give extended-thinking targets the reasoning readiness budget ([#11959](https://github.com/diegosouzapw/OmniRoute/pull/11959)) — thanks @abhisheksharma2411 +- **fix(dashboard):** use opaque background and readable text color on cost chart tooltips ([#11960](https://github.com/diegosouzapw/OmniRoute/pull/11960)) — thanks @ZaimMarzuki +- **fix(antigravity):** send complete loadCodeAssist metadata (ideType/platform/pluginType as numeric enums) ([#11969](https://github.com/diegosouzapw/OmniRoute/pull/11969)) — thanks @rifqiawl +- **fix(core):** resolve DB init race condition and reasoning translation ([#12003](https://github.com/diegosouzapw/OmniRoute/pull/12003)) — thanks @raheemuddin786 +- **fix(admission):** exclude reclaimable page cache from the cgroup pressure ratio ([#12017](https://github.com/diegosouzapw/OmniRoute/pull/12017)) — thanks @HouMinXi +- **fix(build):** prune non-production trees in NFT trace excludes and tsconfig ([#12028](https://github.com/diegosouzapw/OmniRoute/pull/12028)) — thanks @Chewji9875 +- **fix(migrations):** add renamed migration compatibility for 056/073/077/101 ([#12036](https://github.com/diegosouzapw/OmniRoute/pull/12036)) — thanks @oyi77 +- **fix(sse):** default crash-guard logger to console.warn, not console ([#12042](https://github.com/diegosouzapw/OmniRoute/pull/12042)) — thanks @HouMinXi +- **fix(catalog):** derive vision/modalities for built-in auto combos from effective target pool ([#12046](https://github.com/diegosouzapw/OmniRoute/pull/12046)) — thanks @Prajeeth-12 +- **fix(release):** the packaged-app smoke verifies the database opened, not a driver line the primary path never prints (twin of #12032) ([#12047](https://github.com/diegosouzapw/OmniRoute/pull/12047)) +- **fix(barrel):** migrate src/app/ off the @/lib/localDb barrel import (#11795 Phase 2) ([#12051](https://github.com/diegosouzapw/OmniRoute/pull/12051)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate src/lib/ off the localDb barrel to direct db imports (#59) ([#12052](https://github.com/diegosouzapw/OmniRoute/pull/12052)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate open-sse, src/shared, src/sse, src/models, src/domain off the @/lib/localDb barrel import (#11795 Phase 4) ([#12053](https://github.com/diegosouzapw/OmniRoute/pull/12053)) — thanks @jonlwheat2-gif +- **fix(barrel):** delete the @/lib/localDb barrel — every consumer migrated (#11795 Phase 5) ([#12055](https://github.com/diegosouzapw/OmniRoute/pull/12055)) — thanks @jonlwheat2-gif +- **fix(radar):** restore D12 public boundary ([#12057](https://github.com/diegosouzapw/OmniRoute/pull/12057)) +- **fix(dev):** bound webpack and Tailwind scans ([#12075](https://github.com/diegosouzapw/OmniRoute/pull/12075)) — thanks @backryun +- **fix(dev):** isolate root layout settings reads ([#12076](https://github.com/diegosouzapw/OmniRoute/pull/12076)) — thanks @backryun +- **fix(dev):** reduce instrumentation executor fan-out (phase 3) ([#12078](https://github.com/diegosouzapw/OmniRoute/pull/12078)) — thanks @backryun +- **fix(dev):** make logging resources HMR-singleton ([#12079](https://github.com/diegosouzapw/OmniRoute/pull/12079)) — thanks @backryun +- **fix(sse):** keep unavailable forced connections scoped ([#12080](https://github.com/diegosouzapw/OmniRoute/pull/12080)) — thanks @keeltrace +- **fix(dev):** isolate batch dispatch from instrumentation ([#12081](https://github.com/diegosouzapw/OmniRoute/pull/12081)) — thanks @backryun +- **fix(ci):** reconcile release test contract drift ([#12082](https://github.com/diegosouzapw/OmniRoute/pull/12082)) — thanks @backryun +- **fix(ci):** accept CVE-2025-68121 in the prebuilt tls-client .so, auto-close base-red issues, guard Scorecard on the default branch ([#12085](https://github.com/diegosouzapw/OmniRoute/pull/12085)) +- **fix(db):** drop three consumer-less 1proxy exports — dead-code base-red on release/v3.8.51 after the barrel deletion (#12055) ([#12087](https://github.com/diegosouzapw/OmniRoute/pull/12087)) +- **fix(api):** clear the six API-route TypeScript regressions the new gate landed red on ([#12094](https://github.com/diegosouzapw/OmniRoute/pull/12094)) +- **fix(release):** never let the tag-push Create Release append auto notes to the curated body ([#12096](https://github.com/diegosouzapw/OmniRoute/pull/12096)) +- **fix(sse):** honor CLIProxyAPI environment API key ([#12099](https://github.com/diegosouzapw/OmniRoute/pull/12099)) — thanks @RaviTharuma +- **fix(oauth):** bind Google refresh to the client that issued the token ([#12106](https://github.com/diegosouzapw/OmniRoute/pull/12106)) — thanks @HouMinXi +- **fix(dashboard):** make RequestLoggerDetail loadable outside Next — CSS via globals.css + CJS/ESM interop (#11703 base-reds) ([#12114](https://github.com/diegosouzapw/OmniRoute/pull/12114)) +- **fix(leases):** project status lease row to lease columns so joined connection PII never escapes ([#12115](https://github.com/diegosouzapw/OmniRoute/pull/12115)) — thanks @geek007git +- **fix(plugins):** do not kill the plugin process when a fire-and-forget hook times out ([#12116](https://github.com/diegosouzapw/OmniRoute/pull/12116)) — thanks @geek007git +- **fix(guardrails):** restore injection-guard logging on middleware-only routes ([#12117](https://github.com/diegosouzapw/OmniRoute/pull/12117)) — thanks @geek007git +- **fix(plugins):** refresh stored manifest from disk on activate so new hook fields reach existing installs ([#12120](https://github.com/diegosouzapw/OmniRoute/pull/12120)) — thanks @geek007git +- **fix(codex):** preserve existing provider state when bulk-import upserts a matching connection ([#12122](https://github.com/diegosouzapw/OmniRoute/pull/12122)) — thanks @geek007git +- **fix(ci):** clear the two base-reds the 2026-08-30 merge batch left on release/v3.8.51 (round 3) ([#12123](https://github.com/diegosouzapw/OmniRoute/pull/12123)) +- **fix(sse):** strip type:'custom' from Claude tools on agentrouter dispatch ([#12126](https://github.com/diegosouzapw/OmniRoute/pull/12126)) — thanks @wahidsadik371-coder +- **fix(sse):** stop advertising video providers the dispatcher cannot run ([#12131](https://github.com/diegosouzapw/OmniRoute/pull/12131)) — thanks @quiterunner-commits +- **fix(search):** treat HTTP 432 and plan limit errors as transient cooldown ([#12139](https://github.com/diegosouzapw/OmniRoute/pull/12139)) — thanks @HouMinXi +- **fix(sse):** refuse an AI Horde queue that cannot fit the request budget ([#12143](https://github.com/diegosouzapw/OmniRoute/pull/12143)) — thanks @quiterunner-commits +- **fix(ci):** clear the base-reds the afternoon merge batch left on release/v3.8.51 (round 5: provider count 352, TS2554/TS2677) ([#12144](https://github.com/diegosouzapw/OmniRoute/pull/12144)) +- **fix(translator):** strip neutral tool_choice when tools absent in Responses-to-Chat (#12141) ([#12166](https://github.com/diegosouzapw/OmniRoute/pull/12166)) — thanks @HouMinXi +- **fix(guardrails):** pass providerId to getResolvedModelCapabilities in checkComboVision (#12112) ([#12169](https://github.com/diegosouzapw/OmniRoute/pull/12169)) — thanks @HouMinXi +- **fix(translator):** strip plaintext reasoning content for opaque responses backends (#12128) ([#12171](https://github.com/diegosouzapw/OmniRoute/pull/12171)) — thanks @HouMinXi +- **perf(stream):** compile hot-path regexes once, bound token caches, fix quadratic buffering ([#12179](https://github.com/diegosouzapw/OmniRoute/pull/12179)) — thanks @opensource-elearning +- **fix:** resolve compression worker file using runtime anchors instead of… ([#12183](https://github.com/diegosouzapw/OmniRoute/pull/12183)) — thanks @NightStalker-87 +- **fix:** prevent Claude Code session kills via liveness-aware readiness + auto model echo ([#12189](https://github.com/diegosouzapw/OmniRoute/pull/12189)) — thanks @opensource-elearning +- **fix(translator):** preserve falsy primitive values in Gemini and Antigravity function response results ([#12191](https://github.com/diegosouzapw/OmniRoute/pull/12191)) — thanks @Siva010 +- **fix(oauth):** mark empty Antigravity projectId as degraded and clear stale errors (#11284) ([#12205](https://github.com/diegosouzapw/OmniRoute/pull/12205)) — thanks @HouMinXi +- **fix(providers):** gate the Codex auto-ping usage read on the shared quota throttle ([#12209](https://github.com/diegosouzapw/OmniRoute/pull/12209)) — thanks @abhisheksharma2411 +- **fix(combos):** prioritize SQLite row id over inner JSON id and notify delete errors ([#12213](https://github.com/diegosouzapw/OmniRoute/pull/12213)) — thanks @HouMinXi +- **fix(providers):** mark featherless.ai as no longer having a free tier (`hasFree: false`) ([#12216](https://github.com/diegosouzapw/OmniRoute/pull/12216)) — thanks @turbolego +- **fix(deepseek-web):** stop Turbopack dev panic in the PoW worker path resolver ([#12221](https://github.com/diegosouzapw/OmniRoute/pull/12221)) — thanks @hartmark +- **fix(chat-admission):** clarify local 503 source ([#12223](https://github.com/diegosouzapw/OmniRoute/pull/12223)) — thanks @keeltrace +- **fix(dev):** silence webpack runtime module warnings ([#12228](https://github.com/diegosouzapw/OmniRoute/pull/12228)) — thanks @backryun +- **fix(db):** use module.require for CommonJS runtime driver loading ([#12230](https://github.com/diegosouzapw/OmniRoute/pull/12230)) — thanks @raheemuddin786 +- **fix(memory):** honest probe-driven FTS5 keyword status + memory_id rowid sync ([#12231](https://github.com/diegosouzapw/OmniRoute/pull/12231)) — thanks @raheemuddin786 +- **fix(system):** propagate abort signal to stream reader in HTTP version checks ([#12232](https://github.com/diegosouzapw/OmniRoute/pull/12232)) — thanks @raheemuddin786 +- **fix(resilience):** isolate local host execution errors from provider circuit breakers ([#12233](https://github.com/diegosouzapw/OmniRoute/pull/12233)) — thanks @raheemuddin786 +- **fix(sse):** sort injected tools deterministically for prompt caching ([#12234](https://github.com/diegosouzapw/OmniRoute/pull/12234)) — thanks @raheemuddin786 +- **fix(combo):** always clear the loop-safety timer, not just on the happy path (#11804) ([#12245](https://github.com/diegosouzapw/OmniRoute/pull/12245)) +- **fix(quality):** base-red pair — stryker tap registration + turn-pin suites aligned to the window gate ([#12255](https://github.com/diegosouzapw/OmniRoute/pull/12255)) — thanks @backryun +- **fix(dev):** qualify Turbopack runtime boundaries ([#12258](https://github.com/diegosouzapw/OmniRoute/pull/12258)) — thanks @backryun +- **fix(combo):** fall back to full pool when collapsed sole survivor is context-too-small ([#12278](https://github.com/diegosouzapw/OmniRoute/pull/12278)) — thanks @opensource-elearning +- **fix(combo):** bound the pre-dispatch unavailable skip so a stale label cannot dark a pool (#12168) ([#12285](https://github.com/diegosouzapw/OmniRoute/pull/12285)) +- **fix(security):** strip Qwen/Alibaba console-session cookies from provider API responses ([#12287](https://github.com/diegosouzapw/OmniRoute/pull/12287)) +- **fix(usage):** console-aware Token Plan guidance + subscription hint on bailian 401 ([#12288](https://github.com/diegosouzapw/OmniRoute/pull/12288)) +- **fix(resourcePressure):** log numeric detail on every rejection, recover faster ([#12293](https://github.com/diegosouzapw/OmniRoute/pull/12293)) — thanks @hartmark +- **fix(release):** drain the 2026-09-01 base-red window — passthrough usage regression + radarPage i18n keys ([#12327](https://github.com/diegosouzapw/OmniRoute/pull/12327)) +- **fix(ci):** openapi-security-tiers checker must honor routeGuard patterns + imported prefixes ([#12350](https://github.com/diegosouzapw/OmniRoute/pull/12350)) — thanks @ggiak +- **perf(ci):** cache node_modules in the npm-ci-retry composite (#8084 D3) ([#12408](https://github.com/diegosouzapw/OmniRoute/pull/12408)) +- **revert(ui):** point CTAs back at their real destinations ([#12410](https://github.com/diegosouzapw/OmniRoute/pull/12410)) +- **fix(release):** drain the 2026-09-02 base-red — rerank-providers import + api-typecheck baseline ratchet ([#12414](https://github.com/diegosouzapw/OmniRoute/pull/12414)) +- **fix(types):** make system prompt injection noImplicitAny-safe ([#12416](https://github.com/diegosouzapw/OmniRoute/pull/12416)) +- **fix(quality):** validate typecheck baseline schema ([#12419](https://github.com/diegosouzapw/OmniRoute/pull/12419)) +- **fix(memory):** resolve rerank provider node cache import ([#12421](https://github.com/diegosouzapw/OmniRoute/pull/12421)) +- **fix(ci):** repair release-root regressions — pack dedup, web-session syntax, uc-image ids ([#12423](https://github.com/diegosouzapw/OmniRoute/pull/12423)) — thanks @backryun +- **fix(providers):** separate MaxAI and UC credential contracts ([#12431](https://github.com/diegosouzapw/OmniRoute/pull/12431)) +- **fix(cli):** preserve Claude settings on config set ([#12432](https://github.com/diegosouzapw/OmniRoute/pull/12432)) — thanks @KrzysiekSko +- **fix(providers):** repair the maxai credential block truncated by merge auto-resolve ([#12433](https://github.com/diegosouzapw/OmniRoute/pull/12433)) +- **fix(quality):** record the 2026-09-02 merged growth in the file-size baseline ([#12434](https://github.com/diegosouzapw/OmniRoute/pull/12434)) +- **fix(uc):** route UC error strings through sanitizeErrorMessage; allowlist the retired codex id ([#12437](https://github.com/diegosouzapw/OmniRoute/pull/12437)) +- **fix(adapta):** redact streamed upstream errors ([#12438](https://github.com/diegosouzapw/OmniRoute/pull/12438)) +- **fix(sse):** remap non-contiguous upstream tool_calls index to a gap-free output_index ([#12445](https://github.com/diegosouzapw/OmniRoute/pull/12445)) — thanks @hartmark +- **fix(quality):** detect duplicate tool_calls entries in one response ([#12446](https://github.com/diegosouzapw/OmniRoute/pull/12446)) — thanks @hartmark +- **fix(conversations):** resolve turn content OmniRoute never sends back to the client ([#12447](https://github.com/diegosouzapw/OmniRoute/pull/12447)) — thanks @hartmark +- **fix(combo):** do not treat credits-exhausted 401 as auth skip ([#12449](https://github.com/diegosouzapw/OmniRoute/pull/12449)) — thanks @RaviTharuma +- **fix(auth):** do not park healthy quota accounts as expired ([#12452](https://github.com/diegosouzapw/OmniRoute/pull/12452)) — thanks @RaviTharuma +- **fix(responses-continuation):** fail closed on a collector-truncated, empty output array ([#12460](https://github.com/diegosouzapw/OmniRoute/pull/12460)) — thanks @hartmark +- **fix(logging):** raise the SSE payload collector's default cap ([#12461](https://github.com/diegosouzapw/OmniRoute/pull/12461)) — thanks @hartmark +- **fix(resilience):** surface Responses failed.error.message in 502s ([#12472](https://github.com/diegosouzapw/OmniRoute/pull/12472)) — thanks @RaviTharuma +- **fix(security):** unbiased maxai X-Random nonce + stricter URL/regex assertions ([#12502](https://github.com/diegosouzapw/OmniRoute/pull/12502)) +- **fix(video):** re-anchor transcript log-redaction so PII/credential maskers can't reopen the leak (#12150 P1 follow-up) ([#12503](https://github.com/diegosouzapw/OmniRoute/pull/12503)) +- **fix(monitoring):** serve cached credentialHealth off the request path ([#12533](https://github.com/diegosouzapw/OmniRoute/pull/12533)) — thanks @RaviTharuma +- **fix(quota):** keep Antigravity Gemini usable when Claude weekly is empty ([#12566](https://github.com/diegosouzapw/OmniRoute/pull/12566)) — thanks @HouMinXi +- **fix(authz):** hard-gate every credential export and CLI-config write (GHSA-5926-2w35-7h4q) ([#12600](https://github.com/diegosouzapw/OmniRoute/pull/12600)) +- **fix(docs):** restore the Next build — REMOVED_PROVIDERS.md had no frontmatter (base-red #12581) ([#12610](https://github.com/diegosouzapw/OmniRoute/pull/12610)) +- **fix(quality):** re-point the zcodeProtocol public-creds allowlist to line 313 ([#12615](https://github.com/diegosouzapw/OmniRoute/pull/12615)) +- **fix(security):** close 3 advisories — search baseUrl exfil, sk- in the error sanitizer, bifrost relay header leak ([#12620](https://github.com/diegosouzapw/OmniRoute/pull/12620)) +- **fix(providers):** sanitize boolean required and nested bare maps for Gemini (#12269) ([#12624](https://github.com/diegosouzapw/OmniRoute/pull/12624)) — thanks @HouMinXi +- **fix(resilience):** keep Overloaded STREAM_EARLY_EOF off the provider breaker ([#12626](https://github.com/diegosouzapw/OmniRoute/pull/12626)) — thanks @HouMinXi +- **fix(catalog):** re-audit free-tier quotas against official pages ([#12649](https://github.com/diegosouzapw/OmniRoute/pull/12649)) +- **fix(dashboard):** keep a request's pending-tracking id stable across combo target retries ([#12650](https://github.com/diegosouzapw/OmniRoute/pull/12650)) — thanks @hartmark +- **fix(tests):** retire dead model ids from the chat-pipeline integration suite (base-red #12581) ([#12670](https://github.com/diegosouzapw/OmniRoute/pull/12670)) +- **fix:** resolve SqliteError no such table compression_run_telemetry during cleanup ([#12682](https://github.com/diegosouzapw/OmniRoute/pull/12682)) — thanks @groovecityJO +- **fix(combo):** treat a pin-only step as implicit connection allowlist ([#12697](https://github.com/diegosouzapw/OmniRoute/pull/12697)) — thanks @HouMinXi +- **fix(ci):** ship .npmrc in published package so legacy-peer-deps applies to consumers (#11544) ([#12699](https://github.com/diegosouzapw/OmniRoute/pull/12699)) — thanks @KooshaPari +- **fix(docker):** pin 4 CLI tools to exact versions (#12576) ([#12703](https://github.com/diegosouzapw/OmniRoute/pull/12703)) — thanks @KooshaPari +- **fix(docker):** document and harden cli profile trust boundary (#12570) ([#12706](https://github.com/diegosouzapw/OmniRoute/pull/12706)) — thanks @KooshaPari +- **fix(sse):** redact video transcript in pre-guardrail rejected-request logs (#12150 P2 item 7) ([#12710](https://github.com/diegosouzapw/OmniRoute/pull/12710)) +- **fix(i18n):** quote placeholder in OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES description (#12505) ([#12769](https://github.com/diegosouzapw/OmniRoute/pull/12769)) — thanks @KooshaPari +- **fix(api/pricing):** surface validation error message as string, not raw object (#12494) ([#12771](https://github.com/diegosouzapw/OmniRoute/pull/12771)) — thanks @KooshaPari +- **fix(sse):** re-enable prompt compression for native Codex passthrough ([#12834](https://github.com/diegosouzapw/OmniRoute/pull/12834)) — thanks @thomasmaerz +- **fix(dashboard):** model health tests for a provider node set to the Responses API now call `/v1/responses` with a Responses-shaped body instead of `/v1/chat/completions` — those models were reported as `Provider returned HTTP 200 but no text content` even though the same model answered normally through `/v1/responses` ([#13070](https://github.com/diegosouzapw/OmniRoute/issues/13070)) ([#13087](https://github.com/diegosouzapw/OmniRoute/pull/13087)) — thanks @ntdatt812 +- fix(routing): stop a round-robin combo's "opencode" targets from collapsing onto the opencode-zen connection (#11912) ([#13283](https://github.com/diegosouzapw/OmniRoute/pull/13283)) +- fix(routing): stop the reactive-compaction debug log from lying when compression is globally disabled (#11977) ([#13276](https://github.com/diegosouzapw/OmniRoute/pull/13276)) +- fix(electron): relativize standalone-bundle symlink targets so Stage 8 manifest verification stops failing on Windows (#11979) ([#13251](https://github.com/diegosouzapw/OmniRoute/pull/13251)) +- fix(dashboard): surface a visible error when Compression Studio's combined preview run fails (#12061) ([#13288](https://github.com/diegosouzapw/OmniRoute/pull/13288)) +- fix(dashboard): make the compression "Effective pipeline" preview honor the active profile and warn when the master switch is off (#12063) ([#13287](https://github.com/diegosouzapw/OmniRoute/pull/13287)) +- fix(providers): scope TinyCMS Web signer's DOM shims to each call instead of leaking them for the process lifetime, and surface a clean HTTP status on a non-JSON interception-toggles error (#12072) ([#13254](https://github.com/diegosouzapw/OmniRoute/pull/13254)) +- fix(guardrails): stop Vision Bridge from re-selecting a model locked after a 404 (#12111) ([#13259](https://github.com/diegosouzapw/OmniRoute/pull/13259)) +- fix(sse): require Responses-shaped body before native OpenAI-compatible passthrough (#12129) ([#13278](https://github.com/diegosouzapw/OmniRoute/pull/13278)) +- fix(providers): minimax-m3 now collapses manual thinking.type:"enabled" to adaptive, preventing upstream 400 (2013) (#12132) ([#13249](https://github.com/diegosouzapw/OmniRoute/pull/13249)) +- fix(db): scope model visibility overrides by modality so hiding a Chat model no longer hides an identically-ID'd Image/Embeddings/etc. model (#12172) ([#13258](https://github.com/diegosouzapw/OmniRoute/pull/13258)) +- fix(db): scope local-provider apiKey dedup to matching base URL so LM Studio/Ollama-style connections support multiple accounts (#12173) ([#13250](https://github.com/diegosouzapw/OmniRoute/pull/13250)) +- fix(providers): refresh Trae's stale Referer/Origin and forward user timezone so imported connections stop failing with 401 (#12190) ([#13255](https://github.com/diegosouzapw/OmniRoute/pull/13255)) +- fix(providers): route opencode-go/gpt-5.6-luna to /responses instead of /chat/completions (#12196) ([#13275](https://github.com/diegosouzapw/OmniRoute/pull/13275)) +- fix(dashboard): allow deleting the last extra-upstream-header row even when invalid (#12251) ([#13252](https://github.com/diegosouzapw/OmniRoute/pull/13252)) +- **fix(i18n):** translate pre-existing `__MISSING__:` keys for `combo.sort`, `requestLogger.detail` expand/collapse, `common.profile`, and `settings.resilienceCredentialHealth*` across 39 locales ([#12272](https://github.com/diegosouzapw/OmniRoute/issues/12272)) ([#13034](https://github.com/diegosouzapw/OmniRoute/pull/13034)) — thanks @HouMinXi +- fix(cli): run the Node.js runtime compatibility guard before the heavy `tsx/esm` + Commander import chain so an unsupported runtime gets a clear message instead of a raw `Invalid regular expression flags` crash (#12296) ([#13253](https://github.com/diegosouzapw/OmniRoute/pull/13253)) +- fix(dashboard): refresh the providers list after deleting a compatible provider node (#12298) ([#13256](https://github.com/diegosouzapw/OmniRoute/pull/13256)) +- fix(usage): fail closed on API-key budget enforcement when a provider's `auto` routing alias has no pricing row, instead of silently counting it as $0 (#12341) ([#13257](https://github.com/diegosouzapw/OmniRoute/pull/13257)) +- **fix(providers):** include the submitted Agnes video model when polling by `video_id` ([#12356](https://github.com/diegosouzapw/OmniRoute/pull/12356)) — thanks @pacocartones +- **fix(routing):** custom OpenAI-compatible nodes now honor the saved Chat/Responses API type after edits instead of letting the node's original ID prefix override the live connection setting ([#11884](https://github.com/diegosouzapw/OmniRoute/issues/11884)). ([#12358](https://github.com/diegosouzapw/OmniRoute/pull/12358)) — thanks @pacocartones +- fix(sse): surface an error instead of a silent empty 200 when a Claude stream closes with zero bytes (#12398) ([#13285](https://github.com/diegosouzapw/OmniRoute/pull/13285)) +- fix(oauth): warn before the dead localhost:8080 redirect in antigravity/gemini `oauth start` (#12413) ([#13265](https://github.com/diegosouzapw/OmniRoute/pull/13265)) +- fix(providers): stop devin-cli spawn error from double-closing the SSE controller (#12517) ([#13261](https://github.com/diegosouzapw/OmniRoute/pull/13261)) +- **fix(rerank):** clamp Voyage `top_k` to the documents actually sent after empty-string filtering, and honor `return_documents: false` in the NVIDIA response adapter (#12523 — thanks @pacocartones) +- **fix(audio):** `/v1/audio/translations` now resolves combo names the way `/v1/audio/transcriptions` already does, so a combo that `GET /v1/models` advertises is fanned out to its targets instead of being rejected with `400 Invalid translation model: . Use format: provider/model`; literal `provider/model` ids and unknown bare names behave as before (#12536 — thanks @pacocartones) +- **fix(gemini):** strip the JSON-Schema-2020-12 `prefixItems` keyword from Gemini tool schemas at every nesting level, so Claude Code tool definitions no longer fail with `400 Unknown name "prefixItems"` on Gemini models (#12540 — thanks @pacocartones) +- **fix(api-manager):** Expose an accessible loading status while API keys are fetched instead of an empty accessibility tree (#12541 — thanks @pacocartones) +- **fix(video):** Clamp `estimateJpegFrameBytes` at zero for padding-only payloads and build the three encode-side frame data URIs from `JPEG_FRAME_DATA_URI_PREFIX` instead of a repeated literal (#12543 — thanks @pacocartones) +- **fix(devin):** accept Windows `DEVIN_AGENTIC_HOME` sandbox paths (`C:\...\.sandbox\...`) in the isolated-home check so the Devin Claude Bridge no longer fails closed on Windows ([#12405](https://github.com/diegosouzapw/OmniRoute/issues/12405)) (#12545 — thanks @pacocartones) +- **fix(db):** Add `conversation_turn_nodes` and orphaned `agentic_conversations` to the auto-cleanup cycle under the existing `retention.callLogs` window, so identity nodes whose call-log content has already been purged no longer accumulate without bound in `storage.sqlite` (#12548 — thanks @pacocartones) +- **fix(i18n):** Wrap the `~/.claude/profiles//settings.json` placeholder in ICU single quotes in the `OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES` feature-flag description across all 42 locales and the TypeScript default, so next-intl no longer fails with `INVALID_MESSAGE: UNCLOSED_TAG` and the Feature Flags card shows the description instead of the raw key (#12549 — thanks @pacocartones) +- **fix(orchestration):** `updateCloudAgentTask` now publishes the task's real `status` on `agent.task.updated` when an update only touches `result`, `activities` or `error`, instead of the fabricated `"updated"` state, and stays silent when no row matched the id (#12550 — thanks @pacocartones) +- **fix(i18n):** the home "Recent Requests" panel and the Provider Topology legend are now translated instead of rendering English copies on non-English dashboards; the legend reads its own `home.topologyLegend*` labels with consistent casing rather than borrowing the memory-settings "Recent" and analytics "Error" strings (#12551 — thanks @pacocartones). +- **docs(reference):** bring the `FEATURE_FLAGS.md` catalog back to 1:1 with `featureFlagDefinitions.ts` — 20 missing flags added, the two `*_BLOCK_THRESHOLD` env-only knobs moved out of the flag tables, category/total counts and the Live WS port corrected, guarded by a static test (#12552 — thanks @pacocartones) +- **fix(i18n):** backfill missing `usage.kiloPass*` strings in 39 locales and restore `featureFlagOmnirouteDisableThinkingLevelVariantsDescription` in `pt.json` ([#12561](https://github.com/diegosouzapw/OmniRoute/issues/12561)) ([#13027](https://github.com/diegosouzapw/OmniRoute/pull/13027)) — thanks @HouMinXi +- fix(docker): default docker-compose app ports (dashboard/API/live-WS) to loopback instead of `0.0.0.0`, closing the anonymous `/v1` LAN/WAN exposure gap left open by `REQUIRE_API_KEY=false` (#12568) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): close DNS-rebinding SSRF gap in webhook outbound-URL guard (#12569) ([#13243](https://github.com/diegosouzapw/OmniRoute/pull/13243)) +- fix(docker): require a per-session token on the VNC browser CDP bridge and isolate it on a dedicated Docker network (#12571) ([#13241](https://github.com/diegosouzapw/OmniRoute/pull/13241)) +- fix(open-sse): write Adobe Firefly session tokens and cookie jars with 0700/0600 permissions instead of the process umask (#12572) ([#13242](https://github.com/diegosouzapw/OmniRoute/pull/13242)) +- fix(api): remove hardcoded wildcard CORS in openai-to-gemini-sse.ts so the centralized fail-closed CORS gate is the sole source of `Access-Control-Allow-Origin` (#12573) ([#13239](https://github.com/diegosouzapw/OmniRoute/pull/13239)) +- fix(api): enforce API key policy (budget/rate-limit/schedule/endpoint scoping) on the ElevenLabs speech-to-text, text-to-speech and voices proxy routes (#12574) ([#13244](https://github.com/diegosouzapw/OmniRoute/pull/13244)) +- fix(sse): cap HuggingChat NDJSON body size and bound the read loop with the fetch timeout so a stalled or hostile upstream cannot buffer unbounded memory (#12577) ([#13282](https://github.com/diegosouzapw/OmniRoute/pull/13282)) +- fix(docker): scope the cliproxyapi/qdrant/bifrost sidecars to loopback by default and forward `CLIPROXYAPI_MANAGEMENT_KEY` into the cliproxyapi container so its management API is not left both unauthenticated and LAN-published (#12578) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): create DB export temp paths with `fs.mkdtempSync` instead of predictable timestamps (#12579) ([#13237](https://github.com/diegosouzapw/OmniRoute/pull/13237)) +- Cline 401 bodies that say "re-authenticate your Cline account" classify as a refreshable OAuth token, not a terminal expired key. The cooling panel no longer labels every cooldown as a 429; it shows the recorded last error instead. (#12594) ([#13060](https://github.com/diegosouzapw/OmniRoute/pull/13060)) — thanks @HouMinXi +- **fix(catalog):** degrade unknown combo targets instead of dropping LCD modalities, and persist OpenRouter `architecture.input_modalities` into the capability snapshot ([#12613](https://github.com/diegosouzapw/OmniRoute/issues/12613)) ([#13042](https://github.com/diegosouzapw/OmniRoute/pull/13042)) — thanks @HouMinXi +- fix(providers): send `x-api-key` instead of `Authorization: Bearer` for OpenCode Zen's `/v1/responses` endpoint (Muse Spark Contributor models), fixing a 401 on OmniRoute's auth header (#12633) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(sse): surface the actionable "Auggie CLI not found" message when the shell reports a missing `auggie` binary via exit code instead of a spawn error (#12645) ([#13263](https://github.com/diegosouzapw/OmniRoute/pull/13263)) +- **fix(config):** Persist deletions of built-in background-degradation entries — when a stored settings record exists its `degradationMap` is now authoritative instead of being merged under the defaults, so an entry the user removed in the dashboard no longer reappears on the next apply or restart ([#12424](https://github.com/diegosouzapw/OmniRoute/issues/12424)) ([#12647](https://github.com/diegosouzapw/OmniRoute/pull/12647)) — thanks @pacocartones +- **fix(gamification):** action-count badge milestones (First Token, Token Consumer, Token Machine, Token Whale, and the token-sharing tier) are now backed by a durable `xp_action_counts` counter incremented in `addXp()`, instead of a live `COUNT(*)` over `xp_audit_log`. The audit log is pruned by `retention.xpAuditLog` (default 30 days), so on a default install those "lifetime" milestones were really "actions in the last 30 days" and unlocked badges could stop unlocking once old rows aged out. `getActionCount()` and `checkActionCountBadges()` now read the same durable source, and a migration backfills existing totals from the surviving audit rows ([#12546](https://github.com/diegosouzapw/OmniRoute/issues/12546)) ([#12651](https://github.com/diegosouzapw/OmniRoute/pull/12651)) — thanks @pacocartones +- **fix(images):** `/v1/images/edits` now iterates a combo's targets the same way `/v1/images/generations` does (#9239) instead of flattening a bare combo to its first target. A combo whose first target is not edit-capable — or lacks credentials — now falls through to a later edit-capable target rather than hard-erroring, and missing credentials are skipped (not a hard `401`) to match the generations path. The per-target skip/terminal classification is extracted into a shared `runImageComboTargets` loop, so generations behavior is unchanged ([#12547](https://github.com/diegosouzapw/OmniRoute/issues/12547)). ([#12653](https://github.com/diegosouzapw/OmniRoute/pull/12653)) — thanks @pacocartones +- fix(sse): add first-byte watchdog to the TLS-fingerprint transport so a stalled wreq body falls back instead of hanging for minutes (#12656) ([#13272](https://github.com/diegosouzapw/OmniRoute/pull/13272)) +- fix(sse): exempt tiny-budget reasoning probes from combo quality failure and surface persisted-cooldown skips in ALL_TARGETS_SKIPPED diagnostics (#12659) ([#13269](https://github.com/diegosouzapw/OmniRoute/pull/13269)) +- fix(models): declare the real ~1M contextLength for OpenCode Zen's Muse Spark 1.2 models instead of falling back to the 200000 provider default (#12681) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(oauth): align codebuddy-cn OAuth User-Agent with the chat/usage CLI version to avoid WAF false positives (#12702) ([#13264](https://github.com/diegosouzapw/OmniRoute/pull/13264)) +- fix(dashboard): surface an authentication-required banner instead of silently blanking database settings for a guest session (#12709) ([#13270](https://github.com/diegosouzapw/OmniRoute/pull/13270)) +- **fix(resilience):** stop unbounded queue that hangs 6min until Aborted — gate, provider slot, and Bottleneck queue now share a per-connection `maxWaitMs` budget; `fail-closed` on exhaust (503); execution backstop `executionMaxWaitMs` overridable per connection with upstream clamp ([#12715](https://github.com/diegosouzapw/OmniRoute/pull/12715)) — thanks @maxmad64bis (with thanks to @Tushar49 for surfacing the slow-provider need in #12635) +- **fix(auto-combo):** every mode pack now carries `quality` and `reliability` weights (`quality` 0.02, 0.03 in `quality-first`; `reliability` 0.03, 0.04 in `reliability-first`), so selecting a pack no longer silences either signal; `DEFAULT_WEIGHTS` is unchanged ([#12731](https://github.com/diegosouzapw/OmniRoute/pull/12731)) — thanks @maxmad64bis +- **fix(sse):** the provider execution pipeline keeps the upstream error `code`/`type` again — both were lost when the non-streaming leg was extracted, so a config-class `422` (Antigravity missing project) degraded into a generic account cooldown instead of reaching the gate that recognises it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(db):** a legacy `call_logs` table no longer aborts startup — the provider-stats index is created after the column healing runs, not before it, so an install predating `request_type` opens instead of failing with `no such column` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(catalog):** a malformed row in the operator-writable custom-models blob no longer kills every `auto/*` pool ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(tests):** realign the guards that the same day's merges left asserting the old behavior — the provider-count total after GreenPT and EURouter, the GLM stream buffer slot that `#12925` turned into a declared parameter, and the injection scan bound that `#13104` reshaped from a head-only window into head-plus-tail ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- fix(cache): fold tool_choice/tools/response_format into the semantic cache signature so a cached tool_calls response can no longer be replayed for a request whose tool policy forbids it (#12734) ([#13267](https://github.com/diegosouzapw/OmniRoute/pull/13267)) +- **fix(models):** stop treating provider-supplied `isFree:true`/`:free`/`0/0` flags as trusted unless the provider has a documented free tier — only the shipped free-tier catalog decides otherwise; fetched `isFree:true` trusted only for a free-tier provider, custom `isFree:true` via a trusted path ([#12744](https://github.com/diegosouzapw/OmniRoute/pull/12744)) — thanks @maxmad64bis +- fix(memory): authenticate the internal /v1/rerank loopback call so memory reranking no longer silently degrades to unranked order when REQUIRE_API_KEY=true (#12745) ([#13268](https://github.com/diegosouzapw/OmniRoute/pull/13268)) +- fix(sse): parse Ollama Cloud's current usage markup (`$X of $Y used` aria-label, nested width style) (#12749) ([#13260](https://github.com/diegosouzapw/OmniRoute/pull/13260)) +- fix(cli): setup-opencode no longer sends an active context's management token to `/v1/models` when `--api-key`/`OMNIROUTE_API_KEY` is supplied — an explicit flag or the env var now always outranks the context's token, and the flag itself is no longer swallowed by the parent program's global `--api-key` option (#12783) ([#13246](https://github.com/diegosouzapw/OmniRoute/pull/13246)) +- fix(sse): register Arcee AI in the executor provider registry so requests reach api.arcee.ai instead of silently falling back to OpenAI (#12784) ([#13277](https://github.com/diegosouzapw/OmniRoute/pull/13277)) +- **fix(quota):** share one upstream quota read when identical saturation checks arrive at the same time, so traffic bursts don't multiply provider API calls ([#12787](https://github.com/diegosouzapw/OmniRoute/pull/12787)) — thanks @maxmad64bis +- **fix(db):** ignore expired or invalid rate-limit cooldown writes so a stale timestamp can't lock a connection that should be usable — clearing still works as before ([#12788](https://github.com/diegosouzapw/OmniRoute/pull/12788)) — thanks @maxmad64bis +- **fix(providers):** single source for provider order with `xao` ranking alongside `xai-oauth` ([#12790](https://github.com/diegosouzapw/OmniRoute/pull/12790)) — thanks @maxmad64bis +- **fix(routing):** off-table models check the free-model catalog before inheriting premium prices, latency bootstraps from the observed pool median, async tiers read live database pricing with a 90-day freshness gate, and the tier cache invalidates on every pricing write ([#12792](https://github.com/diegosouzapw/OmniRoute/pull/12792)) — thanks @maxmad64bis +- **fix(auto-combo):** snapshot scoring uses the observed per-connection account tier and per-model quality instead of neutral constants, and circuit-open providers rank lower at build time ([#12794](https://github.com/diegosouzapw/OmniRoute/pull/12794)) — thanks @maxmad64bis +- **fix(pool):** empty `auto/*` pools now say why they are empty, and the models listing uses the same paid check as routing ([#12795](https://github.com/diegosouzapw/OmniRoute/pull/12795)) — thanks @maxmad64bis +- fix(routing): recognize CLIProxyAPI's 'unknown provider for model' 400 as fallback-worthy (#12800) ([#13284](https://github.com/diegosouzapw/OmniRoute/pull/13284)) +- **fix(sse):** translate-mode streams now emit the estimated token counts as a trailing usage-only chunk before `[DONE]` when the upstream stays silent, so metered chat clients see totals instead of nothing ([#12828](https://github.com/diegosouzapw/OmniRoute/pull/12828)) — thanks @maxmad64bis +- **fix(db):** provider stats stay truthful on empty and legacy databases: fallback counts default to `0` instead of `null`, latency averages read `null` (not `0`) when no durations were recorded, failures logged before the error-type column existed group under `pre_migration` instead of `unclassified`, and per-provider queries use two new composite indexes ([#12832](https://github.com/diegosouzapw/OmniRoute/pull/12832)) — thanks @maxmad64bis +- fix(nvidia): fail open when a synced model catalog goes stale instead of gating forever (#12849) ([#13248](https://github.com/diegosouzapw/OmniRoute/pull/13248)) +- **fix(db):** WAL maintenance lives in its own module: a `TRUNCATE` checkpoint that hits a busy database now warns with its streak and retries once via `PASSIVE` instead of logging success, and closing-time checkpoints no longer report success on builds without a database file, with the busy totals visible in the authenticated monitoring health payload ([#12853](https://github.com/diegosouzapw/OmniRoute/pull/12853)) — thanks @maxmad64bis +- **fix(health):** quota with no snapshots reads empty instead of a contradicting 0% ([#12857](https://github.com/diegosouzapw/OmniRoute/pull/12857)) — thanks @maxmad64bis +- fix(a2a): accept the dashboard's own session cookie on /a2a so "Run message/send" no longer fails with "Unauthorized: missing or invalid API key" (#12888) ([#13271](https://github.com/diegosouzapw/OmniRoute/pull/13271)) +- **fix(logs):** the Logs grid's in-memory filter pass no longer discards rows the SQL query already matched — selecting an API key from the dropdown (which sends the key's id) returns its calls again, the Combo tab shows every combo instead of only those whose name contains a "1", and the model filter and search cover the same columns as the query ([#12896](https://github.com/diegosouzapw/OmniRoute/pull/12896)) — fixes [#12873](https://github.com/diegosouzapw/OmniRoute/issues/12873) — thanks @ntdatt812 +- **fix(a2a):** `/api/a2a/status` now builds the agent card from the request that asked for it, so a gateway reached at a non-localhost host no longer advertises `http://localhost:20128` as its A2A URL ([#12918](https://github.com/diegosouzapw/OmniRoute/pull/12918)). — thanks @ntdatt812 +- **fix(compression):** progressive aging now appends its `[COMPRESSED:aging:…]` annotation after a turn's `tool_result` blocks instead of in front of them, so Anthropic no longer rejects aged conversations with "`tool_use` ids were found without `tool_result` blocks immediately after" ([#12920](https://github.com/diegosouzapw/OmniRoute/pull/12920)). — thanks @ntdatt812 +- **fix(bedrock):** model import now resolves context limits for every vendor prefix instead of only `anthropic.*`, so `global.openai.gpt-5.6-*` no longer imports with a null `inputTokenLimit` and gets rejected pre-flight at the 200k default ([#12921](https://github.com/diegosouzapw/OmniRoute/pull/12921)). — thanks @ntdatt812 +- **fix(stream):** the 64 KB stream buffer GLM asks for is honoured instead of dropped, and the type error it caused no longer fails the API Route Typecheck gate on every open PR ([#12925](https://github.com/diegosouzapw/OmniRoute/pull/12925)) — thanks @ntdatt812 +- **fix(guardrails):** mask PII inside a `tool_result`'s nested content array, which the masker walked past while redacting its sibling block ([#12930](https://github.com/diegosouzapw/OmniRoute/pull/12930)) — thanks @ntdatt812 +- **fix(dashboard):** the Radar catalog table no longer leaves absent data unexplained — empty limits, unknown context windows, and unreported capabilities each explain themselves on hover, and a new check keeps it that way ([#12937](https://github.com/diegosouzapw/OmniRoute/pull/12937)) — thanks @maxmad64bis +- **fix(sse):** geo-blocked opencode requests rotate to the next account proxy instead of failing, so one refused egress no longer aborts the whole chain ([#12941](https://github.com/diegosouzapw/OmniRoute/pull/12941)) — thanks @maxmad64bis +- **fix(codex):** dashboard "clear cooldown" and the CAS recovery path now drop nested `codexScopeRateLimitedUntil` maps in the same write that nulls `rate_limited_until`, and fresh quota snapshots with headroom lift fallback-sourced scope cooldowns parked by quota preflight ([#12817](https://github.com/diegosouzapw/OmniRoute/issues/12817), [#12860](https://github.com/diegosouzapw/OmniRoute/issues/12860), [#12951](https://github.com/diegosouzapw/OmniRoute/pull/12951)) The dashboard reset-credit button (`consumeCodexResetCredit`) now exercises that same snapshot path after a successful redeem, so a filled quota bar is enough to unpark a leftover fallback Codex child without another manual clear. — thanks @HouMinXi +- **fix(db):** resolve `sql-wasm.wasm` across global npm install and hoisted layouts, ensuring OmniRoute can boot cleanly on Node 24 when native `better-sqlite3` is uncompiled. ([#13035](https://github.com/diegosouzapw/OmniRoute/pull/13035)) — thanks @HouMinXi +- **fix(api):** `DELETE /v1/batches/delete-completed` now sweeps only the calling API key's own completed batches (batches with no owner stay out of a key-scoped sweep on purpose), with an explicit instance-wide mode reserved for authenticated dashboard sessions, a 401 for a presented key that is unknown, revoked, deactivated, banned or expired (never falling through to the session branch), audit logging of every sweep, a sanitized 500 on failure, an owner-scoped file half (a key-scoped sweep never nulls a file another tenant owns) and an atomic sweep — chunked in 200-batch transactions in instance mode — so a mid-way error never leaves a batch pointing at a nulled file (GHSA-wvxc-jp3v-5mg5) ([#12969](https://github.com/diegosouzapw/OmniRoute/pull/12969)) +- **fix(combo):** quota-weighted routing stops drawing on an out-of-credit connection — a 402 now invalidates the stored quota snapshot instead of leaving its stale remaining percentage in place, and a snapshot older than 10 minutes no longer counts as confident headroom for the primary pool ([#12972](https://github.com/diegosouzapw/OmniRoute/pull/12972)) ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- **fix(vision):** Custom Models with "Vision capable" checked no longer have image requests swapped to `glm/glm-4.6v` when the client sends the advertised alias (`vllm/path/...`) or the bare path-shaped id — Vision Bridge now matches the stored override for all three id forms ([#12758](https://github.com/diegosouzapw/OmniRoute/issues/12758)) ([#12974](https://github.com/diegosouzapw/OmniRoute/pull/12974)) — thanks @HouMinXi +- **fix(sse):** transient opencode upstream failures rotate to the next account proxy instead of failing, so one flapping egress no longer aborts the whole chain ([#12975](https://github.com/diegosouzapw/OmniRoute/pull/12975)) — thanks @maxmad64bis +- **fix(azure):** Deployments from GPT-6 onward now send `max_completion_tokens` instead of `max_tokens`, which Azure rejects with HTTP 400. The rule matched a literal `gpt-5`, so each new generation arrived broken; it now matches the generation range, while `gpt-35-turbo` still keeps `max_tokens`. ([#13007](https://github.com/diegosouzapw/OmniRoute/pull/13007)) — thanks @ntdatt812 +- **fix(db):** add an opt-in self-restart circuit for sustained critical memory pressure, gate post-cleanup VACUUM behind a minimum freed-rows threshold, and checkpoint the SQLite WAL every 5 minutes with a size guard that escalates to TRUNCATE, so a growing WAL can no longer stall the event loop into a full outage. ([#13011](https://github.com/diegosouzapw/OmniRoute/pull/13011)) — thanks @HouMinXi +- **fix(auth):** an explicit connection pin may probe a quota-disabled row once and re-enable it on success ([#12874](https://github.com/diegosouzapw/OmniRoute/issues/12874)) ([#13017](https://github.com/diegosouzapw/OmniRoute/pull/13017)) — thanks @HouMinXi +- **fix(api):** thread `X-OmniRoute-Fallback-Attempts` through combo chat completions so streaming and non-streaming responses report how many prior legs were attempted ([#13038](https://github.com/diegosouzapw/OmniRoute/pull/13038)) — thanks @HouMinXi +- Restore provider failure classification and credential refresh on non-streaming requests: classify non-2xx failures to lock models on per-model quota exhaustion, update connection rate limits from headers and body, and pass credential refresh handlers to pipeline execution so 401 tokens can be refreshed and retried (#13043). ([#13069](https://github.com/diegosouzapw/OmniRoute/pull/13069)) — thanks @HouMinXi +- **fix(responses):** wrap forced-non-streaming web_search fallback JSON as Responses SSE so Codex still sees `response.completed` ([#13050](https://github.com/diegosouzapw/OmniRoute/pull/13050)) — thanks @HouMinXi +- **fix(antigravity):** Preserve upstream thought-token usage in normalized completion and reasoning token counts ([#13055](https://github.com/diegosouzapw/OmniRoute/pull/13055)) — thanks @pacocartones +- Fix a concurrency-slot leak in the DeepSeek PoW solver: a worker that failed to spawn (for example a missing worker script) never released its slot, so `MAX_CONCURRENT_WORKERS` failures disabled the solver until restart. ([#13097](https://github.com/diegosouzapw/OmniRoute/pull/13097)) — thanks @anhtahaylove +- **fix(acp):** bound the ACP session output buffers — `stdoutBuffer` and `stderrBuffer` now cap at 1 MiB keeping the most recent output behind a visible `[...output truncated...]` marker, and `stderrBuffer` is reset per prompt instead of accumulating for the lifetime of the session. ([#13100](https://github.com/diegosouzapw/OmniRoute/pull/13100)) — thanks @anhtahaylove +- **fix(acp):** release the `stdout`/`exit` listeners and the idle timer that a `sendPrompt` timeout used to leave attached to the `acpManager` singleton, and drop sessions that exited on their own from the session map instead of keeping them forever. ([#13096](https://github.com/diegosouzapw/OmniRoute/pull/13096)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection and PII scanners now read the text a `tool_result` block carries on `content` (string or nested block list), in messages and in system blocks, so tool output is judged by the same rules as user text ([#13101](https://github.com/diegosouzapw/OmniRoute/pull/13101)) — thanks @ntdatt812 +- **fix(gamification):** close the badge notification SSE stream when the request signal is already aborted before the stream starts — a client that disconnects while the route is still awaiting auth used to leave both the 2s unlock poll and the 15s heartbeat running for the lifetime of the process. ([#13106](https://github.com/diegosouzapw/OmniRoute/pull/13106)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection scan now spends its 16 KB budget on both ends of the request instead of the first 16 KB only, so `system`, `instructions`, `query`, `documents` and the newest turns are no longer hidden behind one long message ([#13104](https://github.com/diegosouzapw/OmniRoute/pull/13104)) — thanks @ntdatt812 +- **fix(dashboard):** expose the Volcano Ark console cookie on quota scraping and unwrap connect-error objects so the dashboard shows the upstream message ([#13107](https://github.com/diegosouzapw/OmniRoute/pull/13107)) — thanks @HouMinXi +- **fix(db):** release the `beforeExit`/`SIGINT`/`SIGTERM` handlers when a `node:sqlite` adapter closes, so a closed adapter and its database handle are no longer pinned to `process` for the lifetime of the run — the same treatment #7494 gave the sql.js adapter. ([#13109](https://github.com/diegosouzapw/OmniRoute/pull/13109)) — thanks @anhtahaylove +- **fix(translator):** `contentSchema` and `unevaluatedItems` are now treated as subschema positions by the tool-schema sanitizer, so a truncation placeholder in either is replaced with a permissive schema instead of being forwarded as a string ([#13110](https://github.com/diegosouzapw/OmniRoute/pull/13110)) — thanks @ntdatt812 +- **fix(cli-helper):** clear the `createLogStream` timeout on the abort path — `stop()` aborts the in-flight fetch and returned through the `signal.aborted` branch, which skipped `clearTimeout` and left an armed timer per stopped stream. The stream reader is now also cancelled when the read loop exits early. ([#13114](https://github.com/diegosouzapw/OmniRoute/pull/13114)) — thanks @anhtahaylove +- **fix(docs):** drop TLS-impersonation, MITM-decrypt, supply-chain attestation, and XOR-mask writeups from the public `/docs` catalog and Docker image. Files stay in git for engineers; operators who need them open the repo, not the website. ([#13136](https://github.com/diegosouzapw/OmniRoute/pull/13136)) — thanks @HouMinXi +- **fix(combo):** parse numeric-epoch `rate_limited_until` in the combo cooldown read path ([#13141](https://github.com/diegosouzapw/OmniRoute/pull/13141)) — thanks @maxmad64bis +- **fix(opencode):** both OpenCode plugins now reject a gateway address typed without `http://` at configuration time, instead of publishing every model with an api url no client can call, and the v2 plugin no longer publishes a model card whose api url is blank or relative ([#13142](https://github.com/diegosouzapw/OmniRoute/pull/13142)) — thanks @maxmad64bis +- **fix(providers):** lock opencode model on upstream 400 model-unavailable ([#13146](https://github.com/diegosouzapw/OmniRoute/pull/13146)) — thanks @maxmad64bis +- **fix(logging):** keep the provider exchange rather than the raw client bodies when a call log exceeds its size budget, and show that recovered payload in the request-detail panel instead of replacing it with the stored response body ([#13147](https://github.com/diegosouzapw/OmniRoute/pull/13147)) — thanks @maxmad64bis +- **fix(traffic-inspector):** the WebSocket route no longer leaks a traffic-buffer subscriber and a 30s ping timer when the client socket is already closed at handler time — listeners are attached before any resource is acquired, a destroyed socket bails out early, and the ping interval stops on a dead socket where `write()` never throws ([#13155](https://github.com/diegosouzapw/OmniRoute/pull/13155)) — thanks @anhtahaylove +- **fix(telegram):** bound the per-user API key cache in the Telegram chat proxy so a burst of distinct chat ids can no longer grow the process heap without limit ([#13165](https://github.com/diegosouzapw/OmniRoute/issues/13165)) ([#13166](https://github.com/diegosouzapw/OmniRoute/pull/13166)) — thanks @anhtahaylove +- **fix(stream):** cancel the upstream response body when the JSON-to-SSE sniff unwinds on a body timeout, so a stalled upstream no longer pins the connection ([#13169](https://github.com/diegosouzapw/OmniRoute/issues/13169)) ([#13171](https://github.com/diegosouzapw/OmniRoute/pull/13171)) — thanks @anhtahaylove +- **fix(telegram):** authenticate webhook deliveries with Telegram's `secret_token` so an unauthenticated caller can no longer mint API keys or spend upstream quota ([#13172](https://github.com/diegosouzapw/OmniRoute/issues/13172)) ([#13175](https://github.com/diegosouzapw/OmniRoute/pull/13175)) — thanks @anhtahaylove +- **fix(models):** give discoverable Gemini 3.8 Flash ids their own 65536 output spec so Antigravity no longer clamps them to 16384 ([#13195](https://github.com/diegosouzapw/OmniRoute/pull/13195)) — thanks @HouMinXi +- **fix(dashboard):** leftover catalog-removed provider rows (gemini-cli) can be listed and purged from the providers page ([#13067](https://github.com/diegosouzapw/OmniRoute/issues/13067)) ([#13197](https://github.com/diegosouzapw/OmniRoute/pull/13197)) — thanks @HouMinXi +- **fix(auth):** a dashboard session now requires the `authenticated: true` claim that login, OIDC and the session refresh already emit — a JWT merely signed with `JWT_SECRET` (for example the Cursor CLI passthrough token, which any API-key holder can obtain) no longer verifies as the `auth_token` cookie on any route, the WebSocket handshake or the live server; existing sessions keep working ([#13298](https://github.com/diegosouzapw/OmniRoute/issues/13298)) ([#13375](https://github.com/diegosouzapw/OmniRoute/pull/13375)) +- **fix(api):** `DELETE /v1/batches/delete-completed` now applies the caller's API-key policy (endpoint allowlist, schedule, usage cap, rate limit) like every other `/v1` route, commits the key-scoped sweep per 200-batch chunk instead of holding one write lock for the whole sweep, refuses to loop without progress, and rejects a scope that names both a key and `allTenants` ([#13684](https://github.com/diegosouzapw/OmniRoute/pull/13684)) +- Restore the API-route typecheck gate: the non-streaming leg result lost its discriminated-union narrowing after the server-owned tool loop reassignment, producing 13 new TS2339 diagnostics in `chatCore.ts`. ([#12963](https://github.com/diegosouzapw/OmniRoute/pull/12963)) +- Restore four non-streaming behaviours the server-owned tool loop refactor dropped: client aborts map to 499 with the fixed `Request aborted` message, an aborted request no longer logs a synthetic `clientResponse`, Claude prompt-cache telemetry is recorded again, and a body that cannot be canonicalized no longer throws when the tool loop is off. ([#12990](https://github.com/diegosouzapw/OmniRoute/pull/12990)) +- **fix(skills):** The CLI registry parser now reads positionals declared with `.addArgument()`, not only those written inline in `.command()`. `tunnel create [type]` was being published as `tunnel create`, so the agent-skills sync gate reported drift on every branch and regenerating would have deleted the argument. ([#13009](https://github.com/diegosouzapw/OmniRoute/pull/13009)) — thanks @ntdatt812 +- **fix(combos):** dashboard combo test uses a short prompt, serial probes, and a 60s timeout so reasoning models and rate-limited free pools do not fail the health check ([#13001](https://github.com/diegosouzapw/OmniRoute/pull/13001)) — thanks @HouMinXi +- fix(compression): terminate idle worker threads on eviction so long-running instances stop leaking OS threads and MessagePorts ([#13091](https://github.com/diegosouzapw/OmniRoute/pull/13091)) — thanks @anhtahaylove +- Redact Google API keys of any length in error bodies: the pattern required exactly 39 characters, so shorter or longer `AIza…` credentials passed through unredacted. ([#12964](https://github.com/diegosouzapw/OmniRoute/pull/12964)) +- **fix(grok-cli):** a 402 "Grok Build usage balance exhausted" parks that Grok login as out of credit (Grok Build CLI, grok.com cookie, and xAI OAuth share the weekly pool). Combo routing then tries the next login instead of locking the model for every account in the pool ([#13061](https://github.com/diegosouzapw/OmniRoute/pull/13061)) — thanks @HouMinXi +- fix(compression): spawn the LLMLingua worker with a file URL object so compression actually runs instead of silently failing open on Node ([#13093](https://github.com/diegosouzapw/OmniRoute/pull/13093)) — thanks @anhtahaylove +- **fix(test):** run the local `test` and `test:unit` scripts at concurrency 4 so a full-suite run no longer exhausts the machine's commit charge and kills unrelated processes ([#13187](https://github.com/diegosouzapw/OmniRoute/pull/13187)) — thanks @anhtahaylove +- **fix(monitoring):** `GET /api/monitoring/health` `credentialHealth` now includes a bounded `failedConnections` list (`connectionId`, `status`, sanitized `lastError`) when the probe-cache gauge `failed>0`, plus `source: probe-cache` and a cheap `staleDbNonOkCount` for sticky SQLite `test_status` on active rows. Documents that the live gauge is not `provider_connections.test_status`. ([#12876](https://github.com/diegosouzapw/OmniRoute/pull/12876)) — thanks @RaviTharuma +- **fix(resilience):** an openai-compatible multi-upstream gateway no longer marks the whole connection `credits_exhausted` when a single upstream (or the `/models` / representative-model health probe) returns 402. Quota failures stay model-scoped; true connection-wide auth failures are unchanged. Single-credential 402 key disable ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239)) is preserved. ([#12875](https://github.com/diegosouzapw/OmniRoute/pull/12875)) — thanks @RaviTharuma +- **fix(dashboard):** nine fixes on the Orchestration Canvas, all diagnosed in the Phase 2 reviews. A source that fails now keeps the timestamp of its FIRST failure instead of being re-stamped every poll — the stale line said "since the last poll" no matter how long the source had been down, and the churn also defeated the snapshot's stable identity (it serializes the source list), so the canvas re-rendered on every tick while anything was broken. A source that HAD data and only then started failing is flagged too: previously only a source with no node at all got the warning, so a source that went down mid-session kept a healthy-looking node forever. The rest are pointwise: clicking a filter chip cancels the pending search debounce (left armed, it fired ~300ms later and silently reverted the chip); the search input carries an accessible name; `?state=running, failed` parses like the unpadded form instead of dropping the padded value; the CSV toggle helper is defined once in `model/urlParams.ts` rather than twice; the agents tab tells "nothing running" apart from "the filter matched nothing", offering a clear-filters button instead of setup links that would be wrong advice there; edges stop emitting SMIL particles above 40 simultaneously active edges, keeping the colored stroke; and the drawer's error banner clears when a retried action succeeds. ([#12983](https://github.com/diegosouzapw/OmniRoute/pull/12983)) +- **fix(i18n):** the nine locales added with the EU-language batch (Greek, Estonian, Irish, Croatian, Lithuanian, Latvian, Maltese, Slovenian, Serbian) were missing the eleven Orchestration Canvas keys that Phase 3 introduced, so the compare-runs panel and the "no runs match these filters" empty state fell back to English in those languages (`deepMergeFallback` substitutes English for an absent key, so nothing rendered blank — it rendered untranslated). The coverage gate does not catch this: it enforces an 80% floor per locale, and eleven missing keys out of ~13,000 leaves coverage at 99.9%. Translated for real in each language, calibrated against the wording each file already uses for "run", "filter" and "skill". ([#13294](https://github.com/diegosouzapw/OmniRoute/pull/13294)) +- **fix(dashboard):** five follow-ups from the Phase 2 Orchestration Canvas review. The History drawer now shows the memory section for runs that already left the live TTL window: the persisted `memory_hits` event is parsed into `metadata.memoryHits` with the same defensive validation the drawer applies, and — because that event is observability rather than a state transition — it no longer leaks into the timeline, where it had been inheriting the task's state and rendering as a duplicate transition. The A2A memory recall runs against its own 1.5s deadline instead of inheriting the memory backend's 30s one; overshooting degrades exactly like any other recall failure (no hits, task proceeds), and the timer is cleared on both paths. Repeating a Conductor run carries its `requirements.cli`/`requirements.model` forward, so the new run is pinned to the same runner profile and model rather than drifting to whatever the fleet picks. A successful repeat from the Agents tab now focuses the run it created, instead of leaving the operator on the finished one. And the auth test for `POST /api/conductor/tasks` moved into the shared `ROUTES` array rather than restating the pattern. ([#12988](https://github.com/diegosouzapw/OmniRoute/pull/12988)) +- fix(plugins): stop leaking an exit listener per plugin hook timeout, which triggered MaxListenersExceededWarning on plugins that ignore SIGTERM ([#13092](https://github.com/diegosouzapw/OmniRoute/pull/13092)) — thanks @anhtahaylove +- **fix(validation):** Provider node edits no longer fail with a generic "Invalid request" when the optional daily-quota reset fields are left blank. The dashboard sends `dailyQuotaResetTimezone` and `dailyQuotaResetHour` as `null`, and only the hour accepted it. ([#13066](https://github.com/diegosouzapw/OmniRoute/issues/13066)) ([#13083](https://github.com/diegosouzapw/OmniRoute/pull/13083)) — thanks @ntdatt812 +- **fix(combo):** quota-aware expansion drops banned, inactive, missing, and wrong-provider connections before quota fetch or model dispatch; pins and allowlists stay selectors, not a bypass. Antigravity automatic exhaustion now requires a reported zero remaining, so a positive balance below 1% stays eligible. ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- Fix an intermittent failure in the quota-weighted routing test suite: scores are a function of `Date.now()`, so peers with identical quota scored microseconds apart never tied and swapped order. ([#12946](https://github.com/diegosouzapw/OmniRoute/pull/12946)) +- fix(volcengine): resolve quota window mapping gaps, safe multi-connection console binding, and response cookie sanitization ([#12950](https://github.com/diegosouzapw/OmniRoute/pull/12950)) — thanks @HouMinXi +- **fix(test):** resolve the WebDAV handler path with `fileURLToPath` so the suite's 37 WebDAV tests run on Windows instead of failing with a doubled `C:\C:\` drive prefix ([#13196](https://github.com/diegosouzapw/OmniRoute/pull/13196)) — thanks @anhtahaylove +- **fix(ui):** fall back from invalid AnySearch icon alias (direct commit `9587e07b69`) +- **fix(sse):** stop rebuilding a truncated summary from the collector's cap-dropped event array ([#12718](https://github.com/diegosouzapw/OmniRoute/pull/12718)) — thanks @hartmark +- **fix(conversations):** show a pending spinner for unresolved tool nodes instead of "(empty)" ([#12727](https://github.com/diegosouzapw/OmniRoute/pull/12727)) — thanks @hartmark +- **fix(streaming):** fail fast when an upstream stream produces only lifecycle/heartbeat events, never real content ([#12741](https://github.com/diegosouzapw/OmniRoute/pull/12741)) — thanks @hartmark +- **fix(responses-continuation):** bridge the write-in-flight window with an in-memory pending store ([#12854](https://github.com/diegosouzapw/OmniRoute/pull/12854)) — thanks @hartmark +- **fix(translator):** drop or map agent_message on Chat Completions fallback ([#12880](https://github.com/diegosouzapw/OmniRoute/pull/12880)) — thanks @RaviTharuma +- **fix(api):** page and stream-complete GET /v1/models for large catalogs ([#12882](https://github.com/diegosouzapw/OmniRoute/pull/12882)) — thanks @RaviTharuma +- **fix(quota):** align AUTH window labels with usage API and clarify 503 ([#12884](https://github.com/diegosouzapw/OmniRoute/pull/12884)) — thanks @RaviTharuma +- **fix(ci):** clear two base-reds on release/v3.8.51 — mutation-coverage gate + image-only-model guard ([#12945](https://github.com/diegosouzapw/OmniRoute/pull/12945)) +- **fix(api):** scope batch bulk-delete to the calling API key ([#13211](https://github.com/diegosouzapw/OmniRoute/pull/13211)) +- **fix(tests):** align model-sync log assertion to shipped path redaction (base-red) ([#13227](https://github.com/diegosouzapw/OmniRoute/pull/13227)) +- **fix(test):** make the ToS heading guard actually require the parentheses ([#13228](https://github.com/diegosouzapw/OmniRoute/pull/13228)) +- **fix(quality):** register 4 drifted covering tests in stryker tap.testFiles ([#13229](https://github.com/diegosouzapw/OmniRoute/pull/13229)) +- **fix(logging):** mask *-api-key headers in request-log pipeline (#13273) ([#13401](https://github.com/diegosouzapw/OmniRoute/pull/13401)) — thanks @KooshaPari +- **fix(tests):** correct stale Jina catalog prefix in custom-models test ([#13403](https://github.com/diegosouzapw/OmniRoute/pull/13403)) — thanks @KooshaPari +- **fix(backend):** prune old backups after health-check-repair snapshots (#13308) ([#13404](https://github.com/diegosouzapw/OmniRoute/pull/13404)) — thanks @KooshaPari +- **fix(backend):** guard memory_fts_au trigger with WHEN clause to stop FTS bloat (#13326) ([#13405](https://github.com/diegosouzapw/OmniRoute/pull/13405)) — thanks @KooshaPari +- **fix(free-tiers):** remove obsolete Together $25 signup credit (#12526) ([#13407](https://github.com/diegosouzapw/OmniRoute/pull/13407)) — thanks @KooshaPari +- **fix:** skip non-chat generation models in Test all models ([#13409](https://github.com/diegosouzapw/OmniRoute/pull/13409)) — thanks @KooshaPari +- **fix(providers):** de-duplicate agy and antigravity model catalogs ([#13410](https://github.com/diegosouzapw/OmniRoute/pull/13410)) — thanks @KooshaPari +- **fix(dashboard):** send provider in body for mini-playground web search ([#13411](https://github.com/diegosouzapw/OmniRoute/pull/13411)) — thanks @KooshaPari +- **fix(resilience):** add CJK quota-exhaustion patterns to 429 classifier ([#13412](https://github.com/diegosouzapw/OmniRoute/pull/13412)) — thanks @KooshaPari +- **fix(backend):** eval runner: failed calls no longer score as passed + dotAll regex ([#13413](https://github.com/diegosouzapw/OmniRoute/pull/13413)) — thanks @KooshaPari +- **fix(backend):** eval runner opts out of output-style and memory injection ([#13414](https://github.com/diegosouzapw/OmniRoute/pull/13414)) — thanks @KooshaPari +- **fix:** chain serial quarantine step in npm test script (fixes #13286) ([#13418](https://github.com/diegosouzapw/OmniRoute/pull/13418)) — thanks @KooshaPari +- **fix(antigravity):** derive stable session ID from credentials for prompt-cache reuse (#13305) ([#13424](https://github.com/diegosouzapw/OmniRoute/pull/13424)) — thanks @KooshaPari +- **fix(memory):** merge memory into top-level system field instead of unshifting at messages[0] (#13425) ([#13427](https://github.com/diegosouzapw/OmniRoute/pull/13427)) — thanks @KooshaPari +- **fix(compression):** preserve instruction blocks from lossy engines ([#13523](https://github.com/diegosouzapw/OmniRoute/pull/13523)) — thanks @KooshaPari +- **fix(compression):** preserve polarity words and newlines in ultra heuristic ([#13524](https://github.com/diegosouzapw/OmniRoute/pull/13524)) — thanks @KooshaPari +- **fix(compression):** run user preservePatterns before built-in patterns (#13457) ([#13525](https://github.com/diegosouzapw/OmniRoute/pull/13525)) — thanks @KooshaPari +- **fix(reasoning):** expand deepseek-v4 regex to match v4.1+ and add command-code provider (#13430) ([#13528](https://github.com/diegosouzapw/OmniRoute/pull/13528)) — thanks @KooshaPari +- **fix(providers):** add alwaysReasons metadata for reka-flash-3 ([#13531](https://github.com/diegosouzapw/OmniRoute/pull/13531)) — thanks @KooshaPari +- **fix(playground):** allow Compare columns to scroll independently ([#13532](https://github.com/diegosouzapw/OmniRoute/pull/13532)) — thanks @KooshaPari +- **fix(security):** empty whitelist allows through to prevent admin lockout ([#13534](https://github.com/diegosouzapw/OmniRoute/pull/13534)) — thanks @KooshaPari +- **fix(tests):** add DISABLE_IOREG_STRATEGY env var for macOS test compatibility (fixes #13467) ([#13539](https://github.com/diegosouzapw/OmniRoute/pull/13539)) — thanks @KooshaPari +- **fix(zed-hosted):** map developer role and Gemini enums for Zed proxy ([#13543](https://github.com/diegosouzapw/OmniRoute/pull/13543)) — thanks @KooshaPari +- **fix(analytics):** normalize provider aliases to canonical id in usage_history ([#13545](https://github.com/diegosouzapw/OmniRoute/pull/13545)) — thanks @KooshaPari +- **fix(combo):** use traceId for call log id to prevent UNIQUE constraint failure ([#13546](https://github.com/diegosouzapw/OmniRoute/pull/13546)) — thanks @KooshaPari +- **fix(auth):** check model lockout before returning synthetic noauth connection ([#13547](https://github.com/diegosouzapw/OmniRoute/pull/13547)) — thanks @KooshaPari +- **fix(guardrails):** skip credential redaction for base64 image data URLs ([#13550](https://github.com/diegosouzapw/OmniRoute/pull/13550)) — thanks @KooshaPari +- **fix(resilience):** increase requestQueue.maxWaitMs default from 15s to 30s ([#13553](https://github.com/diegosouzapw/OmniRoute/pull/13553)) — thanks @KooshaPari +- **fix(docs):** document OMNIROUTE_READY_TIMEOUT_MS and allowlist the test-only DISABLE_IOREG_STRATEGY ([#13692](https://github.com/diegosouzapw/OmniRoute/pull/13692)) + ### 📝 Maintenance ---- +- **docs(readme):** reconcile live v3.8.50 provider, free-tier, CLI, routing, test, community, sponsor, acknowledgment, and SVG metrics with their audited source denominators, including a deduplicated OmniRoute-in-Action snapshot and distinct contributor rankings for merged pull requests, GitHub-attributed commits, and Git history ([#11356](https://github.com/diegosouzapw/OmniRoute/pull/11356)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **docs:** sync the canonical provider count 354 → 356 across `README.md`, `AGENTS.md`, `llm.txt` (+ 42 i18n mirrors), the four README SVG diagrams, `docs/reference/PROVIDER_REFERENCE.md` (regenerated) and the `package.json` description after Opper (#11629) and 1min.ai (#11631) boarded the catalog — closes the `check:docs-counts-sync` strict drifts that kept `release/v3.8.51` red ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11673](https://github.com/diegosouzapw/OmniRoute/pull/11673)) — thanks @jonlwheat2-gif +- **test(providers):** update count-derived assertions after the v3.8.51 provider additions — `APIKEY_PROVIDERS` 233 → 235 (Opper #11629 + 1min.ai #11631), reserved-prefix REGISTRY walk 395 → 398, `WEB_FETCH_PROVIDERS` now includes `nimble-search` (#11620), and the provider translate-path golden snapshot regenerated ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11674](https://github.com/diegosouzapw/OmniRoute/pull/11674)) — thanks @jonlwheat2-gif +- **test(sse):** bump the hard-lease connection-query inventory for `src/lib/tokenHealthCheck.ts` to 2 — the verify-only web-cookie sweep added by #11495 split the single `getProviderConnections` call into oauth + cookie variants, which the frozen inventory had not tracked ([#11495](https://github.com/diegosouzapw/OmniRoute/pull/11495)). ([#11677](https://github.com/diegosouzapw/OmniRoute/pull/11677)) — thanks @jonlwheat2-gif / @oyi77 +- **chore(release):** clear the `release/v3.8.51` base-red gates — docs migration count (+38 i18n mirrors), stryker `tap.testFiles`, lockfile registry host, stale ESLint suppressions, the 7 lint regressions they were hiding, the stale `APIKEY_PROVIDERS` partition count (231 → 233), the `glm-5.3-max` tier missing from the GLM test table, a dead `modelType === "chat"` comparison in the models catalog, and the `resolveLiveWsUrl` / `sanitizeLiveWsPort` helpers from #11388 that a later merge dropped from `wsPath.ts` (dashboard live WebSocket URL resolution) ([#11502](https://github.com/diegosouzapw/OmniRoute/pull/11502)) — thanks @MumuTW +- **chore(free-models):** the free-tier regime table now declares which totals figure each regime feeds and whether it may take the no-auth shortcut, so the sets that used to repeat those answers by hand are derived from it — a new `freeType` no longer compiles until it has answered every question, instead of silently contributing to no total ([#11537](https://github.com/diegosouzapw/OmniRoute/pull/11537)) — thanks @maxmad64bis +- **docs(routing):** LKGP strategy copy now states provider-level stickiness and the explicit rules fallback instead of promising per-target pinning ([#11555](https://github.com/diegosouzapw/OmniRoute/pull/11555)) — thanks @maxmad64bis +- **test(chatcore):** pin the response tool-name alias resolution against the ordering hazard that broke every Gemini/Antigravity MCP tool call in v3.8.49. `extractRequestToolIdentityMap` deletes `translatedBody._toolNameMap`, so the later read is always undefined and the ledger survives only through the `requestToolIdentityMap` fallback — removing that fallback previously left the entire tool-name suite green. The resolution moves into `resolveResponseToolNameMap()` next to the map it depends on, with a regression guard that fails without the recovery. No behaviour change. ([#11575](https://github.com/diegosouzapw/OmniRoute/pull/11575)) — thanks @amartinawi +- **docs(env):** document `OMNIROUTE_CORPUS_CACHE_SIZE` in `.env.example` and `ENVIRONMENT.md`, so the env/docs contract check stops failing Docs Gates on every branch ([#11576](https://github.com/diegosouzapw/OmniRoute/pull/11576)) — thanks @ntdatt812 +- **chore(stryker):** register `route-guard-tunnel-processes-local-only` and `universal-quota-aware-routing` in `tap.testFiles`, so their mutant kills count for the three modules they cover ([#11577](https://github.com/diegosouzapw/OmniRoute/pull/11577)) — thanks @ntdatt812 +- **test(authz):** pin `LOCAL_ONLY_API_GET_EXEMPTIONS` by exact membership instead of by entry count, so the guard names the offending path and also catches a substitution ([#11580](https://github.com/diegosouzapw/OmniRoute/pull/11580)) — thanks @ntdatt812 +- **test(build):** pin `resolveNextBuildBundlerFlag` to the `OMNIROUTE_USE_TURBOPACK` contract it implements, across runtimes, instead of asserting an unimplemented Bun override ([#11583](https://github.com/diegosouzapw/OmniRoute/pull/11583)) — thanks @ntdatt812 +- **test(claude-web):** the Claude Web transport test's barrel anchors no longer pin the exact construction spelling. #11421 made the executor map lazy, so `/"claude-web": new ClaudeWebExecutor\(\)/` stopped matching and the test went red on `release/v3.8.51` itself — a base-red gate every open PR inherited. The anchors now match the alias key and the class name and tolerate either spelling in between, and were checked to still fail when an alias is genuinely repointed at another executor ([#11585](https://github.com/diegosouzapw/OmniRoute/pull/11585)) — thanks @ntdat812 +- **test(models):** pin `qwen3.8-max` resolution per provider — two of the four catalogs now serve the bare id and no longer carry `-preview` ([#11590](https://github.com/diegosouzapw/OmniRoute/pull/11590)) — thanks @ntdatt812 +- **test(executors):** drop the stale 128-tool truncation assertion that contradicted the #11444 pass-through contract ([#11591](https://github.com/diegosouzapw/OmniRoute/pull/11591)) — thanks @ntdatt812 +- **test(token-health):** pin the current terminal-skip boundary — expired with retry budget is probed, `account_deactivated` stays skipped ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)) — thanks @ntdatt812 +- **test(models):** follow #11503's retarget of the `gemini-3-pro-high` alias to the hyphenated catalog id ([#11593](https://github.com/diegosouzapw/OmniRoute/pull/11593)) — thanks @ntdatt812 +- **refactor(video bridge):** extract per-video acquisition, whole-result caching, description, and metrics/abort/cleanup out of `VideoBridgeGuardrail.preCall` into a `processVideoPart` seam in a new `videoBridgePipeline.ts`, behind explicit `VideoMediaBrokerPort`, `VideoAudioTranscriptionPort`, and `VideoDrilldownPort` boundaries; `preCall` now only handles request traversal, policy, and response aggregation. The Video tab's FFmpeg/ffprobe runtime status is now an explicit `unknown` / `restricted` / `unavailable` / `available` state instead of a nullable boolean pair, fixing a case where an in-flight or failed probe was mislabeled as "install FFmpeg" ([#11657](https://github.com/diegosouzapw/OmniRoute/issues/11657)). ([#12016](https://github.com/diegosouzapw/OmniRoute/pull/12016)) +- **test(opencode-plugin):** add unit test coverage for `formatFreeBudget()` naming helper ([#11660](https://github.com/diegosouzapw/OmniRoute/pull/11660)) — thanks @f9td56dbgh-hub +- **docs(video):** clarify that the Video Bridge transcript `source` field (`client`, `embedded`, `audio-bridge`) is presently caller-declared and not yet server-verified — OmniRoute enforces the enum shape but does not cryptographically confirm that an `embedded`/`audio-bridge` label came from a server-owned extraction ([#11661](https://github.com/diegosouzapw/OmniRoute/issues/11661)). ([#12001](https://github.com/diegosouzapw/OmniRoute/pull/12001)) +- **chore(providers):** retire the distributed Raycast Relay and Hailuo Web integrations after a provenance audit confirmed substantial derivation from GPL-3.0 sources; the official GitHub and MiniMax API providers remain available. Migration 163 disables legacy connections and invalidates their active leases while preserving connection IDs, audit/history rows, quota snapshots, usage records, and fail-closed API-key connection policies. ([#11691](https://github.com/diegosouzapw/OmniRoute/pull/11691)) +- **chore(providers):** retire the Felo Web provider while its GPL-derived provenance remains on licensing HOLD ([#11698](https://github.com/diegosouzapw/OmniRoute/pull/11698)) — stale runtime and database references now fail closed while existing connection, lease, and usage history remains preserved for auditability. +- **fix(sse):** retire the Gemini Web image-generation bridge while provenance and licensing remain on HOLD, preserving Gemini Web chat/auth/tools and legitimate Gemini/Nano Banana image providers ([#11708](https://github.com/diegosouzapw/OmniRoute/pull/11708)) +- **chore(audio):** retire the built-in EdgeTTS provider and its 11-voice catalog while provenance/licensing review remains on HOLD; the generic `/v1/audio/speech` endpoint and the other speech providers remain available ([#11711](https://github.com/diegosouzapw/OmniRoute/pull/11711)) +- **fix(providers):** retire the unofficial Qwen Web integration and its `qwen-web`/`qw` identities while third-party provenance remains on HOLD ([#11713](https://github.com/diegosouzapw/OmniRoute/pull/11713)) — direct requests now fail closed with a sanitized HTTP 410 and combos skip the retired target, while migration guards preserve connection IDs, allowlists, usage/audit history and invalidate active leases. Supported Qwen API, Qwen Code, cloud, token-plan, audio, local and model-name surfaces remain available; restoring Qwen Web requires a qualified provenance/license decision. +- **fix(providers):** retire `microsoft-designer-web` and its `msdesigner` alias from runtime and catalogs while preserving stored credentials, provider history and allowlists as inactive records — source dependency on gpt4free's [`MicrosoftDesigner.py`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/g4f/Provider/needs_auth/MicrosoftDesigner.py) is proven at snapshot `0943d16eef757a44deb60651c07bbe5fa958cafe`, whose [`LICENSE`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/LICENSE) is GPL-3.0; expressive adaptation is probable, which records provenance/licensing risk without asserting a legal conclusion ([#11720](https://github.com/diegosouzapw/OmniRoute/pull/11720)). +- **docs(legal):** centralize pinned MIT notices for the vendored GCF codec and local flag assets ([#11726](https://github.com/diegosouzapw/OmniRoute/pull/11726)) +- Split the 2,952-line `adobeFireflyClient.ts` into nine focused modules (catalog, credentials, payload, ARP session, upload, responses, IMS, account, poll) behind an unchanged barrel — no consumer edits and no public API change (#11728). — thanks @MumuTW +- **fix(dashboard):** replaced the Model Lockout card's unprovenanced notification MP3 with a short, locally synthesized Web Audio chime ([#11731](https://github.com/diegosouzapw/OmniRoute/pull/11731)) — both toggles retain optional audible feedback without shipping replacement media or third-party code, and unsupported, suspended, or rejected audio contexts fail open without blocking the setting change. +- **chore(deepseek-web):** remove the provenance-unresolved DeepSeek PoW WASM binary and its runtime loader/tracing while retaining the existing JavaScript solver (slower at high difficulty) ([#11732](https://github.com/diegosouzapw/OmniRoute/pull/11732)). +- **chore(assets):** remove four unused provider assets whose redistribution provenance could not be verified while retaining the active Freebuff SVG ([#11735](https://github.com/diegosouzapw/OmniRoute/pull/11735)) +- **docs(assets):** retired two raster images whose redistribution provenance could not be verified ([#11736](https://github.com/diegosouzapw/OmniRoute/pull/11736)) — PicoClaw documentation now reuses the existing SVG, while Zoo Code remains a text link without an image. +- **docs(legal):** add pinned notices for six LobeHub-derived provider SVGs and 65 byte-exact theSVG assets, retaining registry claims as unverified metadata with explicit trademark and HOLD boundaries ([#11737](https://github.com/diegosouzapw/OmniRoute/pull/11737)). +- **chore(quality):** added a versioned 225/225 provider-asset provenance manifest and blocking drift gate ([#11743](https://github.com/diegosouzapw/OmniRoute/pull/11743)) — the inventory pins magic MIME and SHA-256 for every physical file, records immutable source matches for 71 assets, keeps 69 probable and 85 unresolved entries explicit but non-blocking, and treats provenance as source evidence rather than copyright or trademark clearance. +- **refactor(mitm):** replace four previously derived Traffic Inspector utilities — conversation normalization, SSE merging, secret masking, and header sanitization — with independent clean-room implementations based on public provider schemas, WHATWG SSE, and RFC 6750, while keeping the two independently classified passthrough surfaces unchanged ([#11748](https://github.com/diegosouzapw/OmniRoute/pull/11748)). +- Added a unit test that fails seven days before any dated pack under `config/` (`validUntil` and sibling keys) lapses, naming the file and key. The Alibaba free-tier pack expired on 2026-08-27 and turned every PR red the next morning with no commit involved; renewal now happens on someone's terms, not the clock's. ([#11891](https://github.com/diegosouzapw/OmniRoute/pull/11891)) +- `check:workflows` now fails (under `--strict`/`--ratchet`) when any job routed to a self-hosted runner publishes with `--provenance` — npm rejects that with `422` at the registry, which in v3.8.50 only surfaced after the tag and Docker images were public. ([#11895](https://github.com/diegosouzapw/OmniRoute/pull/11895)) +- `scripts/ops/runner-janitor.sh` now proves a path is idle with one `lsof` snapshot and removes stale leftovers itself (tmpfs after 3 h — it is RAM — disk after 24 h), kills orphan `next-build` processes, prunes checkouts of stopped runners, and alerts on memory pressure; `--dry-run` shows exactly what it would do. `docs/ops/RUNNER_BOX.md` reconciled to the measured box (31 GB, 10 listeners). ([#11893](https://github.com/diegosouzapw/OmniRoute/pull/11893)) +- The `next-build` artefact (1.3 GB) is now written and read under `$RUNNER_TEMP` (per-runner, on disk) instead of `/tmp`, which on the self-hosted pool is a 12 GB tmpfs in RAM. Landing it there took 27–32 of the publish job's 76 minutes, and the fixed `/tmp/e2e-build.tar.gz` name let E2E jobs on different runners overwrite each other's download. ([#11896](https://github.com/diegosouzapw/OmniRoute/pull/11896)) +- The CI `build` job now runs in two concurrency lanes — `main` and pull requests — so a release build is never queued behind (or OOM-killed beside) PR builds on the self-hosted pool, which holds one `next-build` comfortably and two at the edge. ([#11901](https://github.com/diegosouzapw/OmniRoute/pull/11901)) +- Re-freeze the ESLint suppressions on `release/v3.8.51` from a clean-room measurement (2 stale file entries pruned, 55 pre-existing `no-explicit-any` in six new files frozen under #11924) and drop the dead `GPT_SIZE_MAP` constant orphaned by the Adobe Firefly client split, so `No new ESLint warnings` stops failing every PR with exit 2 (Refs #11924) ([#11955](https://github.com/diegosouzapw/OmniRoute/pull/11955)) +- Type the 55 `no-explicit-any` sites that had been frozen under #11924 — four redundant casts in `socksConnectorWithFamily.ts` (undici/socks types already accept them) and the mocks/fixtures of the socks-timeout and isFree suites — and drop their suppression entries; the ESLint ratchet shrinks from 5487 to 5432 (Closes #11924) ([#11975](https://github.com/diegosouzapw/OmniRoute/pull/11975)) +- Take the two hosted-runner builds off the PR rail: `Build App` (`build.yml`) is `workflow_dispatch`-only and `DAST smoke (PR)` runs only for PRs into `main` — the 7 GB hosted VM cannot build `release/v3.8.51` in any profile (19/30 red, VM shutdown ~8 min into `next build`) and both checks had turned into permanent noise on every release PR; the bundle stays validated by `ci.yml` on `main` and by `nightly-release-green` on `release/**` (Closes #11946) ([#11962](https://github.com/diegosouzapw/OmniRoute/pull/11962)) +- Move the four nightly jobs that build the backend (`nightly-schemathesis`, `nightly-llm-security` promptfoo + garak, `nightly-resilience` axe-a11y) off the hosted 7 GB runner — where they died on `release/v3.8.51` unseen — onto the box's new `omni-light` pool (two listeners), and document the reshaped fleet (4 active OmniRoute listeners: 2 `omni-build` + 2 `omni-light`, janitor ceiling 4) (Closes #11965) ([#11967](https://github.com/diegosouzapw/OmniRoute/pull/11967)) +- Stop painting every fork PR into `release/**` red: `quality.yml` `Build (advisory)` is skipped (GitHub still reports `continue-on-error` failures as check FAILURE). Hosted `ubuntu-latest` cannot finish `npm run build` on this tree — same class as #11962 taking `build.yml` off the PR rail. `docker-publish.yml` amd64 now runs on the `.113` `omni-build` pool (31 GB / 32 cores, two listeners) with Turbopack, shares the `heavy-build-main` lane with `ci.yml` `Build` so it queues instead of becoming a third heavy, and keeps per-ref concurrency (a merge storm was starting 8 concurrent OOM builds). arm64 stays on `ubuntu-24.04-arm` with webpack — there is no ARM box. Fallback when `USE_VPS_RUNNER` is off: hosted amd64 + webpack (#11976). ([#12021](https://github.com/diegosouzapw/OmniRoute/pull/12021)) +- `docker-publish.yml` builds with webpack (`OMNIROUTE_USE_TURBOPACK=0`) on **both** arches, including amd64 on the `.113` `omni-build` pool. Turbopack had the RAM (31 GB, `omniroute-113-6`) and still panicked (`TurbopackInternalError: there must be a path to a root`, run 33253576569, 26 min in). The same tree's arm64 webpack build on hosted `ubuntu-24.04-arm` succeeded (run 33264823398). Dockerfile already documents webpack as the Docker escape hatch (#11976). ([#12050](https://github.com/diegosouzapw/OmniRoute/pull/12050)) +- **docs(free-tier):** declare the counting vs deciding regimes for "is it free?" and guard the deciding path from DB-backed catalog resolution ([#12226](https://github.com/diegosouzapw/OmniRoute/pull/12226)) — thanks @maxmad64bis +- **docs(auto-combo):** The mode pack table in `docs/routing/AUTO-COMBO.md` now lists all six shipped packs with every weight each one sets, replacing a four-pack table whose numbers had also drifted from the source. It states plainly that no pack sets `quality`, so selecting any pack silences the observed-quality signal. Six more documents that quote the scoring factor count joined the `check:docs-counts` gate, which caught five stale claims — including one naming nine factors that do not exist — and two stale mode pack counts. The dashboard routing panel, which offered four of the six packs and labelled the default strategy "6-Factor Scoring", is now covered by a test; the two packs it was missing are `reliability-first` and `chaos-mode`, the latter labelled as the fault-injection profile it is rather than as one more routing preference ([#12316](https://github.com/diegosouzapw/OmniRoute/pull/12316)) — thanks @maxmad64bis +- **chore(quality):** rebaseline `src/lib/db/apiKeys.ts` for the ACL the key-creation path now preserves ([#12352](https://github.com/diegosouzapw/OmniRoute/pull/12352)) ([#12673](https://github.com/diegosouzapw/OmniRoute/pull/12673)) — thanks @KrzysiekSko +- **chore(providers):** bump the Claude Code wire identity and the Devin bridge image pin from `2.1.220` to `2.1.258` ([#12402](https://github.com/diegosouzapw/OmniRoute/pull/12402)) — thanks @ggiak ([#12604](https://github.com/diegosouzapw/OmniRoute/pull/12604)) +- **docs(env):** align `.env.example`, the README Bun section, and the troubleshooting guide with the code: `OMNIROUTE_USE_TURBOPACK` also governs `npm run build` (not dev-only), `bun run build` follows that flag instead of auto-selecting Webpack, `OMNIROUTE_CHAT_MAX_HEAVY_IN_FLIGHT` is unset by default (no request-count cap), and the structural `503 chat_admission_busy` message matches `chatAdmissionResponses.ts` (#12404 — thanks @pacocartones) +- **chore(electron):** upgrade the desktop app to Electron 44 (Chromium 152, Node 24.18.1) ([#12217](https://github.com/diegosouzapw/OmniRoute/pull/12217)). **Requires macOS 13 (Ventura) or later** — Chromium dropped macOS 12 (Monterey), so Monterey users must stay on an earlier OmniRoute desktop build. Windows and Linux are unaffected; the app already shipped only x64/arm64, so Electron 44 dropping 32-bit builds changes nothing. Removes the `openAsHidden`/`wasOpenedAsHidden` login-item fields deleted in Electron 44 — hidden autostart continues to work through the `--hidden` argument registered with the login item ([#12554](https://github.com/diegosouzapw/OmniRoute/pull/12554)) — thanks @app/dependabot +- **chore(quality):** rebaseline `src/sse/handlers/chat.ts` for the effective-input persistence the continuation fix needs ([#12641](https://github.com/diegosouzapw/OmniRoute/pull/12641)) ([#12680](https://github.com/diegosouzapw/OmniRoute/pull/12680)) — thanks @hartmark +- `Coverage` job on `ci.yml`: the informational Codecov upload gets its own 5-minute ceiling and `continue-on-error`, and the job budget grows from 20 to 30 minutes (the 8-shard c8 merge alone takes ~10) — a stalled upload no longer ends the job `cancelled` and drags a fully green `main` run's conclusion down with it ([#11972](https://github.com/diegosouzapw/OmniRoute/pull/11972)) +- CI hardening on the PR rail: the four `Unit Tests fast-path` shards get `timeout-minutes: 30` (a hung shard held a PR for 64 min instead of GitHub's 6 h default) and both ESLint file caches lose their `restore-keys` fallback, so a cache built under another suppressions file or lint config can no longer report stale verdicts (Refs #11600, #11924) ([#11963](https://github.com/diegosouzapw/OmniRoute/pull/11963)) +- Every CI job that runs a `next build` (`build`, the npm `publish`, both release-green validations) now targets the `omni-build` runner label, which only two of the eight self-hosted runners carry. The box holds one build comfortably and two at the edge; a third now queues on GitHub instead of being OOM-killed by the kernel. ([#11932](https://github.com/diegosouzapw/OmniRoute/pull/11932)) +- **refactor(combo):** move `handleRoundRobinCombo` (and `resolveTargetTokenLimit`) into `open-sse/services/combo/roundRobinCombo.ts`. `combo.ts` drops from 2164 to 1014 lines (`split("\n").length`); the leaf is 1199 (under the 1200 new-file cap). The round-robin call site uses a dynamic `import()` so `releaseStickyPinOnFailure` / `clearStaleLKGP` can stay exported from `combo.ts` without a static cycle. Skip / sticky / semaphore / safety-timer behavior is unchanged. ([#12811](https://github.com/diegosouzapw/OmniRoute/pull/12811)) — thanks @HouMinXi +- **chore(quality):** rebaseline the file-size caps the error-boundary campaign grew past (`open-sse/executors/codex.ts`, `open-sse/vendor/codex-chatgpt-web/bridge.ts`, both via [#12444](https://github.com/diegosouzapw/OmniRoute/pull/12444)) ([#12654](https://github.com/diegosouzapw/OmniRoute/pull/12654)) +- **chore(quality):** rebaseline the file-size caps the hartmark batch grew past (`combos/page.tsx` via [#12355](https://github.com/diegosouzapw/OmniRoute/pull/12355), `open-sse/services/combo.ts` via [#12338](https://github.com/diegosouzapw/OmniRoute/pull/12338)) ([#12623](https://github.com/diegosouzapw/OmniRoute/pull/12623)) — thanks @hartmark +- **chore(quality):** rebaseline the file-size caps the HouMinXi batch grew past when its PRs stacked (`providers/page.tsx`, `chatCore.ts`, `accountFallback.ts`) — each PR measured correctly in isolation, none saw the stacking ([#12619](https://github.com/diegosouzapw/OmniRoute/pull/12619)) +- **chore(quality):** rebaseline `open-sse/services/combo.ts` for the reset-aware scoring the HouMinXi batch stacked ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) ([#12678](https://github.com/diegosouzapw/OmniRoute/pull/12678)) — thanks @HouMinXi +- **docs:** sync provider catalog count (direct commit `4f11b2ae3d`) +- **docs(skills):** sync omni-inference search fallback (direct commit `b4e76a7ed9`) +- **chore(lint):** prune two stale base suppressions (direct commit `1325047d56`) +- **test(auto-combo):** tolerate full-suite rotation load (direct commit `ad3e293f9f`) +- **test:** drain inherited quality snapshot reds (direct commit `21ed68d8ac`) +- **chore(quality):** rebaseline measured CLI bundle growth (direct commit `d35b3f9779`) +- **test(ui):** reproduce invalid AnySearch icon alias (direct commit `12cc6ca834`) +- **test(translator):** fix the relative imports of the relocated deferred-finish test (direct commit `777d9d1629`) +- **docs(counts):** bring the documented provider and migration counts to the measured values (direct commit `e70bea30e9`) +- **test(security):** drop the guard-target set left unused by the sync (direct commit `a32ba0e362`) +- **chore(db):** renumber model_capabilities migration 163 → 169 (cross-branch collision with 163_radar_feed_cache_generated_at) (direct commit `70f5d4cbf9`) +- **docs(cli):** document CLI_PRIME_AGENT_BIN and correct the CLI Agents count (direct commit `9058e39b61`) +- **chore(deps):** refresh runtimes and adopt ESLint 10 ([#11259](https://github.com/diegosouzapw/OmniRoute/pull/11259)) — thanks @backryun +- **refactor(local-corpus):** implement dynamic root resolution and LRU cache ([#11491](https://github.com/diegosouzapw/OmniRoute/pull/11491)) — thanks @raheemuddin786 +- **chore(quality):** prune stale eslint-suppressions entries ([#11596](https://github.com/diegosouzapw/OmniRoute/pull/11596)) +- **chore(ci):** unblock quality gates - env-doc sync + stryker tap.testFiles registration (#9282) ([#11609](https://github.com/diegosouzapw/OmniRoute/pull/11609)) — thanks @Meet6338-X +- **chore(ci):** point the circular-deps gate at dpdm's real JS entrypoint ([#11615](https://github.com/diegosouzapw/OmniRoute/pull/11615)) — thanks @benzntech +- **chore(quality):** rebaseline file-size for #11448/#11495/#11561 growth ([#11630](https://github.com/diegosouzapw/OmniRoute/pull/11630)) +- **deps:** realign onnxruntime-node pin to @huggingface/transformers' 1.24.3 lockstep ([#11633](https://github.com/diegosouzapw/OmniRoute/pull/11633)) — thanks @jonlwheat2-gif +- **test(autoCombo):** port models_dev_tier #11508 guard from node:test to vitest ([#11635](https://github.com/diegosouzapw/OmniRoute/pull/11635)) — thanks @jonlwheat2-gif +- **chore(quality):** rebaseline file-size for #11083/#11631/#11628 growth ([#11636](https://github.com/diegosouzapw/OmniRoute/pull/11636)) +- **chore(lint):** prune stale ESLint suppressions (base-red #11449) ([#11644](https://github.com/diegosouzapw/OmniRoute/pull/11644)) — thanks @jonlwheat2-gif +- **docs(changelog):** aggregate the ten v3.8.50 fragments into CHANGELOG.md ([#11683](https://github.com/diegosouzapw/OmniRoute/pull/11683)) +- **chore(search):** drop unreachable credential sweep after #11642 ([#11687](https://github.com/diegosouzapw/OmniRoute/pull/11687)) — thanks @HouMinXi +- **ci:** add API route TypeScript regression gate ([#11705](https://github.com/diegosouzapw/OmniRoute/pull/11705)) — thanks @Sabeekhann +- **docs(dedup):** correct the CodeQL claim on the tenant-namespace comment ([#11712](https://github.com/diegosouzapw/OmniRoute/pull/11712)) +- **docs(changelog):** add consolidated v3.8.50 stats, top-25 ranking and a .mailmap ([#11715](https://github.com/diegosouzapw/OmniRoute/pull/11715)) +- **docs:** recommend gstack for AI-assisted workflows ([#11770](https://github.com/diegosouzapw/OmniRoute/pull/11770)) — thanks @santosraju99-hub +- **chore(quality):** tighten complexity/cognitive-complexity ratchets to the current tip; land the missed gateways.ts rebaseline ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **docs(i18n):** finish Freepik → Magnific rebrand in locale strings and README ([#11772](https://github.com/diegosouzapw/OmniRoute/pull/11772)) — thanks @turbolego +- **refactor(auto-combo):** fix divergent scoring in combo health reporting ([#11854](https://github.com/diegosouzapw/OmniRoute/pull/11854)) — thanks @jacobsparts +- **chore(changelog):** add missing fragments for #11919/#11918/#11916 ([#11938](https://github.com/diegosouzapw/OmniRoute/pull/11938)) +- **test(check):** escape the runs-on fixture with JSON.stringify, not a quote-only replace ([#11942](https://github.com/diegosouzapw/OmniRoute/pull/11942)) (direct commit `fb7445eaa3`) +- **test(release):** align five suites with the contracts #11933, #11919 and #11876 shipped on release/v3.8.51 ([#11944](https://github.com/diegosouzapw/OmniRoute/pull/11944)) +- **chore(quality):** remove unreachable code and restore test discovery ([#11950](https://github.com/diegosouzapw/OmniRoute/pull/11950)) — thanks @backryun +- **docs(agents):** sync-back landings are fast-forward, never squash ([#11964](https://github.com/diegosouzapw/OmniRoute/pull/11964)) +- **test(infra):** retry recursive temp-dir removal instead of failing a shard on ENOTEMPTY (#11966) ([#11968](https://github.com/diegosouzapw/OmniRoute/pull/11968)) +- **docs(ops):** the .113 heavy-build ceiling is one runner, not two ([#12048](https://github.com/diegosouzapw/OmniRoute/pull/12048)) +- **test(ui):** correct inactive auto-fetch expectation ([#12098](https://github.com/diegosouzapw/OmniRoute/pull/12098)) — thanks @RaviTharuma +- **docs:** document native dependency check escape hatch ([#12101](https://github.com/diegosouzapw/OmniRoute/pull/12101)) — thanks @RaviTharuma +- **test(providers):** regenerate the translate-path golden for OrcaRouter (#11923) ([#12118](https://github.com/diegosouzapw/OmniRoute/pull/12118)) +- **chore(quality):** velocity phase — loosen every numeric baseline by 20% until v4.0, monitor headroom nightly ([#12125](https://github.com/diegosouzapw/OmniRoute/pull/12125)) +- **chore(lint):** batch 0 of #12146 — type the call-log-cap sqlite rows instead of 45 as-any casts ([#12157](https://github.com/diegosouzapw/OmniRoute/pull/12157)) +- **chore(lint):** batch 4 of #12146 — shared/components react-hooks violations resolved ([#12159](https://github.com/diegosouzapw/OmniRoute/pull/12159)) +- **chore(lint):** batch 1 of #12146 — dashboard/cli-code react-hooks violations resolved ([#12160](https://github.com/diegosouzapw/OmniRoute/pull/12160)) +- **chore(lint):** batch 3 of #12146 — dashboard/settings react-hooks violations resolved ([#12162](https://github.com/diegosouzapw/OmniRoute/pull/12162)) +- **chore(lint):** batch 2 of #12146 — resolve the react-hooks compiler violations in dashboard/providers ([#12163](https://github.com/diegosouzapw/OmniRoute/pull/12163)) +- **chore(quality):** register search-432 cooldown test in stryker tap.testFiles ([#12170](https://github.com/diegosouzapw/OmniRoute/pull/12170)) +- **chore(lint):** batch 5 of #12146 — combos, endpoint, provider-stats, api-manager and costs react-hooks violations resolved ([#12174](https://github.com/diegosouzapw/OmniRoute/pull/12174)) +- **build:** add contributor fast profile ([#12192](https://github.com/diegosouzapw/OmniRoute/pull/12192)) — thanks @rafacpti23 +- **docs(readme):** README touch-ups ([#12193](https://github.com/diegosouzapw/OmniRoute/pull/12193)) ([#12194](https://github.com/diegosouzapw/OmniRoute/pull/12194)) ([#12202](https://github.com/diegosouzapw/OmniRoute/pull/12202)) — thanks @rafacpti23 +- **build:** force the webpack bundler in the contributor build profile ([#12197](https://github.com/diegosouzapw/OmniRoute/pull/12197)) — thanks @rafacpti23 +- **build:** skip standalone packaging in the contributor profile ([#12198](https://github.com/diegosouzapw/OmniRoute/pull/12198)) — thanks @rafacpti23 +- **docs(audit):** align every published number with the code and harden check:docs-counts ([#12200](https://github.com/diegosouzapw/OmniRoute/pull/12200)) +- **build:** stub the instrumentation entrypoints in the contributor profile ([#12203](https://github.com/diegosouzapw/OmniRoute/pull/12203)) — thanks @rafacpti23 +- **build:** omit the standalone output target for contributor builds ([#12204](https://github.com/diegosouzapw/OmniRoute/pull/12204)) — thanks @rafacpti23 +- **chore(lint):** batch 6 of #12146 — memory, radar, audit, analytics, cache, usage, activity, home and RequestLoggerV2 react-hooks violations resolved ([#12208](https://github.com/diegosouzapw/OmniRoute/pull/12208)) +- **docs(diagrams):** rename number-carrying diagram files to stable names ([#12210](https://github.com/diegosouzapw/OmniRoute/pull/12210)) +- **docs(dashboard):** redraw onboarding tier-flow SVGs for the real 4-tier model ([#12211](https://github.com/diegosouzapw/OmniRoute/pull/12211)) +- **docs(api):** document every implemented route in openapi.yaml (276 → 692 paths) ([#12212](https://github.com/diegosouzapw/OmniRoute/pull/12212)) +- **chore(lint):** batch 7 of #12146 — final src tail: 37 react-hooks violations across 33 files resolved ([#12244](https://github.com/diegosouzapw/OmniRoute/pull/12244)) +- Revert "docs: recommend gstack for AI-assisted workflows (#11770)" ([#12248](https://github.com/diegosouzapw/OmniRoute/pull/12248)) ([#12249](https://github.com/diegosouzapw/OmniRoute/pull/12249)) +- **docs:** Chaos Mode setup guide + weighted strategy semantics ([#12250](https://github.com/diegosouzapw/OmniRoute/pull/12250)) +- **docs(agents):** protected-surface merge rule — operator approval for agent-instruction files ([#12253](https://github.com/diegosouzapw/OmniRoute/pull/12253)) +- **chore(quality):** register native-codex turn-pin tests in stryker tap.testFiles ([#12263](https://github.com/diegosouzapw/OmniRoute/pull/12263)) +- **chore(quality):** dedupe tap.testFiles entries added by racing base-red fixes ([#12265](https://github.com/diegosouzapw/OmniRoute/pull/12265)) +- **docs(readme):** introduce OmniRouteTray — the macOS menu-bar companion ([#12276](https://github.com/diegosouzapw/OmniRoute/pull/12276)) — thanks @ggiak +- **chore(proxy):** purge the legacy 1proxy residue — sync/rotator modules, dead DB exports, dead settings tab and flag, docs (#12091) ([#12290](https://github.com/diegosouzapw/OmniRoute/pull/12290)) +- **chore(quality):** baseline-headroom skips generated and vendored files in the fileSize worst-file signal ([#12291](https://github.com/diegosouzapw/OmniRoute/pull/12291)) +- **refactor(video):** unify the JPEG frame data-URI contract ([#12322](https://github.com/diegosouzapw/OmniRoute/pull/12322)) +- **chore(deps):** freeze onnxruntime-node and eslint-plugin-react-hooks out of dependabot groups ([#12329](https://github.com/diegosouzapw/OmniRoute/pull/12329)) +- **test(executors):** restore the #10986 reasoning-only fallback guards ([#12364](https://github.com/diegosouzapw/OmniRoute/pull/12364)) — thanks @pacocartones +- **chore(quality):** re-tighten the file-size ratchet to the real LOC (plan 3.8.52 task 0) ([#12411](https://github.com/diegosouzapw/OmniRoute/pull/12411)) +- **test(grok):** format web executor suite ([#12412](https://github.com/diegosouzapw/OmniRoute/pull/12412)) +- **chore(quality):** ratchet open-sse typecheck baseline to zero ([#12418](https://github.com/diegosouzapw/OmniRoute/pull/12418)) +- **test(build):** guard the artifact path policy arrays against duplicates ([#12422](https://github.com/diegosouzapw/OmniRoute/pull/12422)) +- **chore(lint):** adopt eslint-plugin-react-hooks 7.1.1 ([#12428](https://github.com/diegosouzapw/OmniRoute/pull/12428)) +- **chore(providers):** remove a keyless provider integration at its operator's request ([#12440](https://github.com/diegosouzapw/OmniRoute/pull/12440)) +- **docs(providers):** register providers removed at their operator's request and guard against reintroduction ([#12478](https://github.com/diegosouzapw/OmniRoute/pull/12478)) +- **chore(quality):** base fixes — stryker tap.testFiles + node_modules cache key ([#12482](https://github.com/diegosouzapw/OmniRoute/pull/12482)) +- **docs(arch):** one-process recipe for tens of long /v1/responses ([#12493](https://github.com/diegosouzapw/OmniRoute/pull/12493)) — thanks @RaviTharuma +- **chore(quality):** tighten the CodeQL ratchet baseline from 11 to 6 ([#12530](https://github.com/diegosouzapw/OmniRoute/pull/12530)) +- **chore(deps):** pin browserslist override to ^4.28.8 ([#12592](https://github.com/diegosouzapw/OmniRoute/pull/12592)) — thanks @KooshaPari +- **chore(deps):** bump fast-uri to 3.1.7 in the electron lockfile ([#12601](https://github.com/diegosouzapw/OmniRoute/pull/12601)) +- **docs(quality):** document how the CodeQL ratchet refreshes and how to tighten it ([#12611](https://github.com/diegosouzapw/OmniRoute/pull/12611)) +- **test(ci):** pin the openapi-security-tiers two-arm contract with an executing gate test (#12581) ([#12652](https://github.com/diegosouzapw/OmniRoute/pull/12652)) +- **docs(gamification):** align XP Rewards table with code (#12501) ([#12667](https://github.com/diegosouzapw/OmniRoute/pull/12667)) — thanks @KooshaPari +- **refactor(combo):** split executeTarget into gates, attempt, and loop ([#12746](https://github.com/diegosouzapw/OmniRoute/pull/12746)) — thanks @HouMinXi +- **docs(security):** document socket.yml scanner config + CI workflow link (#12575) ([#12764](https://github.com/diegosouzapw/OmniRoute/pull/12764)) — thanks @KooshaPari +- **chore(ci):** guard commit identity in pre-commit to stop author misattribution ([#12772](https://github.com/diegosouzapw/OmniRoute/pull/12772)) +- **deps:** 17 Dependabot bumps — bump electron from 43.4.0 to 43.4.1 in /electron ([#11426](https://github.com/diegosouzapw/OmniRoute/pull/11426)); bump the development group across 1 directory with 8 updates ([#11428](https://github.com/diegosouzapw/OmniRoute/pull/11428)); bump the production group across 1 directory with 11 updates ([#11440](https://github.com/diegosouzapw/OmniRoute/pull/11440)); chore(deps): bump github/codeql-action/init from 4.37.7 to 4.37.8 ([#11925](https://github.com/diegosouzapw/OmniRoute/pull/11925)); chore(deps): bump github/codeql-action from 4.37.7 to 4.37.8 ([#11926](https://github.com/diegosouzapw/OmniRoute/pull/11926)); chore(deps): bump github/codeql-action/analyze from 4.37.7 to 4.37.8 ([#11927](https://github.com/diegosouzapw/OmniRoute/pull/11927)); build(deps): bump github/codeql-action/init from 4.37.8 to 4.37.9 ([#12345](https://github.com/diegosouzapw/OmniRoute/pull/12345)); build(deps): bump github/codeql-action/analyze from 4.37.8 to 4.37.9 ([#12346](https://github.com/diegosouzapw/OmniRoute/pull/12346)); bump the development group across 1 directory with 2 updates ([#12347](https://github.com/diegosouzapw/OmniRoute/pull/12347)); build(deps): bump github/codeql-action from 4.37.8 to 4.37.9 ([#12349](https://github.com/diegosouzapw/OmniRoute/pull/12349)); bump browserslist from 4.28.2 to 4.28.8 ([#12396](https://github.com/diegosouzapw/OmniRoute/pull/12396)); bump the production group across 1 directory with 4 updates ([#12399](https://github.com/diegosouzapw/OmniRoute/pull/12399)); bump @xmldom/xmldom ([#12500](https://github.com/diegosouzapw/OmniRoute/pull/12500)); bump qs from 6.15.2 to 6.16.0 ([#12512](https://github.com/diegosouzapw/OmniRoute/pull/12512)); bump @xmldom/xmldom from 0.9.10 to 0.9.12 ([#12513](https://github.com/diegosouzapw/OmniRoute/pull/12513)); bump fast-uri from 3.1.5 to 3.1.7 ([#12514](https://github.com/diegosouzapw/OmniRoute/pull/12514)); bump @humanfs/node from 0.16.7 to 0.16.8 ([#12515](https://github.com/diegosouzapw/OmniRoute/pull/12515)) +- Clear the `release/v3.8.51` `check:agent-skills-sync` base-red: regenerate `skills/cli-tunnel/SKILL.md` so the `tunnel create [type]` positional that #13009 taught the generator to read is reflected in the committed skill. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` typecheck base-red from #12731: the new `Mode pack ranking gates` candidates in `open-sse/services/autoCombo/__tests__/autoCombo.test.ts` omitted the required `provider`, `model` and `errorRate` fields of `ProviderCandidate`, failing `check:open-sse-typecheck` (and with it `Fast Quality Gates`) on every open PR. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` docs-sync base-red: EURouter (#13025) and GreenPT (#13024) took the live provider count to 358, leaving 7 STRICT drifts (`PROVIDER_REFERENCE.md`, 4 diagrams, 2 tier-flow images) plus the `AGENTS.md` / `llm.txt` / `package.json` count claims stale. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- **test(models):** Pin `CATALOG_BUILD_TIMEOUT_MS` in the #12058 canonical-catalog regression so a cold catalog build racing the 8s default bound no longer flakes the suite with a 500 ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Allow `@omniroute/opencode-plugin-v2/` in the npm pack policy — [#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870) shipped the v2 plugin beside its v1 sibling without widening the allowlist, so every packed file under it read as an unexpected artifact and failed `check:pack-policy` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Point the release-green validator's pack gate at the tree it is validating — the build-provenance guard defaulted to `origin/main`, which a release branch tip can never be an ancestor of mid-cycle, so `Package artifact` reported a false red on every run and the tarball boot-smoke cascaded off it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **docs(checks):** keep doc counts honest — headings, rankings, catalog, weights, quality gate and scoring diagram now covered ([#12507](https://github.com/diegosouzapw/OmniRoute/pull/12507)) — thanks @maxmad64bis +- **chore(lifecycle):** `check:model-lifecycle` now also diffs `DEFAULT_DEGRADATION_MAP` (the background-task redirect table) against the vendor lifecycle snapshot, refusing a retired id as source or target, with a table-driven unit test beside it. Three rows whose source the vendor had retired — `claude-sonnet-4-20250514`, `gemini-3-pro-preview` and `gpt-5.1-codex` (whose target `gpt-5.1-codex-mini` is retired too) — were dead code, since `checkLifecycle` answers 410 before the redirect runs; they are dropped (#12535 — thanks @pacocartones) +- **test(compression):** cover idle worker eviction at the resource level — the pool must call `terminate()` and must not retain the worker's `MessagePort`, complementing the `exit`-event assertion added with the fix ([#13371](https://github.com/diegosouzapw/OmniRoute/pull/13371)) +- **chore(quality):** raise the `imageGeneration.ts` file-size ceiling for the image-only-model guard that clears the #12945 base-red ([#13392](https://github.com/diegosouzapw/OmniRoute/pull/13392)) +- **chore(quality):** type the combo-test route's JSON response bodies instead of casting them to `any`, drop the now-empty suppression entry, and remove the `hasPerModelQuota` import `chatCore.ts` stopped using when the failure-classification helper was extracted ([#13379](https://github.com/diegosouzapw/OmniRoute/pull/13379)) +- **chore(electron):** the desktop bundle no longer ships the root-level files of every translated docs mirror (`README.md`, `llm.txt`, `CLAUDE.md`, `GEMINI.md`, `CONTRIBUTING.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`) — the packaged app only reads `docs/i18n//docs/**`, which stays. Saves ~11 MB on top of the translated CHANGELOGs already pruned. (#0000) ([#13235](https://github.com/diegosouzapw/OmniRoute/pull/13235)) +- **chore(quality):** retighten the `src/sse/handlers/chat.ts` unused-vars suppression count to the 8 violations that actually remain ([#13385](https://github.com/diegosouzapw/OmniRoute/pull/13385)) +- Bring the documented counts back in line with the code: 171 migrations (was 169) and 20 routing strategies (was 19), plus the regenerated `cli-tunnel` skill reference. ([#12970](https://github.com/diegosouzapw/OmniRoute/pull/12970)) +- Update the documented migration count to 172 after the call-logs provider-stats indexes landed. ([#13209](https://github.com/diegosouzapw/OmniRoute/pull/13209)) +- **chore(ci):** two gates that close the blind spots behind the exclusions above. `check:vitest-exclusions` requires every Vitest exclusion to name a tracking issue and to appear in `config/quality/vitest-exclusions.json` — the previous list grew to 62 files behind a comment pointing at an issue that had been closed for a month. `check-new-key-coverage` requires a key newly added to `en.json` to reach every locale; the existing coverage gate is a percentage floor per locale, so eleven absent keys out of ~13,000 left it at 99.9% while a whole feature shipped untranslated in nine languages. Both are diff-aware, so pre-existing debt stays frozen and neither needed a migration to turn on. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **build:** root layout no longer loads Inter from `next/font/google`, so a production image build does not need fonts.googleapis.com ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **docs(i18n):** bring the eight new `llm.txt` mirrors (km, kn, ml, my, ne, or, pa, si) up to the current provider and migration counts, so `check-docs-sync` stops rejecting every commit ([#13674](https://github.com/diegosouzapw/OmniRoute/pull/13674)) +- **refactor(ui):** flow surfaces (home topology, combo live studio, compression cockpit/waterfall/nodes, token-health badge) express state through the theme-aware `--orch-status-*` tokens instead of fixed dark-mode hex, so green/red/amber/grey stay legible in the light theme; dark mode is byte-identical. Categorical palettes (routing-strategy hues, compression-layer pills, provider brand colors) deliberately stay hex ([#12378](https://github.com/diegosouzapw/OmniRoute/issues/12378)) ([#13203](https://github.com/diegosouzapw/OmniRoute/pull/13203)) +- **chore(tests):** 51 test files that had been excluded from Vitest are running again, restoring roughly 350 assertions to the blocking suite. Each was measured individually first: of the 62 files parked behind the `// #8618 — pre-existing failure` comment, 51 pass against the current tree with no source change, so the exclusions had outlived the failures they were added for. The 11 that genuinely still fail stay excluded, but now point at a live tracker (#13204) rather than at #8618, which was closed in August while the list it tracked kept growing. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **chore(quality):** drop two ESLint suppression entries whose violations no longer exist, so `eslint --suppressions-location` stops rejecting every commit that touches the surrounding files ([#13384](https://github.com/diegosouzapw/OmniRoute/pull/13384)) +- **chore(release):** bring main's post-tag fixes into release/v3.8.51 — sync-back step 2/2 (direct commit `21c488f210`) +- **chore(deps):** patch toml and esbuild in the opencode plugin lockfiles ([#12965](https://github.com/diegosouzapw/OmniRoute/pull/12965)) +- **docs(changelog):** reconcile the v3.8.51 living section — fold 366 fragments, cover every cycle commit, credit every contributor ([#12971](https://github.com/diegosouzapw/OmniRoute/pull/12971)) +- **test(plugin):** wait for the v2 lazy-refresh tier instead of sleeping past it ([#12984](https://github.com/diegosouzapw/OmniRoute/pull/12984)) +- **chore(quality):** rebaseline chatCore.ts after the non-streaming regression fixes ([#13045](https://github.com/diegosouzapw/OmniRoute/pull/13045)) +- **docs(changelog):** add the missing bullet marker to the DeepSeek PoW fragment ([#13200](https://github.com/diegosouzapw/OmniRoute/pull/13200)) +- **chore(deps):** drain the Dependabot queue — 10 of 13 alerts ([#13213](https://github.com/diegosouzapw/OmniRoute/pull/13213)) +- **chore(ci):** make worktrees carry the hooks that enforce the identity gate ([#13236](https://github.com/diegosouzapw/OmniRoute/pull/13236)) +- **chore(deps):** pin csv-parse to ^7.0.2 via overrides (#13117) ([#13300](https://github.com/diegosouzapw/OmniRoute/pull/13300)) — thanks @KooshaPari +- **chore(deps):** bump hono from ^4.12.34 to ^4.13.7 (#13148) ([#13301](https://github.com/diegosouzapw/OmniRoute/pull/13301)) — thanks @KooshaPari +- **chore(deps):** pin joi to ^18.2.8 via overrides (#13085) ([#13302](https://github.com/diegosouzapw/OmniRoute/pull/13302)) — thanks @KooshaPari +- **docs(providers):** add Microsoft 365 Copilot (BizChat) provider guide (#12779) ([#13340](https://github.com/diegosouzapw/OmniRoute/pull/13340)) — thanks @KooshaPari +### 🙌 Contributors + +Thanks to everyone whose work landed in v3.8.51: + +| Contributor | PRs / Issues | +| --- | --- | +| [@5dive-bot](https://github.com/5dive-bot) | #11852 | +| [@abhisheksharma2411](https://github.com/abhisheksharma2411) | #11547, #11684, #11959, #12209 | +| [@adevwithpurpose](https://github.com/adevwithpurpose) | #11464 | +| [@adivekar-utexas](https://github.com/adivekar-utexas) | #12015, #12043, #12090, #12138 | +| [@AIB1TAL0S](https://github.com/AIB1TAL0S) | #12103 | +| [@alltomatos](https://github.com/alltomatos) | #11448 | +| [@alvinveroy](https://github.com/alvinveroy) | #11857, #12027, #12165 | +| [@amaleta](https://github.com/amaleta) | #11906, #11934 | +| [@amartinawi](https://github.com/amartinawi) | #11575 | +| [@AndrianBalanescu](https://github.com/AndrianBalanescu) | #11083, #11830, #11834, #11844 | +| [@anhtahaylove](https://github.com/anhtahaylove) | #13091, #13092, #13093, #13096, #13097, #13100, #13106, #13109, #13114, #13155, #13166, #13171, #13175, #13187, #13196 | +| [@aniruddhaadak80](https://github.com/aniruddhaadak80) | #11760 | +| [@app](https://github.com/app) | #12554 | +| [@arminanton](https://github.com/arminanton) | #11461, #11513 | +| [@AStupidBear](https://github.com/AStupidBear) | #11584 | +| [@atescivitci-cmd](https://github.com/atescivitci-cmd) | #12105 | +| [@b3nw](https://github.com/b3nw) | #11970, #11971, #12147 | +| [@backryun](https://github.com/backryun) | #11259, #11950, #12075, #12076, #12078, #12079, #12081, #12082, #12181, #12228, #12239, #12255, #12258, #12277, #12367, #12423, #12524, #12538 | +| [@benjaminkitt](https://github.com/benjaminkitt) | #11747 | +| [@benzntech](https://github.com/benzntech) | #11614, #11615 | +| [@Bl0ck154](https://github.com/Bl0ck154) | #11948, #11951, #11952, #11953, #11954 | +| [@brick30llc-ctrl](https://github.com/brick30llc-ctrl) | #11752, #12242, #12262 | +| [@Chewji9875](https://github.com/Chewji9875) | #11455, #11875, #12028 | +| [@cheynetom](https://github.com/cheynetom) | #12058 | +| [@davidebaraldo](https://github.com/davidebaraldo) | #12222, #12304 | +| [@davidlinfr](https://github.com/davidlinfr) | #6390 | +| [@ddarkr](https://github.com/ddarkr) | #12124 | +| [@Deftera186](https://github.com/Deftera186) | #11809 | +| [@dpozimski](https://github.com/dpozimski) | #11945 | +| [@DW-MediaLab](https://github.com/DW-MediaLab) | #12229 | +| [@echel0nn](https://github.com/echel0nn) | #11923 | +| [@f9td56dbgh-hub](https://github.com/f9td56dbgh-hub) | #11660 | +| [@fabioluissilva](https://github.com/fabioluissilva) | #11991 | +| [@foreveryh](https://github.com/foreveryh) | #12177 | +| [@geek007git](https://github.com/geek007git) | #12115, #12116, #12117, #12120, #12122 | +| [@geekyNads](https://github.com/geekyNads) | #11784, #12266 | +| [@ggiak](https://github.com/ggiak) | #11561, #12276, #12350, #12402 | +| [@gonisulaimann](https://github.com/gonisulaimann) | #12368, #12369, #12371 | +| [@Gorillaz322](https://github.com/Gorillaz322) | #12207 | +| [@groovecityJO](https://github.com/groovecityJO) | #12682 | +| [@hartmark](https://github.com/hartmark) | #11434, #11452, #11473, #11499, #11703, #11983, #11984, #11985, #11986, #11988, #11989, #11990, #11994, #12221, #12293, #12445, #12446, #12447, #12448, #12460, #12461, #12623, #12646, #12650, #12680, #12717, #12718, #12727, #12741, #12854 | +| [@hizzt](https://github.com/hizzt) | #11894 | +| [@hongnoul](https://github.com/hongnoul) | #11484 | +| [@HouMinXi](https://github.com/HouMinXi) | #11411, #11414, #11512, #11518, #11520, #11641, #11642, #11643, #11687, #11779, #11849, #11850, #11851, #11915, #11916, #11918, #11919, #11920, #12013, #12017, #12033, #12042, #12106, #12139, #12166, #12169, #12171, #12205, #12213, #12312, #12325, #12487, #12488, #12495, #12504, #12557, #12566, #12590, #12591, #12624, #12626, #12632, #12637, #12678, #12696, #12697, #12711, #12733, #12746, #12767, #12770, #12789, #12803, #12805, #12811, #12866, #12868, #12899, #12926, #12934, #12950, #12951, #12974, #13001, #13006, #13011, #13017, #13026, #13027, #13034, #13035, #13038, #13042, #13050, #13060, #13061, #13069, #13107, #13120, #13136, #13195, #13197 | +| [@Hsia97](https://github.com/Hsia97) | #11624 | +| [@jacobsparts](https://github.com/jacobsparts) | #11854, #12155, #12167 | +| [@joglomedia](https://github.com/joglomedia) | #11980 | +| [@jonlwheat2-gif](https://github.com/jonlwheat2-gif) | #11443, #11450, #11549, #11567, #11608, #11633, #11635, #11640, #11644, #11671, #11672, #11673, #11674, #11675, #11676, #11677, #11706, #11783, #12051, #12052, #12053, #12055 | +| [@kanade-hoshino](https://github.com/kanade-hoshino) | #12180 | +| [@Karan825](https://github.com/Karan825) | #11861 | +| [@KaspaPulse](https://github.com/KaspaPulse) | #11389, #11469, #11628, #11666, #11910 | +| [@keeltrace](https://github.com/keeltrace) | #12080, #12223 | +| [@killer30001000](https://github.com/killer30001000) | #12178 | +| [@KooshaPari](https://github.com/KooshaPari) | #12470, #12592, #12667, #12699, #12703, #12706, #12764, #12769, #12771, #13300, #13301, #13302, #13340, #13401, #13403, #13404, #13405, #13406, #13407, #13409, #13410, #13411, #13412, #13413, #13414, #13418, #13424, #13427, #13433, #13523, #13524, #13525, #13528, #13531, #13532, #13534, #13539, #13543, #13545, #13546, #13547, #13550, #13553 | +| [@kriptoburak](https://github.com/kriptoburak) | #11370 | +| [@KrzysiekSko](https://github.com/KrzysiekSko) | #12425, #12432, #12673 | +| [@marcelokarval](https://github.com/marcelokarval) | direct commit / report | +| [@maxmad64bis](https://github.com/maxmad64bis) | #1622, #11435, #11437, #11441, #11537, #11550, #11553, #11555, #11557, #11812, #11842, #11843, #11903, #12151, #12214, #12215, #12218, #12226, #12314, #12316, #12317, #12318, #12319, #12320, #12321, #12507, #12715, #12731, #12744, #12786, #12787, #12788, #12790, #12792, #12794, #12795, #12828, #12832, #12853, #12857, #12870, #12937, #12941, #12975, #13141, #13142, #13146, #13147 | +| [@mdigitalbh81](https://github.com/mdigitalbh81) | #12240 | +| [@Meet6338-X](https://github.com/Meet6338-X) | #11598, #11609, #12110 | +| [@morpheus9393](https://github.com/morpheus9393) | #11943 | +| [@MumuTW](https://github.com/MumuTW) | #11492, #11502, #11506, #11507, #11626, #11675, #11685, #11728, #11746, #11888, #11889, #11890, #11892 | +| [@Neuron-Mr-White](https://github.com/Neuron-Mr-White) | #11622, #11801, #12256 | +| [@NightStalker-87](https://github.com/NightStalker-87) | #12183 | +| [@NoxzRCW](https://github.com/NoxzRCW) | #11879, #11880, #11881, #11882, #11883, #11935 | +| [@ntdat812](https://github.com/ntdat812) | #11585, #12095 | +| [@ntdatt812](https://github.com/ntdatt812) | #11368, #11573, #11574, #11576, #11577, #11580, #11582, #11583, #11588, #11589, #11590, #11591, #11592, #11593, #11672, #12177, #12180, #12873, #12918, #12920, #12921, #12925, #12930, #13007, #13009, #13024, #13025, #13083, #13087, #13101, #13104, #13110 | +| [@official-burak](https://github.com/official-burak) | #11542 | +| [@opensource-elearning](https://github.com/opensource-elearning) | #12179, #12189, #12278, #12286 | +| [@oyi77](https://github.com/oyi77) | #11408, #11409, #11421, #11505, #11677, #12036, #12110 | +| [@pacocartones](https://github.com/pacocartones) | #11521, #11522, #11527, #11528, #11529, #11530, #11531, #11532, #11533, #11534, #11595, #11599, #11603, #11604, #11605, #11607, #11610, #11676, #11714, #11716, #11718, #11767, #11838, #11860, #11862, #11869, #11871, #11872, #11873, #11903, #11906, #11921, #11934, #12356, #12358, #12359, #12360, #12361, #12362, #12364, #12365, #12373, #12374, #12375, #12376, #12377, #12379, #12380, #12381, #12386, #12387, #12389, #12390, #12394, #12395, #12397, #12401, #12403, #12404, #12522, #12523, #12535, #12536, #12540, #12541, #12543, #12545, #12548, #12549, #12550, #12551, #12552, #12647, #12651, #12653, #13055 | +| [@patrykkopycinski](https://github.com/patrykkopycinski) | #11936, #11937, #12224 | +| [@PauloFH](https://github.com/PauloFH) | #11509 | +| [@PauloHSOliveira](https://github.com/PauloHSOliveira) | #12241 | +| [@ponkcore](https://github.com/ponkcore) | #12054 | +| [@Prajeeth-12](https://github.com/Prajeeth-12) | #11634, #12046 | +| [@quiterunner-commits](https://github.com/quiterunner-commits) | #12131, #12143 | +| [@rafacpti23](https://github.com/rafacpti23) | #11554, #11558, #12192, #12197, #12198, #12202, #12203, #12204 | +| [@ragnar-claude](https://github.com/ragnar-claude) | #11564 | +| [@raheemuddin786](https://github.com/raheemuddin786) | #11491, #11839, #11840, #11841, #12003, #12230, #12231, #12232, #12233, #12234 | +| [@rahilmavani](https://github.com/rahilmavani) | #11761 | +| [@Rahulsharma0810](https://github.com/Rahulsharma0810) | #11771 | +| [@RaviTharuma](https://github.com/RaviTharuma) | #11710, #11727, #11797, #11798, #11802, #11805, #11806, #11811, #12098, #12099, #12101, #12449, #12452, #12472, #12473, #12493, #12533, #12607, #12628, #12631, #12636, #12875, #12876, #12880, #12882, #12884 | +| [@rezjalibd](https://github.com/rezjalibd) | #12186 | +| [@RhianB14](https://github.com/RhianB14) | #11692 | +| [@rifqiawl](https://github.com/rifqiawl) | #11517, #11519, #11969 | +| [@rqzbeh](https://github.com/rqzbeh) | #11390 | +| [@Sabeekhann](https://github.com/Sabeekhann) | #11699, #11705 | +| [@santosraju99-hub](https://github.com/santosraju99-hub) | #11755, #11770 | +| [@Seramicx](https://github.com/Seramicx) | #11568 | +| [@Siva010](https://github.com/Siva010) | #12191 | +| [@solstxce](https://github.com/solstxce) | #11597 | +| [@soroush5](https://github.com/soroush5) | #12691 | +| [@tenshiak](https://github.com/tenshiak) | #12279 | +| [@TheDemonTuan](https://github.com/TheDemonTuan) | #11468, #11470, #11471, #11482, #11548, #11758, #11775, #11814 | +| [@thomasmaerz](https://github.com/thomasmaerz) | #12834 | +| [@tuandinh0801](https://github.com/tuandinh0801) | #11454 | +| [@turbolego](https://github.com/turbolego) | #11621, #11762, #11772, #11774, #11781, #12216 | +| [@Tushar49](https://github.com/Tushar49) | #11392 | +| [@ujjawalkaushik1110](https://github.com/ujjawalkaushik1110) | #11863 | +| [@vermasomesh835](https://github.com/vermasomesh835) | #11794 | +| [@vsd2807](https://github.com/vsd2807) | #11565, #11619 | +| [@wahidsadik371-coder](https://github.com/wahidsadik371-coder) | #12126 | +| [@watchingdogs](https://github.com/watchingdogs) | #12031 | +| [@wildcard](https://github.com/wildcard) | #11620 | +| [@xiaoyaner0201](https://github.com/xiaoyaner0201) | #11460, #11662, #12083 | +| [@Xxx91n](https://github.com/Xxx91n) | #11690 | +| [@yourspraveen](https://github.com/yourspraveen) | #11146 | +| [@yxyxy](https://github.com/yxyxy) | #11671 | +| [@ZaimMarzuki](https://github.com/ZaimMarzuki) | #11960 | +| [@Zartharas](https://github.com/Zartharas) | #11340 | +| [@zero-executioner](https://github.com/zero-executioner) | #11631 | +| [@diegosouzapw](https://github.com/diegosouzapw) | maintainer | + +--- ## [3.8.50] — 2026-08-25 _Living section — regenerated 2026-08-12 from all cycle commits (cycle open `ed2db6cb19` → tip). Bullets carry the merged PR and its author; direct pushes listed separately._ diff --git a/docs/i18n/si/CHANGELOG.md b/docs/i18n/si/CHANGELOG.md index e206dd6cd4..2132992676 100644 --- a/docs/i18n/si/CHANGELOG.md +++ b/docs/i18n/si/CHANGELOG.md @@ -96,31 +96,31 @@ ## [3.8.51] — TBD -_Living section — reconciled 2026-09-07 from all cycle commits (`release/v3.8.50` tip `091589089c` → `release/v3.8.51` tip `d6f315018a`, 696 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._ +_Living section — reconciled 2026-09-15 from all cycle commits (`091589089c` → `c0f92ec98a`, 916 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 | **120** | -| 📝 Commits in the cycle | **696** | -| 🔀 Pull requests referenced | **672** | -| 📋 Changelog entries | **685** | -| 🙌 Contributors credited in entries | **118** | +| 👥 People who contributed | **123** | +| 📝 Commits in the cycle | **916** | +| 🔀 Pull requests referenced | **892** | +| 📋 Changelog entries | **919** | +| 🙌 Contributors credited in entries | **121** | | 🤖 Automated dependency commits | 18 | **Entries by type** | Type | Count | | --- | ---: | -| 🐛 Fixes | 403 | -| ✨ Features | 118 | -| 🧹 Chore | 72 | -| 📚 Docs | 36 | -| 🧪 Tests | 29 | +| 🐛 Fixes | 592 | +| ✨ Features | 131 | +| 🧹 Chore | 93 | +| 📚 Docs | 42 | +| 🧪 Tests | 32 | +| ♻️ Refactor | 8 | +| 🏗️ Build | 7 | | ⚡ Performance | 7 | -| ♻️ Refactor | 7 | -| 🏗️ Build | 6 | | ⚙️ CI | 2 | | ⏪ Reverts | 2 | | 📦 Dependencies | 2 | @@ -128,35 +128,35 @@ _Living section — reconciled 2026-09-07 from all cycle commits (`release/v3.8. ### 🏆 Top 25 contributors this cycle -_By commits in `release/v3.8.50..release/v3.8.51` (`091589089c..d6f315018a`), author identities consolidated via `.mailmap` and the merged PR's GitHub login. Bots excluded._ +_By commits in `091589089c..c0f92ec98a`, author identities consolidated via `.mailmap` and the merged PR's GitHub login. Bots excluded._ | # | Contributor | Commits | | ---: | --- | ---: | -| 🥇 | diegosouzapw | 240 | -| 🥈 | Paco Cartones (@pacocartones) | 58 | -| 🥉 | Bob.Hou (@HouMinXi) | 57 | -| 4 | Dizzle (@maxmad64bis) | 27 | -| 5 | Webman (@jonlwheat2-gif) | 22 | -| 6 | Ravi Tharuma (@RaviTharuma) | 21 | -| 7 | backryun | 17 | -| 8 | Markus Hartung (@hartmark) | 17 | -| 9 | Nguyen Thanh Dat (@ntdatt812) | 16 | -| 10 | Rafa Martins (@rafacpti23) | 10 | -| 11 | Syed Raheemuddin (@raheemuddin786) | 10 | -| 12 | Koosha Paridehpour (@KooshaPari) | 9 | -| 13 | Nguyễn Viết Tuấn (@TheDemonTuan) | 8 | -| 14 | MumuTW | 7 | -| 15 | Paijo (@oyi77) | 7 | -| 16 | Tobias Andersen (@turbolego) | 6 | -| 17 | Bl0ck (@Bl0ck154) | 5 | -| 18 | SHANMUGAPRIYAN (@geek007git) | 5 | -| 19 | KaspaPulse | 5 | -| 20 | Abhishek Sharma (@abhisheksharma2411) | 4 | -| 21 | Abhishek Divekar (@adivekar-utexas) | 4 | -| 22 | Andrew B. (@AndrianBalanescu) | 4 | -| 23 | NoxzRCW | 4 | -| 24 | opensource-elearning | 4 | -| 25 | Alvin T. Veroy (@alvinveroy) | 3 | +| 🥇 | diegosouzapw | 323 | +| 🥈 | Bob.Hou (@HouMinXi) | 79 | +| 🥉 | Paco Cartones (@pacocartones) | 77 | +| 4 | Dizzle (@maxmad64bis) | 49 | +| 5 | Koosha Paridehpour (@KooshaPari) | 43 | +| 6 | Nguyen Thanh Dat (@ntdatt812) | 31 | +| 7 | Ravi Tharuma (@RaviTharuma) | 26 | +| 8 | Markus Hartung (@hartmark) | 22 | +| 9 | Webman (@jonlwheat2-gif) | 22 | +| 10 | backryun | 17 | +| 11 | anhtahaylove | 15 | +| 12 | Rafa Martins (@rafacpti23) | 10 | +| 13 | Syed Raheemuddin (@raheemuddin786) | 10 | +| 14 | Nguyễn Viết Tuấn (@TheDemonTuan) | 8 | +| 15 | MumuTW | 7 | +| 16 | Paijo (@oyi77) | 7 | +| 17 | Tobias Andersen (@turbolego) | 6 | +| 18 | Bl0ck (@Bl0ck154) | 5 | +| 19 | SHANMUGAPRIYAN (@geek007git) | 5 | +| 20 | KaspaPulse | 5 | +| 21 | Abhishek Sharma (@abhisheksharma2411) | 4 | +| 22 | Abhishek Divekar (@adivekar-utexas) | 4 | +| 23 | Andrew B. (@AndrianBalanescu) | 4 | +| 24 | NoxzRCW | 4 | +| 25 | opensource-elearning | 4 | ### ✨ New Features @@ -282,6 +282,19 @@ _By commits in `release/v3.8.50..release/v3.8.51` (`091589089c..d6f315018a`), au - **feat(dashboard):** link a log entry's Conversation Context to its owning conversation ([#12646](https://github.com/diegosouzapw/OmniRoute/pull/12646)) — thanks @hartmark - **feat(catalog):** eligibility-gated free-tier bucket ([#12669](https://github.com/diegosouzapw/OmniRoute/pull/12669)) - **feat(db):** fail-closed previous_response_id continuation for redacted video turns (#12150 P2b) ([#12707](https://github.com/diegosouzapw/OmniRoute/pull/12707)) +- **feat(gamification): pay the documented `streak_bonus` and `badge_unlock` XP rewards.** `XP_REWARDS` listed both rewards but the award pipeline never paid them: the private reward table in `events.ts` omitted them, `updateStreak()` did not report when a streak extended, and badge unlocks carried no XP. Every request that extends a daily streak now pays `streak_bonus × streak length` once per UTC day (guarded by a same-day `xp_audit_log` check), and every badge unlocked through the pipeline pays `badge_unlock` once per badge (guarded by the `user_badges` primary key; `unlockBadge()` now reports whether it inserted). Bonus XP flows through the same `addXp` + level sync + global/weekly/monthly leaderboard path as action XP, so level-ups and rankings include it. The Radar supporter recognition unlock stays XP-free. (#12522 — thanks @pacocartones) +- **feat(providers):** advertise a `free-tier` capability in the provider plugin manifest for every provider with documented free models, so sidecars and dashboards can filter free-capable providers without reading the quota catalog ([#12786](https://github.com/diegosouzapw/OmniRoute/pull/12786)) — thanks @maxmad64bis +- **feat(providers):** Added EURouter as an OpenAI-compatible API-key gateway (`https://api.eurouter.ai/v1`), with live model discovery via `passthroughModels`. Its copy states that models are served by third-party upstreams listed per model, so an EU-based router is not read as EU data residency for inference. ([#13025](https://github.com/diegosouzapw/OmniRoute/pull/13025)) — thanks @ntdatt812 +- **feat(providers):** Added GreenPT as an OpenAI-compatible API-key provider (`https://api.greenpt.ai/v1`), with live model discovery via `passthroughModels`. No free-inference badge: the published docs describe a free API subscription billed per token, not a free tier. ([#13024](https://github.com/diegosouzapw/OmniRoute/pull/13024)) — thanks @ntdatt812 +- **feat(i18n):** 9 new locales — Greek (`el`), Croatian (`hr`), Serbian (`sr`), Lithuanian (`lt`), Estonian (`et`), Latvian (`lv`), Slovenian (`sl`), Maltese (`mt`), Irish (`ga`) — across the dashboard, docs mirrors, CLI, README and the site; OmniRoute now ships every official EU language (51 locales). (#13044) +- **feat(i18n):** 8 new locales — Kannada (`kn`), Malayalam (`ml`), Odia (`or`), Punjabi (`pa`), Nepali (`ne`), Sinhala (`si`), Burmese (`my`), Khmer (`km`) — across the dashboard, docs mirrors, CLI, README and the site (59 locales). The translator now restores the ICU literal escape around angle placeholders and splits oversized docs sections before translating. (#0000) ([#13660](https://github.com/diegosouzapw/OmniRoute/pull/13660)) +- feat(providers): **list Agnes 3.0 Flash as the current free chat model, drop retired 1.5 Flash, add Image 2.0/2.5 Flash plus Video 2.5/2.5 Flash, and discover the live `/v1/models` catalog (including the CN host `api.agnes-ai.cn`).** `agnes-1.5-flash` now forwards to `agnes-3.0-flash`. Video 2.5 polls `GET /v1/videos/{id}` (not the V2.0 `/agnesapi` contract). Live `/v1/models` (2026-09-09) no longer serves 1.5; the wiki marks it deprecated. 3.0 Flash is 512K context / 65,536 max output, same window as 2.5. CN-region keys use the existing per-connection base-URL field, default stays `apihub.agnes-ai.com`. ([#13120](https://github.com/diegosouzapw/OmniRoute/pull/13120)) — thanks @HouMinXi +- **feat(sse):** Codex and OpenAI catalogs list GPT-6 Astra with effort aliases (`-low` through `-ultra`); Codex CLI identity pins `@openai/codex@0.153.4` in lockstep with the image ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **feat(dashboard):** the orchestration History tab gained a "Compare runs" mode — toggling it turns each grid cell into a 2-item selection queue (a 3rd click drops the oldest pick), and picking two cells opens a side-by-side comparison panel instead of the usual detail drawer. The panel fetches both runs' detail the same way the drawer does (falling back to persisted history once a run leaves the live TTL window) and shows, per side: identity/source/state, start time, a signed `right - left` delta for duration/cost/event count, the event timeline aligned by index, and any memory hits. One side's fetch failing never blocks the other, and a delta is only ever computed when both sides have a finite value — otherwise it renders "—", never `NaN`. Comparing two runs from different sources or skills still works; a banner marks the deltas as informational rather than hiding them, since the two runs aren't a strict apples-to-apples pair. ([#12677](https://github.com/diegosouzapw/OmniRoute/pull/12677)) +- **feat(dashboard):** badge a conversation that never reached a clean stop ([#12717](https://github.com/diegosouzapw/OmniRoute/pull/12717)) — thanks @hartmark +- **feat(release):** reconcile-changelog tool + version-anchored fragment aggregation ([#12987](https://github.com/diegosouzapw/OmniRoute/pull/12987)) +- **feat(mcp):** carry context_length through MCP list_models_catalog projection (#12776) ([#13406](https://github.com/diegosouzapw/OmniRoute/pull/13406)) — thanks @KooshaPari +- **feat(cli):** make startup readiness budget configurable (#13369) ([#13433](https://github.com/diegosouzapw/OmniRoute/pull/13433)) — thanks @KooshaPari ### 🐛 Bug Fixes @@ -698,6 +711,194 @@ _By commits in `release/v3.8.50..release/v3.8.51` (`091589089c..d6f315018a`), au - **fix(i18n):** quote placeholder in OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES description (#12505) ([#12769](https://github.com/diegosouzapw/OmniRoute/pull/12769)) — thanks @KooshaPari - **fix(api/pricing):** surface validation error message as string, not raw object (#12494) ([#12771](https://github.com/diegosouzapw/OmniRoute/pull/12771)) — thanks @KooshaPari - **fix(sse):** re-enable prompt compression for native Codex passthrough ([#12834](https://github.com/diegosouzapw/OmniRoute/pull/12834)) — thanks @thomasmaerz +- **fix(dashboard):** model health tests for a provider node set to the Responses API now call `/v1/responses` with a Responses-shaped body instead of `/v1/chat/completions` — those models were reported as `Provider returned HTTP 200 but no text content` even though the same model answered normally through `/v1/responses` ([#13070](https://github.com/diegosouzapw/OmniRoute/issues/13070)) ([#13087](https://github.com/diegosouzapw/OmniRoute/pull/13087)) — thanks @ntdatt812 +- fix(routing): stop a round-robin combo's "opencode" targets from collapsing onto the opencode-zen connection (#11912) ([#13283](https://github.com/diegosouzapw/OmniRoute/pull/13283)) +- fix(routing): stop the reactive-compaction debug log from lying when compression is globally disabled (#11977) ([#13276](https://github.com/diegosouzapw/OmniRoute/pull/13276)) +- fix(electron): relativize standalone-bundle symlink targets so Stage 8 manifest verification stops failing on Windows (#11979) ([#13251](https://github.com/diegosouzapw/OmniRoute/pull/13251)) +- fix(dashboard): surface a visible error when Compression Studio's combined preview run fails (#12061) ([#13288](https://github.com/diegosouzapw/OmniRoute/pull/13288)) +- fix(dashboard): make the compression "Effective pipeline" preview honor the active profile and warn when the master switch is off (#12063) ([#13287](https://github.com/diegosouzapw/OmniRoute/pull/13287)) +- fix(providers): scope TinyCMS Web signer's DOM shims to each call instead of leaking them for the process lifetime, and surface a clean HTTP status on a non-JSON interception-toggles error (#12072) ([#13254](https://github.com/diegosouzapw/OmniRoute/pull/13254)) +- fix(guardrails): stop Vision Bridge from re-selecting a model locked after a 404 (#12111) ([#13259](https://github.com/diegosouzapw/OmniRoute/pull/13259)) +- fix(sse): require Responses-shaped body before native OpenAI-compatible passthrough (#12129) ([#13278](https://github.com/diegosouzapw/OmniRoute/pull/13278)) +- fix(providers): minimax-m3 now collapses manual thinking.type:"enabled" to adaptive, preventing upstream 400 (2013) (#12132) ([#13249](https://github.com/diegosouzapw/OmniRoute/pull/13249)) +- fix(db): scope model visibility overrides by modality so hiding a Chat model no longer hides an identically-ID'd Image/Embeddings/etc. model (#12172) ([#13258](https://github.com/diegosouzapw/OmniRoute/pull/13258)) +- fix(db): scope local-provider apiKey dedup to matching base URL so LM Studio/Ollama-style connections support multiple accounts (#12173) ([#13250](https://github.com/diegosouzapw/OmniRoute/pull/13250)) +- fix(providers): refresh Trae's stale Referer/Origin and forward user timezone so imported connections stop failing with 401 (#12190) ([#13255](https://github.com/diegosouzapw/OmniRoute/pull/13255)) +- fix(providers): route opencode-go/gpt-5.6-luna to /responses instead of /chat/completions (#12196) ([#13275](https://github.com/diegosouzapw/OmniRoute/pull/13275)) +- fix(dashboard): allow deleting the last extra-upstream-header row even when invalid (#12251) ([#13252](https://github.com/diegosouzapw/OmniRoute/pull/13252)) +- **fix(i18n):** translate pre-existing `__MISSING__:` keys for `combo.sort`, `requestLogger.detail` expand/collapse, `common.profile`, and `settings.resilienceCredentialHealth*` across 39 locales ([#12272](https://github.com/diegosouzapw/OmniRoute/issues/12272)) ([#13034](https://github.com/diegosouzapw/OmniRoute/pull/13034)) — thanks @HouMinXi +- fix(cli): run the Node.js runtime compatibility guard before the heavy `tsx/esm` + Commander import chain so an unsupported runtime gets a clear message instead of a raw `Invalid regular expression flags` crash (#12296) ([#13253](https://github.com/diegosouzapw/OmniRoute/pull/13253)) +- fix(dashboard): refresh the providers list after deleting a compatible provider node (#12298) ([#13256](https://github.com/diegosouzapw/OmniRoute/pull/13256)) +- fix(usage): fail closed on API-key budget enforcement when a provider's `auto` routing alias has no pricing row, instead of silently counting it as $0 (#12341) ([#13257](https://github.com/diegosouzapw/OmniRoute/pull/13257)) +- **fix(providers):** include the submitted Agnes video model when polling by `video_id` ([#12356](https://github.com/diegosouzapw/OmniRoute/pull/12356)) — thanks @pacocartones +- **fix(routing):** custom OpenAI-compatible nodes now honor the saved Chat/Responses API type after edits instead of letting the node's original ID prefix override the live connection setting ([#11884](https://github.com/diegosouzapw/OmniRoute/issues/11884)). ([#12358](https://github.com/diegosouzapw/OmniRoute/pull/12358)) — thanks @pacocartones +- fix(sse): surface an error instead of a silent empty 200 when a Claude stream closes with zero bytes (#12398) ([#13285](https://github.com/diegosouzapw/OmniRoute/pull/13285)) +- fix(oauth): warn before the dead localhost:8080 redirect in antigravity/gemini `oauth start` (#12413) ([#13265](https://github.com/diegosouzapw/OmniRoute/pull/13265)) +- fix(providers): stop devin-cli spawn error from double-closing the SSE controller (#12517) ([#13261](https://github.com/diegosouzapw/OmniRoute/pull/13261)) +- **fix(rerank):** clamp Voyage `top_k` to the documents actually sent after empty-string filtering, and honor `return_documents: false` in the NVIDIA response adapter (#12523 — thanks @pacocartones) +- **fix(audio):** `/v1/audio/translations` now resolves combo names the way `/v1/audio/transcriptions` already does, so a combo that `GET /v1/models` advertises is fanned out to its targets instead of being rejected with `400 Invalid translation model: . Use format: provider/model`; literal `provider/model` ids and unknown bare names behave as before (#12536 — thanks @pacocartones) +- **fix(gemini):** strip the JSON-Schema-2020-12 `prefixItems` keyword from Gemini tool schemas at every nesting level, so Claude Code tool definitions no longer fail with `400 Unknown name "prefixItems"` on Gemini models (#12540 — thanks @pacocartones) +- **fix(api-manager):** Expose an accessible loading status while API keys are fetched instead of an empty accessibility tree (#12541 — thanks @pacocartones) +- **fix(video):** Clamp `estimateJpegFrameBytes` at zero for padding-only payloads and build the three encode-side frame data URIs from `JPEG_FRAME_DATA_URI_PREFIX` instead of a repeated literal (#12543 — thanks @pacocartones) +- **fix(devin):** accept Windows `DEVIN_AGENTIC_HOME` sandbox paths (`C:\...\.sandbox\...`) in the isolated-home check so the Devin Claude Bridge no longer fails closed on Windows ([#12405](https://github.com/diegosouzapw/OmniRoute/issues/12405)) (#12545 — thanks @pacocartones) +- **fix(db):** Add `conversation_turn_nodes` and orphaned `agentic_conversations` to the auto-cleanup cycle under the existing `retention.callLogs` window, so identity nodes whose call-log content has already been purged no longer accumulate without bound in `storage.sqlite` (#12548 — thanks @pacocartones) +- **fix(i18n):** Wrap the `~/.claude/profiles//settings.json` placeholder in ICU single quotes in the `OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES` feature-flag description across all 42 locales and the TypeScript default, so next-intl no longer fails with `INVALID_MESSAGE: UNCLOSED_TAG` and the Feature Flags card shows the description instead of the raw key (#12549 — thanks @pacocartones) +- **fix(orchestration):** `updateCloudAgentTask` now publishes the task's real `status` on `agent.task.updated` when an update only touches `result`, `activities` or `error`, instead of the fabricated `"updated"` state, and stays silent when no row matched the id (#12550 — thanks @pacocartones) +- **fix(i18n):** the home "Recent Requests" panel and the Provider Topology legend are now translated instead of rendering English copies on non-English dashboards; the legend reads its own `home.topologyLegend*` labels with consistent casing rather than borrowing the memory-settings "Recent" and analytics "Error" strings (#12551 — thanks @pacocartones). +- **docs(reference):** bring the `FEATURE_FLAGS.md` catalog back to 1:1 with `featureFlagDefinitions.ts` — 20 missing flags added, the two `*_BLOCK_THRESHOLD` env-only knobs moved out of the flag tables, category/total counts and the Live WS port corrected, guarded by a static test (#12552 — thanks @pacocartones) +- **fix(i18n):** backfill missing `usage.kiloPass*` strings in 39 locales and restore `featureFlagOmnirouteDisableThinkingLevelVariantsDescription` in `pt.json` ([#12561](https://github.com/diegosouzapw/OmniRoute/issues/12561)) ([#13027](https://github.com/diegosouzapw/OmniRoute/pull/13027)) — thanks @HouMinXi +- fix(docker): default docker-compose app ports (dashboard/API/live-WS) to loopback instead of `0.0.0.0`, closing the anonymous `/v1` LAN/WAN exposure gap left open by `REQUIRE_API_KEY=false` (#12568) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): close DNS-rebinding SSRF gap in webhook outbound-URL guard (#12569) ([#13243](https://github.com/diegosouzapw/OmniRoute/pull/13243)) +- fix(docker): require a per-session token on the VNC browser CDP bridge and isolate it on a dedicated Docker network (#12571) ([#13241](https://github.com/diegosouzapw/OmniRoute/pull/13241)) +- fix(open-sse): write Adobe Firefly session tokens and cookie jars with 0700/0600 permissions instead of the process umask (#12572) ([#13242](https://github.com/diegosouzapw/OmniRoute/pull/13242)) +- fix(api): remove hardcoded wildcard CORS in openai-to-gemini-sse.ts so the centralized fail-closed CORS gate is the sole source of `Access-Control-Allow-Origin` (#12573) ([#13239](https://github.com/diegosouzapw/OmniRoute/pull/13239)) +- fix(api): enforce API key policy (budget/rate-limit/schedule/endpoint scoping) on the ElevenLabs speech-to-text, text-to-speech and voices proxy routes (#12574) ([#13244](https://github.com/diegosouzapw/OmniRoute/pull/13244)) +- fix(sse): cap HuggingChat NDJSON body size and bound the read loop with the fetch timeout so a stalled or hostile upstream cannot buffer unbounded memory (#12577) ([#13282](https://github.com/diegosouzapw/OmniRoute/pull/13282)) +- fix(docker): scope the cliproxyapi/qdrant/bifrost sidecars to loopback by default and forward `CLIPROXYAPI_MANAGEMENT_KEY` into the cliproxyapi container so its management API is not left both unauthenticated and LAN-published (#12578) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): create DB export temp paths with `fs.mkdtempSync` instead of predictable timestamps (#12579) ([#13237](https://github.com/diegosouzapw/OmniRoute/pull/13237)) +- Cline 401 bodies that say "re-authenticate your Cline account" classify as a refreshable OAuth token, not a terminal expired key. The cooling panel no longer labels every cooldown as a 429; it shows the recorded last error instead. (#12594) ([#13060](https://github.com/diegosouzapw/OmniRoute/pull/13060)) — thanks @HouMinXi +- **fix(catalog):** degrade unknown combo targets instead of dropping LCD modalities, and persist OpenRouter `architecture.input_modalities` into the capability snapshot ([#12613](https://github.com/diegosouzapw/OmniRoute/issues/12613)) ([#13042](https://github.com/diegosouzapw/OmniRoute/pull/13042)) — thanks @HouMinXi +- fix(providers): send `x-api-key` instead of `Authorization: Bearer` for OpenCode Zen's `/v1/responses` endpoint (Muse Spark Contributor models), fixing a 401 on OmniRoute's auth header (#12633) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(sse): surface the actionable "Auggie CLI not found" message when the shell reports a missing `auggie` binary via exit code instead of a spawn error (#12645) ([#13263](https://github.com/diegosouzapw/OmniRoute/pull/13263)) +- **fix(config):** Persist deletions of built-in background-degradation entries — when a stored settings record exists its `degradationMap` is now authoritative instead of being merged under the defaults, so an entry the user removed in the dashboard no longer reappears on the next apply or restart ([#12424](https://github.com/diegosouzapw/OmniRoute/issues/12424)) ([#12647](https://github.com/diegosouzapw/OmniRoute/pull/12647)) — thanks @pacocartones +- **fix(gamification):** action-count badge milestones (First Token, Token Consumer, Token Machine, Token Whale, and the token-sharing tier) are now backed by a durable `xp_action_counts` counter incremented in `addXp()`, instead of a live `COUNT(*)` over `xp_audit_log`. The audit log is pruned by `retention.xpAuditLog` (default 30 days), so on a default install those "lifetime" milestones were really "actions in the last 30 days" and unlocked badges could stop unlocking once old rows aged out. `getActionCount()` and `checkActionCountBadges()` now read the same durable source, and a migration backfills existing totals from the surviving audit rows ([#12546](https://github.com/diegosouzapw/OmniRoute/issues/12546)) ([#12651](https://github.com/diegosouzapw/OmniRoute/pull/12651)) — thanks @pacocartones +- **fix(images):** `/v1/images/edits` now iterates a combo's targets the same way `/v1/images/generations` does (#9239) instead of flattening a bare combo to its first target. A combo whose first target is not edit-capable — or lacks credentials — now falls through to a later edit-capable target rather than hard-erroring, and missing credentials are skipped (not a hard `401`) to match the generations path. The per-target skip/terminal classification is extracted into a shared `runImageComboTargets` loop, so generations behavior is unchanged ([#12547](https://github.com/diegosouzapw/OmniRoute/issues/12547)). ([#12653](https://github.com/diegosouzapw/OmniRoute/pull/12653)) — thanks @pacocartones +- fix(sse): add first-byte watchdog to the TLS-fingerprint transport so a stalled wreq body falls back instead of hanging for minutes (#12656) ([#13272](https://github.com/diegosouzapw/OmniRoute/pull/13272)) +- fix(sse): exempt tiny-budget reasoning probes from combo quality failure and surface persisted-cooldown skips in ALL_TARGETS_SKIPPED diagnostics (#12659) ([#13269](https://github.com/diegosouzapw/OmniRoute/pull/13269)) +- fix(models): declare the real ~1M contextLength for OpenCode Zen's Muse Spark 1.2 models instead of falling back to the 200000 provider default (#12681) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(oauth): align codebuddy-cn OAuth User-Agent with the chat/usage CLI version to avoid WAF false positives (#12702) ([#13264](https://github.com/diegosouzapw/OmniRoute/pull/13264)) +- fix(dashboard): surface an authentication-required banner instead of silently blanking database settings for a guest session (#12709) ([#13270](https://github.com/diegosouzapw/OmniRoute/pull/13270)) +- **fix(resilience):** stop unbounded queue that hangs 6min until Aborted — gate, provider slot, and Bottleneck queue now share a per-connection `maxWaitMs` budget; `fail-closed` on exhaust (503); execution backstop `executionMaxWaitMs` overridable per connection with upstream clamp ([#12715](https://github.com/diegosouzapw/OmniRoute/pull/12715)) — thanks @maxmad64bis (with thanks to @Tushar49 for surfacing the slow-provider need in #12635) +- **fix(auto-combo):** every mode pack now carries `quality` and `reliability` weights (`quality` 0.02, 0.03 in `quality-first`; `reliability` 0.03, 0.04 in `reliability-first`), so selecting a pack no longer silences either signal; `DEFAULT_WEIGHTS` is unchanged ([#12731](https://github.com/diegosouzapw/OmniRoute/pull/12731)) — thanks @maxmad64bis +- **fix(sse):** the provider execution pipeline keeps the upstream error `code`/`type` again — both were lost when the non-streaming leg was extracted, so a config-class `422` (Antigravity missing project) degraded into a generic account cooldown instead of reaching the gate that recognises it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(db):** a legacy `call_logs` table no longer aborts startup — the provider-stats index is created after the column healing runs, not before it, so an install predating `request_type` opens instead of failing with `no such column` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(catalog):** a malformed row in the operator-writable custom-models blob no longer kills every `auto/*` pool ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(tests):** realign the guards that the same day's merges left asserting the old behavior — the provider-count total after GreenPT and EURouter, the GLM stream buffer slot that `#12925` turned into a declared parameter, and the injection scan bound that `#13104` reshaped from a head-only window into head-plus-tail ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- fix(cache): fold tool_choice/tools/response_format into the semantic cache signature so a cached tool_calls response can no longer be replayed for a request whose tool policy forbids it (#12734) ([#13267](https://github.com/diegosouzapw/OmniRoute/pull/13267)) +- **fix(models):** stop treating provider-supplied `isFree:true`/`:free`/`0/0` flags as trusted unless the provider has a documented free tier — only the shipped free-tier catalog decides otherwise; fetched `isFree:true` trusted only for a free-tier provider, custom `isFree:true` via a trusted path ([#12744](https://github.com/diegosouzapw/OmniRoute/pull/12744)) — thanks @maxmad64bis +- fix(memory): authenticate the internal /v1/rerank loopback call so memory reranking no longer silently degrades to unranked order when REQUIRE_API_KEY=true (#12745) ([#13268](https://github.com/diegosouzapw/OmniRoute/pull/13268)) +- fix(sse): parse Ollama Cloud's current usage markup (`$X of $Y used` aria-label, nested width style) (#12749) ([#13260](https://github.com/diegosouzapw/OmniRoute/pull/13260)) +- fix(cli): setup-opencode no longer sends an active context's management token to `/v1/models` when `--api-key`/`OMNIROUTE_API_KEY` is supplied — an explicit flag or the env var now always outranks the context's token, and the flag itself is no longer swallowed by the parent program's global `--api-key` option (#12783) ([#13246](https://github.com/diegosouzapw/OmniRoute/pull/13246)) +- fix(sse): register Arcee AI in the executor provider registry so requests reach api.arcee.ai instead of silently falling back to OpenAI (#12784) ([#13277](https://github.com/diegosouzapw/OmniRoute/pull/13277)) +- **fix(quota):** share one upstream quota read when identical saturation checks arrive at the same time, so traffic bursts don't multiply provider API calls ([#12787](https://github.com/diegosouzapw/OmniRoute/pull/12787)) — thanks @maxmad64bis +- **fix(db):** ignore expired or invalid rate-limit cooldown writes so a stale timestamp can't lock a connection that should be usable — clearing still works as before ([#12788](https://github.com/diegosouzapw/OmniRoute/pull/12788)) — thanks @maxmad64bis +- **fix(providers):** single source for provider order with `xao` ranking alongside `xai-oauth` ([#12790](https://github.com/diegosouzapw/OmniRoute/pull/12790)) — thanks @maxmad64bis +- **fix(routing):** off-table models check the free-model catalog before inheriting premium prices, latency bootstraps from the observed pool median, async tiers read live database pricing with a 90-day freshness gate, and the tier cache invalidates on every pricing write ([#12792](https://github.com/diegosouzapw/OmniRoute/pull/12792)) — thanks @maxmad64bis +- **fix(auto-combo):** snapshot scoring uses the observed per-connection account tier and per-model quality instead of neutral constants, and circuit-open providers rank lower at build time ([#12794](https://github.com/diegosouzapw/OmniRoute/pull/12794)) — thanks @maxmad64bis +- **fix(pool):** empty `auto/*` pools now say why they are empty, and the models listing uses the same paid check as routing ([#12795](https://github.com/diegosouzapw/OmniRoute/pull/12795)) — thanks @maxmad64bis +- fix(routing): recognize CLIProxyAPI's 'unknown provider for model' 400 as fallback-worthy (#12800) ([#13284](https://github.com/diegosouzapw/OmniRoute/pull/13284)) +- **fix(sse):** translate-mode streams now emit the estimated token counts as a trailing usage-only chunk before `[DONE]` when the upstream stays silent, so metered chat clients see totals instead of nothing ([#12828](https://github.com/diegosouzapw/OmniRoute/pull/12828)) — thanks @maxmad64bis +- **fix(db):** provider stats stay truthful on empty and legacy databases: fallback counts default to `0` instead of `null`, latency averages read `null` (not `0`) when no durations were recorded, failures logged before the error-type column existed group under `pre_migration` instead of `unclassified`, and per-provider queries use two new composite indexes ([#12832](https://github.com/diegosouzapw/OmniRoute/pull/12832)) — thanks @maxmad64bis +- fix(nvidia): fail open when a synced model catalog goes stale instead of gating forever (#12849) ([#13248](https://github.com/diegosouzapw/OmniRoute/pull/13248)) +- **fix(db):** WAL maintenance lives in its own module: a `TRUNCATE` checkpoint that hits a busy database now warns with its streak and retries once via `PASSIVE` instead of logging success, and closing-time checkpoints no longer report success on builds without a database file, with the busy totals visible in the authenticated monitoring health payload ([#12853](https://github.com/diegosouzapw/OmniRoute/pull/12853)) — thanks @maxmad64bis +- **fix(health):** quota with no snapshots reads empty instead of a contradicting 0% ([#12857](https://github.com/diegosouzapw/OmniRoute/pull/12857)) — thanks @maxmad64bis +- fix(a2a): accept the dashboard's own session cookie on /a2a so "Run message/send" no longer fails with "Unauthorized: missing or invalid API key" (#12888) ([#13271](https://github.com/diegosouzapw/OmniRoute/pull/13271)) +- **fix(logs):** the Logs grid's in-memory filter pass no longer discards rows the SQL query already matched — selecting an API key from the dropdown (which sends the key's id) returns its calls again, the Combo tab shows every combo instead of only those whose name contains a "1", and the model filter and search cover the same columns as the query ([#12896](https://github.com/diegosouzapw/OmniRoute/pull/12896)) — fixes [#12873](https://github.com/diegosouzapw/OmniRoute/issues/12873) — thanks @ntdatt812 +- **fix(a2a):** `/api/a2a/status` now builds the agent card from the request that asked for it, so a gateway reached at a non-localhost host no longer advertises `http://localhost:20128` as its A2A URL ([#12918](https://github.com/diegosouzapw/OmniRoute/pull/12918)). — thanks @ntdatt812 +- **fix(compression):** progressive aging now appends its `[COMPRESSED:aging:…]` annotation after a turn's `tool_result` blocks instead of in front of them, so Anthropic no longer rejects aged conversations with "`tool_use` ids were found without `tool_result` blocks immediately after" ([#12920](https://github.com/diegosouzapw/OmniRoute/pull/12920)). — thanks @ntdatt812 +- **fix(bedrock):** model import now resolves context limits for every vendor prefix instead of only `anthropic.*`, so `global.openai.gpt-5.6-*` no longer imports with a null `inputTokenLimit` and gets rejected pre-flight at the 200k default ([#12921](https://github.com/diegosouzapw/OmniRoute/pull/12921)). — thanks @ntdatt812 +- **fix(stream):** the 64 KB stream buffer GLM asks for is honoured instead of dropped, and the type error it caused no longer fails the API Route Typecheck gate on every open PR ([#12925](https://github.com/diegosouzapw/OmniRoute/pull/12925)) — thanks @ntdatt812 +- **fix(guardrails):** mask PII inside a `tool_result`'s nested content array, which the masker walked past while redacting its sibling block ([#12930](https://github.com/diegosouzapw/OmniRoute/pull/12930)) — thanks @ntdatt812 +- **fix(dashboard):** the Radar catalog table no longer leaves absent data unexplained — empty limits, unknown context windows, and unreported capabilities each explain themselves on hover, and a new check keeps it that way ([#12937](https://github.com/diegosouzapw/OmniRoute/pull/12937)) — thanks @maxmad64bis +- **fix(sse):** geo-blocked opencode requests rotate to the next account proxy instead of failing, so one refused egress no longer aborts the whole chain ([#12941](https://github.com/diegosouzapw/OmniRoute/pull/12941)) — thanks @maxmad64bis +- **fix(codex):** dashboard "clear cooldown" and the CAS recovery path now drop nested `codexScopeRateLimitedUntil` maps in the same write that nulls `rate_limited_until`, and fresh quota snapshots with headroom lift fallback-sourced scope cooldowns parked by quota preflight ([#12817](https://github.com/diegosouzapw/OmniRoute/issues/12817), [#12860](https://github.com/diegosouzapw/OmniRoute/issues/12860), [#12951](https://github.com/diegosouzapw/OmniRoute/pull/12951)) The dashboard reset-credit button (`consumeCodexResetCredit`) now exercises that same snapshot path after a successful redeem, so a filled quota bar is enough to unpark a leftover fallback Codex child without another manual clear. — thanks @HouMinXi +- **fix(db):** resolve `sql-wasm.wasm` across global npm install and hoisted layouts, ensuring OmniRoute can boot cleanly on Node 24 when native `better-sqlite3` is uncompiled. ([#13035](https://github.com/diegosouzapw/OmniRoute/pull/13035)) — thanks @HouMinXi +- **fix(api):** `DELETE /v1/batches/delete-completed` now sweeps only the calling API key's own completed batches (batches with no owner stay out of a key-scoped sweep on purpose), with an explicit instance-wide mode reserved for authenticated dashboard sessions, a 401 for a presented key that is unknown, revoked, deactivated, banned or expired (never falling through to the session branch), audit logging of every sweep, a sanitized 500 on failure, an owner-scoped file half (a key-scoped sweep never nulls a file another tenant owns) and an atomic sweep — chunked in 200-batch transactions in instance mode — so a mid-way error never leaves a batch pointing at a nulled file (GHSA-wvxc-jp3v-5mg5) ([#12969](https://github.com/diegosouzapw/OmniRoute/pull/12969)) +- **fix(combo):** quota-weighted routing stops drawing on an out-of-credit connection — a 402 now invalidates the stored quota snapshot instead of leaving its stale remaining percentage in place, and a snapshot older than 10 minutes no longer counts as confident headroom for the primary pool ([#12972](https://github.com/diegosouzapw/OmniRoute/pull/12972)) ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- **fix(vision):** Custom Models with "Vision capable" checked no longer have image requests swapped to `glm/glm-4.6v` when the client sends the advertised alias (`vllm/path/...`) or the bare path-shaped id — Vision Bridge now matches the stored override for all three id forms ([#12758](https://github.com/diegosouzapw/OmniRoute/issues/12758)) ([#12974](https://github.com/diegosouzapw/OmniRoute/pull/12974)) — thanks @HouMinXi +- **fix(sse):** transient opencode upstream failures rotate to the next account proxy instead of failing, so one flapping egress no longer aborts the whole chain ([#12975](https://github.com/diegosouzapw/OmniRoute/pull/12975)) — thanks @maxmad64bis +- **fix(azure):** Deployments from GPT-6 onward now send `max_completion_tokens` instead of `max_tokens`, which Azure rejects with HTTP 400. The rule matched a literal `gpt-5`, so each new generation arrived broken; it now matches the generation range, while `gpt-35-turbo` still keeps `max_tokens`. ([#13007](https://github.com/diegosouzapw/OmniRoute/pull/13007)) — thanks @ntdatt812 +- **fix(db):** add an opt-in self-restart circuit for sustained critical memory pressure, gate post-cleanup VACUUM behind a minimum freed-rows threshold, and checkpoint the SQLite WAL every 5 minutes with a size guard that escalates to TRUNCATE, so a growing WAL can no longer stall the event loop into a full outage. ([#13011](https://github.com/diegosouzapw/OmniRoute/pull/13011)) — thanks @HouMinXi +- **fix(auth):** an explicit connection pin may probe a quota-disabled row once and re-enable it on success ([#12874](https://github.com/diegosouzapw/OmniRoute/issues/12874)) ([#13017](https://github.com/diegosouzapw/OmniRoute/pull/13017)) — thanks @HouMinXi +- **fix(api):** thread `X-OmniRoute-Fallback-Attempts` through combo chat completions so streaming and non-streaming responses report how many prior legs were attempted ([#13038](https://github.com/diegosouzapw/OmniRoute/pull/13038)) — thanks @HouMinXi +- Restore provider failure classification and credential refresh on non-streaming requests: classify non-2xx failures to lock models on per-model quota exhaustion, update connection rate limits from headers and body, and pass credential refresh handlers to pipeline execution so 401 tokens can be refreshed and retried (#13043). ([#13069](https://github.com/diegosouzapw/OmniRoute/pull/13069)) — thanks @HouMinXi +- **fix(responses):** wrap forced-non-streaming web_search fallback JSON as Responses SSE so Codex still sees `response.completed` ([#13050](https://github.com/diegosouzapw/OmniRoute/pull/13050)) — thanks @HouMinXi +- **fix(antigravity):** Preserve upstream thought-token usage in normalized completion and reasoning token counts ([#13055](https://github.com/diegosouzapw/OmniRoute/pull/13055)) — thanks @pacocartones +- Fix a concurrency-slot leak in the DeepSeek PoW solver: a worker that failed to spawn (for example a missing worker script) never released its slot, so `MAX_CONCURRENT_WORKERS` failures disabled the solver until restart. ([#13097](https://github.com/diegosouzapw/OmniRoute/pull/13097)) — thanks @anhtahaylove +- **fix(acp):** bound the ACP session output buffers — `stdoutBuffer` and `stderrBuffer` now cap at 1 MiB keeping the most recent output behind a visible `[...output truncated...]` marker, and `stderrBuffer` is reset per prompt instead of accumulating for the lifetime of the session. ([#13100](https://github.com/diegosouzapw/OmniRoute/pull/13100)) — thanks @anhtahaylove +- **fix(acp):** release the `stdout`/`exit` listeners and the idle timer that a `sendPrompt` timeout used to leave attached to the `acpManager` singleton, and drop sessions that exited on their own from the session map instead of keeping them forever. ([#13096](https://github.com/diegosouzapw/OmniRoute/pull/13096)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection and PII scanners now read the text a `tool_result` block carries on `content` (string or nested block list), in messages and in system blocks, so tool output is judged by the same rules as user text ([#13101](https://github.com/diegosouzapw/OmniRoute/pull/13101)) — thanks @ntdatt812 +- **fix(gamification):** close the badge notification SSE stream when the request signal is already aborted before the stream starts — a client that disconnects while the route is still awaiting auth used to leave both the 2s unlock poll and the 15s heartbeat running for the lifetime of the process. ([#13106](https://github.com/diegosouzapw/OmniRoute/pull/13106)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection scan now spends its 16 KB budget on both ends of the request instead of the first 16 KB only, so `system`, `instructions`, `query`, `documents` and the newest turns are no longer hidden behind one long message ([#13104](https://github.com/diegosouzapw/OmniRoute/pull/13104)) — thanks @ntdatt812 +- **fix(dashboard):** expose the Volcano Ark console cookie on quota scraping and unwrap connect-error objects so the dashboard shows the upstream message ([#13107](https://github.com/diegosouzapw/OmniRoute/pull/13107)) — thanks @HouMinXi +- **fix(db):** release the `beforeExit`/`SIGINT`/`SIGTERM` handlers when a `node:sqlite` adapter closes, so a closed adapter and its database handle are no longer pinned to `process` for the lifetime of the run — the same treatment #7494 gave the sql.js adapter. ([#13109](https://github.com/diegosouzapw/OmniRoute/pull/13109)) — thanks @anhtahaylove +- **fix(translator):** `contentSchema` and `unevaluatedItems` are now treated as subschema positions by the tool-schema sanitizer, so a truncation placeholder in either is replaced with a permissive schema instead of being forwarded as a string ([#13110](https://github.com/diegosouzapw/OmniRoute/pull/13110)) — thanks @ntdatt812 +- **fix(cli-helper):** clear the `createLogStream` timeout on the abort path — `stop()` aborts the in-flight fetch and returned through the `signal.aborted` branch, which skipped `clearTimeout` and left an armed timer per stopped stream. The stream reader is now also cancelled when the read loop exits early. ([#13114](https://github.com/diegosouzapw/OmniRoute/pull/13114)) — thanks @anhtahaylove +- **fix(docs):** drop TLS-impersonation, MITM-decrypt, supply-chain attestation, and XOR-mask writeups from the public `/docs` catalog and Docker image. Files stay in git for engineers; operators who need them open the repo, not the website. ([#13136](https://github.com/diegosouzapw/OmniRoute/pull/13136)) — thanks @HouMinXi +- **fix(combo):** parse numeric-epoch `rate_limited_until` in the combo cooldown read path ([#13141](https://github.com/diegosouzapw/OmniRoute/pull/13141)) — thanks @maxmad64bis +- **fix(opencode):** both OpenCode plugins now reject a gateway address typed without `http://` at configuration time, instead of publishing every model with an api url no client can call, and the v2 plugin no longer publishes a model card whose api url is blank or relative ([#13142](https://github.com/diegosouzapw/OmniRoute/pull/13142)) — thanks @maxmad64bis +- **fix(providers):** lock opencode model on upstream 400 model-unavailable ([#13146](https://github.com/diegosouzapw/OmniRoute/pull/13146)) — thanks @maxmad64bis +- **fix(logging):** keep the provider exchange rather than the raw client bodies when a call log exceeds its size budget, and show that recovered payload in the request-detail panel instead of replacing it with the stored response body ([#13147](https://github.com/diegosouzapw/OmniRoute/pull/13147)) — thanks @maxmad64bis +- **fix(traffic-inspector):** the WebSocket route no longer leaks a traffic-buffer subscriber and a 30s ping timer when the client socket is already closed at handler time — listeners are attached before any resource is acquired, a destroyed socket bails out early, and the ping interval stops on a dead socket where `write()` never throws ([#13155](https://github.com/diegosouzapw/OmniRoute/pull/13155)) — thanks @anhtahaylove +- **fix(telegram):** bound the per-user API key cache in the Telegram chat proxy so a burst of distinct chat ids can no longer grow the process heap without limit ([#13165](https://github.com/diegosouzapw/OmniRoute/issues/13165)) ([#13166](https://github.com/diegosouzapw/OmniRoute/pull/13166)) — thanks @anhtahaylove +- **fix(stream):** cancel the upstream response body when the JSON-to-SSE sniff unwinds on a body timeout, so a stalled upstream no longer pins the connection ([#13169](https://github.com/diegosouzapw/OmniRoute/issues/13169)) ([#13171](https://github.com/diegosouzapw/OmniRoute/pull/13171)) — thanks @anhtahaylove +- **fix(telegram):** authenticate webhook deliveries with Telegram's `secret_token` so an unauthenticated caller can no longer mint API keys or spend upstream quota ([#13172](https://github.com/diegosouzapw/OmniRoute/issues/13172)) ([#13175](https://github.com/diegosouzapw/OmniRoute/pull/13175)) — thanks @anhtahaylove +- **fix(models):** give discoverable Gemini 3.8 Flash ids their own 65536 output spec so Antigravity no longer clamps them to 16384 ([#13195](https://github.com/diegosouzapw/OmniRoute/pull/13195)) — thanks @HouMinXi +- **fix(dashboard):** leftover catalog-removed provider rows (gemini-cli) can be listed and purged from the providers page ([#13067](https://github.com/diegosouzapw/OmniRoute/issues/13067)) ([#13197](https://github.com/diegosouzapw/OmniRoute/pull/13197)) — thanks @HouMinXi +- **fix(auth):** a dashboard session now requires the `authenticated: true` claim that login, OIDC and the session refresh already emit — a JWT merely signed with `JWT_SECRET` (for example the Cursor CLI passthrough token, which any API-key holder can obtain) no longer verifies as the `auth_token` cookie on any route, the WebSocket handshake or the live server; existing sessions keep working ([#13298](https://github.com/diegosouzapw/OmniRoute/issues/13298)) ([#13375](https://github.com/diegosouzapw/OmniRoute/pull/13375)) +- **fix(api):** `DELETE /v1/batches/delete-completed` now applies the caller's API-key policy (endpoint allowlist, schedule, usage cap, rate limit) like every other `/v1` route, commits the key-scoped sweep per 200-batch chunk instead of holding one write lock for the whole sweep, refuses to loop without progress, and rejects a scope that names both a key and `allTenants` ([#13684](https://github.com/diegosouzapw/OmniRoute/pull/13684)) +- Restore the API-route typecheck gate: the non-streaming leg result lost its discriminated-union narrowing after the server-owned tool loop reassignment, producing 13 new TS2339 diagnostics in `chatCore.ts`. ([#12963](https://github.com/diegosouzapw/OmniRoute/pull/12963)) +- Restore four non-streaming behaviours the server-owned tool loop refactor dropped: client aborts map to 499 with the fixed `Request aborted` message, an aborted request no longer logs a synthetic `clientResponse`, Claude prompt-cache telemetry is recorded again, and a body that cannot be canonicalized no longer throws when the tool loop is off. ([#12990](https://github.com/diegosouzapw/OmniRoute/pull/12990)) +- **fix(skills):** The CLI registry parser now reads positionals declared with `.addArgument()`, not only those written inline in `.command()`. `tunnel create [type]` was being published as `tunnel create`, so the agent-skills sync gate reported drift on every branch and regenerating would have deleted the argument. ([#13009](https://github.com/diegosouzapw/OmniRoute/pull/13009)) — thanks @ntdatt812 +- **fix(combos):** dashboard combo test uses a short prompt, serial probes, and a 60s timeout so reasoning models and rate-limited free pools do not fail the health check ([#13001](https://github.com/diegosouzapw/OmniRoute/pull/13001)) — thanks @HouMinXi +- fix(compression): terminate idle worker threads on eviction so long-running instances stop leaking OS threads and MessagePorts ([#13091](https://github.com/diegosouzapw/OmniRoute/pull/13091)) — thanks @anhtahaylove +- Redact Google API keys of any length in error bodies: the pattern required exactly 39 characters, so shorter or longer `AIza…` credentials passed through unredacted. ([#12964](https://github.com/diegosouzapw/OmniRoute/pull/12964)) +- **fix(grok-cli):** a 402 "Grok Build usage balance exhausted" parks that Grok login as out of credit (Grok Build CLI, grok.com cookie, and xAI OAuth share the weekly pool). Combo routing then tries the next login instead of locking the model for every account in the pool ([#13061](https://github.com/diegosouzapw/OmniRoute/pull/13061)) — thanks @HouMinXi +- fix(compression): spawn the LLMLingua worker with a file URL object so compression actually runs instead of silently failing open on Node ([#13093](https://github.com/diegosouzapw/OmniRoute/pull/13093)) — thanks @anhtahaylove +- **fix(test):** run the local `test` and `test:unit` scripts at concurrency 4 so a full-suite run no longer exhausts the machine's commit charge and kills unrelated processes ([#13187](https://github.com/diegosouzapw/OmniRoute/pull/13187)) — thanks @anhtahaylove +- **fix(monitoring):** `GET /api/monitoring/health` `credentialHealth` now includes a bounded `failedConnections` list (`connectionId`, `status`, sanitized `lastError`) when the probe-cache gauge `failed>0`, plus `source: probe-cache` and a cheap `staleDbNonOkCount` for sticky SQLite `test_status` on active rows. Documents that the live gauge is not `provider_connections.test_status`. ([#12876](https://github.com/diegosouzapw/OmniRoute/pull/12876)) — thanks @RaviTharuma +- **fix(resilience):** an openai-compatible multi-upstream gateway no longer marks the whole connection `credits_exhausted` when a single upstream (or the `/models` / representative-model health probe) returns 402. Quota failures stay model-scoped; true connection-wide auth failures are unchanged. Single-credential 402 key disable ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239)) is preserved. ([#12875](https://github.com/diegosouzapw/OmniRoute/pull/12875)) — thanks @RaviTharuma +- **fix(dashboard):** nine fixes on the Orchestration Canvas, all diagnosed in the Phase 2 reviews. A source that fails now keeps the timestamp of its FIRST failure instead of being re-stamped every poll — the stale line said "since the last poll" no matter how long the source had been down, and the churn also defeated the snapshot's stable identity (it serializes the source list), so the canvas re-rendered on every tick while anything was broken. A source that HAD data and only then started failing is flagged too: previously only a source with no node at all got the warning, so a source that went down mid-session kept a healthy-looking node forever. The rest are pointwise: clicking a filter chip cancels the pending search debounce (left armed, it fired ~300ms later and silently reverted the chip); the search input carries an accessible name; `?state=running, failed` parses like the unpadded form instead of dropping the padded value; the CSV toggle helper is defined once in `model/urlParams.ts` rather than twice; the agents tab tells "nothing running" apart from "the filter matched nothing", offering a clear-filters button instead of setup links that would be wrong advice there; edges stop emitting SMIL particles above 40 simultaneously active edges, keeping the colored stroke; and the drawer's error banner clears when a retried action succeeds. ([#12983](https://github.com/diegosouzapw/OmniRoute/pull/12983)) +- **fix(i18n):** the nine locales added with the EU-language batch (Greek, Estonian, Irish, Croatian, Lithuanian, Latvian, Maltese, Slovenian, Serbian) were missing the eleven Orchestration Canvas keys that Phase 3 introduced, so the compare-runs panel and the "no runs match these filters" empty state fell back to English in those languages (`deepMergeFallback` substitutes English for an absent key, so nothing rendered blank — it rendered untranslated). The coverage gate does not catch this: it enforces an 80% floor per locale, and eleven missing keys out of ~13,000 leaves coverage at 99.9%. Translated for real in each language, calibrated against the wording each file already uses for "run", "filter" and "skill". ([#13294](https://github.com/diegosouzapw/OmniRoute/pull/13294)) +- **fix(dashboard):** five follow-ups from the Phase 2 Orchestration Canvas review. The History drawer now shows the memory section for runs that already left the live TTL window: the persisted `memory_hits` event is parsed into `metadata.memoryHits` with the same defensive validation the drawer applies, and — because that event is observability rather than a state transition — it no longer leaks into the timeline, where it had been inheriting the task's state and rendering as a duplicate transition. The A2A memory recall runs against its own 1.5s deadline instead of inheriting the memory backend's 30s one; overshooting degrades exactly like any other recall failure (no hits, task proceeds), and the timer is cleared on both paths. Repeating a Conductor run carries its `requirements.cli`/`requirements.model` forward, so the new run is pinned to the same runner profile and model rather than drifting to whatever the fleet picks. A successful repeat from the Agents tab now focuses the run it created, instead of leaving the operator on the finished one. And the auth test for `POST /api/conductor/tasks` moved into the shared `ROUTES` array rather than restating the pattern. ([#12988](https://github.com/diegosouzapw/OmniRoute/pull/12988)) +- fix(plugins): stop leaking an exit listener per plugin hook timeout, which triggered MaxListenersExceededWarning on plugins that ignore SIGTERM ([#13092](https://github.com/diegosouzapw/OmniRoute/pull/13092)) — thanks @anhtahaylove +- **fix(validation):** Provider node edits no longer fail with a generic "Invalid request" when the optional daily-quota reset fields are left blank. The dashboard sends `dailyQuotaResetTimezone` and `dailyQuotaResetHour` as `null`, and only the hour accepted it. ([#13066](https://github.com/diegosouzapw/OmniRoute/issues/13066)) ([#13083](https://github.com/diegosouzapw/OmniRoute/pull/13083)) — thanks @ntdatt812 +- **fix(combo):** quota-aware expansion drops banned, inactive, missing, and wrong-provider connections before quota fetch or model dispatch; pins and allowlists stay selectors, not a bypass. Antigravity automatic exhaustion now requires a reported zero remaining, so a positive balance below 1% stays eligible. ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- Fix an intermittent failure in the quota-weighted routing test suite: scores are a function of `Date.now()`, so peers with identical quota scored microseconds apart never tied and swapped order. ([#12946](https://github.com/diegosouzapw/OmniRoute/pull/12946)) +- fix(volcengine): resolve quota window mapping gaps, safe multi-connection console binding, and response cookie sanitization ([#12950](https://github.com/diegosouzapw/OmniRoute/pull/12950)) — thanks @HouMinXi +- **fix(test):** resolve the WebDAV handler path with `fileURLToPath` so the suite's 37 WebDAV tests run on Windows instead of failing with a doubled `C:\C:\` drive prefix ([#13196](https://github.com/diegosouzapw/OmniRoute/pull/13196)) — thanks @anhtahaylove +- **fix(ui):** fall back from invalid AnySearch icon alias (direct commit `9587e07b69`) +- **fix(sse):** stop rebuilding a truncated summary from the collector's cap-dropped event array ([#12718](https://github.com/diegosouzapw/OmniRoute/pull/12718)) — thanks @hartmark +- **fix(conversations):** show a pending spinner for unresolved tool nodes instead of "(empty)" ([#12727](https://github.com/diegosouzapw/OmniRoute/pull/12727)) — thanks @hartmark +- **fix(streaming):** fail fast when an upstream stream produces only lifecycle/heartbeat events, never real content ([#12741](https://github.com/diegosouzapw/OmniRoute/pull/12741)) — thanks @hartmark +- **fix(responses-continuation):** bridge the write-in-flight window with an in-memory pending store ([#12854](https://github.com/diegosouzapw/OmniRoute/pull/12854)) — thanks @hartmark +- **fix(translator):** drop or map agent_message on Chat Completions fallback ([#12880](https://github.com/diegosouzapw/OmniRoute/pull/12880)) — thanks @RaviTharuma +- **fix(api):** page and stream-complete GET /v1/models for large catalogs ([#12882](https://github.com/diegosouzapw/OmniRoute/pull/12882)) — thanks @RaviTharuma +- **fix(quota):** align AUTH window labels with usage API and clarify 503 ([#12884](https://github.com/diegosouzapw/OmniRoute/pull/12884)) — thanks @RaviTharuma +- **fix(ci):** clear two base-reds on release/v3.8.51 — mutation-coverage gate + image-only-model guard ([#12945](https://github.com/diegosouzapw/OmniRoute/pull/12945)) +- **fix(api):** scope batch bulk-delete to the calling API key ([#13211](https://github.com/diegosouzapw/OmniRoute/pull/13211)) +- **fix(tests):** align model-sync log assertion to shipped path redaction (base-red) ([#13227](https://github.com/diegosouzapw/OmniRoute/pull/13227)) +- **fix(test):** make the ToS heading guard actually require the parentheses ([#13228](https://github.com/diegosouzapw/OmniRoute/pull/13228)) +- **fix(quality):** register 4 drifted covering tests in stryker tap.testFiles ([#13229](https://github.com/diegosouzapw/OmniRoute/pull/13229)) +- **fix(logging):** mask *-api-key headers in request-log pipeline (#13273) ([#13401](https://github.com/diegosouzapw/OmniRoute/pull/13401)) — thanks @KooshaPari +- **fix(tests):** correct stale Jina catalog prefix in custom-models test ([#13403](https://github.com/diegosouzapw/OmniRoute/pull/13403)) — thanks @KooshaPari +- **fix(backend):** prune old backups after health-check-repair snapshots (#13308) ([#13404](https://github.com/diegosouzapw/OmniRoute/pull/13404)) — thanks @KooshaPari +- **fix(backend):** guard memory_fts_au trigger with WHEN clause to stop FTS bloat (#13326) ([#13405](https://github.com/diegosouzapw/OmniRoute/pull/13405)) — thanks @KooshaPari +- **fix(free-tiers):** remove obsolete Together $25 signup credit (#12526) ([#13407](https://github.com/diegosouzapw/OmniRoute/pull/13407)) — thanks @KooshaPari +- **fix:** skip non-chat generation models in Test all models ([#13409](https://github.com/diegosouzapw/OmniRoute/pull/13409)) — thanks @KooshaPari +- **fix(providers):** de-duplicate agy and antigravity model catalogs ([#13410](https://github.com/diegosouzapw/OmniRoute/pull/13410)) — thanks @KooshaPari +- **fix(dashboard):** send provider in body for mini-playground web search ([#13411](https://github.com/diegosouzapw/OmniRoute/pull/13411)) — thanks @KooshaPari +- **fix(resilience):** add CJK quota-exhaustion patterns to 429 classifier ([#13412](https://github.com/diegosouzapw/OmniRoute/pull/13412)) — thanks @KooshaPari +- **fix(backend):** eval runner: failed calls no longer score as passed + dotAll regex ([#13413](https://github.com/diegosouzapw/OmniRoute/pull/13413)) — thanks @KooshaPari +- **fix(backend):** eval runner opts out of output-style and memory injection ([#13414](https://github.com/diegosouzapw/OmniRoute/pull/13414)) — thanks @KooshaPari +- **fix:** chain serial quarantine step in npm test script (fixes #13286) ([#13418](https://github.com/diegosouzapw/OmniRoute/pull/13418)) — thanks @KooshaPari +- **fix(antigravity):** derive stable session ID from credentials for prompt-cache reuse (#13305) ([#13424](https://github.com/diegosouzapw/OmniRoute/pull/13424)) — thanks @KooshaPari +- **fix(memory):** merge memory into top-level system field instead of unshifting at messages[0] (#13425) ([#13427](https://github.com/diegosouzapw/OmniRoute/pull/13427)) — thanks @KooshaPari +- **fix(compression):** preserve instruction blocks from lossy engines ([#13523](https://github.com/diegosouzapw/OmniRoute/pull/13523)) — thanks @KooshaPari +- **fix(compression):** preserve polarity words and newlines in ultra heuristic ([#13524](https://github.com/diegosouzapw/OmniRoute/pull/13524)) — thanks @KooshaPari +- **fix(compression):** run user preservePatterns before built-in patterns (#13457) ([#13525](https://github.com/diegosouzapw/OmniRoute/pull/13525)) — thanks @KooshaPari +- **fix(reasoning):** expand deepseek-v4 regex to match v4.1+ and add command-code provider (#13430) ([#13528](https://github.com/diegosouzapw/OmniRoute/pull/13528)) — thanks @KooshaPari +- **fix(providers):** add alwaysReasons metadata for reka-flash-3 ([#13531](https://github.com/diegosouzapw/OmniRoute/pull/13531)) — thanks @KooshaPari +- **fix(playground):** allow Compare columns to scroll independently ([#13532](https://github.com/diegosouzapw/OmniRoute/pull/13532)) — thanks @KooshaPari +- **fix(security):** empty whitelist allows through to prevent admin lockout ([#13534](https://github.com/diegosouzapw/OmniRoute/pull/13534)) — thanks @KooshaPari +- **fix(tests):** add DISABLE_IOREG_STRATEGY env var for macOS test compatibility (fixes #13467) ([#13539](https://github.com/diegosouzapw/OmniRoute/pull/13539)) — thanks @KooshaPari +- **fix(zed-hosted):** map developer role and Gemini enums for Zed proxy ([#13543](https://github.com/diegosouzapw/OmniRoute/pull/13543)) — thanks @KooshaPari +- **fix(analytics):** normalize provider aliases to canonical id in usage_history ([#13545](https://github.com/diegosouzapw/OmniRoute/pull/13545)) — thanks @KooshaPari +- **fix(combo):** use traceId for call log id to prevent UNIQUE constraint failure ([#13546](https://github.com/diegosouzapw/OmniRoute/pull/13546)) — thanks @KooshaPari +- **fix(auth):** check model lockout before returning synthetic noauth connection ([#13547](https://github.com/diegosouzapw/OmniRoute/pull/13547)) — thanks @KooshaPari +- **fix(guardrails):** skip credential redaction for base64 image data URLs ([#13550](https://github.com/diegosouzapw/OmniRoute/pull/13550)) — thanks @KooshaPari +- **fix(resilience):** increase requestQueue.maxWaitMs default from 15s to 30s ([#13553](https://github.com/diegosouzapw/OmniRoute/pull/13553)) — thanks @KooshaPari +- **fix(docs):** document OMNIROUTE_READY_TIMEOUT_MS and allowlist the test-only DISABLE_IOREG_STRATEGY ([#13692](https://github.com/diegosouzapw/OmniRoute/pull/13692)) ### 📝 Maintenance @@ -853,6 +1054,39 @@ _By commits in `release/v3.8.50..release/v3.8.51` (`091589089c..d6f315018a`), au - **docs(security):** document socket.yml scanner config + CI workflow link (#12575) ([#12764](https://github.com/diegosouzapw/OmniRoute/pull/12764)) — thanks @KooshaPari - **chore(ci):** guard commit identity in pre-commit to stop author misattribution ([#12772](https://github.com/diegosouzapw/OmniRoute/pull/12772)) - **deps:** 17 Dependabot bumps — bump electron from 43.4.0 to 43.4.1 in /electron ([#11426](https://github.com/diegosouzapw/OmniRoute/pull/11426)); bump the development group across 1 directory with 8 updates ([#11428](https://github.com/diegosouzapw/OmniRoute/pull/11428)); bump the production group across 1 directory with 11 updates ([#11440](https://github.com/diegosouzapw/OmniRoute/pull/11440)); chore(deps): bump github/codeql-action/init from 4.37.7 to 4.37.8 ([#11925](https://github.com/diegosouzapw/OmniRoute/pull/11925)); chore(deps): bump github/codeql-action from 4.37.7 to 4.37.8 ([#11926](https://github.com/diegosouzapw/OmniRoute/pull/11926)); chore(deps): bump github/codeql-action/analyze from 4.37.7 to 4.37.8 ([#11927](https://github.com/diegosouzapw/OmniRoute/pull/11927)); build(deps): bump github/codeql-action/init from 4.37.8 to 4.37.9 ([#12345](https://github.com/diegosouzapw/OmniRoute/pull/12345)); build(deps): bump github/codeql-action/analyze from 4.37.8 to 4.37.9 ([#12346](https://github.com/diegosouzapw/OmniRoute/pull/12346)); bump the development group across 1 directory with 2 updates ([#12347](https://github.com/diegosouzapw/OmniRoute/pull/12347)); build(deps): bump github/codeql-action from 4.37.8 to 4.37.9 ([#12349](https://github.com/diegosouzapw/OmniRoute/pull/12349)); bump browserslist from 4.28.2 to 4.28.8 ([#12396](https://github.com/diegosouzapw/OmniRoute/pull/12396)); bump the production group across 1 directory with 4 updates ([#12399](https://github.com/diegosouzapw/OmniRoute/pull/12399)); bump @xmldom/xmldom ([#12500](https://github.com/diegosouzapw/OmniRoute/pull/12500)); bump qs from 6.15.2 to 6.16.0 ([#12512](https://github.com/diegosouzapw/OmniRoute/pull/12512)); bump @xmldom/xmldom from 0.9.10 to 0.9.12 ([#12513](https://github.com/diegosouzapw/OmniRoute/pull/12513)); bump fast-uri from 3.1.5 to 3.1.7 ([#12514](https://github.com/diegosouzapw/OmniRoute/pull/12514)); bump @humanfs/node from 0.16.7 to 0.16.8 ([#12515](https://github.com/diegosouzapw/OmniRoute/pull/12515)) +- Clear the `release/v3.8.51` `check:agent-skills-sync` base-red: regenerate `skills/cli-tunnel/SKILL.md` so the `tunnel create [type]` positional that #13009 taught the generator to read is reflected in the committed skill. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` typecheck base-red from #12731: the new `Mode pack ranking gates` candidates in `open-sse/services/autoCombo/__tests__/autoCombo.test.ts` omitted the required `provider`, `model` and `errorRate` fields of `ProviderCandidate`, failing `check:open-sse-typecheck` (and with it `Fast Quality Gates`) on every open PR. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` docs-sync base-red: EURouter (#13025) and GreenPT (#13024) took the live provider count to 358, leaving 7 STRICT drifts (`PROVIDER_REFERENCE.md`, 4 diagrams, 2 tier-flow images) plus the `AGENTS.md` / `llm.txt` / `package.json` count claims stale. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- **test(models):** Pin `CATALOG_BUILD_TIMEOUT_MS` in the #12058 canonical-catalog regression so a cold catalog build racing the 8s default bound no longer flakes the suite with a 500 ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Allow `@omniroute/opencode-plugin-v2/` in the npm pack policy — [#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870) shipped the v2 plugin beside its v1 sibling without widening the allowlist, so every packed file under it read as an unexpected artifact and failed `check:pack-policy` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Point the release-green validator's pack gate at the tree it is validating — the build-provenance guard defaulted to `origin/main`, which a release branch tip can never be an ancestor of mid-cycle, so `Package artifact` reported a false red on every run and the tarball boot-smoke cascaded off it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **docs(checks):** keep doc counts honest — headings, rankings, catalog, weights, quality gate and scoring diagram now covered ([#12507](https://github.com/diegosouzapw/OmniRoute/pull/12507)) — thanks @maxmad64bis +- **chore(lifecycle):** `check:model-lifecycle` now also diffs `DEFAULT_DEGRADATION_MAP` (the background-task redirect table) against the vendor lifecycle snapshot, refusing a retired id as source or target, with a table-driven unit test beside it. Three rows whose source the vendor had retired — `claude-sonnet-4-20250514`, `gemini-3-pro-preview` and `gpt-5.1-codex` (whose target `gpt-5.1-codex-mini` is retired too) — were dead code, since `checkLifecycle` answers 410 before the redirect runs; they are dropped (#12535 — thanks @pacocartones) +- **test(compression):** cover idle worker eviction at the resource level — the pool must call `terminate()` and must not retain the worker's `MessagePort`, complementing the `exit`-event assertion added with the fix ([#13371](https://github.com/diegosouzapw/OmniRoute/pull/13371)) +- **chore(quality):** raise the `imageGeneration.ts` file-size ceiling for the image-only-model guard that clears the #12945 base-red ([#13392](https://github.com/diegosouzapw/OmniRoute/pull/13392)) +- **chore(quality):** type the combo-test route's JSON response bodies instead of casting them to `any`, drop the now-empty suppression entry, and remove the `hasPerModelQuota` import `chatCore.ts` stopped using when the failure-classification helper was extracted ([#13379](https://github.com/diegosouzapw/OmniRoute/pull/13379)) +- **chore(electron):** the desktop bundle no longer ships the root-level files of every translated docs mirror (`README.md`, `llm.txt`, `CLAUDE.md`, `GEMINI.md`, `CONTRIBUTING.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`) — the packaged app only reads `docs/i18n//docs/**`, which stays. Saves ~11 MB on top of the translated CHANGELOGs already pruned. (#0000) ([#13235](https://github.com/diegosouzapw/OmniRoute/pull/13235)) +- **chore(quality):** retighten the `src/sse/handlers/chat.ts` unused-vars suppression count to the 8 violations that actually remain ([#13385](https://github.com/diegosouzapw/OmniRoute/pull/13385)) +- Bring the documented counts back in line with the code: 171 migrations (was 169) and 20 routing strategies (was 19), plus the regenerated `cli-tunnel` skill reference. ([#12970](https://github.com/diegosouzapw/OmniRoute/pull/12970)) +- Update the documented migration count to 172 after the call-logs provider-stats indexes landed. ([#13209](https://github.com/diegosouzapw/OmniRoute/pull/13209)) +- **chore(ci):** two gates that close the blind spots behind the exclusions above. `check:vitest-exclusions` requires every Vitest exclusion to name a tracking issue and to appear in `config/quality/vitest-exclusions.json` — the previous list grew to 62 files behind a comment pointing at an issue that had been closed for a month. `check-new-key-coverage` requires a key newly added to `en.json` to reach every locale; the existing coverage gate is a percentage floor per locale, so eleven absent keys out of ~13,000 left it at 99.9% while a whole feature shipped untranslated in nine languages. Both are diff-aware, so pre-existing debt stays frozen and neither needed a migration to turn on. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **build:** root layout no longer loads Inter from `next/font/google`, so a production image build does not need fonts.googleapis.com ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **docs(i18n):** bring the eight new `llm.txt` mirrors (km, kn, ml, my, ne, or, pa, si) up to the current provider and migration counts, so `check-docs-sync` stops rejecting every commit ([#13674](https://github.com/diegosouzapw/OmniRoute/pull/13674)) +- **refactor(ui):** flow surfaces (home topology, combo live studio, compression cockpit/waterfall/nodes, token-health badge) express state through the theme-aware `--orch-status-*` tokens instead of fixed dark-mode hex, so green/red/amber/grey stay legible in the light theme; dark mode is byte-identical. Categorical palettes (routing-strategy hues, compression-layer pills, provider brand colors) deliberately stay hex ([#12378](https://github.com/diegosouzapw/OmniRoute/issues/12378)) ([#13203](https://github.com/diegosouzapw/OmniRoute/pull/13203)) +- **chore(tests):** 51 test files that had been excluded from Vitest are running again, restoring roughly 350 assertions to the blocking suite. Each was measured individually first: of the 62 files parked behind the `// #8618 — pre-existing failure` comment, 51 pass against the current tree with no source change, so the exclusions had outlived the failures they were added for. The 11 that genuinely still fail stay excluded, but now point at a live tracker (#13204) rather than at #8618, which was closed in August while the list it tracked kept growing. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **chore(quality):** drop two ESLint suppression entries whose violations no longer exist, so `eslint --suppressions-location` stops rejecting every commit that touches the surrounding files ([#13384](https://github.com/diegosouzapw/OmniRoute/pull/13384)) +- **chore(release):** bring main's post-tag fixes into release/v3.8.51 — sync-back step 2/2 (direct commit `21c488f210`) +- **chore(deps):** patch toml and esbuild in the opencode plugin lockfiles ([#12965](https://github.com/diegosouzapw/OmniRoute/pull/12965)) +- **docs(changelog):** reconcile the v3.8.51 living section — fold 366 fragments, cover every cycle commit, credit every contributor ([#12971](https://github.com/diegosouzapw/OmniRoute/pull/12971)) +- **test(plugin):** wait for the v2 lazy-refresh tier instead of sleeping past it ([#12984](https://github.com/diegosouzapw/OmniRoute/pull/12984)) +- **chore(quality):** rebaseline chatCore.ts after the non-streaming regression fixes ([#13045](https://github.com/diegosouzapw/OmniRoute/pull/13045)) +- **docs(changelog):** add the missing bullet marker to the DeepSeek PoW fragment ([#13200](https://github.com/diegosouzapw/OmniRoute/pull/13200)) +- **chore(deps):** drain the Dependabot queue — 10 of 13 alerts ([#13213](https://github.com/diegosouzapw/OmniRoute/pull/13213)) +- **chore(ci):** make worktrees carry the hooks that enforce the identity gate ([#13236](https://github.com/diegosouzapw/OmniRoute/pull/13236)) +- **chore(deps):** pin csv-parse to ^7.0.2 via overrides (#13117) ([#13300](https://github.com/diegosouzapw/OmniRoute/pull/13300)) — thanks @KooshaPari +- **chore(deps):** bump hono from ^4.12.34 to ^4.13.7 (#13148) ([#13301](https://github.com/diegosouzapw/OmniRoute/pull/13301)) — thanks @KooshaPari +- **chore(deps):** pin joi to ^18.2.8 via overrides (#13085) ([#13302](https://github.com/diegosouzapw/OmniRoute/pull/13302)) — thanks @KooshaPari +- **docs(providers):** add Microsoft 365 Copilot (BizChat) provider guide (#12779) ([#13340](https://github.com/diegosouzapw/OmniRoute/pull/13340)) — thanks @KooshaPari ### 🙌 Contributors @@ -870,6 +1104,7 @@ Thanks to everyone whose work landed in v3.8.51: | [@amaleta](https://github.com/amaleta) | #11906, #11934 | | [@amartinawi](https://github.com/amartinawi) | #11575 | | [@AndrianBalanescu](https://github.com/AndrianBalanescu) | #11083, #11830, #11834, #11844 | +| [@anhtahaylove](https://github.com/anhtahaylove) | #13091, #13092, #13093, #13096, #13097, #13100, #13106, #13109, #13114, #13155, #13166, #13171, #13175, #13187, #13196 | | [@aniruddhaadak80](https://github.com/aniruddhaadak80) | #11760 | | [@app](https://github.com/app) | #12554 | | [@arminanton](https://github.com/arminanton) | #11461, #11513 | @@ -899,10 +1134,10 @@ Thanks to everyone whose work landed in v3.8.51: | [@gonisulaimann](https://github.com/gonisulaimann) | #12368, #12369, #12371 | | [@Gorillaz322](https://github.com/Gorillaz322) | #12207 | | [@groovecityJO](https://github.com/groovecityJO) | #12682 | -| [@hartmark](https://github.com/hartmark) | #11434, #11452, #11473, #11499, #11703, #11983, #11984, #11985, #11986, #11988, #11989, #11990, #11994, #12221, #12293, #12445, #12446, #12447, #12448, #12460, #12461, #12623, #12646, #12650, #12680 | +| [@hartmark](https://github.com/hartmark) | #11434, #11452, #11473, #11499, #11703, #11983, #11984, #11985, #11986, #11988, #11989, #11990, #11994, #12221, #12293, #12445, #12446, #12447, #12448, #12460, #12461, #12623, #12646, #12650, #12680, #12717, #12718, #12727, #12741, #12854 | | [@hizzt](https://github.com/hizzt) | #11894 | | [@hongnoul](https://github.com/hongnoul) | #11484 | -| [@HouMinXi](https://github.com/HouMinXi) | #11411, #11414, #11512, #11518, #11520, #11641, #11642, #11643, #11687, #11779, #11849, #11850, #11851, #11915, #11916, #11918, #11919, #11920, #12013, #12017, #12033, #12042, #12106, #12139, #12166, #12169, #12171, #12205, #12213, #12312, #12325, #12487, #12488, #12495, #12504, #12557, #12566, #12590, #12591, #12624, #12626, #12632, #12637, #12678, #12696, #12697, #12711, #12733, #12746, #12767, #12770, #12789, #12803, #12805, #12811, #12866, #12868, #12899, #12926, #12934 | +| [@HouMinXi](https://github.com/HouMinXi) | #11411, #11414, #11512, #11518, #11520, #11641, #11642, #11643, #11687, #11779, #11849, #11850, #11851, #11915, #11916, #11918, #11919, #11920, #12013, #12017, #12033, #12042, #12106, #12139, #12166, #12169, #12171, #12205, #12213, #12312, #12325, #12487, #12488, #12495, #12504, #12557, #12566, #12590, #12591, #12624, #12626, #12632, #12637, #12678, #12696, #12697, #12711, #12733, #12746, #12767, #12770, #12789, #12803, #12805, #12811, #12866, #12868, #12899, #12926, #12934, #12950, #12951, #12974, #13001, #13006, #13011, #13017, #13026, #13027, #13034, #13035, #13038, #13042, #13050, #13060, #13061, #13069, #13107, #13120, #13136, #13195, #13197 | | [@Hsia97](https://github.com/Hsia97) | #11624 | | [@jacobsparts](https://github.com/jacobsparts) | #11854, #12155, #12167 | | [@joglomedia](https://github.com/joglomedia) | #11980 | @@ -912,11 +1147,11 @@ Thanks to everyone whose work landed in v3.8.51: | [@KaspaPulse](https://github.com/KaspaPulse) | #11389, #11469, #11628, #11666, #11910 | | [@keeltrace](https://github.com/keeltrace) | #12080, #12223 | | [@killer30001000](https://github.com/killer30001000) | #12178 | -| [@KooshaPari](https://github.com/KooshaPari) | #12470, #12592, #12667, #12699, #12703, #12706, #12764, #12769, #12771 | +| [@KooshaPari](https://github.com/KooshaPari) | #12470, #12592, #12667, #12699, #12703, #12706, #12764, #12769, #12771, #13300, #13301, #13302, #13340, #13401, #13403, #13404, #13405, #13406, #13407, #13409, #13410, #13411, #13412, #13413, #13414, #13418, #13424, #13427, #13433, #13523, #13524, #13525, #13528, #13531, #13532, #13534, #13539, #13543, #13545, #13546, #13547, #13550, #13553 | | [@kriptoburak](https://github.com/kriptoburak) | #11370 | | [@KrzysiekSko](https://github.com/KrzysiekSko) | #12425, #12432, #12673 | | [@marcelokarval](https://github.com/marcelokarval) | direct commit / report | -| [@maxmad64bis](https://github.com/maxmad64bis) | #1622, #11435, #11437, #11441, #11537, #11550, #11553, #11555, #11557, #11812, #11842, #11843, #11903, #12151, #12214, #12215, #12218, #12226, #12314, #12316, #12317, #12318, #12319, #12320, #12321, #12870 | +| [@maxmad64bis](https://github.com/maxmad64bis) | #1622, #11435, #11437, #11441, #11537, #11550, #11553, #11555, #11557, #11812, #11842, #11843, #11903, #12151, #12214, #12215, #12218, #12226, #12314, #12316, #12317, #12318, #12319, #12320, #12321, #12507, #12715, #12731, #12744, #12786, #12787, #12788, #12790, #12792, #12794, #12795, #12828, #12832, #12853, #12857, #12870, #12937, #12941, #12975, #13141, #13142, #13146, #13147 | | [@mdigitalbh81](https://github.com/mdigitalbh81) | #12240 | | [@Meet6338-X](https://github.com/Meet6338-X) | #11598, #11609, #12110 | | [@morpheus9393](https://github.com/morpheus9393) | #11943 | @@ -925,11 +1160,11 @@ Thanks to everyone whose work landed in v3.8.51: | [@NightStalker-87](https://github.com/NightStalker-87) | #12183 | | [@NoxzRCW](https://github.com/NoxzRCW) | #11879, #11880, #11881, #11882, #11883, #11935 | | [@ntdat812](https://github.com/ntdat812) | #11585, #12095 | -| [@ntdatt812](https://github.com/ntdatt812) | #11368, #11573, #11574, #11576, #11577, #11580, #11582, #11583, #11588, #11589, #11590, #11591, #11592, #11593, #11672, #12177, #12180 | +| [@ntdatt812](https://github.com/ntdatt812) | #11368, #11573, #11574, #11576, #11577, #11580, #11582, #11583, #11588, #11589, #11590, #11591, #11592, #11593, #11672, #12177, #12180, #12873, #12918, #12920, #12921, #12925, #12930, #13007, #13009, #13024, #13025, #13083, #13087, #13101, #13104, #13110 | | [@official-burak](https://github.com/official-burak) | #11542 | | [@opensource-elearning](https://github.com/opensource-elearning) | #12179, #12189, #12278, #12286 | | [@oyi77](https://github.com/oyi77) | #11408, #11409, #11421, #11505, #11677, #12036, #12110 | -| [@pacocartones](https://github.com/pacocartones) | #11521, #11522, #11527, #11528, #11529, #11530, #11531, #11532, #11533, #11534, #11595, #11599, #11603, #11604, #11605, #11607, #11610, #11676, #11714, #11716, #11718, #11767, #11838, #11860, #11862, #11869, #11871, #11872, #11873, #11903, #11906, #11921, #11934, #12359, #12360, #12361, #12362, #12364, #12365, #12373, #12374, #12375, #12376, #12377, #12379, #12380, #12381, #12386, #12387, #12389, #12390, #12394, #12395, #12397, #12401, #12403, #12404 | +| [@pacocartones](https://github.com/pacocartones) | #11521, #11522, #11527, #11528, #11529, #11530, #11531, #11532, #11533, #11534, #11595, #11599, #11603, #11604, #11605, #11607, #11610, #11676, #11714, #11716, #11718, #11767, #11838, #11860, #11862, #11869, #11871, #11872, #11873, #11903, #11906, #11921, #11934, #12356, #12358, #12359, #12360, #12361, #12362, #12364, #12365, #12373, #12374, #12375, #12376, #12377, #12379, #12380, #12381, #12386, #12387, #12389, #12390, #12394, #12395, #12397, #12401, #12403, #12404, #12522, #12523, #12535, #12536, #12540, #12541, #12543, #12545, #12548, #12549, #12550, #12551, #12552, #12647, #12651, #12653, #13055 | | [@patrykkopycinski](https://github.com/patrykkopycinski) | #11936, #11937, #12224 | | [@PauloFH](https://github.com/PauloFH) | #11509 | | [@PauloHSOliveira](https://github.com/PauloHSOliveira) | #12241 | @@ -941,7 +1176,7 @@ Thanks to everyone whose work landed in v3.8.51: | [@raheemuddin786](https://github.com/raheemuddin786) | #11491, #11839, #11840, #11841, #12003, #12230, #12231, #12232, #12233, #12234 | | [@rahilmavani](https://github.com/rahilmavani) | #11761 | | [@Rahulsharma0810](https://github.com/Rahulsharma0810) | #11771 | -| [@RaviTharuma](https://github.com/RaviTharuma) | #11710, #11727, #11797, #11798, #11802, #11805, #11806, #11811, #12098, #12099, #12101, #12449, #12452, #12472, #12473, #12493, #12533, #12607, #12628, #12631, #12636 | +| [@RaviTharuma](https://github.com/RaviTharuma) | #11710, #11727, #11797, #11798, #11802, #11805, #11806, #11811, #12098, #12099, #12101, #12449, #12452, #12472, #12473, #12493, #12533, #12607, #12628, #12631, #12636, #12875, #12876, #12880, #12882, #12884 | | [@rezjalibd](https://github.com/rezjalibd) | #12186 | | [@RhianB14](https://github.com/RhianB14) | #11692 | | [@rifqiawl](https://github.com/rifqiawl) | #11517, #11519, #11969 | diff --git a/docs/i18n/sk/CHANGELOG.md b/docs/i18n/sk/CHANGELOG.md index 7d15d611fa..561c6392de 100644 --- a/docs/i18n/sk/CHANGELOG.md +++ b/docs/i18n/sk/CHANGELOG.md @@ -8,16 +8,1119 @@ ## [3.8.51] — TBD -_Living section — cycle opened at the v3.8.50 freeze (parallel-cycle model). Bullets are aggregated from `changelog.d/` fragments at each `/generate-release` phase._ +_Living section — reconciled 2026-09-15 from all cycle commits (`091589089c` → `c0f92ec98a`, 916 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 | **123** | +| 📝 Commits in the cycle | **916** | +| 🔀 Pull requests referenced | **892** | +| 📋 Changelog entries | **919** | +| 🙌 Contributors credited in entries | **121** | +| 🤖 Automated dependency commits | 18 | + +**Entries by type** + +| Type | Count | +| --- | ---: | +| 🐛 Fixes | 592 | +| ✨ Features | 131 | +| 🧹 Chore | 93 | +| 📚 Docs | 42 | +| 🧪 Tests | 32 | +| ♻️ Refactor | 8 | +| 🏗️ Build | 7 | +| ⚡ Performance | 7 | +| ⚙️ CI | 2 | +| ⏪ Reverts | 2 | +| 📦 Dependencies | 2 | +| 🔒 Security | 1 | + +### 🏆 Top 25 contributors this cycle + +_By commits in `091589089c..c0f92ec98a`, author identities consolidated via `.mailmap` and the merged PR's GitHub login. Bots excluded._ + +| # | Contributor | Commits | +| ---: | --- | ---: | +| 🥇 | diegosouzapw | 323 | +| 🥈 | Bob.Hou (@HouMinXi) | 79 | +| 🥉 | Paco Cartones (@pacocartones) | 77 | +| 4 | Dizzle (@maxmad64bis) | 49 | +| 5 | Koosha Paridehpour (@KooshaPari) | 43 | +| 6 | Nguyen Thanh Dat (@ntdatt812) | 31 | +| 7 | Ravi Tharuma (@RaviTharuma) | 26 | +| 8 | Markus Hartung (@hartmark) | 22 | +| 9 | Webman (@jonlwheat2-gif) | 22 | +| 10 | backryun | 17 | +| 11 | anhtahaylove | 15 | +| 12 | Rafa Martins (@rafacpti23) | 10 | +| 13 | Syed Raheemuddin (@raheemuddin786) | 10 | +| 14 | Nguyễn Viết Tuấn (@TheDemonTuan) | 8 | +| 15 | MumuTW | 7 | +| 16 | Paijo (@oyi77) | 7 | +| 17 | Tobias Andersen (@turbolego) | 6 | +| 18 | Bl0ck (@Bl0ck154) | 5 | +| 19 | SHANMUGAPRIYAN (@geek007git) | 5 | +| 20 | KaspaPulse | 5 | +| 21 | Abhishek Sharma (@abhisheksharma2411) | 4 | +| 22 | Abhishek Divekar (@adivekar-utexas) | 4 | +| 23 | Andrew B. (@AndrianBalanescu) | 4 | +| 24 | NoxzRCW | 4 | +| 25 | opensource-elearning | 4 | ### ✨ New Features +- **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). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- Added Google AI Studio Gemini batch text-to-speech support through `POST /v1/audio/speech`. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- 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. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(routing):** subscription-first auto groupings — `auto/subscription` routes only through plan-included connections with a documented hard-stop overage and fails closed on exhaustion, while `auto/thrifty` orders the pool `subscription → keyless → free → cheap → premium` and steps up one rung at a time as each is exhausted. Billing class comes from a curated per-connection catalog (uncurated is treated as metered, never plan-included), both reuse STRICT_ZERO_COST's per-connection verification, and a quota reading whose `resetAt` has passed is now refreshed regardless of TTL so routing returns to plan capacity as soon as it resets ([#11146](https://github.com/diegosouzapw/OmniRoute/pull/11146)) — thanks @yourspraveen +- **feat(providers):** publish a management-authenticated versioned web-session credential contract from OmniRoute's canonical browser credential metadata ([#11340](https://github.com/diegosouzapw/OmniRoute/pull/11340)) — thanks @Zartharas +- **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)) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add Xquik X search with typed results, credential validation, REST routing, and MCP selection ([#11370](https://github.com/diegosouzapw/OmniRoute/pull/11370)) — thanks @kriptoburak +- **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)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(dashboard):** surface durable exclusive managed leases in the existing Sessions view, keeping leased clients visible across idle gaps while marking connections with in-flight work as active ([#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389)) — thanks @KaspaPulse +- **build(bun):** allow Turbopack bundler flag on Bun 1.4+ with configurable Webpack fallback ([#11471](https://github.com/diegosouzapw/OmniRoute/pull/11471)) — thanks @TheDemonTuan +- feat(api): add an opt-in `modelVisibilityAllowlist`/`modelVisibilityDenylist` settings pair to curate exactly which models `/v1/models` advertises, mirrored into every `auto/*` combo candidate pool so a denied model cannot be routed to via combo selection either (#11481) ([#11997](https://github.com/diegosouzapw/OmniRoute/pull/11997)) +- **feat(rankings):** the Free Provider Rankings page shows what each provider actually served over the last 24 h. It ranked by ELO alone, which left a provider that answers every call with an error in first place; the usage data was already served by the API but never requested. A provider with too small a sample shows a dash, not a number ([#11546](https://github.com/diegosouzapw/OmniRoute/pull/11546)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **feat(guardrails):** enforce a bounded, deterministic contract for Video Bridge transcripts — 256 cues, 4096 input code units and 4 KiB UTF-8 per cue, 64 KiB total text, malformed-Unicode rejection, focus-window scoping, cross-source reconciliation with contributing-source metadata, and a structural provenance trust boundary so caller JSON can never self-assert `embedded`/`audio-bridge` provenance ([#11652](https://github.com/diegosouzapw/OmniRoute/issues/11652)) ([#12009](https://github.com/diegosouzapw/OmniRoute/pull/12009)) +- **feat(video):** orchestrate optional Video Bridge audio extraction and Audio Bridge STT behind a dual opt-in (operator setting AND per-request signal) — a new loopback-only broker `mode=audio` operation shares the frame path's exact process queue, deadline, AbortSignal, and byte budgets to extract a bounded mono 16 kHz PCM WAV from the same already-downloaded video, then reuses the existing Audio Bridge transcription boundary; provider segment timing is preserved when available and marked coarse otherwise, and every failure degrades to a visual-only-safe partial instead of throwing (#11654). ([#12012](https://github.com/diegosouzapw/OmniRoute/pull/12012)) +- Add a tenant-bound Video Bridge drill-down lifecycle on top of the existing secure cache substrate: opaque hashed handles (never raw session/video identifiers), preview/standard/detail multiresolution variants resampled on read, response pagination capped at 8 frames and 32 MiB, and a new authenticated `/api/v1/video-bridge/drilldown` consumer route that stays disabled for remote access by default and denies cross-key access with the same response as a nonexistent handle (no existence oracle). ([#12006](https://github.com/diegosouzapw/OmniRoute/pull/12006)) +- **test(video):** Add the Video Bridge FU-07/FU-09 promotion-evidence harness (#11656) — a frozen Zod manifest schema covering the 8 required scenario kinds (static scenes, rapid cuts, late facts, fades, blur, small text, close events, visual prompt injection) with a minimum of 3 repetitions per case, deterministic declarative fixture recipes (`videoBridgePromotionFixtures.ts`), a pure medians/p95 metrics aggregator, a pure FU-07/FU-09 promotion-verdict evaluator applying the ticket's exact thresholds (missing token usage always holds), a digest-only persistence layer that never retains raw media or raw model responses, and a versioned per-model promotion allowlist shipped empty with every model defaulting to `hold`. The FU-07/FU-09 promotion verdicts themselves remain HOLD — they require a real evidence run against real models on VPS 192.168.0.15. ([#12008](https://github.com/diegosouzapw/OmniRoute/pull/12008)) +- **feat(video bridge):** "embedded" transcript provenance can now be legitimately earned instead of merely asserted — a bounded, allowlisted (`mov_text`/`subrip`/`webvtt`) subtitle probe runs through the loopback-only Video Bridge broker (at most 2 streams, 10s subdeadline bounded by the request deadline, 256 KiB output, 4096-code-unit lines), normalized through a bounded, ReDoS-safe WebVTT parser and Zod-validated end to end. The adapter always resolves to an explicit `success`/`absent`/`transient_failure` outcome — a subtitle failure never breaks the visual description path, and only a fingerprint-verified broker response (never a caller-declared label) can produce embedded cues (#11659). ([#12011](https://github.com/diegosouzapw/OmniRoute/pull/12011)) +- Default new Antigravity-family connections (agy CLI imports and Antigravity OAuth connects) to model auto-sync, so live model discovery lands in the synced catalog and `/v1/models` picks up freshly released upstream models (e.g. Gemini 3.7 Flash tiers) without code changes. Existing connections keep their current setting; the per-connection dashboard toggle remains the opt-out. (#11685 — thanks @MumuTW) ([#11887](https://github.com/diegosouzapw/OmniRoute/pull/11887)) +- **feat(ui):** enable React Compiler (`reactCompiler: true` + `babel-plugin-react-compiler`) for automatic memoization at build time ([#11783](https://github.com/diegosouzapw/OmniRoute/pull/11783)) — thanks @jonlwheat2-gif +- **feat(providers):** add SeekAi (`seekai.cc`) as an OpenAI-compatible New-API gateway — catalog id `seekai` (alias `ska`), `https://seekai.cc/v1`, live `/v1/models` via `passthroughModels`, aggregator-list membership so New-API balance detection can opt in. No referral/aff codes. ([#11786](https://github.com/diegosouzapw/OmniRoute/issues/11786)) ([#12557](https://github.com/diegosouzapw/OmniRoute/pull/12557)) — thanks @HouMinXi +- **feat(zai):** add GLM-5.3-Flash Coding Plan support (1M context, 128K output, vision, `low|high|max` reasoning) and route `zai` GLM-5.3-family API-key traffic through the OpenAI-compatible Coding Plan endpoint with native thinking defaults ([#11801](https://github.com/diegosouzapw/OmniRoute/pull/11801)) — thanks @Neuron-Mr-White +- **feat(combo):** choose how combo models are ordered — manual, provider, score, or name — via a sort control in the dashboard builder, persisted in `config.modelSort` and re-applied on load and after add ([#11812](https://github.com/diegosouzapw/OmniRoute/pull/11812)) — thanks @maxmad64bis +- **feat(free):** custom models can be marked free-tier via `customModels[].isFree`; `isFreeModel()` is the first door and `hidePaidModels` respects it even for providers outside the free budget ([#11843](https://github.com/diegosouzapw/OmniRoute/pull/11843)) — thanks @maxmad64bis +- **feat(nodejs):** add `5dive` as a `configure` target — `omniroute configure 5dive` / `omniroute setup-5dive` write a 5dive auth profile that points an agent fleet's `claude` seats at OmniRoute, with the root-only write, the loopback-vs-`https` endpoint rule and the per-seat model pin handled explicitly ([#11852](https://github.com/diegosouzapw/OmniRoute/pull/11852)) — thanks @5dive-bot +- **feat(sse):** treat `max` as a first-class reasoning-effort tier and clamp per model family (GLM 5.1+/DeepSeek V4+/Kimi K3+ keep native `max`; o1/MiniMax/Grok/Muse Spark clamp to their upstream ceiling) ([#11875](https://github.com/diegosouzapw/OmniRoute/pull/11875)) — thanks @Chewji9875 +- **feat(providers):** the provider plugin manifest now advertises a `usage-fetch` capability for the 40 providers that have a wired usage/quota fetcher, so external dashboards can read it from `GET /api/v1/provider-plugin-manifest` instead of parsing `open-sse/services/usage.ts` after every release. Discovery only — no new fetcher, no quota change, and the Dashboard quota widget stays gated by `USAGE_SUPPORTED_PROVIDERS`. `USAGE_FETCHER_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/fetcherProviders.ts`) and is re-exported from `services/usage.ts`, keeping the manifest module a light leaf instead of pulling the ~490-module usage dispatcher into the manifest route. ([#11903](https://github.com/diegosouzapw/OmniRoute/pull/11903)) — thanks @maxmad64bis / @pacocartones +- **feat(plugins):** `OMNIROUTE_PLUGINS_DIR` sets the directory the runtime plugin scanner reads — and the root the plugin manager installs into — overriding the `HOME`-derived default, so a Docker/K8s deployment can point straight at its bind-mounted plugin tree instead of moving `HOME` just to relocate the scan path. An image that exports no home no longer scans `/tmp/.omniroute/plugins` in silence: the resolved directory is logged once at startup as `scanner.dir_resolved`, naming the input that won. Unset, behaviour is unchanged. Distinct from the CLI-only `OMNIROUTE_PLUGIN_PATH`, which finds `omniroute-cmd-*` command packages and never reached this scanner ([#11906](https://github.com/diegosouzapw/OmniRoute/pull/11906)) — thanks @amaleta / @pacocartones +- **feat(leases):** add an explicit owner-authenticated status action that returns only the active lease's privacy-safe configured connection and provider labels, with generation fencing and no credential or internal-id disclosure ([#11910](https://github.com/diegosouzapw/OmniRoute/pull/11910)) — thanks @KaspaPulse +- **feat(providers):** import-from-file modal shows per-row API errors and ships a downloadable CSV template ([#12071](https://github.com/diegosouzapw/OmniRoute/issues/12071)) ([#12504](https://github.com/diegosouzapw/OmniRoute/pull/12504)) — thanks @HouMinXi +- **feat(providers):** dashboard search matches connection name and `baseUrl` so imported OpenAI-compat nodes surface on the provider card ([#12108](https://github.com/diegosouzapw/OmniRoute/issues/12108)) ([#12495](https://github.com/diegosouzapw/OmniRoute/pull/12495)) — thanks @HouMinXi +- **feat(routing):** add a `score` Auto router strategy that selects the highest configured weighted score and reuses `explorationRate`. ([#12155](https://github.com/diegosouzapw/OmniRoute/pull/12155)) — thanks @jacobsparts +- Add a runtime feature flag to disable universal context handoffs globally without changing the default behavior. ([#12167](https://github.com/diegosouzapw/OmniRoute/pull/12167)) — thanks @jacobsparts +- **feat(providers):** the provider plugin manifest now also advertises a `usage-supported` capability for the 46 providers whose usage API is accepted by the server and Dashboard routes, so integrators can distinguish "the server will serve quota for this provider" from "a fetcher is wired" without reading TypeScript. Discovery only — no fetcher or quota change. `usage-fetch` resolves on id or alias (the usage dispatcher accepts both); `usage-supported` resolves on id alone, matching the runtime guard `USAGE_SUPPORTED_PROVIDERS.includes(providerId)`. `USAGE_SUPPORTED_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/supportedProviders.ts`) and is re-exported from `providers.ts`, mirroring the `fetcherProviders` leaf from #11903 and keeping the manifest a light module. ([#12214](https://github.com/diegosouzapw/OmniRoute/pull/12214)) — thanks @maxmad64bis +- **feat(rankings):** order Free Provider Rankings by what each provider actually served — `GET /api/free-provider-rankings?sortBy=reliability` and a "Most reliable first" toggle on the page. Providers with too few calls to state a success rate keep their score order below the measured ones; the default order is unchanged ([#12218](https://github.com/diegosouzapw/OmniRoute/pull/12218)). — thanks @maxmad64bis +- **perf(sse):** defer `cloneLogPayload()` in the structured SSE collector until after the `maxEvents`/`maxBytes` cap check, eliminating ~9,800 wasted `structuredClone` calls per streaming response (65–71% faster `push()`). Reducer snapshot isolation restored for OpenAI and Responses summaries ([#12241](https://github.com/diegosouzapw/OmniRoute/pull/12241)) — thanks @PauloHSOliveira ([#12243](https://github.com/diegosouzapw/OmniRoute/pull/12243)) +- **feat(usage):** Devin CLI agentic quota (Codeium seat-management GetUserStatus) and OpenRouter key limits plus account credits now surface in Provider Limits ([#12256](https://github.com/diegosouzapw/OmniRoute/pull/12256) — thanks @Neuron-Mr-White) +- **feat(settings):** persist `headroomUrl` through Settings so status/start use the operator URL instead of only `HEADROOM_URL` ([#12306](https://github.com/diegosouzapw/OmniRoute/issues/12306)) ([#12487](https://github.com/diegosouzapw/OmniRoute/pull/12487)) — thanks @HouMinXi +- **feat(auto-combo):** Auto-Combo scoring can now weigh how often a provider/model has actually succeeded. The engine already carried that number on every candidate — 24 hours of usage history behind a ten-sample floor, real-time metrics otherwise — and the scoring function never read it, while the weight table described `stability` as if it did. `reliability` (`1 - failureRate`, with the same field precedence and the same rate-bounding the speed ranking already uses, so a corrupt reading means "nothing observed" rather than "fails every call") is now a declared factor shipping at weight `0`, so routing is unchanged until an operator gives it one, and the `stability` description now matches what that factor computes ([#12317](https://github.com/diegosouzapw/OmniRoute/pull/12317)) — thanks @maxmad64bis +- **feat(routing):** With `freeAccessPolicy: "strict"`, the read-only candidate listing (`GET /v1/auto-combo/{channel}/candidates`) no longer hides the candidates the zero-cost guard excludes — the same read-only transparency the resilience filter already honours (#9133). Each candidate now carries `freeAccessExclusion` saying why it would be kept out, and it tells an exhausted allowance apart from a quota reading that never arrived or went stale, which used to look identical from the outside. Routing is unchanged: the listing reports, it never enforces. The separate `excludeTosAvoid` guard still drops its candidates without a reason; that gap is now documented rather than closed ([#12319](https://github.com/diegosouzapw/OmniRoute/pull/12319)) — thanks @maxmad64bis +- **feat(radar):** The Radar catalog table now shows two facts it was already receiving from the feed and dropping on the floor: the per-model rate limits (requests and tokens, per minute and per day) in a new column, and a badge when a provider's terms state it may train on the prompts you send. A limit of zero renders as zero rather than "rate-only" — for a ceiling those are opposite facts — and a model with no training statement gets no badge, because an absent statement is not a guarantee ([#12320](https://github.com/diegosouzapw/OmniRoute/pull/12320)) — thanks @maxmad64bis +- **feat(radar):** explain Community, single-use, contributor, supporter, recovery, abuse, offers, and privacy rules before either Radar activation action, and remove the superseded fixed-PR grant promise from every UI locale ([#12342](https://github.com/diegosouzapw/OmniRoute/pull/12342)) +- **feat(gamification):** the dashboard Profile page now shows the real daily streak — `/api/gamification/level` returns `streak: { current, longest }` (per key with `apiKeyId`, operator-wide maximum otherwise) and the streak card reads it instead of a hard-coded 0 (#2403) ([#12377](https://github.com/diegosouzapw/OmniRoute/pull/12377)) — thanks @pacocartones +- **feat(gamification):** the dashboard leaderboard now shows each API key's display name under the Name column instead of a truncated key id; `GET /api/gamification/leaderboard` attaches `name` per entry (name only — no key material), while the shared ranking helper and the federation leaderboard stay id-only — thanks @pacocartones ([#12385](https://github.com/diegosouzapw/OmniRoute/pull/12385)) +- **feat(gamification):** enforce the documented 1000 XP/min per-API-key anti-cheat rate limit on the XP award path; over-limit awards are logged and skipped instead of persisted, and the sliding window now matches the timestamp format stored in `xp_audit_log` ([#2403](https://github.com/diegosouzapw/OmniRoute/issues/2403)) ([#12390](https://github.com/diegosouzapw/OmniRoute/pull/12390)) — thanks @pacocartones +- **feat(admin):** localize the gamification anomalies page — the loading state, the Status column and the Suspicious badge now come from the `common` catalog (new `common.suspicious` key propagated to every locale) — add it to the Gamification sidebar group as `gamification-admin` (`/dashboard/gamification/admin`), and expose the loading and empty states as polite `role="status"` live regions (#12401 — thanks @pacocartones) +- **feat(providers):** skip GitHub combo members missing from the live synced catalog, and drop Copilot models that are policy-disabled or hidden from the model picker ([#12473](https://github.com/diegosouzapw/OmniRoute/pull/12473)) — thanks @RaviTharuma +- **feat(i18n):** locale-expansion tooling — `npm run i18n:add-locale` adds a language to config, dashboard, docs mirrors, CLI, README/indexes and site in one command; browser, CLI and cookie detection resolve `uk`, `fil`/`tl`, `zh-Hant` (and the retired legacy `in` → `id`) via config aliases; `config/i18n.json` now ships in the npm package so the published CLI can read it; new real-translation ratio gate (`npm run i18n:check-ratio`, advisory) with a per-locale ratchet baseline; `run-translation --adopt` restores `.i18n-state.json`; `sync-language-bars` generates 🌐 bars from config; `validate_translation.py` loads its allowlist again. Retires the duplicate `in` locale (Indonesian mislabelled as Hindi) — 42 honest locales; saved `NEXT_LOCALE=in` / `OMNIROUTE_LANG=in` keep working. (#12496) +- **feat(api):** Emit gateway-measured `tokens_per_second` (TTFT excluded) on streaming usage and `X-OmniRoute-Tokens-Per-Second` when first-token latency is known ([#12616](https://github.com/diegosouzapw/OmniRoute/issues/12616)) ([#12631](https://github.com/diegosouzapw/OmniRoute/pull/12631)) — thanks @RaviTharuma +- **feat(opencode-plugin): map gateway cost/usage/tok/s onto OpenCode inference payloads** — the official plugin copies `X-OmniRoute-Response-Cost`, token counts, `X-OmniRoute-Tokens-Per-Second` / `usage.tokens_per_second`, TTFT, and the winning `X-OmniRoute-Model` onto the JSON/SSE body OpenCode already consumes. Missing tok/s is left unset (never `tokens / latency`). (#12636) — thanks @RaviTharuma +- **feat(combo):** add combo strategy `quota-weighted`: skip exhausted accounts, then weighted-draw by leftover / in-flight load; existing conversations stay pinned until the account is empty ([#12789](https://github.com/diegosouzapw/OmniRoute/pull/12789)) — thanks @HouMinXi +- **feat(models):** Account live listings become the chat catalog source for Claude, Codex, Copilot, and AGY; public metadata only fills prices on IDs those accounts already list. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **fix(models):** Union `agy` and `antigravity` live catalogs so `agy/gemini-3.8-flash-high` is not rejected after the prefix folds to `antigravity`. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **feat(opencode):** Add `@omniroute/opencode-plugin-v2`, an opencode v2 plugin that gives a v2 install a route to an OmniRoute gateway: it publishes models, combos and auto-combos into the host catalog, refreshes them lazily behind a 300s TTL, and keeps serving the last known catalog from an on-disk snapshot when the gateway is unreachable. The v1 plugin (`@omniroute/opencode-plugin`) is untouched and keeps working as before. The plugin adapts to the host instead of assuming one shape: it reads the shape the host seeds into the catalog draft and publishes the `api` block, the pre-`api` top-level fields, or both. The gateway key comes from the host credential store when one is connected, so no secret needs to sit in a config file; a source the gateway refuses is named in a warning instead of silently degrading the catalog to raw model ids; and tool schemas bound for a Gemini model are stripped of the JSON-Schema keywords Gemini rejects, so tool calling survives that route ([#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870)) — thanks @maxmad64bis +- feat(services): show sanitized CLIProxyAPI account health from its authenticated management API without exposing credentials, file paths, or raw account metadata (#6342) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add AnySearch free web search + URL extract (webFetch) with typed results, credential validation, REST routing, and MCP selection - fallback-only ([#11690](https://github.com/diegosouzapw/OmniRoute/pull/11690)) — thanks @Xxx91n +- **feat(dashboard):** display clamped `[0, 100]%` cached input token ratio in request logs table ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) — thanks @b3nw +- **feat(catalog):** add `OMNIROUTE_DISABLE_THINKING_LEVEL_VARIANTS` feature flag to optionally filter out thinking level variants from model catalog ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) — thanks @b3nw +- **feat(dashboard):** continuously export call logs to external analytics stores. A pluggable destination registry ships the full Logs-tab record set on an hourly `JobRegistry` cron, with a persisted per-destination cursor, batched inserts, a config UI rendered from each destination's own field descriptors, and a REST layer (`/api/log-export/*`) for CRUD, a connection test, and an on-demand run. A destination can opt into `includeBodies` to also ship the request and response payloads shown in the Logs detail pane, including the client and provider views of each call; this is off by default, and payloads inherit the dashboard's PII sanitisation, secret redaction and `noLog` handling. Google BigQuery is the first destination, using a service-account key stored encrypted at rest and streaming inserts keyed by call-log id, into a table that is day-partitioned on `timestamp` and clustered on `api_key_name`, `provider`, `model` and `status`. ([#11945](https://github.com/diegosouzapw/OmniRoute/pull/11945)) — thanks @dpozimski +- **feat(providers):** add MaxAI as a signed, OpenAI-compatible provider serving its 13 paid chat models (GPT-5.6 / Luna / Thinking, Claude 5 Sonnet, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite, Grok 4.1-fast / 4.5, DeepSeek V3.2 / R1, Llama 3.3 70B) through OmniRoute's `/v1` endpoint, with per-request HMAC-SHA1→SM3→AES request signing, live model + context-window discovery from `/models/get_config`, and prompted tool-calling translated to OpenAI `tool_calls` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI vision input — image_url content parts are forwarded inline in `message_content` to the 6 vision-capable models (GPT-5.6 / Luna / Thinking, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite) ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI image generation — 6 image models (gpt-image-1, dall-e-3, flux-1-schnell/dev/pro, sd3-medium) exposed through `POST /v1/images/generations` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI document RAG — inline base64 file/document attachments are uploaded to MaxAI (content-addressed `doc_id`) and attached to the chat via `doc_list` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** browserless MaxAI onboarding — email device-pair login (`/api/providers/[id]/login`) and signed access-token refresh, so a connection can be created and kept fresh without a real browser or Google OAuth ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** per-provider TLS impersonation profile (MaxAI presents a Windows Firefox-150 client fingerprint) so its bot-sensitive endpoints accept OmniRoute traffic ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** add **Nimble** as a web-search and web-fetch provider (`nimble-search`) — `/v1/search` routes to Nimble's search API at `lite` depth (locale, freshness and include/exclude domain filters mapped onto the shared request contract), and `/v1/web/fetch` routes to Nimble Extract, which covers all four fetch formats (`markdown`, `html`, `links`, `screenshot`) from a single call. One API key serves both surfaces. ([#11620](https://github.com/diegosouzapw/OmniRoute/pull/11620)) — thanks @wildcard +- The npm publish is automatic again, through npm Trusted Publishing (OIDC): the hosted `stage-npm` job publishes with a short-lived credential minted from GitHub's id-token — no `NPM_TOKEN`, no 2FA prompt, provenance attached. `publish_mode=staged` (owner approves with 2FA) and `direct` (token) remain available on `workflow_dispatch`. ([#11931](https://github.com/diegosouzapw/OmniRoute/pull/11931)) +- **feat(providers):** Add **Opper** as an API-key gateway provider — EU-hosted AI gateway with 700+ models from 30+ providers behind one OpenAI-compatible API and one key (`OPPER_API_KEY`); model ids use `provider/model` format (e.g. `anthropic/claude-sonnet-4-6`, `openai/gpt-5`); live model catalog at `https://api.opper.ai/v3/compat/models`; entry mirrors `requesty` (same shape, `passthroughModels: true`, no static seed) ([#11629](https://github.com/diegosouzapw/OmniRoute/pull/11629)) +- **feat(dashboard):** the `/dashboard/orchestration` snapshot hook now subscribes to the `agents` WebSocket channel (`agent.task.updated`) instead of `requests` as its refetch trigger, and relaxes its background poll from 5s to 30s while that WS connection is up — falling back to the tighter 5s cadence, reprogrammed live on any connect/disconnect transition, whenever the socket is down. ([#12409](https://github.com/diegosouzapw/OmniRoute/pull/12409)) +- **feat(dashboard):** Orchestration canvas quick wins — search box plus state/source/provider filter chips with a one-click clear, and per-source collapse/expand, all reflected in the URL so a filtered/collapsed view is shareable and survives a refresh; the detail drawer gained a "copy trace JSON" action and hardened error/empty-state and accessibility handling; the Agents-tab edges now animate traveling particles along active (running) connections; and the canvas node/edge status colors moved off fixed hex values onto theme-aware `--orch-status-*` CSS custom properties, so they adapt correctly to light/dark mode. ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- **chore(dashboard):** Orchestration UI hardening pass and the missing component/model test coverage it called for — `OrchestratorNode`/`ActivityNode`/`OverflowNode` rendering, the `?node=`/overflow-click page routing, the Agents-tab orchestrator-click no-op and `showCompleted` toggle, and the overview kanban's done-column sort order (#12270, #12271). ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- New `/dashboard/orchestration` page: live unified view of everything running — Cloud Agent, A2A and Conductor as a real-time graph (Agents tab), the combo cascade (Routing tab, reusing the Combo Live Studio) and a state kanban (Overview tab), with a detail drawer (trace, cost, approve/cancel). Read-only over existing APIs — no new backend. Canvas concept credit: PR #11815 design ([#12261](https://github.com/diegosouzapw/OmniRoute/pull/12261)) +- **feat(dashboard):** new "History" tab on `/dashboard/orchestration` — an Airflow-style grid of finished runs over a 24h/7d/30d preset window, one row per (source, identity), clicking a cell opens the existing detail drawer. It is backed by real persistence: A2A task lifecycle transitions are now written to the `a2a_tasks` table (purged after 30 days, configurable via `OMNIROUTE_A2A_HISTORY_RETENTION_DAYS`) and served by the new `GET /api/a2a/tasks/history` listing endpoint, with the task-detail route falling back to persisted history once a run leaves the in-memory snapshot. Conductor runs stay remote and are not persisted locally — the tab says so instead of silently omitting them. ([#12479](https://github.com/diegosouzapw/OmniRoute/pull/12479)) +- **feat(dashboard):** the orchestration detail drawer gained a "Repeat" action for Cloud Agent, A2A and Conductor tasks — a two-click confirm (click once to arm, click again within the confirm window to fire) re-submits the original prompt/input as a new run. The button is disabled with an explanatory tooltip whenever the original input can't be recovered from the loaded task detail (e.g. it never carried a prompt, or the detail failed to load). Two limitations of the A2A variant, by design: it targets the `/a2a` JSON-RPC endpoint, which authenticates with an API key only (`REQUIRE_API_KEY=true` or a configured `OMNIROUTE_API_KEY` makes a dashboard-session repeat answer `HTTP 400` — surfaced verbatim in the drawer's error line, never as a success), and the `message/send` call is SYNCHRONOUS: the POST blocks for the whole skill run, so the success confirmation only appears once the repeated task finishes. A dashboard-authenticated A2A creation path is deliberately left to a follow-up — widening the endpoint's auth posture is an operator decision, not a side effect of this feature. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(a2a):** A2A task execution now records which memories were consulted for the task's last user message as `metadata.memoryHits` (id/key/type/content-snippet) plus a `memory_hits` history event, purely for observability — the retrieved memory is never injected into a skill's prompt or behavior. Gated by the `OMNIROUTE_A2A_MEMORY_HITS` kill-switch (default enabled; set to `0` to skip the recall lookup entirely). The drawer's new "Memory used" section lists these hits for a2a tasks and is omitted whenever there are none. Known limitation: recall only resolves under the keyless posture — a keyed caller's task owner is a SHA-256 prefix of the API key, while memory rows are keyed by the database api-key id, and no hash→id lookup exists today, so the hit list stays empty for keyed callers. The recorded hits are also kept out of the task's own `input` (and therefore out of the persisted input and of the "Repeat" request body), so repeating a task never re-sends the previous run's memory snippets. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(providers):** add a Perplexity Agent API provider (`perplexity-agent` / `pplx-agent`) for Perplexity `/v1/responses`, including the documented Anthropic, OpenAI, Google, xAI, DeepSeek, Z.AI, Moonshot/Kimi, NVIDIA, and Perplexity model IDs plus Anthropic-model `max_output_tokens` compatibility. ([#12103](https://github.com/diegosouzapw/OmniRoute/pull/12103)) — thanks @AIB1TAL0S +- **feat(providers):** add RPD (Requests Per Day) limit to provider rate limit overrides across UI, schemas, DB, and i18n ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) — thanks @b3nw +- **feat(providers): add UC Direct (uncensored.com Developer API), the metered OpenAI-compatible surface.** A standard OpenAI-compatible passthrough (default executor) for uncensored.com's official REST API at `https://api.uncensored.com/api/v1`: `X-api-key` auth (never-expiring `uai_sk_live_` key), `POST /chat/completions` with streaming SSE and native tool-calling, and the full live metered catalog (82 models across 15 providers, discovered from the public `GET /v1/models`). Registered as provider `uc-direct` (alias `ucd`). Complements the un-metered `uc` persona provider — same models, metered credits and a plain API key instead of a subscription session. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(providers): add UC (uncensored.com), the un-metered subscription "persona" chat as an OpenAI-compatible provider.** A WebSocket web-app port: a durable Clerk credential mints a short-lived session token per connect (browserless — no API key), driving UC's persona socket. Ships the browserless email-code login (request → verify → harvest), the 19 verified persona models (Claude Opus, Gemini, Grok, GLM, Kimi, DeepSeek, MiniMax, incl. the uncensored variants), prompted `` tool-calling with a per-model code-style dialect + auto-cure retry for guardrailed models, live ``/reasoning split, streaming + non-streaming OpenAI responses, and full quota/auth error surfacing (paywall / message-limit / rate-limit → 429, invalid session → 401 re-login). Full multimodal parity via the persona blob-upload layer: **vision** (image input, 15 vision-capable models), **document RAG** (PDF/doc upload, server-side extraction), **image generation** (22 models), **video generation** (14 models, async signed-url → poll), and **TTS** (streaming MP3). Registered as provider `uc` (alias `ucn`). The metered OpenAI-compatible Developer API is a separate `uc-direct` provider. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(logs):** add copy-all button to request detail modal ([#11083](https://github.com/diegosouzapw/OmniRoute/pull/11083)) — thanks @AndrianBalanescu +- **feat(check):** make serviceKinds required and add the reverse-walk provider consistency gate ([#11392](https://github.com/diegosouzapw/OmniRoute/pull/11392)) — thanks @Tushar49 +- **feat(providers):** only advertise verified-working connections by default ([#11448](https://github.com/diegosouzapw/OmniRoute/pull/11448)) — thanks @alltomatos +- **feat(resilience):** add hierarchical concurrency admission ([#11493](https://github.com/diegosouzapw/OmniRoute/pull/11493)) +- **feat(providers):** derive + surface expiry for JWT-bearing web cookies (#11497) ([#11505](https://github.com/diegosouzapw/OmniRoute/pull/11505)) — thanks @oyi77 +- **feat(tunnels):** support named cloudflared tunnels via CLOUDFLARED_CONFIG ([#11561](https://github.com/diegosouzapw/OmniRoute/pull/11561)) — thanks @ggiak +- **feat(compression):** make proactive context-compression threshold a live setting ([#11564](https://github.com/diegosouzapw/OmniRoute/pull/11564)) — thanks @ragnar-claude +- **feat(resilience):** add provider peak-hour protection ([#11622](https://github.com/diegosouzapw/OmniRoute/pull/11622)) — thanks @Neuron-Mr-White +- **feat(providers):** add 1min.ai provider ([#11631](https://github.com/diegosouzapw/OmniRoute/pull/11631)) — thanks @zero-executioner +- **feat(dashboard):** collapsible JSON tree viewer for request/response payloads ([#11703](https://github.com/diegosouzapw/OmniRoute/pull/11703)) — thanks @hartmark +- **feat(routing):** add LiquidAI LFM2.5-2.6B free tier via OpenRouter ([#11752](https://github.com/diegosouzapw/OmniRoute/pull/11752)) — thanks @brick30llc-ctrl +- **feat(pricing):** add GLM-5.3-Flash pricing, model specs, and catalog registration ([#11830](https://github.com/diegosouzapw/OmniRoute/pull/11830)) — thanks @AndrianBalanescu +- **feat(routing):** add relayMode for schema-locked context handoffs ([#11839](https://github.com/diegosouzapw/OmniRoute/pull/11839)) — thanks @raheemuddin786 +- **feat(config):** add support for runtime system prompt configuration and hot-reloading ([#11841](https://github.com/diegosouzapw/OmniRoute/pull/11841)) — thanks @raheemuddin786 +- **feat(routing):** expand connection-aware quota prefilter across combo strategies (#11682) ([#11850](https://github.com/diegosouzapw/OmniRoute/pull/11850)) — thanks @HouMinXi +- **feat(models):** reactive discovery sync after Antigravity model-not-found 404 ([#11888](https://github.com/diegosouzapw/OmniRoute/pull/11888)) — thanks @MumuTW +- **feat(models):** auto-aliases and token limits from synced Antigravity discovery ([#11889](https://github.com/diegosouzapw/OmniRoute/pull/11889)) — thanks @MumuTW +- **feat(settings):** raise sticky round-robin limit caps to 1000 ([#12015](https://github.com/diegosouzapw/OmniRoute/pull/12015)) — thanks @adivekar-utexas +- **feat(resilience):** operator-configurable global credential health check interval ([#12043](https://github.com/diegosouzapw/OmniRoute/pull/12043)) — thanks @adivekar-utexas +- **feat(quota):** use official OpenCode Go usage API ([#12124](https://github.com/diegosouzapw/OmniRoute/pull/12124)) — thanks @ddarkr +- **feat(resilience):** default the credential health check sweep to 60 minutes ([#12138](https://github.com/diegosouzapw/OmniRoute/pull/12138)) — thanks @adivekar-utexas +- **feat(quality):** new-code mode for the complexity and dead-code ratchets (Clean as You Code) ([#12142](https://github.com/diegosouzapw/OmniRoute/pull/12142)) +- **feat(dashboard):** orchestration canvas — unified model + snapshot hook (part 1/2) ([#12156](https://github.com/diegosouzapw/OmniRoute/pull/12156)) +- **feat(usage):** add Kilo Code balance and Kilo Pass quotas ([#12178](https://github.com/diegosouzapw/OmniRoute/pull/12178)) — thanks @killer30001000 +- **feat(providers):** refresh vendored ChatGPT Web connector to v4.0.7 ([#12181](https://github.com/diegosouzapw/OmniRoute/pull/12181)) — thanks @backryun +- **feat(providers):** manual "Clear cooldown" action in the cooling panel ([#12224](https://github.com/diegosouzapw/OmniRoute/pull/12224)) — thanks @patrykkopycinski +- **feat(providers):** restore ChatGPT Web via clean-room browser transport ([#12239](https://github.com/diegosouzapw/OmniRoute/pull/12239)) — thanks @backryun +- **feat(sse):** wire the PROVIDER_PROFILES window gate into the global provider cooldown ([#12247](https://github.com/diegosouzapw/OmniRoute/pull/12247)) +- **feat(providers):** modernize CLOVA Studio chat and embeddings ([#12277](https://github.com/diegosouzapw/OmniRoute/pull/12277)) — thanks @backryun +- **feat(browser):** adopt Obscura as primary headless browser engine with Chromium fallback ([#12286](https://github.com/diegosouzapw/OmniRoute/pull/12286)) — thanks @opensource-elearning +- **feat(sse):** full language parity for output styles (es/de/fr/it/ru/zh + autoDetect) ([#12289](https://github.com/diegosouzapw/OmniRoute/pull/12289)) +- **feat(quality):** complete test:scoped — --full map rebuild, stdin selection, CI loader parity (#8084 D1) ([#12353](https://github.com/diegosouzapw/OmniRoute/pull/12353)) +- **feat(providers):** refresh Fable, Cursor, and Devin catalogs ([#12367](https://github.com/diegosouzapw/OmniRoute/pull/12367)) — thanks @backryun +- **feat(video):** redact transcript text from logs and durable memory (#12150 P1) ([#12427](https://github.com/diegosouzapw/OmniRoute/pull/12427)) +- **feat(dashboard):** parent-link, genuine-continuation badge, and modal perf fixes ([#12448](https://github.com/diegosouzapw/OmniRoute/pull/12448)) — thanks @hartmark +- **feat(dashboard):** adaptive context-budget dial on compression panel ([#12488](https://github.com/diegosouzapw/OmniRoute/pull/12488)) — thanks @HouMinXi +- **feat(providers):** refresh Z.ai Web models and browser transport ([#12524](https://github.com/diegosouzapw/OmniRoute/pull/12524)) — thanks @backryun +- **feat(video):** redact raw client-snapshot transcript fields in the detailed log (#12150 P2) ([#12528](https://github.com/diegosouzapw/OmniRoute/pull/12528)) +- **feat(providers):** refresh NVIDIA hosted models ([#12538](https://github.com/diegosouzapw/OmniRoute/pull/12538)) — thanks @backryun +- **feat(quota):** Moonshot Open Platform balance and TPD lock for custom nodes ([#12590](https://github.com/diegosouzapw/OmniRoute/pull/12590)) — thanks @HouMinXi +- **feat(video):** redact transcript in the in-memory pending-request snapshot (#12430 item 6) ([#12596](https://github.com/diegosouzapw/OmniRoute/pull/12596)) +- **feat(dashboard):** link a log entry's Conversation Context to its owning conversation ([#12646](https://github.com/diegosouzapw/OmniRoute/pull/12646)) — thanks @hartmark +- **feat(catalog):** eligibility-gated free-tier bucket ([#12669](https://github.com/diegosouzapw/OmniRoute/pull/12669)) +- **feat(db):** fail-closed previous_response_id continuation for redacted video turns (#12150 P2b) ([#12707](https://github.com/diegosouzapw/OmniRoute/pull/12707)) +- **feat(gamification): pay the documented `streak_bonus` and `badge_unlock` XP rewards.** `XP_REWARDS` listed both rewards but the award pipeline never paid them: the private reward table in `events.ts` omitted them, `updateStreak()` did not report when a streak extended, and badge unlocks carried no XP. Every request that extends a daily streak now pays `streak_bonus × streak length` once per UTC day (guarded by a same-day `xp_audit_log` check), and every badge unlocked through the pipeline pays `badge_unlock` once per badge (guarded by the `user_badges` primary key; `unlockBadge()` now reports whether it inserted). Bonus XP flows through the same `addXp` + level sync + global/weekly/monthly leaderboard path as action XP, so level-ups and rankings include it. The Radar supporter recognition unlock stays XP-free. (#12522 — thanks @pacocartones) +- **feat(providers):** advertise a `free-tier` capability in the provider plugin manifest for every provider with documented free models, so sidecars and dashboards can filter free-capable providers without reading the quota catalog ([#12786](https://github.com/diegosouzapw/OmniRoute/pull/12786)) — thanks @maxmad64bis +- **feat(providers):** Added EURouter as an OpenAI-compatible API-key gateway (`https://api.eurouter.ai/v1`), with live model discovery via `passthroughModels`. Its copy states that models are served by third-party upstreams listed per model, so an EU-based router is not read as EU data residency for inference. ([#13025](https://github.com/diegosouzapw/OmniRoute/pull/13025)) — thanks @ntdatt812 +- **feat(providers):** Added GreenPT as an OpenAI-compatible API-key provider (`https://api.greenpt.ai/v1`), with live model discovery via `passthroughModels`. No free-inference badge: the published docs describe a free API subscription billed per token, not a free tier. ([#13024](https://github.com/diegosouzapw/OmniRoute/pull/13024)) — thanks @ntdatt812 +- **feat(i18n):** 9 new locales — Greek (`el`), Croatian (`hr`), Serbian (`sr`), Lithuanian (`lt`), Estonian (`et`), Latvian (`lv`), Slovenian (`sl`), Maltese (`mt`), Irish (`ga`) — across the dashboard, docs mirrors, CLI, README and the site; OmniRoute now ships every official EU language (51 locales). (#13044) +- **feat(i18n):** 8 new locales — Kannada (`kn`), Malayalam (`ml`), Odia (`or`), Punjabi (`pa`), Nepali (`ne`), Sinhala (`si`), Burmese (`my`), Khmer (`km`) — across the dashboard, docs mirrors, CLI, README and the site (59 locales). The translator now restores the ICU literal escape around angle placeholders and splits oversized docs sections before translating. (#0000) ([#13660](https://github.com/diegosouzapw/OmniRoute/pull/13660)) +- feat(providers): **list Agnes 3.0 Flash as the current free chat model, drop retired 1.5 Flash, add Image 2.0/2.5 Flash plus Video 2.5/2.5 Flash, and discover the live `/v1/models` catalog (including the CN host `api.agnes-ai.cn`).** `agnes-1.5-flash` now forwards to `agnes-3.0-flash`. Video 2.5 polls `GET /v1/videos/{id}` (not the V2.0 `/agnesapi` contract). Live `/v1/models` (2026-09-09) no longer serves 1.5; the wiki marks it deprecated. 3.0 Flash is 512K context / 65,536 max output, same window as 2.5. CN-region keys use the existing per-connection base-URL field, default stays `apihub.agnes-ai.com`. ([#13120](https://github.com/diegosouzapw/OmniRoute/pull/13120)) — thanks @HouMinXi +- **feat(sse):** Codex and OpenAI catalogs list GPT-6 Astra with effort aliases (`-low` through `-ultra`); Codex CLI identity pins `@openai/codex@0.153.4` in lockstep with the image ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **feat(dashboard):** the orchestration History tab gained a "Compare runs" mode — toggling it turns each grid cell into a 2-item selection queue (a 3rd click drops the oldest pick), and picking two cells opens a side-by-side comparison panel instead of the usual detail drawer. The panel fetches both runs' detail the same way the drawer does (falling back to persisted history once a run leaves the live TTL window) and shows, per side: identity/source/state, start time, a signed `right - left` delta for duration/cost/event count, the event timeline aligned by index, and any memory hits. One side's fetch failing never blocks the other, and a delta is only ever computed when both sides have a finite value — otherwise it renders "—", never `NaN`. Comparing two runs from different sources or skills still works; a banner marks the deltas as informational rather than hiding them, since the two runs aren't a strict apples-to-apples pair. ([#12677](https://github.com/diegosouzapw/OmniRoute/pull/12677)) +- **feat(dashboard):** badge a conversation that never reached a clean stop ([#12717](https://github.com/diegosouzapw/OmniRoute/pull/12717)) — thanks @hartmark +- **feat(release):** reconcile-changelog tool + version-anchored fragment aggregation ([#12987](https://github.com/diegosouzapw/OmniRoute/pull/12987)) +- **feat(mcp):** carry context_length through MCP list_models_catalog projection (#12776) ([#13406](https://github.com/diegosouzapw/OmniRoute/pull/13406)) — thanks @KooshaPari +- **feat(cli):** make startup readiness budget configurable (#13369) ([#13433](https://github.com/diegosouzapw/OmniRoute/pull/13433)) — thanks @KooshaPari + ### 🐛 Bug Fixes +- **security(streaming):** sanitize generic mid-stream error messages before emitting OpenAI, + Responses, or Claude SSE failure frames and before diagnostic logging, while preserving raw + failures for internal classification and keeping client disconnects out of provider failure state. ([#12457](https://github.com/diegosouzapw/OmniRoute/pull/12457)) +- **fix(claude):** `blockExtraUsage: false` no longer lets 5h quota preflight skip the account; extra usage is billed after the session bar is gone, so the request must reach Anthropic ([#12803](https://github.com/diegosouzapw/OmniRoute/pull/12803)) — thanks @HouMinXi +- Sanitize HuggingChat conversation-creation and message-send transport failures before they reach client error bodies or provider logs. ([#12467](https://github.com/diegosouzapw/OmniRoute/pull/12467)) +- **fix(dashboard):** Keep local and theme-aware provider SVG icons at a definite layout size so Chromium does not collapse them to 0×0 after the v3.8.50 image-rendering change ([#12054](https://github.com/diegosouzapw/OmniRoute/pull/12054)) — thanks @ponkcore +- **fix(security):** Sanitize provider and runtime failures before public API, SSE and MCP responses and before persistent request, proxy and usage logs, preventing credentials, stack traces and host filesystem paths from crossing those boundaries while preserving stable error codes and useful diagnostics. ([#12506](https://github.com/diegosouzapw/OmniRoute/pull/12506)) +- **fix(cli):** `omniroute tunnel create` no longer crashes with `Cannot read properties of undefined (reading optsWithGlobals)` — removed the duplicate positional argument that caused Commander.js to misalign the action callback parameters ([#12295](https://github.com/diegosouzapw/OmniRoute/issues/12295)) ([#12368](https://github.com/diegosouzapw/OmniRoute/pull/12368)) — thanks @gonisulaimann +- Stop advertising Gemini Live-only models as supported audio endpoints until OmniRoute proxies the bidirectional Live protocol. ([#11710](https://github.com/diegosouzapw/OmniRoute/pull/11710)) — thanks @RaviTharuma +- **fix(kie):** reroute `flux/kontext` off the KIE Market `createTask` flow — it is catalogued with `isMarket: true` but has no Market catalog page, so KIE rejected it with "model name not supported"; it now hits the dedicated `POST /api/v1/flux/kontext/generate` / `GET /api/v1/flux/kontext/record-info` endpoints instead (#11296). ([#11985](https://github.com/diegosouzapw/OmniRoute/pull/11985)) — thanks @hartmark +- **fix(kie):** correct 12 more KIE Market catalog ids that were sent to `createTask` unchanged but diverge from KIE's documented upstream `model` values — GPT Image 2 T2I/I2I (drops the `gpt/` prefix), GPT Image 1.5 T2I/I2I (`gpt-image/` namespace), Seedream 5.0 Lite T2I/I2I (drops the `.0`), all 4 Flux 2 variants (`flux-2/` namespace, generic variant renamed `flex`), and Wan 2.7 Image / Image Pro (dash instead of dot) — each verified individually against the literal example request published on docs.kie.ai. `#11326`'s "everything else already matches" claim was wrong a second time (#11296); `z-image/4.0-*`/`z-image/4.5-*` and `flux/kontext` remain open, documented as unresolved in `KIE_MARKET_UPSTREAM_MODEL_IDS`'s comment pending further verification. ([#11667](https://github.com/diegosouzapw/OmniRoute/pull/11667)) +- **fix(dashboard):** `useApiKeySave.handleSaveApiKey` no longer forces a full upstream `/models` catalog sync on every non-curated provider connection save — callers can now pass `skipModelSync: true` to opt out, so a workflow that only wants to add one manual model no longer floods the provider's available-models list with hundreds/thousands of synced entries. The flag is a client-side intent signal only and is stripped before the connection payload is POSTed to `/api/providers`; default behavior (full sync on save) is unchanged when the flag is omitted (#11324) ([#11664](https://github.com/diegosouzapw/OmniRoute/pull/11664)) +- **Provider connections:** keep `tokenExpiresAt` when a connection is created. The create-path allowlist omitted it, so every insert stored NULL and the dashboard token badge could read a fresh connection as expired until its first background refresh ([#11368](https://github.com/diegosouzapw/OmniRoute/pull/11368)). — thanks @ntdatt812 +- **fix(combos):** the combo builder's precision-select, global-model-search, and manual-entry flows now serialize a model step's `model` string using the provider's already-computed routing-alias prefix (e.g. `oc/`) instead of rebuilding it from the raw canonical `providerId`, fixing the no-auth "OpenCode Free" provider (`opencode`) being routed to the unrelated paid "OpenCode Zen" provider (`opencode-zen`) because `opencode` doubles as a manual routing-prefix override ([#11433](https://github.com/diegosouzapw/OmniRoute/issues/11433)). ([#11670](https://github.com/diegosouzapw/OmniRoute/pull/11670)) +- **fix(radar):** the catalog feed cache now keeps `generatedAt`, the date the feed's data was built, next to `fetchedAt`, the date this install downloaded it (#11435). The feed schema requires that date and the sync path validates it, but the cache dropped it — so a feed fetched minutes ago and one carrying weeks-old figures looked identical to everything downstream, including the dashboard's "Last fetched" line. `getRadarCatalog().meta` and `GET /api/radar/status` now report both dates, the latter as its own field rather than folded into `version` — and omitted entirely for the offers and intel caches, which keep no build date, where a `null` would read as "unknown" rather than "never stored". The dashboard still shows only the fetch time; surfacing the build date there needs a new translated label and is left to a follow-up. Rows cached before migration 163 read back as `null`: unknown stays unknown instead of borrowing the fetch time. The referrals cache has persisted the same date since migration 142. — thanks @maxmad64bis +- **fix(cli):** postinstall no longer fills `JWT_SECRET` and `API_KEY_SECRET` in the installed package's `.env` (#11436). `.env.example` ships both blank on purpose: the server restores them from its durable store, or generates and persists them there, in `ensureSecrets()`. Pre-filling them defeated that — the file lives inside the package directory, so `npm i -g` replaced it and postinstall wrote _different_ values, while `ensureSecrets()` (which only acts on an empty variable) never got to restore the real ones. Both secrets rotated silently on every update, invalidating dashboard sessions and API-key CRCs. `STORAGE_ENCRYPTION_KEY` left the same list for the same reason in #1622; its comment pointed at a function that no longer exists and now names the real provisioning path. — thanks @maxmad64bis +- **fix(cli):** the CLI now says when a loaded `.env` lives inside the installed package directory (#11437). It already announces every env file it reads, without distinguishing the ones that survive an update from the one that does not: `npm i -g` replaces the package directory wholesale, so values set there are gone at the next update, silently. The warning names the durable path to move them to, and fires only when that file actually supplied a value — a file entirely shadowed by a durable one supplied nothing. A development checkout stays silent: there the same path is stable and documented in `SETUP_GUIDE.md`. — thanks @maxmad64bis +- **fix(free-models):** the shared `isFreeModel()` predicate no longer reports catalog entries marked `freeType: "discontinued"` as free, so `hidePaidModels` can't route to Pollinations' seven premium models that now require a paid key ([#11441](https://github.com/diegosouzapw/OmniRoute/pull/11441)) — thanks @maxmad64bis +- fix(ui): let AnySearch use the normal provider-icon fallback when LobeHub has no matching icon (#11449) +- Fixed the v3.8.50 Costs and Analytics dashboards so flat-rate Claude Code usage can be shown as an explicitly requested token-price estimate without changing default billed-cost semantics. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed archived usage retention so each request is priced individually instead of pricing a day's summed tokens once, which understated archived cost whenever a day mixed cache-heavy and ordinary requests. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed the Costs dashboard so it discloses when displayed figures include flat-rate token-price estimates instead of labelling them as billed spend, using the flag the analytics API already returns; the month-end projection and the CSV/JSON exports carry the same marker, and billed-cost mode is unchanged. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- **fix(combo):** attach the same combo-diagnostics trace (`poolSize`/`attemptOrder`/`excluded`/`terminalReason`, plus `x-omniroute-combo-*` headers) to the round-robin strategy's and the nested pipeline/fusion runtime-unit loop's "Maximum combo retry limit reached" 503 that the priority-strategy path already attaches for the identical terminal condition — previously those two paths returned a bare, contextless 503 ([#11462](https://github.com/diegosouzapw/OmniRoute/issues/11462)). ([#11665](https://github.com/diegosouzapw/OmniRoute/pull/11665)) +- **fix(bun):** use native `bun:sqlite` in `bootstrap-env` and `sync-env` to avoid loading `better-sqlite3` N-API addon during Bun startup ([#11468](https://github.com/diegosouzapw/OmniRoute/pull/11468)) — thanks @TheDemonTuan +- **fix(docker-bun):** make `Dockerfile.bun` install reliable with pre-install script helpers and native `bun:sqlite` smoke check ([#11470](https://github.com/diegosouzapw/OmniRoute/pull/11470)) — thanks @TheDemonTuan +- **fix(docker-bun):** remove every vendored `better-sqlite3` native addon from the Bun runtime image so startup cannot abort before the `bun:sqlite` fallback ([#11482](https://github.com/diegosouzapw/OmniRoute/pull/11482)) — thanks @TheDemonTuan +- **fix(autoCombo):** effort/alias model variants (`gpt-5.6-sol-xhigh`, `gpt-5.6`, cursor's `claude-4.6-opus-high`) inherit their base model's task fitness instead of falling to the wildcard 0.5 ([#11492](https://github.com/diegosouzapw/OmniRoute/pull/11492)) — thanks @MumuTW +- fix(db): rate-limit repeated Arena ELO leaderboard fetch-failure warnings instead of logging one per sync attempt (#11500) ([#11989](https://github.com/diegosouzapw/OmniRoute/pull/11989)) — thanks @hartmark +- **fix(db):** dedupe the raw `[Encryption] Decryption failed...` log line emitted by the lazy-decrypt views (`createLazyRowProxy`/`createLazyConnectionView`), which power `getProviderConnections()` and were re-triggering that line on every CredentialHealth/model-sync cycle for the same corrupt or stale-key credential — a fresh Proxy over a fresh row on every cycle meant the per-proxy memoization never suppressed it, unlike the dedup `decryptConnectionFields()` already had since [#9927](https://github.com/diegosouzapw/OmniRoute/issues/9927). Now shares that dedupe tracking so the line logs at most once per credential ([#11500](https://github.com/diegosouzapw/OmniRoute/issues/11500)). ([#11678](https://github.com/diegosouzapw/OmniRoute/pull/11678)) +- **fix(intelligence):** Arena ELO sync stores a synthesized base-model row for every effort/harness variant (`claude-opus-5-max` → `claude-opus-5`, `gpt-5.6-sol-xhigh (codex-harness)` → `gpt-5.6-sol`), so bare model requests reach the synced intelligence layer instead of the static fallback table; the hand-maintained `MODEL_ALIAS_MAP` — which collapsed generations (`gpt-5.5` → `gpt-5`) — is gone ([#11506](https://github.com/diegosouzapw/OmniRoute/pull/11506)) — thanks @MumuTW +- **fix(autoCombo,sse):** catalog hygiene — the static task-fitness table no longer ranks retired models above live ones (dead rows dropped, patterns matched on segment boundaries), the 7 deprecation aliases that forwarded to retired or non-existent models now point at the vendors' published replacements, an alias is no longer rewritten when the serving provider offers the id as-is, and a new `check:model-lifecycle` gate diffs both tables against a vendor lifecycle snapshot ([#11507](https://github.com/diegosouzapw/OmniRoute/pull/11507)) — thanks @MumuTW +- **fix(sse):** stop dropping resolved `thoughtSignature` values on parallel (multi tool-call) turns sent to Gemini 3.x — the claude→gemini and openai→gemini translators previously kept the signature only on the *first* function call of a message, causing Gemini to reject subsequent calls in the same turn with HTTP 400 "Function call is missing a thought_signature"; each function call now keeps its own resolved signature ([#11510](https://github.com/diegosouzapw/OmniRoute/issues/11510)). ([#11669](https://github.com/diegosouzapw/OmniRoute/pull/11669)) +- Treat Docker publishing from a non-default release branch as an expected skip instead of a failed workflow, while preserving the guard that prevents it from overwriting the `next` channel. ([#11527](https://github.com/diegosouzapw/OmniRoute/pull/11527)) — thanks @pacocartones +- fix(search): prefer credentialed providers over duckduckgo-free fallback (#11524) — the fallback-only loop ran before the credentialed-providers loop in `executeWebSearch`, making configured providers unreachable when `duckduckgo-free` was available. ([#11565](https://github.com/diegosouzapw/OmniRoute/pull/11565)) — thanks @vsd2807 +- **fix(sse):** cap the upstream headers-wait phase for STREAMING requests to a client-realistic ceiling (110s, under Codex's own ~120s hard client-abort window) instead of the flat 10-minute `FETCH_TIMEOUT_MS` default — that default was 5x longer than the body-phase readiness watchdog's own adaptive bound, so a request whose upstream never returned any response at all (not even headers, e.g. a stalled NVIDIA target behind a tool-heavy Responses→Chat translation) kept the client connection alive on keepalives only, guaranteeing the client's own patience ran out first with an opaque 499 instead of OmniRoute detecting and failing the stall fast. Non-streaming requests are unaffected — they keep the existing flat default (`open-sse/utils/fetchStartTimeoutPolicy.ts`) (#11526) ([#11668](https://github.com/diegosouzapw/OmniRoute/pull/11668)) +- **fix(ci):** protocol-clients E2E harness boots the peer-stamped custom server (`run-next.mjs dev`) instead of the bare `next` CLI, so `LOCAL_ONLY` locality resolves from the real TCP peer stamp and `/api/mcp/audit` answers 200 (open bootstrap preserved via an `OMNIROUTE_E2E_BOOTSTRAP_MODE=open` credential clear in `run-next.mjs`, pinned to loopback `HOST`) instead of a deterministic 403 (#11535) ([#11549](https://github.com/diegosouzapw/OmniRoute/pull/11549)) — thanks @jonlwheat2-gif +- **fix(docs):** the Auto-Combo engine is described with the number of factors it actually declares. Four different counts were in circulation — 6, 9, 13 and 14 across `AGENTS.md`, four guides and six code comments — while `DEFAULT_WEIGHTS` declares 15. `check:docs-counts` now reads that number from the source, so adding a factor without updating the prose fails CI ([#11545](https://github.com/diegosouzapw/OmniRoute/pull/11545)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **fix(quota-share):** in-flight leases are per request, so an aborted request's slot ages out instead of being kept alive by later traffic on the same connection ([#11547](https://github.com/diegosouzapw/OmniRoute/pull/11547)) — thanks @abhisheksharma2411 +- The free-tier summary route now serves the refreshed Radar catalog when the feed is active — the same numbers the dashboard shows — and states which catalog answered plus its real build date, instead of always reporting release-frozen figures with a stale curation date. The supporter-key live feed stays reserved to authenticated callers of the instance. ([#11550](https://github.com/diegosouzapw/OmniRoute/pull/11550)) — thanks @maxmad64bis +- **sse:** fix LiveWS/embed-WS servers crashing at startup under the Node/tsx runtime — `liveServer.ts`, `embedWsProxy.ts` and `apiBridgeServer.ts` imported `@/shared/utils/httpClientAbortGuard` without the `.mjs` extension, so the client-abort crash guard added by [#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556) was unreachable and every dependent test failed with `ERR_MODULE_NOT_FOUND` ([#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556)). ([#11671](https://github.com/diegosouzapw/OmniRoute/pull/11671)) — thanks @jonlwheat2-gif / @yxyxy +- fix(chat): shed any of the matched provider-node's routing identifiers (public prefix or internal id) before model lookup, so composites like `//` no longer reach upstream verbatim (#11557) — thanks @maxmad64bis +- **fix(cache):** a semantic-cache entry now expires on its TTL instead of surviving until the next UTC midnight, and `dbEntries` stops counting expired rows ([#11573](https://github.com/diegosouzapw/OmniRoute/pull/11573)) — thanks @ntdatt812 +- **fix(api):** `/v1/models` schedules its stale-while-revalidate rebuild through Next's `after()` again, so a stale catalog reaches the client before the rebuild blocks the event loop ([#11574](https://github.com/diegosouzapw/OmniRoute/pull/11574)) — thanks @ntdatt812 +- **fix(check):** the `known-symbols` executor conformance gate awaits the now-async `getExecutor()`, so it stops reporting all 142 registered aliases as dead symbols and can detect a lazy import that fails ([#11582](https://github.com/diegosouzapw/OmniRoute/pull/11582)) — thanks @ntdatt812 +- **fix(pack):** register `bin/cli/utils/volatileEnvPath.mjs` in `PACK_ARTIFACT_REQUIRED_PATHS`, so the prune can no longer silently drop a module the published CLI imports on every boot ([#11588](https://github.com/diegosouzapw/OmniRoute/pull/11588)) — thanks @ntdatt812 +- **i18n(vi):** translate the five reliability-column strings on the free-provider rankings page, which rendered as `__MISSING__:` markers in the Vietnamese UI ([#11589](https://github.com/diegosouzapw/OmniRoute/pull/11589)) — thanks @ntdatt812 +- **resilience:** restore the expired-connection retry-budget probe in the token-health sweep — the `!isGitHubAccessTokenOnlyConnection` carve-out reintroduced by #11608 contradicted the boundary pinned by #11592, so a GitHub connection parked at `expired` with retry budget remaining was never probed and could never self-heal ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)). ([#11672](https://github.com/diegosouzapw/OmniRoute/pull/11672)) — thanks @jonlwheat2-gif / @ntdatt812 +- **fix(dashboard):** Provider Stats now displays combo success rates on their native 0–100 percentage scale instead of multiplying them twice ([#11595](https://github.com/diegosouzapw/OmniRoute/pull/11595)) — thanks @pacocartones +- **fix(providers):** route DuckDuckGo Web Chat through the verified `duck.ai` endpoint, refresh its free model catalog, and avoid forwarding unsolved anti-abuse challenges ([#11597](https://github.com/diegosouzapw/OmniRoute/pull/11597)) — thanks @solstxce +- **fix(ui):** Console log Refresh and Copy controls now expose localized accessible names, keep copy actions visible on keyboard focus, and announce copy completion safely ([#11599](https://github.com/diegosouzapw/OmniRoute/pull/11599)) — thanks @pacocartones +- Dropped the stale-`.eslintcache` `restore-keys` fallback from both "Restore ESLint file cache" steps in `ci.yml`, so the blocking `Lint` job can no longer be served per-file verdicts computed under a different lint config, suppressions file or lockfile. `quality.yml` had already dropped it in #11963; `ci.yml` — the workflow that actually gates PRs — had not (#11600). ([#11996](https://github.com/diegosouzapw/OmniRoute/pull/11996)) +- fix(ci): pass `--pass-on-unpruned-suppressions` in `run-eslint-json.mjs` so the CI Lint job no longer fails when a suppression is merely orphaned by a genuine fix, mirroring the identical fix already in `validate-release-green.mjs` (#11600) ([#11983](https://github.com/diegosouzapw/OmniRoute/pull/11983)) — thanks @hartmark +- fix(handoff): enforce provider allowlist for universal handoff (#11602) — universal handoff now skips summarization when the selected summary model's provider is not included in the configured provider allowlist. ([#11619](https://github.com/diegosouzapw/OmniRoute/pull/11619)) — thanks @vsd2807 +- **fix(dashboard):** Show a stable error state when Search Analytics returns an HTTP error or malformed data ([#11603](https://github.com/diegosouzapw/OmniRoute/pull/11603)) — thanks @pacocartones +- **test(gamification):** pin the aggregate profile level to the XP-derived semantics of #11604 — `getAggregateXp()` now derives `currentLevel` from the summed XP (`calculateLevel(sum)`), not `MAX(stored current_level)`, and the #3484 fixture levels are aligned with the XP curve ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)). ([#11676](https://github.com/diegosouzapw/OmniRoute/pull/11676)) — thanks @jonlwheat2-gif / @pacocartones +- **fix(dashboard):** Keep the Profile level and progress aligned with aggregate XP, including bounded handling for invalid totals ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)) — thanks @pacocartones +- **fix(dashboard):** Prevent locked hidden badges from revealing their icon or opening private badge details before they are earned ([#11605](https://github.com/diegosouzapw/OmniRoute/pull/11605)) — thanks @pacocartones +- **fix(dashboard):** Restore keyboard focus after shared modals close and cancel delayed autofocus during cleanup ([#11607](https://github.com/diegosouzapw/OmniRoute/pull/11607)) — thanks @pacocartones +- **fix(dashboard):** Enable Enter and Space activation for clickable data-table rows without hijacking nested controls ([#11610](https://github.com/diegosouzapw/OmniRoute/pull/11610)) — thanks @pacocartones +- **fix(autoCombo,sse):** vendor-retired catalog ids are dropped from the auto-combo candidate pool and no longer win on leftover `arena_elo` / `user_override` rows; `getModelLifecycleDecision` consults `model-lifecycle.json` (prefix-stripped) so aggregator traffic is not `untracked` for ids the snapshot already knows ([#11625](https://github.com/diegosouzapw/OmniRoute/issues/11625)) ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)) — thanks @MumuTW +- **test(chatcore):** move Codex/Claude combo fixtures off models the lifecycle guard now rejects — `gpt-5.1-codex`/`gpt-5-codex` are vendor-retired (snapshot, #11626) and `claude-3-5-sonnet-20241022` is shut down, so native-passthrough and combo-fallback tests switched to `gpt-5.6-sol` and `claude-sonnet-4.6` ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)). ([#11675](https://github.com/diegosouzapw/OmniRoute/pull/11675)) — thanks @jonlwheat2-gif / @MumuTW +- fix(opencode-plugin): stop a free-tier budget that rounds up across a magnitude from rendering as `1000K`/`1000M` in the model picker — `fmtTokens` chose its unit from the raw token count and then rounded with `toFixed(1)`, so 999,950–999,999 printed as `1000K` rather than `1M` and just under a billion printed as `1000M` rather than `1B` ([#11684](https://github.com/diegosouzapw/OmniRoute/pull/11684)) — thanks @abhisheksharma2411 +- Cap prose-derived Antigravity quota resets at the configured model cooldown maximum while preserving authoritative Retry-After headers and Google RetryInfo hints. (#11695) Thanks @pacocartones. ([#11823](https://github.com/diegosouzapw/OmniRoute/pull/11823)) +- Compare `_rebaseline_*` notes structurally in the ratchet-bank verifier so an object-valued note no longer reports itself as rewritten on an unmodified tree — the nightly `bank-ratchet-shrinks` lane had been aborting on every run since 2026-08-11, leaving 41,672 lines of earned file-size shrinks unbanked (#11702). ([#11892](https://github.com/diegosouzapw/OmniRoute/pull/11892)) — thanks @MumuTW +- **fix(build):** `prepublish.ts` bundles the ChatGPT Web (Codex) MCP bridge through `runBuildTool()` instead of spawning `npx.cmd` raw, fixing the build crash on Node ≥ 20/Windows where `.cmd` shims cannot be spawned without a shell (EINVAL) ([#11704](https://github.com/diegosouzapw/OmniRoute/issues/11704)) ([#11706](https://github.com/diegosouzapw/OmniRoute/pull/11706)) — thanks @jonlwheat2-gif +- fix(codex): keep `parallel_tool_calls:false` on the translated Codex Responses Lite path (#11707) ([#11984](https://github.com/diegosouzapw/OmniRoute/pull/11984)) — thanks @hartmark +- **fix(tests):** the new Gemini Web Images retirement test (#11708) called the now-async `getExecutor()` synchronously, throwing `TypeError: getExecutor(...).getProvider is not a function` instead of asserting Gemini Web chat stays available. Awaited to match the R0.3 executor-registry refactor (#11220). ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(search):** Allow REST and MCP web-fetch requests to select the advertised `anysearch-search` provider explicitly ([#11716](https://github.com/diegosouzapw/OmniRoute/pull/11716)) — thanks @pacocartones +- **fix(providers):** Keep overnight peak-hour protection active after midnight according to the window's configured start day ([#11718](https://github.com/diegosouzapw/OmniRoute/pull/11718)) — thanks @pacocartones +- **fix(providers):** correct all five g4f.space gateway descriptions to document proof-of-work credits or member API keys, variable limits, and the remote prompt boundary instead of promising free keyless access at five requests per minute ([#11738](https://github.com/diegosouzapw/OmniRoute/pull/11738)). +- **fix(db):** `model_capabilities` is created by a migration instead of lazily on the first models.dev sync, so a clean install and an upgraded install converge on the same schema regardless of which features have run ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(ci):** `check:install-upgrade` now fails on an `npm` install truncated by ENOSPC (npm reports it as a warning and still exits 0), authenticates its health probe so the version assertion works against the hardened health payload, frees the clean-install tree before the upgrade phase, and no longer reports a schema divergence computed from a boot that never served ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(routing):** harden session-affinity key derivation against oversized explicit IDs and empty structural-payload collisions, while recognizing Gemini and common text request shapes ([#11746](https://github.com/diegosouzapw/OmniRoute/pull/11746)) — thanks @MumuTW +- **fix(packages/browser-pool):** regenerate `package-lock.json` so the `packages/browser-pool` workspace's locked `playwright`/`@types/node` (and transitives) match its `package.json` specs, fixing cache-only/offline installs (`npm ci --offline`, Nix `buildNpmPackage`) that previously failed with `ENOTCACHED` ([#11747](https://github.com/diegosouzapw/OmniRoute/issues/11747)) — thanks @benjaminkitt ([#11784](https://github.com/diegosouzapw/OmniRoute/pull/11784)) — thanks @geekyNads +- **fix(assets):** Stop distributing 79 provider and CLI logos without verified source/license provenance, keep every provider/tool available through the internal generic icon, and prevent those IDs from falling through to an external icon CDN. ([#11750](https://github.com/diegosouzapw/OmniRoute/pull/11750)) +- **fix(providers):** retire the common `chatgpt-web` / `cgpt-web` provider because its pre-key and proof-of-work provenance could not be cleared ([#11754](https://github.com/diegosouzapw/OmniRoute/pull/11754)) — explicit chat and image requests, slash-prefixed models, persisted aliases, proxy overrides, and combo targets now fail closed with a sanitized HTTP `410` / `PROVIDER_RETIRED` before upstream dispatch. Migration 163 tombstones matching connections and invalidates their active leases without deleting history or widening API-key allowlists. The independent `chatgpt-web-codex` / `cgpt-codex` provider and its MIT-noticed vendored implementation remain available. +- **fix(usage):** quota and usage refresh no longer 409 when an exclusive lease reserves the connection ([#11758](https://github.com/diegosouzapw/OmniRoute/pull/11758)) — thanks @TheDemonTuan +- Keep the embedding registry's vector width and `embedding` type on models when a synced model exists for the same id, so `/v1/models` no longer reports registry-described embedding models widthless or untyped (#11761) — thanks @rahilmavani +- Correct `google/gemini-embedding-001` on the OpenRouter route to 3072 dimensions, the width it returns when `dimensions` is not sent (#11761) — thanks @rahilmavani +- **fix(vision-bridge):** Restrict automatic vision routing and fallbacks to models available in authoritative live provider catalogs, including aliases and effort variants ([#11767](https://github.com/diegosouzapw/OmniRoute/issues/11767)) — thanks @pacocartones ([#11813](https://github.com/diegosouzapw/OmniRoute/pull/11813)) +- **fix(providers):** reclassify Cerebras as a one-time $5 signup credit (payment method required, 30-day validity), not a recurring no-card 1M tokens/day trial ([#11773](https://github.com/diegosouzapw/OmniRoute/issues/11773)) ([#12591](https://github.com/diegosouzapw/OmniRoute/pull/12591)) — thanks @HouMinXi +- fix(cli): stop prepublish from re-rebuilding the already-built ESM-only opencode-plugin dist (#11787) ([#11990](https://github.com/diegosouzapw/OmniRoute/pull/11990)) — thanks @hartmark +- **fix(db):** Fresh installs no longer log a non-fatal `no such table: compression_run_telemetry` warning when retention cleanup runs before the lazily-created telemetry table exists ([#11802](https://github.com/diegosouzapw/OmniRoute/pull/11802)) — thanks @RaviTharuma +- fix(sse): set X-OmniRoute-Selected-Connection-Id on successful combo dispatches so downstream consumers stop falling back to an empty connection id (#11810) ([#11986](https://github.com/diegosouzapw/OmniRoute/pull/11986)) — thanks @hartmark +- **fix(services):** embedded CLIProxyAPI lifecycle checks now use public `/healthz`, while model discovery uses the configured dedicated data-plane API key instead of the management password ([#11811](https://github.com/diegosouzapw/OmniRoute/pull/11811)) — thanks @RaviTharuma +- Fix persisted model steps returning an error in the playground route simulator, and warn when structural steps cannot be simulated. ([#11862](https://github.com/diegosouzapw/OmniRoute/pull/11862)) — thanks @pacocartones +- **fix(providers):** Antigravity's dynamic mitmAlias table no longer routes `gemini-3.7-flash-{high,medium,low}` to a literal tier-suffixed upstream id just because one connected account's own discovery listed it directly — those display ids always resolve through the safe `gemini-3.7-flash-tiered` static alias, so one account's Google-provisioned access no longer 404s every sibling account of the provider ([#11824](https://github.com/diegosouzapw/OmniRoute/issues/11824), [#11651](https://github.com/diegosouzapw/OmniRoute/issues/11651)) ([#11988](https://github.com/diegosouzapw/OmniRoute/pull/11988)) — thanks @hartmark +- **fix(dashboard):** Expose Profile loading, errors, page structure, and XP progress to assistive technologies ([#11838](https://github.com/diegosouzapw/OmniRoute/pull/11838)) — thanks @pacocartones +- **fix(socks):** `Agent.connectTimeout` now bounds both SOCKS handshake and TLS connect, and `family === null` no longer falls back to `fetch-socks` ([#11842](https://github.com/diegosouzapw/OmniRoute/pull/11842)) — thanks @maxmad64bis +- **perf(compression):** OOM mitigations for large payload hashing, memoization, and token estimation ([#11844](https://github.com/diegosouzapw/OmniRoute/pull/11844) — thanks @AndrianBalanescu) +- Excluded Next.js Node File Trace manifests (`*.nft.json`) from the published npm tarball. They are build-time metadata and are never read while serving, but had grown to 668.7 MB — 61% of the package — which pushed the upload past the registry limit and made `npm publish` fail with `413 Payload Too Large`. ([#11864](https://github.com/diegosouzapw/OmniRoute/pull/11864)) +- **fix(cli):** keep the global OmniRoute server `--base-url` separate from provider-node `--endpoint` values in add, update, and validate commands ([#11860](https://github.com/diegosouzapw/OmniRoute/pull/11860)) — thanks @pacocartones +- **fix(config):** Nous Research's `Hermes-4-405B` model now displays as "Hermes 4 405B (Nous Research)" in both the provider registry and the free-model catalog, instead of the mislabelled "Hermes 4 7B" ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#11993](https://github.com/diegosouzapw/OmniRoute/pull/11993)) +- **fix(provider/nous):** inject required user= tag into Nous Research inference requests to resolve upstream 400 "missing tags" error ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#12044](https://github.com/diegosouzapw/OmniRoute/pull/12044)) +- **fix(build):** `npm run build` now fails in one second with a named package and a copy-pasteable fix when npm silently drops an externalised optional native dependency, instead of dying four minutes in with `Module not found: Can't resolve 'better-sqlite3'` ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- **fix(install):** `postinstall` no longer throws `ReferenceError: isAndroid is not defined` — failing the whole `npm install` — when the `better-sqlite3` rebuild fallback times out; the manual-fix guidance is reachable again ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- Fixed the Alibaba free-tier allowlist test that went red on its own once the shipped catalog's `validUntil` (2026-08-27) passed, leaving every PR and `main` with a failing `Unit Tests (1/8)`. The test now builds its own packs with dates it controls, and covers the expired-pack fallback that production has actually been serving. ([#11867](https://github.com/diegosouzapw/OmniRoute/pull/11867)) +- Split the npm registry upload into its own GitHub-hosted job. npm refuses `--provenance` from a self-hosted runner (`422 ... Only "github-hosted" runners are supported`), which blocked the v3.8.50 publish; the heavy verification cannot move to a hosted runner, so it now hands the proven tarball over instead. ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(ui):** the shared `DataTable` loading state no longer reads its decorative ⏳ glyph out to assistive technology, and now carries the same `role="status"` / `aria-live="polite"` / `aria-busy="true"` semantics as `PageLoading` ([#11869](https://github.com/diegosouzapw/OmniRoute/pull/11869)) — thanks @pacocartones +- **fix(api):** Bound the `timeout` accepted by `POST /api/providers/[id]/login` to the same 15s–600s window the Adobe Firefly and Conol browser-login paths already enforce, so a request can no longer pin a headful browser and the single active-login slot with an arbitrarily large poll budget. ([#11871](https://github.com/diegosouzapw/OmniRoute/pull/11871)) — thanks @pacocartones +- **fix(api):** Stop the tunnel and MITM routes from returning raw child-process error text, which disclosed host paths, binary install locations and Tailscale `tskey-*` credentials in a body some of these routes serve to non-loopback callers, and return a real 400 instead of a framework 500 when a tunnel request body fails validation. ([#11872](https://github.com/diegosouzapw/OmniRoute/pull/11872)) — thanks @pacocartones +- **fix(open-sse):** stream TTFT/ITL are sampled from a monotonic clock (`performance.now`) instead of `Date.now`, so an NTP correction or wall-clock jump can no longer inflate a genuine TTFT or produce a negative one that the `ttft >= 0` guard silently drops — protecting the router's OTel/EWMA/`usage_history`/speed-ranking signals ([#11873](https://github.com/diegosouzapw/OmniRoute/pull/11873)) — thanks @pacocartones +- **fix(providers):** xAI `reasoning_effort: "xhigh"` now reaches grok-4.6+ instead of being silently clamped to `"high"` ([#11879](https://github.com/diegosouzapw/OmniRoute/pull/11879)) — thanks @NoxzRCW +- **fix(dashboard):** the providers page no longer crashes into the error boundary when a provider id collides with an `Object.prototype` member (`constructor`, `__proto__`); icon lookups are own-property guarded ([#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)) — thanks @NoxzRCW +- **fix(skills):** injected skill tools declared in shorthand (`{"content": "string"}`) now forward valid JSON Schema, unblocking providers that validate tool schemas strictly such as Zhipu GLM on the Console Go tier ([#11881](https://github.com/diegosouzapw/OmniRoute/pull/11881)) — thanks @NoxzRCW +- **fix(api):** `POST /api/playground/simulate-route` now surfaces `combo-ref` and `provider-wildcard` persisted combo steps with a specific warning (naming the referenced combo, or the unresolved `provider/model` wildcard) instead of folding them into a generic "unsupported step" count; a `provider-wildcard` step is also now included as an unresolved target so the operator can see it is in the route (ported from [#11882](https://github.com/diegosouzapw/OmniRoute/pull/11882) — thanks @NoxzRCW). ([#11930](https://github.com/diegosouzapw/OmniRoute/pull/11930)) +- **fix(translator):** the streaming OpenAI→Claude translator keeps upstream usage, including prompt-cache tokens, when it arrives on a trailing `choices: []` chunk (Fireworks and any upstream using `stream_options.include_usage`) ([#11883](https://github.com/diegosouzapw/OmniRoute/pull/11883)) — thanks @NoxzRCW +- **fix(cli):** `omniroute update --apply` now tells you explicitly whether a running server was detected and, if so, that you must run `omniroute restart` to apply the update — it never restarted anything and previously implied the update was already live once files were installed. The dashboard's npm-mode Update flow (`/api/system/version`) now tries OmniRoute's own PID-file-managed supervisor before falling back to pm2, and reports an honest "restart required" step instead of a silent pm2-only "skipped" that read like a completed update. The server-side latest-version lookup backing the dashboard's update banner also gained `--prefer-online`, closing the same stale-npm-cache class already fixed in the CLI's own copy for #4376 ([#11885](https://github.com/diegosouzapw/OmniRoute/issues/11885)). ([#12005](https://github.com/diegosouzapw/OmniRoute/pull/12005)) +- **fix(resilience):** clear persisted LKGP pins when a target suffers connection/provider exhaustion or is skipped before dispatch due to cooldown/exhaustion/unavailability, preventing subsequent requests from repeatedly prioritizing known-dead providers ([#11911](https://github.com/diegosouzapw/OmniRoute/issues/11911)). ([#12013](https://github.com/diegosouzapw/OmniRoute/pull/12013)) — thanks @HouMinXi +- **fix(sse):** the OpenAI→Claude stream translator now defers the terminal `message_delta`/`message_stop` emission until the real usage block has arrived (or a genuine end-of-stream flush forces it) instead of emitting it immediately on `finish_reason` — previously, when the trailing usage-only chunk (`{"choices":[],"usage":{...}}`) arrived *after* the `finish_reason` chunk (the normal order for Fireworks/vLLM/Together and other `stream_options.include_usage` upstreams), the client-visible `message_delta` still carried stale/zero usage even though `state.usage` was internally corrected too late to matter (ported from [#11915](https://github.com/diegosouzapw/OmniRoute/pull/11915) — thanks @HouMinXi). ([#11933](https://github.com/diegosouzapw/OmniRoute/pull/11933)) +- **fix(sse):** `stripStore()` now forces `store=false` for stateless OpenAI-compatible Responses-API targets (unless the connection has explicitly opted in via `providerSpecificData.openaiStoreEnabled`), instead of only handling the `openai`/`agentrouter` cases — a client-supplied `store` value previously passed through untouched to backends that don't actually persist responses server-side ([#11916](https://github.com/diegosouzapw/OmniRoute/pull/11916) — thanks @HouMinXi). +- **fix(models):** custom provider-node models (synced, custom, and alias-backed) now appear under their configured prefix in the unified catalog when the operator's model-id prefix mode is canonical, instead of being dropped whenever alias-inclusion was otherwise disabled ([#11918](https://github.com/diegosouzapw/OmniRoute/pull/11918) — thanks @HouMinXi). +- **fix(models):** the unified model catalog now suppresses stale static registry models (including effort-tier variants) for any provider whose active connection has an authoritative live synced catalog, not only providers already using exclusive-synced-listing — a connection with `providerUsesAuthoritativeLiveCatalog` previously kept serving both the live-synced models and the stale static rows side by side ([#11919](https://github.com/diegosouzapw/OmniRoute/pull/11919) — thanks @HouMinXi). +- **fix(ui):** `ProviderIcon`'s three remaining unguarded lookups (`PROVIDER_ICON_ALIASES`, `LOCAL_SVG_ALIASES`, `THEMED_SVGS`) now use `Object.hasOwn()` own-property checks — a provider id such as `constructor` or `__proto__` previously resolved through the prototype chain instead of falling through to the unknown-provider CDN fallback (`getLobeProviderIcon()` itself was already guarded by [#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)); `ProviderPageHeader`'s `color` field is also now optional, matching the rest of the component's defensive typing (ported from [#11920](https://github.com/diegosouzapw/OmniRoute/pull/11920) — thanks @HouMinXi). ([#11935](https://github.com/diegosouzapw/OmniRoute/pull/11935)) — thanks @NoxzRCW +- fix(ollama): preserve multi-byte UTF-8 content split across stream chunks in the Ollama NDJSON transform, which previously corrupted CJK/emoji into U+FFFD (#11921) — thanks @pacocartones +- **fix(providers):** OrcaRouter chat requests now target `/v1/chat/completions` instead of the bare `/v1` API root, fixing the upstream `404 Invalid URL (POST /v1)` ([#11923](https://github.com/diegosouzapw/OmniRoute/pull/11923)). — thanks @echel0nn +- **fix(plugins):** deliver the `onStreamComplete` event to disk-installed plugins. The event shipped in v3.8.50 (#9669) was emitted internally but had no plugin-facing wiring, so no plugin could ever subscribe: the manifest schema silently dropped `hooks.onStreamComplete`, and the loader/manager only knew the seven legacy hooks. `onStreamComplete` is now a declarable manifest hook, wired through the loader and registered by the manager like the other hooks, and its payload carries a `requestId` so consumers can correlate the stream-completion event with the originating request ([#11934](https://github.com/diegosouzapw/OmniRoute/pull/11934)) — thanks @amaleta / @pacocartones +- **fix(db):** the Qdrant embedding-model dropdown now lists local/self-hosted providers (Ollama, LM Studio, vLLM, etc.) — an active connection is treated as "configured" when the provider allows an optional API key, not only when it has a real key or OAuth, so a running local embedding provider is no longer hidden from the picker ([#11949](https://github.com/diegosouzapw/OmniRoute/issues/11949)) ([#11995](https://github.com/diegosouzapw/OmniRoute/pull/11995)) +- **fix(providers):** Vertex AI Anthropic partner-model discovery now calls the Model Garden `v1beta1` publisher list (`/v1beta1/publishers/anthropic/models`, global) and parses its `publisherModels` envelope, so Claude models auto-synced from Vertex populate the active live catalog and route at request time instead of returning `Model '' is not available in the active live catalog` ([#11991](https://github.com/diegosouzapw/OmniRoute/issues/11991)) — thanks @fabioluissilva ([#11998](https://github.com/diegosouzapw/OmniRoute/pull/11998)) +- **fix(cli):** support `--base-url` alongside `--endpoint` in `omniroute nodes add`, `update`, and `validate` subcommands to prevent global `--base-url` shadowing issues ([#11999](https://github.com/diegosouzapw/OmniRoute/issues/11999)). ([#12033](https://github.com/diegosouzapw/OmniRoute/pull/12033)) — thanks @HouMinXi +- **fix(providers):** `cloudflare-ai` no longer refuses image content parts for every Workers AI model ([#12002](https://github.com/diegosouzapw/OmniRoute/pull/12002)) — the plain-string `content` requirement behind #2539 is carried by the _model_ schema, not by the `/ai/v1/chat/completions` endpoint (measured: an all-text part array returns 200 on `@cf/mistralai/mistral-small-3.1-24b-instruct`, `@cf/meta/llama-4-scout-17b-16e-instruct` and `@cf/meta/llama-3.3-70b-instruct-fp8-fast`, and 400 on the text-only `@cf/qwen/qwen2.5-coder-32b-instruct`). `transformRequest()` flattened every array and threw on the first non-text part (#6390), so image input was refused for vision-capable Cloudflare models that accept it. All-text arrays are still flattened — the one shape every model accepts — while an array carrying a non-text part is passed through untouched, so the attachment is still never silently dropped. Regression guards: `tests/unit/cloudflare-ai-image-parts-6390.test.ts`. — thanks @davidlinfr +- **fix(resilience):** decouple the limiter-managed execution backstop from the queue-wait budget — new `requestQueue.executionMaxWaitMs` (env `RATE_LIMIT_EXECUTION_MAX_WAIT_MS`, default 600000 = 10 min) now feeds Bottleneck's post-dispatch `expiration`, while `requestQueue.maxWaitMs` keeps its documented queue-wait semantics. Previously the queue-wait budget doubled as the execution expiration, so legitimate long-running calls on non-incremental gateways (whole generation buffered before the first upstream byte, e.g. Console Go / Command Code tiers serving GLM models) were killed mid-flight at the queue budget with a false 504 `RATE_LIMIT_EXECUTION_TIMEOUT` — the local limiter undercut the provider-aware upstream fetch-start timeouts. The surfaced 504 message now names `requestQueue.executionMaxWaitMs`; the error keeps the #4165 guarantees (disclaims an upstream timeout, preserves the Bottleneck error as `cause`, branded code + trusted provenance, classified request-scoped so combo falls back). A real queue-wait bound (the `Promise.race` around `limiter.schedule()` sketched in #9533) remains future work. (#12025) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **Call logs:** keep the `error` field when an artifact exceeds the storage cap, instead of replacing it with the omission marker. The error is the only field that says *why* a request failed and is typically ~90 bytes next to the multi-hundred-KB bodies that trip the cap, so dropping it left a size-limited row undiagnosable — a provider outage, a local timeout and an upstream 400 all rendered identically. It is now preserved at every fallback stage, truncated to 4KB if it is itself large ([#12026](https://github.com/diegosouzapw/OmniRoute/issues/12026)). ([#12095](https://github.com/diegosouzapw/OmniRoute/pull/12095)) — thanks @ntdat812 +- **fix(diagnostics):** preserve the error field (truncated to 4KB with a `[truncated: …]` suffix) in every call-log artifact size-limit fallback stage. Previously the minimal fallback replaced the error with `[omitted: call log artifact size limit exceeded]`, so an oversized artifact row showed nothing about WHY the request failed — e.g. 91 of 847 opencode-go 504 rows on one production instance were undiagnosable from the dashboard. Oversized request/response bodies are still omitted exactly as before; the error cap is independent of the payload sizes that tripped the fallback. (#12026) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **fix(sse):** OpenAI Responses clients that declare the native `web_search` tool now receive a spec-shaped `web_search_call` output item with `action.sources` alongside the preserved function-call round-trip, so search results executed through OmniRoute's own search backend are consumable by standard Responses clients (Codex, pi-web-access, …). ([#12031](https://github.com/diegosouzapw/OmniRoute/pull/12031)) — thanks @watchingdogs +- **fix(cli):** use in-thread alias resolver hooks on modern runtimes to avoid deprecation noise and improve Node.js forward compatibility ([#12073](https://github.com/diegosouzapw/OmniRoute/issues/12073)). ([#12083](https://github.com/diegosouzapw/OmniRoute/pull/12083)) — thanks @xiaoyaner0201 +- **fix(combo):** an operator-set **Agent Features → Context length** on a combo is now honored at request time. The value was persisted and advertised through `/v1/models`, but `resolveComboContextLimit()` never consulted it — so a multi-target combo whose members carry no per-model window fell through to the provider's generic `defaultContextLength` (openrouter 128000, command-code 200000) and rejected large requests with `Input exceeds context window … limit 128000` despite the combo being explicitly sized much larger. An identical single-target combo worked, because it collapses to its concrete target before the guard runs. Invalid values (0/negative/NaN/Infinity) are ignored, so the existing target → combo-min → fallback order is unchanged. ([#12090](https://github.com/diegosouzapw/OmniRoute/pull/12090)) — thanks @adivekar-utexas +- **fix(sse):** passthrough streams now estimate usage on finish when upstream closes without usage even with `stream_options.include_usage` — avoids `0 tokens / 0%` for providers that stay silent (and correctly handles trailing empty-choices usage) ([#12151](https://github.com/diegosouzapw/OmniRoute/pull/12151)) — thanks @maxmad64bis +- **fix(combos):** clearing an agent feature in the combos editor now persists — unchecking context cache protection, or emptying the system message or tool filter, sends an explicit `null` instead of dropping the field from the `PUT` body, which the update merge read as "leave unchanged" ([#12177](https://github.com/diegosouzapw/OmniRoute/pull/12177)) — thanks @foreveryh / @ntdatt812 +- **fix(memory):** self-hosted embedding endpoints now vectorize — the vector width is measured from the first embedding that comes back instead of being read from a registry that cannot describe them, so `vec_memories` is created and memories stop piling up unvectorized behind a green health check ([#12180](https://github.com/diegosouzapw/OmniRoute/pull/12180)) — thanks @kanade-hoshino / @ntdatt812 +- **fix(translator):** the leading `system` message now reaches Responses-API upstreams when its `content` is a content-part array — it was read as `typeof content === "string" ? content : ""`, so a prompt-caching client (Anthropic `cache_control`, the shape LiteLLM and the Anthropic SDK emit) had its entire system prompt replaced by an empty `instructions`. The request was still accepted with a normal `prompt_tokens` count, so the model answered with no instructions and nothing in the response said they were missing. Mid-conversation system turns already handled the array shape ([#7056](https://github.com/diegosouzapw/OmniRoute/pull/7056)); only the first one did not ([#12206](https://github.com/diegosouzapw/OmniRoute/issues/12206)). Regression guard: `tests/unit/translator-openai-responses-system-content-parts.test.ts`. ([#12207](https://github.com/diegosouzapw/OmniRoute/pull/12207)) — thanks @Gorillaz322 +- **fix(free-tier):** `/api/free-tier/summary` no longer computes its totals from a Radar feed built before the catalog the running release ships. When the cached feed is older — or carries no build date at all — the route answers from the shipped catalog, resolved through the operator's local model state so disabled and tombstoned models stay out of the numbers ([#12215](https://github.com/diegosouzapw/OmniRoute/pull/12215)). — thanks @maxmad64bis +- **fix(oauth):** Keep a Claude personal workspace and a Team organization as separate connections — they share the same email and `accountUUID`, so the email-only OAuth dedup let the second login overwrite the first account's tokens; `organizationUUID` now disambiguates them, the way `workspaceId` does for Codex ([#12222](https://github.com/diegosouzapw/OmniRoute/pull/12222)) — thanks @davidebaraldo +- **fix(resilience):** a 402 on a single paid model of a passthrough/gateway provider (e.g. `kilo-gateway`, `ollama-cloud`) no longer terminalizes the whole connection with a never-auto-recovered `credits_exhausted` status — only the paid model is locked out, so free models on the same key keep serving. 402 variant of [#3027](https://github.com/diegosouzapw/OmniRoute/issues/3027). Single-credential providers are unaffected — a 402 there is still treated as the key being genuinely out of credit ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239) / [#10616](https://github.com/diegosouzapw/OmniRoute/issues/10616)) ([#12242](https://github.com/diegosouzapw/OmniRoute/issues/12242)) — thanks @brick30llc-ctrl ([#12266](https://github.com/diegosouzapw/OmniRoute/pull/12266)) — thanks @geekyNads +- **fix(sse):** trust `finish_reason: "length"`/`"max_tokens"` over the reasoning-consumed-token ratio in response quality validation, so a reasoning model truncated below the old 90% threshold correctly fails and retries instead of returning empty content as a silent "success" ([#12262](https://github.com/diegosouzapw/OmniRoute/pull/12262)) — thanks @brick30llc-ctrl +- **fix(settings):** `PUT /api/settings/cache-config` now persists `alwaysPreserveClientCache` to the flat general settings the runtime cache-control policy actually reads; previously the value landed in the databaseSettings "cache" section and was silently ignored, so the endpoint had no effect on `cache_control` passthrough ([#12304](https://github.com/diegosouzapw/OmniRoute/pull/12304)) — thanks @davidebaraldo +- **fix(grok-cli):** treat omitted SuperGrokPro `creditUsagePercent` as 0% used so Provider Limits still renders a weekly bar (proto3 zero-elision) ([#12312](https://github.com/diegosouzapw/OmniRoute/pull/12312)) — thanks @HouMinXi +- **fix(combo):** Expose the two Auto-Combo scoring factors nobody could set — the combo validation schema and the dashboard weight sliders both declared 13 of the scorer's 15 factors, so `connectionDensity` (spreads load across a provider's connections) and `quality` were dropped on save and offered nowhere. The sliders also shipped their own default table that differed from the engine's on every non-zero factor and summed to 1.05, so the percentages shown next to them added up to 105%. Both lists now match `DEFAULT_WEIGHTS`, and a test keeps them there. Note that a combo whose stored `weights` omitted the two keys was effectively running with them at zero and the other thirteen renormalized upward; it now runs with the engine's intended distribution, so its routing does shift ([#12314](https://github.com/diegosouzapw/OmniRoute/pull/12314)) — thanks @maxmad64bis +- **fix(docs):** The free-tier reference no longer says its numbers come "confidence tagged per row" — no catalog entry carries a confidence tag and the API serves none, so every figure on that page is an estimate of the same, unstated quality. The page now states what an entry does vouch for: an independently documented hard stop (set by hand with the source in a comment, never defaulted to `true`) and a prompt-training disclosure, both with live counts the `check:docs-counts` gate keeps honest ([#12318](https://github.com/diegosouzapw/OmniRoute/pull/12318)) — thanks @maxmad64bis +- **fix(usage):** `adobe-firefly` and `firefly` have had a working usage fetcher since Adobe Firefly landed, but neither was ever added to the registration list, so the provider-plugin manifest, `genericQuotaFetcher` and the free-access quota cache all reported them as having no usage support — while `USAGE_SUPPORTED_PROVIDERS` said the opposite. Both are now declared, which also means their credit balance is fetched like any other declared provider's: `registerGenericQuotaFetchers` now registers a generic quota fetcher for them, and `resolveFreeAccessState` no longer returns early. A test holds the registration list to the dispatcher's switch in both directions, which is what the module's own docstring already asked for in prose ([#12321](https://github.com/diegosouzapw/OmniRoute/pull/12321)) — thanks @maxmad64bis +- **fix(quota):** drop the generic quota cache (agy / Antigravity / Claude OAuth) on an upstream 429 so reset-aware scoring does not keep a 60s stale snapshot, and force-refresh the next usage fetch so inner provider caches cannot recache the same window ([#12325](https://github.com/diegosouzapw/OmniRoute/pull/12325)) — thanks @HouMinXi +- **fix(combos):** deleting a combo now clears its persisted LKGP pins instead of leaving unreachable `key_value` rows behind ([#12326](https://github.com/diegosouzapw/OmniRoute/issues/12326)) ([#12425](https://github.com/diegosouzapw/OmniRoute/pull/12425)) — thanks @KrzysiekSko +- **fix(sse):** Keep ZWNJ (U+200C) and ZWJ (U+200D) in assistant text, reasoning and tool-call arguments — Persian/Kurdish half-space (`ارائه‌دهنده`), Arabic/Indic shaping and emoji sequences no longer lose them; the response de-obfuscation now removes joiners only between ASCII word characters, where the request side inserts them ([#12186](https://github.com/diegosouzapw/OmniRoute/issues/12186)) — thanks @rezjalibd ([#12359](https://github.com/diegosouzapw/OmniRoute/pull/12359)) — thanks @pacocartones +- **fix(resilience):** count resolved upstream 5xx results against the provider circuit breaker on the chat path — `CircuitBreaker.execute()` no longer reads a resolved `{ success: false, status: 5xx }` as a success that cancels the call-site failure, so a provider answering 503s now trips its breaker instead of staying `CLOSED` at `failureCount: 1`; single-model and combo dispatches are each accounted exactly once ([#12254](https://github.com/diegosouzapw/OmniRoute/issues/12254)) ([#12360](https://github.com/diegosouzapw/OmniRoute/pull/12360)) — thanks @pacocartones +- **fix(providers):** resolve the Codex quota auto-ping model from the live provider catalog and lifecycle registry instead of the retired `gpt-5.1-codex-mini`, and pause the ping with one actionable warning when no selectable Codex model exists rather than retrying a shut-down id every cooldown window ([#11905](https://github.com/diegosouzapw/OmniRoute/issues/11905)) ([#12361](https://github.com/diegosouzapw/OmniRoute/pull/12361)) — thanks @pacocartones +- **fix(api):** keep the `{created, data}` wrapper on combo-routed `/v1/images/generations` responses and default Codex image results to `b64_json` on both `/v1/images/generations` and `/v1/images/edits` so Codex CLI's built-in `image_gen` can decode them ([#12268](https://github.com/diegosouzapw/OmniRoute/issues/12268)) ([#12362](https://github.com/diegosouzapw/OmniRoute/pull/12362)) — thanks @pacocartones +- **fix(sse):** Name the shadowed custom provider node when a built-in provider id/alias (e.g. `openference` → `of`) reserves the prefix of an existing OpenAI/Anthropic-compatible node, so the runtime `No active credentials for provider: ` error explains that the prefix routed to the built-in and never reached the node's healthy connections, instead of contradicting the dashboard ([#11943](https://github.com/diegosouzapw/OmniRoute/issues/11943)) — thanks @morpheus9393 ([#12365](https://github.com/diegosouzapw/OmniRoute/pull/12365)) — thanks @pacocartones +- **fix(i18n):** wrap `ccOnboardingKeyPlaceholder` in ICU single quotes across all 43 locales so angle brackets render literally instead of being parsed as rich-text tags, which crashed the Claude Code onboarding block with `INVALID_MESSAGE: INVALID_TAG` ([#12302](https://github.com/diegosouzapw/OmniRoute/issues/12302)) ([#12369](https://github.com/diegosouzapw/OmniRoute/pull/12369)) — thanks @gonisulaimann +- **fix(models):** publish `effort_tiers` on Kimi K3's synced base-model entries (`kmca/k3`, `kmca/k3-256k`) so catalog-only clients (OpenCode, plain SDK pickers) can see and select the reasoning tiers (`low`/`high`/`max`) the synced metadata already carried — the `isSkippedEffortProvider` gate no longer suppresses tier visibility on those base entries, while synthetic `-` variant generation stays prevented and Codex/GLM base models remain excluded unchanged ([#12299](https://github.com/diegosouzapw/OmniRoute/issues/12299)) ([#12371](https://github.com/diegosouzapw/OmniRoute/pull/12371)) — thanks @gonisulaimann +- **fix(guardrails):** keep `auto`/`auto/*` virtual combos exempt from the Vision Bridge `fixedModel` credential guard so a combo target is passed through instead of silently falling back to global auto-selection ([#12237](https://github.com/diegosouzapw/OmniRoute/issues/12237)) ([#12373](https://github.com/diegosouzapw/OmniRoute/pull/12373)) — thanks @pacocartones +- **fix(combo):** capability-filter exhaustion caused by `max_tokens` above every target's known output limit now reports that reason (requested `max_tokens` vs the pool's highest known ceiling) instead of the unrelated "supports structured output" message ([#12229](https://github.com/diegosouzapw/OmniRoute/issues/12229)) — thanks @DW-MediaLab ([#12374](https://github.com/diegosouzapw/OmniRoute/pull/12374)) — thanks @pacocartones +- **fix(auth):** the `least-used` account strategy now prefers accounts without backoff before falling back to oldest `lastUsedAt`, the same tie-break `round-robin` already applies, so a failover no longer lands on a just-rate-limited account for a single request ([#12279](https://github.com/diegosouzapw/OmniRoute/issues/12279)) — thanks @tenshiak ([#12375](https://github.com/diegosouzapw/OmniRoute/pull/12375)) — thanks @pacocartones +- **fix(docker):** the `chatgpt-web-codex-browser` image now finds the Chrome binary under `chrome-linux64/` (Chrome for Testing layout in `playwright:v1.62.0-noble`) as well as the legacy `chrome-linux/`, so the container no longer crash-loops with `exec: --headless=new: not found` ([#12024](https://github.com/diegosouzapw/OmniRoute/issues/12024)) ([#12376](https://github.com/diegosouzapw/OmniRoute/pull/12376)) — thanks @pacocartones +- **fix(providers):** declare `groq/compound` and `allam-2-7b` as non-reasoning models in the curated Groq registry so `reasoning_effort` / `output_config.effort` / `thinking` from Claude Code are stripped instead of forwarded, which Groq rejected with HTTP 400 ([#12134](https://github.com/diegosouzapw/OmniRoute/issues/12134)) ([#12379](https://github.com/diegosouzapw/OmniRoute/pull/12379)) — thanks @pacocartones +- **fix(executors):** `OpencodeExecutor` no longer forces a direct connection when the connection has a proxy assigned in Proxy Management but no per-account proxies: the single-account fast path used to wrap the upstream dispatch in the direct-egress sentinel, discarding the ambient proxy context the chat handler had pinned from `proxy_assignments`, so API-key `opencode`/`opencode-go` connections egressed from the host IP (and hit geoblocks) despite the assignment. The direct pin is now applied only when no ambient proxy context exists ([#11894](https://github.com/diegosouzapw/OmniRoute/issues/11894) — thanks @hizzt) ([#12380](https://github.com/diegosouzapw/OmniRoute/pull/12380)) — thanks @pacocartones +- **fix(api):** `GET /v1/models` with `MODELS_CATALOG_PREFIX_MODE=canonical` (or `?prefix=canonical`) now lists providers whose registry alias is undefined or equal to their own id (Antigravity, Antigravity CLI and other self-aliased built-ins) — their single `provider/model` id was dropped by the alias/canonical duplicate guard in the static, synced, custom and alias-backed catalog loops ([#12058](https://github.com/diegosouzapw/OmniRoute/issues/12058)) — thanks @cheynetom ([#12381](https://github.com/diegosouzapw/OmniRoute/pull/12381)) — thanks @pacocartones +- **fix(translator):** Drop replayed `thinking` blocks that carry no signature (the shape produced from cross-provider `reasoning_content`) instead of stamping the default Claude signature on them, which Anthropic rejected with `400 Invalid signature in thinking block` on the next turn served by an Anthropic rung ([#12105](https://github.com/diegosouzapw/OmniRoute/issues/12105)) — thanks @atescivitci-cmd ([#12386](https://github.com/diegosouzapw/OmniRoute/pull/12386)) — thanks @pacocartones +- **fix(cli):** Resolve Bun's `--preload` polyfill path against the package root instead of `dist/`, so `omniroute` installed with `bun install -g` no longer crashes at startup with `error: preload not found …/dist/open-sse/utils/setupPolyfill.ts` ([#11980](https://github.com/diegosouzapw/OmniRoute/issues/11980)) — thanks @joglomedia ([#12387](https://github.com/diegosouzapw/OmniRoute/pull/12387)) — thanks @pacocartones +- **fix(providers):** `gemini-business` now publishes its model catalog — `/v1/models` and `/v1/providers/gemini-business/models` list the 12 enterprise Gemini ids the executor understands instead of returning an empty list (#12107) ([#12389](https://github.com/diegosouzapw/OmniRoute/pull/12389)) — thanks @pacocartones +- **fix(db):** install `busy_timeout` before the SQLite connection's first statement so a process opening the database while another one closes its WAL connection waits out the transient EXCLUSIVE lock instead of dying with `database is locked`, and recognise the drivers' real BUSY/PROTOCOL/IOERR errors as transient in the corruption probe so the same lock no longer renames the database away as corrupt; deflakes `cross-process contenders never both acquire the same connection` (#12394 — thanks @pacocartones) +- **fix(chat-admission):** derive the `chat_admission_busy` 503 `Retry-After` from observed heavyweight-lease occupancy — the larger of the exhausted `OMNIROUTE_CHAT_ADMISSION_QUEUE_MS` window and the time since capacity last turned over, capped at 60 s — instead of a fixed 1 s (structural) / 2 s (byte-stage) hint that invited Codex/agent fan-out clients to re-send ~1 MiB `/v1/responses` bodies every second into a gate held for the whole SSE lifetime; an idle gate keeps the historical floors ([#12135](https://github.com/diegosouzapw/OmniRoute/issues/12135)) (#12395 — thanks @pacocartones) +- **fix(api-manager):** the API key permissions modal no longer silently drops `allowedCombos` entries its Combo picker cannot render — routing-rule names such as `rt-*`, which the backend already honours — when "All" is clicked and the key is switched back to "Restrict"; those entries now survive the toggle, are listed read-only under the combo list so the count and the list agree, and are saved back verbatim instead of persisting `[]` (deny-all) (#12397 — thanks @pacocartones) +- **fix(catalog):** write the NUL separator of the catalog connection memo key, the provider serviceKind memo key, the Video Bridge promotion group key and a JSON-exactness test fixture as the `\u0000` escape instead of a raw byte — same runtime value, but the raw byte made git, GitHub and ripgrep treat those files as binary (hidden PR diffs, silently skipped searches); a guard test now keeps raw NUL bytes out of `src/`, `open-sse/` and `tests/` (#12403 — thanks @pacocartones) +- **fix(providers):** add `CLAUDE_CODE_CLIENT_VERSION` and `GITHUB_COPILOT_CLI_VERSION` env overrides so Anthropic/Copilot client-version gates can be unblocked without a rebuild ([#12417](https://github.com/diegosouzapw/OmniRoute/issues/12417)) ([#12632](https://github.com/diegosouzapw/OmniRoute/pull/12632)) — thanks @HouMinXi +- **fix(db):** back-fill `last_ping_at` and `last_pinged_reset_key` on `provider_connections` during schema reconciliation so divergent lineages that skipped `123_quota_auto_ping` still accept quota auto-ping writes ([#12470](https://github.com/diegosouzapw/OmniRoute/pull/12470) — thanks @KooshaPari) +- **fix(ci):** document MIT exceptions for `@eloqnt/{config,format-json,format-po}` (next-intl transitive; locked tarballs omit `license`) and keep the A2A lifecycle vitest off the real SQLite persistence seam ([#12581](https://github.com/diegosouzapw/OmniRoute/issues/12581)) ([#12595](https://github.com/diegosouzapw/OmniRoute/pull/12595)) +- OpenCode plugin `/v1/models` catalog fetch now waits 30s by default and attaches HTTP `statusCode` on 401/5xx so host fallback plugins can hop instead of seeing an untyped AbortError/UnknownError. ([#12607](https://github.com/diegosouzapw/OmniRoute/pull/12607)) — thanks @RaviTharuma +- **CI:** the OpenAPI security-tier gate now mirrors `isAlwaysProtectedPath()` in full — it also reads `ALWAYS_PROTECTED_API_PATTERNS`, so the pattern-gated credential routes (`/api/providers/{id}/{claude,codex}-auth/{export,apply-local}`, GHSA-5926-2w35-7h4q) no longer report as unannotated. (#12605) +- **fix(api):** GET /v1/models no longer waits forever on a hung coalesced catalog rebuild; cold-path waits are bounded (`CATALOG_BUILD_TIMEOUT_MS`, default 8s) and a last-good 200 is served when the rebuild times out ([#12627](https://github.com/diegosouzapw/OmniRoute/issues/12627)). ([#12628](https://github.com/diegosouzapw/OmniRoute/pull/12628)) — thanks @RaviTharuma +- **fix(cursor):** a non-array `tool_calls` on an assistant message no longer crashes the cursor request translator with a `TypeError`; both loops now require an array ([#12689](https://github.com/diegosouzapw/OmniRoute/issues/12689)) ([#12691](https://github.com/diegosouzapw/OmniRoute/pull/12691)) — thanks @soroush5 +- **fix(chat):** Chat Completions no longer return empty `content` after a server-owned memory or skills tool; the first provider send and account/model recovery now share one pipeline so a follow-up round-trip can fill the reply ([#12696](https://github.com/diegosouzapw/OmniRoute/issues/12696)) — thanks @HouMinXi ([#12867](https://github.com/diegosouzapw/OmniRoute/pull/12867)) +- **fix(dashboard):** batch-deleting provider keys no longer toasts failure after a successful delete when the confirm button's click event is forwarded as `onAfter` ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) — thanks @HouMinXi +- **fix(glm):** drop the extra 16th argument to `createSSETransformStreamWithLogger` that TypeScript rejected (TS2554) and that never reached the TransformStream ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) ([#12770](https://github.com/diegosouzapw/OmniRoute/pull/12770)) — thanks @HouMinXi +- **fix(dashboard):** pass `nodeMap` into Runtime `QuotaGroup` so a quota monitor in error/exhausted/alerting no longer throws `ReferenceError: nodeMap is not defined`. ([#12868](https://github.com/diegosouzapw/OmniRoute/pull/12868)) — thanks @HouMinXi +- Restricted API keys whose `allowedModels` lists a combo name no longer skip every combo member at pre-dispatch (`ALL_TARGETS_SKIPPED`). Inner-target filtering still applies when the allow-list is a provider prefix or `disableNonPublicModels` is on ([#12899](https://github.com/diegosouzapw/OmniRoute/pull/12899)). — thanks @HouMinXi +- **fix(combos):** Effort-suffixed combo members inherit the base model's `model_context_overrides` row so priority order is not inverted on large requests ([#12926](https://github.com/diegosouzapw/OmniRoute/pull/12926)) — thanks @HouMinXi +- **fix(catalog):** Picker-added `customModels` enter the dispatch-time live catalog so combo members and bare inference no longer 400 ([#12934](https://github.com/diegosouzapw/OmniRoute/pull/12934)) — thanks @HouMinXi +- **fix(docker):** re-derive the Docker build's worker-pool memory budget from the MEASURED ~4.5 GB per-process RSS (the issue owner's own VPS dmesg OOM-killer reproduction) instead of the stale 2560 MB/worker inference #11419 shipped, and lower `OMNIROUTE_BUILD_WORKERS` 3 → 2 so 1 parent + 1 page-data worker (2 processes × 4.5 GB = 9 GB) fits the 12.288 GB (75%) budget on a 16 GB GitHub Actions runner — the previous default (1 parent + 2 workers = 13.5 GB) still overcommitted the runner and kept "Publish to Docker Hub" failing with `cannot allocate memory` after #11419 merged (#7518). ([#11663](https://github.com/diegosouzapw/OmniRoute/pull/11663)) +- **fix(electron):** the packaged Windows desktop build now passes the #7592 cold-restart smoke end to end. Five defects were found and fixed on the way: (1) optional-pack staging failed on any machine whose `tar` is GNU tar — it reads the drive letter in an absolute `-f C:\...` archive path as a remote rsh host (`Cannot connect to C:`), so staging now passes a bare filename with `cwd` at the tarball directory and surfaces tar stderr on failure; (2) the Electron `files` allowlist was missing `lib/loginHeaderCapture.js`, so the packaged main process crashed at startup with `Cannot find module './lib/loginHeaderCapture'`; (3) electron-builder ≥26 injects an `!**/node_modules/**` ignore into every extraResources pattern list that no positive filter can override, which silently dropped the staged runtime `node_modules` (including the better-sqlite3 N-API prebuild) from `resources/app` — a fresh v3.8.50 desktop build would have shipped with no native SQLite driver and reproduced the #7592 sql.js fallback on every machine; an `afterPack` hook now restores the staged `node_modules` after packing; (4) the packaged-app smoke harness redirected `USERPROFILE` into its temp DATA_DIR, but Electron resolves the Roaming profile from `%USERPROFILE%\AppData\Roaming\` (USERPROFILE takes precedence over the APPDATA env var) and the path service throws instead of creating it, so `requestSingleInstanceLock()` returned false and the app exited(0) silently before `app.whenReady()` — the harness now pre-creates the derived tree, and `ensureSmokeEnvDirs` is exported and unit-tested; (5) the #7592 driver assertion parsed a `[DB] Driver: ...` line that the server's primary DB path never printed (only the unused `openDatabaseAsync()` did) — `getDbInstance()` now emits the same line on open so the guard can actually assert the native driver. Also: the smoke env-allowlist unit test hardcoded POSIX paths and could only pass on Linux/macOS; expectations are now host-agnostic, with new regression tests for the USERPROFILE-derived tree and `tarPack` under absolute Windows-style paths ([#11443](https://github.com/diegosouzapw/OmniRoute/pull/11443)) — thanks @jonlwheat2-gif +- fix(sse): stop the auto-combo candidates inspector from silently dropping model-locked/cooled-down rows (#9133) ([#11994](https://github.com/diegosouzapw/OmniRoute/pull/11994)) — thanks @hartmark +- **fix(providers):** Perplexity Web no longer turns upstream stream failures into successful assistant text; pre-content failures remain eligible for fallback, partial output ends with a structured sanitized error, and failed sessions are not persisted ([#12465](https://github.com/diegosouzapw/OmniRoute/pull/12465)) +- **fix(video-bridge):** preserve chronological video and transcript fusion, source-frame timestamps, transcript provenance, full contact-sheet intervals, and pre-abort callback isolation ([#11681](https://github.com/diegosouzapw/OmniRoute/pull/11681)) +- **Z.ai Web:** HTTP 200 streams carrying an upstream error now terminate with a structured failure instead of assistant text plus a normal stop, preserving partial output while allowing pre-content combo fallback. ([#12454](https://github.com/diegosouzapw/OmniRoute/pull/12454)) +- Absorb `Error [AbortError]: request_signal_aborted` and DOMException AbortError shapes in the process-level client-abort crash guard so routine client disconnects no longer kill the server (exit code 7). ([#12165](https://github.com/diegosouzapw/OmniRoute/pull/12165)) — thanks @alvinveroy +- **fix(sse):** Treat Adapta Web `type:error` SSE events as sanitized non-stream failures instead of empty HTTP 200 completions. ([#12459](https://github.com/diegosouzapw/OmniRoute/pull/12459)) +- **fix(autoCombo):** layer 3 (`models_dev_tier`) no longer scores retired models as premium, merge cross-provider capability rows deterministically, and inherit tier scores through variant ids ([#11508](https://github.com/diegosouzapw/OmniRoute/issues/11508)). `getModelsDevTierFitness` now vetoes ids present in `config/quality/model-lifecycle.json` with status `retired` before any other signal; `loadModelCapabilities()` aggregates per `model_id` (any-non-null-true for booleans, max for `limit_context`) instead of last-write-wins over SQLite's undefined row order; a capabilities miss retries through `resolveScoresAs()`'s base id and reports `models_dev_tier:inherited`, matching layers 1–2 (#11492). Adds `getModelsDevTierFitnessWithSource()` and an `invalidateCapabilitiesCache()` test/ops hook. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) — thanks @Meet6338-X +- fix(sse): accept Claude `content:[]` + `stop_reason: max_tokens`/`tool_use` in `detectMalformedNonStream` (match `isEmptyContentResponse`) so Claude Code `/model` probes with `max_tokens: 1` no longer become a false 502 ([#11568](https://github.com/diegosouzapw/OmniRoute/pull/11568)) — thanks @Seramicx +- **fix(security):** sanitize `request.failed` diagnostics before publishing them to live dashboard listeners and replay history, while keeping status, model, provider, latency, and internal call-log diagnostics intact. ([#12469](https://github.com/diegosouzapw/OmniRoute/pull/12469)) +- **fix(pwa):** do not intercept dashboard navigations so Chrome can fall back from a stale HTTP/3 Alt-Svc advertisement (UDP :20128 is unpublished; F5 on a long-lived tab hung until a new tab opened a fresh TCP connection). ([#12767](https://github.com/diegosouzapw/OmniRoute/pull/12767)) — thanks @HouMinXi +- **fix(executors):** handle DuckDuckGo ERR_BN_LIMIT (418) without retrying — when the upstream returns `418 ERR_BN_LIMIT` (rate-limit/ban), the executor now returns the error immediately instead of burning another VQD acquisition that would only count against the IP limit. The retry logic for `418 ERR_CHALLENGE` (unsolved challenge) remains unchanged. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) ([#12110](https://github.com/diegosouzapw/OmniRoute/pull/12110)) — thanks @oyi77 / @Meet6338-X +- Fixed DuckDuckGo streaming responses losing JSON lines and UTF-8 characters split across network chunks. ([#11528](https://github.com/diegosouzapw/OmniRoute/pull/11528)) — thanks @pacocartones +- Harden generated Cloudflare, Vercel, and Deno relays by stripping hop-by-hop, framing, and proxy authentication headers before forwarding requests upstream. ([#11533](https://github.com/diegosouzapw/OmniRoute/pull/11533)) — thanks @pacocartones +- **feat(grok-cli):** Provider Limits shows grok-cli banked reset credits from `GetRemainingResets` (including a real zero; a failed RPC omits the row) and the existing View credits button now calls `ConsumerUiSvc/RedeemReset` for grok-cli. Live tokens use nested fields 10/20/30 (id + Timestamp), not compact 1/2/3. ([#12805](https://github.com/diegosouzapw/OmniRoute/pull/12805)) — thanks @HouMinXi +- **fix(memory):** Embedding Model Quick select, Embedding Source remote dropdown, and Rerank selector now list every configured provider with embedding/rerank support instead of only chat-catalog text matches plus OpenRouter live discovery; a generic OpenAI-compatible `/embeddings` + Cohere-compatible `/rerank` runtime fallback resolves any configured chat provider's embedding/rerank endpoint, so unlisted providers no longer fail with "Unknown embedding provider"; both memory selectors gained a free-text model override ([#11390](https://github.com/diegosouzapw/OmniRoute/pull/11390)) — thanks @rqzbeh +- Harden SQLite upgrades around the historical migration-074 version collision: missing discovery and inspector tables are replayed atomically, pre-existing databases (including setup-created skeletons) receive reusable content-addressed safety snapshots, and Node test/eval probes without `DATA_DIR` are isolated from the operator database. ([#12435](https://github.com/diegosouzapw/OmniRoute/pull/12435)) +- **fix(dashboard):** Moonshot/Kimi Open Platform voucher and cash leftover percentages follow the bucket balance, so an empty wallet no longer paints those rows as 100% while Available is 0% ([#12733](https://github.com/diegosouzapw/OmniRoute/pull/12733)) — thanks @HouMinXi +- **fix(combo):** return non-retryable HTTP 400 when all candidates for a pinned native Codex turn are unavailable due to model-scoped lockout, terminating the turn cleanly while preserving turn continuity and enabling standard Combo routing on subsequent turns ([#12240](https://github.com/diegosouzapw/OmniRoute/pull/12240)) — thanks @mdigitalbh81 +- Fixed auto-router strategies preserving the selected connection through ranking and dispatch when multiple connections share the same provider and model. ([#11530](https://github.com/diegosouzapw/OmniRoute/pull/11530)) — thanks @pacocartones +- Preserve Chat Completions JSON-object response formats when translating requests to the Responses API. ([#11529](https://github.com/diegosouzapw/OmniRoute/pull/11529)) — thanks @pacocartones +- Fix idempotency fingerprints so Responses API requests include `input` and other semantic generation fields using stable serialization, while excluding credentials and request metadata. ([#11532](https://github.com/diegosouzapw/OmniRoute/pull/11532)) — thanks @pacocartones +- **fix(api):** Generated API CLI commands now enforce required OpenAPI request bodies; Combo test commands forward the required `comboName` body, while API keys created by older writers after migration 149 preserve legacy allow-all Combo access without widening explicit empty allowlists — thanks @marcelokarval ([#12070](https://github.com/diegosouzapw/OmniRoute/pull/12070)) +- **fix(grok-web):** treat upstream streaming failures as failures instead of successful assistant text: error-only streams now fail readiness with HTTP 502, while failures after legitimate content preserve that partial output and terminate through the sanitized stream failure path without a normal `stop` completion. ([#12458](https://github.com/diegosouzapw/OmniRoute/pull/12458)) +- HuggingChat now turns HTTP 200 JSONL generation failures into a sanitized 502 before content, or a fixed public stream failure after partial output, so fallback and request persistence no longer record a false successful stop. ([#12456](https://github.com/diegosouzapw/OmniRoute/pull/12456)) +- **fix(providers):** keep 1min.ai HTTP 200 stream errors out of assistant content, preserve partial output, and expose sanitized terminal errors so pre-content failures can fall back. ([#12466](https://github.com/diegosouzapw/OmniRoute/pull/12466)) +- **fix(sessions):** preserve legacy Sessions data when the additive exclusive-session projection is unavailable, returning an empty projection and warning only once per contiguous outage instead of failing the endpoint; the Sessions badge intentionally reflects the merged legacy and exclusive row count introduced by [#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389) ([#11469](https://github.com/diegosouzapw/OmniRoute/pull/11469)) — thanks @KaspaPulse +- Render Nimble Search with the generic provider icon and serve Opper's proven logo locally. ([#12415](https://github.com/diegosouzapw/OmniRoute/pull/12415)) +- **fix(quality):** `config/quality/provider-assets-provenance.jsonl` was stale against the physical `public/providers/` tree (still listing 225 assets and 5 duplicate-content groups after several provider-retirement PRs removed 85 icon files), which would have made `check:provider-asset-provenance` fail on the next full run. Reconciled the manifest to the real 142-file snapshot (142 asset records, 1 duplicate-content group, 2 newly-registered files marked `unresolved` pending provenance research), repinned `auditedCommit`, and updated the regression test's hardcoded counts to match. ([#11876](https://github.com/diegosouzapw/OmniRoute/pull/11876)) +- fix(ui): wrap quota card footer action buttons to prevent clipping Refresh button on narrow cards — when extra action buttons such as `View credits` are present on Codex cards, allow the footer row to flex-wrap with `shrink-0` buttons so `Refresh now` remains visible and clickable across all responsive card widths ([#11464](https://github.com/diegosouzapw/OmniRoute/pull/11464)) — thanks @adevwithpurpose +- Keep Antigravity Gemini usable when the same connection's Claude weekly quota is empty; generic quota cache stays per-connection for every other provider. ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) — thanks @HouMinXi +- Fixed Tailscale tunnel setup to reject ports outside the valid 1-65535 range and safely install Windows MSI packages from paths containing apostrophes. ([#11534](https://github.com/diegosouzapw/OmniRoute/pull/11534)) — thanks @pacocartones +- Classify Cloudflared and Tailscale process-management routes as local-only and spawn-capable while preserving remote access to read-only tunnel status endpoints. ([#11531](https://github.com/diegosouzapw/OmniRoute/pull/11531)) — thanks @pacocartones +- Electron release workflow: the `publish-npm` job now grants `actions: read` to the reusable `npm-publish.yml` it calls (its `publish` job requests it), which is what made GitHub refuse the whole v3.8.50 run at startup and ship the release with zero desktop assets; a `workflow_dispatch` now builds the requested tag instead of the dispatching branch and can skip the npm leg (`publish_npm=false`) when only re-attaching assets ([#11974](https://github.com/diegosouzapw/OmniRoute/pull/11974)) +- **fix(dashboard):** The Combos page usage guide now reads its dismissal through `useSyncExternalStore` instead of correcting SSR state inside an effect, removing an extra commit of the page tree on every load (and the `react-hooks/set-state-in-effect` error it raised). ([#12671](https://github.com/diegosouzapw/OmniRoute/pull/12671)) +- Electron release: `electron/package-lock.json` regained the optional `electron-builder-squirrel-windows` subtree (13 entries) that `npm ci` had been refusing as out of sync, `electron-release.yml` gained a `build_ref` dispatch input and stops regenerating release notes on a re-attach dispatch, and the npm publish workflow attaches the SBOM to the GitHub Release on dispatch publishes too — so the v3.8.51 tag ships every desktop asset and the SBOM like v3.8.49 did ([#12022](https://github.com/diegosouzapw/OmniRoute/pull/12022)) +- Drained the reds every PR against `release/v3.8.51` was born with: documented the three service-worker build-id variables, registered the six retirement/tunnel tests with the mutation gate, approved `eslint-plugin-react-hooks` in the dependency allowlist, added the six `combo.sort.*` strings to `vi` and `pt-BR`, pointed the ChatGPT Web doc at the real migration-168 test, worded the g4f hint around the member key, and realigned four tests to the retired-provider catalog and the legacy-schema fixtures the retirement migrations touch. ([#11940](https://github.com/diegosouzapw/OmniRoute/pull/11940)) +- **fix(providers):** Claude, Grok, LMArena, Notion, and Perplexity web-cookie transports now use pooled `wreq-js` 3.2 instead of the native sidecar, with all nine supported bindings pinned and audited, and the applicable platform binding plus native-license evidence included in each release artifact ([#12429](https://github.com/diegosouzapw/OmniRoute/pull/12429), supersedes [#11753](https://github.com/diegosouzapw/OmniRoute/pull/11753)). +- **fix(providers):** Zed Hosted streaming failures now trigger fallback before content and end partial streams with a sanitized structured error instead of fake assistant text and a normal-success stop. ([#12455](https://github.com/diegosouzapw/OmniRoute/pull/12455)) +- **fix(test):** capture hook result after render (direct commit `b85d0abb0b`) +- **fix(sync):** repair the two regressions the v3.8.50 sync-back left on release/v3.8.51 (direct commit `5ade9e0851`) +- **revert(models):** drop the memo reset hook ported from #10055 (direct commit `282de841ae`) +- **fix(typecheck):** guard the optional connectionId before the reactive model sync (direct commit `f2825bad75`) +- **fix(quota-share):** release the winner's reserved in-flight slot (#11371) ([#11408](https://github.com/diegosouzapw/OmniRoute/pull/11408)) — thanks @oyi77 +- **fix(providers):** stop silently dropping reasoning effort on opencode families (#10788) ([#11409](https://github.com/diegosouzapw/OmniRoute/pull/11409)) — thanks @oyi77 +- **fix(combo):** enable genuine quota-aware routing for generic providers (antigravity, claude, etc.) ([#11411](https://github.com/diegosouzapw/OmniRoute/pull/11411)) — thanks @HouMinXi +- **fix(oauth):** allow expired token retry before deactivation and activate expired retry loop (#P0) ([#11414](https://github.com/diegosouzapw/OmniRoute/pull/11414)) — thanks @HouMinXi +- **perf(executors):** lazy-load the executor registry — defer class imports + construction to first use (#11220) ([#11421](https://github.com/diegosouzapw/OmniRoute/pull/11421)) — thanks @oyi77 +- **fix:** make release/v3.8.50 CI gates green (run 32786966560 root causes) ([#11450](https://github.com/diegosouzapw/OmniRoute/pull/11450)) — thanks @jonlwheat2-gif +- **fix(sse):** exact-domain cookie match and origin-equality URL assertions ([#11452](https://github.com/diegosouzapw/OmniRoute/pull/11452)) — thanks @hartmark +- **fix(providers):** send Responses API payload during OpenAI-compatible validation ([#11454](https://github.com/diegosouzapw/OmniRoute/pull/11454)) — thanks @tuandinh0801 +- **fix(command-code):** fallback to /alpha/generate for Go plan without Provider API access ([#11455](https://github.com/diegosouzapw/OmniRoute/pull/11455)) — thanks @Chewji9875 +- **fix(responses-continuation):** fail closed on a log-truncated stored input/output array ([#11473](https://github.com/diegosouzapw/OmniRoute/pull/11473)) — thanks @hartmark +- **fix(cli):** write jcode settings as [providers.omniroute] in config.toml ([#11484](https://github.com/diegosouzapw/OmniRoute/pull/11484)) — thanks @hongnoul +- **fix(logging):** raise chat-log array truncation cap to 1000 and unify duplicate implementations ([#11499](https://github.com/diegosouzapw/OmniRoute/pull/11499)) — thanks @hartmark +- **fix:** restore live websocket URL helpers ([#11509](https://github.com/diegosouzapw/OmniRoute/pull/11509)) — thanks @PauloFH +- **perf(core):** eliminate async waterfalls across home, cache, and db operations (#11396) ([#11512](https://github.com/diegosouzapw/OmniRoute/pull/11512)) — thanks @HouMinXi +- **fix(kiro):** add runtime.us-east-1.kiro.dev as first-attempt endpoint ([#11517](https://github.com/diegosouzapw/OmniRoute/pull/11517)) — thanks @rifqiawl +- **fix(providers):** remove duplicate 128-tool truncation in OpencodeExecutor (#11444) ([#11518](https://github.com/diegosouzapw/OmniRoute/pull/11518)) — thanks @HouMinXi +- **fix(providers):** poll onboardUser LRO before classifying BYOP ([#11519](https://github.com/diegosouzapw/OmniRoute/pull/11519)) — thanks @rifqiawl +- **fix(translator):** preserve exact tool name casing for Claude tool_use payloads (#11487) ([#11520](https://github.com/diegosouzapw/OmniRoute/pull/11520)) — thanks @HouMinXi +- **fix(kimi-web):** align health probe with executor domain ([#11521](https://github.com/diegosouzapw/OmniRoute/pull/11521)) — thanks @pacocartones +- **fix(cli):** preserve existing admin password during setup ([#11522](https://github.com/diegosouzapw/OmniRoute/pull/11522)) — thanks @pacocartones +- **fix(mitm):** keep Windows DNS elevation off the POSIX sudo path (#11430) ([#11542](https://github.com/diegosouzapw/OmniRoute/pull/11542)) — thanks @official-burak +- **fix(sse):** scale chat admission by ingest byte budget instead of a fixed request count ([#11548](https://github.com/diegosouzapw/OmniRoute/pull/11548)) — thanks @TheDemonTuan +- **fix:** accept Gemini Web cookie exports ([#11554](https://github.com/diegosouzapw/OmniRoute/pull/11554)) — thanks @rafacpti23 +- **fix(i18n):** complete Brazilian Portuguese translations ([#11558](https://github.com/diegosouzapw/OmniRoute/pull/11558)) — thanks @rafacpti23 +- **fix(sse):** explicit types for openai-responses pureHelpers — clears last failing core typecheck gate ([#11567](https://github.com/diegosouzapw/OmniRoute/pull/11567)) — thanks @jonlwheat2-gif +- **fix(opencode):** hydrate Proxy Pool references for no-auth connections ([#11584](https://github.com/diegosouzapw/OmniRoute/pull/11584)) — thanks @AStupidBear +- **fix(ci):** resolve inherited release/v3.8.51 base-red CI failures ([#11608](https://github.com/diegosouzapw/OmniRoute/pull/11608)) — thanks @jonlwheat2-gif +- **fix(db):** fallback load STORAGE_ENCRYPTION_KEY from env files ([#11614](https://github.com/diegosouzapw/OmniRoute/pull/11614)) — thanks @benzntech +- **fix(providers):** mark Brave Search as no longer having a free tier ([#11621](https://github.com/diegosouzapw/OmniRoute/pull/11621)) — thanks @turbolego +- **fix:** pass tool result content through as string in OpenAI/Claude -> Gemini translation ([#11624](https://github.com/diegosouzapw/OmniRoute/pull/11624)) — thanks @Hsia97 +- **fix(sse):** scope credential health to exact key ([#11628](https://github.com/diegosouzapw/OmniRoute/pull/11628)) — thanks @KaspaPulse +- **fix(install):** guard prepare script against missing husky ([#11634](https://github.com/diegosouzapw/OmniRoute/pull/11634)) — thanks @Prajeeth-12 +- **fix(security):** redact NVIDIA_API_KEY at the log sink in the ad-hoc diag script ([#11639](https://github.com/diegosouzapw/OmniRoute/pull/11639)) +- **fix(types):** clear the two type regressions red on release/v3.8.51 ([#11640](https://github.com/diegosouzapw/OmniRoute/pull/11640)) — thanks @jonlwheat2-gif +- **fix(build):** resolve workspace: protocol dependencies to concrete semver versions during prepublish (#11540) ([#11641](https://github.com/diegosouzapw/OmniRoute/pull/11641)) — thanks @HouMinXi +- **fix(search):** properly resolve configured search connection in /v1/responses pipeline (#11524) ([#11642](https://github.com/diegosouzapw/OmniRoute/pull/11642)) — thanks @HouMinXi +- **fix(authz):** trust cf-connecting-ip for via-proxy detection when peer is Cloudflare edge (#11514) ([#11643](https://github.com/diegosouzapw/OmniRoute/pull/11643)) — thanks @HouMinXi +- **fix(security):** compare the OIDC state cookie in constant time ([#11646](https://github.com/diegosouzapw/OmniRoute/pull/11646)) +- **fix(security):** namespace the dedup hash by the calling API key ([#11649](https://github.com/diegosouzapw/OmniRoute/pull/11649)) +- **fix(models):** gate the Codex-native catalog loop on prefix mode (#11632) ([#11662](https://github.com/diegosouzapw/OmniRoute/pull/11662)) — thanks @xiaoyaner0201 +- **fix(dashboard):** project Codex routing quota in account pools ([#11666](https://github.com/diegosouzapw/OmniRoute/pull/11666)) — thanks @KaspaPulse +- **fix(ddgw):** normaliza system no upstream da release v3.8.51 ([#11692](https://github.com/diegosouzapw/OmniRoute/pull/11692)) — thanks @RhianB14 +- **fix(memory):** honor category filter in GET /api/memory ([#11699](https://github.com/diegosouzapw/OmniRoute/pull/11699)) — thanks @Sabeekhann +- **fix(ci):** detect stale workspace lockfile entries ([#11714](https://github.com/diegosouzapw/OmniRoute/pull/11714)) — thanks @pacocartones +- **fix(ci):** stamp dist/BUILD_SHA before the npm publish provenance gate ([#11721](https://github.com/diegosouzapw/OmniRoute/pull/11721)) +- **fix(docker):** decouple the best-effort Bun image from the release manifest ([#11724](https://github.com/diegosouzapw/OmniRoute/pull/11724)) +- **perf(compression):** avoid cloning memo misses twice ([#11727](https://github.com/diegosouzapw/OmniRoute/pull/11727)) — thanks @RaviTharuma +- **fix(docker):** let the bun digest artifact be absent, not fatal ([#11740](https://github.com/diegosouzapw/OmniRoute/pull/11740)) +- **fix(shared):** block cloud-metadata hosts under default remote-image guard ([#11755](https://github.com/diegosouzapw/OmniRoute/pull/11755)) — thanks @santosraju99-hub +- **fix(api):** preserve caller-provided X-Correlation-Id on chat completions ([#11760](https://github.com/diegosouzapw/OmniRoute/pull/11760)) — thanks @aniruddhaadak80 +- **fix(resilience):** lock permanently retired models instead of short backoff (Gemini ban prevention) ([#11762](https://github.com/diegosouzapw/OmniRoute/pull/11762)) — thanks @turbolego +- **fix:** mark Vercel AI Gateway as passthroughModels ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **fix(resilience):** stop hammering permanently-moved endpoints and billing-suspended accounts ([#11774](https://github.com/diegosouzapw/OmniRoute/pull/11774)) — thanks @turbolego +- **fix(lease):** remove global static reservation and gate routing on live active lease occupancy ([#11775](https://github.com/diegosouzapw/OmniRoute/pull/11775)) — thanks @TheDemonTuan +- **fix(ci):** size the install-upgrade gate to a measured run, and log the pack cost ([#11776](https://github.com/diegosouzapw/OmniRoute/pull/11776)) +- **fix(pwa):** stop serving the stale shell after deploys ([#11779](https://github.com/diegosouzapw/OmniRoute/pull/11779)) — thanks @HouMinXi +- **fix(combo):** lock GitHub models rejected as "not supported" for future requests ([#11781](https://github.com/diegosouzapw/OmniRoute/pull/11781)) — thanks @turbolego +- **fix(cli):** probe both IPv4 and IPv6 loopback for server readiness (#11766) ([#11794](https://github.com/diegosouzapw/OmniRoute/pull/11794)) — thanks @vermasomesh835 +- **fix(sse):** preserve CLIProxyAPI wrappers per connection ([#11797](https://github.com/diegosouzapw/OmniRoute/pull/11797)) — thanks @RaviTharuma +- **fix(providers):** honor model auto-fetch opt-in on create ([#11798](https://github.com/diegosouzapw/OmniRoute/pull/11798)) — thanks @RaviTharuma +- **fix(dashboard):** make model auto-fetch order independent ([#11805](https://github.com/diegosouzapw/OmniRoute/pull/11805)) — thanks @RaviTharuma +- **fix(docker):** make GHA cache exports best effort ([#11806](https://github.com/diegosouzapw/OmniRoute/pull/11806)) — thanks @RaviTharuma +- **fix(kiro):** do not permanently ban on 'User is not authorized to make this call' ([#11809](https://github.com/diegosouzapw/OmniRoute/pull/11809)) — thanks @Deftera186 +- **fix(sse):** keep cache-write tokens in OpenAI-shaped usage ([#11814](https://github.com/diegosouzapw/OmniRoute/pull/11814)) — thanks @TheDemonTuan +- **fix(docs):** resolve relative markdown and wiki links across Fumadocs and GitHub wiki ([#11834](https://github.com/diegosouzapw/OmniRoute/pull/11834)) — thanks @AndrianBalanescu +- **fix(chat):** preserve unstripped model string for passthrough provider routing ([#11840](https://github.com/diegosouzapw/OmniRoute/pull/11840)) — thanks @raheemuddin786 +- **fix(mcp):** avoid async initialization inside sync esm wrappers (#11569) ([#11849](https://github.com/diegosouzapw/OmniRoute/pull/11849)) — thanks @HouMinXi +- **fix(sse):** preserve coherent markdown token boundaries across stream translation chunks (#11606) ([#11851](https://github.com/diegosouzapw/OmniRoute/pull/11851)) — thanks @HouMinXi +- **fix(ci):** run the install-upgrade gate on disk, not on the /tmp tmpfs ([#11855](https://github.com/diegosouzapw/OmniRoute/pull/11855)) +- **fix(skills):** expand shorthand property types in injected tool schemas ([#11857](https://github.com/diegosouzapw/OmniRoute/pull/11857)) — thanks @alvinveroy +- **fix(ci):** publish npm from a hosted runner so provenance is accepted ([#11877](https://github.com/diegosouzapw/OmniRoute/pull/11877)) +- **fix(lint):** pin react hooks plugin to 7.0.1 ([#11886](https://github.com/diegosouzapw/OmniRoute/pull/11886)) +- **fix(ci):** surface and clear release-green ESLint failure ([#11890](https://github.com/diegosouzapw/OmniRoute/pull/11890)) — thanks @MumuTW +- **fix(guardrails):** prevent duplicate prompt-injection-guard log output ([#11936](https://github.com/diegosouzapw/OmniRoute/pull/11936)) — thanks @patrykkopycinski +- **fix(auth):** downgrade expected transient states from warn to debug ([#11937](https://github.com/diegosouzapw/OmniRoute/pull/11937)) — thanks @patrykkopycinski +- **fix(codex):** fail over image generation for imported free plans ([#11948](https://github.com/diegosouzapw/OmniRoute/pull/11948)) — thanks @Bl0ck154 +- **fix(codex):** normalize non-stream responses ([#11951](https://github.com/diegosouzapw/OmniRoute/pull/11951)) — thanks @Bl0ck154 +- **fix(images):** forward Antigravity image size ([#11952](https://github.com/diegosouzapw/OmniRoute/pull/11952)) — thanks @Bl0ck154 +- **fix(db):** invalidate connection cache after upsert ([#11953](https://github.com/diegosouzapw/OmniRoute/pull/11953)) — thanks @Bl0ck154 +- **fix(codex):** restore imported account state ([#11954](https://github.com/diegosouzapw/OmniRoute/pull/11954)) — thanks @Bl0ck154 +- **fix(sse):** give extended-thinking targets the reasoning readiness budget ([#11959](https://github.com/diegosouzapw/OmniRoute/pull/11959)) — thanks @abhisheksharma2411 +- **fix(dashboard):** use opaque background and readable text color on cost chart tooltips ([#11960](https://github.com/diegosouzapw/OmniRoute/pull/11960)) — thanks @ZaimMarzuki +- **fix(antigravity):** send complete loadCodeAssist metadata (ideType/platform/pluginType as numeric enums) ([#11969](https://github.com/diegosouzapw/OmniRoute/pull/11969)) — thanks @rifqiawl +- **fix(core):** resolve DB init race condition and reasoning translation ([#12003](https://github.com/diegosouzapw/OmniRoute/pull/12003)) — thanks @raheemuddin786 +- **fix(admission):** exclude reclaimable page cache from the cgroup pressure ratio ([#12017](https://github.com/diegosouzapw/OmniRoute/pull/12017)) — thanks @HouMinXi +- **fix(build):** prune non-production trees in NFT trace excludes and tsconfig ([#12028](https://github.com/diegosouzapw/OmniRoute/pull/12028)) — thanks @Chewji9875 +- **fix(migrations):** add renamed migration compatibility for 056/073/077/101 ([#12036](https://github.com/diegosouzapw/OmniRoute/pull/12036)) — thanks @oyi77 +- **fix(sse):** default crash-guard logger to console.warn, not console ([#12042](https://github.com/diegosouzapw/OmniRoute/pull/12042)) — thanks @HouMinXi +- **fix(catalog):** derive vision/modalities for built-in auto combos from effective target pool ([#12046](https://github.com/diegosouzapw/OmniRoute/pull/12046)) — thanks @Prajeeth-12 +- **fix(release):** the packaged-app smoke verifies the database opened, not a driver line the primary path never prints (twin of #12032) ([#12047](https://github.com/diegosouzapw/OmniRoute/pull/12047)) +- **fix(barrel):** migrate src/app/ off the @/lib/localDb barrel import (#11795 Phase 2) ([#12051](https://github.com/diegosouzapw/OmniRoute/pull/12051)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate src/lib/ off the localDb barrel to direct db imports (#59) ([#12052](https://github.com/diegosouzapw/OmniRoute/pull/12052)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate open-sse, src/shared, src/sse, src/models, src/domain off the @/lib/localDb barrel import (#11795 Phase 4) ([#12053](https://github.com/diegosouzapw/OmniRoute/pull/12053)) — thanks @jonlwheat2-gif +- **fix(barrel):** delete the @/lib/localDb barrel — every consumer migrated (#11795 Phase 5) ([#12055](https://github.com/diegosouzapw/OmniRoute/pull/12055)) — thanks @jonlwheat2-gif +- **fix(radar):** restore D12 public boundary ([#12057](https://github.com/diegosouzapw/OmniRoute/pull/12057)) +- **fix(dev):** bound webpack and Tailwind scans ([#12075](https://github.com/diegosouzapw/OmniRoute/pull/12075)) — thanks @backryun +- **fix(dev):** isolate root layout settings reads ([#12076](https://github.com/diegosouzapw/OmniRoute/pull/12076)) — thanks @backryun +- **fix(dev):** reduce instrumentation executor fan-out (phase 3) ([#12078](https://github.com/diegosouzapw/OmniRoute/pull/12078)) — thanks @backryun +- **fix(dev):** make logging resources HMR-singleton ([#12079](https://github.com/diegosouzapw/OmniRoute/pull/12079)) — thanks @backryun +- **fix(sse):** keep unavailable forced connections scoped ([#12080](https://github.com/diegosouzapw/OmniRoute/pull/12080)) — thanks @keeltrace +- **fix(dev):** isolate batch dispatch from instrumentation ([#12081](https://github.com/diegosouzapw/OmniRoute/pull/12081)) — thanks @backryun +- **fix(ci):** reconcile release test contract drift ([#12082](https://github.com/diegosouzapw/OmniRoute/pull/12082)) — thanks @backryun +- **fix(ci):** accept CVE-2025-68121 in the prebuilt tls-client .so, auto-close base-red issues, guard Scorecard on the default branch ([#12085](https://github.com/diegosouzapw/OmniRoute/pull/12085)) +- **fix(db):** drop three consumer-less 1proxy exports — dead-code base-red on release/v3.8.51 after the barrel deletion (#12055) ([#12087](https://github.com/diegosouzapw/OmniRoute/pull/12087)) +- **fix(api):** clear the six API-route TypeScript regressions the new gate landed red on ([#12094](https://github.com/diegosouzapw/OmniRoute/pull/12094)) +- **fix(release):** never let the tag-push Create Release append auto notes to the curated body ([#12096](https://github.com/diegosouzapw/OmniRoute/pull/12096)) +- **fix(sse):** honor CLIProxyAPI environment API key ([#12099](https://github.com/diegosouzapw/OmniRoute/pull/12099)) — thanks @RaviTharuma +- **fix(oauth):** bind Google refresh to the client that issued the token ([#12106](https://github.com/diegosouzapw/OmniRoute/pull/12106)) — thanks @HouMinXi +- **fix(dashboard):** make RequestLoggerDetail loadable outside Next — CSS via globals.css + CJS/ESM interop (#11703 base-reds) ([#12114](https://github.com/diegosouzapw/OmniRoute/pull/12114)) +- **fix(leases):** project status lease row to lease columns so joined connection PII never escapes ([#12115](https://github.com/diegosouzapw/OmniRoute/pull/12115)) — thanks @geek007git +- **fix(plugins):** do not kill the plugin process when a fire-and-forget hook times out ([#12116](https://github.com/diegosouzapw/OmniRoute/pull/12116)) — thanks @geek007git +- **fix(guardrails):** restore injection-guard logging on middleware-only routes ([#12117](https://github.com/diegosouzapw/OmniRoute/pull/12117)) — thanks @geek007git +- **fix(plugins):** refresh stored manifest from disk on activate so new hook fields reach existing installs ([#12120](https://github.com/diegosouzapw/OmniRoute/pull/12120)) — thanks @geek007git +- **fix(codex):** preserve existing provider state when bulk-import upserts a matching connection ([#12122](https://github.com/diegosouzapw/OmniRoute/pull/12122)) — thanks @geek007git +- **fix(ci):** clear the two base-reds the 2026-08-30 merge batch left on release/v3.8.51 (round 3) ([#12123](https://github.com/diegosouzapw/OmniRoute/pull/12123)) +- **fix(sse):** strip type:'custom' from Claude tools on agentrouter dispatch ([#12126](https://github.com/diegosouzapw/OmniRoute/pull/12126)) — thanks @wahidsadik371-coder +- **fix(sse):** stop advertising video providers the dispatcher cannot run ([#12131](https://github.com/diegosouzapw/OmniRoute/pull/12131)) — thanks @quiterunner-commits +- **fix(search):** treat HTTP 432 and plan limit errors as transient cooldown ([#12139](https://github.com/diegosouzapw/OmniRoute/pull/12139)) — thanks @HouMinXi +- **fix(sse):** refuse an AI Horde queue that cannot fit the request budget ([#12143](https://github.com/diegosouzapw/OmniRoute/pull/12143)) — thanks @quiterunner-commits +- **fix(ci):** clear the base-reds the afternoon merge batch left on release/v3.8.51 (round 5: provider count 352, TS2554/TS2677) ([#12144](https://github.com/diegosouzapw/OmniRoute/pull/12144)) +- **fix(translator):** strip neutral tool_choice when tools absent in Responses-to-Chat (#12141) ([#12166](https://github.com/diegosouzapw/OmniRoute/pull/12166)) — thanks @HouMinXi +- **fix(guardrails):** pass providerId to getResolvedModelCapabilities in checkComboVision (#12112) ([#12169](https://github.com/diegosouzapw/OmniRoute/pull/12169)) — thanks @HouMinXi +- **fix(translator):** strip plaintext reasoning content for opaque responses backends (#12128) ([#12171](https://github.com/diegosouzapw/OmniRoute/pull/12171)) — thanks @HouMinXi +- **perf(stream):** compile hot-path regexes once, bound token caches, fix quadratic buffering ([#12179](https://github.com/diegosouzapw/OmniRoute/pull/12179)) — thanks @opensource-elearning +- **fix:** resolve compression worker file using runtime anchors instead of… ([#12183](https://github.com/diegosouzapw/OmniRoute/pull/12183)) — thanks @NightStalker-87 +- **fix:** prevent Claude Code session kills via liveness-aware readiness + auto model echo ([#12189](https://github.com/diegosouzapw/OmniRoute/pull/12189)) — thanks @opensource-elearning +- **fix(translator):** preserve falsy primitive values in Gemini and Antigravity function response results ([#12191](https://github.com/diegosouzapw/OmniRoute/pull/12191)) — thanks @Siva010 +- **fix(oauth):** mark empty Antigravity projectId as degraded and clear stale errors (#11284) ([#12205](https://github.com/diegosouzapw/OmniRoute/pull/12205)) — thanks @HouMinXi +- **fix(providers):** gate the Codex auto-ping usage read on the shared quota throttle ([#12209](https://github.com/diegosouzapw/OmniRoute/pull/12209)) — thanks @abhisheksharma2411 +- **fix(combos):** prioritize SQLite row id over inner JSON id and notify delete errors ([#12213](https://github.com/diegosouzapw/OmniRoute/pull/12213)) — thanks @HouMinXi +- **fix(providers):** mark featherless.ai as no longer having a free tier (`hasFree: false`) ([#12216](https://github.com/diegosouzapw/OmniRoute/pull/12216)) — thanks @turbolego +- **fix(deepseek-web):** stop Turbopack dev panic in the PoW worker path resolver ([#12221](https://github.com/diegosouzapw/OmniRoute/pull/12221)) — thanks @hartmark +- **fix(chat-admission):** clarify local 503 source ([#12223](https://github.com/diegosouzapw/OmniRoute/pull/12223)) — thanks @keeltrace +- **fix(dev):** silence webpack runtime module warnings ([#12228](https://github.com/diegosouzapw/OmniRoute/pull/12228)) — thanks @backryun +- **fix(db):** use module.require for CommonJS runtime driver loading ([#12230](https://github.com/diegosouzapw/OmniRoute/pull/12230)) — thanks @raheemuddin786 +- **fix(memory):** honest probe-driven FTS5 keyword status + memory_id rowid sync ([#12231](https://github.com/diegosouzapw/OmniRoute/pull/12231)) — thanks @raheemuddin786 +- **fix(system):** propagate abort signal to stream reader in HTTP version checks ([#12232](https://github.com/diegosouzapw/OmniRoute/pull/12232)) — thanks @raheemuddin786 +- **fix(resilience):** isolate local host execution errors from provider circuit breakers ([#12233](https://github.com/diegosouzapw/OmniRoute/pull/12233)) — thanks @raheemuddin786 +- **fix(sse):** sort injected tools deterministically for prompt caching ([#12234](https://github.com/diegosouzapw/OmniRoute/pull/12234)) — thanks @raheemuddin786 +- **fix(combo):** always clear the loop-safety timer, not just on the happy path (#11804) ([#12245](https://github.com/diegosouzapw/OmniRoute/pull/12245)) +- **fix(quality):** base-red pair — stryker tap registration + turn-pin suites aligned to the window gate ([#12255](https://github.com/diegosouzapw/OmniRoute/pull/12255)) — thanks @backryun +- **fix(dev):** qualify Turbopack runtime boundaries ([#12258](https://github.com/diegosouzapw/OmniRoute/pull/12258)) — thanks @backryun +- **fix(combo):** fall back to full pool when collapsed sole survivor is context-too-small ([#12278](https://github.com/diegosouzapw/OmniRoute/pull/12278)) — thanks @opensource-elearning +- **fix(combo):** bound the pre-dispatch unavailable skip so a stale label cannot dark a pool (#12168) ([#12285](https://github.com/diegosouzapw/OmniRoute/pull/12285)) +- **fix(security):** strip Qwen/Alibaba console-session cookies from provider API responses ([#12287](https://github.com/diegosouzapw/OmniRoute/pull/12287)) +- **fix(usage):** console-aware Token Plan guidance + subscription hint on bailian 401 ([#12288](https://github.com/diegosouzapw/OmniRoute/pull/12288)) +- **fix(resourcePressure):** log numeric detail on every rejection, recover faster ([#12293](https://github.com/diegosouzapw/OmniRoute/pull/12293)) — thanks @hartmark +- **fix(release):** drain the 2026-09-01 base-red window — passthrough usage regression + radarPage i18n keys ([#12327](https://github.com/diegosouzapw/OmniRoute/pull/12327)) +- **fix(ci):** openapi-security-tiers checker must honor routeGuard patterns + imported prefixes ([#12350](https://github.com/diegosouzapw/OmniRoute/pull/12350)) — thanks @ggiak +- **perf(ci):** cache node_modules in the npm-ci-retry composite (#8084 D3) ([#12408](https://github.com/diegosouzapw/OmniRoute/pull/12408)) +- **revert(ui):** point CTAs back at their real destinations ([#12410](https://github.com/diegosouzapw/OmniRoute/pull/12410)) +- **fix(release):** drain the 2026-09-02 base-red — rerank-providers import + api-typecheck baseline ratchet ([#12414](https://github.com/diegosouzapw/OmniRoute/pull/12414)) +- **fix(types):** make system prompt injection noImplicitAny-safe ([#12416](https://github.com/diegosouzapw/OmniRoute/pull/12416)) +- **fix(quality):** validate typecheck baseline schema ([#12419](https://github.com/diegosouzapw/OmniRoute/pull/12419)) +- **fix(memory):** resolve rerank provider node cache import ([#12421](https://github.com/diegosouzapw/OmniRoute/pull/12421)) +- **fix(ci):** repair release-root regressions — pack dedup, web-session syntax, uc-image ids ([#12423](https://github.com/diegosouzapw/OmniRoute/pull/12423)) — thanks @backryun +- **fix(providers):** separate MaxAI and UC credential contracts ([#12431](https://github.com/diegosouzapw/OmniRoute/pull/12431)) +- **fix(cli):** preserve Claude settings on config set ([#12432](https://github.com/diegosouzapw/OmniRoute/pull/12432)) — thanks @KrzysiekSko +- **fix(providers):** repair the maxai credential block truncated by merge auto-resolve ([#12433](https://github.com/diegosouzapw/OmniRoute/pull/12433)) +- **fix(quality):** record the 2026-09-02 merged growth in the file-size baseline ([#12434](https://github.com/diegosouzapw/OmniRoute/pull/12434)) +- **fix(uc):** route UC error strings through sanitizeErrorMessage; allowlist the retired codex id ([#12437](https://github.com/diegosouzapw/OmniRoute/pull/12437)) +- **fix(adapta):** redact streamed upstream errors ([#12438](https://github.com/diegosouzapw/OmniRoute/pull/12438)) +- **fix(sse):** remap non-contiguous upstream tool_calls index to a gap-free output_index ([#12445](https://github.com/diegosouzapw/OmniRoute/pull/12445)) — thanks @hartmark +- **fix(quality):** detect duplicate tool_calls entries in one response ([#12446](https://github.com/diegosouzapw/OmniRoute/pull/12446)) — thanks @hartmark +- **fix(conversations):** resolve turn content OmniRoute never sends back to the client ([#12447](https://github.com/diegosouzapw/OmniRoute/pull/12447)) — thanks @hartmark +- **fix(combo):** do not treat credits-exhausted 401 as auth skip ([#12449](https://github.com/diegosouzapw/OmniRoute/pull/12449)) — thanks @RaviTharuma +- **fix(auth):** do not park healthy quota accounts as expired ([#12452](https://github.com/diegosouzapw/OmniRoute/pull/12452)) — thanks @RaviTharuma +- **fix(responses-continuation):** fail closed on a collector-truncated, empty output array ([#12460](https://github.com/diegosouzapw/OmniRoute/pull/12460)) — thanks @hartmark +- **fix(logging):** raise the SSE payload collector's default cap ([#12461](https://github.com/diegosouzapw/OmniRoute/pull/12461)) — thanks @hartmark +- **fix(resilience):** surface Responses failed.error.message in 502s ([#12472](https://github.com/diegosouzapw/OmniRoute/pull/12472)) — thanks @RaviTharuma +- **fix(security):** unbiased maxai X-Random nonce + stricter URL/regex assertions ([#12502](https://github.com/diegosouzapw/OmniRoute/pull/12502)) +- **fix(video):** re-anchor transcript log-redaction so PII/credential maskers can't reopen the leak (#12150 P1 follow-up) ([#12503](https://github.com/diegosouzapw/OmniRoute/pull/12503)) +- **fix(monitoring):** serve cached credentialHealth off the request path ([#12533](https://github.com/diegosouzapw/OmniRoute/pull/12533)) — thanks @RaviTharuma +- **fix(quota):** keep Antigravity Gemini usable when Claude weekly is empty ([#12566](https://github.com/diegosouzapw/OmniRoute/pull/12566)) — thanks @HouMinXi +- **fix(authz):** hard-gate every credential export and CLI-config write (GHSA-5926-2w35-7h4q) ([#12600](https://github.com/diegosouzapw/OmniRoute/pull/12600)) +- **fix(docs):** restore the Next build — REMOVED_PROVIDERS.md had no frontmatter (base-red #12581) ([#12610](https://github.com/diegosouzapw/OmniRoute/pull/12610)) +- **fix(quality):** re-point the zcodeProtocol public-creds allowlist to line 313 ([#12615](https://github.com/diegosouzapw/OmniRoute/pull/12615)) +- **fix(security):** close 3 advisories — search baseUrl exfil, sk- in the error sanitizer, bifrost relay header leak ([#12620](https://github.com/diegosouzapw/OmniRoute/pull/12620)) +- **fix(providers):** sanitize boolean required and nested bare maps for Gemini (#12269) ([#12624](https://github.com/diegosouzapw/OmniRoute/pull/12624)) — thanks @HouMinXi +- **fix(resilience):** keep Overloaded STREAM_EARLY_EOF off the provider breaker ([#12626](https://github.com/diegosouzapw/OmniRoute/pull/12626)) — thanks @HouMinXi +- **fix(catalog):** re-audit free-tier quotas against official pages ([#12649](https://github.com/diegosouzapw/OmniRoute/pull/12649)) +- **fix(dashboard):** keep a request's pending-tracking id stable across combo target retries ([#12650](https://github.com/diegosouzapw/OmniRoute/pull/12650)) — thanks @hartmark +- **fix(tests):** retire dead model ids from the chat-pipeline integration suite (base-red #12581) ([#12670](https://github.com/diegosouzapw/OmniRoute/pull/12670)) +- **fix:** resolve SqliteError no such table compression_run_telemetry during cleanup ([#12682](https://github.com/diegosouzapw/OmniRoute/pull/12682)) — thanks @groovecityJO +- **fix(combo):** treat a pin-only step as implicit connection allowlist ([#12697](https://github.com/diegosouzapw/OmniRoute/pull/12697)) — thanks @HouMinXi +- **fix(ci):** ship .npmrc in published package so legacy-peer-deps applies to consumers (#11544) ([#12699](https://github.com/diegosouzapw/OmniRoute/pull/12699)) — thanks @KooshaPari +- **fix(docker):** pin 4 CLI tools to exact versions (#12576) ([#12703](https://github.com/diegosouzapw/OmniRoute/pull/12703)) — thanks @KooshaPari +- **fix(docker):** document and harden cli profile trust boundary (#12570) ([#12706](https://github.com/diegosouzapw/OmniRoute/pull/12706)) — thanks @KooshaPari +- **fix(sse):** redact video transcript in pre-guardrail rejected-request logs (#12150 P2 item 7) ([#12710](https://github.com/diegosouzapw/OmniRoute/pull/12710)) +- **fix(i18n):** quote placeholder in OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES description (#12505) ([#12769](https://github.com/diegosouzapw/OmniRoute/pull/12769)) — thanks @KooshaPari +- **fix(api/pricing):** surface validation error message as string, not raw object (#12494) ([#12771](https://github.com/diegosouzapw/OmniRoute/pull/12771)) — thanks @KooshaPari +- **fix(sse):** re-enable prompt compression for native Codex passthrough ([#12834](https://github.com/diegosouzapw/OmniRoute/pull/12834)) — thanks @thomasmaerz +- **fix(dashboard):** model health tests for a provider node set to the Responses API now call `/v1/responses` with a Responses-shaped body instead of `/v1/chat/completions` — those models were reported as `Provider returned HTTP 200 but no text content` even though the same model answered normally through `/v1/responses` ([#13070](https://github.com/diegosouzapw/OmniRoute/issues/13070)) ([#13087](https://github.com/diegosouzapw/OmniRoute/pull/13087)) — thanks @ntdatt812 +- fix(routing): stop a round-robin combo's "opencode" targets from collapsing onto the opencode-zen connection (#11912) ([#13283](https://github.com/diegosouzapw/OmniRoute/pull/13283)) +- fix(routing): stop the reactive-compaction debug log from lying when compression is globally disabled (#11977) ([#13276](https://github.com/diegosouzapw/OmniRoute/pull/13276)) +- fix(electron): relativize standalone-bundle symlink targets so Stage 8 manifest verification stops failing on Windows (#11979) ([#13251](https://github.com/diegosouzapw/OmniRoute/pull/13251)) +- fix(dashboard): surface a visible error when Compression Studio's combined preview run fails (#12061) ([#13288](https://github.com/diegosouzapw/OmniRoute/pull/13288)) +- fix(dashboard): make the compression "Effective pipeline" preview honor the active profile and warn when the master switch is off (#12063) ([#13287](https://github.com/diegosouzapw/OmniRoute/pull/13287)) +- fix(providers): scope TinyCMS Web signer's DOM shims to each call instead of leaking them for the process lifetime, and surface a clean HTTP status on a non-JSON interception-toggles error (#12072) ([#13254](https://github.com/diegosouzapw/OmniRoute/pull/13254)) +- fix(guardrails): stop Vision Bridge from re-selecting a model locked after a 404 (#12111) ([#13259](https://github.com/diegosouzapw/OmniRoute/pull/13259)) +- fix(sse): require Responses-shaped body before native OpenAI-compatible passthrough (#12129) ([#13278](https://github.com/diegosouzapw/OmniRoute/pull/13278)) +- fix(providers): minimax-m3 now collapses manual thinking.type:"enabled" to adaptive, preventing upstream 400 (2013) (#12132) ([#13249](https://github.com/diegosouzapw/OmniRoute/pull/13249)) +- fix(db): scope model visibility overrides by modality so hiding a Chat model no longer hides an identically-ID'd Image/Embeddings/etc. model (#12172) ([#13258](https://github.com/diegosouzapw/OmniRoute/pull/13258)) +- fix(db): scope local-provider apiKey dedup to matching base URL so LM Studio/Ollama-style connections support multiple accounts (#12173) ([#13250](https://github.com/diegosouzapw/OmniRoute/pull/13250)) +- fix(providers): refresh Trae's stale Referer/Origin and forward user timezone so imported connections stop failing with 401 (#12190) ([#13255](https://github.com/diegosouzapw/OmniRoute/pull/13255)) +- fix(providers): route opencode-go/gpt-5.6-luna to /responses instead of /chat/completions (#12196) ([#13275](https://github.com/diegosouzapw/OmniRoute/pull/13275)) +- fix(dashboard): allow deleting the last extra-upstream-header row even when invalid (#12251) ([#13252](https://github.com/diegosouzapw/OmniRoute/pull/13252)) +- **fix(i18n):** translate pre-existing `__MISSING__:` keys for `combo.sort`, `requestLogger.detail` expand/collapse, `common.profile`, and `settings.resilienceCredentialHealth*` across 39 locales ([#12272](https://github.com/diegosouzapw/OmniRoute/issues/12272)) ([#13034](https://github.com/diegosouzapw/OmniRoute/pull/13034)) — thanks @HouMinXi +- fix(cli): run the Node.js runtime compatibility guard before the heavy `tsx/esm` + Commander import chain so an unsupported runtime gets a clear message instead of a raw `Invalid regular expression flags` crash (#12296) ([#13253](https://github.com/diegosouzapw/OmniRoute/pull/13253)) +- fix(dashboard): refresh the providers list after deleting a compatible provider node (#12298) ([#13256](https://github.com/diegosouzapw/OmniRoute/pull/13256)) +- fix(usage): fail closed on API-key budget enforcement when a provider's `auto` routing alias has no pricing row, instead of silently counting it as $0 (#12341) ([#13257](https://github.com/diegosouzapw/OmniRoute/pull/13257)) +- **fix(providers):** include the submitted Agnes video model when polling by `video_id` ([#12356](https://github.com/diegosouzapw/OmniRoute/pull/12356)) — thanks @pacocartones +- **fix(routing):** custom OpenAI-compatible nodes now honor the saved Chat/Responses API type after edits instead of letting the node's original ID prefix override the live connection setting ([#11884](https://github.com/diegosouzapw/OmniRoute/issues/11884)). ([#12358](https://github.com/diegosouzapw/OmniRoute/pull/12358)) — thanks @pacocartones +- fix(sse): surface an error instead of a silent empty 200 when a Claude stream closes with zero bytes (#12398) ([#13285](https://github.com/diegosouzapw/OmniRoute/pull/13285)) +- fix(oauth): warn before the dead localhost:8080 redirect in antigravity/gemini `oauth start` (#12413) ([#13265](https://github.com/diegosouzapw/OmniRoute/pull/13265)) +- fix(providers): stop devin-cli spawn error from double-closing the SSE controller (#12517) ([#13261](https://github.com/diegosouzapw/OmniRoute/pull/13261)) +- **fix(rerank):** clamp Voyage `top_k` to the documents actually sent after empty-string filtering, and honor `return_documents: false` in the NVIDIA response adapter (#12523 — thanks @pacocartones) +- **fix(audio):** `/v1/audio/translations` now resolves combo names the way `/v1/audio/transcriptions` already does, so a combo that `GET /v1/models` advertises is fanned out to its targets instead of being rejected with `400 Invalid translation model: . Use format: provider/model`; literal `provider/model` ids and unknown bare names behave as before (#12536 — thanks @pacocartones) +- **fix(gemini):** strip the JSON-Schema-2020-12 `prefixItems` keyword from Gemini tool schemas at every nesting level, so Claude Code tool definitions no longer fail with `400 Unknown name "prefixItems"` on Gemini models (#12540 — thanks @pacocartones) +- **fix(api-manager):** Expose an accessible loading status while API keys are fetched instead of an empty accessibility tree (#12541 — thanks @pacocartones) +- **fix(video):** Clamp `estimateJpegFrameBytes` at zero for padding-only payloads and build the three encode-side frame data URIs from `JPEG_FRAME_DATA_URI_PREFIX` instead of a repeated literal (#12543 — thanks @pacocartones) +- **fix(devin):** accept Windows `DEVIN_AGENTIC_HOME` sandbox paths (`C:\...\.sandbox\...`) in the isolated-home check so the Devin Claude Bridge no longer fails closed on Windows ([#12405](https://github.com/diegosouzapw/OmniRoute/issues/12405)) (#12545 — thanks @pacocartones) +- **fix(db):** Add `conversation_turn_nodes` and orphaned `agentic_conversations` to the auto-cleanup cycle under the existing `retention.callLogs` window, so identity nodes whose call-log content has already been purged no longer accumulate without bound in `storage.sqlite` (#12548 — thanks @pacocartones) +- **fix(i18n):** Wrap the `~/.claude/profiles//settings.json` placeholder in ICU single quotes in the `OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES` feature-flag description across all 42 locales and the TypeScript default, so next-intl no longer fails with `INVALID_MESSAGE: UNCLOSED_TAG` and the Feature Flags card shows the description instead of the raw key (#12549 — thanks @pacocartones) +- **fix(orchestration):** `updateCloudAgentTask` now publishes the task's real `status` on `agent.task.updated` when an update only touches `result`, `activities` or `error`, instead of the fabricated `"updated"` state, and stays silent when no row matched the id (#12550 — thanks @pacocartones) +- **fix(i18n):** the home "Recent Requests" panel and the Provider Topology legend are now translated instead of rendering English copies on non-English dashboards; the legend reads its own `home.topologyLegend*` labels with consistent casing rather than borrowing the memory-settings "Recent" and analytics "Error" strings (#12551 — thanks @pacocartones). +- **docs(reference):** bring the `FEATURE_FLAGS.md` catalog back to 1:1 with `featureFlagDefinitions.ts` — 20 missing flags added, the two `*_BLOCK_THRESHOLD` env-only knobs moved out of the flag tables, category/total counts and the Live WS port corrected, guarded by a static test (#12552 — thanks @pacocartones) +- **fix(i18n):** backfill missing `usage.kiloPass*` strings in 39 locales and restore `featureFlagOmnirouteDisableThinkingLevelVariantsDescription` in `pt.json` ([#12561](https://github.com/diegosouzapw/OmniRoute/issues/12561)) ([#13027](https://github.com/diegosouzapw/OmniRoute/pull/13027)) — thanks @HouMinXi +- fix(docker): default docker-compose app ports (dashboard/API/live-WS) to loopback instead of `0.0.0.0`, closing the anonymous `/v1` LAN/WAN exposure gap left open by `REQUIRE_API_KEY=false` (#12568) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): close DNS-rebinding SSRF gap in webhook outbound-URL guard (#12569) ([#13243](https://github.com/diegosouzapw/OmniRoute/pull/13243)) +- fix(docker): require a per-session token on the VNC browser CDP bridge and isolate it on a dedicated Docker network (#12571) ([#13241](https://github.com/diegosouzapw/OmniRoute/pull/13241)) +- fix(open-sse): write Adobe Firefly session tokens and cookie jars with 0700/0600 permissions instead of the process umask (#12572) ([#13242](https://github.com/diegosouzapw/OmniRoute/pull/13242)) +- fix(api): remove hardcoded wildcard CORS in openai-to-gemini-sse.ts so the centralized fail-closed CORS gate is the sole source of `Access-Control-Allow-Origin` (#12573) ([#13239](https://github.com/diegosouzapw/OmniRoute/pull/13239)) +- fix(api): enforce API key policy (budget/rate-limit/schedule/endpoint scoping) on the ElevenLabs speech-to-text, text-to-speech and voices proxy routes (#12574) ([#13244](https://github.com/diegosouzapw/OmniRoute/pull/13244)) +- fix(sse): cap HuggingChat NDJSON body size and bound the read loop with the fetch timeout so a stalled or hostile upstream cannot buffer unbounded memory (#12577) ([#13282](https://github.com/diegosouzapw/OmniRoute/pull/13282)) +- fix(docker): scope the cliproxyapi/qdrant/bifrost sidecars to loopback by default and forward `CLIPROXYAPI_MANAGEMENT_KEY` into the cliproxyapi container so its management API is not left both unauthenticated and LAN-published (#12578) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): create DB export temp paths with `fs.mkdtempSync` instead of predictable timestamps (#12579) ([#13237](https://github.com/diegosouzapw/OmniRoute/pull/13237)) +- Cline 401 bodies that say "re-authenticate your Cline account" classify as a refreshable OAuth token, not a terminal expired key. The cooling panel no longer labels every cooldown as a 429; it shows the recorded last error instead. (#12594) ([#13060](https://github.com/diegosouzapw/OmniRoute/pull/13060)) — thanks @HouMinXi +- **fix(catalog):** degrade unknown combo targets instead of dropping LCD modalities, and persist OpenRouter `architecture.input_modalities` into the capability snapshot ([#12613](https://github.com/diegosouzapw/OmniRoute/issues/12613)) ([#13042](https://github.com/diegosouzapw/OmniRoute/pull/13042)) — thanks @HouMinXi +- fix(providers): send `x-api-key` instead of `Authorization: Bearer` for OpenCode Zen's `/v1/responses` endpoint (Muse Spark Contributor models), fixing a 401 on OmniRoute's auth header (#12633) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(sse): surface the actionable "Auggie CLI not found" message when the shell reports a missing `auggie` binary via exit code instead of a spawn error (#12645) ([#13263](https://github.com/diegosouzapw/OmniRoute/pull/13263)) +- **fix(config):** Persist deletions of built-in background-degradation entries — when a stored settings record exists its `degradationMap` is now authoritative instead of being merged under the defaults, so an entry the user removed in the dashboard no longer reappears on the next apply or restart ([#12424](https://github.com/diegosouzapw/OmniRoute/issues/12424)) ([#12647](https://github.com/diegosouzapw/OmniRoute/pull/12647)) — thanks @pacocartones +- **fix(gamification):** action-count badge milestones (First Token, Token Consumer, Token Machine, Token Whale, and the token-sharing tier) are now backed by a durable `xp_action_counts` counter incremented in `addXp()`, instead of a live `COUNT(*)` over `xp_audit_log`. The audit log is pruned by `retention.xpAuditLog` (default 30 days), so on a default install those "lifetime" milestones were really "actions in the last 30 days" and unlocked badges could stop unlocking once old rows aged out. `getActionCount()` and `checkActionCountBadges()` now read the same durable source, and a migration backfills existing totals from the surviving audit rows ([#12546](https://github.com/diegosouzapw/OmniRoute/issues/12546)) ([#12651](https://github.com/diegosouzapw/OmniRoute/pull/12651)) — thanks @pacocartones +- **fix(images):** `/v1/images/edits` now iterates a combo's targets the same way `/v1/images/generations` does (#9239) instead of flattening a bare combo to its first target. A combo whose first target is not edit-capable — or lacks credentials — now falls through to a later edit-capable target rather than hard-erroring, and missing credentials are skipped (not a hard `401`) to match the generations path. The per-target skip/terminal classification is extracted into a shared `runImageComboTargets` loop, so generations behavior is unchanged ([#12547](https://github.com/diegosouzapw/OmniRoute/issues/12547)). ([#12653](https://github.com/diegosouzapw/OmniRoute/pull/12653)) — thanks @pacocartones +- fix(sse): add first-byte watchdog to the TLS-fingerprint transport so a stalled wreq body falls back instead of hanging for minutes (#12656) ([#13272](https://github.com/diegosouzapw/OmniRoute/pull/13272)) +- fix(sse): exempt tiny-budget reasoning probes from combo quality failure and surface persisted-cooldown skips in ALL_TARGETS_SKIPPED diagnostics (#12659) ([#13269](https://github.com/diegosouzapw/OmniRoute/pull/13269)) +- fix(models): declare the real ~1M contextLength for OpenCode Zen's Muse Spark 1.2 models instead of falling back to the 200000 provider default (#12681) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(oauth): align codebuddy-cn OAuth User-Agent with the chat/usage CLI version to avoid WAF false positives (#12702) ([#13264](https://github.com/diegosouzapw/OmniRoute/pull/13264)) +- fix(dashboard): surface an authentication-required banner instead of silently blanking database settings for a guest session (#12709) ([#13270](https://github.com/diegosouzapw/OmniRoute/pull/13270)) +- **fix(resilience):** stop unbounded queue that hangs 6min until Aborted — gate, provider slot, and Bottleneck queue now share a per-connection `maxWaitMs` budget; `fail-closed` on exhaust (503); execution backstop `executionMaxWaitMs` overridable per connection with upstream clamp ([#12715](https://github.com/diegosouzapw/OmniRoute/pull/12715)) — thanks @maxmad64bis (with thanks to @Tushar49 for surfacing the slow-provider need in #12635) +- **fix(auto-combo):** every mode pack now carries `quality` and `reliability` weights (`quality` 0.02, 0.03 in `quality-first`; `reliability` 0.03, 0.04 in `reliability-first`), so selecting a pack no longer silences either signal; `DEFAULT_WEIGHTS` is unchanged ([#12731](https://github.com/diegosouzapw/OmniRoute/pull/12731)) — thanks @maxmad64bis +- **fix(sse):** the provider execution pipeline keeps the upstream error `code`/`type` again — both were lost when the non-streaming leg was extracted, so a config-class `422` (Antigravity missing project) degraded into a generic account cooldown instead of reaching the gate that recognises it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(db):** a legacy `call_logs` table no longer aborts startup — the provider-stats index is created after the column healing runs, not before it, so an install predating `request_type` opens instead of failing with `no such column` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(catalog):** a malformed row in the operator-writable custom-models blob no longer kills every `auto/*` pool ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(tests):** realign the guards that the same day's merges left asserting the old behavior — the provider-count total after GreenPT and EURouter, the GLM stream buffer slot that `#12925` turned into a declared parameter, and the injection scan bound that `#13104` reshaped from a head-only window into head-plus-tail ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- fix(cache): fold tool_choice/tools/response_format into the semantic cache signature so a cached tool_calls response can no longer be replayed for a request whose tool policy forbids it (#12734) ([#13267](https://github.com/diegosouzapw/OmniRoute/pull/13267)) +- **fix(models):** stop treating provider-supplied `isFree:true`/`:free`/`0/0` flags as trusted unless the provider has a documented free tier — only the shipped free-tier catalog decides otherwise; fetched `isFree:true` trusted only for a free-tier provider, custom `isFree:true` via a trusted path ([#12744](https://github.com/diegosouzapw/OmniRoute/pull/12744)) — thanks @maxmad64bis +- fix(memory): authenticate the internal /v1/rerank loopback call so memory reranking no longer silently degrades to unranked order when REQUIRE_API_KEY=true (#12745) ([#13268](https://github.com/diegosouzapw/OmniRoute/pull/13268)) +- fix(sse): parse Ollama Cloud's current usage markup (`$X of $Y used` aria-label, nested width style) (#12749) ([#13260](https://github.com/diegosouzapw/OmniRoute/pull/13260)) +- fix(cli): setup-opencode no longer sends an active context's management token to `/v1/models` when `--api-key`/`OMNIROUTE_API_KEY` is supplied — an explicit flag or the env var now always outranks the context's token, and the flag itself is no longer swallowed by the parent program's global `--api-key` option (#12783) ([#13246](https://github.com/diegosouzapw/OmniRoute/pull/13246)) +- fix(sse): register Arcee AI in the executor provider registry so requests reach api.arcee.ai instead of silently falling back to OpenAI (#12784) ([#13277](https://github.com/diegosouzapw/OmniRoute/pull/13277)) +- **fix(quota):** share one upstream quota read when identical saturation checks arrive at the same time, so traffic bursts don't multiply provider API calls ([#12787](https://github.com/diegosouzapw/OmniRoute/pull/12787)) — thanks @maxmad64bis +- **fix(db):** ignore expired or invalid rate-limit cooldown writes so a stale timestamp can't lock a connection that should be usable — clearing still works as before ([#12788](https://github.com/diegosouzapw/OmniRoute/pull/12788)) — thanks @maxmad64bis +- **fix(providers):** single source for provider order with `xao` ranking alongside `xai-oauth` ([#12790](https://github.com/diegosouzapw/OmniRoute/pull/12790)) — thanks @maxmad64bis +- **fix(routing):** off-table models check the free-model catalog before inheriting premium prices, latency bootstraps from the observed pool median, async tiers read live database pricing with a 90-day freshness gate, and the tier cache invalidates on every pricing write ([#12792](https://github.com/diegosouzapw/OmniRoute/pull/12792)) — thanks @maxmad64bis +- **fix(auto-combo):** snapshot scoring uses the observed per-connection account tier and per-model quality instead of neutral constants, and circuit-open providers rank lower at build time ([#12794](https://github.com/diegosouzapw/OmniRoute/pull/12794)) — thanks @maxmad64bis +- **fix(pool):** empty `auto/*` pools now say why they are empty, and the models listing uses the same paid check as routing ([#12795](https://github.com/diegosouzapw/OmniRoute/pull/12795)) — thanks @maxmad64bis +- fix(routing): recognize CLIProxyAPI's 'unknown provider for model' 400 as fallback-worthy (#12800) ([#13284](https://github.com/diegosouzapw/OmniRoute/pull/13284)) +- **fix(sse):** translate-mode streams now emit the estimated token counts as a trailing usage-only chunk before `[DONE]` when the upstream stays silent, so metered chat clients see totals instead of nothing ([#12828](https://github.com/diegosouzapw/OmniRoute/pull/12828)) — thanks @maxmad64bis +- **fix(db):** provider stats stay truthful on empty and legacy databases: fallback counts default to `0` instead of `null`, latency averages read `null` (not `0`) when no durations were recorded, failures logged before the error-type column existed group under `pre_migration` instead of `unclassified`, and per-provider queries use two new composite indexes ([#12832](https://github.com/diegosouzapw/OmniRoute/pull/12832)) — thanks @maxmad64bis +- fix(nvidia): fail open when a synced model catalog goes stale instead of gating forever (#12849) ([#13248](https://github.com/diegosouzapw/OmniRoute/pull/13248)) +- **fix(db):** WAL maintenance lives in its own module: a `TRUNCATE` checkpoint that hits a busy database now warns with its streak and retries once via `PASSIVE` instead of logging success, and closing-time checkpoints no longer report success on builds without a database file, with the busy totals visible in the authenticated monitoring health payload ([#12853](https://github.com/diegosouzapw/OmniRoute/pull/12853)) — thanks @maxmad64bis +- **fix(health):** quota with no snapshots reads empty instead of a contradicting 0% ([#12857](https://github.com/diegosouzapw/OmniRoute/pull/12857)) — thanks @maxmad64bis +- fix(a2a): accept the dashboard's own session cookie on /a2a so "Run message/send" no longer fails with "Unauthorized: missing or invalid API key" (#12888) ([#13271](https://github.com/diegosouzapw/OmniRoute/pull/13271)) +- **fix(logs):** the Logs grid's in-memory filter pass no longer discards rows the SQL query already matched — selecting an API key from the dropdown (which sends the key's id) returns its calls again, the Combo tab shows every combo instead of only those whose name contains a "1", and the model filter and search cover the same columns as the query ([#12896](https://github.com/diegosouzapw/OmniRoute/pull/12896)) — fixes [#12873](https://github.com/diegosouzapw/OmniRoute/issues/12873) — thanks @ntdatt812 +- **fix(a2a):** `/api/a2a/status` now builds the agent card from the request that asked for it, so a gateway reached at a non-localhost host no longer advertises `http://localhost:20128` as its A2A URL ([#12918](https://github.com/diegosouzapw/OmniRoute/pull/12918)). — thanks @ntdatt812 +- **fix(compression):** progressive aging now appends its `[COMPRESSED:aging:…]` annotation after a turn's `tool_result` blocks instead of in front of them, so Anthropic no longer rejects aged conversations with "`tool_use` ids were found without `tool_result` blocks immediately after" ([#12920](https://github.com/diegosouzapw/OmniRoute/pull/12920)). — thanks @ntdatt812 +- **fix(bedrock):** model import now resolves context limits for every vendor prefix instead of only `anthropic.*`, so `global.openai.gpt-5.6-*` no longer imports with a null `inputTokenLimit` and gets rejected pre-flight at the 200k default ([#12921](https://github.com/diegosouzapw/OmniRoute/pull/12921)). — thanks @ntdatt812 +- **fix(stream):** the 64 KB stream buffer GLM asks for is honoured instead of dropped, and the type error it caused no longer fails the API Route Typecheck gate on every open PR ([#12925](https://github.com/diegosouzapw/OmniRoute/pull/12925)) — thanks @ntdatt812 +- **fix(guardrails):** mask PII inside a `tool_result`'s nested content array, which the masker walked past while redacting its sibling block ([#12930](https://github.com/diegosouzapw/OmniRoute/pull/12930)) — thanks @ntdatt812 +- **fix(dashboard):** the Radar catalog table no longer leaves absent data unexplained — empty limits, unknown context windows, and unreported capabilities each explain themselves on hover, and a new check keeps it that way ([#12937](https://github.com/diegosouzapw/OmniRoute/pull/12937)) — thanks @maxmad64bis +- **fix(sse):** geo-blocked opencode requests rotate to the next account proxy instead of failing, so one refused egress no longer aborts the whole chain ([#12941](https://github.com/diegosouzapw/OmniRoute/pull/12941)) — thanks @maxmad64bis +- **fix(codex):** dashboard "clear cooldown" and the CAS recovery path now drop nested `codexScopeRateLimitedUntil` maps in the same write that nulls `rate_limited_until`, and fresh quota snapshots with headroom lift fallback-sourced scope cooldowns parked by quota preflight ([#12817](https://github.com/diegosouzapw/OmniRoute/issues/12817), [#12860](https://github.com/diegosouzapw/OmniRoute/issues/12860), [#12951](https://github.com/diegosouzapw/OmniRoute/pull/12951)) The dashboard reset-credit button (`consumeCodexResetCredit`) now exercises that same snapshot path after a successful redeem, so a filled quota bar is enough to unpark a leftover fallback Codex child without another manual clear. — thanks @HouMinXi +- **fix(db):** resolve `sql-wasm.wasm` across global npm install and hoisted layouts, ensuring OmniRoute can boot cleanly on Node 24 when native `better-sqlite3` is uncompiled. ([#13035](https://github.com/diegosouzapw/OmniRoute/pull/13035)) — thanks @HouMinXi +- **fix(api):** `DELETE /v1/batches/delete-completed` now sweeps only the calling API key's own completed batches (batches with no owner stay out of a key-scoped sweep on purpose), with an explicit instance-wide mode reserved for authenticated dashboard sessions, a 401 for a presented key that is unknown, revoked, deactivated, banned or expired (never falling through to the session branch), audit logging of every sweep, a sanitized 500 on failure, an owner-scoped file half (a key-scoped sweep never nulls a file another tenant owns) and an atomic sweep — chunked in 200-batch transactions in instance mode — so a mid-way error never leaves a batch pointing at a nulled file (GHSA-wvxc-jp3v-5mg5) ([#12969](https://github.com/diegosouzapw/OmniRoute/pull/12969)) +- **fix(combo):** quota-weighted routing stops drawing on an out-of-credit connection — a 402 now invalidates the stored quota snapshot instead of leaving its stale remaining percentage in place, and a snapshot older than 10 minutes no longer counts as confident headroom for the primary pool ([#12972](https://github.com/diegosouzapw/OmniRoute/pull/12972)) ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- **fix(vision):** Custom Models with "Vision capable" checked no longer have image requests swapped to `glm/glm-4.6v` when the client sends the advertised alias (`vllm/path/...`) or the bare path-shaped id — Vision Bridge now matches the stored override for all three id forms ([#12758](https://github.com/diegosouzapw/OmniRoute/issues/12758)) ([#12974](https://github.com/diegosouzapw/OmniRoute/pull/12974)) — thanks @HouMinXi +- **fix(sse):** transient opencode upstream failures rotate to the next account proxy instead of failing, so one flapping egress no longer aborts the whole chain ([#12975](https://github.com/diegosouzapw/OmniRoute/pull/12975)) — thanks @maxmad64bis +- **fix(azure):** Deployments from GPT-6 onward now send `max_completion_tokens` instead of `max_tokens`, which Azure rejects with HTTP 400. The rule matched a literal `gpt-5`, so each new generation arrived broken; it now matches the generation range, while `gpt-35-turbo` still keeps `max_tokens`. ([#13007](https://github.com/diegosouzapw/OmniRoute/pull/13007)) — thanks @ntdatt812 +- **fix(db):** add an opt-in self-restart circuit for sustained critical memory pressure, gate post-cleanup VACUUM behind a minimum freed-rows threshold, and checkpoint the SQLite WAL every 5 minutes with a size guard that escalates to TRUNCATE, so a growing WAL can no longer stall the event loop into a full outage. ([#13011](https://github.com/diegosouzapw/OmniRoute/pull/13011)) — thanks @HouMinXi +- **fix(auth):** an explicit connection pin may probe a quota-disabled row once and re-enable it on success ([#12874](https://github.com/diegosouzapw/OmniRoute/issues/12874)) ([#13017](https://github.com/diegosouzapw/OmniRoute/pull/13017)) — thanks @HouMinXi +- **fix(api):** thread `X-OmniRoute-Fallback-Attempts` through combo chat completions so streaming and non-streaming responses report how many prior legs were attempted ([#13038](https://github.com/diegosouzapw/OmniRoute/pull/13038)) — thanks @HouMinXi +- Restore provider failure classification and credential refresh on non-streaming requests: classify non-2xx failures to lock models on per-model quota exhaustion, update connection rate limits from headers and body, and pass credential refresh handlers to pipeline execution so 401 tokens can be refreshed and retried (#13043). ([#13069](https://github.com/diegosouzapw/OmniRoute/pull/13069)) — thanks @HouMinXi +- **fix(responses):** wrap forced-non-streaming web_search fallback JSON as Responses SSE so Codex still sees `response.completed` ([#13050](https://github.com/diegosouzapw/OmniRoute/pull/13050)) — thanks @HouMinXi +- **fix(antigravity):** Preserve upstream thought-token usage in normalized completion and reasoning token counts ([#13055](https://github.com/diegosouzapw/OmniRoute/pull/13055)) — thanks @pacocartones +- Fix a concurrency-slot leak in the DeepSeek PoW solver: a worker that failed to spawn (for example a missing worker script) never released its slot, so `MAX_CONCURRENT_WORKERS` failures disabled the solver until restart. ([#13097](https://github.com/diegosouzapw/OmniRoute/pull/13097)) — thanks @anhtahaylove +- **fix(acp):** bound the ACP session output buffers — `stdoutBuffer` and `stderrBuffer` now cap at 1 MiB keeping the most recent output behind a visible `[...output truncated...]` marker, and `stderrBuffer` is reset per prompt instead of accumulating for the lifetime of the session. ([#13100](https://github.com/diegosouzapw/OmniRoute/pull/13100)) — thanks @anhtahaylove +- **fix(acp):** release the `stdout`/`exit` listeners and the idle timer that a `sendPrompt` timeout used to leave attached to the `acpManager` singleton, and drop sessions that exited on their own from the session map instead of keeping them forever. ([#13096](https://github.com/diegosouzapw/OmniRoute/pull/13096)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection and PII scanners now read the text a `tool_result` block carries on `content` (string or nested block list), in messages and in system blocks, so tool output is judged by the same rules as user text ([#13101](https://github.com/diegosouzapw/OmniRoute/pull/13101)) — thanks @ntdatt812 +- **fix(gamification):** close the badge notification SSE stream when the request signal is already aborted before the stream starts — a client that disconnects while the route is still awaiting auth used to leave both the 2s unlock poll and the 15s heartbeat running for the lifetime of the process. ([#13106](https://github.com/diegosouzapw/OmniRoute/pull/13106)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection scan now spends its 16 KB budget on both ends of the request instead of the first 16 KB only, so `system`, `instructions`, `query`, `documents` and the newest turns are no longer hidden behind one long message ([#13104](https://github.com/diegosouzapw/OmniRoute/pull/13104)) — thanks @ntdatt812 +- **fix(dashboard):** expose the Volcano Ark console cookie on quota scraping and unwrap connect-error objects so the dashboard shows the upstream message ([#13107](https://github.com/diegosouzapw/OmniRoute/pull/13107)) — thanks @HouMinXi +- **fix(db):** release the `beforeExit`/`SIGINT`/`SIGTERM` handlers when a `node:sqlite` adapter closes, so a closed adapter and its database handle are no longer pinned to `process` for the lifetime of the run — the same treatment #7494 gave the sql.js adapter. ([#13109](https://github.com/diegosouzapw/OmniRoute/pull/13109)) — thanks @anhtahaylove +- **fix(translator):** `contentSchema` and `unevaluatedItems` are now treated as subschema positions by the tool-schema sanitizer, so a truncation placeholder in either is replaced with a permissive schema instead of being forwarded as a string ([#13110](https://github.com/diegosouzapw/OmniRoute/pull/13110)) — thanks @ntdatt812 +- **fix(cli-helper):** clear the `createLogStream` timeout on the abort path — `stop()` aborts the in-flight fetch and returned through the `signal.aborted` branch, which skipped `clearTimeout` and left an armed timer per stopped stream. The stream reader is now also cancelled when the read loop exits early. ([#13114](https://github.com/diegosouzapw/OmniRoute/pull/13114)) — thanks @anhtahaylove +- **fix(docs):** drop TLS-impersonation, MITM-decrypt, supply-chain attestation, and XOR-mask writeups from the public `/docs` catalog and Docker image. Files stay in git for engineers; operators who need them open the repo, not the website. ([#13136](https://github.com/diegosouzapw/OmniRoute/pull/13136)) — thanks @HouMinXi +- **fix(combo):** parse numeric-epoch `rate_limited_until` in the combo cooldown read path ([#13141](https://github.com/diegosouzapw/OmniRoute/pull/13141)) — thanks @maxmad64bis +- **fix(opencode):** both OpenCode plugins now reject a gateway address typed without `http://` at configuration time, instead of publishing every model with an api url no client can call, and the v2 plugin no longer publishes a model card whose api url is blank or relative ([#13142](https://github.com/diegosouzapw/OmniRoute/pull/13142)) — thanks @maxmad64bis +- **fix(providers):** lock opencode model on upstream 400 model-unavailable ([#13146](https://github.com/diegosouzapw/OmniRoute/pull/13146)) — thanks @maxmad64bis +- **fix(logging):** keep the provider exchange rather than the raw client bodies when a call log exceeds its size budget, and show that recovered payload in the request-detail panel instead of replacing it with the stored response body ([#13147](https://github.com/diegosouzapw/OmniRoute/pull/13147)) — thanks @maxmad64bis +- **fix(traffic-inspector):** the WebSocket route no longer leaks a traffic-buffer subscriber and a 30s ping timer when the client socket is already closed at handler time — listeners are attached before any resource is acquired, a destroyed socket bails out early, and the ping interval stops on a dead socket where `write()` never throws ([#13155](https://github.com/diegosouzapw/OmniRoute/pull/13155)) — thanks @anhtahaylove +- **fix(telegram):** bound the per-user API key cache in the Telegram chat proxy so a burst of distinct chat ids can no longer grow the process heap without limit ([#13165](https://github.com/diegosouzapw/OmniRoute/issues/13165)) ([#13166](https://github.com/diegosouzapw/OmniRoute/pull/13166)) — thanks @anhtahaylove +- **fix(stream):** cancel the upstream response body when the JSON-to-SSE sniff unwinds on a body timeout, so a stalled upstream no longer pins the connection ([#13169](https://github.com/diegosouzapw/OmniRoute/issues/13169)) ([#13171](https://github.com/diegosouzapw/OmniRoute/pull/13171)) — thanks @anhtahaylove +- **fix(telegram):** authenticate webhook deliveries with Telegram's `secret_token` so an unauthenticated caller can no longer mint API keys or spend upstream quota ([#13172](https://github.com/diegosouzapw/OmniRoute/issues/13172)) ([#13175](https://github.com/diegosouzapw/OmniRoute/pull/13175)) — thanks @anhtahaylove +- **fix(models):** give discoverable Gemini 3.8 Flash ids their own 65536 output spec so Antigravity no longer clamps them to 16384 ([#13195](https://github.com/diegosouzapw/OmniRoute/pull/13195)) — thanks @HouMinXi +- **fix(dashboard):** leftover catalog-removed provider rows (gemini-cli) can be listed and purged from the providers page ([#13067](https://github.com/diegosouzapw/OmniRoute/issues/13067)) ([#13197](https://github.com/diegosouzapw/OmniRoute/pull/13197)) — thanks @HouMinXi +- **fix(auth):** a dashboard session now requires the `authenticated: true` claim that login, OIDC and the session refresh already emit — a JWT merely signed with `JWT_SECRET` (for example the Cursor CLI passthrough token, which any API-key holder can obtain) no longer verifies as the `auth_token` cookie on any route, the WebSocket handshake or the live server; existing sessions keep working ([#13298](https://github.com/diegosouzapw/OmniRoute/issues/13298)) ([#13375](https://github.com/diegosouzapw/OmniRoute/pull/13375)) +- **fix(api):** `DELETE /v1/batches/delete-completed` now applies the caller's API-key policy (endpoint allowlist, schedule, usage cap, rate limit) like every other `/v1` route, commits the key-scoped sweep per 200-batch chunk instead of holding one write lock for the whole sweep, refuses to loop without progress, and rejects a scope that names both a key and `allTenants` ([#13684](https://github.com/diegosouzapw/OmniRoute/pull/13684)) +- Restore the API-route typecheck gate: the non-streaming leg result lost its discriminated-union narrowing after the server-owned tool loop reassignment, producing 13 new TS2339 diagnostics in `chatCore.ts`. ([#12963](https://github.com/diegosouzapw/OmniRoute/pull/12963)) +- Restore four non-streaming behaviours the server-owned tool loop refactor dropped: client aborts map to 499 with the fixed `Request aborted` message, an aborted request no longer logs a synthetic `clientResponse`, Claude prompt-cache telemetry is recorded again, and a body that cannot be canonicalized no longer throws when the tool loop is off. ([#12990](https://github.com/diegosouzapw/OmniRoute/pull/12990)) +- **fix(skills):** The CLI registry parser now reads positionals declared with `.addArgument()`, not only those written inline in `.command()`. `tunnel create [type]` was being published as `tunnel create`, so the agent-skills sync gate reported drift on every branch and regenerating would have deleted the argument. ([#13009](https://github.com/diegosouzapw/OmniRoute/pull/13009)) — thanks @ntdatt812 +- **fix(combos):** dashboard combo test uses a short prompt, serial probes, and a 60s timeout so reasoning models and rate-limited free pools do not fail the health check ([#13001](https://github.com/diegosouzapw/OmniRoute/pull/13001)) — thanks @HouMinXi +- fix(compression): terminate idle worker threads on eviction so long-running instances stop leaking OS threads and MessagePorts ([#13091](https://github.com/diegosouzapw/OmniRoute/pull/13091)) — thanks @anhtahaylove +- Redact Google API keys of any length in error bodies: the pattern required exactly 39 characters, so shorter or longer `AIza…` credentials passed through unredacted. ([#12964](https://github.com/diegosouzapw/OmniRoute/pull/12964)) +- **fix(grok-cli):** a 402 "Grok Build usage balance exhausted" parks that Grok login as out of credit (Grok Build CLI, grok.com cookie, and xAI OAuth share the weekly pool). Combo routing then tries the next login instead of locking the model for every account in the pool ([#13061](https://github.com/diegosouzapw/OmniRoute/pull/13061)) — thanks @HouMinXi +- fix(compression): spawn the LLMLingua worker with a file URL object so compression actually runs instead of silently failing open on Node ([#13093](https://github.com/diegosouzapw/OmniRoute/pull/13093)) — thanks @anhtahaylove +- **fix(test):** run the local `test` and `test:unit` scripts at concurrency 4 so a full-suite run no longer exhausts the machine's commit charge and kills unrelated processes ([#13187](https://github.com/diegosouzapw/OmniRoute/pull/13187)) — thanks @anhtahaylove +- **fix(monitoring):** `GET /api/monitoring/health` `credentialHealth` now includes a bounded `failedConnections` list (`connectionId`, `status`, sanitized `lastError`) when the probe-cache gauge `failed>0`, plus `source: probe-cache` and a cheap `staleDbNonOkCount` for sticky SQLite `test_status` on active rows. Documents that the live gauge is not `provider_connections.test_status`. ([#12876](https://github.com/diegosouzapw/OmniRoute/pull/12876)) — thanks @RaviTharuma +- **fix(resilience):** an openai-compatible multi-upstream gateway no longer marks the whole connection `credits_exhausted` when a single upstream (or the `/models` / representative-model health probe) returns 402. Quota failures stay model-scoped; true connection-wide auth failures are unchanged. Single-credential 402 key disable ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239)) is preserved. ([#12875](https://github.com/diegosouzapw/OmniRoute/pull/12875)) — thanks @RaviTharuma +- **fix(dashboard):** nine fixes on the Orchestration Canvas, all diagnosed in the Phase 2 reviews. A source that fails now keeps the timestamp of its FIRST failure instead of being re-stamped every poll — the stale line said "since the last poll" no matter how long the source had been down, and the churn also defeated the snapshot's stable identity (it serializes the source list), so the canvas re-rendered on every tick while anything was broken. A source that HAD data and only then started failing is flagged too: previously only a source with no node at all got the warning, so a source that went down mid-session kept a healthy-looking node forever. The rest are pointwise: clicking a filter chip cancels the pending search debounce (left armed, it fired ~300ms later and silently reverted the chip); the search input carries an accessible name; `?state=running, failed` parses like the unpadded form instead of dropping the padded value; the CSV toggle helper is defined once in `model/urlParams.ts` rather than twice; the agents tab tells "nothing running" apart from "the filter matched nothing", offering a clear-filters button instead of setup links that would be wrong advice there; edges stop emitting SMIL particles above 40 simultaneously active edges, keeping the colored stroke; and the drawer's error banner clears when a retried action succeeds. ([#12983](https://github.com/diegosouzapw/OmniRoute/pull/12983)) +- **fix(i18n):** the nine locales added with the EU-language batch (Greek, Estonian, Irish, Croatian, Lithuanian, Latvian, Maltese, Slovenian, Serbian) were missing the eleven Orchestration Canvas keys that Phase 3 introduced, so the compare-runs panel and the "no runs match these filters" empty state fell back to English in those languages (`deepMergeFallback` substitutes English for an absent key, so nothing rendered blank — it rendered untranslated). The coverage gate does not catch this: it enforces an 80% floor per locale, and eleven missing keys out of ~13,000 leaves coverage at 99.9%. Translated for real in each language, calibrated against the wording each file already uses for "run", "filter" and "skill". ([#13294](https://github.com/diegosouzapw/OmniRoute/pull/13294)) +- **fix(dashboard):** five follow-ups from the Phase 2 Orchestration Canvas review. The History drawer now shows the memory section for runs that already left the live TTL window: the persisted `memory_hits` event is parsed into `metadata.memoryHits` with the same defensive validation the drawer applies, and — because that event is observability rather than a state transition — it no longer leaks into the timeline, where it had been inheriting the task's state and rendering as a duplicate transition. The A2A memory recall runs against its own 1.5s deadline instead of inheriting the memory backend's 30s one; overshooting degrades exactly like any other recall failure (no hits, task proceeds), and the timer is cleared on both paths. Repeating a Conductor run carries its `requirements.cli`/`requirements.model` forward, so the new run is pinned to the same runner profile and model rather than drifting to whatever the fleet picks. A successful repeat from the Agents tab now focuses the run it created, instead of leaving the operator on the finished one. And the auth test for `POST /api/conductor/tasks` moved into the shared `ROUTES` array rather than restating the pattern. ([#12988](https://github.com/diegosouzapw/OmniRoute/pull/12988)) +- fix(plugins): stop leaking an exit listener per plugin hook timeout, which triggered MaxListenersExceededWarning on plugins that ignore SIGTERM ([#13092](https://github.com/diegosouzapw/OmniRoute/pull/13092)) — thanks @anhtahaylove +- **fix(validation):** Provider node edits no longer fail with a generic "Invalid request" when the optional daily-quota reset fields are left blank. The dashboard sends `dailyQuotaResetTimezone` and `dailyQuotaResetHour` as `null`, and only the hour accepted it. ([#13066](https://github.com/diegosouzapw/OmniRoute/issues/13066)) ([#13083](https://github.com/diegosouzapw/OmniRoute/pull/13083)) — thanks @ntdatt812 +- **fix(combo):** quota-aware expansion drops banned, inactive, missing, and wrong-provider connections before quota fetch or model dispatch; pins and allowlists stay selectors, not a bypass. Antigravity automatic exhaustion now requires a reported zero remaining, so a positive balance below 1% stays eligible. ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- Fix an intermittent failure in the quota-weighted routing test suite: scores are a function of `Date.now()`, so peers with identical quota scored microseconds apart never tied and swapped order. ([#12946](https://github.com/diegosouzapw/OmniRoute/pull/12946)) +- fix(volcengine): resolve quota window mapping gaps, safe multi-connection console binding, and response cookie sanitization ([#12950](https://github.com/diegosouzapw/OmniRoute/pull/12950)) — thanks @HouMinXi +- **fix(test):** resolve the WebDAV handler path with `fileURLToPath` so the suite's 37 WebDAV tests run on Windows instead of failing with a doubled `C:\C:\` drive prefix ([#13196](https://github.com/diegosouzapw/OmniRoute/pull/13196)) — thanks @anhtahaylove +- **fix(ui):** fall back from invalid AnySearch icon alias (direct commit `9587e07b69`) +- **fix(sse):** stop rebuilding a truncated summary from the collector's cap-dropped event array ([#12718](https://github.com/diegosouzapw/OmniRoute/pull/12718)) — thanks @hartmark +- **fix(conversations):** show a pending spinner for unresolved tool nodes instead of "(empty)" ([#12727](https://github.com/diegosouzapw/OmniRoute/pull/12727)) — thanks @hartmark +- **fix(streaming):** fail fast when an upstream stream produces only lifecycle/heartbeat events, never real content ([#12741](https://github.com/diegosouzapw/OmniRoute/pull/12741)) — thanks @hartmark +- **fix(responses-continuation):** bridge the write-in-flight window with an in-memory pending store ([#12854](https://github.com/diegosouzapw/OmniRoute/pull/12854)) — thanks @hartmark +- **fix(translator):** drop or map agent_message on Chat Completions fallback ([#12880](https://github.com/diegosouzapw/OmniRoute/pull/12880)) — thanks @RaviTharuma +- **fix(api):** page and stream-complete GET /v1/models for large catalogs ([#12882](https://github.com/diegosouzapw/OmniRoute/pull/12882)) — thanks @RaviTharuma +- **fix(quota):** align AUTH window labels with usage API and clarify 503 ([#12884](https://github.com/diegosouzapw/OmniRoute/pull/12884)) — thanks @RaviTharuma +- **fix(ci):** clear two base-reds on release/v3.8.51 — mutation-coverage gate + image-only-model guard ([#12945](https://github.com/diegosouzapw/OmniRoute/pull/12945)) +- **fix(api):** scope batch bulk-delete to the calling API key ([#13211](https://github.com/diegosouzapw/OmniRoute/pull/13211)) +- **fix(tests):** align model-sync log assertion to shipped path redaction (base-red) ([#13227](https://github.com/diegosouzapw/OmniRoute/pull/13227)) +- **fix(test):** make the ToS heading guard actually require the parentheses ([#13228](https://github.com/diegosouzapw/OmniRoute/pull/13228)) +- **fix(quality):** register 4 drifted covering tests in stryker tap.testFiles ([#13229](https://github.com/diegosouzapw/OmniRoute/pull/13229)) +- **fix(logging):** mask *-api-key headers in request-log pipeline (#13273) ([#13401](https://github.com/diegosouzapw/OmniRoute/pull/13401)) — thanks @KooshaPari +- **fix(tests):** correct stale Jina catalog prefix in custom-models test ([#13403](https://github.com/diegosouzapw/OmniRoute/pull/13403)) — thanks @KooshaPari +- **fix(backend):** prune old backups after health-check-repair snapshots (#13308) ([#13404](https://github.com/diegosouzapw/OmniRoute/pull/13404)) — thanks @KooshaPari +- **fix(backend):** guard memory_fts_au trigger with WHEN clause to stop FTS bloat (#13326) ([#13405](https://github.com/diegosouzapw/OmniRoute/pull/13405)) — thanks @KooshaPari +- **fix(free-tiers):** remove obsolete Together $25 signup credit (#12526) ([#13407](https://github.com/diegosouzapw/OmniRoute/pull/13407)) — thanks @KooshaPari +- **fix:** skip non-chat generation models in Test all models ([#13409](https://github.com/diegosouzapw/OmniRoute/pull/13409)) — thanks @KooshaPari +- **fix(providers):** de-duplicate agy and antigravity model catalogs ([#13410](https://github.com/diegosouzapw/OmniRoute/pull/13410)) — thanks @KooshaPari +- **fix(dashboard):** send provider in body for mini-playground web search ([#13411](https://github.com/diegosouzapw/OmniRoute/pull/13411)) — thanks @KooshaPari +- **fix(resilience):** add CJK quota-exhaustion patterns to 429 classifier ([#13412](https://github.com/diegosouzapw/OmniRoute/pull/13412)) — thanks @KooshaPari +- **fix(backend):** eval runner: failed calls no longer score as passed + dotAll regex ([#13413](https://github.com/diegosouzapw/OmniRoute/pull/13413)) — thanks @KooshaPari +- **fix(backend):** eval runner opts out of output-style and memory injection ([#13414](https://github.com/diegosouzapw/OmniRoute/pull/13414)) — thanks @KooshaPari +- **fix:** chain serial quarantine step in npm test script (fixes #13286) ([#13418](https://github.com/diegosouzapw/OmniRoute/pull/13418)) — thanks @KooshaPari +- **fix(antigravity):** derive stable session ID from credentials for prompt-cache reuse (#13305) ([#13424](https://github.com/diegosouzapw/OmniRoute/pull/13424)) — thanks @KooshaPari +- **fix(memory):** merge memory into top-level system field instead of unshifting at messages[0] (#13425) ([#13427](https://github.com/diegosouzapw/OmniRoute/pull/13427)) — thanks @KooshaPari +- **fix(compression):** preserve instruction blocks from lossy engines ([#13523](https://github.com/diegosouzapw/OmniRoute/pull/13523)) — thanks @KooshaPari +- **fix(compression):** preserve polarity words and newlines in ultra heuristic ([#13524](https://github.com/diegosouzapw/OmniRoute/pull/13524)) — thanks @KooshaPari +- **fix(compression):** run user preservePatterns before built-in patterns (#13457) ([#13525](https://github.com/diegosouzapw/OmniRoute/pull/13525)) — thanks @KooshaPari +- **fix(reasoning):** expand deepseek-v4 regex to match v4.1+ and add command-code provider (#13430) ([#13528](https://github.com/diegosouzapw/OmniRoute/pull/13528)) — thanks @KooshaPari +- **fix(providers):** add alwaysReasons metadata for reka-flash-3 ([#13531](https://github.com/diegosouzapw/OmniRoute/pull/13531)) — thanks @KooshaPari +- **fix(playground):** allow Compare columns to scroll independently ([#13532](https://github.com/diegosouzapw/OmniRoute/pull/13532)) — thanks @KooshaPari +- **fix(security):** empty whitelist allows through to prevent admin lockout ([#13534](https://github.com/diegosouzapw/OmniRoute/pull/13534)) — thanks @KooshaPari +- **fix(tests):** add DISABLE_IOREG_STRATEGY env var for macOS test compatibility (fixes #13467) ([#13539](https://github.com/diegosouzapw/OmniRoute/pull/13539)) — thanks @KooshaPari +- **fix(zed-hosted):** map developer role and Gemini enums for Zed proxy ([#13543](https://github.com/diegosouzapw/OmniRoute/pull/13543)) — thanks @KooshaPari +- **fix(analytics):** normalize provider aliases to canonical id in usage_history ([#13545](https://github.com/diegosouzapw/OmniRoute/pull/13545)) — thanks @KooshaPari +- **fix(combo):** use traceId for call log id to prevent UNIQUE constraint failure ([#13546](https://github.com/diegosouzapw/OmniRoute/pull/13546)) — thanks @KooshaPari +- **fix(auth):** check model lockout before returning synthetic noauth connection ([#13547](https://github.com/diegosouzapw/OmniRoute/pull/13547)) — thanks @KooshaPari +- **fix(guardrails):** skip credential redaction for base64 image data URLs ([#13550](https://github.com/diegosouzapw/OmniRoute/pull/13550)) — thanks @KooshaPari +- **fix(resilience):** increase requestQueue.maxWaitMs default from 15s to 30s ([#13553](https://github.com/diegosouzapw/OmniRoute/pull/13553)) — thanks @KooshaPari +- **fix(docs):** document OMNIROUTE_READY_TIMEOUT_MS and allowlist the test-only DISABLE_IOREG_STRATEGY ([#13692](https://github.com/diegosouzapw/OmniRoute/pull/13692)) + ### 📝 Maintenance ---- +- **docs(readme):** reconcile live v3.8.50 provider, free-tier, CLI, routing, test, community, sponsor, acknowledgment, and SVG metrics with their audited source denominators, including a deduplicated OmniRoute-in-Action snapshot and distinct contributor rankings for merged pull requests, GitHub-attributed commits, and Git history ([#11356](https://github.com/diegosouzapw/OmniRoute/pull/11356)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **docs:** sync the canonical provider count 354 → 356 across `README.md`, `AGENTS.md`, `llm.txt` (+ 42 i18n mirrors), the four README SVG diagrams, `docs/reference/PROVIDER_REFERENCE.md` (regenerated) and the `package.json` description after Opper (#11629) and 1min.ai (#11631) boarded the catalog — closes the `check:docs-counts-sync` strict drifts that kept `release/v3.8.51` red ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11673](https://github.com/diegosouzapw/OmniRoute/pull/11673)) — thanks @jonlwheat2-gif +- **test(providers):** update count-derived assertions after the v3.8.51 provider additions — `APIKEY_PROVIDERS` 233 → 235 (Opper #11629 + 1min.ai #11631), reserved-prefix REGISTRY walk 395 → 398, `WEB_FETCH_PROVIDERS` now includes `nimble-search` (#11620), and the provider translate-path golden snapshot regenerated ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11674](https://github.com/diegosouzapw/OmniRoute/pull/11674)) — thanks @jonlwheat2-gif +- **test(sse):** bump the hard-lease connection-query inventory for `src/lib/tokenHealthCheck.ts` to 2 — the verify-only web-cookie sweep added by #11495 split the single `getProviderConnections` call into oauth + cookie variants, which the frozen inventory had not tracked ([#11495](https://github.com/diegosouzapw/OmniRoute/pull/11495)). ([#11677](https://github.com/diegosouzapw/OmniRoute/pull/11677)) — thanks @jonlwheat2-gif / @oyi77 +- **chore(release):** clear the `release/v3.8.51` base-red gates — docs migration count (+38 i18n mirrors), stryker `tap.testFiles`, lockfile registry host, stale ESLint suppressions, the 7 lint regressions they were hiding, the stale `APIKEY_PROVIDERS` partition count (231 → 233), the `glm-5.3-max` tier missing from the GLM test table, a dead `modelType === "chat"` comparison in the models catalog, and the `resolveLiveWsUrl` / `sanitizeLiveWsPort` helpers from #11388 that a later merge dropped from `wsPath.ts` (dashboard live WebSocket URL resolution) ([#11502](https://github.com/diegosouzapw/OmniRoute/pull/11502)) — thanks @MumuTW +- **chore(free-models):** the free-tier regime table now declares which totals figure each regime feeds and whether it may take the no-auth shortcut, so the sets that used to repeat those answers by hand are derived from it — a new `freeType` no longer compiles until it has answered every question, instead of silently contributing to no total ([#11537](https://github.com/diegosouzapw/OmniRoute/pull/11537)) — thanks @maxmad64bis +- **docs(routing):** LKGP strategy copy now states provider-level stickiness and the explicit rules fallback instead of promising per-target pinning ([#11555](https://github.com/diegosouzapw/OmniRoute/pull/11555)) — thanks @maxmad64bis +- **test(chatcore):** pin the response tool-name alias resolution against the ordering hazard that broke every Gemini/Antigravity MCP tool call in v3.8.49. `extractRequestToolIdentityMap` deletes `translatedBody._toolNameMap`, so the later read is always undefined and the ledger survives only through the `requestToolIdentityMap` fallback — removing that fallback previously left the entire tool-name suite green. The resolution moves into `resolveResponseToolNameMap()` next to the map it depends on, with a regression guard that fails without the recovery. No behaviour change. ([#11575](https://github.com/diegosouzapw/OmniRoute/pull/11575)) — thanks @amartinawi +- **docs(env):** document `OMNIROUTE_CORPUS_CACHE_SIZE` in `.env.example` and `ENVIRONMENT.md`, so the env/docs contract check stops failing Docs Gates on every branch ([#11576](https://github.com/diegosouzapw/OmniRoute/pull/11576)) — thanks @ntdatt812 +- **chore(stryker):** register `route-guard-tunnel-processes-local-only` and `universal-quota-aware-routing` in `tap.testFiles`, so their mutant kills count for the three modules they cover ([#11577](https://github.com/diegosouzapw/OmniRoute/pull/11577)) — thanks @ntdatt812 +- **test(authz):** pin `LOCAL_ONLY_API_GET_EXEMPTIONS` by exact membership instead of by entry count, so the guard names the offending path and also catches a substitution ([#11580](https://github.com/diegosouzapw/OmniRoute/pull/11580)) — thanks @ntdatt812 +- **test(build):** pin `resolveNextBuildBundlerFlag` to the `OMNIROUTE_USE_TURBOPACK` contract it implements, across runtimes, instead of asserting an unimplemented Bun override ([#11583](https://github.com/diegosouzapw/OmniRoute/pull/11583)) — thanks @ntdatt812 +- **test(claude-web):** the Claude Web transport test's barrel anchors no longer pin the exact construction spelling. #11421 made the executor map lazy, so `/"claude-web": new ClaudeWebExecutor\(\)/` stopped matching and the test went red on `release/v3.8.51` itself — a base-red gate every open PR inherited. The anchors now match the alias key and the class name and tolerate either spelling in between, and were checked to still fail when an alias is genuinely repointed at another executor ([#11585](https://github.com/diegosouzapw/OmniRoute/pull/11585)) — thanks @ntdat812 +- **test(models):** pin `qwen3.8-max` resolution per provider — two of the four catalogs now serve the bare id and no longer carry `-preview` ([#11590](https://github.com/diegosouzapw/OmniRoute/pull/11590)) — thanks @ntdatt812 +- **test(executors):** drop the stale 128-tool truncation assertion that contradicted the #11444 pass-through contract ([#11591](https://github.com/diegosouzapw/OmniRoute/pull/11591)) — thanks @ntdatt812 +- **test(token-health):** pin the current terminal-skip boundary — expired with retry budget is probed, `account_deactivated` stays skipped ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)) — thanks @ntdatt812 +- **test(models):** follow #11503's retarget of the `gemini-3-pro-high` alias to the hyphenated catalog id ([#11593](https://github.com/diegosouzapw/OmniRoute/pull/11593)) — thanks @ntdatt812 +- **refactor(video bridge):** extract per-video acquisition, whole-result caching, description, and metrics/abort/cleanup out of `VideoBridgeGuardrail.preCall` into a `processVideoPart` seam in a new `videoBridgePipeline.ts`, behind explicit `VideoMediaBrokerPort`, `VideoAudioTranscriptionPort`, and `VideoDrilldownPort` boundaries; `preCall` now only handles request traversal, policy, and response aggregation. The Video tab's FFmpeg/ffprobe runtime status is now an explicit `unknown` / `restricted` / `unavailable` / `available` state instead of a nullable boolean pair, fixing a case where an in-flight or failed probe was mislabeled as "install FFmpeg" ([#11657](https://github.com/diegosouzapw/OmniRoute/issues/11657)). ([#12016](https://github.com/diegosouzapw/OmniRoute/pull/12016)) +- **test(opencode-plugin):** add unit test coverage for `formatFreeBudget()` naming helper ([#11660](https://github.com/diegosouzapw/OmniRoute/pull/11660)) — thanks @f9td56dbgh-hub +- **docs(video):** clarify that the Video Bridge transcript `source` field (`client`, `embedded`, `audio-bridge`) is presently caller-declared and not yet server-verified — OmniRoute enforces the enum shape but does not cryptographically confirm that an `embedded`/`audio-bridge` label came from a server-owned extraction ([#11661](https://github.com/diegosouzapw/OmniRoute/issues/11661)). ([#12001](https://github.com/diegosouzapw/OmniRoute/pull/12001)) +- **chore(providers):** retire the distributed Raycast Relay and Hailuo Web integrations after a provenance audit confirmed substantial derivation from GPL-3.0 sources; the official GitHub and MiniMax API providers remain available. Migration 163 disables legacy connections and invalidates their active leases while preserving connection IDs, audit/history rows, quota snapshots, usage records, and fail-closed API-key connection policies. ([#11691](https://github.com/diegosouzapw/OmniRoute/pull/11691)) +- **chore(providers):** retire the Felo Web provider while its GPL-derived provenance remains on licensing HOLD ([#11698](https://github.com/diegosouzapw/OmniRoute/pull/11698)) — stale runtime and database references now fail closed while existing connection, lease, and usage history remains preserved for auditability. +- **fix(sse):** retire the Gemini Web image-generation bridge while provenance and licensing remain on HOLD, preserving Gemini Web chat/auth/tools and legitimate Gemini/Nano Banana image providers ([#11708](https://github.com/diegosouzapw/OmniRoute/pull/11708)) +- **chore(audio):** retire the built-in EdgeTTS provider and its 11-voice catalog while provenance/licensing review remains on HOLD; the generic `/v1/audio/speech` endpoint and the other speech providers remain available ([#11711](https://github.com/diegosouzapw/OmniRoute/pull/11711)) +- **fix(providers):** retire the unofficial Qwen Web integration and its `qwen-web`/`qw` identities while third-party provenance remains on HOLD ([#11713](https://github.com/diegosouzapw/OmniRoute/pull/11713)) — direct requests now fail closed with a sanitized HTTP 410 and combos skip the retired target, while migration guards preserve connection IDs, allowlists, usage/audit history and invalidate active leases. Supported Qwen API, Qwen Code, cloud, token-plan, audio, local and model-name surfaces remain available; restoring Qwen Web requires a qualified provenance/license decision. +- **fix(providers):** retire `microsoft-designer-web` and its `msdesigner` alias from runtime and catalogs while preserving stored credentials, provider history and allowlists as inactive records — source dependency on gpt4free's [`MicrosoftDesigner.py`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/g4f/Provider/needs_auth/MicrosoftDesigner.py) is proven at snapshot `0943d16eef757a44deb60651c07bbe5fa958cafe`, whose [`LICENSE`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/LICENSE) is GPL-3.0; expressive adaptation is probable, which records provenance/licensing risk without asserting a legal conclusion ([#11720](https://github.com/diegosouzapw/OmniRoute/pull/11720)). +- **docs(legal):** centralize pinned MIT notices for the vendored GCF codec and local flag assets ([#11726](https://github.com/diegosouzapw/OmniRoute/pull/11726)) +- Split the 2,952-line `adobeFireflyClient.ts` into nine focused modules (catalog, credentials, payload, ARP session, upload, responses, IMS, account, poll) behind an unchanged barrel — no consumer edits and no public API change (#11728). — thanks @MumuTW +- **fix(dashboard):** replaced the Model Lockout card's unprovenanced notification MP3 with a short, locally synthesized Web Audio chime ([#11731](https://github.com/diegosouzapw/OmniRoute/pull/11731)) — both toggles retain optional audible feedback without shipping replacement media or third-party code, and unsupported, suspended, or rejected audio contexts fail open without blocking the setting change. +- **chore(deepseek-web):** remove the provenance-unresolved DeepSeek PoW WASM binary and its runtime loader/tracing while retaining the existing JavaScript solver (slower at high difficulty) ([#11732](https://github.com/diegosouzapw/OmniRoute/pull/11732)). +- **chore(assets):** remove four unused provider assets whose redistribution provenance could not be verified while retaining the active Freebuff SVG ([#11735](https://github.com/diegosouzapw/OmniRoute/pull/11735)) +- **docs(assets):** retired two raster images whose redistribution provenance could not be verified ([#11736](https://github.com/diegosouzapw/OmniRoute/pull/11736)) — PicoClaw documentation now reuses the existing SVG, while Zoo Code remains a text link without an image. +- **docs(legal):** add pinned notices for six LobeHub-derived provider SVGs and 65 byte-exact theSVG assets, retaining registry claims as unverified metadata with explicit trademark and HOLD boundaries ([#11737](https://github.com/diegosouzapw/OmniRoute/pull/11737)). +- **chore(quality):** added a versioned 225/225 provider-asset provenance manifest and blocking drift gate ([#11743](https://github.com/diegosouzapw/OmniRoute/pull/11743)) — the inventory pins magic MIME and SHA-256 for every physical file, records immutable source matches for 71 assets, keeps 69 probable and 85 unresolved entries explicit but non-blocking, and treats provenance as source evidence rather than copyright or trademark clearance. +- **refactor(mitm):** replace four previously derived Traffic Inspector utilities — conversation normalization, SSE merging, secret masking, and header sanitization — with independent clean-room implementations based on public provider schemas, WHATWG SSE, and RFC 6750, while keeping the two independently classified passthrough surfaces unchanged ([#11748](https://github.com/diegosouzapw/OmniRoute/pull/11748)). +- Added a unit test that fails seven days before any dated pack under `config/` (`validUntil` and sibling keys) lapses, naming the file and key. The Alibaba free-tier pack expired on 2026-08-27 and turned every PR red the next morning with no commit involved; renewal now happens on someone's terms, not the clock's. ([#11891](https://github.com/diegosouzapw/OmniRoute/pull/11891)) +- `check:workflows` now fails (under `--strict`/`--ratchet`) when any job routed to a self-hosted runner publishes with `--provenance` — npm rejects that with `422` at the registry, which in v3.8.50 only surfaced after the tag and Docker images were public. ([#11895](https://github.com/diegosouzapw/OmniRoute/pull/11895)) +- `scripts/ops/runner-janitor.sh` now proves a path is idle with one `lsof` snapshot and removes stale leftovers itself (tmpfs after 3 h — it is RAM — disk after 24 h), kills orphan `next-build` processes, prunes checkouts of stopped runners, and alerts on memory pressure; `--dry-run` shows exactly what it would do. `docs/ops/RUNNER_BOX.md` reconciled to the measured box (31 GB, 10 listeners). ([#11893](https://github.com/diegosouzapw/OmniRoute/pull/11893)) +- The `next-build` artefact (1.3 GB) is now written and read under `$RUNNER_TEMP` (per-runner, on disk) instead of `/tmp`, which on the self-hosted pool is a 12 GB tmpfs in RAM. Landing it there took 27–32 of the publish job's 76 minutes, and the fixed `/tmp/e2e-build.tar.gz` name let E2E jobs on different runners overwrite each other's download. ([#11896](https://github.com/diegosouzapw/OmniRoute/pull/11896)) +- The CI `build` job now runs in two concurrency lanes — `main` and pull requests — so a release build is never queued behind (or OOM-killed beside) PR builds on the self-hosted pool, which holds one `next-build` comfortably and two at the edge. ([#11901](https://github.com/diegosouzapw/OmniRoute/pull/11901)) +- Re-freeze the ESLint suppressions on `release/v3.8.51` from a clean-room measurement (2 stale file entries pruned, 55 pre-existing `no-explicit-any` in six new files frozen under #11924) and drop the dead `GPT_SIZE_MAP` constant orphaned by the Adobe Firefly client split, so `No new ESLint warnings` stops failing every PR with exit 2 (Refs #11924) ([#11955](https://github.com/diegosouzapw/OmniRoute/pull/11955)) +- Type the 55 `no-explicit-any` sites that had been frozen under #11924 — four redundant casts in `socksConnectorWithFamily.ts` (undici/socks types already accept them) and the mocks/fixtures of the socks-timeout and isFree suites — and drop their suppression entries; the ESLint ratchet shrinks from 5487 to 5432 (Closes #11924) ([#11975](https://github.com/diegosouzapw/OmniRoute/pull/11975)) +- Take the two hosted-runner builds off the PR rail: `Build App` (`build.yml`) is `workflow_dispatch`-only and `DAST smoke (PR)` runs only for PRs into `main` — the 7 GB hosted VM cannot build `release/v3.8.51` in any profile (19/30 red, VM shutdown ~8 min into `next build`) and both checks had turned into permanent noise on every release PR; the bundle stays validated by `ci.yml` on `main` and by `nightly-release-green` on `release/**` (Closes #11946) ([#11962](https://github.com/diegosouzapw/OmniRoute/pull/11962)) +- Move the four nightly jobs that build the backend (`nightly-schemathesis`, `nightly-llm-security` promptfoo + garak, `nightly-resilience` axe-a11y) off the hosted 7 GB runner — where they died on `release/v3.8.51` unseen — onto the box's new `omni-light` pool (two listeners), and document the reshaped fleet (4 active OmniRoute listeners: 2 `omni-build` + 2 `omni-light`, janitor ceiling 4) (Closes #11965) ([#11967](https://github.com/diegosouzapw/OmniRoute/pull/11967)) +- Stop painting every fork PR into `release/**` red: `quality.yml` `Build (advisory)` is skipped (GitHub still reports `continue-on-error` failures as check FAILURE). Hosted `ubuntu-latest` cannot finish `npm run build` on this tree — same class as #11962 taking `build.yml` off the PR rail. `docker-publish.yml` amd64 now runs on the `.113` `omni-build` pool (31 GB / 32 cores, two listeners) with Turbopack, shares the `heavy-build-main` lane with `ci.yml` `Build` so it queues instead of becoming a third heavy, and keeps per-ref concurrency (a merge storm was starting 8 concurrent OOM builds). arm64 stays on `ubuntu-24.04-arm` with webpack — there is no ARM box. Fallback when `USE_VPS_RUNNER` is off: hosted amd64 + webpack (#11976). ([#12021](https://github.com/diegosouzapw/OmniRoute/pull/12021)) +- `docker-publish.yml` builds with webpack (`OMNIROUTE_USE_TURBOPACK=0`) on **both** arches, including amd64 on the `.113` `omni-build` pool. Turbopack had the RAM (31 GB, `omniroute-113-6`) and still panicked (`TurbopackInternalError: there must be a path to a root`, run 33253576569, 26 min in). The same tree's arm64 webpack build on hosted `ubuntu-24.04-arm` succeeded (run 33264823398). Dockerfile already documents webpack as the Docker escape hatch (#11976). ([#12050](https://github.com/diegosouzapw/OmniRoute/pull/12050)) +- **docs(free-tier):** declare the counting vs deciding regimes for "is it free?" and guard the deciding path from DB-backed catalog resolution ([#12226](https://github.com/diegosouzapw/OmniRoute/pull/12226)) — thanks @maxmad64bis +- **docs(auto-combo):** The mode pack table in `docs/routing/AUTO-COMBO.md` now lists all six shipped packs with every weight each one sets, replacing a four-pack table whose numbers had also drifted from the source. It states plainly that no pack sets `quality`, so selecting any pack silences the observed-quality signal. Six more documents that quote the scoring factor count joined the `check:docs-counts` gate, which caught five stale claims — including one naming nine factors that do not exist — and two stale mode pack counts. The dashboard routing panel, which offered four of the six packs and labelled the default strategy "6-Factor Scoring", is now covered by a test; the two packs it was missing are `reliability-first` and `chaos-mode`, the latter labelled as the fault-injection profile it is rather than as one more routing preference ([#12316](https://github.com/diegosouzapw/OmniRoute/pull/12316)) — thanks @maxmad64bis +- **chore(quality):** rebaseline `src/lib/db/apiKeys.ts` for the ACL the key-creation path now preserves ([#12352](https://github.com/diegosouzapw/OmniRoute/pull/12352)) ([#12673](https://github.com/diegosouzapw/OmniRoute/pull/12673)) — thanks @KrzysiekSko +- **chore(providers):** bump the Claude Code wire identity and the Devin bridge image pin from `2.1.220` to `2.1.258` ([#12402](https://github.com/diegosouzapw/OmniRoute/pull/12402)) — thanks @ggiak ([#12604](https://github.com/diegosouzapw/OmniRoute/pull/12604)) +- **docs(env):** align `.env.example`, the README Bun section, and the troubleshooting guide with the code: `OMNIROUTE_USE_TURBOPACK` also governs `npm run build` (not dev-only), `bun run build` follows that flag instead of auto-selecting Webpack, `OMNIROUTE_CHAT_MAX_HEAVY_IN_FLIGHT` is unset by default (no request-count cap), and the structural `503 chat_admission_busy` message matches `chatAdmissionResponses.ts` (#12404 — thanks @pacocartones) +- **chore(electron):** upgrade the desktop app to Electron 44 (Chromium 152, Node 24.18.1) ([#12217](https://github.com/diegosouzapw/OmniRoute/pull/12217)). **Requires macOS 13 (Ventura) or later** — Chromium dropped macOS 12 (Monterey), so Monterey users must stay on an earlier OmniRoute desktop build. Windows and Linux are unaffected; the app already shipped only x64/arm64, so Electron 44 dropping 32-bit builds changes nothing. Removes the `openAsHidden`/`wasOpenedAsHidden` login-item fields deleted in Electron 44 — hidden autostart continues to work through the `--hidden` argument registered with the login item ([#12554](https://github.com/diegosouzapw/OmniRoute/pull/12554)) — thanks @app/dependabot +- **chore(quality):** rebaseline `src/sse/handlers/chat.ts` for the effective-input persistence the continuation fix needs ([#12641](https://github.com/diegosouzapw/OmniRoute/pull/12641)) ([#12680](https://github.com/diegosouzapw/OmniRoute/pull/12680)) — thanks @hartmark +- `Coverage` job on `ci.yml`: the informational Codecov upload gets its own 5-minute ceiling and `continue-on-error`, and the job budget grows from 20 to 30 minutes (the 8-shard c8 merge alone takes ~10) — a stalled upload no longer ends the job `cancelled` and drags a fully green `main` run's conclusion down with it ([#11972](https://github.com/diegosouzapw/OmniRoute/pull/11972)) +- CI hardening on the PR rail: the four `Unit Tests fast-path` shards get `timeout-minutes: 30` (a hung shard held a PR for 64 min instead of GitHub's 6 h default) and both ESLint file caches lose their `restore-keys` fallback, so a cache built under another suppressions file or lint config can no longer report stale verdicts (Refs #11600, #11924) ([#11963](https://github.com/diegosouzapw/OmniRoute/pull/11963)) +- Every CI job that runs a `next build` (`build`, the npm `publish`, both release-green validations) now targets the `omni-build` runner label, which only two of the eight self-hosted runners carry. The box holds one build comfortably and two at the edge; a third now queues on GitHub instead of being OOM-killed by the kernel. ([#11932](https://github.com/diegosouzapw/OmniRoute/pull/11932)) +- **refactor(combo):** move `handleRoundRobinCombo` (and `resolveTargetTokenLimit`) into `open-sse/services/combo/roundRobinCombo.ts`. `combo.ts` drops from 2164 to 1014 lines (`split("\n").length`); the leaf is 1199 (under the 1200 new-file cap). The round-robin call site uses a dynamic `import()` so `releaseStickyPinOnFailure` / `clearStaleLKGP` can stay exported from `combo.ts` without a static cycle. Skip / sticky / semaphore / safety-timer behavior is unchanged. ([#12811](https://github.com/diegosouzapw/OmniRoute/pull/12811)) — thanks @HouMinXi +- **chore(quality):** rebaseline the file-size caps the error-boundary campaign grew past (`open-sse/executors/codex.ts`, `open-sse/vendor/codex-chatgpt-web/bridge.ts`, both via [#12444](https://github.com/diegosouzapw/OmniRoute/pull/12444)) ([#12654](https://github.com/diegosouzapw/OmniRoute/pull/12654)) +- **chore(quality):** rebaseline the file-size caps the hartmark batch grew past (`combos/page.tsx` via [#12355](https://github.com/diegosouzapw/OmniRoute/pull/12355), `open-sse/services/combo.ts` via [#12338](https://github.com/diegosouzapw/OmniRoute/pull/12338)) ([#12623](https://github.com/diegosouzapw/OmniRoute/pull/12623)) — thanks @hartmark +- **chore(quality):** rebaseline the file-size caps the HouMinXi batch grew past when its PRs stacked (`providers/page.tsx`, `chatCore.ts`, `accountFallback.ts`) — each PR measured correctly in isolation, none saw the stacking ([#12619](https://github.com/diegosouzapw/OmniRoute/pull/12619)) +- **chore(quality):** rebaseline `open-sse/services/combo.ts` for the reset-aware scoring the HouMinXi batch stacked ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) ([#12678](https://github.com/diegosouzapw/OmniRoute/pull/12678)) — thanks @HouMinXi +- **docs:** sync provider catalog count (direct commit `4f11b2ae3d`) +- **docs(skills):** sync omni-inference search fallback (direct commit `b4e76a7ed9`) +- **chore(lint):** prune two stale base suppressions (direct commit `1325047d56`) +- **test(auto-combo):** tolerate full-suite rotation load (direct commit `ad3e293f9f`) +- **test:** drain inherited quality snapshot reds (direct commit `21ed68d8ac`) +- **chore(quality):** rebaseline measured CLI bundle growth (direct commit `d35b3f9779`) +- **test(ui):** reproduce invalid AnySearch icon alias (direct commit `12cc6ca834`) +- **test(translator):** fix the relative imports of the relocated deferred-finish test (direct commit `777d9d1629`) +- **docs(counts):** bring the documented provider and migration counts to the measured values (direct commit `e70bea30e9`) +- **test(security):** drop the guard-target set left unused by the sync (direct commit `a32ba0e362`) +- **chore(db):** renumber model_capabilities migration 163 → 169 (cross-branch collision with 163_radar_feed_cache_generated_at) (direct commit `70f5d4cbf9`) +- **docs(cli):** document CLI_PRIME_AGENT_BIN and correct the CLI Agents count (direct commit `9058e39b61`) +- **chore(deps):** refresh runtimes and adopt ESLint 10 ([#11259](https://github.com/diegosouzapw/OmniRoute/pull/11259)) — thanks @backryun +- **refactor(local-corpus):** implement dynamic root resolution and LRU cache ([#11491](https://github.com/diegosouzapw/OmniRoute/pull/11491)) — thanks @raheemuddin786 +- **chore(quality):** prune stale eslint-suppressions entries ([#11596](https://github.com/diegosouzapw/OmniRoute/pull/11596)) +- **chore(ci):** unblock quality gates - env-doc sync + stryker tap.testFiles registration (#9282) ([#11609](https://github.com/diegosouzapw/OmniRoute/pull/11609)) — thanks @Meet6338-X +- **chore(ci):** point the circular-deps gate at dpdm's real JS entrypoint ([#11615](https://github.com/diegosouzapw/OmniRoute/pull/11615)) — thanks @benzntech +- **chore(quality):** rebaseline file-size for #11448/#11495/#11561 growth ([#11630](https://github.com/diegosouzapw/OmniRoute/pull/11630)) +- **deps:** realign onnxruntime-node pin to @huggingface/transformers' 1.24.3 lockstep ([#11633](https://github.com/diegosouzapw/OmniRoute/pull/11633)) — thanks @jonlwheat2-gif +- **test(autoCombo):** port models_dev_tier #11508 guard from node:test to vitest ([#11635](https://github.com/diegosouzapw/OmniRoute/pull/11635)) — thanks @jonlwheat2-gif +- **chore(quality):** rebaseline file-size for #11083/#11631/#11628 growth ([#11636](https://github.com/diegosouzapw/OmniRoute/pull/11636)) +- **chore(lint):** prune stale ESLint suppressions (base-red #11449) ([#11644](https://github.com/diegosouzapw/OmniRoute/pull/11644)) — thanks @jonlwheat2-gif +- **docs(changelog):** aggregate the ten v3.8.50 fragments into CHANGELOG.md ([#11683](https://github.com/diegosouzapw/OmniRoute/pull/11683)) +- **chore(search):** drop unreachable credential sweep after #11642 ([#11687](https://github.com/diegosouzapw/OmniRoute/pull/11687)) — thanks @HouMinXi +- **ci:** add API route TypeScript regression gate ([#11705](https://github.com/diegosouzapw/OmniRoute/pull/11705)) — thanks @Sabeekhann +- **docs(dedup):** correct the CodeQL claim on the tenant-namespace comment ([#11712](https://github.com/diegosouzapw/OmniRoute/pull/11712)) +- **docs(changelog):** add consolidated v3.8.50 stats, top-25 ranking and a .mailmap ([#11715](https://github.com/diegosouzapw/OmniRoute/pull/11715)) +- **docs:** recommend gstack for AI-assisted workflows ([#11770](https://github.com/diegosouzapw/OmniRoute/pull/11770)) — thanks @santosraju99-hub +- **chore(quality):** tighten complexity/cognitive-complexity ratchets to the current tip; land the missed gateways.ts rebaseline ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **docs(i18n):** finish Freepik → Magnific rebrand in locale strings and README ([#11772](https://github.com/diegosouzapw/OmniRoute/pull/11772)) — thanks @turbolego +- **refactor(auto-combo):** fix divergent scoring in combo health reporting ([#11854](https://github.com/diegosouzapw/OmniRoute/pull/11854)) — thanks @jacobsparts +- **chore(changelog):** add missing fragments for #11919/#11918/#11916 ([#11938](https://github.com/diegosouzapw/OmniRoute/pull/11938)) +- **test(check):** escape the runs-on fixture with JSON.stringify, not a quote-only replace ([#11942](https://github.com/diegosouzapw/OmniRoute/pull/11942)) (direct commit `fb7445eaa3`) +- **test(release):** align five suites with the contracts #11933, #11919 and #11876 shipped on release/v3.8.51 ([#11944](https://github.com/diegosouzapw/OmniRoute/pull/11944)) +- **chore(quality):** remove unreachable code and restore test discovery ([#11950](https://github.com/diegosouzapw/OmniRoute/pull/11950)) — thanks @backryun +- **docs(agents):** sync-back landings are fast-forward, never squash ([#11964](https://github.com/diegosouzapw/OmniRoute/pull/11964)) +- **test(infra):** retry recursive temp-dir removal instead of failing a shard on ENOTEMPTY (#11966) ([#11968](https://github.com/diegosouzapw/OmniRoute/pull/11968)) +- **docs(ops):** the .113 heavy-build ceiling is one runner, not two ([#12048](https://github.com/diegosouzapw/OmniRoute/pull/12048)) +- **test(ui):** correct inactive auto-fetch expectation ([#12098](https://github.com/diegosouzapw/OmniRoute/pull/12098)) — thanks @RaviTharuma +- **docs:** document native dependency check escape hatch ([#12101](https://github.com/diegosouzapw/OmniRoute/pull/12101)) — thanks @RaviTharuma +- **test(providers):** regenerate the translate-path golden for OrcaRouter (#11923) ([#12118](https://github.com/diegosouzapw/OmniRoute/pull/12118)) +- **chore(quality):** velocity phase — loosen every numeric baseline by 20% until v4.0, monitor headroom nightly ([#12125](https://github.com/diegosouzapw/OmniRoute/pull/12125)) +- **chore(lint):** batch 0 of #12146 — type the call-log-cap sqlite rows instead of 45 as-any casts ([#12157](https://github.com/diegosouzapw/OmniRoute/pull/12157)) +- **chore(lint):** batch 4 of #12146 — shared/components react-hooks violations resolved ([#12159](https://github.com/diegosouzapw/OmniRoute/pull/12159)) +- **chore(lint):** batch 1 of #12146 — dashboard/cli-code react-hooks violations resolved ([#12160](https://github.com/diegosouzapw/OmniRoute/pull/12160)) +- **chore(lint):** batch 3 of #12146 — dashboard/settings react-hooks violations resolved ([#12162](https://github.com/diegosouzapw/OmniRoute/pull/12162)) +- **chore(lint):** batch 2 of #12146 — resolve the react-hooks compiler violations in dashboard/providers ([#12163](https://github.com/diegosouzapw/OmniRoute/pull/12163)) +- **chore(quality):** register search-432 cooldown test in stryker tap.testFiles ([#12170](https://github.com/diegosouzapw/OmniRoute/pull/12170)) +- **chore(lint):** batch 5 of #12146 — combos, endpoint, provider-stats, api-manager and costs react-hooks violations resolved ([#12174](https://github.com/diegosouzapw/OmniRoute/pull/12174)) +- **build:** add contributor fast profile ([#12192](https://github.com/diegosouzapw/OmniRoute/pull/12192)) — thanks @rafacpti23 +- **docs(readme):** README touch-ups ([#12193](https://github.com/diegosouzapw/OmniRoute/pull/12193)) ([#12194](https://github.com/diegosouzapw/OmniRoute/pull/12194)) ([#12202](https://github.com/diegosouzapw/OmniRoute/pull/12202)) — thanks @rafacpti23 +- **build:** force the webpack bundler in the contributor build profile ([#12197](https://github.com/diegosouzapw/OmniRoute/pull/12197)) — thanks @rafacpti23 +- **build:** skip standalone packaging in the contributor profile ([#12198](https://github.com/diegosouzapw/OmniRoute/pull/12198)) — thanks @rafacpti23 +- **docs(audit):** align every published number with the code and harden check:docs-counts ([#12200](https://github.com/diegosouzapw/OmniRoute/pull/12200)) +- **build:** stub the instrumentation entrypoints in the contributor profile ([#12203](https://github.com/diegosouzapw/OmniRoute/pull/12203)) — thanks @rafacpti23 +- **build:** omit the standalone output target for contributor builds ([#12204](https://github.com/diegosouzapw/OmniRoute/pull/12204)) — thanks @rafacpti23 +- **chore(lint):** batch 6 of #12146 — memory, radar, audit, analytics, cache, usage, activity, home and RequestLoggerV2 react-hooks violations resolved ([#12208](https://github.com/diegosouzapw/OmniRoute/pull/12208)) +- **docs(diagrams):** rename number-carrying diagram files to stable names ([#12210](https://github.com/diegosouzapw/OmniRoute/pull/12210)) +- **docs(dashboard):** redraw onboarding tier-flow SVGs for the real 4-tier model ([#12211](https://github.com/diegosouzapw/OmniRoute/pull/12211)) +- **docs(api):** document every implemented route in openapi.yaml (276 → 692 paths) ([#12212](https://github.com/diegosouzapw/OmniRoute/pull/12212)) +- **chore(lint):** batch 7 of #12146 — final src tail: 37 react-hooks violations across 33 files resolved ([#12244](https://github.com/diegosouzapw/OmniRoute/pull/12244)) +- Revert "docs: recommend gstack for AI-assisted workflows (#11770)" ([#12248](https://github.com/diegosouzapw/OmniRoute/pull/12248)) ([#12249](https://github.com/diegosouzapw/OmniRoute/pull/12249)) +- **docs:** Chaos Mode setup guide + weighted strategy semantics ([#12250](https://github.com/diegosouzapw/OmniRoute/pull/12250)) +- **docs(agents):** protected-surface merge rule — operator approval for agent-instruction files ([#12253](https://github.com/diegosouzapw/OmniRoute/pull/12253)) +- **chore(quality):** register native-codex turn-pin tests in stryker tap.testFiles ([#12263](https://github.com/diegosouzapw/OmniRoute/pull/12263)) +- **chore(quality):** dedupe tap.testFiles entries added by racing base-red fixes ([#12265](https://github.com/diegosouzapw/OmniRoute/pull/12265)) +- **docs(readme):** introduce OmniRouteTray — the macOS menu-bar companion ([#12276](https://github.com/diegosouzapw/OmniRoute/pull/12276)) — thanks @ggiak +- **chore(proxy):** purge the legacy 1proxy residue — sync/rotator modules, dead DB exports, dead settings tab and flag, docs (#12091) ([#12290](https://github.com/diegosouzapw/OmniRoute/pull/12290)) +- **chore(quality):** baseline-headroom skips generated and vendored files in the fileSize worst-file signal ([#12291](https://github.com/diegosouzapw/OmniRoute/pull/12291)) +- **refactor(video):** unify the JPEG frame data-URI contract ([#12322](https://github.com/diegosouzapw/OmniRoute/pull/12322)) +- **chore(deps):** freeze onnxruntime-node and eslint-plugin-react-hooks out of dependabot groups ([#12329](https://github.com/diegosouzapw/OmniRoute/pull/12329)) +- **test(executors):** restore the #10986 reasoning-only fallback guards ([#12364](https://github.com/diegosouzapw/OmniRoute/pull/12364)) — thanks @pacocartones +- **chore(quality):** re-tighten the file-size ratchet to the real LOC (plan 3.8.52 task 0) ([#12411](https://github.com/diegosouzapw/OmniRoute/pull/12411)) +- **test(grok):** format web executor suite ([#12412](https://github.com/diegosouzapw/OmniRoute/pull/12412)) +- **chore(quality):** ratchet open-sse typecheck baseline to zero ([#12418](https://github.com/diegosouzapw/OmniRoute/pull/12418)) +- **test(build):** guard the artifact path policy arrays against duplicates ([#12422](https://github.com/diegosouzapw/OmniRoute/pull/12422)) +- **chore(lint):** adopt eslint-plugin-react-hooks 7.1.1 ([#12428](https://github.com/diegosouzapw/OmniRoute/pull/12428)) +- **chore(providers):** remove a keyless provider integration at its operator's request ([#12440](https://github.com/diegosouzapw/OmniRoute/pull/12440)) +- **docs(providers):** register providers removed at their operator's request and guard against reintroduction ([#12478](https://github.com/diegosouzapw/OmniRoute/pull/12478)) +- **chore(quality):** base fixes — stryker tap.testFiles + node_modules cache key ([#12482](https://github.com/diegosouzapw/OmniRoute/pull/12482)) +- **docs(arch):** one-process recipe for tens of long /v1/responses ([#12493](https://github.com/diegosouzapw/OmniRoute/pull/12493)) — thanks @RaviTharuma +- **chore(quality):** tighten the CodeQL ratchet baseline from 11 to 6 ([#12530](https://github.com/diegosouzapw/OmniRoute/pull/12530)) +- **chore(deps):** pin browserslist override to ^4.28.8 ([#12592](https://github.com/diegosouzapw/OmniRoute/pull/12592)) — thanks @KooshaPari +- **chore(deps):** bump fast-uri to 3.1.7 in the electron lockfile ([#12601](https://github.com/diegosouzapw/OmniRoute/pull/12601)) +- **docs(quality):** document how the CodeQL ratchet refreshes and how to tighten it ([#12611](https://github.com/diegosouzapw/OmniRoute/pull/12611)) +- **test(ci):** pin the openapi-security-tiers two-arm contract with an executing gate test (#12581) ([#12652](https://github.com/diegosouzapw/OmniRoute/pull/12652)) +- **docs(gamification):** align XP Rewards table with code (#12501) ([#12667](https://github.com/diegosouzapw/OmniRoute/pull/12667)) — thanks @KooshaPari +- **refactor(combo):** split executeTarget into gates, attempt, and loop ([#12746](https://github.com/diegosouzapw/OmniRoute/pull/12746)) — thanks @HouMinXi +- **docs(security):** document socket.yml scanner config + CI workflow link (#12575) ([#12764](https://github.com/diegosouzapw/OmniRoute/pull/12764)) — thanks @KooshaPari +- **chore(ci):** guard commit identity in pre-commit to stop author misattribution ([#12772](https://github.com/diegosouzapw/OmniRoute/pull/12772)) +- **deps:** 17 Dependabot bumps — bump electron from 43.4.0 to 43.4.1 in /electron ([#11426](https://github.com/diegosouzapw/OmniRoute/pull/11426)); bump the development group across 1 directory with 8 updates ([#11428](https://github.com/diegosouzapw/OmniRoute/pull/11428)); bump the production group across 1 directory with 11 updates ([#11440](https://github.com/diegosouzapw/OmniRoute/pull/11440)); chore(deps): bump github/codeql-action/init from 4.37.7 to 4.37.8 ([#11925](https://github.com/diegosouzapw/OmniRoute/pull/11925)); chore(deps): bump github/codeql-action from 4.37.7 to 4.37.8 ([#11926](https://github.com/diegosouzapw/OmniRoute/pull/11926)); chore(deps): bump github/codeql-action/analyze from 4.37.7 to 4.37.8 ([#11927](https://github.com/diegosouzapw/OmniRoute/pull/11927)); build(deps): bump github/codeql-action/init from 4.37.8 to 4.37.9 ([#12345](https://github.com/diegosouzapw/OmniRoute/pull/12345)); build(deps): bump github/codeql-action/analyze from 4.37.8 to 4.37.9 ([#12346](https://github.com/diegosouzapw/OmniRoute/pull/12346)); bump the development group across 1 directory with 2 updates ([#12347](https://github.com/diegosouzapw/OmniRoute/pull/12347)); build(deps): bump github/codeql-action from 4.37.8 to 4.37.9 ([#12349](https://github.com/diegosouzapw/OmniRoute/pull/12349)); bump browserslist from 4.28.2 to 4.28.8 ([#12396](https://github.com/diegosouzapw/OmniRoute/pull/12396)); bump the production group across 1 directory with 4 updates ([#12399](https://github.com/diegosouzapw/OmniRoute/pull/12399)); bump @xmldom/xmldom ([#12500](https://github.com/diegosouzapw/OmniRoute/pull/12500)); bump qs from 6.15.2 to 6.16.0 ([#12512](https://github.com/diegosouzapw/OmniRoute/pull/12512)); bump @xmldom/xmldom from 0.9.10 to 0.9.12 ([#12513](https://github.com/diegosouzapw/OmniRoute/pull/12513)); bump fast-uri from 3.1.5 to 3.1.7 ([#12514](https://github.com/diegosouzapw/OmniRoute/pull/12514)); bump @humanfs/node from 0.16.7 to 0.16.8 ([#12515](https://github.com/diegosouzapw/OmniRoute/pull/12515)) +- Clear the `release/v3.8.51` `check:agent-skills-sync` base-red: regenerate `skills/cli-tunnel/SKILL.md` so the `tunnel create [type]` positional that #13009 taught the generator to read is reflected in the committed skill. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` typecheck base-red from #12731: the new `Mode pack ranking gates` candidates in `open-sse/services/autoCombo/__tests__/autoCombo.test.ts` omitted the required `provider`, `model` and `errorRate` fields of `ProviderCandidate`, failing `check:open-sse-typecheck` (and with it `Fast Quality Gates`) on every open PR. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` docs-sync base-red: EURouter (#13025) and GreenPT (#13024) took the live provider count to 358, leaving 7 STRICT drifts (`PROVIDER_REFERENCE.md`, 4 diagrams, 2 tier-flow images) plus the `AGENTS.md` / `llm.txt` / `package.json` count claims stale. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- **test(models):** Pin `CATALOG_BUILD_TIMEOUT_MS` in the #12058 canonical-catalog regression so a cold catalog build racing the 8s default bound no longer flakes the suite with a 500 ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Allow `@omniroute/opencode-plugin-v2/` in the npm pack policy — [#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870) shipped the v2 plugin beside its v1 sibling without widening the allowlist, so every packed file under it read as an unexpected artifact and failed `check:pack-policy` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Point the release-green validator's pack gate at the tree it is validating — the build-provenance guard defaulted to `origin/main`, which a release branch tip can never be an ancestor of mid-cycle, so `Package artifact` reported a false red on every run and the tarball boot-smoke cascaded off it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **docs(checks):** keep doc counts honest — headings, rankings, catalog, weights, quality gate and scoring diagram now covered ([#12507](https://github.com/diegosouzapw/OmniRoute/pull/12507)) — thanks @maxmad64bis +- **chore(lifecycle):** `check:model-lifecycle` now also diffs `DEFAULT_DEGRADATION_MAP` (the background-task redirect table) against the vendor lifecycle snapshot, refusing a retired id as source or target, with a table-driven unit test beside it. Three rows whose source the vendor had retired — `claude-sonnet-4-20250514`, `gemini-3-pro-preview` and `gpt-5.1-codex` (whose target `gpt-5.1-codex-mini` is retired too) — were dead code, since `checkLifecycle` answers 410 before the redirect runs; they are dropped (#12535 — thanks @pacocartones) +- **test(compression):** cover idle worker eviction at the resource level — the pool must call `terminate()` and must not retain the worker's `MessagePort`, complementing the `exit`-event assertion added with the fix ([#13371](https://github.com/diegosouzapw/OmniRoute/pull/13371)) +- **chore(quality):** raise the `imageGeneration.ts` file-size ceiling for the image-only-model guard that clears the #12945 base-red ([#13392](https://github.com/diegosouzapw/OmniRoute/pull/13392)) +- **chore(quality):** type the combo-test route's JSON response bodies instead of casting them to `any`, drop the now-empty suppression entry, and remove the `hasPerModelQuota` import `chatCore.ts` stopped using when the failure-classification helper was extracted ([#13379](https://github.com/diegosouzapw/OmniRoute/pull/13379)) +- **chore(electron):** the desktop bundle no longer ships the root-level files of every translated docs mirror (`README.md`, `llm.txt`, `CLAUDE.md`, `GEMINI.md`, `CONTRIBUTING.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`) — the packaged app only reads `docs/i18n//docs/**`, which stays. Saves ~11 MB on top of the translated CHANGELOGs already pruned. (#0000) ([#13235](https://github.com/diegosouzapw/OmniRoute/pull/13235)) +- **chore(quality):** retighten the `src/sse/handlers/chat.ts` unused-vars suppression count to the 8 violations that actually remain ([#13385](https://github.com/diegosouzapw/OmniRoute/pull/13385)) +- Bring the documented counts back in line with the code: 171 migrations (was 169) and 20 routing strategies (was 19), plus the regenerated `cli-tunnel` skill reference. ([#12970](https://github.com/diegosouzapw/OmniRoute/pull/12970)) +- Update the documented migration count to 172 after the call-logs provider-stats indexes landed. ([#13209](https://github.com/diegosouzapw/OmniRoute/pull/13209)) +- **chore(ci):** two gates that close the blind spots behind the exclusions above. `check:vitest-exclusions` requires every Vitest exclusion to name a tracking issue and to appear in `config/quality/vitest-exclusions.json` — the previous list grew to 62 files behind a comment pointing at an issue that had been closed for a month. `check-new-key-coverage` requires a key newly added to `en.json` to reach every locale; the existing coverage gate is a percentage floor per locale, so eleven absent keys out of ~13,000 left it at 99.9% while a whole feature shipped untranslated in nine languages. Both are diff-aware, so pre-existing debt stays frozen and neither needed a migration to turn on. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **build:** root layout no longer loads Inter from `next/font/google`, so a production image build does not need fonts.googleapis.com ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **docs(i18n):** bring the eight new `llm.txt` mirrors (km, kn, ml, my, ne, or, pa, si) up to the current provider and migration counts, so `check-docs-sync` stops rejecting every commit ([#13674](https://github.com/diegosouzapw/OmniRoute/pull/13674)) +- **refactor(ui):** flow surfaces (home topology, combo live studio, compression cockpit/waterfall/nodes, token-health badge) express state through the theme-aware `--orch-status-*` tokens instead of fixed dark-mode hex, so green/red/amber/grey stay legible in the light theme; dark mode is byte-identical. Categorical palettes (routing-strategy hues, compression-layer pills, provider brand colors) deliberately stay hex ([#12378](https://github.com/diegosouzapw/OmniRoute/issues/12378)) ([#13203](https://github.com/diegosouzapw/OmniRoute/pull/13203)) +- **chore(tests):** 51 test files that had been excluded from Vitest are running again, restoring roughly 350 assertions to the blocking suite. Each was measured individually first: of the 62 files parked behind the `// #8618 — pre-existing failure` comment, 51 pass against the current tree with no source change, so the exclusions had outlived the failures they were added for. The 11 that genuinely still fail stay excluded, but now point at a live tracker (#13204) rather than at #8618, which was closed in August while the list it tracked kept growing. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **chore(quality):** drop two ESLint suppression entries whose violations no longer exist, so `eslint --suppressions-location` stops rejecting every commit that touches the surrounding files ([#13384](https://github.com/diegosouzapw/OmniRoute/pull/13384)) +- **chore(release):** bring main's post-tag fixes into release/v3.8.51 — sync-back step 2/2 (direct commit `21c488f210`) +- **chore(deps):** patch toml and esbuild in the opencode plugin lockfiles ([#12965](https://github.com/diegosouzapw/OmniRoute/pull/12965)) +- **docs(changelog):** reconcile the v3.8.51 living section — fold 366 fragments, cover every cycle commit, credit every contributor ([#12971](https://github.com/diegosouzapw/OmniRoute/pull/12971)) +- **test(plugin):** wait for the v2 lazy-refresh tier instead of sleeping past it ([#12984](https://github.com/diegosouzapw/OmniRoute/pull/12984)) +- **chore(quality):** rebaseline chatCore.ts after the non-streaming regression fixes ([#13045](https://github.com/diegosouzapw/OmniRoute/pull/13045)) +- **docs(changelog):** add the missing bullet marker to the DeepSeek PoW fragment ([#13200](https://github.com/diegosouzapw/OmniRoute/pull/13200)) +- **chore(deps):** drain the Dependabot queue — 10 of 13 alerts ([#13213](https://github.com/diegosouzapw/OmniRoute/pull/13213)) +- **chore(ci):** make worktrees carry the hooks that enforce the identity gate ([#13236](https://github.com/diegosouzapw/OmniRoute/pull/13236)) +- **chore(deps):** pin csv-parse to ^7.0.2 via overrides (#13117) ([#13300](https://github.com/diegosouzapw/OmniRoute/pull/13300)) — thanks @KooshaPari +- **chore(deps):** bump hono from ^4.12.34 to ^4.13.7 (#13148) ([#13301](https://github.com/diegosouzapw/OmniRoute/pull/13301)) — thanks @KooshaPari +- **chore(deps):** pin joi to ^18.2.8 via overrides (#13085) ([#13302](https://github.com/diegosouzapw/OmniRoute/pull/13302)) — thanks @KooshaPari +- **docs(providers):** add Microsoft 365 Copilot (BizChat) provider guide (#12779) ([#13340](https://github.com/diegosouzapw/OmniRoute/pull/13340)) — thanks @KooshaPari +### 🙌 Contributors + +Thanks to everyone whose work landed in v3.8.51: + +| Contributor | PRs / Issues | +| --- | --- | +| [@5dive-bot](https://github.com/5dive-bot) | #11852 | +| [@abhisheksharma2411](https://github.com/abhisheksharma2411) | #11547, #11684, #11959, #12209 | +| [@adevwithpurpose](https://github.com/adevwithpurpose) | #11464 | +| [@adivekar-utexas](https://github.com/adivekar-utexas) | #12015, #12043, #12090, #12138 | +| [@AIB1TAL0S](https://github.com/AIB1TAL0S) | #12103 | +| [@alltomatos](https://github.com/alltomatos) | #11448 | +| [@alvinveroy](https://github.com/alvinveroy) | #11857, #12027, #12165 | +| [@amaleta](https://github.com/amaleta) | #11906, #11934 | +| [@amartinawi](https://github.com/amartinawi) | #11575 | +| [@AndrianBalanescu](https://github.com/AndrianBalanescu) | #11083, #11830, #11834, #11844 | +| [@anhtahaylove](https://github.com/anhtahaylove) | #13091, #13092, #13093, #13096, #13097, #13100, #13106, #13109, #13114, #13155, #13166, #13171, #13175, #13187, #13196 | +| [@aniruddhaadak80](https://github.com/aniruddhaadak80) | #11760 | +| [@app](https://github.com/app) | #12554 | +| [@arminanton](https://github.com/arminanton) | #11461, #11513 | +| [@AStupidBear](https://github.com/AStupidBear) | #11584 | +| [@atescivitci-cmd](https://github.com/atescivitci-cmd) | #12105 | +| [@b3nw](https://github.com/b3nw) | #11970, #11971, #12147 | +| [@backryun](https://github.com/backryun) | #11259, #11950, #12075, #12076, #12078, #12079, #12081, #12082, #12181, #12228, #12239, #12255, #12258, #12277, #12367, #12423, #12524, #12538 | +| [@benjaminkitt](https://github.com/benjaminkitt) | #11747 | +| [@benzntech](https://github.com/benzntech) | #11614, #11615 | +| [@Bl0ck154](https://github.com/Bl0ck154) | #11948, #11951, #11952, #11953, #11954 | +| [@brick30llc-ctrl](https://github.com/brick30llc-ctrl) | #11752, #12242, #12262 | +| [@Chewji9875](https://github.com/Chewji9875) | #11455, #11875, #12028 | +| [@cheynetom](https://github.com/cheynetom) | #12058 | +| [@davidebaraldo](https://github.com/davidebaraldo) | #12222, #12304 | +| [@davidlinfr](https://github.com/davidlinfr) | #6390 | +| [@ddarkr](https://github.com/ddarkr) | #12124 | +| [@Deftera186](https://github.com/Deftera186) | #11809 | +| [@dpozimski](https://github.com/dpozimski) | #11945 | +| [@DW-MediaLab](https://github.com/DW-MediaLab) | #12229 | +| [@echel0nn](https://github.com/echel0nn) | #11923 | +| [@f9td56dbgh-hub](https://github.com/f9td56dbgh-hub) | #11660 | +| [@fabioluissilva](https://github.com/fabioluissilva) | #11991 | +| [@foreveryh](https://github.com/foreveryh) | #12177 | +| [@geek007git](https://github.com/geek007git) | #12115, #12116, #12117, #12120, #12122 | +| [@geekyNads](https://github.com/geekyNads) | #11784, #12266 | +| [@ggiak](https://github.com/ggiak) | #11561, #12276, #12350, #12402 | +| [@gonisulaimann](https://github.com/gonisulaimann) | #12368, #12369, #12371 | +| [@Gorillaz322](https://github.com/Gorillaz322) | #12207 | +| [@groovecityJO](https://github.com/groovecityJO) | #12682 | +| [@hartmark](https://github.com/hartmark) | #11434, #11452, #11473, #11499, #11703, #11983, #11984, #11985, #11986, #11988, #11989, #11990, #11994, #12221, #12293, #12445, #12446, #12447, #12448, #12460, #12461, #12623, #12646, #12650, #12680, #12717, #12718, #12727, #12741, #12854 | +| [@hizzt](https://github.com/hizzt) | #11894 | +| [@hongnoul](https://github.com/hongnoul) | #11484 | +| [@HouMinXi](https://github.com/HouMinXi) | #11411, #11414, #11512, #11518, #11520, #11641, #11642, #11643, #11687, #11779, #11849, #11850, #11851, #11915, #11916, #11918, #11919, #11920, #12013, #12017, #12033, #12042, #12106, #12139, #12166, #12169, #12171, #12205, #12213, #12312, #12325, #12487, #12488, #12495, #12504, #12557, #12566, #12590, #12591, #12624, #12626, #12632, #12637, #12678, #12696, #12697, #12711, #12733, #12746, #12767, #12770, #12789, #12803, #12805, #12811, #12866, #12868, #12899, #12926, #12934, #12950, #12951, #12974, #13001, #13006, #13011, #13017, #13026, #13027, #13034, #13035, #13038, #13042, #13050, #13060, #13061, #13069, #13107, #13120, #13136, #13195, #13197 | +| [@Hsia97](https://github.com/Hsia97) | #11624 | +| [@jacobsparts](https://github.com/jacobsparts) | #11854, #12155, #12167 | +| [@joglomedia](https://github.com/joglomedia) | #11980 | +| [@jonlwheat2-gif](https://github.com/jonlwheat2-gif) | #11443, #11450, #11549, #11567, #11608, #11633, #11635, #11640, #11644, #11671, #11672, #11673, #11674, #11675, #11676, #11677, #11706, #11783, #12051, #12052, #12053, #12055 | +| [@kanade-hoshino](https://github.com/kanade-hoshino) | #12180 | +| [@Karan825](https://github.com/Karan825) | #11861 | +| [@KaspaPulse](https://github.com/KaspaPulse) | #11389, #11469, #11628, #11666, #11910 | +| [@keeltrace](https://github.com/keeltrace) | #12080, #12223 | +| [@killer30001000](https://github.com/killer30001000) | #12178 | +| [@KooshaPari](https://github.com/KooshaPari) | #12470, #12592, #12667, #12699, #12703, #12706, #12764, #12769, #12771, #13300, #13301, #13302, #13340, #13401, #13403, #13404, #13405, #13406, #13407, #13409, #13410, #13411, #13412, #13413, #13414, #13418, #13424, #13427, #13433, #13523, #13524, #13525, #13528, #13531, #13532, #13534, #13539, #13543, #13545, #13546, #13547, #13550, #13553 | +| [@kriptoburak](https://github.com/kriptoburak) | #11370 | +| [@KrzysiekSko](https://github.com/KrzysiekSko) | #12425, #12432, #12673 | +| [@marcelokarval](https://github.com/marcelokarval) | direct commit / report | +| [@maxmad64bis](https://github.com/maxmad64bis) | #1622, #11435, #11437, #11441, #11537, #11550, #11553, #11555, #11557, #11812, #11842, #11843, #11903, #12151, #12214, #12215, #12218, #12226, #12314, #12316, #12317, #12318, #12319, #12320, #12321, #12507, #12715, #12731, #12744, #12786, #12787, #12788, #12790, #12792, #12794, #12795, #12828, #12832, #12853, #12857, #12870, #12937, #12941, #12975, #13141, #13142, #13146, #13147 | +| [@mdigitalbh81](https://github.com/mdigitalbh81) | #12240 | +| [@Meet6338-X](https://github.com/Meet6338-X) | #11598, #11609, #12110 | +| [@morpheus9393](https://github.com/morpheus9393) | #11943 | +| [@MumuTW](https://github.com/MumuTW) | #11492, #11502, #11506, #11507, #11626, #11675, #11685, #11728, #11746, #11888, #11889, #11890, #11892 | +| [@Neuron-Mr-White](https://github.com/Neuron-Mr-White) | #11622, #11801, #12256 | +| [@NightStalker-87](https://github.com/NightStalker-87) | #12183 | +| [@NoxzRCW](https://github.com/NoxzRCW) | #11879, #11880, #11881, #11882, #11883, #11935 | +| [@ntdat812](https://github.com/ntdat812) | #11585, #12095 | +| [@ntdatt812](https://github.com/ntdatt812) | #11368, #11573, #11574, #11576, #11577, #11580, #11582, #11583, #11588, #11589, #11590, #11591, #11592, #11593, #11672, #12177, #12180, #12873, #12918, #12920, #12921, #12925, #12930, #13007, #13009, #13024, #13025, #13083, #13087, #13101, #13104, #13110 | +| [@official-burak](https://github.com/official-burak) | #11542 | +| [@opensource-elearning](https://github.com/opensource-elearning) | #12179, #12189, #12278, #12286 | +| [@oyi77](https://github.com/oyi77) | #11408, #11409, #11421, #11505, #11677, #12036, #12110 | +| [@pacocartones](https://github.com/pacocartones) | #11521, #11522, #11527, #11528, #11529, #11530, #11531, #11532, #11533, #11534, #11595, #11599, #11603, #11604, #11605, #11607, #11610, #11676, #11714, #11716, #11718, #11767, #11838, #11860, #11862, #11869, #11871, #11872, #11873, #11903, #11906, #11921, #11934, #12356, #12358, #12359, #12360, #12361, #12362, #12364, #12365, #12373, #12374, #12375, #12376, #12377, #12379, #12380, #12381, #12386, #12387, #12389, #12390, #12394, #12395, #12397, #12401, #12403, #12404, #12522, #12523, #12535, #12536, #12540, #12541, #12543, #12545, #12548, #12549, #12550, #12551, #12552, #12647, #12651, #12653, #13055 | +| [@patrykkopycinski](https://github.com/patrykkopycinski) | #11936, #11937, #12224 | +| [@PauloFH](https://github.com/PauloFH) | #11509 | +| [@PauloHSOliveira](https://github.com/PauloHSOliveira) | #12241 | +| [@ponkcore](https://github.com/ponkcore) | #12054 | +| [@Prajeeth-12](https://github.com/Prajeeth-12) | #11634, #12046 | +| [@quiterunner-commits](https://github.com/quiterunner-commits) | #12131, #12143 | +| [@rafacpti23](https://github.com/rafacpti23) | #11554, #11558, #12192, #12197, #12198, #12202, #12203, #12204 | +| [@ragnar-claude](https://github.com/ragnar-claude) | #11564 | +| [@raheemuddin786](https://github.com/raheemuddin786) | #11491, #11839, #11840, #11841, #12003, #12230, #12231, #12232, #12233, #12234 | +| [@rahilmavani](https://github.com/rahilmavani) | #11761 | +| [@Rahulsharma0810](https://github.com/Rahulsharma0810) | #11771 | +| [@RaviTharuma](https://github.com/RaviTharuma) | #11710, #11727, #11797, #11798, #11802, #11805, #11806, #11811, #12098, #12099, #12101, #12449, #12452, #12472, #12473, #12493, #12533, #12607, #12628, #12631, #12636, #12875, #12876, #12880, #12882, #12884 | +| [@rezjalibd](https://github.com/rezjalibd) | #12186 | +| [@RhianB14](https://github.com/RhianB14) | #11692 | +| [@rifqiawl](https://github.com/rifqiawl) | #11517, #11519, #11969 | +| [@rqzbeh](https://github.com/rqzbeh) | #11390 | +| [@Sabeekhann](https://github.com/Sabeekhann) | #11699, #11705 | +| [@santosraju99-hub](https://github.com/santosraju99-hub) | #11755, #11770 | +| [@Seramicx](https://github.com/Seramicx) | #11568 | +| [@Siva010](https://github.com/Siva010) | #12191 | +| [@solstxce](https://github.com/solstxce) | #11597 | +| [@soroush5](https://github.com/soroush5) | #12691 | +| [@tenshiak](https://github.com/tenshiak) | #12279 | +| [@TheDemonTuan](https://github.com/TheDemonTuan) | #11468, #11470, #11471, #11482, #11548, #11758, #11775, #11814 | +| [@thomasmaerz](https://github.com/thomasmaerz) | #12834 | +| [@tuandinh0801](https://github.com/tuandinh0801) | #11454 | +| [@turbolego](https://github.com/turbolego) | #11621, #11762, #11772, #11774, #11781, #12216 | +| [@Tushar49](https://github.com/Tushar49) | #11392 | +| [@ujjawalkaushik1110](https://github.com/ujjawalkaushik1110) | #11863 | +| [@vermasomesh835](https://github.com/vermasomesh835) | #11794 | +| [@vsd2807](https://github.com/vsd2807) | #11565, #11619 | +| [@wahidsadik371-coder](https://github.com/wahidsadik371-coder) | #12126 | +| [@watchingdogs](https://github.com/watchingdogs) | #12031 | +| [@wildcard](https://github.com/wildcard) | #11620 | +| [@xiaoyaner0201](https://github.com/xiaoyaner0201) | #11460, #11662, #12083 | +| [@Xxx91n](https://github.com/Xxx91n) | #11690 | +| [@yourspraveen](https://github.com/yourspraveen) | #11146 | +| [@yxyxy](https://github.com/yxyxy) | #11671 | +| [@ZaimMarzuki](https://github.com/ZaimMarzuki) | #11960 | +| [@Zartharas](https://github.com/Zartharas) | #11340 | +| [@zero-executioner](https://github.com/zero-executioner) | #11631 | +| [@diegosouzapw](https://github.com/diegosouzapw) | maintainer | + +--- ## [3.8.50] — 2026-08-25 _Living section — regenerated 2026-08-12 from all cycle commits (cycle open `ed2db6cb19` → tip). Bullets carry the merged PR and its author; direct pushes listed separately._ diff --git a/docs/i18n/sl/CHANGELOG.md b/docs/i18n/sl/CHANGELOG.md index 2e5ab4fdd7..bf50a52362 100644 --- a/docs/i18n/sl/CHANGELOG.md +++ b/docs/i18n/sl/CHANGELOG.md @@ -95,16 +95,1119 @@ ## [3.8.51] — TBD -_Living section — cycle opened at the v3.8.50 freeze (parallel-cycle model). Bullets are aggregated from `changelog.d/` fragments at each `/generate-release` phase._ +_Living section — reconciled 2026-09-15 from all cycle commits (`091589089c` → `c0f92ec98a`, 916 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 | **123** | +| 📝 Commits in the cycle | **916** | +| 🔀 Pull requests referenced | **892** | +| 📋 Changelog entries | **919** | +| 🙌 Contributors credited in entries | **121** | +| 🤖 Automated dependency commits | 18 | + +**Entries by type** + +| Type | Count | +| --- | ---: | +| 🐛 Fixes | 592 | +| ✨ Features | 131 | +| 🧹 Chore | 93 | +| 📚 Docs | 42 | +| 🧪 Tests | 32 | +| ♻️ Refactor | 8 | +| 🏗️ Build | 7 | +| ⚡ Performance | 7 | +| ⚙️ CI | 2 | +| ⏪ Reverts | 2 | +| 📦 Dependencies | 2 | +| 🔒 Security | 1 | + +### 🏆 Top 25 contributors this cycle + +_By commits in `091589089c..c0f92ec98a`, author identities consolidated via `.mailmap` and the merged PR's GitHub login. Bots excluded._ + +| # | Contributor | Commits | +| ---: | --- | ---: | +| 🥇 | diegosouzapw | 323 | +| 🥈 | Bob.Hou (@HouMinXi) | 79 | +| 🥉 | Paco Cartones (@pacocartones) | 77 | +| 4 | Dizzle (@maxmad64bis) | 49 | +| 5 | Koosha Paridehpour (@KooshaPari) | 43 | +| 6 | Nguyen Thanh Dat (@ntdatt812) | 31 | +| 7 | Ravi Tharuma (@RaviTharuma) | 26 | +| 8 | Markus Hartung (@hartmark) | 22 | +| 9 | Webman (@jonlwheat2-gif) | 22 | +| 10 | backryun | 17 | +| 11 | anhtahaylove | 15 | +| 12 | Rafa Martins (@rafacpti23) | 10 | +| 13 | Syed Raheemuddin (@raheemuddin786) | 10 | +| 14 | Nguyễn Viết Tuấn (@TheDemonTuan) | 8 | +| 15 | MumuTW | 7 | +| 16 | Paijo (@oyi77) | 7 | +| 17 | Tobias Andersen (@turbolego) | 6 | +| 18 | Bl0ck (@Bl0ck154) | 5 | +| 19 | SHANMUGAPRIYAN (@geek007git) | 5 | +| 20 | KaspaPulse | 5 | +| 21 | Abhishek Sharma (@abhisheksharma2411) | 4 | +| 22 | Abhishek Divekar (@adivekar-utexas) | 4 | +| 23 | Andrew B. (@AndrianBalanescu) | 4 | +| 24 | NoxzRCW | 4 | +| 25 | opensource-elearning | 4 | ### ✨ New Features +- **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). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- Added Google AI Studio Gemini batch text-to-speech support through `POST /v1/audio/speech`. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- 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. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(routing):** subscription-first auto groupings — `auto/subscription` routes only through plan-included connections with a documented hard-stop overage and fails closed on exhaustion, while `auto/thrifty` orders the pool `subscription → keyless → free → cheap → premium` and steps up one rung at a time as each is exhausted. Billing class comes from a curated per-connection catalog (uncurated is treated as metered, never plan-included), both reuse STRICT_ZERO_COST's per-connection verification, and a quota reading whose `resetAt` has passed is now refreshed regardless of TTL so routing returns to plan capacity as soon as it resets ([#11146](https://github.com/diegosouzapw/OmniRoute/pull/11146)) — thanks @yourspraveen +- **feat(providers):** publish a management-authenticated versioned web-session credential contract from OmniRoute's canonical browser credential metadata ([#11340](https://github.com/diegosouzapw/OmniRoute/pull/11340)) — thanks @Zartharas +- **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)) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add Xquik X search with typed results, credential validation, REST routing, and MCP selection ([#11370](https://github.com/diegosouzapw/OmniRoute/pull/11370)) — thanks @kriptoburak +- **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)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(dashboard):** surface durable exclusive managed leases in the existing Sessions view, keeping leased clients visible across idle gaps while marking connections with in-flight work as active ([#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389)) — thanks @KaspaPulse +- **build(bun):** allow Turbopack bundler flag on Bun 1.4+ with configurable Webpack fallback ([#11471](https://github.com/diegosouzapw/OmniRoute/pull/11471)) — thanks @TheDemonTuan +- feat(api): add an opt-in `modelVisibilityAllowlist`/`modelVisibilityDenylist` settings pair to curate exactly which models `/v1/models` advertises, mirrored into every `auto/*` combo candidate pool so a denied model cannot be routed to via combo selection either (#11481) ([#11997](https://github.com/diegosouzapw/OmniRoute/pull/11997)) +- **feat(rankings):** the Free Provider Rankings page shows what each provider actually served over the last 24 h. It ranked by ELO alone, which left a provider that answers every call with an error in first place; the usage data was already served by the API but never requested. A provider with too small a sample shows a dash, not a number ([#11546](https://github.com/diegosouzapw/OmniRoute/pull/11546)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **feat(guardrails):** enforce a bounded, deterministic contract for Video Bridge transcripts — 256 cues, 4096 input code units and 4 KiB UTF-8 per cue, 64 KiB total text, malformed-Unicode rejection, focus-window scoping, cross-source reconciliation with contributing-source metadata, and a structural provenance trust boundary so caller JSON can never self-assert `embedded`/`audio-bridge` provenance ([#11652](https://github.com/diegosouzapw/OmniRoute/issues/11652)) ([#12009](https://github.com/diegosouzapw/OmniRoute/pull/12009)) +- **feat(video):** orchestrate optional Video Bridge audio extraction and Audio Bridge STT behind a dual opt-in (operator setting AND per-request signal) — a new loopback-only broker `mode=audio` operation shares the frame path's exact process queue, deadline, AbortSignal, and byte budgets to extract a bounded mono 16 kHz PCM WAV from the same already-downloaded video, then reuses the existing Audio Bridge transcription boundary; provider segment timing is preserved when available and marked coarse otherwise, and every failure degrades to a visual-only-safe partial instead of throwing (#11654). ([#12012](https://github.com/diegosouzapw/OmniRoute/pull/12012)) +- Add a tenant-bound Video Bridge drill-down lifecycle on top of the existing secure cache substrate: opaque hashed handles (never raw session/video identifiers), preview/standard/detail multiresolution variants resampled on read, response pagination capped at 8 frames and 32 MiB, and a new authenticated `/api/v1/video-bridge/drilldown` consumer route that stays disabled for remote access by default and denies cross-key access with the same response as a nonexistent handle (no existence oracle). ([#12006](https://github.com/diegosouzapw/OmniRoute/pull/12006)) +- **test(video):** Add the Video Bridge FU-07/FU-09 promotion-evidence harness (#11656) — a frozen Zod manifest schema covering the 8 required scenario kinds (static scenes, rapid cuts, late facts, fades, blur, small text, close events, visual prompt injection) with a minimum of 3 repetitions per case, deterministic declarative fixture recipes (`videoBridgePromotionFixtures.ts`), a pure medians/p95 metrics aggregator, a pure FU-07/FU-09 promotion-verdict evaluator applying the ticket's exact thresholds (missing token usage always holds), a digest-only persistence layer that never retains raw media or raw model responses, and a versioned per-model promotion allowlist shipped empty with every model defaulting to `hold`. The FU-07/FU-09 promotion verdicts themselves remain HOLD — they require a real evidence run against real models on VPS 192.168.0.15. ([#12008](https://github.com/diegosouzapw/OmniRoute/pull/12008)) +- **feat(video bridge):** "embedded" transcript provenance can now be legitimately earned instead of merely asserted — a bounded, allowlisted (`mov_text`/`subrip`/`webvtt`) subtitle probe runs through the loopback-only Video Bridge broker (at most 2 streams, 10s subdeadline bounded by the request deadline, 256 KiB output, 4096-code-unit lines), normalized through a bounded, ReDoS-safe WebVTT parser and Zod-validated end to end. The adapter always resolves to an explicit `success`/`absent`/`transient_failure` outcome — a subtitle failure never breaks the visual description path, and only a fingerprint-verified broker response (never a caller-declared label) can produce embedded cues (#11659). ([#12011](https://github.com/diegosouzapw/OmniRoute/pull/12011)) +- Default new Antigravity-family connections (agy CLI imports and Antigravity OAuth connects) to model auto-sync, so live model discovery lands in the synced catalog and `/v1/models` picks up freshly released upstream models (e.g. Gemini 3.7 Flash tiers) without code changes. Existing connections keep their current setting; the per-connection dashboard toggle remains the opt-out. (#11685 — thanks @MumuTW) ([#11887](https://github.com/diegosouzapw/OmniRoute/pull/11887)) +- **feat(ui):** enable React Compiler (`reactCompiler: true` + `babel-plugin-react-compiler`) for automatic memoization at build time ([#11783](https://github.com/diegosouzapw/OmniRoute/pull/11783)) — thanks @jonlwheat2-gif +- **feat(providers):** add SeekAi (`seekai.cc`) as an OpenAI-compatible New-API gateway — catalog id `seekai` (alias `ska`), `https://seekai.cc/v1`, live `/v1/models` via `passthroughModels`, aggregator-list membership so New-API balance detection can opt in. No referral/aff codes. ([#11786](https://github.com/diegosouzapw/OmniRoute/issues/11786)) ([#12557](https://github.com/diegosouzapw/OmniRoute/pull/12557)) — thanks @HouMinXi +- **feat(zai):** add GLM-5.3-Flash Coding Plan support (1M context, 128K output, vision, `low|high|max` reasoning) and route `zai` GLM-5.3-family API-key traffic through the OpenAI-compatible Coding Plan endpoint with native thinking defaults ([#11801](https://github.com/diegosouzapw/OmniRoute/pull/11801)) — thanks @Neuron-Mr-White +- **feat(combo):** choose how combo models are ordered — manual, provider, score, or name — via a sort control in the dashboard builder, persisted in `config.modelSort` and re-applied on load and after add ([#11812](https://github.com/diegosouzapw/OmniRoute/pull/11812)) — thanks @maxmad64bis +- **feat(free):** custom models can be marked free-tier via `customModels[].isFree`; `isFreeModel()` is the first door and `hidePaidModels` respects it even for providers outside the free budget ([#11843](https://github.com/diegosouzapw/OmniRoute/pull/11843)) — thanks @maxmad64bis +- **feat(nodejs):** add `5dive` as a `configure` target — `omniroute configure 5dive` / `omniroute setup-5dive` write a 5dive auth profile that points an agent fleet's `claude` seats at OmniRoute, with the root-only write, the loopback-vs-`https` endpoint rule and the per-seat model pin handled explicitly ([#11852](https://github.com/diegosouzapw/OmniRoute/pull/11852)) — thanks @5dive-bot +- **feat(sse):** treat `max` as a first-class reasoning-effort tier and clamp per model family (GLM 5.1+/DeepSeek V4+/Kimi K3+ keep native `max`; o1/MiniMax/Grok/Muse Spark clamp to their upstream ceiling) ([#11875](https://github.com/diegosouzapw/OmniRoute/pull/11875)) — thanks @Chewji9875 +- **feat(providers):** the provider plugin manifest now advertises a `usage-fetch` capability for the 40 providers that have a wired usage/quota fetcher, so external dashboards can read it from `GET /api/v1/provider-plugin-manifest` instead of parsing `open-sse/services/usage.ts` after every release. Discovery only — no new fetcher, no quota change, and the Dashboard quota widget stays gated by `USAGE_SUPPORTED_PROVIDERS`. `USAGE_FETCHER_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/fetcherProviders.ts`) and is re-exported from `services/usage.ts`, keeping the manifest module a light leaf instead of pulling the ~490-module usage dispatcher into the manifest route. ([#11903](https://github.com/diegosouzapw/OmniRoute/pull/11903)) — thanks @maxmad64bis / @pacocartones +- **feat(plugins):** `OMNIROUTE_PLUGINS_DIR` sets the directory the runtime plugin scanner reads — and the root the plugin manager installs into — overriding the `HOME`-derived default, so a Docker/K8s deployment can point straight at its bind-mounted plugin tree instead of moving `HOME` just to relocate the scan path. An image that exports no home no longer scans `/tmp/.omniroute/plugins` in silence: the resolved directory is logged once at startup as `scanner.dir_resolved`, naming the input that won. Unset, behaviour is unchanged. Distinct from the CLI-only `OMNIROUTE_PLUGIN_PATH`, which finds `omniroute-cmd-*` command packages and never reached this scanner ([#11906](https://github.com/diegosouzapw/OmniRoute/pull/11906)) — thanks @amaleta / @pacocartones +- **feat(leases):** add an explicit owner-authenticated status action that returns only the active lease's privacy-safe configured connection and provider labels, with generation fencing and no credential or internal-id disclosure ([#11910](https://github.com/diegosouzapw/OmniRoute/pull/11910)) — thanks @KaspaPulse +- **feat(providers):** import-from-file modal shows per-row API errors and ships a downloadable CSV template ([#12071](https://github.com/diegosouzapw/OmniRoute/issues/12071)) ([#12504](https://github.com/diegosouzapw/OmniRoute/pull/12504)) — thanks @HouMinXi +- **feat(providers):** dashboard search matches connection name and `baseUrl` so imported OpenAI-compat nodes surface on the provider card ([#12108](https://github.com/diegosouzapw/OmniRoute/issues/12108)) ([#12495](https://github.com/diegosouzapw/OmniRoute/pull/12495)) — thanks @HouMinXi +- **feat(routing):** add a `score` Auto router strategy that selects the highest configured weighted score and reuses `explorationRate`. ([#12155](https://github.com/diegosouzapw/OmniRoute/pull/12155)) — thanks @jacobsparts +- Add a runtime feature flag to disable universal context handoffs globally without changing the default behavior. ([#12167](https://github.com/diegosouzapw/OmniRoute/pull/12167)) — thanks @jacobsparts +- **feat(providers):** the provider plugin manifest now also advertises a `usage-supported` capability for the 46 providers whose usage API is accepted by the server and Dashboard routes, so integrators can distinguish "the server will serve quota for this provider" from "a fetcher is wired" without reading TypeScript. Discovery only — no fetcher or quota change. `usage-fetch` resolves on id or alias (the usage dispatcher accepts both); `usage-supported` resolves on id alone, matching the runtime guard `USAGE_SUPPORTED_PROVIDERS.includes(providerId)`. `USAGE_SUPPORTED_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/supportedProviders.ts`) and is re-exported from `providers.ts`, mirroring the `fetcherProviders` leaf from #11903 and keeping the manifest a light module. ([#12214](https://github.com/diegosouzapw/OmniRoute/pull/12214)) — thanks @maxmad64bis +- **feat(rankings):** order Free Provider Rankings by what each provider actually served — `GET /api/free-provider-rankings?sortBy=reliability` and a "Most reliable first" toggle on the page. Providers with too few calls to state a success rate keep their score order below the measured ones; the default order is unchanged ([#12218](https://github.com/diegosouzapw/OmniRoute/pull/12218)). — thanks @maxmad64bis +- **perf(sse):** defer `cloneLogPayload()` in the structured SSE collector until after the `maxEvents`/`maxBytes` cap check, eliminating ~9,800 wasted `structuredClone` calls per streaming response (65–71% faster `push()`). Reducer snapshot isolation restored for OpenAI and Responses summaries ([#12241](https://github.com/diegosouzapw/OmniRoute/pull/12241)) — thanks @PauloHSOliveira ([#12243](https://github.com/diegosouzapw/OmniRoute/pull/12243)) +- **feat(usage):** Devin CLI agentic quota (Codeium seat-management GetUserStatus) and OpenRouter key limits plus account credits now surface in Provider Limits ([#12256](https://github.com/diegosouzapw/OmniRoute/pull/12256) — thanks @Neuron-Mr-White) +- **feat(settings):** persist `headroomUrl` through Settings so status/start use the operator URL instead of only `HEADROOM_URL` ([#12306](https://github.com/diegosouzapw/OmniRoute/issues/12306)) ([#12487](https://github.com/diegosouzapw/OmniRoute/pull/12487)) — thanks @HouMinXi +- **feat(auto-combo):** Auto-Combo scoring can now weigh how often a provider/model has actually succeeded. The engine already carried that number on every candidate — 24 hours of usage history behind a ten-sample floor, real-time metrics otherwise — and the scoring function never read it, while the weight table described `stability` as if it did. `reliability` (`1 - failureRate`, with the same field precedence and the same rate-bounding the speed ranking already uses, so a corrupt reading means "nothing observed" rather than "fails every call") is now a declared factor shipping at weight `0`, so routing is unchanged until an operator gives it one, and the `stability` description now matches what that factor computes ([#12317](https://github.com/diegosouzapw/OmniRoute/pull/12317)) — thanks @maxmad64bis +- **feat(routing):** With `freeAccessPolicy: "strict"`, the read-only candidate listing (`GET /v1/auto-combo/{channel}/candidates`) no longer hides the candidates the zero-cost guard excludes — the same read-only transparency the resilience filter already honours (#9133). Each candidate now carries `freeAccessExclusion` saying why it would be kept out, and it tells an exhausted allowance apart from a quota reading that never arrived or went stale, which used to look identical from the outside. Routing is unchanged: the listing reports, it never enforces. The separate `excludeTosAvoid` guard still drops its candidates without a reason; that gap is now documented rather than closed ([#12319](https://github.com/diegosouzapw/OmniRoute/pull/12319)) — thanks @maxmad64bis +- **feat(radar):** The Radar catalog table now shows two facts it was already receiving from the feed and dropping on the floor: the per-model rate limits (requests and tokens, per minute and per day) in a new column, and a badge when a provider's terms state it may train on the prompts you send. A limit of zero renders as zero rather than "rate-only" — for a ceiling those are opposite facts — and a model with no training statement gets no badge, because an absent statement is not a guarantee ([#12320](https://github.com/diegosouzapw/OmniRoute/pull/12320)) — thanks @maxmad64bis +- **feat(radar):** explain Community, single-use, contributor, supporter, recovery, abuse, offers, and privacy rules before either Radar activation action, and remove the superseded fixed-PR grant promise from every UI locale ([#12342](https://github.com/diegosouzapw/OmniRoute/pull/12342)) +- **feat(gamification):** the dashboard Profile page now shows the real daily streak — `/api/gamification/level` returns `streak: { current, longest }` (per key with `apiKeyId`, operator-wide maximum otherwise) and the streak card reads it instead of a hard-coded 0 (#2403) ([#12377](https://github.com/diegosouzapw/OmniRoute/pull/12377)) — thanks @pacocartones +- **feat(gamification):** the dashboard leaderboard now shows each API key's display name under the Name column instead of a truncated key id; `GET /api/gamification/leaderboard` attaches `name` per entry (name only — no key material), while the shared ranking helper and the federation leaderboard stay id-only — thanks @pacocartones ([#12385](https://github.com/diegosouzapw/OmniRoute/pull/12385)) +- **feat(gamification):** enforce the documented 1000 XP/min per-API-key anti-cheat rate limit on the XP award path; over-limit awards are logged and skipped instead of persisted, and the sliding window now matches the timestamp format stored in `xp_audit_log` ([#2403](https://github.com/diegosouzapw/OmniRoute/issues/2403)) ([#12390](https://github.com/diegosouzapw/OmniRoute/pull/12390)) — thanks @pacocartones +- **feat(admin):** localize the gamification anomalies page — the loading state, the Status column and the Suspicious badge now come from the `common` catalog (new `common.suspicious` key propagated to every locale) — add it to the Gamification sidebar group as `gamification-admin` (`/dashboard/gamification/admin`), and expose the loading and empty states as polite `role="status"` live regions (#12401 — thanks @pacocartones) +- **feat(providers):** skip GitHub combo members missing from the live synced catalog, and drop Copilot models that are policy-disabled or hidden from the model picker ([#12473](https://github.com/diegosouzapw/OmniRoute/pull/12473)) — thanks @RaviTharuma +- **feat(i18n):** locale-expansion tooling — `npm run i18n:add-locale` adds a language to config, dashboard, docs mirrors, CLI, README/indexes and site in one command; browser, CLI and cookie detection resolve `uk`, `fil`/`tl`, `zh-Hant` (and the retired legacy `in` → `id`) via config aliases; `config/i18n.json` now ships in the npm package so the published CLI can read it; new real-translation ratio gate (`npm run i18n:check-ratio`, advisory) with a per-locale ratchet baseline; `run-translation --adopt` restores `.i18n-state.json`; `sync-language-bars` generates 🌐 bars from config; `validate_translation.py` loads its allowlist again. Retires the duplicate `in` locale (Indonesian mislabelled as Hindi) — 42 honest locales; saved `NEXT_LOCALE=in` / `OMNIROUTE_LANG=in` keep working. (#12496) +- **feat(api):** Emit gateway-measured `tokens_per_second` (TTFT excluded) on streaming usage and `X-OmniRoute-Tokens-Per-Second` when first-token latency is known ([#12616](https://github.com/diegosouzapw/OmniRoute/issues/12616)) ([#12631](https://github.com/diegosouzapw/OmniRoute/pull/12631)) — thanks @RaviTharuma +- **feat(opencode-plugin): map gateway cost/usage/tok/s onto OpenCode inference payloads** — the official plugin copies `X-OmniRoute-Response-Cost`, token counts, `X-OmniRoute-Tokens-Per-Second` / `usage.tokens_per_second`, TTFT, and the winning `X-OmniRoute-Model` onto the JSON/SSE body OpenCode already consumes. Missing tok/s is left unset (never `tokens / latency`). (#12636) — thanks @RaviTharuma +- **feat(combo):** add combo strategy `quota-weighted`: skip exhausted accounts, then weighted-draw by leftover / in-flight load; existing conversations stay pinned until the account is empty ([#12789](https://github.com/diegosouzapw/OmniRoute/pull/12789)) — thanks @HouMinXi +- **feat(models):** Account live listings become the chat catalog source for Claude, Codex, Copilot, and AGY; public metadata only fills prices on IDs those accounts already list. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **fix(models):** Union `agy` and `antigravity` live catalogs so `agy/gemini-3.8-flash-high` is not rejected after the prefix folds to `antigravity`. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **feat(opencode):** Add `@omniroute/opencode-plugin-v2`, an opencode v2 plugin that gives a v2 install a route to an OmniRoute gateway: it publishes models, combos and auto-combos into the host catalog, refreshes them lazily behind a 300s TTL, and keeps serving the last known catalog from an on-disk snapshot when the gateway is unreachable. The v1 plugin (`@omniroute/opencode-plugin`) is untouched and keeps working as before. The plugin adapts to the host instead of assuming one shape: it reads the shape the host seeds into the catalog draft and publishes the `api` block, the pre-`api` top-level fields, or both. The gateway key comes from the host credential store when one is connected, so no secret needs to sit in a config file; a source the gateway refuses is named in a warning instead of silently degrading the catalog to raw model ids; and tool schemas bound for a Gemini model are stripped of the JSON-Schema keywords Gemini rejects, so tool calling survives that route ([#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870)) — thanks @maxmad64bis +- feat(services): show sanitized CLIProxyAPI account health from its authenticated management API without exposing credentials, file paths, or raw account metadata (#6342) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add AnySearch free web search + URL extract (webFetch) with typed results, credential validation, REST routing, and MCP selection - fallback-only ([#11690](https://github.com/diegosouzapw/OmniRoute/pull/11690)) — thanks @Xxx91n +- **feat(dashboard):** display clamped `[0, 100]%` cached input token ratio in request logs table ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) — thanks @b3nw +- **feat(catalog):** add `OMNIROUTE_DISABLE_THINKING_LEVEL_VARIANTS` feature flag to optionally filter out thinking level variants from model catalog ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) — thanks @b3nw +- **feat(dashboard):** continuously export call logs to external analytics stores. A pluggable destination registry ships the full Logs-tab record set on an hourly `JobRegistry` cron, with a persisted per-destination cursor, batched inserts, a config UI rendered from each destination's own field descriptors, and a REST layer (`/api/log-export/*`) for CRUD, a connection test, and an on-demand run. A destination can opt into `includeBodies` to also ship the request and response payloads shown in the Logs detail pane, including the client and provider views of each call; this is off by default, and payloads inherit the dashboard's PII sanitisation, secret redaction and `noLog` handling. Google BigQuery is the first destination, using a service-account key stored encrypted at rest and streaming inserts keyed by call-log id, into a table that is day-partitioned on `timestamp` and clustered on `api_key_name`, `provider`, `model` and `status`. ([#11945](https://github.com/diegosouzapw/OmniRoute/pull/11945)) — thanks @dpozimski +- **feat(providers):** add MaxAI as a signed, OpenAI-compatible provider serving its 13 paid chat models (GPT-5.6 / Luna / Thinking, Claude 5 Sonnet, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite, Grok 4.1-fast / 4.5, DeepSeek V3.2 / R1, Llama 3.3 70B) through OmniRoute's `/v1` endpoint, with per-request HMAC-SHA1→SM3→AES request signing, live model + context-window discovery from `/models/get_config`, and prompted tool-calling translated to OpenAI `tool_calls` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI vision input — image_url content parts are forwarded inline in `message_content` to the 6 vision-capable models (GPT-5.6 / Luna / Thinking, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite) ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI image generation — 6 image models (gpt-image-1, dall-e-3, flux-1-schnell/dev/pro, sd3-medium) exposed through `POST /v1/images/generations` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI document RAG — inline base64 file/document attachments are uploaded to MaxAI (content-addressed `doc_id`) and attached to the chat via `doc_list` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** browserless MaxAI onboarding — email device-pair login (`/api/providers/[id]/login`) and signed access-token refresh, so a connection can be created and kept fresh without a real browser or Google OAuth ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** per-provider TLS impersonation profile (MaxAI presents a Windows Firefox-150 client fingerprint) so its bot-sensitive endpoints accept OmniRoute traffic ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** add **Nimble** as a web-search and web-fetch provider (`nimble-search`) — `/v1/search` routes to Nimble's search API at `lite` depth (locale, freshness and include/exclude domain filters mapped onto the shared request contract), and `/v1/web/fetch` routes to Nimble Extract, which covers all four fetch formats (`markdown`, `html`, `links`, `screenshot`) from a single call. One API key serves both surfaces. ([#11620](https://github.com/diegosouzapw/OmniRoute/pull/11620)) — thanks @wildcard +- The npm publish is automatic again, through npm Trusted Publishing (OIDC): the hosted `stage-npm` job publishes with a short-lived credential minted from GitHub's id-token — no `NPM_TOKEN`, no 2FA prompt, provenance attached. `publish_mode=staged` (owner approves with 2FA) and `direct` (token) remain available on `workflow_dispatch`. ([#11931](https://github.com/diegosouzapw/OmniRoute/pull/11931)) +- **feat(providers):** Add **Opper** as an API-key gateway provider — EU-hosted AI gateway with 700+ models from 30+ providers behind one OpenAI-compatible API and one key (`OPPER_API_KEY`); model ids use `provider/model` format (e.g. `anthropic/claude-sonnet-4-6`, `openai/gpt-5`); live model catalog at `https://api.opper.ai/v3/compat/models`; entry mirrors `requesty` (same shape, `passthroughModels: true`, no static seed) ([#11629](https://github.com/diegosouzapw/OmniRoute/pull/11629)) +- **feat(dashboard):** the `/dashboard/orchestration` snapshot hook now subscribes to the `agents` WebSocket channel (`agent.task.updated`) instead of `requests` as its refetch trigger, and relaxes its background poll from 5s to 30s while that WS connection is up — falling back to the tighter 5s cadence, reprogrammed live on any connect/disconnect transition, whenever the socket is down. ([#12409](https://github.com/diegosouzapw/OmniRoute/pull/12409)) +- **feat(dashboard):** Orchestration canvas quick wins — search box plus state/source/provider filter chips with a one-click clear, and per-source collapse/expand, all reflected in the URL so a filtered/collapsed view is shareable and survives a refresh; the detail drawer gained a "copy trace JSON" action and hardened error/empty-state and accessibility handling; the Agents-tab edges now animate traveling particles along active (running) connections; and the canvas node/edge status colors moved off fixed hex values onto theme-aware `--orch-status-*` CSS custom properties, so they adapt correctly to light/dark mode. ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- **chore(dashboard):** Orchestration UI hardening pass and the missing component/model test coverage it called for — `OrchestratorNode`/`ActivityNode`/`OverflowNode` rendering, the `?node=`/overflow-click page routing, the Agents-tab orchestrator-click no-op and `showCompleted` toggle, and the overview kanban's done-column sort order (#12270, #12271). ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- New `/dashboard/orchestration` page: live unified view of everything running — Cloud Agent, A2A and Conductor as a real-time graph (Agents tab), the combo cascade (Routing tab, reusing the Combo Live Studio) and a state kanban (Overview tab), with a detail drawer (trace, cost, approve/cancel). Read-only over existing APIs — no new backend. Canvas concept credit: PR #11815 design ([#12261](https://github.com/diegosouzapw/OmniRoute/pull/12261)) +- **feat(dashboard):** new "History" tab on `/dashboard/orchestration` — an Airflow-style grid of finished runs over a 24h/7d/30d preset window, one row per (source, identity), clicking a cell opens the existing detail drawer. It is backed by real persistence: A2A task lifecycle transitions are now written to the `a2a_tasks` table (purged after 30 days, configurable via `OMNIROUTE_A2A_HISTORY_RETENTION_DAYS`) and served by the new `GET /api/a2a/tasks/history` listing endpoint, with the task-detail route falling back to persisted history once a run leaves the in-memory snapshot. Conductor runs stay remote and are not persisted locally — the tab says so instead of silently omitting them. ([#12479](https://github.com/diegosouzapw/OmniRoute/pull/12479)) +- **feat(dashboard):** the orchestration detail drawer gained a "Repeat" action for Cloud Agent, A2A and Conductor tasks — a two-click confirm (click once to arm, click again within the confirm window to fire) re-submits the original prompt/input as a new run. The button is disabled with an explanatory tooltip whenever the original input can't be recovered from the loaded task detail (e.g. it never carried a prompt, or the detail failed to load). Two limitations of the A2A variant, by design: it targets the `/a2a` JSON-RPC endpoint, which authenticates with an API key only (`REQUIRE_API_KEY=true` or a configured `OMNIROUTE_API_KEY` makes a dashboard-session repeat answer `HTTP 400` — surfaced verbatim in the drawer's error line, never as a success), and the `message/send` call is SYNCHRONOUS: the POST blocks for the whole skill run, so the success confirmation only appears once the repeated task finishes. A dashboard-authenticated A2A creation path is deliberately left to a follow-up — widening the endpoint's auth posture is an operator decision, not a side effect of this feature. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(a2a):** A2A task execution now records which memories were consulted for the task's last user message as `metadata.memoryHits` (id/key/type/content-snippet) plus a `memory_hits` history event, purely for observability — the retrieved memory is never injected into a skill's prompt or behavior. Gated by the `OMNIROUTE_A2A_MEMORY_HITS` kill-switch (default enabled; set to `0` to skip the recall lookup entirely). The drawer's new "Memory used" section lists these hits for a2a tasks and is omitted whenever there are none. Known limitation: recall only resolves under the keyless posture — a keyed caller's task owner is a SHA-256 prefix of the API key, while memory rows are keyed by the database api-key id, and no hash→id lookup exists today, so the hit list stays empty for keyed callers. The recorded hits are also kept out of the task's own `input` (and therefore out of the persisted input and of the "Repeat" request body), so repeating a task never re-sends the previous run's memory snippets. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(providers):** add a Perplexity Agent API provider (`perplexity-agent` / `pplx-agent`) for Perplexity `/v1/responses`, including the documented Anthropic, OpenAI, Google, xAI, DeepSeek, Z.AI, Moonshot/Kimi, NVIDIA, and Perplexity model IDs plus Anthropic-model `max_output_tokens` compatibility. ([#12103](https://github.com/diegosouzapw/OmniRoute/pull/12103)) — thanks @AIB1TAL0S +- **feat(providers):** add RPD (Requests Per Day) limit to provider rate limit overrides across UI, schemas, DB, and i18n ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) — thanks @b3nw +- **feat(providers): add UC Direct (uncensored.com Developer API), the metered OpenAI-compatible surface.** A standard OpenAI-compatible passthrough (default executor) for uncensored.com's official REST API at `https://api.uncensored.com/api/v1`: `X-api-key` auth (never-expiring `uai_sk_live_` key), `POST /chat/completions` with streaming SSE and native tool-calling, and the full live metered catalog (82 models across 15 providers, discovered from the public `GET /v1/models`). Registered as provider `uc-direct` (alias `ucd`). Complements the un-metered `uc` persona provider — same models, metered credits and a plain API key instead of a subscription session. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(providers): add UC (uncensored.com), the un-metered subscription "persona" chat as an OpenAI-compatible provider.** A WebSocket web-app port: a durable Clerk credential mints a short-lived session token per connect (browserless — no API key), driving UC's persona socket. Ships the browserless email-code login (request → verify → harvest), the 19 verified persona models (Claude Opus, Gemini, Grok, GLM, Kimi, DeepSeek, MiniMax, incl. the uncensored variants), prompted `` tool-calling with a per-model code-style dialect + auto-cure retry for guardrailed models, live ``/reasoning split, streaming + non-streaming OpenAI responses, and full quota/auth error surfacing (paywall / message-limit / rate-limit → 429, invalid session → 401 re-login). Full multimodal parity via the persona blob-upload layer: **vision** (image input, 15 vision-capable models), **document RAG** (PDF/doc upload, server-side extraction), **image generation** (22 models), **video generation** (14 models, async signed-url → poll), and **TTS** (streaming MP3). Registered as provider `uc` (alias `ucn`). The metered OpenAI-compatible Developer API is a separate `uc-direct` provider. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(logs):** add copy-all button to request detail modal ([#11083](https://github.com/diegosouzapw/OmniRoute/pull/11083)) — thanks @AndrianBalanescu +- **feat(check):** make serviceKinds required and add the reverse-walk provider consistency gate ([#11392](https://github.com/diegosouzapw/OmniRoute/pull/11392)) — thanks @Tushar49 +- **feat(providers):** only advertise verified-working connections by default ([#11448](https://github.com/diegosouzapw/OmniRoute/pull/11448)) — thanks @alltomatos +- **feat(resilience):** add hierarchical concurrency admission ([#11493](https://github.com/diegosouzapw/OmniRoute/pull/11493)) +- **feat(providers):** derive + surface expiry for JWT-bearing web cookies (#11497) ([#11505](https://github.com/diegosouzapw/OmniRoute/pull/11505)) — thanks @oyi77 +- **feat(tunnels):** support named cloudflared tunnels via CLOUDFLARED_CONFIG ([#11561](https://github.com/diegosouzapw/OmniRoute/pull/11561)) — thanks @ggiak +- **feat(compression):** make proactive context-compression threshold a live setting ([#11564](https://github.com/diegosouzapw/OmniRoute/pull/11564)) — thanks @ragnar-claude +- **feat(resilience):** add provider peak-hour protection ([#11622](https://github.com/diegosouzapw/OmniRoute/pull/11622)) — thanks @Neuron-Mr-White +- **feat(providers):** add 1min.ai provider ([#11631](https://github.com/diegosouzapw/OmniRoute/pull/11631)) — thanks @zero-executioner +- **feat(dashboard):** collapsible JSON tree viewer for request/response payloads ([#11703](https://github.com/diegosouzapw/OmniRoute/pull/11703)) — thanks @hartmark +- **feat(routing):** add LiquidAI LFM2.5-2.6B free tier via OpenRouter ([#11752](https://github.com/diegosouzapw/OmniRoute/pull/11752)) — thanks @brick30llc-ctrl +- **feat(pricing):** add GLM-5.3-Flash pricing, model specs, and catalog registration ([#11830](https://github.com/diegosouzapw/OmniRoute/pull/11830)) — thanks @AndrianBalanescu +- **feat(routing):** add relayMode for schema-locked context handoffs ([#11839](https://github.com/diegosouzapw/OmniRoute/pull/11839)) — thanks @raheemuddin786 +- **feat(config):** add support for runtime system prompt configuration and hot-reloading ([#11841](https://github.com/diegosouzapw/OmniRoute/pull/11841)) — thanks @raheemuddin786 +- **feat(routing):** expand connection-aware quota prefilter across combo strategies (#11682) ([#11850](https://github.com/diegosouzapw/OmniRoute/pull/11850)) — thanks @HouMinXi +- **feat(models):** reactive discovery sync after Antigravity model-not-found 404 ([#11888](https://github.com/diegosouzapw/OmniRoute/pull/11888)) — thanks @MumuTW +- **feat(models):** auto-aliases and token limits from synced Antigravity discovery ([#11889](https://github.com/diegosouzapw/OmniRoute/pull/11889)) — thanks @MumuTW +- **feat(settings):** raise sticky round-robin limit caps to 1000 ([#12015](https://github.com/diegosouzapw/OmniRoute/pull/12015)) — thanks @adivekar-utexas +- **feat(resilience):** operator-configurable global credential health check interval ([#12043](https://github.com/diegosouzapw/OmniRoute/pull/12043)) — thanks @adivekar-utexas +- **feat(quota):** use official OpenCode Go usage API ([#12124](https://github.com/diegosouzapw/OmniRoute/pull/12124)) — thanks @ddarkr +- **feat(resilience):** default the credential health check sweep to 60 minutes ([#12138](https://github.com/diegosouzapw/OmniRoute/pull/12138)) — thanks @adivekar-utexas +- **feat(quality):** new-code mode for the complexity and dead-code ratchets (Clean as You Code) ([#12142](https://github.com/diegosouzapw/OmniRoute/pull/12142)) +- **feat(dashboard):** orchestration canvas — unified model + snapshot hook (part 1/2) ([#12156](https://github.com/diegosouzapw/OmniRoute/pull/12156)) +- **feat(usage):** add Kilo Code balance and Kilo Pass quotas ([#12178](https://github.com/diegosouzapw/OmniRoute/pull/12178)) — thanks @killer30001000 +- **feat(providers):** refresh vendored ChatGPT Web connector to v4.0.7 ([#12181](https://github.com/diegosouzapw/OmniRoute/pull/12181)) — thanks @backryun +- **feat(providers):** manual "Clear cooldown" action in the cooling panel ([#12224](https://github.com/diegosouzapw/OmniRoute/pull/12224)) — thanks @patrykkopycinski +- **feat(providers):** restore ChatGPT Web via clean-room browser transport ([#12239](https://github.com/diegosouzapw/OmniRoute/pull/12239)) — thanks @backryun +- **feat(sse):** wire the PROVIDER_PROFILES window gate into the global provider cooldown ([#12247](https://github.com/diegosouzapw/OmniRoute/pull/12247)) +- **feat(providers):** modernize CLOVA Studio chat and embeddings ([#12277](https://github.com/diegosouzapw/OmniRoute/pull/12277)) — thanks @backryun +- **feat(browser):** adopt Obscura as primary headless browser engine with Chromium fallback ([#12286](https://github.com/diegosouzapw/OmniRoute/pull/12286)) — thanks @opensource-elearning +- **feat(sse):** full language parity for output styles (es/de/fr/it/ru/zh + autoDetect) ([#12289](https://github.com/diegosouzapw/OmniRoute/pull/12289)) +- **feat(quality):** complete test:scoped — --full map rebuild, stdin selection, CI loader parity (#8084 D1) ([#12353](https://github.com/diegosouzapw/OmniRoute/pull/12353)) +- **feat(providers):** refresh Fable, Cursor, and Devin catalogs ([#12367](https://github.com/diegosouzapw/OmniRoute/pull/12367)) — thanks @backryun +- **feat(video):** redact transcript text from logs and durable memory (#12150 P1) ([#12427](https://github.com/diegosouzapw/OmniRoute/pull/12427)) +- **feat(dashboard):** parent-link, genuine-continuation badge, and modal perf fixes ([#12448](https://github.com/diegosouzapw/OmniRoute/pull/12448)) — thanks @hartmark +- **feat(dashboard):** adaptive context-budget dial on compression panel ([#12488](https://github.com/diegosouzapw/OmniRoute/pull/12488)) — thanks @HouMinXi +- **feat(providers):** refresh Z.ai Web models and browser transport ([#12524](https://github.com/diegosouzapw/OmniRoute/pull/12524)) — thanks @backryun +- **feat(video):** redact raw client-snapshot transcript fields in the detailed log (#12150 P2) ([#12528](https://github.com/diegosouzapw/OmniRoute/pull/12528)) +- **feat(providers):** refresh NVIDIA hosted models ([#12538](https://github.com/diegosouzapw/OmniRoute/pull/12538)) — thanks @backryun +- **feat(quota):** Moonshot Open Platform balance and TPD lock for custom nodes ([#12590](https://github.com/diegosouzapw/OmniRoute/pull/12590)) — thanks @HouMinXi +- **feat(video):** redact transcript in the in-memory pending-request snapshot (#12430 item 6) ([#12596](https://github.com/diegosouzapw/OmniRoute/pull/12596)) +- **feat(dashboard):** link a log entry's Conversation Context to its owning conversation ([#12646](https://github.com/diegosouzapw/OmniRoute/pull/12646)) — thanks @hartmark +- **feat(catalog):** eligibility-gated free-tier bucket ([#12669](https://github.com/diegosouzapw/OmniRoute/pull/12669)) +- **feat(db):** fail-closed previous_response_id continuation for redacted video turns (#12150 P2b) ([#12707](https://github.com/diegosouzapw/OmniRoute/pull/12707)) +- **feat(gamification): pay the documented `streak_bonus` and `badge_unlock` XP rewards.** `XP_REWARDS` listed both rewards but the award pipeline never paid them: the private reward table in `events.ts` omitted them, `updateStreak()` did not report when a streak extended, and badge unlocks carried no XP. Every request that extends a daily streak now pays `streak_bonus × streak length` once per UTC day (guarded by a same-day `xp_audit_log` check), and every badge unlocked through the pipeline pays `badge_unlock` once per badge (guarded by the `user_badges` primary key; `unlockBadge()` now reports whether it inserted). Bonus XP flows through the same `addXp` + level sync + global/weekly/monthly leaderboard path as action XP, so level-ups and rankings include it. The Radar supporter recognition unlock stays XP-free. (#12522 — thanks @pacocartones) +- **feat(providers):** advertise a `free-tier` capability in the provider plugin manifest for every provider with documented free models, so sidecars and dashboards can filter free-capable providers without reading the quota catalog ([#12786](https://github.com/diegosouzapw/OmniRoute/pull/12786)) — thanks @maxmad64bis +- **feat(providers):** Added EURouter as an OpenAI-compatible API-key gateway (`https://api.eurouter.ai/v1`), with live model discovery via `passthroughModels`. Its copy states that models are served by third-party upstreams listed per model, so an EU-based router is not read as EU data residency for inference. ([#13025](https://github.com/diegosouzapw/OmniRoute/pull/13025)) — thanks @ntdatt812 +- **feat(providers):** Added GreenPT as an OpenAI-compatible API-key provider (`https://api.greenpt.ai/v1`), with live model discovery via `passthroughModels`. No free-inference badge: the published docs describe a free API subscription billed per token, not a free tier. ([#13024](https://github.com/diegosouzapw/OmniRoute/pull/13024)) — thanks @ntdatt812 +- **feat(i18n):** 9 new locales — Greek (`el`), Croatian (`hr`), Serbian (`sr`), Lithuanian (`lt`), Estonian (`et`), Latvian (`lv`), Slovenian (`sl`), Maltese (`mt`), Irish (`ga`) — across the dashboard, docs mirrors, CLI, README and the site; OmniRoute now ships every official EU language (51 locales). (#13044) +- **feat(i18n):** 8 new locales — Kannada (`kn`), Malayalam (`ml`), Odia (`or`), Punjabi (`pa`), Nepali (`ne`), Sinhala (`si`), Burmese (`my`), Khmer (`km`) — across the dashboard, docs mirrors, CLI, README and the site (59 locales). The translator now restores the ICU literal escape around angle placeholders and splits oversized docs sections before translating. (#0000) ([#13660](https://github.com/diegosouzapw/OmniRoute/pull/13660)) +- feat(providers): **list Agnes 3.0 Flash as the current free chat model, drop retired 1.5 Flash, add Image 2.0/2.5 Flash plus Video 2.5/2.5 Flash, and discover the live `/v1/models` catalog (including the CN host `api.agnes-ai.cn`).** `agnes-1.5-flash` now forwards to `agnes-3.0-flash`. Video 2.5 polls `GET /v1/videos/{id}` (not the V2.0 `/agnesapi` contract). Live `/v1/models` (2026-09-09) no longer serves 1.5; the wiki marks it deprecated. 3.0 Flash is 512K context / 65,536 max output, same window as 2.5. CN-region keys use the existing per-connection base-URL field, default stays `apihub.agnes-ai.com`. ([#13120](https://github.com/diegosouzapw/OmniRoute/pull/13120)) — thanks @HouMinXi +- **feat(sse):** Codex and OpenAI catalogs list GPT-6 Astra with effort aliases (`-low` through `-ultra`); Codex CLI identity pins `@openai/codex@0.153.4` in lockstep with the image ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **feat(dashboard):** the orchestration History tab gained a "Compare runs" mode — toggling it turns each grid cell into a 2-item selection queue (a 3rd click drops the oldest pick), and picking two cells opens a side-by-side comparison panel instead of the usual detail drawer. The panel fetches both runs' detail the same way the drawer does (falling back to persisted history once a run leaves the live TTL window) and shows, per side: identity/source/state, start time, a signed `right - left` delta for duration/cost/event count, the event timeline aligned by index, and any memory hits. One side's fetch failing never blocks the other, and a delta is only ever computed when both sides have a finite value — otherwise it renders "—", never `NaN`. Comparing two runs from different sources or skills still works; a banner marks the deltas as informational rather than hiding them, since the two runs aren't a strict apples-to-apples pair. ([#12677](https://github.com/diegosouzapw/OmniRoute/pull/12677)) +- **feat(dashboard):** badge a conversation that never reached a clean stop ([#12717](https://github.com/diegosouzapw/OmniRoute/pull/12717)) — thanks @hartmark +- **feat(release):** reconcile-changelog tool + version-anchored fragment aggregation ([#12987](https://github.com/diegosouzapw/OmniRoute/pull/12987)) +- **feat(mcp):** carry context_length through MCP list_models_catalog projection (#12776) ([#13406](https://github.com/diegosouzapw/OmniRoute/pull/13406)) — thanks @KooshaPari +- **feat(cli):** make startup readiness budget configurable (#13369) ([#13433](https://github.com/diegosouzapw/OmniRoute/pull/13433)) — thanks @KooshaPari + ### 🐛 Bug Fixes +- **security(streaming):** sanitize generic mid-stream error messages before emitting OpenAI, + Responses, or Claude SSE failure frames and before diagnostic logging, while preserving raw + failures for internal classification and keeping client disconnects out of provider failure state. ([#12457](https://github.com/diegosouzapw/OmniRoute/pull/12457)) +- **fix(claude):** `blockExtraUsage: false` no longer lets 5h quota preflight skip the account; extra usage is billed after the session bar is gone, so the request must reach Anthropic ([#12803](https://github.com/diegosouzapw/OmniRoute/pull/12803)) — thanks @HouMinXi +- Sanitize HuggingChat conversation-creation and message-send transport failures before they reach client error bodies or provider logs. ([#12467](https://github.com/diegosouzapw/OmniRoute/pull/12467)) +- **fix(dashboard):** Keep local and theme-aware provider SVG icons at a definite layout size so Chromium does not collapse them to 0×0 after the v3.8.50 image-rendering change ([#12054](https://github.com/diegosouzapw/OmniRoute/pull/12054)) — thanks @ponkcore +- **fix(security):** Sanitize provider and runtime failures before public API, SSE and MCP responses and before persistent request, proxy and usage logs, preventing credentials, stack traces and host filesystem paths from crossing those boundaries while preserving stable error codes and useful diagnostics. ([#12506](https://github.com/diegosouzapw/OmniRoute/pull/12506)) +- **fix(cli):** `omniroute tunnel create` no longer crashes with `Cannot read properties of undefined (reading optsWithGlobals)` — removed the duplicate positional argument that caused Commander.js to misalign the action callback parameters ([#12295](https://github.com/diegosouzapw/OmniRoute/issues/12295)) ([#12368](https://github.com/diegosouzapw/OmniRoute/pull/12368)) — thanks @gonisulaimann +- Stop advertising Gemini Live-only models as supported audio endpoints until OmniRoute proxies the bidirectional Live protocol. ([#11710](https://github.com/diegosouzapw/OmniRoute/pull/11710)) — thanks @RaviTharuma +- **fix(kie):** reroute `flux/kontext` off the KIE Market `createTask` flow — it is catalogued with `isMarket: true` but has no Market catalog page, so KIE rejected it with "model name not supported"; it now hits the dedicated `POST /api/v1/flux/kontext/generate` / `GET /api/v1/flux/kontext/record-info` endpoints instead (#11296). ([#11985](https://github.com/diegosouzapw/OmniRoute/pull/11985)) — thanks @hartmark +- **fix(kie):** correct 12 more KIE Market catalog ids that were sent to `createTask` unchanged but diverge from KIE's documented upstream `model` values — GPT Image 2 T2I/I2I (drops the `gpt/` prefix), GPT Image 1.5 T2I/I2I (`gpt-image/` namespace), Seedream 5.0 Lite T2I/I2I (drops the `.0`), all 4 Flux 2 variants (`flux-2/` namespace, generic variant renamed `flex`), and Wan 2.7 Image / Image Pro (dash instead of dot) — each verified individually against the literal example request published on docs.kie.ai. `#11326`'s "everything else already matches" claim was wrong a second time (#11296); `z-image/4.0-*`/`z-image/4.5-*` and `flux/kontext` remain open, documented as unresolved in `KIE_MARKET_UPSTREAM_MODEL_IDS`'s comment pending further verification. ([#11667](https://github.com/diegosouzapw/OmniRoute/pull/11667)) +- **fix(dashboard):** `useApiKeySave.handleSaveApiKey` no longer forces a full upstream `/models` catalog sync on every non-curated provider connection save — callers can now pass `skipModelSync: true` to opt out, so a workflow that only wants to add one manual model no longer floods the provider's available-models list with hundreds/thousands of synced entries. The flag is a client-side intent signal only and is stripped before the connection payload is POSTed to `/api/providers`; default behavior (full sync on save) is unchanged when the flag is omitted (#11324) ([#11664](https://github.com/diegosouzapw/OmniRoute/pull/11664)) +- **Provider connections:** keep `tokenExpiresAt` when a connection is created. The create-path allowlist omitted it, so every insert stored NULL and the dashboard token badge could read a fresh connection as expired until its first background refresh ([#11368](https://github.com/diegosouzapw/OmniRoute/pull/11368)). — thanks @ntdatt812 +- **fix(combos):** the combo builder's precision-select, global-model-search, and manual-entry flows now serialize a model step's `model` string using the provider's already-computed routing-alias prefix (e.g. `oc/`) instead of rebuilding it from the raw canonical `providerId`, fixing the no-auth "OpenCode Free" provider (`opencode`) being routed to the unrelated paid "OpenCode Zen" provider (`opencode-zen`) because `opencode` doubles as a manual routing-prefix override ([#11433](https://github.com/diegosouzapw/OmniRoute/issues/11433)). ([#11670](https://github.com/diegosouzapw/OmniRoute/pull/11670)) +- **fix(radar):** the catalog feed cache now keeps `generatedAt`, the date the feed's data was built, next to `fetchedAt`, the date this install downloaded it (#11435). The feed schema requires that date and the sync path validates it, but the cache dropped it — so a feed fetched minutes ago and one carrying weeks-old figures looked identical to everything downstream, including the dashboard's "Last fetched" line. `getRadarCatalog().meta` and `GET /api/radar/status` now report both dates, the latter as its own field rather than folded into `version` — and omitted entirely for the offers and intel caches, which keep no build date, where a `null` would read as "unknown" rather than "never stored". The dashboard still shows only the fetch time; surfacing the build date there needs a new translated label and is left to a follow-up. Rows cached before migration 163 read back as `null`: unknown stays unknown instead of borrowing the fetch time. The referrals cache has persisted the same date since migration 142. — thanks @maxmad64bis +- **fix(cli):** postinstall no longer fills `JWT_SECRET` and `API_KEY_SECRET` in the installed package's `.env` (#11436). `.env.example` ships both blank on purpose: the server restores them from its durable store, or generates and persists them there, in `ensureSecrets()`. Pre-filling them defeated that — the file lives inside the package directory, so `npm i -g` replaced it and postinstall wrote _different_ values, while `ensureSecrets()` (which only acts on an empty variable) never got to restore the real ones. Both secrets rotated silently on every update, invalidating dashboard sessions and API-key CRCs. `STORAGE_ENCRYPTION_KEY` left the same list for the same reason in #1622; its comment pointed at a function that no longer exists and now names the real provisioning path. — thanks @maxmad64bis +- **fix(cli):** the CLI now says when a loaded `.env` lives inside the installed package directory (#11437). It already announces every env file it reads, without distinguishing the ones that survive an update from the one that does not: `npm i -g` replaces the package directory wholesale, so values set there are gone at the next update, silently. The warning names the durable path to move them to, and fires only when that file actually supplied a value — a file entirely shadowed by a durable one supplied nothing. A development checkout stays silent: there the same path is stable and documented in `SETUP_GUIDE.md`. — thanks @maxmad64bis +- **fix(free-models):** the shared `isFreeModel()` predicate no longer reports catalog entries marked `freeType: "discontinued"` as free, so `hidePaidModels` can't route to Pollinations' seven premium models that now require a paid key ([#11441](https://github.com/diegosouzapw/OmniRoute/pull/11441)) — thanks @maxmad64bis +- fix(ui): let AnySearch use the normal provider-icon fallback when LobeHub has no matching icon (#11449) +- Fixed the v3.8.50 Costs and Analytics dashboards so flat-rate Claude Code usage can be shown as an explicitly requested token-price estimate without changing default billed-cost semantics. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed archived usage retention so each request is priced individually instead of pricing a day's summed tokens once, which understated archived cost whenever a day mixed cache-heavy and ordinary requests. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed the Costs dashboard so it discloses when displayed figures include flat-rate token-price estimates instead of labelling them as billed spend, using the flag the analytics API already returns; the month-end projection and the CSV/JSON exports carry the same marker, and billed-cost mode is unchanged. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- **fix(combo):** attach the same combo-diagnostics trace (`poolSize`/`attemptOrder`/`excluded`/`terminalReason`, plus `x-omniroute-combo-*` headers) to the round-robin strategy's and the nested pipeline/fusion runtime-unit loop's "Maximum combo retry limit reached" 503 that the priority-strategy path already attaches for the identical terminal condition — previously those two paths returned a bare, contextless 503 ([#11462](https://github.com/diegosouzapw/OmniRoute/issues/11462)). ([#11665](https://github.com/diegosouzapw/OmniRoute/pull/11665)) +- **fix(bun):** use native `bun:sqlite` in `bootstrap-env` and `sync-env` to avoid loading `better-sqlite3` N-API addon during Bun startup ([#11468](https://github.com/diegosouzapw/OmniRoute/pull/11468)) — thanks @TheDemonTuan +- **fix(docker-bun):** make `Dockerfile.bun` install reliable with pre-install script helpers and native `bun:sqlite` smoke check ([#11470](https://github.com/diegosouzapw/OmniRoute/pull/11470)) — thanks @TheDemonTuan +- **fix(docker-bun):** remove every vendored `better-sqlite3` native addon from the Bun runtime image so startup cannot abort before the `bun:sqlite` fallback ([#11482](https://github.com/diegosouzapw/OmniRoute/pull/11482)) — thanks @TheDemonTuan +- **fix(autoCombo):** effort/alias model variants (`gpt-5.6-sol-xhigh`, `gpt-5.6`, cursor's `claude-4.6-opus-high`) inherit their base model's task fitness instead of falling to the wildcard 0.5 ([#11492](https://github.com/diegosouzapw/OmniRoute/pull/11492)) — thanks @MumuTW +- fix(db): rate-limit repeated Arena ELO leaderboard fetch-failure warnings instead of logging one per sync attempt (#11500) ([#11989](https://github.com/diegosouzapw/OmniRoute/pull/11989)) — thanks @hartmark +- **fix(db):** dedupe the raw `[Encryption] Decryption failed...` log line emitted by the lazy-decrypt views (`createLazyRowProxy`/`createLazyConnectionView`), which power `getProviderConnections()` and were re-triggering that line on every CredentialHealth/model-sync cycle for the same corrupt or stale-key credential — a fresh Proxy over a fresh row on every cycle meant the per-proxy memoization never suppressed it, unlike the dedup `decryptConnectionFields()` already had since [#9927](https://github.com/diegosouzapw/OmniRoute/issues/9927). Now shares that dedupe tracking so the line logs at most once per credential ([#11500](https://github.com/diegosouzapw/OmniRoute/issues/11500)). ([#11678](https://github.com/diegosouzapw/OmniRoute/pull/11678)) +- **fix(intelligence):** Arena ELO sync stores a synthesized base-model row for every effort/harness variant (`claude-opus-5-max` → `claude-opus-5`, `gpt-5.6-sol-xhigh (codex-harness)` → `gpt-5.6-sol`), so bare model requests reach the synced intelligence layer instead of the static fallback table; the hand-maintained `MODEL_ALIAS_MAP` — which collapsed generations (`gpt-5.5` → `gpt-5`) — is gone ([#11506](https://github.com/diegosouzapw/OmniRoute/pull/11506)) — thanks @MumuTW +- **fix(autoCombo,sse):** catalog hygiene — the static task-fitness table no longer ranks retired models above live ones (dead rows dropped, patterns matched on segment boundaries), the 7 deprecation aliases that forwarded to retired or non-existent models now point at the vendors' published replacements, an alias is no longer rewritten when the serving provider offers the id as-is, and a new `check:model-lifecycle` gate diffs both tables against a vendor lifecycle snapshot ([#11507](https://github.com/diegosouzapw/OmniRoute/pull/11507)) — thanks @MumuTW +- **fix(sse):** stop dropping resolved `thoughtSignature` values on parallel (multi tool-call) turns sent to Gemini 3.x — the claude→gemini and openai→gemini translators previously kept the signature only on the *first* function call of a message, causing Gemini to reject subsequent calls in the same turn with HTTP 400 "Function call is missing a thought_signature"; each function call now keeps its own resolved signature ([#11510](https://github.com/diegosouzapw/OmniRoute/issues/11510)). ([#11669](https://github.com/diegosouzapw/OmniRoute/pull/11669)) +- Treat Docker publishing from a non-default release branch as an expected skip instead of a failed workflow, while preserving the guard that prevents it from overwriting the `next` channel. ([#11527](https://github.com/diegosouzapw/OmniRoute/pull/11527)) — thanks @pacocartones +- fix(search): prefer credentialed providers over duckduckgo-free fallback (#11524) — the fallback-only loop ran before the credentialed-providers loop in `executeWebSearch`, making configured providers unreachable when `duckduckgo-free` was available. ([#11565](https://github.com/diegosouzapw/OmniRoute/pull/11565)) — thanks @vsd2807 +- **fix(sse):** cap the upstream headers-wait phase for STREAMING requests to a client-realistic ceiling (110s, under Codex's own ~120s hard client-abort window) instead of the flat 10-minute `FETCH_TIMEOUT_MS` default — that default was 5x longer than the body-phase readiness watchdog's own adaptive bound, so a request whose upstream never returned any response at all (not even headers, e.g. a stalled NVIDIA target behind a tool-heavy Responses→Chat translation) kept the client connection alive on keepalives only, guaranteeing the client's own patience ran out first with an opaque 499 instead of OmniRoute detecting and failing the stall fast. Non-streaming requests are unaffected — they keep the existing flat default (`open-sse/utils/fetchStartTimeoutPolicy.ts`) (#11526) ([#11668](https://github.com/diegosouzapw/OmniRoute/pull/11668)) +- **fix(ci):** protocol-clients E2E harness boots the peer-stamped custom server (`run-next.mjs dev`) instead of the bare `next` CLI, so `LOCAL_ONLY` locality resolves from the real TCP peer stamp and `/api/mcp/audit` answers 200 (open bootstrap preserved via an `OMNIROUTE_E2E_BOOTSTRAP_MODE=open` credential clear in `run-next.mjs`, pinned to loopback `HOST`) instead of a deterministic 403 (#11535) ([#11549](https://github.com/diegosouzapw/OmniRoute/pull/11549)) — thanks @jonlwheat2-gif +- **fix(docs):** the Auto-Combo engine is described with the number of factors it actually declares. Four different counts were in circulation — 6, 9, 13 and 14 across `AGENTS.md`, four guides and six code comments — while `DEFAULT_WEIGHTS` declares 15. `check:docs-counts` now reads that number from the source, so adding a factor without updating the prose fails CI ([#11545](https://github.com/diegosouzapw/OmniRoute/pull/11545)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **fix(quota-share):** in-flight leases are per request, so an aborted request's slot ages out instead of being kept alive by later traffic on the same connection ([#11547](https://github.com/diegosouzapw/OmniRoute/pull/11547)) — thanks @abhisheksharma2411 +- The free-tier summary route now serves the refreshed Radar catalog when the feed is active — the same numbers the dashboard shows — and states which catalog answered plus its real build date, instead of always reporting release-frozen figures with a stale curation date. The supporter-key live feed stays reserved to authenticated callers of the instance. ([#11550](https://github.com/diegosouzapw/OmniRoute/pull/11550)) — thanks @maxmad64bis +- **sse:** fix LiveWS/embed-WS servers crashing at startup under the Node/tsx runtime — `liveServer.ts`, `embedWsProxy.ts` and `apiBridgeServer.ts` imported `@/shared/utils/httpClientAbortGuard` without the `.mjs` extension, so the client-abort crash guard added by [#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556) was unreachable and every dependent test failed with `ERR_MODULE_NOT_FOUND` ([#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556)). ([#11671](https://github.com/diegosouzapw/OmniRoute/pull/11671)) — thanks @jonlwheat2-gif / @yxyxy +- fix(chat): shed any of the matched provider-node's routing identifiers (public prefix or internal id) before model lookup, so composites like `//` no longer reach upstream verbatim (#11557) — thanks @maxmad64bis +- **fix(cache):** a semantic-cache entry now expires on its TTL instead of surviving until the next UTC midnight, and `dbEntries` stops counting expired rows ([#11573](https://github.com/diegosouzapw/OmniRoute/pull/11573)) — thanks @ntdatt812 +- **fix(api):** `/v1/models` schedules its stale-while-revalidate rebuild through Next's `after()` again, so a stale catalog reaches the client before the rebuild blocks the event loop ([#11574](https://github.com/diegosouzapw/OmniRoute/pull/11574)) — thanks @ntdatt812 +- **fix(check):** the `known-symbols` executor conformance gate awaits the now-async `getExecutor()`, so it stops reporting all 142 registered aliases as dead symbols and can detect a lazy import that fails ([#11582](https://github.com/diegosouzapw/OmniRoute/pull/11582)) — thanks @ntdatt812 +- **fix(pack):** register `bin/cli/utils/volatileEnvPath.mjs` in `PACK_ARTIFACT_REQUIRED_PATHS`, so the prune can no longer silently drop a module the published CLI imports on every boot ([#11588](https://github.com/diegosouzapw/OmniRoute/pull/11588)) — thanks @ntdatt812 +- **i18n(vi):** translate the five reliability-column strings on the free-provider rankings page, which rendered as `__MISSING__:` markers in the Vietnamese UI ([#11589](https://github.com/diegosouzapw/OmniRoute/pull/11589)) — thanks @ntdatt812 +- **resilience:** restore the expired-connection retry-budget probe in the token-health sweep — the `!isGitHubAccessTokenOnlyConnection` carve-out reintroduced by #11608 contradicted the boundary pinned by #11592, so a GitHub connection parked at `expired` with retry budget remaining was never probed and could never self-heal ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)). ([#11672](https://github.com/diegosouzapw/OmniRoute/pull/11672)) — thanks @jonlwheat2-gif / @ntdatt812 +- **fix(dashboard):** Provider Stats now displays combo success rates on their native 0–100 percentage scale instead of multiplying them twice ([#11595](https://github.com/diegosouzapw/OmniRoute/pull/11595)) — thanks @pacocartones +- **fix(providers):** route DuckDuckGo Web Chat through the verified `duck.ai` endpoint, refresh its free model catalog, and avoid forwarding unsolved anti-abuse challenges ([#11597](https://github.com/diegosouzapw/OmniRoute/pull/11597)) — thanks @solstxce +- **fix(ui):** Console log Refresh and Copy controls now expose localized accessible names, keep copy actions visible on keyboard focus, and announce copy completion safely ([#11599](https://github.com/diegosouzapw/OmniRoute/pull/11599)) — thanks @pacocartones +- Dropped the stale-`.eslintcache` `restore-keys` fallback from both "Restore ESLint file cache" steps in `ci.yml`, so the blocking `Lint` job can no longer be served per-file verdicts computed under a different lint config, suppressions file or lockfile. `quality.yml` had already dropped it in #11963; `ci.yml` — the workflow that actually gates PRs — had not (#11600). ([#11996](https://github.com/diegosouzapw/OmniRoute/pull/11996)) +- fix(ci): pass `--pass-on-unpruned-suppressions` in `run-eslint-json.mjs` so the CI Lint job no longer fails when a suppression is merely orphaned by a genuine fix, mirroring the identical fix already in `validate-release-green.mjs` (#11600) ([#11983](https://github.com/diegosouzapw/OmniRoute/pull/11983)) — thanks @hartmark +- fix(handoff): enforce provider allowlist for universal handoff (#11602) — universal handoff now skips summarization when the selected summary model's provider is not included in the configured provider allowlist. ([#11619](https://github.com/diegosouzapw/OmniRoute/pull/11619)) — thanks @vsd2807 +- **fix(dashboard):** Show a stable error state when Search Analytics returns an HTTP error or malformed data ([#11603](https://github.com/diegosouzapw/OmniRoute/pull/11603)) — thanks @pacocartones +- **test(gamification):** pin the aggregate profile level to the XP-derived semantics of #11604 — `getAggregateXp()` now derives `currentLevel` from the summed XP (`calculateLevel(sum)`), not `MAX(stored current_level)`, and the #3484 fixture levels are aligned with the XP curve ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)). ([#11676](https://github.com/diegosouzapw/OmniRoute/pull/11676)) — thanks @jonlwheat2-gif / @pacocartones +- **fix(dashboard):** Keep the Profile level and progress aligned with aggregate XP, including bounded handling for invalid totals ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)) — thanks @pacocartones +- **fix(dashboard):** Prevent locked hidden badges from revealing their icon or opening private badge details before they are earned ([#11605](https://github.com/diegosouzapw/OmniRoute/pull/11605)) — thanks @pacocartones +- **fix(dashboard):** Restore keyboard focus after shared modals close and cancel delayed autofocus during cleanup ([#11607](https://github.com/diegosouzapw/OmniRoute/pull/11607)) — thanks @pacocartones +- **fix(dashboard):** Enable Enter and Space activation for clickable data-table rows without hijacking nested controls ([#11610](https://github.com/diegosouzapw/OmniRoute/pull/11610)) — thanks @pacocartones +- **fix(autoCombo,sse):** vendor-retired catalog ids are dropped from the auto-combo candidate pool and no longer win on leftover `arena_elo` / `user_override` rows; `getModelLifecycleDecision` consults `model-lifecycle.json` (prefix-stripped) so aggregator traffic is not `untracked` for ids the snapshot already knows ([#11625](https://github.com/diegosouzapw/OmniRoute/issues/11625)) ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)) — thanks @MumuTW +- **test(chatcore):** move Codex/Claude combo fixtures off models the lifecycle guard now rejects — `gpt-5.1-codex`/`gpt-5-codex` are vendor-retired (snapshot, #11626) and `claude-3-5-sonnet-20241022` is shut down, so native-passthrough and combo-fallback tests switched to `gpt-5.6-sol` and `claude-sonnet-4.6` ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)). ([#11675](https://github.com/diegosouzapw/OmniRoute/pull/11675)) — thanks @jonlwheat2-gif / @MumuTW +- fix(opencode-plugin): stop a free-tier budget that rounds up across a magnitude from rendering as `1000K`/`1000M` in the model picker — `fmtTokens` chose its unit from the raw token count and then rounded with `toFixed(1)`, so 999,950–999,999 printed as `1000K` rather than `1M` and just under a billion printed as `1000M` rather than `1B` ([#11684](https://github.com/diegosouzapw/OmniRoute/pull/11684)) — thanks @abhisheksharma2411 +- Cap prose-derived Antigravity quota resets at the configured model cooldown maximum while preserving authoritative Retry-After headers and Google RetryInfo hints. (#11695) Thanks @pacocartones. ([#11823](https://github.com/diegosouzapw/OmniRoute/pull/11823)) +- Compare `_rebaseline_*` notes structurally in the ratchet-bank verifier so an object-valued note no longer reports itself as rewritten on an unmodified tree — the nightly `bank-ratchet-shrinks` lane had been aborting on every run since 2026-08-11, leaving 41,672 lines of earned file-size shrinks unbanked (#11702). ([#11892](https://github.com/diegosouzapw/OmniRoute/pull/11892)) — thanks @MumuTW +- **fix(build):** `prepublish.ts` bundles the ChatGPT Web (Codex) MCP bridge through `runBuildTool()` instead of spawning `npx.cmd` raw, fixing the build crash on Node ≥ 20/Windows where `.cmd` shims cannot be spawned without a shell (EINVAL) ([#11704](https://github.com/diegosouzapw/OmniRoute/issues/11704)) ([#11706](https://github.com/diegosouzapw/OmniRoute/pull/11706)) — thanks @jonlwheat2-gif +- fix(codex): keep `parallel_tool_calls:false` on the translated Codex Responses Lite path (#11707) ([#11984](https://github.com/diegosouzapw/OmniRoute/pull/11984)) — thanks @hartmark +- **fix(tests):** the new Gemini Web Images retirement test (#11708) called the now-async `getExecutor()` synchronously, throwing `TypeError: getExecutor(...).getProvider is not a function` instead of asserting Gemini Web chat stays available. Awaited to match the R0.3 executor-registry refactor (#11220). ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(search):** Allow REST and MCP web-fetch requests to select the advertised `anysearch-search` provider explicitly ([#11716](https://github.com/diegosouzapw/OmniRoute/pull/11716)) — thanks @pacocartones +- **fix(providers):** Keep overnight peak-hour protection active after midnight according to the window's configured start day ([#11718](https://github.com/diegosouzapw/OmniRoute/pull/11718)) — thanks @pacocartones +- **fix(providers):** correct all five g4f.space gateway descriptions to document proof-of-work credits or member API keys, variable limits, and the remote prompt boundary instead of promising free keyless access at five requests per minute ([#11738](https://github.com/diegosouzapw/OmniRoute/pull/11738)). +- **fix(db):** `model_capabilities` is created by a migration instead of lazily on the first models.dev sync, so a clean install and an upgraded install converge on the same schema regardless of which features have run ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(ci):** `check:install-upgrade` now fails on an `npm` install truncated by ENOSPC (npm reports it as a warning and still exits 0), authenticates its health probe so the version assertion works against the hardened health payload, frees the clean-install tree before the upgrade phase, and no longer reports a schema divergence computed from a boot that never served ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(routing):** harden session-affinity key derivation against oversized explicit IDs and empty structural-payload collisions, while recognizing Gemini and common text request shapes ([#11746](https://github.com/diegosouzapw/OmniRoute/pull/11746)) — thanks @MumuTW +- **fix(packages/browser-pool):** regenerate `package-lock.json` so the `packages/browser-pool` workspace's locked `playwright`/`@types/node` (and transitives) match its `package.json` specs, fixing cache-only/offline installs (`npm ci --offline`, Nix `buildNpmPackage`) that previously failed with `ENOTCACHED` ([#11747](https://github.com/diegosouzapw/OmniRoute/issues/11747)) — thanks @benjaminkitt ([#11784](https://github.com/diegosouzapw/OmniRoute/pull/11784)) — thanks @geekyNads +- **fix(assets):** Stop distributing 79 provider and CLI logos without verified source/license provenance, keep every provider/tool available through the internal generic icon, and prevent those IDs from falling through to an external icon CDN. ([#11750](https://github.com/diegosouzapw/OmniRoute/pull/11750)) +- **fix(providers):** retire the common `chatgpt-web` / `cgpt-web` provider because its pre-key and proof-of-work provenance could not be cleared ([#11754](https://github.com/diegosouzapw/OmniRoute/pull/11754)) — explicit chat and image requests, slash-prefixed models, persisted aliases, proxy overrides, and combo targets now fail closed with a sanitized HTTP `410` / `PROVIDER_RETIRED` before upstream dispatch. Migration 163 tombstones matching connections and invalidates their active leases without deleting history or widening API-key allowlists. The independent `chatgpt-web-codex` / `cgpt-codex` provider and its MIT-noticed vendored implementation remain available. +- **fix(usage):** quota and usage refresh no longer 409 when an exclusive lease reserves the connection ([#11758](https://github.com/diegosouzapw/OmniRoute/pull/11758)) — thanks @TheDemonTuan +- Keep the embedding registry's vector width and `embedding` type on models when a synced model exists for the same id, so `/v1/models` no longer reports registry-described embedding models widthless or untyped (#11761) — thanks @rahilmavani +- Correct `google/gemini-embedding-001` on the OpenRouter route to 3072 dimensions, the width it returns when `dimensions` is not sent (#11761) — thanks @rahilmavani +- **fix(vision-bridge):** Restrict automatic vision routing and fallbacks to models available in authoritative live provider catalogs, including aliases and effort variants ([#11767](https://github.com/diegosouzapw/OmniRoute/issues/11767)) — thanks @pacocartones ([#11813](https://github.com/diegosouzapw/OmniRoute/pull/11813)) +- **fix(providers):** reclassify Cerebras as a one-time $5 signup credit (payment method required, 30-day validity), not a recurring no-card 1M tokens/day trial ([#11773](https://github.com/diegosouzapw/OmniRoute/issues/11773)) ([#12591](https://github.com/diegosouzapw/OmniRoute/pull/12591)) — thanks @HouMinXi +- fix(cli): stop prepublish from re-rebuilding the already-built ESM-only opencode-plugin dist (#11787) ([#11990](https://github.com/diegosouzapw/OmniRoute/pull/11990)) — thanks @hartmark +- **fix(db):** Fresh installs no longer log a non-fatal `no such table: compression_run_telemetry` warning when retention cleanup runs before the lazily-created telemetry table exists ([#11802](https://github.com/diegosouzapw/OmniRoute/pull/11802)) — thanks @RaviTharuma +- fix(sse): set X-OmniRoute-Selected-Connection-Id on successful combo dispatches so downstream consumers stop falling back to an empty connection id (#11810) ([#11986](https://github.com/diegosouzapw/OmniRoute/pull/11986)) — thanks @hartmark +- **fix(services):** embedded CLIProxyAPI lifecycle checks now use public `/healthz`, while model discovery uses the configured dedicated data-plane API key instead of the management password ([#11811](https://github.com/diegosouzapw/OmniRoute/pull/11811)) — thanks @RaviTharuma +- Fix persisted model steps returning an error in the playground route simulator, and warn when structural steps cannot be simulated. ([#11862](https://github.com/diegosouzapw/OmniRoute/pull/11862)) — thanks @pacocartones +- **fix(providers):** Antigravity's dynamic mitmAlias table no longer routes `gemini-3.7-flash-{high,medium,low}` to a literal tier-suffixed upstream id just because one connected account's own discovery listed it directly — those display ids always resolve through the safe `gemini-3.7-flash-tiered` static alias, so one account's Google-provisioned access no longer 404s every sibling account of the provider ([#11824](https://github.com/diegosouzapw/OmniRoute/issues/11824), [#11651](https://github.com/diegosouzapw/OmniRoute/issues/11651)) ([#11988](https://github.com/diegosouzapw/OmniRoute/pull/11988)) — thanks @hartmark +- **fix(dashboard):** Expose Profile loading, errors, page structure, and XP progress to assistive technologies ([#11838](https://github.com/diegosouzapw/OmniRoute/pull/11838)) — thanks @pacocartones +- **fix(socks):** `Agent.connectTimeout` now bounds both SOCKS handshake and TLS connect, and `family === null` no longer falls back to `fetch-socks` ([#11842](https://github.com/diegosouzapw/OmniRoute/pull/11842)) — thanks @maxmad64bis +- **perf(compression):** OOM mitigations for large payload hashing, memoization, and token estimation ([#11844](https://github.com/diegosouzapw/OmniRoute/pull/11844) — thanks @AndrianBalanescu) +- Excluded Next.js Node File Trace manifests (`*.nft.json`) from the published npm tarball. They are build-time metadata and are never read while serving, but had grown to 668.7 MB — 61% of the package — which pushed the upload past the registry limit and made `npm publish` fail with `413 Payload Too Large`. ([#11864](https://github.com/diegosouzapw/OmniRoute/pull/11864)) +- **fix(cli):** keep the global OmniRoute server `--base-url` separate from provider-node `--endpoint` values in add, update, and validate commands ([#11860](https://github.com/diegosouzapw/OmniRoute/pull/11860)) — thanks @pacocartones +- **fix(config):** Nous Research's `Hermes-4-405B` model now displays as "Hermes 4 405B (Nous Research)" in both the provider registry and the free-model catalog, instead of the mislabelled "Hermes 4 7B" ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#11993](https://github.com/diegosouzapw/OmniRoute/pull/11993)) +- **fix(provider/nous):** inject required user= tag into Nous Research inference requests to resolve upstream 400 "missing tags" error ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#12044](https://github.com/diegosouzapw/OmniRoute/pull/12044)) +- **fix(build):** `npm run build` now fails in one second with a named package and a copy-pasteable fix when npm silently drops an externalised optional native dependency, instead of dying four minutes in with `Module not found: Can't resolve 'better-sqlite3'` ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- **fix(install):** `postinstall` no longer throws `ReferenceError: isAndroid is not defined` — failing the whole `npm install` — when the `better-sqlite3` rebuild fallback times out; the manual-fix guidance is reachable again ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- Fixed the Alibaba free-tier allowlist test that went red on its own once the shipped catalog's `validUntil` (2026-08-27) passed, leaving every PR and `main` with a failing `Unit Tests (1/8)`. The test now builds its own packs with dates it controls, and covers the expired-pack fallback that production has actually been serving. ([#11867](https://github.com/diegosouzapw/OmniRoute/pull/11867)) +- Split the npm registry upload into its own GitHub-hosted job. npm refuses `--provenance` from a self-hosted runner (`422 ... Only "github-hosted" runners are supported`), which blocked the v3.8.50 publish; the heavy verification cannot move to a hosted runner, so it now hands the proven tarball over instead. ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(ui):** the shared `DataTable` loading state no longer reads its decorative ⏳ glyph out to assistive technology, and now carries the same `role="status"` / `aria-live="polite"` / `aria-busy="true"` semantics as `PageLoading` ([#11869](https://github.com/diegosouzapw/OmniRoute/pull/11869)) — thanks @pacocartones +- **fix(api):** Bound the `timeout` accepted by `POST /api/providers/[id]/login` to the same 15s–600s window the Adobe Firefly and Conol browser-login paths already enforce, so a request can no longer pin a headful browser and the single active-login slot with an arbitrarily large poll budget. ([#11871](https://github.com/diegosouzapw/OmniRoute/pull/11871)) — thanks @pacocartones +- **fix(api):** Stop the tunnel and MITM routes from returning raw child-process error text, which disclosed host paths, binary install locations and Tailscale `tskey-*` credentials in a body some of these routes serve to non-loopback callers, and return a real 400 instead of a framework 500 when a tunnel request body fails validation. ([#11872](https://github.com/diegosouzapw/OmniRoute/pull/11872)) — thanks @pacocartones +- **fix(open-sse):** stream TTFT/ITL are sampled from a monotonic clock (`performance.now`) instead of `Date.now`, so an NTP correction or wall-clock jump can no longer inflate a genuine TTFT or produce a negative one that the `ttft >= 0` guard silently drops — protecting the router's OTel/EWMA/`usage_history`/speed-ranking signals ([#11873](https://github.com/diegosouzapw/OmniRoute/pull/11873)) — thanks @pacocartones +- **fix(providers):** xAI `reasoning_effort: "xhigh"` now reaches grok-4.6+ instead of being silently clamped to `"high"` ([#11879](https://github.com/diegosouzapw/OmniRoute/pull/11879)) — thanks @NoxzRCW +- **fix(dashboard):** the providers page no longer crashes into the error boundary when a provider id collides with an `Object.prototype` member (`constructor`, `__proto__`); icon lookups are own-property guarded ([#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)) — thanks @NoxzRCW +- **fix(skills):** injected skill tools declared in shorthand (`{"content": "string"}`) now forward valid JSON Schema, unblocking providers that validate tool schemas strictly such as Zhipu GLM on the Console Go tier ([#11881](https://github.com/diegosouzapw/OmniRoute/pull/11881)) — thanks @NoxzRCW +- **fix(api):** `POST /api/playground/simulate-route` now surfaces `combo-ref` and `provider-wildcard` persisted combo steps with a specific warning (naming the referenced combo, or the unresolved `provider/model` wildcard) instead of folding them into a generic "unsupported step" count; a `provider-wildcard` step is also now included as an unresolved target so the operator can see it is in the route (ported from [#11882](https://github.com/diegosouzapw/OmniRoute/pull/11882) — thanks @NoxzRCW). ([#11930](https://github.com/diegosouzapw/OmniRoute/pull/11930)) +- **fix(translator):** the streaming OpenAI→Claude translator keeps upstream usage, including prompt-cache tokens, when it arrives on a trailing `choices: []` chunk (Fireworks and any upstream using `stream_options.include_usage`) ([#11883](https://github.com/diegosouzapw/OmniRoute/pull/11883)) — thanks @NoxzRCW +- **fix(cli):** `omniroute update --apply` now tells you explicitly whether a running server was detected and, if so, that you must run `omniroute restart` to apply the update — it never restarted anything and previously implied the update was already live once files were installed. The dashboard's npm-mode Update flow (`/api/system/version`) now tries OmniRoute's own PID-file-managed supervisor before falling back to pm2, and reports an honest "restart required" step instead of a silent pm2-only "skipped" that read like a completed update. The server-side latest-version lookup backing the dashboard's update banner also gained `--prefer-online`, closing the same stale-npm-cache class already fixed in the CLI's own copy for #4376 ([#11885](https://github.com/diegosouzapw/OmniRoute/issues/11885)). ([#12005](https://github.com/diegosouzapw/OmniRoute/pull/12005)) +- **fix(resilience):** clear persisted LKGP pins when a target suffers connection/provider exhaustion or is skipped before dispatch due to cooldown/exhaustion/unavailability, preventing subsequent requests from repeatedly prioritizing known-dead providers ([#11911](https://github.com/diegosouzapw/OmniRoute/issues/11911)). ([#12013](https://github.com/diegosouzapw/OmniRoute/pull/12013)) — thanks @HouMinXi +- **fix(sse):** the OpenAI→Claude stream translator now defers the terminal `message_delta`/`message_stop` emission until the real usage block has arrived (or a genuine end-of-stream flush forces it) instead of emitting it immediately on `finish_reason` — previously, when the trailing usage-only chunk (`{"choices":[],"usage":{...}}`) arrived *after* the `finish_reason` chunk (the normal order for Fireworks/vLLM/Together and other `stream_options.include_usage` upstreams), the client-visible `message_delta` still carried stale/zero usage even though `state.usage` was internally corrected too late to matter (ported from [#11915](https://github.com/diegosouzapw/OmniRoute/pull/11915) — thanks @HouMinXi). ([#11933](https://github.com/diegosouzapw/OmniRoute/pull/11933)) +- **fix(sse):** `stripStore()` now forces `store=false` for stateless OpenAI-compatible Responses-API targets (unless the connection has explicitly opted in via `providerSpecificData.openaiStoreEnabled`), instead of only handling the `openai`/`agentrouter` cases — a client-supplied `store` value previously passed through untouched to backends that don't actually persist responses server-side ([#11916](https://github.com/diegosouzapw/OmniRoute/pull/11916) — thanks @HouMinXi). +- **fix(models):** custom provider-node models (synced, custom, and alias-backed) now appear under their configured prefix in the unified catalog when the operator's model-id prefix mode is canonical, instead of being dropped whenever alias-inclusion was otherwise disabled ([#11918](https://github.com/diegosouzapw/OmniRoute/pull/11918) — thanks @HouMinXi). +- **fix(models):** the unified model catalog now suppresses stale static registry models (including effort-tier variants) for any provider whose active connection has an authoritative live synced catalog, not only providers already using exclusive-synced-listing — a connection with `providerUsesAuthoritativeLiveCatalog` previously kept serving both the live-synced models and the stale static rows side by side ([#11919](https://github.com/diegosouzapw/OmniRoute/pull/11919) — thanks @HouMinXi). +- **fix(ui):** `ProviderIcon`'s three remaining unguarded lookups (`PROVIDER_ICON_ALIASES`, `LOCAL_SVG_ALIASES`, `THEMED_SVGS`) now use `Object.hasOwn()` own-property checks — a provider id such as `constructor` or `__proto__` previously resolved through the prototype chain instead of falling through to the unknown-provider CDN fallback (`getLobeProviderIcon()` itself was already guarded by [#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)); `ProviderPageHeader`'s `color` field is also now optional, matching the rest of the component's defensive typing (ported from [#11920](https://github.com/diegosouzapw/OmniRoute/pull/11920) — thanks @HouMinXi). ([#11935](https://github.com/diegosouzapw/OmniRoute/pull/11935)) — thanks @NoxzRCW +- fix(ollama): preserve multi-byte UTF-8 content split across stream chunks in the Ollama NDJSON transform, which previously corrupted CJK/emoji into U+FFFD (#11921) — thanks @pacocartones +- **fix(providers):** OrcaRouter chat requests now target `/v1/chat/completions` instead of the bare `/v1` API root, fixing the upstream `404 Invalid URL (POST /v1)` ([#11923](https://github.com/diegosouzapw/OmniRoute/pull/11923)). — thanks @echel0nn +- **fix(plugins):** deliver the `onStreamComplete` event to disk-installed plugins. The event shipped in v3.8.50 (#9669) was emitted internally but had no plugin-facing wiring, so no plugin could ever subscribe: the manifest schema silently dropped `hooks.onStreamComplete`, and the loader/manager only knew the seven legacy hooks. `onStreamComplete` is now a declarable manifest hook, wired through the loader and registered by the manager like the other hooks, and its payload carries a `requestId` so consumers can correlate the stream-completion event with the originating request ([#11934](https://github.com/diegosouzapw/OmniRoute/pull/11934)) — thanks @amaleta / @pacocartones +- **fix(db):** the Qdrant embedding-model dropdown now lists local/self-hosted providers (Ollama, LM Studio, vLLM, etc.) — an active connection is treated as "configured" when the provider allows an optional API key, not only when it has a real key or OAuth, so a running local embedding provider is no longer hidden from the picker ([#11949](https://github.com/diegosouzapw/OmniRoute/issues/11949)) ([#11995](https://github.com/diegosouzapw/OmniRoute/pull/11995)) +- **fix(providers):** Vertex AI Anthropic partner-model discovery now calls the Model Garden `v1beta1` publisher list (`/v1beta1/publishers/anthropic/models`, global) and parses its `publisherModels` envelope, so Claude models auto-synced from Vertex populate the active live catalog and route at request time instead of returning `Model '' is not available in the active live catalog` ([#11991](https://github.com/diegosouzapw/OmniRoute/issues/11991)) — thanks @fabioluissilva ([#11998](https://github.com/diegosouzapw/OmniRoute/pull/11998)) +- **fix(cli):** support `--base-url` alongside `--endpoint` in `omniroute nodes add`, `update`, and `validate` subcommands to prevent global `--base-url` shadowing issues ([#11999](https://github.com/diegosouzapw/OmniRoute/issues/11999)). ([#12033](https://github.com/diegosouzapw/OmniRoute/pull/12033)) — thanks @HouMinXi +- **fix(providers):** `cloudflare-ai` no longer refuses image content parts for every Workers AI model ([#12002](https://github.com/diegosouzapw/OmniRoute/pull/12002)) — the plain-string `content` requirement behind #2539 is carried by the _model_ schema, not by the `/ai/v1/chat/completions` endpoint (measured: an all-text part array returns 200 on `@cf/mistralai/mistral-small-3.1-24b-instruct`, `@cf/meta/llama-4-scout-17b-16e-instruct` and `@cf/meta/llama-3.3-70b-instruct-fp8-fast`, and 400 on the text-only `@cf/qwen/qwen2.5-coder-32b-instruct`). `transformRequest()` flattened every array and threw on the first non-text part (#6390), so image input was refused for vision-capable Cloudflare models that accept it. All-text arrays are still flattened — the one shape every model accepts — while an array carrying a non-text part is passed through untouched, so the attachment is still never silently dropped. Regression guards: `tests/unit/cloudflare-ai-image-parts-6390.test.ts`. — thanks @davidlinfr +- **fix(resilience):** decouple the limiter-managed execution backstop from the queue-wait budget — new `requestQueue.executionMaxWaitMs` (env `RATE_LIMIT_EXECUTION_MAX_WAIT_MS`, default 600000 = 10 min) now feeds Bottleneck's post-dispatch `expiration`, while `requestQueue.maxWaitMs` keeps its documented queue-wait semantics. Previously the queue-wait budget doubled as the execution expiration, so legitimate long-running calls on non-incremental gateways (whole generation buffered before the first upstream byte, e.g. Console Go / Command Code tiers serving GLM models) were killed mid-flight at the queue budget with a false 504 `RATE_LIMIT_EXECUTION_TIMEOUT` — the local limiter undercut the provider-aware upstream fetch-start timeouts. The surfaced 504 message now names `requestQueue.executionMaxWaitMs`; the error keeps the #4165 guarantees (disclaims an upstream timeout, preserves the Bottleneck error as `cause`, branded code + trusted provenance, classified request-scoped so combo falls back). A real queue-wait bound (the `Promise.race` around `limiter.schedule()` sketched in #9533) remains future work. (#12025) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **Call logs:** keep the `error` field when an artifact exceeds the storage cap, instead of replacing it with the omission marker. The error is the only field that says *why* a request failed and is typically ~90 bytes next to the multi-hundred-KB bodies that trip the cap, so dropping it left a size-limited row undiagnosable — a provider outage, a local timeout and an upstream 400 all rendered identically. It is now preserved at every fallback stage, truncated to 4KB if it is itself large ([#12026](https://github.com/diegosouzapw/OmniRoute/issues/12026)). ([#12095](https://github.com/diegosouzapw/OmniRoute/pull/12095)) — thanks @ntdat812 +- **fix(diagnostics):** preserve the error field (truncated to 4KB with a `[truncated: …]` suffix) in every call-log artifact size-limit fallback stage. Previously the minimal fallback replaced the error with `[omitted: call log artifact size limit exceeded]`, so an oversized artifact row showed nothing about WHY the request failed — e.g. 91 of 847 opencode-go 504 rows on one production instance were undiagnosable from the dashboard. Oversized request/response bodies are still omitted exactly as before; the error cap is independent of the payload sizes that tripped the fallback. (#12026) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **fix(sse):** OpenAI Responses clients that declare the native `web_search` tool now receive a spec-shaped `web_search_call` output item with `action.sources` alongside the preserved function-call round-trip, so search results executed through OmniRoute's own search backend are consumable by standard Responses clients (Codex, pi-web-access, …). ([#12031](https://github.com/diegosouzapw/OmniRoute/pull/12031)) — thanks @watchingdogs +- **fix(cli):** use in-thread alias resolver hooks on modern runtimes to avoid deprecation noise and improve Node.js forward compatibility ([#12073](https://github.com/diegosouzapw/OmniRoute/issues/12073)). ([#12083](https://github.com/diegosouzapw/OmniRoute/pull/12083)) — thanks @xiaoyaner0201 +- **fix(combo):** an operator-set **Agent Features → Context length** on a combo is now honored at request time. The value was persisted and advertised through `/v1/models`, but `resolveComboContextLimit()` never consulted it — so a multi-target combo whose members carry no per-model window fell through to the provider's generic `defaultContextLength` (openrouter 128000, command-code 200000) and rejected large requests with `Input exceeds context window … limit 128000` despite the combo being explicitly sized much larger. An identical single-target combo worked, because it collapses to its concrete target before the guard runs. Invalid values (0/negative/NaN/Infinity) are ignored, so the existing target → combo-min → fallback order is unchanged. ([#12090](https://github.com/diegosouzapw/OmniRoute/pull/12090)) — thanks @adivekar-utexas +- **fix(sse):** passthrough streams now estimate usage on finish when upstream closes without usage even with `stream_options.include_usage` — avoids `0 tokens / 0%` for providers that stay silent (and correctly handles trailing empty-choices usage) ([#12151](https://github.com/diegosouzapw/OmniRoute/pull/12151)) — thanks @maxmad64bis +- **fix(combos):** clearing an agent feature in the combos editor now persists — unchecking context cache protection, or emptying the system message or tool filter, sends an explicit `null` instead of dropping the field from the `PUT` body, which the update merge read as "leave unchanged" ([#12177](https://github.com/diegosouzapw/OmniRoute/pull/12177)) — thanks @foreveryh / @ntdatt812 +- **fix(memory):** self-hosted embedding endpoints now vectorize — the vector width is measured from the first embedding that comes back instead of being read from a registry that cannot describe them, so `vec_memories` is created and memories stop piling up unvectorized behind a green health check ([#12180](https://github.com/diegosouzapw/OmniRoute/pull/12180)) — thanks @kanade-hoshino / @ntdatt812 +- **fix(translator):** the leading `system` message now reaches Responses-API upstreams when its `content` is a content-part array — it was read as `typeof content === "string" ? content : ""`, so a prompt-caching client (Anthropic `cache_control`, the shape LiteLLM and the Anthropic SDK emit) had its entire system prompt replaced by an empty `instructions`. The request was still accepted with a normal `prompt_tokens` count, so the model answered with no instructions and nothing in the response said they were missing. Mid-conversation system turns already handled the array shape ([#7056](https://github.com/diegosouzapw/OmniRoute/pull/7056)); only the first one did not ([#12206](https://github.com/diegosouzapw/OmniRoute/issues/12206)). Regression guard: `tests/unit/translator-openai-responses-system-content-parts.test.ts`. ([#12207](https://github.com/diegosouzapw/OmniRoute/pull/12207)) — thanks @Gorillaz322 +- **fix(free-tier):** `/api/free-tier/summary` no longer computes its totals from a Radar feed built before the catalog the running release ships. When the cached feed is older — or carries no build date at all — the route answers from the shipped catalog, resolved through the operator's local model state so disabled and tombstoned models stay out of the numbers ([#12215](https://github.com/diegosouzapw/OmniRoute/pull/12215)). — thanks @maxmad64bis +- **fix(oauth):** Keep a Claude personal workspace and a Team organization as separate connections — they share the same email and `accountUUID`, so the email-only OAuth dedup let the second login overwrite the first account's tokens; `organizationUUID` now disambiguates them, the way `workspaceId` does for Codex ([#12222](https://github.com/diegosouzapw/OmniRoute/pull/12222)) — thanks @davidebaraldo +- **fix(resilience):** a 402 on a single paid model of a passthrough/gateway provider (e.g. `kilo-gateway`, `ollama-cloud`) no longer terminalizes the whole connection with a never-auto-recovered `credits_exhausted` status — only the paid model is locked out, so free models on the same key keep serving. 402 variant of [#3027](https://github.com/diegosouzapw/OmniRoute/issues/3027). Single-credential providers are unaffected — a 402 there is still treated as the key being genuinely out of credit ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239) / [#10616](https://github.com/diegosouzapw/OmniRoute/issues/10616)) ([#12242](https://github.com/diegosouzapw/OmniRoute/issues/12242)) — thanks @brick30llc-ctrl ([#12266](https://github.com/diegosouzapw/OmniRoute/pull/12266)) — thanks @geekyNads +- **fix(sse):** trust `finish_reason: "length"`/`"max_tokens"` over the reasoning-consumed-token ratio in response quality validation, so a reasoning model truncated below the old 90% threshold correctly fails and retries instead of returning empty content as a silent "success" ([#12262](https://github.com/diegosouzapw/OmniRoute/pull/12262)) — thanks @brick30llc-ctrl +- **fix(settings):** `PUT /api/settings/cache-config` now persists `alwaysPreserveClientCache` to the flat general settings the runtime cache-control policy actually reads; previously the value landed in the databaseSettings "cache" section and was silently ignored, so the endpoint had no effect on `cache_control` passthrough ([#12304](https://github.com/diegosouzapw/OmniRoute/pull/12304)) — thanks @davidebaraldo +- **fix(grok-cli):** treat omitted SuperGrokPro `creditUsagePercent` as 0% used so Provider Limits still renders a weekly bar (proto3 zero-elision) ([#12312](https://github.com/diegosouzapw/OmniRoute/pull/12312)) — thanks @HouMinXi +- **fix(combo):** Expose the two Auto-Combo scoring factors nobody could set — the combo validation schema and the dashboard weight sliders both declared 13 of the scorer's 15 factors, so `connectionDensity` (spreads load across a provider's connections) and `quality` were dropped on save and offered nowhere. The sliders also shipped their own default table that differed from the engine's on every non-zero factor and summed to 1.05, so the percentages shown next to them added up to 105%. Both lists now match `DEFAULT_WEIGHTS`, and a test keeps them there. Note that a combo whose stored `weights` omitted the two keys was effectively running with them at zero and the other thirteen renormalized upward; it now runs with the engine's intended distribution, so its routing does shift ([#12314](https://github.com/diegosouzapw/OmniRoute/pull/12314)) — thanks @maxmad64bis +- **fix(docs):** The free-tier reference no longer says its numbers come "confidence tagged per row" — no catalog entry carries a confidence tag and the API serves none, so every figure on that page is an estimate of the same, unstated quality. The page now states what an entry does vouch for: an independently documented hard stop (set by hand with the source in a comment, never defaulted to `true`) and a prompt-training disclosure, both with live counts the `check:docs-counts` gate keeps honest ([#12318](https://github.com/diegosouzapw/OmniRoute/pull/12318)) — thanks @maxmad64bis +- **fix(usage):** `adobe-firefly` and `firefly` have had a working usage fetcher since Adobe Firefly landed, but neither was ever added to the registration list, so the provider-plugin manifest, `genericQuotaFetcher` and the free-access quota cache all reported them as having no usage support — while `USAGE_SUPPORTED_PROVIDERS` said the opposite. Both are now declared, which also means their credit balance is fetched like any other declared provider's: `registerGenericQuotaFetchers` now registers a generic quota fetcher for them, and `resolveFreeAccessState` no longer returns early. A test holds the registration list to the dispatcher's switch in both directions, which is what the module's own docstring already asked for in prose ([#12321](https://github.com/diegosouzapw/OmniRoute/pull/12321)) — thanks @maxmad64bis +- **fix(quota):** drop the generic quota cache (agy / Antigravity / Claude OAuth) on an upstream 429 so reset-aware scoring does not keep a 60s stale snapshot, and force-refresh the next usage fetch so inner provider caches cannot recache the same window ([#12325](https://github.com/diegosouzapw/OmniRoute/pull/12325)) — thanks @HouMinXi +- **fix(combos):** deleting a combo now clears its persisted LKGP pins instead of leaving unreachable `key_value` rows behind ([#12326](https://github.com/diegosouzapw/OmniRoute/issues/12326)) ([#12425](https://github.com/diegosouzapw/OmniRoute/pull/12425)) — thanks @KrzysiekSko +- **fix(sse):** Keep ZWNJ (U+200C) and ZWJ (U+200D) in assistant text, reasoning and tool-call arguments — Persian/Kurdish half-space (`ارائه‌دهنده`), Arabic/Indic shaping and emoji sequences no longer lose them; the response de-obfuscation now removes joiners only between ASCII word characters, where the request side inserts them ([#12186](https://github.com/diegosouzapw/OmniRoute/issues/12186)) — thanks @rezjalibd ([#12359](https://github.com/diegosouzapw/OmniRoute/pull/12359)) — thanks @pacocartones +- **fix(resilience):** count resolved upstream 5xx results against the provider circuit breaker on the chat path — `CircuitBreaker.execute()` no longer reads a resolved `{ success: false, status: 5xx }` as a success that cancels the call-site failure, so a provider answering 503s now trips its breaker instead of staying `CLOSED` at `failureCount: 1`; single-model and combo dispatches are each accounted exactly once ([#12254](https://github.com/diegosouzapw/OmniRoute/issues/12254)) ([#12360](https://github.com/diegosouzapw/OmniRoute/pull/12360)) — thanks @pacocartones +- **fix(providers):** resolve the Codex quota auto-ping model from the live provider catalog and lifecycle registry instead of the retired `gpt-5.1-codex-mini`, and pause the ping with one actionable warning when no selectable Codex model exists rather than retrying a shut-down id every cooldown window ([#11905](https://github.com/diegosouzapw/OmniRoute/issues/11905)) ([#12361](https://github.com/diegosouzapw/OmniRoute/pull/12361)) — thanks @pacocartones +- **fix(api):** keep the `{created, data}` wrapper on combo-routed `/v1/images/generations` responses and default Codex image results to `b64_json` on both `/v1/images/generations` and `/v1/images/edits` so Codex CLI's built-in `image_gen` can decode them ([#12268](https://github.com/diegosouzapw/OmniRoute/issues/12268)) ([#12362](https://github.com/diegosouzapw/OmniRoute/pull/12362)) — thanks @pacocartones +- **fix(sse):** Name the shadowed custom provider node when a built-in provider id/alias (e.g. `openference` → `of`) reserves the prefix of an existing OpenAI/Anthropic-compatible node, so the runtime `No active credentials for provider: ` error explains that the prefix routed to the built-in and never reached the node's healthy connections, instead of contradicting the dashboard ([#11943](https://github.com/diegosouzapw/OmniRoute/issues/11943)) — thanks @morpheus9393 ([#12365](https://github.com/diegosouzapw/OmniRoute/pull/12365)) — thanks @pacocartones +- **fix(i18n):** wrap `ccOnboardingKeyPlaceholder` in ICU single quotes across all 43 locales so angle brackets render literally instead of being parsed as rich-text tags, which crashed the Claude Code onboarding block with `INVALID_MESSAGE: INVALID_TAG` ([#12302](https://github.com/diegosouzapw/OmniRoute/issues/12302)) ([#12369](https://github.com/diegosouzapw/OmniRoute/pull/12369)) — thanks @gonisulaimann +- **fix(models):** publish `effort_tiers` on Kimi K3's synced base-model entries (`kmca/k3`, `kmca/k3-256k`) so catalog-only clients (OpenCode, plain SDK pickers) can see and select the reasoning tiers (`low`/`high`/`max`) the synced metadata already carried — the `isSkippedEffortProvider` gate no longer suppresses tier visibility on those base entries, while synthetic `-` variant generation stays prevented and Codex/GLM base models remain excluded unchanged ([#12299](https://github.com/diegosouzapw/OmniRoute/issues/12299)) ([#12371](https://github.com/diegosouzapw/OmniRoute/pull/12371)) — thanks @gonisulaimann +- **fix(guardrails):** keep `auto`/`auto/*` virtual combos exempt from the Vision Bridge `fixedModel` credential guard so a combo target is passed through instead of silently falling back to global auto-selection ([#12237](https://github.com/diegosouzapw/OmniRoute/issues/12237)) ([#12373](https://github.com/diegosouzapw/OmniRoute/pull/12373)) — thanks @pacocartones +- **fix(combo):** capability-filter exhaustion caused by `max_tokens` above every target's known output limit now reports that reason (requested `max_tokens` vs the pool's highest known ceiling) instead of the unrelated "supports structured output" message ([#12229](https://github.com/diegosouzapw/OmniRoute/issues/12229)) — thanks @DW-MediaLab ([#12374](https://github.com/diegosouzapw/OmniRoute/pull/12374)) — thanks @pacocartones +- **fix(auth):** the `least-used` account strategy now prefers accounts without backoff before falling back to oldest `lastUsedAt`, the same tie-break `round-robin` already applies, so a failover no longer lands on a just-rate-limited account for a single request ([#12279](https://github.com/diegosouzapw/OmniRoute/issues/12279)) — thanks @tenshiak ([#12375](https://github.com/diegosouzapw/OmniRoute/pull/12375)) — thanks @pacocartones +- **fix(docker):** the `chatgpt-web-codex-browser` image now finds the Chrome binary under `chrome-linux64/` (Chrome for Testing layout in `playwright:v1.62.0-noble`) as well as the legacy `chrome-linux/`, so the container no longer crash-loops with `exec: --headless=new: not found` ([#12024](https://github.com/diegosouzapw/OmniRoute/issues/12024)) ([#12376](https://github.com/diegosouzapw/OmniRoute/pull/12376)) — thanks @pacocartones +- **fix(providers):** declare `groq/compound` and `allam-2-7b` as non-reasoning models in the curated Groq registry so `reasoning_effort` / `output_config.effort` / `thinking` from Claude Code are stripped instead of forwarded, which Groq rejected with HTTP 400 ([#12134](https://github.com/diegosouzapw/OmniRoute/issues/12134)) ([#12379](https://github.com/diegosouzapw/OmniRoute/pull/12379)) — thanks @pacocartones +- **fix(executors):** `OpencodeExecutor` no longer forces a direct connection when the connection has a proxy assigned in Proxy Management but no per-account proxies: the single-account fast path used to wrap the upstream dispatch in the direct-egress sentinel, discarding the ambient proxy context the chat handler had pinned from `proxy_assignments`, so API-key `opencode`/`opencode-go` connections egressed from the host IP (and hit geoblocks) despite the assignment. The direct pin is now applied only when no ambient proxy context exists ([#11894](https://github.com/diegosouzapw/OmniRoute/issues/11894) — thanks @hizzt) ([#12380](https://github.com/diegosouzapw/OmniRoute/pull/12380)) — thanks @pacocartones +- **fix(api):** `GET /v1/models` with `MODELS_CATALOG_PREFIX_MODE=canonical` (or `?prefix=canonical`) now lists providers whose registry alias is undefined or equal to their own id (Antigravity, Antigravity CLI and other self-aliased built-ins) — their single `provider/model` id was dropped by the alias/canonical duplicate guard in the static, synced, custom and alias-backed catalog loops ([#12058](https://github.com/diegosouzapw/OmniRoute/issues/12058)) — thanks @cheynetom ([#12381](https://github.com/diegosouzapw/OmniRoute/pull/12381)) — thanks @pacocartones +- **fix(translator):** Drop replayed `thinking` blocks that carry no signature (the shape produced from cross-provider `reasoning_content`) instead of stamping the default Claude signature on them, which Anthropic rejected with `400 Invalid signature in thinking block` on the next turn served by an Anthropic rung ([#12105](https://github.com/diegosouzapw/OmniRoute/issues/12105)) — thanks @atescivitci-cmd ([#12386](https://github.com/diegosouzapw/OmniRoute/pull/12386)) — thanks @pacocartones +- **fix(cli):** Resolve Bun's `--preload` polyfill path against the package root instead of `dist/`, so `omniroute` installed with `bun install -g` no longer crashes at startup with `error: preload not found …/dist/open-sse/utils/setupPolyfill.ts` ([#11980](https://github.com/diegosouzapw/OmniRoute/issues/11980)) — thanks @joglomedia ([#12387](https://github.com/diegosouzapw/OmniRoute/pull/12387)) — thanks @pacocartones +- **fix(providers):** `gemini-business` now publishes its model catalog — `/v1/models` and `/v1/providers/gemini-business/models` list the 12 enterprise Gemini ids the executor understands instead of returning an empty list (#12107) ([#12389](https://github.com/diegosouzapw/OmniRoute/pull/12389)) — thanks @pacocartones +- **fix(db):** install `busy_timeout` before the SQLite connection's first statement so a process opening the database while another one closes its WAL connection waits out the transient EXCLUSIVE lock instead of dying with `database is locked`, and recognise the drivers' real BUSY/PROTOCOL/IOERR errors as transient in the corruption probe so the same lock no longer renames the database away as corrupt; deflakes `cross-process contenders never both acquire the same connection` (#12394 — thanks @pacocartones) +- **fix(chat-admission):** derive the `chat_admission_busy` 503 `Retry-After` from observed heavyweight-lease occupancy — the larger of the exhausted `OMNIROUTE_CHAT_ADMISSION_QUEUE_MS` window and the time since capacity last turned over, capped at 60 s — instead of a fixed 1 s (structural) / 2 s (byte-stage) hint that invited Codex/agent fan-out clients to re-send ~1 MiB `/v1/responses` bodies every second into a gate held for the whole SSE lifetime; an idle gate keeps the historical floors ([#12135](https://github.com/diegosouzapw/OmniRoute/issues/12135)) (#12395 — thanks @pacocartones) +- **fix(api-manager):** the API key permissions modal no longer silently drops `allowedCombos` entries its Combo picker cannot render — routing-rule names such as `rt-*`, which the backend already honours — when "All" is clicked and the key is switched back to "Restrict"; those entries now survive the toggle, are listed read-only under the combo list so the count and the list agree, and are saved back verbatim instead of persisting `[]` (deny-all) (#12397 — thanks @pacocartones) +- **fix(catalog):** write the NUL separator of the catalog connection memo key, the provider serviceKind memo key, the Video Bridge promotion group key and a JSON-exactness test fixture as the `\u0000` escape instead of a raw byte — same runtime value, but the raw byte made git, GitHub and ripgrep treat those files as binary (hidden PR diffs, silently skipped searches); a guard test now keeps raw NUL bytes out of `src/`, `open-sse/` and `tests/` (#12403 — thanks @pacocartones) +- **fix(providers):** add `CLAUDE_CODE_CLIENT_VERSION` and `GITHUB_COPILOT_CLI_VERSION` env overrides so Anthropic/Copilot client-version gates can be unblocked without a rebuild ([#12417](https://github.com/diegosouzapw/OmniRoute/issues/12417)) ([#12632](https://github.com/diegosouzapw/OmniRoute/pull/12632)) — thanks @HouMinXi +- **fix(db):** back-fill `last_ping_at` and `last_pinged_reset_key` on `provider_connections` during schema reconciliation so divergent lineages that skipped `123_quota_auto_ping` still accept quota auto-ping writes ([#12470](https://github.com/diegosouzapw/OmniRoute/pull/12470) — thanks @KooshaPari) +- **fix(ci):** document MIT exceptions for `@eloqnt/{config,format-json,format-po}` (next-intl transitive; locked tarballs omit `license`) and keep the A2A lifecycle vitest off the real SQLite persistence seam ([#12581](https://github.com/diegosouzapw/OmniRoute/issues/12581)) ([#12595](https://github.com/diegosouzapw/OmniRoute/pull/12595)) +- OpenCode plugin `/v1/models` catalog fetch now waits 30s by default and attaches HTTP `statusCode` on 401/5xx so host fallback plugins can hop instead of seeing an untyped AbortError/UnknownError. ([#12607](https://github.com/diegosouzapw/OmniRoute/pull/12607)) — thanks @RaviTharuma +- **CI:** the OpenAPI security-tier gate now mirrors `isAlwaysProtectedPath()` in full — it also reads `ALWAYS_PROTECTED_API_PATTERNS`, so the pattern-gated credential routes (`/api/providers/{id}/{claude,codex}-auth/{export,apply-local}`, GHSA-5926-2w35-7h4q) no longer report as unannotated. (#12605) +- **fix(api):** GET /v1/models no longer waits forever on a hung coalesced catalog rebuild; cold-path waits are bounded (`CATALOG_BUILD_TIMEOUT_MS`, default 8s) and a last-good 200 is served when the rebuild times out ([#12627](https://github.com/diegosouzapw/OmniRoute/issues/12627)). ([#12628](https://github.com/diegosouzapw/OmniRoute/pull/12628)) — thanks @RaviTharuma +- **fix(cursor):** a non-array `tool_calls` on an assistant message no longer crashes the cursor request translator with a `TypeError`; both loops now require an array ([#12689](https://github.com/diegosouzapw/OmniRoute/issues/12689)) ([#12691](https://github.com/diegosouzapw/OmniRoute/pull/12691)) — thanks @soroush5 +- **fix(chat):** Chat Completions no longer return empty `content` after a server-owned memory or skills tool; the first provider send and account/model recovery now share one pipeline so a follow-up round-trip can fill the reply ([#12696](https://github.com/diegosouzapw/OmniRoute/issues/12696)) — thanks @HouMinXi ([#12867](https://github.com/diegosouzapw/OmniRoute/pull/12867)) +- **fix(dashboard):** batch-deleting provider keys no longer toasts failure after a successful delete when the confirm button's click event is forwarded as `onAfter` ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) — thanks @HouMinXi +- **fix(glm):** drop the extra 16th argument to `createSSETransformStreamWithLogger` that TypeScript rejected (TS2554) and that never reached the TransformStream ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) ([#12770](https://github.com/diegosouzapw/OmniRoute/pull/12770)) — thanks @HouMinXi +- **fix(dashboard):** pass `nodeMap` into Runtime `QuotaGroup` so a quota monitor in error/exhausted/alerting no longer throws `ReferenceError: nodeMap is not defined`. ([#12868](https://github.com/diegosouzapw/OmniRoute/pull/12868)) — thanks @HouMinXi +- Restricted API keys whose `allowedModels` lists a combo name no longer skip every combo member at pre-dispatch (`ALL_TARGETS_SKIPPED`). Inner-target filtering still applies when the allow-list is a provider prefix or `disableNonPublicModels` is on ([#12899](https://github.com/diegosouzapw/OmniRoute/pull/12899)). — thanks @HouMinXi +- **fix(combos):** Effort-suffixed combo members inherit the base model's `model_context_overrides` row so priority order is not inverted on large requests ([#12926](https://github.com/diegosouzapw/OmniRoute/pull/12926)) — thanks @HouMinXi +- **fix(catalog):** Picker-added `customModels` enter the dispatch-time live catalog so combo members and bare inference no longer 400 ([#12934](https://github.com/diegosouzapw/OmniRoute/pull/12934)) — thanks @HouMinXi +- **fix(docker):** re-derive the Docker build's worker-pool memory budget from the MEASURED ~4.5 GB per-process RSS (the issue owner's own VPS dmesg OOM-killer reproduction) instead of the stale 2560 MB/worker inference #11419 shipped, and lower `OMNIROUTE_BUILD_WORKERS` 3 → 2 so 1 parent + 1 page-data worker (2 processes × 4.5 GB = 9 GB) fits the 12.288 GB (75%) budget on a 16 GB GitHub Actions runner — the previous default (1 parent + 2 workers = 13.5 GB) still overcommitted the runner and kept "Publish to Docker Hub" failing with `cannot allocate memory` after #11419 merged (#7518). ([#11663](https://github.com/diegosouzapw/OmniRoute/pull/11663)) +- **fix(electron):** the packaged Windows desktop build now passes the #7592 cold-restart smoke end to end. Five defects were found and fixed on the way: (1) optional-pack staging failed on any machine whose `tar` is GNU tar — it reads the drive letter in an absolute `-f C:\...` archive path as a remote rsh host (`Cannot connect to C:`), so staging now passes a bare filename with `cwd` at the tarball directory and surfaces tar stderr on failure; (2) the Electron `files` allowlist was missing `lib/loginHeaderCapture.js`, so the packaged main process crashed at startup with `Cannot find module './lib/loginHeaderCapture'`; (3) electron-builder ≥26 injects an `!**/node_modules/**` ignore into every extraResources pattern list that no positive filter can override, which silently dropped the staged runtime `node_modules` (including the better-sqlite3 N-API prebuild) from `resources/app` — a fresh v3.8.50 desktop build would have shipped with no native SQLite driver and reproduced the #7592 sql.js fallback on every machine; an `afterPack` hook now restores the staged `node_modules` after packing; (4) the packaged-app smoke harness redirected `USERPROFILE` into its temp DATA_DIR, but Electron resolves the Roaming profile from `%USERPROFILE%\AppData\Roaming\` (USERPROFILE takes precedence over the APPDATA env var) and the path service throws instead of creating it, so `requestSingleInstanceLock()` returned false and the app exited(0) silently before `app.whenReady()` — the harness now pre-creates the derived tree, and `ensureSmokeEnvDirs` is exported and unit-tested; (5) the #7592 driver assertion parsed a `[DB] Driver: ...` line that the server's primary DB path never printed (only the unused `openDatabaseAsync()` did) — `getDbInstance()` now emits the same line on open so the guard can actually assert the native driver. Also: the smoke env-allowlist unit test hardcoded POSIX paths and could only pass on Linux/macOS; expectations are now host-agnostic, with new regression tests for the USERPROFILE-derived tree and `tarPack` under absolute Windows-style paths ([#11443](https://github.com/diegosouzapw/OmniRoute/pull/11443)) — thanks @jonlwheat2-gif +- fix(sse): stop the auto-combo candidates inspector from silently dropping model-locked/cooled-down rows (#9133) ([#11994](https://github.com/diegosouzapw/OmniRoute/pull/11994)) — thanks @hartmark +- **fix(providers):** Perplexity Web no longer turns upstream stream failures into successful assistant text; pre-content failures remain eligible for fallback, partial output ends with a structured sanitized error, and failed sessions are not persisted ([#12465](https://github.com/diegosouzapw/OmniRoute/pull/12465)) +- **fix(video-bridge):** preserve chronological video and transcript fusion, source-frame timestamps, transcript provenance, full contact-sheet intervals, and pre-abort callback isolation ([#11681](https://github.com/diegosouzapw/OmniRoute/pull/11681)) +- **Z.ai Web:** HTTP 200 streams carrying an upstream error now terminate with a structured failure instead of assistant text plus a normal stop, preserving partial output while allowing pre-content combo fallback. ([#12454](https://github.com/diegosouzapw/OmniRoute/pull/12454)) +- Absorb `Error [AbortError]: request_signal_aborted` and DOMException AbortError shapes in the process-level client-abort crash guard so routine client disconnects no longer kill the server (exit code 7). ([#12165](https://github.com/diegosouzapw/OmniRoute/pull/12165)) — thanks @alvinveroy +- **fix(sse):** Treat Adapta Web `type:error` SSE events as sanitized non-stream failures instead of empty HTTP 200 completions. ([#12459](https://github.com/diegosouzapw/OmniRoute/pull/12459)) +- **fix(autoCombo):** layer 3 (`models_dev_tier`) no longer scores retired models as premium, merge cross-provider capability rows deterministically, and inherit tier scores through variant ids ([#11508](https://github.com/diegosouzapw/OmniRoute/issues/11508)). `getModelsDevTierFitness` now vetoes ids present in `config/quality/model-lifecycle.json` with status `retired` before any other signal; `loadModelCapabilities()` aggregates per `model_id` (any-non-null-true for booleans, max for `limit_context`) instead of last-write-wins over SQLite's undefined row order; a capabilities miss retries through `resolveScoresAs()`'s base id and reports `models_dev_tier:inherited`, matching layers 1–2 (#11492). Adds `getModelsDevTierFitnessWithSource()` and an `invalidateCapabilitiesCache()` test/ops hook. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) — thanks @Meet6338-X +- fix(sse): accept Claude `content:[]` + `stop_reason: max_tokens`/`tool_use` in `detectMalformedNonStream` (match `isEmptyContentResponse`) so Claude Code `/model` probes with `max_tokens: 1` no longer become a false 502 ([#11568](https://github.com/diegosouzapw/OmniRoute/pull/11568)) — thanks @Seramicx +- **fix(security):** sanitize `request.failed` diagnostics before publishing them to live dashboard listeners and replay history, while keeping status, model, provider, latency, and internal call-log diagnostics intact. ([#12469](https://github.com/diegosouzapw/OmniRoute/pull/12469)) +- **fix(pwa):** do not intercept dashboard navigations so Chrome can fall back from a stale HTTP/3 Alt-Svc advertisement (UDP :20128 is unpublished; F5 on a long-lived tab hung until a new tab opened a fresh TCP connection). ([#12767](https://github.com/diegosouzapw/OmniRoute/pull/12767)) — thanks @HouMinXi +- **fix(executors):** handle DuckDuckGo ERR_BN_LIMIT (418) without retrying — when the upstream returns `418 ERR_BN_LIMIT` (rate-limit/ban), the executor now returns the error immediately instead of burning another VQD acquisition that would only count against the IP limit. The retry logic for `418 ERR_CHALLENGE` (unsolved challenge) remains unchanged. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) ([#12110](https://github.com/diegosouzapw/OmniRoute/pull/12110)) — thanks @oyi77 / @Meet6338-X +- Fixed DuckDuckGo streaming responses losing JSON lines and UTF-8 characters split across network chunks. ([#11528](https://github.com/diegosouzapw/OmniRoute/pull/11528)) — thanks @pacocartones +- Harden generated Cloudflare, Vercel, and Deno relays by stripping hop-by-hop, framing, and proxy authentication headers before forwarding requests upstream. ([#11533](https://github.com/diegosouzapw/OmniRoute/pull/11533)) — thanks @pacocartones +- **feat(grok-cli):** Provider Limits shows grok-cli banked reset credits from `GetRemainingResets` (including a real zero; a failed RPC omits the row) and the existing View credits button now calls `ConsumerUiSvc/RedeemReset` for grok-cli. Live tokens use nested fields 10/20/30 (id + Timestamp), not compact 1/2/3. ([#12805](https://github.com/diegosouzapw/OmniRoute/pull/12805)) — thanks @HouMinXi +- **fix(memory):** Embedding Model Quick select, Embedding Source remote dropdown, and Rerank selector now list every configured provider with embedding/rerank support instead of only chat-catalog text matches plus OpenRouter live discovery; a generic OpenAI-compatible `/embeddings` + Cohere-compatible `/rerank` runtime fallback resolves any configured chat provider's embedding/rerank endpoint, so unlisted providers no longer fail with "Unknown embedding provider"; both memory selectors gained a free-text model override ([#11390](https://github.com/diegosouzapw/OmniRoute/pull/11390)) — thanks @rqzbeh +- Harden SQLite upgrades around the historical migration-074 version collision: missing discovery and inspector tables are replayed atomically, pre-existing databases (including setup-created skeletons) receive reusable content-addressed safety snapshots, and Node test/eval probes without `DATA_DIR` are isolated from the operator database. ([#12435](https://github.com/diegosouzapw/OmniRoute/pull/12435)) +- **fix(dashboard):** Moonshot/Kimi Open Platform voucher and cash leftover percentages follow the bucket balance, so an empty wallet no longer paints those rows as 100% while Available is 0% ([#12733](https://github.com/diegosouzapw/OmniRoute/pull/12733)) — thanks @HouMinXi +- **fix(combo):** return non-retryable HTTP 400 when all candidates for a pinned native Codex turn are unavailable due to model-scoped lockout, terminating the turn cleanly while preserving turn continuity and enabling standard Combo routing on subsequent turns ([#12240](https://github.com/diegosouzapw/OmniRoute/pull/12240)) — thanks @mdigitalbh81 +- Fixed auto-router strategies preserving the selected connection through ranking and dispatch when multiple connections share the same provider and model. ([#11530](https://github.com/diegosouzapw/OmniRoute/pull/11530)) — thanks @pacocartones +- Preserve Chat Completions JSON-object response formats when translating requests to the Responses API. ([#11529](https://github.com/diegosouzapw/OmniRoute/pull/11529)) — thanks @pacocartones +- Fix idempotency fingerprints so Responses API requests include `input` and other semantic generation fields using stable serialization, while excluding credentials and request metadata. ([#11532](https://github.com/diegosouzapw/OmniRoute/pull/11532)) — thanks @pacocartones +- **fix(api):** Generated API CLI commands now enforce required OpenAPI request bodies; Combo test commands forward the required `comboName` body, while API keys created by older writers after migration 149 preserve legacy allow-all Combo access without widening explicit empty allowlists — thanks @marcelokarval ([#12070](https://github.com/diegosouzapw/OmniRoute/pull/12070)) +- **fix(grok-web):** treat upstream streaming failures as failures instead of successful assistant text: error-only streams now fail readiness with HTTP 502, while failures after legitimate content preserve that partial output and terminate through the sanitized stream failure path without a normal `stop` completion. ([#12458](https://github.com/diegosouzapw/OmniRoute/pull/12458)) +- HuggingChat now turns HTTP 200 JSONL generation failures into a sanitized 502 before content, or a fixed public stream failure after partial output, so fallback and request persistence no longer record a false successful stop. ([#12456](https://github.com/diegosouzapw/OmniRoute/pull/12456)) +- **fix(providers):** keep 1min.ai HTTP 200 stream errors out of assistant content, preserve partial output, and expose sanitized terminal errors so pre-content failures can fall back. ([#12466](https://github.com/diegosouzapw/OmniRoute/pull/12466)) +- **fix(sessions):** preserve legacy Sessions data when the additive exclusive-session projection is unavailable, returning an empty projection and warning only once per contiguous outage instead of failing the endpoint; the Sessions badge intentionally reflects the merged legacy and exclusive row count introduced by [#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389) ([#11469](https://github.com/diegosouzapw/OmniRoute/pull/11469)) — thanks @KaspaPulse +- Render Nimble Search with the generic provider icon and serve Opper's proven logo locally. ([#12415](https://github.com/diegosouzapw/OmniRoute/pull/12415)) +- **fix(quality):** `config/quality/provider-assets-provenance.jsonl` was stale against the physical `public/providers/` tree (still listing 225 assets and 5 duplicate-content groups after several provider-retirement PRs removed 85 icon files), which would have made `check:provider-asset-provenance` fail on the next full run. Reconciled the manifest to the real 142-file snapshot (142 asset records, 1 duplicate-content group, 2 newly-registered files marked `unresolved` pending provenance research), repinned `auditedCommit`, and updated the regression test's hardcoded counts to match. ([#11876](https://github.com/diegosouzapw/OmniRoute/pull/11876)) +- fix(ui): wrap quota card footer action buttons to prevent clipping Refresh button on narrow cards — when extra action buttons such as `View credits` are present on Codex cards, allow the footer row to flex-wrap with `shrink-0` buttons so `Refresh now` remains visible and clickable across all responsive card widths ([#11464](https://github.com/diegosouzapw/OmniRoute/pull/11464)) — thanks @adevwithpurpose +- Keep Antigravity Gemini usable when the same connection's Claude weekly quota is empty; generic quota cache stays per-connection for every other provider. ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) — thanks @HouMinXi +- Fixed Tailscale tunnel setup to reject ports outside the valid 1-65535 range and safely install Windows MSI packages from paths containing apostrophes. ([#11534](https://github.com/diegosouzapw/OmniRoute/pull/11534)) — thanks @pacocartones +- Classify Cloudflared and Tailscale process-management routes as local-only and spawn-capable while preserving remote access to read-only tunnel status endpoints. ([#11531](https://github.com/diegosouzapw/OmniRoute/pull/11531)) — thanks @pacocartones +- Electron release workflow: the `publish-npm` job now grants `actions: read` to the reusable `npm-publish.yml` it calls (its `publish` job requests it), which is what made GitHub refuse the whole v3.8.50 run at startup and ship the release with zero desktop assets; a `workflow_dispatch` now builds the requested tag instead of the dispatching branch and can skip the npm leg (`publish_npm=false`) when only re-attaching assets ([#11974](https://github.com/diegosouzapw/OmniRoute/pull/11974)) +- **fix(dashboard):** The Combos page usage guide now reads its dismissal through `useSyncExternalStore` instead of correcting SSR state inside an effect, removing an extra commit of the page tree on every load (and the `react-hooks/set-state-in-effect` error it raised). ([#12671](https://github.com/diegosouzapw/OmniRoute/pull/12671)) +- Electron release: `electron/package-lock.json` regained the optional `electron-builder-squirrel-windows` subtree (13 entries) that `npm ci` had been refusing as out of sync, `electron-release.yml` gained a `build_ref` dispatch input and stops regenerating release notes on a re-attach dispatch, and the npm publish workflow attaches the SBOM to the GitHub Release on dispatch publishes too — so the v3.8.51 tag ships every desktop asset and the SBOM like v3.8.49 did ([#12022](https://github.com/diegosouzapw/OmniRoute/pull/12022)) +- Drained the reds every PR against `release/v3.8.51` was born with: documented the three service-worker build-id variables, registered the six retirement/tunnel tests with the mutation gate, approved `eslint-plugin-react-hooks` in the dependency allowlist, added the six `combo.sort.*` strings to `vi` and `pt-BR`, pointed the ChatGPT Web doc at the real migration-168 test, worded the g4f hint around the member key, and realigned four tests to the retired-provider catalog and the legacy-schema fixtures the retirement migrations touch. ([#11940](https://github.com/diegosouzapw/OmniRoute/pull/11940)) +- **fix(providers):** Claude, Grok, LMArena, Notion, and Perplexity web-cookie transports now use pooled `wreq-js` 3.2 instead of the native sidecar, with all nine supported bindings pinned and audited, and the applicable platform binding plus native-license evidence included in each release artifact ([#12429](https://github.com/diegosouzapw/OmniRoute/pull/12429), supersedes [#11753](https://github.com/diegosouzapw/OmniRoute/pull/11753)). +- **fix(providers):** Zed Hosted streaming failures now trigger fallback before content and end partial streams with a sanitized structured error instead of fake assistant text and a normal-success stop. ([#12455](https://github.com/diegosouzapw/OmniRoute/pull/12455)) +- **fix(test):** capture hook result after render (direct commit `b85d0abb0b`) +- **fix(sync):** repair the two regressions the v3.8.50 sync-back left on release/v3.8.51 (direct commit `5ade9e0851`) +- **revert(models):** drop the memo reset hook ported from #10055 (direct commit `282de841ae`) +- **fix(typecheck):** guard the optional connectionId before the reactive model sync (direct commit `f2825bad75`) +- **fix(quota-share):** release the winner's reserved in-flight slot (#11371) ([#11408](https://github.com/diegosouzapw/OmniRoute/pull/11408)) — thanks @oyi77 +- **fix(providers):** stop silently dropping reasoning effort on opencode families (#10788) ([#11409](https://github.com/diegosouzapw/OmniRoute/pull/11409)) — thanks @oyi77 +- **fix(combo):** enable genuine quota-aware routing for generic providers (antigravity, claude, etc.) ([#11411](https://github.com/diegosouzapw/OmniRoute/pull/11411)) — thanks @HouMinXi +- **fix(oauth):** allow expired token retry before deactivation and activate expired retry loop (#P0) ([#11414](https://github.com/diegosouzapw/OmniRoute/pull/11414)) — thanks @HouMinXi +- **perf(executors):** lazy-load the executor registry — defer class imports + construction to first use (#11220) ([#11421](https://github.com/diegosouzapw/OmniRoute/pull/11421)) — thanks @oyi77 +- **fix:** make release/v3.8.50 CI gates green (run 32786966560 root causes) ([#11450](https://github.com/diegosouzapw/OmniRoute/pull/11450)) — thanks @jonlwheat2-gif +- **fix(sse):** exact-domain cookie match and origin-equality URL assertions ([#11452](https://github.com/diegosouzapw/OmniRoute/pull/11452)) — thanks @hartmark +- **fix(providers):** send Responses API payload during OpenAI-compatible validation ([#11454](https://github.com/diegosouzapw/OmniRoute/pull/11454)) — thanks @tuandinh0801 +- **fix(command-code):** fallback to /alpha/generate for Go plan without Provider API access ([#11455](https://github.com/diegosouzapw/OmniRoute/pull/11455)) — thanks @Chewji9875 +- **fix(responses-continuation):** fail closed on a log-truncated stored input/output array ([#11473](https://github.com/diegosouzapw/OmniRoute/pull/11473)) — thanks @hartmark +- **fix(cli):** write jcode settings as [providers.omniroute] in config.toml ([#11484](https://github.com/diegosouzapw/OmniRoute/pull/11484)) — thanks @hongnoul +- **fix(logging):** raise chat-log array truncation cap to 1000 and unify duplicate implementations ([#11499](https://github.com/diegosouzapw/OmniRoute/pull/11499)) — thanks @hartmark +- **fix:** restore live websocket URL helpers ([#11509](https://github.com/diegosouzapw/OmniRoute/pull/11509)) — thanks @PauloFH +- **perf(core):** eliminate async waterfalls across home, cache, and db operations (#11396) ([#11512](https://github.com/diegosouzapw/OmniRoute/pull/11512)) — thanks @HouMinXi +- **fix(kiro):** add runtime.us-east-1.kiro.dev as first-attempt endpoint ([#11517](https://github.com/diegosouzapw/OmniRoute/pull/11517)) — thanks @rifqiawl +- **fix(providers):** remove duplicate 128-tool truncation in OpencodeExecutor (#11444) ([#11518](https://github.com/diegosouzapw/OmniRoute/pull/11518)) — thanks @HouMinXi +- **fix(providers):** poll onboardUser LRO before classifying BYOP ([#11519](https://github.com/diegosouzapw/OmniRoute/pull/11519)) — thanks @rifqiawl +- **fix(translator):** preserve exact tool name casing for Claude tool_use payloads (#11487) ([#11520](https://github.com/diegosouzapw/OmniRoute/pull/11520)) — thanks @HouMinXi +- **fix(kimi-web):** align health probe with executor domain ([#11521](https://github.com/diegosouzapw/OmniRoute/pull/11521)) — thanks @pacocartones +- **fix(cli):** preserve existing admin password during setup ([#11522](https://github.com/diegosouzapw/OmniRoute/pull/11522)) — thanks @pacocartones +- **fix(mitm):** keep Windows DNS elevation off the POSIX sudo path (#11430) ([#11542](https://github.com/diegosouzapw/OmniRoute/pull/11542)) — thanks @official-burak +- **fix(sse):** scale chat admission by ingest byte budget instead of a fixed request count ([#11548](https://github.com/diegosouzapw/OmniRoute/pull/11548)) — thanks @TheDemonTuan +- **fix:** accept Gemini Web cookie exports ([#11554](https://github.com/diegosouzapw/OmniRoute/pull/11554)) — thanks @rafacpti23 +- **fix(i18n):** complete Brazilian Portuguese translations ([#11558](https://github.com/diegosouzapw/OmniRoute/pull/11558)) — thanks @rafacpti23 +- **fix(sse):** explicit types for openai-responses pureHelpers — clears last failing core typecheck gate ([#11567](https://github.com/diegosouzapw/OmniRoute/pull/11567)) — thanks @jonlwheat2-gif +- **fix(opencode):** hydrate Proxy Pool references for no-auth connections ([#11584](https://github.com/diegosouzapw/OmniRoute/pull/11584)) — thanks @AStupidBear +- **fix(ci):** resolve inherited release/v3.8.51 base-red CI failures ([#11608](https://github.com/diegosouzapw/OmniRoute/pull/11608)) — thanks @jonlwheat2-gif +- **fix(db):** fallback load STORAGE_ENCRYPTION_KEY from env files ([#11614](https://github.com/diegosouzapw/OmniRoute/pull/11614)) — thanks @benzntech +- **fix(providers):** mark Brave Search as no longer having a free tier ([#11621](https://github.com/diegosouzapw/OmniRoute/pull/11621)) — thanks @turbolego +- **fix:** pass tool result content through as string in OpenAI/Claude -> Gemini translation ([#11624](https://github.com/diegosouzapw/OmniRoute/pull/11624)) — thanks @Hsia97 +- **fix(sse):** scope credential health to exact key ([#11628](https://github.com/diegosouzapw/OmniRoute/pull/11628)) — thanks @KaspaPulse +- **fix(install):** guard prepare script against missing husky ([#11634](https://github.com/diegosouzapw/OmniRoute/pull/11634)) — thanks @Prajeeth-12 +- **fix(security):** redact NVIDIA_API_KEY at the log sink in the ad-hoc diag script ([#11639](https://github.com/diegosouzapw/OmniRoute/pull/11639)) +- **fix(types):** clear the two type regressions red on release/v3.8.51 ([#11640](https://github.com/diegosouzapw/OmniRoute/pull/11640)) — thanks @jonlwheat2-gif +- **fix(build):** resolve workspace: protocol dependencies to concrete semver versions during prepublish (#11540) ([#11641](https://github.com/diegosouzapw/OmniRoute/pull/11641)) — thanks @HouMinXi +- **fix(search):** properly resolve configured search connection in /v1/responses pipeline (#11524) ([#11642](https://github.com/diegosouzapw/OmniRoute/pull/11642)) — thanks @HouMinXi +- **fix(authz):** trust cf-connecting-ip for via-proxy detection when peer is Cloudflare edge (#11514) ([#11643](https://github.com/diegosouzapw/OmniRoute/pull/11643)) — thanks @HouMinXi +- **fix(security):** compare the OIDC state cookie in constant time ([#11646](https://github.com/diegosouzapw/OmniRoute/pull/11646)) +- **fix(security):** namespace the dedup hash by the calling API key ([#11649](https://github.com/diegosouzapw/OmniRoute/pull/11649)) +- **fix(models):** gate the Codex-native catalog loop on prefix mode (#11632) ([#11662](https://github.com/diegosouzapw/OmniRoute/pull/11662)) — thanks @xiaoyaner0201 +- **fix(dashboard):** project Codex routing quota in account pools ([#11666](https://github.com/diegosouzapw/OmniRoute/pull/11666)) — thanks @KaspaPulse +- **fix(ddgw):** normaliza system no upstream da release v3.8.51 ([#11692](https://github.com/diegosouzapw/OmniRoute/pull/11692)) — thanks @RhianB14 +- **fix(memory):** honor category filter in GET /api/memory ([#11699](https://github.com/diegosouzapw/OmniRoute/pull/11699)) — thanks @Sabeekhann +- **fix(ci):** detect stale workspace lockfile entries ([#11714](https://github.com/diegosouzapw/OmniRoute/pull/11714)) — thanks @pacocartones +- **fix(ci):** stamp dist/BUILD_SHA before the npm publish provenance gate ([#11721](https://github.com/diegosouzapw/OmniRoute/pull/11721)) +- **fix(docker):** decouple the best-effort Bun image from the release manifest ([#11724](https://github.com/diegosouzapw/OmniRoute/pull/11724)) +- **perf(compression):** avoid cloning memo misses twice ([#11727](https://github.com/diegosouzapw/OmniRoute/pull/11727)) — thanks @RaviTharuma +- **fix(docker):** let the bun digest artifact be absent, not fatal ([#11740](https://github.com/diegosouzapw/OmniRoute/pull/11740)) +- **fix(shared):** block cloud-metadata hosts under default remote-image guard ([#11755](https://github.com/diegosouzapw/OmniRoute/pull/11755)) — thanks @santosraju99-hub +- **fix(api):** preserve caller-provided X-Correlation-Id on chat completions ([#11760](https://github.com/diegosouzapw/OmniRoute/pull/11760)) — thanks @aniruddhaadak80 +- **fix(resilience):** lock permanently retired models instead of short backoff (Gemini ban prevention) ([#11762](https://github.com/diegosouzapw/OmniRoute/pull/11762)) — thanks @turbolego +- **fix:** mark Vercel AI Gateway as passthroughModels ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **fix(resilience):** stop hammering permanently-moved endpoints and billing-suspended accounts ([#11774](https://github.com/diegosouzapw/OmniRoute/pull/11774)) — thanks @turbolego +- **fix(lease):** remove global static reservation and gate routing on live active lease occupancy ([#11775](https://github.com/diegosouzapw/OmniRoute/pull/11775)) — thanks @TheDemonTuan +- **fix(ci):** size the install-upgrade gate to a measured run, and log the pack cost ([#11776](https://github.com/diegosouzapw/OmniRoute/pull/11776)) +- **fix(pwa):** stop serving the stale shell after deploys ([#11779](https://github.com/diegosouzapw/OmniRoute/pull/11779)) — thanks @HouMinXi +- **fix(combo):** lock GitHub models rejected as "not supported" for future requests ([#11781](https://github.com/diegosouzapw/OmniRoute/pull/11781)) — thanks @turbolego +- **fix(cli):** probe both IPv4 and IPv6 loopback for server readiness (#11766) ([#11794](https://github.com/diegosouzapw/OmniRoute/pull/11794)) — thanks @vermasomesh835 +- **fix(sse):** preserve CLIProxyAPI wrappers per connection ([#11797](https://github.com/diegosouzapw/OmniRoute/pull/11797)) — thanks @RaviTharuma +- **fix(providers):** honor model auto-fetch opt-in on create ([#11798](https://github.com/diegosouzapw/OmniRoute/pull/11798)) — thanks @RaviTharuma +- **fix(dashboard):** make model auto-fetch order independent ([#11805](https://github.com/diegosouzapw/OmniRoute/pull/11805)) — thanks @RaviTharuma +- **fix(docker):** make GHA cache exports best effort ([#11806](https://github.com/diegosouzapw/OmniRoute/pull/11806)) — thanks @RaviTharuma +- **fix(kiro):** do not permanently ban on 'User is not authorized to make this call' ([#11809](https://github.com/diegosouzapw/OmniRoute/pull/11809)) — thanks @Deftera186 +- **fix(sse):** keep cache-write tokens in OpenAI-shaped usage ([#11814](https://github.com/diegosouzapw/OmniRoute/pull/11814)) — thanks @TheDemonTuan +- **fix(docs):** resolve relative markdown and wiki links across Fumadocs and GitHub wiki ([#11834](https://github.com/diegosouzapw/OmniRoute/pull/11834)) — thanks @AndrianBalanescu +- **fix(chat):** preserve unstripped model string for passthrough provider routing ([#11840](https://github.com/diegosouzapw/OmniRoute/pull/11840)) — thanks @raheemuddin786 +- **fix(mcp):** avoid async initialization inside sync esm wrappers (#11569) ([#11849](https://github.com/diegosouzapw/OmniRoute/pull/11849)) — thanks @HouMinXi +- **fix(sse):** preserve coherent markdown token boundaries across stream translation chunks (#11606) ([#11851](https://github.com/diegosouzapw/OmniRoute/pull/11851)) — thanks @HouMinXi +- **fix(ci):** run the install-upgrade gate on disk, not on the /tmp tmpfs ([#11855](https://github.com/diegosouzapw/OmniRoute/pull/11855)) +- **fix(skills):** expand shorthand property types in injected tool schemas ([#11857](https://github.com/diegosouzapw/OmniRoute/pull/11857)) — thanks @alvinveroy +- **fix(ci):** publish npm from a hosted runner so provenance is accepted ([#11877](https://github.com/diegosouzapw/OmniRoute/pull/11877)) +- **fix(lint):** pin react hooks plugin to 7.0.1 ([#11886](https://github.com/diegosouzapw/OmniRoute/pull/11886)) +- **fix(ci):** surface and clear release-green ESLint failure ([#11890](https://github.com/diegosouzapw/OmniRoute/pull/11890)) — thanks @MumuTW +- **fix(guardrails):** prevent duplicate prompt-injection-guard log output ([#11936](https://github.com/diegosouzapw/OmniRoute/pull/11936)) — thanks @patrykkopycinski +- **fix(auth):** downgrade expected transient states from warn to debug ([#11937](https://github.com/diegosouzapw/OmniRoute/pull/11937)) — thanks @patrykkopycinski +- **fix(codex):** fail over image generation for imported free plans ([#11948](https://github.com/diegosouzapw/OmniRoute/pull/11948)) — thanks @Bl0ck154 +- **fix(codex):** normalize non-stream responses ([#11951](https://github.com/diegosouzapw/OmniRoute/pull/11951)) — thanks @Bl0ck154 +- **fix(images):** forward Antigravity image size ([#11952](https://github.com/diegosouzapw/OmniRoute/pull/11952)) — thanks @Bl0ck154 +- **fix(db):** invalidate connection cache after upsert ([#11953](https://github.com/diegosouzapw/OmniRoute/pull/11953)) — thanks @Bl0ck154 +- **fix(codex):** restore imported account state ([#11954](https://github.com/diegosouzapw/OmniRoute/pull/11954)) — thanks @Bl0ck154 +- **fix(sse):** give extended-thinking targets the reasoning readiness budget ([#11959](https://github.com/diegosouzapw/OmniRoute/pull/11959)) — thanks @abhisheksharma2411 +- **fix(dashboard):** use opaque background and readable text color on cost chart tooltips ([#11960](https://github.com/diegosouzapw/OmniRoute/pull/11960)) — thanks @ZaimMarzuki +- **fix(antigravity):** send complete loadCodeAssist metadata (ideType/platform/pluginType as numeric enums) ([#11969](https://github.com/diegosouzapw/OmniRoute/pull/11969)) — thanks @rifqiawl +- **fix(core):** resolve DB init race condition and reasoning translation ([#12003](https://github.com/diegosouzapw/OmniRoute/pull/12003)) — thanks @raheemuddin786 +- **fix(admission):** exclude reclaimable page cache from the cgroup pressure ratio ([#12017](https://github.com/diegosouzapw/OmniRoute/pull/12017)) — thanks @HouMinXi +- **fix(build):** prune non-production trees in NFT trace excludes and tsconfig ([#12028](https://github.com/diegosouzapw/OmniRoute/pull/12028)) — thanks @Chewji9875 +- **fix(migrations):** add renamed migration compatibility for 056/073/077/101 ([#12036](https://github.com/diegosouzapw/OmniRoute/pull/12036)) — thanks @oyi77 +- **fix(sse):** default crash-guard logger to console.warn, not console ([#12042](https://github.com/diegosouzapw/OmniRoute/pull/12042)) — thanks @HouMinXi +- **fix(catalog):** derive vision/modalities for built-in auto combos from effective target pool ([#12046](https://github.com/diegosouzapw/OmniRoute/pull/12046)) — thanks @Prajeeth-12 +- **fix(release):** the packaged-app smoke verifies the database opened, not a driver line the primary path never prints (twin of #12032) ([#12047](https://github.com/diegosouzapw/OmniRoute/pull/12047)) +- **fix(barrel):** migrate src/app/ off the @/lib/localDb barrel import (#11795 Phase 2) ([#12051](https://github.com/diegosouzapw/OmniRoute/pull/12051)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate src/lib/ off the localDb barrel to direct db imports (#59) ([#12052](https://github.com/diegosouzapw/OmniRoute/pull/12052)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate open-sse, src/shared, src/sse, src/models, src/domain off the @/lib/localDb barrel import (#11795 Phase 4) ([#12053](https://github.com/diegosouzapw/OmniRoute/pull/12053)) — thanks @jonlwheat2-gif +- **fix(barrel):** delete the @/lib/localDb barrel — every consumer migrated (#11795 Phase 5) ([#12055](https://github.com/diegosouzapw/OmniRoute/pull/12055)) — thanks @jonlwheat2-gif +- **fix(radar):** restore D12 public boundary ([#12057](https://github.com/diegosouzapw/OmniRoute/pull/12057)) +- **fix(dev):** bound webpack and Tailwind scans ([#12075](https://github.com/diegosouzapw/OmniRoute/pull/12075)) — thanks @backryun +- **fix(dev):** isolate root layout settings reads ([#12076](https://github.com/diegosouzapw/OmniRoute/pull/12076)) — thanks @backryun +- **fix(dev):** reduce instrumentation executor fan-out (phase 3) ([#12078](https://github.com/diegosouzapw/OmniRoute/pull/12078)) — thanks @backryun +- **fix(dev):** make logging resources HMR-singleton ([#12079](https://github.com/diegosouzapw/OmniRoute/pull/12079)) — thanks @backryun +- **fix(sse):** keep unavailable forced connections scoped ([#12080](https://github.com/diegosouzapw/OmniRoute/pull/12080)) — thanks @keeltrace +- **fix(dev):** isolate batch dispatch from instrumentation ([#12081](https://github.com/diegosouzapw/OmniRoute/pull/12081)) — thanks @backryun +- **fix(ci):** reconcile release test contract drift ([#12082](https://github.com/diegosouzapw/OmniRoute/pull/12082)) — thanks @backryun +- **fix(ci):** accept CVE-2025-68121 in the prebuilt tls-client .so, auto-close base-red issues, guard Scorecard on the default branch ([#12085](https://github.com/diegosouzapw/OmniRoute/pull/12085)) +- **fix(db):** drop three consumer-less 1proxy exports — dead-code base-red on release/v3.8.51 after the barrel deletion (#12055) ([#12087](https://github.com/diegosouzapw/OmniRoute/pull/12087)) +- **fix(api):** clear the six API-route TypeScript regressions the new gate landed red on ([#12094](https://github.com/diegosouzapw/OmniRoute/pull/12094)) +- **fix(release):** never let the tag-push Create Release append auto notes to the curated body ([#12096](https://github.com/diegosouzapw/OmniRoute/pull/12096)) +- **fix(sse):** honor CLIProxyAPI environment API key ([#12099](https://github.com/diegosouzapw/OmniRoute/pull/12099)) — thanks @RaviTharuma +- **fix(oauth):** bind Google refresh to the client that issued the token ([#12106](https://github.com/diegosouzapw/OmniRoute/pull/12106)) — thanks @HouMinXi +- **fix(dashboard):** make RequestLoggerDetail loadable outside Next — CSS via globals.css + CJS/ESM interop (#11703 base-reds) ([#12114](https://github.com/diegosouzapw/OmniRoute/pull/12114)) +- **fix(leases):** project status lease row to lease columns so joined connection PII never escapes ([#12115](https://github.com/diegosouzapw/OmniRoute/pull/12115)) — thanks @geek007git +- **fix(plugins):** do not kill the plugin process when a fire-and-forget hook times out ([#12116](https://github.com/diegosouzapw/OmniRoute/pull/12116)) — thanks @geek007git +- **fix(guardrails):** restore injection-guard logging on middleware-only routes ([#12117](https://github.com/diegosouzapw/OmniRoute/pull/12117)) — thanks @geek007git +- **fix(plugins):** refresh stored manifest from disk on activate so new hook fields reach existing installs ([#12120](https://github.com/diegosouzapw/OmniRoute/pull/12120)) — thanks @geek007git +- **fix(codex):** preserve existing provider state when bulk-import upserts a matching connection ([#12122](https://github.com/diegosouzapw/OmniRoute/pull/12122)) — thanks @geek007git +- **fix(ci):** clear the two base-reds the 2026-08-30 merge batch left on release/v3.8.51 (round 3) ([#12123](https://github.com/diegosouzapw/OmniRoute/pull/12123)) +- **fix(sse):** strip type:'custom' from Claude tools on agentrouter dispatch ([#12126](https://github.com/diegosouzapw/OmniRoute/pull/12126)) — thanks @wahidsadik371-coder +- **fix(sse):** stop advertising video providers the dispatcher cannot run ([#12131](https://github.com/diegosouzapw/OmniRoute/pull/12131)) — thanks @quiterunner-commits +- **fix(search):** treat HTTP 432 and plan limit errors as transient cooldown ([#12139](https://github.com/diegosouzapw/OmniRoute/pull/12139)) — thanks @HouMinXi +- **fix(sse):** refuse an AI Horde queue that cannot fit the request budget ([#12143](https://github.com/diegosouzapw/OmniRoute/pull/12143)) — thanks @quiterunner-commits +- **fix(ci):** clear the base-reds the afternoon merge batch left on release/v3.8.51 (round 5: provider count 352, TS2554/TS2677) ([#12144](https://github.com/diegosouzapw/OmniRoute/pull/12144)) +- **fix(translator):** strip neutral tool_choice when tools absent in Responses-to-Chat (#12141) ([#12166](https://github.com/diegosouzapw/OmniRoute/pull/12166)) — thanks @HouMinXi +- **fix(guardrails):** pass providerId to getResolvedModelCapabilities in checkComboVision (#12112) ([#12169](https://github.com/diegosouzapw/OmniRoute/pull/12169)) — thanks @HouMinXi +- **fix(translator):** strip plaintext reasoning content for opaque responses backends (#12128) ([#12171](https://github.com/diegosouzapw/OmniRoute/pull/12171)) — thanks @HouMinXi +- **perf(stream):** compile hot-path regexes once, bound token caches, fix quadratic buffering ([#12179](https://github.com/diegosouzapw/OmniRoute/pull/12179)) — thanks @opensource-elearning +- **fix:** resolve compression worker file using runtime anchors instead of… ([#12183](https://github.com/diegosouzapw/OmniRoute/pull/12183)) — thanks @NightStalker-87 +- **fix:** prevent Claude Code session kills via liveness-aware readiness + auto model echo ([#12189](https://github.com/diegosouzapw/OmniRoute/pull/12189)) — thanks @opensource-elearning +- **fix(translator):** preserve falsy primitive values in Gemini and Antigravity function response results ([#12191](https://github.com/diegosouzapw/OmniRoute/pull/12191)) — thanks @Siva010 +- **fix(oauth):** mark empty Antigravity projectId as degraded and clear stale errors (#11284) ([#12205](https://github.com/diegosouzapw/OmniRoute/pull/12205)) — thanks @HouMinXi +- **fix(providers):** gate the Codex auto-ping usage read on the shared quota throttle ([#12209](https://github.com/diegosouzapw/OmniRoute/pull/12209)) — thanks @abhisheksharma2411 +- **fix(combos):** prioritize SQLite row id over inner JSON id and notify delete errors ([#12213](https://github.com/diegosouzapw/OmniRoute/pull/12213)) — thanks @HouMinXi +- **fix(providers):** mark featherless.ai as no longer having a free tier (`hasFree: false`) ([#12216](https://github.com/diegosouzapw/OmniRoute/pull/12216)) — thanks @turbolego +- **fix(deepseek-web):** stop Turbopack dev panic in the PoW worker path resolver ([#12221](https://github.com/diegosouzapw/OmniRoute/pull/12221)) — thanks @hartmark +- **fix(chat-admission):** clarify local 503 source ([#12223](https://github.com/diegosouzapw/OmniRoute/pull/12223)) — thanks @keeltrace +- **fix(dev):** silence webpack runtime module warnings ([#12228](https://github.com/diegosouzapw/OmniRoute/pull/12228)) — thanks @backryun +- **fix(db):** use module.require for CommonJS runtime driver loading ([#12230](https://github.com/diegosouzapw/OmniRoute/pull/12230)) — thanks @raheemuddin786 +- **fix(memory):** honest probe-driven FTS5 keyword status + memory_id rowid sync ([#12231](https://github.com/diegosouzapw/OmniRoute/pull/12231)) — thanks @raheemuddin786 +- **fix(system):** propagate abort signal to stream reader in HTTP version checks ([#12232](https://github.com/diegosouzapw/OmniRoute/pull/12232)) — thanks @raheemuddin786 +- **fix(resilience):** isolate local host execution errors from provider circuit breakers ([#12233](https://github.com/diegosouzapw/OmniRoute/pull/12233)) — thanks @raheemuddin786 +- **fix(sse):** sort injected tools deterministically for prompt caching ([#12234](https://github.com/diegosouzapw/OmniRoute/pull/12234)) — thanks @raheemuddin786 +- **fix(combo):** always clear the loop-safety timer, not just on the happy path (#11804) ([#12245](https://github.com/diegosouzapw/OmniRoute/pull/12245)) +- **fix(quality):** base-red pair — stryker tap registration + turn-pin suites aligned to the window gate ([#12255](https://github.com/diegosouzapw/OmniRoute/pull/12255)) — thanks @backryun +- **fix(dev):** qualify Turbopack runtime boundaries ([#12258](https://github.com/diegosouzapw/OmniRoute/pull/12258)) — thanks @backryun +- **fix(combo):** fall back to full pool when collapsed sole survivor is context-too-small ([#12278](https://github.com/diegosouzapw/OmniRoute/pull/12278)) — thanks @opensource-elearning +- **fix(combo):** bound the pre-dispatch unavailable skip so a stale label cannot dark a pool (#12168) ([#12285](https://github.com/diegosouzapw/OmniRoute/pull/12285)) +- **fix(security):** strip Qwen/Alibaba console-session cookies from provider API responses ([#12287](https://github.com/diegosouzapw/OmniRoute/pull/12287)) +- **fix(usage):** console-aware Token Plan guidance + subscription hint on bailian 401 ([#12288](https://github.com/diegosouzapw/OmniRoute/pull/12288)) +- **fix(resourcePressure):** log numeric detail on every rejection, recover faster ([#12293](https://github.com/diegosouzapw/OmniRoute/pull/12293)) — thanks @hartmark +- **fix(release):** drain the 2026-09-01 base-red window — passthrough usage regression + radarPage i18n keys ([#12327](https://github.com/diegosouzapw/OmniRoute/pull/12327)) +- **fix(ci):** openapi-security-tiers checker must honor routeGuard patterns + imported prefixes ([#12350](https://github.com/diegosouzapw/OmniRoute/pull/12350)) — thanks @ggiak +- **perf(ci):** cache node_modules in the npm-ci-retry composite (#8084 D3) ([#12408](https://github.com/diegosouzapw/OmniRoute/pull/12408)) +- **revert(ui):** point CTAs back at their real destinations ([#12410](https://github.com/diegosouzapw/OmniRoute/pull/12410)) +- **fix(release):** drain the 2026-09-02 base-red — rerank-providers import + api-typecheck baseline ratchet ([#12414](https://github.com/diegosouzapw/OmniRoute/pull/12414)) +- **fix(types):** make system prompt injection noImplicitAny-safe ([#12416](https://github.com/diegosouzapw/OmniRoute/pull/12416)) +- **fix(quality):** validate typecheck baseline schema ([#12419](https://github.com/diegosouzapw/OmniRoute/pull/12419)) +- **fix(memory):** resolve rerank provider node cache import ([#12421](https://github.com/diegosouzapw/OmniRoute/pull/12421)) +- **fix(ci):** repair release-root regressions — pack dedup, web-session syntax, uc-image ids ([#12423](https://github.com/diegosouzapw/OmniRoute/pull/12423)) — thanks @backryun +- **fix(providers):** separate MaxAI and UC credential contracts ([#12431](https://github.com/diegosouzapw/OmniRoute/pull/12431)) +- **fix(cli):** preserve Claude settings on config set ([#12432](https://github.com/diegosouzapw/OmniRoute/pull/12432)) — thanks @KrzysiekSko +- **fix(providers):** repair the maxai credential block truncated by merge auto-resolve ([#12433](https://github.com/diegosouzapw/OmniRoute/pull/12433)) +- **fix(quality):** record the 2026-09-02 merged growth in the file-size baseline ([#12434](https://github.com/diegosouzapw/OmniRoute/pull/12434)) +- **fix(uc):** route UC error strings through sanitizeErrorMessage; allowlist the retired codex id ([#12437](https://github.com/diegosouzapw/OmniRoute/pull/12437)) +- **fix(adapta):** redact streamed upstream errors ([#12438](https://github.com/diegosouzapw/OmniRoute/pull/12438)) +- **fix(sse):** remap non-contiguous upstream tool_calls index to a gap-free output_index ([#12445](https://github.com/diegosouzapw/OmniRoute/pull/12445)) — thanks @hartmark +- **fix(quality):** detect duplicate tool_calls entries in one response ([#12446](https://github.com/diegosouzapw/OmniRoute/pull/12446)) — thanks @hartmark +- **fix(conversations):** resolve turn content OmniRoute never sends back to the client ([#12447](https://github.com/diegosouzapw/OmniRoute/pull/12447)) — thanks @hartmark +- **fix(combo):** do not treat credits-exhausted 401 as auth skip ([#12449](https://github.com/diegosouzapw/OmniRoute/pull/12449)) — thanks @RaviTharuma +- **fix(auth):** do not park healthy quota accounts as expired ([#12452](https://github.com/diegosouzapw/OmniRoute/pull/12452)) — thanks @RaviTharuma +- **fix(responses-continuation):** fail closed on a collector-truncated, empty output array ([#12460](https://github.com/diegosouzapw/OmniRoute/pull/12460)) — thanks @hartmark +- **fix(logging):** raise the SSE payload collector's default cap ([#12461](https://github.com/diegosouzapw/OmniRoute/pull/12461)) — thanks @hartmark +- **fix(resilience):** surface Responses failed.error.message in 502s ([#12472](https://github.com/diegosouzapw/OmniRoute/pull/12472)) — thanks @RaviTharuma +- **fix(security):** unbiased maxai X-Random nonce + stricter URL/regex assertions ([#12502](https://github.com/diegosouzapw/OmniRoute/pull/12502)) +- **fix(video):** re-anchor transcript log-redaction so PII/credential maskers can't reopen the leak (#12150 P1 follow-up) ([#12503](https://github.com/diegosouzapw/OmniRoute/pull/12503)) +- **fix(monitoring):** serve cached credentialHealth off the request path ([#12533](https://github.com/diegosouzapw/OmniRoute/pull/12533)) — thanks @RaviTharuma +- **fix(quota):** keep Antigravity Gemini usable when Claude weekly is empty ([#12566](https://github.com/diegosouzapw/OmniRoute/pull/12566)) — thanks @HouMinXi +- **fix(authz):** hard-gate every credential export and CLI-config write (GHSA-5926-2w35-7h4q) ([#12600](https://github.com/diegosouzapw/OmniRoute/pull/12600)) +- **fix(docs):** restore the Next build — REMOVED_PROVIDERS.md had no frontmatter (base-red #12581) ([#12610](https://github.com/diegosouzapw/OmniRoute/pull/12610)) +- **fix(quality):** re-point the zcodeProtocol public-creds allowlist to line 313 ([#12615](https://github.com/diegosouzapw/OmniRoute/pull/12615)) +- **fix(security):** close 3 advisories — search baseUrl exfil, sk- in the error sanitizer, bifrost relay header leak ([#12620](https://github.com/diegosouzapw/OmniRoute/pull/12620)) +- **fix(providers):** sanitize boolean required and nested bare maps for Gemini (#12269) ([#12624](https://github.com/diegosouzapw/OmniRoute/pull/12624)) — thanks @HouMinXi +- **fix(resilience):** keep Overloaded STREAM_EARLY_EOF off the provider breaker ([#12626](https://github.com/diegosouzapw/OmniRoute/pull/12626)) — thanks @HouMinXi +- **fix(catalog):** re-audit free-tier quotas against official pages ([#12649](https://github.com/diegosouzapw/OmniRoute/pull/12649)) +- **fix(dashboard):** keep a request's pending-tracking id stable across combo target retries ([#12650](https://github.com/diegosouzapw/OmniRoute/pull/12650)) — thanks @hartmark +- **fix(tests):** retire dead model ids from the chat-pipeline integration suite (base-red #12581) ([#12670](https://github.com/diegosouzapw/OmniRoute/pull/12670)) +- **fix:** resolve SqliteError no such table compression_run_telemetry during cleanup ([#12682](https://github.com/diegosouzapw/OmniRoute/pull/12682)) — thanks @groovecityJO +- **fix(combo):** treat a pin-only step as implicit connection allowlist ([#12697](https://github.com/diegosouzapw/OmniRoute/pull/12697)) — thanks @HouMinXi +- **fix(ci):** ship .npmrc in published package so legacy-peer-deps applies to consumers (#11544) ([#12699](https://github.com/diegosouzapw/OmniRoute/pull/12699)) — thanks @KooshaPari +- **fix(docker):** pin 4 CLI tools to exact versions (#12576) ([#12703](https://github.com/diegosouzapw/OmniRoute/pull/12703)) — thanks @KooshaPari +- **fix(docker):** document and harden cli profile trust boundary (#12570) ([#12706](https://github.com/diegosouzapw/OmniRoute/pull/12706)) — thanks @KooshaPari +- **fix(sse):** redact video transcript in pre-guardrail rejected-request logs (#12150 P2 item 7) ([#12710](https://github.com/diegosouzapw/OmniRoute/pull/12710)) +- **fix(i18n):** quote placeholder in OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES description (#12505) ([#12769](https://github.com/diegosouzapw/OmniRoute/pull/12769)) — thanks @KooshaPari +- **fix(api/pricing):** surface validation error message as string, not raw object (#12494) ([#12771](https://github.com/diegosouzapw/OmniRoute/pull/12771)) — thanks @KooshaPari +- **fix(sse):** re-enable prompt compression for native Codex passthrough ([#12834](https://github.com/diegosouzapw/OmniRoute/pull/12834)) — thanks @thomasmaerz +- **fix(dashboard):** model health tests for a provider node set to the Responses API now call `/v1/responses` with a Responses-shaped body instead of `/v1/chat/completions` — those models were reported as `Provider returned HTTP 200 but no text content` even though the same model answered normally through `/v1/responses` ([#13070](https://github.com/diegosouzapw/OmniRoute/issues/13070)) ([#13087](https://github.com/diegosouzapw/OmniRoute/pull/13087)) — thanks @ntdatt812 +- fix(routing): stop a round-robin combo's "opencode" targets from collapsing onto the opencode-zen connection (#11912) ([#13283](https://github.com/diegosouzapw/OmniRoute/pull/13283)) +- fix(routing): stop the reactive-compaction debug log from lying when compression is globally disabled (#11977) ([#13276](https://github.com/diegosouzapw/OmniRoute/pull/13276)) +- fix(electron): relativize standalone-bundle symlink targets so Stage 8 manifest verification stops failing on Windows (#11979) ([#13251](https://github.com/diegosouzapw/OmniRoute/pull/13251)) +- fix(dashboard): surface a visible error when Compression Studio's combined preview run fails (#12061) ([#13288](https://github.com/diegosouzapw/OmniRoute/pull/13288)) +- fix(dashboard): make the compression "Effective pipeline" preview honor the active profile and warn when the master switch is off (#12063) ([#13287](https://github.com/diegosouzapw/OmniRoute/pull/13287)) +- fix(providers): scope TinyCMS Web signer's DOM shims to each call instead of leaking them for the process lifetime, and surface a clean HTTP status on a non-JSON interception-toggles error (#12072) ([#13254](https://github.com/diegosouzapw/OmniRoute/pull/13254)) +- fix(guardrails): stop Vision Bridge from re-selecting a model locked after a 404 (#12111) ([#13259](https://github.com/diegosouzapw/OmniRoute/pull/13259)) +- fix(sse): require Responses-shaped body before native OpenAI-compatible passthrough (#12129) ([#13278](https://github.com/diegosouzapw/OmniRoute/pull/13278)) +- fix(providers): minimax-m3 now collapses manual thinking.type:"enabled" to adaptive, preventing upstream 400 (2013) (#12132) ([#13249](https://github.com/diegosouzapw/OmniRoute/pull/13249)) +- fix(db): scope model visibility overrides by modality so hiding a Chat model no longer hides an identically-ID'd Image/Embeddings/etc. model (#12172) ([#13258](https://github.com/diegosouzapw/OmniRoute/pull/13258)) +- fix(db): scope local-provider apiKey dedup to matching base URL so LM Studio/Ollama-style connections support multiple accounts (#12173) ([#13250](https://github.com/diegosouzapw/OmniRoute/pull/13250)) +- fix(providers): refresh Trae's stale Referer/Origin and forward user timezone so imported connections stop failing with 401 (#12190) ([#13255](https://github.com/diegosouzapw/OmniRoute/pull/13255)) +- fix(providers): route opencode-go/gpt-5.6-luna to /responses instead of /chat/completions (#12196) ([#13275](https://github.com/diegosouzapw/OmniRoute/pull/13275)) +- fix(dashboard): allow deleting the last extra-upstream-header row even when invalid (#12251) ([#13252](https://github.com/diegosouzapw/OmniRoute/pull/13252)) +- **fix(i18n):** translate pre-existing `__MISSING__:` keys for `combo.sort`, `requestLogger.detail` expand/collapse, `common.profile`, and `settings.resilienceCredentialHealth*` across 39 locales ([#12272](https://github.com/diegosouzapw/OmniRoute/issues/12272)) ([#13034](https://github.com/diegosouzapw/OmniRoute/pull/13034)) — thanks @HouMinXi +- fix(cli): run the Node.js runtime compatibility guard before the heavy `tsx/esm` + Commander import chain so an unsupported runtime gets a clear message instead of a raw `Invalid regular expression flags` crash (#12296) ([#13253](https://github.com/diegosouzapw/OmniRoute/pull/13253)) +- fix(dashboard): refresh the providers list after deleting a compatible provider node (#12298) ([#13256](https://github.com/diegosouzapw/OmniRoute/pull/13256)) +- fix(usage): fail closed on API-key budget enforcement when a provider's `auto` routing alias has no pricing row, instead of silently counting it as $0 (#12341) ([#13257](https://github.com/diegosouzapw/OmniRoute/pull/13257)) +- **fix(providers):** include the submitted Agnes video model when polling by `video_id` ([#12356](https://github.com/diegosouzapw/OmniRoute/pull/12356)) — thanks @pacocartones +- **fix(routing):** custom OpenAI-compatible nodes now honor the saved Chat/Responses API type after edits instead of letting the node's original ID prefix override the live connection setting ([#11884](https://github.com/diegosouzapw/OmniRoute/issues/11884)). ([#12358](https://github.com/diegosouzapw/OmniRoute/pull/12358)) — thanks @pacocartones +- fix(sse): surface an error instead of a silent empty 200 when a Claude stream closes with zero bytes (#12398) ([#13285](https://github.com/diegosouzapw/OmniRoute/pull/13285)) +- fix(oauth): warn before the dead localhost:8080 redirect in antigravity/gemini `oauth start` (#12413) ([#13265](https://github.com/diegosouzapw/OmniRoute/pull/13265)) +- fix(providers): stop devin-cli spawn error from double-closing the SSE controller (#12517) ([#13261](https://github.com/diegosouzapw/OmniRoute/pull/13261)) +- **fix(rerank):** clamp Voyage `top_k` to the documents actually sent after empty-string filtering, and honor `return_documents: false` in the NVIDIA response adapter (#12523 — thanks @pacocartones) +- **fix(audio):** `/v1/audio/translations` now resolves combo names the way `/v1/audio/transcriptions` already does, so a combo that `GET /v1/models` advertises is fanned out to its targets instead of being rejected with `400 Invalid translation model: . Use format: provider/model`; literal `provider/model` ids and unknown bare names behave as before (#12536 — thanks @pacocartones) +- **fix(gemini):** strip the JSON-Schema-2020-12 `prefixItems` keyword from Gemini tool schemas at every nesting level, so Claude Code tool definitions no longer fail with `400 Unknown name "prefixItems"` on Gemini models (#12540 — thanks @pacocartones) +- **fix(api-manager):** Expose an accessible loading status while API keys are fetched instead of an empty accessibility tree (#12541 — thanks @pacocartones) +- **fix(video):** Clamp `estimateJpegFrameBytes` at zero for padding-only payloads and build the three encode-side frame data URIs from `JPEG_FRAME_DATA_URI_PREFIX` instead of a repeated literal (#12543 — thanks @pacocartones) +- **fix(devin):** accept Windows `DEVIN_AGENTIC_HOME` sandbox paths (`C:\...\.sandbox\...`) in the isolated-home check so the Devin Claude Bridge no longer fails closed on Windows ([#12405](https://github.com/diegosouzapw/OmniRoute/issues/12405)) (#12545 — thanks @pacocartones) +- **fix(db):** Add `conversation_turn_nodes` and orphaned `agentic_conversations` to the auto-cleanup cycle under the existing `retention.callLogs` window, so identity nodes whose call-log content has already been purged no longer accumulate without bound in `storage.sqlite` (#12548 — thanks @pacocartones) +- **fix(i18n):** Wrap the `~/.claude/profiles//settings.json` placeholder in ICU single quotes in the `OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES` feature-flag description across all 42 locales and the TypeScript default, so next-intl no longer fails with `INVALID_MESSAGE: UNCLOSED_TAG` and the Feature Flags card shows the description instead of the raw key (#12549 — thanks @pacocartones) +- **fix(orchestration):** `updateCloudAgentTask` now publishes the task's real `status` on `agent.task.updated` when an update only touches `result`, `activities` or `error`, instead of the fabricated `"updated"` state, and stays silent when no row matched the id (#12550 — thanks @pacocartones) +- **fix(i18n):** the home "Recent Requests" panel and the Provider Topology legend are now translated instead of rendering English copies on non-English dashboards; the legend reads its own `home.topologyLegend*` labels with consistent casing rather than borrowing the memory-settings "Recent" and analytics "Error" strings (#12551 — thanks @pacocartones). +- **docs(reference):** bring the `FEATURE_FLAGS.md` catalog back to 1:1 with `featureFlagDefinitions.ts` — 20 missing flags added, the two `*_BLOCK_THRESHOLD` env-only knobs moved out of the flag tables, category/total counts and the Live WS port corrected, guarded by a static test (#12552 — thanks @pacocartones) +- **fix(i18n):** backfill missing `usage.kiloPass*` strings in 39 locales and restore `featureFlagOmnirouteDisableThinkingLevelVariantsDescription` in `pt.json` ([#12561](https://github.com/diegosouzapw/OmniRoute/issues/12561)) ([#13027](https://github.com/diegosouzapw/OmniRoute/pull/13027)) — thanks @HouMinXi +- fix(docker): default docker-compose app ports (dashboard/API/live-WS) to loopback instead of `0.0.0.0`, closing the anonymous `/v1` LAN/WAN exposure gap left open by `REQUIRE_API_KEY=false` (#12568) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): close DNS-rebinding SSRF gap in webhook outbound-URL guard (#12569) ([#13243](https://github.com/diegosouzapw/OmniRoute/pull/13243)) +- fix(docker): require a per-session token on the VNC browser CDP bridge and isolate it on a dedicated Docker network (#12571) ([#13241](https://github.com/diegosouzapw/OmniRoute/pull/13241)) +- fix(open-sse): write Adobe Firefly session tokens and cookie jars with 0700/0600 permissions instead of the process umask (#12572) ([#13242](https://github.com/diegosouzapw/OmniRoute/pull/13242)) +- fix(api): remove hardcoded wildcard CORS in openai-to-gemini-sse.ts so the centralized fail-closed CORS gate is the sole source of `Access-Control-Allow-Origin` (#12573) ([#13239](https://github.com/diegosouzapw/OmniRoute/pull/13239)) +- fix(api): enforce API key policy (budget/rate-limit/schedule/endpoint scoping) on the ElevenLabs speech-to-text, text-to-speech and voices proxy routes (#12574) ([#13244](https://github.com/diegosouzapw/OmniRoute/pull/13244)) +- fix(sse): cap HuggingChat NDJSON body size and bound the read loop with the fetch timeout so a stalled or hostile upstream cannot buffer unbounded memory (#12577) ([#13282](https://github.com/diegosouzapw/OmniRoute/pull/13282)) +- fix(docker): scope the cliproxyapi/qdrant/bifrost sidecars to loopback by default and forward `CLIPROXYAPI_MANAGEMENT_KEY` into the cliproxyapi container so its management API is not left both unauthenticated and LAN-published (#12578) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): create DB export temp paths with `fs.mkdtempSync` instead of predictable timestamps (#12579) ([#13237](https://github.com/diegosouzapw/OmniRoute/pull/13237)) +- Cline 401 bodies that say "re-authenticate your Cline account" classify as a refreshable OAuth token, not a terminal expired key. The cooling panel no longer labels every cooldown as a 429; it shows the recorded last error instead. (#12594) ([#13060](https://github.com/diegosouzapw/OmniRoute/pull/13060)) — thanks @HouMinXi +- **fix(catalog):** degrade unknown combo targets instead of dropping LCD modalities, and persist OpenRouter `architecture.input_modalities` into the capability snapshot ([#12613](https://github.com/diegosouzapw/OmniRoute/issues/12613)) ([#13042](https://github.com/diegosouzapw/OmniRoute/pull/13042)) — thanks @HouMinXi +- fix(providers): send `x-api-key` instead of `Authorization: Bearer` for OpenCode Zen's `/v1/responses` endpoint (Muse Spark Contributor models), fixing a 401 on OmniRoute's auth header (#12633) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(sse): surface the actionable "Auggie CLI not found" message when the shell reports a missing `auggie` binary via exit code instead of a spawn error (#12645) ([#13263](https://github.com/diegosouzapw/OmniRoute/pull/13263)) +- **fix(config):** Persist deletions of built-in background-degradation entries — when a stored settings record exists its `degradationMap` is now authoritative instead of being merged under the defaults, so an entry the user removed in the dashboard no longer reappears on the next apply or restart ([#12424](https://github.com/diegosouzapw/OmniRoute/issues/12424)) ([#12647](https://github.com/diegosouzapw/OmniRoute/pull/12647)) — thanks @pacocartones +- **fix(gamification):** action-count badge milestones (First Token, Token Consumer, Token Machine, Token Whale, and the token-sharing tier) are now backed by a durable `xp_action_counts` counter incremented in `addXp()`, instead of a live `COUNT(*)` over `xp_audit_log`. The audit log is pruned by `retention.xpAuditLog` (default 30 days), so on a default install those "lifetime" milestones were really "actions in the last 30 days" and unlocked badges could stop unlocking once old rows aged out. `getActionCount()` and `checkActionCountBadges()` now read the same durable source, and a migration backfills existing totals from the surviving audit rows ([#12546](https://github.com/diegosouzapw/OmniRoute/issues/12546)) ([#12651](https://github.com/diegosouzapw/OmniRoute/pull/12651)) — thanks @pacocartones +- **fix(images):** `/v1/images/edits` now iterates a combo's targets the same way `/v1/images/generations` does (#9239) instead of flattening a bare combo to its first target. A combo whose first target is not edit-capable — or lacks credentials — now falls through to a later edit-capable target rather than hard-erroring, and missing credentials are skipped (not a hard `401`) to match the generations path. The per-target skip/terminal classification is extracted into a shared `runImageComboTargets` loop, so generations behavior is unchanged ([#12547](https://github.com/diegosouzapw/OmniRoute/issues/12547)). ([#12653](https://github.com/diegosouzapw/OmniRoute/pull/12653)) — thanks @pacocartones +- fix(sse): add first-byte watchdog to the TLS-fingerprint transport so a stalled wreq body falls back instead of hanging for minutes (#12656) ([#13272](https://github.com/diegosouzapw/OmniRoute/pull/13272)) +- fix(sse): exempt tiny-budget reasoning probes from combo quality failure and surface persisted-cooldown skips in ALL_TARGETS_SKIPPED diagnostics (#12659) ([#13269](https://github.com/diegosouzapw/OmniRoute/pull/13269)) +- fix(models): declare the real ~1M contextLength for OpenCode Zen's Muse Spark 1.2 models instead of falling back to the 200000 provider default (#12681) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(oauth): align codebuddy-cn OAuth User-Agent with the chat/usage CLI version to avoid WAF false positives (#12702) ([#13264](https://github.com/diegosouzapw/OmniRoute/pull/13264)) +- fix(dashboard): surface an authentication-required banner instead of silently blanking database settings for a guest session (#12709) ([#13270](https://github.com/diegosouzapw/OmniRoute/pull/13270)) +- **fix(resilience):** stop unbounded queue that hangs 6min until Aborted — gate, provider slot, and Bottleneck queue now share a per-connection `maxWaitMs` budget; `fail-closed` on exhaust (503); execution backstop `executionMaxWaitMs` overridable per connection with upstream clamp ([#12715](https://github.com/diegosouzapw/OmniRoute/pull/12715)) — thanks @maxmad64bis (with thanks to @Tushar49 for surfacing the slow-provider need in #12635) +- **fix(auto-combo):** every mode pack now carries `quality` and `reliability` weights (`quality` 0.02, 0.03 in `quality-first`; `reliability` 0.03, 0.04 in `reliability-first`), so selecting a pack no longer silences either signal; `DEFAULT_WEIGHTS` is unchanged ([#12731](https://github.com/diegosouzapw/OmniRoute/pull/12731)) — thanks @maxmad64bis +- **fix(sse):** the provider execution pipeline keeps the upstream error `code`/`type` again — both were lost when the non-streaming leg was extracted, so a config-class `422` (Antigravity missing project) degraded into a generic account cooldown instead of reaching the gate that recognises it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(db):** a legacy `call_logs` table no longer aborts startup — the provider-stats index is created after the column healing runs, not before it, so an install predating `request_type` opens instead of failing with `no such column` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(catalog):** a malformed row in the operator-writable custom-models blob no longer kills every `auto/*` pool ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(tests):** realign the guards that the same day's merges left asserting the old behavior — the provider-count total after GreenPT and EURouter, the GLM stream buffer slot that `#12925` turned into a declared parameter, and the injection scan bound that `#13104` reshaped from a head-only window into head-plus-tail ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- fix(cache): fold tool_choice/tools/response_format into the semantic cache signature so a cached tool_calls response can no longer be replayed for a request whose tool policy forbids it (#12734) ([#13267](https://github.com/diegosouzapw/OmniRoute/pull/13267)) +- **fix(models):** stop treating provider-supplied `isFree:true`/`:free`/`0/0` flags as trusted unless the provider has a documented free tier — only the shipped free-tier catalog decides otherwise; fetched `isFree:true` trusted only for a free-tier provider, custom `isFree:true` via a trusted path ([#12744](https://github.com/diegosouzapw/OmniRoute/pull/12744)) — thanks @maxmad64bis +- fix(memory): authenticate the internal /v1/rerank loopback call so memory reranking no longer silently degrades to unranked order when REQUIRE_API_KEY=true (#12745) ([#13268](https://github.com/diegosouzapw/OmniRoute/pull/13268)) +- fix(sse): parse Ollama Cloud's current usage markup (`$X of $Y used` aria-label, nested width style) (#12749) ([#13260](https://github.com/diegosouzapw/OmniRoute/pull/13260)) +- fix(cli): setup-opencode no longer sends an active context's management token to `/v1/models` when `--api-key`/`OMNIROUTE_API_KEY` is supplied — an explicit flag or the env var now always outranks the context's token, and the flag itself is no longer swallowed by the parent program's global `--api-key` option (#12783) ([#13246](https://github.com/diegosouzapw/OmniRoute/pull/13246)) +- fix(sse): register Arcee AI in the executor provider registry so requests reach api.arcee.ai instead of silently falling back to OpenAI (#12784) ([#13277](https://github.com/diegosouzapw/OmniRoute/pull/13277)) +- **fix(quota):** share one upstream quota read when identical saturation checks arrive at the same time, so traffic bursts don't multiply provider API calls ([#12787](https://github.com/diegosouzapw/OmniRoute/pull/12787)) — thanks @maxmad64bis +- **fix(db):** ignore expired or invalid rate-limit cooldown writes so a stale timestamp can't lock a connection that should be usable — clearing still works as before ([#12788](https://github.com/diegosouzapw/OmniRoute/pull/12788)) — thanks @maxmad64bis +- **fix(providers):** single source for provider order with `xao` ranking alongside `xai-oauth` ([#12790](https://github.com/diegosouzapw/OmniRoute/pull/12790)) — thanks @maxmad64bis +- **fix(routing):** off-table models check the free-model catalog before inheriting premium prices, latency bootstraps from the observed pool median, async tiers read live database pricing with a 90-day freshness gate, and the tier cache invalidates on every pricing write ([#12792](https://github.com/diegosouzapw/OmniRoute/pull/12792)) — thanks @maxmad64bis +- **fix(auto-combo):** snapshot scoring uses the observed per-connection account tier and per-model quality instead of neutral constants, and circuit-open providers rank lower at build time ([#12794](https://github.com/diegosouzapw/OmniRoute/pull/12794)) — thanks @maxmad64bis +- **fix(pool):** empty `auto/*` pools now say why they are empty, and the models listing uses the same paid check as routing ([#12795](https://github.com/diegosouzapw/OmniRoute/pull/12795)) — thanks @maxmad64bis +- fix(routing): recognize CLIProxyAPI's 'unknown provider for model' 400 as fallback-worthy (#12800) ([#13284](https://github.com/diegosouzapw/OmniRoute/pull/13284)) +- **fix(sse):** translate-mode streams now emit the estimated token counts as a trailing usage-only chunk before `[DONE]` when the upstream stays silent, so metered chat clients see totals instead of nothing ([#12828](https://github.com/diegosouzapw/OmniRoute/pull/12828)) — thanks @maxmad64bis +- **fix(db):** provider stats stay truthful on empty and legacy databases: fallback counts default to `0` instead of `null`, latency averages read `null` (not `0`) when no durations were recorded, failures logged before the error-type column existed group under `pre_migration` instead of `unclassified`, and per-provider queries use two new composite indexes ([#12832](https://github.com/diegosouzapw/OmniRoute/pull/12832)) — thanks @maxmad64bis +- fix(nvidia): fail open when a synced model catalog goes stale instead of gating forever (#12849) ([#13248](https://github.com/diegosouzapw/OmniRoute/pull/13248)) +- **fix(db):** WAL maintenance lives in its own module: a `TRUNCATE` checkpoint that hits a busy database now warns with its streak and retries once via `PASSIVE` instead of logging success, and closing-time checkpoints no longer report success on builds without a database file, with the busy totals visible in the authenticated monitoring health payload ([#12853](https://github.com/diegosouzapw/OmniRoute/pull/12853)) — thanks @maxmad64bis +- **fix(health):** quota with no snapshots reads empty instead of a contradicting 0% ([#12857](https://github.com/diegosouzapw/OmniRoute/pull/12857)) — thanks @maxmad64bis +- fix(a2a): accept the dashboard's own session cookie on /a2a so "Run message/send" no longer fails with "Unauthorized: missing or invalid API key" (#12888) ([#13271](https://github.com/diegosouzapw/OmniRoute/pull/13271)) +- **fix(logs):** the Logs grid's in-memory filter pass no longer discards rows the SQL query already matched — selecting an API key from the dropdown (which sends the key's id) returns its calls again, the Combo tab shows every combo instead of only those whose name contains a "1", and the model filter and search cover the same columns as the query ([#12896](https://github.com/diegosouzapw/OmniRoute/pull/12896)) — fixes [#12873](https://github.com/diegosouzapw/OmniRoute/issues/12873) — thanks @ntdatt812 +- **fix(a2a):** `/api/a2a/status` now builds the agent card from the request that asked for it, so a gateway reached at a non-localhost host no longer advertises `http://localhost:20128` as its A2A URL ([#12918](https://github.com/diegosouzapw/OmniRoute/pull/12918)). — thanks @ntdatt812 +- **fix(compression):** progressive aging now appends its `[COMPRESSED:aging:…]` annotation after a turn's `tool_result` blocks instead of in front of them, so Anthropic no longer rejects aged conversations with "`tool_use` ids were found without `tool_result` blocks immediately after" ([#12920](https://github.com/diegosouzapw/OmniRoute/pull/12920)). — thanks @ntdatt812 +- **fix(bedrock):** model import now resolves context limits for every vendor prefix instead of only `anthropic.*`, so `global.openai.gpt-5.6-*` no longer imports with a null `inputTokenLimit` and gets rejected pre-flight at the 200k default ([#12921](https://github.com/diegosouzapw/OmniRoute/pull/12921)). — thanks @ntdatt812 +- **fix(stream):** the 64 KB stream buffer GLM asks for is honoured instead of dropped, and the type error it caused no longer fails the API Route Typecheck gate on every open PR ([#12925](https://github.com/diegosouzapw/OmniRoute/pull/12925)) — thanks @ntdatt812 +- **fix(guardrails):** mask PII inside a `tool_result`'s nested content array, which the masker walked past while redacting its sibling block ([#12930](https://github.com/diegosouzapw/OmniRoute/pull/12930)) — thanks @ntdatt812 +- **fix(dashboard):** the Radar catalog table no longer leaves absent data unexplained — empty limits, unknown context windows, and unreported capabilities each explain themselves on hover, and a new check keeps it that way ([#12937](https://github.com/diegosouzapw/OmniRoute/pull/12937)) — thanks @maxmad64bis +- **fix(sse):** geo-blocked opencode requests rotate to the next account proxy instead of failing, so one refused egress no longer aborts the whole chain ([#12941](https://github.com/diegosouzapw/OmniRoute/pull/12941)) — thanks @maxmad64bis +- **fix(codex):** dashboard "clear cooldown" and the CAS recovery path now drop nested `codexScopeRateLimitedUntil` maps in the same write that nulls `rate_limited_until`, and fresh quota snapshots with headroom lift fallback-sourced scope cooldowns parked by quota preflight ([#12817](https://github.com/diegosouzapw/OmniRoute/issues/12817), [#12860](https://github.com/diegosouzapw/OmniRoute/issues/12860), [#12951](https://github.com/diegosouzapw/OmniRoute/pull/12951)) The dashboard reset-credit button (`consumeCodexResetCredit`) now exercises that same snapshot path after a successful redeem, so a filled quota bar is enough to unpark a leftover fallback Codex child without another manual clear. — thanks @HouMinXi +- **fix(db):** resolve `sql-wasm.wasm` across global npm install and hoisted layouts, ensuring OmniRoute can boot cleanly on Node 24 when native `better-sqlite3` is uncompiled. ([#13035](https://github.com/diegosouzapw/OmniRoute/pull/13035)) — thanks @HouMinXi +- **fix(api):** `DELETE /v1/batches/delete-completed` now sweeps only the calling API key's own completed batches (batches with no owner stay out of a key-scoped sweep on purpose), with an explicit instance-wide mode reserved for authenticated dashboard sessions, a 401 for a presented key that is unknown, revoked, deactivated, banned or expired (never falling through to the session branch), audit logging of every sweep, a sanitized 500 on failure, an owner-scoped file half (a key-scoped sweep never nulls a file another tenant owns) and an atomic sweep — chunked in 200-batch transactions in instance mode — so a mid-way error never leaves a batch pointing at a nulled file (GHSA-wvxc-jp3v-5mg5) ([#12969](https://github.com/diegosouzapw/OmniRoute/pull/12969)) +- **fix(combo):** quota-weighted routing stops drawing on an out-of-credit connection — a 402 now invalidates the stored quota snapshot instead of leaving its stale remaining percentage in place, and a snapshot older than 10 minutes no longer counts as confident headroom for the primary pool ([#12972](https://github.com/diegosouzapw/OmniRoute/pull/12972)) ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- **fix(vision):** Custom Models with "Vision capable" checked no longer have image requests swapped to `glm/glm-4.6v` when the client sends the advertised alias (`vllm/path/...`) or the bare path-shaped id — Vision Bridge now matches the stored override for all three id forms ([#12758](https://github.com/diegosouzapw/OmniRoute/issues/12758)) ([#12974](https://github.com/diegosouzapw/OmniRoute/pull/12974)) — thanks @HouMinXi +- **fix(sse):** transient opencode upstream failures rotate to the next account proxy instead of failing, so one flapping egress no longer aborts the whole chain ([#12975](https://github.com/diegosouzapw/OmniRoute/pull/12975)) — thanks @maxmad64bis +- **fix(azure):** Deployments from GPT-6 onward now send `max_completion_tokens` instead of `max_tokens`, which Azure rejects with HTTP 400. The rule matched a literal `gpt-5`, so each new generation arrived broken; it now matches the generation range, while `gpt-35-turbo` still keeps `max_tokens`. ([#13007](https://github.com/diegosouzapw/OmniRoute/pull/13007)) — thanks @ntdatt812 +- **fix(db):** add an opt-in self-restart circuit for sustained critical memory pressure, gate post-cleanup VACUUM behind a minimum freed-rows threshold, and checkpoint the SQLite WAL every 5 minutes with a size guard that escalates to TRUNCATE, so a growing WAL can no longer stall the event loop into a full outage. ([#13011](https://github.com/diegosouzapw/OmniRoute/pull/13011)) — thanks @HouMinXi +- **fix(auth):** an explicit connection pin may probe a quota-disabled row once and re-enable it on success ([#12874](https://github.com/diegosouzapw/OmniRoute/issues/12874)) ([#13017](https://github.com/diegosouzapw/OmniRoute/pull/13017)) — thanks @HouMinXi +- **fix(api):** thread `X-OmniRoute-Fallback-Attempts` through combo chat completions so streaming and non-streaming responses report how many prior legs were attempted ([#13038](https://github.com/diegosouzapw/OmniRoute/pull/13038)) — thanks @HouMinXi +- Restore provider failure classification and credential refresh on non-streaming requests: classify non-2xx failures to lock models on per-model quota exhaustion, update connection rate limits from headers and body, and pass credential refresh handlers to pipeline execution so 401 tokens can be refreshed and retried (#13043). ([#13069](https://github.com/diegosouzapw/OmniRoute/pull/13069)) — thanks @HouMinXi +- **fix(responses):** wrap forced-non-streaming web_search fallback JSON as Responses SSE so Codex still sees `response.completed` ([#13050](https://github.com/diegosouzapw/OmniRoute/pull/13050)) — thanks @HouMinXi +- **fix(antigravity):** Preserve upstream thought-token usage in normalized completion and reasoning token counts ([#13055](https://github.com/diegosouzapw/OmniRoute/pull/13055)) — thanks @pacocartones +- Fix a concurrency-slot leak in the DeepSeek PoW solver: a worker that failed to spawn (for example a missing worker script) never released its slot, so `MAX_CONCURRENT_WORKERS` failures disabled the solver until restart. ([#13097](https://github.com/diegosouzapw/OmniRoute/pull/13097)) — thanks @anhtahaylove +- **fix(acp):** bound the ACP session output buffers — `stdoutBuffer` and `stderrBuffer` now cap at 1 MiB keeping the most recent output behind a visible `[...output truncated...]` marker, and `stderrBuffer` is reset per prompt instead of accumulating for the lifetime of the session. ([#13100](https://github.com/diegosouzapw/OmniRoute/pull/13100)) — thanks @anhtahaylove +- **fix(acp):** release the `stdout`/`exit` listeners and the idle timer that a `sendPrompt` timeout used to leave attached to the `acpManager` singleton, and drop sessions that exited on their own from the session map instead of keeping them forever. ([#13096](https://github.com/diegosouzapw/OmniRoute/pull/13096)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection and PII scanners now read the text a `tool_result` block carries on `content` (string or nested block list), in messages and in system blocks, so tool output is judged by the same rules as user text ([#13101](https://github.com/diegosouzapw/OmniRoute/pull/13101)) — thanks @ntdatt812 +- **fix(gamification):** close the badge notification SSE stream when the request signal is already aborted before the stream starts — a client that disconnects while the route is still awaiting auth used to leave both the 2s unlock poll and the 15s heartbeat running for the lifetime of the process. ([#13106](https://github.com/diegosouzapw/OmniRoute/pull/13106)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection scan now spends its 16 KB budget on both ends of the request instead of the first 16 KB only, so `system`, `instructions`, `query`, `documents` and the newest turns are no longer hidden behind one long message ([#13104](https://github.com/diegosouzapw/OmniRoute/pull/13104)) — thanks @ntdatt812 +- **fix(dashboard):** expose the Volcano Ark console cookie on quota scraping and unwrap connect-error objects so the dashboard shows the upstream message ([#13107](https://github.com/diegosouzapw/OmniRoute/pull/13107)) — thanks @HouMinXi +- **fix(db):** release the `beforeExit`/`SIGINT`/`SIGTERM` handlers when a `node:sqlite` adapter closes, so a closed adapter and its database handle are no longer pinned to `process` for the lifetime of the run — the same treatment #7494 gave the sql.js adapter. ([#13109](https://github.com/diegosouzapw/OmniRoute/pull/13109)) — thanks @anhtahaylove +- **fix(translator):** `contentSchema` and `unevaluatedItems` are now treated as subschema positions by the tool-schema sanitizer, so a truncation placeholder in either is replaced with a permissive schema instead of being forwarded as a string ([#13110](https://github.com/diegosouzapw/OmniRoute/pull/13110)) — thanks @ntdatt812 +- **fix(cli-helper):** clear the `createLogStream` timeout on the abort path — `stop()` aborts the in-flight fetch and returned through the `signal.aborted` branch, which skipped `clearTimeout` and left an armed timer per stopped stream. The stream reader is now also cancelled when the read loop exits early. ([#13114](https://github.com/diegosouzapw/OmniRoute/pull/13114)) — thanks @anhtahaylove +- **fix(docs):** drop TLS-impersonation, MITM-decrypt, supply-chain attestation, and XOR-mask writeups from the public `/docs` catalog and Docker image. Files stay in git for engineers; operators who need them open the repo, not the website. ([#13136](https://github.com/diegosouzapw/OmniRoute/pull/13136)) — thanks @HouMinXi +- **fix(combo):** parse numeric-epoch `rate_limited_until` in the combo cooldown read path ([#13141](https://github.com/diegosouzapw/OmniRoute/pull/13141)) — thanks @maxmad64bis +- **fix(opencode):** both OpenCode plugins now reject a gateway address typed without `http://` at configuration time, instead of publishing every model with an api url no client can call, and the v2 plugin no longer publishes a model card whose api url is blank or relative ([#13142](https://github.com/diegosouzapw/OmniRoute/pull/13142)) — thanks @maxmad64bis +- **fix(providers):** lock opencode model on upstream 400 model-unavailable ([#13146](https://github.com/diegosouzapw/OmniRoute/pull/13146)) — thanks @maxmad64bis +- **fix(logging):** keep the provider exchange rather than the raw client bodies when a call log exceeds its size budget, and show that recovered payload in the request-detail panel instead of replacing it with the stored response body ([#13147](https://github.com/diegosouzapw/OmniRoute/pull/13147)) — thanks @maxmad64bis +- **fix(traffic-inspector):** the WebSocket route no longer leaks a traffic-buffer subscriber and a 30s ping timer when the client socket is already closed at handler time — listeners are attached before any resource is acquired, a destroyed socket bails out early, and the ping interval stops on a dead socket where `write()` never throws ([#13155](https://github.com/diegosouzapw/OmniRoute/pull/13155)) — thanks @anhtahaylove +- **fix(telegram):** bound the per-user API key cache in the Telegram chat proxy so a burst of distinct chat ids can no longer grow the process heap without limit ([#13165](https://github.com/diegosouzapw/OmniRoute/issues/13165)) ([#13166](https://github.com/diegosouzapw/OmniRoute/pull/13166)) — thanks @anhtahaylove +- **fix(stream):** cancel the upstream response body when the JSON-to-SSE sniff unwinds on a body timeout, so a stalled upstream no longer pins the connection ([#13169](https://github.com/diegosouzapw/OmniRoute/issues/13169)) ([#13171](https://github.com/diegosouzapw/OmniRoute/pull/13171)) — thanks @anhtahaylove +- **fix(telegram):** authenticate webhook deliveries with Telegram's `secret_token` so an unauthenticated caller can no longer mint API keys or spend upstream quota ([#13172](https://github.com/diegosouzapw/OmniRoute/issues/13172)) ([#13175](https://github.com/diegosouzapw/OmniRoute/pull/13175)) — thanks @anhtahaylove +- **fix(models):** give discoverable Gemini 3.8 Flash ids their own 65536 output spec so Antigravity no longer clamps them to 16384 ([#13195](https://github.com/diegosouzapw/OmniRoute/pull/13195)) — thanks @HouMinXi +- **fix(dashboard):** leftover catalog-removed provider rows (gemini-cli) can be listed and purged from the providers page ([#13067](https://github.com/diegosouzapw/OmniRoute/issues/13067)) ([#13197](https://github.com/diegosouzapw/OmniRoute/pull/13197)) — thanks @HouMinXi +- **fix(auth):** a dashboard session now requires the `authenticated: true` claim that login, OIDC and the session refresh already emit — a JWT merely signed with `JWT_SECRET` (for example the Cursor CLI passthrough token, which any API-key holder can obtain) no longer verifies as the `auth_token` cookie on any route, the WebSocket handshake or the live server; existing sessions keep working ([#13298](https://github.com/diegosouzapw/OmniRoute/issues/13298)) ([#13375](https://github.com/diegosouzapw/OmniRoute/pull/13375)) +- **fix(api):** `DELETE /v1/batches/delete-completed` now applies the caller's API-key policy (endpoint allowlist, schedule, usage cap, rate limit) like every other `/v1` route, commits the key-scoped sweep per 200-batch chunk instead of holding one write lock for the whole sweep, refuses to loop without progress, and rejects a scope that names both a key and `allTenants` ([#13684](https://github.com/diegosouzapw/OmniRoute/pull/13684)) +- Restore the API-route typecheck gate: the non-streaming leg result lost its discriminated-union narrowing after the server-owned tool loop reassignment, producing 13 new TS2339 diagnostics in `chatCore.ts`. ([#12963](https://github.com/diegosouzapw/OmniRoute/pull/12963)) +- Restore four non-streaming behaviours the server-owned tool loop refactor dropped: client aborts map to 499 with the fixed `Request aborted` message, an aborted request no longer logs a synthetic `clientResponse`, Claude prompt-cache telemetry is recorded again, and a body that cannot be canonicalized no longer throws when the tool loop is off. ([#12990](https://github.com/diegosouzapw/OmniRoute/pull/12990)) +- **fix(skills):** The CLI registry parser now reads positionals declared with `.addArgument()`, not only those written inline in `.command()`. `tunnel create [type]` was being published as `tunnel create`, so the agent-skills sync gate reported drift on every branch and regenerating would have deleted the argument. ([#13009](https://github.com/diegosouzapw/OmniRoute/pull/13009)) — thanks @ntdatt812 +- **fix(combos):** dashboard combo test uses a short prompt, serial probes, and a 60s timeout so reasoning models and rate-limited free pools do not fail the health check ([#13001](https://github.com/diegosouzapw/OmniRoute/pull/13001)) — thanks @HouMinXi +- fix(compression): terminate idle worker threads on eviction so long-running instances stop leaking OS threads and MessagePorts ([#13091](https://github.com/diegosouzapw/OmniRoute/pull/13091)) — thanks @anhtahaylove +- Redact Google API keys of any length in error bodies: the pattern required exactly 39 characters, so shorter or longer `AIza…` credentials passed through unredacted. ([#12964](https://github.com/diegosouzapw/OmniRoute/pull/12964)) +- **fix(grok-cli):** a 402 "Grok Build usage balance exhausted" parks that Grok login as out of credit (Grok Build CLI, grok.com cookie, and xAI OAuth share the weekly pool). Combo routing then tries the next login instead of locking the model for every account in the pool ([#13061](https://github.com/diegosouzapw/OmniRoute/pull/13061)) — thanks @HouMinXi +- fix(compression): spawn the LLMLingua worker with a file URL object so compression actually runs instead of silently failing open on Node ([#13093](https://github.com/diegosouzapw/OmniRoute/pull/13093)) — thanks @anhtahaylove +- **fix(test):** run the local `test` and `test:unit` scripts at concurrency 4 so a full-suite run no longer exhausts the machine's commit charge and kills unrelated processes ([#13187](https://github.com/diegosouzapw/OmniRoute/pull/13187)) — thanks @anhtahaylove +- **fix(monitoring):** `GET /api/monitoring/health` `credentialHealth` now includes a bounded `failedConnections` list (`connectionId`, `status`, sanitized `lastError`) when the probe-cache gauge `failed>0`, plus `source: probe-cache` and a cheap `staleDbNonOkCount` for sticky SQLite `test_status` on active rows. Documents that the live gauge is not `provider_connections.test_status`. ([#12876](https://github.com/diegosouzapw/OmniRoute/pull/12876)) — thanks @RaviTharuma +- **fix(resilience):** an openai-compatible multi-upstream gateway no longer marks the whole connection `credits_exhausted` when a single upstream (or the `/models` / representative-model health probe) returns 402. Quota failures stay model-scoped; true connection-wide auth failures are unchanged. Single-credential 402 key disable ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239)) is preserved. ([#12875](https://github.com/diegosouzapw/OmniRoute/pull/12875)) — thanks @RaviTharuma +- **fix(dashboard):** nine fixes on the Orchestration Canvas, all diagnosed in the Phase 2 reviews. A source that fails now keeps the timestamp of its FIRST failure instead of being re-stamped every poll — the stale line said "since the last poll" no matter how long the source had been down, and the churn also defeated the snapshot's stable identity (it serializes the source list), so the canvas re-rendered on every tick while anything was broken. A source that HAD data and only then started failing is flagged too: previously only a source with no node at all got the warning, so a source that went down mid-session kept a healthy-looking node forever. The rest are pointwise: clicking a filter chip cancels the pending search debounce (left armed, it fired ~300ms later and silently reverted the chip); the search input carries an accessible name; `?state=running, failed` parses like the unpadded form instead of dropping the padded value; the CSV toggle helper is defined once in `model/urlParams.ts` rather than twice; the agents tab tells "nothing running" apart from "the filter matched nothing", offering a clear-filters button instead of setup links that would be wrong advice there; edges stop emitting SMIL particles above 40 simultaneously active edges, keeping the colored stroke; and the drawer's error banner clears when a retried action succeeds. ([#12983](https://github.com/diegosouzapw/OmniRoute/pull/12983)) +- **fix(i18n):** the nine locales added with the EU-language batch (Greek, Estonian, Irish, Croatian, Lithuanian, Latvian, Maltese, Slovenian, Serbian) were missing the eleven Orchestration Canvas keys that Phase 3 introduced, so the compare-runs panel and the "no runs match these filters" empty state fell back to English in those languages (`deepMergeFallback` substitutes English for an absent key, so nothing rendered blank — it rendered untranslated). The coverage gate does not catch this: it enforces an 80% floor per locale, and eleven missing keys out of ~13,000 leaves coverage at 99.9%. Translated for real in each language, calibrated against the wording each file already uses for "run", "filter" and "skill". ([#13294](https://github.com/diegosouzapw/OmniRoute/pull/13294)) +- **fix(dashboard):** five follow-ups from the Phase 2 Orchestration Canvas review. The History drawer now shows the memory section for runs that already left the live TTL window: the persisted `memory_hits` event is parsed into `metadata.memoryHits` with the same defensive validation the drawer applies, and — because that event is observability rather than a state transition — it no longer leaks into the timeline, where it had been inheriting the task's state and rendering as a duplicate transition. The A2A memory recall runs against its own 1.5s deadline instead of inheriting the memory backend's 30s one; overshooting degrades exactly like any other recall failure (no hits, task proceeds), and the timer is cleared on both paths. Repeating a Conductor run carries its `requirements.cli`/`requirements.model` forward, so the new run is pinned to the same runner profile and model rather than drifting to whatever the fleet picks. A successful repeat from the Agents tab now focuses the run it created, instead of leaving the operator on the finished one. And the auth test for `POST /api/conductor/tasks` moved into the shared `ROUTES` array rather than restating the pattern. ([#12988](https://github.com/diegosouzapw/OmniRoute/pull/12988)) +- fix(plugins): stop leaking an exit listener per plugin hook timeout, which triggered MaxListenersExceededWarning on plugins that ignore SIGTERM ([#13092](https://github.com/diegosouzapw/OmniRoute/pull/13092)) — thanks @anhtahaylove +- **fix(validation):** Provider node edits no longer fail with a generic "Invalid request" when the optional daily-quota reset fields are left blank. The dashboard sends `dailyQuotaResetTimezone` and `dailyQuotaResetHour` as `null`, and only the hour accepted it. ([#13066](https://github.com/diegosouzapw/OmniRoute/issues/13066)) ([#13083](https://github.com/diegosouzapw/OmniRoute/pull/13083)) — thanks @ntdatt812 +- **fix(combo):** quota-aware expansion drops banned, inactive, missing, and wrong-provider connections before quota fetch or model dispatch; pins and allowlists stay selectors, not a bypass. Antigravity automatic exhaustion now requires a reported zero remaining, so a positive balance below 1% stays eligible. ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- Fix an intermittent failure in the quota-weighted routing test suite: scores are a function of `Date.now()`, so peers with identical quota scored microseconds apart never tied and swapped order. ([#12946](https://github.com/diegosouzapw/OmniRoute/pull/12946)) +- fix(volcengine): resolve quota window mapping gaps, safe multi-connection console binding, and response cookie sanitization ([#12950](https://github.com/diegosouzapw/OmniRoute/pull/12950)) — thanks @HouMinXi +- **fix(test):** resolve the WebDAV handler path with `fileURLToPath` so the suite's 37 WebDAV tests run on Windows instead of failing with a doubled `C:\C:\` drive prefix ([#13196](https://github.com/diegosouzapw/OmniRoute/pull/13196)) — thanks @anhtahaylove +- **fix(ui):** fall back from invalid AnySearch icon alias (direct commit `9587e07b69`) +- **fix(sse):** stop rebuilding a truncated summary from the collector's cap-dropped event array ([#12718](https://github.com/diegosouzapw/OmniRoute/pull/12718)) — thanks @hartmark +- **fix(conversations):** show a pending spinner for unresolved tool nodes instead of "(empty)" ([#12727](https://github.com/diegosouzapw/OmniRoute/pull/12727)) — thanks @hartmark +- **fix(streaming):** fail fast when an upstream stream produces only lifecycle/heartbeat events, never real content ([#12741](https://github.com/diegosouzapw/OmniRoute/pull/12741)) — thanks @hartmark +- **fix(responses-continuation):** bridge the write-in-flight window with an in-memory pending store ([#12854](https://github.com/diegosouzapw/OmniRoute/pull/12854)) — thanks @hartmark +- **fix(translator):** drop or map agent_message on Chat Completions fallback ([#12880](https://github.com/diegosouzapw/OmniRoute/pull/12880)) — thanks @RaviTharuma +- **fix(api):** page and stream-complete GET /v1/models for large catalogs ([#12882](https://github.com/diegosouzapw/OmniRoute/pull/12882)) — thanks @RaviTharuma +- **fix(quota):** align AUTH window labels with usage API and clarify 503 ([#12884](https://github.com/diegosouzapw/OmniRoute/pull/12884)) — thanks @RaviTharuma +- **fix(ci):** clear two base-reds on release/v3.8.51 — mutation-coverage gate + image-only-model guard ([#12945](https://github.com/diegosouzapw/OmniRoute/pull/12945)) +- **fix(api):** scope batch bulk-delete to the calling API key ([#13211](https://github.com/diegosouzapw/OmniRoute/pull/13211)) +- **fix(tests):** align model-sync log assertion to shipped path redaction (base-red) ([#13227](https://github.com/diegosouzapw/OmniRoute/pull/13227)) +- **fix(test):** make the ToS heading guard actually require the parentheses ([#13228](https://github.com/diegosouzapw/OmniRoute/pull/13228)) +- **fix(quality):** register 4 drifted covering tests in stryker tap.testFiles ([#13229](https://github.com/diegosouzapw/OmniRoute/pull/13229)) +- **fix(logging):** mask *-api-key headers in request-log pipeline (#13273) ([#13401](https://github.com/diegosouzapw/OmniRoute/pull/13401)) — thanks @KooshaPari +- **fix(tests):** correct stale Jina catalog prefix in custom-models test ([#13403](https://github.com/diegosouzapw/OmniRoute/pull/13403)) — thanks @KooshaPari +- **fix(backend):** prune old backups after health-check-repair snapshots (#13308) ([#13404](https://github.com/diegosouzapw/OmniRoute/pull/13404)) — thanks @KooshaPari +- **fix(backend):** guard memory_fts_au trigger with WHEN clause to stop FTS bloat (#13326) ([#13405](https://github.com/diegosouzapw/OmniRoute/pull/13405)) — thanks @KooshaPari +- **fix(free-tiers):** remove obsolete Together $25 signup credit (#12526) ([#13407](https://github.com/diegosouzapw/OmniRoute/pull/13407)) — thanks @KooshaPari +- **fix:** skip non-chat generation models in Test all models ([#13409](https://github.com/diegosouzapw/OmniRoute/pull/13409)) — thanks @KooshaPari +- **fix(providers):** de-duplicate agy and antigravity model catalogs ([#13410](https://github.com/diegosouzapw/OmniRoute/pull/13410)) — thanks @KooshaPari +- **fix(dashboard):** send provider in body for mini-playground web search ([#13411](https://github.com/diegosouzapw/OmniRoute/pull/13411)) — thanks @KooshaPari +- **fix(resilience):** add CJK quota-exhaustion patterns to 429 classifier ([#13412](https://github.com/diegosouzapw/OmniRoute/pull/13412)) — thanks @KooshaPari +- **fix(backend):** eval runner: failed calls no longer score as passed + dotAll regex ([#13413](https://github.com/diegosouzapw/OmniRoute/pull/13413)) — thanks @KooshaPari +- **fix(backend):** eval runner opts out of output-style and memory injection ([#13414](https://github.com/diegosouzapw/OmniRoute/pull/13414)) — thanks @KooshaPari +- **fix:** chain serial quarantine step in npm test script (fixes #13286) ([#13418](https://github.com/diegosouzapw/OmniRoute/pull/13418)) — thanks @KooshaPari +- **fix(antigravity):** derive stable session ID from credentials for prompt-cache reuse (#13305) ([#13424](https://github.com/diegosouzapw/OmniRoute/pull/13424)) — thanks @KooshaPari +- **fix(memory):** merge memory into top-level system field instead of unshifting at messages[0] (#13425) ([#13427](https://github.com/diegosouzapw/OmniRoute/pull/13427)) — thanks @KooshaPari +- **fix(compression):** preserve instruction blocks from lossy engines ([#13523](https://github.com/diegosouzapw/OmniRoute/pull/13523)) — thanks @KooshaPari +- **fix(compression):** preserve polarity words and newlines in ultra heuristic ([#13524](https://github.com/diegosouzapw/OmniRoute/pull/13524)) — thanks @KooshaPari +- **fix(compression):** run user preservePatterns before built-in patterns (#13457) ([#13525](https://github.com/diegosouzapw/OmniRoute/pull/13525)) — thanks @KooshaPari +- **fix(reasoning):** expand deepseek-v4 regex to match v4.1+ and add command-code provider (#13430) ([#13528](https://github.com/diegosouzapw/OmniRoute/pull/13528)) — thanks @KooshaPari +- **fix(providers):** add alwaysReasons metadata for reka-flash-3 ([#13531](https://github.com/diegosouzapw/OmniRoute/pull/13531)) — thanks @KooshaPari +- **fix(playground):** allow Compare columns to scroll independently ([#13532](https://github.com/diegosouzapw/OmniRoute/pull/13532)) — thanks @KooshaPari +- **fix(security):** empty whitelist allows through to prevent admin lockout ([#13534](https://github.com/diegosouzapw/OmniRoute/pull/13534)) — thanks @KooshaPari +- **fix(tests):** add DISABLE_IOREG_STRATEGY env var for macOS test compatibility (fixes #13467) ([#13539](https://github.com/diegosouzapw/OmniRoute/pull/13539)) — thanks @KooshaPari +- **fix(zed-hosted):** map developer role and Gemini enums for Zed proxy ([#13543](https://github.com/diegosouzapw/OmniRoute/pull/13543)) — thanks @KooshaPari +- **fix(analytics):** normalize provider aliases to canonical id in usage_history ([#13545](https://github.com/diegosouzapw/OmniRoute/pull/13545)) — thanks @KooshaPari +- **fix(combo):** use traceId for call log id to prevent UNIQUE constraint failure ([#13546](https://github.com/diegosouzapw/OmniRoute/pull/13546)) — thanks @KooshaPari +- **fix(auth):** check model lockout before returning synthetic noauth connection ([#13547](https://github.com/diegosouzapw/OmniRoute/pull/13547)) — thanks @KooshaPari +- **fix(guardrails):** skip credential redaction for base64 image data URLs ([#13550](https://github.com/diegosouzapw/OmniRoute/pull/13550)) — thanks @KooshaPari +- **fix(resilience):** increase requestQueue.maxWaitMs default from 15s to 30s ([#13553](https://github.com/diegosouzapw/OmniRoute/pull/13553)) — thanks @KooshaPari +- **fix(docs):** document OMNIROUTE_READY_TIMEOUT_MS and allowlist the test-only DISABLE_IOREG_STRATEGY ([#13692](https://github.com/diegosouzapw/OmniRoute/pull/13692)) + ### 📝 Maintenance ---- +- **docs(readme):** reconcile live v3.8.50 provider, free-tier, CLI, routing, test, community, sponsor, acknowledgment, and SVG metrics with their audited source denominators, including a deduplicated OmniRoute-in-Action snapshot and distinct contributor rankings for merged pull requests, GitHub-attributed commits, and Git history ([#11356](https://github.com/diegosouzapw/OmniRoute/pull/11356)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **docs:** sync the canonical provider count 354 → 356 across `README.md`, `AGENTS.md`, `llm.txt` (+ 42 i18n mirrors), the four README SVG diagrams, `docs/reference/PROVIDER_REFERENCE.md` (regenerated) and the `package.json` description after Opper (#11629) and 1min.ai (#11631) boarded the catalog — closes the `check:docs-counts-sync` strict drifts that kept `release/v3.8.51` red ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11673](https://github.com/diegosouzapw/OmniRoute/pull/11673)) — thanks @jonlwheat2-gif +- **test(providers):** update count-derived assertions after the v3.8.51 provider additions — `APIKEY_PROVIDERS` 233 → 235 (Opper #11629 + 1min.ai #11631), reserved-prefix REGISTRY walk 395 → 398, `WEB_FETCH_PROVIDERS` now includes `nimble-search` (#11620), and the provider translate-path golden snapshot regenerated ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11674](https://github.com/diegosouzapw/OmniRoute/pull/11674)) — thanks @jonlwheat2-gif +- **test(sse):** bump the hard-lease connection-query inventory for `src/lib/tokenHealthCheck.ts` to 2 — the verify-only web-cookie sweep added by #11495 split the single `getProviderConnections` call into oauth + cookie variants, which the frozen inventory had not tracked ([#11495](https://github.com/diegosouzapw/OmniRoute/pull/11495)). ([#11677](https://github.com/diegosouzapw/OmniRoute/pull/11677)) — thanks @jonlwheat2-gif / @oyi77 +- **chore(release):** clear the `release/v3.8.51` base-red gates — docs migration count (+38 i18n mirrors), stryker `tap.testFiles`, lockfile registry host, stale ESLint suppressions, the 7 lint regressions they were hiding, the stale `APIKEY_PROVIDERS` partition count (231 → 233), the `glm-5.3-max` tier missing from the GLM test table, a dead `modelType === "chat"` comparison in the models catalog, and the `resolveLiveWsUrl` / `sanitizeLiveWsPort` helpers from #11388 that a later merge dropped from `wsPath.ts` (dashboard live WebSocket URL resolution) ([#11502](https://github.com/diegosouzapw/OmniRoute/pull/11502)) — thanks @MumuTW +- **chore(free-models):** the free-tier regime table now declares which totals figure each regime feeds and whether it may take the no-auth shortcut, so the sets that used to repeat those answers by hand are derived from it — a new `freeType` no longer compiles until it has answered every question, instead of silently contributing to no total ([#11537](https://github.com/diegosouzapw/OmniRoute/pull/11537)) — thanks @maxmad64bis +- **docs(routing):** LKGP strategy copy now states provider-level stickiness and the explicit rules fallback instead of promising per-target pinning ([#11555](https://github.com/diegosouzapw/OmniRoute/pull/11555)) — thanks @maxmad64bis +- **test(chatcore):** pin the response tool-name alias resolution against the ordering hazard that broke every Gemini/Antigravity MCP tool call in v3.8.49. `extractRequestToolIdentityMap` deletes `translatedBody._toolNameMap`, so the later read is always undefined and the ledger survives only through the `requestToolIdentityMap` fallback — removing that fallback previously left the entire tool-name suite green. The resolution moves into `resolveResponseToolNameMap()` next to the map it depends on, with a regression guard that fails without the recovery. No behaviour change. ([#11575](https://github.com/diegosouzapw/OmniRoute/pull/11575)) — thanks @amartinawi +- **docs(env):** document `OMNIROUTE_CORPUS_CACHE_SIZE` in `.env.example` and `ENVIRONMENT.md`, so the env/docs contract check stops failing Docs Gates on every branch ([#11576](https://github.com/diegosouzapw/OmniRoute/pull/11576)) — thanks @ntdatt812 +- **chore(stryker):** register `route-guard-tunnel-processes-local-only` and `universal-quota-aware-routing` in `tap.testFiles`, so their mutant kills count for the three modules they cover ([#11577](https://github.com/diegosouzapw/OmniRoute/pull/11577)) — thanks @ntdatt812 +- **test(authz):** pin `LOCAL_ONLY_API_GET_EXEMPTIONS` by exact membership instead of by entry count, so the guard names the offending path and also catches a substitution ([#11580](https://github.com/diegosouzapw/OmniRoute/pull/11580)) — thanks @ntdatt812 +- **test(build):** pin `resolveNextBuildBundlerFlag` to the `OMNIROUTE_USE_TURBOPACK` contract it implements, across runtimes, instead of asserting an unimplemented Bun override ([#11583](https://github.com/diegosouzapw/OmniRoute/pull/11583)) — thanks @ntdatt812 +- **test(claude-web):** the Claude Web transport test's barrel anchors no longer pin the exact construction spelling. #11421 made the executor map lazy, so `/"claude-web": new ClaudeWebExecutor\(\)/` stopped matching and the test went red on `release/v3.8.51` itself — a base-red gate every open PR inherited. The anchors now match the alias key and the class name and tolerate either spelling in between, and were checked to still fail when an alias is genuinely repointed at another executor ([#11585](https://github.com/diegosouzapw/OmniRoute/pull/11585)) — thanks @ntdat812 +- **test(models):** pin `qwen3.8-max` resolution per provider — two of the four catalogs now serve the bare id and no longer carry `-preview` ([#11590](https://github.com/diegosouzapw/OmniRoute/pull/11590)) — thanks @ntdatt812 +- **test(executors):** drop the stale 128-tool truncation assertion that contradicted the #11444 pass-through contract ([#11591](https://github.com/diegosouzapw/OmniRoute/pull/11591)) — thanks @ntdatt812 +- **test(token-health):** pin the current terminal-skip boundary — expired with retry budget is probed, `account_deactivated` stays skipped ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)) — thanks @ntdatt812 +- **test(models):** follow #11503's retarget of the `gemini-3-pro-high` alias to the hyphenated catalog id ([#11593](https://github.com/diegosouzapw/OmniRoute/pull/11593)) — thanks @ntdatt812 +- **refactor(video bridge):** extract per-video acquisition, whole-result caching, description, and metrics/abort/cleanup out of `VideoBridgeGuardrail.preCall` into a `processVideoPart` seam in a new `videoBridgePipeline.ts`, behind explicit `VideoMediaBrokerPort`, `VideoAudioTranscriptionPort`, and `VideoDrilldownPort` boundaries; `preCall` now only handles request traversal, policy, and response aggregation. The Video tab's FFmpeg/ffprobe runtime status is now an explicit `unknown` / `restricted` / `unavailable` / `available` state instead of a nullable boolean pair, fixing a case where an in-flight or failed probe was mislabeled as "install FFmpeg" ([#11657](https://github.com/diegosouzapw/OmniRoute/issues/11657)). ([#12016](https://github.com/diegosouzapw/OmniRoute/pull/12016)) +- **test(opencode-plugin):** add unit test coverage for `formatFreeBudget()` naming helper ([#11660](https://github.com/diegosouzapw/OmniRoute/pull/11660)) — thanks @f9td56dbgh-hub +- **docs(video):** clarify that the Video Bridge transcript `source` field (`client`, `embedded`, `audio-bridge`) is presently caller-declared and not yet server-verified — OmniRoute enforces the enum shape but does not cryptographically confirm that an `embedded`/`audio-bridge` label came from a server-owned extraction ([#11661](https://github.com/diegosouzapw/OmniRoute/issues/11661)). ([#12001](https://github.com/diegosouzapw/OmniRoute/pull/12001)) +- **chore(providers):** retire the distributed Raycast Relay and Hailuo Web integrations after a provenance audit confirmed substantial derivation from GPL-3.0 sources; the official GitHub and MiniMax API providers remain available. Migration 163 disables legacy connections and invalidates their active leases while preserving connection IDs, audit/history rows, quota snapshots, usage records, and fail-closed API-key connection policies. ([#11691](https://github.com/diegosouzapw/OmniRoute/pull/11691)) +- **chore(providers):** retire the Felo Web provider while its GPL-derived provenance remains on licensing HOLD ([#11698](https://github.com/diegosouzapw/OmniRoute/pull/11698)) — stale runtime and database references now fail closed while existing connection, lease, and usage history remains preserved for auditability. +- **fix(sse):** retire the Gemini Web image-generation bridge while provenance and licensing remain on HOLD, preserving Gemini Web chat/auth/tools and legitimate Gemini/Nano Banana image providers ([#11708](https://github.com/diegosouzapw/OmniRoute/pull/11708)) +- **chore(audio):** retire the built-in EdgeTTS provider and its 11-voice catalog while provenance/licensing review remains on HOLD; the generic `/v1/audio/speech` endpoint and the other speech providers remain available ([#11711](https://github.com/diegosouzapw/OmniRoute/pull/11711)) +- **fix(providers):** retire the unofficial Qwen Web integration and its `qwen-web`/`qw` identities while third-party provenance remains on HOLD ([#11713](https://github.com/diegosouzapw/OmniRoute/pull/11713)) — direct requests now fail closed with a sanitized HTTP 410 and combos skip the retired target, while migration guards preserve connection IDs, allowlists, usage/audit history and invalidate active leases. Supported Qwen API, Qwen Code, cloud, token-plan, audio, local and model-name surfaces remain available; restoring Qwen Web requires a qualified provenance/license decision. +- **fix(providers):** retire `microsoft-designer-web` and its `msdesigner` alias from runtime and catalogs while preserving stored credentials, provider history and allowlists as inactive records — source dependency on gpt4free's [`MicrosoftDesigner.py`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/g4f/Provider/needs_auth/MicrosoftDesigner.py) is proven at snapshot `0943d16eef757a44deb60651c07bbe5fa958cafe`, whose [`LICENSE`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/LICENSE) is GPL-3.0; expressive adaptation is probable, which records provenance/licensing risk without asserting a legal conclusion ([#11720](https://github.com/diegosouzapw/OmniRoute/pull/11720)). +- **docs(legal):** centralize pinned MIT notices for the vendored GCF codec and local flag assets ([#11726](https://github.com/diegosouzapw/OmniRoute/pull/11726)) +- Split the 2,952-line `adobeFireflyClient.ts` into nine focused modules (catalog, credentials, payload, ARP session, upload, responses, IMS, account, poll) behind an unchanged barrel — no consumer edits and no public API change (#11728). — thanks @MumuTW +- **fix(dashboard):** replaced the Model Lockout card's unprovenanced notification MP3 with a short, locally synthesized Web Audio chime ([#11731](https://github.com/diegosouzapw/OmniRoute/pull/11731)) — both toggles retain optional audible feedback without shipping replacement media or third-party code, and unsupported, suspended, or rejected audio contexts fail open without blocking the setting change. +- **chore(deepseek-web):** remove the provenance-unresolved DeepSeek PoW WASM binary and its runtime loader/tracing while retaining the existing JavaScript solver (slower at high difficulty) ([#11732](https://github.com/diegosouzapw/OmniRoute/pull/11732)). +- **chore(assets):** remove four unused provider assets whose redistribution provenance could not be verified while retaining the active Freebuff SVG ([#11735](https://github.com/diegosouzapw/OmniRoute/pull/11735)) +- **docs(assets):** retired two raster images whose redistribution provenance could not be verified ([#11736](https://github.com/diegosouzapw/OmniRoute/pull/11736)) — PicoClaw documentation now reuses the existing SVG, while Zoo Code remains a text link without an image. +- **docs(legal):** add pinned notices for six LobeHub-derived provider SVGs and 65 byte-exact theSVG assets, retaining registry claims as unverified metadata with explicit trademark and HOLD boundaries ([#11737](https://github.com/diegosouzapw/OmniRoute/pull/11737)). +- **chore(quality):** added a versioned 225/225 provider-asset provenance manifest and blocking drift gate ([#11743](https://github.com/diegosouzapw/OmniRoute/pull/11743)) — the inventory pins magic MIME and SHA-256 for every physical file, records immutable source matches for 71 assets, keeps 69 probable and 85 unresolved entries explicit but non-blocking, and treats provenance as source evidence rather than copyright or trademark clearance. +- **refactor(mitm):** replace four previously derived Traffic Inspector utilities — conversation normalization, SSE merging, secret masking, and header sanitization — with independent clean-room implementations based on public provider schemas, WHATWG SSE, and RFC 6750, while keeping the two independently classified passthrough surfaces unchanged ([#11748](https://github.com/diegosouzapw/OmniRoute/pull/11748)). +- Added a unit test that fails seven days before any dated pack under `config/` (`validUntil` and sibling keys) lapses, naming the file and key. The Alibaba free-tier pack expired on 2026-08-27 and turned every PR red the next morning with no commit involved; renewal now happens on someone's terms, not the clock's. ([#11891](https://github.com/diegosouzapw/OmniRoute/pull/11891)) +- `check:workflows` now fails (under `--strict`/`--ratchet`) when any job routed to a self-hosted runner publishes with `--provenance` — npm rejects that with `422` at the registry, which in v3.8.50 only surfaced after the tag and Docker images were public. ([#11895](https://github.com/diegosouzapw/OmniRoute/pull/11895)) +- `scripts/ops/runner-janitor.sh` now proves a path is idle with one `lsof` snapshot and removes stale leftovers itself (tmpfs after 3 h — it is RAM — disk after 24 h), kills orphan `next-build` processes, prunes checkouts of stopped runners, and alerts on memory pressure; `--dry-run` shows exactly what it would do. `docs/ops/RUNNER_BOX.md` reconciled to the measured box (31 GB, 10 listeners). ([#11893](https://github.com/diegosouzapw/OmniRoute/pull/11893)) +- The `next-build` artefact (1.3 GB) is now written and read under `$RUNNER_TEMP` (per-runner, on disk) instead of `/tmp`, which on the self-hosted pool is a 12 GB tmpfs in RAM. Landing it there took 27–32 of the publish job's 76 minutes, and the fixed `/tmp/e2e-build.tar.gz` name let E2E jobs on different runners overwrite each other's download. ([#11896](https://github.com/diegosouzapw/OmniRoute/pull/11896)) +- The CI `build` job now runs in two concurrency lanes — `main` and pull requests — so a release build is never queued behind (or OOM-killed beside) PR builds on the self-hosted pool, which holds one `next-build` comfortably and two at the edge. ([#11901](https://github.com/diegosouzapw/OmniRoute/pull/11901)) +- Re-freeze the ESLint suppressions on `release/v3.8.51` from a clean-room measurement (2 stale file entries pruned, 55 pre-existing `no-explicit-any` in six new files frozen under #11924) and drop the dead `GPT_SIZE_MAP` constant orphaned by the Adobe Firefly client split, so `No new ESLint warnings` stops failing every PR with exit 2 (Refs #11924) ([#11955](https://github.com/diegosouzapw/OmniRoute/pull/11955)) +- Type the 55 `no-explicit-any` sites that had been frozen under #11924 — four redundant casts in `socksConnectorWithFamily.ts` (undici/socks types already accept them) and the mocks/fixtures of the socks-timeout and isFree suites — and drop their suppression entries; the ESLint ratchet shrinks from 5487 to 5432 (Closes #11924) ([#11975](https://github.com/diegosouzapw/OmniRoute/pull/11975)) +- Take the two hosted-runner builds off the PR rail: `Build App` (`build.yml`) is `workflow_dispatch`-only and `DAST smoke (PR)` runs only for PRs into `main` — the 7 GB hosted VM cannot build `release/v3.8.51` in any profile (19/30 red, VM shutdown ~8 min into `next build`) and both checks had turned into permanent noise on every release PR; the bundle stays validated by `ci.yml` on `main` and by `nightly-release-green` on `release/**` (Closes #11946) ([#11962](https://github.com/diegosouzapw/OmniRoute/pull/11962)) +- Move the four nightly jobs that build the backend (`nightly-schemathesis`, `nightly-llm-security` promptfoo + garak, `nightly-resilience` axe-a11y) off the hosted 7 GB runner — where they died on `release/v3.8.51` unseen — onto the box's new `omni-light` pool (two listeners), and document the reshaped fleet (4 active OmniRoute listeners: 2 `omni-build` + 2 `omni-light`, janitor ceiling 4) (Closes #11965) ([#11967](https://github.com/diegosouzapw/OmniRoute/pull/11967)) +- Stop painting every fork PR into `release/**` red: `quality.yml` `Build (advisory)` is skipped (GitHub still reports `continue-on-error` failures as check FAILURE). Hosted `ubuntu-latest` cannot finish `npm run build` on this tree — same class as #11962 taking `build.yml` off the PR rail. `docker-publish.yml` amd64 now runs on the `.113` `omni-build` pool (31 GB / 32 cores, two listeners) with Turbopack, shares the `heavy-build-main` lane with `ci.yml` `Build` so it queues instead of becoming a third heavy, and keeps per-ref concurrency (a merge storm was starting 8 concurrent OOM builds). arm64 stays on `ubuntu-24.04-arm` with webpack — there is no ARM box. Fallback when `USE_VPS_RUNNER` is off: hosted amd64 + webpack (#11976). ([#12021](https://github.com/diegosouzapw/OmniRoute/pull/12021)) +- `docker-publish.yml` builds with webpack (`OMNIROUTE_USE_TURBOPACK=0`) on **both** arches, including amd64 on the `.113` `omni-build` pool. Turbopack had the RAM (31 GB, `omniroute-113-6`) and still panicked (`TurbopackInternalError: there must be a path to a root`, run 33253576569, 26 min in). The same tree's arm64 webpack build on hosted `ubuntu-24.04-arm` succeeded (run 33264823398). Dockerfile already documents webpack as the Docker escape hatch (#11976). ([#12050](https://github.com/diegosouzapw/OmniRoute/pull/12050)) +- **docs(free-tier):** declare the counting vs deciding regimes for "is it free?" and guard the deciding path from DB-backed catalog resolution ([#12226](https://github.com/diegosouzapw/OmniRoute/pull/12226)) — thanks @maxmad64bis +- **docs(auto-combo):** The mode pack table in `docs/routing/AUTO-COMBO.md` now lists all six shipped packs with every weight each one sets, replacing a four-pack table whose numbers had also drifted from the source. It states plainly that no pack sets `quality`, so selecting any pack silences the observed-quality signal. Six more documents that quote the scoring factor count joined the `check:docs-counts` gate, which caught five stale claims — including one naming nine factors that do not exist — and two stale mode pack counts. The dashboard routing panel, which offered four of the six packs and labelled the default strategy "6-Factor Scoring", is now covered by a test; the two packs it was missing are `reliability-first` and `chaos-mode`, the latter labelled as the fault-injection profile it is rather than as one more routing preference ([#12316](https://github.com/diegosouzapw/OmniRoute/pull/12316)) — thanks @maxmad64bis +- **chore(quality):** rebaseline `src/lib/db/apiKeys.ts` for the ACL the key-creation path now preserves ([#12352](https://github.com/diegosouzapw/OmniRoute/pull/12352)) ([#12673](https://github.com/diegosouzapw/OmniRoute/pull/12673)) — thanks @KrzysiekSko +- **chore(providers):** bump the Claude Code wire identity and the Devin bridge image pin from `2.1.220` to `2.1.258` ([#12402](https://github.com/diegosouzapw/OmniRoute/pull/12402)) — thanks @ggiak ([#12604](https://github.com/diegosouzapw/OmniRoute/pull/12604)) +- **docs(env):** align `.env.example`, the README Bun section, and the troubleshooting guide with the code: `OMNIROUTE_USE_TURBOPACK` also governs `npm run build` (not dev-only), `bun run build` follows that flag instead of auto-selecting Webpack, `OMNIROUTE_CHAT_MAX_HEAVY_IN_FLIGHT` is unset by default (no request-count cap), and the structural `503 chat_admission_busy` message matches `chatAdmissionResponses.ts` (#12404 — thanks @pacocartones) +- **chore(electron):** upgrade the desktop app to Electron 44 (Chromium 152, Node 24.18.1) ([#12217](https://github.com/diegosouzapw/OmniRoute/pull/12217)). **Requires macOS 13 (Ventura) or later** — Chromium dropped macOS 12 (Monterey), so Monterey users must stay on an earlier OmniRoute desktop build. Windows and Linux are unaffected; the app already shipped only x64/arm64, so Electron 44 dropping 32-bit builds changes nothing. Removes the `openAsHidden`/`wasOpenedAsHidden` login-item fields deleted in Electron 44 — hidden autostart continues to work through the `--hidden` argument registered with the login item ([#12554](https://github.com/diegosouzapw/OmniRoute/pull/12554)) — thanks @app/dependabot +- **chore(quality):** rebaseline `src/sse/handlers/chat.ts` for the effective-input persistence the continuation fix needs ([#12641](https://github.com/diegosouzapw/OmniRoute/pull/12641)) ([#12680](https://github.com/diegosouzapw/OmniRoute/pull/12680)) — thanks @hartmark +- `Coverage` job on `ci.yml`: the informational Codecov upload gets its own 5-minute ceiling and `continue-on-error`, and the job budget grows from 20 to 30 minutes (the 8-shard c8 merge alone takes ~10) — a stalled upload no longer ends the job `cancelled` and drags a fully green `main` run's conclusion down with it ([#11972](https://github.com/diegosouzapw/OmniRoute/pull/11972)) +- CI hardening on the PR rail: the four `Unit Tests fast-path` shards get `timeout-minutes: 30` (a hung shard held a PR for 64 min instead of GitHub's 6 h default) and both ESLint file caches lose their `restore-keys` fallback, so a cache built under another suppressions file or lint config can no longer report stale verdicts (Refs #11600, #11924) ([#11963](https://github.com/diegosouzapw/OmniRoute/pull/11963)) +- Every CI job that runs a `next build` (`build`, the npm `publish`, both release-green validations) now targets the `omni-build` runner label, which only two of the eight self-hosted runners carry. The box holds one build comfortably and two at the edge; a third now queues on GitHub instead of being OOM-killed by the kernel. ([#11932](https://github.com/diegosouzapw/OmniRoute/pull/11932)) +- **refactor(combo):** move `handleRoundRobinCombo` (and `resolveTargetTokenLimit`) into `open-sse/services/combo/roundRobinCombo.ts`. `combo.ts` drops from 2164 to 1014 lines (`split("\n").length`); the leaf is 1199 (under the 1200 new-file cap). The round-robin call site uses a dynamic `import()` so `releaseStickyPinOnFailure` / `clearStaleLKGP` can stay exported from `combo.ts` without a static cycle. Skip / sticky / semaphore / safety-timer behavior is unchanged. ([#12811](https://github.com/diegosouzapw/OmniRoute/pull/12811)) — thanks @HouMinXi +- **chore(quality):** rebaseline the file-size caps the error-boundary campaign grew past (`open-sse/executors/codex.ts`, `open-sse/vendor/codex-chatgpt-web/bridge.ts`, both via [#12444](https://github.com/diegosouzapw/OmniRoute/pull/12444)) ([#12654](https://github.com/diegosouzapw/OmniRoute/pull/12654)) +- **chore(quality):** rebaseline the file-size caps the hartmark batch grew past (`combos/page.tsx` via [#12355](https://github.com/diegosouzapw/OmniRoute/pull/12355), `open-sse/services/combo.ts` via [#12338](https://github.com/diegosouzapw/OmniRoute/pull/12338)) ([#12623](https://github.com/diegosouzapw/OmniRoute/pull/12623)) — thanks @hartmark +- **chore(quality):** rebaseline the file-size caps the HouMinXi batch grew past when its PRs stacked (`providers/page.tsx`, `chatCore.ts`, `accountFallback.ts`) — each PR measured correctly in isolation, none saw the stacking ([#12619](https://github.com/diegosouzapw/OmniRoute/pull/12619)) +- **chore(quality):** rebaseline `open-sse/services/combo.ts` for the reset-aware scoring the HouMinXi batch stacked ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) ([#12678](https://github.com/diegosouzapw/OmniRoute/pull/12678)) — thanks @HouMinXi +- **docs:** sync provider catalog count (direct commit `4f11b2ae3d`) +- **docs(skills):** sync omni-inference search fallback (direct commit `b4e76a7ed9`) +- **chore(lint):** prune two stale base suppressions (direct commit `1325047d56`) +- **test(auto-combo):** tolerate full-suite rotation load (direct commit `ad3e293f9f`) +- **test:** drain inherited quality snapshot reds (direct commit `21ed68d8ac`) +- **chore(quality):** rebaseline measured CLI bundle growth (direct commit `d35b3f9779`) +- **test(ui):** reproduce invalid AnySearch icon alias (direct commit `12cc6ca834`) +- **test(translator):** fix the relative imports of the relocated deferred-finish test (direct commit `777d9d1629`) +- **docs(counts):** bring the documented provider and migration counts to the measured values (direct commit `e70bea30e9`) +- **test(security):** drop the guard-target set left unused by the sync (direct commit `a32ba0e362`) +- **chore(db):** renumber model_capabilities migration 163 → 169 (cross-branch collision with 163_radar_feed_cache_generated_at) (direct commit `70f5d4cbf9`) +- **docs(cli):** document CLI_PRIME_AGENT_BIN and correct the CLI Agents count (direct commit `9058e39b61`) +- **chore(deps):** refresh runtimes and adopt ESLint 10 ([#11259](https://github.com/diegosouzapw/OmniRoute/pull/11259)) — thanks @backryun +- **refactor(local-corpus):** implement dynamic root resolution and LRU cache ([#11491](https://github.com/diegosouzapw/OmniRoute/pull/11491)) — thanks @raheemuddin786 +- **chore(quality):** prune stale eslint-suppressions entries ([#11596](https://github.com/diegosouzapw/OmniRoute/pull/11596)) +- **chore(ci):** unblock quality gates - env-doc sync + stryker tap.testFiles registration (#9282) ([#11609](https://github.com/diegosouzapw/OmniRoute/pull/11609)) — thanks @Meet6338-X +- **chore(ci):** point the circular-deps gate at dpdm's real JS entrypoint ([#11615](https://github.com/diegosouzapw/OmniRoute/pull/11615)) — thanks @benzntech +- **chore(quality):** rebaseline file-size for #11448/#11495/#11561 growth ([#11630](https://github.com/diegosouzapw/OmniRoute/pull/11630)) +- **deps:** realign onnxruntime-node pin to @huggingface/transformers' 1.24.3 lockstep ([#11633](https://github.com/diegosouzapw/OmniRoute/pull/11633)) — thanks @jonlwheat2-gif +- **test(autoCombo):** port models_dev_tier #11508 guard from node:test to vitest ([#11635](https://github.com/diegosouzapw/OmniRoute/pull/11635)) — thanks @jonlwheat2-gif +- **chore(quality):** rebaseline file-size for #11083/#11631/#11628 growth ([#11636](https://github.com/diegosouzapw/OmniRoute/pull/11636)) +- **chore(lint):** prune stale ESLint suppressions (base-red #11449) ([#11644](https://github.com/diegosouzapw/OmniRoute/pull/11644)) — thanks @jonlwheat2-gif +- **docs(changelog):** aggregate the ten v3.8.50 fragments into CHANGELOG.md ([#11683](https://github.com/diegosouzapw/OmniRoute/pull/11683)) +- **chore(search):** drop unreachable credential sweep after #11642 ([#11687](https://github.com/diegosouzapw/OmniRoute/pull/11687)) — thanks @HouMinXi +- **ci:** add API route TypeScript regression gate ([#11705](https://github.com/diegosouzapw/OmniRoute/pull/11705)) — thanks @Sabeekhann +- **docs(dedup):** correct the CodeQL claim on the tenant-namespace comment ([#11712](https://github.com/diegosouzapw/OmniRoute/pull/11712)) +- **docs(changelog):** add consolidated v3.8.50 stats, top-25 ranking and a .mailmap ([#11715](https://github.com/diegosouzapw/OmniRoute/pull/11715)) +- **docs:** recommend gstack for AI-assisted workflows ([#11770](https://github.com/diegosouzapw/OmniRoute/pull/11770)) — thanks @santosraju99-hub +- **chore(quality):** tighten complexity/cognitive-complexity ratchets to the current tip; land the missed gateways.ts rebaseline ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **docs(i18n):** finish Freepik → Magnific rebrand in locale strings and README ([#11772](https://github.com/diegosouzapw/OmniRoute/pull/11772)) — thanks @turbolego +- **refactor(auto-combo):** fix divergent scoring in combo health reporting ([#11854](https://github.com/diegosouzapw/OmniRoute/pull/11854)) — thanks @jacobsparts +- **chore(changelog):** add missing fragments for #11919/#11918/#11916 ([#11938](https://github.com/diegosouzapw/OmniRoute/pull/11938)) +- **test(check):** escape the runs-on fixture with JSON.stringify, not a quote-only replace ([#11942](https://github.com/diegosouzapw/OmniRoute/pull/11942)) (direct commit `fb7445eaa3`) +- **test(release):** align five suites with the contracts #11933, #11919 and #11876 shipped on release/v3.8.51 ([#11944](https://github.com/diegosouzapw/OmniRoute/pull/11944)) +- **chore(quality):** remove unreachable code and restore test discovery ([#11950](https://github.com/diegosouzapw/OmniRoute/pull/11950)) — thanks @backryun +- **docs(agents):** sync-back landings are fast-forward, never squash ([#11964](https://github.com/diegosouzapw/OmniRoute/pull/11964)) +- **test(infra):** retry recursive temp-dir removal instead of failing a shard on ENOTEMPTY (#11966) ([#11968](https://github.com/diegosouzapw/OmniRoute/pull/11968)) +- **docs(ops):** the .113 heavy-build ceiling is one runner, not two ([#12048](https://github.com/diegosouzapw/OmniRoute/pull/12048)) +- **test(ui):** correct inactive auto-fetch expectation ([#12098](https://github.com/diegosouzapw/OmniRoute/pull/12098)) — thanks @RaviTharuma +- **docs:** document native dependency check escape hatch ([#12101](https://github.com/diegosouzapw/OmniRoute/pull/12101)) — thanks @RaviTharuma +- **test(providers):** regenerate the translate-path golden for OrcaRouter (#11923) ([#12118](https://github.com/diegosouzapw/OmniRoute/pull/12118)) +- **chore(quality):** velocity phase — loosen every numeric baseline by 20% until v4.0, monitor headroom nightly ([#12125](https://github.com/diegosouzapw/OmniRoute/pull/12125)) +- **chore(lint):** batch 0 of #12146 — type the call-log-cap sqlite rows instead of 45 as-any casts ([#12157](https://github.com/diegosouzapw/OmniRoute/pull/12157)) +- **chore(lint):** batch 4 of #12146 — shared/components react-hooks violations resolved ([#12159](https://github.com/diegosouzapw/OmniRoute/pull/12159)) +- **chore(lint):** batch 1 of #12146 — dashboard/cli-code react-hooks violations resolved ([#12160](https://github.com/diegosouzapw/OmniRoute/pull/12160)) +- **chore(lint):** batch 3 of #12146 — dashboard/settings react-hooks violations resolved ([#12162](https://github.com/diegosouzapw/OmniRoute/pull/12162)) +- **chore(lint):** batch 2 of #12146 — resolve the react-hooks compiler violations in dashboard/providers ([#12163](https://github.com/diegosouzapw/OmniRoute/pull/12163)) +- **chore(quality):** register search-432 cooldown test in stryker tap.testFiles ([#12170](https://github.com/diegosouzapw/OmniRoute/pull/12170)) +- **chore(lint):** batch 5 of #12146 — combos, endpoint, provider-stats, api-manager and costs react-hooks violations resolved ([#12174](https://github.com/diegosouzapw/OmniRoute/pull/12174)) +- **build:** add contributor fast profile ([#12192](https://github.com/diegosouzapw/OmniRoute/pull/12192)) — thanks @rafacpti23 +- **docs(readme):** README touch-ups ([#12193](https://github.com/diegosouzapw/OmniRoute/pull/12193)) ([#12194](https://github.com/diegosouzapw/OmniRoute/pull/12194)) ([#12202](https://github.com/diegosouzapw/OmniRoute/pull/12202)) — thanks @rafacpti23 +- **build:** force the webpack bundler in the contributor build profile ([#12197](https://github.com/diegosouzapw/OmniRoute/pull/12197)) — thanks @rafacpti23 +- **build:** skip standalone packaging in the contributor profile ([#12198](https://github.com/diegosouzapw/OmniRoute/pull/12198)) — thanks @rafacpti23 +- **docs(audit):** align every published number with the code and harden check:docs-counts ([#12200](https://github.com/diegosouzapw/OmniRoute/pull/12200)) +- **build:** stub the instrumentation entrypoints in the contributor profile ([#12203](https://github.com/diegosouzapw/OmniRoute/pull/12203)) — thanks @rafacpti23 +- **build:** omit the standalone output target for contributor builds ([#12204](https://github.com/diegosouzapw/OmniRoute/pull/12204)) — thanks @rafacpti23 +- **chore(lint):** batch 6 of #12146 — memory, radar, audit, analytics, cache, usage, activity, home and RequestLoggerV2 react-hooks violations resolved ([#12208](https://github.com/diegosouzapw/OmniRoute/pull/12208)) +- **docs(diagrams):** rename number-carrying diagram files to stable names ([#12210](https://github.com/diegosouzapw/OmniRoute/pull/12210)) +- **docs(dashboard):** redraw onboarding tier-flow SVGs for the real 4-tier model ([#12211](https://github.com/diegosouzapw/OmniRoute/pull/12211)) +- **docs(api):** document every implemented route in openapi.yaml (276 → 692 paths) ([#12212](https://github.com/diegosouzapw/OmniRoute/pull/12212)) +- **chore(lint):** batch 7 of #12146 — final src tail: 37 react-hooks violations across 33 files resolved ([#12244](https://github.com/diegosouzapw/OmniRoute/pull/12244)) +- Revert "docs: recommend gstack for AI-assisted workflows (#11770)" ([#12248](https://github.com/diegosouzapw/OmniRoute/pull/12248)) ([#12249](https://github.com/diegosouzapw/OmniRoute/pull/12249)) +- **docs:** Chaos Mode setup guide + weighted strategy semantics ([#12250](https://github.com/diegosouzapw/OmniRoute/pull/12250)) +- **docs(agents):** protected-surface merge rule — operator approval for agent-instruction files ([#12253](https://github.com/diegosouzapw/OmniRoute/pull/12253)) +- **chore(quality):** register native-codex turn-pin tests in stryker tap.testFiles ([#12263](https://github.com/diegosouzapw/OmniRoute/pull/12263)) +- **chore(quality):** dedupe tap.testFiles entries added by racing base-red fixes ([#12265](https://github.com/diegosouzapw/OmniRoute/pull/12265)) +- **docs(readme):** introduce OmniRouteTray — the macOS menu-bar companion ([#12276](https://github.com/diegosouzapw/OmniRoute/pull/12276)) — thanks @ggiak +- **chore(proxy):** purge the legacy 1proxy residue — sync/rotator modules, dead DB exports, dead settings tab and flag, docs (#12091) ([#12290](https://github.com/diegosouzapw/OmniRoute/pull/12290)) +- **chore(quality):** baseline-headroom skips generated and vendored files in the fileSize worst-file signal ([#12291](https://github.com/diegosouzapw/OmniRoute/pull/12291)) +- **refactor(video):** unify the JPEG frame data-URI contract ([#12322](https://github.com/diegosouzapw/OmniRoute/pull/12322)) +- **chore(deps):** freeze onnxruntime-node and eslint-plugin-react-hooks out of dependabot groups ([#12329](https://github.com/diegosouzapw/OmniRoute/pull/12329)) +- **test(executors):** restore the #10986 reasoning-only fallback guards ([#12364](https://github.com/diegosouzapw/OmniRoute/pull/12364)) — thanks @pacocartones +- **chore(quality):** re-tighten the file-size ratchet to the real LOC (plan 3.8.52 task 0) ([#12411](https://github.com/diegosouzapw/OmniRoute/pull/12411)) +- **test(grok):** format web executor suite ([#12412](https://github.com/diegosouzapw/OmniRoute/pull/12412)) +- **chore(quality):** ratchet open-sse typecheck baseline to zero ([#12418](https://github.com/diegosouzapw/OmniRoute/pull/12418)) +- **test(build):** guard the artifact path policy arrays against duplicates ([#12422](https://github.com/diegosouzapw/OmniRoute/pull/12422)) +- **chore(lint):** adopt eslint-plugin-react-hooks 7.1.1 ([#12428](https://github.com/diegosouzapw/OmniRoute/pull/12428)) +- **chore(providers):** remove a keyless provider integration at its operator's request ([#12440](https://github.com/diegosouzapw/OmniRoute/pull/12440)) +- **docs(providers):** register providers removed at their operator's request and guard against reintroduction ([#12478](https://github.com/diegosouzapw/OmniRoute/pull/12478)) +- **chore(quality):** base fixes — stryker tap.testFiles + node_modules cache key ([#12482](https://github.com/diegosouzapw/OmniRoute/pull/12482)) +- **docs(arch):** one-process recipe for tens of long /v1/responses ([#12493](https://github.com/diegosouzapw/OmniRoute/pull/12493)) — thanks @RaviTharuma +- **chore(quality):** tighten the CodeQL ratchet baseline from 11 to 6 ([#12530](https://github.com/diegosouzapw/OmniRoute/pull/12530)) +- **chore(deps):** pin browserslist override to ^4.28.8 ([#12592](https://github.com/diegosouzapw/OmniRoute/pull/12592)) — thanks @KooshaPari +- **chore(deps):** bump fast-uri to 3.1.7 in the electron lockfile ([#12601](https://github.com/diegosouzapw/OmniRoute/pull/12601)) +- **docs(quality):** document how the CodeQL ratchet refreshes and how to tighten it ([#12611](https://github.com/diegosouzapw/OmniRoute/pull/12611)) +- **test(ci):** pin the openapi-security-tiers two-arm contract with an executing gate test (#12581) ([#12652](https://github.com/diegosouzapw/OmniRoute/pull/12652)) +- **docs(gamification):** align XP Rewards table with code (#12501) ([#12667](https://github.com/diegosouzapw/OmniRoute/pull/12667)) — thanks @KooshaPari +- **refactor(combo):** split executeTarget into gates, attempt, and loop ([#12746](https://github.com/diegosouzapw/OmniRoute/pull/12746)) — thanks @HouMinXi +- **docs(security):** document socket.yml scanner config + CI workflow link (#12575) ([#12764](https://github.com/diegosouzapw/OmniRoute/pull/12764)) — thanks @KooshaPari +- **chore(ci):** guard commit identity in pre-commit to stop author misattribution ([#12772](https://github.com/diegosouzapw/OmniRoute/pull/12772)) +- **deps:** 17 Dependabot bumps — bump electron from 43.4.0 to 43.4.1 in /electron ([#11426](https://github.com/diegosouzapw/OmniRoute/pull/11426)); bump the development group across 1 directory with 8 updates ([#11428](https://github.com/diegosouzapw/OmniRoute/pull/11428)); bump the production group across 1 directory with 11 updates ([#11440](https://github.com/diegosouzapw/OmniRoute/pull/11440)); chore(deps): bump github/codeql-action/init from 4.37.7 to 4.37.8 ([#11925](https://github.com/diegosouzapw/OmniRoute/pull/11925)); chore(deps): bump github/codeql-action from 4.37.7 to 4.37.8 ([#11926](https://github.com/diegosouzapw/OmniRoute/pull/11926)); chore(deps): bump github/codeql-action/analyze from 4.37.7 to 4.37.8 ([#11927](https://github.com/diegosouzapw/OmniRoute/pull/11927)); build(deps): bump github/codeql-action/init from 4.37.8 to 4.37.9 ([#12345](https://github.com/diegosouzapw/OmniRoute/pull/12345)); build(deps): bump github/codeql-action/analyze from 4.37.8 to 4.37.9 ([#12346](https://github.com/diegosouzapw/OmniRoute/pull/12346)); bump the development group across 1 directory with 2 updates ([#12347](https://github.com/diegosouzapw/OmniRoute/pull/12347)); build(deps): bump github/codeql-action from 4.37.8 to 4.37.9 ([#12349](https://github.com/diegosouzapw/OmniRoute/pull/12349)); bump browserslist from 4.28.2 to 4.28.8 ([#12396](https://github.com/diegosouzapw/OmniRoute/pull/12396)); bump the production group across 1 directory with 4 updates ([#12399](https://github.com/diegosouzapw/OmniRoute/pull/12399)); bump @xmldom/xmldom ([#12500](https://github.com/diegosouzapw/OmniRoute/pull/12500)); bump qs from 6.15.2 to 6.16.0 ([#12512](https://github.com/diegosouzapw/OmniRoute/pull/12512)); bump @xmldom/xmldom from 0.9.10 to 0.9.12 ([#12513](https://github.com/diegosouzapw/OmniRoute/pull/12513)); bump fast-uri from 3.1.5 to 3.1.7 ([#12514](https://github.com/diegosouzapw/OmniRoute/pull/12514)); bump @humanfs/node from 0.16.7 to 0.16.8 ([#12515](https://github.com/diegosouzapw/OmniRoute/pull/12515)) +- Clear the `release/v3.8.51` `check:agent-skills-sync` base-red: regenerate `skills/cli-tunnel/SKILL.md` so the `tunnel create [type]` positional that #13009 taught the generator to read is reflected in the committed skill. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` typecheck base-red from #12731: the new `Mode pack ranking gates` candidates in `open-sse/services/autoCombo/__tests__/autoCombo.test.ts` omitted the required `provider`, `model` and `errorRate` fields of `ProviderCandidate`, failing `check:open-sse-typecheck` (and with it `Fast Quality Gates`) on every open PR. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` docs-sync base-red: EURouter (#13025) and GreenPT (#13024) took the live provider count to 358, leaving 7 STRICT drifts (`PROVIDER_REFERENCE.md`, 4 diagrams, 2 tier-flow images) plus the `AGENTS.md` / `llm.txt` / `package.json` count claims stale. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- **test(models):** Pin `CATALOG_BUILD_TIMEOUT_MS` in the #12058 canonical-catalog regression so a cold catalog build racing the 8s default bound no longer flakes the suite with a 500 ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Allow `@omniroute/opencode-plugin-v2/` in the npm pack policy — [#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870) shipped the v2 plugin beside its v1 sibling without widening the allowlist, so every packed file under it read as an unexpected artifact and failed `check:pack-policy` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Point the release-green validator's pack gate at the tree it is validating — the build-provenance guard defaulted to `origin/main`, which a release branch tip can never be an ancestor of mid-cycle, so `Package artifact` reported a false red on every run and the tarball boot-smoke cascaded off it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **docs(checks):** keep doc counts honest — headings, rankings, catalog, weights, quality gate and scoring diagram now covered ([#12507](https://github.com/diegosouzapw/OmniRoute/pull/12507)) — thanks @maxmad64bis +- **chore(lifecycle):** `check:model-lifecycle` now also diffs `DEFAULT_DEGRADATION_MAP` (the background-task redirect table) against the vendor lifecycle snapshot, refusing a retired id as source or target, with a table-driven unit test beside it. Three rows whose source the vendor had retired — `claude-sonnet-4-20250514`, `gemini-3-pro-preview` and `gpt-5.1-codex` (whose target `gpt-5.1-codex-mini` is retired too) — were dead code, since `checkLifecycle` answers 410 before the redirect runs; they are dropped (#12535 — thanks @pacocartones) +- **test(compression):** cover idle worker eviction at the resource level — the pool must call `terminate()` and must not retain the worker's `MessagePort`, complementing the `exit`-event assertion added with the fix ([#13371](https://github.com/diegosouzapw/OmniRoute/pull/13371)) +- **chore(quality):** raise the `imageGeneration.ts` file-size ceiling for the image-only-model guard that clears the #12945 base-red ([#13392](https://github.com/diegosouzapw/OmniRoute/pull/13392)) +- **chore(quality):** type the combo-test route's JSON response bodies instead of casting them to `any`, drop the now-empty suppression entry, and remove the `hasPerModelQuota` import `chatCore.ts` stopped using when the failure-classification helper was extracted ([#13379](https://github.com/diegosouzapw/OmniRoute/pull/13379)) +- **chore(electron):** the desktop bundle no longer ships the root-level files of every translated docs mirror (`README.md`, `llm.txt`, `CLAUDE.md`, `GEMINI.md`, `CONTRIBUTING.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`) — the packaged app only reads `docs/i18n//docs/**`, which stays. Saves ~11 MB on top of the translated CHANGELOGs already pruned. (#0000) ([#13235](https://github.com/diegosouzapw/OmniRoute/pull/13235)) +- **chore(quality):** retighten the `src/sse/handlers/chat.ts` unused-vars suppression count to the 8 violations that actually remain ([#13385](https://github.com/diegosouzapw/OmniRoute/pull/13385)) +- Bring the documented counts back in line with the code: 171 migrations (was 169) and 20 routing strategies (was 19), plus the regenerated `cli-tunnel` skill reference. ([#12970](https://github.com/diegosouzapw/OmniRoute/pull/12970)) +- Update the documented migration count to 172 after the call-logs provider-stats indexes landed. ([#13209](https://github.com/diegosouzapw/OmniRoute/pull/13209)) +- **chore(ci):** two gates that close the blind spots behind the exclusions above. `check:vitest-exclusions` requires every Vitest exclusion to name a tracking issue and to appear in `config/quality/vitest-exclusions.json` — the previous list grew to 62 files behind a comment pointing at an issue that had been closed for a month. `check-new-key-coverage` requires a key newly added to `en.json` to reach every locale; the existing coverage gate is a percentage floor per locale, so eleven absent keys out of ~13,000 left it at 99.9% while a whole feature shipped untranslated in nine languages. Both are diff-aware, so pre-existing debt stays frozen and neither needed a migration to turn on. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **build:** root layout no longer loads Inter from `next/font/google`, so a production image build does not need fonts.googleapis.com ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **docs(i18n):** bring the eight new `llm.txt` mirrors (km, kn, ml, my, ne, or, pa, si) up to the current provider and migration counts, so `check-docs-sync` stops rejecting every commit ([#13674](https://github.com/diegosouzapw/OmniRoute/pull/13674)) +- **refactor(ui):** flow surfaces (home topology, combo live studio, compression cockpit/waterfall/nodes, token-health badge) express state through the theme-aware `--orch-status-*` tokens instead of fixed dark-mode hex, so green/red/amber/grey stay legible in the light theme; dark mode is byte-identical. Categorical palettes (routing-strategy hues, compression-layer pills, provider brand colors) deliberately stay hex ([#12378](https://github.com/diegosouzapw/OmniRoute/issues/12378)) ([#13203](https://github.com/diegosouzapw/OmniRoute/pull/13203)) +- **chore(tests):** 51 test files that had been excluded from Vitest are running again, restoring roughly 350 assertions to the blocking suite. Each was measured individually first: of the 62 files parked behind the `// #8618 — pre-existing failure` comment, 51 pass against the current tree with no source change, so the exclusions had outlived the failures they were added for. The 11 that genuinely still fail stay excluded, but now point at a live tracker (#13204) rather than at #8618, which was closed in August while the list it tracked kept growing. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **chore(quality):** drop two ESLint suppression entries whose violations no longer exist, so `eslint --suppressions-location` stops rejecting every commit that touches the surrounding files ([#13384](https://github.com/diegosouzapw/OmniRoute/pull/13384)) +- **chore(release):** bring main's post-tag fixes into release/v3.8.51 — sync-back step 2/2 (direct commit `21c488f210`) +- **chore(deps):** patch toml and esbuild in the opencode plugin lockfiles ([#12965](https://github.com/diegosouzapw/OmniRoute/pull/12965)) +- **docs(changelog):** reconcile the v3.8.51 living section — fold 366 fragments, cover every cycle commit, credit every contributor ([#12971](https://github.com/diegosouzapw/OmniRoute/pull/12971)) +- **test(plugin):** wait for the v2 lazy-refresh tier instead of sleeping past it ([#12984](https://github.com/diegosouzapw/OmniRoute/pull/12984)) +- **chore(quality):** rebaseline chatCore.ts after the non-streaming regression fixes ([#13045](https://github.com/diegosouzapw/OmniRoute/pull/13045)) +- **docs(changelog):** add the missing bullet marker to the DeepSeek PoW fragment ([#13200](https://github.com/diegosouzapw/OmniRoute/pull/13200)) +- **chore(deps):** drain the Dependabot queue — 10 of 13 alerts ([#13213](https://github.com/diegosouzapw/OmniRoute/pull/13213)) +- **chore(ci):** make worktrees carry the hooks that enforce the identity gate ([#13236](https://github.com/diegosouzapw/OmniRoute/pull/13236)) +- **chore(deps):** pin csv-parse to ^7.0.2 via overrides (#13117) ([#13300](https://github.com/diegosouzapw/OmniRoute/pull/13300)) — thanks @KooshaPari +- **chore(deps):** bump hono from ^4.12.34 to ^4.13.7 (#13148) ([#13301](https://github.com/diegosouzapw/OmniRoute/pull/13301)) — thanks @KooshaPari +- **chore(deps):** pin joi to ^18.2.8 via overrides (#13085) ([#13302](https://github.com/diegosouzapw/OmniRoute/pull/13302)) — thanks @KooshaPari +- **docs(providers):** add Microsoft 365 Copilot (BizChat) provider guide (#12779) ([#13340](https://github.com/diegosouzapw/OmniRoute/pull/13340)) — thanks @KooshaPari +### 🙌 Contributors + +Thanks to everyone whose work landed in v3.8.51: + +| Contributor | PRs / Issues | +| --- | --- | +| [@5dive-bot](https://github.com/5dive-bot) | #11852 | +| [@abhisheksharma2411](https://github.com/abhisheksharma2411) | #11547, #11684, #11959, #12209 | +| [@adevwithpurpose](https://github.com/adevwithpurpose) | #11464 | +| [@adivekar-utexas](https://github.com/adivekar-utexas) | #12015, #12043, #12090, #12138 | +| [@AIB1TAL0S](https://github.com/AIB1TAL0S) | #12103 | +| [@alltomatos](https://github.com/alltomatos) | #11448 | +| [@alvinveroy](https://github.com/alvinveroy) | #11857, #12027, #12165 | +| [@amaleta](https://github.com/amaleta) | #11906, #11934 | +| [@amartinawi](https://github.com/amartinawi) | #11575 | +| [@AndrianBalanescu](https://github.com/AndrianBalanescu) | #11083, #11830, #11834, #11844 | +| [@anhtahaylove](https://github.com/anhtahaylove) | #13091, #13092, #13093, #13096, #13097, #13100, #13106, #13109, #13114, #13155, #13166, #13171, #13175, #13187, #13196 | +| [@aniruddhaadak80](https://github.com/aniruddhaadak80) | #11760 | +| [@app](https://github.com/app) | #12554 | +| [@arminanton](https://github.com/arminanton) | #11461, #11513 | +| [@AStupidBear](https://github.com/AStupidBear) | #11584 | +| [@atescivitci-cmd](https://github.com/atescivitci-cmd) | #12105 | +| [@b3nw](https://github.com/b3nw) | #11970, #11971, #12147 | +| [@backryun](https://github.com/backryun) | #11259, #11950, #12075, #12076, #12078, #12079, #12081, #12082, #12181, #12228, #12239, #12255, #12258, #12277, #12367, #12423, #12524, #12538 | +| [@benjaminkitt](https://github.com/benjaminkitt) | #11747 | +| [@benzntech](https://github.com/benzntech) | #11614, #11615 | +| [@Bl0ck154](https://github.com/Bl0ck154) | #11948, #11951, #11952, #11953, #11954 | +| [@brick30llc-ctrl](https://github.com/brick30llc-ctrl) | #11752, #12242, #12262 | +| [@Chewji9875](https://github.com/Chewji9875) | #11455, #11875, #12028 | +| [@cheynetom](https://github.com/cheynetom) | #12058 | +| [@davidebaraldo](https://github.com/davidebaraldo) | #12222, #12304 | +| [@davidlinfr](https://github.com/davidlinfr) | #6390 | +| [@ddarkr](https://github.com/ddarkr) | #12124 | +| [@Deftera186](https://github.com/Deftera186) | #11809 | +| [@dpozimski](https://github.com/dpozimski) | #11945 | +| [@DW-MediaLab](https://github.com/DW-MediaLab) | #12229 | +| [@echel0nn](https://github.com/echel0nn) | #11923 | +| [@f9td56dbgh-hub](https://github.com/f9td56dbgh-hub) | #11660 | +| [@fabioluissilva](https://github.com/fabioluissilva) | #11991 | +| [@foreveryh](https://github.com/foreveryh) | #12177 | +| [@geek007git](https://github.com/geek007git) | #12115, #12116, #12117, #12120, #12122 | +| [@geekyNads](https://github.com/geekyNads) | #11784, #12266 | +| [@ggiak](https://github.com/ggiak) | #11561, #12276, #12350, #12402 | +| [@gonisulaimann](https://github.com/gonisulaimann) | #12368, #12369, #12371 | +| [@Gorillaz322](https://github.com/Gorillaz322) | #12207 | +| [@groovecityJO](https://github.com/groovecityJO) | #12682 | +| [@hartmark](https://github.com/hartmark) | #11434, #11452, #11473, #11499, #11703, #11983, #11984, #11985, #11986, #11988, #11989, #11990, #11994, #12221, #12293, #12445, #12446, #12447, #12448, #12460, #12461, #12623, #12646, #12650, #12680, #12717, #12718, #12727, #12741, #12854 | +| [@hizzt](https://github.com/hizzt) | #11894 | +| [@hongnoul](https://github.com/hongnoul) | #11484 | +| [@HouMinXi](https://github.com/HouMinXi) | #11411, #11414, #11512, #11518, #11520, #11641, #11642, #11643, #11687, #11779, #11849, #11850, #11851, #11915, #11916, #11918, #11919, #11920, #12013, #12017, #12033, #12042, #12106, #12139, #12166, #12169, #12171, #12205, #12213, #12312, #12325, #12487, #12488, #12495, #12504, #12557, #12566, #12590, #12591, #12624, #12626, #12632, #12637, #12678, #12696, #12697, #12711, #12733, #12746, #12767, #12770, #12789, #12803, #12805, #12811, #12866, #12868, #12899, #12926, #12934, #12950, #12951, #12974, #13001, #13006, #13011, #13017, #13026, #13027, #13034, #13035, #13038, #13042, #13050, #13060, #13061, #13069, #13107, #13120, #13136, #13195, #13197 | +| [@Hsia97](https://github.com/Hsia97) | #11624 | +| [@jacobsparts](https://github.com/jacobsparts) | #11854, #12155, #12167 | +| [@joglomedia](https://github.com/joglomedia) | #11980 | +| [@jonlwheat2-gif](https://github.com/jonlwheat2-gif) | #11443, #11450, #11549, #11567, #11608, #11633, #11635, #11640, #11644, #11671, #11672, #11673, #11674, #11675, #11676, #11677, #11706, #11783, #12051, #12052, #12053, #12055 | +| [@kanade-hoshino](https://github.com/kanade-hoshino) | #12180 | +| [@Karan825](https://github.com/Karan825) | #11861 | +| [@KaspaPulse](https://github.com/KaspaPulse) | #11389, #11469, #11628, #11666, #11910 | +| [@keeltrace](https://github.com/keeltrace) | #12080, #12223 | +| [@killer30001000](https://github.com/killer30001000) | #12178 | +| [@KooshaPari](https://github.com/KooshaPari) | #12470, #12592, #12667, #12699, #12703, #12706, #12764, #12769, #12771, #13300, #13301, #13302, #13340, #13401, #13403, #13404, #13405, #13406, #13407, #13409, #13410, #13411, #13412, #13413, #13414, #13418, #13424, #13427, #13433, #13523, #13524, #13525, #13528, #13531, #13532, #13534, #13539, #13543, #13545, #13546, #13547, #13550, #13553 | +| [@kriptoburak](https://github.com/kriptoburak) | #11370 | +| [@KrzysiekSko](https://github.com/KrzysiekSko) | #12425, #12432, #12673 | +| [@marcelokarval](https://github.com/marcelokarval) | direct commit / report | +| [@maxmad64bis](https://github.com/maxmad64bis) | #1622, #11435, #11437, #11441, #11537, #11550, #11553, #11555, #11557, #11812, #11842, #11843, #11903, #12151, #12214, #12215, #12218, #12226, #12314, #12316, #12317, #12318, #12319, #12320, #12321, #12507, #12715, #12731, #12744, #12786, #12787, #12788, #12790, #12792, #12794, #12795, #12828, #12832, #12853, #12857, #12870, #12937, #12941, #12975, #13141, #13142, #13146, #13147 | +| [@mdigitalbh81](https://github.com/mdigitalbh81) | #12240 | +| [@Meet6338-X](https://github.com/Meet6338-X) | #11598, #11609, #12110 | +| [@morpheus9393](https://github.com/morpheus9393) | #11943 | +| [@MumuTW](https://github.com/MumuTW) | #11492, #11502, #11506, #11507, #11626, #11675, #11685, #11728, #11746, #11888, #11889, #11890, #11892 | +| [@Neuron-Mr-White](https://github.com/Neuron-Mr-White) | #11622, #11801, #12256 | +| [@NightStalker-87](https://github.com/NightStalker-87) | #12183 | +| [@NoxzRCW](https://github.com/NoxzRCW) | #11879, #11880, #11881, #11882, #11883, #11935 | +| [@ntdat812](https://github.com/ntdat812) | #11585, #12095 | +| [@ntdatt812](https://github.com/ntdatt812) | #11368, #11573, #11574, #11576, #11577, #11580, #11582, #11583, #11588, #11589, #11590, #11591, #11592, #11593, #11672, #12177, #12180, #12873, #12918, #12920, #12921, #12925, #12930, #13007, #13009, #13024, #13025, #13083, #13087, #13101, #13104, #13110 | +| [@official-burak](https://github.com/official-burak) | #11542 | +| [@opensource-elearning](https://github.com/opensource-elearning) | #12179, #12189, #12278, #12286 | +| [@oyi77](https://github.com/oyi77) | #11408, #11409, #11421, #11505, #11677, #12036, #12110 | +| [@pacocartones](https://github.com/pacocartones) | #11521, #11522, #11527, #11528, #11529, #11530, #11531, #11532, #11533, #11534, #11595, #11599, #11603, #11604, #11605, #11607, #11610, #11676, #11714, #11716, #11718, #11767, #11838, #11860, #11862, #11869, #11871, #11872, #11873, #11903, #11906, #11921, #11934, #12356, #12358, #12359, #12360, #12361, #12362, #12364, #12365, #12373, #12374, #12375, #12376, #12377, #12379, #12380, #12381, #12386, #12387, #12389, #12390, #12394, #12395, #12397, #12401, #12403, #12404, #12522, #12523, #12535, #12536, #12540, #12541, #12543, #12545, #12548, #12549, #12550, #12551, #12552, #12647, #12651, #12653, #13055 | +| [@patrykkopycinski](https://github.com/patrykkopycinski) | #11936, #11937, #12224 | +| [@PauloFH](https://github.com/PauloFH) | #11509 | +| [@PauloHSOliveira](https://github.com/PauloHSOliveira) | #12241 | +| [@ponkcore](https://github.com/ponkcore) | #12054 | +| [@Prajeeth-12](https://github.com/Prajeeth-12) | #11634, #12046 | +| [@quiterunner-commits](https://github.com/quiterunner-commits) | #12131, #12143 | +| [@rafacpti23](https://github.com/rafacpti23) | #11554, #11558, #12192, #12197, #12198, #12202, #12203, #12204 | +| [@ragnar-claude](https://github.com/ragnar-claude) | #11564 | +| [@raheemuddin786](https://github.com/raheemuddin786) | #11491, #11839, #11840, #11841, #12003, #12230, #12231, #12232, #12233, #12234 | +| [@rahilmavani](https://github.com/rahilmavani) | #11761 | +| [@Rahulsharma0810](https://github.com/Rahulsharma0810) | #11771 | +| [@RaviTharuma](https://github.com/RaviTharuma) | #11710, #11727, #11797, #11798, #11802, #11805, #11806, #11811, #12098, #12099, #12101, #12449, #12452, #12472, #12473, #12493, #12533, #12607, #12628, #12631, #12636, #12875, #12876, #12880, #12882, #12884 | +| [@rezjalibd](https://github.com/rezjalibd) | #12186 | +| [@RhianB14](https://github.com/RhianB14) | #11692 | +| [@rifqiawl](https://github.com/rifqiawl) | #11517, #11519, #11969 | +| [@rqzbeh](https://github.com/rqzbeh) | #11390 | +| [@Sabeekhann](https://github.com/Sabeekhann) | #11699, #11705 | +| [@santosraju99-hub](https://github.com/santosraju99-hub) | #11755, #11770 | +| [@Seramicx](https://github.com/Seramicx) | #11568 | +| [@Siva010](https://github.com/Siva010) | #12191 | +| [@solstxce](https://github.com/solstxce) | #11597 | +| [@soroush5](https://github.com/soroush5) | #12691 | +| [@tenshiak](https://github.com/tenshiak) | #12279 | +| [@TheDemonTuan](https://github.com/TheDemonTuan) | #11468, #11470, #11471, #11482, #11548, #11758, #11775, #11814 | +| [@thomasmaerz](https://github.com/thomasmaerz) | #12834 | +| [@tuandinh0801](https://github.com/tuandinh0801) | #11454 | +| [@turbolego](https://github.com/turbolego) | #11621, #11762, #11772, #11774, #11781, #12216 | +| [@Tushar49](https://github.com/Tushar49) | #11392 | +| [@ujjawalkaushik1110](https://github.com/ujjawalkaushik1110) | #11863 | +| [@vermasomesh835](https://github.com/vermasomesh835) | #11794 | +| [@vsd2807](https://github.com/vsd2807) | #11565, #11619 | +| [@wahidsadik371-coder](https://github.com/wahidsadik371-coder) | #12126 | +| [@watchingdogs](https://github.com/watchingdogs) | #12031 | +| [@wildcard](https://github.com/wildcard) | #11620 | +| [@xiaoyaner0201](https://github.com/xiaoyaner0201) | #11460, #11662, #12083 | +| [@Xxx91n](https://github.com/Xxx91n) | #11690 | +| [@yourspraveen](https://github.com/yourspraveen) | #11146 | +| [@yxyxy](https://github.com/yxyxy) | #11671 | +| [@ZaimMarzuki](https://github.com/ZaimMarzuki) | #11960 | +| [@Zartharas](https://github.com/Zartharas) | #11340 | +| [@zero-executioner](https://github.com/zero-executioner) | #11631 | +| [@diegosouzapw](https://github.com/diegosouzapw) | maintainer | + +--- ## [3.8.50] — 2026-08-25 _Living section — regenerated 2026-08-12 from all cycle commits (cycle open `ed2db6cb19` → tip). Bullets carry the merged PR and its author; direct pushes listed separately._ diff --git a/docs/i18n/sr/CHANGELOG.md b/docs/i18n/sr/CHANGELOG.md index bc74999505..a1c81262d0 100644 --- a/docs/i18n/sr/CHANGELOG.md +++ b/docs/i18n/sr/CHANGELOG.md @@ -95,16 +95,1119 @@ ## [3.8.51] — TBD -_Living section — cycle opened at the v3.8.50 freeze (parallel-cycle model). Bullets are aggregated from `changelog.d/` fragments at each `/generate-release` phase._ +_Living section — reconciled 2026-09-15 from all cycle commits (`091589089c` → `c0f92ec98a`, 916 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 | **123** | +| 📝 Commits in the cycle | **916** | +| 🔀 Pull requests referenced | **892** | +| 📋 Changelog entries | **919** | +| 🙌 Contributors credited in entries | **121** | +| 🤖 Automated dependency commits | 18 | + +**Entries by type** + +| Type | Count | +| --- | ---: | +| 🐛 Fixes | 592 | +| ✨ Features | 131 | +| 🧹 Chore | 93 | +| 📚 Docs | 42 | +| 🧪 Tests | 32 | +| ♻️ Refactor | 8 | +| 🏗️ Build | 7 | +| ⚡ Performance | 7 | +| ⚙️ CI | 2 | +| ⏪ Reverts | 2 | +| 📦 Dependencies | 2 | +| 🔒 Security | 1 | + +### 🏆 Top 25 contributors this cycle + +_By commits in `091589089c..c0f92ec98a`, author identities consolidated via `.mailmap` and the merged PR's GitHub login. Bots excluded._ + +| # | Contributor | Commits | +| ---: | --- | ---: | +| 🥇 | diegosouzapw | 323 | +| 🥈 | Bob.Hou (@HouMinXi) | 79 | +| 🥉 | Paco Cartones (@pacocartones) | 77 | +| 4 | Dizzle (@maxmad64bis) | 49 | +| 5 | Koosha Paridehpour (@KooshaPari) | 43 | +| 6 | Nguyen Thanh Dat (@ntdatt812) | 31 | +| 7 | Ravi Tharuma (@RaviTharuma) | 26 | +| 8 | Markus Hartung (@hartmark) | 22 | +| 9 | Webman (@jonlwheat2-gif) | 22 | +| 10 | backryun | 17 | +| 11 | anhtahaylove | 15 | +| 12 | Rafa Martins (@rafacpti23) | 10 | +| 13 | Syed Raheemuddin (@raheemuddin786) | 10 | +| 14 | Nguyễn Viết Tuấn (@TheDemonTuan) | 8 | +| 15 | MumuTW | 7 | +| 16 | Paijo (@oyi77) | 7 | +| 17 | Tobias Andersen (@turbolego) | 6 | +| 18 | Bl0ck (@Bl0ck154) | 5 | +| 19 | SHANMUGAPRIYAN (@geek007git) | 5 | +| 20 | KaspaPulse | 5 | +| 21 | Abhishek Sharma (@abhisheksharma2411) | 4 | +| 22 | Abhishek Divekar (@adivekar-utexas) | 4 | +| 23 | Andrew B. (@AndrianBalanescu) | 4 | +| 24 | NoxzRCW | 4 | +| 25 | opensource-elearning | 4 | ### ✨ New Features +- **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). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- Added Google AI Studio Gemini batch text-to-speech support through `POST /v1/audio/speech`. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- 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. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(routing):** subscription-first auto groupings — `auto/subscription` routes only through plan-included connections with a documented hard-stop overage and fails closed on exhaustion, while `auto/thrifty` orders the pool `subscription → keyless → free → cheap → premium` and steps up one rung at a time as each is exhausted. Billing class comes from a curated per-connection catalog (uncurated is treated as metered, never plan-included), both reuse STRICT_ZERO_COST's per-connection verification, and a quota reading whose `resetAt` has passed is now refreshed regardless of TTL so routing returns to plan capacity as soon as it resets ([#11146](https://github.com/diegosouzapw/OmniRoute/pull/11146)) — thanks @yourspraveen +- **feat(providers):** publish a management-authenticated versioned web-session credential contract from OmniRoute's canonical browser credential metadata ([#11340](https://github.com/diegosouzapw/OmniRoute/pull/11340)) — thanks @Zartharas +- **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)) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add Xquik X search with typed results, credential validation, REST routing, and MCP selection ([#11370](https://github.com/diegosouzapw/OmniRoute/pull/11370)) — thanks @kriptoburak +- **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)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(dashboard):** surface durable exclusive managed leases in the existing Sessions view, keeping leased clients visible across idle gaps while marking connections with in-flight work as active ([#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389)) — thanks @KaspaPulse +- **build(bun):** allow Turbopack bundler flag on Bun 1.4+ with configurable Webpack fallback ([#11471](https://github.com/diegosouzapw/OmniRoute/pull/11471)) — thanks @TheDemonTuan +- feat(api): add an opt-in `modelVisibilityAllowlist`/`modelVisibilityDenylist` settings pair to curate exactly which models `/v1/models` advertises, mirrored into every `auto/*` combo candidate pool so a denied model cannot be routed to via combo selection either (#11481) ([#11997](https://github.com/diegosouzapw/OmniRoute/pull/11997)) +- **feat(rankings):** the Free Provider Rankings page shows what each provider actually served over the last 24 h. It ranked by ELO alone, which left a provider that answers every call with an error in first place; the usage data was already served by the API but never requested. A provider with too small a sample shows a dash, not a number ([#11546](https://github.com/diegosouzapw/OmniRoute/pull/11546)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **feat(guardrails):** enforce a bounded, deterministic contract for Video Bridge transcripts — 256 cues, 4096 input code units and 4 KiB UTF-8 per cue, 64 KiB total text, malformed-Unicode rejection, focus-window scoping, cross-source reconciliation with contributing-source metadata, and a structural provenance trust boundary so caller JSON can never self-assert `embedded`/`audio-bridge` provenance ([#11652](https://github.com/diegosouzapw/OmniRoute/issues/11652)) ([#12009](https://github.com/diegosouzapw/OmniRoute/pull/12009)) +- **feat(video):** orchestrate optional Video Bridge audio extraction and Audio Bridge STT behind a dual opt-in (operator setting AND per-request signal) — a new loopback-only broker `mode=audio` operation shares the frame path's exact process queue, deadline, AbortSignal, and byte budgets to extract a bounded mono 16 kHz PCM WAV from the same already-downloaded video, then reuses the existing Audio Bridge transcription boundary; provider segment timing is preserved when available and marked coarse otherwise, and every failure degrades to a visual-only-safe partial instead of throwing (#11654). ([#12012](https://github.com/diegosouzapw/OmniRoute/pull/12012)) +- Add a tenant-bound Video Bridge drill-down lifecycle on top of the existing secure cache substrate: opaque hashed handles (never raw session/video identifiers), preview/standard/detail multiresolution variants resampled on read, response pagination capped at 8 frames and 32 MiB, and a new authenticated `/api/v1/video-bridge/drilldown` consumer route that stays disabled for remote access by default and denies cross-key access with the same response as a nonexistent handle (no existence oracle). ([#12006](https://github.com/diegosouzapw/OmniRoute/pull/12006)) +- **test(video):** Add the Video Bridge FU-07/FU-09 promotion-evidence harness (#11656) — a frozen Zod manifest schema covering the 8 required scenario kinds (static scenes, rapid cuts, late facts, fades, blur, small text, close events, visual prompt injection) with a minimum of 3 repetitions per case, deterministic declarative fixture recipes (`videoBridgePromotionFixtures.ts`), a pure medians/p95 metrics aggregator, a pure FU-07/FU-09 promotion-verdict evaluator applying the ticket's exact thresholds (missing token usage always holds), a digest-only persistence layer that never retains raw media or raw model responses, and a versioned per-model promotion allowlist shipped empty with every model defaulting to `hold`. The FU-07/FU-09 promotion verdicts themselves remain HOLD — they require a real evidence run against real models on VPS 192.168.0.15. ([#12008](https://github.com/diegosouzapw/OmniRoute/pull/12008)) +- **feat(video bridge):** "embedded" transcript provenance can now be legitimately earned instead of merely asserted — a bounded, allowlisted (`mov_text`/`subrip`/`webvtt`) subtitle probe runs through the loopback-only Video Bridge broker (at most 2 streams, 10s subdeadline bounded by the request deadline, 256 KiB output, 4096-code-unit lines), normalized through a bounded, ReDoS-safe WebVTT parser and Zod-validated end to end. The adapter always resolves to an explicit `success`/`absent`/`transient_failure` outcome — a subtitle failure never breaks the visual description path, and only a fingerprint-verified broker response (never a caller-declared label) can produce embedded cues (#11659). ([#12011](https://github.com/diegosouzapw/OmniRoute/pull/12011)) +- Default new Antigravity-family connections (agy CLI imports and Antigravity OAuth connects) to model auto-sync, so live model discovery lands in the synced catalog and `/v1/models` picks up freshly released upstream models (e.g. Gemini 3.7 Flash tiers) without code changes. Existing connections keep their current setting; the per-connection dashboard toggle remains the opt-out. (#11685 — thanks @MumuTW) ([#11887](https://github.com/diegosouzapw/OmniRoute/pull/11887)) +- **feat(ui):** enable React Compiler (`reactCompiler: true` + `babel-plugin-react-compiler`) for automatic memoization at build time ([#11783](https://github.com/diegosouzapw/OmniRoute/pull/11783)) — thanks @jonlwheat2-gif +- **feat(providers):** add SeekAi (`seekai.cc`) as an OpenAI-compatible New-API gateway — catalog id `seekai` (alias `ska`), `https://seekai.cc/v1`, live `/v1/models` via `passthroughModels`, aggregator-list membership so New-API balance detection can opt in. No referral/aff codes. ([#11786](https://github.com/diegosouzapw/OmniRoute/issues/11786)) ([#12557](https://github.com/diegosouzapw/OmniRoute/pull/12557)) — thanks @HouMinXi +- **feat(zai):** add GLM-5.3-Flash Coding Plan support (1M context, 128K output, vision, `low|high|max` reasoning) and route `zai` GLM-5.3-family API-key traffic through the OpenAI-compatible Coding Plan endpoint with native thinking defaults ([#11801](https://github.com/diegosouzapw/OmniRoute/pull/11801)) — thanks @Neuron-Mr-White +- **feat(combo):** choose how combo models are ordered — manual, provider, score, or name — via a sort control in the dashboard builder, persisted in `config.modelSort` and re-applied on load and after add ([#11812](https://github.com/diegosouzapw/OmniRoute/pull/11812)) — thanks @maxmad64bis +- **feat(free):** custom models can be marked free-tier via `customModels[].isFree`; `isFreeModel()` is the first door and `hidePaidModels` respects it even for providers outside the free budget ([#11843](https://github.com/diegosouzapw/OmniRoute/pull/11843)) — thanks @maxmad64bis +- **feat(nodejs):** add `5dive` as a `configure` target — `omniroute configure 5dive` / `omniroute setup-5dive` write a 5dive auth profile that points an agent fleet's `claude` seats at OmniRoute, with the root-only write, the loopback-vs-`https` endpoint rule and the per-seat model pin handled explicitly ([#11852](https://github.com/diegosouzapw/OmniRoute/pull/11852)) — thanks @5dive-bot +- **feat(sse):** treat `max` as a first-class reasoning-effort tier and clamp per model family (GLM 5.1+/DeepSeek V4+/Kimi K3+ keep native `max`; o1/MiniMax/Grok/Muse Spark clamp to their upstream ceiling) ([#11875](https://github.com/diegosouzapw/OmniRoute/pull/11875)) — thanks @Chewji9875 +- **feat(providers):** the provider plugin manifest now advertises a `usage-fetch` capability for the 40 providers that have a wired usage/quota fetcher, so external dashboards can read it from `GET /api/v1/provider-plugin-manifest` instead of parsing `open-sse/services/usage.ts` after every release. Discovery only — no new fetcher, no quota change, and the Dashboard quota widget stays gated by `USAGE_SUPPORTED_PROVIDERS`. `USAGE_FETCHER_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/fetcherProviders.ts`) and is re-exported from `services/usage.ts`, keeping the manifest module a light leaf instead of pulling the ~490-module usage dispatcher into the manifest route. ([#11903](https://github.com/diegosouzapw/OmniRoute/pull/11903)) — thanks @maxmad64bis / @pacocartones +- **feat(plugins):** `OMNIROUTE_PLUGINS_DIR` sets the directory the runtime plugin scanner reads — and the root the plugin manager installs into — overriding the `HOME`-derived default, so a Docker/K8s deployment can point straight at its bind-mounted plugin tree instead of moving `HOME` just to relocate the scan path. An image that exports no home no longer scans `/tmp/.omniroute/plugins` in silence: the resolved directory is logged once at startup as `scanner.dir_resolved`, naming the input that won. Unset, behaviour is unchanged. Distinct from the CLI-only `OMNIROUTE_PLUGIN_PATH`, which finds `omniroute-cmd-*` command packages and never reached this scanner ([#11906](https://github.com/diegosouzapw/OmniRoute/pull/11906)) — thanks @amaleta / @pacocartones +- **feat(leases):** add an explicit owner-authenticated status action that returns only the active lease's privacy-safe configured connection and provider labels, with generation fencing and no credential or internal-id disclosure ([#11910](https://github.com/diegosouzapw/OmniRoute/pull/11910)) — thanks @KaspaPulse +- **feat(providers):** import-from-file modal shows per-row API errors and ships a downloadable CSV template ([#12071](https://github.com/diegosouzapw/OmniRoute/issues/12071)) ([#12504](https://github.com/diegosouzapw/OmniRoute/pull/12504)) — thanks @HouMinXi +- **feat(providers):** dashboard search matches connection name and `baseUrl` so imported OpenAI-compat nodes surface on the provider card ([#12108](https://github.com/diegosouzapw/OmniRoute/issues/12108)) ([#12495](https://github.com/diegosouzapw/OmniRoute/pull/12495)) — thanks @HouMinXi +- **feat(routing):** add a `score` Auto router strategy that selects the highest configured weighted score and reuses `explorationRate`. ([#12155](https://github.com/diegosouzapw/OmniRoute/pull/12155)) — thanks @jacobsparts +- Add a runtime feature flag to disable universal context handoffs globally without changing the default behavior. ([#12167](https://github.com/diegosouzapw/OmniRoute/pull/12167)) — thanks @jacobsparts +- **feat(providers):** the provider plugin manifest now also advertises a `usage-supported` capability for the 46 providers whose usage API is accepted by the server and Dashboard routes, so integrators can distinguish "the server will serve quota for this provider" from "a fetcher is wired" without reading TypeScript. Discovery only — no fetcher or quota change. `usage-fetch` resolves on id or alias (the usage dispatcher accepts both); `usage-supported` resolves on id alone, matching the runtime guard `USAGE_SUPPORTED_PROVIDERS.includes(providerId)`. `USAGE_SUPPORTED_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/supportedProviders.ts`) and is re-exported from `providers.ts`, mirroring the `fetcherProviders` leaf from #11903 and keeping the manifest a light module. ([#12214](https://github.com/diegosouzapw/OmniRoute/pull/12214)) — thanks @maxmad64bis +- **feat(rankings):** order Free Provider Rankings by what each provider actually served — `GET /api/free-provider-rankings?sortBy=reliability` and a "Most reliable first" toggle on the page. Providers with too few calls to state a success rate keep their score order below the measured ones; the default order is unchanged ([#12218](https://github.com/diegosouzapw/OmniRoute/pull/12218)). — thanks @maxmad64bis +- **perf(sse):** defer `cloneLogPayload()` in the structured SSE collector until after the `maxEvents`/`maxBytes` cap check, eliminating ~9,800 wasted `structuredClone` calls per streaming response (65–71% faster `push()`). Reducer snapshot isolation restored for OpenAI and Responses summaries ([#12241](https://github.com/diegosouzapw/OmniRoute/pull/12241)) — thanks @PauloHSOliveira ([#12243](https://github.com/diegosouzapw/OmniRoute/pull/12243)) +- **feat(usage):** Devin CLI agentic quota (Codeium seat-management GetUserStatus) and OpenRouter key limits plus account credits now surface in Provider Limits ([#12256](https://github.com/diegosouzapw/OmniRoute/pull/12256) — thanks @Neuron-Mr-White) +- **feat(settings):** persist `headroomUrl` through Settings so status/start use the operator URL instead of only `HEADROOM_URL` ([#12306](https://github.com/diegosouzapw/OmniRoute/issues/12306)) ([#12487](https://github.com/diegosouzapw/OmniRoute/pull/12487)) — thanks @HouMinXi +- **feat(auto-combo):** Auto-Combo scoring can now weigh how often a provider/model has actually succeeded. The engine already carried that number on every candidate — 24 hours of usage history behind a ten-sample floor, real-time metrics otherwise — and the scoring function never read it, while the weight table described `stability` as if it did. `reliability` (`1 - failureRate`, with the same field precedence and the same rate-bounding the speed ranking already uses, so a corrupt reading means "nothing observed" rather than "fails every call") is now a declared factor shipping at weight `0`, so routing is unchanged until an operator gives it one, and the `stability` description now matches what that factor computes ([#12317](https://github.com/diegosouzapw/OmniRoute/pull/12317)) — thanks @maxmad64bis +- **feat(routing):** With `freeAccessPolicy: "strict"`, the read-only candidate listing (`GET /v1/auto-combo/{channel}/candidates`) no longer hides the candidates the zero-cost guard excludes — the same read-only transparency the resilience filter already honours (#9133). Each candidate now carries `freeAccessExclusion` saying why it would be kept out, and it tells an exhausted allowance apart from a quota reading that never arrived or went stale, which used to look identical from the outside. Routing is unchanged: the listing reports, it never enforces. The separate `excludeTosAvoid` guard still drops its candidates without a reason; that gap is now documented rather than closed ([#12319](https://github.com/diegosouzapw/OmniRoute/pull/12319)) — thanks @maxmad64bis +- **feat(radar):** The Radar catalog table now shows two facts it was already receiving from the feed and dropping on the floor: the per-model rate limits (requests and tokens, per minute and per day) in a new column, and a badge when a provider's terms state it may train on the prompts you send. A limit of zero renders as zero rather than "rate-only" — for a ceiling those are opposite facts — and a model with no training statement gets no badge, because an absent statement is not a guarantee ([#12320](https://github.com/diegosouzapw/OmniRoute/pull/12320)) — thanks @maxmad64bis +- **feat(radar):** explain Community, single-use, contributor, supporter, recovery, abuse, offers, and privacy rules before either Radar activation action, and remove the superseded fixed-PR grant promise from every UI locale ([#12342](https://github.com/diegosouzapw/OmniRoute/pull/12342)) +- **feat(gamification):** the dashboard Profile page now shows the real daily streak — `/api/gamification/level` returns `streak: { current, longest }` (per key with `apiKeyId`, operator-wide maximum otherwise) and the streak card reads it instead of a hard-coded 0 (#2403) ([#12377](https://github.com/diegosouzapw/OmniRoute/pull/12377)) — thanks @pacocartones +- **feat(gamification):** the dashboard leaderboard now shows each API key's display name under the Name column instead of a truncated key id; `GET /api/gamification/leaderboard` attaches `name` per entry (name only — no key material), while the shared ranking helper and the federation leaderboard stay id-only — thanks @pacocartones ([#12385](https://github.com/diegosouzapw/OmniRoute/pull/12385)) +- **feat(gamification):** enforce the documented 1000 XP/min per-API-key anti-cheat rate limit on the XP award path; over-limit awards are logged and skipped instead of persisted, and the sliding window now matches the timestamp format stored in `xp_audit_log` ([#2403](https://github.com/diegosouzapw/OmniRoute/issues/2403)) ([#12390](https://github.com/diegosouzapw/OmniRoute/pull/12390)) — thanks @pacocartones +- **feat(admin):** localize the gamification anomalies page — the loading state, the Status column and the Suspicious badge now come from the `common` catalog (new `common.suspicious` key propagated to every locale) — add it to the Gamification sidebar group as `gamification-admin` (`/dashboard/gamification/admin`), and expose the loading and empty states as polite `role="status"` live regions (#12401 — thanks @pacocartones) +- **feat(providers):** skip GitHub combo members missing from the live synced catalog, and drop Copilot models that are policy-disabled or hidden from the model picker ([#12473](https://github.com/diegosouzapw/OmniRoute/pull/12473)) — thanks @RaviTharuma +- **feat(i18n):** locale-expansion tooling — `npm run i18n:add-locale` adds a language to config, dashboard, docs mirrors, CLI, README/indexes and site in one command; browser, CLI and cookie detection resolve `uk`, `fil`/`tl`, `zh-Hant` (and the retired legacy `in` → `id`) via config aliases; `config/i18n.json` now ships in the npm package so the published CLI can read it; new real-translation ratio gate (`npm run i18n:check-ratio`, advisory) with a per-locale ratchet baseline; `run-translation --adopt` restores `.i18n-state.json`; `sync-language-bars` generates 🌐 bars from config; `validate_translation.py` loads its allowlist again. Retires the duplicate `in` locale (Indonesian mislabelled as Hindi) — 42 honest locales; saved `NEXT_LOCALE=in` / `OMNIROUTE_LANG=in` keep working. (#12496) +- **feat(api):** Emit gateway-measured `tokens_per_second` (TTFT excluded) on streaming usage and `X-OmniRoute-Tokens-Per-Second` when first-token latency is known ([#12616](https://github.com/diegosouzapw/OmniRoute/issues/12616)) ([#12631](https://github.com/diegosouzapw/OmniRoute/pull/12631)) — thanks @RaviTharuma +- **feat(opencode-plugin): map gateway cost/usage/tok/s onto OpenCode inference payloads** — the official plugin copies `X-OmniRoute-Response-Cost`, token counts, `X-OmniRoute-Tokens-Per-Second` / `usage.tokens_per_second`, TTFT, and the winning `X-OmniRoute-Model` onto the JSON/SSE body OpenCode already consumes. Missing tok/s is left unset (never `tokens / latency`). (#12636) — thanks @RaviTharuma +- **feat(combo):** add combo strategy `quota-weighted`: skip exhausted accounts, then weighted-draw by leftover / in-flight load; existing conversations stay pinned until the account is empty ([#12789](https://github.com/diegosouzapw/OmniRoute/pull/12789)) — thanks @HouMinXi +- **feat(models):** Account live listings become the chat catalog source for Claude, Codex, Copilot, and AGY; public metadata only fills prices on IDs those accounts already list. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **fix(models):** Union `agy` and `antigravity` live catalogs so `agy/gemini-3.8-flash-high` is not rejected after the prefix folds to `antigravity`. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **feat(opencode):** Add `@omniroute/opencode-plugin-v2`, an opencode v2 plugin that gives a v2 install a route to an OmniRoute gateway: it publishes models, combos and auto-combos into the host catalog, refreshes them lazily behind a 300s TTL, and keeps serving the last known catalog from an on-disk snapshot when the gateway is unreachable. The v1 plugin (`@omniroute/opencode-plugin`) is untouched and keeps working as before. The plugin adapts to the host instead of assuming one shape: it reads the shape the host seeds into the catalog draft and publishes the `api` block, the pre-`api` top-level fields, or both. The gateway key comes from the host credential store when one is connected, so no secret needs to sit in a config file; a source the gateway refuses is named in a warning instead of silently degrading the catalog to raw model ids; and tool schemas bound for a Gemini model are stripped of the JSON-Schema keywords Gemini rejects, so tool calling survives that route ([#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870)) — thanks @maxmad64bis +- feat(services): show sanitized CLIProxyAPI account health from its authenticated management API without exposing credentials, file paths, or raw account metadata (#6342) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add AnySearch free web search + URL extract (webFetch) with typed results, credential validation, REST routing, and MCP selection - fallback-only ([#11690](https://github.com/diegosouzapw/OmniRoute/pull/11690)) — thanks @Xxx91n +- **feat(dashboard):** display clamped `[0, 100]%` cached input token ratio in request logs table ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) — thanks @b3nw +- **feat(catalog):** add `OMNIROUTE_DISABLE_THINKING_LEVEL_VARIANTS` feature flag to optionally filter out thinking level variants from model catalog ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) — thanks @b3nw +- **feat(dashboard):** continuously export call logs to external analytics stores. A pluggable destination registry ships the full Logs-tab record set on an hourly `JobRegistry` cron, with a persisted per-destination cursor, batched inserts, a config UI rendered from each destination's own field descriptors, and a REST layer (`/api/log-export/*`) for CRUD, a connection test, and an on-demand run. A destination can opt into `includeBodies` to also ship the request and response payloads shown in the Logs detail pane, including the client and provider views of each call; this is off by default, and payloads inherit the dashboard's PII sanitisation, secret redaction and `noLog` handling. Google BigQuery is the first destination, using a service-account key stored encrypted at rest and streaming inserts keyed by call-log id, into a table that is day-partitioned on `timestamp` and clustered on `api_key_name`, `provider`, `model` and `status`. ([#11945](https://github.com/diegosouzapw/OmniRoute/pull/11945)) — thanks @dpozimski +- **feat(providers):** add MaxAI as a signed, OpenAI-compatible provider serving its 13 paid chat models (GPT-5.6 / Luna / Thinking, Claude 5 Sonnet, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite, Grok 4.1-fast / 4.5, DeepSeek V3.2 / R1, Llama 3.3 70B) through OmniRoute's `/v1` endpoint, with per-request HMAC-SHA1→SM3→AES request signing, live model + context-window discovery from `/models/get_config`, and prompted tool-calling translated to OpenAI `tool_calls` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI vision input — image_url content parts are forwarded inline in `message_content` to the 6 vision-capable models (GPT-5.6 / Luna / Thinking, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite) ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI image generation — 6 image models (gpt-image-1, dall-e-3, flux-1-schnell/dev/pro, sd3-medium) exposed through `POST /v1/images/generations` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI document RAG — inline base64 file/document attachments are uploaded to MaxAI (content-addressed `doc_id`) and attached to the chat via `doc_list` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** browserless MaxAI onboarding — email device-pair login (`/api/providers/[id]/login`) and signed access-token refresh, so a connection can be created and kept fresh without a real browser or Google OAuth ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** per-provider TLS impersonation profile (MaxAI presents a Windows Firefox-150 client fingerprint) so its bot-sensitive endpoints accept OmniRoute traffic ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** add **Nimble** as a web-search and web-fetch provider (`nimble-search`) — `/v1/search` routes to Nimble's search API at `lite` depth (locale, freshness and include/exclude domain filters mapped onto the shared request contract), and `/v1/web/fetch` routes to Nimble Extract, which covers all four fetch formats (`markdown`, `html`, `links`, `screenshot`) from a single call. One API key serves both surfaces. ([#11620](https://github.com/diegosouzapw/OmniRoute/pull/11620)) — thanks @wildcard +- The npm publish is automatic again, through npm Trusted Publishing (OIDC): the hosted `stage-npm` job publishes with a short-lived credential minted from GitHub's id-token — no `NPM_TOKEN`, no 2FA prompt, provenance attached. `publish_mode=staged` (owner approves with 2FA) and `direct` (token) remain available on `workflow_dispatch`. ([#11931](https://github.com/diegosouzapw/OmniRoute/pull/11931)) +- **feat(providers):** Add **Opper** as an API-key gateway provider — EU-hosted AI gateway with 700+ models from 30+ providers behind one OpenAI-compatible API and one key (`OPPER_API_KEY`); model ids use `provider/model` format (e.g. `anthropic/claude-sonnet-4-6`, `openai/gpt-5`); live model catalog at `https://api.opper.ai/v3/compat/models`; entry mirrors `requesty` (same shape, `passthroughModels: true`, no static seed) ([#11629](https://github.com/diegosouzapw/OmniRoute/pull/11629)) +- **feat(dashboard):** the `/dashboard/orchestration` snapshot hook now subscribes to the `agents` WebSocket channel (`agent.task.updated`) instead of `requests` as its refetch trigger, and relaxes its background poll from 5s to 30s while that WS connection is up — falling back to the tighter 5s cadence, reprogrammed live on any connect/disconnect transition, whenever the socket is down. ([#12409](https://github.com/diegosouzapw/OmniRoute/pull/12409)) +- **feat(dashboard):** Orchestration canvas quick wins — search box plus state/source/provider filter chips with a one-click clear, and per-source collapse/expand, all reflected in the URL so a filtered/collapsed view is shareable and survives a refresh; the detail drawer gained a "copy trace JSON" action and hardened error/empty-state and accessibility handling; the Agents-tab edges now animate traveling particles along active (running) connections; and the canvas node/edge status colors moved off fixed hex values onto theme-aware `--orch-status-*` CSS custom properties, so they adapt correctly to light/dark mode. ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- **chore(dashboard):** Orchestration UI hardening pass and the missing component/model test coverage it called for — `OrchestratorNode`/`ActivityNode`/`OverflowNode` rendering, the `?node=`/overflow-click page routing, the Agents-tab orchestrator-click no-op and `showCompleted` toggle, and the overview kanban's done-column sort order (#12270, #12271). ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- New `/dashboard/orchestration` page: live unified view of everything running — Cloud Agent, A2A and Conductor as a real-time graph (Agents tab), the combo cascade (Routing tab, reusing the Combo Live Studio) and a state kanban (Overview tab), with a detail drawer (trace, cost, approve/cancel). Read-only over existing APIs — no new backend. Canvas concept credit: PR #11815 design ([#12261](https://github.com/diegosouzapw/OmniRoute/pull/12261)) +- **feat(dashboard):** new "History" tab on `/dashboard/orchestration` — an Airflow-style grid of finished runs over a 24h/7d/30d preset window, one row per (source, identity), clicking a cell opens the existing detail drawer. It is backed by real persistence: A2A task lifecycle transitions are now written to the `a2a_tasks` table (purged after 30 days, configurable via `OMNIROUTE_A2A_HISTORY_RETENTION_DAYS`) and served by the new `GET /api/a2a/tasks/history` listing endpoint, with the task-detail route falling back to persisted history once a run leaves the in-memory snapshot. Conductor runs stay remote and are not persisted locally — the tab says so instead of silently omitting them. ([#12479](https://github.com/diegosouzapw/OmniRoute/pull/12479)) +- **feat(dashboard):** the orchestration detail drawer gained a "Repeat" action for Cloud Agent, A2A and Conductor tasks — a two-click confirm (click once to arm, click again within the confirm window to fire) re-submits the original prompt/input as a new run. The button is disabled with an explanatory tooltip whenever the original input can't be recovered from the loaded task detail (e.g. it never carried a prompt, or the detail failed to load). Two limitations of the A2A variant, by design: it targets the `/a2a` JSON-RPC endpoint, which authenticates with an API key only (`REQUIRE_API_KEY=true` or a configured `OMNIROUTE_API_KEY` makes a dashboard-session repeat answer `HTTP 400` — surfaced verbatim in the drawer's error line, never as a success), and the `message/send` call is SYNCHRONOUS: the POST blocks for the whole skill run, so the success confirmation only appears once the repeated task finishes. A dashboard-authenticated A2A creation path is deliberately left to a follow-up — widening the endpoint's auth posture is an operator decision, not a side effect of this feature. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(a2a):** A2A task execution now records which memories were consulted for the task's last user message as `metadata.memoryHits` (id/key/type/content-snippet) plus a `memory_hits` history event, purely for observability — the retrieved memory is never injected into a skill's prompt or behavior. Gated by the `OMNIROUTE_A2A_MEMORY_HITS` kill-switch (default enabled; set to `0` to skip the recall lookup entirely). The drawer's new "Memory used" section lists these hits for a2a tasks and is omitted whenever there are none. Known limitation: recall only resolves under the keyless posture — a keyed caller's task owner is a SHA-256 prefix of the API key, while memory rows are keyed by the database api-key id, and no hash→id lookup exists today, so the hit list stays empty for keyed callers. The recorded hits are also kept out of the task's own `input` (and therefore out of the persisted input and of the "Repeat" request body), so repeating a task never re-sends the previous run's memory snippets. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(providers):** add a Perplexity Agent API provider (`perplexity-agent` / `pplx-agent`) for Perplexity `/v1/responses`, including the documented Anthropic, OpenAI, Google, xAI, DeepSeek, Z.AI, Moonshot/Kimi, NVIDIA, and Perplexity model IDs plus Anthropic-model `max_output_tokens` compatibility. ([#12103](https://github.com/diegosouzapw/OmniRoute/pull/12103)) — thanks @AIB1TAL0S +- **feat(providers):** add RPD (Requests Per Day) limit to provider rate limit overrides across UI, schemas, DB, and i18n ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) — thanks @b3nw +- **feat(providers): add UC Direct (uncensored.com Developer API), the metered OpenAI-compatible surface.** A standard OpenAI-compatible passthrough (default executor) for uncensored.com's official REST API at `https://api.uncensored.com/api/v1`: `X-api-key` auth (never-expiring `uai_sk_live_` key), `POST /chat/completions` with streaming SSE and native tool-calling, and the full live metered catalog (82 models across 15 providers, discovered from the public `GET /v1/models`). Registered as provider `uc-direct` (alias `ucd`). Complements the un-metered `uc` persona provider — same models, metered credits and a plain API key instead of a subscription session. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(providers): add UC (uncensored.com), the un-metered subscription "persona" chat as an OpenAI-compatible provider.** A WebSocket web-app port: a durable Clerk credential mints a short-lived session token per connect (browserless — no API key), driving UC's persona socket. Ships the browserless email-code login (request → verify → harvest), the 19 verified persona models (Claude Opus, Gemini, Grok, GLM, Kimi, DeepSeek, MiniMax, incl. the uncensored variants), prompted `` tool-calling with a per-model code-style dialect + auto-cure retry for guardrailed models, live ``/reasoning split, streaming + non-streaming OpenAI responses, and full quota/auth error surfacing (paywall / message-limit / rate-limit → 429, invalid session → 401 re-login). Full multimodal parity via the persona blob-upload layer: **vision** (image input, 15 vision-capable models), **document RAG** (PDF/doc upload, server-side extraction), **image generation** (22 models), **video generation** (14 models, async signed-url → poll), and **TTS** (streaming MP3). Registered as provider `uc` (alias `ucn`). The metered OpenAI-compatible Developer API is a separate `uc-direct` provider. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(logs):** add copy-all button to request detail modal ([#11083](https://github.com/diegosouzapw/OmniRoute/pull/11083)) — thanks @AndrianBalanescu +- **feat(check):** make serviceKinds required and add the reverse-walk provider consistency gate ([#11392](https://github.com/diegosouzapw/OmniRoute/pull/11392)) — thanks @Tushar49 +- **feat(providers):** only advertise verified-working connections by default ([#11448](https://github.com/diegosouzapw/OmniRoute/pull/11448)) — thanks @alltomatos +- **feat(resilience):** add hierarchical concurrency admission ([#11493](https://github.com/diegosouzapw/OmniRoute/pull/11493)) +- **feat(providers):** derive + surface expiry for JWT-bearing web cookies (#11497) ([#11505](https://github.com/diegosouzapw/OmniRoute/pull/11505)) — thanks @oyi77 +- **feat(tunnels):** support named cloudflared tunnels via CLOUDFLARED_CONFIG ([#11561](https://github.com/diegosouzapw/OmniRoute/pull/11561)) — thanks @ggiak +- **feat(compression):** make proactive context-compression threshold a live setting ([#11564](https://github.com/diegosouzapw/OmniRoute/pull/11564)) — thanks @ragnar-claude +- **feat(resilience):** add provider peak-hour protection ([#11622](https://github.com/diegosouzapw/OmniRoute/pull/11622)) — thanks @Neuron-Mr-White +- **feat(providers):** add 1min.ai provider ([#11631](https://github.com/diegosouzapw/OmniRoute/pull/11631)) — thanks @zero-executioner +- **feat(dashboard):** collapsible JSON tree viewer for request/response payloads ([#11703](https://github.com/diegosouzapw/OmniRoute/pull/11703)) — thanks @hartmark +- **feat(routing):** add LiquidAI LFM2.5-2.6B free tier via OpenRouter ([#11752](https://github.com/diegosouzapw/OmniRoute/pull/11752)) — thanks @brick30llc-ctrl +- **feat(pricing):** add GLM-5.3-Flash pricing, model specs, and catalog registration ([#11830](https://github.com/diegosouzapw/OmniRoute/pull/11830)) — thanks @AndrianBalanescu +- **feat(routing):** add relayMode for schema-locked context handoffs ([#11839](https://github.com/diegosouzapw/OmniRoute/pull/11839)) — thanks @raheemuddin786 +- **feat(config):** add support for runtime system prompt configuration and hot-reloading ([#11841](https://github.com/diegosouzapw/OmniRoute/pull/11841)) — thanks @raheemuddin786 +- **feat(routing):** expand connection-aware quota prefilter across combo strategies (#11682) ([#11850](https://github.com/diegosouzapw/OmniRoute/pull/11850)) — thanks @HouMinXi +- **feat(models):** reactive discovery sync after Antigravity model-not-found 404 ([#11888](https://github.com/diegosouzapw/OmniRoute/pull/11888)) — thanks @MumuTW +- **feat(models):** auto-aliases and token limits from synced Antigravity discovery ([#11889](https://github.com/diegosouzapw/OmniRoute/pull/11889)) — thanks @MumuTW +- **feat(settings):** raise sticky round-robin limit caps to 1000 ([#12015](https://github.com/diegosouzapw/OmniRoute/pull/12015)) — thanks @adivekar-utexas +- **feat(resilience):** operator-configurable global credential health check interval ([#12043](https://github.com/diegosouzapw/OmniRoute/pull/12043)) — thanks @adivekar-utexas +- **feat(quota):** use official OpenCode Go usage API ([#12124](https://github.com/diegosouzapw/OmniRoute/pull/12124)) — thanks @ddarkr +- **feat(resilience):** default the credential health check sweep to 60 minutes ([#12138](https://github.com/diegosouzapw/OmniRoute/pull/12138)) — thanks @adivekar-utexas +- **feat(quality):** new-code mode for the complexity and dead-code ratchets (Clean as You Code) ([#12142](https://github.com/diegosouzapw/OmniRoute/pull/12142)) +- **feat(dashboard):** orchestration canvas — unified model + snapshot hook (part 1/2) ([#12156](https://github.com/diegosouzapw/OmniRoute/pull/12156)) +- **feat(usage):** add Kilo Code balance and Kilo Pass quotas ([#12178](https://github.com/diegosouzapw/OmniRoute/pull/12178)) — thanks @killer30001000 +- **feat(providers):** refresh vendored ChatGPT Web connector to v4.0.7 ([#12181](https://github.com/diegosouzapw/OmniRoute/pull/12181)) — thanks @backryun +- **feat(providers):** manual "Clear cooldown" action in the cooling panel ([#12224](https://github.com/diegosouzapw/OmniRoute/pull/12224)) — thanks @patrykkopycinski +- **feat(providers):** restore ChatGPT Web via clean-room browser transport ([#12239](https://github.com/diegosouzapw/OmniRoute/pull/12239)) — thanks @backryun +- **feat(sse):** wire the PROVIDER_PROFILES window gate into the global provider cooldown ([#12247](https://github.com/diegosouzapw/OmniRoute/pull/12247)) +- **feat(providers):** modernize CLOVA Studio chat and embeddings ([#12277](https://github.com/diegosouzapw/OmniRoute/pull/12277)) — thanks @backryun +- **feat(browser):** adopt Obscura as primary headless browser engine with Chromium fallback ([#12286](https://github.com/diegosouzapw/OmniRoute/pull/12286)) — thanks @opensource-elearning +- **feat(sse):** full language parity for output styles (es/de/fr/it/ru/zh + autoDetect) ([#12289](https://github.com/diegosouzapw/OmniRoute/pull/12289)) +- **feat(quality):** complete test:scoped — --full map rebuild, stdin selection, CI loader parity (#8084 D1) ([#12353](https://github.com/diegosouzapw/OmniRoute/pull/12353)) +- **feat(providers):** refresh Fable, Cursor, and Devin catalogs ([#12367](https://github.com/diegosouzapw/OmniRoute/pull/12367)) — thanks @backryun +- **feat(video):** redact transcript text from logs and durable memory (#12150 P1) ([#12427](https://github.com/diegosouzapw/OmniRoute/pull/12427)) +- **feat(dashboard):** parent-link, genuine-continuation badge, and modal perf fixes ([#12448](https://github.com/diegosouzapw/OmniRoute/pull/12448)) — thanks @hartmark +- **feat(dashboard):** adaptive context-budget dial on compression panel ([#12488](https://github.com/diegosouzapw/OmniRoute/pull/12488)) — thanks @HouMinXi +- **feat(providers):** refresh Z.ai Web models and browser transport ([#12524](https://github.com/diegosouzapw/OmniRoute/pull/12524)) — thanks @backryun +- **feat(video):** redact raw client-snapshot transcript fields in the detailed log (#12150 P2) ([#12528](https://github.com/diegosouzapw/OmniRoute/pull/12528)) +- **feat(providers):** refresh NVIDIA hosted models ([#12538](https://github.com/diegosouzapw/OmniRoute/pull/12538)) — thanks @backryun +- **feat(quota):** Moonshot Open Platform balance and TPD lock for custom nodes ([#12590](https://github.com/diegosouzapw/OmniRoute/pull/12590)) — thanks @HouMinXi +- **feat(video):** redact transcript in the in-memory pending-request snapshot (#12430 item 6) ([#12596](https://github.com/diegosouzapw/OmniRoute/pull/12596)) +- **feat(dashboard):** link a log entry's Conversation Context to its owning conversation ([#12646](https://github.com/diegosouzapw/OmniRoute/pull/12646)) — thanks @hartmark +- **feat(catalog):** eligibility-gated free-tier bucket ([#12669](https://github.com/diegosouzapw/OmniRoute/pull/12669)) +- **feat(db):** fail-closed previous_response_id continuation for redacted video turns (#12150 P2b) ([#12707](https://github.com/diegosouzapw/OmniRoute/pull/12707)) +- **feat(gamification): pay the documented `streak_bonus` and `badge_unlock` XP rewards.** `XP_REWARDS` listed both rewards but the award pipeline never paid them: the private reward table in `events.ts` omitted them, `updateStreak()` did not report when a streak extended, and badge unlocks carried no XP. Every request that extends a daily streak now pays `streak_bonus × streak length` once per UTC day (guarded by a same-day `xp_audit_log` check), and every badge unlocked through the pipeline pays `badge_unlock` once per badge (guarded by the `user_badges` primary key; `unlockBadge()` now reports whether it inserted). Bonus XP flows through the same `addXp` + level sync + global/weekly/monthly leaderboard path as action XP, so level-ups and rankings include it. The Radar supporter recognition unlock stays XP-free. (#12522 — thanks @pacocartones) +- **feat(providers):** advertise a `free-tier` capability in the provider plugin manifest for every provider with documented free models, so sidecars and dashboards can filter free-capable providers without reading the quota catalog ([#12786](https://github.com/diegosouzapw/OmniRoute/pull/12786)) — thanks @maxmad64bis +- **feat(providers):** Added EURouter as an OpenAI-compatible API-key gateway (`https://api.eurouter.ai/v1`), with live model discovery via `passthroughModels`. Its copy states that models are served by third-party upstreams listed per model, so an EU-based router is not read as EU data residency for inference. ([#13025](https://github.com/diegosouzapw/OmniRoute/pull/13025)) — thanks @ntdatt812 +- **feat(providers):** Added GreenPT as an OpenAI-compatible API-key provider (`https://api.greenpt.ai/v1`), with live model discovery via `passthroughModels`. No free-inference badge: the published docs describe a free API subscription billed per token, not a free tier. ([#13024](https://github.com/diegosouzapw/OmniRoute/pull/13024)) — thanks @ntdatt812 +- **feat(i18n):** 9 new locales — Greek (`el`), Croatian (`hr`), Serbian (`sr`), Lithuanian (`lt`), Estonian (`et`), Latvian (`lv`), Slovenian (`sl`), Maltese (`mt`), Irish (`ga`) — across the dashboard, docs mirrors, CLI, README and the site; OmniRoute now ships every official EU language (51 locales). (#13044) +- **feat(i18n):** 8 new locales — Kannada (`kn`), Malayalam (`ml`), Odia (`or`), Punjabi (`pa`), Nepali (`ne`), Sinhala (`si`), Burmese (`my`), Khmer (`km`) — across the dashboard, docs mirrors, CLI, README and the site (59 locales). The translator now restores the ICU literal escape around angle placeholders and splits oversized docs sections before translating. (#0000) ([#13660](https://github.com/diegosouzapw/OmniRoute/pull/13660)) +- feat(providers): **list Agnes 3.0 Flash as the current free chat model, drop retired 1.5 Flash, add Image 2.0/2.5 Flash plus Video 2.5/2.5 Flash, and discover the live `/v1/models` catalog (including the CN host `api.agnes-ai.cn`).** `agnes-1.5-flash` now forwards to `agnes-3.0-flash`. Video 2.5 polls `GET /v1/videos/{id}` (not the V2.0 `/agnesapi` contract). Live `/v1/models` (2026-09-09) no longer serves 1.5; the wiki marks it deprecated. 3.0 Flash is 512K context / 65,536 max output, same window as 2.5. CN-region keys use the existing per-connection base-URL field, default stays `apihub.agnes-ai.com`. ([#13120](https://github.com/diegosouzapw/OmniRoute/pull/13120)) — thanks @HouMinXi +- **feat(sse):** Codex and OpenAI catalogs list GPT-6 Astra with effort aliases (`-low` through `-ultra`); Codex CLI identity pins `@openai/codex@0.153.4` in lockstep with the image ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **feat(dashboard):** the orchestration History tab gained a "Compare runs" mode — toggling it turns each grid cell into a 2-item selection queue (a 3rd click drops the oldest pick), and picking two cells opens a side-by-side comparison panel instead of the usual detail drawer. The panel fetches both runs' detail the same way the drawer does (falling back to persisted history once a run leaves the live TTL window) and shows, per side: identity/source/state, start time, a signed `right - left` delta for duration/cost/event count, the event timeline aligned by index, and any memory hits. One side's fetch failing never blocks the other, and a delta is only ever computed when both sides have a finite value — otherwise it renders "—", never `NaN`. Comparing two runs from different sources or skills still works; a banner marks the deltas as informational rather than hiding them, since the two runs aren't a strict apples-to-apples pair. ([#12677](https://github.com/diegosouzapw/OmniRoute/pull/12677)) +- **feat(dashboard):** badge a conversation that never reached a clean stop ([#12717](https://github.com/diegosouzapw/OmniRoute/pull/12717)) — thanks @hartmark +- **feat(release):** reconcile-changelog tool + version-anchored fragment aggregation ([#12987](https://github.com/diegosouzapw/OmniRoute/pull/12987)) +- **feat(mcp):** carry context_length through MCP list_models_catalog projection (#12776) ([#13406](https://github.com/diegosouzapw/OmniRoute/pull/13406)) — thanks @KooshaPari +- **feat(cli):** make startup readiness budget configurable (#13369) ([#13433](https://github.com/diegosouzapw/OmniRoute/pull/13433)) — thanks @KooshaPari + ### 🐛 Bug Fixes +- **security(streaming):** sanitize generic mid-stream error messages before emitting OpenAI, + Responses, or Claude SSE failure frames and before diagnostic logging, while preserving raw + failures for internal classification and keeping client disconnects out of provider failure state. ([#12457](https://github.com/diegosouzapw/OmniRoute/pull/12457)) +- **fix(claude):** `blockExtraUsage: false` no longer lets 5h quota preflight skip the account; extra usage is billed after the session bar is gone, so the request must reach Anthropic ([#12803](https://github.com/diegosouzapw/OmniRoute/pull/12803)) — thanks @HouMinXi +- Sanitize HuggingChat conversation-creation and message-send transport failures before they reach client error bodies or provider logs. ([#12467](https://github.com/diegosouzapw/OmniRoute/pull/12467)) +- **fix(dashboard):** Keep local and theme-aware provider SVG icons at a definite layout size so Chromium does not collapse them to 0×0 after the v3.8.50 image-rendering change ([#12054](https://github.com/diegosouzapw/OmniRoute/pull/12054)) — thanks @ponkcore +- **fix(security):** Sanitize provider and runtime failures before public API, SSE and MCP responses and before persistent request, proxy and usage logs, preventing credentials, stack traces and host filesystem paths from crossing those boundaries while preserving stable error codes and useful diagnostics. ([#12506](https://github.com/diegosouzapw/OmniRoute/pull/12506)) +- **fix(cli):** `omniroute tunnel create` no longer crashes with `Cannot read properties of undefined (reading optsWithGlobals)` — removed the duplicate positional argument that caused Commander.js to misalign the action callback parameters ([#12295](https://github.com/diegosouzapw/OmniRoute/issues/12295)) ([#12368](https://github.com/diegosouzapw/OmniRoute/pull/12368)) — thanks @gonisulaimann +- Stop advertising Gemini Live-only models as supported audio endpoints until OmniRoute proxies the bidirectional Live protocol. ([#11710](https://github.com/diegosouzapw/OmniRoute/pull/11710)) — thanks @RaviTharuma +- **fix(kie):** reroute `flux/kontext` off the KIE Market `createTask` flow — it is catalogued with `isMarket: true` but has no Market catalog page, so KIE rejected it with "model name not supported"; it now hits the dedicated `POST /api/v1/flux/kontext/generate` / `GET /api/v1/flux/kontext/record-info` endpoints instead (#11296). ([#11985](https://github.com/diegosouzapw/OmniRoute/pull/11985)) — thanks @hartmark +- **fix(kie):** correct 12 more KIE Market catalog ids that were sent to `createTask` unchanged but diverge from KIE's documented upstream `model` values — GPT Image 2 T2I/I2I (drops the `gpt/` prefix), GPT Image 1.5 T2I/I2I (`gpt-image/` namespace), Seedream 5.0 Lite T2I/I2I (drops the `.0`), all 4 Flux 2 variants (`flux-2/` namespace, generic variant renamed `flex`), and Wan 2.7 Image / Image Pro (dash instead of dot) — each verified individually against the literal example request published on docs.kie.ai. `#11326`'s "everything else already matches" claim was wrong a second time (#11296); `z-image/4.0-*`/`z-image/4.5-*` and `flux/kontext` remain open, documented as unresolved in `KIE_MARKET_UPSTREAM_MODEL_IDS`'s comment pending further verification. ([#11667](https://github.com/diegosouzapw/OmniRoute/pull/11667)) +- **fix(dashboard):** `useApiKeySave.handleSaveApiKey` no longer forces a full upstream `/models` catalog sync on every non-curated provider connection save — callers can now pass `skipModelSync: true` to opt out, so a workflow that only wants to add one manual model no longer floods the provider's available-models list with hundreds/thousands of synced entries. The flag is a client-side intent signal only and is stripped before the connection payload is POSTed to `/api/providers`; default behavior (full sync on save) is unchanged when the flag is omitted (#11324) ([#11664](https://github.com/diegosouzapw/OmniRoute/pull/11664)) +- **Provider connections:** keep `tokenExpiresAt` when a connection is created. The create-path allowlist omitted it, so every insert stored NULL and the dashboard token badge could read a fresh connection as expired until its first background refresh ([#11368](https://github.com/diegosouzapw/OmniRoute/pull/11368)). — thanks @ntdatt812 +- **fix(combos):** the combo builder's precision-select, global-model-search, and manual-entry flows now serialize a model step's `model` string using the provider's already-computed routing-alias prefix (e.g. `oc/`) instead of rebuilding it from the raw canonical `providerId`, fixing the no-auth "OpenCode Free" provider (`opencode`) being routed to the unrelated paid "OpenCode Zen" provider (`opencode-zen`) because `opencode` doubles as a manual routing-prefix override ([#11433](https://github.com/diegosouzapw/OmniRoute/issues/11433)). ([#11670](https://github.com/diegosouzapw/OmniRoute/pull/11670)) +- **fix(radar):** the catalog feed cache now keeps `generatedAt`, the date the feed's data was built, next to `fetchedAt`, the date this install downloaded it (#11435). The feed schema requires that date and the sync path validates it, but the cache dropped it — so a feed fetched minutes ago and one carrying weeks-old figures looked identical to everything downstream, including the dashboard's "Last fetched" line. `getRadarCatalog().meta` and `GET /api/radar/status` now report both dates, the latter as its own field rather than folded into `version` — and omitted entirely for the offers and intel caches, which keep no build date, where a `null` would read as "unknown" rather than "never stored". The dashboard still shows only the fetch time; surfacing the build date there needs a new translated label and is left to a follow-up. Rows cached before migration 163 read back as `null`: unknown stays unknown instead of borrowing the fetch time. The referrals cache has persisted the same date since migration 142. — thanks @maxmad64bis +- **fix(cli):** postinstall no longer fills `JWT_SECRET` and `API_KEY_SECRET` in the installed package's `.env` (#11436). `.env.example` ships both blank on purpose: the server restores them from its durable store, or generates and persists them there, in `ensureSecrets()`. Pre-filling them defeated that — the file lives inside the package directory, so `npm i -g` replaced it and postinstall wrote _different_ values, while `ensureSecrets()` (which only acts on an empty variable) never got to restore the real ones. Both secrets rotated silently on every update, invalidating dashboard sessions and API-key CRCs. `STORAGE_ENCRYPTION_KEY` left the same list for the same reason in #1622; its comment pointed at a function that no longer exists and now names the real provisioning path. — thanks @maxmad64bis +- **fix(cli):** the CLI now says when a loaded `.env` lives inside the installed package directory (#11437). It already announces every env file it reads, without distinguishing the ones that survive an update from the one that does not: `npm i -g` replaces the package directory wholesale, so values set there are gone at the next update, silently. The warning names the durable path to move them to, and fires only when that file actually supplied a value — a file entirely shadowed by a durable one supplied nothing. A development checkout stays silent: there the same path is stable and documented in `SETUP_GUIDE.md`. — thanks @maxmad64bis +- **fix(free-models):** the shared `isFreeModel()` predicate no longer reports catalog entries marked `freeType: "discontinued"` as free, so `hidePaidModels` can't route to Pollinations' seven premium models that now require a paid key ([#11441](https://github.com/diegosouzapw/OmniRoute/pull/11441)) — thanks @maxmad64bis +- fix(ui): let AnySearch use the normal provider-icon fallback when LobeHub has no matching icon (#11449) +- Fixed the v3.8.50 Costs and Analytics dashboards so flat-rate Claude Code usage can be shown as an explicitly requested token-price estimate without changing default billed-cost semantics. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed archived usage retention so each request is priced individually instead of pricing a day's summed tokens once, which understated archived cost whenever a day mixed cache-heavy and ordinary requests. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed the Costs dashboard so it discloses when displayed figures include flat-rate token-price estimates instead of labelling them as billed spend, using the flag the analytics API already returns; the month-end projection and the CSV/JSON exports carry the same marker, and billed-cost mode is unchanged. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- **fix(combo):** attach the same combo-diagnostics trace (`poolSize`/`attemptOrder`/`excluded`/`terminalReason`, plus `x-omniroute-combo-*` headers) to the round-robin strategy's and the nested pipeline/fusion runtime-unit loop's "Maximum combo retry limit reached" 503 that the priority-strategy path already attaches for the identical terminal condition — previously those two paths returned a bare, contextless 503 ([#11462](https://github.com/diegosouzapw/OmniRoute/issues/11462)). ([#11665](https://github.com/diegosouzapw/OmniRoute/pull/11665)) +- **fix(bun):** use native `bun:sqlite` in `bootstrap-env` and `sync-env` to avoid loading `better-sqlite3` N-API addon during Bun startup ([#11468](https://github.com/diegosouzapw/OmniRoute/pull/11468)) — thanks @TheDemonTuan +- **fix(docker-bun):** make `Dockerfile.bun` install reliable with pre-install script helpers and native `bun:sqlite` smoke check ([#11470](https://github.com/diegosouzapw/OmniRoute/pull/11470)) — thanks @TheDemonTuan +- **fix(docker-bun):** remove every vendored `better-sqlite3` native addon from the Bun runtime image so startup cannot abort before the `bun:sqlite` fallback ([#11482](https://github.com/diegosouzapw/OmniRoute/pull/11482)) — thanks @TheDemonTuan +- **fix(autoCombo):** effort/alias model variants (`gpt-5.6-sol-xhigh`, `gpt-5.6`, cursor's `claude-4.6-opus-high`) inherit their base model's task fitness instead of falling to the wildcard 0.5 ([#11492](https://github.com/diegosouzapw/OmniRoute/pull/11492)) — thanks @MumuTW +- fix(db): rate-limit repeated Arena ELO leaderboard fetch-failure warnings instead of logging one per sync attempt (#11500) ([#11989](https://github.com/diegosouzapw/OmniRoute/pull/11989)) — thanks @hartmark +- **fix(db):** dedupe the raw `[Encryption] Decryption failed...` log line emitted by the lazy-decrypt views (`createLazyRowProxy`/`createLazyConnectionView`), which power `getProviderConnections()` and were re-triggering that line on every CredentialHealth/model-sync cycle for the same corrupt or stale-key credential — a fresh Proxy over a fresh row on every cycle meant the per-proxy memoization never suppressed it, unlike the dedup `decryptConnectionFields()` already had since [#9927](https://github.com/diegosouzapw/OmniRoute/issues/9927). Now shares that dedupe tracking so the line logs at most once per credential ([#11500](https://github.com/diegosouzapw/OmniRoute/issues/11500)). ([#11678](https://github.com/diegosouzapw/OmniRoute/pull/11678)) +- **fix(intelligence):** Arena ELO sync stores a synthesized base-model row for every effort/harness variant (`claude-opus-5-max` → `claude-opus-5`, `gpt-5.6-sol-xhigh (codex-harness)` → `gpt-5.6-sol`), so bare model requests reach the synced intelligence layer instead of the static fallback table; the hand-maintained `MODEL_ALIAS_MAP` — which collapsed generations (`gpt-5.5` → `gpt-5`) — is gone ([#11506](https://github.com/diegosouzapw/OmniRoute/pull/11506)) — thanks @MumuTW +- **fix(autoCombo,sse):** catalog hygiene — the static task-fitness table no longer ranks retired models above live ones (dead rows dropped, patterns matched on segment boundaries), the 7 deprecation aliases that forwarded to retired or non-existent models now point at the vendors' published replacements, an alias is no longer rewritten when the serving provider offers the id as-is, and a new `check:model-lifecycle` gate diffs both tables against a vendor lifecycle snapshot ([#11507](https://github.com/diegosouzapw/OmniRoute/pull/11507)) — thanks @MumuTW +- **fix(sse):** stop dropping resolved `thoughtSignature` values on parallel (multi tool-call) turns sent to Gemini 3.x — the claude→gemini and openai→gemini translators previously kept the signature only on the *first* function call of a message, causing Gemini to reject subsequent calls in the same turn with HTTP 400 "Function call is missing a thought_signature"; each function call now keeps its own resolved signature ([#11510](https://github.com/diegosouzapw/OmniRoute/issues/11510)). ([#11669](https://github.com/diegosouzapw/OmniRoute/pull/11669)) +- Treat Docker publishing from a non-default release branch as an expected skip instead of a failed workflow, while preserving the guard that prevents it from overwriting the `next` channel. ([#11527](https://github.com/diegosouzapw/OmniRoute/pull/11527)) — thanks @pacocartones +- fix(search): prefer credentialed providers over duckduckgo-free fallback (#11524) — the fallback-only loop ran before the credentialed-providers loop in `executeWebSearch`, making configured providers unreachable when `duckduckgo-free` was available. ([#11565](https://github.com/diegosouzapw/OmniRoute/pull/11565)) — thanks @vsd2807 +- **fix(sse):** cap the upstream headers-wait phase for STREAMING requests to a client-realistic ceiling (110s, under Codex's own ~120s hard client-abort window) instead of the flat 10-minute `FETCH_TIMEOUT_MS` default — that default was 5x longer than the body-phase readiness watchdog's own adaptive bound, so a request whose upstream never returned any response at all (not even headers, e.g. a stalled NVIDIA target behind a tool-heavy Responses→Chat translation) kept the client connection alive on keepalives only, guaranteeing the client's own patience ran out first with an opaque 499 instead of OmniRoute detecting and failing the stall fast. Non-streaming requests are unaffected — they keep the existing flat default (`open-sse/utils/fetchStartTimeoutPolicy.ts`) (#11526) ([#11668](https://github.com/diegosouzapw/OmniRoute/pull/11668)) +- **fix(ci):** protocol-clients E2E harness boots the peer-stamped custom server (`run-next.mjs dev`) instead of the bare `next` CLI, so `LOCAL_ONLY` locality resolves from the real TCP peer stamp and `/api/mcp/audit` answers 200 (open bootstrap preserved via an `OMNIROUTE_E2E_BOOTSTRAP_MODE=open` credential clear in `run-next.mjs`, pinned to loopback `HOST`) instead of a deterministic 403 (#11535) ([#11549](https://github.com/diegosouzapw/OmniRoute/pull/11549)) — thanks @jonlwheat2-gif +- **fix(docs):** the Auto-Combo engine is described with the number of factors it actually declares. Four different counts were in circulation — 6, 9, 13 and 14 across `AGENTS.md`, four guides and six code comments — while `DEFAULT_WEIGHTS` declares 15. `check:docs-counts` now reads that number from the source, so adding a factor without updating the prose fails CI ([#11545](https://github.com/diegosouzapw/OmniRoute/pull/11545)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **fix(quota-share):** in-flight leases are per request, so an aborted request's slot ages out instead of being kept alive by later traffic on the same connection ([#11547](https://github.com/diegosouzapw/OmniRoute/pull/11547)) — thanks @abhisheksharma2411 +- The free-tier summary route now serves the refreshed Radar catalog when the feed is active — the same numbers the dashboard shows — and states which catalog answered plus its real build date, instead of always reporting release-frozen figures with a stale curation date. The supporter-key live feed stays reserved to authenticated callers of the instance. ([#11550](https://github.com/diegosouzapw/OmniRoute/pull/11550)) — thanks @maxmad64bis +- **sse:** fix LiveWS/embed-WS servers crashing at startup under the Node/tsx runtime — `liveServer.ts`, `embedWsProxy.ts` and `apiBridgeServer.ts` imported `@/shared/utils/httpClientAbortGuard` without the `.mjs` extension, so the client-abort crash guard added by [#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556) was unreachable and every dependent test failed with `ERR_MODULE_NOT_FOUND` ([#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556)). ([#11671](https://github.com/diegosouzapw/OmniRoute/pull/11671)) — thanks @jonlwheat2-gif / @yxyxy +- fix(chat): shed any of the matched provider-node's routing identifiers (public prefix or internal id) before model lookup, so composites like `//` no longer reach upstream verbatim (#11557) — thanks @maxmad64bis +- **fix(cache):** a semantic-cache entry now expires on its TTL instead of surviving until the next UTC midnight, and `dbEntries` stops counting expired rows ([#11573](https://github.com/diegosouzapw/OmniRoute/pull/11573)) — thanks @ntdatt812 +- **fix(api):** `/v1/models` schedules its stale-while-revalidate rebuild through Next's `after()` again, so a stale catalog reaches the client before the rebuild blocks the event loop ([#11574](https://github.com/diegosouzapw/OmniRoute/pull/11574)) — thanks @ntdatt812 +- **fix(check):** the `known-symbols` executor conformance gate awaits the now-async `getExecutor()`, so it stops reporting all 142 registered aliases as dead symbols and can detect a lazy import that fails ([#11582](https://github.com/diegosouzapw/OmniRoute/pull/11582)) — thanks @ntdatt812 +- **fix(pack):** register `bin/cli/utils/volatileEnvPath.mjs` in `PACK_ARTIFACT_REQUIRED_PATHS`, so the prune can no longer silently drop a module the published CLI imports on every boot ([#11588](https://github.com/diegosouzapw/OmniRoute/pull/11588)) — thanks @ntdatt812 +- **i18n(vi):** translate the five reliability-column strings on the free-provider rankings page, which rendered as `__MISSING__:` markers in the Vietnamese UI ([#11589](https://github.com/diegosouzapw/OmniRoute/pull/11589)) — thanks @ntdatt812 +- **resilience:** restore the expired-connection retry-budget probe in the token-health sweep — the `!isGitHubAccessTokenOnlyConnection` carve-out reintroduced by #11608 contradicted the boundary pinned by #11592, so a GitHub connection parked at `expired` with retry budget remaining was never probed and could never self-heal ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)). ([#11672](https://github.com/diegosouzapw/OmniRoute/pull/11672)) — thanks @jonlwheat2-gif / @ntdatt812 +- **fix(dashboard):** Provider Stats now displays combo success rates on their native 0–100 percentage scale instead of multiplying them twice ([#11595](https://github.com/diegosouzapw/OmniRoute/pull/11595)) — thanks @pacocartones +- **fix(providers):** route DuckDuckGo Web Chat through the verified `duck.ai` endpoint, refresh its free model catalog, and avoid forwarding unsolved anti-abuse challenges ([#11597](https://github.com/diegosouzapw/OmniRoute/pull/11597)) — thanks @solstxce +- **fix(ui):** Console log Refresh and Copy controls now expose localized accessible names, keep copy actions visible on keyboard focus, and announce copy completion safely ([#11599](https://github.com/diegosouzapw/OmniRoute/pull/11599)) — thanks @pacocartones +- Dropped the stale-`.eslintcache` `restore-keys` fallback from both "Restore ESLint file cache" steps in `ci.yml`, so the blocking `Lint` job can no longer be served per-file verdicts computed under a different lint config, suppressions file or lockfile. `quality.yml` had already dropped it in #11963; `ci.yml` — the workflow that actually gates PRs — had not (#11600). ([#11996](https://github.com/diegosouzapw/OmniRoute/pull/11996)) +- fix(ci): pass `--pass-on-unpruned-suppressions` in `run-eslint-json.mjs` so the CI Lint job no longer fails when a suppression is merely orphaned by a genuine fix, mirroring the identical fix already in `validate-release-green.mjs` (#11600) ([#11983](https://github.com/diegosouzapw/OmniRoute/pull/11983)) — thanks @hartmark +- fix(handoff): enforce provider allowlist for universal handoff (#11602) — universal handoff now skips summarization when the selected summary model's provider is not included in the configured provider allowlist. ([#11619](https://github.com/diegosouzapw/OmniRoute/pull/11619)) — thanks @vsd2807 +- **fix(dashboard):** Show a stable error state when Search Analytics returns an HTTP error or malformed data ([#11603](https://github.com/diegosouzapw/OmniRoute/pull/11603)) — thanks @pacocartones +- **test(gamification):** pin the aggregate profile level to the XP-derived semantics of #11604 — `getAggregateXp()` now derives `currentLevel` from the summed XP (`calculateLevel(sum)`), not `MAX(stored current_level)`, and the #3484 fixture levels are aligned with the XP curve ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)). ([#11676](https://github.com/diegosouzapw/OmniRoute/pull/11676)) — thanks @jonlwheat2-gif / @pacocartones +- **fix(dashboard):** Keep the Profile level and progress aligned with aggregate XP, including bounded handling for invalid totals ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)) — thanks @pacocartones +- **fix(dashboard):** Prevent locked hidden badges from revealing their icon or opening private badge details before they are earned ([#11605](https://github.com/diegosouzapw/OmniRoute/pull/11605)) — thanks @pacocartones +- **fix(dashboard):** Restore keyboard focus after shared modals close and cancel delayed autofocus during cleanup ([#11607](https://github.com/diegosouzapw/OmniRoute/pull/11607)) — thanks @pacocartones +- **fix(dashboard):** Enable Enter and Space activation for clickable data-table rows without hijacking nested controls ([#11610](https://github.com/diegosouzapw/OmniRoute/pull/11610)) — thanks @pacocartones +- **fix(autoCombo,sse):** vendor-retired catalog ids are dropped from the auto-combo candidate pool and no longer win on leftover `arena_elo` / `user_override` rows; `getModelLifecycleDecision` consults `model-lifecycle.json` (prefix-stripped) so aggregator traffic is not `untracked` for ids the snapshot already knows ([#11625](https://github.com/diegosouzapw/OmniRoute/issues/11625)) ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)) — thanks @MumuTW +- **test(chatcore):** move Codex/Claude combo fixtures off models the lifecycle guard now rejects — `gpt-5.1-codex`/`gpt-5-codex` are vendor-retired (snapshot, #11626) and `claude-3-5-sonnet-20241022` is shut down, so native-passthrough and combo-fallback tests switched to `gpt-5.6-sol` and `claude-sonnet-4.6` ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)). ([#11675](https://github.com/diegosouzapw/OmniRoute/pull/11675)) — thanks @jonlwheat2-gif / @MumuTW +- fix(opencode-plugin): stop a free-tier budget that rounds up across a magnitude from rendering as `1000K`/`1000M` in the model picker — `fmtTokens` chose its unit from the raw token count and then rounded with `toFixed(1)`, so 999,950–999,999 printed as `1000K` rather than `1M` and just under a billion printed as `1000M` rather than `1B` ([#11684](https://github.com/diegosouzapw/OmniRoute/pull/11684)) — thanks @abhisheksharma2411 +- Cap prose-derived Antigravity quota resets at the configured model cooldown maximum while preserving authoritative Retry-After headers and Google RetryInfo hints. (#11695) Thanks @pacocartones. ([#11823](https://github.com/diegosouzapw/OmniRoute/pull/11823)) +- Compare `_rebaseline_*` notes structurally in the ratchet-bank verifier so an object-valued note no longer reports itself as rewritten on an unmodified tree — the nightly `bank-ratchet-shrinks` lane had been aborting on every run since 2026-08-11, leaving 41,672 lines of earned file-size shrinks unbanked (#11702). ([#11892](https://github.com/diegosouzapw/OmniRoute/pull/11892)) — thanks @MumuTW +- **fix(build):** `prepublish.ts` bundles the ChatGPT Web (Codex) MCP bridge through `runBuildTool()` instead of spawning `npx.cmd` raw, fixing the build crash on Node ≥ 20/Windows where `.cmd` shims cannot be spawned without a shell (EINVAL) ([#11704](https://github.com/diegosouzapw/OmniRoute/issues/11704)) ([#11706](https://github.com/diegosouzapw/OmniRoute/pull/11706)) — thanks @jonlwheat2-gif +- fix(codex): keep `parallel_tool_calls:false` on the translated Codex Responses Lite path (#11707) ([#11984](https://github.com/diegosouzapw/OmniRoute/pull/11984)) — thanks @hartmark +- **fix(tests):** the new Gemini Web Images retirement test (#11708) called the now-async `getExecutor()` synchronously, throwing `TypeError: getExecutor(...).getProvider is not a function` instead of asserting Gemini Web chat stays available. Awaited to match the R0.3 executor-registry refactor (#11220). ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(search):** Allow REST and MCP web-fetch requests to select the advertised `anysearch-search` provider explicitly ([#11716](https://github.com/diegosouzapw/OmniRoute/pull/11716)) — thanks @pacocartones +- **fix(providers):** Keep overnight peak-hour protection active after midnight according to the window's configured start day ([#11718](https://github.com/diegosouzapw/OmniRoute/pull/11718)) — thanks @pacocartones +- **fix(providers):** correct all five g4f.space gateway descriptions to document proof-of-work credits or member API keys, variable limits, and the remote prompt boundary instead of promising free keyless access at five requests per minute ([#11738](https://github.com/diegosouzapw/OmniRoute/pull/11738)). +- **fix(db):** `model_capabilities` is created by a migration instead of lazily on the first models.dev sync, so a clean install and an upgraded install converge on the same schema regardless of which features have run ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(ci):** `check:install-upgrade` now fails on an `npm` install truncated by ENOSPC (npm reports it as a warning and still exits 0), authenticates its health probe so the version assertion works against the hardened health payload, frees the clean-install tree before the upgrade phase, and no longer reports a schema divergence computed from a boot that never served ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(routing):** harden session-affinity key derivation against oversized explicit IDs and empty structural-payload collisions, while recognizing Gemini and common text request shapes ([#11746](https://github.com/diegosouzapw/OmniRoute/pull/11746)) — thanks @MumuTW +- **fix(packages/browser-pool):** regenerate `package-lock.json` so the `packages/browser-pool` workspace's locked `playwright`/`@types/node` (and transitives) match its `package.json` specs, fixing cache-only/offline installs (`npm ci --offline`, Nix `buildNpmPackage`) that previously failed with `ENOTCACHED` ([#11747](https://github.com/diegosouzapw/OmniRoute/issues/11747)) — thanks @benjaminkitt ([#11784](https://github.com/diegosouzapw/OmniRoute/pull/11784)) — thanks @geekyNads +- **fix(assets):** Stop distributing 79 provider and CLI logos without verified source/license provenance, keep every provider/tool available through the internal generic icon, and prevent those IDs from falling through to an external icon CDN. ([#11750](https://github.com/diegosouzapw/OmniRoute/pull/11750)) +- **fix(providers):** retire the common `chatgpt-web` / `cgpt-web` provider because its pre-key and proof-of-work provenance could not be cleared ([#11754](https://github.com/diegosouzapw/OmniRoute/pull/11754)) — explicit chat and image requests, slash-prefixed models, persisted aliases, proxy overrides, and combo targets now fail closed with a sanitized HTTP `410` / `PROVIDER_RETIRED` before upstream dispatch. Migration 163 tombstones matching connections and invalidates their active leases without deleting history or widening API-key allowlists. The independent `chatgpt-web-codex` / `cgpt-codex` provider and its MIT-noticed vendored implementation remain available. +- **fix(usage):** quota and usage refresh no longer 409 when an exclusive lease reserves the connection ([#11758](https://github.com/diegosouzapw/OmniRoute/pull/11758)) — thanks @TheDemonTuan +- Keep the embedding registry's vector width and `embedding` type on models when a synced model exists for the same id, so `/v1/models` no longer reports registry-described embedding models widthless or untyped (#11761) — thanks @rahilmavani +- Correct `google/gemini-embedding-001` on the OpenRouter route to 3072 dimensions, the width it returns when `dimensions` is not sent (#11761) — thanks @rahilmavani +- **fix(vision-bridge):** Restrict automatic vision routing and fallbacks to models available in authoritative live provider catalogs, including aliases and effort variants ([#11767](https://github.com/diegosouzapw/OmniRoute/issues/11767)) — thanks @pacocartones ([#11813](https://github.com/diegosouzapw/OmniRoute/pull/11813)) +- **fix(providers):** reclassify Cerebras as a one-time $5 signup credit (payment method required, 30-day validity), not a recurring no-card 1M tokens/day trial ([#11773](https://github.com/diegosouzapw/OmniRoute/issues/11773)) ([#12591](https://github.com/diegosouzapw/OmniRoute/pull/12591)) — thanks @HouMinXi +- fix(cli): stop prepublish from re-rebuilding the already-built ESM-only opencode-plugin dist (#11787) ([#11990](https://github.com/diegosouzapw/OmniRoute/pull/11990)) — thanks @hartmark +- **fix(db):** Fresh installs no longer log a non-fatal `no such table: compression_run_telemetry` warning when retention cleanup runs before the lazily-created telemetry table exists ([#11802](https://github.com/diegosouzapw/OmniRoute/pull/11802)) — thanks @RaviTharuma +- fix(sse): set X-OmniRoute-Selected-Connection-Id on successful combo dispatches so downstream consumers stop falling back to an empty connection id (#11810) ([#11986](https://github.com/diegosouzapw/OmniRoute/pull/11986)) — thanks @hartmark +- **fix(services):** embedded CLIProxyAPI lifecycle checks now use public `/healthz`, while model discovery uses the configured dedicated data-plane API key instead of the management password ([#11811](https://github.com/diegosouzapw/OmniRoute/pull/11811)) — thanks @RaviTharuma +- Fix persisted model steps returning an error in the playground route simulator, and warn when structural steps cannot be simulated. ([#11862](https://github.com/diegosouzapw/OmniRoute/pull/11862)) — thanks @pacocartones +- **fix(providers):** Antigravity's dynamic mitmAlias table no longer routes `gemini-3.7-flash-{high,medium,low}` to a literal tier-suffixed upstream id just because one connected account's own discovery listed it directly — those display ids always resolve through the safe `gemini-3.7-flash-tiered` static alias, so one account's Google-provisioned access no longer 404s every sibling account of the provider ([#11824](https://github.com/diegosouzapw/OmniRoute/issues/11824), [#11651](https://github.com/diegosouzapw/OmniRoute/issues/11651)) ([#11988](https://github.com/diegosouzapw/OmniRoute/pull/11988)) — thanks @hartmark +- **fix(dashboard):** Expose Profile loading, errors, page structure, and XP progress to assistive technologies ([#11838](https://github.com/diegosouzapw/OmniRoute/pull/11838)) — thanks @pacocartones +- **fix(socks):** `Agent.connectTimeout` now bounds both SOCKS handshake and TLS connect, and `family === null` no longer falls back to `fetch-socks` ([#11842](https://github.com/diegosouzapw/OmniRoute/pull/11842)) — thanks @maxmad64bis +- **perf(compression):** OOM mitigations for large payload hashing, memoization, and token estimation ([#11844](https://github.com/diegosouzapw/OmniRoute/pull/11844) — thanks @AndrianBalanescu) +- Excluded Next.js Node File Trace manifests (`*.nft.json`) from the published npm tarball. They are build-time metadata and are never read while serving, but had grown to 668.7 MB — 61% of the package — which pushed the upload past the registry limit and made `npm publish` fail with `413 Payload Too Large`. ([#11864](https://github.com/diegosouzapw/OmniRoute/pull/11864)) +- **fix(cli):** keep the global OmniRoute server `--base-url` separate from provider-node `--endpoint` values in add, update, and validate commands ([#11860](https://github.com/diegosouzapw/OmniRoute/pull/11860)) — thanks @pacocartones +- **fix(config):** Nous Research's `Hermes-4-405B` model now displays as "Hermes 4 405B (Nous Research)" in both the provider registry and the free-model catalog, instead of the mislabelled "Hermes 4 7B" ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#11993](https://github.com/diegosouzapw/OmniRoute/pull/11993)) +- **fix(provider/nous):** inject required user= tag into Nous Research inference requests to resolve upstream 400 "missing tags" error ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#12044](https://github.com/diegosouzapw/OmniRoute/pull/12044)) +- **fix(build):** `npm run build` now fails in one second with a named package and a copy-pasteable fix when npm silently drops an externalised optional native dependency, instead of dying four minutes in with `Module not found: Can't resolve 'better-sqlite3'` ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- **fix(install):** `postinstall` no longer throws `ReferenceError: isAndroid is not defined` — failing the whole `npm install` — when the `better-sqlite3` rebuild fallback times out; the manual-fix guidance is reachable again ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- Fixed the Alibaba free-tier allowlist test that went red on its own once the shipped catalog's `validUntil` (2026-08-27) passed, leaving every PR and `main` with a failing `Unit Tests (1/8)`. The test now builds its own packs with dates it controls, and covers the expired-pack fallback that production has actually been serving. ([#11867](https://github.com/diegosouzapw/OmniRoute/pull/11867)) +- Split the npm registry upload into its own GitHub-hosted job. npm refuses `--provenance` from a self-hosted runner (`422 ... Only "github-hosted" runners are supported`), which blocked the v3.8.50 publish; the heavy verification cannot move to a hosted runner, so it now hands the proven tarball over instead. ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(ui):** the shared `DataTable` loading state no longer reads its decorative ⏳ glyph out to assistive technology, and now carries the same `role="status"` / `aria-live="polite"` / `aria-busy="true"` semantics as `PageLoading` ([#11869](https://github.com/diegosouzapw/OmniRoute/pull/11869)) — thanks @pacocartones +- **fix(api):** Bound the `timeout` accepted by `POST /api/providers/[id]/login` to the same 15s–600s window the Adobe Firefly and Conol browser-login paths already enforce, so a request can no longer pin a headful browser and the single active-login slot with an arbitrarily large poll budget. ([#11871](https://github.com/diegosouzapw/OmniRoute/pull/11871)) — thanks @pacocartones +- **fix(api):** Stop the tunnel and MITM routes from returning raw child-process error text, which disclosed host paths, binary install locations and Tailscale `tskey-*` credentials in a body some of these routes serve to non-loopback callers, and return a real 400 instead of a framework 500 when a tunnel request body fails validation. ([#11872](https://github.com/diegosouzapw/OmniRoute/pull/11872)) — thanks @pacocartones +- **fix(open-sse):** stream TTFT/ITL are sampled from a monotonic clock (`performance.now`) instead of `Date.now`, so an NTP correction or wall-clock jump can no longer inflate a genuine TTFT or produce a negative one that the `ttft >= 0` guard silently drops — protecting the router's OTel/EWMA/`usage_history`/speed-ranking signals ([#11873](https://github.com/diegosouzapw/OmniRoute/pull/11873)) — thanks @pacocartones +- **fix(providers):** xAI `reasoning_effort: "xhigh"` now reaches grok-4.6+ instead of being silently clamped to `"high"` ([#11879](https://github.com/diegosouzapw/OmniRoute/pull/11879)) — thanks @NoxzRCW +- **fix(dashboard):** the providers page no longer crashes into the error boundary when a provider id collides with an `Object.prototype` member (`constructor`, `__proto__`); icon lookups are own-property guarded ([#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)) — thanks @NoxzRCW +- **fix(skills):** injected skill tools declared in shorthand (`{"content": "string"}`) now forward valid JSON Schema, unblocking providers that validate tool schemas strictly such as Zhipu GLM on the Console Go tier ([#11881](https://github.com/diegosouzapw/OmniRoute/pull/11881)) — thanks @NoxzRCW +- **fix(api):** `POST /api/playground/simulate-route` now surfaces `combo-ref` and `provider-wildcard` persisted combo steps with a specific warning (naming the referenced combo, or the unresolved `provider/model` wildcard) instead of folding them into a generic "unsupported step" count; a `provider-wildcard` step is also now included as an unresolved target so the operator can see it is in the route (ported from [#11882](https://github.com/diegosouzapw/OmniRoute/pull/11882) — thanks @NoxzRCW). ([#11930](https://github.com/diegosouzapw/OmniRoute/pull/11930)) +- **fix(translator):** the streaming OpenAI→Claude translator keeps upstream usage, including prompt-cache tokens, when it arrives on a trailing `choices: []` chunk (Fireworks and any upstream using `stream_options.include_usage`) ([#11883](https://github.com/diegosouzapw/OmniRoute/pull/11883)) — thanks @NoxzRCW +- **fix(cli):** `omniroute update --apply` now tells you explicitly whether a running server was detected and, if so, that you must run `omniroute restart` to apply the update — it never restarted anything and previously implied the update was already live once files were installed. The dashboard's npm-mode Update flow (`/api/system/version`) now tries OmniRoute's own PID-file-managed supervisor before falling back to pm2, and reports an honest "restart required" step instead of a silent pm2-only "skipped" that read like a completed update. The server-side latest-version lookup backing the dashboard's update banner also gained `--prefer-online`, closing the same stale-npm-cache class already fixed in the CLI's own copy for #4376 ([#11885](https://github.com/diegosouzapw/OmniRoute/issues/11885)). ([#12005](https://github.com/diegosouzapw/OmniRoute/pull/12005)) +- **fix(resilience):** clear persisted LKGP pins when a target suffers connection/provider exhaustion or is skipped before dispatch due to cooldown/exhaustion/unavailability, preventing subsequent requests from repeatedly prioritizing known-dead providers ([#11911](https://github.com/diegosouzapw/OmniRoute/issues/11911)). ([#12013](https://github.com/diegosouzapw/OmniRoute/pull/12013)) — thanks @HouMinXi +- **fix(sse):** the OpenAI→Claude stream translator now defers the terminal `message_delta`/`message_stop` emission until the real usage block has arrived (or a genuine end-of-stream flush forces it) instead of emitting it immediately on `finish_reason` — previously, when the trailing usage-only chunk (`{"choices":[],"usage":{...}}`) arrived *after* the `finish_reason` chunk (the normal order for Fireworks/vLLM/Together and other `stream_options.include_usage` upstreams), the client-visible `message_delta` still carried stale/zero usage even though `state.usage` was internally corrected too late to matter (ported from [#11915](https://github.com/diegosouzapw/OmniRoute/pull/11915) — thanks @HouMinXi). ([#11933](https://github.com/diegosouzapw/OmniRoute/pull/11933)) +- **fix(sse):** `stripStore()` now forces `store=false` for stateless OpenAI-compatible Responses-API targets (unless the connection has explicitly opted in via `providerSpecificData.openaiStoreEnabled`), instead of only handling the `openai`/`agentrouter` cases — a client-supplied `store` value previously passed through untouched to backends that don't actually persist responses server-side ([#11916](https://github.com/diegosouzapw/OmniRoute/pull/11916) — thanks @HouMinXi). +- **fix(models):** custom provider-node models (synced, custom, and alias-backed) now appear under their configured prefix in the unified catalog when the operator's model-id prefix mode is canonical, instead of being dropped whenever alias-inclusion was otherwise disabled ([#11918](https://github.com/diegosouzapw/OmniRoute/pull/11918) — thanks @HouMinXi). +- **fix(models):** the unified model catalog now suppresses stale static registry models (including effort-tier variants) for any provider whose active connection has an authoritative live synced catalog, not only providers already using exclusive-synced-listing — a connection with `providerUsesAuthoritativeLiveCatalog` previously kept serving both the live-synced models and the stale static rows side by side ([#11919](https://github.com/diegosouzapw/OmniRoute/pull/11919) — thanks @HouMinXi). +- **fix(ui):** `ProviderIcon`'s three remaining unguarded lookups (`PROVIDER_ICON_ALIASES`, `LOCAL_SVG_ALIASES`, `THEMED_SVGS`) now use `Object.hasOwn()` own-property checks — a provider id such as `constructor` or `__proto__` previously resolved through the prototype chain instead of falling through to the unknown-provider CDN fallback (`getLobeProviderIcon()` itself was already guarded by [#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)); `ProviderPageHeader`'s `color` field is also now optional, matching the rest of the component's defensive typing (ported from [#11920](https://github.com/diegosouzapw/OmniRoute/pull/11920) — thanks @HouMinXi). ([#11935](https://github.com/diegosouzapw/OmniRoute/pull/11935)) — thanks @NoxzRCW +- fix(ollama): preserve multi-byte UTF-8 content split across stream chunks in the Ollama NDJSON transform, which previously corrupted CJK/emoji into U+FFFD (#11921) — thanks @pacocartones +- **fix(providers):** OrcaRouter chat requests now target `/v1/chat/completions` instead of the bare `/v1` API root, fixing the upstream `404 Invalid URL (POST /v1)` ([#11923](https://github.com/diegosouzapw/OmniRoute/pull/11923)). — thanks @echel0nn +- **fix(plugins):** deliver the `onStreamComplete` event to disk-installed plugins. The event shipped in v3.8.50 (#9669) was emitted internally but had no plugin-facing wiring, so no plugin could ever subscribe: the manifest schema silently dropped `hooks.onStreamComplete`, and the loader/manager only knew the seven legacy hooks. `onStreamComplete` is now a declarable manifest hook, wired through the loader and registered by the manager like the other hooks, and its payload carries a `requestId` so consumers can correlate the stream-completion event with the originating request ([#11934](https://github.com/diegosouzapw/OmniRoute/pull/11934)) — thanks @amaleta / @pacocartones +- **fix(db):** the Qdrant embedding-model dropdown now lists local/self-hosted providers (Ollama, LM Studio, vLLM, etc.) — an active connection is treated as "configured" when the provider allows an optional API key, not only when it has a real key or OAuth, so a running local embedding provider is no longer hidden from the picker ([#11949](https://github.com/diegosouzapw/OmniRoute/issues/11949)) ([#11995](https://github.com/diegosouzapw/OmniRoute/pull/11995)) +- **fix(providers):** Vertex AI Anthropic partner-model discovery now calls the Model Garden `v1beta1` publisher list (`/v1beta1/publishers/anthropic/models`, global) and parses its `publisherModels` envelope, so Claude models auto-synced from Vertex populate the active live catalog and route at request time instead of returning `Model '' is not available in the active live catalog` ([#11991](https://github.com/diegosouzapw/OmniRoute/issues/11991)) — thanks @fabioluissilva ([#11998](https://github.com/diegosouzapw/OmniRoute/pull/11998)) +- **fix(cli):** support `--base-url` alongside `--endpoint` in `omniroute nodes add`, `update`, and `validate` subcommands to prevent global `--base-url` shadowing issues ([#11999](https://github.com/diegosouzapw/OmniRoute/issues/11999)). ([#12033](https://github.com/diegosouzapw/OmniRoute/pull/12033)) — thanks @HouMinXi +- **fix(providers):** `cloudflare-ai` no longer refuses image content parts for every Workers AI model ([#12002](https://github.com/diegosouzapw/OmniRoute/pull/12002)) — the plain-string `content` requirement behind #2539 is carried by the _model_ schema, not by the `/ai/v1/chat/completions` endpoint (measured: an all-text part array returns 200 on `@cf/mistralai/mistral-small-3.1-24b-instruct`, `@cf/meta/llama-4-scout-17b-16e-instruct` and `@cf/meta/llama-3.3-70b-instruct-fp8-fast`, and 400 on the text-only `@cf/qwen/qwen2.5-coder-32b-instruct`). `transformRequest()` flattened every array and threw on the first non-text part (#6390), so image input was refused for vision-capable Cloudflare models that accept it. All-text arrays are still flattened — the one shape every model accepts — while an array carrying a non-text part is passed through untouched, so the attachment is still never silently dropped. Regression guards: `tests/unit/cloudflare-ai-image-parts-6390.test.ts`. — thanks @davidlinfr +- **fix(resilience):** decouple the limiter-managed execution backstop from the queue-wait budget — new `requestQueue.executionMaxWaitMs` (env `RATE_LIMIT_EXECUTION_MAX_WAIT_MS`, default 600000 = 10 min) now feeds Bottleneck's post-dispatch `expiration`, while `requestQueue.maxWaitMs` keeps its documented queue-wait semantics. Previously the queue-wait budget doubled as the execution expiration, so legitimate long-running calls on non-incremental gateways (whole generation buffered before the first upstream byte, e.g. Console Go / Command Code tiers serving GLM models) were killed mid-flight at the queue budget with a false 504 `RATE_LIMIT_EXECUTION_TIMEOUT` — the local limiter undercut the provider-aware upstream fetch-start timeouts. The surfaced 504 message now names `requestQueue.executionMaxWaitMs`; the error keeps the #4165 guarantees (disclaims an upstream timeout, preserves the Bottleneck error as `cause`, branded code + trusted provenance, classified request-scoped so combo falls back). A real queue-wait bound (the `Promise.race` around `limiter.schedule()` sketched in #9533) remains future work. (#12025) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **Call logs:** keep the `error` field when an artifact exceeds the storage cap, instead of replacing it with the omission marker. The error is the only field that says *why* a request failed and is typically ~90 bytes next to the multi-hundred-KB bodies that trip the cap, so dropping it left a size-limited row undiagnosable — a provider outage, a local timeout and an upstream 400 all rendered identically. It is now preserved at every fallback stage, truncated to 4KB if it is itself large ([#12026](https://github.com/diegosouzapw/OmniRoute/issues/12026)). ([#12095](https://github.com/diegosouzapw/OmniRoute/pull/12095)) — thanks @ntdat812 +- **fix(diagnostics):** preserve the error field (truncated to 4KB with a `[truncated: …]` suffix) in every call-log artifact size-limit fallback stage. Previously the minimal fallback replaced the error with `[omitted: call log artifact size limit exceeded]`, so an oversized artifact row showed nothing about WHY the request failed — e.g. 91 of 847 opencode-go 504 rows on one production instance were undiagnosable from the dashboard. Oversized request/response bodies are still omitted exactly as before; the error cap is independent of the payload sizes that tripped the fallback. (#12026) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **fix(sse):** OpenAI Responses clients that declare the native `web_search` tool now receive a spec-shaped `web_search_call` output item with `action.sources` alongside the preserved function-call round-trip, so search results executed through OmniRoute's own search backend are consumable by standard Responses clients (Codex, pi-web-access, …). ([#12031](https://github.com/diegosouzapw/OmniRoute/pull/12031)) — thanks @watchingdogs +- **fix(cli):** use in-thread alias resolver hooks on modern runtimes to avoid deprecation noise and improve Node.js forward compatibility ([#12073](https://github.com/diegosouzapw/OmniRoute/issues/12073)). ([#12083](https://github.com/diegosouzapw/OmniRoute/pull/12083)) — thanks @xiaoyaner0201 +- **fix(combo):** an operator-set **Agent Features → Context length** on a combo is now honored at request time. The value was persisted and advertised through `/v1/models`, but `resolveComboContextLimit()` never consulted it — so a multi-target combo whose members carry no per-model window fell through to the provider's generic `defaultContextLength` (openrouter 128000, command-code 200000) and rejected large requests with `Input exceeds context window … limit 128000` despite the combo being explicitly sized much larger. An identical single-target combo worked, because it collapses to its concrete target before the guard runs. Invalid values (0/negative/NaN/Infinity) are ignored, so the existing target → combo-min → fallback order is unchanged. ([#12090](https://github.com/diegosouzapw/OmniRoute/pull/12090)) — thanks @adivekar-utexas +- **fix(sse):** passthrough streams now estimate usage on finish when upstream closes without usage even with `stream_options.include_usage` — avoids `0 tokens / 0%` for providers that stay silent (and correctly handles trailing empty-choices usage) ([#12151](https://github.com/diegosouzapw/OmniRoute/pull/12151)) — thanks @maxmad64bis +- **fix(combos):** clearing an agent feature in the combos editor now persists — unchecking context cache protection, or emptying the system message or tool filter, sends an explicit `null` instead of dropping the field from the `PUT` body, which the update merge read as "leave unchanged" ([#12177](https://github.com/diegosouzapw/OmniRoute/pull/12177)) — thanks @foreveryh / @ntdatt812 +- **fix(memory):** self-hosted embedding endpoints now vectorize — the vector width is measured from the first embedding that comes back instead of being read from a registry that cannot describe them, so `vec_memories` is created and memories stop piling up unvectorized behind a green health check ([#12180](https://github.com/diegosouzapw/OmniRoute/pull/12180)) — thanks @kanade-hoshino / @ntdatt812 +- **fix(translator):** the leading `system` message now reaches Responses-API upstreams when its `content` is a content-part array — it was read as `typeof content === "string" ? content : ""`, so a prompt-caching client (Anthropic `cache_control`, the shape LiteLLM and the Anthropic SDK emit) had its entire system prompt replaced by an empty `instructions`. The request was still accepted with a normal `prompt_tokens` count, so the model answered with no instructions and nothing in the response said they were missing. Mid-conversation system turns already handled the array shape ([#7056](https://github.com/diegosouzapw/OmniRoute/pull/7056)); only the first one did not ([#12206](https://github.com/diegosouzapw/OmniRoute/issues/12206)). Regression guard: `tests/unit/translator-openai-responses-system-content-parts.test.ts`. ([#12207](https://github.com/diegosouzapw/OmniRoute/pull/12207)) — thanks @Gorillaz322 +- **fix(free-tier):** `/api/free-tier/summary` no longer computes its totals from a Radar feed built before the catalog the running release ships. When the cached feed is older — or carries no build date at all — the route answers from the shipped catalog, resolved through the operator's local model state so disabled and tombstoned models stay out of the numbers ([#12215](https://github.com/diegosouzapw/OmniRoute/pull/12215)). — thanks @maxmad64bis +- **fix(oauth):** Keep a Claude personal workspace and a Team organization as separate connections — they share the same email and `accountUUID`, so the email-only OAuth dedup let the second login overwrite the first account's tokens; `organizationUUID` now disambiguates them, the way `workspaceId` does for Codex ([#12222](https://github.com/diegosouzapw/OmniRoute/pull/12222)) — thanks @davidebaraldo +- **fix(resilience):** a 402 on a single paid model of a passthrough/gateway provider (e.g. `kilo-gateway`, `ollama-cloud`) no longer terminalizes the whole connection with a never-auto-recovered `credits_exhausted` status — only the paid model is locked out, so free models on the same key keep serving. 402 variant of [#3027](https://github.com/diegosouzapw/OmniRoute/issues/3027). Single-credential providers are unaffected — a 402 there is still treated as the key being genuinely out of credit ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239) / [#10616](https://github.com/diegosouzapw/OmniRoute/issues/10616)) ([#12242](https://github.com/diegosouzapw/OmniRoute/issues/12242)) — thanks @brick30llc-ctrl ([#12266](https://github.com/diegosouzapw/OmniRoute/pull/12266)) — thanks @geekyNads +- **fix(sse):** trust `finish_reason: "length"`/`"max_tokens"` over the reasoning-consumed-token ratio in response quality validation, so a reasoning model truncated below the old 90% threshold correctly fails and retries instead of returning empty content as a silent "success" ([#12262](https://github.com/diegosouzapw/OmniRoute/pull/12262)) — thanks @brick30llc-ctrl +- **fix(settings):** `PUT /api/settings/cache-config` now persists `alwaysPreserveClientCache` to the flat general settings the runtime cache-control policy actually reads; previously the value landed in the databaseSettings "cache" section and was silently ignored, so the endpoint had no effect on `cache_control` passthrough ([#12304](https://github.com/diegosouzapw/OmniRoute/pull/12304)) — thanks @davidebaraldo +- **fix(grok-cli):** treat omitted SuperGrokPro `creditUsagePercent` as 0% used so Provider Limits still renders a weekly bar (proto3 zero-elision) ([#12312](https://github.com/diegosouzapw/OmniRoute/pull/12312)) — thanks @HouMinXi +- **fix(combo):** Expose the two Auto-Combo scoring factors nobody could set — the combo validation schema and the dashboard weight sliders both declared 13 of the scorer's 15 factors, so `connectionDensity` (spreads load across a provider's connections) and `quality` were dropped on save and offered nowhere. The sliders also shipped their own default table that differed from the engine's on every non-zero factor and summed to 1.05, so the percentages shown next to them added up to 105%. Both lists now match `DEFAULT_WEIGHTS`, and a test keeps them there. Note that a combo whose stored `weights` omitted the two keys was effectively running with them at zero and the other thirteen renormalized upward; it now runs with the engine's intended distribution, so its routing does shift ([#12314](https://github.com/diegosouzapw/OmniRoute/pull/12314)) — thanks @maxmad64bis +- **fix(docs):** The free-tier reference no longer says its numbers come "confidence tagged per row" — no catalog entry carries a confidence tag and the API serves none, so every figure on that page is an estimate of the same, unstated quality. The page now states what an entry does vouch for: an independently documented hard stop (set by hand with the source in a comment, never defaulted to `true`) and a prompt-training disclosure, both with live counts the `check:docs-counts` gate keeps honest ([#12318](https://github.com/diegosouzapw/OmniRoute/pull/12318)) — thanks @maxmad64bis +- **fix(usage):** `adobe-firefly` and `firefly` have had a working usage fetcher since Adobe Firefly landed, but neither was ever added to the registration list, so the provider-plugin manifest, `genericQuotaFetcher` and the free-access quota cache all reported them as having no usage support — while `USAGE_SUPPORTED_PROVIDERS` said the opposite. Both are now declared, which also means their credit balance is fetched like any other declared provider's: `registerGenericQuotaFetchers` now registers a generic quota fetcher for them, and `resolveFreeAccessState` no longer returns early. A test holds the registration list to the dispatcher's switch in both directions, which is what the module's own docstring already asked for in prose ([#12321](https://github.com/diegosouzapw/OmniRoute/pull/12321)) — thanks @maxmad64bis +- **fix(quota):** drop the generic quota cache (agy / Antigravity / Claude OAuth) on an upstream 429 so reset-aware scoring does not keep a 60s stale snapshot, and force-refresh the next usage fetch so inner provider caches cannot recache the same window ([#12325](https://github.com/diegosouzapw/OmniRoute/pull/12325)) — thanks @HouMinXi +- **fix(combos):** deleting a combo now clears its persisted LKGP pins instead of leaving unreachable `key_value` rows behind ([#12326](https://github.com/diegosouzapw/OmniRoute/issues/12326)) ([#12425](https://github.com/diegosouzapw/OmniRoute/pull/12425)) — thanks @KrzysiekSko +- **fix(sse):** Keep ZWNJ (U+200C) and ZWJ (U+200D) in assistant text, reasoning and tool-call arguments — Persian/Kurdish half-space (`ارائه‌دهنده`), Arabic/Indic shaping and emoji sequences no longer lose them; the response de-obfuscation now removes joiners only between ASCII word characters, where the request side inserts them ([#12186](https://github.com/diegosouzapw/OmniRoute/issues/12186)) — thanks @rezjalibd ([#12359](https://github.com/diegosouzapw/OmniRoute/pull/12359)) — thanks @pacocartones +- **fix(resilience):** count resolved upstream 5xx results against the provider circuit breaker on the chat path — `CircuitBreaker.execute()` no longer reads a resolved `{ success: false, status: 5xx }` as a success that cancels the call-site failure, so a provider answering 503s now trips its breaker instead of staying `CLOSED` at `failureCount: 1`; single-model and combo dispatches are each accounted exactly once ([#12254](https://github.com/diegosouzapw/OmniRoute/issues/12254)) ([#12360](https://github.com/diegosouzapw/OmniRoute/pull/12360)) — thanks @pacocartones +- **fix(providers):** resolve the Codex quota auto-ping model from the live provider catalog and lifecycle registry instead of the retired `gpt-5.1-codex-mini`, and pause the ping with one actionable warning when no selectable Codex model exists rather than retrying a shut-down id every cooldown window ([#11905](https://github.com/diegosouzapw/OmniRoute/issues/11905)) ([#12361](https://github.com/diegosouzapw/OmniRoute/pull/12361)) — thanks @pacocartones +- **fix(api):** keep the `{created, data}` wrapper on combo-routed `/v1/images/generations` responses and default Codex image results to `b64_json` on both `/v1/images/generations` and `/v1/images/edits` so Codex CLI's built-in `image_gen` can decode them ([#12268](https://github.com/diegosouzapw/OmniRoute/issues/12268)) ([#12362](https://github.com/diegosouzapw/OmniRoute/pull/12362)) — thanks @pacocartones +- **fix(sse):** Name the shadowed custom provider node when a built-in provider id/alias (e.g. `openference` → `of`) reserves the prefix of an existing OpenAI/Anthropic-compatible node, so the runtime `No active credentials for provider: ` error explains that the prefix routed to the built-in and never reached the node's healthy connections, instead of contradicting the dashboard ([#11943](https://github.com/diegosouzapw/OmniRoute/issues/11943)) — thanks @morpheus9393 ([#12365](https://github.com/diegosouzapw/OmniRoute/pull/12365)) — thanks @pacocartones +- **fix(i18n):** wrap `ccOnboardingKeyPlaceholder` in ICU single quotes across all 43 locales so angle brackets render literally instead of being parsed as rich-text tags, which crashed the Claude Code onboarding block with `INVALID_MESSAGE: INVALID_TAG` ([#12302](https://github.com/diegosouzapw/OmniRoute/issues/12302)) ([#12369](https://github.com/diegosouzapw/OmniRoute/pull/12369)) — thanks @gonisulaimann +- **fix(models):** publish `effort_tiers` on Kimi K3's synced base-model entries (`kmca/k3`, `kmca/k3-256k`) so catalog-only clients (OpenCode, plain SDK pickers) can see and select the reasoning tiers (`low`/`high`/`max`) the synced metadata already carried — the `isSkippedEffortProvider` gate no longer suppresses tier visibility on those base entries, while synthetic `-` variant generation stays prevented and Codex/GLM base models remain excluded unchanged ([#12299](https://github.com/diegosouzapw/OmniRoute/issues/12299)) ([#12371](https://github.com/diegosouzapw/OmniRoute/pull/12371)) — thanks @gonisulaimann +- **fix(guardrails):** keep `auto`/`auto/*` virtual combos exempt from the Vision Bridge `fixedModel` credential guard so a combo target is passed through instead of silently falling back to global auto-selection ([#12237](https://github.com/diegosouzapw/OmniRoute/issues/12237)) ([#12373](https://github.com/diegosouzapw/OmniRoute/pull/12373)) — thanks @pacocartones +- **fix(combo):** capability-filter exhaustion caused by `max_tokens` above every target's known output limit now reports that reason (requested `max_tokens` vs the pool's highest known ceiling) instead of the unrelated "supports structured output" message ([#12229](https://github.com/diegosouzapw/OmniRoute/issues/12229)) — thanks @DW-MediaLab ([#12374](https://github.com/diegosouzapw/OmniRoute/pull/12374)) — thanks @pacocartones +- **fix(auth):** the `least-used` account strategy now prefers accounts without backoff before falling back to oldest `lastUsedAt`, the same tie-break `round-robin` already applies, so a failover no longer lands on a just-rate-limited account for a single request ([#12279](https://github.com/diegosouzapw/OmniRoute/issues/12279)) — thanks @tenshiak ([#12375](https://github.com/diegosouzapw/OmniRoute/pull/12375)) — thanks @pacocartones +- **fix(docker):** the `chatgpt-web-codex-browser` image now finds the Chrome binary under `chrome-linux64/` (Chrome for Testing layout in `playwright:v1.62.0-noble`) as well as the legacy `chrome-linux/`, so the container no longer crash-loops with `exec: --headless=new: not found` ([#12024](https://github.com/diegosouzapw/OmniRoute/issues/12024)) ([#12376](https://github.com/diegosouzapw/OmniRoute/pull/12376)) — thanks @pacocartones +- **fix(providers):** declare `groq/compound` and `allam-2-7b` as non-reasoning models in the curated Groq registry so `reasoning_effort` / `output_config.effort` / `thinking` from Claude Code are stripped instead of forwarded, which Groq rejected with HTTP 400 ([#12134](https://github.com/diegosouzapw/OmniRoute/issues/12134)) ([#12379](https://github.com/diegosouzapw/OmniRoute/pull/12379)) — thanks @pacocartones +- **fix(executors):** `OpencodeExecutor` no longer forces a direct connection when the connection has a proxy assigned in Proxy Management but no per-account proxies: the single-account fast path used to wrap the upstream dispatch in the direct-egress sentinel, discarding the ambient proxy context the chat handler had pinned from `proxy_assignments`, so API-key `opencode`/`opencode-go` connections egressed from the host IP (and hit geoblocks) despite the assignment. The direct pin is now applied only when no ambient proxy context exists ([#11894](https://github.com/diegosouzapw/OmniRoute/issues/11894) — thanks @hizzt) ([#12380](https://github.com/diegosouzapw/OmniRoute/pull/12380)) — thanks @pacocartones +- **fix(api):** `GET /v1/models` with `MODELS_CATALOG_PREFIX_MODE=canonical` (or `?prefix=canonical`) now lists providers whose registry alias is undefined or equal to their own id (Antigravity, Antigravity CLI and other self-aliased built-ins) — their single `provider/model` id was dropped by the alias/canonical duplicate guard in the static, synced, custom and alias-backed catalog loops ([#12058](https://github.com/diegosouzapw/OmniRoute/issues/12058)) — thanks @cheynetom ([#12381](https://github.com/diegosouzapw/OmniRoute/pull/12381)) — thanks @pacocartones +- **fix(translator):** Drop replayed `thinking` blocks that carry no signature (the shape produced from cross-provider `reasoning_content`) instead of stamping the default Claude signature on them, which Anthropic rejected with `400 Invalid signature in thinking block` on the next turn served by an Anthropic rung ([#12105](https://github.com/diegosouzapw/OmniRoute/issues/12105)) — thanks @atescivitci-cmd ([#12386](https://github.com/diegosouzapw/OmniRoute/pull/12386)) — thanks @pacocartones +- **fix(cli):** Resolve Bun's `--preload` polyfill path against the package root instead of `dist/`, so `omniroute` installed with `bun install -g` no longer crashes at startup with `error: preload not found …/dist/open-sse/utils/setupPolyfill.ts` ([#11980](https://github.com/diegosouzapw/OmniRoute/issues/11980)) — thanks @joglomedia ([#12387](https://github.com/diegosouzapw/OmniRoute/pull/12387)) — thanks @pacocartones +- **fix(providers):** `gemini-business` now publishes its model catalog — `/v1/models` and `/v1/providers/gemini-business/models` list the 12 enterprise Gemini ids the executor understands instead of returning an empty list (#12107) ([#12389](https://github.com/diegosouzapw/OmniRoute/pull/12389)) — thanks @pacocartones +- **fix(db):** install `busy_timeout` before the SQLite connection's first statement so a process opening the database while another one closes its WAL connection waits out the transient EXCLUSIVE lock instead of dying with `database is locked`, and recognise the drivers' real BUSY/PROTOCOL/IOERR errors as transient in the corruption probe so the same lock no longer renames the database away as corrupt; deflakes `cross-process contenders never both acquire the same connection` (#12394 — thanks @pacocartones) +- **fix(chat-admission):** derive the `chat_admission_busy` 503 `Retry-After` from observed heavyweight-lease occupancy — the larger of the exhausted `OMNIROUTE_CHAT_ADMISSION_QUEUE_MS` window and the time since capacity last turned over, capped at 60 s — instead of a fixed 1 s (structural) / 2 s (byte-stage) hint that invited Codex/agent fan-out clients to re-send ~1 MiB `/v1/responses` bodies every second into a gate held for the whole SSE lifetime; an idle gate keeps the historical floors ([#12135](https://github.com/diegosouzapw/OmniRoute/issues/12135)) (#12395 — thanks @pacocartones) +- **fix(api-manager):** the API key permissions modal no longer silently drops `allowedCombos` entries its Combo picker cannot render — routing-rule names such as `rt-*`, which the backend already honours — when "All" is clicked and the key is switched back to "Restrict"; those entries now survive the toggle, are listed read-only under the combo list so the count and the list agree, and are saved back verbatim instead of persisting `[]` (deny-all) (#12397 — thanks @pacocartones) +- **fix(catalog):** write the NUL separator of the catalog connection memo key, the provider serviceKind memo key, the Video Bridge promotion group key and a JSON-exactness test fixture as the `\u0000` escape instead of a raw byte — same runtime value, but the raw byte made git, GitHub and ripgrep treat those files as binary (hidden PR diffs, silently skipped searches); a guard test now keeps raw NUL bytes out of `src/`, `open-sse/` and `tests/` (#12403 — thanks @pacocartones) +- **fix(providers):** add `CLAUDE_CODE_CLIENT_VERSION` and `GITHUB_COPILOT_CLI_VERSION` env overrides so Anthropic/Copilot client-version gates can be unblocked without a rebuild ([#12417](https://github.com/diegosouzapw/OmniRoute/issues/12417)) ([#12632](https://github.com/diegosouzapw/OmniRoute/pull/12632)) — thanks @HouMinXi +- **fix(db):** back-fill `last_ping_at` and `last_pinged_reset_key` on `provider_connections` during schema reconciliation so divergent lineages that skipped `123_quota_auto_ping` still accept quota auto-ping writes ([#12470](https://github.com/diegosouzapw/OmniRoute/pull/12470) — thanks @KooshaPari) +- **fix(ci):** document MIT exceptions for `@eloqnt/{config,format-json,format-po}` (next-intl transitive; locked tarballs omit `license`) and keep the A2A lifecycle vitest off the real SQLite persistence seam ([#12581](https://github.com/diegosouzapw/OmniRoute/issues/12581)) ([#12595](https://github.com/diegosouzapw/OmniRoute/pull/12595)) +- OpenCode plugin `/v1/models` catalog fetch now waits 30s by default and attaches HTTP `statusCode` on 401/5xx so host fallback plugins can hop instead of seeing an untyped AbortError/UnknownError. ([#12607](https://github.com/diegosouzapw/OmniRoute/pull/12607)) — thanks @RaviTharuma +- **CI:** the OpenAPI security-tier gate now mirrors `isAlwaysProtectedPath()` in full — it also reads `ALWAYS_PROTECTED_API_PATTERNS`, so the pattern-gated credential routes (`/api/providers/{id}/{claude,codex}-auth/{export,apply-local}`, GHSA-5926-2w35-7h4q) no longer report as unannotated. (#12605) +- **fix(api):** GET /v1/models no longer waits forever on a hung coalesced catalog rebuild; cold-path waits are bounded (`CATALOG_BUILD_TIMEOUT_MS`, default 8s) and a last-good 200 is served when the rebuild times out ([#12627](https://github.com/diegosouzapw/OmniRoute/issues/12627)). ([#12628](https://github.com/diegosouzapw/OmniRoute/pull/12628)) — thanks @RaviTharuma +- **fix(cursor):** a non-array `tool_calls` on an assistant message no longer crashes the cursor request translator with a `TypeError`; both loops now require an array ([#12689](https://github.com/diegosouzapw/OmniRoute/issues/12689)) ([#12691](https://github.com/diegosouzapw/OmniRoute/pull/12691)) — thanks @soroush5 +- **fix(chat):** Chat Completions no longer return empty `content` after a server-owned memory or skills tool; the first provider send and account/model recovery now share one pipeline so a follow-up round-trip can fill the reply ([#12696](https://github.com/diegosouzapw/OmniRoute/issues/12696)) — thanks @HouMinXi ([#12867](https://github.com/diegosouzapw/OmniRoute/pull/12867)) +- **fix(dashboard):** batch-deleting provider keys no longer toasts failure after a successful delete when the confirm button's click event is forwarded as `onAfter` ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) — thanks @HouMinXi +- **fix(glm):** drop the extra 16th argument to `createSSETransformStreamWithLogger` that TypeScript rejected (TS2554) and that never reached the TransformStream ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) ([#12770](https://github.com/diegosouzapw/OmniRoute/pull/12770)) — thanks @HouMinXi +- **fix(dashboard):** pass `nodeMap` into Runtime `QuotaGroup` so a quota monitor in error/exhausted/alerting no longer throws `ReferenceError: nodeMap is not defined`. ([#12868](https://github.com/diegosouzapw/OmniRoute/pull/12868)) — thanks @HouMinXi +- Restricted API keys whose `allowedModels` lists a combo name no longer skip every combo member at pre-dispatch (`ALL_TARGETS_SKIPPED`). Inner-target filtering still applies when the allow-list is a provider prefix or `disableNonPublicModels` is on ([#12899](https://github.com/diegosouzapw/OmniRoute/pull/12899)). — thanks @HouMinXi +- **fix(combos):** Effort-suffixed combo members inherit the base model's `model_context_overrides` row so priority order is not inverted on large requests ([#12926](https://github.com/diegosouzapw/OmniRoute/pull/12926)) — thanks @HouMinXi +- **fix(catalog):** Picker-added `customModels` enter the dispatch-time live catalog so combo members and bare inference no longer 400 ([#12934](https://github.com/diegosouzapw/OmniRoute/pull/12934)) — thanks @HouMinXi +- **fix(docker):** re-derive the Docker build's worker-pool memory budget from the MEASURED ~4.5 GB per-process RSS (the issue owner's own VPS dmesg OOM-killer reproduction) instead of the stale 2560 MB/worker inference #11419 shipped, and lower `OMNIROUTE_BUILD_WORKERS` 3 → 2 so 1 parent + 1 page-data worker (2 processes × 4.5 GB = 9 GB) fits the 12.288 GB (75%) budget on a 16 GB GitHub Actions runner — the previous default (1 parent + 2 workers = 13.5 GB) still overcommitted the runner and kept "Publish to Docker Hub" failing with `cannot allocate memory` after #11419 merged (#7518). ([#11663](https://github.com/diegosouzapw/OmniRoute/pull/11663)) +- **fix(electron):** the packaged Windows desktop build now passes the #7592 cold-restart smoke end to end. Five defects were found and fixed on the way: (1) optional-pack staging failed on any machine whose `tar` is GNU tar — it reads the drive letter in an absolute `-f C:\...` archive path as a remote rsh host (`Cannot connect to C:`), so staging now passes a bare filename with `cwd` at the tarball directory and surfaces tar stderr on failure; (2) the Electron `files` allowlist was missing `lib/loginHeaderCapture.js`, so the packaged main process crashed at startup with `Cannot find module './lib/loginHeaderCapture'`; (3) electron-builder ≥26 injects an `!**/node_modules/**` ignore into every extraResources pattern list that no positive filter can override, which silently dropped the staged runtime `node_modules` (including the better-sqlite3 N-API prebuild) from `resources/app` — a fresh v3.8.50 desktop build would have shipped with no native SQLite driver and reproduced the #7592 sql.js fallback on every machine; an `afterPack` hook now restores the staged `node_modules` after packing; (4) the packaged-app smoke harness redirected `USERPROFILE` into its temp DATA_DIR, but Electron resolves the Roaming profile from `%USERPROFILE%\AppData\Roaming\` (USERPROFILE takes precedence over the APPDATA env var) and the path service throws instead of creating it, so `requestSingleInstanceLock()` returned false and the app exited(0) silently before `app.whenReady()` — the harness now pre-creates the derived tree, and `ensureSmokeEnvDirs` is exported and unit-tested; (5) the #7592 driver assertion parsed a `[DB] Driver: ...` line that the server's primary DB path never printed (only the unused `openDatabaseAsync()` did) — `getDbInstance()` now emits the same line on open so the guard can actually assert the native driver. Also: the smoke env-allowlist unit test hardcoded POSIX paths and could only pass on Linux/macOS; expectations are now host-agnostic, with new regression tests for the USERPROFILE-derived tree and `tarPack` under absolute Windows-style paths ([#11443](https://github.com/diegosouzapw/OmniRoute/pull/11443)) — thanks @jonlwheat2-gif +- fix(sse): stop the auto-combo candidates inspector from silently dropping model-locked/cooled-down rows (#9133) ([#11994](https://github.com/diegosouzapw/OmniRoute/pull/11994)) — thanks @hartmark +- **fix(providers):** Perplexity Web no longer turns upstream stream failures into successful assistant text; pre-content failures remain eligible for fallback, partial output ends with a structured sanitized error, and failed sessions are not persisted ([#12465](https://github.com/diegosouzapw/OmniRoute/pull/12465)) +- **fix(video-bridge):** preserve chronological video and transcript fusion, source-frame timestamps, transcript provenance, full contact-sheet intervals, and pre-abort callback isolation ([#11681](https://github.com/diegosouzapw/OmniRoute/pull/11681)) +- **Z.ai Web:** HTTP 200 streams carrying an upstream error now terminate with a structured failure instead of assistant text plus a normal stop, preserving partial output while allowing pre-content combo fallback. ([#12454](https://github.com/diegosouzapw/OmniRoute/pull/12454)) +- Absorb `Error [AbortError]: request_signal_aborted` and DOMException AbortError shapes in the process-level client-abort crash guard so routine client disconnects no longer kill the server (exit code 7). ([#12165](https://github.com/diegosouzapw/OmniRoute/pull/12165)) — thanks @alvinveroy +- **fix(sse):** Treat Adapta Web `type:error` SSE events as sanitized non-stream failures instead of empty HTTP 200 completions. ([#12459](https://github.com/diegosouzapw/OmniRoute/pull/12459)) +- **fix(autoCombo):** layer 3 (`models_dev_tier`) no longer scores retired models as premium, merge cross-provider capability rows deterministically, and inherit tier scores through variant ids ([#11508](https://github.com/diegosouzapw/OmniRoute/issues/11508)). `getModelsDevTierFitness` now vetoes ids present in `config/quality/model-lifecycle.json` with status `retired` before any other signal; `loadModelCapabilities()` aggregates per `model_id` (any-non-null-true for booleans, max for `limit_context`) instead of last-write-wins over SQLite's undefined row order; a capabilities miss retries through `resolveScoresAs()`'s base id and reports `models_dev_tier:inherited`, matching layers 1–2 (#11492). Adds `getModelsDevTierFitnessWithSource()` and an `invalidateCapabilitiesCache()` test/ops hook. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) — thanks @Meet6338-X +- fix(sse): accept Claude `content:[]` + `stop_reason: max_tokens`/`tool_use` in `detectMalformedNonStream` (match `isEmptyContentResponse`) so Claude Code `/model` probes with `max_tokens: 1` no longer become a false 502 ([#11568](https://github.com/diegosouzapw/OmniRoute/pull/11568)) — thanks @Seramicx +- **fix(security):** sanitize `request.failed` diagnostics before publishing them to live dashboard listeners and replay history, while keeping status, model, provider, latency, and internal call-log diagnostics intact. ([#12469](https://github.com/diegosouzapw/OmniRoute/pull/12469)) +- **fix(pwa):** do not intercept dashboard navigations so Chrome can fall back from a stale HTTP/3 Alt-Svc advertisement (UDP :20128 is unpublished; F5 on a long-lived tab hung until a new tab opened a fresh TCP connection). ([#12767](https://github.com/diegosouzapw/OmniRoute/pull/12767)) — thanks @HouMinXi +- **fix(executors):** handle DuckDuckGo ERR_BN_LIMIT (418) without retrying — when the upstream returns `418 ERR_BN_LIMIT` (rate-limit/ban), the executor now returns the error immediately instead of burning another VQD acquisition that would only count against the IP limit. The retry logic for `418 ERR_CHALLENGE` (unsolved challenge) remains unchanged. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) ([#12110](https://github.com/diegosouzapw/OmniRoute/pull/12110)) — thanks @oyi77 / @Meet6338-X +- Fixed DuckDuckGo streaming responses losing JSON lines and UTF-8 characters split across network chunks. ([#11528](https://github.com/diegosouzapw/OmniRoute/pull/11528)) — thanks @pacocartones +- Harden generated Cloudflare, Vercel, and Deno relays by stripping hop-by-hop, framing, and proxy authentication headers before forwarding requests upstream. ([#11533](https://github.com/diegosouzapw/OmniRoute/pull/11533)) — thanks @pacocartones +- **feat(grok-cli):** Provider Limits shows grok-cli banked reset credits from `GetRemainingResets` (including a real zero; a failed RPC omits the row) and the existing View credits button now calls `ConsumerUiSvc/RedeemReset` for grok-cli. Live tokens use nested fields 10/20/30 (id + Timestamp), not compact 1/2/3. ([#12805](https://github.com/diegosouzapw/OmniRoute/pull/12805)) — thanks @HouMinXi +- **fix(memory):** Embedding Model Quick select, Embedding Source remote dropdown, and Rerank selector now list every configured provider with embedding/rerank support instead of only chat-catalog text matches plus OpenRouter live discovery; a generic OpenAI-compatible `/embeddings` + Cohere-compatible `/rerank` runtime fallback resolves any configured chat provider's embedding/rerank endpoint, so unlisted providers no longer fail with "Unknown embedding provider"; both memory selectors gained a free-text model override ([#11390](https://github.com/diegosouzapw/OmniRoute/pull/11390)) — thanks @rqzbeh +- Harden SQLite upgrades around the historical migration-074 version collision: missing discovery and inspector tables are replayed atomically, pre-existing databases (including setup-created skeletons) receive reusable content-addressed safety snapshots, and Node test/eval probes without `DATA_DIR` are isolated from the operator database. ([#12435](https://github.com/diegosouzapw/OmniRoute/pull/12435)) +- **fix(dashboard):** Moonshot/Kimi Open Platform voucher and cash leftover percentages follow the bucket balance, so an empty wallet no longer paints those rows as 100% while Available is 0% ([#12733](https://github.com/diegosouzapw/OmniRoute/pull/12733)) — thanks @HouMinXi +- **fix(combo):** return non-retryable HTTP 400 when all candidates for a pinned native Codex turn are unavailable due to model-scoped lockout, terminating the turn cleanly while preserving turn continuity and enabling standard Combo routing on subsequent turns ([#12240](https://github.com/diegosouzapw/OmniRoute/pull/12240)) — thanks @mdigitalbh81 +- Fixed auto-router strategies preserving the selected connection through ranking and dispatch when multiple connections share the same provider and model. ([#11530](https://github.com/diegosouzapw/OmniRoute/pull/11530)) — thanks @pacocartones +- Preserve Chat Completions JSON-object response formats when translating requests to the Responses API. ([#11529](https://github.com/diegosouzapw/OmniRoute/pull/11529)) — thanks @pacocartones +- Fix idempotency fingerprints so Responses API requests include `input` and other semantic generation fields using stable serialization, while excluding credentials and request metadata. ([#11532](https://github.com/diegosouzapw/OmniRoute/pull/11532)) — thanks @pacocartones +- **fix(api):** Generated API CLI commands now enforce required OpenAPI request bodies; Combo test commands forward the required `comboName` body, while API keys created by older writers after migration 149 preserve legacy allow-all Combo access without widening explicit empty allowlists — thanks @marcelokarval ([#12070](https://github.com/diegosouzapw/OmniRoute/pull/12070)) +- **fix(grok-web):** treat upstream streaming failures as failures instead of successful assistant text: error-only streams now fail readiness with HTTP 502, while failures after legitimate content preserve that partial output and terminate through the sanitized stream failure path without a normal `stop` completion. ([#12458](https://github.com/diegosouzapw/OmniRoute/pull/12458)) +- HuggingChat now turns HTTP 200 JSONL generation failures into a sanitized 502 before content, or a fixed public stream failure after partial output, so fallback and request persistence no longer record a false successful stop. ([#12456](https://github.com/diegosouzapw/OmniRoute/pull/12456)) +- **fix(providers):** keep 1min.ai HTTP 200 stream errors out of assistant content, preserve partial output, and expose sanitized terminal errors so pre-content failures can fall back. ([#12466](https://github.com/diegosouzapw/OmniRoute/pull/12466)) +- **fix(sessions):** preserve legacy Sessions data when the additive exclusive-session projection is unavailable, returning an empty projection and warning only once per contiguous outage instead of failing the endpoint; the Sessions badge intentionally reflects the merged legacy and exclusive row count introduced by [#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389) ([#11469](https://github.com/diegosouzapw/OmniRoute/pull/11469)) — thanks @KaspaPulse +- Render Nimble Search with the generic provider icon and serve Opper's proven logo locally. ([#12415](https://github.com/diegosouzapw/OmniRoute/pull/12415)) +- **fix(quality):** `config/quality/provider-assets-provenance.jsonl` was stale against the physical `public/providers/` tree (still listing 225 assets and 5 duplicate-content groups after several provider-retirement PRs removed 85 icon files), which would have made `check:provider-asset-provenance` fail on the next full run. Reconciled the manifest to the real 142-file snapshot (142 asset records, 1 duplicate-content group, 2 newly-registered files marked `unresolved` pending provenance research), repinned `auditedCommit`, and updated the regression test's hardcoded counts to match. ([#11876](https://github.com/diegosouzapw/OmniRoute/pull/11876)) +- fix(ui): wrap quota card footer action buttons to prevent clipping Refresh button on narrow cards — when extra action buttons such as `View credits` are present on Codex cards, allow the footer row to flex-wrap with `shrink-0` buttons so `Refresh now` remains visible and clickable across all responsive card widths ([#11464](https://github.com/diegosouzapw/OmniRoute/pull/11464)) — thanks @adevwithpurpose +- Keep Antigravity Gemini usable when the same connection's Claude weekly quota is empty; generic quota cache stays per-connection for every other provider. ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) — thanks @HouMinXi +- Fixed Tailscale tunnel setup to reject ports outside the valid 1-65535 range and safely install Windows MSI packages from paths containing apostrophes. ([#11534](https://github.com/diegosouzapw/OmniRoute/pull/11534)) — thanks @pacocartones +- Classify Cloudflared and Tailscale process-management routes as local-only and spawn-capable while preserving remote access to read-only tunnel status endpoints. ([#11531](https://github.com/diegosouzapw/OmniRoute/pull/11531)) — thanks @pacocartones +- Electron release workflow: the `publish-npm` job now grants `actions: read` to the reusable `npm-publish.yml` it calls (its `publish` job requests it), which is what made GitHub refuse the whole v3.8.50 run at startup and ship the release with zero desktop assets; a `workflow_dispatch` now builds the requested tag instead of the dispatching branch and can skip the npm leg (`publish_npm=false`) when only re-attaching assets ([#11974](https://github.com/diegosouzapw/OmniRoute/pull/11974)) +- **fix(dashboard):** The Combos page usage guide now reads its dismissal through `useSyncExternalStore` instead of correcting SSR state inside an effect, removing an extra commit of the page tree on every load (and the `react-hooks/set-state-in-effect` error it raised). ([#12671](https://github.com/diegosouzapw/OmniRoute/pull/12671)) +- Electron release: `electron/package-lock.json` regained the optional `electron-builder-squirrel-windows` subtree (13 entries) that `npm ci` had been refusing as out of sync, `electron-release.yml` gained a `build_ref` dispatch input and stops regenerating release notes on a re-attach dispatch, and the npm publish workflow attaches the SBOM to the GitHub Release on dispatch publishes too — so the v3.8.51 tag ships every desktop asset and the SBOM like v3.8.49 did ([#12022](https://github.com/diegosouzapw/OmniRoute/pull/12022)) +- Drained the reds every PR against `release/v3.8.51` was born with: documented the three service-worker build-id variables, registered the six retirement/tunnel tests with the mutation gate, approved `eslint-plugin-react-hooks` in the dependency allowlist, added the six `combo.sort.*` strings to `vi` and `pt-BR`, pointed the ChatGPT Web doc at the real migration-168 test, worded the g4f hint around the member key, and realigned four tests to the retired-provider catalog and the legacy-schema fixtures the retirement migrations touch. ([#11940](https://github.com/diegosouzapw/OmniRoute/pull/11940)) +- **fix(providers):** Claude, Grok, LMArena, Notion, and Perplexity web-cookie transports now use pooled `wreq-js` 3.2 instead of the native sidecar, with all nine supported bindings pinned and audited, and the applicable platform binding plus native-license evidence included in each release artifact ([#12429](https://github.com/diegosouzapw/OmniRoute/pull/12429), supersedes [#11753](https://github.com/diegosouzapw/OmniRoute/pull/11753)). +- **fix(providers):** Zed Hosted streaming failures now trigger fallback before content and end partial streams with a sanitized structured error instead of fake assistant text and a normal-success stop. ([#12455](https://github.com/diegosouzapw/OmniRoute/pull/12455)) +- **fix(test):** capture hook result after render (direct commit `b85d0abb0b`) +- **fix(sync):** repair the two regressions the v3.8.50 sync-back left on release/v3.8.51 (direct commit `5ade9e0851`) +- **revert(models):** drop the memo reset hook ported from #10055 (direct commit `282de841ae`) +- **fix(typecheck):** guard the optional connectionId before the reactive model sync (direct commit `f2825bad75`) +- **fix(quota-share):** release the winner's reserved in-flight slot (#11371) ([#11408](https://github.com/diegosouzapw/OmniRoute/pull/11408)) — thanks @oyi77 +- **fix(providers):** stop silently dropping reasoning effort on opencode families (#10788) ([#11409](https://github.com/diegosouzapw/OmniRoute/pull/11409)) — thanks @oyi77 +- **fix(combo):** enable genuine quota-aware routing for generic providers (antigravity, claude, etc.) ([#11411](https://github.com/diegosouzapw/OmniRoute/pull/11411)) — thanks @HouMinXi +- **fix(oauth):** allow expired token retry before deactivation and activate expired retry loop (#P0) ([#11414](https://github.com/diegosouzapw/OmniRoute/pull/11414)) — thanks @HouMinXi +- **perf(executors):** lazy-load the executor registry — defer class imports + construction to first use (#11220) ([#11421](https://github.com/diegosouzapw/OmniRoute/pull/11421)) — thanks @oyi77 +- **fix:** make release/v3.8.50 CI gates green (run 32786966560 root causes) ([#11450](https://github.com/diegosouzapw/OmniRoute/pull/11450)) — thanks @jonlwheat2-gif +- **fix(sse):** exact-domain cookie match and origin-equality URL assertions ([#11452](https://github.com/diegosouzapw/OmniRoute/pull/11452)) — thanks @hartmark +- **fix(providers):** send Responses API payload during OpenAI-compatible validation ([#11454](https://github.com/diegosouzapw/OmniRoute/pull/11454)) — thanks @tuandinh0801 +- **fix(command-code):** fallback to /alpha/generate for Go plan without Provider API access ([#11455](https://github.com/diegosouzapw/OmniRoute/pull/11455)) — thanks @Chewji9875 +- **fix(responses-continuation):** fail closed on a log-truncated stored input/output array ([#11473](https://github.com/diegosouzapw/OmniRoute/pull/11473)) — thanks @hartmark +- **fix(cli):** write jcode settings as [providers.omniroute] in config.toml ([#11484](https://github.com/diegosouzapw/OmniRoute/pull/11484)) — thanks @hongnoul +- **fix(logging):** raise chat-log array truncation cap to 1000 and unify duplicate implementations ([#11499](https://github.com/diegosouzapw/OmniRoute/pull/11499)) — thanks @hartmark +- **fix:** restore live websocket URL helpers ([#11509](https://github.com/diegosouzapw/OmniRoute/pull/11509)) — thanks @PauloFH +- **perf(core):** eliminate async waterfalls across home, cache, and db operations (#11396) ([#11512](https://github.com/diegosouzapw/OmniRoute/pull/11512)) — thanks @HouMinXi +- **fix(kiro):** add runtime.us-east-1.kiro.dev as first-attempt endpoint ([#11517](https://github.com/diegosouzapw/OmniRoute/pull/11517)) — thanks @rifqiawl +- **fix(providers):** remove duplicate 128-tool truncation in OpencodeExecutor (#11444) ([#11518](https://github.com/diegosouzapw/OmniRoute/pull/11518)) — thanks @HouMinXi +- **fix(providers):** poll onboardUser LRO before classifying BYOP ([#11519](https://github.com/diegosouzapw/OmniRoute/pull/11519)) — thanks @rifqiawl +- **fix(translator):** preserve exact tool name casing for Claude tool_use payloads (#11487) ([#11520](https://github.com/diegosouzapw/OmniRoute/pull/11520)) — thanks @HouMinXi +- **fix(kimi-web):** align health probe with executor domain ([#11521](https://github.com/diegosouzapw/OmniRoute/pull/11521)) — thanks @pacocartones +- **fix(cli):** preserve existing admin password during setup ([#11522](https://github.com/diegosouzapw/OmniRoute/pull/11522)) — thanks @pacocartones +- **fix(mitm):** keep Windows DNS elevation off the POSIX sudo path (#11430) ([#11542](https://github.com/diegosouzapw/OmniRoute/pull/11542)) — thanks @official-burak +- **fix(sse):** scale chat admission by ingest byte budget instead of a fixed request count ([#11548](https://github.com/diegosouzapw/OmniRoute/pull/11548)) — thanks @TheDemonTuan +- **fix:** accept Gemini Web cookie exports ([#11554](https://github.com/diegosouzapw/OmniRoute/pull/11554)) — thanks @rafacpti23 +- **fix(i18n):** complete Brazilian Portuguese translations ([#11558](https://github.com/diegosouzapw/OmniRoute/pull/11558)) — thanks @rafacpti23 +- **fix(sse):** explicit types for openai-responses pureHelpers — clears last failing core typecheck gate ([#11567](https://github.com/diegosouzapw/OmniRoute/pull/11567)) — thanks @jonlwheat2-gif +- **fix(opencode):** hydrate Proxy Pool references for no-auth connections ([#11584](https://github.com/diegosouzapw/OmniRoute/pull/11584)) — thanks @AStupidBear +- **fix(ci):** resolve inherited release/v3.8.51 base-red CI failures ([#11608](https://github.com/diegosouzapw/OmniRoute/pull/11608)) — thanks @jonlwheat2-gif +- **fix(db):** fallback load STORAGE_ENCRYPTION_KEY from env files ([#11614](https://github.com/diegosouzapw/OmniRoute/pull/11614)) — thanks @benzntech +- **fix(providers):** mark Brave Search as no longer having a free tier ([#11621](https://github.com/diegosouzapw/OmniRoute/pull/11621)) — thanks @turbolego +- **fix:** pass tool result content through as string in OpenAI/Claude -> Gemini translation ([#11624](https://github.com/diegosouzapw/OmniRoute/pull/11624)) — thanks @Hsia97 +- **fix(sse):** scope credential health to exact key ([#11628](https://github.com/diegosouzapw/OmniRoute/pull/11628)) — thanks @KaspaPulse +- **fix(install):** guard prepare script against missing husky ([#11634](https://github.com/diegosouzapw/OmniRoute/pull/11634)) — thanks @Prajeeth-12 +- **fix(security):** redact NVIDIA_API_KEY at the log sink in the ad-hoc diag script ([#11639](https://github.com/diegosouzapw/OmniRoute/pull/11639)) +- **fix(types):** clear the two type regressions red on release/v3.8.51 ([#11640](https://github.com/diegosouzapw/OmniRoute/pull/11640)) — thanks @jonlwheat2-gif +- **fix(build):** resolve workspace: protocol dependencies to concrete semver versions during prepublish (#11540) ([#11641](https://github.com/diegosouzapw/OmniRoute/pull/11641)) — thanks @HouMinXi +- **fix(search):** properly resolve configured search connection in /v1/responses pipeline (#11524) ([#11642](https://github.com/diegosouzapw/OmniRoute/pull/11642)) — thanks @HouMinXi +- **fix(authz):** trust cf-connecting-ip for via-proxy detection when peer is Cloudflare edge (#11514) ([#11643](https://github.com/diegosouzapw/OmniRoute/pull/11643)) — thanks @HouMinXi +- **fix(security):** compare the OIDC state cookie in constant time ([#11646](https://github.com/diegosouzapw/OmniRoute/pull/11646)) +- **fix(security):** namespace the dedup hash by the calling API key ([#11649](https://github.com/diegosouzapw/OmniRoute/pull/11649)) +- **fix(models):** gate the Codex-native catalog loop on prefix mode (#11632) ([#11662](https://github.com/diegosouzapw/OmniRoute/pull/11662)) — thanks @xiaoyaner0201 +- **fix(dashboard):** project Codex routing quota in account pools ([#11666](https://github.com/diegosouzapw/OmniRoute/pull/11666)) — thanks @KaspaPulse +- **fix(ddgw):** normaliza system no upstream da release v3.8.51 ([#11692](https://github.com/diegosouzapw/OmniRoute/pull/11692)) — thanks @RhianB14 +- **fix(memory):** honor category filter in GET /api/memory ([#11699](https://github.com/diegosouzapw/OmniRoute/pull/11699)) — thanks @Sabeekhann +- **fix(ci):** detect stale workspace lockfile entries ([#11714](https://github.com/diegosouzapw/OmniRoute/pull/11714)) — thanks @pacocartones +- **fix(ci):** stamp dist/BUILD_SHA before the npm publish provenance gate ([#11721](https://github.com/diegosouzapw/OmniRoute/pull/11721)) +- **fix(docker):** decouple the best-effort Bun image from the release manifest ([#11724](https://github.com/diegosouzapw/OmniRoute/pull/11724)) +- **perf(compression):** avoid cloning memo misses twice ([#11727](https://github.com/diegosouzapw/OmniRoute/pull/11727)) — thanks @RaviTharuma +- **fix(docker):** let the bun digest artifact be absent, not fatal ([#11740](https://github.com/diegosouzapw/OmniRoute/pull/11740)) +- **fix(shared):** block cloud-metadata hosts under default remote-image guard ([#11755](https://github.com/diegosouzapw/OmniRoute/pull/11755)) — thanks @santosraju99-hub +- **fix(api):** preserve caller-provided X-Correlation-Id on chat completions ([#11760](https://github.com/diegosouzapw/OmniRoute/pull/11760)) — thanks @aniruddhaadak80 +- **fix(resilience):** lock permanently retired models instead of short backoff (Gemini ban prevention) ([#11762](https://github.com/diegosouzapw/OmniRoute/pull/11762)) — thanks @turbolego +- **fix:** mark Vercel AI Gateway as passthroughModels ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **fix(resilience):** stop hammering permanently-moved endpoints and billing-suspended accounts ([#11774](https://github.com/diegosouzapw/OmniRoute/pull/11774)) — thanks @turbolego +- **fix(lease):** remove global static reservation and gate routing on live active lease occupancy ([#11775](https://github.com/diegosouzapw/OmniRoute/pull/11775)) — thanks @TheDemonTuan +- **fix(ci):** size the install-upgrade gate to a measured run, and log the pack cost ([#11776](https://github.com/diegosouzapw/OmniRoute/pull/11776)) +- **fix(pwa):** stop serving the stale shell after deploys ([#11779](https://github.com/diegosouzapw/OmniRoute/pull/11779)) — thanks @HouMinXi +- **fix(combo):** lock GitHub models rejected as "not supported" for future requests ([#11781](https://github.com/diegosouzapw/OmniRoute/pull/11781)) — thanks @turbolego +- **fix(cli):** probe both IPv4 and IPv6 loopback for server readiness (#11766) ([#11794](https://github.com/diegosouzapw/OmniRoute/pull/11794)) — thanks @vermasomesh835 +- **fix(sse):** preserve CLIProxyAPI wrappers per connection ([#11797](https://github.com/diegosouzapw/OmniRoute/pull/11797)) — thanks @RaviTharuma +- **fix(providers):** honor model auto-fetch opt-in on create ([#11798](https://github.com/diegosouzapw/OmniRoute/pull/11798)) — thanks @RaviTharuma +- **fix(dashboard):** make model auto-fetch order independent ([#11805](https://github.com/diegosouzapw/OmniRoute/pull/11805)) — thanks @RaviTharuma +- **fix(docker):** make GHA cache exports best effort ([#11806](https://github.com/diegosouzapw/OmniRoute/pull/11806)) — thanks @RaviTharuma +- **fix(kiro):** do not permanently ban on 'User is not authorized to make this call' ([#11809](https://github.com/diegosouzapw/OmniRoute/pull/11809)) — thanks @Deftera186 +- **fix(sse):** keep cache-write tokens in OpenAI-shaped usage ([#11814](https://github.com/diegosouzapw/OmniRoute/pull/11814)) — thanks @TheDemonTuan +- **fix(docs):** resolve relative markdown and wiki links across Fumadocs and GitHub wiki ([#11834](https://github.com/diegosouzapw/OmniRoute/pull/11834)) — thanks @AndrianBalanescu +- **fix(chat):** preserve unstripped model string for passthrough provider routing ([#11840](https://github.com/diegosouzapw/OmniRoute/pull/11840)) — thanks @raheemuddin786 +- **fix(mcp):** avoid async initialization inside sync esm wrappers (#11569) ([#11849](https://github.com/diegosouzapw/OmniRoute/pull/11849)) — thanks @HouMinXi +- **fix(sse):** preserve coherent markdown token boundaries across stream translation chunks (#11606) ([#11851](https://github.com/diegosouzapw/OmniRoute/pull/11851)) — thanks @HouMinXi +- **fix(ci):** run the install-upgrade gate on disk, not on the /tmp tmpfs ([#11855](https://github.com/diegosouzapw/OmniRoute/pull/11855)) +- **fix(skills):** expand shorthand property types in injected tool schemas ([#11857](https://github.com/diegosouzapw/OmniRoute/pull/11857)) — thanks @alvinveroy +- **fix(ci):** publish npm from a hosted runner so provenance is accepted ([#11877](https://github.com/diegosouzapw/OmniRoute/pull/11877)) +- **fix(lint):** pin react hooks plugin to 7.0.1 ([#11886](https://github.com/diegosouzapw/OmniRoute/pull/11886)) +- **fix(ci):** surface and clear release-green ESLint failure ([#11890](https://github.com/diegosouzapw/OmniRoute/pull/11890)) — thanks @MumuTW +- **fix(guardrails):** prevent duplicate prompt-injection-guard log output ([#11936](https://github.com/diegosouzapw/OmniRoute/pull/11936)) — thanks @patrykkopycinski +- **fix(auth):** downgrade expected transient states from warn to debug ([#11937](https://github.com/diegosouzapw/OmniRoute/pull/11937)) — thanks @patrykkopycinski +- **fix(codex):** fail over image generation for imported free plans ([#11948](https://github.com/diegosouzapw/OmniRoute/pull/11948)) — thanks @Bl0ck154 +- **fix(codex):** normalize non-stream responses ([#11951](https://github.com/diegosouzapw/OmniRoute/pull/11951)) — thanks @Bl0ck154 +- **fix(images):** forward Antigravity image size ([#11952](https://github.com/diegosouzapw/OmniRoute/pull/11952)) — thanks @Bl0ck154 +- **fix(db):** invalidate connection cache after upsert ([#11953](https://github.com/diegosouzapw/OmniRoute/pull/11953)) — thanks @Bl0ck154 +- **fix(codex):** restore imported account state ([#11954](https://github.com/diegosouzapw/OmniRoute/pull/11954)) — thanks @Bl0ck154 +- **fix(sse):** give extended-thinking targets the reasoning readiness budget ([#11959](https://github.com/diegosouzapw/OmniRoute/pull/11959)) — thanks @abhisheksharma2411 +- **fix(dashboard):** use opaque background and readable text color on cost chart tooltips ([#11960](https://github.com/diegosouzapw/OmniRoute/pull/11960)) — thanks @ZaimMarzuki +- **fix(antigravity):** send complete loadCodeAssist metadata (ideType/platform/pluginType as numeric enums) ([#11969](https://github.com/diegosouzapw/OmniRoute/pull/11969)) — thanks @rifqiawl +- **fix(core):** resolve DB init race condition and reasoning translation ([#12003](https://github.com/diegosouzapw/OmniRoute/pull/12003)) — thanks @raheemuddin786 +- **fix(admission):** exclude reclaimable page cache from the cgroup pressure ratio ([#12017](https://github.com/diegosouzapw/OmniRoute/pull/12017)) — thanks @HouMinXi +- **fix(build):** prune non-production trees in NFT trace excludes and tsconfig ([#12028](https://github.com/diegosouzapw/OmniRoute/pull/12028)) — thanks @Chewji9875 +- **fix(migrations):** add renamed migration compatibility for 056/073/077/101 ([#12036](https://github.com/diegosouzapw/OmniRoute/pull/12036)) — thanks @oyi77 +- **fix(sse):** default crash-guard logger to console.warn, not console ([#12042](https://github.com/diegosouzapw/OmniRoute/pull/12042)) — thanks @HouMinXi +- **fix(catalog):** derive vision/modalities for built-in auto combos from effective target pool ([#12046](https://github.com/diegosouzapw/OmniRoute/pull/12046)) — thanks @Prajeeth-12 +- **fix(release):** the packaged-app smoke verifies the database opened, not a driver line the primary path never prints (twin of #12032) ([#12047](https://github.com/diegosouzapw/OmniRoute/pull/12047)) +- **fix(barrel):** migrate src/app/ off the @/lib/localDb barrel import (#11795 Phase 2) ([#12051](https://github.com/diegosouzapw/OmniRoute/pull/12051)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate src/lib/ off the localDb barrel to direct db imports (#59) ([#12052](https://github.com/diegosouzapw/OmniRoute/pull/12052)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate open-sse, src/shared, src/sse, src/models, src/domain off the @/lib/localDb barrel import (#11795 Phase 4) ([#12053](https://github.com/diegosouzapw/OmniRoute/pull/12053)) — thanks @jonlwheat2-gif +- **fix(barrel):** delete the @/lib/localDb barrel — every consumer migrated (#11795 Phase 5) ([#12055](https://github.com/diegosouzapw/OmniRoute/pull/12055)) — thanks @jonlwheat2-gif +- **fix(radar):** restore D12 public boundary ([#12057](https://github.com/diegosouzapw/OmniRoute/pull/12057)) +- **fix(dev):** bound webpack and Tailwind scans ([#12075](https://github.com/diegosouzapw/OmniRoute/pull/12075)) — thanks @backryun +- **fix(dev):** isolate root layout settings reads ([#12076](https://github.com/diegosouzapw/OmniRoute/pull/12076)) — thanks @backryun +- **fix(dev):** reduce instrumentation executor fan-out (phase 3) ([#12078](https://github.com/diegosouzapw/OmniRoute/pull/12078)) — thanks @backryun +- **fix(dev):** make logging resources HMR-singleton ([#12079](https://github.com/diegosouzapw/OmniRoute/pull/12079)) — thanks @backryun +- **fix(sse):** keep unavailable forced connections scoped ([#12080](https://github.com/diegosouzapw/OmniRoute/pull/12080)) — thanks @keeltrace +- **fix(dev):** isolate batch dispatch from instrumentation ([#12081](https://github.com/diegosouzapw/OmniRoute/pull/12081)) — thanks @backryun +- **fix(ci):** reconcile release test contract drift ([#12082](https://github.com/diegosouzapw/OmniRoute/pull/12082)) — thanks @backryun +- **fix(ci):** accept CVE-2025-68121 in the prebuilt tls-client .so, auto-close base-red issues, guard Scorecard on the default branch ([#12085](https://github.com/diegosouzapw/OmniRoute/pull/12085)) +- **fix(db):** drop three consumer-less 1proxy exports — dead-code base-red on release/v3.8.51 after the barrel deletion (#12055) ([#12087](https://github.com/diegosouzapw/OmniRoute/pull/12087)) +- **fix(api):** clear the six API-route TypeScript regressions the new gate landed red on ([#12094](https://github.com/diegosouzapw/OmniRoute/pull/12094)) +- **fix(release):** never let the tag-push Create Release append auto notes to the curated body ([#12096](https://github.com/diegosouzapw/OmniRoute/pull/12096)) +- **fix(sse):** honor CLIProxyAPI environment API key ([#12099](https://github.com/diegosouzapw/OmniRoute/pull/12099)) — thanks @RaviTharuma +- **fix(oauth):** bind Google refresh to the client that issued the token ([#12106](https://github.com/diegosouzapw/OmniRoute/pull/12106)) — thanks @HouMinXi +- **fix(dashboard):** make RequestLoggerDetail loadable outside Next — CSS via globals.css + CJS/ESM interop (#11703 base-reds) ([#12114](https://github.com/diegosouzapw/OmniRoute/pull/12114)) +- **fix(leases):** project status lease row to lease columns so joined connection PII never escapes ([#12115](https://github.com/diegosouzapw/OmniRoute/pull/12115)) — thanks @geek007git +- **fix(plugins):** do not kill the plugin process when a fire-and-forget hook times out ([#12116](https://github.com/diegosouzapw/OmniRoute/pull/12116)) — thanks @geek007git +- **fix(guardrails):** restore injection-guard logging on middleware-only routes ([#12117](https://github.com/diegosouzapw/OmniRoute/pull/12117)) — thanks @geek007git +- **fix(plugins):** refresh stored manifest from disk on activate so new hook fields reach existing installs ([#12120](https://github.com/diegosouzapw/OmniRoute/pull/12120)) — thanks @geek007git +- **fix(codex):** preserve existing provider state when bulk-import upserts a matching connection ([#12122](https://github.com/diegosouzapw/OmniRoute/pull/12122)) — thanks @geek007git +- **fix(ci):** clear the two base-reds the 2026-08-30 merge batch left on release/v3.8.51 (round 3) ([#12123](https://github.com/diegosouzapw/OmniRoute/pull/12123)) +- **fix(sse):** strip type:'custom' from Claude tools on agentrouter dispatch ([#12126](https://github.com/diegosouzapw/OmniRoute/pull/12126)) — thanks @wahidsadik371-coder +- **fix(sse):** stop advertising video providers the dispatcher cannot run ([#12131](https://github.com/diegosouzapw/OmniRoute/pull/12131)) — thanks @quiterunner-commits +- **fix(search):** treat HTTP 432 and plan limit errors as transient cooldown ([#12139](https://github.com/diegosouzapw/OmniRoute/pull/12139)) — thanks @HouMinXi +- **fix(sse):** refuse an AI Horde queue that cannot fit the request budget ([#12143](https://github.com/diegosouzapw/OmniRoute/pull/12143)) — thanks @quiterunner-commits +- **fix(ci):** clear the base-reds the afternoon merge batch left on release/v3.8.51 (round 5: provider count 352, TS2554/TS2677) ([#12144](https://github.com/diegosouzapw/OmniRoute/pull/12144)) +- **fix(translator):** strip neutral tool_choice when tools absent in Responses-to-Chat (#12141) ([#12166](https://github.com/diegosouzapw/OmniRoute/pull/12166)) — thanks @HouMinXi +- **fix(guardrails):** pass providerId to getResolvedModelCapabilities in checkComboVision (#12112) ([#12169](https://github.com/diegosouzapw/OmniRoute/pull/12169)) — thanks @HouMinXi +- **fix(translator):** strip plaintext reasoning content for opaque responses backends (#12128) ([#12171](https://github.com/diegosouzapw/OmniRoute/pull/12171)) — thanks @HouMinXi +- **perf(stream):** compile hot-path regexes once, bound token caches, fix quadratic buffering ([#12179](https://github.com/diegosouzapw/OmniRoute/pull/12179)) — thanks @opensource-elearning +- **fix:** resolve compression worker file using runtime anchors instead of… ([#12183](https://github.com/diegosouzapw/OmniRoute/pull/12183)) — thanks @NightStalker-87 +- **fix:** prevent Claude Code session kills via liveness-aware readiness + auto model echo ([#12189](https://github.com/diegosouzapw/OmniRoute/pull/12189)) — thanks @opensource-elearning +- **fix(translator):** preserve falsy primitive values in Gemini and Antigravity function response results ([#12191](https://github.com/diegosouzapw/OmniRoute/pull/12191)) — thanks @Siva010 +- **fix(oauth):** mark empty Antigravity projectId as degraded and clear stale errors (#11284) ([#12205](https://github.com/diegosouzapw/OmniRoute/pull/12205)) — thanks @HouMinXi +- **fix(providers):** gate the Codex auto-ping usage read on the shared quota throttle ([#12209](https://github.com/diegosouzapw/OmniRoute/pull/12209)) — thanks @abhisheksharma2411 +- **fix(combos):** prioritize SQLite row id over inner JSON id and notify delete errors ([#12213](https://github.com/diegosouzapw/OmniRoute/pull/12213)) — thanks @HouMinXi +- **fix(providers):** mark featherless.ai as no longer having a free tier (`hasFree: false`) ([#12216](https://github.com/diegosouzapw/OmniRoute/pull/12216)) — thanks @turbolego +- **fix(deepseek-web):** stop Turbopack dev panic in the PoW worker path resolver ([#12221](https://github.com/diegosouzapw/OmniRoute/pull/12221)) — thanks @hartmark +- **fix(chat-admission):** clarify local 503 source ([#12223](https://github.com/diegosouzapw/OmniRoute/pull/12223)) — thanks @keeltrace +- **fix(dev):** silence webpack runtime module warnings ([#12228](https://github.com/diegosouzapw/OmniRoute/pull/12228)) — thanks @backryun +- **fix(db):** use module.require for CommonJS runtime driver loading ([#12230](https://github.com/diegosouzapw/OmniRoute/pull/12230)) — thanks @raheemuddin786 +- **fix(memory):** honest probe-driven FTS5 keyword status + memory_id rowid sync ([#12231](https://github.com/diegosouzapw/OmniRoute/pull/12231)) — thanks @raheemuddin786 +- **fix(system):** propagate abort signal to stream reader in HTTP version checks ([#12232](https://github.com/diegosouzapw/OmniRoute/pull/12232)) — thanks @raheemuddin786 +- **fix(resilience):** isolate local host execution errors from provider circuit breakers ([#12233](https://github.com/diegosouzapw/OmniRoute/pull/12233)) — thanks @raheemuddin786 +- **fix(sse):** sort injected tools deterministically for prompt caching ([#12234](https://github.com/diegosouzapw/OmniRoute/pull/12234)) — thanks @raheemuddin786 +- **fix(combo):** always clear the loop-safety timer, not just on the happy path (#11804) ([#12245](https://github.com/diegosouzapw/OmniRoute/pull/12245)) +- **fix(quality):** base-red pair — stryker tap registration + turn-pin suites aligned to the window gate ([#12255](https://github.com/diegosouzapw/OmniRoute/pull/12255)) — thanks @backryun +- **fix(dev):** qualify Turbopack runtime boundaries ([#12258](https://github.com/diegosouzapw/OmniRoute/pull/12258)) — thanks @backryun +- **fix(combo):** fall back to full pool when collapsed sole survivor is context-too-small ([#12278](https://github.com/diegosouzapw/OmniRoute/pull/12278)) — thanks @opensource-elearning +- **fix(combo):** bound the pre-dispatch unavailable skip so a stale label cannot dark a pool (#12168) ([#12285](https://github.com/diegosouzapw/OmniRoute/pull/12285)) +- **fix(security):** strip Qwen/Alibaba console-session cookies from provider API responses ([#12287](https://github.com/diegosouzapw/OmniRoute/pull/12287)) +- **fix(usage):** console-aware Token Plan guidance + subscription hint on bailian 401 ([#12288](https://github.com/diegosouzapw/OmniRoute/pull/12288)) +- **fix(resourcePressure):** log numeric detail on every rejection, recover faster ([#12293](https://github.com/diegosouzapw/OmniRoute/pull/12293)) — thanks @hartmark +- **fix(release):** drain the 2026-09-01 base-red window — passthrough usage regression + radarPage i18n keys ([#12327](https://github.com/diegosouzapw/OmniRoute/pull/12327)) +- **fix(ci):** openapi-security-tiers checker must honor routeGuard patterns + imported prefixes ([#12350](https://github.com/diegosouzapw/OmniRoute/pull/12350)) — thanks @ggiak +- **perf(ci):** cache node_modules in the npm-ci-retry composite (#8084 D3) ([#12408](https://github.com/diegosouzapw/OmniRoute/pull/12408)) +- **revert(ui):** point CTAs back at their real destinations ([#12410](https://github.com/diegosouzapw/OmniRoute/pull/12410)) +- **fix(release):** drain the 2026-09-02 base-red — rerank-providers import + api-typecheck baseline ratchet ([#12414](https://github.com/diegosouzapw/OmniRoute/pull/12414)) +- **fix(types):** make system prompt injection noImplicitAny-safe ([#12416](https://github.com/diegosouzapw/OmniRoute/pull/12416)) +- **fix(quality):** validate typecheck baseline schema ([#12419](https://github.com/diegosouzapw/OmniRoute/pull/12419)) +- **fix(memory):** resolve rerank provider node cache import ([#12421](https://github.com/diegosouzapw/OmniRoute/pull/12421)) +- **fix(ci):** repair release-root regressions — pack dedup, web-session syntax, uc-image ids ([#12423](https://github.com/diegosouzapw/OmniRoute/pull/12423)) — thanks @backryun +- **fix(providers):** separate MaxAI and UC credential contracts ([#12431](https://github.com/diegosouzapw/OmniRoute/pull/12431)) +- **fix(cli):** preserve Claude settings on config set ([#12432](https://github.com/diegosouzapw/OmniRoute/pull/12432)) — thanks @KrzysiekSko +- **fix(providers):** repair the maxai credential block truncated by merge auto-resolve ([#12433](https://github.com/diegosouzapw/OmniRoute/pull/12433)) +- **fix(quality):** record the 2026-09-02 merged growth in the file-size baseline ([#12434](https://github.com/diegosouzapw/OmniRoute/pull/12434)) +- **fix(uc):** route UC error strings through sanitizeErrorMessage; allowlist the retired codex id ([#12437](https://github.com/diegosouzapw/OmniRoute/pull/12437)) +- **fix(adapta):** redact streamed upstream errors ([#12438](https://github.com/diegosouzapw/OmniRoute/pull/12438)) +- **fix(sse):** remap non-contiguous upstream tool_calls index to a gap-free output_index ([#12445](https://github.com/diegosouzapw/OmniRoute/pull/12445)) — thanks @hartmark +- **fix(quality):** detect duplicate tool_calls entries in one response ([#12446](https://github.com/diegosouzapw/OmniRoute/pull/12446)) — thanks @hartmark +- **fix(conversations):** resolve turn content OmniRoute never sends back to the client ([#12447](https://github.com/diegosouzapw/OmniRoute/pull/12447)) — thanks @hartmark +- **fix(combo):** do not treat credits-exhausted 401 as auth skip ([#12449](https://github.com/diegosouzapw/OmniRoute/pull/12449)) — thanks @RaviTharuma +- **fix(auth):** do not park healthy quota accounts as expired ([#12452](https://github.com/diegosouzapw/OmniRoute/pull/12452)) — thanks @RaviTharuma +- **fix(responses-continuation):** fail closed on a collector-truncated, empty output array ([#12460](https://github.com/diegosouzapw/OmniRoute/pull/12460)) — thanks @hartmark +- **fix(logging):** raise the SSE payload collector's default cap ([#12461](https://github.com/diegosouzapw/OmniRoute/pull/12461)) — thanks @hartmark +- **fix(resilience):** surface Responses failed.error.message in 502s ([#12472](https://github.com/diegosouzapw/OmniRoute/pull/12472)) — thanks @RaviTharuma +- **fix(security):** unbiased maxai X-Random nonce + stricter URL/regex assertions ([#12502](https://github.com/diegosouzapw/OmniRoute/pull/12502)) +- **fix(video):** re-anchor transcript log-redaction so PII/credential maskers can't reopen the leak (#12150 P1 follow-up) ([#12503](https://github.com/diegosouzapw/OmniRoute/pull/12503)) +- **fix(monitoring):** serve cached credentialHealth off the request path ([#12533](https://github.com/diegosouzapw/OmniRoute/pull/12533)) — thanks @RaviTharuma +- **fix(quota):** keep Antigravity Gemini usable when Claude weekly is empty ([#12566](https://github.com/diegosouzapw/OmniRoute/pull/12566)) — thanks @HouMinXi +- **fix(authz):** hard-gate every credential export and CLI-config write (GHSA-5926-2w35-7h4q) ([#12600](https://github.com/diegosouzapw/OmniRoute/pull/12600)) +- **fix(docs):** restore the Next build — REMOVED_PROVIDERS.md had no frontmatter (base-red #12581) ([#12610](https://github.com/diegosouzapw/OmniRoute/pull/12610)) +- **fix(quality):** re-point the zcodeProtocol public-creds allowlist to line 313 ([#12615](https://github.com/diegosouzapw/OmniRoute/pull/12615)) +- **fix(security):** close 3 advisories — search baseUrl exfil, sk- in the error sanitizer, bifrost relay header leak ([#12620](https://github.com/diegosouzapw/OmniRoute/pull/12620)) +- **fix(providers):** sanitize boolean required and nested bare maps for Gemini (#12269) ([#12624](https://github.com/diegosouzapw/OmniRoute/pull/12624)) — thanks @HouMinXi +- **fix(resilience):** keep Overloaded STREAM_EARLY_EOF off the provider breaker ([#12626](https://github.com/diegosouzapw/OmniRoute/pull/12626)) — thanks @HouMinXi +- **fix(catalog):** re-audit free-tier quotas against official pages ([#12649](https://github.com/diegosouzapw/OmniRoute/pull/12649)) +- **fix(dashboard):** keep a request's pending-tracking id stable across combo target retries ([#12650](https://github.com/diegosouzapw/OmniRoute/pull/12650)) — thanks @hartmark +- **fix(tests):** retire dead model ids from the chat-pipeline integration suite (base-red #12581) ([#12670](https://github.com/diegosouzapw/OmniRoute/pull/12670)) +- **fix:** resolve SqliteError no such table compression_run_telemetry during cleanup ([#12682](https://github.com/diegosouzapw/OmniRoute/pull/12682)) — thanks @groovecityJO +- **fix(combo):** treat a pin-only step as implicit connection allowlist ([#12697](https://github.com/diegosouzapw/OmniRoute/pull/12697)) — thanks @HouMinXi +- **fix(ci):** ship .npmrc in published package so legacy-peer-deps applies to consumers (#11544) ([#12699](https://github.com/diegosouzapw/OmniRoute/pull/12699)) — thanks @KooshaPari +- **fix(docker):** pin 4 CLI tools to exact versions (#12576) ([#12703](https://github.com/diegosouzapw/OmniRoute/pull/12703)) — thanks @KooshaPari +- **fix(docker):** document and harden cli profile trust boundary (#12570) ([#12706](https://github.com/diegosouzapw/OmniRoute/pull/12706)) — thanks @KooshaPari +- **fix(sse):** redact video transcript in pre-guardrail rejected-request logs (#12150 P2 item 7) ([#12710](https://github.com/diegosouzapw/OmniRoute/pull/12710)) +- **fix(i18n):** quote placeholder in OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES description (#12505) ([#12769](https://github.com/diegosouzapw/OmniRoute/pull/12769)) — thanks @KooshaPari +- **fix(api/pricing):** surface validation error message as string, not raw object (#12494) ([#12771](https://github.com/diegosouzapw/OmniRoute/pull/12771)) — thanks @KooshaPari +- **fix(sse):** re-enable prompt compression for native Codex passthrough ([#12834](https://github.com/diegosouzapw/OmniRoute/pull/12834)) — thanks @thomasmaerz +- **fix(dashboard):** model health tests for a provider node set to the Responses API now call `/v1/responses` with a Responses-shaped body instead of `/v1/chat/completions` — those models were reported as `Provider returned HTTP 200 but no text content` even though the same model answered normally through `/v1/responses` ([#13070](https://github.com/diegosouzapw/OmniRoute/issues/13070)) ([#13087](https://github.com/diegosouzapw/OmniRoute/pull/13087)) — thanks @ntdatt812 +- fix(routing): stop a round-robin combo's "opencode" targets from collapsing onto the opencode-zen connection (#11912) ([#13283](https://github.com/diegosouzapw/OmniRoute/pull/13283)) +- fix(routing): stop the reactive-compaction debug log from lying when compression is globally disabled (#11977) ([#13276](https://github.com/diegosouzapw/OmniRoute/pull/13276)) +- fix(electron): relativize standalone-bundle symlink targets so Stage 8 manifest verification stops failing on Windows (#11979) ([#13251](https://github.com/diegosouzapw/OmniRoute/pull/13251)) +- fix(dashboard): surface a visible error when Compression Studio's combined preview run fails (#12061) ([#13288](https://github.com/diegosouzapw/OmniRoute/pull/13288)) +- fix(dashboard): make the compression "Effective pipeline" preview honor the active profile and warn when the master switch is off (#12063) ([#13287](https://github.com/diegosouzapw/OmniRoute/pull/13287)) +- fix(providers): scope TinyCMS Web signer's DOM shims to each call instead of leaking them for the process lifetime, and surface a clean HTTP status on a non-JSON interception-toggles error (#12072) ([#13254](https://github.com/diegosouzapw/OmniRoute/pull/13254)) +- fix(guardrails): stop Vision Bridge from re-selecting a model locked after a 404 (#12111) ([#13259](https://github.com/diegosouzapw/OmniRoute/pull/13259)) +- fix(sse): require Responses-shaped body before native OpenAI-compatible passthrough (#12129) ([#13278](https://github.com/diegosouzapw/OmniRoute/pull/13278)) +- fix(providers): minimax-m3 now collapses manual thinking.type:"enabled" to adaptive, preventing upstream 400 (2013) (#12132) ([#13249](https://github.com/diegosouzapw/OmniRoute/pull/13249)) +- fix(db): scope model visibility overrides by modality so hiding a Chat model no longer hides an identically-ID'd Image/Embeddings/etc. model (#12172) ([#13258](https://github.com/diegosouzapw/OmniRoute/pull/13258)) +- fix(db): scope local-provider apiKey dedup to matching base URL so LM Studio/Ollama-style connections support multiple accounts (#12173) ([#13250](https://github.com/diegosouzapw/OmniRoute/pull/13250)) +- fix(providers): refresh Trae's stale Referer/Origin and forward user timezone so imported connections stop failing with 401 (#12190) ([#13255](https://github.com/diegosouzapw/OmniRoute/pull/13255)) +- fix(providers): route opencode-go/gpt-5.6-luna to /responses instead of /chat/completions (#12196) ([#13275](https://github.com/diegosouzapw/OmniRoute/pull/13275)) +- fix(dashboard): allow deleting the last extra-upstream-header row even when invalid (#12251) ([#13252](https://github.com/diegosouzapw/OmniRoute/pull/13252)) +- **fix(i18n):** translate pre-existing `__MISSING__:` keys for `combo.sort`, `requestLogger.detail` expand/collapse, `common.profile`, and `settings.resilienceCredentialHealth*` across 39 locales ([#12272](https://github.com/diegosouzapw/OmniRoute/issues/12272)) ([#13034](https://github.com/diegosouzapw/OmniRoute/pull/13034)) — thanks @HouMinXi +- fix(cli): run the Node.js runtime compatibility guard before the heavy `tsx/esm` + Commander import chain so an unsupported runtime gets a clear message instead of a raw `Invalid regular expression flags` crash (#12296) ([#13253](https://github.com/diegosouzapw/OmniRoute/pull/13253)) +- fix(dashboard): refresh the providers list after deleting a compatible provider node (#12298) ([#13256](https://github.com/diegosouzapw/OmniRoute/pull/13256)) +- fix(usage): fail closed on API-key budget enforcement when a provider's `auto` routing alias has no pricing row, instead of silently counting it as $0 (#12341) ([#13257](https://github.com/diegosouzapw/OmniRoute/pull/13257)) +- **fix(providers):** include the submitted Agnes video model when polling by `video_id` ([#12356](https://github.com/diegosouzapw/OmniRoute/pull/12356)) — thanks @pacocartones +- **fix(routing):** custom OpenAI-compatible nodes now honor the saved Chat/Responses API type after edits instead of letting the node's original ID prefix override the live connection setting ([#11884](https://github.com/diegosouzapw/OmniRoute/issues/11884)). ([#12358](https://github.com/diegosouzapw/OmniRoute/pull/12358)) — thanks @pacocartones +- fix(sse): surface an error instead of a silent empty 200 when a Claude stream closes with zero bytes (#12398) ([#13285](https://github.com/diegosouzapw/OmniRoute/pull/13285)) +- fix(oauth): warn before the dead localhost:8080 redirect in antigravity/gemini `oauth start` (#12413) ([#13265](https://github.com/diegosouzapw/OmniRoute/pull/13265)) +- fix(providers): stop devin-cli spawn error from double-closing the SSE controller (#12517) ([#13261](https://github.com/diegosouzapw/OmniRoute/pull/13261)) +- **fix(rerank):** clamp Voyage `top_k` to the documents actually sent after empty-string filtering, and honor `return_documents: false` in the NVIDIA response adapter (#12523 — thanks @pacocartones) +- **fix(audio):** `/v1/audio/translations` now resolves combo names the way `/v1/audio/transcriptions` already does, so a combo that `GET /v1/models` advertises is fanned out to its targets instead of being rejected with `400 Invalid translation model: . Use format: provider/model`; literal `provider/model` ids and unknown bare names behave as before (#12536 — thanks @pacocartones) +- **fix(gemini):** strip the JSON-Schema-2020-12 `prefixItems` keyword from Gemini tool schemas at every nesting level, so Claude Code tool definitions no longer fail with `400 Unknown name "prefixItems"` on Gemini models (#12540 — thanks @pacocartones) +- **fix(api-manager):** Expose an accessible loading status while API keys are fetched instead of an empty accessibility tree (#12541 — thanks @pacocartones) +- **fix(video):** Clamp `estimateJpegFrameBytes` at zero for padding-only payloads and build the three encode-side frame data URIs from `JPEG_FRAME_DATA_URI_PREFIX` instead of a repeated literal (#12543 — thanks @pacocartones) +- **fix(devin):** accept Windows `DEVIN_AGENTIC_HOME` sandbox paths (`C:\...\.sandbox\...`) in the isolated-home check so the Devin Claude Bridge no longer fails closed on Windows ([#12405](https://github.com/diegosouzapw/OmniRoute/issues/12405)) (#12545 — thanks @pacocartones) +- **fix(db):** Add `conversation_turn_nodes` and orphaned `agentic_conversations` to the auto-cleanup cycle under the existing `retention.callLogs` window, so identity nodes whose call-log content has already been purged no longer accumulate without bound in `storage.sqlite` (#12548 — thanks @pacocartones) +- **fix(i18n):** Wrap the `~/.claude/profiles//settings.json` placeholder in ICU single quotes in the `OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES` feature-flag description across all 42 locales and the TypeScript default, so next-intl no longer fails with `INVALID_MESSAGE: UNCLOSED_TAG` and the Feature Flags card shows the description instead of the raw key (#12549 — thanks @pacocartones) +- **fix(orchestration):** `updateCloudAgentTask` now publishes the task's real `status` on `agent.task.updated` when an update only touches `result`, `activities` or `error`, instead of the fabricated `"updated"` state, and stays silent when no row matched the id (#12550 — thanks @pacocartones) +- **fix(i18n):** the home "Recent Requests" panel and the Provider Topology legend are now translated instead of rendering English copies on non-English dashboards; the legend reads its own `home.topologyLegend*` labels with consistent casing rather than borrowing the memory-settings "Recent" and analytics "Error" strings (#12551 — thanks @pacocartones). +- **docs(reference):** bring the `FEATURE_FLAGS.md` catalog back to 1:1 with `featureFlagDefinitions.ts` — 20 missing flags added, the two `*_BLOCK_THRESHOLD` env-only knobs moved out of the flag tables, category/total counts and the Live WS port corrected, guarded by a static test (#12552 — thanks @pacocartones) +- **fix(i18n):** backfill missing `usage.kiloPass*` strings in 39 locales and restore `featureFlagOmnirouteDisableThinkingLevelVariantsDescription` in `pt.json` ([#12561](https://github.com/diegosouzapw/OmniRoute/issues/12561)) ([#13027](https://github.com/diegosouzapw/OmniRoute/pull/13027)) — thanks @HouMinXi +- fix(docker): default docker-compose app ports (dashboard/API/live-WS) to loopback instead of `0.0.0.0`, closing the anonymous `/v1` LAN/WAN exposure gap left open by `REQUIRE_API_KEY=false` (#12568) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): close DNS-rebinding SSRF gap in webhook outbound-URL guard (#12569) ([#13243](https://github.com/diegosouzapw/OmniRoute/pull/13243)) +- fix(docker): require a per-session token on the VNC browser CDP bridge and isolate it on a dedicated Docker network (#12571) ([#13241](https://github.com/diegosouzapw/OmniRoute/pull/13241)) +- fix(open-sse): write Adobe Firefly session tokens and cookie jars with 0700/0600 permissions instead of the process umask (#12572) ([#13242](https://github.com/diegosouzapw/OmniRoute/pull/13242)) +- fix(api): remove hardcoded wildcard CORS in openai-to-gemini-sse.ts so the centralized fail-closed CORS gate is the sole source of `Access-Control-Allow-Origin` (#12573) ([#13239](https://github.com/diegosouzapw/OmniRoute/pull/13239)) +- fix(api): enforce API key policy (budget/rate-limit/schedule/endpoint scoping) on the ElevenLabs speech-to-text, text-to-speech and voices proxy routes (#12574) ([#13244](https://github.com/diegosouzapw/OmniRoute/pull/13244)) +- fix(sse): cap HuggingChat NDJSON body size and bound the read loop with the fetch timeout so a stalled or hostile upstream cannot buffer unbounded memory (#12577) ([#13282](https://github.com/diegosouzapw/OmniRoute/pull/13282)) +- fix(docker): scope the cliproxyapi/qdrant/bifrost sidecars to loopback by default and forward `CLIPROXYAPI_MANAGEMENT_KEY` into the cliproxyapi container so its management API is not left both unauthenticated and LAN-published (#12578) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): create DB export temp paths with `fs.mkdtempSync` instead of predictable timestamps (#12579) ([#13237](https://github.com/diegosouzapw/OmniRoute/pull/13237)) +- Cline 401 bodies that say "re-authenticate your Cline account" classify as a refreshable OAuth token, not a terminal expired key. The cooling panel no longer labels every cooldown as a 429; it shows the recorded last error instead. (#12594) ([#13060](https://github.com/diegosouzapw/OmniRoute/pull/13060)) — thanks @HouMinXi +- **fix(catalog):** degrade unknown combo targets instead of dropping LCD modalities, and persist OpenRouter `architecture.input_modalities` into the capability snapshot ([#12613](https://github.com/diegosouzapw/OmniRoute/issues/12613)) ([#13042](https://github.com/diegosouzapw/OmniRoute/pull/13042)) — thanks @HouMinXi +- fix(providers): send `x-api-key` instead of `Authorization: Bearer` for OpenCode Zen's `/v1/responses` endpoint (Muse Spark Contributor models), fixing a 401 on OmniRoute's auth header (#12633) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(sse): surface the actionable "Auggie CLI not found" message when the shell reports a missing `auggie` binary via exit code instead of a spawn error (#12645) ([#13263](https://github.com/diegosouzapw/OmniRoute/pull/13263)) +- **fix(config):** Persist deletions of built-in background-degradation entries — when a stored settings record exists its `degradationMap` is now authoritative instead of being merged under the defaults, so an entry the user removed in the dashboard no longer reappears on the next apply or restart ([#12424](https://github.com/diegosouzapw/OmniRoute/issues/12424)) ([#12647](https://github.com/diegosouzapw/OmniRoute/pull/12647)) — thanks @pacocartones +- **fix(gamification):** action-count badge milestones (First Token, Token Consumer, Token Machine, Token Whale, and the token-sharing tier) are now backed by a durable `xp_action_counts` counter incremented in `addXp()`, instead of a live `COUNT(*)` over `xp_audit_log`. The audit log is pruned by `retention.xpAuditLog` (default 30 days), so on a default install those "lifetime" milestones were really "actions in the last 30 days" and unlocked badges could stop unlocking once old rows aged out. `getActionCount()` and `checkActionCountBadges()` now read the same durable source, and a migration backfills existing totals from the surviving audit rows ([#12546](https://github.com/diegosouzapw/OmniRoute/issues/12546)) ([#12651](https://github.com/diegosouzapw/OmniRoute/pull/12651)) — thanks @pacocartones +- **fix(images):** `/v1/images/edits` now iterates a combo's targets the same way `/v1/images/generations` does (#9239) instead of flattening a bare combo to its first target. A combo whose first target is not edit-capable — or lacks credentials — now falls through to a later edit-capable target rather than hard-erroring, and missing credentials are skipped (not a hard `401`) to match the generations path. The per-target skip/terminal classification is extracted into a shared `runImageComboTargets` loop, so generations behavior is unchanged ([#12547](https://github.com/diegosouzapw/OmniRoute/issues/12547)). ([#12653](https://github.com/diegosouzapw/OmniRoute/pull/12653)) — thanks @pacocartones +- fix(sse): add first-byte watchdog to the TLS-fingerprint transport so a stalled wreq body falls back instead of hanging for minutes (#12656) ([#13272](https://github.com/diegosouzapw/OmniRoute/pull/13272)) +- fix(sse): exempt tiny-budget reasoning probes from combo quality failure and surface persisted-cooldown skips in ALL_TARGETS_SKIPPED diagnostics (#12659) ([#13269](https://github.com/diegosouzapw/OmniRoute/pull/13269)) +- fix(models): declare the real ~1M contextLength for OpenCode Zen's Muse Spark 1.2 models instead of falling back to the 200000 provider default (#12681) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(oauth): align codebuddy-cn OAuth User-Agent with the chat/usage CLI version to avoid WAF false positives (#12702) ([#13264](https://github.com/diegosouzapw/OmniRoute/pull/13264)) +- fix(dashboard): surface an authentication-required banner instead of silently blanking database settings for a guest session (#12709) ([#13270](https://github.com/diegosouzapw/OmniRoute/pull/13270)) +- **fix(resilience):** stop unbounded queue that hangs 6min until Aborted — gate, provider slot, and Bottleneck queue now share a per-connection `maxWaitMs` budget; `fail-closed` on exhaust (503); execution backstop `executionMaxWaitMs` overridable per connection with upstream clamp ([#12715](https://github.com/diegosouzapw/OmniRoute/pull/12715)) — thanks @maxmad64bis (with thanks to @Tushar49 for surfacing the slow-provider need in #12635) +- **fix(auto-combo):** every mode pack now carries `quality` and `reliability` weights (`quality` 0.02, 0.03 in `quality-first`; `reliability` 0.03, 0.04 in `reliability-first`), so selecting a pack no longer silences either signal; `DEFAULT_WEIGHTS` is unchanged ([#12731](https://github.com/diegosouzapw/OmniRoute/pull/12731)) — thanks @maxmad64bis +- **fix(sse):** the provider execution pipeline keeps the upstream error `code`/`type` again — both were lost when the non-streaming leg was extracted, so a config-class `422` (Antigravity missing project) degraded into a generic account cooldown instead of reaching the gate that recognises it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(db):** a legacy `call_logs` table no longer aborts startup — the provider-stats index is created after the column healing runs, not before it, so an install predating `request_type` opens instead of failing with `no such column` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(catalog):** a malformed row in the operator-writable custom-models blob no longer kills every `auto/*` pool ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(tests):** realign the guards that the same day's merges left asserting the old behavior — the provider-count total after GreenPT and EURouter, the GLM stream buffer slot that `#12925` turned into a declared parameter, and the injection scan bound that `#13104` reshaped from a head-only window into head-plus-tail ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- fix(cache): fold tool_choice/tools/response_format into the semantic cache signature so a cached tool_calls response can no longer be replayed for a request whose tool policy forbids it (#12734) ([#13267](https://github.com/diegosouzapw/OmniRoute/pull/13267)) +- **fix(models):** stop treating provider-supplied `isFree:true`/`:free`/`0/0` flags as trusted unless the provider has a documented free tier — only the shipped free-tier catalog decides otherwise; fetched `isFree:true` trusted only for a free-tier provider, custom `isFree:true` via a trusted path ([#12744](https://github.com/diegosouzapw/OmniRoute/pull/12744)) — thanks @maxmad64bis +- fix(memory): authenticate the internal /v1/rerank loopback call so memory reranking no longer silently degrades to unranked order when REQUIRE_API_KEY=true (#12745) ([#13268](https://github.com/diegosouzapw/OmniRoute/pull/13268)) +- fix(sse): parse Ollama Cloud's current usage markup (`$X of $Y used` aria-label, nested width style) (#12749) ([#13260](https://github.com/diegosouzapw/OmniRoute/pull/13260)) +- fix(cli): setup-opencode no longer sends an active context's management token to `/v1/models` when `--api-key`/`OMNIROUTE_API_KEY` is supplied — an explicit flag or the env var now always outranks the context's token, and the flag itself is no longer swallowed by the parent program's global `--api-key` option (#12783) ([#13246](https://github.com/diegosouzapw/OmniRoute/pull/13246)) +- fix(sse): register Arcee AI in the executor provider registry so requests reach api.arcee.ai instead of silently falling back to OpenAI (#12784) ([#13277](https://github.com/diegosouzapw/OmniRoute/pull/13277)) +- **fix(quota):** share one upstream quota read when identical saturation checks arrive at the same time, so traffic bursts don't multiply provider API calls ([#12787](https://github.com/diegosouzapw/OmniRoute/pull/12787)) — thanks @maxmad64bis +- **fix(db):** ignore expired or invalid rate-limit cooldown writes so a stale timestamp can't lock a connection that should be usable — clearing still works as before ([#12788](https://github.com/diegosouzapw/OmniRoute/pull/12788)) — thanks @maxmad64bis +- **fix(providers):** single source for provider order with `xao` ranking alongside `xai-oauth` ([#12790](https://github.com/diegosouzapw/OmniRoute/pull/12790)) — thanks @maxmad64bis +- **fix(routing):** off-table models check the free-model catalog before inheriting premium prices, latency bootstraps from the observed pool median, async tiers read live database pricing with a 90-day freshness gate, and the tier cache invalidates on every pricing write ([#12792](https://github.com/diegosouzapw/OmniRoute/pull/12792)) — thanks @maxmad64bis +- **fix(auto-combo):** snapshot scoring uses the observed per-connection account tier and per-model quality instead of neutral constants, and circuit-open providers rank lower at build time ([#12794](https://github.com/diegosouzapw/OmniRoute/pull/12794)) — thanks @maxmad64bis +- **fix(pool):** empty `auto/*` pools now say why they are empty, and the models listing uses the same paid check as routing ([#12795](https://github.com/diegosouzapw/OmniRoute/pull/12795)) — thanks @maxmad64bis +- fix(routing): recognize CLIProxyAPI's 'unknown provider for model' 400 as fallback-worthy (#12800) ([#13284](https://github.com/diegosouzapw/OmniRoute/pull/13284)) +- **fix(sse):** translate-mode streams now emit the estimated token counts as a trailing usage-only chunk before `[DONE]` when the upstream stays silent, so metered chat clients see totals instead of nothing ([#12828](https://github.com/diegosouzapw/OmniRoute/pull/12828)) — thanks @maxmad64bis +- **fix(db):** provider stats stay truthful on empty and legacy databases: fallback counts default to `0` instead of `null`, latency averages read `null` (not `0`) when no durations were recorded, failures logged before the error-type column existed group under `pre_migration` instead of `unclassified`, and per-provider queries use two new composite indexes ([#12832](https://github.com/diegosouzapw/OmniRoute/pull/12832)) — thanks @maxmad64bis +- fix(nvidia): fail open when a synced model catalog goes stale instead of gating forever (#12849) ([#13248](https://github.com/diegosouzapw/OmniRoute/pull/13248)) +- **fix(db):** WAL maintenance lives in its own module: a `TRUNCATE` checkpoint that hits a busy database now warns with its streak and retries once via `PASSIVE` instead of logging success, and closing-time checkpoints no longer report success on builds without a database file, with the busy totals visible in the authenticated monitoring health payload ([#12853](https://github.com/diegosouzapw/OmniRoute/pull/12853)) — thanks @maxmad64bis +- **fix(health):** quota with no snapshots reads empty instead of a contradicting 0% ([#12857](https://github.com/diegosouzapw/OmniRoute/pull/12857)) — thanks @maxmad64bis +- fix(a2a): accept the dashboard's own session cookie on /a2a so "Run message/send" no longer fails with "Unauthorized: missing or invalid API key" (#12888) ([#13271](https://github.com/diegosouzapw/OmniRoute/pull/13271)) +- **fix(logs):** the Logs grid's in-memory filter pass no longer discards rows the SQL query already matched — selecting an API key from the dropdown (which sends the key's id) returns its calls again, the Combo tab shows every combo instead of only those whose name contains a "1", and the model filter and search cover the same columns as the query ([#12896](https://github.com/diegosouzapw/OmniRoute/pull/12896)) — fixes [#12873](https://github.com/diegosouzapw/OmniRoute/issues/12873) — thanks @ntdatt812 +- **fix(a2a):** `/api/a2a/status` now builds the agent card from the request that asked for it, so a gateway reached at a non-localhost host no longer advertises `http://localhost:20128` as its A2A URL ([#12918](https://github.com/diegosouzapw/OmniRoute/pull/12918)). — thanks @ntdatt812 +- **fix(compression):** progressive aging now appends its `[COMPRESSED:aging:…]` annotation after a turn's `tool_result` blocks instead of in front of them, so Anthropic no longer rejects aged conversations with "`tool_use` ids were found without `tool_result` blocks immediately after" ([#12920](https://github.com/diegosouzapw/OmniRoute/pull/12920)). — thanks @ntdatt812 +- **fix(bedrock):** model import now resolves context limits for every vendor prefix instead of only `anthropic.*`, so `global.openai.gpt-5.6-*` no longer imports with a null `inputTokenLimit` and gets rejected pre-flight at the 200k default ([#12921](https://github.com/diegosouzapw/OmniRoute/pull/12921)). — thanks @ntdatt812 +- **fix(stream):** the 64 KB stream buffer GLM asks for is honoured instead of dropped, and the type error it caused no longer fails the API Route Typecheck gate on every open PR ([#12925](https://github.com/diegosouzapw/OmniRoute/pull/12925)) — thanks @ntdatt812 +- **fix(guardrails):** mask PII inside a `tool_result`'s nested content array, which the masker walked past while redacting its sibling block ([#12930](https://github.com/diegosouzapw/OmniRoute/pull/12930)) — thanks @ntdatt812 +- **fix(dashboard):** the Radar catalog table no longer leaves absent data unexplained — empty limits, unknown context windows, and unreported capabilities each explain themselves on hover, and a new check keeps it that way ([#12937](https://github.com/diegosouzapw/OmniRoute/pull/12937)) — thanks @maxmad64bis +- **fix(sse):** geo-blocked opencode requests rotate to the next account proxy instead of failing, so one refused egress no longer aborts the whole chain ([#12941](https://github.com/diegosouzapw/OmniRoute/pull/12941)) — thanks @maxmad64bis +- **fix(codex):** dashboard "clear cooldown" and the CAS recovery path now drop nested `codexScopeRateLimitedUntil` maps in the same write that nulls `rate_limited_until`, and fresh quota snapshots with headroom lift fallback-sourced scope cooldowns parked by quota preflight ([#12817](https://github.com/diegosouzapw/OmniRoute/issues/12817), [#12860](https://github.com/diegosouzapw/OmniRoute/issues/12860), [#12951](https://github.com/diegosouzapw/OmniRoute/pull/12951)) The dashboard reset-credit button (`consumeCodexResetCredit`) now exercises that same snapshot path after a successful redeem, so a filled quota bar is enough to unpark a leftover fallback Codex child without another manual clear. — thanks @HouMinXi +- **fix(db):** resolve `sql-wasm.wasm` across global npm install and hoisted layouts, ensuring OmniRoute can boot cleanly on Node 24 when native `better-sqlite3` is uncompiled. ([#13035](https://github.com/diegosouzapw/OmniRoute/pull/13035)) — thanks @HouMinXi +- **fix(api):** `DELETE /v1/batches/delete-completed` now sweeps only the calling API key's own completed batches (batches with no owner stay out of a key-scoped sweep on purpose), with an explicit instance-wide mode reserved for authenticated dashboard sessions, a 401 for a presented key that is unknown, revoked, deactivated, banned or expired (never falling through to the session branch), audit logging of every sweep, a sanitized 500 on failure, an owner-scoped file half (a key-scoped sweep never nulls a file another tenant owns) and an atomic sweep — chunked in 200-batch transactions in instance mode — so a mid-way error never leaves a batch pointing at a nulled file (GHSA-wvxc-jp3v-5mg5) ([#12969](https://github.com/diegosouzapw/OmniRoute/pull/12969)) +- **fix(combo):** quota-weighted routing stops drawing on an out-of-credit connection — a 402 now invalidates the stored quota snapshot instead of leaving its stale remaining percentage in place, and a snapshot older than 10 minutes no longer counts as confident headroom for the primary pool ([#12972](https://github.com/diegosouzapw/OmniRoute/pull/12972)) ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- **fix(vision):** Custom Models with "Vision capable" checked no longer have image requests swapped to `glm/glm-4.6v` when the client sends the advertised alias (`vllm/path/...`) or the bare path-shaped id — Vision Bridge now matches the stored override for all three id forms ([#12758](https://github.com/diegosouzapw/OmniRoute/issues/12758)) ([#12974](https://github.com/diegosouzapw/OmniRoute/pull/12974)) — thanks @HouMinXi +- **fix(sse):** transient opencode upstream failures rotate to the next account proxy instead of failing, so one flapping egress no longer aborts the whole chain ([#12975](https://github.com/diegosouzapw/OmniRoute/pull/12975)) — thanks @maxmad64bis +- **fix(azure):** Deployments from GPT-6 onward now send `max_completion_tokens` instead of `max_tokens`, which Azure rejects with HTTP 400. The rule matched a literal `gpt-5`, so each new generation arrived broken; it now matches the generation range, while `gpt-35-turbo` still keeps `max_tokens`. ([#13007](https://github.com/diegosouzapw/OmniRoute/pull/13007)) — thanks @ntdatt812 +- **fix(db):** add an opt-in self-restart circuit for sustained critical memory pressure, gate post-cleanup VACUUM behind a minimum freed-rows threshold, and checkpoint the SQLite WAL every 5 minutes with a size guard that escalates to TRUNCATE, so a growing WAL can no longer stall the event loop into a full outage. ([#13011](https://github.com/diegosouzapw/OmniRoute/pull/13011)) — thanks @HouMinXi +- **fix(auth):** an explicit connection pin may probe a quota-disabled row once and re-enable it on success ([#12874](https://github.com/diegosouzapw/OmniRoute/issues/12874)) ([#13017](https://github.com/diegosouzapw/OmniRoute/pull/13017)) — thanks @HouMinXi +- **fix(api):** thread `X-OmniRoute-Fallback-Attempts` through combo chat completions so streaming and non-streaming responses report how many prior legs were attempted ([#13038](https://github.com/diegosouzapw/OmniRoute/pull/13038)) — thanks @HouMinXi +- Restore provider failure classification and credential refresh on non-streaming requests: classify non-2xx failures to lock models on per-model quota exhaustion, update connection rate limits from headers and body, and pass credential refresh handlers to pipeline execution so 401 tokens can be refreshed and retried (#13043). ([#13069](https://github.com/diegosouzapw/OmniRoute/pull/13069)) — thanks @HouMinXi +- **fix(responses):** wrap forced-non-streaming web_search fallback JSON as Responses SSE so Codex still sees `response.completed` ([#13050](https://github.com/diegosouzapw/OmniRoute/pull/13050)) — thanks @HouMinXi +- **fix(antigravity):** Preserve upstream thought-token usage in normalized completion and reasoning token counts ([#13055](https://github.com/diegosouzapw/OmniRoute/pull/13055)) — thanks @pacocartones +- Fix a concurrency-slot leak in the DeepSeek PoW solver: a worker that failed to spawn (for example a missing worker script) never released its slot, so `MAX_CONCURRENT_WORKERS` failures disabled the solver until restart. ([#13097](https://github.com/diegosouzapw/OmniRoute/pull/13097)) — thanks @anhtahaylove +- **fix(acp):** bound the ACP session output buffers — `stdoutBuffer` and `stderrBuffer` now cap at 1 MiB keeping the most recent output behind a visible `[...output truncated...]` marker, and `stderrBuffer` is reset per prompt instead of accumulating for the lifetime of the session. ([#13100](https://github.com/diegosouzapw/OmniRoute/pull/13100)) — thanks @anhtahaylove +- **fix(acp):** release the `stdout`/`exit` listeners and the idle timer that a `sendPrompt` timeout used to leave attached to the `acpManager` singleton, and drop sessions that exited on their own from the session map instead of keeping them forever. ([#13096](https://github.com/diegosouzapw/OmniRoute/pull/13096)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection and PII scanners now read the text a `tool_result` block carries on `content` (string or nested block list), in messages and in system blocks, so tool output is judged by the same rules as user text ([#13101](https://github.com/diegosouzapw/OmniRoute/pull/13101)) — thanks @ntdatt812 +- **fix(gamification):** close the badge notification SSE stream when the request signal is already aborted before the stream starts — a client that disconnects while the route is still awaiting auth used to leave both the 2s unlock poll and the 15s heartbeat running for the lifetime of the process. ([#13106](https://github.com/diegosouzapw/OmniRoute/pull/13106)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection scan now spends its 16 KB budget on both ends of the request instead of the first 16 KB only, so `system`, `instructions`, `query`, `documents` and the newest turns are no longer hidden behind one long message ([#13104](https://github.com/diegosouzapw/OmniRoute/pull/13104)) — thanks @ntdatt812 +- **fix(dashboard):** expose the Volcano Ark console cookie on quota scraping and unwrap connect-error objects so the dashboard shows the upstream message ([#13107](https://github.com/diegosouzapw/OmniRoute/pull/13107)) — thanks @HouMinXi +- **fix(db):** release the `beforeExit`/`SIGINT`/`SIGTERM` handlers when a `node:sqlite` adapter closes, so a closed adapter and its database handle are no longer pinned to `process` for the lifetime of the run — the same treatment #7494 gave the sql.js adapter. ([#13109](https://github.com/diegosouzapw/OmniRoute/pull/13109)) — thanks @anhtahaylove +- **fix(translator):** `contentSchema` and `unevaluatedItems` are now treated as subschema positions by the tool-schema sanitizer, so a truncation placeholder in either is replaced with a permissive schema instead of being forwarded as a string ([#13110](https://github.com/diegosouzapw/OmniRoute/pull/13110)) — thanks @ntdatt812 +- **fix(cli-helper):** clear the `createLogStream` timeout on the abort path — `stop()` aborts the in-flight fetch and returned through the `signal.aborted` branch, which skipped `clearTimeout` and left an armed timer per stopped stream. The stream reader is now also cancelled when the read loop exits early. ([#13114](https://github.com/diegosouzapw/OmniRoute/pull/13114)) — thanks @anhtahaylove +- **fix(docs):** drop TLS-impersonation, MITM-decrypt, supply-chain attestation, and XOR-mask writeups from the public `/docs` catalog and Docker image. Files stay in git for engineers; operators who need them open the repo, not the website. ([#13136](https://github.com/diegosouzapw/OmniRoute/pull/13136)) — thanks @HouMinXi +- **fix(combo):** parse numeric-epoch `rate_limited_until` in the combo cooldown read path ([#13141](https://github.com/diegosouzapw/OmniRoute/pull/13141)) — thanks @maxmad64bis +- **fix(opencode):** both OpenCode plugins now reject a gateway address typed without `http://` at configuration time, instead of publishing every model with an api url no client can call, and the v2 plugin no longer publishes a model card whose api url is blank or relative ([#13142](https://github.com/diegosouzapw/OmniRoute/pull/13142)) — thanks @maxmad64bis +- **fix(providers):** lock opencode model on upstream 400 model-unavailable ([#13146](https://github.com/diegosouzapw/OmniRoute/pull/13146)) — thanks @maxmad64bis +- **fix(logging):** keep the provider exchange rather than the raw client bodies when a call log exceeds its size budget, and show that recovered payload in the request-detail panel instead of replacing it with the stored response body ([#13147](https://github.com/diegosouzapw/OmniRoute/pull/13147)) — thanks @maxmad64bis +- **fix(traffic-inspector):** the WebSocket route no longer leaks a traffic-buffer subscriber and a 30s ping timer when the client socket is already closed at handler time — listeners are attached before any resource is acquired, a destroyed socket bails out early, and the ping interval stops on a dead socket where `write()` never throws ([#13155](https://github.com/diegosouzapw/OmniRoute/pull/13155)) — thanks @anhtahaylove +- **fix(telegram):** bound the per-user API key cache in the Telegram chat proxy so a burst of distinct chat ids can no longer grow the process heap without limit ([#13165](https://github.com/diegosouzapw/OmniRoute/issues/13165)) ([#13166](https://github.com/diegosouzapw/OmniRoute/pull/13166)) — thanks @anhtahaylove +- **fix(stream):** cancel the upstream response body when the JSON-to-SSE sniff unwinds on a body timeout, so a stalled upstream no longer pins the connection ([#13169](https://github.com/diegosouzapw/OmniRoute/issues/13169)) ([#13171](https://github.com/diegosouzapw/OmniRoute/pull/13171)) — thanks @anhtahaylove +- **fix(telegram):** authenticate webhook deliveries with Telegram's `secret_token` so an unauthenticated caller can no longer mint API keys or spend upstream quota ([#13172](https://github.com/diegosouzapw/OmniRoute/issues/13172)) ([#13175](https://github.com/diegosouzapw/OmniRoute/pull/13175)) — thanks @anhtahaylove +- **fix(models):** give discoverable Gemini 3.8 Flash ids their own 65536 output spec so Antigravity no longer clamps them to 16384 ([#13195](https://github.com/diegosouzapw/OmniRoute/pull/13195)) — thanks @HouMinXi +- **fix(dashboard):** leftover catalog-removed provider rows (gemini-cli) can be listed and purged from the providers page ([#13067](https://github.com/diegosouzapw/OmniRoute/issues/13067)) ([#13197](https://github.com/diegosouzapw/OmniRoute/pull/13197)) — thanks @HouMinXi +- **fix(auth):** a dashboard session now requires the `authenticated: true` claim that login, OIDC and the session refresh already emit — a JWT merely signed with `JWT_SECRET` (for example the Cursor CLI passthrough token, which any API-key holder can obtain) no longer verifies as the `auth_token` cookie on any route, the WebSocket handshake or the live server; existing sessions keep working ([#13298](https://github.com/diegosouzapw/OmniRoute/issues/13298)) ([#13375](https://github.com/diegosouzapw/OmniRoute/pull/13375)) +- **fix(api):** `DELETE /v1/batches/delete-completed` now applies the caller's API-key policy (endpoint allowlist, schedule, usage cap, rate limit) like every other `/v1` route, commits the key-scoped sweep per 200-batch chunk instead of holding one write lock for the whole sweep, refuses to loop without progress, and rejects a scope that names both a key and `allTenants` ([#13684](https://github.com/diegosouzapw/OmniRoute/pull/13684)) +- Restore the API-route typecheck gate: the non-streaming leg result lost its discriminated-union narrowing after the server-owned tool loop reassignment, producing 13 new TS2339 diagnostics in `chatCore.ts`. ([#12963](https://github.com/diegosouzapw/OmniRoute/pull/12963)) +- Restore four non-streaming behaviours the server-owned tool loop refactor dropped: client aborts map to 499 with the fixed `Request aborted` message, an aborted request no longer logs a synthetic `clientResponse`, Claude prompt-cache telemetry is recorded again, and a body that cannot be canonicalized no longer throws when the tool loop is off. ([#12990](https://github.com/diegosouzapw/OmniRoute/pull/12990)) +- **fix(skills):** The CLI registry parser now reads positionals declared with `.addArgument()`, not only those written inline in `.command()`. `tunnel create [type]` was being published as `tunnel create`, so the agent-skills sync gate reported drift on every branch and regenerating would have deleted the argument. ([#13009](https://github.com/diegosouzapw/OmniRoute/pull/13009)) — thanks @ntdatt812 +- **fix(combos):** dashboard combo test uses a short prompt, serial probes, and a 60s timeout so reasoning models and rate-limited free pools do not fail the health check ([#13001](https://github.com/diegosouzapw/OmniRoute/pull/13001)) — thanks @HouMinXi +- fix(compression): terminate idle worker threads on eviction so long-running instances stop leaking OS threads and MessagePorts ([#13091](https://github.com/diegosouzapw/OmniRoute/pull/13091)) — thanks @anhtahaylove +- Redact Google API keys of any length in error bodies: the pattern required exactly 39 characters, so shorter or longer `AIza…` credentials passed through unredacted. ([#12964](https://github.com/diegosouzapw/OmniRoute/pull/12964)) +- **fix(grok-cli):** a 402 "Grok Build usage balance exhausted" parks that Grok login as out of credit (Grok Build CLI, grok.com cookie, and xAI OAuth share the weekly pool). Combo routing then tries the next login instead of locking the model for every account in the pool ([#13061](https://github.com/diegosouzapw/OmniRoute/pull/13061)) — thanks @HouMinXi +- fix(compression): spawn the LLMLingua worker with a file URL object so compression actually runs instead of silently failing open on Node ([#13093](https://github.com/diegosouzapw/OmniRoute/pull/13093)) — thanks @anhtahaylove +- **fix(test):** run the local `test` and `test:unit` scripts at concurrency 4 so a full-suite run no longer exhausts the machine's commit charge and kills unrelated processes ([#13187](https://github.com/diegosouzapw/OmniRoute/pull/13187)) — thanks @anhtahaylove +- **fix(monitoring):** `GET /api/monitoring/health` `credentialHealth` now includes a bounded `failedConnections` list (`connectionId`, `status`, sanitized `lastError`) when the probe-cache gauge `failed>0`, plus `source: probe-cache` and a cheap `staleDbNonOkCount` for sticky SQLite `test_status` on active rows. Documents that the live gauge is not `provider_connections.test_status`. ([#12876](https://github.com/diegosouzapw/OmniRoute/pull/12876)) — thanks @RaviTharuma +- **fix(resilience):** an openai-compatible multi-upstream gateway no longer marks the whole connection `credits_exhausted` when a single upstream (or the `/models` / representative-model health probe) returns 402. Quota failures stay model-scoped; true connection-wide auth failures are unchanged. Single-credential 402 key disable ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239)) is preserved. ([#12875](https://github.com/diegosouzapw/OmniRoute/pull/12875)) — thanks @RaviTharuma +- **fix(dashboard):** nine fixes on the Orchestration Canvas, all diagnosed in the Phase 2 reviews. A source that fails now keeps the timestamp of its FIRST failure instead of being re-stamped every poll — the stale line said "since the last poll" no matter how long the source had been down, and the churn also defeated the snapshot's stable identity (it serializes the source list), so the canvas re-rendered on every tick while anything was broken. A source that HAD data and only then started failing is flagged too: previously only a source with no node at all got the warning, so a source that went down mid-session kept a healthy-looking node forever. The rest are pointwise: clicking a filter chip cancels the pending search debounce (left armed, it fired ~300ms later and silently reverted the chip); the search input carries an accessible name; `?state=running, failed` parses like the unpadded form instead of dropping the padded value; the CSV toggle helper is defined once in `model/urlParams.ts` rather than twice; the agents tab tells "nothing running" apart from "the filter matched nothing", offering a clear-filters button instead of setup links that would be wrong advice there; edges stop emitting SMIL particles above 40 simultaneously active edges, keeping the colored stroke; and the drawer's error banner clears when a retried action succeeds. ([#12983](https://github.com/diegosouzapw/OmniRoute/pull/12983)) +- **fix(i18n):** the nine locales added with the EU-language batch (Greek, Estonian, Irish, Croatian, Lithuanian, Latvian, Maltese, Slovenian, Serbian) were missing the eleven Orchestration Canvas keys that Phase 3 introduced, so the compare-runs panel and the "no runs match these filters" empty state fell back to English in those languages (`deepMergeFallback` substitutes English for an absent key, so nothing rendered blank — it rendered untranslated). The coverage gate does not catch this: it enforces an 80% floor per locale, and eleven missing keys out of ~13,000 leaves coverage at 99.9%. Translated for real in each language, calibrated against the wording each file already uses for "run", "filter" and "skill". ([#13294](https://github.com/diegosouzapw/OmniRoute/pull/13294)) +- **fix(dashboard):** five follow-ups from the Phase 2 Orchestration Canvas review. The History drawer now shows the memory section for runs that already left the live TTL window: the persisted `memory_hits` event is parsed into `metadata.memoryHits` with the same defensive validation the drawer applies, and — because that event is observability rather than a state transition — it no longer leaks into the timeline, where it had been inheriting the task's state and rendering as a duplicate transition. The A2A memory recall runs against its own 1.5s deadline instead of inheriting the memory backend's 30s one; overshooting degrades exactly like any other recall failure (no hits, task proceeds), and the timer is cleared on both paths. Repeating a Conductor run carries its `requirements.cli`/`requirements.model` forward, so the new run is pinned to the same runner profile and model rather than drifting to whatever the fleet picks. A successful repeat from the Agents tab now focuses the run it created, instead of leaving the operator on the finished one. And the auth test for `POST /api/conductor/tasks` moved into the shared `ROUTES` array rather than restating the pattern. ([#12988](https://github.com/diegosouzapw/OmniRoute/pull/12988)) +- fix(plugins): stop leaking an exit listener per plugin hook timeout, which triggered MaxListenersExceededWarning on plugins that ignore SIGTERM ([#13092](https://github.com/diegosouzapw/OmniRoute/pull/13092)) — thanks @anhtahaylove +- **fix(validation):** Provider node edits no longer fail with a generic "Invalid request" when the optional daily-quota reset fields are left blank. The dashboard sends `dailyQuotaResetTimezone` and `dailyQuotaResetHour` as `null`, and only the hour accepted it. ([#13066](https://github.com/diegosouzapw/OmniRoute/issues/13066)) ([#13083](https://github.com/diegosouzapw/OmniRoute/pull/13083)) — thanks @ntdatt812 +- **fix(combo):** quota-aware expansion drops banned, inactive, missing, and wrong-provider connections before quota fetch or model dispatch; pins and allowlists stay selectors, not a bypass. Antigravity automatic exhaustion now requires a reported zero remaining, so a positive balance below 1% stays eligible. ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- Fix an intermittent failure in the quota-weighted routing test suite: scores are a function of `Date.now()`, so peers with identical quota scored microseconds apart never tied and swapped order. ([#12946](https://github.com/diegosouzapw/OmniRoute/pull/12946)) +- fix(volcengine): resolve quota window mapping gaps, safe multi-connection console binding, and response cookie sanitization ([#12950](https://github.com/diegosouzapw/OmniRoute/pull/12950)) — thanks @HouMinXi +- **fix(test):** resolve the WebDAV handler path with `fileURLToPath` so the suite's 37 WebDAV tests run on Windows instead of failing with a doubled `C:\C:\` drive prefix ([#13196](https://github.com/diegosouzapw/OmniRoute/pull/13196)) — thanks @anhtahaylove +- **fix(ui):** fall back from invalid AnySearch icon alias (direct commit `9587e07b69`) +- **fix(sse):** stop rebuilding a truncated summary from the collector's cap-dropped event array ([#12718](https://github.com/diegosouzapw/OmniRoute/pull/12718)) — thanks @hartmark +- **fix(conversations):** show a pending spinner for unresolved tool nodes instead of "(empty)" ([#12727](https://github.com/diegosouzapw/OmniRoute/pull/12727)) — thanks @hartmark +- **fix(streaming):** fail fast when an upstream stream produces only lifecycle/heartbeat events, never real content ([#12741](https://github.com/diegosouzapw/OmniRoute/pull/12741)) — thanks @hartmark +- **fix(responses-continuation):** bridge the write-in-flight window with an in-memory pending store ([#12854](https://github.com/diegosouzapw/OmniRoute/pull/12854)) — thanks @hartmark +- **fix(translator):** drop or map agent_message on Chat Completions fallback ([#12880](https://github.com/diegosouzapw/OmniRoute/pull/12880)) — thanks @RaviTharuma +- **fix(api):** page and stream-complete GET /v1/models for large catalogs ([#12882](https://github.com/diegosouzapw/OmniRoute/pull/12882)) — thanks @RaviTharuma +- **fix(quota):** align AUTH window labels with usage API and clarify 503 ([#12884](https://github.com/diegosouzapw/OmniRoute/pull/12884)) — thanks @RaviTharuma +- **fix(ci):** clear two base-reds on release/v3.8.51 — mutation-coverage gate + image-only-model guard ([#12945](https://github.com/diegosouzapw/OmniRoute/pull/12945)) +- **fix(api):** scope batch bulk-delete to the calling API key ([#13211](https://github.com/diegosouzapw/OmniRoute/pull/13211)) +- **fix(tests):** align model-sync log assertion to shipped path redaction (base-red) ([#13227](https://github.com/diegosouzapw/OmniRoute/pull/13227)) +- **fix(test):** make the ToS heading guard actually require the parentheses ([#13228](https://github.com/diegosouzapw/OmniRoute/pull/13228)) +- **fix(quality):** register 4 drifted covering tests in stryker tap.testFiles ([#13229](https://github.com/diegosouzapw/OmniRoute/pull/13229)) +- **fix(logging):** mask *-api-key headers in request-log pipeline (#13273) ([#13401](https://github.com/diegosouzapw/OmniRoute/pull/13401)) — thanks @KooshaPari +- **fix(tests):** correct stale Jina catalog prefix in custom-models test ([#13403](https://github.com/diegosouzapw/OmniRoute/pull/13403)) — thanks @KooshaPari +- **fix(backend):** prune old backups after health-check-repair snapshots (#13308) ([#13404](https://github.com/diegosouzapw/OmniRoute/pull/13404)) — thanks @KooshaPari +- **fix(backend):** guard memory_fts_au trigger with WHEN clause to stop FTS bloat (#13326) ([#13405](https://github.com/diegosouzapw/OmniRoute/pull/13405)) — thanks @KooshaPari +- **fix(free-tiers):** remove obsolete Together $25 signup credit (#12526) ([#13407](https://github.com/diegosouzapw/OmniRoute/pull/13407)) — thanks @KooshaPari +- **fix:** skip non-chat generation models in Test all models ([#13409](https://github.com/diegosouzapw/OmniRoute/pull/13409)) — thanks @KooshaPari +- **fix(providers):** de-duplicate agy and antigravity model catalogs ([#13410](https://github.com/diegosouzapw/OmniRoute/pull/13410)) — thanks @KooshaPari +- **fix(dashboard):** send provider in body for mini-playground web search ([#13411](https://github.com/diegosouzapw/OmniRoute/pull/13411)) — thanks @KooshaPari +- **fix(resilience):** add CJK quota-exhaustion patterns to 429 classifier ([#13412](https://github.com/diegosouzapw/OmniRoute/pull/13412)) — thanks @KooshaPari +- **fix(backend):** eval runner: failed calls no longer score as passed + dotAll regex ([#13413](https://github.com/diegosouzapw/OmniRoute/pull/13413)) — thanks @KooshaPari +- **fix(backend):** eval runner opts out of output-style and memory injection ([#13414](https://github.com/diegosouzapw/OmniRoute/pull/13414)) — thanks @KooshaPari +- **fix:** chain serial quarantine step in npm test script (fixes #13286) ([#13418](https://github.com/diegosouzapw/OmniRoute/pull/13418)) — thanks @KooshaPari +- **fix(antigravity):** derive stable session ID from credentials for prompt-cache reuse (#13305) ([#13424](https://github.com/diegosouzapw/OmniRoute/pull/13424)) — thanks @KooshaPari +- **fix(memory):** merge memory into top-level system field instead of unshifting at messages[0] (#13425) ([#13427](https://github.com/diegosouzapw/OmniRoute/pull/13427)) — thanks @KooshaPari +- **fix(compression):** preserve instruction blocks from lossy engines ([#13523](https://github.com/diegosouzapw/OmniRoute/pull/13523)) — thanks @KooshaPari +- **fix(compression):** preserve polarity words and newlines in ultra heuristic ([#13524](https://github.com/diegosouzapw/OmniRoute/pull/13524)) — thanks @KooshaPari +- **fix(compression):** run user preservePatterns before built-in patterns (#13457) ([#13525](https://github.com/diegosouzapw/OmniRoute/pull/13525)) — thanks @KooshaPari +- **fix(reasoning):** expand deepseek-v4 regex to match v4.1+ and add command-code provider (#13430) ([#13528](https://github.com/diegosouzapw/OmniRoute/pull/13528)) — thanks @KooshaPari +- **fix(providers):** add alwaysReasons metadata for reka-flash-3 ([#13531](https://github.com/diegosouzapw/OmniRoute/pull/13531)) — thanks @KooshaPari +- **fix(playground):** allow Compare columns to scroll independently ([#13532](https://github.com/diegosouzapw/OmniRoute/pull/13532)) — thanks @KooshaPari +- **fix(security):** empty whitelist allows through to prevent admin lockout ([#13534](https://github.com/diegosouzapw/OmniRoute/pull/13534)) — thanks @KooshaPari +- **fix(tests):** add DISABLE_IOREG_STRATEGY env var for macOS test compatibility (fixes #13467) ([#13539](https://github.com/diegosouzapw/OmniRoute/pull/13539)) — thanks @KooshaPari +- **fix(zed-hosted):** map developer role and Gemini enums for Zed proxy ([#13543](https://github.com/diegosouzapw/OmniRoute/pull/13543)) — thanks @KooshaPari +- **fix(analytics):** normalize provider aliases to canonical id in usage_history ([#13545](https://github.com/diegosouzapw/OmniRoute/pull/13545)) — thanks @KooshaPari +- **fix(combo):** use traceId for call log id to prevent UNIQUE constraint failure ([#13546](https://github.com/diegosouzapw/OmniRoute/pull/13546)) — thanks @KooshaPari +- **fix(auth):** check model lockout before returning synthetic noauth connection ([#13547](https://github.com/diegosouzapw/OmniRoute/pull/13547)) — thanks @KooshaPari +- **fix(guardrails):** skip credential redaction for base64 image data URLs ([#13550](https://github.com/diegosouzapw/OmniRoute/pull/13550)) — thanks @KooshaPari +- **fix(resilience):** increase requestQueue.maxWaitMs default from 15s to 30s ([#13553](https://github.com/diegosouzapw/OmniRoute/pull/13553)) — thanks @KooshaPari +- **fix(docs):** document OMNIROUTE_READY_TIMEOUT_MS and allowlist the test-only DISABLE_IOREG_STRATEGY ([#13692](https://github.com/diegosouzapw/OmniRoute/pull/13692)) + ### 📝 Maintenance ---- +- **docs(readme):** reconcile live v3.8.50 provider, free-tier, CLI, routing, test, community, sponsor, acknowledgment, and SVG metrics with their audited source denominators, including a deduplicated OmniRoute-in-Action snapshot and distinct contributor rankings for merged pull requests, GitHub-attributed commits, and Git history ([#11356](https://github.com/diegosouzapw/OmniRoute/pull/11356)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **docs:** sync the canonical provider count 354 → 356 across `README.md`, `AGENTS.md`, `llm.txt` (+ 42 i18n mirrors), the four README SVG diagrams, `docs/reference/PROVIDER_REFERENCE.md` (regenerated) and the `package.json` description after Opper (#11629) and 1min.ai (#11631) boarded the catalog — closes the `check:docs-counts-sync` strict drifts that kept `release/v3.8.51` red ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11673](https://github.com/diegosouzapw/OmniRoute/pull/11673)) — thanks @jonlwheat2-gif +- **test(providers):** update count-derived assertions after the v3.8.51 provider additions — `APIKEY_PROVIDERS` 233 → 235 (Opper #11629 + 1min.ai #11631), reserved-prefix REGISTRY walk 395 → 398, `WEB_FETCH_PROVIDERS` now includes `nimble-search` (#11620), and the provider translate-path golden snapshot regenerated ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11674](https://github.com/diegosouzapw/OmniRoute/pull/11674)) — thanks @jonlwheat2-gif +- **test(sse):** bump the hard-lease connection-query inventory for `src/lib/tokenHealthCheck.ts` to 2 — the verify-only web-cookie sweep added by #11495 split the single `getProviderConnections` call into oauth + cookie variants, which the frozen inventory had not tracked ([#11495](https://github.com/diegosouzapw/OmniRoute/pull/11495)). ([#11677](https://github.com/diegosouzapw/OmniRoute/pull/11677)) — thanks @jonlwheat2-gif / @oyi77 +- **chore(release):** clear the `release/v3.8.51` base-red gates — docs migration count (+38 i18n mirrors), stryker `tap.testFiles`, lockfile registry host, stale ESLint suppressions, the 7 lint regressions they were hiding, the stale `APIKEY_PROVIDERS` partition count (231 → 233), the `glm-5.3-max` tier missing from the GLM test table, a dead `modelType === "chat"` comparison in the models catalog, and the `resolveLiveWsUrl` / `sanitizeLiveWsPort` helpers from #11388 that a later merge dropped from `wsPath.ts` (dashboard live WebSocket URL resolution) ([#11502](https://github.com/diegosouzapw/OmniRoute/pull/11502)) — thanks @MumuTW +- **chore(free-models):** the free-tier regime table now declares which totals figure each regime feeds and whether it may take the no-auth shortcut, so the sets that used to repeat those answers by hand are derived from it — a new `freeType` no longer compiles until it has answered every question, instead of silently contributing to no total ([#11537](https://github.com/diegosouzapw/OmniRoute/pull/11537)) — thanks @maxmad64bis +- **docs(routing):** LKGP strategy copy now states provider-level stickiness and the explicit rules fallback instead of promising per-target pinning ([#11555](https://github.com/diegosouzapw/OmniRoute/pull/11555)) — thanks @maxmad64bis +- **test(chatcore):** pin the response tool-name alias resolution against the ordering hazard that broke every Gemini/Antigravity MCP tool call in v3.8.49. `extractRequestToolIdentityMap` deletes `translatedBody._toolNameMap`, so the later read is always undefined and the ledger survives only through the `requestToolIdentityMap` fallback — removing that fallback previously left the entire tool-name suite green. The resolution moves into `resolveResponseToolNameMap()` next to the map it depends on, with a regression guard that fails without the recovery. No behaviour change. ([#11575](https://github.com/diegosouzapw/OmniRoute/pull/11575)) — thanks @amartinawi +- **docs(env):** document `OMNIROUTE_CORPUS_CACHE_SIZE` in `.env.example` and `ENVIRONMENT.md`, so the env/docs contract check stops failing Docs Gates on every branch ([#11576](https://github.com/diegosouzapw/OmniRoute/pull/11576)) — thanks @ntdatt812 +- **chore(stryker):** register `route-guard-tunnel-processes-local-only` and `universal-quota-aware-routing` in `tap.testFiles`, so their mutant kills count for the three modules they cover ([#11577](https://github.com/diegosouzapw/OmniRoute/pull/11577)) — thanks @ntdatt812 +- **test(authz):** pin `LOCAL_ONLY_API_GET_EXEMPTIONS` by exact membership instead of by entry count, so the guard names the offending path and also catches a substitution ([#11580](https://github.com/diegosouzapw/OmniRoute/pull/11580)) — thanks @ntdatt812 +- **test(build):** pin `resolveNextBuildBundlerFlag` to the `OMNIROUTE_USE_TURBOPACK` contract it implements, across runtimes, instead of asserting an unimplemented Bun override ([#11583](https://github.com/diegosouzapw/OmniRoute/pull/11583)) — thanks @ntdatt812 +- **test(claude-web):** the Claude Web transport test's barrel anchors no longer pin the exact construction spelling. #11421 made the executor map lazy, so `/"claude-web": new ClaudeWebExecutor\(\)/` stopped matching and the test went red on `release/v3.8.51` itself — a base-red gate every open PR inherited. The anchors now match the alias key and the class name and tolerate either spelling in between, and were checked to still fail when an alias is genuinely repointed at another executor ([#11585](https://github.com/diegosouzapw/OmniRoute/pull/11585)) — thanks @ntdat812 +- **test(models):** pin `qwen3.8-max` resolution per provider — two of the four catalogs now serve the bare id and no longer carry `-preview` ([#11590](https://github.com/diegosouzapw/OmniRoute/pull/11590)) — thanks @ntdatt812 +- **test(executors):** drop the stale 128-tool truncation assertion that contradicted the #11444 pass-through contract ([#11591](https://github.com/diegosouzapw/OmniRoute/pull/11591)) — thanks @ntdatt812 +- **test(token-health):** pin the current terminal-skip boundary — expired with retry budget is probed, `account_deactivated` stays skipped ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)) — thanks @ntdatt812 +- **test(models):** follow #11503's retarget of the `gemini-3-pro-high` alias to the hyphenated catalog id ([#11593](https://github.com/diegosouzapw/OmniRoute/pull/11593)) — thanks @ntdatt812 +- **refactor(video bridge):** extract per-video acquisition, whole-result caching, description, and metrics/abort/cleanup out of `VideoBridgeGuardrail.preCall` into a `processVideoPart` seam in a new `videoBridgePipeline.ts`, behind explicit `VideoMediaBrokerPort`, `VideoAudioTranscriptionPort`, and `VideoDrilldownPort` boundaries; `preCall` now only handles request traversal, policy, and response aggregation. The Video tab's FFmpeg/ffprobe runtime status is now an explicit `unknown` / `restricted` / `unavailable` / `available` state instead of a nullable boolean pair, fixing a case where an in-flight or failed probe was mislabeled as "install FFmpeg" ([#11657](https://github.com/diegosouzapw/OmniRoute/issues/11657)). ([#12016](https://github.com/diegosouzapw/OmniRoute/pull/12016)) +- **test(opencode-plugin):** add unit test coverage for `formatFreeBudget()` naming helper ([#11660](https://github.com/diegosouzapw/OmniRoute/pull/11660)) — thanks @f9td56dbgh-hub +- **docs(video):** clarify that the Video Bridge transcript `source` field (`client`, `embedded`, `audio-bridge`) is presently caller-declared and not yet server-verified — OmniRoute enforces the enum shape but does not cryptographically confirm that an `embedded`/`audio-bridge` label came from a server-owned extraction ([#11661](https://github.com/diegosouzapw/OmniRoute/issues/11661)). ([#12001](https://github.com/diegosouzapw/OmniRoute/pull/12001)) +- **chore(providers):** retire the distributed Raycast Relay and Hailuo Web integrations after a provenance audit confirmed substantial derivation from GPL-3.0 sources; the official GitHub and MiniMax API providers remain available. Migration 163 disables legacy connections and invalidates their active leases while preserving connection IDs, audit/history rows, quota snapshots, usage records, and fail-closed API-key connection policies. ([#11691](https://github.com/diegosouzapw/OmniRoute/pull/11691)) +- **chore(providers):** retire the Felo Web provider while its GPL-derived provenance remains on licensing HOLD ([#11698](https://github.com/diegosouzapw/OmniRoute/pull/11698)) — stale runtime and database references now fail closed while existing connection, lease, and usage history remains preserved for auditability. +- **fix(sse):** retire the Gemini Web image-generation bridge while provenance and licensing remain on HOLD, preserving Gemini Web chat/auth/tools and legitimate Gemini/Nano Banana image providers ([#11708](https://github.com/diegosouzapw/OmniRoute/pull/11708)) +- **chore(audio):** retire the built-in EdgeTTS provider and its 11-voice catalog while provenance/licensing review remains on HOLD; the generic `/v1/audio/speech` endpoint and the other speech providers remain available ([#11711](https://github.com/diegosouzapw/OmniRoute/pull/11711)) +- **fix(providers):** retire the unofficial Qwen Web integration and its `qwen-web`/`qw` identities while third-party provenance remains on HOLD ([#11713](https://github.com/diegosouzapw/OmniRoute/pull/11713)) — direct requests now fail closed with a sanitized HTTP 410 and combos skip the retired target, while migration guards preserve connection IDs, allowlists, usage/audit history and invalidate active leases. Supported Qwen API, Qwen Code, cloud, token-plan, audio, local and model-name surfaces remain available; restoring Qwen Web requires a qualified provenance/license decision. +- **fix(providers):** retire `microsoft-designer-web` and its `msdesigner` alias from runtime and catalogs while preserving stored credentials, provider history and allowlists as inactive records — source dependency on gpt4free's [`MicrosoftDesigner.py`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/g4f/Provider/needs_auth/MicrosoftDesigner.py) is proven at snapshot `0943d16eef757a44deb60651c07bbe5fa958cafe`, whose [`LICENSE`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/LICENSE) is GPL-3.0; expressive adaptation is probable, which records provenance/licensing risk without asserting a legal conclusion ([#11720](https://github.com/diegosouzapw/OmniRoute/pull/11720)). +- **docs(legal):** centralize pinned MIT notices for the vendored GCF codec and local flag assets ([#11726](https://github.com/diegosouzapw/OmniRoute/pull/11726)) +- Split the 2,952-line `adobeFireflyClient.ts` into nine focused modules (catalog, credentials, payload, ARP session, upload, responses, IMS, account, poll) behind an unchanged barrel — no consumer edits and no public API change (#11728). — thanks @MumuTW +- **fix(dashboard):** replaced the Model Lockout card's unprovenanced notification MP3 with a short, locally synthesized Web Audio chime ([#11731](https://github.com/diegosouzapw/OmniRoute/pull/11731)) — both toggles retain optional audible feedback without shipping replacement media or third-party code, and unsupported, suspended, or rejected audio contexts fail open without blocking the setting change. +- **chore(deepseek-web):** remove the provenance-unresolved DeepSeek PoW WASM binary and its runtime loader/tracing while retaining the existing JavaScript solver (slower at high difficulty) ([#11732](https://github.com/diegosouzapw/OmniRoute/pull/11732)). +- **chore(assets):** remove four unused provider assets whose redistribution provenance could not be verified while retaining the active Freebuff SVG ([#11735](https://github.com/diegosouzapw/OmniRoute/pull/11735)) +- **docs(assets):** retired two raster images whose redistribution provenance could not be verified ([#11736](https://github.com/diegosouzapw/OmniRoute/pull/11736)) — PicoClaw documentation now reuses the existing SVG, while Zoo Code remains a text link without an image. +- **docs(legal):** add pinned notices for six LobeHub-derived provider SVGs and 65 byte-exact theSVG assets, retaining registry claims as unverified metadata with explicit trademark and HOLD boundaries ([#11737](https://github.com/diegosouzapw/OmniRoute/pull/11737)). +- **chore(quality):** added a versioned 225/225 provider-asset provenance manifest and blocking drift gate ([#11743](https://github.com/diegosouzapw/OmniRoute/pull/11743)) — the inventory pins magic MIME and SHA-256 for every physical file, records immutable source matches for 71 assets, keeps 69 probable and 85 unresolved entries explicit but non-blocking, and treats provenance as source evidence rather than copyright or trademark clearance. +- **refactor(mitm):** replace four previously derived Traffic Inspector utilities — conversation normalization, SSE merging, secret masking, and header sanitization — with independent clean-room implementations based on public provider schemas, WHATWG SSE, and RFC 6750, while keeping the two independently classified passthrough surfaces unchanged ([#11748](https://github.com/diegosouzapw/OmniRoute/pull/11748)). +- Added a unit test that fails seven days before any dated pack under `config/` (`validUntil` and sibling keys) lapses, naming the file and key. The Alibaba free-tier pack expired on 2026-08-27 and turned every PR red the next morning with no commit involved; renewal now happens on someone's terms, not the clock's. ([#11891](https://github.com/diegosouzapw/OmniRoute/pull/11891)) +- `check:workflows` now fails (under `--strict`/`--ratchet`) when any job routed to a self-hosted runner publishes with `--provenance` — npm rejects that with `422` at the registry, which in v3.8.50 only surfaced after the tag and Docker images were public. ([#11895](https://github.com/diegosouzapw/OmniRoute/pull/11895)) +- `scripts/ops/runner-janitor.sh` now proves a path is idle with one `lsof` snapshot and removes stale leftovers itself (tmpfs after 3 h — it is RAM — disk after 24 h), kills orphan `next-build` processes, prunes checkouts of stopped runners, and alerts on memory pressure; `--dry-run` shows exactly what it would do. `docs/ops/RUNNER_BOX.md` reconciled to the measured box (31 GB, 10 listeners). ([#11893](https://github.com/diegosouzapw/OmniRoute/pull/11893)) +- The `next-build` artefact (1.3 GB) is now written and read under `$RUNNER_TEMP` (per-runner, on disk) instead of `/tmp`, which on the self-hosted pool is a 12 GB tmpfs in RAM. Landing it there took 27–32 of the publish job's 76 minutes, and the fixed `/tmp/e2e-build.tar.gz` name let E2E jobs on different runners overwrite each other's download. ([#11896](https://github.com/diegosouzapw/OmniRoute/pull/11896)) +- The CI `build` job now runs in two concurrency lanes — `main` and pull requests — so a release build is never queued behind (or OOM-killed beside) PR builds on the self-hosted pool, which holds one `next-build` comfortably and two at the edge. ([#11901](https://github.com/diegosouzapw/OmniRoute/pull/11901)) +- Re-freeze the ESLint suppressions on `release/v3.8.51` from a clean-room measurement (2 stale file entries pruned, 55 pre-existing `no-explicit-any` in six new files frozen under #11924) and drop the dead `GPT_SIZE_MAP` constant orphaned by the Adobe Firefly client split, so `No new ESLint warnings` stops failing every PR with exit 2 (Refs #11924) ([#11955](https://github.com/diegosouzapw/OmniRoute/pull/11955)) +- Type the 55 `no-explicit-any` sites that had been frozen under #11924 — four redundant casts in `socksConnectorWithFamily.ts` (undici/socks types already accept them) and the mocks/fixtures of the socks-timeout and isFree suites — and drop their suppression entries; the ESLint ratchet shrinks from 5487 to 5432 (Closes #11924) ([#11975](https://github.com/diegosouzapw/OmniRoute/pull/11975)) +- Take the two hosted-runner builds off the PR rail: `Build App` (`build.yml`) is `workflow_dispatch`-only and `DAST smoke (PR)` runs only for PRs into `main` — the 7 GB hosted VM cannot build `release/v3.8.51` in any profile (19/30 red, VM shutdown ~8 min into `next build`) and both checks had turned into permanent noise on every release PR; the bundle stays validated by `ci.yml` on `main` and by `nightly-release-green` on `release/**` (Closes #11946) ([#11962](https://github.com/diegosouzapw/OmniRoute/pull/11962)) +- Move the four nightly jobs that build the backend (`nightly-schemathesis`, `nightly-llm-security` promptfoo + garak, `nightly-resilience` axe-a11y) off the hosted 7 GB runner — where they died on `release/v3.8.51` unseen — onto the box's new `omni-light` pool (two listeners), and document the reshaped fleet (4 active OmniRoute listeners: 2 `omni-build` + 2 `omni-light`, janitor ceiling 4) (Closes #11965) ([#11967](https://github.com/diegosouzapw/OmniRoute/pull/11967)) +- Stop painting every fork PR into `release/**` red: `quality.yml` `Build (advisory)` is skipped (GitHub still reports `continue-on-error` failures as check FAILURE). Hosted `ubuntu-latest` cannot finish `npm run build` on this tree — same class as #11962 taking `build.yml` off the PR rail. `docker-publish.yml` amd64 now runs on the `.113` `omni-build` pool (31 GB / 32 cores, two listeners) with Turbopack, shares the `heavy-build-main` lane with `ci.yml` `Build` so it queues instead of becoming a third heavy, and keeps per-ref concurrency (a merge storm was starting 8 concurrent OOM builds). arm64 stays on `ubuntu-24.04-arm` with webpack — there is no ARM box. Fallback when `USE_VPS_RUNNER` is off: hosted amd64 + webpack (#11976). ([#12021](https://github.com/diegosouzapw/OmniRoute/pull/12021)) +- `docker-publish.yml` builds with webpack (`OMNIROUTE_USE_TURBOPACK=0`) on **both** arches, including amd64 on the `.113` `omni-build` pool. Turbopack had the RAM (31 GB, `omniroute-113-6`) and still panicked (`TurbopackInternalError: there must be a path to a root`, run 33253576569, 26 min in). The same tree's arm64 webpack build on hosted `ubuntu-24.04-arm` succeeded (run 33264823398). Dockerfile already documents webpack as the Docker escape hatch (#11976). ([#12050](https://github.com/diegosouzapw/OmniRoute/pull/12050)) +- **docs(free-tier):** declare the counting vs deciding regimes for "is it free?" and guard the deciding path from DB-backed catalog resolution ([#12226](https://github.com/diegosouzapw/OmniRoute/pull/12226)) — thanks @maxmad64bis +- **docs(auto-combo):** The mode pack table in `docs/routing/AUTO-COMBO.md` now lists all six shipped packs with every weight each one sets, replacing a four-pack table whose numbers had also drifted from the source. It states plainly that no pack sets `quality`, so selecting any pack silences the observed-quality signal. Six more documents that quote the scoring factor count joined the `check:docs-counts` gate, which caught five stale claims — including one naming nine factors that do not exist — and two stale mode pack counts. The dashboard routing panel, which offered four of the six packs and labelled the default strategy "6-Factor Scoring", is now covered by a test; the two packs it was missing are `reliability-first` and `chaos-mode`, the latter labelled as the fault-injection profile it is rather than as one more routing preference ([#12316](https://github.com/diegosouzapw/OmniRoute/pull/12316)) — thanks @maxmad64bis +- **chore(quality):** rebaseline `src/lib/db/apiKeys.ts` for the ACL the key-creation path now preserves ([#12352](https://github.com/diegosouzapw/OmniRoute/pull/12352)) ([#12673](https://github.com/diegosouzapw/OmniRoute/pull/12673)) — thanks @KrzysiekSko +- **chore(providers):** bump the Claude Code wire identity and the Devin bridge image pin from `2.1.220` to `2.1.258` ([#12402](https://github.com/diegosouzapw/OmniRoute/pull/12402)) — thanks @ggiak ([#12604](https://github.com/diegosouzapw/OmniRoute/pull/12604)) +- **docs(env):** align `.env.example`, the README Bun section, and the troubleshooting guide with the code: `OMNIROUTE_USE_TURBOPACK` also governs `npm run build` (not dev-only), `bun run build` follows that flag instead of auto-selecting Webpack, `OMNIROUTE_CHAT_MAX_HEAVY_IN_FLIGHT` is unset by default (no request-count cap), and the structural `503 chat_admission_busy` message matches `chatAdmissionResponses.ts` (#12404 — thanks @pacocartones) +- **chore(electron):** upgrade the desktop app to Electron 44 (Chromium 152, Node 24.18.1) ([#12217](https://github.com/diegosouzapw/OmniRoute/pull/12217)). **Requires macOS 13 (Ventura) or later** — Chromium dropped macOS 12 (Monterey), so Monterey users must stay on an earlier OmniRoute desktop build. Windows and Linux are unaffected; the app already shipped only x64/arm64, so Electron 44 dropping 32-bit builds changes nothing. Removes the `openAsHidden`/`wasOpenedAsHidden` login-item fields deleted in Electron 44 — hidden autostart continues to work through the `--hidden` argument registered with the login item ([#12554](https://github.com/diegosouzapw/OmniRoute/pull/12554)) — thanks @app/dependabot +- **chore(quality):** rebaseline `src/sse/handlers/chat.ts` for the effective-input persistence the continuation fix needs ([#12641](https://github.com/diegosouzapw/OmniRoute/pull/12641)) ([#12680](https://github.com/diegosouzapw/OmniRoute/pull/12680)) — thanks @hartmark +- `Coverage` job on `ci.yml`: the informational Codecov upload gets its own 5-minute ceiling and `continue-on-error`, and the job budget grows from 20 to 30 minutes (the 8-shard c8 merge alone takes ~10) — a stalled upload no longer ends the job `cancelled` and drags a fully green `main` run's conclusion down with it ([#11972](https://github.com/diegosouzapw/OmniRoute/pull/11972)) +- CI hardening on the PR rail: the four `Unit Tests fast-path` shards get `timeout-minutes: 30` (a hung shard held a PR for 64 min instead of GitHub's 6 h default) and both ESLint file caches lose their `restore-keys` fallback, so a cache built under another suppressions file or lint config can no longer report stale verdicts (Refs #11600, #11924) ([#11963](https://github.com/diegosouzapw/OmniRoute/pull/11963)) +- Every CI job that runs a `next build` (`build`, the npm `publish`, both release-green validations) now targets the `omni-build` runner label, which only two of the eight self-hosted runners carry. The box holds one build comfortably and two at the edge; a third now queues on GitHub instead of being OOM-killed by the kernel. ([#11932](https://github.com/diegosouzapw/OmniRoute/pull/11932)) +- **refactor(combo):** move `handleRoundRobinCombo` (and `resolveTargetTokenLimit`) into `open-sse/services/combo/roundRobinCombo.ts`. `combo.ts` drops from 2164 to 1014 lines (`split("\n").length`); the leaf is 1199 (under the 1200 new-file cap). The round-robin call site uses a dynamic `import()` so `releaseStickyPinOnFailure` / `clearStaleLKGP` can stay exported from `combo.ts` without a static cycle. Skip / sticky / semaphore / safety-timer behavior is unchanged. ([#12811](https://github.com/diegosouzapw/OmniRoute/pull/12811)) — thanks @HouMinXi +- **chore(quality):** rebaseline the file-size caps the error-boundary campaign grew past (`open-sse/executors/codex.ts`, `open-sse/vendor/codex-chatgpt-web/bridge.ts`, both via [#12444](https://github.com/diegosouzapw/OmniRoute/pull/12444)) ([#12654](https://github.com/diegosouzapw/OmniRoute/pull/12654)) +- **chore(quality):** rebaseline the file-size caps the hartmark batch grew past (`combos/page.tsx` via [#12355](https://github.com/diegosouzapw/OmniRoute/pull/12355), `open-sse/services/combo.ts` via [#12338](https://github.com/diegosouzapw/OmniRoute/pull/12338)) ([#12623](https://github.com/diegosouzapw/OmniRoute/pull/12623)) — thanks @hartmark +- **chore(quality):** rebaseline the file-size caps the HouMinXi batch grew past when its PRs stacked (`providers/page.tsx`, `chatCore.ts`, `accountFallback.ts`) — each PR measured correctly in isolation, none saw the stacking ([#12619](https://github.com/diegosouzapw/OmniRoute/pull/12619)) +- **chore(quality):** rebaseline `open-sse/services/combo.ts` for the reset-aware scoring the HouMinXi batch stacked ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) ([#12678](https://github.com/diegosouzapw/OmniRoute/pull/12678)) — thanks @HouMinXi +- **docs:** sync provider catalog count (direct commit `4f11b2ae3d`) +- **docs(skills):** sync omni-inference search fallback (direct commit `b4e76a7ed9`) +- **chore(lint):** prune two stale base suppressions (direct commit `1325047d56`) +- **test(auto-combo):** tolerate full-suite rotation load (direct commit `ad3e293f9f`) +- **test:** drain inherited quality snapshot reds (direct commit `21ed68d8ac`) +- **chore(quality):** rebaseline measured CLI bundle growth (direct commit `d35b3f9779`) +- **test(ui):** reproduce invalid AnySearch icon alias (direct commit `12cc6ca834`) +- **test(translator):** fix the relative imports of the relocated deferred-finish test (direct commit `777d9d1629`) +- **docs(counts):** bring the documented provider and migration counts to the measured values (direct commit `e70bea30e9`) +- **test(security):** drop the guard-target set left unused by the sync (direct commit `a32ba0e362`) +- **chore(db):** renumber model_capabilities migration 163 → 169 (cross-branch collision with 163_radar_feed_cache_generated_at) (direct commit `70f5d4cbf9`) +- **docs(cli):** document CLI_PRIME_AGENT_BIN and correct the CLI Agents count (direct commit `9058e39b61`) +- **chore(deps):** refresh runtimes and adopt ESLint 10 ([#11259](https://github.com/diegosouzapw/OmniRoute/pull/11259)) — thanks @backryun +- **refactor(local-corpus):** implement dynamic root resolution and LRU cache ([#11491](https://github.com/diegosouzapw/OmniRoute/pull/11491)) — thanks @raheemuddin786 +- **chore(quality):** prune stale eslint-suppressions entries ([#11596](https://github.com/diegosouzapw/OmniRoute/pull/11596)) +- **chore(ci):** unblock quality gates - env-doc sync + stryker tap.testFiles registration (#9282) ([#11609](https://github.com/diegosouzapw/OmniRoute/pull/11609)) — thanks @Meet6338-X +- **chore(ci):** point the circular-deps gate at dpdm's real JS entrypoint ([#11615](https://github.com/diegosouzapw/OmniRoute/pull/11615)) — thanks @benzntech +- **chore(quality):** rebaseline file-size for #11448/#11495/#11561 growth ([#11630](https://github.com/diegosouzapw/OmniRoute/pull/11630)) +- **deps:** realign onnxruntime-node pin to @huggingface/transformers' 1.24.3 lockstep ([#11633](https://github.com/diegosouzapw/OmniRoute/pull/11633)) — thanks @jonlwheat2-gif +- **test(autoCombo):** port models_dev_tier #11508 guard from node:test to vitest ([#11635](https://github.com/diegosouzapw/OmniRoute/pull/11635)) — thanks @jonlwheat2-gif +- **chore(quality):** rebaseline file-size for #11083/#11631/#11628 growth ([#11636](https://github.com/diegosouzapw/OmniRoute/pull/11636)) +- **chore(lint):** prune stale ESLint suppressions (base-red #11449) ([#11644](https://github.com/diegosouzapw/OmniRoute/pull/11644)) — thanks @jonlwheat2-gif +- **docs(changelog):** aggregate the ten v3.8.50 fragments into CHANGELOG.md ([#11683](https://github.com/diegosouzapw/OmniRoute/pull/11683)) +- **chore(search):** drop unreachable credential sweep after #11642 ([#11687](https://github.com/diegosouzapw/OmniRoute/pull/11687)) — thanks @HouMinXi +- **ci:** add API route TypeScript regression gate ([#11705](https://github.com/diegosouzapw/OmniRoute/pull/11705)) — thanks @Sabeekhann +- **docs(dedup):** correct the CodeQL claim on the tenant-namespace comment ([#11712](https://github.com/diegosouzapw/OmniRoute/pull/11712)) +- **docs(changelog):** add consolidated v3.8.50 stats, top-25 ranking and a .mailmap ([#11715](https://github.com/diegosouzapw/OmniRoute/pull/11715)) +- **docs:** recommend gstack for AI-assisted workflows ([#11770](https://github.com/diegosouzapw/OmniRoute/pull/11770)) — thanks @santosraju99-hub +- **chore(quality):** tighten complexity/cognitive-complexity ratchets to the current tip; land the missed gateways.ts rebaseline ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **docs(i18n):** finish Freepik → Magnific rebrand in locale strings and README ([#11772](https://github.com/diegosouzapw/OmniRoute/pull/11772)) — thanks @turbolego +- **refactor(auto-combo):** fix divergent scoring in combo health reporting ([#11854](https://github.com/diegosouzapw/OmniRoute/pull/11854)) — thanks @jacobsparts +- **chore(changelog):** add missing fragments for #11919/#11918/#11916 ([#11938](https://github.com/diegosouzapw/OmniRoute/pull/11938)) +- **test(check):** escape the runs-on fixture with JSON.stringify, not a quote-only replace ([#11942](https://github.com/diegosouzapw/OmniRoute/pull/11942)) (direct commit `fb7445eaa3`) +- **test(release):** align five suites with the contracts #11933, #11919 and #11876 shipped on release/v3.8.51 ([#11944](https://github.com/diegosouzapw/OmniRoute/pull/11944)) +- **chore(quality):** remove unreachable code and restore test discovery ([#11950](https://github.com/diegosouzapw/OmniRoute/pull/11950)) — thanks @backryun +- **docs(agents):** sync-back landings are fast-forward, never squash ([#11964](https://github.com/diegosouzapw/OmniRoute/pull/11964)) +- **test(infra):** retry recursive temp-dir removal instead of failing a shard on ENOTEMPTY (#11966) ([#11968](https://github.com/diegosouzapw/OmniRoute/pull/11968)) +- **docs(ops):** the .113 heavy-build ceiling is one runner, not two ([#12048](https://github.com/diegosouzapw/OmniRoute/pull/12048)) +- **test(ui):** correct inactive auto-fetch expectation ([#12098](https://github.com/diegosouzapw/OmniRoute/pull/12098)) — thanks @RaviTharuma +- **docs:** document native dependency check escape hatch ([#12101](https://github.com/diegosouzapw/OmniRoute/pull/12101)) — thanks @RaviTharuma +- **test(providers):** regenerate the translate-path golden for OrcaRouter (#11923) ([#12118](https://github.com/diegosouzapw/OmniRoute/pull/12118)) +- **chore(quality):** velocity phase — loosen every numeric baseline by 20% until v4.0, monitor headroom nightly ([#12125](https://github.com/diegosouzapw/OmniRoute/pull/12125)) +- **chore(lint):** batch 0 of #12146 — type the call-log-cap sqlite rows instead of 45 as-any casts ([#12157](https://github.com/diegosouzapw/OmniRoute/pull/12157)) +- **chore(lint):** batch 4 of #12146 — shared/components react-hooks violations resolved ([#12159](https://github.com/diegosouzapw/OmniRoute/pull/12159)) +- **chore(lint):** batch 1 of #12146 — dashboard/cli-code react-hooks violations resolved ([#12160](https://github.com/diegosouzapw/OmniRoute/pull/12160)) +- **chore(lint):** batch 3 of #12146 — dashboard/settings react-hooks violations resolved ([#12162](https://github.com/diegosouzapw/OmniRoute/pull/12162)) +- **chore(lint):** batch 2 of #12146 — resolve the react-hooks compiler violations in dashboard/providers ([#12163](https://github.com/diegosouzapw/OmniRoute/pull/12163)) +- **chore(quality):** register search-432 cooldown test in stryker tap.testFiles ([#12170](https://github.com/diegosouzapw/OmniRoute/pull/12170)) +- **chore(lint):** batch 5 of #12146 — combos, endpoint, provider-stats, api-manager and costs react-hooks violations resolved ([#12174](https://github.com/diegosouzapw/OmniRoute/pull/12174)) +- **build:** add contributor fast profile ([#12192](https://github.com/diegosouzapw/OmniRoute/pull/12192)) — thanks @rafacpti23 +- **docs(readme):** README touch-ups ([#12193](https://github.com/diegosouzapw/OmniRoute/pull/12193)) ([#12194](https://github.com/diegosouzapw/OmniRoute/pull/12194)) ([#12202](https://github.com/diegosouzapw/OmniRoute/pull/12202)) — thanks @rafacpti23 +- **build:** force the webpack bundler in the contributor build profile ([#12197](https://github.com/diegosouzapw/OmniRoute/pull/12197)) — thanks @rafacpti23 +- **build:** skip standalone packaging in the contributor profile ([#12198](https://github.com/diegosouzapw/OmniRoute/pull/12198)) — thanks @rafacpti23 +- **docs(audit):** align every published number with the code and harden check:docs-counts ([#12200](https://github.com/diegosouzapw/OmniRoute/pull/12200)) +- **build:** stub the instrumentation entrypoints in the contributor profile ([#12203](https://github.com/diegosouzapw/OmniRoute/pull/12203)) — thanks @rafacpti23 +- **build:** omit the standalone output target for contributor builds ([#12204](https://github.com/diegosouzapw/OmniRoute/pull/12204)) — thanks @rafacpti23 +- **chore(lint):** batch 6 of #12146 — memory, radar, audit, analytics, cache, usage, activity, home and RequestLoggerV2 react-hooks violations resolved ([#12208](https://github.com/diegosouzapw/OmniRoute/pull/12208)) +- **docs(diagrams):** rename number-carrying diagram files to stable names ([#12210](https://github.com/diegosouzapw/OmniRoute/pull/12210)) +- **docs(dashboard):** redraw onboarding tier-flow SVGs for the real 4-tier model ([#12211](https://github.com/diegosouzapw/OmniRoute/pull/12211)) +- **docs(api):** document every implemented route in openapi.yaml (276 → 692 paths) ([#12212](https://github.com/diegosouzapw/OmniRoute/pull/12212)) +- **chore(lint):** batch 7 of #12146 — final src tail: 37 react-hooks violations across 33 files resolved ([#12244](https://github.com/diegosouzapw/OmniRoute/pull/12244)) +- Revert "docs: recommend gstack for AI-assisted workflows (#11770)" ([#12248](https://github.com/diegosouzapw/OmniRoute/pull/12248)) ([#12249](https://github.com/diegosouzapw/OmniRoute/pull/12249)) +- **docs:** Chaos Mode setup guide + weighted strategy semantics ([#12250](https://github.com/diegosouzapw/OmniRoute/pull/12250)) +- **docs(agents):** protected-surface merge rule — operator approval for agent-instruction files ([#12253](https://github.com/diegosouzapw/OmniRoute/pull/12253)) +- **chore(quality):** register native-codex turn-pin tests in stryker tap.testFiles ([#12263](https://github.com/diegosouzapw/OmniRoute/pull/12263)) +- **chore(quality):** dedupe tap.testFiles entries added by racing base-red fixes ([#12265](https://github.com/diegosouzapw/OmniRoute/pull/12265)) +- **docs(readme):** introduce OmniRouteTray — the macOS menu-bar companion ([#12276](https://github.com/diegosouzapw/OmniRoute/pull/12276)) — thanks @ggiak +- **chore(proxy):** purge the legacy 1proxy residue — sync/rotator modules, dead DB exports, dead settings tab and flag, docs (#12091) ([#12290](https://github.com/diegosouzapw/OmniRoute/pull/12290)) +- **chore(quality):** baseline-headroom skips generated and vendored files in the fileSize worst-file signal ([#12291](https://github.com/diegosouzapw/OmniRoute/pull/12291)) +- **refactor(video):** unify the JPEG frame data-URI contract ([#12322](https://github.com/diegosouzapw/OmniRoute/pull/12322)) +- **chore(deps):** freeze onnxruntime-node and eslint-plugin-react-hooks out of dependabot groups ([#12329](https://github.com/diegosouzapw/OmniRoute/pull/12329)) +- **test(executors):** restore the #10986 reasoning-only fallback guards ([#12364](https://github.com/diegosouzapw/OmniRoute/pull/12364)) — thanks @pacocartones +- **chore(quality):** re-tighten the file-size ratchet to the real LOC (plan 3.8.52 task 0) ([#12411](https://github.com/diegosouzapw/OmniRoute/pull/12411)) +- **test(grok):** format web executor suite ([#12412](https://github.com/diegosouzapw/OmniRoute/pull/12412)) +- **chore(quality):** ratchet open-sse typecheck baseline to zero ([#12418](https://github.com/diegosouzapw/OmniRoute/pull/12418)) +- **test(build):** guard the artifact path policy arrays against duplicates ([#12422](https://github.com/diegosouzapw/OmniRoute/pull/12422)) +- **chore(lint):** adopt eslint-plugin-react-hooks 7.1.1 ([#12428](https://github.com/diegosouzapw/OmniRoute/pull/12428)) +- **chore(providers):** remove a keyless provider integration at its operator's request ([#12440](https://github.com/diegosouzapw/OmniRoute/pull/12440)) +- **docs(providers):** register providers removed at their operator's request and guard against reintroduction ([#12478](https://github.com/diegosouzapw/OmniRoute/pull/12478)) +- **chore(quality):** base fixes — stryker tap.testFiles + node_modules cache key ([#12482](https://github.com/diegosouzapw/OmniRoute/pull/12482)) +- **docs(arch):** one-process recipe for tens of long /v1/responses ([#12493](https://github.com/diegosouzapw/OmniRoute/pull/12493)) — thanks @RaviTharuma +- **chore(quality):** tighten the CodeQL ratchet baseline from 11 to 6 ([#12530](https://github.com/diegosouzapw/OmniRoute/pull/12530)) +- **chore(deps):** pin browserslist override to ^4.28.8 ([#12592](https://github.com/diegosouzapw/OmniRoute/pull/12592)) — thanks @KooshaPari +- **chore(deps):** bump fast-uri to 3.1.7 in the electron lockfile ([#12601](https://github.com/diegosouzapw/OmniRoute/pull/12601)) +- **docs(quality):** document how the CodeQL ratchet refreshes and how to tighten it ([#12611](https://github.com/diegosouzapw/OmniRoute/pull/12611)) +- **test(ci):** pin the openapi-security-tiers two-arm contract with an executing gate test (#12581) ([#12652](https://github.com/diegosouzapw/OmniRoute/pull/12652)) +- **docs(gamification):** align XP Rewards table with code (#12501) ([#12667](https://github.com/diegosouzapw/OmniRoute/pull/12667)) — thanks @KooshaPari +- **refactor(combo):** split executeTarget into gates, attempt, and loop ([#12746](https://github.com/diegosouzapw/OmniRoute/pull/12746)) — thanks @HouMinXi +- **docs(security):** document socket.yml scanner config + CI workflow link (#12575) ([#12764](https://github.com/diegosouzapw/OmniRoute/pull/12764)) — thanks @KooshaPari +- **chore(ci):** guard commit identity in pre-commit to stop author misattribution ([#12772](https://github.com/diegosouzapw/OmniRoute/pull/12772)) +- **deps:** 17 Dependabot bumps — bump electron from 43.4.0 to 43.4.1 in /electron ([#11426](https://github.com/diegosouzapw/OmniRoute/pull/11426)); bump the development group across 1 directory with 8 updates ([#11428](https://github.com/diegosouzapw/OmniRoute/pull/11428)); bump the production group across 1 directory with 11 updates ([#11440](https://github.com/diegosouzapw/OmniRoute/pull/11440)); chore(deps): bump github/codeql-action/init from 4.37.7 to 4.37.8 ([#11925](https://github.com/diegosouzapw/OmniRoute/pull/11925)); chore(deps): bump github/codeql-action from 4.37.7 to 4.37.8 ([#11926](https://github.com/diegosouzapw/OmniRoute/pull/11926)); chore(deps): bump github/codeql-action/analyze from 4.37.7 to 4.37.8 ([#11927](https://github.com/diegosouzapw/OmniRoute/pull/11927)); build(deps): bump github/codeql-action/init from 4.37.8 to 4.37.9 ([#12345](https://github.com/diegosouzapw/OmniRoute/pull/12345)); build(deps): bump github/codeql-action/analyze from 4.37.8 to 4.37.9 ([#12346](https://github.com/diegosouzapw/OmniRoute/pull/12346)); bump the development group across 1 directory with 2 updates ([#12347](https://github.com/diegosouzapw/OmniRoute/pull/12347)); build(deps): bump github/codeql-action from 4.37.8 to 4.37.9 ([#12349](https://github.com/diegosouzapw/OmniRoute/pull/12349)); bump browserslist from 4.28.2 to 4.28.8 ([#12396](https://github.com/diegosouzapw/OmniRoute/pull/12396)); bump the production group across 1 directory with 4 updates ([#12399](https://github.com/diegosouzapw/OmniRoute/pull/12399)); bump @xmldom/xmldom ([#12500](https://github.com/diegosouzapw/OmniRoute/pull/12500)); bump qs from 6.15.2 to 6.16.0 ([#12512](https://github.com/diegosouzapw/OmniRoute/pull/12512)); bump @xmldom/xmldom from 0.9.10 to 0.9.12 ([#12513](https://github.com/diegosouzapw/OmniRoute/pull/12513)); bump fast-uri from 3.1.5 to 3.1.7 ([#12514](https://github.com/diegosouzapw/OmniRoute/pull/12514)); bump @humanfs/node from 0.16.7 to 0.16.8 ([#12515](https://github.com/diegosouzapw/OmniRoute/pull/12515)) +- Clear the `release/v3.8.51` `check:agent-skills-sync` base-red: regenerate `skills/cli-tunnel/SKILL.md` so the `tunnel create [type]` positional that #13009 taught the generator to read is reflected in the committed skill. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` typecheck base-red from #12731: the new `Mode pack ranking gates` candidates in `open-sse/services/autoCombo/__tests__/autoCombo.test.ts` omitted the required `provider`, `model` and `errorRate` fields of `ProviderCandidate`, failing `check:open-sse-typecheck` (and with it `Fast Quality Gates`) on every open PR. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` docs-sync base-red: EURouter (#13025) and GreenPT (#13024) took the live provider count to 358, leaving 7 STRICT drifts (`PROVIDER_REFERENCE.md`, 4 diagrams, 2 tier-flow images) plus the `AGENTS.md` / `llm.txt` / `package.json` count claims stale. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- **test(models):** Pin `CATALOG_BUILD_TIMEOUT_MS` in the #12058 canonical-catalog regression so a cold catalog build racing the 8s default bound no longer flakes the suite with a 500 ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Allow `@omniroute/opencode-plugin-v2/` in the npm pack policy — [#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870) shipped the v2 plugin beside its v1 sibling without widening the allowlist, so every packed file under it read as an unexpected artifact and failed `check:pack-policy` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Point the release-green validator's pack gate at the tree it is validating — the build-provenance guard defaulted to `origin/main`, which a release branch tip can never be an ancestor of mid-cycle, so `Package artifact` reported a false red on every run and the tarball boot-smoke cascaded off it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **docs(checks):** keep doc counts honest — headings, rankings, catalog, weights, quality gate and scoring diagram now covered ([#12507](https://github.com/diegosouzapw/OmniRoute/pull/12507)) — thanks @maxmad64bis +- **chore(lifecycle):** `check:model-lifecycle` now also diffs `DEFAULT_DEGRADATION_MAP` (the background-task redirect table) against the vendor lifecycle snapshot, refusing a retired id as source or target, with a table-driven unit test beside it. Three rows whose source the vendor had retired — `claude-sonnet-4-20250514`, `gemini-3-pro-preview` and `gpt-5.1-codex` (whose target `gpt-5.1-codex-mini` is retired too) — were dead code, since `checkLifecycle` answers 410 before the redirect runs; they are dropped (#12535 — thanks @pacocartones) +- **test(compression):** cover idle worker eviction at the resource level — the pool must call `terminate()` and must not retain the worker's `MessagePort`, complementing the `exit`-event assertion added with the fix ([#13371](https://github.com/diegosouzapw/OmniRoute/pull/13371)) +- **chore(quality):** raise the `imageGeneration.ts` file-size ceiling for the image-only-model guard that clears the #12945 base-red ([#13392](https://github.com/diegosouzapw/OmniRoute/pull/13392)) +- **chore(quality):** type the combo-test route's JSON response bodies instead of casting them to `any`, drop the now-empty suppression entry, and remove the `hasPerModelQuota` import `chatCore.ts` stopped using when the failure-classification helper was extracted ([#13379](https://github.com/diegosouzapw/OmniRoute/pull/13379)) +- **chore(electron):** the desktop bundle no longer ships the root-level files of every translated docs mirror (`README.md`, `llm.txt`, `CLAUDE.md`, `GEMINI.md`, `CONTRIBUTING.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`) — the packaged app only reads `docs/i18n//docs/**`, which stays. Saves ~11 MB on top of the translated CHANGELOGs already pruned. (#0000) ([#13235](https://github.com/diegosouzapw/OmniRoute/pull/13235)) +- **chore(quality):** retighten the `src/sse/handlers/chat.ts` unused-vars suppression count to the 8 violations that actually remain ([#13385](https://github.com/diegosouzapw/OmniRoute/pull/13385)) +- Bring the documented counts back in line with the code: 171 migrations (was 169) and 20 routing strategies (was 19), plus the regenerated `cli-tunnel` skill reference. ([#12970](https://github.com/diegosouzapw/OmniRoute/pull/12970)) +- Update the documented migration count to 172 after the call-logs provider-stats indexes landed. ([#13209](https://github.com/diegosouzapw/OmniRoute/pull/13209)) +- **chore(ci):** two gates that close the blind spots behind the exclusions above. `check:vitest-exclusions` requires every Vitest exclusion to name a tracking issue and to appear in `config/quality/vitest-exclusions.json` — the previous list grew to 62 files behind a comment pointing at an issue that had been closed for a month. `check-new-key-coverage` requires a key newly added to `en.json` to reach every locale; the existing coverage gate is a percentage floor per locale, so eleven absent keys out of ~13,000 left it at 99.9% while a whole feature shipped untranslated in nine languages. Both are diff-aware, so pre-existing debt stays frozen and neither needed a migration to turn on. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **build:** root layout no longer loads Inter from `next/font/google`, so a production image build does not need fonts.googleapis.com ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **docs(i18n):** bring the eight new `llm.txt` mirrors (km, kn, ml, my, ne, or, pa, si) up to the current provider and migration counts, so `check-docs-sync` stops rejecting every commit ([#13674](https://github.com/diegosouzapw/OmniRoute/pull/13674)) +- **refactor(ui):** flow surfaces (home topology, combo live studio, compression cockpit/waterfall/nodes, token-health badge) express state through the theme-aware `--orch-status-*` tokens instead of fixed dark-mode hex, so green/red/amber/grey stay legible in the light theme; dark mode is byte-identical. Categorical palettes (routing-strategy hues, compression-layer pills, provider brand colors) deliberately stay hex ([#12378](https://github.com/diegosouzapw/OmniRoute/issues/12378)) ([#13203](https://github.com/diegosouzapw/OmniRoute/pull/13203)) +- **chore(tests):** 51 test files that had been excluded from Vitest are running again, restoring roughly 350 assertions to the blocking suite. Each was measured individually first: of the 62 files parked behind the `// #8618 — pre-existing failure` comment, 51 pass against the current tree with no source change, so the exclusions had outlived the failures they were added for. The 11 that genuinely still fail stay excluded, but now point at a live tracker (#13204) rather than at #8618, which was closed in August while the list it tracked kept growing. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **chore(quality):** drop two ESLint suppression entries whose violations no longer exist, so `eslint --suppressions-location` stops rejecting every commit that touches the surrounding files ([#13384](https://github.com/diegosouzapw/OmniRoute/pull/13384)) +- **chore(release):** bring main's post-tag fixes into release/v3.8.51 — sync-back step 2/2 (direct commit `21c488f210`) +- **chore(deps):** patch toml and esbuild in the opencode plugin lockfiles ([#12965](https://github.com/diegosouzapw/OmniRoute/pull/12965)) +- **docs(changelog):** reconcile the v3.8.51 living section — fold 366 fragments, cover every cycle commit, credit every contributor ([#12971](https://github.com/diegosouzapw/OmniRoute/pull/12971)) +- **test(plugin):** wait for the v2 lazy-refresh tier instead of sleeping past it ([#12984](https://github.com/diegosouzapw/OmniRoute/pull/12984)) +- **chore(quality):** rebaseline chatCore.ts after the non-streaming regression fixes ([#13045](https://github.com/diegosouzapw/OmniRoute/pull/13045)) +- **docs(changelog):** add the missing bullet marker to the DeepSeek PoW fragment ([#13200](https://github.com/diegosouzapw/OmniRoute/pull/13200)) +- **chore(deps):** drain the Dependabot queue — 10 of 13 alerts ([#13213](https://github.com/diegosouzapw/OmniRoute/pull/13213)) +- **chore(ci):** make worktrees carry the hooks that enforce the identity gate ([#13236](https://github.com/diegosouzapw/OmniRoute/pull/13236)) +- **chore(deps):** pin csv-parse to ^7.0.2 via overrides (#13117) ([#13300](https://github.com/diegosouzapw/OmniRoute/pull/13300)) — thanks @KooshaPari +- **chore(deps):** bump hono from ^4.12.34 to ^4.13.7 (#13148) ([#13301](https://github.com/diegosouzapw/OmniRoute/pull/13301)) — thanks @KooshaPari +- **chore(deps):** pin joi to ^18.2.8 via overrides (#13085) ([#13302](https://github.com/diegosouzapw/OmniRoute/pull/13302)) — thanks @KooshaPari +- **docs(providers):** add Microsoft 365 Copilot (BizChat) provider guide (#12779) ([#13340](https://github.com/diegosouzapw/OmniRoute/pull/13340)) — thanks @KooshaPari +### 🙌 Contributors + +Thanks to everyone whose work landed in v3.8.51: + +| Contributor | PRs / Issues | +| --- | --- | +| [@5dive-bot](https://github.com/5dive-bot) | #11852 | +| [@abhisheksharma2411](https://github.com/abhisheksharma2411) | #11547, #11684, #11959, #12209 | +| [@adevwithpurpose](https://github.com/adevwithpurpose) | #11464 | +| [@adivekar-utexas](https://github.com/adivekar-utexas) | #12015, #12043, #12090, #12138 | +| [@AIB1TAL0S](https://github.com/AIB1TAL0S) | #12103 | +| [@alltomatos](https://github.com/alltomatos) | #11448 | +| [@alvinveroy](https://github.com/alvinveroy) | #11857, #12027, #12165 | +| [@amaleta](https://github.com/amaleta) | #11906, #11934 | +| [@amartinawi](https://github.com/amartinawi) | #11575 | +| [@AndrianBalanescu](https://github.com/AndrianBalanescu) | #11083, #11830, #11834, #11844 | +| [@anhtahaylove](https://github.com/anhtahaylove) | #13091, #13092, #13093, #13096, #13097, #13100, #13106, #13109, #13114, #13155, #13166, #13171, #13175, #13187, #13196 | +| [@aniruddhaadak80](https://github.com/aniruddhaadak80) | #11760 | +| [@app](https://github.com/app) | #12554 | +| [@arminanton](https://github.com/arminanton) | #11461, #11513 | +| [@AStupidBear](https://github.com/AStupidBear) | #11584 | +| [@atescivitci-cmd](https://github.com/atescivitci-cmd) | #12105 | +| [@b3nw](https://github.com/b3nw) | #11970, #11971, #12147 | +| [@backryun](https://github.com/backryun) | #11259, #11950, #12075, #12076, #12078, #12079, #12081, #12082, #12181, #12228, #12239, #12255, #12258, #12277, #12367, #12423, #12524, #12538 | +| [@benjaminkitt](https://github.com/benjaminkitt) | #11747 | +| [@benzntech](https://github.com/benzntech) | #11614, #11615 | +| [@Bl0ck154](https://github.com/Bl0ck154) | #11948, #11951, #11952, #11953, #11954 | +| [@brick30llc-ctrl](https://github.com/brick30llc-ctrl) | #11752, #12242, #12262 | +| [@Chewji9875](https://github.com/Chewji9875) | #11455, #11875, #12028 | +| [@cheynetom](https://github.com/cheynetom) | #12058 | +| [@davidebaraldo](https://github.com/davidebaraldo) | #12222, #12304 | +| [@davidlinfr](https://github.com/davidlinfr) | #6390 | +| [@ddarkr](https://github.com/ddarkr) | #12124 | +| [@Deftera186](https://github.com/Deftera186) | #11809 | +| [@dpozimski](https://github.com/dpozimski) | #11945 | +| [@DW-MediaLab](https://github.com/DW-MediaLab) | #12229 | +| [@echel0nn](https://github.com/echel0nn) | #11923 | +| [@f9td56dbgh-hub](https://github.com/f9td56dbgh-hub) | #11660 | +| [@fabioluissilva](https://github.com/fabioluissilva) | #11991 | +| [@foreveryh](https://github.com/foreveryh) | #12177 | +| [@geek007git](https://github.com/geek007git) | #12115, #12116, #12117, #12120, #12122 | +| [@geekyNads](https://github.com/geekyNads) | #11784, #12266 | +| [@ggiak](https://github.com/ggiak) | #11561, #12276, #12350, #12402 | +| [@gonisulaimann](https://github.com/gonisulaimann) | #12368, #12369, #12371 | +| [@Gorillaz322](https://github.com/Gorillaz322) | #12207 | +| [@groovecityJO](https://github.com/groovecityJO) | #12682 | +| [@hartmark](https://github.com/hartmark) | #11434, #11452, #11473, #11499, #11703, #11983, #11984, #11985, #11986, #11988, #11989, #11990, #11994, #12221, #12293, #12445, #12446, #12447, #12448, #12460, #12461, #12623, #12646, #12650, #12680, #12717, #12718, #12727, #12741, #12854 | +| [@hizzt](https://github.com/hizzt) | #11894 | +| [@hongnoul](https://github.com/hongnoul) | #11484 | +| [@HouMinXi](https://github.com/HouMinXi) | #11411, #11414, #11512, #11518, #11520, #11641, #11642, #11643, #11687, #11779, #11849, #11850, #11851, #11915, #11916, #11918, #11919, #11920, #12013, #12017, #12033, #12042, #12106, #12139, #12166, #12169, #12171, #12205, #12213, #12312, #12325, #12487, #12488, #12495, #12504, #12557, #12566, #12590, #12591, #12624, #12626, #12632, #12637, #12678, #12696, #12697, #12711, #12733, #12746, #12767, #12770, #12789, #12803, #12805, #12811, #12866, #12868, #12899, #12926, #12934, #12950, #12951, #12974, #13001, #13006, #13011, #13017, #13026, #13027, #13034, #13035, #13038, #13042, #13050, #13060, #13061, #13069, #13107, #13120, #13136, #13195, #13197 | +| [@Hsia97](https://github.com/Hsia97) | #11624 | +| [@jacobsparts](https://github.com/jacobsparts) | #11854, #12155, #12167 | +| [@joglomedia](https://github.com/joglomedia) | #11980 | +| [@jonlwheat2-gif](https://github.com/jonlwheat2-gif) | #11443, #11450, #11549, #11567, #11608, #11633, #11635, #11640, #11644, #11671, #11672, #11673, #11674, #11675, #11676, #11677, #11706, #11783, #12051, #12052, #12053, #12055 | +| [@kanade-hoshino](https://github.com/kanade-hoshino) | #12180 | +| [@Karan825](https://github.com/Karan825) | #11861 | +| [@KaspaPulse](https://github.com/KaspaPulse) | #11389, #11469, #11628, #11666, #11910 | +| [@keeltrace](https://github.com/keeltrace) | #12080, #12223 | +| [@killer30001000](https://github.com/killer30001000) | #12178 | +| [@KooshaPari](https://github.com/KooshaPari) | #12470, #12592, #12667, #12699, #12703, #12706, #12764, #12769, #12771, #13300, #13301, #13302, #13340, #13401, #13403, #13404, #13405, #13406, #13407, #13409, #13410, #13411, #13412, #13413, #13414, #13418, #13424, #13427, #13433, #13523, #13524, #13525, #13528, #13531, #13532, #13534, #13539, #13543, #13545, #13546, #13547, #13550, #13553 | +| [@kriptoburak](https://github.com/kriptoburak) | #11370 | +| [@KrzysiekSko](https://github.com/KrzysiekSko) | #12425, #12432, #12673 | +| [@marcelokarval](https://github.com/marcelokarval) | direct commit / report | +| [@maxmad64bis](https://github.com/maxmad64bis) | #1622, #11435, #11437, #11441, #11537, #11550, #11553, #11555, #11557, #11812, #11842, #11843, #11903, #12151, #12214, #12215, #12218, #12226, #12314, #12316, #12317, #12318, #12319, #12320, #12321, #12507, #12715, #12731, #12744, #12786, #12787, #12788, #12790, #12792, #12794, #12795, #12828, #12832, #12853, #12857, #12870, #12937, #12941, #12975, #13141, #13142, #13146, #13147 | +| [@mdigitalbh81](https://github.com/mdigitalbh81) | #12240 | +| [@Meet6338-X](https://github.com/Meet6338-X) | #11598, #11609, #12110 | +| [@morpheus9393](https://github.com/morpheus9393) | #11943 | +| [@MumuTW](https://github.com/MumuTW) | #11492, #11502, #11506, #11507, #11626, #11675, #11685, #11728, #11746, #11888, #11889, #11890, #11892 | +| [@Neuron-Mr-White](https://github.com/Neuron-Mr-White) | #11622, #11801, #12256 | +| [@NightStalker-87](https://github.com/NightStalker-87) | #12183 | +| [@NoxzRCW](https://github.com/NoxzRCW) | #11879, #11880, #11881, #11882, #11883, #11935 | +| [@ntdat812](https://github.com/ntdat812) | #11585, #12095 | +| [@ntdatt812](https://github.com/ntdatt812) | #11368, #11573, #11574, #11576, #11577, #11580, #11582, #11583, #11588, #11589, #11590, #11591, #11592, #11593, #11672, #12177, #12180, #12873, #12918, #12920, #12921, #12925, #12930, #13007, #13009, #13024, #13025, #13083, #13087, #13101, #13104, #13110 | +| [@official-burak](https://github.com/official-burak) | #11542 | +| [@opensource-elearning](https://github.com/opensource-elearning) | #12179, #12189, #12278, #12286 | +| [@oyi77](https://github.com/oyi77) | #11408, #11409, #11421, #11505, #11677, #12036, #12110 | +| [@pacocartones](https://github.com/pacocartones) | #11521, #11522, #11527, #11528, #11529, #11530, #11531, #11532, #11533, #11534, #11595, #11599, #11603, #11604, #11605, #11607, #11610, #11676, #11714, #11716, #11718, #11767, #11838, #11860, #11862, #11869, #11871, #11872, #11873, #11903, #11906, #11921, #11934, #12356, #12358, #12359, #12360, #12361, #12362, #12364, #12365, #12373, #12374, #12375, #12376, #12377, #12379, #12380, #12381, #12386, #12387, #12389, #12390, #12394, #12395, #12397, #12401, #12403, #12404, #12522, #12523, #12535, #12536, #12540, #12541, #12543, #12545, #12548, #12549, #12550, #12551, #12552, #12647, #12651, #12653, #13055 | +| [@patrykkopycinski](https://github.com/patrykkopycinski) | #11936, #11937, #12224 | +| [@PauloFH](https://github.com/PauloFH) | #11509 | +| [@PauloHSOliveira](https://github.com/PauloHSOliveira) | #12241 | +| [@ponkcore](https://github.com/ponkcore) | #12054 | +| [@Prajeeth-12](https://github.com/Prajeeth-12) | #11634, #12046 | +| [@quiterunner-commits](https://github.com/quiterunner-commits) | #12131, #12143 | +| [@rafacpti23](https://github.com/rafacpti23) | #11554, #11558, #12192, #12197, #12198, #12202, #12203, #12204 | +| [@ragnar-claude](https://github.com/ragnar-claude) | #11564 | +| [@raheemuddin786](https://github.com/raheemuddin786) | #11491, #11839, #11840, #11841, #12003, #12230, #12231, #12232, #12233, #12234 | +| [@rahilmavani](https://github.com/rahilmavani) | #11761 | +| [@Rahulsharma0810](https://github.com/Rahulsharma0810) | #11771 | +| [@RaviTharuma](https://github.com/RaviTharuma) | #11710, #11727, #11797, #11798, #11802, #11805, #11806, #11811, #12098, #12099, #12101, #12449, #12452, #12472, #12473, #12493, #12533, #12607, #12628, #12631, #12636, #12875, #12876, #12880, #12882, #12884 | +| [@rezjalibd](https://github.com/rezjalibd) | #12186 | +| [@RhianB14](https://github.com/RhianB14) | #11692 | +| [@rifqiawl](https://github.com/rifqiawl) | #11517, #11519, #11969 | +| [@rqzbeh](https://github.com/rqzbeh) | #11390 | +| [@Sabeekhann](https://github.com/Sabeekhann) | #11699, #11705 | +| [@santosraju99-hub](https://github.com/santosraju99-hub) | #11755, #11770 | +| [@Seramicx](https://github.com/Seramicx) | #11568 | +| [@Siva010](https://github.com/Siva010) | #12191 | +| [@solstxce](https://github.com/solstxce) | #11597 | +| [@soroush5](https://github.com/soroush5) | #12691 | +| [@tenshiak](https://github.com/tenshiak) | #12279 | +| [@TheDemonTuan](https://github.com/TheDemonTuan) | #11468, #11470, #11471, #11482, #11548, #11758, #11775, #11814 | +| [@thomasmaerz](https://github.com/thomasmaerz) | #12834 | +| [@tuandinh0801](https://github.com/tuandinh0801) | #11454 | +| [@turbolego](https://github.com/turbolego) | #11621, #11762, #11772, #11774, #11781, #12216 | +| [@Tushar49](https://github.com/Tushar49) | #11392 | +| [@ujjawalkaushik1110](https://github.com/ujjawalkaushik1110) | #11863 | +| [@vermasomesh835](https://github.com/vermasomesh835) | #11794 | +| [@vsd2807](https://github.com/vsd2807) | #11565, #11619 | +| [@wahidsadik371-coder](https://github.com/wahidsadik371-coder) | #12126 | +| [@watchingdogs](https://github.com/watchingdogs) | #12031 | +| [@wildcard](https://github.com/wildcard) | #11620 | +| [@xiaoyaner0201](https://github.com/xiaoyaner0201) | #11460, #11662, #12083 | +| [@Xxx91n](https://github.com/Xxx91n) | #11690 | +| [@yourspraveen](https://github.com/yourspraveen) | #11146 | +| [@yxyxy](https://github.com/yxyxy) | #11671 | +| [@ZaimMarzuki](https://github.com/ZaimMarzuki) | #11960 | +| [@Zartharas](https://github.com/Zartharas) | #11340 | +| [@zero-executioner](https://github.com/zero-executioner) | #11631 | +| [@diegosouzapw](https://github.com/diegosouzapw) | maintainer | + +--- ## [3.8.50] — 2026-08-25 _Living section — regenerated 2026-08-12 from all cycle commits (cycle open `ed2db6cb19` → tip). Bullets carry the merged PR and its author; direct pushes listed separately._ diff --git a/docs/i18n/sv/CHANGELOG.md b/docs/i18n/sv/CHANGELOG.md index e43a935445..7859c72318 100644 --- a/docs/i18n/sv/CHANGELOG.md +++ b/docs/i18n/sv/CHANGELOG.md @@ -8,16 +8,1119 @@ ## [3.8.51] — TBD -_Living section — cycle opened at the v3.8.50 freeze (parallel-cycle model). Bullets are aggregated from `changelog.d/` fragments at each `/generate-release` phase._ +_Living section — reconciled 2026-09-15 from all cycle commits (`091589089c` → `c0f92ec98a`, 916 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 | **123** | +| 📝 Commits in the cycle | **916** | +| 🔀 Pull requests referenced | **892** | +| 📋 Changelog entries | **919** | +| 🙌 Contributors credited in entries | **121** | +| 🤖 Automated dependency commits | 18 | + +**Entries by type** + +| Type | Count | +| --- | ---: | +| 🐛 Fixes | 592 | +| ✨ Features | 131 | +| 🧹 Chore | 93 | +| 📚 Docs | 42 | +| 🧪 Tests | 32 | +| ♻️ Refactor | 8 | +| 🏗️ Build | 7 | +| ⚡ Performance | 7 | +| ⚙️ CI | 2 | +| ⏪ Reverts | 2 | +| 📦 Dependencies | 2 | +| 🔒 Security | 1 | + +### 🏆 Top 25 contributors this cycle + +_By commits in `091589089c..c0f92ec98a`, author identities consolidated via `.mailmap` and the merged PR's GitHub login. Bots excluded._ + +| # | Contributor | Commits | +| ---: | --- | ---: | +| 🥇 | diegosouzapw | 323 | +| 🥈 | Bob.Hou (@HouMinXi) | 79 | +| 🥉 | Paco Cartones (@pacocartones) | 77 | +| 4 | Dizzle (@maxmad64bis) | 49 | +| 5 | Koosha Paridehpour (@KooshaPari) | 43 | +| 6 | Nguyen Thanh Dat (@ntdatt812) | 31 | +| 7 | Ravi Tharuma (@RaviTharuma) | 26 | +| 8 | Markus Hartung (@hartmark) | 22 | +| 9 | Webman (@jonlwheat2-gif) | 22 | +| 10 | backryun | 17 | +| 11 | anhtahaylove | 15 | +| 12 | Rafa Martins (@rafacpti23) | 10 | +| 13 | Syed Raheemuddin (@raheemuddin786) | 10 | +| 14 | Nguyễn Viết Tuấn (@TheDemonTuan) | 8 | +| 15 | MumuTW | 7 | +| 16 | Paijo (@oyi77) | 7 | +| 17 | Tobias Andersen (@turbolego) | 6 | +| 18 | Bl0ck (@Bl0ck154) | 5 | +| 19 | SHANMUGAPRIYAN (@geek007git) | 5 | +| 20 | KaspaPulse | 5 | +| 21 | Abhishek Sharma (@abhisheksharma2411) | 4 | +| 22 | Abhishek Divekar (@adivekar-utexas) | 4 | +| 23 | Andrew B. (@AndrianBalanescu) | 4 | +| 24 | NoxzRCW | 4 | +| 25 | opensource-elearning | 4 | ### ✨ New Features +- **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). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- Added Google AI Studio Gemini batch text-to-speech support through `POST /v1/audio/speech`. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- 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. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(routing):** subscription-first auto groupings — `auto/subscription` routes only through plan-included connections with a documented hard-stop overage and fails closed on exhaustion, while `auto/thrifty` orders the pool `subscription → keyless → free → cheap → premium` and steps up one rung at a time as each is exhausted. Billing class comes from a curated per-connection catalog (uncurated is treated as metered, never plan-included), both reuse STRICT_ZERO_COST's per-connection verification, and a quota reading whose `resetAt` has passed is now refreshed regardless of TTL so routing returns to plan capacity as soon as it resets ([#11146](https://github.com/diegosouzapw/OmniRoute/pull/11146)) — thanks @yourspraveen +- **feat(providers):** publish a management-authenticated versioned web-session credential contract from OmniRoute's canonical browser credential metadata ([#11340](https://github.com/diegosouzapw/OmniRoute/pull/11340)) — thanks @Zartharas +- **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)) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add Xquik X search with typed results, credential validation, REST routing, and MCP selection ([#11370](https://github.com/diegosouzapw/OmniRoute/pull/11370)) — thanks @kriptoburak +- **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)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(dashboard):** surface durable exclusive managed leases in the existing Sessions view, keeping leased clients visible across idle gaps while marking connections with in-flight work as active ([#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389)) — thanks @KaspaPulse +- **build(bun):** allow Turbopack bundler flag on Bun 1.4+ with configurable Webpack fallback ([#11471](https://github.com/diegosouzapw/OmniRoute/pull/11471)) — thanks @TheDemonTuan +- feat(api): add an opt-in `modelVisibilityAllowlist`/`modelVisibilityDenylist` settings pair to curate exactly which models `/v1/models` advertises, mirrored into every `auto/*` combo candidate pool so a denied model cannot be routed to via combo selection either (#11481) ([#11997](https://github.com/diegosouzapw/OmniRoute/pull/11997)) +- **feat(rankings):** the Free Provider Rankings page shows what each provider actually served over the last 24 h. It ranked by ELO alone, which left a provider that answers every call with an error in first place; the usage data was already served by the API but never requested. A provider with too small a sample shows a dash, not a number ([#11546](https://github.com/diegosouzapw/OmniRoute/pull/11546)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **feat(guardrails):** enforce a bounded, deterministic contract for Video Bridge transcripts — 256 cues, 4096 input code units and 4 KiB UTF-8 per cue, 64 KiB total text, malformed-Unicode rejection, focus-window scoping, cross-source reconciliation with contributing-source metadata, and a structural provenance trust boundary so caller JSON can never self-assert `embedded`/`audio-bridge` provenance ([#11652](https://github.com/diegosouzapw/OmniRoute/issues/11652)) ([#12009](https://github.com/diegosouzapw/OmniRoute/pull/12009)) +- **feat(video):** orchestrate optional Video Bridge audio extraction and Audio Bridge STT behind a dual opt-in (operator setting AND per-request signal) — a new loopback-only broker `mode=audio` operation shares the frame path's exact process queue, deadline, AbortSignal, and byte budgets to extract a bounded mono 16 kHz PCM WAV from the same already-downloaded video, then reuses the existing Audio Bridge transcription boundary; provider segment timing is preserved when available and marked coarse otherwise, and every failure degrades to a visual-only-safe partial instead of throwing (#11654). ([#12012](https://github.com/diegosouzapw/OmniRoute/pull/12012)) +- Add a tenant-bound Video Bridge drill-down lifecycle on top of the existing secure cache substrate: opaque hashed handles (never raw session/video identifiers), preview/standard/detail multiresolution variants resampled on read, response pagination capped at 8 frames and 32 MiB, and a new authenticated `/api/v1/video-bridge/drilldown` consumer route that stays disabled for remote access by default and denies cross-key access with the same response as a nonexistent handle (no existence oracle). ([#12006](https://github.com/diegosouzapw/OmniRoute/pull/12006)) +- **test(video):** Add the Video Bridge FU-07/FU-09 promotion-evidence harness (#11656) — a frozen Zod manifest schema covering the 8 required scenario kinds (static scenes, rapid cuts, late facts, fades, blur, small text, close events, visual prompt injection) with a minimum of 3 repetitions per case, deterministic declarative fixture recipes (`videoBridgePromotionFixtures.ts`), a pure medians/p95 metrics aggregator, a pure FU-07/FU-09 promotion-verdict evaluator applying the ticket's exact thresholds (missing token usage always holds), a digest-only persistence layer that never retains raw media or raw model responses, and a versioned per-model promotion allowlist shipped empty with every model defaulting to `hold`. The FU-07/FU-09 promotion verdicts themselves remain HOLD — they require a real evidence run against real models on VPS 192.168.0.15. ([#12008](https://github.com/diegosouzapw/OmniRoute/pull/12008)) +- **feat(video bridge):** "embedded" transcript provenance can now be legitimately earned instead of merely asserted — a bounded, allowlisted (`mov_text`/`subrip`/`webvtt`) subtitle probe runs through the loopback-only Video Bridge broker (at most 2 streams, 10s subdeadline bounded by the request deadline, 256 KiB output, 4096-code-unit lines), normalized through a bounded, ReDoS-safe WebVTT parser and Zod-validated end to end. The adapter always resolves to an explicit `success`/`absent`/`transient_failure` outcome — a subtitle failure never breaks the visual description path, and only a fingerprint-verified broker response (never a caller-declared label) can produce embedded cues (#11659). ([#12011](https://github.com/diegosouzapw/OmniRoute/pull/12011)) +- Default new Antigravity-family connections (agy CLI imports and Antigravity OAuth connects) to model auto-sync, so live model discovery lands in the synced catalog and `/v1/models` picks up freshly released upstream models (e.g. Gemini 3.7 Flash tiers) without code changes. Existing connections keep their current setting; the per-connection dashboard toggle remains the opt-out. (#11685 — thanks @MumuTW) ([#11887](https://github.com/diegosouzapw/OmniRoute/pull/11887)) +- **feat(ui):** enable React Compiler (`reactCompiler: true` + `babel-plugin-react-compiler`) for automatic memoization at build time ([#11783](https://github.com/diegosouzapw/OmniRoute/pull/11783)) — thanks @jonlwheat2-gif +- **feat(providers):** add SeekAi (`seekai.cc`) as an OpenAI-compatible New-API gateway — catalog id `seekai` (alias `ska`), `https://seekai.cc/v1`, live `/v1/models` via `passthroughModels`, aggregator-list membership so New-API balance detection can opt in. No referral/aff codes. ([#11786](https://github.com/diegosouzapw/OmniRoute/issues/11786)) ([#12557](https://github.com/diegosouzapw/OmniRoute/pull/12557)) — thanks @HouMinXi +- **feat(zai):** add GLM-5.3-Flash Coding Plan support (1M context, 128K output, vision, `low|high|max` reasoning) and route `zai` GLM-5.3-family API-key traffic through the OpenAI-compatible Coding Plan endpoint with native thinking defaults ([#11801](https://github.com/diegosouzapw/OmniRoute/pull/11801)) — thanks @Neuron-Mr-White +- **feat(combo):** choose how combo models are ordered — manual, provider, score, or name — via a sort control in the dashboard builder, persisted in `config.modelSort` and re-applied on load and after add ([#11812](https://github.com/diegosouzapw/OmniRoute/pull/11812)) — thanks @maxmad64bis +- **feat(free):** custom models can be marked free-tier via `customModels[].isFree`; `isFreeModel()` is the first door and `hidePaidModels` respects it even for providers outside the free budget ([#11843](https://github.com/diegosouzapw/OmniRoute/pull/11843)) — thanks @maxmad64bis +- **feat(nodejs):** add `5dive` as a `configure` target — `omniroute configure 5dive` / `omniroute setup-5dive` write a 5dive auth profile that points an agent fleet's `claude` seats at OmniRoute, with the root-only write, the loopback-vs-`https` endpoint rule and the per-seat model pin handled explicitly ([#11852](https://github.com/diegosouzapw/OmniRoute/pull/11852)) — thanks @5dive-bot +- **feat(sse):** treat `max` as a first-class reasoning-effort tier and clamp per model family (GLM 5.1+/DeepSeek V4+/Kimi K3+ keep native `max`; o1/MiniMax/Grok/Muse Spark clamp to their upstream ceiling) ([#11875](https://github.com/diegosouzapw/OmniRoute/pull/11875)) — thanks @Chewji9875 +- **feat(providers):** the provider plugin manifest now advertises a `usage-fetch` capability for the 40 providers that have a wired usage/quota fetcher, so external dashboards can read it from `GET /api/v1/provider-plugin-manifest` instead of parsing `open-sse/services/usage.ts` after every release. Discovery only — no new fetcher, no quota change, and the Dashboard quota widget stays gated by `USAGE_SUPPORTED_PROVIDERS`. `USAGE_FETCHER_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/fetcherProviders.ts`) and is re-exported from `services/usage.ts`, keeping the manifest module a light leaf instead of pulling the ~490-module usage dispatcher into the manifest route. ([#11903](https://github.com/diegosouzapw/OmniRoute/pull/11903)) — thanks @maxmad64bis / @pacocartones +- **feat(plugins):** `OMNIROUTE_PLUGINS_DIR` sets the directory the runtime plugin scanner reads — and the root the plugin manager installs into — overriding the `HOME`-derived default, so a Docker/K8s deployment can point straight at its bind-mounted plugin tree instead of moving `HOME` just to relocate the scan path. An image that exports no home no longer scans `/tmp/.omniroute/plugins` in silence: the resolved directory is logged once at startup as `scanner.dir_resolved`, naming the input that won. Unset, behaviour is unchanged. Distinct from the CLI-only `OMNIROUTE_PLUGIN_PATH`, which finds `omniroute-cmd-*` command packages and never reached this scanner ([#11906](https://github.com/diegosouzapw/OmniRoute/pull/11906)) — thanks @amaleta / @pacocartones +- **feat(leases):** add an explicit owner-authenticated status action that returns only the active lease's privacy-safe configured connection and provider labels, with generation fencing and no credential or internal-id disclosure ([#11910](https://github.com/diegosouzapw/OmniRoute/pull/11910)) — thanks @KaspaPulse +- **feat(providers):** import-from-file modal shows per-row API errors and ships a downloadable CSV template ([#12071](https://github.com/diegosouzapw/OmniRoute/issues/12071)) ([#12504](https://github.com/diegosouzapw/OmniRoute/pull/12504)) — thanks @HouMinXi +- **feat(providers):** dashboard search matches connection name and `baseUrl` so imported OpenAI-compat nodes surface on the provider card ([#12108](https://github.com/diegosouzapw/OmniRoute/issues/12108)) ([#12495](https://github.com/diegosouzapw/OmniRoute/pull/12495)) — thanks @HouMinXi +- **feat(routing):** add a `score` Auto router strategy that selects the highest configured weighted score and reuses `explorationRate`. ([#12155](https://github.com/diegosouzapw/OmniRoute/pull/12155)) — thanks @jacobsparts +- Add a runtime feature flag to disable universal context handoffs globally without changing the default behavior. ([#12167](https://github.com/diegosouzapw/OmniRoute/pull/12167)) — thanks @jacobsparts +- **feat(providers):** the provider plugin manifest now also advertises a `usage-supported` capability for the 46 providers whose usage API is accepted by the server and Dashboard routes, so integrators can distinguish "the server will serve quota for this provider" from "a fetcher is wired" without reading TypeScript. Discovery only — no fetcher or quota change. `usage-fetch` resolves on id or alias (the usage dispatcher accepts both); `usage-supported` resolves on id alone, matching the runtime guard `USAGE_SUPPORTED_PROVIDERS.includes(providerId)`. `USAGE_SUPPORTED_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/supportedProviders.ts`) and is re-exported from `providers.ts`, mirroring the `fetcherProviders` leaf from #11903 and keeping the manifest a light module. ([#12214](https://github.com/diegosouzapw/OmniRoute/pull/12214)) — thanks @maxmad64bis +- **feat(rankings):** order Free Provider Rankings by what each provider actually served — `GET /api/free-provider-rankings?sortBy=reliability` and a "Most reliable first" toggle on the page. Providers with too few calls to state a success rate keep their score order below the measured ones; the default order is unchanged ([#12218](https://github.com/diegosouzapw/OmniRoute/pull/12218)). — thanks @maxmad64bis +- **perf(sse):** defer `cloneLogPayload()` in the structured SSE collector until after the `maxEvents`/`maxBytes` cap check, eliminating ~9,800 wasted `structuredClone` calls per streaming response (65–71% faster `push()`). Reducer snapshot isolation restored for OpenAI and Responses summaries ([#12241](https://github.com/diegosouzapw/OmniRoute/pull/12241)) — thanks @PauloHSOliveira ([#12243](https://github.com/diegosouzapw/OmniRoute/pull/12243)) +- **feat(usage):** Devin CLI agentic quota (Codeium seat-management GetUserStatus) and OpenRouter key limits plus account credits now surface in Provider Limits ([#12256](https://github.com/diegosouzapw/OmniRoute/pull/12256) — thanks @Neuron-Mr-White) +- **feat(settings):** persist `headroomUrl` through Settings so status/start use the operator URL instead of only `HEADROOM_URL` ([#12306](https://github.com/diegosouzapw/OmniRoute/issues/12306)) ([#12487](https://github.com/diegosouzapw/OmniRoute/pull/12487)) — thanks @HouMinXi +- **feat(auto-combo):** Auto-Combo scoring can now weigh how often a provider/model has actually succeeded. The engine already carried that number on every candidate — 24 hours of usage history behind a ten-sample floor, real-time metrics otherwise — and the scoring function never read it, while the weight table described `stability` as if it did. `reliability` (`1 - failureRate`, with the same field precedence and the same rate-bounding the speed ranking already uses, so a corrupt reading means "nothing observed" rather than "fails every call") is now a declared factor shipping at weight `0`, so routing is unchanged until an operator gives it one, and the `stability` description now matches what that factor computes ([#12317](https://github.com/diegosouzapw/OmniRoute/pull/12317)) — thanks @maxmad64bis +- **feat(routing):** With `freeAccessPolicy: "strict"`, the read-only candidate listing (`GET /v1/auto-combo/{channel}/candidates`) no longer hides the candidates the zero-cost guard excludes — the same read-only transparency the resilience filter already honours (#9133). Each candidate now carries `freeAccessExclusion` saying why it would be kept out, and it tells an exhausted allowance apart from a quota reading that never arrived or went stale, which used to look identical from the outside. Routing is unchanged: the listing reports, it never enforces. The separate `excludeTosAvoid` guard still drops its candidates without a reason; that gap is now documented rather than closed ([#12319](https://github.com/diegosouzapw/OmniRoute/pull/12319)) — thanks @maxmad64bis +- **feat(radar):** The Radar catalog table now shows two facts it was already receiving from the feed and dropping on the floor: the per-model rate limits (requests and tokens, per minute and per day) in a new column, and a badge when a provider's terms state it may train on the prompts you send. A limit of zero renders as zero rather than "rate-only" — for a ceiling those are opposite facts — and a model with no training statement gets no badge, because an absent statement is not a guarantee ([#12320](https://github.com/diegosouzapw/OmniRoute/pull/12320)) — thanks @maxmad64bis +- **feat(radar):** explain Community, single-use, contributor, supporter, recovery, abuse, offers, and privacy rules before either Radar activation action, and remove the superseded fixed-PR grant promise from every UI locale ([#12342](https://github.com/diegosouzapw/OmniRoute/pull/12342)) +- **feat(gamification):** the dashboard Profile page now shows the real daily streak — `/api/gamification/level` returns `streak: { current, longest }` (per key with `apiKeyId`, operator-wide maximum otherwise) and the streak card reads it instead of a hard-coded 0 (#2403) ([#12377](https://github.com/diegosouzapw/OmniRoute/pull/12377)) — thanks @pacocartones +- **feat(gamification):** the dashboard leaderboard now shows each API key's display name under the Name column instead of a truncated key id; `GET /api/gamification/leaderboard` attaches `name` per entry (name only — no key material), while the shared ranking helper and the federation leaderboard stay id-only — thanks @pacocartones ([#12385](https://github.com/diegosouzapw/OmniRoute/pull/12385)) +- **feat(gamification):** enforce the documented 1000 XP/min per-API-key anti-cheat rate limit on the XP award path; over-limit awards are logged and skipped instead of persisted, and the sliding window now matches the timestamp format stored in `xp_audit_log` ([#2403](https://github.com/diegosouzapw/OmniRoute/issues/2403)) ([#12390](https://github.com/diegosouzapw/OmniRoute/pull/12390)) — thanks @pacocartones +- **feat(admin):** localize the gamification anomalies page — the loading state, the Status column and the Suspicious badge now come from the `common` catalog (new `common.suspicious` key propagated to every locale) — add it to the Gamification sidebar group as `gamification-admin` (`/dashboard/gamification/admin`), and expose the loading and empty states as polite `role="status"` live regions (#12401 — thanks @pacocartones) +- **feat(providers):** skip GitHub combo members missing from the live synced catalog, and drop Copilot models that are policy-disabled or hidden from the model picker ([#12473](https://github.com/diegosouzapw/OmniRoute/pull/12473)) — thanks @RaviTharuma +- **feat(i18n):** locale-expansion tooling — `npm run i18n:add-locale` adds a language to config, dashboard, docs mirrors, CLI, README/indexes and site in one command; browser, CLI and cookie detection resolve `uk`, `fil`/`tl`, `zh-Hant` (and the retired legacy `in` → `id`) via config aliases; `config/i18n.json` now ships in the npm package so the published CLI can read it; new real-translation ratio gate (`npm run i18n:check-ratio`, advisory) with a per-locale ratchet baseline; `run-translation --adopt` restores `.i18n-state.json`; `sync-language-bars` generates 🌐 bars from config; `validate_translation.py` loads its allowlist again. Retires the duplicate `in` locale (Indonesian mislabelled as Hindi) — 42 honest locales; saved `NEXT_LOCALE=in` / `OMNIROUTE_LANG=in` keep working. (#12496) +- **feat(api):** Emit gateway-measured `tokens_per_second` (TTFT excluded) on streaming usage and `X-OmniRoute-Tokens-Per-Second` when first-token latency is known ([#12616](https://github.com/diegosouzapw/OmniRoute/issues/12616)) ([#12631](https://github.com/diegosouzapw/OmniRoute/pull/12631)) — thanks @RaviTharuma +- **feat(opencode-plugin): map gateway cost/usage/tok/s onto OpenCode inference payloads** — the official plugin copies `X-OmniRoute-Response-Cost`, token counts, `X-OmniRoute-Tokens-Per-Second` / `usage.tokens_per_second`, TTFT, and the winning `X-OmniRoute-Model` onto the JSON/SSE body OpenCode already consumes. Missing tok/s is left unset (never `tokens / latency`). (#12636) — thanks @RaviTharuma +- **feat(combo):** add combo strategy `quota-weighted`: skip exhausted accounts, then weighted-draw by leftover / in-flight load; existing conversations stay pinned until the account is empty ([#12789](https://github.com/diegosouzapw/OmniRoute/pull/12789)) — thanks @HouMinXi +- **feat(models):** Account live listings become the chat catalog source for Claude, Codex, Copilot, and AGY; public metadata only fills prices on IDs those accounts already list. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **fix(models):** Union `agy` and `antigravity` live catalogs so `agy/gemini-3.8-flash-high` is not rejected after the prefix folds to `antigravity`. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **feat(opencode):** Add `@omniroute/opencode-plugin-v2`, an opencode v2 plugin that gives a v2 install a route to an OmniRoute gateway: it publishes models, combos and auto-combos into the host catalog, refreshes them lazily behind a 300s TTL, and keeps serving the last known catalog from an on-disk snapshot when the gateway is unreachable. The v1 plugin (`@omniroute/opencode-plugin`) is untouched and keeps working as before. The plugin adapts to the host instead of assuming one shape: it reads the shape the host seeds into the catalog draft and publishes the `api` block, the pre-`api` top-level fields, or both. The gateway key comes from the host credential store when one is connected, so no secret needs to sit in a config file; a source the gateway refuses is named in a warning instead of silently degrading the catalog to raw model ids; and tool schemas bound for a Gemini model are stripped of the JSON-Schema keywords Gemini rejects, so tool calling survives that route ([#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870)) — thanks @maxmad64bis +- feat(services): show sanitized CLIProxyAPI account health from its authenticated management API without exposing credentials, file paths, or raw account metadata (#6342) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add AnySearch free web search + URL extract (webFetch) with typed results, credential validation, REST routing, and MCP selection - fallback-only ([#11690](https://github.com/diegosouzapw/OmniRoute/pull/11690)) — thanks @Xxx91n +- **feat(dashboard):** display clamped `[0, 100]%` cached input token ratio in request logs table ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) — thanks @b3nw +- **feat(catalog):** add `OMNIROUTE_DISABLE_THINKING_LEVEL_VARIANTS` feature flag to optionally filter out thinking level variants from model catalog ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) — thanks @b3nw +- **feat(dashboard):** continuously export call logs to external analytics stores. A pluggable destination registry ships the full Logs-tab record set on an hourly `JobRegistry` cron, with a persisted per-destination cursor, batched inserts, a config UI rendered from each destination's own field descriptors, and a REST layer (`/api/log-export/*`) for CRUD, a connection test, and an on-demand run. A destination can opt into `includeBodies` to also ship the request and response payloads shown in the Logs detail pane, including the client and provider views of each call; this is off by default, and payloads inherit the dashboard's PII sanitisation, secret redaction and `noLog` handling. Google BigQuery is the first destination, using a service-account key stored encrypted at rest and streaming inserts keyed by call-log id, into a table that is day-partitioned on `timestamp` and clustered on `api_key_name`, `provider`, `model` and `status`. ([#11945](https://github.com/diegosouzapw/OmniRoute/pull/11945)) — thanks @dpozimski +- **feat(providers):** add MaxAI as a signed, OpenAI-compatible provider serving its 13 paid chat models (GPT-5.6 / Luna / Thinking, Claude 5 Sonnet, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite, Grok 4.1-fast / 4.5, DeepSeek V3.2 / R1, Llama 3.3 70B) through OmniRoute's `/v1` endpoint, with per-request HMAC-SHA1→SM3→AES request signing, live model + context-window discovery from `/models/get_config`, and prompted tool-calling translated to OpenAI `tool_calls` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI vision input — image_url content parts are forwarded inline in `message_content` to the 6 vision-capable models (GPT-5.6 / Luna / Thinking, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite) ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI image generation — 6 image models (gpt-image-1, dall-e-3, flux-1-schnell/dev/pro, sd3-medium) exposed through `POST /v1/images/generations` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI document RAG — inline base64 file/document attachments are uploaded to MaxAI (content-addressed `doc_id`) and attached to the chat via `doc_list` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** browserless MaxAI onboarding — email device-pair login (`/api/providers/[id]/login`) and signed access-token refresh, so a connection can be created and kept fresh without a real browser or Google OAuth ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** per-provider TLS impersonation profile (MaxAI presents a Windows Firefox-150 client fingerprint) so its bot-sensitive endpoints accept OmniRoute traffic ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** add **Nimble** as a web-search and web-fetch provider (`nimble-search`) — `/v1/search` routes to Nimble's search API at `lite` depth (locale, freshness and include/exclude domain filters mapped onto the shared request contract), and `/v1/web/fetch` routes to Nimble Extract, which covers all four fetch formats (`markdown`, `html`, `links`, `screenshot`) from a single call. One API key serves both surfaces. ([#11620](https://github.com/diegosouzapw/OmniRoute/pull/11620)) — thanks @wildcard +- The npm publish is automatic again, through npm Trusted Publishing (OIDC): the hosted `stage-npm` job publishes with a short-lived credential minted from GitHub's id-token — no `NPM_TOKEN`, no 2FA prompt, provenance attached. `publish_mode=staged` (owner approves with 2FA) and `direct` (token) remain available on `workflow_dispatch`. ([#11931](https://github.com/diegosouzapw/OmniRoute/pull/11931)) +- **feat(providers):** Add **Opper** as an API-key gateway provider — EU-hosted AI gateway with 700+ models from 30+ providers behind one OpenAI-compatible API and one key (`OPPER_API_KEY`); model ids use `provider/model` format (e.g. `anthropic/claude-sonnet-4-6`, `openai/gpt-5`); live model catalog at `https://api.opper.ai/v3/compat/models`; entry mirrors `requesty` (same shape, `passthroughModels: true`, no static seed) ([#11629](https://github.com/diegosouzapw/OmniRoute/pull/11629)) +- **feat(dashboard):** the `/dashboard/orchestration` snapshot hook now subscribes to the `agents` WebSocket channel (`agent.task.updated`) instead of `requests` as its refetch trigger, and relaxes its background poll from 5s to 30s while that WS connection is up — falling back to the tighter 5s cadence, reprogrammed live on any connect/disconnect transition, whenever the socket is down. ([#12409](https://github.com/diegosouzapw/OmniRoute/pull/12409)) +- **feat(dashboard):** Orchestration canvas quick wins — search box plus state/source/provider filter chips with a one-click clear, and per-source collapse/expand, all reflected in the URL so a filtered/collapsed view is shareable and survives a refresh; the detail drawer gained a "copy trace JSON" action and hardened error/empty-state and accessibility handling; the Agents-tab edges now animate traveling particles along active (running) connections; and the canvas node/edge status colors moved off fixed hex values onto theme-aware `--orch-status-*` CSS custom properties, so they adapt correctly to light/dark mode. ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- **chore(dashboard):** Orchestration UI hardening pass and the missing component/model test coverage it called for — `OrchestratorNode`/`ActivityNode`/`OverflowNode` rendering, the `?node=`/overflow-click page routing, the Agents-tab orchestrator-click no-op and `showCompleted` toggle, and the overview kanban's done-column sort order (#12270, #12271). ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- New `/dashboard/orchestration` page: live unified view of everything running — Cloud Agent, A2A and Conductor as a real-time graph (Agents tab), the combo cascade (Routing tab, reusing the Combo Live Studio) and a state kanban (Overview tab), with a detail drawer (trace, cost, approve/cancel). Read-only over existing APIs — no new backend. Canvas concept credit: PR #11815 design ([#12261](https://github.com/diegosouzapw/OmniRoute/pull/12261)) +- **feat(dashboard):** new "History" tab on `/dashboard/orchestration` — an Airflow-style grid of finished runs over a 24h/7d/30d preset window, one row per (source, identity), clicking a cell opens the existing detail drawer. It is backed by real persistence: A2A task lifecycle transitions are now written to the `a2a_tasks` table (purged after 30 days, configurable via `OMNIROUTE_A2A_HISTORY_RETENTION_DAYS`) and served by the new `GET /api/a2a/tasks/history` listing endpoint, with the task-detail route falling back to persisted history once a run leaves the in-memory snapshot. Conductor runs stay remote and are not persisted locally — the tab says so instead of silently omitting them. ([#12479](https://github.com/diegosouzapw/OmniRoute/pull/12479)) +- **feat(dashboard):** the orchestration detail drawer gained a "Repeat" action for Cloud Agent, A2A and Conductor tasks — a two-click confirm (click once to arm, click again within the confirm window to fire) re-submits the original prompt/input as a new run. The button is disabled with an explanatory tooltip whenever the original input can't be recovered from the loaded task detail (e.g. it never carried a prompt, or the detail failed to load). Two limitations of the A2A variant, by design: it targets the `/a2a` JSON-RPC endpoint, which authenticates with an API key only (`REQUIRE_API_KEY=true` or a configured `OMNIROUTE_API_KEY` makes a dashboard-session repeat answer `HTTP 400` — surfaced verbatim in the drawer's error line, never as a success), and the `message/send` call is SYNCHRONOUS: the POST blocks for the whole skill run, so the success confirmation only appears once the repeated task finishes. A dashboard-authenticated A2A creation path is deliberately left to a follow-up — widening the endpoint's auth posture is an operator decision, not a side effect of this feature. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(a2a):** A2A task execution now records which memories were consulted for the task's last user message as `metadata.memoryHits` (id/key/type/content-snippet) plus a `memory_hits` history event, purely for observability — the retrieved memory is never injected into a skill's prompt or behavior. Gated by the `OMNIROUTE_A2A_MEMORY_HITS` kill-switch (default enabled; set to `0` to skip the recall lookup entirely). The drawer's new "Memory used" section lists these hits for a2a tasks and is omitted whenever there are none. Known limitation: recall only resolves under the keyless posture — a keyed caller's task owner is a SHA-256 prefix of the API key, while memory rows are keyed by the database api-key id, and no hash→id lookup exists today, so the hit list stays empty for keyed callers. The recorded hits are also kept out of the task's own `input` (and therefore out of the persisted input and of the "Repeat" request body), so repeating a task never re-sends the previous run's memory snippets. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(providers):** add a Perplexity Agent API provider (`perplexity-agent` / `pplx-agent`) for Perplexity `/v1/responses`, including the documented Anthropic, OpenAI, Google, xAI, DeepSeek, Z.AI, Moonshot/Kimi, NVIDIA, and Perplexity model IDs plus Anthropic-model `max_output_tokens` compatibility. ([#12103](https://github.com/diegosouzapw/OmniRoute/pull/12103)) — thanks @AIB1TAL0S +- **feat(providers):** add RPD (Requests Per Day) limit to provider rate limit overrides across UI, schemas, DB, and i18n ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) — thanks @b3nw +- **feat(providers): add UC Direct (uncensored.com Developer API), the metered OpenAI-compatible surface.** A standard OpenAI-compatible passthrough (default executor) for uncensored.com's official REST API at `https://api.uncensored.com/api/v1`: `X-api-key` auth (never-expiring `uai_sk_live_` key), `POST /chat/completions` with streaming SSE and native tool-calling, and the full live metered catalog (82 models across 15 providers, discovered from the public `GET /v1/models`). Registered as provider `uc-direct` (alias `ucd`). Complements the un-metered `uc` persona provider — same models, metered credits and a plain API key instead of a subscription session. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(providers): add UC (uncensored.com), the un-metered subscription "persona" chat as an OpenAI-compatible provider.** A WebSocket web-app port: a durable Clerk credential mints a short-lived session token per connect (browserless — no API key), driving UC's persona socket. Ships the browserless email-code login (request → verify → harvest), the 19 verified persona models (Claude Opus, Gemini, Grok, GLM, Kimi, DeepSeek, MiniMax, incl. the uncensored variants), prompted `` tool-calling with a per-model code-style dialect + auto-cure retry for guardrailed models, live ``/reasoning split, streaming + non-streaming OpenAI responses, and full quota/auth error surfacing (paywall / message-limit / rate-limit → 429, invalid session → 401 re-login). Full multimodal parity via the persona blob-upload layer: **vision** (image input, 15 vision-capable models), **document RAG** (PDF/doc upload, server-side extraction), **image generation** (22 models), **video generation** (14 models, async signed-url → poll), and **TTS** (streaming MP3). Registered as provider `uc` (alias `ucn`). The metered OpenAI-compatible Developer API is a separate `uc-direct` provider. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(logs):** add copy-all button to request detail modal ([#11083](https://github.com/diegosouzapw/OmniRoute/pull/11083)) — thanks @AndrianBalanescu +- **feat(check):** make serviceKinds required and add the reverse-walk provider consistency gate ([#11392](https://github.com/diegosouzapw/OmniRoute/pull/11392)) — thanks @Tushar49 +- **feat(providers):** only advertise verified-working connections by default ([#11448](https://github.com/diegosouzapw/OmniRoute/pull/11448)) — thanks @alltomatos +- **feat(resilience):** add hierarchical concurrency admission ([#11493](https://github.com/diegosouzapw/OmniRoute/pull/11493)) +- **feat(providers):** derive + surface expiry for JWT-bearing web cookies (#11497) ([#11505](https://github.com/diegosouzapw/OmniRoute/pull/11505)) — thanks @oyi77 +- **feat(tunnels):** support named cloudflared tunnels via CLOUDFLARED_CONFIG ([#11561](https://github.com/diegosouzapw/OmniRoute/pull/11561)) — thanks @ggiak +- **feat(compression):** make proactive context-compression threshold a live setting ([#11564](https://github.com/diegosouzapw/OmniRoute/pull/11564)) — thanks @ragnar-claude +- **feat(resilience):** add provider peak-hour protection ([#11622](https://github.com/diegosouzapw/OmniRoute/pull/11622)) — thanks @Neuron-Mr-White +- **feat(providers):** add 1min.ai provider ([#11631](https://github.com/diegosouzapw/OmniRoute/pull/11631)) — thanks @zero-executioner +- **feat(dashboard):** collapsible JSON tree viewer for request/response payloads ([#11703](https://github.com/diegosouzapw/OmniRoute/pull/11703)) — thanks @hartmark +- **feat(routing):** add LiquidAI LFM2.5-2.6B free tier via OpenRouter ([#11752](https://github.com/diegosouzapw/OmniRoute/pull/11752)) — thanks @brick30llc-ctrl +- **feat(pricing):** add GLM-5.3-Flash pricing, model specs, and catalog registration ([#11830](https://github.com/diegosouzapw/OmniRoute/pull/11830)) — thanks @AndrianBalanescu +- **feat(routing):** add relayMode for schema-locked context handoffs ([#11839](https://github.com/diegosouzapw/OmniRoute/pull/11839)) — thanks @raheemuddin786 +- **feat(config):** add support for runtime system prompt configuration and hot-reloading ([#11841](https://github.com/diegosouzapw/OmniRoute/pull/11841)) — thanks @raheemuddin786 +- **feat(routing):** expand connection-aware quota prefilter across combo strategies (#11682) ([#11850](https://github.com/diegosouzapw/OmniRoute/pull/11850)) — thanks @HouMinXi +- **feat(models):** reactive discovery sync after Antigravity model-not-found 404 ([#11888](https://github.com/diegosouzapw/OmniRoute/pull/11888)) — thanks @MumuTW +- **feat(models):** auto-aliases and token limits from synced Antigravity discovery ([#11889](https://github.com/diegosouzapw/OmniRoute/pull/11889)) — thanks @MumuTW +- **feat(settings):** raise sticky round-robin limit caps to 1000 ([#12015](https://github.com/diegosouzapw/OmniRoute/pull/12015)) — thanks @adivekar-utexas +- **feat(resilience):** operator-configurable global credential health check interval ([#12043](https://github.com/diegosouzapw/OmniRoute/pull/12043)) — thanks @adivekar-utexas +- **feat(quota):** use official OpenCode Go usage API ([#12124](https://github.com/diegosouzapw/OmniRoute/pull/12124)) — thanks @ddarkr +- **feat(resilience):** default the credential health check sweep to 60 minutes ([#12138](https://github.com/diegosouzapw/OmniRoute/pull/12138)) — thanks @adivekar-utexas +- **feat(quality):** new-code mode for the complexity and dead-code ratchets (Clean as You Code) ([#12142](https://github.com/diegosouzapw/OmniRoute/pull/12142)) +- **feat(dashboard):** orchestration canvas — unified model + snapshot hook (part 1/2) ([#12156](https://github.com/diegosouzapw/OmniRoute/pull/12156)) +- **feat(usage):** add Kilo Code balance and Kilo Pass quotas ([#12178](https://github.com/diegosouzapw/OmniRoute/pull/12178)) — thanks @killer30001000 +- **feat(providers):** refresh vendored ChatGPT Web connector to v4.0.7 ([#12181](https://github.com/diegosouzapw/OmniRoute/pull/12181)) — thanks @backryun +- **feat(providers):** manual "Clear cooldown" action in the cooling panel ([#12224](https://github.com/diegosouzapw/OmniRoute/pull/12224)) — thanks @patrykkopycinski +- **feat(providers):** restore ChatGPT Web via clean-room browser transport ([#12239](https://github.com/diegosouzapw/OmniRoute/pull/12239)) — thanks @backryun +- **feat(sse):** wire the PROVIDER_PROFILES window gate into the global provider cooldown ([#12247](https://github.com/diegosouzapw/OmniRoute/pull/12247)) +- **feat(providers):** modernize CLOVA Studio chat and embeddings ([#12277](https://github.com/diegosouzapw/OmniRoute/pull/12277)) — thanks @backryun +- **feat(browser):** adopt Obscura as primary headless browser engine with Chromium fallback ([#12286](https://github.com/diegosouzapw/OmniRoute/pull/12286)) — thanks @opensource-elearning +- **feat(sse):** full language parity for output styles (es/de/fr/it/ru/zh + autoDetect) ([#12289](https://github.com/diegosouzapw/OmniRoute/pull/12289)) +- **feat(quality):** complete test:scoped — --full map rebuild, stdin selection, CI loader parity (#8084 D1) ([#12353](https://github.com/diegosouzapw/OmniRoute/pull/12353)) +- **feat(providers):** refresh Fable, Cursor, and Devin catalogs ([#12367](https://github.com/diegosouzapw/OmniRoute/pull/12367)) — thanks @backryun +- **feat(video):** redact transcript text from logs and durable memory (#12150 P1) ([#12427](https://github.com/diegosouzapw/OmniRoute/pull/12427)) +- **feat(dashboard):** parent-link, genuine-continuation badge, and modal perf fixes ([#12448](https://github.com/diegosouzapw/OmniRoute/pull/12448)) — thanks @hartmark +- **feat(dashboard):** adaptive context-budget dial on compression panel ([#12488](https://github.com/diegosouzapw/OmniRoute/pull/12488)) — thanks @HouMinXi +- **feat(providers):** refresh Z.ai Web models and browser transport ([#12524](https://github.com/diegosouzapw/OmniRoute/pull/12524)) — thanks @backryun +- **feat(video):** redact raw client-snapshot transcript fields in the detailed log (#12150 P2) ([#12528](https://github.com/diegosouzapw/OmniRoute/pull/12528)) +- **feat(providers):** refresh NVIDIA hosted models ([#12538](https://github.com/diegosouzapw/OmniRoute/pull/12538)) — thanks @backryun +- **feat(quota):** Moonshot Open Platform balance and TPD lock for custom nodes ([#12590](https://github.com/diegosouzapw/OmniRoute/pull/12590)) — thanks @HouMinXi +- **feat(video):** redact transcript in the in-memory pending-request snapshot (#12430 item 6) ([#12596](https://github.com/diegosouzapw/OmniRoute/pull/12596)) +- **feat(dashboard):** link a log entry's Conversation Context to its owning conversation ([#12646](https://github.com/diegosouzapw/OmniRoute/pull/12646)) — thanks @hartmark +- **feat(catalog):** eligibility-gated free-tier bucket ([#12669](https://github.com/diegosouzapw/OmniRoute/pull/12669)) +- **feat(db):** fail-closed previous_response_id continuation for redacted video turns (#12150 P2b) ([#12707](https://github.com/diegosouzapw/OmniRoute/pull/12707)) +- **feat(gamification): pay the documented `streak_bonus` and `badge_unlock` XP rewards.** `XP_REWARDS` listed both rewards but the award pipeline never paid them: the private reward table in `events.ts` omitted them, `updateStreak()` did not report when a streak extended, and badge unlocks carried no XP. Every request that extends a daily streak now pays `streak_bonus × streak length` once per UTC day (guarded by a same-day `xp_audit_log` check), and every badge unlocked through the pipeline pays `badge_unlock` once per badge (guarded by the `user_badges` primary key; `unlockBadge()` now reports whether it inserted). Bonus XP flows through the same `addXp` + level sync + global/weekly/monthly leaderboard path as action XP, so level-ups and rankings include it. The Radar supporter recognition unlock stays XP-free. (#12522 — thanks @pacocartones) +- **feat(providers):** advertise a `free-tier` capability in the provider plugin manifest for every provider with documented free models, so sidecars and dashboards can filter free-capable providers without reading the quota catalog ([#12786](https://github.com/diegosouzapw/OmniRoute/pull/12786)) — thanks @maxmad64bis +- **feat(providers):** Added EURouter as an OpenAI-compatible API-key gateway (`https://api.eurouter.ai/v1`), with live model discovery via `passthroughModels`. Its copy states that models are served by third-party upstreams listed per model, so an EU-based router is not read as EU data residency for inference. ([#13025](https://github.com/diegosouzapw/OmniRoute/pull/13025)) — thanks @ntdatt812 +- **feat(providers):** Added GreenPT as an OpenAI-compatible API-key provider (`https://api.greenpt.ai/v1`), with live model discovery via `passthroughModels`. No free-inference badge: the published docs describe a free API subscription billed per token, not a free tier. ([#13024](https://github.com/diegosouzapw/OmniRoute/pull/13024)) — thanks @ntdatt812 +- **feat(i18n):** 9 new locales — Greek (`el`), Croatian (`hr`), Serbian (`sr`), Lithuanian (`lt`), Estonian (`et`), Latvian (`lv`), Slovenian (`sl`), Maltese (`mt`), Irish (`ga`) — across the dashboard, docs mirrors, CLI, README and the site; OmniRoute now ships every official EU language (51 locales). (#13044) +- **feat(i18n):** 8 new locales — Kannada (`kn`), Malayalam (`ml`), Odia (`or`), Punjabi (`pa`), Nepali (`ne`), Sinhala (`si`), Burmese (`my`), Khmer (`km`) — across the dashboard, docs mirrors, CLI, README and the site (59 locales). The translator now restores the ICU literal escape around angle placeholders and splits oversized docs sections before translating. (#0000) ([#13660](https://github.com/diegosouzapw/OmniRoute/pull/13660)) +- feat(providers): **list Agnes 3.0 Flash as the current free chat model, drop retired 1.5 Flash, add Image 2.0/2.5 Flash plus Video 2.5/2.5 Flash, and discover the live `/v1/models` catalog (including the CN host `api.agnes-ai.cn`).** `agnes-1.5-flash` now forwards to `agnes-3.0-flash`. Video 2.5 polls `GET /v1/videos/{id}` (not the V2.0 `/agnesapi` contract). Live `/v1/models` (2026-09-09) no longer serves 1.5; the wiki marks it deprecated. 3.0 Flash is 512K context / 65,536 max output, same window as 2.5. CN-region keys use the existing per-connection base-URL field, default stays `apihub.agnes-ai.com`. ([#13120](https://github.com/diegosouzapw/OmniRoute/pull/13120)) — thanks @HouMinXi +- **feat(sse):** Codex and OpenAI catalogs list GPT-6 Astra with effort aliases (`-low` through `-ultra`); Codex CLI identity pins `@openai/codex@0.153.4` in lockstep with the image ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **feat(dashboard):** the orchestration History tab gained a "Compare runs" mode — toggling it turns each grid cell into a 2-item selection queue (a 3rd click drops the oldest pick), and picking two cells opens a side-by-side comparison panel instead of the usual detail drawer. The panel fetches both runs' detail the same way the drawer does (falling back to persisted history once a run leaves the live TTL window) and shows, per side: identity/source/state, start time, a signed `right - left` delta for duration/cost/event count, the event timeline aligned by index, and any memory hits. One side's fetch failing never blocks the other, and a delta is only ever computed when both sides have a finite value — otherwise it renders "—", never `NaN`. Comparing two runs from different sources or skills still works; a banner marks the deltas as informational rather than hiding them, since the two runs aren't a strict apples-to-apples pair. ([#12677](https://github.com/diegosouzapw/OmniRoute/pull/12677)) +- **feat(dashboard):** badge a conversation that never reached a clean stop ([#12717](https://github.com/diegosouzapw/OmniRoute/pull/12717)) — thanks @hartmark +- **feat(release):** reconcile-changelog tool + version-anchored fragment aggregation ([#12987](https://github.com/diegosouzapw/OmniRoute/pull/12987)) +- **feat(mcp):** carry context_length through MCP list_models_catalog projection (#12776) ([#13406](https://github.com/diegosouzapw/OmniRoute/pull/13406)) — thanks @KooshaPari +- **feat(cli):** make startup readiness budget configurable (#13369) ([#13433](https://github.com/diegosouzapw/OmniRoute/pull/13433)) — thanks @KooshaPari + ### 🐛 Bug Fixes +- **security(streaming):** sanitize generic mid-stream error messages before emitting OpenAI, + Responses, or Claude SSE failure frames and before diagnostic logging, while preserving raw + failures for internal classification and keeping client disconnects out of provider failure state. ([#12457](https://github.com/diegosouzapw/OmniRoute/pull/12457)) +- **fix(claude):** `blockExtraUsage: false` no longer lets 5h quota preflight skip the account; extra usage is billed after the session bar is gone, so the request must reach Anthropic ([#12803](https://github.com/diegosouzapw/OmniRoute/pull/12803)) — thanks @HouMinXi +- Sanitize HuggingChat conversation-creation and message-send transport failures before they reach client error bodies or provider logs. ([#12467](https://github.com/diegosouzapw/OmniRoute/pull/12467)) +- **fix(dashboard):** Keep local and theme-aware provider SVG icons at a definite layout size so Chromium does not collapse them to 0×0 after the v3.8.50 image-rendering change ([#12054](https://github.com/diegosouzapw/OmniRoute/pull/12054)) — thanks @ponkcore +- **fix(security):** Sanitize provider and runtime failures before public API, SSE and MCP responses and before persistent request, proxy and usage logs, preventing credentials, stack traces and host filesystem paths from crossing those boundaries while preserving stable error codes and useful diagnostics. ([#12506](https://github.com/diegosouzapw/OmniRoute/pull/12506)) +- **fix(cli):** `omniroute tunnel create` no longer crashes with `Cannot read properties of undefined (reading optsWithGlobals)` — removed the duplicate positional argument that caused Commander.js to misalign the action callback parameters ([#12295](https://github.com/diegosouzapw/OmniRoute/issues/12295)) ([#12368](https://github.com/diegosouzapw/OmniRoute/pull/12368)) — thanks @gonisulaimann +- Stop advertising Gemini Live-only models as supported audio endpoints until OmniRoute proxies the bidirectional Live protocol. ([#11710](https://github.com/diegosouzapw/OmniRoute/pull/11710)) — thanks @RaviTharuma +- **fix(kie):** reroute `flux/kontext` off the KIE Market `createTask` flow — it is catalogued with `isMarket: true` but has no Market catalog page, so KIE rejected it with "model name not supported"; it now hits the dedicated `POST /api/v1/flux/kontext/generate` / `GET /api/v1/flux/kontext/record-info` endpoints instead (#11296). ([#11985](https://github.com/diegosouzapw/OmniRoute/pull/11985)) — thanks @hartmark +- **fix(kie):** correct 12 more KIE Market catalog ids that were sent to `createTask` unchanged but diverge from KIE's documented upstream `model` values — GPT Image 2 T2I/I2I (drops the `gpt/` prefix), GPT Image 1.5 T2I/I2I (`gpt-image/` namespace), Seedream 5.0 Lite T2I/I2I (drops the `.0`), all 4 Flux 2 variants (`flux-2/` namespace, generic variant renamed `flex`), and Wan 2.7 Image / Image Pro (dash instead of dot) — each verified individually against the literal example request published on docs.kie.ai. `#11326`'s "everything else already matches" claim was wrong a second time (#11296); `z-image/4.0-*`/`z-image/4.5-*` and `flux/kontext` remain open, documented as unresolved in `KIE_MARKET_UPSTREAM_MODEL_IDS`'s comment pending further verification. ([#11667](https://github.com/diegosouzapw/OmniRoute/pull/11667)) +- **fix(dashboard):** `useApiKeySave.handleSaveApiKey` no longer forces a full upstream `/models` catalog sync on every non-curated provider connection save — callers can now pass `skipModelSync: true` to opt out, so a workflow that only wants to add one manual model no longer floods the provider's available-models list with hundreds/thousands of synced entries. The flag is a client-side intent signal only and is stripped before the connection payload is POSTed to `/api/providers`; default behavior (full sync on save) is unchanged when the flag is omitted (#11324) ([#11664](https://github.com/diegosouzapw/OmniRoute/pull/11664)) +- **Provider connections:** keep `tokenExpiresAt` when a connection is created. The create-path allowlist omitted it, so every insert stored NULL and the dashboard token badge could read a fresh connection as expired until its first background refresh ([#11368](https://github.com/diegosouzapw/OmniRoute/pull/11368)). — thanks @ntdatt812 +- **fix(combos):** the combo builder's precision-select, global-model-search, and manual-entry flows now serialize a model step's `model` string using the provider's already-computed routing-alias prefix (e.g. `oc/`) instead of rebuilding it from the raw canonical `providerId`, fixing the no-auth "OpenCode Free" provider (`opencode`) being routed to the unrelated paid "OpenCode Zen" provider (`opencode-zen`) because `opencode` doubles as a manual routing-prefix override ([#11433](https://github.com/diegosouzapw/OmniRoute/issues/11433)). ([#11670](https://github.com/diegosouzapw/OmniRoute/pull/11670)) +- **fix(radar):** the catalog feed cache now keeps `generatedAt`, the date the feed's data was built, next to `fetchedAt`, the date this install downloaded it (#11435). The feed schema requires that date and the sync path validates it, but the cache dropped it — so a feed fetched minutes ago and one carrying weeks-old figures looked identical to everything downstream, including the dashboard's "Last fetched" line. `getRadarCatalog().meta` and `GET /api/radar/status` now report both dates, the latter as its own field rather than folded into `version` — and omitted entirely for the offers and intel caches, which keep no build date, where a `null` would read as "unknown" rather than "never stored". The dashboard still shows only the fetch time; surfacing the build date there needs a new translated label and is left to a follow-up. Rows cached before migration 163 read back as `null`: unknown stays unknown instead of borrowing the fetch time. The referrals cache has persisted the same date since migration 142. — thanks @maxmad64bis +- **fix(cli):** postinstall no longer fills `JWT_SECRET` and `API_KEY_SECRET` in the installed package's `.env` (#11436). `.env.example` ships both blank on purpose: the server restores them from its durable store, or generates and persists them there, in `ensureSecrets()`. Pre-filling them defeated that — the file lives inside the package directory, so `npm i -g` replaced it and postinstall wrote _different_ values, while `ensureSecrets()` (which only acts on an empty variable) never got to restore the real ones. Both secrets rotated silently on every update, invalidating dashboard sessions and API-key CRCs. `STORAGE_ENCRYPTION_KEY` left the same list for the same reason in #1622; its comment pointed at a function that no longer exists and now names the real provisioning path. — thanks @maxmad64bis +- **fix(cli):** the CLI now says when a loaded `.env` lives inside the installed package directory (#11437). It already announces every env file it reads, without distinguishing the ones that survive an update from the one that does not: `npm i -g` replaces the package directory wholesale, so values set there are gone at the next update, silently. The warning names the durable path to move them to, and fires only when that file actually supplied a value — a file entirely shadowed by a durable one supplied nothing. A development checkout stays silent: there the same path is stable and documented in `SETUP_GUIDE.md`. — thanks @maxmad64bis +- **fix(free-models):** the shared `isFreeModel()` predicate no longer reports catalog entries marked `freeType: "discontinued"` as free, so `hidePaidModels` can't route to Pollinations' seven premium models that now require a paid key ([#11441](https://github.com/diegosouzapw/OmniRoute/pull/11441)) — thanks @maxmad64bis +- fix(ui): let AnySearch use the normal provider-icon fallback when LobeHub has no matching icon (#11449) +- Fixed the v3.8.50 Costs and Analytics dashboards so flat-rate Claude Code usage can be shown as an explicitly requested token-price estimate without changing default billed-cost semantics. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed archived usage retention so each request is priced individually instead of pricing a day's summed tokens once, which understated archived cost whenever a day mixed cache-heavy and ordinary requests. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed the Costs dashboard so it discloses when displayed figures include flat-rate token-price estimates instead of labelling them as billed spend, using the flag the analytics API already returns; the month-end projection and the CSV/JSON exports carry the same marker, and billed-cost mode is unchanged. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- **fix(combo):** attach the same combo-diagnostics trace (`poolSize`/`attemptOrder`/`excluded`/`terminalReason`, plus `x-omniroute-combo-*` headers) to the round-robin strategy's and the nested pipeline/fusion runtime-unit loop's "Maximum combo retry limit reached" 503 that the priority-strategy path already attaches for the identical terminal condition — previously those two paths returned a bare, contextless 503 ([#11462](https://github.com/diegosouzapw/OmniRoute/issues/11462)). ([#11665](https://github.com/diegosouzapw/OmniRoute/pull/11665)) +- **fix(bun):** use native `bun:sqlite` in `bootstrap-env` and `sync-env` to avoid loading `better-sqlite3` N-API addon during Bun startup ([#11468](https://github.com/diegosouzapw/OmniRoute/pull/11468)) — thanks @TheDemonTuan +- **fix(docker-bun):** make `Dockerfile.bun` install reliable with pre-install script helpers and native `bun:sqlite` smoke check ([#11470](https://github.com/diegosouzapw/OmniRoute/pull/11470)) — thanks @TheDemonTuan +- **fix(docker-bun):** remove every vendored `better-sqlite3` native addon from the Bun runtime image so startup cannot abort before the `bun:sqlite` fallback ([#11482](https://github.com/diegosouzapw/OmniRoute/pull/11482)) — thanks @TheDemonTuan +- **fix(autoCombo):** effort/alias model variants (`gpt-5.6-sol-xhigh`, `gpt-5.6`, cursor's `claude-4.6-opus-high`) inherit their base model's task fitness instead of falling to the wildcard 0.5 ([#11492](https://github.com/diegosouzapw/OmniRoute/pull/11492)) — thanks @MumuTW +- fix(db): rate-limit repeated Arena ELO leaderboard fetch-failure warnings instead of logging one per sync attempt (#11500) ([#11989](https://github.com/diegosouzapw/OmniRoute/pull/11989)) — thanks @hartmark +- **fix(db):** dedupe the raw `[Encryption] Decryption failed...` log line emitted by the lazy-decrypt views (`createLazyRowProxy`/`createLazyConnectionView`), which power `getProviderConnections()` and were re-triggering that line on every CredentialHealth/model-sync cycle for the same corrupt or stale-key credential — a fresh Proxy over a fresh row on every cycle meant the per-proxy memoization never suppressed it, unlike the dedup `decryptConnectionFields()` already had since [#9927](https://github.com/diegosouzapw/OmniRoute/issues/9927). Now shares that dedupe tracking so the line logs at most once per credential ([#11500](https://github.com/diegosouzapw/OmniRoute/issues/11500)). ([#11678](https://github.com/diegosouzapw/OmniRoute/pull/11678)) +- **fix(intelligence):** Arena ELO sync stores a synthesized base-model row for every effort/harness variant (`claude-opus-5-max` → `claude-opus-5`, `gpt-5.6-sol-xhigh (codex-harness)` → `gpt-5.6-sol`), so bare model requests reach the synced intelligence layer instead of the static fallback table; the hand-maintained `MODEL_ALIAS_MAP` — which collapsed generations (`gpt-5.5` → `gpt-5`) — is gone ([#11506](https://github.com/diegosouzapw/OmniRoute/pull/11506)) — thanks @MumuTW +- **fix(autoCombo,sse):** catalog hygiene — the static task-fitness table no longer ranks retired models above live ones (dead rows dropped, patterns matched on segment boundaries), the 7 deprecation aliases that forwarded to retired or non-existent models now point at the vendors' published replacements, an alias is no longer rewritten when the serving provider offers the id as-is, and a new `check:model-lifecycle` gate diffs both tables against a vendor lifecycle snapshot ([#11507](https://github.com/diegosouzapw/OmniRoute/pull/11507)) — thanks @MumuTW +- **fix(sse):** stop dropping resolved `thoughtSignature` values on parallel (multi tool-call) turns sent to Gemini 3.x — the claude→gemini and openai→gemini translators previously kept the signature only on the *first* function call of a message, causing Gemini to reject subsequent calls in the same turn with HTTP 400 "Function call is missing a thought_signature"; each function call now keeps its own resolved signature ([#11510](https://github.com/diegosouzapw/OmniRoute/issues/11510)). ([#11669](https://github.com/diegosouzapw/OmniRoute/pull/11669)) +- Treat Docker publishing from a non-default release branch as an expected skip instead of a failed workflow, while preserving the guard that prevents it from overwriting the `next` channel. ([#11527](https://github.com/diegosouzapw/OmniRoute/pull/11527)) — thanks @pacocartones +- fix(search): prefer credentialed providers over duckduckgo-free fallback (#11524) — the fallback-only loop ran before the credentialed-providers loop in `executeWebSearch`, making configured providers unreachable when `duckduckgo-free` was available. ([#11565](https://github.com/diegosouzapw/OmniRoute/pull/11565)) — thanks @vsd2807 +- **fix(sse):** cap the upstream headers-wait phase for STREAMING requests to a client-realistic ceiling (110s, under Codex's own ~120s hard client-abort window) instead of the flat 10-minute `FETCH_TIMEOUT_MS` default — that default was 5x longer than the body-phase readiness watchdog's own adaptive bound, so a request whose upstream never returned any response at all (not even headers, e.g. a stalled NVIDIA target behind a tool-heavy Responses→Chat translation) kept the client connection alive on keepalives only, guaranteeing the client's own patience ran out first with an opaque 499 instead of OmniRoute detecting and failing the stall fast. Non-streaming requests are unaffected — they keep the existing flat default (`open-sse/utils/fetchStartTimeoutPolicy.ts`) (#11526) ([#11668](https://github.com/diegosouzapw/OmniRoute/pull/11668)) +- **fix(ci):** protocol-clients E2E harness boots the peer-stamped custom server (`run-next.mjs dev`) instead of the bare `next` CLI, so `LOCAL_ONLY` locality resolves from the real TCP peer stamp and `/api/mcp/audit` answers 200 (open bootstrap preserved via an `OMNIROUTE_E2E_BOOTSTRAP_MODE=open` credential clear in `run-next.mjs`, pinned to loopback `HOST`) instead of a deterministic 403 (#11535) ([#11549](https://github.com/diegosouzapw/OmniRoute/pull/11549)) — thanks @jonlwheat2-gif +- **fix(docs):** the Auto-Combo engine is described with the number of factors it actually declares. Four different counts were in circulation — 6, 9, 13 and 14 across `AGENTS.md`, four guides and six code comments — while `DEFAULT_WEIGHTS` declares 15. `check:docs-counts` now reads that number from the source, so adding a factor without updating the prose fails CI ([#11545](https://github.com/diegosouzapw/OmniRoute/pull/11545)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **fix(quota-share):** in-flight leases are per request, so an aborted request's slot ages out instead of being kept alive by later traffic on the same connection ([#11547](https://github.com/diegosouzapw/OmniRoute/pull/11547)) — thanks @abhisheksharma2411 +- The free-tier summary route now serves the refreshed Radar catalog when the feed is active — the same numbers the dashboard shows — and states which catalog answered plus its real build date, instead of always reporting release-frozen figures with a stale curation date. The supporter-key live feed stays reserved to authenticated callers of the instance. ([#11550](https://github.com/diegosouzapw/OmniRoute/pull/11550)) — thanks @maxmad64bis +- **sse:** fix LiveWS/embed-WS servers crashing at startup under the Node/tsx runtime — `liveServer.ts`, `embedWsProxy.ts` and `apiBridgeServer.ts` imported `@/shared/utils/httpClientAbortGuard` without the `.mjs` extension, so the client-abort crash guard added by [#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556) was unreachable and every dependent test failed with `ERR_MODULE_NOT_FOUND` ([#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556)). ([#11671](https://github.com/diegosouzapw/OmniRoute/pull/11671)) — thanks @jonlwheat2-gif / @yxyxy +- fix(chat): shed any of the matched provider-node's routing identifiers (public prefix or internal id) before model lookup, so composites like `//` no longer reach upstream verbatim (#11557) — thanks @maxmad64bis +- **fix(cache):** a semantic-cache entry now expires on its TTL instead of surviving until the next UTC midnight, and `dbEntries` stops counting expired rows ([#11573](https://github.com/diegosouzapw/OmniRoute/pull/11573)) — thanks @ntdatt812 +- **fix(api):** `/v1/models` schedules its stale-while-revalidate rebuild through Next's `after()` again, so a stale catalog reaches the client before the rebuild blocks the event loop ([#11574](https://github.com/diegosouzapw/OmniRoute/pull/11574)) — thanks @ntdatt812 +- **fix(check):** the `known-symbols` executor conformance gate awaits the now-async `getExecutor()`, so it stops reporting all 142 registered aliases as dead symbols and can detect a lazy import that fails ([#11582](https://github.com/diegosouzapw/OmniRoute/pull/11582)) — thanks @ntdatt812 +- **fix(pack):** register `bin/cli/utils/volatileEnvPath.mjs` in `PACK_ARTIFACT_REQUIRED_PATHS`, so the prune can no longer silently drop a module the published CLI imports on every boot ([#11588](https://github.com/diegosouzapw/OmniRoute/pull/11588)) — thanks @ntdatt812 +- **i18n(vi):** translate the five reliability-column strings on the free-provider rankings page, which rendered as `__MISSING__:` markers in the Vietnamese UI ([#11589](https://github.com/diegosouzapw/OmniRoute/pull/11589)) — thanks @ntdatt812 +- **resilience:** restore the expired-connection retry-budget probe in the token-health sweep — the `!isGitHubAccessTokenOnlyConnection` carve-out reintroduced by #11608 contradicted the boundary pinned by #11592, so a GitHub connection parked at `expired` with retry budget remaining was never probed and could never self-heal ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)). ([#11672](https://github.com/diegosouzapw/OmniRoute/pull/11672)) — thanks @jonlwheat2-gif / @ntdatt812 +- **fix(dashboard):** Provider Stats now displays combo success rates on their native 0–100 percentage scale instead of multiplying them twice ([#11595](https://github.com/diegosouzapw/OmniRoute/pull/11595)) — thanks @pacocartones +- **fix(providers):** route DuckDuckGo Web Chat through the verified `duck.ai` endpoint, refresh its free model catalog, and avoid forwarding unsolved anti-abuse challenges ([#11597](https://github.com/diegosouzapw/OmniRoute/pull/11597)) — thanks @solstxce +- **fix(ui):** Console log Refresh and Copy controls now expose localized accessible names, keep copy actions visible on keyboard focus, and announce copy completion safely ([#11599](https://github.com/diegosouzapw/OmniRoute/pull/11599)) — thanks @pacocartones +- Dropped the stale-`.eslintcache` `restore-keys` fallback from both "Restore ESLint file cache" steps in `ci.yml`, so the blocking `Lint` job can no longer be served per-file verdicts computed under a different lint config, suppressions file or lockfile. `quality.yml` had already dropped it in #11963; `ci.yml` — the workflow that actually gates PRs — had not (#11600). ([#11996](https://github.com/diegosouzapw/OmniRoute/pull/11996)) +- fix(ci): pass `--pass-on-unpruned-suppressions` in `run-eslint-json.mjs` so the CI Lint job no longer fails when a suppression is merely orphaned by a genuine fix, mirroring the identical fix already in `validate-release-green.mjs` (#11600) ([#11983](https://github.com/diegosouzapw/OmniRoute/pull/11983)) — thanks @hartmark +- fix(handoff): enforce provider allowlist for universal handoff (#11602) — universal handoff now skips summarization when the selected summary model's provider is not included in the configured provider allowlist. ([#11619](https://github.com/diegosouzapw/OmniRoute/pull/11619)) — thanks @vsd2807 +- **fix(dashboard):** Show a stable error state when Search Analytics returns an HTTP error or malformed data ([#11603](https://github.com/diegosouzapw/OmniRoute/pull/11603)) — thanks @pacocartones +- **test(gamification):** pin the aggregate profile level to the XP-derived semantics of #11604 — `getAggregateXp()` now derives `currentLevel` from the summed XP (`calculateLevel(sum)`), not `MAX(stored current_level)`, and the #3484 fixture levels are aligned with the XP curve ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)). ([#11676](https://github.com/diegosouzapw/OmniRoute/pull/11676)) — thanks @jonlwheat2-gif / @pacocartones +- **fix(dashboard):** Keep the Profile level and progress aligned with aggregate XP, including bounded handling for invalid totals ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)) — thanks @pacocartones +- **fix(dashboard):** Prevent locked hidden badges from revealing their icon or opening private badge details before they are earned ([#11605](https://github.com/diegosouzapw/OmniRoute/pull/11605)) — thanks @pacocartones +- **fix(dashboard):** Restore keyboard focus after shared modals close and cancel delayed autofocus during cleanup ([#11607](https://github.com/diegosouzapw/OmniRoute/pull/11607)) — thanks @pacocartones +- **fix(dashboard):** Enable Enter and Space activation for clickable data-table rows without hijacking nested controls ([#11610](https://github.com/diegosouzapw/OmniRoute/pull/11610)) — thanks @pacocartones +- **fix(autoCombo,sse):** vendor-retired catalog ids are dropped from the auto-combo candidate pool and no longer win on leftover `arena_elo` / `user_override` rows; `getModelLifecycleDecision` consults `model-lifecycle.json` (prefix-stripped) so aggregator traffic is not `untracked` for ids the snapshot already knows ([#11625](https://github.com/diegosouzapw/OmniRoute/issues/11625)) ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)) — thanks @MumuTW +- **test(chatcore):** move Codex/Claude combo fixtures off models the lifecycle guard now rejects — `gpt-5.1-codex`/`gpt-5-codex` are vendor-retired (snapshot, #11626) and `claude-3-5-sonnet-20241022` is shut down, so native-passthrough and combo-fallback tests switched to `gpt-5.6-sol` and `claude-sonnet-4.6` ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)). ([#11675](https://github.com/diegosouzapw/OmniRoute/pull/11675)) — thanks @jonlwheat2-gif / @MumuTW +- fix(opencode-plugin): stop a free-tier budget that rounds up across a magnitude from rendering as `1000K`/`1000M` in the model picker — `fmtTokens` chose its unit from the raw token count and then rounded with `toFixed(1)`, so 999,950–999,999 printed as `1000K` rather than `1M` and just under a billion printed as `1000M` rather than `1B` ([#11684](https://github.com/diegosouzapw/OmniRoute/pull/11684)) — thanks @abhisheksharma2411 +- Cap prose-derived Antigravity quota resets at the configured model cooldown maximum while preserving authoritative Retry-After headers and Google RetryInfo hints. (#11695) Thanks @pacocartones. ([#11823](https://github.com/diegosouzapw/OmniRoute/pull/11823)) +- Compare `_rebaseline_*` notes structurally in the ratchet-bank verifier so an object-valued note no longer reports itself as rewritten on an unmodified tree — the nightly `bank-ratchet-shrinks` lane had been aborting on every run since 2026-08-11, leaving 41,672 lines of earned file-size shrinks unbanked (#11702). ([#11892](https://github.com/diegosouzapw/OmniRoute/pull/11892)) — thanks @MumuTW +- **fix(build):** `prepublish.ts` bundles the ChatGPT Web (Codex) MCP bridge through `runBuildTool()` instead of spawning `npx.cmd` raw, fixing the build crash on Node ≥ 20/Windows where `.cmd` shims cannot be spawned without a shell (EINVAL) ([#11704](https://github.com/diegosouzapw/OmniRoute/issues/11704)) ([#11706](https://github.com/diegosouzapw/OmniRoute/pull/11706)) — thanks @jonlwheat2-gif +- fix(codex): keep `parallel_tool_calls:false` on the translated Codex Responses Lite path (#11707) ([#11984](https://github.com/diegosouzapw/OmniRoute/pull/11984)) — thanks @hartmark +- **fix(tests):** the new Gemini Web Images retirement test (#11708) called the now-async `getExecutor()` synchronously, throwing `TypeError: getExecutor(...).getProvider is not a function` instead of asserting Gemini Web chat stays available. Awaited to match the R0.3 executor-registry refactor (#11220). ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(search):** Allow REST and MCP web-fetch requests to select the advertised `anysearch-search` provider explicitly ([#11716](https://github.com/diegosouzapw/OmniRoute/pull/11716)) — thanks @pacocartones +- **fix(providers):** Keep overnight peak-hour protection active after midnight according to the window's configured start day ([#11718](https://github.com/diegosouzapw/OmniRoute/pull/11718)) — thanks @pacocartones +- **fix(providers):** correct all five g4f.space gateway descriptions to document proof-of-work credits or member API keys, variable limits, and the remote prompt boundary instead of promising free keyless access at five requests per minute ([#11738](https://github.com/diegosouzapw/OmniRoute/pull/11738)). +- **fix(db):** `model_capabilities` is created by a migration instead of lazily on the first models.dev sync, so a clean install and an upgraded install converge on the same schema regardless of which features have run ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(ci):** `check:install-upgrade` now fails on an `npm` install truncated by ENOSPC (npm reports it as a warning and still exits 0), authenticates its health probe so the version assertion works against the hardened health payload, frees the clean-install tree before the upgrade phase, and no longer reports a schema divergence computed from a boot that never served ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(routing):** harden session-affinity key derivation against oversized explicit IDs and empty structural-payload collisions, while recognizing Gemini and common text request shapes ([#11746](https://github.com/diegosouzapw/OmniRoute/pull/11746)) — thanks @MumuTW +- **fix(packages/browser-pool):** regenerate `package-lock.json` so the `packages/browser-pool` workspace's locked `playwright`/`@types/node` (and transitives) match its `package.json` specs, fixing cache-only/offline installs (`npm ci --offline`, Nix `buildNpmPackage`) that previously failed with `ENOTCACHED` ([#11747](https://github.com/diegosouzapw/OmniRoute/issues/11747)) — thanks @benjaminkitt ([#11784](https://github.com/diegosouzapw/OmniRoute/pull/11784)) — thanks @geekyNads +- **fix(assets):** Stop distributing 79 provider and CLI logos without verified source/license provenance, keep every provider/tool available through the internal generic icon, and prevent those IDs from falling through to an external icon CDN. ([#11750](https://github.com/diegosouzapw/OmniRoute/pull/11750)) +- **fix(providers):** retire the common `chatgpt-web` / `cgpt-web` provider because its pre-key and proof-of-work provenance could not be cleared ([#11754](https://github.com/diegosouzapw/OmniRoute/pull/11754)) — explicit chat and image requests, slash-prefixed models, persisted aliases, proxy overrides, and combo targets now fail closed with a sanitized HTTP `410` / `PROVIDER_RETIRED` before upstream dispatch. Migration 163 tombstones matching connections and invalidates their active leases without deleting history or widening API-key allowlists. The independent `chatgpt-web-codex` / `cgpt-codex` provider and its MIT-noticed vendored implementation remain available. +- **fix(usage):** quota and usage refresh no longer 409 when an exclusive lease reserves the connection ([#11758](https://github.com/diegosouzapw/OmniRoute/pull/11758)) — thanks @TheDemonTuan +- Keep the embedding registry's vector width and `embedding` type on models when a synced model exists for the same id, so `/v1/models` no longer reports registry-described embedding models widthless or untyped (#11761) — thanks @rahilmavani +- Correct `google/gemini-embedding-001` on the OpenRouter route to 3072 dimensions, the width it returns when `dimensions` is not sent (#11761) — thanks @rahilmavani +- **fix(vision-bridge):** Restrict automatic vision routing and fallbacks to models available in authoritative live provider catalogs, including aliases and effort variants ([#11767](https://github.com/diegosouzapw/OmniRoute/issues/11767)) — thanks @pacocartones ([#11813](https://github.com/diegosouzapw/OmniRoute/pull/11813)) +- **fix(providers):** reclassify Cerebras as a one-time $5 signup credit (payment method required, 30-day validity), not a recurring no-card 1M tokens/day trial ([#11773](https://github.com/diegosouzapw/OmniRoute/issues/11773)) ([#12591](https://github.com/diegosouzapw/OmniRoute/pull/12591)) — thanks @HouMinXi +- fix(cli): stop prepublish from re-rebuilding the already-built ESM-only opencode-plugin dist (#11787) ([#11990](https://github.com/diegosouzapw/OmniRoute/pull/11990)) — thanks @hartmark +- **fix(db):** Fresh installs no longer log a non-fatal `no such table: compression_run_telemetry` warning when retention cleanup runs before the lazily-created telemetry table exists ([#11802](https://github.com/diegosouzapw/OmniRoute/pull/11802)) — thanks @RaviTharuma +- fix(sse): set X-OmniRoute-Selected-Connection-Id on successful combo dispatches so downstream consumers stop falling back to an empty connection id (#11810) ([#11986](https://github.com/diegosouzapw/OmniRoute/pull/11986)) — thanks @hartmark +- **fix(services):** embedded CLIProxyAPI lifecycle checks now use public `/healthz`, while model discovery uses the configured dedicated data-plane API key instead of the management password ([#11811](https://github.com/diegosouzapw/OmniRoute/pull/11811)) — thanks @RaviTharuma +- Fix persisted model steps returning an error in the playground route simulator, and warn when structural steps cannot be simulated. ([#11862](https://github.com/diegosouzapw/OmniRoute/pull/11862)) — thanks @pacocartones +- **fix(providers):** Antigravity's dynamic mitmAlias table no longer routes `gemini-3.7-flash-{high,medium,low}` to a literal tier-suffixed upstream id just because one connected account's own discovery listed it directly — those display ids always resolve through the safe `gemini-3.7-flash-tiered` static alias, so one account's Google-provisioned access no longer 404s every sibling account of the provider ([#11824](https://github.com/diegosouzapw/OmniRoute/issues/11824), [#11651](https://github.com/diegosouzapw/OmniRoute/issues/11651)) ([#11988](https://github.com/diegosouzapw/OmniRoute/pull/11988)) — thanks @hartmark +- **fix(dashboard):** Expose Profile loading, errors, page structure, and XP progress to assistive technologies ([#11838](https://github.com/diegosouzapw/OmniRoute/pull/11838)) — thanks @pacocartones +- **fix(socks):** `Agent.connectTimeout` now bounds both SOCKS handshake and TLS connect, and `family === null` no longer falls back to `fetch-socks` ([#11842](https://github.com/diegosouzapw/OmniRoute/pull/11842)) — thanks @maxmad64bis +- **perf(compression):** OOM mitigations for large payload hashing, memoization, and token estimation ([#11844](https://github.com/diegosouzapw/OmniRoute/pull/11844) — thanks @AndrianBalanescu) +- Excluded Next.js Node File Trace manifests (`*.nft.json`) from the published npm tarball. They are build-time metadata and are never read while serving, but had grown to 668.7 MB — 61% of the package — which pushed the upload past the registry limit and made `npm publish` fail with `413 Payload Too Large`. ([#11864](https://github.com/diegosouzapw/OmniRoute/pull/11864)) +- **fix(cli):** keep the global OmniRoute server `--base-url` separate from provider-node `--endpoint` values in add, update, and validate commands ([#11860](https://github.com/diegosouzapw/OmniRoute/pull/11860)) — thanks @pacocartones +- **fix(config):** Nous Research's `Hermes-4-405B` model now displays as "Hermes 4 405B (Nous Research)" in both the provider registry and the free-model catalog, instead of the mislabelled "Hermes 4 7B" ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#11993](https://github.com/diegosouzapw/OmniRoute/pull/11993)) +- **fix(provider/nous):** inject required user= tag into Nous Research inference requests to resolve upstream 400 "missing tags" error ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#12044](https://github.com/diegosouzapw/OmniRoute/pull/12044)) +- **fix(build):** `npm run build` now fails in one second with a named package and a copy-pasteable fix when npm silently drops an externalised optional native dependency, instead of dying four minutes in with `Module not found: Can't resolve 'better-sqlite3'` ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- **fix(install):** `postinstall` no longer throws `ReferenceError: isAndroid is not defined` — failing the whole `npm install` — when the `better-sqlite3` rebuild fallback times out; the manual-fix guidance is reachable again ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- Fixed the Alibaba free-tier allowlist test that went red on its own once the shipped catalog's `validUntil` (2026-08-27) passed, leaving every PR and `main` with a failing `Unit Tests (1/8)`. The test now builds its own packs with dates it controls, and covers the expired-pack fallback that production has actually been serving. ([#11867](https://github.com/diegosouzapw/OmniRoute/pull/11867)) +- Split the npm registry upload into its own GitHub-hosted job. npm refuses `--provenance` from a self-hosted runner (`422 ... Only "github-hosted" runners are supported`), which blocked the v3.8.50 publish; the heavy verification cannot move to a hosted runner, so it now hands the proven tarball over instead. ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(ui):** the shared `DataTable` loading state no longer reads its decorative ⏳ glyph out to assistive technology, and now carries the same `role="status"` / `aria-live="polite"` / `aria-busy="true"` semantics as `PageLoading` ([#11869](https://github.com/diegosouzapw/OmniRoute/pull/11869)) — thanks @pacocartones +- **fix(api):** Bound the `timeout` accepted by `POST /api/providers/[id]/login` to the same 15s–600s window the Adobe Firefly and Conol browser-login paths already enforce, so a request can no longer pin a headful browser and the single active-login slot with an arbitrarily large poll budget. ([#11871](https://github.com/diegosouzapw/OmniRoute/pull/11871)) — thanks @pacocartones +- **fix(api):** Stop the tunnel and MITM routes from returning raw child-process error text, which disclosed host paths, binary install locations and Tailscale `tskey-*` credentials in a body some of these routes serve to non-loopback callers, and return a real 400 instead of a framework 500 when a tunnel request body fails validation. ([#11872](https://github.com/diegosouzapw/OmniRoute/pull/11872)) — thanks @pacocartones +- **fix(open-sse):** stream TTFT/ITL are sampled from a monotonic clock (`performance.now`) instead of `Date.now`, so an NTP correction or wall-clock jump can no longer inflate a genuine TTFT or produce a negative one that the `ttft >= 0` guard silently drops — protecting the router's OTel/EWMA/`usage_history`/speed-ranking signals ([#11873](https://github.com/diegosouzapw/OmniRoute/pull/11873)) — thanks @pacocartones +- **fix(providers):** xAI `reasoning_effort: "xhigh"` now reaches grok-4.6+ instead of being silently clamped to `"high"` ([#11879](https://github.com/diegosouzapw/OmniRoute/pull/11879)) — thanks @NoxzRCW +- **fix(dashboard):** the providers page no longer crashes into the error boundary when a provider id collides with an `Object.prototype` member (`constructor`, `__proto__`); icon lookups are own-property guarded ([#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)) — thanks @NoxzRCW +- **fix(skills):** injected skill tools declared in shorthand (`{"content": "string"}`) now forward valid JSON Schema, unblocking providers that validate tool schemas strictly such as Zhipu GLM on the Console Go tier ([#11881](https://github.com/diegosouzapw/OmniRoute/pull/11881)) — thanks @NoxzRCW +- **fix(api):** `POST /api/playground/simulate-route` now surfaces `combo-ref` and `provider-wildcard` persisted combo steps with a specific warning (naming the referenced combo, or the unresolved `provider/model` wildcard) instead of folding them into a generic "unsupported step" count; a `provider-wildcard` step is also now included as an unresolved target so the operator can see it is in the route (ported from [#11882](https://github.com/diegosouzapw/OmniRoute/pull/11882) — thanks @NoxzRCW). ([#11930](https://github.com/diegosouzapw/OmniRoute/pull/11930)) +- **fix(translator):** the streaming OpenAI→Claude translator keeps upstream usage, including prompt-cache tokens, when it arrives on a trailing `choices: []` chunk (Fireworks and any upstream using `stream_options.include_usage`) ([#11883](https://github.com/diegosouzapw/OmniRoute/pull/11883)) — thanks @NoxzRCW +- **fix(cli):** `omniroute update --apply` now tells you explicitly whether a running server was detected and, if so, that you must run `omniroute restart` to apply the update — it never restarted anything and previously implied the update was already live once files were installed. The dashboard's npm-mode Update flow (`/api/system/version`) now tries OmniRoute's own PID-file-managed supervisor before falling back to pm2, and reports an honest "restart required" step instead of a silent pm2-only "skipped" that read like a completed update. The server-side latest-version lookup backing the dashboard's update banner also gained `--prefer-online`, closing the same stale-npm-cache class already fixed in the CLI's own copy for #4376 ([#11885](https://github.com/diegosouzapw/OmniRoute/issues/11885)). ([#12005](https://github.com/diegosouzapw/OmniRoute/pull/12005)) +- **fix(resilience):** clear persisted LKGP pins when a target suffers connection/provider exhaustion or is skipped before dispatch due to cooldown/exhaustion/unavailability, preventing subsequent requests from repeatedly prioritizing known-dead providers ([#11911](https://github.com/diegosouzapw/OmniRoute/issues/11911)). ([#12013](https://github.com/diegosouzapw/OmniRoute/pull/12013)) — thanks @HouMinXi +- **fix(sse):** the OpenAI→Claude stream translator now defers the terminal `message_delta`/`message_stop` emission until the real usage block has arrived (or a genuine end-of-stream flush forces it) instead of emitting it immediately on `finish_reason` — previously, when the trailing usage-only chunk (`{"choices":[],"usage":{...}}`) arrived *after* the `finish_reason` chunk (the normal order for Fireworks/vLLM/Together and other `stream_options.include_usage` upstreams), the client-visible `message_delta` still carried stale/zero usage even though `state.usage` was internally corrected too late to matter (ported from [#11915](https://github.com/diegosouzapw/OmniRoute/pull/11915) — thanks @HouMinXi). ([#11933](https://github.com/diegosouzapw/OmniRoute/pull/11933)) +- **fix(sse):** `stripStore()` now forces `store=false` for stateless OpenAI-compatible Responses-API targets (unless the connection has explicitly opted in via `providerSpecificData.openaiStoreEnabled`), instead of only handling the `openai`/`agentrouter` cases — a client-supplied `store` value previously passed through untouched to backends that don't actually persist responses server-side ([#11916](https://github.com/diegosouzapw/OmniRoute/pull/11916) — thanks @HouMinXi). +- **fix(models):** custom provider-node models (synced, custom, and alias-backed) now appear under their configured prefix in the unified catalog when the operator's model-id prefix mode is canonical, instead of being dropped whenever alias-inclusion was otherwise disabled ([#11918](https://github.com/diegosouzapw/OmniRoute/pull/11918) — thanks @HouMinXi). +- **fix(models):** the unified model catalog now suppresses stale static registry models (including effort-tier variants) for any provider whose active connection has an authoritative live synced catalog, not only providers already using exclusive-synced-listing — a connection with `providerUsesAuthoritativeLiveCatalog` previously kept serving both the live-synced models and the stale static rows side by side ([#11919](https://github.com/diegosouzapw/OmniRoute/pull/11919) — thanks @HouMinXi). +- **fix(ui):** `ProviderIcon`'s three remaining unguarded lookups (`PROVIDER_ICON_ALIASES`, `LOCAL_SVG_ALIASES`, `THEMED_SVGS`) now use `Object.hasOwn()` own-property checks — a provider id such as `constructor` or `__proto__` previously resolved through the prototype chain instead of falling through to the unknown-provider CDN fallback (`getLobeProviderIcon()` itself was already guarded by [#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)); `ProviderPageHeader`'s `color` field is also now optional, matching the rest of the component's defensive typing (ported from [#11920](https://github.com/diegosouzapw/OmniRoute/pull/11920) — thanks @HouMinXi). ([#11935](https://github.com/diegosouzapw/OmniRoute/pull/11935)) — thanks @NoxzRCW +- fix(ollama): preserve multi-byte UTF-8 content split across stream chunks in the Ollama NDJSON transform, which previously corrupted CJK/emoji into U+FFFD (#11921) — thanks @pacocartones +- **fix(providers):** OrcaRouter chat requests now target `/v1/chat/completions` instead of the bare `/v1` API root, fixing the upstream `404 Invalid URL (POST /v1)` ([#11923](https://github.com/diegosouzapw/OmniRoute/pull/11923)). — thanks @echel0nn +- **fix(plugins):** deliver the `onStreamComplete` event to disk-installed plugins. The event shipped in v3.8.50 (#9669) was emitted internally but had no plugin-facing wiring, so no plugin could ever subscribe: the manifest schema silently dropped `hooks.onStreamComplete`, and the loader/manager only knew the seven legacy hooks. `onStreamComplete` is now a declarable manifest hook, wired through the loader and registered by the manager like the other hooks, and its payload carries a `requestId` so consumers can correlate the stream-completion event with the originating request ([#11934](https://github.com/diegosouzapw/OmniRoute/pull/11934)) — thanks @amaleta / @pacocartones +- **fix(db):** the Qdrant embedding-model dropdown now lists local/self-hosted providers (Ollama, LM Studio, vLLM, etc.) — an active connection is treated as "configured" when the provider allows an optional API key, not only when it has a real key or OAuth, so a running local embedding provider is no longer hidden from the picker ([#11949](https://github.com/diegosouzapw/OmniRoute/issues/11949)) ([#11995](https://github.com/diegosouzapw/OmniRoute/pull/11995)) +- **fix(providers):** Vertex AI Anthropic partner-model discovery now calls the Model Garden `v1beta1` publisher list (`/v1beta1/publishers/anthropic/models`, global) and parses its `publisherModels` envelope, so Claude models auto-synced from Vertex populate the active live catalog and route at request time instead of returning `Model '' is not available in the active live catalog` ([#11991](https://github.com/diegosouzapw/OmniRoute/issues/11991)) — thanks @fabioluissilva ([#11998](https://github.com/diegosouzapw/OmniRoute/pull/11998)) +- **fix(cli):** support `--base-url` alongside `--endpoint` in `omniroute nodes add`, `update`, and `validate` subcommands to prevent global `--base-url` shadowing issues ([#11999](https://github.com/diegosouzapw/OmniRoute/issues/11999)). ([#12033](https://github.com/diegosouzapw/OmniRoute/pull/12033)) — thanks @HouMinXi +- **fix(providers):** `cloudflare-ai` no longer refuses image content parts for every Workers AI model ([#12002](https://github.com/diegosouzapw/OmniRoute/pull/12002)) — the plain-string `content` requirement behind #2539 is carried by the _model_ schema, not by the `/ai/v1/chat/completions` endpoint (measured: an all-text part array returns 200 on `@cf/mistralai/mistral-small-3.1-24b-instruct`, `@cf/meta/llama-4-scout-17b-16e-instruct` and `@cf/meta/llama-3.3-70b-instruct-fp8-fast`, and 400 on the text-only `@cf/qwen/qwen2.5-coder-32b-instruct`). `transformRequest()` flattened every array and threw on the first non-text part (#6390), so image input was refused for vision-capable Cloudflare models that accept it. All-text arrays are still flattened — the one shape every model accepts — while an array carrying a non-text part is passed through untouched, so the attachment is still never silently dropped. Regression guards: `tests/unit/cloudflare-ai-image-parts-6390.test.ts`. — thanks @davidlinfr +- **fix(resilience):** decouple the limiter-managed execution backstop from the queue-wait budget — new `requestQueue.executionMaxWaitMs` (env `RATE_LIMIT_EXECUTION_MAX_WAIT_MS`, default 600000 = 10 min) now feeds Bottleneck's post-dispatch `expiration`, while `requestQueue.maxWaitMs` keeps its documented queue-wait semantics. Previously the queue-wait budget doubled as the execution expiration, so legitimate long-running calls on non-incremental gateways (whole generation buffered before the first upstream byte, e.g. Console Go / Command Code tiers serving GLM models) were killed mid-flight at the queue budget with a false 504 `RATE_LIMIT_EXECUTION_TIMEOUT` — the local limiter undercut the provider-aware upstream fetch-start timeouts. The surfaced 504 message now names `requestQueue.executionMaxWaitMs`; the error keeps the #4165 guarantees (disclaims an upstream timeout, preserves the Bottleneck error as `cause`, branded code + trusted provenance, classified request-scoped so combo falls back). A real queue-wait bound (the `Promise.race` around `limiter.schedule()` sketched in #9533) remains future work. (#12025) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **Call logs:** keep the `error` field when an artifact exceeds the storage cap, instead of replacing it with the omission marker. The error is the only field that says *why* a request failed and is typically ~90 bytes next to the multi-hundred-KB bodies that trip the cap, so dropping it left a size-limited row undiagnosable — a provider outage, a local timeout and an upstream 400 all rendered identically. It is now preserved at every fallback stage, truncated to 4KB if it is itself large ([#12026](https://github.com/diegosouzapw/OmniRoute/issues/12026)). ([#12095](https://github.com/diegosouzapw/OmniRoute/pull/12095)) — thanks @ntdat812 +- **fix(diagnostics):** preserve the error field (truncated to 4KB with a `[truncated: …]` suffix) in every call-log artifact size-limit fallback stage. Previously the minimal fallback replaced the error with `[omitted: call log artifact size limit exceeded]`, so an oversized artifact row showed nothing about WHY the request failed — e.g. 91 of 847 opencode-go 504 rows on one production instance were undiagnosable from the dashboard. Oversized request/response bodies are still omitted exactly as before; the error cap is independent of the payload sizes that tripped the fallback. (#12026) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **fix(sse):** OpenAI Responses clients that declare the native `web_search` tool now receive a spec-shaped `web_search_call` output item with `action.sources` alongside the preserved function-call round-trip, so search results executed through OmniRoute's own search backend are consumable by standard Responses clients (Codex, pi-web-access, …). ([#12031](https://github.com/diegosouzapw/OmniRoute/pull/12031)) — thanks @watchingdogs +- **fix(cli):** use in-thread alias resolver hooks on modern runtimes to avoid deprecation noise and improve Node.js forward compatibility ([#12073](https://github.com/diegosouzapw/OmniRoute/issues/12073)). ([#12083](https://github.com/diegosouzapw/OmniRoute/pull/12083)) — thanks @xiaoyaner0201 +- **fix(combo):** an operator-set **Agent Features → Context length** on a combo is now honored at request time. The value was persisted and advertised through `/v1/models`, but `resolveComboContextLimit()` never consulted it — so a multi-target combo whose members carry no per-model window fell through to the provider's generic `defaultContextLength` (openrouter 128000, command-code 200000) and rejected large requests with `Input exceeds context window … limit 128000` despite the combo being explicitly sized much larger. An identical single-target combo worked, because it collapses to its concrete target before the guard runs. Invalid values (0/negative/NaN/Infinity) are ignored, so the existing target → combo-min → fallback order is unchanged. ([#12090](https://github.com/diegosouzapw/OmniRoute/pull/12090)) — thanks @adivekar-utexas +- **fix(sse):** passthrough streams now estimate usage on finish when upstream closes without usage even with `stream_options.include_usage` — avoids `0 tokens / 0%` for providers that stay silent (and correctly handles trailing empty-choices usage) ([#12151](https://github.com/diegosouzapw/OmniRoute/pull/12151)) — thanks @maxmad64bis +- **fix(combos):** clearing an agent feature in the combos editor now persists — unchecking context cache protection, or emptying the system message or tool filter, sends an explicit `null` instead of dropping the field from the `PUT` body, which the update merge read as "leave unchanged" ([#12177](https://github.com/diegosouzapw/OmniRoute/pull/12177)) — thanks @foreveryh / @ntdatt812 +- **fix(memory):** self-hosted embedding endpoints now vectorize — the vector width is measured from the first embedding that comes back instead of being read from a registry that cannot describe them, so `vec_memories` is created and memories stop piling up unvectorized behind a green health check ([#12180](https://github.com/diegosouzapw/OmniRoute/pull/12180)) — thanks @kanade-hoshino / @ntdatt812 +- **fix(translator):** the leading `system` message now reaches Responses-API upstreams when its `content` is a content-part array — it was read as `typeof content === "string" ? content : ""`, so a prompt-caching client (Anthropic `cache_control`, the shape LiteLLM and the Anthropic SDK emit) had its entire system prompt replaced by an empty `instructions`. The request was still accepted with a normal `prompt_tokens` count, so the model answered with no instructions and nothing in the response said they were missing. Mid-conversation system turns already handled the array shape ([#7056](https://github.com/diegosouzapw/OmniRoute/pull/7056)); only the first one did not ([#12206](https://github.com/diegosouzapw/OmniRoute/issues/12206)). Regression guard: `tests/unit/translator-openai-responses-system-content-parts.test.ts`. ([#12207](https://github.com/diegosouzapw/OmniRoute/pull/12207)) — thanks @Gorillaz322 +- **fix(free-tier):** `/api/free-tier/summary` no longer computes its totals from a Radar feed built before the catalog the running release ships. When the cached feed is older — or carries no build date at all — the route answers from the shipped catalog, resolved through the operator's local model state so disabled and tombstoned models stay out of the numbers ([#12215](https://github.com/diegosouzapw/OmniRoute/pull/12215)). — thanks @maxmad64bis +- **fix(oauth):** Keep a Claude personal workspace and a Team organization as separate connections — they share the same email and `accountUUID`, so the email-only OAuth dedup let the second login overwrite the first account's tokens; `organizationUUID` now disambiguates them, the way `workspaceId` does for Codex ([#12222](https://github.com/diegosouzapw/OmniRoute/pull/12222)) — thanks @davidebaraldo +- **fix(resilience):** a 402 on a single paid model of a passthrough/gateway provider (e.g. `kilo-gateway`, `ollama-cloud`) no longer terminalizes the whole connection with a never-auto-recovered `credits_exhausted` status — only the paid model is locked out, so free models on the same key keep serving. 402 variant of [#3027](https://github.com/diegosouzapw/OmniRoute/issues/3027). Single-credential providers are unaffected — a 402 there is still treated as the key being genuinely out of credit ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239) / [#10616](https://github.com/diegosouzapw/OmniRoute/issues/10616)) ([#12242](https://github.com/diegosouzapw/OmniRoute/issues/12242)) — thanks @brick30llc-ctrl ([#12266](https://github.com/diegosouzapw/OmniRoute/pull/12266)) — thanks @geekyNads +- **fix(sse):** trust `finish_reason: "length"`/`"max_tokens"` over the reasoning-consumed-token ratio in response quality validation, so a reasoning model truncated below the old 90% threshold correctly fails and retries instead of returning empty content as a silent "success" ([#12262](https://github.com/diegosouzapw/OmniRoute/pull/12262)) — thanks @brick30llc-ctrl +- **fix(settings):** `PUT /api/settings/cache-config` now persists `alwaysPreserveClientCache` to the flat general settings the runtime cache-control policy actually reads; previously the value landed in the databaseSettings "cache" section and was silently ignored, so the endpoint had no effect on `cache_control` passthrough ([#12304](https://github.com/diegosouzapw/OmniRoute/pull/12304)) — thanks @davidebaraldo +- **fix(grok-cli):** treat omitted SuperGrokPro `creditUsagePercent` as 0% used so Provider Limits still renders a weekly bar (proto3 zero-elision) ([#12312](https://github.com/diegosouzapw/OmniRoute/pull/12312)) — thanks @HouMinXi +- **fix(combo):** Expose the two Auto-Combo scoring factors nobody could set — the combo validation schema and the dashboard weight sliders both declared 13 of the scorer's 15 factors, so `connectionDensity` (spreads load across a provider's connections) and `quality` were dropped on save and offered nowhere. The sliders also shipped their own default table that differed from the engine's on every non-zero factor and summed to 1.05, so the percentages shown next to them added up to 105%. Both lists now match `DEFAULT_WEIGHTS`, and a test keeps them there. Note that a combo whose stored `weights` omitted the two keys was effectively running with them at zero and the other thirteen renormalized upward; it now runs with the engine's intended distribution, so its routing does shift ([#12314](https://github.com/diegosouzapw/OmniRoute/pull/12314)) — thanks @maxmad64bis +- **fix(docs):** The free-tier reference no longer says its numbers come "confidence tagged per row" — no catalog entry carries a confidence tag and the API serves none, so every figure on that page is an estimate of the same, unstated quality. The page now states what an entry does vouch for: an independently documented hard stop (set by hand with the source in a comment, never defaulted to `true`) and a prompt-training disclosure, both with live counts the `check:docs-counts` gate keeps honest ([#12318](https://github.com/diegosouzapw/OmniRoute/pull/12318)) — thanks @maxmad64bis +- **fix(usage):** `adobe-firefly` and `firefly` have had a working usage fetcher since Adobe Firefly landed, but neither was ever added to the registration list, so the provider-plugin manifest, `genericQuotaFetcher` and the free-access quota cache all reported them as having no usage support — while `USAGE_SUPPORTED_PROVIDERS` said the opposite. Both are now declared, which also means their credit balance is fetched like any other declared provider's: `registerGenericQuotaFetchers` now registers a generic quota fetcher for them, and `resolveFreeAccessState` no longer returns early. A test holds the registration list to the dispatcher's switch in both directions, which is what the module's own docstring already asked for in prose ([#12321](https://github.com/diegosouzapw/OmniRoute/pull/12321)) — thanks @maxmad64bis +- **fix(quota):** drop the generic quota cache (agy / Antigravity / Claude OAuth) on an upstream 429 so reset-aware scoring does not keep a 60s stale snapshot, and force-refresh the next usage fetch so inner provider caches cannot recache the same window ([#12325](https://github.com/diegosouzapw/OmniRoute/pull/12325)) — thanks @HouMinXi +- **fix(combos):** deleting a combo now clears its persisted LKGP pins instead of leaving unreachable `key_value` rows behind ([#12326](https://github.com/diegosouzapw/OmniRoute/issues/12326)) ([#12425](https://github.com/diegosouzapw/OmniRoute/pull/12425)) — thanks @KrzysiekSko +- **fix(sse):** Keep ZWNJ (U+200C) and ZWJ (U+200D) in assistant text, reasoning and tool-call arguments — Persian/Kurdish half-space (`ارائه‌دهنده`), Arabic/Indic shaping and emoji sequences no longer lose them; the response de-obfuscation now removes joiners only between ASCII word characters, where the request side inserts them ([#12186](https://github.com/diegosouzapw/OmniRoute/issues/12186)) — thanks @rezjalibd ([#12359](https://github.com/diegosouzapw/OmniRoute/pull/12359)) — thanks @pacocartones +- **fix(resilience):** count resolved upstream 5xx results against the provider circuit breaker on the chat path — `CircuitBreaker.execute()` no longer reads a resolved `{ success: false, status: 5xx }` as a success that cancels the call-site failure, so a provider answering 503s now trips its breaker instead of staying `CLOSED` at `failureCount: 1`; single-model and combo dispatches are each accounted exactly once ([#12254](https://github.com/diegosouzapw/OmniRoute/issues/12254)) ([#12360](https://github.com/diegosouzapw/OmniRoute/pull/12360)) — thanks @pacocartones +- **fix(providers):** resolve the Codex quota auto-ping model from the live provider catalog and lifecycle registry instead of the retired `gpt-5.1-codex-mini`, and pause the ping with one actionable warning when no selectable Codex model exists rather than retrying a shut-down id every cooldown window ([#11905](https://github.com/diegosouzapw/OmniRoute/issues/11905)) ([#12361](https://github.com/diegosouzapw/OmniRoute/pull/12361)) — thanks @pacocartones +- **fix(api):** keep the `{created, data}` wrapper on combo-routed `/v1/images/generations` responses and default Codex image results to `b64_json` on both `/v1/images/generations` and `/v1/images/edits` so Codex CLI's built-in `image_gen` can decode them ([#12268](https://github.com/diegosouzapw/OmniRoute/issues/12268)) ([#12362](https://github.com/diegosouzapw/OmniRoute/pull/12362)) — thanks @pacocartones +- **fix(sse):** Name the shadowed custom provider node when a built-in provider id/alias (e.g. `openference` → `of`) reserves the prefix of an existing OpenAI/Anthropic-compatible node, so the runtime `No active credentials for provider: ` error explains that the prefix routed to the built-in and never reached the node's healthy connections, instead of contradicting the dashboard ([#11943](https://github.com/diegosouzapw/OmniRoute/issues/11943)) — thanks @morpheus9393 ([#12365](https://github.com/diegosouzapw/OmniRoute/pull/12365)) — thanks @pacocartones +- **fix(i18n):** wrap `ccOnboardingKeyPlaceholder` in ICU single quotes across all 43 locales so angle brackets render literally instead of being parsed as rich-text tags, which crashed the Claude Code onboarding block with `INVALID_MESSAGE: INVALID_TAG` ([#12302](https://github.com/diegosouzapw/OmniRoute/issues/12302)) ([#12369](https://github.com/diegosouzapw/OmniRoute/pull/12369)) — thanks @gonisulaimann +- **fix(models):** publish `effort_tiers` on Kimi K3's synced base-model entries (`kmca/k3`, `kmca/k3-256k`) so catalog-only clients (OpenCode, plain SDK pickers) can see and select the reasoning tiers (`low`/`high`/`max`) the synced metadata already carried — the `isSkippedEffortProvider` gate no longer suppresses tier visibility on those base entries, while synthetic `-` variant generation stays prevented and Codex/GLM base models remain excluded unchanged ([#12299](https://github.com/diegosouzapw/OmniRoute/issues/12299)) ([#12371](https://github.com/diegosouzapw/OmniRoute/pull/12371)) — thanks @gonisulaimann +- **fix(guardrails):** keep `auto`/`auto/*` virtual combos exempt from the Vision Bridge `fixedModel` credential guard so a combo target is passed through instead of silently falling back to global auto-selection ([#12237](https://github.com/diegosouzapw/OmniRoute/issues/12237)) ([#12373](https://github.com/diegosouzapw/OmniRoute/pull/12373)) — thanks @pacocartones +- **fix(combo):** capability-filter exhaustion caused by `max_tokens` above every target's known output limit now reports that reason (requested `max_tokens` vs the pool's highest known ceiling) instead of the unrelated "supports structured output" message ([#12229](https://github.com/diegosouzapw/OmniRoute/issues/12229)) — thanks @DW-MediaLab ([#12374](https://github.com/diegosouzapw/OmniRoute/pull/12374)) — thanks @pacocartones +- **fix(auth):** the `least-used` account strategy now prefers accounts without backoff before falling back to oldest `lastUsedAt`, the same tie-break `round-robin` already applies, so a failover no longer lands on a just-rate-limited account for a single request ([#12279](https://github.com/diegosouzapw/OmniRoute/issues/12279)) — thanks @tenshiak ([#12375](https://github.com/diegosouzapw/OmniRoute/pull/12375)) — thanks @pacocartones +- **fix(docker):** the `chatgpt-web-codex-browser` image now finds the Chrome binary under `chrome-linux64/` (Chrome for Testing layout in `playwright:v1.62.0-noble`) as well as the legacy `chrome-linux/`, so the container no longer crash-loops with `exec: --headless=new: not found` ([#12024](https://github.com/diegosouzapw/OmniRoute/issues/12024)) ([#12376](https://github.com/diegosouzapw/OmniRoute/pull/12376)) — thanks @pacocartones +- **fix(providers):** declare `groq/compound` and `allam-2-7b` as non-reasoning models in the curated Groq registry so `reasoning_effort` / `output_config.effort` / `thinking` from Claude Code are stripped instead of forwarded, which Groq rejected with HTTP 400 ([#12134](https://github.com/diegosouzapw/OmniRoute/issues/12134)) ([#12379](https://github.com/diegosouzapw/OmniRoute/pull/12379)) — thanks @pacocartones +- **fix(executors):** `OpencodeExecutor` no longer forces a direct connection when the connection has a proxy assigned in Proxy Management but no per-account proxies: the single-account fast path used to wrap the upstream dispatch in the direct-egress sentinel, discarding the ambient proxy context the chat handler had pinned from `proxy_assignments`, so API-key `opencode`/`opencode-go` connections egressed from the host IP (and hit geoblocks) despite the assignment. The direct pin is now applied only when no ambient proxy context exists ([#11894](https://github.com/diegosouzapw/OmniRoute/issues/11894) — thanks @hizzt) ([#12380](https://github.com/diegosouzapw/OmniRoute/pull/12380)) — thanks @pacocartones +- **fix(api):** `GET /v1/models` with `MODELS_CATALOG_PREFIX_MODE=canonical` (or `?prefix=canonical`) now lists providers whose registry alias is undefined or equal to their own id (Antigravity, Antigravity CLI and other self-aliased built-ins) — their single `provider/model` id was dropped by the alias/canonical duplicate guard in the static, synced, custom and alias-backed catalog loops ([#12058](https://github.com/diegosouzapw/OmniRoute/issues/12058)) — thanks @cheynetom ([#12381](https://github.com/diegosouzapw/OmniRoute/pull/12381)) — thanks @pacocartones +- **fix(translator):** Drop replayed `thinking` blocks that carry no signature (the shape produced from cross-provider `reasoning_content`) instead of stamping the default Claude signature on them, which Anthropic rejected with `400 Invalid signature in thinking block` on the next turn served by an Anthropic rung ([#12105](https://github.com/diegosouzapw/OmniRoute/issues/12105)) — thanks @atescivitci-cmd ([#12386](https://github.com/diegosouzapw/OmniRoute/pull/12386)) — thanks @pacocartones +- **fix(cli):** Resolve Bun's `--preload` polyfill path against the package root instead of `dist/`, so `omniroute` installed with `bun install -g` no longer crashes at startup with `error: preload not found …/dist/open-sse/utils/setupPolyfill.ts` ([#11980](https://github.com/diegosouzapw/OmniRoute/issues/11980)) — thanks @joglomedia ([#12387](https://github.com/diegosouzapw/OmniRoute/pull/12387)) — thanks @pacocartones +- **fix(providers):** `gemini-business` now publishes its model catalog — `/v1/models` and `/v1/providers/gemini-business/models` list the 12 enterprise Gemini ids the executor understands instead of returning an empty list (#12107) ([#12389](https://github.com/diegosouzapw/OmniRoute/pull/12389)) — thanks @pacocartones +- **fix(db):** install `busy_timeout` before the SQLite connection's first statement so a process opening the database while another one closes its WAL connection waits out the transient EXCLUSIVE lock instead of dying with `database is locked`, and recognise the drivers' real BUSY/PROTOCOL/IOERR errors as transient in the corruption probe so the same lock no longer renames the database away as corrupt; deflakes `cross-process contenders never both acquire the same connection` (#12394 — thanks @pacocartones) +- **fix(chat-admission):** derive the `chat_admission_busy` 503 `Retry-After` from observed heavyweight-lease occupancy — the larger of the exhausted `OMNIROUTE_CHAT_ADMISSION_QUEUE_MS` window and the time since capacity last turned over, capped at 60 s — instead of a fixed 1 s (structural) / 2 s (byte-stage) hint that invited Codex/agent fan-out clients to re-send ~1 MiB `/v1/responses` bodies every second into a gate held for the whole SSE lifetime; an idle gate keeps the historical floors ([#12135](https://github.com/diegosouzapw/OmniRoute/issues/12135)) (#12395 — thanks @pacocartones) +- **fix(api-manager):** the API key permissions modal no longer silently drops `allowedCombos` entries its Combo picker cannot render — routing-rule names such as `rt-*`, which the backend already honours — when "All" is clicked and the key is switched back to "Restrict"; those entries now survive the toggle, are listed read-only under the combo list so the count and the list agree, and are saved back verbatim instead of persisting `[]` (deny-all) (#12397 — thanks @pacocartones) +- **fix(catalog):** write the NUL separator of the catalog connection memo key, the provider serviceKind memo key, the Video Bridge promotion group key and a JSON-exactness test fixture as the `\u0000` escape instead of a raw byte — same runtime value, but the raw byte made git, GitHub and ripgrep treat those files as binary (hidden PR diffs, silently skipped searches); a guard test now keeps raw NUL bytes out of `src/`, `open-sse/` and `tests/` (#12403 — thanks @pacocartones) +- **fix(providers):** add `CLAUDE_CODE_CLIENT_VERSION` and `GITHUB_COPILOT_CLI_VERSION` env overrides so Anthropic/Copilot client-version gates can be unblocked without a rebuild ([#12417](https://github.com/diegosouzapw/OmniRoute/issues/12417)) ([#12632](https://github.com/diegosouzapw/OmniRoute/pull/12632)) — thanks @HouMinXi +- **fix(db):** back-fill `last_ping_at` and `last_pinged_reset_key` on `provider_connections` during schema reconciliation so divergent lineages that skipped `123_quota_auto_ping` still accept quota auto-ping writes ([#12470](https://github.com/diegosouzapw/OmniRoute/pull/12470) — thanks @KooshaPari) +- **fix(ci):** document MIT exceptions for `@eloqnt/{config,format-json,format-po}` (next-intl transitive; locked tarballs omit `license`) and keep the A2A lifecycle vitest off the real SQLite persistence seam ([#12581](https://github.com/diegosouzapw/OmniRoute/issues/12581)) ([#12595](https://github.com/diegosouzapw/OmniRoute/pull/12595)) +- OpenCode plugin `/v1/models` catalog fetch now waits 30s by default and attaches HTTP `statusCode` on 401/5xx so host fallback plugins can hop instead of seeing an untyped AbortError/UnknownError. ([#12607](https://github.com/diegosouzapw/OmniRoute/pull/12607)) — thanks @RaviTharuma +- **CI:** the OpenAPI security-tier gate now mirrors `isAlwaysProtectedPath()` in full — it also reads `ALWAYS_PROTECTED_API_PATTERNS`, so the pattern-gated credential routes (`/api/providers/{id}/{claude,codex}-auth/{export,apply-local}`, GHSA-5926-2w35-7h4q) no longer report as unannotated. (#12605) +- **fix(api):** GET /v1/models no longer waits forever on a hung coalesced catalog rebuild; cold-path waits are bounded (`CATALOG_BUILD_TIMEOUT_MS`, default 8s) and a last-good 200 is served when the rebuild times out ([#12627](https://github.com/diegosouzapw/OmniRoute/issues/12627)). ([#12628](https://github.com/diegosouzapw/OmniRoute/pull/12628)) — thanks @RaviTharuma +- **fix(cursor):** a non-array `tool_calls` on an assistant message no longer crashes the cursor request translator with a `TypeError`; both loops now require an array ([#12689](https://github.com/diegosouzapw/OmniRoute/issues/12689)) ([#12691](https://github.com/diegosouzapw/OmniRoute/pull/12691)) — thanks @soroush5 +- **fix(chat):** Chat Completions no longer return empty `content` after a server-owned memory or skills tool; the first provider send and account/model recovery now share one pipeline so a follow-up round-trip can fill the reply ([#12696](https://github.com/diegosouzapw/OmniRoute/issues/12696)) — thanks @HouMinXi ([#12867](https://github.com/diegosouzapw/OmniRoute/pull/12867)) +- **fix(dashboard):** batch-deleting provider keys no longer toasts failure after a successful delete when the confirm button's click event is forwarded as `onAfter` ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) — thanks @HouMinXi +- **fix(glm):** drop the extra 16th argument to `createSSETransformStreamWithLogger` that TypeScript rejected (TS2554) and that never reached the TransformStream ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) ([#12770](https://github.com/diegosouzapw/OmniRoute/pull/12770)) — thanks @HouMinXi +- **fix(dashboard):** pass `nodeMap` into Runtime `QuotaGroup` so a quota monitor in error/exhausted/alerting no longer throws `ReferenceError: nodeMap is not defined`. ([#12868](https://github.com/diegosouzapw/OmniRoute/pull/12868)) — thanks @HouMinXi +- Restricted API keys whose `allowedModels` lists a combo name no longer skip every combo member at pre-dispatch (`ALL_TARGETS_SKIPPED`). Inner-target filtering still applies when the allow-list is a provider prefix or `disableNonPublicModels` is on ([#12899](https://github.com/diegosouzapw/OmniRoute/pull/12899)). — thanks @HouMinXi +- **fix(combos):** Effort-suffixed combo members inherit the base model's `model_context_overrides` row so priority order is not inverted on large requests ([#12926](https://github.com/diegosouzapw/OmniRoute/pull/12926)) — thanks @HouMinXi +- **fix(catalog):** Picker-added `customModels` enter the dispatch-time live catalog so combo members and bare inference no longer 400 ([#12934](https://github.com/diegosouzapw/OmniRoute/pull/12934)) — thanks @HouMinXi +- **fix(docker):** re-derive the Docker build's worker-pool memory budget from the MEASURED ~4.5 GB per-process RSS (the issue owner's own VPS dmesg OOM-killer reproduction) instead of the stale 2560 MB/worker inference #11419 shipped, and lower `OMNIROUTE_BUILD_WORKERS` 3 → 2 so 1 parent + 1 page-data worker (2 processes × 4.5 GB = 9 GB) fits the 12.288 GB (75%) budget on a 16 GB GitHub Actions runner — the previous default (1 parent + 2 workers = 13.5 GB) still overcommitted the runner and kept "Publish to Docker Hub" failing with `cannot allocate memory` after #11419 merged (#7518). ([#11663](https://github.com/diegosouzapw/OmniRoute/pull/11663)) +- **fix(electron):** the packaged Windows desktop build now passes the #7592 cold-restart smoke end to end. Five defects were found and fixed on the way: (1) optional-pack staging failed on any machine whose `tar` is GNU tar — it reads the drive letter in an absolute `-f C:\...` archive path as a remote rsh host (`Cannot connect to C:`), so staging now passes a bare filename with `cwd` at the tarball directory and surfaces tar stderr on failure; (2) the Electron `files` allowlist was missing `lib/loginHeaderCapture.js`, so the packaged main process crashed at startup with `Cannot find module './lib/loginHeaderCapture'`; (3) electron-builder ≥26 injects an `!**/node_modules/**` ignore into every extraResources pattern list that no positive filter can override, which silently dropped the staged runtime `node_modules` (including the better-sqlite3 N-API prebuild) from `resources/app` — a fresh v3.8.50 desktop build would have shipped with no native SQLite driver and reproduced the #7592 sql.js fallback on every machine; an `afterPack` hook now restores the staged `node_modules` after packing; (4) the packaged-app smoke harness redirected `USERPROFILE` into its temp DATA_DIR, but Electron resolves the Roaming profile from `%USERPROFILE%\AppData\Roaming\` (USERPROFILE takes precedence over the APPDATA env var) and the path service throws instead of creating it, so `requestSingleInstanceLock()` returned false and the app exited(0) silently before `app.whenReady()` — the harness now pre-creates the derived tree, and `ensureSmokeEnvDirs` is exported and unit-tested; (5) the #7592 driver assertion parsed a `[DB] Driver: ...` line that the server's primary DB path never printed (only the unused `openDatabaseAsync()` did) — `getDbInstance()` now emits the same line on open so the guard can actually assert the native driver. Also: the smoke env-allowlist unit test hardcoded POSIX paths and could only pass on Linux/macOS; expectations are now host-agnostic, with new regression tests for the USERPROFILE-derived tree and `tarPack` under absolute Windows-style paths ([#11443](https://github.com/diegosouzapw/OmniRoute/pull/11443)) — thanks @jonlwheat2-gif +- fix(sse): stop the auto-combo candidates inspector from silently dropping model-locked/cooled-down rows (#9133) ([#11994](https://github.com/diegosouzapw/OmniRoute/pull/11994)) — thanks @hartmark +- **fix(providers):** Perplexity Web no longer turns upstream stream failures into successful assistant text; pre-content failures remain eligible for fallback, partial output ends with a structured sanitized error, and failed sessions are not persisted ([#12465](https://github.com/diegosouzapw/OmniRoute/pull/12465)) +- **fix(video-bridge):** preserve chronological video and transcript fusion, source-frame timestamps, transcript provenance, full contact-sheet intervals, and pre-abort callback isolation ([#11681](https://github.com/diegosouzapw/OmniRoute/pull/11681)) +- **Z.ai Web:** HTTP 200 streams carrying an upstream error now terminate with a structured failure instead of assistant text plus a normal stop, preserving partial output while allowing pre-content combo fallback. ([#12454](https://github.com/diegosouzapw/OmniRoute/pull/12454)) +- Absorb `Error [AbortError]: request_signal_aborted` and DOMException AbortError shapes in the process-level client-abort crash guard so routine client disconnects no longer kill the server (exit code 7). ([#12165](https://github.com/diegosouzapw/OmniRoute/pull/12165)) — thanks @alvinveroy +- **fix(sse):** Treat Adapta Web `type:error` SSE events as sanitized non-stream failures instead of empty HTTP 200 completions. ([#12459](https://github.com/diegosouzapw/OmniRoute/pull/12459)) +- **fix(autoCombo):** layer 3 (`models_dev_tier`) no longer scores retired models as premium, merge cross-provider capability rows deterministically, and inherit tier scores through variant ids ([#11508](https://github.com/diegosouzapw/OmniRoute/issues/11508)). `getModelsDevTierFitness` now vetoes ids present in `config/quality/model-lifecycle.json` with status `retired` before any other signal; `loadModelCapabilities()` aggregates per `model_id` (any-non-null-true for booleans, max for `limit_context`) instead of last-write-wins over SQLite's undefined row order; a capabilities miss retries through `resolveScoresAs()`'s base id and reports `models_dev_tier:inherited`, matching layers 1–2 (#11492). Adds `getModelsDevTierFitnessWithSource()` and an `invalidateCapabilitiesCache()` test/ops hook. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) — thanks @Meet6338-X +- fix(sse): accept Claude `content:[]` + `stop_reason: max_tokens`/`tool_use` in `detectMalformedNonStream` (match `isEmptyContentResponse`) so Claude Code `/model` probes with `max_tokens: 1` no longer become a false 502 ([#11568](https://github.com/diegosouzapw/OmniRoute/pull/11568)) — thanks @Seramicx +- **fix(security):** sanitize `request.failed` diagnostics before publishing them to live dashboard listeners and replay history, while keeping status, model, provider, latency, and internal call-log diagnostics intact. ([#12469](https://github.com/diegosouzapw/OmniRoute/pull/12469)) +- **fix(pwa):** do not intercept dashboard navigations so Chrome can fall back from a stale HTTP/3 Alt-Svc advertisement (UDP :20128 is unpublished; F5 on a long-lived tab hung until a new tab opened a fresh TCP connection). ([#12767](https://github.com/diegosouzapw/OmniRoute/pull/12767)) — thanks @HouMinXi +- **fix(executors):** handle DuckDuckGo ERR_BN_LIMIT (418) without retrying — when the upstream returns `418 ERR_BN_LIMIT` (rate-limit/ban), the executor now returns the error immediately instead of burning another VQD acquisition that would only count against the IP limit. The retry logic for `418 ERR_CHALLENGE` (unsolved challenge) remains unchanged. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) ([#12110](https://github.com/diegosouzapw/OmniRoute/pull/12110)) — thanks @oyi77 / @Meet6338-X +- Fixed DuckDuckGo streaming responses losing JSON lines and UTF-8 characters split across network chunks. ([#11528](https://github.com/diegosouzapw/OmniRoute/pull/11528)) — thanks @pacocartones +- Harden generated Cloudflare, Vercel, and Deno relays by stripping hop-by-hop, framing, and proxy authentication headers before forwarding requests upstream. ([#11533](https://github.com/diegosouzapw/OmniRoute/pull/11533)) — thanks @pacocartones +- **feat(grok-cli):** Provider Limits shows grok-cli banked reset credits from `GetRemainingResets` (including a real zero; a failed RPC omits the row) and the existing View credits button now calls `ConsumerUiSvc/RedeemReset` for grok-cli. Live tokens use nested fields 10/20/30 (id + Timestamp), not compact 1/2/3. ([#12805](https://github.com/diegosouzapw/OmniRoute/pull/12805)) — thanks @HouMinXi +- **fix(memory):** Embedding Model Quick select, Embedding Source remote dropdown, and Rerank selector now list every configured provider with embedding/rerank support instead of only chat-catalog text matches plus OpenRouter live discovery; a generic OpenAI-compatible `/embeddings` + Cohere-compatible `/rerank` runtime fallback resolves any configured chat provider's embedding/rerank endpoint, so unlisted providers no longer fail with "Unknown embedding provider"; both memory selectors gained a free-text model override ([#11390](https://github.com/diegosouzapw/OmniRoute/pull/11390)) — thanks @rqzbeh +- Harden SQLite upgrades around the historical migration-074 version collision: missing discovery and inspector tables are replayed atomically, pre-existing databases (including setup-created skeletons) receive reusable content-addressed safety snapshots, and Node test/eval probes without `DATA_DIR` are isolated from the operator database. ([#12435](https://github.com/diegosouzapw/OmniRoute/pull/12435)) +- **fix(dashboard):** Moonshot/Kimi Open Platform voucher and cash leftover percentages follow the bucket balance, so an empty wallet no longer paints those rows as 100% while Available is 0% ([#12733](https://github.com/diegosouzapw/OmniRoute/pull/12733)) — thanks @HouMinXi +- **fix(combo):** return non-retryable HTTP 400 when all candidates for a pinned native Codex turn are unavailable due to model-scoped lockout, terminating the turn cleanly while preserving turn continuity and enabling standard Combo routing on subsequent turns ([#12240](https://github.com/diegosouzapw/OmniRoute/pull/12240)) — thanks @mdigitalbh81 +- Fixed auto-router strategies preserving the selected connection through ranking and dispatch when multiple connections share the same provider and model. ([#11530](https://github.com/diegosouzapw/OmniRoute/pull/11530)) — thanks @pacocartones +- Preserve Chat Completions JSON-object response formats when translating requests to the Responses API. ([#11529](https://github.com/diegosouzapw/OmniRoute/pull/11529)) — thanks @pacocartones +- Fix idempotency fingerprints so Responses API requests include `input` and other semantic generation fields using stable serialization, while excluding credentials and request metadata. ([#11532](https://github.com/diegosouzapw/OmniRoute/pull/11532)) — thanks @pacocartones +- **fix(api):** Generated API CLI commands now enforce required OpenAPI request bodies; Combo test commands forward the required `comboName` body, while API keys created by older writers after migration 149 preserve legacy allow-all Combo access without widening explicit empty allowlists — thanks @marcelokarval ([#12070](https://github.com/diegosouzapw/OmniRoute/pull/12070)) +- **fix(grok-web):** treat upstream streaming failures as failures instead of successful assistant text: error-only streams now fail readiness with HTTP 502, while failures after legitimate content preserve that partial output and terminate through the sanitized stream failure path without a normal `stop` completion. ([#12458](https://github.com/diegosouzapw/OmniRoute/pull/12458)) +- HuggingChat now turns HTTP 200 JSONL generation failures into a sanitized 502 before content, or a fixed public stream failure after partial output, so fallback and request persistence no longer record a false successful stop. ([#12456](https://github.com/diegosouzapw/OmniRoute/pull/12456)) +- **fix(providers):** keep 1min.ai HTTP 200 stream errors out of assistant content, preserve partial output, and expose sanitized terminal errors so pre-content failures can fall back. ([#12466](https://github.com/diegosouzapw/OmniRoute/pull/12466)) +- **fix(sessions):** preserve legacy Sessions data when the additive exclusive-session projection is unavailable, returning an empty projection and warning only once per contiguous outage instead of failing the endpoint; the Sessions badge intentionally reflects the merged legacy and exclusive row count introduced by [#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389) ([#11469](https://github.com/diegosouzapw/OmniRoute/pull/11469)) — thanks @KaspaPulse +- Render Nimble Search with the generic provider icon and serve Opper's proven logo locally. ([#12415](https://github.com/diegosouzapw/OmniRoute/pull/12415)) +- **fix(quality):** `config/quality/provider-assets-provenance.jsonl` was stale against the physical `public/providers/` tree (still listing 225 assets and 5 duplicate-content groups after several provider-retirement PRs removed 85 icon files), which would have made `check:provider-asset-provenance` fail on the next full run. Reconciled the manifest to the real 142-file snapshot (142 asset records, 1 duplicate-content group, 2 newly-registered files marked `unresolved` pending provenance research), repinned `auditedCommit`, and updated the regression test's hardcoded counts to match. ([#11876](https://github.com/diegosouzapw/OmniRoute/pull/11876)) +- fix(ui): wrap quota card footer action buttons to prevent clipping Refresh button on narrow cards — when extra action buttons such as `View credits` are present on Codex cards, allow the footer row to flex-wrap with `shrink-0` buttons so `Refresh now` remains visible and clickable across all responsive card widths ([#11464](https://github.com/diegosouzapw/OmniRoute/pull/11464)) — thanks @adevwithpurpose +- Keep Antigravity Gemini usable when the same connection's Claude weekly quota is empty; generic quota cache stays per-connection for every other provider. ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) — thanks @HouMinXi +- Fixed Tailscale tunnel setup to reject ports outside the valid 1-65535 range and safely install Windows MSI packages from paths containing apostrophes. ([#11534](https://github.com/diegosouzapw/OmniRoute/pull/11534)) — thanks @pacocartones +- Classify Cloudflared and Tailscale process-management routes as local-only and spawn-capable while preserving remote access to read-only tunnel status endpoints. ([#11531](https://github.com/diegosouzapw/OmniRoute/pull/11531)) — thanks @pacocartones +- Electron release workflow: the `publish-npm` job now grants `actions: read` to the reusable `npm-publish.yml` it calls (its `publish` job requests it), which is what made GitHub refuse the whole v3.8.50 run at startup and ship the release with zero desktop assets; a `workflow_dispatch` now builds the requested tag instead of the dispatching branch and can skip the npm leg (`publish_npm=false`) when only re-attaching assets ([#11974](https://github.com/diegosouzapw/OmniRoute/pull/11974)) +- **fix(dashboard):** The Combos page usage guide now reads its dismissal through `useSyncExternalStore` instead of correcting SSR state inside an effect, removing an extra commit of the page tree on every load (and the `react-hooks/set-state-in-effect` error it raised). ([#12671](https://github.com/diegosouzapw/OmniRoute/pull/12671)) +- Electron release: `electron/package-lock.json` regained the optional `electron-builder-squirrel-windows` subtree (13 entries) that `npm ci` had been refusing as out of sync, `electron-release.yml` gained a `build_ref` dispatch input and stops regenerating release notes on a re-attach dispatch, and the npm publish workflow attaches the SBOM to the GitHub Release on dispatch publishes too — so the v3.8.51 tag ships every desktop asset and the SBOM like v3.8.49 did ([#12022](https://github.com/diegosouzapw/OmniRoute/pull/12022)) +- Drained the reds every PR against `release/v3.8.51` was born with: documented the three service-worker build-id variables, registered the six retirement/tunnel tests with the mutation gate, approved `eslint-plugin-react-hooks` in the dependency allowlist, added the six `combo.sort.*` strings to `vi` and `pt-BR`, pointed the ChatGPT Web doc at the real migration-168 test, worded the g4f hint around the member key, and realigned four tests to the retired-provider catalog and the legacy-schema fixtures the retirement migrations touch. ([#11940](https://github.com/diegosouzapw/OmniRoute/pull/11940)) +- **fix(providers):** Claude, Grok, LMArena, Notion, and Perplexity web-cookie transports now use pooled `wreq-js` 3.2 instead of the native sidecar, with all nine supported bindings pinned and audited, and the applicable platform binding plus native-license evidence included in each release artifact ([#12429](https://github.com/diegosouzapw/OmniRoute/pull/12429), supersedes [#11753](https://github.com/diegosouzapw/OmniRoute/pull/11753)). +- **fix(providers):** Zed Hosted streaming failures now trigger fallback before content and end partial streams with a sanitized structured error instead of fake assistant text and a normal-success stop. ([#12455](https://github.com/diegosouzapw/OmniRoute/pull/12455)) +- **fix(test):** capture hook result after render (direct commit `b85d0abb0b`) +- **fix(sync):** repair the two regressions the v3.8.50 sync-back left on release/v3.8.51 (direct commit `5ade9e0851`) +- **revert(models):** drop the memo reset hook ported from #10055 (direct commit `282de841ae`) +- **fix(typecheck):** guard the optional connectionId before the reactive model sync (direct commit `f2825bad75`) +- **fix(quota-share):** release the winner's reserved in-flight slot (#11371) ([#11408](https://github.com/diegosouzapw/OmniRoute/pull/11408)) — thanks @oyi77 +- **fix(providers):** stop silently dropping reasoning effort on opencode families (#10788) ([#11409](https://github.com/diegosouzapw/OmniRoute/pull/11409)) — thanks @oyi77 +- **fix(combo):** enable genuine quota-aware routing for generic providers (antigravity, claude, etc.) ([#11411](https://github.com/diegosouzapw/OmniRoute/pull/11411)) — thanks @HouMinXi +- **fix(oauth):** allow expired token retry before deactivation and activate expired retry loop (#P0) ([#11414](https://github.com/diegosouzapw/OmniRoute/pull/11414)) — thanks @HouMinXi +- **perf(executors):** lazy-load the executor registry — defer class imports + construction to first use (#11220) ([#11421](https://github.com/diegosouzapw/OmniRoute/pull/11421)) — thanks @oyi77 +- **fix:** make release/v3.8.50 CI gates green (run 32786966560 root causes) ([#11450](https://github.com/diegosouzapw/OmniRoute/pull/11450)) — thanks @jonlwheat2-gif +- **fix(sse):** exact-domain cookie match and origin-equality URL assertions ([#11452](https://github.com/diegosouzapw/OmniRoute/pull/11452)) — thanks @hartmark +- **fix(providers):** send Responses API payload during OpenAI-compatible validation ([#11454](https://github.com/diegosouzapw/OmniRoute/pull/11454)) — thanks @tuandinh0801 +- **fix(command-code):** fallback to /alpha/generate for Go plan without Provider API access ([#11455](https://github.com/diegosouzapw/OmniRoute/pull/11455)) — thanks @Chewji9875 +- **fix(responses-continuation):** fail closed on a log-truncated stored input/output array ([#11473](https://github.com/diegosouzapw/OmniRoute/pull/11473)) — thanks @hartmark +- **fix(cli):** write jcode settings as [providers.omniroute] in config.toml ([#11484](https://github.com/diegosouzapw/OmniRoute/pull/11484)) — thanks @hongnoul +- **fix(logging):** raise chat-log array truncation cap to 1000 and unify duplicate implementations ([#11499](https://github.com/diegosouzapw/OmniRoute/pull/11499)) — thanks @hartmark +- **fix:** restore live websocket URL helpers ([#11509](https://github.com/diegosouzapw/OmniRoute/pull/11509)) — thanks @PauloFH +- **perf(core):** eliminate async waterfalls across home, cache, and db operations (#11396) ([#11512](https://github.com/diegosouzapw/OmniRoute/pull/11512)) — thanks @HouMinXi +- **fix(kiro):** add runtime.us-east-1.kiro.dev as first-attempt endpoint ([#11517](https://github.com/diegosouzapw/OmniRoute/pull/11517)) — thanks @rifqiawl +- **fix(providers):** remove duplicate 128-tool truncation in OpencodeExecutor (#11444) ([#11518](https://github.com/diegosouzapw/OmniRoute/pull/11518)) — thanks @HouMinXi +- **fix(providers):** poll onboardUser LRO before classifying BYOP ([#11519](https://github.com/diegosouzapw/OmniRoute/pull/11519)) — thanks @rifqiawl +- **fix(translator):** preserve exact tool name casing for Claude tool_use payloads (#11487) ([#11520](https://github.com/diegosouzapw/OmniRoute/pull/11520)) — thanks @HouMinXi +- **fix(kimi-web):** align health probe with executor domain ([#11521](https://github.com/diegosouzapw/OmniRoute/pull/11521)) — thanks @pacocartones +- **fix(cli):** preserve existing admin password during setup ([#11522](https://github.com/diegosouzapw/OmniRoute/pull/11522)) — thanks @pacocartones +- **fix(mitm):** keep Windows DNS elevation off the POSIX sudo path (#11430) ([#11542](https://github.com/diegosouzapw/OmniRoute/pull/11542)) — thanks @official-burak +- **fix(sse):** scale chat admission by ingest byte budget instead of a fixed request count ([#11548](https://github.com/diegosouzapw/OmniRoute/pull/11548)) — thanks @TheDemonTuan +- **fix:** accept Gemini Web cookie exports ([#11554](https://github.com/diegosouzapw/OmniRoute/pull/11554)) — thanks @rafacpti23 +- **fix(i18n):** complete Brazilian Portuguese translations ([#11558](https://github.com/diegosouzapw/OmniRoute/pull/11558)) — thanks @rafacpti23 +- **fix(sse):** explicit types for openai-responses pureHelpers — clears last failing core typecheck gate ([#11567](https://github.com/diegosouzapw/OmniRoute/pull/11567)) — thanks @jonlwheat2-gif +- **fix(opencode):** hydrate Proxy Pool references for no-auth connections ([#11584](https://github.com/diegosouzapw/OmniRoute/pull/11584)) — thanks @AStupidBear +- **fix(ci):** resolve inherited release/v3.8.51 base-red CI failures ([#11608](https://github.com/diegosouzapw/OmniRoute/pull/11608)) — thanks @jonlwheat2-gif +- **fix(db):** fallback load STORAGE_ENCRYPTION_KEY from env files ([#11614](https://github.com/diegosouzapw/OmniRoute/pull/11614)) — thanks @benzntech +- **fix(providers):** mark Brave Search as no longer having a free tier ([#11621](https://github.com/diegosouzapw/OmniRoute/pull/11621)) — thanks @turbolego +- **fix:** pass tool result content through as string in OpenAI/Claude -> Gemini translation ([#11624](https://github.com/diegosouzapw/OmniRoute/pull/11624)) — thanks @Hsia97 +- **fix(sse):** scope credential health to exact key ([#11628](https://github.com/diegosouzapw/OmniRoute/pull/11628)) — thanks @KaspaPulse +- **fix(install):** guard prepare script against missing husky ([#11634](https://github.com/diegosouzapw/OmniRoute/pull/11634)) — thanks @Prajeeth-12 +- **fix(security):** redact NVIDIA_API_KEY at the log sink in the ad-hoc diag script ([#11639](https://github.com/diegosouzapw/OmniRoute/pull/11639)) +- **fix(types):** clear the two type regressions red on release/v3.8.51 ([#11640](https://github.com/diegosouzapw/OmniRoute/pull/11640)) — thanks @jonlwheat2-gif +- **fix(build):** resolve workspace: protocol dependencies to concrete semver versions during prepublish (#11540) ([#11641](https://github.com/diegosouzapw/OmniRoute/pull/11641)) — thanks @HouMinXi +- **fix(search):** properly resolve configured search connection in /v1/responses pipeline (#11524) ([#11642](https://github.com/diegosouzapw/OmniRoute/pull/11642)) — thanks @HouMinXi +- **fix(authz):** trust cf-connecting-ip for via-proxy detection when peer is Cloudflare edge (#11514) ([#11643](https://github.com/diegosouzapw/OmniRoute/pull/11643)) — thanks @HouMinXi +- **fix(security):** compare the OIDC state cookie in constant time ([#11646](https://github.com/diegosouzapw/OmniRoute/pull/11646)) +- **fix(security):** namespace the dedup hash by the calling API key ([#11649](https://github.com/diegosouzapw/OmniRoute/pull/11649)) +- **fix(models):** gate the Codex-native catalog loop on prefix mode (#11632) ([#11662](https://github.com/diegosouzapw/OmniRoute/pull/11662)) — thanks @xiaoyaner0201 +- **fix(dashboard):** project Codex routing quota in account pools ([#11666](https://github.com/diegosouzapw/OmniRoute/pull/11666)) — thanks @KaspaPulse +- **fix(ddgw):** normaliza system no upstream da release v3.8.51 ([#11692](https://github.com/diegosouzapw/OmniRoute/pull/11692)) — thanks @RhianB14 +- **fix(memory):** honor category filter in GET /api/memory ([#11699](https://github.com/diegosouzapw/OmniRoute/pull/11699)) — thanks @Sabeekhann +- **fix(ci):** detect stale workspace lockfile entries ([#11714](https://github.com/diegosouzapw/OmniRoute/pull/11714)) — thanks @pacocartones +- **fix(ci):** stamp dist/BUILD_SHA before the npm publish provenance gate ([#11721](https://github.com/diegosouzapw/OmniRoute/pull/11721)) +- **fix(docker):** decouple the best-effort Bun image from the release manifest ([#11724](https://github.com/diegosouzapw/OmniRoute/pull/11724)) +- **perf(compression):** avoid cloning memo misses twice ([#11727](https://github.com/diegosouzapw/OmniRoute/pull/11727)) — thanks @RaviTharuma +- **fix(docker):** let the bun digest artifact be absent, not fatal ([#11740](https://github.com/diegosouzapw/OmniRoute/pull/11740)) +- **fix(shared):** block cloud-metadata hosts under default remote-image guard ([#11755](https://github.com/diegosouzapw/OmniRoute/pull/11755)) — thanks @santosraju99-hub +- **fix(api):** preserve caller-provided X-Correlation-Id on chat completions ([#11760](https://github.com/diegosouzapw/OmniRoute/pull/11760)) — thanks @aniruddhaadak80 +- **fix(resilience):** lock permanently retired models instead of short backoff (Gemini ban prevention) ([#11762](https://github.com/diegosouzapw/OmniRoute/pull/11762)) — thanks @turbolego +- **fix:** mark Vercel AI Gateway as passthroughModels ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **fix(resilience):** stop hammering permanently-moved endpoints and billing-suspended accounts ([#11774](https://github.com/diegosouzapw/OmniRoute/pull/11774)) — thanks @turbolego +- **fix(lease):** remove global static reservation and gate routing on live active lease occupancy ([#11775](https://github.com/diegosouzapw/OmniRoute/pull/11775)) — thanks @TheDemonTuan +- **fix(ci):** size the install-upgrade gate to a measured run, and log the pack cost ([#11776](https://github.com/diegosouzapw/OmniRoute/pull/11776)) +- **fix(pwa):** stop serving the stale shell after deploys ([#11779](https://github.com/diegosouzapw/OmniRoute/pull/11779)) — thanks @HouMinXi +- **fix(combo):** lock GitHub models rejected as "not supported" for future requests ([#11781](https://github.com/diegosouzapw/OmniRoute/pull/11781)) — thanks @turbolego +- **fix(cli):** probe both IPv4 and IPv6 loopback for server readiness (#11766) ([#11794](https://github.com/diegosouzapw/OmniRoute/pull/11794)) — thanks @vermasomesh835 +- **fix(sse):** preserve CLIProxyAPI wrappers per connection ([#11797](https://github.com/diegosouzapw/OmniRoute/pull/11797)) — thanks @RaviTharuma +- **fix(providers):** honor model auto-fetch opt-in on create ([#11798](https://github.com/diegosouzapw/OmniRoute/pull/11798)) — thanks @RaviTharuma +- **fix(dashboard):** make model auto-fetch order independent ([#11805](https://github.com/diegosouzapw/OmniRoute/pull/11805)) — thanks @RaviTharuma +- **fix(docker):** make GHA cache exports best effort ([#11806](https://github.com/diegosouzapw/OmniRoute/pull/11806)) — thanks @RaviTharuma +- **fix(kiro):** do not permanently ban on 'User is not authorized to make this call' ([#11809](https://github.com/diegosouzapw/OmniRoute/pull/11809)) — thanks @Deftera186 +- **fix(sse):** keep cache-write tokens in OpenAI-shaped usage ([#11814](https://github.com/diegosouzapw/OmniRoute/pull/11814)) — thanks @TheDemonTuan +- **fix(docs):** resolve relative markdown and wiki links across Fumadocs and GitHub wiki ([#11834](https://github.com/diegosouzapw/OmniRoute/pull/11834)) — thanks @AndrianBalanescu +- **fix(chat):** preserve unstripped model string for passthrough provider routing ([#11840](https://github.com/diegosouzapw/OmniRoute/pull/11840)) — thanks @raheemuddin786 +- **fix(mcp):** avoid async initialization inside sync esm wrappers (#11569) ([#11849](https://github.com/diegosouzapw/OmniRoute/pull/11849)) — thanks @HouMinXi +- **fix(sse):** preserve coherent markdown token boundaries across stream translation chunks (#11606) ([#11851](https://github.com/diegosouzapw/OmniRoute/pull/11851)) — thanks @HouMinXi +- **fix(ci):** run the install-upgrade gate on disk, not on the /tmp tmpfs ([#11855](https://github.com/diegosouzapw/OmniRoute/pull/11855)) +- **fix(skills):** expand shorthand property types in injected tool schemas ([#11857](https://github.com/diegosouzapw/OmniRoute/pull/11857)) — thanks @alvinveroy +- **fix(ci):** publish npm from a hosted runner so provenance is accepted ([#11877](https://github.com/diegosouzapw/OmniRoute/pull/11877)) +- **fix(lint):** pin react hooks plugin to 7.0.1 ([#11886](https://github.com/diegosouzapw/OmniRoute/pull/11886)) +- **fix(ci):** surface and clear release-green ESLint failure ([#11890](https://github.com/diegosouzapw/OmniRoute/pull/11890)) — thanks @MumuTW +- **fix(guardrails):** prevent duplicate prompt-injection-guard log output ([#11936](https://github.com/diegosouzapw/OmniRoute/pull/11936)) — thanks @patrykkopycinski +- **fix(auth):** downgrade expected transient states from warn to debug ([#11937](https://github.com/diegosouzapw/OmniRoute/pull/11937)) — thanks @patrykkopycinski +- **fix(codex):** fail over image generation for imported free plans ([#11948](https://github.com/diegosouzapw/OmniRoute/pull/11948)) — thanks @Bl0ck154 +- **fix(codex):** normalize non-stream responses ([#11951](https://github.com/diegosouzapw/OmniRoute/pull/11951)) — thanks @Bl0ck154 +- **fix(images):** forward Antigravity image size ([#11952](https://github.com/diegosouzapw/OmniRoute/pull/11952)) — thanks @Bl0ck154 +- **fix(db):** invalidate connection cache after upsert ([#11953](https://github.com/diegosouzapw/OmniRoute/pull/11953)) — thanks @Bl0ck154 +- **fix(codex):** restore imported account state ([#11954](https://github.com/diegosouzapw/OmniRoute/pull/11954)) — thanks @Bl0ck154 +- **fix(sse):** give extended-thinking targets the reasoning readiness budget ([#11959](https://github.com/diegosouzapw/OmniRoute/pull/11959)) — thanks @abhisheksharma2411 +- **fix(dashboard):** use opaque background and readable text color on cost chart tooltips ([#11960](https://github.com/diegosouzapw/OmniRoute/pull/11960)) — thanks @ZaimMarzuki +- **fix(antigravity):** send complete loadCodeAssist metadata (ideType/platform/pluginType as numeric enums) ([#11969](https://github.com/diegosouzapw/OmniRoute/pull/11969)) — thanks @rifqiawl +- **fix(core):** resolve DB init race condition and reasoning translation ([#12003](https://github.com/diegosouzapw/OmniRoute/pull/12003)) — thanks @raheemuddin786 +- **fix(admission):** exclude reclaimable page cache from the cgroup pressure ratio ([#12017](https://github.com/diegosouzapw/OmniRoute/pull/12017)) — thanks @HouMinXi +- **fix(build):** prune non-production trees in NFT trace excludes and tsconfig ([#12028](https://github.com/diegosouzapw/OmniRoute/pull/12028)) — thanks @Chewji9875 +- **fix(migrations):** add renamed migration compatibility for 056/073/077/101 ([#12036](https://github.com/diegosouzapw/OmniRoute/pull/12036)) — thanks @oyi77 +- **fix(sse):** default crash-guard logger to console.warn, not console ([#12042](https://github.com/diegosouzapw/OmniRoute/pull/12042)) — thanks @HouMinXi +- **fix(catalog):** derive vision/modalities for built-in auto combos from effective target pool ([#12046](https://github.com/diegosouzapw/OmniRoute/pull/12046)) — thanks @Prajeeth-12 +- **fix(release):** the packaged-app smoke verifies the database opened, not a driver line the primary path never prints (twin of #12032) ([#12047](https://github.com/diegosouzapw/OmniRoute/pull/12047)) +- **fix(barrel):** migrate src/app/ off the @/lib/localDb barrel import (#11795 Phase 2) ([#12051](https://github.com/diegosouzapw/OmniRoute/pull/12051)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate src/lib/ off the localDb barrel to direct db imports (#59) ([#12052](https://github.com/diegosouzapw/OmniRoute/pull/12052)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate open-sse, src/shared, src/sse, src/models, src/domain off the @/lib/localDb barrel import (#11795 Phase 4) ([#12053](https://github.com/diegosouzapw/OmniRoute/pull/12053)) — thanks @jonlwheat2-gif +- **fix(barrel):** delete the @/lib/localDb barrel — every consumer migrated (#11795 Phase 5) ([#12055](https://github.com/diegosouzapw/OmniRoute/pull/12055)) — thanks @jonlwheat2-gif +- **fix(radar):** restore D12 public boundary ([#12057](https://github.com/diegosouzapw/OmniRoute/pull/12057)) +- **fix(dev):** bound webpack and Tailwind scans ([#12075](https://github.com/diegosouzapw/OmniRoute/pull/12075)) — thanks @backryun +- **fix(dev):** isolate root layout settings reads ([#12076](https://github.com/diegosouzapw/OmniRoute/pull/12076)) — thanks @backryun +- **fix(dev):** reduce instrumentation executor fan-out (phase 3) ([#12078](https://github.com/diegosouzapw/OmniRoute/pull/12078)) — thanks @backryun +- **fix(dev):** make logging resources HMR-singleton ([#12079](https://github.com/diegosouzapw/OmniRoute/pull/12079)) — thanks @backryun +- **fix(sse):** keep unavailable forced connections scoped ([#12080](https://github.com/diegosouzapw/OmniRoute/pull/12080)) — thanks @keeltrace +- **fix(dev):** isolate batch dispatch from instrumentation ([#12081](https://github.com/diegosouzapw/OmniRoute/pull/12081)) — thanks @backryun +- **fix(ci):** reconcile release test contract drift ([#12082](https://github.com/diegosouzapw/OmniRoute/pull/12082)) — thanks @backryun +- **fix(ci):** accept CVE-2025-68121 in the prebuilt tls-client .so, auto-close base-red issues, guard Scorecard on the default branch ([#12085](https://github.com/diegosouzapw/OmniRoute/pull/12085)) +- **fix(db):** drop three consumer-less 1proxy exports — dead-code base-red on release/v3.8.51 after the barrel deletion (#12055) ([#12087](https://github.com/diegosouzapw/OmniRoute/pull/12087)) +- **fix(api):** clear the six API-route TypeScript regressions the new gate landed red on ([#12094](https://github.com/diegosouzapw/OmniRoute/pull/12094)) +- **fix(release):** never let the tag-push Create Release append auto notes to the curated body ([#12096](https://github.com/diegosouzapw/OmniRoute/pull/12096)) +- **fix(sse):** honor CLIProxyAPI environment API key ([#12099](https://github.com/diegosouzapw/OmniRoute/pull/12099)) — thanks @RaviTharuma +- **fix(oauth):** bind Google refresh to the client that issued the token ([#12106](https://github.com/diegosouzapw/OmniRoute/pull/12106)) — thanks @HouMinXi +- **fix(dashboard):** make RequestLoggerDetail loadable outside Next — CSS via globals.css + CJS/ESM interop (#11703 base-reds) ([#12114](https://github.com/diegosouzapw/OmniRoute/pull/12114)) +- **fix(leases):** project status lease row to lease columns so joined connection PII never escapes ([#12115](https://github.com/diegosouzapw/OmniRoute/pull/12115)) — thanks @geek007git +- **fix(plugins):** do not kill the plugin process when a fire-and-forget hook times out ([#12116](https://github.com/diegosouzapw/OmniRoute/pull/12116)) — thanks @geek007git +- **fix(guardrails):** restore injection-guard logging on middleware-only routes ([#12117](https://github.com/diegosouzapw/OmniRoute/pull/12117)) — thanks @geek007git +- **fix(plugins):** refresh stored manifest from disk on activate so new hook fields reach existing installs ([#12120](https://github.com/diegosouzapw/OmniRoute/pull/12120)) — thanks @geek007git +- **fix(codex):** preserve existing provider state when bulk-import upserts a matching connection ([#12122](https://github.com/diegosouzapw/OmniRoute/pull/12122)) — thanks @geek007git +- **fix(ci):** clear the two base-reds the 2026-08-30 merge batch left on release/v3.8.51 (round 3) ([#12123](https://github.com/diegosouzapw/OmniRoute/pull/12123)) +- **fix(sse):** strip type:'custom' from Claude tools on agentrouter dispatch ([#12126](https://github.com/diegosouzapw/OmniRoute/pull/12126)) — thanks @wahidsadik371-coder +- **fix(sse):** stop advertising video providers the dispatcher cannot run ([#12131](https://github.com/diegosouzapw/OmniRoute/pull/12131)) — thanks @quiterunner-commits +- **fix(search):** treat HTTP 432 and plan limit errors as transient cooldown ([#12139](https://github.com/diegosouzapw/OmniRoute/pull/12139)) — thanks @HouMinXi +- **fix(sse):** refuse an AI Horde queue that cannot fit the request budget ([#12143](https://github.com/diegosouzapw/OmniRoute/pull/12143)) — thanks @quiterunner-commits +- **fix(ci):** clear the base-reds the afternoon merge batch left on release/v3.8.51 (round 5: provider count 352, TS2554/TS2677) ([#12144](https://github.com/diegosouzapw/OmniRoute/pull/12144)) +- **fix(translator):** strip neutral tool_choice when tools absent in Responses-to-Chat (#12141) ([#12166](https://github.com/diegosouzapw/OmniRoute/pull/12166)) — thanks @HouMinXi +- **fix(guardrails):** pass providerId to getResolvedModelCapabilities in checkComboVision (#12112) ([#12169](https://github.com/diegosouzapw/OmniRoute/pull/12169)) — thanks @HouMinXi +- **fix(translator):** strip plaintext reasoning content for opaque responses backends (#12128) ([#12171](https://github.com/diegosouzapw/OmniRoute/pull/12171)) — thanks @HouMinXi +- **perf(stream):** compile hot-path regexes once, bound token caches, fix quadratic buffering ([#12179](https://github.com/diegosouzapw/OmniRoute/pull/12179)) — thanks @opensource-elearning +- **fix:** resolve compression worker file using runtime anchors instead of… ([#12183](https://github.com/diegosouzapw/OmniRoute/pull/12183)) — thanks @NightStalker-87 +- **fix:** prevent Claude Code session kills via liveness-aware readiness + auto model echo ([#12189](https://github.com/diegosouzapw/OmniRoute/pull/12189)) — thanks @opensource-elearning +- **fix(translator):** preserve falsy primitive values in Gemini and Antigravity function response results ([#12191](https://github.com/diegosouzapw/OmniRoute/pull/12191)) — thanks @Siva010 +- **fix(oauth):** mark empty Antigravity projectId as degraded and clear stale errors (#11284) ([#12205](https://github.com/diegosouzapw/OmniRoute/pull/12205)) — thanks @HouMinXi +- **fix(providers):** gate the Codex auto-ping usage read on the shared quota throttle ([#12209](https://github.com/diegosouzapw/OmniRoute/pull/12209)) — thanks @abhisheksharma2411 +- **fix(combos):** prioritize SQLite row id over inner JSON id and notify delete errors ([#12213](https://github.com/diegosouzapw/OmniRoute/pull/12213)) — thanks @HouMinXi +- **fix(providers):** mark featherless.ai as no longer having a free tier (`hasFree: false`) ([#12216](https://github.com/diegosouzapw/OmniRoute/pull/12216)) — thanks @turbolego +- **fix(deepseek-web):** stop Turbopack dev panic in the PoW worker path resolver ([#12221](https://github.com/diegosouzapw/OmniRoute/pull/12221)) — thanks @hartmark +- **fix(chat-admission):** clarify local 503 source ([#12223](https://github.com/diegosouzapw/OmniRoute/pull/12223)) — thanks @keeltrace +- **fix(dev):** silence webpack runtime module warnings ([#12228](https://github.com/diegosouzapw/OmniRoute/pull/12228)) — thanks @backryun +- **fix(db):** use module.require for CommonJS runtime driver loading ([#12230](https://github.com/diegosouzapw/OmniRoute/pull/12230)) — thanks @raheemuddin786 +- **fix(memory):** honest probe-driven FTS5 keyword status + memory_id rowid sync ([#12231](https://github.com/diegosouzapw/OmniRoute/pull/12231)) — thanks @raheemuddin786 +- **fix(system):** propagate abort signal to stream reader in HTTP version checks ([#12232](https://github.com/diegosouzapw/OmniRoute/pull/12232)) — thanks @raheemuddin786 +- **fix(resilience):** isolate local host execution errors from provider circuit breakers ([#12233](https://github.com/diegosouzapw/OmniRoute/pull/12233)) — thanks @raheemuddin786 +- **fix(sse):** sort injected tools deterministically for prompt caching ([#12234](https://github.com/diegosouzapw/OmniRoute/pull/12234)) — thanks @raheemuddin786 +- **fix(combo):** always clear the loop-safety timer, not just on the happy path (#11804) ([#12245](https://github.com/diegosouzapw/OmniRoute/pull/12245)) +- **fix(quality):** base-red pair — stryker tap registration + turn-pin suites aligned to the window gate ([#12255](https://github.com/diegosouzapw/OmniRoute/pull/12255)) — thanks @backryun +- **fix(dev):** qualify Turbopack runtime boundaries ([#12258](https://github.com/diegosouzapw/OmniRoute/pull/12258)) — thanks @backryun +- **fix(combo):** fall back to full pool when collapsed sole survivor is context-too-small ([#12278](https://github.com/diegosouzapw/OmniRoute/pull/12278)) — thanks @opensource-elearning +- **fix(combo):** bound the pre-dispatch unavailable skip so a stale label cannot dark a pool (#12168) ([#12285](https://github.com/diegosouzapw/OmniRoute/pull/12285)) +- **fix(security):** strip Qwen/Alibaba console-session cookies from provider API responses ([#12287](https://github.com/diegosouzapw/OmniRoute/pull/12287)) +- **fix(usage):** console-aware Token Plan guidance + subscription hint on bailian 401 ([#12288](https://github.com/diegosouzapw/OmniRoute/pull/12288)) +- **fix(resourcePressure):** log numeric detail on every rejection, recover faster ([#12293](https://github.com/diegosouzapw/OmniRoute/pull/12293)) — thanks @hartmark +- **fix(release):** drain the 2026-09-01 base-red window — passthrough usage regression + radarPage i18n keys ([#12327](https://github.com/diegosouzapw/OmniRoute/pull/12327)) +- **fix(ci):** openapi-security-tiers checker must honor routeGuard patterns + imported prefixes ([#12350](https://github.com/diegosouzapw/OmniRoute/pull/12350)) — thanks @ggiak +- **perf(ci):** cache node_modules in the npm-ci-retry composite (#8084 D3) ([#12408](https://github.com/diegosouzapw/OmniRoute/pull/12408)) +- **revert(ui):** point CTAs back at their real destinations ([#12410](https://github.com/diegosouzapw/OmniRoute/pull/12410)) +- **fix(release):** drain the 2026-09-02 base-red — rerank-providers import + api-typecheck baseline ratchet ([#12414](https://github.com/diegosouzapw/OmniRoute/pull/12414)) +- **fix(types):** make system prompt injection noImplicitAny-safe ([#12416](https://github.com/diegosouzapw/OmniRoute/pull/12416)) +- **fix(quality):** validate typecheck baseline schema ([#12419](https://github.com/diegosouzapw/OmniRoute/pull/12419)) +- **fix(memory):** resolve rerank provider node cache import ([#12421](https://github.com/diegosouzapw/OmniRoute/pull/12421)) +- **fix(ci):** repair release-root regressions — pack dedup, web-session syntax, uc-image ids ([#12423](https://github.com/diegosouzapw/OmniRoute/pull/12423)) — thanks @backryun +- **fix(providers):** separate MaxAI and UC credential contracts ([#12431](https://github.com/diegosouzapw/OmniRoute/pull/12431)) +- **fix(cli):** preserve Claude settings on config set ([#12432](https://github.com/diegosouzapw/OmniRoute/pull/12432)) — thanks @KrzysiekSko +- **fix(providers):** repair the maxai credential block truncated by merge auto-resolve ([#12433](https://github.com/diegosouzapw/OmniRoute/pull/12433)) +- **fix(quality):** record the 2026-09-02 merged growth in the file-size baseline ([#12434](https://github.com/diegosouzapw/OmniRoute/pull/12434)) +- **fix(uc):** route UC error strings through sanitizeErrorMessage; allowlist the retired codex id ([#12437](https://github.com/diegosouzapw/OmniRoute/pull/12437)) +- **fix(adapta):** redact streamed upstream errors ([#12438](https://github.com/diegosouzapw/OmniRoute/pull/12438)) +- **fix(sse):** remap non-contiguous upstream tool_calls index to a gap-free output_index ([#12445](https://github.com/diegosouzapw/OmniRoute/pull/12445)) — thanks @hartmark +- **fix(quality):** detect duplicate tool_calls entries in one response ([#12446](https://github.com/diegosouzapw/OmniRoute/pull/12446)) — thanks @hartmark +- **fix(conversations):** resolve turn content OmniRoute never sends back to the client ([#12447](https://github.com/diegosouzapw/OmniRoute/pull/12447)) — thanks @hartmark +- **fix(combo):** do not treat credits-exhausted 401 as auth skip ([#12449](https://github.com/diegosouzapw/OmniRoute/pull/12449)) — thanks @RaviTharuma +- **fix(auth):** do not park healthy quota accounts as expired ([#12452](https://github.com/diegosouzapw/OmniRoute/pull/12452)) — thanks @RaviTharuma +- **fix(responses-continuation):** fail closed on a collector-truncated, empty output array ([#12460](https://github.com/diegosouzapw/OmniRoute/pull/12460)) — thanks @hartmark +- **fix(logging):** raise the SSE payload collector's default cap ([#12461](https://github.com/diegosouzapw/OmniRoute/pull/12461)) — thanks @hartmark +- **fix(resilience):** surface Responses failed.error.message in 502s ([#12472](https://github.com/diegosouzapw/OmniRoute/pull/12472)) — thanks @RaviTharuma +- **fix(security):** unbiased maxai X-Random nonce + stricter URL/regex assertions ([#12502](https://github.com/diegosouzapw/OmniRoute/pull/12502)) +- **fix(video):** re-anchor transcript log-redaction so PII/credential maskers can't reopen the leak (#12150 P1 follow-up) ([#12503](https://github.com/diegosouzapw/OmniRoute/pull/12503)) +- **fix(monitoring):** serve cached credentialHealth off the request path ([#12533](https://github.com/diegosouzapw/OmniRoute/pull/12533)) — thanks @RaviTharuma +- **fix(quota):** keep Antigravity Gemini usable when Claude weekly is empty ([#12566](https://github.com/diegosouzapw/OmniRoute/pull/12566)) — thanks @HouMinXi +- **fix(authz):** hard-gate every credential export and CLI-config write (GHSA-5926-2w35-7h4q) ([#12600](https://github.com/diegosouzapw/OmniRoute/pull/12600)) +- **fix(docs):** restore the Next build — REMOVED_PROVIDERS.md had no frontmatter (base-red #12581) ([#12610](https://github.com/diegosouzapw/OmniRoute/pull/12610)) +- **fix(quality):** re-point the zcodeProtocol public-creds allowlist to line 313 ([#12615](https://github.com/diegosouzapw/OmniRoute/pull/12615)) +- **fix(security):** close 3 advisories — search baseUrl exfil, sk- in the error sanitizer, bifrost relay header leak ([#12620](https://github.com/diegosouzapw/OmniRoute/pull/12620)) +- **fix(providers):** sanitize boolean required and nested bare maps for Gemini (#12269) ([#12624](https://github.com/diegosouzapw/OmniRoute/pull/12624)) — thanks @HouMinXi +- **fix(resilience):** keep Overloaded STREAM_EARLY_EOF off the provider breaker ([#12626](https://github.com/diegosouzapw/OmniRoute/pull/12626)) — thanks @HouMinXi +- **fix(catalog):** re-audit free-tier quotas against official pages ([#12649](https://github.com/diegosouzapw/OmniRoute/pull/12649)) +- **fix(dashboard):** keep a request's pending-tracking id stable across combo target retries ([#12650](https://github.com/diegosouzapw/OmniRoute/pull/12650)) — thanks @hartmark +- **fix(tests):** retire dead model ids from the chat-pipeline integration suite (base-red #12581) ([#12670](https://github.com/diegosouzapw/OmniRoute/pull/12670)) +- **fix:** resolve SqliteError no such table compression_run_telemetry during cleanup ([#12682](https://github.com/diegosouzapw/OmniRoute/pull/12682)) — thanks @groovecityJO +- **fix(combo):** treat a pin-only step as implicit connection allowlist ([#12697](https://github.com/diegosouzapw/OmniRoute/pull/12697)) — thanks @HouMinXi +- **fix(ci):** ship .npmrc in published package so legacy-peer-deps applies to consumers (#11544) ([#12699](https://github.com/diegosouzapw/OmniRoute/pull/12699)) — thanks @KooshaPari +- **fix(docker):** pin 4 CLI tools to exact versions (#12576) ([#12703](https://github.com/diegosouzapw/OmniRoute/pull/12703)) — thanks @KooshaPari +- **fix(docker):** document and harden cli profile trust boundary (#12570) ([#12706](https://github.com/diegosouzapw/OmniRoute/pull/12706)) — thanks @KooshaPari +- **fix(sse):** redact video transcript in pre-guardrail rejected-request logs (#12150 P2 item 7) ([#12710](https://github.com/diegosouzapw/OmniRoute/pull/12710)) +- **fix(i18n):** quote placeholder in OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES description (#12505) ([#12769](https://github.com/diegosouzapw/OmniRoute/pull/12769)) — thanks @KooshaPari +- **fix(api/pricing):** surface validation error message as string, not raw object (#12494) ([#12771](https://github.com/diegosouzapw/OmniRoute/pull/12771)) — thanks @KooshaPari +- **fix(sse):** re-enable prompt compression for native Codex passthrough ([#12834](https://github.com/diegosouzapw/OmniRoute/pull/12834)) — thanks @thomasmaerz +- **fix(dashboard):** model health tests for a provider node set to the Responses API now call `/v1/responses` with a Responses-shaped body instead of `/v1/chat/completions` — those models were reported as `Provider returned HTTP 200 but no text content` even though the same model answered normally through `/v1/responses` ([#13070](https://github.com/diegosouzapw/OmniRoute/issues/13070)) ([#13087](https://github.com/diegosouzapw/OmniRoute/pull/13087)) — thanks @ntdatt812 +- fix(routing): stop a round-robin combo's "opencode" targets from collapsing onto the opencode-zen connection (#11912) ([#13283](https://github.com/diegosouzapw/OmniRoute/pull/13283)) +- fix(routing): stop the reactive-compaction debug log from lying when compression is globally disabled (#11977) ([#13276](https://github.com/diegosouzapw/OmniRoute/pull/13276)) +- fix(electron): relativize standalone-bundle symlink targets so Stage 8 manifest verification stops failing on Windows (#11979) ([#13251](https://github.com/diegosouzapw/OmniRoute/pull/13251)) +- fix(dashboard): surface a visible error when Compression Studio's combined preview run fails (#12061) ([#13288](https://github.com/diegosouzapw/OmniRoute/pull/13288)) +- fix(dashboard): make the compression "Effective pipeline" preview honor the active profile and warn when the master switch is off (#12063) ([#13287](https://github.com/diegosouzapw/OmniRoute/pull/13287)) +- fix(providers): scope TinyCMS Web signer's DOM shims to each call instead of leaking them for the process lifetime, and surface a clean HTTP status on a non-JSON interception-toggles error (#12072) ([#13254](https://github.com/diegosouzapw/OmniRoute/pull/13254)) +- fix(guardrails): stop Vision Bridge from re-selecting a model locked after a 404 (#12111) ([#13259](https://github.com/diegosouzapw/OmniRoute/pull/13259)) +- fix(sse): require Responses-shaped body before native OpenAI-compatible passthrough (#12129) ([#13278](https://github.com/diegosouzapw/OmniRoute/pull/13278)) +- fix(providers): minimax-m3 now collapses manual thinking.type:"enabled" to adaptive, preventing upstream 400 (2013) (#12132) ([#13249](https://github.com/diegosouzapw/OmniRoute/pull/13249)) +- fix(db): scope model visibility overrides by modality so hiding a Chat model no longer hides an identically-ID'd Image/Embeddings/etc. model (#12172) ([#13258](https://github.com/diegosouzapw/OmniRoute/pull/13258)) +- fix(db): scope local-provider apiKey dedup to matching base URL so LM Studio/Ollama-style connections support multiple accounts (#12173) ([#13250](https://github.com/diegosouzapw/OmniRoute/pull/13250)) +- fix(providers): refresh Trae's stale Referer/Origin and forward user timezone so imported connections stop failing with 401 (#12190) ([#13255](https://github.com/diegosouzapw/OmniRoute/pull/13255)) +- fix(providers): route opencode-go/gpt-5.6-luna to /responses instead of /chat/completions (#12196) ([#13275](https://github.com/diegosouzapw/OmniRoute/pull/13275)) +- fix(dashboard): allow deleting the last extra-upstream-header row even when invalid (#12251) ([#13252](https://github.com/diegosouzapw/OmniRoute/pull/13252)) +- **fix(i18n):** translate pre-existing `__MISSING__:` keys for `combo.sort`, `requestLogger.detail` expand/collapse, `common.profile`, and `settings.resilienceCredentialHealth*` across 39 locales ([#12272](https://github.com/diegosouzapw/OmniRoute/issues/12272)) ([#13034](https://github.com/diegosouzapw/OmniRoute/pull/13034)) — thanks @HouMinXi +- fix(cli): run the Node.js runtime compatibility guard before the heavy `tsx/esm` + Commander import chain so an unsupported runtime gets a clear message instead of a raw `Invalid regular expression flags` crash (#12296) ([#13253](https://github.com/diegosouzapw/OmniRoute/pull/13253)) +- fix(dashboard): refresh the providers list after deleting a compatible provider node (#12298) ([#13256](https://github.com/diegosouzapw/OmniRoute/pull/13256)) +- fix(usage): fail closed on API-key budget enforcement when a provider's `auto` routing alias has no pricing row, instead of silently counting it as $0 (#12341) ([#13257](https://github.com/diegosouzapw/OmniRoute/pull/13257)) +- **fix(providers):** include the submitted Agnes video model when polling by `video_id` ([#12356](https://github.com/diegosouzapw/OmniRoute/pull/12356)) — thanks @pacocartones +- **fix(routing):** custom OpenAI-compatible nodes now honor the saved Chat/Responses API type after edits instead of letting the node's original ID prefix override the live connection setting ([#11884](https://github.com/diegosouzapw/OmniRoute/issues/11884)). ([#12358](https://github.com/diegosouzapw/OmniRoute/pull/12358)) — thanks @pacocartones +- fix(sse): surface an error instead of a silent empty 200 when a Claude stream closes with zero bytes (#12398) ([#13285](https://github.com/diegosouzapw/OmniRoute/pull/13285)) +- fix(oauth): warn before the dead localhost:8080 redirect in antigravity/gemini `oauth start` (#12413) ([#13265](https://github.com/diegosouzapw/OmniRoute/pull/13265)) +- fix(providers): stop devin-cli spawn error from double-closing the SSE controller (#12517) ([#13261](https://github.com/diegosouzapw/OmniRoute/pull/13261)) +- **fix(rerank):** clamp Voyage `top_k` to the documents actually sent after empty-string filtering, and honor `return_documents: false` in the NVIDIA response adapter (#12523 — thanks @pacocartones) +- **fix(audio):** `/v1/audio/translations` now resolves combo names the way `/v1/audio/transcriptions` already does, so a combo that `GET /v1/models` advertises is fanned out to its targets instead of being rejected with `400 Invalid translation model: . Use format: provider/model`; literal `provider/model` ids and unknown bare names behave as before (#12536 — thanks @pacocartones) +- **fix(gemini):** strip the JSON-Schema-2020-12 `prefixItems` keyword from Gemini tool schemas at every nesting level, so Claude Code tool definitions no longer fail with `400 Unknown name "prefixItems"` on Gemini models (#12540 — thanks @pacocartones) +- **fix(api-manager):** Expose an accessible loading status while API keys are fetched instead of an empty accessibility tree (#12541 — thanks @pacocartones) +- **fix(video):** Clamp `estimateJpegFrameBytes` at zero for padding-only payloads and build the three encode-side frame data URIs from `JPEG_FRAME_DATA_URI_PREFIX` instead of a repeated literal (#12543 — thanks @pacocartones) +- **fix(devin):** accept Windows `DEVIN_AGENTIC_HOME` sandbox paths (`C:\...\.sandbox\...`) in the isolated-home check so the Devin Claude Bridge no longer fails closed on Windows ([#12405](https://github.com/diegosouzapw/OmniRoute/issues/12405)) (#12545 — thanks @pacocartones) +- **fix(db):** Add `conversation_turn_nodes` and orphaned `agentic_conversations` to the auto-cleanup cycle under the existing `retention.callLogs` window, so identity nodes whose call-log content has already been purged no longer accumulate without bound in `storage.sqlite` (#12548 — thanks @pacocartones) +- **fix(i18n):** Wrap the `~/.claude/profiles//settings.json` placeholder in ICU single quotes in the `OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES` feature-flag description across all 42 locales and the TypeScript default, so next-intl no longer fails with `INVALID_MESSAGE: UNCLOSED_TAG` and the Feature Flags card shows the description instead of the raw key (#12549 — thanks @pacocartones) +- **fix(orchestration):** `updateCloudAgentTask` now publishes the task's real `status` on `agent.task.updated` when an update only touches `result`, `activities` or `error`, instead of the fabricated `"updated"` state, and stays silent when no row matched the id (#12550 — thanks @pacocartones) +- **fix(i18n):** the home "Recent Requests" panel and the Provider Topology legend are now translated instead of rendering English copies on non-English dashboards; the legend reads its own `home.topologyLegend*` labels with consistent casing rather than borrowing the memory-settings "Recent" and analytics "Error" strings (#12551 — thanks @pacocartones). +- **docs(reference):** bring the `FEATURE_FLAGS.md` catalog back to 1:1 with `featureFlagDefinitions.ts` — 20 missing flags added, the two `*_BLOCK_THRESHOLD` env-only knobs moved out of the flag tables, category/total counts and the Live WS port corrected, guarded by a static test (#12552 — thanks @pacocartones) +- **fix(i18n):** backfill missing `usage.kiloPass*` strings in 39 locales and restore `featureFlagOmnirouteDisableThinkingLevelVariantsDescription` in `pt.json` ([#12561](https://github.com/diegosouzapw/OmniRoute/issues/12561)) ([#13027](https://github.com/diegosouzapw/OmniRoute/pull/13027)) — thanks @HouMinXi +- fix(docker): default docker-compose app ports (dashboard/API/live-WS) to loopback instead of `0.0.0.0`, closing the anonymous `/v1` LAN/WAN exposure gap left open by `REQUIRE_API_KEY=false` (#12568) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): close DNS-rebinding SSRF gap in webhook outbound-URL guard (#12569) ([#13243](https://github.com/diegosouzapw/OmniRoute/pull/13243)) +- fix(docker): require a per-session token on the VNC browser CDP bridge and isolate it on a dedicated Docker network (#12571) ([#13241](https://github.com/diegosouzapw/OmniRoute/pull/13241)) +- fix(open-sse): write Adobe Firefly session tokens and cookie jars with 0700/0600 permissions instead of the process umask (#12572) ([#13242](https://github.com/diegosouzapw/OmniRoute/pull/13242)) +- fix(api): remove hardcoded wildcard CORS in openai-to-gemini-sse.ts so the centralized fail-closed CORS gate is the sole source of `Access-Control-Allow-Origin` (#12573) ([#13239](https://github.com/diegosouzapw/OmniRoute/pull/13239)) +- fix(api): enforce API key policy (budget/rate-limit/schedule/endpoint scoping) on the ElevenLabs speech-to-text, text-to-speech and voices proxy routes (#12574) ([#13244](https://github.com/diegosouzapw/OmniRoute/pull/13244)) +- fix(sse): cap HuggingChat NDJSON body size and bound the read loop with the fetch timeout so a stalled or hostile upstream cannot buffer unbounded memory (#12577) ([#13282](https://github.com/diegosouzapw/OmniRoute/pull/13282)) +- fix(docker): scope the cliproxyapi/qdrant/bifrost sidecars to loopback by default and forward `CLIPROXYAPI_MANAGEMENT_KEY` into the cliproxyapi container so its management API is not left both unauthenticated and LAN-published (#12578) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): create DB export temp paths with `fs.mkdtempSync` instead of predictable timestamps (#12579) ([#13237](https://github.com/diegosouzapw/OmniRoute/pull/13237)) +- Cline 401 bodies that say "re-authenticate your Cline account" classify as a refreshable OAuth token, not a terminal expired key. The cooling panel no longer labels every cooldown as a 429; it shows the recorded last error instead. (#12594) ([#13060](https://github.com/diegosouzapw/OmniRoute/pull/13060)) — thanks @HouMinXi +- **fix(catalog):** degrade unknown combo targets instead of dropping LCD modalities, and persist OpenRouter `architecture.input_modalities` into the capability snapshot ([#12613](https://github.com/diegosouzapw/OmniRoute/issues/12613)) ([#13042](https://github.com/diegosouzapw/OmniRoute/pull/13042)) — thanks @HouMinXi +- fix(providers): send `x-api-key` instead of `Authorization: Bearer` for OpenCode Zen's `/v1/responses` endpoint (Muse Spark Contributor models), fixing a 401 on OmniRoute's auth header (#12633) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(sse): surface the actionable "Auggie CLI not found" message when the shell reports a missing `auggie` binary via exit code instead of a spawn error (#12645) ([#13263](https://github.com/diegosouzapw/OmniRoute/pull/13263)) +- **fix(config):** Persist deletions of built-in background-degradation entries — when a stored settings record exists its `degradationMap` is now authoritative instead of being merged under the defaults, so an entry the user removed in the dashboard no longer reappears on the next apply or restart ([#12424](https://github.com/diegosouzapw/OmniRoute/issues/12424)) ([#12647](https://github.com/diegosouzapw/OmniRoute/pull/12647)) — thanks @pacocartones +- **fix(gamification):** action-count badge milestones (First Token, Token Consumer, Token Machine, Token Whale, and the token-sharing tier) are now backed by a durable `xp_action_counts` counter incremented in `addXp()`, instead of a live `COUNT(*)` over `xp_audit_log`. The audit log is pruned by `retention.xpAuditLog` (default 30 days), so on a default install those "lifetime" milestones were really "actions in the last 30 days" and unlocked badges could stop unlocking once old rows aged out. `getActionCount()` and `checkActionCountBadges()` now read the same durable source, and a migration backfills existing totals from the surviving audit rows ([#12546](https://github.com/diegosouzapw/OmniRoute/issues/12546)) ([#12651](https://github.com/diegosouzapw/OmniRoute/pull/12651)) — thanks @pacocartones +- **fix(images):** `/v1/images/edits` now iterates a combo's targets the same way `/v1/images/generations` does (#9239) instead of flattening a bare combo to its first target. A combo whose first target is not edit-capable — or lacks credentials — now falls through to a later edit-capable target rather than hard-erroring, and missing credentials are skipped (not a hard `401`) to match the generations path. The per-target skip/terminal classification is extracted into a shared `runImageComboTargets` loop, so generations behavior is unchanged ([#12547](https://github.com/diegosouzapw/OmniRoute/issues/12547)). ([#12653](https://github.com/diegosouzapw/OmniRoute/pull/12653)) — thanks @pacocartones +- fix(sse): add first-byte watchdog to the TLS-fingerprint transport so a stalled wreq body falls back instead of hanging for minutes (#12656) ([#13272](https://github.com/diegosouzapw/OmniRoute/pull/13272)) +- fix(sse): exempt tiny-budget reasoning probes from combo quality failure and surface persisted-cooldown skips in ALL_TARGETS_SKIPPED diagnostics (#12659) ([#13269](https://github.com/diegosouzapw/OmniRoute/pull/13269)) +- fix(models): declare the real ~1M contextLength for OpenCode Zen's Muse Spark 1.2 models instead of falling back to the 200000 provider default (#12681) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(oauth): align codebuddy-cn OAuth User-Agent with the chat/usage CLI version to avoid WAF false positives (#12702) ([#13264](https://github.com/diegosouzapw/OmniRoute/pull/13264)) +- fix(dashboard): surface an authentication-required banner instead of silently blanking database settings for a guest session (#12709) ([#13270](https://github.com/diegosouzapw/OmniRoute/pull/13270)) +- **fix(resilience):** stop unbounded queue that hangs 6min until Aborted — gate, provider slot, and Bottleneck queue now share a per-connection `maxWaitMs` budget; `fail-closed` on exhaust (503); execution backstop `executionMaxWaitMs` overridable per connection with upstream clamp ([#12715](https://github.com/diegosouzapw/OmniRoute/pull/12715)) — thanks @maxmad64bis (with thanks to @Tushar49 for surfacing the slow-provider need in #12635) +- **fix(auto-combo):** every mode pack now carries `quality` and `reliability` weights (`quality` 0.02, 0.03 in `quality-first`; `reliability` 0.03, 0.04 in `reliability-first`), so selecting a pack no longer silences either signal; `DEFAULT_WEIGHTS` is unchanged ([#12731](https://github.com/diegosouzapw/OmniRoute/pull/12731)) — thanks @maxmad64bis +- **fix(sse):** the provider execution pipeline keeps the upstream error `code`/`type` again — both were lost when the non-streaming leg was extracted, so a config-class `422` (Antigravity missing project) degraded into a generic account cooldown instead of reaching the gate that recognises it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(db):** a legacy `call_logs` table no longer aborts startup — the provider-stats index is created after the column healing runs, not before it, so an install predating `request_type` opens instead of failing with `no such column` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(catalog):** a malformed row in the operator-writable custom-models blob no longer kills every `auto/*` pool ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(tests):** realign the guards that the same day's merges left asserting the old behavior — the provider-count total after GreenPT and EURouter, the GLM stream buffer slot that `#12925` turned into a declared parameter, and the injection scan bound that `#13104` reshaped from a head-only window into head-plus-tail ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- fix(cache): fold tool_choice/tools/response_format into the semantic cache signature so a cached tool_calls response can no longer be replayed for a request whose tool policy forbids it (#12734) ([#13267](https://github.com/diegosouzapw/OmniRoute/pull/13267)) +- **fix(models):** stop treating provider-supplied `isFree:true`/`:free`/`0/0` flags as trusted unless the provider has a documented free tier — only the shipped free-tier catalog decides otherwise; fetched `isFree:true` trusted only for a free-tier provider, custom `isFree:true` via a trusted path ([#12744](https://github.com/diegosouzapw/OmniRoute/pull/12744)) — thanks @maxmad64bis +- fix(memory): authenticate the internal /v1/rerank loopback call so memory reranking no longer silently degrades to unranked order when REQUIRE_API_KEY=true (#12745) ([#13268](https://github.com/diegosouzapw/OmniRoute/pull/13268)) +- fix(sse): parse Ollama Cloud's current usage markup (`$X of $Y used` aria-label, nested width style) (#12749) ([#13260](https://github.com/diegosouzapw/OmniRoute/pull/13260)) +- fix(cli): setup-opencode no longer sends an active context's management token to `/v1/models` when `--api-key`/`OMNIROUTE_API_KEY` is supplied — an explicit flag or the env var now always outranks the context's token, and the flag itself is no longer swallowed by the parent program's global `--api-key` option (#12783) ([#13246](https://github.com/diegosouzapw/OmniRoute/pull/13246)) +- fix(sse): register Arcee AI in the executor provider registry so requests reach api.arcee.ai instead of silently falling back to OpenAI (#12784) ([#13277](https://github.com/diegosouzapw/OmniRoute/pull/13277)) +- **fix(quota):** share one upstream quota read when identical saturation checks arrive at the same time, so traffic bursts don't multiply provider API calls ([#12787](https://github.com/diegosouzapw/OmniRoute/pull/12787)) — thanks @maxmad64bis +- **fix(db):** ignore expired or invalid rate-limit cooldown writes so a stale timestamp can't lock a connection that should be usable — clearing still works as before ([#12788](https://github.com/diegosouzapw/OmniRoute/pull/12788)) — thanks @maxmad64bis +- **fix(providers):** single source for provider order with `xao` ranking alongside `xai-oauth` ([#12790](https://github.com/diegosouzapw/OmniRoute/pull/12790)) — thanks @maxmad64bis +- **fix(routing):** off-table models check the free-model catalog before inheriting premium prices, latency bootstraps from the observed pool median, async tiers read live database pricing with a 90-day freshness gate, and the tier cache invalidates on every pricing write ([#12792](https://github.com/diegosouzapw/OmniRoute/pull/12792)) — thanks @maxmad64bis +- **fix(auto-combo):** snapshot scoring uses the observed per-connection account tier and per-model quality instead of neutral constants, and circuit-open providers rank lower at build time ([#12794](https://github.com/diegosouzapw/OmniRoute/pull/12794)) — thanks @maxmad64bis +- **fix(pool):** empty `auto/*` pools now say why they are empty, and the models listing uses the same paid check as routing ([#12795](https://github.com/diegosouzapw/OmniRoute/pull/12795)) — thanks @maxmad64bis +- fix(routing): recognize CLIProxyAPI's 'unknown provider for model' 400 as fallback-worthy (#12800) ([#13284](https://github.com/diegosouzapw/OmniRoute/pull/13284)) +- **fix(sse):** translate-mode streams now emit the estimated token counts as a trailing usage-only chunk before `[DONE]` when the upstream stays silent, so metered chat clients see totals instead of nothing ([#12828](https://github.com/diegosouzapw/OmniRoute/pull/12828)) — thanks @maxmad64bis +- **fix(db):** provider stats stay truthful on empty and legacy databases: fallback counts default to `0` instead of `null`, latency averages read `null` (not `0`) when no durations were recorded, failures logged before the error-type column existed group under `pre_migration` instead of `unclassified`, and per-provider queries use two new composite indexes ([#12832](https://github.com/diegosouzapw/OmniRoute/pull/12832)) — thanks @maxmad64bis +- fix(nvidia): fail open when a synced model catalog goes stale instead of gating forever (#12849) ([#13248](https://github.com/diegosouzapw/OmniRoute/pull/13248)) +- **fix(db):** WAL maintenance lives in its own module: a `TRUNCATE` checkpoint that hits a busy database now warns with its streak and retries once via `PASSIVE` instead of logging success, and closing-time checkpoints no longer report success on builds without a database file, with the busy totals visible in the authenticated monitoring health payload ([#12853](https://github.com/diegosouzapw/OmniRoute/pull/12853)) — thanks @maxmad64bis +- **fix(health):** quota with no snapshots reads empty instead of a contradicting 0% ([#12857](https://github.com/diegosouzapw/OmniRoute/pull/12857)) — thanks @maxmad64bis +- fix(a2a): accept the dashboard's own session cookie on /a2a so "Run message/send" no longer fails with "Unauthorized: missing or invalid API key" (#12888) ([#13271](https://github.com/diegosouzapw/OmniRoute/pull/13271)) +- **fix(logs):** the Logs grid's in-memory filter pass no longer discards rows the SQL query already matched — selecting an API key from the dropdown (which sends the key's id) returns its calls again, the Combo tab shows every combo instead of only those whose name contains a "1", and the model filter and search cover the same columns as the query ([#12896](https://github.com/diegosouzapw/OmniRoute/pull/12896)) — fixes [#12873](https://github.com/diegosouzapw/OmniRoute/issues/12873) — thanks @ntdatt812 +- **fix(a2a):** `/api/a2a/status` now builds the agent card from the request that asked for it, so a gateway reached at a non-localhost host no longer advertises `http://localhost:20128` as its A2A URL ([#12918](https://github.com/diegosouzapw/OmniRoute/pull/12918)). — thanks @ntdatt812 +- **fix(compression):** progressive aging now appends its `[COMPRESSED:aging:…]` annotation after a turn's `tool_result` blocks instead of in front of them, so Anthropic no longer rejects aged conversations with "`tool_use` ids were found without `tool_result` blocks immediately after" ([#12920](https://github.com/diegosouzapw/OmniRoute/pull/12920)). — thanks @ntdatt812 +- **fix(bedrock):** model import now resolves context limits for every vendor prefix instead of only `anthropic.*`, so `global.openai.gpt-5.6-*` no longer imports with a null `inputTokenLimit` and gets rejected pre-flight at the 200k default ([#12921](https://github.com/diegosouzapw/OmniRoute/pull/12921)). — thanks @ntdatt812 +- **fix(stream):** the 64 KB stream buffer GLM asks for is honoured instead of dropped, and the type error it caused no longer fails the API Route Typecheck gate on every open PR ([#12925](https://github.com/diegosouzapw/OmniRoute/pull/12925)) — thanks @ntdatt812 +- **fix(guardrails):** mask PII inside a `tool_result`'s nested content array, which the masker walked past while redacting its sibling block ([#12930](https://github.com/diegosouzapw/OmniRoute/pull/12930)) — thanks @ntdatt812 +- **fix(dashboard):** the Radar catalog table no longer leaves absent data unexplained — empty limits, unknown context windows, and unreported capabilities each explain themselves on hover, and a new check keeps it that way ([#12937](https://github.com/diegosouzapw/OmniRoute/pull/12937)) — thanks @maxmad64bis +- **fix(sse):** geo-blocked opencode requests rotate to the next account proxy instead of failing, so one refused egress no longer aborts the whole chain ([#12941](https://github.com/diegosouzapw/OmniRoute/pull/12941)) — thanks @maxmad64bis +- **fix(codex):** dashboard "clear cooldown" and the CAS recovery path now drop nested `codexScopeRateLimitedUntil` maps in the same write that nulls `rate_limited_until`, and fresh quota snapshots with headroom lift fallback-sourced scope cooldowns parked by quota preflight ([#12817](https://github.com/diegosouzapw/OmniRoute/issues/12817), [#12860](https://github.com/diegosouzapw/OmniRoute/issues/12860), [#12951](https://github.com/diegosouzapw/OmniRoute/pull/12951)) The dashboard reset-credit button (`consumeCodexResetCredit`) now exercises that same snapshot path after a successful redeem, so a filled quota bar is enough to unpark a leftover fallback Codex child without another manual clear. — thanks @HouMinXi +- **fix(db):** resolve `sql-wasm.wasm` across global npm install and hoisted layouts, ensuring OmniRoute can boot cleanly on Node 24 when native `better-sqlite3` is uncompiled. ([#13035](https://github.com/diegosouzapw/OmniRoute/pull/13035)) — thanks @HouMinXi +- **fix(api):** `DELETE /v1/batches/delete-completed` now sweeps only the calling API key's own completed batches (batches with no owner stay out of a key-scoped sweep on purpose), with an explicit instance-wide mode reserved for authenticated dashboard sessions, a 401 for a presented key that is unknown, revoked, deactivated, banned or expired (never falling through to the session branch), audit logging of every sweep, a sanitized 500 on failure, an owner-scoped file half (a key-scoped sweep never nulls a file another tenant owns) and an atomic sweep — chunked in 200-batch transactions in instance mode — so a mid-way error never leaves a batch pointing at a nulled file (GHSA-wvxc-jp3v-5mg5) ([#12969](https://github.com/diegosouzapw/OmniRoute/pull/12969)) +- **fix(combo):** quota-weighted routing stops drawing on an out-of-credit connection — a 402 now invalidates the stored quota snapshot instead of leaving its stale remaining percentage in place, and a snapshot older than 10 minutes no longer counts as confident headroom for the primary pool ([#12972](https://github.com/diegosouzapw/OmniRoute/pull/12972)) ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- **fix(vision):** Custom Models with "Vision capable" checked no longer have image requests swapped to `glm/glm-4.6v` when the client sends the advertised alias (`vllm/path/...`) or the bare path-shaped id — Vision Bridge now matches the stored override for all three id forms ([#12758](https://github.com/diegosouzapw/OmniRoute/issues/12758)) ([#12974](https://github.com/diegosouzapw/OmniRoute/pull/12974)) — thanks @HouMinXi +- **fix(sse):** transient opencode upstream failures rotate to the next account proxy instead of failing, so one flapping egress no longer aborts the whole chain ([#12975](https://github.com/diegosouzapw/OmniRoute/pull/12975)) — thanks @maxmad64bis +- **fix(azure):** Deployments from GPT-6 onward now send `max_completion_tokens` instead of `max_tokens`, which Azure rejects with HTTP 400. The rule matched a literal `gpt-5`, so each new generation arrived broken; it now matches the generation range, while `gpt-35-turbo` still keeps `max_tokens`. ([#13007](https://github.com/diegosouzapw/OmniRoute/pull/13007)) — thanks @ntdatt812 +- **fix(db):** add an opt-in self-restart circuit for sustained critical memory pressure, gate post-cleanup VACUUM behind a minimum freed-rows threshold, and checkpoint the SQLite WAL every 5 minutes with a size guard that escalates to TRUNCATE, so a growing WAL can no longer stall the event loop into a full outage. ([#13011](https://github.com/diegosouzapw/OmniRoute/pull/13011)) — thanks @HouMinXi +- **fix(auth):** an explicit connection pin may probe a quota-disabled row once and re-enable it on success ([#12874](https://github.com/diegosouzapw/OmniRoute/issues/12874)) ([#13017](https://github.com/diegosouzapw/OmniRoute/pull/13017)) — thanks @HouMinXi +- **fix(api):** thread `X-OmniRoute-Fallback-Attempts` through combo chat completions so streaming and non-streaming responses report how many prior legs were attempted ([#13038](https://github.com/diegosouzapw/OmniRoute/pull/13038)) — thanks @HouMinXi +- Restore provider failure classification and credential refresh on non-streaming requests: classify non-2xx failures to lock models on per-model quota exhaustion, update connection rate limits from headers and body, and pass credential refresh handlers to pipeline execution so 401 tokens can be refreshed and retried (#13043). ([#13069](https://github.com/diegosouzapw/OmniRoute/pull/13069)) — thanks @HouMinXi +- **fix(responses):** wrap forced-non-streaming web_search fallback JSON as Responses SSE so Codex still sees `response.completed` ([#13050](https://github.com/diegosouzapw/OmniRoute/pull/13050)) — thanks @HouMinXi +- **fix(antigravity):** Preserve upstream thought-token usage in normalized completion and reasoning token counts ([#13055](https://github.com/diegosouzapw/OmniRoute/pull/13055)) — thanks @pacocartones +- Fix a concurrency-slot leak in the DeepSeek PoW solver: a worker that failed to spawn (for example a missing worker script) never released its slot, so `MAX_CONCURRENT_WORKERS` failures disabled the solver until restart. ([#13097](https://github.com/diegosouzapw/OmniRoute/pull/13097)) — thanks @anhtahaylove +- **fix(acp):** bound the ACP session output buffers — `stdoutBuffer` and `stderrBuffer` now cap at 1 MiB keeping the most recent output behind a visible `[...output truncated...]` marker, and `stderrBuffer` is reset per prompt instead of accumulating for the lifetime of the session. ([#13100](https://github.com/diegosouzapw/OmniRoute/pull/13100)) — thanks @anhtahaylove +- **fix(acp):** release the `stdout`/`exit` listeners and the idle timer that a `sendPrompt` timeout used to leave attached to the `acpManager` singleton, and drop sessions that exited on their own from the session map instead of keeping them forever. ([#13096](https://github.com/diegosouzapw/OmniRoute/pull/13096)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection and PII scanners now read the text a `tool_result` block carries on `content` (string or nested block list), in messages and in system blocks, so tool output is judged by the same rules as user text ([#13101](https://github.com/diegosouzapw/OmniRoute/pull/13101)) — thanks @ntdatt812 +- **fix(gamification):** close the badge notification SSE stream when the request signal is already aborted before the stream starts — a client that disconnects while the route is still awaiting auth used to leave both the 2s unlock poll and the 15s heartbeat running for the lifetime of the process. ([#13106](https://github.com/diegosouzapw/OmniRoute/pull/13106)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection scan now spends its 16 KB budget on both ends of the request instead of the first 16 KB only, so `system`, `instructions`, `query`, `documents` and the newest turns are no longer hidden behind one long message ([#13104](https://github.com/diegosouzapw/OmniRoute/pull/13104)) — thanks @ntdatt812 +- **fix(dashboard):** expose the Volcano Ark console cookie on quota scraping and unwrap connect-error objects so the dashboard shows the upstream message ([#13107](https://github.com/diegosouzapw/OmniRoute/pull/13107)) — thanks @HouMinXi +- **fix(db):** release the `beforeExit`/`SIGINT`/`SIGTERM` handlers when a `node:sqlite` adapter closes, so a closed adapter and its database handle are no longer pinned to `process` for the lifetime of the run — the same treatment #7494 gave the sql.js adapter. ([#13109](https://github.com/diegosouzapw/OmniRoute/pull/13109)) — thanks @anhtahaylove +- **fix(translator):** `contentSchema` and `unevaluatedItems` are now treated as subschema positions by the tool-schema sanitizer, so a truncation placeholder in either is replaced with a permissive schema instead of being forwarded as a string ([#13110](https://github.com/diegosouzapw/OmniRoute/pull/13110)) — thanks @ntdatt812 +- **fix(cli-helper):** clear the `createLogStream` timeout on the abort path — `stop()` aborts the in-flight fetch and returned through the `signal.aborted` branch, which skipped `clearTimeout` and left an armed timer per stopped stream. The stream reader is now also cancelled when the read loop exits early. ([#13114](https://github.com/diegosouzapw/OmniRoute/pull/13114)) — thanks @anhtahaylove +- **fix(docs):** drop TLS-impersonation, MITM-decrypt, supply-chain attestation, and XOR-mask writeups from the public `/docs` catalog and Docker image. Files stay in git for engineers; operators who need them open the repo, not the website. ([#13136](https://github.com/diegosouzapw/OmniRoute/pull/13136)) — thanks @HouMinXi +- **fix(combo):** parse numeric-epoch `rate_limited_until` in the combo cooldown read path ([#13141](https://github.com/diegosouzapw/OmniRoute/pull/13141)) — thanks @maxmad64bis +- **fix(opencode):** both OpenCode plugins now reject a gateway address typed without `http://` at configuration time, instead of publishing every model with an api url no client can call, and the v2 plugin no longer publishes a model card whose api url is blank or relative ([#13142](https://github.com/diegosouzapw/OmniRoute/pull/13142)) — thanks @maxmad64bis +- **fix(providers):** lock opencode model on upstream 400 model-unavailable ([#13146](https://github.com/diegosouzapw/OmniRoute/pull/13146)) — thanks @maxmad64bis +- **fix(logging):** keep the provider exchange rather than the raw client bodies when a call log exceeds its size budget, and show that recovered payload in the request-detail panel instead of replacing it with the stored response body ([#13147](https://github.com/diegosouzapw/OmniRoute/pull/13147)) — thanks @maxmad64bis +- **fix(traffic-inspector):** the WebSocket route no longer leaks a traffic-buffer subscriber and a 30s ping timer when the client socket is already closed at handler time — listeners are attached before any resource is acquired, a destroyed socket bails out early, and the ping interval stops on a dead socket where `write()` never throws ([#13155](https://github.com/diegosouzapw/OmniRoute/pull/13155)) — thanks @anhtahaylove +- **fix(telegram):** bound the per-user API key cache in the Telegram chat proxy so a burst of distinct chat ids can no longer grow the process heap without limit ([#13165](https://github.com/diegosouzapw/OmniRoute/issues/13165)) ([#13166](https://github.com/diegosouzapw/OmniRoute/pull/13166)) — thanks @anhtahaylove +- **fix(stream):** cancel the upstream response body when the JSON-to-SSE sniff unwinds on a body timeout, so a stalled upstream no longer pins the connection ([#13169](https://github.com/diegosouzapw/OmniRoute/issues/13169)) ([#13171](https://github.com/diegosouzapw/OmniRoute/pull/13171)) — thanks @anhtahaylove +- **fix(telegram):** authenticate webhook deliveries with Telegram's `secret_token` so an unauthenticated caller can no longer mint API keys or spend upstream quota ([#13172](https://github.com/diegosouzapw/OmniRoute/issues/13172)) ([#13175](https://github.com/diegosouzapw/OmniRoute/pull/13175)) — thanks @anhtahaylove +- **fix(models):** give discoverable Gemini 3.8 Flash ids their own 65536 output spec so Antigravity no longer clamps them to 16384 ([#13195](https://github.com/diegosouzapw/OmniRoute/pull/13195)) — thanks @HouMinXi +- **fix(dashboard):** leftover catalog-removed provider rows (gemini-cli) can be listed and purged from the providers page ([#13067](https://github.com/diegosouzapw/OmniRoute/issues/13067)) ([#13197](https://github.com/diegosouzapw/OmniRoute/pull/13197)) — thanks @HouMinXi +- **fix(auth):** a dashboard session now requires the `authenticated: true` claim that login, OIDC and the session refresh already emit — a JWT merely signed with `JWT_SECRET` (for example the Cursor CLI passthrough token, which any API-key holder can obtain) no longer verifies as the `auth_token` cookie on any route, the WebSocket handshake or the live server; existing sessions keep working ([#13298](https://github.com/diegosouzapw/OmniRoute/issues/13298)) ([#13375](https://github.com/diegosouzapw/OmniRoute/pull/13375)) +- **fix(api):** `DELETE /v1/batches/delete-completed` now applies the caller's API-key policy (endpoint allowlist, schedule, usage cap, rate limit) like every other `/v1` route, commits the key-scoped sweep per 200-batch chunk instead of holding one write lock for the whole sweep, refuses to loop without progress, and rejects a scope that names both a key and `allTenants` ([#13684](https://github.com/diegosouzapw/OmniRoute/pull/13684)) +- Restore the API-route typecheck gate: the non-streaming leg result lost its discriminated-union narrowing after the server-owned tool loop reassignment, producing 13 new TS2339 diagnostics in `chatCore.ts`. ([#12963](https://github.com/diegosouzapw/OmniRoute/pull/12963)) +- Restore four non-streaming behaviours the server-owned tool loop refactor dropped: client aborts map to 499 with the fixed `Request aborted` message, an aborted request no longer logs a synthetic `clientResponse`, Claude prompt-cache telemetry is recorded again, and a body that cannot be canonicalized no longer throws when the tool loop is off. ([#12990](https://github.com/diegosouzapw/OmniRoute/pull/12990)) +- **fix(skills):** The CLI registry parser now reads positionals declared with `.addArgument()`, not only those written inline in `.command()`. `tunnel create [type]` was being published as `tunnel create`, so the agent-skills sync gate reported drift on every branch and regenerating would have deleted the argument. ([#13009](https://github.com/diegosouzapw/OmniRoute/pull/13009)) — thanks @ntdatt812 +- **fix(combos):** dashboard combo test uses a short prompt, serial probes, and a 60s timeout so reasoning models and rate-limited free pools do not fail the health check ([#13001](https://github.com/diegosouzapw/OmniRoute/pull/13001)) — thanks @HouMinXi +- fix(compression): terminate idle worker threads on eviction so long-running instances stop leaking OS threads and MessagePorts ([#13091](https://github.com/diegosouzapw/OmniRoute/pull/13091)) — thanks @anhtahaylove +- Redact Google API keys of any length in error bodies: the pattern required exactly 39 characters, so shorter or longer `AIza…` credentials passed through unredacted. ([#12964](https://github.com/diegosouzapw/OmniRoute/pull/12964)) +- **fix(grok-cli):** a 402 "Grok Build usage balance exhausted" parks that Grok login as out of credit (Grok Build CLI, grok.com cookie, and xAI OAuth share the weekly pool). Combo routing then tries the next login instead of locking the model for every account in the pool ([#13061](https://github.com/diegosouzapw/OmniRoute/pull/13061)) — thanks @HouMinXi +- fix(compression): spawn the LLMLingua worker with a file URL object so compression actually runs instead of silently failing open on Node ([#13093](https://github.com/diegosouzapw/OmniRoute/pull/13093)) — thanks @anhtahaylove +- **fix(test):** run the local `test` and `test:unit` scripts at concurrency 4 so a full-suite run no longer exhausts the machine's commit charge and kills unrelated processes ([#13187](https://github.com/diegosouzapw/OmniRoute/pull/13187)) — thanks @anhtahaylove +- **fix(monitoring):** `GET /api/monitoring/health` `credentialHealth` now includes a bounded `failedConnections` list (`connectionId`, `status`, sanitized `lastError`) when the probe-cache gauge `failed>0`, plus `source: probe-cache` and a cheap `staleDbNonOkCount` for sticky SQLite `test_status` on active rows. Documents that the live gauge is not `provider_connections.test_status`. ([#12876](https://github.com/diegosouzapw/OmniRoute/pull/12876)) — thanks @RaviTharuma +- **fix(resilience):** an openai-compatible multi-upstream gateway no longer marks the whole connection `credits_exhausted` when a single upstream (or the `/models` / representative-model health probe) returns 402. Quota failures stay model-scoped; true connection-wide auth failures are unchanged. Single-credential 402 key disable ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239)) is preserved. ([#12875](https://github.com/diegosouzapw/OmniRoute/pull/12875)) — thanks @RaviTharuma +- **fix(dashboard):** nine fixes on the Orchestration Canvas, all diagnosed in the Phase 2 reviews. A source that fails now keeps the timestamp of its FIRST failure instead of being re-stamped every poll — the stale line said "since the last poll" no matter how long the source had been down, and the churn also defeated the snapshot's stable identity (it serializes the source list), so the canvas re-rendered on every tick while anything was broken. A source that HAD data and only then started failing is flagged too: previously only a source with no node at all got the warning, so a source that went down mid-session kept a healthy-looking node forever. The rest are pointwise: clicking a filter chip cancels the pending search debounce (left armed, it fired ~300ms later and silently reverted the chip); the search input carries an accessible name; `?state=running, failed` parses like the unpadded form instead of dropping the padded value; the CSV toggle helper is defined once in `model/urlParams.ts` rather than twice; the agents tab tells "nothing running" apart from "the filter matched nothing", offering a clear-filters button instead of setup links that would be wrong advice there; edges stop emitting SMIL particles above 40 simultaneously active edges, keeping the colored stroke; and the drawer's error banner clears when a retried action succeeds. ([#12983](https://github.com/diegosouzapw/OmniRoute/pull/12983)) +- **fix(i18n):** the nine locales added with the EU-language batch (Greek, Estonian, Irish, Croatian, Lithuanian, Latvian, Maltese, Slovenian, Serbian) were missing the eleven Orchestration Canvas keys that Phase 3 introduced, so the compare-runs panel and the "no runs match these filters" empty state fell back to English in those languages (`deepMergeFallback` substitutes English for an absent key, so nothing rendered blank — it rendered untranslated). The coverage gate does not catch this: it enforces an 80% floor per locale, and eleven missing keys out of ~13,000 leaves coverage at 99.9%. Translated for real in each language, calibrated against the wording each file already uses for "run", "filter" and "skill". ([#13294](https://github.com/diegosouzapw/OmniRoute/pull/13294)) +- **fix(dashboard):** five follow-ups from the Phase 2 Orchestration Canvas review. The History drawer now shows the memory section for runs that already left the live TTL window: the persisted `memory_hits` event is parsed into `metadata.memoryHits` with the same defensive validation the drawer applies, and — because that event is observability rather than a state transition — it no longer leaks into the timeline, where it had been inheriting the task's state and rendering as a duplicate transition. The A2A memory recall runs against its own 1.5s deadline instead of inheriting the memory backend's 30s one; overshooting degrades exactly like any other recall failure (no hits, task proceeds), and the timer is cleared on both paths. Repeating a Conductor run carries its `requirements.cli`/`requirements.model` forward, so the new run is pinned to the same runner profile and model rather than drifting to whatever the fleet picks. A successful repeat from the Agents tab now focuses the run it created, instead of leaving the operator on the finished one. And the auth test for `POST /api/conductor/tasks` moved into the shared `ROUTES` array rather than restating the pattern. ([#12988](https://github.com/diegosouzapw/OmniRoute/pull/12988)) +- fix(plugins): stop leaking an exit listener per plugin hook timeout, which triggered MaxListenersExceededWarning on plugins that ignore SIGTERM ([#13092](https://github.com/diegosouzapw/OmniRoute/pull/13092)) — thanks @anhtahaylove +- **fix(validation):** Provider node edits no longer fail with a generic "Invalid request" when the optional daily-quota reset fields are left blank. The dashboard sends `dailyQuotaResetTimezone` and `dailyQuotaResetHour` as `null`, and only the hour accepted it. ([#13066](https://github.com/diegosouzapw/OmniRoute/issues/13066)) ([#13083](https://github.com/diegosouzapw/OmniRoute/pull/13083)) — thanks @ntdatt812 +- **fix(combo):** quota-aware expansion drops banned, inactive, missing, and wrong-provider connections before quota fetch or model dispatch; pins and allowlists stay selectors, not a bypass. Antigravity automatic exhaustion now requires a reported zero remaining, so a positive balance below 1% stays eligible. ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- Fix an intermittent failure in the quota-weighted routing test suite: scores are a function of `Date.now()`, so peers with identical quota scored microseconds apart never tied and swapped order. ([#12946](https://github.com/diegosouzapw/OmniRoute/pull/12946)) +- fix(volcengine): resolve quota window mapping gaps, safe multi-connection console binding, and response cookie sanitization ([#12950](https://github.com/diegosouzapw/OmniRoute/pull/12950)) — thanks @HouMinXi +- **fix(test):** resolve the WebDAV handler path with `fileURLToPath` so the suite's 37 WebDAV tests run on Windows instead of failing with a doubled `C:\C:\` drive prefix ([#13196](https://github.com/diegosouzapw/OmniRoute/pull/13196)) — thanks @anhtahaylove +- **fix(ui):** fall back from invalid AnySearch icon alias (direct commit `9587e07b69`) +- **fix(sse):** stop rebuilding a truncated summary from the collector's cap-dropped event array ([#12718](https://github.com/diegosouzapw/OmniRoute/pull/12718)) — thanks @hartmark +- **fix(conversations):** show a pending spinner for unresolved tool nodes instead of "(empty)" ([#12727](https://github.com/diegosouzapw/OmniRoute/pull/12727)) — thanks @hartmark +- **fix(streaming):** fail fast when an upstream stream produces only lifecycle/heartbeat events, never real content ([#12741](https://github.com/diegosouzapw/OmniRoute/pull/12741)) — thanks @hartmark +- **fix(responses-continuation):** bridge the write-in-flight window with an in-memory pending store ([#12854](https://github.com/diegosouzapw/OmniRoute/pull/12854)) — thanks @hartmark +- **fix(translator):** drop or map agent_message on Chat Completions fallback ([#12880](https://github.com/diegosouzapw/OmniRoute/pull/12880)) — thanks @RaviTharuma +- **fix(api):** page and stream-complete GET /v1/models for large catalogs ([#12882](https://github.com/diegosouzapw/OmniRoute/pull/12882)) — thanks @RaviTharuma +- **fix(quota):** align AUTH window labels with usage API and clarify 503 ([#12884](https://github.com/diegosouzapw/OmniRoute/pull/12884)) — thanks @RaviTharuma +- **fix(ci):** clear two base-reds on release/v3.8.51 — mutation-coverage gate + image-only-model guard ([#12945](https://github.com/diegosouzapw/OmniRoute/pull/12945)) +- **fix(api):** scope batch bulk-delete to the calling API key ([#13211](https://github.com/diegosouzapw/OmniRoute/pull/13211)) +- **fix(tests):** align model-sync log assertion to shipped path redaction (base-red) ([#13227](https://github.com/diegosouzapw/OmniRoute/pull/13227)) +- **fix(test):** make the ToS heading guard actually require the parentheses ([#13228](https://github.com/diegosouzapw/OmniRoute/pull/13228)) +- **fix(quality):** register 4 drifted covering tests in stryker tap.testFiles ([#13229](https://github.com/diegosouzapw/OmniRoute/pull/13229)) +- **fix(logging):** mask *-api-key headers in request-log pipeline (#13273) ([#13401](https://github.com/diegosouzapw/OmniRoute/pull/13401)) — thanks @KooshaPari +- **fix(tests):** correct stale Jina catalog prefix in custom-models test ([#13403](https://github.com/diegosouzapw/OmniRoute/pull/13403)) — thanks @KooshaPari +- **fix(backend):** prune old backups after health-check-repair snapshots (#13308) ([#13404](https://github.com/diegosouzapw/OmniRoute/pull/13404)) — thanks @KooshaPari +- **fix(backend):** guard memory_fts_au trigger with WHEN clause to stop FTS bloat (#13326) ([#13405](https://github.com/diegosouzapw/OmniRoute/pull/13405)) — thanks @KooshaPari +- **fix(free-tiers):** remove obsolete Together $25 signup credit (#12526) ([#13407](https://github.com/diegosouzapw/OmniRoute/pull/13407)) — thanks @KooshaPari +- **fix:** skip non-chat generation models in Test all models ([#13409](https://github.com/diegosouzapw/OmniRoute/pull/13409)) — thanks @KooshaPari +- **fix(providers):** de-duplicate agy and antigravity model catalogs ([#13410](https://github.com/diegosouzapw/OmniRoute/pull/13410)) — thanks @KooshaPari +- **fix(dashboard):** send provider in body for mini-playground web search ([#13411](https://github.com/diegosouzapw/OmniRoute/pull/13411)) — thanks @KooshaPari +- **fix(resilience):** add CJK quota-exhaustion patterns to 429 classifier ([#13412](https://github.com/diegosouzapw/OmniRoute/pull/13412)) — thanks @KooshaPari +- **fix(backend):** eval runner: failed calls no longer score as passed + dotAll regex ([#13413](https://github.com/diegosouzapw/OmniRoute/pull/13413)) — thanks @KooshaPari +- **fix(backend):** eval runner opts out of output-style and memory injection ([#13414](https://github.com/diegosouzapw/OmniRoute/pull/13414)) — thanks @KooshaPari +- **fix:** chain serial quarantine step in npm test script (fixes #13286) ([#13418](https://github.com/diegosouzapw/OmniRoute/pull/13418)) — thanks @KooshaPari +- **fix(antigravity):** derive stable session ID from credentials for prompt-cache reuse (#13305) ([#13424](https://github.com/diegosouzapw/OmniRoute/pull/13424)) — thanks @KooshaPari +- **fix(memory):** merge memory into top-level system field instead of unshifting at messages[0] (#13425) ([#13427](https://github.com/diegosouzapw/OmniRoute/pull/13427)) — thanks @KooshaPari +- **fix(compression):** preserve instruction blocks from lossy engines ([#13523](https://github.com/diegosouzapw/OmniRoute/pull/13523)) — thanks @KooshaPari +- **fix(compression):** preserve polarity words and newlines in ultra heuristic ([#13524](https://github.com/diegosouzapw/OmniRoute/pull/13524)) — thanks @KooshaPari +- **fix(compression):** run user preservePatterns before built-in patterns (#13457) ([#13525](https://github.com/diegosouzapw/OmniRoute/pull/13525)) — thanks @KooshaPari +- **fix(reasoning):** expand deepseek-v4 regex to match v4.1+ and add command-code provider (#13430) ([#13528](https://github.com/diegosouzapw/OmniRoute/pull/13528)) — thanks @KooshaPari +- **fix(providers):** add alwaysReasons metadata for reka-flash-3 ([#13531](https://github.com/diegosouzapw/OmniRoute/pull/13531)) — thanks @KooshaPari +- **fix(playground):** allow Compare columns to scroll independently ([#13532](https://github.com/diegosouzapw/OmniRoute/pull/13532)) — thanks @KooshaPari +- **fix(security):** empty whitelist allows through to prevent admin lockout ([#13534](https://github.com/diegosouzapw/OmniRoute/pull/13534)) — thanks @KooshaPari +- **fix(tests):** add DISABLE_IOREG_STRATEGY env var for macOS test compatibility (fixes #13467) ([#13539](https://github.com/diegosouzapw/OmniRoute/pull/13539)) — thanks @KooshaPari +- **fix(zed-hosted):** map developer role and Gemini enums for Zed proxy ([#13543](https://github.com/diegosouzapw/OmniRoute/pull/13543)) — thanks @KooshaPari +- **fix(analytics):** normalize provider aliases to canonical id in usage_history ([#13545](https://github.com/diegosouzapw/OmniRoute/pull/13545)) — thanks @KooshaPari +- **fix(combo):** use traceId for call log id to prevent UNIQUE constraint failure ([#13546](https://github.com/diegosouzapw/OmniRoute/pull/13546)) — thanks @KooshaPari +- **fix(auth):** check model lockout before returning synthetic noauth connection ([#13547](https://github.com/diegosouzapw/OmniRoute/pull/13547)) — thanks @KooshaPari +- **fix(guardrails):** skip credential redaction for base64 image data URLs ([#13550](https://github.com/diegosouzapw/OmniRoute/pull/13550)) — thanks @KooshaPari +- **fix(resilience):** increase requestQueue.maxWaitMs default from 15s to 30s ([#13553](https://github.com/diegosouzapw/OmniRoute/pull/13553)) — thanks @KooshaPari +- **fix(docs):** document OMNIROUTE_READY_TIMEOUT_MS and allowlist the test-only DISABLE_IOREG_STRATEGY ([#13692](https://github.com/diegosouzapw/OmniRoute/pull/13692)) + ### 📝 Maintenance ---- +- **docs(readme):** reconcile live v3.8.50 provider, free-tier, CLI, routing, test, community, sponsor, acknowledgment, and SVG metrics with their audited source denominators, including a deduplicated OmniRoute-in-Action snapshot and distinct contributor rankings for merged pull requests, GitHub-attributed commits, and Git history ([#11356](https://github.com/diegosouzapw/OmniRoute/pull/11356)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **docs:** sync the canonical provider count 354 → 356 across `README.md`, `AGENTS.md`, `llm.txt` (+ 42 i18n mirrors), the four README SVG diagrams, `docs/reference/PROVIDER_REFERENCE.md` (regenerated) and the `package.json` description after Opper (#11629) and 1min.ai (#11631) boarded the catalog — closes the `check:docs-counts-sync` strict drifts that kept `release/v3.8.51` red ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11673](https://github.com/diegosouzapw/OmniRoute/pull/11673)) — thanks @jonlwheat2-gif +- **test(providers):** update count-derived assertions after the v3.8.51 provider additions — `APIKEY_PROVIDERS` 233 → 235 (Opper #11629 + 1min.ai #11631), reserved-prefix REGISTRY walk 395 → 398, `WEB_FETCH_PROVIDERS` now includes `nimble-search` (#11620), and the provider translate-path golden snapshot regenerated ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11674](https://github.com/diegosouzapw/OmniRoute/pull/11674)) — thanks @jonlwheat2-gif +- **test(sse):** bump the hard-lease connection-query inventory for `src/lib/tokenHealthCheck.ts` to 2 — the verify-only web-cookie sweep added by #11495 split the single `getProviderConnections` call into oauth + cookie variants, which the frozen inventory had not tracked ([#11495](https://github.com/diegosouzapw/OmniRoute/pull/11495)). ([#11677](https://github.com/diegosouzapw/OmniRoute/pull/11677)) — thanks @jonlwheat2-gif / @oyi77 +- **chore(release):** clear the `release/v3.8.51` base-red gates — docs migration count (+38 i18n mirrors), stryker `tap.testFiles`, lockfile registry host, stale ESLint suppressions, the 7 lint regressions they were hiding, the stale `APIKEY_PROVIDERS` partition count (231 → 233), the `glm-5.3-max` tier missing from the GLM test table, a dead `modelType === "chat"` comparison in the models catalog, and the `resolveLiveWsUrl` / `sanitizeLiveWsPort` helpers from #11388 that a later merge dropped from `wsPath.ts` (dashboard live WebSocket URL resolution) ([#11502](https://github.com/diegosouzapw/OmniRoute/pull/11502)) — thanks @MumuTW +- **chore(free-models):** the free-tier regime table now declares which totals figure each regime feeds and whether it may take the no-auth shortcut, so the sets that used to repeat those answers by hand are derived from it — a new `freeType` no longer compiles until it has answered every question, instead of silently contributing to no total ([#11537](https://github.com/diegosouzapw/OmniRoute/pull/11537)) — thanks @maxmad64bis +- **docs(routing):** LKGP strategy copy now states provider-level stickiness and the explicit rules fallback instead of promising per-target pinning ([#11555](https://github.com/diegosouzapw/OmniRoute/pull/11555)) — thanks @maxmad64bis +- **test(chatcore):** pin the response tool-name alias resolution against the ordering hazard that broke every Gemini/Antigravity MCP tool call in v3.8.49. `extractRequestToolIdentityMap` deletes `translatedBody._toolNameMap`, so the later read is always undefined and the ledger survives only through the `requestToolIdentityMap` fallback — removing that fallback previously left the entire tool-name suite green. The resolution moves into `resolveResponseToolNameMap()` next to the map it depends on, with a regression guard that fails without the recovery. No behaviour change. ([#11575](https://github.com/diegosouzapw/OmniRoute/pull/11575)) — thanks @amartinawi +- **docs(env):** document `OMNIROUTE_CORPUS_CACHE_SIZE` in `.env.example` and `ENVIRONMENT.md`, so the env/docs contract check stops failing Docs Gates on every branch ([#11576](https://github.com/diegosouzapw/OmniRoute/pull/11576)) — thanks @ntdatt812 +- **chore(stryker):** register `route-guard-tunnel-processes-local-only` and `universal-quota-aware-routing` in `tap.testFiles`, so their mutant kills count for the three modules they cover ([#11577](https://github.com/diegosouzapw/OmniRoute/pull/11577)) — thanks @ntdatt812 +- **test(authz):** pin `LOCAL_ONLY_API_GET_EXEMPTIONS` by exact membership instead of by entry count, so the guard names the offending path and also catches a substitution ([#11580](https://github.com/diegosouzapw/OmniRoute/pull/11580)) — thanks @ntdatt812 +- **test(build):** pin `resolveNextBuildBundlerFlag` to the `OMNIROUTE_USE_TURBOPACK` contract it implements, across runtimes, instead of asserting an unimplemented Bun override ([#11583](https://github.com/diegosouzapw/OmniRoute/pull/11583)) — thanks @ntdatt812 +- **test(claude-web):** the Claude Web transport test's barrel anchors no longer pin the exact construction spelling. #11421 made the executor map lazy, so `/"claude-web": new ClaudeWebExecutor\(\)/` stopped matching and the test went red on `release/v3.8.51` itself — a base-red gate every open PR inherited. The anchors now match the alias key and the class name and tolerate either spelling in between, and were checked to still fail when an alias is genuinely repointed at another executor ([#11585](https://github.com/diegosouzapw/OmniRoute/pull/11585)) — thanks @ntdat812 +- **test(models):** pin `qwen3.8-max` resolution per provider — two of the four catalogs now serve the bare id and no longer carry `-preview` ([#11590](https://github.com/diegosouzapw/OmniRoute/pull/11590)) — thanks @ntdatt812 +- **test(executors):** drop the stale 128-tool truncation assertion that contradicted the #11444 pass-through contract ([#11591](https://github.com/diegosouzapw/OmniRoute/pull/11591)) — thanks @ntdatt812 +- **test(token-health):** pin the current terminal-skip boundary — expired with retry budget is probed, `account_deactivated` stays skipped ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)) — thanks @ntdatt812 +- **test(models):** follow #11503's retarget of the `gemini-3-pro-high` alias to the hyphenated catalog id ([#11593](https://github.com/diegosouzapw/OmniRoute/pull/11593)) — thanks @ntdatt812 +- **refactor(video bridge):** extract per-video acquisition, whole-result caching, description, and metrics/abort/cleanup out of `VideoBridgeGuardrail.preCall` into a `processVideoPart` seam in a new `videoBridgePipeline.ts`, behind explicit `VideoMediaBrokerPort`, `VideoAudioTranscriptionPort`, and `VideoDrilldownPort` boundaries; `preCall` now only handles request traversal, policy, and response aggregation. The Video tab's FFmpeg/ffprobe runtime status is now an explicit `unknown` / `restricted` / `unavailable` / `available` state instead of a nullable boolean pair, fixing a case where an in-flight or failed probe was mislabeled as "install FFmpeg" ([#11657](https://github.com/diegosouzapw/OmniRoute/issues/11657)). ([#12016](https://github.com/diegosouzapw/OmniRoute/pull/12016)) +- **test(opencode-plugin):** add unit test coverage for `formatFreeBudget()` naming helper ([#11660](https://github.com/diegosouzapw/OmniRoute/pull/11660)) — thanks @f9td56dbgh-hub +- **docs(video):** clarify that the Video Bridge transcript `source` field (`client`, `embedded`, `audio-bridge`) is presently caller-declared and not yet server-verified — OmniRoute enforces the enum shape but does not cryptographically confirm that an `embedded`/`audio-bridge` label came from a server-owned extraction ([#11661](https://github.com/diegosouzapw/OmniRoute/issues/11661)). ([#12001](https://github.com/diegosouzapw/OmniRoute/pull/12001)) +- **chore(providers):** retire the distributed Raycast Relay and Hailuo Web integrations after a provenance audit confirmed substantial derivation from GPL-3.0 sources; the official GitHub and MiniMax API providers remain available. Migration 163 disables legacy connections and invalidates their active leases while preserving connection IDs, audit/history rows, quota snapshots, usage records, and fail-closed API-key connection policies. ([#11691](https://github.com/diegosouzapw/OmniRoute/pull/11691)) +- **chore(providers):** retire the Felo Web provider while its GPL-derived provenance remains on licensing HOLD ([#11698](https://github.com/diegosouzapw/OmniRoute/pull/11698)) — stale runtime and database references now fail closed while existing connection, lease, and usage history remains preserved for auditability. +- **fix(sse):** retire the Gemini Web image-generation bridge while provenance and licensing remain on HOLD, preserving Gemini Web chat/auth/tools and legitimate Gemini/Nano Banana image providers ([#11708](https://github.com/diegosouzapw/OmniRoute/pull/11708)) +- **chore(audio):** retire the built-in EdgeTTS provider and its 11-voice catalog while provenance/licensing review remains on HOLD; the generic `/v1/audio/speech` endpoint and the other speech providers remain available ([#11711](https://github.com/diegosouzapw/OmniRoute/pull/11711)) +- **fix(providers):** retire the unofficial Qwen Web integration and its `qwen-web`/`qw` identities while third-party provenance remains on HOLD ([#11713](https://github.com/diegosouzapw/OmniRoute/pull/11713)) — direct requests now fail closed with a sanitized HTTP 410 and combos skip the retired target, while migration guards preserve connection IDs, allowlists, usage/audit history and invalidate active leases. Supported Qwen API, Qwen Code, cloud, token-plan, audio, local and model-name surfaces remain available; restoring Qwen Web requires a qualified provenance/license decision. +- **fix(providers):** retire `microsoft-designer-web` and its `msdesigner` alias from runtime and catalogs while preserving stored credentials, provider history and allowlists as inactive records — source dependency on gpt4free's [`MicrosoftDesigner.py`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/g4f/Provider/needs_auth/MicrosoftDesigner.py) is proven at snapshot `0943d16eef757a44deb60651c07bbe5fa958cafe`, whose [`LICENSE`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/LICENSE) is GPL-3.0; expressive adaptation is probable, which records provenance/licensing risk without asserting a legal conclusion ([#11720](https://github.com/diegosouzapw/OmniRoute/pull/11720)). +- **docs(legal):** centralize pinned MIT notices for the vendored GCF codec and local flag assets ([#11726](https://github.com/diegosouzapw/OmniRoute/pull/11726)) +- Split the 2,952-line `adobeFireflyClient.ts` into nine focused modules (catalog, credentials, payload, ARP session, upload, responses, IMS, account, poll) behind an unchanged barrel — no consumer edits and no public API change (#11728). — thanks @MumuTW +- **fix(dashboard):** replaced the Model Lockout card's unprovenanced notification MP3 with a short, locally synthesized Web Audio chime ([#11731](https://github.com/diegosouzapw/OmniRoute/pull/11731)) — both toggles retain optional audible feedback without shipping replacement media or third-party code, and unsupported, suspended, or rejected audio contexts fail open without blocking the setting change. +- **chore(deepseek-web):** remove the provenance-unresolved DeepSeek PoW WASM binary and its runtime loader/tracing while retaining the existing JavaScript solver (slower at high difficulty) ([#11732](https://github.com/diegosouzapw/OmniRoute/pull/11732)). +- **chore(assets):** remove four unused provider assets whose redistribution provenance could not be verified while retaining the active Freebuff SVG ([#11735](https://github.com/diegosouzapw/OmniRoute/pull/11735)) +- **docs(assets):** retired two raster images whose redistribution provenance could not be verified ([#11736](https://github.com/diegosouzapw/OmniRoute/pull/11736)) — PicoClaw documentation now reuses the existing SVG, while Zoo Code remains a text link without an image. +- **docs(legal):** add pinned notices for six LobeHub-derived provider SVGs and 65 byte-exact theSVG assets, retaining registry claims as unverified metadata with explicit trademark and HOLD boundaries ([#11737](https://github.com/diegosouzapw/OmniRoute/pull/11737)). +- **chore(quality):** added a versioned 225/225 provider-asset provenance manifest and blocking drift gate ([#11743](https://github.com/diegosouzapw/OmniRoute/pull/11743)) — the inventory pins magic MIME and SHA-256 for every physical file, records immutable source matches for 71 assets, keeps 69 probable and 85 unresolved entries explicit but non-blocking, and treats provenance as source evidence rather than copyright or trademark clearance. +- **refactor(mitm):** replace four previously derived Traffic Inspector utilities — conversation normalization, SSE merging, secret masking, and header sanitization — with independent clean-room implementations based on public provider schemas, WHATWG SSE, and RFC 6750, while keeping the two independently classified passthrough surfaces unchanged ([#11748](https://github.com/diegosouzapw/OmniRoute/pull/11748)). +- Added a unit test that fails seven days before any dated pack under `config/` (`validUntil` and sibling keys) lapses, naming the file and key. The Alibaba free-tier pack expired on 2026-08-27 and turned every PR red the next morning with no commit involved; renewal now happens on someone's terms, not the clock's. ([#11891](https://github.com/diegosouzapw/OmniRoute/pull/11891)) +- `check:workflows` now fails (under `--strict`/`--ratchet`) when any job routed to a self-hosted runner publishes with `--provenance` — npm rejects that with `422` at the registry, which in v3.8.50 only surfaced after the tag and Docker images were public. ([#11895](https://github.com/diegosouzapw/OmniRoute/pull/11895)) +- `scripts/ops/runner-janitor.sh` now proves a path is idle with one `lsof` snapshot and removes stale leftovers itself (tmpfs after 3 h — it is RAM — disk after 24 h), kills orphan `next-build` processes, prunes checkouts of stopped runners, and alerts on memory pressure; `--dry-run` shows exactly what it would do. `docs/ops/RUNNER_BOX.md` reconciled to the measured box (31 GB, 10 listeners). ([#11893](https://github.com/diegosouzapw/OmniRoute/pull/11893)) +- The `next-build` artefact (1.3 GB) is now written and read under `$RUNNER_TEMP` (per-runner, on disk) instead of `/tmp`, which on the self-hosted pool is a 12 GB tmpfs in RAM. Landing it there took 27–32 of the publish job's 76 minutes, and the fixed `/tmp/e2e-build.tar.gz` name let E2E jobs on different runners overwrite each other's download. ([#11896](https://github.com/diegosouzapw/OmniRoute/pull/11896)) +- The CI `build` job now runs in two concurrency lanes — `main` and pull requests — so a release build is never queued behind (or OOM-killed beside) PR builds on the self-hosted pool, which holds one `next-build` comfortably and two at the edge. ([#11901](https://github.com/diegosouzapw/OmniRoute/pull/11901)) +- Re-freeze the ESLint suppressions on `release/v3.8.51` from a clean-room measurement (2 stale file entries pruned, 55 pre-existing `no-explicit-any` in six new files frozen under #11924) and drop the dead `GPT_SIZE_MAP` constant orphaned by the Adobe Firefly client split, so `No new ESLint warnings` stops failing every PR with exit 2 (Refs #11924) ([#11955](https://github.com/diegosouzapw/OmniRoute/pull/11955)) +- Type the 55 `no-explicit-any` sites that had been frozen under #11924 — four redundant casts in `socksConnectorWithFamily.ts` (undici/socks types already accept them) and the mocks/fixtures of the socks-timeout and isFree suites — and drop their suppression entries; the ESLint ratchet shrinks from 5487 to 5432 (Closes #11924) ([#11975](https://github.com/diegosouzapw/OmniRoute/pull/11975)) +- Take the two hosted-runner builds off the PR rail: `Build App` (`build.yml`) is `workflow_dispatch`-only and `DAST smoke (PR)` runs only for PRs into `main` — the 7 GB hosted VM cannot build `release/v3.8.51` in any profile (19/30 red, VM shutdown ~8 min into `next build`) and both checks had turned into permanent noise on every release PR; the bundle stays validated by `ci.yml` on `main` and by `nightly-release-green` on `release/**` (Closes #11946) ([#11962](https://github.com/diegosouzapw/OmniRoute/pull/11962)) +- Move the four nightly jobs that build the backend (`nightly-schemathesis`, `nightly-llm-security` promptfoo + garak, `nightly-resilience` axe-a11y) off the hosted 7 GB runner — where they died on `release/v3.8.51` unseen — onto the box's new `omni-light` pool (two listeners), and document the reshaped fleet (4 active OmniRoute listeners: 2 `omni-build` + 2 `omni-light`, janitor ceiling 4) (Closes #11965) ([#11967](https://github.com/diegosouzapw/OmniRoute/pull/11967)) +- Stop painting every fork PR into `release/**` red: `quality.yml` `Build (advisory)` is skipped (GitHub still reports `continue-on-error` failures as check FAILURE). Hosted `ubuntu-latest` cannot finish `npm run build` on this tree — same class as #11962 taking `build.yml` off the PR rail. `docker-publish.yml` amd64 now runs on the `.113` `omni-build` pool (31 GB / 32 cores, two listeners) with Turbopack, shares the `heavy-build-main` lane with `ci.yml` `Build` so it queues instead of becoming a third heavy, and keeps per-ref concurrency (a merge storm was starting 8 concurrent OOM builds). arm64 stays on `ubuntu-24.04-arm` with webpack — there is no ARM box. Fallback when `USE_VPS_RUNNER` is off: hosted amd64 + webpack (#11976). ([#12021](https://github.com/diegosouzapw/OmniRoute/pull/12021)) +- `docker-publish.yml` builds with webpack (`OMNIROUTE_USE_TURBOPACK=0`) on **both** arches, including amd64 on the `.113` `omni-build` pool. Turbopack had the RAM (31 GB, `omniroute-113-6`) and still panicked (`TurbopackInternalError: there must be a path to a root`, run 33253576569, 26 min in). The same tree's arm64 webpack build on hosted `ubuntu-24.04-arm` succeeded (run 33264823398). Dockerfile already documents webpack as the Docker escape hatch (#11976). ([#12050](https://github.com/diegosouzapw/OmniRoute/pull/12050)) +- **docs(free-tier):** declare the counting vs deciding regimes for "is it free?" and guard the deciding path from DB-backed catalog resolution ([#12226](https://github.com/diegosouzapw/OmniRoute/pull/12226)) — thanks @maxmad64bis +- **docs(auto-combo):** The mode pack table in `docs/routing/AUTO-COMBO.md` now lists all six shipped packs with every weight each one sets, replacing a four-pack table whose numbers had also drifted from the source. It states plainly that no pack sets `quality`, so selecting any pack silences the observed-quality signal. Six more documents that quote the scoring factor count joined the `check:docs-counts` gate, which caught five stale claims — including one naming nine factors that do not exist — and two stale mode pack counts. The dashboard routing panel, which offered four of the six packs and labelled the default strategy "6-Factor Scoring", is now covered by a test; the two packs it was missing are `reliability-first` and `chaos-mode`, the latter labelled as the fault-injection profile it is rather than as one more routing preference ([#12316](https://github.com/diegosouzapw/OmniRoute/pull/12316)) — thanks @maxmad64bis +- **chore(quality):** rebaseline `src/lib/db/apiKeys.ts` for the ACL the key-creation path now preserves ([#12352](https://github.com/diegosouzapw/OmniRoute/pull/12352)) ([#12673](https://github.com/diegosouzapw/OmniRoute/pull/12673)) — thanks @KrzysiekSko +- **chore(providers):** bump the Claude Code wire identity and the Devin bridge image pin from `2.1.220` to `2.1.258` ([#12402](https://github.com/diegosouzapw/OmniRoute/pull/12402)) — thanks @ggiak ([#12604](https://github.com/diegosouzapw/OmniRoute/pull/12604)) +- **docs(env):** align `.env.example`, the README Bun section, and the troubleshooting guide with the code: `OMNIROUTE_USE_TURBOPACK` also governs `npm run build` (not dev-only), `bun run build` follows that flag instead of auto-selecting Webpack, `OMNIROUTE_CHAT_MAX_HEAVY_IN_FLIGHT` is unset by default (no request-count cap), and the structural `503 chat_admission_busy` message matches `chatAdmissionResponses.ts` (#12404 — thanks @pacocartones) +- **chore(electron):** upgrade the desktop app to Electron 44 (Chromium 152, Node 24.18.1) ([#12217](https://github.com/diegosouzapw/OmniRoute/pull/12217)). **Requires macOS 13 (Ventura) or later** — Chromium dropped macOS 12 (Monterey), so Monterey users must stay on an earlier OmniRoute desktop build. Windows and Linux are unaffected; the app already shipped only x64/arm64, so Electron 44 dropping 32-bit builds changes nothing. Removes the `openAsHidden`/`wasOpenedAsHidden` login-item fields deleted in Electron 44 — hidden autostart continues to work through the `--hidden` argument registered with the login item ([#12554](https://github.com/diegosouzapw/OmniRoute/pull/12554)) — thanks @app/dependabot +- **chore(quality):** rebaseline `src/sse/handlers/chat.ts` for the effective-input persistence the continuation fix needs ([#12641](https://github.com/diegosouzapw/OmniRoute/pull/12641)) ([#12680](https://github.com/diegosouzapw/OmniRoute/pull/12680)) — thanks @hartmark +- `Coverage` job on `ci.yml`: the informational Codecov upload gets its own 5-minute ceiling and `continue-on-error`, and the job budget grows from 20 to 30 minutes (the 8-shard c8 merge alone takes ~10) — a stalled upload no longer ends the job `cancelled` and drags a fully green `main` run's conclusion down with it ([#11972](https://github.com/diegosouzapw/OmniRoute/pull/11972)) +- CI hardening on the PR rail: the four `Unit Tests fast-path` shards get `timeout-minutes: 30` (a hung shard held a PR for 64 min instead of GitHub's 6 h default) and both ESLint file caches lose their `restore-keys` fallback, so a cache built under another suppressions file or lint config can no longer report stale verdicts (Refs #11600, #11924) ([#11963](https://github.com/diegosouzapw/OmniRoute/pull/11963)) +- Every CI job that runs a `next build` (`build`, the npm `publish`, both release-green validations) now targets the `omni-build` runner label, which only two of the eight self-hosted runners carry. The box holds one build comfortably and two at the edge; a third now queues on GitHub instead of being OOM-killed by the kernel. ([#11932](https://github.com/diegosouzapw/OmniRoute/pull/11932)) +- **refactor(combo):** move `handleRoundRobinCombo` (and `resolveTargetTokenLimit`) into `open-sse/services/combo/roundRobinCombo.ts`. `combo.ts` drops from 2164 to 1014 lines (`split("\n").length`); the leaf is 1199 (under the 1200 new-file cap). The round-robin call site uses a dynamic `import()` so `releaseStickyPinOnFailure` / `clearStaleLKGP` can stay exported from `combo.ts` without a static cycle. Skip / sticky / semaphore / safety-timer behavior is unchanged. ([#12811](https://github.com/diegosouzapw/OmniRoute/pull/12811)) — thanks @HouMinXi +- **chore(quality):** rebaseline the file-size caps the error-boundary campaign grew past (`open-sse/executors/codex.ts`, `open-sse/vendor/codex-chatgpt-web/bridge.ts`, both via [#12444](https://github.com/diegosouzapw/OmniRoute/pull/12444)) ([#12654](https://github.com/diegosouzapw/OmniRoute/pull/12654)) +- **chore(quality):** rebaseline the file-size caps the hartmark batch grew past (`combos/page.tsx` via [#12355](https://github.com/diegosouzapw/OmniRoute/pull/12355), `open-sse/services/combo.ts` via [#12338](https://github.com/diegosouzapw/OmniRoute/pull/12338)) ([#12623](https://github.com/diegosouzapw/OmniRoute/pull/12623)) — thanks @hartmark +- **chore(quality):** rebaseline the file-size caps the HouMinXi batch grew past when its PRs stacked (`providers/page.tsx`, `chatCore.ts`, `accountFallback.ts`) — each PR measured correctly in isolation, none saw the stacking ([#12619](https://github.com/diegosouzapw/OmniRoute/pull/12619)) +- **chore(quality):** rebaseline `open-sse/services/combo.ts` for the reset-aware scoring the HouMinXi batch stacked ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) ([#12678](https://github.com/diegosouzapw/OmniRoute/pull/12678)) — thanks @HouMinXi +- **docs:** sync provider catalog count (direct commit `4f11b2ae3d`) +- **docs(skills):** sync omni-inference search fallback (direct commit `b4e76a7ed9`) +- **chore(lint):** prune two stale base suppressions (direct commit `1325047d56`) +- **test(auto-combo):** tolerate full-suite rotation load (direct commit `ad3e293f9f`) +- **test:** drain inherited quality snapshot reds (direct commit `21ed68d8ac`) +- **chore(quality):** rebaseline measured CLI bundle growth (direct commit `d35b3f9779`) +- **test(ui):** reproduce invalid AnySearch icon alias (direct commit `12cc6ca834`) +- **test(translator):** fix the relative imports of the relocated deferred-finish test (direct commit `777d9d1629`) +- **docs(counts):** bring the documented provider and migration counts to the measured values (direct commit `e70bea30e9`) +- **test(security):** drop the guard-target set left unused by the sync (direct commit `a32ba0e362`) +- **chore(db):** renumber model_capabilities migration 163 → 169 (cross-branch collision with 163_radar_feed_cache_generated_at) (direct commit `70f5d4cbf9`) +- **docs(cli):** document CLI_PRIME_AGENT_BIN and correct the CLI Agents count (direct commit `9058e39b61`) +- **chore(deps):** refresh runtimes and adopt ESLint 10 ([#11259](https://github.com/diegosouzapw/OmniRoute/pull/11259)) — thanks @backryun +- **refactor(local-corpus):** implement dynamic root resolution and LRU cache ([#11491](https://github.com/diegosouzapw/OmniRoute/pull/11491)) — thanks @raheemuddin786 +- **chore(quality):** prune stale eslint-suppressions entries ([#11596](https://github.com/diegosouzapw/OmniRoute/pull/11596)) +- **chore(ci):** unblock quality gates - env-doc sync + stryker tap.testFiles registration (#9282) ([#11609](https://github.com/diegosouzapw/OmniRoute/pull/11609)) — thanks @Meet6338-X +- **chore(ci):** point the circular-deps gate at dpdm's real JS entrypoint ([#11615](https://github.com/diegosouzapw/OmniRoute/pull/11615)) — thanks @benzntech +- **chore(quality):** rebaseline file-size for #11448/#11495/#11561 growth ([#11630](https://github.com/diegosouzapw/OmniRoute/pull/11630)) +- **deps:** realign onnxruntime-node pin to @huggingface/transformers' 1.24.3 lockstep ([#11633](https://github.com/diegosouzapw/OmniRoute/pull/11633)) — thanks @jonlwheat2-gif +- **test(autoCombo):** port models_dev_tier #11508 guard from node:test to vitest ([#11635](https://github.com/diegosouzapw/OmniRoute/pull/11635)) — thanks @jonlwheat2-gif +- **chore(quality):** rebaseline file-size for #11083/#11631/#11628 growth ([#11636](https://github.com/diegosouzapw/OmniRoute/pull/11636)) +- **chore(lint):** prune stale ESLint suppressions (base-red #11449) ([#11644](https://github.com/diegosouzapw/OmniRoute/pull/11644)) — thanks @jonlwheat2-gif +- **docs(changelog):** aggregate the ten v3.8.50 fragments into CHANGELOG.md ([#11683](https://github.com/diegosouzapw/OmniRoute/pull/11683)) +- **chore(search):** drop unreachable credential sweep after #11642 ([#11687](https://github.com/diegosouzapw/OmniRoute/pull/11687)) — thanks @HouMinXi +- **ci:** add API route TypeScript regression gate ([#11705](https://github.com/diegosouzapw/OmniRoute/pull/11705)) — thanks @Sabeekhann +- **docs(dedup):** correct the CodeQL claim on the tenant-namespace comment ([#11712](https://github.com/diegosouzapw/OmniRoute/pull/11712)) +- **docs(changelog):** add consolidated v3.8.50 stats, top-25 ranking and a .mailmap ([#11715](https://github.com/diegosouzapw/OmniRoute/pull/11715)) +- **docs:** recommend gstack for AI-assisted workflows ([#11770](https://github.com/diegosouzapw/OmniRoute/pull/11770)) — thanks @santosraju99-hub +- **chore(quality):** tighten complexity/cognitive-complexity ratchets to the current tip; land the missed gateways.ts rebaseline ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **docs(i18n):** finish Freepik → Magnific rebrand in locale strings and README ([#11772](https://github.com/diegosouzapw/OmniRoute/pull/11772)) — thanks @turbolego +- **refactor(auto-combo):** fix divergent scoring in combo health reporting ([#11854](https://github.com/diegosouzapw/OmniRoute/pull/11854)) — thanks @jacobsparts +- **chore(changelog):** add missing fragments for #11919/#11918/#11916 ([#11938](https://github.com/diegosouzapw/OmniRoute/pull/11938)) +- **test(check):** escape the runs-on fixture with JSON.stringify, not a quote-only replace ([#11942](https://github.com/diegosouzapw/OmniRoute/pull/11942)) (direct commit `fb7445eaa3`) +- **test(release):** align five suites with the contracts #11933, #11919 and #11876 shipped on release/v3.8.51 ([#11944](https://github.com/diegosouzapw/OmniRoute/pull/11944)) +- **chore(quality):** remove unreachable code and restore test discovery ([#11950](https://github.com/diegosouzapw/OmniRoute/pull/11950)) — thanks @backryun +- **docs(agents):** sync-back landings are fast-forward, never squash ([#11964](https://github.com/diegosouzapw/OmniRoute/pull/11964)) +- **test(infra):** retry recursive temp-dir removal instead of failing a shard on ENOTEMPTY (#11966) ([#11968](https://github.com/diegosouzapw/OmniRoute/pull/11968)) +- **docs(ops):** the .113 heavy-build ceiling is one runner, not two ([#12048](https://github.com/diegosouzapw/OmniRoute/pull/12048)) +- **test(ui):** correct inactive auto-fetch expectation ([#12098](https://github.com/diegosouzapw/OmniRoute/pull/12098)) — thanks @RaviTharuma +- **docs:** document native dependency check escape hatch ([#12101](https://github.com/diegosouzapw/OmniRoute/pull/12101)) — thanks @RaviTharuma +- **test(providers):** regenerate the translate-path golden for OrcaRouter (#11923) ([#12118](https://github.com/diegosouzapw/OmniRoute/pull/12118)) +- **chore(quality):** velocity phase — loosen every numeric baseline by 20% until v4.0, monitor headroom nightly ([#12125](https://github.com/diegosouzapw/OmniRoute/pull/12125)) +- **chore(lint):** batch 0 of #12146 — type the call-log-cap sqlite rows instead of 45 as-any casts ([#12157](https://github.com/diegosouzapw/OmniRoute/pull/12157)) +- **chore(lint):** batch 4 of #12146 — shared/components react-hooks violations resolved ([#12159](https://github.com/diegosouzapw/OmniRoute/pull/12159)) +- **chore(lint):** batch 1 of #12146 — dashboard/cli-code react-hooks violations resolved ([#12160](https://github.com/diegosouzapw/OmniRoute/pull/12160)) +- **chore(lint):** batch 3 of #12146 — dashboard/settings react-hooks violations resolved ([#12162](https://github.com/diegosouzapw/OmniRoute/pull/12162)) +- **chore(lint):** batch 2 of #12146 — resolve the react-hooks compiler violations in dashboard/providers ([#12163](https://github.com/diegosouzapw/OmniRoute/pull/12163)) +- **chore(quality):** register search-432 cooldown test in stryker tap.testFiles ([#12170](https://github.com/diegosouzapw/OmniRoute/pull/12170)) +- **chore(lint):** batch 5 of #12146 — combos, endpoint, provider-stats, api-manager and costs react-hooks violations resolved ([#12174](https://github.com/diegosouzapw/OmniRoute/pull/12174)) +- **build:** add contributor fast profile ([#12192](https://github.com/diegosouzapw/OmniRoute/pull/12192)) — thanks @rafacpti23 +- **docs(readme):** README touch-ups ([#12193](https://github.com/diegosouzapw/OmniRoute/pull/12193)) ([#12194](https://github.com/diegosouzapw/OmniRoute/pull/12194)) ([#12202](https://github.com/diegosouzapw/OmniRoute/pull/12202)) — thanks @rafacpti23 +- **build:** force the webpack bundler in the contributor build profile ([#12197](https://github.com/diegosouzapw/OmniRoute/pull/12197)) — thanks @rafacpti23 +- **build:** skip standalone packaging in the contributor profile ([#12198](https://github.com/diegosouzapw/OmniRoute/pull/12198)) — thanks @rafacpti23 +- **docs(audit):** align every published number with the code and harden check:docs-counts ([#12200](https://github.com/diegosouzapw/OmniRoute/pull/12200)) +- **build:** stub the instrumentation entrypoints in the contributor profile ([#12203](https://github.com/diegosouzapw/OmniRoute/pull/12203)) — thanks @rafacpti23 +- **build:** omit the standalone output target for contributor builds ([#12204](https://github.com/diegosouzapw/OmniRoute/pull/12204)) — thanks @rafacpti23 +- **chore(lint):** batch 6 of #12146 — memory, radar, audit, analytics, cache, usage, activity, home and RequestLoggerV2 react-hooks violations resolved ([#12208](https://github.com/diegosouzapw/OmniRoute/pull/12208)) +- **docs(diagrams):** rename number-carrying diagram files to stable names ([#12210](https://github.com/diegosouzapw/OmniRoute/pull/12210)) +- **docs(dashboard):** redraw onboarding tier-flow SVGs for the real 4-tier model ([#12211](https://github.com/diegosouzapw/OmniRoute/pull/12211)) +- **docs(api):** document every implemented route in openapi.yaml (276 → 692 paths) ([#12212](https://github.com/diegosouzapw/OmniRoute/pull/12212)) +- **chore(lint):** batch 7 of #12146 — final src tail: 37 react-hooks violations across 33 files resolved ([#12244](https://github.com/diegosouzapw/OmniRoute/pull/12244)) +- Revert "docs: recommend gstack for AI-assisted workflows (#11770)" ([#12248](https://github.com/diegosouzapw/OmniRoute/pull/12248)) ([#12249](https://github.com/diegosouzapw/OmniRoute/pull/12249)) +- **docs:** Chaos Mode setup guide + weighted strategy semantics ([#12250](https://github.com/diegosouzapw/OmniRoute/pull/12250)) +- **docs(agents):** protected-surface merge rule — operator approval for agent-instruction files ([#12253](https://github.com/diegosouzapw/OmniRoute/pull/12253)) +- **chore(quality):** register native-codex turn-pin tests in stryker tap.testFiles ([#12263](https://github.com/diegosouzapw/OmniRoute/pull/12263)) +- **chore(quality):** dedupe tap.testFiles entries added by racing base-red fixes ([#12265](https://github.com/diegosouzapw/OmniRoute/pull/12265)) +- **docs(readme):** introduce OmniRouteTray — the macOS menu-bar companion ([#12276](https://github.com/diegosouzapw/OmniRoute/pull/12276)) — thanks @ggiak +- **chore(proxy):** purge the legacy 1proxy residue — sync/rotator modules, dead DB exports, dead settings tab and flag, docs (#12091) ([#12290](https://github.com/diegosouzapw/OmniRoute/pull/12290)) +- **chore(quality):** baseline-headroom skips generated and vendored files in the fileSize worst-file signal ([#12291](https://github.com/diegosouzapw/OmniRoute/pull/12291)) +- **refactor(video):** unify the JPEG frame data-URI contract ([#12322](https://github.com/diegosouzapw/OmniRoute/pull/12322)) +- **chore(deps):** freeze onnxruntime-node and eslint-plugin-react-hooks out of dependabot groups ([#12329](https://github.com/diegosouzapw/OmniRoute/pull/12329)) +- **test(executors):** restore the #10986 reasoning-only fallback guards ([#12364](https://github.com/diegosouzapw/OmniRoute/pull/12364)) — thanks @pacocartones +- **chore(quality):** re-tighten the file-size ratchet to the real LOC (plan 3.8.52 task 0) ([#12411](https://github.com/diegosouzapw/OmniRoute/pull/12411)) +- **test(grok):** format web executor suite ([#12412](https://github.com/diegosouzapw/OmniRoute/pull/12412)) +- **chore(quality):** ratchet open-sse typecheck baseline to zero ([#12418](https://github.com/diegosouzapw/OmniRoute/pull/12418)) +- **test(build):** guard the artifact path policy arrays against duplicates ([#12422](https://github.com/diegosouzapw/OmniRoute/pull/12422)) +- **chore(lint):** adopt eslint-plugin-react-hooks 7.1.1 ([#12428](https://github.com/diegosouzapw/OmniRoute/pull/12428)) +- **chore(providers):** remove a keyless provider integration at its operator's request ([#12440](https://github.com/diegosouzapw/OmniRoute/pull/12440)) +- **docs(providers):** register providers removed at their operator's request and guard against reintroduction ([#12478](https://github.com/diegosouzapw/OmniRoute/pull/12478)) +- **chore(quality):** base fixes — stryker tap.testFiles + node_modules cache key ([#12482](https://github.com/diegosouzapw/OmniRoute/pull/12482)) +- **docs(arch):** one-process recipe for tens of long /v1/responses ([#12493](https://github.com/diegosouzapw/OmniRoute/pull/12493)) — thanks @RaviTharuma +- **chore(quality):** tighten the CodeQL ratchet baseline from 11 to 6 ([#12530](https://github.com/diegosouzapw/OmniRoute/pull/12530)) +- **chore(deps):** pin browserslist override to ^4.28.8 ([#12592](https://github.com/diegosouzapw/OmniRoute/pull/12592)) — thanks @KooshaPari +- **chore(deps):** bump fast-uri to 3.1.7 in the electron lockfile ([#12601](https://github.com/diegosouzapw/OmniRoute/pull/12601)) +- **docs(quality):** document how the CodeQL ratchet refreshes and how to tighten it ([#12611](https://github.com/diegosouzapw/OmniRoute/pull/12611)) +- **test(ci):** pin the openapi-security-tiers two-arm contract with an executing gate test (#12581) ([#12652](https://github.com/diegosouzapw/OmniRoute/pull/12652)) +- **docs(gamification):** align XP Rewards table with code (#12501) ([#12667](https://github.com/diegosouzapw/OmniRoute/pull/12667)) — thanks @KooshaPari +- **refactor(combo):** split executeTarget into gates, attempt, and loop ([#12746](https://github.com/diegosouzapw/OmniRoute/pull/12746)) — thanks @HouMinXi +- **docs(security):** document socket.yml scanner config + CI workflow link (#12575) ([#12764](https://github.com/diegosouzapw/OmniRoute/pull/12764)) — thanks @KooshaPari +- **chore(ci):** guard commit identity in pre-commit to stop author misattribution ([#12772](https://github.com/diegosouzapw/OmniRoute/pull/12772)) +- **deps:** 17 Dependabot bumps — bump electron from 43.4.0 to 43.4.1 in /electron ([#11426](https://github.com/diegosouzapw/OmniRoute/pull/11426)); bump the development group across 1 directory with 8 updates ([#11428](https://github.com/diegosouzapw/OmniRoute/pull/11428)); bump the production group across 1 directory with 11 updates ([#11440](https://github.com/diegosouzapw/OmniRoute/pull/11440)); chore(deps): bump github/codeql-action/init from 4.37.7 to 4.37.8 ([#11925](https://github.com/diegosouzapw/OmniRoute/pull/11925)); chore(deps): bump github/codeql-action from 4.37.7 to 4.37.8 ([#11926](https://github.com/diegosouzapw/OmniRoute/pull/11926)); chore(deps): bump github/codeql-action/analyze from 4.37.7 to 4.37.8 ([#11927](https://github.com/diegosouzapw/OmniRoute/pull/11927)); build(deps): bump github/codeql-action/init from 4.37.8 to 4.37.9 ([#12345](https://github.com/diegosouzapw/OmniRoute/pull/12345)); build(deps): bump github/codeql-action/analyze from 4.37.8 to 4.37.9 ([#12346](https://github.com/diegosouzapw/OmniRoute/pull/12346)); bump the development group across 1 directory with 2 updates ([#12347](https://github.com/diegosouzapw/OmniRoute/pull/12347)); build(deps): bump github/codeql-action from 4.37.8 to 4.37.9 ([#12349](https://github.com/diegosouzapw/OmniRoute/pull/12349)); bump browserslist from 4.28.2 to 4.28.8 ([#12396](https://github.com/diegosouzapw/OmniRoute/pull/12396)); bump the production group across 1 directory with 4 updates ([#12399](https://github.com/diegosouzapw/OmniRoute/pull/12399)); bump @xmldom/xmldom ([#12500](https://github.com/diegosouzapw/OmniRoute/pull/12500)); bump qs from 6.15.2 to 6.16.0 ([#12512](https://github.com/diegosouzapw/OmniRoute/pull/12512)); bump @xmldom/xmldom from 0.9.10 to 0.9.12 ([#12513](https://github.com/diegosouzapw/OmniRoute/pull/12513)); bump fast-uri from 3.1.5 to 3.1.7 ([#12514](https://github.com/diegosouzapw/OmniRoute/pull/12514)); bump @humanfs/node from 0.16.7 to 0.16.8 ([#12515](https://github.com/diegosouzapw/OmniRoute/pull/12515)) +- Clear the `release/v3.8.51` `check:agent-skills-sync` base-red: regenerate `skills/cli-tunnel/SKILL.md` so the `tunnel create [type]` positional that #13009 taught the generator to read is reflected in the committed skill. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` typecheck base-red from #12731: the new `Mode pack ranking gates` candidates in `open-sse/services/autoCombo/__tests__/autoCombo.test.ts` omitted the required `provider`, `model` and `errorRate` fields of `ProviderCandidate`, failing `check:open-sse-typecheck` (and with it `Fast Quality Gates`) on every open PR. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` docs-sync base-red: EURouter (#13025) and GreenPT (#13024) took the live provider count to 358, leaving 7 STRICT drifts (`PROVIDER_REFERENCE.md`, 4 diagrams, 2 tier-flow images) plus the `AGENTS.md` / `llm.txt` / `package.json` count claims stale. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- **test(models):** Pin `CATALOG_BUILD_TIMEOUT_MS` in the #12058 canonical-catalog regression so a cold catalog build racing the 8s default bound no longer flakes the suite with a 500 ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Allow `@omniroute/opencode-plugin-v2/` in the npm pack policy — [#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870) shipped the v2 plugin beside its v1 sibling without widening the allowlist, so every packed file under it read as an unexpected artifact and failed `check:pack-policy` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Point the release-green validator's pack gate at the tree it is validating — the build-provenance guard defaulted to `origin/main`, which a release branch tip can never be an ancestor of mid-cycle, so `Package artifact` reported a false red on every run and the tarball boot-smoke cascaded off it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **docs(checks):** keep doc counts honest — headings, rankings, catalog, weights, quality gate and scoring diagram now covered ([#12507](https://github.com/diegosouzapw/OmniRoute/pull/12507)) — thanks @maxmad64bis +- **chore(lifecycle):** `check:model-lifecycle` now also diffs `DEFAULT_DEGRADATION_MAP` (the background-task redirect table) against the vendor lifecycle snapshot, refusing a retired id as source or target, with a table-driven unit test beside it. Three rows whose source the vendor had retired — `claude-sonnet-4-20250514`, `gemini-3-pro-preview` and `gpt-5.1-codex` (whose target `gpt-5.1-codex-mini` is retired too) — were dead code, since `checkLifecycle` answers 410 before the redirect runs; they are dropped (#12535 — thanks @pacocartones) +- **test(compression):** cover idle worker eviction at the resource level — the pool must call `terminate()` and must not retain the worker's `MessagePort`, complementing the `exit`-event assertion added with the fix ([#13371](https://github.com/diegosouzapw/OmniRoute/pull/13371)) +- **chore(quality):** raise the `imageGeneration.ts` file-size ceiling for the image-only-model guard that clears the #12945 base-red ([#13392](https://github.com/diegosouzapw/OmniRoute/pull/13392)) +- **chore(quality):** type the combo-test route's JSON response bodies instead of casting them to `any`, drop the now-empty suppression entry, and remove the `hasPerModelQuota` import `chatCore.ts` stopped using when the failure-classification helper was extracted ([#13379](https://github.com/diegosouzapw/OmniRoute/pull/13379)) +- **chore(electron):** the desktop bundle no longer ships the root-level files of every translated docs mirror (`README.md`, `llm.txt`, `CLAUDE.md`, `GEMINI.md`, `CONTRIBUTING.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`) — the packaged app only reads `docs/i18n//docs/**`, which stays. Saves ~11 MB on top of the translated CHANGELOGs already pruned. (#0000) ([#13235](https://github.com/diegosouzapw/OmniRoute/pull/13235)) +- **chore(quality):** retighten the `src/sse/handlers/chat.ts` unused-vars suppression count to the 8 violations that actually remain ([#13385](https://github.com/diegosouzapw/OmniRoute/pull/13385)) +- Bring the documented counts back in line with the code: 171 migrations (was 169) and 20 routing strategies (was 19), plus the regenerated `cli-tunnel` skill reference. ([#12970](https://github.com/diegosouzapw/OmniRoute/pull/12970)) +- Update the documented migration count to 172 after the call-logs provider-stats indexes landed. ([#13209](https://github.com/diegosouzapw/OmniRoute/pull/13209)) +- **chore(ci):** two gates that close the blind spots behind the exclusions above. `check:vitest-exclusions` requires every Vitest exclusion to name a tracking issue and to appear in `config/quality/vitest-exclusions.json` — the previous list grew to 62 files behind a comment pointing at an issue that had been closed for a month. `check-new-key-coverage` requires a key newly added to `en.json` to reach every locale; the existing coverage gate is a percentage floor per locale, so eleven absent keys out of ~13,000 left it at 99.9% while a whole feature shipped untranslated in nine languages. Both are diff-aware, so pre-existing debt stays frozen and neither needed a migration to turn on. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **build:** root layout no longer loads Inter from `next/font/google`, so a production image build does not need fonts.googleapis.com ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **docs(i18n):** bring the eight new `llm.txt` mirrors (km, kn, ml, my, ne, or, pa, si) up to the current provider and migration counts, so `check-docs-sync` stops rejecting every commit ([#13674](https://github.com/diegosouzapw/OmniRoute/pull/13674)) +- **refactor(ui):** flow surfaces (home topology, combo live studio, compression cockpit/waterfall/nodes, token-health badge) express state through the theme-aware `--orch-status-*` tokens instead of fixed dark-mode hex, so green/red/amber/grey stay legible in the light theme; dark mode is byte-identical. Categorical palettes (routing-strategy hues, compression-layer pills, provider brand colors) deliberately stay hex ([#12378](https://github.com/diegosouzapw/OmniRoute/issues/12378)) ([#13203](https://github.com/diegosouzapw/OmniRoute/pull/13203)) +- **chore(tests):** 51 test files that had been excluded from Vitest are running again, restoring roughly 350 assertions to the blocking suite. Each was measured individually first: of the 62 files parked behind the `// #8618 — pre-existing failure` comment, 51 pass against the current tree with no source change, so the exclusions had outlived the failures they were added for. The 11 that genuinely still fail stay excluded, but now point at a live tracker (#13204) rather than at #8618, which was closed in August while the list it tracked kept growing. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **chore(quality):** drop two ESLint suppression entries whose violations no longer exist, so `eslint --suppressions-location` stops rejecting every commit that touches the surrounding files ([#13384](https://github.com/diegosouzapw/OmniRoute/pull/13384)) +- **chore(release):** bring main's post-tag fixes into release/v3.8.51 — sync-back step 2/2 (direct commit `21c488f210`) +- **chore(deps):** patch toml and esbuild in the opencode plugin lockfiles ([#12965](https://github.com/diegosouzapw/OmniRoute/pull/12965)) +- **docs(changelog):** reconcile the v3.8.51 living section — fold 366 fragments, cover every cycle commit, credit every contributor ([#12971](https://github.com/diegosouzapw/OmniRoute/pull/12971)) +- **test(plugin):** wait for the v2 lazy-refresh tier instead of sleeping past it ([#12984](https://github.com/diegosouzapw/OmniRoute/pull/12984)) +- **chore(quality):** rebaseline chatCore.ts after the non-streaming regression fixes ([#13045](https://github.com/diegosouzapw/OmniRoute/pull/13045)) +- **docs(changelog):** add the missing bullet marker to the DeepSeek PoW fragment ([#13200](https://github.com/diegosouzapw/OmniRoute/pull/13200)) +- **chore(deps):** drain the Dependabot queue — 10 of 13 alerts ([#13213](https://github.com/diegosouzapw/OmniRoute/pull/13213)) +- **chore(ci):** make worktrees carry the hooks that enforce the identity gate ([#13236](https://github.com/diegosouzapw/OmniRoute/pull/13236)) +- **chore(deps):** pin csv-parse to ^7.0.2 via overrides (#13117) ([#13300](https://github.com/diegosouzapw/OmniRoute/pull/13300)) — thanks @KooshaPari +- **chore(deps):** bump hono from ^4.12.34 to ^4.13.7 (#13148) ([#13301](https://github.com/diegosouzapw/OmniRoute/pull/13301)) — thanks @KooshaPari +- **chore(deps):** pin joi to ^18.2.8 via overrides (#13085) ([#13302](https://github.com/diegosouzapw/OmniRoute/pull/13302)) — thanks @KooshaPari +- **docs(providers):** add Microsoft 365 Copilot (BizChat) provider guide (#12779) ([#13340](https://github.com/diegosouzapw/OmniRoute/pull/13340)) — thanks @KooshaPari +### 🙌 Contributors + +Thanks to everyone whose work landed in v3.8.51: + +| Contributor | PRs / Issues | +| --- | --- | +| [@5dive-bot](https://github.com/5dive-bot) | #11852 | +| [@abhisheksharma2411](https://github.com/abhisheksharma2411) | #11547, #11684, #11959, #12209 | +| [@adevwithpurpose](https://github.com/adevwithpurpose) | #11464 | +| [@adivekar-utexas](https://github.com/adivekar-utexas) | #12015, #12043, #12090, #12138 | +| [@AIB1TAL0S](https://github.com/AIB1TAL0S) | #12103 | +| [@alltomatos](https://github.com/alltomatos) | #11448 | +| [@alvinveroy](https://github.com/alvinveroy) | #11857, #12027, #12165 | +| [@amaleta](https://github.com/amaleta) | #11906, #11934 | +| [@amartinawi](https://github.com/amartinawi) | #11575 | +| [@AndrianBalanescu](https://github.com/AndrianBalanescu) | #11083, #11830, #11834, #11844 | +| [@anhtahaylove](https://github.com/anhtahaylove) | #13091, #13092, #13093, #13096, #13097, #13100, #13106, #13109, #13114, #13155, #13166, #13171, #13175, #13187, #13196 | +| [@aniruddhaadak80](https://github.com/aniruddhaadak80) | #11760 | +| [@app](https://github.com/app) | #12554 | +| [@arminanton](https://github.com/arminanton) | #11461, #11513 | +| [@AStupidBear](https://github.com/AStupidBear) | #11584 | +| [@atescivitci-cmd](https://github.com/atescivitci-cmd) | #12105 | +| [@b3nw](https://github.com/b3nw) | #11970, #11971, #12147 | +| [@backryun](https://github.com/backryun) | #11259, #11950, #12075, #12076, #12078, #12079, #12081, #12082, #12181, #12228, #12239, #12255, #12258, #12277, #12367, #12423, #12524, #12538 | +| [@benjaminkitt](https://github.com/benjaminkitt) | #11747 | +| [@benzntech](https://github.com/benzntech) | #11614, #11615 | +| [@Bl0ck154](https://github.com/Bl0ck154) | #11948, #11951, #11952, #11953, #11954 | +| [@brick30llc-ctrl](https://github.com/brick30llc-ctrl) | #11752, #12242, #12262 | +| [@Chewji9875](https://github.com/Chewji9875) | #11455, #11875, #12028 | +| [@cheynetom](https://github.com/cheynetom) | #12058 | +| [@davidebaraldo](https://github.com/davidebaraldo) | #12222, #12304 | +| [@davidlinfr](https://github.com/davidlinfr) | #6390 | +| [@ddarkr](https://github.com/ddarkr) | #12124 | +| [@Deftera186](https://github.com/Deftera186) | #11809 | +| [@dpozimski](https://github.com/dpozimski) | #11945 | +| [@DW-MediaLab](https://github.com/DW-MediaLab) | #12229 | +| [@echel0nn](https://github.com/echel0nn) | #11923 | +| [@f9td56dbgh-hub](https://github.com/f9td56dbgh-hub) | #11660 | +| [@fabioluissilva](https://github.com/fabioluissilva) | #11991 | +| [@foreveryh](https://github.com/foreveryh) | #12177 | +| [@geek007git](https://github.com/geek007git) | #12115, #12116, #12117, #12120, #12122 | +| [@geekyNads](https://github.com/geekyNads) | #11784, #12266 | +| [@ggiak](https://github.com/ggiak) | #11561, #12276, #12350, #12402 | +| [@gonisulaimann](https://github.com/gonisulaimann) | #12368, #12369, #12371 | +| [@Gorillaz322](https://github.com/Gorillaz322) | #12207 | +| [@groovecityJO](https://github.com/groovecityJO) | #12682 | +| [@hartmark](https://github.com/hartmark) | #11434, #11452, #11473, #11499, #11703, #11983, #11984, #11985, #11986, #11988, #11989, #11990, #11994, #12221, #12293, #12445, #12446, #12447, #12448, #12460, #12461, #12623, #12646, #12650, #12680, #12717, #12718, #12727, #12741, #12854 | +| [@hizzt](https://github.com/hizzt) | #11894 | +| [@hongnoul](https://github.com/hongnoul) | #11484 | +| [@HouMinXi](https://github.com/HouMinXi) | #11411, #11414, #11512, #11518, #11520, #11641, #11642, #11643, #11687, #11779, #11849, #11850, #11851, #11915, #11916, #11918, #11919, #11920, #12013, #12017, #12033, #12042, #12106, #12139, #12166, #12169, #12171, #12205, #12213, #12312, #12325, #12487, #12488, #12495, #12504, #12557, #12566, #12590, #12591, #12624, #12626, #12632, #12637, #12678, #12696, #12697, #12711, #12733, #12746, #12767, #12770, #12789, #12803, #12805, #12811, #12866, #12868, #12899, #12926, #12934, #12950, #12951, #12974, #13001, #13006, #13011, #13017, #13026, #13027, #13034, #13035, #13038, #13042, #13050, #13060, #13061, #13069, #13107, #13120, #13136, #13195, #13197 | +| [@Hsia97](https://github.com/Hsia97) | #11624 | +| [@jacobsparts](https://github.com/jacobsparts) | #11854, #12155, #12167 | +| [@joglomedia](https://github.com/joglomedia) | #11980 | +| [@jonlwheat2-gif](https://github.com/jonlwheat2-gif) | #11443, #11450, #11549, #11567, #11608, #11633, #11635, #11640, #11644, #11671, #11672, #11673, #11674, #11675, #11676, #11677, #11706, #11783, #12051, #12052, #12053, #12055 | +| [@kanade-hoshino](https://github.com/kanade-hoshino) | #12180 | +| [@Karan825](https://github.com/Karan825) | #11861 | +| [@KaspaPulse](https://github.com/KaspaPulse) | #11389, #11469, #11628, #11666, #11910 | +| [@keeltrace](https://github.com/keeltrace) | #12080, #12223 | +| [@killer30001000](https://github.com/killer30001000) | #12178 | +| [@KooshaPari](https://github.com/KooshaPari) | #12470, #12592, #12667, #12699, #12703, #12706, #12764, #12769, #12771, #13300, #13301, #13302, #13340, #13401, #13403, #13404, #13405, #13406, #13407, #13409, #13410, #13411, #13412, #13413, #13414, #13418, #13424, #13427, #13433, #13523, #13524, #13525, #13528, #13531, #13532, #13534, #13539, #13543, #13545, #13546, #13547, #13550, #13553 | +| [@kriptoburak](https://github.com/kriptoburak) | #11370 | +| [@KrzysiekSko](https://github.com/KrzysiekSko) | #12425, #12432, #12673 | +| [@marcelokarval](https://github.com/marcelokarval) | direct commit / report | +| [@maxmad64bis](https://github.com/maxmad64bis) | #1622, #11435, #11437, #11441, #11537, #11550, #11553, #11555, #11557, #11812, #11842, #11843, #11903, #12151, #12214, #12215, #12218, #12226, #12314, #12316, #12317, #12318, #12319, #12320, #12321, #12507, #12715, #12731, #12744, #12786, #12787, #12788, #12790, #12792, #12794, #12795, #12828, #12832, #12853, #12857, #12870, #12937, #12941, #12975, #13141, #13142, #13146, #13147 | +| [@mdigitalbh81](https://github.com/mdigitalbh81) | #12240 | +| [@Meet6338-X](https://github.com/Meet6338-X) | #11598, #11609, #12110 | +| [@morpheus9393](https://github.com/morpheus9393) | #11943 | +| [@MumuTW](https://github.com/MumuTW) | #11492, #11502, #11506, #11507, #11626, #11675, #11685, #11728, #11746, #11888, #11889, #11890, #11892 | +| [@Neuron-Mr-White](https://github.com/Neuron-Mr-White) | #11622, #11801, #12256 | +| [@NightStalker-87](https://github.com/NightStalker-87) | #12183 | +| [@NoxzRCW](https://github.com/NoxzRCW) | #11879, #11880, #11881, #11882, #11883, #11935 | +| [@ntdat812](https://github.com/ntdat812) | #11585, #12095 | +| [@ntdatt812](https://github.com/ntdatt812) | #11368, #11573, #11574, #11576, #11577, #11580, #11582, #11583, #11588, #11589, #11590, #11591, #11592, #11593, #11672, #12177, #12180, #12873, #12918, #12920, #12921, #12925, #12930, #13007, #13009, #13024, #13025, #13083, #13087, #13101, #13104, #13110 | +| [@official-burak](https://github.com/official-burak) | #11542 | +| [@opensource-elearning](https://github.com/opensource-elearning) | #12179, #12189, #12278, #12286 | +| [@oyi77](https://github.com/oyi77) | #11408, #11409, #11421, #11505, #11677, #12036, #12110 | +| [@pacocartones](https://github.com/pacocartones) | #11521, #11522, #11527, #11528, #11529, #11530, #11531, #11532, #11533, #11534, #11595, #11599, #11603, #11604, #11605, #11607, #11610, #11676, #11714, #11716, #11718, #11767, #11838, #11860, #11862, #11869, #11871, #11872, #11873, #11903, #11906, #11921, #11934, #12356, #12358, #12359, #12360, #12361, #12362, #12364, #12365, #12373, #12374, #12375, #12376, #12377, #12379, #12380, #12381, #12386, #12387, #12389, #12390, #12394, #12395, #12397, #12401, #12403, #12404, #12522, #12523, #12535, #12536, #12540, #12541, #12543, #12545, #12548, #12549, #12550, #12551, #12552, #12647, #12651, #12653, #13055 | +| [@patrykkopycinski](https://github.com/patrykkopycinski) | #11936, #11937, #12224 | +| [@PauloFH](https://github.com/PauloFH) | #11509 | +| [@PauloHSOliveira](https://github.com/PauloHSOliveira) | #12241 | +| [@ponkcore](https://github.com/ponkcore) | #12054 | +| [@Prajeeth-12](https://github.com/Prajeeth-12) | #11634, #12046 | +| [@quiterunner-commits](https://github.com/quiterunner-commits) | #12131, #12143 | +| [@rafacpti23](https://github.com/rafacpti23) | #11554, #11558, #12192, #12197, #12198, #12202, #12203, #12204 | +| [@ragnar-claude](https://github.com/ragnar-claude) | #11564 | +| [@raheemuddin786](https://github.com/raheemuddin786) | #11491, #11839, #11840, #11841, #12003, #12230, #12231, #12232, #12233, #12234 | +| [@rahilmavani](https://github.com/rahilmavani) | #11761 | +| [@Rahulsharma0810](https://github.com/Rahulsharma0810) | #11771 | +| [@RaviTharuma](https://github.com/RaviTharuma) | #11710, #11727, #11797, #11798, #11802, #11805, #11806, #11811, #12098, #12099, #12101, #12449, #12452, #12472, #12473, #12493, #12533, #12607, #12628, #12631, #12636, #12875, #12876, #12880, #12882, #12884 | +| [@rezjalibd](https://github.com/rezjalibd) | #12186 | +| [@RhianB14](https://github.com/RhianB14) | #11692 | +| [@rifqiawl](https://github.com/rifqiawl) | #11517, #11519, #11969 | +| [@rqzbeh](https://github.com/rqzbeh) | #11390 | +| [@Sabeekhann](https://github.com/Sabeekhann) | #11699, #11705 | +| [@santosraju99-hub](https://github.com/santosraju99-hub) | #11755, #11770 | +| [@Seramicx](https://github.com/Seramicx) | #11568 | +| [@Siva010](https://github.com/Siva010) | #12191 | +| [@solstxce](https://github.com/solstxce) | #11597 | +| [@soroush5](https://github.com/soroush5) | #12691 | +| [@tenshiak](https://github.com/tenshiak) | #12279 | +| [@TheDemonTuan](https://github.com/TheDemonTuan) | #11468, #11470, #11471, #11482, #11548, #11758, #11775, #11814 | +| [@thomasmaerz](https://github.com/thomasmaerz) | #12834 | +| [@tuandinh0801](https://github.com/tuandinh0801) | #11454 | +| [@turbolego](https://github.com/turbolego) | #11621, #11762, #11772, #11774, #11781, #12216 | +| [@Tushar49](https://github.com/Tushar49) | #11392 | +| [@ujjawalkaushik1110](https://github.com/ujjawalkaushik1110) | #11863 | +| [@vermasomesh835](https://github.com/vermasomesh835) | #11794 | +| [@vsd2807](https://github.com/vsd2807) | #11565, #11619 | +| [@wahidsadik371-coder](https://github.com/wahidsadik371-coder) | #12126 | +| [@watchingdogs](https://github.com/watchingdogs) | #12031 | +| [@wildcard](https://github.com/wildcard) | #11620 | +| [@xiaoyaner0201](https://github.com/xiaoyaner0201) | #11460, #11662, #12083 | +| [@Xxx91n](https://github.com/Xxx91n) | #11690 | +| [@yourspraveen](https://github.com/yourspraveen) | #11146 | +| [@yxyxy](https://github.com/yxyxy) | #11671 | +| [@ZaimMarzuki](https://github.com/ZaimMarzuki) | #11960 | +| [@Zartharas](https://github.com/Zartharas) | #11340 | +| [@zero-executioner](https://github.com/zero-executioner) | #11631 | +| [@diegosouzapw](https://github.com/diegosouzapw) | maintainer | + +--- ## [3.8.50] — 2026-08-25 _Living section — regenerated 2026-08-12 from all cycle commits (cycle open `ed2db6cb19` → tip). Bullets carry the merged PR and its author; direct pushes listed separately._ diff --git a/docs/i18n/sw/CHANGELOG.md b/docs/i18n/sw/CHANGELOG.md index 255a50f186..f301c04271 100644 --- a/docs/i18n/sw/CHANGELOG.md +++ b/docs/i18n/sw/CHANGELOG.md @@ -8,16 +8,1119 @@ ## [3.8.51] — TBD -_Living section — cycle opened at the v3.8.50 freeze (parallel-cycle model). Bullets are aggregated from `changelog.d/` fragments at each `/generate-release` phase._ +_Living section — reconciled 2026-09-15 from all cycle commits (`091589089c` → `c0f92ec98a`, 916 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 | **123** | +| 📝 Commits in the cycle | **916** | +| 🔀 Pull requests referenced | **892** | +| 📋 Changelog entries | **919** | +| 🙌 Contributors credited in entries | **121** | +| 🤖 Automated dependency commits | 18 | + +**Entries by type** + +| Type | Count | +| --- | ---: | +| 🐛 Fixes | 592 | +| ✨ Features | 131 | +| 🧹 Chore | 93 | +| 📚 Docs | 42 | +| 🧪 Tests | 32 | +| ♻️ Refactor | 8 | +| 🏗️ Build | 7 | +| ⚡ Performance | 7 | +| ⚙️ CI | 2 | +| ⏪ Reverts | 2 | +| 📦 Dependencies | 2 | +| 🔒 Security | 1 | + +### 🏆 Top 25 contributors this cycle + +_By commits in `091589089c..c0f92ec98a`, author identities consolidated via `.mailmap` and the merged PR's GitHub login. Bots excluded._ + +| # | Contributor | Commits | +| ---: | --- | ---: | +| 🥇 | diegosouzapw | 323 | +| 🥈 | Bob.Hou (@HouMinXi) | 79 | +| 🥉 | Paco Cartones (@pacocartones) | 77 | +| 4 | Dizzle (@maxmad64bis) | 49 | +| 5 | Koosha Paridehpour (@KooshaPari) | 43 | +| 6 | Nguyen Thanh Dat (@ntdatt812) | 31 | +| 7 | Ravi Tharuma (@RaviTharuma) | 26 | +| 8 | Markus Hartung (@hartmark) | 22 | +| 9 | Webman (@jonlwheat2-gif) | 22 | +| 10 | backryun | 17 | +| 11 | anhtahaylove | 15 | +| 12 | Rafa Martins (@rafacpti23) | 10 | +| 13 | Syed Raheemuddin (@raheemuddin786) | 10 | +| 14 | Nguyễn Viết Tuấn (@TheDemonTuan) | 8 | +| 15 | MumuTW | 7 | +| 16 | Paijo (@oyi77) | 7 | +| 17 | Tobias Andersen (@turbolego) | 6 | +| 18 | Bl0ck (@Bl0ck154) | 5 | +| 19 | SHANMUGAPRIYAN (@geek007git) | 5 | +| 20 | KaspaPulse | 5 | +| 21 | Abhishek Sharma (@abhisheksharma2411) | 4 | +| 22 | Abhishek Divekar (@adivekar-utexas) | 4 | +| 23 | Andrew B. (@AndrianBalanescu) | 4 | +| 24 | NoxzRCW | 4 | +| 25 | opensource-elearning | 4 | ### ✨ New Features +- **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). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- Added Google AI Studio Gemini batch text-to-speech support through `POST /v1/audio/speech`. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- 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. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(routing):** subscription-first auto groupings — `auto/subscription` routes only through plan-included connections with a documented hard-stop overage and fails closed on exhaustion, while `auto/thrifty` orders the pool `subscription → keyless → free → cheap → premium` and steps up one rung at a time as each is exhausted. Billing class comes from a curated per-connection catalog (uncurated is treated as metered, never plan-included), both reuse STRICT_ZERO_COST's per-connection verification, and a quota reading whose `resetAt` has passed is now refreshed regardless of TTL so routing returns to plan capacity as soon as it resets ([#11146](https://github.com/diegosouzapw/OmniRoute/pull/11146)) — thanks @yourspraveen +- **feat(providers):** publish a management-authenticated versioned web-session credential contract from OmniRoute's canonical browser credential metadata ([#11340](https://github.com/diegosouzapw/OmniRoute/pull/11340)) — thanks @Zartharas +- **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)) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add Xquik X search with typed results, credential validation, REST routing, and MCP selection ([#11370](https://github.com/diegosouzapw/OmniRoute/pull/11370)) — thanks @kriptoburak +- **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)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(dashboard):** surface durable exclusive managed leases in the existing Sessions view, keeping leased clients visible across idle gaps while marking connections with in-flight work as active ([#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389)) — thanks @KaspaPulse +- **build(bun):** allow Turbopack bundler flag on Bun 1.4+ with configurable Webpack fallback ([#11471](https://github.com/diegosouzapw/OmniRoute/pull/11471)) — thanks @TheDemonTuan +- feat(api): add an opt-in `modelVisibilityAllowlist`/`modelVisibilityDenylist` settings pair to curate exactly which models `/v1/models` advertises, mirrored into every `auto/*` combo candidate pool so a denied model cannot be routed to via combo selection either (#11481) ([#11997](https://github.com/diegosouzapw/OmniRoute/pull/11997)) +- **feat(rankings):** the Free Provider Rankings page shows what each provider actually served over the last 24 h. It ranked by ELO alone, which left a provider that answers every call with an error in first place; the usage data was already served by the API but never requested. A provider with too small a sample shows a dash, not a number ([#11546](https://github.com/diegosouzapw/OmniRoute/pull/11546)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **feat(guardrails):** enforce a bounded, deterministic contract for Video Bridge transcripts — 256 cues, 4096 input code units and 4 KiB UTF-8 per cue, 64 KiB total text, malformed-Unicode rejection, focus-window scoping, cross-source reconciliation with contributing-source metadata, and a structural provenance trust boundary so caller JSON can never self-assert `embedded`/`audio-bridge` provenance ([#11652](https://github.com/diegosouzapw/OmniRoute/issues/11652)) ([#12009](https://github.com/diegosouzapw/OmniRoute/pull/12009)) +- **feat(video):** orchestrate optional Video Bridge audio extraction and Audio Bridge STT behind a dual opt-in (operator setting AND per-request signal) — a new loopback-only broker `mode=audio` operation shares the frame path's exact process queue, deadline, AbortSignal, and byte budgets to extract a bounded mono 16 kHz PCM WAV from the same already-downloaded video, then reuses the existing Audio Bridge transcription boundary; provider segment timing is preserved when available and marked coarse otherwise, and every failure degrades to a visual-only-safe partial instead of throwing (#11654). ([#12012](https://github.com/diegosouzapw/OmniRoute/pull/12012)) +- Add a tenant-bound Video Bridge drill-down lifecycle on top of the existing secure cache substrate: opaque hashed handles (never raw session/video identifiers), preview/standard/detail multiresolution variants resampled on read, response pagination capped at 8 frames and 32 MiB, and a new authenticated `/api/v1/video-bridge/drilldown` consumer route that stays disabled for remote access by default and denies cross-key access with the same response as a nonexistent handle (no existence oracle). ([#12006](https://github.com/diegosouzapw/OmniRoute/pull/12006)) +- **test(video):** Add the Video Bridge FU-07/FU-09 promotion-evidence harness (#11656) — a frozen Zod manifest schema covering the 8 required scenario kinds (static scenes, rapid cuts, late facts, fades, blur, small text, close events, visual prompt injection) with a minimum of 3 repetitions per case, deterministic declarative fixture recipes (`videoBridgePromotionFixtures.ts`), a pure medians/p95 metrics aggregator, a pure FU-07/FU-09 promotion-verdict evaluator applying the ticket's exact thresholds (missing token usage always holds), a digest-only persistence layer that never retains raw media or raw model responses, and a versioned per-model promotion allowlist shipped empty with every model defaulting to `hold`. The FU-07/FU-09 promotion verdicts themselves remain HOLD — they require a real evidence run against real models on VPS 192.168.0.15. ([#12008](https://github.com/diegosouzapw/OmniRoute/pull/12008)) +- **feat(video bridge):** "embedded" transcript provenance can now be legitimately earned instead of merely asserted — a bounded, allowlisted (`mov_text`/`subrip`/`webvtt`) subtitle probe runs through the loopback-only Video Bridge broker (at most 2 streams, 10s subdeadline bounded by the request deadline, 256 KiB output, 4096-code-unit lines), normalized through a bounded, ReDoS-safe WebVTT parser and Zod-validated end to end. The adapter always resolves to an explicit `success`/`absent`/`transient_failure` outcome — a subtitle failure never breaks the visual description path, and only a fingerprint-verified broker response (never a caller-declared label) can produce embedded cues (#11659). ([#12011](https://github.com/diegosouzapw/OmniRoute/pull/12011)) +- Default new Antigravity-family connections (agy CLI imports and Antigravity OAuth connects) to model auto-sync, so live model discovery lands in the synced catalog and `/v1/models` picks up freshly released upstream models (e.g. Gemini 3.7 Flash tiers) without code changes. Existing connections keep their current setting; the per-connection dashboard toggle remains the opt-out. (#11685 — thanks @MumuTW) ([#11887](https://github.com/diegosouzapw/OmniRoute/pull/11887)) +- **feat(ui):** enable React Compiler (`reactCompiler: true` + `babel-plugin-react-compiler`) for automatic memoization at build time ([#11783](https://github.com/diegosouzapw/OmniRoute/pull/11783)) — thanks @jonlwheat2-gif +- **feat(providers):** add SeekAi (`seekai.cc`) as an OpenAI-compatible New-API gateway — catalog id `seekai` (alias `ska`), `https://seekai.cc/v1`, live `/v1/models` via `passthroughModels`, aggregator-list membership so New-API balance detection can opt in. No referral/aff codes. ([#11786](https://github.com/diegosouzapw/OmniRoute/issues/11786)) ([#12557](https://github.com/diegosouzapw/OmniRoute/pull/12557)) — thanks @HouMinXi +- **feat(zai):** add GLM-5.3-Flash Coding Plan support (1M context, 128K output, vision, `low|high|max` reasoning) and route `zai` GLM-5.3-family API-key traffic through the OpenAI-compatible Coding Plan endpoint with native thinking defaults ([#11801](https://github.com/diegosouzapw/OmniRoute/pull/11801)) — thanks @Neuron-Mr-White +- **feat(combo):** choose how combo models are ordered — manual, provider, score, or name — via a sort control in the dashboard builder, persisted in `config.modelSort` and re-applied on load and after add ([#11812](https://github.com/diegosouzapw/OmniRoute/pull/11812)) — thanks @maxmad64bis +- **feat(free):** custom models can be marked free-tier via `customModels[].isFree`; `isFreeModel()` is the first door and `hidePaidModels` respects it even for providers outside the free budget ([#11843](https://github.com/diegosouzapw/OmniRoute/pull/11843)) — thanks @maxmad64bis +- **feat(nodejs):** add `5dive` as a `configure` target — `omniroute configure 5dive` / `omniroute setup-5dive` write a 5dive auth profile that points an agent fleet's `claude` seats at OmniRoute, with the root-only write, the loopback-vs-`https` endpoint rule and the per-seat model pin handled explicitly ([#11852](https://github.com/diegosouzapw/OmniRoute/pull/11852)) — thanks @5dive-bot +- **feat(sse):** treat `max` as a first-class reasoning-effort tier and clamp per model family (GLM 5.1+/DeepSeek V4+/Kimi K3+ keep native `max`; o1/MiniMax/Grok/Muse Spark clamp to their upstream ceiling) ([#11875](https://github.com/diegosouzapw/OmniRoute/pull/11875)) — thanks @Chewji9875 +- **feat(providers):** the provider plugin manifest now advertises a `usage-fetch` capability for the 40 providers that have a wired usage/quota fetcher, so external dashboards can read it from `GET /api/v1/provider-plugin-manifest` instead of parsing `open-sse/services/usage.ts` after every release. Discovery only — no new fetcher, no quota change, and the Dashboard quota widget stays gated by `USAGE_SUPPORTED_PROVIDERS`. `USAGE_FETCHER_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/fetcherProviders.ts`) and is re-exported from `services/usage.ts`, keeping the manifest module a light leaf instead of pulling the ~490-module usage dispatcher into the manifest route. ([#11903](https://github.com/diegosouzapw/OmniRoute/pull/11903)) — thanks @maxmad64bis / @pacocartones +- **feat(plugins):** `OMNIROUTE_PLUGINS_DIR` sets the directory the runtime plugin scanner reads — and the root the plugin manager installs into — overriding the `HOME`-derived default, so a Docker/K8s deployment can point straight at its bind-mounted plugin tree instead of moving `HOME` just to relocate the scan path. An image that exports no home no longer scans `/tmp/.omniroute/plugins` in silence: the resolved directory is logged once at startup as `scanner.dir_resolved`, naming the input that won. Unset, behaviour is unchanged. Distinct from the CLI-only `OMNIROUTE_PLUGIN_PATH`, which finds `omniroute-cmd-*` command packages and never reached this scanner ([#11906](https://github.com/diegosouzapw/OmniRoute/pull/11906)) — thanks @amaleta / @pacocartones +- **feat(leases):** add an explicit owner-authenticated status action that returns only the active lease's privacy-safe configured connection and provider labels, with generation fencing and no credential or internal-id disclosure ([#11910](https://github.com/diegosouzapw/OmniRoute/pull/11910)) — thanks @KaspaPulse +- **feat(providers):** import-from-file modal shows per-row API errors and ships a downloadable CSV template ([#12071](https://github.com/diegosouzapw/OmniRoute/issues/12071)) ([#12504](https://github.com/diegosouzapw/OmniRoute/pull/12504)) — thanks @HouMinXi +- **feat(providers):** dashboard search matches connection name and `baseUrl` so imported OpenAI-compat nodes surface on the provider card ([#12108](https://github.com/diegosouzapw/OmniRoute/issues/12108)) ([#12495](https://github.com/diegosouzapw/OmniRoute/pull/12495)) — thanks @HouMinXi +- **feat(routing):** add a `score` Auto router strategy that selects the highest configured weighted score and reuses `explorationRate`. ([#12155](https://github.com/diegosouzapw/OmniRoute/pull/12155)) — thanks @jacobsparts +- Add a runtime feature flag to disable universal context handoffs globally without changing the default behavior. ([#12167](https://github.com/diegosouzapw/OmniRoute/pull/12167)) — thanks @jacobsparts +- **feat(providers):** the provider plugin manifest now also advertises a `usage-supported` capability for the 46 providers whose usage API is accepted by the server and Dashboard routes, so integrators can distinguish "the server will serve quota for this provider" from "a fetcher is wired" without reading TypeScript. Discovery only — no fetcher or quota change. `usage-fetch` resolves on id or alias (the usage dispatcher accepts both); `usage-supported` resolves on id alone, matching the runtime guard `USAGE_SUPPORTED_PROVIDERS.includes(providerId)`. `USAGE_SUPPORTED_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/supportedProviders.ts`) and is re-exported from `providers.ts`, mirroring the `fetcherProviders` leaf from #11903 and keeping the manifest a light module. ([#12214](https://github.com/diegosouzapw/OmniRoute/pull/12214)) — thanks @maxmad64bis +- **feat(rankings):** order Free Provider Rankings by what each provider actually served — `GET /api/free-provider-rankings?sortBy=reliability` and a "Most reliable first" toggle on the page. Providers with too few calls to state a success rate keep their score order below the measured ones; the default order is unchanged ([#12218](https://github.com/diegosouzapw/OmniRoute/pull/12218)). — thanks @maxmad64bis +- **perf(sse):** defer `cloneLogPayload()` in the structured SSE collector until after the `maxEvents`/`maxBytes` cap check, eliminating ~9,800 wasted `structuredClone` calls per streaming response (65–71% faster `push()`). Reducer snapshot isolation restored for OpenAI and Responses summaries ([#12241](https://github.com/diegosouzapw/OmniRoute/pull/12241)) — thanks @PauloHSOliveira ([#12243](https://github.com/diegosouzapw/OmniRoute/pull/12243)) +- **feat(usage):** Devin CLI agentic quota (Codeium seat-management GetUserStatus) and OpenRouter key limits plus account credits now surface in Provider Limits ([#12256](https://github.com/diegosouzapw/OmniRoute/pull/12256) — thanks @Neuron-Mr-White) +- **feat(settings):** persist `headroomUrl` through Settings so status/start use the operator URL instead of only `HEADROOM_URL` ([#12306](https://github.com/diegosouzapw/OmniRoute/issues/12306)) ([#12487](https://github.com/diegosouzapw/OmniRoute/pull/12487)) — thanks @HouMinXi +- **feat(auto-combo):** Auto-Combo scoring can now weigh how often a provider/model has actually succeeded. The engine already carried that number on every candidate — 24 hours of usage history behind a ten-sample floor, real-time metrics otherwise — and the scoring function never read it, while the weight table described `stability` as if it did. `reliability` (`1 - failureRate`, with the same field precedence and the same rate-bounding the speed ranking already uses, so a corrupt reading means "nothing observed" rather than "fails every call") is now a declared factor shipping at weight `0`, so routing is unchanged until an operator gives it one, and the `stability` description now matches what that factor computes ([#12317](https://github.com/diegosouzapw/OmniRoute/pull/12317)) — thanks @maxmad64bis +- **feat(routing):** With `freeAccessPolicy: "strict"`, the read-only candidate listing (`GET /v1/auto-combo/{channel}/candidates`) no longer hides the candidates the zero-cost guard excludes — the same read-only transparency the resilience filter already honours (#9133). Each candidate now carries `freeAccessExclusion` saying why it would be kept out, and it tells an exhausted allowance apart from a quota reading that never arrived or went stale, which used to look identical from the outside. Routing is unchanged: the listing reports, it never enforces. The separate `excludeTosAvoid` guard still drops its candidates without a reason; that gap is now documented rather than closed ([#12319](https://github.com/diegosouzapw/OmniRoute/pull/12319)) — thanks @maxmad64bis +- **feat(radar):** The Radar catalog table now shows two facts it was already receiving from the feed and dropping on the floor: the per-model rate limits (requests and tokens, per minute and per day) in a new column, and a badge when a provider's terms state it may train on the prompts you send. A limit of zero renders as zero rather than "rate-only" — for a ceiling those are opposite facts — and a model with no training statement gets no badge, because an absent statement is not a guarantee ([#12320](https://github.com/diegosouzapw/OmniRoute/pull/12320)) — thanks @maxmad64bis +- **feat(radar):** explain Community, single-use, contributor, supporter, recovery, abuse, offers, and privacy rules before either Radar activation action, and remove the superseded fixed-PR grant promise from every UI locale ([#12342](https://github.com/diegosouzapw/OmniRoute/pull/12342)) +- **feat(gamification):** the dashboard Profile page now shows the real daily streak — `/api/gamification/level` returns `streak: { current, longest }` (per key with `apiKeyId`, operator-wide maximum otherwise) and the streak card reads it instead of a hard-coded 0 (#2403) ([#12377](https://github.com/diegosouzapw/OmniRoute/pull/12377)) — thanks @pacocartones +- **feat(gamification):** the dashboard leaderboard now shows each API key's display name under the Name column instead of a truncated key id; `GET /api/gamification/leaderboard` attaches `name` per entry (name only — no key material), while the shared ranking helper and the federation leaderboard stay id-only — thanks @pacocartones ([#12385](https://github.com/diegosouzapw/OmniRoute/pull/12385)) +- **feat(gamification):** enforce the documented 1000 XP/min per-API-key anti-cheat rate limit on the XP award path; over-limit awards are logged and skipped instead of persisted, and the sliding window now matches the timestamp format stored in `xp_audit_log` ([#2403](https://github.com/diegosouzapw/OmniRoute/issues/2403)) ([#12390](https://github.com/diegosouzapw/OmniRoute/pull/12390)) — thanks @pacocartones +- **feat(admin):** localize the gamification anomalies page — the loading state, the Status column and the Suspicious badge now come from the `common` catalog (new `common.suspicious` key propagated to every locale) — add it to the Gamification sidebar group as `gamification-admin` (`/dashboard/gamification/admin`), and expose the loading and empty states as polite `role="status"` live regions (#12401 — thanks @pacocartones) +- **feat(providers):** skip GitHub combo members missing from the live synced catalog, and drop Copilot models that are policy-disabled or hidden from the model picker ([#12473](https://github.com/diegosouzapw/OmniRoute/pull/12473)) — thanks @RaviTharuma +- **feat(i18n):** locale-expansion tooling — `npm run i18n:add-locale` adds a language to config, dashboard, docs mirrors, CLI, README/indexes and site in one command; browser, CLI and cookie detection resolve `uk`, `fil`/`tl`, `zh-Hant` (and the retired legacy `in` → `id`) via config aliases; `config/i18n.json` now ships in the npm package so the published CLI can read it; new real-translation ratio gate (`npm run i18n:check-ratio`, advisory) with a per-locale ratchet baseline; `run-translation --adopt` restores `.i18n-state.json`; `sync-language-bars` generates 🌐 bars from config; `validate_translation.py` loads its allowlist again. Retires the duplicate `in` locale (Indonesian mislabelled as Hindi) — 42 honest locales; saved `NEXT_LOCALE=in` / `OMNIROUTE_LANG=in` keep working. (#12496) +- **feat(api):** Emit gateway-measured `tokens_per_second` (TTFT excluded) on streaming usage and `X-OmniRoute-Tokens-Per-Second` when first-token latency is known ([#12616](https://github.com/diegosouzapw/OmniRoute/issues/12616)) ([#12631](https://github.com/diegosouzapw/OmniRoute/pull/12631)) — thanks @RaviTharuma +- **feat(opencode-plugin): map gateway cost/usage/tok/s onto OpenCode inference payloads** — the official plugin copies `X-OmniRoute-Response-Cost`, token counts, `X-OmniRoute-Tokens-Per-Second` / `usage.tokens_per_second`, TTFT, and the winning `X-OmniRoute-Model` onto the JSON/SSE body OpenCode already consumes. Missing tok/s is left unset (never `tokens / latency`). (#12636) — thanks @RaviTharuma +- **feat(combo):** add combo strategy `quota-weighted`: skip exhausted accounts, then weighted-draw by leftover / in-flight load; existing conversations stay pinned until the account is empty ([#12789](https://github.com/diegosouzapw/OmniRoute/pull/12789)) — thanks @HouMinXi +- **feat(models):** Account live listings become the chat catalog source for Claude, Codex, Copilot, and AGY; public metadata only fills prices on IDs those accounts already list. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **fix(models):** Union `agy` and `antigravity` live catalogs so `agy/gemini-3.8-flash-high` is not rejected after the prefix folds to `antigravity`. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **feat(opencode):** Add `@omniroute/opencode-plugin-v2`, an opencode v2 plugin that gives a v2 install a route to an OmniRoute gateway: it publishes models, combos and auto-combos into the host catalog, refreshes them lazily behind a 300s TTL, and keeps serving the last known catalog from an on-disk snapshot when the gateway is unreachable. The v1 plugin (`@omniroute/opencode-plugin`) is untouched and keeps working as before. The plugin adapts to the host instead of assuming one shape: it reads the shape the host seeds into the catalog draft and publishes the `api` block, the pre-`api` top-level fields, or both. The gateway key comes from the host credential store when one is connected, so no secret needs to sit in a config file; a source the gateway refuses is named in a warning instead of silently degrading the catalog to raw model ids; and tool schemas bound for a Gemini model are stripped of the JSON-Schema keywords Gemini rejects, so tool calling survives that route ([#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870)) — thanks @maxmad64bis +- feat(services): show sanitized CLIProxyAPI account health from its authenticated management API without exposing credentials, file paths, or raw account metadata (#6342) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add AnySearch free web search + URL extract (webFetch) with typed results, credential validation, REST routing, and MCP selection - fallback-only ([#11690](https://github.com/diegosouzapw/OmniRoute/pull/11690)) — thanks @Xxx91n +- **feat(dashboard):** display clamped `[0, 100]%` cached input token ratio in request logs table ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) — thanks @b3nw +- **feat(catalog):** add `OMNIROUTE_DISABLE_THINKING_LEVEL_VARIANTS` feature flag to optionally filter out thinking level variants from model catalog ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) — thanks @b3nw +- **feat(dashboard):** continuously export call logs to external analytics stores. A pluggable destination registry ships the full Logs-tab record set on an hourly `JobRegistry` cron, with a persisted per-destination cursor, batched inserts, a config UI rendered from each destination's own field descriptors, and a REST layer (`/api/log-export/*`) for CRUD, a connection test, and an on-demand run. A destination can opt into `includeBodies` to also ship the request and response payloads shown in the Logs detail pane, including the client and provider views of each call; this is off by default, and payloads inherit the dashboard's PII sanitisation, secret redaction and `noLog` handling. Google BigQuery is the first destination, using a service-account key stored encrypted at rest and streaming inserts keyed by call-log id, into a table that is day-partitioned on `timestamp` and clustered on `api_key_name`, `provider`, `model` and `status`. ([#11945](https://github.com/diegosouzapw/OmniRoute/pull/11945)) — thanks @dpozimski +- **feat(providers):** add MaxAI as a signed, OpenAI-compatible provider serving its 13 paid chat models (GPT-5.6 / Luna / Thinking, Claude 5 Sonnet, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite, Grok 4.1-fast / 4.5, DeepSeek V3.2 / R1, Llama 3.3 70B) through OmniRoute's `/v1` endpoint, with per-request HMAC-SHA1→SM3→AES request signing, live model + context-window discovery from `/models/get_config`, and prompted tool-calling translated to OpenAI `tool_calls` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI vision input — image_url content parts are forwarded inline in `message_content` to the 6 vision-capable models (GPT-5.6 / Luna / Thinking, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite) ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI image generation — 6 image models (gpt-image-1, dall-e-3, flux-1-schnell/dev/pro, sd3-medium) exposed through `POST /v1/images/generations` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI document RAG — inline base64 file/document attachments are uploaded to MaxAI (content-addressed `doc_id`) and attached to the chat via `doc_list` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** browserless MaxAI onboarding — email device-pair login (`/api/providers/[id]/login`) and signed access-token refresh, so a connection can be created and kept fresh without a real browser or Google OAuth ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** per-provider TLS impersonation profile (MaxAI presents a Windows Firefox-150 client fingerprint) so its bot-sensitive endpoints accept OmniRoute traffic ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** add **Nimble** as a web-search and web-fetch provider (`nimble-search`) — `/v1/search` routes to Nimble's search API at `lite` depth (locale, freshness and include/exclude domain filters mapped onto the shared request contract), and `/v1/web/fetch` routes to Nimble Extract, which covers all four fetch formats (`markdown`, `html`, `links`, `screenshot`) from a single call. One API key serves both surfaces. ([#11620](https://github.com/diegosouzapw/OmniRoute/pull/11620)) — thanks @wildcard +- The npm publish is automatic again, through npm Trusted Publishing (OIDC): the hosted `stage-npm` job publishes with a short-lived credential minted from GitHub's id-token — no `NPM_TOKEN`, no 2FA prompt, provenance attached. `publish_mode=staged` (owner approves with 2FA) and `direct` (token) remain available on `workflow_dispatch`. ([#11931](https://github.com/diegosouzapw/OmniRoute/pull/11931)) +- **feat(providers):** Add **Opper** as an API-key gateway provider — EU-hosted AI gateway with 700+ models from 30+ providers behind one OpenAI-compatible API and one key (`OPPER_API_KEY`); model ids use `provider/model` format (e.g. `anthropic/claude-sonnet-4-6`, `openai/gpt-5`); live model catalog at `https://api.opper.ai/v3/compat/models`; entry mirrors `requesty` (same shape, `passthroughModels: true`, no static seed) ([#11629](https://github.com/diegosouzapw/OmniRoute/pull/11629)) +- **feat(dashboard):** the `/dashboard/orchestration` snapshot hook now subscribes to the `agents` WebSocket channel (`agent.task.updated`) instead of `requests` as its refetch trigger, and relaxes its background poll from 5s to 30s while that WS connection is up — falling back to the tighter 5s cadence, reprogrammed live on any connect/disconnect transition, whenever the socket is down. ([#12409](https://github.com/diegosouzapw/OmniRoute/pull/12409)) +- **feat(dashboard):** Orchestration canvas quick wins — search box plus state/source/provider filter chips with a one-click clear, and per-source collapse/expand, all reflected in the URL so a filtered/collapsed view is shareable and survives a refresh; the detail drawer gained a "copy trace JSON" action and hardened error/empty-state and accessibility handling; the Agents-tab edges now animate traveling particles along active (running) connections; and the canvas node/edge status colors moved off fixed hex values onto theme-aware `--orch-status-*` CSS custom properties, so they adapt correctly to light/dark mode. ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- **chore(dashboard):** Orchestration UI hardening pass and the missing component/model test coverage it called for — `OrchestratorNode`/`ActivityNode`/`OverflowNode` rendering, the `?node=`/overflow-click page routing, the Agents-tab orchestrator-click no-op and `showCompleted` toggle, and the overview kanban's done-column sort order (#12270, #12271). ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- New `/dashboard/orchestration` page: live unified view of everything running — Cloud Agent, A2A and Conductor as a real-time graph (Agents tab), the combo cascade (Routing tab, reusing the Combo Live Studio) and a state kanban (Overview tab), with a detail drawer (trace, cost, approve/cancel). Read-only over existing APIs — no new backend. Canvas concept credit: PR #11815 design ([#12261](https://github.com/diegosouzapw/OmniRoute/pull/12261)) +- **feat(dashboard):** new "History" tab on `/dashboard/orchestration` — an Airflow-style grid of finished runs over a 24h/7d/30d preset window, one row per (source, identity), clicking a cell opens the existing detail drawer. It is backed by real persistence: A2A task lifecycle transitions are now written to the `a2a_tasks` table (purged after 30 days, configurable via `OMNIROUTE_A2A_HISTORY_RETENTION_DAYS`) and served by the new `GET /api/a2a/tasks/history` listing endpoint, with the task-detail route falling back to persisted history once a run leaves the in-memory snapshot. Conductor runs stay remote and are not persisted locally — the tab says so instead of silently omitting them. ([#12479](https://github.com/diegosouzapw/OmniRoute/pull/12479)) +- **feat(dashboard):** the orchestration detail drawer gained a "Repeat" action for Cloud Agent, A2A and Conductor tasks — a two-click confirm (click once to arm, click again within the confirm window to fire) re-submits the original prompt/input as a new run. The button is disabled with an explanatory tooltip whenever the original input can't be recovered from the loaded task detail (e.g. it never carried a prompt, or the detail failed to load). Two limitations of the A2A variant, by design: it targets the `/a2a` JSON-RPC endpoint, which authenticates with an API key only (`REQUIRE_API_KEY=true` or a configured `OMNIROUTE_API_KEY` makes a dashboard-session repeat answer `HTTP 400` — surfaced verbatim in the drawer's error line, never as a success), and the `message/send` call is SYNCHRONOUS: the POST blocks for the whole skill run, so the success confirmation only appears once the repeated task finishes. A dashboard-authenticated A2A creation path is deliberately left to a follow-up — widening the endpoint's auth posture is an operator decision, not a side effect of this feature. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(a2a):** A2A task execution now records which memories were consulted for the task's last user message as `metadata.memoryHits` (id/key/type/content-snippet) plus a `memory_hits` history event, purely for observability — the retrieved memory is never injected into a skill's prompt or behavior. Gated by the `OMNIROUTE_A2A_MEMORY_HITS` kill-switch (default enabled; set to `0` to skip the recall lookup entirely). The drawer's new "Memory used" section lists these hits for a2a tasks and is omitted whenever there are none. Known limitation: recall only resolves under the keyless posture — a keyed caller's task owner is a SHA-256 prefix of the API key, while memory rows are keyed by the database api-key id, and no hash→id lookup exists today, so the hit list stays empty for keyed callers. The recorded hits are also kept out of the task's own `input` (and therefore out of the persisted input and of the "Repeat" request body), so repeating a task never re-sends the previous run's memory snippets. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(providers):** add a Perplexity Agent API provider (`perplexity-agent` / `pplx-agent`) for Perplexity `/v1/responses`, including the documented Anthropic, OpenAI, Google, xAI, DeepSeek, Z.AI, Moonshot/Kimi, NVIDIA, and Perplexity model IDs plus Anthropic-model `max_output_tokens` compatibility. ([#12103](https://github.com/diegosouzapw/OmniRoute/pull/12103)) — thanks @AIB1TAL0S +- **feat(providers):** add RPD (Requests Per Day) limit to provider rate limit overrides across UI, schemas, DB, and i18n ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) — thanks @b3nw +- **feat(providers): add UC Direct (uncensored.com Developer API), the metered OpenAI-compatible surface.** A standard OpenAI-compatible passthrough (default executor) for uncensored.com's official REST API at `https://api.uncensored.com/api/v1`: `X-api-key` auth (never-expiring `uai_sk_live_` key), `POST /chat/completions` with streaming SSE and native tool-calling, and the full live metered catalog (82 models across 15 providers, discovered from the public `GET /v1/models`). Registered as provider `uc-direct` (alias `ucd`). Complements the un-metered `uc` persona provider — same models, metered credits and a plain API key instead of a subscription session. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(providers): add UC (uncensored.com), the un-metered subscription "persona" chat as an OpenAI-compatible provider.** A WebSocket web-app port: a durable Clerk credential mints a short-lived session token per connect (browserless — no API key), driving UC's persona socket. Ships the browserless email-code login (request → verify → harvest), the 19 verified persona models (Claude Opus, Gemini, Grok, GLM, Kimi, DeepSeek, MiniMax, incl. the uncensored variants), prompted `` tool-calling with a per-model code-style dialect + auto-cure retry for guardrailed models, live ``/reasoning split, streaming + non-streaming OpenAI responses, and full quota/auth error surfacing (paywall / message-limit / rate-limit → 429, invalid session → 401 re-login). Full multimodal parity via the persona blob-upload layer: **vision** (image input, 15 vision-capable models), **document RAG** (PDF/doc upload, server-side extraction), **image generation** (22 models), **video generation** (14 models, async signed-url → poll), and **TTS** (streaming MP3). Registered as provider `uc` (alias `ucn`). The metered OpenAI-compatible Developer API is a separate `uc-direct` provider. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(logs):** add copy-all button to request detail modal ([#11083](https://github.com/diegosouzapw/OmniRoute/pull/11083)) — thanks @AndrianBalanescu +- **feat(check):** make serviceKinds required and add the reverse-walk provider consistency gate ([#11392](https://github.com/diegosouzapw/OmniRoute/pull/11392)) — thanks @Tushar49 +- **feat(providers):** only advertise verified-working connections by default ([#11448](https://github.com/diegosouzapw/OmniRoute/pull/11448)) — thanks @alltomatos +- **feat(resilience):** add hierarchical concurrency admission ([#11493](https://github.com/diegosouzapw/OmniRoute/pull/11493)) +- **feat(providers):** derive + surface expiry for JWT-bearing web cookies (#11497) ([#11505](https://github.com/diegosouzapw/OmniRoute/pull/11505)) — thanks @oyi77 +- **feat(tunnels):** support named cloudflared tunnels via CLOUDFLARED_CONFIG ([#11561](https://github.com/diegosouzapw/OmniRoute/pull/11561)) — thanks @ggiak +- **feat(compression):** make proactive context-compression threshold a live setting ([#11564](https://github.com/diegosouzapw/OmniRoute/pull/11564)) — thanks @ragnar-claude +- **feat(resilience):** add provider peak-hour protection ([#11622](https://github.com/diegosouzapw/OmniRoute/pull/11622)) — thanks @Neuron-Mr-White +- **feat(providers):** add 1min.ai provider ([#11631](https://github.com/diegosouzapw/OmniRoute/pull/11631)) — thanks @zero-executioner +- **feat(dashboard):** collapsible JSON tree viewer for request/response payloads ([#11703](https://github.com/diegosouzapw/OmniRoute/pull/11703)) — thanks @hartmark +- **feat(routing):** add LiquidAI LFM2.5-2.6B free tier via OpenRouter ([#11752](https://github.com/diegosouzapw/OmniRoute/pull/11752)) — thanks @brick30llc-ctrl +- **feat(pricing):** add GLM-5.3-Flash pricing, model specs, and catalog registration ([#11830](https://github.com/diegosouzapw/OmniRoute/pull/11830)) — thanks @AndrianBalanescu +- **feat(routing):** add relayMode for schema-locked context handoffs ([#11839](https://github.com/diegosouzapw/OmniRoute/pull/11839)) — thanks @raheemuddin786 +- **feat(config):** add support for runtime system prompt configuration and hot-reloading ([#11841](https://github.com/diegosouzapw/OmniRoute/pull/11841)) — thanks @raheemuddin786 +- **feat(routing):** expand connection-aware quota prefilter across combo strategies (#11682) ([#11850](https://github.com/diegosouzapw/OmniRoute/pull/11850)) — thanks @HouMinXi +- **feat(models):** reactive discovery sync after Antigravity model-not-found 404 ([#11888](https://github.com/diegosouzapw/OmniRoute/pull/11888)) — thanks @MumuTW +- **feat(models):** auto-aliases and token limits from synced Antigravity discovery ([#11889](https://github.com/diegosouzapw/OmniRoute/pull/11889)) — thanks @MumuTW +- **feat(settings):** raise sticky round-robin limit caps to 1000 ([#12015](https://github.com/diegosouzapw/OmniRoute/pull/12015)) — thanks @adivekar-utexas +- **feat(resilience):** operator-configurable global credential health check interval ([#12043](https://github.com/diegosouzapw/OmniRoute/pull/12043)) — thanks @adivekar-utexas +- **feat(quota):** use official OpenCode Go usage API ([#12124](https://github.com/diegosouzapw/OmniRoute/pull/12124)) — thanks @ddarkr +- **feat(resilience):** default the credential health check sweep to 60 minutes ([#12138](https://github.com/diegosouzapw/OmniRoute/pull/12138)) — thanks @adivekar-utexas +- **feat(quality):** new-code mode for the complexity and dead-code ratchets (Clean as You Code) ([#12142](https://github.com/diegosouzapw/OmniRoute/pull/12142)) +- **feat(dashboard):** orchestration canvas — unified model + snapshot hook (part 1/2) ([#12156](https://github.com/diegosouzapw/OmniRoute/pull/12156)) +- **feat(usage):** add Kilo Code balance and Kilo Pass quotas ([#12178](https://github.com/diegosouzapw/OmniRoute/pull/12178)) — thanks @killer30001000 +- **feat(providers):** refresh vendored ChatGPT Web connector to v4.0.7 ([#12181](https://github.com/diegosouzapw/OmniRoute/pull/12181)) — thanks @backryun +- **feat(providers):** manual "Clear cooldown" action in the cooling panel ([#12224](https://github.com/diegosouzapw/OmniRoute/pull/12224)) — thanks @patrykkopycinski +- **feat(providers):** restore ChatGPT Web via clean-room browser transport ([#12239](https://github.com/diegosouzapw/OmniRoute/pull/12239)) — thanks @backryun +- **feat(sse):** wire the PROVIDER_PROFILES window gate into the global provider cooldown ([#12247](https://github.com/diegosouzapw/OmniRoute/pull/12247)) +- **feat(providers):** modernize CLOVA Studio chat and embeddings ([#12277](https://github.com/diegosouzapw/OmniRoute/pull/12277)) — thanks @backryun +- **feat(browser):** adopt Obscura as primary headless browser engine with Chromium fallback ([#12286](https://github.com/diegosouzapw/OmniRoute/pull/12286)) — thanks @opensource-elearning +- **feat(sse):** full language parity for output styles (es/de/fr/it/ru/zh + autoDetect) ([#12289](https://github.com/diegosouzapw/OmniRoute/pull/12289)) +- **feat(quality):** complete test:scoped — --full map rebuild, stdin selection, CI loader parity (#8084 D1) ([#12353](https://github.com/diegosouzapw/OmniRoute/pull/12353)) +- **feat(providers):** refresh Fable, Cursor, and Devin catalogs ([#12367](https://github.com/diegosouzapw/OmniRoute/pull/12367)) — thanks @backryun +- **feat(video):** redact transcript text from logs and durable memory (#12150 P1) ([#12427](https://github.com/diegosouzapw/OmniRoute/pull/12427)) +- **feat(dashboard):** parent-link, genuine-continuation badge, and modal perf fixes ([#12448](https://github.com/diegosouzapw/OmniRoute/pull/12448)) — thanks @hartmark +- **feat(dashboard):** adaptive context-budget dial on compression panel ([#12488](https://github.com/diegosouzapw/OmniRoute/pull/12488)) — thanks @HouMinXi +- **feat(providers):** refresh Z.ai Web models and browser transport ([#12524](https://github.com/diegosouzapw/OmniRoute/pull/12524)) — thanks @backryun +- **feat(video):** redact raw client-snapshot transcript fields in the detailed log (#12150 P2) ([#12528](https://github.com/diegosouzapw/OmniRoute/pull/12528)) +- **feat(providers):** refresh NVIDIA hosted models ([#12538](https://github.com/diegosouzapw/OmniRoute/pull/12538)) — thanks @backryun +- **feat(quota):** Moonshot Open Platform balance and TPD lock for custom nodes ([#12590](https://github.com/diegosouzapw/OmniRoute/pull/12590)) — thanks @HouMinXi +- **feat(video):** redact transcript in the in-memory pending-request snapshot (#12430 item 6) ([#12596](https://github.com/diegosouzapw/OmniRoute/pull/12596)) +- **feat(dashboard):** link a log entry's Conversation Context to its owning conversation ([#12646](https://github.com/diegosouzapw/OmniRoute/pull/12646)) — thanks @hartmark +- **feat(catalog):** eligibility-gated free-tier bucket ([#12669](https://github.com/diegosouzapw/OmniRoute/pull/12669)) +- **feat(db):** fail-closed previous_response_id continuation for redacted video turns (#12150 P2b) ([#12707](https://github.com/diegosouzapw/OmniRoute/pull/12707)) +- **feat(gamification): pay the documented `streak_bonus` and `badge_unlock` XP rewards.** `XP_REWARDS` listed both rewards but the award pipeline never paid them: the private reward table in `events.ts` omitted them, `updateStreak()` did not report when a streak extended, and badge unlocks carried no XP. Every request that extends a daily streak now pays `streak_bonus × streak length` once per UTC day (guarded by a same-day `xp_audit_log` check), and every badge unlocked through the pipeline pays `badge_unlock` once per badge (guarded by the `user_badges` primary key; `unlockBadge()` now reports whether it inserted). Bonus XP flows through the same `addXp` + level sync + global/weekly/monthly leaderboard path as action XP, so level-ups and rankings include it. The Radar supporter recognition unlock stays XP-free. (#12522 — thanks @pacocartones) +- **feat(providers):** advertise a `free-tier` capability in the provider plugin manifest for every provider with documented free models, so sidecars and dashboards can filter free-capable providers without reading the quota catalog ([#12786](https://github.com/diegosouzapw/OmniRoute/pull/12786)) — thanks @maxmad64bis +- **feat(providers):** Added EURouter as an OpenAI-compatible API-key gateway (`https://api.eurouter.ai/v1`), with live model discovery via `passthroughModels`. Its copy states that models are served by third-party upstreams listed per model, so an EU-based router is not read as EU data residency for inference. ([#13025](https://github.com/diegosouzapw/OmniRoute/pull/13025)) — thanks @ntdatt812 +- **feat(providers):** Added GreenPT as an OpenAI-compatible API-key provider (`https://api.greenpt.ai/v1`), with live model discovery via `passthroughModels`. No free-inference badge: the published docs describe a free API subscription billed per token, not a free tier. ([#13024](https://github.com/diegosouzapw/OmniRoute/pull/13024)) — thanks @ntdatt812 +- **feat(i18n):** 9 new locales — Greek (`el`), Croatian (`hr`), Serbian (`sr`), Lithuanian (`lt`), Estonian (`et`), Latvian (`lv`), Slovenian (`sl`), Maltese (`mt`), Irish (`ga`) — across the dashboard, docs mirrors, CLI, README and the site; OmniRoute now ships every official EU language (51 locales). (#13044) +- **feat(i18n):** 8 new locales — Kannada (`kn`), Malayalam (`ml`), Odia (`or`), Punjabi (`pa`), Nepali (`ne`), Sinhala (`si`), Burmese (`my`), Khmer (`km`) — across the dashboard, docs mirrors, CLI, README and the site (59 locales). The translator now restores the ICU literal escape around angle placeholders and splits oversized docs sections before translating. (#0000) ([#13660](https://github.com/diegosouzapw/OmniRoute/pull/13660)) +- feat(providers): **list Agnes 3.0 Flash as the current free chat model, drop retired 1.5 Flash, add Image 2.0/2.5 Flash plus Video 2.5/2.5 Flash, and discover the live `/v1/models` catalog (including the CN host `api.agnes-ai.cn`).** `agnes-1.5-flash` now forwards to `agnes-3.0-flash`. Video 2.5 polls `GET /v1/videos/{id}` (not the V2.0 `/agnesapi` contract). Live `/v1/models` (2026-09-09) no longer serves 1.5; the wiki marks it deprecated. 3.0 Flash is 512K context / 65,536 max output, same window as 2.5. CN-region keys use the existing per-connection base-URL field, default stays `apihub.agnes-ai.com`. ([#13120](https://github.com/diegosouzapw/OmniRoute/pull/13120)) — thanks @HouMinXi +- **feat(sse):** Codex and OpenAI catalogs list GPT-6 Astra with effort aliases (`-low` through `-ultra`); Codex CLI identity pins `@openai/codex@0.153.4` in lockstep with the image ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **feat(dashboard):** the orchestration History tab gained a "Compare runs" mode — toggling it turns each grid cell into a 2-item selection queue (a 3rd click drops the oldest pick), and picking two cells opens a side-by-side comparison panel instead of the usual detail drawer. The panel fetches both runs' detail the same way the drawer does (falling back to persisted history once a run leaves the live TTL window) and shows, per side: identity/source/state, start time, a signed `right - left` delta for duration/cost/event count, the event timeline aligned by index, and any memory hits. One side's fetch failing never blocks the other, and a delta is only ever computed when both sides have a finite value — otherwise it renders "—", never `NaN`. Comparing two runs from different sources or skills still works; a banner marks the deltas as informational rather than hiding them, since the two runs aren't a strict apples-to-apples pair. ([#12677](https://github.com/diegosouzapw/OmniRoute/pull/12677)) +- **feat(dashboard):** badge a conversation that never reached a clean stop ([#12717](https://github.com/diegosouzapw/OmniRoute/pull/12717)) — thanks @hartmark +- **feat(release):** reconcile-changelog tool + version-anchored fragment aggregation ([#12987](https://github.com/diegosouzapw/OmniRoute/pull/12987)) +- **feat(mcp):** carry context_length through MCP list_models_catalog projection (#12776) ([#13406](https://github.com/diegosouzapw/OmniRoute/pull/13406)) — thanks @KooshaPari +- **feat(cli):** make startup readiness budget configurable (#13369) ([#13433](https://github.com/diegosouzapw/OmniRoute/pull/13433)) — thanks @KooshaPari + ### 🐛 Bug Fixes +- **security(streaming):** sanitize generic mid-stream error messages before emitting OpenAI, + Responses, or Claude SSE failure frames and before diagnostic logging, while preserving raw + failures for internal classification and keeping client disconnects out of provider failure state. ([#12457](https://github.com/diegosouzapw/OmniRoute/pull/12457)) +- **fix(claude):** `blockExtraUsage: false` no longer lets 5h quota preflight skip the account; extra usage is billed after the session bar is gone, so the request must reach Anthropic ([#12803](https://github.com/diegosouzapw/OmniRoute/pull/12803)) — thanks @HouMinXi +- Sanitize HuggingChat conversation-creation and message-send transport failures before they reach client error bodies or provider logs. ([#12467](https://github.com/diegosouzapw/OmniRoute/pull/12467)) +- **fix(dashboard):** Keep local and theme-aware provider SVG icons at a definite layout size so Chromium does not collapse them to 0×0 after the v3.8.50 image-rendering change ([#12054](https://github.com/diegosouzapw/OmniRoute/pull/12054)) — thanks @ponkcore +- **fix(security):** Sanitize provider and runtime failures before public API, SSE and MCP responses and before persistent request, proxy and usage logs, preventing credentials, stack traces and host filesystem paths from crossing those boundaries while preserving stable error codes and useful diagnostics. ([#12506](https://github.com/diegosouzapw/OmniRoute/pull/12506)) +- **fix(cli):** `omniroute tunnel create` no longer crashes with `Cannot read properties of undefined (reading optsWithGlobals)` — removed the duplicate positional argument that caused Commander.js to misalign the action callback parameters ([#12295](https://github.com/diegosouzapw/OmniRoute/issues/12295)) ([#12368](https://github.com/diegosouzapw/OmniRoute/pull/12368)) — thanks @gonisulaimann +- Stop advertising Gemini Live-only models as supported audio endpoints until OmniRoute proxies the bidirectional Live protocol. ([#11710](https://github.com/diegosouzapw/OmniRoute/pull/11710)) — thanks @RaviTharuma +- **fix(kie):** reroute `flux/kontext` off the KIE Market `createTask` flow — it is catalogued with `isMarket: true` but has no Market catalog page, so KIE rejected it with "model name not supported"; it now hits the dedicated `POST /api/v1/flux/kontext/generate` / `GET /api/v1/flux/kontext/record-info` endpoints instead (#11296). ([#11985](https://github.com/diegosouzapw/OmniRoute/pull/11985)) — thanks @hartmark +- **fix(kie):** correct 12 more KIE Market catalog ids that were sent to `createTask` unchanged but diverge from KIE's documented upstream `model` values — GPT Image 2 T2I/I2I (drops the `gpt/` prefix), GPT Image 1.5 T2I/I2I (`gpt-image/` namespace), Seedream 5.0 Lite T2I/I2I (drops the `.0`), all 4 Flux 2 variants (`flux-2/` namespace, generic variant renamed `flex`), and Wan 2.7 Image / Image Pro (dash instead of dot) — each verified individually against the literal example request published on docs.kie.ai. `#11326`'s "everything else already matches" claim was wrong a second time (#11296); `z-image/4.0-*`/`z-image/4.5-*` and `flux/kontext` remain open, documented as unresolved in `KIE_MARKET_UPSTREAM_MODEL_IDS`'s comment pending further verification. ([#11667](https://github.com/diegosouzapw/OmniRoute/pull/11667)) +- **fix(dashboard):** `useApiKeySave.handleSaveApiKey` no longer forces a full upstream `/models` catalog sync on every non-curated provider connection save — callers can now pass `skipModelSync: true` to opt out, so a workflow that only wants to add one manual model no longer floods the provider's available-models list with hundreds/thousands of synced entries. The flag is a client-side intent signal only and is stripped before the connection payload is POSTed to `/api/providers`; default behavior (full sync on save) is unchanged when the flag is omitted (#11324) ([#11664](https://github.com/diegosouzapw/OmniRoute/pull/11664)) +- **Provider connections:** keep `tokenExpiresAt` when a connection is created. The create-path allowlist omitted it, so every insert stored NULL and the dashboard token badge could read a fresh connection as expired until its first background refresh ([#11368](https://github.com/diegosouzapw/OmniRoute/pull/11368)). — thanks @ntdatt812 +- **fix(combos):** the combo builder's precision-select, global-model-search, and manual-entry flows now serialize a model step's `model` string using the provider's already-computed routing-alias prefix (e.g. `oc/`) instead of rebuilding it from the raw canonical `providerId`, fixing the no-auth "OpenCode Free" provider (`opencode`) being routed to the unrelated paid "OpenCode Zen" provider (`opencode-zen`) because `opencode` doubles as a manual routing-prefix override ([#11433](https://github.com/diegosouzapw/OmniRoute/issues/11433)). ([#11670](https://github.com/diegosouzapw/OmniRoute/pull/11670)) +- **fix(radar):** the catalog feed cache now keeps `generatedAt`, the date the feed's data was built, next to `fetchedAt`, the date this install downloaded it (#11435). The feed schema requires that date and the sync path validates it, but the cache dropped it — so a feed fetched minutes ago and one carrying weeks-old figures looked identical to everything downstream, including the dashboard's "Last fetched" line. `getRadarCatalog().meta` and `GET /api/radar/status` now report both dates, the latter as its own field rather than folded into `version` — and omitted entirely for the offers and intel caches, which keep no build date, where a `null` would read as "unknown" rather than "never stored". The dashboard still shows only the fetch time; surfacing the build date there needs a new translated label and is left to a follow-up. Rows cached before migration 163 read back as `null`: unknown stays unknown instead of borrowing the fetch time. The referrals cache has persisted the same date since migration 142. — thanks @maxmad64bis +- **fix(cli):** postinstall no longer fills `JWT_SECRET` and `API_KEY_SECRET` in the installed package's `.env` (#11436). `.env.example` ships both blank on purpose: the server restores them from its durable store, or generates and persists them there, in `ensureSecrets()`. Pre-filling them defeated that — the file lives inside the package directory, so `npm i -g` replaced it and postinstall wrote _different_ values, while `ensureSecrets()` (which only acts on an empty variable) never got to restore the real ones. Both secrets rotated silently on every update, invalidating dashboard sessions and API-key CRCs. `STORAGE_ENCRYPTION_KEY` left the same list for the same reason in #1622; its comment pointed at a function that no longer exists and now names the real provisioning path. — thanks @maxmad64bis +- **fix(cli):** the CLI now says when a loaded `.env` lives inside the installed package directory (#11437). It already announces every env file it reads, without distinguishing the ones that survive an update from the one that does not: `npm i -g` replaces the package directory wholesale, so values set there are gone at the next update, silently. The warning names the durable path to move them to, and fires only when that file actually supplied a value — a file entirely shadowed by a durable one supplied nothing. A development checkout stays silent: there the same path is stable and documented in `SETUP_GUIDE.md`. — thanks @maxmad64bis +- **fix(free-models):** the shared `isFreeModel()` predicate no longer reports catalog entries marked `freeType: "discontinued"` as free, so `hidePaidModels` can't route to Pollinations' seven premium models that now require a paid key ([#11441](https://github.com/diegosouzapw/OmniRoute/pull/11441)) — thanks @maxmad64bis +- fix(ui): let AnySearch use the normal provider-icon fallback when LobeHub has no matching icon (#11449) +- Fixed the v3.8.50 Costs and Analytics dashboards so flat-rate Claude Code usage can be shown as an explicitly requested token-price estimate without changing default billed-cost semantics. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed archived usage retention so each request is priced individually instead of pricing a day's summed tokens once, which understated archived cost whenever a day mixed cache-heavy and ordinary requests. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed the Costs dashboard so it discloses when displayed figures include flat-rate token-price estimates instead of labelling them as billed spend, using the flag the analytics API already returns; the month-end projection and the CSV/JSON exports carry the same marker, and billed-cost mode is unchanged. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- **fix(combo):** attach the same combo-diagnostics trace (`poolSize`/`attemptOrder`/`excluded`/`terminalReason`, plus `x-omniroute-combo-*` headers) to the round-robin strategy's and the nested pipeline/fusion runtime-unit loop's "Maximum combo retry limit reached" 503 that the priority-strategy path already attaches for the identical terminal condition — previously those two paths returned a bare, contextless 503 ([#11462](https://github.com/diegosouzapw/OmniRoute/issues/11462)). ([#11665](https://github.com/diegosouzapw/OmniRoute/pull/11665)) +- **fix(bun):** use native `bun:sqlite` in `bootstrap-env` and `sync-env` to avoid loading `better-sqlite3` N-API addon during Bun startup ([#11468](https://github.com/diegosouzapw/OmniRoute/pull/11468)) — thanks @TheDemonTuan +- **fix(docker-bun):** make `Dockerfile.bun` install reliable with pre-install script helpers and native `bun:sqlite` smoke check ([#11470](https://github.com/diegosouzapw/OmniRoute/pull/11470)) — thanks @TheDemonTuan +- **fix(docker-bun):** remove every vendored `better-sqlite3` native addon from the Bun runtime image so startup cannot abort before the `bun:sqlite` fallback ([#11482](https://github.com/diegosouzapw/OmniRoute/pull/11482)) — thanks @TheDemonTuan +- **fix(autoCombo):** effort/alias model variants (`gpt-5.6-sol-xhigh`, `gpt-5.6`, cursor's `claude-4.6-opus-high`) inherit their base model's task fitness instead of falling to the wildcard 0.5 ([#11492](https://github.com/diegosouzapw/OmniRoute/pull/11492)) — thanks @MumuTW +- fix(db): rate-limit repeated Arena ELO leaderboard fetch-failure warnings instead of logging one per sync attempt (#11500) ([#11989](https://github.com/diegosouzapw/OmniRoute/pull/11989)) — thanks @hartmark +- **fix(db):** dedupe the raw `[Encryption] Decryption failed...` log line emitted by the lazy-decrypt views (`createLazyRowProxy`/`createLazyConnectionView`), which power `getProviderConnections()` and were re-triggering that line on every CredentialHealth/model-sync cycle for the same corrupt or stale-key credential — a fresh Proxy over a fresh row on every cycle meant the per-proxy memoization never suppressed it, unlike the dedup `decryptConnectionFields()` already had since [#9927](https://github.com/diegosouzapw/OmniRoute/issues/9927). Now shares that dedupe tracking so the line logs at most once per credential ([#11500](https://github.com/diegosouzapw/OmniRoute/issues/11500)). ([#11678](https://github.com/diegosouzapw/OmniRoute/pull/11678)) +- **fix(intelligence):** Arena ELO sync stores a synthesized base-model row for every effort/harness variant (`claude-opus-5-max` → `claude-opus-5`, `gpt-5.6-sol-xhigh (codex-harness)` → `gpt-5.6-sol`), so bare model requests reach the synced intelligence layer instead of the static fallback table; the hand-maintained `MODEL_ALIAS_MAP` — which collapsed generations (`gpt-5.5` → `gpt-5`) — is gone ([#11506](https://github.com/diegosouzapw/OmniRoute/pull/11506)) — thanks @MumuTW +- **fix(autoCombo,sse):** catalog hygiene — the static task-fitness table no longer ranks retired models above live ones (dead rows dropped, patterns matched on segment boundaries), the 7 deprecation aliases that forwarded to retired or non-existent models now point at the vendors' published replacements, an alias is no longer rewritten when the serving provider offers the id as-is, and a new `check:model-lifecycle` gate diffs both tables against a vendor lifecycle snapshot ([#11507](https://github.com/diegosouzapw/OmniRoute/pull/11507)) — thanks @MumuTW +- **fix(sse):** stop dropping resolved `thoughtSignature` values on parallel (multi tool-call) turns sent to Gemini 3.x — the claude→gemini and openai→gemini translators previously kept the signature only on the *first* function call of a message, causing Gemini to reject subsequent calls in the same turn with HTTP 400 "Function call is missing a thought_signature"; each function call now keeps its own resolved signature ([#11510](https://github.com/diegosouzapw/OmniRoute/issues/11510)). ([#11669](https://github.com/diegosouzapw/OmniRoute/pull/11669)) +- Treat Docker publishing from a non-default release branch as an expected skip instead of a failed workflow, while preserving the guard that prevents it from overwriting the `next` channel. ([#11527](https://github.com/diegosouzapw/OmniRoute/pull/11527)) — thanks @pacocartones +- fix(search): prefer credentialed providers over duckduckgo-free fallback (#11524) — the fallback-only loop ran before the credentialed-providers loop in `executeWebSearch`, making configured providers unreachable when `duckduckgo-free` was available. ([#11565](https://github.com/diegosouzapw/OmniRoute/pull/11565)) — thanks @vsd2807 +- **fix(sse):** cap the upstream headers-wait phase for STREAMING requests to a client-realistic ceiling (110s, under Codex's own ~120s hard client-abort window) instead of the flat 10-minute `FETCH_TIMEOUT_MS` default — that default was 5x longer than the body-phase readiness watchdog's own adaptive bound, so a request whose upstream never returned any response at all (not even headers, e.g. a stalled NVIDIA target behind a tool-heavy Responses→Chat translation) kept the client connection alive on keepalives only, guaranteeing the client's own patience ran out first with an opaque 499 instead of OmniRoute detecting and failing the stall fast. Non-streaming requests are unaffected — they keep the existing flat default (`open-sse/utils/fetchStartTimeoutPolicy.ts`) (#11526) ([#11668](https://github.com/diegosouzapw/OmniRoute/pull/11668)) +- **fix(ci):** protocol-clients E2E harness boots the peer-stamped custom server (`run-next.mjs dev`) instead of the bare `next` CLI, so `LOCAL_ONLY` locality resolves from the real TCP peer stamp and `/api/mcp/audit` answers 200 (open bootstrap preserved via an `OMNIROUTE_E2E_BOOTSTRAP_MODE=open` credential clear in `run-next.mjs`, pinned to loopback `HOST`) instead of a deterministic 403 (#11535) ([#11549](https://github.com/diegosouzapw/OmniRoute/pull/11549)) — thanks @jonlwheat2-gif +- **fix(docs):** the Auto-Combo engine is described with the number of factors it actually declares. Four different counts were in circulation — 6, 9, 13 and 14 across `AGENTS.md`, four guides and six code comments — while `DEFAULT_WEIGHTS` declares 15. `check:docs-counts` now reads that number from the source, so adding a factor without updating the prose fails CI ([#11545](https://github.com/diegosouzapw/OmniRoute/pull/11545)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **fix(quota-share):** in-flight leases are per request, so an aborted request's slot ages out instead of being kept alive by later traffic on the same connection ([#11547](https://github.com/diegosouzapw/OmniRoute/pull/11547)) — thanks @abhisheksharma2411 +- The free-tier summary route now serves the refreshed Radar catalog when the feed is active — the same numbers the dashboard shows — and states which catalog answered plus its real build date, instead of always reporting release-frozen figures with a stale curation date. The supporter-key live feed stays reserved to authenticated callers of the instance. ([#11550](https://github.com/diegosouzapw/OmniRoute/pull/11550)) — thanks @maxmad64bis +- **sse:** fix LiveWS/embed-WS servers crashing at startup under the Node/tsx runtime — `liveServer.ts`, `embedWsProxy.ts` and `apiBridgeServer.ts` imported `@/shared/utils/httpClientAbortGuard` without the `.mjs` extension, so the client-abort crash guard added by [#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556) was unreachable and every dependent test failed with `ERR_MODULE_NOT_FOUND` ([#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556)). ([#11671](https://github.com/diegosouzapw/OmniRoute/pull/11671)) — thanks @jonlwheat2-gif / @yxyxy +- fix(chat): shed any of the matched provider-node's routing identifiers (public prefix or internal id) before model lookup, so composites like `//` no longer reach upstream verbatim (#11557) — thanks @maxmad64bis +- **fix(cache):** a semantic-cache entry now expires on its TTL instead of surviving until the next UTC midnight, and `dbEntries` stops counting expired rows ([#11573](https://github.com/diegosouzapw/OmniRoute/pull/11573)) — thanks @ntdatt812 +- **fix(api):** `/v1/models` schedules its stale-while-revalidate rebuild through Next's `after()` again, so a stale catalog reaches the client before the rebuild blocks the event loop ([#11574](https://github.com/diegosouzapw/OmniRoute/pull/11574)) — thanks @ntdatt812 +- **fix(check):** the `known-symbols` executor conformance gate awaits the now-async `getExecutor()`, so it stops reporting all 142 registered aliases as dead symbols and can detect a lazy import that fails ([#11582](https://github.com/diegosouzapw/OmniRoute/pull/11582)) — thanks @ntdatt812 +- **fix(pack):** register `bin/cli/utils/volatileEnvPath.mjs` in `PACK_ARTIFACT_REQUIRED_PATHS`, so the prune can no longer silently drop a module the published CLI imports on every boot ([#11588](https://github.com/diegosouzapw/OmniRoute/pull/11588)) — thanks @ntdatt812 +- **i18n(vi):** translate the five reliability-column strings on the free-provider rankings page, which rendered as `__MISSING__:` markers in the Vietnamese UI ([#11589](https://github.com/diegosouzapw/OmniRoute/pull/11589)) — thanks @ntdatt812 +- **resilience:** restore the expired-connection retry-budget probe in the token-health sweep — the `!isGitHubAccessTokenOnlyConnection` carve-out reintroduced by #11608 contradicted the boundary pinned by #11592, so a GitHub connection parked at `expired` with retry budget remaining was never probed and could never self-heal ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)). ([#11672](https://github.com/diegosouzapw/OmniRoute/pull/11672)) — thanks @jonlwheat2-gif / @ntdatt812 +- **fix(dashboard):** Provider Stats now displays combo success rates on their native 0–100 percentage scale instead of multiplying them twice ([#11595](https://github.com/diegosouzapw/OmniRoute/pull/11595)) — thanks @pacocartones +- **fix(providers):** route DuckDuckGo Web Chat through the verified `duck.ai` endpoint, refresh its free model catalog, and avoid forwarding unsolved anti-abuse challenges ([#11597](https://github.com/diegosouzapw/OmniRoute/pull/11597)) — thanks @solstxce +- **fix(ui):** Console log Refresh and Copy controls now expose localized accessible names, keep copy actions visible on keyboard focus, and announce copy completion safely ([#11599](https://github.com/diegosouzapw/OmniRoute/pull/11599)) — thanks @pacocartones +- Dropped the stale-`.eslintcache` `restore-keys` fallback from both "Restore ESLint file cache" steps in `ci.yml`, so the blocking `Lint` job can no longer be served per-file verdicts computed under a different lint config, suppressions file or lockfile. `quality.yml` had already dropped it in #11963; `ci.yml` — the workflow that actually gates PRs — had not (#11600). ([#11996](https://github.com/diegosouzapw/OmniRoute/pull/11996)) +- fix(ci): pass `--pass-on-unpruned-suppressions` in `run-eslint-json.mjs` so the CI Lint job no longer fails when a suppression is merely orphaned by a genuine fix, mirroring the identical fix already in `validate-release-green.mjs` (#11600) ([#11983](https://github.com/diegosouzapw/OmniRoute/pull/11983)) — thanks @hartmark +- fix(handoff): enforce provider allowlist for universal handoff (#11602) — universal handoff now skips summarization when the selected summary model's provider is not included in the configured provider allowlist. ([#11619](https://github.com/diegosouzapw/OmniRoute/pull/11619)) — thanks @vsd2807 +- **fix(dashboard):** Show a stable error state when Search Analytics returns an HTTP error or malformed data ([#11603](https://github.com/diegosouzapw/OmniRoute/pull/11603)) — thanks @pacocartones +- **test(gamification):** pin the aggregate profile level to the XP-derived semantics of #11604 — `getAggregateXp()` now derives `currentLevel` from the summed XP (`calculateLevel(sum)`), not `MAX(stored current_level)`, and the #3484 fixture levels are aligned with the XP curve ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)). ([#11676](https://github.com/diegosouzapw/OmniRoute/pull/11676)) — thanks @jonlwheat2-gif / @pacocartones +- **fix(dashboard):** Keep the Profile level and progress aligned with aggregate XP, including bounded handling for invalid totals ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)) — thanks @pacocartones +- **fix(dashboard):** Prevent locked hidden badges from revealing their icon or opening private badge details before they are earned ([#11605](https://github.com/diegosouzapw/OmniRoute/pull/11605)) — thanks @pacocartones +- **fix(dashboard):** Restore keyboard focus after shared modals close and cancel delayed autofocus during cleanup ([#11607](https://github.com/diegosouzapw/OmniRoute/pull/11607)) — thanks @pacocartones +- **fix(dashboard):** Enable Enter and Space activation for clickable data-table rows without hijacking nested controls ([#11610](https://github.com/diegosouzapw/OmniRoute/pull/11610)) — thanks @pacocartones +- **fix(autoCombo,sse):** vendor-retired catalog ids are dropped from the auto-combo candidate pool and no longer win on leftover `arena_elo` / `user_override` rows; `getModelLifecycleDecision` consults `model-lifecycle.json` (prefix-stripped) so aggregator traffic is not `untracked` for ids the snapshot already knows ([#11625](https://github.com/diegosouzapw/OmniRoute/issues/11625)) ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)) — thanks @MumuTW +- **test(chatcore):** move Codex/Claude combo fixtures off models the lifecycle guard now rejects — `gpt-5.1-codex`/`gpt-5-codex` are vendor-retired (snapshot, #11626) and `claude-3-5-sonnet-20241022` is shut down, so native-passthrough and combo-fallback tests switched to `gpt-5.6-sol` and `claude-sonnet-4.6` ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)). ([#11675](https://github.com/diegosouzapw/OmniRoute/pull/11675)) — thanks @jonlwheat2-gif / @MumuTW +- fix(opencode-plugin): stop a free-tier budget that rounds up across a magnitude from rendering as `1000K`/`1000M` in the model picker — `fmtTokens` chose its unit from the raw token count and then rounded with `toFixed(1)`, so 999,950–999,999 printed as `1000K` rather than `1M` and just under a billion printed as `1000M` rather than `1B` ([#11684](https://github.com/diegosouzapw/OmniRoute/pull/11684)) — thanks @abhisheksharma2411 +- Cap prose-derived Antigravity quota resets at the configured model cooldown maximum while preserving authoritative Retry-After headers and Google RetryInfo hints. (#11695) Thanks @pacocartones. ([#11823](https://github.com/diegosouzapw/OmniRoute/pull/11823)) +- Compare `_rebaseline_*` notes structurally in the ratchet-bank verifier so an object-valued note no longer reports itself as rewritten on an unmodified tree — the nightly `bank-ratchet-shrinks` lane had been aborting on every run since 2026-08-11, leaving 41,672 lines of earned file-size shrinks unbanked (#11702). ([#11892](https://github.com/diegosouzapw/OmniRoute/pull/11892)) — thanks @MumuTW +- **fix(build):** `prepublish.ts` bundles the ChatGPT Web (Codex) MCP bridge through `runBuildTool()` instead of spawning `npx.cmd` raw, fixing the build crash on Node ≥ 20/Windows where `.cmd` shims cannot be spawned without a shell (EINVAL) ([#11704](https://github.com/diegosouzapw/OmniRoute/issues/11704)) ([#11706](https://github.com/diegosouzapw/OmniRoute/pull/11706)) — thanks @jonlwheat2-gif +- fix(codex): keep `parallel_tool_calls:false` on the translated Codex Responses Lite path (#11707) ([#11984](https://github.com/diegosouzapw/OmniRoute/pull/11984)) — thanks @hartmark +- **fix(tests):** the new Gemini Web Images retirement test (#11708) called the now-async `getExecutor()` synchronously, throwing `TypeError: getExecutor(...).getProvider is not a function` instead of asserting Gemini Web chat stays available. Awaited to match the R0.3 executor-registry refactor (#11220). ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(search):** Allow REST and MCP web-fetch requests to select the advertised `anysearch-search` provider explicitly ([#11716](https://github.com/diegosouzapw/OmniRoute/pull/11716)) — thanks @pacocartones +- **fix(providers):** Keep overnight peak-hour protection active after midnight according to the window's configured start day ([#11718](https://github.com/diegosouzapw/OmniRoute/pull/11718)) — thanks @pacocartones +- **fix(providers):** correct all five g4f.space gateway descriptions to document proof-of-work credits or member API keys, variable limits, and the remote prompt boundary instead of promising free keyless access at five requests per minute ([#11738](https://github.com/diegosouzapw/OmniRoute/pull/11738)). +- **fix(db):** `model_capabilities` is created by a migration instead of lazily on the first models.dev sync, so a clean install and an upgraded install converge on the same schema regardless of which features have run ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(ci):** `check:install-upgrade` now fails on an `npm` install truncated by ENOSPC (npm reports it as a warning and still exits 0), authenticates its health probe so the version assertion works against the hardened health payload, frees the clean-install tree before the upgrade phase, and no longer reports a schema divergence computed from a boot that never served ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(routing):** harden session-affinity key derivation against oversized explicit IDs and empty structural-payload collisions, while recognizing Gemini and common text request shapes ([#11746](https://github.com/diegosouzapw/OmniRoute/pull/11746)) — thanks @MumuTW +- **fix(packages/browser-pool):** regenerate `package-lock.json` so the `packages/browser-pool` workspace's locked `playwright`/`@types/node` (and transitives) match its `package.json` specs, fixing cache-only/offline installs (`npm ci --offline`, Nix `buildNpmPackage`) that previously failed with `ENOTCACHED` ([#11747](https://github.com/diegosouzapw/OmniRoute/issues/11747)) — thanks @benjaminkitt ([#11784](https://github.com/diegosouzapw/OmniRoute/pull/11784)) — thanks @geekyNads +- **fix(assets):** Stop distributing 79 provider and CLI logos without verified source/license provenance, keep every provider/tool available through the internal generic icon, and prevent those IDs from falling through to an external icon CDN. ([#11750](https://github.com/diegosouzapw/OmniRoute/pull/11750)) +- **fix(providers):** retire the common `chatgpt-web` / `cgpt-web` provider because its pre-key and proof-of-work provenance could not be cleared ([#11754](https://github.com/diegosouzapw/OmniRoute/pull/11754)) — explicit chat and image requests, slash-prefixed models, persisted aliases, proxy overrides, and combo targets now fail closed with a sanitized HTTP `410` / `PROVIDER_RETIRED` before upstream dispatch. Migration 163 tombstones matching connections and invalidates their active leases without deleting history or widening API-key allowlists. The independent `chatgpt-web-codex` / `cgpt-codex` provider and its MIT-noticed vendored implementation remain available. +- **fix(usage):** quota and usage refresh no longer 409 when an exclusive lease reserves the connection ([#11758](https://github.com/diegosouzapw/OmniRoute/pull/11758)) — thanks @TheDemonTuan +- Keep the embedding registry's vector width and `embedding` type on models when a synced model exists for the same id, so `/v1/models` no longer reports registry-described embedding models widthless or untyped (#11761) — thanks @rahilmavani +- Correct `google/gemini-embedding-001` on the OpenRouter route to 3072 dimensions, the width it returns when `dimensions` is not sent (#11761) — thanks @rahilmavani +- **fix(vision-bridge):** Restrict automatic vision routing and fallbacks to models available in authoritative live provider catalogs, including aliases and effort variants ([#11767](https://github.com/diegosouzapw/OmniRoute/issues/11767)) — thanks @pacocartones ([#11813](https://github.com/diegosouzapw/OmniRoute/pull/11813)) +- **fix(providers):** reclassify Cerebras as a one-time $5 signup credit (payment method required, 30-day validity), not a recurring no-card 1M tokens/day trial ([#11773](https://github.com/diegosouzapw/OmniRoute/issues/11773)) ([#12591](https://github.com/diegosouzapw/OmniRoute/pull/12591)) — thanks @HouMinXi +- fix(cli): stop prepublish from re-rebuilding the already-built ESM-only opencode-plugin dist (#11787) ([#11990](https://github.com/diegosouzapw/OmniRoute/pull/11990)) — thanks @hartmark +- **fix(db):** Fresh installs no longer log a non-fatal `no such table: compression_run_telemetry` warning when retention cleanup runs before the lazily-created telemetry table exists ([#11802](https://github.com/diegosouzapw/OmniRoute/pull/11802)) — thanks @RaviTharuma +- fix(sse): set X-OmniRoute-Selected-Connection-Id on successful combo dispatches so downstream consumers stop falling back to an empty connection id (#11810) ([#11986](https://github.com/diegosouzapw/OmniRoute/pull/11986)) — thanks @hartmark +- **fix(services):** embedded CLIProxyAPI lifecycle checks now use public `/healthz`, while model discovery uses the configured dedicated data-plane API key instead of the management password ([#11811](https://github.com/diegosouzapw/OmniRoute/pull/11811)) — thanks @RaviTharuma +- Fix persisted model steps returning an error in the playground route simulator, and warn when structural steps cannot be simulated. ([#11862](https://github.com/diegosouzapw/OmniRoute/pull/11862)) — thanks @pacocartones +- **fix(providers):** Antigravity's dynamic mitmAlias table no longer routes `gemini-3.7-flash-{high,medium,low}` to a literal tier-suffixed upstream id just because one connected account's own discovery listed it directly — those display ids always resolve through the safe `gemini-3.7-flash-tiered` static alias, so one account's Google-provisioned access no longer 404s every sibling account of the provider ([#11824](https://github.com/diegosouzapw/OmniRoute/issues/11824), [#11651](https://github.com/diegosouzapw/OmniRoute/issues/11651)) ([#11988](https://github.com/diegosouzapw/OmniRoute/pull/11988)) — thanks @hartmark +- **fix(dashboard):** Expose Profile loading, errors, page structure, and XP progress to assistive technologies ([#11838](https://github.com/diegosouzapw/OmniRoute/pull/11838)) — thanks @pacocartones +- **fix(socks):** `Agent.connectTimeout` now bounds both SOCKS handshake and TLS connect, and `family === null` no longer falls back to `fetch-socks` ([#11842](https://github.com/diegosouzapw/OmniRoute/pull/11842)) — thanks @maxmad64bis +- **perf(compression):** OOM mitigations for large payload hashing, memoization, and token estimation ([#11844](https://github.com/diegosouzapw/OmniRoute/pull/11844) — thanks @AndrianBalanescu) +- Excluded Next.js Node File Trace manifests (`*.nft.json`) from the published npm tarball. They are build-time metadata and are never read while serving, but had grown to 668.7 MB — 61% of the package — which pushed the upload past the registry limit and made `npm publish` fail with `413 Payload Too Large`. ([#11864](https://github.com/diegosouzapw/OmniRoute/pull/11864)) +- **fix(cli):** keep the global OmniRoute server `--base-url` separate from provider-node `--endpoint` values in add, update, and validate commands ([#11860](https://github.com/diegosouzapw/OmniRoute/pull/11860)) — thanks @pacocartones +- **fix(config):** Nous Research's `Hermes-4-405B` model now displays as "Hermes 4 405B (Nous Research)" in both the provider registry and the free-model catalog, instead of the mislabelled "Hermes 4 7B" ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#11993](https://github.com/diegosouzapw/OmniRoute/pull/11993)) +- **fix(provider/nous):** inject required user= tag into Nous Research inference requests to resolve upstream 400 "missing tags" error ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#12044](https://github.com/diegosouzapw/OmniRoute/pull/12044)) +- **fix(build):** `npm run build` now fails in one second with a named package and a copy-pasteable fix when npm silently drops an externalised optional native dependency, instead of dying four minutes in with `Module not found: Can't resolve 'better-sqlite3'` ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- **fix(install):** `postinstall` no longer throws `ReferenceError: isAndroid is not defined` — failing the whole `npm install` — when the `better-sqlite3` rebuild fallback times out; the manual-fix guidance is reachable again ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- Fixed the Alibaba free-tier allowlist test that went red on its own once the shipped catalog's `validUntil` (2026-08-27) passed, leaving every PR and `main` with a failing `Unit Tests (1/8)`. The test now builds its own packs with dates it controls, and covers the expired-pack fallback that production has actually been serving. ([#11867](https://github.com/diegosouzapw/OmniRoute/pull/11867)) +- Split the npm registry upload into its own GitHub-hosted job. npm refuses `--provenance` from a self-hosted runner (`422 ... Only "github-hosted" runners are supported`), which blocked the v3.8.50 publish; the heavy verification cannot move to a hosted runner, so it now hands the proven tarball over instead. ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(ui):** the shared `DataTable` loading state no longer reads its decorative ⏳ glyph out to assistive technology, and now carries the same `role="status"` / `aria-live="polite"` / `aria-busy="true"` semantics as `PageLoading` ([#11869](https://github.com/diegosouzapw/OmniRoute/pull/11869)) — thanks @pacocartones +- **fix(api):** Bound the `timeout` accepted by `POST /api/providers/[id]/login` to the same 15s–600s window the Adobe Firefly and Conol browser-login paths already enforce, so a request can no longer pin a headful browser and the single active-login slot with an arbitrarily large poll budget. ([#11871](https://github.com/diegosouzapw/OmniRoute/pull/11871)) — thanks @pacocartones +- **fix(api):** Stop the tunnel and MITM routes from returning raw child-process error text, which disclosed host paths, binary install locations and Tailscale `tskey-*` credentials in a body some of these routes serve to non-loopback callers, and return a real 400 instead of a framework 500 when a tunnel request body fails validation. ([#11872](https://github.com/diegosouzapw/OmniRoute/pull/11872)) — thanks @pacocartones +- **fix(open-sse):** stream TTFT/ITL are sampled from a monotonic clock (`performance.now`) instead of `Date.now`, so an NTP correction or wall-clock jump can no longer inflate a genuine TTFT or produce a negative one that the `ttft >= 0` guard silently drops — protecting the router's OTel/EWMA/`usage_history`/speed-ranking signals ([#11873](https://github.com/diegosouzapw/OmniRoute/pull/11873)) — thanks @pacocartones +- **fix(providers):** xAI `reasoning_effort: "xhigh"` now reaches grok-4.6+ instead of being silently clamped to `"high"` ([#11879](https://github.com/diegosouzapw/OmniRoute/pull/11879)) — thanks @NoxzRCW +- **fix(dashboard):** the providers page no longer crashes into the error boundary when a provider id collides with an `Object.prototype` member (`constructor`, `__proto__`); icon lookups are own-property guarded ([#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)) — thanks @NoxzRCW +- **fix(skills):** injected skill tools declared in shorthand (`{"content": "string"}`) now forward valid JSON Schema, unblocking providers that validate tool schemas strictly such as Zhipu GLM on the Console Go tier ([#11881](https://github.com/diegosouzapw/OmniRoute/pull/11881)) — thanks @NoxzRCW +- **fix(api):** `POST /api/playground/simulate-route` now surfaces `combo-ref` and `provider-wildcard` persisted combo steps with a specific warning (naming the referenced combo, or the unresolved `provider/model` wildcard) instead of folding them into a generic "unsupported step" count; a `provider-wildcard` step is also now included as an unresolved target so the operator can see it is in the route (ported from [#11882](https://github.com/diegosouzapw/OmniRoute/pull/11882) — thanks @NoxzRCW). ([#11930](https://github.com/diegosouzapw/OmniRoute/pull/11930)) +- **fix(translator):** the streaming OpenAI→Claude translator keeps upstream usage, including prompt-cache tokens, when it arrives on a trailing `choices: []` chunk (Fireworks and any upstream using `stream_options.include_usage`) ([#11883](https://github.com/diegosouzapw/OmniRoute/pull/11883)) — thanks @NoxzRCW +- **fix(cli):** `omniroute update --apply` now tells you explicitly whether a running server was detected and, if so, that you must run `omniroute restart` to apply the update — it never restarted anything and previously implied the update was already live once files were installed. The dashboard's npm-mode Update flow (`/api/system/version`) now tries OmniRoute's own PID-file-managed supervisor before falling back to pm2, and reports an honest "restart required" step instead of a silent pm2-only "skipped" that read like a completed update. The server-side latest-version lookup backing the dashboard's update banner also gained `--prefer-online`, closing the same stale-npm-cache class already fixed in the CLI's own copy for #4376 ([#11885](https://github.com/diegosouzapw/OmniRoute/issues/11885)). ([#12005](https://github.com/diegosouzapw/OmniRoute/pull/12005)) +- **fix(resilience):** clear persisted LKGP pins when a target suffers connection/provider exhaustion or is skipped before dispatch due to cooldown/exhaustion/unavailability, preventing subsequent requests from repeatedly prioritizing known-dead providers ([#11911](https://github.com/diegosouzapw/OmniRoute/issues/11911)). ([#12013](https://github.com/diegosouzapw/OmniRoute/pull/12013)) — thanks @HouMinXi +- **fix(sse):** the OpenAI→Claude stream translator now defers the terminal `message_delta`/`message_stop` emission until the real usage block has arrived (or a genuine end-of-stream flush forces it) instead of emitting it immediately on `finish_reason` — previously, when the trailing usage-only chunk (`{"choices":[],"usage":{...}}`) arrived *after* the `finish_reason` chunk (the normal order for Fireworks/vLLM/Together and other `stream_options.include_usage` upstreams), the client-visible `message_delta` still carried stale/zero usage even though `state.usage` was internally corrected too late to matter (ported from [#11915](https://github.com/diegosouzapw/OmniRoute/pull/11915) — thanks @HouMinXi). ([#11933](https://github.com/diegosouzapw/OmniRoute/pull/11933)) +- **fix(sse):** `stripStore()` now forces `store=false` for stateless OpenAI-compatible Responses-API targets (unless the connection has explicitly opted in via `providerSpecificData.openaiStoreEnabled`), instead of only handling the `openai`/`agentrouter` cases — a client-supplied `store` value previously passed through untouched to backends that don't actually persist responses server-side ([#11916](https://github.com/diegosouzapw/OmniRoute/pull/11916) — thanks @HouMinXi). +- **fix(models):** custom provider-node models (synced, custom, and alias-backed) now appear under their configured prefix in the unified catalog when the operator's model-id prefix mode is canonical, instead of being dropped whenever alias-inclusion was otherwise disabled ([#11918](https://github.com/diegosouzapw/OmniRoute/pull/11918) — thanks @HouMinXi). +- **fix(models):** the unified model catalog now suppresses stale static registry models (including effort-tier variants) for any provider whose active connection has an authoritative live synced catalog, not only providers already using exclusive-synced-listing — a connection with `providerUsesAuthoritativeLiveCatalog` previously kept serving both the live-synced models and the stale static rows side by side ([#11919](https://github.com/diegosouzapw/OmniRoute/pull/11919) — thanks @HouMinXi). +- **fix(ui):** `ProviderIcon`'s three remaining unguarded lookups (`PROVIDER_ICON_ALIASES`, `LOCAL_SVG_ALIASES`, `THEMED_SVGS`) now use `Object.hasOwn()` own-property checks — a provider id such as `constructor` or `__proto__` previously resolved through the prototype chain instead of falling through to the unknown-provider CDN fallback (`getLobeProviderIcon()` itself was already guarded by [#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)); `ProviderPageHeader`'s `color` field is also now optional, matching the rest of the component's defensive typing (ported from [#11920](https://github.com/diegosouzapw/OmniRoute/pull/11920) — thanks @HouMinXi). ([#11935](https://github.com/diegosouzapw/OmniRoute/pull/11935)) — thanks @NoxzRCW +- fix(ollama): preserve multi-byte UTF-8 content split across stream chunks in the Ollama NDJSON transform, which previously corrupted CJK/emoji into U+FFFD (#11921) — thanks @pacocartones +- **fix(providers):** OrcaRouter chat requests now target `/v1/chat/completions` instead of the bare `/v1` API root, fixing the upstream `404 Invalid URL (POST /v1)` ([#11923](https://github.com/diegosouzapw/OmniRoute/pull/11923)). — thanks @echel0nn +- **fix(plugins):** deliver the `onStreamComplete` event to disk-installed plugins. The event shipped in v3.8.50 (#9669) was emitted internally but had no plugin-facing wiring, so no plugin could ever subscribe: the manifest schema silently dropped `hooks.onStreamComplete`, and the loader/manager only knew the seven legacy hooks. `onStreamComplete` is now a declarable manifest hook, wired through the loader and registered by the manager like the other hooks, and its payload carries a `requestId` so consumers can correlate the stream-completion event with the originating request ([#11934](https://github.com/diegosouzapw/OmniRoute/pull/11934)) — thanks @amaleta / @pacocartones +- **fix(db):** the Qdrant embedding-model dropdown now lists local/self-hosted providers (Ollama, LM Studio, vLLM, etc.) — an active connection is treated as "configured" when the provider allows an optional API key, not only when it has a real key or OAuth, so a running local embedding provider is no longer hidden from the picker ([#11949](https://github.com/diegosouzapw/OmniRoute/issues/11949)) ([#11995](https://github.com/diegosouzapw/OmniRoute/pull/11995)) +- **fix(providers):** Vertex AI Anthropic partner-model discovery now calls the Model Garden `v1beta1` publisher list (`/v1beta1/publishers/anthropic/models`, global) and parses its `publisherModels` envelope, so Claude models auto-synced from Vertex populate the active live catalog and route at request time instead of returning `Model '' is not available in the active live catalog` ([#11991](https://github.com/diegosouzapw/OmniRoute/issues/11991)) — thanks @fabioluissilva ([#11998](https://github.com/diegosouzapw/OmniRoute/pull/11998)) +- **fix(cli):** support `--base-url` alongside `--endpoint` in `omniroute nodes add`, `update`, and `validate` subcommands to prevent global `--base-url` shadowing issues ([#11999](https://github.com/diegosouzapw/OmniRoute/issues/11999)). ([#12033](https://github.com/diegosouzapw/OmniRoute/pull/12033)) — thanks @HouMinXi +- **fix(providers):** `cloudflare-ai` no longer refuses image content parts for every Workers AI model ([#12002](https://github.com/diegosouzapw/OmniRoute/pull/12002)) — the plain-string `content` requirement behind #2539 is carried by the _model_ schema, not by the `/ai/v1/chat/completions` endpoint (measured: an all-text part array returns 200 on `@cf/mistralai/mistral-small-3.1-24b-instruct`, `@cf/meta/llama-4-scout-17b-16e-instruct` and `@cf/meta/llama-3.3-70b-instruct-fp8-fast`, and 400 on the text-only `@cf/qwen/qwen2.5-coder-32b-instruct`). `transformRequest()` flattened every array and threw on the first non-text part (#6390), so image input was refused for vision-capable Cloudflare models that accept it. All-text arrays are still flattened — the one shape every model accepts — while an array carrying a non-text part is passed through untouched, so the attachment is still never silently dropped. Regression guards: `tests/unit/cloudflare-ai-image-parts-6390.test.ts`. — thanks @davidlinfr +- **fix(resilience):** decouple the limiter-managed execution backstop from the queue-wait budget — new `requestQueue.executionMaxWaitMs` (env `RATE_LIMIT_EXECUTION_MAX_WAIT_MS`, default 600000 = 10 min) now feeds Bottleneck's post-dispatch `expiration`, while `requestQueue.maxWaitMs` keeps its documented queue-wait semantics. Previously the queue-wait budget doubled as the execution expiration, so legitimate long-running calls on non-incremental gateways (whole generation buffered before the first upstream byte, e.g. Console Go / Command Code tiers serving GLM models) were killed mid-flight at the queue budget with a false 504 `RATE_LIMIT_EXECUTION_TIMEOUT` — the local limiter undercut the provider-aware upstream fetch-start timeouts. The surfaced 504 message now names `requestQueue.executionMaxWaitMs`; the error keeps the #4165 guarantees (disclaims an upstream timeout, preserves the Bottleneck error as `cause`, branded code + trusted provenance, classified request-scoped so combo falls back). A real queue-wait bound (the `Promise.race` around `limiter.schedule()` sketched in #9533) remains future work. (#12025) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **Call logs:** keep the `error` field when an artifact exceeds the storage cap, instead of replacing it with the omission marker. The error is the only field that says *why* a request failed and is typically ~90 bytes next to the multi-hundred-KB bodies that trip the cap, so dropping it left a size-limited row undiagnosable — a provider outage, a local timeout and an upstream 400 all rendered identically. It is now preserved at every fallback stage, truncated to 4KB if it is itself large ([#12026](https://github.com/diegosouzapw/OmniRoute/issues/12026)). ([#12095](https://github.com/diegosouzapw/OmniRoute/pull/12095)) — thanks @ntdat812 +- **fix(diagnostics):** preserve the error field (truncated to 4KB with a `[truncated: …]` suffix) in every call-log artifact size-limit fallback stage. Previously the minimal fallback replaced the error with `[omitted: call log artifact size limit exceeded]`, so an oversized artifact row showed nothing about WHY the request failed — e.g. 91 of 847 opencode-go 504 rows on one production instance were undiagnosable from the dashboard. Oversized request/response bodies are still omitted exactly as before; the error cap is independent of the payload sizes that tripped the fallback. (#12026) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **fix(sse):** OpenAI Responses clients that declare the native `web_search` tool now receive a spec-shaped `web_search_call` output item with `action.sources` alongside the preserved function-call round-trip, so search results executed through OmniRoute's own search backend are consumable by standard Responses clients (Codex, pi-web-access, …). ([#12031](https://github.com/diegosouzapw/OmniRoute/pull/12031)) — thanks @watchingdogs +- **fix(cli):** use in-thread alias resolver hooks on modern runtimes to avoid deprecation noise and improve Node.js forward compatibility ([#12073](https://github.com/diegosouzapw/OmniRoute/issues/12073)). ([#12083](https://github.com/diegosouzapw/OmniRoute/pull/12083)) — thanks @xiaoyaner0201 +- **fix(combo):** an operator-set **Agent Features → Context length** on a combo is now honored at request time. The value was persisted and advertised through `/v1/models`, but `resolveComboContextLimit()` never consulted it — so a multi-target combo whose members carry no per-model window fell through to the provider's generic `defaultContextLength` (openrouter 128000, command-code 200000) and rejected large requests with `Input exceeds context window … limit 128000` despite the combo being explicitly sized much larger. An identical single-target combo worked, because it collapses to its concrete target before the guard runs. Invalid values (0/negative/NaN/Infinity) are ignored, so the existing target → combo-min → fallback order is unchanged. ([#12090](https://github.com/diegosouzapw/OmniRoute/pull/12090)) — thanks @adivekar-utexas +- **fix(sse):** passthrough streams now estimate usage on finish when upstream closes without usage even with `stream_options.include_usage` — avoids `0 tokens / 0%` for providers that stay silent (and correctly handles trailing empty-choices usage) ([#12151](https://github.com/diegosouzapw/OmniRoute/pull/12151)) — thanks @maxmad64bis +- **fix(combos):** clearing an agent feature in the combos editor now persists — unchecking context cache protection, or emptying the system message or tool filter, sends an explicit `null` instead of dropping the field from the `PUT` body, which the update merge read as "leave unchanged" ([#12177](https://github.com/diegosouzapw/OmniRoute/pull/12177)) — thanks @foreveryh / @ntdatt812 +- **fix(memory):** self-hosted embedding endpoints now vectorize — the vector width is measured from the first embedding that comes back instead of being read from a registry that cannot describe them, so `vec_memories` is created and memories stop piling up unvectorized behind a green health check ([#12180](https://github.com/diegosouzapw/OmniRoute/pull/12180)) — thanks @kanade-hoshino / @ntdatt812 +- **fix(translator):** the leading `system` message now reaches Responses-API upstreams when its `content` is a content-part array — it was read as `typeof content === "string" ? content : ""`, so a prompt-caching client (Anthropic `cache_control`, the shape LiteLLM and the Anthropic SDK emit) had its entire system prompt replaced by an empty `instructions`. The request was still accepted with a normal `prompt_tokens` count, so the model answered with no instructions and nothing in the response said they were missing. Mid-conversation system turns already handled the array shape ([#7056](https://github.com/diegosouzapw/OmniRoute/pull/7056)); only the first one did not ([#12206](https://github.com/diegosouzapw/OmniRoute/issues/12206)). Regression guard: `tests/unit/translator-openai-responses-system-content-parts.test.ts`. ([#12207](https://github.com/diegosouzapw/OmniRoute/pull/12207)) — thanks @Gorillaz322 +- **fix(free-tier):** `/api/free-tier/summary` no longer computes its totals from a Radar feed built before the catalog the running release ships. When the cached feed is older — or carries no build date at all — the route answers from the shipped catalog, resolved through the operator's local model state so disabled and tombstoned models stay out of the numbers ([#12215](https://github.com/diegosouzapw/OmniRoute/pull/12215)). — thanks @maxmad64bis +- **fix(oauth):** Keep a Claude personal workspace and a Team organization as separate connections — they share the same email and `accountUUID`, so the email-only OAuth dedup let the second login overwrite the first account's tokens; `organizationUUID` now disambiguates them, the way `workspaceId` does for Codex ([#12222](https://github.com/diegosouzapw/OmniRoute/pull/12222)) — thanks @davidebaraldo +- **fix(resilience):** a 402 on a single paid model of a passthrough/gateway provider (e.g. `kilo-gateway`, `ollama-cloud`) no longer terminalizes the whole connection with a never-auto-recovered `credits_exhausted` status — only the paid model is locked out, so free models on the same key keep serving. 402 variant of [#3027](https://github.com/diegosouzapw/OmniRoute/issues/3027). Single-credential providers are unaffected — a 402 there is still treated as the key being genuinely out of credit ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239) / [#10616](https://github.com/diegosouzapw/OmniRoute/issues/10616)) ([#12242](https://github.com/diegosouzapw/OmniRoute/issues/12242)) — thanks @brick30llc-ctrl ([#12266](https://github.com/diegosouzapw/OmniRoute/pull/12266)) — thanks @geekyNads +- **fix(sse):** trust `finish_reason: "length"`/`"max_tokens"` over the reasoning-consumed-token ratio in response quality validation, so a reasoning model truncated below the old 90% threshold correctly fails and retries instead of returning empty content as a silent "success" ([#12262](https://github.com/diegosouzapw/OmniRoute/pull/12262)) — thanks @brick30llc-ctrl +- **fix(settings):** `PUT /api/settings/cache-config` now persists `alwaysPreserveClientCache` to the flat general settings the runtime cache-control policy actually reads; previously the value landed in the databaseSettings "cache" section and was silently ignored, so the endpoint had no effect on `cache_control` passthrough ([#12304](https://github.com/diegosouzapw/OmniRoute/pull/12304)) — thanks @davidebaraldo +- **fix(grok-cli):** treat omitted SuperGrokPro `creditUsagePercent` as 0% used so Provider Limits still renders a weekly bar (proto3 zero-elision) ([#12312](https://github.com/diegosouzapw/OmniRoute/pull/12312)) — thanks @HouMinXi +- **fix(combo):** Expose the two Auto-Combo scoring factors nobody could set — the combo validation schema and the dashboard weight sliders both declared 13 of the scorer's 15 factors, so `connectionDensity` (spreads load across a provider's connections) and `quality` were dropped on save and offered nowhere. The sliders also shipped their own default table that differed from the engine's on every non-zero factor and summed to 1.05, so the percentages shown next to them added up to 105%. Both lists now match `DEFAULT_WEIGHTS`, and a test keeps them there. Note that a combo whose stored `weights` omitted the two keys was effectively running with them at zero and the other thirteen renormalized upward; it now runs with the engine's intended distribution, so its routing does shift ([#12314](https://github.com/diegosouzapw/OmniRoute/pull/12314)) — thanks @maxmad64bis +- **fix(docs):** The free-tier reference no longer says its numbers come "confidence tagged per row" — no catalog entry carries a confidence tag and the API serves none, so every figure on that page is an estimate of the same, unstated quality. The page now states what an entry does vouch for: an independently documented hard stop (set by hand with the source in a comment, never defaulted to `true`) and a prompt-training disclosure, both with live counts the `check:docs-counts` gate keeps honest ([#12318](https://github.com/diegosouzapw/OmniRoute/pull/12318)) — thanks @maxmad64bis +- **fix(usage):** `adobe-firefly` and `firefly` have had a working usage fetcher since Adobe Firefly landed, but neither was ever added to the registration list, so the provider-plugin manifest, `genericQuotaFetcher` and the free-access quota cache all reported them as having no usage support — while `USAGE_SUPPORTED_PROVIDERS` said the opposite. Both are now declared, which also means their credit balance is fetched like any other declared provider's: `registerGenericQuotaFetchers` now registers a generic quota fetcher for them, and `resolveFreeAccessState` no longer returns early. A test holds the registration list to the dispatcher's switch in both directions, which is what the module's own docstring already asked for in prose ([#12321](https://github.com/diegosouzapw/OmniRoute/pull/12321)) — thanks @maxmad64bis +- **fix(quota):** drop the generic quota cache (agy / Antigravity / Claude OAuth) on an upstream 429 so reset-aware scoring does not keep a 60s stale snapshot, and force-refresh the next usage fetch so inner provider caches cannot recache the same window ([#12325](https://github.com/diegosouzapw/OmniRoute/pull/12325)) — thanks @HouMinXi +- **fix(combos):** deleting a combo now clears its persisted LKGP pins instead of leaving unreachable `key_value` rows behind ([#12326](https://github.com/diegosouzapw/OmniRoute/issues/12326)) ([#12425](https://github.com/diegosouzapw/OmniRoute/pull/12425)) — thanks @KrzysiekSko +- **fix(sse):** Keep ZWNJ (U+200C) and ZWJ (U+200D) in assistant text, reasoning and tool-call arguments — Persian/Kurdish half-space (`ارائه‌دهنده`), Arabic/Indic shaping and emoji sequences no longer lose them; the response de-obfuscation now removes joiners only between ASCII word characters, where the request side inserts them ([#12186](https://github.com/diegosouzapw/OmniRoute/issues/12186)) — thanks @rezjalibd ([#12359](https://github.com/diegosouzapw/OmniRoute/pull/12359)) — thanks @pacocartones +- **fix(resilience):** count resolved upstream 5xx results against the provider circuit breaker on the chat path — `CircuitBreaker.execute()` no longer reads a resolved `{ success: false, status: 5xx }` as a success that cancels the call-site failure, so a provider answering 503s now trips its breaker instead of staying `CLOSED` at `failureCount: 1`; single-model and combo dispatches are each accounted exactly once ([#12254](https://github.com/diegosouzapw/OmniRoute/issues/12254)) ([#12360](https://github.com/diegosouzapw/OmniRoute/pull/12360)) — thanks @pacocartones +- **fix(providers):** resolve the Codex quota auto-ping model from the live provider catalog and lifecycle registry instead of the retired `gpt-5.1-codex-mini`, and pause the ping with one actionable warning when no selectable Codex model exists rather than retrying a shut-down id every cooldown window ([#11905](https://github.com/diegosouzapw/OmniRoute/issues/11905)) ([#12361](https://github.com/diegosouzapw/OmniRoute/pull/12361)) — thanks @pacocartones +- **fix(api):** keep the `{created, data}` wrapper on combo-routed `/v1/images/generations` responses and default Codex image results to `b64_json` on both `/v1/images/generations` and `/v1/images/edits` so Codex CLI's built-in `image_gen` can decode them ([#12268](https://github.com/diegosouzapw/OmniRoute/issues/12268)) ([#12362](https://github.com/diegosouzapw/OmniRoute/pull/12362)) — thanks @pacocartones +- **fix(sse):** Name the shadowed custom provider node when a built-in provider id/alias (e.g. `openference` → `of`) reserves the prefix of an existing OpenAI/Anthropic-compatible node, so the runtime `No active credentials for provider: ` error explains that the prefix routed to the built-in and never reached the node's healthy connections, instead of contradicting the dashboard ([#11943](https://github.com/diegosouzapw/OmniRoute/issues/11943)) — thanks @morpheus9393 ([#12365](https://github.com/diegosouzapw/OmniRoute/pull/12365)) — thanks @pacocartones +- **fix(i18n):** wrap `ccOnboardingKeyPlaceholder` in ICU single quotes across all 43 locales so angle brackets render literally instead of being parsed as rich-text tags, which crashed the Claude Code onboarding block with `INVALID_MESSAGE: INVALID_TAG` ([#12302](https://github.com/diegosouzapw/OmniRoute/issues/12302)) ([#12369](https://github.com/diegosouzapw/OmniRoute/pull/12369)) — thanks @gonisulaimann +- **fix(models):** publish `effort_tiers` on Kimi K3's synced base-model entries (`kmca/k3`, `kmca/k3-256k`) so catalog-only clients (OpenCode, plain SDK pickers) can see and select the reasoning tiers (`low`/`high`/`max`) the synced metadata already carried — the `isSkippedEffortProvider` gate no longer suppresses tier visibility on those base entries, while synthetic `-` variant generation stays prevented and Codex/GLM base models remain excluded unchanged ([#12299](https://github.com/diegosouzapw/OmniRoute/issues/12299)) ([#12371](https://github.com/diegosouzapw/OmniRoute/pull/12371)) — thanks @gonisulaimann +- **fix(guardrails):** keep `auto`/`auto/*` virtual combos exempt from the Vision Bridge `fixedModel` credential guard so a combo target is passed through instead of silently falling back to global auto-selection ([#12237](https://github.com/diegosouzapw/OmniRoute/issues/12237)) ([#12373](https://github.com/diegosouzapw/OmniRoute/pull/12373)) — thanks @pacocartones +- **fix(combo):** capability-filter exhaustion caused by `max_tokens` above every target's known output limit now reports that reason (requested `max_tokens` vs the pool's highest known ceiling) instead of the unrelated "supports structured output" message ([#12229](https://github.com/diegosouzapw/OmniRoute/issues/12229)) — thanks @DW-MediaLab ([#12374](https://github.com/diegosouzapw/OmniRoute/pull/12374)) — thanks @pacocartones +- **fix(auth):** the `least-used` account strategy now prefers accounts without backoff before falling back to oldest `lastUsedAt`, the same tie-break `round-robin` already applies, so a failover no longer lands on a just-rate-limited account for a single request ([#12279](https://github.com/diegosouzapw/OmniRoute/issues/12279)) — thanks @tenshiak ([#12375](https://github.com/diegosouzapw/OmniRoute/pull/12375)) — thanks @pacocartones +- **fix(docker):** the `chatgpt-web-codex-browser` image now finds the Chrome binary under `chrome-linux64/` (Chrome for Testing layout in `playwright:v1.62.0-noble`) as well as the legacy `chrome-linux/`, so the container no longer crash-loops with `exec: --headless=new: not found` ([#12024](https://github.com/diegosouzapw/OmniRoute/issues/12024)) ([#12376](https://github.com/diegosouzapw/OmniRoute/pull/12376)) — thanks @pacocartones +- **fix(providers):** declare `groq/compound` and `allam-2-7b` as non-reasoning models in the curated Groq registry so `reasoning_effort` / `output_config.effort` / `thinking` from Claude Code are stripped instead of forwarded, which Groq rejected with HTTP 400 ([#12134](https://github.com/diegosouzapw/OmniRoute/issues/12134)) ([#12379](https://github.com/diegosouzapw/OmniRoute/pull/12379)) — thanks @pacocartones +- **fix(executors):** `OpencodeExecutor` no longer forces a direct connection when the connection has a proxy assigned in Proxy Management but no per-account proxies: the single-account fast path used to wrap the upstream dispatch in the direct-egress sentinel, discarding the ambient proxy context the chat handler had pinned from `proxy_assignments`, so API-key `opencode`/`opencode-go` connections egressed from the host IP (and hit geoblocks) despite the assignment. The direct pin is now applied only when no ambient proxy context exists ([#11894](https://github.com/diegosouzapw/OmniRoute/issues/11894) — thanks @hizzt) ([#12380](https://github.com/diegosouzapw/OmniRoute/pull/12380)) — thanks @pacocartones +- **fix(api):** `GET /v1/models` with `MODELS_CATALOG_PREFIX_MODE=canonical` (or `?prefix=canonical`) now lists providers whose registry alias is undefined or equal to their own id (Antigravity, Antigravity CLI and other self-aliased built-ins) — their single `provider/model` id was dropped by the alias/canonical duplicate guard in the static, synced, custom and alias-backed catalog loops ([#12058](https://github.com/diegosouzapw/OmniRoute/issues/12058)) — thanks @cheynetom ([#12381](https://github.com/diegosouzapw/OmniRoute/pull/12381)) — thanks @pacocartones +- **fix(translator):** Drop replayed `thinking` blocks that carry no signature (the shape produced from cross-provider `reasoning_content`) instead of stamping the default Claude signature on them, which Anthropic rejected with `400 Invalid signature in thinking block` on the next turn served by an Anthropic rung ([#12105](https://github.com/diegosouzapw/OmniRoute/issues/12105)) — thanks @atescivitci-cmd ([#12386](https://github.com/diegosouzapw/OmniRoute/pull/12386)) — thanks @pacocartones +- **fix(cli):** Resolve Bun's `--preload` polyfill path against the package root instead of `dist/`, so `omniroute` installed with `bun install -g` no longer crashes at startup with `error: preload not found …/dist/open-sse/utils/setupPolyfill.ts` ([#11980](https://github.com/diegosouzapw/OmniRoute/issues/11980)) — thanks @joglomedia ([#12387](https://github.com/diegosouzapw/OmniRoute/pull/12387)) — thanks @pacocartones +- **fix(providers):** `gemini-business` now publishes its model catalog — `/v1/models` and `/v1/providers/gemini-business/models` list the 12 enterprise Gemini ids the executor understands instead of returning an empty list (#12107) ([#12389](https://github.com/diegosouzapw/OmniRoute/pull/12389)) — thanks @pacocartones +- **fix(db):** install `busy_timeout` before the SQLite connection's first statement so a process opening the database while another one closes its WAL connection waits out the transient EXCLUSIVE lock instead of dying with `database is locked`, and recognise the drivers' real BUSY/PROTOCOL/IOERR errors as transient in the corruption probe so the same lock no longer renames the database away as corrupt; deflakes `cross-process contenders never both acquire the same connection` (#12394 — thanks @pacocartones) +- **fix(chat-admission):** derive the `chat_admission_busy` 503 `Retry-After` from observed heavyweight-lease occupancy — the larger of the exhausted `OMNIROUTE_CHAT_ADMISSION_QUEUE_MS` window and the time since capacity last turned over, capped at 60 s — instead of a fixed 1 s (structural) / 2 s (byte-stage) hint that invited Codex/agent fan-out clients to re-send ~1 MiB `/v1/responses` bodies every second into a gate held for the whole SSE lifetime; an idle gate keeps the historical floors ([#12135](https://github.com/diegosouzapw/OmniRoute/issues/12135)) (#12395 — thanks @pacocartones) +- **fix(api-manager):** the API key permissions modal no longer silently drops `allowedCombos` entries its Combo picker cannot render — routing-rule names such as `rt-*`, which the backend already honours — when "All" is clicked and the key is switched back to "Restrict"; those entries now survive the toggle, are listed read-only under the combo list so the count and the list agree, and are saved back verbatim instead of persisting `[]` (deny-all) (#12397 — thanks @pacocartones) +- **fix(catalog):** write the NUL separator of the catalog connection memo key, the provider serviceKind memo key, the Video Bridge promotion group key and a JSON-exactness test fixture as the `\u0000` escape instead of a raw byte — same runtime value, but the raw byte made git, GitHub and ripgrep treat those files as binary (hidden PR diffs, silently skipped searches); a guard test now keeps raw NUL bytes out of `src/`, `open-sse/` and `tests/` (#12403 — thanks @pacocartones) +- **fix(providers):** add `CLAUDE_CODE_CLIENT_VERSION` and `GITHUB_COPILOT_CLI_VERSION` env overrides so Anthropic/Copilot client-version gates can be unblocked without a rebuild ([#12417](https://github.com/diegosouzapw/OmniRoute/issues/12417)) ([#12632](https://github.com/diegosouzapw/OmniRoute/pull/12632)) — thanks @HouMinXi +- **fix(db):** back-fill `last_ping_at` and `last_pinged_reset_key` on `provider_connections` during schema reconciliation so divergent lineages that skipped `123_quota_auto_ping` still accept quota auto-ping writes ([#12470](https://github.com/diegosouzapw/OmniRoute/pull/12470) — thanks @KooshaPari) +- **fix(ci):** document MIT exceptions for `@eloqnt/{config,format-json,format-po}` (next-intl transitive; locked tarballs omit `license`) and keep the A2A lifecycle vitest off the real SQLite persistence seam ([#12581](https://github.com/diegosouzapw/OmniRoute/issues/12581)) ([#12595](https://github.com/diegosouzapw/OmniRoute/pull/12595)) +- OpenCode plugin `/v1/models` catalog fetch now waits 30s by default and attaches HTTP `statusCode` on 401/5xx so host fallback plugins can hop instead of seeing an untyped AbortError/UnknownError. ([#12607](https://github.com/diegosouzapw/OmniRoute/pull/12607)) — thanks @RaviTharuma +- **CI:** the OpenAPI security-tier gate now mirrors `isAlwaysProtectedPath()` in full — it also reads `ALWAYS_PROTECTED_API_PATTERNS`, so the pattern-gated credential routes (`/api/providers/{id}/{claude,codex}-auth/{export,apply-local}`, GHSA-5926-2w35-7h4q) no longer report as unannotated. (#12605) +- **fix(api):** GET /v1/models no longer waits forever on a hung coalesced catalog rebuild; cold-path waits are bounded (`CATALOG_BUILD_TIMEOUT_MS`, default 8s) and a last-good 200 is served when the rebuild times out ([#12627](https://github.com/diegosouzapw/OmniRoute/issues/12627)). ([#12628](https://github.com/diegosouzapw/OmniRoute/pull/12628)) — thanks @RaviTharuma +- **fix(cursor):** a non-array `tool_calls` on an assistant message no longer crashes the cursor request translator with a `TypeError`; both loops now require an array ([#12689](https://github.com/diegosouzapw/OmniRoute/issues/12689)) ([#12691](https://github.com/diegosouzapw/OmniRoute/pull/12691)) — thanks @soroush5 +- **fix(chat):** Chat Completions no longer return empty `content` after a server-owned memory or skills tool; the first provider send and account/model recovery now share one pipeline so a follow-up round-trip can fill the reply ([#12696](https://github.com/diegosouzapw/OmniRoute/issues/12696)) — thanks @HouMinXi ([#12867](https://github.com/diegosouzapw/OmniRoute/pull/12867)) +- **fix(dashboard):** batch-deleting provider keys no longer toasts failure after a successful delete when the confirm button's click event is forwarded as `onAfter` ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) — thanks @HouMinXi +- **fix(glm):** drop the extra 16th argument to `createSSETransformStreamWithLogger` that TypeScript rejected (TS2554) and that never reached the TransformStream ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) ([#12770](https://github.com/diegosouzapw/OmniRoute/pull/12770)) — thanks @HouMinXi +- **fix(dashboard):** pass `nodeMap` into Runtime `QuotaGroup` so a quota monitor in error/exhausted/alerting no longer throws `ReferenceError: nodeMap is not defined`. ([#12868](https://github.com/diegosouzapw/OmniRoute/pull/12868)) — thanks @HouMinXi +- Restricted API keys whose `allowedModels` lists a combo name no longer skip every combo member at pre-dispatch (`ALL_TARGETS_SKIPPED`). Inner-target filtering still applies when the allow-list is a provider prefix or `disableNonPublicModels` is on ([#12899](https://github.com/diegosouzapw/OmniRoute/pull/12899)). — thanks @HouMinXi +- **fix(combos):** Effort-suffixed combo members inherit the base model's `model_context_overrides` row so priority order is not inverted on large requests ([#12926](https://github.com/diegosouzapw/OmniRoute/pull/12926)) — thanks @HouMinXi +- **fix(catalog):** Picker-added `customModels` enter the dispatch-time live catalog so combo members and bare inference no longer 400 ([#12934](https://github.com/diegosouzapw/OmniRoute/pull/12934)) — thanks @HouMinXi +- **fix(docker):** re-derive the Docker build's worker-pool memory budget from the MEASURED ~4.5 GB per-process RSS (the issue owner's own VPS dmesg OOM-killer reproduction) instead of the stale 2560 MB/worker inference #11419 shipped, and lower `OMNIROUTE_BUILD_WORKERS` 3 → 2 so 1 parent + 1 page-data worker (2 processes × 4.5 GB = 9 GB) fits the 12.288 GB (75%) budget on a 16 GB GitHub Actions runner — the previous default (1 parent + 2 workers = 13.5 GB) still overcommitted the runner and kept "Publish to Docker Hub" failing with `cannot allocate memory` after #11419 merged (#7518). ([#11663](https://github.com/diegosouzapw/OmniRoute/pull/11663)) +- **fix(electron):** the packaged Windows desktop build now passes the #7592 cold-restart smoke end to end. Five defects were found and fixed on the way: (1) optional-pack staging failed on any machine whose `tar` is GNU tar — it reads the drive letter in an absolute `-f C:\...` archive path as a remote rsh host (`Cannot connect to C:`), so staging now passes a bare filename with `cwd` at the tarball directory and surfaces tar stderr on failure; (2) the Electron `files` allowlist was missing `lib/loginHeaderCapture.js`, so the packaged main process crashed at startup with `Cannot find module './lib/loginHeaderCapture'`; (3) electron-builder ≥26 injects an `!**/node_modules/**` ignore into every extraResources pattern list that no positive filter can override, which silently dropped the staged runtime `node_modules` (including the better-sqlite3 N-API prebuild) from `resources/app` — a fresh v3.8.50 desktop build would have shipped with no native SQLite driver and reproduced the #7592 sql.js fallback on every machine; an `afterPack` hook now restores the staged `node_modules` after packing; (4) the packaged-app smoke harness redirected `USERPROFILE` into its temp DATA_DIR, but Electron resolves the Roaming profile from `%USERPROFILE%\AppData\Roaming\` (USERPROFILE takes precedence over the APPDATA env var) and the path service throws instead of creating it, so `requestSingleInstanceLock()` returned false and the app exited(0) silently before `app.whenReady()` — the harness now pre-creates the derived tree, and `ensureSmokeEnvDirs` is exported and unit-tested; (5) the #7592 driver assertion parsed a `[DB] Driver: ...` line that the server's primary DB path never printed (only the unused `openDatabaseAsync()` did) — `getDbInstance()` now emits the same line on open so the guard can actually assert the native driver. Also: the smoke env-allowlist unit test hardcoded POSIX paths and could only pass on Linux/macOS; expectations are now host-agnostic, with new regression tests for the USERPROFILE-derived tree and `tarPack` under absolute Windows-style paths ([#11443](https://github.com/diegosouzapw/OmniRoute/pull/11443)) — thanks @jonlwheat2-gif +- fix(sse): stop the auto-combo candidates inspector from silently dropping model-locked/cooled-down rows (#9133) ([#11994](https://github.com/diegosouzapw/OmniRoute/pull/11994)) — thanks @hartmark +- **fix(providers):** Perplexity Web no longer turns upstream stream failures into successful assistant text; pre-content failures remain eligible for fallback, partial output ends with a structured sanitized error, and failed sessions are not persisted ([#12465](https://github.com/diegosouzapw/OmniRoute/pull/12465)) +- **fix(video-bridge):** preserve chronological video and transcript fusion, source-frame timestamps, transcript provenance, full contact-sheet intervals, and pre-abort callback isolation ([#11681](https://github.com/diegosouzapw/OmniRoute/pull/11681)) +- **Z.ai Web:** HTTP 200 streams carrying an upstream error now terminate with a structured failure instead of assistant text plus a normal stop, preserving partial output while allowing pre-content combo fallback. ([#12454](https://github.com/diegosouzapw/OmniRoute/pull/12454)) +- Absorb `Error [AbortError]: request_signal_aborted` and DOMException AbortError shapes in the process-level client-abort crash guard so routine client disconnects no longer kill the server (exit code 7). ([#12165](https://github.com/diegosouzapw/OmniRoute/pull/12165)) — thanks @alvinveroy +- **fix(sse):** Treat Adapta Web `type:error` SSE events as sanitized non-stream failures instead of empty HTTP 200 completions. ([#12459](https://github.com/diegosouzapw/OmniRoute/pull/12459)) +- **fix(autoCombo):** layer 3 (`models_dev_tier`) no longer scores retired models as premium, merge cross-provider capability rows deterministically, and inherit tier scores through variant ids ([#11508](https://github.com/diegosouzapw/OmniRoute/issues/11508)). `getModelsDevTierFitness` now vetoes ids present in `config/quality/model-lifecycle.json` with status `retired` before any other signal; `loadModelCapabilities()` aggregates per `model_id` (any-non-null-true for booleans, max for `limit_context`) instead of last-write-wins over SQLite's undefined row order; a capabilities miss retries through `resolveScoresAs()`'s base id and reports `models_dev_tier:inherited`, matching layers 1–2 (#11492). Adds `getModelsDevTierFitnessWithSource()` and an `invalidateCapabilitiesCache()` test/ops hook. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) — thanks @Meet6338-X +- fix(sse): accept Claude `content:[]` + `stop_reason: max_tokens`/`tool_use` in `detectMalformedNonStream` (match `isEmptyContentResponse`) so Claude Code `/model` probes with `max_tokens: 1` no longer become a false 502 ([#11568](https://github.com/diegosouzapw/OmniRoute/pull/11568)) — thanks @Seramicx +- **fix(security):** sanitize `request.failed` diagnostics before publishing them to live dashboard listeners and replay history, while keeping status, model, provider, latency, and internal call-log diagnostics intact. ([#12469](https://github.com/diegosouzapw/OmniRoute/pull/12469)) +- **fix(pwa):** do not intercept dashboard navigations so Chrome can fall back from a stale HTTP/3 Alt-Svc advertisement (UDP :20128 is unpublished; F5 on a long-lived tab hung until a new tab opened a fresh TCP connection). ([#12767](https://github.com/diegosouzapw/OmniRoute/pull/12767)) — thanks @HouMinXi +- **fix(executors):** handle DuckDuckGo ERR_BN_LIMIT (418) without retrying — when the upstream returns `418 ERR_BN_LIMIT` (rate-limit/ban), the executor now returns the error immediately instead of burning another VQD acquisition that would only count against the IP limit. The retry logic for `418 ERR_CHALLENGE` (unsolved challenge) remains unchanged. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) ([#12110](https://github.com/diegosouzapw/OmniRoute/pull/12110)) — thanks @oyi77 / @Meet6338-X +- Fixed DuckDuckGo streaming responses losing JSON lines and UTF-8 characters split across network chunks. ([#11528](https://github.com/diegosouzapw/OmniRoute/pull/11528)) — thanks @pacocartones +- Harden generated Cloudflare, Vercel, and Deno relays by stripping hop-by-hop, framing, and proxy authentication headers before forwarding requests upstream. ([#11533](https://github.com/diegosouzapw/OmniRoute/pull/11533)) — thanks @pacocartones +- **feat(grok-cli):** Provider Limits shows grok-cli banked reset credits from `GetRemainingResets` (including a real zero; a failed RPC omits the row) and the existing View credits button now calls `ConsumerUiSvc/RedeemReset` for grok-cli. Live tokens use nested fields 10/20/30 (id + Timestamp), not compact 1/2/3. ([#12805](https://github.com/diegosouzapw/OmniRoute/pull/12805)) — thanks @HouMinXi +- **fix(memory):** Embedding Model Quick select, Embedding Source remote dropdown, and Rerank selector now list every configured provider with embedding/rerank support instead of only chat-catalog text matches plus OpenRouter live discovery; a generic OpenAI-compatible `/embeddings` + Cohere-compatible `/rerank` runtime fallback resolves any configured chat provider's embedding/rerank endpoint, so unlisted providers no longer fail with "Unknown embedding provider"; both memory selectors gained a free-text model override ([#11390](https://github.com/diegosouzapw/OmniRoute/pull/11390)) — thanks @rqzbeh +- Harden SQLite upgrades around the historical migration-074 version collision: missing discovery and inspector tables are replayed atomically, pre-existing databases (including setup-created skeletons) receive reusable content-addressed safety snapshots, and Node test/eval probes without `DATA_DIR` are isolated from the operator database. ([#12435](https://github.com/diegosouzapw/OmniRoute/pull/12435)) +- **fix(dashboard):** Moonshot/Kimi Open Platform voucher and cash leftover percentages follow the bucket balance, so an empty wallet no longer paints those rows as 100% while Available is 0% ([#12733](https://github.com/diegosouzapw/OmniRoute/pull/12733)) — thanks @HouMinXi +- **fix(combo):** return non-retryable HTTP 400 when all candidates for a pinned native Codex turn are unavailable due to model-scoped lockout, terminating the turn cleanly while preserving turn continuity and enabling standard Combo routing on subsequent turns ([#12240](https://github.com/diegosouzapw/OmniRoute/pull/12240)) — thanks @mdigitalbh81 +- Fixed auto-router strategies preserving the selected connection through ranking and dispatch when multiple connections share the same provider and model. ([#11530](https://github.com/diegosouzapw/OmniRoute/pull/11530)) — thanks @pacocartones +- Preserve Chat Completions JSON-object response formats when translating requests to the Responses API. ([#11529](https://github.com/diegosouzapw/OmniRoute/pull/11529)) — thanks @pacocartones +- Fix idempotency fingerprints so Responses API requests include `input` and other semantic generation fields using stable serialization, while excluding credentials and request metadata. ([#11532](https://github.com/diegosouzapw/OmniRoute/pull/11532)) — thanks @pacocartones +- **fix(api):** Generated API CLI commands now enforce required OpenAPI request bodies; Combo test commands forward the required `comboName` body, while API keys created by older writers after migration 149 preserve legacy allow-all Combo access without widening explicit empty allowlists — thanks @marcelokarval ([#12070](https://github.com/diegosouzapw/OmniRoute/pull/12070)) +- **fix(grok-web):** treat upstream streaming failures as failures instead of successful assistant text: error-only streams now fail readiness with HTTP 502, while failures after legitimate content preserve that partial output and terminate through the sanitized stream failure path without a normal `stop` completion. ([#12458](https://github.com/diegosouzapw/OmniRoute/pull/12458)) +- HuggingChat now turns HTTP 200 JSONL generation failures into a sanitized 502 before content, or a fixed public stream failure after partial output, so fallback and request persistence no longer record a false successful stop. ([#12456](https://github.com/diegosouzapw/OmniRoute/pull/12456)) +- **fix(providers):** keep 1min.ai HTTP 200 stream errors out of assistant content, preserve partial output, and expose sanitized terminal errors so pre-content failures can fall back. ([#12466](https://github.com/diegosouzapw/OmniRoute/pull/12466)) +- **fix(sessions):** preserve legacy Sessions data when the additive exclusive-session projection is unavailable, returning an empty projection and warning only once per contiguous outage instead of failing the endpoint; the Sessions badge intentionally reflects the merged legacy and exclusive row count introduced by [#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389) ([#11469](https://github.com/diegosouzapw/OmniRoute/pull/11469)) — thanks @KaspaPulse +- Render Nimble Search with the generic provider icon and serve Opper's proven logo locally. ([#12415](https://github.com/diegosouzapw/OmniRoute/pull/12415)) +- **fix(quality):** `config/quality/provider-assets-provenance.jsonl` was stale against the physical `public/providers/` tree (still listing 225 assets and 5 duplicate-content groups after several provider-retirement PRs removed 85 icon files), which would have made `check:provider-asset-provenance` fail on the next full run. Reconciled the manifest to the real 142-file snapshot (142 asset records, 1 duplicate-content group, 2 newly-registered files marked `unresolved` pending provenance research), repinned `auditedCommit`, and updated the regression test's hardcoded counts to match. ([#11876](https://github.com/diegosouzapw/OmniRoute/pull/11876)) +- fix(ui): wrap quota card footer action buttons to prevent clipping Refresh button on narrow cards — when extra action buttons such as `View credits` are present on Codex cards, allow the footer row to flex-wrap with `shrink-0` buttons so `Refresh now` remains visible and clickable across all responsive card widths ([#11464](https://github.com/diegosouzapw/OmniRoute/pull/11464)) — thanks @adevwithpurpose +- Keep Antigravity Gemini usable when the same connection's Claude weekly quota is empty; generic quota cache stays per-connection for every other provider. ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) — thanks @HouMinXi +- Fixed Tailscale tunnel setup to reject ports outside the valid 1-65535 range and safely install Windows MSI packages from paths containing apostrophes. ([#11534](https://github.com/diegosouzapw/OmniRoute/pull/11534)) — thanks @pacocartones +- Classify Cloudflared and Tailscale process-management routes as local-only and spawn-capable while preserving remote access to read-only tunnel status endpoints. ([#11531](https://github.com/diegosouzapw/OmniRoute/pull/11531)) — thanks @pacocartones +- Electron release workflow: the `publish-npm` job now grants `actions: read` to the reusable `npm-publish.yml` it calls (its `publish` job requests it), which is what made GitHub refuse the whole v3.8.50 run at startup and ship the release with zero desktop assets; a `workflow_dispatch` now builds the requested tag instead of the dispatching branch and can skip the npm leg (`publish_npm=false`) when only re-attaching assets ([#11974](https://github.com/diegosouzapw/OmniRoute/pull/11974)) +- **fix(dashboard):** The Combos page usage guide now reads its dismissal through `useSyncExternalStore` instead of correcting SSR state inside an effect, removing an extra commit of the page tree on every load (and the `react-hooks/set-state-in-effect` error it raised). ([#12671](https://github.com/diegosouzapw/OmniRoute/pull/12671)) +- Electron release: `electron/package-lock.json` regained the optional `electron-builder-squirrel-windows` subtree (13 entries) that `npm ci` had been refusing as out of sync, `electron-release.yml` gained a `build_ref` dispatch input and stops regenerating release notes on a re-attach dispatch, and the npm publish workflow attaches the SBOM to the GitHub Release on dispatch publishes too — so the v3.8.51 tag ships every desktop asset and the SBOM like v3.8.49 did ([#12022](https://github.com/diegosouzapw/OmniRoute/pull/12022)) +- Drained the reds every PR against `release/v3.8.51` was born with: documented the three service-worker build-id variables, registered the six retirement/tunnel tests with the mutation gate, approved `eslint-plugin-react-hooks` in the dependency allowlist, added the six `combo.sort.*` strings to `vi` and `pt-BR`, pointed the ChatGPT Web doc at the real migration-168 test, worded the g4f hint around the member key, and realigned four tests to the retired-provider catalog and the legacy-schema fixtures the retirement migrations touch. ([#11940](https://github.com/diegosouzapw/OmniRoute/pull/11940)) +- **fix(providers):** Claude, Grok, LMArena, Notion, and Perplexity web-cookie transports now use pooled `wreq-js` 3.2 instead of the native sidecar, with all nine supported bindings pinned and audited, and the applicable platform binding plus native-license evidence included in each release artifact ([#12429](https://github.com/diegosouzapw/OmniRoute/pull/12429), supersedes [#11753](https://github.com/diegosouzapw/OmniRoute/pull/11753)). +- **fix(providers):** Zed Hosted streaming failures now trigger fallback before content and end partial streams with a sanitized structured error instead of fake assistant text and a normal-success stop. ([#12455](https://github.com/diegosouzapw/OmniRoute/pull/12455)) +- **fix(test):** capture hook result after render (direct commit `b85d0abb0b`) +- **fix(sync):** repair the two regressions the v3.8.50 sync-back left on release/v3.8.51 (direct commit `5ade9e0851`) +- **revert(models):** drop the memo reset hook ported from #10055 (direct commit `282de841ae`) +- **fix(typecheck):** guard the optional connectionId before the reactive model sync (direct commit `f2825bad75`) +- **fix(quota-share):** release the winner's reserved in-flight slot (#11371) ([#11408](https://github.com/diegosouzapw/OmniRoute/pull/11408)) — thanks @oyi77 +- **fix(providers):** stop silently dropping reasoning effort on opencode families (#10788) ([#11409](https://github.com/diegosouzapw/OmniRoute/pull/11409)) — thanks @oyi77 +- **fix(combo):** enable genuine quota-aware routing for generic providers (antigravity, claude, etc.) ([#11411](https://github.com/diegosouzapw/OmniRoute/pull/11411)) — thanks @HouMinXi +- **fix(oauth):** allow expired token retry before deactivation and activate expired retry loop (#P0) ([#11414](https://github.com/diegosouzapw/OmniRoute/pull/11414)) — thanks @HouMinXi +- **perf(executors):** lazy-load the executor registry — defer class imports + construction to first use (#11220) ([#11421](https://github.com/diegosouzapw/OmniRoute/pull/11421)) — thanks @oyi77 +- **fix:** make release/v3.8.50 CI gates green (run 32786966560 root causes) ([#11450](https://github.com/diegosouzapw/OmniRoute/pull/11450)) — thanks @jonlwheat2-gif +- **fix(sse):** exact-domain cookie match and origin-equality URL assertions ([#11452](https://github.com/diegosouzapw/OmniRoute/pull/11452)) — thanks @hartmark +- **fix(providers):** send Responses API payload during OpenAI-compatible validation ([#11454](https://github.com/diegosouzapw/OmniRoute/pull/11454)) — thanks @tuandinh0801 +- **fix(command-code):** fallback to /alpha/generate for Go plan without Provider API access ([#11455](https://github.com/diegosouzapw/OmniRoute/pull/11455)) — thanks @Chewji9875 +- **fix(responses-continuation):** fail closed on a log-truncated stored input/output array ([#11473](https://github.com/diegosouzapw/OmniRoute/pull/11473)) — thanks @hartmark +- **fix(cli):** write jcode settings as [providers.omniroute] in config.toml ([#11484](https://github.com/diegosouzapw/OmniRoute/pull/11484)) — thanks @hongnoul +- **fix(logging):** raise chat-log array truncation cap to 1000 and unify duplicate implementations ([#11499](https://github.com/diegosouzapw/OmniRoute/pull/11499)) — thanks @hartmark +- **fix:** restore live websocket URL helpers ([#11509](https://github.com/diegosouzapw/OmniRoute/pull/11509)) — thanks @PauloFH +- **perf(core):** eliminate async waterfalls across home, cache, and db operations (#11396) ([#11512](https://github.com/diegosouzapw/OmniRoute/pull/11512)) — thanks @HouMinXi +- **fix(kiro):** add runtime.us-east-1.kiro.dev as first-attempt endpoint ([#11517](https://github.com/diegosouzapw/OmniRoute/pull/11517)) — thanks @rifqiawl +- **fix(providers):** remove duplicate 128-tool truncation in OpencodeExecutor (#11444) ([#11518](https://github.com/diegosouzapw/OmniRoute/pull/11518)) — thanks @HouMinXi +- **fix(providers):** poll onboardUser LRO before classifying BYOP ([#11519](https://github.com/diegosouzapw/OmniRoute/pull/11519)) — thanks @rifqiawl +- **fix(translator):** preserve exact tool name casing for Claude tool_use payloads (#11487) ([#11520](https://github.com/diegosouzapw/OmniRoute/pull/11520)) — thanks @HouMinXi +- **fix(kimi-web):** align health probe with executor domain ([#11521](https://github.com/diegosouzapw/OmniRoute/pull/11521)) — thanks @pacocartones +- **fix(cli):** preserve existing admin password during setup ([#11522](https://github.com/diegosouzapw/OmniRoute/pull/11522)) — thanks @pacocartones +- **fix(mitm):** keep Windows DNS elevation off the POSIX sudo path (#11430) ([#11542](https://github.com/diegosouzapw/OmniRoute/pull/11542)) — thanks @official-burak +- **fix(sse):** scale chat admission by ingest byte budget instead of a fixed request count ([#11548](https://github.com/diegosouzapw/OmniRoute/pull/11548)) — thanks @TheDemonTuan +- **fix:** accept Gemini Web cookie exports ([#11554](https://github.com/diegosouzapw/OmniRoute/pull/11554)) — thanks @rafacpti23 +- **fix(i18n):** complete Brazilian Portuguese translations ([#11558](https://github.com/diegosouzapw/OmniRoute/pull/11558)) — thanks @rafacpti23 +- **fix(sse):** explicit types for openai-responses pureHelpers — clears last failing core typecheck gate ([#11567](https://github.com/diegosouzapw/OmniRoute/pull/11567)) — thanks @jonlwheat2-gif +- **fix(opencode):** hydrate Proxy Pool references for no-auth connections ([#11584](https://github.com/diegosouzapw/OmniRoute/pull/11584)) — thanks @AStupidBear +- **fix(ci):** resolve inherited release/v3.8.51 base-red CI failures ([#11608](https://github.com/diegosouzapw/OmniRoute/pull/11608)) — thanks @jonlwheat2-gif +- **fix(db):** fallback load STORAGE_ENCRYPTION_KEY from env files ([#11614](https://github.com/diegosouzapw/OmniRoute/pull/11614)) — thanks @benzntech +- **fix(providers):** mark Brave Search as no longer having a free tier ([#11621](https://github.com/diegosouzapw/OmniRoute/pull/11621)) — thanks @turbolego +- **fix:** pass tool result content through as string in OpenAI/Claude -> Gemini translation ([#11624](https://github.com/diegosouzapw/OmniRoute/pull/11624)) — thanks @Hsia97 +- **fix(sse):** scope credential health to exact key ([#11628](https://github.com/diegosouzapw/OmniRoute/pull/11628)) — thanks @KaspaPulse +- **fix(install):** guard prepare script against missing husky ([#11634](https://github.com/diegosouzapw/OmniRoute/pull/11634)) — thanks @Prajeeth-12 +- **fix(security):** redact NVIDIA_API_KEY at the log sink in the ad-hoc diag script ([#11639](https://github.com/diegosouzapw/OmniRoute/pull/11639)) +- **fix(types):** clear the two type regressions red on release/v3.8.51 ([#11640](https://github.com/diegosouzapw/OmniRoute/pull/11640)) — thanks @jonlwheat2-gif +- **fix(build):** resolve workspace: protocol dependencies to concrete semver versions during prepublish (#11540) ([#11641](https://github.com/diegosouzapw/OmniRoute/pull/11641)) — thanks @HouMinXi +- **fix(search):** properly resolve configured search connection in /v1/responses pipeline (#11524) ([#11642](https://github.com/diegosouzapw/OmniRoute/pull/11642)) — thanks @HouMinXi +- **fix(authz):** trust cf-connecting-ip for via-proxy detection when peer is Cloudflare edge (#11514) ([#11643](https://github.com/diegosouzapw/OmniRoute/pull/11643)) — thanks @HouMinXi +- **fix(security):** compare the OIDC state cookie in constant time ([#11646](https://github.com/diegosouzapw/OmniRoute/pull/11646)) +- **fix(security):** namespace the dedup hash by the calling API key ([#11649](https://github.com/diegosouzapw/OmniRoute/pull/11649)) +- **fix(models):** gate the Codex-native catalog loop on prefix mode (#11632) ([#11662](https://github.com/diegosouzapw/OmniRoute/pull/11662)) — thanks @xiaoyaner0201 +- **fix(dashboard):** project Codex routing quota in account pools ([#11666](https://github.com/diegosouzapw/OmniRoute/pull/11666)) — thanks @KaspaPulse +- **fix(ddgw):** normaliza system no upstream da release v3.8.51 ([#11692](https://github.com/diegosouzapw/OmniRoute/pull/11692)) — thanks @RhianB14 +- **fix(memory):** honor category filter in GET /api/memory ([#11699](https://github.com/diegosouzapw/OmniRoute/pull/11699)) — thanks @Sabeekhann +- **fix(ci):** detect stale workspace lockfile entries ([#11714](https://github.com/diegosouzapw/OmniRoute/pull/11714)) — thanks @pacocartones +- **fix(ci):** stamp dist/BUILD_SHA before the npm publish provenance gate ([#11721](https://github.com/diegosouzapw/OmniRoute/pull/11721)) +- **fix(docker):** decouple the best-effort Bun image from the release manifest ([#11724](https://github.com/diegosouzapw/OmniRoute/pull/11724)) +- **perf(compression):** avoid cloning memo misses twice ([#11727](https://github.com/diegosouzapw/OmniRoute/pull/11727)) — thanks @RaviTharuma +- **fix(docker):** let the bun digest artifact be absent, not fatal ([#11740](https://github.com/diegosouzapw/OmniRoute/pull/11740)) +- **fix(shared):** block cloud-metadata hosts under default remote-image guard ([#11755](https://github.com/diegosouzapw/OmniRoute/pull/11755)) — thanks @santosraju99-hub +- **fix(api):** preserve caller-provided X-Correlation-Id on chat completions ([#11760](https://github.com/diegosouzapw/OmniRoute/pull/11760)) — thanks @aniruddhaadak80 +- **fix(resilience):** lock permanently retired models instead of short backoff (Gemini ban prevention) ([#11762](https://github.com/diegosouzapw/OmniRoute/pull/11762)) — thanks @turbolego +- **fix:** mark Vercel AI Gateway as passthroughModels ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **fix(resilience):** stop hammering permanently-moved endpoints and billing-suspended accounts ([#11774](https://github.com/diegosouzapw/OmniRoute/pull/11774)) — thanks @turbolego +- **fix(lease):** remove global static reservation and gate routing on live active lease occupancy ([#11775](https://github.com/diegosouzapw/OmniRoute/pull/11775)) — thanks @TheDemonTuan +- **fix(ci):** size the install-upgrade gate to a measured run, and log the pack cost ([#11776](https://github.com/diegosouzapw/OmniRoute/pull/11776)) +- **fix(pwa):** stop serving the stale shell after deploys ([#11779](https://github.com/diegosouzapw/OmniRoute/pull/11779)) — thanks @HouMinXi +- **fix(combo):** lock GitHub models rejected as "not supported" for future requests ([#11781](https://github.com/diegosouzapw/OmniRoute/pull/11781)) — thanks @turbolego +- **fix(cli):** probe both IPv4 and IPv6 loopback for server readiness (#11766) ([#11794](https://github.com/diegosouzapw/OmniRoute/pull/11794)) — thanks @vermasomesh835 +- **fix(sse):** preserve CLIProxyAPI wrappers per connection ([#11797](https://github.com/diegosouzapw/OmniRoute/pull/11797)) — thanks @RaviTharuma +- **fix(providers):** honor model auto-fetch opt-in on create ([#11798](https://github.com/diegosouzapw/OmniRoute/pull/11798)) — thanks @RaviTharuma +- **fix(dashboard):** make model auto-fetch order independent ([#11805](https://github.com/diegosouzapw/OmniRoute/pull/11805)) — thanks @RaviTharuma +- **fix(docker):** make GHA cache exports best effort ([#11806](https://github.com/diegosouzapw/OmniRoute/pull/11806)) — thanks @RaviTharuma +- **fix(kiro):** do not permanently ban on 'User is not authorized to make this call' ([#11809](https://github.com/diegosouzapw/OmniRoute/pull/11809)) — thanks @Deftera186 +- **fix(sse):** keep cache-write tokens in OpenAI-shaped usage ([#11814](https://github.com/diegosouzapw/OmniRoute/pull/11814)) — thanks @TheDemonTuan +- **fix(docs):** resolve relative markdown and wiki links across Fumadocs and GitHub wiki ([#11834](https://github.com/diegosouzapw/OmniRoute/pull/11834)) — thanks @AndrianBalanescu +- **fix(chat):** preserve unstripped model string for passthrough provider routing ([#11840](https://github.com/diegosouzapw/OmniRoute/pull/11840)) — thanks @raheemuddin786 +- **fix(mcp):** avoid async initialization inside sync esm wrappers (#11569) ([#11849](https://github.com/diegosouzapw/OmniRoute/pull/11849)) — thanks @HouMinXi +- **fix(sse):** preserve coherent markdown token boundaries across stream translation chunks (#11606) ([#11851](https://github.com/diegosouzapw/OmniRoute/pull/11851)) — thanks @HouMinXi +- **fix(ci):** run the install-upgrade gate on disk, not on the /tmp tmpfs ([#11855](https://github.com/diegosouzapw/OmniRoute/pull/11855)) +- **fix(skills):** expand shorthand property types in injected tool schemas ([#11857](https://github.com/diegosouzapw/OmniRoute/pull/11857)) — thanks @alvinveroy +- **fix(ci):** publish npm from a hosted runner so provenance is accepted ([#11877](https://github.com/diegosouzapw/OmniRoute/pull/11877)) +- **fix(lint):** pin react hooks plugin to 7.0.1 ([#11886](https://github.com/diegosouzapw/OmniRoute/pull/11886)) +- **fix(ci):** surface and clear release-green ESLint failure ([#11890](https://github.com/diegosouzapw/OmniRoute/pull/11890)) — thanks @MumuTW +- **fix(guardrails):** prevent duplicate prompt-injection-guard log output ([#11936](https://github.com/diegosouzapw/OmniRoute/pull/11936)) — thanks @patrykkopycinski +- **fix(auth):** downgrade expected transient states from warn to debug ([#11937](https://github.com/diegosouzapw/OmniRoute/pull/11937)) — thanks @patrykkopycinski +- **fix(codex):** fail over image generation for imported free plans ([#11948](https://github.com/diegosouzapw/OmniRoute/pull/11948)) — thanks @Bl0ck154 +- **fix(codex):** normalize non-stream responses ([#11951](https://github.com/diegosouzapw/OmniRoute/pull/11951)) — thanks @Bl0ck154 +- **fix(images):** forward Antigravity image size ([#11952](https://github.com/diegosouzapw/OmniRoute/pull/11952)) — thanks @Bl0ck154 +- **fix(db):** invalidate connection cache after upsert ([#11953](https://github.com/diegosouzapw/OmniRoute/pull/11953)) — thanks @Bl0ck154 +- **fix(codex):** restore imported account state ([#11954](https://github.com/diegosouzapw/OmniRoute/pull/11954)) — thanks @Bl0ck154 +- **fix(sse):** give extended-thinking targets the reasoning readiness budget ([#11959](https://github.com/diegosouzapw/OmniRoute/pull/11959)) — thanks @abhisheksharma2411 +- **fix(dashboard):** use opaque background and readable text color on cost chart tooltips ([#11960](https://github.com/diegosouzapw/OmniRoute/pull/11960)) — thanks @ZaimMarzuki +- **fix(antigravity):** send complete loadCodeAssist metadata (ideType/platform/pluginType as numeric enums) ([#11969](https://github.com/diegosouzapw/OmniRoute/pull/11969)) — thanks @rifqiawl +- **fix(core):** resolve DB init race condition and reasoning translation ([#12003](https://github.com/diegosouzapw/OmniRoute/pull/12003)) — thanks @raheemuddin786 +- **fix(admission):** exclude reclaimable page cache from the cgroup pressure ratio ([#12017](https://github.com/diegosouzapw/OmniRoute/pull/12017)) — thanks @HouMinXi +- **fix(build):** prune non-production trees in NFT trace excludes and tsconfig ([#12028](https://github.com/diegosouzapw/OmniRoute/pull/12028)) — thanks @Chewji9875 +- **fix(migrations):** add renamed migration compatibility for 056/073/077/101 ([#12036](https://github.com/diegosouzapw/OmniRoute/pull/12036)) — thanks @oyi77 +- **fix(sse):** default crash-guard logger to console.warn, not console ([#12042](https://github.com/diegosouzapw/OmniRoute/pull/12042)) — thanks @HouMinXi +- **fix(catalog):** derive vision/modalities for built-in auto combos from effective target pool ([#12046](https://github.com/diegosouzapw/OmniRoute/pull/12046)) — thanks @Prajeeth-12 +- **fix(release):** the packaged-app smoke verifies the database opened, not a driver line the primary path never prints (twin of #12032) ([#12047](https://github.com/diegosouzapw/OmniRoute/pull/12047)) +- **fix(barrel):** migrate src/app/ off the @/lib/localDb barrel import (#11795 Phase 2) ([#12051](https://github.com/diegosouzapw/OmniRoute/pull/12051)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate src/lib/ off the localDb barrel to direct db imports (#59) ([#12052](https://github.com/diegosouzapw/OmniRoute/pull/12052)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate open-sse, src/shared, src/sse, src/models, src/domain off the @/lib/localDb barrel import (#11795 Phase 4) ([#12053](https://github.com/diegosouzapw/OmniRoute/pull/12053)) — thanks @jonlwheat2-gif +- **fix(barrel):** delete the @/lib/localDb barrel — every consumer migrated (#11795 Phase 5) ([#12055](https://github.com/diegosouzapw/OmniRoute/pull/12055)) — thanks @jonlwheat2-gif +- **fix(radar):** restore D12 public boundary ([#12057](https://github.com/diegosouzapw/OmniRoute/pull/12057)) +- **fix(dev):** bound webpack and Tailwind scans ([#12075](https://github.com/diegosouzapw/OmniRoute/pull/12075)) — thanks @backryun +- **fix(dev):** isolate root layout settings reads ([#12076](https://github.com/diegosouzapw/OmniRoute/pull/12076)) — thanks @backryun +- **fix(dev):** reduce instrumentation executor fan-out (phase 3) ([#12078](https://github.com/diegosouzapw/OmniRoute/pull/12078)) — thanks @backryun +- **fix(dev):** make logging resources HMR-singleton ([#12079](https://github.com/diegosouzapw/OmniRoute/pull/12079)) — thanks @backryun +- **fix(sse):** keep unavailable forced connections scoped ([#12080](https://github.com/diegosouzapw/OmniRoute/pull/12080)) — thanks @keeltrace +- **fix(dev):** isolate batch dispatch from instrumentation ([#12081](https://github.com/diegosouzapw/OmniRoute/pull/12081)) — thanks @backryun +- **fix(ci):** reconcile release test contract drift ([#12082](https://github.com/diegosouzapw/OmniRoute/pull/12082)) — thanks @backryun +- **fix(ci):** accept CVE-2025-68121 in the prebuilt tls-client .so, auto-close base-red issues, guard Scorecard on the default branch ([#12085](https://github.com/diegosouzapw/OmniRoute/pull/12085)) +- **fix(db):** drop three consumer-less 1proxy exports — dead-code base-red on release/v3.8.51 after the barrel deletion (#12055) ([#12087](https://github.com/diegosouzapw/OmniRoute/pull/12087)) +- **fix(api):** clear the six API-route TypeScript regressions the new gate landed red on ([#12094](https://github.com/diegosouzapw/OmniRoute/pull/12094)) +- **fix(release):** never let the tag-push Create Release append auto notes to the curated body ([#12096](https://github.com/diegosouzapw/OmniRoute/pull/12096)) +- **fix(sse):** honor CLIProxyAPI environment API key ([#12099](https://github.com/diegosouzapw/OmniRoute/pull/12099)) — thanks @RaviTharuma +- **fix(oauth):** bind Google refresh to the client that issued the token ([#12106](https://github.com/diegosouzapw/OmniRoute/pull/12106)) — thanks @HouMinXi +- **fix(dashboard):** make RequestLoggerDetail loadable outside Next — CSS via globals.css + CJS/ESM interop (#11703 base-reds) ([#12114](https://github.com/diegosouzapw/OmniRoute/pull/12114)) +- **fix(leases):** project status lease row to lease columns so joined connection PII never escapes ([#12115](https://github.com/diegosouzapw/OmniRoute/pull/12115)) — thanks @geek007git +- **fix(plugins):** do not kill the plugin process when a fire-and-forget hook times out ([#12116](https://github.com/diegosouzapw/OmniRoute/pull/12116)) — thanks @geek007git +- **fix(guardrails):** restore injection-guard logging on middleware-only routes ([#12117](https://github.com/diegosouzapw/OmniRoute/pull/12117)) — thanks @geek007git +- **fix(plugins):** refresh stored manifest from disk on activate so new hook fields reach existing installs ([#12120](https://github.com/diegosouzapw/OmniRoute/pull/12120)) — thanks @geek007git +- **fix(codex):** preserve existing provider state when bulk-import upserts a matching connection ([#12122](https://github.com/diegosouzapw/OmniRoute/pull/12122)) — thanks @geek007git +- **fix(ci):** clear the two base-reds the 2026-08-30 merge batch left on release/v3.8.51 (round 3) ([#12123](https://github.com/diegosouzapw/OmniRoute/pull/12123)) +- **fix(sse):** strip type:'custom' from Claude tools on agentrouter dispatch ([#12126](https://github.com/diegosouzapw/OmniRoute/pull/12126)) — thanks @wahidsadik371-coder +- **fix(sse):** stop advertising video providers the dispatcher cannot run ([#12131](https://github.com/diegosouzapw/OmniRoute/pull/12131)) — thanks @quiterunner-commits +- **fix(search):** treat HTTP 432 and plan limit errors as transient cooldown ([#12139](https://github.com/diegosouzapw/OmniRoute/pull/12139)) — thanks @HouMinXi +- **fix(sse):** refuse an AI Horde queue that cannot fit the request budget ([#12143](https://github.com/diegosouzapw/OmniRoute/pull/12143)) — thanks @quiterunner-commits +- **fix(ci):** clear the base-reds the afternoon merge batch left on release/v3.8.51 (round 5: provider count 352, TS2554/TS2677) ([#12144](https://github.com/diegosouzapw/OmniRoute/pull/12144)) +- **fix(translator):** strip neutral tool_choice when tools absent in Responses-to-Chat (#12141) ([#12166](https://github.com/diegosouzapw/OmniRoute/pull/12166)) — thanks @HouMinXi +- **fix(guardrails):** pass providerId to getResolvedModelCapabilities in checkComboVision (#12112) ([#12169](https://github.com/diegosouzapw/OmniRoute/pull/12169)) — thanks @HouMinXi +- **fix(translator):** strip plaintext reasoning content for opaque responses backends (#12128) ([#12171](https://github.com/diegosouzapw/OmniRoute/pull/12171)) — thanks @HouMinXi +- **perf(stream):** compile hot-path regexes once, bound token caches, fix quadratic buffering ([#12179](https://github.com/diegosouzapw/OmniRoute/pull/12179)) — thanks @opensource-elearning +- **fix:** resolve compression worker file using runtime anchors instead of… ([#12183](https://github.com/diegosouzapw/OmniRoute/pull/12183)) — thanks @NightStalker-87 +- **fix:** prevent Claude Code session kills via liveness-aware readiness + auto model echo ([#12189](https://github.com/diegosouzapw/OmniRoute/pull/12189)) — thanks @opensource-elearning +- **fix(translator):** preserve falsy primitive values in Gemini and Antigravity function response results ([#12191](https://github.com/diegosouzapw/OmniRoute/pull/12191)) — thanks @Siva010 +- **fix(oauth):** mark empty Antigravity projectId as degraded and clear stale errors (#11284) ([#12205](https://github.com/diegosouzapw/OmniRoute/pull/12205)) — thanks @HouMinXi +- **fix(providers):** gate the Codex auto-ping usage read on the shared quota throttle ([#12209](https://github.com/diegosouzapw/OmniRoute/pull/12209)) — thanks @abhisheksharma2411 +- **fix(combos):** prioritize SQLite row id over inner JSON id and notify delete errors ([#12213](https://github.com/diegosouzapw/OmniRoute/pull/12213)) — thanks @HouMinXi +- **fix(providers):** mark featherless.ai as no longer having a free tier (`hasFree: false`) ([#12216](https://github.com/diegosouzapw/OmniRoute/pull/12216)) — thanks @turbolego +- **fix(deepseek-web):** stop Turbopack dev panic in the PoW worker path resolver ([#12221](https://github.com/diegosouzapw/OmniRoute/pull/12221)) — thanks @hartmark +- **fix(chat-admission):** clarify local 503 source ([#12223](https://github.com/diegosouzapw/OmniRoute/pull/12223)) — thanks @keeltrace +- **fix(dev):** silence webpack runtime module warnings ([#12228](https://github.com/diegosouzapw/OmniRoute/pull/12228)) — thanks @backryun +- **fix(db):** use module.require for CommonJS runtime driver loading ([#12230](https://github.com/diegosouzapw/OmniRoute/pull/12230)) — thanks @raheemuddin786 +- **fix(memory):** honest probe-driven FTS5 keyword status + memory_id rowid sync ([#12231](https://github.com/diegosouzapw/OmniRoute/pull/12231)) — thanks @raheemuddin786 +- **fix(system):** propagate abort signal to stream reader in HTTP version checks ([#12232](https://github.com/diegosouzapw/OmniRoute/pull/12232)) — thanks @raheemuddin786 +- **fix(resilience):** isolate local host execution errors from provider circuit breakers ([#12233](https://github.com/diegosouzapw/OmniRoute/pull/12233)) — thanks @raheemuddin786 +- **fix(sse):** sort injected tools deterministically for prompt caching ([#12234](https://github.com/diegosouzapw/OmniRoute/pull/12234)) — thanks @raheemuddin786 +- **fix(combo):** always clear the loop-safety timer, not just on the happy path (#11804) ([#12245](https://github.com/diegosouzapw/OmniRoute/pull/12245)) +- **fix(quality):** base-red pair — stryker tap registration + turn-pin suites aligned to the window gate ([#12255](https://github.com/diegosouzapw/OmniRoute/pull/12255)) — thanks @backryun +- **fix(dev):** qualify Turbopack runtime boundaries ([#12258](https://github.com/diegosouzapw/OmniRoute/pull/12258)) — thanks @backryun +- **fix(combo):** fall back to full pool when collapsed sole survivor is context-too-small ([#12278](https://github.com/diegosouzapw/OmniRoute/pull/12278)) — thanks @opensource-elearning +- **fix(combo):** bound the pre-dispatch unavailable skip so a stale label cannot dark a pool (#12168) ([#12285](https://github.com/diegosouzapw/OmniRoute/pull/12285)) +- **fix(security):** strip Qwen/Alibaba console-session cookies from provider API responses ([#12287](https://github.com/diegosouzapw/OmniRoute/pull/12287)) +- **fix(usage):** console-aware Token Plan guidance + subscription hint on bailian 401 ([#12288](https://github.com/diegosouzapw/OmniRoute/pull/12288)) +- **fix(resourcePressure):** log numeric detail on every rejection, recover faster ([#12293](https://github.com/diegosouzapw/OmniRoute/pull/12293)) — thanks @hartmark +- **fix(release):** drain the 2026-09-01 base-red window — passthrough usage regression + radarPage i18n keys ([#12327](https://github.com/diegosouzapw/OmniRoute/pull/12327)) +- **fix(ci):** openapi-security-tiers checker must honor routeGuard patterns + imported prefixes ([#12350](https://github.com/diegosouzapw/OmniRoute/pull/12350)) — thanks @ggiak +- **perf(ci):** cache node_modules in the npm-ci-retry composite (#8084 D3) ([#12408](https://github.com/diegosouzapw/OmniRoute/pull/12408)) +- **revert(ui):** point CTAs back at their real destinations ([#12410](https://github.com/diegosouzapw/OmniRoute/pull/12410)) +- **fix(release):** drain the 2026-09-02 base-red — rerank-providers import + api-typecheck baseline ratchet ([#12414](https://github.com/diegosouzapw/OmniRoute/pull/12414)) +- **fix(types):** make system prompt injection noImplicitAny-safe ([#12416](https://github.com/diegosouzapw/OmniRoute/pull/12416)) +- **fix(quality):** validate typecheck baseline schema ([#12419](https://github.com/diegosouzapw/OmniRoute/pull/12419)) +- **fix(memory):** resolve rerank provider node cache import ([#12421](https://github.com/diegosouzapw/OmniRoute/pull/12421)) +- **fix(ci):** repair release-root regressions — pack dedup, web-session syntax, uc-image ids ([#12423](https://github.com/diegosouzapw/OmniRoute/pull/12423)) — thanks @backryun +- **fix(providers):** separate MaxAI and UC credential contracts ([#12431](https://github.com/diegosouzapw/OmniRoute/pull/12431)) +- **fix(cli):** preserve Claude settings on config set ([#12432](https://github.com/diegosouzapw/OmniRoute/pull/12432)) — thanks @KrzysiekSko +- **fix(providers):** repair the maxai credential block truncated by merge auto-resolve ([#12433](https://github.com/diegosouzapw/OmniRoute/pull/12433)) +- **fix(quality):** record the 2026-09-02 merged growth in the file-size baseline ([#12434](https://github.com/diegosouzapw/OmniRoute/pull/12434)) +- **fix(uc):** route UC error strings through sanitizeErrorMessage; allowlist the retired codex id ([#12437](https://github.com/diegosouzapw/OmniRoute/pull/12437)) +- **fix(adapta):** redact streamed upstream errors ([#12438](https://github.com/diegosouzapw/OmniRoute/pull/12438)) +- **fix(sse):** remap non-contiguous upstream tool_calls index to a gap-free output_index ([#12445](https://github.com/diegosouzapw/OmniRoute/pull/12445)) — thanks @hartmark +- **fix(quality):** detect duplicate tool_calls entries in one response ([#12446](https://github.com/diegosouzapw/OmniRoute/pull/12446)) — thanks @hartmark +- **fix(conversations):** resolve turn content OmniRoute never sends back to the client ([#12447](https://github.com/diegosouzapw/OmniRoute/pull/12447)) — thanks @hartmark +- **fix(combo):** do not treat credits-exhausted 401 as auth skip ([#12449](https://github.com/diegosouzapw/OmniRoute/pull/12449)) — thanks @RaviTharuma +- **fix(auth):** do not park healthy quota accounts as expired ([#12452](https://github.com/diegosouzapw/OmniRoute/pull/12452)) — thanks @RaviTharuma +- **fix(responses-continuation):** fail closed on a collector-truncated, empty output array ([#12460](https://github.com/diegosouzapw/OmniRoute/pull/12460)) — thanks @hartmark +- **fix(logging):** raise the SSE payload collector's default cap ([#12461](https://github.com/diegosouzapw/OmniRoute/pull/12461)) — thanks @hartmark +- **fix(resilience):** surface Responses failed.error.message in 502s ([#12472](https://github.com/diegosouzapw/OmniRoute/pull/12472)) — thanks @RaviTharuma +- **fix(security):** unbiased maxai X-Random nonce + stricter URL/regex assertions ([#12502](https://github.com/diegosouzapw/OmniRoute/pull/12502)) +- **fix(video):** re-anchor transcript log-redaction so PII/credential maskers can't reopen the leak (#12150 P1 follow-up) ([#12503](https://github.com/diegosouzapw/OmniRoute/pull/12503)) +- **fix(monitoring):** serve cached credentialHealth off the request path ([#12533](https://github.com/diegosouzapw/OmniRoute/pull/12533)) — thanks @RaviTharuma +- **fix(quota):** keep Antigravity Gemini usable when Claude weekly is empty ([#12566](https://github.com/diegosouzapw/OmniRoute/pull/12566)) — thanks @HouMinXi +- **fix(authz):** hard-gate every credential export and CLI-config write (GHSA-5926-2w35-7h4q) ([#12600](https://github.com/diegosouzapw/OmniRoute/pull/12600)) +- **fix(docs):** restore the Next build — REMOVED_PROVIDERS.md had no frontmatter (base-red #12581) ([#12610](https://github.com/diegosouzapw/OmniRoute/pull/12610)) +- **fix(quality):** re-point the zcodeProtocol public-creds allowlist to line 313 ([#12615](https://github.com/diegosouzapw/OmniRoute/pull/12615)) +- **fix(security):** close 3 advisories — search baseUrl exfil, sk- in the error sanitizer, bifrost relay header leak ([#12620](https://github.com/diegosouzapw/OmniRoute/pull/12620)) +- **fix(providers):** sanitize boolean required and nested bare maps for Gemini (#12269) ([#12624](https://github.com/diegosouzapw/OmniRoute/pull/12624)) — thanks @HouMinXi +- **fix(resilience):** keep Overloaded STREAM_EARLY_EOF off the provider breaker ([#12626](https://github.com/diegosouzapw/OmniRoute/pull/12626)) — thanks @HouMinXi +- **fix(catalog):** re-audit free-tier quotas against official pages ([#12649](https://github.com/diegosouzapw/OmniRoute/pull/12649)) +- **fix(dashboard):** keep a request's pending-tracking id stable across combo target retries ([#12650](https://github.com/diegosouzapw/OmniRoute/pull/12650)) — thanks @hartmark +- **fix(tests):** retire dead model ids from the chat-pipeline integration suite (base-red #12581) ([#12670](https://github.com/diegosouzapw/OmniRoute/pull/12670)) +- **fix:** resolve SqliteError no such table compression_run_telemetry during cleanup ([#12682](https://github.com/diegosouzapw/OmniRoute/pull/12682)) — thanks @groovecityJO +- **fix(combo):** treat a pin-only step as implicit connection allowlist ([#12697](https://github.com/diegosouzapw/OmniRoute/pull/12697)) — thanks @HouMinXi +- **fix(ci):** ship .npmrc in published package so legacy-peer-deps applies to consumers (#11544) ([#12699](https://github.com/diegosouzapw/OmniRoute/pull/12699)) — thanks @KooshaPari +- **fix(docker):** pin 4 CLI tools to exact versions (#12576) ([#12703](https://github.com/diegosouzapw/OmniRoute/pull/12703)) — thanks @KooshaPari +- **fix(docker):** document and harden cli profile trust boundary (#12570) ([#12706](https://github.com/diegosouzapw/OmniRoute/pull/12706)) — thanks @KooshaPari +- **fix(sse):** redact video transcript in pre-guardrail rejected-request logs (#12150 P2 item 7) ([#12710](https://github.com/diegosouzapw/OmniRoute/pull/12710)) +- **fix(i18n):** quote placeholder in OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES description (#12505) ([#12769](https://github.com/diegosouzapw/OmniRoute/pull/12769)) — thanks @KooshaPari +- **fix(api/pricing):** surface validation error message as string, not raw object (#12494) ([#12771](https://github.com/diegosouzapw/OmniRoute/pull/12771)) — thanks @KooshaPari +- **fix(sse):** re-enable prompt compression for native Codex passthrough ([#12834](https://github.com/diegosouzapw/OmniRoute/pull/12834)) — thanks @thomasmaerz +- **fix(dashboard):** model health tests for a provider node set to the Responses API now call `/v1/responses` with a Responses-shaped body instead of `/v1/chat/completions` — those models were reported as `Provider returned HTTP 200 but no text content` even though the same model answered normally through `/v1/responses` ([#13070](https://github.com/diegosouzapw/OmniRoute/issues/13070)) ([#13087](https://github.com/diegosouzapw/OmniRoute/pull/13087)) — thanks @ntdatt812 +- fix(routing): stop a round-robin combo's "opencode" targets from collapsing onto the opencode-zen connection (#11912) ([#13283](https://github.com/diegosouzapw/OmniRoute/pull/13283)) +- fix(routing): stop the reactive-compaction debug log from lying when compression is globally disabled (#11977) ([#13276](https://github.com/diegosouzapw/OmniRoute/pull/13276)) +- fix(electron): relativize standalone-bundle symlink targets so Stage 8 manifest verification stops failing on Windows (#11979) ([#13251](https://github.com/diegosouzapw/OmniRoute/pull/13251)) +- fix(dashboard): surface a visible error when Compression Studio's combined preview run fails (#12061) ([#13288](https://github.com/diegosouzapw/OmniRoute/pull/13288)) +- fix(dashboard): make the compression "Effective pipeline" preview honor the active profile and warn when the master switch is off (#12063) ([#13287](https://github.com/diegosouzapw/OmniRoute/pull/13287)) +- fix(providers): scope TinyCMS Web signer's DOM shims to each call instead of leaking them for the process lifetime, and surface a clean HTTP status on a non-JSON interception-toggles error (#12072) ([#13254](https://github.com/diegosouzapw/OmniRoute/pull/13254)) +- fix(guardrails): stop Vision Bridge from re-selecting a model locked after a 404 (#12111) ([#13259](https://github.com/diegosouzapw/OmniRoute/pull/13259)) +- fix(sse): require Responses-shaped body before native OpenAI-compatible passthrough (#12129) ([#13278](https://github.com/diegosouzapw/OmniRoute/pull/13278)) +- fix(providers): minimax-m3 now collapses manual thinking.type:"enabled" to adaptive, preventing upstream 400 (2013) (#12132) ([#13249](https://github.com/diegosouzapw/OmniRoute/pull/13249)) +- fix(db): scope model visibility overrides by modality so hiding a Chat model no longer hides an identically-ID'd Image/Embeddings/etc. model (#12172) ([#13258](https://github.com/diegosouzapw/OmniRoute/pull/13258)) +- fix(db): scope local-provider apiKey dedup to matching base URL so LM Studio/Ollama-style connections support multiple accounts (#12173) ([#13250](https://github.com/diegosouzapw/OmniRoute/pull/13250)) +- fix(providers): refresh Trae's stale Referer/Origin and forward user timezone so imported connections stop failing with 401 (#12190) ([#13255](https://github.com/diegosouzapw/OmniRoute/pull/13255)) +- fix(providers): route opencode-go/gpt-5.6-luna to /responses instead of /chat/completions (#12196) ([#13275](https://github.com/diegosouzapw/OmniRoute/pull/13275)) +- fix(dashboard): allow deleting the last extra-upstream-header row even when invalid (#12251) ([#13252](https://github.com/diegosouzapw/OmniRoute/pull/13252)) +- **fix(i18n):** translate pre-existing `__MISSING__:` keys for `combo.sort`, `requestLogger.detail` expand/collapse, `common.profile`, and `settings.resilienceCredentialHealth*` across 39 locales ([#12272](https://github.com/diegosouzapw/OmniRoute/issues/12272)) ([#13034](https://github.com/diegosouzapw/OmniRoute/pull/13034)) — thanks @HouMinXi +- fix(cli): run the Node.js runtime compatibility guard before the heavy `tsx/esm` + Commander import chain so an unsupported runtime gets a clear message instead of a raw `Invalid regular expression flags` crash (#12296) ([#13253](https://github.com/diegosouzapw/OmniRoute/pull/13253)) +- fix(dashboard): refresh the providers list after deleting a compatible provider node (#12298) ([#13256](https://github.com/diegosouzapw/OmniRoute/pull/13256)) +- fix(usage): fail closed on API-key budget enforcement when a provider's `auto` routing alias has no pricing row, instead of silently counting it as $0 (#12341) ([#13257](https://github.com/diegosouzapw/OmniRoute/pull/13257)) +- **fix(providers):** include the submitted Agnes video model when polling by `video_id` ([#12356](https://github.com/diegosouzapw/OmniRoute/pull/12356)) — thanks @pacocartones +- **fix(routing):** custom OpenAI-compatible nodes now honor the saved Chat/Responses API type after edits instead of letting the node's original ID prefix override the live connection setting ([#11884](https://github.com/diegosouzapw/OmniRoute/issues/11884)). ([#12358](https://github.com/diegosouzapw/OmniRoute/pull/12358)) — thanks @pacocartones +- fix(sse): surface an error instead of a silent empty 200 when a Claude stream closes with zero bytes (#12398) ([#13285](https://github.com/diegosouzapw/OmniRoute/pull/13285)) +- fix(oauth): warn before the dead localhost:8080 redirect in antigravity/gemini `oauth start` (#12413) ([#13265](https://github.com/diegosouzapw/OmniRoute/pull/13265)) +- fix(providers): stop devin-cli spawn error from double-closing the SSE controller (#12517) ([#13261](https://github.com/diegosouzapw/OmniRoute/pull/13261)) +- **fix(rerank):** clamp Voyage `top_k` to the documents actually sent after empty-string filtering, and honor `return_documents: false` in the NVIDIA response adapter (#12523 — thanks @pacocartones) +- **fix(audio):** `/v1/audio/translations` now resolves combo names the way `/v1/audio/transcriptions` already does, so a combo that `GET /v1/models` advertises is fanned out to its targets instead of being rejected with `400 Invalid translation model: . Use format: provider/model`; literal `provider/model` ids and unknown bare names behave as before (#12536 — thanks @pacocartones) +- **fix(gemini):** strip the JSON-Schema-2020-12 `prefixItems` keyword from Gemini tool schemas at every nesting level, so Claude Code tool definitions no longer fail with `400 Unknown name "prefixItems"` on Gemini models (#12540 — thanks @pacocartones) +- **fix(api-manager):** Expose an accessible loading status while API keys are fetched instead of an empty accessibility tree (#12541 — thanks @pacocartones) +- **fix(video):** Clamp `estimateJpegFrameBytes` at zero for padding-only payloads and build the three encode-side frame data URIs from `JPEG_FRAME_DATA_URI_PREFIX` instead of a repeated literal (#12543 — thanks @pacocartones) +- **fix(devin):** accept Windows `DEVIN_AGENTIC_HOME` sandbox paths (`C:\...\.sandbox\...`) in the isolated-home check so the Devin Claude Bridge no longer fails closed on Windows ([#12405](https://github.com/diegosouzapw/OmniRoute/issues/12405)) (#12545 — thanks @pacocartones) +- **fix(db):** Add `conversation_turn_nodes` and orphaned `agentic_conversations` to the auto-cleanup cycle under the existing `retention.callLogs` window, so identity nodes whose call-log content has already been purged no longer accumulate without bound in `storage.sqlite` (#12548 — thanks @pacocartones) +- **fix(i18n):** Wrap the `~/.claude/profiles//settings.json` placeholder in ICU single quotes in the `OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES` feature-flag description across all 42 locales and the TypeScript default, so next-intl no longer fails with `INVALID_MESSAGE: UNCLOSED_TAG` and the Feature Flags card shows the description instead of the raw key (#12549 — thanks @pacocartones) +- **fix(orchestration):** `updateCloudAgentTask` now publishes the task's real `status` on `agent.task.updated` when an update only touches `result`, `activities` or `error`, instead of the fabricated `"updated"` state, and stays silent when no row matched the id (#12550 — thanks @pacocartones) +- **fix(i18n):** the home "Recent Requests" panel and the Provider Topology legend are now translated instead of rendering English copies on non-English dashboards; the legend reads its own `home.topologyLegend*` labels with consistent casing rather than borrowing the memory-settings "Recent" and analytics "Error" strings (#12551 — thanks @pacocartones). +- **docs(reference):** bring the `FEATURE_FLAGS.md` catalog back to 1:1 with `featureFlagDefinitions.ts` — 20 missing flags added, the two `*_BLOCK_THRESHOLD` env-only knobs moved out of the flag tables, category/total counts and the Live WS port corrected, guarded by a static test (#12552 — thanks @pacocartones) +- **fix(i18n):** backfill missing `usage.kiloPass*` strings in 39 locales and restore `featureFlagOmnirouteDisableThinkingLevelVariantsDescription` in `pt.json` ([#12561](https://github.com/diegosouzapw/OmniRoute/issues/12561)) ([#13027](https://github.com/diegosouzapw/OmniRoute/pull/13027)) — thanks @HouMinXi +- fix(docker): default docker-compose app ports (dashboard/API/live-WS) to loopback instead of `0.0.0.0`, closing the anonymous `/v1` LAN/WAN exposure gap left open by `REQUIRE_API_KEY=false` (#12568) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): close DNS-rebinding SSRF gap in webhook outbound-URL guard (#12569) ([#13243](https://github.com/diegosouzapw/OmniRoute/pull/13243)) +- fix(docker): require a per-session token on the VNC browser CDP bridge and isolate it on a dedicated Docker network (#12571) ([#13241](https://github.com/diegosouzapw/OmniRoute/pull/13241)) +- fix(open-sse): write Adobe Firefly session tokens and cookie jars with 0700/0600 permissions instead of the process umask (#12572) ([#13242](https://github.com/diegosouzapw/OmniRoute/pull/13242)) +- fix(api): remove hardcoded wildcard CORS in openai-to-gemini-sse.ts so the centralized fail-closed CORS gate is the sole source of `Access-Control-Allow-Origin` (#12573) ([#13239](https://github.com/diegosouzapw/OmniRoute/pull/13239)) +- fix(api): enforce API key policy (budget/rate-limit/schedule/endpoint scoping) on the ElevenLabs speech-to-text, text-to-speech and voices proxy routes (#12574) ([#13244](https://github.com/diegosouzapw/OmniRoute/pull/13244)) +- fix(sse): cap HuggingChat NDJSON body size and bound the read loop with the fetch timeout so a stalled or hostile upstream cannot buffer unbounded memory (#12577) ([#13282](https://github.com/diegosouzapw/OmniRoute/pull/13282)) +- fix(docker): scope the cliproxyapi/qdrant/bifrost sidecars to loopback by default and forward `CLIPROXYAPI_MANAGEMENT_KEY` into the cliproxyapi container so its management API is not left both unauthenticated and LAN-published (#12578) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): create DB export temp paths with `fs.mkdtempSync` instead of predictable timestamps (#12579) ([#13237](https://github.com/diegosouzapw/OmniRoute/pull/13237)) +- Cline 401 bodies that say "re-authenticate your Cline account" classify as a refreshable OAuth token, not a terminal expired key. The cooling panel no longer labels every cooldown as a 429; it shows the recorded last error instead. (#12594) ([#13060](https://github.com/diegosouzapw/OmniRoute/pull/13060)) — thanks @HouMinXi +- **fix(catalog):** degrade unknown combo targets instead of dropping LCD modalities, and persist OpenRouter `architecture.input_modalities` into the capability snapshot ([#12613](https://github.com/diegosouzapw/OmniRoute/issues/12613)) ([#13042](https://github.com/diegosouzapw/OmniRoute/pull/13042)) — thanks @HouMinXi +- fix(providers): send `x-api-key` instead of `Authorization: Bearer` for OpenCode Zen's `/v1/responses` endpoint (Muse Spark Contributor models), fixing a 401 on OmniRoute's auth header (#12633) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(sse): surface the actionable "Auggie CLI not found" message when the shell reports a missing `auggie` binary via exit code instead of a spawn error (#12645) ([#13263](https://github.com/diegosouzapw/OmniRoute/pull/13263)) +- **fix(config):** Persist deletions of built-in background-degradation entries — when a stored settings record exists its `degradationMap` is now authoritative instead of being merged under the defaults, so an entry the user removed in the dashboard no longer reappears on the next apply or restart ([#12424](https://github.com/diegosouzapw/OmniRoute/issues/12424)) ([#12647](https://github.com/diegosouzapw/OmniRoute/pull/12647)) — thanks @pacocartones +- **fix(gamification):** action-count badge milestones (First Token, Token Consumer, Token Machine, Token Whale, and the token-sharing tier) are now backed by a durable `xp_action_counts` counter incremented in `addXp()`, instead of a live `COUNT(*)` over `xp_audit_log`. The audit log is pruned by `retention.xpAuditLog` (default 30 days), so on a default install those "lifetime" milestones were really "actions in the last 30 days" and unlocked badges could stop unlocking once old rows aged out. `getActionCount()` and `checkActionCountBadges()` now read the same durable source, and a migration backfills existing totals from the surviving audit rows ([#12546](https://github.com/diegosouzapw/OmniRoute/issues/12546)) ([#12651](https://github.com/diegosouzapw/OmniRoute/pull/12651)) — thanks @pacocartones +- **fix(images):** `/v1/images/edits` now iterates a combo's targets the same way `/v1/images/generations` does (#9239) instead of flattening a bare combo to its first target. A combo whose first target is not edit-capable — or lacks credentials — now falls through to a later edit-capable target rather than hard-erroring, and missing credentials are skipped (not a hard `401`) to match the generations path. The per-target skip/terminal classification is extracted into a shared `runImageComboTargets` loop, so generations behavior is unchanged ([#12547](https://github.com/diegosouzapw/OmniRoute/issues/12547)). ([#12653](https://github.com/diegosouzapw/OmniRoute/pull/12653)) — thanks @pacocartones +- fix(sse): add first-byte watchdog to the TLS-fingerprint transport so a stalled wreq body falls back instead of hanging for minutes (#12656) ([#13272](https://github.com/diegosouzapw/OmniRoute/pull/13272)) +- fix(sse): exempt tiny-budget reasoning probes from combo quality failure and surface persisted-cooldown skips in ALL_TARGETS_SKIPPED diagnostics (#12659) ([#13269](https://github.com/diegosouzapw/OmniRoute/pull/13269)) +- fix(models): declare the real ~1M contextLength for OpenCode Zen's Muse Spark 1.2 models instead of falling back to the 200000 provider default (#12681) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(oauth): align codebuddy-cn OAuth User-Agent with the chat/usage CLI version to avoid WAF false positives (#12702) ([#13264](https://github.com/diegosouzapw/OmniRoute/pull/13264)) +- fix(dashboard): surface an authentication-required banner instead of silently blanking database settings for a guest session (#12709) ([#13270](https://github.com/diegosouzapw/OmniRoute/pull/13270)) +- **fix(resilience):** stop unbounded queue that hangs 6min until Aborted — gate, provider slot, and Bottleneck queue now share a per-connection `maxWaitMs` budget; `fail-closed` on exhaust (503); execution backstop `executionMaxWaitMs` overridable per connection with upstream clamp ([#12715](https://github.com/diegosouzapw/OmniRoute/pull/12715)) — thanks @maxmad64bis (with thanks to @Tushar49 for surfacing the slow-provider need in #12635) +- **fix(auto-combo):** every mode pack now carries `quality` and `reliability` weights (`quality` 0.02, 0.03 in `quality-first`; `reliability` 0.03, 0.04 in `reliability-first`), so selecting a pack no longer silences either signal; `DEFAULT_WEIGHTS` is unchanged ([#12731](https://github.com/diegosouzapw/OmniRoute/pull/12731)) — thanks @maxmad64bis +- **fix(sse):** the provider execution pipeline keeps the upstream error `code`/`type` again — both were lost when the non-streaming leg was extracted, so a config-class `422` (Antigravity missing project) degraded into a generic account cooldown instead of reaching the gate that recognises it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(db):** a legacy `call_logs` table no longer aborts startup — the provider-stats index is created after the column healing runs, not before it, so an install predating `request_type` opens instead of failing with `no such column` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(catalog):** a malformed row in the operator-writable custom-models blob no longer kills every `auto/*` pool ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(tests):** realign the guards that the same day's merges left asserting the old behavior — the provider-count total after GreenPT and EURouter, the GLM stream buffer slot that `#12925` turned into a declared parameter, and the injection scan bound that `#13104` reshaped from a head-only window into head-plus-tail ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- fix(cache): fold tool_choice/tools/response_format into the semantic cache signature so a cached tool_calls response can no longer be replayed for a request whose tool policy forbids it (#12734) ([#13267](https://github.com/diegosouzapw/OmniRoute/pull/13267)) +- **fix(models):** stop treating provider-supplied `isFree:true`/`:free`/`0/0` flags as trusted unless the provider has a documented free tier — only the shipped free-tier catalog decides otherwise; fetched `isFree:true` trusted only for a free-tier provider, custom `isFree:true` via a trusted path ([#12744](https://github.com/diegosouzapw/OmniRoute/pull/12744)) — thanks @maxmad64bis +- fix(memory): authenticate the internal /v1/rerank loopback call so memory reranking no longer silently degrades to unranked order when REQUIRE_API_KEY=true (#12745) ([#13268](https://github.com/diegosouzapw/OmniRoute/pull/13268)) +- fix(sse): parse Ollama Cloud's current usage markup (`$X of $Y used` aria-label, nested width style) (#12749) ([#13260](https://github.com/diegosouzapw/OmniRoute/pull/13260)) +- fix(cli): setup-opencode no longer sends an active context's management token to `/v1/models` when `--api-key`/`OMNIROUTE_API_KEY` is supplied — an explicit flag or the env var now always outranks the context's token, and the flag itself is no longer swallowed by the parent program's global `--api-key` option (#12783) ([#13246](https://github.com/diegosouzapw/OmniRoute/pull/13246)) +- fix(sse): register Arcee AI in the executor provider registry so requests reach api.arcee.ai instead of silently falling back to OpenAI (#12784) ([#13277](https://github.com/diegosouzapw/OmniRoute/pull/13277)) +- **fix(quota):** share one upstream quota read when identical saturation checks arrive at the same time, so traffic bursts don't multiply provider API calls ([#12787](https://github.com/diegosouzapw/OmniRoute/pull/12787)) — thanks @maxmad64bis +- **fix(db):** ignore expired or invalid rate-limit cooldown writes so a stale timestamp can't lock a connection that should be usable — clearing still works as before ([#12788](https://github.com/diegosouzapw/OmniRoute/pull/12788)) — thanks @maxmad64bis +- **fix(providers):** single source for provider order with `xao` ranking alongside `xai-oauth` ([#12790](https://github.com/diegosouzapw/OmniRoute/pull/12790)) — thanks @maxmad64bis +- **fix(routing):** off-table models check the free-model catalog before inheriting premium prices, latency bootstraps from the observed pool median, async tiers read live database pricing with a 90-day freshness gate, and the tier cache invalidates on every pricing write ([#12792](https://github.com/diegosouzapw/OmniRoute/pull/12792)) — thanks @maxmad64bis +- **fix(auto-combo):** snapshot scoring uses the observed per-connection account tier and per-model quality instead of neutral constants, and circuit-open providers rank lower at build time ([#12794](https://github.com/diegosouzapw/OmniRoute/pull/12794)) — thanks @maxmad64bis +- **fix(pool):** empty `auto/*` pools now say why they are empty, and the models listing uses the same paid check as routing ([#12795](https://github.com/diegosouzapw/OmniRoute/pull/12795)) — thanks @maxmad64bis +- fix(routing): recognize CLIProxyAPI's 'unknown provider for model' 400 as fallback-worthy (#12800) ([#13284](https://github.com/diegosouzapw/OmniRoute/pull/13284)) +- **fix(sse):** translate-mode streams now emit the estimated token counts as a trailing usage-only chunk before `[DONE]` when the upstream stays silent, so metered chat clients see totals instead of nothing ([#12828](https://github.com/diegosouzapw/OmniRoute/pull/12828)) — thanks @maxmad64bis +- **fix(db):** provider stats stay truthful on empty and legacy databases: fallback counts default to `0` instead of `null`, latency averages read `null` (not `0`) when no durations were recorded, failures logged before the error-type column existed group under `pre_migration` instead of `unclassified`, and per-provider queries use two new composite indexes ([#12832](https://github.com/diegosouzapw/OmniRoute/pull/12832)) — thanks @maxmad64bis +- fix(nvidia): fail open when a synced model catalog goes stale instead of gating forever (#12849) ([#13248](https://github.com/diegosouzapw/OmniRoute/pull/13248)) +- **fix(db):** WAL maintenance lives in its own module: a `TRUNCATE` checkpoint that hits a busy database now warns with its streak and retries once via `PASSIVE` instead of logging success, and closing-time checkpoints no longer report success on builds without a database file, with the busy totals visible in the authenticated monitoring health payload ([#12853](https://github.com/diegosouzapw/OmniRoute/pull/12853)) — thanks @maxmad64bis +- **fix(health):** quota with no snapshots reads empty instead of a contradicting 0% ([#12857](https://github.com/diegosouzapw/OmniRoute/pull/12857)) — thanks @maxmad64bis +- fix(a2a): accept the dashboard's own session cookie on /a2a so "Run message/send" no longer fails with "Unauthorized: missing or invalid API key" (#12888) ([#13271](https://github.com/diegosouzapw/OmniRoute/pull/13271)) +- **fix(logs):** the Logs grid's in-memory filter pass no longer discards rows the SQL query already matched — selecting an API key from the dropdown (which sends the key's id) returns its calls again, the Combo tab shows every combo instead of only those whose name contains a "1", and the model filter and search cover the same columns as the query ([#12896](https://github.com/diegosouzapw/OmniRoute/pull/12896)) — fixes [#12873](https://github.com/diegosouzapw/OmniRoute/issues/12873) — thanks @ntdatt812 +- **fix(a2a):** `/api/a2a/status` now builds the agent card from the request that asked for it, so a gateway reached at a non-localhost host no longer advertises `http://localhost:20128` as its A2A URL ([#12918](https://github.com/diegosouzapw/OmniRoute/pull/12918)). — thanks @ntdatt812 +- **fix(compression):** progressive aging now appends its `[COMPRESSED:aging:…]` annotation after a turn's `tool_result` blocks instead of in front of them, so Anthropic no longer rejects aged conversations with "`tool_use` ids were found without `tool_result` blocks immediately after" ([#12920](https://github.com/diegosouzapw/OmniRoute/pull/12920)). — thanks @ntdatt812 +- **fix(bedrock):** model import now resolves context limits for every vendor prefix instead of only `anthropic.*`, so `global.openai.gpt-5.6-*` no longer imports with a null `inputTokenLimit` and gets rejected pre-flight at the 200k default ([#12921](https://github.com/diegosouzapw/OmniRoute/pull/12921)). — thanks @ntdatt812 +- **fix(stream):** the 64 KB stream buffer GLM asks for is honoured instead of dropped, and the type error it caused no longer fails the API Route Typecheck gate on every open PR ([#12925](https://github.com/diegosouzapw/OmniRoute/pull/12925)) — thanks @ntdatt812 +- **fix(guardrails):** mask PII inside a `tool_result`'s nested content array, which the masker walked past while redacting its sibling block ([#12930](https://github.com/diegosouzapw/OmniRoute/pull/12930)) — thanks @ntdatt812 +- **fix(dashboard):** the Radar catalog table no longer leaves absent data unexplained — empty limits, unknown context windows, and unreported capabilities each explain themselves on hover, and a new check keeps it that way ([#12937](https://github.com/diegosouzapw/OmniRoute/pull/12937)) — thanks @maxmad64bis +- **fix(sse):** geo-blocked opencode requests rotate to the next account proxy instead of failing, so one refused egress no longer aborts the whole chain ([#12941](https://github.com/diegosouzapw/OmniRoute/pull/12941)) — thanks @maxmad64bis +- **fix(codex):** dashboard "clear cooldown" and the CAS recovery path now drop nested `codexScopeRateLimitedUntil` maps in the same write that nulls `rate_limited_until`, and fresh quota snapshots with headroom lift fallback-sourced scope cooldowns parked by quota preflight ([#12817](https://github.com/diegosouzapw/OmniRoute/issues/12817), [#12860](https://github.com/diegosouzapw/OmniRoute/issues/12860), [#12951](https://github.com/diegosouzapw/OmniRoute/pull/12951)) The dashboard reset-credit button (`consumeCodexResetCredit`) now exercises that same snapshot path after a successful redeem, so a filled quota bar is enough to unpark a leftover fallback Codex child without another manual clear. — thanks @HouMinXi +- **fix(db):** resolve `sql-wasm.wasm` across global npm install and hoisted layouts, ensuring OmniRoute can boot cleanly on Node 24 when native `better-sqlite3` is uncompiled. ([#13035](https://github.com/diegosouzapw/OmniRoute/pull/13035)) — thanks @HouMinXi +- **fix(api):** `DELETE /v1/batches/delete-completed` now sweeps only the calling API key's own completed batches (batches with no owner stay out of a key-scoped sweep on purpose), with an explicit instance-wide mode reserved for authenticated dashboard sessions, a 401 for a presented key that is unknown, revoked, deactivated, banned or expired (never falling through to the session branch), audit logging of every sweep, a sanitized 500 on failure, an owner-scoped file half (a key-scoped sweep never nulls a file another tenant owns) and an atomic sweep — chunked in 200-batch transactions in instance mode — so a mid-way error never leaves a batch pointing at a nulled file (GHSA-wvxc-jp3v-5mg5) ([#12969](https://github.com/diegosouzapw/OmniRoute/pull/12969)) +- **fix(combo):** quota-weighted routing stops drawing on an out-of-credit connection — a 402 now invalidates the stored quota snapshot instead of leaving its stale remaining percentage in place, and a snapshot older than 10 minutes no longer counts as confident headroom for the primary pool ([#12972](https://github.com/diegosouzapw/OmniRoute/pull/12972)) ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- **fix(vision):** Custom Models with "Vision capable" checked no longer have image requests swapped to `glm/glm-4.6v` when the client sends the advertised alias (`vllm/path/...`) or the bare path-shaped id — Vision Bridge now matches the stored override for all three id forms ([#12758](https://github.com/diegosouzapw/OmniRoute/issues/12758)) ([#12974](https://github.com/diegosouzapw/OmniRoute/pull/12974)) — thanks @HouMinXi +- **fix(sse):** transient opencode upstream failures rotate to the next account proxy instead of failing, so one flapping egress no longer aborts the whole chain ([#12975](https://github.com/diegosouzapw/OmniRoute/pull/12975)) — thanks @maxmad64bis +- **fix(azure):** Deployments from GPT-6 onward now send `max_completion_tokens` instead of `max_tokens`, which Azure rejects with HTTP 400. The rule matched a literal `gpt-5`, so each new generation arrived broken; it now matches the generation range, while `gpt-35-turbo` still keeps `max_tokens`. ([#13007](https://github.com/diegosouzapw/OmniRoute/pull/13007)) — thanks @ntdatt812 +- **fix(db):** add an opt-in self-restart circuit for sustained critical memory pressure, gate post-cleanup VACUUM behind a minimum freed-rows threshold, and checkpoint the SQLite WAL every 5 minutes with a size guard that escalates to TRUNCATE, so a growing WAL can no longer stall the event loop into a full outage. ([#13011](https://github.com/diegosouzapw/OmniRoute/pull/13011)) — thanks @HouMinXi +- **fix(auth):** an explicit connection pin may probe a quota-disabled row once and re-enable it on success ([#12874](https://github.com/diegosouzapw/OmniRoute/issues/12874)) ([#13017](https://github.com/diegosouzapw/OmniRoute/pull/13017)) — thanks @HouMinXi +- **fix(api):** thread `X-OmniRoute-Fallback-Attempts` through combo chat completions so streaming and non-streaming responses report how many prior legs were attempted ([#13038](https://github.com/diegosouzapw/OmniRoute/pull/13038)) — thanks @HouMinXi +- Restore provider failure classification and credential refresh on non-streaming requests: classify non-2xx failures to lock models on per-model quota exhaustion, update connection rate limits from headers and body, and pass credential refresh handlers to pipeline execution so 401 tokens can be refreshed and retried (#13043). ([#13069](https://github.com/diegosouzapw/OmniRoute/pull/13069)) — thanks @HouMinXi +- **fix(responses):** wrap forced-non-streaming web_search fallback JSON as Responses SSE so Codex still sees `response.completed` ([#13050](https://github.com/diegosouzapw/OmniRoute/pull/13050)) — thanks @HouMinXi +- **fix(antigravity):** Preserve upstream thought-token usage in normalized completion and reasoning token counts ([#13055](https://github.com/diegosouzapw/OmniRoute/pull/13055)) — thanks @pacocartones +- Fix a concurrency-slot leak in the DeepSeek PoW solver: a worker that failed to spawn (for example a missing worker script) never released its slot, so `MAX_CONCURRENT_WORKERS` failures disabled the solver until restart. ([#13097](https://github.com/diegosouzapw/OmniRoute/pull/13097)) — thanks @anhtahaylove +- **fix(acp):** bound the ACP session output buffers — `stdoutBuffer` and `stderrBuffer` now cap at 1 MiB keeping the most recent output behind a visible `[...output truncated...]` marker, and `stderrBuffer` is reset per prompt instead of accumulating for the lifetime of the session. ([#13100](https://github.com/diegosouzapw/OmniRoute/pull/13100)) — thanks @anhtahaylove +- **fix(acp):** release the `stdout`/`exit` listeners and the idle timer that a `sendPrompt` timeout used to leave attached to the `acpManager` singleton, and drop sessions that exited on their own from the session map instead of keeping them forever. ([#13096](https://github.com/diegosouzapw/OmniRoute/pull/13096)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection and PII scanners now read the text a `tool_result` block carries on `content` (string or nested block list), in messages and in system blocks, so tool output is judged by the same rules as user text ([#13101](https://github.com/diegosouzapw/OmniRoute/pull/13101)) — thanks @ntdatt812 +- **fix(gamification):** close the badge notification SSE stream when the request signal is already aborted before the stream starts — a client that disconnects while the route is still awaiting auth used to leave both the 2s unlock poll and the 15s heartbeat running for the lifetime of the process. ([#13106](https://github.com/diegosouzapw/OmniRoute/pull/13106)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection scan now spends its 16 KB budget on both ends of the request instead of the first 16 KB only, so `system`, `instructions`, `query`, `documents` and the newest turns are no longer hidden behind one long message ([#13104](https://github.com/diegosouzapw/OmniRoute/pull/13104)) — thanks @ntdatt812 +- **fix(dashboard):** expose the Volcano Ark console cookie on quota scraping and unwrap connect-error objects so the dashboard shows the upstream message ([#13107](https://github.com/diegosouzapw/OmniRoute/pull/13107)) — thanks @HouMinXi +- **fix(db):** release the `beforeExit`/`SIGINT`/`SIGTERM` handlers when a `node:sqlite` adapter closes, so a closed adapter and its database handle are no longer pinned to `process` for the lifetime of the run — the same treatment #7494 gave the sql.js adapter. ([#13109](https://github.com/diegosouzapw/OmniRoute/pull/13109)) — thanks @anhtahaylove +- **fix(translator):** `contentSchema` and `unevaluatedItems` are now treated as subschema positions by the tool-schema sanitizer, so a truncation placeholder in either is replaced with a permissive schema instead of being forwarded as a string ([#13110](https://github.com/diegosouzapw/OmniRoute/pull/13110)) — thanks @ntdatt812 +- **fix(cli-helper):** clear the `createLogStream` timeout on the abort path — `stop()` aborts the in-flight fetch and returned through the `signal.aborted` branch, which skipped `clearTimeout` and left an armed timer per stopped stream. The stream reader is now also cancelled when the read loop exits early. ([#13114](https://github.com/diegosouzapw/OmniRoute/pull/13114)) — thanks @anhtahaylove +- **fix(docs):** drop TLS-impersonation, MITM-decrypt, supply-chain attestation, and XOR-mask writeups from the public `/docs` catalog and Docker image. Files stay in git for engineers; operators who need them open the repo, not the website. ([#13136](https://github.com/diegosouzapw/OmniRoute/pull/13136)) — thanks @HouMinXi +- **fix(combo):** parse numeric-epoch `rate_limited_until` in the combo cooldown read path ([#13141](https://github.com/diegosouzapw/OmniRoute/pull/13141)) — thanks @maxmad64bis +- **fix(opencode):** both OpenCode plugins now reject a gateway address typed without `http://` at configuration time, instead of publishing every model with an api url no client can call, and the v2 plugin no longer publishes a model card whose api url is blank or relative ([#13142](https://github.com/diegosouzapw/OmniRoute/pull/13142)) — thanks @maxmad64bis +- **fix(providers):** lock opencode model on upstream 400 model-unavailable ([#13146](https://github.com/diegosouzapw/OmniRoute/pull/13146)) — thanks @maxmad64bis +- **fix(logging):** keep the provider exchange rather than the raw client bodies when a call log exceeds its size budget, and show that recovered payload in the request-detail panel instead of replacing it with the stored response body ([#13147](https://github.com/diegosouzapw/OmniRoute/pull/13147)) — thanks @maxmad64bis +- **fix(traffic-inspector):** the WebSocket route no longer leaks a traffic-buffer subscriber and a 30s ping timer when the client socket is already closed at handler time — listeners are attached before any resource is acquired, a destroyed socket bails out early, and the ping interval stops on a dead socket where `write()` never throws ([#13155](https://github.com/diegosouzapw/OmniRoute/pull/13155)) — thanks @anhtahaylove +- **fix(telegram):** bound the per-user API key cache in the Telegram chat proxy so a burst of distinct chat ids can no longer grow the process heap without limit ([#13165](https://github.com/diegosouzapw/OmniRoute/issues/13165)) ([#13166](https://github.com/diegosouzapw/OmniRoute/pull/13166)) — thanks @anhtahaylove +- **fix(stream):** cancel the upstream response body when the JSON-to-SSE sniff unwinds on a body timeout, so a stalled upstream no longer pins the connection ([#13169](https://github.com/diegosouzapw/OmniRoute/issues/13169)) ([#13171](https://github.com/diegosouzapw/OmniRoute/pull/13171)) — thanks @anhtahaylove +- **fix(telegram):** authenticate webhook deliveries with Telegram's `secret_token` so an unauthenticated caller can no longer mint API keys or spend upstream quota ([#13172](https://github.com/diegosouzapw/OmniRoute/issues/13172)) ([#13175](https://github.com/diegosouzapw/OmniRoute/pull/13175)) — thanks @anhtahaylove +- **fix(models):** give discoverable Gemini 3.8 Flash ids their own 65536 output spec so Antigravity no longer clamps them to 16384 ([#13195](https://github.com/diegosouzapw/OmniRoute/pull/13195)) — thanks @HouMinXi +- **fix(dashboard):** leftover catalog-removed provider rows (gemini-cli) can be listed and purged from the providers page ([#13067](https://github.com/diegosouzapw/OmniRoute/issues/13067)) ([#13197](https://github.com/diegosouzapw/OmniRoute/pull/13197)) — thanks @HouMinXi +- **fix(auth):** a dashboard session now requires the `authenticated: true` claim that login, OIDC and the session refresh already emit — a JWT merely signed with `JWT_SECRET` (for example the Cursor CLI passthrough token, which any API-key holder can obtain) no longer verifies as the `auth_token` cookie on any route, the WebSocket handshake or the live server; existing sessions keep working ([#13298](https://github.com/diegosouzapw/OmniRoute/issues/13298)) ([#13375](https://github.com/diegosouzapw/OmniRoute/pull/13375)) +- **fix(api):** `DELETE /v1/batches/delete-completed` now applies the caller's API-key policy (endpoint allowlist, schedule, usage cap, rate limit) like every other `/v1` route, commits the key-scoped sweep per 200-batch chunk instead of holding one write lock for the whole sweep, refuses to loop without progress, and rejects a scope that names both a key and `allTenants` ([#13684](https://github.com/diegosouzapw/OmniRoute/pull/13684)) +- Restore the API-route typecheck gate: the non-streaming leg result lost its discriminated-union narrowing after the server-owned tool loop reassignment, producing 13 new TS2339 diagnostics in `chatCore.ts`. ([#12963](https://github.com/diegosouzapw/OmniRoute/pull/12963)) +- Restore four non-streaming behaviours the server-owned tool loop refactor dropped: client aborts map to 499 with the fixed `Request aborted` message, an aborted request no longer logs a synthetic `clientResponse`, Claude prompt-cache telemetry is recorded again, and a body that cannot be canonicalized no longer throws when the tool loop is off. ([#12990](https://github.com/diegosouzapw/OmniRoute/pull/12990)) +- **fix(skills):** The CLI registry parser now reads positionals declared with `.addArgument()`, not only those written inline in `.command()`. `tunnel create [type]` was being published as `tunnel create`, so the agent-skills sync gate reported drift on every branch and regenerating would have deleted the argument. ([#13009](https://github.com/diegosouzapw/OmniRoute/pull/13009)) — thanks @ntdatt812 +- **fix(combos):** dashboard combo test uses a short prompt, serial probes, and a 60s timeout so reasoning models and rate-limited free pools do not fail the health check ([#13001](https://github.com/diegosouzapw/OmniRoute/pull/13001)) — thanks @HouMinXi +- fix(compression): terminate idle worker threads on eviction so long-running instances stop leaking OS threads and MessagePorts ([#13091](https://github.com/diegosouzapw/OmniRoute/pull/13091)) — thanks @anhtahaylove +- Redact Google API keys of any length in error bodies: the pattern required exactly 39 characters, so shorter or longer `AIza…` credentials passed through unredacted. ([#12964](https://github.com/diegosouzapw/OmniRoute/pull/12964)) +- **fix(grok-cli):** a 402 "Grok Build usage balance exhausted" parks that Grok login as out of credit (Grok Build CLI, grok.com cookie, and xAI OAuth share the weekly pool). Combo routing then tries the next login instead of locking the model for every account in the pool ([#13061](https://github.com/diegosouzapw/OmniRoute/pull/13061)) — thanks @HouMinXi +- fix(compression): spawn the LLMLingua worker with a file URL object so compression actually runs instead of silently failing open on Node ([#13093](https://github.com/diegosouzapw/OmniRoute/pull/13093)) — thanks @anhtahaylove +- **fix(test):** run the local `test` and `test:unit` scripts at concurrency 4 so a full-suite run no longer exhausts the machine's commit charge and kills unrelated processes ([#13187](https://github.com/diegosouzapw/OmniRoute/pull/13187)) — thanks @anhtahaylove +- **fix(monitoring):** `GET /api/monitoring/health` `credentialHealth` now includes a bounded `failedConnections` list (`connectionId`, `status`, sanitized `lastError`) when the probe-cache gauge `failed>0`, plus `source: probe-cache` and a cheap `staleDbNonOkCount` for sticky SQLite `test_status` on active rows. Documents that the live gauge is not `provider_connections.test_status`. ([#12876](https://github.com/diegosouzapw/OmniRoute/pull/12876)) — thanks @RaviTharuma +- **fix(resilience):** an openai-compatible multi-upstream gateway no longer marks the whole connection `credits_exhausted` when a single upstream (or the `/models` / representative-model health probe) returns 402. Quota failures stay model-scoped; true connection-wide auth failures are unchanged. Single-credential 402 key disable ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239)) is preserved. ([#12875](https://github.com/diegosouzapw/OmniRoute/pull/12875)) — thanks @RaviTharuma +- **fix(dashboard):** nine fixes on the Orchestration Canvas, all diagnosed in the Phase 2 reviews. A source that fails now keeps the timestamp of its FIRST failure instead of being re-stamped every poll — the stale line said "since the last poll" no matter how long the source had been down, and the churn also defeated the snapshot's stable identity (it serializes the source list), so the canvas re-rendered on every tick while anything was broken. A source that HAD data and only then started failing is flagged too: previously only a source with no node at all got the warning, so a source that went down mid-session kept a healthy-looking node forever. The rest are pointwise: clicking a filter chip cancels the pending search debounce (left armed, it fired ~300ms later and silently reverted the chip); the search input carries an accessible name; `?state=running, failed` parses like the unpadded form instead of dropping the padded value; the CSV toggle helper is defined once in `model/urlParams.ts` rather than twice; the agents tab tells "nothing running" apart from "the filter matched nothing", offering a clear-filters button instead of setup links that would be wrong advice there; edges stop emitting SMIL particles above 40 simultaneously active edges, keeping the colored stroke; and the drawer's error banner clears when a retried action succeeds. ([#12983](https://github.com/diegosouzapw/OmniRoute/pull/12983)) +- **fix(i18n):** the nine locales added with the EU-language batch (Greek, Estonian, Irish, Croatian, Lithuanian, Latvian, Maltese, Slovenian, Serbian) were missing the eleven Orchestration Canvas keys that Phase 3 introduced, so the compare-runs panel and the "no runs match these filters" empty state fell back to English in those languages (`deepMergeFallback` substitutes English for an absent key, so nothing rendered blank — it rendered untranslated). The coverage gate does not catch this: it enforces an 80% floor per locale, and eleven missing keys out of ~13,000 leaves coverage at 99.9%. Translated for real in each language, calibrated against the wording each file already uses for "run", "filter" and "skill". ([#13294](https://github.com/diegosouzapw/OmniRoute/pull/13294)) +- **fix(dashboard):** five follow-ups from the Phase 2 Orchestration Canvas review. The History drawer now shows the memory section for runs that already left the live TTL window: the persisted `memory_hits` event is parsed into `metadata.memoryHits` with the same defensive validation the drawer applies, and — because that event is observability rather than a state transition — it no longer leaks into the timeline, where it had been inheriting the task's state and rendering as a duplicate transition. The A2A memory recall runs against its own 1.5s deadline instead of inheriting the memory backend's 30s one; overshooting degrades exactly like any other recall failure (no hits, task proceeds), and the timer is cleared on both paths. Repeating a Conductor run carries its `requirements.cli`/`requirements.model` forward, so the new run is pinned to the same runner profile and model rather than drifting to whatever the fleet picks. A successful repeat from the Agents tab now focuses the run it created, instead of leaving the operator on the finished one. And the auth test for `POST /api/conductor/tasks` moved into the shared `ROUTES` array rather than restating the pattern. ([#12988](https://github.com/diegosouzapw/OmniRoute/pull/12988)) +- fix(plugins): stop leaking an exit listener per plugin hook timeout, which triggered MaxListenersExceededWarning on plugins that ignore SIGTERM ([#13092](https://github.com/diegosouzapw/OmniRoute/pull/13092)) — thanks @anhtahaylove +- **fix(validation):** Provider node edits no longer fail with a generic "Invalid request" when the optional daily-quota reset fields are left blank. The dashboard sends `dailyQuotaResetTimezone` and `dailyQuotaResetHour` as `null`, and only the hour accepted it. ([#13066](https://github.com/diegosouzapw/OmniRoute/issues/13066)) ([#13083](https://github.com/diegosouzapw/OmniRoute/pull/13083)) — thanks @ntdatt812 +- **fix(combo):** quota-aware expansion drops banned, inactive, missing, and wrong-provider connections before quota fetch or model dispatch; pins and allowlists stay selectors, not a bypass. Antigravity automatic exhaustion now requires a reported zero remaining, so a positive balance below 1% stays eligible. ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- Fix an intermittent failure in the quota-weighted routing test suite: scores are a function of `Date.now()`, so peers with identical quota scored microseconds apart never tied and swapped order. ([#12946](https://github.com/diegosouzapw/OmniRoute/pull/12946)) +- fix(volcengine): resolve quota window mapping gaps, safe multi-connection console binding, and response cookie sanitization ([#12950](https://github.com/diegosouzapw/OmniRoute/pull/12950)) — thanks @HouMinXi +- **fix(test):** resolve the WebDAV handler path with `fileURLToPath` so the suite's 37 WebDAV tests run on Windows instead of failing with a doubled `C:\C:\` drive prefix ([#13196](https://github.com/diegosouzapw/OmniRoute/pull/13196)) — thanks @anhtahaylove +- **fix(ui):** fall back from invalid AnySearch icon alias (direct commit `9587e07b69`) +- **fix(sse):** stop rebuilding a truncated summary from the collector's cap-dropped event array ([#12718](https://github.com/diegosouzapw/OmniRoute/pull/12718)) — thanks @hartmark +- **fix(conversations):** show a pending spinner for unresolved tool nodes instead of "(empty)" ([#12727](https://github.com/diegosouzapw/OmniRoute/pull/12727)) — thanks @hartmark +- **fix(streaming):** fail fast when an upstream stream produces only lifecycle/heartbeat events, never real content ([#12741](https://github.com/diegosouzapw/OmniRoute/pull/12741)) — thanks @hartmark +- **fix(responses-continuation):** bridge the write-in-flight window with an in-memory pending store ([#12854](https://github.com/diegosouzapw/OmniRoute/pull/12854)) — thanks @hartmark +- **fix(translator):** drop or map agent_message on Chat Completions fallback ([#12880](https://github.com/diegosouzapw/OmniRoute/pull/12880)) — thanks @RaviTharuma +- **fix(api):** page and stream-complete GET /v1/models for large catalogs ([#12882](https://github.com/diegosouzapw/OmniRoute/pull/12882)) — thanks @RaviTharuma +- **fix(quota):** align AUTH window labels with usage API and clarify 503 ([#12884](https://github.com/diegosouzapw/OmniRoute/pull/12884)) — thanks @RaviTharuma +- **fix(ci):** clear two base-reds on release/v3.8.51 — mutation-coverage gate + image-only-model guard ([#12945](https://github.com/diegosouzapw/OmniRoute/pull/12945)) +- **fix(api):** scope batch bulk-delete to the calling API key ([#13211](https://github.com/diegosouzapw/OmniRoute/pull/13211)) +- **fix(tests):** align model-sync log assertion to shipped path redaction (base-red) ([#13227](https://github.com/diegosouzapw/OmniRoute/pull/13227)) +- **fix(test):** make the ToS heading guard actually require the parentheses ([#13228](https://github.com/diegosouzapw/OmniRoute/pull/13228)) +- **fix(quality):** register 4 drifted covering tests in stryker tap.testFiles ([#13229](https://github.com/diegosouzapw/OmniRoute/pull/13229)) +- **fix(logging):** mask *-api-key headers in request-log pipeline (#13273) ([#13401](https://github.com/diegosouzapw/OmniRoute/pull/13401)) — thanks @KooshaPari +- **fix(tests):** correct stale Jina catalog prefix in custom-models test ([#13403](https://github.com/diegosouzapw/OmniRoute/pull/13403)) — thanks @KooshaPari +- **fix(backend):** prune old backups after health-check-repair snapshots (#13308) ([#13404](https://github.com/diegosouzapw/OmniRoute/pull/13404)) — thanks @KooshaPari +- **fix(backend):** guard memory_fts_au trigger with WHEN clause to stop FTS bloat (#13326) ([#13405](https://github.com/diegosouzapw/OmniRoute/pull/13405)) — thanks @KooshaPari +- **fix(free-tiers):** remove obsolete Together $25 signup credit (#12526) ([#13407](https://github.com/diegosouzapw/OmniRoute/pull/13407)) — thanks @KooshaPari +- **fix:** skip non-chat generation models in Test all models ([#13409](https://github.com/diegosouzapw/OmniRoute/pull/13409)) — thanks @KooshaPari +- **fix(providers):** de-duplicate agy and antigravity model catalogs ([#13410](https://github.com/diegosouzapw/OmniRoute/pull/13410)) — thanks @KooshaPari +- **fix(dashboard):** send provider in body for mini-playground web search ([#13411](https://github.com/diegosouzapw/OmniRoute/pull/13411)) — thanks @KooshaPari +- **fix(resilience):** add CJK quota-exhaustion patterns to 429 classifier ([#13412](https://github.com/diegosouzapw/OmniRoute/pull/13412)) — thanks @KooshaPari +- **fix(backend):** eval runner: failed calls no longer score as passed + dotAll regex ([#13413](https://github.com/diegosouzapw/OmniRoute/pull/13413)) — thanks @KooshaPari +- **fix(backend):** eval runner opts out of output-style and memory injection ([#13414](https://github.com/diegosouzapw/OmniRoute/pull/13414)) — thanks @KooshaPari +- **fix:** chain serial quarantine step in npm test script (fixes #13286) ([#13418](https://github.com/diegosouzapw/OmniRoute/pull/13418)) — thanks @KooshaPari +- **fix(antigravity):** derive stable session ID from credentials for prompt-cache reuse (#13305) ([#13424](https://github.com/diegosouzapw/OmniRoute/pull/13424)) — thanks @KooshaPari +- **fix(memory):** merge memory into top-level system field instead of unshifting at messages[0] (#13425) ([#13427](https://github.com/diegosouzapw/OmniRoute/pull/13427)) — thanks @KooshaPari +- **fix(compression):** preserve instruction blocks from lossy engines ([#13523](https://github.com/diegosouzapw/OmniRoute/pull/13523)) — thanks @KooshaPari +- **fix(compression):** preserve polarity words and newlines in ultra heuristic ([#13524](https://github.com/diegosouzapw/OmniRoute/pull/13524)) — thanks @KooshaPari +- **fix(compression):** run user preservePatterns before built-in patterns (#13457) ([#13525](https://github.com/diegosouzapw/OmniRoute/pull/13525)) — thanks @KooshaPari +- **fix(reasoning):** expand deepseek-v4 regex to match v4.1+ and add command-code provider (#13430) ([#13528](https://github.com/diegosouzapw/OmniRoute/pull/13528)) — thanks @KooshaPari +- **fix(providers):** add alwaysReasons metadata for reka-flash-3 ([#13531](https://github.com/diegosouzapw/OmniRoute/pull/13531)) — thanks @KooshaPari +- **fix(playground):** allow Compare columns to scroll independently ([#13532](https://github.com/diegosouzapw/OmniRoute/pull/13532)) — thanks @KooshaPari +- **fix(security):** empty whitelist allows through to prevent admin lockout ([#13534](https://github.com/diegosouzapw/OmniRoute/pull/13534)) — thanks @KooshaPari +- **fix(tests):** add DISABLE_IOREG_STRATEGY env var for macOS test compatibility (fixes #13467) ([#13539](https://github.com/diegosouzapw/OmniRoute/pull/13539)) — thanks @KooshaPari +- **fix(zed-hosted):** map developer role and Gemini enums for Zed proxy ([#13543](https://github.com/diegosouzapw/OmniRoute/pull/13543)) — thanks @KooshaPari +- **fix(analytics):** normalize provider aliases to canonical id in usage_history ([#13545](https://github.com/diegosouzapw/OmniRoute/pull/13545)) — thanks @KooshaPari +- **fix(combo):** use traceId for call log id to prevent UNIQUE constraint failure ([#13546](https://github.com/diegosouzapw/OmniRoute/pull/13546)) — thanks @KooshaPari +- **fix(auth):** check model lockout before returning synthetic noauth connection ([#13547](https://github.com/diegosouzapw/OmniRoute/pull/13547)) — thanks @KooshaPari +- **fix(guardrails):** skip credential redaction for base64 image data URLs ([#13550](https://github.com/diegosouzapw/OmniRoute/pull/13550)) — thanks @KooshaPari +- **fix(resilience):** increase requestQueue.maxWaitMs default from 15s to 30s ([#13553](https://github.com/diegosouzapw/OmniRoute/pull/13553)) — thanks @KooshaPari +- **fix(docs):** document OMNIROUTE_READY_TIMEOUT_MS and allowlist the test-only DISABLE_IOREG_STRATEGY ([#13692](https://github.com/diegosouzapw/OmniRoute/pull/13692)) + ### 📝 Maintenance ---- +- **docs(readme):** reconcile live v3.8.50 provider, free-tier, CLI, routing, test, community, sponsor, acknowledgment, and SVG metrics with their audited source denominators, including a deduplicated OmniRoute-in-Action snapshot and distinct contributor rankings for merged pull requests, GitHub-attributed commits, and Git history ([#11356](https://github.com/diegosouzapw/OmniRoute/pull/11356)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **docs:** sync the canonical provider count 354 → 356 across `README.md`, `AGENTS.md`, `llm.txt` (+ 42 i18n mirrors), the four README SVG diagrams, `docs/reference/PROVIDER_REFERENCE.md` (regenerated) and the `package.json` description after Opper (#11629) and 1min.ai (#11631) boarded the catalog — closes the `check:docs-counts-sync` strict drifts that kept `release/v3.8.51` red ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11673](https://github.com/diegosouzapw/OmniRoute/pull/11673)) — thanks @jonlwheat2-gif +- **test(providers):** update count-derived assertions after the v3.8.51 provider additions — `APIKEY_PROVIDERS` 233 → 235 (Opper #11629 + 1min.ai #11631), reserved-prefix REGISTRY walk 395 → 398, `WEB_FETCH_PROVIDERS` now includes `nimble-search` (#11620), and the provider translate-path golden snapshot regenerated ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11674](https://github.com/diegosouzapw/OmniRoute/pull/11674)) — thanks @jonlwheat2-gif +- **test(sse):** bump the hard-lease connection-query inventory for `src/lib/tokenHealthCheck.ts` to 2 — the verify-only web-cookie sweep added by #11495 split the single `getProviderConnections` call into oauth + cookie variants, which the frozen inventory had not tracked ([#11495](https://github.com/diegosouzapw/OmniRoute/pull/11495)). ([#11677](https://github.com/diegosouzapw/OmniRoute/pull/11677)) — thanks @jonlwheat2-gif / @oyi77 +- **chore(release):** clear the `release/v3.8.51` base-red gates — docs migration count (+38 i18n mirrors), stryker `tap.testFiles`, lockfile registry host, stale ESLint suppressions, the 7 lint regressions they were hiding, the stale `APIKEY_PROVIDERS` partition count (231 → 233), the `glm-5.3-max` tier missing from the GLM test table, a dead `modelType === "chat"` comparison in the models catalog, and the `resolveLiveWsUrl` / `sanitizeLiveWsPort` helpers from #11388 that a later merge dropped from `wsPath.ts` (dashboard live WebSocket URL resolution) ([#11502](https://github.com/diegosouzapw/OmniRoute/pull/11502)) — thanks @MumuTW +- **chore(free-models):** the free-tier regime table now declares which totals figure each regime feeds and whether it may take the no-auth shortcut, so the sets that used to repeat those answers by hand are derived from it — a new `freeType` no longer compiles until it has answered every question, instead of silently contributing to no total ([#11537](https://github.com/diegosouzapw/OmniRoute/pull/11537)) — thanks @maxmad64bis +- **docs(routing):** LKGP strategy copy now states provider-level stickiness and the explicit rules fallback instead of promising per-target pinning ([#11555](https://github.com/diegosouzapw/OmniRoute/pull/11555)) — thanks @maxmad64bis +- **test(chatcore):** pin the response tool-name alias resolution against the ordering hazard that broke every Gemini/Antigravity MCP tool call in v3.8.49. `extractRequestToolIdentityMap` deletes `translatedBody._toolNameMap`, so the later read is always undefined and the ledger survives only through the `requestToolIdentityMap` fallback — removing that fallback previously left the entire tool-name suite green. The resolution moves into `resolveResponseToolNameMap()` next to the map it depends on, with a regression guard that fails without the recovery. No behaviour change. ([#11575](https://github.com/diegosouzapw/OmniRoute/pull/11575)) — thanks @amartinawi +- **docs(env):** document `OMNIROUTE_CORPUS_CACHE_SIZE` in `.env.example` and `ENVIRONMENT.md`, so the env/docs contract check stops failing Docs Gates on every branch ([#11576](https://github.com/diegosouzapw/OmniRoute/pull/11576)) — thanks @ntdatt812 +- **chore(stryker):** register `route-guard-tunnel-processes-local-only` and `universal-quota-aware-routing` in `tap.testFiles`, so their mutant kills count for the three modules they cover ([#11577](https://github.com/diegosouzapw/OmniRoute/pull/11577)) — thanks @ntdatt812 +- **test(authz):** pin `LOCAL_ONLY_API_GET_EXEMPTIONS` by exact membership instead of by entry count, so the guard names the offending path and also catches a substitution ([#11580](https://github.com/diegosouzapw/OmniRoute/pull/11580)) — thanks @ntdatt812 +- **test(build):** pin `resolveNextBuildBundlerFlag` to the `OMNIROUTE_USE_TURBOPACK` contract it implements, across runtimes, instead of asserting an unimplemented Bun override ([#11583](https://github.com/diegosouzapw/OmniRoute/pull/11583)) — thanks @ntdatt812 +- **test(claude-web):** the Claude Web transport test's barrel anchors no longer pin the exact construction spelling. #11421 made the executor map lazy, so `/"claude-web": new ClaudeWebExecutor\(\)/` stopped matching and the test went red on `release/v3.8.51` itself — a base-red gate every open PR inherited. The anchors now match the alias key and the class name and tolerate either spelling in between, and were checked to still fail when an alias is genuinely repointed at another executor ([#11585](https://github.com/diegosouzapw/OmniRoute/pull/11585)) — thanks @ntdat812 +- **test(models):** pin `qwen3.8-max` resolution per provider — two of the four catalogs now serve the bare id and no longer carry `-preview` ([#11590](https://github.com/diegosouzapw/OmniRoute/pull/11590)) — thanks @ntdatt812 +- **test(executors):** drop the stale 128-tool truncation assertion that contradicted the #11444 pass-through contract ([#11591](https://github.com/diegosouzapw/OmniRoute/pull/11591)) — thanks @ntdatt812 +- **test(token-health):** pin the current terminal-skip boundary — expired with retry budget is probed, `account_deactivated` stays skipped ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)) — thanks @ntdatt812 +- **test(models):** follow #11503's retarget of the `gemini-3-pro-high` alias to the hyphenated catalog id ([#11593](https://github.com/diegosouzapw/OmniRoute/pull/11593)) — thanks @ntdatt812 +- **refactor(video bridge):** extract per-video acquisition, whole-result caching, description, and metrics/abort/cleanup out of `VideoBridgeGuardrail.preCall` into a `processVideoPart` seam in a new `videoBridgePipeline.ts`, behind explicit `VideoMediaBrokerPort`, `VideoAudioTranscriptionPort`, and `VideoDrilldownPort` boundaries; `preCall` now only handles request traversal, policy, and response aggregation. The Video tab's FFmpeg/ffprobe runtime status is now an explicit `unknown` / `restricted` / `unavailable` / `available` state instead of a nullable boolean pair, fixing a case where an in-flight or failed probe was mislabeled as "install FFmpeg" ([#11657](https://github.com/diegosouzapw/OmniRoute/issues/11657)). ([#12016](https://github.com/diegosouzapw/OmniRoute/pull/12016)) +- **test(opencode-plugin):** add unit test coverage for `formatFreeBudget()` naming helper ([#11660](https://github.com/diegosouzapw/OmniRoute/pull/11660)) — thanks @f9td56dbgh-hub +- **docs(video):** clarify that the Video Bridge transcript `source` field (`client`, `embedded`, `audio-bridge`) is presently caller-declared and not yet server-verified — OmniRoute enforces the enum shape but does not cryptographically confirm that an `embedded`/`audio-bridge` label came from a server-owned extraction ([#11661](https://github.com/diegosouzapw/OmniRoute/issues/11661)). ([#12001](https://github.com/diegosouzapw/OmniRoute/pull/12001)) +- **chore(providers):** retire the distributed Raycast Relay and Hailuo Web integrations after a provenance audit confirmed substantial derivation from GPL-3.0 sources; the official GitHub and MiniMax API providers remain available. Migration 163 disables legacy connections and invalidates their active leases while preserving connection IDs, audit/history rows, quota snapshots, usage records, and fail-closed API-key connection policies. ([#11691](https://github.com/diegosouzapw/OmniRoute/pull/11691)) +- **chore(providers):** retire the Felo Web provider while its GPL-derived provenance remains on licensing HOLD ([#11698](https://github.com/diegosouzapw/OmniRoute/pull/11698)) — stale runtime and database references now fail closed while existing connection, lease, and usage history remains preserved for auditability. +- **fix(sse):** retire the Gemini Web image-generation bridge while provenance and licensing remain on HOLD, preserving Gemini Web chat/auth/tools and legitimate Gemini/Nano Banana image providers ([#11708](https://github.com/diegosouzapw/OmniRoute/pull/11708)) +- **chore(audio):** retire the built-in EdgeTTS provider and its 11-voice catalog while provenance/licensing review remains on HOLD; the generic `/v1/audio/speech` endpoint and the other speech providers remain available ([#11711](https://github.com/diegosouzapw/OmniRoute/pull/11711)) +- **fix(providers):** retire the unofficial Qwen Web integration and its `qwen-web`/`qw` identities while third-party provenance remains on HOLD ([#11713](https://github.com/diegosouzapw/OmniRoute/pull/11713)) — direct requests now fail closed with a sanitized HTTP 410 and combos skip the retired target, while migration guards preserve connection IDs, allowlists, usage/audit history and invalidate active leases. Supported Qwen API, Qwen Code, cloud, token-plan, audio, local and model-name surfaces remain available; restoring Qwen Web requires a qualified provenance/license decision. +- **fix(providers):** retire `microsoft-designer-web` and its `msdesigner` alias from runtime and catalogs while preserving stored credentials, provider history and allowlists as inactive records — source dependency on gpt4free's [`MicrosoftDesigner.py`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/g4f/Provider/needs_auth/MicrosoftDesigner.py) is proven at snapshot `0943d16eef757a44deb60651c07bbe5fa958cafe`, whose [`LICENSE`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/LICENSE) is GPL-3.0; expressive adaptation is probable, which records provenance/licensing risk without asserting a legal conclusion ([#11720](https://github.com/diegosouzapw/OmniRoute/pull/11720)). +- **docs(legal):** centralize pinned MIT notices for the vendored GCF codec and local flag assets ([#11726](https://github.com/diegosouzapw/OmniRoute/pull/11726)) +- Split the 2,952-line `adobeFireflyClient.ts` into nine focused modules (catalog, credentials, payload, ARP session, upload, responses, IMS, account, poll) behind an unchanged barrel — no consumer edits and no public API change (#11728). — thanks @MumuTW +- **fix(dashboard):** replaced the Model Lockout card's unprovenanced notification MP3 with a short, locally synthesized Web Audio chime ([#11731](https://github.com/diegosouzapw/OmniRoute/pull/11731)) — both toggles retain optional audible feedback without shipping replacement media or third-party code, and unsupported, suspended, or rejected audio contexts fail open without blocking the setting change. +- **chore(deepseek-web):** remove the provenance-unresolved DeepSeek PoW WASM binary and its runtime loader/tracing while retaining the existing JavaScript solver (slower at high difficulty) ([#11732](https://github.com/diegosouzapw/OmniRoute/pull/11732)). +- **chore(assets):** remove four unused provider assets whose redistribution provenance could not be verified while retaining the active Freebuff SVG ([#11735](https://github.com/diegosouzapw/OmniRoute/pull/11735)) +- **docs(assets):** retired two raster images whose redistribution provenance could not be verified ([#11736](https://github.com/diegosouzapw/OmniRoute/pull/11736)) — PicoClaw documentation now reuses the existing SVG, while Zoo Code remains a text link without an image. +- **docs(legal):** add pinned notices for six LobeHub-derived provider SVGs and 65 byte-exact theSVG assets, retaining registry claims as unverified metadata with explicit trademark and HOLD boundaries ([#11737](https://github.com/diegosouzapw/OmniRoute/pull/11737)). +- **chore(quality):** added a versioned 225/225 provider-asset provenance manifest and blocking drift gate ([#11743](https://github.com/diegosouzapw/OmniRoute/pull/11743)) — the inventory pins magic MIME and SHA-256 for every physical file, records immutable source matches for 71 assets, keeps 69 probable and 85 unresolved entries explicit but non-blocking, and treats provenance as source evidence rather than copyright or trademark clearance. +- **refactor(mitm):** replace four previously derived Traffic Inspector utilities — conversation normalization, SSE merging, secret masking, and header sanitization — with independent clean-room implementations based on public provider schemas, WHATWG SSE, and RFC 6750, while keeping the two independently classified passthrough surfaces unchanged ([#11748](https://github.com/diegosouzapw/OmniRoute/pull/11748)). +- Added a unit test that fails seven days before any dated pack under `config/` (`validUntil` and sibling keys) lapses, naming the file and key. The Alibaba free-tier pack expired on 2026-08-27 and turned every PR red the next morning with no commit involved; renewal now happens on someone's terms, not the clock's. ([#11891](https://github.com/diegosouzapw/OmniRoute/pull/11891)) +- `check:workflows` now fails (under `--strict`/`--ratchet`) when any job routed to a self-hosted runner publishes with `--provenance` — npm rejects that with `422` at the registry, which in v3.8.50 only surfaced after the tag and Docker images were public. ([#11895](https://github.com/diegosouzapw/OmniRoute/pull/11895)) +- `scripts/ops/runner-janitor.sh` now proves a path is idle with one `lsof` snapshot and removes stale leftovers itself (tmpfs after 3 h — it is RAM — disk after 24 h), kills orphan `next-build` processes, prunes checkouts of stopped runners, and alerts on memory pressure; `--dry-run` shows exactly what it would do. `docs/ops/RUNNER_BOX.md` reconciled to the measured box (31 GB, 10 listeners). ([#11893](https://github.com/diegosouzapw/OmniRoute/pull/11893)) +- The `next-build` artefact (1.3 GB) is now written and read under `$RUNNER_TEMP` (per-runner, on disk) instead of `/tmp`, which on the self-hosted pool is a 12 GB tmpfs in RAM. Landing it there took 27–32 of the publish job's 76 minutes, and the fixed `/tmp/e2e-build.tar.gz` name let E2E jobs on different runners overwrite each other's download. ([#11896](https://github.com/diegosouzapw/OmniRoute/pull/11896)) +- The CI `build` job now runs in two concurrency lanes — `main` and pull requests — so a release build is never queued behind (or OOM-killed beside) PR builds on the self-hosted pool, which holds one `next-build` comfortably and two at the edge. ([#11901](https://github.com/diegosouzapw/OmniRoute/pull/11901)) +- Re-freeze the ESLint suppressions on `release/v3.8.51` from a clean-room measurement (2 stale file entries pruned, 55 pre-existing `no-explicit-any` in six new files frozen under #11924) and drop the dead `GPT_SIZE_MAP` constant orphaned by the Adobe Firefly client split, so `No new ESLint warnings` stops failing every PR with exit 2 (Refs #11924) ([#11955](https://github.com/diegosouzapw/OmniRoute/pull/11955)) +- Type the 55 `no-explicit-any` sites that had been frozen under #11924 — four redundant casts in `socksConnectorWithFamily.ts` (undici/socks types already accept them) and the mocks/fixtures of the socks-timeout and isFree suites — and drop their suppression entries; the ESLint ratchet shrinks from 5487 to 5432 (Closes #11924) ([#11975](https://github.com/diegosouzapw/OmniRoute/pull/11975)) +- Take the two hosted-runner builds off the PR rail: `Build App` (`build.yml`) is `workflow_dispatch`-only and `DAST smoke (PR)` runs only for PRs into `main` — the 7 GB hosted VM cannot build `release/v3.8.51` in any profile (19/30 red, VM shutdown ~8 min into `next build`) and both checks had turned into permanent noise on every release PR; the bundle stays validated by `ci.yml` on `main` and by `nightly-release-green` on `release/**` (Closes #11946) ([#11962](https://github.com/diegosouzapw/OmniRoute/pull/11962)) +- Move the four nightly jobs that build the backend (`nightly-schemathesis`, `nightly-llm-security` promptfoo + garak, `nightly-resilience` axe-a11y) off the hosted 7 GB runner — where they died on `release/v3.8.51` unseen — onto the box's new `omni-light` pool (two listeners), and document the reshaped fleet (4 active OmniRoute listeners: 2 `omni-build` + 2 `omni-light`, janitor ceiling 4) (Closes #11965) ([#11967](https://github.com/diegosouzapw/OmniRoute/pull/11967)) +- Stop painting every fork PR into `release/**` red: `quality.yml` `Build (advisory)` is skipped (GitHub still reports `continue-on-error` failures as check FAILURE). Hosted `ubuntu-latest` cannot finish `npm run build` on this tree — same class as #11962 taking `build.yml` off the PR rail. `docker-publish.yml` amd64 now runs on the `.113` `omni-build` pool (31 GB / 32 cores, two listeners) with Turbopack, shares the `heavy-build-main` lane with `ci.yml` `Build` so it queues instead of becoming a third heavy, and keeps per-ref concurrency (a merge storm was starting 8 concurrent OOM builds). arm64 stays on `ubuntu-24.04-arm` with webpack — there is no ARM box. Fallback when `USE_VPS_RUNNER` is off: hosted amd64 + webpack (#11976). ([#12021](https://github.com/diegosouzapw/OmniRoute/pull/12021)) +- `docker-publish.yml` builds with webpack (`OMNIROUTE_USE_TURBOPACK=0`) on **both** arches, including amd64 on the `.113` `omni-build` pool. Turbopack had the RAM (31 GB, `omniroute-113-6`) and still panicked (`TurbopackInternalError: there must be a path to a root`, run 33253576569, 26 min in). The same tree's arm64 webpack build on hosted `ubuntu-24.04-arm` succeeded (run 33264823398). Dockerfile already documents webpack as the Docker escape hatch (#11976). ([#12050](https://github.com/diegosouzapw/OmniRoute/pull/12050)) +- **docs(free-tier):** declare the counting vs deciding regimes for "is it free?" and guard the deciding path from DB-backed catalog resolution ([#12226](https://github.com/diegosouzapw/OmniRoute/pull/12226)) — thanks @maxmad64bis +- **docs(auto-combo):** The mode pack table in `docs/routing/AUTO-COMBO.md` now lists all six shipped packs with every weight each one sets, replacing a four-pack table whose numbers had also drifted from the source. It states plainly that no pack sets `quality`, so selecting any pack silences the observed-quality signal. Six more documents that quote the scoring factor count joined the `check:docs-counts` gate, which caught five stale claims — including one naming nine factors that do not exist — and two stale mode pack counts. The dashboard routing panel, which offered four of the six packs and labelled the default strategy "6-Factor Scoring", is now covered by a test; the two packs it was missing are `reliability-first` and `chaos-mode`, the latter labelled as the fault-injection profile it is rather than as one more routing preference ([#12316](https://github.com/diegosouzapw/OmniRoute/pull/12316)) — thanks @maxmad64bis +- **chore(quality):** rebaseline `src/lib/db/apiKeys.ts` for the ACL the key-creation path now preserves ([#12352](https://github.com/diegosouzapw/OmniRoute/pull/12352)) ([#12673](https://github.com/diegosouzapw/OmniRoute/pull/12673)) — thanks @KrzysiekSko +- **chore(providers):** bump the Claude Code wire identity and the Devin bridge image pin from `2.1.220` to `2.1.258` ([#12402](https://github.com/diegosouzapw/OmniRoute/pull/12402)) — thanks @ggiak ([#12604](https://github.com/diegosouzapw/OmniRoute/pull/12604)) +- **docs(env):** align `.env.example`, the README Bun section, and the troubleshooting guide with the code: `OMNIROUTE_USE_TURBOPACK` also governs `npm run build` (not dev-only), `bun run build` follows that flag instead of auto-selecting Webpack, `OMNIROUTE_CHAT_MAX_HEAVY_IN_FLIGHT` is unset by default (no request-count cap), and the structural `503 chat_admission_busy` message matches `chatAdmissionResponses.ts` (#12404 — thanks @pacocartones) +- **chore(electron):** upgrade the desktop app to Electron 44 (Chromium 152, Node 24.18.1) ([#12217](https://github.com/diegosouzapw/OmniRoute/pull/12217)). **Requires macOS 13 (Ventura) or later** — Chromium dropped macOS 12 (Monterey), so Monterey users must stay on an earlier OmniRoute desktop build. Windows and Linux are unaffected; the app already shipped only x64/arm64, so Electron 44 dropping 32-bit builds changes nothing. Removes the `openAsHidden`/`wasOpenedAsHidden` login-item fields deleted in Electron 44 — hidden autostart continues to work through the `--hidden` argument registered with the login item ([#12554](https://github.com/diegosouzapw/OmniRoute/pull/12554)) — thanks @app/dependabot +- **chore(quality):** rebaseline `src/sse/handlers/chat.ts` for the effective-input persistence the continuation fix needs ([#12641](https://github.com/diegosouzapw/OmniRoute/pull/12641)) ([#12680](https://github.com/diegosouzapw/OmniRoute/pull/12680)) — thanks @hartmark +- `Coverage` job on `ci.yml`: the informational Codecov upload gets its own 5-minute ceiling and `continue-on-error`, and the job budget grows from 20 to 30 minutes (the 8-shard c8 merge alone takes ~10) — a stalled upload no longer ends the job `cancelled` and drags a fully green `main` run's conclusion down with it ([#11972](https://github.com/diegosouzapw/OmniRoute/pull/11972)) +- CI hardening on the PR rail: the four `Unit Tests fast-path` shards get `timeout-minutes: 30` (a hung shard held a PR for 64 min instead of GitHub's 6 h default) and both ESLint file caches lose their `restore-keys` fallback, so a cache built under another suppressions file or lint config can no longer report stale verdicts (Refs #11600, #11924) ([#11963](https://github.com/diegosouzapw/OmniRoute/pull/11963)) +- Every CI job that runs a `next build` (`build`, the npm `publish`, both release-green validations) now targets the `omni-build` runner label, which only two of the eight self-hosted runners carry. The box holds one build comfortably and two at the edge; a third now queues on GitHub instead of being OOM-killed by the kernel. ([#11932](https://github.com/diegosouzapw/OmniRoute/pull/11932)) +- **refactor(combo):** move `handleRoundRobinCombo` (and `resolveTargetTokenLimit`) into `open-sse/services/combo/roundRobinCombo.ts`. `combo.ts` drops from 2164 to 1014 lines (`split("\n").length`); the leaf is 1199 (under the 1200 new-file cap). The round-robin call site uses a dynamic `import()` so `releaseStickyPinOnFailure` / `clearStaleLKGP` can stay exported from `combo.ts` without a static cycle. Skip / sticky / semaphore / safety-timer behavior is unchanged. ([#12811](https://github.com/diegosouzapw/OmniRoute/pull/12811)) — thanks @HouMinXi +- **chore(quality):** rebaseline the file-size caps the error-boundary campaign grew past (`open-sse/executors/codex.ts`, `open-sse/vendor/codex-chatgpt-web/bridge.ts`, both via [#12444](https://github.com/diegosouzapw/OmniRoute/pull/12444)) ([#12654](https://github.com/diegosouzapw/OmniRoute/pull/12654)) +- **chore(quality):** rebaseline the file-size caps the hartmark batch grew past (`combos/page.tsx` via [#12355](https://github.com/diegosouzapw/OmniRoute/pull/12355), `open-sse/services/combo.ts` via [#12338](https://github.com/diegosouzapw/OmniRoute/pull/12338)) ([#12623](https://github.com/diegosouzapw/OmniRoute/pull/12623)) — thanks @hartmark +- **chore(quality):** rebaseline the file-size caps the HouMinXi batch grew past when its PRs stacked (`providers/page.tsx`, `chatCore.ts`, `accountFallback.ts`) — each PR measured correctly in isolation, none saw the stacking ([#12619](https://github.com/diegosouzapw/OmniRoute/pull/12619)) +- **chore(quality):** rebaseline `open-sse/services/combo.ts` for the reset-aware scoring the HouMinXi batch stacked ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) ([#12678](https://github.com/diegosouzapw/OmniRoute/pull/12678)) — thanks @HouMinXi +- **docs:** sync provider catalog count (direct commit `4f11b2ae3d`) +- **docs(skills):** sync omni-inference search fallback (direct commit `b4e76a7ed9`) +- **chore(lint):** prune two stale base suppressions (direct commit `1325047d56`) +- **test(auto-combo):** tolerate full-suite rotation load (direct commit `ad3e293f9f`) +- **test:** drain inherited quality snapshot reds (direct commit `21ed68d8ac`) +- **chore(quality):** rebaseline measured CLI bundle growth (direct commit `d35b3f9779`) +- **test(ui):** reproduce invalid AnySearch icon alias (direct commit `12cc6ca834`) +- **test(translator):** fix the relative imports of the relocated deferred-finish test (direct commit `777d9d1629`) +- **docs(counts):** bring the documented provider and migration counts to the measured values (direct commit `e70bea30e9`) +- **test(security):** drop the guard-target set left unused by the sync (direct commit `a32ba0e362`) +- **chore(db):** renumber model_capabilities migration 163 → 169 (cross-branch collision with 163_radar_feed_cache_generated_at) (direct commit `70f5d4cbf9`) +- **docs(cli):** document CLI_PRIME_AGENT_BIN and correct the CLI Agents count (direct commit `9058e39b61`) +- **chore(deps):** refresh runtimes and adopt ESLint 10 ([#11259](https://github.com/diegosouzapw/OmniRoute/pull/11259)) — thanks @backryun +- **refactor(local-corpus):** implement dynamic root resolution and LRU cache ([#11491](https://github.com/diegosouzapw/OmniRoute/pull/11491)) — thanks @raheemuddin786 +- **chore(quality):** prune stale eslint-suppressions entries ([#11596](https://github.com/diegosouzapw/OmniRoute/pull/11596)) +- **chore(ci):** unblock quality gates - env-doc sync + stryker tap.testFiles registration (#9282) ([#11609](https://github.com/diegosouzapw/OmniRoute/pull/11609)) — thanks @Meet6338-X +- **chore(ci):** point the circular-deps gate at dpdm's real JS entrypoint ([#11615](https://github.com/diegosouzapw/OmniRoute/pull/11615)) — thanks @benzntech +- **chore(quality):** rebaseline file-size for #11448/#11495/#11561 growth ([#11630](https://github.com/diegosouzapw/OmniRoute/pull/11630)) +- **deps:** realign onnxruntime-node pin to @huggingface/transformers' 1.24.3 lockstep ([#11633](https://github.com/diegosouzapw/OmniRoute/pull/11633)) — thanks @jonlwheat2-gif +- **test(autoCombo):** port models_dev_tier #11508 guard from node:test to vitest ([#11635](https://github.com/diegosouzapw/OmniRoute/pull/11635)) — thanks @jonlwheat2-gif +- **chore(quality):** rebaseline file-size for #11083/#11631/#11628 growth ([#11636](https://github.com/diegosouzapw/OmniRoute/pull/11636)) +- **chore(lint):** prune stale ESLint suppressions (base-red #11449) ([#11644](https://github.com/diegosouzapw/OmniRoute/pull/11644)) — thanks @jonlwheat2-gif +- **docs(changelog):** aggregate the ten v3.8.50 fragments into CHANGELOG.md ([#11683](https://github.com/diegosouzapw/OmniRoute/pull/11683)) +- **chore(search):** drop unreachable credential sweep after #11642 ([#11687](https://github.com/diegosouzapw/OmniRoute/pull/11687)) — thanks @HouMinXi +- **ci:** add API route TypeScript regression gate ([#11705](https://github.com/diegosouzapw/OmniRoute/pull/11705)) — thanks @Sabeekhann +- **docs(dedup):** correct the CodeQL claim on the tenant-namespace comment ([#11712](https://github.com/diegosouzapw/OmniRoute/pull/11712)) +- **docs(changelog):** add consolidated v3.8.50 stats, top-25 ranking and a .mailmap ([#11715](https://github.com/diegosouzapw/OmniRoute/pull/11715)) +- **docs:** recommend gstack for AI-assisted workflows ([#11770](https://github.com/diegosouzapw/OmniRoute/pull/11770)) — thanks @santosraju99-hub +- **chore(quality):** tighten complexity/cognitive-complexity ratchets to the current tip; land the missed gateways.ts rebaseline ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **docs(i18n):** finish Freepik → Magnific rebrand in locale strings and README ([#11772](https://github.com/diegosouzapw/OmniRoute/pull/11772)) — thanks @turbolego +- **refactor(auto-combo):** fix divergent scoring in combo health reporting ([#11854](https://github.com/diegosouzapw/OmniRoute/pull/11854)) — thanks @jacobsparts +- **chore(changelog):** add missing fragments for #11919/#11918/#11916 ([#11938](https://github.com/diegosouzapw/OmniRoute/pull/11938)) +- **test(check):** escape the runs-on fixture with JSON.stringify, not a quote-only replace ([#11942](https://github.com/diegosouzapw/OmniRoute/pull/11942)) (direct commit `fb7445eaa3`) +- **test(release):** align five suites with the contracts #11933, #11919 and #11876 shipped on release/v3.8.51 ([#11944](https://github.com/diegosouzapw/OmniRoute/pull/11944)) +- **chore(quality):** remove unreachable code and restore test discovery ([#11950](https://github.com/diegosouzapw/OmniRoute/pull/11950)) — thanks @backryun +- **docs(agents):** sync-back landings are fast-forward, never squash ([#11964](https://github.com/diegosouzapw/OmniRoute/pull/11964)) +- **test(infra):** retry recursive temp-dir removal instead of failing a shard on ENOTEMPTY (#11966) ([#11968](https://github.com/diegosouzapw/OmniRoute/pull/11968)) +- **docs(ops):** the .113 heavy-build ceiling is one runner, not two ([#12048](https://github.com/diegosouzapw/OmniRoute/pull/12048)) +- **test(ui):** correct inactive auto-fetch expectation ([#12098](https://github.com/diegosouzapw/OmniRoute/pull/12098)) — thanks @RaviTharuma +- **docs:** document native dependency check escape hatch ([#12101](https://github.com/diegosouzapw/OmniRoute/pull/12101)) — thanks @RaviTharuma +- **test(providers):** regenerate the translate-path golden for OrcaRouter (#11923) ([#12118](https://github.com/diegosouzapw/OmniRoute/pull/12118)) +- **chore(quality):** velocity phase — loosen every numeric baseline by 20% until v4.0, monitor headroom nightly ([#12125](https://github.com/diegosouzapw/OmniRoute/pull/12125)) +- **chore(lint):** batch 0 of #12146 — type the call-log-cap sqlite rows instead of 45 as-any casts ([#12157](https://github.com/diegosouzapw/OmniRoute/pull/12157)) +- **chore(lint):** batch 4 of #12146 — shared/components react-hooks violations resolved ([#12159](https://github.com/diegosouzapw/OmniRoute/pull/12159)) +- **chore(lint):** batch 1 of #12146 — dashboard/cli-code react-hooks violations resolved ([#12160](https://github.com/diegosouzapw/OmniRoute/pull/12160)) +- **chore(lint):** batch 3 of #12146 — dashboard/settings react-hooks violations resolved ([#12162](https://github.com/diegosouzapw/OmniRoute/pull/12162)) +- **chore(lint):** batch 2 of #12146 — resolve the react-hooks compiler violations in dashboard/providers ([#12163](https://github.com/diegosouzapw/OmniRoute/pull/12163)) +- **chore(quality):** register search-432 cooldown test in stryker tap.testFiles ([#12170](https://github.com/diegosouzapw/OmniRoute/pull/12170)) +- **chore(lint):** batch 5 of #12146 — combos, endpoint, provider-stats, api-manager and costs react-hooks violations resolved ([#12174](https://github.com/diegosouzapw/OmniRoute/pull/12174)) +- **build:** add contributor fast profile ([#12192](https://github.com/diegosouzapw/OmniRoute/pull/12192)) — thanks @rafacpti23 +- **docs(readme):** README touch-ups ([#12193](https://github.com/diegosouzapw/OmniRoute/pull/12193)) ([#12194](https://github.com/diegosouzapw/OmniRoute/pull/12194)) ([#12202](https://github.com/diegosouzapw/OmniRoute/pull/12202)) — thanks @rafacpti23 +- **build:** force the webpack bundler in the contributor build profile ([#12197](https://github.com/diegosouzapw/OmniRoute/pull/12197)) — thanks @rafacpti23 +- **build:** skip standalone packaging in the contributor profile ([#12198](https://github.com/diegosouzapw/OmniRoute/pull/12198)) — thanks @rafacpti23 +- **docs(audit):** align every published number with the code and harden check:docs-counts ([#12200](https://github.com/diegosouzapw/OmniRoute/pull/12200)) +- **build:** stub the instrumentation entrypoints in the contributor profile ([#12203](https://github.com/diegosouzapw/OmniRoute/pull/12203)) — thanks @rafacpti23 +- **build:** omit the standalone output target for contributor builds ([#12204](https://github.com/diegosouzapw/OmniRoute/pull/12204)) — thanks @rafacpti23 +- **chore(lint):** batch 6 of #12146 — memory, radar, audit, analytics, cache, usage, activity, home and RequestLoggerV2 react-hooks violations resolved ([#12208](https://github.com/diegosouzapw/OmniRoute/pull/12208)) +- **docs(diagrams):** rename number-carrying diagram files to stable names ([#12210](https://github.com/diegosouzapw/OmniRoute/pull/12210)) +- **docs(dashboard):** redraw onboarding tier-flow SVGs for the real 4-tier model ([#12211](https://github.com/diegosouzapw/OmniRoute/pull/12211)) +- **docs(api):** document every implemented route in openapi.yaml (276 → 692 paths) ([#12212](https://github.com/diegosouzapw/OmniRoute/pull/12212)) +- **chore(lint):** batch 7 of #12146 — final src tail: 37 react-hooks violations across 33 files resolved ([#12244](https://github.com/diegosouzapw/OmniRoute/pull/12244)) +- Revert "docs: recommend gstack for AI-assisted workflows (#11770)" ([#12248](https://github.com/diegosouzapw/OmniRoute/pull/12248)) ([#12249](https://github.com/diegosouzapw/OmniRoute/pull/12249)) +- **docs:** Chaos Mode setup guide + weighted strategy semantics ([#12250](https://github.com/diegosouzapw/OmniRoute/pull/12250)) +- **docs(agents):** protected-surface merge rule — operator approval for agent-instruction files ([#12253](https://github.com/diegosouzapw/OmniRoute/pull/12253)) +- **chore(quality):** register native-codex turn-pin tests in stryker tap.testFiles ([#12263](https://github.com/diegosouzapw/OmniRoute/pull/12263)) +- **chore(quality):** dedupe tap.testFiles entries added by racing base-red fixes ([#12265](https://github.com/diegosouzapw/OmniRoute/pull/12265)) +- **docs(readme):** introduce OmniRouteTray — the macOS menu-bar companion ([#12276](https://github.com/diegosouzapw/OmniRoute/pull/12276)) — thanks @ggiak +- **chore(proxy):** purge the legacy 1proxy residue — sync/rotator modules, dead DB exports, dead settings tab and flag, docs (#12091) ([#12290](https://github.com/diegosouzapw/OmniRoute/pull/12290)) +- **chore(quality):** baseline-headroom skips generated and vendored files in the fileSize worst-file signal ([#12291](https://github.com/diegosouzapw/OmniRoute/pull/12291)) +- **refactor(video):** unify the JPEG frame data-URI contract ([#12322](https://github.com/diegosouzapw/OmniRoute/pull/12322)) +- **chore(deps):** freeze onnxruntime-node and eslint-plugin-react-hooks out of dependabot groups ([#12329](https://github.com/diegosouzapw/OmniRoute/pull/12329)) +- **test(executors):** restore the #10986 reasoning-only fallback guards ([#12364](https://github.com/diegosouzapw/OmniRoute/pull/12364)) — thanks @pacocartones +- **chore(quality):** re-tighten the file-size ratchet to the real LOC (plan 3.8.52 task 0) ([#12411](https://github.com/diegosouzapw/OmniRoute/pull/12411)) +- **test(grok):** format web executor suite ([#12412](https://github.com/diegosouzapw/OmniRoute/pull/12412)) +- **chore(quality):** ratchet open-sse typecheck baseline to zero ([#12418](https://github.com/diegosouzapw/OmniRoute/pull/12418)) +- **test(build):** guard the artifact path policy arrays against duplicates ([#12422](https://github.com/diegosouzapw/OmniRoute/pull/12422)) +- **chore(lint):** adopt eslint-plugin-react-hooks 7.1.1 ([#12428](https://github.com/diegosouzapw/OmniRoute/pull/12428)) +- **chore(providers):** remove a keyless provider integration at its operator's request ([#12440](https://github.com/diegosouzapw/OmniRoute/pull/12440)) +- **docs(providers):** register providers removed at their operator's request and guard against reintroduction ([#12478](https://github.com/diegosouzapw/OmniRoute/pull/12478)) +- **chore(quality):** base fixes — stryker tap.testFiles + node_modules cache key ([#12482](https://github.com/diegosouzapw/OmniRoute/pull/12482)) +- **docs(arch):** one-process recipe for tens of long /v1/responses ([#12493](https://github.com/diegosouzapw/OmniRoute/pull/12493)) — thanks @RaviTharuma +- **chore(quality):** tighten the CodeQL ratchet baseline from 11 to 6 ([#12530](https://github.com/diegosouzapw/OmniRoute/pull/12530)) +- **chore(deps):** pin browserslist override to ^4.28.8 ([#12592](https://github.com/diegosouzapw/OmniRoute/pull/12592)) — thanks @KooshaPari +- **chore(deps):** bump fast-uri to 3.1.7 in the electron lockfile ([#12601](https://github.com/diegosouzapw/OmniRoute/pull/12601)) +- **docs(quality):** document how the CodeQL ratchet refreshes and how to tighten it ([#12611](https://github.com/diegosouzapw/OmniRoute/pull/12611)) +- **test(ci):** pin the openapi-security-tiers two-arm contract with an executing gate test (#12581) ([#12652](https://github.com/diegosouzapw/OmniRoute/pull/12652)) +- **docs(gamification):** align XP Rewards table with code (#12501) ([#12667](https://github.com/diegosouzapw/OmniRoute/pull/12667)) — thanks @KooshaPari +- **refactor(combo):** split executeTarget into gates, attempt, and loop ([#12746](https://github.com/diegosouzapw/OmniRoute/pull/12746)) — thanks @HouMinXi +- **docs(security):** document socket.yml scanner config + CI workflow link (#12575) ([#12764](https://github.com/diegosouzapw/OmniRoute/pull/12764)) — thanks @KooshaPari +- **chore(ci):** guard commit identity in pre-commit to stop author misattribution ([#12772](https://github.com/diegosouzapw/OmniRoute/pull/12772)) +- **deps:** 17 Dependabot bumps — bump electron from 43.4.0 to 43.4.1 in /electron ([#11426](https://github.com/diegosouzapw/OmniRoute/pull/11426)); bump the development group across 1 directory with 8 updates ([#11428](https://github.com/diegosouzapw/OmniRoute/pull/11428)); bump the production group across 1 directory with 11 updates ([#11440](https://github.com/diegosouzapw/OmniRoute/pull/11440)); chore(deps): bump github/codeql-action/init from 4.37.7 to 4.37.8 ([#11925](https://github.com/diegosouzapw/OmniRoute/pull/11925)); chore(deps): bump github/codeql-action from 4.37.7 to 4.37.8 ([#11926](https://github.com/diegosouzapw/OmniRoute/pull/11926)); chore(deps): bump github/codeql-action/analyze from 4.37.7 to 4.37.8 ([#11927](https://github.com/diegosouzapw/OmniRoute/pull/11927)); build(deps): bump github/codeql-action/init from 4.37.8 to 4.37.9 ([#12345](https://github.com/diegosouzapw/OmniRoute/pull/12345)); build(deps): bump github/codeql-action/analyze from 4.37.8 to 4.37.9 ([#12346](https://github.com/diegosouzapw/OmniRoute/pull/12346)); bump the development group across 1 directory with 2 updates ([#12347](https://github.com/diegosouzapw/OmniRoute/pull/12347)); build(deps): bump github/codeql-action from 4.37.8 to 4.37.9 ([#12349](https://github.com/diegosouzapw/OmniRoute/pull/12349)); bump browserslist from 4.28.2 to 4.28.8 ([#12396](https://github.com/diegosouzapw/OmniRoute/pull/12396)); bump the production group across 1 directory with 4 updates ([#12399](https://github.com/diegosouzapw/OmniRoute/pull/12399)); bump @xmldom/xmldom ([#12500](https://github.com/diegosouzapw/OmniRoute/pull/12500)); bump qs from 6.15.2 to 6.16.0 ([#12512](https://github.com/diegosouzapw/OmniRoute/pull/12512)); bump @xmldom/xmldom from 0.9.10 to 0.9.12 ([#12513](https://github.com/diegosouzapw/OmniRoute/pull/12513)); bump fast-uri from 3.1.5 to 3.1.7 ([#12514](https://github.com/diegosouzapw/OmniRoute/pull/12514)); bump @humanfs/node from 0.16.7 to 0.16.8 ([#12515](https://github.com/diegosouzapw/OmniRoute/pull/12515)) +- Clear the `release/v3.8.51` `check:agent-skills-sync` base-red: regenerate `skills/cli-tunnel/SKILL.md` so the `tunnel create [type]` positional that #13009 taught the generator to read is reflected in the committed skill. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` typecheck base-red from #12731: the new `Mode pack ranking gates` candidates in `open-sse/services/autoCombo/__tests__/autoCombo.test.ts` omitted the required `provider`, `model` and `errorRate` fields of `ProviderCandidate`, failing `check:open-sse-typecheck` (and with it `Fast Quality Gates`) on every open PR. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` docs-sync base-red: EURouter (#13025) and GreenPT (#13024) took the live provider count to 358, leaving 7 STRICT drifts (`PROVIDER_REFERENCE.md`, 4 diagrams, 2 tier-flow images) plus the `AGENTS.md` / `llm.txt` / `package.json` count claims stale. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- **test(models):** Pin `CATALOG_BUILD_TIMEOUT_MS` in the #12058 canonical-catalog regression so a cold catalog build racing the 8s default bound no longer flakes the suite with a 500 ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Allow `@omniroute/opencode-plugin-v2/` in the npm pack policy — [#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870) shipped the v2 plugin beside its v1 sibling without widening the allowlist, so every packed file under it read as an unexpected artifact and failed `check:pack-policy` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Point the release-green validator's pack gate at the tree it is validating — the build-provenance guard defaulted to `origin/main`, which a release branch tip can never be an ancestor of mid-cycle, so `Package artifact` reported a false red on every run and the tarball boot-smoke cascaded off it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **docs(checks):** keep doc counts honest — headings, rankings, catalog, weights, quality gate and scoring diagram now covered ([#12507](https://github.com/diegosouzapw/OmniRoute/pull/12507)) — thanks @maxmad64bis +- **chore(lifecycle):** `check:model-lifecycle` now also diffs `DEFAULT_DEGRADATION_MAP` (the background-task redirect table) against the vendor lifecycle snapshot, refusing a retired id as source or target, with a table-driven unit test beside it. Three rows whose source the vendor had retired — `claude-sonnet-4-20250514`, `gemini-3-pro-preview` and `gpt-5.1-codex` (whose target `gpt-5.1-codex-mini` is retired too) — were dead code, since `checkLifecycle` answers 410 before the redirect runs; they are dropped (#12535 — thanks @pacocartones) +- **test(compression):** cover idle worker eviction at the resource level — the pool must call `terminate()` and must not retain the worker's `MessagePort`, complementing the `exit`-event assertion added with the fix ([#13371](https://github.com/diegosouzapw/OmniRoute/pull/13371)) +- **chore(quality):** raise the `imageGeneration.ts` file-size ceiling for the image-only-model guard that clears the #12945 base-red ([#13392](https://github.com/diegosouzapw/OmniRoute/pull/13392)) +- **chore(quality):** type the combo-test route's JSON response bodies instead of casting them to `any`, drop the now-empty suppression entry, and remove the `hasPerModelQuota` import `chatCore.ts` stopped using when the failure-classification helper was extracted ([#13379](https://github.com/diegosouzapw/OmniRoute/pull/13379)) +- **chore(electron):** the desktop bundle no longer ships the root-level files of every translated docs mirror (`README.md`, `llm.txt`, `CLAUDE.md`, `GEMINI.md`, `CONTRIBUTING.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`) — the packaged app only reads `docs/i18n//docs/**`, which stays. Saves ~11 MB on top of the translated CHANGELOGs already pruned. (#0000) ([#13235](https://github.com/diegosouzapw/OmniRoute/pull/13235)) +- **chore(quality):** retighten the `src/sse/handlers/chat.ts` unused-vars suppression count to the 8 violations that actually remain ([#13385](https://github.com/diegosouzapw/OmniRoute/pull/13385)) +- Bring the documented counts back in line with the code: 171 migrations (was 169) and 20 routing strategies (was 19), plus the regenerated `cli-tunnel` skill reference. ([#12970](https://github.com/diegosouzapw/OmniRoute/pull/12970)) +- Update the documented migration count to 172 after the call-logs provider-stats indexes landed. ([#13209](https://github.com/diegosouzapw/OmniRoute/pull/13209)) +- **chore(ci):** two gates that close the blind spots behind the exclusions above. `check:vitest-exclusions` requires every Vitest exclusion to name a tracking issue and to appear in `config/quality/vitest-exclusions.json` — the previous list grew to 62 files behind a comment pointing at an issue that had been closed for a month. `check-new-key-coverage` requires a key newly added to `en.json` to reach every locale; the existing coverage gate is a percentage floor per locale, so eleven absent keys out of ~13,000 left it at 99.9% while a whole feature shipped untranslated in nine languages. Both are diff-aware, so pre-existing debt stays frozen and neither needed a migration to turn on. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **build:** root layout no longer loads Inter from `next/font/google`, so a production image build does not need fonts.googleapis.com ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **docs(i18n):** bring the eight new `llm.txt` mirrors (km, kn, ml, my, ne, or, pa, si) up to the current provider and migration counts, so `check-docs-sync` stops rejecting every commit ([#13674](https://github.com/diegosouzapw/OmniRoute/pull/13674)) +- **refactor(ui):** flow surfaces (home topology, combo live studio, compression cockpit/waterfall/nodes, token-health badge) express state through the theme-aware `--orch-status-*` tokens instead of fixed dark-mode hex, so green/red/amber/grey stay legible in the light theme; dark mode is byte-identical. Categorical palettes (routing-strategy hues, compression-layer pills, provider brand colors) deliberately stay hex ([#12378](https://github.com/diegosouzapw/OmniRoute/issues/12378)) ([#13203](https://github.com/diegosouzapw/OmniRoute/pull/13203)) +- **chore(tests):** 51 test files that had been excluded from Vitest are running again, restoring roughly 350 assertions to the blocking suite. Each was measured individually first: of the 62 files parked behind the `// #8618 — pre-existing failure` comment, 51 pass against the current tree with no source change, so the exclusions had outlived the failures they were added for. The 11 that genuinely still fail stay excluded, but now point at a live tracker (#13204) rather than at #8618, which was closed in August while the list it tracked kept growing. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **chore(quality):** drop two ESLint suppression entries whose violations no longer exist, so `eslint --suppressions-location` stops rejecting every commit that touches the surrounding files ([#13384](https://github.com/diegosouzapw/OmniRoute/pull/13384)) +- **chore(release):** bring main's post-tag fixes into release/v3.8.51 — sync-back step 2/2 (direct commit `21c488f210`) +- **chore(deps):** patch toml and esbuild in the opencode plugin lockfiles ([#12965](https://github.com/diegosouzapw/OmniRoute/pull/12965)) +- **docs(changelog):** reconcile the v3.8.51 living section — fold 366 fragments, cover every cycle commit, credit every contributor ([#12971](https://github.com/diegosouzapw/OmniRoute/pull/12971)) +- **test(plugin):** wait for the v2 lazy-refresh tier instead of sleeping past it ([#12984](https://github.com/diegosouzapw/OmniRoute/pull/12984)) +- **chore(quality):** rebaseline chatCore.ts after the non-streaming regression fixes ([#13045](https://github.com/diegosouzapw/OmniRoute/pull/13045)) +- **docs(changelog):** add the missing bullet marker to the DeepSeek PoW fragment ([#13200](https://github.com/diegosouzapw/OmniRoute/pull/13200)) +- **chore(deps):** drain the Dependabot queue — 10 of 13 alerts ([#13213](https://github.com/diegosouzapw/OmniRoute/pull/13213)) +- **chore(ci):** make worktrees carry the hooks that enforce the identity gate ([#13236](https://github.com/diegosouzapw/OmniRoute/pull/13236)) +- **chore(deps):** pin csv-parse to ^7.0.2 via overrides (#13117) ([#13300](https://github.com/diegosouzapw/OmniRoute/pull/13300)) — thanks @KooshaPari +- **chore(deps):** bump hono from ^4.12.34 to ^4.13.7 (#13148) ([#13301](https://github.com/diegosouzapw/OmniRoute/pull/13301)) — thanks @KooshaPari +- **chore(deps):** pin joi to ^18.2.8 via overrides (#13085) ([#13302](https://github.com/diegosouzapw/OmniRoute/pull/13302)) — thanks @KooshaPari +- **docs(providers):** add Microsoft 365 Copilot (BizChat) provider guide (#12779) ([#13340](https://github.com/diegosouzapw/OmniRoute/pull/13340)) — thanks @KooshaPari +### 🙌 Contributors + +Thanks to everyone whose work landed in v3.8.51: + +| Contributor | PRs / Issues | +| --- | --- | +| [@5dive-bot](https://github.com/5dive-bot) | #11852 | +| [@abhisheksharma2411](https://github.com/abhisheksharma2411) | #11547, #11684, #11959, #12209 | +| [@adevwithpurpose](https://github.com/adevwithpurpose) | #11464 | +| [@adivekar-utexas](https://github.com/adivekar-utexas) | #12015, #12043, #12090, #12138 | +| [@AIB1TAL0S](https://github.com/AIB1TAL0S) | #12103 | +| [@alltomatos](https://github.com/alltomatos) | #11448 | +| [@alvinveroy](https://github.com/alvinveroy) | #11857, #12027, #12165 | +| [@amaleta](https://github.com/amaleta) | #11906, #11934 | +| [@amartinawi](https://github.com/amartinawi) | #11575 | +| [@AndrianBalanescu](https://github.com/AndrianBalanescu) | #11083, #11830, #11834, #11844 | +| [@anhtahaylove](https://github.com/anhtahaylove) | #13091, #13092, #13093, #13096, #13097, #13100, #13106, #13109, #13114, #13155, #13166, #13171, #13175, #13187, #13196 | +| [@aniruddhaadak80](https://github.com/aniruddhaadak80) | #11760 | +| [@app](https://github.com/app) | #12554 | +| [@arminanton](https://github.com/arminanton) | #11461, #11513 | +| [@AStupidBear](https://github.com/AStupidBear) | #11584 | +| [@atescivitci-cmd](https://github.com/atescivitci-cmd) | #12105 | +| [@b3nw](https://github.com/b3nw) | #11970, #11971, #12147 | +| [@backryun](https://github.com/backryun) | #11259, #11950, #12075, #12076, #12078, #12079, #12081, #12082, #12181, #12228, #12239, #12255, #12258, #12277, #12367, #12423, #12524, #12538 | +| [@benjaminkitt](https://github.com/benjaminkitt) | #11747 | +| [@benzntech](https://github.com/benzntech) | #11614, #11615 | +| [@Bl0ck154](https://github.com/Bl0ck154) | #11948, #11951, #11952, #11953, #11954 | +| [@brick30llc-ctrl](https://github.com/brick30llc-ctrl) | #11752, #12242, #12262 | +| [@Chewji9875](https://github.com/Chewji9875) | #11455, #11875, #12028 | +| [@cheynetom](https://github.com/cheynetom) | #12058 | +| [@davidebaraldo](https://github.com/davidebaraldo) | #12222, #12304 | +| [@davidlinfr](https://github.com/davidlinfr) | #6390 | +| [@ddarkr](https://github.com/ddarkr) | #12124 | +| [@Deftera186](https://github.com/Deftera186) | #11809 | +| [@dpozimski](https://github.com/dpozimski) | #11945 | +| [@DW-MediaLab](https://github.com/DW-MediaLab) | #12229 | +| [@echel0nn](https://github.com/echel0nn) | #11923 | +| [@f9td56dbgh-hub](https://github.com/f9td56dbgh-hub) | #11660 | +| [@fabioluissilva](https://github.com/fabioluissilva) | #11991 | +| [@foreveryh](https://github.com/foreveryh) | #12177 | +| [@geek007git](https://github.com/geek007git) | #12115, #12116, #12117, #12120, #12122 | +| [@geekyNads](https://github.com/geekyNads) | #11784, #12266 | +| [@ggiak](https://github.com/ggiak) | #11561, #12276, #12350, #12402 | +| [@gonisulaimann](https://github.com/gonisulaimann) | #12368, #12369, #12371 | +| [@Gorillaz322](https://github.com/Gorillaz322) | #12207 | +| [@groovecityJO](https://github.com/groovecityJO) | #12682 | +| [@hartmark](https://github.com/hartmark) | #11434, #11452, #11473, #11499, #11703, #11983, #11984, #11985, #11986, #11988, #11989, #11990, #11994, #12221, #12293, #12445, #12446, #12447, #12448, #12460, #12461, #12623, #12646, #12650, #12680, #12717, #12718, #12727, #12741, #12854 | +| [@hizzt](https://github.com/hizzt) | #11894 | +| [@hongnoul](https://github.com/hongnoul) | #11484 | +| [@HouMinXi](https://github.com/HouMinXi) | #11411, #11414, #11512, #11518, #11520, #11641, #11642, #11643, #11687, #11779, #11849, #11850, #11851, #11915, #11916, #11918, #11919, #11920, #12013, #12017, #12033, #12042, #12106, #12139, #12166, #12169, #12171, #12205, #12213, #12312, #12325, #12487, #12488, #12495, #12504, #12557, #12566, #12590, #12591, #12624, #12626, #12632, #12637, #12678, #12696, #12697, #12711, #12733, #12746, #12767, #12770, #12789, #12803, #12805, #12811, #12866, #12868, #12899, #12926, #12934, #12950, #12951, #12974, #13001, #13006, #13011, #13017, #13026, #13027, #13034, #13035, #13038, #13042, #13050, #13060, #13061, #13069, #13107, #13120, #13136, #13195, #13197 | +| [@Hsia97](https://github.com/Hsia97) | #11624 | +| [@jacobsparts](https://github.com/jacobsparts) | #11854, #12155, #12167 | +| [@joglomedia](https://github.com/joglomedia) | #11980 | +| [@jonlwheat2-gif](https://github.com/jonlwheat2-gif) | #11443, #11450, #11549, #11567, #11608, #11633, #11635, #11640, #11644, #11671, #11672, #11673, #11674, #11675, #11676, #11677, #11706, #11783, #12051, #12052, #12053, #12055 | +| [@kanade-hoshino](https://github.com/kanade-hoshino) | #12180 | +| [@Karan825](https://github.com/Karan825) | #11861 | +| [@KaspaPulse](https://github.com/KaspaPulse) | #11389, #11469, #11628, #11666, #11910 | +| [@keeltrace](https://github.com/keeltrace) | #12080, #12223 | +| [@killer30001000](https://github.com/killer30001000) | #12178 | +| [@KooshaPari](https://github.com/KooshaPari) | #12470, #12592, #12667, #12699, #12703, #12706, #12764, #12769, #12771, #13300, #13301, #13302, #13340, #13401, #13403, #13404, #13405, #13406, #13407, #13409, #13410, #13411, #13412, #13413, #13414, #13418, #13424, #13427, #13433, #13523, #13524, #13525, #13528, #13531, #13532, #13534, #13539, #13543, #13545, #13546, #13547, #13550, #13553 | +| [@kriptoburak](https://github.com/kriptoburak) | #11370 | +| [@KrzysiekSko](https://github.com/KrzysiekSko) | #12425, #12432, #12673 | +| [@marcelokarval](https://github.com/marcelokarval) | direct commit / report | +| [@maxmad64bis](https://github.com/maxmad64bis) | #1622, #11435, #11437, #11441, #11537, #11550, #11553, #11555, #11557, #11812, #11842, #11843, #11903, #12151, #12214, #12215, #12218, #12226, #12314, #12316, #12317, #12318, #12319, #12320, #12321, #12507, #12715, #12731, #12744, #12786, #12787, #12788, #12790, #12792, #12794, #12795, #12828, #12832, #12853, #12857, #12870, #12937, #12941, #12975, #13141, #13142, #13146, #13147 | +| [@mdigitalbh81](https://github.com/mdigitalbh81) | #12240 | +| [@Meet6338-X](https://github.com/Meet6338-X) | #11598, #11609, #12110 | +| [@morpheus9393](https://github.com/morpheus9393) | #11943 | +| [@MumuTW](https://github.com/MumuTW) | #11492, #11502, #11506, #11507, #11626, #11675, #11685, #11728, #11746, #11888, #11889, #11890, #11892 | +| [@Neuron-Mr-White](https://github.com/Neuron-Mr-White) | #11622, #11801, #12256 | +| [@NightStalker-87](https://github.com/NightStalker-87) | #12183 | +| [@NoxzRCW](https://github.com/NoxzRCW) | #11879, #11880, #11881, #11882, #11883, #11935 | +| [@ntdat812](https://github.com/ntdat812) | #11585, #12095 | +| [@ntdatt812](https://github.com/ntdatt812) | #11368, #11573, #11574, #11576, #11577, #11580, #11582, #11583, #11588, #11589, #11590, #11591, #11592, #11593, #11672, #12177, #12180, #12873, #12918, #12920, #12921, #12925, #12930, #13007, #13009, #13024, #13025, #13083, #13087, #13101, #13104, #13110 | +| [@official-burak](https://github.com/official-burak) | #11542 | +| [@opensource-elearning](https://github.com/opensource-elearning) | #12179, #12189, #12278, #12286 | +| [@oyi77](https://github.com/oyi77) | #11408, #11409, #11421, #11505, #11677, #12036, #12110 | +| [@pacocartones](https://github.com/pacocartones) | #11521, #11522, #11527, #11528, #11529, #11530, #11531, #11532, #11533, #11534, #11595, #11599, #11603, #11604, #11605, #11607, #11610, #11676, #11714, #11716, #11718, #11767, #11838, #11860, #11862, #11869, #11871, #11872, #11873, #11903, #11906, #11921, #11934, #12356, #12358, #12359, #12360, #12361, #12362, #12364, #12365, #12373, #12374, #12375, #12376, #12377, #12379, #12380, #12381, #12386, #12387, #12389, #12390, #12394, #12395, #12397, #12401, #12403, #12404, #12522, #12523, #12535, #12536, #12540, #12541, #12543, #12545, #12548, #12549, #12550, #12551, #12552, #12647, #12651, #12653, #13055 | +| [@patrykkopycinski](https://github.com/patrykkopycinski) | #11936, #11937, #12224 | +| [@PauloFH](https://github.com/PauloFH) | #11509 | +| [@PauloHSOliveira](https://github.com/PauloHSOliveira) | #12241 | +| [@ponkcore](https://github.com/ponkcore) | #12054 | +| [@Prajeeth-12](https://github.com/Prajeeth-12) | #11634, #12046 | +| [@quiterunner-commits](https://github.com/quiterunner-commits) | #12131, #12143 | +| [@rafacpti23](https://github.com/rafacpti23) | #11554, #11558, #12192, #12197, #12198, #12202, #12203, #12204 | +| [@ragnar-claude](https://github.com/ragnar-claude) | #11564 | +| [@raheemuddin786](https://github.com/raheemuddin786) | #11491, #11839, #11840, #11841, #12003, #12230, #12231, #12232, #12233, #12234 | +| [@rahilmavani](https://github.com/rahilmavani) | #11761 | +| [@Rahulsharma0810](https://github.com/Rahulsharma0810) | #11771 | +| [@RaviTharuma](https://github.com/RaviTharuma) | #11710, #11727, #11797, #11798, #11802, #11805, #11806, #11811, #12098, #12099, #12101, #12449, #12452, #12472, #12473, #12493, #12533, #12607, #12628, #12631, #12636, #12875, #12876, #12880, #12882, #12884 | +| [@rezjalibd](https://github.com/rezjalibd) | #12186 | +| [@RhianB14](https://github.com/RhianB14) | #11692 | +| [@rifqiawl](https://github.com/rifqiawl) | #11517, #11519, #11969 | +| [@rqzbeh](https://github.com/rqzbeh) | #11390 | +| [@Sabeekhann](https://github.com/Sabeekhann) | #11699, #11705 | +| [@santosraju99-hub](https://github.com/santosraju99-hub) | #11755, #11770 | +| [@Seramicx](https://github.com/Seramicx) | #11568 | +| [@Siva010](https://github.com/Siva010) | #12191 | +| [@solstxce](https://github.com/solstxce) | #11597 | +| [@soroush5](https://github.com/soroush5) | #12691 | +| [@tenshiak](https://github.com/tenshiak) | #12279 | +| [@TheDemonTuan](https://github.com/TheDemonTuan) | #11468, #11470, #11471, #11482, #11548, #11758, #11775, #11814 | +| [@thomasmaerz](https://github.com/thomasmaerz) | #12834 | +| [@tuandinh0801](https://github.com/tuandinh0801) | #11454 | +| [@turbolego](https://github.com/turbolego) | #11621, #11762, #11772, #11774, #11781, #12216 | +| [@Tushar49](https://github.com/Tushar49) | #11392 | +| [@ujjawalkaushik1110](https://github.com/ujjawalkaushik1110) | #11863 | +| [@vermasomesh835](https://github.com/vermasomesh835) | #11794 | +| [@vsd2807](https://github.com/vsd2807) | #11565, #11619 | +| [@wahidsadik371-coder](https://github.com/wahidsadik371-coder) | #12126 | +| [@watchingdogs](https://github.com/watchingdogs) | #12031 | +| [@wildcard](https://github.com/wildcard) | #11620 | +| [@xiaoyaner0201](https://github.com/xiaoyaner0201) | #11460, #11662, #12083 | +| [@Xxx91n](https://github.com/Xxx91n) | #11690 | +| [@yourspraveen](https://github.com/yourspraveen) | #11146 | +| [@yxyxy](https://github.com/yxyxy) | #11671 | +| [@ZaimMarzuki](https://github.com/ZaimMarzuki) | #11960 | +| [@Zartharas](https://github.com/Zartharas) | #11340 | +| [@zero-executioner](https://github.com/zero-executioner) | #11631 | +| [@diegosouzapw](https://github.com/diegosouzapw) | maintainer | + +--- ## [3.8.50] — 2026-08-25 _Living section — regenerated 2026-08-12 from all cycle commits (cycle open `ed2db6cb19` → tip). Bullets carry the merged PR and its author; direct pushes listed separately._ diff --git a/docs/i18n/ta/CHANGELOG.md b/docs/i18n/ta/CHANGELOG.md index 12cff649ae..0913499aa0 100644 --- a/docs/i18n/ta/CHANGELOG.md +++ b/docs/i18n/ta/CHANGELOG.md @@ -8,16 +8,1119 @@ ## [3.8.51] — TBD -_Living section — cycle opened at the v3.8.50 freeze (parallel-cycle model). Bullets are aggregated from `changelog.d/` fragments at each `/generate-release` phase._ +_Living section — reconciled 2026-09-15 from all cycle commits (`091589089c` → `c0f92ec98a`, 916 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 | **123** | +| 📝 Commits in the cycle | **916** | +| 🔀 Pull requests referenced | **892** | +| 📋 Changelog entries | **919** | +| 🙌 Contributors credited in entries | **121** | +| 🤖 Automated dependency commits | 18 | + +**Entries by type** + +| Type | Count | +| --- | ---: | +| 🐛 Fixes | 592 | +| ✨ Features | 131 | +| 🧹 Chore | 93 | +| 📚 Docs | 42 | +| 🧪 Tests | 32 | +| ♻️ Refactor | 8 | +| 🏗️ Build | 7 | +| ⚡ Performance | 7 | +| ⚙️ CI | 2 | +| ⏪ Reverts | 2 | +| 📦 Dependencies | 2 | +| 🔒 Security | 1 | + +### 🏆 Top 25 contributors this cycle + +_By commits in `091589089c..c0f92ec98a`, author identities consolidated via `.mailmap` and the merged PR's GitHub login. Bots excluded._ + +| # | Contributor | Commits | +| ---: | --- | ---: | +| 🥇 | diegosouzapw | 323 | +| 🥈 | Bob.Hou (@HouMinXi) | 79 | +| 🥉 | Paco Cartones (@pacocartones) | 77 | +| 4 | Dizzle (@maxmad64bis) | 49 | +| 5 | Koosha Paridehpour (@KooshaPari) | 43 | +| 6 | Nguyen Thanh Dat (@ntdatt812) | 31 | +| 7 | Ravi Tharuma (@RaviTharuma) | 26 | +| 8 | Markus Hartung (@hartmark) | 22 | +| 9 | Webman (@jonlwheat2-gif) | 22 | +| 10 | backryun | 17 | +| 11 | anhtahaylove | 15 | +| 12 | Rafa Martins (@rafacpti23) | 10 | +| 13 | Syed Raheemuddin (@raheemuddin786) | 10 | +| 14 | Nguyễn Viết Tuấn (@TheDemonTuan) | 8 | +| 15 | MumuTW | 7 | +| 16 | Paijo (@oyi77) | 7 | +| 17 | Tobias Andersen (@turbolego) | 6 | +| 18 | Bl0ck (@Bl0ck154) | 5 | +| 19 | SHANMUGAPRIYAN (@geek007git) | 5 | +| 20 | KaspaPulse | 5 | +| 21 | Abhishek Sharma (@abhisheksharma2411) | 4 | +| 22 | Abhishek Divekar (@adivekar-utexas) | 4 | +| 23 | Andrew B. (@AndrianBalanescu) | 4 | +| 24 | NoxzRCW | 4 | +| 25 | opensource-elearning | 4 | ### ✨ New Features +- **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). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- Added Google AI Studio Gemini batch text-to-speech support through `POST /v1/audio/speech`. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- 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. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(routing):** subscription-first auto groupings — `auto/subscription` routes only through plan-included connections with a documented hard-stop overage and fails closed on exhaustion, while `auto/thrifty` orders the pool `subscription → keyless → free → cheap → premium` and steps up one rung at a time as each is exhausted. Billing class comes from a curated per-connection catalog (uncurated is treated as metered, never plan-included), both reuse STRICT_ZERO_COST's per-connection verification, and a quota reading whose `resetAt` has passed is now refreshed regardless of TTL so routing returns to plan capacity as soon as it resets ([#11146](https://github.com/diegosouzapw/OmniRoute/pull/11146)) — thanks @yourspraveen +- **feat(providers):** publish a management-authenticated versioned web-session credential contract from OmniRoute's canonical browser credential metadata ([#11340](https://github.com/diegosouzapw/OmniRoute/pull/11340)) — thanks @Zartharas +- **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)) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add Xquik X search with typed results, credential validation, REST routing, and MCP selection ([#11370](https://github.com/diegosouzapw/OmniRoute/pull/11370)) — thanks @kriptoburak +- **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)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(dashboard):** surface durable exclusive managed leases in the existing Sessions view, keeping leased clients visible across idle gaps while marking connections with in-flight work as active ([#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389)) — thanks @KaspaPulse +- **build(bun):** allow Turbopack bundler flag on Bun 1.4+ with configurable Webpack fallback ([#11471](https://github.com/diegosouzapw/OmniRoute/pull/11471)) — thanks @TheDemonTuan +- feat(api): add an opt-in `modelVisibilityAllowlist`/`modelVisibilityDenylist` settings pair to curate exactly which models `/v1/models` advertises, mirrored into every `auto/*` combo candidate pool so a denied model cannot be routed to via combo selection either (#11481) ([#11997](https://github.com/diegosouzapw/OmniRoute/pull/11997)) +- **feat(rankings):** the Free Provider Rankings page shows what each provider actually served over the last 24 h. It ranked by ELO alone, which left a provider that answers every call with an error in first place; the usage data was already served by the API but never requested. A provider with too small a sample shows a dash, not a number ([#11546](https://github.com/diegosouzapw/OmniRoute/pull/11546)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **feat(guardrails):** enforce a bounded, deterministic contract for Video Bridge transcripts — 256 cues, 4096 input code units and 4 KiB UTF-8 per cue, 64 KiB total text, malformed-Unicode rejection, focus-window scoping, cross-source reconciliation with contributing-source metadata, and a structural provenance trust boundary so caller JSON can never self-assert `embedded`/`audio-bridge` provenance ([#11652](https://github.com/diegosouzapw/OmniRoute/issues/11652)) ([#12009](https://github.com/diegosouzapw/OmniRoute/pull/12009)) +- **feat(video):** orchestrate optional Video Bridge audio extraction and Audio Bridge STT behind a dual opt-in (operator setting AND per-request signal) — a new loopback-only broker `mode=audio` operation shares the frame path's exact process queue, deadline, AbortSignal, and byte budgets to extract a bounded mono 16 kHz PCM WAV from the same already-downloaded video, then reuses the existing Audio Bridge transcription boundary; provider segment timing is preserved when available and marked coarse otherwise, and every failure degrades to a visual-only-safe partial instead of throwing (#11654). ([#12012](https://github.com/diegosouzapw/OmniRoute/pull/12012)) +- Add a tenant-bound Video Bridge drill-down lifecycle on top of the existing secure cache substrate: opaque hashed handles (never raw session/video identifiers), preview/standard/detail multiresolution variants resampled on read, response pagination capped at 8 frames and 32 MiB, and a new authenticated `/api/v1/video-bridge/drilldown` consumer route that stays disabled for remote access by default and denies cross-key access with the same response as a nonexistent handle (no existence oracle). ([#12006](https://github.com/diegosouzapw/OmniRoute/pull/12006)) +- **test(video):** Add the Video Bridge FU-07/FU-09 promotion-evidence harness (#11656) — a frozen Zod manifest schema covering the 8 required scenario kinds (static scenes, rapid cuts, late facts, fades, blur, small text, close events, visual prompt injection) with a minimum of 3 repetitions per case, deterministic declarative fixture recipes (`videoBridgePromotionFixtures.ts`), a pure medians/p95 metrics aggregator, a pure FU-07/FU-09 promotion-verdict evaluator applying the ticket's exact thresholds (missing token usage always holds), a digest-only persistence layer that never retains raw media or raw model responses, and a versioned per-model promotion allowlist shipped empty with every model defaulting to `hold`. The FU-07/FU-09 promotion verdicts themselves remain HOLD — they require a real evidence run against real models on VPS 192.168.0.15. ([#12008](https://github.com/diegosouzapw/OmniRoute/pull/12008)) +- **feat(video bridge):** "embedded" transcript provenance can now be legitimately earned instead of merely asserted — a bounded, allowlisted (`mov_text`/`subrip`/`webvtt`) subtitle probe runs through the loopback-only Video Bridge broker (at most 2 streams, 10s subdeadline bounded by the request deadline, 256 KiB output, 4096-code-unit lines), normalized through a bounded, ReDoS-safe WebVTT parser and Zod-validated end to end. The adapter always resolves to an explicit `success`/`absent`/`transient_failure` outcome — a subtitle failure never breaks the visual description path, and only a fingerprint-verified broker response (never a caller-declared label) can produce embedded cues (#11659). ([#12011](https://github.com/diegosouzapw/OmniRoute/pull/12011)) +- Default new Antigravity-family connections (agy CLI imports and Antigravity OAuth connects) to model auto-sync, so live model discovery lands in the synced catalog and `/v1/models` picks up freshly released upstream models (e.g. Gemini 3.7 Flash tiers) without code changes. Existing connections keep their current setting; the per-connection dashboard toggle remains the opt-out. (#11685 — thanks @MumuTW) ([#11887](https://github.com/diegosouzapw/OmniRoute/pull/11887)) +- **feat(ui):** enable React Compiler (`reactCompiler: true` + `babel-plugin-react-compiler`) for automatic memoization at build time ([#11783](https://github.com/diegosouzapw/OmniRoute/pull/11783)) — thanks @jonlwheat2-gif +- **feat(providers):** add SeekAi (`seekai.cc`) as an OpenAI-compatible New-API gateway — catalog id `seekai` (alias `ska`), `https://seekai.cc/v1`, live `/v1/models` via `passthroughModels`, aggregator-list membership so New-API balance detection can opt in. No referral/aff codes. ([#11786](https://github.com/diegosouzapw/OmniRoute/issues/11786)) ([#12557](https://github.com/diegosouzapw/OmniRoute/pull/12557)) — thanks @HouMinXi +- **feat(zai):** add GLM-5.3-Flash Coding Plan support (1M context, 128K output, vision, `low|high|max` reasoning) and route `zai` GLM-5.3-family API-key traffic through the OpenAI-compatible Coding Plan endpoint with native thinking defaults ([#11801](https://github.com/diegosouzapw/OmniRoute/pull/11801)) — thanks @Neuron-Mr-White +- **feat(combo):** choose how combo models are ordered — manual, provider, score, or name — via a sort control in the dashboard builder, persisted in `config.modelSort` and re-applied on load and after add ([#11812](https://github.com/diegosouzapw/OmniRoute/pull/11812)) — thanks @maxmad64bis +- **feat(free):** custom models can be marked free-tier via `customModels[].isFree`; `isFreeModel()` is the first door and `hidePaidModels` respects it even for providers outside the free budget ([#11843](https://github.com/diegosouzapw/OmniRoute/pull/11843)) — thanks @maxmad64bis +- **feat(nodejs):** add `5dive` as a `configure` target — `omniroute configure 5dive` / `omniroute setup-5dive` write a 5dive auth profile that points an agent fleet's `claude` seats at OmniRoute, with the root-only write, the loopback-vs-`https` endpoint rule and the per-seat model pin handled explicitly ([#11852](https://github.com/diegosouzapw/OmniRoute/pull/11852)) — thanks @5dive-bot +- **feat(sse):** treat `max` as a first-class reasoning-effort tier and clamp per model family (GLM 5.1+/DeepSeek V4+/Kimi K3+ keep native `max`; o1/MiniMax/Grok/Muse Spark clamp to their upstream ceiling) ([#11875](https://github.com/diegosouzapw/OmniRoute/pull/11875)) — thanks @Chewji9875 +- **feat(providers):** the provider plugin manifest now advertises a `usage-fetch` capability for the 40 providers that have a wired usage/quota fetcher, so external dashboards can read it from `GET /api/v1/provider-plugin-manifest` instead of parsing `open-sse/services/usage.ts` after every release. Discovery only — no new fetcher, no quota change, and the Dashboard quota widget stays gated by `USAGE_SUPPORTED_PROVIDERS`. `USAGE_FETCHER_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/fetcherProviders.ts`) and is re-exported from `services/usage.ts`, keeping the manifest module a light leaf instead of pulling the ~490-module usage dispatcher into the manifest route. ([#11903](https://github.com/diegosouzapw/OmniRoute/pull/11903)) — thanks @maxmad64bis / @pacocartones +- **feat(plugins):** `OMNIROUTE_PLUGINS_DIR` sets the directory the runtime plugin scanner reads — and the root the plugin manager installs into — overriding the `HOME`-derived default, so a Docker/K8s deployment can point straight at its bind-mounted plugin tree instead of moving `HOME` just to relocate the scan path. An image that exports no home no longer scans `/tmp/.omniroute/plugins` in silence: the resolved directory is logged once at startup as `scanner.dir_resolved`, naming the input that won. Unset, behaviour is unchanged. Distinct from the CLI-only `OMNIROUTE_PLUGIN_PATH`, which finds `omniroute-cmd-*` command packages and never reached this scanner ([#11906](https://github.com/diegosouzapw/OmniRoute/pull/11906)) — thanks @amaleta / @pacocartones +- **feat(leases):** add an explicit owner-authenticated status action that returns only the active lease's privacy-safe configured connection and provider labels, with generation fencing and no credential or internal-id disclosure ([#11910](https://github.com/diegosouzapw/OmniRoute/pull/11910)) — thanks @KaspaPulse +- **feat(providers):** import-from-file modal shows per-row API errors and ships a downloadable CSV template ([#12071](https://github.com/diegosouzapw/OmniRoute/issues/12071)) ([#12504](https://github.com/diegosouzapw/OmniRoute/pull/12504)) — thanks @HouMinXi +- **feat(providers):** dashboard search matches connection name and `baseUrl` so imported OpenAI-compat nodes surface on the provider card ([#12108](https://github.com/diegosouzapw/OmniRoute/issues/12108)) ([#12495](https://github.com/diegosouzapw/OmniRoute/pull/12495)) — thanks @HouMinXi +- **feat(routing):** add a `score` Auto router strategy that selects the highest configured weighted score and reuses `explorationRate`. ([#12155](https://github.com/diegosouzapw/OmniRoute/pull/12155)) — thanks @jacobsparts +- Add a runtime feature flag to disable universal context handoffs globally without changing the default behavior. ([#12167](https://github.com/diegosouzapw/OmniRoute/pull/12167)) — thanks @jacobsparts +- **feat(providers):** the provider plugin manifest now also advertises a `usage-supported` capability for the 46 providers whose usage API is accepted by the server and Dashboard routes, so integrators can distinguish "the server will serve quota for this provider" from "a fetcher is wired" without reading TypeScript. Discovery only — no fetcher or quota change. `usage-fetch` resolves on id or alias (the usage dispatcher accepts both); `usage-supported` resolves on id alone, matching the runtime guard `USAGE_SUPPORTED_PROVIDERS.includes(providerId)`. `USAGE_SUPPORTED_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/supportedProviders.ts`) and is re-exported from `providers.ts`, mirroring the `fetcherProviders` leaf from #11903 and keeping the manifest a light module. ([#12214](https://github.com/diegosouzapw/OmniRoute/pull/12214)) — thanks @maxmad64bis +- **feat(rankings):** order Free Provider Rankings by what each provider actually served — `GET /api/free-provider-rankings?sortBy=reliability` and a "Most reliable first" toggle on the page. Providers with too few calls to state a success rate keep their score order below the measured ones; the default order is unchanged ([#12218](https://github.com/diegosouzapw/OmniRoute/pull/12218)). — thanks @maxmad64bis +- **perf(sse):** defer `cloneLogPayload()` in the structured SSE collector until after the `maxEvents`/`maxBytes` cap check, eliminating ~9,800 wasted `structuredClone` calls per streaming response (65–71% faster `push()`). Reducer snapshot isolation restored for OpenAI and Responses summaries ([#12241](https://github.com/diegosouzapw/OmniRoute/pull/12241)) — thanks @PauloHSOliveira ([#12243](https://github.com/diegosouzapw/OmniRoute/pull/12243)) +- **feat(usage):** Devin CLI agentic quota (Codeium seat-management GetUserStatus) and OpenRouter key limits plus account credits now surface in Provider Limits ([#12256](https://github.com/diegosouzapw/OmniRoute/pull/12256) — thanks @Neuron-Mr-White) +- **feat(settings):** persist `headroomUrl` through Settings so status/start use the operator URL instead of only `HEADROOM_URL` ([#12306](https://github.com/diegosouzapw/OmniRoute/issues/12306)) ([#12487](https://github.com/diegosouzapw/OmniRoute/pull/12487)) — thanks @HouMinXi +- **feat(auto-combo):** Auto-Combo scoring can now weigh how often a provider/model has actually succeeded. The engine already carried that number on every candidate — 24 hours of usage history behind a ten-sample floor, real-time metrics otherwise — and the scoring function never read it, while the weight table described `stability` as if it did. `reliability` (`1 - failureRate`, with the same field precedence and the same rate-bounding the speed ranking already uses, so a corrupt reading means "nothing observed" rather than "fails every call") is now a declared factor shipping at weight `0`, so routing is unchanged until an operator gives it one, and the `stability` description now matches what that factor computes ([#12317](https://github.com/diegosouzapw/OmniRoute/pull/12317)) — thanks @maxmad64bis +- **feat(routing):** With `freeAccessPolicy: "strict"`, the read-only candidate listing (`GET /v1/auto-combo/{channel}/candidates`) no longer hides the candidates the zero-cost guard excludes — the same read-only transparency the resilience filter already honours (#9133). Each candidate now carries `freeAccessExclusion` saying why it would be kept out, and it tells an exhausted allowance apart from a quota reading that never arrived or went stale, which used to look identical from the outside. Routing is unchanged: the listing reports, it never enforces. The separate `excludeTosAvoid` guard still drops its candidates without a reason; that gap is now documented rather than closed ([#12319](https://github.com/diegosouzapw/OmniRoute/pull/12319)) — thanks @maxmad64bis +- **feat(radar):** The Radar catalog table now shows two facts it was already receiving from the feed and dropping on the floor: the per-model rate limits (requests and tokens, per minute and per day) in a new column, and a badge when a provider's terms state it may train on the prompts you send. A limit of zero renders as zero rather than "rate-only" — for a ceiling those are opposite facts — and a model with no training statement gets no badge, because an absent statement is not a guarantee ([#12320](https://github.com/diegosouzapw/OmniRoute/pull/12320)) — thanks @maxmad64bis +- **feat(radar):** explain Community, single-use, contributor, supporter, recovery, abuse, offers, and privacy rules before either Radar activation action, and remove the superseded fixed-PR grant promise from every UI locale ([#12342](https://github.com/diegosouzapw/OmniRoute/pull/12342)) +- **feat(gamification):** the dashboard Profile page now shows the real daily streak — `/api/gamification/level` returns `streak: { current, longest }` (per key with `apiKeyId`, operator-wide maximum otherwise) and the streak card reads it instead of a hard-coded 0 (#2403) ([#12377](https://github.com/diegosouzapw/OmniRoute/pull/12377)) — thanks @pacocartones +- **feat(gamification):** the dashboard leaderboard now shows each API key's display name under the Name column instead of a truncated key id; `GET /api/gamification/leaderboard` attaches `name` per entry (name only — no key material), while the shared ranking helper and the federation leaderboard stay id-only — thanks @pacocartones ([#12385](https://github.com/diegosouzapw/OmniRoute/pull/12385)) +- **feat(gamification):** enforce the documented 1000 XP/min per-API-key anti-cheat rate limit on the XP award path; over-limit awards are logged and skipped instead of persisted, and the sliding window now matches the timestamp format stored in `xp_audit_log` ([#2403](https://github.com/diegosouzapw/OmniRoute/issues/2403)) ([#12390](https://github.com/diegosouzapw/OmniRoute/pull/12390)) — thanks @pacocartones +- **feat(admin):** localize the gamification anomalies page — the loading state, the Status column and the Suspicious badge now come from the `common` catalog (new `common.suspicious` key propagated to every locale) — add it to the Gamification sidebar group as `gamification-admin` (`/dashboard/gamification/admin`), and expose the loading and empty states as polite `role="status"` live regions (#12401 — thanks @pacocartones) +- **feat(providers):** skip GitHub combo members missing from the live synced catalog, and drop Copilot models that are policy-disabled or hidden from the model picker ([#12473](https://github.com/diegosouzapw/OmniRoute/pull/12473)) — thanks @RaviTharuma +- **feat(i18n):** locale-expansion tooling — `npm run i18n:add-locale` adds a language to config, dashboard, docs mirrors, CLI, README/indexes and site in one command; browser, CLI and cookie detection resolve `uk`, `fil`/`tl`, `zh-Hant` (and the retired legacy `in` → `id`) via config aliases; `config/i18n.json` now ships in the npm package so the published CLI can read it; new real-translation ratio gate (`npm run i18n:check-ratio`, advisory) with a per-locale ratchet baseline; `run-translation --adopt` restores `.i18n-state.json`; `sync-language-bars` generates 🌐 bars from config; `validate_translation.py` loads its allowlist again. Retires the duplicate `in` locale (Indonesian mislabelled as Hindi) — 42 honest locales; saved `NEXT_LOCALE=in` / `OMNIROUTE_LANG=in` keep working. (#12496) +- **feat(api):** Emit gateway-measured `tokens_per_second` (TTFT excluded) on streaming usage and `X-OmniRoute-Tokens-Per-Second` when first-token latency is known ([#12616](https://github.com/diegosouzapw/OmniRoute/issues/12616)) ([#12631](https://github.com/diegosouzapw/OmniRoute/pull/12631)) — thanks @RaviTharuma +- **feat(opencode-plugin): map gateway cost/usage/tok/s onto OpenCode inference payloads** — the official plugin copies `X-OmniRoute-Response-Cost`, token counts, `X-OmniRoute-Tokens-Per-Second` / `usage.tokens_per_second`, TTFT, and the winning `X-OmniRoute-Model` onto the JSON/SSE body OpenCode already consumes. Missing tok/s is left unset (never `tokens / latency`). (#12636) — thanks @RaviTharuma +- **feat(combo):** add combo strategy `quota-weighted`: skip exhausted accounts, then weighted-draw by leftover / in-flight load; existing conversations stay pinned until the account is empty ([#12789](https://github.com/diegosouzapw/OmniRoute/pull/12789)) — thanks @HouMinXi +- **feat(models):** Account live listings become the chat catalog source for Claude, Codex, Copilot, and AGY; public metadata only fills prices on IDs those accounts already list. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **fix(models):** Union `agy` and `antigravity` live catalogs so `agy/gemini-3.8-flash-high` is not rejected after the prefix folds to `antigravity`. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **feat(opencode):** Add `@omniroute/opencode-plugin-v2`, an opencode v2 plugin that gives a v2 install a route to an OmniRoute gateway: it publishes models, combos and auto-combos into the host catalog, refreshes them lazily behind a 300s TTL, and keeps serving the last known catalog from an on-disk snapshot when the gateway is unreachable. The v1 plugin (`@omniroute/opencode-plugin`) is untouched and keeps working as before. The plugin adapts to the host instead of assuming one shape: it reads the shape the host seeds into the catalog draft and publishes the `api` block, the pre-`api` top-level fields, or both. The gateway key comes from the host credential store when one is connected, so no secret needs to sit in a config file; a source the gateway refuses is named in a warning instead of silently degrading the catalog to raw model ids; and tool schemas bound for a Gemini model are stripped of the JSON-Schema keywords Gemini rejects, so tool calling survives that route ([#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870)) — thanks @maxmad64bis +- feat(services): show sanitized CLIProxyAPI account health from its authenticated management API without exposing credentials, file paths, or raw account metadata (#6342) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add AnySearch free web search + URL extract (webFetch) with typed results, credential validation, REST routing, and MCP selection - fallback-only ([#11690](https://github.com/diegosouzapw/OmniRoute/pull/11690)) — thanks @Xxx91n +- **feat(dashboard):** display clamped `[0, 100]%` cached input token ratio in request logs table ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) — thanks @b3nw +- **feat(catalog):** add `OMNIROUTE_DISABLE_THINKING_LEVEL_VARIANTS` feature flag to optionally filter out thinking level variants from model catalog ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) — thanks @b3nw +- **feat(dashboard):** continuously export call logs to external analytics stores. A pluggable destination registry ships the full Logs-tab record set on an hourly `JobRegistry` cron, with a persisted per-destination cursor, batched inserts, a config UI rendered from each destination's own field descriptors, and a REST layer (`/api/log-export/*`) for CRUD, a connection test, and an on-demand run. A destination can opt into `includeBodies` to also ship the request and response payloads shown in the Logs detail pane, including the client and provider views of each call; this is off by default, and payloads inherit the dashboard's PII sanitisation, secret redaction and `noLog` handling. Google BigQuery is the first destination, using a service-account key stored encrypted at rest and streaming inserts keyed by call-log id, into a table that is day-partitioned on `timestamp` and clustered on `api_key_name`, `provider`, `model` and `status`. ([#11945](https://github.com/diegosouzapw/OmniRoute/pull/11945)) — thanks @dpozimski +- **feat(providers):** add MaxAI as a signed, OpenAI-compatible provider serving its 13 paid chat models (GPT-5.6 / Luna / Thinking, Claude 5 Sonnet, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite, Grok 4.1-fast / 4.5, DeepSeek V3.2 / R1, Llama 3.3 70B) through OmniRoute's `/v1` endpoint, with per-request HMAC-SHA1→SM3→AES request signing, live model + context-window discovery from `/models/get_config`, and prompted tool-calling translated to OpenAI `tool_calls` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI vision input — image_url content parts are forwarded inline in `message_content` to the 6 vision-capable models (GPT-5.6 / Luna / Thinking, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite) ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI image generation — 6 image models (gpt-image-1, dall-e-3, flux-1-schnell/dev/pro, sd3-medium) exposed through `POST /v1/images/generations` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI document RAG — inline base64 file/document attachments are uploaded to MaxAI (content-addressed `doc_id`) and attached to the chat via `doc_list` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** browserless MaxAI onboarding — email device-pair login (`/api/providers/[id]/login`) and signed access-token refresh, so a connection can be created and kept fresh without a real browser or Google OAuth ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** per-provider TLS impersonation profile (MaxAI presents a Windows Firefox-150 client fingerprint) so its bot-sensitive endpoints accept OmniRoute traffic ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** add **Nimble** as a web-search and web-fetch provider (`nimble-search`) — `/v1/search` routes to Nimble's search API at `lite` depth (locale, freshness and include/exclude domain filters mapped onto the shared request contract), and `/v1/web/fetch` routes to Nimble Extract, which covers all four fetch formats (`markdown`, `html`, `links`, `screenshot`) from a single call. One API key serves both surfaces. ([#11620](https://github.com/diegosouzapw/OmniRoute/pull/11620)) — thanks @wildcard +- The npm publish is automatic again, through npm Trusted Publishing (OIDC): the hosted `stage-npm` job publishes with a short-lived credential minted from GitHub's id-token — no `NPM_TOKEN`, no 2FA prompt, provenance attached. `publish_mode=staged` (owner approves with 2FA) and `direct` (token) remain available on `workflow_dispatch`. ([#11931](https://github.com/diegosouzapw/OmniRoute/pull/11931)) +- **feat(providers):** Add **Opper** as an API-key gateway provider — EU-hosted AI gateway with 700+ models from 30+ providers behind one OpenAI-compatible API and one key (`OPPER_API_KEY`); model ids use `provider/model` format (e.g. `anthropic/claude-sonnet-4-6`, `openai/gpt-5`); live model catalog at `https://api.opper.ai/v3/compat/models`; entry mirrors `requesty` (same shape, `passthroughModels: true`, no static seed) ([#11629](https://github.com/diegosouzapw/OmniRoute/pull/11629)) +- **feat(dashboard):** the `/dashboard/orchestration` snapshot hook now subscribes to the `agents` WebSocket channel (`agent.task.updated`) instead of `requests` as its refetch trigger, and relaxes its background poll from 5s to 30s while that WS connection is up — falling back to the tighter 5s cadence, reprogrammed live on any connect/disconnect transition, whenever the socket is down. ([#12409](https://github.com/diegosouzapw/OmniRoute/pull/12409)) +- **feat(dashboard):** Orchestration canvas quick wins — search box plus state/source/provider filter chips with a one-click clear, and per-source collapse/expand, all reflected in the URL so a filtered/collapsed view is shareable and survives a refresh; the detail drawer gained a "copy trace JSON" action and hardened error/empty-state and accessibility handling; the Agents-tab edges now animate traveling particles along active (running) connections; and the canvas node/edge status colors moved off fixed hex values onto theme-aware `--orch-status-*` CSS custom properties, so they adapt correctly to light/dark mode. ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- **chore(dashboard):** Orchestration UI hardening pass and the missing component/model test coverage it called for — `OrchestratorNode`/`ActivityNode`/`OverflowNode` rendering, the `?node=`/overflow-click page routing, the Agents-tab orchestrator-click no-op and `showCompleted` toggle, and the overview kanban's done-column sort order (#12270, #12271). ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- New `/dashboard/orchestration` page: live unified view of everything running — Cloud Agent, A2A and Conductor as a real-time graph (Agents tab), the combo cascade (Routing tab, reusing the Combo Live Studio) and a state kanban (Overview tab), with a detail drawer (trace, cost, approve/cancel). Read-only over existing APIs — no new backend. Canvas concept credit: PR #11815 design ([#12261](https://github.com/diegosouzapw/OmniRoute/pull/12261)) +- **feat(dashboard):** new "History" tab on `/dashboard/orchestration` — an Airflow-style grid of finished runs over a 24h/7d/30d preset window, one row per (source, identity), clicking a cell opens the existing detail drawer. It is backed by real persistence: A2A task lifecycle transitions are now written to the `a2a_tasks` table (purged after 30 days, configurable via `OMNIROUTE_A2A_HISTORY_RETENTION_DAYS`) and served by the new `GET /api/a2a/tasks/history` listing endpoint, with the task-detail route falling back to persisted history once a run leaves the in-memory snapshot. Conductor runs stay remote and are not persisted locally — the tab says so instead of silently omitting them. ([#12479](https://github.com/diegosouzapw/OmniRoute/pull/12479)) +- **feat(dashboard):** the orchestration detail drawer gained a "Repeat" action for Cloud Agent, A2A and Conductor tasks — a two-click confirm (click once to arm, click again within the confirm window to fire) re-submits the original prompt/input as a new run. The button is disabled with an explanatory tooltip whenever the original input can't be recovered from the loaded task detail (e.g. it never carried a prompt, or the detail failed to load). Two limitations of the A2A variant, by design: it targets the `/a2a` JSON-RPC endpoint, which authenticates with an API key only (`REQUIRE_API_KEY=true` or a configured `OMNIROUTE_API_KEY` makes a dashboard-session repeat answer `HTTP 400` — surfaced verbatim in the drawer's error line, never as a success), and the `message/send` call is SYNCHRONOUS: the POST blocks for the whole skill run, so the success confirmation only appears once the repeated task finishes. A dashboard-authenticated A2A creation path is deliberately left to a follow-up — widening the endpoint's auth posture is an operator decision, not a side effect of this feature. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(a2a):** A2A task execution now records which memories were consulted for the task's last user message as `metadata.memoryHits` (id/key/type/content-snippet) plus a `memory_hits` history event, purely for observability — the retrieved memory is never injected into a skill's prompt or behavior. Gated by the `OMNIROUTE_A2A_MEMORY_HITS` kill-switch (default enabled; set to `0` to skip the recall lookup entirely). The drawer's new "Memory used" section lists these hits for a2a tasks and is omitted whenever there are none. Known limitation: recall only resolves under the keyless posture — a keyed caller's task owner is a SHA-256 prefix of the API key, while memory rows are keyed by the database api-key id, and no hash→id lookup exists today, so the hit list stays empty for keyed callers. The recorded hits are also kept out of the task's own `input` (and therefore out of the persisted input and of the "Repeat" request body), so repeating a task never re-sends the previous run's memory snippets. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(providers):** add a Perplexity Agent API provider (`perplexity-agent` / `pplx-agent`) for Perplexity `/v1/responses`, including the documented Anthropic, OpenAI, Google, xAI, DeepSeek, Z.AI, Moonshot/Kimi, NVIDIA, and Perplexity model IDs plus Anthropic-model `max_output_tokens` compatibility. ([#12103](https://github.com/diegosouzapw/OmniRoute/pull/12103)) — thanks @AIB1TAL0S +- **feat(providers):** add RPD (Requests Per Day) limit to provider rate limit overrides across UI, schemas, DB, and i18n ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) — thanks @b3nw +- **feat(providers): add UC Direct (uncensored.com Developer API), the metered OpenAI-compatible surface.** A standard OpenAI-compatible passthrough (default executor) for uncensored.com's official REST API at `https://api.uncensored.com/api/v1`: `X-api-key` auth (never-expiring `uai_sk_live_` key), `POST /chat/completions` with streaming SSE and native tool-calling, and the full live metered catalog (82 models across 15 providers, discovered from the public `GET /v1/models`). Registered as provider `uc-direct` (alias `ucd`). Complements the un-metered `uc` persona provider — same models, metered credits and a plain API key instead of a subscription session. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(providers): add UC (uncensored.com), the un-metered subscription "persona" chat as an OpenAI-compatible provider.** A WebSocket web-app port: a durable Clerk credential mints a short-lived session token per connect (browserless — no API key), driving UC's persona socket. Ships the browserless email-code login (request → verify → harvest), the 19 verified persona models (Claude Opus, Gemini, Grok, GLM, Kimi, DeepSeek, MiniMax, incl. the uncensored variants), prompted `` tool-calling with a per-model code-style dialect + auto-cure retry for guardrailed models, live ``/reasoning split, streaming + non-streaming OpenAI responses, and full quota/auth error surfacing (paywall / message-limit / rate-limit → 429, invalid session → 401 re-login). Full multimodal parity via the persona blob-upload layer: **vision** (image input, 15 vision-capable models), **document RAG** (PDF/doc upload, server-side extraction), **image generation** (22 models), **video generation** (14 models, async signed-url → poll), and **TTS** (streaming MP3). Registered as provider `uc` (alias `ucn`). The metered OpenAI-compatible Developer API is a separate `uc-direct` provider. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(logs):** add copy-all button to request detail modal ([#11083](https://github.com/diegosouzapw/OmniRoute/pull/11083)) — thanks @AndrianBalanescu +- **feat(check):** make serviceKinds required and add the reverse-walk provider consistency gate ([#11392](https://github.com/diegosouzapw/OmniRoute/pull/11392)) — thanks @Tushar49 +- **feat(providers):** only advertise verified-working connections by default ([#11448](https://github.com/diegosouzapw/OmniRoute/pull/11448)) — thanks @alltomatos +- **feat(resilience):** add hierarchical concurrency admission ([#11493](https://github.com/diegosouzapw/OmniRoute/pull/11493)) +- **feat(providers):** derive + surface expiry for JWT-bearing web cookies (#11497) ([#11505](https://github.com/diegosouzapw/OmniRoute/pull/11505)) — thanks @oyi77 +- **feat(tunnels):** support named cloudflared tunnels via CLOUDFLARED_CONFIG ([#11561](https://github.com/diegosouzapw/OmniRoute/pull/11561)) — thanks @ggiak +- **feat(compression):** make proactive context-compression threshold a live setting ([#11564](https://github.com/diegosouzapw/OmniRoute/pull/11564)) — thanks @ragnar-claude +- **feat(resilience):** add provider peak-hour protection ([#11622](https://github.com/diegosouzapw/OmniRoute/pull/11622)) — thanks @Neuron-Mr-White +- **feat(providers):** add 1min.ai provider ([#11631](https://github.com/diegosouzapw/OmniRoute/pull/11631)) — thanks @zero-executioner +- **feat(dashboard):** collapsible JSON tree viewer for request/response payloads ([#11703](https://github.com/diegosouzapw/OmniRoute/pull/11703)) — thanks @hartmark +- **feat(routing):** add LiquidAI LFM2.5-2.6B free tier via OpenRouter ([#11752](https://github.com/diegosouzapw/OmniRoute/pull/11752)) — thanks @brick30llc-ctrl +- **feat(pricing):** add GLM-5.3-Flash pricing, model specs, and catalog registration ([#11830](https://github.com/diegosouzapw/OmniRoute/pull/11830)) — thanks @AndrianBalanescu +- **feat(routing):** add relayMode for schema-locked context handoffs ([#11839](https://github.com/diegosouzapw/OmniRoute/pull/11839)) — thanks @raheemuddin786 +- **feat(config):** add support for runtime system prompt configuration and hot-reloading ([#11841](https://github.com/diegosouzapw/OmniRoute/pull/11841)) — thanks @raheemuddin786 +- **feat(routing):** expand connection-aware quota prefilter across combo strategies (#11682) ([#11850](https://github.com/diegosouzapw/OmniRoute/pull/11850)) — thanks @HouMinXi +- **feat(models):** reactive discovery sync after Antigravity model-not-found 404 ([#11888](https://github.com/diegosouzapw/OmniRoute/pull/11888)) — thanks @MumuTW +- **feat(models):** auto-aliases and token limits from synced Antigravity discovery ([#11889](https://github.com/diegosouzapw/OmniRoute/pull/11889)) — thanks @MumuTW +- **feat(settings):** raise sticky round-robin limit caps to 1000 ([#12015](https://github.com/diegosouzapw/OmniRoute/pull/12015)) — thanks @adivekar-utexas +- **feat(resilience):** operator-configurable global credential health check interval ([#12043](https://github.com/diegosouzapw/OmniRoute/pull/12043)) — thanks @adivekar-utexas +- **feat(quota):** use official OpenCode Go usage API ([#12124](https://github.com/diegosouzapw/OmniRoute/pull/12124)) — thanks @ddarkr +- **feat(resilience):** default the credential health check sweep to 60 minutes ([#12138](https://github.com/diegosouzapw/OmniRoute/pull/12138)) — thanks @adivekar-utexas +- **feat(quality):** new-code mode for the complexity and dead-code ratchets (Clean as You Code) ([#12142](https://github.com/diegosouzapw/OmniRoute/pull/12142)) +- **feat(dashboard):** orchestration canvas — unified model + snapshot hook (part 1/2) ([#12156](https://github.com/diegosouzapw/OmniRoute/pull/12156)) +- **feat(usage):** add Kilo Code balance and Kilo Pass quotas ([#12178](https://github.com/diegosouzapw/OmniRoute/pull/12178)) — thanks @killer30001000 +- **feat(providers):** refresh vendored ChatGPT Web connector to v4.0.7 ([#12181](https://github.com/diegosouzapw/OmniRoute/pull/12181)) — thanks @backryun +- **feat(providers):** manual "Clear cooldown" action in the cooling panel ([#12224](https://github.com/diegosouzapw/OmniRoute/pull/12224)) — thanks @patrykkopycinski +- **feat(providers):** restore ChatGPT Web via clean-room browser transport ([#12239](https://github.com/diegosouzapw/OmniRoute/pull/12239)) — thanks @backryun +- **feat(sse):** wire the PROVIDER_PROFILES window gate into the global provider cooldown ([#12247](https://github.com/diegosouzapw/OmniRoute/pull/12247)) +- **feat(providers):** modernize CLOVA Studio chat and embeddings ([#12277](https://github.com/diegosouzapw/OmniRoute/pull/12277)) — thanks @backryun +- **feat(browser):** adopt Obscura as primary headless browser engine with Chromium fallback ([#12286](https://github.com/diegosouzapw/OmniRoute/pull/12286)) — thanks @opensource-elearning +- **feat(sse):** full language parity for output styles (es/de/fr/it/ru/zh + autoDetect) ([#12289](https://github.com/diegosouzapw/OmniRoute/pull/12289)) +- **feat(quality):** complete test:scoped — --full map rebuild, stdin selection, CI loader parity (#8084 D1) ([#12353](https://github.com/diegosouzapw/OmniRoute/pull/12353)) +- **feat(providers):** refresh Fable, Cursor, and Devin catalogs ([#12367](https://github.com/diegosouzapw/OmniRoute/pull/12367)) — thanks @backryun +- **feat(video):** redact transcript text from logs and durable memory (#12150 P1) ([#12427](https://github.com/diegosouzapw/OmniRoute/pull/12427)) +- **feat(dashboard):** parent-link, genuine-continuation badge, and modal perf fixes ([#12448](https://github.com/diegosouzapw/OmniRoute/pull/12448)) — thanks @hartmark +- **feat(dashboard):** adaptive context-budget dial on compression panel ([#12488](https://github.com/diegosouzapw/OmniRoute/pull/12488)) — thanks @HouMinXi +- **feat(providers):** refresh Z.ai Web models and browser transport ([#12524](https://github.com/diegosouzapw/OmniRoute/pull/12524)) — thanks @backryun +- **feat(video):** redact raw client-snapshot transcript fields in the detailed log (#12150 P2) ([#12528](https://github.com/diegosouzapw/OmniRoute/pull/12528)) +- **feat(providers):** refresh NVIDIA hosted models ([#12538](https://github.com/diegosouzapw/OmniRoute/pull/12538)) — thanks @backryun +- **feat(quota):** Moonshot Open Platform balance and TPD lock for custom nodes ([#12590](https://github.com/diegosouzapw/OmniRoute/pull/12590)) — thanks @HouMinXi +- **feat(video):** redact transcript in the in-memory pending-request snapshot (#12430 item 6) ([#12596](https://github.com/diegosouzapw/OmniRoute/pull/12596)) +- **feat(dashboard):** link a log entry's Conversation Context to its owning conversation ([#12646](https://github.com/diegosouzapw/OmniRoute/pull/12646)) — thanks @hartmark +- **feat(catalog):** eligibility-gated free-tier bucket ([#12669](https://github.com/diegosouzapw/OmniRoute/pull/12669)) +- **feat(db):** fail-closed previous_response_id continuation for redacted video turns (#12150 P2b) ([#12707](https://github.com/diegosouzapw/OmniRoute/pull/12707)) +- **feat(gamification): pay the documented `streak_bonus` and `badge_unlock` XP rewards.** `XP_REWARDS` listed both rewards but the award pipeline never paid them: the private reward table in `events.ts` omitted them, `updateStreak()` did not report when a streak extended, and badge unlocks carried no XP. Every request that extends a daily streak now pays `streak_bonus × streak length` once per UTC day (guarded by a same-day `xp_audit_log` check), and every badge unlocked through the pipeline pays `badge_unlock` once per badge (guarded by the `user_badges` primary key; `unlockBadge()` now reports whether it inserted). Bonus XP flows through the same `addXp` + level sync + global/weekly/monthly leaderboard path as action XP, so level-ups and rankings include it. The Radar supporter recognition unlock stays XP-free. (#12522 — thanks @pacocartones) +- **feat(providers):** advertise a `free-tier` capability in the provider plugin manifest for every provider with documented free models, so sidecars and dashboards can filter free-capable providers without reading the quota catalog ([#12786](https://github.com/diegosouzapw/OmniRoute/pull/12786)) — thanks @maxmad64bis +- **feat(providers):** Added EURouter as an OpenAI-compatible API-key gateway (`https://api.eurouter.ai/v1`), with live model discovery via `passthroughModels`. Its copy states that models are served by third-party upstreams listed per model, so an EU-based router is not read as EU data residency for inference. ([#13025](https://github.com/diegosouzapw/OmniRoute/pull/13025)) — thanks @ntdatt812 +- **feat(providers):** Added GreenPT as an OpenAI-compatible API-key provider (`https://api.greenpt.ai/v1`), with live model discovery via `passthroughModels`. No free-inference badge: the published docs describe a free API subscription billed per token, not a free tier. ([#13024](https://github.com/diegosouzapw/OmniRoute/pull/13024)) — thanks @ntdatt812 +- **feat(i18n):** 9 new locales — Greek (`el`), Croatian (`hr`), Serbian (`sr`), Lithuanian (`lt`), Estonian (`et`), Latvian (`lv`), Slovenian (`sl`), Maltese (`mt`), Irish (`ga`) — across the dashboard, docs mirrors, CLI, README and the site; OmniRoute now ships every official EU language (51 locales). (#13044) +- **feat(i18n):** 8 new locales — Kannada (`kn`), Malayalam (`ml`), Odia (`or`), Punjabi (`pa`), Nepali (`ne`), Sinhala (`si`), Burmese (`my`), Khmer (`km`) — across the dashboard, docs mirrors, CLI, README and the site (59 locales). The translator now restores the ICU literal escape around angle placeholders and splits oversized docs sections before translating. (#0000) ([#13660](https://github.com/diegosouzapw/OmniRoute/pull/13660)) +- feat(providers): **list Agnes 3.0 Flash as the current free chat model, drop retired 1.5 Flash, add Image 2.0/2.5 Flash plus Video 2.5/2.5 Flash, and discover the live `/v1/models` catalog (including the CN host `api.agnes-ai.cn`).** `agnes-1.5-flash` now forwards to `agnes-3.0-flash`. Video 2.5 polls `GET /v1/videos/{id}` (not the V2.0 `/agnesapi` contract). Live `/v1/models` (2026-09-09) no longer serves 1.5; the wiki marks it deprecated. 3.0 Flash is 512K context / 65,536 max output, same window as 2.5. CN-region keys use the existing per-connection base-URL field, default stays `apihub.agnes-ai.com`. ([#13120](https://github.com/diegosouzapw/OmniRoute/pull/13120)) — thanks @HouMinXi +- **feat(sse):** Codex and OpenAI catalogs list GPT-6 Astra with effort aliases (`-low` through `-ultra`); Codex CLI identity pins `@openai/codex@0.153.4` in lockstep with the image ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **feat(dashboard):** the orchestration History tab gained a "Compare runs" mode — toggling it turns each grid cell into a 2-item selection queue (a 3rd click drops the oldest pick), and picking two cells opens a side-by-side comparison panel instead of the usual detail drawer. The panel fetches both runs' detail the same way the drawer does (falling back to persisted history once a run leaves the live TTL window) and shows, per side: identity/source/state, start time, a signed `right - left` delta for duration/cost/event count, the event timeline aligned by index, and any memory hits. One side's fetch failing never blocks the other, and a delta is only ever computed when both sides have a finite value — otherwise it renders "—", never `NaN`. Comparing two runs from different sources or skills still works; a banner marks the deltas as informational rather than hiding them, since the two runs aren't a strict apples-to-apples pair. ([#12677](https://github.com/diegosouzapw/OmniRoute/pull/12677)) +- **feat(dashboard):** badge a conversation that never reached a clean stop ([#12717](https://github.com/diegosouzapw/OmniRoute/pull/12717)) — thanks @hartmark +- **feat(release):** reconcile-changelog tool + version-anchored fragment aggregation ([#12987](https://github.com/diegosouzapw/OmniRoute/pull/12987)) +- **feat(mcp):** carry context_length through MCP list_models_catalog projection (#12776) ([#13406](https://github.com/diegosouzapw/OmniRoute/pull/13406)) — thanks @KooshaPari +- **feat(cli):** make startup readiness budget configurable (#13369) ([#13433](https://github.com/diegosouzapw/OmniRoute/pull/13433)) — thanks @KooshaPari + ### 🐛 Bug Fixes +- **security(streaming):** sanitize generic mid-stream error messages before emitting OpenAI, + Responses, or Claude SSE failure frames and before diagnostic logging, while preserving raw + failures for internal classification and keeping client disconnects out of provider failure state. ([#12457](https://github.com/diegosouzapw/OmniRoute/pull/12457)) +- **fix(claude):** `blockExtraUsage: false` no longer lets 5h quota preflight skip the account; extra usage is billed after the session bar is gone, so the request must reach Anthropic ([#12803](https://github.com/diegosouzapw/OmniRoute/pull/12803)) — thanks @HouMinXi +- Sanitize HuggingChat conversation-creation and message-send transport failures before they reach client error bodies or provider logs. ([#12467](https://github.com/diegosouzapw/OmniRoute/pull/12467)) +- **fix(dashboard):** Keep local and theme-aware provider SVG icons at a definite layout size so Chromium does not collapse them to 0×0 after the v3.8.50 image-rendering change ([#12054](https://github.com/diegosouzapw/OmniRoute/pull/12054)) — thanks @ponkcore +- **fix(security):** Sanitize provider and runtime failures before public API, SSE and MCP responses and before persistent request, proxy and usage logs, preventing credentials, stack traces and host filesystem paths from crossing those boundaries while preserving stable error codes and useful diagnostics. ([#12506](https://github.com/diegosouzapw/OmniRoute/pull/12506)) +- **fix(cli):** `omniroute tunnel create` no longer crashes with `Cannot read properties of undefined (reading optsWithGlobals)` — removed the duplicate positional argument that caused Commander.js to misalign the action callback parameters ([#12295](https://github.com/diegosouzapw/OmniRoute/issues/12295)) ([#12368](https://github.com/diegosouzapw/OmniRoute/pull/12368)) — thanks @gonisulaimann +- Stop advertising Gemini Live-only models as supported audio endpoints until OmniRoute proxies the bidirectional Live protocol. ([#11710](https://github.com/diegosouzapw/OmniRoute/pull/11710)) — thanks @RaviTharuma +- **fix(kie):** reroute `flux/kontext` off the KIE Market `createTask` flow — it is catalogued with `isMarket: true` but has no Market catalog page, so KIE rejected it with "model name not supported"; it now hits the dedicated `POST /api/v1/flux/kontext/generate` / `GET /api/v1/flux/kontext/record-info` endpoints instead (#11296). ([#11985](https://github.com/diegosouzapw/OmniRoute/pull/11985)) — thanks @hartmark +- **fix(kie):** correct 12 more KIE Market catalog ids that were sent to `createTask` unchanged but diverge from KIE's documented upstream `model` values — GPT Image 2 T2I/I2I (drops the `gpt/` prefix), GPT Image 1.5 T2I/I2I (`gpt-image/` namespace), Seedream 5.0 Lite T2I/I2I (drops the `.0`), all 4 Flux 2 variants (`flux-2/` namespace, generic variant renamed `flex`), and Wan 2.7 Image / Image Pro (dash instead of dot) — each verified individually against the literal example request published on docs.kie.ai. `#11326`'s "everything else already matches" claim was wrong a second time (#11296); `z-image/4.0-*`/`z-image/4.5-*` and `flux/kontext` remain open, documented as unresolved in `KIE_MARKET_UPSTREAM_MODEL_IDS`'s comment pending further verification. ([#11667](https://github.com/diegosouzapw/OmniRoute/pull/11667)) +- **fix(dashboard):** `useApiKeySave.handleSaveApiKey` no longer forces a full upstream `/models` catalog sync on every non-curated provider connection save — callers can now pass `skipModelSync: true` to opt out, so a workflow that only wants to add one manual model no longer floods the provider's available-models list with hundreds/thousands of synced entries. The flag is a client-side intent signal only and is stripped before the connection payload is POSTed to `/api/providers`; default behavior (full sync on save) is unchanged when the flag is omitted (#11324) ([#11664](https://github.com/diegosouzapw/OmniRoute/pull/11664)) +- **Provider connections:** keep `tokenExpiresAt` when a connection is created. The create-path allowlist omitted it, so every insert stored NULL and the dashboard token badge could read a fresh connection as expired until its first background refresh ([#11368](https://github.com/diegosouzapw/OmniRoute/pull/11368)). — thanks @ntdatt812 +- **fix(combos):** the combo builder's precision-select, global-model-search, and manual-entry flows now serialize a model step's `model` string using the provider's already-computed routing-alias prefix (e.g. `oc/`) instead of rebuilding it from the raw canonical `providerId`, fixing the no-auth "OpenCode Free" provider (`opencode`) being routed to the unrelated paid "OpenCode Zen" provider (`opencode-zen`) because `opencode` doubles as a manual routing-prefix override ([#11433](https://github.com/diegosouzapw/OmniRoute/issues/11433)). ([#11670](https://github.com/diegosouzapw/OmniRoute/pull/11670)) +- **fix(radar):** the catalog feed cache now keeps `generatedAt`, the date the feed's data was built, next to `fetchedAt`, the date this install downloaded it (#11435). The feed schema requires that date and the sync path validates it, but the cache dropped it — so a feed fetched minutes ago and one carrying weeks-old figures looked identical to everything downstream, including the dashboard's "Last fetched" line. `getRadarCatalog().meta` and `GET /api/radar/status` now report both dates, the latter as its own field rather than folded into `version` — and omitted entirely for the offers and intel caches, which keep no build date, where a `null` would read as "unknown" rather than "never stored". The dashboard still shows only the fetch time; surfacing the build date there needs a new translated label and is left to a follow-up. Rows cached before migration 163 read back as `null`: unknown stays unknown instead of borrowing the fetch time. The referrals cache has persisted the same date since migration 142. — thanks @maxmad64bis +- **fix(cli):** postinstall no longer fills `JWT_SECRET` and `API_KEY_SECRET` in the installed package's `.env` (#11436). `.env.example` ships both blank on purpose: the server restores them from its durable store, or generates and persists them there, in `ensureSecrets()`. Pre-filling them defeated that — the file lives inside the package directory, so `npm i -g` replaced it and postinstall wrote _different_ values, while `ensureSecrets()` (which only acts on an empty variable) never got to restore the real ones. Both secrets rotated silently on every update, invalidating dashboard sessions and API-key CRCs. `STORAGE_ENCRYPTION_KEY` left the same list for the same reason in #1622; its comment pointed at a function that no longer exists and now names the real provisioning path. — thanks @maxmad64bis +- **fix(cli):** the CLI now says when a loaded `.env` lives inside the installed package directory (#11437). It already announces every env file it reads, without distinguishing the ones that survive an update from the one that does not: `npm i -g` replaces the package directory wholesale, so values set there are gone at the next update, silently. The warning names the durable path to move them to, and fires only when that file actually supplied a value — a file entirely shadowed by a durable one supplied nothing. A development checkout stays silent: there the same path is stable and documented in `SETUP_GUIDE.md`. — thanks @maxmad64bis +- **fix(free-models):** the shared `isFreeModel()` predicate no longer reports catalog entries marked `freeType: "discontinued"` as free, so `hidePaidModels` can't route to Pollinations' seven premium models that now require a paid key ([#11441](https://github.com/diegosouzapw/OmniRoute/pull/11441)) — thanks @maxmad64bis +- fix(ui): let AnySearch use the normal provider-icon fallback when LobeHub has no matching icon (#11449) +- Fixed the v3.8.50 Costs and Analytics dashboards so flat-rate Claude Code usage can be shown as an explicitly requested token-price estimate without changing default billed-cost semantics. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed archived usage retention so each request is priced individually instead of pricing a day's summed tokens once, which understated archived cost whenever a day mixed cache-heavy and ordinary requests. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed the Costs dashboard so it discloses when displayed figures include flat-rate token-price estimates instead of labelling them as billed spend, using the flag the analytics API already returns; the month-end projection and the CSV/JSON exports carry the same marker, and billed-cost mode is unchanged. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- **fix(combo):** attach the same combo-diagnostics trace (`poolSize`/`attemptOrder`/`excluded`/`terminalReason`, plus `x-omniroute-combo-*` headers) to the round-robin strategy's and the nested pipeline/fusion runtime-unit loop's "Maximum combo retry limit reached" 503 that the priority-strategy path already attaches for the identical terminal condition — previously those two paths returned a bare, contextless 503 ([#11462](https://github.com/diegosouzapw/OmniRoute/issues/11462)). ([#11665](https://github.com/diegosouzapw/OmniRoute/pull/11665)) +- **fix(bun):** use native `bun:sqlite` in `bootstrap-env` and `sync-env` to avoid loading `better-sqlite3` N-API addon during Bun startup ([#11468](https://github.com/diegosouzapw/OmniRoute/pull/11468)) — thanks @TheDemonTuan +- **fix(docker-bun):** make `Dockerfile.bun` install reliable with pre-install script helpers and native `bun:sqlite` smoke check ([#11470](https://github.com/diegosouzapw/OmniRoute/pull/11470)) — thanks @TheDemonTuan +- **fix(docker-bun):** remove every vendored `better-sqlite3` native addon from the Bun runtime image so startup cannot abort before the `bun:sqlite` fallback ([#11482](https://github.com/diegosouzapw/OmniRoute/pull/11482)) — thanks @TheDemonTuan +- **fix(autoCombo):** effort/alias model variants (`gpt-5.6-sol-xhigh`, `gpt-5.6`, cursor's `claude-4.6-opus-high`) inherit their base model's task fitness instead of falling to the wildcard 0.5 ([#11492](https://github.com/diegosouzapw/OmniRoute/pull/11492)) — thanks @MumuTW +- fix(db): rate-limit repeated Arena ELO leaderboard fetch-failure warnings instead of logging one per sync attempt (#11500) ([#11989](https://github.com/diegosouzapw/OmniRoute/pull/11989)) — thanks @hartmark +- **fix(db):** dedupe the raw `[Encryption] Decryption failed...` log line emitted by the lazy-decrypt views (`createLazyRowProxy`/`createLazyConnectionView`), which power `getProviderConnections()` and were re-triggering that line on every CredentialHealth/model-sync cycle for the same corrupt or stale-key credential — a fresh Proxy over a fresh row on every cycle meant the per-proxy memoization never suppressed it, unlike the dedup `decryptConnectionFields()` already had since [#9927](https://github.com/diegosouzapw/OmniRoute/issues/9927). Now shares that dedupe tracking so the line logs at most once per credential ([#11500](https://github.com/diegosouzapw/OmniRoute/issues/11500)). ([#11678](https://github.com/diegosouzapw/OmniRoute/pull/11678)) +- **fix(intelligence):** Arena ELO sync stores a synthesized base-model row for every effort/harness variant (`claude-opus-5-max` → `claude-opus-5`, `gpt-5.6-sol-xhigh (codex-harness)` → `gpt-5.6-sol`), so bare model requests reach the synced intelligence layer instead of the static fallback table; the hand-maintained `MODEL_ALIAS_MAP` — which collapsed generations (`gpt-5.5` → `gpt-5`) — is gone ([#11506](https://github.com/diegosouzapw/OmniRoute/pull/11506)) — thanks @MumuTW +- **fix(autoCombo,sse):** catalog hygiene — the static task-fitness table no longer ranks retired models above live ones (dead rows dropped, patterns matched on segment boundaries), the 7 deprecation aliases that forwarded to retired or non-existent models now point at the vendors' published replacements, an alias is no longer rewritten when the serving provider offers the id as-is, and a new `check:model-lifecycle` gate diffs both tables against a vendor lifecycle snapshot ([#11507](https://github.com/diegosouzapw/OmniRoute/pull/11507)) — thanks @MumuTW +- **fix(sse):** stop dropping resolved `thoughtSignature` values on parallel (multi tool-call) turns sent to Gemini 3.x — the claude→gemini and openai→gemini translators previously kept the signature only on the *first* function call of a message, causing Gemini to reject subsequent calls in the same turn with HTTP 400 "Function call is missing a thought_signature"; each function call now keeps its own resolved signature ([#11510](https://github.com/diegosouzapw/OmniRoute/issues/11510)). ([#11669](https://github.com/diegosouzapw/OmniRoute/pull/11669)) +- Treat Docker publishing from a non-default release branch as an expected skip instead of a failed workflow, while preserving the guard that prevents it from overwriting the `next` channel. ([#11527](https://github.com/diegosouzapw/OmniRoute/pull/11527)) — thanks @pacocartones +- fix(search): prefer credentialed providers over duckduckgo-free fallback (#11524) — the fallback-only loop ran before the credentialed-providers loop in `executeWebSearch`, making configured providers unreachable when `duckduckgo-free` was available. ([#11565](https://github.com/diegosouzapw/OmniRoute/pull/11565)) — thanks @vsd2807 +- **fix(sse):** cap the upstream headers-wait phase for STREAMING requests to a client-realistic ceiling (110s, under Codex's own ~120s hard client-abort window) instead of the flat 10-minute `FETCH_TIMEOUT_MS` default — that default was 5x longer than the body-phase readiness watchdog's own adaptive bound, so a request whose upstream never returned any response at all (not even headers, e.g. a stalled NVIDIA target behind a tool-heavy Responses→Chat translation) kept the client connection alive on keepalives only, guaranteeing the client's own patience ran out first with an opaque 499 instead of OmniRoute detecting and failing the stall fast. Non-streaming requests are unaffected — they keep the existing flat default (`open-sse/utils/fetchStartTimeoutPolicy.ts`) (#11526) ([#11668](https://github.com/diegosouzapw/OmniRoute/pull/11668)) +- **fix(ci):** protocol-clients E2E harness boots the peer-stamped custom server (`run-next.mjs dev`) instead of the bare `next` CLI, so `LOCAL_ONLY` locality resolves from the real TCP peer stamp and `/api/mcp/audit` answers 200 (open bootstrap preserved via an `OMNIROUTE_E2E_BOOTSTRAP_MODE=open` credential clear in `run-next.mjs`, pinned to loopback `HOST`) instead of a deterministic 403 (#11535) ([#11549](https://github.com/diegosouzapw/OmniRoute/pull/11549)) — thanks @jonlwheat2-gif +- **fix(docs):** the Auto-Combo engine is described with the number of factors it actually declares. Four different counts were in circulation — 6, 9, 13 and 14 across `AGENTS.md`, four guides and six code comments — while `DEFAULT_WEIGHTS` declares 15. `check:docs-counts` now reads that number from the source, so adding a factor without updating the prose fails CI ([#11545](https://github.com/diegosouzapw/OmniRoute/pull/11545)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **fix(quota-share):** in-flight leases are per request, so an aborted request's slot ages out instead of being kept alive by later traffic on the same connection ([#11547](https://github.com/diegosouzapw/OmniRoute/pull/11547)) — thanks @abhisheksharma2411 +- The free-tier summary route now serves the refreshed Radar catalog when the feed is active — the same numbers the dashboard shows — and states which catalog answered plus its real build date, instead of always reporting release-frozen figures with a stale curation date. The supporter-key live feed stays reserved to authenticated callers of the instance. ([#11550](https://github.com/diegosouzapw/OmniRoute/pull/11550)) — thanks @maxmad64bis +- **sse:** fix LiveWS/embed-WS servers crashing at startup under the Node/tsx runtime — `liveServer.ts`, `embedWsProxy.ts` and `apiBridgeServer.ts` imported `@/shared/utils/httpClientAbortGuard` without the `.mjs` extension, so the client-abort crash guard added by [#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556) was unreachable and every dependent test failed with `ERR_MODULE_NOT_FOUND` ([#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556)). ([#11671](https://github.com/diegosouzapw/OmniRoute/pull/11671)) — thanks @jonlwheat2-gif / @yxyxy +- fix(chat): shed any of the matched provider-node's routing identifiers (public prefix or internal id) before model lookup, so composites like `//` no longer reach upstream verbatim (#11557) — thanks @maxmad64bis +- **fix(cache):** a semantic-cache entry now expires on its TTL instead of surviving until the next UTC midnight, and `dbEntries` stops counting expired rows ([#11573](https://github.com/diegosouzapw/OmniRoute/pull/11573)) — thanks @ntdatt812 +- **fix(api):** `/v1/models` schedules its stale-while-revalidate rebuild through Next's `after()` again, so a stale catalog reaches the client before the rebuild blocks the event loop ([#11574](https://github.com/diegosouzapw/OmniRoute/pull/11574)) — thanks @ntdatt812 +- **fix(check):** the `known-symbols` executor conformance gate awaits the now-async `getExecutor()`, so it stops reporting all 142 registered aliases as dead symbols and can detect a lazy import that fails ([#11582](https://github.com/diegosouzapw/OmniRoute/pull/11582)) — thanks @ntdatt812 +- **fix(pack):** register `bin/cli/utils/volatileEnvPath.mjs` in `PACK_ARTIFACT_REQUIRED_PATHS`, so the prune can no longer silently drop a module the published CLI imports on every boot ([#11588](https://github.com/diegosouzapw/OmniRoute/pull/11588)) — thanks @ntdatt812 +- **i18n(vi):** translate the five reliability-column strings on the free-provider rankings page, which rendered as `__MISSING__:` markers in the Vietnamese UI ([#11589](https://github.com/diegosouzapw/OmniRoute/pull/11589)) — thanks @ntdatt812 +- **resilience:** restore the expired-connection retry-budget probe in the token-health sweep — the `!isGitHubAccessTokenOnlyConnection` carve-out reintroduced by #11608 contradicted the boundary pinned by #11592, so a GitHub connection parked at `expired` with retry budget remaining was never probed and could never self-heal ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)). ([#11672](https://github.com/diegosouzapw/OmniRoute/pull/11672)) — thanks @jonlwheat2-gif / @ntdatt812 +- **fix(dashboard):** Provider Stats now displays combo success rates on their native 0–100 percentage scale instead of multiplying them twice ([#11595](https://github.com/diegosouzapw/OmniRoute/pull/11595)) — thanks @pacocartones +- **fix(providers):** route DuckDuckGo Web Chat through the verified `duck.ai` endpoint, refresh its free model catalog, and avoid forwarding unsolved anti-abuse challenges ([#11597](https://github.com/diegosouzapw/OmniRoute/pull/11597)) — thanks @solstxce +- **fix(ui):** Console log Refresh and Copy controls now expose localized accessible names, keep copy actions visible on keyboard focus, and announce copy completion safely ([#11599](https://github.com/diegosouzapw/OmniRoute/pull/11599)) — thanks @pacocartones +- Dropped the stale-`.eslintcache` `restore-keys` fallback from both "Restore ESLint file cache" steps in `ci.yml`, so the blocking `Lint` job can no longer be served per-file verdicts computed under a different lint config, suppressions file or lockfile. `quality.yml` had already dropped it in #11963; `ci.yml` — the workflow that actually gates PRs — had not (#11600). ([#11996](https://github.com/diegosouzapw/OmniRoute/pull/11996)) +- fix(ci): pass `--pass-on-unpruned-suppressions` in `run-eslint-json.mjs` so the CI Lint job no longer fails when a suppression is merely orphaned by a genuine fix, mirroring the identical fix already in `validate-release-green.mjs` (#11600) ([#11983](https://github.com/diegosouzapw/OmniRoute/pull/11983)) — thanks @hartmark +- fix(handoff): enforce provider allowlist for universal handoff (#11602) — universal handoff now skips summarization when the selected summary model's provider is not included in the configured provider allowlist. ([#11619](https://github.com/diegosouzapw/OmniRoute/pull/11619)) — thanks @vsd2807 +- **fix(dashboard):** Show a stable error state when Search Analytics returns an HTTP error or malformed data ([#11603](https://github.com/diegosouzapw/OmniRoute/pull/11603)) — thanks @pacocartones +- **test(gamification):** pin the aggregate profile level to the XP-derived semantics of #11604 — `getAggregateXp()` now derives `currentLevel` from the summed XP (`calculateLevel(sum)`), not `MAX(stored current_level)`, and the #3484 fixture levels are aligned with the XP curve ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)). ([#11676](https://github.com/diegosouzapw/OmniRoute/pull/11676)) — thanks @jonlwheat2-gif / @pacocartones +- **fix(dashboard):** Keep the Profile level and progress aligned with aggregate XP, including bounded handling for invalid totals ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)) — thanks @pacocartones +- **fix(dashboard):** Prevent locked hidden badges from revealing their icon or opening private badge details before they are earned ([#11605](https://github.com/diegosouzapw/OmniRoute/pull/11605)) — thanks @pacocartones +- **fix(dashboard):** Restore keyboard focus after shared modals close and cancel delayed autofocus during cleanup ([#11607](https://github.com/diegosouzapw/OmniRoute/pull/11607)) — thanks @pacocartones +- **fix(dashboard):** Enable Enter and Space activation for clickable data-table rows without hijacking nested controls ([#11610](https://github.com/diegosouzapw/OmniRoute/pull/11610)) — thanks @pacocartones +- **fix(autoCombo,sse):** vendor-retired catalog ids are dropped from the auto-combo candidate pool and no longer win on leftover `arena_elo` / `user_override` rows; `getModelLifecycleDecision` consults `model-lifecycle.json` (prefix-stripped) so aggregator traffic is not `untracked` for ids the snapshot already knows ([#11625](https://github.com/diegosouzapw/OmniRoute/issues/11625)) ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)) — thanks @MumuTW +- **test(chatcore):** move Codex/Claude combo fixtures off models the lifecycle guard now rejects — `gpt-5.1-codex`/`gpt-5-codex` are vendor-retired (snapshot, #11626) and `claude-3-5-sonnet-20241022` is shut down, so native-passthrough and combo-fallback tests switched to `gpt-5.6-sol` and `claude-sonnet-4.6` ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)). ([#11675](https://github.com/diegosouzapw/OmniRoute/pull/11675)) — thanks @jonlwheat2-gif / @MumuTW +- fix(opencode-plugin): stop a free-tier budget that rounds up across a magnitude from rendering as `1000K`/`1000M` in the model picker — `fmtTokens` chose its unit from the raw token count and then rounded with `toFixed(1)`, so 999,950–999,999 printed as `1000K` rather than `1M` and just under a billion printed as `1000M` rather than `1B` ([#11684](https://github.com/diegosouzapw/OmniRoute/pull/11684)) — thanks @abhisheksharma2411 +- Cap prose-derived Antigravity quota resets at the configured model cooldown maximum while preserving authoritative Retry-After headers and Google RetryInfo hints. (#11695) Thanks @pacocartones. ([#11823](https://github.com/diegosouzapw/OmniRoute/pull/11823)) +- Compare `_rebaseline_*` notes structurally in the ratchet-bank verifier so an object-valued note no longer reports itself as rewritten on an unmodified tree — the nightly `bank-ratchet-shrinks` lane had been aborting on every run since 2026-08-11, leaving 41,672 lines of earned file-size shrinks unbanked (#11702). ([#11892](https://github.com/diegosouzapw/OmniRoute/pull/11892)) — thanks @MumuTW +- **fix(build):** `prepublish.ts` bundles the ChatGPT Web (Codex) MCP bridge through `runBuildTool()` instead of spawning `npx.cmd` raw, fixing the build crash on Node ≥ 20/Windows where `.cmd` shims cannot be spawned without a shell (EINVAL) ([#11704](https://github.com/diegosouzapw/OmniRoute/issues/11704)) ([#11706](https://github.com/diegosouzapw/OmniRoute/pull/11706)) — thanks @jonlwheat2-gif +- fix(codex): keep `parallel_tool_calls:false` on the translated Codex Responses Lite path (#11707) ([#11984](https://github.com/diegosouzapw/OmniRoute/pull/11984)) — thanks @hartmark +- **fix(tests):** the new Gemini Web Images retirement test (#11708) called the now-async `getExecutor()` synchronously, throwing `TypeError: getExecutor(...).getProvider is not a function` instead of asserting Gemini Web chat stays available. Awaited to match the R0.3 executor-registry refactor (#11220). ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(search):** Allow REST and MCP web-fetch requests to select the advertised `anysearch-search` provider explicitly ([#11716](https://github.com/diegosouzapw/OmniRoute/pull/11716)) — thanks @pacocartones +- **fix(providers):** Keep overnight peak-hour protection active after midnight according to the window's configured start day ([#11718](https://github.com/diegosouzapw/OmniRoute/pull/11718)) — thanks @pacocartones +- **fix(providers):** correct all five g4f.space gateway descriptions to document proof-of-work credits or member API keys, variable limits, and the remote prompt boundary instead of promising free keyless access at five requests per minute ([#11738](https://github.com/diegosouzapw/OmniRoute/pull/11738)). +- **fix(db):** `model_capabilities` is created by a migration instead of lazily on the first models.dev sync, so a clean install and an upgraded install converge on the same schema regardless of which features have run ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(ci):** `check:install-upgrade` now fails on an `npm` install truncated by ENOSPC (npm reports it as a warning and still exits 0), authenticates its health probe so the version assertion works against the hardened health payload, frees the clean-install tree before the upgrade phase, and no longer reports a schema divergence computed from a boot that never served ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(routing):** harden session-affinity key derivation against oversized explicit IDs and empty structural-payload collisions, while recognizing Gemini and common text request shapes ([#11746](https://github.com/diegosouzapw/OmniRoute/pull/11746)) — thanks @MumuTW +- **fix(packages/browser-pool):** regenerate `package-lock.json` so the `packages/browser-pool` workspace's locked `playwright`/`@types/node` (and transitives) match its `package.json` specs, fixing cache-only/offline installs (`npm ci --offline`, Nix `buildNpmPackage`) that previously failed with `ENOTCACHED` ([#11747](https://github.com/diegosouzapw/OmniRoute/issues/11747)) — thanks @benjaminkitt ([#11784](https://github.com/diegosouzapw/OmniRoute/pull/11784)) — thanks @geekyNads +- **fix(assets):** Stop distributing 79 provider and CLI logos without verified source/license provenance, keep every provider/tool available through the internal generic icon, and prevent those IDs from falling through to an external icon CDN. ([#11750](https://github.com/diegosouzapw/OmniRoute/pull/11750)) +- **fix(providers):** retire the common `chatgpt-web` / `cgpt-web` provider because its pre-key and proof-of-work provenance could not be cleared ([#11754](https://github.com/diegosouzapw/OmniRoute/pull/11754)) — explicit chat and image requests, slash-prefixed models, persisted aliases, proxy overrides, and combo targets now fail closed with a sanitized HTTP `410` / `PROVIDER_RETIRED` before upstream dispatch. Migration 163 tombstones matching connections and invalidates their active leases without deleting history or widening API-key allowlists. The independent `chatgpt-web-codex` / `cgpt-codex` provider and its MIT-noticed vendored implementation remain available. +- **fix(usage):** quota and usage refresh no longer 409 when an exclusive lease reserves the connection ([#11758](https://github.com/diegosouzapw/OmniRoute/pull/11758)) — thanks @TheDemonTuan +- Keep the embedding registry's vector width and `embedding` type on models when a synced model exists for the same id, so `/v1/models` no longer reports registry-described embedding models widthless or untyped (#11761) — thanks @rahilmavani +- Correct `google/gemini-embedding-001` on the OpenRouter route to 3072 dimensions, the width it returns when `dimensions` is not sent (#11761) — thanks @rahilmavani +- **fix(vision-bridge):** Restrict automatic vision routing and fallbacks to models available in authoritative live provider catalogs, including aliases and effort variants ([#11767](https://github.com/diegosouzapw/OmniRoute/issues/11767)) — thanks @pacocartones ([#11813](https://github.com/diegosouzapw/OmniRoute/pull/11813)) +- **fix(providers):** reclassify Cerebras as a one-time $5 signup credit (payment method required, 30-day validity), not a recurring no-card 1M tokens/day trial ([#11773](https://github.com/diegosouzapw/OmniRoute/issues/11773)) ([#12591](https://github.com/diegosouzapw/OmniRoute/pull/12591)) — thanks @HouMinXi +- fix(cli): stop prepublish from re-rebuilding the already-built ESM-only opencode-plugin dist (#11787) ([#11990](https://github.com/diegosouzapw/OmniRoute/pull/11990)) — thanks @hartmark +- **fix(db):** Fresh installs no longer log a non-fatal `no such table: compression_run_telemetry` warning when retention cleanup runs before the lazily-created telemetry table exists ([#11802](https://github.com/diegosouzapw/OmniRoute/pull/11802)) — thanks @RaviTharuma +- fix(sse): set X-OmniRoute-Selected-Connection-Id on successful combo dispatches so downstream consumers stop falling back to an empty connection id (#11810) ([#11986](https://github.com/diegosouzapw/OmniRoute/pull/11986)) — thanks @hartmark +- **fix(services):** embedded CLIProxyAPI lifecycle checks now use public `/healthz`, while model discovery uses the configured dedicated data-plane API key instead of the management password ([#11811](https://github.com/diegosouzapw/OmniRoute/pull/11811)) — thanks @RaviTharuma +- Fix persisted model steps returning an error in the playground route simulator, and warn when structural steps cannot be simulated. ([#11862](https://github.com/diegosouzapw/OmniRoute/pull/11862)) — thanks @pacocartones +- **fix(providers):** Antigravity's dynamic mitmAlias table no longer routes `gemini-3.7-flash-{high,medium,low}` to a literal tier-suffixed upstream id just because one connected account's own discovery listed it directly — those display ids always resolve through the safe `gemini-3.7-flash-tiered` static alias, so one account's Google-provisioned access no longer 404s every sibling account of the provider ([#11824](https://github.com/diegosouzapw/OmniRoute/issues/11824), [#11651](https://github.com/diegosouzapw/OmniRoute/issues/11651)) ([#11988](https://github.com/diegosouzapw/OmniRoute/pull/11988)) — thanks @hartmark +- **fix(dashboard):** Expose Profile loading, errors, page structure, and XP progress to assistive technologies ([#11838](https://github.com/diegosouzapw/OmniRoute/pull/11838)) — thanks @pacocartones +- **fix(socks):** `Agent.connectTimeout` now bounds both SOCKS handshake and TLS connect, and `family === null` no longer falls back to `fetch-socks` ([#11842](https://github.com/diegosouzapw/OmniRoute/pull/11842)) — thanks @maxmad64bis +- **perf(compression):** OOM mitigations for large payload hashing, memoization, and token estimation ([#11844](https://github.com/diegosouzapw/OmniRoute/pull/11844) — thanks @AndrianBalanescu) +- Excluded Next.js Node File Trace manifests (`*.nft.json`) from the published npm tarball. They are build-time metadata and are never read while serving, but had grown to 668.7 MB — 61% of the package — which pushed the upload past the registry limit and made `npm publish` fail with `413 Payload Too Large`. ([#11864](https://github.com/diegosouzapw/OmniRoute/pull/11864)) +- **fix(cli):** keep the global OmniRoute server `--base-url` separate from provider-node `--endpoint` values in add, update, and validate commands ([#11860](https://github.com/diegosouzapw/OmniRoute/pull/11860)) — thanks @pacocartones +- **fix(config):** Nous Research's `Hermes-4-405B` model now displays as "Hermes 4 405B (Nous Research)" in both the provider registry and the free-model catalog, instead of the mislabelled "Hermes 4 7B" ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#11993](https://github.com/diegosouzapw/OmniRoute/pull/11993)) +- **fix(provider/nous):** inject required user= tag into Nous Research inference requests to resolve upstream 400 "missing tags" error ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#12044](https://github.com/diegosouzapw/OmniRoute/pull/12044)) +- **fix(build):** `npm run build` now fails in one second with a named package and a copy-pasteable fix when npm silently drops an externalised optional native dependency, instead of dying four minutes in with `Module not found: Can't resolve 'better-sqlite3'` ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- **fix(install):** `postinstall` no longer throws `ReferenceError: isAndroid is not defined` — failing the whole `npm install` — when the `better-sqlite3` rebuild fallback times out; the manual-fix guidance is reachable again ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- Fixed the Alibaba free-tier allowlist test that went red on its own once the shipped catalog's `validUntil` (2026-08-27) passed, leaving every PR and `main` with a failing `Unit Tests (1/8)`. The test now builds its own packs with dates it controls, and covers the expired-pack fallback that production has actually been serving. ([#11867](https://github.com/diegosouzapw/OmniRoute/pull/11867)) +- Split the npm registry upload into its own GitHub-hosted job. npm refuses `--provenance` from a self-hosted runner (`422 ... Only "github-hosted" runners are supported`), which blocked the v3.8.50 publish; the heavy verification cannot move to a hosted runner, so it now hands the proven tarball over instead. ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(ui):** the shared `DataTable` loading state no longer reads its decorative ⏳ glyph out to assistive technology, and now carries the same `role="status"` / `aria-live="polite"` / `aria-busy="true"` semantics as `PageLoading` ([#11869](https://github.com/diegosouzapw/OmniRoute/pull/11869)) — thanks @pacocartones +- **fix(api):** Bound the `timeout` accepted by `POST /api/providers/[id]/login` to the same 15s–600s window the Adobe Firefly and Conol browser-login paths already enforce, so a request can no longer pin a headful browser and the single active-login slot with an arbitrarily large poll budget. ([#11871](https://github.com/diegosouzapw/OmniRoute/pull/11871)) — thanks @pacocartones +- **fix(api):** Stop the tunnel and MITM routes from returning raw child-process error text, which disclosed host paths, binary install locations and Tailscale `tskey-*` credentials in a body some of these routes serve to non-loopback callers, and return a real 400 instead of a framework 500 when a tunnel request body fails validation. ([#11872](https://github.com/diegosouzapw/OmniRoute/pull/11872)) — thanks @pacocartones +- **fix(open-sse):** stream TTFT/ITL are sampled from a monotonic clock (`performance.now`) instead of `Date.now`, so an NTP correction or wall-clock jump can no longer inflate a genuine TTFT or produce a negative one that the `ttft >= 0` guard silently drops — protecting the router's OTel/EWMA/`usage_history`/speed-ranking signals ([#11873](https://github.com/diegosouzapw/OmniRoute/pull/11873)) — thanks @pacocartones +- **fix(providers):** xAI `reasoning_effort: "xhigh"` now reaches grok-4.6+ instead of being silently clamped to `"high"` ([#11879](https://github.com/diegosouzapw/OmniRoute/pull/11879)) — thanks @NoxzRCW +- **fix(dashboard):** the providers page no longer crashes into the error boundary when a provider id collides with an `Object.prototype` member (`constructor`, `__proto__`); icon lookups are own-property guarded ([#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)) — thanks @NoxzRCW +- **fix(skills):** injected skill tools declared in shorthand (`{"content": "string"}`) now forward valid JSON Schema, unblocking providers that validate tool schemas strictly such as Zhipu GLM on the Console Go tier ([#11881](https://github.com/diegosouzapw/OmniRoute/pull/11881)) — thanks @NoxzRCW +- **fix(api):** `POST /api/playground/simulate-route` now surfaces `combo-ref` and `provider-wildcard` persisted combo steps with a specific warning (naming the referenced combo, or the unresolved `provider/model` wildcard) instead of folding them into a generic "unsupported step" count; a `provider-wildcard` step is also now included as an unresolved target so the operator can see it is in the route (ported from [#11882](https://github.com/diegosouzapw/OmniRoute/pull/11882) — thanks @NoxzRCW). ([#11930](https://github.com/diegosouzapw/OmniRoute/pull/11930)) +- **fix(translator):** the streaming OpenAI→Claude translator keeps upstream usage, including prompt-cache tokens, when it arrives on a trailing `choices: []` chunk (Fireworks and any upstream using `stream_options.include_usage`) ([#11883](https://github.com/diegosouzapw/OmniRoute/pull/11883)) — thanks @NoxzRCW +- **fix(cli):** `omniroute update --apply` now tells you explicitly whether a running server was detected and, if so, that you must run `omniroute restart` to apply the update — it never restarted anything and previously implied the update was already live once files were installed. The dashboard's npm-mode Update flow (`/api/system/version`) now tries OmniRoute's own PID-file-managed supervisor before falling back to pm2, and reports an honest "restart required" step instead of a silent pm2-only "skipped" that read like a completed update. The server-side latest-version lookup backing the dashboard's update banner also gained `--prefer-online`, closing the same stale-npm-cache class already fixed in the CLI's own copy for #4376 ([#11885](https://github.com/diegosouzapw/OmniRoute/issues/11885)). ([#12005](https://github.com/diegosouzapw/OmniRoute/pull/12005)) +- **fix(resilience):** clear persisted LKGP pins when a target suffers connection/provider exhaustion or is skipped before dispatch due to cooldown/exhaustion/unavailability, preventing subsequent requests from repeatedly prioritizing known-dead providers ([#11911](https://github.com/diegosouzapw/OmniRoute/issues/11911)). ([#12013](https://github.com/diegosouzapw/OmniRoute/pull/12013)) — thanks @HouMinXi +- **fix(sse):** the OpenAI→Claude stream translator now defers the terminal `message_delta`/`message_stop` emission until the real usage block has arrived (or a genuine end-of-stream flush forces it) instead of emitting it immediately on `finish_reason` — previously, when the trailing usage-only chunk (`{"choices":[],"usage":{...}}`) arrived *after* the `finish_reason` chunk (the normal order for Fireworks/vLLM/Together and other `stream_options.include_usage` upstreams), the client-visible `message_delta` still carried stale/zero usage even though `state.usage` was internally corrected too late to matter (ported from [#11915](https://github.com/diegosouzapw/OmniRoute/pull/11915) — thanks @HouMinXi). ([#11933](https://github.com/diegosouzapw/OmniRoute/pull/11933)) +- **fix(sse):** `stripStore()` now forces `store=false` for stateless OpenAI-compatible Responses-API targets (unless the connection has explicitly opted in via `providerSpecificData.openaiStoreEnabled`), instead of only handling the `openai`/`agentrouter` cases — a client-supplied `store` value previously passed through untouched to backends that don't actually persist responses server-side ([#11916](https://github.com/diegosouzapw/OmniRoute/pull/11916) — thanks @HouMinXi). +- **fix(models):** custom provider-node models (synced, custom, and alias-backed) now appear under their configured prefix in the unified catalog when the operator's model-id prefix mode is canonical, instead of being dropped whenever alias-inclusion was otherwise disabled ([#11918](https://github.com/diegosouzapw/OmniRoute/pull/11918) — thanks @HouMinXi). +- **fix(models):** the unified model catalog now suppresses stale static registry models (including effort-tier variants) for any provider whose active connection has an authoritative live synced catalog, not only providers already using exclusive-synced-listing — a connection with `providerUsesAuthoritativeLiveCatalog` previously kept serving both the live-synced models and the stale static rows side by side ([#11919](https://github.com/diegosouzapw/OmniRoute/pull/11919) — thanks @HouMinXi). +- **fix(ui):** `ProviderIcon`'s three remaining unguarded lookups (`PROVIDER_ICON_ALIASES`, `LOCAL_SVG_ALIASES`, `THEMED_SVGS`) now use `Object.hasOwn()` own-property checks — a provider id such as `constructor` or `__proto__` previously resolved through the prototype chain instead of falling through to the unknown-provider CDN fallback (`getLobeProviderIcon()` itself was already guarded by [#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)); `ProviderPageHeader`'s `color` field is also now optional, matching the rest of the component's defensive typing (ported from [#11920](https://github.com/diegosouzapw/OmniRoute/pull/11920) — thanks @HouMinXi). ([#11935](https://github.com/diegosouzapw/OmniRoute/pull/11935)) — thanks @NoxzRCW +- fix(ollama): preserve multi-byte UTF-8 content split across stream chunks in the Ollama NDJSON transform, which previously corrupted CJK/emoji into U+FFFD (#11921) — thanks @pacocartones +- **fix(providers):** OrcaRouter chat requests now target `/v1/chat/completions` instead of the bare `/v1` API root, fixing the upstream `404 Invalid URL (POST /v1)` ([#11923](https://github.com/diegosouzapw/OmniRoute/pull/11923)). — thanks @echel0nn +- **fix(plugins):** deliver the `onStreamComplete` event to disk-installed plugins. The event shipped in v3.8.50 (#9669) was emitted internally but had no plugin-facing wiring, so no plugin could ever subscribe: the manifest schema silently dropped `hooks.onStreamComplete`, and the loader/manager only knew the seven legacy hooks. `onStreamComplete` is now a declarable manifest hook, wired through the loader and registered by the manager like the other hooks, and its payload carries a `requestId` so consumers can correlate the stream-completion event with the originating request ([#11934](https://github.com/diegosouzapw/OmniRoute/pull/11934)) — thanks @amaleta / @pacocartones +- **fix(db):** the Qdrant embedding-model dropdown now lists local/self-hosted providers (Ollama, LM Studio, vLLM, etc.) — an active connection is treated as "configured" when the provider allows an optional API key, not only when it has a real key or OAuth, so a running local embedding provider is no longer hidden from the picker ([#11949](https://github.com/diegosouzapw/OmniRoute/issues/11949)) ([#11995](https://github.com/diegosouzapw/OmniRoute/pull/11995)) +- **fix(providers):** Vertex AI Anthropic partner-model discovery now calls the Model Garden `v1beta1` publisher list (`/v1beta1/publishers/anthropic/models`, global) and parses its `publisherModels` envelope, so Claude models auto-synced from Vertex populate the active live catalog and route at request time instead of returning `Model '' is not available in the active live catalog` ([#11991](https://github.com/diegosouzapw/OmniRoute/issues/11991)) — thanks @fabioluissilva ([#11998](https://github.com/diegosouzapw/OmniRoute/pull/11998)) +- **fix(cli):** support `--base-url` alongside `--endpoint` in `omniroute nodes add`, `update`, and `validate` subcommands to prevent global `--base-url` shadowing issues ([#11999](https://github.com/diegosouzapw/OmniRoute/issues/11999)). ([#12033](https://github.com/diegosouzapw/OmniRoute/pull/12033)) — thanks @HouMinXi +- **fix(providers):** `cloudflare-ai` no longer refuses image content parts for every Workers AI model ([#12002](https://github.com/diegosouzapw/OmniRoute/pull/12002)) — the plain-string `content` requirement behind #2539 is carried by the _model_ schema, not by the `/ai/v1/chat/completions` endpoint (measured: an all-text part array returns 200 on `@cf/mistralai/mistral-small-3.1-24b-instruct`, `@cf/meta/llama-4-scout-17b-16e-instruct` and `@cf/meta/llama-3.3-70b-instruct-fp8-fast`, and 400 on the text-only `@cf/qwen/qwen2.5-coder-32b-instruct`). `transformRequest()` flattened every array and threw on the first non-text part (#6390), so image input was refused for vision-capable Cloudflare models that accept it. All-text arrays are still flattened — the one shape every model accepts — while an array carrying a non-text part is passed through untouched, so the attachment is still never silently dropped. Regression guards: `tests/unit/cloudflare-ai-image-parts-6390.test.ts`. — thanks @davidlinfr +- **fix(resilience):** decouple the limiter-managed execution backstop from the queue-wait budget — new `requestQueue.executionMaxWaitMs` (env `RATE_LIMIT_EXECUTION_MAX_WAIT_MS`, default 600000 = 10 min) now feeds Bottleneck's post-dispatch `expiration`, while `requestQueue.maxWaitMs` keeps its documented queue-wait semantics. Previously the queue-wait budget doubled as the execution expiration, so legitimate long-running calls on non-incremental gateways (whole generation buffered before the first upstream byte, e.g. Console Go / Command Code tiers serving GLM models) were killed mid-flight at the queue budget with a false 504 `RATE_LIMIT_EXECUTION_TIMEOUT` — the local limiter undercut the provider-aware upstream fetch-start timeouts. The surfaced 504 message now names `requestQueue.executionMaxWaitMs`; the error keeps the #4165 guarantees (disclaims an upstream timeout, preserves the Bottleneck error as `cause`, branded code + trusted provenance, classified request-scoped so combo falls back). A real queue-wait bound (the `Promise.race` around `limiter.schedule()` sketched in #9533) remains future work. (#12025) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **Call logs:** keep the `error` field when an artifact exceeds the storage cap, instead of replacing it with the omission marker. The error is the only field that says *why* a request failed and is typically ~90 bytes next to the multi-hundred-KB bodies that trip the cap, so dropping it left a size-limited row undiagnosable — a provider outage, a local timeout and an upstream 400 all rendered identically. It is now preserved at every fallback stage, truncated to 4KB if it is itself large ([#12026](https://github.com/diegosouzapw/OmniRoute/issues/12026)). ([#12095](https://github.com/diegosouzapw/OmniRoute/pull/12095)) — thanks @ntdat812 +- **fix(diagnostics):** preserve the error field (truncated to 4KB with a `[truncated: …]` suffix) in every call-log artifact size-limit fallback stage. Previously the minimal fallback replaced the error with `[omitted: call log artifact size limit exceeded]`, so an oversized artifact row showed nothing about WHY the request failed — e.g. 91 of 847 opencode-go 504 rows on one production instance were undiagnosable from the dashboard. Oversized request/response bodies are still omitted exactly as before; the error cap is independent of the payload sizes that tripped the fallback. (#12026) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **fix(sse):** OpenAI Responses clients that declare the native `web_search` tool now receive a spec-shaped `web_search_call` output item with `action.sources` alongside the preserved function-call round-trip, so search results executed through OmniRoute's own search backend are consumable by standard Responses clients (Codex, pi-web-access, …). ([#12031](https://github.com/diegosouzapw/OmniRoute/pull/12031)) — thanks @watchingdogs +- **fix(cli):** use in-thread alias resolver hooks on modern runtimes to avoid deprecation noise and improve Node.js forward compatibility ([#12073](https://github.com/diegosouzapw/OmniRoute/issues/12073)). ([#12083](https://github.com/diegosouzapw/OmniRoute/pull/12083)) — thanks @xiaoyaner0201 +- **fix(combo):** an operator-set **Agent Features → Context length** on a combo is now honored at request time. The value was persisted and advertised through `/v1/models`, but `resolveComboContextLimit()` never consulted it — so a multi-target combo whose members carry no per-model window fell through to the provider's generic `defaultContextLength` (openrouter 128000, command-code 200000) and rejected large requests with `Input exceeds context window … limit 128000` despite the combo being explicitly sized much larger. An identical single-target combo worked, because it collapses to its concrete target before the guard runs. Invalid values (0/negative/NaN/Infinity) are ignored, so the existing target → combo-min → fallback order is unchanged. ([#12090](https://github.com/diegosouzapw/OmniRoute/pull/12090)) — thanks @adivekar-utexas +- **fix(sse):** passthrough streams now estimate usage on finish when upstream closes without usage even with `stream_options.include_usage` — avoids `0 tokens / 0%` for providers that stay silent (and correctly handles trailing empty-choices usage) ([#12151](https://github.com/diegosouzapw/OmniRoute/pull/12151)) — thanks @maxmad64bis +- **fix(combos):** clearing an agent feature in the combos editor now persists — unchecking context cache protection, or emptying the system message or tool filter, sends an explicit `null` instead of dropping the field from the `PUT` body, which the update merge read as "leave unchanged" ([#12177](https://github.com/diegosouzapw/OmniRoute/pull/12177)) — thanks @foreveryh / @ntdatt812 +- **fix(memory):** self-hosted embedding endpoints now vectorize — the vector width is measured from the first embedding that comes back instead of being read from a registry that cannot describe them, so `vec_memories` is created and memories stop piling up unvectorized behind a green health check ([#12180](https://github.com/diegosouzapw/OmniRoute/pull/12180)) — thanks @kanade-hoshino / @ntdatt812 +- **fix(translator):** the leading `system` message now reaches Responses-API upstreams when its `content` is a content-part array — it was read as `typeof content === "string" ? content : ""`, so a prompt-caching client (Anthropic `cache_control`, the shape LiteLLM and the Anthropic SDK emit) had its entire system prompt replaced by an empty `instructions`. The request was still accepted with a normal `prompt_tokens` count, so the model answered with no instructions and nothing in the response said they were missing. Mid-conversation system turns already handled the array shape ([#7056](https://github.com/diegosouzapw/OmniRoute/pull/7056)); only the first one did not ([#12206](https://github.com/diegosouzapw/OmniRoute/issues/12206)). Regression guard: `tests/unit/translator-openai-responses-system-content-parts.test.ts`. ([#12207](https://github.com/diegosouzapw/OmniRoute/pull/12207)) — thanks @Gorillaz322 +- **fix(free-tier):** `/api/free-tier/summary` no longer computes its totals from a Radar feed built before the catalog the running release ships. When the cached feed is older — or carries no build date at all — the route answers from the shipped catalog, resolved through the operator's local model state so disabled and tombstoned models stay out of the numbers ([#12215](https://github.com/diegosouzapw/OmniRoute/pull/12215)). — thanks @maxmad64bis +- **fix(oauth):** Keep a Claude personal workspace and a Team organization as separate connections — they share the same email and `accountUUID`, so the email-only OAuth dedup let the second login overwrite the first account's tokens; `organizationUUID` now disambiguates them, the way `workspaceId` does for Codex ([#12222](https://github.com/diegosouzapw/OmniRoute/pull/12222)) — thanks @davidebaraldo +- **fix(resilience):** a 402 on a single paid model of a passthrough/gateway provider (e.g. `kilo-gateway`, `ollama-cloud`) no longer terminalizes the whole connection with a never-auto-recovered `credits_exhausted` status — only the paid model is locked out, so free models on the same key keep serving. 402 variant of [#3027](https://github.com/diegosouzapw/OmniRoute/issues/3027). Single-credential providers are unaffected — a 402 there is still treated as the key being genuinely out of credit ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239) / [#10616](https://github.com/diegosouzapw/OmniRoute/issues/10616)) ([#12242](https://github.com/diegosouzapw/OmniRoute/issues/12242)) — thanks @brick30llc-ctrl ([#12266](https://github.com/diegosouzapw/OmniRoute/pull/12266)) — thanks @geekyNads +- **fix(sse):** trust `finish_reason: "length"`/`"max_tokens"` over the reasoning-consumed-token ratio in response quality validation, so a reasoning model truncated below the old 90% threshold correctly fails and retries instead of returning empty content as a silent "success" ([#12262](https://github.com/diegosouzapw/OmniRoute/pull/12262)) — thanks @brick30llc-ctrl +- **fix(settings):** `PUT /api/settings/cache-config` now persists `alwaysPreserveClientCache` to the flat general settings the runtime cache-control policy actually reads; previously the value landed in the databaseSettings "cache" section and was silently ignored, so the endpoint had no effect on `cache_control` passthrough ([#12304](https://github.com/diegosouzapw/OmniRoute/pull/12304)) — thanks @davidebaraldo +- **fix(grok-cli):** treat omitted SuperGrokPro `creditUsagePercent` as 0% used so Provider Limits still renders a weekly bar (proto3 zero-elision) ([#12312](https://github.com/diegosouzapw/OmniRoute/pull/12312)) — thanks @HouMinXi +- **fix(combo):** Expose the two Auto-Combo scoring factors nobody could set — the combo validation schema and the dashboard weight sliders both declared 13 of the scorer's 15 factors, so `connectionDensity` (spreads load across a provider's connections) and `quality` were dropped on save and offered nowhere. The sliders also shipped their own default table that differed from the engine's on every non-zero factor and summed to 1.05, so the percentages shown next to them added up to 105%. Both lists now match `DEFAULT_WEIGHTS`, and a test keeps them there. Note that a combo whose stored `weights` omitted the two keys was effectively running with them at zero and the other thirteen renormalized upward; it now runs with the engine's intended distribution, so its routing does shift ([#12314](https://github.com/diegosouzapw/OmniRoute/pull/12314)) — thanks @maxmad64bis +- **fix(docs):** The free-tier reference no longer says its numbers come "confidence tagged per row" — no catalog entry carries a confidence tag and the API serves none, so every figure on that page is an estimate of the same, unstated quality. The page now states what an entry does vouch for: an independently documented hard stop (set by hand with the source in a comment, never defaulted to `true`) and a prompt-training disclosure, both with live counts the `check:docs-counts` gate keeps honest ([#12318](https://github.com/diegosouzapw/OmniRoute/pull/12318)) — thanks @maxmad64bis +- **fix(usage):** `adobe-firefly` and `firefly` have had a working usage fetcher since Adobe Firefly landed, but neither was ever added to the registration list, so the provider-plugin manifest, `genericQuotaFetcher` and the free-access quota cache all reported them as having no usage support — while `USAGE_SUPPORTED_PROVIDERS` said the opposite. Both are now declared, which also means their credit balance is fetched like any other declared provider's: `registerGenericQuotaFetchers` now registers a generic quota fetcher for them, and `resolveFreeAccessState` no longer returns early. A test holds the registration list to the dispatcher's switch in both directions, which is what the module's own docstring already asked for in prose ([#12321](https://github.com/diegosouzapw/OmniRoute/pull/12321)) — thanks @maxmad64bis +- **fix(quota):** drop the generic quota cache (agy / Antigravity / Claude OAuth) on an upstream 429 so reset-aware scoring does not keep a 60s stale snapshot, and force-refresh the next usage fetch so inner provider caches cannot recache the same window ([#12325](https://github.com/diegosouzapw/OmniRoute/pull/12325)) — thanks @HouMinXi +- **fix(combos):** deleting a combo now clears its persisted LKGP pins instead of leaving unreachable `key_value` rows behind ([#12326](https://github.com/diegosouzapw/OmniRoute/issues/12326)) ([#12425](https://github.com/diegosouzapw/OmniRoute/pull/12425)) — thanks @KrzysiekSko +- **fix(sse):** Keep ZWNJ (U+200C) and ZWJ (U+200D) in assistant text, reasoning and tool-call arguments — Persian/Kurdish half-space (`ارائه‌دهنده`), Arabic/Indic shaping and emoji sequences no longer lose them; the response de-obfuscation now removes joiners only between ASCII word characters, where the request side inserts them ([#12186](https://github.com/diegosouzapw/OmniRoute/issues/12186)) — thanks @rezjalibd ([#12359](https://github.com/diegosouzapw/OmniRoute/pull/12359)) — thanks @pacocartones +- **fix(resilience):** count resolved upstream 5xx results against the provider circuit breaker on the chat path — `CircuitBreaker.execute()` no longer reads a resolved `{ success: false, status: 5xx }` as a success that cancels the call-site failure, so a provider answering 503s now trips its breaker instead of staying `CLOSED` at `failureCount: 1`; single-model and combo dispatches are each accounted exactly once ([#12254](https://github.com/diegosouzapw/OmniRoute/issues/12254)) ([#12360](https://github.com/diegosouzapw/OmniRoute/pull/12360)) — thanks @pacocartones +- **fix(providers):** resolve the Codex quota auto-ping model from the live provider catalog and lifecycle registry instead of the retired `gpt-5.1-codex-mini`, and pause the ping with one actionable warning when no selectable Codex model exists rather than retrying a shut-down id every cooldown window ([#11905](https://github.com/diegosouzapw/OmniRoute/issues/11905)) ([#12361](https://github.com/diegosouzapw/OmniRoute/pull/12361)) — thanks @pacocartones +- **fix(api):** keep the `{created, data}` wrapper on combo-routed `/v1/images/generations` responses and default Codex image results to `b64_json` on both `/v1/images/generations` and `/v1/images/edits` so Codex CLI's built-in `image_gen` can decode them ([#12268](https://github.com/diegosouzapw/OmniRoute/issues/12268)) ([#12362](https://github.com/diegosouzapw/OmniRoute/pull/12362)) — thanks @pacocartones +- **fix(sse):** Name the shadowed custom provider node when a built-in provider id/alias (e.g. `openference` → `of`) reserves the prefix of an existing OpenAI/Anthropic-compatible node, so the runtime `No active credentials for provider: ` error explains that the prefix routed to the built-in and never reached the node's healthy connections, instead of contradicting the dashboard ([#11943](https://github.com/diegosouzapw/OmniRoute/issues/11943)) — thanks @morpheus9393 ([#12365](https://github.com/diegosouzapw/OmniRoute/pull/12365)) — thanks @pacocartones +- **fix(i18n):** wrap `ccOnboardingKeyPlaceholder` in ICU single quotes across all 43 locales so angle brackets render literally instead of being parsed as rich-text tags, which crashed the Claude Code onboarding block with `INVALID_MESSAGE: INVALID_TAG` ([#12302](https://github.com/diegosouzapw/OmniRoute/issues/12302)) ([#12369](https://github.com/diegosouzapw/OmniRoute/pull/12369)) — thanks @gonisulaimann +- **fix(models):** publish `effort_tiers` on Kimi K3's synced base-model entries (`kmca/k3`, `kmca/k3-256k`) so catalog-only clients (OpenCode, plain SDK pickers) can see and select the reasoning tiers (`low`/`high`/`max`) the synced metadata already carried — the `isSkippedEffortProvider` gate no longer suppresses tier visibility on those base entries, while synthetic `-` variant generation stays prevented and Codex/GLM base models remain excluded unchanged ([#12299](https://github.com/diegosouzapw/OmniRoute/issues/12299)) ([#12371](https://github.com/diegosouzapw/OmniRoute/pull/12371)) — thanks @gonisulaimann +- **fix(guardrails):** keep `auto`/`auto/*` virtual combos exempt from the Vision Bridge `fixedModel` credential guard so a combo target is passed through instead of silently falling back to global auto-selection ([#12237](https://github.com/diegosouzapw/OmniRoute/issues/12237)) ([#12373](https://github.com/diegosouzapw/OmniRoute/pull/12373)) — thanks @pacocartones +- **fix(combo):** capability-filter exhaustion caused by `max_tokens` above every target's known output limit now reports that reason (requested `max_tokens` vs the pool's highest known ceiling) instead of the unrelated "supports structured output" message ([#12229](https://github.com/diegosouzapw/OmniRoute/issues/12229)) — thanks @DW-MediaLab ([#12374](https://github.com/diegosouzapw/OmniRoute/pull/12374)) — thanks @pacocartones +- **fix(auth):** the `least-used` account strategy now prefers accounts without backoff before falling back to oldest `lastUsedAt`, the same tie-break `round-robin` already applies, so a failover no longer lands on a just-rate-limited account for a single request ([#12279](https://github.com/diegosouzapw/OmniRoute/issues/12279)) — thanks @tenshiak ([#12375](https://github.com/diegosouzapw/OmniRoute/pull/12375)) — thanks @pacocartones +- **fix(docker):** the `chatgpt-web-codex-browser` image now finds the Chrome binary under `chrome-linux64/` (Chrome for Testing layout in `playwright:v1.62.0-noble`) as well as the legacy `chrome-linux/`, so the container no longer crash-loops with `exec: --headless=new: not found` ([#12024](https://github.com/diegosouzapw/OmniRoute/issues/12024)) ([#12376](https://github.com/diegosouzapw/OmniRoute/pull/12376)) — thanks @pacocartones +- **fix(providers):** declare `groq/compound` and `allam-2-7b` as non-reasoning models in the curated Groq registry so `reasoning_effort` / `output_config.effort` / `thinking` from Claude Code are stripped instead of forwarded, which Groq rejected with HTTP 400 ([#12134](https://github.com/diegosouzapw/OmniRoute/issues/12134)) ([#12379](https://github.com/diegosouzapw/OmniRoute/pull/12379)) — thanks @pacocartones +- **fix(executors):** `OpencodeExecutor` no longer forces a direct connection when the connection has a proxy assigned in Proxy Management but no per-account proxies: the single-account fast path used to wrap the upstream dispatch in the direct-egress sentinel, discarding the ambient proxy context the chat handler had pinned from `proxy_assignments`, so API-key `opencode`/`opencode-go` connections egressed from the host IP (and hit geoblocks) despite the assignment. The direct pin is now applied only when no ambient proxy context exists ([#11894](https://github.com/diegosouzapw/OmniRoute/issues/11894) — thanks @hizzt) ([#12380](https://github.com/diegosouzapw/OmniRoute/pull/12380)) — thanks @pacocartones +- **fix(api):** `GET /v1/models` with `MODELS_CATALOG_PREFIX_MODE=canonical` (or `?prefix=canonical`) now lists providers whose registry alias is undefined or equal to their own id (Antigravity, Antigravity CLI and other self-aliased built-ins) — their single `provider/model` id was dropped by the alias/canonical duplicate guard in the static, synced, custom and alias-backed catalog loops ([#12058](https://github.com/diegosouzapw/OmniRoute/issues/12058)) — thanks @cheynetom ([#12381](https://github.com/diegosouzapw/OmniRoute/pull/12381)) — thanks @pacocartones +- **fix(translator):** Drop replayed `thinking` blocks that carry no signature (the shape produced from cross-provider `reasoning_content`) instead of stamping the default Claude signature on them, which Anthropic rejected with `400 Invalid signature in thinking block` on the next turn served by an Anthropic rung ([#12105](https://github.com/diegosouzapw/OmniRoute/issues/12105)) — thanks @atescivitci-cmd ([#12386](https://github.com/diegosouzapw/OmniRoute/pull/12386)) — thanks @pacocartones +- **fix(cli):** Resolve Bun's `--preload` polyfill path against the package root instead of `dist/`, so `omniroute` installed with `bun install -g` no longer crashes at startup with `error: preload not found …/dist/open-sse/utils/setupPolyfill.ts` ([#11980](https://github.com/diegosouzapw/OmniRoute/issues/11980)) — thanks @joglomedia ([#12387](https://github.com/diegosouzapw/OmniRoute/pull/12387)) — thanks @pacocartones +- **fix(providers):** `gemini-business` now publishes its model catalog — `/v1/models` and `/v1/providers/gemini-business/models` list the 12 enterprise Gemini ids the executor understands instead of returning an empty list (#12107) ([#12389](https://github.com/diegosouzapw/OmniRoute/pull/12389)) — thanks @pacocartones +- **fix(db):** install `busy_timeout` before the SQLite connection's first statement so a process opening the database while another one closes its WAL connection waits out the transient EXCLUSIVE lock instead of dying with `database is locked`, and recognise the drivers' real BUSY/PROTOCOL/IOERR errors as transient in the corruption probe so the same lock no longer renames the database away as corrupt; deflakes `cross-process contenders never both acquire the same connection` (#12394 — thanks @pacocartones) +- **fix(chat-admission):** derive the `chat_admission_busy` 503 `Retry-After` from observed heavyweight-lease occupancy — the larger of the exhausted `OMNIROUTE_CHAT_ADMISSION_QUEUE_MS` window and the time since capacity last turned over, capped at 60 s — instead of a fixed 1 s (structural) / 2 s (byte-stage) hint that invited Codex/agent fan-out clients to re-send ~1 MiB `/v1/responses` bodies every second into a gate held for the whole SSE lifetime; an idle gate keeps the historical floors ([#12135](https://github.com/diegosouzapw/OmniRoute/issues/12135)) (#12395 — thanks @pacocartones) +- **fix(api-manager):** the API key permissions modal no longer silently drops `allowedCombos` entries its Combo picker cannot render — routing-rule names such as `rt-*`, which the backend already honours — when "All" is clicked and the key is switched back to "Restrict"; those entries now survive the toggle, are listed read-only under the combo list so the count and the list agree, and are saved back verbatim instead of persisting `[]` (deny-all) (#12397 — thanks @pacocartones) +- **fix(catalog):** write the NUL separator of the catalog connection memo key, the provider serviceKind memo key, the Video Bridge promotion group key and a JSON-exactness test fixture as the `\u0000` escape instead of a raw byte — same runtime value, but the raw byte made git, GitHub and ripgrep treat those files as binary (hidden PR diffs, silently skipped searches); a guard test now keeps raw NUL bytes out of `src/`, `open-sse/` and `tests/` (#12403 — thanks @pacocartones) +- **fix(providers):** add `CLAUDE_CODE_CLIENT_VERSION` and `GITHUB_COPILOT_CLI_VERSION` env overrides so Anthropic/Copilot client-version gates can be unblocked without a rebuild ([#12417](https://github.com/diegosouzapw/OmniRoute/issues/12417)) ([#12632](https://github.com/diegosouzapw/OmniRoute/pull/12632)) — thanks @HouMinXi +- **fix(db):** back-fill `last_ping_at` and `last_pinged_reset_key` on `provider_connections` during schema reconciliation so divergent lineages that skipped `123_quota_auto_ping` still accept quota auto-ping writes ([#12470](https://github.com/diegosouzapw/OmniRoute/pull/12470) — thanks @KooshaPari) +- **fix(ci):** document MIT exceptions for `@eloqnt/{config,format-json,format-po}` (next-intl transitive; locked tarballs omit `license`) and keep the A2A lifecycle vitest off the real SQLite persistence seam ([#12581](https://github.com/diegosouzapw/OmniRoute/issues/12581)) ([#12595](https://github.com/diegosouzapw/OmniRoute/pull/12595)) +- OpenCode plugin `/v1/models` catalog fetch now waits 30s by default and attaches HTTP `statusCode` on 401/5xx so host fallback plugins can hop instead of seeing an untyped AbortError/UnknownError. ([#12607](https://github.com/diegosouzapw/OmniRoute/pull/12607)) — thanks @RaviTharuma +- **CI:** the OpenAPI security-tier gate now mirrors `isAlwaysProtectedPath()` in full — it also reads `ALWAYS_PROTECTED_API_PATTERNS`, so the pattern-gated credential routes (`/api/providers/{id}/{claude,codex}-auth/{export,apply-local}`, GHSA-5926-2w35-7h4q) no longer report as unannotated. (#12605) +- **fix(api):** GET /v1/models no longer waits forever on a hung coalesced catalog rebuild; cold-path waits are bounded (`CATALOG_BUILD_TIMEOUT_MS`, default 8s) and a last-good 200 is served when the rebuild times out ([#12627](https://github.com/diegosouzapw/OmniRoute/issues/12627)). ([#12628](https://github.com/diegosouzapw/OmniRoute/pull/12628)) — thanks @RaviTharuma +- **fix(cursor):** a non-array `tool_calls` on an assistant message no longer crashes the cursor request translator with a `TypeError`; both loops now require an array ([#12689](https://github.com/diegosouzapw/OmniRoute/issues/12689)) ([#12691](https://github.com/diegosouzapw/OmniRoute/pull/12691)) — thanks @soroush5 +- **fix(chat):** Chat Completions no longer return empty `content` after a server-owned memory or skills tool; the first provider send and account/model recovery now share one pipeline so a follow-up round-trip can fill the reply ([#12696](https://github.com/diegosouzapw/OmniRoute/issues/12696)) — thanks @HouMinXi ([#12867](https://github.com/diegosouzapw/OmniRoute/pull/12867)) +- **fix(dashboard):** batch-deleting provider keys no longer toasts failure after a successful delete when the confirm button's click event is forwarded as `onAfter` ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) — thanks @HouMinXi +- **fix(glm):** drop the extra 16th argument to `createSSETransformStreamWithLogger` that TypeScript rejected (TS2554) and that never reached the TransformStream ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) ([#12770](https://github.com/diegosouzapw/OmniRoute/pull/12770)) — thanks @HouMinXi +- **fix(dashboard):** pass `nodeMap` into Runtime `QuotaGroup` so a quota monitor in error/exhausted/alerting no longer throws `ReferenceError: nodeMap is not defined`. ([#12868](https://github.com/diegosouzapw/OmniRoute/pull/12868)) — thanks @HouMinXi +- Restricted API keys whose `allowedModels` lists a combo name no longer skip every combo member at pre-dispatch (`ALL_TARGETS_SKIPPED`). Inner-target filtering still applies when the allow-list is a provider prefix or `disableNonPublicModels` is on ([#12899](https://github.com/diegosouzapw/OmniRoute/pull/12899)). — thanks @HouMinXi +- **fix(combos):** Effort-suffixed combo members inherit the base model's `model_context_overrides` row so priority order is not inverted on large requests ([#12926](https://github.com/diegosouzapw/OmniRoute/pull/12926)) — thanks @HouMinXi +- **fix(catalog):** Picker-added `customModels` enter the dispatch-time live catalog so combo members and bare inference no longer 400 ([#12934](https://github.com/diegosouzapw/OmniRoute/pull/12934)) — thanks @HouMinXi +- **fix(docker):** re-derive the Docker build's worker-pool memory budget from the MEASURED ~4.5 GB per-process RSS (the issue owner's own VPS dmesg OOM-killer reproduction) instead of the stale 2560 MB/worker inference #11419 shipped, and lower `OMNIROUTE_BUILD_WORKERS` 3 → 2 so 1 parent + 1 page-data worker (2 processes × 4.5 GB = 9 GB) fits the 12.288 GB (75%) budget on a 16 GB GitHub Actions runner — the previous default (1 parent + 2 workers = 13.5 GB) still overcommitted the runner and kept "Publish to Docker Hub" failing with `cannot allocate memory` after #11419 merged (#7518). ([#11663](https://github.com/diegosouzapw/OmniRoute/pull/11663)) +- **fix(electron):** the packaged Windows desktop build now passes the #7592 cold-restart smoke end to end. Five defects were found and fixed on the way: (1) optional-pack staging failed on any machine whose `tar` is GNU tar — it reads the drive letter in an absolute `-f C:\...` archive path as a remote rsh host (`Cannot connect to C:`), so staging now passes a bare filename with `cwd` at the tarball directory and surfaces tar stderr on failure; (2) the Electron `files` allowlist was missing `lib/loginHeaderCapture.js`, so the packaged main process crashed at startup with `Cannot find module './lib/loginHeaderCapture'`; (3) electron-builder ≥26 injects an `!**/node_modules/**` ignore into every extraResources pattern list that no positive filter can override, which silently dropped the staged runtime `node_modules` (including the better-sqlite3 N-API prebuild) from `resources/app` — a fresh v3.8.50 desktop build would have shipped with no native SQLite driver and reproduced the #7592 sql.js fallback on every machine; an `afterPack` hook now restores the staged `node_modules` after packing; (4) the packaged-app smoke harness redirected `USERPROFILE` into its temp DATA_DIR, but Electron resolves the Roaming profile from `%USERPROFILE%\AppData\Roaming\` (USERPROFILE takes precedence over the APPDATA env var) and the path service throws instead of creating it, so `requestSingleInstanceLock()` returned false and the app exited(0) silently before `app.whenReady()` — the harness now pre-creates the derived tree, and `ensureSmokeEnvDirs` is exported and unit-tested; (5) the #7592 driver assertion parsed a `[DB] Driver: ...` line that the server's primary DB path never printed (only the unused `openDatabaseAsync()` did) — `getDbInstance()` now emits the same line on open so the guard can actually assert the native driver. Also: the smoke env-allowlist unit test hardcoded POSIX paths and could only pass on Linux/macOS; expectations are now host-agnostic, with new regression tests for the USERPROFILE-derived tree and `tarPack` under absolute Windows-style paths ([#11443](https://github.com/diegosouzapw/OmniRoute/pull/11443)) — thanks @jonlwheat2-gif +- fix(sse): stop the auto-combo candidates inspector from silently dropping model-locked/cooled-down rows (#9133) ([#11994](https://github.com/diegosouzapw/OmniRoute/pull/11994)) — thanks @hartmark +- **fix(providers):** Perplexity Web no longer turns upstream stream failures into successful assistant text; pre-content failures remain eligible for fallback, partial output ends with a structured sanitized error, and failed sessions are not persisted ([#12465](https://github.com/diegosouzapw/OmniRoute/pull/12465)) +- **fix(video-bridge):** preserve chronological video and transcript fusion, source-frame timestamps, transcript provenance, full contact-sheet intervals, and pre-abort callback isolation ([#11681](https://github.com/diegosouzapw/OmniRoute/pull/11681)) +- **Z.ai Web:** HTTP 200 streams carrying an upstream error now terminate with a structured failure instead of assistant text plus a normal stop, preserving partial output while allowing pre-content combo fallback. ([#12454](https://github.com/diegosouzapw/OmniRoute/pull/12454)) +- Absorb `Error [AbortError]: request_signal_aborted` and DOMException AbortError shapes in the process-level client-abort crash guard so routine client disconnects no longer kill the server (exit code 7). ([#12165](https://github.com/diegosouzapw/OmniRoute/pull/12165)) — thanks @alvinveroy +- **fix(sse):** Treat Adapta Web `type:error` SSE events as sanitized non-stream failures instead of empty HTTP 200 completions. ([#12459](https://github.com/diegosouzapw/OmniRoute/pull/12459)) +- **fix(autoCombo):** layer 3 (`models_dev_tier`) no longer scores retired models as premium, merge cross-provider capability rows deterministically, and inherit tier scores through variant ids ([#11508](https://github.com/diegosouzapw/OmniRoute/issues/11508)). `getModelsDevTierFitness` now vetoes ids present in `config/quality/model-lifecycle.json` with status `retired` before any other signal; `loadModelCapabilities()` aggregates per `model_id` (any-non-null-true for booleans, max for `limit_context`) instead of last-write-wins over SQLite's undefined row order; a capabilities miss retries through `resolveScoresAs()`'s base id and reports `models_dev_tier:inherited`, matching layers 1–2 (#11492). Adds `getModelsDevTierFitnessWithSource()` and an `invalidateCapabilitiesCache()` test/ops hook. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) — thanks @Meet6338-X +- fix(sse): accept Claude `content:[]` + `stop_reason: max_tokens`/`tool_use` in `detectMalformedNonStream` (match `isEmptyContentResponse`) so Claude Code `/model` probes with `max_tokens: 1` no longer become a false 502 ([#11568](https://github.com/diegosouzapw/OmniRoute/pull/11568)) — thanks @Seramicx +- **fix(security):** sanitize `request.failed` diagnostics before publishing them to live dashboard listeners and replay history, while keeping status, model, provider, latency, and internal call-log diagnostics intact. ([#12469](https://github.com/diegosouzapw/OmniRoute/pull/12469)) +- **fix(pwa):** do not intercept dashboard navigations so Chrome can fall back from a stale HTTP/3 Alt-Svc advertisement (UDP :20128 is unpublished; F5 on a long-lived tab hung until a new tab opened a fresh TCP connection). ([#12767](https://github.com/diegosouzapw/OmniRoute/pull/12767)) — thanks @HouMinXi +- **fix(executors):** handle DuckDuckGo ERR_BN_LIMIT (418) without retrying — when the upstream returns `418 ERR_BN_LIMIT` (rate-limit/ban), the executor now returns the error immediately instead of burning another VQD acquisition that would only count against the IP limit. The retry logic for `418 ERR_CHALLENGE` (unsolved challenge) remains unchanged. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) ([#12110](https://github.com/diegosouzapw/OmniRoute/pull/12110)) — thanks @oyi77 / @Meet6338-X +- Fixed DuckDuckGo streaming responses losing JSON lines and UTF-8 characters split across network chunks. ([#11528](https://github.com/diegosouzapw/OmniRoute/pull/11528)) — thanks @pacocartones +- Harden generated Cloudflare, Vercel, and Deno relays by stripping hop-by-hop, framing, and proxy authentication headers before forwarding requests upstream. ([#11533](https://github.com/diegosouzapw/OmniRoute/pull/11533)) — thanks @pacocartones +- **feat(grok-cli):** Provider Limits shows grok-cli banked reset credits from `GetRemainingResets` (including a real zero; a failed RPC omits the row) and the existing View credits button now calls `ConsumerUiSvc/RedeemReset` for grok-cli. Live tokens use nested fields 10/20/30 (id + Timestamp), not compact 1/2/3. ([#12805](https://github.com/diegosouzapw/OmniRoute/pull/12805)) — thanks @HouMinXi +- **fix(memory):** Embedding Model Quick select, Embedding Source remote dropdown, and Rerank selector now list every configured provider with embedding/rerank support instead of only chat-catalog text matches plus OpenRouter live discovery; a generic OpenAI-compatible `/embeddings` + Cohere-compatible `/rerank` runtime fallback resolves any configured chat provider's embedding/rerank endpoint, so unlisted providers no longer fail with "Unknown embedding provider"; both memory selectors gained a free-text model override ([#11390](https://github.com/diegosouzapw/OmniRoute/pull/11390)) — thanks @rqzbeh +- Harden SQLite upgrades around the historical migration-074 version collision: missing discovery and inspector tables are replayed atomically, pre-existing databases (including setup-created skeletons) receive reusable content-addressed safety snapshots, and Node test/eval probes without `DATA_DIR` are isolated from the operator database. ([#12435](https://github.com/diegosouzapw/OmniRoute/pull/12435)) +- **fix(dashboard):** Moonshot/Kimi Open Platform voucher and cash leftover percentages follow the bucket balance, so an empty wallet no longer paints those rows as 100% while Available is 0% ([#12733](https://github.com/diegosouzapw/OmniRoute/pull/12733)) — thanks @HouMinXi +- **fix(combo):** return non-retryable HTTP 400 when all candidates for a pinned native Codex turn are unavailable due to model-scoped lockout, terminating the turn cleanly while preserving turn continuity and enabling standard Combo routing on subsequent turns ([#12240](https://github.com/diegosouzapw/OmniRoute/pull/12240)) — thanks @mdigitalbh81 +- Fixed auto-router strategies preserving the selected connection through ranking and dispatch when multiple connections share the same provider and model. ([#11530](https://github.com/diegosouzapw/OmniRoute/pull/11530)) — thanks @pacocartones +- Preserve Chat Completions JSON-object response formats when translating requests to the Responses API. ([#11529](https://github.com/diegosouzapw/OmniRoute/pull/11529)) — thanks @pacocartones +- Fix idempotency fingerprints so Responses API requests include `input` and other semantic generation fields using stable serialization, while excluding credentials and request metadata. ([#11532](https://github.com/diegosouzapw/OmniRoute/pull/11532)) — thanks @pacocartones +- **fix(api):** Generated API CLI commands now enforce required OpenAPI request bodies; Combo test commands forward the required `comboName` body, while API keys created by older writers after migration 149 preserve legacy allow-all Combo access without widening explicit empty allowlists — thanks @marcelokarval ([#12070](https://github.com/diegosouzapw/OmniRoute/pull/12070)) +- **fix(grok-web):** treat upstream streaming failures as failures instead of successful assistant text: error-only streams now fail readiness with HTTP 502, while failures after legitimate content preserve that partial output and terminate through the sanitized stream failure path without a normal `stop` completion. ([#12458](https://github.com/diegosouzapw/OmniRoute/pull/12458)) +- HuggingChat now turns HTTP 200 JSONL generation failures into a sanitized 502 before content, or a fixed public stream failure after partial output, so fallback and request persistence no longer record a false successful stop. ([#12456](https://github.com/diegosouzapw/OmniRoute/pull/12456)) +- **fix(providers):** keep 1min.ai HTTP 200 stream errors out of assistant content, preserve partial output, and expose sanitized terminal errors so pre-content failures can fall back. ([#12466](https://github.com/diegosouzapw/OmniRoute/pull/12466)) +- **fix(sessions):** preserve legacy Sessions data when the additive exclusive-session projection is unavailable, returning an empty projection and warning only once per contiguous outage instead of failing the endpoint; the Sessions badge intentionally reflects the merged legacy and exclusive row count introduced by [#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389) ([#11469](https://github.com/diegosouzapw/OmniRoute/pull/11469)) — thanks @KaspaPulse +- Render Nimble Search with the generic provider icon and serve Opper's proven logo locally. ([#12415](https://github.com/diegosouzapw/OmniRoute/pull/12415)) +- **fix(quality):** `config/quality/provider-assets-provenance.jsonl` was stale against the physical `public/providers/` tree (still listing 225 assets and 5 duplicate-content groups after several provider-retirement PRs removed 85 icon files), which would have made `check:provider-asset-provenance` fail on the next full run. Reconciled the manifest to the real 142-file snapshot (142 asset records, 1 duplicate-content group, 2 newly-registered files marked `unresolved` pending provenance research), repinned `auditedCommit`, and updated the regression test's hardcoded counts to match. ([#11876](https://github.com/diegosouzapw/OmniRoute/pull/11876)) +- fix(ui): wrap quota card footer action buttons to prevent clipping Refresh button on narrow cards — when extra action buttons such as `View credits` are present on Codex cards, allow the footer row to flex-wrap with `shrink-0` buttons so `Refresh now` remains visible and clickable across all responsive card widths ([#11464](https://github.com/diegosouzapw/OmniRoute/pull/11464)) — thanks @adevwithpurpose +- Keep Antigravity Gemini usable when the same connection's Claude weekly quota is empty; generic quota cache stays per-connection for every other provider. ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) — thanks @HouMinXi +- Fixed Tailscale tunnel setup to reject ports outside the valid 1-65535 range and safely install Windows MSI packages from paths containing apostrophes. ([#11534](https://github.com/diegosouzapw/OmniRoute/pull/11534)) — thanks @pacocartones +- Classify Cloudflared and Tailscale process-management routes as local-only and spawn-capable while preserving remote access to read-only tunnel status endpoints. ([#11531](https://github.com/diegosouzapw/OmniRoute/pull/11531)) — thanks @pacocartones +- Electron release workflow: the `publish-npm` job now grants `actions: read` to the reusable `npm-publish.yml` it calls (its `publish` job requests it), which is what made GitHub refuse the whole v3.8.50 run at startup and ship the release with zero desktop assets; a `workflow_dispatch` now builds the requested tag instead of the dispatching branch and can skip the npm leg (`publish_npm=false`) when only re-attaching assets ([#11974](https://github.com/diegosouzapw/OmniRoute/pull/11974)) +- **fix(dashboard):** The Combos page usage guide now reads its dismissal through `useSyncExternalStore` instead of correcting SSR state inside an effect, removing an extra commit of the page tree on every load (and the `react-hooks/set-state-in-effect` error it raised). ([#12671](https://github.com/diegosouzapw/OmniRoute/pull/12671)) +- Electron release: `electron/package-lock.json` regained the optional `electron-builder-squirrel-windows` subtree (13 entries) that `npm ci` had been refusing as out of sync, `electron-release.yml` gained a `build_ref` dispatch input and stops regenerating release notes on a re-attach dispatch, and the npm publish workflow attaches the SBOM to the GitHub Release on dispatch publishes too — so the v3.8.51 tag ships every desktop asset and the SBOM like v3.8.49 did ([#12022](https://github.com/diegosouzapw/OmniRoute/pull/12022)) +- Drained the reds every PR against `release/v3.8.51` was born with: documented the three service-worker build-id variables, registered the six retirement/tunnel tests with the mutation gate, approved `eslint-plugin-react-hooks` in the dependency allowlist, added the six `combo.sort.*` strings to `vi` and `pt-BR`, pointed the ChatGPT Web doc at the real migration-168 test, worded the g4f hint around the member key, and realigned four tests to the retired-provider catalog and the legacy-schema fixtures the retirement migrations touch. ([#11940](https://github.com/diegosouzapw/OmniRoute/pull/11940)) +- **fix(providers):** Claude, Grok, LMArena, Notion, and Perplexity web-cookie transports now use pooled `wreq-js` 3.2 instead of the native sidecar, with all nine supported bindings pinned and audited, and the applicable platform binding plus native-license evidence included in each release artifact ([#12429](https://github.com/diegosouzapw/OmniRoute/pull/12429), supersedes [#11753](https://github.com/diegosouzapw/OmniRoute/pull/11753)). +- **fix(providers):** Zed Hosted streaming failures now trigger fallback before content and end partial streams with a sanitized structured error instead of fake assistant text and a normal-success stop. ([#12455](https://github.com/diegosouzapw/OmniRoute/pull/12455)) +- **fix(test):** capture hook result after render (direct commit `b85d0abb0b`) +- **fix(sync):** repair the two regressions the v3.8.50 sync-back left on release/v3.8.51 (direct commit `5ade9e0851`) +- **revert(models):** drop the memo reset hook ported from #10055 (direct commit `282de841ae`) +- **fix(typecheck):** guard the optional connectionId before the reactive model sync (direct commit `f2825bad75`) +- **fix(quota-share):** release the winner's reserved in-flight slot (#11371) ([#11408](https://github.com/diegosouzapw/OmniRoute/pull/11408)) — thanks @oyi77 +- **fix(providers):** stop silently dropping reasoning effort on opencode families (#10788) ([#11409](https://github.com/diegosouzapw/OmniRoute/pull/11409)) — thanks @oyi77 +- **fix(combo):** enable genuine quota-aware routing for generic providers (antigravity, claude, etc.) ([#11411](https://github.com/diegosouzapw/OmniRoute/pull/11411)) — thanks @HouMinXi +- **fix(oauth):** allow expired token retry before deactivation and activate expired retry loop (#P0) ([#11414](https://github.com/diegosouzapw/OmniRoute/pull/11414)) — thanks @HouMinXi +- **perf(executors):** lazy-load the executor registry — defer class imports + construction to first use (#11220) ([#11421](https://github.com/diegosouzapw/OmniRoute/pull/11421)) — thanks @oyi77 +- **fix:** make release/v3.8.50 CI gates green (run 32786966560 root causes) ([#11450](https://github.com/diegosouzapw/OmniRoute/pull/11450)) — thanks @jonlwheat2-gif +- **fix(sse):** exact-domain cookie match and origin-equality URL assertions ([#11452](https://github.com/diegosouzapw/OmniRoute/pull/11452)) — thanks @hartmark +- **fix(providers):** send Responses API payload during OpenAI-compatible validation ([#11454](https://github.com/diegosouzapw/OmniRoute/pull/11454)) — thanks @tuandinh0801 +- **fix(command-code):** fallback to /alpha/generate for Go plan without Provider API access ([#11455](https://github.com/diegosouzapw/OmniRoute/pull/11455)) — thanks @Chewji9875 +- **fix(responses-continuation):** fail closed on a log-truncated stored input/output array ([#11473](https://github.com/diegosouzapw/OmniRoute/pull/11473)) — thanks @hartmark +- **fix(cli):** write jcode settings as [providers.omniroute] in config.toml ([#11484](https://github.com/diegosouzapw/OmniRoute/pull/11484)) — thanks @hongnoul +- **fix(logging):** raise chat-log array truncation cap to 1000 and unify duplicate implementations ([#11499](https://github.com/diegosouzapw/OmniRoute/pull/11499)) — thanks @hartmark +- **fix:** restore live websocket URL helpers ([#11509](https://github.com/diegosouzapw/OmniRoute/pull/11509)) — thanks @PauloFH +- **perf(core):** eliminate async waterfalls across home, cache, and db operations (#11396) ([#11512](https://github.com/diegosouzapw/OmniRoute/pull/11512)) — thanks @HouMinXi +- **fix(kiro):** add runtime.us-east-1.kiro.dev as first-attempt endpoint ([#11517](https://github.com/diegosouzapw/OmniRoute/pull/11517)) — thanks @rifqiawl +- **fix(providers):** remove duplicate 128-tool truncation in OpencodeExecutor (#11444) ([#11518](https://github.com/diegosouzapw/OmniRoute/pull/11518)) — thanks @HouMinXi +- **fix(providers):** poll onboardUser LRO before classifying BYOP ([#11519](https://github.com/diegosouzapw/OmniRoute/pull/11519)) — thanks @rifqiawl +- **fix(translator):** preserve exact tool name casing for Claude tool_use payloads (#11487) ([#11520](https://github.com/diegosouzapw/OmniRoute/pull/11520)) — thanks @HouMinXi +- **fix(kimi-web):** align health probe with executor domain ([#11521](https://github.com/diegosouzapw/OmniRoute/pull/11521)) — thanks @pacocartones +- **fix(cli):** preserve existing admin password during setup ([#11522](https://github.com/diegosouzapw/OmniRoute/pull/11522)) — thanks @pacocartones +- **fix(mitm):** keep Windows DNS elevation off the POSIX sudo path (#11430) ([#11542](https://github.com/diegosouzapw/OmniRoute/pull/11542)) — thanks @official-burak +- **fix(sse):** scale chat admission by ingest byte budget instead of a fixed request count ([#11548](https://github.com/diegosouzapw/OmniRoute/pull/11548)) — thanks @TheDemonTuan +- **fix:** accept Gemini Web cookie exports ([#11554](https://github.com/diegosouzapw/OmniRoute/pull/11554)) — thanks @rafacpti23 +- **fix(i18n):** complete Brazilian Portuguese translations ([#11558](https://github.com/diegosouzapw/OmniRoute/pull/11558)) — thanks @rafacpti23 +- **fix(sse):** explicit types for openai-responses pureHelpers — clears last failing core typecheck gate ([#11567](https://github.com/diegosouzapw/OmniRoute/pull/11567)) — thanks @jonlwheat2-gif +- **fix(opencode):** hydrate Proxy Pool references for no-auth connections ([#11584](https://github.com/diegosouzapw/OmniRoute/pull/11584)) — thanks @AStupidBear +- **fix(ci):** resolve inherited release/v3.8.51 base-red CI failures ([#11608](https://github.com/diegosouzapw/OmniRoute/pull/11608)) — thanks @jonlwheat2-gif +- **fix(db):** fallback load STORAGE_ENCRYPTION_KEY from env files ([#11614](https://github.com/diegosouzapw/OmniRoute/pull/11614)) — thanks @benzntech +- **fix(providers):** mark Brave Search as no longer having a free tier ([#11621](https://github.com/diegosouzapw/OmniRoute/pull/11621)) — thanks @turbolego +- **fix:** pass tool result content through as string in OpenAI/Claude -> Gemini translation ([#11624](https://github.com/diegosouzapw/OmniRoute/pull/11624)) — thanks @Hsia97 +- **fix(sse):** scope credential health to exact key ([#11628](https://github.com/diegosouzapw/OmniRoute/pull/11628)) — thanks @KaspaPulse +- **fix(install):** guard prepare script against missing husky ([#11634](https://github.com/diegosouzapw/OmniRoute/pull/11634)) — thanks @Prajeeth-12 +- **fix(security):** redact NVIDIA_API_KEY at the log sink in the ad-hoc diag script ([#11639](https://github.com/diegosouzapw/OmniRoute/pull/11639)) +- **fix(types):** clear the two type regressions red on release/v3.8.51 ([#11640](https://github.com/diegosouzapw/OmniRoute/pull/11640)) — thanks @jonlwheat2-gif +- **fix(build):** resolve workspace: protocol dependencies to concrete semver versions during prepublish (#11540) ([#11641](https://github.com/diegosouzapw/OmniRoute/pull/11641)) — thanks @HouMinXi +- **fix(search):** properly resolve configured search connection in /v1/responses pipeline (#11524) ([#11642](https://github.com/diegosouzapw/OmniRoute/pull/11642)) — thanks @HouMinXi +- **fix(authz):** trust cf-connecting-ip for via-proxy detection when peer is Cloudflare edge (#11514) ([#11643](https://github.com/diegosouzapw/OmniRoute/pull/11643)) — thanks @HouMinXi +- **fix(security):** compare the OIDC state cookie in constant time ([#11646](https://github.com/diegosouzapw/OmniRoute/pull/11646)) +- **fix(security):** namespace the dedup hash by the calling API key ([#11649](https://github.com/diegosouzapw/OmniRoute/pull/11649)) +- **fix(models):** gate the Codex-native catalog loop on prefix mode (#11632) ([#11662](https://github.com/diegosouzapw/OmniRoute/pull/11662)) — thanks @xiaoyaner0201 +- **fix(dashboard):** project Codex routing quota in account pools ([#11666](https://github.com/diegosouzapw/OmniRoute/pull/11666)) — thanks @KaspaPulse +- **fix(ddgw):** normaliza system no upstream da release v3.8.51 ([#11692](https://github.com/diegosouzapw/OmniRoute/pull/11692)) — thanks @RhianB14 +- **fix(memory):** honor category filter in GET /api/memory ([#11699](https://github.com/diegosouzapw/OmniRoute/pull/11699)) — thanks @Sabeekhann +- **fix(ci):** detect stale workspace lockfile entries ([#11714](https://github.com/diegosouzapw/OmniRoute/pull/11714)) — thanks @pacocartones +- **fix(ci):** stamp dist/BUILD_SHA before the npm publish provenance gate ([#11721](https://github.com/diegosouzapw/OmniRoute/pull/11721)) +- **fix(docker):** decouple the best-effort Bun image from the release manifest ([#11724](https://github.com/diegosouzapw/OmniRoute/pull/11724)) +- **perf(compression):** avoid cloning memo misses twice ([#11727](https://github.com/diegosouzapw/OmniRoute/pull/11727)) — thanks @RaviTharuma +- **fix(docker):** let the bun digest artifact be absent, not fatal ([#11740](https://github.com/diegosouzapw/OmniRoute/pull/11740)) +- **fix(shared):** block cloud-metadata hosts under default remote-image guard ([#11755](https://github.com/diegosouzapw/OmniRoute/pull/11755)) — thanks @santosraju99-hub +- **fix(api):** preserve caller-provided X-Correlation-Id on chat completions ([#11760](https://github.com/diegosouzapw/OmniRoute/pull/11760)) — thanks @aniruddhaadak80 +- **fix(resilience):** lock permanently retired models instead of short backoff (Gemini ban prevention) ([#11762](https://github.com/diegosouzapw/OmniRoute/pull/11762)) — thanks @turbolego +- **fix:** mark Vercel AI Gateway as passthroughModels ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **fix(resilience):** stop hammering permanently-moved endpoints and billing-suspended accounts ([#11774](https://github.com/diegosouzapw/OmniRoute/pull/11774)) — thanks @turbolego +- **fix(lease):** remove global static reservation and gate routing on live active lease occupancy ([#11775](https://github.com/diegosouzapw/OmniRoute/pull/11775)) — thanks @TheDemonTuan +- **fix(ci):** size the install-upgrade gate to a measured run, and log the pack cost ([#11776](https://github.com/diegosouzapw/OmniRoute/pull/11776)) +- **fix(pwa):** stop serving the stale shell after deploys ([#11779](https://github.com/diegosouzapw/OmniRoute/pull/11779)) — thanks @HouMinXi +- **fix(combo):** lock GitHub models rejected as "not supported" for future requests ([#11781](https://github.com/diegosouzapw/OmniRoute/pull/11781)) — thanks @turbolego +- **fix(cli):** probe both IPv4 and IPv6 loopback for server readiness (#11766) ([#11794](https://github.com/diegosouzapw/OmniRoute/pull/11794)) — thanks @vermasomesh835 +- **fix(sse):** preserve CLIProxyAPI wrappers per connection ([#11797](https://github.com/diegosouzapw/OmniRoute/pull/11797)) — thanks @RaviTharuma +- **fix(providers):** honor model auto-fetch opt-in on create ([#11798](https://github.com/diegosouzapw/OmniRoute/pull/11798)) — thanks @RaviTharuma +- **fix(dashboard):** make model auto-fetch order independent ([#11805](https://github.com/diegosouzapw/OmniRoute/pull/11805)) — thanks @RaviTharuma +- **fix(docker):** make GHA cache exports best effort ([#11806](https://github.com/diegosouzapw/OmniRoute/pull/11806)) — thanks @RaviTharuma +- **fix(kiro):** do not permanently ban on 'User is not authorized to make this call' ([#11809](https://github.com/diegosouzapw/OmniRoute/pull/11809)) — thanks @Deftera186 +- **fix(sse):** keep cache-write tokens in OpenAI-shaped usage ([#11814](https://github.com/diegosouzapw/OmniRoute/pull/11814)) — thanks @TheDemonTuan +- **fix(docs):** resolve relative markdown and wiki links across Fumadocs and GitHub wiki ([#11834](https://github.com/diegosouzapw/OmniRoute/pull/11834)) — thanks @AndrianBalanescu +- **fix(chat):** preserve unstripped model string for passthrough provider routing ([#11840](https://github.com/diegosouzapw/OmniRoute/pull/11840)) — thanks @raheemuddin786 +- **fix(mcp):** avoid async initialization inside sync esm wrappers (#11569) ([#11849](https://github.com/diegosouzapw/OmniRoute/pull/11849)) — thanks @HouMinXi +- **fix(sse):** preserve coherent markdown token boundaries across stream translation chunks (#11606) ([#11851](https://github.com/diegosouzapw/OmniRoute/pull/11851)) — thanks @HouMinXi +- **fix(ci):** run the install-upgrade gate on disk, not on the /tmp tmpfs ([#11855](https://github.com/diegosouzapw/OmniRoute/pull/11855)) +- **fix(skills):** expand shorthand property types in injected tool schemas ([#11857](https://github.com/diegosouzapw/OmniRoute/pull/11857)) — thanks @alvinveroy +- **fix(ci):** publish npm from a hosted runner so provenance is accepted ([#11877](https://github.com/diegosouzapw/OmniRoute/pull/11877)) +- **fix(lint):** pin react hooks plugin to 7.0.1 ([#11886](https://github.com/diegosouzapw/OmniRoute/pull/11886)) +- **fix(ci):** surface and clear release-green ESLint failure ([#11890](https://github.com/diegosouzapw/OmniRoute/pull/11890)) — thanks @MumuTW +- **fix(guardrails):** prevent duplicate prompt-injection-guard log output ([#11936](https://github.com/diegosouzapw/OmniRoute/pull/11936)) — thanks @patrykkopycinski +- **fix(auth):** downgrade expected transient states from warn to debug ([#11937](https://github.com/diegosouzapw/OmniRoute/pull/11937)) — thanks @patrykkopycinski +- **fix(codex):** fail over image generation for imported free plans ([#11948](https://github.com/diegosouzapw/OmniRoute/pull/11948)) — thanks @Bl0ck154 +- **fix(codex):** normalize non-stream responses ([#11951](https://github.com/diegosouzapw/OmniRoute/pull/11951)) — thanks @Bl0ck154 +- **fix(images):** forward Antigravity image size ([#11952](https://github.com/diegosouzapw/OmniRoute/pull/11952)) — thanks @Bl0ck154 +- **fix(db):** invalidate connection cache after upsert ([#11953](https://github.com/diegosouzapw/OmniRoute/pull/11953)) — thanks @Bl0ck154 +- **fix(codex):** restore imported account state ([#11954](https://github.com/diegosouzapw/OmniRoute/pull/11954)) — thanks @Bl0ck154 +- **fix(sse):** give extended-thinking targets the reasoning readiness budget ([#11959](https://github.com/diegosouzapw/OmniRoute/pull/11959)) — thanks @abhisheksharma2411 +- **fix(dashboard):** use opaque background and readable text color on cost chart tooltips ([#11960](https://github.com/diegosouzapw/OmniRoute/pull/11960)) — thanks @ZaimMarzuki +- **fix(antigravity):** send complete loadCodeAssist metadata (ideType/platform/pluginType as numeric enums) ([#11969](https://github.com/diegosouzapw/OmniRoute/pull/11969)) — thanks @rifqiawl +- **fix(core):** resolve DB init race condition and reasoning translation ([#12003](https://github.com/diegosouzapw/OmniRoute/pull/12003)) — thanks @raheemuddin786 +- **fix(admission):** exclude reclaimable page cache from the cgroup pressure ratio ([#12017](https://github.com/diegosouzapw/OmniRoute/pull/12017)) — thanks @HouMinXi +- **fix(build):** prune non-production trees in NFT trace excludes and tsconfig ([#12028](https://github.com/diegosouzapw/OmniRoute/pull/12028)) — thanks @Chewji9875 +- **fix(migrations):** add renamed migration compatibility for 056/073/077/101 ([#12036](https://github.com/diegosouzapw/OmniRoute/pull/12036)) — thanks @oyi77 +- **fix(sse):** default crash-guard logger to console.warn, not console ([#12042](https://github.com/diegosouzapw/OmniRoute/pull/12042)) — thanks @HouMinXi +- **fix(catalog):** derive vision/modalities for built-in auto combos from effective target pool ([#12046](https://github.com/diegosouzapw/OmniRoute/pull/12046)) — thanks @Prajeeth-12 +- **fix(release):** the packaged-app smoke verifies the database opened, not a driver line the primary path never prints (twin of #12032) ([#12047](https://github.com/diegosouzapw/OmniRoute/pull/12047)) +- **fix(barrel):** migrate src/app/ off the @/lib/localDb barrel import (#11795 Phase 2) ([#12051](https://github.com/diegosouzapw/OmniRoute/pull/12051)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate src/lib/ off the localDb barrel to direct db imports (#59) ([#12052](https://github.com/diegosouzapw/OmniRoute/pull/12052)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate open-sse, src/shared, src/sse, src/models, src/domain off the @/lib/localDb barrel import (#11795 Phase 4) ([#12053](https://github.com/diegosouzapw/OmniRoute/pull/12053)) — thanks @jonlwheat2-gif +- **fix(barrel):** delete the @/lib/localDb barrel — every consumer migrated (#11795 Phase 5) ([#12055](https://github.com/diegosouzapw/OmniRoute/pull/12055)) — thanks @jonlwheat2-gif +- **fix(radar):** restore D12 public boundary ([#12057](https://github.com/diegosouzapw/OmniRoute/pull/12057)) +- **fix(dev):** bound webpack and Tailwind scans ([#12075](https://github.com/diegosouzapw/OmniRoute/pull/12075)) — thanks @backryun +- **fix(dev):** isolate root layout settings reads ([#12076](https://github.com/diegosouzapw/OmniRoute/pull/12076)) — thanks @backryun +- **fix(dev):** reduce instrumentation executor fan-out (phase 3) ([#12078](https://github.com/diegosouzapw/OmniRoute/pull/12078)) — thanks @backryun +- **fix(dev):** make logging resources HMR-singleton ([#12079](https://github.com/diegosouzapw/OmniRoute/pull/12079)) — thanks @backryun +- **fix(sse):** keep unavailable forced connections scoped ([#12080](https://github.com/diegosouzapw/OmniRoute/pull/12080)) — thanks @keeltrace +- **fix(dev):** isolate batch dispatch from instrumentation ([#12081](https://github.com/diegosouzapw/OmniRoute/pull/12081)) — thanks @backryun +- **fix(ci):** reconcile release test contract drift ([#12082](https://github.com/diegosouzapw/OmniRoute/pull/12082)) — thanks @backryun +- **fix(ci):** accept CVE-2025-68121 in the prebuilt tls-client .so, auto-close base-red issues, guard Scorecard on the default branch ([#12085](https://github.com/diegosouzapw/OmniRoute/pull/12085)) +- **fix(db):** drop three consumer-less 1proxy exports — dead-code base-red on release/v3.8.51 after the barrel deletion (#12055) ([#12087](https://github.com/diegosouzapw/OmniRoute/pull/12087)) +- **fix(api):** clear the six API-route TypeScript regressions the new gate landed red on ([#12094](https://github.com/diegosouzapw/OmniRoute/pull/12094)) +- **fix(release):** never let the tag-push Create Release append auto notes to the curated body ([#12096](https://github.com/diegosouzapw/OmniRoute/pull/12096)) +- **fix(sse):** honor CLIProxyAPI environment API key ([#12099](https://github.com/diegosouzapw/OmniRoute/pull/12099)) — thanks @RaviTharuma +- **fix(oauth):** bind Google refresh to the client that issued the token ([#12106](https://github.com/diegosouzapw/OmniRoute/pull/12106)) — thanks @HouMinXi +- **fix(dashboard):** make RequestLoggerDetail loadable outside Next — CSS via globals.css + CJS/ESM interop (#11703 base-reds) ([#12114](https://github.com/diegosouzapw/OmniRoute/pull/12114)) +- **fix(leases):** project status lease row to lease columns so joined connection PII never escapes ([#12115](https://github.com/diegosouzapw/OmniRoute/pull/12115)) — thanks @geek007git +- **fix(plugins):** do not kill the plugin process when a fire-and-forget hook times out ([#12116](https://github.com/diegosouzapw/OmniRoute/pull/12116)) — thanks @geek007git +- **fix(guardrails):** restore injection-guard logging on middleware-only routes ([#12117](https://github.com/diegosouzapw/OmniRoute/pull/12117)) — thanks @geek007git +- **fix(plugins):** refresh stored manifest from disk on activate so new hook fields reach existing installs ([#12120](https://github.com/diegosouzapw/OmniRoute/pull/12120)) — thanks @geek007git +- **fix(codex):** preserve existing provider state when bulk-import upserts a matching connection ([#12122](https://github.com/diegosouzapw/OmniRoute/pull/12122)) — thanks @geek007git +- **fix(ci):** clear the two base-reds the 2026-08-30 merge batch left on release/v3.8.51 (round 3) ([#12123](https://github.com/diegosouzapw/OmniRoute/pull/12123)) +- **fix(sse):** strip type:'custom' from Claude tools on agentrouter dispatch ([#12126](https://github.com/diegosouzapw/OmniRoute/pull/12126)) — thanks @wahidsadik371-coder +- **fix(sse):** stop advertising video providers the dispatcher cannot run ([#12131](https://github.com/diegosouzapw/OmniRoute/pull/12131)) — thanks @quiterunner-commits +- **fix(search):** treat HTTP 432 and plan limit errors as transient cooldown ([#12139](https://github.com/diegosouzapw/OmniRoute/pull/12139)) — thanks @HouMinXi +- **fix(sse):** refuse an AI Horde queue that cannot fit the request budget ([#12143](https://github.com/diegosouzapw/OmniRoute/pull/12143)) — thanks @quiterunner-commits +- **fix(ci):** clear the base-reds the afternoon merge batch left on release/v3.8.51 (round 5: provider count 352, TS2554/TS2677) ([#12144](https://github.com/diegosouzapw/OmniRoute/pull/12144)) +- **fix(translator):** strip neutral tool_choice when tools absent in Responses-to-Chat (#12141) ([#12166](https://github.com/diegosouzapw/OmniRoute/pull/12166)) — thanks @HouMinXi +- **fix(guardrails):** pass providerId to getResolvedModelCapabilities in checkComboVision (#12112) ([#12169](https://github.com/diegosouzapw/OmniRoute/pull/12169)) — thanks @HouMinXi +- **fix(translator):** strip plaintext reasoning content for opaque responses backends (#12128) ([#12171](https://github.com/diegosouzapw/OmniRoute/pull/12171)) — thanks @HouMinXi +- **perf(stream):** compile hot-path regexes once, bound token caches, fix quadratic buffering ([#12179](https://github.com/diegosouzapw/OmniRoute/pull/12179)) — thanks @opensource-elearning +- **fix:** resolve compression worker file using runtime anchors instead of… ([#12183](https://github.com/diegosouzapw/OmniRoute/pull/12183)) — thanks @NightStalker-87 +- **fix:** prevent Claude Code session kills via liveness-aware readiness + auto model echo ([#12189](https://github.com/diegosouzapw/OmniRoute/pull/12189)) — thanks @opensource-elearning +- **fix(translator):** preserve falsy primitive values in Gemini and Antigravity function response results ([#12191](https://github.com/diegosouzapw/OmniRoute/pull/12191)) — thanks @Siva010 +- **fix(oauth):** mark empty Antigravity projectId as degraded and clear stale errors (#11284) ([#12205](https://github.com/diegosouzapw/OmniRoute/pull/12205)) — thanks @HouMinXi +- **fix(providers):** gate the Codex auto-ping usage read on the shared quota throttle ([#12209](https://github.com/diegosouzapw/OmniRoute/pull/12209)) — thanks @abhisheksharma2411 +- **fix(combos):** prioritize SQLite row id over inner JSON id and notify delete errors ([#12213](https://github.com/diegosouzapw/OmniRoute/pull/12213)) — thanks @HouMinXi +- **fix(providers):** mark featherless.ai as no longer having a free tier (`hasFree: false`) ([#12216](https://github.com/diegosouzapw/OmniRoute/pull/12216)) — thanks @turbolego +- **fix(deepseek-web):** stop Turbopack dev panic in the PoW worker path resolver ([#12221](https://github.com/diegosouzapw/OmniRoute/pull/12221)) — thanks @hartmark +- **fix(chat-admission):** clarify local 503 source ([#12223](https://github.com/diegosouzapw/OmniRoute/pull/12223)) — thanks @keeltrace +- **fix(dev):** silence webpack runtime module warnings ([#12228](https://github.com/diegosouzapw/OmniRoute/pull/12228)) — thanks @backryun +- **fix(db):** use module.require for CommonJS runtime driver loading ([#12230](https://github.com/diegosouzapw/OmniRoute/pull/12230)) — thanks @raheemuddin786 +- **fix(memory):** honest probe-driven FTS5 keyword status + memory_id rowid sync ([#12231](https://github.com/diegosouzapw/OmniRoute/pull/12231)) — thanks @raheemuddin786 +- **fix(system):** propagate abort signal to stream reader in HTTP version checks ([#12232](https://github.com/diegosouzapw/OmniRoute/pull/12232)) — thanks @raheemuddin786 +- **fix(resilience):** isolate local host execution errors from provider circuit breakers ([#12233](https://github.com/diegosouzapw/OmniRoute/pull/12233)) — thanks @raheemuddin786 +- **fix(sse):** sort injected tools deterministically for prompt caching ([#12234](https://github.com/diegosouzapw/OmniRoute/pull/12234)) — thanks @raheemuddin786 +- **fix(combo):** always clear the loop-safety timer, not just on the happy path (#11804) ([#12245](https://github.com/diegosouzapw/OmniRoute/pull/12245)) +- **fix(quality):** base-red pair — stryker tap registration + turn-pin suites aligned to the window gate ([#12255](https://github.com/diegosouzapw/OmniRoute/pull/12255)) — thanks @backryun +- **fix(dev):** qualify Turbopack runtime boundaries ([#12258](https://github.com/diegosouzapw/OmniRoute/pull/12258)) — thanks @backryun +- **fix(combo):** fall back to full pool when collapsed sole survivor is context-too-small ([#12278](https://github.com/diegosouzapw/OmniRoute/pull/12278)) — thanks @opensource-elearning +- **fix(combo):** bound the pre-dispatch unavailable skip so a stale label cannot dark a pool (#12168) ([#12285](https://github.com/diegosouzapw/OmniRoute/pull/12285)) +- **fix(security):** strip Qwen/Alibaba console-session cookies from provider API responses ([#12287](https://github.com/diegosouzapw/OmniRoute/pull/12287)) +- **fix(usage):** console-aware Token Plan guidance + subscription hint on bailian 401 ([#12288](https://github.com/diegosouzapw/OmniRoute/pull/12288)) +- **fix(resourcePressure):** log numeric detail on every rejection, recover faster ([#12293](https://github.com/diegosouzapw/OmniRoute/pull/12293)) — thanks @hartmark +- **fix(release):** drain the 2026-09-01 base-red window — passthrough usage regression + radarPage i18n keys ([#12327](https://github.com/diegosouzapw/OmniRoute/pull/12327)) +- **fix(ci):** openapi-security-tiers checker must honor routeGuard patterns + imported prefixes ([#12350](https://github.com/diegosouzapw/OmniRoute/pull/12350)) — thanks @ggiak +- **perf(ci):** cache node_modules in the npm-ci-retry composite (#8084 D3) ([#12408](https://github.com/diegosouzapw/OmniRoute/pull/12408)) +- **revert(ui):** point CTAs back at their real destinations ([#12410](https://github.com/diegosouzapw/OmniRoute/pull/12410)) +- **fix(release):** drain the 2026-09-02 base-red — rerank-providers import + api-typecheck baseline ratchet ([#12414](https://github.com/diegosouzapw/OmniRoute/pull/12414)) +- **fix(types):** make system prompt injection noImplicitAny-safe ([#12416](https://github.com/diegosouzapw/OmniRoute/pull/12416)) +- **fix(quality):** validate typecheck baseline schema ([#12419](https://github.com/diegosouzapw/OmniRoute/pull/12419)) +- **fix(memory):** resolve rerank provider node cache import ([#12421](https://github.com/diegosouzapw/OmniRoute/pull/12421)) +- **fix(ci):** repair release-root regressions — pack dedup, web-session syntax, uc-image ids ([#12423](https://github.com/diegosouzapw/OmniRoute/pull/12423)) — thanks @backryun +- **fix(providers):** separate MaxAI and UC credential contracts ([#12431](https://github.com/diegosouzapw/OmniRoute/pull/12431)) +- **fix(cli):** preserve Claude settings on config set ([#12432](https://github.com/diegosouzapw/OmniRoute/pull/12432)) — thanks @KrzysiekSko +- **fix(providers):** repair the maxai credential block truncated by merge auto-resolve ([#12433](https://github.com/diegosouzapw/OmniRoute/pull/12433)) +- **fix(quality):** record the 2026-09-02 merged growth in the file-size baseline ([#12434](https://github.com/diegosouzapw/OmniRoute/pull/12434)) +- **fix(uc):** route UC error strings through sanitizeErrorMessage; allowlist the retired codex id ([#12437](https://github.com/diegosouzapw/OmniRoute/pull/12437)) +- **fix(adapta):** redact streamed upstream errors ([#12438](https://github.com/diegosouzapw/OmniRoute/pull/12438)) +- **fix(sse):** remap non-contiguous upstream tool_calls index to a gap-free output_index ([#12445](https://github.com/diegosouzapw/OmniRoute/pull/12445)) — thanks @hartmark +- **fix(quality):** detect duplicate tool_calls entries in one response ([#12446](https://github.com/diegosouzapw/OmniRoute/pull/12446)) — thanks @hartmark +- **fix(conversations):** resolve turn content OmniRoute never sends back to the client ([#12447](https://github.com/diegosouzapw/OmniRoute/pull/12447)) — thanks @hartmark +- **fix(combo):** do not treat credits-exhausted 401 as auth skip ([#12449](https://github.com/diegosouzapw/OmniRoute/pull/12449)) — thanks @RaviTharuma +- **fix(auth):** do not park healthy quota accounts as expired ([#12452](https://github.com/diegosouzapw/OmniRoute/pull/12452)) — thanks @RaviTharuma +- **fix(responses-continuation):** fail closed on a collector-truncated, empty output array ([#12460](https://github.com/diegosouzapw/OmniRoute/pull/12460)) — thanks @hartmark +- **fix(logging):** raise the SSE payload collector's default cap ([#12461](https://github.com/diegosouzapw/OmniRoute/pull/12461)) — thanks @hartmark +- **fix(resilience):** surface Responses failed.error.message in 502s ([#12472](https://github.com/diegosouzapw/OmniRoute/pull/12472)) — thanks @RaviTharuma +- **fix(security):** unbiased maxai X-Random nonce + stricter URL/regex assertions ([#12502](https://github.com/diegosouzapw/OmniRoute/pull/12502)) +- **fix(video):** re-anchor transcript log-redaction so PII/credential maskers can't reopen the leak (#12150 P1 follow-up) ([#12503](https://github.com/diegosouzapw/OmniRoute/pull/12503)) +- **fix(monitoring):** serve cached credentialHealth off the request path ([#12533](https://github.com/diegosouzapw/OmniRoute/pull/12533)) — thanks @RaviTharuma +- **fix(quota):** keep Antigravity Gemini usable when Claude weekly is empty ([#12566](https://github.com/diegosouzapw/OmniRoute/pull/12566)) — thanks @HouMinXi +- **fix(authz):** hard-gate every credential export and CLI-config write (GHSA-5926-2w35-7h4q) ([#12600](https://github.com/diegosouzapw/OmniRoute/pull/12600)) +- **fix(docs):** restore the Next build — REMOVED_PROVIDERS.md had no frontmatter (base-red #12581) ([#12610](https://github.com/diegosouzapw/OmniRoute/pull/12610)) +- **fix(quality):** re-point the zcodeProtocol public-creds allowlist to line 313 ([#12615](https://github.com/diegosouzapw/OmniRoute/pull/12615)) +- **fix(security):** close 3 advisories — search baseUrl exfil, sk- in the error sanitizer, bifrost relay header leak ([#12620](https://github.com/diegosouzapw/OmniRoute/pull/12620)) +- **fix(providers):** sanitize boolean required and nested bare maps for Gemini (#12269) ([#12624](https://github.com/diegosouzapw/OmniRoute/pull/12624)) — thanks @HouMinXi +- **fix(resilience):** keep Overloaded STREAM_EARLY_EOF off the provider breaker ([#12626](https://github.com/diegosouzapw/OmniRoute/pull/12626)) — thanks @HouMinXi +- **fix(catalog):** re-audit free-tier quotas against official pages ([#12649](https://github.com/diegosouzapw/OmniRoute/pull/12649)) +- **fix(dashboard):** keep a request's pending-tracking id stable across combo target retries ([#12650](https://github.com/diegosouzapw/OmniRoute/pull/12650)) — thanks @hartmark +- **fix(tests):** retire dead model ids from the chat-pipeline integration suite (base-red #12581) ([#12670](https://github.com/diegosouzapw/OmniRoute/pull/12670)) +- **fix:** resolve SqliteError no such table compression_run_telemetry during cleanup ([#12682](https://github.com/diegosouzapw/OmniRoute/pull/12682)) — thanks @groovecityJO +- **fix(combo):** treat a pin-only step as implicit connection allowlist ([#12697](https://github.com/diegosouzapw/OmniRoute/pull/12697)) — thanks @HouMinXi +- **fix(ci):** ship .npmrc in published package so legacy-peer-deps applies to consumers (#11544) ([#12699](https://github.com/diegosouzapw/OmniRoute/pull/12699)) — thanks @KooshaPari +- **fix(docker):** pin 4 CLI tools to exact versions (#12576) ([#12703](https://github.com/diegosouzapw/OmniRoute/pull/12703)) — thanks @KooshaPari +- **fix(docker):** document and harden cli profile trust boundary (#12570) ([#12706](https://github.com/diegosouzapw/OmniRoute/pull/12706)) — thanks @KooshaPari +- **fix(sse):** redact video transcript in pre-guardrail rejected-request logs (#12150 P2 item 7) ([#12710](https://github.com/diegosouzapw/OmniRoute/pull/12710)) +- **fix(i18n):** quote placeholder in OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES description (#12505) ([#12769](https://github.com/diegosouzapw/OmniRoute/pull/12769)) — thanks @KooshaPari +- **fix(api/pricing):** surface validation error message as string, not raw object (#12494) ([#12771](https://github.com/diegosouzapw/OmniRoute/pull/12771)) — thanks @KooshaPari +- **fix(sse):** re-enable prompt compression for native Codex passthrough ([#12834](https://github.com/diegosouzapw/OmniRoute/pull/12834)) — thanks @thomasmaerz +- **fix(dashboard):** model health tests for a provider node set to the Responses API now call `/v1/responses` with a Responses-shaped body instead of `/v1/chat/completions` — those models were reported as `Provider returned HTTP 200 but no text content` even though the same model answered normally through `/v1/responses` ([#13070](https://github.com/diegosouzapw/OmniRoute/issues/13070)) ([#13087](https://github.com/diegosouzapw/OmniRoute/pull/13087)) — thanks @ntdatt812 +- fix(routing): stop a round-robin combo's "opencode" targets from collapsing onto the opencode-zen connection (#11912) ([#13283](https://github.com/diegosouzapw/OmniRoute/pull/13283)) +- fix(routing): stop the reactive-compaction debug log from lying when compression is globally disabled (#11977) ([#13276](https://github.com/diegosouzapw/OmniRoute/pull/13276)) +- fix(electron): relativize standalone-bundle symlink targets so Stage 8 manifest verification stops failing on Windows (#11979) ([#13251](https://github.com/diegosouzapw/OmniRoute/pull/13251)) +- fix(dashboard): surface a visible error when Compression Studio's combined preview run fails (#12061) ([#13288](https://github.com/diegosouzapw/OmniRoute/pull/13288)) +- fix(dashboard): make the compression "Effective pipeline" preview honor the active profile and warn when the master switch is off (#12063) ([#13287](https://github.com/diegosouzapw/OmniRoute/pull/13287)) +- fix(providers): scope TinyCMS Web signer's DOM shims to each call instead of leaking them for the process lifetime, and surface a clean HTTP status on a non-JSON interception-toggles error (#12072) ([#13254](https://github.com/diegosouzapw/OmniRoute/pull/13254)) +- fix(guardrails): stop Vision Bridge from re-selecting a model locked after a 404 (#12111) ([#13259](https://github.com/diegosouzapw/OmniRoute/pull/13259)) +- fix(sse): require Responses-shaped body before native OpenAI-compatible passthrough (#12129) ([#13278](https://github.com/diegosouzapw/OmniRoute/pull/13278)) +- fix(providers): minimax-m3 now collapses manual thinking.type:"enabled" to adaptive, preventing upstream 400 (2013) (#12132) ([#13249](https://github.com/diegosouzapw/OmniRoute/pull/13249)) +- fix(db): scope model visibility overrides by modality so hiding a Chat model no longer hides an identically-ID'd Image/Embeddings/etc. model (#12172) ([#13258](https://github.com/diegosouzapw/OmniRoute/pull/13258)) +- fix(db): scope local-provider apiKey dedup to matching base URL so LM Studio/Ollama-style connections support multiple accounts (#12173) ([#13250](https://github.com/diegosouzapw/OmniRoute/pull/13250)) +- fix(providers): refresh Trae's stale Referer/Origin and forward user timezone so imported connections stop failing with 401 (#12190) ([#13255](https://github.com/diegosouzapw/OmniRoute/pull/13255)) +- fix(providers): route opencode-go/gpt-5.6-luna to /responses instead of /chat/completions (#12196) ([#13275](https://github.com/diegosouzapw/OmniRoute/pull/13275)) +- fix(dashboard): allow deleting the last extra-upstream-header row even when invalid (#12251) ([#13252](https://github.com/diegosouzapw/OmniRoute/pull/13252)) +- **fix(i18n):** translate pre-existing `__MISSING__:` keys for `combo.sort`, `requestLogger.detail` expand/collapse, `common.profile`, and `settings.resilienceCredentialHealth*` across 39 locales ([#12272](https://github.com/diegosouzapw/OmniRoute/issues/12272)) ([#13034](https://github.com/diegosouzapw/OmniRoute/pull/13034)) — thanks @HouMinXi +- fix(cli): run the Node.js runtime compatibility guard before the heavy `tsx/esm` + Commander import chain so an unsupported runtime gets a clear message instead of a raw `Invalid regular expression flags` crash (#12296) ([#13253](https://github.com/diegosouzapw/OmniRoute/pull/13253)) +- fix(dashboard): refresh the providers list after deleting a compatible provider node (#12298) ([#13256](https://github.com/diegosouzapw/OmniRoute/pull/13256)) +- fix(usage): fail closed on API-key budget enforcement when a provider's `auto` routing alias has no pricing row, instead of silently counting it as $0 (#12341) ([#13257](https://github.com/diegosouzapw/OmniRoute/pull/13257)) +- **fix(providers):** include the submitted Agnes video model when polling by `video_id` ([#12356](https://github.com/diegosouzapw/OmniRoute/pull/12356)) — thanks @pacocartones +- **fix(routing):** custom OpenAI-compatible nodes now honor the saved Chat/Responses API type after edits instead of letting the node's original ID prefix override the live connection setting ([#11884](https://github.com/diegosouzapw/OmniRoute/issues/11884)). ([#12358](https://github.com/diegosouzapw/OmniRoute/pull/12358)) — thanks @pacocartones +- fix(sse): surface an error instead of a silent empty 200 when a Claude stream closes with zero bytes (#12398) ([#13285](https://github.com/diegosouzapw/OmniRoute/pull/13285)) +- fix(oauth): warn before the dead localhost:8080 redirect in antigravity/gemini `oauth start` (#12413) ([#13265](https://github.com/diegosouzapw/OmniRoute/pull/13265)) +- fix(providers): stop devin-cli spawn error from double-closing the SSE controller (#12517) ([#13261](https://github.com/diegosouzapw/OmniRoute/pull/13261)) +- **fix(rerank):** clamp Voyage `top_k` to the documents actually sent after empty-string filtering, and honor `return_documents: false` in the NVIDIA response adapter (#12523 — thanks @pacocartones) +- **fix(audio):** `/v1/audio/translations` now resolves combo names the way `/v1/audio/transcriptions` already does, so a combo that `GET /v1/models` advertises is fanned out to its targets instead of being rejected with `400 Invalid translation model: . Use format: provider/model`; literal `provider/model` ids and unknown bare names behave as before (#12536 — thanks @pacocartones) +- **fix(gemini):** strip the JSON-Schema-2020-12 `prefixItems` keyword from Gemini tool schemas at every nesting level, so Claude Code tool definitions no longer fail with `400 Unknown name "prefixItems"` on Gemini models (#12540 — thanks @pacocartones) +- **fix(api-manager):** Expose an accessible loading status while API keys are fetched instead of an empty accessibility tree (#12541 — thanks @pacocartones) +- **fix(video):** Clamp `estimateJpegFrameBytes` at zero for padding-only payloads and build the three encode-side frame data URIs from `JPEG_FRAME_DATA_URI_PREFIX` instead of a repeated literal (#12543 — thanks @pacocartones) +- **fix(devin):** accept Windows `DEVIN_AGENTIC_HOME` sandbox paths (`C:\...\.sandbox\...`) in the isolated-home check so the Devin Claude Bridge no longer fails closed on Windows ([#12405](https://github.com/diegosouzapw/OmniRoute/issues/12405)) (#12545 — thanks @pacocartones) +- **fix(db):** Add `conversation_turn_nodes` and orphaned `agentic_conversations` to the auto-cleanup cycle under the existing `retention.callLogs` window, so identity nodes whose call-log content has already been purged no longer accumulate without bound in `storage.sqlite` (#12548 — thanks @pacocartones) +- **fix(i18n):** Wrap the `~/.claude/profiles//settings.json` placeholder in ICU single quotes in the `OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES` feature-flag description across all 42 locales and the TypeScript default, so next-intl no longer fails with `INVALID_MESSAGE: UNCLOSED_TAG` and the Feature Flags card shows the description instead of the raw key (#12549 — thanks @pacocartones) +- **fix(orchestration):** `updateCloudAgentTask` now publishes the task's real `status` on `agent.task.updated` when an update only touches `result`, `activities` or `error`, instead of the fabricated `"updated"` state, and stays silent when no row matched the id (#12550 — thanks @pacocartones) +- **fix(i18n):** the home "Recent Requests" panel and the Provider Topology legend are now translated instead of rendering English copies on non-English dashboards; the legend reads its own `home.topologyLegend*` labels with consistent casing rather than borrowing the memory-settings "Recent" and analytics "Error" strings (#12551 — thanks @pacocartones). +- **docs(reference):** bring the `FEATURE_FLAGS.md` catalog back to 1:1 with `featureFlagDefinitions.ts` — 20 missing flags added, the two `*_BLOCK_THRESHOLD` env-only knobs moved out of the flag tables, category/total counts and the Live WS port corrected, guarded by a static test (#12552 — thanks @pacocartones) +- **fix(i18n):** backfill missing `usage.kiloPass*` strings in 39 locales and restore `featureFlagOmnirouteDisableThinkingLevelVariantsDescription` in `pt.json` ([#12561](https://github.com/diegosouzapw/OmniRoute/issues/12561)) ([#13027](https://github.com/diegosouzapw/OmniRoute/pull/13027)) — thanks @HouMinXi +- fix(docker): default docker-compose app ports (dashboard/API/live-WS) to loopback instead of `0.0.0.0`, closing the anonymous `/v1` LAN/WAN exposure gap left open by `REQUIRE_API_KEY=false` (#12568) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): close DNS-rebinding SSRF gap in webhook outbound-URL guard (#12569) ([#13243](https://github.com/diegosouzapw/OmniRoute/pull/13243)) +- fix(docker): require a per-session token on the VNC browser CDP bridge and isolate it on a dedicated Docker network (#12571) ([#13241](https://github.com/diegosouzapw/OmniRoute/pull/13241)) +- fix(open-sse): write Adobe Firefly session tokens and cookie jars with 0700/0600 permissions instead of the process umask (#12572) ([#13242](https://github.com/diegosouzapw/OmniRoute/pull/13242)) +- fix(api): remove hardcoded wildcard CORS in openai-to-gemini-sse.ts so the centralized fail-closed CORS gate is the sole source of `Access-Control-Allow-Origin` (#12573) ([#13239](https://github.com/diegosouzapw/OmniRoute/pull/13239)) +- fix(api): enforce API key policy (budget/rate-limit/schedule/endpoint scoping) on the ElevenLabs speech-to-text, text-to-speech and voices proxy routes (#12574) ([#13244](https://github.com/diegosouzapw/OmniRoute/pull/13244)) +- fix(sse): cap HuggingChat NDJSON body size and bound the read loop with the fetch timeout so a stalled or hostile upstream cannot buffer unbounded memory (#12577) ([#13282](https://github.com/diegosouzapw/OmniRoute/pull/13282)) +- fix(docker): scope the cliproxyapi/qdrant/bifrost sidecars to loopback by default and forward `CLIPROXYAPI_MANAGEMENT_KEY` into the cliproxyapi container so its management API is not left both unauthenticated and LAN-published (#12578) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): create DB export temp paths with `fs.mkdtempSync` instead of predictable timestamps (#12579) ([#13237](https://github.com/diegosouzapw/OmniRoute/pull/13237)) +- Cline 401 bodies that say "re-authenticate your Cline account" classify as a refreshable OAuth token, not a terminal expired key. The cooling panel no longer labels every cooldown as a 429; it shows the recorded last error instead. (#12594) ([#13060](https://github.com/diegosouzapw/OmniRoute/pull/13060)) — thanks @HouMinXi +- **fix(catalog):** degrade unknown combo targets instead of dropping LCD modalities, and persist OpenRouter `architecture.input_modalities` into the capability snapshot ([#12613](https://github.com/diegosouzapw/OmniRoute/issues/12613)) ([#13042](https://github.com/diegosouzapw/OmniRoute/pull/13042)) — thanks @HouMinXi +- fix(providers): send `x-api-key` instead of `Authorization: Bearer` for OpenCode Zen's `/v1/responses` endpoint (Muse Spark Contributor models), fixing a 401 on OmniRoute's auth header (#12633) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(sse): surface the actionable "Auggie CLI not found" message when the shell reports a missing `auggie` binary via exit code instead of a spawn error (#12645) ([#13263](https://github.com/diegosouzapw/OmniRoute/pull/13263)) +- **fix(config):** Persist deletions of built-in background-degradation entries — when a stored settings record exists its `degradationMap` is now authoritative instead of being merged under the defaults, so an entry the user removed in the dashboard no longer reappears on the next apply or restart ([#12424](https://github.com/diegosouzapw/OmniRoute/issues/12424)) ([#12647](https://github.com/diegosouzapw/OmniRoute/pull/12647)) — thanks @pacocartones +- **fix(gamification):** action-count badge milestones (First Token, Token Consumer, Token Machine, Token Whale, and the token-sharing tier) are now backed by a durable `xp_action_counts` counter incremented in `addXp()`, instead of a live `COUNT(*)` over `xp_audit_log`. The audit log is pruned by `retention.xpAuditLog` (default 30 days), so on a default install those "lifetime" milestones were really "actions in the last 30 days" and unlocked badges could stop unlocking once old rows aged out. `getActionCount()` and `checkActionCountBadges()` now read the same durable source, and a migration backfills existing totals from the surviving audit rows ([#12546](https://github.com/diegosouzapw/OmniRoute/issues/12546)) ([#12651](https://github.com/diegosouzapw/OmniRoute/pull/12651)) — thanks @pacocartones +- **fix(images):** `/v1/images/edits` now iterates a combo's targets the same way `/v1/images/generations` does (#9239) instead of flattening a bare combo to its first target. A combo whose first target is not edit-capable — or lacks credentials — now falls through to a later edit-capable target rather than hard-erroring, and missing credentials are skipped (not a hard `401`) to match the generations path. The per-target skip/terminal classification is extracted into a shared `runImageComboTargets` loop, so generations behavior is unchanged ([#12547](https://github.com/diegosouzapw/OmniRoute/issues/12547)). ([#12653](https://github.com/diegosouzapw/OmniRoute/pull/12653)) — thanks @pacocartones +- fix(sse): add first-byte watchdog to the TLS-fingerprint transport so a stalled wreq body falls back instead of hanging for minutes (#12656) ([#13272](https://github.com/diegosouzapw/OmniRoute/pull/13272)) +- fix(sse): exempt tiny-budget reasoning probes from combo quality failure and surface persisted-cooldown skips in ALL_TARGETS_SKIPPED diagnostics (#12659) ([#13269](https://github.com/diegosouzapw/OmniRoute/pull/13269)) +- fix(models): declare the real ~1M contextLength for OpenCode Zen's Muse Spark 1.2 models instead of falling back to the 200000 provider default (#12681) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(oauth): align codebuddy-cn OAuth User-Agent with the chat/usage CLI version to avoid WAF false positives (#12702) ([#13264](https://github.com/diegosouzapw/OmniRoute/pull/13264)) +- fix(dashboard): surface an authentication-required banner instead of silently blanking database settings for a guest session (#12709) ([#13270](https://github.com/diegosouzapw/OmniRoute/pull/13270)) +- **fix(resilience):** stop unbounded queue that hangs 6min until Aborted — gate, provider slot, and Bottleneck queue now share a per-connection `maxWaitMs` budget; `fail-closed` on exhaust (503); execution backstop `executionMaxWaitMs` overridable per connection with upstream clamp ([#12715](https://github.com/diegosouzapw/OmniRoute/pull/12715)) — thanks @maxmad64bis (with thanks to @Tushar49 for surfacing the slow-provider need in #12635) +- **fix(auto-combo):** every mode pack now carries `quality` and `reliability` weights (`quality` 0.02, 0.03 in `quality-first`; `reliability` 0.03, 0.04 in `reliability-first`), so selecting a pack no longer silences either signal; `DEFAULT_WEIGHTS` is unchanged ([#12731](https://github.com/diegosouzapw/OmniRoute/pull/12731)) — thanks @maxmad64bis +- **fix(sse):** the provider execution pipeline keeps the upstream error `code`/`type` again — both were lost when the non-streaming leg was extracted, so a config-class `422` (Antigravity missing project) degraded into a generic account cooldown instead of reaching the gate that recognises it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(db):** a legacy `call_logs` table no longer aborts startup — the provider-stats index is created after the column healing runs, not before it, so an install predating `request_type` opens instead of failing with `no such column` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(catalog):** a malformed row in the operator-writable custom-models blob no longer kills every `auto/*` pool ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(tests):** realign the guards that the same day's merges left asserting the old behavior — the provider-count total after GreenPT and EURouter, the GLM stream buffer slot that `#12925` turned into a declared parameter, and the injection scan bound that `#13104` reshaped from a head-only window into head-plus-tail ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- fix(cache): fold tool_choice/tools/response_format into the semantic cache signature so a cached tool_calls response can no longer be replayed for a request whose tool policy forbids it (#12734) ([#13267](https://github.com/diegosouzapw/OmniRoute/pull/13267)) +- **fix(models):** stop treating provider-supplied `isFree:true`/`:free`/`0/0` flags as trusted unless the provider has a documented free tier — only the shipped free-tier catalog decides otherwise; fetched `isFree:true` trusted only for a free-tier provider, custom `isFree:true` via a trusted path ([#12744](https://github.com/diegosouzapw/OmniRoute/pull/12744)) — thanks @maxmad64bis +- fix(memory): authenticate the internal /v1/rerank loopback call so memory reranking no longer silently degrades to unranked order when REQUIRE_API_KEY=true (#12745) ([#13268](https://github.com/diegosouzapw/OmniRoute/pull/13268)) +- fix(sse): parse Ollama Cloud's current usage markup (`$X of $Y used` aria-label, nested width style) (#12749) ([#13260](https://github.com/diegosouzapw/OmniRoute/pull/13260)) +- fix(cli): setup-opencode no longer sends an active context's management token to `/v1/models` when `--api-key`/`OMNIROUTE_API_KEY` is supplied — an explicit flag or the env var now always outranks the context's token, and the flag itself is no longer swallowed by the parent program's global `--api-key` option (#12783) ([#13246](https://github.com/diegosouzapw/OmniRoute/pull/13246)) +- fix(sse): register Arcee AI in the executor provider registry so requests reach api.arcee.ai instead of silently falling back to OpenAI (#12784) ([#13277](https://github.com/diegosouzapw/OmniRoute/pull/13277)) +- **fix(quota):** share one upstream quota read when identical saturation checks arrive at the same time, so traffic bursts don't multiply provider API calls ([#12787](https://github.com/diegosouzapw/OmniRoute/pull/12787)) — thanks @maxmad64bis +- **fix(db):** ignore expired or invalid rate-limit cooldown writes so a stale timestamp can't lock a connection that should be usable — clearing still works as before ([#12788](https://github.com/diegosouzapw/OmniRoute/pull/12788)) — thanks @maxmad64bis +- **fix(providers):** single source for provider order with `xao` ranking alongside `xai-oauth` ([#12790](https://github.com/diegosouzapw/OmniRoute/pull/12790)) — thanks @maxmad64bis +- **fix(routing):** off-table models check the free-model catalog before inheriting premium prices, latency bootstraps from the observed pool median, async tiers read live database pricing with a 90-day freshness gate, and the tier cache invalidates on every pricing write ([#12792](https://github.com/diegosouzapw/OmniRoute/pull/12792)) — thanks @maxmad64bis +- **fix(auto-combo):** snapshot scoring uses the observed per-connection account tier and per-model quality instead of neutral constants, and circuit-open providers rank lower at build time ([#12794](https://github.com/diegosouzapw/OmniRoute/pull/12794)) — thanks @maxmad64bis +- **fix(pool):** empty `auto/*` pools now say why they are empty, and the models listing uses the same paid check as routing ([#12795](https://github.com/diegosouzapw/OmniRoute/pull/12795)) — thanks @maxmad64bis +- fix(routing): recognize CLIProxyAPI's 'unknown provider for model' 400 as fallback-worthy (#12800) ([#13284](https://github.com/diegosouzapw/OmniRoute/pull/13284)) +- **fix(sse):** translate-mode streams now emit the estimated token counts as a trailing usage-only chunk before `[DONE]` when the upstream stays silent, so metered chat clients see totals instead of nothing ([#12828](https://github.com/diegosouzapw/OmniRoute/pull/12828)) — thanks @maxmad64bis +- **fix(db):** provider stats stay truthful on empty and legacy databases: fallback counts default to `0` instead of `null`, latency averages read `null` (not `0`) when no durations were recorded, failures logged before the error-type column existed group under `pre_migration` instead of `unclassified`, and per-provider queries use two new composite indexes ([#12832](https://github.com/diegosouzapw/OmniRoute/pull/12832)) — thanks @maxmad64bis +- fix(nvidia): fail open when a synced model catalog goes stale instead of gating forever (#12849) ([#13248](https://github.com/diegosouzapw/OmniRoute/pull/13248)) +- **fix(db):** WAL maintenance lives in its own module: a `TRUNCATE` checkpoint that hits a busy database now warns with its streak and retries once via `PASSIVE` instead of logging success, and closing-time checkpoints no longer report success on builds without a database file, with the busy totals visible in the authenticated monitoring health payload ([#12853](https://github.com/diegosouzapw/OmniRoute/pull/12853)) — thanks @maxmad64bis +- **fix(health):** quota with no snapshots reads empty instead of a contradicting 0% ([#12857](https://github.com/diegosouzapw/OmniRoute/pull/12857)) — thanks @maxmad64bis +- fix(a2a): accept the dashboard's own session cookie on /a2a so "Run message/send" no longer fails with "Unauthorized: missing or invalid API key" (#12888) ([#13271](https://github.com/diegosouzapw/OmniRoute/pull/13271)) +- **fix(logs):** the Logs grid's in-memory filter pass no longer discards rows the SQL query already matched — selecting an API key from the dropdown (which sends the key's id) returns its calls again, the Combo tab shows every combo instead of only those whose name contains a "1", and the model filter and search cover the same columns as the query ([#12896](https://github.com/diegosouzapw/OmniRoute/pull/12896)) — fixes [#12873](https://github.com/diegosouzapw/OmniRoute/issues/12873) — thanks @ntdatt812 +- **fix(a2a):** `/api/a2a/status` now builds the agent card from the request that asked for it, so a gateway reached at a non-localhost host no longer advertises `http://localhost:20128` as its A2A URL ([#12918](https://github.com/diegosouzapw/OmniRoute/pull/12918)). — thanks @ntdatt812 +- **fix(compression):** progressive aging now appends its `[COMPRESSED:aging:…]` annotation after a turn's `tool_result` blocks instead of in front of them, so Anthropic no longer rejects aged conversations with "`tool_use` ids were found without `tool_result` blocks immediately after" ([#12920](https://github.com/diegosouzapw/OmniRoute/pull/12920)). — thanks @ntdatt812 +- **fix(bedrock):** model import now resolves context limits for every vendor prefix instead of only `anthropic.*`, so `global.openai.gpt-5.6-*` no longer imports with a null `inputTokenLimit` and gets rejected pre-flight at the 200k default ([#12921](https://github.com/diegosouzapw/OmniRoute/pull/12921)). — thanks @ntdatt812 +- **fix(stream):** the 64 KB stream buffer GLM asks for is honoured instead of dropped, and the type error it caused no longer fails the API Route Typecheck gate on every open PR ([#12925](https://github.com/diegosouzapw/OmniRoute/pull/12925)) — thanks @ntdatt812 +- **fix(guardrails):** mask PII inside a `tool_result`'s nested content array, which the masker walked past while redacting its sibling block ([#12930](https://github.com/diegosouzapw/OmniRoute/pull/12930)) — thanks @ntdatt812 +- **fix(dashboard):** the Radar catalog table no longer leaves absent data unexplained — empty limits, unknown context windows, and unreported capabilities each explain themselves on hover, and a new check keeps it that way ([#12937](https://github.com/diegosouzapw/OmniRoute/pull/12937)) — thanks @maxmad64bis +- **fix(sse):** geo-blocked opencode requests rotate to the next account proxy instead of failing, so one refused egress no longer aborts the whole chain ([#12941](https://github.com/diegosouzapw/OmniRoute/pull/12941)) — thanks @maxmad64bis +- **fix(codex):** dashboard "clear cooldown" and the CAS recovery path now drop nested `codexScopeRateLimitedUntil` maps in the same write that nulls `rate_limited_until`, and fresh quota snapshots with headroom lift fallback-sourced scope cooldowns parked by quota preflight ([#12817](https://github.com/diegosouzapw/OmniRoute/issues/12817), [#12860](https://github.com/diegosouzapw/OmniRoute/issues/12860), [#12951](https://github.com/diegosouzapw/OmniRoute/pull/12951)) The dashboard reset-credit button (`consumeCodexResetCredit`) now exercises that same snapshot path after a successful redeem, so a filled quota bar is enough to unpark a leftover fallback Codex child without another manual clear. — thanks @HouMinXi +- **fix(db):** resolve `sql-wasm.wasm` across global npm install and hoisted layouts, ensuring OmniRoute can boot cleanly on Node 24 when native `better-sqlite3` is uncompiled. ([#13035](https://github.com/diegosouzapw/OmniRoute/pull/13035)) — thanks @HouMinXi +- **fix(api):** `DELETE /v1/batches/delete-completed` now sweeps only the calling API key's own completed batches (batches with no owner stay out of a key-scoped sweep on purpose), with an explicit instance-wide mode reserved for authenticated dashboard sessions, a 401 for a presented key that is unknown, revoked, deactivated, banned or expired (never falling through to the session branch), audit logging of every sweep, a sanitized 500 on failure, an owner-scoped file half (a key-scoped sweep never nulls a file another tenant owns) and an atomic sweep — chunked in 200-batch transactions in instance mode — so a mid-way error never leaves a batch pointing at a nulled file (GHSA-wvxc-jp3v-5mg5) ([#12969](https://github.com/diegosouzapw/OmniRoute/pull/12969)) +- **fix(combo):** quota-weighted routing stops drawing on an out-of-credit connection — a 402 now invalidates the stored quota snapshot instead of leaving its stale remaining percentage in place, and a snapshot older than 10 minutes no longer counts as confident headroom for the primary pool ([#12972](https://github.com/diegosouzapw/OmniRoute/pull/12972)) ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- **fix(vision):** Custom Models with "Vision capable" checked no longer have image requests swapped to `glm/glm-4.6v` when the client sends the advertised alias (`vllm/path/...`) or the bare path-shaped id — Vision Bridge now matches the stored override for all three id forms ([#12758](https://github.com/diegosouzapw/OmniRoute/issues/12758)) ([#12974](https://github.com/diegosouzapw/OmniRoute/pull/12974)) — thanks @HouMinXi +- **fix(sse):** transient opencode upstream failures rotate to the next account proxy instead of failing, so one flapping egress no longer aborts the whole chain ([#12975](https://github.com/diegosouzapw/OmniRoute/pull/12975)) — thanks @maxmad64bis +- **fix(azure):** Deployments from GPT-6 onward now send `max_completion_tokens` instead of `max_tokens`, which Azure rejects with HTTP 400. The rule matched a literal `gpt-5`, so each new generation arrived broken; it now matches the generation range, while `gpt-35-turbo` still keeps `max_tokens`. ([#13007](https://github.com/diegosouzapw/OmniRoute/pull/13007)) — thanks @ntdatt812 +- **fix(db):** add an opt-in self-restart circuit for sustained critical memory pressure, gate post-cleanup VACUUM behind a minimum freed-rows threshold, and checkpoint the SQLite WAL every 5 minutes with a size guard that escalates to TRUNCATE, so a growing WAL can no longer stall the event loop into a full outage. ([#13011](https://github.com/diegosouzapw/OmniRoute/pull/13011)) — thanks @HouMinXi +- **fix(auth):** an explicit connection pin may probe a quota-disabled row once and re-enable it on success ([#12874](https://github.com/diegosouzapw/OmniRoute/issues/12874)) ([#13017](https://github.com/diegosouzapw/OmniRoute/pull/13017)) — thanks @HouMinXi +- **fix(api):** thread `X-OmniRoute-Fallback-Attempts` through combo chat completions so streaming and non-streaming responses report how many prior legs were attempted ([#13038](https://github.com/diegosouzapw/OmniRoute/pull/13038)) — thanks @HouMinXi +- Restore provider failure classification and credential refresh on non-streaming requests: classify non-2xx failures to lock models on per-model quota exhaustion, update connection rate limits from headers and body, and pass credential refresh handlers to pipeline execution so 401 tokens can be refreshed and retried (#13043). ([#13069](https://github.com/diegosouzapw/OmniRoute/pull/13069)) — thanks @HouMinXi +- **fix(responses):** wrap forced-non-streaming web_search fallback JSON as Responses SSE so Codex still sees `response.completed` ([#13050](https://github.com/diegosouzapw/OmniRoute/pull/13050)) — thanks @HouMinXi +- **fix(antigravity):** Preserve upstream thought-token usage in normalized completion and reasoning token counts ([#13055](https://github.com/diegosouzapw/OmniRoute/pull/13055)) — thanks @pacocartones +- Fix a concurrency-slot leak in the DeepSeek PoW solver: a worker that failed to spawn (for example a missing worker script) never released its slot, so `MAX_CONCURRENT_WORKERS` failures disabled the solver until restart. ([#13097](https://github.com/diegosouzapw/OmniRoute/pull/13097)) — thanks @anhtahaylove +- **fix(acp):** bound the ACP session output buffers — `stdoutBuffer` and `stderrBuffer` now cap at 1 MiB keeping the most recent output behind a visible `[...output truncated...]` marker, and `stderrBuffer` is reset per prompt instead of accumulating for the lifetime of the session. ([#13100](https://github.com/diegosouzapw/OmniRoute/pull/13100)) — thanks @anhtahaylove +- **fix(acp):** release the `stdout`/`exit` listeners and the idle timer that a `sendPrompt` timeout used to leave attached to the `acpManager` singleton, and drop sessions that exited on their own from the session map instead of keeping them forever. ([#13096](https://github.com/diegosouzapw/OmniRoute/pull/13096)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection and PII scanners now read the text a `tool_result` block carries on `content` (string or nested block list), in messages and in system blocks, so tool output is judged by the same rules as user text ([#13101](https://github.com/diegosouzapw/OmniRoute/pull/13101)) — thanks @ntdatt812 +- **fix(gamification):** close the badge notification SSE stream when the request signal is already aborted before the stream starts — a client that disconnects while the route is still awaiting auth used to leave both the 2s unlock poll and the 15s heartbeat running for the lifetime of the process. ([#13106](https://github.com/diegosouzapw/OmniRoute/pull/13106)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection scan now spends its 16 KB budget on both ends of the request instead of the first 16 KB only, so `system`, `instructions`, `query`, `documents` and the newest turns are no longer hidden behind one long message ([#13104](https://github.com/diegosouzapw/OmniRoute/pull/13104)) — thanks @ntdatt812 +- **fix(dashboard):** expose the Volcano Ark console cookie on quota scraping and unwrap connect-error objects so the dashboard shows the upstream message ([#13107](https://github.com/diegosouzapw/OmniRoute/pull/13107)) — thanks @HouMinXi +- **fix(db):** release the `beforeExit`/`SIGINT`/`SIGTERM` handlers when a `node:sqlite` adapter closes, so a closed adapter and its database handle are no longer pinned to `process` for the lifetime of the run — the same treatment #7494 gave the sql.js adapter. ([#13109](https://github.com/diegosouzapw/OmniRoute/pull/13109)) — thanks @anhtahaylove +- **fix(translator):** `contentSchema` and `unevaluatedItems` are now treated as subschema positions by the tool-schema sanitizer, so a truncation placeholder in either is replaced with a permissive schema instead of being forwarded as a string ([#13110](https://github.com/diegosouzapw/OmniRoute/pull/13110)) — thanks @ntdatt812 +- **fix(cli-helper):** clear the `createLogStream` timeout on the abort path — `stop()` aborts the in-flight fetch and returned through the `signal.aborted` branch, which skipped `clearTimeout` and left an armed timer per stopped stream. The stream reader is now also cancelled when the read loop exits early. ([#13114](https://github.com/diegosouzapw/OmniRoute/pull/13114)) — thanks @anhtahaylove +- **fix(docs):** drop TLS-impersonation, MITM-decrypt, supply-chain attestation, and XOR-mask writeups from the public `/docs` catalog and Docker image. Files stay in git for engineers; operators who need them open the repo, not the website. ([#13136](https://github.com/diegosouzapw/OmniRoute/pull/13136)) — thanks @HouMinXi +- **fix(combo):** parse numeric-epoch `rate_limited_until` in the combo cooldown read path ([#13141](https://github.com/diegosouzapw/OmniRoute/pull/13141)) — thanks @maxmad64bis +- **fix(opencode):** both OpenCode plugins now reject a gateway address typed without `http://` at configuration time, instead of publishing every model with an api url no client can call, and the v2 plugin no longer publishes a model card whose api url is blank or relative ([#13142](https://github.com/diegosouzapw/OmniRoute/pull/13142)) — thanks @maxmad64bis +- **fix(providers):** lock opencode model on upstream 400 model-unavailable ([#13146](https://github.com/diegosouzapw/OmniRoute/pull/13146)) — thanks @maxmad64bis +- **fix(logging):** keep the provider exchange rather than the raw client bodies when a call log exceeds its size budget, and show that recovered payload in the request-detail panel instead of replacing it with the stored response body ([#13147](https://github.com/diegosouzapw/OmniRoute/pull/13147)) — thanks @maxmad64bis +- **fix(traffic-inspector):** the WebSocket route no longer leaks a traffic-buffer subscriber and a 30s ping timer when the client socket is already closed at handler time — listeners are attached before any resource is acquired, a destroyed socket bails out early, and the ping interval stops on a dead socket where `write()` never throws ([#13155](https://github.com/diegosouzapw/OmniRoute/pull/13155)) — thanks @anhtahaylove +- **fix(telegram):** bound the per-user API key cache in the Telegram chat proxy so a burst of distinct chat ids can no longer grow the process heap without limit ([#13165](https://github.com/diegosouzapw/OmniRoute/issues/13165)) ([#13166](https://github.com/diegosouzapw/OmniRoute/pull/13166)) — thanks @anhtahaylove +- **fix(stream):** cancel the upstream response body when the JSON-to-SSE sniff unwinds on a body timeout, so a stalled upstream no longer pins the connection ([#13169](https://github.com/diegosouzapw/OmniRoute/issues/13169)) ([#13171](https://github.com/diegosouzapw/OmniRoute/pull/13171)) — thanks @anhtahaylove +- **fix(telegram):** authenticate webhook deliveries with Telegram's `secret_token` so an unauthenticated caller can no longer mint API keys or spend upstream quota ([#13172](https://github.com/diegosouzapw/OmniRoute/issues/13172)) ([#13175](https://github.com/diegosouzapw/OmniRoute/pull/13175)) — thanks @anhtahaylove +- **fix(models):** give discoverable Gemini 3.8 Flash ids their own 65536 output spec so Antigravity no longer clamps them to 16384 ([#13195](https://github.com/diegosouzapw/OmniRoute/pull/13195)) — thanks @HouMinXi +- **fix(dashboard):** leftover catalog-removed provider rows (gemini-cli) can be listed and purged from the providers page ([#13067](https://github.com/diegosouzapw/OmniRoute/issues/13067)) ([#13197](https://github.com/diegosouzapw/OmniRoute/pull/13197)) — thanks @HouMinXi +- **fix(auth):** a dashboard session now requires the `authenticated: true` claim that login, OIDC and the session refresh already emit — a JWT merely signed with `JWT_SECRET` (for example the Cursor CLI passthrough token, which any API-key holder can obtain) no longer verifies as the `auth_token` cookie on any route, the WebSocket handshake or the live server; existing sessions keep working ([#13298](https://github.com/diegosouzapw/OmniRoute/issues/13298)) ([#13375](https://github.com/diegosouzapw/OmniRoute/pull/13375)) +- **fix(api):** `DELETE /v1/batches/delete-completed` now applies the caller's API-key policy (endpoint allowlist, schedule, usage cap, rate limit) like every other `/v1` route, commits the key-scoped sweep per 200-batch chunk instead of holding one write lock for the whole sweep, refuses to loop without progress, and rejects a scope that names both a key and `allTenants` ([#13684](https://github.com/diegosouzapw/OmniRoute/pull/13684)) +- Restore the API-route typecheck gate: the non-streaming leg result lost its discriminated-union narrowing after the server-owned tool loop reassignment, producing 13 new TS2339 diagnostics in `chatCore.ts`. ([#12963](https://github.com/diegosouzapw/OmniRoute/pull/12963)) +- Restore four non-streaming behaviours the server-owned tool loop refactor dropped: client aborts map to 499 with the fixed `Request aborted` message, an aborted request no longer logs a synthetic `clientResponse`, Claude prompt-cache telemetry is recorded again, and a body that cannot be canonicalized no longer throws when the tool loop is off. ([#12990](https://github.com/diegosouzapw/OmniRoute/pull/12990)) +- **fix(skills):** The CLI registry parser now reads positionals declared with `.addArgument()`, not only those written inline in `.command()`. `tunnel create [type]` was being published as `tunnel create`, so the agent-skills sync gate reported drift on every branch and regenerating would have deleted the argument. ([#13009](https://github.com/diegosouzapw/OmniRoute/pull/13009)) — thanks @ntdatt812 +- **fix(combos):** dashboard combo test uses a short prompt, serial probes, and a 60s timeout so reasoning models and rate-limited free pools do not fail the health check ([#13001](https://github.com/diegosouzapw/OmniRoute/pull/13001)) — thanks @HouMinXi +- fix(compression): terminate idle worker threads on eviction so long-running instances stop leaking OS threads and MessagePorts ([#13091](https://github.com/diegosouzapw/OmniRoute/pull/13091)) — thanks @anhtahaylove +- Redact Google API keys of any length in error bodies: the pattern required exactly 39 characters, so shorter or longer `AIza…` credentials passed through unredacted. ([#12964](https://github.com/diegosouzapw/OmniRoute/pull/12964)) +- **fix(grok-cli):** a 402 "Grok Build usage balance exhausted" parks that Grok login as out of credit (Grok Build CLI, grok.com cookie, and xAI OAuth share the weekly pool). Combo routing then tries the next login instead of locking the model for every account in the pool ([#13061](https://github.com/diegosouzapw/OmniRoute/pull/13061)) — thanks @HouMinXi +- fix(compression): spawn the LLMLingua worker with a file URL object so compression actually runs instead of silently failing open on Node ([#13093](https://github.com/diegosouzapw/OmniRoute/pull/13093)) — thanks @anhtahaylove +- **fix(test):** run the local `test` and `test:unit` scripts at concurrency 4 so a full-suite run no longer exhausts the machine's commit charge and kills unrelated processes ([#13187](https://github.com/diegosouzapw/OmniRoute/pull/13187)) — thanks @anhtahaylove +- **fix(monitoring):** `GET /api/monitoring/health` `credentialHealth` now includes a bounded `failedConnections` list (`connectionId`, `status`, sanitized `lastError`) when the probe-cache gauge `failed>0`, plus `source: probe-cache` and a cheap `staleDbNonOkCount` for sticky SQLite `test_status` on active rows. Documents that the live gauge is not `provider_connections.test_status`. ([#12876](https://github.com/diegosouzapw/OmniRoute/pull/12876)) — thanks @RaviTharuma +- **fix(resilience):** an openai-compatible multi-upstream gateway no longer marks the whole connection `credits_exhausted` when a single upstream (or the `/models` / representative-model health probe) returns 402. Quota failures stay model-scoped; true connection-wide auth failures are unchanged. Single-credential 402 key disable ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239)) is preserved. ([#12875](https://github.com/diegosouzapw/OmniRoute/pull/12875)) — thanks @RaviTharuma +- **fix(dashboard):** nine fixes on the Orchestration Canvas, all diagnosed in the Phase 2 reviews. A source that fails now keeps the timestamp of its FIRST failure instead of being re-stamped every poll — the stale line said "since the last poll" no matter how long the source had been down, and the churn also defeated the snapshot's stable identity (it serializes the source list), so the canvas re-rendered on every tick while anything was broken. A source that HAD data and only then started failing is flagged too: previously only a source with no node at all got the warning, so a source that went down mid-session kept a healthy-looking node forever. The rest are pointwise: clicking a filter chip cancels the pending search debounce (left armed, it fired ~300ms later and silently reverted the chip); the search input carries an accessible name; `?state=running, failed` parses like the unpadded form instead of dropping the padded value; the CSV toggle helper is defined once in `model/urlParams.ts` rather than twice; the agents tab tells "nothing running" apart from "the filter matched nothing", offering a clear-filters button instead of setup links that would be wrong advice there; edges stop emitting SMIL particles above 40 simultaneously active edges, keeping the colored stroke; and the drawer's error banner clears when a retried action succeeds. ([#12983](https://github.com/diegosouzapw/OmniRoute/pull/12983)) +- **fix(i18n):** the nine locales added with the EU-language batch (Greek, Estonian, Irish, Croatian, Lithuanian, Latvian, Maltese, Slovenian, Serbian) were missing the eleven Orchestration Canvas keys that Phase 3 introduced, so the compare-runs panel and the "no runs match these filters" empty state fell back to English in those languages (`deepMergeFallback` substitutes English for an absent key, so nothing rendered blank — it rendered untranslated). The coverage gate does not catch this: it enforces an 80% floor per locale, and eleven missing keys out of ~13,000 leaves coverage at 99.9%. Translated for real in each language, calibrated against the wording each file already uses for "run", "filter" and "skill". ([#13294](https://github.com/diegosouzapw/OmniRoute/pull/13294)) +- **fix(dashboard):** five follow-ups from the Phase 2 Orchestration Canvas review. The History drawer now shows the memory section for runs that already left the live TTL window: the persisted `memory_hits` event is parsed into `metadata.memoryHits` with the same defensive validation the drawer applies, and — because that event is observability rather than a state transition — it no longer leaks into the timeline, where it had been inheriting the task's state and rendering as a duplicate transition. The A2A memory recall runs against its own 1.5s deadline instead of inheriting the memory backend's 30s one; overshooting degrades exactly like any other recall failure (no hits, task proceeds), and the timer is cleared on both paths. Repeating a Conductor run carries its `requirements.cli`/`requirements.model` forward, so the new run is pinned to the same runner profile and model rather than drifting to whatever the fleet picks. A successful repeat from the Agents tab now focuses the run it created, instead of leaving the operator on the finished one. And the auth test for `POST /api/conductor/tasks` moved into the shared `ROUTES` array rather than restating the pattern. ([#12988](https://github.com/diegosouzapw/OmniRoute/pull/12988)) +- fix(plugins): stop leaking an exit listener per plugin hook timeout, which triggered MaxListenersExceededWarning on plugins that ignore SIGTERM ([#13092](https://github.com/diegosouzapw/OmniRoute/pull/13092)) — thanks @anhtahaylove +- **fix(validation):** Provider node edits no longer fail with a generic "Invalid request" when the optional daily-quota reset fields are left blank. The dashboard sends `dailyQuotaResetTimezone` and `dailyQuotaResetHour` as `null`, and only the hour accepted it. ([#13066](https://github.com/diegosouzapw/OmniRoute/issues/13066)) ([#13083](https://github.com/diegosouzapw/OmniRoute/pull/13083)) — thanks @ntdatt812 +- **fix(combo):** quota-aware expansion drops banned, inactive, missing, and wrong-provider connections before quota fetch or model dispatch; pins and allowlists stay selectors, not a bypass. Antigravity automatic exhaustion now requires a reported zero remaining, so a positive balance below 1% stays eligible. ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- Fix an intermittent failure in the quota-weighted routing test suite: scores are a function of `Date.now()`, so peers with identical quota scored microseconds apart never tied and swapped order. ([#12946](https://github.com/diegosouzapw/OmniRoute/pull/12946)) +- fix(volcengine): resolve quota window mapping gaps, safe multi-connection console binding, and response cookie sanitization ([#12950](https://github.com/diegosouzapw/OmniRoute/pull/12950)) — thanks @HouMinXi +- **fix(test):** resolve the WebDAV handler path with `fileURLToPath` so the suite's 37 WebDAV tests run on Windows instead of failing with a doubled `C:\C:\` drive prefix ([#13196](https://github.com/diegosouzapw/OmniRoute/pull/13196)) — thanks @anhtahaylove +- **fix(ui):** fall back from invalid AnySearch icon alias (direct commit `9587e07b69`) +- **fix(sse):** stop rebuilding a truncated summary from the collector's cap-dropped event array ([#12718](https://github.com/diegosouzapw/OmniRoute/pull/12718)) — thanks @hartmark +- **fix(conversations):** show a pending spinner for unresolved tool nodes instead of "(empty)" ([#12727](https://github.com/diegosouzapw/OmniRoute/pull/12727)) — thanks @hartmark +- **fix(streaming):** fail fast when an upstream stream produces only lifecycle/heartbeat events, never real content ([#12741](https://github.com/diegosouzapw/OmniRoute/pull/12741)) — thanks @hartmark +- **fix(responses-continuation):** bridge the write-in-flight window with an in-memory pending store ([#12854](https://github.com/diegosouzapw/OmniRoute/pull/12854)) — thanks @hartmark +- **fix(translator):** drop or map agent_message on Chat Completions fallback ([#12880](https://github.com/diegosouzapw/OmniRoute/pull/12880)) — thanks @RaviTharuma +- **fix(api):** page and stream-complete GET /v1/models for large catalogs ([#12882](https://github.com/diegosouzapw/OmniRoute/pull/12882)) — thanks @RaviTharuma +- **fix(quota):** align AUTH window labels with usage API and clarify 503 ([#12884](https://github.com/diegosouzapw/OmniRoute/pull/12884)) — thanks @RaviTharuma +- **fix(ci):** clear two base-reds on release/v3.8.51 — mutation-coverage gate + image-only-model guard ([#12945](https://github.com/diegosouzapw/OmniRoute/pull/12945)) +- **fix(api):** scope batch bulk-delete to the calling API key ([#13211](https://github.com/diegosouzapw/OmniRoute/pull/13211)) +- **fix(tests):** align model-sync log assertion to shipped path redaction (base-red) ([#13227](https://github.com/diegosouzapw/OmniRoute/pull/13227)) +- **fix(test):** make the ToS heading guard actually require the parentheses ([#13228](https://github.com/diegosouzapw/OmniRoute/pull/13228)) +- **fix(quality):** register 4 drifted covering tests in stryker tap.testFiles ([#13229](https://github.com/diegosouzapw/OmniRoute/pull/13229)) +- **fix(logging):** mask *-api-key headers in request-log pipeline (#13273) ([#13401](https://github.com/diegosouzapw/OmniRoute/pull/13401)) — thanks @KooshaPari +- **fix(tests):** correct stale Jina catalog prefix in custom-models test ([#13403](https://github.com/diegosouzapw/OmniRoute/pull/13403)) — thanks @KooshaPari +- **fix(backend):** prune old backups after health-check-repair snapshots (#13308) ([#13404](https://github.com/diegosouzapw/OmniRoute/pull/13404)) — thanks @KooshaPari +- **fix(backend):** guard memory_fts_au trigger with WHEN clause to stop FTS bloat (#13326) ([#13405](https://github.com/diegosouzapw/OmniRoute/pull/13405)) — thanks @KooshaPari +- **fix(free-tiers):** remove obsolete Together $25 signup credit (#12526) ([#13407](https://github.com/diegosouzapw/OmniRoute/pull/13407)) — thanks @KooshaPari +- **fix:** skip non-chat generation models in Test all models ([#13409](https://github.com/diegosouzapw/OmniRoute/pull/13409)) — thanks @KooshaPari +- **fix(providers):** de-duplicate agy and antigravity model catalogs ([#13410](https://github.com/diegosouzapw/OmniRoute/pull/13410)) — thanks @KooshaPari +- **fix(dashboard):** send provider in body for mini-playground web search ([#13411](https://github.com/diegosouzapw/OmniRoute/pull/13411)) — thanks @KooshaPari +- **fix(resilience):** add CJK quota-exhaustion patterns to 429 classifier ([#13412](https://github.com/diegosouzapw/OmniRoute/pull/13412)) — thanks @KooshaPari +- **fix(backend):** eval runner: failed calls no longer score as passed + dotAll regex ([#13413](https://github.com/diegosouzapw/OmniRoute/pull/13413)) — thanks @KooshaPari +- **fix(backend):** eval runner opts out of output-style and memory injection ([#13414](https://github.com/diegosouzapw/OmniRoute/pull/13414)) — thanks @KooshaPari +- **fix:** chain serial quarantine step in npm test script (fixes #13286) ([#13418](https://github.com/diegosouzapw/OmniRoute/pull/13418)) — thanks @KooshaPari +- **fix(antigravity):** derive stable session ID from credentials for prompt-cache reuse (#13305) ([#13424](https://github.com/diegosouzapw/OmniRoute/pull/13424)) — thanks @KooshaPari +- **fix(memory):** merge memory into top-level system field instead of unshifting at messages[0] (#13425) ([#13427](https://github.com/diegosouzapw/OmniRoute/pull/13427)) — thanks @KooshaPari +- **fix(compression):** preserve instruction blocks from lossy engines ([#13523](https://github.com/diegosouzapw/OmniRoute/pull/13523)) — thanks @KooshaPari +- **fix(compression):** preserve polarity words and newlines in ultra heuristic ([#13524](https://github.com/diegosouzapw/OmniRoute/pull/13524)) — thanks @KooshaPari +- **fix(compression):** run user preservePatterns before built-in patterns (#13457) ([#13525](https://github.com/diegosouzapw/OmniRoute/pull/13525)) — thanks @KooshaPari +- **fix(reasoning):** expand deepseek-v4 regex to match v4.1+ and add command-code provider (#13430) ([#13528](https://github.com/diegosouzapw/OmniRoute/pull/13528)) — thanks @KooshaPari +- **fix(providers):** add alwaysReasons metadata for reka-flash-3 ([#13531](https://github.com/diegosouzapw/OmniRoute/pull/13531)) — thanks @KooshaPari +- **fix(playground):** allow Compare columns to scroll independently ([#13532](https://github.com/diegosouzapw/OmniRoute/pull/13532)) — thanks @KooshaPari +- **fix(security):** empty whitelist allows through to prevent admin lockout ([#13534](https://github.com/diegosouzapw/OmniRoute/pull/13534)) — thanks @KooshaPari +- **fix(tests):** add DISABLE_IOREG_STRATEGY env var for macOS test compatibility (fixes #13467) ([#13539](https://github.com/diegosouzapw/OmniRoute/pull/13539)) — thanks @KooshaPari +- **fix(zed-hosted):** map developer role and Gemini enums for Zed proxy ([#13543](https://github.com/diegosouzapw/OmniRoute/pull/13543)) — thanks @KooshaPari +- **fix(analytics):** normalize provider aliases to canonical id in usage_history ([#13545](https://github.com/diegosouzapw/OmniRoute/pull/13545)) — thanks @KooshaPari +- **fix(combo):** use traceId for call log id to prevent UNIQUE constraint failure ([#13546](https://github.com/diegosouzapw/OmniRoute/pull/13546)) — thanks @KooshaPari +- **fix(auth):** check model lockout before returning synthetic noauth connection ([#13547](https://github.com/diegosouzapw/OmniRoute/pull/13547)) — thanks @KooshaPari +- **fix(guardrails):** skip credential redaction for base64 image data URLs ([#13550](https://github.com/diegosouzapw/OmniRoute/pull/13550)) — thanks @KooshaPari +- **fix(resilience):** increase requestQueue.maxWaitMs default from 15s to 30s ([#13553](https://github.com/diegosouzapw/OmniRoute/pull/13553)) — thanks @KooshaPari +- **fix(docs):** document OMNIROUTE_READY_TIMEOUT_MS and allowlist the test-only DISABLE_IOREG_STRATEGY ([#13692](https://github.com/diegosouzapw/OmniRoute/pull/13692)) + ### 📝 Maintenance ---- +- **docs(readme):** reconcile live v3.8.50 provider, free-tier, CLI, routing, test, community, sponsor, acknowledgment, and SVG metrics with their audited source denominators, including a deduplicated OmniRoute-in-Action snapshot and distinct contributor rankings for merged pull requests, GitHub-attributed commits, and Git history ([#11356](https://github.com/diegosouzapw/OmniRoute/pull/11356)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **docs:** sync the canonical provider count 354 → 356 across `README.md`, `AGENTS.md`, `llm.txt` (+ 42 i18n mirrors), the four README SVG diagrams, `docs/reference/PROVIDER_REFERENCE.md` (regenerated) and the `package.json` description after Opper (#11629) and 1min.ai (#11631) boarded the catalog — closes the `check:docs-counts-sync` strict drifts that kept `release/v3.8.51` red ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11673](https://github.com/diegosouzapw/OmniRoute/pull/11673)) — thanks @jonlwheat2-gif +- **test(providers):** update count-derived assertions after the v3.8.51 provider additions — `APIKEY_PROVIDERS` 233 → 235 (Opper #11629 + 1min.ai #11631), reserved-prefix REGISTRY walk 395 → 398, `WEB_FETCH_PROVIDERS` now includes `nimble-search` (#11620), and the provider translate-path golden snapshot regenerated ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11674](https://github.com/diegosouzapw/OmniRoute/pull/11674)) — thanks @jonlwheat2-gif +- **test(sse):** bump the hard-lease connection-query inventory for `src/lib/tokenHealthCheck.ts` to 2 — the verify-only web-cookie sweep added by #11495 split the single `getProviderConnections` call into oauth + cookie variants, which the frozen inventory had not tracked ([#11495](https://github.com/diegosouzapw/OmniRoute/pull/11495)). ([#11677](https://github.com/diegosouzapw/OmniRoute/pull/11677)) — thanks @jonlwheat2-gif / @oyi77 +- **chore(release):** clear the `release/v3.8.51` base-red gates — docs migration count (+38 i18n mirrors), stryker `tap.testFiles`, lockfile registry host, stale ESLint suppressions, the 7 lint regressions they were hiding, the stale `APIKEY_PROVIDERS` partition count (231 → 233), the `glm-5.3-max` tier missing from the GLM test table, a dead `modelType === "chat"` comparison in the models catalog, and the `resolveLiveWsUrl` / `sanitizeLiveWsPort` helpers from #11388 that a later merge dropped from `wsPath.ts` (dashboard live WebSocket URL resolution) ([#11502](https://github.com/diegosouzapw/OmniRoute/pull/11502)) — thanks @MumuTW +- **chore(free-models):** the free-tier regime table now declares which totals figure each regime feeds and whether it may take the no-auth shortcut, so the sets that used to repeat those answers by hand are derived from it — a new `freeType` no longer compiles until it has answered every question, instead of silently contributing to no total ([#11537](https://github.com/diegosouzapw/OmniRoute/pull/11537)) — thanks @maxmad64bis +- **docs(routing):** LKGP strategy copy now states provider-level stickiness and the explicit rules fallback instead of promising per-target pinning ([#11555](https://github.com/diegosouzapw/OmniRoute/pull/11555)) — thanks @maxmad64bis +- **test(chatcore):** pin the response tool-name alias resolution against the ordering hazard that broke every Gemini/Antigravity MCP tool call in v3.8.49. `extractRequestToolIdentityMap` deletes `translatedBody._toolNameMap`, so the later read is always undefined and the ledger survives only through the `requestToolIdentityMap` fallback — removing that fallback previously left the entire tool-name suite green. The resolution moves into `resolveResponseToolNameMap()` next to the map it depends on, with a regression guard that fails without the recovery. No behaviour change. ([#11575](https://github.com/diegosouzapw/OmniRoute/pull/11575)) — thanks @amartinawi +- **docs(env):** document `OMNIROUTE_CORPUS_CACHE_SIZE` in `.env.example` and `ENVIRONMENT.md`, so the env/docs contract check stops failing Docs Gates on every branch ([#11576](https://github.com/diegosouzapw/OmniRoute/pull/11576)) — thanks @ntdatt812 +- **chore(stryker):** register `route-guard-tunnel-processes-local-only` and `universal-quota-aware-routing` in `tap.testFiles`, so their mutant kills count for the three modules they cover ([#11577](https://github.com/diegosouzapw/OmniRoute/pull/11577)) — thanks @ntdatt812 +- **test(authz):** pin `LOCAL_ONLY_API_GET_EXEMPTIONS` by exact membership instead of by entry count, so the guard names the offending path and also catches a substitution ([#11580](https://github.com/diegosouzapw/OmniRoute/pull/11580)) — thanks @ntdatt812 +- **test(build):** pin `resolveNextBuildBundlerFlag` to the `OMNIROUTE_USE_TURBOPACK` contract it implements, across runtimes, instead of asserting an unimplemented Bun override ([#11583](https://github.com/diegosouzapw/OmniRoute/pull/11583)) — thanks @ntdatt812 +- **test(claude-web):** the Claude Web transport test's barrel anchors no longer pin the exact construction spelling. #11421 made the executor map lazy, so `/"claude-web": new ClaudeWebExecutor\(\)/` stopped matching and the test went red on `release/v3.8.51` itself — a base-red gate every open PR inherited. The anchors now match the alias key and the class name and tolerate either spelling in between, and were checked to still fail when an alias is genuinely repointed at another executor ([#11585](https://github.com/diegosouzapw/OmniRoute/pull/11585)) — thanks @ntdat812 +- **test(models):** pin `qwen3.8-max` resolution per provider — two of the four catalogs now serve the bare id and no longer carry `-preview` ([#11590](https://github.com/diegosouzapw/OmniRoute/pull/11590)) — thanks @ntdatt812 +- **test(executors):** drop the stale 128-tool truncation assertion that contradicted the #11444 pass-through contract ([#11591](https://github.com/diegosouzapw/OmniRoute/pull/11591)) — thanks @ntdatt812 +- **test(token-health):** pin the current terminal-skip boundary — expired with retry budget is probed, `account_deactivated` stays skipped ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)) — thanks @ntdatt812 +- **test(models):** follow #11503's retarget of the `gemini-3-pro-high` alias to the hyphenated catalog id ([#11593](https://github.com/diegosouzapw/OmniRoute/pull/11593)) — thanks @ntdatt812 +- **refactor(video bridge):** extract per-video acquisition, whole-result caching, description, and metrics/abort/cleanup out of `VideoBridgeGuardrail.preCall` into a `processVideoPart` seam in a new `videoBridgePipeline.ts`, behind explicit `VideoMediaBrokerPort`, `VideoAudioTranscriptionPort`, and `VideoDrilldownPort` boundaries; `preCall` now only handles request traversal, policy, and response aggregation. The Video tab's FFmpeg/ffprobe runtime status is now an explicit `unknown` / `restricted` / `unavailable` / `available` state instead of a nullable boolean pair, fixing a case where an in-flight or failed probe was mislabeled as "install FFmpeg" ([#11657](https://github.com/diegosouzapw/OmniRoute/issues/11657)). ([#12016](https://github.com/diegosouzapw/OmniRoute/pull/12016)) +- **test(opencode-plugin):** add unit test coverage for `formatFreeBudget()` naming helper ([#11660](https://github.com/diegosouzapw/OmniRoute/pull/11660)) — thanks @f9td56dbgh-hub +- **docs(video):** clarify that the Video Bridge transcript `source` field (`client`, `embedded`, `audio-bridge`) is presently caller-declared and not yet server-verified — OmniRoute enforces the enum shape but does not cryptographically confirm that an `embedded`/`audio-bridge` label came from a server-owned extraction ([#11661](https://github.com/diegosouzapw/OmniRoute/issues/11661)). ([#12001](https://github.com/diegosouzapw/OmniRoute/pull/12001)) +- **chore(providers):** retire the distributed Raycast Relay and Hailuo Web integrations after a provenance audit confirmed substantial derivation from GPL-3.0 sources; the official GitHub and MiniMax API providers remain available. Migration 163 disables legacy connections and invalidates their active leases while preserving connection IDs, audit/history rows, quota snapshots, usage records, and fail-closed API-key connection policies. ([#11691](https://github.com/diegosouzapw/OmniRoute/pull/11691)) +- **chore(providers):** retire the Felo Web provider while its GPL-derived provenance remains on licensing HOLD ([#11698](https://github.com/diegosouzapw/OmniRoute/pull/11698)) — stale runtime and database references now fail closed while existing connection, lease, and usage history remains preserved for auditability. +- **fix(sse):** retire the Gemini Web image-generation bridge while provenance and licensing remain on HOLD, preserving Gemini Web chat/auth/tools and legitimate Gemini/Nano Banana image providers ([#11708](https://github.com/diegosouzapw/OmniRoute/pull/11708)) +- **chore(audio):** retire the built-in EdgeTTS provider and its 11-voice catalog while provenance/licensing review remains on HOLD; the generic `/v1/audio/speech` endpoint and the other speech providers remain available ([#11711](https://github.com/diegosouzapw/OmniRoute/pull/11711)) +- **fix(providers):** retire the unofficial Qwen Web integration and its `qwen-web`/`qw` identities while third-party provenance remains on HOLD ([#11713](https://github.com/diegosouzapw/OmniRoute/pull/11713)) — direct requests now fail closed with a sanitized HTTP 410 and combos skip the retired target, while migration guards preserve connection IDs, allowlists, usage/audit history and invalidate active leases. Supported Qwen API, Qwen Code, cloud, token-plan, audio, local and model-name surfaces remain available; restoring Qwen Web requires a qualified provenance/license decision. +- **fix(providers):** retire `microsoft-designer-web` and its `msdesigner` alias from runtime and catalogs while preserving stored credentials, provider history and allowlists as inactive records — source dependency on gpt4free's [`MicrosoftDesigner.py`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/g4f/Provider/needs_auth/MicrosoftDesigner.py) is proven at snapshot `0943d16eef757a44deb60651c07bbe5fa958cafe`, whose [`LICENSE`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/LICENSE) is GPL-3.0; expressive adaptation is probable, which records provenance/licensing risk without asserting a legal conclusion ([#11720](https://github.com/diegosouzapw/OmniRoute/pull/11720)). +- **docs(legal):** centralize pinned MIT notices for the vendored GCF codec and local flag assets ([#11726](https://github.com/diegosouzapw/OmniRoute/pull/11726)) +- Split the 2,952-line `adobeFireflyClient.ts` into nine focused modules (catalog, credentials, payload, ARP session, upload, responses, IMS, account, poll) behind an unchanged barrel — no consumer edits and no public API change (#11728). — thanks @MumuTW +- **fix(dashboard):** replaced the Model Lockout card's unprovenanced notification MP3 with a short, locally synthesized Web Audio chime ([#11731](https://github.com/diegosouzapw/OmniRoute/pull/11731)) — both toggles retain optional audible feedback without shipping replacement media or third-party code, and unsupported, suspended, or rejected audio contexts fail open without blocking the setting change. +- **chore(deepseek-web):** remove the provenance-unresolved DeepSeek PoW WASM binary and its runtime loader/tracing while retaining the existing JavaScript solver (slower at high difficulty) ([#11732](https://github.com/diegosouzapw/OmniRoute/pull/11732)). +- **chore(assets):** remove four unused provider assets whose redistribution provenance could not be verified while retaining the active Freebuff SVG ([#11735](https://github.com/diegosouzapw/OmniRoute/pull/11735)) +- **docs(assets):** retired two raster images whose redistribution provenance could not be verified ([#11736](https://github.com/diegosouzapw/OmniRoute/pull/11736)) — PicoClaw documentation now reuses the existing SVG, while Zoo Code remains a text link without an image. +- **docs(legal):** add pinned notices for six LobeHub-derived provider SVGs and 65 byte-exact theSVG assets, retaining registry claims as unverified metadata with explicit trademark and HOLD boundaries ([#11737](https://github.com/diegosouzapw/OmniRoute/pull/11737)). +- **chore(quality):** added a versioned 225/225 provider-asset provenance manifest and blocking drift gate ([#11743](https://github.com/diegosouzapw/OmniRoute/pull/11743)) — the inventory pins magic MIME and SHA-256 for every physical file, records immutable source matches for 71 assets, keeps 69 probable and 85 unresolved entries explicit but non-blocking, and treats provenance as source evidence rather than copyright or trademark clearance. +- **refactor(mitm):** replace four previously derived Traffic Inspector utilities — conversation normalization, SSE merging, secret masking, and header sanitization — with independent clean-room implementations based on public provider schemas, WHATWG SSE, and RFC 6750, while keeping the two independently classified passthrough surfaces unchanged ([#11748](https://github.com/diegosouzapw/OmniRoute/pull/11748)). +- Added a unit test that fails seven days before any dated pack under `config/` (`validUntil` and sibling keys) lapses, naming the file and key. The Alibaba free-tier pack expired on 2026-08-27 and turned every PR red the next morning with no commit involved; renewal now happens on someone's terms, not the clock's. ([#11891](https://github.com/diegosouzapw/OmniRoute/pull/11891)) +- `check:workflows` now fails (under `--strict`/`--ratchet`) when any job routed to a self-hosted runner publishes with `--provenance` — npm rejects that with `422` at the registry, which in v3.8.50 only surfaced after the tag and Docker images were public. ([#11895](https://github.com/diegosouzapw/OmniRoute/pull/11895)) +- `scripts/ops/runner-janitor.sh` now proves a path is idle with one `lsof` snapshot and removes stale leftovers itself (tmpfs after 3 h — it is RAM — disk after 24 h), kills orphan `next-build` processes, prunes checkouts of stopped runners, and alerts on memory pressure; `--dry-run` shows exactly what it would do. `docs/ops/RUNNER_BOX.md` reconciled to the measured box (31 GB, 10 listeners). ([#11893](https://github.com/diegosouzapw/OmniRoute/pull/11893)) +- The `next-build` artefact (1.3 GB) is now written and read under `$RUNNER_TEMP` (per-runner, on disk) instead of `/tmp`, which on the self-hosted pool is a 12 GB tmpfs in RAM. Landing it there took 27–32 of the publish job's 76 minutes, and the fixed `/tmp/e2e-build.tar.gz` name let E2E jobs on different runners overwrite each other's download. ([#11896](https://github.com/diegosouzapw/OmniRoute/pull/11896)) +- The CI `build` job now runs in two concurrency lanes — `main` and pull requests — so a release build is never queued behind (or OOM-killed beside) PR builds on the self-hosted pool, which holds one `next-build` comfortably and two at the edge. ([#11901](https://github.com/diegosouzapw/OmniRoute/pull/11901)) +- Re-freeze the ESLint suppressions on `release/v3.8.51` from a clean-room measurement (2 stale file entries pruned, 55 pre-existing `no-explicit-any` in six new files frozen under #11924) and drop the dead `GPT_SIZE_MAP` constant orphaned by the Adobe Firefly client split, so `No new ESLint warnings` stops failing every PR with exit 2 (Refs #11924) ([#11955](https://github.com/diegosouzapw/OmniRoute/pull/11955)) +- Type the 55 `no-explicit-any` sites that had been frozen under #11924 — four redundant casts in `socksConnectorWithFamily.ts` (undici/socks types already accept them) and the mocks/fixtures of the socks-timeout and isFree suites — and drop their suppression entries; the ESLint ratchet shrinks from 5487 to 5432 (Closes #11924) ([#11975](https://github.com/diegosouzapw/OmniRoute/pull/11975)) +- Take the two hosted-runner builds off the PR rail: `Build App` (`build.yml`) is `workflow_dispatch`-only and `DAST smoke (PR)` runs only for PRs into `main` — the 7 GB hosted VM cannot build `release/v3.8.51` in any profile (19/30 red, VM shutdown ~8 min into `next build`) and both checks had turned into permanent noise on every release PR; the bundle stays validated by `ci.yml` on `main` and by `nightly-release-green` on `release/**` (Closes #11946) ([#11962](https://github.com/diegosouzapw/OmniRoute/pull/11962)) +- Move the four nightly jobs that build the backend (`nightly-schemathesis`, `nightly-llm-security` promptfoo + garak, `nightly-resilience` axe-a11y) off the hosted 7 GB runner — where they died on `release/v3.8.51` unseen — onto the box's new `omni-light` pool (two listeners), and document the reshaped fleet (4 active OmniRoute listeners: 2 `omni-build` + 2 `omni-light`, janitor ceiling 4) (Closes #11965) ([#11967](https://github.com/diegosouzapw/OmniRoute/pull/11967)) +- Stop painting every fork PR into `release/**` red: `quality.yml` `Build (advisory)` is skipped (GitHub still reports `continue-on-error` failures as check FAILURE). Hosted `ubuntu-latest` cannot finish `npm run build` on this tree — same class as #11962 taking `build.yml` off the PR rail. `docker-publish.yml` amd64 now runs on the `.113` `omni-build` pool (31 GB / 32 cores, two listeners) with Turbopack, shares the `heavy-build-main` lane with `ci.yml` `Build` so it queues instead of becoming a third heavy, and keeps per-ref concurrency (a merge storm was starting 8 concurrent OOM builds). arm64 stays on `ubuntu-24.04-arm` with webpack — there is no ARM box. Fallback when `USE_VPS_RUNNER` is off: hosted amd64 + webpack (#11976). ([#12021](https://github.com/diegosouzapw/OmniRoute/pull/12021)) +- `docker-publish.yml` builds with webpack (`OMNIROUTE_USE_TURBOPACK=0`) on **both** arches, including amd64 on the `.113` `omni-build` pool. Turbopack had the RAM (31 GB, `omniroute-113-6`) and still panicked (`TurbopackInternalError: there must be a path to a root`, run 33253576569, 26 min in). The same tree's arm64 webpack build on hosted `ubuntu-24.04-arm` succeeded (run 33264823398). Dockerfile already documents webpack as the Docker escape hatch (#11976). ([#12050](https://github.com/diegosouzapw/OmniRoute/pull/12050)) +- **docs(free-tier):** declare the counting vs deciding regimes for "is it free?" and guard the deciding path from DB-backed catalog resolution ([#12226](https://github.com/diegosouzapw/OmniRoute/pull/12226)) — thanks @maxmad64bis +- **docs(auto-combo):** The mode pack table in `docs/routing/AUTO-COMBO.md` now lists all six shipped packs with every weight each one sets, replacing a four-pack table whose numbers had also drifted from the source. It states plainly that no pack sets `quality`, so selecting any pack silences the observed-quality signal. Six more documents that quote the scoring factor count joined the `check:docs-counts` gate, which caught five stale claims — including one naming nine factors that do not exist — and two stale mode pack counts. The dashboard routing panel, which offered four of the six packs and labelled the default strategy "6-Factor Scoring", is now covered by a test; the two packs it was missing are `reliability-first` and `chaos-mode`, the latter labelled as the fault-injection profile it is rather than as one more routing preference ([#12316](https://github.com/diegosouzapw/OmniRoute/pull/12316)) — thanks @maxmad64bis +- **chore(quality):** rebaseline `src/lib/db/apiKeys.ts` for the ACL the key-creation path now preserves ([#12352](https://github.com/diegosouzapw/OmniRoute/pull/12352)) ([#12673](https://github.com/diegosouzapw/OmniRoute/pull/12673)) — thanks @KrzysiekSko +- **chore(providers):** bump the Claude Code wire identity and the Devin bridge image pin from `2.1.220` to `2.1.258` ([#12402](https://github.com/diegosouzapw/OmniRoute/pull/12402)) — thanks @ggiak ([#12604](https://github.com/diegosouzapw/OmniRoute/pull/12604)) +- **docs(env):** align `.env.example`, the README Bun section, and the troubleshooting guide with the code: `OMNIROUTE_USE_TURBOPACK` also governs `npm run build` (not dev-only), `bun run build` follows that flag instead of auto-selecting Webpack, `OMNIROUTE_CHAT_MAX_HEAVY_IN_FLIGHT` is unset by default (no request-count cap), and the structural `503 chat_admission_busy` message matches `chatAdmissionResponses.ts` (#12404 — thanks @pacocartones) +- **chore(electron):** upgrade the desktop app to Electron 44 (Chromium 152, Node 24.18.1) ([#12217](https://github.com/diegosouzapw/OmniRoute/pull/12217)). **Requires macOS 13 (Ventura) or later** — Chromium dropped macOS 12 (Monterey), so Monterey users must stay on an earlier OmniRoute desktop build. Windows and Linux are unaffected; the app already shipped only x64/arm64, so Electron 44 dropping 32-bit builds changes nothing. Removes the `openAsHidden`/`wasOpenedAsHidden` login-item fields deleted in Electron 44 — hidden autostart continues to work through the `--hidden` argument registered with the login item ([#12554](https://github.com/diegosouzapw/OmniRoute/pull/12554)) — thanks @app/dependabot +- **chore(quality):** rebaseline `src/sse/handlers/chat.ts` for the effective-input persistence the continuation fix needs ([#12641](https://github.com/diegosouzapw/OmniRoute/pull/12641)) ([#12680](https://github.com/diegosouzapw/OmniRoute/pull/12680)) — thanks @hartmark +- `Coverage` job on `ci.yml`: the informational Codecov upload gets its own 5-minute ceiling and `continue-on-error`, and the job budget grows from 20 to 30 minutes (the 8-shard c8 merge alone takes ~10) — a stalled upload no longer ends the job `cancelled` and drags a fully green `main` run's conclusion down with it ([#11972](https://github.com/diegosouzapw/OmniRoute/pull/11972)) +- CI hardening on the PR rail: the four `Unit Tests fast-path` shards get `timeout-minutes: 30` (a hung shard held a PR for 64 min instead of GitHub's 6 h default) and both ESLint file caches lose their `restore-keys` fallback, so a cache built under another suppressions file or lint config can no longer report stale verdicts (Refs #11600, #11924) ([#11963](https://github.com/diegosouzapw/OmniRoute/pull/11963)) +- Every CI job that runs a `next build` (`build`, the npm `publish`, both release-green validations) now targets the `omni-build` runner label, which only two of the eight self-hosted runners carry. The box holds one build comfortably and two at the edge; a third now queues on GitHub instead of being OOM-killed by the kernel. ([#11932](https://github.com/diegosouzapw/OmniRoute/pull/11932)) +- **refactor(combo):** move `handleRoundRobinCombo` (and `resolveTargetTokenLimit`) into `open-sse/services/combo/roundRobinCombo.ts`. `combo.ts` drops from 2164 to 1014 lines (`split("\n").length`); the leaf is 1199 (under the 1200 new-file cap). The round-robin call site uses a dynamic `import()` so `releaseStickyPinOnFailure` / `clearStaleLKGP` can stay exported from `combo.ts` without a static cycle. Skip / sticky / semaphore / safety-timer behavior is unchanged. ([#12811](https://github.com/diegosouzapw/OmniRoute/pull/12811)) — thanks @HouMinXi +- **chore(quality):** rebaseline the file-size caps the error-boundary campaign grew past (`open-sse/executors/codex.ts`, `open-sse/vendor/codex-chatgpt-web/bridge.ts`, both via [#12444](https://github.com/diegosouzapw/OmniRoute/pull/12444)) ([#12654](https://github.com/diegosouzapw/OmniRoute/pull/12654)) +- **chore(quality):** rebaseline the file-size caps the hartmark batch grew past (`combos/page.tsx` via [#12355](https://github.com/diegosouzapw/OmniRoute/pull/12355), `open-sse/services/combo.ts` via [#12338](https://github.com/diegosouzapw/OmniRoute/pull/12338)) ([#12623](https://github.com/diegosouzapw/OmniRoute/pull/12623)) — thanks @hartmark +- **chore(quality):** rebaseline the file-size caps the HouMinXi batch grew past when its PRs stacked (`providers/page.tsx`, `chatCore.ts`, `accountFallback.ts`) — each PR measured correctly in isolation, none saw the stacking ([#12619](https://github.com/diegosouzapw/OmniRoute/pull/12619)) +- **chore(quality):** rebaseline `open-sse/services/combo.ts` for the reset-aware scoring the HouMinXi batch stacked ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) ([#12678](https://github.com/diegosouzapw/OmniRoute/pull/12678)) — thanks @HouMinXi +- **docs:** sync provider catalog count (direct commit `4f11b2ae3d`) +- **docs(skills):** sync omni-inference search fallback (direct commit `b4e76a7ed9`) +- **chore(lint):** prune two stale base suppressions (direct commit `1325047d56`) +- **test(auto-combo):** tolerate full-suite rotation load (direct commit `ad3e293f9f`) +- **test:** drain inherited quality snapshot reds (direct commit `21ed68d8ac`) +- **chore(quality):** rebaseline measured CLI bundle growth (direct commit `d35b3f9779`) +- **test(ui):** reproduce invalid AnySearch icon alias (direct commit `12cc6ca834`) +- **test(translator):** fix the relative imports of the relocated deferred-finish test (direct commit `777d9d1629`) +- **docs(counts):** bring the documented provider and migration counts to the measured values (direct commit `e70bea30e9`) +- **test(security):** drop the guard-target set left unused by the sync (direct commit `a32ba0e362`) +- **chore(db):** renumber model_capabilities migration 163 → 169 (cross-branch collision with 163_radar_feed_cache_generated_at) (direct commit `70f5d4cbf9`) +- **docs(cli):** document CLI_PRIME_AGENT_BIN and correct the CLI Agents count (direct commit `9058e39b61`) +- **chore(deps):** refresh runtimes and adopt ESLint 10 ([#11259](https://github.com/diegosouzapw/OmniRoute/pull/11259)) — thanks @backryun +- **refactor(local-corpus):** implement dynamic root resolution and LRU cache ([#11491](https://github.com/diegosouzapw/OmniRoute/pull/11491)) — thanks @raheemuddin786 +- **chore(quality):** prune stale eslint-suppressions entries ([#11596](https://github.com/diegosouzapw/OmniRoute/pull/11596)) +- **chore(ci):** unblock quality gates - env-doc sync + stryker tap.testFiles registration (#9282) ([#11609](https://github.com/diegosouzapw/OmniRoute/pull/11609)) — thanks @Meet6338-X +- **chore(ci):** point the circular-deps gate at dpdm's real JS entrypoint ([#11615](https://github.com/diegosouzapw/OmniRoute/pull/11615)) — thanks @benzntech +- **chore(quality):** rebaseline file-size for #11448/#11495/#11561 growth ([#11630](https://github.com/diegosouzapw/OmniRoute/pull/11630)) +- **deps:** realign onnxruntime-node pin to @huggingface/transformers' 1.24.3 lockstep ([#11633](https://github.com/diegosouzapw/OmniRoute/pull/11633)) — thanks @jonlwheat2-gif +- **test(autoCombo):** port models_dev_tier #11508 guard from node:test to vitest ([#11635](https://github.com/diegosouzapw/OmniRoute/pull/11635)) — thanks @jonlwheat2-gif +- **chore(quality):** rebaseline file-size for #11083/#11631/#11628 growth ([#11636](https://github.com/diegosouzapw/OmniRoute/pull/11636)) +- **chore(lint):** prune stale ESLint suppressions (base-red #11449) ([#11644](https://github.com/diegosouzapw/OmniRoute/pull/11644)) — thanks @jonlwheat2-gif +- **docs(changelog):** aggregate the ten v3.8.50 fragments into CHANGELOG.md ([#11683](https://github.com/diegosouzapw/OmniRoute/pull/11683)) +- **chore(search):** drop unreachable credential sweep after #11642 ([#11687](https://github.com/diegosouzapw/OmniRoute/pull/11687)) — thanks @HouMinXi +- **ci:** add API route TypeScript regression gate ([#11705](https://github.com/diegosouzapw/OmniRoute/pull/11705)) — thanks @Sabeekhann +- **docs(dedup):** correct the CodeQL claim on the tenant-namespace comment ([#11712](https://github.com/diegosouzapw/OmniRoute/pull/11712)) +- **docs(changelog):** add consolidated v3.8.50 stats, top-25 ranking and a .mailmap ([#11715](https://github.com/diegosouzapw/OmniRoute/pull/11715)) +- **docs:** recommend gstack for AI-assisted workflows ([#11770](https://github.com/diegosouzapw/OmniRoute/pull/11770)) — thanks @santosraju99-hub +- **chore(quality):** tighten complexity/cognitive-complexity ratchets to the current tip; land the missed gateways.ts rebaseline ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **docs(i18n):** finish Freepik → Magnific rebrand in locale strings and README ([#11772](https://github.com/diegosouzapw/OmniRoute/pull/11772)) — thanks @turbolego +- **refactor(auto-combo):** fix divergent scoring in combo health reporting ([#11854](https://github.com/diegosouzapw/OmniRoute/pull/11854)) — thanks @jacobsparts +- **chore(changelog):** add missing fragments for #11919/#11918/#11916 ([#11938](https://github.com/diegosouzapw/OmniRoute/pull/11938)) +- **test(check):** escape the runs-on fixture with JSON.stringify, not a quote-only replace ([#11942](https://github.com/diegosouzapw/OmniRoute/pull/11942)) (direct commit `fb7445eaa3`) +- **test(release):** align five suites with the contracts #11933, #11919 and #11876 shipped on release/v3.8.51 ([#11944](https://github.com/diegosouzapw/OmniRoute/pull/11944)) +- **chore(quality):** remove unreachable code and restore test discovery ([#11950](https://github.com/diegosouzapw/OmniRoute/pull/11950)) — thanks @backryun +- **docs(agents):** sync-back landings are fast-forward, never squash ([#11964](https://github.com/diegosouzapw/OmniRoute/pull/11964)) +- **test(infra):** retry recursive temp-dir removal instead of failing a shard on ENOTEMPTY (#11966) ([#11968](https://github.com/diegosouzapw/OmniRoute/pull/11968)) +- **docs(ops):** the .113 heavy-build ceiling is one runner, not two ([#12048](https://github.com/diegosouzapw/OmniRoute/pull/12048)) +- **test(ui):** correct inactive auto-fetch expectation ([#12098](https://github.com/diegosouzapw/OmniRoute/pull/12098)) — thanks @RaviTharuma +- **docs:** document native dependency check escape hatch ([#12101](https://github.com/diegosouzapw/OmniRoute/pull/12101)) — thanks @RaviTharuma +- **test(providers):** regenerate the translate-path golden for OrcaRouter (#11923) ([#12118](https://github.com/diegosouzapw/OmniRoute/pull/12118)) +- **chore(quality):** velocity phase — loosen every numeric baseline by 20% until v4.0, monitor headroom nightly ([#12125](https://github.com/diegosouzapw/OmniRoute/pull/12125)) +- **chore(lint):** batch 0 of #12146 — type the call-log-cap sqlite rows instead of 45 as-any casts ([#12157](https://github.com/diegosouzapw/OmniRoute/pull/12157)) +- **chore(lint):** batch 4 of #12146 — shared/components react-hooks violations resolved ([#12159](https://github.com/diegosouzapw/OmniRoute/pull/12159)) +- **chore(lint):** batch 1 of #12146 — dashboard/cli-code react-hooks violations resolved ([#12160](https://github.com/diegosouzapw/OmniRoute/pull/12160)) +- **chore(lint):** batch 3 of #12146 — dashboard/settings react-hooks violations resolved ([#12162](https://github.com/diegosouzapw/OmniRoute/pull/12162)) +- **chore(lint):** batch 2 of #12146 — resolve the react-hooks compiler violations in dashboard/providers ([#12163](https://github.com/diegosouzapw/OmniRoute/pull/12163)) +- **chore(quality):** register search-432 cooldown test in stryker tap.testFiles ([#12170](https://github.com/diegosouzapw/OmniRoute/pull/12170)) +- **chore(lint):** batch 5 of #12146 — combos, endpoint, provider-stats, api-manager and costs react-hooks violations resolved ([#12174](https://github.com/diegosouzapw/OmniRoute/pull/12174)) +- **build:** add contributor fast profile ([#12192](https://github.com/diegosouzapw/OmniRoute/pull/12192)) — thanks @rafacpti23 +- **docs(readme):** README touch-ups ([#12193](https://github.com/diegosouzapw/OmniRoute/pull/12193)) ([#12194](https://github.com/diegosouzapw/OmniRoute/pull/12194)) ([#12202](https://github.com/diegosouzapw/OmniRoute/pull/12202)) — thanks @rafacpti23 +- **build:** force the webpack bundler in the contributor build profile ([#12197](https://github.com/diegosouzapw/OmniRoute/pull/12197)) — thanks @rafacpti23 +- **build:** skip standalone packaging in the contributor profile ([#12198](https://github.com/diegosouzapw/OmniRoute/pull/12198)) — thanks @rafacpti23 +- **docs(audit):** align every published number with the code and harden check:docs-counts ([#12200](https://github.com/diegosouzapw/OmniRoute/pull/12200)) +- **build:** stub the instrumentation entrypoints in the contributor profile ([#12203](https://github.com/diegosouzapw/OmniRoute/pull/12203)) — thanks @rafacpti23 +- **build:** omit the standalone output target for contributor builds ([#12204](https://github.com/diegosouzapw/OmniRoute/pull/12204)) — thanks @rafacpti23 +- **chore(lint):** batch 6 of #12146 — memory, radar, audit, analytics, cache, usage, activity, home and RequestLoggerV2 react-hooks violations resolved ([#12208](https://github.com/diegosouzapw/OmniRoute/pull/12208)) +- **docs(diagrams):** rename number-carrying diagram files to stable names ([#12210](https://github.com/diegosouzapw/OmniRoute/pull/12210)) +- **docs(dashboard):** redraw onboarding tier-flow SVGs for the real 4-tier model ([#12211](https://github.com/diegosouzapw/OmniRoute/pull/12211)) +- **docs(api):** document every implemented route in openapi.yaml (276 → 692 paths) ([#12212](https://github.com/diegosouzapw/OmniRoute/pull/12212)) +- **chore(lint):** batch 7 of #12146 — final src tail: 37 react-hooks violations across 33 files resolved ([#12244](https://github.com/diegosouzapw/OmniRoute/pull/12244)) +- Revert "docs: recommend gstack for AI-assisted workflows (#11770)" ([#12248](https://github.com/diegosouzapw/OmniRoute/pull/12248)) ([#12249](https://github.com/diegosouzapw/OmniRoute/pull/12249)) +- **docs:** Chaos Mode setup guide + weighted strategy semantics ([#12250](https://github.com/diegosouzapw/OmniRoute/pull/12250)) +- **docs(agents):** protected-surface merge rule — operator approval for agent-instruction files ([#12253](https://github.com/diegosouzapw/OmniRoute/pull/12253)) +- **chore(quality):** register native-codex turn-pin tests in stryker tap.testFiles ([#12263](https://github.com/diegosouzapw/OmniRoute/pull/12263)) +- **chore(quality):** dedupe tap.testFiles entries added by racing base-red fixes ([#12265](https://github.com/diegosouzapw/OmniRoute/pull/12265)) +- **docs(readme):** introduce OmniRouteTray — the macOS menu-bar companion ([#12276](https://github.com/diegosouzapw/OmniRoute/pull/12276)) — thanks @ggiak +- **chore(proxy):** purge the legacy 1proxy residue — sync/rotator modules, dead DB exports, dead settings tab and flag, docs (#12091) ([#12290](https://github.com/diegosouzapw/OmniRoute/pull/12290)) +- **chore(quality):** baseline-headroom skips generated and vendored files in the fileSize worst-file signal ([#12291](https://github.com/diegosouzapw/OmniRoute/pull/12291)) +- **refactor(video):** unify the JPEG frame data-URI contract ([#12322](https://github.com/diegosouzapw/OmniRoute/pull/12322)) +- **chore(deps):** freeze onnxruntime-node and eslint-plugin-react-hooks out of dependabot groups ([#12329](https://github.com/diegosouzapw/OmniRoute/pull/12329)) +- **test(executors):** restore the #10986 reasoning-only fallback guards ([#12364](https://github.com/diegosouzapw/OmniRoute/pull/12364)) — thanks @pacocartones +- **chore(quality):** re-tighten the file-size ratchet to the real LOC (plan 3.8.52 task 0) ([#12411](https://github.com/diegosouzapw/OmniRoute/pull/12411)) +- **test(grok):** format web executor suite ([#12412](https://github.com/diegosouzapw/OmniRoute/pull/12412)) +- **chore(quality):** ratchet open-sse typecheck baseline to zero ([#12418](https://github.com/diegosouzapw/OmniRoute/pull/12418)) +- **test(build):** guard the artifact path policy arrays against duplicates ([#12422](https://github.com/diegosouzapw/OmniRoute/pull/12422)) +- **chore(lint):** adopt eslint-plugin-react-hooks 7.1.1 ([#12428](https://github.com/diegosouzapw/OmniRoute/pull/12428)) +- **chore(providers):** remove a keyless provider integration at its operator's request ([#12440](https://github.com/diegosouzapw/OmniRoute/pull/12440)) +- **docs(providers):** register providers removed at their operator's request and guard against reintroduction ([#12478](https://github.com/diegosouzapw/OmniRoute/pull/12478)) +- **chore(quality):** base fixes — stryker tap.testFiles + node_modules cache key ([#12482](https://github.com/diegosouzapw/OmniRoute/pull/12482)) +- **docs(arch):** one-process recipe for tens of long /v1/responses ([#12493](https://github.com/diegosouzapw/OmniRoute/pull/12493)) — thanks @RaviTharuma +- **chore(quality):** tighten the CodeQL ratchet baseline from 11 to 6 ([#12530](https://github.com/diegosouzapw/OmniRoute/pull/12530)) +- **chore(deps):** pin browserslist override to ^4.28.8 ([#12592](https://github.com/diegosouzapw/OmniRoute/pull/12592)) — thanks @KooshaPari +- **chore(deps):** bump fast-uri to 3.1.7 in the electron lockfile ([#12601](https://github.com/diegosouzapw/OmniRoute/pull/12601)) +- **docs(quality):** document how the CodeQL ratchet refreshes and how to tighten it ([#12611](https://github.com/diegosouzapw/OmniRoute/pull/12611)) +- **test(ci):** pin the openapi-security-tiers two-arm contract with an executing gate test (#12581) ([#12652](https://github.com/diegosouzapw/OmniRoute/pull/12652)) +- **docs(gamification):** align XP Rewards table with code (#12501) ([#12667](https://github.com/diegosouzapw/OmniRoute/pull/12667)) — thanks @KooshaPari +- **refactor(combo):** split executeTarget into gates, attempt, and loop ([#12746](https://github.com/diegosouzapw/OmniRoute/pull/12746)) — thanks @HouMinXi +- **docs(security):** document socket.yml scanner config + CI workflow link (#12575) ([#12764](https://github.com/diegosouzapw/OmniRoute/pull/12764)) — thanks @KooshaPari +- **chore(ci):** guard commit identity in pre-commit to stop author misattribution ([#12772](https://github.com/diegosouzapw/OmniRoute/pull/12772)) +- **deps:** 17 Dependabot bumps — bump electron from 43.4.0 to 43.4.1 in /electron ([#11426](https://github.com/diegosouzapw/OmniRoute/pull/11426)); bump the development group across 1 directory with 8 updates ([#11428](https://github.com/diegosouzapw/OmniRoute/pull/11428)); bump the production group across 1 directory with 11 updates ([#11440](https://github.com/diegosouzapw/OmniRoute/pull/11440)); chore(deps): bump github/codeql-action/init from 4.37.7 to 4.37.8 ([#11925](https://github.com/diegosouzapw/OmniRoute/pull/11925)); chore(deps): bump github/codeql-action from 4.37.7 to 4.37.8 ([#11926](https://github.com/diegosouzapw/OmniRoute/pull/11926)); chore(deps): bump github/codeql-action/analyze from 4.37.7 to 4.37.8 ([#11927](https://github.com/diegosouzapw/OmniRoute/pull/11927)); build(deps): bump github/codeql-action/init from 4.37.8 to 4.37.9 ([#12345](https://github.com/diegosouzapw/OmniRoute/pull/12345)); build(deps): bump github/codeql-action/analyze from 4.37.8 to 4.37.9 ([#12346](https://github.com/diegosouzapw/OmniRoute/pull/12346)); bump the development group across 1 directory with 2 updates ([#12347](https://github.com/diegosouzapw/OmniRoute/pull/12347)); build(deps): bump github/codeql-action from 4.37.8 to 4.37.9 ([#12349](https://github.com/diegosouzapw/OmniRoute/pull/12349)); bump browserslist from 4.28.2 to 4.28.8 ([#12396](https://github.com/diegosouzapw/OmniRoute/pull/12396)); bump the production group across 1 directory with 4 updates ([#12399](https://github.com/diegosouzapw/OmniRoute/pull/12399)); bump @xmldom/xmldom ([#12500](https://github.com/diegosouzapw/OmniRoute/pull/12500)); bump qs from 6.15.2 to 6.16.0 ([#12512](https://github.com/diegosouzapw/OmniRoute/pull/12512)); bump @xmldom/xmldom from 0.9.10 to 0.9.12 ([#12513](https://github.com/diegosouzapw/OmniRoute/pull/12513)); bump fast-uri from 3.1.5 to 3.1.7 ([#12514](https://github.com/diegosouzapw/OmniRoute/pull/12514)); bump @humanfs/node from 0.16.7 to 0.16.8 ([#12515](https://github.com/diegosouzapw/OmniRoute/pull/12515)) +- Clear the `release/v3.8.51` `check:agent-skills-sync` base-red: regenerate `skills/cli-tunnel/SKILL.md` so the `tunnel create [type]` positional that #13009 taught the generator to read is reflected in the committed skill. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` typecheck base-red from #12731: the new `Mode pack ranking gates` candidates in `open-sse/services/autoCombo/__tests__/autoCombo.test.ts` omitted the required `provider`, `model` and `errorRate` fields of `ProviderCandidate`, failing `check:open-sse-typecheck` (and with it `Fast Quality Gates`) on every open PR. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` docs-sync base-red: EURouter (#13025) and GreenPT (#13024) took the live provider count to 358, leaving 7 STRICT drifts (`PROVIDER_REFERENCE.md`, 4 diagrams, 2 tier-flow images) plus the `AGENTS.md` / `llm.txt` / `package.json` count claims stale. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- **test(models):** Pin `CATALOG_BUILD_TIMEOUT_MS` in the #12058 canonical-catalog regression so a cold catalog build racing the 8s default bound no longer flakes the suite with a 500 ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Allow `@omniroute/opencode-plugin-v2/` in the npm pack policy — [#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870) shipped the v2 plugin beside its v1 sibling without widening the allowlist, so every packed file under it read as an unexpected artifact and failed `check:pack-policy` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Point the release-green validator's pack gate at the tree it is validating — the build-provenance guard defaulted to `origin/main`, which a release branch tip can never be an ancestor of mid-cycle, so `Package artifact` reported a false red on every run and the tarball boot-smoke cascaded off it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **docs(checks):** keep doc counts honest — headings, rankings, catalog, weights, quality gate and scoring diagram now covered ([#12507](https://github.com/diegosouzapw/OmniRoute/pull/12507)) — thanks @maxmad64bis +- **chore(lifecycle):** `check:model-lifecycle` now also diffs `DEFAULT_DEGRADATION_MAP` (the background-task redirect table) against the vendor lifecycle snapshot, refusing a retired id as source or target, with a table-driven unit test beside it. Three rows whose source the vendor had retired — `claude-sonnet-4-20250514`, `gemini-3-pro-preview` and `gpt-5.1-codex` (whose target `gpt-5.1-codex-mini` is retired too) — were dead code, since `checkLifecycle` answers 410 before the redirect runs; they are dropped (#12535 — thanks @pacocartones) +- **test(compression):** cover idle worker eviction at the resource level — the pool must call `terminate()` and must not retain the worker's `MessagePort`, complementing the `exit`-event assertion added with the fix ([#13371](https://github.com/diegosouzapw/OmniRoute/pull/13371)) +- **chore(quality):** raise the `imageGeneration.ts` file-size ceiling for the image-only-model guard that clears the #12945 base-red ([#13392](https://github.com/diegosouzapw/OmniRoute/pull/13392)) +- **chore(quality):** type the combo-test route's JSON response bodies instead of casting them to `any`, drop the now-empty suppression entry, and remove the `hasPerModelQuota` import `chatCore.ts` stopped using when the failure-classification helper was extracted ([#13379](https://github.com/diegosouzapw/OmniRoute/pull/13379)) +- **chore(electron):** the desktop bundle no longer ships the root-level files of every translated docs mirror (`README.md`, `llm.txt`, `CLAUDE.md`, `GEMINI.md`, `CONTRIBUTING.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`) — the packaged app only reads `docs/i18n//docs/**`, which stays. Saves ~11 MB on top of the translated CHANGELOGs already pruned. (#0000) ([#13235](https://github.com/diegosouzapw/OmniRoute/pull/13235)) +- **chore(quality):** retighten the `src/sse/handlers/chat.ts` unused-vars suppression count to the 8 violations that actually remain ([#13385](https://github.com/diegosouzapw/OmniRoute/pull/13385)) +- Bring the documented counts back in line with the code: 171 migrations (was 169) and 20 routing strategies (was 19), plus the regenerated `cli-tunnel` skill reference. ([#12970](https://github.com/diegosouzapw/OmniRoute/pull/12970)) +- Update the documented migration count to 172 after the call-logs provider-stats indexes landed. ([#13209](https://github.com/diegosouzapw/OmniRoute/pull/13209)) +- **chore(ci):** two gates that close the blind spots behind the exclusions above. `check:vitest-exclusions` requires every Vitest exclusion to name a tracking issue and to appear in `config/quality/vitest-exclusions.json` — the previous list grew to 62 files behind a comment pointing at an issue that had been closed for a month. `check-new-key-coverage` requires a key newly added to `en.json` to reach every locale; the existing coverage gate is a percentage floor per locale, so eleven absent keys out of ~13,000 left it at 99.9% while a whole feature shipped untranslated in nine languages. Both are diff-aware, so pre-existing debt stays frozen and neither needed a migration to turn on. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **build:** root layout no longer loads Inter from `next/font/google`, so a production image build does not need fonts.googleapis.com ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **docs(i18n):** bring the eight new `llm.txt` mirrors (km, kn, ml, my, ne, or, pa, si) up to the current provider and migration counts, so `check-docs-sync` stops rejecting every commit ([#13674](https://github.com/diegosouzapw/OmniRoute/pull/13674)) +- **refactor(ui):** flow surfaces (home topology, combo live studio, compression cockpit/waterfall/nodes, token-health badge) express state through the theme-aware `--orch-status-*` tokens instead of fixed dark-mode hex, so green/red/amber/grey stay legible in the light theme; dark mode is byte-identical. Categorical palettes (routing-strategy hues, compression-layer pills, provider brand colors) deliberately stay hex ([#12378](https://github.com/diegosouzapw/OmniRoute/issues/12378)) ([#13203](https://github.com/diegosouzapw/OmniRoute/pull/13203)) +- **chore(tests):** 51 test files that had been excluded from Vitest are running again, restoring roughly 350 assertions to the blocking suite. Each was measured individually first: of the 62 files parked behind the `// #8618 — pre-existing failure` comment, 51 pass against the current tree with no source change, so the exclusions had outlived the failures they were added for. The 11 that genuinely still fail stay excluded, but now point at a live tracker (#13204) rather than at #8618, which was closed in August while the list it tracked kept growing. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **chore(quality):** drop two ESLint suppression entries whose violations no longer exist, so `eslint --suppressions-location` stops rejecting every commit that touches the surrounding files ([#13384](https://github.com/diegosouzapw/OmniRoute/pull/13384)) +- **chore(release):** bring main's post-tag fixes into release/v3.8.51 — sync-back step 2/2 (direct commit `21c488f210`) +- **chore(deps):** patch toml and esbuild in the opencode plugin lockfiles ([#12965](https://github.com/diegosouzapw/OmniRoute/pull/12965)) +- **docs(changelog):** reconcile the v3.8.51 living section — fold 366 fragments, cover every cycle commit, credit every contributor ([#12971](https://github.com/diegosouzapw/OmniRoute/pull/12971)) +- **test(plugin):** wait for the v2 lazy-refresh tier instead of sleeping past it ([#12984](https://github.com/diegosouzapw/OmniRoute/pull/12984)) +- **chore(quality):** rebaseline chatCore.ts after the non-streaming regression fixes ([#13045](https://github.com/diegosouzapw/OmniRoute/pull/13045)) +- **docs(changelog):** add the missing bullet marker to the DeepSeek PoW fragment ([#13200](https://github.com/diegosouzapw/OmniRoute/pull/13200)) +- **chore(deps):** drain the Dependabot queue — 10 of 13 alerts ([#13213](https://github.com/diegosouzapw/OmniRoute/pull/13213)) +- **chore(ci):** make worktrees carry the hooks that enforce the identity gate ([#13236](https://github.com/diegosouzapw/OmniRoute/pull/13236)) +- **chore(deps):** pin csv-parse to ^7.0.2 via overrides (#13117) ([#13300](https://github.com/diegosouzapw/OmniRoute/pull/13300)) — thanks @KooshaPari +- **chore(deps):** bump hono from ^4.12.34 to ^4.13.7 (#13148) ([#13301](https://github.com/diegosouzapw/OmniRoute/pull/13301)) — thanks @KooshaPari +- **chore(deps):** pin joi to ^18.2.8 via overrides (#13085) ([#13302](https://github.com/diegosouzapw/OmniRoute/pull/13302)) — thanks @KooshaPari +- **docs(providers):** add Microsoft 365 Copilot (BizChat) provider guide (#12779) ([#13340](https://github.com/diegosouzapw/OmniRoute/pull/13340)) — thanks @KooshaPari +### 🙌 Contributors + +Thanks to everyone whose work landed in v3.8.51: + +| Contributor | PRs / Issues | +| --- | --- | +| [@5dive-bot](https://github.com/5dive-bot) | #11852 | +| [@abhisheksharma2411](https://github.com/abhisheksharma2411) | #11547, #11684, #11959, #12209 | +| [@adevwithpurpose](https://github.com/adevwithpurpose) | #11464 | +| [@adivekar-utexas](https://github.com/adivekar-utexas) | #12015, #12043, #12090, #12138 | +| [@AIB1TAL0S](https://github.com/AIB1TAL0S) | #12103 | +| [@alltomatos](https://github.com/alltomatos) | #11448 | +| [@alvinveroy](https://github.com/alvinveroy) | #11857, #12027, #12165 | +| [@amaleta](https://github.com/amaleta) | #11906, #11934 | +| [@amartinawi](https://github.com/amartinawi) | #11575 | +| [@AndrianBalanescu](https://github.com/AndrianBalanescu) | #11083, #11830, #11834, #11844 | +| [@anhtahaylove](https://github.com/anhtahaylove) | #13091, #13092, #13093, #13096, #13097, #13100, #13106, #13109, #13114, #13155, #13166, #13171, #13175, #13187, #13196 | +| [@aniruddhaadak80](https://github.com/aniruddhaadak80) | #11760 | +| [@app](https://github.com/app) | #12554 | +| [@arminanton](https://github.com/arminanton) | #11461, #11513 | +| [@AStupidBear](https://github.com/AStupidBear) | #11584 | +| [@atescivitci-cmd](https://github.com/atescivitci-cmd) | #12105 | +| [@b3nw](https://github.com/b3nw) | #11970, #11971, #12147 | +| [@backryun](https://github.com/backryun) | #11259, #11950, #12075, #12076, #12078, #12079, #12081, #12082, #12181, #12228, #12239, #12255, #12258, #12277, #12367, #12423, #12524, #12538 | +| [@benjaminkitt](https://github.com/benjaminkitt) | #11747 | +| [@benzntech](https://github.com/benzntech) | #11614, #11615 | +| [@Bl0ck154](https://github.com/Bl0ck154) | #11948, #11951, #11952, #11953, #11954 | +| [@brick30llc-ctrl](https://github.com/brick30llc-ctrl) | #11752, #12242, #12262 | +| [@Chewji9875](https://github.com/Chewji9875) | #11455, #11875, #12028 | +| [@cheynetom](https://github.com/cheynetom) | #12058 | +| [@davidebaraldo](https://github.com/davidebaraldo) | #12222, #12304 | +| [@davidlinfr](https://github.com/davidlinfr) | #6390 | +| [@ddarkr](https://github.com/ddarkr) | #12124 | +| [@Deftera186](https://github.com/Deftera186) | #11809 | +| [@dpozimski](https://github.com/dpozimski) | #11945 | +| [@DW-MediaLab](https://github.com/DW-MediaLab) | #12229 | +| [@echel0nn](https://github.com/echel0nn) | #11923 | +| [@f9td56dbgh-hub](https://github.com/f9td56dbgh-hub) | #11660 | +| [@fabioluissilva](https://github.com/fabioluissilva) | #11991 | +| [@foreveryh](https://github.com/foreveryh) | #12177 | +| [@geek007git](https://github.com/geek007git) | #12115, #12116, #12117, #12120, #12122 | +| [@geekyNads](https://github.com/geekyNads) | #11784, #12266 | +| [@ggiak](https://github.com/ggiak) | #11561, #12276, #12350, #12402 | +| [@gonisulaimann](https://github.com/gonisulaimann) | #12368, #12369, #12371 | +| [@Gorillaz322](https://github.com/Gorillaz322) | #12207 | +| [@groovecityJO](https://github.com/groovecityJO) | #12682 | +| [@hartmark](https://github.com/hartmark) | #11434, #11452, #11473, #11499, #11703, #11983, #11984, #11985, #11986, #11988, #11989, #11990, #11994, #12221, #12293, #12445, #12446, #12447, #12448, #12460, #12461, #12623, #12646, #12650, #12680, #12717, #12718, #12727, #12741, #12854 | +| [@hizzt](https://github.com/hizzt) | #11894 | +| [@hongnoul](https://github.com/hongnoul) | #11484 | +| [@HouMinXi](https://github.com/HouMinXi) | #11411, #11414, #11512, #11518, #11520, #11641, #11642, #11643, #11687, #11779, #11849, #11850, #11851, #11915, #11916, #11918, #11919, #11920, #12013, #12017, #12033, #12042, #12106, #12139, #12166, #12169, #12171, #12205, #12213, #12312, #12325, #12487, #12488, #12495, #12504, #12557, #12566, #12590, #12591, #12624, #12626, #12632, #12637, #12678, #12696, #12697, #12711, #12733, #12746, #12767, #12770, #12789, #12803, #12805, #12811, #12866, #12868, #12899, #12926, #12934, #12950, #12951, #12974, #13001, #13006, #13011, #13017, #13026, #13027, #13034, #13035, #13038, #13042, #13050, #13060, #13061, #13069, #13107, #13120, #13136, #13195, #13197 | +| [@Hsia97](https://github.com/Hsia97) | #11624 | +| [@jacobsparts](https://github.com/jacobsparts) | #11854, #12155, #12167 | +| [@joglomedia](https://github.com/joglomedia) | #11980 | +| [@jonlwheat2-gif](https://github.com/jonlwheat2-gif) | #11443, #11450, #11549, #11567, #11608, #11633, #11635, #11640, #11644, #11671, #11672, #11673, #11674, #11675, #11676, #11677, #11706, #11783, #12051, #12052, #12053, #12055 | +| [@kanade-hoshino](https://github.com/kanade-hoshino) | #12180 | +| [@Karan825](https://github.com/Karan825) | #11861 | +| [@KaspaPulse](https://github.com/KaspaPulse) | #11389, #11469, #11628, #11666, #11910 | +| [@keeltrace](https://github.com/keeltrace) | #12080, #12223 | +| [@killer30001000](https://github.com/killer30001000) | #12178 | +| [@KooshaPari](https://github.com/KooshaPari) | #12470, #12592, #12667, #12699, #12703, #12706, #12764, #12769, #12771, #13300, #13301, #13302, #13340, #13401, #13403, #13404, #13405, #13406, #13407, #13409, #13410, #13411, #13412, #13413, #13414, #13418, #13424, #13427, #13433, #13523, #13524, #13525, #13528, #13531, #13532, #13534, #13539, #13543, #13545, #13546, #13547, #13550, #13553 | +| [@kriptoburak](https://github.com/kriptoburak) | #11370 | +| [@KrzysiekSko](https://github.com/KrzysiekSko) | #12425, #12432, #12673 | +| [@marcelokarval](https://github.com/marcelokarval) | direct commit / report | +| [@maxmad64bis](https://github.com/maxmad64bis) | #1622, #11435, #11437, #11441, #11537, #11550, #11553, #11555, #11557, #11812, #11842, #11843, #11903, #12151, #12214, #12215, #12218, #12226, #12314, #12316, #12317, #12318, #12319, #12320, #12321, #12507, #12715, #12731, #12744, #12786, #12787, #12788, #12790, #12792, #12794, #12795, #12828, #12832, #12853, #12857, #12870, #12937, #12941, #12975, #13141, #13142, #13146, #13147 | +| [@mdigitalbh81](https://github.com/mdigitalbh81) | #12240 | +| [@Meet6338-X](https://github.com/Meet6338-X) | #11598, #11609, #12110 | +| [@morpheus9393](https://github.com/morpheus9393) | #11943 | +| [@MumuTW](https://github.com/MumuTW) | #11492, #11502, #11506, #11507, #11626, #11675, #11685, #11728, #11746, #11888, #11889, #11890, #11892 | +| [@Neuron-Mr-White](https://github.com/Neuron-Mr-White) | #11622, #11801, #12256 | +| [@NightStalker-87](https://github.com/NightStalker-87) | #12183 | +| [@NoxzRCW](https://github.com/NoxzRCW) | #11879, #11880, #11881, #11882, #11883, #11935 | +| [@ntdat812](https://github.com/ntdat812) | #11585, #12095 | +| [@ntdatt812](https://github.com/ntdatt812) | #11368, #11573, #11574, #11576, #11577, #11580, #11582, #11583, #11588, #11589, #11590, #11591, #11592, #11593, #11672, #12177, #12180, #12873, #12918, #12920, #12921, #12925, #12930, #13007, #13009, #13024, #13025, #13083, #13087, #13101, #13104, #13110 | +| [@official-burak](https://github.com/official-burak) | #11542 | +| [@opensource-elearning](https://github.com/opensource-elearning) | #12179, #12189, #12278, #12286 | +| [@oyi77](https://github.com/oyi77) | #11408, #11409, #11421, #11505, #11677, #12036, #12110 | +| [@pacocartones](https://github.com/pacocartones) | #11521, #11522, #11527, #11528, #11529, #11530, #11531, #11532, #11533, #11534, #11595, #11599, #11603, #11604, #11605, #11607, #11610, #11676, #11714, #11716, #11718, #11767, #11838, #11860, #11862, #11869, #11871, #11872, #11873, #11903, #11906, #11921, #11934, #12356, #12358, #12359, #12360, #12361, #12362, #12364, #12365, #12373, #12374, #12375, #12376, #12377, #12379, #12380, #12381, #12386, #12387, #12389, #12390, #12394, #12395, #12397, #12401, #12403, #12404, #12522, #12523, #12535, #12536, #12540, #12541, #12543, #12545, #12548, #12549, #12550, #12551, #12552, #12647, #12651, #12653, #13055 | +| [@patrykkopycinski](https://github.com/patrykkopycinski) | #11936, #11937, #12224 | +| [@PauloFH](https://github.com/PauloFH) | #11509 | +| [@PauloHSOliveira](https://github.com/PauloHSOliveira) | #12241 | +| [@ponkcore](https://github.com/ponkcore) | #12054 | +| [@Prajeeth-12](https://github.com/Prajeeth-12) | #11634, #12046 | +| [@quiterunner-commits](https://github.com/quiterunner-commits) | #12131, #12143 | +| [@rafacpti23](https://github.com/rafacpti23) | #11554, #11558, #12192, #12197, #12198, #12202, #12203, #12204 | +| [@ragnar-claude](https://github.com/ragnar-claude) | #11564 | +| [@raheemuddin786](https://github.com/raheemuddin786) | #11491, #11839, #11840, #11841, #12003, #12230, #12231, #12232, #12233, #12234 | +| [@rahilmavani](https://github.com/rahilmavani) | #11761 | +| [@Rahulsharma0810](https://github.com/Rahulsharma0810) | #11771 | +| [@RaviTharuma](https://github.com/RaviTharuma) | #11710, #11727, #11797, #11798, #11802, #11805, #11806, #11811, #12098, #12099, #12101, #12449, #12452, #12472, #12473, #12493, #12533, #12607, #12628, #12631, #12636, #12875, #12876, #12880, #12882, #12884 | +| [@rezjalibd](https://github.com/rezjalibd) | #12186 | +| [@RhianB14](https://github.com/RhianB14) | #11692 | +| [@rifqiawl](https://github.com/rifqiawl) | #11517, #11519, #11969 | +| [@rqzbeh](https://github.com/rqzbeh) | #11390 | +| [@Sabeekhann](https://github.com/Sabeekhann) | #11699, #11705 | +| [@santosraju99-hub](https://github.com/santosraju99-hub) | #11755, #11770 | +| [@Seramicx](https://github.com/Seramicx) | #11568 | +| [@Siva010](https://github.com/Siva010) | #12191 | +| [@solstxce](https://github.com/solstxce) | #11597 | +| [@soroush5](https://github.com/soroush5) | #12691 | +| [@tenshiak](https://github.com/tenshiak) | #12279 | +| [@TheDemonTuan](https://github.com/TheDemonTuan) | #11468, #11470, #11471, #11482, #11548, #11758, #11775, #11814 | +| [@thomasmaerz](https://github.com/thomasmaerz) | #12834 | +| [@tuandinh0801](https://github.com/tuandinh0801) | #11454 | +| [@turbolego](https://github.com/turbolego) | #11621, #11762, #11772, #11774, #11781, #12216 | +| [@Tushar49](https://github.com/Tushar49) | #11392 | +| [@ujjawalkaushik1110](https://github.com/ujjawalkaushik1110) | #11863 | +| [@vermasomesh835](https://github.com/vermasomesh835) | #11794 | +| [@vsd2807](https://github.com/vsd2807) | #11565, #11619 | +| [@wahidsadik371-coder](https://github.com/wahidsadik371-coder) | #12126 | +| [@watchingdogs](https://github.com/watchingdogs) | #12031 | +| [@wildcard](https://github.com/wildcard) | #11620 | +| [@xiaoyaner0201](https://github.com/xiaoyaner0201) | #11460, #11662, #12083 | +| [@Xxx91n](https://github.com/Xxx91n) | #11690 | +| [@yourspraveen](https://github.com/yourspraveen) | #11146 | +| [@yxyxy](https://github.com/yxyxy) | #11671 | +| [@ZaimMarzuki](https://github.com/ZaimMarzuki) | #11960 | +| [@Zartharas](https://github.com/Zartharas) | #11340 | +| [@zero-executioner](https://github.com/zero-executioner) | #11631 | +| [@diegosouzapw](https://github.com/diegosouzapw) | maintainer | + +--- ## [3.8.50] — 2026-08-25 _Living section — regenerated 2026-08-12 from all cycle commits (cycle open `ed2db6cb19` → tip). Bullets carry the merged PR and its author; direct pushes listed separately._ diff --git a/docs/i18n/te/CHANGELOG.md b/docs/i18n/te/CHANGELOG.md index c22966d88d..b563c8ea09 100644 --- a/docs/i18n/te/CHANGELOG.md +++ b/docs/i18n/te/CHANGELOG.md @@ -8,16 +8,1119 @@ ## [3.8.51] — TBD -_Living section — cycle opened at the v3.8.50 freeze (parallel-cycle model). Bullets are aggregated from `changelog.d/` fragments at each `/generate-release` phase._ +_Living section — reconciled 2026-09-15 from all cycle commits (`091589089c` → `c0f92ec98a`, 916 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 | **123** | +| 📝 Commits in the cycle | **916** | +| 🔀 Pull requests referenced | **892** | +| 📋 Changelog entries | **919** | +| 🙌 Contributors credited in entries | **121** | +| 🤖 Automated dependency commits | 18 | + +**Entries by type** + +| Type | Count | +| --- | ---: | +| 🐛 Fixes | 592 | +| ✨ Features | 131 | +| 🧹 Chore | 93 | +| 📚 Docs | 42 | +| 🧪 Tests | 32 | +| ♻️ Refactor | 8 | +| 🏗️ Build | 7 | +| ⚡ Performance | 7 | +| ⚙️ CI | 2 | +| ⏪ Reverts | 2 | +| 📦 Dependencies | 2 | +| 🔒 Security | 1 | + +### 🏆 Top 25 contributors this cycle + +_By commits in `091589089c..c0f92ec98a`, author identities consolidated via `.mailmap` and the merged PR's GitHub login. Bots excluded._ + +| # | Contributor | Commits | +| ---: | --- | ---: | +| 🥇 | diegosouzapw | 323 | +| 🥈 | Bob.Hou (@HouMinXi) | 79 | +| 🥉 | Paco Cartones (@pacocartones) | 77 | +| 4 | Dizzle (@maxmad64bis) | 49 | +| 5 | Koosha Paridehpour (@KooshaPari) | 43 | +| 6 | Nguyen Thanh Dat (@ntdatt812) | 31 | +| 7 | Ravi Tharuma (@RaviTharuma) | 26 | +| 8 | Markus Hartung (@hartmark) | 22 | +| 9 | Webman (@jonlwheat2-gif) | 22 | +| 10 | backryun | 17 | +| 11 | anhtahaylove | 15 | +| 12 | Rafa Martins (@rafacpti23) | 10 | +| 13 | Syed Raheemuddin (@raheemuddin786) | 10 | +| 14 | Nguyễn Viết Tuấn (@TheDemonTuan) | 8 | +| 15 | MumuTW | 7 | +| 16 | Paijo (@oyi77) | 7 | +| 17 | Tobias Andersen (@turbolego) | 6 | +| 18 | Bl0ck (@Bl0ck154) | 5 | +| 19 | SHANMUGAPRIYAN (@geek007git) | 5 | +| 20 | KaspaPulse | 5 | +| 21 | Abhishek Sharma (@abhisheksharma2411) | 4 | +| 22 | Abhishek Divekar (@adivekar-utexas) | 4 | +| 23 | Andrew B. (@AndrianBalanescu) | 4 | +| 24 | NoxzRCW | 4 | +| 25 | opensource-elearning | 4 | ### ✨ New Features +- **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). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- Added Google AI Studio Gemini batch text-to-speech support through `POST /v1/audio/speech`. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- 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. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(routing):** subscription-first auto groupings — `auto/subscription` routes only through plan-included connections with a documented hard-stop overage and fails closed on exhaustion, while `auto/thrifty` orders the pool `subscription → keyless → free → cheap → premium` and steps up one rung at a time as each is exhausted. Billing class comes from a curated per-connection catalog (uncurated is treated as metered, never plan-included), both reuse STRICT_ZERO_COST's per-connection verification, and a quota reading whose `resetAt` has passed is now refreshed regardless of TTL so routing returns to plan capacity as soon as it resets ([#11146](https://github.com/diegosouzapw/OmniRoute/pull/11146)) — thanks @yourspraveen +- **feat(providers):** publish a management-authenticated versioned web-session credential contract from OmniRoute's canonical browser credential metadata ([#11340](https://github.com/diegosouzapw/OmniRoute/pull/11340)) — thanks @Zartharas +- **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)) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add Xquik X search with typed results, credential validation, REST routing, and MCP selection ([#11370](https://github.com/diegosouzapw/OmniRoute/pull/11370)) — thanks @kriptoburak +- **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)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(dashboard):** surface durable exclusive managed leases in the existing Sessions view, keeping leased clients visible across idle gaps while marking connections with in-flight work as active ([#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389)) — thanks @KaspaPulse +- **build(bun):** allow Turbopack bundler flag on Bun 1.4+ with configurable Webpack fallback ([#11471](https://github.com/diegosouzapw/OmniRoute/pull/11471)) — thanks @TheDemonTuan +- feat(api): add an opt-in `modelVisibilityAllowlist`/`modelVisibilityDenylist` settings pair to curate exactly which models `/v1/models` advertises, mirrored into every `auto/*` combo candidate pool so a denied model cannot be routed to via combo selection either (#11481) ([#11997](https://github.com/diegosouzapw/OmniRoute/pull/11997)) +- **feat(rankings):** the Free Provider Rankings page shows what each provider actually served over the last 24 h. It ranked by ELO alone, which left a provider that answers every call with an error in first place; the usage data was already served by the API but never requested. A provider with too small a sample shows a dash, not a number ([#11546](https://github.com/diegosouzapw/OmniRoute/pull/11546)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **feat(guardrails):** enforce a bounded, deterministic contract for Video Bridge transcripts — 256 cues, 4096 input code units and 4 KiB UTF-8 per cue, 64 KiB total text, malformed-Unicode rejection, focus-window scoping, cross-source reconciliation with contributing-source metadata, and a structural provenance trust boundary so caller JSON can never self-assert `embedded`/`audio-bridge` provenance ([#11652](https://github.com/diegosouzapw/OmniRoute/issues/11652)) ([#12009](https://github.com/diegosouzapw/OmniRoute/pull/12009)) +- **feat(video):** orchestrate optional Video Bridge audio extraction and Audio Bridge STT behind a dual opt-in (operator setting AND per-request signal) — a new loopback-only broker `mode=audio` operation shares the frame path's exact process queue, deadline, AbortSignal, and byte budgets to extract a bounded mono 16 kHz PCM WAV from the same already-downloaded video, then reuses the existing Audio Bridge transcription boundary; provider segment timing is preserved when available and marked coarse otherwise, and every failure degrades to a visual-only-safe partial instead of throwing (#11654). ([#12012](https://github.com/diegosouzapw/OmniRoute/pull/12012)) +- Add a tenant-bound Video Bridge drill-down lifecycle on top of the existing secure cache substrate: opaque hashed handles (never raw session/video identifiers), preview/standard/detail multiresolution variants resampled on read, response pagination capped at 8 frames and 32 MiB, and a new authenticated `/api/v1/video-bridge/drilldown` consumer route that stays disabled for remote access by default and denies cross-key access with the same response as a nonexistent handle (no existence oracle). ([#12006](https://github.com/diegosouzapw/OmniRoute/pull/12006)) +- **test(video):** Add the Video Bridge FU-07/FU-09 promotion-evidence harness (#11656) — a frozen Zod manifest schema covering the 8 required scenario kinds (static scenes, rapid cuts, late facts, fades, blur, small text, close events, visual prompt injection) with a minimum of 3 repetitions per case, deterministic declarative fixture recipes (`videoBridgePromotionFixtures.ts`), a pure medians/p95 metrics aggregator, a pure FU-07/FU-09 promotion-verdict evaluator applying the ticket's exact thresholds (missing token usage always holds), a digest-only persistence layer that never retains raw media or raw model responses, and a versioned per-model promotion allowlist shipped empty with every model defaulting to `hold`. The FU-07/FU-09 promotion verdicts themselves remain HOLD — they require a real evidence run against real models on VPS 192.168.0.15. ([#12008](https://github.com/diegosouzapw/OmniRoute/pull/12008)) +- **feat(video bridge):** "embedded" transcript provenance can now be legitimately earned instead of merely asserted — a bounded, allowlisted (`mov_text`/`subrip`/`webvtt`) subtitle probe runs through the loopback-only Video Bridge broker (at most 2 streams, 10s subdeadline bounded by the request deadline, 256 KiB output, 4096-code-unit lines), normalized through a bounded, ReDoS-safe WebVTT parser and Zod-validated end to end. The adapter always resolves to an explicit `success`/`absent`/`transient_failure` outcome — a subtitle failure never breaks the visual description path, and only a fingerprint-verified broker response (never a caller-declared label) can produce embedded cues (#11659). ([#12011](https://github.com/diegosouzapw/OmniRoute/pull/12011)) +- Default new Antigravity-family connections (agy CLI imports and Antigravity OAuth connects) to model auto-sync, so live model discovery lands in the synced catalog and `/v1/models` picks up freshly released upstream models (e.g. Gemini 3.7 Flash tiers) without code changes. Existing connections keep their current setting; the per-connection dashboard toggle remains the opt-out. (#11685 — thanks @MumuTW) ([#11887](https://github.com/diegosouzapw/OmniRoute/pull/11887)) +- **feat(ui):** enable React Compiler (`reactCompiler: true` + `babel-plugin-react-compiler`) for automatic memoization at build time ([#11783](https://github.com/diegosouzapw/OmniRoute/pull/11783)) — thanks @jonlwheat2-gif +- **feat(providers):** add SeekAi (`seekai.cc`) as an OpenAI-compatible New-API gateway — catalog id `seekai` (alias `ska`), `https://seekai.cc/v1`, live `/v1/models` via `passthroughModels`, aggregator-list membership so New-API balance detection can opt in. No referral/aff codes. ([#11786](https://github.com/diegosouzapw/OmniRoute/issues/11786)) ([#12557](https://github.com/diegosouzapw/OmniRoute/pull/12557)) — thanks @HouMinXi +- **feat(zai):** add GLM-5.3-Flash Coding Plan support (1M context, 128K output, vision, `low|high|max` reasoning) and route `zai` GLM-5.3-family API-key traffic through the OpenAI-compatible Coding Plan endpoint with native thinking defaults ([#11801](https://github.com/diegosouzapw/OmniRoute/pull/11801)) — thanks @Neuron-Mr-White +- **feat(combo):** choose how combo models are ordered — manual, provider, score, or name — via a sort control in the dashboard builder, persisted in `config.modelSort` and re-applied on load and after add ([#11812](https://github.com/diegosouzapw/OmniRoute/pull/11812)) — thanks @maxmad64bis +- **feat(free):** custom models can be marked free-tier via `customModels[].isFree`; `isFreeModel()` is the first door and `hidePaidModels` respects it even for providers outside the free budget ([#11843](https://github.com/diegosouzapw/OmniRoute/pull/11843)) — thanks @maxmad64bis +- **feat(nodejs):** add `5dive` as a `configure` target — `omniroute configure 5dive` / `omniroute setup-5dive` write a 5dive auth profile that points an agent fleet's `claude` seats at OmniRoute, with the root-only write, the loopback-vs-`https` endpoint rule and the per-seat model pin handled explicitly ([#11852](https://github.com/diegosouzapw/OmniRoute/pull/11852)) — thanks @5dive-bot +- **feat(sse):** treat `max` as a first-class reasoning-effort tier and clamp per model family (GLM 5.1+/DeepSeek V4+/Kimi K3+ keep native `max`; o1/MiniMax/Grok/Muse Spark clamp to their upstream ceiling) ([#11875](https://github.com/diegosouzapw/OmniRoute/pull/11875)) — thanks @Chewji9875 +- **feat(providers):** the provider plugin manifest now advertises a `usage-fetch` capability for the 40 providers that have a wired usage/quota fetcher, so external dashboards can read it from `GET /api/v1/provider-plugin-manifest` instead of parsing `open-sse/services/usage.ts` after every release. Discovery only — no new fetcher, no quota change, and the Dashboard quota widget stays gated by `USAGE_SUPPORTED_PROVIDERS`. `USAGE_FETCHER_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/fetcherProviders.ts`) and is re-exported from `services/usage.ts`, keeping the manifest module a light leaf instead of pulling the ~490-module usage dispatcher into the manifest route. ([#11903](https://github.com/diegosouzapw/OmniRoute/pull/11903)) — thanks @maxmad64bis / @pacocartones +- **feat(plugins):** `OMNIROUTE_PLUGINS_DIR` sets the directory the runtime plugin scanner reads — and the root the plugin manager installs into — overriding the `HOME`-derived default, so a Docker/K8s deployment can point straight at its bind-mounted plugin tree instead of moving `HOME` just to relocate the scan path. An image that exports no home no longer scans `/tmp/.omniroute/plugins` in silence: the resolved directory is logged once at startup as `scanner.dir_resolved`, naming the input that won. Unset, behaviour is unchanged. Distinct from the CLI-only `OMNIROUTE_PLUGIN_PATH`, which finds `omniroute-cmd-*` command packages and never reached this scanner ([#11906](https://github.com/diegosouzapw/OmniRoute/pull/11906)) — thanks @amaleta / @pacocartones +- **feat(leases):** add an explicit owner-authenticated status action that returns only the active lease's privacy-safe configured connection and provider labels, with generation fencing and no credential or internal-id disclosure ([#11910](https://github.com/diegosouzapw/OmniRoute/pull/11910)) — thanks @KaspaPulse +- **feat(providers):** import-from-file modal shows per-row API errors and ships a downloadable CSV template ([#12071](https://github.com/diegosouzapw/OmniRoute/issues/12071)) ([#12504](https://github.com/diegosouzapw/OmniRoute/pull/12504)) — thanks @HouMinXi +- **feat(providers):** dashboard search matches connection name and `baseUrl` so imported OpenAI-compat nodes surface on the provider card ([#12108](https://github.com/diegosouzapw/OmniRoute/issues/12108)) ([#12495](https://github.com/diegosouzapw/OmniRoute/pull/12495)) — thanks @HouMinXi +- **feat(routing):** add a `score` Auto router strategy that selects the highest configured weighted score and reuses `explorationRate`. ([#12155](https://github.com/diegosouzapw/OmniRoute/pull/12155)) — thanks @jacobsparts +- Add a runtime feature flag to disable universal context handoffs globally without changing the default behavior. ([#12167](https://github.com/diegosouzapw/OmniRoute/pull/12167)) — thanks @jacobsparts +- **feat(providers):** the provider plugin manifest now also advertises a `usage-supported` capability for the 46 providers whose usage API is accepted by the server and Dashboard routes, so integrators can distinguish "the server will serve quota for this provider" from "a fetcher is wired" without reading TypeScript. Discovery only — no fetcher or quota change. `usage-fetch` resolves on id or alias (the usage dispatcher accepts both); `usage-supported` resolves on id alone, matching the runtime guard `USAGE_SUPPORTED_PROVIDERS.includes(providerId)`. `USAGE_SUPPORTED_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/supportedProviders.ts`) and is re-exported from `providers.ts`, mirroring the `fetcherProviders` leaf from #11903 and keeping the manifest a light module. ([#12214](https://github.com/diegosouzapw/OmniRoute/pull/12214)) — thanks @maxmad64bis +- **feat(rankings):** order Free Provider Rankings by what each provider actually served — `GET /api/free-provider-rankings?sortBy=reliability` and a "Most reliable first" toggle on the page. Providers with too few calls to state a success rate keep their score order below the measured ones; the default order is unchanged ([#12218](https://github.com/diegosouzapw/OmniRoute/pull/12218)). — thanks @maxmad64bis +- **perf(sse):** defer `cloneLogPayload()` in the structured SSE collector until after the `maxEvents`/`maxBytes` cap check, eliminating ~9,800 wasted `structuredClone` calls per streaming response (65–71% faster `push()`). Reducer snapshot isolation restored for OpenAI and Responses summaries ([#12241](https://github.com/diegosouzapw/OmniRoute/pull/12241)) — thanks @PauloHSOliveira ([#12243](https://github.com/diegosouzapw/OmniRoute/pull/12243)) +- **feat(usage):** Devin CLI agentic quota (Codeium seat-management GetUserStatus) and OpenRouter key limits plus account credits now surface in Provider Limits ([#12256](https://github.com/diegosouzapw/OmniRoute/pull/12256) — thanks @Neuron-Mr-White) +- **feat(settings):** persist `headroomUrl` through Settings so status/start use the operator URL instead of only `HEADROOM_URL` ([#12306](https://github.com/diegosouzapw/OmniRoute/issues/12306)) ([#12487](https://github.com/diegosouzapw/OmniRoute/pull/12487)) — thanks @HouMinXi +- **feat(auto-combo):** Auto-Combo scoring can now weigh how often a provider/model has actually succeeded. The engine already carried that number on every candidate — 24 hours of usage history behind a ten-sample floor, real-time metrics otherwise — and the scoring function never read it, while the weight table described `stability` as if it did. `reliability` (`1 - failureRate`, with the same field precedence and the same rate-bounding the speed ranking already uses, so a corrupt reading means "nothing observed" rather than "fails every call") is now a declared factor shipping at weight `0`, so routing is unchanged until an operator gives it one, and the `stability` description now matches what that factor computes ([#12317](https://github.com/diegosouzapw/OmniRoute/pull/12317)) — thanks @maxmad64bis +- **feat(routing):** With `freeAccessPolicy: "strict"`, the read-only candidate listing (`GET /v1/auto-combo/{channel}/candidates`) no longer hides the candidates the zero-cost guard excludes — the same read-only transparency the resilience filter already honours (#9133). Each candidate now carries `freeAccessExclusion` saying why it would be kept out, and it tells an exhausted allowance apart from a quota reading that never arrived or went stale, which used to look identical from the outside. Routing is unchanged: the listing reports, it never enforces. The separate `excludeTosAvoid` guard still drops its candidates without a reason; that gap is now documented rather than closed ([#12319](https://github.com/diegosouzapw/OmniRoute/pull/12319)) — thanks @maxmad64bis +- **feat(radar):** The Radar catalog table now shows two facts it was already receiving from the feed and dropping on the floor: the per-model rate limits (requests and tokens, per minute and per day) in a new column, and a badge when a provider's terms state it may train on the prompts you send. A limit of zero renders as zero rather than "rate-only" — for a ceiling those are opposite facts — and a model with no training statement gets no badge, because an absent statement is not a guarantee ([#12320](https://github.com/diegosouzapw/OmniRoute/pull/12320)) — thanks @maxmad64bis +- **feat(radar):** explain Community, single-use, contributor, supporter, recovery, abuse, offers, and privacy rules before either Radar activation action, and remove the superseded fixed-PR grant promise from every UI locale ([#12342](https://github.com/diegosouzapw/OmniRoute/pull/12342)) +- **feat(gamification):** the dashboard Profile page now shows the real daily streak — `/api/gamification/level` returns `streak: { current, longest }` (per key with `apiKeyId`, operator-wide maximum otherwise) and the streak card reads it instead of a hard-coded 0 (#2403) ([#12377](https://github.com/diegosouzapw/OmniRoute/pull/12377)) — thanks @pacocartones +- **feat(gamification):** the dashboard leaderboard now shows each API key's display name under the Name column instead of a truncated key id; `GET /api/gamification/leaderboard` attaches `name` per entry (name only — no key material), while the shared ranking helper and the federation leaderboard stay id-only — thanks @pacocartones ([#12385](https://github.com/diegosouzapw/OmniRoute/pull/12385)) +- **feat(gamification):** enforce the documented 1000 XP/min per-API-key anti-cheat rate limit on the XP award path; over-limit awards are logged and skipped instead of persisted, and the sliding window now matches the timestamp format stored in `xp_audit_log` ([#2403](https://github.com/diegosouzapw/OmniRoute/issues/2403)) ([#12390](https://github.com/diegosouzapw/OmniRoute/pull/12390)) — thanks @pacocartones +- **feat(admin):** localize the gamification anomalies page — the loading state, the Status column and the Suspicious badge now come from the `common` catalog (new `common.suspicious` key propagated to every locale) — add it to the Gamification sidebar group as `gamification-admin` (`/dashboard/gamification/admin`), and expose the loading and empty states as polite `role="status"` live regions (#12401 — thanks @pacocartones) +- **feat(providers):** skip GitHub combo members missing from the live synced catalog, and drop Copilot models that are policy-disabled or hidden from the model picker ([#12473](https://github.com/diegosouzapw/OmniRoute/pull/12473)) — thanks @RaviTharuma +- **feat(i18n):** locale-expansion tooling — `npm run i18n:add-locale` adds a language to config, dashboard, docs mirrors, CLI, README/indexes and site in one command; browser, CLI and cookie detection resolve `uk`, `fil`/`tl`, `zh-Hant` (and the retired legacy `in` → `id`) via config aliases; `config/i18n.json` now ships in the npm package so the published CLI can read it; new real-translation ratio gate (`npm run i18n:check-ratio`, advisory) with a per-locale ratchet baseline; `run-translation --adopt` restores `.i18n-state.json`; `sync-language-bars` generates 🌐 bars from config; `validate_translation.py` loads its allowlist again. Retires the duplicate `in` locale (Indonesian mislabelled as Hindi) — 42 honest locales; saved `NEXT_LOCALE=in` / `OMNIROUTE_LANG=in` keep working. (#12496) +- **feat(api):** Emit gateway-measured `tokens_per_second` (TTFT excluded) on streaming usage and `X-OmniRoute-Tokens-Per-Second` when first-token latency is known ([#12616](https://github.com/diegosouzapw/OmniRoute/issues/12616)) ([#12631](https://github.com/diegosouzapw/OmniRoute/pull/12631)) — thanks @RaviTharuma +- **feat(opencode-plugin): map gateway cost/usage/tok/s onto OpenCode inference payloads** — the official plugin copies `X-OmniRoute-Response-Cost`, token counts, `X-OmniRoute-Tokens-Per-Second` / `usage.tokens_per_second`, TTFT, and the winning `X-OmniRoute-Model` onto the JSON/SSE body OpenCode already consumes. Missing tok/s is left unset (never `tokens / latency`). (#12636) — thanks @RaviTharuma +- **feat(combo):** add combo strategy `quota-weighted`: skip exhausted accounts, then weighted-draw by leftover / in-flight load; existing conversations stay pinned until the account is empty ([#12789](https://github.com/diegosouzapw/OmniRoute/pull/12789)) — thanks @HouMinXi +- **feat(models):** Account live listings become the chat catalog source for Claude, Codex, Copilot, and AGY; public metadata only fills prices on IDs those accounts already list. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **fix(models):** Union `agy` and `antigravity` live catalogs so `agy/gemini-3.8-flash-high` is not rejected after the prefix folds to `antigravity`. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **feat(opencode):** Add `@omniroute/opencode-plugin-v2`, an opencode v2 plugin that gives a v2 install a route to an OmniRoute gateway: it publishes models, combos and auto-combos into the host catalog, refreshes them lazily behind a 300s TTL, and keeps serving the last known catalog from an on-disk snapshot when the gateway is unreachable. The v1 plugin (`@omniroute/opencode-plugin`) is untouched and keeps working as before. The plugin adapts to the host instead of assuming one shape: it reads the shape the host seeds into the catalog draft and publishes the `api` block, the pre-`api` top-level fields, or both. The gateway key comes from the host credential store when one is connected, so no secret needs to sit in a config file; a source the gateway refuses is named in a warning instead of silently degrading the catalog to raw model ids; and tool schemas bound for a Gemini model are stripped of the JSON-Schema keywords Gemini rejects, so tool calling survives that route ([#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870)) — thanks @maxmad64bis +- feat(services): show sanitized CLIProxyAPI account health from its authenticated management API without exposing credentials, file paths, or raw account metadata (#6342) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add AnySearch free web search + URL extract (webFetch) with typed results, credential validation, REST routing, and MCP selection - fallback-only ([#11690](https://github.com/diegosouzapw/OmniRoute/pull/11690)) — thanks @Xxx91n +- **feat(dashboard):** display clamped `[0, 100]%` cached input token ratio in request logs table ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) — thanks @b3nw +- **feat(catalog):** add `OMNIROUTE_DISABLE_THINKING_LEVEL_VARIANTS` feature flag to optionally filter out thinking level variants from model catalog ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) — thanks @b3nw +- **feat(dashboard):** continuously export call logs to external analytics stores. A pluggable destination registry ships the full Logs-tab record set on an hourly `JobRegistry` cron, with a persisted per-destination cursor, batched inserts, a config UI rendered from each destination's own field descriptors, and a REST layer (`/api/log-export/*`) for CRUD, a connection test, and an on-demand run. A destination can opt into `includeBodies` to also ship the request and response payloads shown in the Logs detail pane, including the client and provider views of each call; this is off by default, and payloads inherit the dashboard's PII sanitisation, secret redaction and `noLog` handling. Google BigQuery is the first destination, using a service-account key stored encrypted at rest and streaming inserts keyed by call-log id, into a table that is day-partitioned on `timestamp` and clustered on `api_key_name`, `provider`, `model` and `status`. ([#11945](https://github.com/diegosouzapw/OmniRoute/pull/11945)) — thanks @dpozimski +- **feat(providers):** add MaxAI as a signed, OpenAI-compatible provider serving its 13 paid chat models (GPT-5.6 / Luna / Thinking, Claude 5 Sonnet, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite, Grok 4.1-fast / 4.5, DeepSeek V3.2 / R1, Llama 3.3 70B) through OmniRoute's `/v1` endpoint, with per-request HMAC-SHA1→SM3→AES request signing, live model + context-window discovery from `/models/get_config`, and prompted tool-calling translated to OpenAI `tool_calls` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI vision input — image_url content parts are forwarded inline in `message_content` to the 6 vision-capable models (GPT-5.6 / Luna / Thinking, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite) ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI image generation — 6 image models (gpt-image-1, dall-e-3, flux-1-schnell/dev/pro, sd3-medium) exposed through `POST /v1/images/generations` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI document RAG — inline base64 file/document attachments are uploaded to MaxAI (content-addressed `doc_id`) and attached to the chat via `doc_list` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** browserless MaxAI onboarding — email device-pair login (`/api/providers/[id]/login`) and signed access-token refresh, so a connection can be created and kept fresh without a real browser or Google OAuth ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** per-provider TLS impersonation profile (MaxAI presents a Windows Firefox-150 client fingerprint) so its bot-sensitive endpoints accept OmniRoute traffic ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** add **Nimble** as a web-search and web-fetch provider (`nimble-search`) — `/v1/search` routes to Nimble's search API at `lite` depth (locale, freshness and include/exclude domain filters mapped onto the shared request contract), and `/v1/web/fetch` routes to Nimble Extract, which covers all four fetch formats (`markdown`, `html`, `links`, `screenshot`) from a single call. One API key serves both surfaces. ([#11620](https://github.com/diegosouzapw/OmniRoute/pull/11620)) — thanks @wildcard +- The npm publish is automatic again, through npm Trusted Publishing (OIDC): the hosted `stage-npm` job publishes with a short-lived credential minted from GitHub's id-token — no `NPM_TOKEN`, no 2FA prompt, provenance attached. `publish_mode=staged` (owner approves with 2FA) and `direct` (token) remain available on `workflow_dispatch`. ([#11931](https://github.com/diegosouzapw/OmniRoute/pull/11931)) +- **feat(providers):** Add **Opper** as an API-key gateway provider — EU-hosted AI gateway with 700+ models from 30+ providers behind one OpenAI-compatible API and one key (`OPPER_API_KEY`); model ids use `provider/model` format (e.g. `anthropic/claude-sonnet-4-6`, `openai/gpt-5`); live model catalog at `https://api.opper.ai/v3/compat/models`; entry mirrors `requesty` (same shape, `passthroughModels: true`, no static seed) ([#11629](https://github.com/diegosouzapw/OmniRoute/pull/11629)) +- **feat(dashboard):** the `/dashboard/orchestration` snapshot hook now subscribes to the `agents` WebSocket channel (`agent.task.updated`) instead of `requests` as its refetch trigger, and relaxes its background poll from 5s to 30s while that WS connection is up — falling back to the tighter 5s cadence, reprogrammed live on any connect/disconnect transition, whenever the socket is down. ([#12409](https://github.com/diegosouzapw/OmniRoute/pull/12409)) +- **feat(dashboard):** Orchestration canvas quick wins — search box plus state/source/provider filter chips with a one-click clear, and per-source collapse/expand, all reflected in the URL so a filtered/collapsed view is shareable and survives a refresh; the detail drawer gained a "copy trace JSON" action and hardened error/empty-state and accessibility handling; the Agents-tab edges now animate traveling particles along active (running) connections; and the canvas node/edge status colors moved off fixed hex values onto theme-aware `--orch-status-*` CSS custom properties, so they adapt correctly to light/dark mode. ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- **chore(dashboard):** Orchestration UI hardening pass and the missing component/model test coverage it called for — `OrchestratorNode`/`ActivityNode`/`OverflowNode` rendering, the `?node=`/overflow-click page routing, the Agents-tab orchestrator-click no-op and `showCompleted` toggle, and the overview kanban's done-column sort order (#12270, #12271). ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- New `/dashboard/orchestration` page: live unified view of everything running — Cloud Agent, A2A and Conductor as a real-time graph (Agents tab), the combo cascade (Routing tab, reusing the Combo Live Studio) and a state kanban (Overview tab), with a detail drawer (trace, cost, approve/cancel). Read-only over existing APIs — no new backend. Canvas concept credit: PR #11815 design ([#12261](https://github.com/diegosouzapw/OmniRoute/pull/12261)) +- **feat(dashboard):** new "History" tab on `/dashboard/orchestration` — an Airflow-style grid of finished runs over a 24h/7d/30d preset window, one row per (source, identity), clicking a cell opens the existing detail drawer. It is backed by real persistence: A2A task lifecycle transitions are now written to the `a2a_tasks` table (purged after 30 days, configurable via `OMNIROUTE_A2A_HISTORY_RETENTION_DAYS`) and served by the new `GET /api/a2a/tasks/history` listing endpoint, with the task-detail route falling back to persisted history once a run leaves the in-memory snapshot. Conductor runs stay remote and are not persisted locally — the tab says so instead of silently omitting them. ([#12479](https://github.com/diegosouzapw/OmniRoute/pull/12479)) +- **feat(dashboard):** the orchestration detail drawer gained a "Repeat" action for Cloud Agent, A2A and Conductor tasks — a two-click confirm (click once to arm, click again within the confirm window to fire) re-submits the original prompt/input as a new run. The button is disabled with an explanatory tooltip whenever the original input can't be recovered from the loaded task detail (e.g. it never carried a prompt, or the detail failed to load). Two limitations of the A2A variant, by design: it targets the `/a2a` JSON-RPC endpoint, which authenticates with an API key only (`REQUIRE_API_KEY=true` or a configured `OMNIROUTE_API_KEY` makes a dashboard-session repeat answer `HTTP 400` — surfaced verbatim in the drawer's error line, never as a success), and the `message/send` call is SYNCHRONOUS: the POST blocks for the whole skill run, so the success confirmation only appears once the repeated task finishes. A dashboard-authenticated A2A creation path is deliberately left to a follow-up — widening the endpoint's auth posture is an operator decision, not a side effect of this feature. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(a2a):** A2A task execution now records which memories were consulted for the task's last user message as `metadata.memoryHits` (id/key/type/content-snippet) plus a `memory_hits` history event, purely for observability — the retrieved memory is never injected into a skill's prompt or behavior. Gated by the `OMNIROUTE_A2A_MEMORY_HITS` kill-switch (default enabled; set to `0` to skip the recall lookup entirely). The drawer's new "Memory used" section lists these hits for a2a tasks and is omitted whenever there are none. Known limitation: recall only resolves under the keyless posture — a keyed caller's task owner is a SHA-256 prefix of the API key, while memory rows are keyed by the database api-key id, and no hash→id lookup exists today, so the hit list stays empty for keyed callers. The recorded hits are also kept out of the task's own `input` (and therefore out of the persisted input and of the "Repeat" request body), so repeating a task never re-sends the previous run's memory snippets. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(providers):** add a Perplexity Agent API provider (`perplexity-agent` / `pplx-agent`) for Perplexity `/v1/responses`, including the documented Anthropic, OpenAI, Google, xAI, DeepSeek, Z.AI, Moonshot/Kimi, NVIDIA, and Perplexity model IDs plus Anthropic-model `max_output_tokens` compatibility. ([#12103](https://github.com/diegosouzapw/OmniRoute/pull/12103)) — thanks @AIB1TAL0S +- **feat(providers):** add RPD (Requests Per Day) limit to provider rate limit overrides across UI, schemas, DB, and i18n ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) — thanks @b3nw +- **feat(providers): add UC Direct (uncensored.com Developer API), the metered OpenAI-compatible surface.** A standard OpenAI-compatible passthrough (default executor) for uncensored.com's official REST API at `https://api.uncensored.com/api/v1`: `X-api-key` auth (never-expiring `uai_sk_live_` key), `POST /chat/completions` with streaming SSE and native tool-calling, and the full live metered catalog (82 models across 15 providers, discovered from the public `GET /v1/models`). Registered as provider `uc-direct` (alias `ucd`). Complements the un-metered `uc` persona provider — same models, metered credits and a plain API key instead of a subscription session. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(providers): add UC (uncensored.com), the un-metered subscription "persona" chat as an OpenAI-compatible provider.** A WebSocket web-app port: a durable Clerk credential mints a short-lived session token per connect (browserless — no API key), driving UC's persona socket. Ships the browserless email-code login (request → verify → harvest), the 19 verified persona models (Claude Opus, Gemini, Grok, GLM, Kimi, DeepSeek, MiniMax, incl. the uncensored variants), prompted `` tool-calling with a per-model code-style dialect + auto-cure retry for guardrailed models, live ``/reasoning split, streaming + non-streaming OpenAI responses, and full quota/auth error surfacing (paywall / message-limit / rate-limit → 429, invalid session → 401 re-login). Full multimodal parity via the persona blob-upload layer: **vision** (image input, 15 vision-capable models), **document RAG** (PDF/doc upload, server-side extraction), **image generation** (22 models), **video generation** (14 models, async signed-url → poll), and **TTS** (streaming MP3). Registered as provider `uc` (alias `ucn`). The metered OpenAI-compatible Developer API is a separate `uc-direct` provider. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(logs):** add copy-all button to request detail modal ([#11083](https://github.com/diegosouzapw/OmniRoute/pull/11083)) — thanks @AndrianBalanescu +- **feat(check):** make serviceKinds required and add the reverse-walk provider consistency gate ([#11392](https://github.com/diegosouzapw/OmniRoute/pull/11392)) — thanks @Tushar49 +- **feat(providers):** only advertise verified-working connections by default ([#11448](https://github.com/diegosouzapw/OmniRoute/pull/11448)) — thanks @alltomatos +- **feat(resilience):** add hierarchical concurrency admission ([#11493](https://github.com/diegosouzapw/OmniRoute/pull/11493)) +- **feat(providers):** derive + surface expiry for JWT-bearing web cookies (#11497) ([#11505](https://github.com/diegosouzapw/OmniRoute/pull/11505)) — thanks @oyi77 +- **feat(tunnels):** support named cloudflared tunnels via CLOUDFLARED_CONFIG ([#11561](https://github.com/diegosouzapw/OmniRoute/pull/11561)) — thanks @ggiak +- **feat(compression):** make proactive context-compression threshold a live setting ([#11564](https://github.com/diegosouzapw/OmniRoute/pull/11564)) — thanks @ragnar-claude +- **feat(resilience):** add provider peak-hour protection ([#11622](https://github.com/diegosouzapw/OmniRoute/pull/11622)) — thanks @Neuron-Mr-White +- **feat(providers):** add 1min.ai provider ([#11631](https://github.com/diegosouzapw/OmniRoute/pull/11631)) — thanks @zero-executioner +- **feat(dashboard):** collapsible JSON tree viewer for request/response payloads ([#11703](https://github.com/diegosouzapw/OmniRoute/pull/11703)) — thanks @hartmark +- **feat(routing):** add LiquidAI LFM2.5-2.6B free tier via OpenRouter ([#11752](https://github.com/diegosouzapw/OmniRoute/pull/11752)) — thanks @brick30llc-ctrl +- **feat(pricing):** add GLM-5.3-Flash pricing, model specs, and catalog registration ([#11830](https://github.com/diegosouzapw/OmniRoute/pull/11830)) — thanks @AndrianBalanescu +- **feat(routing):** add relayMode for schema-locked context handoffs ([#11839](https://github.com/diegosouzapw/OmniRoute/pull/11839)) — thanks @raheemuddin786 +- **feat(config):** add support for runtime system prompt configuration and hot-reloading ([#11841](https://github.com/diegosouzapw/OmniRoute/pull/11841)) — thanks @raheemuddin786 +- **feat(routing):** expand connection-aware quota prefilter across combo strategies (#11682) ([#11850](https://github.com/diegosouzapw/OmniRoute/pull/11850)) — thanks @HouMinXi +- **feat(models):** reactive discovery sync after Antigravity model-not-found 404 ([#11888](https://github.com/diegosouzapw/OmniRoute/pull/11888)) — thanks @MumuTW +- **feat(models):** auto-aliases and token limits from synced Antigravity discovery ([#11889](https://github.com/diegosouzapw/OmniRoute/pull/11889)) — thanks @MumuTW +- **feat(settings):** raise sticky round-robin limit caps to 1000 ([#12015](https://github.com/diegosouzapw/OmniRoute/pull/12015)) — thanks @adivekar-utexas +- **feat(resilience):** operator-configurable global credential health check interval ([#12043](https://github.com/diegosouzapw/OmniRoute/pull/12043)) — thanks @adivekar-utexas +- **feat(quota):** use official OpenCode Go usage API ([#12124](https://github.com/diegosouzapw/OmniRoute/pull/12124)) — thanks @ddarkr +- **feat(resilience):** default the credential health check sweep to 60 minutes ([#12138](https://github.com/diegosouzapw/OmniRoute/pull/12138)) — thanks @adivekar-utexas +- **feat(quality):** new-code mode for the complexity and dead-code ratchets (Clean as You Code) ([#12142](https://github.com/diegosouzapw/OmniRoute/pull/12142)) +- **feat(dashboard):** orchestration canvas — unified model + snapshot hook (part 1/2) ([#12156](https://github.com/diegosouzapw/OmniRoute/pull/12156)) +- **feat(usage):** add Kilo Code balance and Kilo Pass quotas ([#12178](https://github.com/diegosouzapw/OmniRoute/pull/12178)) — thanks @killer30001000 +- **feat(providers):** refresh vendored ChatGPT Web connector to v4.0.7 ([#12181](https://github.com/diegosouzapw/OmniRoute/pull/12181)) — thanks @backryun +- **feat(providers):** manual "Clear cooldown" action in the cooling panel ([#12224](https://github.com/diegosouzapw/OmniRoute/pull/12224)) — thanks @patrykkopycinski +- **feat(providers):** restore ChatGPT Web via clean-room browser transport ([#12239](https://github.com/diegosouzapw/OmniRoute/pull/12239)) — thanks @backryun +- **feat(sse):** wire the PROVIDER_PROFILES window gate into the global provider cooldown ([#12247](https://github.com/diegosouzapw/OmniRoute/pull/12247)) +- **feat(providers):** modernize CLOVA Studio chat and embeddings ([#12277](https://github.com/diegosouzapw/OmniRoute/pull/12277)) — thanks @backryun +- **feat(browser):** adopt Obscura as primary headless browser engine with Chromium fallback ([#12286](https://github.com/diegosouzapw/OmniRoute/pull/12286)) — thanks @opensource-elearning +- **feat(sse):** full language parity for output styles (es/de/fr/it/ru/zh + autoDetect) ([#12289](https://github.com/diegosouzapw/OmniRoute/pull/12289)) +- **feat(quality):** complete test:scoped — --full map rebuild, stdin selection, CI loader parity (#8084 D1) ([#12353](https://github.com/diegosouzapw/OmniRoute/pull/12353)) +- **feat(providers):** refresh Fable, Cursor, and Devin catalogs ([#12367](https://github.com/diegosouzapw/OmniRoute/pull/12367)) — thanks @backryun +- **feat(video):** redact transcript text from logs and durable memory (#12150 P1) ([#12427](https://github.com/diegosouzapw/OmniRoute/pull/12427)) +- **feat(dashboard):** parent-link, genuine-continuation badge, and modal perf fixes ([#12448](https://github.com/diegosouzapw/OmniRoute/pull/12448)) — thanks @hartmark +- **feat(dashboard):** adaptive context-budget dial on compression panel ([#12488](https://github.com/diegosouzapw/OmniRoute/pull/12488)) — thanks @HouMinXi +- **feat(providers):** refresh Z.ai Web models and browser transport ([#12524](https://github.com/diegosouzapw/OmniRoute/pull/12524)) — thanks @backryun +- **feat(video):** redact raw client-snapshot transcript fields in the detailed log (#12150 P2) ([#12528](https://github.com/diegosouzapw/OmniRoute/pull/12528)) +- **feat(providers):** refresh NVIDIA hosted models ([#12538](https://github.com/diegosouzapw/OmniRoute/pull/12538)) — thanks @backryun +- **feat(quota):** Moonshot Open Platform balance and TPD lock for custom nodes ([#12590](https://github.com/diegosouzapw/OmniRoute/pull/12590)) — thanks @HouMinXi +- **feat(video):** redact transcript in the in-memory pending-request snapshot (#12430 item 6) ([#12596](https://github.com/diegosouzapw/OmniRoute/pull/12596)) +- **feat(dashboard):** link a log entry's Conversation Context to its owning conversation ([#12646](https://github.com/diegosouzapw/OmniRoute/pull/12646)) — thanks @hartmark +- **feat(catalog):** eligibility-gated free-tier bucket ([#12669](https://github.com/diegosouzapw/OmniRoute/pull/12669)) +- **feat(db):** fail-closed previous_response_id continuation for redacted video turns (#12150 P2b) ([#12707](https://github.com/diegosouzapw/OmniRoute/pull/12707)) +- **feat(gamification): pay the documented `streak_bonus` and `badge_unlock` XP rewards.** `XP_REWARDS` listed both rewards but the award pipeline never paid them: the private reward table in `events.ts` omitted them, `updateStreak()` did not report when a streak extended, and badge unlocks carried no XP. Every request that extends a daily streak now pays `streak_bonus × streak length` once per UTC day (guarded by a same-day `xp_audit_log` check), and every badge unlocked through the pipeline pays `badge_unlock` once per badge (guarded by the `user_badges` primary key; `unlockBadge()` now reports whether it inserted). Bonus XP flows through the same `addXp` + level sync + global/weekly/monthly leaderboard path as action XP, so level-ups and rankings include it. The Radar supporter recognition unlock stays XP-free. (#12522 — thanks @pacocartones) +- **feat(providers):** advertise a `free-tier` capability in the provider plugin manifest for every provider with documented free models, so sidecars and dashboards can filter free-capable providers without reading the quota catalog ([#12786](https://github.com/diegosouzapw/OmniRoute/pull/12786)) — thanks @maxmad64bis +- **feat(providers):** Added EURouter as an OpenAI-compatible API-key gateway (`https://api.eurouter.ai/v1`), with live model discovery via `passthroughModels`. Its copy states that models are served by third-party upstreams listed per model, so an EU-based router is not read as EU data residency for inference. ([#13025](https://github.com/diegosouzapw/OmniRoute/pull/13025)) — thanks @ntdatt812 +- **feat(providers):** Added GreenPT as an OpenAI-compatible API-key provider (`https://api.greenpt.ai/v1`), with live model discovery via `passthroughModels`. No free-inference badge: the published docs describe a free API subscription billed per token, not a free tier. ([#13024](https://github.com/diegosouzapw/OmniRoute/pull/13024)) — thanks @ntdatt812 +- **feat(i18n):** 9 new locales — Greek (`el`), Croatian (`hr`), Serbian (`sr`), Lithuanian (`lt`), Estonian (`et`), Latvian (`lv`), Slovenian (`sl`), Maltese (`mt`), Irish (`ga`) — across the dashboard, docs mirrors, CLI, README and the site; OmniRoute now ships every official EU language (51 locales). (#13044) +- **feat(i18n):** 8 new locales — Kannada (`kn`), Malayalam (`ml`), Odia (`or`), Punjabi (`pa`), Nepali (`ne`), Sinhala (`si`), Burmese (`my`), Khmer (`km`) — across the dashboard, docs mirrors, CLI, README and the site (59 locales). The translator now restores the ICU literal escape around angle placeholders and splits oversized docs sections before translating. (#0000) ([#13660](https://github.com/diegosouzapw/OmniRoute/pull/13660)) +- feat(providers): **list Agnes 3.0 Flash as the current free chat model, drop retired 1.5 Flash, add Image 2.0/2.5 Flash plus Video 2.5/2.5 Flash, and discover the live `/v1/models` catalog (including the CN host `api.agnes-ai.cn`).** `agnes-1.5-flash` now forwards to `agnes-3.0-flash`. Video 2.5 polls `GET /v1/videos/{id}` (not the V2.0 `/agnesapi` contract). Live `/v1/models` (2026-09-09) no longer serves 1.5; the wiki marks it deprecated. 3.0 Flash is 512K context / 65,536 max output, same window as 2.5. CN-region keys use the existing per-connection base-URL field, default stays `apihub.agnes-ai.com`. ([#13120](https://github.com/diegosouzapw/OmniRoute/pull/13120)) — thanks @HouMinXi +- **feat(sse):** Codex and OpenAI catalogs list GPT-6 Astra with effort aliases (`-low` through `-ultra`); Codex CLI identity pins `@openai/codex@0.153.4` in lockstep with the image ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **feat(dashboard):** the orchestration History tab gained a "Compare runs" mode — toggling it turns each grid cell into a 2-item selection queue (a 3rd click drops the oldest pick), and picking two cells opens a side-by-side comparison panel instead of the usual detail drawer. The panel fetches both runs' detail the same way the drawer does (falling back to persisted history once a run leaves the live TTL window) and shows, per side: identity/source/state, start time, a signed `right - left` delta for duration/cost/event count, the event timeline aligned by index, and any memory hits. One side's fetch failing never blocks the other, and a delta is only ever computed when both sides have a finite value — otherwise it renders "—", never `NaN`. Comparing two runs from different sources or skills still works; a banner marks the deltas as informational rather than hiding them, since the two runs aren't a strict apples-to-apples pair. ([#12677](https://github.com/diegosouzapw/OmniRoute/pull/12677)) +- **feat(dashboard):** badge a conversation that never reached a clean stop ([#12717](https://github.com/diegosouzapw/OmniRoute/pull/12717)) — thanks @hartmark +- **feat(release):** reconcile-changelog tool + version-anchored fragment aggregation ([#12987](https://github.com/diegosouzapw/OmniRoute/pull/12987)) +- **feat(mcp):** carry context_length through MCP list_models_catalog projection (#12776) ([#13406](https://github.com/diegosouzapw/OmniRoute/pull/13406)) — thanks @KooshaPari +- **feat(cli):** make startup readiness budget configurable (#13369) ([#13433](https://github.com/diegosouzapw/OmniRoute/pull/13433)) — thanks @KooshaPari + ### 🐛 Bug Fixes +- **security(streaming):** sanitize generic mid-stream error messages before emitting OpenAI, + Responses, or Claude SSE failure frames and before diagnostic logging, while preserving raw + failures for internal classification and keeping client disconnects out of provider failure state. ([#12457](https://github.com/diegosouzapw/OmniRoute/pull/12457)) +- **fix(claude):** `blockExtraUsage: false` no longer lets 5h quota preflight skip the account; extra usage is billed after the session bar is gone, so the request must reach Anthropic ([#12803](https://github.com/diegosouzapw/OmniRoute/pull/12803)) — thanks @HouMinXi +- Sanitize HuggingChat conversation-creation and message-send transport failures before they reach client error bodies or provider logs. ([#12467](https://github.com/diegosouzapw/OmniRoute/pull/12467)) +- **fix(dashboard):** Keep local and theme-aware provider SVG icons at a definite layout size so Chromium does not collapse them to 0×0 after the v3.8.50 image-rendering change ([#12054](https://github.com/diegosouzapw/OmniRoute/pull/12054)) — thanks @ponkcore +- **fix(security):** Sanitize provider and runtime failures before public API, SSE and MCP responses and before persistent request, proxy and usage logs, preventing credentials, stack traces and host filesystem paths from crossing those boundaries while preserving stable error codes and useful diagnostics. ([#12506](https://github.com/diegosouzapw/OmniRoute/pull/12506)) +- **fix(cli):** `omniroute tunnel create` no longer crashes with `Cannot read properties of undefined (reading optsWithGlobals)` — removed the duplicate positional argument that caused Commander.js to misalign the action callback parameters ([#12295](https://github.com/diegosouzapw/OmniRoute/issues/12295)) ([#12368](https://github.com/diegosouzapw/OmniRoute/pull/12368)) — thanks @gonisulaimann +- Stop advertising Gemini Live-only models as supported audio endpoints until OmniRoute proxies the bidirectional Live protocol. ([#11710](https://github.com/diegosouzapw/OmniRoute/pull/11710)) — thanks @RaviTharuma +- **fix(kie):** reroute `flux/kontext` off the KIE Market `createTask` flow — it is catalogued with `isMarket: true` but has no Market catalog page, so KIE rejected it with "model name not supported"; it now hits the dedicated `POST /api/v1/flux/kontext/generate` / `GET /api/v1/flux/kontext/record-info` endpoints instead (#11296). ([#11985](https://github.com/diegosouzapw/OmniRoute/pull/11985)) — thanks @hartmark +- **fix(kie):** correct 12 more KIE Market catalog ids that were sent to `createTask` unchanged but diverge from KIE's documented upstream `model` values — GPT Image 2 T2I/I2I (drops the `gpt/` prefix), GPT Image 1.5 T2I/I2I (`gpt-image/` namespace), Seedream 5.0 Lite T2I/I2I (drops the `.0`), all 4 Flux 2 variants (`flux-2/` namespace, generic variant renamed `flex`), and Wan 2.7 Image / Image Pro (dash instead of dot) — each verified individually against the literal example request published on docs.kie.ai. `#11326`'s "everything else already matches" claim was wrong a second time (#11296); `z-image/4.0-*`/`z-image/4.5-*` and `flux/kontext` remain open, documented as unresolved in `KIE_MARKET_UPSTREAM_MODEL_IDS`'s comment pending further verification. ([#11667](https://github.com/diegosouzapw/OmniRoute/pull/11667)) +- **fix(dashboard):** `useApiKeySave.handleSaveApiKey` no longer forces a full upstream `/models` catalog sync on every non-curated provider connection save — callers can now pass `skipModelSync: true` to opt out, so a workflow that only wants to add one manual model no longer floods the provider's available-models list with hundreds/thousands of synced entries. The flag is a client-side intent signal only and is stripped before the connection payload is POSTed to `/api/providers`; default behavior (full sync on save) is unchanged when the flag is omitted (#11324) ([#11664](https://github.com/diegosouzapw/OmniRoute/pull/11664)) +- **Provider connections:** keep `tokenExpiresAt` when a connection is created. The create-path allowlist omitted it, so every insert stored NULL and the dashboard token badge could read a fresh connection as expired until its first background refresh ([#11368](https://github.com/diegosouzapw/OmniRoute/pull/11368)). — thanks @ntdatt812 +- **fix(combos):** the combo builder's precision-select, global-model-search, and manual-entry flows now serialize a model step's `model` string using the provider's already-computed routing-alias prefix (e.g. `oc/`) instead of rebuilding it from the raw canonical `providerId`, fixing the no-auth "OpenCode Free" provider (`opencode`) being routed to the unrelated paid "OpenCode Zen" provider (`opencode-zen`) because `opencode` doubles as a manual routing-prefix override ([#11433](https://github.com/diegosouzapw/OmniRoute/issues/11433)). ([#11670](https://github.com/diegosouzapw/OmniRoute/pull/11670)) +- **fix(radar):** the catalog feed cache now keeps `generatedAt`, the date the feed's data was built, next to `fetchedAt`, the date this install downloaded it (#11435). The feed schema requires that date and the sync path validates it, but the cache dropped it — so a feed fetched minutes ago and one carrying weeks-old figures looked identical to everything downstream, including the dashboard's "Last fetched" line. `getRadarCatalog().meta` and `GET /api/radar/status` now report both dates, the latter as its own field rather than folded into `version` — and omitted entirely for the offers and intel caches, which keep no build date, where a `null` would read as "unknown" rather than "never stored". The dashboard still shows only the fetch time; surfacing the build date there needs a new translated label and is left to a follow-up. Rows cached before migration 163 read back as `null`: unknown stays unknown instead of borrowing the fetch time. The referrals cache has persisted the same date since migration 142. — thanks @maxmad64bis +- **fix(cli):** postinstall no longer fills `JWT_SECRET` and `API_KEY_SECRET` in the installed package's `.env` (#11436). `.env.example` ships both blank on purpose: the server restores them from its durable store, or generates and persists them there, in `ensureSecrets()`. Pre-filling them defeated that — the file lives inside the package directory, so `npm i -g` replaced it and postinstall wrote _different_ values, while `ensureSecrets()` (which only acts on an empty variable) never got to restore the real ones. Both secrets rotated silently on every update, invalidating dashboard sessions and API-key CRCs. `STORAGE_ENCRYPTION_KEY` left the same list for the same reason in #1622; its comment pointed at a function that no longer exists and now names the real provisioning path. — thanks @maxmad64bis +- **fix(cli):** the CLI now says when a loaded `.env` lives inside the installed package directory (#11437). It already announces every env file it reads, without distinguishing the ones that survive an update from the one that does not: `npm i -g` replaces the package directory wholesale, so values set there are gone at the next update, silently. The warning names the durable path to move them to, and fires only when that file actually supplied a value — a file entirely shadowed by a durable one supplied nothing. A development checkout stays silent: there the same path is stable and documented in `SETUP_GUIDE.md`. — thanks @maxmad64bis +- **fix(free-models):** the shared `isFreeModel()` predicate no longer reports catalog entries marked `freeType: "discontinued"` as free, so `hidePaidModels` can't route to Pollinations' seven premium models that now require a paid key ([#11441](https://github.com/diegosouzapw/OmniRoute/pull/11441)) — thanks @maxmad64bis +- fix(ui): let AnySearch use the normal provider-icon fallback when LobeHub has no matching icon (#11449) +- Fixed the v3.8.50 Costs and Analytics dashboards so flat-rate Claude Code usage can be shown as an explicitly requested token-price estimate without changing default billed-cost semantics. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed archived usage retention so each request is priced individually instead of pricing a day's summed tokens once, which understated archived cost whenever a day mixed cache-heavy and ordinary requests. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed the Costs dashboard so it discloses when displayed figures include flat-rate token-price estimates instead of labelling them as billed spend, using the flag the analytics API already returns; the month-end projection and the CSV/JSON exports carry the same marker, and billed-cost mode is unchanged. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- **fix(combo):** attach the same combo-diagnostics trace (`poolSize`/`attemptOrder`/`excluded`/`terminalReason`, plus `x-omniroute-combo-*` headers) to the round-robin strategy's and the nested pipeline/fusion runtime-unit loop's "Maximum combo retry limit reached" 503 that the priority-strategy path already attaches for the identical terminal condition — previously those two paths returned a bare, contextless 503 ([#11462](https://github.com/diegosouzapw/OmniRoute/issues/11462)). ([#11665](https://github.com/diegosouzapw/OmniRoute/pull/11665)) +- **fix(bun):** use native `bun:sqlite` in `bootstrap-env` and `sync-env` to avoid loading `better-sqlite3` N-API addon during Bun startup ([#11468](https://github.com/diegosouzapw/OmniRoute/pull/11468)) — thanks @TheDemonTuan +- **fix(docker-bun):** make `Dockerfile.bun` install reliable with pre-install script helpers and native `bun:sqlite` smoke check ([#11470](https://github.com/diegosouzapw/OmniRoute/pull/11470)) — thanks @TheDemonTuan +- **fix(docker-bun):** remove every vendored `better-sqlite3` native addon from the Bun runtime image so startup cannot abort before the `bun:sqlite` fallback ([#11482](https://github.com/diegosouzapw/OmniRoute/pull/11482)) — thanks @TheDemonTuan +- **fix(autoCombo):** effort/alias model variants (`gpt-5.6-sol-xhigh`, `gpt-5.6`, cursor's `claude-4.6-opus-high`) inherit their base model's task fitness instead of falling to the wildcard 0.5 ([#11492](https://github.com/diegosouzapw/OmniRoute/pull/11492)) — thanks @MumuTW +- fix(db): rate-limit repeated Arena ELO leaderboard fetch-failure warnings instead of logging one per sync attempt (#11500) ([#11989](https://github.com/diegosouzapw/OmniRoute/pull/11989)) — thanks @hartmark +- **fix(db):** dedupe the raw `[Encryption] Decryption failed...` log line emitted by the lazy-decrypt views (`createLazyRowProxy`/`createLazyConnectionView`), which power `getProviderConnections()` and were re-triggering that line on every CredentialHealth/model-sync cycle for the same corrupt or stale-key credential — a fresh Proxy over a fresh row on every cycle meant the per-proxy memoization never suppressed it, unlike the dedup `decryptConnectionFields()` already had since [#9927](https://github.com/diegosouzapw/OmniRoute/issues/9927). Now shares that dedupe tracking so the line logs at most once per credential ([#11500](https://github.com/diegosouzapw/OmniRoute/issues/11500)). ([#11678](https://github.com/diegosouzapw/OmniRoute/pull/11678)) +- **fix(intelligence):** Arena ELO sync stores a synthesized base-model row for every effort/harness variant (`claude-opus-5-max` → `claude-opus-5`, `gpt-5.6-sol-xhigh (codex-harness)` → `gpt-5.6-sol`), so bare model requests reach the synced intelligence layer instead of the static fallback table; the hand-maintained `MODEL_ALIAS_MAP` — which collapsed generations (`gpt-5.5` → `gpt-5`) — is gone ([#11506](https://github.com/diegosouzapw/OmniRoute/pull/11506)) — thanks @MumuTW +- **fix(autoCombo,sse):** catalog hygiene — the static task-fitness table no longer ranks retired models above live ones (dead rows dropped, patterns matched on segment boundaries), the 7 deprecation aliases that forwarded to retired or non-existent models now point at the vendors' published replacements, an alias is no longer rewritten when the serving provider offers the id as-is, and a new `check:model-lifecycle` gate diffs both tables against a vendor lifecycle snapshot ([#11507](https://github.com/diegosouzapw/OmniRoute/pull/11507)) — thanks @MumuTW +- **fix(sse):** stop dropping resolved `thoughtSignature` values on parallel (multi tool-call) turns sent to Gemini 3.x — the claude→gemini and openai→gemini translators previously kept the signature only on the *first* function call of a message, causing Gemini to reject subsequent calls in the same turn with HTTP 400 "Function call is missing a thought_signature"; each function call now keeps its own resolved signature ([#11510](https://github.com/diegosouzapw/OmniRoute/issues/11510)). ([#11669](https://github.com/diegosouzapw/OmniRoute/pull/11669)) +- Treat Docker publishing from a non-default release branch as an expected skip instead of a failed workflow, while preserving the guard that prevents it from overwriting the `next` channel. ([#11527](https://github.com/diegosouzapw/OmniRoute/pull/11527)) — thanks @pacocartones +- fix(search): prefer credentialed providers over duckduckgo-free fallback (#11524) — the fallback-only loop ran before the credentialed-providers loop in `executeWebSearch`, making configured providers unreachable when `duckduckgo-free` was available. ([#11565](https://github.com/diegosouzapw/OmniRoute/pull/11565)) — thanks @vsd2807 +- **fix(sse):** cap the upstream headers-wait phase for STREAMING requests to a client-realistic ceiling (110s, under Codex's own ~120s hard client-abort window) instead of the flat 10-minute `FETCH_TIMEOUT_MS` default — that default was 5x longer than the body-phase readiness watchdog's own adaptive bound, so a request whose upstream never returned any response at all (not even headers, e.g. a stalled NVIDIA target behind a tool-heavy Responses→Chat translation) kept the client connection alive on keepalives only, guaranteeing the client's own patience ran out first with an opaque 499 instead of OmniRoute detecting and failing the stall fast. Non-streaming requests are unaffected — they keep the existing flat default (`open-sse/utils/fetchStartTimeoutPolicy.ts`) (#11526) ([#11668](https://github.com/diegosouzapw/OmniRoute/pull/11668)) +- **fix(ci):** protocol-clients E2E harness boots the peer-stamped custom server (`run-next.mjs dev`) instead of the bare `next` CLI, so `LOCAL_ONLY` locality resolves from the real TCP peer stamp and `/api/mcp/audit` answers 200 (open bootstrap preserved via an `OMNIROUTE_E2E_BOOTSTRAP_MODE=open` credential clear in `run-next.mjs`, pinned to loopback `HOST`) instead of a deterministic 403 (#11535) ([#11549](https://github.com/diegosouzapw/OmniRoute/pull/11549)) — thanks @jonlwheat2-gif +- **fix(docs):** the Auto-Combo engine is described with the number of factors it actually declares. Four different counts were in circulation — 6, 9, 13 and 14 across `AGENTS.md`, four guides and six code comments — while `DEFAULT_WEIGHTS` declares 15. `check:docs-counts` now reads that number from the source, so adding a factor without updating the prose fails CI ([#11545](https://github.com/diegosouzapw/OmniRoute/pull/11545)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **fix(quota-share):** in-flight leases are per request, so an aborted request's slot ages out instead of being kept alive by later traffic on the same connection ([#11547](https://github.com/diegosouzapw/OmniRoute/pull/11547)) — thanks @abhisheksharma2411 +- The free-tier summary route now serves the refreshed Radar catalog when the feed is active — the same numbers the dashboard shows — and states which catalog answered plus its real build date, instead of always reporting release-frozen figures with a stale curation date. The supporter-key live feed stays reserved to authenticated callers of the instance. ([#11550](https://github.com/diegosouzapw/OmniRoute/pull/11550)) — thanks @maxmad64bis +- **sse:** fix LiveWS/embed-WS servers crashing at startup under the Node/tsx runtime — `liveServer.ts`, `embedWsProxy.ts` and `apiBridgeServer.ts` imported `@/shared/utils/httpClientAbortGuard` without the `.mjs` extension, so the client-abort crash guard added by [#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556) was unreachable and every dependent test failed with `ERR_MODULE_NOT_FOUND` ([#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556)). ([#11671](https://github.com/diegosouzapw/OmniRoute/pull/11671)) — thanks @jonlwheat2-gif / @yxyxy +- fix(chat): shed any of the matched provider-node's routing identifiers (public prefix or internal id) before model lookup, so composites like `//` no longer reach upstream verbatim (#11557) — thanks @maxmad64bis +- **fix(cache):** a semantic-cache entry now expires on its TTL instead of surviving until the next UTC midnight, and `dbEntries` stops counting expired rows ([#11573](https://github.com/diegosouzapw/OmniRoute/pull/11573)) — thanks @ntdatt812 +- **fix(api):** `/v1/models` schedules its stale-while-revalidate rebuild through Next's `after()` again, so a stale catalog reaches the client before the rebuild blocks the event loop ([#11574](https://github.com/diegosouzapw/OmniRoute/pull/11574)) — thanks @ntdatt812 +- **fix(check):** the `known-symbols` executor conformance gate awaits the now-async `getExecutor()`, so it stops reporting all 142 registered aliases as dead symbols and can detect a lazy import that fails ([#11582](https://github.com/diegosouzapw/OmniRoute/pull/11582)) — thanks @ntdatt812 +- **fix(pack):** register `bin/cli/utils/volatileEnvPath.mjs` in `PACK_ARTIFACT_REQUIRED_PATHS`, so the prune can no longer silently drop a module the published CLI imports on every boot ([#11588](https://github.com/diegosouzapw/OmniRoute/pull/11588)) — thanks @ntdatt812 +- **i18n(vi):** translate the five reliability-column strings on the free-provider rankings page, which rendered as `__MISSING__:` markers in the Vietnamese UI ([#11589](https://github.com/diegosouzapw/OmniRoute/pull/11589)) — thanks @ntdatt812 +- **resilience:** restore the expired-connection retry-budget probe in the token-health sweep — the `!isGitHubAccessTokenOnlyConnection` carve-out reintroduced by #11608 contradicted the boundary pinned by #11592, so a GitHub connection parked at `expired` with retry budget remaining was never probed and could never self-heal ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)). ([#11672](https://github.com/diegosouzapw/OmniRoute/pull/11672)) — thanks @jonlwheat2-gif / @ntdatt812 +- **fix(dashboard):** Provider Stats now displays combo success rates on their native 0–100 percentage scale instead of multiplying them twice ([#11595](https://github.com/diegosouzapw/OmniRoute/pull/11595)) — thanks @pacocartones +- **fix(providers):** route DuckDuckGo Web Chat through the verified `duck.ai` endpoint, refresh its free model catalog, and avoid forwarding unsolved anti-abuse challenges ([#11597](https://github.com/diegosouzapw/OmniRoute/pull/11597)) — thanks @solstxce +- **fix(ui):** Console log Refresh and Copy controls now expose localized accessible names, keep copy actions visible on keyboard focus, and announce copy completion safely ([#11599](https://github.com/diegosouzapw/OmniRoute/pull/11599)) — thanks @pacocartones +- Dropped the stale-`.eslintcache` `restore-keys` fallback from both "Restore ESLint file cache" steps in `ci.yml`, so the blocking `Lint` job can no longer be served per-file verdicts computed under a different lint config, suppressions file or lockfile. `quality.yml` had already dropped it in #11963; `ci.yml` — the workflow that actually gates PRs — had not (#11600). ([#11996](https://github.com/diegosouzapw/OmniRoute/pull/11996)) +- fix(ci): pass `--pass-on-unpruned-suppressions` in `run-eslint-json.mjs` so the CI Lint job no longer fails when a suppression is merely orphaned by a genuine fix, mirroring the identical fix already in `validate-release-green.mjs` (#11600) ([#11983](https://github.com/diegosouzapw/OmniRoute/pull/11983)) — thanks @hartmark +- fix(handoff): enforce provider allowlist for universal handoff (#11602) — universal handoff now skips summarization when the selected summary model's provider is not included in the configured provider allowlist. ([#11619](https://github.com/diegosouzapw/OmniRoute/pull/11619)) — thanks @vsd2807 +- **fix(dashboard):** Show a stable error state when Search Analytics returns an HTTP error or malformed data ([#11603](https://github.com/diegosouzapw/OmniRoute/pull/11603)) — thanks @pacocartones +- **test(gamification):** pin the aggregate profile level to the XP-derived semantics of #11604 — `getAggregateXp()` now derives `currentLevel` from the summed XP (`calculateLevel(sum)`), not `MAX(stored current_level)`, and the #3484 fixture levels are aligned with the XP curve ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)). ([#11676](https://github.com/diegosouzapw/OmniRoute/pull/11676)) — thanks @jonlwheat2-gif / @pacocartones +- **fix(dashboard):** Keep the Profile level and progress aligned with aggregate XP, including bounded handling for invalid totals ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)) — thanks @pacocartones +- **fix(dashboard):** Prevent locked hidden badges from revealing their icon or opening private badge details before they are earned ([#11605](https://github.com/diegosouzapw/OmniRoute/pull/11605)) — thanks @pacocartones +- **fix(dashboard):** Restore keyboard focus after shared modals close and cancel delayed autofocus during cleanup ([#11607](https://github.com/diegosouzapw/OmniRoute/pull/11607)) — thanks @pacocartones +- **fix(dashboard):** Enable Enter and Space activation for clickable data-table rows without hijacking nested controls ([#11610](https://github.com/diegosouzapw/OmniRoute/pull/11610)) — thanks @pacocartones +- **fix(autoCombo,sse):** vendor-retired catalog ids are dropped from the auto-combo candidate pool and no longer win on leftover `arena_elo` / `user_override` rows; `getModelLifecycleDecision` consults `model-lifecycle.json` (prefix-stripped) so aggregator traffic is not `untracked` for ids the snapshot already knows ([#11625](https://github.com/diegosouzapw/OmniRoute/issues/11625)) ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)) — thanks @MumuTW +- **test(chatcore):** move Codex/Claude combo fixtures off models the lifecycle guard now rejects — `gpt-5.1-codex`/`gpt-5-codex` are vendor-retired (snapshot, #11626) and `claude-3-5-sonnet-20241022` is shut down, so native-passthrough and combo-fallback tests switched to `gpt-5.6-sol` and `claude-sonnet-4.6` ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)). ([#11675](https://github.com/diegosouzapw/OmniRoute/pull/11675)) — thanks @jonlwheat2-gif / @MumuTW +- fix(opencode-plugin): stop a free-tier budget that rounds up across a magnitude from rendering as `1000K`/`1000M` in the model picker — `fmtTokens` chose its unit from the raw token count and then rounded with `toFixed(1)`, so 999,950–999,999 printed as `1000K` rather than `1M` and just under a billion printed as `1000M` rather than `1B` ([#11684](https://github.com/diegosouzapw/OmniRoute/pull/11684)) — thanks @abhisheksharma2411 +- Cap prose-derived Antigravity quota resets at the configured model cooldown maximum while preserving authoritative Retry-After headers and Google RetryInfo hints. (#11695) Thanks @pacocartones. ([#11823](https://github.com/diegosouzapw/OmniRoute/pull/11823)) +- Compare `_rebaseline_*` notes structurally in the ratchet-bank verifier so an object-valued note no longer reports itself as rewritten on an unmodified tree — the nightly `bank-ratchet-shrinks` lane had been aborting on every run since 2026-08-11, leaving 41,672 lines of earned file-size shrinks unbanked (#11702). ([#11892](https://github.com/diegosouzapw/OmniRoute/pull/11892)) — thanks @MumuTW +- **fix(build):** `prepublish.ts` bundles the ChatGPT Web (Codex) MCP bridge through `runBuildTool()` instead of spawning `npx.cmd` raw, fixing the build crash on Node ≥ 20/Windows where `.cmd` shims cannot be spawned without a shell (EINVAL) ([#11704](https://github.com/diegosouzapw/OmniRoute/issues/11704)) ([#11706](https://github.com/diegosouzapw/OmniRoute/pull/11706)) — thanks @jonlwheat2-gif +- fix(codex): keep `parallel_tool_calls:false` on the translated Codex Responses Lite path (#11707) ([#11984](https://github.com/diegosouzapw/OmniRoute/pull/11984)) — thanks @hartmark +- **fix(tests):** the new Gemini Web Images retirement test (#11708) called the now-async `getExecutor()` synchronously, throwing `TypeError: getExecutor(...).getProvider is not a function` instead of asserting Gemini Web chat stays available. Awaited to match the R0.3 executor-registry refactor (#11220). ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(search):** Allow REST and MCP web-fetch requests to select the advertised `anysearch-search` provider explicitly ([#11716](https://github.com/diegosouzapw/OmniRoute/pull/11716)) — thanks @pacocartones +- **fix(providers):** Keep overnight peak-hour protection active after midnight according to the window's configured start day ([#11718](https://github.com/diegosouzapw/OmniRoute/pull/11718)) — thanks @pacocartones +- **fix(providers):** correct all five g4f.space gateway descriptions to document proof-of-work credits or member API keys, variable limits, and the remote prompt boundary instead of promising free keyless access at five requests per minute ([#11738](https://github.com/diegosouzapw/OmniRoute/pull/11738)). +- **fix(db):** `model_capabilities` is created by a migration instead of lazily on the first models.dev sync, so a clean install and an upgraded install converge on the same schema regardless of which features have run ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(ci):** `check:install-upgrade` now fails on an `npm` install truncated by ENOSPC (npm reports it as a warning and still exits 0), authenticates its health probe so the version assertion works against the hardened health payload, frees the clean-install tree before the upgrade phase, and no longer reports a schema divergence computed from a boot that never served ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(routing):** harden session-affinity key derivation against oversized explicit IDs and empty structural-payload collisions, while recognizing Gemini and common text request shapes ([#11746](https://github.com/diegosouzapw/OmniRoute/pull/11746)) — thanks @MumuTW +- **fix(packages/browser-pool):** regenerate `package-lock.json` so the `packages/browser-pool` workspace's locked `playwright`/`@types/node` (and transitives) match its `package.json` specs, fixing cache-only/offline installs (`npm ci --offline`, Nix `buildNpmPackage`) that previously failed with `ENOTCACHED` ([#11747](https://github.com/diegosouzapw/OmniRoute/issues/11747)) — thanks @benjaminkitt ([#11784](https://github.com/diegosouzapw/OmniRoute/pull/11784)) — thanks @geekyNads +- **fix(assets):** Stop distributing 79 provider and CLI logos without verified source/license provenance, keep every provider/tool available through the internal generic icon, and prevent those IDs from falling through to an external icon CDN. ([#11750](https://github.com/diegosouzapw/OmniRoute/pull/11750)) +- **fix(providers):** retire the common `chatgpt-web` / `cgpt-web` provider because its pre-key and proof-of-work provenance could not be cleared ([#11754](https://github.com/diegosouzapw/OmniRoute/pull/11754)) — explicit chat and image requests, slash-prefixed models, persisted aliases, proxy overrides, and combo targets now fail closed with a sanitized HTTP `410` / `PROVIDER_RETIRED` before upstream dispatch. Migration 163 tombstones matching connections and invalidates their active leases without deleting history or widening API-key allowlists. The independent `chatgpt-web-codex` / `cgpt-codex` provider and its MIT-noticed vendored implementation remain available. +- **fix(usage):** quota and usage refresh no longer 409 when an exclusive lease reserves the connection ([#11758](https://github.com/diegosouzapw/OmniRoute/pull/11758)) — thanks @TheDemonTuan +- Keep the embedding registry's vector width and `embedding` type on models when a synced model exists for the same id, so `/v1/models` no longer reports registry-described embedding models widthless or untyped (#11761) — thanks @rahilmavani +- Correct `google/gemini-embedding-001` on the OpenRouter route to 3072 dimensions, the width it returns when `dimensions` is not sent (#11761) — thanks @rahilmavani +- **fix(vision-bridge):** Restrict automatic vision routing and fallbacks to models available in authoritative live provider catalogs, including aliases and effort variants ([#11767](https://github.com/diegosouzapw/OmniRoute/issues/11767)) — thanks @pacocartones ([#11813](https://github.com/diegosouzapw/OmniRoute/pull/11813)) +- **fix(providers):** reclassify Cerebras as a one-time $5 signup credit (payment method required, 30-day validity), not a recurring no-card 1M tokens/day trial ([#11773](https://github.com/diegosouzapw/OmniRoute/issues/11773)) ([#12591](https://github.com/diegosouzapw/OmniRoute/pull/12591)) — thanks @HouMinXi +- fix(cli): stop prepublish from re-rebuilding the already-built ESM-only opencode-plugin dist (#11787) ([#11990](https://github.com/diegosouzapw/OmniRoute/pull/11990)) — thanks @hartmark +- **fix(db):** Fresh installs no longer log a non-fatal `no such table: compression_run_telemetry` warning when retention cleanup runs before the lazily-created telemetry table exists ([#11802](https://github.com/diegosouzapw/OmniRoute/pull/11802)) — thanks @RaviTharuma +- fix(sse): set X-OmniRoute-Selected-Connection-Id on successful combo dispatches so downstream consumers stop falling back to an empty connection id (#11810) ([#11986](https://github.com/diegosouzapw/OmniRoute/pull/11986)) — thanks @hartmark +- **fix(services):** embedded CLIProxyAPI lifecycle checks now use public `/healthz`, while model discovery uses the configured dedicated data-plane API key instead of the management password ([#11811](https://github.com/diegosouzapw/OmniRoute/pull/11811)) — thanks @RaviTharuma +- Fix persisted model steps returning an error in the playground route simulator, and warn when structural steps cannot be simulated. ([#11862](https://github.com/diegosouzapw/OmniRoute/pull/11862)) — thanks @pacocartones +- **fix(providers):** Antigravity's dynamic mitmAlias table no longer routes `gemini-3.7-flash-{high,medium,low}` to a literal tier-suffixed upstream id just because one connected account's own discovery listed it directly — those display ids always resolve through the safe `gemini-3.7-flash-tiered` static alias, so one account's Google-provisioned access no longer 404s every sibling account of the provider ([#11824](https://github.com/diegosouzapw/OmniRoute/issues/11824), [#11651](https://github.com/diegosouzapw/OmniRoute/issues/11651)) ([#11988](https://github.com/diegosouzapw/OmniRoute/pull/11988)) — thanks @hartmark +- **fix(dashboard):** Expose Profile loading, errors, page structure, and XP progress to assistive technologies ([#11838](https://github.com/diegosouzapw/OmniRoute/pull/11838)) — thanks @pacocartones +- **fix(socks):** `Agent.connectTimeout` now bounds both SOCKS handshake and TLS connect, and `family === null` no longer falls back to `fetch-socks` ([#11842](https://github.com/diegosouzapw/OmniRoute/pull/11842)) — thanks @maxmad64bis +- **perf(compression):** OOM mitigations for large payload hashing, memoization, and token estimation ([#11844](https://github.com/diegosouzapw/OmniRoute/pull/11844) — thanks @AndrianBalanescu) +- Excluded Next.js Node File Trace manifests (`*.nft.json`) from the published npm tarball. They are build-time metadata and are never read while serving, but had grown to 668.7 MB — 61% of the package — which pushed the upload past the registry limit and made `npm publish` fail with `413 Payload Too Large`. ([#11864](https://github.com/diegosouzapw/OmniRoute/pull/11864)) +- **fix(cli):** keep the global OmniRoute server `--base-url` separate from provider-node `--endpoint` values in add, update, and validate commands ([#11860](https://github.com/diegosouzapw/OmniRoute/pull/11860)) — thanks @pacocartones +- **fix(config):** Nous Research's `Hermes-4-405B` model now displays as "Hermes 4 405B (Nous Research)" in both the provider registry and the free-model catalog, instead of the mislabelled "Hermes 4 7B" ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#11993](https://github.com/diegosouzapw/OmniRoute/pull/11993)) +- **fix(provider/nous):** inject required user= tag into Nous Research inference requests to resolve upstream 400 "missing tags" error ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#12044](https://github.com/diegosouzapw/OmniRoute/pull/12044)) +- **fix(build):** `npm run build` now fails in one second with a named package and a copy-pasteable fix when npm silently drops an externalised optional native dependency, instead of dying four minutes in with `Module not found: Can't resolve 'better-sqlite3'` ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- **fix(install):** `postinstall` no longer throws `ReferenceError: isAndroid is not defined` — failing the whole `npm install` — when the `better-sqlite3` rebuild fallback times out; the manual-fix guidance is reachable again ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- Fixed the Alibaba free-tier allowlist test that went red on its own once the shipped catalog's `validUntil` (2026-08-27) passed, leaving every PR and `main` with a failing `Unit Tests (1/8)`. The test now builds its own packs with dates it controls, and covers the expired-pack fallback that production has actually been serving. ([#11867](https://github.com/diegosouzapw/OmniRoute/pull/11867)) +- Split the npm registry upload into its own GitHub-hosted job. npm refuses `--provenance` from a self-hosted runner (`422 ... Only "github-hosted" runners are supported`), which blocked the v3.8.50 publish; the heavy verification cannot move to a hosted runner, so it now hands the proven tarball over instead. ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(ui):** the shared `DataTable` loading state no longer reads its decorative ⏳ glyph out to assistive technology, and now carries the same `role="status"` / `aria-live="polite"` / `aria-busy="true"` semantics as `PageLoading` ([#11869](https://github.com/diegosouzapw/OmniRoute/pull/11869)) — thanks @pacocartones +- **fix(api):** Bound the `timeout` accepted by `POST /api/providers/[id]/login` to the same 15s–600s window the Adobe Firefly and Conol browser-login paths already enforce, so a request can no longer pin a headful browser and the single active-login slot with an arbitrarily large poll budget. ([#11871](https://github.com/diegosouzapw/OmniRoute/pull/11871)) — thanks @pacocartones +- **fix(api):** Stop the tunnel and MITM routes from returning raw child-process error text, which disclosed host paths, binary install locations and Tailscale `tskey-*` credentials in a body some of these routes serve to non-loopback callers, and return a real 400 instead of a framework 500 when a tunnel request body fails validation. ([#11872](https://github.com/diegosouzapw/OmniRoute/pull/11872)) — thanks @pacocartones +- **fix(open-sse):** stream TTFT/ITL are sampled from a monotonic clock (`performance.now`) instead of `Date.now`, so an NTP correction or wall-clock jump can no longer inflate a genuine TTFT or produce a negative one that the `ttft >= 0` guard silently drops — protecting the router's OTel/EWMA/`usage_history`/speed-ranking signals ([#11873](https://github.com/diegosouzapw/OmniRoute/pull/11873)) — thanks @pacocartones +- **fix(providers):** xAI `reasoning_effort: "xhigh"` now reaches grok-4.6+ instead of being silently clamped to `"high"` ([#11879](https://github.com/diegosouzapw/OmniRoute/pull/11879)) — thanks @NoxzRCW +- **fix(dashboard):** the providers page no longer crashes into the error boundary when a provider id collides with an `Object.prototype` member (`constructor`, `__proto__`); icon lookups are own-property guarded ([#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)) — thanks @NoxzRCW +- **fix(skills):** injected skill tools declared in shorthand (`{"content": "string"}`) now forward valid JSON Schema, unblocking providers that validate tool schemas strictly such as Zhipu GLM on the Console Go tier ([#11881](https://github.com/diegosouzapw/OmniRoute/pull/11881)) — thanks @NoxzRCW +- **fix(api):** `POST /api/playground/simulate-route` now surfaces `combo-ref` and `provider-wildcard` persisted combo steps with a specific warning (naming the referenced combo, or the unresolved `provider/model` wildcard) instead of folding them into a generic "unsupported step" count; a `provider-wildcard` step is also now included as an unresolved target so the operator can see it is in the route (ported from [#11882](https://github.com/diegosouzapw/OmniRoute/pull/11882) — thanks @NoxzRCW). ([#11930](https://github.com/diegosouzapw/OmniRoute/pull/11930)) +- **fix(translator):** the streaming OpenAI→Claude translator keeps upstream usage, including prompt-cache tokens, when it arrives on a trailing `choices: []` chunk (Fireworks and any upstream using `stream_options.include_usage`) ([#11883](https://github.com/diegosouzapw/OmniRoute/pull/11883)) — thanks @NoxzRCW +- **fix(cli):** `omniroute update --apply` now tells you explicitly whether a running server was detected and, if so, that you must run `omniroute restart` to apply the update — it never restarted anything and previously implied the update was already live once files were installed. The dashboard's npm-mode Update flow (`/api/system/version`) now tries OmniRoute's own PID-file-managed supervisor before falling back to pm2, and reports an honest "restart required" step instead of a silent pm2-only "skipped" that read like a completed update. The server-side latest-version lookup backing the dashboard's update banner also gained `--prefer-online`, closing the same stale-npm-cache class already fixed in the CLI's own copy for #4376 ([#11885](https://github.com/diegosouzapw/OmniRoute/issues/11885)). ([#12005](https://github.com/diegosouzapw/OmniRoute/pull/12005)) +- **fix(resilience):** clear persisted LKGP pins when a target suffers connection/provider exhaustion or is skipped before dispatch due to cooldown/exhaustion/unavailability, preventing subsequent requests from repeatedly prioritizing known-dead providers ([#11911](https://github.com/diegosouzapw/OmniRoute/issues/11911)). ([#12013](https://github.com/diegosouzapw/OmniRoute/pull/12013)) — thanks @HouMinXi +- **fix(sse):** the OpenAI→Claude stream translator now defers the terminal `message_delta`/`message_stop` emission until the real usage block has arrived (or a genuine end-of-stream flush forces it) instead of emitting it immediately on `finish_reason` — previously, when the trailing usage-only chunk (`{"choices":[],"usage":{...}}`) arrived *after* the `finish_reason` chunk (the normal order for Fireworks/vLLM/Together and other `stream_options.include_usage` upstreams), the client-visible `message_delta` still carried stale/zero usage even though `state.usage` was internally corrected too late to matter (ported from [#11915](https://github.com/diegosouzapw/OmniRoute/pull/11915) — thanks @HouMinXi). ([#11933](https://github.com/diegosouzapw/OmniRoute/pull/11933)) +- **fix(sse):** `stripStore()` now forces `store=false` for stateless OpenAI-compatible Responses-API targets (unless the connection has explicitly opted in via `providerSpecificData.openaiStoreEnabled`), instead of only handling the `openai`/`agentrouter` cases — a client-supplied `store` value previously passed through untouched to backends that don't actually persist responses server-side ([#11916](https://github.com/diegosouzapw/OmniRoute/pull/11916) — thanks @HouMinXi). +- **fix(models):** custom provider-node models (synced, custom, and alias-backed) now appear under their configured prefix in the unified catalog when the operator's model-id prefix mode is canonical, instead of being dropped whenever alias-inclusion was otherwise disabled ([#11918](https://github.com/diegosouzapw/OmniRoute/pull/11918) — thanks @HouMinXi). +- **fix(models):** the unified model catalog now suppresses stale static registry models (including effort-tier variants) for any provider whose active connection has an authoritative live synced catalog, not only providers already using exclusive-synced-listing — a connection with `providerUsesAuthoritativeLiveCatalog` previously kept serving both the live-synced models and the stale static rows side by side ([#11919](https://github.com/diegosouzapw/OmniRoute/pull/11919) — thanks @HouMinXi). +- **fix(ui):** `ProviderIcon`'s three remaining unguarded lookups (`PROVIDER_ICON_ALIASES`, `LOCAL_SVG_ALIASES`, `THEMED_SVGS`) now use `Object.hasOwn()` own-property checks — a provider id such as `constructor` or `__proto__` previously resolved through the prototype chain instead of falling through to the unknown-provider CDN fallback (`getLobeProviderIcon()` itself was already guarded by [#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)); `ProviderPageHeader`'s `color` field is also now optional, matching the rest of the component's defensive typing (ported from [#11920](https://github.com/diegosouzapw/OmniRoute/pull/11920) — thanks @HouMinXi). ([#11935](https://github.com/diegosouzapw/OmniRoute/pull/11935)) — thanks @NoxzRCW +- fix(ollama): preserve multi-byte UTF-8 content split across stream chunks in the Ollama NDJSON transform, which previously corrupted CJK/emoji into U+FFFD (#11921) — thanks @pacocartones +- **fix(providers):** OrcaRouter chat requests now target `/v1/chat/completions` instead of the bare `/v1` API root, fixing the upstream `404 Invalid URL (POST /v1)` ([#11923](https://github.com/diegosouzapw/OmniRoute/pull/11923)). — thanks @echel0nn +- **fix(plugins):** deliver the `onStreamComplete` event to disk-installed plugins. The event shipped in v3.8.50 (#9669) was emitted internally but had no plugin-facing wiring, so no plugin could ever subscribe: the manifest schema silently dropped `hooks.onStreamComplete`, and the loader/manager only knew the seven legacy hooks. `onStreamComplete` is now a declarable manifest hook, wired through the loader and registered by the manager like the other hooks, and its payload carries a `requestId` so consumers can correlate the stream-completion event with the originating request ([#11934](https://github.com/diegosouzapw/OmniRoute/pull/11934)) — thanks @amaleta / @pacocartones +- **fix(db):** the Qdrant embedding-model dropdown now lists local/self-hosted providers (Ollama, LM Studio, vLLM, etc.) — an active connection is treated as "configured" when the provider allows an optional API key, not only when it has a real key or OAuth, so a running local embedding provider is no longer hidden from the picker ([#11949](https://github.com/diegosouzapw/OmniRoute/issues/11949)) ([#11995](https://github.com/diegosouzapw/OmniRoute/pull/11995)) +- **fix(providers):** Vertex AI Anthropic partner-model discovery now calls the Model Garden `v1beta1` publisher list (`/v1beta1/publishers/anthropic/models`, global) and parses its `publisherModels` envelope, so Claude models auto-synced from Vertex populate the active live catalog and route at request time instead of returning `Model '' is not available in the active live catalog` ([#11991](https://github.com/diegosouzapw/OmniRoute/issues/11991)) — thanks @fabioluissilva ([#11998](https://github.com/diegosouzapw/OmniRoute/pull/11998)) +- **fix(cli):** support `--base-url` alongside `--endpoint` in `omniroute nodes add`, `update`, and `validate` subcommands to prevent global `--base-url` shadowing issues ([#11999](https://github.com/diegosouzapw/OmniRoute/issues/11999)). ([#12033](https://github.com/diegosouzapw/OmniRoute/pull/12033)) — thanks @HouMinXi +- **fix(providers):** `cloudflare-ai` no longer refuses image content parts for every Workers AI model ([#12002](https://github.com/diegosouzapw/OmniRoute/pull/12002)) — the plain-string `content` requirement behind #2539 is carried by the _model_ schema, not by the `/ai/v1/chat/completions` endpoint (measured: an all-text part array returns 200 on `@cf/mistralai/mistral-small-3.1-24b-instruct`, `@cf/meta/llama-4-scout-17b-16e-instruct` and `@cf/meta/llama-3.3-70b-instruct-fp8-fast`, and 400 on the text-only `@cf/qwen/qwen2.5-coder-32b-instruct`). `transformRequest()` flattened every array and threw on the first non-text part (#6390), so image input was refused for vision-capable Cloudflare models that accept it. All-text arrays are still flattened — the one shape every model accepts — while an array carrying a non-text part is passed through untouched, so the attachment is still never silently dropped. Regression guards: `tests/unit/cloudflare-ai-image-parts-6390.test.ts`. — thanks @davidlinfr +- **fix(resilience):** decouple the limiter-managed execution backstop from the queue-wait budget — new `requestQueue.executionMaxWaitMs` (env `RATE_LIMIT_EXECUTION_MAX_WAIT_MS`, default 600000 = 10 min) now feeds Bottleneck's post-dispatch `expiration`, while `requestQueue.maxWaitMs` keeps its documented queue-wait semantics. Previously the queue-wait budget doubled as the execution expiration, so legitimate long-running calls on non-incremental gateways (whole generation buffered before the first upstream byte, e.g. Console Go / Command Code tiers serving GLM models) were killed mid-flight at the queue budget with a false 504 `RATE_LIMIT_EXECUTION_TIMEOUT` — the local limiter undercut the provider-aware upstream fetch-start timeouts. The surfaced 504 message now names `requestQueue.executionMaxWaitMs`; the error keeps the #4165 guarantees (disclaims an upstream timeout, preserves the Bottleneck error as `cause`, branded code + trusted provenance, classified request-scoped so combo falls back). A real queue-wait bound (the `Promise.race` around `limiter.schedule()` sketched in #9533) remains future work. (#12025) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **Call logs:** keep the `error` field when an artifact exceeds the storage cap, instead of replacing it with the omission marker. The error is the only field that says *why* a request failed and is typically ~90 bytes next to the multi-hundred-KB bodies that trip the cap, so dropping it left a size-limited row undiagnosable — a provider outage, a local timeout and an upstream 400 all rendered identically. It is now preserved at every fallback stage, truncated to 4KB if it is itself large ([#12026](https://github.com/diegosouzapw/OmniRoute/issues/12026)). ([#12095](https://github.com/diegosouzapw/OmniRoute/pull/12095)) — thanks @ntdat812 +- **fix(diagnostics):** preserve the error field (truncated to 4KB with a `[truncated: …]` suffix) in every call-log artifact size-limit fallback stage. Previously the minimal fallback replaced the error with `[omitted: call log artifact size limit exceeded]`, so an oversized artifact row showed nothing about WHY the request failed — e.g. 91 of 847 opencode-go 504 rows on one production instance were undiagnosable from the dashboard. Oversized request/response bodies are still omitted exactly as before; the error cap is independent of the payload sizes that tripped the fallback. (#12026) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **fix(sse):** OpenAI Responses clients that declare the native `web_search` tool now receive a spec-shaped `web_search_call` output item with `action.sources` alongside the preserved function-call round-trip, so search results executed through OmniRoute's own search backend are consumable by standard Responses clients (Codex, pi-web-access, …). ([#12031](https://github.com/diegosouzapw/OmniRoute/pull/12031)) — thanks @watchingdogs +- **fix(cli):** use in-thread alias resolver hooks on modern runtimes to avoid deprecation noise and improve Node.js forward compatibility ([#12073](https://github.com/diegosouzapw/OmniRoute/issues/12073)). ([#12083](https://github.com/diegosouzapw/OmniRoute/pull/12083)) — thanks @xiaoyaner0201 +- **fix(combo):** an operator-set **Agent Features → Context length** on a combo is now honored at request time. The value was persisted and advertised through `/v1/models`, but `resolveComboContextLimit()` never consulted it — so a multi-target combo whose members carry no per-model window fell through to the provider's generic `defaultContextLength` (openrouter 128000, command-code 200000) and rejected large requests with `Input exceeds context window … limit 128000` despite the combo being explicitly sized much larger. An identical single-target combo worked, because it collapses to its concrete target before the guard runs. Invalid values (0/negative/NaN/Infinity) are ignored, so the existing target → combo-min → fallback order is unchanged. ([#12090](https://github.com/diegosouzapw/OmniRoute/pull/12090)) — thanks @adivekar-utexas +- **fix(sse):** passthrough streams now estimate usage on finish when upstream closes without usage even with `stream_options.include_usage` — avoids `0 tokens / 0%` for providers that stay silent (and correctly handles trailing empty-choices usage) ([#12151](https://github.com/diegosouzapw/OmniRoute/pull/12151)) — thanks @maxmad64bis +- **fix(combos):** clearing an agent feature in the combos editor now persists — unchecking context cache protection, or emptying the system message or tool filter, sends an explicit `null` instead of dropping the field from the `PUT` body, which the update merge read as "leave unchanged" ([#12177](https://github.com/diegosouzapw/OmniRoute/pull/12177)) — thanks @foreveryh / @ntdatt812 +- **fix(memory):** self-hosted embedding endpoints now vectorize — the vector width is measured from the first embedding that comes back instead of being read from a registry that cannot describe them, so `vec_memories` is created and memories stop piling up unvectorized behind a green health check ([#12180](https://github.com/diegosouzapw/OmniRoute/pull/12180)) — thanks @kanade-hoshino / @ntdatt812 +- **fix(translator):** the leading `system` message now reaches Responses-API upstreams when its `content` is a content-part array — it was read as `typeof content === "string" ? content : ""`, so a prompt-caching client (Anthropic `cache_control`, the shape LiteLLM and the Anthropic SDK emit) had its entire system prompt replaced by an empty `instructions`. The request was still accepted with a normal `prompt_tokens` count, so the model answered with no instructions and nothing in the response said they were missing. Mid-conversation system turns already handled the array shape ([#7056](https://github.com/diegosouzapw/OmniRoute/pull/7056)); only the first one did not ([#12206](https://github.com/diegosouzapw/OmniRoute/issues/12206)). Regression guard: `tests/unit/translator-openai-responses-system-content-parts.test.ts`. ([#12207](https://github.com/diegosouzapw/OmniRoute/pull/12207)) — thanks @Gorillaz322 +- **fix(free-tier):** `/api/free-tier/summary` no longer computes its totals from a Radar feed built before the catalog the running release ships. When the cached feed is older — or carries no build date at all — the route answers from the shipped catalog, resolved through the operator's local model state so disabled and tombstoned models stay out of the numbers ([#12215](https://github.com/diegosouzapw/OmniRoute/pull/12215)). — thanks @maxmad64bis +- **fix(oauth):** Keep a Claude personal workspace and a Team organization as separate connections — they share the same email and `accountUUID`, so the email-only OAuth dedup let the second login overwrite the first account's tokens; `organizationUUID` now disambiguates them, the way `workspaceId` does for Codex ([#12222](https://github.com/diegosouzapw/OmniRoute/pull/12222)) — thanks @davidebaraldo +- **fix(resilience):** a 402 on a single paid model of a passthrough/gateway provider (e.g. `kilo-gateway`, `ollama-cloud`) no longer terminalizes the whole connection with a never-auto-recovered `credits_exhausted` status — only the paid model is locked out, so free models on the same key keep serving. 402 variant of [#3027](https://github.com/diegosouzapw/OmniRoute/issues/3027). Single-credential providers are unaffected — a 402 there is still treated as the key being genuinely out of credit ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239) / [#10616](https://github.com/diegosouzapw/OmniRoute/issues/10616)) ([#12242](https://github.com/diegosouzapw/OmniRoute/issues/12242)) — thanks @brick30llc-ctrl ([#12266](https://github.com/diegosouzapw/OmniRoute/pull/12266)) — thanks @geekyNads +- **fix(sse):** trust `finish_reason: "length"`/`"max_tokens"` over the reasoning-consumed-token ratio in response quality validation, so a reasoning model truncated below the old 90% threshold correctly fails and retries instead of returning empty content as a silent "success" ([#12262](https://github.com/diegosouzapw/OmniRoute/pull/12262)) — thanks @brick30llc-ctrl +- **fix(settings):** `PUT /api/settings/cache-config` now persists `alwaysPreserveClientCache` to the flat general settings the runtime cache-control policy actually reads; previously the value landed in the databaseSettings "cache" section and was silently ignored, so the endpoint had no effect on `cache_control` passthrough ([#12304](https://github.com/diegosouzapw/OmniRoute/pull/12304)) — thanks @davidebaraldo +- **fix(grok-cli):** treat omitted SuperGrokPro `creditUsagePercent` as 0% used so Provider Limits still renders a weekly bar (proto3 zero-elision) ([#12312](https://github.com/diegosouzapw/OmniRoute/pull/12312)) — thanks @HouMinXi +- **fix(combo):** Expose the two Auto-Combo scoring factors nobody could set — the combo validation schema and the dashboard weight sliders both declared 13 of the scorer's 15 factors, so `connectionDensity` (spreads load across a provider's connections) and `quality` were dropped on save and offered nowhere. The sliders also shipped their own default table that differed from the engine's on every non-zero factor and summed to 1.05, so the percentages shown next to them added up to 105%. Both lists now match `DEFAULT_WEIGHTS`, and a test keeps them there. Note that a combo whose stored `weights` omitted the two keys was effectively running with them at zero and the other thirteen renormalized upward; it now runs with the engine's intended distribution, so its routing does shift ([#12314](https://github.com/diegosouzapw/OmniRoute/pull/12314)) — thanks @maxmad64bis +- **fix(docs):** The free-tier reference no longer says its numbers come "confidence tagged per row" — no catalog entry carries a confidence tag and the API serves none, so every figure on that page is an estimate of the same, unstated quality. The page now states what an entry does vouch for: an independently documented hard stop (set by hand with the source in a comment, never defaulted to `true`) and a prompt-training disclosure, both with live counts the `check:docs-counts` gate keeps honest ([#12318](https://github.com/diegosouzapw/OmniRoute/pull/12318)) — thanks @maxmad64bis +- **fix(usage):** `adobe-firefly` and `firefly` have had a working usage fetcher since Adobe Firefly landed, but neither was ever added to the registration list, so the provider-plugin manifest, `genericQuotaFetcher` and the free-access quota cache all reported them as having no usage support — while `USAGE_SUPPORTED_PROVIDERS` said the opposite. Both are now declared, which also means their credit balance is fetched like any other declared provider's: `registerGenericQuotaFetchers` now registers a generic quota fetcher for them, and `resolveFreeAccessState` no longer returns early. A test holds the registration list to the dispatcher's switch in both directions, which is what the module's own docstring already asked for in prose ([#12321](https://github.com/diegosouzapw/OmniRoute/pull/12321)) — thanks @maxmad64bis +- **fix(quota):** drop the generic quota cache (agy / Antigravity / Claude OAuth) on an upstream 429 so reset-aware scoring does not keep a 60s stale snapshot, and force-refresh the next usage fetch so inner provider caches cannot recache the same window ([#12325](https://github.com/diegosouzapw/OmniRoute/pull/12325)) — thanks @HouMinXi +- **fix(combos):** deleting a combo now clears its persisted LKGP pins instead of leaving unreachable `key_value` rows behind ([#12326](https://github.com/diegosouzapw/OmniRoute/issues/12326)) ([#12425](https://github.com/diegosouzapw/OmniRoute/pull/12425)) — thanks @KrzysiekSko +- **fix(sse):** Keep ZWNJ (U+200C) and ZWJ (U+200D) in assistant text, reasoning and tool-call arguments — Persian/Kurdish half-space (`ارائه‌دهنده`), Arabic/Indic shaping and emoji sequences no longer lose them; the response de-obfuscation now removes joiners only between ASCII word characters, where the request side inserts them ([#12186](https://github.com/diegosouzapw/OmniRoute/issues/12186)) — thanks @rezjalibd ([#12359](https://github.com/diegosouzapw/OmniRoute/pull/12359)) — thanks @pacocartones +- **fix(resilience):** count resolved upstream 5xx results against the provider circuit breaker on the chat path — `CircuitBreaker.execute()` no longer reads a resolved `{ success: false, status: 5xx }` as a success that cancels the call-site failure, so a provider answering 503s now trips its breaker instead of staying `CLOSED` at `failureCount: 1`; single-model and combo dispatches are each accounted exactly once ([#12254](https://github.com/diegosouzapw/OmniRoute/issues/12254)) ([#12360](https://github.com/diegosouzapw/OmniRoute/pull/12360)) — thanks @pacocartones +- **fix(providers):** resolve the Codex quota auto-ping model from the live provider catalog and lifecycle registry instead of the retired `gpt-5.1-codex-mini`, and pause the ping with one actionable warning when no selectable Codex model exists rather than retrying a shut-down id every cooldown window ([#11905](https://github.com/diegosouzapw/OmniRoute/issues/11905)) ([#12361](https://github.com/diegosouzapw/OmniRoute/pull/12361)) — thanks @pacocartones +- **fix(api):** keep the `{created, data}` wrapper on combo-routed `/v1/images/generations` responses and default Codex image results to `b64_json` on both `/v1/images/generations` and `/v1/images/edits` so Codex CLI's built-in `image_gen` can decode them ([#12268](https://github.com/diegosouzapw/OmniRoute/issues/12268)) ([#12362](https://github.com/diegosouzapw/OmniRoute/pull/12362)) — thanks @pacocartones +- **fix(sse):** Name the shadowed custom provider node when a built-in provider id/alias (e.g. `openference` → `of`) reserves the prefix of an existing OpenAI/Anthropic-compatible node, so the runtime `No active credentials for provider: ` error explains that the prefix routed to the built-in and never reached the node's healthy connections, instead of contradicting the dashboard ([#11943](https://github.com/diegosouzapw/OmniRoute/issues/11943)) — thanks @morpheus9393 ([#12365](https://github.com/diegosouzapw/OmniRoute/pull/12365)) — thanks @pacocartones +- **fix(i18n):** wrap `ccOnboardingKeyPlaceholder` in ICU single quotes across all 43 locales so angle brackets render literally instead of being parsed as rich-text tags, which crashed the Claude Code onboarding block with `INVALID_MESSAGE: INVALID_TAG` ([#12302](https://github.com/diegosouzapw/OmniRoute/issues/12302)) ([#12369](https://github.com/diegosouzapw/OmniRoute/pull/12369)) — thanks @gonisulaimann +- **fix(models):** publish `effort_tiers` on Kimi K3's synced base-model entries (`kmca/k3`, `kmca/k3-256k`) so catalog-only clients (OpenCode, plain SDK pickers) can see and select the reasoning tiers (`low`/`high`/`max`) the synced metadata already carried — the `isSkippedEffortProvider` gate no longer suppresses tier visibility on those base entries, while synthetic `-` variant generation stays prevented and Codex/GLM base models remain excluded unchanged ([#12299](https://github.com/diegosouzapw/OmniRoute/issues/12299)) ([#12371](https://github.com/diegosouzapw/OmniRoute/pull/12371)) — thanks @gonisulaimann +- **fix(guardrails):** keep `auto`/`auto/*` virtual combos exempt from the Vision Bridge `fixedModel` credential guard so a combo target is passed through instead of silently falling back to global auto-selection ([#12237](https://github.com/diegosouzapw/OmniRoute/issues/12237)) ([#12373](https://github.com/diegosouzapw/OmniRoute/pull/12373)) — thanks @pacocartones +- **fix(combo):** capability-filter exhaustion caused by `max_tokens` above every target's known output limit now reports that reason (requested `max_tokens` vs the pool's highest known ceiling) instead of the unrelated "supports structured output" message ([#12229](https://github.com/diegosouzapw/OmniRoute/issues/12229)) — thanks @DW-MediaLab ([#12374](https://github.com/diegosouzapw/OmniRoute/pull/12374)) — thanks @pacocartones +- **fix(auth):** the `least-used` account strategy now prefers accounts without backoff before falling back to oldest `lastUsedAt`, the same tie-break `round-robin` already applies, so a failover no longer lands on a just-rate-limited account for a single request ([#12279](https://github.com/diegosouzapw/OmniRoute/issues/12279)) — thanks @tenshiak ([#12375](https://github.com/diegosouzapw/OmniRoute/pull/12375)) — thanks @pacocartones +- **fix(docker):** the `chatgpt-web-codex-browser` image now finds the Chrome binary under `chrome-linux64/` (Chrome for Testing layout in `playwright:v1.62.0-noble`) as well as the legacy `chrome-linux/`, so the container no longer crash-loops with `exec: --headless=new: not found` ([#12024](https://github.com/diegosouzapw/OmniRoute/issues/12024)) ([#12376](https://github.com/diegosouzapw/OmniRoute/pull/12376)) — thanks @pacocartones +- **fix(providers):** declare `groq/compound` and `allam-2-7b` as non-reasoning models in the curated Groq registry so `reasoning_effort` / `output_config.effort` / `thinking` from Claude Code are stripped instead of forwarded, which Groq rejected with HTTP 400 ([#12134](https://github.com/diegosouzapw/OmniRoute/issues/12134)) ([#12379](https://github.com/diegosouzapw/OmniRoute/pull/12379)) — thanks @pacocartones +- **fix(executors):** `OpencodeExecutor` no longer forces a direct connection when the connection has a proxy assigned in Proxy Management but no per-account proxies: the single-account fast path used to wrap the upstream dispatch in the direct-egress sentinel, discarding the ambient proxy context the chat handler had pinned from `proxy_assignments`, so API-key `opencode`/`opencode-go` connections egressed from the host IP (and hit geoblocks) despite the assignment. The direct pin is now applied only when no ambient proxy context exists ([#11894](https://github.com/diegosouzapw/OmniRoute/issues/11894) — thanks @hizzt) ([#12380](https://github.com/diegosouzapw/OmniRoute/pull/12380)) — thanks @pacocartones +- **fix(api):** `GET /v1/models` with `MODELS_CATALOG_PREFIX_MODE=canonical` (or `?prefix=canonical`) now lists providers whose registry alias is undefined or equal to their own id (Antigravity, Antigravity CLI and other self-aliased built-ins) — their single `provider/model` id was dropped by the alias/canonical duplicate guard in the static, synced, custom and alias-backed catalog loops ([#12058](https://github.com/diegosouzapw/OmniRoute/issues/12058)) — thanks @cheynetom ([#12381](https://github.com/diegosouzapw/OmniRoute/pull/12381)) — thanks @pacocartones +- **fix(translator):** Drop replayed `thinking` blocks that carry no signature (the shape produced from cross-provider `reasoning_content`) instead of stamping the default Claude signature on them, which Anthropic rejected with `400 Invalid signature in thinking block` on the next turn served by an Anthropic rung ([#12105](https://github.com/diegosouzapw/OmniRoute/issues/12105)) — thanks @atescivitci-cmd ([#12386](https://github.com/diegosouzapw/OmniRoute/pull/12386)) — thanks @pacocartones +- **fix(cli):** Resolve Bun's `--preload` polyfill path against the package root instead of `dist/`, so `omniroute` installed with `bun install -g` no longer crashes at startup with `error: preload not found …/dist/open-sse/utils/setupPolyfill.ts` ([#11980](https://github.com/diegosouzapw/OmniRoute/issues/11980)) — thanks @joglomedia ([#12387](https://github.com/diegosouzapw/OmniRoute/pull/12387)) — thanks @pacocartones +- **fix(providers):** `gemini-business` now publishes its model catalog — `/v1/models` and `/v1/providers/gemini-business/models` list the 12 enterprise Gemini ids the executor understands instead of returning an empty list (#12107) ([#12389](https://github.com/diegosouzapw/OmniRoute/pull/12389)) — thanks @pacocartones +- **fix(db):** install `busy_timeout` before the SQLite connection's first statement so a process opening the database while another one closes its WAL connection waits out the transient EXCLUSIVE lock instead of dying with `database is locked`, and recognise the drivers' real BUSY/PROTOCOL/IOERR errors as transient in the corruption probe so the same lock no longer renames the database away as corrupt; deflakes `cross-process contenders never both acquire the same connection` (#12394 — thanks @pacocartones) +- **fix(chat-admission):** derive the `chat_admission_busy` 503 `Retry-After` from observed heavyweight-lease occupancy — the larger of the exhausted `OMNIROUTE_CHAT_ADMISSION_QUEUE_MS` window and the time since capacity last turned over, capped at 60 s — instead of a fixed 1 s (structural) / 2 s (byte-stage) hint that invited Codex/agent fan-out clients to re-send ~1 MiB `/v1/responses` bodies every second into a gate held for the whole SSE lifetime; an idle gate keeps the historical floors ([#12135](https://github.com/diegosouzapw/OmniRoute/issues/12135)) (#12395 — thanks @pacocartones) +- **fix(api-manager):** the API key permissions modal no longer silently drops `allowedCombos` entries its Combo picker cannot render — routing-rule names such as `rt-*`, which the backend already honours — when "All" is clicked and the key is switched back to "Restrict"; those entries now survive the toggle, are listed read-only under the combo list so the count and the list agree, and are saved back verbatim instead of persisting `[]` (deny-all) (#12397 — thanks @pacocartones) +- **fix(catalog):** write the NUL separator of the catalog connection memo key, the provider serviceKind memo key, the Video Bridge promotion group key and a JSON-exactness test fixture as the `\u0000` escape instead of a raw byte — same runtime value, but the raw byte made git, GitHub and ripgrep treat those files as binary (hidden PR diffs, silently skipped searches); a guard test now keeps raw NUL bytes out of `src/`, `open-sse/` and `tests/` (#12403 — thanks @pacocartones) +- **fix(providers):** add `CLAUDE_CODE_CLIENT_VERSION` and `GITHUB_COPILOT_CLI_VERSION` env overrides so Anthropic/Copilot client-version gates can be unblocked without a rebuild ([#12417](https://github.com/diegosouzapw/OmniRoute/issues/12417)) ([#12632](https://github.com/diegosouzapw/OmniRoute/pull/12632)) — thanks @HouMinXi +- **fix(db):** back-fill `last_ping_at` and `last_pinged_reset_key` on `provider_connections` during schema reconciliation so divergent lineages that skipped `123_quota_auto_ping` still accept quota auto-ping writes ([#12470](https://github.com/diegosouzapw/OmniRoute/pull/12470) — thanks @KooshaPari) +- **fix(ci):** document MIT exceptions for `@eloqnt/{config,format-json,format-po}` (next-intl transitive; locked tarballs omit `license`) and keep the A2A lifecycle vitest off the real SQLite persistence seam ([#12581](https://github.com/diegosouzapw/OmniRoute/issues/12581)) ([#12595](https://github.com/diegosouzapw/OmniRoute/pull/12595)) +- OpenCode plugin `/v1/models` catalog fetch now waits 30s by default and attaches HTTP `statusCode` on 401/5xx so host fallback plugins can hop instead of seeing an untyped AbortError/UnknownError. ([#12607](https://github.com/diegosouzapw/OmniRoute/pull/12607)) — thanks @RaviTharuma +- **CI:** the OpenAPI security-tier gate now mirrors `isAlwaysProtectedPath()` in full — it also reads `ALWAYS_PROTECTED_API_PATTERNS`, so the pattern-gated credential routes (`/api/providers/{id}/{claude,codex}-auth/{export,apply-local}`, GHSA-5926-2w35-7h4q) no longer report as unannotated. (#12605) +- **fix(api):** GET /v1/models no longer waits forever on a hung coalesced catalog rebuild; cold-path waits are bounded (`CATALOG_BUILD_TIMEOUT_MS`, default 8s) and a last-good 200 is served when the rebuild times out ([#12627](https://github.com/diegosouzapw/OmniRoute/issues/12627)). ([#12628](https://github.com/diegosouzapw/OmniRoute/pull/12628)) — thanks @RaviTharuma +- **fix(cursor):** a non-array `tool_calls` on an assistant message no longer crashes the cursor request translator with a `TypeError`; both loops now require an array ([#12689](https://github.com/diegosouzapw/OmniRoute/issues/12689)) ([#12691](https://github.com/diegosouzapw/OmniRoute/pull/12691)) — thanks @soroush5 +- **fix(chat):** Chat Completions no longer return empty `content` after a server-owned memory or skills tool; the first provider send and account/model recovery now share one pipeline so a follow-up round-trip can fill the reply ([#12696](https://github.com/diegosouzapw/OmniRoute/issues/12696)) — thanks @HouMinXi ([#12867](https://github.com/diegosouzapw/OmniRoute/pull/12867)) +- **fix(dashboard):** batch-deleting provider keys no longer toasts failure after a successful delete when the confirm button's click event is forwarded as `onAfter` ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) — thanks @HouMinXi +- **fix(glm):** drop the extra 16th argument to `createSSETransformStreamWithLogger` that TypeScript rejected (TS2554) and that never reached the TransformStream ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) ([#12770](https://github.com/diegosouzapw/OmniRoute/pull/12770)) — thanks @HouMinXi +- **fix(dashboard):** pass `nodeMap` into Runtime `QuotaGroup` so a quota monitor in error/exhausted/alerting no longer throws `ReferenceError: nodeMap is not defined`. ([#12868](https://github.com/diegosouzapw/OmniRoute/pull/12868)) — thanks @HouMinXi +- Restricted API keys whose `allowedModels` lists a combo name no longer skip every combo member at pre-dispatch (`ALL_TARGETS_SKIPPED`). Inner-target filtering still applies when the allow-list is a provider prefix or `disableNonPublicModels` is on ([#12899](https://github.com/diegosouzapw/OmniRoute/pull/12899)). — thanks @HouMinXi +- **fix(combos):** Effort-suffixed combo members inherit the base model's `model_context_overrides` row so priority order is not inverted on large requests ([#12926](https://github.com/diegosouzapw/OmniRoute/pull/12926)) — thanks @HouMinXi +- **fix(catalog):** Picker-added `customModels` enter the dispatch-time live catalog so combo members and bare inference no longer 400 ([#12934](https://github.com/diegosouzapw/OmniRoute/pull/12934)) — thanks @HouMinXi +- **fix(docker):** re-derive the Docker build's worker-pool memory budget from the MEASURED ~4.5 GB per-process RSS (the issue owner's own VPS dmesg OOM-killer reproduction) instead of the stale 2560 MB/worker inference #11419 shipped, and lower `OMNIROUTE_BUILD_WORKERS` 3 → 2 so 1 parent + 1 page-data worker (2 processes × 4.5 GB = 9 GB) fits the 12.288 GB (75%) budget on a 16 GB GitHub Actions runner — the previous default (1 parent + 2 workers = 13.5 GB) still overcommitted the runner and kept "Publish to Docker Hub" failing with `cannot allocate memory` after #11419 merged (#7518). ([#11663](https://github.com/diegosouzapw/OmniRoute/pull/11663)) +- **fix(electron):** the packaged Windows desktop build now passes the #7592 cold-restart smoke end to end. Five defects were found and fixed on the way: (1) optional-pack staging failed on any machine whose `tar` is GNU tar — it reads the drive letter in an absolute `-f C:\...` archive path as a remote rsh host (`Cannot connect to C:`), so staging now passes a bare filename with `cwd` at the tarball directory and surfaces tar stderr on failure; (2) the Electron `files` allowlist was missing `lib/loginHeaderCapture.js`, so the packaged main process crashed at startup with `Cannot find module './lib/loginHeaderCapture'`; (3) electron-builder ≥26 injects an `!**/node_modules/**` ignore into every extraResources pattern list that no positive filter can override, which silently dropped the staged runtime `node_modules` (including the better-sqlite3 N-API prebuild) from `resources/app` — a fresh v3.8.50 desktop build would have shipped with no native SQLite driver and reproduced the #7592 sql.js fallback on every machine; an `afterPack` hook now restores the staged `node_modules` after packing; (4) the packaged-app smoke harness redirected `USERPROFILE` into its temp DATA_DIR, but Electron resolves the Roaming profile from `%USERPROFILE%\AppData\Roaming\` (USERPROFILE takes precedence over the APPDATA env var) and the path service throws instead of creating it, so `requestSingleInstanceLock()` returned false and the app exited(0) silently before `app.whenReady()` — the harness now pre-creates the derived tree, and `ensureSmokeEnvDirs` is exported and unit-tested; (5) the #7592 driver assertion parsed a `[DB] Driver: ...` line that the server's primary DB path never printed (only the unused `openDatabaseAsync()` did) — `getDbInstance()` now emits the same line on open so the guard can actually assert the native driver. Also: the smoke env-allowlist unit test hardcoded POSIX paths and could only pass on Linux/macOS; expectations are now host-agnostic, with new regression tests for the USERPROFILE-derived tree and `tarPack` under absolute Windows-style paths ([#11443](https://github.com/diegosouzapw/OmniRoute/pull/11443)) — thanks @jonlwheat2-gif +- fix(sse): stop the auto-combo candidates inspector from silently dropping model-locked/cooled-down rows (#9133) ([#11994](https://github.com/diegosouzapw/OmniRoute/pull/11994)) — thanks @hartmark +- **fix(providers):** Perplexity Web no longer turns upstream stream failures into successful assistant text; pre-content failures remain eligible for fallback, partial output ends with a structured sanitized error, and failed sessions are not persisted ([#12465](https://github.com/diegosouzapw/OmniRoute/pull/12465)) +- **fix(video-bridge):** preserve chronological video and transcript fusion, source-frame timestamps, transcript provenance, full contact-sheet intervals, and pre-abort callback isolation ([#11681](https://github.com/diegosouzapw/OmniRoute/pull/11681)) +- **Z.ai Web:** HTTP 200 streams carrying an upstream error now terminate with a structured failure instead of assistant text plus a normal stop, preserving partial output while allowing pre-content combo fallback. ([#12454](https://github.com/diegosouzapw/OmniRoute/pull/12454)) +- Absorb `Error [AbortError]: request_signal_aborted` and DOMException AbortError shapes in the process-level client-abort crash guard so routine client disconnects no longer kill the server (exit code 7). ([#12165](https://github.com/diegosouzapw/OmniRoute/pull/12165)) — thanks @alvinveroy +- **fix(sse):** Treat Adapta Web `type:error` SSE events as sanitized non-stream failures instead of empty HTTP 200 completions. ([#12459](https://github.com/diegosouzapw/OmniRoute/pull/12459)) +- **fix(autoCombo):** layer 3 (`models_dev_tier`) no longer scores retired models as premium, merge cross-provider capability rows deterministically, and inherit tier scores through variant ids ([#11508](https://github.com/diegosouzapw/OmniRoute/issues/11508)). `getModelsDevTierFitness` now vetoes ids present in `config/quality/model-lifecycle.json` with status `retired` before any other signal; `loadModelCapabilities()` aggregates per `model_id` (any-non-null-true for booleans, max for `limit_context`) instead of last-write-wins over SQLite's undefined row order; a capabilities miss retries through `resolveScoresAs()`'s base id and reports `models_dev_tier:inherited`, matching layers 1–2 (#11492). Adds `getModelsDevTierFitnessWithSource()` and an `invalidateCapabilitiesCache()` test/ops hook. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) — thanks @Meet6338-X +- fix(sse): accept Claude `content:[]` + `stop_reason: max_tokens`/`tool_use` in `detectMalformedNonStream` (match `isEmptyContentResponse`) so Claude Code `/model` probes with `max_tokens: 1` no longer become a false 502 ([#11568](https://github.com/diegosouzapw/OmniRoute/pull/11568)) — thanks @Seramicx +- **fix(security):** sanitize `request.failed` diagnostics before publishing them to live dashboard listeners and replay history, while keeping status, model, provider, latency, and internal call-log diagnostics intact. ([#12469](https://github.com/diegosouzapw/OmniRoute/pull/12469)) +- **fix(pwa):** do not intercept dashboard navigations so Chrome can fall back from a stale HTTP/3 Alt-Svc advertisement (UDP :20128 is unpublished; F5 on a long-lived tab hung until a new tab opened a fresh TCP connection). ([#12767](https://github.com/diegosouzapw/OmniRoute/pull/12767)) — thanks @HouMinXi +- **fix(executors):** handle DuckDuckGo ERR_BN_LIMIT (418) without retrying — when the upstream returns `418 ERR_BN_LIMIT` (rate-limit/ban), the executor now returns the error immediately instead of burning another VQD acquisition that would only count against the IP limit. The retry logic for `418 ERR_CHALLENGE` (unsolved challenge) remains unchanged. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) ([#12110](https://github.com/diegosouzapw/OmniRoute/pull/12110)) — thanks @oyi77 / @Meet6338-X +- Fixed DuckDuckGo streaming responses losing JSON lines and UTF-8 characters split across network chunks. ([#11528](https://github.com/diegosouzapw/OmniRoute/pull/11528)) — thanks @pacocartones +- Harden generated Cloudflare, Vercel, and Deno relays by stripping hop-by-hop, framing, and proxy authentication headers before forwarding requests upstream. ([#11533](https://github.com/diegosouzapw/OmniRoute/pull/11533)) — thanks @pacocartones +- **feat(grok-cli):** Provider Limits shows grok-cli banked reset credits from `GetRemainingResets` (including a real zero; a failed RPC omits the row) and the existing View credits button now calls `ConsumerUiSvc/RedeemReset` for grok-cli. Live tokens use nested fields 10/20/30 (id + Timestamp), not compact 1/2/3. ([#12805](https://github.com/diegosouzapw/OmniRoute/pull/12805)) — thanks @HouMinXi +- **fix(memory):** Embedding Model Quick select, Embedding Source remote dropdown, and Rerank selector now list every configured provider with embedding/rerank support instead of only chat-catalog text matches plus OpenRouter live discovery; a generic OpenAI-compatible `/embeddings` + Cohere-compatible `/rerank` runtime fallback resolves any configured chat provider's embedding/rerank endpoint, so unlisted providers no longer fail with "Unknown embedding provider"; both memory selectors gained a free-text model override ([#11390](https://github.com/diegosouzapw/OmniRoute/pull/11390)) — thanks @rqzbeh +- Harden SQLite upgrades around the historical migration-074 version collision: missing discovery and inspector tables are replayed atomically, pre-existing databases (including setup-created skeletons) receive reusable content-addressed safety snapshots, and Node test/eval probes without `DATA_DIR` are isolated from the operator database. ([#12435](https://github.com/diegosouzapw/OmniRoute/pull/12435)) +- **fix(dashboard):** Moonshot/Kimi Open Platform voucher and cash leftover percentages follow the bucket balance, so an empty wallet no longer paints those rows as 100% while Available is 0% ([#12733](https://github.com/diegosouzapw/OmniRoute/pull/12733)) — thanks @HouMinXi +- **fix(combo):** return non-retryable HTTP 400 when all candidates for a pinned native Codex turn are unavailable due to model-scoped lockout, terminating the turn cleanly while preserving turn continuity and enabling standard Combo routing on subsequent turns ([#12240](https://github.com/diegosouzapw/OmniRoute/pull/12240)) — thanks @mdigitalbh81 +- Fixed auto-router strategies preserving the selected connection through ranking and dispatch when multiple connections share the same provider and model. ([#11530](https://github.com/diegosouzapw/OmniRoute/pull/11530)) — thanks @pacocartones +- Preserve Chat Completions JSON-object response formats when translating requests to the Responses API. ([#11529](https://github.com/diegosouzapw/OmniRoute/pull/11529)) — thanks @pacocartones +- Fix idempotency fingerprints so Responses API requests include `input` and other semantic generation fields using stable serialization, while excluding credentials and request metadata. ([#11532](https://github.com/diegosouzapw/OmniRoute/pull/11532)) — thanks @pacocartones +- **fix(api):** Generated API CLI commands now enforce required OpenAPI request bodies; Combo test commands forward the required `comboName` body, while API keys created by older writers after migration 149 preserve legacy allow-all Combo access without widening explicit empty allowlists — thanks @marcelokarval ([#12070](https://github.com/diegosouzapw/OmniRoute/pull/12070)) +- **fix(grok-web):** treat upstream streaming failures as failures instead of successful assistant text: error-only streams now fail readiness with HTTP 502, while failures after legitimate content preserve that partial output and terminate through the sanitized stream failure path without a normal `stop` completion. ([#12458](https://github.com/diegosouzapw/OmniRoute/pull/12458)) +- HuggingChat now turns HTTP 200 JSONL generation failures into a sanitized 502 before content, or a fixed public stream failure after partial output, so fallback and request persistence no longer record a false successful stop. ([#12456](https://github.com/diegosouzapw/OmniRoute/pull/12456)) +- **fix(providers):** keep 1min.ai HTTP 200 stream errors out of assistant content, preserve partial output, and expose sanitized terminal errors so pre-content failures can fall back. ([#12466](https://github.com/diegosouzapw/OmniRoute/pull/12466)) +- **fix(sessions):** preserve legacy Sessions data when the additive exclusive-session projection is unavailable, returning an empty projection and warning only once per contiguous outage instead of failing the endpoint; the Sessions badge intentionally reflects the merged legacy and exclusive row count introduced by [#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389) ([#11469](https://github.com/diegosouzapw/OmniRoute/pull/11469)) — thanks @KaspaPulse +- Render Nimble Search with the generic provider icon and serve Opper's proven logo locally. ([#12415](https://github.com/diegosouzapw/OmniRoute/pull/12415)) +- **fix(quality):** `config/quality/provider-assets-provenance.jsonl` was stale against the physical `public/providers/` tree (still listing 225 assets and 5 duplicate-content groups after several provider-retirement PRs removed 85 icon files), which would have made `check:provider-asset-provenance` fail on the next full run. Reconciled the manifest to the real 142-file snapshot (142 asset records, 1 duplicate-content group, 2 newly-registered files marked `unresolved` pending provenance research), repinned `auditedCommit`, and updated the regression test's hardcoded counts to match. ([#11876](https://github.com/diegosouzapw/OmniRoute/pull/11876)) +- fix(ui): wrap quota card footer action buttons to prevent clipping Refresh button on narrow cards — when extra action buttons such as `View credits` are present on Codex cards, allow the footer row to flex-wrap with `shrink-0` buttons so `Refresh now` remains visible and clickable across all responsive card widths ([#11464](https://github.com/diegosouzapw/OmniRoute/pull/11464)) — thanks @adevwithpurpose +- Keep Antigravity Gemini usable when the same connection's Claude weekly quota is empty; generic quota cache stays per-connection for every other provider. ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) — thanks @HouMinXi +- Fixed Tailscale tunnel setup to reject ports outside the valid 1-65535 range and safely install Windows MSI packages from paths containing apostrophes. ([#11534](https://github.com/diegosouzapw/OmniRoute/pull/11534)) — thanks @pacocartones +- Classify Cloudflared and Tailscale process-management routes as local-only and spawn-capable while preserving remote access to read-only tunnel status endpoints. ([#11531](https://github.com/diegosouzapw/OmniRoute/pull/11531)) — thanks @pacocartones +- Electron release workflow: the `publish-npm` job now grants `actions: read` to the reusable `npm-publish.yml` it calls (its `publish` job requests it), which is what made GitHub refuse the whole v3.8.50 run at startup and ship the release with zero desktop assets; a `workflow_dispatch` now builds the requested tag instead of the dispatching branch and can skip the npm leg (`publish_npm=false`) when only re-attaching assets ([#11974](https://github.com/diegosouzapw/OmniRoute/pull/11974)) +- **fix(dashboard):** The Combos page usage guide now reads its dismissal through `useSyncExternalStore` instead of correcting SSR state inside an effect, removing an extra commit of the page tree on every load (and the `react-hooks/set-state-in-effect` error it raised). ([#12671](https://github.com/diegosouzapw/OmniRoute/pull/12671)) +- Electron release: `electron/package-lock.json` regained the optional `electron-builder-squirrel-windows` subtree (13 entries) that `npm ci` had been refusing as out of sync, `electron-release.yml` gained a `build_ref` dispatch input and stops regenerating release notes on a re-attach dispatch, and the npm publish workflow attaches the SBOM to the GitHub Release on dispatch publishes too — so the v3.8.51 tag ships every desktop asset and the SBOM like v3.8.49 did ([#12022](https://github.com/diegosouzapw/OmniRoute/pull/12022)) +- Drained the reds every PR against `release/v3.8.51` was born with: documented the three service-worker build-id variables, registered the six retirement/tunnel tests with the mutation gate, approved `eslint-plugin-react-hooks` in the dependency allowlist, added the six `combo.sort.*` strings to `vi` and `pt-BR`, pointed the ChatGPT Web doc at the real migration-168 test, worded the g4f hint around the member key, and realigned four tests to the retired-provider catalog and the legacy-schema fixtures the retirement migrations touch. ([#11940](https://github.com/diegosouzapw/OmniRoute/pull/11940)) +- **fix(providers):** Claude, Grok, LMArena, Notion, and Perplexity web-cookie transports now use pooled `wreq-js` 3.2 instead of the native sidecar, with all nine supported bindings pinned and audited, and the applicable platform binding plus native-license evidence included in each release artifact ([#12429](https://github.com/diegosouzapw/OmniRoute/pull/12429), supersedes [#11753](https://github.com/diegosouzapw/OmniRoute/pull/11753)). +- **fix(providers):** Zed Hosted streaming failures now trigger fallback before content and end partial streams with a sanitized structured error instead of fake assistant text and a normal-success stop. ([#12455](https://github.com/diegosouzapw/OmniRoute/pull/12455)) +- **fix(test):** capture hook result after render (direct commit `b85d0abb0b`) +- **fix(sync):** repair the two regressions the v3.8.50 sync-back left on release/v3.8.51 (direct commit `5ade9e0851`) +- **revert(models):** drop the memo reset hook ported from #10055 (direct commit `282de841ae`) +- **fix(typecheck):** guard the optional connectionId before the reactive model sync (direct commit `f2825bad75`) +- **fix(quota-share):** release the winner's reserved in-flight slot (#11371) ([#11408](https://github.com/diegosouzapw/OmniRoute/pull/11408)) — thanks @oyi77 +- **fix(providers):** stop silently dropping reasoning effort on opencode families (#10788) ([#11409](https://github.com/diegosouzapw/OmniRoute/pull/11409)) — thanks @oyi77 +- **fix(combo):** enable genuine quota-aware routing for generic providers (antigravity, claude, etc.) ([#11411](https://github.com/diegosouzapw/OmniRoute/pull/11411)) — thanks @HouMinXi +- **fix(oauth):** allow expired token retry before deactivation and activate expired retry loop (#P0) ([#11414](https://github.com/diegosouzapw/OmniRoute/pull/11414)) — thanks @HouMinXi +- **perf(executors):** lazy-load the executor registry — defer class imports + construction to first use (#11220) ([#11421](https://github.com/diegosouzapw/OmniRoute/pull/11421)) — thanks @oyi77 +- **fix:** make release/v3.8.50 CI gates green (run 32786966560 root causes) ([#11450](https://github.com/diegosouzapw/OmniRoute/pull/11450)) — thanks @jonlwheat2-gif +- **fix(sse):** exact-domain cookie match and origin-equality URL assertions ([#11452](https://github.com/diegosouzapw/OmniRoute/pull/11452)) — thanks @hartmark +- **fix(providers):** send Responses API payload during OpenAI-compatible validation ([#11454](https://github.com/diegosouzapw/OmniRoute/pull/11454)) — thanks @tuandinh0801 +- **fix(command-code):** fallback to /alpha/generate for Go plan without Provider API access ([#11455](https://github.com/diegosouzapw/OmniRoute/pull/11455)) — thanks @Chewji9875 +- **fix(responses-continuation):** fail closed on a log-truncated stored input/output array ([#11473](https://github.com/diegosouzapw/OmniRoute/pull/11473)) — thanks @hartmark +- **fix(cli):** write jcode settings as [providers.omniroute] in config.toml ([#11484](https://github.com/diegosouzapw/OmniRoute/pull/11484)) — thanks @hongnoul +- **fix(logging):** raise chat-log array truncation cap to 1000 and unify duplicate implementations ([#11499](https://github.com/diegosouzapw/OmniRoute/pull/11499)) — thanks @hartmark +- **fix:** restore live websocket URL helpers ([#11509](https://github.com/diegosouzapw/OmniRoute/pull/11509)) — thanks @PauloFH +- **perf(core):** eliminate async waterfalls across home, cache, and db operations (#11396) ([#11512](https://github.com/diegosouzapw/OmniRoute/pull/11512)) — thanks @HouMinXi +- **fix(kiro):** add runtime.us-east-1.kiro.dev as first-attempt endpoint ([#11517](https://github.com/diegosouzapw/OmniRoute/pull/11517)) — thanks @rifqiawl +- **fix(providers):** remove duplicate 128-tool truncation in OpencodeExecutor (#11444) ([#11518](https://github.com/diegosouzapw/OmniRoute/pull/11518)) — thanks @HouMinXi +- **fix(providers):** poll onboardUser LRO before classifying BYOP ([#11519](https://github.com/diegosouzapw/OmniRoute/pull/11519)) — thanks @rifqiawl +- **fix(translator):** preserve exact tool name casing for Claude tool_use payloads (#11487) ([#11520](https://github.com/diegosouzapw/OmniRoute/pull/11520)) — thanks @HouMinXi +- **fix(kimi-web):** align health probe with executor domain ([#11521](https://github.com/diegosouzapw/OmniRoute/pull/11521)) — thanks @pacocartones +- **fix(cli):** preserve existing admin password during setup ([#11522](https://github.com/diegosouzapw/OmniRoute/pull/11522)) — thanks @pacocartones +- **fix(mitm):** keep Windows DNS elevation off the POSIX sudo path (#11430) ([#11542](https://github.com/diegosouzapw/OmniRoute/pull/11542)) — thanks @official-burak +- **fix(sse):** scale chat admission by ingest byte budget instead of a fixed request count ([#11548](https://github.com/diegosouzapw/OmniRoute/pull/11548)) — thanks @TheDemonTuan +- **fix:** accept Gemini Web cookie exports ([#11554](https://github.com/diegosouzapw/OmniRoute/pull/11554)) — thanks @rafacpti23 +- **fix(i18n):** complete Brazilian Portuguese translations ([#11558](https://github.com/diegosouzapw/OmniRoute/pull/11558)) — thanks @rafacpti23 +- **fix(sse):** explicit types for openai-responses pureHelpers — clears last failing core typecheck gate ([#11567](https://github.com/diegosouzapw/OmniRoute/pull/11567)) — thanks @jonlwheat2-gif +- **fix(opencode):** hydrate Proxy Pool references for no-auth connections ([#11584](https://github.com/diegosouzapw/OmniRoute/pull/11584)) — thanks @AStupidBear +- **fix(ci):** resolve inherited release/v3.8.51 base-red CI failures ([#11608](https://github.com/diegosouzapw/OmniRoute/pull/11608)) — thanks @jonlwheat2-gif +- **fix(db):** fallback load STORAGE_ENCRYPTION_KEY from env files ([#11614](https://github.com/diegosouzapw/OmniRoute/pull/11614)) — thanks @benzntech +- **fix(providers):** mark Brave Search as no longer having a free tier ([#11621](https://github.com/diegosouzapw/OmniRoute/pull/11621)) — thanks @turbolego +- **fix:** pass tool result content through as string in OpenAI/Claude -> Gemini translation ([#11624](https://github.com/diegosouzapw/OmniRoute/pull/11624)) — thanks @Hsia97 +- **fix(sse):** scope credential health to exact key ([#11628](https://github.com/diegosouzapw/OmniRoute/pull/11628)) — thanks @KaspaPulse +- **fix(install):** guard prepare script against missing husky ([#11634](https://github.com/diegosouzapw/OmniRoute/pull/11634)) — thanks @Prajeeth-12 +- **fix(security):** redact NVIDIA_API_KEY at the log sink in the ad-hoc diag script ([#11639](https://github.com/diegosouzapw/OmniRoute/pull/11639)) +- **fix(types):** clear the two type regressions red on release/v3.8.51 ([#11640](https://github.com/diegosouzapw/OmniRoute/pull/11640)) — thanks @jonlwheat2-gif +- **fix(build):** resolve workspace: protocol dependencies to concrete semver versions during prepublish (#11540) ([#11641](https://github.com/diegosouzapw/OmniRoute/pull/11641)) — thanks @HouMinXi +- **fix(search):** properly resolve configured search connection in /v1/responses pipeline (#11524) ([#11642](https://github.com/diegosouzapw/OmniRoute/pull/11642)) — thanks @HouMinXi +- **fix(authz):** trust cf-connecting-ip for via-proxy detection when peer is Cloudflare edge (#11514) ([#11643](https://github.com/diegosouzapw/OmniRoute/pull/11643)) — thanks @HouMinXi +- **fix(security):** compare the OIDC state cookie in constant time ([#11646](https://github.com/diegosouzapw/OmniRoute/pull/11646)) +- **fix(security):** namespace the dedup hash by the calling API key ([#11649](https://github.com/diegosouzapw/OmniRoute/pull/11649)) +- **fix(models):** gate the Codex-native catalog loop on prefix mode (#11632) ([#11662](https://github.com/diegosouzapw/OmniRoute/pull/11662)) — thanks @xiaoyaner0201 +- **fix(dashboard):** project Codex routing quota in account pools ([#11666](https://github.com/diegosouzapw/OmniRoute/pull/11666)) — thanks @KaspaPulse +- **fix(ddgw):** normaliza system no upstream da release v3.8.51 ([#11692](https://github.com/diegosouzapw/OmniRoute/pull/11692)) — thanks @RhianB14 +- **fix(memory):** honor category filter in GET /api/memory ([#11699](https://github.com/diegosouzapw/OmniRoute/pull/11699)) — thanks @Sabeekhann +- **fix(ci):** detect stale workspace lockfile entries ([#11714](https://github.com/diegosouzapw/OmniRoute/pull/11714)) — thanks @pacocartones +- **fix(ci):** stamp dist/BUILD_SHA before the npm publish provenance gate ([#11721](https://github.com/diegosouzapw/OmniRoute/pull/11721)) +- **fix(docker):** decouple the best-effort Bun image from the release manifest ([#11724](https://github.com/diegosouzapw/OmniRoute/pull/11724)) +- **perf(compression):** avoid cloning memo misses twice ([#11727](https://github.com/diegosouzapw/OmniRoute/pull/11727)) — thanks @RaviTharuma +- **fix(docker):** let the bun digest artifact be absent, not fatal ([#11740](https://github.com/diegosouzapw/OmniRoute/pull/11740)) +- **fix(shared):** block cloud-metadata hosts under default remote-image guard ([#11755](https://github.com/diegosouzapw/OmniRoute/pull/11755)) — thanks @santosraju99-hub +- **fix(api):** preserve caller-provided X-Correlation-Id on chat completions ([#11760](https://github.com/diegosouzapw/OmniRoute/pull/11760)) — thanks @aniruddhaadak80 +- **fix(resilience):** lock permanently retired models instead of short backoff (Gemini ban prevention) ([#11762](https://github.com/diegosouzapw/OmniRoute/pull/11762)) — thanks @turbolego +- **fix:** mark Vercel AI Gateway as passthroughModels ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **fix(resilience):** stop hammering permanently-moved endpoints and billing-suspended accounts ([#11774](https://github.com/diegosouzapw/OmniRoute/pull/11774)) — thanks @turbolego +- **fix(lease):** remove global static reservation and gate routing on live active lease occupancy ([#11775](https://github.com/diegosouzapw/OmniRoute/pull/11775)) — thanks @TheDemonTuan +- **fix(ci):** size the install-upgrade gate to a measured run, and log the pack cost ([#11776](https://github.com/diegosouzapw/OmniRoute/pull/11776)) +- **fix(pwa):** stop serving the stale shell after deploys ([#11779](https://github.com/diegosouzapw/OmniRoute/pull/11779)) — thanks @HouMinXi +- **fix(combo):** lock GitHub models rejected as "not supported" for future requests ([#11781](https://github.com/diegosouzapw/OmniRoute/pull/11781)) — thanks @turbolego +- **fix(cli):** probe both IPv4 and IPv6 loopback for server readiness (#11766) ([#11794](https://github.com/diegosouzapw/OmniRoute/pull/11794)) — thanks @vermasomesh835 +- **fix(sse):** preserve CLIProxyAPI wrappers per connection ([#11797](https://github.com/diegosouzapw/OmniRoute/pull/11797)) — thanks @RaviTharuma +- **fix(providers):** honor model auto-fetch opt-in on create ([#11798](https://github.com/diegosouzapw/OmniRoute/pull/11798)) — thanks @RaviTharuma +- **fix(dashboard):** make model auto-fetch order independent ([#11805](https://github.com/diegosouzapw/OmniRoute/pull/11805)) — thanks @RaviTharuma +- **fix(docker):** make GHA cache exports best effort ([#11806](https://github.com/diegosouzapw/OmniRoute/pull/11806)) — thanks @RaviTharuma +- **fix(kiro):** do not permanently ban on 'User is not authorized to make this call' ([#11809](https://github.com/diegosouzapw/OmniRoute/pull/11809)) — thanks @Deftera186 +- **fix(sse):** keep cache-write tokens in OpenAI-shaped usage ([#11814](https://github.com/diegosouzapw/OmniRoute/pull/11814)) — thanks @TheDemonTuan +- **fix(docs):** resolve relative markdown and wiki links across Fumadocs and GitHub wiki ([#11834](https://github.com/diegosouzapw/OmniRoute/pull/11834)) — thanks @AndrianBalanescu +- **fix(chat):** preserve unstripped model string for passthrough provider routing ([#11840](https://github.com/diegosouzapw/OmniRoute/pull/11840)) — thanks @raheemuddin786 +- **fix(mcp):** avoid async initialization inside sync esm wrappers (#11569) ([#11849](https://github.com/diegosouzapw/OmniRoute/pull/11849)) — thanks @HouMinXi +- **fix(sse):** preserve coherent markdown token boundaries across stream translation chunks (#11606) ([#11851](https://github.com/diegosouzapw/OmniRoute/pull/11851)) — thanks @HouMinXi +- **fix(ci):** run the install-upgrade gate on disk, not on the /tmp tmpfs ([#11855](https://github.com/diegosouzapw/OmniRoute/pull/11855)) +- **fix(skills):** expand shorthand property types in injected tool schemas ([#11857](https://github.com/diegosouzapw/OmniRoute/pull/11857)) — thanks @alvinveroy +- **fix(ci):** publish npm from a hosted runner so provenance is accepted ([#11877](https://github.com/diegosouzapw/OmniRoute/pull/11877)) +- **fix(lint):** pin react hooks plugin to 7.0.1 ([#11886](https://github.com/diegosouzapw/OmniRoute/pull/11886)) +- **fix(ci):** surface and clear release-green ESLint failure ([#11890](https://github.com/diegosouzapw/OmniRoute/pull/11890)) — thanks @MumuTW +- **fix(guardrails):** prevent duplicate prompt-injection-guard log output ([#11936](https://github.com/diegosouzapw/OmniRoute/pull/11936)) — thanks @patrykkopycinski +- **fix(auth):** downgrade expected transient states from warn to debug ([#11937](https://github.com/diegosouzapw/OmniRoute/pull/11937)) — thanks @patrykkopycinski +- **fix(codex):** fail over image generation for imported free plans ([#11948](https://github.com/diegosouzapw/OmniRoute/pull/11948)) — thanks @Bl0ck154 +- **fix(codex):** normalize non-stream responses ([#11951](https://github.com/diegosouzapw/OmniRoute/pull/11951)) — thanks @Bl0ck154 +- **fix(images):** forward Antigravity image size ([#11952](https://github.com/diegosouzapw/OmniRoute/pull/11952)) — thanks @Bl0ck154 +- **fix(db):** invalidate connection cache after upsert ([#11953](https://github.com/diegosouzapw/OmniRoute/pull/11953)) — thanks @Bl0ck154 +- **fix(codex):** restore imported account state ([#11954](https://github.com/diegosouzapw/OmniRoute/pull/11954)) — thanks @Bl0ck154 +- **fix(sse):** give extended-thinking targets the reasoning readiness budget ([#11959](https://github.com/diegosouzapw/OmniRoute/pull/11959)) — thanks @abhisheksharma2411 +- **fix(dashboard):** use opaque background and readable text color on cost chart tooltips ([#11960](https://github.com/diegosouzapw/OmniRoute/pull/11960)) — thanks @ZaimMarzuki +- **fix(antigravity):** send complete loadCodeAssist metadata (ideType/platform/pluginType as numeric enums) ([#11969](https://github.com/diegosouzapw/OmniRoute/pull/11969)) — thanks @rifqiawl +- **fix(core):** resolve DB init race condition and reasoning translation ([#12003](https://github.com/diegosouzapw/OmniRoute/pull/12003)) — thanks @raheemuddin786 +- **fix(admission):** exclude reclaimable page cache from the cgroup pressure ratio ([#12017](https://github.com/diegosouzapw/OmniRoute/pull/12017)) — thanks @HouMinXi +- **fix(build):** prune non-production trees in NFT trace excludes and tsconfig ([#12028](https://github.com/diegosouzapw/OmniRoute/pull/12028)) — thanks @Chewji9875 +- **fix(migrations):** add renamed migration compatibility for 056/073/077/101 ([#12036](https://github.com/diegosouzapw/OmniRoute/pull/12036)) — thanks @oyi77 +- **fix(sse):** default crash-guard logger to console.warn, not console ([#12042](https://github.com/diegosouzapw/OmniRoute/pull/12042)) — thanks @HouMinXi +- **fix(catalog):** derive vision/modalities for built-in auto combos from effective target pool ([#12046](https://github.com/diegosouzapw/OmniRoute/pull/12046)) — thanks @Prajeeth-12 +- **fix(release):** the packaged-app smoke verifies the database opened, not a driver line the primary path never prints (twin of #12032) ([#12047](https://github.com/diegosouzapw/OmniRoute/pull/12047)) +- **fix(barrel):** migrate src/app/ off the @/lib/localDb barrel import (#11795 Phase 2) ([#12051](https://github.com/diegosouzapw/OmniRoute/pull/12051)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate src/lib/ off the localDb barrel to direct db imports (#59) ([#12052](https://github.com/diegosouzapw/OmniRoute/pull/12052)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate open-sse, src/shared, src/sse, src/models, src/domain off the @/lib/localDb barrel import (#11795 Phase 4) ([#12053](https://github.com/diegosouzapw/OmniRoute/pull/12053)) — thanks @jonlwheat2-gif +- **fix(barrel):** delete the @/lib/localDb barrel — every consumer migrated (#11795 Phase 5) ([#12055](https://github.com/diegosouzapw/OmniRoute/pull/12055)) — thanks @jonlwheat2-gif +- **fix(radar):** restore D12 public boundary ([#12057](https://github.com/diegosouzapw/OmniRoute/pull/12057)) +- **fix(dev):** bound webpack and Tailwind scans ([#12075](https://github.com/diegosouzapw/OmniRoute/pull/12075)) — thanks @backryun +- **fix(dev):** isolate root layout settings reads ([#12076](https://github.com/diegosouzapw/OmniRoute/pull/12076)) — thanks @backryun +- **fix(dev):** reduce instrumentation executor fan-out (phase 3) ([#12078](https://github.com/diegosouzapw/OmniRoute/pull/12078)) — thanks @backryun +- **fix(dev):** make logging resources HMR-singleton ([#12079](https://github.com/diegosouzapw/OmniRoute/pull/12079)) — thanks @backryun +- **fix(sse):** keep unavailable forced connections scoped ([#12080](https://github.com/diegosouzapw/OmniRoute/pull/12080)) — thanks @keeltrace +- **fix(dev):** isolate batch dispatch from instrumentation ([#12081](https://github.com/diegosouzapw/OmniRoute/pull/12081)) — thanks @backryun +- **fix(ci):** reconcile release test contract drift ([#12082](https://github.com/diegosouzapw/OmniRoute/pull/12082)) — thanks @backryun +- **fix(ci):** accept CVE-2025-68121 in the prebuilt tls-client .so, auto-close base-red issues, guard Scorecard on the default branch ([#12085](https://github.com/diegosouzapw/OmniRoute/pull/12085)) +- **fix(db):** drop three consumer-less 1proxy exports — dead-code base-red on release/v3.8.51 after the barrel deletion (#12055) ([#12087](https://github.com/diegosouzapw/OmniRoute/pull/12087)) +- **fix(api):** clear the six API-route TypeScript regressions the new gate landed red on ([#12094](https://github.com/diegosouzapw/OmniRoute/pull/12094)) +- **fix(release):** never let the tag-push Create Release append auto notes to the curated body ([#12096](https://github.com/diegosouzapw/OmniRoute/pull/12096)) +- **fix(sse):** honor CLIProxyAPI environment API key ([#12099](https://github.com/diegosouzapw/OmniRoute/pull/12099)) — thanks @RaviTharuma +- **fix(oauth):** bind Google refresh to the client that issued the token ([#12106](https://github.com/diegosouzapw/OmniRoute/pull/12106)) — thanks @HouMinXi +- **fix(dashboard):** make RequestLoggerDetail loadable outside Next — CSS via globals.css + CJS/ESM interop (#11703 base-reds) ([#12114](https://github.com/diegosouzapw/OmniRoute/pull/12114)) +- **fix(leases):** project status lease row to lease columns so joined connection PII never escapes ([#12115](https://github.com/diegosouzapw/OmniRoute/pull/12115)) — thanks @geek007git +- **fix(plugins):** do not kill the plugin process when a fire-and-forget hook times out ([#12116](https://github.com/diegosouzapw/OmniRoute/pull/12116)) — thanks @geek007git +- **fix(guardrails):** restore injection-guard logging on middleware-only routes ([#12117](https://github.com/diegosouzapw/OmniRoute/pull/12117)) — thanks @geek007git +- **fix(plugins):** refresh stored manifest from disk on activate so new hook fields reach existing installs ([#12120](https://github.com/diegosouzapw/OmniRoute/pull/12120)) — thanks @geek007git +- **fix(codex):** preserve existing provider state when bulk-import upserts a matching connection ([#12122](https://github.com/diegosouzapw/OmniRoute/pull/12122)) — thanks @geek007git +- **fix(ci):** clear the two base-reds the 2026-08-30 merge batch left on release/v3.8.51 (round 3) ([#12123](https://github.com/diegosouzapw/OmniRoute/pull/12123)) +- **fix(sse):** strip type:'custom' from Claude tools on agentrouter dispatch ([#12126](https://github.com/diegosouzapw/OmniRoute/pull/12126)) — thanks @wahidsadik371-coder +- **fix(sse):** stop advertising video providers the dispatcher cannot run ([#12131](https://github.com/diegosouzapw/OmniRoute/pull/12131)) — thanks @quiterunner-commits +- **fix(search):** treat HTTP 432 and plan limit errors as transient cooldown ([#12139](https://github.com/diegosouzapw/OmniRoute/pull/12139)) — thanks @HouMinXi +- **fix(sse):** refuse an AI Horde queue that cannot fit the request budget ([#12143](https://github.com/diegosouzapw/OmniRoute/pull/12143)) — thanks @quiterunner-commits +- **fix(ci):** clear the base-reds the afternoon merge batch left on release/v3.8.51 (round 5: provider count 352, TS2554/TS2677) ([#12144](https://github.com/diegosouzapw/OmniRoute/pull/12144)) +- **fix(translator):** strip neutral tool_choice when tools absent in Responses-to-Chat (#12141) ([#12166](https://github.com/diegosouzapw/OmniRoute/pull/12166)) — thanks @HouMinXi +- **fix(guardrails):** pass providerId to getResolvedModelCapabilities in checkComboVision (#12112) ([#12169](https://github.com/diegosouzapw/OmniRoute/pull/12169)) — thanks @HouMinXi +- **fix(translator):** strip plaintext reasoning content for opaque responses backends (#12128) ([#12171](https://github.com/diegosouzapw/OmniRoute/pull/12171)) — thanks @HouMinXi +- **perf(stream):** compile hot-path regexes once, bound token caches, fix quadratic buffering ([#12179](https://github.com/diegosouzapw/OmniRoute/pull/12179)) — thanks @opensource-elearning +- **fix:** resolve compression worker file using runtime anchors instead of… ([#12183](https://github.com/diegosouzapw/OmniRoute/pull/12183)) — thanks @NightStalker-87 +- **fix:** prevent Claude Code session kills via liveness-aware readiness + auto model echo ([#12189](https://github.com/diegosouzapw/OmniRoute/pull/12189)) — thanks @opensource-elearning +- **fix(translator):** preserve falsy primitive values in Gemini and Antigravity function response results ([#12191](https://github.com/diegosouzapw/OmniRoute/pull/12191)) — thanks @Siva010 +- **fix(oauth):** mark empty Antigravity projectId as degraded and clear stale errors (#11284) ([#12205](https://github.com/diegosouzapw/OmniRoute/pull/12205)) — thanks @HouMinXi +- **fix(providers):** gate the Codex auto-ping usage read on the shared quota throttle ([#12209](https://github.com/diegosouzapw/OmniRoute/pull/12209)) — thanks @abhisheksharma2411 +- **fix(combos):** prioritize SQLite row id over inner JSON id and notify delete errors ([#12213](https://github.com/diegosouzapw/OmniRoute/pull/12213)) — thanks @HouMinXi +- **fix(providers):** mark featherless.ai as no longer having a free tier (`hasFree: false`) ([#12216](https://github.com/diegosouzapw/OmniRoute/pull/12216)) — thanks @turbolego +- **fix(deepseek-web):** stop Turbopack dev panic in the PoW worker path resolver ([#12221](https://github.com/diegosouzapw/OmniRoute/pull/12221)) — thanks @hartmark +- **fix(chat-admission):** clarify local 503 source ([#12223](https://github.com/diegosouzapw/OmniRoute/pull/12223)) — thanks @keeltrace +- **fix(dev):** silence webpack runtime module warnings ([#12228](https://github.com/diegosouzapw/OmniRoute/pull/12228)) — thanks @backryun +- **fix(db):** use module.require for CommonJS runtime driver loading ([#12230](https://github.com/diegosouzapw/OmniRoute/pull/12230)) — thanks @raheemuddin786 +- **fix(memory):** honest probe-driven FTS5 keyword status + memory_id rowid sync ([#12231](https://github.com/diegosouzapw/OmniRoute/pull/12231)) — thanks @raheemuddin786 +- **fix(system):** propagate abort signal to stream reader in HTTP version checks ([#12232](https://github.com/diegosouzapw/OmniRoute/pull/12232)) — thanks @raheemuddin786 +- **fix(resilience):** isolate local host execution errors from provider circuit breakers ([#12233](https://github.com/diegosouzapw/OmniRoute/pull/12233)) — thanks @raheemuddin786 +- **fix(sse):** sort injected tools deterministically for prompt caching ([#12234](https://github.com/diegosouzapw/OmniRoute/pull/12234)) — thanks @raheemuddin786 +- **fix(combo):** always clear the loop-safety timer, not just on the happy path (#11804) ([#12245](https://github.com/diegosouzapw/OmniRoute/pull/12245)) +- **fix(quality):** base-red pair — stryker tap registration + turn-pin suites aligned to the window gate ([#12255](https://github.com/diegosouzapw/OmniRoute/pull/12255)) — thanks @backryun +- **fix(dev):** qualify Turbopack runtime boundaries ([#12258](https://github.com/diegosouzapw/OmniRoute/pull/12258)) — thanks @backryun +- **fix(combo):** fall back to full pool when collapsed sole survivor is context-too-small ([#12278](https://github.com/diegosouzapw/OmniRoute/pull/12278)) — thanks @opensource-elearning +- **fix(combo):** bound the pre-dispatch unavailable skip so a stale label cannot dark a pool (#12168) ([#12285](https://github.com/diegosouzapw/OmniRoute/pull/12285)) +- **fix(security):** strip Qwen/Alibaba console-session cookies from provider API responses ([#12287](https://github.com/diegosouzapw/OmniRoute/pull/12287)) +- **fix(usage):** console-aware Token Plan guidance + subscription hint on bailian 401 ([#12288](https://github.com/diegosouzapw/OmniRoute/pull/12288)) +- **fix(resourcePressure):** log numeric detail on every rejection, recover faster ([#12293](https://github.com/diegosouzapw/OmniRoute/pull/12293)) — thanks @hartmark +- **fix(release):** drain the 2026-09-01 base-red window — passthrough usage regression + radarPage i18n keys ([#12327](https://github.com/diegosouzapw/OmniRoute/pull/12327)) +- **fix(ci):** openapi-security-tiers checker must honor routeGuard patterns + imported prefixes ([#12350](https://github.com/diegosouzapw/OmniRoute/pull/12350)) — thanks @ggiak +- **perf(ci):** cache node_modules in the npm-ci-retry composite (#8084 D3) ([#12408](https://github.com/diegosouzapw/OmniRoute/pull/12408)) +- **revert(ui):** point CTAs back at their real destinations ([#12410](https://github.com/diegosouzapw/OmniRoute/pull/12410)) +- **fix(release):** drain the 2026-09-02 base-red — rerank-providers import + api-typecheck baseline ratchet ([#12414](https://github.com/diegosouzapw/OmniRoute/pull/12414)) +- **fix(types):** make system prompt injection noImplicitAny-safe ([#12416](https://github.com/diegosouzapw/OmniRoute/pull/12416)) +- **fix(quality):** validate typecheck baseline schema ([#12419](https://github.com/diegosouzapw/OmniRoute/pull/12419)) +- **fix(memory):** resolve rerank provider node cache import ([#12421](https://github.com/diegosouzapw/OmniRoute/pull/12421)) +- **fix(ci):** repair release-root regressions — pack dedup, web-session syntax, uc-image ids ([#12423](https://github.com/diegosouzapw/OmniRoute/pull/12423)) — thanks @backryun +- **fix(providers):** separate MaxAI and UC credential contracts ([#12431](https://github.com/diegosouzapw/OmniRoute/pull/12431)) +- **fix(cli):** preserve Claude settings on config set ([#12432](https://github.com/diegosouzapw/OmniRoute/pull/12432)) — thanks @KrzysiekSko +- **fix(providers):** repair the maxai credential block truncated by merge auto-resolve ([#12433](https://github.com/diegosouzapw/OmniRoute/pull/12433)) +- **fix(quality):** record the 2026-09-02 merged growth in the file-size baseline ([#12434](https://github.com/diegosouzapw/OmniRoute/pull/12434)) +- **fix(uc):** route UC error strings through sanitizeErrorMessage; allowlist the retired codex id ([#12437](https://github.com/diegosouzapw/OmniRoute/pull/12437)) +- **fix(adapta):** redact streamed upstream errors ([#12438](https://github.com/diegosouzapw/OmniRoute/pull/12438)) +- **fix(sse):** remap non-contiguous upstream tool_calls index to a gap-free output_index ([#12445](https://github.com/diegosouzapw/OmniRoute/pull/12445)) — thanks @hartmark +- **fix(quality):** detect duplicate tool_calls entries in one response ([#12446](https://github.com/diegosouzapw/OmniRoute/pull/12446)) — thanks @hartmark +- **fix(conversations):** resolve turn content OmniRoute never sends back to the client ([#12447](https://github.com/diegosouzapw/OmniRoute/pull/12447)) — thanks @hartmark +- **fix(combo):** do not treat credits-exhausted 401 as auth skip ([#12449](https://github.com/diegosouzapw/OmniRoute/pull/12449)) — thanks @RaviTharuma +- **fix(auth):** do not park healthy quota accounts as expired ([#12452](https://github.com/diegosouzapw/OmniRoute/pull/12452)) — thanks @RaviTharuma +- **fix(responses-continuation):** fail closed on a collector-truncated, empty output array ([#12460](https://github.com/diegosouzapw/OmniRoute/pull/12460)) — thanks @hartmark +- **fix(logging):** raise the SSE payload collector's default cap ([#12461](https://github.com/diegosouzapw/OmniRoute/pull/12461)) — thanks @hartmark +- **fix(resilience):** surface Responses failed.error.message in 502s ([#12472](https://github.com/diegosouzapw/OmniRoute/pull/12472)) — thanks @RaviTharuma +- **fix(security):** unbiased maxai X-Random nonce + stricter URL/regex assertions ([#12502](https://github.com/diegosouzapw/OmniRoute/pull/12502)) +- **fix(video):** re-anchor transcript log-redaction so PII/credential maskers can't reopen the leak (#12150 P1 follow-up) ([#12503](https://github.com/diegosouzapw/OmniRoute/pull/12503)) +- **fix(monitoring):** serve cached credentialHealth off the request path ([#12533](https://github.com/diegosouzapw/OmniRoute/pull/12533)) — thanks @RaviTharuma +- **fix(quota):** keep Antigravity Gemini usable when Claude weekly is empty ([#12566](https://github.com/diegosouzapw/OmniRoute/pull/12566)) — thanks @HouMinXi +- **fix(authz):** hard-gate every credential export and CLI-config write (GHSA-5926-2w35-7h4q) ([#12600](https://github.com/diegosouzapw/OmniRoute/pull/12600)) +- **fix(docs):** restore the Next build — REMOVED_PROVIDERS.md had no frontmatter (base-red #12581) ([#12610](https://github.com/diegosouzapw/OmniRoute/pull/12610)) +- **fix(quality):** re-point the zcodeProtocol public-creds allowlist to line 313 ([#12615](https://github.com/diegosouzapw/OmniRoute/pull/12615)) +- **fix(security):** close 3 advisories — search baseUrl exfil, sk- in the error sanitizer, bifrost relay header leak ([#12620](https://github.com/diegosouzapw/OmniRoute/pull/12620)) +- **fix(providers):** sanitize boolean required and nested bare maps for Gemini (#12269) ([#12624](https://github.com/diegosouzapw/OmniRoute/pull/12624)) — thanks @HouMinXi +- **fix(resilience):** keep Overloaded STREAM_EARLY_EOF off the provider breaker ([#12626](https://github.com/diegosouzapw/OmniRoute/pull/12626)) — thanks @HouMinXi +- **fix(catalog):** re-audit free-tier quotas against official pages ([#12649](https://github.com/diegosouzapw/OmniRoute/pull/12649)) +- **fix(dashboard):** keep a request's pending-tracking id stable across combo target retries ([#12650](https://github.com/diegosouzapw/OmniRoute/pull/12650)) — thanks @hartmark +- **fix(tests):** retire dead model ids from the chat-pipeline integration suite (base-red #12581) ([#12670](https://github.com/diegosouzapw/OmniRoute/pull/12670)) +- **fix:** resolve SqliteError no such table compression_run_telemetry during cleanup ([#12682](https://github.com/diegosouzapw/OmniRoute/pull/12682)) — thanks @groovecityJO +- **fix(combo):** treat a pin-only step as implicit connection allowlist ([#12697](https://github.com/diegosouzapw/OmniRoute/pull/12697)) — thanks @HouMinXi +- **fix(ci):** ship .npmrc in published package so legacy-peer-deps applies to consumers (#11544) ([#12699](https://github.com/diegosouzapw/OmniRoute/pull/12699)) — thanks @KooshaPari +- **fix(docker):** pin 4 CLI tools to exact versions (#12576) ([#12703](https://github.com/diegosouzapw/OmniRoute/pull/12703)) — thanks @KooshaPari +- **fix(docker):** document and harden cli profile trust boundary (#12570) ([#12706](https://github.com/diegosouzapw/OmniRoute/pull/12706)) — thanks @KooshaPari +- **fix(sse):** redact video transcript in pre-guardrail rejected-request logs (#12150 P2 item 7) ([#12710](https://github.com/diegosouzapw/OmniRoute/pull/12710)) +- **fix(i18n):** quote placeholder in OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES description (#12505) ([#12769](https://github.com/diegosouzapw/OmniRoute/pull/12769)) — thanks @KooshaPari +- **fix(api/pricing):** surface validation error message as string, not raw object (#12494) ([#12771](https://github.com/diegosouzapw/OmniRoute/pull/12771)) — thanks @KooshaPari +- **fix(sse):** re-enable prompt compression for native Codex passthrough ([#12834](https://github.com/diegosouzapw/OmniRoute/pull/12834)) — thanks @thomasmaerz +- **fix(dashboard):** model health tests for a provider node set to the Responses API now call `/v1/responses` with a Responses-shaped body instead of `/v1/chat/completions` — those models were reported as `Provider returned HTTP 200 but no text content` even though the same model answered normally through `/v1/responses` ([#13070](https://github.com/diegosouzapw/OmniRoute/issues/13070)) ([#13087](https://github.com/diegosouzapw/OmniRoute/pull/13087)) — thanks @ntdatt812 +- fix(routing): stop a round-robin combo's "opencode" targets from collapsing onto the opencode-zen connection (#11912) ([#13283](https://github.com/diegosouzapw/OmniRoute/pull/13283)) +- fix(routing): stop the reactive-compaction debug log from lying when compression is globally disabled (#11977) ([#13276](https://github.com/diegosouzapw/OmniRoute/pull/13276)) +- fix(electron): relativize standalone-bundle symlink targets so Stage 8 manifest verification stops failing on Windows (#11979) ([#13251](https://github.com/diegosouzapw/OmniRoute/pull/13251)) +- fix(dashboard): surface a visible error when Compression Studio's combined preview run fails (#12061) ([#13288](https://github.com/diegosouzapw/OmniRoute/pull/13288)) +- fix(dashboard): make the compression "Effective pipeline" preview honor the active profile and warn when the master switch is off (#12063) ([#13287](https://github.com/diegosouzapw/OmniRoute/pull/13287)) +- fix(providers): scope TinyCMS Web signer's DOM shims to each call instead of leaking them for the process lifetime, and surface a clean HTTP status on a non-JSON interception-toggles error (#12072) ([#13254](https://github.com/diegosouzapw/OmniRoute/pull/13254)) +- fix(guardrails): stop Vision Bridge from re-selecting a model locked after a 404 (#12111) ([#13259](https://github.com/diegosouzapw/OmniRoute/pull/13259)) +- fix(sse): require Responses-shaped body before native OpenAI-compatible passthrough (#12129) ([#13278](https://github.com/diegosouzapw/OmniRoute/pull/13278)) +- fix(providers): minimax-m3 now collapses manual thinking.type:"enabled" to adaptive, preventing upstream 400 (2013) (#12132) ([#13249](https://github.com/diegosouzapw/OmniRoute/pull/13249)) +- fix(db): scope model visibility overrides by modality so hiding a Chat model no longer hides an identically-ID'd Image/Embeddings/etc. model (#12172) ([#13258](https://github.com/diegosouzapw/OmniRoute/pull/13258)) +- fix(db): scope local-provider apiKey dedup to matching base URL so LM Studio/Ollama-style connections support multiple accounts (#12173) ([#13250](https://github.com/diegosouzapw/OmniRoute/pull/13250)) +- fix(providers): refresh Trae's stale Referer/Origin and forward user timezone so imported connections stop failing with 401 (#12190) ([#13255](https://github.com/diegosouzapw/OmniRoute/pull/13255)) +- fix(providers): route opencode-go/gpt-5.6-luna to /responses instead of /chat/completions (#12196) ([#13275](https://github.com/diegosouzapw/OmniRoute/pull/13275)) +- fix(dashboard): allow deleting the last extra-upstream-header row even when invalid (#12251) ([#13252](https://github.com/diegosouzapw/OmniRoute/pull/13252)) +- **fix(i18n):** translate pre-existing `__MISSING__:` keys for `combo.sort`, `requestLogger.detail` expand/collapse, `common.profile`, and `settings.resilienceCredentialHealth*` across 39 locales ([#12272](https://github.com/diegosouzapw/OmniRoute/issues/12272)) ([#13034](https://github.com/diegosouzapw/OmniRoute/pull/13034)) — thanks @HouMinXi +- fix(cli): run the Node.js runtime compatibility guard before the heavy `tsx/esm` + Commander import chain so an unsupported runtime gets a clear message instead of a raw `Invalid regular expression flags` crash (#12296) ([#13253](https://github.com/diegosouzapw/OmniRoute/pull/13253)) +- fix(dashboard): refresh the providers list after deleting a compatible provider node (#12298) ([#13256](https://github.com/diegosouzapw/OmniRoute/pull/13256)) +- fix(usage): fail closed on API-key budget enforcement when a provider's `auto` routing alias has no pricing row, instead of silently counting it as $0 (#12341) ([#13257](https://github.com/diegosouzapw/OmniRoute/pull/13257)) +- **fix(providers):** include the submitted Agnes video model when polling by `video_id` ([#12356](https://github.com/diegosouzapw/OmniRoute/pull/12356)) — thanks @pacocartones +- **fix(routing):** custom OpenAI-compatible nodes now honor the saved Chat/Responses API type after edits instead of letting the node's original ID prefix override the live connection setting ([#11884](https://github.com/diegosouzapw/OmniRoute/issues/11884)). ([#12358](https://github.com/diegosouzapw/OmniRoute/pull/12358)) — thanks @pacocartones +- fix(sse): surface an error instead of a silent empty 200 when a Claude stream closes with zero bytes (#12398) ([#13285](https://github.com/diegosouzapw/OmniRoute/pull/13285)) +- fix(oauth): warn before the dead localhost:8080 redirect in antigravity/gemini `oauth start` (#12413) ([#13265](https://github.com/diegosouzapw/OmniRoute/pull/13265)) +- fix(providers): stop devin-cli spawn error from double-closing the SSE controller (#12517) ([#13261](https://github.com/diegosouzapw/OmniRoute/pull/13261)) +- **fix(rerank):** clamp Voyage `top_k` to the documents actually sent after empty-string filtering, and honor `return_documents: false` in the NVIDIA response adapter (#12523 — thanks @pacocartones) +- **fix(audio):** `/v1/audio/translations` now resolves combo names the way `/v1/audio/transcriptions` already does, so a combo that `GET /v1/models` advertises is fanned out to its targets instead of being rejected with `400 Invalid translation model: . Use format: provider/model`; literal `provider/model` ids and unknown bare names behave as before (#12536 — thanks @pacocartones) +- **fix(gemini):** strip the JSON-Schema-2020-12 `prefixItems` keyword from Gemini tool schemas at every nesting level, so Claude Code tool definitions no longer fail with `400 Unknown name "prefixItems"` on Gemini models (#12540 — thanks @pacocartones) +- **fix(api-manager):** Expose an accessible loading status while API keys are fetched instead of an empty accessibility tree (#12541 — thanks @pacocartones) +- **fix(video):** Clamp `estimateJpegFrameBytes` at zero for padding-only payloads and build the three encode-side frame data URIs from `JPEG_FRAME_DATA_URI_PREFIX` instead of a repeated literal (#12543 — thanks @pacocartones) +- **fix(devin):** accept Windows `DEVIN_AGENTIC_HOME` sandbox paths (`C:\...\.sandbox\...`) in the isolated-home check so the Devin Claude Bridge no longer fails closed on Windows ([#12405](https://github.com/diegosouzapw/OmniRoute/issues/12405)) (#12545 — thanks @pacocartones) +- **fix(db):** Add `conversation_turn_nodes` and orphaned `agentic_conversations` to the auto-cleanup cycle under the existing `retention.callLogs` window, so identity nodes whose call-log content has already been purged no longer accumulate without bound in `storage.sqlite` (#12548 — thanks @pacocartones) +- **fix(i18n):** Wrap the `~/.claude/profiles//settings.json` placeholder in ICU single quotes in the `OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES` feature-flag description across all 42 locales and the TypeScript default, so next-intl no longer fails with `INVALID_MESSAGE: UNCLOSED_TAG` and the Feature Flags card shows the description instead of the raw key (#12549 — thanks @pacocartones) +- **fix(orchestration):** `updateCloudAgentTask` now publishes the task's real `status` on `agent.task.updated` when an update only touches `result`, `activities` or `error`, instead of the fabricated `"updated"` state, and stays silent when no row matched the id (#12550 — thanks @pacocartones) +- **fix(i18n):** the home "Recent Requests" panel and the Provider Topology legend are now translated instead of rendering English copies on non-English dashboards; the legend reads its own `home.topologyLegend*` labels with consistent casing rather than borrowing the memory-settings "Recent" and analytics "Error" strings (#12551 — thanks @pacocartones). +- **docs(reference):** bring the `FEATURE_FLAGS.md` catalog back to 1:1 with `featureFlagDefinitions.ts` — 20 missing flags added, the two `*_BLOCK_THRESHOLD` env-only knobs moved out of the flag tables, category/total counts and the Live WS port corrected, guarded by a static test (#12552 — thanks @pacocartones) +- **fix(i18n):** backfill missing `usage.kiloPass*` strings in 39 locales and restore `featureFlagOmnirouteDisableThinkingLevelVariantsDescription` in `pt.json` ([#12561](https://github.com/diegosouzapw/OmniRoute/issues/12561)) ([#13027](https://github.com/diegosouzapw/OmniRoute/pull/13027)) — thanks @HouMinXi +- fix(docker): default docker-compose app ports (dashboard/API/live-WS) to loopback instead of `0.0.0.0`, closing the anonymous `/v1` LAN/WAN exposure gap left open by `REQUIRE_API_KEY=false` (#12568) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): close DNS-rebinding SSRF gap in webhook outbound-URL guard (#12569) ([#13243](https://github.com/diegosouzapw/OmniRoute/pull/13243)) +- fix(docker): require a per-session token on the VNC browser CDP bridge and isolate it on a dedicated Docker network (#12571) ([#13241](https://github.com/diegosouzapw/OmniRoute/pull/13241)) +- fix(open-sse): write Adobe Firefly session tokens and cookie jars with 0700/0600 permissions instead of the process umask (#12572) ([#13242](https://github.com/diegosouzapw/OmniRoute/pull/13242)) +- fix(api): remove hardcoded wildcard CORS in openai-to-gemini-sse.ts so the centralized fail-closed CORS gate is the sole source of `Access-Control-Allow-Origin` (#12573) ([#13239](https://github.com/diegosouzapw/OmniRoute/pull/13239)) +- fix(api): enforce API key policy (budget/rate-limit/schedule/endpoint scoping) on the ElevenLabs speech-to-text, text-to-speech and voices proxy routes (#12574) ([#13244](https://github.com/diegosouzapw/OmniRoute/pull/13244)) +- fix(sse): cap HuggingChat NDJSON body size and bound the read loop with the fetch timeout so a stalled or hostile upstream cannot buffer unbounded memory (#12577) ([#13282](https://github.com/diegosouzapw/OmniRoute/pull/13282)) +- fix(docker): scope the cliproxyapi/qdrant/bifrost sidecars to loopback by default and forward `CLIPROXYAPI_MANAGEMENT_KEY` into the cliproxyapi container so its management API is not left both unauthenticated and LAN-published (#12578) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): create DB export temp paths with `fs.mkdtempSync` instead of predictable timestamps (#12579) ([#13237](https://github.com/diegosouzapw/OmniRoute/pull/13237)) +- Cline 401 bodies that say "re-authenticate your Cline account" classify as a refreshable OAuth token, not a terminal expired key. The cooling panel no longer labels every cooldown as a 429; it shows the recorded last error instead. (#12594) ([#13060](https://github.com/diegosouzapw/OmniRoute/pull/13060)) — thanks @HouMinXi +- **fix(catalog):** degrade unknown combo targets instead of dropping LCD modalities, and persist OpenRouter `architecture.input_modalities` into the capability snapshot ([#12613](https://github.com/diegosouzapw/OmniRoute/issues/12613)) ([#13042](https://github.com/diegosouzapw/OmniRoute/pull/13042)) — thanks @HouMinXi +- fix(providers): send `x-api-key` instead of `Authorization: Bearer` for OpenCode Zen's `/v1/responses` endpoint (Muse Spark Contributor models), fixing a 401 on OmniRoute's auth header (#12633) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(sse): surface the actionable "Auggie CLI not found" message when the shell reports a missing `auggie` binary via exit code instead of a spawn error (#12645) ([#13263](https://github.com/diegosouzapw/OmniRoute/pull/13263)) +- **fix(config):** Persist deletions of built-in background-degradation entries — when a stored settings record exists its `degradationMap` is now authoritative instead of being merged under the defaults, so an entry the user removed in the dashboard no longer reappears on the next apply or restart ([#12424](https://github.com/diegosouzapw/OmniRoute/issues/12424)) ([#12647](https://github.com/diegosouzapw/OmniRoute/pull/12647)) — thanks @pacocartones +- **fix(gamification):** action-count badge milestones (First Token, Token Consumer, Token Machine, Token Whale, and the token-sharing tier) are now backed by a durable `xp_action_counts` counter incremented in `addXp()`, instead of a live `COUNT(*)` over `xp_audit_log`. The audit log is pruned by `retention.xpAuditLog` (default 30 days), so on a default install those "lifetime" milestones were really "actions in the last 30 days" and unlocked badges could stop unlocking once old rows aged out. `getActionCount()` and `checkActionCountBadges()` now read the same durable source, and a migration backfills existing totals from the surviving audit rows ([#12546](https://github.com/diegosouzapw/OmniRoute/issues/12546)) ([#12651](https://github.com/diegosouzapw/OmniRoute/pull/12651)) — thanks @pacocartones +- **fix(images):** `/v1/images/edits` now iterates a combo's targets the same way `/v1/images/generations` does (#9239) instead of flattening a bare combo to its first target. A combo whose first target is not edit-capable — or lacks credentials — now falls through to a later edit-capable target rather than hard-erroring, and missing credentials are skipped (not a hard `401`) to match the generations path. The per-target skip/terminal classification is extracted into a shared `runImageComboTargets` loop, so generations behavior is unchanged ([#12547](https://github.com/diegosouzapw/OmniRoute/issues/12547)). ([#12653](https://github.com/diegosouzapw/OmniRoute/pull/12653)) — thanks @pacocartones +- fix(sse): add first-byte watchdog to the TLS-fingerprint transport so a stalled wreq body falls back instead of hanging for minutes (#12656) ([#13272](https://github.com/diegosouzapw/OmniRoute/pull/13272)) +- fix(sse): exempt tiny-budget reasoning probes from combo quality failure and surface persisted-cooldown skips in ALL_TARGETS_SKIPPED diagnostics (#12659) ([#13269](https://github.com/diegosouzapw/OmniRoute/pull/13269)) +- fix(models): declare the real ~1M contextLength for OpenCode Zen's Muse Spark 1.2 models instead of falling back to the 200000 provider default (#12681) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(oauth): align codebuddy-cn OAuth User-Agent with the chat/usage CLI version to avoid WAF false positives (#12702) ([#13264](https://github.com/diegosouzapw/OmniRoute/pull/13264)) +- fix(dashboard): surface an authentication-required banner instead of silently blanking database settings for a guest session (#12709) ([#13270](https://github.com/diegosouzapw/OmniRoute/pull/13270)) +- **fix(resilience):** stop unbounded queue that hangs 6min until Aborted — gate, provider slot, and Bottleneck queue now share a per-connection `maxWaitMs` budget; `fail-closed` on exhaust (503); execution backstop `executionMaxWaitMs` overridable per connection with upstream clamp ([#12715](https://github.com/diegosouzapw/OmniRoute/pull/12715)) — thanks @maxmad64bis (with thanks to @Tushar49 for surfacing the slow-provider need in #12635) +- **fix(auto-combo):** every mode pack now carries `quality` and `reliability` weights (`quality` 0.02, 0.03 in `quality-first`; `reliability` 0.03, 0.04 in `reliability-first`), so selecting a pack no longer silences either signal; `DEFAULT_WEIGHTS` is unchanged ([#12731](https://github.com/diegosouzapw/OmniRoute/pull/12731)) — thanks @maxmad64bis +- **fix(sse):** the provider execution pipeline keeps the upstream error `code`/`type` again — both were lost when the non-streaming leg was extracted, so a config-class `422` (Antigravity missing project) degraded into a generic account cooldown instead of reaching the gate that recognises it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(db):** a legacy `call_logs` table no longer aborts startup — the provider-stats index is created after the column healing runs, not before it, so an install predating `request_type` opens instead of failing with `no such column` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(catalog):** a malformed row in the operator-writable custom-models blob no longer kills every `auto/*` pool ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(tests):** realign the guards that the same day's merges left asserting the old behavior — the provider-count total after GreenPT and EURouter, the GLM stream buffer slot that `#12925` turned into a declared parameter, and the injection scan bound that `#13104` reshaped from a head-only window into head-plus-tail ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- fix(cache): fold tool_choice/tools/response_format into the semantic cache signature so a cached tool_calls response can no longer be replayed for a request whose tool policy forbids it (#12734) ([#13267](https://github.com/diegosouzapw/OmniRoute/pull/13267)) +- **fix(models):** stop treating provider-supplied `isFree:true`/`:free`/`0/0` flags as trusted unless the provider has a documented free tier — only the shipped free-tier catalog decides otherwise; fetched `isFree:true` trusted only for a free-tier provider, custom `isFree:true` via a trusted path ([#12744](https://github.com/diegosouzapw/OmniRoute/pull/12744)) — thanks @maxmad64bis +- fix(memory): authenticate the internal /v1/rerank loopback call so memory reranking no longer silently degrades to unranked order when REQUIRE_API_KEY=true (#12745) ([#13268](https://github.com/diegosouzapw/OmniRoute/pull/13268)) +- fix(sse): parse Ollama Cloud's current usage markup (`$X of $Y used` aria-label, nested width style) (#12749) ([#13260](https://github.com/diegosouzapw/OmniRoute/pull/13260)) +- fix(cli): setup-opencode no longer sends an active context's management token to `/v1/models` when `--api-key`/`OMNIROUTE_API_KEY` is supplied — an explicit flag or the env var now always outranks the context's token, and the flag itself is no longer swallowed by the parent program's global `--api-key` option (#12783) ([#13246](https://github.com/diegosouzapw/OmniRoute/pull/13246)) +- fix(sse): register Arcee AI in the executor provider registry so requests reach api.arcee.ai instead of silently falling back to OpenAI (#12784) ([#13277](https://github.com/diegosouzapw/OmniRoute/pull/13277)) +- **fix(quota):** share one upstream quota read when identical saturation checks arrive at the same time, so traffic bursts don't multiply provider API calls ([#12787](https://github.com/diegosouzapw/OmniRoute/pull/12787)) — thanks @maxmad64bis +- **fix(db):** ignore expired or invalid rate-limit cooldown writes so a stale timestamp can't lock a connection that should be usable — clearing still works as before ([#12788](https://github.com/diegosouzapw/OmniRoute/pull/12788)) — thanks @maxmad64bis +- **fix(providers):** single source for provider order with `xao` ranking alongside `xai-oauth` ([#12790](https://github.com/diegosouzapw/OmniRoute/pull/12790)) — thanks @maxmad64bis +- **fix(routing):** off-table models check the free-model catalog before inheriting premium prices, latency bootstraps from the observed pool median, async tiers read live database pricing with a 90-day freshness gate, and the tier cache invalidates on every pricing write ([#12792](https://github.com/diegosouzapw/OmniRoute/pull/12792)) — thanks @maxmad64bis +- **fix(auto-combo):** snapshot scoring uses the observed per-connection account tier and per-model quality instead of neutral constants, and circuit-open providers rank lower at build time ([#12794](https://github.com/diegosouzapw/OmniRoute/pull/12794)) — thanks @maxmad64bis +- **fix(pool):** empty `auto/*` pools now say why they are empty, and the models listing uses the same paid check as routing ([#12795](https://github.com/diegosouzapw/OmniRoute/pull/12795)) — thanks @maxmad64bis +- fix(routing): recognize CLIProxyAPI's 'unknown provider for model' 400 as fallback-worthy (#12800) ([#13284](https://github.com/diegosouzapw/OmniRoute/pull/13284)) +- **fix(sse):** translate-mode streams now emit the estimated token counts as a trailing usage-only chunk before `[DONE]` when the upstream stays silent, so metered chat clients see totals instead of nothing ([#12828](https://github.com/diegosouzapw/OmniRoute/pull/12828)) — thanks @maxmad64bis +- **fix(db):** provider stats stay truthful on empty and legacy databases: fallback counts default to `0` instead of `null`, latency averages read `null` (not `0`) when no durations were recorded, failures logged before the error-type column existed group under `pre_migration` instead of `unclassified`, and per-provider queries use two new composite indexes ([#12832](https://github.com/diegosouzapw/OmniRoute/pull/12832)) — thanks @maxmad64bis +- fix(nvidia): fail open when a synced model catalog goes stale instead of gating forever (#12849) ([#13248](https://github.com/diegosouzapw/OmniRoute/pull/13248)) +- **fix(db):** WAL maintenance lives in its own module: a `TRUNCATE` checkpoint that hits a busy database now warns with its streak and retries once via `PASSIVE` instead of logging success, and closing-time checkpoints no longer report success on builds without a database file, with the busy totals visible in the authenticated monitoring health payload ([#12853](https://github.com/diegosouzapw/OmniRoute/pull/12853)) — thanks @maxmad64bis +- **fix(health):** quota with no snapshots reads empty instead of a contradicting 0% ([#12857](https://github.com/diegosouzapw/OmniRoute/pull/12857)) — thanks @maxmad64bis +- fix(a2a): accept the dashboard's own session cookie on /a2a so "Run message/send" no longer fails with "Unauthorized: missing or invalid API key" (#12888) ([#13271](https://github.com/diegosouzapw/OmniRoute/pull/13271)) +- **fix(logs):** the Logs grid's in-memory filter pass no longer discards rows the SQL query already matched — selecting an API key from the dropdown (which sends the key's id) returns its calls again, the Combo tab shows every combo instead of only those whose name contains a "1", and the model filter and search cover the same columns as the query ([#12896](https://github.com/diegosouzapw/OmniRoute/pull/12896)) — fixes [#12873](https://github.com/diegosouzapw/OmniRoute/issues/12873) — thanks @ntdatt812 +- **fix(a2a):** `/api/a2a/status` now builds the agent card from the request that asked for it, so a gateway reached at a non-localhost host no longer advertises `http://localhost:20128` as its A2A URL ([#12918](https://github.com/diegosouzapw/OmniRoute/pull/12918)). — thanks @ntdatt812 +- **fix(compression):** progressive aging now appends its `[COMPRESSED:aging:…]` annotation after a turn's `tool_result` blocks instead of in front of them, so Anthropic no longer rejects aged conversations with "`tool_use` ids were found without `tool_result` blocks immediately after" ([#12920](https://github.com/diegosouzapw/OmniRoute/pull/12920)). — thanks @ntdatt812 +- **fix(bedrock):** model import now resolves context limits for every vendor prefix instead of only `anthropic.*`, so `global.openai.gpt-5.6-*` no longer imports with a null `inputTokenLimit` and gets rejected pre-flight at the 200k default ([#12921](https://github.com/diegosouzapw/OmniRoute/pull/12921)). — thanks @ntdatt812 +- **fix(stream):** the 64 KB stream buffer GLM asks for is honoured instead of dropped, and the type error it caused no longer fails the API Route Typecheck gate on every open PR ([#12925](https://github.com/diegosouzapw/OmniRoute/pull/12925)) — thanks @ntdatt812 +- **fix(guardrails):** mask PII inside a `tool_result`'s nested content array, which the masker walked past while redacting its sibling block ([#12930](https://github.com/diegosouzapw/OmniRoute/pull/12930)) — thanks @ntdatt812 +- **fix(dashboard):** the Radar catalog table no longer leaves absent data unexplained — empty limits, unknown context windows, and unreported capabilities each explain themselves on hover, and a new check keeps it that way ([#12937](https://github.com/diegosouzapw/OmniRoute/pull/12937)) — thanks @maxmad64bis +- **fix(sse):** geo-blocked opencode requests rotate to the next account proxy instead of failing, so one refused egress no longer aborts the whole chain ([#12941](https://github.com/diegosouzapw/OmniRoute/pull/12941)) — thanks @maxmad64bis +- **fix(codex):** dashboard "clear cooldown" and the CAS recovery path now drop nested `codexScopeRateLimitedUntil` maps in the same write that nulls `rate_limited_until`, and fresh quota snapshots with headroom lift fallback-sourced scope cooldowns parked by quota preflight ([#12817](https://github.com/diegosouzapw/OmniRoute/issues/12817), [#12860](https://github.com/diegosouzapw/OmniRoute/issues/12860), [#12951](https://github.com/diegosouzapw/OmniRoute/pull/12951)) The dashboard reset-credit button (`consumeCodexResetCredit`) now exercises that same snapshot path after a successful redeem, so a filled quota bar is enough to unpark a leftover fallback Codex child without another manual clear. — thanks @HouMinXi +- **fix(db):** resolve `sql-wasm.wasm` across global npm install and hoisted layouts, ensuring OmniRoute can boot cleanly on Node 24 when native `better-sqlite3` is uncompiled. ([#13035](https://github.com/diegosouzapw/OmniRoute/pull/13035)) — thanks @HouMinXi +- **fix(api):** `DELETE /v1/batches/delete-completed` now sweeps only the calling API key's own completed batches (batches with no owner stay out of a key-scoped sweep on purpose), with an explicit instance-wide mode reserved for authenticated dashboard sessions, a 401 for a presented key that is unknown, revoked, deactivated, banned or expired (never falling through to the session branch), audit logging of every sweep, a sanitized 500 on failure, an owner-scoped file half (a key-scoped sweep never nulls a file another tenant owns) and an atomic sweep — chunked in 200-batch transactions in instance mode — so a mid-way error never leaves a batch pointing at a nulled file (GHSA-wvxc-jp3v-5mg5) ([#12969](https://github.com/diegosouzapw/OmniRoute/pull/12969)) +- **fix(combo):** quota-weighted routing stops drawing on an out-of-credit connection — a 402 now invalidates the stored quota snapshot instead of leaving its stale remaining percentage in place, and a snapshot older than 10 minutes no longer counts as confident headroom for the primary pool ([#12972](https://github.com/diegosouzapw/OmniRoute/pull/12972)) ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- **fix(vision):** Custom Models with "Vision capable" checked no longer have image requests swapped to `glm/glm-4.6v` when the client sends the advertised alias (`vllm/path/...`) or the bare path-shaped id — Vision Bridge now matches the stored override for all three id forms ([#12758](https://github.com/diegosouzapw/OmniRoute/issues/12758)) ([#12974](https://github.com/diegosouzapw/OmniRoute/pull/12974)) — thanks @HouMinXi +- **fix(sse):** transient opencode upstream failures rotate to the next account proxy instead of failing, so one flapping egress no longer aborts the whole chain ([#12975](https://github.com/diegosouzapw/OmniRoute/pull/12975)) — thanks @maxmad64bis +- **fix(azure):** Deployments from GPT-6 onward now send `max_completion_tokens` instead of `max_tokens`, which Azure rejects with HTTP 400. The rule matched a literal `gpt-5`, so each new generation arrived broken; it now matches the generation range, while `gpt-35-turbo` still keeps `max_tokens`. ([#13007](https://github.com/diegosouzapw/OmniRoute/pull/13007)) — thanks @ntdatt812 +- **fix(db):** add an opt-in self-restart circuit for sustained critical memory pressure, gate post-cleanup VACUUM behind a minimum freed-rows threshold, and checkpoint the SQLite WAL every 5 minutes with a size guard that escalates to TRUNCATE, so a growing WAL can no longer stall the event loop into a full outage. ([#13011](https://github.com/diegosouzapw/OmniRoute/pull/13011)) — thanks @HouMinXi +- **fix(auth):** an explicit connection pin may probe a quota-disabled row once and re-enable it on success ([#12874](https://github.com/diegosouzapw/OmniRoute/issues/12874)) ([#13017](https://github.com/diegosouzapw/OmniRoute/pull/13017)) — thanks @HouMinXi +- **fix(api):** thread `X-OmniRoute-Fallback-Attempts` through combo chat completions so streaming and non-streaming responses report how many prior legs were attempted ([#13038](https://github.com/diegosouzapw/OmniRoute/pull/13038)) — thanks @HouMinXi +- Restore provider failure classification and credential refresh on non-streaming requests: classify non-2xx failures to lock models on per-model quota exhaustion, update connection rate limits from headers and body, and pass credential refresh handlers to pipeline execution so 401 tokens can be refreshed and retried (#13043). ([#13069](https://github.com/diegosouzapw/OmniRoute/pull/13069)) — thanks @HouMinXi +- **fix(responses):** wrap forced-non-streaming web_search fallback JSON as Responses SSE so Codex still sees `response.completed` ([#13050](https://github.com/diegosouzapw/OmniRoute/pull/13050)) — thanks @HouMinXi +- **fix(antigravity):** Preserve upstream thought-token usage in normalized completion and reasoning token counts ([#13055](https://github.com/diegosouzapw/OmniRoute/pull/13055)) — thanks @pacocartones +- Fix a concurrency-slot leak in the DeepSeek PoW solver: a worker that failed to spawn (for example a missing worker script) never released its slot, so `MAX_CONCURRENT_WORKERS` failures disabled the solver until restart. ([#13097](https://github.com/diegosouzapw/OmniRoute/pull/13097)) — thanks @anhtahaylove +- **fix(acp):** bound the ACP session output buffers — `stdoutBuffer` and `stderrBuffer` now cap at 1 MiB keeping the most recent output behind a visible `[...output truncated...]` marker, and `stderrBuffer` is reset per prompt instead of accumulating for the lifetime of the session. ([#13100](https://github.com/diegosouzapw/OmniRoute/pull/13100)) — thanks @anhtahaylove +- **fix(acp):** release the `stdout`/`exit` listeners and the idle timer that a `sendPrompt` timeout used to leave attached to the `acpManager` singleton, and drop sessions that exited on their own from the session map instead of keeping them forever. ([#13096](https://github.com/diegosouzapw/OmniRoute/pull/13096)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection and PII scanners now read the text a `tool_result` block carries on `content` (string or nested block list), in messages and in system blocks, so tool output is judged by the same rules as user text ([#13101](https://github.com/diegosouzapw/OmniRoute/pull/13101)) — thanks @ntdatt812 +- **fix(gamification):** close the badge notification SSE stream when the request signal is already aborted before the stream starts — a client that disconnects while the route is still awaiting auth used to leave both the 2s unlock poll and the 15s heartbeat running for the lifetime of the process. ([#13106](https://github.com/diegosouzapw/OmniRoute/pull/13106)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection scan now spends its 16 KB budget on both ends of the request instead of the first 16 KB only, so `system`, `instructions`, `query`, `documents` and the newest turns are no longer hidden behind one long message ([#13104](https://github.com/diegosouzapw/OmniRoute/pull/13104)) — thanks @ntdatt812 +- **fix(dashboard):** expose the Volcano Ark console cookie on quota scraping and unwrap connect-error objects so the dashboard shows the upstream message ([#13107](https://github.com/diegosouzapw/OmniRoute/pull/13107)) — thanks @HouMinXi +- **fix(db):** release the `beforeExit`/`SIGINT`/`SIGTERM` handlers when a `node:sqlite` adapter closes, so a closed adapter and its database handle are no longer pinned to `process` for the lifetime of the run — the same treatment #7494 gave the sql.js adapter. ([#13109](https://github.com/diegosouzapw/OmniRoute/pull/13109)) — thanks @anhtahaylove +- **fix(translator):** `contentSchema` and `unevaluatedItems` are now treated as subschema positions by the tool-schema sanitizer, so a truncation placeholder in either is replaced with a permissive schema instead of being forwarded as a string ([#13110](https://github.com/diegosouzapw/OmniRoute/pull/13110)) — thanks @ntdatt812 +- **fix(cli-helper):** clear the `createLogStream` timeout on the abort path — `stop()` aborts the in-flight fetch and returned through the `signal.aborted` branch, which skipped `clearTimeout` and left an armed timer per stopped stream. The stream reader is now also cancelled when the read loop exits early. ([#13114](https://github.com/diegosouzapw/OmniRoute/pull/13114)) — thanks @anhtahaylove +- **fix(docs):** drop TLS-impersonation, MITM-decrypt, supply-chain attestation, and XOR-mask writeups from the public `/docs` catalog and Docker image. Files stay in git for engineers; operators who need them open the repo, not the website. ([#13136](https://github.com/diegosouzapw/OmniRoute/pull/13136)) — thanks @HouMinXi +- **fix(combo):** parse numeric-epoch `rate_limited_until` in the combo cooldown read path ([#13141](https://github.com/diegosouzapw/OmniRoute/pull/13141)) — thanks @maxmad64bis +- **fix(opencode):** both OpenCode plugins now reject a gateway address typed without `http://` at configuration time, instead of publishing every model with an api url no client can call, and the v2 plugin no longer publishes a model card whose api url is blank or relative ([#13142](https://github.com/diegosouzapw/OmniRoute/pull/13142)) — thanks @maxmad64bis +- **fix(providers):** lock opencode model on upstream 400 model-unavailable ([#13146](https://github.com/diegosouzapw/OmniRoute/pull/13146)) — thanks @maxmad64bis +- **fix(logging):** keep the provider exchange rather than the raw client bodies when a call log exceeds its size budget, and show that recovered payload in the request-detail panel instead of replacing it with the stored response body ([#13147](https://github.com/diegosouzapw/OmniRoute/pull/13147)) — thanks @maxmad64bis +- **fix(traffic-inspector):** the WebSocket route no longer leaks a traffic-buffer subscriber and a 30s ping timer when the client socket is already closed at handler time — listeners are attached before any resource is acquired, a destroyed socket bails out early, and the ping interval stops on a dead socket where `write()` never throws ([#13155](https://github.com/diegosouzapw/OmniRoute/pull/13155)) — thanks @anhtahaylove +- **fix(telegram):** bound the per-user API key cache in the Telegram chat proxy so a burst of distinct chat ids can no longer grow the process heap without limit ([#13165](https://github.com/diegosouzapw/OmniRoute/issues/13165)) ([#13166](https://github.com/diegosouzapw/OmniRoute/pull/13166)) — thanks @anhtahaylove +- **fix(stream):** cancel the upstream response body when the JSON-to-SSE sniff unwinds on a body timeout, so a stalled upstream no longer pins the connection ([#13169](https://github.com/diegosouzapw/OmniRoute/issues/13169)) ([#13171](https://github.com/diegosouzapw/OmniRoute/pull/13171)) — thanks @anhtahaylove +- **fix(telegram):** authenticate webhook deliveries with Telegram's `secret_token` so an unauthenticated caller can no longer mint API keys or spend upstream quota ([#13172](https://github.com/diegosouzapw/OmniRoute/issues/13172)) ([#13175](https://github.com/diegosouzapw/OmniRoute/pull/13175)) — thanks @anhtahaylove +- **fix(models):** give discoverable Gemini 3.8 Flash ids their own 65536 output spec so Antigravity no longer clamps them to 16384 ([#13195](https://github.com/diegosouzapw/OmniRoute/pull/13195)) — thanks @HouMinXi +- **fix(dashboard):** leftover catalog-removed provider rows (gemini-cli) can be listed and purged from the providers page ([#13067](https://github.com/diegosouzapw/OmniRoute/issues/13067)) ([#13197](https://github.com/diegosouzapw/OmniRoute/pull/13197)) — thanks @HouMinXi +- **fix(auth):** a dashboard session now requires the `authenticated: true` claim that login, OIDC and the session refresh already emit — a JWT merely signed with `JWT_SECRET` (for example the Cursor CLI passthrough token, which any API-key holder can obtain) no longer verifies as the `auth_token` cookie on any route, the WebSocket handshake or the live server; existing sessions keep working ([#13298](https://github.com/diegosouzapw/OmniRoute/issues/13298)) ([#13375](https://github.com/diegosouzapw/OmniRoute/pull/13375)) +- **fix(api):** `DELETE /v1/batches/delete-completed` now applies the caller's API-key policy (endpoint allowlist, schedule, usage cap, rate limit) like every other `/v1` route, commits the key-scoped sweep per 200-batch chunk instead of holding one write lock for the whole sweep, refuses to loop without progress, and rejects a scope that names both a key and `allTenants` ([#13684](https://github.com/diegosouzapw/OmniRoute/pull/13684)) +- Restore the API-route typecheck gate: the non-streaming leg result lost its discriminated-union narrowing after the server-owned tool loop reassignment, producing 13 new TS2339 diagnostics in `chatCore.ts`. ([#12963](https://github.com/diegosouzapw/OmniRoute/pull/12963)) +- Restore four non-streaming behaviours the server-owned tool loop refactor dropped: client aborts map to 499 with the fixed `Request aborted` message, an aborted request no longer logs a synthetic `clientResponse`, Claude prompt-cache telemetry is recorded again, and a body that cannot be canonicalized no longer throws when the tool loop is off. ([#12990](https://github.com/diegosouzapw/OmniRoute/pull/12990)) +- **fix(skills):** The CLI registry parser now reads positionals declared with `.addArgument()`, not only those written inline in `.command()`. `tunnel create [type]` was being published as `tunnel create`, so the agent-skills sync gate reported drift on every branch and regenerating would have deleted the argument. ([#13009](https://github.com/diegosouzapw/OmniRoute/pull/13009)) — thanks @ntdatt812 +- **fix(combos):** dashboard combo test uses a short prompt, serial probes, and a 60s timeout so reasoning models and rate-limited free pools do not fail the health check ([#13001](https://github.com/diegosouzapw/OmniRoute/pull/13001)) — thanks @HouMinXi +- fix(compression): terminate idle worker threads on eviction so long-running instances stop leaking OS threads and MessagePorts ([#13091](https://github.com/diegosouzapw/OmniRoute/pull/13091)) — thanks @anhtahaylove +- Redact Google API keys of any length in error bodies: the pattern required exactly 39 characters, so shorter or longer `AIza…` credentials passed through unredacted. ([#12964](https://github.com/diegosouzapw/OmniRoute/pull/12964)) +- **fix(grok-cli):** a 402 "Grok Build usage balance exhausted" parks that Grok login as out of credit (Grok Build CLI, grok.com cookie, and xAI OAuth share the weekly pool). Combo routing then tries the next login instead of locking the model for every account in the pool ([#13061](https://github.com/diegosouzapw/OmniRoute/pull/13061)) — thanks @HouMinXi +- fix(compression): spawn the LLMLingua worker with a file URL object so compression actually runs instead of silently failing open on Node ([#13093](https://github.com/diegosouzapw/OmniRoute/pull/13093)) — thanks @anhtahaylove +- **fix(test):** run the local `test` and `test:unit` scripts at concurrency 4 so a full-suite run no longer exhausts the machine's commit charge and kills unrelated processes ([#13187](https://github.com/diegosouzapw/OmniRoute/pull/13187)) — thanks @anhtahaylove +- **fix(monitoring):** `GET /api/monitoring/health` `credentialHealth` now includes a bounded `failedConnections` list (`connectionId`, `status`, sanitized `lastError`) when the probe-cache gauge `failed>0`, plus `source: probe-cache` and a cheap `staleDbNonOkCount` for sticky SQLite `test_status` on active rows. Documents that the live gauge is not `provider_connections.test_status`. ([#12876](https://github.com/diegosouzapw/OmniRoute/pull/12876)) — thanks @RaviTharuma +- **fix(resilience):** an openai-compatible multi-upstream gateway no longer marks the whole connection `credits_exhausted` when a single upstream (or the `/models` / representative-model health probe) returns 402. Quota failures stay model-scoped; true connection-wide auth failures are unchanged. Single-credential 402 key disable ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239)) is preserved. ([#12875](https://github.com/diegosouzapw/OmniRoute/pull/12875)) — thanks @RaviTharuma +- **fix(dashboard):** nine fixes on the Orchestration Canvas, all diagnosed in the Phase 2 reviews. A source that fails now keeps the timestamp of its FIRST failure instead of being re-stamped every poll — the stale line said "since the last poll" no matter how long the source had been down, and the churn also defeated the snapshot's stable identity (it serializes the source list), so the canvas re-rendered on every tick while anything was broken. A source that HAD data and only then started failing is flagged too: previously only a source with no node at all got the warning, so a source that went down mid-session kept a healthy-looking node forever. The rest are pointwise: clicking a filter chip cancels the pending search debounce (left armed, it fired ~300ms later and silently reverted the chip); the search input carries an accessible name; `?state=running, failed` parses like the unpadded form instead of dropping the padded value; the CSV toggle helper is defined once in `model/urlParams.ts` rather than twice; the agents tab tells "nothing running" apart from "the filter matched nothing", offering a clear-filters button instead of setup links that would be wrong advice there; edges stop emitting SMIL particles above 40 simultaneously active edges, keeping the colored stroke; and the drawer's error banner clears when a retried action succeeds. ([#12983](https://github.com/diegosouzapw/OmniRoute/pull/12983)) +- **fix(i18n):** the nine locales added with the EU-language batch (Greek, Estonian, Irish, Croatian, Lithuanian, Latvian, Maltese, Slovenian, Serbian) were missing the eleven Orchestration Canvas keys that Phase 3 introduced, so the compare-runs panel and the "no runs match these filters" empty state fell back to English in those languages (`deepMergeFallback` substitutes English for an absent key, so nothing rendered blank — it rendered untranslated). The coverage gate does not catch this: it enforces an 80% floor per locale, and eleven missing keys out of ~13,000 leaves coverage at 99.9%. Translated for real in each language, calibrated against the wording each file already uses for "run", "filter" and "skill". ([#13294](https://github.com/diegosouzapw/OmniRoute/pull/13294)) +- **fix(dashboard):** five follow-ups from the Phase 2 Orchestration Canvas review. The History drawer now shows the memory section for runs that already left the live TTL window: the persisted `memory_hits` event is parsed into `metadata.memoryHits` with the same defensive validation the drawer applies, and — because that event is observability rather than a state transition — it no longer leaks into the timeline, where it had been inheriting the task's state and rendering as a duplicate transition. The A2A memory recall runs against its own 1.5s deadline instead of inheriting the memory backend's 30s one; overshooting degrades exactly like any other recall failure (no hits, task proceeds), and the timer is cleared on both paths. Repeating a Conductor run carries its `requirements.cli`/`requirements.model` forward, so the new run is pinned to the same runner profile and model rather than drifting to whatever the fleet picks. A successful repeat from the Agents tab now focuses the run it created, instead of leaving the operator on the finished one. And the auth test for `POST /api/conductor/tasks` moved into the shared `ROUTES` array rather than restating the pattern. ([#12988](https://github.com/diegosouzapw/OmniRoute/pull/12988)) +- fix(plugins): stop leaking an exit listener per plugin hook timeout, which triggered MaxListenersExceededWarning on plugins that ignore SIGTERM ([#13092](https://github.com/diegosouzapw/OmniRoute/pull/13092)) — thanks @anhtahaylove +- **fix(validation):** Provider node edits no longer fail with a generic "Invalid request" when the optional daily-quota reset fields are left blank. The dashboard sends `dailyQuotaResetTimezone` and `dailyQuotaResetHour` as `null`, and only the hour accepted it. ([#13066](https://github.com/diegosouzapw/OmniRoute/issues/13066)) ([#13083](https://github.com/diegosouzapw/OmniRoute/pull/13083)) — thanks @ntdatt812 +- **fix(combo):** quota-aware expansion drops banned, inactive, missing, and wrong-provider connections before quota fetch or model dispatch; pins and allowlists stay selectors, not a bypass. Antigravity automatic exhaustion now requires a reported zero remaining, so a positive balance below 1% stays eligible. ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- Fix an intermittent failure in the quota-weighted routing test suite: scores are a function of `Date.now()`, so peers with identical quota scored microseconds apart never tied and swapped order. ([#12946](https://github.com/diegosouzapw/OmniRoute/pull/12946)) +- fix(volcengine): resolve quota window mapping gaps, safe multi-connection console binding, and response cookie sanitization ([#12950](https://github.com/diegosouzapw/OmniRoute/pull/12950)) — thanks @HouMinXi +- **fix(test):** resolve the WebDAV handler path with `fileURLToPath` so the suite's 37 WebDAV tests run on Windows instead of failing with a doubled `C:\C:\` drive prefix ([#13196](https://github.com/diegosouzapw/OmniRoute/pull/13196)) — thanks @anhtahaylove +- **fix(ui):** fall back from invalid AnySearch icon alias (direct commit `9587e07b69`) +- **fix(sse):** stop rebuilding a truncated summary from the collector's cap-dropped event array ([#12718](https://github.com/diegosouzapw/OmniRoute/pull/12718)) — thanks @hartmark +- **fix(conversations):** show a pending spinner for unresolved tool nodes instead of "(empty)" ([#12727](https://github.com/diegosouzapw/OmniRoute/pull/12727)) — thanks @hartmark +- **fix(streaming):** fail fast when an upstream stream produces only lifecycle/heartbeat events, never real content ([#12741](https://github.com/diegosouzapw/OmniRoute/pull/12741)) — thanks @hartmark +- **fix(responses-continuation):** bridge the write-in-flight window with an in-memory pending store ([#12854](https://github.com/diegosouzapw/OmniRoute/pull/12854)) — thanks @hartmark +- **fix(translator):** drop or map agent_message on Chat Completions fallback ([#12880](https://github.com/diegosouzapw/OmniRoute/pull/12880)) — thanks @RaviTharuma +- **fix(api):** page and stream-complete GET /v1/models for large catalogs ([#12882](https://github.com/diegosouzapw/OmniRoute/pull/12882)) — thanks @RaviTharuma +- **fix(quota):** align AUTH window labels with usage API and clarify 503 ([#12884](https://github.com/diegosouzapw/OmniRoute/pull/12884)) — thanks @RaviTharuma +- **fix(ci):** clear two base-reds on release/v3.8.51 — mutation-coverage gate + image-only-model guard ([#12945](https://github.com/diegosouzapw/OmniRoute/pull/12945)) +- **fix(api):** scope batch bulk-delete to the calling API key ([#13211](https://github.com/diegosouzapw/OmniRoute/pull/13211)) +- **fix(tests):** align model-sync log assertion to shipped path redaction (base-red) ([#13227](https://github.com/diegosouzapw/OmniRoute/pull/13227)) +- **fix(test):** make the ToS heading guard actually require the parentheses ([#13228](https://github.com/diegosouzapw/OmniRoute/pull/13228)) +- **fix(quality):** register 4 drifted covering tests in stryker tap.testFiles ([#13229](https://github.com/diegosouzapw/OmniRoute/pull/13229)) +- **fix(logging):** mask *-api-key headers in request-log pipeline (#13273) ([#13401](https://github.com/diegosouzapw/OmniRoute/pull/13401)) — thanks @KooshaPari +- **fix(tests):** correct stale Jina catalog prefix in custom-models test ([#13403](https://github.com/diegosouzapw/OmniRoute/pull/13403)) — thanks @KooshaPari +- **fix(backend):** prune old backups after health-check-repair snapshots (#13308) ([#13404](https://github.com/diegosouzapw/OmniRoute/pull/13404)) — thanks @KooshaPari +- **fix(backend):** guard memory_fts_au trigger with WHEN clause to stop FTS bloat (#13326) ([#13405](https://github.com/diegosouzapw/OmniRoute/pull/13405)) — thanks @KooshaPari +- **fix(free-tiers):** remove obsolete Together $25 signup credit (#12526) ([#13407](https://github.com/diegosouzapw/OmniRoute/pull/13407)) — thanks @KooshaPari +- **fix:** skip non-chat generation models in Test all models ([#13409](https://github.com/diegosouzapw/OmniRoute/pull/13409)) — thanks @KooshaPari +- **fix(providers):** de-duplicate agy and antigravity model catalogs ([#13410](https://github.com/diegosouzapw/OmniRoute/pull/13410)) — thanks @KooshaPari +- **fix(dashboard):** send provider in body for mini-playground web search ([#13411](https://github.com/diegosouzapw/OmniRoute/pull/13411)) — thanks @KooshaPari +- **fix(resilience):** add CJK quota-exhaustion patterns to 429 classifier ([#13412](https://github.com/diegosouzapw/OmniRoute/pull/13412)) — thanks @KooshaPari +- **fix(backend):** eval runner: failed calls no longer score as passed + dotAll regex ([#13413](https://github.com/diegosouzapw/OmniRoute/pull/13413)) — thanks @KooshaPari +- **fix(backend):** eval runner opts out of output-style and memory injection ([#13414](https://github.com/diegosouzapw/OmniRoute/pull/13414)) — thanks @KooshaPari +- **fix:** chain serial quarantine step in npm test script (fixes #13286) ([#13418](https://github.com/diegosouzapw/OmniRoute/pull/13418)) — thanks @KooshaPari +- **fix(antigravity):** derive stable session ID from credentials for prompt-cache reuse (#13305) ([#13424](https://github.com/diegosouzapw/OmniRoute/pull/13424)) — thanks @KooshaPari +- **fix(memory):** merge memory into top-level system field instead of unshifting at messages[0] (#13425) ([#13427](https://github.com/diegosouzapw/OmniRoute/pull/13427)) — thanks @KooshaPari +- **fix(compression):** preserve instruction blocks from lossy engines ([#13523](https://github.com/diegosouzapw/OmniRoute/pull/13523)) — thanks @KooshaPari +- **fix(compression):** preserve polarity words and newlines in ultra heuristic ([#13524](https://github.com/diegosouzapw/OmniRoute/pull/13524)) — thanks @KooshaPari +- **fix(compression):** run user preservePatterns before built-in patterns (#13457) ([#13525](https://github.com/diegosouzapw/OmniRoute/pull/13525)) — thanks @KooshaPari +- **fix(reasoning):** expand deepseek-v4 regex to match v4.1+ and add command-code provider (#13430) ([#13528](https://github.com/diegosouzapw/OmniRoute/pull/13528)) — thanks @KooshaPari +- **fix(providers):** add alwaysReasons metadata for reka-flash-3 ([#13531](https://github.com/diegosouzapw/OmniRoute/pull/13531)) — thanks @KooshaPari +- **fix(playground):** allow Compare columns to scroll independently ([#13532](https://github.com/diegosouzapw/OmniRoute/pull/13532)) — thanks @KooshaPari +- **fix(security):** empty whitelist allows through to prevent admin lockout ([#13534](https://github.com/diegosouzapw/OmniRoute/pull/13534)) — thanks @KooshaPari +- **fix(tests):** add DISABLE_IOREG_STRATEGY env var for macOS test compatibility (fixes #13467) ([#13539](https://github.com/diegosouzapw/OmniRoute/pull/13539)) — thanks @KooshaPari +- **fix(zed-hosted):** map developer role and Gemini enums for Zed proxy ([#13543](https://github.com/diegosouzapw/OmniRoute/pull/13543)) — thanks @KooshaPari +- **fix(analytics):** normalize provider aliases to canonical id in usage_history ([#13545](https://github.com/diegosouzapw/OmniRoute/pull/13545)) — thanks @KooshaPari +- **fix(combo):** use traceId for call log id to prevent UNIQUE constraint failure ([#13546](https://github.com/diegosouzapw/OmniRoute/pull/13546)) — thanks @KooshaPari +- **fix(auth):** check model lockout before returning synthetic noauth connection ([#13547](https://github.com/diegosouzapw/OmniRoute/pull/13547)) — thanks @KooshaPari +- **fix(guardrails):** skip credential redaction for base64 image data URLs ([#13550](https://github.com/diegosouzapw/OmniRoute/pull/13550)) — thanks @KooshaPari +- **fix(resilience):** increase requestQueue.maxWaitMs default from 15s to 30s ([#13553](https://github.com/diegosouzapw/OmniRoute/pull/13553)) — thanks @KooshaPari +- **fix(docs):** document OMNIROUTE_READY_TIMEOUT_MS and allowlist the test-only DISABLE_IOREG_STRATEGY ([#13692](https://github.com/diegosouzapw/OmniRoute/pull/13692)) + ### 📝 Maintenance ---- +- **docs(readme):** reconcile live v3.8.50 provider, free-tier, CLI, routing, test, community, sponsor, acknowledgment, and SVG metrics with their audited source denominators, including a deduplicated OmniRoute-in-Action snapshot and distinct contributor rankings for merged pull requests, GitHub-attributed commits, and Git history ([#11356](https://github.com/diegosouzapw/OmniRoute/pull/11356)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **docs:** sync the canonical provider count 354 → 356 across `README.md`, `AGENTS.md`, `llm.txt` (+ 42 i18n mirrors), the four README SVG diagrams, `docs/reference/PROVIDER_REFERENCE.md` (regenerated) and the `package.json` description after Opper (#11629) and 1min.ai (#11631) boarded the catalog — closes the `check:docs-counts-sync` strict drifts that kept `release/v3.8.51` red ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11673](https://github.com/diegosouzapw/OmniRoute/pull/11673)) — thanks @jonlwheat2-gif +- **test(providers):** update count-derived assertions after the v3.8.51 provider additions — `APIKEY_PROVIDERS` 233 → 235 (Opper #11629 + 1min.ai #11631), reserved-prefix REGISTRY walk 395 → 398, `WEB_FETCH_PROVIDERS` now includes `nimble-search` (#11620), and the provider translate-path golden snapshot regenerated ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11674](https://github.com/diegosouzapw/OmniRoute/pull/11674)) — thanks @jonlwheat2-gif +- **test(sse):** bump the hard-lease connection-query inventory for `src/lib/tokenHealthCheck.ts` to 2 — the verify-only web-cookie sweep added by #11495 split the single `getProviderConnections` call into oauth + cookie variants, which the frozen inventory had not tracked ([#11495](https://github.com/diegosouzapw/OmniRoute/pull/11495)). ([#11677](https://github.com/diegosouzapw/OmniRoute/pull/11677)) — thanks @jonlwheat2-gif / @oyi77 +- **chore(release):** clear the `release/v3.8.51` base-red gates — docs migration count (+38 i18n mirrors), stryker `tap.testFiles`, lockfile registry host, stale ESLint suppressions, the 7 lint regressions they were hiding, the stale `APIKEY_PROVIDERS` partition count (231 → 233), the `glm-5.3-max` tier missing from the GLM test table, a dead `modelType === "chat"` comparison in the models catalog, and the `resolveLiveWsUrl` / `sanitizeLiveWsPort` helpers from #11388 that a later merge dropped from `wsPath.ts` (dashboard live WebSocket URL resolution) ([#11502](https://github.com/diegosouzapw/OmniRoute/pull/11502)) — thanks @MumuTW +- **chore(free-models):** the free-tier regime table now declares which totals figure each regime feeds and whether it may take the no-auth shortcut, so the sets that used to repeat those answers by hand are derived from it — a new `freeType` no longer compiles until it has answered every question, instead of silently contributing to no total ([#11537](https://github.com/diegosouzapw/OmniRoute/pull/11537)) — thanks @maxmad64bis +- **docs(routing):** LKGP strategy copy now states provider-level stickiness and the explicit rules fallback instead of promising per-target pinning ([#11555](https://github.com/diegosouzapw/OmniRoute/pull/11555)) — thanks @maxmad64bis +- **test(chatcore):** pin the response tool-name alias resolution against the ordering hazard that broke every Gemini/Antigravity MCP tool call in v3.8.49. `extractRequestToolIdentityMap` deletes `translatedBody._toolNameMap`, so the later read is always undefined and the ledger survives only through the `requestToolIdentityMap` fallback — removing that fallback previously left the entire tool-name suite green. The resolution moves into `resolveResponseToolNameMap()` next to the map it depends on, with a regression guard that fails without the recovery. No behaviour change. ([#11575](https://github.com/diegosouzapw/OmniRoute/pull/11575)) — thanks @amartinawi +- **docs(env):** document `OMNIROUTE_CORPUS_CACHE_SIZE` in `.env.example` and `ENVIRONMENT.md`, so the env/docs contract check stops failing Docs Gates on every branch ([#11576](https://github.com/diegosouzapw/OmniRoute/pull/11576)) — thanks @ntdatt812 +- **chore(stryker):** register `route-guard-tunnel-processes-local-only` and `universal-quota-aware-routing` in `tap.testFiles`, so their mutant kills count for the three modules they cover ([#11577](https://github.com/diegosouzapw/OmniRoute/pull/11577)) — thanks @ntdatt812 +- **test(authz):** pin `LOCAL_ONLY_API_GET_EXEMPTIONS` by exact membership instead of by entry count, so the guard names the offending path and also catches a substitution ([#11580](https://github.com/diegosouzapw/OmniRoute/pull/11580)) — thanks @ntdatt812 +- **test(build):** pin `resolveNextBuildBundlerFlag` to the `OMNIROUTE_USE_TURBOPACK` contract it implements, across runtimes, instead of asserting an unimplemented Bun override ([#11583](https://github.com/diegosouzapw/OmniRoute/pull/11583)) — thanks @ntdatt812 +- **test(claude-web):** the Claude Web transport test's barrel anchors no longer pin the exact construction spelling. #11421 made the executor map lazy, so `/"claude-web": new ClaudeWebExecutor\(\)/` stopped matching and the test went red on `release/v3.8.51` itself — a base-red gate every open PR inherited. The anchors now match the alias key and the class name and tolerate either spelling in between, and were checked to still fail when an alias is genuinely repointed at another executor ([#11585](https://github.com/diegosouzapw/OmniRoute/pull/11585)) — thanks @ntdat812 +- **test(models):** pin `qwen3.8-max` resolution per provider — two of the four catalogs now serve the bare id and no longer carry `-preview` ([#11590](https://github.com/diegosouzapw/OmniRoute/pull/11590)) — thanks @ntdatt812 +- **test(executors):** drop the stale 128-tool truncation assertion that contradicted the #11444 pass-through contract ([#11591](https://github.com/diegosouzapw/OmniRoute/pull/11591)) — thanks @ntdatt812 +- **test(token-health):** pin the current terminal-skip boundary — expired with retry budget is probed, `account_deactivated` stays skipped ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)) — thanks @ntdatt812 +- **test(models):** follow #11503's retarget of the `gemini-3-pro-high` alias to the hyphenated catalog id ([#11593](https://github.com/diegosouzapw/OmniRoute/pull/11593)) — thanks @ntdatt812 +- **refactor(video bridge):** extract per-video acquisition, whole-result caching, description, and metrics/abort/cleanup out of `VideoBridgeGuardrail.preCall` into a `processVideoPart` seam in a new `videoBridgePipeline.ts`, behind explicit `VideoMediaBrokerPort`, `VideoAudioTranscriptionPort`, and `VideoDrilldownPort` boundaries; `preCall` now only handles request traversal, policy, and response aggregation. The Video tab's FFmpeg/ffprobe runtime status is now an explicit `unknown` / `restricted` / `unavailable` / `available` state instead of a nullable boolean pair, fixing a case where an in-flight or failed probe was mislabeled as "install FFmpeg" ([#11657](https://github.com/diegosouzapw/OmniRoute/issues/11657)). ([#12016](https://github.com/diegosouzapw/OmniRoute/pull/12016)) +- **test(opencode-plugin):** add unit test coverage for `formatFreeBudget()` naming helper ([#11660](https://github.com/diegosouzapw/OmniRoute/pull/11660)) — thanks @f9td56dbgh-hub +- **docs(video):** clarify that the Video Bridge transcript `source` field (`client`, `embedded`, `audio-bridge`) is presently caller-declared and not yet server-verified — OmniRoute enforces the enum shape but does not cryptographically confirm that an `embedded`/`audio-bridge` label came from a server-owned extraction ([#11661](https://github.com/diegosouzapw/OmniRoute/issues/11661)). ([#12001](https://github.com/diegosouzapw/OmniRoute/pull/12001)) +- **chore(providers):** retire the distributed Raycast Relay and Hailuo Web integrations after a provenance audit confirmed substantial derivation from GPL-3.0 sources; the official GitHub and MiniMax API providers remain available. Migration 163 disables legacy connections and invalidates their active leases while preserving connection IDs, audit/history rows, quota snapshots, usage records, and fail-closed API-key connection policies. ([#11691](https://github.com/diegosouzapw/OmniRoute/pull/11691)) +- **chore(providers):** retire the Felo Web provider while its GPL-derived provenance remains on licensing HOLD ([#11698](https://github.com/diegosouzapw/OmniRoute/pull/11698)) — stale runtime and database references now fail closed while existing connection, lease, and usage history remains preserved for auditability. +- **fix(sse):** retire the Gemini Web image-generation bridge while provenance and licensing remain on HOLD, preserving Gemini Web chat/auth/tools and legitimate Gemini/Nano Banana image providers ([#11708](https://github.com/diegosouzapw/OmniRoute/pull/11708)) +- **chore(audio):** retire the built-in EdgeTTS provider and its 11-voice catalog while provenance/licensing review remains on HOLD; the generic `/v1/audio/speech` endpoint and the other speech providers remain available ([#11711](https://github.com/diegosouzapw/OmniRoute/pull/11711)) +- **fix(providers):** retire the unofficial Qwen Web integration and its `qwen-web`/`qw` identities while third-party provenance remains on HOLD ([#11713](https://github.com/diegosouzapw/OmniRoute/pull/11713)) — direct requests now fail closed with a sanitized HTTP 410 and combos skip the retired target, while migration guards preserve connection IDs, allowlists, usage/audit history and invalidate active leases. Supported Qwen API, Qwen Code, cloud, token-plan, audio, local and model-name surfaces remain available; restoring Qwen Web requires a qualified provenance/license decision. +- **fix(providers):** retire `microsoft-designer-web` and its `msdesigner` alias from runtime and catalogs while preserving stored credentials, provider history and allowlists as inactive records — source dependency on gpt4free's [`MicrosoftDesigner.py`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/g4f/Provider/needs_auth/MicrosoftDesigner.py) is proven at snapshot `0943d16eef757a44deb60651c07bbe5fa958cafe`, whose [`LICENSE`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/LICENSE) is GPL-3.0; expressive adaptation is probable, which records provenance/licensing risk without asserting a legal conclusion ([#11720](https://github.com/diegosouzapw/OmniRoute/pull/11720)). +- **docs(legal):** centralize pinned MIT notices for the vendored GCF codec and local flag assets ([#11726](https://github.com/diegosouzapw/OmniRoute/pull/11726)) +- Split the 2,952-line `adobeFireflyClient.ts` into nine focused modules (catalog, credentials, payload, ARP session, upload, responses, IMS, account, poll) behind an unchanged barrel — no consumer edits and no public API change (#11728). — thanks @MumuTW +- **fix(dashboard):** replaced the Model Lockout card's unprovenanced notification MP3 with a short, locally synthesized Web Audio chime ([#11731](https://github.com/diegosouzapw/OmniRoute/pull/11731)) — both toggles retain optional audible feedback without shipping replacement media or third-party code, and unsupported, suspended, or rejected audio contexts fail open without blocking the setting change. +- **chore(deepseek-web):** remove the provenance-unresolved DeepSeek PoW WASM binary and its runtime loader/tracing while retaining the existing JavaScript solver (slower at high difficulty) ([#11732](https://github.com/diegosouzapw/OmniRoute/pull/11732)). +- **chore(assets):** remove four unused provider assets whose redistribution provenance could not be verified while retaining the active Freebuff SVG ([#11735](https://github.com/diegosouzapw/OmniRoute/pull/11735)) +- **docs(assets):** retired two raster images whose redistribution provenance could not be verified ([#11736](https://github.com/diegosouzapw/OmniRoute/pull/11736)) — PicoClaw documentation now reuses the existing SVG, while Zoo Code remains a text link without an image. +- **docs(legal):** add pinned notices for six LobeHub-derived provider SVGs and 65 byte-exact theSVG assets, retaining registry claims as unverified metadata with explicit trademark and HOLD boundaries ([#11737](https://github.com/diegosouzapw/OmniRoute/pull/11737)). +- **chore(quality):** added a versioned 225/225 provider-asset provenance manifest and blocking drift gate ([#11743](https://github.com/diegosouzapw/OmniRoute/pull/11743)) — the inventory pins magic MIME and SHA-256 for every physical file, records immutable source matches for 71 assets, keeps 69 probable and 85 unresolved entries explicit but non-blocking, and treats provenance as source evidence rather than copyright or trademark clearance. +- **refactor(mitm):** replace four previously derived Traffic Inspector utilities — conversation normalization, SSE merging, secret masking, and header sanitization — with independent clean-room implementations based on public provider schemas, WHATWG SSE, and RFC 6750, while keeping the two independently classified passthrough surfaces unchanged ([#11748](https://github.com/diegosouzapw/OmniRoute/pull/11748)). +- Added a unit test that fails seven days before any dated pack under `config/` (`validUntil` and sibling keys) lapses, naming the file and key. The Alibaba free-tier pack expired on 2026-08-27 and turned every PR red the next morning with no commit involved; renewal now happens on someone's terms, not the clock's. ([#11891](https://github.com/diegosouzapw/OmniRoute/pull/11891)) +- `check:workflows` now fails (under `--strict`/`--ratchet`) when any job routed to a self-hosted runner publishes with `--provenance` — npm rejects that with `422` at the registry, which in v3.8.50 only surfaced after the tag and Docker images were public. ([#11895](https://github.com/diegosouzapw/OmniRoute/pull/11895)) +- `scripts/ops/runner-janitor.sh` now proves a path is idle with one `lsof` snapshot and removes stale leftovers itself (tmpfs after 3 h — it is RAM — disk after 24 h), kills orphan `next-build` processes, prunes checkouts of stopped runners, and alerts on memory pressure; `--dry-run` shows exactly what it would do. `docs/ops/RUNNER_BOX.md` reconciled to the measured box (31 GB, 10 listeners). ([#11893](https://github.com/diegosouzapw/OmniRoute/pull/11893)) +- The `next-build` artefact (1.3 GB) is now written and read under `$RUNNER_TEMP` (per-runner, on disk) instead of `/tmp`, which on the self-hosted pool is a 12 GB tmpfs in RAM. Landing it there took 27–32 of the publish job's 76 minutes, and the fixed `/tmp/e2e-build.tar.gz` name let E2E jobs on different runners overwrite each other's download. ([#11896](https://github.com/diegosouzapw/OmniRoute/pull/11896)) +- The CI `build` job now runs in two concurrency lanes — `main` and pull requests — so a release build is never queued behind (or OOM-killed beside) PR builds on the self-hosted pool, which holds one `next-build` comfortably and two at the edge. ([#11901](https://github.com/diegosouzapw/OmniRoute/pull/11901)) +- Re-freeze the ESLint suppressions on `release/v3.8.51` from a clean-room measurement (2 stale file entries pruned, 55 pre-existing `no-explicit-any` in six new files frozen under #11924) and drop the dead `GPT_SIZE_MAP` constant orphaned by the Adobe Firefly client split, so `No new ESLint warnings` stops failing every PR with exit 2 (Refs #11924) ([#11955](https://github.com/diegosouzapw/OmniRoute/pull/11955)) +- Type the 55 `no-explicit-any` sites that had been frozen under #11924 — four redundant casts in `socksConnectorWithFamily.ts` (undici/socks types already accept them) and the mocks/fixtures of the socks-timeout and isFree suites — and drop their suppression entries; the ESLint ratchet shrinks from 5487 to 5432 (Closes #11924) ([#11975](https://github.com/diegosouzapw/OmniRoute/pull/11975)) +- Take the two hosted-runner builds off the PR rail: `Build App` (`build.yml`) is `workflow_dispatch`-only and `DAST smoke (PR)` runs only for PRs into `main` — the 7 GB hosted VM cannot build `release/v3.8.51` in any profile (19/30 red, VM shutdown ~8 min into `next build`) and both checks had turned into permanent noise on every release PR; the bundle stays validated by `ci.yml` on `main` and by `nightly-release-green` on `release/**` (Closes #11946) ([#11962](https://github.com/diegosouzapw/OmniRoute/pull/11962)) +- Move the four nightly jobs that build the backend (`nightly-schemathesis`, `nightly-llm-security` promptfoo + garak, `nightly-resilience` axe-a11y) off the hosted 7 GB runner — where they died on `release/v3.8.51` unseen — onto the box's new `omni-light` pool (two listeners), and document the reshaped fleet (4 active OmniRoute listeners: 2 `omni-build` + 2 `omni-light`, janitor ceiling 4) (Closes #11965) ([#11967](https://github.com/diegosouzapw/OmniRoute/pull/11967)) +- Stop painting every fork PR into `release/**` red: `quality.yml` `Build (advisory)` is skipped (GitHub still reports `continue-on-error` failures as check FAILURE). Hosted `ubuntu-latest` cannot finish `npm run build` on this tree — same class as #11962 taking `build.yml` off the PR rail. `docker-publish.yml` amd64 now runs on the `.113` `omni-build` pool (31 GB / 32 cores, two listeners) with Turbopack, shares the `heavy-build-main` lane with `ci.yml` `Build` so it queues instead of becoming a third heavy, and keeps per-ref concurrency (a merge storm was starting 8 concurrent OOM builds). arm64 stays on `ubuntu-24.04-arm` with webpack — there is no ARM box. Fallback when `USE_VPS_RUNNER` is off: hosted amd64 + webpack (#11976). ([#12021](https://github.com/diegosouzapw/OmniRoute/pull/12021)) +- `docker-publish.yml` builds with webpack (`OMNIROUTE_USE_TURBOPACK=0`) on **both** arches, including amd64 on the `.113` `omni-build` pool. Turbopack had the RAM (31 GB, `omniroute-113-6`) and still panicked (`TurbopackInternalError: there must be a path to a root`, run 33253576569, 26 min in). The same tree's arm64 webpack build on hosted `ubuntu-24.04-arm` succeeded (run 33264823398). Dockerfile already documents webpack as the Docker escape hatch (#11976). ([#12050](https://github.com/diegosouzapw/OmniRoute/pull/12050)) +- **docs(free-tier):** declare the counting vs deciding regimes for "is it free?" and guard the deciding path from DB-backed catalog resolution ([#12226](https://github.com/diegosouzapw/OmniRoute/pull/12226)) — thanks @maxmad64bis +- **docs(auto-combo):** The mode pack table in `docs/routing/AUTO-COMBO.md` now lists all six shipped packs with every weight each one sets, replacing a four-pack table whose numbers had also drifted from the source. It states plainly that no pack sets `quality`, so selecting any pack silences the observed-quality signal. Six more documents that quote the scoring factor count joined the `check:docs-counts` gate, which caught five stale claims — including one naming nine factors that do not exist — and two stale mode pack counts. The dashboard routing panel, which offered four of the six packs and labelled the default strategy "6-Factor Scoring", is now covered by a test; the two packs it was missing are `reliability-first` and `chaos-mode`, the latter labelled as the fault-injection profile it is rather than as one more routing preference ([#12316](https://github.com/diegosouzapw/OmniRoute/pull/12316)) — thanks @maxmad64bis +- **chore(quality):** rebaseline `src/lib/db/apiKeys.ts` for the ACL the key-creation path now preserves ([#12352](https://github.com/diegosouzapw/OmniRoute/pull/12352)) ([#12673](https://github.com/diegosouzapw/OmniRoute/pull/12673)) — thanks @KrzysiekSko +- **chore(providers):** bump the Claude Code wire identity and the Devin bridge image pin from `2.1.220` to `2.1.258` ([#12402](https://github.com/diegosouzapw/OmniRoute/pull/12402)) — thanks @ggiak ([#12604](https://github.com/diegosouzapw/OmniRoute/pull/12604)) +- **docs(env):** align `.env.example`, the README Bun section, and the troubleshooting guide with the code: `OMNIROUTE_USE_TURBOPACK` also governs `npm run build` (not dev-only), `bun run build` follows that flag instead of auto-selecting Webpack, `OMNIROUTE_CHAT_MAX_HEAVY_IN_FLIGHT` is unset by default (no request-count cap), and the structural `503 chat_admission_busy` message matches `chatAdmissionResponses.ts` (#12404 — thanks @pacocartones) +- **chore(electron):** upgrade the desktop app to Electron 44 (Chromium 152, Node 24.18.1) ([#12217](https://github.com/diegosouzapw/OmniRoute/pull/12217)). **Requires macOS 13 (Ventura) or later** — Chromium dropped macOS 12 (Monterey), so Monterey users must stay on an earlier OmniRoute desktop build. Windows and Linux are unaffected; the app already shipped only x64/arm64, so Electron 44 dropping 32-bit builds changes nothing. Removes the `openAsHidden`/`wasOpenedAsHidden` login-item fields deleted in Electron 44 — hidden autostart continues to work through the `--hidden` argument registered with the login item ([#12554](https://github.com/diegosouzapw/OmniRoute/pull/12554)) — thanks @app/dependabot +- **chore(quality):** rebaseline `src/sse/handlers/chat.ts` for the effective-input persistence the continuation fix needs ([#12641](https://github.com/diegosouzapw/OmniRoute/pull/12641)) ([#12680](https://github.com/diegosouzapw/OmniRoute/pull/12680)) — thanks @hartmark +- `Coverage` job on `ci.yml`: the informational Codecov upload gets its own 5-minute ceiling and `continue-on-error`, and the job budget grows from 20 to 30 minutes (the 8-shard c8 merge alone takes ~10) — a stalled upload no longer ends the job `cancelled` and drags a fully green `main` run's conclusion down with it ([#11972](https://github.com/diegosouzapw/OmniRoute/pull/11972)) +- CI hardening on the PR rail: the four `Unit Tests fast-path` shards get `timeout-minutes: 30` (a hung shard held a PR for 64 min instead of GitHub's 6 h default) and both ESLint file caches lose their `restore-keys` fallback, so a cache built under another suppressions file or lint config can no longer report stale verdicts (Refs #11600, #11924) ([#11963](https://github.com/diegosouzapw/OmniRoute/pull/11963)) +- Every CI job that runs a `next build` (`build`, the npm `publish`, both release-green validations) now targets the `omni-build` runner label, which only two of the eight self-hosted runners carry. The box holds one build comfortably and two at the edge; a third now queues on GitHub instead of being OOM-killed by the kernel. ([#11932](https://github.com/diegosouzapw/OmniRoute/pull/11932)) +- **refactor(combo):** move `handleRoundRobinCombo` (and `resolveTargetTokenLimit`) into `open-sse/services/combo/roundRobinCombo.ts`. `combo.ts` drops from 2164 to 1014 lines (`split("\n").length`); the leaf is 1199 (under the 1200 new-file cap). The round-robin call site uses a dynamic `import()` so `releaseStickyPinOnFailure` / `clearStaleLKGP` can stay exported from `combo.ts` without a static cycle. Skip / sticky / semaphore / safety-timer behavior is unchanged. ([#12811](https://github.com/diegosouzapw/OmniRoute/pull/12811)) — thanks @HouMinXi +- **chore(quality):** rebaseline the file-size caps the error-boundary campaign grew past (`open-sse/executors/codex.ts`, `open-sse/vendor/codex-chatgpt-web/bridge.ts`, both via [#12444](https://github.com/diegosouzapw/OmniRoute/pull/12444)) ([#12654](https://github.com/diegosouzapw/OmniRoute/pull/12654)) +- **chore(quality):** rebaseline the file-size caps the hartmark batch grew past (`combos/page.tsx` via [#12355](https://github.com/diegosouzapw/OmniRoute/pull/12355), `open-sse/services/combo.ts` via [#12338](https://github.com/diegosouzapw/OmniRoute/pull/12338)) ([#12623](https://github.com/diegosouzapw/OmniRoute/pull/12623)) — thanks @hartmark +- **chore(quality):** rebaseline the file-size caps the HouMinXi batch grew past when its PRs stacked (`providers/page.tsx`, `chatCore.ts`, `accountFallback.ts`) — each PR measured correctly in isolation, none saw the stacking ([#12619](https://github.com/diegosouzapw/OmniRoute/pull/12619)) +- **chore(quality):** rebaseline `open-sse/services/combo.ts` for the reset-aware scoring the HouMinXi batch stacked ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) ([#12678](https://github.com/diegosouzapw/OmniRoute/pull/12678)) — thanks @HouMinXi +- **docs:** sync provider catalog count (direct commit `4f11b2ae3d`) +- **docs(skills):** sync omni-inference search fallback (direct commit `b4e76a7ed9`) +- **chore(lint):** prune two stale base suppressions (direct commit `1325047d56`) +- **test(auto-combo):** tolerate full-suite rotation load (direct commit `ad3e293f9f`) +- **test:** drain inherited quality snapshot reds (direct commit `21ed68d8ac`) +- **chore(quality):** rebaseline measured CLI bundle growth (direct commit `d35b3f9779`) +- **test(ui):** reproduce invalid AnySearch icon alias (direct commit `12cc6ca834`) +- **test(translator):** fix the relative imports of the relocated deferred-finish test (direct commit `777d9d1629`) +- **docs(counts):** bring the documented provider and migration counts to the measured values (direct commit `e70bea30e9`) +- **test(security):** drop the guard-target set left unused by the sync (direct commit `a32ba0e362`) +- **chore(db):** renumber model_capabilities migration 163 → 169 (cross-branch collision with 163_radar_feed_cache_generated_at) (direct commit `70f5d4cbf9`) +- **docs(cli):** document CLI_PRIME_AGENT_BIN and correct the CLI Agents count (direct commit `9058e39b61`) +- **chore(deps):** refresh runtimes and adopt ESLint 10 ([#11259](https://github.com/diegosouzapw/OmniRoute/pull/11259)) — thanks @backryun +- **refactor(local-corpus):** implement dynamic root resolution and LRU cache ([#11491](https://github.com/diegosouzapw/OmniRoute/pull/11491)) — thanks @raheemuddin786 +- **chore(quality):** prune stale eslint-suppressions entries ([#11596](https://github.com/diegosouzapw/OmniRoute/pull/11596)) +- **chore(ci):** unblock quality gates - env-doc sync + stryker tap.testFiles registration (#9282) ([#11609](https://github.com/diegosouzapw/OmniRoute/pull/11609)) — thanks @Meet6338-X +- **chore(ci):** point the circular-deps gate at dpdm's real JS entrypoint ([#11615](https://github.com/diegosouzapw/OmniRoute/pull/11615)) — thanks @benzntech +- **chore(quality):** rebaseline file-size for #11448/#11495/#11561 growth ([#11630](https://github.com/diegosouzapw/OmniRoute/pull/11630)) +- **deps:** realign onnxruntime-node pin to @huggingface/transformers' 1.24.3 lockstep ([#11633](https://github.com/diegosouzapw/OmniRoute/pull/11633)) — thanks @jonlwheat2-gif +- **test(autoCombo):** port models_dev_tier #11508 guard from node:test to vitest ([#11635](https://github.com/diegosouzapw/OmniRoute/pull/11635)) — thanks @jonlwheat2-gif +- **chore(quality):** rebaseline file-size for #11083/#11631/#11628 growth ([#11636](https://github.com/diegosouzapw/OmniRoute/pull/11636)) +- **chore(lint):** prune stale ESLint suppressions (base-red #11449) ([#11644](https://github.com/diegosouzapw/OmniRoute/pull/11644)) — thanks @jonlwheat2-gif +- **docs(changelog):** aggregate the ten v3.8.50 fragments into CHANGELOG.md ([#11683](https://github.com/diegosouzapw/OmniRoute/pull/11683)) +- **chore(search):** drop unreachable credential sweep after #11642 ([#11687](https://github.com/diegosouzapw/OmniRoute/pull/11687)) — thanks @HouMinXi +- **ci:** add API route TypeScript regression gate ([#11705](https://github.com/diegosouzapw/OmniRoute/pull/11705)) — thanks @Sabeekhann +- **docs(dedup):** correct the CodeQL claim on the tenant-namespace comment ([#11712](https://github.com/diegosouzapw/OmniRoute/pull/11712)) +- **docs(changelog):** add consolidated v3.8.50 stats, top-25 ranking and a .mailmap ([#11715](https://github.com/diegosouzapw/OmniRoute/pull/11715)) +- **docs:** recommend gstack for AI-assisted workflows ([#11770](https://github.com/diegosouzapw/OmniRoute/pull/11770)) — thanks @santosraju99-hub +- **chore(quality):** tighten complexity/cognitive-complexity ratchets to the current tip; land the missed gateways.ts rebaseline ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **docs(i18n):** finish Freepik → Magnific rebrand in locale strings and README ([#11772](https://github.com/diegosouzapw/OmniRoute/pull/11772)) — thanks @turbolego +- **refactor(auto-combo):** fix divergent scoring in combo health reporting ([#11854](https://github.com/diegosouzapw/OmniRoute/pull/11854)) — thanks @jacobsparts +- **chore(changelog):** add missing fragments for #11919/#11918/#11916 ([#11938](https://github.com/diegosouzapw/OmniRoute/pull/11938)) +- **test(check):** escape the runs-on fixture with JSON.stringify, not a quote-only replace ([#11942](https://github.com/diegosouzapw/OmniRoute/pull/11942)) (direct commit `fb7445eaa3`) +- **test(release):** align five suites with the contracts #11933, #11919 and #11876 shipped on release/v3.8.51 ([#11944](https://github.com/diegosouzapw/OmniRoute/pull/11944)) +- **chore(quality):** remove unreachable code and restore test discovery ([#11950](https://github.com/diegosouzapw/OmniRoute/pull/11950)) — thanks @backryun +- **docs(agents):** sync-back landings are fast-forward, never squash ([#11964](https://github.com/diegosouzapw/OmniRoute/pull/11964)) +- **test(infra):** retry recursive temp-dir removal instead of failing a shard on ENOTEMPTY (#11966) ([#11968](https://github.com/diegosouzapw/OmniRoute/pull/11968)) +- **docs(ops):** the .113 heavy-build ceiling is one runner, not two ([#12048](https://github.com/diegosouzapw/OmniRoute/pull/12048)) +- **test(ui):** correct inactive auto-fetch expectation ([#12098](https://github.com/diegosouzapw/OmniRoute/pull/12098)) — thanks @RaviTharuma +- **docs:** document native dependency check escape hatch ([#12101](https://github.com/diegosouzapw/OmniRoute/pull/12101)) — thanks @RaviTharuma +- **test(providers):** regenerate the translate-path golden for OrcaRouter (#11923) ([#12118](https://github.com/diegosouzapw/OmniRoute/pull/12118)) +- **chore(quality):** velocity phase — loosen every numeric baseline by 20% until v4.0, monitor headroom nightly ([#12125](https://github.com/diegosouzapw/OmniRoute/pull/12125)) +- **chore(lint):** batch 0 of #12146 — type the call-log-cap sqlite rows instead of 45 as-any casts ([#12157](https://github.com/diegosouzapw/OmniRoute/pull/12157)) +- **chore(lint):** batch 4 of #12146 — shared/components react-hooks violations resolved ([#12159](https://github.com/diegosouzapw/OmniRoute/pull/12159)) +- **chore(lint):** batch 1 of #12146 — dashboard/cli-code react-hooks violations resolved ([#12160](https://github.com/diegosouzapw/OmniRoute/pull/12160)) +- **chore(lint):** batch 3 of #12146 — dashboard/settings react-hooks violations resolved ([#12162](https://github.com/diegosouzapw/OmniRoute/pull/12162)) +- **chore(lint):** batch 2 of #12146 — resolve the react-hooks compiler violations in dashboard/providers ([#12163](https://github.com/diegosouzapw/OmniRoute/pull/12163)) +- **chore(quality):** register search-432 cooldown test in stryker tap.testFiles ([#12170](https://github.com/diegosouzapw/OmniRoute/pull/12170)) +- **chore(lint):** batch 5 of #12146 — combos, endpoint, provider-stats, api-manager and costs react-hooks violations resolved ([#12174](https://github.com/diegosouzapw/OmniRoute/pull/12174)) +- **build:** add contributor fast profile ([#12192](https://github.com/diegosouzapw/OmniRoute/pull/12192)) — thanks @rafacpti23 +- **docs(readme):** README touch-ups ([#12193](https://github.com/diegosouzapw/OmniRoute/pull/12193)) ([#12194](https://github.com/diegosouzapw/OmniRoute/pull/12194)) ([#12202](https://github.com/diegosouzapw/OmniRoute/pull/12202)) — thanks @rafacpti23 +- **build:** force the webpack bundler in the contributor build profile ([#12197](https://github.com/diegosouzapw/OmniRoute/pull/12197)) — thanks @rafacpti23 +- **build:** skip standalone packaging in the contributor profile ([#12198](https://github.com/diegosouzapw/OmniRoute/pull/12198)) — thanks @rafacpti23 +- **docs(audit):** align every published number with the code and harden check:docs-counts ([#12200](https://github.com/diegosouzapw/OmniRoute/pull/12200)) +- **build:** stub the instrumentation entrypoints in the contributor profile ([#12203](https://github.com/diegosouzapw/OmniRoute/pull/12203)) — thanks @rafacpti23 +- **build:** omit the standalone output target for contributor builds ([#12204](https://github.com/diegosouzapw/OmniRoute/pull/12204)) — thanks @rafacpti23 +- **chore(lint):** batch 6 of #12146 — memory, radar, audit, analytics, cache, usage, activity, home and RequestLoggerV2 react-hooks violations resolved ([#12208](https://github.com/diegosouzapw/OmniRoute/pull/12208)) +- **docs(diagrams):** rename number-carrying diagram files to stable names ([#12210](https://github.com/diegosouzapw/OmniRoute/pull/12210)) +- **docs(dashboard):** redraw onboarding tier-flow SVGs for the real 4-tier model ([#12211](https://github.com/diegosouzapw/OmniRoute/pull/12211)) +- **docs(api):** document every implemented route in openapi.yaml (276 → 692 paths) ([#12212](https://github.com/diegosouzapw/OmniRoute/pull/12212)) +- **chore(lint):** batch 7 of #12146 — final src tail: 37 react-hooks violations across 33 files resolved ([#12244](https://github.com/diegosouzapw/OmniRoute/pull/12244)) +- Revert "docs: recommend gstack for AI-assisted workflows (#11770)" ([#12248](https://github.com/diegosouzapw/OmniRoute/pull/12248)) ([#12249](https://github.com/diegosouzapw/OmniRoute/pull/12249)) +- **docs:** Chaos Mode setup guide + weighted strategy semantics ([#12250](https://github.com/diegosouzapw/OmniRoute/pull/12250)) +- **docs(agents):** protected-surface merge rule — operator approval for agent-instruction files ([#12253](https://github.com/diegosouzapw/OmniRoute/pull/12253)) +- **chore(quality):** register native-codex turn-pin tests in stryker tap.testFiles ([#12263](https://github.com/diegosouzapw/OmniRoute/pull/12263)) +- **chore(quality):** dedupe tap.testFiles entries added by racing base-red fixes ([#12265](https://github.com/diegosouzapw/OmniRoute/pull/12265)) +- **docs(readme):** introduce OmniRouteTray — the macOS menu-bar companion ([#12276](https://github.com/diegosouzapw/OmniRoute/pull/12276)) — thanks @ggiak +- **chore(proxy):** purge the legacy 1proxy residue — sync/rotator modules, dead DB exports, dead settings tab and flag, docs (#12091) ([#12290](https://github.com/diegosouzapw/OmniRoute/pull/12290)) +- **chore(quality):** baseline-headroom skips generated and vendored files in the fileSize worst-file signal ([#12291](https://github.com/diegosouzapw/OmniRoute/pull/12291)) +- **refactor(video):** unify the JPEG frame data-URI contract ([#12322](https://github.com/diegosouzapw/OmniRoute/pull/12322)) +- **chore(deps):** freeze onnxruntime-node and eslint-plugin-react-hooks out of dependabot groups ([#12329](https://github.com/diegosouzapw/OmniRoute/pull/12329)) +- **test(executors):** restore the #10986 reasoning-only fallback guards ([#12364](https://github.com/diegosouzapw/OmniRoute/pull/12364)) — thanks @pacocartones +- **chore(quality):** re-tighten the file-size ratchet to the real LOC (plan 3.8.52 task 0) ([#12411](https://github.com/diegosouzapw/OmniRoute/pull/12411)) +- **test(grok):** format web executor suite ([#12412](https://github.com/diegosouzapw/OmniRoute/pull/12412)) +- **chore(quality):** ratchet open-sse typecheck baseline to zero ([#12418](https://github.com/diegosouzapw/OmniRoute/pull/12418)) +- **test(build):** guard the artifact path policy arrays against duplicates ([#12422](https://github.com/diegosouzapw/OmniRoute/pull/12422)) +- **chore(lint):** adopt eslint-plugin-react-hooks 7.1.1 ([#12428](https://github.com/diegosouzapw/OmniRoute/pull/12428)) +- **chore(providers):** remove a keyless provider integration at its operator's request ([#12440](https://github.com/diegosouzapw/OmniRoute/pull/12440)) +- **docs(providers):** register providers removed at their operator's request and guard against reintroduction ([#12478](https://github.com/diegosouzapw/OmniRoute/pull/12478)) +- **chore(quality):** base fixes — stryker tap.testFiles + node_modules cache key ([#12482](https://github.com/diegosouzapw/OmniRoute/pull/12482)) +- **docs(arch):** one-process recipe for tens of long /v1/responses ([#12493](https://github.com/diegosouzapw/OmniRoute/pull/12493)) — thanks @RaviTharuma +- **chore(quality):** tighten the CodeQL ratchet baseline from 11 to 6 ([#12530](https://github.com/diegosouzapw/OmniRoute/pull/12530)) +- **chore(deps):** pin browserslist override to ^4.28.8 ([#12592](https://github.com/diegosouzapw/OmniRoute/pull/12592)) — thanks @KooshaPari +- **chore(deps):** bump fast-uri to 3.1.7 in the electron lockfile ([#12601](https://github.com/diegosouzapw/OmniRoute/pull/12601)) +- **docs(quality):** document how the CodeQL ratchet refreshes and how to tighten it ([#12611](https://github.com/diegosouzapw/OmniRoute/pull/12611)) +- **test(ci):** pin the openapi-security-tiers two-arm contract with an executing gate test (#12581) ([#12652](https://github.com/diegosouzapw/OmniRoute/pull/12652)) +- **docs(gamification):** align XP Rewards table with code (#12501) ([#12667](https://github.com/diegosouzapw/OmniRoute/pull/12667)) — thanks @KooshaPari +- **refactor(combo):** split executeTarget into gates, attempt, and loop ([#12746](https://github.com/diegosouzapw/OmniRoute/pull/12746)) — thanks @HouMinXi +- **docs(security):** document socket.yml scanner config + CI workflow link (#12575) ([#12764](https://github.com/diegosouzapw/OmniRoute/pull/12764)) — thanks @KooshaPari +- **chore(ci):** guard commit identity in pre-commit to stop author misattribution ([#12772](https://github.com/diegosouzapw/OmniRoute/pull/12772)) +- **deps:** 17 Dependabot bumps — bump electron from 43.4.0 to 43.4.1 in /electron ([#11426](https://github.com/diegosouzapw/OmniRoute/pull/11426)); bump the development group across 1 directory with 8 updates ([#11428](https://github.com/diegosouzapw/OmniRoute/pull/11428)); bump the production group across 1 directory with 11 updates ([#11440](https://github.com/diegosouzapw/OmniRoute/pull/11440)); chore(deps): bump github/codeql-action/init from 4.37.7 to 4.37.8 ([#11925](https://github.com/diegosouzapw/OmniRoute/pull/11925)); chore(deps): bump github/codeql-action from 4.37.7 to 4.37.8 ([#11926](https://github.com/diegosouzapw/OmniRoute/pull/11926)); chore(deps): bump github/codeql-action/analyze from 4.37.7 to 4.37.8 ([#11927](https://github.com/diegosouzapw/OmniRoute/pull/11927)); build(deps): bump github/codeql-action/init from 4.37.8 to 4.37.9 ([#12345](https://github.com/diegosouzapw/OmniRoute/pull/12345)); build(deps): bump github/codeql-action/analyze from 4.37.8 to 4.37.9 ([#12346](https://github.com/diegosouzapw/OmniRoute/pull/12346)); bump the development group across 1 directory with 2 updates ([#12347](https://github.com/diegosouzapw/OmniRoute/pull/12347)); build(deps): bump github/codeql-action from 4.37.8 to 4.37.9 ([#12349](https://github.com/diegosouzapw/OmniRoute/pull/12349)); bump browserslist from 4.28.2 to 4.28.8 ([#12396](https://github.com/diegosouzapw/OmniRoute/pull/12396)); bump the production group across 1 directory with 4 updates ([#12399](https://github.com/diegosouzapw/OmniRoute/pull/12399)); bump @xmldom/xmldom ([#12500](https://github.com/diegosouzapw/OmniRoute/pull/12500)); bump qs from 6.15.2 to 6.16.0 ([#12512](https://github.com/diegosouzapw/OmniRoute/pull/12512)); bump @xmldom/xmldom from 0.9.10 to 0.9.12 ([#12513](https://github.com/diegosouzapw/OmniRoute/pull/12513)); bump fast-uri from 3.1.5 to 3.1.7 ([#12514](https://github.com/diegosouzapw/OmniRoute/pull/12514)); bump @humanfs/node from 0.16.7 to 0.16.8 ([#12515](https://github.com/diegosouzapw/OmniRoute/pull/12515)) +- Clear the `release/v3.8.51` `check:agent-skills-sync` base-red: regenerate `skills/cli-tunnel/SKILL.md` so the `tunnel create [type]` positional that #13009 taught the generator to read is reflected in the committed skill. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` typecheck base-red from #12731: the new `Mode pack ranking gates` candidates in `open-sse/services/autoCombo/__tests__/autoCombo.test.ts` omitted the required `provider`, `model` and `errorRate` fields of `ProviderCandidate`, failing `check:open-sse-typecheck` (and with it `Fast Quality Gates`) on every open PR. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` docs-sync base-red: EURouter (#13025) and GreenPT (#13024) took the live provider count to 358, leaving 7 STRICT drifts (`PROVIDER_REFERENCE.md`, 4 diagrams, 2 tier-flow images) plus the `AGENTS.md` / `llm.txt` / `package.json` count claims stale. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- **test(models):** Pin `CATALOG_BUILD_TIMEOUT_MS` in the #12058 canonical-catalog regression so a cold catalog build racing the 8s default bound no longer flakes the suite with a 500 ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Allow `@omniroute/opencode-plugin-v2/` in the npm pack policy — [#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870) shipped the v2 plugin beside its v1 sibling without widening the allowlist, so every packed file under it read as an unexpected artifact and failed `check:pack-policy` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Point the release-green validator's pack gate at the tree it is validating — the build-provenance guard defaulted to `origin/main`, which a release branch tip can never be an ancestor of mid-cycle, so `Package artifact` reported a false red on every run and the tarball boot-smoke cascaded off it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **docs(checks):** keep doc counts honest — headings, rankings, catalog, weights, quality gate and scoring diagram now covered ([#12507](https://github.com/diegosouzapw/OmniRoute/pull/12507)) — thanks @maxmad64bis +- **chore(lifecycle):** `check:model-lifecycle` now also diffs `DEFAULT_DEGRADATION_MAP` (the background-task redirect table) against the vendor lifecycle snapshot, refusing a retired id as source or target, with a table-driven unit test beside it. Three rows whose source the vendor had retired — `claude-sonnet-4-20250514`, `gemini-3-pro-preview` and `gpt-5.1-codex` (whose target `gpt-5.1-codex-mini` is retired too) — were dead code, since `checkLifecycle` answers 410 before the redirect runs; they are dropped (#12535 — thanks @pacocartones) +- **test(compression):** cover idle worker eviction at the resource level — the pool must call `terminate()` and must not retain the worker's `MessagePort`, complementing the `exit`-event assertion added with the fix ([#13371](https://github.com/diegosouzapw/OmniRoute/pull/13371)) +- **chore(quality):** raise the `imageGeneration.ts` file-size ceiling for the image-only-model guard that clears the #12945 base-red ([#13392](https://github.com/diegosouzapw/OmniRoute/pull/13392)) +- **chore(quality):** type the combo-test route's JSON response bodies instead of casting them to `any`, drop the now-empty suppression entry, and remove the `hasPerModelQuota` import `chatCore.ts` stopped using when the failure-classification helper was extracted ([#13379](https://github.com/diegosouzapw/OmniRoute/pull/13379)) +- **chore(electron):** the desktop bundle no longer ships the root-level files of every translated docs mirror (`README.md`, `llm.txt`, `CLAUDE.md`, `GEMINI.md`, `CONTRIBUTING.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`) — the packaged app only reads `docs/i18n//docs/**`, which stays. Saves ~11 MB on top of the translated CHANGELOGs already pruned. (#0000) ([#13235](https://github.com/diegosouzapw/OmniRoute/pull/13235)) +- **chore(quality):** retighten the `src/sse/handlers/chat.ts` unused-vars suppression count to the 8 violations that actually remain ([#13385](https://github.com/diegosouzapw/OmniRoute/pull/13385)) +- Bring the documented counts back in line with the code: 171 migrations (was 169) and 20 routing strategies (was 19), plus the regenerated `cli-tunnel` skill reference. ([#12970](https://github.com/diegosouzapw/OmniRoute/pull/12970)) +- Update the documented migration count to 172 after the call-logs provider-stats indexes landed. ([#13209](https://github.com/diegosouzapw/OmniRoute/pull/13209)) +- **chore(ci):** two gates that close the blind spots behind the exclusions above. `check:vitest-exclusions` requires every Vitest exclusion to name a tracking issue and to appear in `config/quality/vitest-exclusions.json` — the previous list grew to 62 files behind a comment pointing at an issue that had been closed for a month. `check-new-key-coverage` requires a key newly added to `en.json` to reach every locale; the existing coverage gate is a percentage floor per locale, so eleven absent keys out of ~13,000 left it at 99.9% while a whole feature shipped untranslated in nine languages. Both are diff-aware, so pre-existing debt stays frozen and neither needed a migration to turn on. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **build:** root layout no longer loads Inter from `next/font/google`, so a production image build does not need fonts.googleapis.com ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **docs(i18n):** bring the eight new `llm.txt` mirrors (km, kn, ml, my, ne, or, pa, si) up to the current provider and migration counts, so `check-docs-sync` stops rejecting every commit ([#13674](https://github.com/diegosouzapw/OmniRoute/pull/13674)) +- **refactor(ui):** flow surfaces (home topology, combo live studio, compression cockpit/waterfall/nodes, token-health badge) express state through the theme-aware `--orch-status-*` tokens instead of fixed dark-mode hex, so green/red/amber/grey stay legible in the light theme; dark mode is byte-identical. Categorical palettes (routing-strategy hues, compression-layer pills, provider brand colors) deliberately stay hex ([#12378](https://github.com/diegosouzapw/OmniRoute/issues/12378)) ([#13203](https://github.com/diegosouzapw/OmniRoute/pull/13203)) +- **chore(tests):** 51 test files that had been excluded from Vitest are running again, restoring roughly 350 assertions to the blocking suite. Each was measured individually first: of the 62 files parked behind the `// #8618 — pre-existing failure` comment, 51 pass against the current tree with no source change, so the exclusions had outlived the failures they were added for. The 11 that genuinely still fail stay excluded, but now point at a live tracker (#13204) rather than at #8618, which was closed in August while the list it tracked kept growing. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **chore(quality):** drop two ESLint suppression entries whose violations no longer exist, so `eslint --suppressions-location` stops rejecting every commit that touches the surrounding files ([#13384](https://github.com/diegosouzapw/OmniRoute/pull/13384)) +- **chore(release):** bring main's post-tag fixes into release/v3.8.51 — sync-back step 2/2 (direct commit `21c488f210`) +- **chore(deps):** patch toml and esbuild in the opencode plugin lockfiles ([#12965](https://github.com/diegosouzapw/OmniRoute/pull/12965)) +- **docs(changelog):** reconcile the v3.8.51 living section — fold 366 fragments, cover every cycle commit, credit every contributor ([#12971](https://github.com/diegosouzapw/OmniRoute/pull/12971)) +- **test(plugin):** wait for the v2 lazy-refresh tier instead of sleeping past it ([#12984](https://github.com/diegosouzapw/OmniRoute/pull/12984)) +- **chore(quality):** rebaseline chatCore.ts after the non-streaming regression fixes ([#13045](https://github.com/diegosouzapw/OmniRoute/pull/13045)) +- **docs(changelog):** add the missing bullet marker to the DeepSeek PoW fragment ([#13200](https://github.com/diegosouzapw/OmniRoute/pull/13200)) +- **chore(deps):** drain the Dependabot queue — 10 of 13 alerts ([#13213](https://github.com/diegosouzapw/OmniRoute/pull/13213)) +- **chore(ci):** make worktrees carry the hooks that enforce the identity gate ([#13236](https://github.com/diegosouzapw/OmniRoute/pull/13236)) +- **chore(deps):** pin csv-parse to ^7.0.2 via overrides (#13117) ([#13300](https://github.com/diegosouzapw/OmniRoute/pull/13300)) — thanks @KooshaPari +- **chore(deps):** bump hono from ^4.12.34 to ^4.13.7 (#13148) ([#13301](https://github.com/diegosouzapw/OmniRoute/pull/13301)) — thanks @KooshaPari +- **chore(deps):** pin joi to ^18.2.8 via overrides (#13085) ([#13302](https://github.com/diegosouzapw/OmniRoute/pull/13302)) — thanks @KooshaPari +- **docs(providers):** add Microsoft 365 Copilot (BizChat) provider guide (#12779) ([#13340](https://github.com/diegosouzapw/OmniRoute/pull/13340)) — thanks @KooshaPari +### 🙌 Contributors + +Thanks to everyone whose work landed in v3.8.51: + +| Contributor | PRs / Issues | +| --- | --- | +| [@5dive-bot](https://github.com/5dive-bot) | #11852 | +| [@abhisheksharma2411](https://github.com/abhisheksharma2411) | #11547, #11684, #11959, #12209 | +| [@adevwithpurpose](https://github.com/adevwithpurpose) | #11464 | +| [@adivekar-utexas](https://github.com/adivekar-utexas) | #12015, #12043, #12090, #12138 | +| [@AIB1TAL0S](https://github.com/AIB1TAL0S) | #12103 | +| [@alltomatos](https://github.com/alltomatos) | #11448 | +| [@alvinveroy](https://github.com/alvinveroy) | #11857, #12027, #12165 | +| [@amaleta](https://github.com/amaleta) | #11906, #11934 | +| [@amartinawi](https://github.com/amartinawi) | #11575 | +| [@AndrianBalanescu](https://github.com/AndrianBalanescu) | #11083, #11830, #11834, #11844 | +| [@anhtahaylove](https://github.com/anhtahaylove) | #13091, #13092, #13093, #13096, #13097, #13100, #13106, #13109, #13114, #13155, #13166, #13171, #13175, #13187, #13196 | +| [@aniruddhaadak80](https://github.com/aniruddhaadak80) | #11760 | +| [@app](https://github.com/app) | #12554 | +| [@arminanton](https://github.com/arminanton) | #11461, #11513 | +| [@AStupidBear](https://github.com/AStupidBear) | #11584 | +| [@atescivitci-cmd](https://github.com/atescivitci-cmd) | #12105 | +| [@b3nw](https://github.com/b3nw) | #11970, #11971, #12147 | +| [@backryun](https://github.com/backryun) | #11259, #11950, #12075, #12076, #12078, #12079, #12081, #12082, #12181, #12228, #12239, #12255, #12258, #12277, #12367, #12423, #12524, #12538 | +| [@benjaminkitt](https://github.com/benjaminkitt) | #11747 | +| [@benzntech](https://github.com/benzntech) | #11614, #11615 | +| [@Bl0ck154](https://github.com/Bl0ck154) | #11948, #11951, #11952, #11953, #11954 | +| [@brick30llc-ctrl](https://github.com/brick30llc-ctrl) | #11752, #12242, #12262 | +| [@Chewji9875](https://github.com/Chewji9875) | #11455, #11875, #12028 | +| [@cheynetom](https://github.com/cheynetom) | #12058 | +| [@davidebaraldo](https://github.com/davidebaraldo) | #12222, #12304 | +| [@davidlinfr](https://github.com/davidlinfr) | #6390 | +| [@ddarkr](https://github.com/ddarkr) | #12124 | +| [@Deftera186](https://github.com/Deftera186) | #11809 | +| [@dpozimski](https://github.com/dpozimski) | #11945 | +| [@DW-MediaLab](https://github.com/DW-MediaLab) | #12229 | +| [@echel0nn](https://github.com/echel0nn) | #11923 | +| [@f9td56dbgh-hub](https://github.com/f9td56dbgh-hub) | #11660 | +| [@fabioluissilva](https://github.com/fabioluissilva) | #11991 | +| [@foreveryh](https://github.com/foreveryh) | #12177 | +| [@geek007git](https://github.com/geek007git) | #12115, #12116, #12117, #12120, #12122 | +| [@geekyNads](https://github.com/geekyNads) | #11784, #12266 | +| [@ggiak](https://github.com/ggiak) | #11561, #12276, #12350, #12402 | +| [@gonisulaimann](https://github.com/gonisulaimann) | #12368, #12369, #12371 | +| [@Gorillaz322](https://github.com/Gorillaz322) | #12207 | +| [@groovecityJO](https://github.com/groovecityJO) | #12682 | +| [@hartmark](https://github.com/hartmark) | #11434, #11452, #11473, #11499, #11703, #11983, #11984, #11985, #11986, #11988, #11989, #11990, #11994, #12221, #12293, #12445, #12446, #12447, #12448, #12460, #12461, #12623, #12646, #12650, #12680, #12717, #12718, #12727, #12741, #12854 | +| [@hizzt](https://github.com/hizzt) | #11894 | +| [@hongnoul](https://github.com/hongnoul) | #11484 | +| [@HouMinXi](https://github.com/HouMinXi) | #11411, #11414, #11512, #11518, #11520, #11641, #11642, #11643, #11687, #11779, #11849, #11850, #11851, #11915, #11916, #11918, #11919, #11920, #12013, #12017, #12033, #12042, #12106, #12139, #12166, #12169, #12171, #12205, #12213, #12312, #12325, #12487, #12488, #12495, #12504, #12557, #12566, #12590, #12591, #12624, #12626, #12632, #12637, #12678, #12696, #12697, #12711, #12733, #12746, #12767, #12770, #12789, #12803, #12805, #12811, #12866, #12868, #12899, #12926, #12934, #12950, #12951, #12974, #13001, #13006, #13011, #13017, #13026, #13027, #13034, #13035, #13038, #13042, #13050, #13060, #13061, #13069, #13107, #13120, #13136, #13195, #13197 | +| [@Hsia97](https://github.com/Hsia97) | #11624 | +| [@jacobsparts](https://github.com/jacobsparts) | #11854, #12155, #12167 | +| [@joglomedia](https://github.com/joglomedia) | #11980 | +| [@jonlwheat2-gif](https://github.com/jonlwheat2-gif) | #11443, #11450, #11549, #11567, #11608, #11633, #11635, #11640, #11644, #11671, #11672, #11673, #11674, #11675, #11676, #11677, #11706, #11783, #12051, #12052, #12053, #12055 | +| [@kanade-hoshino](https://github.com/kanade-hoshino) | #12180 | +| [@Karan825](https://github.com/Karan825) | #11861 | +| [@KaspaPulse](https://github.com/KaspaPulse) | #11389, #11469, #11628, #11666, #11910 | +| [@keeltrace](https://github.com/keeltrace) | #12080, #12223 | +| [@killer30001000](https://github.com/killer30001000) | #12178 | +| [@KooshaPari](https://github.com/KooshaPari) | #12470, #12592, #12667, #12699, #12703, #12706, #12764, #12769, #12771, #13300, #13301, #13302, #13340, #13401, #13403, #13404, #13405, #13406, #13407, #13409, #13410, #13411, #13412, #13413, #13414, #13418, #13424, #13427, #13433, #13523, #13524, #13525, #13528, #13531, #13532, #13534, #13539, #13543, #13545, #13546, #13547, #13550, #13553 | +| [@kriptoburak](https://github.com/kriptoburak) | #11370 | +| [@KrzysiekSko](https://github.com/KrzysiekSko) | #12425, #12432, #12673 | +| [@marcelokarval](https://github.com/marcelokarval) | direct commit / report | +| [@maxmad64bis](https://github.com/maxmad64bis) | #1622, #11435, #11437, #11441, #11537, #11550, #11553, #11555, #11557, #11812, #11842, #11843, #11903, #12151, #12214, #12215, #12218, #12226, #12314, #12316, #12317, #12318, #12319, #12320, #12321, #12507, #12715, #12731, #12744, #12786, #12787, #12788, #12790, #12792, #12794, #12795, #12828, #12832, #12853, #12857, #12870, #12937, #12941, #12975, #13141, #13142, #13146, #13147 | +| [@mdigitalbh81](https://github.com/mdigitalbh81) | #12240 | +| [@Meet6338-X](https://github.com/Meet6338-X) | #11598, #11609, #12110 | +| [@morpheus9393](https://github.com/morpheus9393) | #11943 | +| [@MumuTW](https://github.com/MumuTW) | #11492, #11502, #11506, #11507, #11626, #11675, #11685, #11728, #11746, #11888, #11889, #11890, #11892 | +| [@Neuron-Mr-White](https://github.com/Neuron-Mr-White) | #11622, #11801, #12256 | +| [@NightStalker-87](https://github.com/NightStalker-87) | #12183 | +| [@NoxzRCW](https://github.com/NoxzRCW) | #11879, #11880, #11881, #11882, #11883, #11935 | +| [@ntdat812](https://github.com/ntdat812) | #11585, #12095 | +| [@ntdatt812](https://github.com/ntdatt812) | #11368, #11573, #11574, #11576, #11577, #11580, #11582, #11583, #11588, #11589, #11590, #11591, #11592, #11593, #11672, #12177, #12180, #12873, #12918, #12920, #12921, #12925, #12930, #13007, #13009, #13024, #13025, #13083, #13087, #13101, #13104, #13110 | +| [@official-burak](https://github.com/official-burak) | #11542 | +| [@opensource-elearning](https://github.com/opensource-elearning) | #12179, #12189, #12278, #12286 | +| [@oyi77](https://github.com/oyi77) | #11408, #11409, #11421, #11505, #11677, #12036, #12110 | +| [@pacocartones](https://github.com/pacocartones) | #11521, #11522, #11527, #11528, #11529, #11530, #11531, #11532, #11533, #11534, #11595, #11599, #11603, #11604, #11605, #11607, #11610, #11676, #11714, #11716, #11718, #11767, #11838, #11860, #11862, #11869, #11871, #11872, #11873, #11903, #11906, #11921, #11934, #12356, #12358, #12359, #12360, #12361, #12362, #12364, #12365, #12373, #12374, #12375, #12376, #12377, #12379, #12380, #12381, #12386, #12387, #12389, #12390, #12394, #12395, #12397, #12401, #12403, #12404, #12522, #12523, #12535, #12536, #12540, #12541, #12543, #12545, #12548, #12549, #12550, #12551, #12552, #12647, #12651, #12653, #13055 | +| [@patrykkopycinski](https://github.com/patrykkopycinski) | #11936, #11937, #12224 | +| [@PauloFH](https://github.com/PauloFH) | #11509 | +| [@PauloHSOliveira](https://github.com/PauloHSOliveira) | #12241 | +| [@ponkcore](https://github.com/ponkcore) | #12054 | +| [@Prajeeth-12](https://github.com/Prajeeth-12) | #11634, #12046 | +| [@quiterunner-commits](https://github.com/quiterunner-commits) | #12131, #12143 | +| [@rafacpti23](https://github.com/rafacpti23) | #11554, #11558, #12192, #12197, #12198, #12202, #12203, #12204 | +| [@ragnar-claude](https://github.com/ragnar-claude) | #11564 | +| [@raheemuddin786](https://github.com/raheemuddin786) | #11491, #11839, #11840, #11841, #12003, #12230, #12231, #12232, #12233, #12234 | +| [@rahilmavani](https://github.com/rahilmavani) | #11761 | +| [@Rahulsharma0810](https://github.com/Rahulsharma0810) | #11771 | +| [@RaviTharuma](https://github.com/RaviTharuma) | #11710, #11727, #11797, #11798, #11802, #11805, #11806, #11811, #12098, #12099, #12101, #12449, #12452, #12472, #12473, #12493, #12533, #12607, #12628, #12631, #12636, #12875, #12876, #12880, #12882, #12884 | +| [@rezjalibd](https://github.com/rezjalibd) | #12186 | +| [@RhianB14](https://github.com/RhianB14) | #11692 | +| [@rifqiawl](https://github.com/rifqiawl) | #11517, #11519, #11969 | +| [@rqzbeh](https://github.com/rqzbeh) | #11390 | +| [@Sabeekhann](https://github.com/Sabeekhann) | #11699, #11705 | +| [@santosraju99-hub](https://github.com/santosraju99-hub) | #11755, #11770 | +| [@Seramicx](https://github.com/Seramicx) | #11568 | +| [@Siva010](https://github.com/Siva010) | #12191 | +| [@solstxce](https://github.com/solstxce) | #11597 | +| [@soroush5](https://github.com/soroush5) | #12691 | +| [@tenshiak](https://github.com/tenshiak) | #12279 | +| [@TheDemonTuan](https://github.com/TheDemonTuan) | #11468, #11470, #11471, #11482, #11548, #11758, #11775, #11814 | +| [@thomasmaerz](https://github.com/thomasmaerz) | #12834 | +| [@tuandinh0801](https://github.com/tuandinh0801) | #11454 | +| [@turbolego](https://github.com/turbolego) | #11621, #11762, #11772, #11774, #11781, #12216 | +| [@Tushar49](https://github.com/Tushar49) | #11392 | +| [@ujjawalkaushik1110](https://github.com/ujjawalkaushik1110) | #11863 | +| [@vermasomesh835](https://github.com/vermasomesh835) | #11794 | +| [@vsd2807](https://github.com/vsd2807) | #11565, #11619 | +| [@wahidsadik371-coder](https://github.com/wahidsadik371-coder) | #12126 | +| [@watchingdogs](https://github.com/watchingdogs) | #12031 | +| [@wildcard](https://github.com/wildcard) | #11620 | +| [@xiaoyaner0201](https://github.com/xiaoyaner0201) | #11460, #11662, #12083 | +| [@Xxx91n](https://github.com/Xxx91n) | #11690 | +| [@yourspraveen](https://github.com/yourspraveen) | #11146 | +| [@yxyxy](https://github.com/yxyxy) | #11671 | +| [@ZaimMarzuki](https://github.com/ZaimMarzuki) | #11960 | +| [@Zartharas](https://github.com/Zartharas) | #11340 | +| [@zero-executioner](https://github.com/zero-executioner) | #11631 | +| [@diegosouzapw](https://github.com/diegosouzapw) | maintainer | + +--- ## [3.8.50] — 2026-08-25 _Living section — regenerated 2026-08-12 from all cycle commits (cycle open `ed2db6cb19` → tip). Bullets carry the merged PR and its author; direct pushes listed separately._ diff --git a/docs/i18n/th/CHANGELOG.md b/docs/i18n/th/CHANGELOG.md index b53a450ebb..7a8daa1d12 100644 --- a/docs/i18n/th/CHANGELOG.md +++ b/docs/i18n/th/CHANGELOG.md @@ -8,16 +8,1119 @@ ## [3.8.51] — TBD -_Living section — cycle opened at the v3.8.50 freeze (parallel-cycle model). Bullets are aggregated from `changelog.d/` fragments at each `/generate-release` phase._ +_Living section — reconciled 2026-09-15 from all cycle commits (`091589089c` → `c0f92ec98a`, 916 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 | **123** | +| 📝 Commits in the cycle | **916** | +| 🔀 Pull requests referenced | **892** | +| 📋 Changelog entries | **919** | +| 🙌 Contributors credited in entries | **121** | +| 🤖 Automated dependency commits | 18 | + +**Entries by type** + +| Type | Count | +| --- | ---: | +| 🐛 Fixes | 592 | +| ✨ Features | 131 | +| 🧹 Chore | 93 | +| 📚 Docs | 42 | +| 🧪 Tests | 32 | +| ♻️ Refactor | 8 | +| 🏗️ Build | 7 | +| ⚡ Performance | 7 | +| ⚙️ CI | 2 | +| ⏪ Reverts | 2 | +| 📦 Dependencies | 2 | +| 🔒 Security | 1 | + +### 🏆 Top 25 contributors this cycle + +_By commits in `091589089c..c0f92ec98a`, author identities consolidated via `.mailmap` and the merged PR's GitHub login. Bots excluded._ + +| # | Contributor | Commits | +| ---: | --- | ---: | +| 🥇 | diegosouzapw | 323 | +| 🥈 | Bob.Hou (@HouMinXi) | 79 | +| 🥉 | Paco Cartones (@pacocartones) | 77 | +| 4 | Dizzle (@maxmad64bis) | 49 | +| 5 | Koosha Paridehpour (@KooshaPari) | 43 | +| 6 | Nguyen Thanh Dat (@ntdatt812) | 31 | +| 7 | Ravi Tharuma (@RaviTharuma) | 26 | +| 8 | Markus Hartung (@hartmark) | 22 | +| 9 | Webman (@jonlwheat2-gif) | 22 | +| 10 | backryun | 17 | +| 11 | anhtahaylove | 15 | +| 12 | Rafa Martins (@rafacpti23) | 10 | +| 13 | Syed Raheemuddin (@raheemuddin786) | 10 | +| 14 | Nguyễn Viết Tuấn (@TheDemonTuan) | 8 | +| 15 | MumuTW | 7 | +| 16 | Paijo (@oyi77) | 7 | +| 17 | Tobias Andersen (@turbolego) | 6 | +| 18 | Bl0ck (@Bl0ck154) | 5 | +| 19 | SHANMUGAPRIYAN (@geek007git) | 5 | +| 20 | KaspaPulse | 5 | +| 21 | Abhishek Sharma (@abhisheksharma2411) | 4 | +| 22 | Abhishek Divekar (@adivekar-utexas) | 4 | +| 23 | Andrew B. (@AndrianBalanescu) | 4 | +| 24 | NoxzRCW | 4 | +| 25 | opensource-elearning | 4 | ### ✨ New Features +- **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). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- Added Google AI Studio Gemini batch text-to-speech support through `POST /v1/audio/speech`. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- 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. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(routing):** subscription-first auto groupings — `auto/subscription` routes only through plan-included connections with a documented hard-stop overage and fails closed on exhaustion, while `auto/thrifty` orders the pool `subscription → keyless → free → cheap → premium` and steps up one rung at a time as each is exhausted. Billing class comes from a curated per-connection catalog (uncurated is treated as metered, never plan-included), both reuse STRICT_ZERO_COST's per-connection verification, and a quota reading whose `resetAt` has passed is now refreshed regardless of TTL so routing returns to plan capacity as soon as it resets ([#11146](https://github.com/diegosouzapw/OmniRoute/pull/11146)) — thanks @yourspraveen +- **feat(providers):** publish a management-authenticated versioned web-session credential contract from OmniRoute's canonical browser credential metadata ([#11340](https://github.com/diegosouzapw/OmniRoute/pull/11340)) — thanks @Zartharas +- **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)) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add Xquik X search with typed results, credential validation, REST routing, and MCP selection ([#11370](https://github.com/diegosouzapw/OmniRoute/pull/11370)) — thanks @kriptoburak +- **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)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(dashboard):** surface durable exclusive managed leases in the existing Sessions view, keeping leased clients visible across idle gaps while marking connections with in-flight work as active ([#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389)) — thanks @KaspaPulse +- **build(bun):** allow Turbopack bundler flag on Bun 1.4+ with configurable Webpack fallback ([#11471](https://github.com/diegosouzapw/OmniRoute/pull/11471)) — thanks @TheDemonTuan +- feat(api): add an opt-in `modelVisibilityAllowlist`/`modelVisibilityDenylist` settings pair to curate exactly which models `/v1/models` advertises, mirrored into every `auto/*` combo candidate pool so a denied model cannot be routed to via combo selection either (#11481) ([#11997](https://github.com/diegosouzapw/OmniRoute/pull/11997)) +- **feat(rankings):** the Free Provider Rankings page shows what each provider actually served over the last 24 h. It ranked by ELO alone, which left a provider that answers every call with an error in first place; the usage data was already served by the API but never requested. A provider with too small a sample shows a dash, not a number ([#11546](https://github.com/diegosouzapw/OmniRoute/pull/11546)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **feat(guardrails):** enforce a bounded, deterministic contract for Video Bridge transcripts — 256 cues, 4096 input code units and 4 KiB UTF-8 per cue, 64 KiB total text, malformed-Unicode rejection, focus-window scoping, cross-source reconciliation with contributing-source metadata, and a structural provenance trust boundary so caller JSON can never self-assert `embedded`/`audio-bridge` provenance ([#11652](https://github.com/diegosouzapw/OmniRoute/issues/11652)) ([#12009](https://github.com/diegosouzapw/OmniRoute/pull/12009)) +- **feat(video):** orchestrate optional Video Bridge audio extraction and Audio Bridge STT behind a dual opt-in (operator setting AND per-request signal) — a new loopback-only broker `mode=audio` operation shares the frame path's exact process queue, deadline, AbortSignal, and byte budgets to extract a bounded mono 16 kHz PCM WAV from the same already-downloaded video, then reuses the existing Audio Bridge transcription boundary; provider segment timing is preserved when available and marked coarse otherwise, and every failure degrades to a visual-only-safe partial instead of throwing (#11654). ([#12012](https://github.com/diegosouzapw/OmniRoute/pull/12012)) +- Add a tenant-bound Video Bridge drill-down lifecycle on top of the existing secure cache substrate: opaque hashed handles (never raw session/video identifiers), preview/standard/detail multiresolution variants resampled on read, response pagination capped at 8 frames and 32 MiB, and a new authenticated `/api/v1/video-bridge/drilldown` consumer route that stays disabled for remote access by default and denies cross-key access with the same response as a nonexistent handle (no existence oracle). ([#12006](https://github.com/diegosouzapw/OmniRoute/pull/12006)) +- **test(video):** Add the Video Bridge FU-07/FU-09 promotion-evidence harness (#11656) — a frozen Zod manifest schema covering the 8 required scenario kinds (static scenes, rapid cuts, late facts, fades, blur, small text, close events, visual prompt injection) with a minimum of 3 repetitions per case, deterministic declarative fixture recipes (`videoBridgePromotionFixtures.ts`), a pure medians/p95 metrics aggregator, a pure FU-07/FU-09 promotion-verdict evaluator applying the ticket's exact thresholds (missing token usage always holds), a digest-only persistence layer that never retains raw media or raw model responses, and a versioned per-model promotion allowlist shipped empty with every model defaulting to `hold`. The FU-07/FU-09 promotion verdicts themselves remain HOLD — they require a real evidence run against real models on VPS 192.168.0.15. ([#12008](https://github.com/diegosouzapw/OmniRoute/pull/12008)) +- **feat(video bridge):** "embedded" transcript provenance can now be legitimately earned instead of merely asserted — a bounded, allowlisted (`mov_text`/`subrip`/`webvtt`) subtitle probe runs through the loopback-only Video Bridge broker (at most 2 streams, 10s subdeadline bounded by the request deadline, 256 KiB output, 4096-code-unit lines), normalized through a bounded, ReDoS-safe WebVTT parser and Zod-validated end to end. The adapter always resolves to an explicit `success`/`absent`/`transient_failure` outcome — a subtitle failure never breaks the visual description path, and only a fingerprint-verified broker response (never a caller-declared label) can produce embedded cues (#11659). ([#12011](https://github.com/diegosouzapw/OmniRoute/pull/12011)) +- Default new Antigravity-family connections (agy CLI imports and Antigravity OAuth connects) to model auto-sync, so live model discovery lands in the synced catalog and `/v1/models` picks up freshly released upstream models (e.g. Gemini 3.7 Flash tiers) without code changes. Existing connections keep their current setting; the per-connection dashboard toggle remains the opt-out. (#11685 — thanks @MumuTW) ([#11887](https://github.com/diegosouzapw/OmniRoute/pull/11887)) +- **feat(ui):** enable React Compiler (`reactCompiler: true` + `babel-plugin-react-compiler`) for automatic memoization at build time ([#11783](https://github.com/diegosouzapw/OmniRoute/pull/11783)) — thanks @jonlwheat2-gif +- **feat(providers):** add SeekAi (`seekai.cc`) as an OpenAI-compatible New-API gateway — catalog id `seekai` (alias `ska`), `https://seekai.cc/v1`, live `/v1/models` via `passthroughModels`, aggregator-list membership so New-API balance detection can opt in. No referral/aff codes. ([#11786](https://github.com/diegosouzapw/OmniRoute/issues/11786)) ([#12557](https://github.com/diegosouzapw/OmniRoute/pull/12557)) — thanks @HouMinXi +- **feat(zai):** add GLM-5.3-Flash Coding Plan support (1M context, 128K output, vision, `low|high|max` reasoning) and route `zai` GLM-5.3-family API-key traffic through the OpenAI-compatible Coding Plan endpoint with native thinking defaults ([#11801](https://github.com/diegosouzapw/OmniRoute/pull/11801)) — thanks @Neuron-Mr-White +- **feat(combo):** choose how combo models are ordered — manual, provider, score, or name — via a sort control in the dashboard builder, persisted in `config.modelSort` and re-applied on load and after add ([#11812](https://github.com/diegosouzapw/OmniRoute/pull/11812)) — thanks @maxmad64bis +- **feat(free):** custom models can be marked free-tier via `customModels[].isFree`; `isFreeModel()` is the first door and `hidePaidModels` respects it even for providers outside the free budget ([#11843](https://github.com/diegosouzapw/OmniRoute/pull/11843)) — thanks @maxmad64bis +- **feat(nodejs):** add `5dive` as a `configure` target — `omniroute configure 5dive` / `omniroute setup-5dive` write a 5dive auth profile that points an agent fleet's `claude` seats at OmniRoute, with the root-only write, the loopback-vs-`https` endpoint rule and the per-seat model pin handled explicitly ([#11852](https://github.com/diegosouzapw/OmniRoute/pull/11852)) — thanks @5dive-bot +- **feat(sse):** treat `max` as a first-class reasoning-effort tier and clamp per model family (GLM 5.1+/DeepSeek V4+/Kimi K3+ keep native `max`; o1/MiniMax/Grok/Muse Spark clamp to their upstream ceiling) ([#11875](https://github.com/diegosouzapw/OmniRoute/pull/11875)) — thanks @Chewji9875 +- **feat(providers):** the provider plugin manifest now advertises a `usage-fetch` capability for the 40 providers that have a wired usage/quota fetcher, so external dashboards can read it from `GET /api/v1/provider-plugin-manifest` instead of parsing `open-sse/services/usage.ts` after every release. Discovery only — no new fetcher, no quota change, and the Dashboard quota widget stays gated by `USAGE_SUPPORTED_PROVIDERS`. `USAGE_FETCHER_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/fetcherProviders.ts`) and is re-exported from `services/usage.ts`, keeping the manifest module a light leaf instead of pulling the ~490-module usage dispatcher into the manifest route. ([#11903](https://github.com/diegosouzapw/OmniRoute/pull/11903)) — thanks @maxmad64bis / @pacocartones +- **feat(plugins):** `OMNIROUTE_PLUGINS_DIR` sets the directory the runtime plugin scanner reads — and the root the plugin manager installs into — overriding the `HOME`-derived default, so a Docker/K8s deployment can point straight at its bind-mounted plugin tree instead of moving `HOME` just to relocate the scan path. An image that exports no home no longer scans `/tmp/.omniroute/plugins` in silence: the resolved directory is logged once at startup as `scanner.dir_resolved`, naming the input that won. Unset, behaviour is unchanged. Distinct from the CLI-only `OMNIROUTE_PLUGIN_PATH`, which finds `omniroute-cmd-*` command packages and never reached this scanner ([#11906](https://github.com/diegosouzapw/OmniRoute/pull/11906)) — thanks @amaleta / @pacocartones +- **feat(leases):** add an explicit owner-authenticated status action that returns only the active lease's privacy-safe configured connection and provider labels, with generation fencing and no credential or internal-id disclosure ([#11910](https://github.com/diegosouzapw/OmniRoute/pull/11910)) — thanks @KaspaPulse +- **feat(providers):** import-from-file modal shows per-row API errors and ships a downloadable CSV template ([#12071](https://github.com/diegosouzapw/OmniRoute/issues/12071)) ([#12504](https://github.com/diegosouzapw/OmniRoute/pull/12504)) — thanks @HouMinXi +- **feat(providers):** dashboard search matches connection name and `baseUrl` so imported OpenAI-compat nodes surface on the provider card ([#12108](https://github.com/diegosouzapw/OmniRoute/issues/12108)) ([#12495](https://github.com/diegosouzapw/OmniRoute/pull/12495)) — thanks @HouMinXi +- **feat(routing):** add a `score` Auto router strategy that selects the highest configured weighted score and reuses `explorationRate`. ([#12155](https://github.com/diegosouzapw/OmniRoute/pull/12155)) — thanks @jacobsparts +- Add a runtime feature flag to disable universal context handoffs globally without changing the default behavior. ([#12167](https://github.com/diegosouzapw/OmniRoute/pull/12167)) — thanks @jacobsparts +- **feat(providers):** the provider plugin manifest now also advertises a `usage-supported` capability for the 46 providers whose usage API is accepted by the server and Dashboard routes, so integrators can distinguish "the server will serve quota for this provider" from "a fetcher is wired" without reading TypeScript. Discovery only — no fetcher or quota change. `usage-fetch` resolves on id or alias (the usage dispatcher accepts both); `usage-supported` resolves on id alone, matching the runtime guard `USAGE_SUPPORTED_PROVIDERS.includes(providerId)`. `USAGE_SUPPORTED_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/supportedProviders.ts`) and is re-exported from `providers.ts`, mirroring the `fetcherProviders` leaf from #11903 and keeping the manifest a light module. ([#12214](https://github.com/diegosouzapw/OmniRoute/pull/12214)) — thanks @maxmad64bis +- **feat(rankings):** order Free Provider Rankings by what each provider actually served — `GET /api/free-provider-rankings?sortBy=reliability` and a "Most reliable first" toggle on the page. Providers with too few calls to state a success rate keep their score order below the measured ones; the default order is unchanged ([#12218](https://github.com/diegosouzapw/OmniRoute/pull/12218)). — thanks @maxmad64bis +- **perf(sse):** defer `cloneLogPayload()` in the structured SSE collector until after the `maxEvents`/`maxBytes` cap check, eliminating ~9,800 wasted `structuredClone` calls per streaming response (65–71% faster `push()`). Reducer snapshot isolation restored for OpenAI and Responses summaries ([#12241](https://github.com/diegosouzapw/OmniRoute/pull/12241)) — thanks @PauloHSOliveira ([#12243](https://github.com/diegosouzapw/OmniRoute/pull/12243)) +- **feat(usage):** Devin CLI agentic quota (Codeium seat-management GetUserStatus) and OpenRouter key limits plus account credits now surface in Provider Limits ([#12256](https://github.com/diegosouzapw/OmniRoute/pull/12256) — thanks @Neuron-Mr-White) +- **feat(settings):** persist `headroomUrl` through Settings so status/start use the operator URL instead of only `HEADROOM_URL` ([#12306](https://github.com/diegosouzapw/OmniRoute/issues/12306)) ([#12487](https://github.com/diegosouzapw/OmniRoute/pull/12487)) — thanks @HouMinXi +- **feat(auto-combo):** Auto-Combo scoring can now weigh how often a provider/model has actually succeeded. The engine already carried that number on every candidate — 24 hours of usage history behind a ten-sample floor, real-time metrics otherwise — and the scoring function never read it, while the weight table described `stability` as if it did. `reliability` (`1 - failureRate`, with the same field precedence and the same rate-bounding the speed ranking already uses, so a corrupt reading means "nothing observed" rather than "fails every call") is now a declared factor shipping at weight `0`, so routing is unchanged until an operator gives it one, and the `stability` description now matches what that factor computes ([#12317](https://github.com/diegosouzapw/OmniRoute/pull/12317)) — thanks @maxmad64bis +- **feat(routing):** With `freeAccessPolicy: "strict"`, the read-only candidate listing (`GET /v1/auto-combo/{channel}/candidates`) no longer hides the candidates the zero-cost guard excludes — the same read-only transparency the resilience filter already honours (#9133). Each candidate now carries `freeAccessExclusion` saying why it would be kept out, and it tells an exhausted allowance apart from a quota reading that never arrived or went stale, which used to look identical from the outside. Routing is unchanged: the listing reports, it never enforces. The separate `excludeTosAvoid` guard still drops its candidates without a reason; that gap is now documented rather than closed ([#12319](https://github.com/diegosouzapw/OmniRoute/pull/12319)) — thanks @maxmad64bis +- **feat(radar):** The Radar catalog table now shows two facts it was already receiving from the feed and dropping on the floor: the per-model rate limits (requests and tokens, per minute and per day) in a new column, and a badge when a provider's terms state it may train on the prompts you send. A limit of zero renders as zero rather than "rate-only" — for a ceiling those are opposite facts — and a model with no training statement gets no badge, because an absent statement is not a guarantee ([#12320](https://github.com/diegosouzapw/OmniRoute/pull/12320)) — thanks @maxmad64bis +- **feat(radar):** explain Community, single-use, contributor, supporter, recovery, abuse, offers, and privacy rules before either Radar activation action, and remove the superseded fixed-PR grant promise from every UI locale ([#12342](https://github.com/diegosouzapw/OmniRoute/pull/12342)) +- **feat(gamification):** the dashboard Profile page now shows the real daily streak — `/api/gamification/level` returns `streak: { current, longest }` (per key with `apiKeyId`, operator-wide maximum otherwise) and the streak card reads it instead of a hard-coded 0 (#2403) ([#12377](https://github.com/diegosouzapw/OmniRoute/pull/12377)) — thanks @pacocartones +- **feat(gamification):** the dashboard leaderboard now shows each API key's display name under the Name column instead of a truncated key id; `GET /api/gamification/leaderboard` attaches `name` per entry (name only — no key material), while the shared ranking helper and the federation leaderboard stay id-only — thanks @pacocartones ([#12385](https://github.com/diegosouzapw/OmniRoute/pull/12385)) +- **feat(gamification):** enforce the documented 1000 XP/min per-API-key anti-cheat rate limit on the XP award path; over-limit awards are logged and skipped instead of persisted, and the sliding window now matches the timestamp format stored in `xp_audit_log` ([#2403](https://github.com/diegosouzapw/OmniRoute/issues/2403)) ([#12390](https://github.com/diegosouzapw/OmniRoute/pull/12390)) — thanks @pacocartones +- **feat(admin):** localize the gamification anomalies page — the loading state, the Status column and the Suspicious badge now come from the `common` catalog (new `common.suspicious` key propagated to every locale) — add it to the Gamification sidebar group as `gamification-admin` (`/dashboard/gamification/admin`), and expose the loading and empty states as polite `role="status"` live regions (#12401 — thanks @pacocartones) +- **feat(providers):** skip GitHub combo members missing from the live synced catalog, and drop Copilot models that are policy-disabled or hidden from the model picker ([#12473](https://github.com/diegosouzapw/OmniRoute/pull/12473)) — thanks @RaviTharuma +- **feat(i18n):** locale-expansion tooling — `npm run i18n:add-locale` adds a language to config, dashboard, docs mirrors, CLI, README/indexes and site in one command; browser, CLI and cookie detection resolve `uk`, `fil`/`tl`, `zh-Hant` (and the retired legacy `in` → `id`) via config aliases; `config/i18n.json` now ships in the npm package so the published CLI can read it; new real-translation ratio gate (`npm run i18n:check-ratio`, advisory) with a per-locale ratchet baseline; `run-translation --adopt` restores `.i18n-state.json`; `sync-language-bars` generates 🌐 bars from config; `validate_translation.py` loads its allowlist again. Retires the duplicate `in` locale (Indonesian mislabelled as Hindi) — 42 honest locales; saved `NEXT_LOCALE=in` / `OMNIROUTE_LANG=in` keep working. (#12496) +- **feat(api):** Emit gateway-measured `tokens_per_second` (TTFT excluded) on streaming usage and `X-OmniRoute-Tokens-Per-Second` when first-token latency is known ([#12616](https://github.com/diegosouzapw/OmniRoute/issues/12616)) ([#12631](https://github.com/diegosouzapw/OmniRoute/pull/12631)) — thanks @RaviTharuma +- **feat(opencode-plugin): map gateway cost/usage/tok/s onto OpenCode inference payloads** — the official plugin copies `X-OmniRoute-Response-Cost`, token counts, `X-OmniRoute-Tokens-Per-Second` / `usage.tokens_per_second`, TTFT, and the winning `X-OmniRoute-Model` onto the JSON/SSE body OpenCode already consumes. Missing tok/s is left unset (never `tokens / latency`). (#12636) — thanks @RaviTharuma +- **feat(combo):** add combo strategy `quota-weighted`: skip exhausted accounts, then weighted-draw by leftover / in-flight load; existing conversations stay pinned until the account is empty ([#12789](https://github.com/diegosouzapw/OmniRoute/pull/12789)) — thanks @HouMinXi +- **feat(models):** Account live listings become the chat catalog source for Claude, Codex, Copilot, and AGY; public metadata only fills prices on IDs those accounts already list. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **fix(models):** Union `agy` and `antigravity` live catalogs so `agy/gemini-3.8-flash-high` is not rejected after the prefix folds to `antigravity`. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **feat(opencode):** Add `@omniroute/opencode-plugin-v2`, an opencode v2 plugin that gives a v2 install a route to an OmniRoute gateway: it publishes models, combos and auto-combos into the host catalog, refreshes them lazily behind a 300s TTL, and keeps serving the last known catalog from an on-disk snapshot when the gateway is unreachable. The v1 plugin (`@omniroute/opencode-plugin`) is untouched and keeps working as before. The plugin adapts to the host instead of assuming one shape: it reads the shape the host seeds into the catalog draft and publishes the `api` block, the pre-`api` top-level fields, or both. The gateway key comes from the host credential store when one is connected, so no secret needs to sit in a config file; a source the gateway refuses is named in a warning instead of silently degrading the catalog to raw model ids; and tool schemas bound for a Gemini model are stripped of the JSON-Schema keywords Gemini rejects, so tool calling survives that route ([#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870)) — thanks @maxmad64bis +- feat(services): show sanitized CLIProxyAPI account health from its authenticated management API without exposing credentials, file paths, or raw account metadata (#6342) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add AnySearch free web search + URL extract (webFetch) with typed results, credential validation, REST routing, and MCP selection - fallback-only ([#11690](https://github.com/diegosouzapw/OmniRoute/pull/11690)) — thanks @Xxx91n +- **feat(dashboard):** display clamped `[0, 100]%` cached input token ratio in request logs table ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) — thanks @b3nw +- **feat(catalog):** add `OMNIROUTE_DISABLE_THINKING_LEVEL_VARIANTS` feature flag to optionally filter out thinking level variants from model catalog ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) — thanks @b3nw +- **feat(dashboard):** continuously export call logs to external analytics stores. A pluggable destination registry ships the full Logs-tab record set on an hourly `JobRegistry` cron, with a persisted per-destination cursor, batched inserts, a config UI rendered from each destination's own field descriptors, and a REST layer (`/api/log-export/*`) for CRUD, a connection test, and an on-demand run. A destination can opt into `includeBodies` to also ship the request and response payloads shown in the Logs detail pane, including the client and provider views of each call; this is off by default, and payloads inherit the dashboard's PII sanitisation, secret redaction and `noLog` handling. Google BigQuery is the first destination, using a service-account key stored encrypted at rest and streaming inserts keyed by call-log id, into a table that is day-partitioned on `timestamp` and clustered on `api_key_name`, `provider`, `model` and `status`. ([#11945](https://github.com/diegosouzapw/OmniRoute/pull/11945)) — thanks @dpozimski +- **feat(providers):** add MaxAI as a signed, OpenAI-compatible provider serving its 13 paid chat models (GPT-5.6 / Luna / Thinking, Claude 5 Sonnet, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite, Grok 4.1-fast / 4.5, DeepSeek V3.2 / R1, Llama 3.3 70B) through OmniRoute's `/v1` endpoint, with per-request HMAC-SHA1→SM3→AES request signing, live model + context-window discovery from `/models/get_config`, and prompted tool-calling translated to OpenAI `tool_calls` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI vision input — image_url content parts are forwarded inline in `message_content` to the 6 vision-capable models (GPT-5.6 / Luna / Thinking, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite) ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI image generation — 6 image models (gpt-image-1, dall-e-3, flux-1-schnell/dev/pro, sd3-medium) exposed through `POST /v1/images/generations` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI document RAG — inline base64 file/document attachments are uploaded to MaxAI (content-addressed `doc_id`) and attached to the chat via `doc_list` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** browserless MaxAI onboarding — email device-pair login (`/api/providers/[id]/login`) and signed access-token refresh, so a connection can be created and kept fresh without a real browser or Google OAuth ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** per-provider TLS impersonation profile (MaxAI presents a Windows Firefox-150 client fingerprint) so its bot-sensitive endpoints accept OmniRoute traffic ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** add **Nimble** as a web-search and web-fetch provider (`nimble-search`) — `/v1/search` routes to Nimble's search API at `lite` depth (locale, freshness and include/exclude domain filters mapped onto the shared request contract), and `/v1/web/fetch` routes to Nimble Extract, which covers all four fetch formats (`markdown`, `html`, `links`, `screenshot`) from a single call. One API key serves both surfaces. ([#11620](https://github.com/diegosouzapw/OmniRoute/pull/11620)) — thanks @wildcard +- The npm publish is automatic again, through npm Trusted Publishing (OIDC): the hosted `stage-npm` job publishes with a short-lived credential minted from GitHub's id-token — no `NPM_TOKEN`, no 2FA prompt, provenance attached. `publish_mode=staged` (owner approves with 2FA) and `direct` (token) remain available on `workflow_dispatch`. ([#11931](https://github.com/diegosouzapw/OmniRoute/pull/11931)) +- **feat(providers):** Add **Opper** as an API-key gateway provider — EU-hosted AI gateway with 700+ models from 30+ providers behind one OpenAI-compatible API and one key (`OPPER_API_KEY`); model ids use `provider/model` format (e.g. `anthropic/claude-sonnet-4-6`, `openai/gpt-5`); live model catalog at `https://api.opper.ai/v3/compat/models`; entry mirrors `requesty` (same shape, `passthroughModels: true`, no static seed) ([#11629](https://github.com/diegosouzapw/OmniRoute/pull/11629)) +- **feat(dashboard):** the `/dashboard/orchestration` snapshot hook now subscribes to the `agents` WebSocket channel (`agent.task.updated`) instead of `requests` as its refetch trigger, and relaxes its background poll from 5s to 30s while that WS connection is up — falling back to the tighter 5s cadence, reprogrammed live on any connect/disconnect transition, whenever the socket is down. ([#12409](https://github.com/diegosouzapw/OmniRoute/pull/12409)) +- **feat(dashboard):** Orchestration canvas quick wins — search box plus state/source/provider filter chips with a one-click clear, and per-source collapse/expand, all reflected in the URL so a filtered/collapsed view is shareable and survives a refresh; the detail drawer gained a "copy trace JSON" action and hardened error/empty-state and accessibility handling; the Agents-tab edges now animate traveling particles along active (running) connections; and the canvas node/edge status colors moved off fixed hex values onto theme-aware `--orch-status-*` CSS custom properties, so they adapt correctly to light/dark mode. ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- **chore(dashboard):** Orchestration UI hardening pass and the missing component/model test coverage it called for — `OrchestratorNode`/`ActivityNode`/`OverflowNode` rendering, the `?node=`/overflow-click page routing, the Agents-tab orchestrator-click no-op and `showCompleted` toggle, and the overview kanban's done-column sort order (#12270, #12271). ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- New `/dashboard/orchestration` page: live unified view of everything running — Cloud Agent, A2A and Conductor as a real-time graph (Agents tab), the combo cascade (Routing tab, reusing the Combo Live Studio) and a state kanban (Overview tab), with a detail drawer (trace, cost, approve/cancel). Read-only over existing APIs — no new backend. Canvas concept credit: PR #11815 design ([#12261](https://github.com/diegosouzapw/OmniRoute/pull/12261)) +- **feat(dashboard):** new "History" tab on `/dashboard/orchestration` — an Airflow-style grid of finished runs over a 24h/7d/30d preset window, one row per (source, identity), clicking a cell opens the existing detail drawer. It is backed by real persistence: A2A task lifecycle transitions are now written to the `a2a_tasks` table (purged after 30 days, configurable via `OMNIROUTE_A2A_HISTORY_RETENTION_DAYS`) and served by the new `GET /api/a2a/tasks/history` listing endpoint, with the task-detail route falling back to persisted history once a run leaves the in-memory snapshot. Conductor runs stay remote and are not persisted locally — the tab says so instead of silently omitting them. ([#12479](https://github.com/diegosouzapw/OmniRoute/pull/12479)) +- **feat(dashboard):** the orchestration detail drawer gained a "Repeat" action for Cloud Agent, A2A and Conductor tasks — a two-click confirm (click once to arm, click again within the confirm window to fire) re-submits the original prompt/input as a new run. The button is disabled with an explanatory tooltip whenever the original input can't be recovered from the loaded task detail (e.g. it never carried a prompt, or the detail failed to load). Two limitations of the A2A variant, by design: it targets the `/a2a` JSON-RPC endpoint, which authenticates with an API key only (`REQUIRE_API_KEY=true` or a configured `OMNIROUTE_API_KEY` makes a dashboard-session repeat answer `HTTP 400` — surfaced verbatim in the drawer's error line, never as a success), and the `message/send` call is SYNCHRONOUS: the POST blocks for the whole skill run, so the success confirmation only appears once the repeated task finishes. A dashboard-authenticated A2A creation path is deliberately left to a follow-up — widening the endpoint's auth posture is an operator decision, not a side effect of this feature. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(a2a):** A2A task execution now records which memories were consulted for the task's last user message as `metadata.memoryHits` (id/key/type/content-snippet) plus a `memory_hits` history event, purely for observability — the retrieved memory is never injected into a skill's prompt or behavior. Gated by the `OMNIROUTE_A2A_MEMORY_HITS` kill-switch (default enabled; set to `0` to skip the recall lookup entirely). The drawer's new "Memory used" section lists these hits for a2a tasks and is omitted whenever there are none. Known limitation: recall only resolves under the keyless posture — a keyed caller's task owner is a SHA-256 prefix of the API key, while memory rows are keyed by the database api-key id, and no hash→id lookup exists today, so the hit list stays empty for keyed callers. The recorded hits are also kept out of the task's own `input` (and therefore out of the persisted input and of the "Repeat" request body), so repeating a task never re-sends the previous run's memory snippets. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(providers):** add a Perplexity Agent API provider (`perplexity-agent` / `pplx-agent`) for Perplexity `/v1/responses`, including the documented Anthropic, OpenAI, Google, xAI, DeepSeek, Z.AI, Moonshot/Kimi, NVIDIA, and Perplexity model IDs plus Anthropic-model `max_output_tokens` compatibility. ([#12103](https://github.com/diegosouzapw/OmniRoute/pull/12103)) — thanks @AIB1TAL0S +- **feat(providers):** add RPD (Requests Per Day) limit to provider rate limit overrides across UI, schemas, DB, and i18n ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) — thanks @b3nw +- **feat(providers): add UC Direct (uncensored.com Developer API), the metered OpenAI-compatible surface.** A standard OpenAI-compatible passthrough (default executor) for uncensored.com's official REST API at `https://api.uncensored.com/api/v1`: `X-api-key` auth (never-expiring `uai_sk_live_` key), `POST /chat/completions` with streaming SSE and native tool-calling, and the full live metered catalog (82 models across 15 providers, discovered from the public `GET /v1/models`). Registered as provider `uc-direct` (alias `ucd`). Complements the un-metered `uc` persona provider — same models, metered credits and a plain API key instead of a subscription session. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(providers): add UC (uncensored.com), the un-metered subscription "persona" chat as an OpenAI-compatible provider.** A WebSocket web-app port: a durable Clerk credential mints a short-lived session token per connect (browserless — no API key), driving UC's persona socket. Ships the browserless email-code login (request → verify → harvest), the 19 verified persona models (Claude Opus, Gemini, Grok, GLM, Kimi, DeepSeek, MiniMax, incl. the uncensored variants), prompted `` tool-calling with a per-model code-style dialect + auto-cure retry for guardrailed models, live ``/reasoning split, streaming + non-streaming OpenAI responses, and full quota/auth error surfacing (paywall / message-limit / rate-limit → 429, invalid session → 401 re-login). Full multimodal parity via the persona blob-upload layer: **vision** (image input, 15 vision-capable models), **document RAG** (PDF/doc upload, server-side extraction), **image generation** (22 models), **video generation** (14 models, async signed-url → poll), and **TTS** (streaming MP3). Registered as provider `uc` (alias `ucn`). The metered OpenAI-compatible Developer API is a separate `uc-direct` provider. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(logs):** add copy-all button to request detail modal ([#11083](https://github.com/diegosouzapw/OmniRoute/pull/11083)) — thanks @AndrianBalanescu +- **feat(check):** make serviceKinds required and add the reverse-walk provider consistency gate ([#11392](https://github.com/diegosouzapw/OmniRoute/pull/11392)) — thanks @Tushar49 +- **feat(providers):** only advertise verified-working connections by default ([#11448](https://github.com/diegosouzapw/OmniRoute/pull/11448)) — thanks @alltomatos +- **feat(resilience):** add hierarchical concurrency admission ([#11493](https://github.com/diegosouzapw/OmniRoute/pull/11493)) +- **feat(providers):** derive + surface expiry for JWT-bearing web cookies (#11497) ([#11505](https://github.com/diegosouzapw/OmniRoute/pull/11505)) — thanks @oyi77 +- **feat(tunnels):** support named cloudflared tunnels via CLOUDFLARED_CONFIG ([#11561](https://github.com/diegosouzapw/OmniRoute/pull/11561)) — thanks @ggiak +- **feat(compression):** make proactive context-compression threshold a live setting ([#11564](https://github.com/diegosouzapw/OmniRoute/pull/11564)) — thanks @ragnar-claude +- **feat(resilience):** add provider peak-hour protection ([#11622](https://github.com/diegosouzapw/OmniRoute/pull/11622)) — thanks @Neuron-Mr-White +- **feat(providers):** add 1min.ai provider ([#11631](https://github.com/diegosouzapw/OmniRoute/pull/11631)) — thanks @zero-executioner +- **feat(dashboard):** collapsible JSON tree viewer for request/response payloads ([#11703](https://github.com/diegosouzapw/OmniRoute/pull/11703)) — thanks @hartmark +- **feat(routing):** add LiquidAI LFM2.5-2.6B free tier via OpenRouter ([#11752](https://github.com/diegosouzapw/OmniRoute/pull/11752)) — thanks @brick30llc-ctrl +- **feat(pricing):** add GLM-5.3-Flash pricing, model specs, and catalog registration ([#11830](https://github.com/diegosouzapw/OmniRoute/pull/11830)) — thanks @AndrianBalanescu +- **feat(routing):** add relayMode for schema-locked context handoffs ([#11839](https://github.com/diegosouzapw/OmniRoute/pull/11839)) — thanks @raheemuddin786 +- **feat(config):** add support for runtime system prompt configuration and hot-reloading ([#11841](https://github.com/diegosouzapw/OmniRoute/pull/11841)) — thanks @raheemuddin786 +- **feat(routing):** expand connection-aware quota prefilter across combo strategies (#11682) ([#11850](https://github.com/diegosouzapw/OmniRoute/pull/11850)) — thanks @HouMinXi +- **feat(models):** reactive discovery sync after Antigravity model-not-found 404 ([#11888](https://github.com/diegosouzapw/OmniRoute/pull/11888)) — thanks @MumuTW +- **feat(models):** auto-aliases and token limits from synced Antigravity discovery ([#11889](https://github.com/diegosouzapw/OmniRoute/pull/11889)) — thanks @MumuTW +- **feat(settings):** raise sticky round-robin limit caps to 1000 ([#12015](https://github.com/diegosouzapw/OmniRoute/pull/12015)) — thanks @adivekar-utexas +- **feat(resilience):** operator-configurable global credential health check interval ([#12043](https://github.com/diegosouzapw/OmniRoute/pull/12043)) — thanks @adivekar-utexas +- **feat(quota):** use official OpenCode Go usage API ([#12124](https://github.com/diegosouzapw/OmniRoute/pull/12124)) — thanks @ddarkr +- **feat(resilience):** default the credential health check sweep to 60 minutes ([#12138](https://github.com/diegosouzapw/OmniRoute/pull/12138)) — thanks @adivekar-utexas +- **feat(quality):** new-code mode for the complexity and dead-code ratchets (Clean as You Code) ([#12142](https://github.com/diegosouzapw/OmniRoute/pull/12142)) +- **feat(dashboard):** orchestration canvas — unified model + snapshot hook (part 1/2) ([#12156](https://github.com/diegosouzapw/OmniRoute/pull/12156)) +- **feat(usage):** add Kilo Code balance and Kilo Pass quotas ([#12178](https://github.com/diegosouzapw/OmniRoute/pull/12178)) — thanks @killer30001000 +- **feat(providers):** refresh vendored ChatGPT Web connector to v4.0.7 ([#12181](https://github.com/diegosouzapw/OmniRoute/pull/12181)) — thanks @backryun +- **feat(providers):** manual "Clear cooldown" action in the cooling panel ([#12224](https://github.com/diegosouzapw/OmniRoute/pull/12224)) — thanks @patrykkopycinski +- **feat(providers):** restore ChatGPT Web via clean-room browser transport ([#12239](https://github.com/diegosouzapw/OmniRoute/pull/12239)) — thanks @backryun +- **feat(sse):** wire the PROVIDER_PROFILES window gate into the global provider cooldown ([#12247](https://github.com/diegosouzapw/OmniRoute/pull/12247)) +- **feat(providers):** modernize CLOVA Studio chat and embeddings ([#12277](https://github.com/diegosouzapw/OmniRoute/pull/12277)) — thanks @backryun +- **feat(browser):** adopt Obscura as primary headless browser engine with Chromium fallback ([#12286](https://github.com/diegosouzapw/OmniRoute/pull/12286)) — thanks @opensource-elearning +- **feat(sse):** full language parity for output styles (es/de/fr/it/ru/zh + autoDetect) ([#12289](https://github.com/diegosouzapw/OmniRoute/pull/12289)) +- **feat(quality):** complete test:scoped — --full map rebuild, stdin selection, CI loader parity (#8084 D1) ([#12353](https://github.com/diegosouzapw/OmniRoute/pull/12353)) +- **feat(providers):** refresh Fable, Cursor, and Devin catalogs ([#12367](https://github.com/diegosouzapw/OmniRoute/pull/12367)) — thanks @backryun +- **feat(video):** redact transcript text from logs and durable memory (#12150 P1) ([#12427](https://github.com/diegosouzapw/OmniRoute/pull/12427)) +- **feat(dashboard):** parent-link, genuine-continuation badge, and modal perf fixes ([#12448](https://github.com/diegosouzapw/OmniRoute/pull/12448)) — thanks @hartmark +- **feat(dashboard):** adaptive context-budget dial on compression panel ([#12488](https://github.com/diegosouzapw/OmniRoute/pull/12488)) — thanks @HouMinXi +- **feat(providers):** refresh Z.ai Web models and browser transport ([#12524](https://github.com/diegosouzapw/OmniRoute/pull/12524)) — thanks @backryun +- **feat(video):** redact raw client-snapshot transcript fields in the detailed log (#12150 P2) ([#12528](https://github.com/diegosouzapw/OmniRoute/pull/12528)) +- **feat(providers):** refresh NVIDIA hosted models ([#12538](https://github.com/diegosouzapw/OmniRoute/pull/12538)) — thanks @backryun +- **feat(quota):** Moonshot Open Platform balance and TPD lock for custom nodes ([#12590](https://github.com/diegosouzapw/OmniRoute/pull/12590)) — thanks @HouMinXi +- **feat(video):** redact transcript in the in-memory pending-request snapshot (#12430 item 6) ([#12596](https://github.com/diegosouzapw/OmniRoute/pull/12596)) +- **feat(dashboard):** link a log entry's Conversation Context to its owning conversation ([#12646](https://github.com/diegosouzapw/OmniRoute/pull/12646)) — thanks @hartmark +- **feat(catalog):** eligibility-gated free-tier bucket ([#12669](https://github.com/diegosouzapw/OmniRoute/pull/12669)) +- **feat(db):** fail-closed previous_response_id continuation for redacted video turns (#12150 P2b) ([#12707](https://github.com/diegosouzapw/OmniRoute/pull/12707)) +- **feat(gamification): pay the documented `streak_bonus` and `badge_unlock` XP rewards.** `XP_REWARDS` listed both rewards but the award pipeline never paid them: the private reward table in `events.ts` omitted them, `updateStreak()` did not report when a streak extended, and badge unlocks carried no XP. Every request that extends a daily streak now pays `streak_bonus × streak length` once per UTC day (guarded by a same-day `xp_audit_log` check), and every badge unlocked through the pipeline pays `badge_unlock` once per badge (guarded by the `user_badges` primary key; `unlockBadge()` now reports whether it inserted). Bonus XP flows through the same `addXp` + level sync + global/weekly/monthly leaderboard path as action XP, so level-ups and rankings include it. The Radar supporter recognition unlock stays XP-free. (#12522 — thanks @pacocartones) +- **feat(providers):** advertise a `free-tier` capability in the provider plugin manifest for every provider with documented free models, so sidecars and dashboards can filter free-capable providers without reading the quota catalog ([#12786](https://github.com/diegosouzapw/OmniRoute/pull/12786)) — thanks @maxmad64bis +- **feat(providers):** Added EURouter as an OpenAI-compatible API-key gateway (`https://api.eurouter.ai/v1`), with live model discovery via `passthroughModels`. Its copy states that models are served by third-party upstreams listed per model, so an EU-based router is not read as EU data residency for inference. ([#13025](https://github.com/diegosouzapw/OmniRoute/pull/13025)) — thanks @ntdatt812 +- **feat(providers):** Added GreenPT as an OpenAI-compatible API-key provider (`https://api.greenpt.ai/v1`), with live model discovery via `passthroughModels`. No free-inference badge: the published docs describe a free API subscription billed per token, not a free tier. ([#13024](https://github.com/diegosouzapw/OmniRoute/pull/13024)) — thanks @ntdatt812 +- **feat(i18n):** 9 new locales — Greek (`el`), Croatian (`hr`), Serbian (`sr`), Lithuanian (`lt`), Estonian (`et`), Latvian (`lv`), Slovenian (`sl`), Maltese (`mt`), Irish (`ga`) — across the dashboard, docs mirrors, CLI, README and the site; OmniRoute now ships every official EU language (51 locales). (#13044) +- **feat(i18n):** 8 new locales — Kannada (`kn`), Malayalam (`ml`), Odia (`or`), Punjabi (`pa`), Nepali (`ne`), Sinhala (`si`), Burmese (`my`), Khmer (`km`) — across the dashboard, docs mirrors, CLI, README and the site (59 locales). The translator now restores the ICU literal escape around angle placeholders and splits oversized docs sections before translating. (#0000) ([#13660](https://github.com/diegosouzapw/OmniRoute/pull/13660)) +- feat(providers): **list Agnes 3.0 Flash as the current free chat model, drop retired 1.5 Flash, add Image 2.0/2.5 Flash plus Video 2.5/2.5 Flash, and discover the live `/v1/models` catalog (including the CN host `api.agnes-ai.cn`).** `agnes-1.5-flash` now forwards to `agnes-3.0-flash`. Video 2.5 polls `GET /v1/videos/{id}` (not the V2.0 `/agnesapi` contract). Live `/v1/models` (2026-09-09) no longer serves 1.5; the wiki marks it deprecated. 3.0 Flash is 512K context / 65,536 max output, same window as 2.5. CN-region keys use the existing per-connection base-URL field, default stays `apihub.agnes-ai.com`. ([#13120](https://github.com/diegosouzapw/OmniRoute/pull/13120)) — thanks @HouMinXi +- **feat(sse):** Codex and OpenAI catalogs list GPT-6 Astra with effort aliases (`-low` through `-ultra`); Codex CLI identity pins `@openai/codex@0.153.4` in lockstep with the image ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **feat(dashboard):** the orchestration History tab gained a "Compare runs" mode — toggling it turns each grid cell into a 2-item selection queue (a 3rd click drops the oldest pick), and picking two cells opens a side-by-side comparison panel instead of the usual detail drawer. The panel fetches both runs' detail the same way the drawer does (falling back to persisted history once a run leaves the live TTL window) and shows, per side: identity/source/state, start time, a signed `right - left` delta for duration/cost/event count, the event timeline aligned by index, and any memory hits. One side's fetch failing never blocks the other, and a delta is only ever computed when both sides have a finite value — otherwise it renders "—", never `NaN`. Comparing two runs from different sources or skills still works; a banner marks the deltas as informational rather than hiding them, since the two runs aren't a strict apples-to-apples pair. ([#12677](https://github.com/diegosouzapw/OmniRoute/pull/12677)) +- **feat(dashboard):** badge a conversation that never reached a clean stop ([#12717](https://github.com/diegosouzapw/OmniRoute/pull/12717)) — thanks @hartmark +- **feat(release):** reconcile-changelog tool + version-anchored fragment aggregation ([#12987](https://github.com/diegosouzapw/OmniRoute/pull/12987)) +- **feat(mcp):** carry context_length through MCP list_models_catalog projection (#12776) ([#13406](https://github.com/diegosouzapw/OmniRoute/pull/13406)) — thanks @KooshaPari +- **feat(cli):** make startup readiness budget configurable (#13369) ([#13433](https://github.com/diegosouzapw/OmniRoute/pull/13433)) — thanks @KooshaPari + ### 🐛 Bug Fixes +- **security(streaming):** sanitize generic mid-stream error messages before emitting OpenAI, + Responses, or Claude SSE failure frames and before diagnostic logging, while preserving raw + failures for internal classification and keeping client disconnects out of provider failure state. ([#12457](https://github.com/diegosouzapw/OmniRoute/pull/12457)) +- **fix(claude):** `blockExtraUsage: false` no longer lets 5h quota preflight skip the account; extra usage is billed after the session bar is gone, so the request must reach Anthropic ([#12803](https://github.com/diegosouzapw/OmniRoute/pull/12803)) — thanks @HouMinXi +- Sanitize HuggingChat conversation-creation and message-send transport failures before they reach client error bodies or provider logs. ([#12467](https://github.com/diegosouzapw/OmniRoute/pull/12467)) +- **fix(dashboard):** Keep local and theme-aware provider SVG icons at a definite layout size so Chromium does not collapse them to 0×0 after the v3.8.50 image-rendering change ([#12054](https://github.com/diegosouzapw/OmniRoute/pull/12054)) — thanks @ponkcore +- **fix(security):** Sanitize provider and runtime failures before public API, SSE and MCP responses and before persistent request, proxy and usage logs, preventing credentials, stack traces and host filesystem paths from crossing those boundaries while preserving stable error codes and useful diagnostics. ([#12506](https://github.com/diegosouzapw/OmniRoute/pull/12506)) +- **fix(cli):** `omniroute tunnel create` no longer crashes with `Cannot read properties of undefined (reading optsWithGlobals)` — removed the duplicate positional argument that caused Commander.js to misalign the action callback parameters ([#12295](https://github.com/diegosouzapw/OmniRoute/issues/12295)) ([#12368](https://github.com/diegosouzapw/OmniRoute/pull/12368)) — thanks @gonisulaimann +- Stop advertising Gemini Live-only models as supported audio endpoints until OmniRoute proxies the bidirectional Live protocol. ([#11710](https://github.com/diegosouzapw/OmniRoute/pull/11710)) — thanks @RaviTharuma +- **fix(kie):** reroute `flux/kontext` off the KIE Market `createTask` flow — it is catalogued with `isMarket: true` but has no Market catalog page, so KIE rejected it with "model name not supported"; it now hits the dedicated `POST /api/v1/flux/kontext/generate` / `GET /api/v1/flux/kontext/record-info` endpoints instead (#11296). ([#11985](https://github.com/diegosouzapw/OmniRoute/pull/11985)) — thanks @hartmark +- **fix(kie):** correct 12 more KIE Market catalog ids that were sent to `createTask` unchanged but diverge from KIE's documented upstream `model` values — GPT Image 2 T2I/I2I (drops the `gpt/` prefix), GPT Image 1.5 T2I/I2I (`gpt-image/` namespace), Seedream 5.0 Lite T2I/I2I (drops the `.0`), all 4 Flux 2 variants (`flux-2/` namespace, generic variant renamed `flex`), and Wan 2.7 Image / Image Pro (dash instead of dot) — each verified individually against the literal example request published on docs.kie.ai. `#11326`'s "everything else already matches" claim was wrong a second time (#11296); `z-image/4.0-*`/`z-image/4.5-*` and `flux/kontext` remain open, documented as unresolved in `KIE_MARKET_UPSTREAM_MODEL_IDS`'s comment pending further verification. ([#11667](https://github.com/diegosouzapw/OmniRoute/pull/11667)) +- **fix(dashboard):** `useApiKeySave.handleSaveApiKey` no longer forces a full upstream `/models` catalog sync on every non-curated provider connection save — callers can now pass `skipModelSync: true` to opt out, so a workflow that only wants to add one manual model no longer floods the provider's available-models list with hundreds/thousands of synced entries. The flag is a client-side intent signal only and is stripped before the connection payload is POSTed to `/api/providers`; default behavior (full sync on save) is unchanged when the flag is omitted (#11324) ([#11664](https://github.com/diegosouzapw/OmniRoute/pull/11664)) +- **Provider connections:** keep `tokenExpiresAt` when a connection is created. The create-path allowlist omitted it, so every insert stored NULL and the dashboard token badge could read a fresh connection as expired until its first background refresh ([#11368](https://github.com/diegosouzapw/OmniRoute/pull/11368)). — thanks @ntdatt812 +- **fix(combos):** the combo builder's precision-select, global-model-search, and manual-entry flows now serialize a model step's `model` string using the provider's already-computed routing-alias prefix (e.g. `oc/`) instead of rebuilding it from the raw canonical `providerId`, fixing the no-auth "OpenCode Free" provider (`opencode`) being routed to the unrelated paid "OpenCode Zen" provider (`opencode-zen`) because `opencode` doubles as a manual routing-prefix override ([#11433](https://github.com/diegosouzapw/OmniRoute/issues/11433)). ([#11670](https://github.com/diegosouzapw/OmniRoute/pull/11670)) +- **fix(radar):** the catalog feed cache now keeps `generatedAt`, the date the feed's data was built, next to `fetchedAt`, the date this install downloaded it (#11435). The feed schema requires that date and the sync path validates it, but the cache dropped it — so a feed fetched minutes ago and one carrying weeks-old figures looked identical to everything downstream, including the dashboard's "Last fetched" line. `getRadarCatalog().meta` and `GET /api/radar/status` now report both dates, the latter as its own field rather than folded into `version` — and omitted entirely for the offers and intel caches, which keep no build date, where a `null` would read as "unknown" rather than "never stored". The dashboard still shows only the fetch time; surfacing the build date there needs a new translated label and is left to a follow-up. Rows cached before migration 163 read back as `null`: unknown stays unknown instead of borrowing the fetch time. The referrals cache has persisted the same date since migration 142. — thanks @maxmad64bis +- **fix(cli):** postinstall no longer fills `JWT_SECRET` and `API_KEY_SECRET` in the installed package's `.env` (#11436). `.env.example` ships both blank on purpose: the server restores them from its durable store, or generates and persists them there, in `ensureSecrets()`. Pre-filling them defeated that — the file lives inside the package directory, so `npm i -g` replaced it and postinstall wrote _different_ values, while `ensureSecrets()` (which only acts on an empty variable) never got to restore the real ones. Both secrets rotated silently on every update, invalidating dashboard sessions and API-key CRCs. `STORAGE_ENCRYPTION_KEY` left the same list for the same reason in #1622; its comment pointed at a function that no longer exists and now names the real provisioning path. — thanks @maxmad64bis +- **fix(cli):** the CLI now says when a loaded `.env` lives inside the installed package directory (#11437). It already announces every env file it reads, without distinguishing the ones that survive an update from the one that does not: `npm i -g` replaces the package directory wholesale, so values set there are gone at the next update, silently. The warning names the durable path to move them to, and fires only when that file actually supplied a value — a file entirely shadowed by a durable one supplied nothing. A development checkout stays silent: there the same path is stable and documented in `SETUP_GUIDE.md`. — thanks @maxmad64bis +- **fix(free-models):** the shared `isFreeModel()` predicate no longer reports catalog entries marked `freeType: "discontinued"` as free, so `hidePaidModels` can't route to Pollinations' seven premium models that now require a paid key ([#11441](https://github.com/diegosouzapw/OmniRoute/pull/11441)) — thanks @maxmad64bis +- fix(ui): let AnySearch use the normal provider-icon fallback when LobeHub has no matching icon (#11449) +- Fixed the v3.8.50 Costs and Analytics dashboards so flat-rate Claude Code usage can be shown as an explicitly requested token-price estimate without changing default billed-cost semantics. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed archived usage retention so each request is priced individually instead of pricing a day's summed tokens once, which understated archived cost whenever a day mixed cache-heavy and ordinary requests. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed the Costs dashboard so it discloses when displayed figures include flat-rate token-price estimates instead of labelling them as billed spend, using the flag the analytics API already returns; the month-end projection and the CSV/JSON exports carry the same marker, and billed-cost mode is unchanged. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- **fix(combo):** attach the same combo-diagnostics trace (`poolSize`/`attemptOrder`/`excluded`/`terminalReason`, plus `x-omniroute-combo-*` headers) to the round-robin strategy's and the nested pipeline/fusion runtime-unit loop's "Maximum combo retry limit reached" 503 that the priority-strategy path already attaches for the identical terminal condition — previously those two paths returned a bare, contextless 503 ([#11462](https://github.com/diegosouzapw/OmniRoute/issues/11462)). ([#11665](https://github.com/diegosouzapw/OmniRoute/pull/11665)) +- **fix(bun):** use native `bun:sqlite` in `bootstrap-env` and `sync-env` to avoid loading `better-sqlite3` N-API addon during Bun startup ([#11468](https://github.com/diegosouzapw/OmniRoute/pull/11468)) — thanks @TheDemonTuan +- **fix(docker-bun):** make `Dockerfile.bun` install reliable with pre-install script helpers and native `bun:sqlite` smoke check ([#11470](https://github.com/diegosouzapw/OmniRoute/pull/11470)) — thanks @TheDemonTuan +- **fix(docker-bun):** remove every vendored `better-sqlite3` native addon from the Bun runtime image so startup cannot abort before the `bun:sqlite` fallback ([#11482](https://github.com/diegosouzapw/OmniRoute/pull/11482)) — thanks @TheDemonTuan +- **fix(autoCombo):** effort/alias model variants (`gpt-5.6-sol-xhigh`, `gpt-5.6`, cursor's `claude-4.6-opus-high`) inherit their base model's task fitness instead of falling to the wildcard 0.5 ([#11492](https://github.com/diegosouzapw/OmniRoute/pull/11492)) — thanks @MumuTW +- fix(db): rate-limit repeated Arena ELO leaderboard fetch-failure warnings instead of logging one per sync attempt (#11500) ([#11989](https://github.com/diegosouzapw/OmniRoute/pull/11989)) — thanks @hartmark +- **fix(db):** dedupe the raw `[Encryption] Decryption failed...` log line emitted by the lazy-decrypt views (`createLazyRowProxy`/`createLazyConnectionView`), which power `getProviderConnections()` and were re-triggering that line on every CredentialHealth/model-sync cycle for the same corrupt or stale-key credential — a fresh Proxy over a fresh row on every cycle meant the per-proxy memoization never suppressed it, unlike the dedup `decryptConnectionFields()` already had since [#9927](https://github.com/diegosouzapw/OmniRoute/issues/9927). Now shares that dedupe tracking so the line logs at most once per credential ([#11500](https://github.com/diegosouzapw/OmniRoute/issues/11500)). ([#11678](https://github.com/diegosouzapw/OmniRoute/pull/11678)) +- **fix(intelligence):** Arena ELO sync stores a synthesized base-model row for every effort/harness variant (`claude-opus-5-max` → `claude-opus-5`, `gpt-5.6-sol-xhigh (codex-harness)` → `gpt-5.6-sol`), so bare model requests reach the synced intelligence layer instead of the static fallback table; the hand-maintained `MODEL_ALIAS_MAP` — which collapsed generations (`gpt-5.5` → `gpt-5`) — is gone ([#11506](https://github.com/diegosouzapw/OmniRoute/pull/11506)) — thanks @MumuTW +- **fix(autoCombo,sse):** catalog hygiene — the static task-fitness table no longer ranks retired models above live ones (dead rows dropped, patterns matched on segment boundaries), the 7 deprecation aliases that forwarded to retired or non-existent models now point at the vendors' published replacements, an alias is no longer rewritten when the serving provider offers the id as-is, and a new `check:model-lifecycle` gate diffs both tables against a vendor lifecycle snapshot ([#11507](https://github.com/diegosouzapw/OmniRoute/pull/11507)) — thanks @MumuTW +- **fix(sse):** stop dropping resolved `thoughtSignature` values on parallel (multi tool-call) turns sent to Gemini 3.x — the claude→gemini and openai→gemini translators previously kept the signature only on the *first* function call of a message, causing Gemini to reject subsequent calls in the same turn with HTTP 400 "Function call is missing a thought_signature"; each function call now keeps its own resolved signature ([#11510](https://github.com/diegosouzapw/OmniRoute/issues/11510)). ([#11669](https://github.com/diegosouzapw/OmniRoute/pull/11669)) +- Treat Docker publishing from a non-default release branch as an expected skip instead of a failed workflow, while preserving the guard that prevents it from overwriting the `next` channel. ([#11527](https://github.com/diegosouzapw/OmniRoute/pull/11527)) — thanks @pacocartones +- fix(search): prefer credentialed providers over duckduckgo-free fallback (#11524) — the fallback-only loop ran before the credentialed-providers loop in `executeWebSearch`, making configured providers unreachable when `duckduckgo-free` was available. ([#11565](https://github.com/diegosouzapw/OmniRoute/pull/11565)) — thanks @vsd2807 +- **fix(sse):** cap the upstream headers-wait phase for STREAMING requests to a client-realistic ceiling (110s, under Codex's own ~120s hard client-abort window) instead of the flat 10-minute `FETCH_TIMEOUT_MS` default — that default was 5x longer than the body-phase readiness watchdog's own adaptive bound, so a request whose upstream never returned any response at all (not even headers, e.g. a stalled NVIDIA target behind a tool-heavy Responses→Chat translation) kept the client connection alive on keepalives only, guaranteeing the client's own patience ran out first with an opaque 499 instead of OmniRoute detecting and failing the stall fast. Non-streaming requests are unaffected — they keep the existing flat default (`open-sse/utils/fetchStartTimeoutPolicy.ts`) (#11526) ([#11668](https://github.com/diegosouzapw/OmniRoute/pull/11668)) +- **fix(ci):** protocol-clients E2E harness boots the peer-stamped custom server (`run-next.mjs dev`) instead of the bare `next` CLI, so `LOCAL_ONLY` locality resolves from the real TCP peer stamp and `/api/mcp/audit` answers 200 (open bootstrap preserved via an `OMNIROUTE_E2E_BOOTSTRAP_MODE=open` credential clear in `run-next.mjs`, pinned to loopback `HOST`) instead of a deterministic 403 (#11535) ([#11549](https://github.com/diegosouzapw/OmniRoute/pull/11549)) — thanks @jonlwheat2-gif +- **fix(docs):** the Auto-Combo engine is described with the number of factors it actually declares. Four different counts were in circulation — 6, 9, 13 and 14 across `AGENTS.md`, four guides and six code comments — while `DEFAULT_WEIGHTS` declares 15. `check:docs-counts` now reads that number from the source, so adding a factor without updating the prose fails CI ([#11545](https://github.com/diegosouzapw/OmniRoute/pull/11545)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **fix(quota-share):** in-flight leases are per request, so an aborted request's slot ages out instead of being kept alive by later traffic on the same connection ([#11547](https://github.com/diegosouzapw/OmniRoute/pull/11547)) — thanks @abhisheksharma2411 +- The free-tier summary route now serves the refreshed Radar catalog when the feed is active — the same numbers the dashboard shows — and states which catalog answered plus its real build date, instead of always reporting release-frozen figures with a stale curation date. The supporter-key live feed stays reserved to authenticated callers of the instance. ([#11550](https://github.com/diegosouzapw/OmniRoute/pull/11550)) — thanks @maxmad64bis +- **sse:** fix LiveWS/embed-WS servers crashing at startup under the Node/tsx runtime — `liveServer.ts`, `embedWsProxy.ts` and `apiBridgeServer.ts` imported `@/shared/utils/httpClientAbortGuard` without the `.mjs` extension, so the client-abort crash guard added by [#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556) was unreachable and every dependent test failed with `ERR_MODULE_NOT_FOUND` ([#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556)). ([#11671](https://github.com/diegosouzapw/OmniRoute/pull/11671)) — thanks @jonlwheat2-gif / @yxyxy +- fix(chat): shed any of the matched provider-node's routing identifiers (public prefix or internal id) before model lookup, so composites like `//` no longer reach upstream verbatim (#11557) — thanks @maxmad64bis +- **fix(cache):** a semantic-cache entry now expires on its TTL instead of surviving until the next UTC midnight, and `dbEntries` stops counting expired rows ([#11573](https://github.com/diegosouzapw/OmniRoute/pull/11573)) — thanks @ntdatt812 +- **fix(api):** `/v1/models` schedules its stale-while-revalidate rebuild through Next's `after()` again, so a stale catalog reaches the client before the rebuild blocks the event loop ([#11574](https://github.com/diegosouzapw/OmniRoute/pull/11574)) — thanks @ntdatt812 +- **fix(check):** the `known-symbols` executor conformance gate awaits the now-async `getExecutor()`, so it stops reporting all 142 registered aliases as dead symbols and can detect a lazy import that fails ([#11582](https://github.com/diegosouzapw/OmniRoute/pull/11582)) — thanks @ntdatt812 +- **fix(pack):** register `bin/cli/utils/volatileEnvPath.mjs` in `PACK_ARTIFACT_REQUIRED_PATHS`, so the prune can no longer silently drop a module the published CLI imports on every boot ([#11588](https://github.com/diegosouzapw/OmniRoute/pull/11588)) — thanks @ntdatt812 +- **i18n(vi):** translate the five reliability-column strings on the free-provider rankings page, which rendered as `__MISSING__:` markers in the Vietnamese UI ([#11589](https://github.com/diegosouzapw/OmniRoute/pull/11589)) — thanks @ntdatt812 +- **resilience:** restore the expired-connection retry-budget probe in the token-health sweep — the `!isGitHubAccessTokenOnlyConnection` carve-out reintroduced by #11608 contradicted the boundary pinned by #11592, so a GitHub connection parked at `expired` with retry budget remaining was never probed and could never self-heal ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)). ([#11672](https://github.com/diegosouzapw/OmniRoute/pull/11672)) — thanks @jonlwheat2-gif / @ntdatt812 +- **fix(dashboard):** Provider Stats now displays combo success rates on their native 0–100 percentage scale instead of multiplying them twice ([#11595](https://github.com/diegosouzapw/OmniRoute/pull/11595)) — thanks @pacocartones +- **fix(providers):** route DuckDuckGo Web Chat through the verified `duck.ai` endpoint, refresh its free model catalog, and avoid forwarding unsolved anti-abuse challenges ([#11597](https://github.com/diegosouzapw/OmniRoute/pull/11597)) — thanks @solstxce +- **fix(ui):** Console log Refresh and Copy controls now expose localized accessible names, keep copy actions visible on keyboard focus, and announce copy completion safely ([#11599](https://github.com/diegosouzapw/OmniRoute/pull/11599)) — thanks @pacocartones +- Dropped the stale-`.eslintcache` `restore-keys` fallback from both "Restore ESLint file cache" steps in `ci.yml`, so the blocking `Lint` job can no longer be served per-file verdicts computed under a different lint config, suppressions file or lockfile. `quality.yml` had already dropped it in #11963; `ci.yml` — the workflow that actually gates PRs — had not (#11600). ([#11996](https://github.com/diegosouzapw/OmniRoute/pull/11996)) +- fix(ci): pass `--pass-on-unpruned-suppressions` in `run-eslint-json.mjs` so the CI Lint job no longer fails when a suppression is merely orphaned by a genuine fix, mirroring the identical fix already in `validate-release-green.mjs` (#11600) ([#11983](https://github.com/diegosouzapw/OmniRoute/pull/11983)) — thanks @hartmark +- fix(handoff): enforce provider allowlist for universal handoff (#11602) — universal handoff now skips summarization when the selected summary model's provider is not included in the configured provider allowlist. ([#11619](https://github.com/diegosouzapw/OmniRoute/pull/11619)) — thanks @vsd2807 +- **fix(dashboard):** Show a stable error state when Search Analytics returns an HTTP error or malformed data ([#11603](https://github.com/diegosouzapw/OmniRoute/pull/11603)) — thanks @pacocartones +- **test(gamification):** pin the aggregate profile level to the XP-derived semantics of #11604 — `getAggregateXp()` now derives `currentLevel` from the summed XP (`calculateLevel(sum)`), not `MAX(stored current_level)`, and the #3484 fixture levels are aligned with the XP curve ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)). ([#11676](https://github.com/diegosouzapw/OmniRoute/pull/11676)) — thanks @jonlwheat2-gif / @pacocartones +- **fix(dashboard):** Keep the Profile level and progress aligned with aggregate XP, including bounded handling for invalid totals ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)) — thanks @pacocartones +- **fix(dashboard):** Prevent locked hidden badges from revealing their icon or opening private badge details before they are earned ([#11605](https://github.com/diegosouzapw/OmniRoute/pull/11605)) — thanks @pacocartones +- **fix(dashboard):** Restore keyboard focus after shared modals close and cancel delayed autofocus during cleanup ([#11607](https://github.com/diegosouzapw/OmniRoute/pull/11607)) — thanks @pacocartones +- **fix(dashboard):** Enable Enter and Space activation for clickable data-table rows without hijacking nested controls ([#11610](https://github.com/diegosouzapw/OmniRoute/pull/11610)) — thanks @pacocartones +- **fix(autoCombo,sse):** vendor-retired catalog ids are dropped from the auto-combo candidate pool and no longer win on leftover `arena_elo` / `user_override` rows; `getModelLifecycleDecision` consults `model-lifecycle.json` (prefix-stripped) so aggregator traffic is not `untracked` for ids the snapshot already knows ([#11625](https://github.com/diegosouzapw/OmniRoute/issues/11625)) ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)) — thanks @MumuTW +- **test(chatcore):** move Codex/Claude combo fixtures off models the lifecycle guard now rejects — `gpt-5.1-codex`/`gpt-5-codex` are vendor-retired (snapshot, #11626) and `claude-3-5-sonnet-20241022` is shut down, so native-passthrough and combo-fallback tests switched to `gpt-5.6-sol` and `claude-sonnet-4.6` ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)). ([#11675](https://github.com/diegosouzapw/OmniRoute/pull/11675)) — thanks @jonlwheat2-gif / @MumuTW +- fix(opencode-plugin): stop a free-tier budget that rounds up across a magnitude from rendering as `1000K`/`1000M` in the model picker — `fmtTokens` chose its unit from the raw token count and then rounded with `toFixed(1)`, so 999,950–999,999 printed as `1000K` rather than `1M` and just under a billion printed as `1000M` rather than `1B` ([#11684](https://github.com/diegosouzapw/OmniRoute/pull/11684)) — thanks @abhisheksharma2411 +- Cap prose-derived Antigravity quota resets at the configured model cooldown maximum while preserving authoritative Retry-After headers and Google RetryInfo hints. (#11695) Thanks @pacocartones. ([#11823](https://github.com/diegosouzapw/OmniRoute/pull/11823)) +- Compare `_rebaseline_*` notes structurally in the ratchet-bank verifier so an object-valued note no longer reports itself as rewritten on an unmodified tree — the nightly `bank-ratchet-shrinks` lane had been aborting on every run since 2026-08-11, leaving 41,672 lines of earned file-size shrinks unbanked (#11702). ([#11892](https://github.com/diegosouzapw/OmniRoute/pull/11892)) — thanks @MumuTW +- **fix(build):** `prepublish.ts` bundles the ChatGPT Web (Codex) MCP bridge through `runBuildTool()` instead of spawning `npx.cmd` raw, fixing the build crash on Node ≥ 20/Windows where `.cmd` shims cannot be spawned without a shell (EINVAL) ([#11704](https://github.com/diegosouzapw/OmniRoute/issues/11704)) ([#11706](https://github.com/diegosouzapw/OmniRoute/pull/11706)) — thanks @jonlwheat2-gif +- fix(codex): keep `parallel_tool_calls:false` on the translated Codex Responses Lite path (#11707) ([#11984](https://github.com/diegosouzapw/OmniRoute/pull/11984)) — thanks @hartmark +- **fix(tests):** the new Gemini Web Images retirement test (#11708) called the now-async `getExecutor()` synchronously, throwing `TypeError: getExecutor(...).getProvider is not a function` instead of asserting Gemini Web chat stays available. Awaited to match the R0.3 executor-registry refactor (#11220). ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(search):** Allow REST and MCP web-fetch requests to select the advertised `anysearch-search` provider explicitly ([#11716](https://github.com/diegosouzapw/OmniRoute/pull/11716)) — thanks @pacocartones +- **fix(providers):** Keep overnight peak-hour protection active after midnight according to the window's configured start day ([#11718](https://github.com/diegosouzapw/OmniRoute/pull/11718)) — thanks @pacocartones +- **fix(providers):** correct all five g4f.space gateway descriptions to document proof-of-work credits or member API keys, variable limits, and the remote prompt boundary instead of promising free keyless access at five requests per minute ([#11738](https://github.com/diegosouzapw/OmniRoute/pull/11738)). +- **fix(db):** `model_capabilities` is created by a migration instead of lazily on the first models.dev sync, so a clean install and an upgraded install converge on the same schema regardless of which features have run ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(ci):** `check:install-upgrade` now fails on an `npm` install truncated by ENOSPC (npm reports it as a warning and still exits 0), authenticates its health probe so the version assertion works against the hardened health payload, frees the clean-install tree before the upgrade phase, and no longer reports a schema divergence computed from a boot that never served ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(routing):** harden session-affinity key derivation against oversized explicit IDs and empty structural-payload collisions, while recognizing Gemini and common text request shapes ([#11746](https://github.com/diegosouzapw/OmniRoute/pull/11746)) — thanks @MumuTW +- **fix(packages/browser-pool):** regenerate `package-lock.json` so the `packages/browser-pool` workspace's locked `playwright`/`@types/node` (and transitives) match its `package.json` specs, fixing cache-only/offline installs (`npm ci --offline`, Nix `buildNpmPackage`) that previously failed with `ENOTCACHED` ([#11747](https://github.com/diegosouzapw/OmniRoute/issues/11747)) — thanks @benjaminkitt ([#11784](https://github.com/diegosouzapw/OmniRoute/pull/11784)) — thanks @geekyNads +- **fix(assets):** Stop distributing 79 provider and CLI logos without verified source/license provenance, keep every provider/tool available through the internal generic icon, and prevent those IDs from falling through to an external icon CDN. ([#11750](https://github.com/diegosouzapw/OmniRoute/pull/11750)) +- **fix(providers):** retire the common `chatgpt-web` / `cgpt-web` provider because its pre-key and proof-of-work provenance could not be cleared ([#11754](https://github.com/diegosouzapw/OmniRoute/pull/11754)) — explicit chat and image requests, slash-prefixed models, persisted aliases, proxy overrides, and combo targets now fail closed with a sanitized HTTP `410` / `PROVIDER_RETIRED` before upstream dispatch. Migration 163 tombstones matching connections and invalidates their active leases without deleting history or widening API-key allowlists. The independent `chatgpt-web-codex` / `cgpt-codex` provider and its MIT-noticed vendored implementation remain available. +- **fix(usage):** quota and usage refresh no longer 409 when an exclusive lease reserves the connection ([#11758](https://github.com/diegosouzapw/OmniRoute/pull/11758)) — thanks @TheDemonTuan +- Keep the embedding registry's vector width and `embedding` type on models when a synced model exists for the same id, so `/v1/models` no longer reports registry-described embedding models widthless or untyped (#11761) — thanks @rahilmavani +- Correct `google/gemini-embedding-001` on the OpenRouter route to 3072 dimensions, the width it returns when `dimensions` is not sent (#11761) — thanks @rahilmavani +- **fix(vision-bridge):** Restrict automatic vision routing and fallbacks to models available in authoritative live provider catalogs, including aliases and effort variants ([#11767](https://github.com/diegosouzapw/OmniRoute/issues/11767)) — thanks @pacocartones ([#11813](https://github.com/diegosouzapw/OmniRoute/pull/11813)) +- **fix(providers):** reclassify Cerebras as a one-time $5 signup credit (payment method required, 30-day validity), not a recurring no-card 1M tokens/day trial ([#11773](https://github.com/diegosouzapw/OmniRoute/issues/11773)) ([#12591](https://github.com/diegosouzapw/OmniRoute/pull/12591)) — thanks @HouMinXi +- fix(cli): stop prepublish from re-rebuilding the already-built ESM-only opencode-plugin dist (#11787) ([#11990](https://github.com/diegosouzapw/OmniRoute/pull/11990)) — thanks @hartmark +- **fix(db):** Fresh installs no longer log a non-fatal `no such table: compression_run_telemetry` warning when retention cleanup runs before the lazily-created telemetry table exists ([#11802](https://github.com/diegosouzapw/OmniRoute/pull/11802)) — thanks @RaviTharuma +- fix(sse): set X-OmniRoute-Selected-Connection-Id on successful combo dispatches so downstream consumers stop falling back to an empty connection id (#11810) ([#11986](https://github.com/diegosouzapw/OmniRoute/pull/11986)) — thanks @hartmark +- **fix(services):** embedded CLIProxyAPI lifecycle checks now use public `/healthz`, while model discovery uses the configured dedicated data-plane API key instead of the management password ([#11811](https://github.com/diegosouzapw/OmniRoute/pull/11811)) — thanks @RaviTharuma +- Fix persisted model steps returning an error in the playground route simulator, and warn when structural steps cannot be simulated. ([#11862](https://github.com/diegosouzapw/OmniRoute/pull/11862)) — thanks @pacocartones +- **fix(providers):** Antigravity's dynamic mitmAlias table no longer routes `gemini-3.7-flash-{high,medium,low}` to a literal tier-suffixed upstream id just because one connected account's own discovery listed it directly — those display ids always resolve through the safe `gemini-3.7-flash-tiered` static alias, so one account's Google-provisioned access no longer 404s every sibling account of the provider ([#11824](https://github.com/diegosouzapw/OmniRoute/issues/11824), [#11651](https://github.com/diegosouzapw/OmniRoute/issues/11651)) ([#11988](https://github.com/diegosouzapw/OmniRoute/pull/11988)) — thanks @hartmark +- **fix(dashboard):** Expose Profile loading, errors, page structure, and XP progress to assistive technologies ([#11838](https://github.com/diegosouzapw/OmniRoute/pull/11838)) — thanks @pacocartones +- **fix(socks):** `Agent.connectTimeout` now bounds both SOCKS handshake and TLS connect, and `family === null` no longer falls back to `fetch-socks` ([#11842](https://github.com/diegosouzapw/OmniRoute/pull/11842)) — thanks @maxmad64bis +- **perf(compression):** OOM mitigations for large payload hashing, memoization, and token estimation ([#11844](https://github.com/diegosouzapw/OmniRoute/pull/11844) — thanks @AndrianBalanescu) +- Excluded Next.js Node File Trace manifests (`*.nft.json`) from the published npm tarball. They are build-time metadata and are never read while serving, but had grown to 668.7 MB — 61% of the package — which pushed the upload past the registry limit and made `npm publish` fail with `413 Payload Too Large`. ([#11864](https://github.com/diegosouzapw/OmniRoute/pull/11864)) +- **fix(cli):** keep the global OmniRoute server `--base-url` separate from provider-node `--endpoint` values in add, update, and validate commands ([#11860](https://github.com/diegosouzapw/OmniRoute/pull/11860)) — thanks @pacocartones +- **fix(config):** Nous Research's `Hermes-4-405B` model now displays as "Hermes 4 405B (Nous Research)" in both the provider registry and the free-model catalog, instead of the mislabelled "Hermes 4 7B" ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#11993](https://github.com/diegosouzapw/OmniRoute/pull/11993)) +- **fix(provider/nous):** inject required user= tag into Nous Research inference requests to resolve upstream 400 "missing tags" error ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#12044](https://github.com/diegosouzapw/OmniRoute/pull/12044)) +- **fix(build):** `npm run build` now fails in one second with a named package and a copy-pasteable fix when npm silently drops an externalised optional native dependency, instead of dying four minutes in with `Module not found: Can't resolve 'better-sqlite3'` ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- **fix(install):** `postinstall` no longer throws `ReferenceError: isAndroid is not defined` — failing the whole `npm install` — when the `better-sqlite3` rebuild fallback times out; the manual-fix guidance is reachable again ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- Fixed the Alibaba free-tier allowlist test that went red on its own once the shipped catalog's `validUntil` (2026-08-27) passed, leaving every PR and `main` with a failing `Unit Tests (1/8)`. The test now builds its own packs with dates it controls, and covers the expired-pack fallback that production has actually been serving. ([#11867](https://github.com/diegosouzapw/OmniRoute/pull/11867)) +- Split the npm registry upload into its own GitHub-hosted job. npm refuses `--provenance` from a self-hosted runner (`422 ... Only "github-hosted" runners are supported`), which blocked the v3.8.50 publish; the heavy verification cannot move to a hosted runner, so it now hands the proven tarball over instead. ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(ui):** the shared `DataTable` loading state no longer reads its decorative ⏳ glyph out to assistive technology, and now carries the same `role="status"` / `aria-live="polite"` / `aria-busy="true"` semantics as `PageLoading` ([#11869](https://github.com/diegosouzapw/OmniRoute/pull/11869)) — thanks @pacocartones +- **fix(api):** Bound the `timeout` accepted by `POST /api/providers/[id]/login` to the same 15s–600s window the Adobe Firefly and Conol browser-login paths already enforce, so a request can no longer pin a headful browser and the single active-login slot with an arbitrarily large poll budget. ([#11871](https://github.com/diegosouzapw/OmniRoute/pull/11871)) — thanks @pacocartones +- **fix(api):** Stop the tunnel and MITM routes from returning raw child-process error text, which disclosed host paths, binary install locations and Tailscale `tskey-*` credentials in a body some of these routes serve to non-loopback callers, and return a real 400 instead of a framework 500 when a tunnel request body fails validation. ([#11872](https://github.com/diegosouzapw/OmniRoute/pull/11872)) — thanks @pacocartones +- **fix(open-sse):** stream TTFT/ITL are sampled from a monotonic clock (`performance.now`) instead of `Date.now`, so an NTP correction or wall-clock jump can no longer inflate a genuine TTFT or produce a negative one that the `ttft >= 0` guard silently drops — protecting the router's OTel/EWMA/`usage_history`/speed-ranking signals ([#11873](https://github.com/diegosouzapw/OmniRoute/pull/11873)) — thanks @pacocartones +- **fix(providers):** xAI `reasoning_effort: "xhigh"` now reaches grok-4.6+ instead of being silently clamped to `"high"` ([#11879](https://github.com/diegosouzapw/OmniRoute/pull/11879)) — thanks @NoxzRCW +- **fix(dashboard):** the providers page no longer crashes into the error boundary when a provider id collides with an `Object.prototype` member (`constructor`, `__proto__`); icon lookups are own-property guarded ([#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)) — thanks @NoxzRCW +- **fix(skills):** injected skill tools declared in shorthand (`{"content": "string"}`) now forward valid JSON Schema, unblocking providers that validate tool schemas strictly such as Zhipu GLM on the Console Go tier ([#11881](https://github.com/diegosouzapw/OmniRoute/pull/11881)) — thanks @NoxzRCW +- **fix(api):** `POST /api/playground/simulate-route` now surfaces `combo-ref` and `provider-wildcard` persisted combo steps with a specific warning (naming the referenced combo, or the unresolved `provider/model` wildcard) instead of folding them into a generic "unsupported step" count; a `provider-wildcard` step is also now included as an unresolved target so the operator can see it is in the route (ported from [#11882](https://github.com/diegosouzapw/OmniRoute/pull/11882) — thanks @NoxzRCW). ([#11930](https://github.com/diegosouzapw/OmniRoute/pull/11930)) +- **fix(translator):** the streaming OpenAI→Claude translator keeps upstream usage, including prompt-cache tokens, when it arrives on a trailing `choices: []` chunk (Fireworks and any upstream using `stream_options.include_usage`) ([#11883](https://github.com/diegosouzapw/OmniRoute/pull/11883)) — thanks @NoxzRCW +- **fix(cli):** `omniroute update --apply` now tells you explicitly whether a running server was detected and, if so, that you must run `omniroute restart` to apply the update — it never restarted anything and previously implied the update was already live once files were installed. The dashboard's npm-mode Update flow (`/api/system/version`) now tries OmniRoute's own PID-file-managed supervisor before falling back to pm2, and reports an honest "restart required" step instead of a silent pm2-only "skipped" that read like a completed update. The server-side latest-version lookup backing the dashboard's update banner also gained `--prefer-online`, closing the same stale-npm-cache class already fixed in the CLI's own copy for #4376 ([#11885](https://github.com/diegosouzapw/OmniRoute/issues/11885)). ([#12005](https://github.com/diegosouzapw/OmniRoute/pull/12005)) +- **fix(resilience):** clear persisted LKGP pins when a target suffers connection/provider exhaustion or is skipped before dispatch due to cooldown/exhaustion/unavailability, preventing subsequent requests from repeatedly prioritizing known-dead providers ([#11911](https://github.com/diegosouzapw/OmniRoute/issues/11911)). ([#12013](https://github.com/diegosouzapw/OmniRoute/pull/12013)) — thanks @HouMinXi +- **fix(sse):** the OpenAI→Claude stream translator now defers the terminal `message_delta`/`message_stop` emission until the real usage block has arrived (or a genuine end-of-stream flush forces it) instead of emitting it immediately on `finish_reason` — previously, when the trailing usage-only chunk (`{"choices":[],"usage":{...}}`) arrived *after* the `finish_reason` chunk (the normal order for Fireworks/vLLM/Together and other `stream_options.include_usage` upstreams), the client-visible `message_delta` still carried stale/zero usage even though `state.usage` was internally corrected too late to matter (ported from [#11915](https://github.com/diegosouzapw/OmniRoute/pull/11915) — thanks @HouMinXi). ([#11933](https://github.com/diegosouzapw/OmniRoute/pull/11933)) +- **fix(sse):** `stripStore()` now forces `store=false` for stateless OpenAI-compatible Responses-API targets (unless the connection has explicitly opted in via `providerSpecificData.openaiStoreEnabled`), instead of only handling the `openai`/`agentrouter` cases — a client-supplied `store` value previously passed through untouched to backends that don't actually persist responses server-side ([#11916](https://github.com/diegosouzapw/OmniRoute/pull/11916) — thanks @HouMinXi). +- **fix(models):** custom provider-node models (synced, custom, and alias-backed) now appear under their configured prefix in the unified catalog when the operator's model-id prefix mode is canonical, instead of being dropped whenever alias-inclusion was otherwise disabled ([#11918](https://github.com/diegosouzapw/OmniRoute/pull/11918) — thanks @HouMinXi). +- **fix(models):** the unified model catalog now suppresses stale static registry models (including effort-tier variants) for any provider whose active connection has an authoritative live synced catalog, not only providers already using exclusive-synced-listing — a connection with `providerUsesAuthoritativeLiveCatalog` previously kept serving both the live-synced models and the stale static rows side by side ([#11919](https://github.com/diegosouzapw/OmniRoute/pull/11919) — thanks @HouMinXi). +- **fix(ui):** `ProviderIcon`'s three remaining unguarded lookups (`PROVIDER_ICON_ALIASES`, `LOCAL_SVG_ALIASES`, `THEMED_SVGS`) now use `Object.hasOwn()` own-property checks — a provider id such as `constructor` or `__proto__` previously resolved through the prototype chain instead of falling through to the unknown-provider CDN fallback (`getLobeProviderIcon()` itself was already guarded by [#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)); `ProviderPageHeader`'s `color` field is also now optional, matching the rest of the component's defensive typing (ported from [#11920](https://github.com/diegosouzapw/OmniRoute/pull/11920) — thanks @HouMinXi). ([#11935](https://github.com/diegosouzapw/OmniRoute/pull/11935)) — thanks @NoxzRCW +- fix(ollama): preserve multi-byte UTF-8 content split across stream chunks in the Ollama NDJSON transform, which previously corrupted CJK/emoji into U+FFFD (#11921) — thanks @pacocartones +- **fix(providers):** OrcaRouter chat requests now target `/v1/chat/completions` instead of the bare `/v1` API root, fixing the upstream `404 Invalid URL (POST /v1)` ([#11923](https://github.com/diegosouzapw/OmniRoute/pull/11923)). — thanks @echel0nn +- **fix(plugins):** deliver the `onStreamComplete` event to disk-installed plugins. The event shipped in v3.8.50 (#9669) was emitted internally but had no plugin-facing wiring, so no plugin could ever subscribe: the manifest schema silently dropped `hooks.onStreamComplete`, and the loader/manager only knew the seven legacy hooks. `onStreamComplete` is now a declarable manifest hook, wired through the loader and registered by the manager like the other hooks, and its payload carries a `requestId` so consumers can correlate the stream-completion event with the originating request ([#11934](https://github.com/diegosouzapw/OmniRoute/pull/11934)) — thanks @amaleta / @pacocartones +- **fix(db):** the Qdrant embedding-model dropdown now lists local/self-hosted providers (Ollama, LM Studio, vLLM, etc.) — an active connection is treated as "configured" when the provider allows an optional API key, not only when it has a real key or OAuth, so a running local embedding provider is no longer hidden from the picker ([#11949](https://github.com/diegosouzapw/OmniRoute/issues/11949)) ([#11995](https://github.com/diegosouzapw/OmniRoute/pull/11995)) +- **fix(providers):** Vertex AI Anthropic partner-model discovery now calls the Model Garden `v1beta1` publisher list (`/v1beta1/publishers/anthropic/models`, global) and parses its `publisherModels` envelope, so Claude models auto-synced from Vertex populate the active live catalog and route at request time instead of returning `Model '' is not available in the active live catalog` ([#11991](https://github.com/diegosouzapw/OmniRoute/issues/11991)) — thanks @fabioluissilva ([#11998](https://github.com/diegosouzapw/OmniRoute/pull/11998)) +- **fix(cli):** support `--base-url` alongside `--endpoint` in `omniroute nodes add`, `update`, and `validate` subcommands to prevent global `--base-url` shadowing issues ([#11999](https://github.com/diegosouzapw/OmniRoute/issues/11999)). ([#12033](https://github.com/diegosouzapw/OmniRoute/pull/12033)) — thanks @HouMinXi +- **fix(providers):** `cloudflare-ai` no longer refuses image content parts for every Workers AI model ([#12002](https://github.com/diegosouzapw/OmniRoute/pull/12002)) — the plain-string `content` requirement behind #2539 is carried by the _model_ schema, not by the `/ai/v1/chat/completions` endpoint (measured: an all-text part array returns 200 on `@cf/mistralai/mistral-small-3.1-24b-instruct`, `@cf/meta/llama-4-scout-17b-16e-instruct` and `@cf/meta/llama-3.3-70b-instruct-fp8-fast`, and 400 on the text-only `@cf/qwen/qwen2.5-coder-32b-instruct`). `transformRequest()` flattened every array and threw on the first non-text part (#6390), so image input was refused for vision-capable Cloudflare models that accept it. All-text arrays are still flattened — the one shape every model accepts — while an array carrying a non-text part is passed through untouched, so the attachment is still never silently dropped. Regression guards: `tests/unit/cloudflare-ai-image-parts-6390.test.ts`. — thanks @davidlinfr +- **fix(resilience):** decouple the limiter-managed execution backstop from the queue-wait budget — new `requestQueue.executionMaxWaitMs` (env `RATE_LIMIT_EXECUTION_MAX_WAIT_MS`, default 600000 = 10 min) now feeds Bottleneck's post-dispatch `expiration`, while `requestQueue.maxWaitMs` keeps its documented queue-wait semantics. Previously the queue-wait budget doubled as the execution expiration, so legitimate long-running calls on non-incremental gateways (whole generation buffered before the first upstream byte, e.g. Console Go / Command Code tiers serving GLM models) were killed mid-flight at the queue budget with a false 504 `RATE_LIMIT_EXECUTION_TIMEOUT` — the local limiter undercut the provider-aware upstream fetch-start timeouts. The surfaced 504 message now names `requestQueue.executionMaxWaitMs`; the error keeps the #4165 guarantees (disclaims an upstream timeout, preserves the Bottleneck error as `cause`, branded code + trusted provenance, classified request-scoped so combo falls back). A real queue-wait bound (the `Promise.race` around `limiter.schedule()` sketched in #9533) remains future work. (#12025) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **Call logs:** keep the `error` field when an artifact exceeds the storage cap, instead of replacing it with the omission marker. The error is the only field that says *why* a request failed and is typically ~90 bytes next to the multi-hundred-KB bodies that trip the cap, so dropping it left a size-limited row undiagnosable — a provider outage, a local timeout and an upstream 400 all rendered identically. It is now preserved at every fallback stage, truncated to 4KB if it is itself large ([#12026](https://github.com/diegosouzapw/OmniRoute/issues/12026)). ([#12095](https://github.com/diegosouzapw/OmniRoute/pull/12095)) — thanks @ntdat812 +- **fix(diagnostics):** preserve the error field (truncated to 4KB with a `[truncated: …]` suffix) in every call-log artifact size-limit fallback stage. Previously the minimal fallback replaced the error with `[omitted: call log artifact size limit exceeded]`, so an oversized artifact row showed nothing about WHY the request failed — e.g. 91 of 847 opencode-go 504 rows on one production instance were undiagnosable from the dashboard. Oversized request/response bodies are still omitted exactly as before; the error cap is independent of the payload sizes that tripped the fallback. (#12026) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **fix(sse):** OpenAI Responses clients that declare the native `web_search` tool now receive a spec-shaped `web_search_call` output item with `action.sources` alongside the preserved function-call round-trip, so search results executed through OmniRoute's own search backend are consumable by standard Responses clients (Codex, pi-web-access, …). ([#12031](https://github.com/diegosouzapw/OmniRoute/pull/12031)) — thanks @watchingdogs +- **fix(cli):** use in-thread alias resolver hooks on modern runtimes to avoid deprecation noise and improve Node.js forward compatibility ([#12073](https://github.com/diegosouzapw/OmniRoute/issues/12073)). ([#12083](https://github.com/diegosouzapw/OmniRoute/pull/12083)) — thanks @xiaoyaner0201 +- **fix(combo):** an operator-set **Agent Features → Context length** on a combo is now honored at request time. The value was persisted and advertised through `/v1/models`, but `resolveComboContextLimit()` never consulted it — so a multi-target combo whose members carry no per-model window fell through to the provider's generic `defaultContextLength` (openrouter 128000, command-code 200000) and rejected large requests with `Input exceeds context window … limit 128000` despite the combo being explicitly sized much larger. An identical single-target combo worked, because it collapses to its concrete target before the guard runs. Invalid values (0/negative/NaN/Infinity) are ignored, so the existing target → combo-min → fallback order is unchanged. ([#12090](https://github.com/diegosouzapw/OmniRoute/pull/12090)) — thanks @adivekar-utexas +- **fix(sse):** passthrough streams now estimate usage on finish when upstream closes without usage even with `stream_options.include_usage` — avoids `0 tokens / 0%` for providers that stay silent (and correctly handles trailing empty-choices usage) ([#12151](https://github.com/diegosouzapw/OmniRoute/pull/12151)) — thanks @maxmad64bis +- **fix(combos):** clearing an agent feature in the combos editor now persists — unchecking context cache protection, or emptying the system message or tool filter, sends an explicit `null` instead of dropping the field from the `PUT` body, which the update merge read as "leave unchanged" ([#12177](https://github.com/diegosouzapw/OmniRoute/pull/12177)) — thanks @foreveryh / @ntdatt812 +- **fix(memory):** self-hosted embedding endpoints now vectorize — the vector width is measured from the first embedding that comes back instead of being read from a registry that cannot describe them, so `vec_memories` is created and memories stop piling up unvectorized behind a green health check ([#12180](https://github.com/diegosouzapw/OmniRoute/pull/12180)) — thanks @kanade-hoshino / @ntdatt812 +- **fix(translator):** the leading `system` message now reaches Responses-API upstreams when its `content` is a content-part array — it was read as `typeof content === "string" ? content : ""`, so a prompt-caching client (Anthropic `cache_control`, the shape LiteLLM and the Anthropic SDK emit) had its entire system prompt replaced by an empty `instructions`. The request was still accepted with a normal `prompt_tokens` count, so the model answered with no instructions and nothing in the response said they were missing. Mid-conversation system turns already handled the array shape ([#7056](https://github.com/diegosouzapw/OmniRoute/pull/7056)); only the first one did not ([#12206](https://github.com/diegosouzapw/OmniRoute/issues/12206)). Regression guard: `tests/unit/translator-openai-responses-system-content-parts.test.ts`. ([#12207](https://github.com/diegosouzapw/OmniRoute/pull/12207)) — thanks @Gorillaz322 +- **fix(free-tier):** `/api/free-tier/summary` no longer computes its totals from a Radar feed built before the catalog the running release ships. When the cached feed is older — or carries no build date at all — the route answers from the shipped catalog, resolved through the operator's local model state so disabled and tombstoned models stay out of the numbers ([#12215](https://github.com/diegosouzapw/OmniRoute/pull/12215)). — thanks @maxmad64bis +- **fix(oauth):** Keep a Claude personal workspace and a Team organization as separate connections — they share the same email and `accountUUID`, so the email-only OAuth dedup let the second login overwrite the first account's tokens; `organizationUUID` now disambiguates them, the way `workspaceId` does for Codex ([#12222](https://github.com/diegosouzapw/OmniRoute/pull/12222)) — thanks @davidebaraldo +- **fix(resilience):** a 402 on a single paid model of a passthrough/gateway provider (e.g. `kilo-gateway`, `ollama-cloud`) no longer terminalizes the whole connection with a never-auto-recovered `credits_exhausted` status — only the paid model is locked out, so free models on the same key keep serving. 402 variant of [#3027](https://github.com/diegosouzapw/OmniRoute/issues/3027). Single-credential providers are unaffected — a 402 there is still treated as the key being genuinely out of credit ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239) / [#10616](https://github.com/diegosouzapw/OmniRoute/issues/10616)) ([#12242](https://github.com/diegosouzapw/OmniRoute/issues/12242)) — thanks @brick30llc-ctrl ([#12266](https://github.com/diegosouzapw/OmniRoute/pull/12266)) — thanks @geekyNads +- **fix(sse):** trust `finish_reason: "length"`/`"max_tokens"` over the reasoning-consumed-token ratio in response quality validation, so a reasoning model truncated below the old 90% threshold correctly fails and retries instead of returning empty content as a silent "success" ([#12262](https://github.com/diegosouzapw/OmniRoute/pull/12262)) — thanks @brick30llc-ctrl +- **fix(settings):** `PUT /api/settings/cache-config` now persists `alwaysPreserveClientCache` to the flat general settings the runtime cache-control policy actually reads; previously the value landed in the databaseSettings "cache" section and was silently ignored, so the endpoint had no effect on `cache_control` passthrough ([#12304](https://github.com/diegosouzapw/OmniRoute/pull/12304)) — thanks @davidebaraldo +- **fix(grok-cli):** treat omitted SuperGrokPro `creditUsagePercent` as 0% used so Provider Limits still renders a weekly bar (proto3 zero-elision) ([#12312](https://github.com/diegosouzapw/OmniRoute/pull/12312)) — thanks @HouMinXi +- **fix(combo):** Expose the two Auto-Combo scoring factors nobody could set — the combo validation schema and the dashboard weight sliders both declared 13 of the scorer's 15 factors, so `connectionDensity` (spreads load across a provider's connections) and `quality` were dropped on save and offered nowhere. The sliders also shipped their own default table that differed from the engine's on every non-zero factor and summed to 1.05, so the percentages shown next to them added up to 105%. Both lists now match `DEFAULT_WEIGHTS`, and a test keeps them there. Note that a combo whose stored `weights` omitted the two keys was effectively running with them at zero and the other thirteen renormalized upward; it now runs with the engine's intended distribution, so its routing does shift ([#12314](https://github.com/diegosouzapw/OmniRoute/pull/12314)) — thanks @maxmad64bis +- **fix(docs):** The free-tier reference no longer says its numbers come "confidence tagged per row" — no catalog entry carries a confidence tag and the API serves none, so every figure on that page is an estimate of the same, unstated quality. The page now states what an entry does vouch for: an independently documented hard stop (set by hand with the source in a comment, never defaulted to `true`) and a prompt-training disclosure, both with live counts the `check:docs-counts` gate keeps honest ([#12318](https://github.com/diegosouzapw/OmniRoute/pull/12318)) — thanks @maxmad64bis +- **fix(usage):** `adobe-firefly` and `firefly` have had a working usage fetcher since Adobe Firefly landed, but neither was ever added to the registration list, so the provider-plugin manifest, `genericQuotaFetcher` and the free-access quota cache all reported them as having no usage support — while `USAGE_SUPPORTED_PROVIDERS` said the opposite. Both are now declared, which also means their credit balance is fetched like any other declared provider's: `registerGenericQuotaFetchers` now registers a generic quota fetcher for them, and `resolveFreeAccessState` no longer returns early. A test holds the registration list to the dispatcher's switch in both directions, which is what the module's own docstring already asked for in prose ([#12321](https://github.com/diegosouzapw/OmniRoute/pull/12321)) — thanks @maxmad64bis +- **fix(quota):** drop the generic quota cache (agy / Antigravity / Claude OAuth) on an upstream 429 so reset-aware scoring does not keep a 60s stale snapshot, and force-refresh the next usage fetch so inner provider caches cannot recache the same window ([#12325](https://github.com/diegosouzapw/OmniRoute/pull/12325)) — thanks @HouMinXi +- **fix(combos):** deleting a combo now clears its persisted LKGP pins instead of leaving unreachable `key_value` rows behind ([#12326](https://github.com/diegosouzapw/OmniRoute/issues/12326)) ([#12425](https://github.com/diegosouzapw/OmniRoute/pull/12425)) — thanks @KrzysiekSko +- **fix(sse):** Keep ZWNJ (U+200C) and ZWJ (U+200D) in assistant text, reasoning and tool-call arguments — Persian/Kurdish half-space (`ارائه‌دهنده`), Arabic/Indic shaping and emoji sequences no longer lose them; the response de-obfuscation now removes joiners only between ASCII word characters, where the request side inserts them ([#12186](https://github.com/diegosouzapw/OmniRoute/issues/12186)) — thanks @rezjalibd ([#12359](https://github.com/diegosouzapw/OmniRoute/pull/12359)) — thanks @pacocartones +- **fix(resilience):** count resolved upstream 5xx results against the provider circuit breaker on the chat path — `CircuitBreaker.execute()` no longer reads a resolved `{ success: false, status: 5xx }` as a success that cancels the call-site failure, so a provider answering 503s now trips its breaker instead of staying `CLOSED` at `failureCount: 1`; single-model and combo dispatches are each accounted exactly once ([#12254](https://github.com/diegosouzapw/OmniRoute/issues/12254)) ([#12360](https://github.com/diegosouzapw/OmniRoute/pull/12360)) — thanks @pacocartones +- **fix(providers):** resolve the Codex quota auto-ping model from the live provider catalog and lifecycle registry instead of the retired `gpt-5.1-codex-mini`, and pause the ping with one actionable warning when no selectable Codex model exists rather than retrying a shut-down id every cooldown window ([#11905](https://github.com/diegosouzapw/OmniRoute/issues/11905)) ([#12361](https://github.com/diegosouzapw/OmniRoute/pull/12361)) — thanks @pacocartones +- **fix(api):** keep the `{created, data}` wrapper on combo-routed `/v1/images/generations` responses and default Codex image results to `b64_json` on both `/v1/images/generations` and `/v1/images/edits` so Codex CLI's built-in `image_gen` can decode them ([#12268](https://github.com/diegosouzapw/OmniRoute/issues/12268)) ([#12362](https://github.com/diegosouzapw/OmniRoute/pull/12362)) — thanks @pacocartones +- **fix(sse):** Name the shadowed custom provider node when a built-in provider id/alias (e.g. `openference` → `of`) reserves the prefix of an existing OpenAI/Anthropic-compatible node, so the runtime `No active credentials for provider: ` error explains that the prefix routed to the built-in and never reached the node's healthy connections, instead of contradicting the dashboard ([#11943](https://github.com/diegosouzapw/OmniRoute/issues/11943)) — thanks @morpheus9393 ([#12365](https://github.com/diegosouzapw/OmniRoute/pull/12365)) — thanks @pacocartones +- **fix(i18n):** wrap `ccOnboardingKeyPlaceholder` in ICU single quotes across all 43 locales so angle brackets render literally instead of being parsed as rich-text tags, which crashed the Claude Code onboarding block with `INVALID_MESSAGE: INVALID_TAG` ([#12302](https://github.com/diegosouzapw/OmniRoute/issues/12302)) ([#12369](https://github.com/diegosouzapw/OmniRoute/pull/12369)) — thanks @gonisulaimann +- **fix(models):** publish `effort_tiers` on Kimi K3's synced base-model entries (`kmca/k3`, `kmca/k3-256k`) so catalog-only clients (OpenCode, plain SDK pickers) can see and select the reasoning tiers (`low`/`high`/`max`) the synced metadata already carried — the `isSkippedEffortProvider` gate no longer suppresses tier visibility on those base entries, while synthetic `-` variant generation stays prevented and Codex/GLM base models remain excluded unchanged ([#12299](https://github.com/diegosouzapw/OmniRoute/issues/12299)) ([#12371](https://github.com/diegosouzapw/OmniRoute/pull/12371)) — thanks @gonisulaimann +- **fix(guardrails):** keep `auto`/`auto/*` virtual combos exempt from the Vision Bridge `fixedModel` credential guard so a combo target is passed through instead of silently falling back to global auto-selection ([#12237](https://github.com/diegosouzapw/OmniRoute/issues/12237)) ([#12373](https://github.com/diegosouzapw/OmniRoute/pull/12373)) — thanks @pacocartones +- **fix(combo):** capability-filter exhaustion caused by `max_tokens` above every target's known output limit now reports that reason (requested `max_tokens` vs the pool's highest known ceiling) instead of the unrelated "supports structured output" message ([#12229](https://github.com/diegosouzapw/OmniRoute/issues/12229)) — thanks @DW-MediaLab ([#12374](https://github.com/diegosouzapw/OmniRoute/pull/12374)) — thanks @pacocartones +- **fix(auth):** the `least-used` account strategy now prefers accounts without backoff before falling back to oldest `lastUsedAt`, the same tie-break `round-robin` already applies, so a failover no longer lands on a just-rate-limited account for a single request ([#12279](https://github.com/diegosouzapw/OmniRoute/issues/12279)) — thanks @tenshiak ([#12375](https://github.com/diegosouzapw/OmniRoute/pull/12375)) — thanks @pacocartones +- **fix(docker):** the `chatgpt-web-codex-browser` image now finds the Chrome binary under `chrome-linux64/` (Chrome for Testing layout in `playwright:v1.62.0-noble`) as well as the legacy `chrome-linux/`, so the container no longer crash-loops with `exec: --headless=new: not found` ([#12024](https://github.com/diegosouzapw/OmniRoute/issues/12024)) ([#12376](https://github.com/diegosouzapw/OmniRoute/pull/12376)) — thanks @pacocartones +- **fix(providers):** declare `groq/compound` and `allam-2-7b` as non-reasoning models in the curated Groq registry so `reasoning_effort` / `output_config.effort` / `thinking` from Claude Code are stripped instead of forwarded, which Groq rejected with HTTP 400 ([#12134](https://github.com/diegosouzapw/OmniRoute/issues/12134)) ([#12379](https://github.com/diegosouzapw/OmniRoute/pull/12379)) — thanks @pacocartones +- **fix(executors):** `OpencodeExecutor` no longer forces a direct connection when the connection has a proxy assigned in Proxy Management but no per-account proxies: the single-account fast path used to wrap the upstream dispatch in the direct-egress sentinel, discarding the ambient proxy context the chat handler had pinned from `proxy_assignments`, so API-key `opencode`/`opencode-go` connections egressed from the host IP (and hit geoblocks) despite the assignment. The direct pin is now applied only when no ambient proxy context exists ([#11894](https://github.com/diegosouzapw/OmniRoute/issues/11894) — thanks @hizzt) ([#12380](https://github.com/diegosouzapw/OmniRoute/pull/12380)) — thanks @pacocartones +- **fix(api):** `GET /v1/models` with `MODELS_CATALOG_PREFIX_MODE=canonical` (or `?prefix=canonical`) now lists providers whose registry alias is undefined or equal to their own id (Antigravity, Antigravity CLI and other self-aliased built-ins) — their single `provider/model` id was dropped by the alias/canonical duplicate guard in the static, synced, custom and alias-backed catalog loops ([#12058](https://github.com/diegosouzapw/OmniRoute/issues/12058)) — thanks @cheynetom ([#12381](https://github.com/diegosouzapw/OmniRoute/pull/12381)) — thanks @pacocartones +- **fix(translator):** Drop replayed `thinking` blocks that carry no signature (the shape produced from cross-provider `reasoning_content`) instead of stamping the default Claude signature on them, which Anthropic rejected with `400 Invalid signature in thinking block` on the next turn served by an Anthropic rung ([#12105](https://github.com/diegosouzapw/OmniRoute/issues/12105)) — thanks @atescivitci-cmd ([#12386](https://github.com/diegosouzapw/OmniRoute/pull/12386)) — thanks @pacocartones +- **fix(cli):** Resolve Bun's `--preload` polyfill path against the package root instead of `dist/`, so `omniroute` installed with `bun install -g` no longer crashes at startup with `error: preload not found …/dist/open-sse/utils/setupPolyfill.ts` ([#11980](https://github.com/diegosouzapw/OmniRoute/issues/11980)) — thanks @joglomedia ([#12387](https://github.com/diegosouzapw/OmniRoute/pull/12387)) — thanks @pacocartones +- **fix(providers):** `gemini-business` now publishes its model catalog — `/v1/models` and `/v1/providers/gemini-business/models` list the 12 enterprise Gemini ids the executor understands instead of returning an empty list (#12107) ([#12389](https://github.com/diegosouzapw/OmniRoute/pull/12389)) — thanks @pacocartones +- **fix(db):** install `busy_timeout` before the SQLite connection's first statement so a process opening the database while another one closes its WAL connection waits out the transient EXCLUSIVE lock instead of dying with `database is locked`, and recognise the drivers' real BUSY/PROTOCOL/IOERR errors as transient in the corruption probe so the same lock no longer renames the database away as corrupt; deflakes `cross-process contenders never both acquire the same connection` (#12394 — thanks @pacocartones) +- **fix(chat-admission):** derive the `chat_admission_busy` 503 `Retry-After` from observed heavyweight-lease occupancy — the larger of the exhausted `OMNIROUTE_CHAT_ADMISSION_QUEUE_MS` window and the time since capacity last turned over, capped at 60 s — instead of a fixed 1 s (structural) / 2 s (byte-stage) hint that invited Codex/agent fan-out clients to re-send ~1 MiB `/v1/responses` bodies every second into a gate held for the whole SSE lifetime; an idle gate keeps the historical floors ([#12135](https://github.com/diegosouzapw/OmniRoute/issues/12135)) (#12395 — thanks @pacocartones) +- **fix(api-manager):** the API key permissions modal no longer silently drops `allowedCombos` entries its Combo picker cannot render — routing-rule names such as `rt-*`, which the backend already honours — when "All" is clicked and the key is switched back to "Restrict"; those entries now survive the toggle, are listed read-only under the combo list so the count and the list agree, and are saved back verbatim instead of persisting `[]` (deny-all) (#12397 — thanks @pacocartones) +- **fix(catalog):** write the NUL separator of the catalog connection memo key, the provider serviceKind memo key, the Video Bridge promotion group key and a JSON-exactness test fixture as the `\u0000` escape instead of a raw byte — same runtime value, but the raw byte made git, GitHub and ripgrep treat those files as binary (hidden PR diffs, silently skipped searches); a guard test now keeps raw NUL bytes out of `src/`, `open-sse/` and `tests/` (#12403 — thanks @pacocartones) +- **fix(providers):** add `CLAUDE_CODE_CLIENT_VERSION` and `GITHUB_COPILOT_CLI_VERSION` env overrides so Anthropic/Copilot client-version gates can be unblocked without a rebuild ([#12417](https://github.com/diegosouzapw/OmniRoute/issues/12417)) ([#12632](https://github.com/diegosouzapw/OmniRoute/pull/12632)) — thanks @HouMinXi +- **fix(db):** back-fill `last_ping_at` and `last_pinged_reset_key` on `provider_connections` during schema reconciliation so divergent lineages that skipped `123_quota_auto_ping` still accept quota auto-ping writes ([#12470](https://github.com/diegosouzapw/OmniRoute/pull/12470) — thanks @KooshaPari) +- **fix(ci):** document MIT exceptions for `@eloqnt/{config,format-json,format-po}` (next-intl transitive; locked tarballs omit `license`) and keep the A2A lifecycle vitest off the real SQLite persistence seam ([#12581](https://github.com/diegosouzapw/OmniRoute/issues/12581)) ([#12595](https://github.com/diegosouzapw/OmniRoute/pull/12595)) +- OpenCode plugin `/v1/models` catalog fetch now waits 30s by default and attaches HTTP `statusCode` on 401/5xx so host fallback plugins can hop instead of seeing an untyped AbortError/UnknownError. ([#12607](https://github.com/diegosouzapw/OmniRoute/pull/12607)) — thanks @RaviTharuma +- **CI:** the OpenAPI security-tier gate now mirrors `isAlwaysProtectedPath()` in full — it also reads `ALWAYS_PROTECTED_API_PATTERNS`, so the pattern-gated credential routes (`/api/providers/{id}/{claude,codex}-auth/{export,apply-local}`, GHSA-5926-2w35-7h4q) no longer report as unannotated. (#12605) +- **fix(api):** GET /v1/models no longer waits forever on a hung coalesced catalog rebuild; cold-path waits are bounded (`CATALOG_BUILD_TIMEOUT_MS`, default 8s) and a last-good 200 is served when the rebuild times out ([#12627](https://github.com/diegosouzapw/OmniRoute/issues/12627)). ([#12628](https://github.com/diegosouzapw/OmniRoute/pull/12628)) — thanks @RaviTharuma +- **fix(cursor):** a non-array `tool_calls` on an assistant message no longer crashes the cursor request translator with a `TypeError`; both loops now require an array ([#12689](https://github.com/diegosouzapw/OmniRoute/issues/12689)) ([#12691](https://github.com/diegosouzapw/OmniRoute/pull/12691)) — thanks @soroush5 +- **fix(chat):** Chat Completions no longer return empty `content` after a server-owned memory or skills tool; the first provider send and account/model recovery now share one pipeline so a follow-up round-trip can fill the reply ([#12696](https://github.com/diegosouzapw/OmniRoute/issues/12696)) — thanks @HouMinXi ([#12867](https://github.com/diegosouzapw/OmniRoute/pull/12867)) +- **fix(dashboard):** batch-deleting provider keys no longer toasts failure after a successful delete when the confirm button's click event is forwarded as `onAfter` ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) — thanks @HouMinXi +- **fix(glm):** drop the extra 16th argument to `createSSETransformStreamWithLogger` that TypeScript rejected (TS2554) and that never reached the TransformStream ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) ([#12770](https://github.com/diegosouzapw/OmniRoute/pull/12770)) — thanks @HouMinXi +- **fix(dashboard):** pass `nodeMap` into Runtime `QuotaGroup` so a quota monitor in error/exhausted/alerting no longer throws `ReferenceError: nodeMap is not defined`. ([#12868](https://github.com/diegosouzapw/OmniRoute/pull/12868)) — thanks @HouMinXi +- Restricted API keys whose `allowedModels` lists a combo name no longer skip every combo member at pre-dispatch (`ALL_TARGETS_SKIPPED`). Inner-target filtering still applies when the allow-list is a provider prefix or `disableNonPublicModels` is on ([#12899](https://github.com/diegosouzapw/OmniRoute/pull/12899)). — thanks @HouMinXi +- **fix(combos):** Effort-suffixed combo members inherit the base model's `model_context_overrides` row so priority order is not inverted on large requests ([#12926](https://github.com/diegosouzapw/OmniRoute/pull/12926)) — thanks @HouMinXi +- **fix(catalog):** Picker-added `customModels` enter the dispatch-time live catalog so combo members and bare inference no longer 400 ([#12934](https://github.com/diegosouzapw/OmniRoute/pull/12934)) — thanks @HouMinXi +- **fix(docker):** re-derive the Docker build's worker-pool memory budget from the MEASURED ~4.5 GB per-process RSS (the issue owner's own VPS dmesg OOM-killer reproduction) instead of the stale 2560 MB/worker inference #11419 shipped, and lower `OMNIROUTE_BUILD_WORKERS` 3 → 2 so 1 parent + 1 page-data worker (2 processes × 4.5 GB = 9 GB) fits the 12.288 GB (75%) budget on a 16 GB GitHub Actions runner — the previous default (1 parent + 2 workers = 13.5 GB) still overcommitted the runner and kept "Publish to Docker Hub" failing with `cannot allocate memory` after #11419 merged (#7518). ([#11663](https://github.com/diegosouzapw/OmniRoute/pull/11663)) +- **fix(electron):** the packaged Windows desktop build now passes the #7592 cold-restart smoke end to end. Five defects were found and fixed on the way: (1) optional-pack staging failed on any machine whose `tar` is GNU tar — it reads the drive letter in an absolute `-f C:\...` archive path as a remote rsh host (`Cannot connect to C:`), so staging now passes a bare filename with `cwd` at the tarball directory and surfaces tar stderr on failure; (2) the Electron `files` allowlist was missing `lib/loginHeaderCapture.js`, so the packaged main process crashed at startup with `Cannot find module './lib/loginHeaderCapture'`; (3) electron-builder ≥26 injects an `!**/node_modules/**` ignore into every extraResources pattern list that no positive filter can override, which silently dropped the staged runtime `node_modules` (including the better-sqlite3 N-API prebuild) from `resources/app` — a fresh v3.8.50 desktop build would have shipped with no native SQLite driver and reproduced the #7592 sql.js fallback on every machine; an `afterPack` hook now restores the staged `node_modules` after packing; (4) the packaged-app smoke harness redirected `USERPROFILE` into its temp DATA_DIR, but Electron resolves the Roaming profile from `%USERPROFILE%\AppData\Roaming\` (USERPROFILE takes precedence over the APPDATA env var) and the path service throws instead of creating it, so `requestSingleInstanceLock()` returned false and the app exited(0) silently before `app.whenReady()` — the harness now pre-creates the derived tree, and `ensureSmokeEnvDirs` is exported and unit-tested; (5) the #7592 driver assertion parsed a `[DB] Driver: ...` line that the server's primary DB path never printed (only the unused `openDatabaseAsync()` did) — `getDbInstance()` now emits the same line on open so the guard can actually assert the native driver. Also: the smoke env-allowlist unit test hardcoded POSIX paths and could only pass on Linux/macOS; expectations are now host-agnostic, with new regression tests for the USERPROFILE-derived tree and `tarPack` under absolute Windows-style paths ([#11443](https://github.com/diegosouzapw/OmniRoute/pull/11443)) — thanks @jonlwheat2-gif +- fix(sse): stop the auto-combo candidates inspector from silently dropping model-locked/cooled-down rows (#9133) ([#11994](https://github.com/diegosouzapw/OmniRoute/pull/11994)) — thanks @hartmark +- **fix(providers):** Perplexity Web no longer turns upstream stream failures into successful assistant text; pre-content failures remain eligible for fallback, partial output ends with a structured sanitized error, and failed sessions are not persisted ([#12465](https://github.com/diegosouzapw/OmniRoute/pull/12465)) +- **fix(video-bridge):** preserve chronological video and transcript fusion, source-frame timestamps, transcript provenance, full contact-sheet intervals, and pre-abort callback isolation ([#11681](https://github.com/diegosouzapw/OmniRoute/pull/11681)) +- **Z.ai Web:** HTTP 200 streams carrying an upstream error now terminate with a structured failure instead of assistant text plus a normal stop, preserving partial output while allowing pre-content combo fallback. ([#12454](https://github.com/diegosouzapw/OmniRoute/pull/12454)) +- Absorb `Error [AbortError]: request_signal_aborted` and DOMException AbortError shapes in the process-level client-abort crash guard so routine client disconnects no longer kill the server (exit code 7). ([#12165](https://github.com/diegosouzapw/OmniRoute/pull/12165)) — thanks @alvinveroy +- **fix(sse):** Treat Adapta Web `type:error` SSE events as sanitized non-stream failures instead of empty HTTP 200 completions. ([#12459](https://github.com/diegosouzapw/OmniRoute/pull/12459)) +- **fix(autoCombo):** layer 3 (`models_dev_tier`) no longer scores retired models as premium, merge cross-provider capability rows deterministically, and inherit tier scores through variant ids ([#11508](https://github.com/diegosouzapw/OmniRoute/issues/11508)). `getModelsDevTierFitness` now vetoes ids present in `config/quality/model-lifecycle.json` with status `retired` before any other signal; `loadModelCapabilities()` aggregates per `model_id` (any-non-null-true for booleans, max for `limit_context`) instead of last-write-wins over SQLite's undefined row order; a capabilities miss retries through `resolveScoresAs()`'s base id and reports `models_dev_tier:inherited`, matching layers 1–2 (#11492). Adds `getModelsDevTierFitnessWithSource()` and an `invalidateCapabilitiesCache()` test/ops hook. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) — thanks @Meet6338-X +- fix(sse): accept Claude `content:[]` + `stop_reason: max_tokens`/`tool_use` in `detectMalformedNonStream` (match `isEmptyContentResponse`) so Claude Code `/model` probes with `max_tokens: 1` no longer become a false 502 ([#11568](https://github.com/diegosouzapw/OmniRoute/pull/11568)) — thanks @Seramicx +- **fix(security):** sanitize `request.failed` diagnostics before publishing them to live dashboard listeners and replay history, while keeping status, model, provider, latency, and internal call-log diagnostics intact. ([#12469](https://github.com/diegosouzapw/OmniRoute/pull/12469)) +- **fix(pwa):** do not intercept dashboard navigations so Chrome can fall back from a stale HTTP/3 Alt-Svc advertisement (UDP :20128 is unpublished; F5 on a long-lived tab hung until a new tab opened a fresh TCP connection). ([#12767](https://github.com/diegosouzapw/OmniRoute/pull/12767)) — thanks @HouMinXi +- **fix(executors):** handle DuckDuckGo ERR_BN_LIMIT (418) without retrying — when the upstream returns `418 ERR_BN_LIMIT` (rate-limit/ban), the executor now returns the error immediately instead of burning another VQD acquisition that would only count against the IP limit. The retry logic for `418 ERR_CHALLENGE` (unsolved challenge) remains unchanged. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) ([#12110](https://github.com/diegosouzapw/OmniRoute/pull/12110)) — thanks @oyi77 / @Meet6338-X +- Fixed DuckDuckGo streaming responses losing JSON lines and UTF-8 characters split across network chunks. ([#11528](https://github.com/diegosouzapw/OmniRoute/pull/11528)) — thanks @pacocartones +- Harden generated Cloudflare, Vercel, and Deno relays by stripping hop-by-hop, framing, and proxy authentication headers before forwarding requests upstream. ([#11533](https://github.com/diegosouzapw/OmniRoute/pull/11533)) — thanks @pacocartones +- **feat(grok-cli):** Provider Limits shows grok-cli banked reset credits from `GetRemainingResets` (including a real zero; a failed RPC omits the row) and the existing View credits button now calls `ConsumerUiSvc/RedeemReset` for grok-cli. Live tokens use nested fields 10/20/30 (id + Timestamp), not compact 1/2/3. ([#12805](https://github.com/diegosouzapw/OmniRoute/pull/12805)) — thanks @HouMinXi +- **fix(memory):** Embedding Model Quick select, Embedding Source remote dropdown, and Rerank selector now list every configured provider with embedding/rerank support instead of only chat-catalog text matches plus OpenRouter live discovery; a generic OpenAI-compatible `/embeddings` + Cohere-compatible `/rerank` runtime fallback resolves any configured chat provider's embedding/rerank endpoint, so unlisted providers no longer fail with "Unknown embedding provider"; both memory selectors gained a free-text model override ([#11390](https://github.com/diegosouzapw/OmniRoute/pull/11390)) — thanks @rqzbeh +- Harden SQLite upgrades around the historical migration-074 version collision: missing discovery and inspector tables are replayed atomically, pre-existing databases (including setup-created skeletons) receive reusable content-addressed safety snapshots, and Node test/eval probes without `DATA_DIR` are isolated from the operator database. ([#12435](https://github.com/diegosouzapw/OmniRoute/pull/12435)) +- **fix(dashboard):** Moonshot/Kimi Open Platform voucher and cash leftover percentages follow the bucket balance, so an empty wallet no longer paints those rows as 100% while Available is 0% ([#12733](https://github.com/diegosouzapw/OmniRoute/pull/12733)) — thanks @HouMinXi +- **fix(combo):** return non-retryable HTTP 400 when all candidates for a pinned native Codex turn are unavailable due to model-scoped lockout, terminating the turn cleanly while preserving turn continuity and enabling standard Combo routing on subsequent turns ([#12240](https://github.com/diegosouzapw/OmniRoute/pull/12240)) — thanks @mdigitalbh81 +- Fixed auto-router strategies preserving the selected connection through ranking and dispatch when multiple connections share the same provider and model. ([#11530](https://github.com/diegosouzapw/OmniRoute/pull/11530)) — thanks @pacocartones +- Preserve Chat Completions JSON-object response formats when translating requests to the Responses API. ([#11529](https://github.com/diegosouzapw/OmniRoute/pull/11529)) — thanks @pacocartones +- Fix idempotency fingerprints so Responses API requests include `input` and other semantic generation fields using stable serialization, while excluding credentials and request metadata. ([#11532](https://github.com/diegosouzapw/OmniRoute/pull/11532)) — thanks @pacocartones +- **fix(api):** Generated API CLI commands now enforce required OpenAPI request bodies; Combo test commands forward the required `comboName` body, while API keys created by older writers after migration 149 preserve legacy allow-all Combo access without widening explicit empty allowlists — thanks @marcelokarval ([#12070](https://github.com/diegosouzapw/OmniRoute/pull/12070)) +- **fix(grok-web):** treat upstream streaming failures as failures instead of successful assistant text: error-only streams now fail readiness with HTTP 502, while failures after legitimate content preserve that partial output and terminate through the sanitized stream failure path without a normal `stop` completion. ([#12458](https://github.com/diegosouzapw/OmniRoute/pull/12458)) +- HuggingChat now turns HTTP 200 JSONL generation failures into a sanitized 502 before content, or a fixed public stream failure after partial output, so fallback and request persistence no longer record a false successful stop. ([#12456](https://github.com/diegosouzapw/OmniRoute/pull/12456)) +- **fix(providers):** keep 1min.ai HTTP 200 stream errors out of assistant content, preserve partial output, and expose sanitized terminal errors so pre-content failures can fall back. ([#12466](https://github.com/diegosouzapw/OmniRoute/pull/12466)) +- **fix(sessions):** preserve legacy Sessions data when the additive exclusive-session projection is unavailable, returning an empty projection and warning only once per contiguous outage instead of failing the endpoint; the Sessions badge intentionally reflects the merged legacy and exclusive row count introduced by [#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389) ([#11469](https://github.com/diegosouzapw/OmniRoute/pull/11469)) — thanks @KaspaPulse +- Render Nimble Search with the generic provider icon and serve Opper's proven logo locally. ([#12415](https://github.com/diegosouzapw/OmniRoute/pull/12415)) +- **fix(quality):** `config/quality/provider-assets-provenance.jsonl` was stale against the physical `public/providers/` tree (still listing 225 assets and 5 duplicate-content groups after several provider-retirement PRs removed 85 icon files), which would have made `check:provider-asset-provenance` fail on the next full run. Reconciled the manifest to the real 142-file snapshot (142 asset records, 1 duplicate-content group, 2 newly-registered files marked `unresolved` pending provenance research), repinned `auditedCommit`, and updated the regression test's hardcoded counts to match. ([#11876](https://github.com/diegosouzapw/OmniRoute/pull/11876)) +- fix(ui): wrap quota card footer action buttons to prevent clipping Refresh button on narrow cards — when extra action buttons such as `View credits` are present on Codex cards, allow the footer row to flex-wrap with `shrink-0` buttons so `Refresh now` remains visible and clickable across all responsive card widths ([#11464](https://github.com/diegosouzapw/OmniRoute/pull/11464)) — thanks @adevwithpurpose +- Keep Antigravity Gemini usable when the same connection's Claude weekly quota is empty; generic quota cache stays per-connection for every other provider. ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) — thanks @HouMinXi +- Fixed Tailscale tunnel setup to reject ports outside the valid 1-65535 range and safely install Windows MSI packages from paths containing apostrophes. ([#11534](https://github.com/diegosouzapw/OmniRoute/pull/11534)) — thanks @pacocartones +- Classify Cloudflared and Tailscale process-management routes as local-only and spawn-capable while preserving remote access to read-only tunnel status endpoints. ([#11531](https://github.com/diegosouzapw/OmniRoute/pull/11531)) — thanks @pacocartones +- Electron release workflow: the `publish-npm` job now grants `actions: read` to the reusable `npm-publish.yml` it calls (its `publish` job requests it), which is what made GitHub refuse the whole v3.8.50 run at startup and ship the release with zero desktop assets; a `workflow_dispatch` now builds the requested tag instead of the dispatching branch and can skip the npm leg (`publish_npm=false`) when only re-attaching assets ([#11974](https://github.com/diegosouzapw/OmniRoute/pull/11974)) +- **fix(dashboard):** The Combos page usage guide now reads its dismissal through `useSyncExternalStore` instead of correcting SSR state inside an effect, removing an extra commit of the page tree on every load (and the `react-hooks/set-state-in-effect` error it raised). ([#12671](https://github.com/diegosouzapw/OmniRoute/pull/12671)) +- Electron release: `electron/package-lock.json` regained the optional `electron-builder-squirrel-windows` subtree (13 entries) that `npm ci` had been refusing as out of sync, `electron-release.yml` gained a `build_ref` dispatch input and stops regenerating release notes on a re-attach dispatch, and the npm publish workflow attaches the SBOM to the GitHub Release on dispatch publishes too — so the v3.8.51 tag ships every desktop asset and the SBOM like v3.8.49 did ([#12022](https://github.com/diegosouzapw/OmniRoute/pull/12022)) +- Drained the reds every PR against `release/v3.8.51` was born with: documented the three service-worker build-id variables, registered the six retirement/tunnel tests with the mutation gate, approved `eslint-plugin-react-hooks` in the dependency allowlist, added the six `combo.sort.*` strings to `vi` and `pt-BR`, pointed the ChatGPT Web doc at the real migration-168 test, worded the g4f hint around the member key, and realigned four tests to the retired-provider catalog and the legacy-schema fixtures the retirement migrations touch. ([#11940](https://github.com/diegosouzapw/OmniRoute/pull/11940)) +- **fix(providers):** Claude, Grok, LMArena, Notion, and Perplexity web-cookie transports now use pooled `wreq-js` 3.2 instead of the native sidecar, with all nine supported bindings pinned and audited, and the applicable platform binding plus native-license evidence included in each release artifact ([#12429](https://github.com/diegosouzapw/OmniRoute/pull/12429), supersedes [#11753](https://github.com/diegosouzapw/OmniRoute/pull/11753)). +- **fix(providers):** Zed Hosted streaming failures now trigger fallback before content and end partial streams with a sanitized structured error instead of fake assistant text and a normal-success stop. ([#12455](https://github.com/diegosouzapw/OmniRoute/pull/12455)) +- **fix(test):** capture hook result after render (direct commit `b85d0abb0b`) +- **fix(sync):** repair the two regressions the v3.8.50 sync-back left on release/v3.8.51 (direct commit `5ade9e0851`) +- **revert(models):** drop the memo reset hook ported from #10055 (direct commit `282de841ae`) +- **fix(typecheck):** guard the optional connectionId before the reactive model sync (direct commit `f2825bad75`) +- **fix(quota-share):** release the winner's reserved in-flight slot (#11371) ([#11408](https://github.com/diegosouzapw/OmniRoute/pull/11408)) — thanks @oyi77 +- **fix(providers):** stop silently dropping reasoning effort on opencode families (#10788) ([#11409](https://github.com/diegosouzapw/OmniRoute/pull/11409)) — thanks @oyi77 +- **fix(combo):** enable genuine quota-aware routing for generic providers (antigravity, claude, etc.) ([#11411](https://github.com/diegosouzapw/OmniRoute/pull/11411)) — thanks @HouMinXi +- **fix(oauth):** allow expired token retry before deactivation and activate expired retry loop (#P0) ([#11414](https://github.com/diegosouzapw/OmniRoute/pull/11414)) — thanks @HouMinXi +- **perf(executors):** lazy-load the executor registry — defer class imports + construction to first use (#11220) ([#11421](https://github.com/diegosouzapw/OmniRoute/pull/11421)) — thanks @oyi77 +- **fix:** make release/v3.8.50 CI gates green (run 32786966560 root causes) ([#11450](https://github.com/diegosouzapw/OmniRoute/pull/11450)) — thanks @jonlwheat2-gif +- **fix(sse):** exact-domain cookie match and origin-equality URL assertions ([#11452](https://github.com/diegosouzapw/OmniRoute/pull/11452)) — thanks @hartmark +- **fix(providers):** send Responses API payload during OpenAI-compatible validation ([#11454](https://github.com/diegosouzapw/OmniRoute/pull/11454)) — thanks @tuandinh0801 +- **fix(command-code):** fallback to /alpha/generate for Go plan without Provider API access ([#11455](https://github.com/diegosouzapw/OmniRoute/pull/11455)) — thanks @Chewji9875 +- **fix(responses-continuation):** fail closed on a log-truncated stored input/output array ([#11473](https://github.com/diegosouzapw/OmniRoute/pull/11473)) — thanks @hartmark +- **fix(cli):** write jcode settings as [providers.omniroute] in config.toml ([#11484](https://github.com/diegosouzapw/OmniRoute/pull/11484)) — thanks @hongnoul +- **fix(logging):** raise chat-log array truncation cap to 1000 and unify duplicate implementations ([#11499](https://github.com/diegosouzapw/OmniRoute/pull/11499)) — thanks @hartmark +- **fix:** restore live websocket URL helpers ([#11509](https://github.com/diegosouzapw/OmniRoute/pull/11509)) — thanks @PauloFH +- **perf(core):** eliminate async waterfalls across home, cache, and db operations (#11396) ([#11512](https://github.com/diegosouzapw/OmniRoute/pull/11512)) — thanks @HouMinXi +- **fix(kiro):** add runtime.us-east-1.kiro.dev as first-attempt endpoint ([#11517](https://github.com/diegosouzapw/OmniRoute/pull/11517)) — thanks @rifqiawl +- **fix(providers):** remove duplicate 128-tool truncation in OpencodeExecutor (#11444) ([#11518](https://github.com/diegosouzapw/OmniRoute/pull/11518)) — thanks @HouMinXi +- **fix(providers):** poll onboardUser LRO before classifying BYOP ([#11519](https://github.com/diegosouzapw/OmniRoute/pull/11519)) — thanks @rifqiawl +- **fix(translator):** preserve exact tool name casing for Claude tool_use payloads (#11487) ([#11520](https://github.com/diegosouzapw/OmniRoute/pull/11520)) — thanks @HouMinXi +- **fix(kimi-web):** align health probe with executor domain ([#11521](https://github.com/diegosouzapw/OmniRoute/pull/11521)) — thanks @pacocartones +- **fix(cli):** preserve existing admin password during setup ([#11522](https://github.com/diegosouzapw/OmniRoute/pull/11522)) — thanks @pacocartones +- **fix(mitm):** keep Windows DNS elevation off the POSIX sudo path (#11430) ([#11542](https://github.com/diegosouzapw/OmniRoute/pull/11542)) — thanks @official-burak +- **fix(sse):** scale chat admission by ingest byte budget instead of a fixed request count ([#11548](https://github.com/diegosouzapw/OmniRoute/pull/11548)) — thanks @TheDemonTuan +- **fix:** accept Gemini Web cookie exports ([#11554](https://github.com/diegosouzapw/OmniRoute/pull/11554)) — thanks @rafacpti23 +- **fix(i18n):** complete Brazilian Portuguese translations ([#11558](https://github.com/diegosouzapw/OmniRoute/pull/11558)) — thanks @rafacpti23 +- **fix(sse):** explicit types for openai-responses pureHelpers — clears last failing core typecheck gate ([#11567](https://github.com/diegosouzapw/OmniRoute/pull/11567)) — thanks @jonlwheat2-gif +- **fix(opencode):** hydrate Proxy Pool references for no-auth connections ([#11584](https://github.com/diegosouzapw/OmniRoute/pull/11584)) — thanks @AStupidBear +- **fix(ci):** resolve inherited release/v3.8.51 base-red CI failures ([#11608](https://github.com/diegosouzapw/OmniRoute/pull/11608)) — thanks @jonlwheat2-gif +- **fix(db):** fallback load STORAGE_ENCRYPTION_KEY from env files ([#11614](https://github.com/diegosouzapw/OmniRoute/pull/11614)) — thanks @benzntech +- **fix(providers):** mark Brave Search as no longer having a free tier ([#11621](https://github.com/diegosouzapw/OmniRoute/pull/11621)) — thanks @turbolego +- **fix:** pass tool result content through as string in OpenAI/Claude -> Gemini translation ([#11624](https://github.com/diegosouzapw/OmniRoute/pull/11624)) — thanks @Hsia97 +- **fix(sse):** scope credential health to exact key ([#11628](https://github.com/diegosouzapw/OmniRoute/pull/11628)) — thanks @KaspaPulse +- **fix(install):** guard prepare script against missing husky ([#11634](https://github.com/diegosouzapw/OmniRoute/pull/11634)) — thanks @Prajeeth-12 +- **fix(security):** redact NVIDIA_API_KEY at the log sink in the ad-hoc diag script ([#11639](https://github.com/diegosouzapw/OmniRoute/pull/11639)) +- **fix(types):** clear the two type regressions red on release/v3.8.51 ([#11640](https://github.com/diegosouzapw/OmniRoute/pull/11640)) — thanks @jonlwheat2-gif +- **fix(build):** resolve workspace: protocol dependencies to concrete semver versions during prepublish (#11540) ([#11641](https://github.com/diegosouzapw/OmniRoute/pull/11641)) — thanks @HouMinXi +- **fix(search):** properly resolve configured search connection in /v1/responses pipeline (#11524) ([#11642](https://github.com/diegosouzapw/OmniRoute/pull/11642)) — thanks @HouMinXi +- **fix(authz):** trust cf-connecting-ip for via-proxy detection when peer is Cloudflare edge (#11514) ([#11643](https://github.com/diegosouzapw/OmniRoute/pull/11643)) — thanks @HouMinXi +- **fix(security):** compare the OIDC state cookie in constant time ([#11646](https://github.com/diegosouzapw/OmniRoute/pull/11646)) +- **fix(security):** namespace the dedup hash by the calling API key ([#11649](https://github.com/diegosouzapw/OmniRoute/pull/11649)) +- **fix(models):** gate the Codex-native catalog loop on prefix mode (#11632) ([#11662](https://github.com/diegosouzapw/OmniRoute/pull/11662)) — thanks @xiaoyaner0201 +- **fix(dashboard):** project Codex routing quota in account pools ([#11666](https://github.com/diegosouzapw/OmniRoute/pull/11666)) — thanks @KaspaPulse +- **fix(ddgw):** normaliza system no upstream da release v3.8.51 ([#11692](https://github.com/diegosouzapw/OmniRoute/pull/11692)) — thanks @RhianB14 +- **fix(memory):** honor category filter in GET /api/memory ([#11699](https://github.com/diegosouzapw/OmniRoute/pull/11699)) — thanks @Sabeekhann +- **fix(ci):** detect stale workspace lockfile entries ([#11714](https://github.com/diegosouzapw/OmniRoute/pull/11714)) — thanks @pacocartones +- **fix(ci):** stamp dist/BUILD_SHA before the npm publish provenance gate ([#11721](https://github.com/diegosouzapw/OmniRoute/pull/11721)) +- **fix(docker):** decouple the best-effort Bun image from the release manifest ([#11724](https://github.com/diegosouzapw/OmniRoute/pull/11724)) +- **perf(compression):** avoid cloning memo misses twice ([#11727](https://github.com/diegosouzapw/OmniRoute/pull/11727)) — thanks @RaviTharuma +- **fix(docker):** let the bun digest artifact be absent, not fatal ([#11740](https://github.com/diegosouzapw/OmniRoute/pull/11740)) +- **fix(shared):** block cloud-metadata hosts under default remote-image guard ([#11755](https://github.com/diegosouzapw/OmniRoute/pull/11755)) — thanks @santosraju99-hub +- **fix(api):** preserve caller-provided X-Correlation-Id on chat completions ([#11760](https://github.com/diegosouzapw/OmniRoute/pull/11760)) — thanks @aniruddhaadak80 +- **fix(resilience):** lock permanently retired models instead of short backoff (Gemini ban prevention) ([#11762](https://github.com/diegosouzapw/OmniRoute/pull/11762)) — thanks @turbolego +- **fix:** mark Vercel AI Gateway as passthroughModels ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **fix(resilience):** stop hammering permanently-moved endpoints and billing-suspended accounts ([#11774](https://github.com/diegosouzapw/OmniRoute/pull/11774)) — thanks @turbolego +- **fix(lease):** remove global static reservation and gate routing on live active lease occupancy ([#11775](https://github.com/diegosouzapw/OmniRoute/pull/11775)) — thanks @TheDemonTuan +- **fix(ci):** size the install-upgrade gate to a measured run, and log the pack cost ([#11776](https://github.com/diegosouzapw/OmniRoute/pull/11776)) +- **fix(pwa):** stop serving the stale shell after deploys ([#11779](https://github.com/diegosouzapw/OmniRoute/pull/11779)) — thanks @HouMinXi +- **fix(combo):** lock GitHub models rejected as "not supported" for future requests ([#11781](https://github.com/diegosouzapw/OmniRoute/pull/11781)) — thanks @turbolego +- **fix(cli):** probe both IPv4 and IPv6 loopback for server readiness (#11766) ([#11794](https://github.com/diegosouzapw/OmniRoute/pull/11794)) — thanks @vermasomesh835 +- **fix(sse):** preserve CLIProxyAPI wrappers per connection ([#11797](https://github.com/diegosouzapw/OmniRoute/pull/11797)) — thanks @RaviTharuma +- **fix(providers):** honor model auto-fetch opt-in on create ([#11798](https://github.com/diegosouzapw/OmniRoute/pull/11798)) — thanks @RaviTharuma +- **fix(dashboard):** make model auto-fetch order independent ([#11805](https://github.com/diegosouzapw/OmniRoute/pull/11805)) — thanks @RaviTharuma +- **fix(docker):** make GHA cache exports best effort ([#11806](https://github.com/diegosouzapw/OmniRoute/pull/11806)) — thanks @RaviTharuma +- **fix(kiro):** do not permanently ban on 'User is not authorized to make this call' ([#11809](https://github.com/diegosouzapw/OmniRoute/pull/11809)) — thanks @Deftera186 +- **fix(sse):** keep cache-write tokens in OpenAI-shaped usage ([#11814](https://github.com/diegosouzapw/OmniRoute/pull/11814)) — thanks @TheDemonTuan +- **fix(docs):** resolve relative markdown and wiki links across Fumadocs and GitHub wiki ([#11834](https://github.com/diegosouzapw/OmniRoute/pull/11834)) — thanks @AndrianBalanescu +- **fix(chat):** preserve unstripped model string for passthrough provider routing ([#11840](https://github.com/diegosouzapw/OmniRoute/pull/11840)) — thanks @raheemuddin786 +- **fix(mcp):** avoid async initialization inside sync esm wrappers (#11569) ([#11849](https://github.com/diegosouzapw/OmniRoute/pull/11849)) — thanks @HouMinXi +- **fix(sse):** preserve coherent markdown token boundaries across stream translation chunks (#11606) ([#11851](https://github.com/diegosouzapw/OmniRoute/pull/11851)) — thanks @HouMinXi +- **fix(ci):** run the install-upgrade gate on disk, not on the /tmp tmpfs ([#11855](https://github.com/diegosouzapw/OmniRoute/pull/11855)) +- **fix(skills):** expand shorthand property types in injected tool schemas ([#11857](https://github.com/diegosouzapw/OmniRoute/pull/11857)) — thanks @alvinveroy +- **fix(ci):** publish npm from a hosted runner so provenance is accepted ([#11877](https://github.com/diegosouzapw/OmniRoute/pull/11877)) +- **fix(lint):** pin react hooks plugin to 7.0.1 ([#11886](https://github.com/diegosouzapw/OmniRoute/pull/11886)) +- **fix(ci):** surface and clear release-green ESLint failure ([#11890](https://github.com/diegosouzapw/OmniRoute/pull/11890)) — thanks @MumuTW +- **fix(guardrails):** prevent duplicate prompt-injection-guard log output ([#11936](https://github.com/diegosouzapw/OmniRoute/pull/11936)) — thanks @patrykkopycinski +- **fix(auth):** downgrade expected transient states from warn to debug ([#11937](https://github.com/diegosouzapw/OmniRoute/pull/11937)) — thanks @patrykkopycinski +- **fix(codex):** fail over image generation for imported free plans ([#11948](https://github.com/diegosouzapw/OmniRoute/pull/11948)) — thanks @Bl0ck154 +- **fix(codex):** normalize non-stream responses ([#11951](https://github.com/diegosouzapw/OmniRoute/pull/11951)) — thanks @Bl0ck154 +- **fix(images):** forward Antigravity image size ([#11952](https://github.com/diegosouzapw/OmniRoute/pull/11952)) — thanks @Bl0ck154 +- **fix(db):** invalidate connection cache after upsert ([#11953](https://github.com/diegosouzapw/OmniRoute/pull/11953)) — thanks @Bl0ck154 +- **fix(codex):** restore imported account state ([#11954](https://github.com/diegosouzapw/OmniRoute/pull/11954)) — thanks @Bl0ck154 +- **fix(sse):** give extended-thinking targets the reasoning readiness budget ([#11959](https://github.com/diegosouzapw/OmniRoute/pull/11959)) — thanks @abhisheksharma2411 +- **fix(dashboard):** use opaque background and readable text color on cost chart tooltips ([#11960](https://github.com/diegosouzapw/OmniRoute/pull/11960)) — thanks @ZaimMarzuki +- **fix(antigravity):** send complete loadCodeAssist metadata (ideType/platform/pluginType as numeric enums) ([#11969](https://github.com/diegosouzapw/OmniRoute/pull/11969)) — thanks @rifqiawl +- **fix(core):** resolve DB init race condition and reasoning translation ([#12003](https://github.com/diegosouzapw/OmniRoute/pull/12003)) — thanks @raheemuddin786 +- **fix(admission):** exclude reclaimable page cache from the cgroup pressure ratio ([#12017](https://github.com/diegosouzapw/OmniRoute/pull/12017)) — thanks @HouMinXi +- **fix(build):** prune non-production trees in NFT trace excludes and tsconfig ([#12028](https://github.com/diegosouzapw/OmniRoute/pull/12028)) — thanks @Chewji9875 +- **fix(migrations):** add renamed migration compatibility for 056/073/077/101 ([#12036](https://github.com/diegosouzapw/OmniRoute/pull/12036)) — thanks @oyi77 +- **fix(sse):** default crash-guard logger to console.warn, not console ([#12042](https://github.com/diegosouzapw/OmniRoute/pull/12042)) — thanks @HouMinXi +- **fix(catalog):** derive vision/modalities for built-in auto combos from effective target pool ([#12046](https://github.com/diegosouzapw/OmniRoute/pull/12046)) — thanks @Prajeeth-12 +- **fix(release):** the packaged-app smoke verifies the database opened, not a driver line the primary path never prints (twin of #12032) ([#12047](https://github.com/diegosouzapw/OmniRoute/pull/12047)) +- **fix(barrel):** migrate src/app/ off the @/lib/localDb barrel import (#11795 Phase 2) ([#12051](https://github.com/diegosouzapw/OmniRoute/pull/12051)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate src/lib/ off the localDb barrel to direct db imports (#59) ([#12052](https://github.com/diegosouzapw/OmniRoute/pull/12052)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate open-sse, src/shared, src/sse, src/models, src/domain off the @/lib/localDb barrel import (#11795 Phase 4) ([#12053](https://github.com/diegosouzapw/OmniRoute/pull/12053)) — thanks @jonlwheat2-gif +- **fix(barrel):** delete the @/lib/localDb barrel — every consumer migrated (#11795 Phase 5) ([#12055](https://github.com/diegosouzapw/OmniRoute/pull/12055)) — thanks @jonlwheat2-gif +- **fix(radar):** restore D12 public boundary ([#12057](https://github.com/diegosouzapw/OmniRoute/pull/12057)) +- **fix(dev):** bound webpack and Tailwind scans ([#12075](https://github.com/diegosouzapw/OmniRoute/pull/12075)) — thanks @backryun +- **fix(dev):** isolate root layout settings reads ([#12076](https://github.com/diegosouzapw/OmniRoute/pull/12076)) — thanks @backryun +- **fix(dev):** reduce instrumentation executor fan-out (phase 3) ([#12078](https://github.com/diegosouzapw/OmniRoute/pull/12078)) — thanks @backryun +- **fix(dev):** make logging resources HMR-singleton ([#12079](https://github.com/diegosouzapw/OmniRoute/pull/12079)) — thanks @backryun +- **fix(sse):** keep unavailable forced connections scoped ([#12080](https://github.com/diegosouzapw/OmniRoute/pull/12080)) — thanks @keeltrace +- **fix(dev):** isolate batch dispatch from instrumentation ([#12081](https://github.com/diegosouzapw/OmniRoute/pull/12081)) — thanks @backryun +- **fix(ci):** reconcile release test contract drift ([#12082](https://github.com/diegosouzapw/OmniRoute/pull/12082)) — thanks @backryun +- **fix(ci):** accept CVE-2025-68121 in the prebuilt tls-client .so, auto-close base-red issues, guard Scorecard on the default branch ([#12085](https://github.com/diegosouzapw/OmniRoute/pull/12085)) +- **fix(db):** drop three consumer-less 1proxy exports — dead-code base-red on release/v3.8.51 after the barrel deletion (#12055) ([#12087](https://github.com/diegosouzapw/OmniRoute/pull/12087)) +- **fix(api):** clear the six API-route TypeScript regressions the new gate landed red on ([#12094](https://github.com/diegosouzapw/OmniRoute/pull/12094)) +- **fix(release):** never let the tag-push Create Release append auto notes to the curated body ([#12096](https://github.com/diegosouzapw/OmniRoute/pull/12096)) +- **fix(sse):** honor CLIProxyAPI environment API key ([#12099](https://github.com/diegosouzapw/OmniRoute/pull/12099)) — thanks @RaviTharuma +- **fix(oauth):** bind Google refresh to the client that issued the token ([#12106](https://github.com/diegosouzapw/OmniRoute/pull/12106)) — thanks @HouMinXi +- **fix(dashboard):** make RequestLoggerDetail loadable outside Next — CSS via globals.css + CJS/ESM interop (#11703 base-reds) ([#12114](https://github.com/diegosouzapw/OmniRoute/pull/12114)) +- **fix(leases):** project status lease row to lease columns so joined connection PII never escapes ([#12115](https://github.com/diegosouzapw/OmniRoute/pull/12115)) — thanks @geek007git +- **fix(plugins):** do not kill the plugin process when a fire-and-forget hook times out ([#12116](https://github.com/diegosouzapw/OmniRoute/pull/12116)) — thanks @geek007git +- **fix(guardrails):** restore injection-guard logging on middleware-only routes ([#12117](https://github.com/diegosouzapw/OmniRoute/pull/12117)) — thanks @geek007git +- **fix(plugins):** refresh stored manifest from disk on activate so new hook fields reach existing installs ([#12120](https://github.com/diegosouzapw/OmniRoute/pull/12120)) — thanks @geek007git +- **fix(codex):** preserve existing provider state when bulk-import upserts a matching connection ([#12122](https://github.com/diegosouzapw/OmniRoute/pull/12122)) — thanks @geek007git +- **fix(ci):** clear the two base-reds the 2026-08-30 merge batch left on release/v3.8.51 (round 3) ([#12123](https://github.com/diegosouzapw/OmniRoute/pull/12123)) +- **fix(sse):** strip type:'custom' from Claude tools on agentrouter dispatch ([#12126](https://github.com/diegosouzapw/OmniRoute/pull/12126)) — thanks @wahidsadik371-coder +- **fix(sse):** stop advertising video providers the dispatcher cannot run ([#12131](https://github.com/diegosouzapw/OmniRoute/pull/12131)) — thanks @quiterunner-commits +- **fix(search):** treat HTTP 432 and plan limit errors as transient cooldown ([#12139](https://github.com/diegosouzapw/OmniRoute/pull/12139)) — thanks @HouMinXi +- **fix(sse):** refuse an AI Horde queue that cannot fit the request budget ([#12143](https://github.com/diegosouzapw/OmniRoute/pull/12143)) — thanks @quiterunner-commits +- **fix(ci):** clear the base-reds the afternoon merge batch left on release/v3.8.51 (round 5: provider count 352, TS2554/TS2677) ([#12144](https://github.com/diegosouzapw/OmniRoute/pull/12144)) +- **fix(translator):** strip neutral tool_choice when tools absent in Responses-to-Chat (#12141) ([#12166](https://github.com/diegosouzapw/OmniRoute/pull/12166)) — thanks @HouMinXi +- **fix(guardrails):** pass providerId to getResolvedModelCapabilities in checkComboVision (#12112) ([#12169](https://github.com/diegosouzapw/OmniRoute/pull/12169)) — thanks @HouMinXi +- **fix(translator):** strip plaintext reasoning content for opaque responses backends (#12128) ([#12171](https://github.com/diegosouzapw/OmniRoute/pull/12171)) — thanks @HouMinXi +- **perf(stream):** compile hot-path regexes once, bound token caches, fix quadratic buffering ([#12179](https://github.com/diegosouzapw/OmniRoute/pull/12179)) — thanks @opensource-elearning +- **fix:** resolve compression worker file using runtime anchors instead of… ([#12183](https://github.com/diegosouzapw/OmniRoute/pull/12183)) — thanks @NightStalker-87 +- **fix:** prevent Claude Code session kills via liveness-aware readiness + auto model echo ([#12189](https://github.com/diegosouzapw/OmniRoute/pull/12189)) — thanks @opensource-elearning +- **fix(translator):** preserve falsy primitive values in Gemini and Antigravity function response results ([#12191](https://github.com/diegosouzapw/OmniRoute/pull/12191)) — thanks @Siva010 +- **fix(oauth):** mark empty Antigravity projectId as degraded and clear stale errors (#11284) ([#12205](https://github.com/diegosouzapw/OmniRoute/pull/12205)) — thanks @HouMinXi +- **fix(providers):** gate the Codex auto-ping usage read on the shared quota throttle ([#12209](https://github.com/diegosouzapw/OmniRoute/pull/12209)) — thanks @abhisheksharma2411 +- **fix(combos):** prioritize SQLite row id over inner JSON id and notify delete errors ([#12213](https://github.com/diegosouzapw/OmniRoute/pull/12213)) — thanks @HouMinXi +- **fix(providers):** mark featherless.ai as no longer having a free tier (`hasFree: false`) ([#12216](https://github.com/diegosouzapw/OmniRoute/pull/12216)) — thanks @turbolego +- **fix(deepseek-web):** stop Turbopack dev panic in the PoW worker path resolver ([#12221](https://github.com/diegosouzapw/OmniRoute/pull/12221)) — thanks @hartmark +- **fix(chat-admission):** clarify local 503 source ([#12223](https://github.com/diegosouzapw/OmniRoute/pull/12223)) — thanks @keeltrace +- **fix(dev):** silence webpack runtime module warnings ([#12228](https://github.com/diegosouzapw/OmniRoute/pull/12228)) — thanks @backryun +- **fix(db):** use module.require for CommonJS runtime driver loading ([#12230](https://github.com/diegosouzapw/OmniRoute/pull/12230)) — thanks @raheemuddin786 +- **fix(memory):** honest probe-driven FTS5 keyword status + memory_id rowid sync ([#12231](https://github.com/diegosouzapw/OmniRoute/pull/12231)) — thanks @raheemuddin786 +- **fix(system):** propagate abort signal to stream reader in HTTP version checks ([#12232](https://github.com/diegosouzapw/OmniRoute/pull/12232)) — thanks @raheemuddin786 +- **fix(resilience):** isolate local host execution errors from provider circuit breakers ([#12233](https://github.com/diegosouzapw/OmniRoute/pull/12233)) — thanks @raheemuddin786 +- **fix(sse):** sort injected tools deterministically for prompt caching ([#12234](https://github.com/diegosouzapw/OmniRoute/pull/12234)) — thanks @raheemuddin786 +- **fix(combo):** always clear the loop-safety timer, not just on the happy path (#11804) ([#12245](https://github.com/diegosouzapw/OmniRoute/pull/12245)) +- **fix(quality):** base-red pair — stryker tap registration + turn-pin suites aligned to the window gate ([#12255](https://github.com/diegosouzapw/OmniRoute/pull/12255)) — thanks @backryun +- **fix(dev):** qualify Turbopack runtime boundaries ([#12258](https://github.com/diegosouzapw/OmniRoute/pull/12258)) — thanks @backryun +- **fix(combo):** fall back to full pool when collapsed sole survivor is context-too-small ([#12278](https://github.com/diegosouzapw/OmniRoute/pull/12278)) — thanks @opensource-elearning +- **fix(combo):** bound the pre-dispatch unavailable skip so a stale label cannot dark a pool (#12168) ([#12285](https://github.com/diegosouzapw/OmniRoute/pull/12285)) +- **fix(security):** strip Qwen/Alibaba console-session cookies from provider API responses ([#12287](https://github.com/diegosouzapw/OmniRoute/pull/12287)) +- **fix(usage):** console-aware Token Plan guidance + subscription hint on bailian 401 ([#12288](https://github.com/diegosouzapw/OmniRoute/pull/12288)) +- **fix(resourcePressure):** log numeric detail on every rejection, recover faster ([#12293](https://github.com/diegosouzapw/OmniRoute/pull/12293)) — thanks @hartmark +- **fix(release):** drain the 2026-09-01 base-red window — passthrough usage regression + radarPage i18n keys ([#12327](https://github.com/diegosouzapw/OmniRoute/pull/12327)) +- **fix(ci):** openapi-security-tiers checker must honor routeGuard patterns + imported prefixes ([#12350](https://github.com/diegosouzapw/OmniRoute/pull/12350)) — thanks @ggiak +- **perf(ci):** cache node_modules in the npm-ci-retry composite (#8084 D3) ([#12408](https://github.com/diegosouzapw/OmniRoute/pull/12408)) +- **revert(ui):** point CTAs back at their real destinations ([#12410](https://github.com/diegosouzapw/OmniRoute/pull/12410)) +- **fix(release):** drain the 2026-09-02 base-red — rerank-providers import + api-typecheck baseline ratchet ([#12414](https://github.com/diegosouzapw/OmniRoute/pull/12414)) +- **fix(types):** make system prompt injection noImplicitAny-safe ([#12416](https://github.com/diegosouzapw/OmniRoute/pull/12416)) +- **fix(quality):** validate typecheck baseline schema ([#12419](https://github.com/diegosouzapw/OmniRoute/pull/12419)) +- **fix(memory):** resolve rerank provider node cache import ([#12421](https://github.com/diegosouzapw/OmniRoute/pull/12421)) +- **fix(ci):** repair release-root regressions — pack dedup, web-session syntax, uc-image ids ([#12423](https://github.com/diegosouzapw/OmniRoute/pull/12423)) — thanks @backryun +- **fix(providers):** separate MaxAI and UC credential contracts ([#12431](https://github.com/diegosouzapw/OmniRoute/pull/12431)) +- **fix(cli):** preserve Claude settings on config set ([#12432](https://github.com/diegosouzapw/OmniRoute/pull/12432)) — thanks @KrzysiekSko +- **fix(providers):** repair the maxai credential block truncated by merge auto-resolve ([#12433](https://github.com/diegosouzapw/OmniRoute/pull/12433)) +- **fix(quality):** record the 2026-09-02 merged growth in the file-size baseline ([#12434](https://github.com/diegosouzapw/OmniRoute/pull/12434)) +- **fix(uc):** route UC error strings through sanitizeErrorMessage; allowlist the retired codex id ([#12437](https://github.com/diegosouzapw/OmniRoute/pull/12437)) +- **fix(adapta):** redact streamed upstream errors ([#12438](https://github.com/diegosouzapw/OmniRoute/pull/12438)) +- **fix(sse):** remap non-contiguous upstream tool_calls index to a gap-free output_index ([#12445](https://github.com/diegosouzapw/OmniRoute/pull/12445)) — thanks @hartmark +- **fix(quality):** detect duplicate tool_calls entries in one response ([#12446](https://github.com/diegosouzapw/OmniRoute/pull/12446)) — thanks @hartmark +- **fix(conversations):** resolve turn content OmniRoute never sends back to the client ([#12447](https://github.com/diegosouzapw/OmniRoute/pull/12447)) — thanks @hartmark +- **fix(combo):** do not treat credits-exhausted 401 as auth skip ([#12449](https://github.com/diegosouzapw/OmniRoute/pull/12449)) — thanks @RaviTharuma +- **fix(auth):** do not park healthy quota accounts as expired ([#12452](https://github.com/diegosouzapw/OmniRoute/pull/12452)) — thanks @RaviTharuma +- **fix(responses-continuation):** fail closed on a collector-truncated, empty output array ([#12460](https://github.com/diegosouzapw/OmniRoute/pull/12460)) — thanks @hartmark +- **fix(logging):** raise the SSE payload collector's default cap ([#12461](https://github.com/diegosouzapw/OmniRoute/pull/12461)) — thanks @hartmark +- **fix(resilience):** surface Responses failed.error.message in 502s ([#12472](https://github.com/diegosouzapw/OmniRoute/pull/12472)) — thanks @RaviTharuma +- **fix(security):** unbiased maxai X-Random nonce + stricter URL/regex assertions ([#12502](https://github.com/diegosouzapw/OmniRoute/pull/12502)) +- **fix(video):** re-anchor transcript log-redaction so PII/credential maskers can't reopen the leak (#12150 P1 follow-up) ([#12503](https://github.com/diegosouzapw/OmniRoute/pull/12503)) +- **fix(monitoring):** serve cached credentialHealth off the request path ([#12533](https://github.com/diegosouzapw/OmniRoute/pull/12533)) — thanks @RaviTharuma +- **fix(quota):** keep Antigravity Gemini usable when Claude weekly is empty ([#12566](https://github.com/diegosouzapw/OmniRoute/pull/12566)) — thanks @HouMinXi +- **fix(authz):** hard-gate every credential export and CLI-config write (GHSA-5926-2w35-7h4q) ([#12600](https://github.com/diegosouzapw/OmniRoute/pull/12600)) +- **fix(docs):** restore the Next build — REMOVED_PROVIDERS.md had no frontmatter (base-red #12581) ([#12610](https://github.com/diegosouzapw/OmniRoute/pull/12610)) +- **fix(quality):** re-point the zcodeProtocol public-creds allowlist to line 313 ([#12615](https://github.com/diegosouzapw/OmniRoute/pull/12615)) +- **fix(security):** close 3 advisories — search baseUrl exfil, sk- in the error sanitizer, bifrost relay header leak ([#12620](https://github.com/diegosouzapw/OmniRoute/pull/12620)) +- **fix(providers):** sanitize boolean required and nested bare maps for Gemini (#12269) ([#12624](https://github.com/diegosouzapw/OmniRoute/pull/12624)) — thanks @HouMinXi +- **fix(resilience):** keep Overloaded STREAM_EARLY_EOF off the provider breaker ([#12626](https://github.com/diegosouzapw/OmniRoute/pull/12626)) — thanks @HouMinXi +- **fix(catalog):** re-audit free-tier quotas against official pages ([#12649](https://github.com/diegosouzapw/OmniRoute/pull/12649)) +- **fix(dashboard):** keep a request's pending-tracking id stable across combo target retries ([#12650](https://github.com/diegosouzapw/OmniRoute/pull/12650)) — thanks @hartmark +- **fix(tests):** retire dead model ids from the chat-pipeline integration suite (base-red #12581) ([#12670](https://github.com/diegosouzapw/OmniRoute/pull/12670)) +- **fix:** resolve SqliteError no such table compression_run_telemetry during cleanup ([#12682](https://github.com/diegosouzapw/OmniRoute/pull/12682)) — thanks @groovecityJO +- **fix(combo):** treat a pin-only step as implicit connection allowlist ([#12697](https://github.com/diegosouzapw/OmniRoute/pull/12697)) — thanks @HouMinXi +- **fix(ci):** ship .npmrc in published package so legacy-peer-deps applies to consumers (#11544) ([#12699](https://github.com/diegosouzapw/OmniRoute/pull/12699)) — thanks @KooshaPari +- **fix(docker):** pin 4 CLI tools to exact versions (#12576) ([#12703](https://github.com/diegosouzapw/OmniRoute/pull/12703)) — thanks @KooshaPari +- **fix(docker):** document and harden cli profile trust boundary (#12570) ([#12706](https://github.com/diegosouzapw/OmniRoute/pull/12706)) — thanks @KooshaPari +- **fix(sse):** redact video transcript in pre-guardrail rejected-request logs (#12150 P2 item 7) ([#12710](https://github.com/diegosouzapw/OmniRoute/pull/12710)) +- **fix(i18n):** quote placeholder in OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES description (#12505) ([#12769](https://github.com/diegosouzapw/OmniRoute/pull/12769)) — thanks @KooshaPari +- **fix(api/pricing):** surface validation error message as string, not raw object (#12494) ([#12771](https://github.com/diegosouzapw/OmniRoute/pull/12771)) — thanks @KooshaPari +- **fix(sse):** re-enable prompt compression for native Codex passthrough ([#12834](https://github.com/diegosouzapw/OmniRoute/pull/12834)) — thanks @thomasmaerz +- **fix(dashboard):** model health tests for a provider node set to the Responses API now call `/v1/responses` with a Responses-shaped body instead of `/v1/chat/completions` — those models were reported as `Provider returned HTTP 200 but no text content` even though the same model answered normally through `/v1/responses` ([#13070](https://github.com/diegosouzapw/OmniRoute/issues/13070)) ([#13087](https://github.com/diegosouzapw/OmniRoute/pull/13087)) — thanks @ntdatt812 +- fix(routing): stop a round-robin combo's "opencode" targets from collapsing onto the opencode-zen connection (#11912) ([#13283](https://github.com/diegosouzapw/OmniRoute/pull/13283)) +- fix(routing): stop the reactive-compaction debug log from lying when compression is globally disabled (#11977) ([#13276](https://github.com/diegosouzapw/OmniRoute/pull/13276)) +- fix(electron): relativize standalone-bundle symlink targets so Stage 8 manifest verification stops failing on Windows (#11979) ([#13251](https://github.com/diegosouzapw/OmniRoute/pull/13251)) +- fix(dashboard): surface a visible error when Compression Studio's combined preview run fails (#12061) ([#13288](https://github.com/diegosouzapw/OmniRoute/pull/13288)) +- fix(dashboard): make the compression "Effective pipeline" preview honor the active profile and warn when the master switch is off (#12063) ([#13287](https://github.com/diegosouzapw/OmniRoute/pull/13287)) +- fix(providers): scope TinyCMS Web signer's DOM shims to each call instead of leaking them for the process lifetime, and surface a clean HTTP status on a non-JSON interception-toggles error (#12072) ([#13254](https://github.com/diegosouzapw/OmniRoute/pull/13254)) +- fix(guardrails): stop Vision Bridge from re-selecting a model locked after a 404 (#12111) ([#13259](https://github.com/diegosouzapw/OmniRoute/pull/13259)) +- fix(sse): require Responses-shaped body before native OpenAI-compatible passthrough (#12129) ([#13278](https://github.com/diegosouzapw/OmniRoute/pull/13278)) +- fix(providers): minimax-m3 now collapses manual thinking.type:"enabled" to adaptive, preventing upstream 400 (2013) (#12132) ([#13249](https://github.com/diegosouzapw/OmniRoute/pull/13249)) +- fix(db): scope model visibility overrides by modality so hiding a Chat model no longer hides an identically-ID'd Image/Embeddings/etc. model (#12172) ([#13258](https://github.com/diegosouzapw/OmniRoute/pull/13258)) +- fix(db): scope local-provider apiKey dedup to matching base URL so LM Studio/Ollama-style connections support multiple accounts (#12173) ([#13250](https://github.com/diegosouzapw/OmniRoute/pull/13250)) +- fix(providers): refresh Trae's stale Referer/Origin and forward user timezone so imported connections stop failing with 401 (#12190) ([#13255](https://github.com/diegosouzapw/OmniRoute/pull/13255)) +- fix(providers): route opencode-go/gpt-5.6-luna to /responses instead of /chat/completions (#12196) ([#13275](https://github.com/diegosouzapw/OmniRoute/pull/13275)) +- fix(dashboard): allow deleting the last extra-upstream-header row even when invalid (#12251) ([#13252](https://github.com/diegosouzapw/OmniRoute/pull/13252)) +- **fix(i18n):** translate pre-existing `__MISSING__:` keys for `combo.sort`, `requestLogger.detail` expand/collapse, `common.profile`, and `settings.resilienceCredentialHealth*` across 39 locales ([#12272](https://github.com/diegosouzapw/OmniRoute/issues/12272)) ([#13034](https://github.com/diegosouzapw/OmniRoute/pull/13034)) — thanks @HouMinXi +- fix(cli): run the Node.js runtime compatibility guard before the heavy `tsx/esm` + Commander import chain so an unsupported runtime gets a clear message instead of a raw `Invalid regular expression flags` crash (#12296) ([#13253](https://github.com/diegosouzapw/OmniRoute/pull/13253)) +- fix(dashboard): refresh the providers list after deleting a compatible provider node (#12298) ([#13256](https://github.com/diegosouzapw/OmniRoute/pull/13256)) +- fix(usage): fail closed on API-key budget enforcement when a provider's `auto` routing alias has no pricing row, instead of silently counting it as $0 (#12341) ([#13257](https://github.com/diegosouzapw/OmniRoute/pull/13257)) +- **fix(providers):** include the submitted Agnes video model when polling by `video_id` ([#12356](https://github.com/diegosouzapw/OmniRoute/pull/12356)) — thanks @pacocartones +- **fix(routing):** custom OpenAI-compatible nodes now honor the saved Chat/Responses API type after edits instead of letting the node's original ID prefix override the live connection setting ([#11884](https://github.com/diegosouzapw/OmniRoute/issues/11884)). ([#12358](https://github.com/diegosouzapw/OmniRoute/pull/12358)) — thanks @pacocartones +- fix(sse): surface an error instead of a silent empty 200 when a Claude stream closes with zero bytes (#12398) ([#13285](https://github.com/diegosouzapw/OmniRoute/pull/13285)) +- fix(oauth): warn before the dead localhost:8080 redirect in antigravity/gemini `oauth start` (#12413) ([#13265](https://github.com/diegosouzapw/OmniRoute/pull/13265)) +- fix(providers): stop devin-cli spawn error from double-closing the SSE controller (#12517) ([#13261](https://github.com/diegosouzapw/OmniRoute/pull/13261)) +- **fix(rerank):** clamp Voyage `top_k` to the documents actually sent after empty-string filtering, and honor `return_documents: false` in the NVIDIA response adapter (#12523 — thanks @pacocartones) +- **fix(audio):** `/v1/audio/translations` now resolves combo names the way `/v1/audio/transcriptions` already does, so a combo that `GET /v1/models` advertises is fanned out to its targets instead of being rejected with `400 Invalid translation model: . Use format: provider/model`; literal `provider/model` ids and unknown bare names behave as before (#12536 — thanks @pacocartones) +- **fix(gemini):** strip the JSON-Schema-2020-12 `prefixItems` keyword from Gemini tool schemas at every nesting level, so Claude Code tool definitions no longer fail with `400 Unknown name "prefixItems"` on Gemini models (#12540 — thanks @pacocartones) +- **fix(api-manager):** Expose an accessible loading status while API keys are fetched instead of an empty accessibility tree (#12541 — thanks @pacocartones) +- **fix(video):** Clamp `estimateJpegFrameBytes` at zero for padding-only payloads and build the three encode-side frame data URIs from `JPEG_FRAME_DATA_URI_PREFIX` instead of a repeated literal (#12543 — thanks @pacocartones) +- **fix(devin):** accept Windows `DEVIN_AGENTIC_HOME` sandbox paths (`C:\...\.sandbox\...`) in the isolated-home check so the Devin Claude Bridge no longer fails closed on Windows ([#12405](https://github.com/diegosouzapw/OmniRoute/issues/12405)) (#12545 — thanks @pacocartones) +- **fix(db):** Add `conversation_turn_nodes` and orphaned `agentic_conversations` to the auto-cleanup cycle under the existing `retention.callLogs` window, so identity nodes whose call-log content has already been purged no longer accumulate without bound in `storage.sqlite` (#12548 — thanks @pacocartones) +- **fix(i18n):** Wrap the `~/.claude/profiles//settings.json` placeholder in ICU single quotes in the `OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES` feature-flag description across all 42 locales and the TypeScript default, so next-intl no longer fails with `INVALID_MESSAGE: UNCLOSED_TAG` and the Feature Flags card shows the description instead of the raw key (#12549 — thanks @pacocartones) +- **fix(orchestration):** `updateCloudAgentTask` now publishes the task's real `status` on `agent.task.updated` when an update only touches `result`, `activities` or `error`, instead of the fabricated `"updated"` state, and stays silent when no row matched the id (#12550 — thanks @pacocartones) +- **fix(i18n):** the home "Recent Requests" panel and the Provider Topology legend are now translated instead of rendering English copies on non-English dashboards; the legend reads its own `home.topologyLegend*` labels with consistent casing rather than borrowing the memory-settings "Recent" and analytics "Error" strings (#12551 — thanks @pacocartones). +- **docs(reference):** bring the `FEATURE_FLAGS.md` catalog back to 1:1 with `featureFlagDefinitions.ts` — 20 missing flags added, the two `*_BLOCK_THRESHOLD` env-only knobs moved out of the flag tables, category/total counts and the Live WS port corrected, guarded by a static test (#12552 — thanks @pacocartones) +- **fix(i18n):** backfill missing `usage.kiloPass*` strings in 39 locales and restore `featureFlagOmnirouteDisableThinkingLevelVariantsDescription` in `pt.json` ([#12561](https://github.com/diegosouzapw/OmniRoute/issues/12561)) ([#13027](https://github.com/diegosouzapw/OmniRoute/pull/13027)) — thanks @HouMinXi +- fix(docker): default docker-compose app ports (dashboard/API/live-WS) to loopback instead of `0.0.0.0`, closing the anonymous `/v1` LAN/WAN exposure gap left open by `REQUIRE_API_KEY=false` (#12568) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): close DNS-rebinding SSRF gap in webhook outbound-URL guard (#12569) ([#13243](https://github.com/diegosouzapw/OmniRoute/pull/13243)) +- fix(docker): require a per-session token on the VNC browser CDP bridge and isolate it on a dedicated Docker network (#12571) ([#13241](https://github.com/diegosouzapw/OmniRoute/pull/13241)) +- fix(open-sse): write Adobe Firefly session tokens and cookie jars with 0700/0600 permissions instead of the process umask (#12572) ([#13242](https://github.com/diegosouzapw/OmniRoute/pull/13242)) +- fix(api): remove hardcoded wildcard CORS in openai-to-gemini-sse.ts so the centralized fail-closed CORS gate is the sole source of `Access-Control-Allow-Origin` (#12573) ([#13239](https://github.com/diegosouzapw/OmniRoute/pull/13239)) +- fix(api): enforce API key policy (budget/rate-limit/schedule/endpoint scoping) on the ElevenLabs speech-to-text, text-to-speech and voices proxy routes (#12574) ([#13244](https://github.com/diegosouzapw/OmniRoute/pull/13244)) +- fix(sse): cap HuggingChat NDJSON body size and bound the read loop with the fetch timeout so a stalled or hostile upstream cannot buffer unbounded memory (#12577) ([#13282](https://github.com/diegosouzapw/OmniRoute/pull/13282)) +- fix(docker): scope the cliproxyapi/qdrant/bifrost sidecars to loopback by default and forward `CLIPROXYAPI_MANAGEMENT_KEY` into the cliproxyapi container so its management API is not left both unauthenticated and LAN-published (#12578) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): create DB export temp paths with `fs.mkdtempSync` instead of predictable timestamps (#12579) ([#13237](https://github.com/diegosouzapw/OmniRoute/pull/13237)) +- Cline 401 bodies that say "re-authenticate your Cline account" classify as a refreshable OAuth token, not a terminal expired key. The cooling panel no longer labels every cooldown as a 429; it shows the recorded last error instead. (#12594) ([#13060](https://github.com/diegosouzapw/OmniRoute/pull/13060)) — thanks @HouMinXi +- **fix(catalog):** degrade unknown combo targets instead of dropping LCD modalities, and persist OpenRouter `architecture.input_modalities` into the capability snapshot ([#12613](https://github.com/diegosouzapw/OmniRoute/issues/12613)) ([#13042](https://github.com/diegosouzapw/OmniRoute/pull/13042)) — thanks @HouMinXi +- fix(providers): send `x-api-key` instead of `Authorization: Bearer` for OpenCode Zen's `/v1/responses` endpoint (Muse Spark Contributor models), fixing a 401 on OmniRoute's auth header (#12633) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(sse): surface the actionable "Auggie CLI not found" message when the shell reports a missing `auggie` binary via exit code instead of a spawn error (#12645) ([#13263](https://github.com/diegosouzapw/OmniRoute/pull/13263)) +- **fix(config):** Persist deletions of built-in background-degradation entries — when a stored settings record exists its `degradationMap` is now authoritative instead of being merged under the defaults, so an entry the user removed in the dashboard no longer reappears on the next apply or restart ([#12424](https://github.com/diegosouzapw/OmniRoute/issues/12424)) ([#12647](https://github.com/diegosouzapw/OmniRoute/pull/12647)) — thanks @pacocartones +- **fix(gamification):** action-count badge milestones (First Token, Token Consumer, Token Machine, Token Whale, and the token-sharing tier) are now backed by a durable `xp_action_counts` counter incremented in `addXp()`, instead of a live `COUNT(*)` over `xp_audit_log`. The audit log is pruned by `retention.xpAuditLog` (default 30 days), so on a default install those "lifetime" milestones were really "actions in the last 30 days" and unlocked badges could stop unlocking once old rows aged out. `getActionCount()` and `checkActionCountBadges()` now read the same durable source, and a migration backfills existing totals from the surviving audit rows ([#12546](https://github.com/diegosouzapw/OmniRoute/issues/12546)) ([#12651](https://github.com/diegosouzapw/OmniRoute/pull/12651)) — thanks @pacocartones +- **fix(images):** `/v1/images/edits` now iterates a combo's targets the same way `/v1/images/generations` does (#9239) instead of flattening a bare combo to its first target. A combo whose first target is not edit-capable — or lacks credentials — now falls through to a later edit-capable target rather than hard-erroring, and missing credentials are skipped (not a hard `401`) to match the generations path. The per-target skip/terminal classification is extracted into a shared `runImageComboTargets` loop, so generations behavior is unchanged ([#12547](https://github.com/diegosouzapw/OmniRoute/issues/12547)). ([#12653](https://github.com/diegosouzapw/OmniRoute/pull/12653)) — thanks @pacocartones +- fix(sse): add first-byte watchdog to the TLS-fingerprint transport so a stalled wreq body falls back instead of hanging for minutes (#12656) ([#13272](https://github.com/diegosouzapw/OmniRoute/pull/13272)) +- fix(sse): exempt tiny-budget reasoning probes from combo quality failure and surface persisted-cooldown skips in ALL_TARGETS_SKIPPED diagnostics (#12659) ([#13269](https://github.com/diegosouzapw/OmniRoute/pull/13269)) +- fix(models): declare the real ~1M contextLength for OpenCode Zen's Muse Spark 1.2 models instead of falling back to the 200000 provider default (#12681) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(oauth): align codebuddy-cn OAuth User-Agent with the chat/usage CLI version to avoid WAF false positives (#12702) ([#13264](https://github.com/diegosouzapw/OmniRoute/pull/13264)) +- fix(dashboard): surface an authentication-required banner instead of silently blanking database settings for a guest session (#12709) ([#13270](https://github.com/diegosouzapw/OmniRoute/pull/13270)) +- **fix(resilience):** stop unbounded queue that hangs 6min until Aborted — gate, provider slot, and Bottleneck queue now share a per-connection `maxWaitMs` budget; `fail-closed` on exhaust (503); execution backstop `executionMaxWaitMs` overridable per connection with upstream clamp ([#12715](https://github.com/diegosouzapw/OmniRoute/pull/12715)) — thanks @maxmad64bis (with thanks to @Tushar49 for surfacing the slow-provider need in #12635) +- **fix(auto-combo):** every mode pack now carries `quality` and `reliability` weights (`quality` 0.02, 0.03 in `quality-first`; `reliability` 0.03, 0.04 in `reliability-first`), so selecting a pack no longer silences either signal; `DEFAULT_WEIGHTS` is unchanged ([#12731](https://github.com/diegosouzapw/OmniRoute/pull/12731)) — thanks @maxmad64bis +- **fix(sse):** the provider execution pipeline keeps the upstream error `code`/`type` again — both were lost when the non-streaming leg was extracted, so a config-class `422` (Antigravity missing project) degraded into a generic account cooldown instead of reaching the gate that recognises it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(db):** a legacy `call_logs` table no longer aborts startup — the provider-stats index is created after the column healing runs, not before it, so an install predating `request_type` opens instead of failing with `no such column` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(catalog):** a malformed row in the operator-writable custom-models blob no longer kills every `auto/*` pool ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(tests):** realign the guards that the same day's merges left asserting the old behavior — the provider-count total after GreenPT and EURouter, the GLM stream buffer slot that `#12925` turned into a declared parameter, and the injection scan bound that `#13104` reshaped from a head-only window into head-plus-tail ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- fix(cache): fold tool_choice/tools/response_format into the semantic cache signature so a cached tool_calls response can no longer be replayed for a request whose tool policy forbids it (#12734) ([#13267](https://github.com/diegosouzapw/OmniRoute/pull/13267)) +- **fix(models):** stop treating provider-supplied `isFree:true`/`:free`/`0/0` flags as trusted unless the provider has a documented free tier — only the shipped free-tier catalog decides otherwise; fetched `isFree:true` trusted only for a free-tier provider, custom `isFree:true` via a trusted path ([#12744](https://github.com/diegosouzapw/OmniRoute/pull/12744)) — thanks @maxmad64bis +- fix(memory): authenticate the internal /v1/rerank loopback call so memory reranking no longer silently degrades to unranked order when REQUIRE_API_KEY=true (#12745) ([#13268](https://github.com/diegosouzapw/OmniRoute/pull/13268)) +- fix(sse): parse Ollama Cloud's current usage markup (`$X of $Y used` aria-label, nested width style) (#12749) ([#13260](https://github.com/diegosouzapw/OmniRoute/pull/13260)) +- fix(cli): setup-opencode no longer sends an active context's management token to `/v1/models` when `--api-key`/`OMNIROUTE_API_KEY` is supplied — an explicit flag or the env var now always outranks the context's token, and the flag itself is no longer swallowed by the parent program's global `--api-key` option (#12783) ([#13246](https://github.com/diegosouzapw/OmniRoute/pull/13246)) +- fix(sse): register Arcee AI in the executor provider registry so requests reach api.arcee.ai instead of silently falling back to OpenAI (#12784) ([#13277](https://github.com/diegosouzapw/OmniRoute/pull/13277)) +- **fix(quota):** share one upstream quota read when identical saturation checks arrive at the same time, so traffic bursts don't multiply provider API calls ([#12787](https://github.com/diegosouzapw/OmniRoute/pull/12787)) — thanks @maxmad64bis +- **fix(db):** ignore expired or invalid rate-limit cooldown writes so a stale timestamp can't lock a connection that should be usable — clearing still works as before ([#12788](https://github.com/diegosouzapw/OmniRoute/pull/12788)) — thanks @maxmad64bis +- **fix(providers):** single source for provider order with `xao` ranking alongside `xai-oauth` ([#12790](https://github.com/diegosouzapw/OmniRoute/pull/12790)) — thanks @maxmad64bis +- **fix(routing):** off-table models check the free-model catalog before inheriting premium prices, latency bootstraps from the observed pool median, async tiers read live database pricing with a 90-day freshness gate, and the tier cache invalidates on every pricing write ([#12792](https://github.com/diegosouzapw/OmniRoute/pull/12792)) — thanks @maxmad64bis +- **fix(auto-combo):** snapshot scoring uses the observed per-connection account tier and per-model quality instead of neutral constants, and circuit-open providers rank lower at build time ([#12794](https://github.com/diegosouzapw/OmniRoute/pull/12794)) — thanks @maxmad64bis +- **fix(pool):** empty `auto/*` pools now say why they are empty, and the models listing uses the same paid check as routing ([#12795](https://github.com/diegosouzapw/OmniRoute/pull/12795)) — thanks @maxmad64bis +- fix(routing): recognize CLIProxyAPI's 'unknown provider for model' 400 as fallback-worthy (#12800) ([#13284](https://github.com/diegosouzapw/OmniRoute/pull/13284)) +- **fix(sse):** translate-mode streams now emit the estimated token counts as a trailing usage-only chunk before `[DONE]` when the upstream stays silent, so metered chat clients see totals instead of nothing ([#12828](https://github.com/diegosouzapw/OmniRoute/pull/12828)) — thanks @maxmad64bis +- **fix(db):** provider stats stay truthful on empty and legacy databases: fallback counts default to `0` instead of `null`, latency averages read `null` (not `0`) when no durations were recorded, failures logged before the error-type column existed group under `pre_migration` instead of `unclassified`, and per-provider queries use two new composite indexes ([#12832](https://github.com/diegosouzapw/OmniRoute/pull/12832)) — thanks @maxmad64bis +- fix(nvidia): fail open when a synced model catalog goes stale instead of gating forever (#12849) ([#13248](https://github.com/diegosouzapw/OmniRoute/pull/13248)) +- **fix(db):** WAL maintenance lives in its own module: a `TRUNCATE` checkpoint that hits a busy database now warns with its streak and retries once via `PASSIVE` instead of logging success, and closing-time checkpoints no longer report success on builds without a database file, with the busy totals visible in the authenticated monitoring health payload ([#12853](https://github.com/diegosouzapw/OmniRoute/pull/12853)) — thanks @maxmad64bis +- **fix(health):** quota with no snapshots reads empty instead of a contradicting 0% ([#12857](https://github.com/diegosouzapw/OmniRoute/pull/12857)) — thanks @maxmad64bis +- fix(a2a): accept the dashboard's own session cookie on /a2a so "Run message/send" no longer fails with "Unauthorized: missing or invalid API key" (#12888) ([#13271](https://github.com/diegosouzapw/OmniRoute/pull/13271)) +- **fix(logs):** the Logs grid's in-memory filter pass no longer discards rows the SQL query already matched — selecting an API key from the dropdown (which sends the key's id) returns its calls again, the Combo tab shows every combo instead of only those whose name contains a "1", and the model filter and search cover the same columns as the query ([#12896](https://github.com/diegosouzapw/OmniRoute/pull/12896)) — fixes [#12873](https://github.com/diegosouzapw/OmniRoute/issues/12873) — thanks @ntdatt812 +- **fix(a2a):** `/api/a2a/status` now builds the agent card from the request that asked for it, so a gateway reached at a non-localhost host no longer advertises `http://localhost:20128` as its A2A URL ([#12918](https://github.com/diegosouzapw/OmniRoute/pull/12918)). — thanks @ntdatt812 +- **fix(compression):** progressive aging now appends its `[COMPRESSED:aging:…]` annotation after a turn's `tool_result` blocks instead of in front of them, so Anthropic no longer rejects aged conversations with "`tool_use` ids were found without `tool_result` blocks immediately after" ([#12920](https://github.com/diegosouzapw/OmniRoute/pull/12920)). — thanks @ntdatt812 +- **fix(bedrock):** model import now resolves context limits for every vendor prefix instead of only `anthropic.*`, so `global.openai.gpt-5.6-*` no longer imports with a null `inputTokenLimit` and gets rejected pre-flight at the 200k default ([#12921](https://github.com/diegosouzapw/OmniRoute/pull/12921)). — thanks @ntdatt812 +- **fix(stream):** the 64 KB stream buffer GLM asks for is honoured instead of dropped, and the type error it caused no longer fails the API Route Typecheck gate on every open PR ([#12925](https://github.com/diegosouzapw/OmniRoute/pull/12925)) — thanks @ntdatt812 +- **fix(guardrails):** mask PII inside a `tool_result`'s nested content array, which the masker walked past while redacting its sibling block ([#12930](https://github.com/diegosouzapw/OmniRoute/pull/12930)) — thanks @ntdatt812 +- **fix(dashboard):** the Radar catalog table no longer leaves absent data unexplained — empty limits, unknown context windows, and unreported capabilities each explain themselves on hover, and a new check keeps it that way ([#12937](https://github.com/diegosouzapw/OmniRoute/pull/12937)) — thanks @maxmad64bis +- **fix(sse):** geo-blocked opencode requests rotate to the next account proxy instead of failing, so one refused egress no longer aborts the whole chain ([#12941](https://github.com/diegosouzapw/OmniRoute/pull/12941)) — thanks @maxmad64bis +- **fix(codex):** dashboard "clear cooldown" and the CAS recovery path now drop nested `codexScopeRateLimitedUntil` maps in the same write that nulls `rate_limited_until`, and fresh quota snapshots with headroom lift fallback-sourced scope cooldowns parked by quota preflight ([#12817](https://github.com/diegosouzapw/OmniRoute/issues/12817), [#12860](https://github.com/diegosouzapw/OmniRoute/issues/12860), [#12951](https://github.com/diegosouzapw/OmniRoute/pull/12951)) The dashboard reset-credit button (`consumeCodexResetCredit`) now exercises that same snapshot path after a successful redeem, so a filled quota bar is enough to unpark a leftover fallback Codex child without another manual clear. — thanks @HouMinXi +- **fix(db):** resolve `sql-wasm.wasm` across global npm install and hoisted layouts, ensuring OmniRoute can boot cleanly on Node 24 when native `better-sqlite3` is uncompiled. ([#13035](https://github.com/diegosouzapw/OmniRoute/pull/13035)) — thanks @HouMinXi +- **fix(api):** `DELETE /v1/batches/delete-completed` now sweeps only the calling API key's own completed batches (batches with no owner stay out of a key-scoped sweep on purpose), with an explicit instance-wide mode reserved for authenticated dashboard sessions, a 401 for a presented key that is unknown, revoked, deactivated, banned or expired (never falling through to the session branch), audit logging of every sweep, a sanitized 500 on failure, an owner-scoped file half (a key-scoped sweep never nulls a file another tenant owns) and an atomic sweep — chunked in 200-batch transactions in instance mode — so a mid-way error never leaves a batch pointing at a nulled file (GHSA-wvxc-jp3v-5mg5) ([#12969](https://github.com/diegosouzapw/OmniRoute/pull/12969)) +- **fix(combo):** quota-weighted routing stops drawing on an out-of-credit connection — a 402 now invalidates the stored quota snapshot instead of leaving its stale remaining percentage in place, and a snapshot older than 10 minutes no longer counts as confident headroom for the primary pool ([#12972](https://github.com/diegosouzapw/OmniRoute/pull/12972)) ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- **fix(vision):** Custom Models with "Vision capable" checked no longer have image requests swapped to `glm/glm-4.6v` when the client sends the advertised alias (`vllm/path/...`) or the bare path-shaped id — Vision Bridge now matches the stored override for all three id forms ([#12758](https://github.com/diegosouzapw/OmniRoute/issues/12758)) ([#12974](https://github.com/diegosouzapw/OmniRoute/pull/12974)) — thanks @HouMinXi +- **fix(sse):** transient opencode upstream failures rotate to the next account proxy instead of failing, so one flapping egress no longer aborts the whole chain ([#12975](https://github.com/diegosouzapw/OmniRoute/pull/12975)) — thanks @maxmad64bis +- **fix(azure):** Deployments from GPT-6 onward now send `max_completion_tokens` instead of `max_tokens`, which Azure rejects with HTTP 400. The rule matched a literal `gpt-5`, so each new generation arrived broken; it now matches the generation range, while `gpt-35-turbo` still keeps `max_tokens`. ([#13007](https://github.com/diegosouzapw/OmniRoute/pull/13007)) — thanks @ntdatt812 +- **fix(db):** add an opt-in self-restart circuit for sustained critical memory pressure, gate post-cleanup VACUUM behind a minimum freed-rows threshold, and checkpoint the SQLite WAL every 5 minutes with a size guard that escalates to TRUNCATE, so a growing WAL can no longer stall the event loop into a full outage. ([#13011](https://github.com/diegosouzapw/OmniRoute/pull/13011)) — thanks @HouMinXi +- **fix(auth):** an explicit connection pin may probe a quota-disabled row once and re-enable it on success ([#12874](https://github.com/diegosouzapw/OmniRoute/issues/12874)) ([#13017](https://github.com/diegosouzapw/OmniRoute/pull/13017)) — thanks @HouMinXi +- **fix(api):** thread `X-OmniRoute-Fallback-Attempts` through combo chat completions so streaming and non-streaming responses report how many prior legs were attempted ([#13038](https://github.com/diegosouzapw/OmniRoute/pull/13038)) — thanks @HouMinXi +- Restore provider failure classification and credential refresh on non-streaming requests: classify non-2xx failures to lock models on per-model quota exhaustion, update connection rate limits from headers and body, and pass credential refresh handlers to pipeline execution so 401 tokens can be refreshed and retried (#13043). ([#13069](https://github.com/diegosouzapw/OmniRoute/pull/13069)) — thanks @HouMinXi +- **fix(responses):** wrap forced-non-streaming web_search fallback JSON as Responses SSE so Codex still sees `response.completed` ([#13050](https://github.com/diegosouzapw/OmniRoute/pull/13050)) — thanks @HouMinXi +- **fix(antigravity):** Preserve upstream thought-token usage in normalized completion and reasoning token counts ([#13055](https://github.com/diegosouzapw/OmniRoute/pull/13055)) — thanks @pacocartones +- Fix a concurrency-slot leak in the DeepSeek PoW solver: a worker that failed to spawn (for example a missing worker script) never released its slot, so `MAX_CONCURRENT_WORKERS` failures disabled the solver until restart. ([#13097](https://github.com/diegosouzapw/OmniRoute/pull/13097)) — thanks @anhtahaylove +- **fix(acp):** bound the ACP session output buffers — `stdoutBuffer` and `stderrBuffer` now cap at 1 MiB keeping the most recent output behind a visible `[...output truncated...]` marker, and `stderrBuffer` is reset per prompt instead of accumulating for the lifetime of the session. ([#13100](https://github.com/diegosouzapw/OmniRoute/pull/13100)) — thanks @anhtahaylove +- **fix(acp):** release the `stdout`/`exit` listeners and the idle timer that a `sendPrompt` timeout used to leave attached to the `acpManager` singleton, and drop sessions that exited on their own from the session map instead of keeping them forever. ([#13096](https://github.com/diegosouzapw/OmniRoute/pull/13096)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection and PII scanners now read the text a `tool_result` block carries on `content` (string or nested block list), in messages and in system blocks, so tool output is judged by the same rules as user text ([#13101](https://github.com/diegosouzapw/OmniRoute/pull/13101)) — thanks @ntdatt812 +- **fix(gamification):** close the badge notification SSE stream when the request signal is already aborted before the stream starts — a client that disconnects while the route is still awaiting auth used to leave both the 2s unlock poll and the 15s heartbeat running for the lifetime of the process. ([#13106](https://github.com/diegosouzapw/OmniRoute/pull/13106)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection scan now spends its 16 KB budget on both ends of the request instead of the first 16 KB only, so `system`, `instructions`, `query`, `documents` and the newest turns are no longer hidden behind one long message ([#13104](https://github.com/diegosouzapw/OmniRoute/pull/13104)) — thanks @ntdatt812 +- **fix(dashboard):** expose the Volcano Ark console cookie on quota scraping and unwrap connect-error objects so the dashboard shows the upstream message ([#13107](https://github.com/diegosouzapw/OmniRoute/pull/13107)) — thanks @HouMinXi +- **fix(db):** release the `beforeExit`/`SIGINT`/`SIGTERM` handlers when a `node:sqlite` adapter closes, so a closed adapter and its database handle are no longer pinned to `process` for the lifetime of the run — the same treatment #7494 gave the sql.js adapter. ([#13109](https://github.com/diegosouzapw/OmniRoute/pull/13109)) — thanks @anhtahaylove +- **fix(translator):** `contentSchema` and `unevaluatedItems` are now treated as subschema positions by the tool-schema sanitizer, so a truncation placeholder in either is replaced with a permissive schema instead of being forwarded as a string ([#13110](https://github.com/diegosouzapw/OmniRoute/pull/13110)) — thanks @ntdatt812 +- **fix(cli-helper):** clear the `createLogStream` timeout on the abort path — `stop()` aborts the in-flight fetch and returned through the `signal.aborted` branch, which skipped `clearTimeout` and left an armed timer per stopped stream. The stream reader is now also cancelled when the read loop exits early. ([#13114](https://github.com/diegosouzapw/OmniRoute/pull/13114)) — thanks @anhtahaylove +- **fix(docs):** drop TLS-impersonation, MITM-decrypt, supply-chain attestation, and XOR-mask writeups from the public `/docs` catalog and Docker image. Files stay in git for engineers; operators who need them open the repo, not the website. ([#13136](https://github.com/diegosouzapw/OmniRoute/pull/13136)) — thanks @HouMinXi +- **fix(combo):** parse numeric-epoch `rate_limited_until` in the combo cooldown read path ([#13141](https://github.com/diegosouzapw/OmniRoute/pull/13141)) — thanks @maxmad64bis +- **fix(opencode):** both OpenCode plugins now reject a gateway address typed without `http://` at configuration time, instead of publishing every model with an api url no client can call, and the v2 plugin no longer publishes a model card whose api url is blank or relative ([#13142](https://github.com/diegosouzapw/OmniRoute/pull/13142)) — thanks @maxmad64bis +- **fix(providers):** lock opencode model on upstream 400 model-unavailable ([#13146](https://github.com/diegosouzapw/OmniRoute/pull/13146)) — thanks @maxmad64bis +- **fix(logging):** keep the provider exchange rather than the raw client bodies when a call log exceeds its size budget, and show that recovered payload in the request-detail panel instead of replacing it with the stored response body ([#13147](https://github.com/diegosouzapw/OmniRoute/pull/13147)) — thanks @maxmad64bis +- **fix(traffic-inspector):** the WebSocket route no longer leaks a traffic-buffer subscriber and a 30s ping timer when the client socket is already closed at handler time — listeners are attached before any resource is acquired, a destroyed socket bails out early, and the ping interval stops on a dead socket where `write()` never throws ([#13155](https://github.com/diegosouzapw/OmniRoute/pull/13155)) — thanks @anhtahaylove +- **fix(telegram):** bound the per-user API key cache in the Telegram chat proxy so a burst of distinct chat ids can no longer grow the process heap without limit ([#13165](https://github.com/diegosouzapw/OmniRoute/issues/13165)) ([#13166](https://github.com/diegosouzapw/OmniRoute/pull/13166)) — thanks @anhtahaylove +- **fix(stream):** cancel the upstream response body when the JSON-to-SSE sniff unwinds on a body timeout, so a stalled upstream no longer pins the connection ([#13169](https://github.com/diegosouzapw/OmniRoute/issues/13169)) ([#13171](https://github.com/diegosouzapw/OmniRoute/pull/13171)) — thanks @anhtahaylove +- **fix(telegram):** authenticate webhook deliveries with Telegram's `secret_token` so an unauthenticated caller can no longer mint API keys or spend upstream quota ([#13172](https://github.com/diegosouzapw/OmniRoute/issues/13172)) ([#13175](https://github.com/diegosouzapw/OmniRoute/pull/13175)) — thanks @anhtahaylove +- **fix(models):** give discoverable Gemini 3.8 Flash ids their own 65536 output spec so Antigravity no longer clamps them to 16384 ([#13195](https://github.com/diegosouzapw/OmniRoute/pull/13195)) — thanks @HouMinXi +- **fix(dashboard):** leftover catalog-removed provider rows (gemini-cli) can be listed and purged from the providers page ([#13067](https://github.com/diegosouzapw/OmniRoute/issues/13067)) ([#13197](https://github.com/diegosouzapw/OmniRoute/pull/13197)) — thanks @HouMinXi +- **fix(auth):** a dashboard session now requires the `authenticated: true` claim that login, OIDC and the session refresh already emit — a JWT merely signed with `JWT_SECRET` (for example the Cursor CLI passthrough token, which any API-key holder can obtain) no longer verifies as the `auth_token` cookie on any route, the WebSocket handshake or the live server; existing sessions keep working ([#13298](https://github.com/diegosouzapw/OmniRoute/issues/13298)) ([#13375](https://github.com/diegosouzapw/OmniRoute/pull/13375)) +- **fix(api):** `DELETE /v1/batches/delete-completed` now applies the caller's API-key policy (endpoint allowlist, schedule, usage cap, rate limit) like every other `/v1` route, commits the key-scoped sweep per 200-batch chunk instead of holding one write lock for the whole sweep, refuses to loop without progress, and rejects a scope that names both a key and `allTenants` ([#13684](https://github.com/diegosouzapw/OmniRoute/pull/13684)) +- Restore the API-route typecheck gate: the non-streaming leg result lost its discriminated-union narrowing after the server-owned tool loop reassignment, producing 13 new TS2339 diagnostics in `chatCore.ts`. ([#12963](https://github.com/diegosouzapw/OmniRoute/pull/12963)) +- Restore four non-streaming behaviours the server-owned tool loop refactor dropped: client aborts map to 499 with the fixed `Request aborted` message, an aborted request no longer logs a synthetic `clientResponse`, Claude prompt-cache telemetry is recorded again, and a body that cannot be canonicalized no longer throws when the tool loop is off. ([#12990](https://github.com/diegosouzapw/OmniRoute/pull/12990)) +- **fix(skills):** The CLI registry parser now reads positionals declared with `.addArgument()`, not only those written inline in `.command()`. `tunnel create [type]` was being published as `tunnel create`, so the agent-skills sync gate reported drift on every branch and regenerating would have deleted the argument. ([#13009](https://github.com/diegosouzapw/OmniRoute/pull/13009)) — thanks @ntdatt812 +- **fix(combos):** dashboard combo test uses a short prompt, serial probes, and a 60s timeout so reasoning models and rate-limited free pools do not fail the health check ([#13001](https://github.com/diegosouzapw/OmniRoute/pull/13001)) — thanks @HouMinXi +- fix(compression): terminate idle worker threads on eviction so long-running instances stop leaking OS threads and MessagePorts ([#13091](https://github.com/diegosouzapw/OmniRoute/pull/13091)) — thanks @anhtahaylove +- Redact Google API keys of any length in error bodies: the pattern required exactly 39 characters, so shorter or longer `AIza…` credentials passed through unredacted. ([#12964](https://github.com/diegosouzapw/OmniRoute/pull/12964)) +- **fix(grok-cli):** a 402 "Grok Build usage balance exhausted" parks that Grok login as out of credit (Grok Build CLI, grok.com cookie, and xAI OAuth share the weekly pool). Combo routing then tries the next login instead of locking the model for every account in the pool ([#13061](https://github.com/diegosouzapw/OmniRoute/pull/13061)) — thanks @HouMinXi +- fix(compression): spawn the LLMLingua worker with a file URL object so compression actually runs instead of silently failing open on Node ([#13093](https://github.com/diegosouzapw/OmniRoute/pull/13093)) — thanks @anhtahaylove +- **fix(test):** run the local `test` and `test:unit` scripts at concurrency 4 so a full-suite run no longer exhausts the machine's commit charge and kills unrelated processes ([#13187](https://github.com/diegosouzapw/OmniRoute/pull/13187)) — thanks @anhtahaylove +- **fix(monitoring):** `GET /api/monitoring/health` `credentialHealth` now includes a bounded `failedConnections` list (`connectionId`, `status`, sanitized `lastError`) when the probe-cache gauge `failed>0`, plus `source: probe-cache` and a cheap `staleDbNonOkCount` for sticky SQLite `test_status` on active rows. Documents that the live gauge is not `provider_connections.test_status`. ([#12876](https://github.com/diegosouzapw/OmniRoute/pull/12876)) — thanks @RaviTharuma +- **fix(resilience):** an openai-compatible multi-upstream gateway no longer marks the whole connection `credits_exhausted` when a single upstream (or the `/models` / representative-model health probe) returns 402. Quota failures stay model-scoped; true connection-wide auth failures are unchanged. Single-credential 402 key disable ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239)) is preserved. ([#12875](https://github.com/diegosouzapw/OmniRoute/pull/12875)) — thanks @RaviTharuma +- **fix(dashboard):** nine fixes on the Orchestration Canvas, all diagnosed in the Phase 2 reviews. A source that fails now keeps the timestamp of its FIRST failure instead of being re-stamped every poll — the stale line said "since the last poll" no matter how long the source had been down, and the churn also defeated the snapshot's stable identity (it serializes the source list), so the canvas re-rendered on every tick while anything was broken. A source that HAD data and only then started failing is flagged too: previously only a source with no node at all got the warning, so a source that went down mid-session kept a healthy-looking node forever. The rest are pointwise: clicking a filter chip cancels the pending search debounce (left armed, it fired ~300ms later and silently reverted the chip); the search input carries an accessible name; `?state=running, failed` parses like the unpadded form instead of dropping the padded value; the CSV toggle helper is defined once in `model/urlParams.ts` rather than twice; the agents tab tells "nothing running" apart from "the filter matched nothing", offering a clear-filters button instead of setup links that would be wrong advice there; edges stop emitting SMIL particles above 40 simultaneously active edges, keeping the colored stroke; and the drawer's error banner clears when a retried action succeeds. ([#12983](https://github.com/diegosouzapw/OmniRoute/pull/12983)) +- **fix(i18n):** the nine locales added with the EU-language batch (Greek, Estonian, Irish, Croatian, Lithuanian, Latvian, Maltese, Slovenian, Serbian) were missing the eleven Orchestration Canvas keys that Phase 3 introduced, so the compare-runs panel and the "no runs match these filters" empty state fell back to English in those languages (`deepMergeFallback` substitutes English for an absent key, so nothing rendered blank — it rendered untranslated). The coverage gate does not catch this: it enforces an 80% floor per locale, and eleven missing keys out of ~13,000 leaves coverage at 99.9%. Translated for real in each language, calibrated against the wording each file already uses for "run", "filter" and "skill". ([#13294](https://github.com/diegosouzapw/OmniRoute/pull/13294)) +- **fix(dashboard):** five follow-ups from the Phase 2 Orchestration Canvas review. The History drawer now shows the memory section for runs that already left the live TTL window: the persisted `memory_hits` event is parsed into `metadata.memoryHits` with the same defensive validation the drawer applies, and — because that event is observability rather than a state transition — it no longer leaks into the timeline, where it had been inheriting the task's state and rendering as a duplicate transition. The A2A memory recall runs against its own 1.5s deadline instead of inheriting the memory backend's 30s one; overshooting degrades exactly like any other recall failure (no hits, task proceeds), and the timer is cleared on both paths. Repeating a Conductor run carries its `requirements.cli`/`requirements.model` forward, so the new run is pinned to the same runner profile and model rather than drifting to whatever the fleet picks. A successful repeat from the Agents tab now focuses the run it created, instead of leaving the operator on the finished one. And the auth test for `POST /api/conductor/tasks` moved into the shared `ROUTES` array rather than restating the pattern. ([#12988](https://github.com/diegosouzapw/OmniRoute/pull/12988)) +- fix(plugins): stop leaking an exit listener per plugin hook timeout, which triggered MaxListenersExceededWarning on plugins that ignore SIGTERM ([#13092](https://github.com/diegosouzapw/OmniRoute/pull/13092)) — thanks @anhtahaylove +- **fix(validation):** Provider node edits no longer fail with a generic "Invalid request" when the optional daily-quota reset fields are left blank. The dashboard sends `dailyQuotaResetTimezone` and `dailyQuotaResetHour` as `null`, and only the hour accepted it. ([#13066](https://github.com/diegosouzapw/OmniRoute/issues/13066)) ([#13083](https://github.com/diegosouzapw/OmniRoute/pull/13083)) — thanks @ntdatt812 +- **fix(combo):** quota-aware expansion drops banned, inactive, missing, and wrong-provider connections before quota fetch or model dispatch; pins and allowlists stay selectors, not a bypass. Antigravity automatic exhaustion now requires a reported zero remaining, so a positive balance below 1% stays eligible. ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- Fix an intermittent failure in the quota-weighted routing test suite: scores are a function of `Date.now()`, so peers with identical quota scored microseconds apart never tied and swapped order. ([#12946](https://github.com/diegosouzapw/OmniRoute/pull/12946)) +- fix(volcengine): resolve quota window mapping gaps, safe multi-connection console binding, and response cookie sanitization ([#12950](https://github.com/diegosouzapw/OmniRoute/pull/12950)) — thanks @HouMinXi +- **fix(test):** resolve the WebDAV handler path with `fileURLToPath` so the suite's 37 WebDAV tests run on Windows instead of failing with a doubled `C:\C:\` drive prefix ([#13196](https://github.com/diegosouzapw/OmniRoute/pull/13196)) — thanks @anhtahaylove +- **fix(ui):** fall back from invalid AnySearch icon alias (direct commit `9587e07b69`) +- **fix(sse):** stop rebuilding a truncated summary from the collector's cap-dropped event array ([#12718](https://github.com/diegosouzapw/OmniRoute/pull/12718)) — thanks @hartmark +- **fix(conversations):** show a pending spinner for unresolved tool nodes instead of "(empty)" ([#12727](https://github.com/diegosouzapw/OmniRoute/pull/12727)) — thanks @hartmark +- **fix(streaming):** fail fast when an upstream stream produces only lifecycle/heartbeat events, never real content ([#12741](https://github.com/diegosouzapw/OmniRoute/pull/12741)) — thanks @hartmark +- **fix(responses-continuation):** bridge the write-in-flight window with an in-memory pending store ([#12854](https://github.com/diegosouzapw/OmniRoute/pull/12854)) — thanks @hartmark +- **fix(translator):** drop or map agent_message on Chat Completions fallback ([#12880](https://github.com/diegosouzapw/OmniRoute/pull/12880)) — thanks @RaviTharuma +- **fix(api):** page and stream-complete GET /v1/models for large catalogs ([#12882](https://github.com/diegosouzapw/OmniRoute/pull/12882)) — thanks @RaviTharuma +- **fix(quota):** align AUTH window labels with usage API and clarify 503 ([#12884](https://github.com/diegosouzapw/OmniRoute/pull/12884)) — thanks @RaviTharuma +- **fix(ci):** clear two base-reds on release/v3.8.51 — mutation-coverage gate + image-only-model guard ([#12945](https://github.com/diegosouzapw/OmniRoute/pull/12945)) +- **fix(api):** scope batch bulk-delete to the calling API key ([#13211](https://github.com/diegosouzapw/OmniRoute/pull/13211)) +- **fix(tests):** align model-sync log assertion to shipped path redaction (base-red) ([#13227](https://github.com/diegosouzapw/OmniRoute/pull/13227)) +- **fix(test):** make the ToS heading guard actually require the parentheses ([#13228](https://github.com/diegosouzapw/OmniRoute/pull/13228)) +- **fix(quality):** register 4 drifted covering tests in stryker tap.testFiles ([#13229](https://github.com/diegosouzapw/OmniRoute/pull/13229)) +- **fix(logging):** mask *-api-key headers in request-log pipeline (#13273) ([#13401](https://github.com/diegosouzapw/OmniRoute/pull/13401)) — thanks @KooshaPari +- **fix(tests):** correct stale Jina catalog prefix in custom-models test ([#13403](https://github.com/diegosouzapw/OmniRoute/pull/13403)) — thanks @KooshaPari +- **fix(backend):** prune old backups after health-check-repair snapshots (#13308) ([#13404](https://github.com/diegosouzapw/OmniRoute/pull/13404)) — thanks @KooshaPari +- **fix(backend):** guard memory_fts_au trigger with WHEN clause to stop FTS bloat (#13326) ([#13405](https://github.com/diegosouzapw/OmniRoute/pull/13405)) — thanks @KooshaPari +- **fix(free-tiers):** remove obsolete Together $25 signup credit (#12526) ([#13407](https://github.com/diegosouzapw/OmniRoute/pull/13407)) — thanks @KooshaPari +- **fix:** skip non-chat generation models in Test all models ([#13409](https://github.com/diegosouzapw/OmniRoute/pull/13409)) — thanks @KooshaPari +- **fix(providers):** de-duplicate agy and antigravity model catalogs ([#13410](https://github.com/diegosouzapw/OmniRoute/pull/13410)) — thanks @KooshaPari +- **fix(dashboard):** send provider in body for mini-playground web search ([#13411](https://github.com/diegosouzapw/OmniRoute/pull/13411)) — thanks @KooshaPari +- **fix(resilience):** add CJK quota-exhaustion patterns to 429 classifier ([#13412](https://github.com/diegosouzapw/OmniRoute/pull/13412)) — thanks @KooshaPari +- **fix(backend):** eval runner: failed calls no longer score as passed + dotAll regex ([#13413](https://github.com/diegosouzapw/OmniRoute/pull/13413)) — thanks @KooshaPari +- **fix(backend):** eval runner opts out of output-style and memory injection ([#13414](https://github.com/diegosouzapw/OmniRoute/pull/13414)) — thanks @KooshaPari +- **fix:** chain serial quarantine step in npm test script (fixes #13286) ([#13418](https://github.com/diegosouzapw/OmniRoute/pull/13418)) — thanks @KooshaPari +- **fix(antigravity):** derive stable session ID from credentials for prompt-cache reuse (#13305) ([#13424](https://github.com/diegosouzapw/OmniRoute/pull/13424)) — thanks @KooshaPari +- **fix(memory):** merge memory into top-level system field instead of unshifting at messages[0] (#13425) ([#13427](https://github.com/diegosouzapw/OmniRoute/pull/13427)) — thanks @KooshaPari +- **fix(compression):** preserve instruction blocks from lossy engines ([#13523](https://github.com/diegosouzapw/OmniRoute/pull/13523)) — thanks @KooshaPari +- **fix(compression):** preserve polarity words and newlines in ultra heuristic ([#13524](https://github.com/diegosouzapw/OmniRoute/pull/13524)) — thanks @KooshaPari +- **fix(compression):** run user preservePatterns before built-in patterns (#13457) ([#13525](https://github.com/diegosouzapw/OmniRoute/pull/13525)) — thanks @KooshaPari +- **fix(reasoning):** expand deepseek-v4 regex to match v4.1+ and add command-code provider (#13430) ([#13528](https://github.com/diegosouzapw/OmniRoute/pull/13528)) — thanks @KooshaPari +- **fix(providers):** add alwaysReasons metadata for reka-flash-3 ([#13531](https://github.com/diegosouzapw/OmniRoute/pull/13531)) — thanks @KooshaPari +- **fix(playground):** allow Compare columns to scroll independently ([#13532](https://github.com/diegosouzapw/OmniRoute/pull/13532)) — thanks @KooshaPari +- **fix(security):** empty whitelist allows through to prevent admin lockout ([#13534](https://github.com/diegosouzapw/OmniRoute/pull/13534)) — thanks @KooshaPari +- **fix(tests):** add DISABLE_IOREG_STRATEGY env var for macOS test compatibility (fixes #13467) ([#13539](https://github.com/diegosouzapw/OmniRoute/pull/13539)) — thanks @KooshaPari +- **fix(zed-hosted):** map developer role and Gemini enums for Zed proxy ([#13543](https://github.com/diegosouzapw/OmniRoute/pull/13543)) — thanks @KooshaPari +- **fix(analytics):** normalize provider aliases to canonical id in usage_history ([#13545](https://github.com/diegosouzapw/OmniRoute/pull/13545)) — thanks @KooshaPari +- **fix(combo):** use traceId for call log id to prevent UNIQUE constraint failure ([#13546](https://github.com/diegosouzapw/OmniRoute/pull/13546)) — thanks @KooshaPari +- **fix(auth):** check model lockout before returning synthetic noauth connection ([#13547](https://github.com/diegosouzapw/OmniRoute/pull/13547)) — thanks @KooshaPari +- **fix(guardrails):** skip credential redaction for base64 image data URLs ([#13550](https://github.com/diegosouzapw/OmniRoute/pull/13550)) — thanks @KooshaPari +- **fix(resilience):** increase requestQueue.maxWaitMs default from 15s to 30s ([#13553](https://github.com/diegosouzapw/OmniRoute/pull/13553)) — thanks @KooshaPari +- **fix(docs):** document OMNIROUTE_READY_TIMEOUT_MS and allowlist the test-only DISABLE_IOREG_STRATEGY ([#13692](https://github.com/diegosouzapw/OmniRoute/pull/13692)) + ### 📝 Maintenance ---- +- **docs(readme):** reconcile live v3.8.50 provider, free-tier, CLI, routing, test, community, sponsor, acknowledgment, and SVG metrics with their audited source denominators, including a deduplicated OmniRoute-in-Action snapshot and distinct contributor rankings for merged pull requests, GitHub-attributed commits, and Git history ([#11356](https://github.com/diegosouzapw/OmniRoute/pull/11356)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **docs:** sync the canonical provider count 354 → 356 across `README.md`, `AGENTS.md`, `llm.txt` (+ 42 i18n mirrors), the four README SVG diagrams, `docs/reference/PROVIDER_REFERENCE.md` (regenerated) and the `package.json` description after Opper (#11629) and 1min.ai (#11631) boarded the catalog — closes the `check:docs-counts-sync` strict drifts that kept `release/v3.8.51` red ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11673](https://github.com/diegosouzapw/OmniRoute/pull/11673)) — thanks @jonlwheat2-gif +- **test(providers):** update count-derived assertions after the v3.8.51 provider additions — `APIKEY_PROVIDERS` 233 → 235 (Opper #11629 + 1min.ai #11631), reserved-prefix REGISTRY walk 395 → 398, `WEB_FETCH_PROVIDERS` now includes `nimble-search` (#11620), and the provider translate-path golden snapshot regenerated ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11674](https://github.com/diegosouzapw/OmniRoute/pull/11674)) — thanks @jonlwheat2-gif +- **test(sse):** bump the hard-lease connection-query inventory for `src/lib/tokenHealthCheck.ts` to 2 — the verify-only web-cookie sweep added by #11495 split the single `getProviderConnections` call into oauth + cookie variants, which the frozen inventory had not tracked ([#11495](https://github.com/diegosouzapw/OmniRoute/pull/11495)). ([#11677](https://github.com/diegosouzapw/OmniRoute/pull/11677)) — thanks @jonlwheat2-gif / @oyi77 +- **chore(release):** clear the `release/v3.8.51` base-red gates — docs migration count (+38 i18n mirrors), stryker `tap.testFiles`, lockfile registry host, stale ESLint suppressions, the 7 lint regressions they were hiding, the stale `APIKEY_PROVIDERS` partition count (231 → 233), the `glm-5.3-max` tier missing from the GLM test table, a dead `modelType === "chat"` comparison in the models catalog, and the `resolveLiveWsUrl` / `sanitizeLiveWsPort` helpers from #11388 that a later merge dropped from `wsPath.ts` (dashboard live WebSocket URL resolution) ([#11502](https://github.com/diegosouzapw/OmniRoute/pull/11502)) — thanks @MumuTW +- **chore(free-models):** the free-tier regime table now declares which totals figure each regime feeds and whether it may take the no-auth shortcut, so the sets that used to repeat those answers by hand are derived from it — a new `freeType` no longer compiles until it has answered every question, instead of silently contributing to no total ([#11537](https://github.com/diegosouzapw/OmniRoute/pull/11537)) — thanks @maxmad64bis +- **docs(routing):** LKGP strategy copy now states provider-level stickiness and the explicit rules fallback instead of promising per-target pinning ([#11555](https://github.com/diegosouzapw/OmniRoute/pull/11555)) — thanks @maxmad64bis +- **test(chatcore):** pin the response tool-name alias resolution against the ordering hazard that broke every Gemini/Antigravity MCP tool call in v3.8.49. `extractRequestToolIdentityMap` deletes `translatedBody._toolNameMap`, so the later read is always undefined and the ledger survives only through the `requestToolIdentityMap` fallback — removing that fallback previously left the entire tool-name suite green. The resolution moves into `resolveResponseToolNameMap()` next to the map it depends on, with a regression guard that fails without the recovery. No behaviour change. ([#11575](https://github.com/diegosouzapw/OmniRoute/pull/11575)) — thanks @amartinawi +- **docs(env):** document `OMNIROUTE_CORPUS_CACHE_SIZE` in `.env.example` and `ENVIRONMENT.md`, so the env/docs contract check stops failing Docs Gates on every branch ([#11576](https://github.com/diegosouzapw/OmniRoute/pull/11576)) — thanks @ntdatt812 +- **chore(stryker):** register `route-guard-tunnel-processes-local-only` and `universal-quota-aware-routing` in `tap.testFiles`, so their mutant kills count for the three modules they cover ([#11577](https://github.com/diegosouzapw/OmniRoute/pull/11577)) — thanks @ntdatt812 +- **test(authz):** pin `LOCAL_ONLY_API_GET_EXEMPTIONS` by exact membership instead of by entry count, so the guard names the offending path and also catches a substitution ([#11580](https://github.com/diegosouzapw/OmniRoute/pull/11580)) — thanks @ntdatt812 +- **test(build):** pin `resolveNextBuildBundlerFlag` to the `OMNIROUTE_USE_TURBOPACK` contract it implements, across runtimes, instead of asserting an unimplemented Bun override ([#11583](https://github.com/diegosouzapw/OmniRoute/pull/11583)) — thanks @ntdatt812 +- **test(claude-web):** the Claude Web transport test's barrel anchors no longer pin the exact construction spelling. #11421 made the executor map lazy, so `/"claude-web": new ClaudeWebExecutor\(\)/` stopped matching and the test went red on `release/v3.8.51` itself — a base-red gate every open PR inherited. The anchors now match the alias key and the class name and tolerate either spelling in between, and were checked to still fail when an alias is genuinely repointed at another executor ([#11585](https://github.com/diegosouzapw/OmniRoute/pull/11585)) — thanks @ntdat812 +- **test(models):** pin `qwen3.8-max` resolution per provider — two of the four catalogs now serve the bare id and no longer carry `-preview` ([#11590](https://github.com/diegosouzapw/OmniRoute/pull/11590)) — thanks @ntdatt812 +- **test(executors):** drop the stale 128-tool truncation assertion that contradicted the #11444 pass-through contract ([#11591](https://github.com/diegosouzapw/OmniRoute/pull/11591)) — thanks @ntdatt812 +- **test(token-health):** pin the current terminal-skip boundary — expired with retry budget is probed, `account_deactivated` stays skipped ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)) — thanks @ntdatt812 +- **test(models):** follow #11503's retarget of the `gemini-3-pro-high` alias to the hyphenated catalog id ([#11593](https://github.com/diegosouzapw/OmniRoute/pull/11593)) — thanks @ntdatt812 +- **refactor(video bridge):** extract per-video acquisition, whole-result caching, description, and metrics/abort/cleanup out of `VideoBridgeGuardrail.preCall` into a `processVideoPart` seam in a new `videoBridgePipeline.ts`, behind explicit `VideoMediaBrokerPort`, `VideoAudioTranscriptionPort`, and `VideoDrilldownPort` boundaries; `preCall` now only handles request traversal, policy, and response aggregation. The Video tab's FFmpeg/ffprobe runtime status is now an explicit `unknown` / `restricted` / `unavailable` / `available` state instead of a nullable boolean pair, fixing a case where an in-flight or failed probe was mislabeled as "install FFmpeg" ([#11657](https://github.com/diegosouzapw/OmniRoute/issues/11657)). ([#12016](https://github.com/diegosouzapw/OmniRoute/pull/12016)) +- **test(opencode-plugin):** add unit test coverage for `formatFreeBudget()` naming helper ([#11660](https://github.com/diegosouzapw/OmniRoute/pull/11660)) — thanks @f9td56dbgh-hub +- **docs(video):** clarify that the Video Bridge transcript `source` field (`client`, `embedded`, `audio-bridge`) is presently caller-declared and not yet server-verified — OmniRoute enforces the enum shape but does not cryptographically confirm that an `embedded`/`audio-bridge` label came from a server-owned extraction ([#11661](https://github.com/diegosouzapw/OmniRoute/issues/11661)). ([#12001](https://github.com/diegosouzapw/OmniRoute/pull/12001)) +- **chore(providers):** retire the distributed Raycast Relay and Hailuo Web integrations after a provenance audit confirmed substantial derivation from GPL-3.0 sources; the official GitHub and MiniMax API providers remain available. Migration 163 disables legacy connections and invalidates their active leases while preserving connection IDs, audit/history rows, quota snapshots, usage records, and fail-closed API-key connection policies. ([#11691](https://github.com/diegosouzapw/OmniRoute/pull/11691)) +- **chore(providers):** retire the Felo Web provider while its GPL-derived provenance remains on licensing HOLD ([#11698](https://github.com/diegosouzapw/OmniRoute/pull/11698)) — stale runtime and database references now fail closed while existing connection, lease, and usage history remains preserved for auditability. +- **fix(sse):** retire the Gemini Web image-generation bridge while provenance and licensing remain on HOLD, preserving Gemini Web chat/auth/tools and legitimate Gemini/Nano Banana image providers ([#11708](https://github.com/diegosouzapw/OmniRoute/pull/11708)) +- **chore(audio):** retire the built-in EdgeTTS provider and its 11-voice catalog while provenance/licensing review remains on HOLD; the generic `/v1/audio/speech` endpoint and the other speech providers remain available ([#11711](https://github.com/diegosouzapw/OmniRoute/pull/11711)) +- **fix(providers):** retire the unofficial Qwen Web integration and its `qwen-web`/`qw` identities while third-party provenance remains on HOLD ([#11713](https://github.com/diegosouzapw/OmniRoute/pull/11713)) — direct requests now fail closed with a sanitized HTTP 410 and combos skip the retired target, while migration guards preserve connection IDs, allowlists, usage/audit history and invalidate active leases. Supported Qwen API, Qwen Code, cloud, token-plan, audio, local and model-name surfaces remain available; restoring Qwen Web requires a qualified provenance/license decision. +- **fix(providers):** retire `microsoft-designer-web` and its `msdesigner` alias from runtime and catalogs while preserving stored credentials, provider history and allowlists as inactive records — source dependency on gpt4free's [`MicrosoftDesigner.py`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/g4f/Provider/needs_auth/MicrosoftDesigner.py) is proven at snapshot `0943d16eef757a44deb60651c07bbe5fa958cafe`, whose [`LICENSE`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/LICENSE) is GPL-3.0; expressive adaptation is probable, which records provenance/licensing risk without asserting a legal conclusion ([#11720](https://github.com/diegosouzapw/OmniRoute/pull/11720)). +- **docs(legal):** centralize pinned MIT notices for the vendored GCF codec and local flag assets ([#11726](https://github.com/diegosouzapw/OmniRoute/pull/11726)) +- Split the 2,952-line `adobeFireflyClient.ts` into nine focused modules (catalog, credentials, payload, ARP session, upload, responses, IMS, account, poll) behind an unchanged barrel — no consumer edits and no public API change (#11728). — thanks @MumuTW +- **fix(dashboard):** replaced the Model Lockout card's unprovenanced notification MP3 with a short, locally synthesized Web Audio chime ([#11731](https://github.com/diegosouzapw/OmniRoute/pull/11731)) — both toggles retain optional audible feedback without shipping replacement media or third-party code, and unsupported, suspended, or rejected audio contexts fail open without blocking the setting change. +- **chore(deepseek-web):** remove the provenance-unresolved DeepSeek PoW WASM binary and its runtime loader/tracing while retaining the existing JavaScript solver (slower at high difficulty) ([#11732](https://github.com/diegosouzapw/OmniRoute/pull/11732)). +- **chore(assets):** remove four unused provider assets whose redistribution provenance could not be verified while retaining the active Freebuff SVG ([#11735](https://github.com/diegosouzapw/OmniRoute/pull/11735)) +- **docs(assets):** retired two raster images whose redistribution provenance could not be verified ([#11736](https://github.com/diegosouzapw/OmniRoute/pull/11736)) — PicoClaw documentation now reuses the existing SVG, while Zoo Code remains a text link without an image. +- **docs(legal):** add pinned notices for six LobeHub-derived provider SVGs and 65 byte-exact theSVG assets, retaining registry claims as unverified metadata with explicit trademark and HOLD boundaries ([#11737](https://github.com/diegosouzapw/OmniRoute/pull/11737)). +- **chore(quality):** added a versioned 225/225 provider-asset provenance manifest and blocking drift gate ([#11743](https://github.com/diegosouzapw/OmniRoute/pull/11743)) — the inventory pins magic MIME and SHA-256 for every physical file, records immutable source matches for 71 assets, keeps 69 probable and 85 unresolved entries explicit but non-blocking, and treats provenance as source evidence rather than copyright or trademark clearance. +- **refactor(mitm):** replace four previously derived Traffic Inspector utilities — conversation normalization, SSE merging, secret masking, and header sanitization — with independent clean-room implementations based on public provider schemas, WHATWG SSE, and RFC 6750, while keeping the two independently classified passthrough surfaces unchanged ([#11748](https://github.com/diegosouzapw/OmniRoute/pull/11748)). +- Added a unit test that fails seven days before any dated pack under `config/` (`validUntil` and sibling keys) lapses, naming the file and key. The Alibaba free-tier pack expired on 2026-08-27 and turned every PR red the next morning with no commit involved; renewal now happens on someone's terms, not the clock's. ([#11891](https://github.com/diegosouzapw/OmniRoute/pull/11891)) +- `check:workflows` now fails (under `--strict`/`--ratchet`) when any job routed to a self-hosted runner publishes with `--provenance` — npm rejects that with `422` at the registry, which in v3.8.50 only surfaced after the tag and Docker images were public. ([#11895](https://github.com/diegosouzapw/OmniRoute/pull/11895)) +- `scripts/ops/runner-janitor.sh` now proves a path is idle with one `lsof` snapshot and removes stale leftovers itself (tmpfs after 3 h — it is RAM — disk after 24 h), kills orphan `next-build` processes, prunes checkouts of stopped runners, and alerts on memory pressure; `--dry-run` shows exactly what it would do. `docs/ops/RUNNER_BOX.md` reconciled to the measured box (31 GB, 10 listeners). ([#11893](https://github.com/diegosouzapw/OmniRoute/pull/11893)) +- The `next-build` artefact (1.3 GB) is now written and read under `$RUNNER_TEMP` (per-runner, on disk) instead of `/tmp`, which on the self-hosted pool is a 12 GB tmpfs in RAM. Landing it there took 27–32 of the publish job's 76 minutes, and the fixed `/tmp/e2e-build.tar.gz` name let E2E jobs on different runners overwrite each other's download. ([#11896](https://github.com/diegosouzapw/OmniRoute/pull/11896)) +- The CI `build` job now runs in two concurrency lanes — `main` and pull requests — so a release build is never queued behind (or OOM-killed beside) PR builds on the self-hosted pool, which holds one `next-build` comfortably and two at the edge. ([#11901](https://github.com/diegosouzapw/OmniRoute/pull/11901)) +- Re-freeze the ESLint suppressions on `release/v3.8.51` from a clean-room measurement (2 stale file entries pruned, 55 pre-existing `no-explicit-any` in six new files frozen under #11924) and drop the dead `GPT_SIZE_MAP` constant orphaned by the Adobe Firefly client split, so `No new ESLint warnings` stops failing every PR with exit 2 (Refs #11924) ([#11955](https://github.com/diegosouzapw/OmniRoute/pull/11955)) +- Type the 55 `no-explicit-any` sites that had been frozen under #11924 — four redundant casts in `socksConnectorWithFamily.ts` (undici/socks types already accept them) and the mocks/fixtures of the socks-timeout and isFree suites — and drop their suppression entries; the ESLint ratchet shrinks from 5487 to 5432 (Closes #11924) ([#11975](https://github.com/diegosouzapw/OmniRoute/pull/11975)) +- Take the two hosted-runner builds off the PR rail: `Build App` (`build.yml`) is `workflow_dispatch`-only and `DAST smoke (PR)` runs only for PRs into `main` — the 7 GB hosted VM cannot build `release/v3.8.51` in any profile (19/30 red, VM shutdown ~8 min into `next build`) and both checks had turned into permanent noise on every release PR; the bundle stays validated by `ci.yml` on `main` and by `nightly-release-green` on `release/**` (Closes #11946) ([#11962](https://github.com/diegosouzapw/OmniRoute/pull/11962)) +- Move the four nightly jobs that build the backend (`nightly-schemathesis`, `nightly-llm-security` promptfoo + garak, `nightly-resilience` axe-a11y) off the hosted 7 GB runner — where they died on `release/v3.8.51` unseen — onto the box's new `omni-light` pool (two listeners), and document the reshaped fleet (4 active OmniRoute listeners: 2 `omni-build` + 2 `omni-light`, janitor ceiling 4) (Closes #11965) ([#11967](https://github.com/diegosouzapw/OmniRoute/pull/11967)) +- Stop painting every fork PR into `release/**` red: `quality.yml` `Build (advisory)` is skipped (GitHub still reports `continue-on-error` failures as check FAILURE). Hosted `ubuntu-latest` cannot finish `npm run build` on this tree — same class as #11962 taking `build.yml` off the PR rail. `docker-publish.yml` amd64 now runs on the `.113` `omni-build` pool (31 GB / 32 cores, two listeners) with Turbopack, shares the `heavy-build-main` lane with `ci.yml` `Build` so it queues instead of becoming a third heavy, and keeps per-ref concurrency (a merge storm was starting 8 concurrent OOM builds). arm64 stays on `ubuntu-24.04-arm` with webpack — there is no ARM box. Fallback when `USE_VPS_RUNNER` is off: hosted amd64 + webpack (#11976). ([#12021](https://github.com/diegosouzapw/OmniRoute/pull/12021)) +- `docker-publish.yml` builds with webpack (`OMNIROUTE_USE_TURBOPACK=0`) on **both** arches, including amd64 on the `.113` `omni-build` pool. Turbopack had the RAM (31 GB, `omniroute-113-6`) and still panicked (`TurbopackInternalError: there must be a path to a root`, run 33253576569, 26 min in). The same tree's arm64 webpack build on hosted `ubuntu-24.04-arm` succeeded (run 33264823398). Dockerfile already documents webpack as the Docker escape hatch (#11976). ([#12050](https://github.com/diegosouzapw/OmniRoute/pull/12050)) +- **docs(free-tier):** declare the counting vs deciding regimes for "is it free?" and guard the deciding path from DB-backed catalog resolution ([#12226](https://github.com/diegosouzapw/OmniRoute/pull/12226)) — thanks @maxmad64bis +- **docs(auto-combo):** The mode pack table in `docs/routing/AUTO-COMBO.md` now lists all six shipped packs with every weight each one sets, replacing a four-pack table whose numbers had also drifted from the source. It states plainly that no pack sets `quality`, so selecting any pack silences the observed-quality signal. Six more documents that quote the scoring factor count joined the `check:docs-counts` gate, which caught five stale claims — including one naming nine factors that do not exist — and two stale mode pack counts. The dashboard routing panel, which offered four of the six packs and labelled the default strategy "6-Factor Scoring", is now covered by a test; the two packs it was missing are `reliability-first` and `chaos-mode`, the latter labelled as the fault-injection profile it is rather than as one more routing preference ([#12316](https://github.com/diegosouzapw/OmniRoute/pull/12316)) — thanks @maxmad64bis +- **chore(quality):** rebaseline `src/lib/db/apiKeys.ts` for the ACL the key-creation path now preserves ([#12352](https://github.com/diegosouzapw/OmniRoute/pull/12352)) ([#12673](https://github.com/diegosouzapw/OmniRoute/pull/12673)) — thanks @KrzysiekSko +- **chore(providers):** bump the Claude Code wire identity and the Devin bridge image pin from `2.1.220` to `2.1.258` ([#12402](https://github.com/diegosouzapw/OmniRoute/pull/12402)) — thanks @ggiak ([#12604](https://github.com/diegosouzapw/OmniRoute/pull/12604)) +- **docs(env):** align `.env.example`, the README Bun section, and the troubleshooting guide with the code: `OMNIROUTE_USE_TURBOPACK` also governs `npm run build` (not dev-only), `bun run build` follows that flag instead of auto-selecting Webpack, `OMNIROUTE_CHAT_MAX_HEAVY_IN_FLIGHT` is unset by default (no request-count cap), and the structural `503 chat_admission_busy` message matches `chatAdmissionResponses.ts` (#12404 — thanks @pacocartones) +- **chore(electron):** upgrade the desktop app to Electron 44 (Chromium 152, Node 24.18.1) ([#12217](https://github.com/diegosouzapw/OmniRoute/pull/12217)). **Requires macOS 13 (Ventura) or later** — Chromium dropped macOS 12 (Monterey), so Monterey users must stay on an earlier OmniRoute desktop build. Windows and Linux are unaffected; the app already shipped only x64/arm64, so Electron 44 dropping 32-bit builds changes nothing. Removes the `openAsHidden`/`wasOpenedAsHidden` login-item fields deleted in Electron 44 — hidden autostart continues to work through the `--hidden` argument registered with the login item ([#12554](https://github.com/diegosouzapw/OmniRoute/pull/12554)) — thanks @app/dependabot +- **chore(quality):** rebaseline `src/sse/handlers/chat.ts` for the effective-input persistence the continuation fix needs ([#12641](https://github.com/diegosouzapw/OmniRoute/pull/12641)) ([#12680](https://github.com/diegosouzapw/OmniRoute/pull/12680)) — thanks @hartmark +- `Coverage` job on `ci.yml`: the informational Codecov upload gets its own 5-minute ceiling and `continue-on-error`, and the job budget grows from 20 to 30 minutes (the 8-shard c8 merge alone takes ~10) — a stalled upload no longer ends the job `cancelled` and drags a fully green `main` run's conclusion down with it ([#11972](https://github.com/diegosouzapw/OmniRoute/pull/11972)) +- CI hardening on the PR rail: the four `Unit Tests fast-path` shards get `timeout-minutes: 30` (a hung shard held a PR for 64 min instead of GitHub's 6 h default) and both ESLint file caches lose their `restore-keys` fallback, so a cache built under another suppressions file or lint config can no longer report stale verdicts (Refs #11600, #11924) ([#11963](https://github.com/diegosouzapw/OmniRoute/pull/11963)) +- Every CI job that runs a `next build` (`build`, the npm `publish`, both release-green validations) now targets the `omni-build` runner label, which only two of the eight self-hosted runners carry. The box holds one build comfortably and two at the edge; a third now queues on GitHub instead of being OOM-killed by the kernel. ([#11932](https://github.com/diegosouzapw/OmniRoute/pull/11932)) +- **refactor(combo):** move `handleRoundRobinCombo` (and `resolveTargetTokenLimit`) into `open-sse/services/combo/roundRobinCombo.ts`. `combo.ts` drops from 2164 to 1014 lines (`split("\n").length`); the leaf is 1199 (under the 1200 new-file cap). The round-robin call site uses a dynamic `import()` so `releaseStickyPinOnFailure` / `clearStaleLKGP` can stay exported from `combo.ts` without a static cycle. Skip / sticky / semaphore / safety-timer behavior is unchanged. ([#12811](https://github.com/diegosouzapw/OmniRoute/pull/12811)) — thanks @HouMinXi +- **chore(quality):** rebaseline the file-size caps the error-boundary campaign grew past (`open-sse/executors/codex.ts`, `open-sse/vendor/codex-chatgpt-web/bridge.ts`, both via [#12444](https://github.com/diegosouzapw/OmniRoute/pull/12444)) ([#12654](https://github.com/diegosouzapw/OmniRoute/pull/12654)) +- **chore(quality):** rebaseline the file-size caps the hartmark batch grew past (`combos/page.tsx` via [#12355](https://github.com/diegosouzapw/OmniRoute/pull/12355), `open-sse/services/combo.ts` via [#12338](https://github.com/diegosouzapw/OmniRoute/pull/12338)) ([#12623](https://github.com/diegosouzapw/OmniRoute/pull/12623)) — thanks @hartmark +- **chore(quality):** rebaseline the file-size caps the HouMinXi batch grew past when its PRs stacked (`providers/page.tsx`, `chatCore.ts`, `accountFallback.ts`) — each PR measured correctly in isolation, none saw the stacking ([#12619](https://github.com/diegosouzapw/OmniRoute/pull/12619)) +- **chore(quality):** rebaseline `open-sse/services/combo.ts` for the reset-aware scoring the HouMinXi batch stacked ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) ([#12678](https://github.com/diegosouzapw/OmniRoute/pull/12678)) — thanks @HouMinXi +- **docs:** sync provider catalog count (direct commit `4f11b2ae3d`) +- **docs(skills):** sync omni-inference search fallback (direct commit `b4e76a7ed9`) +- **chore(lint):** prune two stale base suppressions (direct commit `1325047d56`) +- **test(auto-combo):** tolerate full-suite rotation load (direct commit `ad3e293f9f`) +- **test:** drain inherited quality snapshot reds (direct commit `21ed68d8ac`) +- **chore(quality):** rebaseline measured CLI bundle growth (direct commit `d35b3f9779`) +- **test(ui):** reproduce invalid AnySearch icon alias (direct commit `12cc6ca834`) +- **test(translator):** fix the relative imports of the relocated deferred-finish test (direct commit `777d9d1629`) +- **docs(counts):** bring the documented provider and migration counts to the measured values (direct commit `e70bea30e9`) +- **test(security):** drop the guard-target set left unused by the sync (direct commit `a32ba0e362`) +- **chore(db):** renumber model_capabilities migration 163 → 169 (cross-branch collision with 163_radar_feed_cache_generated_at) (direct commit `70f5d4cbf9`) +- **docs(cli):** document CLI_PRIME_AGENT_BIN and correct the CLI Agents count (direct commit `9058e39b61`) +- **chore(deps):** refresh runtimes and adopt ESLint 10 ([#11259](https://github.com/diegosouzapw/OmniRoute/pull/11259)) — thanks @backryun +- **refactor(local-corpus):** implement dynamic root resolution and LRU cache ([#11491](https://github.com/diegosouzapw/OmniRoute/pull/11491)) — thanks @raheemuddin786 +- **chore(quality):** prune stale eslint-suppressions entries ([#11596](https://github.com/diegosouzapw/OmniRoute/pull/11596)) +- **chore(ci):** unblock quality gates - env-doc sync + stryker tap.testFiles registration (#9282) ([#11609](https://github.com/diegosouzapw/OmniRoute/pull/11609)) — thanks @Meet6338-X +- **chore(ci):** point the circular-deps gate at dpdm's real JS entrypoint ([#11615](https://github.com/diegosouzapw/OmniRoute/pull/11615)) — thanks @benzntech +- **chore(quality):** rebaseline file-size for #11448/#11495/#11561 growth ([#11630](https://github.com/diegosouzapw/OmniRoute/pull/11630)) +- **deps:** realign onnxruntime-node pin to @huggingface/transformers' 1.24.3 lockstep ([#11633](https://github.com/diegosouzapw/OmniRoute/pull/11633)) — thanks @jonlwheat2-gif +- **test(autoCombo):** port models_dev_tier #11508 guard from node:test to vitest ([#11635](https://github.com/diegosouzapw/OmniRoute/pull/11635)) — thanks @jonlwheat2-gif +- **chore(quality):** rebaseline file-size for #11083/#11631/#11628 growth ([#11636](https://github.com/diegosouzapw/OmniRoute/pull/11636)) +- **chore(lint):** prune stale ESLint suppressions (base-red #11449) ([#11644](https://github.com/diegosouzapw/OmniRoute/pull/11644)) — thanks @jonlwheat2-gif +- **docs(changelog):** aggregate the ten v3.8.50 fragments into CHANGELOG.md ([#11683](https://github.com/diegosouzapw/OmniRoute/pull/11683)) +- **chore(search):** drop unreachable credential sweep after #11642 ([#11687](https://github.com/diegosouzapw/OmniRoute/pull/11687)) — thanks @HouMinXi +- **ci:** add API route TypeScript regression gate ([#11705](https://github.com/diegosouzapw/OmniRoute/pull/11705)) — thanks @Sabeekhann +- **docs(dedup):** correct the CodeQL claim on the tenant-namespace comment ([#11712](https://github.com/diegosouzapw/OmniRoute/pull/11712)) +- **docs(changelog):** add consolidated v3.8.50 stats, top-25 ranking and a .mailmap ([#11715](https://github.com/diegosouzapw/OmniRoute/pull/11715)) +- **docs:** recommend gstack for AI-assisted workflows ([#11770](https://github.com/diegosouzapw/OmniRoute/pull/11770)) — thanks @santosraju99-hub +- **chore(quality):** tighten complexity/cognitive-complexity ratchets to the current tip; land the missed gateways.ts rebaseline ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **docs(i18n):** finish Freepik → Magnific rebrand in locale strings and README ([#11772](https://github.com/diegosouzapw/OmniRoute/pull/11772)) — thanks @turbolego +- **refactor(auto-combo):** fix divergent scoring in combo health reporting ([#11854](https://github.com/diegosouzapw/OmniRoute/pull/11854)) — thanks @jacobsparts +- **chore(changelog):** add missing fragments for #11919/#11918/#11916 ([#11938](https://github.com/diegosouzapw/OmniRoute/pull/11938)) +- **test(check):** escape the runs-on fixture with JSON.stringify, not a quote-only replace ([#11942](https://github.com/diegosouzapw/OmniRoute/pull/11942)) (direct commit `fb7445eaa3`) +- **test(release):** align five suites with the contracts #11933, #11919 and #11876 shipped on release/v3.8.51 ([#11944](https://github.com/diegosouzapw/OmniRoute/pull/11944)) +- **chore(quality):** remove unreachable code and restore test discovery ([#11950](https://github.com/diegosouzapw/OmniRoute/pull/11950)) — thanks @backryun +- **docs(agents):** sync-back landings are fast-forward, never squash ([#11964](https://github.com/diegosouzapw/OmniRoute/pull/11964)) +- **test(infra):** retry recursive temp-dir removal instead of failing a shard on ENOTEMPTY (#11966) ([#11968](https://github.com/diegosouzapw/OmniRoute/pull/11968)) +- **docs(ops):** the .113 heavy-build ceiling is one runner, not two ([#12048](https://github.com/diegosouzapw/OmniRoute/pull/12048)) +- **test(ui):** correct inactive auto-fetch expectation ([#12098](https://github.com/diegosouzapw/OmniRoute/pull/12098)) — thanks @RaviTharuma +- **docs:** document native dependency check escape hatch ([#12101](https://github.com/diegosouzapw/OmniRoute/pull/12101)) — thanks @RaviTharuma +- **test(providers):** regenerate the translate-path golden for OrcaRouter (#11923) ([#12118](https://github.com/diegosouzapw/OmniRoute/pull/12118)) +- **chore(quality):** velocity phase — loosen every numeric baseline by 20% until v4.0, monitor headroom nightly ([#12125](https://github.com/diegosouzapw/OmniRoute/pull/12125)) +- **chore(lint):** batch 0 of #12146 — type the call-log-cap sqlite rows instead of 45 as-any casts ([#12157](https://github.com/diegosouzapw/OmniRoute/pull/12157)) +- **chore(lint):** batch 4 of #12146 — shared/components react-hooks violations resolved ([#12159](https://github.com/diegosouzapw/OmniRoute/pull/12159)) +- **chore(lint):** batch 1 of #12146 — dashboard/cli-code react-hooks violations resolved ([#12160](https://github.com/diegosouzapw/OmniRoute/pull/12160)) +- **chore(lint):** batch 3 of #12146 — dashboard/settings react-hooks violations resolved ([#12162](https://github.com/diegosouzapw/OmniRoute/pull/12162)) +- **chore(lint):** batch 2 of #12146 — resolve the react-hooks compiler violations in dashboard/providers ([#12163](https://github.com/diegosouzapw/OmniRoute/pull/12163)) +- **chore(quality):** register search-432 cooldown test in stryker tap.testFiles ([#12170](https://github.com/diegosouzapw/OmniRoute/pull/12170)) +- **chore(lint):** batch 5 of #12146 — combos, endpoint, provider-stats, api-manager and costs react-hooks violations resolved ([#12174](https://github.com/diegosouzapw/OmniRoute/pull/12174)) +- **build:** add contributor fast profile ([#12192](https://github.com/diegosouzapw/OmniRoute/pull/12192)) — thanks @rafacpti23 +- **docs(readme):** README touch-ups ([#12193](https://github.com/diegosouzapw/OmniRoute/pull/12193)) ([#12194](https://github.com/diegosouzapw/OmniRoute/pull/12194)) ([#12202](https://github.com/diegosouzapw/OmniRoute/pull/12202)) — thanks @rafacpti23 +- **build:** force the webpack bundler in the contributor build profile ([#12197](https://github.com/diegosouzapw/OmniRoute/pull/12197)) — thanks @rafacpti23 +- **build:** skip standalone packaging in the contributor profile ([#12198](https://github.com/diegosouzapw/OmniRoute/pull/12198)) — thanks @rafacpti23 +- **docs(audit):** align every published number with the code and harden check:docs-counts ([#12200](https://github.com/diegosouzapw/OmniRoute/pull/12200)) +- **build:** stub the instrumentation entrypoints in the contributor profile ([#12203](https://github.com/diegosouzapw/OmniRoute/pull/12203)) — thanks @rafacpti23 +- **build:** omit the standalone output target for contributor builds ([#12204](https://github.com/diegosouzapw/OmniRoute/pull/12204)) — thanks @rafacpti23 +- **chore(lint):** batch 6 of #12146 — memory, radar, audit, analytics, cache, usage, activity, home and RequestLoggerV2 react-hooks violations resolved ([#12208](https://github.com/diegosouzapw/OmniRoute/pull/12208)) +- **docs(diagrams):** rename number-carrying diagram files to stable names ([#12210](https://github.com/diegosouzapw/OmniRoute/pull/12210)) +- **docs(dashboard):** redraw onboarding tier-flow SVGs for the real 4-tier model ([#12211](https://github.com/diegosouzapw/OmniRoute/pull/12211)) +- **docs(api):** document every implemented route in openapi.yaml (276 → 692 paths) ([#12212](https://github.com/diegosouzapw/OmniRoute/pull/12212)) +- **chore(lint):** batch 7 of #12146 — final src tail: 37 react-hooks violations across 33 files resolved ([#12244](https://github.com/diegosouzapw/OmniRoute/pull/12244)) +- Revert "docs: recommend gstack for AI-assisted workflows (#11770)" ([#12248](https://github.com/diegosouzapw/OmniRoute/pull/12248)) ([#12249](https://github.com/diegosouzapw/OmniRoute/pull/12249)) +- **docs:** Chaos Mode setup guide + weighted strategy semantics ([#12250](https://github.com/diegosouzapw/OmniRoute/pull/12250)) +- **docs(agents):** protected-surface merge rule — operator approval for agent-instruction files ([#12253](https://github.com/diegosouzapw/OmniRoute/pull/12253)) +- **chore(quality):** register native-codex turn-pin tests in stryker tap.testFiles ([#12263](https://github.com/diegosouzapw/OmniRoute/pull/12263)) +- **chore(quality):** dedupe tap.testFiles entries added by racing base-red fixes ([#12265](https://github.com/diegosouzapw/OmniRoute/pull/12265)) +- **docs(readme):** introduce OmniRouteTray — the macOS menu-bar companion ([#12276](https://github.com/diegosouzapw/OmniRoute/pull/12276)) — thanks @ggiak +- **chore(proxy):** purge the legacy 1proxy residue — sync/rotator modules, dead DB exports, dead settings tab and flag, docs (#12091) ([#12290](https://github.com/diegosouzapw/OmniRoute/pull/12290)) +- **chore(quality):** baseline-headroom skips generated and vendored files in the fileSize worst-file signal ([#12291](https://github.com/diegosouzapw/OmniRoute/pull/12291)) +- **refactor(video):** unify the JPEG frame data-URI contract ([#12322](https://github.com/diegosouzapw/OmniRoute/pull/12322)) +- **chore(deps):** freeze onnxruntime-node and eslint-plugin-react-hooks out of dependabot groups ([#12329](https://github.com/diegosouzapw/OmniRoute/pull/12329)) +- **test(executors):** restore the #10986 reasoning-only fallback guards ([#12364](https://github.com/diegosouzapw/OmniRoute/pull/12364)) — thanks @pacocartones +- **chore(quality):** re-tighten the file-size ratchet to the real LOC (plan 3.8.52 task 0) ([#12411](https://github.com/diegosouzapw/OmniRoute/pull/12411)) +- **test(grok):** format web executor suite ([#12412](https://github.com/diegosouzapw/OmniRoute/pull/12412)) +- **chore(quality):** ratchet open-sse typecheck baseline to zero ([#12418](https://github.com/diegosouzapw/OmniRoute/pull/12418)) +- **test(build):** guard the artifact path policy arrays against duplicates ([#12422](https://github.com/diegosouzapw/OmniRoute/pull/12422)) +- **chore(lint):** adopt eslint-plugin-react-hooks 7.1.1 ([#12428](https://github.com/diegosouzapw/OmniRoute/pull/12428)) +- **chore(providers):** remove a keyless provider integration at its operator's request ([#12440](https://github.com/diegosouzapw/OmniRoute/pull/12440)) +- **docs(providers):** register providers removed at their operator's request and guard against reintroduction ([#12478](https://github.com/diegosouzapw/OmniRoute/pull/12478)) +- **chore(quality):** base fixes — stryker tap.testFiles + node_modules cache key ([#12482](https://github.com/diegosouzapw/OmniRoute/pull/12482)) +- **docs(arch):** one-process recipe for tens of long /v1/responses ([#12493](https://github.com/diegosouzapw/OmniRoute/pull/12493)) — thanks @RaviTharuma +- **chore(quality):** tighten the CodeQL ratchet baseline from 11 to 6 ([#12530](https://github.com/diegosouzapw/OmniRoute/pull/12530)) +- **chore(deps):** pin browserslist override to ^4.28.8 ([#12592](https://github.com/diegosouzapw/OmniRoute/pull/12592)) — thanks @KooshaPari +- **chore(deps):** bump fast-uri to 3.1.7 in the electron lockfile ([#12601](https://github.com/diegosouzapw/OmniRoute/pull/12601)) +- **docs(quality):** document how the CodeQL ratchet refreshes and how to tighten it ([#12611](https://github.com/diegosouzapw/OmniRoute/pull/12611)) +- **test(ci):** pin the openapi-security-tiers two-arm contract with an executing gate test (#12581) ([#12652](https://github.com/diegosouzapw/OmniRoute/pull/12652)) +- **docs(gamification):** align XP Rewards table with code (#12501) ([#12667](https://github.com/diegosouzapw/OmniRoute/pull/12667)) — thanks @KooshaPari +- **refactor(combo):** split executeTarget into gates, attempt, and loop ([#12746](https://github.com/diegosouzapw/OmniRoute/pull/12746)) — thanks @HouMinXi +- **docs(security):** document socket.yml scanner config + CI workflow link (#12575) ([#12764](https://github.com/diegosouzapw/OmniRoute/pull/12764)) — thanks @KooshaPari +- **chore(ci):** guard commit identity in pre-commit to stop author misattribution ([#12772](https://github.com/diegosouzapw/OmniRoute/pull/12772)) +- **deps:** 17 Dependabot bumps — bump electron from 43.4.0 to 43.4.1 in /electron ([#11426](https://github.com/diegosouzapw/OmniRoute/pull/11426)); bump the development group across 1 directory with 8 updates ([#11428](https://github.com/diegosouzapw/OmniRoute/pull/11428)); bump the production group across 1 directory with 11 updates ([#11440](https://github.com/diegosouzapw/OmniRoute/pull/11440)); chore(deps): bump github/codeql-action/init from 4.37.7 to 4.37.8 ([#11925](https://github.com/diegosouzapw/OmniRoute/pull/11925)); chore(deps): bump github/codeql-action from 4.37.7 to 4.37.8 ([#11926](https://github.com/diegosouzapw/OmniRoute/pull/11926)); chore(deps): bump github/codeql-action/analyze from 4.37.7 to 4.37.8 ([#11927](https://github.com/diegosouzapw/OmniRoute/pull/11927)); build(deps): bump github/codeql-action/init from 4.37.8 to 4.37.9 ([#12345](https://github.com/diegosouzapw/OmniRoute/pull/12345)); build(deps): bump github/codeql-action/analyze from 4.37.8 to 4.37.9 ([#12346](https://github.com/diegosouzapw/OmniRoute/pull/12346)); bump the development group across 1 directory with 2 updates ([#12347](https://github.com/diegosouzapw/OmniRoute/pull/12347)); build(deps): bump github/codeql-action from 4.37.8 to 4.37.9 ([#12349](https://github.com/diegosouzapw/OmniRoute/pull/12349)); bump browserslist from 4.28.2 to 4.28.8 ([#12396](https://github.com/diegosouzapw/OmniRoute/pull/12396)); bump the production group across 1 directory with 4 updates ([#12399](https://github.com/diegosouzapw/OmniRoute/pull/12399)); bump @xmldom/xmldom ([#12500](https://github.com/diegosouzapw/OmniRoute/pull/12500)); bump qs from 6.15.2 to 6.16.0 ([#12512](https://github.com/diegosouzapw/OmniRoute/pull/12512)); bump @xmldom/xmldom from 0.9.10 to 0.9.12 ([#12513](https://github.com/diegosouzapw/OmniRoute/pull/12513)); bump fast-uri from 3.1.5 to 3.1.7 ([#12514](https://github.com/diegosouzapw/OmniRoute/pull/12514)); bump @humanfs/node from 0.16.7 to 0.16.8 ([#12515](https://github.com/diegosouzapw/OmniRoute/pull/12515)) +- Clear the `release/v3.8.51` `check:agent-skills-sync` base-red: regenerate `skills/cli-tunnel/SKILL.md` so the `tunnel create [type]` positional that #13009 taught the generator to read is reflected in the committed skill. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` typecheck base-red from #12731: the new `Mode pack ranking gates` candidates in `open-sse/services/autoCombo/__tests__/autoCombo.test.ts` omitted the required `provider`, `model` and `errorRate` fields of `ProviderCandidate`, failing `check:open-sse-typecheck` (and with it `Fast Quality Gates`) on every open PR. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` docs-sync base-red: EURouter (#13025) and GreenPT (#13024) took the live provider count to 358, leaving 7 STRICT drifts (`PROVIDER_REFERENCE.md`, 4 diagrams, 2 tier-flow images) plus the `AGENTS.md` / `llm.txt` / `package.json` count claims stale. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- **test(models):** Pin `CATALOG_BUILD_TIMEOUT_MS` in the #12058 canonical-catalog regression so a cold catalog build racing the 8s default bound no longer flakes the suite with a 500 ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Allow `@omniroute/opencode-plugin-v2/` in the npm pack policy — [#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870) shipped the v2 plugin beside its v1 sibling without widening the allowlist, so every packed file under it read as an unexpected artifact and failed `check:pack-policy` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Point the release-green validator's pack gate at the tree it is validating — the build-provenance guard defaulted to `origin/main`, which a release branch tip can never be an ancestor of mid-cycle, so `Package artifact` reported a false red on every run and the tarball boot-smoke cascaded off it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **docs(checks):** keep doc counts honest — headings, rankings, catalog, weights, quality gate and scoring diagram now covered ([#12507](https://github.com/diegosouzapw/OmniRoute/pull/12507)) — thanks @maxmad64bis +- **chore(lifecycle):** `check:model-lifecycle` now also diffs `DEFAULT_DEGRADATION_MAP` (the background-task redirect table) against the vendor lifecycle snapshot, refusing a retired id as source or target, with a table-driven unit test beside it. Three rows whose source the vendor had retired — `claude-sonnet-4-20250514`, `gemini-3-pro-preview` and `gpt-5.1-codex` (whose target `gpt-5.1-codex-mini` is retired too) — were dead code, since `checkLifecycle` answers 410 before the redirect runs; they are dropped (#12535 — thanks @pacocartones) +- **test(compression):** cover idle worker eviction at the resource level — the pool must call `terminate()` and must not retain the worker's `MessagePort`, complementing the `exit`-event assertion added with the fix ([#13371](https://github.com/diegosouzapw/OmniRoute/pull/13371)) +- **chore(quality):** raise the `imageGeneration.ts` file-size ceiling for the image-only-model guard that clears the #12945 base-red ([#13392](https://github.com/diegosouzapw/OmniRoute/pull/13392)) +- **chore(quality):** type the combo-test route's JSON response bodies instead of casting them to `any`, drop the now-empty suppression entry, and remove the `hasPerModelQuota` import `chatCore.ts` stopped using when the failure-classification helper was extracted ([#13379](https://github.com/diegosouzapw/OmniRoute/pull/13379)) +- **chore(electron):** the desktop bundle no longer ships the root-level files of every translated docs mirror (`README.md`, `llm.txt`, `CLAUDE.md`, `GEMINI.md`, `CONTRIBUTING.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`) — the packaged app only reads `docs/i18n//docs/**`, which stays. Saves ~11 MB on top of the translated CHANGELOGs already pruned. (#0000) ([#13235](https://github.com/diegosouzapw/OmniRoute/pull/13235)) +- **chore(quality):** retighten the `src/sse/handlers/chat.ts` unused-vars suppression count to the 8 violations that actually remain ([#13385](https://github.com/diegosouzapw/OmniRoute/pull/13385)) +- Bring the documented counts back in line with the code: 171 migrations (was 169) and 20 routing strategies (was 19), plus the regenerated `cli-tunnel` skill reference. ([#12970](https://github.com/diegosouzapw/OmniRoute/pull/12970)) +- Update the documented migration count to 172 after the call-logs provider-stats indexes landed. ([#13209](https://github.com/diegosouzapw/OmniRoute/pull/13209)) +- **chore(ci):** two gates that close the blind spots behind the exclusions above. `check:vitest-exclusions` requires every Vitest exclusion to name a tracking issue and to appear in `config/quality/vitest-exclusions.json` — the previous list grew to 62 files behind a comment pointing at an issue that had been closed for a month. `check-new-key-coverage` requires a key newly added to `en.json` to reach every locale; the existing coverage gate is a percentage floor per locale, so eleven absent keys out of ~13,000 left it at 99.9% while a whole feature shipped untranslated in nine languages. Both are diff-aware, so pre-existing debt stays frozen and neither needed a migration to turn on. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **build:** root layout no longer loads Inter from `next/font/google`, so a production image build does not need fonts.googleapis.com ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **docs(i18n):** bring the eight new `llm.txt` mirrors (km, kn, ml, my, ne, or, pa, si) up to the current provider and migration counts, so `check-docs-sync` stops rejecting every commit ([#13674](https://github.com/diegosouzapw/OmniRoute/pull/13674)) +- **refactor(ui):** flow surfaces (home topology, combo live studio, compression cockpit/waterfall/nodes, token-health badge) express state through the theme-aware `--orch-status-*` tokens instead of fixed dark-mode hex, so green/red/amber/grey stay legible in the light theme; dark mode is byte-identical. Categorical palettes (routing-strategy hues, compression-layer pills, provider brand colors) deliberately stay hex ([#12378](https://github.com/diegosouzapw/OmniRoute/issues/12378)) ([#13203](https://github.com/diegosouzapw/OmniRoute/pull/13203)) +- **chore(tests):** 51 test files that had been excluded from Vitest are running again, restoring roughly 350 assertions to the blocking suite. Each was measured individually first: of the 62 files parked behind the `// #8618 — pre-existing failure` comment, 51 pass against the current tree with no source change, so the exclusions had outlived the failures they were added for. The 11 that genuinely still fail stay excluded, but now point at a live tracker (#13204) rather than at #8618, which was closed in August while the list it tracked kept growing. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **chore(quality):** drop two ESLint suppression entries whose violations no longer exist, so `eslint --suppressions-location` stops rejecting every commit that touches the surrounding files ([#13384](https://github.com/diegosouzapw/OmniRoute/pull/13384)) +- **chore(release):** bring main's post-tag fixes into release/v3.8.51 — sync-back step 2/2 (direct commit `21c488f210`) +- **chore(deps):** patch toml and esbuild in the opencode plugin lockfiles ([#12965](https://github.com/diegosouzapw/OmniRoute/pull/12965)) +- **docs(changelog):** reconcile the v3.8.51 living section — fold 366 fragments, cover every cycle commit, credit every contributor ([#12971](https://github.com/diegosouzapw/OmniRoute/pull/12971)) +- **test(plugin):** wait for the v2 lazy-refresh tier instead of sleeping past it ([#12984](https://github.com/diegosouzapw/OmniRoute/pull/12984)) +- **chore(quality):** rebaseline chatCore.ts after the non-streaming regression fixes ([#13045](https://github.com/diegosouzapw/OmniRoute/pull/13045)) +- **docs(changelog):** add the missing bullet marker to the DeepSeek PoW fragment ([#13200](https://github.com/diegosouzapw/OmniRoute/pull/13200)) +- **chore(deps):** drain the Dependabot queue — 10 of 13 alerts ([#13213](https://github.com/diegosouzapw/OmniRoute/pull/13213)) +- **chore(ci):** make worktrees carry the hooks that enforce the identity gate ([#13236](https://github.com/diegosouzapw/OmniRoute/pull/13236)) +- **chore(deps):** pin csv-parse to ^7.0.2 via overrides (#13117) ([#13300](https://github.com/diegosouzapw/OmniRoute/pull/13300)) — thanks @KooshaPari +- **chore(deps):** bump hono from ^4.12.34 to ^4.13.7 (#13148) ([#13301](https://github.com/diegosouzapw/OmniRoute/pull/13301)) — thanks @KooshaPari +- **chore(deps):** pin joi to ^18.2.8 via overrides (#13085) ([#13302](https://github.com/diegosouzapw/OmniRoute/pull/13302)) — thanks @KooshaPari +- **docs(providers):** add Microsoft 365 Copilot (BizChat) provider guide (#12779) ([#13340](https://github.com/diegosouzapw/OmniRoute/pull/13340)) — thanks @KooshaPari +### 🙌 Contributors + +Thanks to everyone whose work landed in v3.8.51: + +| Contributor | PRs / Issues | +| --- | --- | +| [@5dive-bot](https://github.com/5dive-bot) | #11852 | +| [@abhisheksharma2411](https://github.com/abhisheksharma2411) | #11547, #11684, #11959, #12209 | +| [@adevwithpurpose](https://github.com/adevwithpurpose) | #11464 | +| [@adivekar-utexas](https://github.com/adivekar-utexas) | #12015, #12043, #12090, #12138 | +| [@AIB1TAL0S](https://github.com/AIB1TAL0S) | #12103 | +| [@alltomatos](https://github.com/alltomatos) | #11448 | +| [@alvinveroy](https://github.com/alvinveroy) | #11857, #12027, #12165 | +| [@amaleta](https://github.com/amaleta) | #11906, #11934 | +| [@amartinawi](https://github.com/amartinawi) | #11575 | +| [@AndrianBalanescu](https://github.com/AndrianBalanescu) | #11083, #11830, #11834, #11844 | +| [@anhtahaylove](https://github.com/anhtahaylove) | #13091, #13092, #13093, #13096, #13097, #13100, #13106, #13109, #13114, #13155, #13166, #13171, #13175, #13187, #13196 | +| [@aniruddhaadak80](https://github.com/aniruddhaadak80) | #11760 | +| [@app](https://github.com/app) | #12554 | +| [@arminanton](https://github.com/arminanton) | #11461, #11513 | +| [@AStupidBear](https://github.com/AStupidBear) | #11584 | +| [@atescivitci-cmd](https://github.com/atescivitci-cmd) | #12105 | +| [@b3nw](https://github.com/b3nw) | #11970, #11971, #12147 | +| [@backryun](https://github.com/backryun) | #11259, #11950, #12075, #12076, #12078, #12079, #12081, #12082, #12181, #12228, #12239, #12255, #12258, #12277, #12367, #12423, #12524, #12538 | +| [@benjaminkitt](https://github.com/benjaminkitt) | #11747 | +| [@benzntech](https://github.com/benzntech) | #11614, #11615 | +| [@Bl0ck154](https://github.com/Bl0ck154) | #11948, #11951, #11952, #11953, #11954 | +| [@brick30llc-ctrl](https://github.com/brick30llc-ctrl) | #11752, #12242, #12262 | +| [@Chewji9875](https://github.com/Chewji9875) | #11455, #11875, #12028 | +| [@cheynetom](https://github.com/cheynetom) | #12058 | +| [@davidebaraldo](https://github.com/davidebaraldo) | #12222, #12304 | +| [@davidlinfr](https://github.com/davidlinfr) | #6390 | +| [@ddarkr](https://github.com/ddarkr) | #12124 | +| [@Deftera186](https://github.com/Deftera186) | #11809 | +| [@dpozimski](https://github.com/dpozimski) | #11945 | +| [@DW-MediaLab](https://github.com/DW-MediaLab) | #12229 | +| [@echel0nn](https://github.com/echel0nn) | #11923 | +| [@f9td56dbgh-hub](https://github.com/f9td56dbgh-hub) | #11660 | +| [@fabioluissilva](https://github.com/fabioluissilva) | #11991 | +| [@foreveryh](https://github.com/foreveryh) | #12177 | +| [@geek007git](https://github.com/geek007git) | #12115, #12116, #12117, #12120, #12122 | +| [@geekyNads](https://github.com/geekyNads) | #11784, #12266 | +| [@ggiak](https://github.com/ggiak) | #11561, #12276, #12350, #12402 | +| [@gonisulaimann](https://github.com/gonisulaimann) | #12368, #12369, #12371 | +| [@Gorillaz322](https://github.com/Gorillaz322) | #12207 | +| [@groovecityJO](https://github.com/groovecityJO) | #12682 | +| [@hartmark](https://github.com/hartmark) | #11434, #11452, #11473, #11499, #11703, #11983, #11984, #11985, #11986, #11988, #11989, #11990, #11994, #12221, #12293, #12445, #12446, #12447, #12448, #12460, #12461, #12623, #12646, #12650, #12680, #12717, #12718, #12727, #12741, #12854 | +| [@hizzt](https://github.com/hizzt) | #11894 | +| [@hongnoul](https://github.com/hongnoul) | #11484 | +| [@HouMinXi](https://github.com/HouMinXi) | #11411, #11414, #11512, #11518, #11520, #11641, #11642, #11643, #11687, #11779, #11849, #11850, #11851, #11915, #11916, #11918, #11919, #11920, #12013, #12017, #12033, #12042, #12106, #12139, #12166, #12169, #12171, #12205, #12213, #12312, #12325, #12487, #12488, #12495, #12504, #12557, #12566, #12590, #12591, #12624, #12626, #12632, #12637, #12678, #12696, #12697, #12711, #12733, #12746, #12767, #12770, #12789, #12803, #12805, #12811, #12866, #12868, #12899, #12926, #12934, #12950, #12951, #12974, #13001, #13006, #13011, #13017, #13026, #13027, #13034, #13035, #13038, #13042, #13050, #13060, #13061, #13069, #13107, #13120, #13136, #13195, #13197 | +| [@Hsia97](https://github.com/Hsia97) | #11624 | +| [@jacobsparts](https://github.com/jacobsparts) | #11854, #12155, #12167 | +| [@joglomedia](https://github.com/joglomedia) | #11980 | +| [@jonlwheat2-gif](https://github.com/jonlwheat2-gif) | #11443, #11450, #11549, #11567, #11608, #11633, #11635, #11640, #11644, #11671, #11672, #11673, #11674, #11675, #11676, #11677, #11706, #11783, #12051, #12052, #12053, #12055 | +| [@kanade-hoshino](https://github.com/kanade-hoshino) | #12180 | +| [@Karan825](https://github.com/Karan825) | #11861 | +| [@KaspaPulse](https://github.com/KaspaPulse) | #11389, #11469, #11628, #11666, #11910 | +| [@keeltrace](https://github.com/keeltrace) | #12080, #12223 | +| [@killer30001000](https://github.com/killer30001000) | #12178 | +| [@KooshaPari](https://github.com/KooshaPari) | #12470, #12592, #12667, #12699, #12703, #12706, #12764, #12769, #12771, #13300, #13301, #13302, #13340, #13401, #13403, #13404, #13405, #13406, #13407, #13409, #13410, #13411, #13412, #13413, #13414, #13418, #13424, #13427, #13433, #13523, #13524, #13525, #13528, #13531, #13532, #13534, #13539, #13543, #13545, #13546, #13547, #13550, #13553 | +| [@kriptoburak](https://github.com/kriptoburak) | #11370 | +| [@KrzysiekSko](https://github.com/KrzysiekSko) | #12425, #12432, #12673 | +| [@marcelokarval](https://github.com/marcelokarval) | direct commit / report | +| [@maxmad64bis](https://github.com/maxmad64bis) | #1622, #11435, #11437, #11441, #11537, #11550, #11553, #11555, #11557, #11812, #11842, #11843, #11903, #12151, #12214, #12215, #12218, #12226, #12314, #12316, #12317, #12318, #12319, #12320, #12321, #12507, #12715, #12731, #12744, #12786, #12787, #12788, #12790, #12792, #12794, #12795, #12828, #12832, #12853, #12857, #12870, #12937, #12941, #12975, #13141, #13142, #13146, #13147 | +| [@mdigitalbh81](https://github.com/mdigitalbh81) | #12240 | +| [@Meet6338-X](https://github.com/Meet6338-X) | #11598, #11609, #12110 | +| [@morpheus9393](https://github.com/morpheus9393) | #11943 | +| [@MumuTW](https://github.com/MumuTW) | #11492, #11502, #11506, #11507, #11626, #11675, #11685, #11728, #11746, #11888, #11889, #11890, #11892 | +| [@Neuron-Mr-White](https://github.com/Neuron-Mr-White) | #11622, #11801, #12256 | +| [@NightStalker-87](https://github.com/NightStalker-87) | #12183 | +| [@NoxzRCW](https://github.com/NoxzRCW) | #11879, #11880, #11881, #11882, #11883, #11935 | +| [@ntdat812](https://github.com/ntdat812) | #11585, #12095 | +| [@ntdatt812](https://github.com/ntdatt812) | #11368, #11573, #11574, #11576, #11577, #11580, #11582, #11583, #11588, #11589, #11590, #11591, #11592, #11593, #11672, #12177, #12180, #12873, #12918, #12920, #12921, #12925, #12930, #13007, #13009, #13024, #13025, #13083, #13087, #13101, #13104, #13110 | +| [@official-burak](https://github.com/official-burak) | #11542 | +| [@opensource-elearning](https://github.com/opensource-elearning) | #12179, #12189, #12278, #12286 | +| [@oyi77](https://github.com/oyi77) | #11408, #11409, #11421, #11505, #11677, #12036, #12110 | +| [@pacocartones](https://github.com/pacocartones) | #11521, #11522, #11527, #11528, #11529, #11530, #11531, #11532, #11533, #11534, #11595, #11599, #11603, #11604, #11605, #11607, #11610, #11676, #11714, #11716, #11718, #11767, #11838, #11860, #11862, #11869, #11871, #11872, #11873, #11903, #11906, #11921, #11934, #12356, #12358, #12359, #12360, #12361, #12362, #12364, #12365, #12373, #12374, #12375, #12376, #12377, #12379, #12380, #12381, #12386, #12387, #12389, #12390, #12394, #12395, #12397, #12401, #12403, #12404, #12522, #12523, #12535, #12536, #12540, #12541, #12543, #12545, #12548, #12549, #12550, #12551, #12552, #12647, #12651, #12653, #13055 | +| [@patrykkopycinski](https://github.com/patrykkopycinski) | #11936, #11937, #12224 | +| [@PauloFH](https://github.com/PauloFH) | #11509 | +| [@PauloHSOliveira](https://github.com/PauloHSOliveira) | #12241 | +| [@ponkcore](https://github.com/ponkcore) | #12054 | +| [@Prajeeth-12](https://github.com/Prajeeth-12) | #11634, #12046 | +| [@quiterunner-commits](https://github.com/quiterunner-commits) | #12131, #12143 | +| [@rafacpti23](https://github.com/rafacpti23) | #11554, #11558, #12192, #12197, #12198, #12202, #12203, #12204 | +| [@ragnar-claude](https://github.com/ragnar-claude) | #11564 | +| [@raheemuddin786](https://github.com/raheemuddin786) | #11491, #11839, #11840, #11841, #12003, #12230, #12231, #12232, #12233, #12234 | +| [@rahilmavani](https://github.com/rahilmavani) | #11761 | +| [@Rahulsharma0810](https://github.com/Rahulsharma0810) | #11771 | +| [@RaviTharuma](https://github.com/RaviTharuma) | #11710, #11727, #11797, #11798, #11802, #11805, #11806, #11811, #12098, #12099, #12101, #12449, #12452, #12472, #12473, #12493, #12533, #12607, #12628, #12631, #12636, #12875, #12876, #12880, #12882, #12884 | +| [@rezjalibd](https://github.com/rezjalibd) | #12186 | +| [@RhianB14](https://github.com/RhianB14) | #11692 | +| [@rifqiawl](https://github.com/rifqiawl) | #11517, #11519, #11969 | +| [@rqzbeh](https://github.com/rqzbeh) | #11390 | +| [@Sabeekhann](https://github.com/Sabeekhann) | #11699, #11705 | +| [@santosraju99-hub](https://github.com/santosraju99-hub) | #11755, #11770 | +| [@Seramicx](https://github.com/Seramicx) | #11568 | +| [@Siva010](https://github.com/Siva010) | #12191 | +| [@solstxce](https://github.com/solstxce) | #11597 | +| [@soroush5](https://github.com/soroush5) | #12691 | +| [@tenshiak](https://github.com/tenshiak) | #12279 | +| [@TheDemonTuan](https://github.com/TheDemonTuan) | #11468, #11470, #11471, #11482, #11548, #11758, #11775, #11814 | +| [@thomasmaerz](https://github.com/thomasmaerz) | #12834 | +| [@tuandinh0801](https://github.com/tuandinh0801) | #11454 | +| [@turbolego](https://github.com/turbolego) | #11621, #11762, #11772, #11774, #11781, #12216 | +| [@Tushar49](https://github.com/Tushar49) | #11392 | +| [@ujjawalkaushik1110](https://github.com/ujjawalkaushik1110) | #11863 | +| [@vermasomesh835](https://github.com/vermasomesh835) | #11794 | +| [@vsd2807](https://github.com/vsd2807) | #11565, #11619 | +| [@wahidsadik371-coder](https://github.com/wahidsadik371-coder) | #12126 | +| [@watchingdogs](https://github.com/watchingdogs) | #12031 | +| [@wildcard](https://github.com/wildcard) | #11620 | +| [@xiaoyaner0201](https://github.com/xiaoyaner0201) | #11460, #11662, #12083 | +| [@Xxx91n](https://github.com/Xxx91n) | #11690 | +| [@yourspraveen](https://github.com/yourspraveen) | #11146 | +| [@yxyxy](https://github.com/yxyxy) | #11671 | +| [@ZaimMarzuki](https://github.com/ZaimMarzuki) | #11960 | +| [@Zartharas](https://github.com/Zartharas) | #11340 | +| [@zero-executioner](https://github.com/zero-executioner) | #11631 | +| [@diegosouzapw](https://github.com/diegosouzapw) | maintainer | + +--- ## [3.8.50] — 2026-08-25 _Living section — regenerated 2026-08-12 from all cycle commits (cycle open `ed2db6cb19` → tip). Bullets carry the merged PR and its author; direct pushes listed separately._ diff --git a/docs/i18n/tr/CHANGELOG.md b/docs/i18n/tr/CHANGELOG.md index f83d44052b..864701dc9f 100644 --- a/docs/i18n/tr/CHANGELOG.md +++ b/docs/i18n/tr/CHANGELOG.md @@ -8,16 +8,1119 @@ ## [3.8.51] — TBD -_Living section — cycle opened at the v3.8.50 freeze (parallel-cycle model). Bullets are aggregated from `changelog.d/` fragments at each `/generate-release` phase._ +_Living section — reconciled 2026-09-15 from all cycle commits (`091589089c` → `c0f92ec98a`, 916 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 | **123** | +| 📝 Commits in the cycle | **916** | +| 🔀 Pull requests referenced | **892** | +| 📋 Changelog entries | **919** | +| 🙌 Contributors credited in entries | **121** | +| 🤖 Automated dependency commits | 18 | + +**Entries by type** + +| Type | Count | +| --- | ---: | +| 🐛 Fixes | 592 | +| ✨ Features | 131 | +| 🧹 Chore | 93 | +| 📚 Docs | 42 | +| 🧪 Tests | 32 | +| ♻️ Refactor | 8 | +| 🏗️ Build | 7 | +| ⚡ Performance | 7 | +| ⚙️ CI | 2 | +| ⏪ Reverts | 2 | +| 📦 Dependencies | 2 | +| 🔒 Security | 1 | + +### 🏆 Top 25 contributors this cycle + +_By commits in `091589089c..c0f92ec98a`, author identities consolidated via `.mailmap` and the merged PR's GitHub login. Bots excluded._ + +| # | Contributor | Commits | +| ---: | --- | ---: | +| 🥇 | diegosouzapw | 323 | +| 🥈 | Bob.Hou (@HouMinXi) | 79 | +| 🥉 | Paco Cartones (@pacocartones) | 77 | +| 4 | Dizzle (@maxmad64bis) | 49 | +| 5 | Koosha Paridehpour (@KooshaPari) | 43 | +| 6 | Nguyen Thanh Dat (@ntdatt812) | 31 | +| 7 | Ravi Tharuma (@RaviTharuma) | 26 | +| 8 | Markus Hartung (@hartmark) | 22 | +| 9 | Webman (@jonlwheat2-gif) | 22 | +| 10 | backryun | 17 | +| 11 | anhtahaylove | 15 | +| 12 | Rafa Martins (@rafacpti23) | 10 | +| 13 | Syed Raheemuddin (@raheemuddin786) | 10 | +| 14 | Nguyễn Viết Tuấn (@TheDemonTuan) | 8 | +| 15 | MumuTW | 7 | +| 16 | Paijo (@oyi77) | 7 | +| 17 | Tobias Andersen (@turbolego) | 6 | +| 18 | Bl0ck (@Bl0ck154) | 5 | +| 19 | SHANMUGAPRIYAN (@geek007git) | 5 | +| 20 | KaspaPulse | 5 | +| 21 | Abhishek Sharma (@abhisheksharma2411) | 4 | +| 22 | Abhishek Divekar (@adivekar-utexas) | 4 | +| 23 | Andrew B. (@AndrianBalanescu) | 4 | +| 24 | NoxzRCW | 4 | +| 25 | opensource-elearning | 4 | ### ✨ New Features +- **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). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- Added Google AI Studio Gemini batch text-to-speech support through `POST /v1/audio/speech`. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- 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. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(routing):** subscription-first auto groupings — `auto/subscription` routes only through plan-included connections with a documented hard-stop overage and fails closed on exhaustion, while `auto/thrifty` orders the pool `subscription → keyless → free → cheap → premium` and steps up one rung at a time as each is exhausted. Billing class comes from a curated per-connection catalog (uncurated is treated as metered, never plan-included), both reuse STRICT_ZERO_COST's per-connection verification, and a quota reading whose `resetAt` has passed is now refreshed regardless of TTL so routing returns to plan capacity as soon as it resets ([#11146](https://github.com/diegosouzapw/OmniRoute/pull/11146)) — thanks @yourspraveen +- **feat(providers):** publish a management-authenticated versioned web-session credential contract from OmniRoute's canonical browser credential metadata ([#11340](https://github.com/diegosouzapw/OmniRoute/pull/11340)) — thanks @Zartharas +- **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)) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add Xquik X search with typed results, credential validation, REST routing, and MCP selection ([#11370](https://github.com/diegosouzapw/OmniRoute/pull/11370)) — thanks @kriptoburak +- **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)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(dashboard):** surface durable exclusive managed leases in the existing Sessions view, keeping leased clients visible across idle gaps while marking connections with in-flight work as active ([#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389)) — thanks @KaspaPulse +- **build(bun):** allow Turbopack bundler flag on Bun 1.4+ with configurable Webpack fallback ([#11471](https://github.com/diegosouzapw/OmniRoute/pull/11471)) — thanks @TheDemonTuan +- feat(api): add an opt-in `modelVisibilityAllowlist`/`modelVisibilityDenylist` settings pair to curate exactly which models `/v1/models` advertises, mirrored into every `auto/*` combo candidate pool so a denied model cannot be routed to via combo selection either (#11481) ([#11997](https://github.com/diegosouzapw/OmniRoute/pull/11997)) +- **feat(rankings):** the Free Provider Rankings page shows what each provider actually served over the last 24 h. It ranked by ELO alone, which left a provider that answers every call with an error in first place; the usage data was already served by the API but never requested. A provider with too small a sample shows a dash, not a number ([#11546](https://github.com/diegosouzapw/OmniRoute/pull/11546)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **feat(guardrails):** enforce a bounded, deterministic contract for Video Bridge transcripts — 256 cues, 4096 input code units and 4 KiB UTF-8 per cue, 64 KiB total text, malformed-Unicode rejection, focus-window scoping, cross-source reconciliation with contributing-source metadata, and a structural provenance trust boundary so caller JSON can never self-assert `embedded`/`audio-bridge` provenance ([#11652](https://github.com/diegosouzapw/OmniRoute/issues/11652)) ([#12009](https://github.com/diegosouzapw/OmniRoute/pull/12009)) +- **feat(video):** orchestrate optional Video Bridge audio extraction and Audio Bridge STT behind a dual opt-in (operator setting AND per-request signal) — a new loopback-only broker `mode=audio` operation shares the frame path's exact process queue, deadline, AbortSignal, and byte budgets to extract a bounded mono 16 kHz PCM WAV from the same already-downloaded video, then reuses the existing Audio Bridge transcription boundary; provider segment timing is preserved when available and marked coarse otherwise, and every failure degrades to a visual-only-safe partial instead of throwing (#11654). ([#12012](https://github.com/diegosouzapw/OmniRoute/pull/12012)) +- Add a tenant-bound Video Bridge drill-down lifecycle on top of the existing secure cache substrate: opaque hashed handles (never raw session/video identifiers), preview/standard/detail multiresolution variants resampled on read, response pagination capped at 8 frames and 32 MiB, and a new authenticated `/api/v1/video-bridge/drilldown` consumer route that stays disabled for remote access by default and denies cross-key access with the same response as a nonexistent handle (no existence oracle). ([#12006](https://github.com/diegosouzapw/OmniRoute/pull/12006)) +- **test(video):** Add the Video Bridge FU-07/FU-09 promotion-evidence harness (#11656) — a frozen Zod manifest schema covering the 8 required scenario kinds (static scenes, rapid cuts, late facts, fades, blur, small text, close events, visual prompt injection) with a minimum of 3 repetitions per case, deterministic declarative fixture recipes (`videoBridgePromotionFixtures.ts`), a pure medians/p95 metrics aggregator, a pure FU-07/FU-09 promotion-verdict evaluator applying the ticket's exact thresholds (missing token usage always holds), a digest-only persistence layer that never retains raw media or raw model responses, and a versioned per-model promotion allowlist shipped empty with every model defaulting to `hold`. The FU-07/FU-09 promotion verdicts themselves remain HOLD — they require a real evidence run against real models on VPS 192.168.0.15. ([#12008](https://github.com/diegosouzapw/OmniRoute/pull/12008)) +- **feat(video bridge):** "embedded" transcript provenance can now be legitimately earned instead of merely asserted — a bounded, allowlisted (`mov_text`/`subrip`/`webvtt`) subtitle probe runs through the loopback-only Video Bridge broker (at most 2 streams, 10s subdeadline bounded by the request deadline, 256 KiB output, 4096-code-unit lines), normalized through a bounded, ReDoS-safe WebVTT parser and Zod-validated end to end. The adapter always resolves to an explicit `success`/`absent`/`transient_failure` outcome — a subtitle failure never breaks the visual description path, and only a fingerprint-verified broker response (never a caller-declared label) can produce embedded cues (#11659). ([#12011](https://github.com/diegosouzapw/OmniRoute/pull/12011)) +- Default new Antigravity-family connections (agy CLI imports and Antigravity OAuth connects) to model auto-sync, so live model discovery lands in the synced catalog and `/v1/models` picks up freshly released upstream models (e.g. Gemini 3.7 Flash tiers) without code changes. Existing connections keep their current setting; the per-connection dashboard toggle remains the opt-out. (#11685 — thanks @MumuTW) ([#11887](https://github.com/diegosouzapw/OmniRoute/pull/11887)) +- **feat(ui):** enable React Compiler (`reactCompiler: true` + `babel-plugin-react-compiler`) for automatic memoization at build time ([#11783](https://github.com/diegosouzapw/OmniRoute/pull/11783)) — thanks @jonlwheat2-gif +- **feat(providers):** add SeekAi (`seekai.cc`) as an OpenAI-compatible New-API gateway — catalog id `seekai` (alias `ska`), `https://seekai.cc/v1`, live `/v1/models` via `passthroughModels`, aggregator-list membership so New-API balance detection can opt in. No referral/aff codes. ([#11786](https://github.com/diegosouzapw/OmniRoute/issues/11786)) ([#12557](https://github.com/diegosouzapw/OmniRoute/pull/12557)) — thanks @HouMinXi +- **feat(zai):** add GLM-5.3-Flash Coding Plan support (1M context, 128K output, vision, `low|high|max` reasoning) and route `zai` GLM-5.3-family API-key traffic through the OpenAI-compatible Coding Plan endpoint with native thinking defaults ([#11801](https://github.com/diegosouzapw/OmniRoute/pull/11801)) — thanks @Neuron-Mr-White +- **feat(combo):** choose how combo models are ordered — manual, provider, score, or name — via a sort control in the dashboard builder, persisted in `config.modelSort` and re-applied on load and after add ([#11812](https://github.com/diegosouzapw/OmniRoute/pull/11812)) — thanks @maxmad64bis +- **feat(free):** custom models can be marked free-tier via `customModels[].isFree`; `isFreeModel()` is the first door and `hidePaidModels` respects it even for providers outside the free budget ([#11843](https://github.com/diegosouzapw/OmniRoute/pull/11843)) — thanks @maxmad64bis +- **feat(nodejs):** add `5dive` as a `configure` target — `omniroute configure 5dive` / `omniroute setup-5dive` write a 5dive auth profile that points an agent fleet's `claude` seats at OmniRoute, with the root-only write, the loopback-vs-`https` endpoint rule and the per-seat model pin handled explicitly ([#11852](https://github.com/diegosouzapw/OmniRoute/pull/11852)) — thanks @5dive-bot +- **feat(sse):** treat `max` as a first-class reasoning-effort tier and clamp per model family (GLM 5.1+/DeepSeek V4+/Kimi K3+ keep native `max`; o1/MiniMax/Grok/Muse Spark clamp to their upstream ceiling) ([#11875](https://github.com/diegosouzapw/OmniRoute/pull/11875)) — thanks @Chewji9875 +- **feat(providers):** the provider plugin manifest now advertises a `usage-fetch` capability for the 40 providers that have a wired usage/quota fetcher, so external dashboards can read it from `GET /api/v1/provider-plugin-manifest` instead of parsing `open-sse/services/usage.ts` after every release. Discovery only — no new fetcher, no quota change, and the Dashboard quota widget stays gated by `USAGE_SUPPORTED_PROVIDERS`. `USAGE_FETCHER_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/fetcherProviders.ts`) and is re-exported from `services/usage.ts`, keeping the manifest module a light leaf instead of pulling the ~490-module usage dispatcher into the manifest route. ([#11903](https://github.com/diegosouzapw/OmniRoute/pull/11903)) — thanks @maxmad64bis / @pacocartones +- **feat(plugins):** `OMNIROUTE_PLUGINS_DIR` sets the directory the runtime plugin scanner reads — and the root the plugin manager installs into — overriding the `HOME`-derived default, so a Docker/K8s deployment can point straight at its bind-mounted plugin tree instead of moving `HOME` just to relocate the scan path. An image that exports no home no longer scans `/tmp/.omniroute/plugins` in silence: the resolved directory is logged once at startup as `scanner.dir_resolved`, naming the input that won. Unset, behaviour is unchanged. Distinct from the CLI-only `OMNIROUTE_PLUGIN_PATH`, which finds `omniroute-cmd-*` command packages and never reached this scanner ([#11906](https://github.com/diegosouzapw/OmniRoute/pull/11906)) — thanks @amaleta / @pacocartones +- **feat(leases):** add an explicit owner-authenticated status action that returns only the active lease's privacy-safe configured connection and provider labels, with generation fencing and no credential or internal-id disclosure ([#11910](https://github.com/diegosouzapw/OmniRoute/pull/11910)) — thanks @KaspaPulse +- **feat(providers):** import-from-file modal shows per-row API errors and ships a downloadable CSV template ([#12071](https://github.com/diegosouzapw/OmniRoute/issues/12071)) ([#12504](https://github.com/diegosouzapw/OmniRoute/pull/12504)) — thanks @HouMinXi +- **feat(providers):** dashboard search matches connection name and `baseUrl` so imported OpenAI-compat nodes surface on the provider card ([#12108](https://github.com/diegosouzapw/OmniRoute/issues/12108)) ([#12495](https://github.com/diegosouzapw/OmniRoute/pull/12495)) — thanks @HouMinXi +- **feat(routing):** add a `score` Auto router strategy that selects the highest configured weighted score and reuses `explorationRate`. ([#12155](https://github.com/diegosouzapw/OmniRoute/pull/12155)) — thanks @jacobsparts +- Add a runtime feature flag to disable universal context handoffs globally without changing the default behavior. ([#12167](https://github.com/diegosouzapw/OmniRoute/pull/12167)) — thanks @jacobsparts +- **feat(providers):** the provider plugin manifest now also advertises a `usage-supported` capability for the 46 providers whose usage API is accepted by the server and Dashboard routes, so integrators can distinguish "the server will serve quota for this provider" from "a fetcher is wired" without reading TypeScript. Discovery only — no fetcher or quota change. `usage-fetch` resolves on id or alias (the usage dispatcher accepts both); `usage-supported` resolves on id alone, matching the runtime guard `USAGE_SUPPORTED_PROVIDERS.includes(providerId)`. `USAGE_SUPPORTED_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/supportedProviders.ts`) and is re-exported from `providers.ts`, mirroring the `fetcherProviders` leaf from #11903 and keeping the manifest a light module. ([#12214](https://github.com/diegosouzapw/OmniRoute/pull/12214)) — thanks @maxmad64bis +- **feat(rankings):** order Free Provider Rankings by what each provider actually served — `GET /api/free-provider-rankings?sortBy=reliability` and a "Most reliable first" toggle on the page. Providers with too few calls to state a success rate keep their score order below the measured ones; the default order is unchanged ([#12218](https://github.com/diegosouzapw/OmniRoute/pull/12218)). — thanks @maxmad64bis +- **perf(sse):** defer `cloneLogPayload()` in the structured SSE collector until after the `maxEvents`/`maxBytes` cap check, eliminating ~9,800 wasted `structuredClone` calls per streaming response (65–71% faster `push()`). Reducer snapshot isolation restored for OpenAI and Responses summaries ([#12241](https://github.com/diegosouzapw/OmniRoute/pull/12241)) — thanks @PauloHSOliveira ([#12243](https://github.com/diegosouzapw/OmniRoute/pull/12243)) +- **feat(usage):** Devin CLI agentic quota (Codeium seat-management GetUserStatus) and OpenRouter key limits plus account credits now surface in Provider Limits ([#12256](https://github.com/diegosouzapw/OmniRoute/pull/12256) — thanks @Neuron-Mr-White) +- **feat(settings):** persist `headroomUrl` through Settings so status/start use the operator URL instead of only `HEADROOM_URL` ([#12306](https://github.com/diegosouzapw/OmniRoute/issues/12306)) ([#12487](https://github.com/diegosouzapw/OmniRoute/pull/12487)) — thanks @HouMinXi +- **feat(auto-combo):** Auto-Combo scoring can now weigh how often a provider/model has actually succeeded. The engine already carried that number on every candidate — 24 hours of usage history behind a ten-sample floor, real-time metrics otherwise — and the scoring function never read it, while the weight table described `stability` as if it did. `reliability` (`1 - failureRate`, with the same field precedence and the same rate-bounding the speed ranking already uses, so a corrupt reading means "nothing observed" rather than "fails every call") is now a declared factor shipping at weight `0`, so routing is unchanged until an operator gives it one, and the `stability` description now matches what that factor computes ([#12317](https://github.com/diegosouzapw/OmniRoute/pull/12317)) — thanks @maxmad64bis +- **feat(routing):** With `freeAccessPolicy: "strict"`, the read-only candidate listing (`GET /v1/auto-combo/{channel}/candidates`) no longer hides the candidates the zero-cost guard excludes — the same read-only transparency the resilience filter already honours (#9133). Each candidate now carries `freeAccessExclusion` saying why it would be kept out, and it tells an exhausted allowance apart from a quota reading that never arrived or went stale, which used to look identical from the outside. Routing is unchanged: the listing reports, it never enforces. The separate `excludeTosAvoid` guard still drops its candidates without a reason; that gap is now documented rather than closed ([#12319](https://github.com/diegosouzapw/OmniRoute/pull/12319)) — thanks @maxmad64bis +- **feat(radar):** The Radar catalog table now shows two facts it was already receiving from the feed and dropping on the floor: the per-model rate limits (requests and tokens, per minute and per day) in a new column, and a badge when a provider's terms state it may train on the prompts you send. A limit of zero renders as zero rather than "rate-only" — for a ceiling those are opposite facts — and a model with no training statement gets no badge, because an absent statement is not a guarantee ([#12320](https://github.com/diegosouzapw/OmniRoute/pull/12320)) — thanks @maxmad64bis +- **feat(radar):** explain Community, single-use, contributor, supporter, recovery, abuse, offers, and privacy rules before either Radar activation action, and remove the superseded fixed-PR grant promise from every UI locale ([#12342](https://github.com/diegosouzapw/OmniRoute/pull/12342)) +- **feat(gamification):** the dashboard Profile page now shows the real daily streak — `/api/gamification/level` returns `streak: { current, longest }` (per key with `apiKeyId`, operator-wide maximum otherwise) and the streak card reads it instead of a hard-coded 0 (#2403) ([#12377](https://github.com/diegosouzapw/OmniRoute/pull/12377)) — thanks @pacocartones +- **feat(gamification):** the dashboard leaderboard now shows each API key's display name under the Name column instead of a truncated key id; `GET /api/gamification/leaderboard` attaches `name` per entry (name only — no key material), while the shared ranking helper and the federation leaderboard stay id-only — thanks @pacocartones ([#12385](https://github.com/diegosouzapw/OmniRoute/pull/12385)) +- **feat(gamification):** enforce the documented 1000 XP/min per-API-key anti-cheat rate limit on the XP award path; over-limit awards are logged and skipped instead of persisted, and the sliding window now matches the timestamp format stored in `xp_audit_log` ([#2403](https://github.com/diegosouzapw/OmniRoute/issues/2403)) ([#12390](https://github.com/diegosouzapw/OmniRoute/pull/12390)) — thanks @pacocartones +- **feat(admin):** localize the gamification anomalies page — the loading state, the Status column and the Suspicious badge now come from the `common` catalog (new `common.suspicious` key propagated to every locale) — add it to the Gamification sidebar group as `gamification-admin` (`/dashboard/gamification/admin`), and expose the loading and empty states as polite `role="status"` live regions (#12401 — thanks @pacocartones) +- **feat(providers):** skip GitHub combo members missing from the live synced catalog, and drop Copilot models that are policy-disabled or hidden from the model picker ([#12473](https://github.com/diegosouzapw/OmniRoute/pull/12473)) — thanks @RaviTharuma +- **feat(i18n):** locale-expansion tooling — `npm run i18n:add-locale` adds a language to config, dashboard, docs mirrors, CLI, README/indexes and site in one command; browser, CLI and cookie detection resolve `uk`, `fil`/`tl`, `zh-Hant` (and the retired legacy `in` → `id`) via config aliases; `config/i18n.json` now ships in the npm package so the published CLI can read it; new real-translation ratio gate (`npm run i18n:check-ratio`, advisory) with a per-locale ratchet baseline; `run-translation --adopt` restores `.i18n-state.json`; `sync-language-bars` generates 🌐 bars from config; `validate_translation.py` loads its allowlist again. Retires the duplicate `in` locale (Indonesian mislabelled as Hindi) — 42 honest locales; saved `NEXT_LOCALE=in` / `OMNIROUTE_LANG=in` keep working. (#12496) +- **feat(api):** Emit gateway-measured `tokens_per_second` (TTFT excluded) on streaming usage and `X-OmniRoute-Tokens-Per-Second` when first-token latency is known ([#12616](https://github.com/diegosouzapw/OmniRoute/issues/12616)) ([#12631](https://github.com/diegosouzapw/OmniRoute/pull/12631)) — thanks @RaviTharuma +- **feat(opencode-plugin): map gateway cost/usage/tok/s onto OpenCode inference payloads** — the official plugin copies `X-OmniRoute-Response-Cost`, token counts, `X-OmniRoute-Tokens-Per-Second` / `usage.tokens_per_second`, TTFT, and the winning `X-OmniRoute-Model` onto the JSON/SSE body OpenCode already consumes. Missing tok/s is left unset (never `tokens / latency`). (#12636) — thanks @RaviTharuma +- **feat(combo):** add combo strategy `quota-weighted`: skip exhausted accounts, then weighted-draw by leftover / in-flight load; existing conversations stay pinned until the account is empty ([#12789](https://github.com/diegosouzapw/OmniRoute/pull/12789)) — thanks @HouMinXi +- **feat(models):** Account live listings become the chat catalog source for Claude, Codex, Copilot, and AGY; public metadata only fills prices on IDs those accounts already list. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **fix(models):** Union `agy` and `antigravity` live catalogs so `agy/gemini-3.8-flash-high` is not rejected after the prefix folds to `antigravity`. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **feat(opencode):** Add `@omniroute/opencode-plugin-v2`, an opencode v2 plugin that gives a v2 install a route to an OmniRoute gateway: it publishes models, combos and auto-combos into the host catalog, refreshes them lazily behind a 300s TTL, and keeps serving the last known catalog from an on-disk snapshot when the gateway is unreachable. The v1 plugin (`@omniroute/opencode-plugin`) is untouched and keeps working as before. The plugin adapts to the host instead of assuming one shape: it reads the shape the host seeds into the catalog draft and publishes the `api` block, the pre-`api` top-level fields, or both. The gateway key comes from the host credential store when one is connected, so no secret needs to sit in a config file; a source the gateway refuses is named in a warning instead of silently degrading the catalog to raw model ids; and tool schemas bound for a Gemini model are stripped of the JSON-Schema keywords Gemini rejects, so tool calling survives that route ([#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870)) — thanks @maxmad64bis +- feat(services): show sanitized CLIProxyAPI account health from its authenticated management API without exposing credentials, file paths, or raw account metadata (#6342) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add AnySearch free web search + URL extract (webFetch) with typed results, credential validation, REST routing, and MCP selection - fallback-only ([#11690](https://github.com/diegosouzapw/OmniRoute/pull/11690)) — thanks @Xxx91n +- **feat(dashboard):** display clamped `[0, 100]%` cached input token ratio in request logs table ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) — thanks @b3nw +- **feat(catalog):** add `OMNIROUTE_DISABLE_THINKING_LEVEL_VARIANTS` feature flag to optionally filter out thinking level variants from model catalog ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) — thanks @b3nw +- **feat(dashboard):** continuously export call logs to external analytics stores. A pluggable destination registry ships the full Logs-tab record set on an hourly `JobRegistry` cron, with a persisted per-destination cursor, batched inserts, a config UI rendered from each destination's own field descriptors, and a REST layer (`/api/log-export/*`) for CRUD, a connection test, and an on-demand run. A destination can opt into `includeBodies` to also ship the request and response payloads shown in the Logs detail pane, including the client and provider views of each call; this is off by default, and payloads inherit the dashboard's PII sanitisation, secret redaction and `noLog` handling. Google BigQuery is the first destination, using a service-account key stored encrypted at rest and streaming inserts keyed by call-log id, into a table that is day-partitioned on `timestamp` and clustered on `api_key_name`, `provider`, `model` and `status`. ([#11945](https://github.com/diegosouzapw/OmniRoute/pull/11945)) — thanks @dpozimski +- **feat(providers):** add MaxAI as a signed, OpenAI-compatible provider serving its 13 paid chat models (GPT-5.6 / Luna / Thinking, Claude 5 Sonnet, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite, Grok 4.1-fast / 4.5, DeepSeek V3.2 / R1, Llama 3.3 70B) through OmniRoute's `/v1` endpoint, with per-request HMAC-SHA1→SM3→AES request signing, live model + context-window discovery from `/models/get_config`, and prompted tool-calling translated to OpenAI `tool_calls` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI vision input — image_url content parts are forwarded inline in `message_content` to the 6 vision-capable models (GPT-5.6 / Luna / Thinking, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite) ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI image generation — 6 image models (gpt-image-1, dall-e-3, flux-1-schnell/dev/pro, sd3-medium) exposed through `POST /v1/images/generations` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI document RAG — inline base64 file/document attachments are uploaded to MaxAI (content-addressed `doc_id`) and attached to the chat via `doc_list` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** browserless MaxAI onboarding — email device-pair login (`/api/providers/[id]/login`) and signed access-token refresh, so a connection can be created and kept fresh without a real browser or Google OAuth ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** per-provider TLS impersonation profile (MaxAI presents a Windows Firefox-150 client fingerprint) so its bot-sensitive endpoints accept OmniRoute traffic ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** add **Nimble** as a web-search and web-fetch provider (`nimble-search`) — `/v1/search` routes to Nimble's search API at `lite` depth (locale, freshness and include/exclude domain filters mapped onto the shared request contract), and `/v1/web/fetch` routes to Nimble Extract, which covers all four fetch formats (`markdown`, `html`, `links`, `screenshot`) from a single call. One API key serves both surfaces. ([#11620](https://github.com/diegosouzapw/OmniRoute/pull/11620)) — thanks @wildcard +- The npm publish is automatic again, through npm Trusted Publishing (OIDC): the hosted `stage-npm` job publishes with a short-lived credential minted from GitHub's id-token — no `NPM_TOKEN`, no 2FA prompt, provenance attached. `publish_mode=staged` (owner approves with 2FA) and `direct` (token) remain available on `workflow_dispatch`. ([#11931](https://github.com/diegosouzapw/OmniRoute/pull/11931)) +- **feat(providers):** Add **Opper** as an API-key gateway provider — EU-hosted AI gateway with 700+ models from 30+ providers behind one OpenAI-compatible API and one key (`OPPER_API_KEY`); model ids use `provider/model` format (e.g. `anthropic/claude-sonnet-4-6`, `openai/gpt-5`); live model catalog at `https://api.opper.ai/v3/compat/models`; entry mirrors `requesty` (same shape, `passthroughModels: true`, no static seed) ([#11629](https://github.com/diegosouzapw/OmniRoute/pull/11629)) +- **feat(dashboard):** the `/dashboard/orchestration` snapshot hook now subscribes to the `agents` WebSocket channel (`agent.task.updated`) instead of `requests` as its refetch trigger, and relaxes its background poll from 5s to 30s while that WS connection is up — falling back to the tighter 5s cadence, reprogrammed live on any connect/disconnect transition, whenever the socket is down. ([#12409](https://github.com/diegosouzapw/OmniRoute/pull/12409)) +- **feat(dashboard):** Orchestration canvas quick wins — search box plus state/source/provider filter chips with a one-click clear, and per-source collapse/expand, all reflected in the URL so a filtered/collapsed view is shareable and survives a refresh; the detail drawer gained a "copy trace JSON" action and hardened error/empty-state and accessibility handling; the Agents-tab edges now animate traveling particles along active (running) connections; and the canvas node/edge status colors moved off fixed hex values onto theme-aware `--orch-status-*` CSS custom properties, so they adapt correctly to light/dark mode. ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- **chore(dashboard):** Orchestration UI hardening pass and the missing component/model test coverage it called for — `OrchestratorNode`/`ActivityNode`/`OverflowNode` rendering, the `?node=`/overflow-click page routing, the Agents-tab orchestrator-click no-op and `showCompleted` toggle, and the overview kanban's done-column sort order (#12270, #12271). ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- New `/dashboard/orchestration` page: live unified view of everything running — Cloud Agent, A2A and Conductor as a real-time graph (Agents tab), the combo cascade (Routing tab, reusing the Combo Live Studio) and a state kanban (Overview tab), with a detail drawer (trace, cost, approve/cancel). Read-only over existing APIs — no new backend. Canvas concept credit: PR #11815 design ([#12261](https://github.com/diegosouzapw/OmniRoute/pull/12261)) +- **feat(dashboard):** new "History" tab on `/dashboard/orchestration` — an Airflow-style grid of finished runs over a 24h/7d/30d preset window, one row per (source, identity), clicking a cell opens the existing detail drawer. It is backed by real persistence: A2A task lifecycle transitions are now written to the `a2a_tasks` table (purged after 30 days, configurable via `OMNIROUTE_A2A_HISTORY_RETENTION_DAYS`) and served by the new `GET /api/a2a/tasks/history` listing endpoint, with the task-detail route falling back to persisted history once a run leaves the in-memory snapshot. Conductor runs stay remote and are not persisted locally — the tab says so instead of silently omitting them. ([#12479](https://github.com/diegosouzapw/OmniRoute/pull/12479)) +- **feat(dashboard):** the orchestration detail drawer gained a "Repeat" action for Cloud Agent, A2A and Conductor tasks — a two-click confirm (click once to arm, click again within the confirm window to fire) re-submits the original prompt/input as a new run. The button is disabled with an explanatory tooltip whenever the original input can't be recovered from the loaded task detail (e.g. it never carried a prompt, or the detail failed to load). Two limitations of the A2A variant, by design: it targets the `/a2a` JSON-RPC endpoint, which authenticates with an API key only (`REQUIRE_API_KEY=true` or a configured `OMNIROUTE_API_KEY` makes a dashboard-session repeat answer `HTTP 400` — surfaced verbatim in the drawer's error line, never as a success), and the `message/send` call is SYNCHRONOUS: the POST blocks for the whole skill run, so the success confirmation only appears once the repeated task finishes. A dashboard-authenticated A2A creation path is deliberately left to a follow-up — widening the endpoint's auth posture is an operator decision, not a side effect of this feature. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(a2a):** A2A task execution now records which memories were consulted for the task's last user message as `metadata.memoryHits` (id/key/type/content-snippet) plus a `memory_hits` history event, purely for observability — the retrieved memory is never injected into a skill's prompt or behavior. Gated by the `OMNIROUTE_A2A_MEMORY_HITS` kill-switch (default enabled; set to `0` to skip the recall lookup entirely). The drawer's new "Memory used" section lists these hits for a2a tasks and is omitted whenever there are none. Known limitation: recall only resolves under the keyless posture — a keyed caller's task owner is a SHA-256 prefix of the API key, while memory rows are keyed by the database api-key id, and no hash→id lookup exists today, so the hit list stays empty for keyed callers. The recorded hits are also kept out of the task's own `input` (and therefore out of the persisted input and of the "Repeat" request body), so repeating a task never re-sends the previous run's memory snippets. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(providers):** add a Perplexity Agent API provider (`perplexity-agent` / `pplx-agent`) for Perplexity `/v1/responses`, including the documented Anthropic, OpenAI, Google, xAI, DeepSeek, Z.AI, Moonshot/Kimi, NVIDIA, and Perplexity model IDs plus Anthropic-model `max_output_tokens` compatibility. ([#12103](https://github.com/diegosouzapw/OmniRoute/pull/12103)) — thanks @AIB1TAL0S +- **feat(providers):** add RPD (Requests Per Day) limit to provider rate limit overrides across UI, schemas, DB, and i18n ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) — thanks @b3nw +- **feat(providers): add UC Direct (uncensored.com Developer API), the metered OpenAI-compatible surface.** A standard OpenAI-compatible passthrough (default executor) for uncensored.com's official REST API at `https://api.uncensored.com/api/v1`: `X-api-key` auth (never-expiring `uai_sk_live_` key), `POST /chat/completions` with streaming SSE and native tool-calling, and the full live metered catalog (82 models across 15 providers, discovered from the public `GET /v1/models`). Registered as provider `uc-direct` (alias `ucd`). Complements the un-metered `uc` persona provider — same models, metered credits and a plain API key instead of a subscription session. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(providers): add UC (uncensored.com), the un-metered subscription "persona" chat as an OpenAI-compatible provider.** A WebSocket web-app port: a durable Clerk credential mints a short-lived session token per connect (browserless — no API key), driving UC's persona socket. Ships the browserless email-code login (request → verify → harvest), the 19 verified persona models (Claude Opus, Gemini, Grok, GLM, Kimi, DeepSeek, MiniMax, incl. the uncensored variants), prompted `` tool-calling with a per-model code-style dialect + auto-cure retry for guardrailed models, live ``/reasoning split, streaming + non-streaming OpenAI responses, and full quota/auth error surfacing (paywall / message-limit / rate-limit → 429, invalid session → 401 re-login). Full multimodal parity via the persona blob-upload layer: **vision** (image input, 15 vision-capable models), **document RAG** (PDF/doc upload, server-side extraction), **image generation** (22 models), **video generation** (14 models, async signed-url → poll), and **TTS** (streaming MP3). Registered as provider `uc` (alias `ucn`). The metered OpenAI-compatible Developer API is a separate `uc-direct` provider. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(logs):** add copy-all button to request detail modal ([#11083](https://github.com/diegosouzapw/OmniRoute/pull/11083)) — thanks @AndrianBalanescu +- **feat(check):** make serviceKinds required and add the reverse-walk provider consistency gate ([#11392](https://github.com/diegosouzapw/OmniRoute/pull/11392)) — thanks @Tushar49 +- **feat(providers):** only advertise verified-working connections by default ([#11448](https://github.com/diegosouzapw/OmniRoute/pull/11448)) — thanks @alltomatos +- **feat(resilience):** add hierarchical concurrency admission ([#11493](https://github.com/diegosouzapw/OmniRoute/pull/11493)) +- **feat(providers):** derive + surface expiry for JWT-bearing web cookies (#11497) ([#11505](https://github.com/diegosouzapw/OmniRoute/pull/11505)) — thanks @oyi77 +- **feat(tunnels):** support named cloudflared tunnels via CLOUDFLARED_CONFIG ([#11561](https://github.com/diegosouzapw/OmniRoute/pull/11561)) — thanks @ggiak +- **feat(compression):** make proactive context-compression threshold a live setting ([#11564](https://github.com/diegosouzapw/OmniRoute/pull/11564)) — thanks @ragnar-claude +- **feat(resilience):** add provider peak-hour protection ([#11622](https://github.com/diegosouzapw/OmniRoute/pull/11622)) — thanks @Neuron-Mr-White +- **feat(providers):** add 1min.ai provider ([#11631](https://github.com/diegosouzapw/OmniRoute/pull/11631)) — thanks @zero-executioner +- **feat(dashboard):** collapsible JSON tree viewer for request/response payloads ([#11703](https://github.com/diegosouzapw/OmniRoute/pull/11703)) — thanks @hartmark +- **feat(routing):** add LiquidAI LFM2.5-2.6B free tier via OpenRouter ([#11752](https://github.com/diegosouzapw/OmniRoute/pull/11752)) — thanks @brick30llc-ctrl +- **feat(pricing):** add GLM-5.3-Flash pricing, model specs, and catalog registration ([#11830](https://github.com/diegosouzapw/OmniRoute/pull/11830)) — thanks @AndrianBalanescu +- **feat(routing):** add relayMode for schema-locked context handoffs ([#11839](https://github.com/diegosouzapw/OmniRoute/pull/11839)) — thanks @raheemuddin786 +- **feat(config):** add support for runtime system prompt configuration and hot-reloading ([#11841](https://github.com/diegosouzapw/OmniRoute/pull/11841)) — thanks @raheemuddin786 +- **feat(routing):** expand connection-aware quota prefilter across combo strategies (#11682) ([#11850](https://github.com/diegosouzapw/OmniRoute/pull/11850)) — thanks @HouMinXi +- **feat(models):** reactive discovery sync after Antigravity model-not-found 404 ([#11888](https://github.com/diegosouzapw/OmniRoute/pull/11888)) — thanks @MumuTW +- **feat(models):** auto-aliases and token limits from synced Antigravity discovery ([#11889](https://github.com/diegosouzapw/OmniRoute/pull/11889)) — thanks @MumuTW +- **feat(settings):** raise sticky round-robin limit caps to 1000 ([#12015](https://github.com/diegosouzapw/OmniRoute/pull/12015)) — thanks @adivekar-utexas +- **feat(resilience):** operator-configurable global credential health check interval ([#12043](https://github.com/diegosouzapw/OmniRoute/pull/12043)) — thanks @adivekar-utexas +- **feat(quota):** use official OpenCode Go usage API ([#12124](https://github.com/diegosouzapw/OmniRoute/pull/12124)) — thanks @ddarkr +- **feat(resilience):** default the credential health check sweep to 60 minutes ([#12138](https://github.com/diegosouzapw/OmniRoute/pull/12138)) — thanks @adivekar-utexas +- **feat(quality):** new-code mode for the complexity and dead-code ratchets (Clean as You Code) ([#12142](https://github.com/diegosouzapw/OmniRoute/pull/12142)) +- **feat(dashboard):** orchestration canvas — unified model + snapshot hook (part 1/2) ([#12156](https://github.com/diegosouzapw/OmniRoute/pull/12156)) +- **feat(usage):** add Kilo Code balance and Kilo Pass quotas ([#12178](https://github.com/diegosouzapw/OmniRoute/pull/12178)) — thanks @killer30001000 +- **feat(providers):** refresh vendored ChatGPT Web connector to v4.0.7 ([#12181](https://github.com/diegosouzapw/OmniRoute/pull/12181)) — thanks @backryun +- **feat(providers):** manual "Clear cooldown" action in the cooling panel ([#12224](https://github.com/diegosouzapw/OmniRoute/pull/12224)) — thanks @patrykkopycinski +- **feat(providers):** restore ChatGPT Web via clean-room browser transport ([#12239](https://github.com/diegosouzapw/OmniRoute/pull/12239)) — thanks @backryun +- **feat(sse):** wire the PROVIDER_PROFILES window gate into the global provider cooldown ([#12247](https://github.com/diegosouzapw/OmniRoute/pull/12247)) +- **feat(providers):** modernize CLOVA Studio chat and embeddings ([#12277](https://github.com/diegosouzapw/OmniRoute/pull/12277)) — thanks @backryun +- **feat(browser):** adopt Obscura as primary headless browser engine with Chromium fallback ([#12286](https://github.com/diegosouzapw/OmniRoute/pull/12286)) — thanks @opensource-elearning +- **feat(sse):** full language parity for output styles (es/de/fr/it/ru/zh + autoDetect) ([#12289](https://github.com/diegosouzapw/OmniRoute/pull/12289)) +- **feat(quality):** complete test:scoped — --full map rebuild, stdin selection, CI loader parity (#8084 D1) ([#12353](https://github.com/diegosouzapw/OmniRoute/pull/12353)) +- **feat(providers):** refresh Fable, Cursor, and Devin catalogs ([#12367](https://github.com/diegosouzapw/OmniRoute/pull/12367)) — thanks @backryun +- **feat(video):** redact transcript text from logs and durable memory (#12150 P1) ([#12427](https://github.com/diegosouzapw/OmniRoute/pull/12427)) +- **feat(dashboard):** parent-link, genuine-continuation badge, and modal perf fixes ([#12448](https://github.com/diegosouzapw/OmniRoute/pull/12448)) — thanks @hartmark +- **feat(dashboard):** adaptive context-budget dial on compression panel ([#12488](https://github.com/diegosouzapw/OmniRoute/pull/12488)) — thanks @HouMinXi +- **feat(providers):** refresh Z.ai Web models and browser transport ([#12524](https://github.com/diegosouzapw/OmniRoute/pull/12524)) — thanks @backryun +- **feat(video):** redact raw client-snapshot transcript fields in the detailed log (#12150 P2) ([#12528](https://github.com/diegosouzapw/OmniRoute/pull/12528)) +- **feat(providers):** refresh NVIDIA hosted models ([#12538](https://github.com/diegosouzapw/OmniRoute/pull/12538)) — thanks @backryun +- **feat(quota):** Moonshot Open Platform balance and TPD lock for custom nodes ([#12590](https://github.com/diegosouzapw/OmniRoute/pull/12590)) — thanks @HouMinXi +- **feat(video):** redact transcript in the in-memory pending-request snapshot (#12430 item 6) ([#12596](https://github.com/diegosouzapw/OmniRoute/pull/12596)) +- **feat(dashboard):** link a log entry's Conversation Context to its owning conversation ([#12646](https://github.com/diegosouzapw/OmniRoute/pull/12646)) — thanks @hartmark +- **feat(catalog):** eligibility-gated free-tier bucket ([#12669](https://github.com/diegosouzapw/OmniRoute/pull/12669)) +- **feat(db):** fail-closed previous_response_id continuation for redacted video turns (#12150 P2b) ([#12707](https://github.com/diegosouzapw/OmniRoute/pull/12707)) +- **feat(gamification): pay the documented `streak_bonus` and `badge_unlock` XP rewards.** `XP_REWARDS` listed both rewards but the award pipeline never paid them: the private reward table in `events.ts` omitted them, `updateStreak()` did not report when a streak extended, and badge unlocks carried no XP. Every request that extends a daily streak now pays `streak_bonus × streak length` once per UTC day (guarded by a same-day `xp_audit_log` check), and every badge unlocked through the pipeline pays `badge_unlock` once per badge (guarded by the `user_badges` primary key; `unlockBadge()` now reports whether it inserted). Bonus XP flows through the same `addXp` + level sync + global/weekly/monthly leaderboard path as action XP, so level-ups and rankings include it. The Radar supporter recognition unlock stays XP-free. (#12522 — thanks @pacocartones) +- **feat(providers):** advertise a `free-tier` capability in the provider plugin manifest for every provider with documented free models, so sidecars and dashboards can filter free-capable providers without reading the quota catalog ([#12786](https://github.com/diegosouzapw/OmniRoute/pull/12786)) — thanks @maxmad64bis +- **feat(providers):** Added EURouter as an OpenAI-compatible API-key gateway (`https://api.eurouter.ai/v1`), with live model discovery via `passthroughModels`. Its copy states that models are served by third-party upstreams listed per model, so an EU-based router is not read as EU data residency for inference. ([#13025](https://github.com/diegosouzapw/OmniRoute/pull/13025)) — thanks @ntdatt812 +- **feat(providers):** Added GreenPT as an OpenAI-compatible API-key provider (`https://api.greenpt.ai/v1`), with live model discovery via `passthroughModels`. No free-inference badge: the published docs describe a free API subscription billed per token, not a free tier. ([#13024](https://github.com/diegosouzapw/OmniRoute/pull/13024)) — thanks @ntdatt812 +- **feat(i18n):** 9 new locales — Greek (`el`), Croatian (`hr`), Serbian (`sr`), Lithuanian (`lt`), Estonian (`et`), Latvian (`lv`), Slovenian (`sl`), Maltese (`mt`), Irish (`ga`) — across the dashboard, docs mirrors, CLI, README and the site; OmniRoute now ships every official EU language (51 locales). (#13044) +- **feat(i18n):** 8 new locales — Kannada (`kn`), Malayalam (`ml`), Odia (`or`), Punjabi (`pa`), Nepali (`ne`), Sinhala (`si`), Burmese (`my`), Khmer (`km`) — across the dashboard, docs mirrors, CLI, README and the site (59 locales). The translator now restores the ICU literal escape around angle placeholders and splits oversized docs sections before translating. (#0000) ([#13660](https://github.com/diegosouzapw/OmniRoute/pull/13660)) +- feat(providers): **list Agnes 3.0 Flash as the current free chat model, drop retired 1.5 Flash, add Image 2.0/2.5 Flash plus Video 2.5/2.5 Flash, and discover the live `/v1/models` catalog (including the CN host `api.agnes-ai.cn`).** `agnes-1.5-flash` now forwards to `agnes-3.0-flash`. Video 2.5 polls `GET /v1/videos/{id}` (not the V2.0 `/agnesapi` contract). Live `/v1/models` (2026-09-09) no longer serves 1.5; the wiki marks it deprecated. 3.0 Flash is 512K context / 65,536 max output, same window as 2.5. CN-region keys use the existing per-connection base-URL field, default stays `apihub.agnes-ai.com`. ([#13120](https://github.com/diegosouzapw/OmniRoute/pull/13120)) — thanks @HouMinXi +- **feat(sse):** Codex and OpenAI catalogs list GPT-6 Astra with effort aliases (`-low` through `-ultra`); Codex CLI identity pins `@openai/codex@0.153.4` in lockstep with the image ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **feat(dashboard):** the orchestration History tab gained a "Compare runs" mode — toggling it turns each grid cell into a 2-item selection queue (a 3rd click drops the oldest pick), and picking two cells opens a side-by-side comparison panel instead of the usual detail drawer. The panel fetches both runs' detail the same way the drawer does (falling back to persisted history once a run leaves the live TTL window) and shows, per side: identity/source/state, start time, a signed `right - left` delta for duration/cost/event count, the event timeline aligned by index, and any memory hits. One side's fetch failing never blocks the other, and a delta is only ever computed when both sides have a finite value — otherwise it renders "—", never `NaN`. Comparing two runs from different sources or skills still works; a banner marks the deltas as informational rather than hiding them, since the two runs aren't a strict apples-to-apples pair. ([#12677](https://github.com/diegosouzapw/OmniRoute/pull/12677)) +- **feat(dashboard):** badge a conversation that never reached a clean stop ([#12717](https://github.com/diegosouzapw/OmniRoute/pull/12717)) — thanks @hartmark +- **feat(release):** reconcile-changelog tool + version-anchored fragment aggregation ([#12987](https://github.com/diegosouzapw/OmniRoute/pull/12987)) +- **feat(mcp):** carry context_length through MCP list_models_catalog projection (#12776) ([#13406](https://github.com/diegosouzapw/OmniRoute/pull/13406)) — thanks @KooshaPari +- **feat(cli):** make startup readiness budget configurable (#13369) ([#13433](https://github.com/diegosouzapw/OmniRoute/pull/13433)) — thanks @KooshaPari + ### 🐛 Bug Fixes +- **security(streaming):** sanitize generic mid-stream error messages before emitting OpenAI, + Responses, or Claude SSE failure frames and before diagnostic logging, while preserving raw + failures for internal classification and keeping client disconnects out of provider failure state. ([#12457](https://github.com/diegosouzapw/OmniRoute/pull/12457)) +- **fix(claude):** `blockExtraUsage: false` no longer lets 5h quota preflight skip the account; extra usage is billed after the session bar is gone, so the request must reach Anthropic ([#12803](https://github.com/diegosouzapw/OmniRoute/pull/12803)) — thanks @HouMinXi +- Sanitize HuggingChat conversation-creation and message-send transport failures before they reach client error bodies or provider logs. ([#12467](https://github.com/diegosouzapw/OmniRoute/pull/12467)) +- **fix(dashboard):** Keep local and theme-aware provider SVG icons at a definite layout size so Chromium does not collapse them to 0×0 after the v3.8.50 image-rendering change ([#12054](https://github.com/diegosouzapw/OmniRoute/pull/12054)) — thanks @ponkcore +- **fix(security):** Sanitize provider and runtime failures before public API, SSE and MCP responses and before persistent request, proxy and usage logs, preventing credentials, stack traces and host filesystem paths from crossing those boundaries while preserving stable error codes and useful diagnostics. ([#12506](https://github.com/diegosouzapw/OmniRoute/pull/12506)) +- **fix(cli):** `omniroute tunnel create` no longer crashes with `Cannot read properties of undefined (reading optsWithGlobals)` — removed the duplicate positional argument that caused Commander.js to misalign the action callback parameters ([#12295](https://github.com/diegosouzapw/OmniRoute/issues/12295)) ([#12368](https://github.com/diegosouzapw/OmniRoute/pull/12368)) — thanks @gonisulaimann +- Stop advertising Gemini Live-only models as supported audio endpoints until OmniRoute proxies the bidirectional Live protocol. ([#11710](https://github.com/diegosouzapw/OmniRoute/pull/11710)) — thanks @RaviTharuma +- **fix(kie):** reroute `flux/kontext` off the KIE Market `createTask` flow — it is catalogued with `isMarket: true` but has no Market catalog page, so KIE rejected it with "model name not supported"; it now hits the dedicated `POST /api/v1/flux/kontext/generate` / `GET /api/v1/flux/kontext/record-info` endpoints instead (#11296). ([#11985](https://github.com/diegosouzapw/OmniRoute/pull/11985)) — thanks @hartmark +- **fix(kie):** correct 12 more KIE Market catalog ids that were sent to `createTask` unchanged but diverge from KIE's documented upstream `model` values — GPT Image 2 T2I/I2I (drops the `gpt/` prefix), GPT Image 1.5 T2I/I2I (`gpt-image/` namespace), Seedream 5.0 Lite T2I/I2I (drops the `.0`), all 4 Flux 2 variants (`flux-2/` namespace, generic variant renamed `flex`), and Wan 2.7 Image / Image Pro (dash instead of dot) — each verified individually against the literal example request published on docs.kie.ai. `#11326`'s "everything else already matches" claim was wrong a second time (#11296); `z-image/4.0-*`/`z-image/4.5-*` and `flux/kontext` remain open, documented as unresolved in `KIE_MARKET_UPSTREAM_MODEL_IDS`'s comment pending further verification. ([#11667](https://github.com/diegosouzapw/OmniRoute/pull/11667)) +- **fix(dashboard):** `useApiKeySave.handleSaveApiKey` no longer forces a full upstream `/models` catalog sync on every non-curated provider connection save — callers can now pass `skipModelSync: true` to opt out, so a workflow that only wants to add one manual model no longer floods the provider's available-models list with hundreds/thousands of synced entries. The flag is a client-side intent signal only and is stripped before the connection payload is POSTed to `/api/providers`; default behavior (full sync on save) is unchanged when the flag is omitted (#11324) ([#11664](https://github.com/diegosouzapw/OmniRoute/pull/11664)) +- **Provider connections:** keep `tokenExpiresAt` when a connection is created. The create-path allowlist omitted it, so every insert stored NULL and the dashboard token badge could read a fresh connection as expired until its first background refresh ([#11368](https://github.com/diegosouzapw/OmniRoute/pull/11368)). — thanks @ntdatt812 +- **fix(combos):** the combo builder's precision-select, global-model-search, and manual-entry flows now serialize a model step's `model` string using the provider's already-computed routing-alias prefix (e.g. `oc/`) instead of rebuilding it from the raw canonical `providerId`, fixing the no-auth "OpenCode Free" provider (`opencode`) being routed to the unrelated paid "OpenCode Zen" provider (`opencode-zen`) because `opencode` doubles as a manual routing-prefix override ([#11433](https://github.com/diegosouzapw/OmniRoute/issues/11433)). ([#11670](https://github.com/diegosouzapw/OmniRoute/pull/11670)) +- **fix(radar):** the catalog feed cache now keeps `generatedAt`, the date the feed's data was built, next to `fetchedAt`, the date this install downloaded it (#11435). The feed schema requires that date and the sync path validates it, but the cache dropped it — so a feed fetched minutes ago and one carrying weeks-old figures looked identical to everything downstream, including the dashboard's "Last fetched" line. `getRadarCatalog().meta` and `GET /api/radar/status` now report both dates, the latter as its own field rather than folded into `version` — and omitted entirely for the offers and intel caches, which keep no build date, where a `null` would read as "unknown" rather than "never stored". The dashboard still shows only the fetch time; surfacing the build date there needs a new translated label and is left to a follow-up. Rows cached before migration 163 read back as `null`: unknown stays unknown instead of borrowing the fetch time. The referrals cache has persisted the same date since migration 142. — thanks @maxmad64bis +- **fix(cli):** postinstall no longer fills `JWT_SECRET` and `API_KEY_SECRET` in the installed package's `.env` (#11436). `.env.example` ships both blank on purpose: the server restores them from its durable store, or generates and persists them there, in `ensureSecrets()`. Pre-filling them defeated that — the file lives inside the package directory, so `npm i -g` replaced it and postinstall wrote _different_ values, while `ensureSecrets()` (which only acts on an empty variable) never got to restore the real ones. Both secrets rotated silently on every update, invalidating dashboard sessions and API-key CRCs. `STORAGE_ENCRYPTION_KEY` left the same list for the same reason in #1622; its comment pointed at a function that no longer exists and now names the real provisioning path. — thanks @maxmad64bis +- **fix(cli):** the CLI now says when a loaded `.env` lives inside the installed package directory (#11437). It already announces every env file it reads, without distinguishing the ones that survive an update from the one that does not: `npm i -g` replaces the package directory wholesale, so values set there are gone at the next update, silently. The warning names the durable path to move them to, and fires only when that file actually supplied a value — a file entirely shadowed by a durable one supplied nothing. A development checkout stays silent: there the same path is stable and documented in `SETUP_GUIDE.md`. — thanks @maxmad64bis +- **fix(free-models):** the shared `isFreeModel()` predicate no longer reports catalog entries marked `freeType: "discontinued"` as free, so `hidePaidModels` can't route to Pollinations' seven premium models that now require a paid key ([#11441](https://github.com/diegosouzapw/OmniRoute/pull/11441)) — thanks @maxmad64bis +- fix(ui): let AnySearch use the normal provider-icon fallback when LobeHub has no matching icon (#11449) +- Fixed the v3.8.50 Costs and Analytics dashboards so flat-rate Claude Code usage can be shown as an explicitly requested token-price estimate without changing default billed-cost semantics. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed archived usage retention so each request is priced individually instead of pricing a day's summed tokens once, which understated archived cost whenever a day mixed cache-heavy and ordinary requests. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed the Costs dashboard so it discloses when displayed figures include flat-rate token-price estimates instead of labelling them as billed spend, using the flag the analytics API already returns; the month-end projection and the CSV/JSON exports carry the same marker, and billed-cost mode is unchanged. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- **fix(combo):** attach the same combo-diagnostics trace (`poolSize`/`attemptOrder`/`excluded`/`terminalReason`, plus `x-omniroute-combo-*` headers) to the round-robin strategy's and the nested pipeline/fusion runtime-unit loop's "Maximum combo retry limit reached" 503 that the priority-strategy path already attaches for the identical terminal condition — previously those two paths returned a bare, contextless 503 ([#11462](https://github.com/diegosouzapw/OmniRoute/issues/11462)). ([#11665](https://github.com/diegosouzapw/OmniRoute/pull/11665)) +- **fix(bun):** use native `bun:sqlite` in `bootstrap-env` and `sync-env` to avoid loading `better-sqlite3` N-API addon during Bun startup ([#11468](https://github.com/diegosouzapw/OmniRoute/pull/11468)) — thanks @TheDemonTuan +- **fix(docker-bun):** make `Dockerfile.bun` install reliable with pre-install script helpers and native `bun:sqlite` smoke check ([#11470](https://github.com/diegosouzapw/OmniRoute/pull/11470)) — thanks @TheDemonTuan +- **fix(docker-bun):** remove every vendored `better-sqlite3` native addon from the Bun runtime image so startup cannot abort before the `bun:sqlite` fallback ([#11482](https://github.com/diegosouzapw/OmniRoute/pull/11482)) — thanks @TheDemonTuan +- **fix(autoCombo):** effort/alias model variants (`gpt-5.6-sol-xhigh`, `gpt-5.6`, cursor's `claude-4.6-opus-high`) inherit their base model's task fitness instead of falling to the wildcard 0.5 ([#11492](https://github.com/diegosouzapw/OmniRoute/pull/11492)) — thanks @MumuTW +- fix(db): rate-limit repeated Arena ELO leaderboard fetch-failure warnings instead of logging one per sync attempt (#11500) ([#11989](https://github.com/diegosouzapw/OmniRoute/pull/11989)) — thanks @hartmark +- **fix(db):** dedupe the raw `[Encryption] Decryption failed...` log line emitted by the lazy-decrypt views (`createLazyRowProxy`/`createLazyConnectionView`), which power `getProviderConnections()` and were re-triggering that line on every CredentialHealth/model-sync cycle for the same corrupt or stale-key credential — a fresh Proxy over a fresh row on every cycle meant the per-proxy memoization never suppressed it, unlike the dedup `decryptConnectionFields()` already had since [#9927](https://github.com/diegosouzapw/OmniRoute/issues/9927). Now shares that dedupe tracking so the line logs at most once per credential ([#11500](https://github.com/diegosouzapw/OmniRoute/issues/11500)). ([#11678](https://github.com/diegosouzapw/OmniRoute/pull/11678)) +- **fix(intelligence):** Arena ELO sync stores a synthesized base-model row for every effort/harness variant (`claude-opus-5-max` → `claude-opus-5`, `gpt-5.6-sol-xhigh (codex-harness)` → `gpt-5.6-sol`), so bare model requests reach the synced intelligence layer instead of the static fallback table; the hand-maintained `MODEL_ALIAS_MAP` — which collapsed generations (`gpt-5.5` → `gpt-5`) — is gone ([#11506](https://github.com/diegosouzapw/OmniRoute/pull/11506)) — thanks @MumuTW +- **fix(autoCombo,sse):** catalog hygiene — the static task-fitness table no longer ranks retired models above live ones (dead rows dropped, patterns matched on segment boundaries), the 7 deprecation aliases that forwarded to retired or non-existent models now point at the vendors' published replacements, an alias is no longer rewritten when the serving provider offers the id as-is, and a new `check:model-lifecycle` gate diffs both tables against a vendor lifecycle snapshot ([#11507](https://github.com/diegosouzapw/OmniRoute/pull/11507)) — thanks @MumuTW +- **fix(sse):** stop dropping resolved `thoughtSignature` values on parallel (multi tool-call) turns sent to Gemini 3.x — the claude→gemini and openai→gemini translators previously kept the signature only on the *first* function call of a message, causing Gemini to reject subsequent calls in the same turn with HTTP 400 "Function call is missing a thought_signature"; each function call now keeps its own resolved signature ([#11510](https://github.com/diegosouzapw/OmniRoute/issues/11510)). ([#11669](https://github.com/diegosouzapw/OmniRoute/pull/11669)) +- Treat Docker publishing from a non-default release branch as an expected skip instead of a failed workflow, while preserving the guard that prevents it from overwriting the `next` channel. ([#11527](https://github.com/diegosouzapw/OmniRoute/pull/11527)) — thanks @pacocartones +- fix(search): prefer credentialed providers over duckduckgo-free fallback (#11524) — the fallback-only loop ran before the credentialed-providers loop in `executeWebSearch`, making configured providers unreachable when `duckduckgo-free` was available. ([#11565](https://github.com/diegosouzapw/OmniRoute/pull/11565)) — thanks @vsd2807 +- **fix(sse):** cap the upstream headers-wait phase for STREAMING requests to a client-realistic ceiling (110s, under Codex's own ~120s hard client-abort window) instead of the flat 10-minute `FETCH_TIMEOUT_MS` default — that default was 5x longer than the body-phase readiness watchdog's own adaptive bound, so a request whose upstream never returned any response at all (not even headers, e.g. a stalled NVIDIA target behind a tool-heavy Responses→Chat translation) kept the client connection alive on keepalives only, guaranteeing the client's own patience ran out first with an opaque 499 instead of OmniRoute detecting and failing the stall fast. Non-streaming requests are unaffected — they keep the existing flat default (`open-sse/utils/fetchStartTimeoutPolicy.ts`) (#11526) ([#11668](https://github.com/diegosouzapw/OmniRoute/pull/11668)) +- **fix(ci):** protocol-clients E2E harness boots the peer-stamped custom server (`run-next.mjs dev`) instead of the bare `next` CLI, so `LOCAL_ONLY` locality resolves from the real TCP peer stamp and `/api/mcp/audit` answers 200 (open bootstrap preserved via an `OMNIROUTE_E2E_BOOTSTRAP_MODE=open` credential clear in `run-next.mjs`, pinned to loopback `HOST`) instead of a deterministic 403 (#11535) ([#11549](https://github.com/diegosouzapw/OmniRoute/pull/11549)) — thanks @jonlwheat2-gif +- **fix(docs):** the Auto-Combo engine is described with the number of factors it actually declares. Four different counts were in circulation — 6, 9, 13 and 14 across `AGENTS.md`, four guides and six code comments — while `DEFAULT_WEIGHTS` declares 15. `check:docs-counts` now reads that number from the source, so adding a factor without updating the prose fails CI ([#11545](https://github.com/diegosouzapw/OmniRoute/pull/11545)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **fix(quota-share):** in-flight leases are per request, so an aborted request's slot ages out instead of being kept alive by later traffic on the same connection ([#11547](https://github.com/diegosouzapw/OmniRoute/pull/11547)) — thanks @abhisheksharma2411 +- The free-tier summary route now serves the refreshed Radar catalog when the feed is active — the same numbers the dashboard shows — and states which catalog answered plus its real build date, instead of always reporting release-frozen figures with a stale curation date. The supporter-key live feed stays reserved to authenticated callers of the instance. ([#11550](https://github.com/diegosouzapw/OmniRoute/pull/11550)) — thanks @maxmad64bis +- **sse:** fix LiveWS/embed-WS servers crashing at startup under the Node/tsx runtime — `liveServer.ts`, `embedWsProxy.ts` and `apiBridgeServer.ts` imported `@/shared/utils/httpClientAbortGuard` without the `.mjs` extension, so the client-abort crash guard added by [#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556) was unreachable and every dependent test failed with `ERR_MODULE_NOT_FOUND` ([#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556)). ([#11671](https://github.com/diegosouzapw/OmniRoute/pull/11671)) — thanks @jonlwheat2-gif / @yxyxy +- fix(chat): shed any of the matched provider-node's routing identifiers (public prefix or internal id) before model lookup, so composites like `//` no longer reach upstream verbatim (#11557) — thanks @maxmad64bis +- **fix(cache):** a semantic-cache entry now expires on its TTL instead of surviving until the next UTC midnight, and `dbEntries` stops counting expired rows ([#11573](https://github.com/diegosouzapw/OmniRoute/pull/11573)) — thanks @ntdatt812 +- **fix(api):** `/v1/models` schedules its stale-while-revalidate rebuild through Next's `after()` again, so a stale catalog reaches the client before the rebuild blocks the event loop ([#11574](https://github.com/diegosouzapw/OmniRoute/pull/11574)) — thanks @ntdatt812 +- **fix(check):** the `known-symbols` executor conformance gate awaits the now-async `getExecutor()`, so it stops reporting all 142 registered aliases as dead symbols and can detect a lazy import that fails ([#11582](https://github.com/diegosouzapw/OmniRoute/pull/11582)) — thanks @ntdatt812 +- **fix(pack):** register `bin/cli/utils/volatileEnvPath.mjs` in `PACK_ARTIFACT_REQUIRED_PATHS`, so the prune can no longer silently drop a module the published CLI imports on every boot ([#11588](https://github.com/diegosouzapw/OmniRoute/pull/11588)) — thanks @ntdatt812 +- **i18n(vi):** translate the five reliability-column strings on the free-provider rankings page, which rendered as `__MISSING__:` markers in the Vietnamese UI ([#11589](https://github.com/diegosouzapw/OmniRoute/pull/11589)) — thanks @ntdatt812 +- **resilience:** restore the expired-connection retry-budget probe in the token-health sweep — the `!isGitHubAccessTokenOnlyConnection` carve-out reintroduced by #11608 contradicted the boundary pinned by #11592, so a GitHub connection parked at `expired` with retry budget remaining was never probed and could never self-heal ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)). ([#11672](https://github.com/diegosouzapw/OmniRoute/pull/11672)) — thanks @jonlwheat2-gif / @ntdatt812 +- **fix(dashboard):** Provider Stats now displays combo success rates on their native 0–100 percentage scale instead of multiplying them twice ([#11595](https://github.com/diegosouzapw/OmniRoute/pull/11595)) — thanks @pacocartones +- **fix(providers):** route DuckDuckGo Web Chat through the verified `duck.ai` endpoint, refresh its free model catalog, and avoid forwarding unsolved anti-abuse challenges ([#11597](https://github.com/diegosouzapw/OmniRoute/pull/11597)) — thanks @solstxce +- **fix(ui):** Console log Refresh and Copy controls now expose localized accessible names, keep copy actions visible on keyboard focus, and announce copy completion safely ([#11599](https://github.com/diegosouzapw/OmniRoute/pull/11599)) — thanks @pacocartones +- Dropped the stale-`.eslintcache` `restore-keys` fallback from both "Restore ESLint file cache" steps in `ci.yml`, so the blocking `Lint` job can no longer be served per-file verdicts computed under a different lint config, suppressions file or lockfile. `quality.yml` had already dropped it in #11963; `ci.yml` — the workflow that actually gates PRs — had not (#11600). ([#11996](https://github.com/diegosouzapw/OmniRoute/pull/11996)) +- fix(ci): pass `--pass-on-unpruned-suppressions` in `run-eslint-json.mjs` so the CI Lint job no longer fails when a suppression is merely orphaned by a genuine fix, mirroring the identical fix already in `validate-release-green.mjs` (#11600) ([#11983](https://github.com/diegosouzapw/OmniRoute/pull/11983)) — thanks @hartmark +- fix(handoff): enforce provider allowlist for universal handoff (#11602) — universal handoff now skips summarization when the selected summary model's provider is not included in the configured provider allowlist. ([#11619](https://github.com/diegosouzapw/OmniRoute/pull/11619)) — thanks @vsd2807 +- **fix(dashboard):** Show a stable error state when Search Analytics returns an HTTP error or malformed data ([#11603](https://github.com/diegosouzapw/OmniRoute/pull/11603)) — thanks @pacocartones +- **test(gamification):** pin the aggregate profile level to the XP-derived semantics of #11604 — `getAggregateXp()` now derives `currentLevel` from the summed XP (`calculateLevel(sum)`), not `MAX(stored current_level)`, and the #3484 fixture levels are aligned with the XP curve ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)). ([#11676](https://github.com/diegosouzapw/OmniRoute/pull/11676)) — thanks @jonlwheat2-gif / @pacocartones +- **fix(dashboard):** Keep the Profile level and progress aligned with aggregate XP, including bounded handling for invalid totals ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)) — thanks @pacocartones +- **fix(dashboard):** Prevent locked hidden badges from revealing their icon or opening private badge details before they are earned ([#11605](https://github.com/diegosouzapw/OmniRoute/pull/11605)) — thanks @pacocartones +- **fix(dashboard):** Restore keyboard focus after shared modals close and cancel delayed autofocus during cleanup ([#11607](https://github.com/diegosouzapw/OmniRoute/pull/11607)) — thanks @pacocartones +- **fix(dashboard):** Enable Enter and Space activation for clickable data-table rows without hijacking nested controls ([#11610](https://github.com/diegosouzapw/OmniRoute/pull/11610)) — thanks @pacocartones +- **fix(autoCombo,sse):** vendor-retired catalog ids are dropped from the auto-combo candidate pool and no longer win on leftover `arena_elo` / `user_override` rows; `getModelLifecycleDecision` consults `model-lifecycle.json` (prefix-stripped) so aggregator traffic is not `untracked` for ids the snapshot already knows ([#11625](https://github.com/diegosouzapw/OmniRoute/issues/11625)) ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)) — thanks @MumuTW +- **test(chatcore):** move Codex/Claude combo fixtures off models the lifecycle guard now rejects — `gpt-5.1-codex`/`gpt-5-codex` are vendor-retired (snapshot, #11626) and `claude-3-5-sonnet-20241022` is shut down, so native-passthrough and combo-fallback tests switched to `gpt-5.6-sol` and `claude-sonnet-4.6` ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)). ([#11675](https://github.com/diegosouzapw/OmniRoute/pull/11675)) — thanks @jonlwheat2-gif / @MumuTW +- fix(opencode-plugin): stop a free-tier budget that rounds up across a magnitude from rendering as `1000K`/`1000M` in the model picker — `fmtTokens` chose its unit from the raw token count and then rounded with `toFixed(1)`, so 999,950–999,999 printed as `1000K` rather than `1M` and just under a billion printed as `1000M` rather than `1B` ([#11684](https://github.com/diegosouzapw/OmniRoute/pull/11684)) — thanks @abhisheksharma2411 +- Cap prose-derived Antigravity quota resets at the configured model cooldown maximum while preserving authoritative Retry-After headers and Google RetryInfo hints. (#11695) Thanks @pacocartones. ([#11823](https://github.com/diegosouzapw/OmniRoute/pull/11823)) +- Compare `_rebaseline_*` notes structurally in the ratchet-bank verifier so an object-valued note no longer reports itself as rewritten on an unmodified tree — the nightly `bank-ratchet-shrinks` lane had been aborting on every run since 2026-08-11, leaving 41,672 lines of earned file-size shrinks unbanked (#11702). ([#11892](https://github.com/diegosouzapw/OmniRoute/pull/11892)) — thanks @MumuTW +- **fix(build):** `prepublish.ts` bundles the ChatGPT Web (Codex) MCP bridge through `runBuildTool()` instead of spawning `npx.cmd` raw, fixing the build crash on Node ≥ 20/Windows where `.cmd` shims cannot be spawned without a shell (EINVAL) ([#11704](https://github.com/diegosouzapw/OmniRoute/issues/11704)) ([#11706](https://github.com/diegosouzapw/OmniRoute/pull/11706)) — thanks @jonlwheat2-gif +- fix(codex): keep `parallel_tool_calls:false` on the translated Codex Responses Lite path (#11707) ([#11984](https://github.com/diegosouzapw/OmniRoute/pull/11984)) — thanks @hartmark +- **fix(tests):** the new Gemini Web Images retirement test (#11708) called the now-async `getExecutor()` synchronously, throwing `TypeError: getExecutor(...).getProvider is not a function` instead of asserting Gemini Web chat stays available. Awaited to match the R0.3 executor-registry refactor (#11220). ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(search):** Allow REST and MCP web-fetch requests to select the advertised `anysearch-search` provider explicitly ([#11716](https://github.com/diegosouzapw/OmniRoute/pull/11716)) — thanks @pacocartones +- **fix(providers):** Keep overnight peak-hour protection active after midnight according to the window's configured start day ([#11718](https://github.com/diegosouzapw/OmniRoute/pull/11718)) — thanks @pacocartones +- **fix(providers):** correct all five g4f.space gateway descriptions to document proof-of-work credits or member API keys, variable limits, and the remote prompt boundary instead of promising free keyless access at five requests per minute ([#11738](https://github.com/diegosouzapw/OmniRoute/pull/11738)). +- **fix(db):** `model_capabilities` is created by a migration instead of lazily on the first models.dev sync, so a clean install and an upgraded install converge on the same schema regardless of which features have run ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(ci):** `check:install-upgrade` now fails on an `npm` install truncated by ENOSPC (npm reports it as a warning and still exits 0), authenticates its health probe so the version assertion works against the hardened health payload, frees the clean-install tree before the upgrade phase, and no longer reports a schema divergence computed from a boot that never served ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(routing):** harden session-affinity key derivation against oversized explicit IDs and empty structural-payload collisions, while recognizing Gemini and common text request shapes ([#11746](https://github.com/diegosouzapw/OmniRoute/pull/11746)) — thanks @MumuTW +- **fix(packages/browser-pool):** regenerate `package-lock.json` so the `packages/browser-pool` workspace's locked `playwright`/`@types/node` (and transitives) match its `package.json` specs, fixing cache-only/offline installs (`npm ci --offline`, Nix `buildNpmPackage`) that previously failed with `ENOTCACHED` ([#11747](https://github.com/diegosouzapw/OmniRoute/issues/11747)) — thanks @benjaminkitt ([#11784](https://github.com/diegosouzapw/OmniRoute/pull/11784)) — thanks @geekyNads +- **fix(assets):** Stop distributing 79 provider and CLI logos without verified source/license provenance, keep every provider/tool available through the internal generic icon, and prevent those IDs from falling through to an external icon CDN. ([#11750](https://github.com/diegosouzapw/OmniRoute/pull/11750)) +- **fix(providers):** retire the common `chatgpt-web` / `cgpt-web` provider because its pre-key and proof-of-work provenance could not be cleared ([#11754](https://github.com/diegosouzapw/OmniRoute/pull/11754)) — explicit chat and image requests, slash-prefixed models, persisted aliases, proxy overrides, and combo targets now fail closed with a sanitized HTTP `410` / `PROVIDER_RETIRED` before upstream dispatch. Migration 163 tombstones matching connections and invalidates their active leases without deleting history or widening API-key allowlists. The independent `chatgpt-web-codex` / `cgpt-codex` provider and its MIT-noticed vendored implementation remain available. +- **fix(usage):** quota and usage refresh no longer 409 when an exclusive lease reserves the connection ([#11758](https://github.com/diegosouzapw/OmniRoute/pull/11758)) — thanks @TheDemonTuan +- Keep the embedding registry's vector width and `embedding` type on models when a synced model exists for the same id, so `/v1/models` no longer reports registry-described embedding models widthless or untyped (#11761) — thanks @rahilmavani +- Correct `google/gemini-embedding-001` on the OpenRouter route to 3072 dimensions, the width it returns when `dimensions` is not sent (#11761) — thanks @rahilmavani +- **fix(vision-bridge):** Restrict automatic vision routing and fallbacks to models available in authoritative live provider catalogs, including aliases and effort variants ([#11767](https://github.com/diegosouzapw/OmniRoute/issues/11767)) — thanks @pacocartones ([#11813](https://github.com/diegosouzapw/OmniRoute/pull/11813)) +- **fix(providers):** reclassify Cerebras as a one-time $5 signup credit (payment method required, 30-day validity), not a recurring no-card 1M tokens/day trial ([#11773](https://github.com/diegosouzapw/OmniRoute/issues/11773)) ([#12591](https://github.com/diegosouzapw/OmniRoute/pull/12591)) — thanks @HouMinXi +- fix(cli): stop prepublish from re-rebuilding the already-built ESM-only opencode-plugin dist (#11787) ([#11990](https://github.com/diegosouzapw/OmniRoute/pull/11990)) — thanks @hartmark +- **fix(db):** Fresh installs no longer log a non-fatal `no such table: compression_run_telemetry` warning when retention cleanup runs before the lazily-created telemetry table exists ([#11802](https://github.com/diegosouzapw/OmniRoute/pull/11802)) — thanks @RaviTharuma +- fix(sse): set X-OmniRoute-Selected-Connection-Id on successful combo dispatches so downstream consumers stop falling back to an empty connection id (#11810) ([#11986](https://github.com/diegosouzapw/OmniRoute/pull/11986)) — thanks @hartmark +- **fix(services):** embedded CLIProxyAPI lifecycle checks now use public `/healthz`, while model discovery uses the configured dedicated data-plane API key instead of the management password ([#11811](https://github.com/diegosouzapw/OmniRoute/pull/11811)) — thanks @RaviTharuma +- Fix persisted model steps returning an error in the playground route simulator, and warn when structural steps cannot be simulated. ([#11862](https://github.com/diegosouzapw/OmniRoute/pull/11862)) — thanks @pacocartones +- **fix(providers):** Antigravity's dynamic mitmAlias table no longer routes `gemini-3.7-flash-{high,medium,low}` to a literal tier-suffixed upstream id just because one connected account's own discovery listed it directly — those display ids always resolve through the safe `gemini-3.7-flash-tiered` static alias, so one account's Google-provisioned access no longer 404s every sibling account of the provider ([#11824](https://github.com/diegosouzapw/OmniRoute/issues/11824), [#11651](https://github.com/diegosouzapw/OmniRoute/issues/11651)) ([#11988](https://github.com/diegosouzapw/OmniRoute/pull/11988)) — thanks @hartmark +- **fix(dashboard):** Expose Profile loading, errors, page structure, and XP progress to assistive technologies ([#11838](https://github.com/diegosouzapw/OmniRoute/pull/11838)) — thanks @pacocartones +- **fix(socks):** `Agent.connectTimeout` now bounds both SOCKS handshake and TLS connect, and `family === null` no longer falls back to `fetch-socks` ([#11842](https://github.com/diegosouzapw/OmniRoute/pull/11842)) — thanks @maxmad64bis +- **perf(compression):** OOM mitigations for large payload hashing, memoization, and token estimation ([#11844](https://github.com/diegosouzapw/OmniRoute/pull/11844) — thanks @AndrianBalanescu) +- Excluded Next.js Node File Trace manifests (`*.nft.json`) from the published npm tarball. They are build-time metadata and are never read while serving, but had grown to 668.7 MB — 61% of the package — which pushed the upload past the registry limit and made `npm publish` fail with `413 Payload Too Large`. ([#11864](https://github.com/diegosouzapw/OmniRoute/pull/11864)) +- **fix(cli):** keep the global OmniRoute server `--base-url` separate from provider-node `--endpoint` values in add, update, and validate commands ([#11860](https://github.com/diegosouzapw/OmniRoute/pull/11860)) — thanks @pacocartones +- **fix(config):** Nous Research's `Hermes-4-405B` model now displays as "Hermes 4 405B (Nous Research)" in both the provider registry and the free-model catalog, instead of the mislabelled "Hermes 4 7B" ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#11993](https://github.com/diegosouzapw/OmniRoute/pull/11993)) +- **fix(provider/nous):** inject required user= tag into Nous Research inference requests to resolve upstream 400 "missing tags" error ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#12044](https://github.com/diegosouzapw/OmniRoute/pull/12044)) +- **fix(build):** `npm run build` now fails in one second with a named package and a copy-pasteable fix when npm silently drops an externalised optional native dependency, instead of dying four minutes in with `Module not found: Can't resolve 'better-sqlite3'` ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- **fix(install):** `postinstall` no longer throws `ReferenceError: isAndroid is not defined` — failing the whole `npm install` — when the `better-sqlite3` rebuild fallback times out; the manual-fix guidance is reachable again ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- Fixed the Alibaba free-tier allowlist test that went red on its own once the shipped catalog's `validUntil` (2026-08-27) passed, leaving every PR and `main` with a failing `Unit Tests (1/8)`. The test now builds its own packs with dates it controls, and covers the expired-pack fallback that production has actually been serving. ([#11867](https://github.com/diegosouzapw/OmniRoute/pull/11867)) +- Split the npm registry upload into its own GitHub-hosted job. npm refuses `--provenance` from a self-hosted runner (`422 ... Only "github-hosted" runners are supported`), which blocked the v3.8.50 publish; the heavy verification cannot move to a hosted runner, so it now hands the proven tarball over instead. ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(ui):** the shared `DataTable` loading state no longer reads its decorative ⏳ glyph out to assistive technology, and now carries the same `role="status"` / `aria-live="polite"` / `aria-busy="true"` semantics as `PageLoading` ([#11869](https://github.com/diegosouzapw/OmniRoute/pull/11869)) — thanks @pacocartones +- **fix(api):** Bound the `timeout` accepted by `POST /api/providers/[id]/login` to the same 15s–600s window the Adobe Firefly and Conol browser-login paths already enforce, so a request can no longer pin a headful browser and the single active-login slot with an arbitrarily large poll budget. ([#11871](https://github.com/diegosouzapw/OmniRoute/pull/11871)) — thanks @pacocartones +- **fix(api):** Stop the tunnel and MITM routes from returning raw child-process error text, which disclosed host paths, binary install locations and Tailscale `tskey-*` credentials in a body some of these routes serve to non-loopback callers, and return a real 400 instead of a framework 500 when a tunnel request body fails validation. ([#11872](https://github.com/diegosouzapw/OmniRoute/pull/11872)) — thanks @pacocartones +- **fix(open-sse):** stream TTFT/ITL are sampled from a monotonic clock (`performance.now`) instead of `Date.now`, so an NTP correction or wall-clock jump can no longer inflate a genuine TTFT or produce a negative one that the `ttft >= 0` guard silently drops — protecting the router's OTel/EWMA/`usage_history`/speed-ranking signals ([#11873](https://github.com/diegosouzapw/OmniRoute/pull/11873)) — thanks @pacocartones +- **fix(providers):** xAI `reasoning_effort: "xhigh"` now reaches grok-4.6+ instead of being silently clamped to `"high"` ([#11879](https://github.com/diegosouzapw/OmniRoute/pull/11879)) — thanks @NoxzRCW +- **fix(dashboard):** the providers page no longer crashes into the error boundary when a provider id collides with an `Object.prototype` member (`constructor`, `__proto__`); icon lookups are own-property guarded ([#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)) — thanks @NoxzRCW +- **fix(skills):** injected skill tools declared in shorthand (`{"content": "string"}`) now forward valid JSON Schema, unblocking providers that validate tool schemas strictly such as Zhipu GLM on the Console Go tier ([#11881](https://github.com/diegosouzapw/OmniRoute/pull/11881)) — thanks @NoxzRCW +- **fix(api):** `POST /api/playground/simulate-route` now surfaces `combo-ref` and `provider-wildcard` persisted combo steps with a specific warning (naming the referenced combo, or the unresolved `provider/model` wildcard) instead of folding them into a generic "unsupported step" count; a `provider-wildcard` step is also now included as an unresolved target so the operator can see it is in the route (ported from [#11882](https://github.com/diegosouzapw/OmniRoute/pull/11882) — thanks @NoxzRCW). ([#11930](https://github.com/diegosouzapw/OmniRoute/pull/11930)) +- **fix(translator):** the streaming OpenAI→Claude translator keeps upstream usage, including prompt-cache tokens, when it arrives on a trailing `choices: []` chunk (Fireworks and any upstream using `stream_options.include_usage`) ([#11883](https://github.com/diegosouzapw/OmniRoute/pull/11883)) — thanks @NoxzRCW +- **fix(cli):** `omniroute update --apply` now tells you explicitly whether a running server was detected and, if so, that you must run `omniroute restart` to apply the update — it never restarted anything and previously implied the update was already live once files were installed. The dashboard's npm-mode Update flow (`/api/system/version`) now tries OmniRoute's own PID-file-managed supervisor before falling back to pm2, and reports an honest "restart required" step instead of a silent pm2-only "skipped" that read like a completed update. The server-side latest-version lookup backing the dashboard's update banner also gained `--prefer-online`, closing the same stale-npm-cache class already fixed in the CLI's own copy for #4376 ([#11885](https://github.com/diegosouzapw/OmniRoute/issues/11885)). ([#12005](https://github.com/diegosouzapw/OmniRoute/pull/12005)) +- **fix(resilience):** clear persisted LKGP pins when a target suffers connection/provider exhaustion or is skipped before dispatch due to cooldown/exhaustion/unavailability, preventing subsequent requests from repeatedly prioritizing known-dead providers ([#11911](https://github.com/diegosouzapw/OmniRoute/issues/11911)). ([#12013](https://github.com/diegosouzapw/OmniRoute/pull/12013)) — thanks @HouMinXi +- **fix(sse):** the OpenAI→Claude stream translator now defers the terminal `message_delta`/`message_stop` emission until the real usage block has arrived (or a genuine end-of-stream flush forces it) instead of emitting it immediately on `finish_reason` — previously, when the trailing usage-only chunk (`{"choices":[],"usage":{...}}`) arrived *after* the `finish_reason` chunk (the normal order for Fireworks/vLLM/Together and other `stream_options.include_usage` upstreams), the client-visible `message_delta` still carried stale/zero usage even though `state.usage` was internally corrected too late to matter (ported from [#11915](https://github.com/diegosouzapw/OmniRoute/pull/11915) — thanks @HouMinXi). ([#11933](https://github.com/diegosouzapw/OmniRoute/pull/11933)) +- **fix(sse):** `stripStore()` now forces `store=false` for stateless OpenAI-compatible Responses-API targets (unless the connection has explicitly opted in via `providerSpecificData.openaiStoreEnabled`), instead of only handling the `openai`/`agentrouter` cases — a client-supplied `store` value previously passed through untouched to backends that don't actually persist responses server-side ([#11916](https://github.com/diegosouzapw/OmniRoute/pull/11916) — thanks @HouMinXi). +- **fix(models):** custom provider-node models (synced, custom, and alias-backed) now appear under their configured prefix in the unified catalog when the operator's model-id prefix mode is canonical, instead of being dropped whenever alias-inclusion was otherwise disabled ([#11918](https://github.com/diegosouzapw/OmniRoute/pull/11918) — thanks @HouMinXi). +- **fix(models):** the unified model catalog now suppresses stale static registry models (including effort-tier variants) for any provider whose active connection has an authoritative live synced catalog, not only providers already using exclusive-synced-listing — a connection with `providerUsesAuthoritativeLiveCatalog` previously kept serving both the live-synced models and the stale static rows side by side ([#11919](https://github.com/diegosouzapw/OmniRoute/pull/11919) — thanks @HouMinXi). +- **fix(ui):** `ProviderIcon`'s three remaining unguarded lookups (`PROVIDER_ICON_ALIASES`, `LOCAL_SVG_ALIASES`, `THEMED_SVGS`) now use `Object.hasOwn()` own-property checks — a provider id such as `constructor` or `__proto__` previously resolved through the prototype chain instead of falling through to the unknown-provider CDN fallback (`getLobeProviderIcon()` itself was already guarded by [#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)); `ProviderPageHeader`'s `color` field is also now optional, matching the rest of the component's defensive typing (ported from [#11920](https://github.com/diegosouzapw/OmniRoute/pull/11920) — thanks @HouMinXi). ([#11935](https://github.com/diegosouzapw/OmniRoute/pull/11935)) — thanks @NoxzRCW +- fix(ollama): preserve multi-byte UTF-8 content split across stream chunks in the Ollama NDJSON transform, which previously corrupted CJK/emoji into U+FFFD (#11921) — thanks @pacocartones +- **fix(providers):** OrcaRouter chat requests now target `/v1/chat/completions` instead of the bare `/v1` API root, fixing the upstream `404 Invalid URL (POST /v1)` ([#11923](https://github.com/diegosouzapw/OmniRoute/pull/11923)). — thanks @echel0nn +- **fix(plugins):** deliver the `onStreamComplete` event to disk-installed plugins. The event shipped in v3.8.50 (#9669) was emitted internally but had no plugin-facing wiring, so no plugin could ever subscribe: the manifest schema silently dropped `hooks.onStreamComplete`, and the loader/manager only knew the seven legacy hooks. `onStreamComplete` is now a declarable manifest hook, wired through the loader and registered by the manager like the other hooks, and its payload carries a `requestId` so consumers can correlate the stream-completion event with the originating request ([#11934](https://github.com/diegosouzapw/OmniRoute/pull/11934)) — thanks @amaleta / @pacocartones +- **fix(db):** the Qdrant embedding-model dropdown now lists local/self-hosted providers (Ollama, LM Studio, vLLM, etc.) — an active connection is treated as "configured" when the provider allows an optional API key, not only when it has a real key or OAuth, so a running local embedding provider is no longer hidden from the picker ([#11949](https://github.com/diegosouzapw/OmniRoute/issues/11949)) ([#11995](https://github.com/diegosouzapw/OmniRoute/pull/11995)) +- **fix(providers):** Vertex AI Anthropic partner-model discovery now calls the Model Garden `v1beta1` publisher list (`/v1beta1/publishers/anthropic/models`, global) and parses its `publisherModels` envelope, so Claude models auto-synced from Vertex populate the active live catalog and route at request time instead of returning `Model '' is not available in the active live catalog` ([#11991](https://github.com/diegosouzapw/OmniRoute/issues/11991)) — thanks @fabioluissilva ([#11998](https://github.com/diegosouzapw/OmniRoute/pull/11998)) +- **fix(cli):** support `--base-url` alongside `--endpoint` in `omniroute nodes add`, `update`, and `validate` subcommands to prevent global `--base-url` shadowing issues ([#11999](https://github.com/diegosouzapw/OmniRoute/issues/11999)). ([#12033](https://github.com/diegosouzapw/OmniRoute/pull/12033)) — thanks @HouMinXi +- **fix(providers):** `cloudflare-ai` no longer refuses image content parts for every Workers AI model ([#12002](https://github.com/diegosouzapw/OmniRoute/pull/12002)) — the plain-string `content` requirement behind #2539 is carried by the _model_ schema, not by the `/ai/v1/chat/completions` endpoint (measured: an all-text part array returns 200 on `@cf/mistralai/mistral-small-3.1-24b-instruct`, `@cf/meta/llama-4-scout-17b-16e-instruct` and `@cf/meta/llama-3.3-70b-instruct-fp8-fast`, and 400 on the text-only `@cf/qwen/qwen2.5-coder-32b-instruct`). `transformRequest()` flattened every array and threw on the first non-text part (#6390), so image input was refused for vision-capable Cloudflare models that accept it. All-text arrays are still flattened — the one shape every model accepts — while an array carrying a non-text part is passed through untouched, so the attachment is still never silently dropped. Regression guards: `tests/unit/cloudflare-ai-image-parts-6390.test.ts`. — thanks @davidlinfr +- **fix(resilience):** decouple the limiter-managed execution backstop from the queue-wait budget — new `requestQueue.executionMaxWaitMs` (env `RATE_LIMIT_EXECUTION_MAX_WAIT_MS`, default 600000 = 10 min) now feeds Bottleneck's post-dispatch `expiration`, while `requestQueue.maxWaitMs` keeps its documented queue-wait semantics. Previously the queue-wait budget doubled as the execution expiration, so legitimate long-running calls on non-incremental gateways (whole generation buffered before the first upstream byte, e.g. Console Go / Command Code tiers serving GLM models) were killed mid-flight at the queue budget with a false 504 `RATE_LIMIT_EXECUTION_TIMEOUT` — the local limiter undercut the provider-aware upstream fetch-start timeouts. The surfaced 504 message now names `requestQueue.executionMaxWaitMs`; the error keeps the #4165 guarantees (disclaims an upstream timeout, preserves the Bottleneck error as `cause`, branded code + trusted provenance, classified request-scoped so combo falls back). A real queue-wait bound (the `Promise.race` around `limiter.schedule()` sketched in #9533) remains future work. (#12025) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **Call logs:** keep the `error` field when an artifact exceeds the storage cap, instead of replacing it with the omission marker. The error is the only field that says *why* a request failed and is typically ~90 bytes next to the multi-hundred-KB bodies that trip the cap, so dropping it left a size-limited row undiagnosable — a provider outage, a local timeout and an upstream 400 all rendered identically. It is now preserved at every fallback stage, truncated to 4KB if it is itself large ([#12026](https://github.com/diegosouzapw/OmniRoute/issues/12026)). ([#12095](https://github.com/diegosouzapw/OmniRoute/pull/12095)) — thanks @ntdat812 +- **fix(diagnostics):** preserve the error field (truncated to 4KB with a `[truncated: …]` suffix) in every call-log artifact size-limit fallback stage. Previously the minimal fallback replaced the error with `[omitted: call log artifact size limit exceeded]`, so an oversized artifact row showed nothing about WHY the request failed — e.g. 91 of 847 opencode-go 504 rows on one production instance were undiagnosable from the dashboard. Oversized request/response bodies are still omitted exactly as before; the error cap is independent of the payload sizes that tripped the fallback. (#12026) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **fix(sse):** OpenAI Responses clients that declare the native `web_search` tool now receive a spec-shaped `web_search_call` output item with `action.sources` alongside the preserved function-call round-trip, so search results executed through OmniRoute's own search backend are consumable by standard Responses clients (Codex, pi-web-access, …). ([#12031](https://github.com/diegosouzapw/OmniRoute/pull/12031)) — thanks @watchingdogs +- **fix(cli):** use in-thread alias resolver hooks on modern runtimes to avoid deprecation noise and improve Node.js forward compatibility ([#12073](https://github.com/diegosouzapw/OmniRoute/issues/12073)). ([#12083](https://github.com/diegosouzapw/OmniRoute/pull/12083)) — thanks @xiaoyaner0201 +- **fix(combo):** an operator-set **Agent Features → Context length** on a combo is now honored at request time. The value was persisted and advertised through `/v1/models`, but `resolveComboContextLimit()` never consulted it — so a multi-target combo whose members carry no per-model window fell through to the provider's generic `defaultContextLength` (openrouter 128000, command-code 200000) and rejected large requests with `Input exceeds context window … limit 128000` despite the combo being explicitly sized much larger. An identical single-target combo worked, because it collapses to its concrete target before the guard runs. Invalid values (0/negative/NaN/Infinity) are ignored, so the existing target → combo-min → fallback order is unchanged. ([#12090](https://github.com/diegosouzapw/OmniRoute/pull/12090)) — thanks @adivekar-utexas +- **fix(sse):** passthrough streams now estimate usage on finish when upstream closes without usage even with `stream_options.include_usage` — avoids `0 tokens / 0%` for providers that stay silent (and correctly handles trailing empty-choices usage) ([#12151](https://github.com/diegosouzapw/OmniRoute/pull/12151)) — thanks @maxmad64bis +- **fix(combos):** clearing an agent feature in the combos editor now persists — unchecking context cache protection, or emptying the system message or tool filter, sends an explicit `null` instead of dropping the field from the `PUT` body, which the update merge read as "leave unchanged" ([#12177](https://github.com/diegosouzapw/OmniRoute/pull/12177)) — thanks @foreveryh / @ntdatt812 +- **fix(memory):** self-hosted embedding endpoints now vectorize — the vector width is measured from the first embedding that comes back instead of being read from a registry that cannot describe them, so `vec_memories` is created and memories stop piling up unvectorized behind a green health check ([#12180](https://github.com/diegosouzapw/OmniRoute/pull/12180)) — thanks @kanade-hoshino / @ntdatt812 +- **fix(translator):** the leading `system` message now reaches Responses-API upstreams when its `content` is a content-part array — it was read as `typeof content === "string" ? content : ""`, so a prompt-caching client (Anthropic `cache_control`, the shape LiteLLM and the Anthropic SDK emit) had its entire system prompt replaced by an empty `instructions`. The request was still accepted with a normal `prompt_tokens` count, so the model answered with no instructions and nothing in the response said they were missing. Mid-conversation system turns already handled the array shape ([#7056](https://github.com/diegosouzapw/OmniRoute/pull/7056)); only the first one did not ([#12206](https://github.com/diegosouzapw/OmniRoute/issues/12206)). Regression guard: `tests/unit/translator-openai-responses-system-content-parts.test.ts`. ([#12207](https://github.com/diegosouzapw/OmniRoute/pull/12207)) — thanks @Gorillaz322 +- **fix(free-tier):** `/api/free-tier/summary` no longer computes its totals from a Radar feed built before the catalog the running release ships. When the cached feed is older — or carries no build date at all — the route answers from the shipped catalog, resolved through the operator's local model state so disabled and tombstoned models stay out of the numbers ([#12215](https://github.com/diegosouzapw/OmniRoute/pull/12215)). — thanks @maxmad64bis +- **fix(oauth):** Keep a Claude personal workspace and a Team organization as separate connections — they share the same email and `accountUUID`, so the email-only OAuth dedup let the second login overwrite the first account's tokens; `organizationUUID` now disambiguates them, the way `workspaceId` does for Codex ([#12222](https://github.com/diegosouzapw/OmniRoute/pull/12222)) — thanks @davidebaraldo +- **fix(resilience):** a 402 on a single paid model of a passthrough/gateway provider (e.g. `kilo-gateway`, `ollama-cloud`) no longer terminalizes the whole connection with a never-auto-recovered `credits_exhausted` status — only the paid model is locked out, so free models on the same key keep serving. 402 variant of [#3027](https://github.com/diegosouzapw/OmniRoute/issues/3027). Single-credential providers are unaffected — a 402 there is still treated as the key being genuinely out of credit ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239) / [#10616](https://github.com/diegosouzapw/OmniRoute/issues/10616)) ([#12242](https://github.com/diegosouzapw/OmniRoute/issues/12242)) — thanks @brick30llc-ctrl ([#12266](https://github.com/diegosouzapw/OmniRoute/pull/12266)) — thanks @geekyNads +- **fix(sse):** trust `finish_reason: "length"`/`"max_tokens"` over the reasoning-consumed-token ratio in response quality validation, so a reasoning model truncated below the old 90% threshold correctly fails and retries instead of returning empty content as a silent "success" ([#12262](https://github.com/diegosouzapw/OmniRoute/pull/12262)) — thanks @brick30llc-ctrl +- **fix(settings):** `PUT /api/settings/cache-config` now persists `alwaysPreserveClientCache` to the flat general settings the runtime cache-control policy actually reads; previously the value landed in the databaseSettings "cache" section and was silently ignored, so the endpoint had no effect on `cache_control` passthrough ([#12304](https://github.com/diegosouzapw/OmniRoute/pull/12304)) — thanks @davidebaraldo +- **fix(grok-cli):** treat omitted SuperGrokPro `creditUsagePercent` as 0% used so Provider Limits still renders a weekly bar (proto3 zero-elision) ([#12312](https://github.com/diegosouzapw/OmniRoute/pull/12312)) — thanks @HouMinXi +- **fix(combo):** Expose the two Auto-Combo scoring factors nobody could set — the combo validation schema and the dashboard weight sliders both declared 13 of the scorer's 15 factors, so `connectionDensity` (spreads load across a provider's connections) and `quality` were dropped on save and offered nowhere. The sliders also shipped their own default table that differed from the engine's on every non-zero factor and summed to 1.05, so the percentages shown next to them added up to 105%. Both lists now match `DEFAULT_WEIGHTS`, and a test keeps them there. Note that a combo whose stored `weights` omitted the two keys was effectively running with them at zero and the other thirteen renormalized upward; it now runs with the engine's intended distribution, so its routing does shift ([#12314](https://github.com/diegosouzapw/OmniRoute/pull/12314)) — thanks @maxmad64bis +- **fix(docs):** The free-tier reference no longer says its numbers come "confidence tagged per row" — no catalog entry carries a confidence tag and the API serves none, so every figure on that page is an estimate of the same, unstated quality. The page now states what an entry does vouch for: an independently documented hard stop (set by hand with the source in a comment, never defaulted to `true`) and a prompt-training disclosure, both with live counts the `check:docs-counts` gate keeps honest ([#12318](https://github.com/diegosouzapw/OmniRoute/pull/12318)) — thanks @maxmad64bis +- **fix(usage):** `adobe-firefly` and `firefly` have had a working usage fetcher since Adobe Firefly landed, but neither was ever added to the registration list, so the provider-plugin manifest, `genericQuotaFetcher` and the free-access quota cache all reported them as having no usage support — while `USAGE_SUPPORTED_PROVIDERS` said the opposite. Both are now declared, which also means their credit balance is fetched like any other declared provider's: `registerGenericQuotaFetchers` now registers a generic quota fetcher for them, and `resolveFreeAccessState` no longer returns early. A test holds the registration list to the dispatcher's switch in both directions, which is what the module's own docstring already asked for in prose ([#12321](https://github.com/diegosouzapw/OmniRoute/pull/12321)) — thanks @maxmad64bis +- **fix(quota):** drop the generic quota cache (agy / Antigravity / Claude OAuth) on an upstream 429 so reset-aware scoring does not keep a 60s stale snapshot, and force-refresh the next usage fetch so inner provider caches cannot recache the same window ([#12325](https://github.com/diegosouzapw/OmniRoute/pull/12325)) — thanks @HouMinXi +- **fix(combos):** deleting a combo now clears its persisted LKGP pins instead of leaving unreachable `key_value` rows behind ([#12326](https://github.com/diegosouzapw/OmniRoute/issues/12326)) ([#12425](https://github.com/diegosouzapw/OmniRoute/pull/12425)) — thanks @KrzysiekSko +- **fix(sse):** Keep ZWNJ (U+200C) and ZWJ (U+200D) in assistant text, reasoning and tool-call arguments — Persian/Kurdish half-space (`ارائه‌دهنده`), Arabic/Indic shaping and emoji sequences no longer lose them; the response de-obfuscation now removes joiners only between ASCII word characters, where the request side inserts them ([#12186](https://github.com/diegosouzapw/OmniRoute/issues/12186)) — thanks @rezjalibd ([#12359](https://github.com/diegosouzapw/OmniRoute/pull/12359)) — thanks @pacocartones +- **fix(resilience):** count resolved upstream 5xx results against the provider circuit breaker on the chat path — `CircuitBreaker.execute()` no longer reads a resolved `{ success: false, status: 5xx }` as a success that cancels the call-site failure, so a provider answering 503s now trips its breaker instead of staying `CLOSED` at `failureCount: 1`; single-model and combo dispatches are each accounted exactly once ([#12254](https://github.com/diegosouzapw/OmniRoute/issues/12254)) ([#12360](https://github.com/diegosouzapw/OmniRoute/pull/12360)) — thanks @pacocartones +- **fix(providers):** resolve the Codex quota auto-ping model from the live provider catalog and lifecycle registry instead of the retired `gpt-5.1-codex-mini`, and pause the ping with one actionable warning when no selectable Codex model exists rather than retrying a shut-down id every cooldown window ([#11905](https://github.com/diegosouzapw/OmniRoute/issues/11905)) ([#12361](https://github.com/diegosouzapw/OmniRoute/pull/12361)) — thanks @pacocartones +- **fix(api):** keep the `{created, data}` wrapper on combo-routed `/v1/images/generations` responses and default Codex image results to `b64_json` on both `/v1/images/generations` and `/v1/images/edits` so Codex CLI's built-in `image_gen` can decode them ([#12268](https://github.com/diegosouzapw/OmniRoute/issues/12268)) ([#12362](https://github.com/diegosouzapw/OmniRoute/pull/12362)) — thanks @pacocartones +- **fix(sse):** Name the shadowed custom provider node when a built-in provider id/alias (e.g. `openference` → `of`) reserves the prefix of an existing OpenAI/Anthropic-compatible node, so the runtime `No active credentials for provider: ` error explains that the prefix routed to the built-in and never reached the node's healthy connections, instead of contradicting the dashboard ([#11943](https://github.com/diegosouzapw/OmniRoute/issues/11943)) — thanks @morpheus9393 ([#12365](https://github.com/diegosouzapw/OmniRoute/pull/12365)) — thanks @pacocartones +- **fix(i18n):** wrap `ccOnboardingKeyPlaceholder` in ICU single quotes across all 43 locales so angle brackets render literally instead of being parsed as rich-text tags, which crashed the Claude Code onboarding block with `INVALID_MESSAGE: INVALID_TAG` ([#12302](https://github.com/diegosouzapw/OmniRoute/issues/12302)) ([#12369](https://github.com/diegosouzapw/OmniRoute/pull/12369)) — thanks @gonisulaimann +- **fix(models):** publish `effort_tiers` on Kimi K3's synced base-model entries (`kmca/k3`, `kmca/k3-256k`) so catalog-only clients (OpenCode, plain SDK pickers) can see and select the reasoning tiers (`low`/`high`/`max`) the synced metadata already carried — the `isSkippedEffortProvider` gate no longer suppresses tier visibility on those base entries, while synthetic `-` variant generation stays prevented and Codex/GLM base models remain excluded unchanged ([#12299](https://github.com/diegosouzapw/OmniRoute/issues/12299)) ([#12371](https://github.com/diegosouzapw/OmniRoute/pull/12371)) — thanks @gonisulaimann +- **fix(guardrails):** keep `auto`/`auto/*` virtual combos exempt from the Vision Bridge `fixedModel` credential guard so a combo target is passed through instead of silently falling back to global auto-selection ([#12237](https://github.com/diegosouzapw/OmniRoute/issues/12237)) ([#12373](https://github.com/diegosouzapw/OmniRoute/pull/12373)) — thanks @pacocartones +- **fix(combo):** capability-filter exhaustion caused by `max_tokens` above every target's known output limit now reports that reason (requested `max_tokens` vs the pool's highest known ceiling) instead of the unrelated "supports structured output" message ([#12229](https://github.com/diegosouzapw/OmniRoute/issues/12229)) — thanks @DW-MediaLab ([#12374](https://github.com/diegosouzapw/OmniRoute/pull/12374)) — thanks @pacocartones +- **fix(auth):** the `least-used` account strategy now prefers accounts without backoff before falling back to oldest `lastUsedAt`, the same tie-break `round-robin` already applies, so a failover no longer lands on a just-rate-limited account for a single request ([#12279](https://github.com/diegosouzapw/OmniRoute/issues/12279)) — thanks @tenshiak ([#12375](https://github.com/diegosouzapw/OmniRoute/pull/12375)) — thanks @pacocartones +- **fix(docker):** the `chatgpt-web-codex-browser` image now finds the Chrome binary under `chrome-linux64/` (Chrome for Testing layout in `playwright:v1.62.0-noble`) as well as the legacy `chrome-linux/`, so the container no longer crash-loops with `exec: --headless=new: not found` ([#12024](https://github.com/diegosouzapw/OmniRoute/issues/12024)) ([#12376](https://github.com/diegosouzapw/OmniRoute/pull/12376)) — thanks @pacocartones +- **fix(providers):** declare `groq/compound` and `allam-2-7b` as non-reasoning models in the curated Groq registry so `reasoning_effort` / `output_config.effort` / `thinking` from Claude Code are stripped instead of forwarded, which Groq rejected with HTTP 400 ([#12134](https://github.com/diegosouzapw/OmniRoute/issues/12134)) ([#12379](https://github.com/diegosouzapw/OmniRoute/pull/12379)) — thanks @pacocartones +- **fix(executors):** `OpencodeExecutor` no longer forces a direct connection when the connection has a proxy assigned in Proxy Management but no per-account proxies: the single-account fast path used to wrap the upstream dispatch in the direct-egress sentinel, discarding the ambient proxy context the chat handler had pinned from `proxy_assignments`, so API-key `opencode`/`opencode-go` connections egressed from the host IP (and hit geoblocks) despite the assignment. The direct pin is now applied only when no ambient proxy context exists ([#11894](https://github.com/diegosouzapw/OmniRoute/issues/11894) — thanks @hizzt) ([#12380](https://github.com/diegosouzapw/OmniRoute/pull/12380)) — thanks @pacocartones +- **fix(api):** `GET /v1/models` with `MODELS_CATALOG_PREFIX_MODE=canonical` (or `?prefix=canonical`) now lists providers whose registry alias is undefined or equal to their own id (Antigravity, Antigravity CLI and other self-aliased built-ins) — their single `provider/model` id was dropped by the alias/canonical duplicate guard in the static, synced, custom and alias-backed catalog loops ([#12058](https://github.com/diegosouzapw/OmniRoute/issues/12058)) — thanks @cheynetom ([#12381](https://github.com/diegosouzapw/OmniRoute/pull/12381)) — thanks @pacocartones +- **fix(translator):** Drop replayed `thinking` blocks that carry no signature (the shape produced from cross-provider `reasoning_content`) instead of stamping the default Claude signature on them, which Anthropic rejected with `400 Invalid signature in thinking block` on the next turn served by an Anthropic rung ([#12105](https://github.com/diegosouzapw/OmniRoute/issues/12105)) — thanks @atescivitci-cmd ([#12386](https://github.com/diegosouzapw/OmniRoute/pull/12386)) — thanks @pacocartones +- **fix(cli):** Resolve Bun's `--preload` polyfill path against the package root instead of `dist/`, so `omniroute` installed with `bun install -g` no longer crashes at startup with `error: preload not found …/dist/open-sse/utils/setupPolyfill.ts` ([#11980](https://github.com/diegosouzapw/OmniRoute/issues/11980)) — thanks @joglomedia ([#12387](https://github.com/diegosouzapw/OmniRoute/pull/12387)) — thanks @pacocartones +- **fix(providers):** `gemini-business` now publishes its model catalog — `/v1/models` and `/v1/providers/gemini-business/models` list the 12 enterprise Gemini ids the executor understands instead of returning an empty list (#12107) ([#12389](https://github.com/diegosouzapw/OmniRoute/pull/12389)) — thanks @pacocartones +- **fix(db):** install `busy_timeout` before the SQLite connection's first statement so a process opening the database while another one closes its WAL connection waits out the transient EXCLUSIVE lock instead of dying with `database is locked`, and recognise the drivers' real BUSY/PROTOCOL/IOERR errors as transient in the corruption probe so the same lock no longer renames the database away as corrupt; deflakes `cross-process contenders never both acquire the same connection` (#12394 — thanks @pacocartones) +- **fix(chat-admission):** derive the `chat_admission_busy` 503 `Retry-After` from observed heavyweight-lease occupancy — the larger of the exhausted `OMNIROUTE_CHAT_ADMISSION_QUEUE_MS` window and the time since capacity last turned over, capped at 60 s — instead of a fixed 1 s (structural) / 2 s (byte-stage) hint that invited Codex/agent fan-out clients to re-send ~1 MiB `/v1/responses` bodies every second into a gate held for the whole SSE lifetime; an idle gate keeps the historical floors ([#12135](https://github.com/diegosouzapw/OmniRoute/issues/12135)) (#12395 — thanks @pacocartones) +- **fix(api-manager):** the API key permissions modal no longer silently drops `allowedCombos` entries its Combo picker cannot render — routing-rule names such as `rt-*`, which the backend already honours — when "All" is clicked and the key is switched back to "Restrict"; those entries now survive the toggle, are listed read-only under the combo list so the count and the list agree, and are saved back verbatim instead of persisting `[]` (deny-all) (#12397 — thanks @pacocartones) +- **fix(catalog):** write the NUL separator of the catalog connection memo key, the provider serviceKind memo key, the Video Bridge promotion group key and a JSON-exactness test fixture as the `\u0000` escape instead of a raw byte — same runtime value, but the raw byte made git, GitHub and ripgrep treat those files as binary (hidden PR diffs, silently skipped searches); a guard test now keeps raw NUL bytes out of `src/`, `open-sse/` and `tests/` (#12403 — thanks @pacocartones) +- **fix(providers):** add `CLAUDE_CODE_CLIENT_VERSION` and `GITHUB_COPILOT_CLI_VERSION` env overrides so Anthropic/Copilot client-version gates can be unblocked without a rebuild ([#12417](https://github.com/diegosouzapw/OmniRoute/issues/12417)) ([#12632](https://github.com/diegosouzapw/OmniRoute/pull/12632)) — thanks @HouMinXi +- **fix(db):** back-fill `last_ping_at` and `last_pinged_reset_key` on `provider_connections` during schema reconciliation so divergent lineages that skipped `123_quota_auto_ping` still accept quota auto-ping writes ([#12470](https://github.com/diegosouzapw/OmniRoute/pull/12470) — thanks @KooshaPari) +- **fix(ci):** document MIT exceptions for `@eloqnt/{config,format-json,format-po}` (next-intl transitive; locked tarballs omit `license`) and keep the A2A lifecycle vitest off the real SQLite persistence seam ([#12581](https://github.com/diegosouzapw/OmniRoute/issues/12581)) ([#12595](https://github.com/diegosouzapw/OmniRoute/pull/12595)) +- OpenCode plugin `/v1/models` catalog fetch now waits 30s by default and attaches HTTP `statusCode` on 401/5xx so host fallback plugins can hop instead of seeing an untyped AbortError/UnknownError. ([#12607](https://github.com/diegosouzapw/OmniRoute/pull/12607)) — thanks @RaviTharuma +- **CI:** the OpenAPI security-tier gate now mirrors `isAlwaysProtectedPath()` in full — it also reads `ALWAYS_PROTECTED_API_PATTERNS`, so the pattern-gated credential routes (`/api/providers/{id}/{claude,codex}-auth/{export,apply-local}`, GHSA-5926-2w35-7h4q) no longer report as unannotated. (#12605) +- **fix(api):** GET /v1/models no longer waits forever on a hung coalesced catalog rebuild; cold-path waits are bounded (`CATALOG_BUILD_TIMEOUT_MS`, default 8s) and a last-good 200 is served when the rebuild times out ([#12627](https://github.com/diegosouzapw/OmniRoute/issues/12627)). ([#12628](https://github.com/diegosouzapw/OmniRoute/pull/12628)) — thanks @RaviTharuma +- **fix(cursor):** a non-array `tool_calls` on an assistant message no longer crashes the cursor request translator with a `TypeError`; both loops now require an array ([#12689](https://github.com/diegosouzapw/OmniRoute/issues/12689)) ([#12691](https://github.com/diegosouzapw/OmniRoute/pull/12691)) — thanks @soroush5 +- **fix(chat):** Chat Completions no longer return empty `content` after a server-owned memory or skills tool; the first provider send and account/model recovery now share one pipeline so a follow-up round-trip can fill the reply ([#12696](https://github.com/diegosouzapw/OmniRoute/issues/12696)) — thanks @HouMinXi ([#12867](https://github.com/diegosouzapw/OmniRoute/pull/12867)) +- **fix(dashboard):** batch-deleting provider keys no longer toasts failure after a successful delete when the confirm button's click event is forwarded as `onAfter` ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) — thanks @HouMinXi +- **fix(glm):** drop the extra 16th argument to `createSSETransformStreamWithLogger` that TypeScript rejected (TS2554) and that never reached the TransformStream ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) ([#12770](https://github.com/diegosouzapw/OmniRoute/pull/12770)) — thanks @HouMinXi +- **fix(dashboard):** pass `nodeMap` into Runtime `QuotaGroup` so a quota monitor in error/exhausted/alerting no longer throws `ReferenceError: nodeMap is not defined`. ([#12868](https://github.com/diegosouzapw/OmniRoute/pull/12868)) — thanks @HouMinXi +- Restricted API keys whose `allowedModels` lists a combo name no longer skip every combo member at pre-dispatch (`ALL_TARGETS_SKIPPED`). Inner-target filtering still applies when the allow-list is a provider prefix or `disableNonPublicModels` is on ([#12899](https://github.com/diegosouzapw/OmniRoute/pull/12899)). — thanks @HouMinXi +- **fix(combos):** Effort-suffixed combo members inherit the base model's `model_context_overrides` row so priority order is not inverted on large requests ([#12926](https://github.com/diegosouzapw/OmniRoute/pull/12926)) — thanks @HouMinXi +- **fix(catalog):** Picker-added `customModels` enter the dispatch-time live catalog so combo members and bare inference no longer 400 ([#12934](https://github.com/diegosouzapw/OmniRoute/pull/12934)) — thanks @HouMinXi +- **fix(docker):** re-derive the Docker build's worker-pool memory budget from the MEASURED ~4.5 GB per-process RSS (the issue owner's own VPS dmesg OOM-killer reproduction) instead of the stale 2560 MB/worker inference #11419 shipped, and lower `OMNIROUTE_BUILD_WORKERS` 3 → 2 so 1 parent + 1 page-data worker (2 processes × 4.5 GB = 9 GB) fits the 12.288 GB (75%) budget on a 16 GB GitHub Actions runner — the previous default (1 parent + 2 workers = 13.5 GB) still overcommitted the runner and kept "Publish to Docker Hub" failing with `cannot allocate memory` after #11419 merged (#7518). ([#11663](https://github.com/diegosouzapw/OmniRoute/pull/11663)) +- **fix(electron):** the packaged Windows desktop build now passes the #7592 cold-restart smoke end to end. Five defects were found and fixed on the way: (1) optional-pack staging failed on any machine whose `tar` is GNU tar — it reads the drive letter in an absolute `-f C:\...` archive path as a remote rsh host (`Cannot connect to C:`), so staging now passes a bare filename with `cwd` at the tarball directory and surfaces tar stderr on failure; (2) the Electron `files` allowlist was missing `lib/loginHeaderCapture.js`, so the packaged main process crashed at startup with `Cannot find module './lib/loginHeaderCapture'`; (3) electron-builder ≥26 injects an `!**/node_modules/**` ignore into every extraResources pattern list that no positive filter can override, which silently dropped the staged runtime `node_modules` (including the better-sqlite3 N-API prebuild) from `resources/app` — a fresh v3.8.50 desktop build would have shipped with no native SQLite driver and reproduced the #7592 sql.js fallback on every machine; an `afterPack` hook now restores the staged `node_modules` after packing; (4) the packaged-app smoke harness redirected `USERPROFILE` into its temp DATA_DIR, but Electron resolves the Roaming profile from `%USERPROFILE%\AppData\Roaming\` (USERPROFILE takes precedence over the APPDATA env var) and the path service throws instead of creating it, so `requestSingleInstanceLock()` returned false and the app exited(0) silently before `app.whenReady()` — the harness now pre-creates the derived tree, and `ensureSmokeEnvDirs` is exported and unit-tested; (5) the #7592 driver assertion parsed a `[DB] Driver: ...` line that the server's primary DB path never printed (only the unused `openDatabaseAsync()` did) — `getDbInstance()` now emits the same line on open so the guard can actually assert the native driver. Also: the smoke env-allowlist unit test hardcoded POSIX paths and could only pass on Linux/macOS; expectations are now host-agnostic, with new regression tests for the USERPROFILE-derived tree and `tarPack` under absolute Windows-style paths ([#11443](https://github.com/diegosouzapw/OmniRoute/pull/11443)) — thanks @jonlwheat2-gif +- fix(sse): stop the auto-combo candidates inspector from silently dropping model-locked/cooled-down rows (#9133) ([#11994](https://github.com/diegosouzapw/OmniRoute/pull/11994)) — thanks @hartmark +- **fix(providers):** Perplexity Web no longer turns upstream stream failures into successful assistant text; pre-content failures remain eligible for fallback, partial output ends with a structured sanitized error, and failed sessions are not persisted ([#12465](https://github.com/diegosouzapw/OmniRoute/pull/12465)) +- **fix(video-bridge):** preserve chronological video and transcript fusion, source-frame timestamps, transcript provenance, full contact-sheet intervals, and pre-abort callback isolation ([#11681](https://github.com/diegosouzapw/OmniRoute/pull/11681)) +- **Z.ai Web:** HTTP 200 streams carrying an upstream error now terminate with a structured failure instead of assistant text plus a normal stop, preserving partial output while allowing pre-content combo fallback. ([#12454](https://github.com/diegosouzapw/OmniRoute/pull/12454)) +- Absorb `Error [AbortError]: request_signal_aborted` and DOMException AbortError shapes in the process-level client-abort crash guard so routine client disconnects no longer kill the server (exit code 7). ([#12165](https://github.com/diegosouzapw/OmniRoute/pull/12165)) — thanks @alvinveroy +- **fix(sse):** Treat Adapta Web `type:error` SSE events as sanitized non-stream failures instead of empty HTTP 200 completions. ([#12459](https://github.com/diegosouzapw/OmniRoute/pull/12459)) +- **fix(autoCombo):** layer 3 (`models_dev_tier`) no longer scores retired models as premium, merge cross-provider capability rows deterministically, and inherit tier scores through variant ids ([#11508](https://github.com/diegosouzapw/OmniRoute/issues/11508)). `getModelsDevTierFitness` now vetoes ids present in `config/quality/model-lifecycle.json` with status `retired` before any other signal; `loadModelCapabilities()` aggregates per `model_id` (any-non-null-true for booleans, max for `limit_context`) instead of last-write-wins over SQLite's undefined row order; a capabilities miss retries through `resolveScoresAs()`'s base id and reports `models_dev_tier:inherited`, matching layers 1–2 (#11492). Adds `getModelsDevTierFitnessWithSource()` and an `invalidateCapabilitiesCache()` test/ops hook. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) — thanks @Meet6338-X +- fix(sse): accept Claude `content:[]` + `stop_reason: max_tokens`/`tool_use` in `detectMalformedNonStream` (match `isEmptyContentResponse`) so Claude Code `/model` probes with `max_tokens: 1` no longer become a false 502 ([#11568](https://github.com/diegosouzapw/OmniRoute/pull/11568)) — thanks @Seramicx +- **fix(security):** sanitize `request.failed` diagnostics before publishing them to live dashboard listeners and replay history, while keeping status, model, provider, latency, and internal call-log diagnostics intact. ([#12469](https://github.com/diegosouzapw/OmniRoute/pull/12469)) +- **fix(pwa):** do not intercept dashboard navigations so Chrome can fall back from a stale HTTP/3 Alt-Svc advertisement (UDP :20128 is unpublished; F5 on a long-lived tab hung until a new tab opened a fresh TCP connection). ([#12767](https://github.com/diegosouzapw/OmniRoute/pull/12767)) — thanks @HouMinXi +- **fix(executors):** handle DuckDuckGo ERR_BN_LIMIT (418) without retrying — when the upstream returns `418 ERR_BN_LIMIT` (rate-limit/ban), the executor now returns the error immediately instead of burning another VQD acquisition that would only count against the IP limit. The retry logic for `418 ERR_CHALLENGE` (unsolved challenge) remains unchanged. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) ([#12110](https://github.com/diegosouzapw/OmniRoute/pull/12110)) — thanks @oyi77 / @Meet6338-X +- Fixed DuckDuckGo streaming responses losing JSON lines and UTF-8 characters split across network chunks. ([#11528](https://github.com/diegosouzapw/OmniRoute/pull/11528)) — thanks @pacocartones +- Harden generated Cloudflare, Vercel, and Deno relays by stripping hop-by-hop, framing, and proxy authentication headers before forwarding requests upstream. ([#11533](https://github.com/diegosouzapw/OmniRoute/pull/11533)) — thanks @pacocartones +- **feat(grok-cli):** Provider Limits shows grok-cli banked reset credits from `GetRemainingResets` (including a real zero; a failed RPC omits the row) and the existing View credits button now calls `ConsumerUiSvc/RedeemReset` for grok-cli. Live tokens use nested fields 10/20/30 (id + Timestamp), not compact 1/2/3. ([#12805](https://github.com/diegosouzapw/OmniRoute/pull/12805)) — thanks @HouMinXi +- **fix(memory):** Embedding Model Quick select, Embedding Source remote dropdown, and Rerank selector now list every configured provider with embedding/rerank support instead of only chat-catalog text matches plus OpenRouter live discovery; a generic OpenAI-compatible `/embeddings` + Cohere-compatible `/rerank` runtime fallback resolves any configured chat provider's embedding/rerank endpoint, so unlisted providers no longer fail with "Unknown embedding provider"; both memory selectors gained a free-text model override ([#11390](https://github.com/diegosouzapw/OmniRoute/pull/11390)) — thanks @rqzbeh +- Harden SQLite upgrades around the historical migration-074 version collision: missing discovery and inspector tables are replayed atomically, pre-existing databases (including setup-created skeletons) receive reusable content-addressed safety snapshots, and Node test/eval probes without `DATA_DIR` are isolated from the operator database. ([#12435](https://github.com/diegosouzapw/OmniRoute/pull/12435)) +- **fix(dashboard):** Moonshot/Kimi Open Platform voucher and cash leftover percentages follow the bucket balance, so an empty wallet no longer paints those rows as 100% while Available is 0% ([#12733](https://github.com/diegosouzapw/OmniRoute/pull/12733)) — thanks @HouMinXi +- **fix(combo):** return non-retryable HTTP 400 when all candidates for a pinned native Codex turn are unavailable due to model-scoped lockout, terminating the turn cleanly while preserving turn continuity and enabling standard Combo routing on subsequent turns ([#12240](https://github.com/diegosouzapw/OmniRoute/pull/12240)) — thanks @mdigitalbh81 +- Fixed auto-router strategies preserving the selected connection through ranking and dispatch when multiple connections share the same provider and model. ([#11530](https://github.com/diegosouzapw/OmniRoute/pull/11530)) — thanks @pacocartones +- Preserve Chat Completions JSON-object response formats when translating requests to the Responses API. ([#11529](https://github.com/diegosouzapw/OmniRoute/pull/11529)) — thanks @pacocartones +- Fix idempotency fingerprints so Responses API requests include `input` and other semantic generation fields using stable serialization, while excluding credentials and request metadata. ([#11532](https://github.com/diegosouzapw/OmniRoute/pull/11532)) — thanks @pacocartones +- **fix(api):** Generated API CLI commands now enforce required OpenAPI request bodies; Combo test commands forward the required `comboName` body, while API keys created by older writers after migration 149 preserve legacy allow-all Combo access without widening explicit empty allowlists — thanks @marcelokarval ([#12070](https://github.com/diegosouzapw/OmniRoute/pull/12070)) +- **fix(grok-web):** treat upstream streaming failures as failures instead of successful assistant text: error-only streams now fail readiness with HTTP 502, while failures after legitimate content preserve that partial output and terminate through the sanitized stream failure path without a normal `stop` completion. ([#12458](https://github.com/diegosouzapw/OmniRoute/pull/12458)) +- HuggingChat now turns HTTP 200 JSONL generation failures into a sanitized 502 before content, or a fixed public stream failure after partial output, so fallback and request persistence no longer record a false successful stop. ([#12456](https://github.com/diegosouzapw/OmniRoute/pull/12456)) +- **fix(providers):** keep 1min.ai HTTP 200 stream errors out of assistant content, preserve partial output, and expose sanitized terminal errors so pre-content failures can fall back. ([#12466](https://github.com/diegosouzapw/OmniRoute/pull/12466)) +- **fix(sessions):** preserve legacy Sessions data when the additive exclusive-session projection is unavailable, returning an empty projection and warning only once per contiguous outage instead of failing the endpoint; the Sessions badge intentionally reflects the merged legacy and exclusive row count introduced by [#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389) ([#11469](https://github.com/diegosouzapw/OmniRoute/pull/11469)) — thanks @KaspaPulse +- Render Nimble Search with the generic provider icon and serve Opper's proven logo locally. ([#12415](https://github.com/diegosouzapw/OmniRoute/pull/12415)) +- **fix(quality):** `config/quality/provider-assets-provenance.jsonl` was stale against the physical `public/providers/` tree (still listing 225 assets and 5 duplicate-content groups after several provider-retirement PRs removed 85 icon files), which would have made `check:provider-asset-provenance` fail on the next full run. Reconciled the manifest to the real 142-file snapshot (142 asset records, 1 duplicate-content group, 2 newly-registered files marked `unresolved` pending provenance research), repinned `auditedCommit`, and updated the regression test's hardcoded counts to match. ([#11876](https://github.com/diegosouzapw/OmniRoute/pull/11876)) +- fix(ui): wrap quota card footer action buttons to prevent clipping Refresh button on narrow cards — when extra action buttons such as `View credits` are present on Codex cards, allow the footer row to flex-wrap with `shrink-0` buttons so `Refresh now` remains visible and clickable across all responsive card widths ([#11464](https://github.com/diegosouzapw/OmniRoute/pull/11464)) — thanks @adevwithpurpose +- Keep Antigravity Gemini usable when the same connection's Claude weekly quota is empty; generic quota cache stays per-connection for every other provider. ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) — thanks @HouMinXi +- Fixed Tailscale tunnel setup to reject ports outside the valid 1-65535 range and safely install Windows MSI packages from paths containing apostrophes. ([#11534](https://github.com/diegosouzapw/OmniRoute/pull/11534)) — thanks @pacocartones +- Classify Cloudflared and Tailscale process-management routes as local-only and spawn-capable while preserving remote access to read-only tunnel status endpoints. ([#11531](https://github.com/diegosouzapw/OmniRoute/pull/11531)) — thanks @pacocartones +- Electron release workflow: the `publish-npm` job now grants `actions: read` to the reusable `npm-publish.yml` it calls (its `publish` job requests it), which is what made GitHub refuse the whole v3.8.50 run at startup and ship the release with zero desktop assets; a `workflow_dispatch` now builds the requested tag instead of the dispatching branch and can skip the npm leg (`publish_npm=false`) when only re-attaching assets ([#11974](https://github.com/diegosouzapw/OmniRoute/pull/11974)) +- **fix(dashboard):** The Combos page usage guide now reads its dismissal through `useSyncExternalStore` instead of correcting SSR state inside an effect, removing an extra commit of the page tree on every load (and the `react-hooks/set-state-in-effect` error it raised). ([#12671](https://github.com/diegosouzapw/OmniRoute/pull/12671)) +- Electron release: `electron/package-lock.json` regained the optional `electron-builder-squirrel-windows` subtree (13 entries) that `npm ci` had been refusing as out of sync, `electron-release.yml` gained a `build_ref` dispatch input and stops regenerating release notes on a re-attach dispatch, and the npm publish workflow attaches the SBOM to the GitHub Release on dispatch publishes too — so the v3.8.51 tag ships every desktop asset and the SBOM like v3.8.49 did ([#12022](https://github.com/diegosouzapw/OmniRoute/pull/12022)) +- Drained the reds every PR against `release/v3.8.51` was born with: documented the three service-worker build-id variables, registered the six retirement/tunnel tests with the mutation gate, approved `eslint-plugin-react-hooks` in the dependency allowlist, added the six `combo.sort.*` strings to `vi` and `pt-BR`, pointed the ChatGPT Web doc at the real migration-168 test, worded the g4f hint around the member key, and realigned four tests to the retired-provider catalog and the legacy-schema fixtures the retirement migrations touch. ([#11940](https://github.com/diegosouzapw/OmniRoute/pull/11940)) +- **fix(providers):** Claude, Grok, LMArena, Notion, and Perplexity web-cookie transports now use pooled `wreq-js` 3.2 instead of the native sidecar, with all nine supported bindings pinned and audited, and the applicable platform binding plus native-license evidence included in each release artifact ([#12429](https://github.com/diegosouzapw/OmniRoute/pull/12429), supersedes [#11753](https://github.com/diegosouzapw/OmniRoute/pull/11753)). +- **fix(providers):** Zed Hosted streaming failures now trigger fallback before content and end partial streams with a sanitized structured error instead of fake assistant text and a normal-success stop. ([#12455](https://github.com/diegosouzapw/OmniRoute/pull/12455)) +- **fix(test):** capture hook result after render (direct commit `b85d0abb0b`) +- **fix(sync):** repair the two regressions the v3.8.50 sync-back left on release/v3.8.51 (direct commit `5ade9e0851`) +- **revert(models):** drop the memo reset hook ported from #10055 (direct commit `282de841ae`) +- **fix(typecheck):** guard the optional connectionId before the reactive model sync (direct commit `f2825bad75`) +- **fix(quota-share):** release the winner's reserved in-flight slot (#11371) ([#11408](https://github.com/diegosouzapw/OmniRoute/pull/11408)) — thanks @oyi77 +- **fix(providers):** stop silently dropping reasoning effort on opencode families (#10788) ([#11409](https://github.com/diegosouzapw/OmniRoute/pull/11409)) — thanks @oyi77 +- **fix(combo):** enable genuine quota-aware routing for generic providers (antigravity, claude, etc.) ([#11411](https://github.com/diegosouzapw/OmniRoute/pull/11411)) — thanks @HouMinXi +- **fix(oauth):** allow expired token retry before deactivation and activate expired retry loop (#P0) ([#11414](https://github.com/diegosouzapw/OmniRoute/pull/11414)) — thanks @HouMinXi +- **perf(executors):** lazy-load the executor registry — defer class imports + construction to first use (#11220) ([#11421](https://github.com/diegosouzapw/OmniRoute/pull/11421)) — thanks @oyi77 +- **fix:** make release/v3.8.50 CI gates green (run 32786966560 root causes) ([#11450](https://github.com/diegosouzapw/OmniRoute/pull/11450)) — thanks @jonlwheat2-gif +- **fix(sse):** exact-domain cookie match and origin-equality URL assertions ([#11452](https://github.com/diegosouzapw/OmniRoute/pull/11452)) — thanks @hartmark +- **fix(providers):** send Responses API payload during OpenAI-compatible validation ([#11454](https://github.com/diegosouzapw/OmniRoute/pull/11454)) — thanks @tuandinh0801 +- **fix(command-code):** fallback to /alpha/generate for Go plan without Provider API access ([#11455](https://github.com/diegosouzapw/OmniRoute/pull/11455)) — thanks @Chewji9875 +- **fix(responses-continuation):** fail closed on a log-truncated stored input/output array ([#11473](https://github.com/diegosouzapw/OmniRoute/pull/11473)) — thanks @hartmark +- **fix(cli):** write jcode settings as [providers.omniroute] in config.toml ([#11484](https://github.com/diegosouzapw/OmniRoute/pull/11484)) — thanks @hongnoul +- **fix(logging):** raise chat-log array truncation cap to 1000 and unify duplicate implementations ([#11499](https://github.com/diegosouzapw/OmniRoute/pull/11499)) — thanks @hartmark +- **fix:** restore live websocket URL helpers ([#11509](https://github.com/diegosouzapw/OmniRoute/pull/11509)) — thanks @PauloFH +- **perf(core):** eliminate async waterfalls across home, cache, and db operations (#11396) ([#11512](https://github.com/diegosouzapw/OmniRoute/pull/11512)) — thanks @HouMinXi +- **fix(kiro):** add runtime.us-east-1.kiro.dev as first-attempt endpoint ([#11517](https://github.com/diegosouzapw/OmniRoute/pull/11517)) — thanks @rifqiawl +- **fix(providers):** remove duplicate 128-tool truncation in OpencodeExecutor (#11444) ([#11518](https://github.com/diegosouzapw/OmniRoute/pull/11518)) — thanks @HouMinXi +- **fix(providers):** poll onboardUser LRO before classifying BYOP ([#11519](https://github.com/diegosouzapw/OmniRoute/pull/11519)) — thanks @rifqiawl +- **fix(translator):** preserve exact tool name casing for Claude tool_use payloads (#11487) ([#11520](https://github.com/diegosouzapw/OmniRoute/pull/11520)) — thanks @HouMinXi +- **fix(kimi-web):** align health probe with executor domain ([#11521](https://github.com/diegosouzapw/OmniRoute/pull/11521)) — thanks @pacocartones +- **fix(cli):** preserve existing admin password during setup ([#11522](https://github.com/diegosouzapw/OmniRoute/pull/11522)) — thanks @pacocartones +- **fix(mitm):** keep Windows DNS elevation off the POSIX sudo path (#11430) ([#11542](https://github.com/diegosouzapw/OmniRoute/pull/11542)) — thanks @official-burak +- **fix(sse):** scale chat admission by ingest byte budget instead of a fixed request count ([#11548](https://github.com/diegosouzapw/OmniRoute/pull/11548)) — thanks @TheDemonTuan +- **fix:** accept Gemini Web cookie exports ([#11554](https://github.com/diegosouzapw/OmniRoute/pull/11554)) — thanks @rafacpti23 +- **fix(i18n):** complete Brazilian Portuguese translations ([#11558](https://github.com/diegosouzapw/OmniRoute/pull/11558)) — thanks @rafacpti23 +- **fix(sse):** explicit types for openai-responses pureHelpers — clears last failing core typecheck gate ([#11567](https://github.com/diegosouzapw/OmniRoute/pull/11567)) — thanks @jonlwheat2-gif +- **fix(opencode):** hydrate Proxy Pool references for no-auth connections ([#11584](https://github.com/diegosouzapw/OmniRoute/pull/11584)) — thanks @AStupidBear +- **fix(ci):** resolve inherited release/v3.8.51 base-red CI failures ([#11608](https://github.com/diegosouzapw/OmniRoute/pull/11608)) — thanks @jonlwheat2-gif +- **fix(db):** fallback load STORAGE_ENCRYPTION_KEY from env files ([#11614](https://github.com/diegosouzapw/OmniRoute/pull/11614)) — thanks @benzntech +- **fix(providers):** mark Brave Search as no longer having a free tier ([#11621](https://github.com/diegosouzapw/OmniRoute/pull/11621)) — thanks @turbolego +- **fix:** pass tool result content through as string in OpenAI/Claude -> Gemini translation ([#11624](https://github.com/diegosouzapw/OmniRoute/pull/11624)) — thanks @Hsia97 +- **fix(sse):** scope credential health to exact key ([#11628](https://github.com/diegosouzapw/OmniRoute/pull/11628)) — thanks @KaspaPulse +- **fix(install):** guard prepare script against missing husky ([#11634](https://github.com/diegosouzapw/OmniRoute/pull/11634)) — thanks @Prajeeth-12 +- **fix(security):** redact NVIDIA_API_KEY at the log sink in the ad-hoc diag script ([#11639](https://github.com/diegosouzapw/OmniRoute/pull/11639)) +- **fix(types):** clear the two type regressions red on release/v3.8.51 ([#11640](https://github.com/diegosouzapw/OmniRoute/pull/11640)) — thanks @jonlwheat2-gif +- **fix(build):** resolve workspace: protocol dependencies to concrete semver versions during prepublish (#11540) ([#11641](https://github.com/diegosouzapw/OmniRoute/pull/11641)) — thanks @HouMinXi +- **fix(search):** properly resolve configured search connection in /v1/responses pipeline (#11524) ([#11642](https://github.com/diegosouzapw/OmniRoute/pull/11642)) — thanks @HouMinXi +- **fix(authz):** trust cf-connecting-ip for via-proxy detection when peer is Cloudflare edge (#11514) ([#11643](https://github.com/diegosouzapw/OmniRoute/pull/11643)) — thanks @HouMinXi +- **fix(security):** compare the OIDC state cookie in constant time ([#11646](https://github.com/diegosouzapw/OmniRoute/pull/11646)) +- **fix(security):** namespace the dedup hash by the calling API key ([#11649](https://github.com/diegosouzapw/OmniRoute/pull/11649)) +- **fix(models):** gate the Codex-native catalog loop on prefix mode (#11632) ([#11662](https://github.com/diegosouzapw/OmniRoute/pull/11662)) — thanks @xiaoyaner0201 +- **fix(dashboard):** project Codex routing quota in account pools ([#11666](https://github.com/diegosouzapw/OmniRoute/pull/11666)) — thanks @KaspaPulse +- **fix(ddgw):** normaliza system no upstream da release v3.8.51 ([#11692](https://github.com/diegosouzapw/OmniRoute/pull/11692)) — thanks @RhianB14 +- **fix(memory):** honor category filter in GET /api/memory ([#11699](https://github.com/diegosouzapw/OmniRoute/pull/11699)) — thanks @Sabeekhann +- **fix(ci):** detect stale workspace lockfile entries ([#11714](https://github.com/diegosouzapw/OmniRoute/pull/11714)) — thanks @pacocartones +- **fix(ci):** stamp dist/BUILD_SHA before the npm publish provenance gate ([#11721](https://github.com/diegosouzapw/OmniRoute/pull/11721)) +- **fix(docker):** decouple the best-effort Bun image from the release manifest ([#11724](https://github.com/diegosouzapw/OmniRoute/pull/11724)) +- **perf(compression):** avoid cloning memo misses twice ([#11727](https://github.com/diegosouzapw/OmniRoute/pull/11727)) — thanks @RaviTharuma +- **fix(docker):** let the bun digest artifact be absent, not fatal ([#11740](https://github.com/diegosouzapw/OmniRoute/pull/11740)) +- **fix(shared):** block cloud-metadata hosts under default remote-image guard ([#11755](https://github.com/diegosouzapw/OmniRoute/pull/11755)) — thanks @santosraju99-hub +- **fix(api):** preserve caller-provided X-Correlation-Id on chat completions ([#11760](https://github.com/diegosouzapw/OmniRoute/pull/11760)) — thanks @aniruddhaadak80 +- **fix(resilience):** lock permanently retired models instead of short backoff (Gemini ban prevention) ([#11762](https://github.com/diegosouzapw/OmniRoute/pull/11762)) — thanks @turbolego +- **fix:** mark Vercel AI Gateway as passthroughModels ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **fix(resilience):** stop hammering permanently-moved endpoints and billing-suspended accounts ([#11774](https://github.com/diegosouzapw/OmniRoute/pull/11774)) — thanks @turbolego +- **fix(lease):** remove global static reservation and gate routing on live active lease occupancy ([#11775](https://github.com/diegosouzapw/OmniRoute/pull/11775)) — thanks @TheDemonTuan +- **fix(ci):** size the install-upgrade gate to a measured run, and log the pack cost ([#11776](https://github.com/diegosouzapw/OmniRoute/pull/11776)) +- **fix(pwa):** stop serving the stale shell after deploys ([#11779](https://github.com/diegosouzapw/OmniRoute/pull/11779)) — thanks @HouMinXi +- **fix(combo):** lock GitHub models rejected as "not supported" for future requests ([#11781](https://github.com/diegosouzapw/OmniRoute/pull/11781)) — thanks @turbolego +- **fix(cli):** probe both IPv4 and IPv6 loopback for server readiness (#11766) ([#11794](https://github.com/diegosouzapw/OmniRoute/pull/11794)) — thanks @vermasomesh835 +- **fix(sse):** preserve CLIProxyAPI wrappers per connection ([#11797](https://github.com/diegosouzapw/OmniRoute/pull/11797)) — thanks @RaviTharuma +- **fix(providers):** honor model auto-fetch opt-in on create ([#11798](https://github.com/diegosouzapw/OmniRoute/pull/11798)) — thanks @RaviTharuma +- **fix(dashboard):** make model auto-fetch order independent ([#11805](https://github.com/diegosouzapw/OmniRoute/pull/11805)) — thanks @RaviTharuma +- **fix(docker):** make GHA cache exports best effort ([#11806](https://github.com/diegosouzapw/OmniRoute/pull/11806)) — thanks @RaviTharuma +- **fix(kiro):** do not permanently ban on 'User is not authorized to make this call' ([#11809](https://github.com/diegosouzapw/OmniRoute/pull/11809)) — thanks @Deftera186 +- **fix(sse):** keep cache-write tokens in OpenAI-shaped usage ([#11814](https://github.com/diegosouzapw/OmniRoute/pull/11814)) — thanks @TheDemonTuan +- **fix(docs):** resolve relative markdown and wiki links across Fumadocs and GitHub wiki ([#11834](https://github.com/diegosouzapw/OmniRoute/pull/11834)) — thanks @AndrianBalanescu +- **fix(chat):** preserve unstripped model string for passthrough provider routing ([#11840](https://github.com/diegosouzapw/OmniRoute/pull/11840)) — thanks @raheemuddin786 +- **fix(mcp):** avoid async initialization inside sync esm wrappers (#11569) ([#11849](https://github.com/diegosouzapw/OmniRoute/pull/11849)) — thanks @HouMinXi +- **fix(sse):** preserve coherent markdown token boundaries across stream translation chunks (#11606) ([#11851](https://github.com/diegosouzapw/OmniRoute/pull/11851)) — thanks @HouMinXi +- **fix(ci):** run the install-upgrade gate on disk, not on the /tmp tmpfs ([#11855](https://github.com/diegosouzapw/OmniRoute/pull/11855)) +- **fix(skills):** expand shorthand property types in injected tool schemas ([#11857](https://github.com/diegosouzapw/OmniRoute/pull/11857)) — thanks @alvinveroy +- **fix(ci):** publish npm from a hosted runner so provenance is accepted ([#11877](https://github.com/diegosouzapw/OmniRoute/pull/11877)) +- **fix(lint):** pin react hooks plugin to 7.0.1 ([#11886](https://github.com/diegosouzapw/OmniRoute/pull/11886)) +- **fix(ci):** surface and clear release-green ESLint failure ([#11890](https://github.com/diegosouzapw/OmniRoute/pull/11890)) — thanks @MumuTW +- **fix(guardrails):** prevent duplicate prompt-injection-guard log output ([#11936](https://github.com/diegosouzapw/OmniRoute/pull/11936)) — thanks @patrykkopycinski +- **fix(auth):** downgrade expected transient states from warn to debug ([#11937](https://github.com/diegosouzapw/OmniRoute/pull/11937)) — thanks @patrykkopycinski +- **fix(codex):** fail over image generation for imported free plans ([#11948](https://github.com/diegosouzapw/OmniRoute/pull/11948)) — thanks @Bl0ck154 +- **fix(codex):** normalize non-stream responses ([#11951](https://github.com/diegosouzapw/OmniRoute/pull/11951)) — thanks @Bl0ck154 +- **fix(images):** forward Antigravity image size ([#11952](https://github.com/diegosouzapw/OmniRoute/pull/11952)) — thanks @Bl0ck154 +- **fix(db):** invalidate connection cache after upsert ([#11953](https://github.com/diegosouzapw/OmniRoute/pull/11953)) — thanks @Bl0ck154 +- **fix(codex):** restore imported account state ([#11954](https://github.com/diegosouzapw/OmniRoute/pull/11954)) — thanks @Bl0ck154 +- **fix(sse):** give extended-thinking targets the reasoning readiness budget ([#11959](https://github.com/diegosouzapw/OmniRoute/pull/11959)) — thanks @abhisheksharma2411 +- **fix(dashboard):** use opaque background and readable text color on cost chart tooltips ([#11960](https://github.com/diegosouzapw/OmniRoute/pull/11960)) — thanks @ZaimMarzuki +- **fix(antigravity):** send complete loadCodeAssist metadata (ideType/platform/pluginType as numeric enums) ([#11969](https://github.com/diegosouzapw/OmniRoute/pull/11969)) — thanks @rifqiawl +- **fix(core):** resolve DB init race condition and reasoning translation ([#12003](https://github.com/diegosouzapw/OmniRoute/pull/12003)) — thanks @raheemuddin786 +- **fix(admission):** exclude reclaimable page cache from the cgroup pressure ratio ([#12017](https://github.com/diegosouzapw/OmniRoute/pull/12017)) — thanks @HouMinXi +- **fix(build):** prune non-production trees in NFT trace excludes and tsconfig ([#12028](https://github.com/diegosouzapw/OmniRoute/pull/12028)) — thanks @Chewji9875 +- **fix(migrations):** add renamed migration compatibility for 056/073/077/101 ([#12036](https://github.com/diegosouzapw/OmniRoute/pull/12036)) — thanks @oyi77 +- **fix(sse):** default crash-guard logger to console.warn, not console ([#12042](https://github.com/diegosouzapw/OmniRoute/pull/12042)) — thanks @HouMinXi +- **fix(catalog):** derive vision/modalities for built-in auto combos from effective target pool ([#12046](https://github.com/diegosouzapw/OmniRoute/pull/12046)) — thanks @Prajeeth-12 +- **fix(release):** the packaged-app smoke verifies the database opened, not a driver line the primary path never prints (twin of #12032) ([#12047](https://github.com/diegosouzapw/OmniRoute/pull/12047)) +- **fix(barrel):** migrate src/app/ off the @/lib/localDb barrel import (#11795 Phase 2) ([#12051](https://github.com/diegosouzapw/OmniRoute/pull/12051)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate src/lib/ off the localDb barrel to direct db imports (#59) ([#12052](https://github.com/diegosouzapw/OmniRoute/pull/12052)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate open-sse, src/shared, src/sse, src/models, src/domain off the @/lib/localDb barrel import (#11795 Phase 4) ([#12053](https://github.com/diegosouzapw/OmniRoute/pull/12053)) — thanks @jonlwheat2-gif +- **fix(barrel):** delete the @/lib/localDb barrel — every consumer migrated (#11795 Phase 5) ([#12055](https://github.com/diegosouzapw/OmniRoute/pull/12055)) — thanks @jonlwheat2-gif +- **fix(radar):** restore D12 public boundary ([#12057](https://github.com/diegosouzapw/OmniRoute/pull/12057)) +- **fix(dev):** bound webpack and Tailwind scans ([#12075](https://github.com/diegosouzapw/OmniRoute/pull/12075)) — thanks @backryun +- **fix(dev):** isolate root layout settings reads ([#12076](https://github.com/diegosouzapw/OmniRoute/pull/12076)) — thanks @backryun +- **fix(dev):** reduce instrumentation executor fan-out (phase 3) ([#12078](https://github.com/diegosouzapw/OmniRoute/pull/12078)) — thanks @backryun +- **fix(dev):** make logging resources HMR-singleton ([#12079](https://github.com/diegosouzapw/OmniRoute/pull/12079)) — thanks @backryun +- **fix(sse):** keep unavailable forced connections scoped ([#12080](https://github.com/diegosouzapw/OmniRoute/pull/12080)) — thanks @keeltrace +- **fix(dev):** isolate batch dispatch from instrumentation ([#12081](https://github.com/diegosouzapw/OmniRoute/pull/12081)) — thanks @backryun +- **fix(ci):** reconcile release test contract drift ([#12082](https://github.com/diegosouzapw/OmniRoute/pull/12082)) — thanks @backryun +- **fix(ci):** accept CVE-2025-68121 in the prebuilt tls-client .so, auto-close base-red issues, guard Scorecard on the default branch ([#12085](https://github.com/diegosouzapw/OmniRoute/pull/12085)) +- **fix(db):** drop three consumer-less 1proxy exports — dead-code base-red on release/v3.8.51 after the barrel deletion (#12055) ([#12087](https://github.com/diegosouzapw/OmniRoute/pull/12087)) +- **fix(api):** clear the six API-route TypeScript regressions the new gate landed red on ([#12094](https://github.com/diegosouzapw/OmniRoute/pull/12094)) +- **fix(release):** never let the tag-push Create Release append auto notes to the curated body ([#12096](https://github.com/diegosouzapw/OmniRoute/pull/12096)) +- **fix(sse):** honor CLIProxyAPI environment API key ([#12099](https://github.com/diegosouzapw/OmniRoute/pull/12099)) — thanks @RaviTharuma +- **fix(oauth):** bind Google refresh to the client that issued the token ([#12106](https://github.com/diegosouzapw/OmniRoute/pull/12106)) — thanks @HouMinXi +- **fix(dashboard):** make RequestLoggerDetail loadable outside Next — CSS via globals.css + CJS/ESM interop (#11703 base-reds) ([#12114](https://github.com/diegosouzapw/OmniRoute/pull/12114)) +- **fix(leases):** project status lease row to lease columns so joined connection PII never escapes ([#12115](https://github.com/diegosouzapw/OmniRoute/pull/12115)) — thanks @geek007git +- **fix(plugins):** do not kill the plugin process when a fire-and-forget hook times out ([#12116](https://github.com/diegosouzapw/OmniRoute/pull/12116)) — thanks @geek007git +- **fix(guardrails):** restore injection-guard logging on middleware-only routes ([#12117](https://github.com/diegosouzapw/OmniRoute/pull/12117)) — thanks @geek007git +- **fix(plugins):** refresh stored manifest from disk on activate so new hook fields reach existing installs ([#12120](https://github.com/diegosouzapw/OmniRoute/pull/12120)) — thanks @geek007git +- **fix(codex):** preserve existing provider state when bulk-import upserts a matching connection ([#12122](https://github.com/diegosouzapw/OmniRoute/pull/12122)) — thanks @geek007git +- **fix(ci):** clear the two base-reds the 2026-08-30 merge batch left on release/v3.8.51 (round 3) ([#12123](https://github.com/diegosouzapw/OmniRoute/pull/12123)) +- **fix(sse):** strip type:'custom' from Claude tools on agentrouter dispatch ([#12126](https://github.com/diegosouzapw/OmniRoute/pull/12126)) — thanks @wahidsadik371-coder +- **fix(sse):** stop advertising video providers the dispatcher cannot run ([#12131](https://github.com/diegosouzapw/OmniRoute/pull/12131)) — thanks @quiterunner-commits +- **fix(search):** treat HTTP 432 and plan limit errors as transient cooldown ([#12139](https://github.com/diegosouzapw/OmniRoute/pull/12139)) — thanks @HouMinXi +- **fix(sse):** refuse an AI Horde queue that cannot fit the request budget ([#12143](https://github.com/diegosouzapw/OmniRoute/pull/12143)) — thanks @quiterunner-commits +- **fix(ci):** clear the base-reds the afternoon merge batch left on release/v3.8.51 (round 5: provider count 352, TS2554/TS2677) ([#12144](https://github.com/diegosouzapw/OmniRoute/pull/12144)) +- **fix(translator):** strip neutral tool_choice when tools absent in Responses-to-Chat (#12141) ([#12166](https://github.com/diegosouzapw/OmniRoute/pull/12166)) — thanks @HouMinXi +- **fix(guardrails):** pass providerId to getResolvedModelCapabilities in checkComboVision (#12112) ([#12169](https://github.com/diegosouzapw/OmniRoute/pull/12169)) — thanks @HouMinXi +- **fix(translator):** strip plaintext reasoning content for opaque responses backends (#12128) ([#12171](https://github.com/diegosouzapw/OmniRoute/pull/12171)) — thanks @HouMinXi +- **perf(stream):** compile hot-path regexes once, bound token caches, fix quadratic buffering ([#12179](https://github.com/diegosouzapw/OmniRoute/pull/12179)) — thanks @opensource-elearning +- **fix:** resolve compression worker file using runtime anchors instead of… ([#12183](https://github.com/diegosouzapw/OmniRoute/pull/12183)) — thanks @NightStalker-87 +- **fix:** prevent Claude Code session kills via liveness-aware readiness + auto model echo ([#12189](https://github.com/diegosouzapw/OmniRoute/pull/12189)) — thanks @opensource-elearning +- **fix(translator):** preserve falsy primitive values in Gemini and Antigravity function response results ([#12191](https://github.com/diegosouzapw/OmniRoute/pull/12191)) — thanks @Siva010 +- **fix(oauth):** mark empty Antigravity projectId as degraded and clear stale errors (#11284) ([#12205](https://github.com/diegosouzapw/OmniRoute/pull/12205)) — thanks @HouMinXi +- **fix(providers):** gate the Codex auto-ping usage read on the shared quota throttle ([#12209](https://github.com/diegosouzapw/OmniRoute/pull/12209)) — thanks @abhisheksharma2411 +- **fix(combos):** prioritize SQLite row id over inner JSON id and notify delete errors ([#12213](https://github.com/diegosouzapw/OmniRoute/pull/12213)) — thanks @HouMinXi +- **fix(providers):** mark featherless.ai as no longer having a free tier (`hasFree: false`) ([#12216](https://github.com/diegosouzapw/OmniRoute/pull/12216)) — thanks @turbolego +- **fix(deepseek-web):** stop Turbopack dev panic in the PoW worker path resolver ([#12221](https://github.com/diegosouzapw/OmniRoute/pull/12221)) — thanks @hartmark +- **fix(chat-admission):** clarify local 503 source ([#12223](https://github.com/diegosouzapw/OmniRoute/pull/12223)) — thanks @keeltrace +- **fix(dev):** silence webpack runtime module warnings ([#12228](https://github.com/diegosouzapw/OmniRoute/pull/12228)) — thanks @backryun +- **fix(db):** use module.require for CommonJS runtime driver loading ([#12230](https://github.com/diegosouzapw/OmniRoute/pull/12230)) — thanks @raheemuddin786 +- **fix(memory):** honest probe-driven FTS5 keyword status + memory_id rowid sync ([#12231](https://github.com/diegosouzapw/OmniRoute/pull/12231)) — thanks @raheemuddin786 +- **fix(system):** propagate abort signal to stream reader in HTTP version checks ([#12232](https://github.com/diegosouzapw/OmniRoute/pull/12232)) — thanks @raheemuddin786 +- **fix(resilience):** isolate local host execution errors from provider circuit breakers ([#12233](https://github.com/diegosouzapw/OmniRoute/pull/12233)) — thanks @raheemuddin786 +- **fix(sse):** sort injected tools deterministically for prompt caching ([#12234](https://github.com/diegosouzapw/OmniRoute/pull/12234)) — thanks @raheemuddin786 +- **fix(combo):** always clear the loop-safety timer, not just on the happy path (#11804) ([#12245](https://github.com/diegosouzapw/OmniRoute/pull/12245)) +- **fix(quality):** base-red pair — stryker tap registration + turn-pin suites aligned to the window gate ([#12255](https://github.com/diegosouzapw/OmniRoute/pull/12255)) — thanks @backryun +- **fix(dev):** qualify Turbopack runtime boundaries ([#12258](https://github.com/diegosouzapw/OmniRoute/pull/12258)) — thanks @backryun +- **fix(combo):** fall back to full pool when collapsed sole survivor is context-too-small ([#12278](https://github.com/diegosouzapw/OmniRoute/pull/12278)) — thanks @opensource-elearning +- **fix(combo):** bound the pre-dispatch unavailable skip so a stale label cannot dark a pool (#12168) ([#12285](https://github.com/diegosouzapw/OmniRoute/pull/12285)) +- **fix(security):** strip Qwen/Alibaba console-session cookies from provider API responses ([#12287](https://github.com/diegosouzapw/OmniRoute/pull/12287)) +- **fix(usage):** console-aware Token Plan guidance + subscription hint on bailian 401 ([#12288](https://github.com/diegosouzapw/OmniRoute/pull/12288)) +- **fix(resourcePressure):** log numeric detail on every rejection, recover faster ([#12293](https://github.com/diegosouzapw/OmniRoute/pull/12293)) — thanks @hartmark +- **fix(release):** drain the 2026-09-01 base-red window — passthrough usage regression + radarPage i18n keys ([#12327](https://github.com/diegosouzapw/OmniRoute/pull/12327)) +- **fix(ci):** openapi-security-tiers checker must honor routeGuard patterns + imported prefixes ([#12350](https://github.com/diegosouzapw/OmniRoute/pull/12350)) — thanks @ggiak +- **perf(ci):** cache node_modules in the npm-ci-retry composite (#8084 D3) ([#12408](https://github.com/diegosouzapw/OmniRoute/pull/12408)) +- **revert(ui):** point CTAs back at their real destinations ([#12410](https://github.com/diegosouzapw/OmniRoute/pull/12410)) +- **fix(release):** drain the 2026-09-02 base-red — rerank-providers import + api-typecheck baseline ratchet ([#12414](https://github.com/diegosouzapw/OmniRoute/pull/12414)) +- **fix(types):** make system prompt injection noImplicitAny-safe ([#12416](https://github.com/diegosouzapw/OmniRoute/pull/12416)) +- **fix(quality):** validate typecheck baseline schema ([#12419](https://github.com/diegosouzapw/OmniRoute/pull/12419)) +- **fix(memory):** resolve rerank provider node cache import ([#12421](https://github.com/diegosouzapw/OmniRoute/pull/12421)) +- **fix(ci):** repair release-root regressions — pack dedup, web-session syntax, uc-image ids ([#12423](https://github.com/diegosouzapw/OmniRoute/pull/12423)) — thanks @backryun +- **fix(providers):** separate MaxAI and UC credential contracts ([#12431](https://github.com/diegosouzapw/OmniRoute/pull/12431)) +- **fix(cli):** preserve Claude settings on config set ([#12432](https://github.com/diegosouzapw/OmniRoute/pull/12432)) — thanks @KrzysiekSko +- **fix(providers):** repair the maxai credential block truncated by merge auto-resolve ([#12433](https://github.com/diegosouzapw/OmniRoute/pull/12433)) +- **fix(quality):** record the 2026-09-02 merged growth in the file-size baseline ([#12434](https://github.com/diegosouzapw/OmniRoute/pull/12434)) +- **fix(uc):** route UC error strings through sanitizeErrorMessage; allowlist the retired codex id ([#12437](https://github.com/diegosouzapw/OmniRoute/pull/12437)) +- **fix(adapta):** redact streamed upstream errors ([#12438](https://github.com/diegosouzapw/OmniRoute/pull/12438)) +- **fix(sse):** remap non-contiguous upstream tool_calls index to a gap-free output_index ([#12445](https://github.com/diegosouzapw/OmniRoute/pull/12445)) — thanks @hartmark +- **fix(quality):** detect duplicate tool_calls entries in one response ([#12446](https://github.com/diegosouzapw/OmniRoute/pull/12446)) — thanks @hartmark +- **fix(conversations):** resolve turn content OmniRoute never sends back to the client ([#12447](https://github.com/diegosouzapw/OmniRoute/pull/12447)) — thanks @hartmark +- **fix(combo):** do not treat credits-exhausted 401 as auth skip ([#12449](https://github.com/diegosouzapw/OmniRoute/pull/12449)) — thanks @RaviTharuma +- **fix(auth):** do not park healthy quota accounts as expired ([#12452](https://github.com/diegosouzapw/OmniRoute/pull/12452)) — thanks @RaviTharuma +- **fix(responses-continuation):** fail closed on a collector-truncated, empty output array ([#12460](https://github.com/diegosouzapw/OmniRoute/pull/12460)) — thanks @hartmark +- **fix(logging):** raise the SSE payload collector's default cap ([#12461](https://github.com/diegosouzapw/OmniRoute/pull/12461)) — thanks @hartmark +- **fix(resilience):** surface Responses failed.error.message in 502s ([#12472](https://github.com/diegosouzapw/OmniRoute/pull/12472)) — thanks @RaviTharuma +- **fix(security):** unbiased maxai X-Random nonce + stricter URL/regex assertions ([#12502](https://github.com/diegosouzapw/OmniRoute/pull/12502)) +- **fix(video):** re-anchor transcript log-redaction so PII/credential maskers can't reopen the leak (#12150 P1 follow-up) ([#12503](https://github.com/diegosouzapw/OmniRoute/pull/12503)) +- **fix(monitoring):** serve cached credentialHealth off the request path ([#12533](https://github.com/diegosouzapw/OmniRoute/pull/12533)) — thanks @RaviTharuma +- **fix(quota):** keep Antigravity Gemini usable when Claude weekly is empty ([#12566](https://github.com/diegosouzapw/OmniRoute/pull/12566)) — thanks @HouMinXi +- **fix(authz):** hard-gate every credential export and CLI-config write (GHSA-5926-2w35-7h4q) ([#12600](https://github.com/diegosouzapw/OmniRoute/pull/12600)) +- **fix(docs):** restore the Next build — REMOVED_PROVIDERS.md had no frontmatter (base-red #12581) ([#12610](https://github.com/diegosouzapw/OmniRoute/pull/12610)) +- **fix(quality):** re-point the zcodeProtocol public-creds allowlist to line 313 ([#12615](https://github.com/diegosouzapw/OmniRoute/pull/12615)) +- **fix(security):** close 3 advisories — search baseUrl exfil, sk- in the error sanitizer, bifrost relay header leak ([#12620](https://github.com/diegosouzapw/OmniRoute/pull/12620)) +- **fix(providers):** sanitize boolean required and nested bare maps for Gemini (#12269) ([#12624](https://github.com/diegosouzapw/OmniRoute/pull/12624)) — thanks @HouMinXi +- **fix(resilience):** keep Overloaded STREAM_EARLY_EOF off the provider breaker ([#12626](https://github.com/diegosouzapw/OmniRoute/pull/12626)) — thanks @HouMinXi +- **fix(catalog):** re-audit free-tier quotas against official pages ([#12649](https://github.com/diegosouzapw/OmniRoute/pull/12649)) +- **fix(dashboard):** keep a request's pending-tracking id stable across combo target retries ([#12650](https://github.com/diegosouzapw/OmniRoute/pull/12650)) — thanks @hartmark +- **fix(tests):** retire dead model ids from the chat-pipeline integration suite (base-red #12581) ([#12670](https://github.com/diegosouzapw/OmniRoute/pull/12670)) +- **fix:** resolve SqliteError no such table compression_run_telemetry during cleanup ([#12682](https://github.com/diegosouzapw/OmniRoute/pull/12682)) — thanks @groovecityJO +- **fix(combo):** treat a pin-only step as implicit connection allowlist ([#12697](https://github.com/diegosouzapw/OmniRoute/pull/12697)) — thanks @HouMinXi +- **fix(ci):** ship .npmrc in published package so legacy-peer-deps applies to consumers (#11544) ([#12699](https://github.com/diegosouzapw/OmniRoute/pull/12699)) — thanks @KooshaPari +- **fix(docker):** pin 4 CLI tools to exact versions (#12576) ([#12703](https://github.com/diegosouzapw/OmniRoute/pull/12703)) — thanks @KooshaPari +- **fix(docker):** document and harden cli profile trust boundary (#12570) ([#12706](https://github.com/diegosouzapw/OmniRoute/pull/12706)) — thanks @KooshaPari +- **fix(sse):** redact video transcript in pre-guardrail rejected-request logs (#12150 P2 item 7) ([#12710](https://github.com/diegosouzapw/OmniRoute/pull/12710)) +- **fix(i18n):** quote placeholder in OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES description (#12505) ([#12769](https://github.com/diegosouzapw/OmniRoute/pull/12769)) — thanks @KooshaPari +- **fix(api/pricing):** surface validation error message as string, not raw object (#12494) ([#12771](https://github.com/diegosouzapw/OmniRoute/pull/12771)) — thanks @KooshaPari +- **fix(sse):** re-enable prompt compression for native Codex passthrough ([#12834](https://github.com/diegosouzapw/OmniRoute/pull/12834)) — thanks @thomasmaerz +- **fix(dashboard):** model health tests for a provider node set to the Responses API now call `/v1/responses` with a Responses-shaped body instead of `/v1/chat/completions` — those models were reported as `Provider returned HTTP 200 but no text content` even though the same model answered normally through `/v1/responses` ([#13070](https://github.com/diegosouzapw/OmniRoute/issues/13070)) ([#13087](https://github.com/diegosouzapw/OmniRoute/pull/13087)) — thanks @ntdatt812 +- fix(routing): stop a round-robin combo's "opencode" targets from collapsing onto the opencode-zen connection (#11912) ([#13283](https://github.com/diegosouzapw/OmniRoute/pull/13283)) +- fix(routing): stop the reactive-compaction debug log from lying when compression is globally disabled (#11977) ([#13276](https://github.com/diegosouzapw/OmniRoute/pull/13276)) +- fix(electron): relativize standalone-bundle symlink targets so Stage 8 manifest verification stops failing on Windows (#11979) ([#13251](https://github.com/diegosouzapw/OmniRoute/pull/13251)) +- fix(dashboard): surface a visible error when Compression Studio's combined preview run fails (#12061) ([#13288](https://github.com/diegosouzapw/OmniRoute/pull/13288)) +- fix(dashboard): make the compression "Effective pipeline" preview honor the active profile and warn when the master switch is off (#12063) ([#13287](https://github.com/diegosouzapw/OmniRoute/pull/13287)) +- fix(providers): scope TinyCMS Web signer's DOM shims to each call instead of leaking them for the process lifetime, and surface a clean HTTP status on a non-JSON interception-toggles error (#12072) ([#13254](https://github.com/diegosouzapw/OmniRoute/pull/13254)) +- fix(guardrails): stop Vision Bridge from re-selecting a model locked after a 404 (#12111) ([#13259](https://github.com/diegosouzapw/OmniRoute/pull/13259)) +- fix(sse): require Responses-shaped body before native OpenAI-compatible passthrough (#12129) ([#13278](https://github.com/diegosouzapw/OmniRoute/pull/13278)) +- fix(providers): minimax-m3 now collapses manual thinking.type:"enabled" to adaptive, preventing upstream 400 (2013) (#12132) ([#13249](https://github.com/diegosouzapw/OmniRoute/pull/13249)) +- fix(db): scope model visibility overrides by modality so hiding a Chat model no longer hides an identically-ID'd Image/Embeddings/etc. model (#12172) ([#13258](https://github.com/diegosouzapw/OmniRoute/pull/13258)) +- fix(db): scope local-provider apiKey dedup to matching base URL so LM Studio/Ollama-style connections support multiple accounts (#12173) ([#13250](https://github.com/diegosouzapw/OmniRoute/pull/13250)) +- fix(providers): refresh Trae's stale Referer/Origin and forward user timezone so imported connections stop failing with 401 (#12190) ([#13255](https://github.com/diegosouzapw/OmniRoute/pull/13255)) +- fix(providers): route opencode-go/gpt-5.6-luna to /responses instead of /chat/completions (#12196) ([#13275](https://github.com/diegosouzapw/OmniRoute/pull/13275)) +- fix(dashboard): allow deleting the last extra-upstream-header row even when invalid (#12251) ([#13252](https://github.com/diegosouzapw/OmniRoute/pull/13252)) +- **fix(i18n):** translate pre-existing `__MISSING__:` keys for `combo.sort`, `requestLogger.detail` expand/collapse, `common.profile`, and `settings.resilienceCredentialHealth*` across 39 locales ([#12272](https://github.com/diegosouzapw/OmniRoute/issues/12272)) ([#13034](https://github.com/diegosouzapw/OmniRoute/pull/13034)) — thanks @HouMinXi +- fix(cli): run the Node.js runtime compatibility guard before the heavy `tsx/esm` + Commander import chain so an unsupported runtime gets a clear message instead of a raw `Invalid regular expression flags` crash (#12296) ([#13253](https://github.com/diegosouzapw/OmniRoute/pull/13253)) +- fix(dashboard): refresh the providers list after deleting a compatible provider node (#12298) ([#13256](https://github.com/diegosouzapw/OmniRoute/pull/13256)) +- fix(usage): fail closed on API-key budget enforcement when a provider's `auto` routing alias has no pricing row, instead of silently counting it as $0 (#12341) ([#13257](https://github.com/diegosouzapw/OmniRoute/pull/13257)) +- **fix(providers):** include the submitted Agnes video model when polling by `video_id` ([#12356](https://github.com/diegosouzapw/OmniRoute/pull/12356)) — thanks @pacocartones +- **fix(routing):** custom OpenAI-compatible nodes now honor the saved Chat/Responses API type after edits instead of letting the node's original ID prefix override the live connection setting ([#11884](https://github.com/diegosouzapw/OmniRoute/issues/11884)). ([#12358](https://github.com/diegosouzapw/OmniRoute/pull/12358)) — thanks @pacocartones +- fix(sse): surface an error instead of a silent empty 200 when a Claude stream closes with zero bytes (#12398) ([#13285](https://github.com/diegosouzapw/OmniRoute/pull/13285)) +- fix(oauth): warn before the dead localhost:8080 redirect in antigravity/gemini `oauth start` (#12413) ([#13265](https://github.com/diegosouzapw/OmniRoute/pull/13265)) +- fix(providers): stop devin-cli spawn error from double-closing the SSE controller (#12517) ([#13261](https://github.com/diegosouzapw/OmniRoute/pull/13261)) +- **fix(rerank):** clamp Voyage `top_k` to the documents actually sent after empty-string filtering, and honor `return_documents: false` in the NVIDIA response adapter (#12523 — thanks @pacocartones) +- **fix(audio):** `/v1/audio/translations` now resolves combo names the way `/v1/audio/transcriptions` already does, so a combo that `GET /v1/models` advertises is fanned out to its targets instead of being rejected with `400 Invalid translation model: . Use format: provider/model`; literal `provider/model` ids and unknown bare names behave as before (#12536 — thanks @pacocartones) +- **fix(gemini):** strip the JSON-Schema-2020-12 `prefixItems` keyword from Gemini tool schemas at every nesting level, so Claude Code tool definitions no longer fail with `400 Unknown name "prefixItems"` on Gemini models (#12540 — thanks @pacocartones) +- **fix(api-manager):** Expose an accessible loading status while API keys are fetched instead of an empty accessibility tree (#12541 — thanks @pacocartones) +- **fix(video):** Clamp `estimateJpegFrameBytes` at zero for padding-only payloads and build the three encode-side frame data URIs from `JPEG_FRAME_DATA_URI_PREFIX` instead of a repeated literal (#12543 — thanks @pacocartones) +- **fix(devin):** accept Windows `DEVIN_AGENTIC_HOME` sandbox paths (`C:\...\.sandbox\...`) in the isolated-home check so the Devin Claude Bridge no longer fails closed on Windows ([#12405](https://github.com/diegosouzapw/OmniRoute/issues/12405)) (#12545 — thanks @pacocartones) +- **fix(db):** Add `conversation_turn_nodes` and orphaned `agentic_conversations` to the auto-cleanup cycle under the existing `retention.callLogs` window, so identity nodes whose call-log content has already been purged no longer accumulate without bound in `storage.sqlite` (#12548 — thanks @pacocartones) +- **fix(i18n):** Wrap the `~/.claude/profiles//settings.json` placeholder in ICU single quotes in the `OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES` feature-flag description across all 42 locales and the TypeScript default, so next-intl no longer fails with `INVALID_MESSAGE: UNCLOSED_TAG` and the Feature Flags card shows the description instead of the raw key (#12549 — thanks @pacocartones) +- **fix(orchestration):** `updateCloudAgentTask` now publishes the task's real `status` on `agent.task.updated` when an update only touches `result`, `activities` or `error`, instead of the fabricated `"updated"` state, and stays silent when no row matched the id (#12550 — thanks @pacocartones) +- **fix(i18n):** the home "Recent Requests" panel and the Provider Topology legend are now translated instead of rendering English copies on non-English dashboards; the legend reads its own `home.topologyLegend*` labels with consistent casing rather than borrowing the memory-settings "Recent" and analytics "Error" strings (#12551 — thanks @pacocartones). +- **docs(reference):** bring the `FEATURE_FLAGS.md` catalog back to 1:1 with `featureFlagDefinitions.ts` — 20 missing flags added, the two `*_BLOCK_THRESHOLD` env-only knobs moved out of the flag tables, category/total counts and the Live WS port corrected, guarded by a static test (#12552 — thanks @pacocartones) +- **fix(i18n):** backfill missing `usage.kiloPass*` strings in 39 locales and restore `featureFlagOmnirouteDisableThinkingLevelVariantsDescription` in `pt.json` ([#12561](https://github.com/diegosouzapw/OmniRoute/issues/12561)) ([#13027](https://github.com/diegosouzapw/OmniRoute/pull/13027)) — thanks @HouMinXi +- fix(docker): default docker-compose app ports (dashboard/API/live-WS) to loopback instead of `0.0.0.0`, closing the anonymous `/v1` LAN/WAN exposure gap left open by `REQUIRE_API_KEY=false` (#12568) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): close DNS-rebinding SSRF gap in webhook outbound-URL guard (#12569) ([#13243](https://github.com/diegosouzapw/OmniRoute/pull/13243)) +- fix(docker): require a per-session token on the VNC browser CDP bridge and isolate it on a dedicated Docker network (#12571) ([#13241](https://github.com/diegosouzapw/OmniRoute/pull/13241)) +- fix(open-sse): write Adobe Firefly session tokens and cookie jars with 0700/0600 permissions instead of the process umask (#12572) ([#13242](https://github.com/diegosouzapw/OmniRoute/pull/13242)) +- fix(api): remove hardcoded wildcard CORS in openai-to-gemini-sse.ts so the centralized fail-closed CORS gate is the sole source of `Access-Control-Allow-Origin` (#12573) ([#13239](https://github.com/diegosouzapw/OmniRoute/pull/13239)) +- fix(api): enforce API key policy (budget/rate-limit/schedule/endpoint scoping) on the ElevenLabs speech-to-text, text-to-speech and voices proxy routes (#12574) ([#13244](https://github.com/diegosouzapw/OmniRoute/pull/13244)) +- fix(sse): cap HuggingChat NDJSON body size and bound the read loop with the fetch timeout so a stalled or hostile upstream cannot buffer unbounded memory (#12577) ([#13282](https://github.com/diegosouzapw/OmniRoute/pull/13282)) +- fix(docker): scope the cliproxyapi/qdrant/bifrost sidecars to loopback by default and forward `CLIPROXYAPI_MANAGEMENT_KEY` into the cliproxyapi container so its management API is not left both unauthenticated and LAN-published (#12578) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): create DB export temp paths with `fs.mkdtempSync` instead of predictable timestamps (#12579) ([#13237](https://github.com/diegosouzapw/OmniRoute/pull/13237)) +- Cline 401 bodies that say "re-authenticate your Cline account" classify as a refreshable OAuth token, not a terminal expired key. The cooling panel no longer labels every cooldown as a 429; it shows the recorded last error instead. (#12594) ([#13060](https://github.com/diegosouzapw/OmniRoute/pull/13060)) — thanks @HouMinXi +- **fix(catalog):** degrade unknown combo targets instead of dropping LCD modalities, and persist OpenRouter `architecture.input_modalities` into the capability snapshot ([#12613](https://github.com/diegosouzapw/OmniRoute/issues/12613)) ([#13042](https://github.com/diegosouzapw/OmniRoute/pull/13042)) — thanks @HouMinXi +- fix(providers): send `x-api-key` instead of `Authorization: Bearer` for OpenCode Zen's `/v1/responses` endpoint (Muse Spark Contributor models), fixing a 401 on OmniRoute's auth header (#12633) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(sse): surface the actionable "Auggie CLI not found" message when the shell reports a missing `auggie` binary via exit code instead of a spawn error (#12645) ([#13263](https://github.com/diegosouzapw/OmniRoute/pull/13263)) +- **fix(config):** Persist deletions of built-in background-degradation entries — when a stored settings record exists its `degradationMap` is now authoritative instead of being merged under the defaults, so an entry the user removed in the dashboard no longer reappears on the next apply or restart ([#12424](https://github.com/diegosouzapw/OmniRoute/issues/12424)) ([#12647](https://github.com/diegosouzapw/OmniRoute/pull/12647)) — thanks @pacocartones +- **fix(gamification):** action-count badge milestones (First Token, Token Consumer, Token Machine, Token Whale, and the token-sharing tier) are now backed by a durable `xp_action_counts` counter incremented in `addXp()`, instead of a live `COUNT(*)` over `xp_audit_log`. The audit log is pruned by `retention.xpAuditLog` (default 30 days), so on a default install those "lifetime" milestones were really "actions in the last 30 days" and unlocked badges could stop unlocking once old rows aged out. `getActionCount()` and `checkActionCountBadges()` now read the same durable source, and a migration backfills existing totals from the surviving audit rows ([#12546](https://github.com/diegosouzapw/OmniRoute/issues/12546)) ([#12651](https://github.com/diegosouzapw/OmniRoute/pull/12651)) — thanks @pacocartones +- **fix(images):** `/v1/images/edits` now iterates a combo's targets the same way `/v1/images/generations` does (#9239) instead of flattening a bare combo to its first target. A combo whose first target is not edit-capable — or lacks credentials — now falls through to a later edit-capable target rather than hard-erroring, and missing credentials are skipped (not a hard `401`) to match the generations path. The per-target skip/terminal classification is extracted into a shared `runImageComboTargets` loop, so generations behavior is unchanged ([#12547](https://github.com/diegosouzapw/OmniRoute/issues/12547)). ([#12653](https://github.com/diegosouzapw/OmniRoute/pull/12653)) — thanks @pacocartones +- fix(sse): add first-byte watchdog to the TLS-fingerprint transport so a stalled wreq body falls back instead of hanging for minutes (#12656) ([#13272](https://github.com/diegosouzapw/OmniRoute/pull/13272)) +- fix(sse): exempt tiny-budget reasoning probes from combo quality failure and surface persisted-cooldown skips in ALL_TARGETS_SKIPPED diagnostics (#12659) ([#13269](https://github.com/diegosouzapw/OmniRoute/pull/13269)) +- fix(models): declare the real ~1M contextLength for OpenCode Zen's Muse Spark 1.2 models instead of falling back to the 200000 provider default (#12681) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(oauth): align codebuddy-cn OAuth User-Agent with the chat/usage CLI version to avoid WAF false positives (#12702) ([#13264](https://github.com/diegosouzapw/OmniRoute/pull/13264)) +- fix(dashboard): surface an authentication-required banner instead of silently blanking database settings for a guest session (#12709) ([#13270](https://github.com/diegosouzapw/OmniRoute/pull/13270)) +- **fix(resilience):** stop unbounded queue that hangs 6min until Aborted — gate, provider slot, and Bottleneck queue now share a per-connection `maxWaitMs` budget; `fail-closed` on exhaust (503); execution backstop `executionMaxWaitMs` overridable per connection with upstream clamp ([#12715](https://github.com/diegosouzapw/OmniRoute/pull/12715)) — thanks @maxmad64bis (with thanks to @Tushar49 for surfacing the slow-provider need in #12635) +- **fix(auto-combo):** every mode pack now carries `quality` and `reliability` weights (`quality` 0.02, 0.03 in `quality-first`; `reliability` 0.03, 0.04 in `reliability-first`), so selecting a pack no longer silences either signal; `DEFAULT_WEIGHTS` is unchanged ([#12731](https://github.com/diegosouzapw/OmniRoute/pull/12731)) — thanks @maxmad64bis +- **fix(sse):** the provider execution pipeline keeps the upstream error `code`/`type` again — both were lost when the non-streaming leg was extracted, so a config-class `422` (Antigravity missing project) degraded into a generic account cooldown instead of reaching the gate that recognises it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(db):** a legacy `call_logs` table no longer aborts startup — the provider-stats index is created after the column healing runs, not before it, so an install predating `request_type` opens instead of failing with `no such column` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(catalog):** a malformed row in the operator-writable custom-models blob no longer kills every `auto/*` pool ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(tests):** realign the guards that the same day's merges left asserting the old behavior — the provider-count total after GreenPT and EURouter, the GLM stream buffer slot that `#12925` turned into a declared parameter, and the injection scan bound that `#13104` reshaped from a head-only window into head-plus-tail ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- fix(cache): fold tool_choice/tools/response_format into the semantic cache signature so a cached tool_calls response can no longer be replayed for a request whose tool policy forbids it (#12734) ([#13267](https://github.com/diegosouzapw/OmniRoute/pull/13267)) +- **fix(models):** stop treating provider-supplied `isFree:true`/`:free`/`0/0` flags as trusted unless the provider has a documented free tier — only the shipped free-tier catalog decides otherwise; fetched `isFree:true` trusted only for a free-tier provider, custom `isFree:true` via a trusted path ([#12744](https://github.com/diegosouzapw/OmniRoute/pull/12744)) — thanks @maxmad64bis +- fix(memory): authenticate the internal /v1/rerank loopback call so memory reranking no longer silently degrades to unranked order when REQUIRE_API_KEY=true (#12745) ([#13268](https://github.com/diegosouzapw/OmniRoute/pull/13268)) +- fix(sse): parse Ollama Cloud's current usage markup (`$X of $Y used` aria-label, nested width style) (#12749) ([#13260](https://github.com/diegosouzapw/OmniRoute/pull/13260)) +- fix(cli): setup-opencode no longer sends an active context's management token to `/v1/models` when `--api-key`/`OMNIROUTE_API_KEY` is supplied — an explicit flag or the env var now always outranks the context's token, and the flag itself is no longer swallowed by the parent program's global `--api-key` option (#12783) ([#13246](https://github.com/diegosouzapw/OmniRoute/pull/13246)) +- fix(sse): register Arcee AI in the executor provider registry so requests reach api.arcee.ai instead of silently falling back to OpenAI (#12784) ([#13277](https://github.com/diegosouzapw/OmniRoute/pull/13277)) +- **fix(quota):** share one upstream quota read when identical saturation checks arrive at the same time, so traffic bursts don't multiply provider API calls ([#12787](https://github.com/diegosouzapw/OmniRoute/pull/12787)) — thanks @maxmad64bis +- **fix(db):** ignore expired or invalid rate-limit cooldown writes so a stale timestamp can't lock a connection that should be usable — clearing still works as before ([#12788](https://github.com/diegosouzapw/OmniRoute/pull/12788)) — thanks @maxmad64bis +- **fix(providers):** single source for provider order with `xao` ranking alongside `xai-oauth` ([#12790](https://github.com/diegosouzapw/OmniRoute/pull/12790)) — thanks @maxmad64bis +- **fix(routing):** off-table models check the free-model catalog before inheriting premium prices, latency bootstraps from the observed pool median, async tiers read live database pricing with a 90-day freshness gate, and the tier cache invalidates on every pricing write ([#12792](https://github.com/diegosouzapw/OmniRoute/pull/12792)) — thanks @maxmad64bis +- **fix(auto-combo):** snapshot scoring uses the observed per-connection account tier and per-model quality instead of neutral constants, and circuit-open providers rank lower at build time ([#12794](https://github.com/diegosouzapw/OmniRoute/pull/12794)) — thanks @maxmad64bis +- **fix(pool):** empty `auto/*` pools now say why they are empty, and the models listing uses the same paid check as routing ([#12795](https://github.com/diegosouzapw/OmniRoute/pull/12795)) — thanks @maxmad64bis +- fix(routing): recognize CLIProxyAPI's 'unknown provider for model' 400 as fallback-worthy (#12800) ([#13284](https://github.com/diegosouzapw/OmniRoute/pull/13284)) +- **fix(sse):** translate-mode streams now emit the estimated token counts as a trailing usage-only chunk before `[DONE]` when the upstream stays silent, so metered chat clients see totals instead of nothing ([#12828](https://github.com/diegosouzapw/OmniRoute/pull/12828)) — thanks @maxmad64bis +- **fix(db):** provider stats stay truthful on empty and legacy databases: fallback counts default to `0` instead of `null`, latency averages read `null` (not `0`) when no durations were recorded, failures logged before the error-type column existed group under `pre_migration` instead of `unclassified`, and per-provider queries use two new composite indexes ([#12832](https://github.com/diegosouzapw/OmniRoute/pull/12832)) — thanks @maxmad64bis +- fix(nvidia): fail open when a synced model catalog goes stale instead of gating forever (#12849) ([#13248](https://github.com/diegosouzapw/OmniRoute/pull/13248)) +- **fix(db):** WAL maintenance lives in its own module: a `TRUNCATE` checkpoint that hits a busy database now warns with its streak and retries once via `PASSIVE` instead of logging success, and closing-time checkpoints no longer report success on builds without a database file, with the busy totals visible in the authenticated monitoring health payload ([#12853](https://github.com/diegosouzapw/OmniRoute/pull/12853)) — thanks @maxmad64bis +- **fix(health):** quota with no snapshots reads empty instead of a contradicting 0% ([#12857](https://github.com/diegosouzapw/OmniRoute/pull/12857)) — thanks @maxmad64bis +- fix(a2a): accept the dashboard's own session cookie on /a2a so "Run message/send" no longer fails with "Unauthorized: missing or invalid API key" (#12888) ([#13271](https://github.com/diegosouzapw/OmniRoute/pull/13271)) +- **fix(logs):** the Logs grid's in-memory filter pass no longer discards rows the SQL query already matched — selecting an API key from the dropdown (which sends the key's id) returns its calls again, the Combo tab shows every combo instead of only those whose name contains a "1", and the model filter and search cover the same columns as the query ([#12896](https://github.com/diegosouzapw/OmniRoute/pull/12896)) — fixes [#12873](https://github.com/diegosouzapw/OmniRoute/issues/12873) — thanks @ntdatt812 +- **fix(a2a):** `/api/a2a/status` now builds the agent card from the request that asked for it, so a gateway reached at a non-localhost host no longer advertises `http://localhost:20128` as its A2A URL ([#12918](https://github.com/diegosouzapw/OmniRoute/pull/12918)). — thanks @ntdatt812 +- **fix(compression):** progressive aging now appends its `[COMPRESSED:aging:…]` annotation after a turn's `tool_result` blocks instead of in front of them, so Anthropic no longer rejects aged conversations with "`tool_use` ids were found without `tool_result` blocks immediately after" ([#12920](https://github.com/diegosouzapw/OmniRoute/pull/12920)). — thanks @ntdatt812 +- **fix(bedrock):** model import now resolves context limits for every vendor prefix instead of only `anthropic.*`, so `global.openai.gpt-5.6-*` no longer imports with a null `inputTokenLimit` and gets rejected pre-flight at the 200k default ([#12921](https://github.com/diegosouzapw/OmniRoute/pull/12921)). — thanks @ntdatt812 +- **fix(stream):** the 64 KB stream buffer GLM asks for is honoured instead of dropped, and the type error it caused no longer fails the API Route Typecheck gate on every open PR ([#12925](https://github.com/diegosouzapw/OmniRoute/pull/12925)) — thanks @ntdatt812 +- **fix(guardrails):** mask PII inside a `tool_result`'s nested content array, which the masker walked past while redacting its sibling block ([#12930](https://github.com/diegosouzapw/OmniRoute/pull/12930)) — thanks @ntdatt812 +- **fix(dashboard):** the Radar catalog table no longer leaves absent data unexplained — empty limits, unknown context windows, and unreported capabilities each explain themselves on hover, and a new check keeps it that way ([#12937](https://github.com/diegosouzapw/OmniRoute/pull/12937)) — thanks @maxmad64bis +- **fix(sse):** geo-blocked opencode requests rotate to the next account proxy instead of failing, so one refused egress no longer aborts the whole chain ([#12941](https://github.com/diegosouzapw/OmniRoute/pull/12941)) — thanks @maxmad64bis +- **fix(codex):** dashboard "clear cooldown" and the CAS recovery path now drop nested `codexScopeRateLimitedUntil` maps in the same write that nulls `rate_limited_until`, and fresh quota snapshots with headroom lift fallback-sourced scope cooldowns parked by quota preflight ([#12817](https://github.com/diegosouzapw/OmniRoute/issues/12817), [#12860](https://github.com/diegosouzapw/OmniRoute/issues/12860), [#12951](https://github.com/diegosouzapw/OmniRoute/pull/12951)) The dashboard reset-credit button (`consumeCodexResetCredit`) now exercises that same snapshot path after a successful redeem, so a filled quota bar is enough to unpark a leftover fallback Codex child without another manual clear. — thanks @HouMinXi +- **fix(db):** resolve `sql-wasm.wasm` across global npm install and hoisted layouts, ensuring OmniRoute can boot cleanly on Node 24 when native `better-sqlite3` is uncompiled. ([#13035](https://github.com/diegosouzapw/OmniRoute/pull/13035)) — thanks @HouMinXi +- **fix(api):** `DELETE /v1/batches/delete-completed` now sweeps only the calling API key's own completed batches (batches with no owner stay out of a key-scoped sweep on purpose), with an explicit instance-wide mode reserved for authenticated dashboard sessions, a 401 for a presented key that is unknown, revoked, deactivated, banned or expired (never falling through to the session branch), audit logging of every sweep, a sanitized 500 on failure, an owner-scoped file half (a key-scoped sweep never nulls a file another tenant owns) and an atomic sweep — chunked in 200-batch transactions in instance mode — so a mid-way error never leaves a batch pointing at a nulled file (GHSA-wvxc-jp3v-5mg5) ([#12969](https://github.com/diegosouzapw/OmniRoute/pull/12969)) +- **fix(combo):** quota-weighted routing stops drawing on an out-of-credit connection — a 402 now invalidates the stored quota snapshot instead of leaving its stale remaining percentage in place, and a snapshot older than 10 minutes no longer counts as confident headroom for the primary pool ([#12972](https://github.com/diegosouzapw/OmniRoute/pull/12972)) ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- **fix(vision):** Custom Models with "Vision capable" checked no longer have image requests swapped to `glm/glm-4.6v` when the client sends the advertised alias (`vllm/path/...`) or the bare path-shaped id — Vision Bridge now matches the stored override for all three id forms ([#12758](https://github.com/diegosouzapw/OmniRoute/issues/12758)) ([#12974](https://github.com/diegosouzapw/OmniRoute/pull/12974)) — thanks @HouMinXi +- **fix(sse):** transient opencode upstream failures rotate to the next account proxy instead of failing, so one flapping egress no longer aborts the whole chain ([#12975](https://github.com/diegosouzapw/OmniRoute/pull/12975)) — thanks @maxmad64bis +- **fix(azure):** Deployments from GPT-6 onward now send `max_completion_tokens` instead of `max_tokens`, which Azure rejects with HTTP 400. The rule matched a literal `gpt-5`, so each new generation arrived broken; it now matches the generation range, while `gpt-35-turbo` still keeps `max_tokens`. ([#13007](https://github.com/diegosouzapw/OmniRoute/pull/13007)) — thanks @ntdatt812 +- **fix(db):** add an opt-in self-restart circuit for sustained critical memory pressure, gate post-cleanup VACUUM behind a minimum freed-rows threshold, and checkpoint the SQLite WAL every 5 minutes with a size guard that escalates to TRUNCATE, so a growing WAL can no longer stall the event loop into a full outage. ([#13011](https://github.com/diegosouzapw/OmniRoute/pull/13011)) — thanks @HouMinXi +- **fix(auth):** an explicit connection pin may probe a quota-disabled row once and re-enable it on success ([#12874](https://github.com/diegosouzapw/OmniRoute/issues/12874)) ([#13017](https://github.com/diegosouzapw/OmniRoute/pull/13017)) — thanks @HouMinXi +- **fix(api):** thread `X-OmniRoute-Fallback-Attempts` through combo chat completions so streaming and non-streaming responses report how many prior legs were attempted ([#13038](https://github.com/diegosouzapw/OmniRoute/pull/13038)) — thanks @HouMinXi +- Restore provider failure classification and credential refresh on non-streaming requests: classify non-2xx failures to lock models on per-model quota exhaustion, update connection rate limits from headers and body, and pass credential refresh handlers to pipeline execution so 401 tokens can be refreshed and retried (#13043). ([#13069](https://github.com/diegosouzapw/OmniRoute/pull/13069)) — thanks @HouMinXi +- **fix(responses):** wrap forced-non-streaming web_search fallback JSON as Responses SSE so Codex still sees `response.completed` ([#13050](https://github.com/diegosouzapw/OmniRoute/pull/13050)) — thanks @HouMinXi +- **fix(antigravity):** Preserve upstream thought-token usage in normalized completion and reasoning token counts ([#13055](https://github.com/diegosouzapw/OmniRoute/pull/13055)) — thanks @pacocartones +- Fix a concurrency-slot leak in the DeepSeek PoW solver: a worker that failed to spawn (for example a missing worker script) never released its slot, so `MAX_CONCURRENT_WORKERS` failures disabled the solver until restart. ([#13097](https://github.com/diegosouzapw/OmniRoute/pull/13097)) — thanks @anhtahaylove +- **fix(acp):** bound the ACP session output buffers — `stdoutBuffer` and `stderrBuffer` now cap at 1 MiB keeping the most recent output behind a visible `[...output truncated...]` marker, and `stderrBuffer` is reset per prompt instead of accumulating for the lifetime of the session. ([#13100](https://github.com/diegosouzapw/OmniRoute/pull/13100)) — thanks @anhtahaylove +- **fix(acp):** release the `stdout`/`exit` listeners and the idle timer that a `sendPrompt` timeout used to leave attached to the `acpManager` singleton, and drop sessions that exited on their own from the session map instead of keeping them forever. ([#13096](https://github.com/diegosouzapw/OmniRoute/pull/13096)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection and PII scanners now read the text a `tool_result` block carries on `content` (string or nested block list), in messages and in system blocks, so tool output is judged by the same rules as user text ([#13101](https://github.com/diegosouzapw/OmniRoute/pull/13101)) — thanks @ntdatt812 +- **fix(gamification):** close the badge notification SSE stream when the request signal is already aborted before the stream starts — a client that disconnects while the route is still awaiting auth used to leave both the 2s unlock poll and the 15s heartbeat running for the lifetime of the process. ([#13106](https://github.com/diegosouzapw/OmniRoute/pull/13106)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection scan now spends its 16 KB budget on both ends of the request instead of the first 16 KB only, so `system`, `instructions`, `query`, `documents` and the newest turns are no longer hidden behind one long message ([#13104](https://github.com/diegosouzapw/OmniRoute/pull/13104)) — thanks @ntdatt812 +- **fix(dashboard):** expose the Volcano Ark console cookie on quota scraping and unwrap connect-error objects so the dashboard shows the upstream message ([#13107](https://github.com/diegosouzapw/OmniRoute/pull/13107)) — thanks @HouMinXi +- **fix(db):** release the `beforeExit`/`SIGINT`/`SIGTERM` handlers when a `node:sqlite` adapter closes, so a closed adapter and its database handle are no longer pinned to `process` for the lifetime of the run — the same treatment #7494 gave the sql.js adapter. ([#13109](https://github.com/diegosouzapw/OmniRoute/pull/13109)) — thanks @anhtahaylove +- **fix(translator):** `contentSchema` and `unevaluatedItems` are now treated as subschema positions by the tool-schema sanitizer, so a truncation placeholder in either is replaced with a permissive schema instead of being forwarded as a string ([#13110](https://github.com/diegosouzapw/OmniRoute/pull/13110)) — thanks @ntdatt812 +- **fix(cli-helper):** clear the `createLogStream` timeout on the abort path — `stop()` aborts the in-flight fetch and returned through the `signal.aborted` branch, which skipped `clearTimeout` and left an armed timer per stopped stream. The stream reader is now also cancelled when the read loop exits early. ([#13114](https://github.com/diegosouzapw/OmniRoute/pull/13114)) — thanks @anhtahaylove +- **fix(docs):** drop TLS-impersonation, MITM-decrypt, supply-chain attestation, and XOR-mask writeups from the public `/docs` catalog and Docker image. Files stay in git for engineers; operators who need them open the repo, not the website. ([#13136](https://github.com/diegosouzapw/OmniRoute/pull/13136)) — thanks @HouMinXi +- **fix(combo):** parse numeric-epoch `rate_limited_until` in the combo cooldown read path ([#13141](https://github.com/diegosouzapw/OmniRoute/pull/13141)) — thanks @maxmad64bis +- **fix(opencode):** both OpenCode plugins now reject a gateway address typed without `http://` at configuration time, instead of publishing every model with an api url no client can call, and the v2 plugin no longer publishes a model card whose api url is blank or relative ([#13142](https://github.com/diegosouzapw/OmniRoute/pull/13142)) — thanks @maxmad64bis +- **fix(providers):** lock opencode model on upstream 400 model-unavailable ([#13146](https://github.com/diegosouzapw/OmniRoute/pull/13146)) — thanks @maxmad64bis +- **fix(logging):** keep the provider exchange rather than the raw client bodies when a call log exceeds its size budget, and show that recovered payload in the request-detail panel instead of replacing it with the stored response body ([#13147](https://github.com/diegosouzapw/OmniRoute/pull/13147)) — thanks @maxmad64bis +- **fix(traffic-inspector):** the WebSocket route no longer leaks a traffic-buffer subscriber and a 30s ping timer when the client socket is already closed at handler time — listeners are attached before any resource is acquired, a destroyed socket bails out early, and the ping interval stops on a dead socket where `write()` never throws ([#13155](https://github.com/diegosouzapw/OmniRoute/pull/13155)) — thanks @anhtahaylove +- **fix(telegram):** bound the per-user API key cache in the Telegram chat proxy so a burst of distinct chat ids can no longer grow the process heap without limit ([#13165](https://github.com/diegosouzapw/OmniRoute/issues/13165)) ([#13166](https://github.com/diegosouzapw/OmniRoute/pull/13166)) — thanks @anhtahaylove +- **fix(stream):** cancel the upstream response body when the JSON-to-SSE sniff unwinds on a body timeout, so a stalled upstream no longer pins the connection ([#13169](https://github.com/diegosouzapw/OmniRoute/issues/13169)) ([#13171](https://github.com/diegosouzapw/OmniRoute/pull/13171)) — thanks @anhtahaylove +- **fix(telegram):** authenticate webhook deliveries with Telegram's `secret_token` so an unauthenticated caller can no longer mint API keys or spend upstream quota ([#13172](https://github.com/diegosouzapw/OmniRoute/issues/13172)) ([#13175](https://github.com/diegosouzapw/OmniRoute/pull/13175)) — thanks @anhtahaylove +- **fix(models):** give discoverable Gemini 3.8 Flash ids their own 65536 output spec so Antigravity no longer clamps them to 16384 ([#13195](https://github.com/diegosouzapw/OmniRoute/pull/13195)) — thanks @HouMinXi +- **fix(dashboard):** leftover catalog-removed provider rows (gemini-cli) can be listed and purged from the providers page ([#13067](https://github.com/diegosouzapw/OmniRoute/issues/13067)) ([#13197](https://github.com/diegosouzapw/OmniRoute/pull/13197)) — thanks @HouMinXi +- **fix(auth):** a dashboard session now requires the `authenticated: true` claim that login, OIDC and the session refresh already emit — a JWT merely signed with `JWT_SECRET` (for example the Cursor CLI passthrough token, which any API-key holder can obtain) no longer verifies as the `auth_token` cookie on any route, the WebSocket handshake or the live server; existing sessions keep working ([#13298](https://github.com/diegosouzapw/OmniRoute/issues/13298)) ([#13375](https://github.com/diegosouzapw/OmniRoute/pull/13375)) +- **fix(api):** `DELETE /v1/batches/delete-completed` now applies the caller's API-key policy (endpoint allowlist, schedule, usage cap, rate limit) like every other `/v1` route, commits the key-scoped sweep per 200-batch chunk instead of holding one write lock for the whole sweep, refuses to loop without progress, and rejects a scope that names both a key and `allTenants` ([#13684](https://github.com/diegosouzapw/OmniRoute/pull/13684)) +- Restore the API-route typecheck gate: the non-streaming leg result lost its discriminated-union narrowing after the server-owned tool loop reassignment, producing 13 new TS2339 diagnostics in `chatCore.ts`. ([#12963](https://github.com/diegosouzapw/OmniRoute/pull/12963)) +- Restore four non-streaming behaviours the server-owned tool loop refactor dropped: client aborts map to 499 with the fixed `Request aborted` message, an aborted request no longer logs a synthetic `clientResponse`, Claude prompt-cache telemetry is recorded again, and a body that cannot be canonicalized no longer throws when the tool loop is off. ([#12990](https://github.com/diegosouzapw/OmniRoute/pull/12990)) +- **fix(skills):** The CLI registry parser now reads positionals declared with `.addArgument()`, not only those written inline in `.command()`. `tunnel create [type]` was being published as `tunnel create`, so the agent-skills sync gate reported drift on every branch and regenerating would have deleted the argument. ([#13009](https://github.com/diegosouzapw/OmniRoute/pull/13009)) — thanks @ntdatt812 +- **fix(combos):** dashboard combo test uses a short prompt, serial probes, and a 60s timeout so reasoning models and rate-limited free pools do not fail the health check ([#13001](https://github.com/diegosouzapw/OmniRoute/pull/13001)) — thanks @HouMinXi +- fix(compression): terminate idle worker threads on eviction so long-running instances stop leaking OS threads and MessagePorts ([#13091](https://github.com/diegosouzapw/OmniRoute/pull/13091)) — thanks @anhtahaylove +- Redact Google API keys of any length in error bodies: the pattern required exactly 39 characters, so shorter or longer `AIza…` credentials passed through unredacted. ([#12964](https://github.com/diegosouzapw/OmniRoute/pull/12964)) +- **fix(grok-cli):** a 402 "Grok Build usage balance exhausted" parks that Grok login as out of credit (Grok Build CLI, grok.com cookie, and xAI OAuth share the weekly pool). Combo routing then tries the next login instead of locking the model for every account in the pool ([#13061](https://github.com/diegosouzapw/OmniRoute/pull/13061)) — thanks @HouMinXi +- fix(compression): spawn the LLMLingua worker with a file URL object so compression actually runs instead of silently failing open on Node ([#13093](https://github.com/diegosouzapw/OmniRoute/pull/13093)) — thanks @anhtahaylove +- **fix(test):** run the local `test` and `test:unit` scripts at concurrency 4 so a full-suite run no longer exhausts the machine's commit charge and kills unrelated processes ([#13187](https://github.com/diegosouzapw/OmniRoute/pull/13187)) — thanks @anhtahaylove +- **fix(monitoring):** `GET /api/monitoring/health` `credentialHealth` now includes a bounded `failedConnections` list (`connectionId`, `status`, sanitized `lastError`) when the probe-cache gauge `failed>0`, plus `source: probe-cache` and a cheap `staleDbNonOkCount` for sticky SQLite `test_status` on active rows. Documents that the live gauge is not `provider_connections.test_status`. ([#12876](https://github.com/diegosouzapw/OmniRoute/pull/12876)) — thanks @RaviTharuma +- **fix(resilience):** an openai-compatible multi-upstream gateway no longer marks the whole connection `credits_exhausted` when a single upstream (or the `/models` / representative-model health probe) returns 402. Quota failures stay model-scoped; true connection-wide auth failures are unchanged. Single-credential 402 key disable ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239)) is preserved. ([#12875](https://github.com/diegosouzapw/OmniRoute/pull/12875)) — thanks @RaviTharuma +- **fix(dashboard):** nine fixes on the Orchestration Canvas, all diagnosed in the Phase 2 reviews. A source that fails now keeps the timestamp of its FIRST failure instead of being re-stamped every poll — the stale line said "since the last poll" no matter how long the source had been down, and the churn also defeated the snapshot's stable identity (it serializes the source list), so the canvas re-rendered on every tick while anything was broken. A source that HAD data and only then started failing is flagged too: previously only a source with no node at all got the warning, so a source that went down mid-session kept a healthy-looking node forever. The rest are pointwise: clicking a filter chip cancels the pending search debounce (left armed, it fired ~300ms later and silently reverted the chip); the search input carries an accessible name; `?state=running, failed` parses like the unpadded form instead of dropping the padded value; the CSV toggle helper is defined once in `model/urlParams.ts` rather than twice; the agents tab tells "nothing running" apart from "the filter matched nothing", offering a clear-filters button instead of setup links that would be wrong advice there; edges stop emitting SMIL particles above 40 simultaneously active edges, keeping the colored stroke; and the drawer's error banner clears when a retried action succeeds. ([#12983](https://github.com/diegosouzapw/OmniRoute/pull/12983)) +- **fix(i18n):** the nine locales added with the EU-language batch (Greek, Estonian, Irish, Croatian, Lithuanian, Latvian, Maltese, Slovenian, Serbian) were missing the eleven Orchestration Canvas keys that Phase 3 introduced, so the compare-runs panel and the "no runs match these filters" empty state fell back to English in those languages (`deepMergeFallback` substitutes English for an absent key, so nothing rendered blank — it rendered untranslated). The coverage gate does not catch this: it enforces an 80% floor per locale, and eleven missing keys out of ~13,000 leaves coverage at 99.9%. Translated for real in each language, calibrated against the wording each file already uses for "run", "filter" and "skill". ([#13294](https://github.com/diegosouzapw/OmniRoute/pull/13294)) +- **fix(dashboard):** five follow-ups from the Phase 2 Orchestration Canvas review. The History drawer now shows the memory section for runs that already left the live TTL window: the persisted `memory_hits` event is parsed into `metadata.memoryHits` with the same defensive validation the drawer applies, and — because that event is observability rather than a state transition — it no longer leaks into the timeline, where it had been inheriting the task's state and rendering as a duplicate transition. The A2A memory recall runs against its own 1.5s deadline instead of inheriting the memory backend's 30s one; overshooting degrades exactly like any other recall failure (no hits, task proceeds), and the timer is cleared on both paths. Repeating a Conductor run carries its `requirements.cli`/`requirements.model` forward, so the new run is pinned to the same runner profile and model rather than drifting to whatever the fleet picks. A successful repeat from the Agents tab now focuses the run it created, instead of leaving the operator on the finished one. And the auth test for `POST /api/conductor/tasks` moved into the shared `ROUTES` array rather than restating the pattern. ([#12988](https://github.com/diegosouzapw/OmniRoute/pull/12988)) +- fix(plugins): stop leaking an exit listener per plugin hook timeout, which triggered MaxListenersExceededWarning on plugins that ignore SIGTERM ([#13092](https://github.com/diegosouzapw/OmniRoute/pull/13092)) — thanks @anhtahaylove +- **fix(validation):** Provider node edits no longer fail with a generic "Invalid request" when the optional daily-quota reset fields are left blank. The dashboard sends `dailyQuotaResetTimezone` and `dailyQuotaResetHour` as `null`, and only the hour accepted it. ([#13066](https://github.com/diegosouzapw/OmniRoute/issues/13066)) ([#13083](https://github.com/diegosouzapw/OmniRoute/pull/13083)) — thanks @ntdatt812 +- **fix(combo):** quota-aware expansion drops banned, inactive, missing, and wrong-provider connections before quota fetch or model dispatch; pins and allowlists stay selectors, not a bypass. Antigravity automatic exhaustion now requires a reported zero remaining, so a positive balance below 1% stays eligible. ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- Fix an intermittent failure in the quota-weighted routing test suite: scores are a function of `Date.now()`, so peers with identical quota scored microseconds apart never tied and swapped order. ([#12946](https://github.com/diegosouzapw/OmniRoute/pull/12946)) +- fix(volcengine): resolve quota window mapping gaps, safe multi-connection console binding, and response cookie sanitization ([#12950](https://github.com/diegosouzapw/OmniRoute/pull/12950)) — thanks @HouMinXi +- **fix(test):** resolve the WebDAV handler path with `fileURLToPath` so the suite's 37 WebDAV tests run on Windows instead of failing with a doubled `C:\C:\` drive prefix ([#13196](https://github.com/diegosouzapw/OmniRoute/pull/13196)) — thanks @anhtahaylove +- **fix(ui):** fall back from invalid AnySearch icon alias (direct commit `9587e07b69`) +- **fix(sse):** stop rebuilding a truncated summary from the collector's cap-dropped event array ([#12718](https://github.com/diegosouzapw/OmniRoute/pull/12718)) — thanks @hartmark +- **fix(conversations):** show a pending spinner for unresolved tool nodes instead of "(empty)" ([#12727](https://github.com/diegosouzapw/OmniRoute/pull/12727)) — thanks @hartmark +- **fix(streaming):** fail fast when an upstream stream produces only lifecycle/heartbeat events, never real content ([#12741](https://github.com/diegosouzapw/OmniRoute/pull/12741)) — thanks @hartmark +- **fix(responses-continuation):** bridge the write-in-flight window with an in-memory pending store ([#12854](https://github.com/diegosouzapw/OmniRoute/pull/12854)) — thanks @hartmark +- **fix(translator):** drop or map agent_message on Chat Completions fallback ([#12880](https://github.com/diegosouzapw/OmniRoute/pull/12880)) — thanks @RaviTharuma +- **fix(api):** page and stream-complete GET /v1/models for large catalogs ([#12882](https://github.com/diegosouzapw/OmniRoute/pull/12882)) — thanks @RaviTharuma +- **fix(quota):** align AUTH window labels with usage API and clarify 503 ([#12884](https://github.com/diegosouzapw/OmniRoute/pull/12884)) — thanks @RaviTharuma +- **fix(ci):** clear two base-reds on release/v3.8.51 — mutation-coverage gate + image-only-model guard ([#12945](https://github.com/diegosouzapw/OmniRoute/pull/12945)) +- **fix(api):** scope batch bulk-delete to the calling API key ([#13211](https://github.com/diegosouzapw/OmniRoute/pull/13211)) +- **fix(tests):** align model-sync log assertion to shipped path redaction (base-red) ([#13227](https://github.com/diegosouzapw/OmniRoute/pull/13227)) +- **fix(test):** make the ToS heading guard actually require the parentheses ([#13228](https://github.com/diegosouzapw/OmniRoute/pull/13228)) +- **fix(quality):** register 4 drifted covering tests in stryker tap.testFiles ([#13229](https://github.com/diegosouzapw/OmniRoute/pull/13229)) +- **fix(logging):** mask *-api-key headers in request-log pipeline (#13273) ([#13401](https://github.com/diegosouzapw/OmniRoute/pull/13401)) — thanks @KooshaPari +- **fix(tests):** correct stale Jina catalog prefix in custom-models test ([#13403](https://github.com/diegosouzapw/OmniRoute/pull/13403)) — thanks @KooshaPari +- **fix(backend):** prune old backups after health-check-repair snapshots (#13308) ([#13404](https://github.com/diegosouzapw/OmniRoute/pull/13404)) — thanks @KooshaPari +- **fix(backend):** guard memory_fts_au trigger with WHEN clause to stop FTS bloat (#13326) ([#13405](https://github.com/diegosouzapw/OmniRoute/pull/13405)) — thanks @KooshaPari +- **fix(free-tiers):** remove obsolete Together $25 signup credit (#12526) ([#13407](https://github.com/diegosouzapw/OmniRoute/pull/13407)) — thanks @KooshaPari +- **fix:** skip non-chat generation models in Test all models ([#13409](https://github.com/diegosouzapw/OmniRoute/pull/13409)) — thanks @KooshaPari +- **fix(providers):** de-duplicate agy and antigravity model catalogs ([#13410](https://github.com/diegosouzapw/OmniRoute/pull/13410)) — thanks @KooshaPari +- **fix(dashboard):** send provider in body for mini-playground web search ([#13411](https://github.com/diegosouzapw/OmniRoute/pull/13411)) — thanks @KooshaPari +- **fix(resilience):** add CJK quota-exhaustion patterns to 429 classifier ([#13412](https://github.com/diegosouzapw/OmniRoute/pull/13412)) — thanks @KooshaPari +- **fix(backend):** eval runner: failed calls no longer score as passed + dotAll regex ([#13413](https://github.com/diegosouzapw/OmniRoute/pull/13413)) — thanks @KooshaPari +- **fix(backend):** eval runner opts out of output-style and memory injection ([#13414](https://github.com/diegosouzapw/OmniRoute/pull/13414)) — thanks @KooshaPari +- **fix:** chain serial quarantine step in npm test script (fixes #13286) ([#13418](https://github.com/diegosouzapw/OmniRoute/pull/13418)) — thanks @KooshaPari +- **fix(antigravity):** derive stable session ID from credentials for prompt-cache reuse (#13305) ([#13424](https://github.com/diegosouzapw/OmniRoute/pull/13424)) — thanks @KooshaPari +- **fix(memory):** merge memory into top-level system field instead of unshifting at messages[0] (#13425) ([#13427](https://github.com/diegosouzapw/OmniRoute/pull/13427)) — thanks @KooshaPari +- **fix(compression):** preserve instruction blocks from lossy engines ([#13523](https://github.com/diegosouzapw/OmniRoute/pull/13523)) — thanks @KooshaPari +- **fix(compression):** preserve polarity words and newlines in ultra heuristic ([#13524](https://github.com/diegosouzapw/OmniRoute/pull/13524)) — thanks @KooshaPari +- **fix(compression):** run user preservePatterns before built-in patterns (#13457) ([#13525](https://github.com/diegosouzapw/OmniRoute/pull/13525)) — thanks @KooshaPari +- **fix(reasoning):** expand deepseek-v4 regex to match v4.1+ and add command-code provider (#13430) ([#13528](https://github.com/diegosouzapw/OmniRoute/pull/13528)) — thanks @KooshaPari +- **fix(providers):** add alwaysReasons metadata for reka-flash-3 ([#13531](https://github.com/diegosouzapw/OmniRoute/pull/13531)) — thanks @KooshaPari +- **fix(playground):** allow Compare columns to scroll independently ([#13532](https://github.com/diegosouzapw/OmniRoute/pull/13532)) — thanks @KooshaPari +- **fix(security):** empty whitelist allows through to prevent admin lockout ([#13534](https://github.com/diegosouzapw/OmniRoute/pull/13534)) — thanks @KooshaPari +- **fix(tests):** add DISABLE_IOREG_STRATEGY env var for macOS test compatibility (fixes #13467) ([#13539](https://github.com/diegosouzapw/OmniRoute/pull/13539)) — thanks @KooshaPari +- **fix(zed-hosted):** map developer role and Gemini enums for Zed proxy ([#13543](https://github.com/diegosouzapw/OmniRoute/pull/13543)) — thanks @KooshaPari +- **fix(analytics):** normalize provider aliases to canonical id in usage_history ([#13545](https://github.com/diegosouzapw/OmniRoute/pull/13545)) — thanks @KooshaPari +- **fix(combo):** use traceId for call log id to prevent UNIQUE constraint failure ([#13546](https://github.com/diegosouzapw/OmniRoute/pull/13546)) — thanks @KooshaPari +- **fix(auth):** check model lockout before returning synthetic noauth connection ([#13547](https://github.com/diegosouzapw/OmniRoute/pull/13547)) — thanks @KooshaPari +- **fix(guardrails):** skip credential redaction for base64 image data URLs ([#13550](https://github.com/diegosouzapw/OmniRoute/pull/13550)) — thanks @KooshaPari +- **fix(resilience):** increase requestQueue.maxWaitMs default from 15s to 30s ([#13553](https://github.com/diegosouzapw/OmniRoute/pull/13553)) — thanks @KooshaPari +- **fix(docs):** document OMNIROUTE_READY_TIMEOUT_MS and allowlist the test-only DISABLE_IOREG_STRATEGY ([#13692](https://github.com/diegosouzapw/OmniRoute/pull/13692)) + ### 📝 Maintenance ---- +- **docs(readme):** reconcile live v3.8.50 provider, free-tier, CLI, routing, test, community, sponsor, acknowledgment, and SVG metrics with their audited source denominators, including a deduplicated OmniRoute-in-Action snapshot and distinct contributor rankings for merged pull requests, GitHub-attributed commits, and Git history ([#11356](https://github.com/diegosouzapw/OmniRoute/pull/11356)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **docs:** sync the canonical provider count 354 → 356 across `README.md`, `AGENTS.md`, `llm.txt` (+ 42 i18n mirrors), the four README SVG diagrams, `docs/reference/PROVIDER_REFERENCE.md` (regenerated) and the `package.json` description after Opper (#11629) and 1min.ai (#11631) boarded the catalog — closes the `check:docs-counts-sync` strict drifts that kept `release/v3.8.51` red ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11673](https://github.com/diegosouzapw/OmniRoute/pull/11673)) — thanks @jonlwheat2-gif +- **test(providers):** update count-derived assertions after the v3.8.51 provider additions — `APIKEY_PROVIDERS` 233 → 235 (Opper #11629 + 1min.ai #11631), reserved-prefix REGISTRY walk 395 → 398, `WEB_FETCH_PROVIDERS` now includes `nimble-search` (#11620), and the provider translate-path golden snapshot regenerated ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11674](https://github.com/diegosouzapw/OmniRoute/pull/11674)) — thanks @jonlwheat2-gif +- **test(sse):** bump the hard-lease connection-query inventory for `src/lib/tokenHealthCheck.ts` to 2 — the verify-only web-cookie sweep added by #11495 split the single `getProviderConnections` call into oauth + cookie variants, which the frozen inventory had not tracked ([#11495](https://github.com/diegosouzapw/OmniRoute/pull/11495)). ([#11677](https://github.com/diegosouzapw/OmniRoute/pull/11677)) — thanks @jonlwheat2-gif / @oyi77 +- **chore(release):** clear the `release/v3.8.51` base-red gates — docs migration count (+38 i18n mirrors), stryker `tap.testFiles`, lockfile registry host, stale ESLint suppressions, the 7 lint regressions they were hiding, the stale `APIKEY_PROVIDERS` partition count (231 → 233), the `glm-5.3-max` tier missing from the GLM test table, a dead `modelType === "chat"` comparison in the models catalog, and the `resolveLiveWsUrl` / `sanitizeLiveWsPort` helpers from #11388 that a later merge dropped from `wsPath.ts` (dashboard live WebSocket URL resolution) ([#11502](https://github.com/diegosouzapw/OmniRoute/pull/11502)) — thanks @MumuTW +- **chore(free-models):** the free-tier regime table now declares which totals figure each regime feeds and whether it may take the no-auth shortcut, so the sets that used to repeat those answers by hand are derived from it — a new `freeType` no longer compiles until it has answered every question, instead of silently contributing to no total ([#11537](https://github.com/diegosouzapw/OmniRoute/pull/11537)) — thanks @maxmad64bis +- **docs(routing):** LKGP strategy copy now states provider-level stickiness and the explicit rules fallback instead of promising per-target pinning ([#11555](https://github.com/diegosouzapw/OmniRoute/pull/11555)) — thanks @maxmad64bis +- **test(chatcore):** pin the response tool-name alias resolution against the ordering hazard that broke every Gemini/Antigravity MCP tool call in v3.8.49. `extractRequestToolIdentityMap` deletes `translatedBody._toolNameMap`, so the later read is always undefined and the ledger survives only through the `requestToolIdentityMap` fallback — removing that fallback previously left the entire tool-name suite green. The resolution moves into `resolveResponseToolNameMap()` next to the map it depends on, with a regression guard that fails without the recovery. No behaviour change. ([#11575](https://github.com/diegosouzapw/OmniRoute/pull/11575)) — thanks @amartinawi +- **docs(env):** document `OMNIROUTE_CORPUS_CACHE_SIZE` in `.env.example` and `ENVIRONMENT.md`, so the env/docs contract check stops failing Docs Gates on every branch ([#11576](https://github.com/diegosouzapw/OmniRoute/pull/11576)) — thanks @ntdatt812 +- **chore(stryker):** register `route-guard-tunnel-processes-local-only` and `universal-quota-aware-routing` in `tap.testFiles`, so their mutant kills count for the three modules they cover ([#11577](https://github.com/diegosouzapw/OmniRoute/pull/11577)) — thanks @ntdatt812 +- **test(authz):** pin `LOCAL_ONLY_API_GET_EXEMPTIONS` by exact membership instead of by entry count, so the guard names the offending path and also catches a substitution ([#11580](https://github.com/diegosouzapw/OmniRoute/pull/11580)) — thanks @ntdatt812 +- **test(build):** pin `resolveNextBuildBundlerFlag` to the `OMNIROUTE_USE_TURBOPACK` contract it implements, across runtimes, instead of asserting an unimplemented Bun override ([#11583](https://github.com/diegosouzapw/OmniRoute/pull/11583)) — thanks @ntdatt812 +- **test(claude-web):** the Claude Web transport test's barrel anchors no longer pin the exact construction spelling. #11421 made the executor map lazy, so `/"claude-web": new ClaudeWebExecutor\(\)/` stopped matching and the test went red on `release/v3.8.51` itself — a base-red gate every open PR inherited. The anchors now match the alias key and the class name and tolerate either spelling in between, and were checked to still fail when an alias is genuinely repointed at another executor ([#11585](https://github.com/diegosouzapw/OmniRoute/pull/11585)) — thanks @ntdat812 +- **test(models):** pin `qwen3.8-max` resolution per provider — two of the four catalogs now serve the bare id and no longer carry `-preview` ([#11590](https://github.com/diegosouzapw/OmniRoute/pull/11590)) — thanks @ntdatt812 +- **test(executors):** drop the stale 128-tool truncation assertion that contradicted the #11444 pass-through contract ([#11591](https://github.com/diegosouzapw/OmniRoute/pull/11591)) — thanks @ntdatt812 +- **test(token-health):** pin the current terminal-skip boundary — expired with retry budget is probed, `account_deactivated` stays skipped ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)) — thanks @ntdatt812 +- **test(models):** follow #11503's retarget of the `gemini-3-pro-high` alias to the hyphenated catalog id ([#11593](https://github.com/diegosouzapw/OmniRoute/pull/11593)) — thanks @ntdatt812 +- **refactor(video bridge):** extract per-video acquisition, whole-result caching, description, and metrics/abort/cleanup out of `VideoBridgeGuardrail.preCall` into a `processVideoPart` seam in a new `videoBridgePipeline.ts`, behind explicit `VideoMediaBrokerPort`, `VideoAudioTranscriptionPort`, and `VideoDrilldownPort` boundaries; `preCall` now only handles request traversal, policy, and response aggregation. The Video tab's FFmpeg/ffprobe runtime status is now an explicit `unknown` / `restricted` / `unavailable` / `available` state instead of a nullable boolean pair, fixing a case where an in-flight or failed probe was mislabeled as "install FFmpeg" ([#11657](https://github.com/diegosouzapw/OmniRoute/issues/11657)). ([#12016](https://github.com/diegosouzapw/OmniRoute/pull/12016)) +- **test(opencode-plugin):** add unit test coverage for `formatFreeBudget()` naming helper ([#11660](https://github.com/diegosouzapw/OmniRoute/pull/11660)) — thanks @f9td56dbgh-hub +- **docs(video):** clarify that the Video Bridge transcript `source` field (`client`, `embedded`, `audio-bridge`) is presently caller-declared and not yet server-verified — OmniRoute enforces the enum shape but does not cryptographically confirm that an `embedded`/`audio-bridge` label came from a server-owned extraction ([#11661](https://github.com/diegosouzapw/OmniRoute/issues/11661)). ([#12001](https://github.com/diegosouzapw/OmniRoute/pull/12001)) +- **chore(providers):** retire the distributed Raycast Relay and Hailuo Web integrations after a provenance audit confirmed substantial derivation from GPL-3.0 sources; the official GitHub and MiniMax API providers remain available. Migration 163 disables legacy connections and invalidates their active leases while preserving connection IDs, audit/history rows, quota snapshots, usage records, and fail-closed API-key connection policies. ([#11691](https://github.com/diegosouzapw/OmniRoute/pull/11691)) +- **chore(providers):** retire the Felo Web provider while its GPL-derived provenance remains on licensing HOLD ([#11698](https://github.com/diegosouzapw/OmniRoute/pull/11698)) — stale runtime and database references now fail closed while existing connection, lease, and usage history remains preserved for auditability. +- **fix(sse):** retire the Gemini Web image-generation bridge while provenance and licensing remain on HOLD, preserving Gemini Web chat/auth/tools and legitimate Gemini/Nano Banana image providers ([#11708](https://github.com/diegosouzapw/OmniRoute/pull/11708)) +- **chore(audio):** retire the built-in EdgeTTS provider and its 11-voice catalog while provenance/licensing review remains on HOLD; the generic `/v1/audio/speech` endpoint and the other speech providers remain available ([#11711](https://github.com/diegosouzapw/OmniRoute/pull/11711)) +- **fix(providers):** retire the unofficial Qwen Web integration and its `qwen-web`/`qw` identities while third-party provenance remains on HOLD ([#11713](https://github.com/diegosouzapw/OmniRoute/pull/11713)) — direct requests now fail closed with a sanitized HTTP 410 and combos skip the retired target, while migration guards preserve connection IDs, allowlists, usage/audit history and invalidate active leases. Supported Qwen API, Qwen Code, cloud, token-plan, audio, local and model-name surfaces remain available; restoring Qwen Web requires a qualified provenance/license decision. +- **fix(providers):** retire `microsoft-designer-web` and its `msdesigner` alias from runtime and catalogs while preserving stored credentials, provider history and allowlists as inactive records — source dependency on gpt4free's [`MicrosoftDesigner.py`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/g4f/Provider/needs_auth/MicrosoftDesigner.py) is proven at snapshot `0943d16eef757a44deb60651c07bbe5fa958cafe`, whose [`LICENSE`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/LICENSE) is GPL-3.0; expressive adaptation is probable, which records provenance/licensing risk without asserting a legal conclusion ([#11720](https://github.com/diegosouzapw/OmniRoute/pull/11720)). +- **docs(legal):** centralize pinned MIT notices for the vendored GCF codec and local flag assets ([#11726](https://github.com/diegosouzapw/OmniRoute/pull/11726)) +- Split the 2,952-line `adobeFireflyClient.ts` into nine focused modules (catalog, credentials, payload, ARP session, upload, responses, IMS, account, poll) behind an unchanged barrel — no consumer edits and no public API change (#11728). — thanks @MumuTW +- **fix(dashboard):** replaced the Model Lockout card's unprovenanced notification MP3 with a short, locally synthesized Web Audio chime ([#11731](https://github.com/diegosouzapw/OmniRoute/pull/11731)) — both toggles retain optional audible feedback without shipping replacement media or third-party code, and unsupported, suspended, or rejected audio contexts fail open without blocking the setting change. +- **chore(deepseek-web):** remove the provenance-unresolved DeepSeek PoW WASM binary and its runtime loader/tracing while retaining the existing JavaScript solver (slower at high difficulty) ([#11732](https://github.com/diegosouzapw/OmniRoute/pull/11732)). +- **chore(assets):** remove four unused provider assets whose redistribution provenance could not be verified while retaining the active Freebuff SVG ([#11735](https://github.com/diegosouzapw/OmniRoute/pull/11735)) +- **docs(assets):** retired two raster images whose redistribution provenance could not be verified ([#11736](https://github.com/diegosouzapw/OmniRoute/pull/11736)) — PicoClaw documentation now reuses the existing SVG, while Zoo Code remains a text link without an image. +- **docs(legal):** add pinned notices for six LobeHub-derived provider SVGs and 65 byte-exact theSVG assets, retaining registry claims as unverified metadata with explicit trademark and HOLD boundaries ([#11737](https://github.com/diegosouzapw/OmniRoute/pull/11737)). +- **chore(quality):** added a versioned 225/225 provider-asset provenance manifest and blocking drift gate ([#11743](https://github.com/diegosouzapw/OmniRoute/pull/11743)) — the inventory pins magic MIME and SHA-256 for every physical file, records immutable source matches for 71 assets, keeps 69 probable and 85 unresolved entries explicit but non-blocking, and treats provenance as source evidence rather than copyright or trademark clearance. +- **refactor(mitm):** replace four previously derived Traffic Inspector utilities — conversation normalization, SSE merging, secret masking, and header sanitization — with independent clean-room implementations based on public provider schemas, WHATWG SSE, and RFC 6750, while keeping the two independently classified passthrough surfaces unchanged ([#11748](https://github.com/diegosouzapw/OmniRoute/pull/11748)). +- Added a unit test that fails seven days before any dated pack under `config/` (`validUntil` and sibling keys) lapses, naming the file and key. The Alibaba free-tier pack expired on 2026-08-27 and turned every PR red the next morning with no commit involved; renewal now happens on someone's terms, not the clock's. ([#11891](https://github.com/diegosouzapw/OmniRoute/pull/11891)) +- `check:workflows` now fails (under `--strict`/`--ratchet`) when any job routed to a self-hosted runner publishes with `--provenance` — npm rejects that with `422` at the registry, which in v3.8.50 only surfaced after the tag and Docker images were public. ([#11895](https://github.com/diegosouzapw/OmniRoute/pull/11895)) +- `scripts/ops/runner-janitor.sh` now proves a path is idle with one `lsof` snapshot and removes stale leftovers itself (tmpfs after 3 h — it is RAM — disk after 24 h), kills orphan `next-build` processes, prunes checkouts of stopped runners, and alerts on memory pressure; `--dry-run` shows exactly what it would do. `docs/ops/RUNNER_BOX.md` reconciled to the measured box (31 GB, 10 listeners). ([#11893](https://github.com/diegosouzapw/OmniRoute/pull/11893)) +- The `next-build` artefact (1.3 GB) is now written and read under `$RUNNER_TEMP` (per-runner, on disk) instead of `/tmp`, which on the self-hosted pool is a 12 GB tmpfs in RAM. Landing it there took 27–32 of the publish job's 76 minutes, and the fixed `/tmp/e2e-build.tar.gz` name let E2E jobs on different runners overwrite each other's download. ([#11896](https://github.com/diegosouzapw/OmniRoute/pull/11896)) +- The CI `build` job now runs in two concurrency lanes — `main` and pull requests — so a release build is never queued behind (or OOM-killed beside) PR builds on the self-hosted pool, which holds one `next-build` comfortably and two at the edge. ([#11901](https://github.com/diegosouzapw/OmniRoute/pull/11901)) +- Re-freeze the ESLint suppressions on `release/v3.8.51` from a clean-room measurement (2 stale file entries pruned, 55 pre-existing `no-explicit-any` in six new files frozen under #11924) and drop the dead `GPT_SIZE_MAP` constant orphaned by the Adobe Firefly client split, so `No new ESLint warnings` stops failing every PR with exit 2 (Refs #11924) ([#11955](https://github.com/diegosouzapw/OmniRoute/pull/11955)) +- Type the 55 `no-explicit-any` sites that had been frozen under #11924 — four redundant casts in `socksConnectorWithFamily.ts` (undici/socks types already accept them) and the mocks/fixtures of the socks-timeout and isFree suites — and drop their suppression entries; the ESLint ratchet shrinks from 5487 to 5432 (Closes #11924) ([#11975](https://github.com/diegosouzapw/OmniRoute/pull/11975)) +- Take the two hosted-runner builds off the PR rail: `Build App` (`build.yml`) is `workflow_dispatch`-only and `DAST smoke (PR)` runs only for PRs into `main` — the 7 GB hosted VM cannot build `release/v3.8.51` in any profile (19/30 red, VM shutdown ~8 min into `next build`) and both checks had turned into permanent noise on every release PR; the bundle stays validated by `ci.yml` on `main` and by `nightly-release-green` on `release/**` (Closes #11946) ([#11962](https://github.com/diegosouzapw/OmniRoute/pull/11962)) +- Move the four nightly jobs that build the backend (`nightly-schemathesis`, `nightly-llm-security` promptfoo + garak, `nightly-resilience` axe-a11y) off the hosted 7 GB runner — where they died on `release/v3.8.51` unseen — onto the box's new `omni-light` pool (two listeners), and document the reshaped fleet (4 active OmniRoute listeners: 2 `omni-build` + 2 `omni-light`, janitor ceiling 4) (Closes #11965) ([#11967](https://github.com/diegosouzapw/OmniRoute/pull/11967)) +- Stop painting every fork PR into `release/**` red: `quality.yml` `Build (advisory)` is skipped (GitHub still reports `continue-on-error` failures as check FAILURE). Hosted `ubuntu-latest` cannot finish `npm run build` on this tree — same class as #11962 taking `build.yml` off the PR rail. `docker-publish.yml` amd64 now runs on the `.113` `omni-build` pool (31 GB / 32 cores, two listeners) with Turbopack, shares the `heavy-build-main` lane with `ci.yml` `Build` so it queues instead of becoming a third heavy, and keeps per-ref concurrency (a merge storm was starting 8 concurrent OOM builds). arm64 stays on `ubuntu-24.04-arm` with webpack — there is no ARM box. Fallback when `USE_VPS_RUNNER` is off: hosted amd64 + webpack (#11976). ([#12021](https://github.com/diegosouzapw/OmniRoute/pull/12021)) +- `docker-publish.yml` builds with webpack (`OMNIROUTE_USE_TURBOPACK=0`) on **both** arches, including amd64 on the `.113` `omni-build` pool. Turbopack had the RAM (31 GB, `omniroute-113-6`) and still panicked (`TurbopackInternalError: there must be a path to a root`, run 33253576569, 26 min in). The same tree's arm64 webpack build on hosted `ubuntu-24.04-arm` succeeded (run 33264823398). Dockerfile already documents webpack as the Docker escape hatch (#11976). ([#12050](https://github.com/diegosouzapw/OmniRoute/pull/12050)) +- **docs(free-tier):** declare the counting vs deciding regimes for "is it free?" and guard the deciding path from DB-backed catalog resolution ([#12226](https://github.com/diegosouzapw/OmniRoute/pull/12226)) — thanks @maxmad64bis +- **docs(auto-combo):** The mode pack table in `docs/routing/AUTO-COMBO.md` now lists all six shipped packs with every weight each one sets, replacing a four-pack table whose numbers had also drifted from the source. It states plainly that no pack sets `quality`, so selecting any pack silences the observed-quality signal. Six more documents that quote the scoring factor count joined the `check:docs-counts` gate, which caught five stale claims — including one naming nine factors that do not exist — and two stale mode pack counts. The dashboard routing panel, which offered four of the six packs and labelled the default strategy "6-Factor Scoring", is now covered by a test; the two packs it was missing are `reliability-first` and `chaos-mode`, the latter labelled as the fault-injection profile it is rather than as one more routing preference ([#12316](https://github.com/diegosouzapw/OmniRoute/pull/12316)) — thanks @maxmad64bis +- **chore(quality):** rebaseline `src/lib/db/apiKeys.ts` for the ACL the key-creation path now preserves ([#12352](https://github.com/diegosouzapw/OmniRoute/pull/12352)) ([#12673](https://github.com/diegosouzapw/OmniRoute/pull/12673)) — thanks @KrzysiekSko +- **chore(providers):** bump the Claude Code wire identity and the Devin bridge image pin from `2.1.220` to `2.1.258` ([#12402](https://github.com/diegosouzapw/OmniRoute/pull/12402)) — thanks @ggiak ([#12604](https://github.com/diegosouzapw/OmniRoute/pull/12604)) +- **docs(env):** align `.env.example`, the README Bun section, and the troubleshooting guide with the code: `OMNIROUTE_USE_TURBOPACK` also governs `npm run build` (not dev-only), `bun run build` follows that flag instead of auto-selecting Webpack, `OMNIROUTE_CHAT_MAX_HEAVY_IN_FLIGHT` is unset by default (no request-count cap), and the structural `503 chat_admission_busy` message matches `chatAdmissionResponses.ts` (#12404 — thanks @pacocartones) +- **chore(electron):** upgrade the desktop app to Electron 44 (Chromium 152, Node 24.18.1) ([#12217](https://github.com/diegosouzapw/OmniRoute/pull/12217)). **Requires macOS 13 (Ventura) or later** — Chromium dropped macOS 12 (Monterey), so Monterey users must stay on an earlier OmniRoute desktop build. Windows and Linux are unaffected; the app already shipped only x64/arm64, so Electron 44 dropping 32-bit builds changes nothing. Removes the `openAsHidden`/`wasOpenedAsHidden` login-item fields deleted in Electron 44 — hidden autostart continues to work through the `--hidden` argument registered with the login item ([#12554](https://github.com/diegosouzapw/OmniRoute/pull/12554)) — thanks @app/dependabot +- **chore(quality):** rebaseline `src/sse/handlers/chat.ts` for the effective-input persistence the continuation fix needs ([#12641](https://github.com/diegosouzapw/OmniRoute/pull/12641)) ([#12680](https://github.com/diegosouzapw/OmniRoute/pull/12680)) — thanks @hartmark +- `Coverage` job on `ci.yml`: the informational Codecov upload gets its own 5-minute ceiling and `continue-on-error`, and the job budget grows from 20 to 30 minutes (the 8-shard c8 merge alone takes ~10) — a stalled upload no longer ends the job `cancelled` and drags a fully green `main` run's conclusion down with it ([#11972](https://github.com/diegosouzapw/OmniRoute/pull/11972)) +- CI hardening on the PR rail: the four `Unit Tests fast-path` shards get `timeout-minutes: 30` (a hung shard held a PR for 64 min instead of GitHub's 6 h default) and both ESLint file caches lose their `restore-keys` fallback, so a cache built under another suppressions file or lint config can no longer report stale verdicts (Refs #11600, #11924) ([#11963](https://github.com/diegosouzapw/OmniRoute/pull/11963)) +- Every CI job that runs a `next build` (`build`, the npm `publish`, both release-green validations) now targets the `omni-build` runner label, which only two of the eight self-hosted runners carry. The box holds one build comfortably and two at the edge; a third now queues on GitHub instead of being OOM-killed by the kernel. ([#11932](https://github.com/diegosouzapw/OmniRoute/pull/11932)) +- **refactor(combo):** move `handleRoundRobinCombo` (and `resolveTargetTokenLimit`) into `open-sse/services/combo/roundRobinCombo.ts`. `combo.ts` drops from 2164 to 1014 lines (`split("\n").length`); the leaf is 1199 (under the 1200 new-file cap). The round-robin call site uses a dynamic `import()` so `releaseStickyPinOnFailure` / `clearStaleLKGP` can stay exported from `combo.ts` without a static cycle. Skip / sticky / semaphore / safety-timer behavior is unchanged. ([#12811](https://github.com/diegosouzapw/OmniRoute/pull/12811)) — thanks @HouMinXi +- **chore(quality):** rebaseline the file-size caps the error-boundary campaign grew past (`open-sse/executors/codex.ts`, `open-sse/vendor/codex-chatgpt-web/bridge.ts`, both via [#12444](https://github.com/diegosouzapw/OmniRoute/pull/12444)) ([#12654](https://github.com/diegosouzapw/OmniRoute/pull/12654)) +- **chore(quality):** rebaseline the file-size caps the hartmark batch grew past (`combos/page.tsx` via [#12355](https://github.com/diegosouzapw/OmniRoute/pull/12355), `open-sse/services/combo.ts` via [#12338](https://github.com/diegosouzapw/OmniRoute/pull/12338)) ([#12623](https://github.com/diegosouzapw/OmniRoute/pull/12623)) — thanks @hartmark +- **chore(quality):** rebaseline the file-size caps the HouMinXi batch grew past when its PRs stacked (`providers/page.tsx`, `chatCore.ts`, `accountFallback.ts`) — each PR measured correctly in isolation, none saw the stacking ([#12619](https://github.com/diegosouzapw/OmniRoute/pull/12619)) +- **chore(quality):** rebaseline `open-sse/services/combo.ts` for the reset-aware scoring the HouMinXi batch stacked ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) ([#12678](https://github.com/diegosouzapw/OmniRoute/pull/12678)) — thanks @HouMinXi +- **docs:** sync provider catalog count (direct commit `4f11b2ae3d`) +- **docs(skills):** sync omni-inference search fallback (direct commit `b4e76a7ed9`) +- **chore(lint):** prune two stale base suppressions (direct commit `1325047d56`) +- **test(auto-combo):** tolerate full-suite rotation load (direct commit `ad3e293f9f`) +- **test:** drain inherited quality snapshot reds (direct commit `21ed68d8ac`) +- **chore(quality):** rebaseline measured CLI bundle growth (direct commit `d35b3f9779`) +- **test(ui):** reproduce invalid AnySearch icon alias (direct commit `12cc6ca834`) +- **test(translator):** fix the relative imports of the relocated deferred-finish test (direct commit `777d9d1629`) +- **docs(counts):** bring the documented provider and migration counts to the measured values (direct commit `e70bea30e9`) +- **test(security):** drop the guard-target set left unused by the sync (direct commit `a32ba0e362`) +- **chore(db):** renumber model_capabilities migration 163 → 169 (cross-branch collision with 163_radar_feed_cache_generated_at) (direct commit `70f5d4cbf9`) +- **docs(cli):** document CLI_PRIME_AGENT_BIN and correct the CLI Agents count (direct commit `9058e39b61`) +- **chore(deps):** refresh runtimes and adopt ESLint 10 ([#11259](https://github.com/diegosouzapw/OmniRoute/pull/11259)) — thanks @backryun +- **refactor(local-corpus):** implement dynamic root resolution and LRU cache ([#11491](https://github.com/diegosouzapw/OmniRoute/pull/11491)) — thanks @raheemuddin786 +- **chore(quality):** prune stale eslint-suppressions entries ([#11596](https://github.com/diegosouzapw/OmniRoute/pull/11596)) +- **chore(ci):** unblock quality gates - env-doc sync + stryker tap.testFiles registration (#9282) ([#11609](https://github.com/diegosouzapw/OmniRoute/pull/11609)) — thanks @Meet6338-X +- **chore(ci):** point the circular-deps gate at dpdm's real JS entrypoint ([#11615](https://github.com/diegosouzapw/OmniRoute/pull/11615)) — thanks @benzntech +- **chore(quality):** rebaseline file-size for #11448/#11495/#11561 growth ([#11630](https://github.com/diegosouzapw/OmniRoute/pull/11630)) +- **deps:** realign onnxruntime-node pin to @huggingface/transformers' 1.24.3 lockstep ([#11633](https://github.com/diegosouzapw/OmniRoute/pull/11633)) — thanks @jonlwheat2-gif +- **test(autoCombo):** port models_dev_tier #11508 guard from node:test to vitest ([#11635](https://github.com/diegosouzapw/OmniRoute/pull/11635)) — thanks @jonlwheat2-gif +- **chore(quality):** rebaseline file-size for #11083/#11631/#11628 growth ([#11636](https://github.com/diegosouzapw/OmniRoute/pull/11636)) +- **chore(lint):** prune stale ESLint suppressions (base-red #11449) ([#11644](https://github.com/diegosouzapw/OmniRoute/pull/11644)) — thanks @jonlwheat2-gif +- **docs(changelog):** aggregate the ten v3.8.50 fragments into CHANGELOG.md ([#11683](https://github.com/diegosouzapw/OmniRoute/pull/11683)) +- **chore(search):** drop unreachable credential sweep after #11642 ([#11687](https://github.com/diegosouzapw/OmniRoute/pull/11687)) — thanks @HouMinXi +- **ci:** add API route TypeScript regression gate ([#11705](https://github.com/diegosouzapw/OmniRoute/pull/11705)) — thanks @Sabeekhann +- **docs(dedup):** correct the CodeQL claim on the tenant-namespace comment ([#11712](https://github.com/diegosouzapw/OmniRoute/pull/11712)) +- **docs(changelog):** add consolidated v3.8.50 stats, top-25 ranking and a .mailmap ([#11715](https://github.com/diegosouzapw/OmniRoute/pull/11715)) +- **docs:** recommend gstack for AI-assisted workflows ([#11770](https://github.com/diegosouzapw/OmniRoute/pull/11770)) — thanks @santosraju99-hub +- **chore(quality):** tighten complexity/cognitive-complexity ratchets to the current tip; land the missed gateways.ts rebaseline ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **docs(i18n):** finish Freepik → Magnific rebrand in locale strings and README ([#11772](https://github.com/diegosouzapw/OmniRoute/pull/11772)) — thanks @turbolego +- **refactor(auto-combo):** fix divergent scoring in combo health reporting ([#11854](https://github.com/diegosouzapw/OmniRoute/pull/11854)) — thanks @jacobsparts +- **chore(changelog):** add missing fragments for #11919/#11918/#11916 ([#11938](https://github.com/diegosouzapw/OmniRoute/pull/11938)) +- **test(check):** escape the runs-on fixture with JSON.stringify, not a quote-only replace ([#11942](https://github.com/diegosouzapw/OmniRoute/pull/11942)) (direct commit `fb7445eaa3`) +- **test(release):** align five suites with the contracts #11933, #11919 and #11876 shipped on release/v3.8.51 ([#11944](https://github.com/diegosouzapw/OmniRoute/pull/11944)) +- **chore(quality):** remove unreachable code and restore test discovery ([#11950](https://github.com/diegosouzapw/OmniRoute/pull/11950)) — thanks @backryun +- **docs(agents):** sync-back landings are fast-forward, never squash ([#11964](https://github.com/diegosouzapw/OmniRoute/pull/11964)) +- **test(infra):** retry recursive temp-dir removal instead of failing a shard on ENOTEMPTY (#11966) ([#11968](https://github.com/diegosouzapw/OmniRoute/pull/11968)) +- **docs(ops):** the .113 heavy-build ceiling is one runner, not two ([#12048](https://github.com/diegosouzapw/OmniRoute/pull/12048)) +- **test(ui):** correct inactive auto-fetch expectation ([#12098](https://github.com/diegosouzapw/OmniRoute/pull/12098)) — thanks @RaviTharuma +- **docs:** document native dependency check escape hatch ([#12101](https://github.com/diegosouzapw/OmniRoute/pull/12101)) — thanks @RaviTharuma +- **test(providers):** regenerate the translate-path golden for OrcaRouter (#11923) ([#12118](https://github.com/diegosouzapw/OmniRoute/pull/12118)) +- **chore(quality):** velocity phase — loosen every numeric baseline by 20% until v4.0, monitor headroom nightly ([#12125](https://github.com/diegosouzapw/OmniRoute/pull/12125)) +- **chore(lint):** batch 0 of #12146 — type the call-log-cap sqlite rows instead of 45 as-any casts ([#12157](https://github.com/diegosouzapw/OmniRoute/pull/12157)) +- **chore(lint):** batch 4 of #12146 — shared/components react-hooks violations resolved ([#12159](https://github.com/diegosouzapw/OmniRoute/pull/12159)) +- **chore(lint):** batch 1 of #12146 — dashboard/cli-code react-hooks violations resolved ([#12160](https://github.com/diegosouzapw/OmniRoute/pull/12160)) +- **chore(lint):** batch 3 of #12146 — dashboard/settings react-hooks violations resolved ([#12162](https://github.com/diegosouzapw/OmniRoute/pull/12162)) +- **chore(lint):** batch 2 of #12146 — resolve the react-hooks compiler violations in dashboard/providers ([#12163](https://github.com/diegosouzapw/OmniRoute/pull/12163)) +- **chore(quality):** register search-432 cooldown test in stryker tap.testFiles ([#12170](https://github.com/diegosouzapw/OmniRoute/pull/12170)) +- **chore(lint):** batch 5 of #12146 — combos, endpoint, provider-stats, api-manager and costs react-hooks violations resolved ([#12174](https://github.com/diegosouzapw/OmniRoute/pull/12174)) +- **build:** add contributor fast profile ([#12192](https://github.com/diegosouzapw/OmniRoute/pull/12192)) — thanks @rafacpti23 +- **docs(readme):** README touch-ups ([#12193](https://github.com/diegosouzapw/OmniRoute/pull/12193)) ([#12194](https://github.com/diegosouzapw/OmniRoute/pull/12194)) ([#12202](https://github.com/diegosouzapw/OmniRoute/pull/12202)) — thanks @rafacpti23 +- **build:** force the webpack bundler in the contributor build profile ([#12197](https://github.com/diegosouzapw/OmniRoute/pull/12197)) — thanks @rafacpti23 +- **build:** skip standalone packaging in the contributor profile ([#12198](https://github.com/diegosouzapw/OmniRoute/pull/12198)) — thanks @rafacpti23 +- **docs(audit):** align every published number with the code and harden check:docs-counts ([#12200](https://github.com/diegosouzapw/OmniRoute/pull/12200)) +- **build:** stub the instrumentation entrypoints in the contributor profile ([#12203](https://github.com/diegosouzapw/OmniRoute/pull/12203)) — thanks @rafacpti23 +- **build:** omit the standalone output target for contributor builds ([#12204](https://github.com/diegosouzapw/OmniRoute/pull/12204)) — thanks @rafacpti23 +- **chore(lint):** batch 6 of #12146 — memory, radar, audit, analytics, cache, usage, activity, home and RequestLoggerV2 react-hooks violations resolved ([#12208](https://github.com/diegosouzapw/OmniRoute/pull/12208)) +- **docs(diagrams):** rename number-carrying diagram files to stable names ([#12210](https://github.com/diegosouzapw/OmniRoute/pull/12210)) +- **docs(dashboard):** redraw onboarding tier-flow SVGs for the real 4-tier model ([#12211](https://github.com/diegosouzapw/OmniRoute/pull/12211)) +- **docs(api):** document every implemented route in openapi.yaml (276 → 692 paths) ([#12212](https://github.com/diegosouzapw/OmniRoute/pull/12212)) +- **chore(lint):** batch 7 of #12146 — final src tail: 37 react-hooks violations across 33 files resolved ([#12244](https://github.com/diegosouzapw/OmniRoute/pull/12244)) +- Revert "docs: recommend gstack for AI-assisted workflows (#11770)" ([#12248](https://github.com/diegosouzapw/OmniRoute/pull/12248)) ([#12249](https://github.com/diegosouzapw/OmniRoute/pull/12249)) +- **docs:** Chaos Mode setup guide + weighted strategy semantics ([#12250](https://github.com/diegosouzapw/OmniRoute/pull/12250)) +- **docs(agents):** protected-surface merge rule — operator approval for agent-instruction files ([#12253](https://github.com/diegosouzapw/OmniRoute/pull/12253)) +- **chore(quality):** register native-codex turn-pin tests in stryker tap.testFiles ([#12263](https://github.com/diegosouzapw/OmniRoute/pull/12263)) +- **chore(quality):** dedupe tap.testFiles entries added by racing base-red fixes ([#12265](https://github.com/diegosouzapw/OmniRoute/pull/12265)) +- **docs(readme):** introduce OmniRouteTray — the macOS menu-bar companion ([#12276](https://github.com/diegosouzapw/OmniRoute/pull/12276)) — thanks @ggiak +- **chore(proxy):** purge the legacy 1proxy residue — sync/rotator modules, dead DB exports, dead settings tab and flag, docs (#12091) ([#12290](https://github.com/diegosouzapw/OmniRoute/pull/12290)) +- **chore(quality):** baseline-headroom skips generated and vendored files in the fileSize worst-file signal ([#12291](https://github.com/diegosouzapw/OmniRoute/pull/12291)) +- **refactor(video):** unify the JPEG frame data-URI contract ([#12322](https://github.com/diegosouzapw/OmniRoute/pull/12322)) +- **chore(deps):** freeze onnxruntime-node and eslint-plugin-react-hooks out of dependabot groups ([#12329](https://github.com/diegosouzapw/OmniRoute/pull/12329)) +- **test(executors):** restore the #10986 reasoning-only fallback guards ([#12364](https://github.com/diegosouzapw/OmniRoute/pull/12364)) — thanks @pacocartones +- **chore(quality):** re-tighten the file-size ratchet to the real LOC (plan 3.8.52 task 0) ([#12411](https://github.com/diegosouzapw/OmniRoute/pull/12411)) +- **test(grok):** format web executor suite ([#12412](https://github.com/diegosouzapw/OmniRoute/pull/12412)) +- **chore(quality):** ratchet open-sse typecheck baseline to zero ([#12418](https://github.com/diegosouzapw/OmniRoute/pull/12418)) +- **test(build):** guard the artifact path policy arrays against duplicates ([#12422](https://github.com/diegosouzapw/OmniRoute/pull/12422)) +- **chore(lint):** adopt eslint-plugin-react-hooks 7.1.1 ([#12428](https://github.com/diegosouzapw/OmniRoute/pull/12428)) +- **chore(providers):** remove a keyless provider integration at its operator's request ([#12440](https://github.com/diegosouzapw/OmniRoute/pull/12440)) +- **docs(providers):** register providers removed at their operator's request and guard against reintroduction ([#12478](https://github.com/diegosouzapw/OmniRoute/pull/12478)) +- **chore(quality):** base fixes — stryker tap.testFiles + node_modules cache key ([#12482](https://github.com/diegosouzapw/OmniRoute/pull/12482)) +- **docs(arch):** one-process recipe for tens of long /v1/responses ([#12493](https://github.com/diegosouzapw/OmniRoute/pull/12493)) — thanks @RaviTharuma +- **chore(quality):** tighten the CodeQL ratchet baseline from 11 to 6 ([#12530](https://github.com/diegosouzapw/OmniRoute/pull/12530)) +- **chore(deps):** pin browserslist override to ^4.28.8 ([#12592](https://github.com/diegosouzapw/OmniRoute/pull/12592)) — thanks @KooshaPari +- **chore(deps):** bump fast-uri to 3.1.7 in the electron lockfile ([#12601](https://github.com/diegosouzapw/OmniRoute/pull/12601)) +- **docs(quality):** document how the CodeQL ratchet refreshes and how to tighten it ([#12611](https://github.com/diegosouzapw/OmniRoute/pull/12611)) +- **test(ci):** pin the openapi-security-tiers two-arm contract with an executing gate test (#12581) ([#12652](https://github.com/diegosouzapw/OmniRoute/pull/12652)) +- **docs(gamification):** align XP Rewards table with code (#12501) ([#12667](https://github.com/diegosouzapw/OmniRoute/pull/12667)) — thanks @KooshaPari +- **refactor(combo):** split executeTarget into gates, attempt, and loop ([#12746](https://github.com/diegosouzapw/OmniRoute/pull/12746)) — thanks @HouMinXi +- **docs(security):** document socket.yml scanner config + CI workflow link (#12575) ([#12764](https://github.com/diegosouzapw/OmniRoute/pull/12764)) — thanks @KooshaPari +- **chore(ci):** guard commit identity in pre-commit to stop author misattribution ([#12772](https://github.com/diegosouzapw/OmniRoute/pull/12772)) +- **deps:** 17 Dependabot bumps — bump electron from 43.4.0 to 43.4.1 in /electron ([#11426](https://github.com/diegosouzapw/OmniRoute/pull/11426)); bump the development group across 1 directory with 8 updates ([#11428](https://github.com/diegosouzapw/OmniRoute/pull/11428)); bump the production group across 1 directory with 11 updates ([#11440](https://github.com/diegosouzapw/OmniRoute/pull/11440)); chore(deps): bump github/codeql-action/init from 4.37.7 to 4.37.8 ([#11925](https://github.com/diegosouzapw/OmniRoute/pull/11925)); chore(deps): bump github/codeql-action from 4.37.7 to 4.37.8 ([#11926](https://github.com/diegosouzapw/OmniRoute/pull/11926)); chore(deps): bump github/codeql-action/analyze from 4.37.7 to 4.37.8 ([#11927](https://github.com/diegosouzapw/OmniRoute/pull/11927)); build(deps): bump github/codeql-action/init from 4.37.8 to 4.37.9 ([#12345](https://github.com/diegosouzapw/OmniRoute/pull/12345)); build(deps): bump github/codeql-action/analyze from 4.37.8 to 4.37.9 ([#12346](https://github.com/diegosouzapw/OmniRoute/pull/12346)); bump the development group across 1 directory with 2 updates ([#12347](https://github.com/diegosouzapw/OmniRoute/pull/12347)); build(deps): bump github/codeql-action from 4.37.8 to 4.37.9 ([#12349](https://github.com/diegosouzapw/OmniRoute/pull/12349)); bump browserslist from 4.28.2 to 4.28.8 ([#12396](https://github.com/diegosouzapw/OmniRoute/pull/12396)); bump the production group across 1 directory with 4 updates ([#12399](https://github.com/diegosouzapw/OmniRoute/pull/12399)); bump @xmldom/xmldom ([#12500](https://github.com/diegosouzapw/OmniRoute/pull/12500)); bump qs from 6.15.2 to 6.16.0 ([#12512](https://github.com/diegosouzapw/OmniRoute/pull/12512)); bump @xmldom/xmldom from 0.9.10 to 0.9.12 ([#12513](https://github.com/diegosouzapw/OmniRoute/pull/12513)); bump fast-uri from 3.1.5 to 3.1.7 ([#12514](https://github.com/diegosouzapw/OmniRoute/pull/12514)); bump @humanfs/node from 0.16.7 to 0.16.8 ([#12515](https://github.com/diegosouzapw/OmniRoute/pull/12515)) +- Clear the `release/v3.8.51` `check:agent-skills-sync` base-red: regenerate `skills/cli-tunnel/SKILL.md` so the `tunnel create [type]` positional that #13009 taught the generator to read is reflected in the committed skill. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` typecheck base-red from #12731: the new `Mode pack ranking gates` candidates in `open-sse/services/autoCombo/__tests__/autoCombo.test.ts` omitted the required `provider`, `model` and `errorRate` fields of `ProviderCandidate`, failing `check:open-sse-typecheck` (and with it `Fast Quality Gates`) on every open PR. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` docs-sync base-red: EURouter (#13025) and GreenPT (#13024) took the live provider count to 358, leaving 7 STRICT drifts (`PROVIDER_REFERENCE.md`, 4 diagrams, 2 tier-flow images) plus the `AGENTS.md` / `llm.txt` / `package.json` count claims stale. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- **test(models):** Pin `CATALOG_BUILD_TIMEOUT_MS` in the #12058 canonical-catalog regression so a cold catalog build racing the 8s default bound no longer flakes the suite with a 500 ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Allow `@omniroute/opencode-plugin-v2/` in the npm pack policy — [#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870) shipped the v2 plugin beside its v1 sibling without widening the allowlist, so every packed file under it read as an unexpected artifact and failed `check:pack-policy` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Point the release-green validator's pack gate at the tree it is validating — the build-provenance guard defaulted to `origin/main`, which a release branch tip can never be an ancestor of mid-cycle, so `Package artifact` reported a false red on every run and the tarball boot-smoke cascaded off it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **docs(checks):** keep doc counts honest — headings, rankings, catalog, weights, quality gate and scoring diagram now covered ([#12507](https://github.com/diegosouzapw/OmniRoute/pull/12507)) — thanks @maxmad64bis +- **chore(lifecycle):** `check:model-lifecycle` now also diffs `DEFAULT_DEGRADATION_MAP` (the background-task redirect table) against the vendor lifecycle snapshot, refusing a retired id as source or target, with a table-driven unit test beside it. Three rows whose source the vendor had retired — `claude-sonnet-4-20250514`, `gemini-3-pro-preview` and `gpt-5.1-codex` (whose target `gpt-5.1-codex-mini` is retired too) — were dead code, since `checkLifecycle` answers 410 before the redirect runs; they are dropped (#12535 — thanks @pacocartones) +- **test(compression):** cover idle worker eviction at the resource level — the pool must call `terminate()` and must not retain the worker's `MessagePort`, complementing the `exit`-event assertion added with the fix ([#13371](https://github.com/diegosouzapw/OmniRoute/pull/13371)) +- **chore(quality):** raise the `imageGeneration.ts` file-size ceiling for the image-only-model guard that clears the #12945 base-red ([#13392](https://github.com/diegosouzapw/OmniRoute/pull/13392)) +- **chore(quality):** type the combo-test route's JSON response bodies instead of casting them to `any`, drop the now-empty suppression entry, and remove the `hasPerModelQuota` import `chatCore.ts` stopped using when the failure-classification helper was extracted ([#13379](https://github.com/diegosouzapw/OmniRoute/pull/13379)) +- **chore(electron):** the desktop bundle no longer ships the root-level files of every translated docs mirror (`README.md`, `llm.txt`, `CLAUDE.md`, `GEMINI.md`, `CONTRIBUTING.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`) — the packaged app only reads `docs/i18n//docs/**`, which stays. Saves ~11 MB on top of the translated CHANGELOGs already pruned. (#0000) ([#13235](https://github.com/diegosouzapw/OmniRoute/pull/13235)) +- **chore(quality):** retighten the `src/sse/handlers/chat.ts` unused-vars suppression count to the 8 violations that actually remain ([#13385](https://github.com/diegosouzapw/OmniRoute/pull/13385)) +- Bring the documented counts back in line with the code: 171 migrations (was 169) and 20 routing strategies (was 19), plus the regenerated `cli-tunnel` skill reference. ([#12970](https://github.com/diegosouzapw/OmniRoute/pull/12970)) +- Update the documented migration count to 172 after the call-logs provider-stats indexes landed. ([#13209](https://github.com/diegosouzapw/OmniRoute/pull/13209)) +- **chore(ci):** two gates that close the blind spots behind the exclusions above. `check:vitest-exclusions` requires every Vitest exclusion to name a tracking issue and to appear in `config/quality/vitest-exclusions.json` — the previous list grew to 62 files behind a comment pointing at an issue that had been closed for a month. `check-new-key-coverage` requires a key newly added to `en.json` to reach every locale; the existing coverage gate is a percentage floor per locale, so eleven absent keys out of ~13,000 left it at 99.9% while a whole feature shipped untranslated in nine languages. Both are diff-aware, so pre-existing debt stays frozen and neither needed a migration to turn on. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **build:** root layout no longer loads Inter from `next/font/google`, so a production image build does not need fonts.googleapis.com ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **docs(i18n):** bring the eight new `llm.txt` mirrors (km, kn, ml, my, ne, or, pa, si) up to the current provider and migration counts, so `check-docs-sync` stops rejecting every commit ([#13674](https://github.com/diegosouzapw/OmniRoute/pull/13674)) +- **refactor(ui):** flow surfaces (home topology, combo live studio, compression cockpit/waterfall/nodes, token-health badge) express state through the theme-aware `--orch-status-*` tokens instead of fixed dark-mode hex, so green/red/amber/grey stay legible in the light theme; dark mode is byte-identical. Categorical palettes (routing-strategy hues, compression-layer pills, provider brand colors) deliberately stay hex ([#12378](https://github.com/diegosouzapw/OmniRoute/issues/12378)) ([#13203](https://github.com/diegosouzapw/OmniRoute/pull/13203)) +- **chore(tests):** 51 test files that had been excluded from Vitest are running again, restoring roughly 350 assertions to the blocking suite. Each was measured individually first: of the 62 files parked behind the `// #8618 — pre-existing failure` comment, 51 pass against the current tree with no source change, so the exclusions had outlived the failures they were added for. The 11 that genuinely still fail stay excluded, but now point at a live tracker (#13204) rather than at #8618, which was closed in August while the list it tracked kept growing. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **chore(quality):** drop two ESLint suppression entries whose violations no longer exist, so `eslint --suppressions-location` stops rejecting every commit that touches the surrounding files ([#13384](https://github.com/diegosouzapw/OmniRoute/pull/13384)) +- **chore(release):** bring main's post-tag fixes into release/v3.8.51 — sync-back step 2/2 (direct commit `21c488f210`) +- **chore(deps):** patch toml and esbuild in the opencode plugin lockfiles ([#12965](https://github.com/diegosouzapw/OmniRoute/pull/12965)) +- **docs(changelog):** reconcile the v3.8.51 living section — fold 366 fragments, cover every cycle commit, credit every contributor ([#12971](https://github.com/diegosouzapw/OmniRoute/pull/12971)) +- **test(plugin):** wait for the v2 lazy-refresh tier instead of sleeping past it ([#12984](https://github.com/diegosouzapw/OmniRoute/pull/12984)) +- **chore(quality):** rebaseline chatCore.ts after the non-streaming regression fixes ([#13045](https://github.com/diegosouzapw/OmniRoute/pull/13045)) +- **docs(changelog):** add the missing bullet marker to the DeepSeek PoW fragment ([#13200](https://github.com/diegosouzapw/OmniRoute/pull/13200)) +- **chore(deps):** drain the Dependabot queue — 10 of 13 alerts ([#13213](https://github.com/diegosouzapw/OmniRoute/pull/13213)) +- **chore(ci):** make worktrees carry the hooks that enforce the identity gate ([#13236](https://github.com/diegosouzapw/OmniRoute/pull/13236)) +- **chore(deps):** pin csv-parse to ^7.0.2 via overrides (#13117) ([#13300](https://github.com/diegosouzapw/OmniRoute/pull/13300)) — thanks @KooshaPari +- **chore(deps):** bump hono from ^4.12.34 to ^4.13.7 (#13148) ([#13301](https://github.com/diegosouzapw/OmniRoute/pull/13301)) — thanks @KooshaPari +- **chore(deps):** pin joi to ^18.2.8 via overrides (#13085) ([#13302](https://github.com/diegosouzapw/OmniRoute/pull/13302)) — thanks @KooshaPari +- **docs(providers):** add Microsoft 365 Copilot (BizChat) provider guide (#12779) ([#13340](https://github.com/diegosouzapw/OmniRoute/pull/13340)) — thanks @KooshaPari +### 🙌 Contributors + +Thanks to everyone whose work landed in v3.8.51: + +| Contributor | PRs / Issues | +| --- | --- | +| [@5dive-bot](https://github.com/5dive-bot) | #11852 | +| [@abhisheksharma2411](https://github.com/abhisheksharma2411) | #11547, #11684, #11959, #12209 | +| [@adevwithpurpose](https://github.com/adevwithpurpose) | #11464 | +| [@adivekar-utexas](https://github.com/adivekar-utexas) | #12015, #12043, #12090, #12138 | +| [@AIB1TAL0S](https://github.com/AIB1TAL0S) | #12103 | +| [@alltomatos](https://github.com/alltomatos) | #11448 | +| [@alvinveroy](https://github.com/alvinveroy) | #11857, #12027, #12165 | +| [@amaleta](https://github.com/amaleta) | #11906, #11934 | +| [@amartinawi](https://github.com/amartinawi) | #11575 | +| [@AndrianBalanescu](https://github.com/AndrianBalanescu) | #11083, #11830, #11834, #11844 | +| [@anhtahaylove](https://github.com/anhtahaylove) | #13091, #13092, #13093, #13096, #13097, #13100, #13106, #13109, #13114, #13155, #13166, #13171, #13175, #13187, #13196 | +| [@aniruddhaadak80](https://github.com/aniruddhaadak80) | #11760 | +| [@app](https://github.com/app) | #12554 | +| [@arminanton](https://github.com/arminanton) | #11461, #11513 | +| [@AStupidBear](https://github.com/AStupidBear) | #11584 | +| [@atescivitci-cmd](https://github.com/atescivitci-cmd) | #12105 | +| [@b3nw](https://github.com/b3nw) | #11970, #11971, #12147 | +| [@backryun](https://github.com/backryun) | #11259, #11950, #12075, #12076, #12078, #12079, #12081, #12082, #12181, #12228, #12239, #12255, #12258, #12277, #12367, #12423, #12524, #12538 | +| [@benjaminkitt](https://github.com/benjaminkitt) | #11747 | +| [@benzntech](https://github.com/benzntech) | #11614, #11615 | +| [@Bl0ck154](https://github.com/Bl0ck154) | #11948, #11951, #11952, #11953, #11954 | +| [@brick30llc-ctrl](https://github.com/brick30llc-ctrl) | #11752, #12242, #12262 | +| [@Chewji9875](https://github.com/Chewji9875) | #11455, #11875, #12028 | +| [@cheynetom](https://github.com/cheynetom) | #12058 | +| [@davidebaraldo](https://github.com/davidebaraldo) | #12222, #12304 | +| [@davidlinfr](https://github.com/davidlinfr) | #6390 | +| [@ddarkr](https://github.com/ddarkr) | #12124 | +| [@Deftera186](https://github.com/Deftera186) | #11809 | +| [@dpozimski](https://github.com/dpozimski) | #11945 | +| [@DW-MediaLab](https://github.com/DW-MediaLab) | #12229 | +| [@echel0nn](https://github.com/echel0nn) | #11923 | +| [@f9td56dbgh-hub](https://github.com/f9td56dbgh-hub) | #11660 | +| [@fabioluissilva](https://github.com/fabioluissilva) | #11991 | +| [@foreveryh](https://github.com/foreveryh) | #12177 | +| [@geek007git](https://github.com/geek007git) | #12115, #12116, #12117, #12120, #12122 | +| [@geekyNads](https://github.com/geekyNads) | #11784, #12266 | +| [@ggiak](https://github.com/ggiak) | #11561, #12276, #12350, #12402 | +| [@gonisulaimann](https://github.com/gonisulaimann) | #12368, #12369, #12371 | +| [@Gorillaz322](https://github.com/Gorillaz322) | #12207 | +| [@groovecityJO](https://github.com/groovecityJO) | #12682 | +| [@hartmark](https://github.com/hartmark) | #11434, #11452, #11473, #11499, #11703, #11983, #11984, #11985, #11986, #11988, #11989, #11990, #11994, #12221, #12293, #12445, #12446, #12447, #12448, #12460, #12461, #12623, #12646, #12650, #12680, #12717, #12718, #12727, #12741, #12854 | +| [@hizzt](https://github.com/hizzt) | #11894 | +| [@hongnoul](https://github.com/hongnoul) | #11484 | +| [@HouMinXi](https://github.com/HouMinXi) | #11411, #11414, #11512, #11518, #11520, #11641, #11642, #11643, #11687, #11779, #11849, #11850, #11851, #11915, #11916, #11918, #11919, #11920, #12013, #12017, #12033, #12042, #12106, #12139, #12166, #12169, #12171, #12205, #12213, #12312, #12325, #12487, #12488, #12495, #12504, #12557, #12566, #12590, #12591, #12624, #12626, #12632, #12637, #12678, #12696, #12697, #12711, #12733, #12746, #12767, #12770, #12789, #12803, #12805, #12811, #12866, #12868, #12899, #12926, #12934, #12950, #12951, #12974, #13001, #13006, #13011, #13017, #13026, #13027, #13034, #13035, #13038, #13042, #13050, #13060, #13061, #13069, #13107, #13120, #13136, #13195, #13197 | +| [@Hsia97](https://github.com/Hsia97) | #11624 | +| [@jacobsparts](https://github.com/jacobsparts) | #11854, #12155, #12167 | +| [@joglomedia](https://github.com/joglomedia) | #11980 | +| [@jonlwheat2-gif](https://github.com/jonlwheat2-gif) | #11443, #11450, #11549, #11567, #11608, #11633, #11635, #11640, #11644, #11671, #11672, #11673, #11674, #11675, #11676, #11677, #11706, #11783, #12051, #12052, #12053, #12055 | +| [@kanade-hoshino](https://github.com/kanade-hoshino) | #12180 | +| [@Karan825](https://github.com/Karan825) | #11861 | +| [@KaspaPulse](https://github.com/KaspaPulse) | #11389, #11469, #11628, #11666, #11910 | +| [@keeltrace](https://github.com/keeltrace) | #12080, #12223 | +| [@killer30001000](https://github.com/killer30001000) | #12178 | +| [@KooshaPari](https://github.com/KooshaPari) | #12470, #12592, #12667, #12699, #12703, #12706, #12764, #12769, #12771, #13300, #13301, #13302, #13340, #13401, #13403, #13404, #13405, #13406, #13407, #13409, #13410, #13411, #13412, #13413, #13414, #13418, #13424, #13427, #13433, #13523, #13524, #13525, #13528, #13531, #13532, #13534, #13539, #13543, #13545, #13546, #13547, #13550, #13553 | +| [@kriptoburak](https://github.com/kriptoburak) | #11370 | +| [@KrzysiekSko](https://github.com/KrzysiekSko) | #12425, #12432, #12673 | +| [@marcelokarval](https://github.com/marcelokarval) | direct commit / report | +| [@maxmad64bis](https://github.com/maxmad64bis) | #1622, #11435, #11437, #11441, #11537, #11550, #11553, #11555, #11557, #11812, #11842, #11843, #11903, #12151, #12214, #12215, #12218, #12226, #12314, #12316, #12317, #12318, #12319, #12320, #12321, #12507, #12715, #12731, #12744, #12786, #12787, #12788, #12790, #12792, #12794, #12795, #12828, #12832, #12853, #12857, #12870, #12937, #12941, #12975, #13141, #13142, #13146, #13147 | +| [@mdigitalbh81](https://github.com/mdigitalbh81) | #12240 | +| [@Meet6338-X](https://github.com/Meet6338-X) | #11598, #11609, #12110 | +| [@morpheus9393](https://github.com/morpheus9393) | #11943 | +| [@MumuTW](https://github.com/MumuTW) | #11492, #11502, #11506, #11507, #11626, #11675, #11685, #11728, #11746, #11888, #11889, #11890, #11892 | +| [@Neuron-Mr-White](https://github.com/Neuron-Mr-White) | #11622, #11801, #12256 | +| [@NightStalker-87](https://github.com/NightStalker-87) | #12183 | +| [@NoxzRCW](https://github.com/NoxzRCW) | #11879, #11880, #11881, #11882, #11883, #11935 | +| [@ntdat812](https://github.com/ntdat812) | #11585, #12095 | +| [@ntdatt812](https://github.com/ntdatt812) | #11368, #11573, #11574, #11576, #11577, #11580, #11582, #11583, #11588, #11589, #11590, #11591, #11592, #11593, #11672, #12177, #12180, #12873, #12918, #12920, #12921, #12925, #12930, #13007, #13009, #13024, #13025, #13083, #13087, #13101, #13104, #13110 | +| [@official-burak](https://github.com/official-burak) | #11542 | +| [@opensource-elearning](https://github.com/opensource-elearning) | #12179, #12189, #12278, #12286 | +| [@oyi77](https://github.com/oyi77) | #11408, #11409, #11421, #11505, #11677, #12036, #12110 | +| [@pacocartones](https://github.com/pacocartones) | #11521, #11522, #11527, #11528, #11529, #11530, #11531, #11532, #11533, #11534, #11595, #11599, #11603, #11604, #11605, #11607, #11610, #11676, #11714, #11716, #11718, #11767, #11838, #11860, #11862, #11869, #11871, #11872, #11873, #11903, #11906, #11921, #11934, #12356, #12358, #12359, #12360, #12361, #12362, #12364, #12365, #12373, #12374, #12375, #12376, #12377, #12379, #12380, #12381, #12386, #12387, #12389, #12390, #12394, #12395, #12397, #12401, #12403, #12404, #12522, #12523, #12535, #12536, #12540, #12541, #12543, #12545, #12548, #12549, #12550, #12551, #12552, #12647, #12651, #12653, #13055 | +| [@patrykkopycinski](https://github.com/patrykkopycinski) | #11936, #11937, #12224 | +| [@PauloFH](https://github.com/PauloFH) | #11509 | +| [@PauloHSOliveira](https://github.com/PauloHSOliveira) | #12241 | +| [@ponkcore](https://github.com/ponkcore) | #12054 | +| [@Prajeeth-12](https://github.com/Prajeeth-12) | #11634, #12046 | +| [@quiterunner-commits](https://github.com/quiterunner-commits) | #12131, #12143 | +| [@rafacpti23](https://github.com/rafacpti23) | #11554, #11558, #12192, #12197, #12198, #12202, #12203, #12204 | +| [@ragnar-claude](https://github.com/ragnar-claude) | #11564 | +| [@raheemuddin786](https://github.com/raheemuddin786) | #11491, #11839, #11840, #11841, #12003, #12230, #12231, #12232, #12233, #12234 | +| [@rahilmavani](https://github.com/rahilmavani) | #11761 | +| [@Rahulsharma0810](https://github.com/Rahulsharma0810) | #11771 | +| [@RaviTharuma](https://github.com/RaviTharuma) | #11710, #11727, #11797, #11798, #11802, #11805, #11806, #11811, #12098, #12099, #12101, #12449, #12452, #12472, #12473, #12493, #12533, #12607, #12628, #12631, #12636, #12875, #12876, #12880, #12882, #12884 | +| [@rezjalibd](https://github.com/rezjalibd) | #12186 | +| [@RhianB14](https://github.com/RhianB14) | #11692 | +| [@rifqiawl](https://github.com/rifqiawl) | #11517, #11519, #11969 | +| [@rqzbeh](https://github.com/rqzbeh) | #11390 | +| [@Sabeekhann](https://github.com/Sabeekhann) | #11699, #11705 | +| [@santosraju99-hub](https://github.com/santosraju99-hub) | #11755, #11770 | +| [@Seramicx](https://github.com/Seramicx) | #11568 | +| [@Siva010](https://github.com/Siva010) | #12191 | +| [@solstxce](https://github.com/solstxce) | #11597 | +| [@soroush5](https://github.com/soroush5) | #12691 | +| [@tenshiak](https://github.com/tenshiak) | #12279 | +| [@TheDemonTuan](https://github.com/TheDemonTuan) | #11468, #11470, #11471, #11482, #11548, #11758, #11775, #11814 | +| [@thomasmaerz](https://github.com/thomasmaerz) | #12834 | +| [@tuandinh0801](https://github.com/tuandinh0801) | #11454 | +| [@turbolego](https://github.com/turbolego) | #11621, #11762, #11772, #11774, #11781, #12216 | +| [@Tushar49](https://github.com/Tushar49) | #11392 | +| [@ujjawalkaushik1110](https://github.com/ujjawalkaushik1110) | #11863 | +| [@vermasomesh835](https://github.com/vermasomesh835) | #11794 | +| [@vsd2807](https://github.com/vsd2807) | #11565, #11619 | +| [@wahidsadik371-coder](https://github.com/wahidsadik371-coder) | #12126 | +| [@watchingdogs](https://github.com/watchingdogs) | #12031 | +| [@wildcard](https://github.com/wildcard) | #11620 | +| [@xiaoyaner0201](https://github.com/xiaoyaner0201) | #11460, #11662, #12083 | +| [@Xxx91n](https://github.com/Xxx91n) | #11690 | +| [@yourspraveen](https://github.com/yourspraveen) | #11146 | +| [@yxyxy](https://github.com/yxyxy) | #11671 | +| [@ZaimMarzuki](https://github.com/ZaimMarzuki) | #11960 | +| [@Zartharas](https://github.com/Zartharas) | #11340 | +| [@zero-executioner](https://github.com/zero-executioner) | #11631 | +| [@diegosouzapw](https://github.com/diegosouzapw) | maintainer | + +--- ## [3.8.50] — 2026-08-25 _Living section — regenerated 2026-08-12 from all cycle commits (cycle open `ed2db6cb19` → tip). Bullets carry the merged PR and its author; direct pushes listed separately._ diff --git a/docs/i18n/uk-UA/CHANGELOG.md b/docs/i18n/uk-UA/CHANGELOG.md index 3b7ba2d9a3..8ca211d3c3 100644 --- a/docs/i18n/uk-UA/CHANGELOG.md +++ b/docs/i18n/uk-UA/CHANGELOG.md @@ -8,16 +8,1119 @@ ## [3.8.51] — TBD -_Living section — cycle opened at the v3.8.50 freeze (parallel-cycle model). Bullets are aggregated from `changelog.d/` fragments at each `/generate-release` phase._ +_Living section — reconciled 2026-09-15 from all cycle commits (`091589089c` → `c0f92ec98a`, 916 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 | **123** | +| 📝 Commits in the cycle | **916** | +| 🔀 Pull requests referenced | **892** | +| 📋 Changelog entries | **919** | +| 🙌 Contributors credited in entries | **121** | +| 🤖 Automated dependency commits | 18 | + +**Entries by type** + +| Type | Count | +| --- | ---: | +| 🐛 Fixes | 592 | +| ✨ Features | 131 | +| 🧹 Chore | 93 | +| 📚 Docs | 42 | +| 🧪 Tests | 32 | +| ♻️ Refactor | 8 | +| 🏗️ Build | 7 | +| ⚡ Performance | 7 | +| ⚙️ CI | 2 | +| ⏪ Reverts | 2 | +| 📦 Dependencies | 2 | +| 🔒 Security | 1 | + +### 🏆 Top 25 contributors this cycle + +_By commits in `091589089c..c0f92ec98a`, author identities consolidated via `.mailmap` and the merged PR's GitHub login. Bots excluded._ + +| # | Contributor | Commits | +| ---: | --- | ---: | +| 🥇 | diegosouzapw | 323 | +| 🥈 | Bob.Hou (@HouMinXi) | 79 | +| 🥉 | Paco Cartones (@pacocartones) | 77 | +| 4 | Dizzle (@maxmad64bis) | 49 | +| 5 | Koosha Paridehpour (@KooshaPari) | 43 | +| 6 | Nguyen Thanh Dat (@ntdatt812) | 31 | +| 7 | Ravi Tharuma (@RaviTharuma) | 26 | +| 8 | Markus Hartung (@hartmark) | 22 | +| 9 | Webman (@jonlwheat2-gif) | 22 | +| 10 | backryun | 17 | +| 11 | anhtahaylove | 15 | +| 12 | Rafa Martins (@rafacpti23) | 10 | +| 13 | Syed Raheemuddin (@raheemuddin786) | 10 | +| 14 | Nguyễn Viết Tuấn (@TheDemonTuan) | 8 | +| 15 | MumuTW | 7 | +| 16 | Paijo (@oyi77) | 7 | +| 17 | Tobias Andersen (@turbolego) | 6 | +| 18 | Bl0ck (@Bl0ck154) | 5 | +| 19 | SHANMUGAPRIYAN (@geek007git) | 5 | +| 20 | KaspaPulse | 5 | +| 21 | Abhishek Sharma (@abhisheksharma2411) | 4 | +| 22 | Abhishek Divekar (@adivekar-utexas) | 4 | +| 23 | Andrew B. (@AndrianBalanescu) | 4 | +| 24 | NoxzRCW | 4 | +| 25 | opensource-elearning | 4 | ### ✨ New Features +- **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). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- Added Google AI Studio Gemini batch text-to-speech support through `POST /v1/audio/speech`. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- 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. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(routing):** subscription-first auto groupings — `auto/subscription` routes only through plan-included connections with a documented hard-stop overage and fails closed on exhaustion, while `auto/thrifty` orders the pool `subscription → keyless → free → cheap → premium` and steps up one rung at a time as each is exhausted. Billing class comes from a curated per-connection catalog (uncurated is treated as metered, never plan-included), both reuse STRICT_ZERO_COST's per-connection verification, and a quota reading whose `resetAt` has passed is now refreshed regardless of TTL so routing returns to plan capacity as soon as it resets ([#11146](https://github.com/diegosouzapw/OmniRoute/pull/11146)) — thanks @yourspraveen +- **feat(providers):** publish a management-authenticated versioned web-session credential contract from OmniRoute's canonical browser credential metadata ([#11340](https://github.com/diegosouzapw/OmniRoute/pull/11340)) — thanks @Zartharas +- **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)) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add Xquik X search with typed results, credential validation, REST routing, and MCP selection ([#11370](https://github.com/diegosouzapw/OmniRoute/pull/11370)) — thanks @kriptoburak +- **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)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(dashboard):** surface durable exclusive managed leases in the existing Sessions view, keeping leased clients visible across idle gaps while marking connections with in-flight work as active ([#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389)) — thanks @KaspaPulse +- **build(bun):** allow Turbopack bundler flag on Bun 1.4+ with configurable Webpack fallback ([#11471](https://github.com/diegosouzapw/OmniRoute/pull/11471)) — thanks @TheDemonTuan +- feat(api): add an opt-in `modelVisibilityAllowlist`/`modelVisibilityDenylist` settings pair to curate exactly which models `/v1/models` advertises, mirrored into every `auto/*` combo candidate pool so a denied model cannot be routed to via combo selection either (#11481) ([#11997](https://github.com/diegosouzapw/OmniRoute/pull/11997)) +- **feat(rankings):** the Free Provider Rankings page shows what each provider actually served over the last 24 h. It ranked by ELO alone, which left a provider that answers every call with an error in first place; the usage data was already served by the API but never requested. A provider with too small a sample shows a dash, not a number ([#11546](https://github.com/diegosouzapw/OmniRoute/pull/11546)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **feat(guardrails):** enforce a bounded, deterministic contract for Video Bridge transcripts — 256 cues, 4096 input code units and 4 KiB UTF-8 per cue, 64 KiB total text, malformed-Unicode rejection, focus-window scoping, cross-source reconciliation with contributing-source metadata, and a structural provenance trust boundary so caller JSON can never self-assert `embedded`/`audio-bridge` provenance ([#11652](https://github.com/diegosouzapw/OmniRoute/issues/11652)) ([#12009](https://github.com/diegosouzapw/OmniRoute/pull/12009)) +- **feat(video):** orchestrate optional Video Bridge audio extraction and Audio Bridge STT behind a dual opt-in (operator setting AND per-request signal) — a new loopback-only broker `mode=audio` operation shares the frame path's exact process queue, deadline, AbortSignal, and byte budgets to extract a bounded mono 16 kHz PCM WAV from the same already-downloaded video, then reuses the existing Audio Bridge transcription boundary; provider segment timing is preserved when available and marked coarse otherwise, and every failure degrades to a visual-only-safe partial instead of throwing (#11654). ([#12012](https://github.com/diegosouzapw/OmniRoute/pull/12012)) +- Add a tenant-bound Video Bridge drill-down lifecycle on top of the existing secure cache substrate: opaque hashed handles (never raw session/video identifiers), preview/standard/detail multiresolution variants resampled on read, response pagination capped at 8 frames and 32 MiB, and a new authenticated `/api/v1/video-bridge/drilldown` consumer route that stays disabled for remote access by default and denies cross-key access with the same response as a nonexistent handle (no existence oracle). ([#12006](https://github.com/diegosouzapw/OmniRoute/pull/12006)) +- **test(video):** Add the Video Bridge FU-07/FU-09 promotion-evidence harness (#11656) — a frozen Zod manifest schema covering the 8 required scenario kinds (static scenes, rapid cuts, late facts, fades, blur, small text, close events, visual prompt injection) with a minimum of 3 repetitions per case, deterministic declarative fixture recipes (`videoBridgePromotionFixtures.ts`), a pure medians/p95 metrics aggregator, a pure FU-07/FU-09 promotion-verdict evaluator applying the ticket's exact thresholds (missing token usage always holds), a digest-only persistence layer that never retains raw media or raw model responses, and a versioned per-model promotion allowlist shipped empty with every model defaulting to `hold`. The FU-07/FU-09 promotion verdicts themselves remain HOLD — they require a real evidence run against real models on VPS 192.168.0.15. ([#12008](https://github.com/diegosouzapw/OmniRoute/pull/12008)) +- **feat(video bridge):** "embedded" transcript provenance can now be legitimately earned instead of merely asserted — a bounded, allowlisted (`mov_text`/`subrip`/`webvtt`) subtitle probe runs through the loopback-only Video Bridge broker (at most 2 streams, 10s subdeadline bounded by the request deadline, 256 KiB output, 4096-code-unit lines), normalized through a bounded, ReDoS-safe WebVTT parser and Zod-validated end to end. The adapter always resolves to an explicit `success`/`absent`/`transient_failure` outcome — a subtitle failure never breaks the visual description path, and only a fingerprint-verified broker response (never a caller-declared label) can produce embedded cues (#11659). ([#12011](https://github.com/diegosouzapw/OmniRoute/pull/12011)) +- Default new Antigravity-family connections (agy CLI imports and Antigravity OAuth connects) to model auto-sync, so live model discovery lands in the synced catalog and `/v1/models` picks up freshly released upstream models (e.g. Gemini 3.7 Flash tiers) without code changes. Existing connections keep their current setting; the per-connection dashboard toggle remains the opt-out. (#11685 — thanks @MumuTW) ([#11887](https://github.com/diegosouzapw/OmniRoute/pull/11887)) +- **feat(ui):** enable React Compiler (`reactCompiler: true` + `babel-plugin-react-compiler`) for automatic memoization at build time ([#11783](https://github.com/diegosouzapw/OmniRoute/pull/11783)) — thanks @jonlwheat2-gif +- **feat(providers):** add SeekAi (`seekai.cc`) as an OpenAI-compatible New-API gateway — catalog id `seekai` (alias `ska`), `https://seekai.cc/v1`, live `/v1/models` via `passthroughModels`, aggregator-list membership so New-API balance detection can opt in. No referral/aff codes. ([#11786](https://github.com/diegosouzapw/OmniRoute/issues/11786)) ([#12557](https://github.com/diegosouzapw/OmniRoute/pull/12557)) — thanks @HouMinXi +- **feat(zai):** add GLM-5.3-Flash Coding Plan support (1M context, 128K output, vision, `low|high|max` reasoning) and route `zai` GLM-5.3-family API-key traffic through the OpenAI-compatible Coding Plan endpoint with native thinking defaults ([#11801](https://github.com/diegosouzapw/OmniRoute/pull/11801)) — thanks @Neuron-Mr-White +- **feat(combo):** choose how combo models are ordered — manual, provider, score, or name — via a sort control in the dashboard builder, persisted in `config.modelSort` and re-applied on load and after add ([#11812](https://github.com/diegosouzapw/OmniRoute/pull/11812)) — thanks @maxmad64bis +- **feat(free):** custom models can be marked free-tier via `customModels[].isFree`; `isFreeModel()` is the first door and `hidePaidModels` respects it even for providers outside the free budget ([#11843](https://github.com/diegosouzapw/OmniRoute/pull/11843)) — thanks @maxmad64bis +- **feat(nodejs):** add `5dive` as a `configure` target — `omniroute configure 5dive` / `omniroute setup-5dive` write a 5dive auth profile that points an agent fleet's `claude` seats at OmniRoute, with the root-only write, the loopback-vs-`https` endpoint rule and the per-seat model pin handled explicitly ([#11852](https://github.com/diegosouzapw/OmniRoute/pull/11852)) — thanks @5dive-bot +- **feat(sse):** treat `max` as a first-class reasoning-effort tier and clamp per model family (GLM 5.1+/DeepSeek V4+/Kimi K3+ keep native `max`; o1/MiniMax/Grok/Muse Spark clamp to their upstream ceiling) ([#11875](https://github.com/diegosouzapw/OmniRoute/pull/11875)) — thanks @Chewji9875 +- **feat(providers):** the provider plugin manifest now advertises a `usage-fetch` capability for the 40 providers that have a wired usage/quota fetcher, so external dashboards can read it from `GET /api/v1/provider-plugin-manifest` instead of parsing `open-sse/services/usage.ts` after every release. Discovery only — no new fetcher, no quota change, and the Dashboard quota widget stays gated by `USAGE_SUPPORTED_PROVIDERS`. `USAGE_FETCHER_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/fetcherProviders.ts`) and is re-exported from `services/usage.ts`, keeping the manifest module a light leaf instead of pulling the ~490-module usage dispatcher into the manifest route. ([#11903](https://github.com/diegosouzapw/OmniRoute/pull/11903)) — thanks @maxmad64bis / @pacocartones +- **feat(plugins):** `OMNIROUTE_PLUGINS_DIR` sets the directory the runtime plugin scanner reads — and the root the plugin manager installs into — overriding the `HOME`-derived default, so a Docker/K8s deployment can point straight at its bind-mounted plugin tree instead of moving `HOME` just to relocate the scan path. An image that exports no home no longer scans `/tmp/.omniroute/plugins` in silence: the resolved directory is logged once at startup as `scanner.dir_resolved`, naming the input that won. Unset, behaviour is unchanged. Distinct from the CLI-only `OMNIROUTE_PLUGIN_PATH`, which finds `omniroute-cmd-*` command packages and never reached this scanner ([#11906](https://github.com/diegosouzapw/OmniRoute/pull/11906)) — thanks @amaleta / @pacocartones +- **feat(leases):** add an explicit owner-authenticated status action that returns only the active lease's privacy-safe configured connection and provider labels, with generation fencing and no credential or internal-id disclosure ([#11910](https://github.com/diegosouzapw/OmniRoute/pull/11910)) — thanks @KaspaPulse +- **feat(providers):** import-from-file modal shows per-row API errors and ships a downloadable CSV template ([#12071](https://github.com/diegosouzapw/OmniRoute/issues/12071)) ([#12504](https://github.com/diegosouzapw/OmniRoute/pull/12504)) — thanks @HouMinXi +- **feat(providers):** dashboard search matches connection name and `baseUrl` so imported OpenAI-compat nodes surface on the provider card ([#12108](https://github.com/diegosouzapw/OmniRoute/issues/12108)) ([#12495](https://github.com/diegosouzapw/OmniRoute/pull/12495)) — thanks @HouMinXi +- **feat(routing):** add a `score` Auto router strategy that selects the highest configured weighted score and reuses `explorationRate`. ([#12155](https://github.com/diegosouzapw/OmniRoute/pull/12155)) — thanks @jacobsparts +- Add a runtime feature flag to disable universal context handoffs globally without changing the default behavior. ([#12167](https://github.com/diegosouzapw/OmniRoute/pull/12167)) — thanks @jacobsparts +- **feat(providers):** the provider plugin manifest now also advertises a `usage-supported` capability for the 46 providers whose usage API is accepted by the server and Dashboard routes, so integrators can distinguish "the server will serve quota for this provider" from "a fetcher is wired" without reading TypeScript. Discovery only — no fetcher or quota change. `usage-fetch` resolves on id or alias (the usage dispatcher accepts both); `usage-supported` resolves on id alone, matching the runtime guard `USAGE_SUPPORTED_PROVIDERS.includes(providerId)`. `USAGE_SUPPORTED_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/supportedProviders.ts`) and is re-exported from `providers.ts`, mirroring the `fetcherProviders` leaf from #11903 and keeping the manifest a light module. ([#12214](https://github.com/diegosouzapw/OmniRoute/pull/12214)) — thanks @maxmad64bis +- **feat(rankings):** order Free Provider Rankings by what each provider actually served — `GET /api/free-provider-rankings?sortBy=reliability` and a "Most reliable first" toggle on the page. Providers with too few calls to state a success rate keep their score order below the measured ones; the default order is unchanged ([#12218](https://github.com/diegosouzapw/OmniRoute/pull/12218)). — thanks @maxmad64bis +- **perf(sse):** defer `cloneLogPayload()` in the structured SSE collector until after the `maxEvents`/`maxBytes` cap check, eliminating ~9,800 wasted `structuredClone` calls per streaming response (65–71% faster `push()`). Reducer snapshot isolation restored for OpenAI and Responses summaries ([#12241](https://github.com/diegosouzapw/OmniRoute/pull/12241)) — thanks @PauloHSOliveira ([#12243](https://github.com/diegosouzapw/OmniRoute/pull/12243)) +- **feat(usage):** Devin CLI agentic quota (Codeium seat-management GetUserStatus) and OpenRouter key limits plus account credits now surface in Provider Limits ([#12256](https://github.com/diegosouzapw/OmniRoute/pull/12256) — thanks @Neuron-Mr-White) +- **feat(settings):** persist `headroomUrl` through Settings so status/start use the operator URL instead of only `HEADROOM_URL` ([#12306](https://github.com/diegosouzapw/OmniRoute/issues/12306)) ([#12487](https://github.com/diegosouzapw/OmniRoute/pull/12487)) — thanks @HouMinXi +- **feat(auto-combo):** Auto-Combo scoring can now weigh how often a provider/model has actually succeeded. The engine already carried that number on every candidate — 24 hours of usage history behind a ten-sample floor, real-time metrics otherwise — and the scoring function never read it, while the weight table described `stability` as if it did. `reliability` (`1 - failureRate`, with the same field precedence and the same rate-bounding the speed ranking already uses, so a corrupt reading means "nothing observed" rather than "fails every call") is now a declared factor shipping at weight `0`, so routing is unchanged until an operator gives it one, and the `stability` description now matches what that factor computes ([#12317](https://github.com/diegosouzapw/OmniRoute/pull/12317)) — thanks @maxmad64bis +- **feat(routing):** With `freeAccessPolicy: "strict"`, the read-only candidate listing (`GET /v1/auto-combo/{channel}/candidates`) no longer hides the candidates the zero-cost guard excludes — the same read-only transparency the resilience filter already honours (#9133). Each candidate now carries `freeAccessExclusion` saying why it would be kept out, and it tells an exhausted allowance apart from a quota reading that never arrived or went stale, which used to look identical from the outside. Routing is unchanged: the listing reports, it never enforces. The separate `excludeTosAvoid` guard still drops its candidates without a reason; that gap is now documented rather than closed ([#12319](https://github.com/diegosouzapw/OmniRoute/pull/12319)) — thanks @maxmad64bis +- **feat(radar):** The Radar catalog table now shows two facts it was already receiving from the feed and dropping on the floor: the per-model rate limits (requests and tokens, per minute and per day) in a new column, and a badge when a provider's terms state it may train on the prompts you send. A limit of zero renders as zero rather than "rate-only" — for a ceiling those are opposite facts — and a model with no training statement gets no badge, because an absent statement is not a guarantee ([#12320](https://github.com/diegosouzapw/OmniRoute/pull/12320)) — thanks @maxmad64bis +- **feat(radar):** explain Community, single-use, contributor, supporter, recovery, abuse, offers, and privacy rules before either Radar activation action, and remove the superseded fixed-PR grant promise from every UI locale ([#12342](https://github.com/diegosouzapw/OmniRoute/pull/12342)) +- **feat(gamification):** the dashboard Profile page now shows the real daily streak — `/api/gamification/level` returns `streak: { current, longest }` (per key with `apiKeyId`, operator-wide maximum otherwise) and the streak card reads it instead of a hard-coded 0 (#2403) ([#12377](https://github.com/diegosouzapw/OmniRoute/pull/12377)) — thanks @pacocartones +- **feat(gamification):** the dashboard leaderboard now shows each API key's display name under the Name column instead of a truncated key id; `GET /api/gamification/leaderboard` attaches `name` per entry (name only — no key material), while the shared ranking helper and the federation leaderboard stay id-only — thanks @pacocartones ([#12385](https://github.com/diegosouzapw/OmniRoute/pull/12385)) +- **feat(gamification):** enforce the documented 1000 XP/min per-API-key anti-cheat rate limit on the XP award path; over-limit awards are logged and skipped instead of persisted, and the sliding window now matches the timestamp format stored in `xp_audit_log` ([#2403](https://github.com/diegosouzapw/OmniRoute/issues/2403)) ([#12390](https://github.com/diegosouzapw/OmniRoute/pull/12390)) — thanks @pacocartones +- **feat(admin):** localize the gamification anomalies page — the loading state, the Status column and the Suspicious badge now come from the `common` catalog (new `common.suspicious` key propagated to every locale) — add it to the Gamification sidebar group as `gamification-admin` (`/dashboard/gamification/admin`), and expose the loading and empty states as polite `role="status"` live regions (#12401 — thanks @pacocartones) +- **feat(providers):** skip GitHub combo members missing from the live synced catalog, and drop Copilot models that are policy-disabled or hidden from the model picker ([#12473](https://github.com/diegosouzapw/OmniRoute/pull/12473)) — thanks @RaviTharuma +- **feat(i18n):** locale-expansion tooling — `npm run i18n:add-locale` adds a language to config, dashboard, docs mirrors, CLI, README/indexes and site in one command; browser, CLI and cookie detection resolve `uk`, `fil`/`tl`, `zh-Hant` (and the retired legacy `in` → `id`) via config aliases; `config/i18n.json` now ships in the npm package so the published CLI can read it; new real-translation ratio gate (`npm run i18n:check-ratio`, advisory) with a per-locale ratchet baseline; `run-translation --adopt` restores `.i18n-state.json`; `sync-language-bars` generates 🌐 bars from config; `validate_translation.py` loads its allowlist again. Retires the duplicate `in` locale (Indonesian mislabelled as Hindi) — 42 honest locales; saved `NEXT_LOCALE=in` / `OMNIROUTE_LANG=in` keep working. (#12496) +- **feat(api):** Emit gateway-measured `tokens_per_second` (TTFT excluded) on streaming usage and `X-OmniRoute-Tokens-Per-Second` when first-token latency is known ([#12616](https://github.com/diegosouzapw/OmniRoute/issues/12616)) ([#12631](https://github.com/diegosouzapw/OmniRoute/pull/12631)) — thanks @RaviTharuma +- **feat(opencode-plugin): map gateway cost/usage/tok/s onto OpenCode inference payloads** — the official plugin copies `X-OmniRoute-Response-Cost`, token counts, `X-OmniRoute-Tokens-Per-Second` / `usage.tokens_per_second`, TTFT, and the winning `X-OmniRoute-Model` onto the JSON/SSE body OpenCode already consumes. Missing tok/s is left unset (never `tokens / latency`). (#12636) — thanks @RaviTharuma +- **feat(combo):** add combo strategy `quota-weighted`: skip exhausted accounts, then weighted-draw by leftover / in-flight load; existing conversations stay pinned until the account is empty ([#12789](https://github.com/diegosouzapw/OmniRoute/pull/12789)) — thanks @HouMinXi +- **feat(models):** Account live listings become the chat catalog source for Claude, Codex, Copilot, and AGY; public metadata only fills prices on IDs those accounts already list. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **fix(models):** Union `agy` and `antigravity` live catalogs so `agy/gemini-3.8-flash-high` is not rejected after the prefix folds to `antigravity`. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **feat(opencode):** Add `@omniroute/opencode-plugin-v2`, an opencode v2 plugin that gives a v2 install a route to an OmniRoute gateway: it publishes models, combos and auto-combos into the host catalog, refreshes them lazily behind a 300s TTL, and keeps serving the last known catalog from an on-disk snapshot when the gateway is unreachable. The v1 plugin (`@omniroute/opencode-plugin`) is untouched and keeps working as before. The plugin adapts to the host instead of assuming one shape: it reads the shape the host seeds into the catalog draft and publishes the `api` block, the pre-`api` top-level fields, or both. The gateway key comes from the host credential store when one is connected, so no secret needs to sit in a config file; a source the gateway refuses is named in a warning instead of silently degrading the catalog to raw model ids; and tool schemas bound for a Gemini model are stripped of the JSON-Schema keywords Gemini rejects, so tool calling survives that route ([#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870)) — thanks @maxmad64bis +- feat(services): show sanitized CLIProxyAPI account health from its authenticated management API without exposing credentials, file paths, or raw account metadata (#6342) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add AnySearch free web search + URL extract (webFetch) with typed results, credential validation, REST routing, and MCP selection - fallback-only ([#11690](https://github.com/diegosouzapw/OmniRoute/pull/11690)) — thanks @Xxx91n +- **feat(dashboard):** display clamped `[0, 100]%` cached input token ratio in request logs table ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) — thanks @b3nw +- **feat(catalog):** add `OMNIROUTE_DISABLE_THINKING_LEVEL_VARIANTS` feature flag to optionally filter out thinking level variants from model catalog ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) — thanks @b3nw +- **feat(dashboard):** continuously export call logs to external analytics stores. A pluggable destination registry ships the full Logs-tab record set on an hourly `JobRegistry` cron, with a persisted per-destination cursor, batched inserts, a config UI rendered from each destination's own field descriptors, and a REST layer (`/api/log-export/*`) for CRUD, a connection test, and an on-demand run. A destination can opt into `includeBodies` to also ship the request and response payloads shown in the Logs detail pane, including the client and provider views of each call; this is off by default, and payloads inherit the dashboard's PII sanitisation, secret redaction and `noLog` handling. Google BigQuery is the first destination, using a service-account key stored encrypted at rest and streaming inserts keyed by call-log id, into a table that is day-partitioned on `timestamp` and clustered on `api_key_name`, `provider`, `model` and `status`. ([#11945](https://github.com/diegosouzapw/OmniRoute/pull/11945)) — thanks @dpozimski +- **feat(providers):** add MaxAI as a signed, OpenAI-compatible provider serving its 13 paid chat models (GPT-5.6 / Luna / Thinking, Claude 5 Sonnet, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite, Grok 4.1-fast / 4.5, DeepSeek V3.2 / R1, Llama 3.3 70B) through OmniRoute's `/v1` endpoint, with per-request HMAC-SHA1→SM3→AES request signing, live model + context-window discovery from `/models/get_config`, and prompted tool-calling translated to OpenAI `tool_calls` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI vision input — image_url content parts are forwarded inline in `message_content` to the 6 vision-capable models (GPT-5.6 / Luna / Thinking, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite) ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI image generation — 6 image models (gpt-image-1, dall-e-3, flux-1-schnell/dev/pro, sd3-medium) exposed through `POST /v1/images/generations` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI document RAG — inline base64 file/document attachments are uploaded to MaxAI (content-addressed `doc_id`) and attached to the chat via `doc_list` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** browserless MaxAI onboarding — email device-pair login (`/api/providers/[id]/login`) and signed access-token refresh, so a connection can be created and kept fresh without a real browser or Google OAuth ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** per-provider TLS impersonation profile (MaxAI presents a Windows Firefox-150 client fingerprint) so its bot-sensitive endpoints accept OmniRoute traffic ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** add **Nimble** as a web-search and web-fetch provider (`nimble-search`) — `/v1/search` routes to Nimble's search API at `lite` depth (locale, freshness and include/exclude domain filters mapped onto the shared request contract), and `/v1/web/fetch` routes to Nimble Extract, which covers all four fetch formats (`markdown`, `html`, `links`, `screenshot`) from a single call. One API key serves both surfaces. ([#11620](https://github.com/diegosouzapw/OmniRoute/pull/11620)) — thanks @wildcard +- The npm publish is automatic again, through npm Trusted Publishing (OIDC): the hosted `stage-npm` job publishes with a short-lived credential minted from GitHub's id-token — no `NPM_TOKEN`, no 2FA prompt, provenance attached. `publish_mode=staged` (owner approves with 2FA) and `direct` (token) remain available on `workflow_dispatch`. ([#11931](https://github.com/diegosouzapw/OmniRoute/pull/11931)) +- **feat(providers):** Add **Opper** as an API-key gateway provider — EU-hosted AI gateway with 700+ models from 30+ providers behind one OpenAI-compatible API and one key (`OPPER_API_KEY`); model ids use `provider/model` format (e.g. `anthropic/claude-sonnet-4-6`, `openai/gpt-5`); live model catalog at `https://api.opper.ai/v3/compat/models`; entry mirrors `requesty` (same shape, `passthroughModels: true`, no static seed) ([#11629](https://github.com/diegosouzapw/OmniRoute/pull/11629)) +- **feat(dashboard):** the `/dashboard/orchestration` snapshot hook now subscribes to the `agents` WebSocket channel (`agent.task.updated`) instead of `requests` as its refetch trigger, and relaxes its background poll from 5s to 30s while that WS connection is up — falling back to the tighter 5s cadence, reprogrammed live on any connect/disconnect transition, whenever the socket is down. ([#12409](https://github.com/diegosouzapw/OmniRoute/pull/12409)) +- **feat(dashboard):** Orchestration canvas quick wins — search box plus state/source/provider filter chips with a one-click clear, and per-source collapse/expand, all reflected in the URL so a filtered/collapsed view is shareable and survives a refresh; the detail drawer gained a "copy trace JSON" action and hardened error/empty-state and accessibility handling; the Agents-tab edges now animate traveling particles along active (running) connections; and the canvas node/edge status colors moved off fixed hex values onto theme-aware `--orch-status-*` CSS custom properties, so they adapt correctly to light/dark mode. ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- **chore(dashboard):** Orchestration UI hardening pass and the missing component/model test coverage it called for — `OrchestratorNode`/`ActivityNode`/`OverflowNode` rendering, the `?node=`/overflow-click page routing, the Agents-tab orchestrator-click no-op and `showCompleted` toggle, and the overview kanban's done-column sort order (#12270, #12271). ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- New `/dashboard/orchestration` page: live unified view of everything running — Cloud Agent, A2A and Conductor as a real-time graph (Agents tab), the combo cascade (Routing tab, reusing the Combo Live Studio) and a state kanban (Overview tab), with a detail drawer (trace, cost, approve/cancel). Read-only over existing APIs — no new backend. Canvas concept credit: PR #11815 design ([#12261](https://github.com/diegosouzapw/OmniRoute/pull/12261)) +- **feat(dashboard):** new "History" tab on `/dashboard/orchestration` — an Airflow-style grid of finished runs over a 24h/7d/30d preset window, one row per (source, identity), clicking a cell opens the existing detail drawer. It is backed by real persistence: A2A task lifecycle transitions are now written to the `a2a_tasks` table (purged after 30 days, configurable via `OMNIROUTE_A2A_HISTORY_RETENTION_DAYS`) and served by the new `GET /api/a2a/tasks/history` listing endpoint, with the task-detail route falling back to persisted history once a run leaves the in-memory snapshot. Conductor runs stay remote and are not persisted locally — the tab says so instead of silently omitting them. ([#12479](https://github.com/diegosouzapw/OmniRoute/pull/12479)) +- **feat(dashboard):** the orchestration detail drawer gained a "Repeat" action for Cloud Agent, A2A and Conductor tasks — a two-click confirm (click once to arm, click again within the confirm window to fire) re-submits the original prompt/input as a new run. The button is disabled with an explanatory tooltip whenever the original input can't be recovered from the loaded task detail (e.g. it never carried a prompt, or the detail failed to load). Two limitations of the A2A variant, by design: it targets the `/a2a` JSON-RPC endpoint, which authenticates with an API key only (`REQUIRE_API_KEY=true` or a configured `OMNIROUTE_API_KEY` makes a dashboard-session repeat answer `HTTP 400` — surfaced verbatim in the drawer's error line, never as a success), and the `message/send` call is SYNCHRONOUS: the POST blocks for the whole skill run, so the success confirmation only appears once the repeated task finishes. A dashboard-authenticated A2A creation path is deliberately left to a follow-up — widening the endpoint's auth posture is an operator decision, not a side effect of this feature. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(a2a):** A2A task execution now records which memories were consulted for the task's last user message as `metadata.memoryHits` (id/key/type/content-snippet) plus a `memory_hits` history event, purely for observability — the retrieved memory is never injected into a skill's prompt or behavior. Gated by the `OMNIROUTE_A2A_MEMORY_HITS` kill-switch (default enabled; set to `0` to skip the recall lookup entirely). The drawer's new "Memory used" section lists these hits for a2a tasks and is omitted whenever there are none. Known limitation: recall only resolves under the keyless posture — a keyed caller's task owner is a SHA-256 prefix of the API key, while memory rows are keyed by the database api-key id, and no hash→id lookup exists today, so the hit list stays empty for keyed callers. The recorded hits are also kept out of the task's own `input` (and therefore out of the persisted input and of the "Repeat" request body), so repeating a task never re-sends the previous run's memory snippets. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(providers):** add a Perplexity Agent API provider (`perplexity-agent` / `pplx-agent`) for Perplexity `/v1/responses`, including the documented Anthropic, OpenAI, Google, xAI, DeepSeek, Z.AI, Moonshot/Kimi, NVIDIA, and Perplexity model IDs plus Anthropic-model `max_output_tokens` compatibility. ([#12103](https://github.com/diegosouzapw/OmniRoute/pull/12103)) — thanks @AIB1TAL0S +- **feat(providers):** add RPD (Requests Per Day) limit to provider rate limit overrides across UI, schemas, DB, and i18n ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) — thanks @b3nw +- **feat(providers): add UC Direct (uncensored.com Developer API), the metered OpenAI-compatible surface.** A standard OpenAI-compatible passthrough (default executor) for uncensored.com's official REST API at `https://api.uncensored.com/api/v1`: `X-api-key` auth (never-expiring `uai_sk_live_` key), `POST /chat/completions` with streaming SSE and native tool-calling, and the full live metered catalog (82 models across 15 providers, discovered from the public `GET /v1/models`). Registered as provider `uc-direct` (alias `ucd`). Complements the un-metered `uc` persona provider — same models, metered credits and a plain API key instead of a subscription session. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(providers): add UC (uncensored.com), the un-metered subscription "persona" chat as an OpenAI-compatible provider.** A WebSocket web-app port: a durable Clerk credential mints a short-lived session token per connect (browserless — no API key), driving UC's persona socket. Ships the browserless email-code login (request → verify → harvest), the 19 verified persona models (Claude Opus, Gemini, Grok, GLM, Kimi, DeepSeek, MiniMax, incl. the uncensored variants), prompted `` tool-calling with a per-model code-style dialect + auto-cure retry for guardrailed models, live ``/reasoning split, streaming + non-streaming OpenAI responses, and full quota/auth error surfacing (paywall / message-limit / rate-limit → 429, invalid session → 401 re-login). Full multimodal parity via the persona blob-upload layer: **vision** (image input, 15 vision-capable models), **document RAG** (PDF/doc upload, server-side extraction), **image generation** (22 models), **video generation** (14 models, async signed-url → poll), and **TTS** (streaming MP3). Registered as provider `uc` (alias `ucn`). The metered OpenAI-compatible Developer API is a separate `uc-direct` provider. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(logs):** add copy-all button to request detail modal ([#11083](https://github.com/diegosouzapw/OmniRoute/pull/11083)) — thanks @AndrianBalanescu +- **feat(check):** make serviceKinds required and add the reverse-walk provider consistency gate ([#11392](https://github.com/diegosouzapw/OmniRoute/pull/11392)) — thanks @Tushar49 +- **feat(providers):** only advertise verified-working connections by default ([#11448](https://github.com/diegosouzapw/OmniRoute/pull/11448)) — thanks @alltomatos +- **feat(resilience):** add hierarchical concurrency admission ([#11493](https://github.com/diegosouzapw/OmniRoute/pull/11493)) +- **feat(providers):** derive + surface expiry for JWT-bearing web cookies (#11497) ([#11505](https://github.com/diegosouzapw/OmniRoute/pull/11505)) — thanks @oyi77 +- **feat(tunnels):** support named cloudflared tunnels via CLOUDFLARED_CONFIG ([#11561](https://github.com/diegosouzapw/OmniRoute/pull/11561)) — thanks @ggiak +- **feat(compression):** make proactive context-compression threshold a live setting ([#11564](https://github.com/diegosouzapw/OmniRoute/pull/11564)) — thanks @ragnar-claude +- **feat(resilience):** add provider peak-hour protection ([#11622](https://github.com/diegosouzapw/OmniRoute/pull/11622)) — thanks @Neuron-Mr-White +- **feat(providers):** add 1min.ai provider ([#11631](https://github.com/diegosouzapw/OmniRoute/pull/11631)) — thanks @zero-executioner +- **feat(dashboard):** collapsible JSON tree viewer for request/response payloads ([#11703](https://github.com/diegosouzapw/OmniRoute/pull/11703)) — thanks @hartmark +- **feat(routing):** add LiquidAI LFM2.5-2.6B free tier via OpenRouter ([#11752](https://github.com/diegosouzapw/OmniRoute/pull/11752)) — thanks @brick30llc-ctrl +- **feat(pricing):** add GLM-5.3-Flash pricing, model specs, and catalog registration ([#11830](https://github.com/diegosouzapw/OmniRoute/pull/11830)) — thanks @AndrianBalanescu +- **feat(routing):** add relayMode for schema-locked context handoffs ([#11839](https://github.com/diegosouzapw/OmniRoute/pull/11839)) — thanks @raheemuddin786 +- **feat(config):** add support for runtime system prompt configuration and hot-reloading ([#11841](https://github.com/diegosouzapw/OmniRoute/pull/11841)) — thanks @raheemuddin786 +- **feat(routing):** expand connection-aware quota prefilter across combo strategies (#11682) ([#11850](https://github.com/diegosouzapw/OmniRoute/pull/11850)) — thanks @HouMinXi +- **feat(models):** reactive discovery sync after Antigravity model-not-found 404 ([#11888](https://github.com/diegosouzapw/OmniRoute/pull/11888)) — thanks @MumuTW +- **feat(models):** auto-aliases and token limits from synced Antigravity discovery ([#11889](https://github.com/diegosouzapw/OmniRoute/pull/11889)) — thanks @MumuTW +- **feat(settings):** raise sticky round-robin limit caps to 1000 ([#12015](https://github.com/diegosouzapw/OmniRoute/pull/12015)) — thanks @adivekar-utexas +- **feat(resilience):** operator-configurable global credential health check interval ([#12043](https://github.com/diegosouzapw/OmniRoute/pull/12043)) — thanks @adivekar-utexas +- **feat(quota):** use official OpenCode Go usage API ([#12124](https://github.com/diegosouzapw/OmniRoute/pull/12124)) — thanks @ddarkr +- **feat(resilience):** default the credential health check sweep to 60 minutes ([#12138](https://github.com/diegosouzapw/OmniRoute/pull/12138)) — thanks @adivekar-utexas +- **feat(quality):** new-code mode for the complexity and dead-code ratchets (Clean as You Code) ([#12142](https://github.com/diegosouzapw/OmniRoute/pull/12142)) +- **feat(dashboard):** orchestration canvas — unified model + snapshot hook (part 1/2) ([#12156](https://github.com/diegosouzapw/OmniRoute/pull/12156)) +- **feat(usage):** add Kilo Code balance and Kilo Pass quotas ([#12178](https://github.com/diegosouzapw/OmniRoute/pull/12178)) — thanks @killer30001000 +- **feat(providers):** refresh vendored ChatGPT Web connector to v4.0.7 ([#12181](https://github.com/diegosouzapw/OmniRoute/pull/12181)) — thanks @backryun +- **feat(providers):** manual "Clear cooldown" action in the cooling panel ([#12224](https://github.com/diegosouzapw/OmniRoute/pull/12224)) — thanks @patrykkopycinski +- **feat(providers):** restore ChatGPT Web via clean-room browser transport ([#12239](https://github.com/diegosouzapw/OmniRoute/pull/12239)) — thanks @backryun +- **feat(sse):** wire the PROVIDER_PROFILES window gate into the global provider cooldown ([#12247](https://github.com/diegosouzapw/OmniRoute/pull/12247)) +- **feat(providers):** modernize CLOVA Studio chat and embeddings ([#12277](https://github.com/diegosouzapw/OmniRoute/pull/12277)) — thanks @backryun +- **feat(browser):** adopt Obscura as primary headless browser engine with Chromium fallback ([#12286](https://github.com/diegosouzapw/OmniRoute/pull/12286)) — thanks @opensource-elearning +- **feat(sse):** full language parity for output styles (es/de/fr/it/ru/zh + autoDetect) ([#12289](https://github.com/diegosouzapw/OmniRoute/pull/12289)) +- **feat(quality):** complete test:scoped — --full map rebuild, stdin selection, CI loader parity (#8084 D1) ([#12353](https://github.com/diegosouzapw/OmniRoute/pull/12353)) +- **feat(providers):** refresh Fable, Cursor, and Devin catalogs ([#12367](https://github.com/diegosouzapw/OmniRoute/pull/12367)) — thanks @backryun +- **feat(video):** redact transcript text from logs and durable memory (#12150 P1) ([#12427](https://github.com/diegosouzapw/OmniRoute/pull/12427)) +- **feat(dashboard):** parent-link, genuine-continuation badge, and modal perf fixes ([#12448](https://github.com/diegosouzapw/OmniRoute/pull/12448)) — thanks @hartmark +- **feat(dashboard):** adaptive context-budget dial on compression panel ([#12488](https://github.com/diegosouzapw/OmniRoute/pull/12488)) — thanks @HouMinXi +- **feat(providers):** refresh Z.ai Web models and browser transport ([#12524](https://github.com/diegosouzapw/OmniRoute/pull/12524)) — thanks @backryun +- **feat(video):** redact raw client-snapshot transcript fields in the detailed log (#12150 P2) ([#12528](https://github.com/diegosouzapw/OmniRoute/pull/12528)) +- **feat(providers):** refresh NVIDIA hosted models ([#12538](https://github.com/diegosouzapw/OmniRoute/pull/12538)) — thanks @backryun +- **feat(quota):** Moonshot Open Platform balance and TPD lock for custom nodes ([#12590](https://github.com/diegosouzapw/OmniRoute/pull/12590)) — thanks @HouMinXi +- **feat(video):** redact transcript in the in-memory pending-request snapshot (#12430 item 6) ([#12596](https://github.com/diegosouzapw/OmniRoute/pull/12596)) +- **feat(dashboard):** link a log entry's Conversation Context to its owning conversation ([#12646](https://github.com/diegosouzapw/OmniRoute/pull/12646)) — thanks @hartmark +- **feat(catalog):** eligibility-gated free-tier bucket ([#12669](https://github.com/diegosouzapw/OmniRoute/pull/12669)) +- **feat(db):** fail-closed previous_response_id continuation for redacted video turns (#12150 P2b) ([#12707](https://github.com/diegosouzapw/OmniRoute/pull/12707)) +- **feat(gamification): pay the documented `streak_bonus` and `badge_unlock` XP rewards.** `XP_REWARDS` listed both rewards but the award pipeline never paid them: the private reward table in `events.ts` omitted them, `updateStreak()` did not report when a streak extended, and badge unlocks carried no XP. Every request that extends a daily streak now pays `streak_bonus × streak length` once per UTC day (guarded by a same-day `xp_audit_log` check), and every badge unlocked through the pipeline pays `badge_unlock` once per badge (guarded by the `user_badges` primary key; `unlockBadge()` now reports whether it inserted). Bonus XP flows through the same `addXp` + level sync + global/weekly/monthly leaderboard path as action XP, so level-ups and rankings include it. The Radar supporter recognition unlock stays XP-free. (#12522 — thanks @pacocartones) +- **feat(providers):** advertise a `free-tier` capability in the provider plugin manifest for every provider with documented free models, so sidecars and dashboards can filter free-capable providers without reading the quota catalog ([#12786](https://github.com/diegosouzapw/OmniRoute/pull/12786)) — thanks @maxmad64bis +- **feat(providers):** Added EURouter as an OpenAI-compatible API-key gateway (`https://api.eurouter.ai/v1`), with live model discovery via `passthroughModels`. Its copy states that models are served by third-party upstreams listed per model, so an EU-based router is not read as EU data residency for inference. ([#13025](https://github.com/diegosouzapw/OmniRoute/pull/13025)) — thanks @ntdatt812 +- **feat(providers):** Added GreenPT as an OpenAI-compatible API-key provider (`https://api.greenpt.ai/v1`), with live model discovery via `passthroughModels`. No free-inference badge: the published docs describe a free API subscription billed per token, not a free tier. ([#13024](https://github.com/diegosouzapw/OmniRoute/pull/13024)) — thanks @ntdatt812 +- **feat(i18n):** 9 new locales — Greek (`el`), Croatian (`hr`), Serbian (`sr`), Lithuanian (`lt`), Estonian (`et`), Latvian (`lv`), Slovenian (`sl`), Maltese (`mt`), Irish (`ga`) — across the dashboard, docs mirrors, CLI, README and the site; OmniRoute now ships every official EU language (51 locales). (#13044) +- **feat(i18n):** 8 new locales — Kannada (`kn`), Malayalam (`ml`), Odia (`or`), Punjabi (`pa`), Nepali (`ne`), Sinhala (`si`), Burmese (`my`), Khmer (`km`) — across the dashboard, docs mirrors, CLI, README and the site (59 locales). The translator now restores the ICU literal escape around angle placeholders and splits oversized docs sections before translating. (#0000) ([#13660](https://github.com/diegosouzapw/OmniRoute/pull/13660)) +- feat(providers): **list Agnes 3.0 Flash as the current free chat model, drop retired 1.5 Flash, add Image 2.0/2.5 Flash plus Video 2.5/2.5 Flash, and discover the live `/v1/models` catalog (including the CN host `api.agnes-ai.cn`).** `agnes-1.5-flash` now forwards to `agnes-3.0-flash`. Video 2.5 polls `GET /v1/videos/{id}` (not the V2.0 `/agnesapi` contract). Live `/v1/models` (2026-09-09) no longer serves 1.5; the wiki marks it deprecated. 3.0 Flash is 512K context / 65,536 max output, same window as 2.5. CN-region keys use the existing per-connection base-URL field, default stays `apihub.agnes-ai.com`. ([#13120](https://github.com/diegosouzapw/OmniRoute/pull/13120)) — thanks @HouMinXi +- **feat(sse):** Codex and OpenAI catalogs list GPT-6 Astra with effort aliases (`-low` through `-ultra`); Codex CLI identity pins `@openai/codex@0.153.4` in lockstep with the image ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **feat(dashboard):** the orchestration History tab gained a "Compare runs" mode — toggling it turns each grid cell into a 2-item selection queue (a 3rd click drops the oldest pick), and picking two cells opens a side-by-side comparison panel instead of the usual detail drawer. The panel fetches both runs' detail the same way the drawer does (falling back to persisted history once a run leaves the live TTL window) and shows, per side: identity/source/state, start time, a signed `right - left` delta for duration/cost/event count, the event timeline aligned by index, and any memory hits. One side's fetch failing never blocks the other, and a delta is only ever computed when both sides have a finite value — otherwise it renders "—", never `NaN`. Comparing two runs from different sources or skills still works; a banner marks the deltas as informational rather than hiding them, since the two runs aren't a strict apples-to-apples pair. ([#12677](https://github.com/diegosouzapw/OmniRoute/pull/12677)) +- **feat(dashboard):** badge a conversation that never reached a clean stop ([#12717](https://github.com/diegosouzapw/OmniRoute/pull/12717)) — thanks @hartmark +- **feat(release):** reconcile-changelog tool + version-anchored fragment aggregation ([#12987](https://github.com/diegosouzapw/OmniRoute/pull/12987)) +- **feat(mcp):** carry context_length through MCP list_models_catalog projection (#12776) ([#13406](https://github.com/diegosouzapw/OmniRoute/pull/13406)) — thanks @KooshaPari +- **feat(cli):** make startup readiness budget configurable (#13369) ([#13433](https://github.com/diegosouzapw/OmniRoute/pull/13433)) — thanks @KooshaPari + ### 🐛 Bug Fixes +- **security(streaming):** sanitize generic mid-stream error messages before emitting OpenAI, + Responses, or Claude SSE failure frames and before diagnostic logging, while preserving raw + failures for internal classification and keeping client disconnects out of provider failure state. ([#12457](https://github.com/diegosouzapw/OmniRoute/pull/12457)) +- **fix(claude):** `blockExtraUsage: false` no longer lets 5h quota preflight skip the account; extra usage is billed after the session bar is gone, so the request must reach Anthropic ([#12803](https://github.com/diegosouzapw/OmniRoute/pull/12803)) — thanks @HouMinXi +- Sanitize HuggingChat conversation-creation and message-send transport failures before they reach client error bodies or provider logs. ([#12467](https://github.com/diegosouzapw/OmniRoute/pull/12467)) +- **fix(dashboard):** Keep local and theme-aware provider SVG icons at a definite layout size so Chromium does not collapse them to 0×0 after the v3.8.50 image-rendering change ([#12054](https://github.com/diegosouzapw/OmniRoute/pull/12054)) — thanks @ponkcore +- **fix(security):** Sanitize provider and runtime failures before public API, SSE and MCP responses and before persistent request, proxy and usage logs, preventing credentials, stack traces and host filesystem paths from crossing those boundaries while preserving stable error codes and useful diagnostics. ([#12506](https://github.com/diegosouzapw/OmniRoute/pull/12506)) +- **fix(cli):** `omniroute tunnel create` no longer crashes with `Cannot read properties of undefined (reading optsWithGlobals)` — removed the duplicate positional argument that caused Commander.js to misalign the action callback parameters ([#12295](https://github.com/diegosouzapw/OmniRoute/issues/12295)) ([#12368](https://github.com/diegosouzapw/OmniRoute/pull/12368)) — thanks @gonisulaimann +- Stop advertising Gemini Live-only models as supported audio endpoints until OmniRoute proxies the bidirectional Live protocol. ([#11710](https://github.com/diegosouzapw/OmniRoute/pull/11710)) — thanks @RaviTharuma +- **fix(kie):** reroute `flux/kontext` off the KIE Market `createTask` flow — it is catalogued with `isMarket: true` but has no Market catalog page, so KIE rejected it with "model name not supported"; it now hits the dedicated `POST /api/v1/flux/kontext/generate` / `GET /api/v1/flux/kontext/record-info` endpoints instead (#11296). ([#11985](https://github.com/diegosouzapw/OmniRoute/pull/11985)) — thanks @hartmark +- **fix(kie):** correct 12 more KIE Market catalog ids that were sent to `createTask` unchanged but diverge from KIE's documented upstream `model` values — GPT Image 2 T2I/I2I (drops the `gpt/` prefix), GPT Image 1.5 T2I/I2I (`gpt-image/` namespace), Seedream 5.0 Lite T2I/I2I (drops the `.0`), all 4 Flux 2 variants (`flux-2/` namespace, generic variant renamed `flex`), and Wan 2.7 Image / Image Pro (dash instead of dot) — each verified individually against the literal example request published on docs.kie.ai. `#11326`'s "everything else already matches" claim was wrong a second time (#11296); `z-image/4.0-*`/`z-image/4.5-*` and `flux/kontext` remain open, documented as unresolved in `KIE_MARKET_UPSTREAM_MODEL_IDS`'s comment pending further verification. ([#11667](https://github.com/diegosouzapw/OmniRoute/pull/11667)) +- **fix(dashboard):** `useApiKeySave.handleSaveApiKey` no longer forces a full upstream `/models` catalog sync on every non-curated provider connection save — callers can now pass `skipModelSync: true` to opt out, so a workflow that only wants to add one manual model no longer floods the provider's available-models list with hundreds/thousands of synced entries. The flag is a client-side intent signal only and is stripped before the connection payload is POSTed to `/api/providers`; default behavior (full sync on save) is unchanged when the flag is omitted (#11324) ([#11664](https://github.com/diegosouzapw/OmniRoute/pull/11664)) +- **Provider connections:** keep `tokenExpiresAt` when a connection is created. The create-path allowlist omitted it, so every insert stored NULL and the dashboard token badge could read a fresh connection as expired until its first background refresh ([#11368](https://github.com/diegosouzapw/OmniRoute/pull/11368)). — thanks @ntdatt812 +- **fix(combos):** the combo builder's precision-select, global-model-search, and manual-entry flows now serialize a model step's `model` string using the provider's already-computed routing-alias prefix (e.g. `oc/`) instead of rebuilding it from the raw canonical `providerId`, fixing the no-auth "OpenCode Free" provider (`opencode`) being routed to the unrelated paid "OpenCode Zen" provider (`opencode-zen`) because `opencode` doubles as a manual routing-prefix override ([#11433](https://github.com/diegosouzapw/OmniRoute/issues/11433)). ([#11670](https://github.com/diegosouzapw/OmniRoute/pull/11670)) +- **fix(radar):** the catalog feed cache now keeps `generatedAt`, the date the feed's data was built, next to `fetchedAt`, the date this install downloaded it (#11435). The feed schema requires that date and the sync path validates it, but the cache dropped it — so a feed fetched minutes ago and one carrying weeks-old figures looked identical to everything downstream, including the dashboard's "Last fetched" line. `getRadarCatalog().meta` and `GET /api/radar/status` now report both dates, the latter as its own field rather than folded into `version` — and omitted entirely for the offers and intel caches, which keep no build date, where a `null` would read as "unknown" rather than "never stored". The dashboard still shows only the fetch time; surfacing the build date there needs a new translated label and is left to a follow-up. Rows cached before migration 163 read back as `null`: unknown stays unknown instead of borrowing the fetch time. The referrals cache has persisted the same date since migration 142. — thanks @maxmad64bis +- **fix(cli):** postinstall no longer fills `JWT_SECRET` and `API_KEY_SECRET` in the installed package's `.env` (#11436). `.env.example` ships both blank on purpose: the server restores them from its durable store, or generates and persists them there, in `ensureSecrets()`. Pre-filling them defeated that — the file lives inside the package directory, so `npm i -g` replaced it and postinstall wrote _different_ values, while `ensureSecrets()` (which only acts on an empty variable) never got to restore the real ones. Both secrets rotated silently on every update, invalidating dashboard sessions and API-key CRCs. `STORAGE_ENCRYPTION_KEY` left the same list for the same reason in #1622; its comment pointed at a function that no longer exists and now names the real provisioning path. — thanks @maxmad64bis +- **fix(cli):** the CLI now says when a loaded `.env` lives inside the installed package directory (#11437). It already announces every env file it reads, without distinguishing the ones that survive an update from the one that does not: `npm i -g` replaces the package directory wholesale, so values set there are gone at the next update, silently. The warning names the durable path to move them to, and fires only when that file actually supplied a value — a file entirely shadowed by a durable one supplied nothing. A development checkout stays silent: there the same path is stable and documented in `SETUP_GUIDE.md`. — thanks @maxmad64bis +- **fix(free-models):** the shared `isFreeModel()` predicate no longer reports catalog entries marked `freeType: "discontinued"` as free, so `hidePaidModels` can't route to Pollinations' seven premium models that now require a paid key ([#11441](https://github.com/diegosouzapw/OmniRoute/pull/11441)) — thanks @maxmad64bis +- fix(ui): let AnySearch use the normal provider-icon fallback when LobeHub has no matching icon (#11449) +- Fixed the v3.8.50 Costs and Analytics dashboards so flat-rate Claude Code usage can be shown as an explicitly requested token-price estimate without changing default billed-cost semantics. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed archived usage retention so each request is priced individually instead of pricing a day's summed tokens once, which understated archived cost whenever a day mixed cache-heavy and ordinary requests. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed the Costs dashboard so it discloses when displayed figures include flat-rate token-price estimates instead of labelling them as billed spend, using the flag the analytics API already returns; the month-end projection and the CSV/JSON exports carry the same marker, and billed-cost mode is unchanged. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- **fix(combo):** attach the same combo-diagnostics trace (`poolSize`/`attemptOrder`/`excluded`/`terminalReason`, plus `x-omniroute-combo-*` headers) to the round-robin strategy's and the nested pipeline/fusion runtime-unit loop's "Maximum combo retry limit reached" 503 that the priority-strategy path already attaches for the identical terminal condition — previously those two paths returned a bare, contextless 503 ([#11462](https://github.com/diegosouzapw/OmniRoute/issues/11462)). ([#11665](https://github.com/diegosouzapw/OmniRoute/pull/11665)) +- **fix(bun):** use native `bun:sqlite` in `bootstrap-env` and `sync-env` to avoid loading `better-sqlite3` N-API addon during Bun startup ([#11468](https://github.com/diegosouzapw/OmniRoute/pull/11468)) — thanks @TheDemonTuan +- **fix(docker-bun):** make `Dockerfile.bun` install reliable with pre-install script helpers and native `bun:sqlite` smoke check ([#11470](https://github.com/diegosouzapw/OmniRoute/pull/11470)) — thanks @TheDemonTuan +- **fix(docker-bun):** remove every vendored `better-sqlite3` native addon from the Bun runtime image so startup cannot abort before the `bun:sqlite` fallback ([#11482](https://github.com/diegosouzapw/OmniRoute/pull/11482)) — thanks @TheDemonTuan +- **fix(autoCombo):** effort/alias model variants (`gpt-5.6-sol-xhigh`, `gpt-5.6`, cursor's `claude-4.6-opus-high`) inherit their base model's task fitness instead of falling to the wildcard 0.5 ([#11492](https://github.com/diegosouzapw/OmniRoute/pull/11492)) — thanks @MumuTW +- fix(db): rate-limit repeated Arena ELO leaderboard fetch-failure warnings instead of logging one per sync attempt (#11500) ([#11989](https://github.com/diegosouzapw/OmniRoute/pull/11989)) — thanks @hartmark +- **fix(db):** dedupe the raw `[Encryption] Decryption failed...` log line emitted by the lazy-decrypt views (`createLazyRowProxy`/`createLazyConnectionView`), which power `getProviderConnections()` and were re-triggering that line on every CredentialHealth/model-sync cycle for the same corrupt or stale-key credential — a fresh Proxy over a fresh row on every cycle meant the per-proxy memoization never suppressed it, unlike the dedup `decryptConnectionFields()` already had since [#9927](https://github.com/diegosouzapw/OmniRoute/issues/9927). Now shares that dedupe tracking so the line logs at most once per credential ([#11500](https://github.com/diegosouzapw/OmniRoute/issues/11500)). ([#11678](https://github.com/diegosouzapw/OmniRoute/pull/11678)) +- **fix(intelligence):** Arena ELO sync stores a synthesized base-model row for every effort/harness variant (`claude-opus-5-max` → `claude-opus-5`, `gpt-5.6-sol-xhigh (codex-harness)` → `gpt-5.6-sol`), so bare model requests reach the synced intelligence layer instead of the static fallback table; the hand-maintained `MODEL_ALIAS_MAP` — which collapsed generations (`gpt-5.5` → `gpt-5`) — is gone ([#11506](https://github.com/diegosouzapw/OmniRoute/pull/11506)) — thanks @MumuTW +- **fix(autoCombo,sse):** catalog hygiene — the static task-fitness table no longer ranks retired models above live ones (dead rows dropped, patterns matched on segment boundaries), the 7 deprecation aliases that forwarded to retired or non-existent models now point at the vendors' published replacements, an alias is no longer rewritten when the serving provider offers the id as-is, and a new `check:model-lifecycle` gate diffs both tables against a vendor lifecycle snapshot ([#11507](https://github.com/diegosouzapw/OmniRoute/pull/11507)) — thanks @MumuTW +- **fix(sse):** stop dropping resolved `thoughtSignature` values on parallel (multi tool-call) turns sent to Gemini 3.x — the claude→gemini and openai→gemini translators previously kept the signature only on the *first* function call of a message, causing Gemini to reject subsequent calls in the same turn with HTTP 400 "Function call is missing a thought_signature"; each function call now keeps its own resolved signature ([#11510](https://github.com/diegosouzapw/OmniRoute/issues/11510)). ([#11669](https://github.com/diegosouzapw/OmniRoute/pull/11669)) +- Treat Docker publishing from a non-default release branch as an expected skip instead of a failed workflow, while preserving the guard that prevents it from overwriting the `next` channel. ([#11527](https://github.com/diegosouzapw/OmniRoute/pull/11527)) — thanks @pacocartones +- fix(search): prefer credentialed providers over duckduckgo-free fallback (#11524) — the fallback-only loop ran before the credentialed-providers loop in `executeWebSearch`, making configured providers unreachable when `duckduckgo-free` was available. ([#11565](https://github.com/diegosouzapw/OmniRoute/pull/11565)) — thanks @vsd2807 +- **fix(sse):** cap the upstream headers-wait phase for STREAMING requests to a client-realistic ceiling (110s, under Codex's own ~120s hard client-abort window) instead of the flat 10-minute `FETCH_TIMEOUT_MS` default — that default was 5x longer than the body-phase readiness watchdog's own adaptive bound, so a request whose upstream never returned any response at all (not even headers, e.g. a stalled NVIDIA target behind a tool-heavy Responses→Chat translation) kept the client connection alive on keepalives only, guaranteeing the client's own patience ran out first with an opaque 499 instead of OmniRoute detecting and failing the stall fast. Non-streaming requests are unaffected — they keep the existing flat default (`open-sse/utils/fetchStartTimeoutPolicy.ts`) (#11526) ([#11668](https://github.com/diegosouzapw/OmniRoute/pull/11668)) +- **fix(ci):** protocol-clients E2E harness boots the peer-stamped custom server (`run-next.mjs dev`) instead of the bare `next` CLI, so `LOCAL_ONLY` locality resolves from the real TCP peer stamp and `/api/mcp/audit` answers 200 (open bootstrap preserved via an `OMNIROUTE_E2E_BOOTSTRAP_MODE=open` credential clear in `run-next.mjs`, pinned to loopback `HOST`) instead of a deterministic 403 (#11535) ([#11549](https://github.com/diegosouzapw/OmniRoute/pull/11549)) — thanks @jonlwheat2-gif +- **fix(docs):** the Auto-Combo engine is described with the number of factors it actually declares. Four different counts were in circulation — 6, 9, 13 and 14 across `AGENTS.md`, four guides and six code comments — while `DEFAULT_WEIGHTS` declares 15. `check:docs-counts` now reads that number from the source, so adding a factor without updating the prose fails CI ([#11545](https://github.com/diegosouzapw/OmniRoute/pull/11545)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **fix(quota-share):** in-flight leases are per request, so an aborted request's slot ages out instead of being kept alive by later traffic on the same connection ([#11547](https://github.com/diegosouzapw/OmniRoute/pull/11547)) — thanks @abhisheksharma2411 +- The free-tier summary route now serves the refreshed Radar catalog when the feed is active — the same numbers the dashboard shows — and states which catalog answered plus its real build date, instead of always reporting release-frozen figures with a stale curation date. The supporter-key live feed stays reserved to authenticated callers of the instance. ([#11550](https://github.com/diegosouzapw/OmniRoute/pull/11550)) — thanks @maxmad64bis +- **sse:** fix LiveWS/embed-WS servers crashing at startup under the Node/tsx runtime — `liveServer.ts`, `embedWsProxy.ts` and `apiBridgeServer.ts` imported `@/shared/utils/httpClientAbortGuard` without the `.mjs` extension, so the client-abort crash guard added by [#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556) was unreachable and every dependent test failed with `ERR_MODULE_NOT_FOUND` ([#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556)). ([#11671](https://github.com/diegosouzapw/OmniRoute/pull/11671)) — thanks @jonlwheat2-gif / @yxyxy +- fix(chat): shed any of the matched provider-node's routing identifiers (public prefix or internal id) before model lookup, so composites like `//` no longer reach upstream verbatim (#11557) — thanks @maxmad64bis +- **fix(cache):** a semantic-cache entry now expires on its TTL instead of surviving until the next UTC midnight, and `dbEntries` stops counting expired rows ([#11573](https://github.com/diegosouzapw/OmniRoute/pull/11573)) — thanks @ntdatt812 +- **fix(api):** `/v1/models` schedules its stale-while-revalidate rebuild through Next's `after()` again, so a stale catalog reaches the client before the rebuild blocks the event loop ([#11574](https://github.com/diegosouzapw/OmniRoute/pull/11574)) — thanks @ntdatt812 +- **fix(check):** the `known-symbols` executor conformance gate awaits the now-async `getExecutor()`, so it stops reporting all 142 registered aliases as dead symbols and can detect a lazy import that fails ([#11582](https://github.com/diegosouzapw/OmniRoute/pull/11582)) — thanks @ntdatt812 +- **fix(pack):** register `bin/cli/utils/volatileEnvPath.mjs` in `PACK_ARTIFACT_REQUIRED_PATHS`, so the prune can no longer silently drop a module the published CLI imports on every boot ([#11588](https://github.com/diegosouzapw/OmniRoute/pull/11588)) — thanks @ntdatt812 +- **i18n(vi):** translate the five reliability-column strings on the free-provider rankings page, which rendered as `__MISSING__:` markers in the Vietnamese UI ([#11589](https://github.com/diegosouzapw/OmniRoute/pull/11589)) — thanks @ntdatt812 +- **resilience:** restore the expired-connection retry-budget probe in the token-health sweep — the `!isGitHubAccessTokenOnlyConnection` carve-out reintroduced by #11608 contradicted the boundary pinned by #11592, so a GitHub connection parked at `expired` with retry budget remaining was never probed and could never self-heal ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)). ([#11672](https://github.com/diegosouzapw/OmniRoute/pull/11672)) — thanks @jonlwheat2-gif / @ntdatt812 +- **fix(dashboard):** Provider Stats now displays combo success rates on their native 0–100 percentage scale instead of multiplying them twice ([#11595](https://github.com/diegosouzapw/OmniRoute/pull/11595)) — thanks @pacocartones +- **fix(providers):** route DuckDuckGo Web Chat through the verified `duck.ai` endpoint, refresh its free model catalog, and avoid forwarding unsolved anti-abuse challenges ([#11597](https://github.com/diegosouzapw/OmniRoute/pull/11597)) — thanks @solstxce +- **fix(ui):** Console log Refresh and Copy controls now expose localized accessible names, keep copy actions visible on keyboard focus, and announce copy completion safely ([#11599](https://github.com/diegosouzapw/OmniRoute/pull/11599)) — thanks @pacocartones +- Dropped the stale-`.eslintcache` `restore-keys` fallback from both "Restore ESLint file cache" steps in `ci.yml`, so the blocking `Lint` job can no longer be served per-file verdicts computed under a different lint config, suppressions file or lockfile. `quality.yml` had already dropped it in #11963; `ci.yml` — the workflow that actually gates PRs — had not (#11600). ([#11996](https://github.com/diegosouzapw/OmniRoute/pull/11996)) +- fix(ci): pass `--pass-on-unpruned-suppressions` in `run-eslint-json.mjs` so the CI Lint job no longer fails when a suppression is merely orphaned by a genuine fix, mirroring the identical fix already in `validate-release-green.mjs` (#11600) ([#11983](https://github.com/diegosouzapw/OmniRoute/pull/11983)) — thanks @hartmark +- fix(handoff): enforce provider allowlist for universal handoff (#11602) — universal handoff now skips summarization when the selected summary model's provider is not included in the configured provider allowlist. ([#11619](https://github.com/diegosouzapw/OmniRoute/pull/11619)) — thanks @vsd2807 +- **fix(dashboard):** Show a stable error state when Search Analytics returns an HTTP error or malformed data ([#11603](https://github.com/diegosouzapw/OmniRoute/pull/11603)) — thanks @pacocartones +- **test(gamification):** pin the aggregate profile level to the XP-derived semantics of #11604 — `getAggregateXp()` now derives `currentLevel` from the summed XP (`calculateLevel(sum)`), not `MAX(stored current_level)`, and the #3484 fixture levels are aligned with the XP curve ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)). ([#11676](https://github.com/diegosouzapw/OmniRoute/pull/11676)) — thanks @jonlwheat2-gif / @pacocartones +- **fix(dashboard):** Keep the Profile level and progress aligned with aggregate XP, including bounded handling for invalid totals ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)) — thanks @pacocartones +- **fix(dashboard):** Prevent locked hidden badges from revealing their icon or opening private badge details before they are earned ([#11605](https://github.com/diegosouzapw/OmniRoute/pull/11605)) — thanks @pacocartones +- **fix(dashboard):** Restore keyboard focus after shared modals close and cancel delayed autofocus during cleanup ([#11607](https://github.com/diegosouzapw/OmniRoute/pull/11607)) — thanks @pacocartones +- **fix(dashboard):** Enable Enter and Space activation for clickable data-table rows without hijacking nested controls ([#11610](https://github.com/diegosouzapw/OmniRoute/pull/11610)) — thanks @pacocartones +- **fix(autoCombo,sse):** vendor-retired catalog ids are dropped from the auto-combo candidate pool and no longer win on leftover `arena_elo` / `user_override` rows; `getModelLifecycleDecision` consults `model-lifecycle.json` (prefix-stripped) so aggregator traffic is not `untracked` for ids the snapshot already knows ([#11625](https://github.com/diegosouzapw/OmniRoute/issues/11625)) ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)) — thanks @MumuTW +- **test(chatcore):** move Codex/Claude combo fixtures off models the lifecycle guard now rejects — `gpt-5.1-codex`/`gpt-5-codex` are vendor-retired (snapshot, #11626) and `claude-3-5-sonnet-20241022` is shut down, so native-passthrough and combo-fallback tests switched to `gpt-5.6-sol` and `claude-sonnet-4.6` ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)). ([#11675](https://github.com/diegosouzapw/OmniRoute/pull/11675)) — thanks @jonlwheat2-gif / @MumuTW +- fix(opencode-plugin): stop a free-tier budget that rounds up across a magnitude from rendering as `1000K`/`1000M` in the model picker — `fmtTokens` chose its unit from the raw token count and then rounded with `toFixed(1)`, so 999,950–999,999 printed as `1000K` rather than `1M` and just under a billion printed as `1000M` rather than `1B` ([#11684](https://github.com/diegosouzapw/OmniRoute/pull/11684)) — thanks @abhisheksharma2411 +- Cap prose-derived Antigravity quota resets at the configured model cooldown maximum while preserving authoritative Retry-After headers and Google RetryInfo hints. (#11695) Thanks @pacocartones. ([#11823](https://github.com/diegosouzapw/OmniRoute/pull/11823)) +- Compare `_rebaseline_*` notes structurally in the ratchet-bank verifier so an object-valued note no longer reports itself as rewritten on an unmodified tree — the nightly `bank-ratchet-shrinks` lane had been aborting on every run since 2026-08-11, leaving 41,672 lines of earned file-size shrinks unbanked (#11702). ([#11892](https://github.com/diegosouzapw/OmniRoute/pull/11892)) — thanks @MumuTW +- **fix(build):** `prepublish.ts` bundles the ChatGPT Web (Codex) MCP bridge through `runBuildTool()` instead of spawning `npx.cmd` raw, fixing the build crash on Node ≥ 20/Windows where `.cmd` shims cannot be spawned without a shell (EINVAL) ([#11704](https://github.com/diegosouzapw/OmniRoute/issues/11704)) ([#11706](https://github.com/diegosouzapw/OmniRoute/pull/11706)) — thanks @jonlwheat2-gif +- fix(codex): keep `parallel_tool_calls:false` on the translated Codex Responses Lite path (#11707) ([#11984](https://github.com/diegosouzapw/OmniRoute/pull/11984)) — thanks @hartmark +- **fix(tests):** the new Gemini Web Images retirement test (#11708) called the now-async `getExecutor()` synchronously, throwing `TypeError: getExecutor(...).getProvider is not a function` instead of asserting Gemini Web chat stays available. Awaited to match the R0.3 executor-registry refactor (#11220). ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(search):** Allow REST and MCP web-fetch requests to select the advertised `anysearch-search` provider explicitly ([#11716](https://github.com/diegosouzapw/OmniRoute/pull/11716)) — thanks @pacocartones +- **fix(providers):** Keep overnight peak-hour protection active after midnight according to the window's configured start day ([#11718](https://github.com/diegosouzapw/OmniRoute/pull/11718)) — thanks @pacocartones +- **fix(providers):** correct all five g4f.space gateway descriptions to document proof-of-work credits or member API keys, variable limits, and the remote prompt boundary instead of promising free keyless access at five requests per minute ([#11738](https://github.com/diegosouzapw/OmniRoute/pull/11738)). +- **fix(db):** `model_capabilities` is created by a migration instead of lazily on the first models.dev sync, so a clean install and an upgraded install converge on the same schema regardless of which features have run ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(ci):** `check:install-upgrade` now fails on an `npm` install truncated by ENOSPC (npm reports it as a warning and still exits 0), authenticates its health probe so the version assertion works against the hardened health payload, frees the clean-install tree before the upgrade phase, and no longer reports a schema divergence computed from a boot that never served ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(routing):** harden session-affinity key derivation against oversized explicit IDs and empty structural-payload collisions, while recognizing Gemini and common text request shapes ([#11746](https://github.com/diegosouzapw/OmniRoute/pull/11746)) — thanks @MumuTW +- **fix(packages/browser-pool):** regenerate `package-lock.json` so the `packages/browser-pool` workspace's locked `playwright`/`@types/node` (and transitives) match its `package.json` specs, fixing cache-only/offline installs (`npm ci --offline`, Nix `buildNpmPackage`) that previously failed with `ENOTCACHED` ([#11747](https://github.com/diegosouzapw/OmniRoute/issues/11747)) — thanks @benjaminkitt ([#11784](https://github.com/diegosouzapw/OmniRoute/pull/11784)) — thanks @geekyNads +- **fix(assets):** Stop distributing 79 provider and CLI logos without verified source/license provenance, keep every provider/tool available through the internal generic icon, and prevent those IDs from falling through to an external icon CDN. ([#11750](https://github.com/diegosouzapw/OmniRoute/pull/11750)) +- **fix(providers):** retire the common `chatgpt-web` / `cgpt-web` provider because its pre-key and proof-of-work provenance could not be cleared ([#11754](https://github.com/diegosouzapw/OmniRoute/pull/11754)) — explicit chat and image requests, slash-prefixed models, persisted aliases, proxy overrides, and combo targets now fail closed with a sanitized HTTP `410` / `PROVIDER_RETIRED` before upstream dispatch. Migration 163 tombstones matching connections and invalidates their active leases without deleting history or widening API-key allowlists. The independent `chatgpt-web-codex` / `cgpt-codex` provider and its MIT-noticed vendored implementation remain available. +- **fix(usage):** quota and usage refresh no longer 409 when an exclusive lease reserves the connection ([#11758](https://github.com/diegosouzapw/OmniRoute/pull/11758)) — thanks @TheDemonTuan +- Keep the embedding registry's vector width and `embedding` type on models when a synced model exists for the same id, so `/v1/models` no longer reports registry-described embedding models widthless or untyped (#11761) — thanks @rahilmavani +- Correct `google/gemini-embedding-001` on the OpenRouter route to 3072 dimensions, the width it returns when `dimensions` is not sent (#11761) — thanks @rahilmavani +- **fix(vision-bridge):** Restrict automatic vision routing and fallbacks to models available in authoritative live provider catalogs, including aliases and effort variants ([#11767](https://github.com/diegosouzapw/OmniRoute/issues/11767)) — thanks @pacocartones ([#11813](https://github.com/diegosouzapw/OmniRoute/pull/11813)) +- **fix(providers):** reclassify Cerebras as a one-time $5 signup credit (payment method required, 30-day validity), not a recurring no-card 1M tokens/day trial ([#11773](https://github.com/diegosouzapw/OmniRoute/issues/11773)) ([#12591](https://github.com/diegosouzapw/OmniRoute/pull/12591)) — thanks @HouMinXi +- fix(cli): stop prepublish from re-rebuilding the already-built ESM-only opencode-plugin dist (#11787) ([#11990](https://github.com/diegosouzapw/OmniRoute/pull/11990)) — thanks @hartmark +- **fix(db):** Fresh installs no longer log a non-fatal `no such table: compression_run_telemetry` warning when retention cleanup runs before the lazily-created telemetry table exists ([#11802](https://github.com/diegosouzapw/OmniRoute/pull/11802)) — thanks @RaviTharuma +- fix(sse): set X-OmniRoute-Selected-Connection-Id on successful combo dispatches so downstream consumers stop falling back to an empty connection id (#11810) ([#11986](https://github.com/diegosouzapw/OmniRoute/pull/11986)) — thanks @hartmark +- **fix(services):** embedded CLIProxyAPI lifecycle checks now use public `/healthz`, while model discovery uses the configured dedicated data-plane API key instead of the management password ([#11811](https://github.com/diegosouzapw/OmniRoute/pull/11811)) — thanks @RaviTharuma +- Fix persisted model steps returning an error in the playground route simulator, and warn when structural steps cannot be simulated. ([#11862](https://github.com/diegosouzapw/OmniRoute/pull/11862)) — thanks @pacocartones +- **fix(providers):** Antigravity's dynamic mitmAlias table no longer routes `gemini-3.7-flash-{high,medium,low}` to a literal tier-suffixed upstream id just because one connected account's own discovery listed it directly — those display ids always resolve through the safe `gemini-3.7-flash-tiered` static alias, so one account's Google-provisioned access no longer 404s every sibling account of the provider ([#11824](https://github.com/diegosouzapw/OmniRoute/issues/11824), [#11651](https://github.com/diegosouzapw/OmniRoute/issues/11651)) ([#11988](https://github.com/diegosouzapw/OmniRoute/pull/11988)) — thanks @hartmark +- **fix(dashboard):** Expose Profile loading, errors, page structure, and XP progress to assistive technologies ([#11838](https://github.com/diegosouzapw/OmniRoute/pull/11838)) — thanks @pacocartones +- **fix(socks):** `Agent.connectTimeout` now bounds both SOCKS handshake and TLS connect, and `family === null` no longer falls back to `fetch-socks` ([#11842](https://github.com/diegosouzapw/OmniRoute/pull/11842)) — thanks @maxmad64bis +- **perf(compression):** OOM mitigations for large payload hashing, memoization, and token estimation ([#11844](https://github.com/diegosouzapw/OmniRoute/pull/11844) — thanks @AndrianBalanescu) +- Excluded Next.js Node File Trace manifests (`*.nft.json`) from the published npm tarball. They are build-time metadata and are never read while serving, but had grown to 668.7 MB — 61% of the package — which pushed the upload past the registry limit and made `npm publish` fail with `413 Payload Too Large`. ([#11864](https://github.com/diegosouzapw/OmniRoute/pull/11864)) +- **fix(cli):** keep the global OmniRoute server `--base-url` separate from provider-node `--endpoint` values in add, update, and validate commands ([#11860](https://github.com/diegosouzapw/OmniRoute/pull/11860)) — thanks @pacocartones +- **fix(config):** Nous Research's `Hermes-4-405B` model now displays as "Hermes 4 405B (Nous Research)" in both the provider registry and the free-model catalog, instead of the mislabelled "Hermes 4 7B" ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#11993](https://github.com/diegosouzapw/OmniRoute/pull/11993)) +- **fix(provider/nous):** inject required user= tag into Nous Research inference requests to resolve upstream 400 "missing tags" error ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#12044](https://github.com/diegosouzapw/OmniRoute/pull/12044)) +- **fix(build):** `npm run build` now fails in one second with a named package and a copy-pasteable fix when npm silently drops an externalised optional native dependency, instead of dying four minutes in with `Module not found: Can't resolve 'better-sqlite3'` ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- **fix(install):** `postinstall` no longer throws `ReferenceError: isAndroid is not defined` — failing the whole `npm install` — when the `better-sqlite3` rebuild fallback times out; the manual-fix guidance is reachable again ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- Fixed the Alibaba free-tier allowlist test that went red on its own once the shipped catalog's `validUntil` (2026-08-27) passed, leaving every PR and `main` with a failing `Unit Tests (1/8)`. The test now builds its own packs with dates it controls, and covers the expired-pack fallback that production has actually been serving. ([#11867](https://github.com/diegosouzapw/OmniRoute/pull/11867)) +- Split the npm registry upload into its own GitHub-hosted job. npm refuses `--provenance` from a self-hosted runner (`422 ... Only "github-hosted" runners are supported`), which blocked the v3.8.50 publish; the heavy verification cannot move to a hosted runner, so it now hands the proven tarball over instead. ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(ui):** the shared `DataTable` loading state no longer reads its decorative ⏳ glyph out to assistive technology, and now carries the same `role="status"` / `aria-live="polite"` / `aria-busy="true"` semantics as `PageLoading` ([#11869](https://github.com/diegosouzapw/OmniRoute/pull/11869)) — thanks @pacocartones +- **fix(api):** Bound the `timeout` accepted by `POST /api/providers/[id]/login` to the same 15s–600s window the Adobe Firefly and Conol browser-login paths already enforce, so a request can no longer pin a headful browser and the single active-login slot with an arbitrarily large poll budget. ([#11871](https://github.com/diegosouzapw/OmniRoute/pull/11871)) — thanks @pacocartones +- **fix(api):** Stop the tunnel and MITM routes from returning raw child-process error text, which disclosed host paths, binary install locations and Tailscale `tskey-*` credentials in a body some of these routes serve to non-loopback callers, and return a real 400 instead of a framework 500 when a tunnel request body fails validation. ([#11872](https://github.com/diegosouzapw/OmniRoute/pull/11872)) — thanks @pacocartones +- **fix(open-sse):** stream TTFT/ITL are sampled from a monotonic clock (`performance.now`) instead of `Date.now`, so an NTP correction or wall-clock jump can no longer inflate a genuine TTFT or produce a negative one that the `ttft >= 0` guard silently drops — protecting the router's OTel/EWMA/`usage_history`/speed-ranking signals ([#11873](https://github.com/diegosouzapw/OmniRoute/pull/11873)) — thanks @pacocartones +- **fix(providers):** xAI `reasoning_effort: "xhigh"` now reaches grok-4.6+ instead of being silently clamped to `"high"` ([#11879](https://github.com/diegosouzapw/OmniRoute/pull/11879)) — thanks @NoxzRCW +- **fix(dashboard):** the providers page no longer crashes into the error boundary when a provider id collides with an `Object.prototype` member (`constructor`, `__proto__`); icon lookups are own-property guarded ([#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)) — thanks @NoxzRCW +- **fix(skills):** injected skill tools declared in shorthand (`{"content": "string"}`) now forward valid JSON Schema, unblocking providers that validate tool schemas strictly such as Zhipu GLM on the Console Go tier ([#11881](https://github.com/diegosouzapw/OmniRoute/pull/11881)) — thanks @NoxzRCW +- **fix(api):** `POST /api/playground/simulate-route` now surfaces `combo-ref` and `provider-wildcard` persisted combo steps with a specific warning (naming the referenced combo, or the unresolved `provider/model` wildcard) instead of folding them into a generic "unsupported step" count; a `provider-wildcard` step is also now included as an unresolved target so the operator can see it is in the route (ported from [#11882](https://github.com/diegosouzapw/OmniRoute/pull/11882) — thanks @NoxzRCW). ([#11930](https://github.com/diegosouzapw/OmniRoute/pull/11930)) +- **fix(translator):** the streaming OpenAI→Claude translator keeps upstream usage, including prompt-cache tokens, when it arrives on a trailing `choices: []` chunk (Fireworks and any upstream using `stream_options.include_usage`) ([#11883](https://github.com/diegosouzapw/OmniRoute/pull/11883)) — thanks @NoxzRCW +- **fix(cli):** `omniroute update --apply` now tells you explicitly whether a running server was detected and, if so, that you must run `omniroute restart` to apply the update — it never restarted anything and previously implied the update was already live once files were installed. The dashboard's npm-mode Update flow (`/api/system/version`) now tries OmniRoute's own PID-file-managed supervisor before falling back to pm2, and reports an honest "restart required" step instead of a silent pm2-only "skipped" that read like a completed update. The server-side latest-version lookup backing the dashboard's update banner also gained `--prefer-online`, closing the same stale-npm-cache class already fixed in the CLI's own copy for #4376 ([#11885](https://github.com/diegosouzapw/OmniRoute/issues/11885)). ([#12005](https://github.com/diegosouzapw/OmniRoute/pull/12005)) +- **fix(resilience):** clear persisted LKGP pins when a target suffers connection/provider exhaustion or is skipped before dispatch due to cooldown/exhaustion/unavailability, preventing subsequent requests from repeatedly prioritizing known-dead providers ([#11911](https://github.com/diegosouzapw/OmniRoute/issues/11911)). ([#12013](https://github.com/diegosouzapw/OmniRoute/pull/12013)) — thanks @HouMinXi +- **fix(sse):** the OpenAI→Claude stream translator now defers the terminal `message_delta`/`message_stop` emission until the real usage block has arrived (or a genuine end-of-stream flush forces it) instead of emitting it immediately on `finish_reason` — previously, when the trailing usage-only chunk (`{"choices":[],"usage":{...}}`) arrived *after* the `finish_reason` chunk (the normal order for Fireworks/vLLM/Together and other `stream_options.include_usage` upstreams), the client-visible `message_delta` still carried stale/zero usage even though `state.usage` was internally corrected too late to matter (ported from [#11915](https://github.com/diegosouzapw/OmniRoute/pull/11915) — thanks @HouMinXi). ([#11933](https://github.com/diegosouzapw/OmniRoute/pull/11933)) +- **fix(sse):** `stripStore()` now forces `store=false` for stateless OpenAI-compatible Responses-API targets (unless the connection has explicitly opted in via `providerSpecificData.openaiStoreEnabled`), instead of only handling the `openai`/`agentrouter` cases — a client-supplied `store` value previously passed through untouched to backends that don't actually persist responses server-side ([#11916](https://github.com/diegosouzapw/OmniRoute/pull/11916) — thanks @HouMinXi). +- **fix(models):** custom provider-node models (synced, custom, and alias-backed) now appear under their configured prefix in the unified catalog when the operator's model-id prefix mode is canonical, instead of being dropped whenever alias-inclusion was otherwise disabled ([#11918](https://github.com/diegosouzapw/OmniRoute/pull/11918) — thanks @HouMinXi). +- **fix(models):** the unified model catalog now suppresses stale static registry models (including effort-tier variants) for any provider whose active connection has an authoritative live synced catalog, not only providers already using exclusive-synced-listing — a connection with `providerUsesAuthoritativeLiveCatalog` previously kept serving both the live-synced models and the stale static rows side by side ([#11919](https://github.com/diegosouzapw/OmniRoute/pull/11919) — thanks @HouMinXi). +- **fix(ui):** `ProviderIcon`'s three remaining unguarded lookups (`PROVIDER_ICON_ALIASES`, `LOCAL_SVG_ALIASES`, `THEMED_SVGS`) now use `Object.hasOwn()` own-property checks — a provider id such as `constructor` or `__proto__` previously resolved through the prototype chain instead of falling through to the unknown-provider CDN fallback (`getLobeProviderIcon()` itself was already guarded by [#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)); `ProviderPageHeader`'s `color` field is also now optional, matching the rest of the component's defensive typing (ported from [#11920](https://github.com/diegosouzapw/OmniRoute/pull/11920) — thanks @HouMinXi). ([#11935](https://github.com/diegosouzapw/OmniRoute/pull/11935)) — thanks @NoxzRCW +- fix(ollama): preserve multi-byte UTF-8 content split across stream chunks in the Ollama NDJSON transform, which previously corrupted CJK/emoji into U+FFFD (#11921) — thanks @pacocartones +- **fix(providers):** OrcaRouter chat requests now target `/v1/chat/completions` instead of the bare `/v1` API root, fixing the upstream `404 Invalid URL (POST /v1)` ([#11923](https://github.com/diegosouzapw/OmniRoute/pull/11923)). — thanks @echel0nn +- **fix(plugins):** deliver the `onStreamComplete` event to disk-installed plugins. The event shipped in v3.8.50 (#9669) was emitted internally but had no plugin-facing wiring, so no plugin could ever subscribe: the manifest schema silently dropped `hooks.onStreamComplete`, and the loader/manager only knew the seven legacy hooks. `onStreamComplete` is now a declarable manifest hook, wired through the loader and registered by the manager like the other hooks, and its payload carries a `requestId` so consumers can correlate the stream-completion event with the originating request ([#11934](https://github.com/diegosouzapw/OmniRoute/pull/11934)) — thanks @amaleta / @pacocartones +- **fix(db):** the Qdrant embedding-model dropdown now lists local/self-hosted providers (Ollama, LM Studio, vLLM, etc.) — an active connection is treated as "configured" when the provider allows an optional API key, not only when it has a real key or OAuth, so a running local embedding provider is no longer hidden from the picker ([#11949](https://github.com/diegosouzapw/OmniRoute/issues/11949)) ([#11995](https://github.com/diegosouzapw/OmniRoute/pull/11995)) +- **fix(providers):** Vertex AI Anthropic partner-model discovery now calls the Model Garden `v1beta1` publisher list (`/v1beta1/publishers/anthropic/models`, global) and parses its `publisherModels` envelope, so Claude models auto-synced from Vertex populate the active live catalog and route at request time instead of returning `Model '' is not available in the active live catalog` ([#11991](https://github.com/diegosouzapw/OmniRoute/issues/11991)) — thanks @fabioluissilva ([#11998](https://github.com/diegosouzapw/OmniRoute/pull/11998)) +- **fix(cli):** support `--base-url` alongside `--endpoint` in `omniroute nodes add`, `update`, and `validate` subcommands to prevent global `--base-url` shadowing issues ([#11999](https://github.com/diegosouzapw/OmniRoute/issues/11999)). ([#12033](https://github.com/diegosouzapw/OmniRoute/pull/12033)) — thanks @HouMinXi +- **fix(providers):** `cloudflare-ai` no longer refuses image content parts for every Workers AI model ([#12002](https://github.com/diegosouzapw/OmniRoute/pull/12002)) — the plain-string `content` requirement behind #2539 is carried by the _model_ schema, not by the `/ai/v1/chat/completions` endpoint (measured: an all-text part array returns 200 on `@cf/mistralai/mistral-small-3.1-24b-instruct`, `@cf/meta/llama-4-scout-17b-16e-instruct` and `@cf/meta/llama-3.3-70b-instruct-fp8-fast`, and 400 on the text-only `@cf/qwen/qwen2.5-coder-32b-instruct`). `transformRequest()` flattened every array and threw on the first non-text part (#6390), so image input was refused for vision-capable Cloudflare models that accept it. All-text arrays are still flattened — the one shape every model accepts — while an array carrying a non-text part is passed through untouched, so the attachment is still never silently dropped. Regression guards: `tests/unit/cloudflare-ai-image-parts-6390.test.ts`. — thanks @davidlinfr +- **fix(resilience):** decouple the limiter-managed execution backstop from the queue-wait budget — new `requestQueue.executionMaxWaitMs` (env `RATE_LIMIT_EXECUTION_MAX_WAIT_MS`, default 600000 = 10 min) now feeds Bottleneck's post-dispatch `expiration`, while `requestQueue.maxWaitMs` keeps its documented queue-wait semantics. Previously the queue-wait budget doubled as the execution expiration, so legitimate long-running calls on non-incremental gateways (whole generation buffered before the first upstream byte, e.g. Console Go / Command Code tiers serving GLM models) were killed mid-flight at the queue budget with a false 504 `RATE_LIMIT_EXECUTION_TIMEOUT` — the local limiter undercut the provider-aware upstream fetch-start timeouts. The surfaced 504 message now names `requestQueue.executionMaxWaitMs`; the error keeps the #4165 guarantees (disclaims an upstream timeout, preserves the Bottleneck error as `cause`, branded code + trusted provenance, classified request-scoped so combo falls back). A real queue-wait bound (the `Promise.race` around `limiter.schedule()` sketched in #9533) remains future work. (#12025) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **Call logs:** keep the `error` field when an artifact exceeds the storage cap, instead of replacing it with the omission marker. The error is the only field that says *why* a request failed and is typically ~90 bytes next to the multi-hundred-KB bodies that trip the cap, so dropping it left a size-limited row undiagnosable — a provider outage, a local timeout and an upstream 400 all rendered identically. It is now preserved at every fallback stage, truncated to 4KB if it is itself large ([#12026](https://github.com/diegosouzapw/OmniRoute/issues/12026)). ([#12095](https://github.com/diegosouzapw/OmniRoute/pull/12095)) — thanks @ntdat812 +- **fix(diagnostics):** preserve the error field (truncated to 4KB with a `[truncated: …]` suffix) in every call-log artifact size-limit fallback stage. Previously the minimal fallback replaced the error with `[omitted: call log artifact size limit exceeded]`, so an oversized artifact row showed nothing about WHY the request failed — e.g. 91 of 847 opencode-go 504 rows on one production instance were undiagnosable from the dashboard. Oversized request/response bodies are still omitted exactly as before; the error cap is independent of the payload sizes that tripped the fallback. (#12026) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **fix(sse):** OpenAI Responses clients that declare the native `web_search` tool now receive a spec-shaped `web_search_call` output item with `action.sources` alongside the preserved function-call round-trip, so search results executed through OmniRoute's own search backend are consumable by standard Responses clients (Codex, pi-web-access, …). ([#12031](https://github.com/diegosouzapw/OmniRoute/pull/12031)) — thanks @watchingdogs +- **fix(cli):** use in-thread alias resolver hooks on modern runtimes to avoid deprecation noise and improve Node.js forward compatibility ([#12073](https://github.com/diegosouzapw/OmniRoute/issues/12073)). ([#12083](https://github.com/diegosouzapw/OmniRoute/pull/12083)) — thanks @xiaoyaner0201 +- **fix(combo):** an operator-set **Agent Features → Context length** on a combo is now honored at request time. The value was persisted and advertised through `/v1/models`, but `resolveComboContextLimit()` never consulted it — so a multi-target combo whose members carry no per-model window fell through to the provider's generic `defaultContextLength` (openrouter 128000, command-code 200000) and rejected large requests with `Input exceeds context window … limit 128000` despite the combo being explicitly sized much larger. An identical single-target combo worked, because it collapses to its concrete target before the guard runs. Invalid values (0/negative/NaN/Infinity) are ignored, so the existing target → combo-min → fallback order is unchanged. ([#12090](https://github.com/diegosouzapw/OmniRoute/pull/12090)) — thanks @adivekar-utexas +- **fix(sse):** passthrough streams now estimate usage on finish when upstream closes without usage even with `stream_options.include_usage` — avoids `0 tokens / 0%` for providers that stay silent (and correctly handles trailing empty-choices usage) ([#12151](https://github.com/diegosouzapw/OmniRoute/pull/12151)) — thanks @maxmad64bis +- **fix(combos):** clearing an agent feature in the combos editor now persists — unchecking context cache protection, or emptying the system message or tool filter, sends an explicit `null` instead of dropping the field from the `PUT` body, which the update merge read as "leave unchanged" ([#12177](https://github.com/diegosouzapw/OmniRoute/pull/12177)) — thanks @foreveryh / @ntdatt812 +- **fix(memory):** self-hosted embedding endpoints now vectorize — the vector width is measured from the first embedding that comes back instead of being read from a registry that cannot describe them, so `vec_memories` is created and memories stop piling up unvectorized behind a green health check ([#12180](https://github.com/diegosouzapw/OmniRoute/pull/12180)) — thanks @kanade-hoshino / @ntdatt812 +- **fix(translator):** the leading `system` message now reaches Responses-API upstreams when its `content` is a content-part array — it was read as `typeof content === "string" ? content : ""`, so a prompt-caching client (Anthropic `cache_control`, the shape LiteLLM and the Anthropic SDK emit) had its entire system prompt replaced by an empty `instructions`. The request was still accepted with a normal `prompt_tokens` count, so the model answered with no instructions and nothing in the response said they were missing. Mid-conversation system turns already handled the array shape ([#7056](https://github.com/diegosouzapw/OmniRoute/pull/7056)); only the first one did not ([#12206](https://github.com/diegosouzapw/OmniRoute/issues/12206)). Regression guard: `tests/unit/translator-openai-responses-system-content-parts.test.ts`. ([#12207](https://github.com/diegosouzapw/OmniRoute/pull/12207)) — thanks @Gorillaz322 +- **fix(free-tier):** `/api/free-tier/summary` no longer computes its totals from a Radar feed built before the catalog the running release ships. When the cached feed is older — or carries no build date at all — the route answers from the shipped catalog, resolved through the operator's local model state so disabled and tombstoned models stay out of the numbers ([#12215](https://github.com/diegosouzapw/OmniRoute/pull/12215)). — thanks @maxmad64bis +- **fix(oauth):** Keep a Claude personal workspace and a Team organization as separate connections — they share the same email and `accountUUID`, so the email-only OAuth dedup let the second login overwrite the first account's tokens; `organizationUUID` now disambiguates them, the way `workspaceId` does for Codex ([#12222](https://github.com/diegosouzapw/OmniRoute/pull/12222)) — thanks @davidebaraldo +- **fix(resilience):** a 402 on a single paid model of a passthrough/gateway provider (e.g. `kilo-gateway`, `ollama-cloud`) no longer terminalizes the whole connection with a never-auto-recovered `credits_exhausted` status — only the paid model is locked out, so free models on the same key keep serving. 402 variant of [#3027](https://github.com/diegosouzapw/OmniRoute/issues/3027). Single-credential providers are unaffected — a 402 there is still treated as the key being genuinely out of credit ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239) / [#10616](https://github.com/diegosouzapw/OmniRoute/issues/10616)) ([#12242](https://github.com/diegosouzapw/OmniRoute/issues/12242)) — thanks @brick30llc-ctrl ([#12266](https://github.com/diegosouzapw/OmniRoute/pull/12266)) — thanks @geekyNads +- **fix(sse):** trust `finish_reason: "length"`/`"max_tokens"` over the reasoning-consumed-token ratio in response quality validation, so a reasoning model truncated below the old 90% threshold correctly fails and retries instead of returning empty content as a silent "success" ([#12262](https://github.com/diegosouzapw/OmniRoute/pull/12262)) — thanks @brick30llc-ctrl +- **fix(settings):** `PUT /api/settings/cache-config` now persists `alwaysPreserveClientCache` to the flat general settings the runtime cache-control policy actually reads; previously the value landed in the databaseSettings "cache" section and was silently ignored, so the endpoint had no effect on `cache_control` passthrough ([#12304](https://github.com/diegosouzapw/OmniRoute/pull/12304)) — thanks @davidebaraldo +- **fix(grok-cli):** treat omitted SuperGrokPro `creditUsagePercent` as 0% used so Provider Limits still renders a weekly bar (proto3 zero-elision) ([#12312](https://github.com/diegosouzapw/OmniRoute/pull/12312)) — thanks @HouMinXi +- **fix(combo):** Expose the two Auto-Combo scoring factors nobody could set — the combo validation schema and the dashboard weight sliders both declared 13 of the scorer's 15 factors, so `connectionDensity` (spreads load across a provider's connections) and `quality` were dropped on save and offered nowhere. The sliders also shipped their own default table that differed from the engine's on every non-zero factor and summed to 1.05, so the percentages shown next to them added up to 105%. Both lists now match `DEFAULT_WEIGHTS`, and a test keeps them there. Note that a combo whose stored `weights` omitted the two keys was effectively running with them at zero and the other thirteen renormalized upward; it now runs with the engine's intended distribution, so its routing does shift ([#12314](https://github.com/diegosouzapw/OmniRoute/pull/12314)) — thanks @maxmad64bis +- **fix(docs):** The free-tier reference no longer says its numbers come "confidence tagged per row" — no catalog entry carries a confidence tag and the API serves none, so every figure on that page is an estimate of the same, unstated quality. The page now states what an entry does vouch for: an independently documented hard stop (set by hand with the source in a comment, never defaulted to `true`) and a prompt-training disclosure, both with live counts the `check:docs-counts` gate keeps honest ([#12318](https://github.com/diegosouzapw/OmniRoute/pull/12318)) — thanks @maxmad64bis +- **fix(usage):** `adobe-firefly` and `firefly` have had a working usage fetcher since Adobe Firefly landed, but neither was ever added to the registration list, so the provider-plugin manifest, `genericQuotaFetcher` and the free-access quota cache all reported them as having no usage support — while `USAGE_SUPPORTED_PROVIDERS` said the opposite. Both are now declared, which also means their credit balance is fetched like any other declared provider's: `registerGenericQuotaFetchers` now registers a generic quota fetcher for them, and `resolveFreeAccessState` no longer returns early. A test holds the registration list to the dispatcher's switch in both directions, which is what the module's own docstring already asked for in prose ([#12321](https://github.com/diegosouzapw/OmniRoute/pull/12321)) — thanks @maxmad64bis +- **fix(quota):** drop the generic quota cache (agy / Antigravity / Claude OAuth) on an upstream 429 so reset-aware scoring does not keep a 60s stale snapshot, and force-refresh the next usage fetch so inner provider caches cannot recache the same window ([#12325](https://github.com/diegosouzapw/OmniRoute/pull/12325)) — thanks @HouMinXi +- **fix(combos):** deleting a combo now clears its persisted LKGP pins instead of leaving unreachable `key_value` rows behind ([#12326](https://github.com/diegosouzapw/OmniRoute/issues/12326)) ([#12425](https://github.com/diegosouzapw/OmniRoute/pull/12425)) — thanks @KrzysiekSko +- **fix(sse):** Keep ZWNJ (U+200C) and ZWJ (U+200D) in assistant text, reasoning and tool-call arguments — Persian/Kurdish half-space (`ارائه‌دهنده`), Arabic/Indic shaping and emoji sequences no longer lose them; the response de-obfuscation now removes joiners only between ASCII word characters, where the request side inserts them ([#12186](https://github.com/diegosouzapw/OmniRoute/issues/12186)) — thanks @rezjalibd ([#12359](https://github.com/diegosouzapw/OmniRoute/pull/12359)) — thanks @pacocartones +- **fix(resilience):** count resolved upstream 5xx results against the provider circuit breaker on the chat path — `CircuitBreaker.execute()` no longer reads a resolved `{ success: false, status: 5xx }` as a success that cancels the call-site failure, so a provider answering 503s now trips its breaker instead of staying `CLOSED` at `failureCount: 1`; single-model and combo dispatches are each accounted exactly once ([#12254](https://github.com/diegosouzapw/OmniRoute/issues/12254)) ([#12360](https://github.com/diegosouzapw/OmniRoute/pull/12360)) — thanks @pacocartones +- **fix(providers):** resolve the Codex quota auto-ping model from the live provider catalog and lifecycle registry instead of the retired `gpt-5.1-codex-mini`, and pause the ping with one actionable warning when no selectable Codex model exists rather than retrying a shut-down id every cooldown window ([#11905](https://github.com/diegosouzapw/OmniRoute/issues/11905)) ([#12361](https://github.com/diegosouzapw/OmniRoute/pull/12361)) — thanks @pacocartones +- **fix(api):** keep the `{created, data}` wrapper on combo-routed `/v1/images/generations` responses and default Codex image results to `b64_json` on both `/v1/images/generations` and `/v1/images/edits` so Codex CLI's built-in `image_gen` can decode them ([#12268](https://github.com/diegosouzapw/OmniRoute/issues/12268)) ([#12362](https://github.com/diegosouzapw/OmniRoute/pull/12362)) — thanks @pacocartones +- **fix(sse):** Name the shadowed custom provider node when a built-in provider id/alias (e.g. `openference` → `of`) reserves the prefix of an existing OpenAI/Anthropic-compatible node, so the runtime `No active credentials for provider: ` error explains that the prefix routed to the built-in and never reached the node's healthy connections, instead of contradicting the dashboard ([#11943](https://github.com/diegosouzapw/OmniRoute/issues/11943)) — thanks @morpheus9393 ([#12365](https://github.com/diegosouzapw/OmniRoute/pull/12365)) — thanks @pacocartones +- **fix(i18n):** wrap `ccOnboardingKeyPlaceholder` in ICU single quotes across all 43 locales so angle brackets render literally instead of being parsed as rich-text tags, which crashed the Claude Code onboarding block with `INVALID_MESSAGE: INVALID_TAG` ([#12302](https://github.com/diegosouzapw/OmniRoute/issues/12302)) ([#12369](https://github.com/diegosouzapw/OmniRoute/pull/12369)) — thanks @gonisulaimann +- **fix(models):** publish `effort_tiers` on Kimi K3's synced base-model entries (`kmca/k3`, `kmca/k3-256k`) so catalog-only clients (OpenCode, plain SDK pickers) can see and select the reasoning tiers (`low`/`high`/`max`) the synced metadata already carried — the `isSkippedEffortProvider` gate no longer suppresses tier visibility on those base entries, while synthetic `-` variant generation stays prevented and Codex/GLM base models remain excluded unchanged ([#12299](https://github.com/diegosouzapw/OmniRoute/issues/12299)) ([#12371](https://github.com/diegosouzapw/OmniRoute/pull/12371)) — thanks @gonisulaimann +- **fix(guardrails):** keep `auto`/`auto/*` virtual combos exempt from the Vision Bridge `fixedModel` credential guard so a combo target is passed through instead of silently falling back to global auto-selection ([#12237](https://github.com/diegosouzapw/OmniRoute/issues/12237)) ([#12373](https://github.com/diegosouzapw/OmniRoute/pull/12373)) — thanks @pacocartones +- **fix(combo):** capability-filter exhaustion caused by `max_tokens` above every target's known output limit now reports that reason (requested `max_tokens` vs the pool's highest known ceiling) instead of the unrelated "supports structured output" message ([#12229](https://github.com/diegosouzapw/OmniRoute/issues/12229)) — thanks @DW-MediaLab ([#12374](https://github.com/diegosouzapw/OmniRoute/pull/12374)) — thanks @pacocartones +- **fix(auth):** the `least-used` account strategy now prefers accounts without backoff before falling back to oldest `lastUsedAt`, the same tie-break `round-robin` already applies, so a failover no longer lands on a just-rate-limited account for a single request ([#12279](https://github.com/diegosouzapw/OmniRoute/issues/12279)) — thanks @tenshiak ([#12375](https://github.com/diegosouzapw/OmniRoute/pull/12375)) — thanks @pacocartones +- **fix(docker):** the `chatgpt-web-codex-browser` image now finds the Chrome binary under `chrome-linux64/` (Chrome for Testing layout in `playwright:v1.62.0-noble`) as well as the legacy `chrome-linux/`, so the container no longer crash-loops with `exec: --headless=new: not found` ([#12024](https://github.com/diegosouzapw/OmniRoute/issues/12024)) ([#12376](https://github.com/diegosouzapw/OmniRoute/pull/12376)) — thanks @pacocartones +- **fix(providers):** declare `groq/compound` and `allam-2-7b` as non-reasoning models in the curated Groq registry so `reasoning_effort` / `output_config.effort` / `thinking` from Claude Code are stripped instead of forwarded, which Groq rejected with HTTP 400 ([#12134](https://github.com/diegosouzapw/OmniRoute/issues/12134)) ([#12379](https://github.com/diegosouzapw/OmniRoute/pull/12379)) — thanks @pacocartones +- **fix(executors):** `OpencodeExecutor` no longer forces a direct connection when the connection has a proxy assigned in Proxy Management but no per-account proxies: the single-account fast path used to wrap the upstream dispatch in the direct-egress sentinel, discarding the ambient proxy context the chat handler had pinned from `proxy_assignments`, so API-key `opencode`/`opencode-go` connections egressed from the host IP (and hit geoblocks) despite the assignment. The direct pin is now applied only when no ambient proxy context exists ([#11894](https://github.com/diegosouzapw/OmniRoute/issues/11894) — thanks @hizzt) ([#12380](https://github.com/diegosouzapw/OmniRoute/pull/12380)) — thanks @pacocartones +- **fix(api):** `GET /v1/models` with `MODELS_CATALOG_PREFIX_MODE=canonical` (or `?prefix=canonical`) now lists providers whose registry alias is undefined or equal to their own id (Antigravity, Antigravity CLI and other self-aliased built-ins) — their single `provider/model` id was dropped by the alias/canonical duplicate guard in the static, synced, custom and alias-backed catalog loops ([#12058](https://github.com/diegosouzapw/OmniRoute/issues/12058)) — thanks @cheynetom ([#12381](https://github.com/diegosouzapw/OmniRoute/pull/12381)) — thanks @pacocartones +- **fix(translator):** Drop replayed `thinking` blocks that carry no signature (the shape produced from cross-provider `reasoning_content`) instead of stamping the default Claude signature on them, which Anthropic rejected with `400 Invalid signature in thinking block` on the next turn served by an Anthropic rung ([#12105](https://github.com/diegosouzapw/OmniRoute/issues/12105)) — thanks @atescivitci-cmd ([#12386](https://github.com/diegosouzapw/OmniRoute/pull/12386)) — thanks @pacocartones +- **fix(cli):** Resolve Bun's `--preload` polyfill path against the package root instead of `dist/`, so `omniroute` installed with `bun install -g` no longer crashes at startup with `error: preload not found …/dist/open-sse/utils/setupPolyfill.ts` ([#11980](https://github.com/diegosouzapw/OmniRoute/issues/11980)) — thanks @joglomedia ([#12387](https://github.com/diegosouzapw/OmniRoute/pull/12387)) — thanks @pacocartones +- **fix(providers):** `gemini-business` now publishes its model catalog — `/v1/models` and `/v1/providers/gemini-business/models` list the 12 enterprise Gemini ids the executor understands instead of returning an empty list (#12107) ([#12389](https://github.com/diegosouzapw/OmniRoute/pull/12389)) — thanks @pacocartones +- **fix(db):** install `busy_timeout` before the SQLite connection's first statement so a process opening the database while another one closes its WAL connection waits out the transient EXCLUSIVE lock instead of dying with `database is locked`, and recognise the drivers' real BUSY/PROTOCOL/IOERR errors as transient in the corruption probe so the same lock no longer renames the database away as corrupt; deflakes `cross-process contenders never both acquire the same connection` (#12394 — thanks @pacocartones) +- **fix(chat-admission):** derive the `chat_admission_busy` 503 `Retry-After` from observed heavyweight-lease occupancy — the larger of the exhausted `OMNIROUTE_CHAT_ADMISSION_QUEUE_MS` window and the time since capacity last turned over, capped at 60 s — instead of a fixed 1 s (structural) / 2 s (byte-stage) hint that invited Codex/agent fan-out clients to re-send ~1 MiB `/v1/responses` bodies every second into a gate held for the whole SSE lifetime; an idle gate keeps the historical floors ([#12135](https://github.com/diegosouzapw/OmniRoute/issues/12135)) (#12395 — thanks @pacocartones) +- **fix(api-manager):** the API key permissions modal no longer silently drops `allowedCombos` entries its Combo picker cannot render — routing-rule names such as `rt-*`, which the backend already honours — when "All" is clicked and the key is switched back to "Restrict"; those entries now survive the toggle, are listed read-only under the combo list so the count and the list agree, and are saved back verbatim instead of persisting `[]` (deny-all) (#12397 — thanks @pacocartones) +- **fix(catalog):** write the NUL separator of the catalog connection memo key, the provider serviceKind memo key, the Video Bridge promotion group key and a JSON-exactness test fixture as the `\u0000` escape instead of a raw byte — same runtime value, but the raw byte made git, GitHub and ripgrep treat those files as binary (hidden PR diffs, silently skipped searches); a guard test now keeps raw NUL bytes out of `src/`, `open-sse/` and `tests/` (#12403 — thanks @pacocartones) +- **fix(providers):** add `CLAUDE_CODE_CLIENT_VERSION` and `GITHUB_COPILOT_CLI_VERSION` env overrides so Anthropic/Copilot client-version gates can be unblocked without a rebuild ([#12417](https://github.com/diegosouzapw/OmniRoute/issues/12417)) ([#12632](https://github.com/diegosouzapw/OmniRoute/pull/12632)) — thanks @HouMinXi +- **fix(db):** back-fill `last_ping_at` and `last_pinged_reset_key` on `provider_connections` during schema reconciliation so divergent lineages that skipped `123_quota_auto_ping` still accept quota auto-ping writes ([#12470](https://github.com/diegosouzapw/OmniRoute/pull/12470) — thanks @KooshaPari) +- **fix(ci):** document MIT exceptions for `@eloqnt/{config,format-json,format-po}` (next-intl transitive; locked tarballs omit `license`) and keep the A2A lifecycle vitest off the real SQLite persistence seam ([#12581](https://github.com/diegosouzapw/OmniRoute/issues/12581)) ([#12595](https://github.com/diegosouzapw/OmniRoute/pull/12595)) +- OpenCode plugin `/v1/models` catalog fetch now waits 30s by default and attaches HTTP `statusCode` on 401/5xx so host fallback plugins can hop instead of seeing an untyped AbortError/UnknownError. ([#12607](https://github.com/diegosouzapw/OmniRoute/pull/12607)) — thanks @RaviTharuma +- **CI:** the OpenAPI security-tier gate now mirrors `isAlwaysProtectedPath()` in full — it also reads `ALWAYS_PROTECTED_API_PATTERNS`, so the pattern-gated credential routes (`/api/providers/{id}/{claude,codex}-auth/{export,apply-local}`, GHSA-5926-2w35-7h4q) no longer report as unannotated. (#12605) +- **fix(api):** GET /v1/models no longer waits forever on a hung coalesced catalog rebuild; cold-path waits are bounded (`CATALOG_BUILD_TIMEOUT_MS`, default 8s) and a last-good 200 is served when the rebuild times out ([#12627](https://github.com/diegosouzapw/OmniRoute/issues/12627)). ([#12628](https://github.com/diegosouzapw/OmniRoute/pull/12628)) — thanks @RaviTharuma +- **fix(cursor):** a non-array `tool_calls` on an assistant message no longer crashes the cursor request translator with a `TypeError`; both loops now require an array ([#12689](https://github.com/diegosouzapw/OmniRoute/issues/12689)) ([#12691](https://github.com/diegosouzapw/OmniRoute/pull/12691)) — thanks @soroush5 +- **fix(chat):** Chat Completions no longer return empty `content` after a server-owned memory or skills tool; the first provider send and account/model recovery now share one pipeline so a follow-up round-trip can fill the reply ([#12696](https://github.com/diegosouzapw/OmniRoute/issues/12696)) — thanks @HouMinXi ([#12867](https://github.com/diegosouzapw/OmniRoute/pull/12867)) +- **fix(dashboard):** batch-deleting provider keys no longer toasts failure after a successful delete when the confirm button's click event is forwarded as `onAfter` ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) — thanks @HouMinXi +- **fix(glm):** drop the extra 16th argument to `createSSETransformStreamWithLogger` that TypeScript rejected (TS2554) and that never reached the TransformStream ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) ([#12770](https://github.com/diegosouzapw/OmniRoute/pull/12770)) — thanks @HouMinXi +- **fix(dashboard):** pass `nodeMap` into Runtime `QuotaGroup` so a quota monitor in error/exhausted/alerting no longer throws `ReferenceError: nodeMap is not defined`. ([#12868](https://github.com/diegosouzapw/OmniRoute/pull/12868)) — thanks @HouMinXi +- Restricted API keys whose `allowedModels` lists a combo name no longer skip every combo member at pre-dispatch (`ALL_TARGETS_SKIPPED`). Inner-target filtering still applies when the allow-list is a provider prefix or `disableNonPublicModels` is on ([#12899](https://github.com/diegosouzapw/OmniRoute/pull/12899)). — thanks @HouMinXi +- **fix(combos):** Effort-suffixed combo members inherit the base model's `model_context_overrides` row so priority order is not inverted on large requests ([#12926](https://github.com/diegosouzapw/OmniRoute/pull/12926)) — thanks @HouMinXi +- **fix(catalog):** Picker-added `customModels` enter the dispatch-time live catalog so combo members and bare inference no longer 400 ([#12934](https://github.com/diegosouzapw/OmniRoute/pull/12934)) — thanks @HouMinXi +- **fix(docker):** re-derive the Docker build's worker-pool memory budget from the MEASURED ~4.5 GB per-process RSS (the issue owner's own VPS dmesg OOM-killer reproduction) instead of the stale 2560 MB/worker inference #11419 shipped, and lower `OMNIROUTE_BUILD_WORKERS` 3 → 2 so 1 parent + 1 page-data worker (2 processes × 4.5 GB = 9 GB) fits the 12.288 GB (75%) budget on a 16 GB GitHub Actions runner — the previous default (1 parent + 2 workers = 13.5 GB) still overcommitted the runner and kept "Publish to Docker Hub" failing with `cannot allocate memory` after #11419 merged (#7518). ([#11663](https://github.com/diegosouzapw/OmniRoute/pull/11663)) +- **fix(electron):** the packaged Windows desktop build now passes the #7592 cold-restart smoke end to end. Five defects were found and fixed on the way: (1) optional-pack staging failed on any machine whose `tar` is GNU tar — it reads the drive letter in an absolute `-f C:\...` archive path as a remote rsh host (`Cannot connect to C:`), so staging now passes a bare filename with `cwd` at the tarball directory and surfaces tar stderr on failure; (2) the Electron `files` allowlist was missing `lib/loginHeaderCapture.js`, so the packaged main process crashed at startup with `Cannot find module './lib/loginHeaderCapture'`; (3) electron-builder ≥26 injects an `!**/node_modules/**` ignore into every extraResources pattern list that no positive filter can override, which silently dropped the staged runtime `node_modules` (including the better-sqlite3 N-API prebuild) from `resources/app` — a fresh v3.8.50 desktop build would have shipped with no native SQLite driver and reproduced the #7592 sql.js fallback on every machine; an `afterPack` hook now restores the staged `node_modules` after packing; (4) the packaged-app smoke harness redirected `USERPROFILE` into its temp DATA_DIR, but Electron resolves the Roaming profile from `%USERPROFILE%\AppData\Roaming\` (USERPROFILE takes precedence over the APPDATA env var) and the path service throws instead of creating it, so `requestSingleInstanceLock()` returned false and the app exited(0) silently before `app.whenReady()` — the harness now pre-creates the derived tree, and `ensureSmokeEnvDirs` is exported and unit-tested; (5) the #7592 driver assertion parsed a `[DB] Driver: ...` line that the server's primary DB path never printed (only the unused `openDatabaseAsync()` did) — `getDbInstance()` now emits the same line on open so the guard can actually assert the native driver. Also: the smoke env-allowlist unit test hardcoded POSIX paths and could only pass on Linux/macOS; expectations are now host-agnostic, with new regression tests for the USERPROFILE-derived tree and `tarPack` under absolute Windows-style paths ([#11443](https://github.com/diegosouzapw/OmniRoute/pull/11443)) — thanks @jonlwheat2-gif +- fix(sse): stop the auto-combo candidates inspector from silently dropping model-locked/cooled-down rows (#9133) ([#11994](https://github.com/diegosouzapw/OmniRoute/pull/11994)) — thanks @hartmark +- **fix(providers):** Perplexity Web no longer turns upstream stream failures into successful assistant text; pre-content failures remain eligible for fallback, partial output ends with a structured sanitized error, and failed sessions are not persisted ([#12465](https://github.com/diegosouzapw/OmniRoute/pull/12465)) +- **fix(video-bridge):** preserve chronological video and transcript fusion, source-frame timestamps, transcript provenance, full contact-sheet intervals, and pre-abort callback isolation ([#11681](https://github.com/diegosouzapw/OmniRoute/pull/11681)) +- **Z.ai Web:** HTTP 200 streams carrying an upstream error now terminate with a structured failure instead of assistant text plus a normal stop, preserving partial output while allowing pre-content combo fallback. ([#12454](https://github.com/diegosouzapw/OmniRoute/pull/12454)) +- Absorb `Error [AbortError]: request_signal_aborted` and DOMException AbortError shapes in the process-level client-abort crash guard so routine client disconnects no longer kill the server (exit code 7). ([#12165](https://github.com/diegosouzapw/OmniRoute/pull/12165)) — thanks @alvinveroy +- **fix(sse):** Treat Adapta Web `type:error` SSE events as sanitized non-stream failures instead of empty HTTP 200 completions. ([#12459](https://github.com/diegosouzapw/OmniRoute/pull/12459)) +- **fix(autoCombo):** layer 3 (`models_dev_tier`) no longer scores retired models as premium, merge cross-provider capability rows deterministically, and inherit tier scores through variant ids ([#11508](https://github.com/diegosouzapw/OmniRoute/issues/11508)). `getModelsDevTierFitness` now vetoes ids present in `config/quality/model-lifecycle.json` with status `retired` before any other signal; `loadModelCapabilities()` aggregates per `model_id` (any-non-null-true for booleans, max for `limit_context`) instead of last-write-wins over SQLite's undefined row order; a capabilities miss retries through `resolveScoresAs()`'s base id and reports `models_dev_tier:inherited`, matching layers 1–2 (#11492). Adds `getModelsDevTierFitnessWithSource()` and an `invalidateCapabilitiesCache()` test/ops hook. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) — thanks @Meet6338-X +- fix(sse): accept Claude `content:[]` + `stop_reason: max_tokens`/`tool_use` in `detectMalformedNonStream` (match `isEmptyContentResponse`) so Claude Code `/model` probes with `max_tokens: 1` no longer become a false 502 ([#11568](https://github.com/diegosouzapw/OmniRoute/pull/11568)) — thanks @Seramicx +- **fix(security):** sanitize `request.failed` diagnostics before publishing them to live dashboard listeners and replay history, while keeping status, model, provider, latency, and internal call-log diagnostics intact. ([#12469](https://github.com/diegosouzapw/OmniRoute/pull/12469)) +- **fix(pwa):** do not intercept dashboard navigations so Chrome can fall back from a stale HTTP/3 Alt-Svc advertisement (UDP :20128 is unpublished; F5 on a long-lived tab hung until a new tab opened a fresh TCP connection). ([#12767](https://github.com/diegosouzapw/OmniRoute/pull/12767)) — thanks @HouMinXi +- **fix(executors):** handle DuckDuckGo ERR_BN_LIMIT (418) without retrying — when the upstream returns `418 ERR_BN_LIMIT` (rate-limit/ban), the executor now returns the error immediately instead of burning another VQD acquisition that would only count against the IP limit. The retry logic for `418 ERR_CHALLENGE` (unsolved challenge) remains unchanged. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) ([#12110](https://github.com/diegosouzapw/OmniRoute/pull/12110)) — thanks @oyi77 / @Meet6338-X +- Fixed DuckDuckGo streaming responses losing JSON lines and UTF-8 characters split across network chunks. ([#11528](https://github.com/diegosouzapw/OmniRoute/pull/11528)) — thanks @pacocartones +- Harden generated Cloudflare, Vercel, and Deno relays by stripping hop-by-hop, framing, and proxy authentication headers before forwarding requests upstream. ([#11533](https://github.com/diegosouzapw/OmniRoute/pull/11533)) — thanks @pacocartones +- **feat(grok-cli):** Provider Limits shows grok-cli banked reset credits from `GetRemainingResets` (including a real zero; a failed RPC omits the row) and the existing View credits button now calls `ConsumerUiSvc/RedeemReset` for grok-cli. Live tokens use nested fields 10/20/30 (id + Timestamp), not compact 1/2/3. ([#12805](https://github.com/diegosouzapw/OmniRoute/pull/12805)) — thanks @HouMinXi +- **fix(memory):** Embedding Model Quick select, Embedding Source remote dropdown, and Rerank selector now list every configured provider with embedding/rerank support instead of only chat-catalog text matches plus OpenRouter live discovery; a generic OpenAI-compatible `/embeddings` + Cohere-compatible `/rerank` runtime fallback resolves any configured chat provider's embedding/rerank endpoint, so unlisted providers no longer fail with "Unknown embedding provider"; both memory selectors gained a free-text model override ([#11390](https://github.com/diegosouzapw/OmniRoute/pull/11390)) — thanks @rqzbeh +- Harden SQLite upgrades around the historical migration-074 version collision: missing discovery and inspector tables are replayed atomically, pre-existing databases (including setup-created skeletons) receive reusable content-addressed safety snapshots, and Node test/eval probes without `DATA_DIR` are isolated from the operator database. ([#12435](https://github.com/diegosouzapw/OmniRoute/pull/12435)) +- **fix(dashboard):** Moonshot/Kimi Open Platform voucher and cash leftover percentages follow the bucket balance, so an empty wallet no longer paints those rows as 100% while Available is 0% ([#12733](https://github.com/diegosouzapw/OmniRoute/pull/12733)) — thanks @HouMinXi +- **fix(combo):** return non-retryable HTTP 400 when all candidates for a pinned native Codex turn are unavailable due to model-scoped lockout, terminating the turn cleanly while preserving turn continuity and enabling standard Combo routing on subsequent turns ([#12240](https://github.com/diegosouzapw/OmniRoute/pull/12240)) — thanks @mdigitalbh81 +- Fixed auto-router strategies preserving the selected connection through ranking and dispatch when multiple connections share the same provider and model. ([#11530](https://github.com/diegosouzapw/OmniRoute/pull/11530)) — thanks @pacocartones +- Preserve Chat Completions JSON-object response formats when translating requests to the Responses API. ([#11529](https://github.com/diegosouzapw/OmniRoute/pull/11529)) — thanks @pacocartones +- Fix idempotency fingerprints so Responses API requests include `input` and other semantic generation fields using stable serialization, while excluding credentials and request metadata. ([#11532](https://github.com/diegosouzapw/OmniRoute/pull/11532)) — thanks @pacocartones +- **fix(api):** Generated API CLI commands now enforce required OpenAPI request bodies; Combo test commands forward the required `comboName` body, while API keys created by older writers after migration 149 preserve legacy allow-all Combo access without widening explicit empty allowlists — thanks @marcelokarval ([#12070](https://github.com/diegosouzapw/OmniRoute/pull/12070)) +- **fix(grok-web):** treat upstream streaming failures as failures instead of successful assistant text: error-only streams now fail readiness with HTTP 502, while failures after legitimate content preserve that partial output and terminate through the sanitized stream failure path without a normal `stop` completion. ([#12458](https://github.com/diegosouzapw/OmniRoute/pull/12458)) +- HuggingChat now turns HTTP 200 JSONL generation failures into a sanitized 502 before content, or a fixed public stream failure after partial output, so fallback and request persistence no longer record a false successful stop. ([#12456](https://github.com/diegosouzapw/OmniRoute/pull/12456)) +- **fix(providers):** keep 1min.ai HTTP 200 stream errors out of assistant content, preserve partial output, and expose sanitized terminal errors so pre-content failures can fall back. ([#12466](https://github.com/diegosouzapw/OmniRoute/pull/12466)) +- **fix(sessions):** preserve legacy Sessions data when the additive exclusive-session projection is unavailable, returning an empty projection and warning only once per contiguous outage instead of failing the endpoint; the Sessions badge intentionally reflects the merged legacy and exclusive row count introduced by [#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389) ([#11469](https://github.com/diegosouzapw/OmniRoute/pull/11469)) — thanks @KaspaPulse +- Render Nimble Search with the generic provider icon and serve Opper's proven logo locally. ([#12415](https://github.com/diegosouzapw/OmniRoute/pull/12415)) +- **fix(quality):** `config/quality/provider-assets-provenance.jsonl` was stale against the physical `public/providers/` tree (still listing 225 assets and 5 duplicate-content groups after several provider-retirement PRs removed 85 icon files), which would have made `check:provider-asset-provenance` fail on the next full run. Reconciled the manifest to the real 142-file snapshot (142 asset records, 1 duplicate-content group, 2 newly-registered files marked `unresolved` pending provenance research), repinned `auditedCommit`, and updated the regression test's hardcoded counts to match. ([#11876](https://github.com/diegosouzapw/OmniRoute/pull/11876)) +- fix(ui): wrap quota card footer action buttons to prevent clipping Refresh button on narrow cards — when extra action buttons such as `View credits` are present on Codex cards, allow the footer row to flex-wrap with `shrink-0` buttons so `Refresh now` remains visible and clickable across all responsive card widths ([#11464](https://github.com/diegosouzapw/OmniRoute/pull/11464)) — thanks @adevwithpurpose +- Keep Antigravity Gemini usable when the same connection's Claude weekly quota is empty; generic quota cache stays per-connection for every other provider. ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) — thanks @HouMinXi +- Fixed Tailscale tunnel setup to reject ports outside the valid 1-65535 range and safely install Windows MSI packages from paths containing apostrophes. ([#11534](https://github.com/diegosouzapw/OmniRoute/pull/11534)) — thanks @pacocartones +- Classify Cloudflared and Tailscale process-management routes as local-only and spawn-capable while preserving remote access to read-only tunnel status endpoints. ([#11531](https://github.com/diegosouzapw/OmniRoute/pull/11531)) — thanks @pacocartones +- Electron release workflow: the `publish-npm` job now grants `actions: read` to the reusable `npm-publish.yml` it calls (its `publish` job requests it), which is what made GitHub refuse the whole v3.8.50 run at startup and ship the release with zero desktop assets; a `workflow_dispatch` now builds the requested tag instead of the dispatching branch and can skip the npm leg (`publish_npm=false`) when only re-attaching assets ([#11974](https://github.com/diegosouzapw/OmniRoute/pull/11974)) +- **fix(dashboard):** The Combos page usage guide now reads its dismissal through `useSyncExternalStore` instead of correcting SSR state inside an effect, removing an extra commit of the page tree on every load (and the `react-hooks/set-state-in-effect` error it raised). ([#12671](https://github.com/diegosouzapw/OmniRoute/pull/12671)) +- Electron release: `electron/package-lock.json` regained the optional `electron-builder-squirrel-windows` subtree (13 entries) that `npm ci` had been refusing as out of sync, `electron-release.yml` gained a `build_ref` dispatch input and stops regenerating release notes on a re-attach dispatch, and the npm publish workflow attaches the SBOM to the GitHub Release on dispatch publishes too — so the v3.8.51 tag ships every desktop asset and the SBOM like v3.8.49 did ([#12022](https://github.com/diegosouzapw/OmniRoute/pull/12022)) +- Drained the reds every PR against `release/v3.8.51` was born with: documented the three service-worker build-id variables, registered the six retirement/tunnel tests with the mutation gate, approved `eslint-plugin-react-hooks` in the dependency allowlist, added the six `combo.sort.*` strings to `vi` and `pt-BR`, pointed the ChatGPT Web doc at the real migration-168 test, worded the g4f hint around the member key, and realigned four tests to the retired-provider catalog and the legacy-schema fixtures the retirement migrations touch. ([#11940](https://github.com/diegosouzapw/OmniRoute/pull/11940)) +- **fix(providers):** Claude, Grok, LMArena, Notion, and Perplexity web-cookie transports now use pooled `wreq-js` 3.2 instead of the native sidecar, with all nine supported bindings pinned and audited, and the applicable platform binding plus native-license evidence included in each release artifact ([#12429](https://github.com/diegosouzapw/OmniRoute/pull/12429), supersedes [#11753](https://github.com/diegosouzapw/OmniRoute/pull/11753)). +- **fix(providers):** Zed Hosted streaming failures now trigger fallback before content and end partial streams with a sanitized structured error instead of fake assistant text and a normal-success stop. ([#12455](https://github.com/diegosouzapw/OmniRoute/pull/12455)) +- **fix(test):** capture hook result after render (direct commit `b85d0abb0b`) +- **fix(sync):** repair the two regressions the v3.8.50 sync-back left on release/v3.8.51 (direct commit `5ade9e0851`) +- **revert(models):** drop the memo reset hook ported from #10055 (direct commit `282de841ae`) +- **fix(typecheck):** guard the optional connectionId before the reactive model sync (direct commit `f2825bad75`) +- **fix(quota-share):** release the winner's reserved in-flight slot (#11371) ([#11408](https://github.com/diegosouzapw/OmniRoute/pull/11408)) — thanks @oyi77 +- **fix(providers):** stop silently dropping reasoning effort on opencode families (#10788) ([#11409](https://github.com/diegosouzapw/OmniRoute/pull/11409)) — thanks @oyi77 +- **fix(combo):** enable genuine quota-aware routing for generic providers (antigravity, claude, etc.) ([#11411](https://github.com/diegosouzapw/OmniRoute/pull/11411)) — thanks @HouMinXi +- **fix(oauth):** allow expired token retry before deactivation and activate expired retry loop (#P0) ([#11414](https://github.com/diegosouzapw/OmniRoute/pull/11414)) — thanks @HouMinXi +- **perf(executors):** lazy-load the executor registry — defer class imports + construction to first use (#11220) ([#11421](https://github.com/diegosouzapw/OmniRoute/pull/11421)) — thanks @oyi77 +- **fix:** make release/v3.8.50 CI gates green (run 32786966560 root causes) ([#11450](https://github.com/diegosouzapw/OmniRoute/pull/11450)) — thanks @jonlwheat2-gif +- **fix(sse):** exact-domain cookie match and origin-equality URL assertions ([#11452](https://github.com/diegosouzapw/OmniRoute/pull/11452)) — thanks @hartmark +- **fix(providers):** send Responses API payload during OpenAI-compatible validation ([#11454](https://github.com/diegosouzapw/OmniRoute/pull/11454)) — thanks @tuandinh0801 +- **fix(command-code):** fallback to /alpha/generate for Go plan without Provider API access ([#11455](https://github.com/diegosouzapw/OmniRoute/pull/11455)) — thanks @Chewji9875 +- **fix(responses-continuation):** fail closed on a log-truncated stored input/output array ([#11473](https://github.com/diegosouzapw/OmniRoute/pull/11473)) — thanks @hartmark +- **fix(cli):** write jcode settings as [providers.omniroute] in config.toml ([#11484](https://github.com/diegosouzapw/OmniRoute/pull/11484)) — thanks @hongnoul +- **fix(logging):** raise chat-log array truncation cap to 1000 and unify duplicate implementations ([#11499](https://github.com/diegosouzapw/OmniRoute/pull/11499)) — thanks @hartmark +- **fix:** restore live websocket URL helpers ([#11509](https://github.com/diegosouzapw/OmniRoute/pull/11509)) — thanks @PauloFH +- **perf(core):** eliminate async waterfalls across home, cache, and db operations (#11396) ([#11512](https://github.com/diegosouzapw/OmniRoute/pull/11512)) — thanks @HouMinXi +- **fix(kiro):** add runtime.us-east-1.kiro.dev as first-attempt endpoint ([#11517](https://github.com/diegosouzapw/OmniRoute/pull/11517)) — thanks @rifqiawl +- **fix(providers):** remove duplicate 128-tool truncation in OpencodeExecutor (#11444) ([#11518](https://github.com/diegosouzapw/OmniRoute/pull/11518)) — thanks @HouMinXi +- **fix(providers):** poll onboardUser LRO before classifying BYOP ([#11519](https://github.com/diegosouzapw/OmniRoute/pull/11519)) — thanks @rifqiawl +- **fix(translator):** preserve exact tool name casing for Claude tool_use payloads (#11487) ([#11520](https://github.com/diegosouzapw/OmniRoute/pull/11520)) — thanks @HouMinXi +- **fix(kimi-web):** align health probe with executor domain ([#11521](https://github.com/diegosouzapw/OmniRoute/pull/11521)) — thanks @pacocartones +- **fix(cli):** preserve existing admin password during setup ([#11522](https://github.com/diegosouzapw/OmniRoute/pull/11522)) — thanks @pacocartones +- **fix(mitm):** keep Windows DNS elevation off the POSIX sudo path (#11430) ([#11542](https://github.com/diegosouzapw/OmniRoute/pull/11542)) — thanks @official-burak +- **fix(sse):** scale chat admission by ingest byte budget instead of a fixed request count ([#11548](https://github.com/diegosouzapw/OmniRoute/pull/11548)) — thanks @TheDemonTuan +- **fix:** accept Gemini Web cookie exports ([#11554](https://github.com/diegosouzapw/OmniRoute/pull/11554)) — thanks @rafacpti23 +- **fix(i18n):** complete Brazilian Portuguese translations ([#11558](https://github.com/diegosouzapw/OmniRoute/pull/11558)) — thanks @rafacpti23 +- **fix(sse):** explicit types for openai-responses pureHelpers — clears last failing core typecheck gate ([#11567](https://github.com/diegosouzapw/OmniRoute/pull/11567)) — thanks @jonlwheat2-gif +- **fix(opencode):** hydrate Proxy Pool references for no-auth connections ([#11584](https://github.com/diegosouzapw/OmniRoute/pull/11584)) — thanks @AStupidBear +- **fix(ci):** resolve inherited release/v3.8.51 base-red CI failures ([#11608](https://github.com/diegosouzapw/OmniRoute/pull/11608)) — thanks @jonlwheat2-gif +- **fix(db):** fallback load STORAGE_ENCRYPTION_KEY from env files ([#11614](https://github.com/diegosouzapw/OmniRoute/pull/11614)) — thanks @benzntech +- **fix(providers):** mark Brave Search as no longer having a free tier ([#11621](https://github.com/diegosouzapw/OmniRoute/pull/11621)) — thanks @turbolego +- **fix:** pass tool result content through as string in OpenAI/Claude -> Gemini translation ([#11624](https://github.com/diegosouzapw/OmniRoute/pull/11624)) — thanks @Hsia97 +- **fix(sse):** scope credential health to exact key ([#11628](https://github.com/diegosouzapw/OmniRoute/pull/11628)) — thanks @KaspaPulse +- **fix(install):** guard prepare script against missing husky ([#11634](https://github.com/diegosouzapw/OmniRoute/pull/11634)) — thanks @Prajeeth-12 +- **fix(security):** redact NVIDIA_API_KEY at the log sink in the ad-hoc diag script ([#11639](https://github.com/diegosouzapw/OmniRoute/pull/11639)) +- **fix(types):** clear the two type regressions red on release/v3.8.51 ([#11640](https://github.com/diegosouzapw/OmniRoute/pull/11640)) — thanks @jonlwheat2-gif +- **fix(build):** resolve workspace: protocol dependencies to concrete semver versions during prepublish (#11540) ([#11641](https://github.com/diegosouzapw/OmniRoute/pull/11641)) — thanks @HouMinXi +- **fix(search):** properly resolve configured search connection in /v1/responses pipeline (#11524) ([#11642](https://github.com/diegosouzapw/OmniRoute/pull/11642)) — thanks @HouMinXi +- **fix(authz):** trust cf-connecting-ip for via-proxy detection when peer is Cloudflare edge (#11514) ([#11643](https://github.com/diegosouzapw/OmniRoute/pull/11643)) — thanks @HouMinXi +- **fix(security):** compare the OIDC state cookie in constant time ([#11646](https://github.com/diegosouzapw/OmniRoute/pull/11646)) +- **fix(security):** namespace the dedup hash by the calling API key ([#11649](https://github.com/diegosouzapw/OmniRoute/pull/11649)) +- **fix(models):** gate the Codex-native catalog loop on prefix mode (#11632) ([#11662](https://github.com/diegosouzapw/OmniRoute/pull/11662)) — thanks @xiaoyaner0201 +- **fix(dashboard):** project Codex routing quota in account pools ([#11666](https://github.com/diegosouzapw/OmniRoute/pull/11666)) — thanks @KaspaPulse +- **fix(ddgw):** normaliza system no upstream da release v3.8.51 ([#11692](https://github.com/diegosouzapw/OmniRoute/pull/11692)) — thanks @RhianB14 +- **fix(memory):** honor category filter in GET /api/memory ([#11699](https://github.com/diegosouzapw/OmniRoute/pull/11699)) — thanks @Sabeekhann +- **fix(ci):** detect stale workspace lockfile entries ([#11714](https://github.com/diegosouzapw/OmniRoute/pull/11714)) — thanks @pacocartones +- **fix(ci):** stamp dist/BUILD_SHA before the npm publish provenance gate ([#11721](https://github.com/diegosouzapw/OmniRoute/pull/11721)) +- **fix(docker):** decouple the best-effort Bun image from the release manifest ([#11724](https://github.com/diegosouzapw/OmniRoute/pull/11724)) +- **perf(compression):** avoid cloning memo misses twice ([#11727](https://github.com/diegosouzapw/OmniRoute/pull/11727)) — thanks @RaviTharuma +- **fix(docker):** let the bun digest artifact be absent, not fatal ([#11740](https://github.com/diegosouzapw/OmniRoute/pull/11740)) +- **fix(shared):** block cloud-metadata hosts under default remote-image guard ([#11755](https://github.com/diegosouzapw/OmniRoute/pull/11755)) — thanks @santosraju99-hub +- **fix(api):** preserve caller-provided X-Correlation-Id on chat completions ([#11760](https://github.com/diegosouzapw/OmniRoute/pull/11760)) — thanks @aniruddhaadak80 +- **fix(resilience):** lock permanently retired models instead of short backoff (Gemini ban prevention) ([#11762](https://github.com/diegosouzapw/OmniRoute/pull/11762)) — thanks @turbolego +- **fix:** mark Vercel AI Gateway as passthroughModels ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **fix(resilience):** stop hammering permanently-moved endpoints and billing-suspended accounts ([#11774](https://github.com/diegosouzapw/OmniRoute/pull/11774)) — thanks @turbolego +- **fix(lease):** remove global static reservation and gate routing on live active lease occupancy ([#11775](https://github.com/diegosouzapw/OmniRoute/pull/11775)) — thanks @TheDemonTuan +- **fix(ci):** size the install-upgrade gate to a measured run, and log the pack cost ([#11776](https://github.com/diegosouzapw/OmniRoute/pull/11776)) +- **fix(pwa):** stop serving the stale shell after deploys ([#11779](https://github.com/diegosouzapw/OmniRoute/pull/11779)) — thanks @HouMinXi +- **fix(combo):** lock GitHub models rejected as "not supported" for future requests ([#11781](https://github.com/diegosouzapw/OmniRoute/pull/11781)) — thanks @turbolego +- **fix(cli):** probe both IPv4 and IPv6 loopback for server readiness (#11766) ([#11794](https://github.com/diegosouzapw/OmniRoute/pull/11794)) — thanks @vermasomesh835 +- **fix(sse):** preserve CLIProxyAPI wrappers per connection ([#11797](https://github.com/diegosouzapw/OmniRoute/pull/11797)) — thanks @RaviTharuma +- **fix(providers):** honor model auto-fetch opt-in on create ([#11798](https://github.com/diegosouzapw/OmniRoute/pull/11798)) — thanks @RaviTharuma +- **fix(dashboard):** make model auto-fetch order independent ([#11805](https://github.com/diegosouzapw/OmniRoute/pull/11805)) — thanks @RaviTharuma +- **fix(docker):** make GHA cache exports best effort ([#11806](https://github.com/diegosouzapw/OmniRoute/pull/11806)) — thanks @RaviTharuma +- **fix(kiro):** do not permanently ban on 'User is not authorized to make this call' ([#11809](https://github.com/diegosouzapw/OmniRoute/pull/11809)) — thanks @Deftera186 +- **fix(sse):** keep cache-write tokens in OpenAI-shaped usage ([#11814](https://github.com/diegosouzapw/OmniRoute/pull/11814)) — thanks @TheDemonTuan +- **fix(docs):** resolve relative markdown and wiki links across Fumadocs and GitHub wiki ([#11834](https://github.com/diegosouzapw/OmniRoute/pull/11834)) — thanks @AndrianBalanescu +- **fix(chat):** preserve unstripped model string for passthrough provider routing ([#11840](https://github.com/diegosouzapw/OmniRoute/pull/11840)) — thanks @raheemuddin786 +- **fix(mcp):** avoid async initialization inside sync esm wrappers (#11569) ([#11849](https://github.com/diegosouzapw/OmniRoute/pull/11849)) — thanks @HouMinXi +- **fix(sse):** preserve coherent markdown token boundaries across stream translation chunks (#11606) ([#11851](https://github.com/diegosouzapw/OmniRoute/pull/11851)) — thanks @HouMinXi +- **fix(ci):** run the install-upgrade gate on disk, not on the /tmp tmpfs ([#11855](https://github.com/diegosouzapw/OmniRoute/pull/11855)) +- **fix(skills):** expand shorthand property types in injected tool schemas ([#11857](https://github.com/diegosouzapw/OmniRoute/pull/11857)) — thanks @alvinveroy +- **fix(ci):** publish npm from a hosted runner so provenance is accepted ([#11877](https://github.com/diegosouzapw/OmniRoute/pull/11877)) +- **fix(lint):** pin react hooks plugin to 7.0.1 ([#11886](https://github.com/diegosouzapw/OmniRoute/pull/11886)) +- **fix(ci):** surface and clear release-green ESLint failure ([#11890](https://github.com/diegosouzapw/OmniRoute/pull/11890)) — thanks @MumuTW +- **fix(guardrails):** prevent duplicate prompt-injection-guard log output ([#11936](https://github.com/diegosouzapw/OmniRoute/pull/11936)) — thanks @patrykkopycinski +- **fix(auth):** downgrade expected transient states from warn to debug ([#11937](https://github.com/diegosouzapw/OmniRoute/pull/11937)) — thanks @patrykkopycinski +- **fix(codex):** fail over image generation for imported free plans ([#11948](https://github.com/diegosouzapw/OmniRoute/pull/11948)) — thanks @Bl0ck154 +- **fix(codex):** normalize non-stream responses ([#11951](https://github.com/diegosouzapw/OmniRoute/pull/11951)) — thanks @Bl0ck154 +- **fix(images):** forward Antigravity image size ([#11952](https://github.com/diegosouzapw/OmniRoute/pull/11952)) — thanks @Bl0ck154 +- **fix(db):** invalidate connection cache after upsert ([#11953](https://github.com/diegosouzapw/OmniRoute/pull/11953)) — thanks @Bl0ck154 +- **fix(codex):** restore imported account state ([#11954](https://github.com/diegosouzapw/OmniRoute/pull/11954)) — thanks @Bl0ck154 +- **fix(sse):** give extended-thinking targets the reasoning readiness budget ([#11959](https://github.com/diegosouzapw/OmniRoute/pull/11959)) — thanks @abhisheksharma2411 +- **fix(dashboard):** use opaque background and readable text color on cost chart tooltips ([#11960](https://github.com/diegosouzapw/OmniRoute/pull/11960)) — thanks @ZaimMarzuki +- **fix(antigravity):** send complete loadCodeAssist metadata (ideType/platform/pluginType as numeric enums) ([#11969](https://github.com/diegosouzapw/OmniRoute/pull/11969)) — thanks @rifqiawl +- **fix(core):** resolve DB init race condition and reasoning translation ([#12003](https://github.com/diegosouzapw/OmniRoute/pull/12003)) — thanks @raheemuddin786 +- **fix(admission):** exclude reclaimable page cache from the cgroup pressure ratio ([#12017](https://github.com/diegosouzapw/OmniRoute/pull/12017)) — thanks @HouMinXi +- **fix(build):** prune non-production trees in NFT trace excludes and tsconfig ([#12028](https://github.com/diegosouzapw/OmniRoute/pull/12028)) — thanks @Chewji9875 +- **fix(migrations):** add renamed migration compatibility for 056/073/077/101 ([#12036](https://github.com/diegosouzapw/OmniRoute/pull/12036)) — thanks @oyi77 +- **fix(sse):** default crash-guard logger to console.warn, not console ([#12042](https://github.com/diegosouzapw/OmniRoute/pull/12042)) — thanks @HouMinXi +- **fix(catalog):** derive vision/modalities for built-in auto combos from effective target pool ([#12046](https://github.com/diegosouzapw/OmniRoute/pull/12046)) — thanks @Prajeeth-12 +- **fix(release):** the packaged-app smoke verifies the database opened, not a driver line the primary path never prints (twin of #12032) ([#12047](https://github.com/diegosouzapw/OmniRoute/pull/12047)) +- **fix(barrel):** migrate src/app/ off the @/lib/localDb barrel import (#11795 Phase 2) ([#12051](https://github.com/diegosouzapw/OmniRoute/pull/12051)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate src/lib/ off the localDb barrel to direct db imports (#59) ([#12052](https://github.com/diegosouzapw/OmniRoute/pull/12052)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate open-sse, src/shared, src/sse, src/models, src/domain off the @/lib/localDb barrel import (#11795 Phase 4) ([#12053](https://github.com/diegosouzapw/OmniRoute/pull/12053)) — thanks @jonlwheat2-gif +- **fix(barrel):** delete the @/lib/localDb barrel — every consumer migrated (#11795 Phase 5) ([#12055](https://github.com/diegosouzapw/OmniRoute/pull/12055)) — thanks @jonlwheat2-gif +- **fix(radar):** restore D12 public boundary ([#12057](https://github.com/diegosouzapw/OmniRoute/pull/12057)) +- **fix(dev):** bound webpack and Tailwind scans ([#12075](https://github.com/diegosouzapw/OmniRoute/pull/12075)) — thanks @backryun +- **fix(dev):** isolate root layout settings reads ([#12076](https://github.com/diegosouzapw/OmniRoute/pull/12076)) — thanks @backryun +- **fix(dev):** reduce instrumentation executor fan-out (phase 3) ([#12078](https://github.com/diegosouzapw/OmniRoute/pull/12078)) — thanks @backryun +- **fix(dev):** make logging resources HMR-singleton ([#12079](https://github.com/diegosouzapw/OmniRoute/pull/12079)) — thanks @backryun +- **fix(sse):** keep unavailable forced connections scoped ([#12080](https://github.com/diegosouzapw/OmniRoute/pull/12080)) — thanks @keeltrace +- **fix(dev):** isolate batch dispatch from instrumentation ([#12081](https://github.com/diegosouzapw/OmniRoute/pull/12081)) — thanks @backryun +- **fix(ci):** reconcile release test contract drift ([#12082](https://github.com/diegosouzapw/OmniRoute/pull/12082)) — thanks @backryun +- **fix(ci):** accept CVE-2025-68121 in the prebuilt tls-client .so, auto-close base-red issues, guard Scorecard on the default branch ([#12085](https://github.com/diegosouzapw/OmniRoute/pull/12085)) +- **fix(db):** drop three consumer-less 1proxy exports — dead-code base-red on release/v3.8.51 after the barrel deletion (#12055) ([#12087](https://github.com/diegosouzapw/OmniRoute/pull/12087)) +- **fix(api):** clear the six API-route TypeScript regressions the new gate landed red on ([#12094](https://github.com/diegosouzapw/OmniRoute/pull/12094)) +- **fix(release):** never let the tag-push Create Release append auto notes to the curated body ([#12096](https://github.com/diegosouzapw/OmniRoute/pull/12096)) +- **fix(sse):** honor CLIProxyAPI environment API key ([#12099](https://github.com/diegosouzapw/OmniRoute/pull/12099)) — thanks @RaviTharuma +- **fix(oauth):** bind Google refresh to the client that issued the token ([#12106](https://github.com/diegosouzapw/OmniRoute/pull/12106)) — thanks @HouMinXi +- **fix(dashboard):** make RequestLoggerDetail loadable outside Next — CSS via globals.css + CJS/ESM interop (#11703 base-reds) ([#12114](https://github.com/diegosouzapw/OmniRoute/pull/12114)) +- **fix(leases):** project status lease row to lease columns so joined connection PII never escapes ([#12115](https://github.com/diegosouzapw/OmniRoute/pull/12115)) — thanks @geek007git +- **fix(plugins):** do not kill the plugin process when a fire-and-forget hook times out ([#12116](https://github.com/diegosouzapw/OmniRoute/pull/12116)) — thanks @geek007git +- **fix(guardrails):** restore injection-guard logging on middleware-only routes ([#12117](https://github.com/diegosouzapw/OmniRoute/pull/12117)) — thanks @geek007git +- **fix(plugins):** refresh stored manifest from disk on activate so new hook fields reach existing installs ([#12120](https://github.com/diegosouzapw/OmniRoute/pull/12120)) — thanks @geek007git +- **fix(codex):** preserve existing provider state when bulk-import upserts a matching connection ([#12122](https://github.com/diegosouzapw/OmniRoute/pull/12122)) — thanks @geek007git +- **fix(ci):** clear the two base-reds the 2026-08-30 merge batch left on release/v3.8.51 (round 3) ([#12123](https://github.com/diegosouzapw/OmniRoute/pull/12123)) +- **fix(sse):** strip type:'custom' from Claude tools on agentrouter dispatch ([#12126](https://github.com/diegosouzapw/OmniRoute/pull/12126)) — thanks @wahidsadik371-coder +- **fix(sse):** stop advertising video providers the dispatcher cannot run ([#12131](https://github.com/diegosouzapw/OmniRoute/pull/12131)) — thanks @quiterunner-commits +- **fix(search):** treat HTTP 432 and plan limit errors as transient cooldown ([#12139](https://github.com/diegosouzapw/OmniRoute/pull/12139)) — thanks @HouMinXi +- **fix(sse):** refuse an AI Horde queue that cannot fit the request budget ([#12143](https://github.com/diegosouzapw/OmniRoute/pull/12143)) — thanks @quiterunner-commits +- **fix(ci):** clear the base-reds the afternoon merge batch left on release/v3.8.51 (round 5: provider count 352, TS2554/TS2677) ([#12144](https://github.com/diegosouzapw/OmniRoute/pull/12144)) +- **fix(translator):** strip neutral tool_choice when tools absent in Responses-to-Chat (#12141) ([#12166](https://github.com/diegosouzapw/OmniRoute/pull/12166)) — thanks @HouMinXi +- **fix(guardrails):** pass providerId to getResolvedModelCapabilities in checkComboVision (#12112) ([#12169](https://github.com/diegosouzapw/OmniRoute/pull/12169)) — thanks @HouMinXi +- **fix(translator):** strip plaintext reasoning content for opaque responses backends (#12128) ([#12171](https://github.com/diegosouzapw/OmniRoute/pull/12171)) — thanks @HouMinXi +- **perf(stream):** compile hot-path regexes once, bound token caches, fix quadratic buffering ([#12179](https://github.com/diegosouzapw/OmniRoute/pull/12179)) — thanks @opensource-elearning +- **fix:** resolve compression worker file using runtime anchors instead of… ([#12183](https://github.com/diegosouzapw/OmniRoute/pull/12183)) — thanks @NightStalker-87 +- **fix:** prevent Claude Code session kills via liveness-aware readiness + auto model echo ([#12189](https://github.com/diegosouzapw/OmniRoute/pull/12189)) — thanks @opensource-elearning +- **fix(translator):** preserve falsy primitive values in Gemini and Antigravity function response results ([#12191](https://github.com/diegosouzapw/OmniRoute/pull/12191)) — thanks @Siva010 +- **fix(oauth):** mark empty Antigravity projectId as degraded and clear stale errors (#11284) ([#12205](https://github.com/diegosouzapw/OmniRoute/pull/12205)) — thanks @HouMinXi +- **fix(providers):** gate the Codex auto-ping usage read on the shared quota throttle ([#12209](https://github.com/diegosouzapw/OmniRoute/pull/12209)) — thanks @abhisheksharma2411 +- **fix(combos):** prioritize SQLite row id over inner JSON id and notify delete errors ([#12213](https://github.com/diegosouzapw/OmniRoute/pull/12213)) — thanks @HouMinXi +- **fix(providers):** mark featherless.ai as no longer having a free tier (`hasFree: false`) ([#12216](https://github.com/diegosouzapw/OmniRoute/pull/12216)) — thanks @turbolego +- **fix(deepseek-web):** stop Turbopack dev panic in the PoW worker path resolver ([#12221](https://github.com/diegosouzapw/OmniRoute/pull/12221)) — thanks @hartmark +- **fix(chat-admission):** clarify local 503 source ([#12223](https://github.com/diegosouzapw/OmniRoute/pull/12223)) — thanks @keeltrace +- **fix(dev):** silence webpack runtime module warnings ([#12228](https://github.com/diegosouzapw/OmniRoute/pull/12228)) — thanks @backryun +- **fix(db):** use module.require for CommonJS runtime driver loading ([#12230](https://github.com/diegosouzapw/OmniRoute/pull/12230)) — thanks @raheemuddin786 +- **fix(memory):** honest probe-driven FTS5 keyword status + memory_id rowid sync ([#12231](https://github.com/diegosouzapw/OmniRoute/pull/12231)) — thanks @raheemuddin786 +- **fix(system):** propagate abort signal to stream reader in HTTP version checks ([#12232](https://github.com/diegosouzapw/OmniRoute/pull/12232)) — thanks @raheemuddin786 +- **fix(resilience):** isolate local host execution errors from provider circuit breakers ([#12233](https://github.com/diegosouzapw/OmniRoute/pull/12233)) — thanks @raheemuddin786 +- **fix(sse):** sort injected tools deterministically for prompt caching ([#12234](https://github.com/diegosouzapw/OmniRoute/pull/12234)) — thanks @raheemuddin786 +- **fix(combo):** always clear the loop-safety timer, not just on the happy path (#11804) ([#12245](https://github.com/diegosouzapw/OmniRoute/pull/12245)) +- **fix(quality):** base-red pair — stryker tap registration + turn-pin suites aligned to the window gate ([#12255](https://github.com/diegosouzapw/OmniRoute/pull/12255)) — thanks @backryun +- **fix(dev):** qualify Turbopack runtime boundaries ([#12258](https://github.com/diegosouzapw/OmniRoute/pull/12258)) — thanks @backryun +- **fix(combo):** fall back to full pool when collapsed sole survivor is context-too-small ([#12278](https://github.com/diegosouzapw/OmniRoute/pull/12278)) — thanks @opensource-elearning +- **fix(combo):** bound the pre-dispatch unavailable skip so a stale label cannot dark a pool (#12168) ([#12285](https://github.com/diegosouzapw/OmniRoute/pull/12285)) +- **fix(security):** strip Qwen/Alibaba console-session cookies from provider API responses ([#12287](https://github.com/diegosouzapw/OmniRoute/pull/12287)) +- **fix(usage):** console-aware Token Plan guidance + subscription hint on bailian 401 ([#12288](https://github.com/diegosouzapw/OmniRoute/pull/12288)) +- **fix(resourcePressure):** log numeric detail on every rejection, recover faster ([#12293](https://github.com/diegosouzapw/OmniRoute/pull/12293)) — thanks @hartmark +- **fix(release):** drain the 2026-09-01 base-red window — passthrough usage regression + radarPage i18n keys ([#12327](https://github.com/diegosouzapw/OmniRoute/pull/12327)) +- **fix(ci):** openapi-security-tiers checker must honor routeGuard patterns + imported prefixes ([#12350](https://github.com/diegosouzapw/OmniRoute/pull/12350)) — thanks @ggiak +- **perf(ci):** cache node_modules in the npm-ci-retry composite (#8084 D3) ([#12408](https://github.com/diegosouzapw/OmniRoute/pull/12408)) +- **revert(ui):** point CTAs back at their real destinations ([#12410](https://github.com/diegosouzapw/OmniRoute/pull/12410)) +- **fix(release):** drain the 2026-09-02 base-red — rerank-providers import + api-typecheck baseline ratchet ([#12414](https://github.com/diegosouzapw/OmniRoute/pull/12414)) +- **fix(types):** make system prompt injection noImplicitAny-safe ([#12416](https://github.com/diegosouzapw/OmniRoute/pull/12416)) +- **fix(quality):** validate typecheck baseline schema ([#12419](https://github.com/diegosouzapw/OmniRoute/pull/12419)) +- **fix(memory):** resolve rerank provider node cache import ([#12421](https://github.com/diegosouzapw/OmniRoute/pull/12421)) +- **fix(ci):** repair release-root regressions — pack dedup, web-session syntax, uc-image ids ([#12423](https://github.com/diegosouzapw/OmniRoute/pull/12423)) — thanks @backryun +- **fix(providers):** separate MaxAI and UC credential contracts ([#12431](https://github.com/diegosouzapw/OmniRoute/pull/12431)) +- **fix(cli):** preserve Claude settings on config set ([#12432](https://github.com/diegosouzapw/OmniRoute/pull/12432)) — thanks @KrzysiekSko +- **fix(providers):** repair the maxai credential block truncated by merge auto-resolve ([#12433](https://github.com/diegosouzapw/OmniRoute/pull/12433)) +- **fix(quality):** record the 2026-09-02 merged growth in the file-size baseline ([#12434](https://github.com/diegosouzapw/OmniRoute/pull/12434)) +- **fix(uc):** route UC error strings through sanitizeErrorMessage; allowlist the retired codex id ([#12437](https://github.com/diegosouzapw/OmniRoute/pull/12437)) +- **fix(adapta):** redact streamed upstream errors ([#12438](https://github.com/diegosouzapw/OmniRoute/pull/12438)) +- **fix(sse):** remap non-contiguous upstream tool_calls index to a gap-free output_index ([#12445](https://github.com/diegosouzapw/OmniRoute/pull/12445)) — thanks @hartmark +- **fix(quality):** detect duplicate tool_calls entries in one response ([#12446](https://github.com/diegosouzapw/OmniRoute/pull/12446)) — thanks @hartmark +- **fix(conversations):** resolve turn content OmniRoute never sends back to the client ([#12447](https://github.com/diegosouzapw/OmniRoute/pull/12447)) — thanks @hartmark +- **fix(combo):** do not treat credits-exhausted 401 as auth skip ([#12449](https://github.com/diegosouzapw/OmniRoute/pull/12449)) — thanks @RaviTharuma +- **fix(auth):** do not park healthy quota accounts as expired ([#12452](https://github.com/diegosouzapw/OmniRoute/pull/12452)) — thanks @RaviTharuma +- **fix(responses-continuation):** fail closed on a collector-truncated, empty output array ([#12460](https://github.com/diegosouzapw/OmniRoute/pull/12460)) — thanks @hartmark +- **fix(logging):** raise the SSE payload collector's default cap ([#12461](https://github.com/diegosouzapw/OmniRoute/pull/12461)) — thanks @hartmark +- **fix(resilience):** surface Responses failed.error.message in 502s ([#12472](https://github.com/diegosouzapw/OmniRoute/pull/12472)) — thanks @RaviTharuma +- **fix(security):** unbiased maxai X-Random nonce + stricter URL/regex assertions ([#12502](https://github.com/diegosouzapw/OmniRoute/pull/12502)) +- **fix(video):** re-anchor transcript log-redaction so PII/credential maskers can't reopen the leak (#12150 P1 follow-up) ([#12503](https://github.com/diegosouzapw/OmniRoute/pull/12503)) +- **fix(monitoring):** serve cached credentialHealth off the request path ([#12533](https://github.com/diegosouzapw/OmniRoute/pull/12533)) — thanks @RaviTharuma +- **fix(quota):** keep Antigravity Gemini usable when Claude weekly is empty ([#12566](https://github.com/diegosouzapw/OmniRoute/pull/12566)) — thanks @HouMinXi +- **fix(authz):** hard-gate every credential export and CLI-config write (GHSA-5926-2w35-7h4q) ([#12600](https://github.com/diegosouzapw/OmniRoute/pull/12600)) +- **fix(docs):** restore the Next build — REMOVED_PROVIDERS.md had no frontmatter (base-red #12581) ([#12610](https://github.com/diegosouzapw/OmniRoute/pull/12610)) +- **fix(quality):** re-point the zcodeProtocol public-creds allowlist to line 313 ([#12615](https://github.com/diegosouzapw/OmniRoute/pull/12615)) +- **fix(security):** close 3 advisories — search baseUrl exfil, sk- in the error sanitizer, bifrost relay header leak ([#12620](https://github.com/diegosouzapw/OmniRoute/pull/12620)) +- **fix(providers):** sanitize boolean required and nested bare maps for Gemini (#12269) ([#12624](https://github.com/diegosouzapw/OmniRoute/pull/12624)) — thanks @HouMinXi +- **fix(resilience):** keep Overloaded STREAM_EARLY_EOF off the provider breaker ([#12626](https://github.com/diegosouzapw/OmniRoute/pull/12626)) — thanks @HouMinXi +- **fix(catalog):** re-audit free-tier quotas against official pages ([#12649](https://github.com/diegosouzapw/OmniRoute/pull/12649)) +- **fix(dashboard):** keep a request's pending-tracking id stable across combo target retries ([#12650](https://github.com/diegosouzapw/OmniRoute/pull/12650)) — thanks @hartmark +- **fix(tests):** retire dead model ids from the chat-pipeline integration suite (base-red #12581) ([#12670](https://github.com/diegosouzapw/OmniRoute/pull/12670)) +- **fix:** resolve SqliteError no such table compression_run_telemetry during cleanup ([#12682](https://github.com/diegosouzapw/OmniRoute/pull/12682)) — thanks @groovecityJO +- **fix(combo):** treat a pin-only step as implicit connection allowlist ([#12697](https://github.com/diegosouzapw/OmniRoute/pull/12697)) — thanks @HouMinXi +- **fix(ci):** ship .npmrc in published package so legacy-peer-deps applies to consumers (#11544) ([#12699](https://github.com/diegosouzapw/OmniRoute/pull/12699)) — thanks @KooshaPari +- **fix(docker):** pin 4 CLI tools to exact versions (#12576) ([#12703](https://github.com/diegosouzapw/OmniRoute/pull/12703)) — thanks @KooshaPari +- **fix(docker):** document and harden cli profile trust boundary (#12570) ([#12706](https://github.com/diegosouzapw/OmniRoute/pull/12706)) — thanks @KooshaPari +- **fix(sse):** redact video transcript in pre-guardrail rejected-request logs (#12150 P2 item 7) ([#12710](https://github.com/diegosouzapw/OmniRoute/pull/12710)) +- **fix(i18n):** quote placeholder in OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES description (#12505) ([#12769](https://github.com/diegosouzapw/OmniRoute/pull/12769)) — thanks @KooshaPari +- **fix(api/pricing):** surface validation error message as string, not raw object (#12494) ([#12771](https://github.com/diegosouzapw/OmniRoute/pull/12771)) — thanks @KooshaPari +- **fix(sse):** re-enable prompt compression for native Codex passthrough ([#12834](https://github.com/diegosouzapw/OmniRoute/pull/12834)) — thanks @thomasmaerz +- **fix(dashboard):** model health tests for a provider node set to the Responses API now call `/v1/responses` with a Responses-shaped body instead of `/v1/chat/completions` — those models were reported as `Provider returned HTTP 200 but no text content` even though the same model answered normally through `/v1/responses` ([#13070](https://github.com/diegosouzapw/OmniRoute/issues/13070)) ([#13087](https://github.com/diegosouzapw/OmniRoute/pull/13087)) — thanks @ntdatt812 +- fix(routing): stop a round-robin combo's "opencode" targets from collapsing onto the opencode-zen connection (#11912) ([#13283](https://github.com/diegosouzapw/OmniRoute/pull/13283)) +- fix(routing): stop the reactive-compaction debug log from lying when compression is globally disabled (#11977) ([#13276](https://github.com/diegosouzapw/OmniRoute/pull/13276)) +- fix(electron): relativize standalone-bundle symlink targets so Stage 8 manifest verification stops failing on Windows (#11979) ([#13251](https://github.com/diegosouzapw/OmniRoute/pull/13251)) +- fix(dashboard): surface a visible error when Compression Studio's combined preview run fails (#12061) ([#13288](https://github.com/diegosouzapw/OmniRoute/pull/13288)) +- fix(dashboard): make the compression "Effective pipeline" preview honor the active profile and warn when the master switch is off (#12063) ([#13287](https://github.com/diegosouzapw/OmniRoute/pull/13287)) +- fix(providers): scope TinyCMS Web signer's DOM shims to each call instead of leaking them for the process lifetime, and surface a clean HTTP status on a non-JSON interception-toggles error (#12072) ([#13254](https://github.com/diegosouzapw/OmniRoute/pull/13254)) +- fix(guardrails): stop Vision Bridge from re-selecting a model locked after a 404 (#12111) ([#13259](https://github.com/diegosouzapw/OmniRoute/pull/13259)) +- fix(sse): require Responses-shaped body before native OpenAI-compatible passthrough (#12129) ([#13278](https://github.com/diegosouzapw/OmniRoute/pull/13278)) +- fix(providers): minimax-m3 now collapses manual thinking.type:"enabled" to adaptive, preventing upstream 400 (2013) (#12132) ([#13249](https://github.com/diegosouzapw/OmniRoute/pull/13249)) +- fix(db): scope model visibility overrides by modality so hiding a Chat model no longer hides an identically-ID'd Image/Embeddings/etc. model (#12172) ([#13258](https://github.com/diegosouzapw/OmniRoute/pull/13258)) +- fix(db): scope local-provider apiKey dedup to matching base URL so LM Studio/Ollama-style connections support multiple accounts (#12173) ([#13250](https://github.com/diegosouzapw/OmniRoute/pull/13250)) +- fix(providers): refresh Trae's stale Referer/Origin and forward user timezone so imported connections stop failing with 401 (#12190) ([#13255](https://github.com/diegosouzapw/OmniRoute/pull/13255)) +- fix(providers): route opencode-go/gpt-5.6-luna to /responses instead of /chat/completions (#12196) ([#13275](https://github.com/diegosouzapw/OmniRoute/pull/13275)) +- fix(dashboard): allow deleting the last extra-upstream-header row even when invalid (#12251) ([#13252](https://github.com/diegosouzapw/OmniRoute/pull/13252)) +- **fix(i18n):** translate pre-existing `__MISSING__:` keys for `combo.sort`, `requestLogger.detail` expand/collapse, `common.profile`, and `settings.resilienceCredentialHealth*` across 39 locales ([#12272](https://github.com/diegosouzapw/OmniRoute/issues/12272)) ([#13034](https://github.com/diegosouzapw/OmniRoute/pull/13034)) — thanks @HouMinXi +- fix(cli): run the Node.js runtime compatibility guard before the heavy `tsx/esm` + Commander import chain so an unsupported runtime gets a clear message instead of a raw `Invalid regular expression flags` crash (#12296) ([#13253](https://github.com/diegosouzapw/OmniRoute/pull/13253)) +- fix(dashboard): refresh the providers list after deleting a compatible provider node (#12298) ([#13256](https://github.com/diegosouzapw/OmniRoute/pull/13256)) +- fix(usage): fail closed on API-key budget enforcement when a provider's `auto` routing alias has no pricing row, instead of silently counting it as $0 (#12341) ([#13257](https://github.com/diegosouzapw/OmniRoute/pull/13257)) +- **fix(providers):** include the submitted Agnes video model when polling by `video_id` ([#12356](https://github.com/diegosouzapw/OmniRoute/pull/12356)) — thanks @pacocartones +- **fix(routing):** custom OpenAI-compatible nodes now honor the saved Chat/Responses API type after edits instead of letting the node's original ID prefix override the live connection setting ([#11884](https://github.com/diegosouzapw/OmniRoute/issues/11884)). ([#12358](https://github.com/diegosouzapw/OmniRoute/pull/12358)) — thanks @pacocartones +- fix(sse): surface an error instead of a silent empty 200 when a Claude stream closes with zero bytes (#12398) ([#13285](https://github.com/diegosouzapw/OmniRoute/pull/13285)) +- fix(oauth): warn before the dead localhost:8080 redirect in antigravity/gemini `oauth start` (#12413) ([#13265](https://github.com/diegosouzapw/OmniRoute/pull/13265)) +- fix(providers): stop devin-cli spawn error from double-closing the SSE controller (#12517) ([#13261](https://github.com/diegosouzapw/OmniRoute/pull/13261)) +- **fix(rerank):** clamp Voyage `top_k` to the documents actually sent after empty-string filtering, and honor `return_documents: false` in the NVIDIA response adapter (#12523 — thanks @pacocartones) +- **fix(audio):** `/v1/audio/translations` now resolves combo names the way `/v1/audio/transcriptions` already does, so a combo that `GET /v1/models` advertises is fanned out to its targets instead of being rejected with `400 Invalid translation model: . Use format: provider/model`; literal `provider/model` ids and unknown bare names behave as before (#12536 — thanks @pacocartones) +- **fix(gemini):** strip the JSON-Schema-2020-12 `prefixItems` keyword from Gemini tool schemas at every nesting level, so Claude Code tool definitions no longer fail with `400 Unknown name "prefixItems"` on Gemini models (#12540 — thanks @pacocartones) +- **fix(api-manager):** Expose an accessible loading status while API keys are fetched instead of an empty accessibility tree (#12541 — thanks @pacocartones) +- **fix(video):** Clamp `estimateJpegFrameBytes` at zero for padding-only payloads and build the three encode-side frame data URIs from `JPEG_FRAME_DATA_URI_PREFIX` instead of a repeated literal (#12543 — thanks @pacocartones) +- **fix(devin):** accept Windows `DEVIN_AGENTIC_HOME` sandbox paths (`C:\...\.sandbox\...`) in the isolated-home check so the Devin Claude Bridge no longer fails closed on Windows ([#12405](https://github.com/diegosouzapw/OmniRoute/issues/12405)) (#12545 — thanks @pacocartones) +- **fix(db):** Add `conversation_turn_nodes` and orphaned `agentic_conversations` to the auto-cleanup cycle under the existing `retention.callLogs` window, so identity nodes whose call-log content has already been purged no longer accumulate without bound in `storage.sqlite` (#12548 — thanks @pacocartones) +- **fix(i18n):** Wrap the `~/.claude/profiles//settings.json` placeholder in ICU single quotes in the `OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES` feature-flag description across all 42 locales and the TypeScript default, so next-intl no longer fails with `INVALID_MESSAGE: UNCLOSED_TAG` and the Feature Flags card shows the description instead of the raw key (#12549 — thanks @pacocartones) +- **fix(orchestration):** `updateCloudAgentTask` now publishes the task's real `status` on `agent.task.updated` when an update only touches `result`, `activities` or `error`, instead of the fabricated `"updated"` state, and stays silent when no row matched the id (#12550 — thanks @pacocartones) +- **fix(i18n):** the home "Recent Requests" panel and the Provider Topology legend are now translated instead of rendering English copies on non-English dashboards; the legend reads its own `home.topologyLegend*` labels with consistent casing rather than borrowing the memory-settings "Recent" and analytics "Error" strings (#12551 — thanks @pacocartones). +- **docs(reference):** bring the `FEATURE_FLAGS.md` catalog back to 1:1 with `featureFlagDefinitions.ts` — 20 missing flags added, the two `*_BLOCK_THRESHOLD` env-only knobs moved out of the flag tables, category/total counts and the Live WS port corrected, guarded by a static test (#12552 — thanks @pacocartones) +- **fix(i18n):** backfill missing `usage.kiloPass*` strings in 39 locales and restore `featureFlagOmnirouteDisableThinkingLevelVariantsDescription` in `pt.json` ([#12561](https://github.com/diegosouzapw/OmniRoute/issues/12561)) ([#13027](https://github.com/diegosouzapw/OmniRoute/pull/13027)) — thanks @HouMinXi +- fix(docker): default docker-compose app ports (dashboard/API/live-WS) to loopback instead of `0.0.0.0`, closing the anonymous `/v1` LAN/WAN exposure gap left open by `REQUIRE_API_KEY=false` (#12568) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): close DNS-rebinding SSRF gap in webhook outbound-URL guard (#12569) ([#13243](https://github.com/diegosouzapw/OmniRoute/pull/13243)) +- fix(docker): require a per-session token on the VNC browser CDP bridge and isolate it on a dedicated Docker network (#12571) ([#13241](https://github.com/diegosouzapw/OmniRoute/pull/13241)) +- fix(open-sse): write Adobe Firefly session tokens and cookie jars with 0700/0600 permissions instead of the process umask (#12572) ([#13242](https://github.com/diegosouzapw/OmniRoute/pull/13242)) +- fix(api): remove hardcoded wildcard CORS in openai-to-gemini-sse.ts so the centralized fail-closed CORS gate is the sole source of `Access-Control-Allow-Origin` (#12573) ([#13239](https://github.com/diegosouzapw/OmniRoute/pull/13239)) +- fix(api): enforce API key policy (budget/rate-limit/schedule/endpoint scoping) on the ElevenLabs speech-to-text, text-to-speech and voices proxy routes (#12574) ([#13244](https://github.com/diegosouzapw/OmniRoute/pull/13244)) +- fix(sse): cap HuggingChat NDJSON body size and bound the read loop with the fetch timeout so a stalled or hostile upstream cannot buffer unbounded memory (#12577) ([#13282](https://github.com/diegosouzapw/OmniRoute/pull/13282)) +- fix(docker): scope the cliproxyapi/qdrant/bifrost sidecars to loopback by default and forward `CLIPROXYAPI_MANAGEMENT_KEY` into the cliproxyapi container so its management API is not left both unauthenticated and LAN-published (#12578) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): create DB export temp paths with `fs.mkdtempSync` instead of predictable timestamps (#12579) ([#13237](https://github.com/diegosouzapw/OmniRoute/pull/13237)) +- Cline 401 bodies that say "re-authenticate your Cline account" classify as a refreshable OAuth token, not a terminal expired key. The cooling panel no longer labels every cooldown as a 429; it shows the recorded last error instead. (#12594) ([#13060](https://github.com/diegosouzapw/OmniRoute/pull/13060)) — thanks @HouMinXi +- **fix(catalog):** degrade unknown combo targets instead of dropping LCD modalities, and persist OpenRouter `architecture.input_modalities` into the capability snapshot ([#12613](https://github.com/diegosouzapw/OmniRoute/issues/12613)) ([#13042](https://github.com/diegosouzapw/OmniRoute/pull/13042)) — thanks @HouMinXi +- fix(providers): send `x-api-key` instead of `Authorization: Bearer` for OpenCode Zen's `/v1/responses` endpoint (Muse Spark Contributor models), fixing a 401 on OmniRoute's auth header (#12633) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(sse): surface the actionable "Auggie CLI not found" message when the shell reports a missing `auggie` binary via exit code instead of a spawn error (#12645) ([#13263](https://github.com/diegosouzapw/OmniRoute/pull/13263)) +- **fix(config):** Persist deletions of built-in background-degradation entries — when a stored settings record exists its `degradationMap` is now authoritative instead of being merged under the defaults, so an entry the user removed in the dashboard no longer reappears on the next apply or restart ([#12424](https://github.com/diegosouzapw/OmniRoute/issues/12424)) ([#12647](https://github.com/diegosouzapw/OmniRoute/pull/12647)) — thanks @pacocartones +- **fix(gamification):** action-count badge milestones (First Token, Token Consumer, Token Machine, Token Whale, and the token-sharing tier) are now backed by a durable `xp_action_counts` counter incremented in `addXp()`, instead of a live `COUNT(*)` over `xp_audit_log`. The audit log is pruned by `retention.xpAuditLog` (default 30 days), so on a default install those "lifetime" milestones were really "actions in the last 30 days" and unlocked badges could stop unlocking once old rows aged out. `getActionCount()` and `checkActionCountBadges()` now read the same durable source, and a migration backfills existing totals from the surviving audit rows ([#12546](https://github.com/diegosouzapw/OmniRoute/issues/12546)) ([#12651](https://github.com/diegosouzapw/OmniRoute/pull/12651)) — thanks @pacocartones +- **fix(images):** `/v1/images/edits` now iterates a combo's targets the same way `/v1/images/generations` does (#9239) instead of flattening a bare combo to its first target. A combo whose first target is not edit-capable — or lacks credentials — now falls through to a later edit-capable target rather than hard-erroring, and missing credentials are skipped (not a hard `401`) to match the generations path. The per-target skip/terminal classification is extracted into a shared `runImageComboTargets` loop, so generations behavior is unchanged ([#12547](https://github.com/diegosouzapw/OmniRoute/issues/12547)). ([#12653](https://github.com/diegosouzapw/OmniRoute/pull/12653)) — thanks @pacocartones +- fix(sse): add first-byte watchdog to the TLS-fingerprint transport so a stalled wreq body falls back instead of hanging for minutes (#12656) ([#13272](https://github.com/diegosouzapw/OmniRoute/pull/13272)) +- fix(sse): exempt tiny-budget reasoning probes from combo quality failure and surface persisted-cooldown skips in ALL_TARGETS_SKIPPED diagnostics (#12659) ([#13269](https://github.com/diegosouzapw/OmniRoute/pull/13269)) +- fix(models): declare the real ~1M contextLength for OpenCode Zen's Muse Spark 1.2 models instead of falling back to the 200000 provider default (#12681) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(oauth): align codebuddy-cn OAuth User-Agent with the chat/usage CLI version to avoid WAF false positives (#12702) ([#13264](https://github.com/diegosouzapw/OmniRoute/pull/13264)) +- fix(dashboard): surface an authentication-required banner instead of silently blanking database settings for a guest session (#12709) ([#13270](https://github.com/diegosouzapw/OmniRoute/pull/13270)) +- **fix(resilience):** stop unbounded queue that hangs 6min until Aborted — gate, provider slot, and Bottleneck queue now share a per-connection `maxWaitMs` budget; `fail-closed` on exhaust (503); execution backstop `executionMaxWaitMs` overridable per connection with upstream clamp ([#12715](https://github.com/diegosouzapw/OmniRoute/pull/12715)) — thanks @maxmad64bis (with thanks to @Tushar49 for surfacing the slow-provider need in #12635) +- **fix(auto-combo):** every mode pack now carries `quality` and `reliability` weights (`quality` 0.02, 0.03 in `quality-first`; `reliability` 0.03, 0.04 in `reliability-first`), so selecting a pack no longer silences either signal; `DEFAULT_WEIGHTS` is unchanged ([#12731](https://github.com/diegosouzapw/OmniRoute/pull/12731)) — thanks @maxmad64bis +- **fix(sse):** the provider execution pipeline keeps the upstream error `code`/`type` again — both were lost when the non-streaming leg was extracted, so a config-class `422` (Antigravity missing project) degraded into a generic account cooldown instead of reaching the gate that recognises it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(db):** a legacy `call_logs` table no longer aborts startup — the provider-stats index is created after the column healing runs, not before it, so an install predating `request_type` opens instead of failing with `no such column` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(catalog):** a malformed row in the operator-writable custom-models blob no longer kills every `auto/*` pool ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(tests):** realign the guards that the same day's merges left asserting the old behavior — the provider-count total after GreenPT and EURouter, the GLM stream buffer slot that `#12925` turned into a declared parameter, and the injection scan bound that `#13104` reshaped from a head-only window into head-plus-tail ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- fix(cache): fold tool_choice/tools/response_format into the semantic cache signature so a cached tool_calls response can no longer be replayed for a request whose tool policy forbids it (#12734) ([#13267](https://github.com/diegosouzapw/OmniRoute/pull/13267)) +- **fix(models):** stop treating provider-supplied `isFree:true`/`:free`/`0/0` flags as trusted unless the provider has a documented free tier — only the shipped free-tier catalog decides otherwise; fetched `isFree:true` trusted only for a free-tier provider, custom `isFree:true` via a trusted path ([#12744](https://github.com/diegosouzapw/OmniRoute/pull/12744)) — thanks @maxmad64bis +- fix(memory): authenticate the internal /v1/rerank loopback call so memory reranking no longer silently degrades to unranked order when REQUIRE_API_KEY=true (#12745) ([#13268](https://github.com/diegosouzapw/OmniRoute/pull/13268)) +- fix(sse): parse Ollama Cloud's current usage markup (`$X of $Y used` aria-label, nested width style) (#12749) ([#13260](https://github.com/diegosouzapw/OmniRoute/pull/13260)) +- fix(cli): setup-opencode no longer sends an active context's management token to `/v1/models` when `--api-key`/`OMNIROUTE_API_KEY` is supplied — an explicit flag or the env var now always outranks the context's token, and the flag itself is no longer swallowed by the parent program's global `--api-key` option (#12783) ([#13246](https://github.com/diegosouzapw/OmniRoute/pull/13246)) +- fix(sse): register Arcee AI in the executor provider registry so requests reach api.arcee.ai instead of silently falling back to OpenAI (#12784) ([#13277](https://github.com/diegosouzapw/OmniRoute/pull/13277)) +- **fix(quota):** share one upstream quota read when identical saturation checks arrive at the same time, so traffic bursts don't multiply provider API calls ([#12787](https://github.com/diegosouzapw/OmniRoute/pull/12787)) — thanks @maxmad64bis +- **fix(db):** ignore expired or invalid rate-limit cooldown writes so a stale timestamp can't lock a connection that should be usable — clearing still works as before ([#12788](https://github.com/diegosouzapw/OmniRoute/pull/12788)) — thanks @maxmad64bis +- **fix(providers):** single source for provider order with `xao` ranking alongside `xai-oauth` ([#12790](https://github.com/diegosouzapw/OmniRoute/pull/12790)) — thanks @maxmad64bis +- **fix(routing):** off-table models check the free-model catalog before inheriting premium prices, latency bootstraps from the observed pool median, async tiers read live database pricing with a 90-day freshness gate, and the tier cache invalidates on every pricing write ([#12792](https://github.com/diegosouzapw/OmniRoute/pull/12792)) — thanks @maxmad64bis +- **fix(auto-combo):** snapshot scoring uses the observed per-connection account tier and per-model quality instead of neutral constants, and circuit-open providers rank lower at build time ([#12794](https://github.com/diegosouzapw/OmniRoute/pull/12794)) — thanks @maxmad64bis +- **fix(pool):** empty `auto/*` pools now say why they are empty, and the models listing uses the same paid check as routing ([#12795](https://github.com/diegosouzapw/OmniRoute/pull/12795)) — thanks @maxmad64bis +- fix(routing): recognize CLIProxyAPI's 'unknown provider for model' 400 as fallback-worthy (#12800) ([#13284](https://github.com/diegosouzapw/OmniRoute/pull/13284)) +- **fix(sse):** translate-mode streams now emit the estimated token counts as a trailing usage-only chunk before `[DONE]` when the upstream stays silent, so metered chat clients see totals instead of nothing ([#12828](https://github.com/diegosouzapw/OmniRoute/pull/12828)) — thanks @maxmad64bis +- **fix(db):** provider stats stay truthful on empty and legacy databases: fallback counts default to `0` instead of `null`, latency averages read `null` (not `0`) when no durations were recorded, failures logged before the error-type column existed group under `pre_migration` instead of `unclassified`, and per-provider queries use two new composite indexes ([#12832](https://github.com/diegosouzapw/OmniRoute/pull/12832)) — thanks @maxmad64bis +- fix(nvidia): fail open when a synced model catalog goes stale instead of gating forever (#12849) ([#13248](https://github.com/diegosouzapw/OmniRoute/pull/13248)) +- **fix(db):** WAL maintenance lives in its own module: a `TRUNCATE` checkpoint that hits a busy database now warns with its streak and retries once via `PASSIVE` instead of logging success, and closing-time checkpoints no longer report success on builds without a database file, with the busy totals visible in the authenticated monitoring health payload ([#12853](https://github.com/diegosouzapw/OmniRoute/pull/12853)) — thanks @maxmad64bis +- **fix(health):** quota with no snapshots reads empty instead of a contradicting 0% ([#12857](https://github.com/diegosouzapw/OmniRoute/pull/12857)) — thanks @maxmad64bis +- fix(a2a): accept the dashboard's own session cookie on /a2a so "Run message/send" no longer fails with "Unauthorized: missing or invalid API key" (#12888) ([#13271](https://github.com/diegosouzapw/OmniRoute/pull/13271)) +- **fix(logs):** the Logs grid's in-memory filter pass no longer discards rows the SQL query already matched — selecting an API key from the dropdown (which sends the key's id) returns its calls again, the Combo tab shows every combo instead of only those whose name contains a "1", and the model filter and search cover the same columns as the query ([#12896](https://github.com/diegosouzapw/OmniRoute/pull/12896)) — fixes [#12873](https://github.com/diegosouzapw/OmniRoute/issues/12873) — thanks @ntdatt812 +- **fix(a2a):** `/api/a2a/status` now builds the agent card from the request that asked for it, so a gateway reached at a non-localhost host no longer advertises `http://localhost:20128` as its A2A URL ([#12918](https://github.com/diegosouzapw/OmniRoute/pull/12918)). — thanks @ntdatt812 +- **fix(compression):** progressive aging now appends its `[COMPRESSED:aging:…]` annotation after a turn's `tool_result` blocks instead of in front of them, so Anthropic no longer rejects aged conversations with "`tool_use` ids were found without `tool_result` blocks immediately after" ([#12920](https://github.com/diegosouzapw/OmniRoute/pull/12920)). — thanks @ntdatt812 +- **fix(bedrock):** model import now resolves context limits for every vendor prefix instead of only `anthropic.*`, so `global.openai.gpt-5.6-*` no longer imports with a null `inputTokenLimit` and gets rejected pre-flight at the 200k default ([#12921](https://github.com/diegosouzapw/OmniRoute/pull/12921)). — thanks @ntdatt812 +- **fix(stream):** the 64 KB stream buffer GLM asks for is honoured instead of dropped, and the type error it caused no longer fails the API Route Typecheck gate on every open PR ([#12925](https://github.com/diegosouzapw/OmniRoute/pull/12925)) — thanks @ntdatt812 +- **fix(guardrails):** mask PII inside a `tool_result`'s nested content array, which the masker walked past while redacting its sibling block ([#12930](https://github.com/diegosouzapw/OmniRoute/pull/12930)) — thanks @ntdatt812 +- **fix(dashboard):** the Radar catalog table no longer leaves absent data unexplained — empty limits, unknown context windows, and unreported capabilities each explain themselves on hover, and a new check keeps it that way ([#12937](https://github.com/diegosouzapw/OmniRoute/pull/12937)) — thanks @maxmad64bis +- **fix(sse):** geo-blocked opencode requests rotate to the next account proxy instead of failing, so one refused egress no longer aborts the whole chain ([#12941](https://github.com/diegosouzapw/OmniRoute/pull/12941)) — thanks @maxmad64bis +- **fix(codex):** dashboard "clear cooldown" and the CAS recovery path now drop nested `codexScopeRateLimitedUntil` maps in the same write that nulls `rate_limited_until`, and fresh quota snapshots with headroom lift fallback-sourced scope cooldowns parked by quota preflight ([#12817](https://github.com/diegosouzapw/OmniRoute/issues/12817), [#12860](https://github.com/diegosouzapw/OmniRoute/issues/12860), [#12951](https://github.com/diegosouzapw/OmniRoute/pull/12951)) The dashboard reset-credit button (`consumeCodexResetCredit`) now exercises that same snapshot path after a successful redeem, so a filled quota bar is enough to unpark a leftover fallback Codex child without another manual clear. — thanks @HouMinXi +- **fix(db):** resolve `sql-wasm.wasm` across global npm install and hoisted layouts, ensuring OmniRoute can boot cleanly on Node 24 when native `better-sqlite3` is uncompiled. ([#13035](https://github.com/diegosouzapw/OmniRoute/pull/13035)) — thanks @HouMinXi +- **fix(api):** `DELETE /v1/batches/delete-completed` now sweeps only the calling API key's own completed batches (batches with no owner stay out of a key-scoped sweep on purpose), with an explicit instance-wide mode reserved for authenticated dashboard sessions, a 401 for a presented key that is unknown, revoked, deactivated, banned or expired (never falling through to the session branch), audit logging of every sweep, a sanitized 500 on failure, an owner-scoped file half (a key-scoped sweep never nulls a file another tenant owns) and an atomic sweep — chunked in 200-batch transactions in instance mode — so a mid-way error never leaves a batch pointing at a nulled file (GHSA-wvxc-jp3v-5mg5) ([#12969](https://github.com/diegosouzapw/OmniRoute/pull/12969)) +- **fix(combo):** quota-weighted routing stops drawing on an out-of-credit connection — a 402 now invalidates the stored quota snapshot instead of leaving its stale remaining percentage in place, and a snapshot older than 10 minutes no longer counts as confident headroom for the primary pool ([#12972](https://github.com/diegosouzapw/OmniRoute/pull/12972)) ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- **fix(vision):** Custom Models with "Vision capable" checked no longer have image requests swapped to `glm/glm-4.6v` when the client sends the advertised alias (`vllm/path/...`) or the bare path-shaped id — Vision Bridge now matches the stored override for all three id forms ([#12758](https://github.com/diegosouzapw/OmniRoute/issues/12758)) ([#12974](https://github.com/diegosouzapw/OmniRoute/pull/12974)) — thanks @HouMinXi +- **fix(sse):** transient opencode upstream failures rotate to the next account proxy instead of failing, so one flapping egress no longer aborts the whole chain ([#12975](https://github.com/diegosouzapw/OmniRoute/pull/12975)) — thanks @maxmad64bis +- **fix(azure):** Deployments from GPT-6 onward now send `max_completion_tokens` instead of `max_tokens`, which Azure rejects with HTTP 400. The rule matched a literal `gpt-5`, so each new generation arrived broken; it now matches the generation range, while `gpt-35-turbo` still keeps `max_tokens`. ([#13007](https://github.com/diegosouzapw/OmniRoute/pull/13007)) — thanks @ntdatt812 +- **fix(db):** add an opt-in self-restart circuit for sustained critical memory pressure, gate post-cleanup VACUUM behind a minimum freed-rows threshold, and checkpoint the SQLite WAL every 5 minutes with a size guard that escalates to TRUNCATE, so a growing WAL can no longer stall the event loop into a full outage. ([#13011](https://github.com/diegosouzapw/OmniRoute/pull/13011)) — thanks @HouMinXi +- **fix(auth):** an explicit connection pin may probe a quota-disabled row once and re-enable it on success ([#12874](https://github.com/diegosouzapw/OmniRoute/issues/12874)) ([#13017](https://github.com/diegosouzapw/OmniRoute/pull/13017)) — thanks @HouMinXi +- **fix(api):** thread `X-OmniRoute-Fallback-Attempts` through combo chat completions so streaming and non-streaming responses report how many prior legs were attempted ([#13038](https://github.com/diegosouzapw/OmniRoute/pull/13038)) — thanks @HouMinXi +- Restore provider failure classification and credential refresh on non-streaming requests: classify non-2xx failures to lock models on per-model quota exhaustion, update connection rate limits from headers and body, and pass credential refresh handlers to pipeline execution so 401 tokens can be refreshed and retried (#13043). ([#13069](https://github.com/diegosouzapw/OmniRoute/pull/13069)) — thanks @HouMinXi +- **fix(responses):** wrap forced-non-streaming web_search fallback JSON as Responses SSE so Codex still sees `response.completed` ([#13050](https://github.com/diegosouzapw/OmniRoute/pull/13050)) — thanks @HouMinXi +- **fix(antigravity):** Preserve upstream thought-token usage in normalized completion and reasoning token counts ([#13055](https://github.com/diegosouzapw/OmniRoute/pull/13055)) — thanks @pacocartones +- Fix a concurrency-slot leak in the DeepSeek PoW solver: a worker that failed to spawn (for example a missing worker script) never released its slot, so `MAX_CONCURRENT_WORKERS` failures disabled the solver until restart. ([#13097](https://github.com/diegosouzapw/OmniRoute/pull/13097)) — thanks @anhtahaylove +- **fix(acp):** bound the ACP session output buffers — `stdoutBuffer` and `stderrBuffer` now cap at 1 MiB keeping the most recent output behind a visible `[...output truncated...]` marker, and `stderrBuffer` is reset per prompt instead of accumulating for the lifetime of the session. ([#13100](https://github.com/diegosouzapw/OmniRoute/pull/13100)) — thanks @anhtahaylove +- **fix(acp):** release the `stdout`/`exit` listeners and the idle timer that a `sendPrompt` timeout used to leave attached to the `acpManager` singleton, and drop sessions that exited on their own from the session map instead of keeping them forever. ([#13096](https://github.com/diegosouzapw/OmniRoute/pull/13096)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection and PII scanners now read the text a `tool_result` block carries on `content` (string or nested block list), in messages and in system blocks, so tool output is judged by the same rules as user text ([#13101](https://github.com/diegosouzapw/OmniRoute/pull/13101)) — thanks @ntdatt812 +- **fix(gamification):** close the badge notification SSE stream when the request signal is already aborted before the stream starts — a client that disconnects while the route is still awaiting auth used to leave both the 2s unlock poll and the 15s heartbeat running for the lifetime of the process. ([#13106](https://github.com/diegosouzapw/OmniRoute/pull/13106)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection scan now spends its 16 KB budget on both ends of the request instead of the first 16 KB only, so `system`, `instructions`, `query`, `documents` and the newest turns are no longer hidden behind one long message ([#13104](https://github.com/diegosouzapw/OmniRoute/pull/13104)) — thanks @ntdatt812 +- **fix(dashboard):** expose the Volcano Ark console cookie on quota scraping and unwrap connect-error objects so the dashboard shows the upstream message ([#13107](https://github.com/diegosouzapw/OmniRoute/pull/13107)) — thanks @HouMinXi +- **fix(db):** release the `beforeExit`/`SIGINT`/`SIGTERM` handlers when a `node:sqlite` adapter closes, so a closed adapter and its database handle are no longer pinned to `process` for the lifetime of the run — the same treatment #7494 gave the sql.js adapter. ([#13109](https://github.com/diegosouzapw/OmniRoute/pull/13109)) — thanks @anhtahaylove +- **fix(translator):** `contentSchema` and `unevaluatedItems` are now treated as subschema positions by the tool-schema sanitizer, so a truncation placeholder in either is replaced with a permissive schema instead of being forwarded as a string ([#13110](https://github.com/diegosouzapw/OmniRoute/pull/13110)) — thanks @ntdatt812 +- **fix(cli-helper):** clear the `createLogStream` timeout on the abort path — `stop()` aborts the in-flight fetch and returned through the `signal.aborted` branch, which skipped `clearTimeout` and left an armed timer per stopped stream. The stream reader is now also cancelled when the read loop exits early. ([#13114](https://github.com/diegosouzapw/OmniRoute/pull/13114)) — thanks @anhtahaylove +- **fix(docs):** drop TLS-impersonation, MITM-decrypt, supply-chain attestation, and XOR-mask writeups from the public `/docs` catalog and Docker image. Files stay in git for engineers; operators who need them open the repo, not the website. ([#13136](https://github.com/diegosouzapw/OmniRoute/pull/13136)) — thanks @HouMinXi +- **fix(combo):** parse numeric-epoch `rate_limited_until` in the combo cooldown read path ([#13141](https://github.com/diegosouzapw/OmniRoute/pull/13141)) — thanks @maxmad64bis +- **fix(opencode):** both OpenCode plugins now reject a gateway address typed without `http://` at configuration time, instead of publishing every model with an api url no client can call, and the v2 plugin no longer publishes a model card whose api url is blank or relative ([#13142](https://github.com/diegosouzapw/OmniRoute/pull/13142)) — thanks @maxmad64bis +- **fix(providers):** lock opencode model on upstream 400 model-unavailable ([#13146](https://github.com/diegosouzapw/OmniRoute/pull/13146)) — thanks @maxmad64bis +- **fix(logging):** keep the provider exchange rather than the raw client bodies when a call log exceeds its size budget, and show that recovered payload in the request-detail panel instead of replacing it with the stored response body ([#13147](https://github.com/diegosouzapw/OmniRoute/pull/13147)) — thanks @maxmad64bis +- **fix(traffic-inspector):** the WebSocket route no longer leaks a traffic-buffer subscriber and a 30s ping timer when the client socket is already closed at handler time — listeners are attached before any resource is acquired, a destroyed socket bails out early, and the ping interval stops on a dead socket where `write()` never throws ([#13155](https://github.com/diegosouzapw/OmniRoute/pull/13155)) — thanks @anhtahaylove +- **fix(telegram):** bound the per-user API key cache in the Telegram chat proxy so a burst of distinct chat ids can no longer grow the process heap without limit ([#13165](https://github.com/diegosouzapw/OmniRoute/issues/13165)) ([#13166](https://github.com/diegosouzapw/OmniRoute/pull/13166)) — thanks @anhtahaylove +- **fix(stream):** cancel the upstream response body when the JSON-to-SSE sniff unwinds on a body timeout, so a stalled upstream no longer pins the connection ([#13169](https://github.com/diegosouzapw/OmniRoute/issues/13169)) ([#13171](https://github.com/diegosouzapw/OmniRoute/pull/13171)) — thanks @anhtahaylove +- **fix(telegram):** authenticate webhook deliveries with Telegram's `secret_token` so an unauthenticated caller can no longer mint API keys or spend upstream quota ([#13172](https://github.com/diegosouzapw/OmniRoute/issues/13172)) ([#13175](https://github.com/diegosouzapw/OmniRoute/pull/13175)) — thanks @anhtahaylove +- **fix(models):** give discoverable Gemini 3.8 Flash ids their own 65536 output spec so Antigravity no longer clamps them to 16384 ([#13195](https://github.com/diegosouzapw/OmniRoute/pull/13195)) — thanks @HouMinXi +- **fix(dashboard):** leftover catalog-removed provider rows (gemini-cli) can be listed and purged from the providers page ([#13067](https://github.com/diegosouzapw/OmniRoute/issues/13067)) ([#13197](https://github.com/diegosouzapw/OmniRoute/pull/13197)) — thanks @HouMinXi +- **fix(auth):** a dashboard session now requires the `authenticated: true` claim that login, OIDC and the session refresh already emit — a JWT merely signed with `JWT_SECRET` (for example the Cursor CLI passthrough token, which any API-key holder can obtain) no longer verifies as the `auth_token` cookie on any route, the WebSocket handshake or the live server; existing sessions keep working ([#13298](https://github.com/diegosouzapw/OmniRoute/issues/13298)) ([#13375](https://github.com/diegosouzapw/OmniRoute/pull/13375)) +- **fix(api):** `DELETE /v1/batches/delete-completed` now applies the caller's API-key policy (endpoint allowlist, schedule, usage cap, rate limit) like every other `/v1` route, commits the key-scoped sweep per 200-batch chunk instead of holding one write lock for the whole sweep, refuses to loop without progress, and rejects a scope that names both a key and `allTenants` ([#13684](https://github.com/diegosouzapw/OmniRoute/pull/13684)) +- Restore the API-route typecheck gate: the non-streaming leg result lost its discriminated-union narrowing after the server-owned tool loop reassignment, producing 13 new TS2339 diagnostics in `chatCore.ts`. ([#12963](https://github.com/diegosouzapw/OmniRoute/pull/12963)) +- Restore four non-streaming behaviours the server-owned tool loop refactor dropped: client aborts map to 499 with the fixed `Request aborted` message, an aborted request no longer logs a synthetic `clientResponse`, Claude prompt-cache telemetry is recorded again, and a body that cannot be canonicalized no longer throws when the tool loop is off. ([#12990](https://github.com/diegosouzapw/OmniRoute/pull/12990)) +- **fix(skills):** The CLI registry parser now reads positionals declared with `.addArgument()`, not only those written inline in `.command()`. `tunnel create [type]` was being published as `tunnel create`, so the agent-skills sync gate reported drift on every branch and regenerating would have deleted the argument. ([#13009](https://github.com/diegosouzapw/OmniRoute/pull/13009)) — thanks @ntdatt812 +- **fix(combos):** dashboard combo test uses a short prompt, serial probes, and a 60s timeout so reasoning models and rate-limited free pools do not fail the health check ([#13001](https://github.com/diegosouzapw/OmniRoute/pull/13001)) — thanks @HouMinXi +- fix(compression): terminate idle worker threads on eviction so long-running instances stop leaking OS threads and MessagePorts ([#13091](https://github.com/diegosouzapw/OmniRoute/pull/13091)) — thanks @anhtahaylove +- Redact Google API keys of any length in error bodies: the pattern required exactly 39 characters, so shorter or longer `AIza…` credentials passed through unredacted. ([#12964](https://github.com/diegosouzapw/OmniRoute/pull/12964)) +- **fix(grok-cli):** a 402 "Grok Build usage balance exhausted" parks that Grok login as out of credit (Grok Build CLI, grok.com cookie, and xAI OAuth share the weekly pool). Combo routing then tries the next login instead of locking the model for every account in the pool ([#13061](https://github.com/diegosouzapw/OmniRoute/pull/13061)) — thanks @HouMinXi +- fix(compression): spawn the LLMLingua worker with a file URL object so compression actually runs instead of silently failing open on Node ([#13093](https://github.com/diegosouzapw/OmniRoute/pull/13093)) — thanks @anhtahaylove +- **fix(test):** run the local `test` and `test:unit` scripts at concurrency 4 so a full-suite run no longer exhausts the machine's commit charge and kills unrelated processes ([#13187](https://github.com/diegosouzapw/OmniRoute/pull/13187)) — thanks @anhtahaylove +- **fix(monitoring):** `GET /api/monitoring/health` `credentialHealth` now includes a bounded `failedConnections` list (`connectionId`, `status`, sanitized `lastError`) when the probe-cache gauge `failed>0`, plus `source: probe-cache` and a cheap `staleDbNonOkCount` for sticky SQLite `test_status` on active rows. Documents that the live gauge is not `provider_connections.test_status`. ([#12876](https://github.com/diegosouzapw/OmniRoute/pull/12876)) — thanks @RaviTharuma +- **fix(resilience):** an openai-compatible multi-upstream gateway no longer marks the whole connection `credits_exhausted` when a single upstream (or the `/models` / representative-model health probe) returns 402. Quota failures stay model-scoped; true connection-wide auth failures are unchanged. Single-credential 402 key disable ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239)) is preserved. ([#12875](https://github.com/diegosouzapw/OmniRoute/pull/12875)) — thanks @RaviTharuma +- **fix(dashboard):** nine fixes on the Orchestration Canvas, all diagnosed in the Phase 2 reviews. A source that fails now keeps the timestamp of its FIRST failure instead of being re-stamped every poll — the stale line said "since the last poll" no matter how long the source had been down, and the churn also defeated the snapshot's stable identity (it serializes the source list), so the canvas re-rendered on every tick while anything was broken. A source that HAD data and only then started failing is flagged too: previously only a source with no node at all got the warning, so a source that went down mid-session kept a healthy-looking node forever. The rest are pointwise: clicking a filter chip cancels the pending search debounce (left armed, it fired ~300ms later and silently reverted the chip); the search input carries an accessible name; `?state=running, failed` parses like the unpadded form instead of dropping the padded value; the CSV toggle helper is defined once in `model/urlParams.ts` rather than twice; the agents tab tells "nothing running" apart from "the filter matched nothing", offering a clear-filters button instead of setup links that would be wrong advice there; edges stop emitting SMIL particles above 40 simultaneously active edges, keeping the colored stroke; and the drawer's error banner clears when a retried action succeeds. ([#12983](https://github.com/diegosouzapw/OmniRoute/pull/12983)) +- **fix(i18n):** the nine locales added with the EU-language batch (Greek, Estonian, Irish, Croatian, Lithuanian, Latvian, Maltese, Slovenian, Serbian) were missing the eleven Orchestration Canvas keys that Phase 3 introduced, so the compare-runs panel and the "no runs match these filters" empty state fell back to English in those languages (`deepMergeFallback` substitutes English for an absent key, so nothing rendered blank — it rendered untranslated). The coverage gate does not catch this: it enforces an 80% floor per locale, and eleven missing keys out of ~13,000 leaves coverage at 99.9%. Translated for real in each language, calibrated against the wording each file already uses for "run", "filter" and "skill". ([#13294](https://github.com/diegosouzapw/OmniRoute/pull/13294)) +- **fix(dashboard):** five follow-ups from the Phase 2 Orchestration Canvas review. The History drawer now shows the memory section for runs that already left the live TTL window: the persisted `memory_hits` event is parsed into `metadata.memoryHits` with the same defensive validation the drawer applies, and — because that event is observability rather than a state transition — it no longer leaks into the timeline, where it had been inheriting the task's state and rendering as a duplicate transition. The A2A memory recall runs against its own 1.5s deadline instead of inheriting the memory backend's 30s one; overshooting degrades exactly like any other recall failure (no hits, task proceeds), and the timer is cleared on both paths. Repeating a Conductor run carries its `requirements.cli`/`requirements.model` forward, so the new run is pinned to the same runner profile and model rather than drifting to whatever the fleet picks. A successful repeat from the Agents tab now focuses the run it created, instead of leaving the operator on the finished one. And the auth test for `POST /api/conductor/tasks` moved into the shared `ROUTES` array rather than restating the pattern. ([#12988](https://github.com/diegosouzapw/OmniRoute/pull/12988)) +- fix(plugins): stop leaking an exit listener per plugin hook timeout, which triggered MaxListenersExceededWarning on plugins that ignore SIGTERM ([#13092](https://github.com/diegosouzapw/OmniRoute/pull/13092)) — thanks @anhtahaylove +- **fix(validation):** Provider node edits no longer fail with a generic "Invalid request" when the optional daily-quota reset fields are left blank. The dashboard sends `dailyQuotaResetTimezone` and `dailyQuotaResetHour` as `null`, and only the hour accepted it. ([#13066](https://github.com/diegosouzapw/OmniRoute/issues/13066)) ([#13083](https://github.com/diegosouzapw/OmniRoute/pull/13083)) — thanks @ntdatt812 +- **fix(combo):** quota-aware expansion drops banned, inactive, missing, and wrong-provider connections before quota fetch or model dispatch; pins and allowlists stay selectors, not a bypass. Antigravity automatic exhaustion now requires a reported zero remaining, so a positive balance below 1% stays eligible. ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- Fix an intermittent failure in the quota-weighted routing test suite: scores are a function of `Date.now()`, so peers with identical quota scored microseconds apart never tied and swapped order. ([#12946](https://github.com/diegosouzapw/OmniRoute/pull/12946)) +- fix(volcengine): resolve quota window mapping gaps, safe multi-connection console binding, and response cookie sanitization ([#12950](https://github.com/diegosouzapw/OmniRoute/pull/12950)) — thanks @HouMinXi +- **fix(test):** resolve the WebDAV handler path with `fileURLToPath` so the suite's 37 WebDAV tests run on Windows instead of failing with a doubled `C:\C:\` drive prefix ([#13196](https://github.com/diegosouzapw/OmniRoute/pull/13196)) — thanks @anhtahaylove +- **fix(ui):** fall back from invalid AnySearch icon alias (direct commit `9587e07b69`) +- **fix(sse):** stop rebuilding a truncated summary from the collector's cap-dropped event array ([#12718](https://github.com/diegosouzapw/OmniRoute/pull/12718)) — thanks @hartmark +- **fix(conversations):** show a pending spinner for unresolved tool nodes instead of "(empty)" ([#12727](https://github.com/diegosouzapw/OmniRoute/pull/12727)) — thanks @hartmark +- **fix(streaming):** fail fast when an upstream stream produces only lifecycle/heartbeat events, never real content ([#12741](https://github.com/diegosouzapw/OmniRoute/pull/12741)) — thanks @hartmark +- **fix(responses-continuation):** bridge the write-in-flight window with an in-memory pending store ([#12854](https://github.com/diegosouzapw/OmniRoute/pull/12854)) — thanks @hartmark +- **fix(translator):** drop or map agent_message on Chat Completions fallback ([#12880](https://github.com/diegosouzapw/OmniRoute/pull/12880)) — thanks @RaviTharuma +- **fix(api):** page and stream-complete GET /v1/models for large catalogs ([#12882](https://github.com/diegosouzapw/OmniRoute/pull/12882)) — thanks @RaviTharuma +- **fix(quota):** align AUTH window labels with usage API and clarify 503 ([#12884](https://github.com/diegosouzapw/OmniRoute/pull/12884)) — thanks @RaviTharuma +- **fix(ci):** clear two base-reds on release/v3.8.51 — mutation-coverage gate + image-only-model guard ([#12945](https://github.com/diegosouzapw/OmniRoute/pull/12945)) +- **fix(api):** scope batch bulk-delete to the calling API key ([#13211](https://github.com/diegosouzapw/OmniRoute/pull/13211)) +- **fix(tests):** align model-sync log assertion to shipped path redaction (base-red) ([#13227](https://github.com/diegosouzapw/OmniRoute/pull/13227)) +- **fix(test):** make the ToS heading guard actually require the parentheses ([#13228](https://github.com/diegosouzapw/OmniRoute/pull/13228)) +- **fix(quality):** register 4 drifted covering tests in stryker tap.testFiles ([#13229](https://github.com/diegosouzapw/OmniRoute/pull/13229)) +- **fix(logging):** mask *-api-key headers in request-log pipeline (#13273) ([#13401](https://github.com/diegosouzapw/OmniRoute/pull/13401)) — thanks @KooshaPari +- **fix(tests):** correct stale Jina catalog prefix in custom-models test ([#13403](https://github.com/diegosouzapw/OmniRoute/pull/13403)) — thanks @KooshaPari +- **fix(backend):** prune old backups after health-check-repair snapshots (#13308) ([#13404](https://github.com/diegosouzapw/OmniRoute/pull/13404)) — thanks @KooshaPari +- **fix(backend):** guard memory_fts_au trigger with WHEN clause to stop FTS bloat (#13326) ([#13405](https://github.com/diegosouzapw/OmniRoute/pull/13405)) — thanks @KooshaPari +- **fix(free-tiers):** remove obsolete Together $25 signup credit (#12526) ([#13407](https://github.com/diegosouzapw/OmniRoute/pull/13407)) — thanks @KooshaPari +- **fix:** skip non-chat generation models in Test all models ([#13409](https://github.com/diegosouzapw/OmniRoute/pull/13409)) — thanks @KooshaPari +- **fix(providers):** de-duplicate agy and antigravity model catalogs ([#13410](https://github.com/diegosouzapw/OmniRoute/pull/13410)) — thanks @KooshaPari +- **fix(dashboard):** send provider in body for mini-playground web search ([#13411](https://github.com/diegosouzapw/OmniRoute/pull/13411)) — thanks @KooshaPari +- **fix(resilience):** add CJK quota-exhaustion patterns to 429 classifier ([#13412](https://github.com/diegosouzapw/OmniRoute/pull/13412)) — thanks @KooshaPari +- **fix(backend):** eval runner: failed calls no longer score as passed + dotAll regex ([#13413](https://github.com/diegosouzapw/OmniRoute/pull/13413)) — thanks @KooshaPari +- **fix(backend):** eval runner opts out of output-style and memory injection ([#13414](https://github.com/diegosouzapw/OmniRoute/pull/13414)) — thanks @KooshaPari +- **fix:** chain serial quarantine step in npm test script (fixes #13286) ([#13418](https://github.com/diegosouzapw/OmniRoute/pull/13418)) — thanks @KooshaPari +- **fix(antigravity):** derive stable session ID from credentials for prompt-cache reuse (#13305) ([#13424](https://github.com/diegosouzapw/OmniRoute/pull/13424)) — thanks @KooshaPari +- **fix(memory):** merge memory into top-level system field instead of unshifting at messages[0] (#13425) ([#13427](https://github.com/diegosouzapw/OmniRoute/pull/13427)) — thanks @KooshaPari +- **fix(compression):** preserve instruction blocks from lossy engines ([#13523](https://github.com/diegosouzapw/OmniRoute/pull/13523)) — thanks @KooshaPari +- **fix(compression):** preserve polarity words and newlines in ultra heuristic ([#13524](https://github.com/diegosouzapw/OmniRoute/pull/13524)) — thanks @KooshaPari +- **fix(compression):** run user preservePatterns before built-in patterns (#13457) ([#13525](https://github.com/diegosouzapw/OmniRoute/pull/13525)) — thanks @KooshaPari +- **fix(reasoning):** expand deepseek-v4 regex to match v4.1+ and add command-code provider (#13430) ([#13528](https://github.com/diegosouzapw/OmniRoute/pull/13528)) — thanks @KooshaPari +- **fix(providers):** add alwaysReasons metadata for reka-flash-3 ([#13531](https://github.com/diegosouzapw/OmniRoute/pull/13531)) — thanks @KooshaPari +- **fix(playground):** allow Compare columns to scroll independently ([#13532](https://github.com/diegosouzapw/OmniRoute/pull/13532)) — thanks @KooshaPari +- **fix(security):** empty whitelist allows through to prevent admin lockout ([#13534](https://github.com/diegosouzapw/OmniRoute/pull/13534)) — thanks @KooshaPari +- **fix(tests):** add DISABLE_IOREG_STRATEGY env var for macOS test compatibility (fixes #13467) ([#13539](https://github.com/diegosouzapw/OmniRoute/pull/13539)) — thanks @KooshaPari +- **fix(zed-hosted):** map developer role and Gemini enums for Zed proxy ([#13543](https://github.com/diegosouzapw/OmniRoute/pull/13543)) — thanks @KooshaPari +- **fix(analytics):** normalize provider aliases to canonical id in usage_history ([#13545](https://github.com/diegosouzapw/OmniRoute/pull/13545)) — thanks @KooshaPari +- **fix(combo):** use traceId for call log id to prevent UNIQUE constraint failure ([#13546](https://github.com/diegosouzapw/OmniRoute/pull/13546)) — thanks @KooshaPari +- **fix(auth):** check model lockout before returning synthetic noauth connection ([#13547](https://github.com/diegosouzapw/OmniRoute/pull/13547)) — thanks @KooshaPari +- **fix(guardrails):** skip credential redaction for base64 image data URLs ([#13550](https://github.com/diegosouzapw/OmniRoute/pull/13550)) — thanks @KooshaPari +- **fix(resilience):** increase requestQueue.maxWaitMs default from 15s to 30s ([#13553](https://github.com/diegosouzapw/OmniRoute/pull/13553)) — thanks @KooshaPari +- **fix(docs):** document OMNIROUTE_READY_TIMEOUT_MS and allowlist the test-only DISABLE_IOREG_STRATEGY ([#13692](https://github.com/diegosouzapw/OmniRoute/pull/13692)) + ### 📝 Maintenance ---- +- **docs(readme):** reconcile live v3.8.50 provider, free-tier, CLI, routing, test, community, sponsor, acknowledgment, and SVG metrics with their audited source denominators, including a deduplicated OmniRoute-in-Action snapshot and distinct contributor rankings for merged pull requests, GitHub-attributed commits, and Git history ([#11356](https://github.com/diegosouzapw/OmniRoute/pull/11356)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **docs:** sync the canonical provider count 354 → 356 across `README.md`, `AGENTS.md`, `llm.txt` (+ 42 i18n mirrors), the four README SVG diagrams, `docs/reference/PROVIDER_REFERENCE.md` (regenerated) and the `package.json` description after Opper (#11629) and 1min.ai (#11631) boarded the catalog — closes the `check:docs-counts-sync` strict drifts that kept `release/v3.8.51` red ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11673](https://github.com/diegosouzapw/OmniRoute/pull/11673)) — thanks @jonlwheat2-gif +- **test(providers):** update count-derived assertions after the v3.8.51 provider additions — `APIKEY_PROVIDERS` 233 → 235 (Opper #11629 + 1min.ai #11631), reserved-prefix REGISTRY walk 395 → 398, `WEB_FETCH_PROVIDERS` now includes `nimble-search` (#11620), and the provider translate-path golden snapshot regenerated ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11674](https://github.com/diegosouzapw/OmniRoute/pull/11674)) — thanks @jonlwheat2-gif +- **test(sse):** bump the hard-lease connection-query inventory for `src/lib/tokenHealthCheck.ts` to 2 — the verify-only web-cookie sweep added by #11495 split the single `getProviderConnections` call into oauth + cookie variants, which the frozen inventory had not tracked ([#11495](https://github.com/diegosouzapw/OmniRoute/pull/11495)). ([#11677](https://github.com/diegosouzapw/OmniRoute/pull/11677)) — thanks @jonlwheat2-gif / @oyi77 +- **chore(release):** clear the `release/v3.8.51` base-red gates — docs migration count (+38 i18n mirrors), stryker `tap.testFiles`, lockfile registry host, stale ESLint suppressions, the 7 lint regressions they were hiding, the stale `APIKEY_PROVIDERS` partition count (231 → 233), the `glm-5.3-max` tier missing from the GLM test table, a dead `modelType === "chat"` comparison in the models catalog, and the `resolveLiveWsUrl` / `sanitizeLiveWsPort` helpers from #11388 that a later merge dropped from `wsPath.ts` (dashboard live WebSocket URL resolution) ([#11502](https://github.com/diegosouzapw/OmniRoute/pull/11502)) — thanks @MumuTW +- **chore(free-models):** the free-tier regime table now declares which totals figure each regime feeds and whether it may take the no-auth shortcut, so the sets that used to repeat those answers by hand are derived from it — a new `freeType` no longer compiles until it has answered every question, instead of silently contributing to no total ([#11537](https://github.com/diegosouzapw/OmniRoute/pull/11537)) — thanks @maxmad64bis +- **docs(routing):** LKGP strategy copy now states provider-level stickiness and the explicit rules fallback instead of promising per-target pinning ([#11555](https://github.com/diegosouzapw/OmniRoute/pull/11555)) — thanks @maxmad64bis +- **test(chatcore):** pin the response tool-name alias resolution against the ordering hazard that broke every Gemini/Antigravity MCP tool call in v3.8.49. `extractRequestToolIdentityMap` deletes `translatedBody._toolNameMap`, so the later read is always undefined and the ledger survives only through the `requestToolIdentityMap` fallback — removing that fallback previously left the entire tool-name suite green. The resolution moves into `resolveResponseToolNameMap()` next to the map it depends on, with a regression guard that fails without the recovery. No behaviour change. ([#11575](https://github.com/diegosouzapw/OmniRoute/pull/11575)) — thanks @amartinawi +- **docs(env):** document `OMNIROUTE_CORPUS_CACHE_SIZE` in `.env.example` and `ENVIRONMENT.md`, so the env/docs contract check stops failing Docs Gates on every branch ([#11576](https://github.com/diegosouzapw/OmniRoute/pull/11576)) — thanks @ntdatt812 +- **chore(stryker):** register `route-guard-tunnel-processes-local-only` and `universal-quota-aware-routing` in `tap.testFiles`, so their mutant kills count for the three modules they cover ([#11577](https://github.com/diegosouzapw/OmniRoute/pull/11577)) — thanks @ntdatt812 +- **test(authz):** pin `LOCAL_ONLY_API_GET_EXEMPTIONS` by exact membership instead of by entry count, so the guard names the offending path and also catches a substitution ([#11580](https://github.com/diegosouzapw/OmniRoute/pull/11580)) — thanks @ntdatt812 +- **test(build):** pin `resolveNextBuildBundlerFlag` to the `OMNIROUTE_USE_TURBOPACK` contract it implements, across runtimes, instead of asserting an unimplemented Bun override ([#11583](https://github.com/diegosouzapw/OmniRoute/pull/11583)) — thanks @ntdatt812 +- **test(claude-web):** the Claude Web transport test's barrel anchors no longer pin the exact construction spelling. #11421 made the executor map lazy, so `/"claude-web": new ClaudeWebExecutor\(\)/` stopped matching and the test went red on `release/v3.8.51` itself — a base-red gate every open PR inherited. The anchors now match the alias key and the class name and tolerate either spelling in between, and were checked to still fail when an alias is genuinely repointed at another executor ([#11585](https://github.com/diegosouzapw/OmniRoute/pull/11585)) — thanks @ntdat812 +- **test(models):** pin `qwen3.8-max` resolution per provider — two of the four catalogs now serve the bare id and no longer carry `-preview` ([#11590](https://github.com/diegosouzapw/OmniRoute/pull/11590)) — thanks @ntdatt812 +- **test(executors):** drop the stale 128-tool truncation assertion that contradicted the #11444 pass-through contract ([#11591](https://github.com/diegosouzapw/OmniRoute/pull/11591)) — thanks @ntdatt812 +- **test(token-health):** pin the current terminal-skip boundary — expired with retry budget is probed, `account_deactivated` stays skipped ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)) — thanks @ntdatt812 +- **test(models):** follow #11503's retarget of the `gemini-3-pro-high` alias to the hyphenated catalog id ([#11593](https://github.com/diegosouzapw/OmniRoute/pull/11593)) — thanks @ntdatt812 +- **refactor(video bridge):** extract per-video acquisition, whole-result caching, description, and metrics/abort/cleanup out of `VideoBridgeGuardrail.preCall` into a `processVideoPart` seam in a new `videoBridgePipeline.ts`, behind explicit `VideoMediaBrokerPort`, `VideoAudioTranscriptionPort`, and `VideoDrilldownPort` boundaries; `preCall` now only handles request traversal, policy, and response aggregation. The Video tab's FFmpeg/ffprobe runtime status is now an explicit `unknown` / `restricted` / `unavailable` / `available` state instead of a nullable boolean pair, fixing a case where an in-flight or failed probe was mislabeled as "install FFmpeg" ([#11657](https://github.com/diegosouzapw/OmniRoute/issues/11657)). ([#12016](https://github.com/diegosouzapw/OmniRoute/pull/12016)) +- **test(opencode-plugin):** add unit test coverage for `formatFreeBudget()` naming helper ([#11660](https://github.com/diegosouzapw/OmniRoute/pull/11660)) — thanks @f9td56dbgh-hub +- **docs(video):** clarify that the Video Bridge transcript `source` field (`client`, `embedded`, `audio-bridge`) is presently caller-declared and not yet server-verified — OmniRoute enforces the enum shape but does not cryptographically confirm that an `embedded`/`audio-bridge` label came from a server-owned extraction ([#11661](https://github.com/diegosouzapw/OmniRoute/issues/11661)). ([#12001](https://github.com/diegosouzapw/OmniRoute/pull/12001)) +- **chore(providers):** retire the distributed Raycast Relay and Hailuo Web integrations after a provenance audit confirmed substantial derivation from GPL-3.0 sources; the official GitHub and MiniMax API providers remain available. Migration 163 disables legacy connections and invalidates their active leases while preserving connection IDs, audit/history rows, quota snapshots, usage records, and fail-closed API-key connection policies. ([#11691](https://github.com/diegosouzapw/OmniRoute/pull/11691)) +- **chore(providers):** retire the Felo Web provider while its GPL-derived provenance remains on licensing HOLD ([#11698](https://github.com/diegosouzapw/OmniRoute/pull/11698)) — stale runtime and database references now fail closed while existing connection, lease, and usage history remains preserved for auditability. +- **fix(sse):** retire the Gemini Web image-generation bridge while provenance and licensing remain on HOLD, preserving Gemini Web chat/auth/tools and legitimate Gemini/Nano Banana image providers ([#11708](https://github.com/diegosouzapw/OmniRoute/pull/11708)) +- **chore(audio):** retire the built-in EdgeTTS provider and its 11-voice catalog while provenance/licensing review remains on HOLD; the generic `/v1/audio/speech` endpoint and the other speech providers remain available ([#11711](https://github.com/diegosouzapw/OmniRoute/pull/11711)) +- **fix(providers):** retire the unofficial Qwen Web integration and its `qwen-web`/`qw` identities while third-party provenance remains on HOLD ([#11713](https://github.com/diegosouzapw/OmniRoute/pull/11713)) — direct requests now fail closed with a sanitized HTTP 410 and combos skip the retired target, while migration guards preserve connection IDs, allowlists, usage/audit history and invalidate active leases. Supported Qwen API, Qwen Code, cloud, token-plan, audio, local and model-name surfaces remain available; restoring Qwen Web requires a qualified provenance/license decision. +- **fix(providers):** retire `microsoft-designer-web` and its `msdesigner` alias from runtime and catalogs while preserving stored credentials, provider history and allowlists as inactive records — source dependency on gpt4free's [`MicrosoftDesigner.py`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/g4f/Provider/needs_auth/MicrosoftDesigner.py) is proven at snapshot `0943d16eef757a44deb60651c07bbe5fa958cafe`, whose [`LICENSE`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/LICENSE) is GPL-3.0; expressive adaptation is probable, which records provenance/licensing risk without asserting a legal conclusion ([#11720](https://github.com/diegosouzapw/OmniRoute/pull/11720)). +- **docs(legal):** centralize pinned MIT notices for the vendored GCF codec and local flag assets ([#11726](https://github.com/diegosouzapw/OmniRoute/pull/11726)) +- Split the 2,952-line `adobeFireflyClient.ts` into nine focused modules (catalog, credentials, payload, ARP session, upload, responses, IMS, account, poll) behind an unchanged barrel — no consumer edits and no public API change (#11728). — thanks @MumuTW +- **fix(dashboard):** replaced the Model Lockout card's unprovenanced notification MP3 with a short, locally synthesized Web Audio chime ([#11731](https://github.com/diegosouzapw/OmniRoute/pull/11731)) — both toggles retain optional audible feedback without shipping replacement media or third-party code, and unsupported, suspended, or rejected audio contexts fail open without blocking the setting change. +- **chore(deepseek-web):** remove the provenance-unresolved DeepSeek PoW WASM binary and its runtime loader/tracing while retaining the existing JavaScript solver (slower at high difficulty) ([#11732](https://github.com/diegosouzapw/OmniRoute/pull/11732)). +- **chore(assets):** remove four unused provider assets whose redistribution provenance could not be verified while retaining the active Freebuff SVG ([#11735](https://github.com/diegosouzapw/OmniRoute/pull/11735)) +- **docs(assets):** retired two raster images whose redistribution provenance could not be verified ([#11736](https://github.com/diegosouzapw/OmniRoute/pull/11736)) — PicoClaw documentation now reuses the existing SVG, while Zoo Code remains a text link without an image. +- **docs(legal):** add pinned notices for six LobeHub-derived provider SVGs and 65 byte-exact theSVG assets, retaining registry claims as unverified metadata with explicit trademark and HOLD boundaries ([#11737](https://github.com/diegosouzapw/OmniRoute/pull/11737)). +- **chore(quality):** added a versioned 225/225 provider-asset provenance manifest and blocking drift gate ([#11743](https://github.com/diegosouzapw/OmniRoute/pull/11743)) — the inventory pins magic MIME and SHA-256 for every physical file, records immutable source matches for 71 assets, keeps 69 probable and 85 unresolved entries explicit but non-blocking, and treats provenance as source evidence rather than copyright or trademark clearance. +- **refactor(mitm):** replace four previously derived Traffic Inspector utilities — conversation normalization, SSE merging, secret masking, and header sanitization — with independent clean-room implementations based on public provider schemas, WHATWG SSE, and RFC 6750, while keeping the two independently classified passthrough surfaces unchanged ([#11748](https://github.com/diegosouzapw/OmniRoute/pull/11748)). +- Added a unit test that fails seven days before any dated pack under `config/` (`validUntil` and sibling keys) lapses, naming the file and key. The Alibaba free-tier pack expired on 2026-08-27 and turned every PR red the next morning with no commit involved; renewal now happens on someone's terms, not the clock's. ([#11891](https://github.com/diegosouzapw/OmniRoute/pull/11891)) +- `check:workflows` now fails (under `--strict`/`--ratchet`) when any job routed to a self-hosted runner publishes with `--provenance` — npm rejects that with `422` at the registry, which in v3.8.50 only surfaced after the tag and Docker images were public. ([#11895](https://github.com/diegosouzapw/OmniRoute/pull/11895)) +- `scripts/ops/runner-janitor.sh` now proves a path is idle with one `lsof` snapshot and removes stale leftovers itself (tmpfs after 3 h — it is RAM — disk after 24 h), kills orphan `next-build` processes, prunes checkouts of stopped runners, and alerts on memory pressure; `--dry-run` shows exactly what it would do. `docs/ops/RUNNER_BOX.md` reconciled to the measured box (31 GB, 10 listeners). ([#11893](https://github.com/diegosouzapw/OmniRoute/pull/11893)) +- The `next-build` artefact (1.3 GB) is now written and read under `$RUNNER_TEMP` (per-runner, on disk) instead of `/tmp`, which on the self-hosted pool is a 12 GB tmpfs in RAM. Landing it there took 27–32 of the publish job's 76 minutes, and the fixed `/tmp/e2e-build.tar.gz` name let E2E jobs on different runners overwrite each other's download. ([#11896](https://github.com/diegosouzapw/OmniRoute/pull/11896)) +- The CI `build` job now runs in two concurrency lanes — `main` and pull requests — so a release build is never queued behind (or OOM-killed beside) PR builds on the self-hosted pool, which holds one `next-build` comfortably and two at the edge. ([#11901](https://github.com/diegosouzapw/OmniRoute/pull/11901)) +- Re-freeze the ESLint suppressions on `release/v3.8.51` from a clean-room measurement (2 stale file entries pruned, 55 pre-existing `no-explicit-any` in six new files frozen under #11924) and drop the dead `GPT_SIZE_MAP` constant orphaned by the Adobe Firefly client split, so `No new ESLint warnings` stops failing every PR with exit 2 (Refs #11924) ([#11955](https://github.com/diegosouzapw/OmniRoute/pull/11955)) +- Type the 55 `no-explicit-any` sites that had been frozen under #11924 — four redundant casts in `socksConnectorWithFamily.ts` (undici/socks types already accept them) and the mocks/fixtures of the socks-timeout and isFree suites — and drop their suppression entries; the ESLint ratchet shrinks from 5487 to 5432 (Closes #11924) ([#11975](https://github.com/diegosouzapw/OmniRoute/pull/11975)) +- Take the two hosted-runner builds off the PR rail: `Build App` (`build.yml`) is `workflow_dispatch`-only and `DAST smoke (PR)` runs only for PRs into `main` — the 7 GB hosted VM cannot build `release/v3.8.51` in any profile (19/30 red, VM shutdown ~8 min into `next build`) and both checks had turned into permanent noise on every release PR; the bundle stays validated by `ci.yml` on `main` and by `nightly-release-green` on `release/**` (Closes #11946) ([#11962](https://github.com/diegosouzapw/OmniRoute/pull/11962)) +- Move the four nightly jobs that build the backend (`nightly-schemathesis`, `nightly-llm-security` promptfoo + garak, `nightly-resilience` axe-a11y) off the hosted 7 GB runner — where they died on `release/v3.8.51` unseen — onto the box's new `omni-light` pool (two listeners), and document the reshaped fleet (4 active OmniRoute listeners: 2 `omni-build` + 2 `omni-light`, janitor ceiling 4) (Closes #11965) ([#11967](https://github.com/diegosouzapw/OmniRoute/pull/11967)) +- Stop painting every fork PR into `release/**` red: `quality.yml` `Build (advisory)` is skipped (GitHub still reports `continue-on-error` failures as check FAILURE). Hosted `ubuntu-latest` cannot finish `npm run build` on this tree — same class as #11962 taking `build.yml` off the PR rail. `docker-publish.yml` amd64 now runs on the `.113` `omni-build` pool (31 GB / 32 cores, two listeners) with Turbopack, shares the `heavy-build-main` lane with `ci.yml` `Build` so it queues instead of becoming a third heavy, and keeps per-ref concurrency (a merge storm was starting 8 concurrent OOM builds). arm64 stays on `ubuntu-24.04-arm` with webpack — there is no ARM box. Fallback when `USE_VPS_RUNNER` is off: hosted amd64 + webpack (#11976). ([#12021](https://github.com/diegosouzapw/OmniRoute/pull/12021)) +- `docker-publish.yml` builds with webpack (`OMNIROUTE_USE_TURBOPACK=0`) on **both** arches, including amd64 on the `.113` `omni-build` pool. Turbopack had the RAM (31 GB, `omniroute-113-6`) and still panicked (`TurbopackInternalError: there must be a path to a root`, run 33253576569, 26 min in). The same tree's arm64 webpack build on hosted `ubuntu-24.04-arm` succeeded (run 33264823398). Dockerfile already documents webpack as the Docker escape hatch (#11976). ([#12050](https://github.com/diegosouzapw/OmniRoute/pull/12050)) +- **docs(free-tier):** declare the counting vs deciding regimes for "is it free?" and guard the deciding path from DB-backed catalog resolution ([#12226](https://github.com/diegosouzapw/OmniRoute/pull/12226)) — thanks @maxmad64bis +- **docs(auto-combo):** The mode pack table in `docs/routing/AUTO-COMBO.md` now lists all six shipped packs with every weight each one sets, replacing a four-pack table whose numbers had also drifted from the source. It states plainly that no pack sets `quality`, so selecting any pack silences the observed-quality signal. Six more documents that quote the scoring factor count joined the `check:docs-counts` gate, which caught five stale claims — including one naming nine factors that do not exist — and two stale mode pack counts. The dashboard routing panel, which offered four of the six packs and labelled the default strategy "6-Factor Scoring", is now covered by a test; the two packs it was missing are `reliability-first` and `chaos-mode`, the latter labelled as the fault-injection profile it is rather than as one more routing preference ([#12316](https://github.com/diegosouzapw/OmniRoute/pull/12316)) — thanks @maxmad64bis +- **chore(quality):** rebaseline `src/lib/db/apiKeys.ts` for the ACL the key-creation path now preserves ([#12352](https://github.com/diegosouzapw/OmniRoute/pull/12352)) ([#12673](https://github.com/diegosouzapw/OmniRoute/pull/12673)) — thanks @KrzysiekSko +- **chore(providers):** bump the Claude Code wire identity and the Devin bridge image pin from `2.1.220` to `2.1.258` ([#12402](https://github.com/diegosouzapw/OmniRoute/pull/12402)) — thanks @ggiak ([#12604](https://github.com/diegosouzapw/OmniRoute/pull/12604)) +- **docs(env):** align `.env.example`, the README Bun section, and the troubleshooting guide with the code: `OMNIROUTE_USE_TURBOPACK` also governs `npm run build` (not dev-only), `bun run build` follows that flag instead of auto-selecting Webpack, `OMNIROUTE_CHAT_MAX_HEAVY_IN_FLIGHT` is unset by default (no request-count cap), and the structural `503 chat_admission_busy` message matches `chatAdmissionResponses.ts` (#12404 — thanks @pacocartones) +- **chore(electron):** upgrade the desktop app to Electron 44 (Chromium 152, Node 24.18.1) ([#12217](https://github.com/diegosouzapw/OmniRoute/pull/12217)). **Requires macOS 13 (Ventura) or later** — Chromium dropped macOS 12 (Monterey), so Monterey users must stay on an earlier OmniRoute desktop build. Windows and Linux are unaffected; the app already shipped only x64/arm64, so Electron 44 dropping 32-bit builds changes nothing. Removes the `openAsHidden`/`wasOpenedAsHidden` login-item fields deleted in Electron 44 — hidden autostart continues to work through the `--hidden` argument registered with the login item ([#12554](https://github.com/diegosouzapw/OmniRoute/pull/12554)) — thanks @app/dependabot +- **chore(quality):** rebaseline `src/sse/handlers/chat.ts` for the effective-input persistence the continuation fix needs ([#12641](https://github.com/diegosouzapw/OmniRoute/pull/12641)) ([#12680](https://github.com/diegosouzapw/OmniRoute/pull/12680)) — thanks @hartmark +- `Coverage` job on `ci.yml`: the informational Codecov upload gets its own 5-minute ceiling and `continue-on-error`, and the job budget grows from 20 to 30 minutes (the 8-shard c8 merge alone takes ~10) — a stalled upload no longer ends the job `cancelled` and drags a fully green `main` run's conclusion down with it ([#11972](https://github.com/diegosouzapw/OmniRoute/pull/11972)) +- CI hardening on the PR rail: the four `Unit Tests fast-path` shards get `timeout-minutes: 30` (a hung shard held a PR for 64 min instead of GitHub's 6 h default) and both ESLint file caches lose their `restore-keys` fallback, so a cache built under another suppressions file or lint config can no longer report stale verdicts (Refs #11600, #11924) ([#11963](https://github.com/diegosouzapw/OmniRoute/pull/11963)) +- Every CI job that runs a `next build` (`build`, the npm `publish`, both release-green validations) now targets the `omni-build` runner label, which only two of the eight self-hosted runners carry. The box holds one build comfortably and two at the edge; a third now queues on GitHub instead of being OOM-killed by the kernel. ([#11932](https://github.com/diegosouzapw/OmniRoute/pull/11932)) +- **refactor(combo):** move `handleRoundRobinCombo` (and `resolveTargetTokenLimit`) into `open-sse/services/combo/roundRobinCombo.ts`. `combo.ts` drops from 2164 to 1014 lines (`split("\n").length`); the leaf is 1199 (under the 1200 new-file cap). The round-robin call site uses a dynamic `import()` so `releaseStickyPinOnFailure` / `clearStaleLKGP` can stay exported from `combo.ts` without a static cycle. Skip / sticky / semaphore / safety-timer behavior is unchanged. ([#12811](https://github.com/diegosouzapw/OmniRoute/pull/12811)) — thanks @HouMinXi +- **chore(quality):** rebaseline the file-size caps the error-boundary campaign grew past (`open-sse/executors/codex.ts`, `open-sse/vendor/codex-chatgpt-web/bridge.ts`, both via [#12444](https://github.com/diegosouzapw/OmniRoute/pull/12444)) ([#12654](https://github.com/diegosouzapw/OmniRoute/pull/12654)) +- **chore(quality):** rebaseline the file-size caps the hartmark batch grew past (`combos/page.tsx` via [#12355](https://github.com/diegosouzapw/OmniRoute/pull/12355), `open-sse/services/combo.ts` via [#12338](https://github.com/diegosouzapw/OmniRoute/pull/12338)) ([#12623](https://github.com/diegosouzapw/OmniRoute/pull/12623)) — thanks @hartmark +- **chore(quality):** rebaseline the file-size caps the HouMinXi batch grew past when its PRs stacked (`providers/page.tsx`, `chatCore.ts`, `accountFallback.ts`) — each PR measured correctly in isolation, none saw the stacking ([#12619](https://github.com/diegosouzapw/OmniRoute/pull/12619)) +- **chore(quality):** rebaseline `open-sse/services/combo.ts` for the reset-aware scoring the HouMinXi batch stacked ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) ([#12678](https://github.com/diegosouzapw/OmniRoute/pull/12678)) — thanks @HouMinXi +- **docs:** sync provider catalog count (direct commit `4f11b2ae3d`) +- **docs(skills):** sync omni-inference search fallback (direct commit `b4e76a7ed9`) +- **chore(lint):** prune two stale base suppressions (direct commit `1325047d56`) +- **test(auto-combo):** tolerate full-suite rotation load (direct commit `ad3e293f9f`) +- **test:** drain inherited quality snapshot reds (direct commit `21ed68d8ac`) +- **chore(quality):** rebaseline measured CLI bundle growth (direct commit `d35b3f9779`) +- **test(ui):** reproduce invalid AnySearch icon alias (direct commit `12cc6ca834`) +- **test(translator):** fix the relative imports of the relocated deferred-finish test (direct commit `777d9d1629`) +- **docs(counts):** bring the documented provider and migration counts to the measured values (direct commit `e70bea30e9`) +- **test(security):** drop the guard-target set left unused by the sync (direct commit `a32ba0e362`) +- **chore(db):** renumber model_capabilities migration 163 → 169 (cross-branch collision with 163_radar_feed_cache_generated_at) (direct commit `70f5d4cbf9`) +- **docs(cli):** document CLI_PRIME_AGENT_BIN and correct the CLI Agents count (direct commit `9058e39b61`) +- **chore(deps):** refresh runtimes and adopt ESLint 10 ([#11259](https://github.com/diegosouzapw/OmniRoute/pull/11259)) — thanks @backryun +- **refactor(local-corpus):** implement dynamic root resolution and LRU cache ([#11491](https://github.com/diegosouzapw/OmniRoute/pull/11491)) — thanks @raheemuddin786 +- **chore(quality):** prune stale eslint-suppressions entries ([#11596](https://github.com/diegosouzapw/OmniRoute/pull/11596)) +- **chore(ci):** unblock quality gates - env-doc sync + stryker tap.testFiles registration (#9282) ([#11609](https://github.com/diegosouzapw/OmniRoute/pull/11609)) — thanks @Meet6338-X +- **chore(ci):** point the circular-deps gate at dpdm's real JS entrypoint ([#11615](https://github.com/diegosouzapw/OmniRoute/pull/11615)) — thanks @benzntech +- **chore(quality):** rebaseline file-size for #11448/#11495/#11561 growth ([#11630](https://github.com/diegosouzapw/OmniRoute/pull/11630)) +- **deps:** realign onnxruntime-node pin to @huggingface/transformers' 1.24.3 lockstep ([#11633](https://github.com/diegosouzapw/OmniRoute/pull/11633)) — thanks @jonlwheat2-gif +- **test(autoCombo):** port models_dev_tier #11508 guard from node:test to vitest ([#11635](https://github.com/diegosouzapw/OmniRoute/pull/11635)) — thanks @jonlwheat2-gif +- **chore(quality):** rebaseline file-size for #11083/#11631/#11628 growth ([#11636](https://github.com/diegosouzapw/OmniRoute/pull/11636)) +- **chore(lint):** prune stale ESLint suppressions (base-red #11449) ([#11644](https://github.com/diegosouzapw/OmniRoute/pull/11644)) — thanks @jonlwheat2-gif +- **docs(changelog):** aggregate the ten v3.8.50 fragments into CHANGELOG.md ([#11683](https://github.com/diegosouzapw/OmniRoute/pull/11683)) +- **chore(search):** drop unreachable credential sweep after #11642 ([#11687](https://github.com/diegosouzapw/OmniRoute/pull/11687)) — thanks @HouMinXi +- **ci:** add API route TypeScript regression gate ([#11705](https://github.com/diegosouzapw/OmniRoute/pull/11705)) — thanks @Sabeekhann +- **docs(dedup):** correct the CodeQL claim on the tenant-namespace comment ([#11712](https://github.com/diegosouzapw/OmniRoute/pull/11712)) +- **docs(changelog):** add consolidated v3.8.50 stats, top-25 ranking and a .mailmap ([#11715](https://github.com/diegosouzapw/OmniRoute/pull/11715)) +- **docs:** recommend gstack for AI-assisted workflows ([#11770](https://github.com/diegosouzapw/OmniRoute/pull/11770)) — thanks @santosraju99-hub +- **chore(quality):** tighten complexity/cognitive-complexity ratchets to the current tip; land the missed gateways.ts rebaseline ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **docs(i18n):** finish Freepik → Magnific rebrand in locale strings and README ([#11772](https://github.com/diegosouzapw/OmniRoute/pull/11772)) — thanks @turbolego +- **refactor(auto-combo):** fix divergent scoring in combo health reporting ([#11854](https://github.com/diegosouzapw/OmniRoute/pull/11854)) — thanks @jacobsparts +- **chore(changelog):** add missing fragments for #11919/#11918/#11916 ([#11938](https://github.com/diegosouzapw/OmniRoute/pull/11938)) +- **test(check):** escape the runs-on fixture with JSON.stringify, not a quote-only replace ([#11942](https://github.com/diegosouzapw/OmniRoute/pull/11942)) (direct commit `fb7445eaa3`) +- **test(release):** align five suites with the contracts #11933, #11919 and #11876 shipped on release/v3.8.51 ([#11944](https://github.com/diegosouzapw/OmniRoute/pull/11944)) +- **chore(quality):** remove unreachable code and restore test discovery ([#11950](https://github.com/diegosouzapw/OmniRoute/pull/11950)) — thanks @backryun +- **docs(agents):** sync-back landings are fast-forward, never squash ([#11964](https://github.com/diegosouzapw/OmniRoute/pull/11964)) +- **test(infra):** retry recursive temp-dir removal instead of failing a shard on ENOTEMPTY (#11966) ([#11968](https://github.com/diegosouzapw/OmniRoute/pull/11968)) +- **docs(ops):** the .113 heavy-build ceiling is one runner, not two ([#12048](https://github.com/diegosouzapw/OmniRoute/pull/12048)) +- **test(ui):** correct inactive auto-fetch expectation ([#12098](https://github.com/diegosouzapw/OmniRoute/pull/12098)) — thanks @RaviTharuma +- **docs:** document native dependency check escape hatch ([#12101](https://github.com/diegosouzapw/OmniRoute/pull/12101)) — thanks @RaviTharuma +- **test(providers):** regenerate the translate-path golden for OrcaRouter (#11923) ([#12118](https://github.com/diegosouzapw/OmniRoute/pull/12118)) +- **chore(quality):** velocity phase — loosen every numeric baseline by 20% until v4.0, monitor headroom nightly ([#12125](https://github.com/diegosouzapw/OmniRoute/pull/12125)) +- **chore(lint):** batch 0 of #12146 — type the call-log-cap sqlite rows instead of 45 as-any casts ([#12157](https://github.com/diegosouzapw/OmniRoute/pull/12157)) +- **chore(lint):** batch 4 of #12146 — shared/components react-hooks violations resolved ([#12159](https://github.com/diegosouzapw/OmniRoute/pull/12159)) +- **chore(lint):** batch 1 of #12146 — dashboard/cli-code react-hooks violations resolved ([#12160](https://github.com/diegosouzapw/OmniRoute/pull/12160)) +- **chore(lint):** batch 3 of #12146 — dashboard/settings react-hooks violations resolved ([#12162](https://github.com/diegosouzapw/OmniRoute/pull/12162)) +- **chore(lint):** batch 2 of #12146 — resolve the react-hooks compiler violations in dashboard/providers ([#12163](https://github.com/diegosouzapw/OmniRoute/pull/12163)) +- **chore(quality):** register search-432 cooldown test in stryker tap.testFiles ([#12170](https://github.com/diegosouzapw/OmniRoute/pull/12170)) +- **chore(lint):** batch 5 of #12146 — combos, endpoint, provider-stats, api-manager and costs react-hooks violations resolved ([#12174](https://github.com/diegosouzapw/OmniRoute/pull/12174)) +- **build:** add contributor fast profile ([#12192](https://github.com/diegosouzapw/OmniRoute/pull/12192)) — thanks @rafacpti23 +- **docs(readme):** README touch-ups ([#12193](https://github.com/diegosouzapw/OmniRoute/pull/12193)) ([#12194](https://github.com/diegosouzapw/OmniRoute/pull/12194)) ([#12202](https://github.com/diegosouzapw/OmniRoute/pull/12202)) — thanks @rafacpti23 +- **build:** force the webpack bundler in the contributor build profile ([#12197](https://github.com/diegosouzapw/OmniRoute/pull/12197)) — thanks @rafacpti23 +- **build:** skip standalone packaging in the contributor profile ([#12198](https://github.com/diegosouzapw/OmniRoute/pull/12198)) — thanks @rafacpti23 +- **docs(audit):** align every published number with the code and harden check:docs-counts ([#12200](https://github.com/diegosouzapw/OmniRoute/pull/12200)) +- **build:** stub the instrumentation entrypoints in the contributor profile ([#12203](https://github.com/diegosouzapw/OmniRoute/pull/12203)) — thanks @rafacpti23 +- **build:** omit the standalone output target for contributor builds ([#12204](https://github.com/diegosouzapw/OmniRoute/pull/12204)) — thanks @rafacpti23 +- **chore(lint):** batch 6 of #12146 — memory, radar, audit, analytics, cache, usage, activity, home and RequestLoggerV2 react-hooks violations resolved ([#12208](https://github.com/diegosouzapw/OmniRoute/pull/12208)) +- **docs(diagrams):** rename number-carrying diagram files to stable names ([#12210](https://github.com/diegosouzapw/OmniRoute/pull/12210)) +- **docs(dashboard):** redraw onboarding tier-flow SVGs for the real 4-tier model ([#12211](https://github.com/diegosouzapw/OmniRoute/pull/12211)) +- **docs(api):** document every implemented route in openapi.yaml (276 → 692 paths) ([#12212](https://github.com/diegosouzapw/OmniRoute/pull/12212)) +- **chore(lint):** batch 7 of #12146 — final src tail: 37 react-hooks violations across 33 files resolved ([#12244](https://github.com/diegosouzapw/OmniRoute/pull/12244)) +- Revert "docs: recommend gstack for AI-assisted workflows (#11770)" ([#12248](https://github.com/diegosouzapw/OmniRoute/pull/12248)) ([#12249](https://github.com/diegosouzapw/OmniRoute/pull/12249)) +- **docs:** Chaos Mode setup guide + weighted strategy semantics ([#12250](https://github.com/diegosouzapw/OmniRoute/pull/12250)) +- **docs(agents):** protected-surface merge rule — operator approval for agent-instruction files ([#12253](https://github.com/diegosouzapw/OmniRoute/pull/12253)) +- **chore(quality):** register native-codex turn-pin tests in stryker tap.testFiles ([#12263](https://github.com/diegosouzapw/OmniRoute/pull/12263)) +- **chore(quality):** dedupe tap.testFiles entries added by racing base-red fixes ([#12265](https://github.com/diegosouzapw/OmniRoute/pull/12265)) +- **docs(readme):** introduce OmniRouteTray — the macOS menu-bar companion ([#12276](https://github.com/diegosouzapw/OmniRoute/pull/12276)) — thanks @ggiak +- **chore(proxy):** purge the legacy 1proxy residue — sync/rotator modules, dead DB exports, dead settings tab and flag, docs (#12091) ([#12290](https://github.com/diegosouzapw/OmniRoute/pull/12290)) +- **chore(quality):** baseline-headroom skips generated and vendored files in the fileSize worst-file signal ([#12291](https://github.com/diegosouzapw/OmniRoute/pull/12291)) +- **refactor(video):** unify the JPEG frame data-URI contract ([#12322](https://github.com/diegosouzapw/OmniRoute/pull/12322)) +- **chore(deps):** freeze onnxruntime-node and eslint-plugin-react-hooks out of dependabot groups ([#12329](https://github.com/diegosouzapw/OmniRoute/pull/12329)) +- **test(executors):** restore the #10986 reasoning-only fallback guards ([#12364](https://github.com/diegosouzapw/OmniRoute/pull/12364)) — thanks @pacocartones +- **chore(quality):** re-tighten the file-size ratchet to the real LOC (plan 3.8.52 task 0) ([#12411](https://github.com/diegosouzapw/OmniRoute/pull/12411)) +- **test(grok):** format web executor suite ([#12412](https://github.com/diegosouzapw/OmniRoute/pull/12412)) +- **chore(quality):** ratchet open-sse typecheck baseline to zero ([#12418](https://github.com/diegosouzapw/OmniRoute/pull/12418)) +- **test(build):** guard the artifact path policy arrays against duplicates ([#12422](https://github.com/diegosouzapw/OmniRoute/pull/12422)) +- **chore(lint):** adopt eslint-plugin-react-hooks 7.1.1 ([#12428](https://github.com/diegosouzapw/OmniRoute/pull/12428)) +- **chore(providers):** remove a keyless provider integration at its operator's request ([#12440](https://github.com/diegosouzapw/OmniRoute/pull/12440)) +- **docs(providers):** register providers removed at their operator's request and guard against reintroduction ([#12478](https://github.com/diegosouzapw/OmniRoute/pull/12478)) +- **chore(quality):** base fixes — stryker tap.testFiles + node_modules cache key ([#12482](https://github.com/diegosouzapw/OmniRoute/pull/12482)) +- **docs(arch):** one-process recipe for tens of long /v1/responses ([#12493](https://github.com/diegosouzapw/OmniRoute/pull/12493)) — thanks @RaviTharuma +- **chore(quality):** tighten the CodeQL ratchet baseline from 11 to 6 ([#12530](https://github.com/diegosouzapw/OmniRoute/pull/12530)) +- **chore(deps):** pin browserslist override to ^4.28.8 ([#12592](https://github.com/diegosouzapw/OmniRoute/pull/12592)) — thanks @KooshaPari +- **chore(deps):** bump fast-uri to 3.1.7 in the electron lockfile ([#12601](https://github.com/diegosouzapw/OmniRoute/pull/12601)) +- **docs(quality):** document how the CodeQL ratchet refreshes and how to tighten it ([#12611](https://github.com/diegosouzapw/OmniRoute/pull/12611)) +- **test(ci):** pin the openapi-security-tiers two-arm contract with an executing gate test (#12581) ([#12652](https://github.com/diegosouzapw/OmniRoute/pull/12652)) +- **docs(gamification):** align XP Rewards table with code (#12501) ([#12667](https://github.com/diegosouzapw/OmniRoute/pull/12667)) — thanks @KooshaPari +- **refactor(combo):** split executeTarget into gates, attempt, and loop ([#12746](https://github.com/diegosouzapw/OmniRoute/pull/12746)) — thanks @HouMinXi +- **docs(security):** document socket.yml scanner config + CI workflow link (#12575) ([#12764](https://github.com/diegosouzapw/OmniRoute/pull/12764)) — thanks @KooshaPari +- **chore(ci):** guard commit identity in pre-commit to stop author misattribution ([#12772](https://github.com/diegosouzapw/OmniRoute/pull/12772)) +- **deps:** 17 Dependabot bumps — bump electron from 43.4.0 to 43.4.1 in /electron ([#11426](https://github.com/diegosouzapw/OmniRoute/pull/11426)); bump the development group across 1 directory with 8 updates ([#11428](https://github.com/diegosouzapw/OmniRoute/pull/11428)); bump the production group across 1 directory with 11 updates ([#11440](https://github.com/diegosouzapw/OmniRoute/pull/11440)); chore(deps): bump github/codeql-action/init from 4.37.7 to 4.37.8 ([#11925](https://github.com/diegosouzapw/OmniRoute/pull/11925)); chore(deps): bump github/codeql-action from 4.37.7 to 4.37.8 ([#11926](https://github.com/diegosouzapw/OmniRoute/pull/11926)); chore(deps): bump github/codeql-action/analyze from 4.37.7 to 4.37.8 ([#11927](https://github.com/diegosouzapw/OmniRoute/pull/11927)); build(deps): bump github/codeql-action/init from 4.37.8 to 4.37.9 ([#12345](https://github.com/diegosouzapw/OmniRoute/pull/12345)); build(deps): bump github/codeql-action/analyze from 4.37.8 to 4.37.9 ([#12346](https://github.com/diegosouzapw/OmniRoute/pull/12346)); bump the development group across 1 directory with 2 updates ([#12347](https://github.com/diegosouzapw/OmniRoute/pull/12347)); build(deps): bump github/codeql-action from 4.37.8 to 4.37.9 ([#12349](https://github.com/diegosouzapw/OmniRoute/pull/12349)); bump browserslist from 4.28.2 to 4.28.8 ([#12396](https://github.com/diegosouzapw/OmniRoute/pull/12396)); bump the production group across 1 directory with 4 updates ([#12399](https://github.com/diegosouzapw/OmniRoute/pull/12399)); bump @xmldom/xmldom ([#12500](https://github.com/diegosouzapw/OmniRoute/pull/12500)); bump qs from 6.15.2 to 6.16.0 ([#12512](https://github.com/diegosouzapw/OmniRoute/pull/12512)); bump @xmldom/xmldom from 0.9.10 to 0.9.12 ([#12513](https://github.com/diegosouzapw/OmniRoute/pull/12513)); bump fast-uri from 3.1.5 to 3.1.7 ([#12514](https://github.com/diegosouzapw/OmniRoute/pull/12514)); bump @humanfs/node from 0.16.7 to 0.16.8 ([#12515](https://github.com/diegosouzapw/OmniRoute/pull/12515)) +- Clear the `release/v3.8.51` `check:agent-skills-sync` base-red: regenerate `skills/cli-tunnel/SKILL.md` so the `tunnel create [type]` positional that #13009 taught the generator to read is reflected in the committed skill. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` typecheck base-red from #12731: the new `Mode pack ranking gates` candidates in `open-sse/services/autoCombo/__tests__/autoCombo.test.ts` omitted the required `provider`, `model` and `errorRate` fields of `ProviderCandidate`, failing `check:open-sse-typecheck` (and with it `Fast Quality Gates`) on every open PR. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` docs-sync base-red: EURouter (#13025) and GreenPT (#13024) took the live provider count to 358, leaving 7 STRICT drifts (`PROVIDER_REFERENCE.md`, 4 diagrams, 2 tier-flow images) plus the `AGENTS.md` / `llm.txt` / `package.json` count claims stale. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- **test(models):** Pin `CATALOG_BUILD_TIMEOUT_MS` in the #12058 canonical-catalog regression so a cold catalog build racing the 8s default bound no longer flakes the suite with a 500 ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Allow `@omniroute/opencode-plugin-v2/` in the npm pack policy — [#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870) shipped the v2 plugin beside its v1 sibling without widening the allowlist, so every packed file under it read as an unexpected artifact and failed `check:pack-policy` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Point the release-green validator's pack gate at the tree it is validating — the build-provenance guard defaulted to `origin/main`, which a release branch tip can never be an ancestor of mid-cycle, so `Package artifact` reported a false red on every run and the tarball boot-smoke cascaded off it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **docs(checks):** keep doc counts honest — headings, rankings, catalog, weights, quality gate and scoring diagram now covered ([#12507](https://github.com/diegosouzapw/OmniRoute/pull/12507)) — thanks @maxmad64bis +- **chore(lifecycle):** `check:model-lifecycle` now also diffs `DEFAULT_DEGRADATION_MAP` (the background-task redirect table) against the vendor lifecycle snapshot, refusing a retired id as source or target, with a table-driven unit test beside it. Three rows whose source the vendor had retired — `claude-sonnet-4-20250514`, `gemini-3-pro-preview` and `gpt-5.1-codex` (whose target `gpt-5.1-codex-mini` is retired too) — were dead code, since `checkLifecycle` answers 410 before the redirect runs; they are dropped (#12535 — thanks @pacocartones) +- **test(compression):** cover idle worker eviction at the resource level — the pool must call `terminate()` and must not retain the worker's `MessagePort`, complementing the `exit`-event assertion added with the fix ([#13371](https://github.com/diegosouzapw/OmniRoute/pull/13371)) +- **chore(quality):** raise the `imageGeneration.ts` file-size ceiling for the image-only-model guard that clears the #12945 base-red ([#13392](https://github.com/diegosouzapw/OmniRoute/pull/13392)) +- **chore(quality):** type the combo-test route's JSON response bodies instead of casting them to `any`, drop the now-empty suppression entry, and remove the `hasPerModelQuota` import `chatCore.ts` stopped using when the failure-classification helper was extracted ([#13379](https://github.com/diegosouzapw/OmniRoute/pull/13379)) +- **chore(electron):** the desktop bundle no longer ships the root-level files of every translated docs mirror (`README.md`, `llm.txt`, `CLAUDE.md`, `GEMINI.md`, `CONTRIBUTING.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`) — the packaged app only reads `docs/i18n//docs/**`, which stays. Saves ~11 MB on top of the translated CHANGELOGs already pruned. (#0000) ([#13235](https://github.com/diegosouzapw/OmniRoute/pull/13235)) +- **chore(quality):** retighten the `src/sse/handlers/chat.ts` unused-vars suppression count to the 8 violations that actually remain ([#13385](https://github.com/diegosouzapw/OmniRoute/pull/13385)) +- Bring the documented counts back in line with the code: 171 migrations (was 169) and 20 routing strategies (was 19), plus the regenerated `cli-tunnel` skill reference. ([#12970](https://github.com/diegosouzapw/OmniRoute/pull/12970)) +- Update the documented migration count to 172 after the call-logs provider-stats indexes landed. ([#13209](https://github.com/diegosouzapw/OmniRoute/pull/13209)) +- **chore(ci):** two gates that close the blind spots behind the exclusions above. `check:vitest-exclusions` requires every Vitest exclusion to name a tracking issue and to appear in `config/quality/vitest-exclusions.json` — the previous list grew to 62 files behind a comment pointing at an issue that had been closed for a month. `check-new-key-coverage` requires a key newly added to `en.json` to reach every locale; the existing coverage gate is a percentage floor per locale, so eleven absent keys out of ~13,000 left it at 99.9% while a whole feature shipped untranslated in nine languages. Both are diff-aware, so pre-existing debt stays frozen and neither needed a migration to turn on. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **build:** root layout no longer loads Inter from `next/font/google`, so a production image build does not need fonts.googleapis.com ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **docs(i18n):** bring the eight new `llm.txt` mirrors (km, kn, ml, my, ne, or, pa, si) up to the current provider and migration counts, so `check-docs-sync` stops rejecting every commit ([#13674](https://github.com/diegosouzapw/OmniRoute/pull/13674)) +- **refactor(ui):** flow surfaces (home topology, combo live studio, compression cockpit/waterfall/nodes, token-health badge) express state through the theme-aware `--orch-status-*` tokens instead of fixed dark-mode hex, so green/red/amber/grey stay legible in the light theme; dark mode is byte-identical. Categorical palettes (routing-strategy hues, compression-layer pills, provider brand colors) deliberately stay hex ([#12378](https://github.com/diegosouzapw/OmniRoute/issues/12378)) ([#13203](https://github.com/diegosouzapw/OmniRoute/pull/13203)) +- **chore(tests):** 51 test files that had been excluded from Vitest are running again, restoring roughly 350 assertions to the blocking suite. Each was measured individually first: of the 62 files parked behind the `// #8618 — pre-existing failure` comment, 51 pass against the current tree with no source change, so the exclusions had outlived the failures they were added for. The 11 that genuinely still fail stay excluded, but now point at a live tracker (#13204) rather than at #8618, which was closed in August while the list it tracked kept growing. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **chore(quality):** drop two ESLint suppression entries whose violations no longer exist, so `eslint --suppressions-location` stops rejecting every commit that touches the surrounding files ([#13384](https://github.com/diegosouzapw/OmniRoute/pull/13384)) +- **chore(release):** bring main's post-tag fixes into release/v3.8.51 — sync-back step 2/2 (direct commit `21c488f210`) +- **chore(deps):** patch toml and esbuild in the opencode plugin lockfiles ([#12965](https://github.com/diegosouzapw/OmniRoute/pull/12965)) +- **docs(changelog):** reconcile the v3.8.51 living section — fold 366 fragments, cover every cycle commit, credit every contributor ([#12971](https://github.com/diegosouzapw/OmniRoute/pull/12971)) +- **test(plugin):** wait for the v2 lazy-refresh tier instead of sleeping past it ([#12984](https://github.com/diegosouzapw/OmniRoute/pull/12984)) +- **chore(quality):** rebaseline chatCore.ts after the non-streaming regression fixes ([#13045](https://github.com/diegosouzapw/OmniRoute/pull/13045)) +- **docs(changelog):** add the missing bullet marker to the DeepSeek PoW fragment ([#13200](https://github.com/diegosouzapw/OmniRoute/pull/13200)) +- **chore(deps):** drain the Dependabot queue — 10 of 13 alerts ([#13213](https://github.com/diegosouzapw/OmniRoute/pull/13213)) +- **chore(ci):** make worktrees carry the hooks that enforce the identity gate ([#13236](https://github.com/diegosouzapw/OmniRoute/pull/13236)) +- **chore(deps):** pin csv-parse to ^7.0.2 via overrides (#13117) ([#13300](https://github.com/diegosouzapw/OmniRoute/pull/13300)) — thanks @KooshaPari +- **chore(deps):** bump hono from ^4.12.34 to ^4.13.7 (#13148) ([#13301](https://github.com/diegosouzapw/OmniRoute/pull/13301)) — thanks @KooshaPari +- **chore(deps):** pin joi to ^18.2.8 via overrides (#13085) ([#13302](https://github.com/diegosouzapw/OmniRoute/pull/13302)) — thanks @KooshaPari +- **docs(providers):** add Microsoft 365 Copilot (BizChat) provider guide (#12779) ([#13340](https://github.com/diegosouzapw/OmniRoute/pull/13340)) — thanks @KooshaPari +### 🙌 Contributors + +Thanks to everyone whose work landed in v3.8.51: + +| Contributor | PRs / Issues | +| --- | --- | +| [@5dive-bot](https://github.com/5dive-bot) | #11852 | +| [@abhisheksharma2411](https://github.com/abhisheksharma2411) | #11547, #11684, #11959, #12209 | +| [@adevwithpurpose](https://github.com/adevwithpurpose) | #11464 | +| [@adivekar-utexas](https://github.com/adivekar-utexas) | #12015, #12043, #12090, #12138 | +| [@AIB1TAL0S](https://github.com/AIB1TAL0S) | #12103 | +| [@alltomatos](https://github.com/alltomatos) | #11448 | +| [@alvinveroy](https://github.com/alvinveroy) | #11857, #12027, #12165 | +| [@amaleta](https://github.com/amaleta) | #11906, #11934 | +| [@amartinawi](https://github.com/amartinawi) | #11575 | +| [@AndrianBalanescu](https://github.com/AndrianBalanescu) | #11083, #11830, #11834, #11844 | +| [@anhtahaylove](https://github.com/anhtahaylove) | #13091, #13092, #13093, #13096, #13097, #13100, #13106, #13109, #13114, #13155, #13166, #13171, #13175, #13187, #13196 | +| [@aniruddhaadak80](https://github.com/aniruddhaadak80) | #11760 | +| [@app](https://github.com/app) | #12554 | +| [@arminanton](https://github.com/arminanton) | #11461, #11513 | +| [@AStupidBear](https://github.com/AStupidBear) | #11584 | +| [@atescivitci-cmd](https://github.com/atescivitci-cmd) | #12105 | +| [@b3nw](https://github.com/b3nw) | #11970, #11971, #12147 | +| [@backryun](https://github.com/backryun) | #11259, #11950, #12075, #12076, #12078, #12079, #12081, #12082, #12181, #12228, #12239, #12255, #12258, #12277, #12367, #12423, #12524, #12538 | +| [@benjaminkitt](https://github.com/benjaminkitt) | #11747 | +| [@benzntech](https://github.com/benzntech) | #11614, #11615 | +| [@Bl0ck154](https://github.com/Bl0ck154) | #11948, #11951, #11952, #11953, #11954 | +| [@brick30llc-ctrl](https://github.com/brick30llc-ctrl) | #11752, #12242, #12262 | +| [@Chewji9875](https://github.com/Chewji9875) | #11455, #11875, #12028 | +| [@cheynetom](https://github.com/cheynetom) | #12058 | +| [@davidebaraldo](https://github.com/davidebaraldo) | #12222, #12304 | +| [@davidlinfr](https://github.com/davidlinfr) | #6390 | +| [@ddarkr](https://github.com/ddarkr) | #12124 | +| [@Deftera186](https://github.com/Deftera186) | #11809 | +| [@dpozimski](https://github.com/dpozimski) | #11945 | +| [@DW-MediaLab](https://github.com/DW-MediaLab) | #12229 | +| [@echel0nn](https://github.com/echel0nn) | #11923 | +| [@f9td56dbgh-hub](https://github.com/f9td56dbgh-hub) | #11660 | +| [@fabioluissilva](https://github.com/fabioluissilva) | #11991 | +| [@foreveryh](https://github.com/foreveryh) | #12177 | +| [@geek007git](https://github.com/geek007git) | #12115, #12116, #12117, #12120, #12122 | +| [@geekyNads](https://github.com/geekyNads) | #11784, #12266 | +| [@ggiak](https://github.com/ggiak) | #11561, #12276, #12350, #12402 | +| [@gonisulaimann](https://github.com/gonisulaimann) | #12368, #12369, #12371 | +| [@Gorillaz322](https://github.com/Gorillaz322) | #12207 | +| [@groovecityJO](https://github.com/groovecityJO) | #12682 | +| [@hartmark](https://github.com/hartmark) | #11434, #11452, #11473, #11499, #11703, #11983, #11984, #11985, #11986, #11988, #11989, #11990, #11994, #12221, #12293, #12445, #12446, #12447, #12448, #12460, #12461, #12623, #12646, #12650, #12680, #12717, #12718, #12727, #12741, #12854 | +| [@hizzt](https://github.com/hizzt) | #11894 | +| [@hongnoul](https://github.com/hongnoul) | #11484 | +| [@HouMinXi](https://github.com/HouMinXi) | #11411, #11414, #11512, #11518, #11520, #11641, #11642, #11643, #11687, #11779, #11849, #11850, #11851, #11915, #11916, #11918, #11919, #11920, #12013, #12017, #12033, #12042, #12106, #12139, #12166, #12169, #12171, #12205, #12213, #12312, #12325, #12487, #12488, #12495, #12504, #12557, #12566, #12590, #12591, #12624, #12626, #12632, #12637, #12678, #12696, #12697, #12711, #12733, #12746, #12767, #12770, #12789, #12803, #12805, #12811, #12866, #12868, #12899, #12926, #12934, #12950, #12951, #12974, #13001, #13006, #13011, #13017, #13026, #13027, #13034, #13035, #13038, #13042, #13050, #13060, #13061, #13069, #13107, #13120, #13136, #13195, #13197 | +| [@Hsia97](https://github.com/Hsia97) | #11624 | +| [@jacobsparts](https://github.com/jacobsparts) | #11854, #12155, #12167 | +| [@joglomedia](https://github.com/joglomedia) | #11980 | +| [@jonlwheat2-gif](https://github.com/jonlwheat2-gif) | #11443, #11450, #11549, #11567, #11608, #11633, #11635, #11640, #11644, #11671, #11672, #11673, #11674, #11675, #11676, #11677, #11706, #11783, #12051, #12052, #12053, #12055 | +| [@kanade-hoshino](https://github.com/kanade-hoshino) | #12180 | +| [@Karan825](https://github.com/Karan825) | #11861 | +| [@KaspaPulse](https://github.com/KaspaPulse) | #11389, #11469, #11628, #11666, #11910 | +| [@keeltrace](https://github.com/keeltrace) | #12080, #12223 | +| [@killer30001000](https://github.com/killer30001000) | #12178 | +| [@KooshaPari](https://github.com/KooshaPari) | #12470, #12592, #12667, #12699, #12703, #12706, #12764, #12769, #12771, #13300, #13301, #13302, #13340, #13401, #13403, #13404, #13405, #13406, #13407, #13409, #13410, #13411, #13412, #13413, #13414, #13418, #13424, #13427, #13433, #13523, #13524, #13525, #13528, #13531, #13532, #13534, #13539, #13543, #13545, #13546, #13547, #13550, #13553 | +| [@kriptoburak](https://github.com/kriptoburak) | #11370 | +| [@KrzysiekSko](https://github.com/KrzysiekSko) | #12425, #12432, #12673 | +| [@marcelokarval](https://github.com/marcelokarval) | direct commit / report | +| [@maxmad64bis](https://github.com/maxmad64bis) | #1622, #11435, #11437, #11441, #11537, #11550, #11553, #11555, #11557, #11812, #11842, #11843, #11903, #12151, #12214, #12215, #12218, #12226, #12314, #12316, #12317, #12318, #12319, #12320, #12321, #12507, #12715, #12731, #12744, #12786, #12787, #12788, #12790, #12792, #12794, #12795, #12828, #12832, #12853, #12857, #12870, #12937, #12941, #12975, #13141, #13142, #13146, #13147 | +| [@mdigitalbh81](https://github.com/mdigitalbh81) | #12240 | +| [@Meet6338-X](https://github.com/Meet6338-X) | #11598, #11609, #12110 | +| [@morpheus9393](https://github.com/morpheus9393) | #11943 | +| [@MumuTW](https://github.com/MumuTW) | #11492, #11502, #11506, #11507, #11626, #11675, #11685, #11728, #11746, #11888, #11889, #11890, #11892 | +| [@Neuron-Mr-White](https://github.com/Neuron-Mr-White) | #11622, #11801, #12256 | +| [@NightStalker-87](https://github.com/NightStalker-87) | #12183 | +| [@NoxzRCW](https://github.com/NoxzRCW) | #11879, #11880, #11881, #11882, #11883, #11935 | +| [@ntdat812](https://github.com/ntdat812) | #11585, #12095 | +| [@ntdatt812](https://github.com/ntdatt812) | #11368, #11573, #11574, #11576, #11577, #11580, #11582, #11583, #11588, #11589, #11590, #11591, #11592, #11593, #11672, #12177, #12180, #12873, #12918, #12920, #12921, #12925, #12930, #13007, #13009, #13024, #13025, #13083, #13087, #13101, #13104, #13110 | +| [@official-burak](https://github.com/official-burak) | #11542 | +| [@opensource-elearning](https://github.com/opensource-elearning) | #12179, #12189, #12278, #12286 | +| [@oyi77](https://github.com/oyi77) | #11408, #11409, #11421, #11505, #11677, #12036, #12110 | +| [@pacocartones](https://github.com/pacocartones) | #11521, #11522, #11527, #11528, #11529, #11530, #11531, #11532, #11533, #11534, #11595, #11599, #11603, #11604, #11605, #11607, #11610, #11676, #11714, #11716, #11718, #11767, #11838, #11860, #11862, #11869, #11871, #11872, #11873, #11903, #11906, #11921, #11934, #12356, #12358, #12359, #12360, #12361, #12362, #12364, #12365, #12373, #12374, #12375, #12376, #12377, #12379, #12380, #12381, #12386, #12387, #12389, #12390, #12394, #12395, #12397, #12401, #12403, #12404, #12522, #12523, #12535, #12536, #12540, #12541, #12543, #12545, #12548, #12549, #12550, #12551, #12552, #12647, #12651, #12653, #13055 | +| [@patrykkopycinski](https://github.com/patrykkopycinski) | #11936, #11937, #12224 | +| [@PauloFH](https://github.com/PauloFH) | #11509 | +| [@PauloHSOliveira](https://github.com/PauloHSOliveira) | #12241 | +| [@ponkcore](https://github.com/ponkcore) | #12054 | +| [@Prajeeth-12](https://github.com/Prajeeth-12) | #11634, #12046 | +| [@quiterunner-commits](https://github.com/quiterunner-commits) | #12131, #12143 | +| [@rafacpti23](https://github.com/rafacpti23) | #11554, #11558, #12192, #12197, #12198, #12202, #12203, #12204 | +| [@ragnar-claude](https://github.com/ragnar-claude) | #11564 | +| [@raheemuddin786](https://github.com/raheemuddin786) | #11491, #11839, #11840, #11841, #12003, #12230, #12231, #12232, #12233, #12234 | +| [@rahilmavani](https://github.com/rahilmavani) | #11761 | +| [@Rahulsharma0810](https://github.com/Rahulsharma0810) | #11771 | +| [@RaviTharuma](https://github.com/RaviTharuma) | #11710, #11727, #11797, #11798, #11802, #11805, #11806, #11811, #12098, #12099, #12101, #12449, #12452, #12472, #12473, #12493, #12533, #12607, #12628, #12631, #12636, #12875, #12876, #12880, #12882, #12884 | +| [@rezjalibd](https://github.com/rezjalibd) | #12186 | +| [@RhianB14](https://github.com/RhianB14) | #11692 | +| [@rifqiawl](https://github.com/rifqiawl) | #11517, #11519, #11969 | +| [@rqzbeh](https://github.com/rqzbeh) | #11390 | +| [@Sabeekhann](https://github.com/Sabeekhann) | #11699, #11705 | +| [@santosraju99-hub](https://github.com/santosraju99-hub) | #11755, #11770 | +| [@Seramicx](https://github.com/Seramicx) | #11568 | +| [@Siva010](https://github.com/Siva010) | #12191 | +| [@solstxce](https://github.com/solstxce) | #11597 | +| [@soroush5](https://github.com/soroush5) | #12691 | +| [@tenshiak](https://github.com/tenshiak) | #12279 | +| [@TheDemonTuan](https://github.com/TheDemonTuan) | #11468, #11470, #11471, #11482, #11548, #11758, #11775, #11814 | +| [@thomasmaerz](https://github.com/thomasmaerz) | #12834 | +| [@tuandinh0801](https://github.com/tuandinh0801) | #11454 | +| [@turbolego](https://github.com/turbolego) | #11621, #11762, #11772, #11774, #11781, #12216 | +| [@Tushar49](https://github.com/Tushar49) | #11392 | +| [@ujjawalkaushik1110](https://github.com/ujjawalkaushik1110) | #11863 | +| [@vermasomesh835](https://github.com/vermasomesh835) | #11794 | +| [@vsd2807](https://github.com/vsd2807) | #11565, #11619 | +| [@wahidsadik371-coder](https://github.com/wahidsadik371-coder) | #12126 | +| [@watchingdogs](https://github.com/watchingdogs) | #12031 | +| [@wildcard](https://github.com/wildcard) | #11620 | +| [@xiaoyaner0201](https://github.com/xiaoyaner0201) | #11460, #11662, #12083 | +| [@Xxx91n](https://github.com/Xxx91n) | #11690 | +| [@yourspraveen](https://github.com/yourspraveen) | #11146 | +| [@yxyxy](https://github.com/yxyxy) | #11671 | +| [@ZaimMarzuki](https://github.com/ZaimMarzuki) | #11960 | +| [@Zartharas](https://github.com/Zartharas) | #11340 | +| [@zero-executioner](https://github.com/zero-executioner) | #11631 | +| [@diegosouzapw](https://github.com/diegosouzapw) | maintainer | + +--- ## [3.8.50] — 2026-08-25 _Living section — regenerated 2026-08-12 from all cycle commits (cycle open `ed2db6cb19` → tip). Bullets carry the merged PR and its author; direct pushes listed separately._ diff --git a/docs/i18n/ur/CHANGELOG.md b/docs/i18n/ur/CHANGELOG.md index 6b80c3e1c4..6da27516de 100644 --- a/docs/i18n/ur/CHANGELOG.md +++ b/docs/i18n/ur/CHANGELOG.md @@ -8,16 +8,1119 @@ ## [3.8.51] — TBD -_Living section — cycle opened at the v3.8.50 freeze (parallel-cycle model). Bullets are aggregated from `changelog.d/` fragments at each `/generate-release` phase._ +_Living section — reconciled 2026-09-15 from all cycle commits (`091589089c` → `c0f92ec98a`, 916 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 | **123** | +| 📝 Commits in the cycle | **916** | +| 🔀 Pull requests referenced | **892** | +| 📋 Changelog entries | **919** | +| 🙌 Contributors credited in entries | **121** | +| 🤖 Automated dependency commits | 18 | + +**Entries by type** + +| Type | Count | +| --- | ---: | +| 🐛 Fixes | 592 | +| ✨ Features | 131 | +| 🧹 Chore | 93 | +| 📚 Docs | 42 | +| 🧪 Tests | 32 | +| ♻️ Refactor | 8 | +| 🏗️ Build | 7 | +| ⚡ Performance | 7 | +| ⚙️ CI | 2 | +| ⏪ Reverts | 2 | +| 📦 Dependencies | 2 | +| 🔒 Security | 1 | + +### 🏆 Top 25 contributors this cycle + +_By commits in `091589089c..c0f92ec98a`, author identities consolidated via `.mailmap` and the merged PR's GitHub login. Bots excluded._ + +| # | Contributor | Commits | +| ---: | --- | ---: | +| 🥇 | diegosouzapw | 323 | +| 🥈 | Bob.Hou (@HouMinXi) | 79 | +| 🥉 | Paco Cartones (@pacocartones) | 77 | +| 4 | Dizzle (@maxmad64bis) | 49 | +| 5 | Koosha Paridehpour (@KooshaPari) | 43 | +| 6 | Nguyen Thanh Dat (@ntdatt812) | 31 | +| 7 | Ravi Tharuma (@RaviTharuma) | 26 | +| 8 | Markus Hartung (@hartmark) | 22 | +| 9 | Webman (@jonlwheat2-gif) | 22 | +| 10 | backryun | 17 | +| 11 | anhtahaylove | 15 | +| 12 | Rafa Martins (@rafacpti23) | 10 | +| 13 | Syed Raheemuddin (@raheemuddin786) | 10 | +| 14 | Nguyễn Viết Tuấn (@TheDemonTuan) | 8 | +| 15 | MumuTW | 7 | +| 16 | Paijo (@oyi77) | 7 | +| 17 | Tobias Andersen (@turbolego) | 6 | +| 18 | Bl0ck (@Bl0ck154) | 5 | +| 19 | SHANMUGAPRIYAN (@geek007git) | 5 | +| 20 | KaspaPulse | 5 | +| 21 | Abhishek Sharma (@abhisheksharma2411) | 4 | +| 22 | Abhishek Divekar (@adivekar-utexas) | 4 | +| 23 | Andrew B. (@AndrianBalanescu) | 4 | +| 24 | NoxzRCW | 4 | +| 25 | opensource-elearning | 4 | ### ✨ New Features +- **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). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- Added Google AI Studio Gemini batch text-to-speech support through `POST /v1/audio/speech`. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- 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. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(routing):** subscription-first auto groupings — `auto/subscription` routes only through plan-included connections with a documented hard-stop overage and fails closed on exhaustion, while `auto/thrifty` orders the pool `subscription → keyless → free → cheap → premium` and steps up one rung at a time as each is exhausted. Billing class comes from a curated per-connection catalog (uncurated is treated as metered, never plan-included), both reuse STRICT_ZERO_COST's per-connection verification, and a quota reading whose `resetAt` has passed is now refreshed regardless of TTL so routing returns to plan capacity as soon as it resets ([#11146](https://github.com/diegosouzapw/OmniRoute/pull/11146)) — thanks @yourspraveen +- **feat(providers):** publish a management-authenticated versioned web-session credential contract from OmniRoute's canonical browser credential metadata ([#11340](https://github.com/diegosouzapw/OmniRoute/pull/11340)) — thanks @Zartharas +- **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)) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add Xquik X search with typed results, credential validation, REST routing, and MCP selection ([#11370](https://github.com/diegosouzapw/OmniRoute/pull/11370)) — thanks @kriptoburak +- **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)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(dashboard):** surface durable exclusive managed leases in the existing Sessions view, keeping leased clients visible across idle gaps while marking connections with in-flight work as active ([#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389)) — thanks @KaspaPulse +- **build(bun):** allow Turbopack bundler flag on Bun 1.4+ with configurable Webpack fallback ([#11471](https://github.com/diegosouzapw/OmniRoute/pull/11471)) — thanks @TheDemonTuan +- feat(api): add an opt-in `modelVisibilityAllowlist`/`modelVisibilityDenylist` settings pair to curate exactly which models `/v1/models` advertises, mirrored into every `auto/*` combo candidate pool so a denied model cannot be routed to via combo selection either (#11481) ([#11997](https://github.com/diegosouzapw/OmniRoute/pull/11997)) +- **feat(rankings):** the Free Provider Rankings page shows what each provider actually served over the last 24 h. It ranked by ELO alone, which left a provider that answers every call with an error in first place; the usage data was already served by the API but never requested. A provider with too small a sample shows a dash, not a number ([#11546](https://github.com/diegosouzapw/OmniRoute/pull/11546)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **feat(guardrails):** enforce a bounded, deterministic contract for Video Bridge transcripts — 256 cues, 4096 input code units and 4 KiB UTF-8 per cue, 64 KiB total text, malformed-Unicode rejection, focus-window scoping, cross-source reconciliation with contributing-source metadata, and a structural provenance trust boundary so caller JSON can never self-assert `embedded`/`audio-bridge` provenance ([#11652](https://github.com/diegosouzapw/OmniRoute/issues/11652)) ([#12009](https://github.com/diegosouzapw/OmniRoute/pull/12009)) +- **feat(video):** orchestrate optional Video Bridge audio extraction and Audio Bridge STT behind a dual opt-in (operator setting AND per-request signal) — a new loopback-only broker `mode=audio` operation shares the frame path's exact process queue, deadline, AbortSignal, and byte budgets to extract a bounded mono 16 kHz PCM WAV from the same already-downloaded video, then reuses the existing Audio Bridge transcription boundary; provider segment timing is preserved when available and marked coarse otherwise, and every failure degrades to a visual-only-safe partial instead of throwing (#11654). ([#12012](https://github.com/diegosouzapw/OmniRoute/pull/12012)) +- Add a tenant-bound Video Bridge drill-down lifecycle on top of the existing secure cache substrate: opaque hashed handles (never raw session/video identifiers), preview/standard/detail multiresolution variants resampled on read, response pagination capped at 8 frames and 32 MiB, and a new authenticated `/api/v1/video-bridge/drilldown` consumer route that stays disabled for remote access by default and denies cross-key access with the same response as a nonexistent handle (no existence oracle). ([#12006](https://github.com/diegosouzapw/OmniRoute/pull/12006)) +- **test(video):** Add the Video Bridge FU-07/FU-09 promotion-evidence harness (#11656) — a frozen Zod manifest schema covering the 8 required scenario kinds (static scenes, rapid cuts, late facts, fades, blur, small text, close events, visual prompt injection) with a minimum of 3 repetitions per case, deterministic declarative fixture recipes (`videoBridgePromotionFixtures.ts`), a pure medians/p95 metrics aggregator, a pure FU-07/FU-09 promotion-verdict evaluator applying the ticket's exact thresholds (missing token usage always holds), a digest-only persistence layer that never retains raw media or raw model responses, and a versioned per-model promotion allowlist shipped empty with every model defaulting to `hold`. The FU-07/FU-09 promotion verdicts themselves remain HOLD — they require a real evidence run against real models on VPS 192.168.0.15. ([#12008](https://github.com/diegosouzapw/OmniRoute/pull/12008)) +- **feat(video bridge):** "embedded" transcript provenance can now be legitimately earned instead of merely asserted — a bounded, allowlisted (`mov_text`/`subrip`/`webvtt`) subtitle probe runs through the loopback-only Video Bridge broker (at most 2 streams, 10s subdeadline bounded by the request deadline, 256 KiB output, 4096-code-unit lines), normalized through a bounded, ReDoS-safe WebVTT parser and Zod-validated end to end. The adapter always resolves to an explicit `success`/`absent`/`transient_failure` outcome — a subtitle failure never breaks the visual description path, and only a fingerprint-verified broker response (never a caller-declared label) can produce embedded cues (#11659). ([#12011](https://github.com/diegosouzapw/OmniRoute/pull/12011)) +- Default new Antigravity-family connections (agy CLI imports and Antigravity OAuth connects) to model auto-sync, so live model discovery lands in the synced catalog and `/v1/models` picks up freshly released upstream models (e.g. Gemini 3.7 Flash tiers) without code changes. Existing connections keep their current setting; the per-connection dashboard toggle remains the opt-out. (#11685 — thanks @MumuTW) ([#11887](https://github.com/diegosouzapw/OmniRoute/pull/11887)) +- **feat(ui):** enable React Compiler (`reactCompiler: true` + `babel-plugin-react-compiler`) for automatic memoization at build time ([#11783](https://github.com/diegosouzapw/OmniRoute/pull/11783)) — thanks @jonlwheat2-gif +- **feat(providers):** add SeekAi (`seekai.cc`) as an OpenAI-compatible New-API gateway — catalog id `seekai` (alias `ska`), `https://seekai.cc/v1`, live `/v1/models` via `passthroughModels`, aggregator-list membership so New-API balance detection can opt in. No referral/aff codes. ([#11786](https://github.com/diegosouzapw/OmniRoute/issues/11786)) ([#12557](https://github.com/diegosouzapw/OmniRoute/pull/12557)) — thanks @HouMinXi +- **feat(zai):** add GLM-5.3-Flash Coding Plan support (1M context, 128K output, vision, `low|high|max` reasoning) and route `zai` GLM-5.3-family API-key traffic through the OpenAI-compatible Coding Plan endpoint with native thinking defaults ([#11801](https://github.com/diegosouzapw/OmniRoute/pull/11801)) — thanks @Neuron-Mr-White +- **feat(combo):** choose how combo models are ordered — manual, provider, score, or name — via a sort control in the dashboard builder, persisted in `config.modelSort` and re-applied on load and after add ([#11812](https://github.com/diegosouzapw/OmniRoute/pull/11812)) — thanks @maxmad64bis +- **feat(free):** custom models can be marked free-tier via `customModels[].isFree`; `isFreeModel()` is the first door and `hidePaidModels` respects it even for providers outside the free budget ([#11843](https://github.com/diegosouzapw/OmniRoute/pull/11843)) — thanks @maxmad64bis +- **feat(nodejs):** add `5dive` as a `configure` target — `omniroute configure 5dive` / `omniroute setup-5dive` write a 5dive auth profile that points an agent fleet's `claude` seats at OmniRoute, with the root-only write, the loopback-vs-`https` endpoint rule and the per-seat model pin handled explicitly ([#11852](https://github.com/diegosouzapw/OmniRoute/pull/11852)) — thanks @5dive-bot +- **feat(sse):** treat `max` as a first-class reasoning-effort tier and clamp per model family (GLM 5.1+/DeepSeek V4+/Kimi K3+ keep native `max`; o1/MiniMax/Grok/Muse Spark clamp to their upstream ceiling) ([#11875](https://github.com/diegosouzapw/OmniRoute/pull/11875)) — thanks @Chewji9875 +- **feat(providers):** the provider plugin manifest now advertises a `usage-fetch` capability for the 40 providers that have a wired usage/quota fetcher, so external dashboards can read it from `GET /api/v1/provider-plugin-manifest` instead of parsing `open-sse/services/usage.ts` after every release. Discovery only — no new fetcher, no quota change, and the Dashboard quota widget stays gated by `USAGE_SUPPORTED_PROVIDERS`. `USAGE_FETCHER_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/fetcherProviders.ts`) and is re-exported from `services/usage.ts`, keeping the manifest module a light leaf instead of pulling the ~490-module usage dispatcher into the manifest route. ([#11903](https://github.com/diegosouzapw/OmniRoute/pull/11903)) — thanks @maxmad64bis / @pacocartones +- **feat(plugins):** `OMNIROUTE_PLUGINS_DIR` sets the directory the runtime plugin scanner reads — and the root the plugin manager installs into — overriding the `HOME`-derived default, so a Docker/K8s deployment can point straight at its bind-mounted plugin tree instead of moving `HOME` just to relocate the scan path. An image that exports no home no longer scans `/tmp/.omniroute/plugins` in silence: the resolved directory is logged once at startup as `scanner.dir_resolved`, naming the input that won. Unset, behaviour is unchanged. Distinct from the CLI-only `OMNIROUTE_PLUGIN_PATH`, which finds `omniroute-cmd-*` command packages and never reached this scanner ([#11906](https://github.com/diegosouzapw/OmniRoute/pull/11906)) — thanks @amaleta / @pacocartones +- **feat(leases):** add an explicit owner-authenticated status action that returns only the active lease's privacy-safe configured connection and provider labels, with generation fencing and no credential or internal-id disclosure ([#11910](https://github.com/diegosouzapw/OmniRoute/pull/11910)) — thanks @KaspaPulse +- **feat(providers):** import-from-file modal shows per-row API errors and ships a downloadable CSV template ([#12071](https://github.com/diegosouzapw/OmniRoute/issues/12071)) ([#12504](https://github.com/diegosouzapw/OmniRoute/pull/12504)) — thanks @HouMinXi +- **feat(providers):** dashboard search matches connection name and `baseUrl` so imported OpenAI-compat nodes surface on the provider card ([#12108](https://github.com/diegosouzapw/OmniRoute/issues/12108)) ([#12495](https://github.com/diegosouzapw/OmniRoute/pull/12495)) — thanks @HouMinXi +- **feat(routing):** add a `score` Auto router strategy that selects the highest configured weighted score and reuses `explorationRate`. ([#12155](https://github.com/diegosouzapw/OmniRoute/pull/12155)) — thanks @jacobsparts +- Add a runtime feature flag to disable universal context handoffs globally without changing the default behavior. ([#12167](https://github.com/diegosouzapw/OmniRoute/pull/12167)) — thanks @jacobsparts +- **feat(providers):** the provider plugin manifest now also advertises a `usage-supported` capability for the 46 providers whose usage API is accepted by the server and Dashboard routes, so integrators can distinguish "the server will serve quota for this provider" from "a fetcher is wired" without reading TypeScript. Discovery only — no fetcher or quota change. `usage-fetch` resolves on id or alias (the usage dispatcher accepts both); `usage-supported` resolves on id alone, matching the runtime guard `USAGE_SUPPORTED_PROVIDERS.includes(providerId)`. `USAGE_SUPPORTED_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/supportedProviders.ts`) and is re-exported from `providers.ts`, mirroring the `fetcherProviders` leaf from #11903 and keeping the manifest a light module. ([#12214](https://github.com/diegosouzapw/OmniRoute/pull/12214)) — thanks @maxmad64bis +- **feat(rankings):** order Free Provider Rankings by what each provider actually served — `GET /api/free-provider-rankings?sortBy=reliability` and a "Most reliable first" toggle on the page. Providers with too few calls to state a success rate keep their score order below the measured ones; the default order is unchanged ([#12218](https://github.com/diegosouzapw/OmniRoute/pull/12218)). — thanks @maxmad64bis +- **perf(sse):** defer `cloneLogPayload()` in the structured SSE collector until after the `maxEvents`/`maxBytes` cap check, eliminating ~9,800 wasted `structuredClone` calls per streaming response (65–71% faster `push()`). Reducer snapshot isolation restored for OpenAI and Responses summaries ([#12241](https://github.com/diegosouzapw/OmniRoute/pull/12241)) — thanks @PauloHSOliveira ([#12243](https://github.com/diegosouzapw/OmniRoute/pull/12243)) +- **feat(usage):** Devin CLI agentic quota (Codeium seat-management GetUserStatus) and OpenRouter key limits plus account credits now surface in Provider Limits ([#12256](https://github.com/diegosouzapw/OmniRoute/pull/12256) — thanks @Neuron-Mr-White) +- **feat(settings):** persist `headroomUrl` through Settings so status/start use the operator URL instead of only `HEADROOM_URL` ([#12306](https://github.com/diegosouzapw/OmniRoute/issues/12306)) ([#12487](https://github.com/diegosouzapw/OmniRoute/pull/12487)) — thanks @HouMinXi +- **feat(auto-combo):** Auto-Combo scoring can now weigh how often a provider/model has actually succeeded. The engine already carried that number on every candidate — 24 hours of usage history behind a ten-sample floor, real-time metrics otherwise — and the scoring function never read it, while the weight table described `stability` as if it did. `reliability` (`1 - failureRate`, with the same field precedence and the same rate-bounding the speed ranking already uses, so a corrupt reading means "nothing observed" rather than "fails every call") is now a declared factor shipping at weight `0`, so routing is unchanged until an operator gives it one, and the `stability` description now matches what that factor computes ([#12317](https://github.com/diegosouzapw/OmniRoute/pull/12317)) — thanks @maxmad64bis +- **feat(routing):** With `freeAccessPolicy: "strict"`, the read-only candidate listing (`GET /v1/auto-combo/{channel}/candidates`) no longer hides the candidates the zero-cost guard excludes — the same read-only transparency the resilience filter already honours (#9133). Each candidate now carries `freeAccessExclusion` saying why it would be kept out, and it tells an exhausted allowance apart from a quota reading that never arrived or went stale, which used to look identical from the outside. Routing is unchanged: the listing reports, it never enforces. The separate `excludeTosAvoid` guard still drops its candidates without a reason; that gap is now documented rather than closed ([#12319](https://github.com/diegosouzapw/OmniRoute/pull/12319)) — thanks @maxmad64bis +- **feat(radar):** The Radar catalog table now shows two facts it was already receiving from the feed and dropping on the floor: the per-model rate limits (requests and tokens, per minute and per day) in a new column, and a badge when a provider's terms state it may train on the prompts you send. A limit of zero renders as zero rather than "rate-only" — for a ceiling those are opposite facts — and a model with no training statement gets no badge, because an absent statement is not a guarantee ([#12320](https://github.com/diegosouzapw/OmniRoute/pull/12320)) — thanks @maxmad64bis +- **feat(radar):** explain Community, single-use, contributor, supporter, recovery, abuse, offers, and privacy rules before either Radar activation action, and remove the superseded fixed-PR grant promise from every UI locale ([#12342](https://github.com/diegosouzapw/OmniRoute/pull/12342)) +- **feat(gamification):** the dashboard Profile page now shows the real daily streak — `/api/gamification/level` returns `streak: { current, longest }` (per key with `apiKeyId`, operator-wide maximum otherwise) and the streak card reads it instead of a hard-coded 0 (#2403) ([#12377](https://github.com/diegosouzapw/OmniRoute/pull/12377)) — thanks @pacocartones +- **feat(gamification):** the dashboard leaderboard now shows each API key's display name under the Name column instead of a truncated key id; `GET /api/gamification/leaderboard` attaches `name` per entry (name only — no key material), while the shared ranking helper and the federation leaderboard stay id-only — thanks @pacocartones ([#12385](https://github.com/diegosouzapw/OmniRoute/pull/12385)) +- **feat(gamification):** enforce the documented 1000 XP/min per-API-key anti-cheat rate limit on the XP award path; over-limit awards are logged and skipped instead of persisted, and the sliding window now matches the timestamp format stored in `xp_audit_log` ([#2403](https://github.com/diegosouzapw/OmniRoute/issues/2403)) ([#12390](https://github.com/diegosouzapw/OmniRoute/pull/12390)) — thanks @pacocartones +- **feat(admin):** localize the gamification anomalies page — the loading state, the Status column and the Suspicious badge now come from the `common` catalog (new `common.suspicious` key propagated to every locale) — add it to the Gamification sidebar group as `gamification-admin` (`/dashboard/gamification/admin`), and expose the loading and empty states as polite `role="status"` live regions (#12401 — thanks @pacocartones) +- **feat(providers):** skip GitHub combo members missing from the live synced catalog, and drop Copilot models that are policy-disabled or hidden from the model picker ([#12473](https://github.com/diegosouzapw/OmniRoute/pull/12473)) — thanks @RaviTharuma +- **feat(i18n):** locale-expansion tooling — `npm run i18n:add-locale` adds a language to config, dashboard, docs mirrors, CLI, README/indexes and site in one command; browser, CLI and cookie detection resolve `uk`, `fil`/`tl`, `zh-Hant` (and the retired legacy `in` → `id`) via config aliases; `config/i18n.json` now ships in the npm package so the published CLI can read it; new real-translation ratio gate (`npm run i18n:check-ratio`, advisory) with a per-locale ratchet baseline; `run-translation --adopt` restores `.i18n-state.json`; `sync-language-bars` generates 🌐 bars from config; `validate_translation.py` loads its allowlist again. Retires the duplicate `in` locale (Indonesian mislabelled as Hindi) — 42 honest locales; saved `NEXT_LOCALE=in` / `OMNIROUTE_LANG=in` keep working. (#12496) +- **feat(api):** Emit gateway-measured `tokens_per_second` (TTFT excluded) on streaming usage and `X-OmniRoute-Tokens-Per-Second` when first-token latency is known ([#12616](https://github.com/diegosouzapw/OmniRoute/issues/12616)) ([#12631](https://github.com/diegosouzapw/OmniRoute/pull/12631)) — thanks @RaviTharuma +- **feat(opencode-plugin): map gateway cost/usage/tok/s onto OpenCode inference payloads** — the official plugin copies `X-OmniRoute-Response-Cost`, token counts, `X-OmniRoute-Tokens-Per-Second` / `usage.tokens_per_second`, TTFT, and the winning `X-OmniRoute-Model` onto the JSON/SSE body OpenCode already consumes. Missing tok/s is left unset (never `tokens / latency`). (#12636) — thanks @RaviTharuma +- **feat(combo):** add combo strategy `quota-weighted`: skip exhausted accounts, then weighted-draw by leftover / in-flight load; existing conversations stay pinned until the account is empty ([#12789](https://github.com/diegosouzapw/OmniRoute/pull/12789)) — thanks @HouMinXi +- **feat(models):** Account live listings become the chat catalog source for Claude, Codex, Copilot, and AGY; public metadata only fills prices on IDs those accounts already list. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **fix(models):** Union `agy` and `antigravity` live catalogs so `agy/gemini-3.8-flash-high` is not rejected after the prefix folds to `antigravity`. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **feat(opencode):** Add `@omniroute/opencode-plugin-v2`, an opencode v2 plugin that gives a v2 install a route to an OmniRoute gateway: it publishes models, combos and auto-combos into the host catalog, refreshes them lazily behind a 300s TTL, and keeps serving the last known catalog from an on-disk snapshot when the gateway is unreachable. The v1 plugin (`@omniroute/opencode-plugin`) is untouched and keeps working as before. The plugin adapts to the host instead of assuming one shape: it reads the shape the host seeds into the catalog draft and publishes the `api` block, the pre-`api` top-level fields, or both. The gateway key comes from the host credential store when one is connected, so no secret needs to sit in a config file; a source the gateway refuses is named in a warning instead of silently degrading the catalog to raw model ids; and tool schemas bound for a Gemini model are stripped of the JSON-Schema keywords Gemini rejects, so tool calling survives that route ([#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870)) — thanks @maxmad64bis +- feat(services): show sanitized CLIProxyAPI account health from its authenticated management API without exposing credentials, file paths, or raw account metadata (#6342) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add AnySearch free web search + URL extract (webFetch) with typed results, credential validation, REST routing, and MCP selection - fallback-only ([#11690](https://github.com/diegosouzapw/OmniRoute/pull/11690)) — thanks @Xxx91n +- **feat(dashboard):** display clamped `[0, 100]%` cached input token ratio in request logs table ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) — thanks @b3nw +- **feat(catalog):** add `OMNIROUTE_DISABLE_THINKING_LEVEL_VARIANTS` feature flag to optionally filter out thinking level variants from model catalog ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) — thanks @b3nw +- **feat(dashboard):** continuously export call logs to external analytics stores. A pluggable destination registry ships the full Logs-tab record set on an hourly `JobRegistry` cron, with a persisted per-destination cursor, batched inserts, a config UI rendered from each destination's own field descriptors, and a REST layer (`/api/log-export/*`) for CRUD, a connection test, and an on-demand run. A destination can opt into `includeBodies` to also ship the request and response payloads shown in the Logs detail pane, including the client and provider views of each call; this is off by default, and payloads inherit the dashboard's PII sanitisation, secret redaction and `noLog` handling. Google BigQuery is the first destination, using a service-account key stored encrypted at rest and streaming inserts keyed by call-log id, into a table that is day-partitioned on `timestamp` and clustered on `api_key_name`, `provider`, `model` and `status`. ([#11945](https://github.com/diegosouzapw/OmniRoute/pull/11945)) — thanks @dpozimski +- **feat(providers):** add MaxAI as a signed, OpenAI-compatible provider serving its 13 paid chat models (GPT-5.6 / Luna / Thinking, Claude 5 Sonnet, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite, Grok 4.1-fast / 4.5, DeepSeek V3.2 / R1, Llama 3.3 70B) through OmniRoute's `/v1` endpoint, with per-request HMAC-SHA1→SM3→AES request signing, live model + context-window discovery from `/models/get_config`, and prompted tool-calling translated to OpenAI `tool_calls` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI vision input — image_url content parts are forwarded inline in `message_content` to the 6 vision-capable models (GPT-5.6 / Luna / Thinking, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite) ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI image generation — 6 image models (gpt-image-1, dall-e-3, flux-1-schnell/dev/pro, sd3-medium) exposed through `POST /v1/images/generations` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI document RAG — inline base64 file/document attachments are uploaded to MaxAI (content-addressed `doc_id`) and attached to the chat via `doc_list` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** browserless MaxAI onboarding — email device-pair login (`/api/providers/[id]/login`) and signed access-token refresh, so a connection can be created and kept fresh without a real browser or Google OAuth ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** per-provider TLS impersonation profile (MaxAI presents a Windows Firefox-150 client fingerprint) so its bot-sensitive endpoints accept OmniRoute traffic ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** add **Nimble** as a web-search and web-fetch provider (`nimble-search`) — `/v1/search` routes to Nimble's search API at `lite` depth (locale, freshness and include/exclude domain filters mapped onto the shared request contract), and `/v1/web/fetch` routes to Nimble Extract, which covers all four fetch formats (`markdown`, `html`, `links`, `screenshot`) from a single call. One API key serves both surfaces. ([#11620](https://github.com/diegosouzapw/OmniRoute/pull/11620)) — thanks @wildcard +- The npm publish is automatic again, through npm Trusted Publishing (OIDC): the hosted `stage-npm` job publishes with a short-lived credential minted from GitHub's id-token — no `NPM_TOKEN`, no 2FA prompt, provenance attached. `publish_mode=staged` (owner approves with 2FA) and `direct` (token) remain available on `workflow_dispatch`. ([#11931](https://github.com/diegosouzapw/OmniRoute/pull/11931)) +- **feat(providers):** Add **Opper** as an API-key gateway provider — EU-hosted AI gateway with 700+ models from 30+ providers behind one OpenAI-compatible API and one key (`OPPER_API_KEY`); model ids use `provider/model` format (e.g. `anthropic/claude-sonnet-4-6`, `openai/gpt-5`); live model catalog at `https://api.opper.ai/v3/compat/models`; entry mirrors `requesty` (same shape, `passthroughModels: true`, no static seed) ([#11629](https://github.com/diegosouzapw/OmniRoute/pull/11629)) +- **feat(dashboard):** the `/dashboard/orchestration` snapshot hook now subscribes to the `agents` WebSocket channel (`agent.task.updated`) instead of `requests` as its refetch trigger, and relaxes its background poll from 5s to 30s while that WS connection is up — falling back to the tighter 5s cadence, reprogrammed live on any connect/disconnect transition, whenever the socket is down. ([#12409](https://github.com/diegosouzapw/OmniRoute/pull/12409)) +- **feat(dashboard):** Orchestration canvas quick wins — search box plus state/source/provider filter chips with a one-click clear, and per-source collapse/expand, all reflected in the URL so a filtered/collapsed view is shareable and survives a refresh; the detail drawer gained a "copy trace JSON" action and hardened error/empty-state and accessibility handling; the Agents-tab edges now animate traveling particles along active (running) connections; and the canvas node/edge status colors moved off fixed hex values onto theme-aware `--orch-status-*` CSS custom properties, so they adapt correctly to light/dark mode. ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- **chore(dashboard):** Orchestration UI hardening pass and the missing component/model test coverage it called for — `OrchestratorNode`/`ActivityNode`/`OverflowNode` rendering, the `?node=`/overflow-click page routing, the Agents-tab orchestrator-click no-op and `showCompleted` toggle, and the overview kanban's done-column sort order (#12270, #12271). ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- New `/dashboard/orchestration` page: live unified view of everything running — Cloud Agent, A2A and Conductor as a real-time graph (Agents tab), the combo cascade (Routing tab, reusing the Combo Live Studio) and a state kanban (Overview tab), with a detail drawer (trace, cost, approve/cancel). Read-only over existing APIs — no new backend. Canvas concept credit: PR #11815 design ([#12261](https://github.com/diegosouzapw/OmniRoute/pull/12261)) +- **feat(dashboard):** new "History" tab on `/dashboard/orchestration` — an Airflow-style grid of finished runs over a 24h/7d/30d preset window, one row per (source, identity), clicking a cell opens the existing detail drawer. It is backed by real persistence: A2A task lifecycle transitions are now written to the `a2a_tasks` table (purged after 30 days, configurable via `OMNIROUTE_A2A_HISTORY_RETENTION_DAYS`) and served by the new `GET /api/a2a/tasks/history` listing endpoint, with the task-detail route falling back to persisted history once a run leaves the in-memory snapshot. Conductor runs stay remote and are not persisted locally — the tab says so instead of silently omitting them. ([#12479](https://github.com/diegosouzapw/OmniRoute/pull/12479)) +- **feat(dashboard):** the orchestration detail drawer gained a "Repeat" action for Cloud Agent, A2A and Conductor tasks — a two-click confirm (click once to arm, click again within the confirm window to fire) re-submits the original prompt/input as a new run. The button is disabled with an explanatory tooltip whenever the original input can't be recovered from the loaded task detail (e.g. it never carried a prompt, or the detail failed to load). Two limitations of the A2A variant, by design: it targets the `/a2a` JSON-RPC endpoint, which authenticates with an API key only (`REQUIRE_API_KEY=true` or a configured `OMNIROUTE_API_KEY` makes a dashboard-session repeat answer `HTTP 400` — surfaced verbatim in the drawer's error line, never as a success), and the `message/send` call is SYNCHRONOUS: the POST blocks for the whole skill run, so the success confirmation only appears once the repeated task finishes. A dashboard-authenticated A2A creation path is deliberately left to a follow-up — widening the endpoint's auth posture is an operator decision, not a side effect of this feature. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(a2a):** A2A task execution now records which memories were consulted for the task's last user message as `metadata.memoryHits` (id/key/type/content-snippet) plus a `memory_hits` history event, purely for observability — the retrieved memory is never injected into a skill's prompt or behavior. Gated by the `OMNIROUTE_A2A_MEMORY_HITS` kill-switch (default enabled; set to `0` to skip the recall lookup entirely). The drawer's new "Memory used" section lists these hits for a2a tasks and is omitted whenever there are none. Known limitation: recall only resolves under the keyless posture — a keyed caller's task owner is a SHA-256 prefix of the API key, while memory rows are keyed by the database api-key id, and no hash→id lookup exists today, so the hit list stays empty for keyed callers. The recorded hits are also kept out of the task's own `input` (and therefore out of the persisted input and of the "Repeat" request body), so repeating a task never re-sends the previous run's memory snippets. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(providers):** add a Perplexity Agent API provider (`perplexity-agent` / `pplx-agent`) for Perplexity `/v1/responses`, including the documented Anthropic, OpenAI, Google, xAI, DeepSeek, Z.AI, Moonshot/Kimi, NVIDIA, and Perplexity model IDs plus Anthropic-model `max_output_tokens` compatibility. ([#12103](https://github.com/diegosouzapw/OmniRoute/pull/12103)) — thanks @AIB1TAL0S +- **feat(providers):** add RPD (Requests Per Day) limit to provider rate limit overrides across UI, schemas, DB, and i18n ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) — thanks @b3nw +- **feat(providers): add UC Direct (uncensored.com Developer API), the metered OpenAI-compatible surface.** A standard OpenAI-compatible passthrough (default executor) for uncensored.com's official REST API at `https://api.uncensored.com/api/v1`: `X-api-key` auth (never-expiring `uai_sk_live_` key), `POST /chat/completions` with streaming SSE and native tool-calling, and the full live metered catalog (82 models across 15 providers, discovered from the public `GET /v1/models`). Registered as provider `uc-direct` (alias `ucd`). Complements the un-metered `uc` persona provider — same models, metered credits and a plain API key instead of a subscription session. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(providers): add UC (uncensored.com), the un-metered subscription "persona" chat as an OpenAI-compatible provider.** A WebSocket web-app port: a durable Clerk credential mints a short-lived session token per connect (browserless — no API key), driving UC's persona socket. Ships the browserless email-code login (request → verify → harvest), the 19 verified persona models (Claude Opus, Gemini, Grok, GLM, Kimi, DeepSeek, MiniMax, incl. the uncensored variants), prompted `` tool-calling with a per-model code-style dialect + auto-cure retry for guardrailed models, live ``/reasoning split, streaming + non-streaming OpenAI responses, and full quota/auth error surfacing (paywall / message-limit / rate-limit → 429, invalid session → 401 re-login). Full multimodal parity via the persona blob-upload layer: **vision** (image input, 15 vision-capable models), **document RAG** (PDF/doc upload, server-side extraction), **image generation** (22 models), **video generation** (14 models, async signed-url → poll), and **TTS** (streaming MP3). Registered as provider `uc` (alias `ucn`). The metered OpenAI-compatible Developer API is a separate `uc-direct` provider. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(logs):** add copy-all button to request detail modal ([#11083](https://github.com/diegosouzapw/OmniRoute/pull/11083)) — thanks @AndrianBalanescu +- **feat(check):** make serviceKinds required and add the reverse-walk provider consistency gate ([#11392](https://github.com/diegosouzapw/OmniRoute/pull/11392)) — thanks @Tushar49 +- **feat(providers):** only advertise verified-working connections by default ([#11448](https://github.com/diegosouzapw/OmniRoute/pull/11448)) — thanks @alltomatos +- **feat(resilience):** add hierarchical concurrency admission ([#11493](https://github.com/diegosouzapw/OmniRoute/pull/11493)) +- **feat(providers):** derive + surface expiry for JWT-bearing web cookies (#11497) ([#11505](https://github.com/diegosouzapw/OmniRoute/pull/11505)) — thanks @oyi77 +- **feat(tunnels):** support named cloudflared tunnels via CLOUDFLARED_CONFIG ([#11561](https://github.com/diegosouzapw/OmniRoute/pull/11561)) — thanks @ggiak +- **feat(compression):** make proactive context-compression threshold a live setting ([#11564](https://github.com/diegosouzapw/OmniRoute/pull/11564)) — thanks @ragnar-claude +- **feat(resilience):** add provider peak-hour protection ([#11622](https://github.com/diegosouzapw/OmniRoute/pull/11622)) — thanks @Neuron-Mr-White +- **feat(providers):** add 1min.ai provider ([#11631](https://github.com/diegosouzapw/OmniRoute/pull/11631)) — thanks @zero-executioner +- **feat(dashboard):** collapsible JSON tree viewer for request/response payloads ([#11703](https://github.com/diegosouzapw/OmniRoute/pull/11703)) — thanks @hartmark +- **feat(routing):** add LiquidAI LFM2.5-2.6B free tier via OpenRouter ([#11752](https://github.com/diegosouzapw/OmniRoute/pull/11752)) — thanks @brick30llc-ctrl +- **feat(pricing):** add GLM-5.3-Flash pricing, model specs, and catalog registration ([#11830](https://github.com/diegosouzapw/OmniRoute/pull/11830)) — thanks @AndrianBalanescu +- **feat(routing):** add relayMode for schema-locked context handoffs ([#11839](https://github.com/diegosouzapw/OmniRoute/pull/11839)) — thanks @raheemuddin786 +- **feat(config):** add support for runtime system prompt configuration and hot-reloading ([#11841](https://github.com/diegosouzapw/OmniRoute/pull/11841)) — thanks @raheemuddin786 +- **feat(routing):** expand connection-aware quota prefilter across combo strategies (#11682) ([#11850](https://github.com/diegosouzapw/OmniRoute/pull/11850)) — thanks @HouMinXi +- **feat(models):** reactive discovery sync after Antigravity model-not-found 404 ([#11888](https://github.com/diegosouzapw/OmniRoute/pull/11888)) — thanks @MumuTW +- **feat(models):** auto-aliases and token limits from synced Antigravity discovery ([#11889](https://github.com/diegosouzapw/OmniRoute/pull/11889)) — thanks @MumuTW +- **feat(settings):** raise sticky round-robin limit caps to 1000 ([#12015](https://github.com/diegosouzapw/OmniRoute/pull/12015)) — thanks @adivekar-utexas +- **feat(resilience):** operator-configurable global credential health check interval ([#12043](https://github.com/diegosouzapw/OmniRoute/pull/12043)) — thanks @adivekar-utexas +- **feat(quota):** use official OpenCode Go usage API ([#12124](https://github.com/diegosouzapw/OmniRoute/pull/12124)) — thanks @ddarkr +- **feat(resilience):** default the credential health check sweep to 60 minutes ([#12138](https://github.com/diegosouzapw/OmniRoute/pull/12138)) — thanks @adivekar-utexas +- **feat(quality):** new-code mode for the complexity and dead-code ratchets (Clean as You Code) ([#12142](https://github.com/diegosouzapw/OmniRoute/pull/12142)) +- **feat(dashboard):** orchestration canvas — unified model + snapshot hook (part 1/2) ([#12156](https://github.com/diegosouzapw/OmniRoute/pull/12156)) +- **feat(usage):** add Kilo Code balance and Kilo Pass quotas ([#12178](https://github.com/diegosouzapw/OmniRoute/pull/12178)) — thanks @killer30001000 +- **feat(providers):** refresh vendored ChatGPT Web connector to v4.0.7 ([#12181](https://github.com/diegosouzapw/OmniRoute/pull/12181)) — thanks @backryun +- **feat(providers):** manual "Clear cooldown" action in the cooling panel ([#12224](https://github.com/diegosouzapw/OmniRoute/pull/12224)) — thanks @patrykkopycinski +- **feat(providers):** restore ChatGPT Web via clean-room browser transport ([#12239](https://github.com/diegosouzapw/OmniRoute/pull/12239)) — thanks @backryun +- **feat(sse):** wire the PROVIDER_PROFILES window gate into the global provider cooldown ([#12247](https://github.com/diegosouzapw/OmniRoute/pull/12247)) +- **feat(providers):** modernize CLOVA Studio chat and embeddings ([#12277](https://github.com/diegosouzapw/OmniRoute/pull/12277)) — thanks @backryun +- **feat(browser):** adopt Obscura as primary headless browser engine with Chromium fallback ([#12286](https://github.com/diegosouzapw/OmniRoute/pull/12286)) — thanks @opensource-elearning +- **feat(sse):** full language parity for output styles (es/de/fr/it/ru/zh + autoDetect) ([#12289](https://github.com/diegosouzapw/OmniRoute/pull/12289)) +- **feat(quality):** complete test:scoped — --full map rebuild, stdin selection, CI loader parity (#8084 D1) ([#12353](https://github.com/diegosouzapw/OmniRoute/pull/12353)) +- **feat(providers):** refresh Fable, Cursor, and Devin catalogs ([#12367](https://github.com/diegosouzapw/OmniRoute/pull/12367)) — thanks @backryun +- **feat(video):** redact transcript text from logs and durable memory (#12150 P1) ([#12427](https://github.com/diegosouzapw/OmniRoute/pull/12427)) +- **feat(dashboard):** parent-link, genuine-continuation badge, and modal perf fixes ([#12448](https://github.com/diegosouzapw/OmniRoute/pull/12448)) — thanks @hartmark +- **feat(dashboard):** adaptive context-budget dial on compression panel ([#12488](https://github.com/diegosouzapw/OmniRoute/pull/12488)) — thanks @HouMinXi +- **feat(providers):** refresh Z.ai Web models and browser transport ([#12524](https://github.com/diegosouzapw/OmniRoute/pull/12524)) — thanks @backryun +- **feat(video):** redact raw client-snapshot transcript fields in the detailed log (#12150 P2) ([#12528](https://github.com/diegosouzapw/OmniRoute/pull/12528)) +- **feat(providers):** refresh NVIDIA hosted models ([#12538](https://github.com/diegosouzapw/OmniRoute/pull/12538)) — thanks @backryun +- **feat(quota):** Moonshot Open Platform balance and TPD lock for custom nodes ([#12590](https://github.com/diegosouzapw/OmniRoute/pull/12590)) — thanks @HouMinXi +- **feat(video):** redact transcript in the in-memory pending-request snapshot (#12430 item 6) ([#12596](https://github.com/diegosouzapw/OmniRoute/pull/12596)) +- **feat(dashboard):** link a log entry's Conversation Context to its owning conversation ([#12646](https://github.com/diegosouzapw/OmniRoute/pull/12646)) — thanks @hartmark +- **feat(catalog):** eligibility-gated free-tier bucket ([#12669](https://github.com/diegosouzapw/OmniRoute/pull/12669)) +- **feat(db):** fail-closed previous_response_id continuation for redacted video turns (#12150 P2b) ([#12707](https://github.com/diegosouzapw/OmniRoute/pull/12707)) +- **feat(gamification): pay the documented `streak_bonus` and `badge_unlock` XP rewards.** `XP_REWARDS` listed both rewards but the award pipeline never paid them: the private reward table in `events.ts` omitted them, `updateStreak()` did not report when a streak extended, and badge unlocks carried no XP. Every request that extends a daily streak now pays `streak_bonus × streak length` once per UTC day (guarded by a same-day `xp_audit_log` check), and every badge unlocked through the pipeline pays `badge_unlock` once per badge (guarded by the `user_badges` primary key; `unlockBadge()` now reports whether it inserted). Bonus XP flows through the same `addXp` + level sync + global/weekly/monthly leaderboard path as action XP, so level-ups and rankings include it. The Radar supporter recognition unlock stays XP-free. (#12522 — thanks @pacocartones) +- **feat(providers):** advertise a `free-tier` capability in the provider plugin manifest for every provider with documented free models, so sidecars and dashboards can filter free-capable providers without reading the quota catalog ([#12786](https://github.com/diegosouzapw/OmniRoute/pull/12786)) — thanks @maxmad64bis +- **feat(providers):** Added EURouter as an OpenAI-compatible API-key gateway (`https://api.eurouter.ai/v1`), with live model discovery via `passthroughModels`. Its copy states that models are served by third-party upstreams listed per model, so an EU-based router is not read as EU data residency for inference. ([#13025](https://github.com/diegosouzapw/OmniRoute/pull/13025)) — thanks @ntdatt812 +- **feat(providers):** Added GreenPT as an OpenAI-compatible API-key provider (`https://api.greenpt.ai/v1`), with live model discovery via `passthroughModels`. No free-inference badge: the published docs describe a free API subscription billed per token, not a free tier. ([#13024](https://github.com/diegosouzapw/OmniRoute/pull/13024)) — thanks @ntdatt812 +- **feat(i18n):** 9 new locales — Greek (`el`), Croatian (`hr`), Serbian (`sr`), Lithuanian (`lt`), Estonian (`et`), Latvian (`lv`), Slovenian (`sl`), Maltese (`mt`), Irish (`ga`) — across the dashboard, docs mirrors, CLI, README and the site; OmniRoute now ships every official EU language (51 locales). (#13044) +- **feat(i18n):** 8 new locales — Kannada (`kn`), Malayalam (`ml`), Odia (`or`), Punjabi (`pa`), Nepali (`ne`), Sinhala (`si`), Burmese (`my`), Khmer (`km`) — across the dashboard, docs mirrors, CLI, README and the site (59 locales). The translator now restores the ICU literal escape around angle placeholders and splits oversized docs sections before translating. (#0000) ([#13660](https://github.com/diegosouzapw/OmniRoute/pull/13660)) +- feat(providers): **list Agnes 3.0 Flash as the current free chat model, drop retired 1.5 Flash, add Image 2.0/2.5 Flash plus Video 2.5/2.5 Flash, and discover the live `/v1/models` catalog (including the CN host `api.agnes-ai.cn`).** `agnes-1.5-flash` now forwards to `agnes-3.0-flash`. Video 2.5 polls `GET /v1/videos/{id}` (not the V2.0 `/agnesapi` contract). Live `/v1/models` (2026-09-09) no longer serves 1.5; the wiki marks it deprecated. 3.0 Flash is 512K context / 65,536 max output, same window as 2.5. CN-region keys use the existing per-connection base-URL field, default stays `apihub.agnes-ai.com`. ([#13120](https://github.com/diegosouzapw/OmniRoute/pull/13120)) — thanks @HouMinXi +- **feat(sse):** Codex and OpenAI catalogs list GPT-6 Astra with effort aliases (`-low` through `-ultra`); Codex CLI identity pins `@openai/codex@0.153.4` in lockstep with the image ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **feat(dashboard):** the orchestration History tab gained a "Compare runs" mode — toggling it turns each grid cell into a 2-item selection queue (a 3rd click drops the oldest pick), and picking two cells opens a side-by-side comparison panel instead of the usual detail drawer. The panel fetches both runs' detail the same way the drawer does (falling back to persisted history once a run leaves the live TTL window) and shows, per side: identity/source/state, start time, a signed `right - left` delta for duration/cost/event count, the event timeline aligned by index, and any memory hits. One side's fetch failing never blocks the other, and a delta is only ever computed when both sides have a finite value — otherwise it renders "—", never `NaN`. Comparing two runs from different sources or skills still works; a banner marks the deltas as informational rather than hiding them, since the two runs aren't a strict apples-to-apples pair. ([#12677](https://github.com/diegosouzapw/OmniRoute/pull/12677)) +- **feat(dashboard):** badge a conversation that never reached a clean stop ([#12717](https://github.com/diegosouzapw/OmniRoute/pull/12717)) — thanks @hartmark +- **feat(release):** reconcile-changelog tool + version-anchored fragment aggregation ([#12987](https://github.com/diegosouzapw/OmniRoute/pull/12987)) +- **feat(mcp):** carry context_length through MCP list_models_catalog projection (#12776) ([#13406](https://github.com/diegosouzapw/OmniRoute/pull/13406)) — thanks @KooshaPari +- **feat(cli):** make startup readiness budget configurable (#13369) ([#13433](https://github.com/diegosouzapw/OmniRoute/pull/13433)) — thanks @KooshaPari + ### 🐛 Bug Fixes +- **security(streaming):** sanitize generic mid-stream error messages before emitting OpenAI, + Responses, or Claude SSE failure frames and before diagnostic logging, while preserving raw + failures for internal classification and keeping client disconnects out of provider failure state. ([#12457](https://github.com/diegosouzapw/OmniRoute/pull/12457)) +- **fix(claude):** `blockExtraUsage: false` no longer lets 5h quota preflight skip the account; extra usage is billed after the session bar is gone, so the request must reach Anthropic ([#12803](https://github.com/diegosouzapw/OmniRoute/pull/12803)) — thanks @HouMinXi +- Sanitize HuggingChat conversation-creation and message-send transport failures before they reach client error bodies or provider logs. ([#12467](https://github.com/diegosouzapw/OmniRoute/pull/12467)) +- **fix(dashboard):** Keep local and theme-aware provider SVG icons at a definite layout size so Chromium does not collapse them to 0×0 after the v3.8.50 image-rendering change ([#12054](https://github.com/diegosouzapw/OmniRoute/pull/12054)) — thanks @ponkcore +- **fix(security):** Sanitize provider and runtime failures before public API, SSE and MCP responses and before persistent request, proxy and usage logs, preventing credentials, stack traces and host filesystem paths from crossing those boundaries while preserving stable error codes and useful diagnostics. ([#12506](https://github.com/diegosouzapw/OmniRoute/pull/12506)) +- **fix(cli):** `omniroute tunnel create` no longer crashes with `Cannot read properties of undefined (reading optsWithGlobals)` — removed the duplicate positional argument that caused Commander.js to misalign the action callback parameters ([#12295](https://github.com/diegosouzapw/OmniRoute/issues/12295)) ([#12368](https://github.com/diegosouzapw/OmniRoute/pull/12368)) — thanks @gonisulaimann +- Stop advertising Gemini Live-only models as supported audio endpoints until OmniRoute proxies the bidirectional Live protocol. ([#11710](https://github.com/diegosouzapw/OmniRoute/pull/11710)) — thanks @RaviTharuma +- **fix(kie):** reroute `flux/kontext` off the KIE Market `createTask` flow — it is catalogued with `isMarket: true` but has no Market catalog page, so KIE rejected it with "model name not supported"; it now hits the dedicated `POST /api/v1/flux/kontext/generate` / `GET /api/v1/flux/kontext/record-info` endpoints instead (#11296). ([#11985](https://github.com/diegosouzapw/OmniRoute/pull/11985)) — thanks @hartmark +- **fix(kie):** correct 12 more KIE Market catalog ids that were sent to `createTask` unchanged but diverge from KIE's documented upstream `model` values — GPT Image 2 T2I/I2I (drops the `gpt/` prefix), GPT Image 1.5 T2I/I2I (`gpt-image/` namespace), Seedream 5.0 Lite T2I/I2I (drops the `.0`), all 4 Flux 2 variants (`flux-2/` namespace, generic variant renamed `flex`), and Wan 2.7 Image / Image Pro (dash instead of dot) — each verified individually against the literal example request published on docs.kie.ai. `#11326`'s "everything else already matches" claim was wrong a second time (#11296); `z-image/4.0-*`/`z-image/4.5-*` and `flux/kontext` remain open, documented as unresolved in `KIE_MARKET_UPSTREAM_MODEL_IDS`'s comment pending further verification. ([#11667](https://github.com/diegosouzapw/OmniRoute/pull/11667)) +- **fix(dashboard):** `useApiKeySave.handleSaveApiKey` no longer forces a full upstream `/models` catalog sync on every non-curated provider connection save — callers can now pass `skipModelSync: true` to opt out, so a workflow that only wants to add one manual model no longer floods the provider's available-models list with hundreds/thousands of synced entries. The flag is a client-side intent signal only and is stripped before the connection payload is POSTed to `/api/providers`; default behavior (full sync on save) is unchanged when the flag is omitted (#11324) ([#11664](https://github.com/diegosouzapw/OmniRoute/pull/11664)) +- **Provider connections:** keep `tokenExpiresAt` when a connection is created. The create-path allowlist omitted it, so every insert stored NULL and the dashboard token badge could read a fresh connection as expired until its first background refresh ([#11368](https://github.com/diegosouzapw/OmniRoute/pull/11368)). — thanks @ntdatt812 +- **fix(combos):** the combo builder's precision-select, global-model-search, and manual-entry flows now serialize a model step's `model` string using the provider's already-computed routing-alias prefix (e.g. `oc/`) instead of rebuilding it from the raw canonical `providerId`, fixing the no-auth "OpenCode Free" provider (`opencode`) being routed to the unrelated paid "OpenCode Zen" provider (`opencode-zen`) because `opencode` doubles as a manual routing-prefix override ([#11433](https://github.com/diegosouzapw/OmniRoute/issues/11433)). ([#11670](https://github.com/diegosouzapw/OmniRoute/pull/11670)) +- **fix(radar):** the catalog feed cache now keeps `generatedAt`, the date the feed's data was built, next to `fetchedAt`, the date this install downloaded it (#11435). The feed schema requires that date and the sync path validates it, but the cache dropped it — so a feed fetched minutes ago and one carrying weeks-old figures looked identical to everything downstream, including the dashboard's "Last fetched" line. `getRadarCatalog().meta` and `GET /api/radar/status` now report both dates, the latter as its own field rather than folded into `version` — and omitted entirely for the offers and intel caches, which keep no build date, where a `null` would read as "unknown" rather than "never stored". The dashboard still shows only the fetch time; surfacing the build date there needs a new translated label and is left to a follow-up. Rows cached before migration 163 read back as `null`: unknown stays unknown instead of borrowing the fetch time. The referrals cache has persisted the same date since migration 142. — thanks @maxmad64bis +- **fix(cli):** postinstall no longer fills `JWT_SECRET` and `API_KEY_SECRET` in the installed package's `.env` (#11436). `.env.example` ships both blank on purpose: the server restores them from its durable store, or generates and persists them there, in `ensureSecrets()`. Pre-filling them defeated that — the file lives inside the package directory, so `npm i -g` replaced it and postinstall wrote _different_ values, while `ensureSecrets()` (which only acts on an empty variable) never got to restore the real ones. Both secrets rotated silently on every update, invalidating dashboard sessions and API-key CRCs. `STORAGE_ENCRYPTION_KEY` left the same list for the same reason in #1622; its comment pointed at a function that no longer exists and now names the real provisioning path. — thanks @maxmad64bis +- **fix(cli):** the CLI now says when a loaded `.env` lives inside the installed package directory (#11437). It already announces every env file it reads, without distinguishing the ones that survive an update from the one that does not: `npm i -g` replaces the package directory wholesale, so values set there are gone at the next update, silently. The warning names the durable path to move them to, and fires only when that file actually supplied a value — a file entirely shadowed by a durable one supplied nothing. A development checkout stays silent: there the same path is stable and documented in `SETUP_GUIDE.md`. — thanks @maxmad64bis +- **fix(free-models):** the shared `isFreeModel()` predicate no longer reports catalog entries marked `freeType: "discontinued"` as free, so `hidePaidModels` can't route to Pollinations' seven premium models that now require a paid key ([#11441](https://github.com/diegosouzapw/OmniRoute/pull/11441)) — thanks @maxmad64bis +- fix(ui): let AnySearch use the normal provider-icon fallback when LobeHub has no matching icon (#11449) +- Fixed the v3.8.50 Costs and Analytics dashboards so flat-rate Claude Code usage can be shown as an explicitly requested token-price estimate without changing default billed-cost semantics. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed archived usage retention so each request is priced individually instead of pricing a day's summed tokens once, which understated archived cost whenever a day mixed cache-heavy and ordinary requests. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed the Costs dashboard so it discloses when displayed figures include flat-rate token-price estimates instead of labelling them as billed spend, using the flag the analytics API already returns; the month-end projection and the CSV/JSON exports carry the same marker, and billed-cost mode is unchanged. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- **fix(combo):** attach the same combo-diagnostics trace (`poolSize`/`attemptOrder`/`excluded`/`terminalReason`, plus `x-omniroute-combo-*` headers) to the round-robin strategy's and the nested pipeline/fusion runtime-unit loop's "Maximum combo retry limit reached" 503 that the priority-strategy path already attaches for the identical terminal condition — previously those two paths returned a bare, contextless 503 ([#11462](https://github.com/diegosouzapw/OmniRoute/issues/11462)). ([#11665](https://github.com/diegosouzapw/OmniRoute/pull/11665)) +- **fix(bun):** use native `bun:sqlite` in `bootstrap-env` and `sync-env` to avoid loading `better-sqlite3` N-API addon during Bun startup ([#11468](https://github.com/diegosouzapw/OmniRoute/pull/11468)) — thanks @TheDemonTuan +- **fix(docker-bun):** make `Dockerfile.bun` install reliable with pre-install script helpers and native `bun:sqlite` smoke check ([#11470](https://github.com/diegosouzapw/OmniRoute/pull/11470)) — thanks @TheDemonTuan +- **fix(docker-bun):** remove every vendored `better-sqlite3` native addon from the Bun runtime image so startup cannot abort before the `bun:sqlite` fallback ([#11482](https://github.com/diegosouzapw/OmniRoute/pull/11482)) — thanks @TheDemonTuan +- **fix(autoCombo):** effort/alias model variants (`gpt-5.6-sol-xhigh`, `gpt-5.6`, cursor's `claude-4.6-opus-high`) inherit their base model's task fitness instead of falling to the wildcard 0.5 ([#11492](https://github.com/diegosouzapw/OmniRoute/pull/11492)) — thanks @MumuTW +- fix(db): rate-limit repeated Arena ELO leaderboard fetch-failure warnings instead of logging one per sync attempt (#11500) ([#11989](https://github.com/diegosouzapw/OmniRoute/pull/11989)) — thanks @hartmark +- **fix(db):** dedupe the raw `[Encryption] Decryption failed...` log line emitted by the lazy-decrypt views (`createLazyRowProxy`/`createLazyConnectionView`), which power `getProviderConnections()` and were re-triggering that line on every CredentialHealth/model-sync cycle for the same corrupt or stale-key credential — a fresh Proxy over a fresh row on every cycle meant the per-proxy memoization never suppressed it, unlike the dedup `decryptConnectionFields()` already had since [#9927](https://github.com/diegosouzapw/OmniRoute/issues/9927). Now shares that dedupe tracking so the line logs at most once per credential ([#11500](https://github.com/diegosouzapw/OmniRoute/issues/11500)). ([#11678](https://github.com/diegosouzapw/OmniRoute/pull/11678)) +- **fix(intelligence):** Arena ELO sync stores a synthesized base-model row for every effort/harness variant (`claude-opus-5-max` → `claude-opus-5`, `gpt-5.6-sol-xhigh (codex-harness)` → `gpt-5.6-sol`), so bare model requests reach the synced intelligence layer instead of the static fallback table; the hand-maintained `MODEL_ALIAS_MAP` — which collapsed generations (`gpt-5.5` → `gpt-5`) — is gone ([#11506](https://github.com/diegosouzapw/OmniRoute/pull/11506)) — thanks @MumuTW +- **fix(autoCombo,sse):** catalog hygiene — the static task-fitness table no longer ranks retired models above live ones (dead rows dropped, patterns matched on segment boundaries), the 7 deprecation aliases that forwarded to retired or non-existent models now point at the vendors' published replacements, an alias is no longer rewritten when the serving provider offers the id as-is, and a new `check:model-lifecycle` gate diffs both tables against a vendor lifecycle snapshot ([#11507](https://github.com/diegosouzapw/OmniRoute/pull/11507)) — thanks @MumuTW +- **fix(sse):** stop dropping resolved `thoughtSignature` values on parallel (multi tool-call) turns sent to Gemini 3.x — the claude→gemini and openai→gemini translators previously kept the signature only on the *first* function call of a message, causing Gemini to reject subsequent calls in the same turn with HTTP 400 "Function call is missing a thought_signature"; each function call now keeps its own resolved signature ([#11510](https://github.com/diegosouzapw/OmniRoute/issues/11510)). ([#11669](https://github.com/diegosouzapw/OmniRoute/pull/11669)) +- Treat Docker publishing from a non-default release branch as an expected skip instead of a failed workflow, while preserving the guard that prevents it from overwriting the `next` channel. ([#11527](https://github.com/diegosouzapw/OmniRoute/pull/11527)) — thanks @pacocartones +- fix(search): prefer credentialed providers over duckduckgo-free fallback (#11524) — the fallback-only loop ran before the credentialed-providers loop in `executeWebSearch`, making configured providers unreachable when `duckduckgo-free` was available. ([#11565](https://github.com/diegosouzapw/OmniRoute/pull/11565)) — thanks @vsd2807 +- **fix(sse):** cap the upstream headers-wait phase for STREAMING requests to a client-realistic ceiling (110s, under Codex's own ~120s hard client-abort window) instead of the flat 10-minute `FETCH_TIMEOUT_MS` default — that default was 5x longer than the body-phase readiness watchdog's own adaptive bound, so a request whose upstream never returned any response at all (not even headers, e.g. a stalled NVIDIA target behind a tool-heavy Responses→Chat translation) kept the client connection alive on keepalives only, guaranteeing the client's own patience ran out first with an opaque 499 instead of OmniRoute detecting and failing the stall fast. Non-streaming requests are unaffected — they keep the existing flat default (`open-sse/utils/fetchStartTimeoutPolicy.ts`) (#11526) ([#11668](https://github.com/diegosouzapw/OmniRoute/pull/11668)) +- **fix(ci):** protocol-clients E2E harness boots the peer-stamped custom server (`run-next.mjs dev`) instead of the bare `next` CLI, so `LOCAL_ONLY` locality resolves from the real TCP peer stamp and `/api/mcp/audit` answers 200 (open bootstrap preserved via an `OMNIROUTE_E2E_BOOTSTRAP_MODE=open` credential clear in `run-next.mjs`, pinned to loopback `HOST`) instead of a deterministic 403 (#11535) ([#11549](https://github.com/diegosouzapw/OmniRoute/pull/11549)) — thanks @jonlwheat2-gif +- **fix(docs):** the Auto-Combo engine is described with the number of factors it actually declares. Four different counts were in circulation — 6, 9, 13 and 14 across `AGENTS.md`, four guides and six code comments — while `DEFAULT_WEIGHTS` declares 15. `check:docs-counts` now reads that number from the source, so adding a factor without updating the prose fails CI ([#11545](https://github.com/diegosouzapw/OmniRoute/pull/11545)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **fix(quota-share):** in-flight leases are per request, so an aborted request's slot ages out instead of being kept alive by later traffic on the same connection ([#11547](https://github.com/diegosouzapw/OmniRoute/pull/11547)) — thanks @abhisheksharma2411 +- The free-tier summary route now serves the refreshed Radar catalog when the feed is active — the same numbers the dashboard shows — and states which catalog answered plus its real build date, instead of always reporting release-frozen figures with a stale curation date. The supporter-key live feed stays reserved to authenticated callers of the instance. ([#11550](https://github.com/diegosouzapw/OmniRoute/pull/11550)) — thanks @maxmad64bis +- **sse:** fix LiveWS/embed-WS servers crashing at startup under the Node/tsx runtime — `liveServer.ts`, `embedWsProxy.ts` and `apiBridgeServer.ts` imported `@/shared/utils/httpClientAbortGuard` without the `.mjs` extension, so the client-abort crash guard added by [#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556) was unreachable and every dependent test failed with `ERR_MODULE_NOT_FOUND` ([#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556)). ([#11671](https://github.com/diegosouzapw/OmniRoute/pull/11671)) — thanks @jonlwheat2-gif / @yxyxy +- fix(chat): shed any of the matched provider-node's routing identifiers (public prefix or internal id) before model lookup, so composites like `//` no longer reach upstream verbatim (#11557) — thanks @maxmad64bis +- **fix(cache):** a semantic-cache entry now expires on its TTL instead of surviving until the next UTC midnight, and `dbEntries` stops counting expired rows ([#11573](https://github.com/diegosouzapw/OmniRoute/pull/11573)) — thanks @ntdatt812 +- **fix(api):** `/v1/models` schedules its stale-while-revalidate rebuild through Next's `after()` again, so a stale catalog reaches the client before the rebuild blocks the event loop ([#11574](https://github.com/diegosouzapw/OmniRoute/pull/11574)) — thanks @ntdatt812 +- **fix(check):** the `known-symbols` executor conformance gate awaits the now-async `getExecutor()`, so it stops reporting all 142 registered aliases as dead symbols and can detect a lazy import that fails ([#11582](https://github.com/diegosouzapw/OmniRoute/pull/11582)) — thanks @ntdatt812 +- **fix(pack):** register `bin/cli/utils/volatileEnvPath.mjs` in `PACK_ARTIFACT_REQUIRED_PATHS`, so the prune can no longer silently drop a module the published CLI imports on every boot ([#11588](https://github.com/diegosouzapw/OmniRoute/pull/11588)) — thanks @ntdatt812 +- **i18n(vi):** translate the five reliability-column strings on the free-provider rankings page, which rendered as `__MISSING__:` markers in the Vietnamese UI ([#11589](https://github.com/diegosouzapw/OmniRoute/pull/11589)) — thanks @ntdatt812 +- **resilience:** restore the expired-connection retry-budget probe in the token-health sweep — the `!isGitHubAccessTokenOnlyConnection` carve-out reintroduced by #11608 contradicted the boundary pinned by #11592, so a GitHub connection parked at `expired` with retry budget remaining was never probed and could never self-heal ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)). ([#11672](https://github.com/diegosouzapw/OmniRoute/pull/11672)) — thanks @jonlwheat2-gif / @ntdatt812 +- **fix(dashboard):** Provider Stats now displays combo success rates on their native 0–100 percentage scale instead of multiplying them twice ([#11595](https://github.com/diegosouzapw/OmniRoute/pull/11595)) — thanks @pacocartones +- **fix(providers):** route DuckDuckGo Web Chat through the verified `duck.ai` endpoint, refresh its free model catalog, and avoid forwarding unsolved anti-abuse challenges ([#11597](https://github.com/diegosouzapw/OmniRoute/pull/11597)) — thanks @solstxce +- **fix(ui):** Console log Refresh and Copy controls now expose localized accessible names, keep copy actions visible on keyboard focus, and announce copy completion safely ([#11599](https://github.com/diegosouzapw/OmniRoute/pull/11599)) — thanks @pacocartones +- Dropped the stale-`.eslintcache` `restore-keys` fallback from both "Restore ESLint file cache" steps in `ci.yml`, so the blocking `Lint` job can no longer be served per-file verdicts computed under a different lint config, suppressions file or lockfile. `quality.yml` had already dropped it in #11963; `ci.yml` — the workflow that actually gates PRs — had not (#11600). ([#11996](https://github.com/diegosouzapw/OmniRoute/pull/11996)) +- fix(ci): pass `--pass-on-unpruned-suppressions` in `run-eslint-json.mjs` so the CI Lint job no longer fails when a suppression is merely orphaned by a genuine fix, mirroring the identical fix already in `validate-release-green.mjs` (#11600) ([#11983](https://github.com/diegosouzapw/OmniRoute/pull/11983)) — thanks @hartmark +- fix(handoff): enforce provider allowlist for universal handoff (#11602) — universal handoff now skips summarization when the selected summary model's provider is not included in the configured provider allowlist. ([#11619](https://github.com/diegosouzapw/OmniRoute/pull/11619)) — thanks @vsd2807 +- **fix(dashboard):** Show a stable error state when Search Analytics returns an HTTP error or malformed data ([#11603](https://github.com/diegosouzapw/OmniRoute/pull/11603)) — thanks @pacocartones +- **test(gamification):** pin the aggregate profile level to the XP-derived semantics of #11604 — `getAggregateXp()` now derives `currentLevel` from the summed XP (`calculateLevel(sum)`), not `MAX(stored current_level)`, and the #3484 fixture levels are aligned with the XP curve ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)). ([#11676](https://github.com/diegosouzapw/OmniRoute/pull/11676)) — thanks @jonlwheat2-gif / @pacocartones +- **fix(dashboard):** Keep the Profile level and progress aligned with aggregate XP, including bounded handling for invalid totals ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)) — thanks @pacocartones +- **fix(dashboard):** Prevent locked hidden badges from revealing their icon or opening private badge details before they are earned ([#11605](https://github.com/diegosouzapw/OmniRoute/pull/11605)) — thanks @pacocartones +- **fix(dashboard):** Restore keyboard focus after shared modals close and cancel delayed autofocus during cleanup ([#11607](https://github.com/diegosouzapw/OmniRoute/pull/11607)) — thanks @pacocartones +- **fix(dashboard):** Enable Enter and Space activation for clickable data-table rows without hijacking nested controls ([#11610](https://github.com/diegosouzapw/OmniRoute/pull/11610)) — thanks @pacocartones +- **fix(autoCombo,sse):** vendor-retired catalog ids are dropped from the auto-combo candidate pool and no longer win on leftover `arena_elo` / `user_override` rows; `getModelLifecycleDecision` consults `model-lifecycle.json` (prefix-stripped) so aggregator traffic is not `untracked` for ids the snapshot already knows ([#11625](https://github.com/diegosouzapw/OmniRoute/issues/11625)) ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)) — thanks @MumuTW +- **test(chatcore):** move Codex/Claude combo fixtures off models the lifecycle guard now rejects — `gpt-5.1-codex`/`gpt-5-codex` are vendor-retired (snapshot, #11626) and `claude-3-5-sonnet-20241022` is shut down, so native-passthrough and combo-fallback tests switched to `gpt-5.6-sol` and `claude-sonnet-4.6` ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)). ([#11675](https://github.com/diegosouzapw/OmniRoute/pull/11675)) — thanks @jonlwheat2-gif / @MumuTW +- fix(opencode-plugin): stop a free-tier budget that rounds up across a magnitude from rendering as `1000K`/`1000M` in the model picker — `fmtTokens` chose its unit from the raw token count and then rounded with `toFixed(1)`, so 999,950–999,999 printed as `1000K` rather than `1M` and just under a billion printed as `1000M` rather than `1B` ([#11684](https://github.com/diegosouzapw/OmniRoute/pull/11684)) — thanks @abhisheksharma2411 +- Cap prose-derived Antigravity quota resets at the configured model cooldown maximum while preserving authoritative Retry-After headers and Google RetryInfo hints. (#11695) Thanks @pacocartones. ([#11823](https://github.com/diegosouzapw/OmniRoute/pull/11823)) +- Compare `_rebaseline_*` notes structurally in the ratchet-bank verifier so an object-valued note no longer reports itself as rewritten on an unmodified tree — the nightly `bank-ratchet-shrinks` lane had been aborting on every run since 2026-08-11, leaving 41,672 lines of earned file-size shrinks unbanked (#11702). ([#11892](https://github.com/diegosouzapw/OmniRoute/pull/11892)) — thanks @MumuTW +- **fix(build):** `prepublish.ts` bundles the ChatGPT Web (Codex) MCP bridge through `runBuildTool()` instead of spawning `npx.cmd` raw, fixing the build crash on Node ≥ 20/Windows where `.cmd` shims cannot be spawned without a shell (EINVAL) ([#11704](https://github.com/diegosouzapw/OmniRoute/issues/11704)) ([#11706](https://github.com/diegosouzapw/OmniRoute/pull/11706)) — thanks @jonlwheat2-gif +- fix(codex): keep `parallel_tool_calls:false` on the translated Codex Responses Lite path (#11707) ([#11984](https://github.com/diegosouzapw/OmniRoute/pull/11984)) — thanks @hartmark +- **fix(tests):** the new Gemini Web Images retirement test (#11708) called the now-async `getExecutor()` synchronously, throwing `TypeError: getExecutor(...).getProvider is not a function` instead of asserting Gemini Web chat stays available. Awaited to match the R0.3 executor-registry refactor (#11220). ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(search):** Allow REST and MCP web-fetch requests to select the advertised `anysearch-search` provider explicitly ([#11716](https://github.com/diegosouzapw/OmniRoute/pull/11716)) — thanks @pacocartones +- **fix(providers):** Keep overnight peak-hour protection active after midnight according to the window's configured start day ([#11718](https://github.com/diegosouzapw/OmniRoute/pull/11718)) — thanks @pacocartones +- **fix(providers):** correct all five g4f.space gateway descriptions to document proof-of-work credits or member API keys, variable limits, and the remote prompt boundary instead of promising free keyless access at five requests per minute ([#11738](https://github.com/diegosouzapw/OmniRoute/pull/11738)). +- **fix(db):** `model_capabilities` is created by a migration instead of lazily on the first models.dev sync, so a clean install and an upgraded install converge on the same schema regardless of which features have run ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(ci):** `check:install-upgrade` now fails on an `npm` install truncated by ENOSPC (npm reports it as a warning and still exits 0), authenticates its health probe so the version assertion works against the hardened health payload, frees the clean-install tree before the upgrade phase, and no longer reports a schema divergence computed from a boot that never served ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(routing):** harden session-affinity key derivation against oversized explicit IDs and empty structural-payload collisions, while recognizing Gemini and common text request shapes ([#11746](https://github.com/diegosouzapw/OmniRoute/pull/11746)) — thanks @MumuTW +- **fix(packages/browser-pool):** regenerate `package-lock.json` so the `packages/browser-pool` workspace's locked `playwright`/`@types/node` (and transitives) match its `package.json` specs, fixing cache-only/offline installs (`npm ci --offline`, Nix `buildNpmPackage`) that previously failed with `ENOTCACHED` ([#11747](https://github.com/diegosouzapw/OmniRoute/issues/11747)) — thanks @benjaminkitt ([#11784](https://github.com/diegosouzapw/OmniRoute/pull/11784)) — thanks @geekyNads +- **fix(assets):** Stop distributing 79 provider and CLI logos without verified source/license provenance, keep every provider/tool available through the internal generic icon, and prevent those IDs from falling through to an external icon CDN. ([#11750](https://github.com/diegosouzapw/OmniRoute/pull/11750)) +- **fix(providers):** retire the common `chatgpt-web` / `cgpt-web` provider because its pre-key and proof-of-work provenance could not be cleared ([#11754](https://github.com/diegosouzapw/OmniRoute/pull/11754)) — explicit chat and image requests, slash-prefixed models, persisted aliases, proxy overrides, and combo targets now fail closed with a sanitized HTTP `410` / `PROVIDER_RETIRED` before upstream dispatch. Migration 163 tombstones matching connections and invalidates their active leases without deleting history or widening API-key allowlists. The independent `chatgpt-web-codex` / `cgpt-codex` provider and its MIT-noticed vendored implementation remain available. +- **fix(usage):** quota and usage refresh no longer 409 when an exclusive lease reserves the connection ([#11758](https://github.com/diegosouzapw/OmniRoute/pull/11758)) — thanks @TheDemonTuan +- Keep the embedding registry's vector width and `embedding` type on models when a synced model exists for the same id, so `/v1/models` no longer reports registry-described embedding models widthless or untyped (#11761) — thanks @rahilmavani +- Correct `google/gemini-embedding-001` on the OpenRouter route to 3072 dimensions, the width it returns when `dimensions` is not sent (#11761) — thanks @rahilmavani +- **fix(vision-bridge):** Restrict automatic vision routing and fallbacks to models available in authoritative live provider catalogs, including aliases and effort variants ([#11767](https://github.com/diegosouzapw/OmniRoute/issues/11767)) — thanks @pacocartones ([#11813](https://github.com/diegosouzapw/OmniRoute/pull/11813)) +- **fix(providers):** reclassify Cerebras as a one-time $5 signup credit (payment method required, 30-day validity), not a recurring no-card 1M tokens/day trial ([#11773](https://github.com/diegosouzapw/OmniRoute/issues/11773)) ([#12591](https://github.com/diegosouzapw/OmniRoute/pull/12591)) — thanks @HouMinXi +- fix(cli): stop prepublish from re-rebuilding the already-built ESM-only opencode-plugin dist (#11787) ([#11990](https://github.com/diegosouzapw/OmniRoute/pull/11990)) — thanks @hartmark +- **fix(db):** Fresh installs no longer log a non-fatal `no such table: compression_run_telemetry` warning when retention cleanup runs before the lazily-created telemetry table exists ([#11802](https://github.com/diegosouzapw/OmniRoute/pull/11802)) — thanks @RaviTharuma +- fix(sse): set X-OmniRoute-Selected-Connection-Id on successful combo dispatches so downstream consumers stop falling back to an empty connection id (#11810) ([#11986](https://github.com/diegosouzapw/OmniRoute/pull/11986)) — thanks @hartmark +- **fix(services):** embedded CLIProxyAPI lifecycle checks now use public `/healthz`, while model discovery uses the configured dedicated data-plane API key instead of the management password ([#11811](https://github.com/diegosouzapw/OmniRoute/pull/11811)) — thanks @RaviTharuma +- Fix persisted model steps returning an error in the playground route simulator, and warn when structural steps cannot be simulated. ([#11862](https://github.com/diegosouzapw/OmniRoute/pull/11862)) — thanks @pacocartones +- **fix(providers):** Antigravity's dynamic mitmAlias table no longer routes `gemini-3.7-flash-{high,medium,low}` to a literal tier-suffixed upstream id just because one connected account's own discovery listed it directly — those display ids always resolve through the safe `gemini-3.7-flash-tiered` static alias, so one account's Google-provisioned access no longer 404s every sibling account of the provider ([#11824](https://github.com/diegosouzapw/OmniRoute/issues/11824), [#11651](https://github.com/diegosouzapw/OmniRoute/issues/11651)) ([#11988](https://github.com/diegosouzapw/OmniRoute/pull/11988)) — thanks @hartmark +- **fix(dashboard):** Expose Profile loading, errors, page structure, and XP progress to assistive technologies ([#11838](https://github.com/diegosouzapw/OmniRoute/pull/11838)) — thanks @pacocartones +- **fix(socks):** `Agent.connectTimeout` now bounds both SOCKS handshake and TLS connect, and `family === null` no longer falls back to `fetch-socks` ([#11842](https://github.com/diegosouzapw/OmniRoute/pull/11842)) — thanks @maxmad64bis +- **perf(compression):** OOM mitigations for large payload hashing, memoization, and token estimation ([#11844](https://github.com/diegosouzapw/OmniRoute/pull/11844) — thanks @AndrianBalanescu) +- Excluded Next.js Node File Trace manifests (`*.nft.json`) from the published npm tarball. They are build-time metadata and are never read while serving, but had grown to 668.7 MB — 61% of the package — which pushed the upload past the registry limit and made `npm publish` fail with `413 Payload Too Large`. ([#11864](https://github.com/diegosouzapw/OmniRoute/pull/11864)) +- **fix(cli):** keep the global OmniRoute server `--base-url` separate from provider-node `--endpoint` values in add, update, and validate commands ([#11860](https://github.com/diegosouzapw/OmniRoute/pull/11860)) — thanks @pacocartones +- **fix(config):** Nous Research's `Hermes-4-405B` model now displays as "Hermes 4 405B (Nous Research)" in both the provider registry and the free-model catalog, instead of the mislabelled "Hermes 4 7B" ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#11993](https://github.com/diegosouzapw/OmniRoute/pull/11993)) +- **fix(provider/nous):** inject required user= tag into Nous Research inference requests to resolve upstream 400 "missing tags" error ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#12044](https://github.com/diegosouzapw/OmniRoute/pull/12044)) +- **fix(build):** `npm run build` now fails in one second with a named package and a copy-pasteable fix when npm silently drops an externalised optional native dependency, instead of dying four minutes in with `Module not found: Can't resolve 'better-sqlite3'` ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- **fix(install):** `postinstall` no longer throws `ReferenceError: isAndroid is not defined` — failing the whole `npm install` — when the `better-sqlite3` rebuild fallback times out; the manual-fix guidance is reachable again ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- Fixed the Alibaba free-tier allowlist test that went red on its own once the shipped catalog's `validUntil` (2026-08-27) passed, leaving every PR and `main` with a failing `Unit Tests (1/8)`. The test now builds its own packs with dates it controls, and covers the expired-pack fallback that production has actually been serving. ([#11867](https://github.com/diegosouzapw/OmniRoute/pull/11867)) +- Split the npm registry upload into its own GitHub-hosted job. npm refuses `--provenance` from a self-hosted runner (`422 ... Only "github-hosted" runners are supported`), which blocked the v3.8.50 publish; the heavy verification cannot move to a hosted runner, so it now hands the proven tarball over instead. ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(ui):** the shared `DataTable` loading state no longer reads its decorative ⏳ glyph out to assistive technology, and now carries the same `role="status"` / `aria-live="polite"` / `aria-busy="true"` semantics as `PageLoading` ([#11869](https://github.com/diegosouzapw/OmniRoute/pull/11869)) — thanks @pacocartones +- **fix(api):** Bound the `timeout` accepted by `POST /api/providers/[id]/login` to the same 15s–600s window the Adobe Firefly and Conol browser-login paths already enforce, so a request can no longer pin a headful browser and the single active-login slot with an arbitrarily large poll budget. ([#11871](https://github.com/diegosouzapw/OmniRoute/pull/11871)) — thanks @pacocartones +- **fix(api):** Stop the tunnel and MITM routes from returning raw child-process error text, which disclosed host paths, binary install locations and Tailscale `tskey-*` credentials in a body some of these routes serve to non-loopback callers, and return a real 400 instead of a framework 500 when a tunnel request body fails validation. ([#11872](https://github.com/diegosouzapw/OmniRoute/pull/11872)) — thanks @pacocartones +- **fix(open-sse):** stream TTFT/ITL are sampled from a monotonic clock (`performance.now`) instead of `Date.now`, so an NTP correction or wall-clock jump can no longer inflate a genuine TTFT or produce a negative one that the `ttft >= 0` guard silently drops — protecting the router's OTel/EWMA/`usage_history`/speed-ranking signals ([#11873](https://github.com/diegosouzapw/OmniRoute/pull/11873)) — thanks @pacocartones +- **fix(providers):** xAI `reasoning_effort: "xhigh"` now reaches grok-4.6+ instead of being silently clamped to `"high"` ([#11879](https://github.com/diegosouzapw/OmniRoute/pull/11879)) — thanks @NoxzRCW +- **fix(dashboard):** the providers page no longer crashes into the error boundary when a provider id collides with an `Object.prototype` member (`constructor`, `__proto__`); icon lookups are own-property guarded ([#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)) — thanks @NoxzRCW +- **fix(skills):** injected skill tools declared in shorthand (`{"content": "string"}`) now forward valid JSON Schema, unblocking providers that validate tool schemas strictly such as Zhipu GLM on the Console Go tier ([#11881](https://github.com/diegosouzapw/OmniRoute/pull/11881)) — thanks @NoxzRCW +- **fix(api):** `POST /api/playground/simulate-route` now surfaces `combo-ref` and `provider-wildcard` persisted combo steps with a specific warning (naming the referenced combo, or the unresolved `provider/model` wildcard) instead of folding them into a generic "unsupported step" count; a `provider-wildcard` step is also now included as an unresolved target so the operator can see it is in the route (ported from [#11882](https://github.com/diegosouzapw/OmniRoute/pull/11882) — thanks @NoxzRCW). ([#11930](https://github.com/diegosouzapw/OmniRoute/pull/11930)) +- **fix(translator):** the streaming OpenAI→Claude translator keeps upstream usage, including prompt-cache tokens, when it arrives on a trailing `choices: []` chunk (Fireworks and any upstream using `stream_options.include_usage`) ([#11883](https://github.com/diegosouzapw/OmniRoute/pull/11883)) — thanks @NoxzRCW +- **fix(cli):** `omniroute update --apply` now tells you explicitly whether a running server was detected and, if so, that you must run `omniroute restart` to apply the update — it never restarted anything and previously implied the update was already live once files were installed. The dashboard's npm-mode Update flow (`/api/system/version`) now tries OmniRoute's own PID-file-managed supervisor before falling back to pm2, and reports an honest "restart required" step instead of a silent pm2-only "skipped" that read like a completed update. The server-side latest-version lookup backing the dashboard's update banner also gained `--prefer-online`, closing the same stale-npm-cache class already fixed in the CLI's own copy for #4376 ([#11885](https://github.com/diegosouzapw/OmniRoute/issues/11885)). ([#12005](https://github.com/diegosouzapw/OmniRoute/pull/12005)) +- **fix(resilience):** clear persisted LKGP pins when a target suffers connection/provider exhaustion or is skipped before dispatch due to cooldown/exhaustion/unavailability, preventing subsequent requests from repeatedly prioritizing known-dead providers ([#11911](https://github.com/diegosouzapw/OmniRoute/issues/11911)). ([#12013](https://github.com/diegosouzapw/OmniRoute/pull/12013)) — thanks @HouMinXi +- **fix(sse):** the OpenAI→Claude stream translator now defers the terminal `message_delta`/`message_stop` emission until the real usage block has arrived (or a genuine end-of-stream flush forces it) instead of emitting it immediately on `finish_reason` — previously, when the trailing usage-only chunk (`{"choices":[],"usage":{...}}`) arrived *after* the `finish_reason` chunk (the normal order for Fireworks/vLLM/Together and other `stream_options.include_usage` upstreams), the client-visible `message_delta` still carried stale/zero usage even though `state.usage` was internally corrected too late to matter (ported from [#11915](https://github.com/diegosouzapw/OmniRoute/pull/11915) — thanks @HouMinXi). ([#11933](https://github.com/diegosouzapw/OmniRoute/pull/11933)) +- **fix(sse):** `stripStore()` now forces `store=false` for stateless OpenAI-compatible Responses-API targets (unless the connection has explicitly opted in via `providerSpecificData.openaiStoreEnabled`), instead of only handling the `openai`/`agentrouter` cases — a client-supplied `store` value previously passed through untouched to backends that don't actually persist responses server-side ([#11916](https://github.com/diegosouzapw/OmniRoute/pull/11916) — thanks @HouMinXi). +- **fix(models):** custom provider-node models (synced, custom, and alias-backed) now appear under their configured prefix in the unified catalog when the operator's model-id prefix mode is canonical, instead of being dropped whenever alias-inclusion was otherwise disabled ([#11918](https://github.com/diegosouzapw/OmniRoute/pull/11918) — thanks @HouMinXi). +- **fix(models):** the unified model catalog now suppresses stale static registry models (including effort-tier variants) for any provider whose active connection has an authoritative live synced catalog, not only providers already using exclusive-synced-listing — a connection with `providerUsesAuthoritativeLiveCatalog` previously kept serving both the live-synced models and the stale static rows side by side ([#11919](https://github.com/diegosouzapw/OmniRoute/pull/11919) — thanks @HouMinXi). +- **fix(ui):** `ProviderIcon`'s three remaining unguarded lookups (`PROVIDER_ICON_ALIASES`, `LOCAL_SVG_ALIASES`, `THEMED_SVGS`) now use `Object.hasOwn()` own-property checks — a provider id such as `constructor` or `__proto__` previously resolved through the prototype chain instead of falling through to the unknown-provider CDN fallback (`getLobeProviderIcon()` itself was already guarded by [#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)); `ProviderPageHeader`'s `color` field is also now optional, matching the rest of the component's defensive typing (ported from [#11920](https://github.com/diegosouzapw/OmniRoute/pull/11920) — thanks @HouMinXi). ([#11935](https://github.com/diegosouzapw/OmniRoute/pull/11935)) — thanks @NoxzRCW +- fix(ollama): preserve multi-byte UTF-8 content split across stream chunks in the Ollama NDJSON transform, which previously corrupted CJK/emoji into U+FFFD (#11921) — thanks @pacocartones +- **fix(providers):** OrcaRouter chat requests now target `/v1/chat/completions` instead of the bare `/v1` API root, fixing the upstream `404 Invalid URL (POST /v1)` ([#11923](https://github.com/diegosouzapw/OmniRoute/pull/11923)). — thanks @echel0nn +- **fix(plugins):** deliver the `onStreamComplete` event to disk-installed plugins. The event shipped in v3.8.50 (#9669) was emitted internally but had no plugin-facing wiring, so no plugin could ever subscribe: the manifest schema silently dropped `hooks.onStreamComplete`, and the loader/manager only knew the seven legacy hooks. `onStreamComplete` is now a declarable manifest hook, wired through the loader and registered by the manager like the other hooks, and its payload carries a `requestId` so consumers can correlate the stream-completion event with the originating request ([#11934](https://github.com/diegosouzapw/OmniRoute/pull/11934)) — thanks @amaleta / @pacocartones +- **fix(db):** the Qdrant embedding-model dropdown now lists local/self-hosted providers (Ollama, LM Studio, vLLM, etc.) — an active connection is treated as "configured" when the provider allows an optional API key, not only when it has a real key or OAuth, so a running local embedding provider is no longer hidden from the picker ([#11949](https://github.com/diegosouzapw/OmniRoute/issues/11949)) ([#11995](https://github.com/diegosouzapw/OmniRoute/pull/11995)) +- **fix(providers):** Vertex AI Anthropic partner-model discovery now calls the Model Garden `v1beta1` publisher list (`/v1beta1/publishers/anthropic/models`, global) and parses its `publisherModels` envelope, so Claude models auto-synced from Vertex populate the active live catalog and route at request time instead of returning `Model '' is not available in the active live catalog` ([#11991](https://github.com/diegosouzapw/OmniRoute/issues/11991)) — thanks @fabioluissilva ([#11998](https://github.com/diegosouzapw/OmniRoute/pull/11998)) +- **fix(cli):** support `--base-url` alongside `--endpoint` in `omniroute nodes add`, `update`, and `validate` subcommands to prevent global `--base-url` shadowing issues ([#11999](https://github.com/diegosouzapw/OmniRoute/issues/11999)). ([#12033](https://github.com/diegosouzapw/OmniRoute/pull/12033)) — thanks @HouMinXi +- **fix(providers):** `cloudflare-ai` no longer refuses image content parts for every Workers AI model ([#12002](https://github.com/diegosouzapw/OmniRoute/pull/12002)) — the plain-string `content` requirement behind #2539 is carried by the _model_ schema, not by the `/ai/v1/chat/completions` endpoint (measured: an all-text part array returns 200 on `@cf/mistralai/mistral-small-3.1-24b-instruct`, `@cf/meta/llama-4-scout-17b-16e-instruct` and `@cf/meta/llama-3.3-70b-instruct-fp8-fast`, and 400 on the text-only `@cf/qwen/qwen2.5-coder-32b-instruct`). `transformRequest()` flattened every array and threw on the first non-text part (#6390), so image input was refused for vision-capable Cloudflare models that accept it. All-text arrays are still flattened — the one shape every model accepts — while an array carrying a non-text part is passed through untouched, so the attachment is still never silently dropped. Regression guards: `tests/unit/cloudflare-ai-image-parts-6390.test.ts`. — thanks @davidlinfr +- **fix(resilience):** decouple the limiter-managed execution backstop from the queue-wait budget — new `requestQueue.executionMaxWaitMs` (env `RATE_LIMIT_EXECUTION_MAX_WAIT_MS`, default 600000 = 10 min) now feeds Bottleneck's post-dispatch `expiration`, while `requestQueue.maxWaitMs` keeps its documented queue-wait semantics. Previously the queue-wait budget doubled as the execution expiration, so legitimate long-running calls on non-incremental gateways (whole generation buffered before the first upstream byte, e.g. Console Go / Command Code tiers serving GLM models) were killed mid-flight at the queue budget with a false 504 `RATE_LIMIT_EXECUTION_TIMEOUT` — the local limiter undercut the provider-aware upstream fetch-start timeouts. The surfaced 504 message now names `requestQueue.executionMaxWaitMs`; the error keeps the #4165 guarantees (disclaims an upstream timeout, preserves the Bottleneck error as `cause`, branded code + trusted provenance, classified request-scoped so combo falls back). A real queue-wait bound (the `Promise.race` around `limiter.schedule()` sketched in #9533) remains future work. (#12025) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **Call logs:** keep the `error` field when an artifact exceeds the storage cap, instead of replacing it with the omission marker. The error is the only field that says *why* a request failed and is typically ~90 bytes next to the multi-hundred-KB bodies that trip the cap, so dropping it left a size-limited row undiagnosable — a provider outage, a local timeout and an upstream 400 all rendered identically. It is now preserved at every fallback stage, truncated to 4KB if it is itself large ([#12026](https://github.com/diegosouzapw/OmniRoute/issues/12026)). ([#12095](https://github.com/diegosouzapw/OmniRoute/pull/12095)) — thanks @ntdat812 +- **fix(diagnostics):** preserve the error field (truncated to 4KB with a `[truncated: …]` suffix) in every call-log artifact size-limit fallback stage. Previously the minimal fallback replaced the error with `[omitted: call log artifact size limit exceeded]`, so an oversized artifact row showed nothing about WHY the request failed — e.g. 91 of 847 opencode-go 504 rows on one production instance were undiagnosable from the dashboard. Oversized request/response bodies are still omitted exactly as before; the error cap is independent of the payload sizes that tripped the fallback. (#12026) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **fix(sse):** OpenAI Responses clients that declare the native `web_search` tool now receive a spec-shaped `web_search_call` output item with `action.sources` alongside the preserved function-call round-trip, so search results executed through OmniRoute's own search backend are consumable by standard Responses clients (Codex, pi-web-access, …). ([#12031](https://github.com/diegosouzapw/OmniRoute/pull/12031)) — thanks @watchingdogs +- **fix(cli):** use in-thread alias resolver hooks on modern runtimes to avoid deprecation noise and improve Node.js forward compatibility ([#12073](https://github.com/diegosouzapw/OmniRoute/issues/12073)). ([#12083](https://github.com/diegosouzapw/OmniRoute/pull/12083)) — thanks @xiaoyaner0201 +- **fix(combo):** an operator-set **Agent Features → Context length** on a combo is now honored at request time. The value was persisted and advertised through `/v1/models`, but `resolveComboContextLimit()` never consulted it — so a multi-target combo whose members carry no per-model window fell through to the provider's generic `defaultContextLength` (openrouter 128000, command-code 200000) and rejected large requests with `Input exceeds context window … limit 128000` despite the combo being explicitly sized much larger. An identical single-target combo worked, because it collapses to its concrete target before the guard runs. Invalid values (0/negative/NaN/Infinity) are ignored, so the existing target → combo-min → fallback order is unchanged. ([#12090](https://github.com/diegosouzapw/OmniRoute/pull/12090)) — thanks @adivekar-utexas +- **fix(sse):** passthrough streams now estimate usage on finish when upstream closes without usage even with `stream_options.include_usage` — avoids `0 tokens / 0%` for providers that stay silent (and correctly handles trailing empty-choices usage) ([#12151](https://github.com/diegosouzapw/OmniRoute/pull/12151)) — thanks @maxmad64bis +- **fix(combos):** clearing an agent feature in the combos editor now persists — unchecking context cache protection, or emptying the system message or tool filter, sends an explicit `null` instead of dropping the field from the `PUT` body, which the update merge read as "leave unchanged" ([#12177](https://github.com/diegosouzapw/OmniRoute/pull/12177)) — thanks @foreveryh / @ntdatt812 +- **fix(memory):** self-hosted embedding endpoints now vectorize — the vector width is measured from the first embedding that comes back instead of being read from a registry that cannot describe them, so `vec_memories` is created and memories stop piling up unvectorized behind a green health check ([#12180](https://github.com/diegosouzapw/OmniRoute/pull/12180)) — thanks @kanade-hoshino / @ntdatt812 +- **fix(translator):** the leading `system` message now reaches Responses-API upstreams when its `content` is a content-part array — it was read as `typeof content === "string" ? content : ""`, so a prompt-caching client (Anthropic `cache_control`, the shape LiteLLM and the Anthropic SDK emit) had its entire system prompt replaced by an empty `instructions`. The request was still accepted with a normal `prompt_tokens` count, so the model answered with no instructions and nothing in the response said they were missing. Mid-conversation system turns already handled the array shape ([#7056](https://github.com/diegosouzapw/OmniRoute/pull/7056)); only the first one did not ([#12206](https://github.com/diegosouzapw/OmniRoute/issues/12206)). Regression guard: `tests/unit/translator-openai-responses-system-content-parts.test.ts`. ([#12207](https://github.com/diegosouzapw/OmniRoute/pull/12207)) — thanks @Gorillaz322 +- **fix(free-tier):** `/api/free-tier/summary` no longer computes its totals from a Radar feed built before the catalog the running release ships. When the cached feed is older — or carries no build date at all — the route answers from the shipped catalog, resolved through the operator's local model state so disabled and tombstoned models stay out of the numbers ([#12215](https://github.com/diegosouzapw/OmniRoute/pull/12215)). — thanks @maxmad64bis +- **fix(oauth):** Keep a Claude personal workspace and a Team organization as separate connections — they share the same email and `accountUUID`, so the email-only OAuth dedup let the second login overwrite the first account's tokens; `organizationUUID` now disambiguates them, the way `workspaceId` does for Codex ([#12222](https://github.com/diegosouzapw/OmniRoute/pull/12222)) — thanks @davidebaraldo +- **fix(resilience):** a 402 on a single paid model of a passthrough/gateway provider (e.g. `kilo-gateway`, `ollama-cloud`) no longer terminalizes the whole connection with a never-auto-recovered `credits_exhausted` status — only the paid model is locked out, so free models on the same key keep serving. 402 variant of [#3027](https://github.com/diegosouzapw/OmniRoute/issues/3027). Single-credential providers are unaffected — a 402 there is still treated as the key being genuinely out of credit ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239) / [#10616](https://github.com/diegosouzapw/OmniRoute/issues/10616)) ([#12242](https://github.com/diegosouzapw/OmniRoute/issues/12242)) — thanks @brick30llc-ctrl ([#12266](https://github.com/diegosouzapw/OmniRoute/pull/12266)) — thanks @geekyNads +- **fix(sse):** trust `finish_reason: "length"`/`"max_tokens"` over the reasoning-consumed-token ratio in response quality validation, so a reasoning model truncated below the old 90% threshold correctly fails and retries instead of returning empty content as a silent "success" ([#12262](https://github.com/diegosouzapw/OmniRoute/pull/12262)) — thanks @brick30llc-ctrl +- **fix(settings):** `PUT /api/settings/cache-config` now persists `alwaysPreserveClientCache` to the flat general settings the runtime cache-control policy actually reads; previously the value landed in the databaseSettings "cache" section and was silently ignored, so the endpoint had no effect on `cache_control` passthrough ([#12304](https://github.com/diegosouzapw/OmniRoute/pull/12304)) — thanks @davidebaraldo +- **fix(grok-cli):** treat omitted SuperGrokPro `creditUsagePercent` as 0% used so Provider Limits still renders a weekly bar (proto3 zero-elision) ([#12312](https://github.com/diegosouzapw/OmniRoute/pull/12312)) — thanks @HouMinXi +- **fix(combo):** Expose the two Auto-Combo scoring factors nobody could set — the combo validation schema and the dashboard weight sliders both declared 13 of the scorer's 15 factors, so `connectionDensity` (spreads load across a provider's connections) and `quality` were dropped on save and offered nowhere. The sliders also shipped their own default table that differed from the engine's on every non-zero factor and summed to 1.05, so the percentages shown next to them added up to 105%. Both lists now match `DEFAULT_WEIGHTS`, and a test keeps them there. Note that a combo whose stored `weights` omitted the two keys was effectively running with them at zero and the other thirteen renormalized upward; it now runs with the engine's intended distribution, so its routing does shift ([#12314](https://github.com/diegosouzapw/OmniRoute/pull/12314)) — thanks @maxmad64bis +- **fix(docs):** The free-tier reference no longer says its numbers come "confidence tagged per row" — no catalog entry carries a confidence tag and the API serves none, so every figure on that page is an estimate of the same, unstated quality. The page now states what an entry does vouch for: an independently documented hard stop (set by hand with the source in a comment, never defaulted to `true`) and a prompt-training disclosure, both with live counts the `check:docs-counts` gate keeps honest ([#12318](https://github.com/diegosouzapw/OmniRoute/pull/12318)) — thanks @maxmad64bis +- **fix(usage):** `adobe-firefly` and `firefly` have had a working usage fetcher since Adobe Firefly landed, but neither was ever added to the registration list, so the provider-plugin manifest, `genericQuotaFetcher` and the free-access quota cache all reported them as having no usage support — while `USAGE_SUPPORTED_PROVIDERS` said the opposite. Both are now declared, which also means their credit balance is fetched like any other declared provider's: `registerGenericQuotaFetchers` now registers a generic quota fetcher for them, and `resolveFreeAccessState` no longer returns early. A test holds the registration list to the dispatcher's switch in both directions, which is what the module's own docstring already asked for in prose ([#12321](https://github.com/diegosouzapw/OmniRoute/pull/12321)) — thanks @maxmad64bis +- **fix(quota):** drop the generic quota cache (agy / Antigravity / Claude OAuth) on an upstream 429 so reset-aware scoring does not keep a 60s stale snapshot, and force-refresh the next usage fetch so inner provider caches cannot recache the same window ([#12325](https://github.com/diegosouzapw/OmniRoute/pull/12325)) — thanks @HouMinXi +- **fix(combos):** deleting a combo now clears its persisted LKGP pins instead of leaving unreachable `key_value` rows behind ([#12326](https://github.com/diegosouzapw/OmniRoute/issues/12326)) ([#12425](https://github.com/diegosouzapw/OmniRoute/pull/12425)) — thanks @KrzysiekSko +- **fix(sse):** Keep ZWNJ (U+200C) and ZWJ (U+200D) in assistant text, reasoning and tool-call arguments — Persian/Kurdish half-space (`ارائه‌دهنده`), Arabic/Indic shaping and emoji sequences no longer lose them; the response de-obfuscation now removes joiners only between ASCII word characters, where the request side inserts them ([#12186](https://github.com/diegosouzapw/OmniRoute/issues/12186)) — thanks @rezjalibd ([#12359](https://github.com/diegosouzapw/OmniRoute/pull/12359)) — thanks @pacocartones +- **fix(resilience):** count resolved upstream 5xx results against the provider circuit breaker on the chat path — `CircuitBreaker.execute()` no longer reads a resolved `{ success: false, status: 5xx }` as a success that cancels the call-site failure, so a provider answering 503s now trips its breaker instead of staying `CLOSED` at `failureCount: 1`; single-model and combo dispatches are each accounted exactly once ([#12254](https://github.com/diegosouzapw/OmniRoute/issues/12254)) ([#12360](https://github.com/diegosouzapw/OmniRoute/pull/12360)) — thanks @pacocartones +- **fix(providers):** resolve the Codex quota auto-ping model from the live provider catalog and lifecycle registry instead of the retired `gpt-5.1-codex-mini`, and pause the ping with one actionable warning when no selectable Codex model exists rather than retrying a shut-down id every cooldown window ([#11905](https://github.com/diegosouzapw/OmniRoute/issues/11905)) ([#12361](https://github.com/diegosouzapw/OmniRoute/pull/12361)) — thanks @pacocartones +- **fix(api):** keep the `{created, data}` wrapper on combo-routed `/v1/images/generations` responses and default Codex image results to `b64_json` on both `/v1/images/generations` and `/v1/images/edits` so Codex CLI's built-in `image_gen` can decode them ([#12268](https://github.com/diegosouzapw/OmniRoute/issues/12268)) ([#12362](https://github.com/diegosouzapw/OmniRoute/pull/12362)) — thanks @pacocartones +- **fix(sse):** Name the shadowed custom provider node when a built-in provider id/alias (e.g. `openference` → `of`) reserves the prefix of an existing OpenAI/Anthropic-compatible node, so the runtime `No active credentials for provider: ` error explains that the prefix routed to the built-in and never reached the node's healthy connections, instead of contradicting the dashboard ([#11943](https://github.com/diegosouzapw/OmniRoute/issues/11943)) — thanks @morpheus9393 ([#12365](https://github.com/diegosouzapw/OmniRoute/pull/12365)) — thanks @pacocartones +- **fix(i18n):** wrap `ccOnboardingKeyPlaceholder` in ICU single quotes across all 43 locales so angle brackets render literally instead of being parsed as rich-text tags, which crashed the Claude Code onboarding block with `INVALID_MESSAGE: INVALID_TAG` ([#12302](https://github.com/diegosouzapw/OmniRoute/issues/12302)) ([#12369](https://github.com/diegosouzapw/OmniRoute/pull/12369)) — thanks @gonisulaimann +- **fix(models):** publish `effort_tiers` on Kimi K3's synced base-model entries (`kmca/k3`, `kmca/k3-256k`) so catalog-only clients (OpenCode, plain SDK pickers) can see and select the reasoning tiers (`low`/`high`/`max`) the synced metadata already carried — the `isSkippedEffortProvider` gate no longer suppresses tier visibility on those base entries, while synthetic `-` variant generation stays prevented and Codex/GLM base models remain excluded unchanged ([#12299](https://github.com/diegosouzapw/OmniRoute/issues/12299)) ([#12371](https://github.com/diegosouzapw/OmniRoute/pull/12371)) — thanks @gonisulaimann +- **fix(guardrails):** keep `auto`/`auto/*` virtual combos exempt from the Vision Bridge `fixedModel` credential guard so a combo target is passed through instead of silently falling back to global auto-selection ([#12237](https://github.com/diegosouzapw/OmniRoute/issues/12237)) ([#12373](https://github.com/diegosouzapw/OmniRoute/pull/12373)) — thanks @pacocartones +- **fix(combo):** capability-filter exhaustion caused by `max_tokens` above every target's known output limit now reports that reason (requested `max_tokens` vs the pool's highest known ceiling) instead of the unrelated "supports structured output" message ([#12229](https://github.com/diegosouzapw/OmniRoute/issues/12229)) — thanks @DW-MediaLab ([#12374](https://github.com/diegosouzapw/OmniRoute/pull/12374)) — thanks @pacocartones +- **fix(auth):** the `least-used` account strategy now prefers accounts without backoff before falling back to oldest `lastUsedAt`, the same tie-break `round-robin` already applies, so a failover no longer lands on a just-rate-limited account for a single request ([#12279](https://github.com/diegosouzapw/OmniRoute/issues/12279)) — thanks @tenshiak ([#12375](https://github.com/diegosouzapw/OmniRoute/pull/12375)) — thanks @pacocartones +- **fix(docker):** the `chatgpt-web-codex-browser` image now finds the Chrome binary under `chrome-linux64/` (Chrome for Testing layout in `playwright:v1.62.0-noble`) as well as the legacy `chrome-linux/`, so the container no longer crash-loops with `exec: --headless=new: not found` ([#12024](https://github.com/diegosouzapw/OmniRoute/issues/12024)) ([#12376](https://github.com/diegosouzapw/OmniRoute/pull/12376)) — thanks @pacocartones +- **fix(providers):** declare `groq/compound` and `allam-2-7b` as non-reasoning models in the curated Groq registry so `reasoning_effort` / `output_config.effort` / `thinking` from Claude Code are stripped instead of forwarded, which Groq rejected with HTTP 400 ([#12134](https://github.com/diegosouzapw/OmniRoute/issues/12134)) ([#12379](https://github.com/diegosouzapw/OmniRoute/pull/12379)) — thanks @pacocartones +- **fix(executors):** `OpencodeExecutor` no longer forces a direct connection when the connection has a proxy assigned in Proxy Management but no per-account proxies: the single-account fast path used to wrap the upstream dispatch in the direct-egress sentinel, discarding the ambient proxy context the chat handler had pinned from `proxy_assignments`, so API-key `opencode`/`opencode-go` connections egressed from the host IP (and hit geoblocks) despite the assignment. The direct pin is now applied only when no ambient proxy context exists ([#11894](https://github.com/diegosouzapw/OmniRoute/issues/11894) — thanks @hizzt) ([#12380](https://github.com/diegosouzapw/OmniRoute/pull/12380)) — thanks @pacocartones +- **fix(api):** `GET /v1/models` with `MODELS_CATALOG_PREFIX_MODE=canonical` (or `?prefix=canonical`) now lists providers whose registry alias is undefined or equal to their own id (Antigravity, Antigravity CLI and other self-aliased built-ins) — their single `provider/model` id was dropped by the alias/canonical duplicate guard in the static, synced, custom and alias-backed catalog loops ([#12058](https://github.com/diegosouzapw/OmniRoute/issues/12058)) — thanks @cheynetom ([#12381](https://github.com/diegosouzapw/OmniRoute/pull/12381)) — thanks @pacocartones +- **fix(translator):** Drop replayed `thinking` blocks that carry no signature (the shape produced from cross-provider `reasoning_content`) instead of stamping the default Claude signature on them, which Anthropic rejected with `400 Invalid signature in thinking block` on the next turn served by an Anthropic rung ([#12105](https://github.com/diegosouzapw/OmniRoute/issues/12105)) — thanks @atescivitci-cmd ([#12386](https://github.com/diegosouzapw/OmniRoute/pull/12386)) — thanks @pacocartones +- **fix(cli):** Resolve Bun's `--preload` polyfill path against the package root instead of `dist/`, so `omniroute` installed with `bun install -g` no longer crashes at startup with `error: preload not found …/dist/open-sse/utils/setupPolyfill.ts` ([#11980](https://github.com/diegosouzapw/OmniRoute/issues/11980)) — thanks @joglomedia ([#12387](https://github.com/diegosouzapw/OmniRoute/pull/12387)) — thanks @pacocartones +- **fix(providers):** `gemini-business` now publishes its model catalog — `/v1/models` and `/v1/providers/gemini-business/models` list the 12 enterprise Gemini ids the executor understands instead of returning an empty list (#12107) ([#12389](https://github.com/diegosouzapw/OmniRoute/pull/12389)) — thanks @pacocartones +- **fix(db):** install `busy_timeout` before the SQLite connection's first statement so a process opening the database while another one closes its WAL connection waits out the transient EXCLUSIVE lock instead of dying with `database is locked`, and recognise the drivers' real BUSY/PROTOCOL/IOERR errors as transient in the corruption probe so the same lock no longer renames the database away as corrupt; deflakes `cross-process contenders never both acquire the same connection` (#12394 — thanks @pacocartones) +- **fix(chat-admission):** derive the `chat_admission_busy` 503 `Retry-After` from observed heavyweight-lease occupancy — the larger of the exhausted `OMNIROUTE_CHAT_ADMISSION_QUEUE_MS` window and the time since capacity last turned over, capped at 60 s — instead of a fixed 1 s (structural) / 2 s (byte-stage) hint that invited Codex/agent fan-out clients to re-send ~1 MiB `/v1/responses` bodies every second into a gate held for the whole SSE lifetime; an idle gate keeps the historical floors ([#12135](https://github.com/diegosouzapw/OmniRoute/issues/12135)) (#12395 — thanks @pacocartones) +- **fix(api-manager):** the API key permissions modal no longer silently drops `allowedCombos` entries its Combo picker cannot render — routing-rule names such as `rt-*`, which the backend already honours — when "All" is clicked and the key is switched back to "Restrict"; those entries now survive the toggle, are listed read-only under the combo list so the count and the list agree, and are saved back verbatim instead of persisting `[]` (deny-all) (#12397 — thanks @pacocartones) +- **fix(catalog):** write the NUL separator of the catalog connection memo key, the provider serviceKind memo key, the Video Bridge promotion group key and a JSON-exactness test fixture as the `\u0000` escape instead of a raw byte — same runtime value, but the raw byte made git, GitHub and ripgrep treat those files as binary (hidden PR diffs, silently skipped searches); a guard test now keeps raw NUL bytes out of `src/`, `open-sse/` and `tests/` (#12403 — thanks @pacocartones) +- **fix(providers):** add `CLAUDE_CODE_CLIENT_VERSION` and `GITHUB_COPILOT_CLI_VERSION` env overrides so Anthropic/Copilot client-version gates can be unblocked without a rebuild ([#12417](https://github.com/diegosouzapw/OmniRoute/issues/12417)) ([#12632](https://github.com/diegosouzapw/OmniRoute/pull/12632)) — thanks @HouMinXi +- **fix(db):** back-fill `last_ping_at` and `last_pinged_reset_key` on `provider_connections` during schema reconciliation so divergent lineages that skipped `123_quota_auto_ping` still accept quota auto-ping writes ([#12470](https://github.com/diegosouzapw/OmniRoute/pull/12470) — thanks @KooshaPari) +- **fix(ci):** document MIT exceptions for `@eloqnt/{config,format-json,format-po}` (next-intl transitive; locked tarballs omit `license`) and keep the A2A lifecycle vitest off the real SQLite persistence seam ([#12581](https://github.com/diegosouzapw/OmniRoute/issues/12581)) ([#12595](https://github.com/diegosouzapw/OmniRoute/pull/12595)) +- OpenCode plugin `/v1/models` catalog fetch now waits 30s by default and attaches HTTP `statusCode` on 401/5xx so host fallback plugins can hop instead of seeing an untyped AbortError/UnknownError. ([#12607](https://github.com/diegosouzapw/OmniRoute/pull/12607)) — thanks @RaviTharuma +- **CI:** the OpenAPI security-tier gate now mirrors `isAlwaysProtectedPath()` in full — it also reads `ALWAYS_PROTECTED_API_PATTERNS`, so the pattern-gated credential routes (`/api/providers/{id}/{claude,codex}-auth/{export,apply-local}`, GHSA-5926-2w35-7h4q) no longer report as unannotated. (#12605) +- **fix(api):** GET /v1/models no longer waits forever on a hung coalesced catalog rebuild; cold-path waits are bounded (`CATALOG_BUILD_TIMEOUT_MS`, default 8s) and a last-good 200 is served when the rebuild times out ([#12627](https://github.com/diegosouzapw/OmniRoute/issues/12627)). ([#12628](https://github.com/diegosouzapw/OmniRoute/pull/12628)) — thanks @RaviTharuma +- **fix(cursor):** a non-array `tool_calls` on an assistant message no longer crashes the cursor request translator with a `TypeError`; both loops now require an array ([#12689](https://github.com/diegosouzapw/OmniRoute/issues/12689)) ([#12691](https://github.com/diegosouzapw/OmniRoute/pull/12691)) — thanks @soroush5 +- **fix(chat):** Chat Completions no longer return empty `content` after a server-owned memory or skills tool; the first provider send and account/model recovery now share one pipeline so a follow-up round-trip can fill the reply ([#12696](https://github.com/diegosouzapw/OmniRoute/issues/12696)) — thanks @HouMinXi ([#12867](https://github.com/diegosouzapw/OmniRoute/pull/12867)) +- **fix(dashboard):** batch-deleting provider keys no longer toasts failure after a successful delete when the confirm button's click event is forwarded as `onAfter` ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) — thanks @HouMinXi +- **fix(glm):** drop the extra 16th argument to `createSSETransformStreamWithLogger` that TypeScript rejected (TS2554) and that never reached the TransformStream ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) ([#12770](https://github.com/diegosouzapw/OmniRoute/pull/12770)) — thanks @HouMinXi +- **fix(dashboard):** pass `nodeMap` into Runtime `QuotaGroup` so a quota monitor in error/exhausted/alerting no longer throws `ReferenceError: nodeMap is not defined`. ([#12868](https://github.com/diegosouzapw/OmniRoute/pull/12868)) — thanks @HouMinXi +- Restricted API keys whose `allowedModels` lists a combo name no longer skip every combo member at pre-dispatch (`ALL_TARGETS_SKIPPED`). Inner-target filtering still applies when the allow-list is a provider prefix or `disableNonPublicModels` is on ([#12899](https://github.com/diegosouzapw/OmniRoute/pull/12899)). — thanks @HouMinXi +- **fix(combos):** Effort-suffixed combo members inherit the base model's `model_context_overrides` row so priority order is not inverted on large requests ([#12926](https://github.com/diegosouzapw/OmniRoute/pull/12926)) — thanks @HouMinXi +- **fix(catalog):** Picker-added `customModels` enter the dispatch-time live catalog so combo members and bare inference no longer 400 ([#12934](https://github.com/diegosouzapw/OmniRoute/pull/12934)) — thanks @HouMinXi +- **fix(docker):** re-derive the Docker build's worker-pool memory budget from the MEASURED ~4.5 GB per-process RSS (the issue owner's own VPS dmesg OOM-killer reproduction) instead of the stale 2560 MB/worker inference #11419 shipped, and lower `OMNIROUTE_BUILD_WORKERS` 3 → 2 so 1 parent + 1 page-data worker (2 processes × 4.5 GB = 9 GB) fits the 12.288 GB (75%) budget on a 16 GB GitHub Actions runner — the previous default (1 parent + 2 workers = 13.5 GB) still overcommitted the runner and kept "Publish to Docker Hub" failing with `cannot allocate memory` after #11419 merged (#7518). ([#11663](https://github.com/diegosouzapw/OmniRoute/pull/11663)) +- **fix(electron):** the packaged Windows desktop build now passes the #7592 cold-restart smoke end to end. Five defects were found and fixed on the way: (1) optional-pack staging failed on any machine whose `tar` is GNU tar — it reads the drive letter in an absolute `-f C:\...` archive path as a remote rsh host (`Cannot connect to C:`), so staging now passes a bare filename with `cwd` at the tarball directory and surfaces tar stderr on failure; (2) the Electron `files` allowlist was missing `lib/loginHeaderCapture.js`, so the packaged main process crashed at startup with `Cannot find module './lib/loginHeaderCapture'`; (3) electron-builder ≥26 injects an `!**/node_modules/**` ignore into every extraResources pattern list that no positive filter can override, which silently dropped the staged runtime `node_modules` (including the better-sqlite3 N-API prebuild) from `resources/app` — a fresh v3.8.50 desktop build would have shipped with no native SQLite driver and reproduced the #7592 sql.js fallback on every machine; an `afterPack` hook now restores the staged `node_modules` after packing; (4) the packaged-app smoke harness redirected `USERPROFILE` into its temp DATA_DIR, but Electron resolves the Roaming profile from `%USERPROFILE%\AppData\Roaming\` (USERPROFILE takes precedence over the APPDATA env var) and the path service throws instead of creating it, so `requestSingleInstanceLock()` returned false and the app exited(0) silently before `app.whenReady()` — the harness now pre-creates the derived tree, and `ensureSmokeEnvDirs` is exported and unit-tested; (5) the #7592 driver assertion parsed a `[DB] Driver: ...` line that the server's primary DB path never printed (only the unused `openDatabaseAsync()` did) — `getDbInstance()` now emits the same line on open so the guard can actually assert the native driver. Also: the smoke env-allowlist unit test hardcoded POSIX paths and could only pass on Linux/macOS; expectations are now host-agnostic, with new regression tests for the USERPROFILE-derived tree and `tarPack` under absolute Windows-style paths ([#11443](https://github.com/diegosouzapw/OmniRoute/pull/11443)) — thanks @jonlwheat2-gif +- fix(sse): stop the auto-combo candidates inspector from silently dropping model-locked/cooled-down rows (#9133) ([#11994](https://github.com/diegosouzapw/OmniRoute/pull/11994)) — thanks @hartmark +- **fix(providers):** Perplexity Web no longer turns upstream stream failures into successful assistant text; pre-content failures remain eligible for fallback, partial output ends with a structured sanitized error, and failed sessions are not persisted ([#12465](https://github.com/diegosouzapw/OmniRoute/pull/12465)) +- **fix(video-bridge):** preserve chronological video and transcript fusion, source-frame timestamps, transcript provenance, full contact-sheet intervals, and pre-abort callback isolation ([#11681](https://github.com/diegosouzapw/OmniRoute/pull/11681)) +- **Z.ai Web:** HTTP 200 streams carrying an upstream error now terminate with a structured failure instead of assistant text plus a normal stop, preserving partial output while allowing pre-content combo fallback. ([#12454](https://github.com/diegosouzapw/OmniRoute/pull/12454)) +- Absorb `Error [AbortError]: request_signal_aborted` and DOMException AbortError shapes in the process-level client-abort crash guard so routine client disconnects no longer kill the server (exit code 7). ([#12165](https://github.com/diegosouzapw/OmniRoute/pull/12165)) — thanks @alvinveroy +- **fix(sse):** Treat Adapta Web `type:error` SSE events as sanitized non-stream failures instead of empty HTTP 200 completions. ([#12459](https://github.com/diegosouzapw/OmniRoute/pull/12459)) +- **fix(autoCombo):** layer 3 (`models_dev_tier`) no longer scores retired models as premium, merge cross-provider capability rows deterministically, and inherit tier scores through variant ids ([#11508](https://github.com/diegosouzapw/OmniRoute/issues/11508)). `getModelsDevTierFitness` now vetoes ids present in `config/quality/model-lifecycle.json` with status `retired` before any other signal; `loadModelCapabilities()` aggregates per `model_id` (any-non-null-true for booleans, max for `limit_context`) instead of last-write-wins over SQLite's undefined row order; a capabilities miss retries through `resolveScoresAs()`'s base id and reports `models_dev_tier:inherited`, matching layers 1–2 (#11492). Adds `getModelsDevTierFitnessWithSource()` and an `invalidateCapabilitiesCache()` test/ops hook. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) — thanks @Meet6338-X +- fix(sse): accept Claude `content:[]` + `stop_reason: max_tokens`/`tool_use` in `detectMalformedNonStream` (match `isEmptyContentResponse`) so Claude Code `/model` probes with `max_tokens: 1` no longer become a false 502 ([#11568](https://github.com/diegosouzapw/OmniRoute/pull/11568)) — thanks @Seramicx +- **fix(security):** sanitize `request.failed` diagnostics before publishing them to live dashboard listeners and replay history, while keeping status, model, provider, latency, and internal call-log diagnostics intact. ([#12469](https://github.com/diegosouzapw/OmniRoute/pull/12469)) +- **fix(pwa):** do not intercept dashboard navigations so Chrome can fall back from a stale HTTP/3 Alt-Svc advertisement (UDP :20128 is unpublished; F5 on a long-lived tab hung until a new tab opened a fresh TCP connection). ([#12767](https://github.com/diegosouzapw/OmniRoute/pull/12767)) — thanks @HouMinXi +- **fix(executors):** handle DuckDuckGo ERR_BN_LIMIT (418) without retrying — when the upstream returns `418 ERR_BN_LIMIT` (rate-limit/ban), the executor now returns the error immediately instead of burning another VQD acquisition that would only count against the IP limit. The retry logic for `418 ERR_CHALLENGE` (unsolved challenge) remains unchanged. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) ([#12110](https://github.com/diegosouzapw/OmniRoute/pull/12110)) — thanks @oyi77 / @Meet6338-X +- Fixed DuckDuckGo streaming responses losing JSON lines and UTF-8 characters split across network chunks. ([#11528](https://github.com/diegosouzapw/OmniRoute/pull/11528)) — thanks @pacocartones +- Harden generated Cloudflare, Vercel, and Deno relays by stripping hop-by-hop, framing, and proxy authentication headers before forwarding requests upstream. ([#11533](https://github.com/diegosouzapw/OmniRoute/pull/11533)) — thanks @pacocartones +- **feat(grok-cli):** Provider Limits shows grok-cli banked reset credits from `GetRemainingResets` (including a real zero; a failed RPC omits the row) and the existing View credits button now calls `ConsumerUiSvc/RedeemReset` for grok-cli. Live tokens use nested fields 10/20/30 (id + Timestamp), not compact 1/2/3. ([#12805](https://github.com/diegosouzapw/OmniRoute/pull/12805)) — thanks @HouMinXi +- **fix(memory):** Embedding Model Quick select, Embedding Source remote dropdown, and Rerank selector now list every configured provider with embedding/rerank support instead of only chat-catalog text matches plus OpenRouter live discovery; a generic OpenAI-compatible `/embeddings` + Cohere-compatible `/rerank` runtime fallback resolves any configured chat provider's embedding/rerank endpoint, so unlisted providers no longer fail with "Unknown embedding provider"; both memory selectors gained a free-text model override ([#11390](https://github.com/diegosouzapw/OmniRoute/pull/11390)) — thanks @rqzbeh +- Harden SQLite upgrades around the historical migration-074 version collision: missing discovery and inspector tables are replayed atomically, pre-existing databases (including setup-created skeletons) receive reusable content-addressed safety snapshots, and Node test/eval probes without `DATA_DIR` are isolated from the operator database. ([#12435](https://github.com/diegosouzapw/OmniRoute/pull/12435)) +- **fix(dashboard):** Moonshot/Kimi Open Platform voucher and cash leftover percentages follow the bucket balance, so an empty wallet no longer paints those rows as 100% while Available is 0% ([#12733](https://github.com/diegosouzapw/OmniRoute/pull/12733)) — thanks @HouMinXi +- **fix(combo):** return non-retryable HTTP 400 when all candidates for a pinned native Codex turn are unavailable due to model-scoped lockout, terminating the turn cleanly while preserving turn continuity and enabling standard Combo routing on subsequent turns ([#12240](https://github.com/diegosouzapw/OmniRoute/pull/12240)) — thanks @mdigitalbh81 +- Fixed auto-router strategies preserving the selected connection through ranking and dispatch when multiple connections share the same provider and model. ([#11530](https://github.com/diegosouzapw/OmniRoute/pull/11530)) — thanks @pacocartones +- Preserve Chat Completions JSON-object response formats when translating requests to the Responses API. ([#11529](https://github.com/diegosouzapw/OmniRoute/pull/11529)) — thanks @pacocartones +- Fix idempotency fingerprints so Responses API requests include `input` and other semantic generation fields using stable serialization, while excluding credentials and request metadata. ([#11532](https://github.com/diegosouzapw/OmniRoute/pull/11532)) — thanks @pacocartones +- **fix(api):** Generated API CLI commands now enforce required OpenAPI request bodies; Combo test commands forward the required `comboName` body, while API keys created by older writers after migration 149 preserve legacy allow-all Combo access without widening explicit empty allowlists — thanks @marcelokarval ([#12070](https://github.com/diegosouzapw/OmniRoute/pull/12070)) +- **fix(grok-web):** treat upstream streaming failures as failures instead of successful assistant text: error-only streams now fail readiness with HTTP 502, while failures after legitimate content preserve that partial output and terminate through the sanitized stream failure path without a normal `stop` completion. ([#12458](https://github.com/diegosouzapw/OmniRoute/pull/12458)) +- HuggingChat now turns HTTP 200 JSONL generation failures into a sanitized 502 before content, or a fixed public stream failure after partial output, so fallback and request persistence no longer record a false successful stop. ([#12456](https://github.com/diegosouzapw/OmniRoute/pull/12456)) +- **fix(providers):** keep 1min.ai HTTP 200 stream errors out of assistant content, preserve partial output, and expose sanitized terminal errors so pre-content failures can fall back. ([#12466](https://github.com/diegosouzapw/OmniRoute/pull/12466)) +- **fix(sessions):** preserve legacy Sessions data when the additive exclusive-session projection is unavailable, returning an empty projection and warning only once per contiguous outage instead of failing the endpoint; the Sessions badge intentionally reflects the merged legacy and exclusive row count introduced by [#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389) ([#11469](https://github.com/diegosouzapw/OmniRoute/pull/11469)) — thanks @KaspaPulse +- Render Nimble Search with the generic provider icon and serve Opper's proven logo locally. ([#12415](https://github.com/diegosouzapw/OmniRoute/pull/12415)) +- **fix(quality):** `config/quality/provider-assets-provenance.jsonl` was stale against the physical `public/providers/` tree (still listing 225 assets and 5 duplicate-content groups after several provider-retirement PRs removed 85 icon files), which would have made `check:provider-asset-provenance` fail on the next full run. Reconciled the manifest to the real 142-file snapshot (142 asset records, 1 duplicate-content group, 2 newly-registered files marked `unresolved` pending provenance research), repinned `auditedCommit`, and updated the regression test's hardcoded counts to match. ([#11876](https://github.com/diegosouzapw/OmniRoute/pull/11876)) +- fix(ui): wrap quota card footer action buttons to prevent clipping Refresh button on narrow cards — when extra action buttons such as `View credits` are present on Codex cards, allow the footer row to flex-wrap with `shrink-0` buttons so `Refresh now` remains visible and clickable across all responsive card widths ([#11464](https://github.com/diegosouzapw/OmniRoute/pull/11464)) — thanks @adevwithpurpose +- Keep Antigravity Gemini usable when the same connection's Claude weekly quota is empty; generic quota cache stays per-connection for every other provider. ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) — thanks @HouMinXi +- Fixed Tailscale tunnel setup to reject ports outside the valid 1-65535 range and safely install Windows MSI packages from paths containing apostrophes. ([#11534](https://github.com/diegosouzapw/OmniRoute/pull/11534)) — thanks @pacocartones +- Classify Cloudflared and Tailscale process-management routes as local-only and spawn-capable while preserving remote access to read-only tunnel status endpoints. ([#11531](https://github.com/diegosouzapw/OmniRoute/pull/11531)) — thanks @pacocartones +- Electron release workflow: the `publish-npm` job now grants `actions: read` to the reusable `npm-publish.yml` it calls (its `publish` job requests it), which is what made GitHub refuse the whole v3.8.50 run at startup and ship the release with zero desktop assets; a `workflow_dispatch` now builds the requested tag instead of the dispatching branch and can skip the npm leg (`publish_npm=false`) when only re-attaching assets ([#11974](https://github.com/diegosouzapw/OmniRoute/pull/11974)) +- **fix(dashboard):** The Combos page usage guide now reads its dismissal through `useSyncExternalStore` instead of correcting SSR state inside an effect, removing an extra commit of the page tree on every load (and the `react-hooks/set-state-in-effect` error it raised). ([#12671](https://github.com/diegosouzapw/OmniRoute/pull/12671)) +- Electron release: `electron/package-lock.json` regained the optional `electron-builder-squirrel-windows` subtree (13 entries) that `npm ci` had been refusing as out of sync, `electron-release.yml` gained a `build_ref` dispatch input and stops regenerating release notes on a re-attach dispatch, and the npm publish workflow attaches the SBOM to the GitHub Release on dispatch publishes too — so the v3.8.51 tag ships every desktop asset and the SBOM like v3.8.49 did ([#12022](https://github.com/diegosouzapw/OmniRoute/pull/12022)) +- Drained the reds every PR against `release/v3.8.51` was born with: documented the three service-worker build-id variables, registered the six retirement/tunnel tests with the mutation gate, approved `eslint-plugin-react-hooks` in the dependency allowlist, added the six `combo.sort.*` strings to `vi` and `pt-BR`, pointed the ChatGPT Web doc at the real migration-168 test, worded the g4f hint around the member key, and realigned four tests to the retired-provider catalog and the legacy-schema fixtures the retirement migrations touch. ([#11940](https://github.com/diegosouzapw/OmniRoute/pull/11940)) +- **fix(providers):** Claude, Grok, LMArena, Notion, and Perplexity web-cookie transports now use pooled `wreq-js` 3.2 instead of the native sidecar, with all nine supported bindings pinned and audited, and the applicable platform binding plus native-license evidence included in each release artifact ([#12429](https://github.com/diegosouzapw/OmniRoute/pull/12429), supersedes [#11753](https://github.com/diegosouzapw/OmniRoute/pull/11753)). +- **fix(providers):** Zed Hosted streaming failures now trigger fallback before content and end partial streams with a sanitized structured error instead of fake assistant text and a normal-success stop. ([#12455](https://github.com/diegosouzapw/OmniRoute/pull/12455)) +- **fix(test):** capture hook result after render (direct commit `b85d0abb0b`) +- **fix(sync):** repair the two regressions the v3.8.50 sync-back left on release/v3.8.51 (direct commit `5ade9e0851`) +- **revert(models):** drop the memo reset hook ported from #10055 (direct commit `282de841ae`) +- **fix(typecheck):** guard the optional connectionId before the reactive model sync (direct commit `f2825bad75`) +- **fix(quota-share):** release the winner's reserved in-flight slot (#11371) ([#11408](https://github.com/diegosouzapw/OmniRoute/pull/11408)) — thanks @oyi77 +- **fix(providers):** stop silently dropping reasoning effort on opencode families (#10788) ([#11409](https://github.com/diegosouzapw/OmniRoute/pull/11409)) — thanks @oyi77 +- **fix(combo):** enable genuine quota-aware routing for generic providers (antigravity, claude, etc.) ([#11411](https://github.com/diegosouzapw/OmniRoute/pull/11411)) — thanks @HouMinXi +- **fix(oauth):** allow expired token retry before deactivation and activate expired retry loop (#P0) ([#11414](https://github.com/diegosouzapw/OmniRoute/pull/11414)) — thanks @HouMinXi +- **perf(executors):** lazy-load the executor registry — defer class imports + construction to first use (#11220) ([#11421](https://github.com/diegosouzapw/OmniRoute/pull/11421)) — thanks @oyi77 +- **fix:** make release/v3.8.50 CI gates green (run 32786966560 root causes) ([#11450](https://github.com/diegosouzapw/OmniRoute/pull/11450)) — thanks @jonlwheat2-gif +- **fix(sse):** exact-domain cookie match and origin-equality URL assertions ([#11452](https://github.com/diegosouzapw/OmniRoute/pull/11452)) — thanks @hartmark +- **fix(providers):** send Responses API payload during OpenAI-compatible validation ([#11454](https://github.com/diegosouzapw/OmniRoute/pull/11454)) — thanks @tuandinh0801 +- **fix(command-code):** fallback to /alpha/generate for Go plan without Provider API access ([#11455](https://github.com/diegosouzapw/OmniRoute/pull/11455)) — thanks @Chewji9875 +- **fix(responses-continuation):** fail closed on a log-truncated stored input/output array ([#11473](https://github.com/diegosouzapw/OmniRoute/pull/11473)) — thanks @hartmark +- **fix(cli):** write jcode settings as [providers.omniroute] in config.toml ([#11484](https://github.com/diegosouzapw/OmniRoute/pull/11484)) — thanks @hongnoul +- **fix(logging):** raise chat-log array truncation cap to 1000 and unify duplicate implementations ([#11499](https://github.com/diegosouzapw/OmniRoute/pull/11499)) — thanks @hartmark +- **fix:** restore live websocket URL helpers ([#11509](https://github.com/diegosouzapw/OmniRoute/pull/11509)) — thanks @PauloFH +- **perf(core):** eliminate async waterfalls across home, cache, and db operations (#11396) ([#11512](https://github.com/diegosouzapw/OmniRoute/pull/11512)) — thanks @HouMinXi +- **fix(kiro):** add runtime.us-east-1.kiro.dev as first-attempt endpoint ([#11517](https://github.com/diegosouzapw/OmniRoute/pull/11517)) — thanks @rifqiawl +- **fix(providers):** remove duplicate 128-tool truncation in OpencodeExecutor (#11444) ([#11518](https://github.com/diegosouzapw/OmniRoute/pull/11518)) — thanks @HouMinXi +- **fix(providers):** poll onboardUser LRO before classifying BYOP ([#11519](https://github.com/diegosouzapw/OmniRoute/pull/11519)) — thanks @rifqiawl +- **fix(translator):** preserve exact tool name casing for Claude tool_use payloads (#11487) ([#11520](https://github.com/diegosouzapw/OmniRoute/pull/11520)) — thanks @HouMinXi +- **fix(kimi-web):** align health probe with executor domain ([#11521](https://github.com/diegosouzapw/OmniRoute/pull/11521)) — thanks @pacocartones +- **fix(cli):** preserve existing admin password during setup ([#11522](https://github.com/diegosouzapw/OmniRoute/pull/11522)) — thanks @pacocartones +- **fix(mitm):** keep Windows DNS elevation off the POSIX sudo path (#11430) ([#11542](https://github.com/diegosouzapw/OmniRoute/pull/11542)) — thanks @official-burak +- **fix(sse):** scale chat admission by ingest byte budget instead of a fixed request count ([#11548](https://github.com/diegosouzapw/OmniRoute/pull/11548)) — thanks @TheDemonTuan +- **fix:** accept Gemini Web cookie exports ([#11554](https://github.com/diegosouzapw/OmniRoute/pull/11554)) — thanks @rafacpti23 +- **fix(i18n):** complete Brazilian Portuguese translations ([#11558](https://github.com/diegosouzapw/OmniRoute/pull/11558)) — thanks @rafacpti23 +- **fix(sse):** explicit types for openai-responses pureHelpers — clears last failing core typecheck gate ([#11567](https://github.com/diegosouzapw/OmniRoute/pull/11567)) — thanks @jonlwheat2-gif +- **fix(opencode):** hydrate Proxy Pool references for no-auth connections ([#11584](https://github.com/diegosouzapw/OmniRoute/pull/11584)) — thanks @AStupidBear +- **fix(ci):** resolve inherited release/v3.8.51 base-red CI failures ([#11608](https://github.com/diegosouzapw/OmniRoute/pull/11608)) — thanks @jonlwheat2-gif +- **fix(db):** fallback load STORAGE_ENCRYPTION_KEY from env files ([#11614](https://github.com/diegosouzapw/OmniRoute/pull/11614)) — thanks @benzntech +- **fix(providers):** mark Brave Search as no longer having a free tier ([#11621](https://github.com/diegosouzapw/OmniRoute/pull/11621)) — thanks @turbolego +- **fix:** pass tool result content through as string in OpenAI/Claude -> Gemini translation ([#11624](https://github.com/diegosouzapw/OmniRoute/pull/11624)) — thanks @Hsia97 +- **fix(sse):** scope credential health to exact key ([#11628](https://github.com/diegosouzapw/OmniRoute/pull/11628)) — thanks @KaspaPulse +- **fix(install):** guard prepare script against missing husky ([#11634](https://github.com/diegosouzapw/OmniRoute/pull/11634)) — thanks @Prajeeth-12 +- **fix(security):** redact NVIDIA_API_KEY at the log sink in the ad-hoc diag script ([#11639](https://github.com/diegosouzapw/OmniRoute/pull/11639)) +- **fix(types):** clear the two type regressions red on release/v3.8.51 ([#11640](https://github.com/diegosouzapw/OmniRoute/pull/11640)) — thanks @jonlwheat2-gif +- **fix(build):** resolve workspace: protocol dependencies to concrete semver versions during prepublish (#11540) ([#11641](https://github.com/diegosouzapw/OmniRoute/pull/11641)) — thanks @HouMinXi +- **fix(search):** properly resolve configured search connection in /v1/responses pipeline (#11524) ([#11642](https://github.com/diegosouzapw/OmniRoute/pull/11642)) — thanks @HouMinXi +- **fix(authz):** trust cf-connecting-ip for via-proxy detection when peer is Cloudflare edge (#11514) ([#11643](https://github.com/diegosouzapw/OmniRoute/pull/11643)) — thanks @HouMinXi +- **fix(security):** compare the OIDC state cookie in constant time ([#11646](https://github.com/diegosouzapw/OmniRoute/pull/11646)) +- **fix(security):** namespace the dedup hash by the calling API key ([#11649](https://github.com/diegosouzapw/OmniRoute/pull/11649)) +- **fix(models):** gate the Codex-native catalog loop on prefix mode (#11632) ([#11662](https://github.com/diegosouzapw/OmniRoute/pull/11662)) — thanks @xiaoyaner0201 +- **fix(dashboard):** project Codex routing quota in account pools ([#11666](https://github.com/diegosouzapw/OmniRoute/pull/11666)) — thanks @KaspaPulse +- **fix(ddgw):** normaliza system no upstream da release v3.8.51 ([#11692](https://github.com/diegosouzapw/OmniRoute/pull/11692)) — thanks @RhianB14 +- **fix(memory):** honor category filter in GET /api/memory ([#11699](https://github.com/diegosouzapw/OmniRoute/pull/11699)) — thanks @Sabeekhann +- **fix(ci):** detect stale workspace lockfile entries ([#11714](https://github.com/diegosouzapw/OmniRoute/pull/11714)) — thanks @pacocartones +- **fix(ci):** stamp dist/BUILD_SHA before the npm publish provenance gate ([#11721](https://github.com/diegosouzapw/OmniRoute/pull/11721)) +- **fix(docker):** decouple the best-effort Bun image from the release manifest ([#11724](https://github.com/diegosouzapw/OmniRoute/pull/11724)) +- **perf(compression):** avoid cloning memo misses twice ([#11727](https://github.com/diegosouzapw/OmniRoute/pull/11727)) — thanks @RaviTharuma +- **fix(docker):** let the bun digest artifact be absent, not fatal ([#11740](https://github.com/diegosouzapw/OmniRoute/pull/11740)) +- **fix(shared):** block cloud-metadata hosts under default remote-image guard ([#11755](https://github.com/diegosouzapw/OmniRoute/pull/11755)) — thanks @santosraju99-hub +- **fix(api):** preserve caller-provided X-Correlation-Id on chat completions ([#11760](https://github.com/diegosouzapw/OmniRoute/pull/11760)) — thanks @aniruddhaadak80 +- **fix(resilience):** lock permanently retired models instead of short backoff (Gemini ban prevention) ([#11762](https://github.com/diegosouzapw/OmniRoute/pull/11762)) — thanks @turbolego +- **fix:** mark Vercel AI Gateway as passthroughModels ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **fix(resilience):** stop hammering permanently-moved endpoints and billing-suspended accounts ([#11774](https://github.com/diegosouzapw/OmniRoute/pull/11774)) — thanks @turbolego +- **fix(lease):** remove global static reservation and gate routing on live active lease occupancy ([#11775](https://github.com/diegosouzapw/OmniRoute/pull/11775)) — thanks @TheDemonTuan +- **fix(ci):** size the install-upgrade gate to a measured run, and log the pack cost ([#11776](https://github.com/diegosouzapw/OmniRoute/pull/11776)) +- **fix(pwa):** stop serving the stale shell after deploys ([#11779](https://github.com/diegosouzapw/OmniRoute/pull/11779)) — thanks @HouMinXi +- **fix(combo):** lock GitHub models rejected as "not supported" for future requests ([#11781](https://github.com/diegosouzapw/OmniRoute/pull/11781)) — thanks @turbolego +- **fix(cli):** probe both IPv4 and IPv6 loopback for server readiness (#11766) ([#11794](https://github.com/diegosouzapw/OmniRoute/pull/11794)) — thanks @vermasomesh835 +- **fix(sse):** preserve CLIProxyAPI wrappers per connection ([#11797](https://github.com/diegosouzapw/OmniRoute/pull/11797)) — thanks @RaviTharuma +- **fix(providers):** honor model auto-fetch opt-in on create ([#11798](https://github.com/diegosouzapw/OmniRoute/pull/11798)) — thanks @RaviTharuma +- **fix(dashboard):** make model auto-fetch order independent ([#11805](https://github.com/diegosouzapw/OmniRoute/pull/11805)) — thanks @RaviTharuma +- **fix(docker):** make GHA cache exports best effort ([#11806](https://github.com/diegosouzapw/OmniRoute/pull/11806)) — thanks @RaviTharuma +- **fix(kiro):** do not permanently ban on 'User is not authorized to make this call' ([#11809](https://github.com/diegosouzapw/OmniRoute/pull/11809)) — thanks @Deftera186 +- **fix(sse):** keep cache-write tokens in OpenAI-shaped usage ([#11814](https://github.com/diegosouzapw/OmniRoute/pull/11814)) — thanks @TheDemonTuan +- **fix(docs):** resolve relative markdown and wiki links across Fumadocs and GitHub wiki ([#11834](https://github.com/diegosouzapw/OmniRoute/pull/11834)) — thanks @AndrianBalanescu +- **fix(chat):** preserve unstripped model string for passthrough provider routing ([#11840](https://github.com/diegosouzapw/OmniRoute/pull/11840)) — thanks @raheemuddin786 +- **fix(mcp):** avoid async initialization inside sync esm wrappers (#11569) ([#11849](https://github.com/diegosouzapw/OmniRoute/pull/11849)) — thanks @HouMinXi +- **fix(sse):** preserve coherent markdown token boundaries across stream translation chunks (#11606) ([#11851](https://github.com/diegosouzapw/OmniRoute/pull/11851)) — thanks @HouMinXi +- **fix(ci):** run the install-upgrade gate on disk, not on the /tmp tmpfs ([#11855](https://github.com/diegosouzapw/OmniRoute/pull/11855)) +- **fix(skills):** expand shorthand property types in injected tool schemas ([#11857](https://github.com/diegosouzapw/OmniRoute/pull/11857)) — thanks @alvinveroy +- **fix(ci):** publish npm from a hosted runner so provenance is accepted ([#11877](https://github.com/diegosouzapw/OmniRoute/pull/11877)) +- **fix(lint):** pin react hooks plugin to 7.0.1 ([#11886](https://github.com/diegosouzapw/OmniRoute/pull/11886)) +- **fix(ci):** surface and clear release-green ESLint failure ([#11890](https://github.com/diegosouzapw/OmniRoute/pull/11890)) — thanks @MumuTW +- **fix(guardrails):** prevent duplicate prompt-injection-guard log output ([#11936](https://github.com/diegosouzapw/OmniRoute/pull/11936)) — thanks @patrykkopycinski +- **fix(auth):** downgrade expected transient states from warn to debug ([#11937](https://github.com/diegosouzapw/OmniRoute/pull/11937)) — thanks @patrykkopycinski +- **fix(codex):** fail over image generation for imported free plans ([#11948](https://github.com/diegosouzapw/OmniRoute/pull/11948)) — thanks @Bl0ck154 +- **fix(codex):** normalize non-stream responses ([#11951](https://github.com/diegosouzapw/OmniRoute/pull/11951)) — thanks @Bl0ck154 +- **fix(images):** forward Antigravity image size ([#11952](https://github.com/diegosouzapw/OmniRoute/pull/11952)) — thanks @Bl0ck154 +- **fix(db):** invalidate connection cache after upsert ([#11953](https://github.com/diegosouzapw/OmniRoute/pull/11953)) — thanks @Bl0ck154 +- **fix(codex):** restore imported account state ([#11954](https://github.com/diegosouzapw/OmniRoute/pull/11954)) — thanks @Bl0ck154 +- **fix(sse):** give extended-thinking targets the reasoning readiness budget ([#11959](https://github.com/diegosouzapw/OmniRoute/pull/11959)) — thanks @abhisheksharma2411 +- **fix(dashboard):** use opaque background and readable text color on cost chart tooltips ([#11960](https://github.com/diegosouzapw/OmniRoute/pull/11960)) — thanks @ZaimMarzuki +- **fix(antigravity):** send complete loadCodeAssist metadata (ideType/platform/pluginType as numeric enums) ([#11969](https://github.com/diegosouzapw/OmniRoute/pull/11969)) — thanks @rifqiawl +- **fix(core):** resolve DB init race condition and reasoning translation ([#12003](https://github.com/diegosouzapw/OmniRoute/pull/12003)) — thanks @raheemuddin786 +- **fix(admission):** exclude reclaimable page cache from the cgroup pressure ratio ([#12017](https://github.com/diegosouzapw/OmniRoute/pull/12017)) — thanks @HouMinXi +- **fix(build):** prune non-production trees in NFT trace excludes and tsconfig ([#12028](https://github.com/diegosouzapw/OmniRoute/pull/12028)) — thanks @Chewji9875 +- **fix(migrations):** add renamed migration compatibility for 056/073/077/101 ([#12036](https://github.com/diegosouzapw/OmniRoute/pull/12036)) — thanks @oyi77 +- **fix(sse):** default crash-guard logger to console.warn, not console ([#12042](https://github.com/diegosouzapw/OmniRoute/pull/12042)) — thanks @HouMinXi +- **fix(catalog):** derive vision/modalities for built-in auto combos from effective target pool ([#12046](https://github.com/diegosouzapw/OmniRoute/pull/12046)) — thanks @Prajeeth-12 +- **fix(release):** the packaged-app smoke verifies the database opened, not a driver line the primary path never prints (twin of #12032) ([#12047](https://github.com/diegosouzapw/OmniRoute/pull/12047)) +- **fix(barrel):** migrate src/app/ off the @/lib/localDb barrel import (#11795 Phase 2) ([#12051](https://github.com/diegosouzapw/OmniRoute/pull/12051)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate src/lib/ off the localDb barrel to direct db imports (#59) ([#12052](https://github.com/diegosouzapw/OmniRoute/pull/12052)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate open-sse, src/shared, src/sse, src/models, src/domain off the @/lib/localDb barrel import (#11795 Phase 4) ([#12053](https://github.com/diegosouzapw/OmniRoute/pull/12053)) — thanks @jonlwheat2-gif +- **fix(barrel):** delete the @/lib/localDb barrel — every consumer migrated (#11795 Phase 5) ([#12055](https://github.com/diegosouzapw/OmniRoute/pull/12055)) — thanks @jonlwheat2-gif +- **fix(radar):** restore D12 public boundary ([#12057](https://github.com/diegosouzapw/OmniRoute/pull/12057)) +- **fix(dev):** bound webpack and Tailwind scans ([#12075](https://github.com/diegosouzapw/OmniRoute/pull/12075)) — thanks @backryun +- **fix(dev):** isolate root layout settings reads ([#12076](https://github.com/diegosouzapw/OmniRoute/pull/12076)) — thanks @backryun +- **fix(dev):** reduce instrumentation executor fan-out (phase 3) ([#12078](https://github.com/diegosouzapw/OmniRoute/pull/12078)) — thanks @backryun +- **fix(dev):** make logging resources HMR-singleton ([#12079](https://github.com/diegosouzapw/OmniRoute/pull/12079)) — thanks @backryun +- **fix(sse):** keep unavailable forced connections scoped ([#12080](https://github.com/diegosouzapw/OmniRoute/pull/12080)) — thanks @keeltrace +- **fix(dev):** isolate batch dispatch from instrumentation ([#12081](https://github.com/diegosouzapw/OmniRoute/pull/12081)) — thanks @backryun +- **fix(ci):** reconcile release test contract drift ([#12082](https://github.com/diegosouzapw/OmniRoute/pull/12082)) — thanks @backryun +- **fix(ci):** accept CVE-2025-68121 in the prebuilt tls-client .so, auto-close base-red issues, guard Scorecard on the default branch ([#12085](https://github.com/diegosouzapw/OmniRoute/pull/12085)) +- **fix(db):** drop three consumer-less 1proxy exports — dead-code base-red on release/v3.8.51 after the barrel deletion (#12055) ([#12087](https://github.com/diegosouzapw/OmniRoute/pull/12087)) +- **fix(api):** clear the six API-route TypeScript regressions the new gate landed red on ([#12094](https://github.com/diegosouzapw/OmniRoute/pull/12094)) +- **fix(release):** never let the tag-push Create Release append auto notes to the curated body ([#12096](https://github.com/diegosouzapw/OmniRoute/pull/12096)) +- **fix(sse):** honor CLIProxyAPI environment API key ([#12099](https://github.com/diegosouzapw/OmniRoute/pull/12099)) — thanks @RaviTharuma +- **fix(oauth):** bind Google refresh to the client that issued the token ([#12106](https://github.com/diegosouzapw/OmniRoute/pull/12106)) — thanks @HouMinXi +- **fix(dashboard):** make RequestLoggerDetail loadable outside Next — CSS via globals.css + CJS/ESM interop (#11703 base-reds) ([#12114](https://github.com/diegosouzapw/OmniRoute/pull/12114)) +- **fix(leases):** project status lease row to lease columns so joined connection PII never escapes ([#12115](https://github.com/diegosouzapw/OmniRoute/pull/12115)) — thanks @geek007git +- **fix(plugins):** do not kill the plugin process when a fire-and-forget hook times out ([#12116](https://github.com/diegosouzapw/OmniRoute/pull/12116)) — thanks @geek007git +- **fix(guardrails):** restore injection-guard logging on middleware-only routes ([#12117](https://github.com/diegosouzapw/OmniRoute/pull/12117)) — thanks @geek007git +- **fix(plugins):** refresh stored manifest from disk on activate so new hook fields reach existing installs ([#12120](https://github.com/diegosouzapw/OmniRoute/pull/12120)) — thanks @geek007git +- **fix(codex):** preserve existing provider state when bulk-import upserts a matching connection ([#12122](https://github.com/diegosouzapw/OmniRoute/pull/12122)) — thanks @geek007git +- **fix(ci):** clear the two base-reds the 2026-08-30 merge batch left on release/v3.8.51 (round 3) ([#12123](https://github.com/diegosouzapw/OmniRoute/pull/12123)) +- **fix(sse):** strip type:'custom' from Claude tools on agentrouter dispatch ([#12126](https://github.com/diegosouzapw/OmniRoute/pull/12126)) — thanks @wahidsadik371-coder +- **fix(sse):** stop advertising video providers the dispatcher cannot run ([#12131](https://github.com/diegosouzapw/OmniRoute/pull/12131)) — thanks @quiterunner-commits +- **fix(search):** treat HTTP 432 and plan limit errors as transient cooldown ([#12139](https://github.com/diegosouzapw/OmniRoute/pull/12139)) — thanks @HouMinXi +- **fix(sse):** refuse an AI Horde queue that cannot fit the request budget ([#12143](https://github.com/diegosouzapw/OmniRoute/pull/12143)) — thanks @quiterunner-commits +- **fix(ci):** clear the base-reds the afternoon merge batch left on release/v3.8.51 (round 5: provider count 352, TS2554/TS2677) ([#12144](https://github.com/diegosouzapw/OmniRoute/pull/12144)) +- **fix(translator):** strip neutral tool_choice when tools absent in Responses-to-Chat (#12141) ([#12166](https://github.com/diegosouzapw/OmniRoute/pull/12166)) — thanks @HouMinXi +- **fix(guardrails):** pass providerId to getResolvedModelCapabilities in checkComboVision (#12112) ([#12169](https://github.com/diegosouzapw/OmniRoute/pull/12169)) — thanks @HouMinXi +- **fix(translator):** strip plaintext reasoning content for opaque responses backends (#12128) ([#12171](https://github.com/diegosouzapw/OmniRoute/pull/12171)) — thanks @HouMinXi +- **perf(stream):** compile hot-path regexes once, bound token caches, fix quadratic buffering ([#12179](https://github.com/diegosouzapw/OmniRoute/pull/12179)) — thanks @opensource-elearning +- **fix:** resolve compression worker file using runtime anchors instead of… ([#12183](https://github.com/diegosouzapw/OmniRoute/pull/12183)) — thanks @NightStalker-87 +- **fix:** prevent Claude Code session kills via liveness-aware readiness + auto model echo ([#12189](https://github.com/diegosouzapw/OmniRoute/pull/12189)) — thanks @opensource-elearning +- **fix(translator):** preserve falsy primitive values in Gemini and Antigravity function response results ([#12191](https://github.com/diegosouzapw/OmniRoute/pull/12191)) — thanks @Siva010 +- **fix(oauth):** mark empty Antigravity projectId as degraded and clear stale errors (#11284) ([#12205](https://github.com/diegosouzapw/OmniRoute/pull/12205)) — thanks @HouMinXi +- **fix(providers):** gate the Codex auto-ping usage read on the shared quota throttle ([#12209](https://github.com/diegosouzapw/OmniRoute/pull/12209)) — thanks @abhisheksharma2411 +- **fix(combos):** prioritize SQLite row id over inner JSON id and notify delete errors ([#12213](https://github.com/diegosouzapw/OmniRoute/pull/12213)) — thanks @HouMinXi +- **fix(providers):** mark featherless.ai as no longer having a free tier (`hasFree: false`) ([#12216](https://github.com/diegosouzapw/OmniRoute/pull/12216)) — thanks @turbolego +- **fix(deepseek-web):** stop Turbopack dev panic in the PoW worker path resolver ([#12221](https://github.com/diegosouzapw/OmniRoute/pull/12221)) — thanks @hartmark +- **fix(chat-admission):** clarify local 503 source ([#12223](https://github.com/diegosouzapw/OmniRoute/pull/12223)) — thanks @keeltrace +- **fix(dev):** silence webpack runtime module warnings ([#12228](https://github.com/diegosouzapw/OmniRoute/pull/12228)) — thanks @backryun +- **fix(db):** use module.require for CommonJS runtime driver loading ([#12230](https://github.com/diegosouzapw/OmniRoute/pull/12230)) — thanks @raheemuddin786 +- **fix(memory):** honest probe-driven FTS5 keyword status + memory_id rowid sync ([#12231](https://github.com/diegosouzapw/OmniRoute/pull/12231)) — thanks @raheemuddin786 +- **fix(system):** propagate abort signal to stream reader in HTTP version checks ([#12232](https://github.com/diegosouzapw/OmniRoute/pull/12232)) — thanks @raheemuddin786 +- **fix(resilience):** isolate local host execution errors from provider circuit breakers ([#12233](https://github.com/diegosouzapw/OmniRoute/pull/12233)) — thanks @raheemuddin786 +- **fix(sse):** sort injected tools deterministically for prompt caching ([#12234](https://github.com/diegosouzapw/OmniRoute/pull/12234)) — thanks @raheemuddin786 +- **fix(combo):** always clear the loop-safety timer, not just on the happy path (#11804) ([#12245](https://github.com/diegosouzapw/OmniRoute/pull/12245)) +- **fix(quality):** base-red pair — stryker tap registration + turn-pin suites aligned to the window gate ([#12255](https://github.com/diegosouzapw/OmniRoute/pull/12255)) — thanks @backryun +- **fix(dev):** qualify Turbopack runtime boundaries ([#12258](https://github.com/diegosouzapw/OmniRoute/pull/12258)) — thanks @backryun +- **fix(combo):** fall back to full pool when collapsed sole survivor is context-too-small ([#12278](https://github.com/diegosouzapw/OmniRoute/pull/12278)) — thanks @opensource-elearning +- **fix(combo):** bound the pre-dispatch unavailable skip so a stale label cannot dark a pool (#12168) ([#12285](https://github.com/diegosouzapw/OmniRoute/pull/12285)) +- **fix(security):** strip Qwen/Alibaba console-session cookies from provider API responses ([#12287](https://github.com/diegosouzapw/OmniRoute/pull/12287)) +- **fix(usage):** console-aware Token Plan guidance + subscription hint on bailian 401 ([#12288](https://github.com/diegosouzapw/OmniRoute/pull/12288)) +- **fix(resourcePressure):** log numeric detail on every rejection, recover faster ([#12293](https://github.com/diegosouzapw/OmniRoute/pull/12293)) — thanks @hartmark +- **fix(release):** drain the 2026-09-01 base-red window — passthrough usage regression + radarPage i18n keys ([#12327](https://github.com/diegosouzapw/OmniRoute/pull/12327)) +- **fix(ci):** openapi-security-tiers checker must honor routeGuard patterns + imported prefixes ([#12350](https://github.com/diegosouzapw/OmniRoute/pull/12350)) — thanks @ggiak +- **perf(ci):** cache node_modules in the npm-ci-retry composite (#8084 D3) ([#12408](https://github.com/diegosouzapw/OmniRoute/pull/12408)) +- **revert(ui):** point CTAs back at their real destinations ([#12410](https://github.com/diegosouzapw/OmniRoute/pull/12410)) +- **fix(release):** drain the 2026-09-02 base-red — rerank-providers import + api-typecheck baseline ratchet ([#12414](https://github.com/diegosouzapw/OmniRoute/pull/12414)) +- **fix(types):** make system prompt injection noImplicitAny-safe ([#12416](https://github.com/diegosouzapw/OmniRoute/pull/12416)) +- **fix(quality):** validate typecheck baseline schema ([#12419](https://github.com/diegosouzapw/OmniRoute/pull/12419)) +- **fix(memory):** resolve rerank provider node cache import ([#12421](https://github.com/diegosouzapw/OmniRoute/pull/12421)) +- **fix(ci):** repair release-root regressions — pack dedup, web-session syntax, uc-image ids ([#12423](https://github.com/diegosouzapw/OmniRoute/pull/12423)) — thanks @backryun +- **fix(providers):** separate MaxAI and UC credential contracts ([#12431](https://github.com/diegosouzapw/OmniRoute/pull/12431)) +- **fix(cli):** preserve Claude settings on config set ([#12432](https://github.com/diegosouzapw/OmniRoute/pull/12432)) — thanks @KrzysiekSko +- **fix(providers):** repair the maxai credential block truncated by merge auto-resolve ([#12433](https://github.com/diegosouzapw/OmniRoute/pull/12433)) +- **fix(quality):** record the 2026-09-02 merged growth in the file-size baseline ([#12434](https://github.com/diegosouzapw/OmniRoute/pull/12434)) +- **fix(uc):** route UC error strings through sanitizeErrorMessage; allowlist the retired codex id ([#12437](https://github.com/diegosouzapw/OmniRoute/pull/12437)) +- **fix(adapta):** redact streamed upstream errors ([#12438](https://github.com/diegosouzapw/OmniRoute/pull/12438)) +- **fix(sse):** remap non-contiguous upstream tool_calls index to a gap-free output_index ([#12445](https://github.com/diegosouzapw/OmniRoute/pull/12445)) — thanks @hartmark +- **fix(quality):** detect duplicate tool_calls entries in one response ([#12446](https://github.com/diegosouzapw/OmniRoute/pull/12446)) — thanks @hartmark +- **fix(conversations):** resolve turn content OmniRoute never sends back to the client ([#12447](https://github.com/diegosouzapw/OmniRoute/pull/12447)) — thanks @hartmark +- **fix(combo):** do not treat credits-exhausted 401 as auth skip ([#12449](https://github.com/diegosouzapw/OmniRoute/pull/12449)) — thanks @RaviTharuma +- **fix(auth):** do not park healthy quota accounts as expired ([#12452](https://github.com/diegosouzapw/OmniRoute/pull/12452)) — thanks @RaviTharuma +- **fix(responses-continuation):** fail closed on a collector-truncated, empty output array ([#12460](https://github.com/diegosouzapw/OmniRoute/pull/12460)) — thanks @hartmark +- **fix(logging):** raise the SSE payload collector's default cap ([#12461](https://github.com/diegosouzapw/OmniRoute/pull/12461)) — thanks @hartmark +- **fix(resilience):** surface Responses failed.error.message in 502s ([#12472](https://github.com/diegosouzapw/OmniRoute/pull/12472)) — thanks @RaviTharuma +- **fix(security):** unbiased maxai X-Random nonce + stricter URL/regex assertions ([#12502](https://github.com/diegosouzapw/OmniRoute/pull/12502)) +- **fix(video):** re-anchor transcript log-redaction so PII/credential maskers can't reopen the leak (#12150 P1 follow-up) ([#12503](https://github.com/diegosouzapw/OmniRoute/pull/12503)) +- **fix(monitoring):** serve cached credentialHealth off the request path ([#12533](https://github.com/diegosouzapw/OmniRoute/pull/12533)) — thanks @RaviTharuma +- **fix(quota):** keep Antigravity Gemini usable when Claude weekly is empty ([#12566](https://github.com/diegosouzapw/OmniRoute/pull/12566)) — thanks @HouMinXi +- **fix(authz):** hard-gate every credential export and CLI-config write (GHSA-5926-2w35-7h4q) ([#12600](https://github.com/diegosouzapw/OmniRoute/pull/12600)) +- **fix(docs):** restore the Next build — REMOVED_PROVIDERS.md had no frontmatter (base-red #12581) ([#12610](https://github.com/diegosouzapw/OmniRoute/pull/12610)) +- **fix(quality):** re-point the zcodeProtocol public-creds allowlist to line 313 ([#12615](https://github.com/diegosouzapw/OmniRoute/pull/12615)) +- **fix(security):** close 3 advisories — search baseUrl exfil, sk- in the error sanitizer, bifrost relay header leak ([#12620](https://github.com/diegosouzapw/OmniRoute/pull/12620)) +- **fix(providers):** sanitize boolean required and nested bare maps for Gemini (#12269) ([#12624](https://github.com/diegosouzapw/OmniRoute/pull/12624)) — thanks @HouMinXi +- **fix(resilience):** keep Overloaded STREAM_EARLY_EOF off the provider breaker ([#12626](https://github.com/diegosouzapw/OmniRoute/pull/12626)) — thanks @HouMinXi +- **fix(catalog):** re-audit free-tier quotas against official pages ([#12649](https://github.com/diegosouzapw/OmniRoute/pull/12649)) +- **fix(dashboard):** keep a request's pending-tracking id stable across combo target retries ([#12650](https://github.com/diegosouzapw/OmniRoute/pull/12650)) — thanks @hartmark +- **fix(tests):** retire dead model ids from the chat-pipeline integration suite (base-red #12581) ([#12670](https://github.com/diegosouzapw/OmniRoute/pull/12670)) +- **fix:** resolve SqliteError no such table compression_run_telemetry during cleanup ([#12682](https://github.com/diegosouzapw/OmniRoute/pull/12682)) — thanks @groovecityJO +- **fix(combo):** treat a pin-only step as implicit connection allowlist ([#12697](https://github.com/diegosouzapw/OmniRoute/pull/12697)) — thanks @HouMinXi +- **fix(ci):** ship .npmrc in published package so legacy-peer-deps applies to consumers (#11544) ([#12699](https://github.com/diegosouzapw/OmniRoute/pull/12699)) — thanks @KooshaPari +- **fix(docker):** pin 4 CLI tools to exact versions (#12576) ([#12703](https://github.com/diegosouzapw/OmniRoute/pull/12703)) — thanks @KooshaPari +- **fix(docker):** document and harden cli profile trust boundary (#12570) ([#12706](https://github.com/diegosouzapw/OmniRoute/pull/12706)) — thanks @KooshaPari +- **fix(sse):** redact video transcript in pre-guardrail rejected-request logs (#12150 P2 item 7) ([#12710](https://github.com/diegosouzapw/OmniRoute/pull/12710)) +- **fix(i18n):** quote placeholder in OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES description (#12505) ([#12769](https://github.com/diegosouzapw/OmniRoute/pull/12769)) — thanks @KooshaPari +- **fix(api/pricing):** surface validation error message as string, not raw object (#12494) ([#12771](https://github.com/diegosouzapw/OmniRoute/pull/12771)) — thanks @KooshaPari +- **fix(sse):** re-enable prompt compression for native Codex passthrough ([#12834](https://github.com/diegosouzapw/OmniRoute/pull/12834)) — thanks @thomasmaerz +- **fix(dashboard):** model health tests for a provider node set to the Responses API now call `/v1/responses` with a Responses-shaped body instead of `/v1/chat/completions` — those models were reported as `Provider returned HTTP 200 but no text content` even though the same model answered normally through `/v1/responses` ([#13070](https://github.com/diegosouzapw/OmniRoute/issues/13070)) ([#13087](https://github.com/diegosouzapw/OmniRoute/pull/13087)) — thanks @ntdatt812 +- fix(routing): stop a round-robin combo's "opencode" targets from collapsing onto the opencode-zen connection (#11912) ([#13283](https://github.com/diegosouzapw/OmniRoute/pull/13283)) +- fix(routing): stop the reactive-compaction debug log from lying when compression is globally disabled (#11977) ([#13276](https://github.com/diegosouzapw/OmniRoute/pull/13276)) +- fix(electron): relativize standalone-bundle symlink targets so Stage 8 manifest verification stops failing on Windows (#11979) ([#13251](https://github.com/diegosouzapw/OmniRoute/pull/13251)) +- fix(dashboard): surface a visible error when Compression Studio's combined preview run fails (#12061) ([#13288](https://github.com/diegosouzapw/OmniRoute/pull/13288)) +- fix(dashboard): make the compression "Effective pipeline" preview honor the active profile and warn when the master switch is off (#12063) ([#13287](https://github.com/diegosouzapw/OmniRoute/pull/13287)) +- fix(providers): scope TinyCMS Web signer's DOM shims to each call instead of leaking them for the process lifetime, and surface a clean HTTP status on a non-JSON interception-toggles error (#12072) ([#13254](https://github.com/diegosouzapw/OmniRoute/pull/13254)) +- fix(guardrails): stop Vision Bridge from re-selecting a model locked after a 404 (#12111) ([#13259](https://github.com/diegosouzapw/OmniRoute/pull/13259)) +- fix(sse): require Responses-shaped body before native OpenAI-compatible passthrough (#12129) ([#13278](https://github.com/diegosouzapw/OmniRoute/pull/13278)) +- fix(providers): minimax-m3 now collapses manual thinking.type:"enabled" to adaptive, preventing upstream 400 (2013) (#12132) ([#13249](https://github.com/diegosouzapw/OmniRoute/pull/13249)) +- fix(db): scope model visibility overrides by modality so hiding a Chat model no longer hides an identically-ID'd Image/Embeddings/etc. model (#12172) ([#13258](https://github.com/diegosouzapw/OmniRoute/pull/13258)) +- fix(db): scope local-provider apiKey dedup to matching base URL so LM Studio/Ollama-style connections support multiple accounts (#12173) ([#13250](https://github.com/diegosouzapw/OmniRoute/pull/13250)) +- fix(providers): refresh Trae's stale Referer/Origin and forward user timezone so imported connections stop failing with 401 (#12190) ([#13255](https://github.com/diegosouzapw/OmniRoute/pull/13255)) +- fix(providers): route opencode-go/gpt-5.6-luna to /responses instead of /chat/completions (#12196) ([#13275](https://github.com/diegosouzapw/OmniRoute/pull/13275)) +- fix(dashboard): allow deleting the last extra-upstream-header row even when invalid (#12251) ([#13252](https://github.com/diegosouzapw/OmniRoute/pull/13252)) +- **fix(i18n):** translate pre-existing `__MISSING__:` keys for `combo.sort`, `requestLogger.detail` expand/collapse, `common.profile`, and `settings.resilienceCredentialHealth*` across 39 locales ([#12272](https://github.com/diegosouzapw/OmniRoute/issues/12272)) ([#13034](https://github.com/diegosouzapw/OmniRoute/pull/13034)) — thanks @HouMinXi +- fix(cli): run the Node.js runtime compatibility guard before the heavy `tsx/esm` + Commander import chain so an unsupported runtime gets a clear message instead of a raw `Invalid regular expression flags` crash (#12296) ([#13253](https://github.com/diegosouzapw/OmniRoute/pull/13253)) +- fix(dashboard): refresh the providers list after deleting a compatible provider node (#12298) ([#13256](https://github.com/diegosouzapw/OmniRoute/pull/13256)) +- fix(usage): fail closed on API-key budget enforcement when a provider's `auto` routing alias has no pricing row, instead of silently counting it as $0 (#12341) ([#13257](https://github.com/diegosouzapw/OmniRoute/pull/13257)) +- **fix(providers):** include the submitted Agnes video model when polling by `video_id` ([#12356](https://github.com/diegosouzapw/OmniRoute/pull/12356)) — thanks @pacocartones +- **fix(routing):** custom OpenAI-compatible nodes now honor the saved Chat/Responses API type after edits instead of letting the node's original ID prefix override the live connection setting ([#11884](https://github.com/diegosouzapw/OmniRoute/issues/11884)). ([#12358](https://github.com/diegosouzapw/OmniRoute/pull/12358)) — thanks @pacocartones +- fix(sse): surface an error instead of a silent empty 200 when a Claude stream closes with zero bytes (#12398) ([#13285](https://github.com/diegosouzapw/OmniRoute/pull/13285)) +- fix(oauth): warn before the dead localhost:8080 redirect in antigravity/gemini `oauth start` (#12413) ([#13265](https://github.com/diegosouzapw/OmniRoute/pull/13265)) +- fix(providers): stop devin-cli spawn error from double-closing the SSE controller (#12517) ([#13261](https://github.com/diegosouzapw/OmniRoute/pull/13261)) +- **fix(rerank):** clamp Voyage `top_k` to the documents actually sent after empty-string filtering, and honor `return_documents: false` in the NVIDIA response adapter (#12523 — thanks @pacocartones) +- **fix(audio):** `/v1/audio/translations` now resolves combo names the way `/v1/audio/transcriptions` already does, so a combo that `GET /v1/models` advertises is fanned out to its targets instead of being rejected with `400 Invalid translation model: . Use format: provider/model`; literal `provider/model` ids and unknown bare names behave as before (#12536 — thanks @pacocartones) +- **fix(gemini):** strip the JSON-Schema-2020-12 `prefixItems` keyword from Gemini tool schemas at every nesting level, so Claude Code tool definitions no longer fail with `400 Unknown name "prefixItems"` on Gemini models (#12540 — thanks @pacocartones) +- **fix(api-manager):** Expose an accessible loading status while API keys are fetched instead of an empty accessibility tree (#12541 — thanks @pacocartones) +- **fix(video):** Clamp `estimateJpegFrameBytes` at zero for padding-only payloads and build the three encode-side frame data URIs from `JPEG_FRAME_DATA_URI_PREFIX` instead of a repeated literal (#12543 — thanks @pacocartones) +- **fix(devin):** accept Windows `DEVIN_AGENTIC_HOME` sandbox paths (`C:\...\.sandbox\...`) in the isolated-home check so the Devin Claude Bridge no longer fails closed on Windows ([#12405](https://github.com/diegosouzapw/OmniRoute/issues/12405)) (#12545 — thanks @pacocartones) +- **fix(db):** Add `conversation_turn_nodes` and orphaned `agentic_conversations` to the auto-cleanup cycle under the existing `retention.callLogs` window, so identity nodes whose call-log content has already been purged no longer accumulate without bound in `storage.sqlite` (#12548 — thanks @pacocartones) +- **fix(i18n):** Wrap the `~/.claude/profiles//settings.json` placeholder in ICU single quotes in the `OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES` feature-flag description across all 42 locales and the TypeScript default, so next-intl no longer fails with `INVALID_MESSAGE: UNCLOSED_TAG` and the Feature Flags card shows the description instead of the raw key (#12549 — thanks @pacocartones) +- **fix(orchestration):** `updateCloudAgentTask` now publishes the task's real `status` on `agent.task.updated` when an update only touches `result`, `activities` or `error`, instead of the fabricated `"updated"` state, and stays silent when no row matched the id (#12550 — thanks @pacocartones) +- **fix(i18n):** the home "Recent Requests" panel and the Provider Topology legend are now translated instead of rendering English copies on non-English dashboards; the legend reads its own `home.topologyLegend*` labels with consistent casing rather than borrowing the memory-settings "Recent" and analytics "Error" strings (#12551 — thanks @pacocartones). +- **docs(reference):** bring the `FEATURE_FLAGS.md` catalog back to 1:1 with `featureFlagDefinitions.ts` — 20 missing flags added, the two `*_BLOCK_THRESHOLD` env-only knobs moved out of the flag tables, category/total counts and the Live WS port corrected, guarded by a static test (#12552 — thanks @pacocartones) +- **fix(i18n):** backfill missing `usage.kiloPass*` strings in 39 locales and restore `featureFlagOmnirouteDisableThinkingLevelVariantsDescription` in `pt.json` ([#12561](https://github.com/diegosouzapw/OmniRoute/issues/12561)) ([#13027](https://github.com/diegosouzapw/OmniRoute/pull/13027)) — thanks @HouMinXi +- fix(docker): default docker-compose app ports (dashboard/API/live-WS) to loopback instead of `0.0.0.0`, closing the anonymous `/v1` LAN/WAN exposure gap left open by `REQUIRE_API_KEY=false` (#12568) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): close DNS-rebinding SSRF gap in webhook outbound-URL guard (#12569) ([#13243](https://github.com/diegosouzapw/OmniRoute/pull/13243)) +- fix(docker): require a per-session token on the VNC browser CDP bridge and isolate it on a dedicated Docker network (#12571) ([#13241](https://github.com/diegosouzapw/OmniRoute/pull/13241)) +- fix(open-sse): write Adobe Firefly session tokens and cookie jars with 0700/0600 permissions instead of the process umask (#12572) ([#13242](https://github.com/diegosouzapw/OmniRoute/pull/13242)) +- fix(api): remove hardcoded wildcard CORS in openai-to-gemini-sse.ts so the centralized fail-closed CORS gate is the sole source of `Access-Control-Allow-Origin` (#12573) ([#13239](https://github.com/diegosouzapw/OmniRoute/pull/13239)) +- fix(api): enforce API key policy (budget/rate-limit/schedule/endpoint scoping) on the ElevenLabs speech-to-text, text-to-speech and voices proxy routes (#12574) ([#13244](https://github.com/diegosouzapw/OmniRoute/pull/13244)) +- fix(sse): cap HuggingChat NDJSON body size and bound the read loop with the fetch timeout so a stalled or hostile upstream cannot buffer unbounded memory (#12577) ([#13282](https://github.com/diegosouzapw/OmniRoute/pull/13282)) +- fix(docker): scope the cliproxyapi/qdrant/bifrost sidecars to loopback by default and forward `CLIPROXYAPI_MANAGEMENT_KEY` into the cliproxyapi container so its management API is not left both unauthenticated and LAN-published (#12578) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): create DB export temp paths with `fs.mkdtempSync` instead of predictable timestamps (#12579) ([#13237](https://github.com/diegosouzapw/OmniRoute/pull/13237)) +- Cline 401 bodies that say "re-authenticate your Cline account" classify as a refreshable OAuth token, not a terminal expired key. The cooling panel no longer labels every cooldown as a 429; it shows the recorded last error instead. (#12594) ([#13060](https://github.com/diegosouzapw/OmniRoute/pull/13060)) — thanks @HouMinXi +- **fix(catalog):** degrade unknown combo targets instead of dropping LCD modalities, and persist OpenRouter `architecture.input_modalities` into the capability snapshot ([#12613](https://github.com/diegosouzapw/OmniRoute/issues/12613)) ([#13042](https://github.com/diegosouzapw/OmniRoute/pull/13042)) — thanks @HouMinXi +- fix(providers): send `x-api-key` instead of `Authorization: Bearer` for OpenCode Zen's `/v1/responses` endpoint (Muse Spark Contributor models), fixing a 401 on OmniRoute's auth header (#12633) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(sse): surface the actionable "Auggie CLI not found" message when the shell reports a missing `auggie` binary via exit code instead of a spawn error (#12645) ([#13263](https://github.com/diegosouzapw/OmniRoute/pull/13263)) +- **fix(config):** Persist deletions of built-in background-degradation entries — when a stored settings record exists its `degradationMap` is now authoritative instead of being merged under the defaults, so an entry the user removed in the dashboard no longer reappears on the next apply or restart ([#12424](https://github.com/diegosouzapw/OmniRoute/issues/12424)) ([#12647](https://github.com/diegosouzapw/OmniRoute/pull/12647)) — thanks @pacocartones +- **fix(gamification):** action-count badge milestones (First Token, Token Consumer, Token Machine, Token Whale, and the token-sharing tier) are now backed by a durable `xp_action_counts` counter incremented in `addXp()`, instead of a live `COUNT(*)` over `xp_audit_log`. The audit log is pruned by `retention.xpAuditLog` (default 30 days), so on a default install those "lifetime" milestones were really "actions in the last 30 days" and unlocked badges could stop unlocking once old rows aged out. `getActionCount()` and `checkActionCountBadges()` now read the same durable source, and a migration backfills existing totals from the surviving audit rows ([#12546](https://github.com/diegosouzapw/OmniRoute/issues/12546)) ([#12651](https://github.com/diegosouzapw/OmniRoute/pull/12651)) — thanks @pacocartones +- **fix(images):** `/v1/images/edits` now iterates a combo's targets the same way `/v1/images/generations` does (#9239) instead of flattening a bare combo to its first target. A combo whose first target is not edit-capable — or lacks credentials — now falls through to a later edit-capable target rather than hard-erroring, and missing credentials are skipped (not a hard `401`) to match the generations path. The per-target skip/terminal classification is extracted into a shared `runImageComboTargets` loop, so generations behavior is unchanged ([#12547](https://github.com/diegosouzapw/OmniRoute/issues/12547)). ([#12653](https://github.com/diegosouzapw/OmniRoute/pull/12653)) — thanks @pacocartones +- fix(sse): add first-byte watchdog to the TLS-fingerprint transport so a stalled wreq body falls back instead of hanging for minutes (#12656) ([#13272](https://github.com/diegosouzapw/OmniRoute/pull/13272)) +- fix(sse): exempt tiny-budget reasoning probes from combo quality failure and surface persisted-cooldown skips in ALL_TARGETS_SKIPPED diagnostics (#12659) ([#13269](https://github.com/diegosouzapw/OmniRoute/pull/13269)) +- fix(models): declare the real ~1M contextLength for OpenCode Zen's Muse Spark 1.2 models instead of falling back to the 200000 provider default (#12681) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(oauth): align codebuddy-cn OAuth User-Agent with the chat/usage CLI version to avoid WAF false positives (#12702) ([#13264](https://github.com/diegosouzapw/OmniRoute/pull/13264)) +- fix(dashboard): surface an authentication-required banner instead of silently blanking database settings for a guest session (#12709) ([#13270](https://github.com/diegosouzapw/OmniRoute/pull/13270)) +- **fix(resilience):** stop unbounded queue that hangs 6min until Aborted — gate, provider slot, and Bottleneck queue now share a per-connection `maxWaitMs` budget; `fail-closed` on exhaust (503); execution backstop `executionMaxWaitMs` overridable per connection with upstream clamp ([#12715](https://github.com/diegosouzapw/OmniRoute/pull/12715)) — thanks @maxmad64bis (with thanks to @Tushar49 for surfacing the slow-provider need in #12635) +- **fix(auto-combo):** every mode pack now carries `quality` and `reliability` weights (`quality` 0.02, 0.03 in `quality-first`; `reliability` 0.03, 0.04 in `reliability-first`), so selecting a pack no longer silences either signal; `DEFAULT_WEIGHTS` is unchanged ([#12731](https://github.com/diegosouzapw/OmniRoute/pull/12731)) — thanks @maxmad64bis +- **fix(sse):** the provider execution pipeline keeps the upstream error `code`/`type` again — both were lost when the non-streaming leg was extracted, so a config-class `422` (Antigravity missing project) degraded into a generic account cooldown instead of reaching the gate that recognises it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(db):** a legacy `call_logs` table no longer aborts startup — the provider-stats index is created after the column healing runs, not before it, so an install predating `request_type` opens instead of failing with `no such column` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(catalog):** a malformed row in the operator-writable custom-models blob no longer kills every `auto/*` pool ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(tests):** realign the guards that the same day's merges left asserting the old behavior — the provider-count total after GreenPT and EURouter, the GLM stream buffer slot that `#12925` turned into a declared parameter, and the injection scan bound that `#13104` reshaped from a head-only window into head-plus-tail ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- fix(cache): fold tool_choice/tools/response_format into the semantic cache signature so a cached tool_calls response can no longer be replayed for a request whose tool policy forbids it (#12734) ([#13267](https://github.com/diegosouzapw/OmniRoute/pull/13267)) +- **fix(models):** stop treating provider-supplied `isFree:true`/`:free`/`0/0` flags as trusted unless the provider has a documented free tier — only the shipped free-tier catalog decides otherwise; fetched `isFree:true` trusted only for a free-tier provider, custom `isFree:true` via a trusted path ([#12744](https://github.com/diegosouzapw/OmniRoute/pull/12744)) — thanks @maxmad64bis +- fix(memory): authenticate the internal /v1/rerank loopback call so memory reranking no longer silently degrades to unranked order when REQUIRE_API_KEY=true (#12745) ([#13268](https://github.com/diegosouzapw/OmniRoute/pull/13268)) +- fix(sse): parse Ollama Cloud's current usage markup (`$X of $Y used` aria-label, nested width style) (#12749) ([#13260](https://github.com/diegosouzapw/OmniRoute/pull/13260)) +- fix(cli): setup-opencode no longer sends an active context's management token to `/v1/models` when `--api-key`/`OMNIROUTE_API_KEY` is supplied — an explicit flag or the env var now always outranks the context's token, and the flag itself is no longer swallowed by the parent program's global `--api-key` option (#12783) ([#13246](https://github.com/diegosouzapw/OmniRoute/pull/13246)) +- fix(sse): register Arcee AI in the executor provider registry so requests reach api.arcee.ai instead of silently falling back to OpenAI (#12784) ([#13277](https://github.com/diegosouzapw/OmniRoute/pull/13277)) +- **fix(quota):** share one upstream quota read when identical saturation checks arrive at the same time, so traffic bursts don't multiply provider API calls ([#12787](https://github.com/diegosouzapw/OmniRoute/pull/12787)) — thanks @maxmad64bis +- **fix(db):** ignore expired or invalid rate-limit cooldown writes so a stale timestamp can't lock a connection that should be usable — clearing still works as before ([#12788](https://github.com/diegosouzapw/OmniRoute/pull/12788)) — thanks @maxmad64bis +- **fix(providers):** single source for provider order with `xao` ranking alongside `xai-oauth` ([#12790](https://github.com/diegosouzapw/OmniRoute/pull/12790)) — thanks @maxmad64bis +- **fix(routing):** off-table models check the free-model catalog before inheriting premium prices, latency bootstraps from the observed pool median, async tiers read live database pricing with a 90-day freshness gate, and the tier cache invalidates on every pricing write ([#12792](https://github.com/diegosouzapw/OmniRoute/pull/12792)) — thanks @maxmad64bis +- **fix(auto-combo):** snapshot scoring uses the observed per-connection account tier and per-model quality instead of neutral constants, and circuit-open providers rank lower at build time ([#12794](https://github.com/diegosouzapw/OmniRoute/pull/12794)) — thanks @maxmad64bis +- **fix(pool):** empty `auto/*` pools now say why they are empty, and the models listing uses the same paid check as routing ([#12795](https://github.com/diegosouzapw/OmniRoute/pull/12795)) — thanks @maxmad64bis +- fix(routing): recognize CLIProxyAPI's 'unknown provider for model' 400 as fallback-worthy (#12800) ([#13284](https://github.com/diegosouzapw/OmniRoute/pull/13284)) +- **fix(sse):** translate-mode streams now emit the estimated token counts as a trailing usage-only chunk before `[DONE]` when the upstream stays silent, so metered chat clients see totals instead of nothing ([#12828](https://github.com/diegosouzapw/OmniRoute/pull/12828)) — thanks @maxmad64bis +- **fix(db):** provider stats stay truthful on empty and legacy databases: fallback counts default to `0` instead of `null`, latency averages read `null` (not `0`) when no durations were recorded, failures logged before the error-type column existed group under `pre_migration` instead of `unclassified`, and per-provider queries use two new composite indexes ([#12832](https://github.com/diegosouzapw/OmniRoute/pull/12832)) — thanks @maxmad64bis +- fix(nvidia): fail open when a synced model catalog goes stale instead of gating forever (#12849) ([#13248](https://github.com/diegosouzapw/OmniRoute/pull/13248)) +- **fix(db):** WAL maintenance lives in its own module: a `TRUNCATE` checkpoint that hits a busy database now warns with its streak and retries once via `PASSIVE` instead of logging success, and closing-time checkpoints no longer report success on builds without a database file, with the busy totals visible in the authenticated monitoring health payload ([#12853](https://github.com/diegosouzapw/OmniRoute/pull/12853)) — thanks @maxmad64bis +- **fix(health):** quota with no snapshots reads empty instead of a contradicting 0% ([#12857](https://github.com/diegosouzapw/OmniRoute/pull/12857)) — thanks @maxmad64bis +- fix(a2a): accept the dashboard's own session cookie on /a2a so "Run message/send" no longer fails with "Unauthorized: missing or invalid API key" (#12888) ([#13271](https://github.com/diegosouzapw/OmniRoute/pull/13271)) +- **fix(logs):** the Logs grid's in-memory filter pass no longer discards rows the SQL query already matched — selecting an API key from the dropdown (which sends the key's id) returns its calls again, the Combo tab shows every combo instead of only those whose name contains a "1", and the model filter and search cover the same columns as the query ([#12896](https://github.com/diegosouzapw/OmniRoute/pull/12896)) — fixes [#12873](https://github.com/diegosouzapw/OmniRoute/issues/12873) — thanks @ntdatt812 +- **fix(a2a):** `/api/a2a/status` now builds the agent card from the request that asked for it, so a gateway reached at a non-localhost host no longer advertises `http://localhost:20128` as its A2A URL ([#12918](https://github.com/diegosouzapw/OmniRoute/pull/12918)). — thanks @ntdatt812 +- **fix(compression):** progressive aging now appends its `[COMPRESSED:aging:…]` annotation after a turn's `tool_result` blocks instead of in front of them, so Anthropic no longer rejects aged conversations with "`tool_use` ids were found without `tool_result` blocks immediately after" ([#12920](https://github.com/diegosouzapw/OmniRoute/pull/12920)). — thanks @ntdatt812 +- **fix(bedrock):** model import now resolves context limits for every vendor prefix instead of only `anthropic.*`, so `global.openai.gpt-5.6-*` no longer imports with a null `inputTokenLimit` and gets rejected pre-flight at the 200k default ([#12921](https://github.com/diegosouzapw/OmniRoute/pull/12921)). — thanks @ntdatt812 +- **fix(stream):** the 64 KB stream buffer GLM asks for is honoured instead of dropped, and the type error it caused no longer fails the API Route Typecheck gate on every open PR ([#12925](https://github.com/diegosouzapw/OmniRoute/pull/12925)) — thanks @ntdatt812 +- **fix(guardrails):** mask PII inside a `tool_result`'s nested content array, which the masker walked past while redacting its sibling block ([#12930](https://github.com/diegosouzapw/OmniRoute/pull/12930)) — thanks @ntdatt812 +- **fix(dashboard):** the Radar catalog table no longer leaves absent data unexplained — empty limits, unknown context windows, and unreported capabilities each explain themselves on hover, and a new check keeps it that way ([#12937](https://github.com/diegosouzapw/OmniRoute/pull/12937)) — thanks @maxmad64bis +- **fix(sse):** geo-blocked opencode requests rotate to the next account proxy instead of failing, so one refused egress no longer aborts the whole chain ([#12941](https://github.com/diegosouzapw/OmniRoute/pull/12941)) — thanks @maxmad64bis +- **fix(codex):** dashboard "clear cooldown" and the CAS recovery path now drop nested `codexScopeRateLimitedUntil` maps in the same write that nulls `rate_limited_until`, and fresh quota snapshots with headroom lift fallback-sourced scope cooldowns parked by quota preflight ([#12817](https://github.com/diegosouzapw/OmniRoute/issues/12817), [#12860](https://github.com/diegosouzapw/OmniRoute/issues/12860), [#12951](https://github.com/diegosouzapw/OmniRoute/pull/12951)) The dashboard reset-credit button (`consumeCodexResetCredit`) now exercises that same snapshot path after a successful redeem, so a filled quota bar is enough to unpark a leftover fallback Codex child without another manual clear. — thanks @HouMinXi +- **fix(db):** resolve `sql-wasm.wasm` across global npm install and hoisted layouts, ensuring OmniRoute can boot cleanly on Node 24 when native `better-sqlite3` is uncompiled. ([#13035](https://github.com/diegosouzapw/OmniRoute/pull/13035)) — thanks @HouMinXi +- **fix(api):** `DELETE /v1/batches/delete-completed` now sweeps only the calling API key's own completed batches (batches with no owner stay out of a key-scoped sweep on purpose), with an explicit instance-wide mode reserved for authenticated dashboard sessions, a 401 for a presented key that is unknown, revoked, deactivated, banned or expired (never falling through to the session branch), audit logging of every sweep, a sanitized 500 on failure, an owner-scoped file half (a key-scoped sweep never nulls a file another tenant owns) and an atomic sweep — chunked in 200-batch transactions in instance mode — so a mid-way error never leaves a batch pointing at a nulled file (GHSA-wvxc-jp3v-5mg5) ([#12969](https://github.com/diegosouzapw/OmniRoute/pull/12969)) +- **fix(combo):** quota-weighted routing stops drawing on an out-of-credit connection — a 402 now invalidates the stored quota snapshot instead of leaving its stale remaining percentage in place, and a snapshot older than 10 minutes no longer counts as confident headroom for the primary pool ([#12972](https://github.com/diegosouzapw/OmniRoute/pull/12972)) ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- **fix(vision):** Custom Models with "Vision capable" checked no longer have image requests swapped to `glm/glm-4.6v` when the client sends the advertised alias (`vllm/path/...`) or the bare path-shaped id — Vision Bridge now matches the stored override for all three id forms ([#12758](https://github.com/diegosouzapw/OmniRoute/issues/12758)) ([#12974](https://github.com/diegosouzapw/OmniRoute/pull/12974)) — thanks @HouMinXi +- **fix(sse):** transient opencode upstream failures rotate to the next account proxy instead of failing, so one flapping egress no longer aborts the whole chain ([#12975](https://github.com/diegosouzapw/OmniRoute/pull/12975)) — thanks @maxmad64bis +- **fix(azure):** Deployments from GPT-6 onward now send `max_completion_tokens` instead of `max_tokens`, which Azure rejects with HTTP 400. The rule matched a literal `gpt-5`, so each new generation arrived broken; it now matches the generation range, while `gpt-35-turbo` still keeps `max_tokens`. ([#13007](https://github.com/diegosouzapw/OmniRoute/pull/13007)) — thanks @ntdatt812 +- **fix(db):** add an opt-in self-restart circuit for sustained critical memory pressure, gate post-cleanup VACUUM behind a minimum freed-rows threshold, and checkpoint the SQLite WAL every 5 minutes with a size guard that escalates to TRUNCATE, so a growing WAL can no longer stall the event loop into a full outage. ([#13011](https://github.com/diegosouzapw/OmniRoute/pull/13011)) — thanks @HouMinXi +- **fix(auth):** an explicit connection pin may probe a quota-disabled row once and re-enable it on success ([#12874](https://github.com/diegosouzapw/OmniRoute/issues/12874)) ([#13017](https://github.com/diegosouzapw/OmniRoute/pull/13017)) — thanks @HouMinXi +- **fix(api):** thread `X-OmniRoute-Fallback-Attempts` through combo chat completions so streaming and non-streaming responses report how many prior legs were attempted ([#13038](https://github.com/diegosouzapw/OmniRoute/pull/13038)) — thanks @HouMinXi +- Restore provider failure classification and credential refresh on non-streaming requests: classify non-2xx failures to lock models on per-model quota exhaustion, update connection rate limits from headers and body, and pass credential refresh handlers to pipeline execution so 401 tokens can be refreshed and retried (#13043). ([#13069](https://github.com/diegosouzapw/OmniRoute/pull/13069)) — thanks @HouMinXi +- **fix(responses):** wrap forced-non-streaming web_search fallback JSON as Responses SSE so Codex still sees `response.completed` ([#13050](https://github.com/diegosouzapw/OmniRoute/pull/13050)) — thanks @HouMinXi +- **fix(antigravity):** Preserve upstream thought-token usage in normalized completion and reasoning token counts ([#13055](https://github.com/diegosouzapw/OmniRoute/pull/13055)) — thanks @pacocartones +- Fix a concurrency-slot leak in the DeepSeek PoW solver: a worker that failed to spawn (for example a missing worker script) never released its slot, so `MAX_CONCURRENT_WORKERS` failures disabled the solver until restart. ([#13097](https://github.com/diegosouzapw/OmniRoute/pull/13097)) — thanks @anhtahaylove +- **fix(acp):** bound the ACP session output buffers — `stdoutBuffer` and `stderrBuffer` now cap at 1 MiB keeping the most recent output behind a visible `[...output truncated...]` marker, and `stderrBuffer` is reset per prompt instead of accumulating for the lifetime of the session. ([#13100](https://github.com/diegosouzapw/OmniRoute/pull/13100)) — thanks @anhtahaylove +- **fix(acp):** release the `stdout`/`exit` listeners and the idle timer that a `sendPrompt` timeout used to leave attached to the `acpManager` singleton, and drop sessions that exited on their own from the session map instead of keeping them forever. ([#13096](https://github.com/diegosouzapw/OmniRoute/pull/13096)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection and PII scanners now read the text a `tool_result` block carries on `content` (string or nested block list), in messages and in system blocks, so tool output is judged by the same rules as user text ([#13101](https://github.com/diegosouzapw/OmniRoute/pull/13101)) — thanks @ntdatt812 +- **fix(gamification):** close the badge notification SSE stream when the request signal is already aborted before the stream starts — a client that disconnects while the route is still awaiting auth used to leave both the 2s unlock poll and the 15s heartbeat running for the lifetime of the process. ([#13106](https://github.com/diegosouzapw/OmniRoute/pull/13106)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection scan now spends its 16 KB budget on both ends of the request instead of the first 16 KB only, so `system`, `instructions`, `query`, `documents` and the newest turns are no longer hidden behind one long message ([#13104](https://github.com/diegosouzapw/OmniRoute/pull/13104)) — thanks @ntdatt812 +- **fix(dashboard):** expose the Volcano Ark console cookie on quota scraping and unwrap connect-error objects so the dashboard shows the upstream message ([#13107](https://github.com/diegosouzapw/OmniRoute/pull/13107)) — thanks @HouMinXi +- **fix(db):** release the `beforeExit`/`SIGINT`/`SIGTERM` handlers when a `node:sqlite` adapter closes, so a closed adapter and its database handle are no longer pinned to `process` for the lifetime of the run — the same treatment #7494 gave the sql.js adapter. ([#13109](https://github.com/diegosouzapw/OmniRoute/pull/13109)) — thanks @anhtahaylove +- **fix(translator):** `contentSchema` and `unevaluatedItems` are now treated as subschema positions by the tool-schema sanitizer, so a truncation placeholder in either is replaced with a permissive schema instead of being forwarded as a string ([#13110](https://github.com/diegosouzapw/OmniRoute/pull/13110)) — thanks @ntdatt812 +- **fix(cli-helper):** clear the `createLogStream` timeout on the abort path — `stop()` aborts the in-flight fetch and returned through the `signal.aborted` branch, which skipped `clearTimeout` and left an armed timer per stopped stream. The stream reader is now also cancelled when the read loop exits early. ([#13114](https://github.com/diegosouzapw/OmniRoute/pull/13114)) — thanks @anhtahaylove +- **fix(docs):** drop TLS-impersonation, MITM-decrypt, supply-chain attestation, and XOR-mask writeups from the public `/docs` catalog and Docker image. Files stay in git for engineers; operators who need them open the repo, not the website. ([#13136](https://github.com/diegosouzapw/OmniRoute/pull/13136)) — thanks @HouMinXi +- **fix(combo):** parse numeric-epoch `rate_limited_until` in the combo cooldown read path ([#13141](https://github.com/diegosouzapw/OmniRoute/pull/13141)) — thanks @maxmad64bis +- **fix(opencode):** both OpenCode plugins now reject a gateway address typed without `http://` at configuration time, instead of publishing every model with an api url no client can call, and the v2 plugin no longer publishes a model card whose api url is blank or relative ([#13142](https://github.com/diegosouzapw/OmniRoute/pull/13142)) — thanks @maxmad64bis +- **fix(providers):** lock opencode model on upstream 400 model-unavailable ([#13146](https://github.com/diegosouzapw/OmniRoute/pull/13146)) — thanks @maxmad64bis +- **fix(logging):** keep the provider exchange rather than the raw client bodies when a call log exceeds its size budget, and show that recovered payload in the request-detail panel instead of replacing it with the stored response body ([#13147](https://github.com/diegosouzapw/OmniRoute/pull/13147)) — thanks @maxmad64bis +- **fix(traffic-inspector):** the WebSocket route no longer leaks a traffic-buffer subscriber and a 30s ping timer when the client socket is already closed at handler time — listeners are attached before any resource is acquired, a destroyed socket bails out early, and the ping interval stops on a dead socket where `write()` never throws ([#13155](https://github.com/diegosouzapw/OmniRoute/pull/13155)) — thanks @anhtahaylove +- **fix(telegram):** bound the per-user API key cache in the Telegram chat proxy so a burst of distinct chat ids can no longer grow the process heap without limit ([#13165](https://github.com/diegosouzapw/OmniRoute/issues/13165)) ([#13166](https://github.com/diegosouzapw/OmniRoute/pull/13166)) — thanks @anhtahaylove +- **fix(stream):** cancel the upstream response body when the JSON-to-SSE sniff unwinds on a body timeout, so a stalled upstream no longer pins the connection ([#13169](https://github.com/diegosouzapw/OmniRoute/issues/13169)) ([#13171](https://github.com/diegosouzapw/OmniRoute/pull/13171)) — thanks @anhtahaylove +- **fix(telegram):** authenticate webhook deliveries with Telegram's `secret_token` so an unauthenticated caller can no longer mint API keys or spend upstream quota ([#13172](https://github.com/diegosouzapw/OmniRoute/issues/13172)) ([#13175](https://github.com/diegosouzapw/OmniRoute/pull/13175)) — thanks @anhtahaylove +- **fix(models):** give discoverable Gemini 3.8 Flash ids their own 65536 output spec so Antigravity no longer clamps them to 16384 ([#13195](https://github.com/diegosouzapw/OmniRoute/pull/13195)) — thanks @HouMinXi +- **fix(dashboard):** leftover catalog-removed provider rows (gemini-cli) can be listed and purged from the providers page ([#13067](https://github.com/diegosouzapw/OmniRoute/issues/13067)) ([#13197](https://github.com/diegosouzapw/OmniRoute/pull/13197)) — thanks @HouMinXi +- **fix(auth):** a dashboard session now requires the `authenticated: true` claim that login, OIDC and the session refresh already emit — a JWT merely signed with `JWT_SECRET` (for example the Cursor CLI passthrough token, which any API-key holder can obtain) no longer verifies as the `auth_token` cookie on any route, the WebSocket handshake or the live server; existing sessions keep working ([#13298](https://github.com/diegosouzapw/OmniRoute/issues/13298)) ([#13375](https://github.com/diegosouzapw/OmniRoute/pull/13375)) +- **fix(api):** `DELETE /v1/batches/delete-completed` now applies the caller's API-key policy (endpoint allowlist, schedule, usage cap, rate limit) like every other `/v1` route, commits the key-scoped sweep per 200-batch chunk instead of holding one write lock for the whole sweep, refuses to loop without progress, and rejects a scope that names both a key and `allTenants` ([#13684](https://github.com/diegosouzapw/OmniRoute/pull/13684)) +- Restore the API-route typecheck gate: the non-streaming leg result lost its discriminated-union narrowing after the server-owned tool loop reassignment, producing 13 new TS2339 diagnostics in `chatCore.ts`. ([#12963](https://github.com/diegosouzapw/OmniRoute/pull/12963)) +- Restore four non-streaming behaviours the server-owned tool loop refactor dropped: client aborts map to 499 with the fixed `Request aborted` message, an aborted request no longer logs a synthetic `clientResponse`, Claude prompt-cache telemetry is recorded again, and a body that cannot be canonicalized no longer throws when the tool loop is off. ([#12990](https://github.com/diegosouzapw/OmniRoute/pull/12990)) +- **fix(skills):** The CLI registry parser now reads positionals declared with `.addArgument()`, not only those written inline in `.command()`. `tunnel create [type]` was being published as `tunnel create`, so the agent-skills sync gate reported drift on every branch and regenerating would have deleted the argument. ([#13009](https://github.com/diegosouzapw/OmniRoute/pull/13009)) — thanks @ntdatt812 +- **fix(combos):** dashboard combo test uses a short prompt, serial probes, and a 60s timeout so reasoning models and rate-limited free pools do not fail the health check ([#13001](https://github.com/diegosouzapw/OmniRoute/pull/13001)) — thanks @HouMinXi +- fix(compression): terminate idle worker threads on eviction so long-running instances stop leaking OS threads and MessagePorts ([#13091](https://github.com/diegosouzapw/OmniRoute/pull/13091)) — thanks @anhtahaylove +- Redact Google API keys of any length in error bodies: the pattern required exactly 39 characters, so shorter or longer `AIza…` credentials passed through unredacted. ([#12964](https://github.com/diegosouzapw/OmniRoute/pull/12964)) +- **fix(grok-cli):** a 402 "Grok Build usage balance exhausted" parks that Grok login as out of credit (Grok Build CLI, grok.com cookie, and xAI OAuth share the weekly pool). Combo routing then tries the next login instead of locking the model for every account in the pool ([#13061](https://github.com/diegosouzapw/OmniRoute/pull/13061)) — thanks @HouMinXi +- fix(compression): spawn the LLMLingua worker with a file URL object so compression actually runs instead of silently failing open on Node ([#13093](https://github.com/diegosouzapw/OmniRoute/pull/13093)) — thanks @anhtahaylove +- **fix(test):** run the local `test` and `test:unit` scripts at concurrency 4 so a full-suite run no longer exhausts the machine's commit charge and kills unrelated processes ([#13187](https://github.com/diegosouzapw/OmniRoute/pull/13187)) — thanks @anhtahaylove +- **fix(monitoring):** `GET /api/monitoring/health` `credentialHealth` now includes a bounded `failedConnections` list (`connectionId`, `status`, sanitized `lastError`) when the probe-cache gauge `failed>0`, plus `source: probe-cache` and a cheap `staleDbNonOkCount` for sticky SQLite `test_status` on active rows. Documents that the live gauge is not `provider_connections.test_status`. ([#12876](https://github.com/diegosouzapw/OmniRoute/pull/12876)) — thanks @RaviTharuma +- **fix(resilience):** an openai-compatible multi-upstream gateway no longer marks the whole connection `credits_exhausted` when a single upstream (or the `/models` / representative-model health probe) returns 402. Quota failures stay model-scoped; true connection-wide auth failures are unchanged. Single-credential 402 key disable ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239)) is preserved. ([#12875](https://github.com/diegosouzapw/OmniRoute/pull/12875)) — thanks @RaviTharuma +- **fix(dashboard):** nine fixes on the Orchestration Canvas, all diagnosed in the Phase 2 reviews. A source that fails now keeps the timestamp of its FIRST failure instead of being re-stamped every poll — the stale line said "since the last poll" no matter how long the source had been down, and the churn also defeated the snapshot's stable identity (it serializes the source list), so the canvas re-rendered on every tick while anything was broken. A source that HAD data and only then started failing is flagged too: previously only a source with no node at all got the warning, so a source that went down mid-session kept a healthy-looking node forever. The rest are pointwise: clicking a filter chip cancels the pending search debounce (left armed, it fired ~300ms later and silently reverted the chip); the search input carries an accessible name; `?state=running, failed` parses like the unpadded form instead of dropping the padded value; the CSV toggle helper is defined once in `model/urlParams.ts` rather than twice; the agents tab tells "nothing running" apart from "the filter matched nothing", offering a clear-filters button instead of setup links that would be wrong advice there; edges stop emitting SMIL particles above 40 simultaneously active edges, keeping the colored stroke; and the drawer's error banner clears when a retried action succeeds. ([#12983](https://github.com/diegosouzapw/OmniRoute/pull/12983)) +- **fix(i18n):** the nine locales added with the EU-language batch (Greek, Estonian, Irish, Croatian, Lithuanian, Latvian, Maltese, Slovenian, Serbian) were missing the eleven Orchestration Canvas keys that Phase 3 introduced, so the compare-runs panel and the "no runs match these filters" empty state fell back to English in those languages (`deepMergeFallback` substitutes English for an absent key, so nothing rendered blank — it rendered untranslated). The coverage gate does not catch this: it enforces an 80% floor per locale, and eleven missing keys out of ~13,000 leaves coverage at 99.9%. Translated for real in each language, calibrated against the wording each file already uses for "run", "filter" and "skill". ([#13294](https://github.com/diegosouzapw/OmniRoute/pull/13294)) +- **fix(dashboard):** five follow-ups from the Phase 2 Orchestration Canvas review. The History drawer now shows the memory section for runs that already left the live TTL window: the persisted `memory_hits` event is parsed into `metadata.memoryHits` with the same defensive validation the drawer applies, and — because that event is observability rather than a state transition — it no longer leaks into the timeline, where it had been inheriting the task's state and rendering as a duplicate transition. The A2A memory recall runs against its own 1.5s deadline instead of inheriting the memory backend's 30s one; overshooting degrades exactly like any other recall failure (no hits, task proceeds), and the timer is cleared on both paths. Repeating a Conductor run carries its `requirements.cli`/`requirements.model` forward, so the new run is pinned to the same runner profile and model rather than drifting to whatever the fleet picks. A successful repeat from the Agents tab now focuses the run it created, instead of leaving the operator on the finished one. And the auth test for `POST /api/conductor/tasks` moved into the shared `ROUTES` array rather than restating the pattern. ([#12988](https://github.com/diegosouzapw/OmniRoute/pull/12988)) +- fix(plugins): stop leaking an exit listener per plugin hook timeout, which triggered MaxListenersExceededWarning on plugins that ignore SIGTERM ([#13092](https://github.com/diegosouzapw/OmniRoute/pull/13092)) — thanks @anhtahaylove +- **fix(validation):** Provider node edits no longer fail with a generic "Invalid request" when the optional daily-quota reset fields are left blank. The dashboard sends `dailyQuotaResetTimezone` and `dailyQuotaResetHour` as `null`, and only the hour accepted it. ([#13066](https://github.com/diegosouzapw/OmniRoute/issues/13066)) ([#13083](https://github.com/diegosouzapw/OmniRoute/pull/13083)) — thanks @ntdatt812 +- **fix(combo):** quota-aware expansion drops banned, inactive, missing, and wrong-provider connections before quota fetch or model dispatch; pins and allowlists stay selectors, not a bypass. Antigravity automatic exhaustion now requires a reported zero remaining, so a positive balance below 1% stays eligible. ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- Fix an intermittent failure in the quota-weighted routing test suite: scores are a function of `Date.now()`, so peers with identical quota scored microseconds apart never tied and swapped order. ([#12946](https://github.com/diegosouzapw/OmniRoute/pull/12946)) +- fix(volcengine): resolve quota window mapping gaps, safe multi-connection console binding, and response cookie sanitization ([#12950](https://github.com/diegosouzapw/OmniRoute/pull/12950)) — thanks @HouMinXi +- **fix(test):** resolve the WebDAV handler path with `fileURLToPath` so the suite's 37 WebDAV tests run on Windows instead of failing with a doubled `C:\C:\` drive prefix ([#13196](https://github.com/diegosouzapw/OmniRoute/pull/13196)) — thanks @anhtahaylove +- **fix(ui):** fall back from invalid AnySearch icon alias (direct commit `9587e07b69`) +- **fix(sse):** stop rebuilding a truncated summary from the collector's cap-dropped event array ([#12718](https://github.com/diegosouzapw/OmniRoute/pull/12718)) — thanks @hartmark +- **fix(conversations):** show a pending spinner for unresolved tool nodes instead of "(empty)" ([#12727](https://github.com/diegosouzapw/OmniRoute/pull/12727)) — thanks @hartmark +- **fix(streaming):** fail fast when an upstream stream produces only lifecycle/heartbeat events, never real content ([#12741](https://github.com/diegosouzapw/OmniRoute/pull/12741)) — thanks @hartmark +- **fix(responses-continuation):** bridge the write-in-flight window with an in-memory pending store ([#12854](https://github.com/diegosouzapw/OmniRoute/pull/12854)) — thanks @hartmark +- **fix(translator):** drop or map agent_message on Chat Completions fallback ([#12880](https://github.com/diegosouzapw/OmniRoute/pull/12880)) — thanks @RaviTharuma +- **fix(api):** page and stream-complete GET /v1/models for large catalogs ([#12882](https://github.com/diegosouzapw/OmniRoute/pull/12882)) — thanks @RaviTharuma +- **fix(quota):** align AUTH window labels with usage API and clarify 503 ([#12884](https://github.com/diegosouzapw/OmniRoute/pull/12884)) — thanks @RaviTharuma +- **fix(ci):** clear two base-reds on release/v3.8.51 — mutation-coverage gate + image-only-model guard ([#12945](https://github.com/diegosouzapw/OmniRoute/pull/12945)) +- **fix(api):** scope batch bulk-delete to the calling API key ([#13211](https://github.com/diegosouzapw/OmniRoute/pull/13211)) +- **fix(tests):** align model-sync log assertion to shipped path redaction (base-red) ([#13227](https://github.com/diegosouzapw/OmniRoute/pull/13227)) +- **fix(test):** make the ToS heading guard actually require the parentheses ([#13228](https://github.com/diegosouzapw/OmniRoute/pull/13228)) +- **fix(quality):** register 4 drifted covering tests in stryker tap.testFiles ([#13229](https://github.com/diegosouzapw/OmniRoute/pull/13229)) +- **fix(logging):** mask *-api-key headers in request-log pipeline (#13273) ([#13401](https://github.com/diegosouzapw/OmniRoute/pull/13401)) — thanks @KooshaPari +- **fix(tests):** correct stale Jina catalog prefix in custom-models test ([#13403](https://github.com/diegosouzapw/OmniRoute/pull/13403)) — thanks @KooshaPari +- **fix(backend):** prune old backups after health-check-repair snapshots (#13308) ([#13404](https://github.com/diegosouzapw/OmniRoute/pull/13404)) — thanks @KooshaPari +- **fix(backend):** guard memory_fts_au trigger with WHEN clause to stop FTS bloat (#13326) ([#13405](https://github.com/diegosouzapw/OmniRoute/pull/13405)) — thanks @KooshaPari +- **fix(free-tiers):** remove obsolete Together $25 signup credit (#12526) ([#13407](https://github.com/diegosouzapw/OmniRoute/pull/13407)) — thanks @KooshaPari +- **fix:** skip non-chat generation models in Test all models ([#13409](https://github.com/diegosouzapw/OmniRoute/pull/13409)) — thanks @KooshaPari +- **fix(providers):** de-duplicate agy and antigravity model catalogs ([#13410](https://github.com/diegosouzapw/OmniRoute/pull/13410)) — thanks @KooshaPari +- **fix(dashboard):** send provider in body for mini-playground web search ([#13411](https://github.com/diegosouzapw/OmniRoute/pull/13411)) — thanks @KooshaPari +- **fix(resilience):** add CJK quota-exhaustion patterns to 429 classifier ([#13412](https://github.com/diegosouzapw/OmniRoute/pull/13412)) — thanks @KooshaPari +- **fix(backend):** eval runner: failed calls no longer score as passed + dotAll regex ([#13413](https://github.com/diegosouzapw/OmniRoute/pull/13413)) — thanks @KooshaPari +- **fix(backend):** eval runner opts out of output-style and memory injection ([#13414](https://github.com/diegosouzapw/OmniRoute/pull/13414)) — thanks @KooshaPari +- **fix:** chain serial quarantine step in npm test script (fixes #13286) ([#13418](https://github.com/diegosouzapw/OmniRoute/pull/13418)) — thanks @KooshaPari +- **fix(antigravity):** derive stable session ID from credentials for prompt-cache reuse (#13305) ([#13424](https://github.com/diegosouzapw/OmniRoute/pull/13424)) — thanks @KooshaPari +- **fix(memory):** merge memory into top-level system field instead of unshifting at messages[0] (#13425) ([#13427](https://github.com/diegosouzapw/OmniRoute/pull/13427)) — thanks @KooshaPari +- **fix(compression):** preserve instruction blocks from lossy engines ([#13523](https://github.com/diegosouzapw/OmniRoute/pull/13523)) — thanks @KooshaPari +- **fix(compression):** preserve polarity words and newlines in ultra heuristic ([#13524](https://github.com/diegosouzapw/OmniRoute/pull/13524)) — thanks @KooshaPari +- **fix(compression):** run user preservePatterns before built-in patterns (#13457) ([#13525](https://github.com/diegosouzapw/OmniRoute/pull/13525)) — thanks @KooshaPari +- **fix(reasoning):** expand deepseek-v4 regex to match v4.1+ and add command-code provider (#13430) ([#13528](https://github.com/diegosouzapw/OmniRoute/pull/13528)) — thanks @KooshaPari +- **fix(providers):** add alwaysReasons metadata for reka-flash-3 ([#13531](https://github.com/diegosouzapw/OmniRoute/pull/13531)) — thanks @KooshaPari +- **fix(playground):** allow Compare columns to scroll independently ([#13532](https://github.com/diegosouzapw/OmniRoute/pull/13532)) — thanks @KooshaPari +- **fix(security):** empty whitelist allows through to prevent admin lockout ([#13534](https://github.com/diegosouzapw/OmniRoute/pull/13534)) — thanks @KooshaPari +- **fix(tests):** add DISABLE_IOREG_STRATEGY env var for macOS test compatibility (fixes #13467) ([#13539](https://github.com/diegosouzapw/OmniRoute/pull/13539)) — thanks @KooshaPari +- **fix(zed-hosted):** map developer role and Gemini enums for Zed proxy ([#13543](https://github.com/diegosouzapw/OmniRoute/pull/13543)) — thanks @KooshaPari +- **fix(analytics):** normalize provider aliases to canonical id in usage_history ([#13545](https://github.com/diegosouzapw/OmniRoute/pull/13545)) — thanks @KooshaPari +- **fix(combo):** use traceId for call log id to prevent UNIQUE constraint failure ([#13546](https://github.com/diegosouzapw/OmniRoute/pull/13546)) — thanks @KooshaPari +- **fix(auth):** check model lockout before returning synthetic noauth connection ([#13547](https://github.com/diegosouzapw/OmniRoute/pull/13547)) — thanks @KooshaPari +- **fix(guardrails):** skip credential redaction for base64 image data URLs ([#13550](https://github.com/diegosouzapw/OmniRoute/pull/13550)) — thanks @KooshaPari +- **fix(resilience):** increase requestQueue.maxWaitMs default from 15s to 30s ([#13553](https://github.com/diegosouzapw/OmniRoute/pull/13553)) — thanks @KooshaPari +- **fix(docs):** document OMNIROUTE_READY_TIMEOUT_MS and allowlist the test-only DISABLE_IOREG_STRATEGY ([#13692](https://github.com/diegosouzapw/OmniRoute/pull/13692)) + ### 📝 Maintenance ---- +- **docs(readme):** reconcile live v3.8.50 provider, free-tier, CLI, routing, test, community, sponsor, acknowledgment, and SVG metrics with their audited source denominators, including a deduplicated OmniRoute-in-Action snapshot and distinct contributor rankings for merged pull requests, GitHub-attributed commits, and Git history ([#11356](https://github.com/diegosouzapw/OmniRoute/pull/11356)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **docs:** sync the canonical provider count 354 → 356 across `README.md`, `AGENTS.md`, `llm.txt` (+ 42 i18n mirrors), the four README SVG diagrams, `docs/reference/PROVIDER_REFERENCE.md` (regenerated) and the `package.json` description after Opper (#11629) and 1min.ai (#11631) boarded the catalog — closes the `check:docs-counts-sync` strict drifts that kept `release/v3.8.51` red ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11673](https://github.com/diegosouzapw/OmniRoute/pull/11673)) — thanks @jonlwheat2-gif +- **test(providers):** update count-derived assertions after the v3.8.51 provider additions — `APIKEY_PROVIDERS` 233 → 235 (Opper #11629 + 1min.ai #11631), reserved-prefix REGISTRY walk 395 → 398, `WEB_FETCH_PROVIDERS` now includes `nimble-search` (#11620), and the provider translate-path golden snapshot regenerated ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11674](https://github.com/diegosouzapw/OmniRoute/pull/11674)) — thanks @jonlwheat2-gif +- **test(sse):** bump the hard-lease connection-query inventory for `src/lib/tokenHealthCheck.ts` to 2 — the verify-only web-cookie sweep added by #11495 split the single `getProviderConnections` call into oauth + cookie variants, which the frozen inventory had not tracked ([#11495](https://github.com/diegosouzapw/OmniRoute/pull/11495)). ([#11677](https://github.com/diegosouzapw/OmniRoute/pull/11677)) — thanks @jonlwheat2-gif / @oyi77 +- **chore(release):** clear the `release/v3.8.51` base-red gates — docs migration count (+38 i18n mirrors), stryker `tap.testFiles`, lockfile registry host, stale ESLint suppressions, the 7 lint regressions they were hiding, the stale `APIKEY_PROVIDERS` partition count (231 → 233), the `glm-5.3-max` tier missing from the GLM test table, a dead `modelType === "chat"` comparison in the models catalog, and the `resolveLiveWsUrl` / `sanitizeLiveWsPort` helpers from #11388 that a later merge dropped from `wsPath.ts` (dashboard live WebSocket URL resolution) ([#11502](https://github.com/diegosouzapw/OmniRoute/pull/11502)) — thanks @MumuTW +- **chore(free-models):** the free-tier regime table now declares which totals figure each regime feeds and whether it may take the no-auth shortcut, so the sets that used to repeat those answers by hand are derived from it — a new `freeType` no longer compiles until it has answered every question, instead of silently contributing to no total ([#11537](https://github.com/diegosouzapw/OmniRoute/pull/11537)) — thanks @maxmad64bis +- **docs(routing):** LKGP strategy copy now states provider-level stickiness and the explicit rules fallback instead of promising per-target pinning ([#11555](https://github.com/diegosouzapw/OmniRoute/pull/11555)) — thanks @maxmad64bis +- **test(chatcore):** pin the response tool-name alias resolution against the ordering hazard that broke every Gemini/Antigravity MCP tool call in v3.8.49. `extractRequestToolIdentityMap` deletes `translatedBody._toolNameMap`, so the later read is always undefined and the ledger survives only through the `requestToolIdentityMap` fallback — removing that fallback previously left the entire tool-name suite green. The resolution moves into `resolveResponseToolNameMap()` next to the map it depends on, with a regression guard that fails without the recovery. No behaviour change. ([#11575](https://github.com/diegosouzapw/OmniRoute/pull/11575)) — thanks @amartinawi +- **docs(env):** document `OMNIROUTE_CORPUS_CACHE_SIZE` in `.env.example` and `ENVIRONMENT.md`, so the env/docs contract check stops failing Docs Gates on every branch ([#11576](https://github.com/diegosouzapw/OmniRoute/pull/11576)) — thanks @ntdatt812 +- **chore(stryker):** register `route-guard-tunnel-processes-local-only` and `universal-quota-aware-routing` in `tap.testFiles`, so their mutant kills count for the three modules they cover ([#11577](https://github.com/diegosouzapw/OmniRoute/pull/11577)) — thanks @ntdatt812 +- **test(authz):** pin `LOCAL_ONLY_API_GET_EXEMPTIONS` by exact membership instead of by entry count, so the guard names the offending path and also catches a substitution ([#11580](https://github.com/diegosouzapw/OmniRoute/pull/11580)) — thanks @ntdatt812 +- **test(build):** pin `resolveNextBuildBundlerFlag` to the `OMNIROUTE_USE_TURBOPACK` contract it implements, across runtimes, instead of asserting an unimplemented Bun override ([#11583](https://github.com/diegosouzapw/OmniRoute/pull/11583)) — thanks @ntdatt812 +- **test(claude-web):** the Claude Web transport test's barrel anchors no longer pin the exact construction spelling. #11421 made the executor map lazy, so `/"claude-web": new ClaudeWebExecutor\(\)/` stopped matching and the test went red on `release/v3.8.51` itself — a base-red gate every open PR inherited. The anchors now match the alias key and the class name and tolerate either spelling in between, and were checked to still fail when an alias is genuinely repointed at another executor ([#11585](https://github.com/diegosouzapw/OmniRoute/pull/11585)) — thanks @ntdat812 +- **test(models):** pin `qwen3.8-max` resolution per provider — two of the four catalogs now serve the bare id and no longer carry `-preview` ([#11590](https://github.com/diegosouzapw/OmniRoute/pull/11590)) — thanks @ntdatt812 +- **test(executors):** drop the stale 128-tool truncation assertion that contradicted the #11444 pass-through contract ([#11591](https://github.com/diegosouzapw/OmniRoute/pull/11591)) — thanks @ntdatt812 +- **test(token-health):** pin the current terminal-skip boundary — expired with retry budget is probed, `account_deactivated` stays skipped ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)) — thanks @ntdatt812 +- **test(models):** follow #11503's retarget of the `gemini-3-pro-high` alias to the hyphenated catalog id ([#11593](https://github.com/diegosouzapw/OmniRoute/pull/11593)) — thanks @ntdatt812 +- **refactor(video bridge):** extract per-video acquisition, whole-result caching, description, and metrics/abort/cleanup out of `VideoBridgeGuardrail.preCall` into a `processVideoPart` seam in a new `videoBridgePipeline.ts`, behind explicit `VideoMediaBrokerPort`, `VideoAudioTranscriptionPort`, and `VideoDrilldownPort` boundaries; `preCall` now only handles request traversal, policy, and response aggregation. The Video tab's FFmpeg/ffprobe runtime status is now an explicit `unknown` / `restricted` / `unavailable` / `available` state instead of a nullable boolean pair, fixing a case where an in-flight or failed probe was mislabeled as "install FFmpeg" ([#11657](https://github.com/diegosouzapw/OmniRoute/issues/11657)). ([#12016](https://github.com/diegosouzapw/OmniRoute/pull/12016)) +- **test(opencode-plugin):** add unit test coverage for `formatFreeBudget()` naming helper ([#11660](https://github.com/diegosouzapw/OmniRoute/pull/11660)) — thanks @f9td56dbgh-hub +- **docs(video):** clarify that the Video Bridge transcript `source` field (`client`, `embedded`, `audio-bridge`) is presently caller-declared and not yet server-verified — OmniRoute enforces the enum shape but does not cryptographically confirm that an `embedded`/`audio-bridge` label came from a server-owned extraction ([#11661](https://github.com/diegosouzapw/OmniRoute/issues/11661)). ([#12001](https://github.com/diegosouzapw/OmniRoute/pull/12001)) +- **chore(providers):** retire the distributed Raycast Relay and Hailuo Web integrations after a provenance audit confirmed substantial derivation from GPL-3.0 sources; the official GitHub and MiniMax API providers remain available. Migration 163 disables legacy connections and invalidates their active leases while preserving connection IDs, audit/history rows, quota snapshots, usage records, and fail-closed API-key connection policies. ([#11691](https://github.com/diegosouzapw/OmniRoute/pull/11691)) +- **chore(providers):** retire the Felo Web provider while its GPL-derived provenance remains on licensing HOLD ([#11698](https://github.com/diegosouzapw/OmniRoute/pull/11698)) — stale runtime and database references now fail closed while existing connection, lease, and usage history remains preserved for auditability. +- **fix(sse):** retire the Gemini Web image-generation bridge while provenance and licensing remain on HOLD, preserving Gemini Web chat/auth/tools and legitimate Gemini/Nano Banana image providers ([#11708](https://github.com/diegosouzapw/OmniRoute/pull/11708)) +- **chore(audio):** retire the built-in EdgeTTS provider and its 11-voice catalog while provenance/licensing review remains on HOLD; the generic `/v1/audio/speech` endpoint and the other speech providers remain available ([#11711](https://github.com/diegosouzapw/OmniRoute/pull/11711)) +- **fix(providers):** retire the unofficial Qwen Web integration and its `qwen-web`/`qw` identities while third-party provenance remains on HOLD ([#11713](https://github.com/diegosouzapw/OmniRoute/pull/11713)) — direct requests now fail closed with a sanitized HTTP 410 and combos skip the retired target, while migration guards preserve connection IDs, allowlists, usage/audit history and invalidate active leases. Supported Qwen API, Qwen Code, cloud, token-plan, audio, local and model-name surfaces remain available; restoring Qwen Web requires a qualified provenance/license decision. +- **fix(providers):** retire `microsoft-designer-web` and its `msdesigner` alias from runtime and catalogs while preserving stored credentials, provider history and allowlists as inactive records — source dependency on gpt4free's [`MicrosoftDesigner.py`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/g4f/Provider/needs_auth/MicrosoftDesigner.py) is proven at snapshot `0943d16eef757a44deb60651c07bbe5fa958cafe`, whose [`LICENSE`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/LICENSE) is GPL-3.0; expressive adaptation is probable, which records provenance/licensing risk without asserting a legal conclusion ([#11720](https://github.com/diegosouzapw/OmniRoute/pull/11720)). +- **docs(legal):** centralize pinned MIT notices for the vendored GCF codec and local flag assets ([#11726](https://github.com/diegosouzapw/OmniRoute/pull/11726)) +- Split the 2,952-line `adobeFireflyClient.ts` into nine focused modules (catalog, credentials, payload, ARP session, upload, responses, IMS, account, poll) behind an unchanged barrel — no consumer edits and no public API change (#11728). — thanks @MumuTW +- **fix(dashboard):** replaced the Model Lockout card's unprovenanced notification MP3 with a short, locally synthesized Web Audio chime ([#11731](https://github.com/diegosouzapw/OmniRoute/pull/11731)) — both toggles retain optional audible feedback without shipping replacement media or third-party code, and unsupported, suspended, or rejected audio contexts fail open without blocking the setting change. +- **chore(deepseek-web):** remove the provenance-unresolved DeepSeek PoW WASM binary and its runtime loader/tracing while retaining the existing JavaScript solver (slower at high difficulty) ([#11732](https://github.com/diegosouzapw/OmniRoute/pull/11732)). +- **chore(assets):** remove four unused provider assets whose redistribution provenance could not be verified while retaining the active Freebuff SVG ([#11735](https://github.com/diegosouzapw/OmniRoute/pull/11735)) +- **docs(assets):** retired two raster images whose redistribution provenance could not be verified ([#11736](https://github.com/diegosouzapw/OmniRoute/pull/11736)) — PicoClaw documentation now reuses the existing SVG, while Zoo Code remains a text link without an image. +- **docs(legal):** add pinned notices for six LobeHub-derived provider SVGs and 65 byte-exact theSVG assets, retaining registry claims as unverified metadata with explicit trademark and HOLD boundaries ([#11737](https://github.com/diegosouzapw/OmniRoute/pull/11737)). +- **chore(quality):** added a versioned 225/225 provider-asset provenance manifest and blocking drift gate ([#11743](https://github.com/diegosouzapw/OmniRoute/pull/11743)) — the inventory pins magic MIME and SHA-256 for every physical file, records immutable source matches for 71 assets, keeps 69 probable and 85 unresolved entries explicit but non-blocking, and treats provenance as source evidence rather than copyright or trademark clearance. +- **refactor(mitm):** replace four previously derived Traffic Inspector utilities — conversation normalization, SSE merging, secret masking, and header sanitization — with independent clean-room implementations based on public provider schemas, WHATWG SSE, and RFC 6750, while keeping the two independently classified passthrough surfaces unchanged ([#11748](https://github.com/diegosouzapw/OmniRoute/pull/11748)). +- Added a unit test that fails seven days before any dated pack under `config/` (`validUntil` and sibling keys) lapses, naming the file and key. The Alibaba free-tier pack expired on 2026-08-27 and turned every PR red the next morning with no commit involved; renewal now happens on someone's terms, not the clock's. ([#11891](https://github.com/diegosouzapw/OmniRoute/pull/11891)) +- `check:workflows` now fails (under `--strict`/`--ratchet`) when any job routed to a self-hosted runner publishes with `--provenance` — npm rejects that with `422` at the registry, which in v3.8.50 only surfaced after the tag and Docker images were public. ([#11895](https://github.com/diegosouzapw/OmniRoute/pull/11895)) +- `scripts/ops/runner-janitor.sh` now proves a path is idle with one `lsof` snapshot and removes stale leftovers itself (tmpfs after 3 h — it is RAM — disk after 24 h), kills orphan `next-build` processes, prunes checkouts of stopped runners, and alerts on memory pressure; `--dry-run` shows exactly what it would do. `docs/ops/RUNNER_BOX.md` reconciled to the measured box (31 GB, 10 listeners). ([#11893](https://github.com/diegosouzapw/OmniRoute/pull/11893)) +- The `next-build` artefact (1.3 GB) is now written and read under `$RUNNER_TEMP` (per-runner, on disk) instead of `/tmp`, which on the self-hosted pool is a 12 GB tmpfs in RAM. Landing it there took 27–32 of the publish job's 76 minutes, and the fixed `/tmp/e2e-build.tar.gz` name let E2E jobs on different runners overwrite each other's download. ([#11896](https://github.com/diegosouzapw/OmniRoute/pull/11896)) +- The CI `build` job now runs in two concurrency lanes — `main` and pull requests — so a release build is never queued behind (or OOM-killed beside) PR builds on the self-hosted pool, which holds one `next-build` comfortably and two at the edge. ([#11901](https://github.com/diegosouzapw/OmniRoute/pull/11901)) +- Re-freeze the ESLint suppressions on `release/v3.8.51` from a clean-room measurement (2 stale file entries pruned, 55 pre-existing `no-explicit-any` in six new files frozen under #11924) and drop the dead `GPT_SIZE_MAP` constant orphaned by the Adobe Firefly client split, so `No new ESLint warnings` stops failing every PR with exit 2 (Refs #11924) ([#11955](https://github.com/diegosouzapw/OmniRoute/pull/11955)) +- Type the 55 `no-explicit-any` sites that had been frozen under #11924 — four redundant casts in `socksConnectorWithFamily.ts` (undici/socks types already accept them) and the mocks/fixtures of the socks-timeout and isFree suites — and drop their suppression entries; the ESLint ratchet shrinks from 5487 to 5432 (Closes #11924) ([#11975](https://github.com/diegosouzapw/OmniRoute/pull/11975)) +- Take the two hosted-runner builds off the PR rail: `Build App` (`build.yml`) is `workflow_dispatch`-only and `DAST smoke (PR)` runs only for PRs into `main` — the 7 GB hosted VM cannot build `release/v3.8.51` in any profile (19/30 red, VM shutdown ~8 min into `next build`) and both checks had turned into permanent noise on every release PR; the bundle stays validated by `ci.yml` on `main` and by `nightly-release-green` on `release/**` (Closes #11946) ([#11962](https://github.com/diegosouzapw/OmniRoute/pull/11962)) +- Move the four nightly jobs that build the backend (`nightly-schemathesis`, `nightly-llm-security` promptfoo + garak, `nightly-resilience` axe-a11y) off the hosted 7 GB runner — where they died on `release/v3.8.51` unseen — onto the box's new `omni-light` pool (two listeners), and document the reshaped fleet (4 active OmniRoute listeners: 2 `omni-build` + 2 `omni-light`, janitor ceiling 4) (Closes #11965) ([#11967](https://github.com/diegosouzapw/OmniRoute/pull/11967)) +- Stop painting every fork PR into `release/**` red: `quality.yml` `Build (advisory)` is skipped (GitHub still reports `continue-on-error` failures as check FAILURE). Hosted `ubuntu-latest` cannot finish `npm run build` on this tree — same class as #11962 taking `build.yml` off the PR rail. `docker-publish.yml` amd64 now runs on the `.113` `omni-build` pool (31 GB / 32 cores, two listeners) with Turbopack, shares the `heavy-build-main` lane with `ci.yml` `Build` so it queues instead of becoming a third heavy, and keeps per-ref concurrency (a merge storm was starting 8 concurrent OOM builds). arm64 stays on `ubuntu-24.04-arm` with webpack — there is no ARM box. Fallback when `USE_VPS_RUNNER` is off: hosted amd64 + webpack (#11976). ([#12021](https://github.com/diegosouzapw/OmniRoute/pull/12021)) +- `docker-publish.yml` builds with webpack (`OMNIROUTE_USE_TURBOPACK=0`) on **both** arches, including amd64 on the `.113` `omni-build` pool. Turbopack had the RAM (31 GB, `omniroute-113-6`) and still panicked (`TurbopackInternalError: there must be a path to a root`, run 33253576569, 26 min in). The same tree's arm64 webpack build on hosted `ubuntu-24.04-arm` succeeded (run 33264823398). Dockerfile already documents webpack as the Docker escape hatch (#11976). ([#12050](https://github.com/diegosouzapw/OmniRoute/pull/12050)) +- **docs(free-tier):** declare the counting vs deciding regimes for "is it free?" and guard the deciding path from DB-backed catalog resolution ([#12226](https://github.com/diegosouzapw/OmniRoute/pull/12226)) — thanks @maxmad64bis +- **docs(auto-combo):** The mode pack table in `docs/routing/AUTO-COMBO.md` now lists all six shipped packs with every weight each one sets, replacing a four-pack table whose numbers had also drifted from the source. It states plainly that no pack sets `quality`, so selecting any pack silences the observed-quality signal. Six more documents that quote the scoring factor count joined the `check:docs-counts` gate, which caught five stale claims — including one naming nine factors that do not exist — and two stale mode pack counts. The dashboard routing panel, which offered four of the six packs and labelled the default strategy "6-Factor Scoring", is now covered by a test; the two packs it was missing are `reliability-first` and `chaos-mode`, the latter labelled as the fault-injection profile it is rather than as one more routing preference ([#12316](https://github.com/diegosouzapw/OmniRoute/pull/12316)) — thanks @maxmad64bis +- **chore(quality):** rebaseline `src/lib/db/apiKeys.ts` for the ACL the key-creation path now preserves ([#12352](https://github.com/diegosouzapw/OmniRoute/pull/12352)) ([#12673](https://github.com/diegosouzapw/OmniRoute/pull/12673)) — thanks @KrzysiekSko +- **chore(providers):** bump the Claude Code wire identity and the Devin bridge image pin from `2.1.220` to `2.1.258` ([#12402](https://github.com/diegosouzapw/OmniRoute/pull/12402)) — thanks @ggiak ([#12604](https://github.com/diegosouzapw/OmniRoute/pull/12604)) +- **docs(env):** align `.env.example`, the README Bun section, and the troubleshooting guide with the code: `OMNIROUTE_USE_TURBOPACK` also governs `npm run build` (not dev-only), `bun run build` follows that flag instead of auto-selecting Webpack, `OMNIROUTE_CHAT_MAX_HEAVY_IN_FLIGHT` is unset by default (no request-count cap), and the structural `503 chat_admission_busy` message matches `chatAdmissionResponses.ts` (#12404 — thanks @pacocartones) +- **chore(electron):** upgrade the desktop app to Electron 44 (Chromium 152, Node 24.18.1) ([#12217](https://github.com/diegosouzapw/OmniRoute/pull/12217)). **Requires macOS 13 (Ventura) or later** — Chromium dropped macOS 12 (Monterey), so Monterey users must stay on an earlier OmniRoute desktop build. Windows and Linux are unaffected; the app already shipped only x64/arm64, so Electron 44 dropping 32-bit builds changes nothing. Removes the `openAsHidden`/`wasOpenedAsHidden` login-item fields deleted in Electron 44 — hidden autostart continues to work through the `--hidden` argument registered with the login item ([#12554](https://github.com/diegosouzapw/OmniRoute/pull/12554)) — thanks @app/dependabot +- **chore(quality):** rebaseline `src/sse/handlers/chat.ts` for the effective-input persistence the continuation fix needs ([#12641](https://github.com/diegosouzapw/OmniRoute/pull/12641)) ([#12680](https://github.com/diegosouzapw/OmniRoute/pull/12680)) — thanks @hartmark +- `Coverage` job on `ci.yml`: the informational Codecov upload gets its own 5-minute ceiling and `continue-on-error`, and the job budget grows from 20 to 30 minutes (the 8-shard c8 merge alone takes ~10) — a stalled upload no longer ends the job `cancelled` and drags a fully green `main` run's conclusion down with it ([#11972](https://github.com/diegosouzapw/OmniRoute/pull/11972)) +- CI hardening on the PR rail: the four `Unit Tests fast-path` shards get `timeout-minutes: 30` (a hung shard held a PR for 64 min instead of GitHub's 6 h default) and both ESLint file caches lose their `restore-keys` fallback, so a cache built under another suppressions file or lint config can no longer report stale verdicts (Refs #11600, #11924) ([#11963](https://github.com/diegosouzapw/OmniRoute/pull/11963)) +- Every CI job that runs a `next build` (`build`, the npm `publish`, both release-green validations) now targets the `omni-build` runner label, which only two of the eight self-hosted runners carry. The box holds one build comfortably and two at the edge; a third now queues on GitHub instead of being OOM-killed by the kernel. ([#11932](https://github.com/diegosouzapw/OmniRoute/pull/11932)) +- **refactor(combo):** move `handleRoundRobinCombo` (and `resolveTargetTokenLimit`) into `open-sse/services/combo/roundRobinCombo.ts`. `combo.ts` drops from 2164 to 1014 lines (`split("\n").length`); the leaf is 1199 (under the 1200 new-file cap). The round-robin call site uses a dynamic `import()` so `releaseStickyPinOnFailure` / `clearStaleLKGP` can stay exported from `combo.ts` without a static cycle. Skip / sticky / semaphore / safety-timer behavior is unchanged. ([#12811](https://github.com/diegosouzapw/OmniRoute/pull/12811)) — thanks @HouMinXi +- **chore(quality):** rebaseline the file-size caps the error-boundary campaign grew past (`open-sse/executors/codex.ts`, `open-sse/vendor/codex-chatgpt-web/bridge.ts`, both via [#12444](https://github.com/diegosouzapw/OmniRoute/pull/12444)) ([#12654](https://github.com/diegosouzapw/OmniRoute/pull/12654)) +- **chore(quality):** rebaseline the file-size caps the hartmark batch grew past (`combos/page.tsx` via [#12355](https://github.com/diegosouzapw/OmniRoute/pull/12355), `open-sse/services/combo.ts` via [#12338](https://github.com/diegosouzapw/OmniRoute/pull/12338)) ([#12623](https://github.com/diegosouzapw/OmniRoute/pull/12623)) — thanks @hartmark +- **chore(quality):** rebaseline the file-size caps the HouMinXi batch grew past when its PRs stacked (`providers/page.tsx`, `chatCore.ts`, `accountFallback.ts`) — each PR measured correctly in isolation, none saw the stacking ([#12619](https://github.com/diegosouzapw/OmniRoute/pull/12619)) +- **chore(quality):** rebaseline `open-sse/services/combo.ts` for the reset-aware scoring the HouMinXi batch stacked ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) ([#12678](https://github.com/diegosouzapw/OmniRoute/pull/12678)) — thanks @HouMinXi +- **docs:** sync provider catalog count (direct commit `4f11b2ae3d`) +- **docs(skills):** sync omni-inference search fallback (direct commit `b4e76a7ed9`) +- **chore(lint):** prune two stale base suppressions (direct commit `1325047d56`) +- **test(auto-combo):** tolerate full-suite rotation load (direct commit `ad3e293f9f`) +- **test:** drain inherited quality snapshot reds (direct commit `21ed68d8ac`) +- **chore(quality):** rebaseline measured CLI bundle growth (direct commit `d35b3f9779`) +- **test(ui):** reproduce invalid AnySearch icon alias (direct commit `12cc6ca834`) +- **test(translator):** fix the relative imports of the relocated deferred-finish test (direct commit `777d9d1629`) +- **docs(counts):** bring the documented provider and migration counts to the measured values (direct commit `e70bea30e9`) +- **test(security):** drop the guard-target set left unused by the sync (direct commit `a32ba0e362`) +- **chore(db):** renumber model_capabilities migration 163 → 169 (cross-branch collision with 163_radar_feed_cache_generated_at) (direct commit `70f5d4cbf9`) +- **docs(cli):** document CLI_PRIME_AGENT_BIN and correct the CLI Agents count (direct commit `9058e39b61`) +- **chore(deps):** refresh runtimes and adopt ESLint 10 ([#11259](https://github.com/diegosouzapw/OmniRoute/pull/11259)) — thanks @backryun +- **refactor(local-corpus):** implement dynamic root resolution and LRU cache ([#11491](https://github.com/diegosouzapw/OmniRoute/pull/11491)) — thanks @raheemuddin786 +- **chore(quality):** prune stale eslint-suppressions entries ([#11596](https://github.com/diegosouzapw/OmniRoute/pull/11596)) +- **chore(ci):** unblock quality gates - env-doc sync + stryker tap.testFiles registration (#9282) ([#11609](https://github.com/diegosouzapw/OmniRoute/pull/11609)) — thanks @Meet6338-X +- **chore(ci):** point the circular-deps gate at dpdm's real JS entrypoint ([#11615](https://github.com/diegosouzapw/OmniRoute/pull/11615)) — thanks @benzntech +- **chore(quality):** rebaseline file-size for #11448/#11495/#11561 growth ([#11630](https://github.com/diegosouzapw/OmniRoute/pull/11630)) +- **deps:** realign onnxruntime-node pin to @huggingface/transformers' 1.24.3 lockstep ([#11633](https://github.com/diegosouzapw/OmniRoute/pull/11633)) — thanks @jonlwheat2-gif +- **test(autoCombo):** port models_dev_tier #11508 guard from node:test to vitest ([#11635](https://github.com/diegosouzapw/OmniRoute/pull/11635)) — thanks @jonlwheat2-gif +- **chore(quality):** rebaseline file-size for #11083/#11631/#11628 growth ([#11636](https://github.com/diegosouzapw/OmniRoute/pull/11636)) +- **chore(lint):** prune stale ESLint suppressions (base-red #11449) ([#11644](https://github.com/diegosouzapw/OmniRoute/pull/11644)) — thanks @jonlwheat2-gif +- **docs(changelog):** aggregate the ten v3.8.50 fragments into CHANGELOG.md ([#11683](https://github.com/diegosouzapw/OmniRoute/pull/11683)) +- **chore(search):** drop unreachable credential sweep after #11642 ([#11687](https://github.com/diegosouzapw/OmniRoute/pull/11687)) — thanks @HouMinXi +- **ci:** add API route TypeScript regression gate ([#11705](https://github.com/diegosouzapw/OmniRoute/pull/11705)) — thanks @Sabeekhann +- **docs(dedup):** correct the CodeQL claim on the tenant-namespace comment ([#11712](https://github.com/diegosouzapw/OmniRoute/pull/11712)) +- **docs(changelog):** add consolidated v3.8.50 stats, top-25 ranking and a .mailmap ([#11715](https://github.com/diegosouzapw/OmniRoute/pull/11715)) +- **docs:** recommend gstack for AI-assisted workflows ([#11770](https://github.com/diegosouzapw/OmniRoute/pull/11770)) — thanks @santosraju99-hub +- **chore(quality):** tighten complexity/cognitive-complexity ratchets to the current tip; land the missed gateways.ts rebaseline ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **docs(i18n):** finish Freepik → Magnific rebrand in locale strings and README ([#11772](https://github.com/diegosouzapw/OmniRoute/pull/11772)) — thanks @turbolego +- **refactor(auto-combo):** fix divergent scoring in combo health reporting ([#11854](https://github.com/diegosouzapw/OmniRoute/pull/11854)) — thanks @jacobsparts +- **chore(changelog):** add missing fragments for #11919/#11918/#11916 ([#11938](https://github.com/diegosouzapw/OmniRoute/pull/11938)) +- **test(check):** escape the runs-on fixture with JSON.stringify, not a quote-only replace ([#11942](https://github.com/diegosouzapw/OmniRoute/pull/11942)) (direct commit `fb7445eaa3`) +- **test(release):** align five suites with the contracts #11933, #11919 and #11876 shipped on release/v3.8.51 ([#11944](https://github.com/diegosouzapw/OmniRoute/pull/11944)) +- **chore(quality):** remove unreachable code and restore test discovery ([#11950](https://github.com/diegosouzapw/OmniRoute/pull/11950)) — thanks @backryun +- **docs(agents):** sync-back landings are fast-forward, never squash ([#11964](https://github.com/diegosouzapw/OmniRoute/pull/11964)) +- **test(infra):** retry recursive temp-dir removal instead of failing a shard on ENOTEMPTY (#11966) ([#11968](https://github.com/diegosouzapw/OmniRoute/pull/11968)) +- **docs(ops):** the .113 heavy-build ceiling is one runner, not two ([#12048](https://github.com/diegosouzapw/OmniRoute/pull/12048)) +- **test(ui):** correct inactive auto-fetch expectation ([#12098](https://github.com/diegosouzapw/OmniRoute/pull/12098)) — thanks @RaviTharuma +- **docs:** document native dependency check escape hatch ([#12101](https://github.com/diegosouzapw/OmniRoute/pull/12101)) — thanks @RaviTharuma +- **test(providers):** regenerate the translate-path golden for OrcaRouter (#11923) ([#12118](https://github.com/diegosouzapw/OmniRoute/pull/12118)) +- **chore(quality):** velocity phase — loosen every numeric baseline by 20% until v4.0, monitor headroom nightly ([#12125](https://github.com/diegosouzapw/OmniRoute/pull/12125)) +- **chore(lint):** batch 0 of #12146 — type the call-log-cap sqlite rows instead of 45 as-any casts ([#12157](https://github.com/diegosouzapw/OmniRoute/pull/12157)) +- **chore(lint):** batch 4 of #12146 — shared/components react-hooks violations resolved ([#12159](https://github.com/diegosouzapw/OmniRoute/pull/12159)) +- **chore(lint):** batch 1 of #12146 — dashboard/cli-code react-hooks violations resolved ([#12160](https://github.com/diegosouzapw/OmniRoute/pull/12160)) +- **chore(lint):** batch 3 of #12146 — dashboard/settings react-hooks violations resolved ([#12162](https://github.com/diegosouzapw/OmniRoute/pull/12162)) +- **chore(lint):** batch 2 of #12146 — resolve the react-hooks compiler violations in dashboard/providers ([#12163](https://github.com/diegosouzapw/OmniRoute/pull/12163)) +- **chore(quality):** register search-432 cooldown test in stryker tap.testFiles ([#12170](https://github.com/diegosouzapw/OmniRoute/pull/12170)) +- **chore(lint):** batch 5 of #12146 — combos, endpoint, provider-stats, api-manager and costs react-hooks violations resolved ([#12174](https://github.com/diegosouzapw/OmniRoute/pull/12174)) +- **build:** add contributor fast profile ([#12192](https://github.com/diegosouzapw/OmniRoute/pull/12192)) — thanks @rafacpti23 +- **docs(readme):** README touch-ups ([#12193](https://github.com/diegosouzapw/OmniRoute/pull/12193)) ([#12194](https://github.com/diegosouzapw/OmniRoute/pull/12194)) ([#12202](https://github.com/diegosouzapw/OmniRoute/pull/12202)) — thanks @rafacpti23 +- **build:** force the webpack bundler in the contributor build profile ([#12197](https://github.com/diegosouzapw/OmniRoute/pull/12197)) — thanks @rafacpti23 +- **build:** skip standalone packaging in the contributor profile ([#12198](https://github.com/diegosouzapw/OmniRoute/pull/12198)) — thanks @rafacpti23 +- **docs(audit):** align every published number with the code and harden check:docs-counts ([#12200](https://github.com/diegosouzapw/OmniRoute/pull/12200)) +- **build:** stub the instrumentation entrypoints in the contributor profile ([#12203](https://github.com/diegosouzapw/OmniRoute/pull/12203)) — thanks @rafacpti23 +- **build:** omit the standalone output target for contributor builds ([#12204](https://github.com/diegosouzapw/OmniRoute/pull/12204)) — thanks @rafacpti23 +- **chore(lint):** batch 6 of #12146 — memory, radar, audit, analytics, cache, usage, activity, home and RequestLoggerV2 react-hooks violations resolved ([#12208](https://github.com/diegosouzapw/OmniRoute/pull/12208)) +- **docs(diagrams):** rename number-carrying diagram files to stable names ([#12210](https://github.com/diegosouzapw/OmniRoute/pull/12210)) +- **docs(dashboard):** redraw onboarding tier-flow SVGs for the real 4-tier model ([#12211](https://github.com/diegosouzapw/OmniRoute/pull/12211)) +- **docs(api):** document every implemented route in openapi.yaml (276 → 692 paths) ([#12212](https://github.com/diegosouzapw/OmniRoute/pull/12212)) +- **chore(lint):** batch 7 of #12146 — final src tail: 37 react-hooks violations across 33 files resolved ([#12244](https://github.com/diegosouzapw/OmniRoute/pull/12244)) +- Revert "docs: recommend gstack for AI-assisted workflows (#11770)" ([#12248](https://github.com/diegosouzapw/OmniRoute/pull/12248)) ([#12249](https://github.com/diegosouzapw/OmniRoute/pull/12249)) +- **docs:** Chaos Mode setup guide + weighted strategy semantics ([#12250](https://github.com/diegosouzapw/OmniRoute/pull/12250)) +- **docs(agents):** protected-surface merge rule — operator approval for agent-instruction files ([#12253](https://github.com/diegosouzapw/OmniRoute/pull/12253)) +- **chore(quality):** register native-codex turn-pin tests in stryker tap.testFiles ([#12263](https://github.com/diegosouzapw/OmniRoute/pull/12263)) +- **chore(quality):** dedupe tap.testFiles entries added by racing base-red fixes ([#12265](https://github.com/diegosouzapw/OmniRoute/pull/12265)) +- **docs(readme):** introduce OmniRouteTray — the macOS menu-bar companion ([#12276](https://github.com/diegosouzapw/OmniRoute/pull/12276)) — thanks @ggiak +- **chore(proxy):** purge the legacy 1proxy residue — sync/rotator modules, dead DB exports, dead settings tab and flag, docs (#12091) ([#12290](https://github.com/diegosouzapw/OmniRoute/pull/12290)) +- **chore(quality):** baseline-headroom skips generated and vendored files in the fileSize worst-file signal ([#12291](https://github.com/diegosouzapw/OmniRoute/pull/12291)) +- **refactor(video):** unify the JPEG frame data-URI contract ([#12322](https://github.com/diegosouzapw/OmniRoute/pull/12322)) +- **chore(deps):** freeze onnxruntime-node and eslint-plugin-react-hooks out of dependabot groups ([#12329](https://github.com/diegosouzapw/OmniRoute/pull/12329)) +- **test(executors):** restore the #10986 reasoning-only fallback guards ([#12364](https://github.com/diegosouzapw/OmniRoute/pull/12364)) — thanks @pacocartones +- **chore(quality):** re-tighten the file-size ratchet to the real LOC (plan 3.8.52 task 0) ([#12411](https://github.com/diegosouzapw/OmniRoute/pull/12411)) +- **test(grok):** format web executor suite ([#12412](https://github.com/diegosouzapw/OmniRoute/pull/12412)) +- **chore(quality):** ratchet open-sse typecheck baseline to zero ([#12418](https://github.com/diegosouzapw/OmniRoute/pull/12418)) +- **test(build):** guard the artifact path policy arrays against duplicates ([#12422](https://github.com/diegosouzapw/OmniRoute/pull/12422)) +- **chore(lint):** adopt eslint-plugin-react-hooks 7.1.1 ([#12428](https://github.com/diegosouzapw/OmniRoute/pull/12428)) +- **chore(providers):** remove a keyless provider integration at its operator's request ([#12440](https://github.com/diegosouzapw/OmniRoute/pull/12440)) +- **docs(providers):** register providers removed at their operator's request and guard against reintroduction ([#12478](https://github.com/diegosouzapw/OmniRoute/pull/12478)) +- **chore(quality):** base fixes — stryker tap.testFiles + node_modules cache key ([#12482](https://github.com/diegosouzapw/OmniRoute/pull/12482)) +- **docs(arch):** one-process recipe for tens of long /v1/responses ([#12493](https://github.com/diegosouzapw/OmniRoute/pull/12493)) — thanks @RaviTharuma +- **chore(quality):** tighten the CodeQL ratchet baseline from 11 to 6 ([#12530](https://github.com/diegosouzapw/OmniRoute/pull/12530)) +- **chore(deps):** pin browserslist override to ^4.28.8 ([#12592](https://github.com/diegosouzapw/OmniRoute/pull/12592)) — thanks @KooshaPari +- **chore(deps):** bump fast-uri to 3.1.7 in the electron lockfile ([#12601](https://github.com/diegosouzapw/OmniRoute/pull/12601)) +- **docs(quality):** document how the CodeQL ratchet refreshes and how to tighten it ([#12611](https://github.com/diegosouzapw/OmniRoute/pull/12611)) +- **test(ci):** pin the openapi-security-tiers two-arm contract with an executing gate test (#12581) ([#12652](https://github.com/diegosouzapw/OmniRoute/pull/12652)) +- **docs(gamification):** align XP Rewards table with code (#12501) ([#12667](https://github.com/diegosouzapw/OmniRoute/pull/12667)) — thanks @KooshaPari +- **refactor(combo):** split executeTarget into gates, attempt, and loop ([#12746](https://github.com/diegosouzapw/OmniRoute/pull/12746)) — thanks @HouMinXi +- **docs(security):** document socket.yml scanner config + CI workflow link (#12575) ([#12764](https://github.com/diegosouzapw/OmniRoute/pull/12764)) — thanks @KooshaPari +- **chore(ci):** guard commit identity in pre-commit to stop author misattribution ([#12772](https://github.com/diegosouzapw/OmniRoute/pull/12772)) +- **deps:** 17 Dependabot bumps — bump electron from 43.4.0 to 43.4.1 in /electron ([#11426](https://github.com/diegosouzapw/OmniRoute/pull/11426)); bump the development group across 1 directory with 8 updates ([#11428](https://github.com/diegosouzapw/OmniRoute/pull/11428)); bump the production group across 1 directory with 11 updates ([#11440](https://github.com/diegosouzapw/OmniRoute/pull/11440)); chore(deps): bump github/codeql-action/init from 4.37.7 to 4.37.8 ([#11925](https://github.com/diegosouzapw/OmniRoute/pull/11925)); chore(deps): bump github/codeql-action from 4.37.7 to 4.37.8 ([#11926](https://github.com/diegosouzapw/OmniRoute/pull/11926)); chore(deps): bump github/codeql-action/analyze from 4.37.7 to 4.37.8 ([#11927](https://github.com/diegosouzapw/OmniRoute/pull/11927)); build(deps): bump github/codeql-action/init from 4.37.8 to 4.37.9 ([#12345](https://github.com/diegosouzapw/OmniRoute/pull/12345)); build(deps): bump github/codeql-action/analyze from 4.37.8 to 4.37.9 ([#12346](https://github.com/diegosouzapw/OmniRoute/pull/12346)); bump the development group across 1 directory with 2 updates ([#12347](https://github.com/diegosouzapw/OmniRoute/pull/12347)); build(deps): bump github/codeql-action from 4.37.8 to 4.37.9 ([#12349](https://github.com/diegosouzapw/OmniRoute/pull/12349)); bump browserslist from 4.28.2 to 4.28.8 ([#12396](https://github.com/diegosouzapw/OmniRoute/pull/12396)); bump the production group across 1 directory with 4 updates ([#12399](https://github.com/diegosouzapw/OmniRoute/pull/12399)); bump @xmldom/xmldom ([#12500](https://github.com/diegosouzapw/OmniRoute/pull/12500)); bump qs from 6.15.2 to 6.16.0 ([#12512](https://github.com/diegosouzapw/OmniRoute/pull/12512)); bump @xmldom/xmldom from 0.9.10 to 0.9.12 ([#12513](https://github.com/diegosouzapw/OmniRoute/pull/12513)); bump fast-uri from 3.1.5 to 3.1.7 ([#12514](https://github.com/diegosouzapw/OmniRoute/pull/12514)); bump @humanfs/node from 0.16.7 to 0.16.8 ([#12515](https://github.com/diegosouzapw/OmniRoute/pull/12515)) +- Clear the `release/v3.8.51` `check:agent-skills-sync` base-red: regenerate `skills/cli-tunnel/SKILL.md` so the `tunnel create [type]` positional that #13009 taught the generator to read is reflected in the committed skill. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` typecheck base-red from #12731: the new `Mode pack ranking gates` candidates in `open-sse/services/autoCombo/__tests__/autoCombo.test.ts` omitted the required `provider`, `model` and `errorRate` fields of `ProviderCandidate`, failing `check:open-sse-typecheck` (and with it `Fast Quality Gates`) on every open PR. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` docs-sync base-red: EURouter (#13025) and GreenPT (#13024) took the live provider count to 358, leaving 7 STRICT drifts (`PROVIDER_REFERENCE.md`, 4 diagrams, 2 tier-flow images) plus the `AGENTS.md` / `llm.txt` / `package.json` count claims stale. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- **test(models):** Pin `CATALOG_BUILD_TIMEOUT_MS` in the #12058 canonical-catalog regression so a cold catalog build racing the 8s default bound no longer flakes the suite with a 500 ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Allow `@omniroute/opencode-plugin-v2/` in the npm pack policy — [#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870) shipped the v2 plugin beside its v1 sibling without widening the allowlist, so every packed file under it read as an unexpected artifact and failed `check:pack-policy` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Point the release-green validator's pack gate at the tree it is validating — the build-provenance guard defaulted to `origin/main`, which a release branch tip can never be an ancestor of mid-cycle, so `Package artifact` reported a false red on every run and the tarball boot-smoke cascaded off it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **docs(checks):** keep doc counts honest — headings, rankings, catalog, weights, quality gate and scoring diagram now covered ([#12507](https://github.com/diegosouzapw/OmniRoute/pull/12507)) — thanks @maxmad64bis +- **chore(lifecycle):** `check:model-lifecycle` now also diffs `DEFAULT_DEGRADATION_MAP` (the background-task redirect table) against the vendor lifecycle snapshot, refusing a retired id as source or target, with a table-driven unit test beside it. Three rows whose source the vendor had retired — `claude-sonnet-4-20250514`, `gemini-3-pro-preview` and `gpt-5.1-codex` (whose target `gpt-5.1-codex-mini` is retired too) — were dead code, since `checkLifecycle` answers 410 before the redirect runs; they are dropped (#12535 — thanks @pacocartones) +- **test(compression):** cover idle worker eviction at the resource level — the pool must call `terminate()` and must not retain the worker's `MessagePort`, complementing the `exit`-event assertion added with the fix ([#13371](https://github.com/diegosouzapw/OmniRoute/pull/13371)) +- **chore(quality):** raise the `imageGeneration.ts` file-size ceiling for the image-only-model guard that clears the #12945 base-red ([#13392](https://github.com/diegosouzapw/OmniRoute/pull/13392)) +- **chore(quality):** type the combo-test route's JSON response bodies instead of casting them to `any`, drop the now-empty suppression entry, and remove the `hasPerModelQuota` import `chatCore.ts` stopped using when the failure-classification helper was extracted ([#13379](https://github.com/diegosouzapw/OmniRoute/pull/13379)) +- **chore(electron):** the desktop bundle no longer ships the root-level files of every translated docs mirror (`README.md`, `llm.txt`, `CLAUDE.md`, `GEMINI.md`, `CONTRIBUTING.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`) — the packaged app only reads `docs/i18n//docs/**`, which stays. Saves ~11 MB on top of the translated CHANGELOGs already pruned. (#0000) ([#13235](https://github.com/diegosouzapw/OmniRoute/pull/13235)) +- **chore(quality):** retighten the `src/sse/handlers/chat.ts` unused-vars suppression count to the 8 violations that actually remain ([#13385](https://github.com/diegosouzapw/OmniRoute/pull/13385)) +- Bring the documented counts back in line with the code: 171 migrations (was 169) and 20 routing strategies (was 19), plus the regenerated `cli-tunnel` skill reference. ([#12970](https://github.com/diegosouzapw/OmniRoute/pull/12970)) +- Update the documented migration count to 172 after the call-logs provider-stats indexes landed. ([#13209](https://github.com/diegosouzapw/OmniRoute/pull/13209)) +- **chore(ci):** two gates that close the blind spots behind the exclusions above. `check:vitest-exclusions` requires every Vitest exclusion to name a tracking issue and to appear in `config/quality/vitest-exclusions.json` — the previous list grew to 62 files behind a comment pointing at an issue that had been closed for a month. `check-new-key-coverage` requires a key newly added to `en.json` to reach every locale; the existing coverage gate is a percentage floor per locale, so eleven absent keys out of ~13,000 left it at 99.9% while a whole feature shipped untranslated in nine languages. Both are diff-aware, so pre-existing debt stays frozen and neither needed a migration to turn on. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **build:** root layout no longer loads Inter from `next/font/google`, so a production image build does not need fonts.googleapis.com ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **docs(i18n):** bring the eight new `llm.txt` mirrors (km, kn, ml, my, ne, or, pa, si) up to the current provider and migration counts, so `check-docs-sync` stops rejecting every commit ([#13674](https://github.com/diegosouzapw/OmniRoute/pull/13674)) +- **refactor(ui):** flow surfaces (home topology, combo live studio, compression cockpit/waterfall/nodes, token-health badge) express state through the theme-aware `--orch-status-*` tokens instead of fixed dark-mode hex, so green/red/amber/grey stay legible in the light theme; dark mode is byte-identical. Categorical palettes (routing-strategy hues, compression-layer pills, provider brand colors) deliberately stay hex ([#12378](https://github.com/diegosouzapw/OmniRoute/issues/12378)) ([#13203](https://github.com/diegosouzapw/OmniRoute/pull/13203)) +- **chore(tests):** 51 test files that had been excluded from Vitest are running again, restoring roughly 350 assertions to the blocking suite. Each was measured individually first: of the 62 files parked behind the `// #8618 — pre-existing failure` comment, 51 pass against the current tree with no source change, so the exclusions had outlived the failures they were added for. The 11 that genuinely still fail stay excluded, but now point at a live tracker (#13204) rather than at #8618, which was closed in August while the list it tracked kept growing. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **chore(quality):** drop two ESLint suppression entries whose violations no longer exist, so `eslint --suppressions-location` stops rejecting every commit that touches the surrounding files ([#13384](https://github.com/diegosouzapw/OmniRoute/pull/13384)) +- **chore(release):** bring main's post-tag fixes into release/v3.8.51 — sync-back step 2/2 (direct commit `21c488f210`) +- **chore(deps):** patch toml and esbuild in the opencode plugin lockfiles ([#12965](https://github.com/diegosouzapw/OmniRoute/pull/12965)) +- **docs(changelog):** reconcile the v3.8.51 living section — fold 366 fragments, cover every cycle commit, credit every contributor ([#12971](https://github.com/diegosouzapw/OmniRoute/pull/12971)) +- **test(plugin):** wait for the v2 lazy-refresh tier instead of sleeping past it ([#12984](https://github.com/diegosouzapw/OmniRoute/pull/12984)) +- **chore(quality):** rebaseline chatCore.ts after the non-streaming regression fixes ([#13045](https://github.com/diegosouzapw/OmniRoute/pull/13045)) +- **docs(changelog):** add the missing bullet marker to the DeepSeek PoW fragment ([#13200](https://github.com/diegosouzapw/OmniRoute/pull/13200)) +- **chore(deps):** drain the Dependabot queue — 10 of 13 alerts ([#13213](https://github.com/diegosouzapw/OmniRoute/pull/13213)) +- **chore(ci):** make worktrees carry the hooks that enforce the identity gate ([#13236](https://github.com/diegosouzapw/OmniRoute/pull/13236)) +- **chore(deps):** pin csv-parse to ^7.0.2 via overrides (#13117) ([#13300](https://github.com/diegosouzapw/OmniRoute/pull/13300)) — thanks @KooshaPari +- **chore(deps):** bump hono from ^4.12.34 to ^4.13.7 (#13148) ([#13301](https://github.com/diegosouzapw/OmniRoute/pull/13301)) — thanks @KooshaPari +- **chore(deps):** pin joi to ^18.2.8 via overrides (#13085) ([#13302](https://github.com/diegosouzapw/OmniRoute/pull/13302)) — thanks @KooshaPari +- **docs(providers):** add Microsoft 365 Copilot (BizChat) provider guide (#12779) ([#13340](https://github.com/diegosouzapw/OmniRoute/pull/13340)) — thanks @KooshaPari +### 🙌 Contributors + +Thanks to everyone whose work landed in v3.8.51: + +| Contributor | PRs / Issues | +| --- | --- | +| [@5dive-bot](https://github.com/5dive-bot) | #11852 | +| [@abhisheksharma2411](https://github.com/abhisheksharma2411) | #11547, #11684, #11959, #12209 | +| [@adevwithpurpose](https://github.com/adevwithpurpose) | #11464 | +| [@adivekar-utexas](https://github.com/adivekar-utexas) | #12015, #12043, #12090, #12138 | +| [@AIB1TAL0S](https://github.com/AIB1TAL0S) | #12103 | +| [@alltomatos](https://github.com/alltomatos) | #11448 | +| [@alvinveroy](https://github.com/alvinveroy) | #11857, #12027, #12165 | +| [@amaleta](https://github.com/amaleta) | #11906, #11934 | +| [@amartinawi](https://github.com/amartinawi) | #11575 | +| [@AndrianBalanescu](https://github.com/AndrianBalanescu) | #11083, #11830, #11834, #11844 | +| [@anhtahaylove](https://github.com/anhtahaylove) | #13091, #13092, #13093, #13096, #13097, #13100, #13106, #13109, #13114, #13155, #13166, #13171, #13175, #13187, #13196 | +| [@aniruddhaadak80](https://github.com/aniruddhaadak80) | #11760 | +| [@app](https://github.com/app) | #12554 | +| [@arminanton](https://github.com/arminanton) | #11461, #11513 | +| [@AStupidBear](https://github.com/AStupidBear) | #11584 | +| [@atescivitci-cmd](https://github.com/atescivitci-cmd) | #12105 | +| [@b3nw](https://github.com/b3nw) | #11970, #11971, #12147 | +| [@backryun](https://github.com/backryun) | #11259, #11950, #12075, #12076, #12078, #12079, #12081, #12082, #12181, #12228, #12239, #12255, #12258, #12277, #12367, #12423, #12524, #12538 | +| [@benjaminkitt](https://github.com/benjaminkitt) | #11747 | +| [@benzntech](https://github.com/benzntech) | #11614, #11615 | +| [@Bl0ck154](https://github.com/Bl0ck154) | #11948, #11951, #11952, #11953, #11954 | +| [@brick30llc-ctrl](https://github.com/brick30llc-ctrl) | #11752, #12242, #12262 | +| [@Chewji9875](https://github.com/Chewji9875) | #11455, #11875, #12028 | +| [@cheynetom](https://github.com/cheynetom) | #12058 | +| [@davidebaraldo](https://github.com/davidebaraldo) | #12222, #12304 | +| [@davidlinfr](https://github.com/davidlinfr) | #6390 | +| [@ddarkr](https://github.com/ddarkr) | #12124 | +| [@Deftera186](https://github.com/Deftera186) | #11809 | +| [@dpozimski](https://github.com/dpozimski) | #11945 | +| [@DW-MediaLab](https://github.com/DW-MediaLab) | #12229 | +| [@echel0nn](https://github.com/echel0nn) | #11923 | +| [@f9td56dbgh-hub](https://github.com/f9td56dbgh-hub) | #11660 | +| [@fabioluissilva](https://github.com/fabioluissilva) | #11991 | +| [@foreveryh](https://github.com/foreveryh) | #12177 | +| [@geek007git](https://github.com/geek007git) | #12115, #12116, #12117, #12120, #12122 | +| [@geekyNads](https://github.com/geekyNads) | #11784, #12266 | +| [@ggiak](https://github.com/ggiak) | #11561, #12276, #12350, #12402 | +| [@gonisulaimann](https://github.com/gonisulaimann) | #12368, #12369, #12371 | +| [@Gorillaz322](https://github.com/Gorillaz322) | #12207 | +| [@groovecityJO](https://github.com/groovecityJO) | #12682 | +| [@hartmark](https://github.com/hartmark) | #11434, #11452, #11473, #11499, #11703, #11983, #11984, #11985, #11986, #11988, #11989, #11990, #11994, #12221, #12293, #12445, #12446, #12447, #12448, #12460, #12461, #12623, #12646, #12650, #12680, #12717, #12718, #12727, #12741, #12854 | +| [@hizzt](https://github.com/hizzt) | #11894 | +| [@hongnoul](https://github.com/hongnoul) | #11484 | +| [@HouMinXi](https://github.com/HouMinXi) | #11411, #11414, #11512, #11518, #11520, #11641, #11642, #11643, #11687, #11779, #11849, #11850, #11851, #11915, #11916, #11918, #11919, #11920, #12013, #12017, #12033, #12042, #12106, #12139, #12166, #12169, #12171, #12205, #12213, #12312, #12325, #12487, #12488, #12495, #12504, #12557, #12566, #12590, #12591, #12624, #12626, #12632, #12637, #12678, #12696, #12697, #12711, #12733, #12746, #12767, #12770, #12789, #12803, #12805, #12811, #12866, #12868, #12899, #12926, #12934, #12950, #12951, #12974, #13001, #13006, #13011, #13017, #13026, #13027, #13034, #13035, #13038, #13042, #13050, #13060, #13061, #13069, #13107, #13120, #13136, #13195, #13197 | +| [@Hsia97](https://github.com/Hsia97) | #11624 | +| [@jacobsparts](https://github.com/jacobsparts) | #11854, #12155, #12167 | +| [@joglomedia](https://github.com/joglomedia) | #11980 | +| [@jonlwheat2-gif](https://github.com/jonlwheat2-gif) | #11443, #11450, #11549, #11567, #11608, #11633, #11635, #11640, #11644, #11671, #11672, #11673, #11674, #11675, #11676, #11677, #11706, #11783, #12051, #12052, #12053, #12055 | +| [@kanade-hoshino](https://github.com/kanade-hoshino) | #12180 | +| [@Karan825](https://github.com/Karan825) | #11861 | +| [@KaspaPulse](https://github.com/KaspaPulse) | #11389, #11469, #11628, #11666, #11910 | +| [@keeltrace](https://github.com/keeltrace) | #12080, #12223 | +| [@killer30001000](https://github.com/killer30001000) | #12178 | +| [@KooshaPari](https://github.com/KooshaPari) | #12470, #12592, #12667, #12699, #12703, #12706, #12764, #12769, #12771, #13300, #13301, #13302, #13340, #13401, #13403, #13404, #13405, #13406, #13407, #13409, #13410, #13411, #13412, #13413, #13414, #13418, #13424, #13427, #13433, #13523, #13524, #13525, #13528, #13531, #13532, #13534, #13539, #13543, #13545, #13546, #13547, #13550, #13553 | +| [@kriptoburak](https://github.com/kriptoburak) | #11370 | +| [@KrzysiekSko](https://github.com/KrzysiekSko) | #12425, #12432, #12673 | +| [@marcelokarval](https://github.com/marcelokarval) | direct commit / report | +| [@maxmad64bis](https://github.com/maxmad64bis) | #1622, #11435, #11437, #11441, #11537, #11550, #11553, #11555, #11557, #11812, #11842, #11843, #11903, #12151, #12214, #12215, #12218, #12226, #12314, #12316, #12317, #12318, #12319, #12320, #12321, #12507, #12715, #12731, #12744, #12786, #12787, #12788, #12790, #12792, #12794, #12795, #12828, #12832, #12853, #12857, #12870, #12937, #12941, #12975, #13141, #13142, #13146, #13147 | +| [@mdigitalbh81](https://github.com/mdigitalbh81) | #12240 | +| [@Meet6338-X](https://github.com/Meet6338-X) | #11598, #11609, #12110 | +| [@morpheus9393](https://github.com/morpheus9393) | #11943 | +| [@MumuTW](https://github.com/MumuTW) | #11492, #11502, #11506, #11507, #11626, #11675, #11685, #11728, #11746, #11888, #11889, #11890, #11892 | +| [@Neuron-Mr-White](https://github.com/Neuron-Mr-White) | #11622, #11801, #12256 | +| [@NightStalker-87](https://github.com/NightStalker-87) | #12183 | +| [@NoxzRCW](https://github.com/NoxzRCW) | #11879, #11880, #11881, #11882, #11883, #11935 | +| [@ntdat812](https://github.com/ntdat812) | #11585, #12095 | +| [@ntdatt812](https://github.com/ntdatt812) | #11368, #11573, #11574, #11576, #11577, #11580, #11582, #11583, #11588, #11589, #11590, #11591, #11592, #11593, #11672, #12177, #12180, #12873, #12918, #12920, #12921, #12925, #12930, #13007, #13009, #13024, #13025, #13083, #13087, #13101, #13104, #13110 | +| [@official-burak](https://github.com/official-burak) | #11542 | +| [@opensource-elearning](https://github.com/opensource-elearning) | #12179, #12189, #12278, #12286 | +| [@oyi77](https://github.com/oyi77) | #11408, #11409, #11421, #11505, #11677, #12036, #12110 | +| [@pacocartones](https://github.com/pacocartones) | #11521, #11522, #11527, #11528, #11529, #11530, #11531, #11532, #11533, #11534, #11595, #11599, #11603, #11604, #11605, #11607, #11610, #11676, #11714, #11716, #11718, #11767, #11838, #11860, #11862, #11869, #11871, #11872, #11873, #11903, #11906, #11921, #11934, #12356, #12358, #12359, #12360, #12361, #12362, #12364, #12365, #12373, #12374, #12375, #12376, #12377, #12379, #12380, #12381, #12386, #12387, #12389, #12390, #12394, #12395, #12397, #12401, #12403, #12404, #12522, #12523, #12535, #12536, #12540, #12541, #12543, #12545, #12548, #12549, #12550, #12551, #12552, #12647, #12651, #12653, #13055 | +| [@patrykkopycinski](https://github.com/patrykkopycinski) | #11936, #11937, #12224 | +| [@PauloFH](https://github.com/PauloFH) | #11509 | +| [@PauloHSOliveira](https://github.com/PauloHSOliveira) | #12241 | +| [@ponkcore](https://github.com/ponkcore) | #12054 | +| [@Prajeeth-12](https://github.com/Prajeeth-12) | #11634, #12046 | +| [@quiterunner-commits](https://github.com/quiterunner-commits) | #12131, #12143 | +| [@rafacpti23](https://github.com/rafacpti23) | #11554, #11558, #12192, #12197, #12198, #12202, #12203, #12204 | +| [@ragnar-claude](https://github.com/ragnar-claude) | #11564 | +| [@raheemuddin786](https://github.com/raheemuddin786) | #11491, #11839, #11840, #11841, #12003, #12230, #12231, #12232, #12233, #12234 | +| [@rahilmavani](https://github.com/rahilmavani) | #11761 | +| [@Rahulsharma0810](https://github.com/Rahulsharma0810) | #11771 | +| [@RaviTharuma](https://github.com/RaviTharuma) | #11710, #11727, #11797, #11798, #11802, #11805, #11806, #11811, #12098, #12099, #12101, #12449, #12452, #12472, #12473, #12493, #12533, #12607, #12628, #12631, #12636, #12875, #12876, #12880, #12882, #12884 | +| [@rezjalibd](https://github.com/rezjalibd) | #12186 | +| [@RhianB14](https://github.com/RhianB14) | #11692 | +| [@rifqiawl](https://github.com/rifqiawl) | #11517, #11519, #11969 | +| [@rqzbeh](https://github.com/rqzbeh) | #11390 | +| [@Sabeekhann](https://github.com/Sabeekhann) | #11699, #11705 | +| [@santosraju99-hub](https://github.com/santosraju99-hub) | #11755, #11770 | +| [@Seramicx](https://github.com/Seramicx) | #11568 | +| [@Siva010](https://github.com/Siva010) | #12191 | +| [@solstxce](https://github.com/solstxce) | #11597 | +| [@soroush5](https://github.com/soroush5) | #12691 | +| [@tenshiak](https://github.com/tenshiak) | #12279 | +| [@TheDemonTuan](https://github.com/TheDemonTuan) | #11468, #11470, #11471, #11482, #11548, #11758, #11775, #11814 | +| [@thomasmaerz](https://github.com/thomasmaerz) | #12834 | +| [@tuandinh0801](https://github.com/tuandinh0801) | #11454 | +| [@turbolego](https://github.com/turbolego) | #11621, #11762, #11772, #11774, #11781, #12216 | +| [@Tushar49](https://github.com/Tushar49) | #11392 | +| [@ujjawalkaushik1110](https://github.com/ujjawalkaushik1110) | #11863 | +| [@vermasomesh835](https://github.com/vermasomesh835) | #11794 | +| [@vsd2807](https://github.com/vsd2807) | #11565, #11619 | +| [@wahidsadik371-coder](https://github.com/wahidsadik371-coder) | #12126 | +| [@watchingdogs](https://github.com/watchingdogs) | #12031 | +| [@wildcard](https://github.com/wildcard) | #11620 | +| [@xiaoyaner0201](https://github.com/xiaoyaner0201) | #11460, #11662, #12083 | +| [@Xxx91n](https://github.com/Xxx91n) | #11690 | +| [@yourspraveen](https://github.com/yourspraveen) | #11146 | +| [@yxyxy](https://github.com/yxyxy) | #11671 | +| [@ZaimMarzuki](https://github.com/ZaimMarzuki) | #11960 | +| [@Zartharas](https://github.com/Zartharas) | #11340 | +| [@zero-executioner](https://github.com/zero-executioner) | #11631 | +| [@diegosouzapw](https://github.com/diegosouzapw) | maintainer | + +--- ## [3.8.50] — 2026-08-25 _Living section — regenerated 2026-08-12 from all cycle commits (cycle open `ed2db6cb19` → tip). Bullets carry the merged PR and its author; direct pushes listed separately._ diff --git a/docs/i18n/vi/CHANGELOG.md b/docs/i18n/vi/CHANGELOG.md index a4f03cacd7..30e613849e 100644 --- a/docs/i18n/vi/CHANGELOG.md +++ b/docs/i18n/vi/CHANGELOG.md @@ -8,16 +8,1119 @@ ## [3.8.51] — TBD -_Living section — cycle opened at the v3.8.50 freeze (parallel-cycle model). Bullets are aggregated from `changelog.d/` fragments at each `/generate-release` phase._ +_Living section — reconciled 2026-09-15 from all cycle commits (`091589089c` → `c0f92ec98a`, 916 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 | **123** | +| 📝 Commits in the cycle | **916** | +| 🔀 Pull requests referenced | **892** | +| 📋 Changelog entries | **919** | +| 🙌 Contributors credited in entries | **121** | +| 🤖 Automated dependency commits | 18 | + +**Entries by type** + +| Type | Count | +| --- | ---: | +| 🐛 Fixes | 592 | +| ✨ Features | 131 | +| 🧹 Chore | 93 | +| 📚 Docs | 42 | +| 🧪 Tests | 32 | +| ♻️ Refactor | 8 | +| 🏗️ Build | 7 | +| ⚡ Performance | 7 | +| ⚙️ CI | 2 | +| ⏪ Reverts | 2 | +| 📦 Dependencies | 2 | +| 🔒 Security | 1 | + +### 🏆 Top 25 contributors this cycle + +_By commits in `091589089c..c0f92ec98a`, author identities consolidated via `.mailmap` and the merged PR's GitHub login. Bots excluded._ + +| # | Contributor | Commits | +| ---: | --- | ---: | +| 🥇 | diegosouzapw | 323 | +| 🥈 | Bob.Hou (@HouMinXi) | 79 | +| 🥉 | Paco Cartones (@pacocartones) | 77 | +| 4 | Dizzle (@maxmad64bis) | 49 | +| 5 | Koosha Paridehpour (@KooshaPari) | 43 | +| 6 | Nguyen Thanh Dat (@ntdatt812) | 31 | +| 7 | Ravi Tharuma (@RaviTharuma) | 26 | +| 8 | Markus Hartung (@hartmark) | 22 | +| 9 | Webman (@jonlwheat2-gif) | 22 | +| 10 | backryun | 17 | +| 11 | anhtahaylove | 15 | +| 12 | Rafa Martins (@rafacpti23) | 10 | +| 13 | Syed Raheemuddin (@raheemuddin786) | 10 | +| 14 | Nguyễn Viết Tuấn (@TheDemonTuan) | 8 | +| 15 | MumuTW | 7 | +| 16 | Paijo (@oyi77) | 7 | +| 17 | Tobias Andersen (@turbolego) | 6 | +| 18 | Bl0ck (@Bl0ck154) | 5 | +| 19 | SHANMUGAPRIYAN (@geek007git) | 5 | +| 20 | KaspaPulse | 5 | +| 21 | Abhishek Sharma (@abhisheksharma2411) | 4 | +| 22 | Abhishek Divekar (@adivekar-utexas) | 4 | +| 23 | Andrew B. (@AndrianBalanescu) | 4 | +| 24 | NoxzRCW | 4 | +| 25 | opensource-elearning | 4 | ### ✨ New Features +- **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). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- Added Google AI Studio Gemini batch text-to-speech support through `POST /v1/audio/speech`. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- 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. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(routing):** subscription-first auto groupings — `auto/subscription` routes only through plan-included connections with a documented hard-stop overage and fails closed on exhaustion, while `auto/thrifty` orders the pool `subscription → keyless → free → cheap → premium` and steps up one rung at a time as each is exhausted. Billing class comes from a curated per-connection catalog (uncurated is treated as metered, never plan-included), both reuse STRICT_ZERO_COST's per-connection verification, and a quota reading whose `resetAt` has passed is now refreshed regardless of TTL so routing returns to plan capacity as soon as it resets ([#11146](https://github.com/diegosouzapw/OmniRoute/pull/11146)) — thanks @yourspraveen +- **feat(providers):** publish a management-authenticated versioned web-session credential contract from OmniRoute's canonical browser credential metadata ([#11340](https://github.com/diegosouzapw/OmniRoute/pull/11340)) — thanks @Zartharas +- **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)) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add Xquik X search with typed results, credential validation, REST routing, and MCP selection ([#11370](https://github.com/diegosouzapw/OmniRoute/pull/11370)) — thanks @kriptoburak +- **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)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(dashboard):** surface durable exclusive managed leases in the existing Sessions view, keeping leased clients visible across idle gaps while marking connections with in-flight work as active ([#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389)) — thanks @KaspaPulse +- **build(bun):** allow Turbopack bundler flag on Bun 1.4+ with configurable Webpack fallback ([#11471](https://github.com/diegosouzapw/OmniRoute/pull/11471)) — thanks @TheDemonTuan +- feat(api): add an opt-in `modelVisibilityAllowlist`/`modelVisibilityDenylist` settings pair to curate exactly which models `/v1/models` advertises, mirrored into every `auto/*` combo candidate pool so a denied model cannot be routed to via combo selection either (#11481) ([#11997](https://github.com/diegosouzapw/OmniRoute/pull/11997)) +- **feat(rankings):** the Free Provider Rankings page shows what each provider actually served over the last 24 h. It ranked by ELO alone, which left a provider that answers every call with an error in first place; the usage data was already served by the API but never requested. A provider with too small a sample shows a dash, not a number ([#11546](https://github.com/diegosouzapw/OmniRoute/pull/11546)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **feat(guardrails):** enforce a bounded, deterministic contract for Video Bridge transcripts — 256 cues, 4096 input code units and 4 KiB UTF-8 per cue, 64 KiB total text, malformed-Unicode rejection, focus-window scoping, cross-source reconciliation with contributing-source metadata, and a structural provenance trust boundary so caller JSON can never self-assert `embedded`/`audio-bridge` provenance ([#11652](https://github.com/diegosouzapw/OmniRoute/issues/11652)) ([#12009](https://github.com/diegosouzapw/OmniRoute/pull/12009)) +- **feat(video):** orchestrate optional Video Bridge audio extraction and Audio Bridge STT behind a dual opt-in (operator setting AND per-request signal) — a new loopback-only broker `mode=audio` operation shares the frame path's exact process queue, deadline, AbortSignal, and byte budgets to extract a bounded mono 16 kHz PCM WAV from the same already-downloaded video, then reuses the existing Audio Bridge transcription boundary; provider segment timing is preserved when available and marked coarse otherwise, and every failure degrades to a visual-only-safe partial instead of throwing (#11654). ([#12012](https://github.com/diegosouzapw/OmniRoute/pull/12012)) +- Add a tenant-bound Video Bridge drill-down lifecycle on top of the existing secure cache substrate: opaque hashed handles (never raw session/video identifiers), preview/standard/detail multiresolution variants resampled on read, response pagination capped at 8 frames and 32 MiB, and a new authenticated `/api/v1/video-bridge/drilldown` consumer route that stays disabled for remote access by default and denies cross-key access with the same response as a nonexistent handle (no existence oracle). ([#12006](https://github.com/diegosouzapw/OmniRoute/pull/12006)) +- **test(video):** Add the Video Bridge FU-07/FU-09 promotion-evidence harness (#11656) — a frozen Zod manifest schema covering the 8 required scenario kinds (static scenes, rapid cuts, late facts, fades, blur, small text, close events, visual prompt injection) with a minimum of 3 repetitions per case, deterministic declarative fixture recipes (`videoBridgePromotionFixtures.ts`), a pure medians/p95 metrics aggregator, a pure FU-07/FU-09 promotion-verdict evaluator applying the ticket's exact thresholds (missing token usage always holds), a digest-only persistence layer that never retains raw media or raw model responses, and a versioned per-model promotion allowlist shipped empty with every model defaulting to `hold`. The FU-07/FU-09 promotion verdicts themselves remain HOLD — they require a real evidence run against real models on VPS 192.168.0.15. ([#12008](https://github.com/diegosouzapw/OmniRoute/pull/12008)) +- **feat(video bridge):** "embedded" transcript provenance can now be legitimately earned instead of merely asserted — a bounded, allowlisted (`mov_text`/`subrip`/`webvtt`) subtitle probe runs through the loopback-only Video Bridge broker (at most 2 streams, 10s subdeadline bounded by the request deadline, 256 KiB output, 4096-code-unit lines), normalized through a bounded, ReDoS-safe WebVTT parser and Zod-validated end to end. The adapter always resolves to an explicit `success`/`absent`/`transient_failure` outcome — a subtitle failure never breaks the visual description path, and only a fingerprint-verified broker response (never a caller-declared label) can produce embedded cues (#11659). ([#12011](https://github.com/diegosouzapw/OmniRoute/pull/12011)) +- Default new Antigravity-family connections (agy CLI imports and Antigravity OAuth connects) to model auto-sync, so live model discovery lands in the synced catalog and `/v1/models` picks up freshly released upstream models (e.g. Gemini 3.7 Flash tiers) without code changes. Existing connections keep their current setting; the per-connection dashboard toggle remains the opt-out. (#11685 — thanks @MumuTW) ([#11887](https://github.com/diegosouzapw/OmniRoute/pull/11887)) +- **feat(ui):** enable React Compiler (`reactCompiler: true` + `babel-plugin-react-compiler`) for automatic memoization at build time ([#11783](https://github.com/diegosouzapw/OmniRoute/pull/11783)) — thanks @jonlwheat2-gif +- **feat(providers):** add SeekAi (`seekai.cc`) as an OpenAI-compatible New-API gateway — catalog id `seekai` (alias `ska`), `https://seekai.cc/v1`, live `/v1/models` via `passthroughModels`, aggregator-list membership so New-API balance detection can opt in. No referral/aff codes. ([#11786](https://github.com/diegosouzapw/OmniRoute/issues/11786)) ([#12557](https://github.com/diegosouzapw/OmniRoute/pull/12557)) — thanks @HouMinXi +- **feat(zai):** add GLM-5.3-Flash Coding Plan support (1M context, 128K output, vision, `low|high|max` reasoning) and route `zai` GLM-5.3-family API-key traffic through the OpenAI-compatible Coding Plan endpoint with native thinking defaults ([#11801](https://github.com/diegosouzapw/OmniRoute/pull/11801)) — thanks @Neuron-Mr-White +- **feat(combo):** choose how combo models are ordered — manual, provider, score, or name — via a sort control in the dashboard builder, persisted in `config.modelSort` and re-applied on load and after add ([#11812](https://github.com/diegosouzapw/OmniRoute/pull/11812)) — thanks @maxmad64bis +- **feat(free):** custom models can be marked free-tier via `customModels[].isFree`; `isFreeModel()` is the first door and `hidePaidModels` respects it even for providers outside the free budget ([#11843](https://github.com/diegosouzapw/OmniRoute/pull/11843)) — thanks @maxmad64bis +- **feat(nodejs):** add `5dive` as a `configure` target — `omniroute configure 5dive` / `omniroute setup-5dive` write a 5dive auth profile that points an agent fleet's `claude` seats at OmniRoute, with the root-only write, the loopback-vs-`https` endpoint rule and the per-seat model pin handled explicitly ([#11852](https://github.com/diegosouzapw/OmniRoute/pull/11852)) — thanks @5dive-bot +- **feat(sse):** treat `max` as a first-class reasoning-effort tier and clamp per model family (GLM 5.1+/DeepSeek V4+/Kimi K3+ keep native `max`; o1/MiniMax/Grok/Muse Spark clamp to their upstream ceiling) ([#11875](https://github.com/diegosouzapw/OmniRoute/pull/11875)) — thanks @Chewji9875 +- **feat(providers):** the provider plugin manifest now advertises a `usage-fetch` capability for the 40 providers that have a wired usage/quota fetcher, so external dashboards can read it from `GET /api/v1/provider-plugin-manifest` instead of parsing `open-sse/services/usage.ts` after every release. Discovery only — no new fetcher, no quota change, and the Dashboard quota widget stays gated by `USAGE_SUPPORTED_PROVIDERS`. `USAGE_FETCHER_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/fetcherProviders.ts`) and is re-exported from `services/usage.ts`, keeping the manifest module a light leaf instead of pulling the ~490-module usage dispatcher into the manifest route. ([#11903](https://github.com/diegosouzapw/OmniRoute/pull/11903)) — thanks @maxmad64bis / @pacocartones +- **feat(plugins):** `OMNIROUTE_PLUGINS_DIR` sets the directory the runtime plugin scanner reads — and the root the plugin manager installs into — overriding the `HOME`-derived default, so a Docker/K8s deployment can point straight at its bind-mounted plugin tree instead of moving `HOME` just to relocate the scan path. An image that exports no home no longer scans `/tmp/.omniroute/plugins` in silence: the resolved directory is logged once at startup as `scanner.dir_resolved`, naming the input that won. Unset, behaviour is unchanged. Distinct from the CLI-only `OMNIROUTE_PLUGIN_PATH`, which finds `omniroute-cmd-*` command packages and never reached this scanner ([#11906](https://github.com/diegosouzapw/OmniRoute/pull/11906)) — thanks @amaleta / @pacocartones +- **feat(leases):** add an explicit owner-authenticated status action that returns only the active lease's privacy-safe configured connection and provider labels, with generation fencing and no credential or internal-id disclosure ([#11910](https://github.com/diegosouzapw/OmniRoute/pull/11910)) — thanks @KaspaPulse +- **feat(providers):** import-from-file modal shows per-row API errors and ships a downloadable CSV template ([#12071](https://github.com/diegosouzapw/OmniRoute/issues/12071)) ([#12504](https://github.com/diegosouzapw/OmniRoute/pull/12504)) — thanks @HouMinXi +- **feat(providers):** dashboard search matches connection name and `baseUrl` so imported OpenAI-compat nodes surface on the provider card ([#12108](https://github.com/diegosouzapw/OmniRoute/issues/12108)) ([#12495](https://github.com/diegosouzapw/OmniRoute/pull/12495)) — thanks @HouMinXi +- **feat(routing):** add a `score` Auto router strategy that selects the highest configured weighted score and reuses `explorationRate`. ([#12155](https://github.com/diegosouzapw/OmniRoute/pull/12155)) — thanks @jacobsparts +- Add a runtime feature flag to disable universal context handoffs globally without changing the default behavior. ([#12167](https://github.com/diegosouzapw/OmniRoute/pull/12167)) — thanks @jacobsparts +- **feat(providers):** the provider plugin manifest now also advertises a `usage-supported` capability for the 46 providers whose usage API is accepted by the server and Dashboard routes, so integrators can distinguish "the server will serve quota for this provider" from "a fetcher is wired" without reading TypeScript. Discovery only — no fetcher or quota change. `usage-fetch` resolves on id or alias (the usage dispatcher accepts both); `usage-supported` resolves on id alone, matching the runtime guard `USAGE_SUPPORTED_PROVIDERS.includes(providerId)`. `USAGE_SUPPORTED_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/supportedProviders.ts`) and is re-exported from `providers.ts`, mirroring the `fetcherProviders` leaf from #11903 and keeping the manifest a light module. ([#12214](https://github.com/diegosouzapw/OmniRoute/pull/12214)) — thanks @maxmad64bis +- **feat(rankings):** order Free Provider Rankings by what each provider actually served — `GET /api/free-provider-rankings?sortBy=reliability` and a "Most reliable first" toggle on the page. Providers with too few calls to state a success rate keep their score order below the measured ones; the default order is unchanged ([#12218](https://github.com/diegosouzapw/OmniRoute/pull/12218)). — thanks @maxmad64bis +- **perf(sse):** defer `cloneLogPayload()` in the structured SSE collector until after the `maxEvents`/`maxBytes` cap check, eliminating ~9,800 wasted `structuredClone` calls per streaming response (65–71% faster `push()`). Reducer snapshot isolation restored for OpenAI and Responses summaries ([#12241](https://github.com/diegosouzapw/OmniRoute/pull/12241)) — thanks @PauloHSOliveira ([#12243](https://github.com/diegosouzapw/OmniRoute/pull/12243)) +- **feat(usage):** Devin CLI agentic quota (Codeium seat-management GetUserStatus) and OpenRouter key limits plus account credits now surface in Provider Limits ([#12256](https://github.com/diegosouzapw/OmniRoute/pull/12256) — thanks @Neuron-Mr-White) +- **feat(settings):** persist `headroomUrl` through Settings so status/start use the operator URL instead of only `HEADROOM_URL` ([#12306](https://github.com/diegosouzapw/OmniRoute/issues/12306)) ([#12487](https://github.com/diegosouzapw/OmniRoute/pull/12487)) — thanks @HouMinXi +- **feat(auto-combo):** Auto-Combo scoring can now weigh how often a provider/model has actually succeeded. The engine already carried that number on every candidate — 24 hours of usage history behind a ten-sample floor, real-time metrics otherwise — and the scoring function never read it, while the weight table described `stability` as if it did. `reliability` (`1 - failureRate`, with the same field precedence and the same rate-bounding the speed ranking already uses, so a corrupt reading means "nothing observed" rather than "fails every call") is now a declared factor shipping at weight `0`, so routing is unchanged until an operator gives it one, and the `stability` description now matches what that factor computes ([#12317](https://github.com/diegosouzapw/OmniRoute/pull/12317)) — thanks @maxmad64bis +- **feat(routing):** With `freeAccessPolicy: "strict"`, the read-only candidate listing (`GET /v1/auto-combo/{channel}/candidates`) no longer hides the candidates the zero-cost guard excludes — the same read-only transparency the resilience filter already honours (#9133). Each candidate now carries `freeAccessExclusion` saying why it would be kept out, and it tells an exhausted allowance apart from a quota reading that never arrived or went stale, which used to look identical from the outside. Routing is unchanged: the listing reports, it never enforces. The separate `excludeTosAvoid` guard still drops its candidates without a reason; that gap is now documented rather than closed ([#12319](https://github.com/diegosouzapw/OmniRoute/pull/12319)) — thanks @maxmad64bis +- **feat(radar):** The Radar catalog table now shows two facts it was already receiving from the feed and dropping on the floor: the per-model rate limits (requests and tokens, per minute and per day) in a new column, and a badge when a provider's terms state it may train on the prompts you send. A limit of zero renders as zero rather than "rate-only" — for a ceiling those are opposite facts — and a model with no training statement gets no badge, because an absent statement is not a guarantee ([#12320](https://github.com/diegosouzapw/OmniRoute/pull/12320)) — thanks @maxmad64bis +- **feat(radar):** explain Community, single-use, contributor, supporter, recovery, abuse, offers, and privacy rules before either Radar activation action, and remove the superseded fixed-PR grant promise from every UI locale ([#12342](https://github.com/diegosouzapw/OmniRoute/pull/12342)) +- **feat(gamification):** the dashboard Profile page now shows the real daily streak — `/api/gamification/level` returns `streak: { current, longest }` (per key with `apiKeyId`, operator-wide maximum otherwise) and the streak card reads it instead of a hard-coded 0 (#2403) ([#12377](https://github.com/diegosouzapw/OmniRoute/pull/12377)) — thanks @pacocartones +- **feat(gamification):** the dashboard leaderboard now shows each API key's display name under the Name column instead of a truncated key id; `GET /api/gamification/leaderboard` attaches `name` per entry (name only — no key material), while the shared ranking helper and the federation leaderboard stay id-only — thanks @pacocartones ([#12385](https://github.com/diegosouzapw/OmniRoute/pull/12385)) +- **feat(gamification):** enforce the documented 1000 XP/min per-API-key anti-cheat rate limit on the XP award path; over-limit awards are logged and skipped instead of persisted, and the sliding window now matches the timestamp format stored in `xp_audit_log` ([#2403](https://github.com/diegosouzapw/OmniRoute/issues/2403)) ([#12390](https://github.com/diegosouzapw/OmniRoute/pull/12390)) — thanks @pacocartones +- **feat(admin):** localize the gamification anomalies page — the loading state, the Status column and the Suspicious badge now come from the `common` catalog (new `common.suspicious` key propagated to every locale) — add it to the Gamification sidebar group as `gamification-admin` (`/dashboard/gamification/admin`), and expose the loading and empty states as polite `role="status"` live regions (#12401 — thanks @pacocartones) +- **feat(providers):** skip GitHub combo members missing from the live synced catalog, and drop Copilot models that are policy-disabled or hidden from the model picker ([#12473](https://github.com/diegosouzapw/OmniRoute/pull/12473)) — thanks @RaviTharuma +- **feat(i18n):** locale-expansion tooling — `npm run i18n:add-locale` adds a language to config, dashboard, docs mirrors, CLI, README/indexes and site in one command; browser, CLI and cookie detection resolve `uk`, `fil`/`tl`, `zh-Hant` (and the retired legacy `in` → `id`) via config aliases; `config/i18n.json` now ships in the npm package so the published CLI can read it; new real-translation ratio gate (`npm run i18n:check-ratio`, advisory) with a per-locale ratchet baseline; `run-translation --adopt` restores `.i18n-state.json`; `sync-language-bars` generates 🌐 bars from config; `validate_translation.py` loads its allowlist again. Retires the duplicate `in` locale (Indonesian mislabelled as Hindi) — 42 honest locales; saved `NEXT_LOCALE=in` / `OMNIROUTE_LANG=in` keep working. (#12496) +- **feat(api):** Emit gateway-measured `tokens_per_second` (TTFT excluded) on streaming usage and `X-OmniRoute-Tokens-Per-Second` when first-token latency is known ([#12616](https://github.com/diegosouzapw/OmniRoute/issues/12616)) ([#12631](https://github.com/diegosouzapw/OmniRoute/pull/12631)) — thanks @RaviTharuma +- **feat(opencode-plugin): map gateway cost/usage/tok/s onto OpenCode inference payloads** — the official plugin copies `X-OmniRoute-Response-Cost`, token counts, `X-OmniRoute-Tokens-Per-Second` / `usage.tokens_per_second`, TTFT, and the winning `X-OmniRoute-Model` onto the JSON/SSE body OpenCode already consumes. Missing tok/s is left unset (never `tokens / latency`). (#12636) — thanks @RaviTharuma +- **feat(combo):** add combo strategy `quota-weighted`: skip exhausted accounts, then weighted-draw by leftover / in-flight load; existing conversations stay pinned until the account is empty ([#12789](https://github.com/diegosouzapw/OmniRoute/pull/12789)) — thanks @HouMinXi +- **feat(models):** Account live listings become the chat catalog source for Claude, Codex, Copilot, and AGY; public metadata only fills prices on IDs those accounts already list. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **fix(models):** Union `agy` and `antigravity` live catalogs so `agy/gemini-3.8-flash-high` is not rejected after the prefix folds to `antigravity`. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **feat(opencode):** Add `@omniroute/opencode-plugin-v2`, an opencode v2 plugin that gives a v2 install a route to an OmniRoute gateway: it publishes models, combos and auto-combos into the host catalog, refreshes them lazily behind a 300s TTL, and keeps serving the last known catalog from an on-disk snapshot when the gateway is unreachable. The v1 plugin (`@omniroute/opencode-plugin`) is untouched and keeps working as before. The plugin adapts to the host instead of assuming one shape: it reads the shape the host seeds into the catalog draft and publishes the `api` block, the pre-`api` top-level fields, or both. The gateway key comes from the host credential store when one is connected, so no secret needs to sit in a config file; a source the gateway refuses is named in a warning instead of silently degrading the catalog to raw model ids; and tool schemas bound for a Gemini model are stripped of the JSON-Schema keywords Gemini rejects, so tool calling survives that route ([#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870)) — thanks @maxmad64bis +- feat(services): show sanitized CLIProxyAPI account health from its authenticated management API without exposing credentials, file paths, or raw account metadata (#6342) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add AnySearch free web search + URL extract (webFetch) with typed results, credential validation, REST routing, and MCP selection - fallback-only ([#11690](https://github.com/diegosouzapw/OmniRoute/pull/11690)) — thanks @Xxx91n +- **feat(dashboard):** display clamped `[0, 100]%` cached input token ratio in request logs table ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) — thanks @b3nw +- **feat(catalog):** add `OMNIROUTE_DISABLE_THINKING_LEVEL_VARIANTS` feature flag to optionally filter out thinking level variants from model catalog ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) — thanks @b3nw +- **feat(dashboard):** continuously export call logs to external analytics stores. A pluggable destination registry ships the full Logs-tab record set on an hourly `JobRegistry` cron, with a persisted per-destination cursor, batched inserts, a config UI rendered from each destination's own field descriptors, and a REST layer (`/api/log-export/*`) for CRUD, a connection test, and an on-demand run. A destination can opt into `includeBodies` to also ship the request and response payloads shown in the Logs detail pane, including the client and provider views of each call; this is off by default, and payloads inherit the dashboard's PII sanitisation, secret redaction and `noLog` handling. Google BigQuery is the first destination, using a service-account key stored encrypted at rest and streaming inserts keyed by call-log id, into a table that is day-partitioned on `timestamp` and clustered on `api_key_name`, `provider`, `model` and `status`. ([#11945](https://github.com/diegosouzapw/OmniRoute/pull/11945)) — thanks @dpozimski +- **feat(providers):** add MaxAI as a signed, OpenAI-compatible provider serving its 13 paid chat models (GPT-5.6 / Luna / Thinking, Claude 5 Sonnet, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite, Grok 4.1-fast / 4.5, DeepSeek V3.2 / R1, Llama 3.3 70B) through OmniRoute's `/v1` endpoint, with per-request HMAC-SHA1→SM3→AES request signing, live model + context-window discovery from `/models/get_config`, and prompted tool-calling translated to OpenAI `tool_calls` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI vision input — image_url content parts are forwarded inline in `message_content` to the 6 vision-capable models (GPT-5.6 / Luna / Thinking, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite) ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI image generation — 6 image models (gpt-image-1, dall-e-3, flux-1-schnell/dev/pro, sd3-medium) exposed through `POST /v1/images/generations` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI document RAG — inline base64 file/document attachments are uploaded to MaxAI (content-addressed `doc_id`) and attached to the chat via `doc_list` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** browserless MaxAI onboarding — email device-pair login (`/api/providers/[id]/login`) and signed access-token refresh, so a connection can be created and kept fresh without a real browser or Google OAuth ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** per-provider TLS impersonation profile (MaxAI presents a Windows Firefox-150 client fingerprint) so its bot-sensitive endpoints accept OmniRoute traffic ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** add **Nimble** as a web-search and web-fetch provider (`nimble-search`) — `/v1/search` routes to Nimble's search API at `lite` depth (locale, freshness and include/exclude domain filters mapped onto the shared request contract), and `/v1/web/fetch` routes to Nimble Extract, which covers all four fetch formats (`markdown`, `html`, `links`, `screenshot`) from a single call. One API key serves both surfaces. ([#11620](https://github.com/diegosouzapw/OmniRoute/pull/11620)) — thanks @wildcard +- The npm publish is automatic again, through npm Trusted Publishing (OIDC): the hosted `stage-npm` job publishes with a short-lived credential minted from GitHub's id-token — no `NPM_TOKEN`, no 2FA prompt, provenance attached. `publish_mode=staged` (owner approves with 2FA) and `direct` (token) remain available on `workflow_dispatch`. ([#11931](https://github.com/diegosouzapw/OmniRoute/pull/11931)) +- **feat(providers):** Add **Opper** as an API-key gateway provider — EU-hosted AI gateway with 700+ models from 30+ providers behind one OpenAI-compatible API and one key (`OPPER_API_KEY`); model ids use `provider/model` format (e.g. `anthropic/claude-sonnet-4-6`, `openai/gpt-5`); live model catalog at `https://api.opper.ai/v3/compat/models`; entry mirrors `requesty` (same shape, `passthroughModels: true`, no static seed) ([#11629](https://github.com/diegosouzapw/OmniRoute/pull/11629)) +- **feat(dashboard):** the `/dashboard/orchestration` snapshot hook now subscribes to the `agents` WebSocket channel (`agent.task.updated`) instead of `requests` as its refetch trigger, and relaxes its background poll from 5s to 30s while that WS connection is up — falling back to the tighter 5s cadence, reprogrammed live on any connect/disconnect transition, whenever the socket is down. ([#12409](https://github.com/diegosouzapw/OmniRoute/pull/12409)) +- **feat(dashboard):** Orchestration canvas quick wins — search box plus state/source/provider filter chips with a one-click clear, and per-source collapse/expand, all reflected in the URL so a filtered/collapsed view is shareable and survives a refresh; the detail drawer gained a "copy trace JSON" action and hardened error/empty-state and accessibility handling; the Agents-tab edges now animate traveling particles along active (running) connections; and the canvas node/edge status colors moved off fixed hex values onto theme-aware `--orch-status-*` CSS custom properties, so they adapt correctly to light/dark mode. ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- **chore(dashboard):** Orchestration UI hardening pass and the missing component/model test coverage it called for — `OrchestratorNode`/`ActivityNode`/`OverflowNode` rendering, the `?node=`/overflow-click page routing, the Agents-tab orchestrator-click no-op and `showCompleted` toggle, and the overview kanban's done-column sort order (#12270, #12271). ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- New `/dashboard/orchestration` page: live unified view of everything running — Cloud Agent, A2A and Conductor as a real-time graph (Agents tab), the combo cascade (Routing tab, reusing the Combo Live Studio) and a state kanban (Overview tab), with a detail drawer (trace, cost, approve/cancel). Read-only over existing APIs — no new backend. Canvas concept credit: PR #11815 design ([#12261](https://github.com/diegosouzapw/OmniRoute/pull/12261)) +- **feat(dashboard):** new "History" tab on `/dashboard/orchestration` — an Airflow-style grid of finished runs over a 24h/7d/30d preset window, one row per (source, identity), clicking a cell opens the existing detail drawer. It is backed by real persistence: A2A task lifecycle transitions are now written to the `a2a_tasks` table (purged after 30 days, configurable via `OMNIROUTE_A2A_HISTORY_RETENTION_DAYS`) and served by the new `GET /api/a2a/tasks/history` listing endpoint, with the task-detail route falling back to persisted history once a run leaves the in-memory snapshot. Conductor runs stay remote and are not persisted locally — the tab says so instead of silently omitting them. ([#12479](https://github.com/diegosouzapw/OmniRoute/pull/12479)) +- **feat(dashboard):** the orchestration detail drawer gained a "Repeat" action for Cloud Agent, A2A and Conductor tasks — a two-click confirm (click once to arm, click again within the confirm window to fire) re-submits the original prompt/input as a new run. The button is disabled with an explanatory tooltip whenever the original input can't be recovered from the loaded task detail (e.g. it never carried a prompt, or the detail failed to load). Two limitations of the A2A variant, by design: it targets the `/a2a` JSON-RPC endpoint, which authenticates with an API key only (`REQUIRE_API_KEY=true` or a configured `OMNIROUTE_API_KEY` makes a dashboard-session repeat answer `HTTP 400` — surfaced verbatim in the drawer's error line, never as a success), and the `message/send` call is SYNCHRONOUS: the POST blocks for the whole skill run, so the success confirmation only appears once the repeated task finishes. A dashboard-authenticated A2A creation path is deliberately left to a follow-up — widening the endpoint's auth posture is an operator decision, not a side effect of this feature. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(a2a):** A2A task execution now records which memories were consulted for the task's last user message as `metadata.memoryHits` (id/key/type/content-snippet) plus a `memory_hits` history event, purely for observability — the retrieved memory is never injected into a skill's prompt or behavior. Gated by the `OMNIROUTE_A2A_MEMORY_HITS` kill-switch (default enabled; set to `0` to skip the recall lookup entirely). The drawer's new "Memory used" section lists these hits for a2a tasks and is omitted whenever there are none. Known limitation: recall only resolves under the keyless posture — a keyed caller's task owner is a SHA-256 prefix of the API key, while memory rows are keyed by the database api-key id, and no hash→id lookup exists today, so the hit list stays empty for keyed callers. The recorded hits are also kept out of the task's own `input` (and therefore out of the persisted input and of the "Repeat" request body), so repeating a task never re-sends the previous run's memory snippets. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(providers):** add a Perplexity Agent API provider (`perplexity-agent` / `pplx-agent`) for Perplexity `/v1/responses`, including the documented Anthropic, OpenAI, Google, xAI, DeepSeek, Z.AI, Moonshot/Kimi, NVIDIA, and Perplexity model IDs plus Anthropic-model `max_output_tokens` compatibility. ([#12103](https://github.com/diegosouzapw/OmniRoute/pull/12103)) — thanks @AIB1TAL0S +- **feat(providers):** add RPD (Requests Per Day) limit to provider rate limit overrides across UI, schemas, DB, and i18n ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) — thanks @b3nw +- **feat(providers): add UC Direct (uncensored.com Developer API), the metered OpenAI-compatible surface.** A standard OpenAI-compatible passthrough (default executor) for uncensored.com's official REST API at `https://api.uncensored.com/api/v1`: `X-api-key` auth (never-expiring `uai_sk_live_` key), `POST /chat/completions` with streaming SSE and native tool-calling, and the full live metered catalog (82 models across 15 providers, discovered from the public `GET /v1/models`). Registered as provider `uc-direct` (alias `ucd`). Complements the un-metered `uc` persona provider — same models, metered credits and a plain API key instead of a subscription session. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(providers): add UC (uncensored.com), the un-metered subscription "persona" chat as an OpenAI-compatible provider.** A WebSocket web-app port: a durable Clerk credential mints a short-lived session token per connect (browserless — no API key), driving UC's persona socket. Ships the browserless email-code login (request → verify → harvest), the 19 verified persona models (Claude Opus, Gemini, Grok, GLM, Kimi, DeepSeek, MiniMax, incl. the uncensored variants), prompted `` tool-calling with a per-model code-style dialect + auto-cure retry for guardrailed models, live ``/reasoning split, streaming + non-streaming OpenAI responses, and full quota/auth error surfacing (paywall / message-limit / rate-limit → 429, invalid session → 401 re-login). Full multimodal parity via the persona blob-upload layer: **vision** (image input, 15 vision-capable models), **document RAG** (PDF/doc upload, server-side extraction), **image generation** (22 models), **video generation** (14 models, async signed-url → poll), and **TTS** (streaming MP3). Registered as provider `uc` (alias `ucn`). The metered OpenAI-compatible Developer API is a separate `uc-direct` provider. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(logs):** add copy-all button to request detail modal ([#11083](https://github.com/diegosouzapw/OmniRoute/pull/11083)) — thanks @AndrianBalanescu +- **feat(check):** make serviceKinds required and add the reverse-walk provider consistency gate ([#11392](https://github.com/diegosouzapw/OmniRoute/pull/11392)) — thanks @Tushar49 +- **feat(providers):** only advertise verified-working connections by default ([#11448](https://github.com/diegosouzapw/OmniRoute/pull/11448)) — thanks @alltomatos +- **feat(resilience):** add hierarchical concurrency admission ([#11493](https://github.com/diegosouzapw/OmniRoute/pull/11493)) +- **feat(providers):** derive + surface expiry for JWT-bearing web cookies (#11497) ([#11505](https://github.com/diegosouzapw/OmniRoute/pull/11505)) — thanks @oyi77 +- **feat(tunnels):** support named cloudflared tunnels via CLOUDFLARED_CONFIG ([#11561](https://github.com/diegosouzapw/OmniRoute/pull/11561)) — thanks @ggiak +- **feat(compression):** make proactive context-compression threshold a live setting ([#11564](https://github.com/diegosouzapw/OmniRoute/pull/11564)) — thanks @ragnar-claude +- **feat(resilience):** add provider peak-hour protection ([#11622](https://github.com/diegosouzapw/OmniRoute/pull/11622)) — thanks @Neuron-Mr-White +- **feat(providers):** add 1min.ai provider ([#11631](https://github.com/diegosouzapw/OmniRoute/pull/11631)) — thanks @zero-executioner +- **feat(dashboard):** collapsible JSON tree viewer for request/response payloads ([#11703](https://github.com/diegosouzapw/OmniRoute/pull/11703)) — thanks @hartmark +- **feat(routing):** add LiquidAI LFM2.5-2.6B free tier via OpenRouter ([#11752](https://github.com/diegosouzapw/OmniRoute/pull/11752)) — thanks @brick30llc-ctrl +- **feat(pricing):** add GLM-5.3-Flash pricing, model specs, and catalog registration ([#11830](https://github.com/diegosouzapw/OmniRoute/pull/11830)) — thanks @AndrianBalanescu +- **feat(routing):** add relayMode for schema-locked context handoffs ([#11839](https://github.com/diegosouzapw/OmniRoute/pull/11839)) — thanks @raheemuddin786 +- **feat(config):** add support for runtime system prompt configuration and hot-reloading ([#11841](https://github.com/diegosouzapw/OmniRoute/pull/11841)) — thanks @raheemuddin786 +- **feat(routing):** expand connection-aware quota prefilter across combo strategies (#11682) ([#11850](https://github.com/diegosouzapw/OmniRoute/pull/11850)) — thanks @HouMinXi +- **feat(models):** reactive discovery sync after Antigravity model-not-found 404 ([#11888](https://github.com/diegosouzapw/OmniRoute/pull/11888)) — thanks @MumuTW +- **feat(models):** auto-aliases and token limits from synced Antigravity discovery ([#11889](https://github.com/diegosouzapw/OmniRoute/pull/11889)) — thanks @MumuTW +- **feat(settings):** raise sticky round-robin limit caps to 1000 ([#12015](https://github.com/diegosouzapw/OmniRoute/pull/12015)) — thanks @adivekar-utexas +- **feat(resilience):** operator-configurable global credential health check interval ([#12043](https://github.com/diegosouzapw/OmniRoute/pull/12043)) — thanks @adivekar-utexas +- **feat(quota):** use official OpenCode Go usage API ([#12124](https://github.com/diegosouzapw/OmniRoute/pull/12124)) — thanks @ddarkr +- **feat(resilience):** default the credential health check sweep to 60 minutes ([#12138](https://github.com/diegosouzapw/OmniRoute/pull/12138)) — thanks @adivekar-utexas +- **feat(quality):** new-code mode for the complexity and dead-code ratchets (Clean as You Code) ([#12142](https://github.com/diegosouzapw/OmniRoute/pull/12142)) +- **feat(dashboard):** orchestration canvas — unified model + snapshot hook (part 1/2) ([#12156](https://github.com/diegosouzapw/OmniRoute/pull/12156)) +- **feat(usage):** add Kilo Code balance and Kilo Pass quotas ([#12178](https://github.com/diegosouzapw/OmniRoute/pull/12178)) — thanks @killer30001000 +- **feat(providers):** refresh vendored ChatGPT Web connector to v4.0.7 ([#12181](https://github.com/diegosouzapw/OmniRoute/pull/12181)) — thanks @backryun +- **feat(providers):** manual "Clear cooldown" action in the cooling panel ([#12224](https://github.com/diegosouzapw/OmniRoute/pull/12224)) — thanks @patrykkopycinski +- **feat(providers):** restore ChatGPT Web via clean-room browser transport ([#12239](https://github.com/diegosouzapw/OmniRoute/pull/12239)) — thanks @backryun +- **feat(sse):** wire the PROVIDER_PROFILES window gate into the global provider cooldown ([#12247](https://github.com/diegosouzapw/OmniRoute/pull/12247)) +- **feat(providers):** modernize CLOVA Studio chat and embeddings ([#12277](https://github.com/diegosouzapw/OmniRoute/pull/12277)) — thanks @backryun +- **feat(browser):** adopt Obscura as primary headless browser engine with Chromium fallback ([#12286](https://github.com/diegosouzapw/OmniRoute/pull/12286)) — thanks @opensource-elearning +- **feat(sse):** full language parity for output styles (es/de/fr/it/ru/zh + autoDetect) ([#12289](https://github.com/diegosouzapw/OmniRoute/pull/12289)) +- **feat(quality):** complete test:scoped — --full map rebuild, stdin selection, CI loader parity (#8084 D1) ([#12353](https://github.com/diegosouzapw/OmniRoute/pull/12353)) +- **feat(providers):** refresh Fable, Cursor, and Devin catalogs ([#12367](https://github.com/diegosouzapw/OmniRoute/pull/12367)) — thanks @backryun +- **feat(video):** redact transcript text from logs and durable memory (#12150 P1) ([#12427](https://github.com/diegosouzapw/OmniRoute/pull/12427)) +- **feat(dashboard):** parent-link, genuine-continuation badge, and modal perf fixes ([#12448](https://github.com/diegosouzapw/OmniRoute/pull/12448)) — thanks @hartmark +- **feat(dashboard):** adaptive context-budget dial on compression panel ([#12488](https://github.com/diegosouzapw/OmniRoute/pull/12488)) — thanks @HouMinXi +- **feat(providers):** refresh Z.ai Web models and browser transport ([#12524](https://github.com/diegosouzapw/OmniRoute/pull/12524)) — thanks @backryun +- **feat(video):** redact raw client-snapshot transcript fields in the detailed log (#12150 P2) ([#12528](https://github.com/diegosouzapw/OmniRoute/pull/12528)) +- **feat(providers):** refresh NVIDIA hosted models ([#12538](https://github.com/diegosouzapw/OmniRoute/pull/12538)) — thanks @backryun +- **feat(quota):** Moonshot Open Platform balance and TPD lock for custom nodes ([#12590](https://github.com/diegosouzapw/OmniRoute/pull/12590)) — thanks @HouMinXi +- **feat(video):** redact transcript in the in-memory pending-request snapshot (#12430 item 6) ([#12596](https://github.com/diegosouzapw/OmniRoute/pull/12596)) +- **feat(dashboard):** link a log entry's Conversation Context to its owning conversation ([#12646](https://github.com/diegosouzapw/OmniRoute/pull/12646)) — thanks @hartmark +- **feat(catalog):** eligibility-gated free-tier bucket ([#12669](https://github.com/diegosouzapw/OmniRoute/pull/12669)) +- **feat(db):** fail-closed previous_response_id continuation for redacted video turns (#12150 P2b) ([#12707](https://github.com/diegosouzapw/OmniRoute/pull/12707)) +- **feat(gamification): pay the documented `streak_bonus` and `badge_unlock` XP rewards.** `XP_REWARDS` listed both rewards but the award pipeline never paid them: the private reward table in `events.ts` omitted them, `updateStreak()` did not report when a streak extended, and badge unlocks carried no XP. Every request that extends a daily streak now pays `streak_bonus × streak length` once per UTC day (guarded by a same-day `xp_audit_log` check), and every badge unlocked through the pipeline pays `badge_unlock` once per badge (guarded by the `user_badges` primary key; `unlockBadge()` now reports whether it inserted). Bonus XP flows through the same `addXp` + level sync + global/weekly/monthly leaderboard path as action XP, so level-ups and rankings include it. The Radar supporter recognition unlock stays XP-free. (#12522 — thanks @pacocartones) +- **feat(providers):** advertise a `free-tier` capability in the provider plugin manifest for every provider with documented free models, so sidecars and dashboards can filter free-capable providers without reading the quota catalog ([#12786](https://github.com/diegosouzapw/OmniRoute/pull/12786)) — thanks @maxmad64bis +- **feat(providers):** Added EURouter as an OpenAI-compatible API-key gateway (`https://api.eurouter.ai/v1`), with live model discovery via `passthroughModels`. Its copy states that models are served by third-party upstreams listed per model, so an EU-based router is not read as EU data residency for inference. ([#13025](https://github.com/diegosouzapw/OmniRoute/pull/13025)) — thanks @ntdatt812 +- **feat(providers):** Added GreenPT as an OpenAI-compatible API-key provider (`https://api.greenpt.ai/v1`), with live model discovery via `passthroughModels`. No free-inference badge: the published docs describe a free API subscription billed per token, not a free tier. ([#13024](https://github.com/diegosouzapw/OmniRoute/pull/13024)) — thanks @ntdatt812 +- **feat(i18n):** 9 new locales — Greek (`el`), Croatian (`hr`), Serbian (`sr`), Lithuanian (`lt`), Estonian (`et`), Latvian (`lv`), Slovenian (`sl`), Maltese (`mt`), Irish (`ga`) — across the dashboard, docs mirrors, CLI, README and the site; OmniRoute now ships every official EU language (51 locales). (#13044) +- **feat(i18n):** 8 new locales — Kannada (`kn`), Malayalam (`ml`), Odia (`or`), Punjabi (`pa`), Nepali (`ne`), Sinhala (`si`), Burmese (`my`), Khmer (`km`) — across the dashboard, docs mirrors, CLI, README and the site (59 locales). The translator now restores the ICU literal escape around angle placeholders and splits oversized docs sections before translating. (#0000) ([#13660](https://github.com/diegosouzapw/OmniRoute/pull/13660)) +- feat(providers): **list Agnes 3.0 Flash as the current free chat model, drop retired 1.5 Flash, add Image 2.0/2.5 Flash plus Video 2.5/2.5 Flash, and discover the live `/v1/models` catalog (including the CN host `api.agnes-ai.cn`).** `agnes-1.5-flash` now forwards to `agnes-3.0-flash`. Video 2.5 polls `GET /v1/videos/{id}` (not the V2.0 `/agnesapi` contract). Live `/v1/models` (2026-09-09) no longer serves 1.5; the wiki marks it deprecated. 3.0 Flash is 512K context / 65,536 max output, same window as 2.5. CN-region keys use the existing per-connection base-URL field, default stays `apihub.agnes-ai.com`. ([#13120](https://github.com/diegosouzapw/OmniRoute/pull/13120)) — thanks @HouMinXi +- **feat(sse):** Codex and OpenAI catalogs list GPT-6 Astra with effort aliases (`-low` through `-ultra`); Codex CLI identity pins `@openai/codex@0.153.4` in lockstep with the image ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **feat(dashboard):** the orchestration History tab gained a "Compare runs" mode — toggling it turns each grid cell into a 2-item selection queue (a 3rd click drops the oldest pick), and picking two cells opens a side-by-side comparison panel instead of the usual detail drawer. The panel fetches both runs' detail the same way the drawer does (falling back to persisted history once a run leaves the live TTL window) and shows, per side: identity/source/state, start time, a signed `right - left` delta for duration/cost/event count, the event timeline aligned by index, and any memory hits. One side's fetch failing never blocks the other, and a delta is only ever computed when both sides have a finite value — otherwise it renders "—", never `NaN`. Comparing two runs from different sources or skills still works; a banner marks the deltas as informational rather than hiding them, since the two runs aren't a strict apples-to-apples pair. ([#12677](https://github.com/diegosouzapw/OmniRoute/pull/12677)) +- **feat(dashboard):** badge a conversation that never reached a clean stop ([#12717](https://github.com/diegosouzapw/OmniRoute/pull/12717)) — thanks @hartmark +- **feat(release):** reconcile-changelog tool + version-anchored fragment aggregation ([#12987](https://github.com/diegosouzapw/OmniRoute/pull/12987)) +- **feat(mcp):** carry context_length through MCP list_models_catalog projection (#12776) ([#13406](https://github.com/diegosouzapw/OmniRoute/pull/13406)) — thanks @KooshaPari +- **feat(cli):** make startup readiness budget configurable (#13369) ([#13433](https://github.com/diegosouzapw/OmniRoute/pull/13433)) — thanks @KooshaPari + ### 🐛 Bug Fixes +- **security(streaming):** sanitize generic mid-stream error messages before emitting OpenAI, + Responses, or Claude SSE failure frames and before diagnostic logging, while preserving raw + failures for internal classification and keeping client disconnects out of provider failure state. ([#12457](https://github.com/diegosouzapw/OmniRoute/pull/12457)) +- **fix(claude):** `blockExtraUsage: false` no longer lets 5h quota preflight skip the account; extra usage is billed after the session bar is gone, so the request must reach Anthropic ([#12803](https://github.com/diegosouzapw/OmniRoute/pull/12803)) — thanks @HouMinXi +- Sanitize HuggingChat conversation-creation and message-send transport failures before they reach client error bodies or provider logs. ([#12467](https://github.com/diegosouzapw/OmniRoute/pull/12467)) +- **fix(dashboard):** Keep local and theme-aware provider SVG icons at a definite layout size so Chromium does not collapse them to 0×0 after the v3.8.50 image-rendering change ([#12054](https://github.com/diegosouzapw/OmniRoute/pull/12054)) — thanks @ponkcore +- **fix(security):** Sanitize provider and runtime failures before public API, SSE and MCP responses and before persistent request, proxy and usage logs, preventing credentials, stack traces and host filesystem paths from crossing those boundaries while preserving stable error codes and useful diagnostics. ([#12506](https://github.com/diegosouzapw/OmniRoute/pull/12506)) +- **fix(cli):** `omniroute tunnel create` no longer crashes with `Cannot read properties of undefined (reading optsWithGlobals)` — removed the duplicate positional argument that caused Commander.js to misalign the action callback parameters ([#12295](https://github.com/diegosouzapw/OmniRoute/issues/12295)) ([#12368](https://github.com/diegosouzapw/OmniRoute/pull/12368)) — thanks @gonisulaimann +- Stop advertising Gemini Live-only models as supported audio endpoints until OmniRoute proxies the bidirectional Live protocol. ([#11710](https://github.com/diegosouzapw/OmniRoute/pull/11710)) — thanks @RaviTharuma +- **fix(kie):** reroute `flux/kontext` off the KIE Market `createTask` flow — it is catalogued with `isMarket: true` but has no Market catalog page, so KIE rejected it with "model name not supported"; it now hits the dedicated `POST /api/v1/flux/kontext/generate` / `GET /api/v1/flux/kontext/record-info` endpoints instead (#11296). ([#11985](https://github.com/diegosouzapw/OmniRoute/pull/11985)) — thanks @hartmark +- **fix(kie):** correct 12 more KIE Market catalog ids that were sent to `createTask` unchanged but diverge from KIE's documented upstream `model` values — GPT Image 2 T2I/I2I (drops the `gpt/` prefix), GPT Image 1.5 T2I/I2I (`gpt-image/` namespace), Seedream 5.0 Lite T2I/I2I (drops the `.0`), all 4 Flux 2 variants (`flux-2/` namespace, generic variant renamed `flex`), and Wan 2.7 Image / Image Pro (dash instead of dot) — each verified individually against the literal example request published on docs.kie.ai. `#11326`'s "everything else already matches" claim was wrong a second time (#11296); `z-image/4.0-*`/`z-image/4.5-*` and `flux/kontext` remain open, documented as unresolved in `KIE_MARKET_UPSTREAM_MODEL_IDS`'s comment pending further verification. ([#11667](https://github.com/diegosouzapw/OmniRoute/pull/11667)) +- **fix(dashboard):** `useApiKeySave.handleSaveApiKey` no longer forces a full upstream `/models` catalog sync on every non-curated provider connection save — callers can now pass `skipModelSync: true` to opt out, so a workflow that only wants to add one manual model no longer floods the provider's available-models list with hundreds/thousands of synced entries. The flag is a client-side intent signal only and is stripped before the connection payload is POSTed to `/api/providers`; default behavior (full sync on save) is unchanged when the flag is omitted (#11324) ([#11664](https://github.com/diegosouzapw/OmniRoute/pull/11664)) +- **Provider connections:** keep `tokenExpiresAt` when a connection is created. The create-path allowlist omitted it, so every insert stored NULL and the dashboard token badge could read a fresh connection as expired until its first background refresh ([#11368](https://github.com/diegosouzapw/OmniRoute/pull/11368)). — thanks @ntdatt812 +- **fix(combos):** the combo builder's precision-select, global-model-search, and manual-entry flows now serialize a model step's `model` string using the provider's already-computed routing-alias prefix (e.g. `oc/`) instead of rebuilding it from the raw canonical `providerId`, fixing the no-auth "OpenCode Free" provider (`opencode`) being routed to the unrelated paid "OpenCode Zen" provider (`opencode-zen`) because `opencode` doubles as a manual routing-prefix override ([#11433](https://github.com/diegosouzapw/OmniRoute/issues/11433)). ([#11670](https://github.com/diegosouzapw/OmniRoute/pull/11670)) +- **fix(radar):** the catalog feed cache now keeps `generatedAt`, the date the feed's data was built, next to `fetchedAt`, the date this install downloaded it (#11435). The feed schema requires that date and the sync path validates it, but the cache dropped it — so a feed fetched minutes ago and one carrying weeks-old figures looked identical to everything downstream, including the dashboard's "Last fetched" line. `getRadarCatalog().meta` and `GET /api/radar/status` now report both dates, the latter as its own field rather than folded into `version` — and omitted entirely for the offers and intel caches, which keep no build date, where a `null` would read as "unknown" rather than "never stored". The dashboard still shows only the fetch time; surfacing the build date there needs a new translated label and is left to a follow-up. Rows cached before migration 163 read back as `null`: unknown stays unknown instead of borrowing the fetch time. The referrals cache has persisted the same date since migration 142. — thanks @maxmad64bis +- **fix(cli):** postinstall no longer fills `JWT_SECRET` and `API_KEY_SECRET` in the installed package's `.env` (#11436). `.env.example` ships both blank on purpose: the server restores them from its durable store, or generates and persists them there, in `ensureSecrets()`. Pre-filling them defeated that — the file lives inside the package directory, so `npm i -g` replaced it and postinstall wrote _different_ values, while `ensureSecrets()` (which only acts on an empty variable) never got to restore the real ones. Both secrets rotated silently on every update, invalidating dashboard sessions and API-key CRCs. `STORAGE_ENCRYPTION_KEY` left the same list for the same reason in #1622; its comment pointed at a function that no longer exists and now names the real provisioning path. — thanks @maxmad64bis +- **fix(cli):** the CLI now says when a loaded `.env` lives inside the installed package directory (#11437). It already announces every env file it reads, without distinguishing the ones that survive an update from the one that does not: `npm i -g` replaces the package directory wholesale, so values set there are gone at the next update, silently. The warning names the durable path to move them to, and fires only when that file actually supplied a value — a file entirely shadowed by a durable one supplied nothing. A development checkout stays silent: there the same path is stable and documented in `SETUP_GUIDE.md`. — thanks @maxmad64bis +- **fix(free-models):** the shared `isFreeModel()` predicate no longer reports catalog entries marked `freeType: "discontinued"` as free, so `hidePaidModels` can't route to Pollinations' seven premium models that now require a paid key ([#11441](https://github.com/diegosouzapw/OmniRoute/pull/11441)) — thanks @maxmad64bis +- fix(ui): let AnySearch use the normal provider-icon fallback when LobeHub has no matching icon (#11449) +- Fixed the v3.8.50 Costs and Analytics dashboards so flat-rate Claude Code usage can be shown as an explicitly requested token-price estimate without changing default billed-cost semantics. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed archived usage retention so each request is priced individually instead of pricing a day's summed tokens once, which understated archived cost whenever a day mixed cache-heavy and ordinary requests. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed the Costs dashboard so it discloses when displayed figures include flat-rate token-price estimates instead of labelling them as billed spend, using the flag the analytics API already returns; the month-end projection and the CSV/JSON exports carry the same marker, and billed-cost mode is unchanged. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- **fix(combo):** attach the same combo-diagnostics trace (`poolSize`/`attemptOrder`/`excluded`/`terminalReason`, plus `x-omniroute-combo-*` headers) to the round-robin strategy's and the nested pipeline/fusion runtime-unit loop's "Maximum combo retry limit reached" 503 that the priority-strategy path already attaches for the identical terminal condition — previously those two paths returned a bare, contextless 503 ([#11462](https://github.com/diegosouzapw/OmniRoute/issues/11462)). ([#11665](https://github.com/diegosouzapw/OmniRoute/pull/11665)) +- **fix(bun):** use native `bun:sqlite` in `bootstrap-env` and `sync-env` to avoid loading `better-sqlite3` N-API addon during Bun startup ([#11468](https://github.com/diegosouzapw/OmniRoute/pull/11468)) — thanks @TheDemonTuan +- **fix(docker-bun):** make `Dockerfile.bun` install reliable with pre-install script helpers and native `bun:sqlite` smoke check ([#11470](https://github.com/diegosouzapw/OmniRoute/pull/11470)) — thanks @TheDemonTuan +- **fix(docker-bun):** remove every vendored `better-sqlite3` native addon from the Bun runtime image so startup cannot abort before the `bun:sqlite` fallback ([#11482](https://github.com/diegosouzapw/OmniRoute/pull/11482)) — thanks @TheDemonTuan +- **fix(autoCombo):** effort/alias model variants (`gpt-5.6-sol-xhigh`, `gpt-5.6`, cursor's `claude-4.6-opus-high`) inherit their base model's task fitness instead of falling to the wildcard 0.5 ([#11492](https://github.com/diegosouzapw/OmniRoute/pull/11492)) — thanks @MumuTW +- fix(db): rate-limit repeated Arena ELO leaderboard fetch-failure warnings instead of logging one per sync attempt (#11500) ([#11989](https://github.com/diegosouzapw/OmniRoute/pull/11989)) — thanks @hartmark +- **fix(db):** dedupe the raw `[Encryption] Decryption failed...` log line emitted by the lazy-decrypt views (`createLazyRowProxy`/`createLazyConnectionView`), which power `getProviderConnections()` and were re-triggering that line on every CredentialHealth/model-sync cycle for the same corrupt or stale-key credential — a fresh Proxy over a fresh row on every cycle meant the per-proxy memoization never suppressed it, unlike the dedup `decryptConnectionFields()` already had since [#9927](https://github.com/diegosouzapw/OmniRoute/issues/9927). Now shares that dedupe tracking so the line logs at most once per credential ([#11500](https://github.com/diegosouzapw/OmniRoute/issues/11500)). ([#11678](https://github.com/diegosouzapw/OmniRoute/pull/11678)) +- **fix(intelligence):** Arena ELO sync stores a synthesized base-model row for every effort/harness variant (`claude-opus-5-max` → `claude-opus-5`, `gpt-5.6-sol-xhigh (codex-harness)` → `gpt-5.6-sol`), so bare model requests reach the synced intelligence layer instead of the static fallback table; the hand-maintained `MODEL_ALIAS_MAP` — which collapsed generations (`gpt-5.5` → `gpt-5`) — is gone ([#11506](https://github.com/diegosouzapw/OmniRoute/pull/11506)) — thanks @MumuTW +- **fix(autoCombo,sse):** catalog hygiene — the static task-fitness table no longer ranks retired models above live ones (dead rows dropped, patterns matched on segment boundaries), the 7 deprecation aliases that forwarded to retired or non-existent models now point at the vendors' published replacements, an alias is no longer rewritten when the serving provider offers the id as-is, and a new `check:model-lifecycle` gate diffs both tables against a vendor lifecycle snapshot ([#11507](https://github.com/diegosouzapw/OmniRoute/pull/11507)) — thanks @MumuTW +- **fix(sse):** stop dropping resolved `thoughtSignature` values on parallel (multi tool-call) turns sent to Gemini 3.x — the claude→gemini and openai→gemini translators previously kept the signature only on the *first* function call of a message, causing Gemini to reject subsequent calls in the same turn with HTTP 400 "Function call is missing a thought_signature"; each function call now keeps its own resolved signature ([#11510](https://github.com/diegosouzapw/OmniRoute/issues/11510)). ([#11669](https://github.com/diegosouzapw/OmniRoute/pull/11669)) +- Treat Docker publishing from a non-default release branch as an expected skip instead of a failed workflow, while preserving the guard that prevents it from overwriting the `next` channel. ([#11527](https://github.com/diegosouzapw/OmniRoute/pull/11527)) — thanks @pacocartones +- fix(search): prefer credentialed providers over duckduckgo-free fallback (#11524) — the fallback-only loop ran before the credentialed-providers loop in `executeWebSearch`, making configured providers unreachable when `duckduckgo-free` was available. ([#11565](https://github.com/diegosouzapw/OmniRoute/pull/11565)) — thanks @vsd2807 +- **fix(sse):** cap the upstream headers-wait phase for STREAMING requests to a client-realistic ceiling (110s, under Codex's own ~120s hard client-abort window) instead of the flat 10-minute `FETCH_TIMEOUT_MS` default — that default was 5x longer than the body-phase readiness watchdog's own adaptive bound, so a request whose upstream never returned any response at all (not even headers, e.g. a stalled NVIDIA target behind a tool-heavy Responses→Chat translation) kept the client connection alive on keepalives only, guaranteeing the client's own patience ran out first with an opaque 499 instead of OmniRoute detecting and failing the stall fast. Non-streaming requests are unaffected — they keep the existing flat default (`open-sse/utils/fetchStartTimeoutPolicy.ts`) (#11526) ([#11668](https://github.com/diegosouzapw/OmniRoute/pull/11668)) +- **fix(ci):** protocol-clients E2E harness boots the peer-stamped custom server (`run-next.mjs dev`) instead of the bare `next` CLI, so `LOCAL_ONLY` locality resolves from the real TCP peer stamp and `/api/mcp/audit` answers 200 (open bootstrap preserved via an `OMNIROUTE_E2E_BOOTSTRAP_MODE=open` credential clear in `run-next.mjs`, pinned to loopback `HOST`) instead of a deterministic 403 (#11535) ([#11549](https://github.com/diegosouzapw/OmniRoute/pull/11549)) — thanks @jonlwheat2-gif +- **fix(docs):** the Auto-Combo engine is described with the number of factors it actually declares. Four different counts were in circulation — 6, 9, 13 and 14 across `AGENTS.md`, four guides and six code comments — while `DEFAULT_WEIGHTS` declares 15. `check:docs-counts` now reads that number from the source, so adding a factor without updating the prose fails CI ([#11545](https://github.com/diegosouzapw/OmniRoute/pull/11545)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **fix(quota-share):** in-flight leases are per request, so an aborted request's slot ages out instead of being kept alive by later traffic on the same connection ([#11547](https://github.com/diegosouzapw/OmniRoute/pull/11547)) — thanks @abhisheksharma2411 +- The free-tier summary route now serves the refreshed Radar catalog when the feed is active — the same numbers the dashboard shows — and states which catalog answered plus its real build date, instead of always reporting release-frozen figures with a stale curation date. The supporter-key live feed stays reserved to authenticated callers of the instance. ([#11550](https://github.com/diegosouzapw/OmniRoute/pull/11550)) — thanks @maxmad64bis +- **sse:** fix LiveWS/embed-WS servers crashing at startup under the Node/tsx runtime — `liveServer.ts`, `embedWsProxy.ts` and `apiBridgeServer.ts` imported `@/shared/utils/httpClientAbortGuard` without the `.mjs` extension, so the client-abort crash guard added by [#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556) was unreachable and every dependent test failed with `ERR_MODULE_NOT_FOUND` ([#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556)). ([#11671](https://github.com/diegosouzapw/OmniRoute/pull/11671)) — thanks @jonlwheat2-gif / @yxyxy +- fix(chat): shed any of the matched provider-node's routing identifiers (public prefix or internal id) before model lookup, so composites like `//` no longer reach upstream verbatim (#11557) — thanks @maxmad64bis +- **fix(cache):** a semantic-cache entry now expires on its TTL instead of surviving until the next UTC midnight, and `dbEntries` stops counting expired rows ([#11573](https://github.com/diegosouzapw/OmniRoute/pull/11573)) — thanks @ntdatt812 +- **fix(api):** `/v1/models` schedules its stale-while-revalidate rebuild through Next's `after()` again, so a stale catalog reaches the client before the rebuild blocks the event loop ([#11574](https://github.com/diegosouzapw/OmniRoute/pull/11574)) — thanks @ntdatt812 +- **fix(check):** the `known-symbols` executor conformance gate awaits the now-async `getExecutor()`, so it stops reporting all 142 registered aliases as dead symbols and can detect a lazy import that fails ([#11582](https://github.com/diegosouzapw/OmniRoute/pull/11582)) — thanks @ntdatt812 +- **fix(pack):** register `bin/cli/utils/volatileEnvPath.mjs` in `PACK_ARTIFACT_REQUIRED_PATHS`, so the prune can no longer silently drop a module the published CLI imports on every boot ([#11588](https://github.com/diegosouzapw/OmniRoute/pull/11588)) — thanks @ntdatt812 +- **i18n(vi):** translate the five reliability-column strings on the free-provider rankings page, which rendered as `__MISSING__:` markers in the Vietnamese UI ([#11589](https://github.com/diegosouzapw/OmniRoute/pull/11589)) — thanks @ntdatt812 +- **resilience:** restore the expired-connection retry-budget probe in the token-health sweep — the `!isGitHubAccessTokenOnlyConnection` carve-out reintroduced by #11608 contradicted the boundary pinned by #11592, so a GitHub connection parked at `expired` with retry budget remaining was never probed and could never self-heal ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)). ([#11672](https://github.com/diegosouzapw/OmniRoute/pull/11672)) — thanks @jonlwheat2-gif / @ntdatt812 +- **fix(dashboard):** Provider Stats now displays combo success rates on their native 0–100 percentage scale instead of multiplying them twice ([#11595](https://github.com/diegosouzapw/OmniRoute/pull/11595)) — thanks @pacocartones +- **fix(providers):** route DuckDuckGo Web Chat through the verified `duck.ai` endpoint, refresh its free model catalog, and avoid forwarding unsolved anti-abuse challenges ([#11597](https://github.com/diegosouzapw/OmniRoute/pull/11597)) — thanks @solstxce +- **fix(ui):** Console log Refresh and Copy controls now expose localized accessible names, keep copy actions visible on keyboard focus, and announce copy completion safely ([#11599](https://github.com/diegosouzapw/OmniRoute/pull/11599)) — thanks @pacocartones +- Dropped the stale-`.eslintcache` `restore-keys` fallback from both "Restore ESLint file cache" steps in `ci.yml`, so the blocking `Lint` job can no longer be served per-file verdicts computed under a different lint config, suppressions file or lockfile. `quality.yml` had already dropped it in #11963; `ci.yml` — the workflow that actually gates PRs — had not (#11600). ([#11996](https://github.com/diegosouzapw/OmniRoute/pull/11996)) +- fix(ci): pass `--pass-on-unpruned-suppressions` in `run-eslint-json.mjs` so the CI Lint job no longer fails when a suppression is merely orphaned by a genuine fix, mirroring the identical fix already in `validate-release-green.mjs` (#11600) ([#11983](https://github.com/diegosouzapw/OmniRoute/pull/11983)) — thanks @hartmark +- fix(handoff): enforce provider allowlist for universal handoff (#11602) — universal handoff now skips summarization when the selected summary model's provider is not included in the configured provider allowlist. ([#11619](https://github.com/diegosouzapw/OmniRoute/pull/11619)) — thanks @vsd2807 +- **fix(dashboard):** Show a stable error state when Search Analytics returns an HTTP error or malformed data ([#11603](https://github.com/diegosouzapw/OmniRoute/pull/11603)) — thanks @pacocartones +- **test(gamification):** pin the aggregate profile level to the XP-derived semantics of #11604 — `getAggregateXp()` now derives `currentLevel` from the summed XP (`calculateLevel(sum)`), not `MAX(stored current_level)`, and the #3484 fixture levels are aligned with the XP curve ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)). ([#11676](https://github.com/diegosouzapw/OmniRoute/pull/11676)) — thanks @jonlwheat2-gif / @pacocartones +- **fix(dashboard):** Keep the Profile level and progress aligned with aggregate XP, including bounded handling for invalid totals ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)) — thanks @pacocartones +- **fix(dashboard):** Prevent locked hidden badges from revealing their icon or opening private badge details before they are earned ([#11605](https://github.com/diegosouzapw/OmniRoute/pull/11605)) — thanks @pacocartones +- **fix(dashboard):** Restore keyboard focus after shared modals close and cancel delayed autofocus during cleanup ([#11607](https://github.com/diegosouzapw/OmniRoute/pull/11607)) — thanks @pacocartones +- **fix(dashboard):** Enable Enter and Space activation for clickable data-table rows without hijacking nested controls ([#11610](https://github.com/diegosouzapw/OmniRoute/pull/11610)) — thanks @pacocartones +- **fix(autoCombo,sse):** vendor-retired catalog ids are dropped from the auto-combo candidate pool and no longer win on leftover `arena_elo` / `user_override` rows; `getModelLifecycleDecision` consults `model-lifecycle.json` (prefix-stripped) so aggregator traffic is not `untracked` for ids the snapshot already knows ([#11625](https://github.com/diegosouzapw/OmniRoute/issues/11625)) ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)) — thanks @MumuTW +- **test(chatcore):** move Codex/Claude combo fixtures off models the lifecycle guard now rejects — `gpt-5.1-codex`/`gpt-5-codex` are vendor-retired (snapshot, #11626) and `claude-3-5-sonnet-20241022` is shut down, so native-passthrough and combo-fallback tests switched to `gpt-5.6-sol` and `claude-sonnet-4.6` ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)). ([#11675](https://github.com/diegosouzapw/OmniRoute/pull/11675)) — thanks @jonlwheat2-gif / @MumuTW +- fix(opencode-plugin): stop a free-tier budget that rounds up across a magnitude from rendering as `1000K`/`1000M` in the model picker — `fmtTokens` chose its unit from the raw token count and then rounded with `toFixed(1)`, so 999,950–999,999 printed as `1000K` rather than `1M` and just under a billion printed as `1000M` rather than `1B` ([#11684](https://github.com/diegosouzapw/OmniRoute/pull/11684)) — thanks @abhisheksharma2411 +- Cap prose-derived Antigravity quota resets at the configured model cooldown maximum while preserving authoritative Retry-After headers and Google RetryInfo hints. (#11695) Thanks @pacocartones. ([#11823](https://github.com/diegosouzapw/OmniRoute/pull/11823)) +- Compare `_rebaseline_*` notes structurally in the ratchet-bank verifier so an object-valued note no longer reports itself as rewritten on an unmodified tree — the nightly `bank-ratchet-shrinks` lane had been aborting on every run since 2026-08-11, leaving 41,672 lines of earned file-size shrinks unbanked (#11702). ([#11892](https://github.com/diegosouzapw/OmniRoute/pull/11892)) — thanks @MumuTW +- **fix(build):** `prepublish.ts` bundles the ChatGPT Web (Codex) MCP bridge through `runBuildTool()` instead of spawning `npx.cmd` raw, fixing the build crash on Node ≥ 20/Windows where `.cmd` shims cannot be spawned without a shell (EINVAL) ([#11704](https://github.com/diegosouzapw/OmniRoute/issues/11704)) ([#11706](https://github.com/diegosouzapw/OmniRoute/pull/11706)) — thanks @jonlwheat2-gif +- fix(codex): keep `parallel_tool_calls:false` on the translated Codex Responses Lite path (#11707) ([#11984](https://github.com/diegosouzapw/OmniRoute/pull/11984)) — thanks @hartmark +- **fix(tests):** the new Gemini Web Images retirement test (#11708) called the now-async `getExecutor()` synchronously, throwing `TypeError: getExecutor(...).getProvider is not a function` instead of asserting Gemini Web chat stays available. Awaited to match the R0.3 executor-registry refactor (#11220). ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(search):** Allow REST and MCP web-fetch requests to select the advertised `anysearch-search` provider explicitly ([#11716](https://github.com/diegosouzapw/OmniRoute/pull/11716)) — thanks @pacocartones +- **fix(providers):** Keep overnight peak-hour protection active after midnight according to the window's configured start day ([#11718](https://github.com/diegosouzapw/OmniRoute/pull/11718)) — thanks @pacocartones +- **fix(providers):** correct all five g4f.space gateway descriptions to document proof-of-work credits or member API keys, variable limits, and the remote prompt boundary instead of promising free keyless access at five requests per minute ([#11738](https://github.com/diegosouzapw/OmniRoute/pull/11738)). +- **fix(db):** `model_capabilities` is created by a migration instead of lazily on the first models.dev sync, so a clean install and an upgraded install converge on the same schema regardless of which features have run ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(ci):** `check:install-upgrade` now fails on an `npm` install truncated by ENOSPC (npm reports it as a warning and still exits 0), authenticates its health probe so the version assertion works against the hardened health payload, frees the clean-install tree before the upgrade phase, and no longer reports a schema divergence computed from a boot that never served ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(routing):** harden session-affinity key derivation against oversized explicit IDs and empty structural-payload collisions, while recognizing Gemini and common text request shapes ([#11746](https://github.com/diegosouzapw/OmniRoute/pull/11746)) — thanks @MumuTW +- **fix(packages/browser-pool):** regenerate `package-lock.json` so the `packages/browser-pool` workspace's locked `playwright`/`@types/node` (and transitives) match its `package.json` specs, fixing cache-only/offline installs (`npm ci --offline`, Nix `buildNpmPackage`) that previously failed with `ENOTCACHED` ([#11747](https://github.com/diegosouzapw/OmniRoute/issues/11747)) — thanks @benjaminkitt ([#11784](https://github.com/diegosouzapw/OmniRoute/pull/11784)) — thanks @geekyNads +- **fix(assets):** Stop distributing 79 provider and CLI logos without verified source/license provenance, keep every provider/tool available through the internal generic icon, and prevent those IDs from falling through to an external icon CDN. ([#11750](https://github.com/diegosouzapw/OmniRoute/pull/11750)) +- **fix(providers):** retire the common `chatgpt-web` / `cgpt-web` provider because its pre-key and proof-of-work provenance could not be cleared ([#11754](https://github.com/diegosouzapw/OmniRoute/pull/11754)) — explicit chat and image requests, slash-prefixed models, persisted aliases, proxy overrides, and combo targets now fail closed with a sanitized HTTP `410` / `PROVIDER_RETIRED` before upstream dispatch. Migration 163 tombstones matching connections and invalidates their active leases without deleting history or widening API-key allowlists. The independent `chatgpt-web-codex` / `cgpt-codex` provider and its MIT-noticed vendored implementation remain available. +- **fix(usage):** quota and usage refresh no longer 409 when an exclusive lease reserves the connection ([#11758](https://github.com/diegosouzapw/OmniRoute/pull/11758)) — thanks @TheDemonTuan +- Keep the embedding registry's vector width and `embedding` type on models when a synced model exists for the same id, so `/v1/models` no longer reports registry-described embedding models widthless or untyped (#11761) — thanks @rahilmavani +- Correct `google/gemini-embedding-001` on the OpenRouter route to 3072 dimensions, the width it returns when `dimensions` is not sent (#11761) — thanks @rahilmavani +- **fix(vision-bridge):** Restrict automatic vision routing and fallbacks to models available in authoritative live provider catalogs, including aliases and effort variants ([#11767](https://github.com/diegosouzapw/OmniRoute/issues/11767)) — thanks @pacocartones ([#11813](https://github.com/diegosouzapw/OmniRoute/pull/11813)) +- **fix(providers):** reclassify Cerebras as a one-time $5 signup credit (payment method required, 30-day validity), not a recurring no-card 1M tokens/day trial ([#11773](https://github.com/diegosouzapw/OmniRoute/issues/11773)) ([#12591](https://github.com/diegosouzapw/OmniRoute/pull/12591)) — thanks @HouMinXi +- fix(cli): stop prepublish from re-rebuilding the already-built ESM-only opencode-plugin dist (#11787) ([#11990](https://github.com/diegosouzapw/OmniRoute/pull/11990)) — thanks @hartmark +- **fix(db):** Fresh installs no longer log a non-fatal `no such table: compression_run_telemetry` warning when retention cleanup runs before the lazily-created telemetry table exists ([#11802](https://github.com/diegosouzapw/OmniRoute/pull/11802)) — thanks @RaviTharuma +- fix(sse): set X-OmniRoute-Selected-Connection-Id on successful combo dispatches so downstream consumers stop falling back to an empty connection id (#11810) ([#11986](https://github.com/diegosouzapw/OmniRoute/pull/11986)) — thanks @hartmark +- **fix(services):** embedded CLIProxyAPI lifecycle checks now use public `/healthz`, while model discovery uses the configured dedicated data-plane API key instead of the management password ([#11811](https://github.com/diegosouzapw/OmniRoute/pull/11811)) — thanks @RaviTharuma +- Fix persisted model steps returning an error in the playground route simulator, and warn when structural steps cannot be simulated. ([#11862](https://github.com/diegosouzapw/OmniRoute/pull/11862)) — thanks @pacocartones +- **fix(providers):** Antigravity's dynamic mitmAlias table no longer routes `gemini-3.7-flash-{high,medium,low}` to a literal tier-suffixed upstream id just because one connected account's own discovery listed it directly — those display ids always resolve through the safe `gemini-3.7-flash-tiered` static alias, so one account's Google-provisioned access no longer 404s every sibling account of the provider ([#11824](https://github.com/diegosouzapw/OmniRoute/issues/11824), [#11651](https://github.com/diegosouzapw/OmniRoute/issues/11651)) ([#11988](https://github.com/diegosouzapw/OmniRoute/pull/11988)) — thanks @hartmark +- **fix(dashboard):** Expose Profile loading, errors, page structure, and XP progress to assistive technologies ([#11838](https://github.com/diegosouzapw/OmniRoute/pull/11838)) — thanks @pacocartones +- **fix(socks):** `Agent.connectTimeout` now bounds both SOCKS handshake and TLS connect, and `family === null` no longer falls back to `fetch-socks` ([#11842](https://github.com/diegosouzapw/OmniRoute/pull/11842)) — thanks @maxmad64bis +- **perf(compression):** OOM mitigations for large payload hashing, memoization, and token estimation ([#11844](https://github.com/diegosouzapw/OmniRoute/pull/11844) — thanks @AndrianBalanescu) +- Excluded Next.js Node File Trace manifests (`*.nft.json`) from the published npm tarball. They are build-time metadata and are never read while serving, but had grown to 668.7 MB — 61% of the package — which pushed the upload past the registry limit and made `npm publish` fail with `413 Payload Too Large`. ([#11864](https://github.com/diegosouzapw/OmniRoute/pull/11864)) +- **fix(cli):** keep the global OmniRoute server `--base-url` separate from provider-node `--endpoint` values in add, update, and validate commands ([#11860](https://github.com/diegosouzapw/OmniRoute/pull/11860)) — thanks @pacocartones +- **fix(config):** Nous Research's `Hermes-4-405B` model now displays as "Hermes 4 405B (Nous Research)" in both the provider registry and the free-model catalog, instead of the mislabelled "Hermes 4 7B" ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#11993](https://github.com/diegosouzapw/OmniRoute/pull/11993)) +- **fix(provider/nous):** inject required user= tag into Nous Research inference requests to resolve upstream 400 "missing tags" error ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#12044](https://github.com/diegosouzapw/OmniRoute/pull/12044)) +- **fix(build):** `npm run build` now fails in one second with a named package and a copy-pasteable fix when npm silently drops an externalised optional native dependency, instead of dying four minutes in with `Module not found: Can't resolve 'better-sqlite3'` ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- **fix(install):** `postinstall` no longer throws `ReferenceError: isAndroid is not defined` — failing the whole `npm install` — when the `better-sqlite3` rebuild fallback times out; the manual-fix guidance is reachable again ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- Fixed the Alibaba free-tier allowlist test that went red on its own once the shipped catalog's `validUntil` (2026-08-27) passed, leaving every PR and `main` with a failing `Unit Tests (1/8)`. The test now builds its own packs with dates it controls, and covers the expired-pack fallback that production has actually been serving. ([#11867](https://github.com/diegosouzapw/OmniRoute/pull/11867)) +- Split the npm registry upload into its own GitHub-hosted job. npm refuses `--provenance` from a self-hosted runner (`422 ... Only "github-hosted" runners are supported`), which blocked the v3.8.50 publish; the heavy verification cannot move to a hosted runner, so it now hands the proven tarball over instead. ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(ui):** the shared `DataTable` loading state no longer reads its decorative ⏳ glyph out to assistive technology, and now carries the same `role="status"` / `aria-live="polite"` / `aria-busy="true"` semantics as `PageLoading` ([#11869](https://github.com/diegosouzapw/OmniRoute/pull/11869)) — thanks @pacocartones +- **fix(api):** Bound the `timeout` accepted by `POST /api/providers/[id]/login` to the same 15s–600s window the Adobe Firefly and Conol browser-login paths already enforce, so a request can no longer pin a headful browser and the single active-login slot with an arbitrarily large poll budget. ([#11871](https://github.com/diegosouzapw/OmniRoute/pull/11871)) — thanks @pacocartones +- **fix(api):** Stop the tunnel and MITM routes from returning raw child-process error text, which disclosed host paths, binary install locations and Tailscale `tskey-*` credentials in a body some of these routes serve to non-loopback callers, and return a real 400 instead of a framework 500 when a tunnel request body fails validation. ([#11872](https://github.com/diegosouzapw/OmniRoute/pull/11872)) — thanks @pacocartones +- **fix(open-sse):** stream TTFT/ITL are sampled from a monotonic clock (`performance.now`) instead of `Date.now`, so an NTP correction or wall-clock jump can no longer inflate a genuine TTFT or produce a negative one that the `ttft >= 0` guard silently drops — protecting the router's OTel/EWMA/`usage_history`/speed-ranking signals ([#11873](https://github.com/diegosouzapw/OmniRoute/pull/11873)) — thanks @pacocartones +- **fix(providers):** xAI `reasoning_effort: "xhigh"` now reaches grok-4.6+ instead of being silently clamped to `"high"` ([#11879](https://github.com/diegosouzapw/OmniRoute/pull/11879)) — thanks @NoxzRCW +- **fix(dashboard):** the providers page no longer crashes into the error boundary when a provider id collides with an `Object.prototype` member (`constructor`, `__proto__`); icon lookups are own-property guarded ([#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)) — thanks @NoxzRCW +- **fix(skills):** injected skill tools declared in shorthand (`{"content": "string"}`) now forward valid JSON Schema, unblocking providers that validate tool schemas strictly such as Zhipu GLM on the Console Go tier ([#11881](https://github.com/diegosouzapw/OmniRoute/pull/11881)) — thanks @NoxzRCW +- **fix(api):** `POST /api/playground/simulate-route` now surfaces `combo-ref` and `provider-wildcard` persisted combo steps with a specific warning (naming the referenced combo, or the unresolved `provider/model` wildcard) instead of folding them into a generic "unsupported step" count; a `provider-wildcard` step is also now included as an unresolved target so the operator can see it is in the route (ported from [#11882](https://github.com/diegosouzapw/OmniRoute/pull/11882) — thanks @NoxzRCW). ([#11930](https://github.com/diegosouzapw/OmniRoute/pull/11930)) +- **fix(translator):** the streaming OpenAI→Claude translator keeps upstream usage, including prompt-cache tokens, when it arrives on a trailing `choices: []` chunk (Fireworks and any upstream using `stream_options.include_usage`) ([#11883](https://github.com/diegosouzapw/OmniRoute/pull/11883)) — thanks @NoxzRCW +- **fix(cli):** `omniroute update --apply` now tells you explicitly whether a running server was detected and, if so, that you must run `omniroute restart` to apply the update — it never restarted anything and previously implied the update was already live once files were installed. The dashboard's npm-mode Update flow (`/api/system/version`) now tries OmniRoute's own PID-file-managed supervisor before falling back to pm2, and reports an honest "restart required" step instead of a silent pm2-only "skipped" that read like a completed update. The server-side latest-version lookup backing the dashboard's update banner also gained `--prefer-online`, closing the same stale-npm-cache class already fixed in the CLI's own copy for #4376 ([#11885](https://github.com/diegosouzapw/OmniRoute/issues/11885)). ([#12005](https://github.com/diegosouzapw/OmniRoute/pull/12005)) +- **fix(resilience):** clear persisted LKGP pins when a target suffers connection/provider exhaustion or is skipped before dispatch due to cooldown/exhaustion/unavailability, preventing subsequent requests from repeatedly prioritizing known-dead providers ([#11911](https://github.com/diegosouzapw/OmniRoute/issues/11911)). ([#12013](https://github.com/diegosouzapw/OmniRoute/pull/12013)) — thanks @HouMinXi +- **fix(sse):** the OpenAI→Claude stream translator now defers the terminal `message_delta`/`message_stop` emission until the real usage block has arrived (or a genuine end-of-stream flush forces it) instead of emitting it immediately on `finish_reason` — previously, when the trailing usage-only chunk (`{"choices":[],"usage":{...}}`) arrived *after* the `finish_reason` chunk (the normal order for Fireworks/vLLM/Together and other `stream_options.include_usage` upstreams), the client-visible `message_delta` still carried stale/zero usage even though `state.usage` was internally corrected too late to matter (ported from [#11915](https://github.com/diegosouzapw/OmniRoute/pull/11915) — thanks @HouMinXi). ([#11933](https://github.com/diegosouzapw/OmniRoute/pull/11933)) +- **fix(sse):** `stripStore()` now forces `store=false` for stateless OpenAI-compatible Responses-API targets (unless the connection has explicitly opted in via `providerSpecificData.openaiStoreEnabled`), instead of only handling the `openai`/`agentrouter` cases — a client-supplied `store` value previously passed through untouched to backends that don't actually persist responses server-side ([#11916](https://github.com/diegosouzapw/OmniRoute/pull/11916) — thanks @HouMinXi). +- **fix(models):** custom provider-node models (synced, custom, and alias-backed) now appear under their configured prefix in the unified catalog when the operator's model-id prefix mode is canonical, instead of being dropped whenever alias-inclusion was otherwise disabled ([#11918](https://github.com/diegosouzapw/OmniRoute/pull/11918) — thanks @HouMinXi). +- **fix(models):** the unified model catalog now suppresses stale static registry models (including effort-tier variants) for any provider whose active connection has an authoritative live synced catalog, not only providers already using exclusive-synced-listing — a connection with `providerUsesAuthoritativeLiveCatalog` previously kept serving both the live-synced models and the stale static rows side by side ([#11919](https://github.com/diegosouzapw/OmniRoute/pull/11919) — thanks @HouMinXi). +- **fix(ui):** `ProviderIcon`'s three remaining unguarded lookups (`PROVIDER_ICON_ALIASES`, `LOCAL_SVG_ALIASES`, `THEMED_SVGS`) now use `Object.hasOwn()` own-property checks — a provider id such as `constructor` or `__proto__` previously resolved through the prototype chain instead of falling through to the unknown-provider CDN fallback (`getLobeProviderIcon()` itself was already guarded by [#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)); `ProviderPageHeader`'s `color` field is also now optional, matching the rest of the component's defensive typing (ported from [#11920](https://github.com/diegosouzapw/OmniRoute/pull/11920) — thanks @HouMinXi). ([#11935](https://github.com/diegosouzapw/OmniRoute/pull/11935)) — thanks @NoxzRCW +- fix(ollama): preserve multi-byte UTF-8 content split across stream chunks in the Ollama NDJSON transform, which previously corrupted CJK/emoji into U+FFFD (#11921) — thanks @pacocartones +- **fix(providers):** OrcaRouter chat requests now target `/v1/chat/completions` instead of the bare `/v1` API root, fixing the upstream `404 Invalid URL (POST /v1)` ([#11923](https://github.com/diegosouzapw/OmniRoute/pull/11923)). — thanks @echel0nn +- **fix(plugins):** deliver the `onStreamComplete` event to disk-installed plugins. The event shipped in v3.8.50 (#9669) was emitted internally but had no plugin-facing wiring, so no plugin could ever subscribe: the manifest schema silently dropped `hooks.onStreamComplete`, and the loader/manager only knew the seven legacy hooks. `onStreamComplete` is now a declarable manifest hook, wired through the loader and registered by the manager like the other hooks, and its payload carries a `requestId` so consumers can correlate the stream-completion event with the originating request ([#11934](https://github.com/diegosouzapw/OmniRoute/pull/11934)) — thanks @amaleta / @pacocartones +- **fix(db):** the Qdrant embedding-model dropdown now lists local/self-hosted providers (Ollama, LM Studio, vLLM, etc.) — an active connection is treated as "configured" when the provider allows an optional API key, not only when it has a real key or OAuth, so a running local embedding provider is no longer hidden from the picker ([#11949](https://github.com/diegosouzapw/OmniRoute/issues/11949)) ([#11995](https://github.com/diegosouzapw/OmniRoute/pull/11995)) +- **fix(providers):** Vertex AI Anthropic partner-model discovery now calls the Model Garden `v1beta1` publisher list (`/v1beta1/publishers/anthropic/models`, global) and parses its `publisherModels` envelope, so Claude models auto-synced from Vertex populate the active live catalog and route at request time instead of returning `Model '' is not available in the active live catalog` ([#11991](https://github.com/diegosouzapw/OmniRoute/issues/11991)) — thanks @fabioluissilva ([#11998](https://github.com/diegosouzapw/OmniRoute/pull/11998)) +- **fix(cli):** support `--base-url` alongside `--endpoint` in `omniroute nodes add`, `update`, and `validate` subcommands to prevent global `--base-url` shadowing issues ([#11999](https://github.com/diegosouzapw/OmniRoute/issues/11999)). ([#12033](https://github.com/diegosouzapw/OmniRoute/pull/12033)) — thanks @HouMinXi +- **fix(providers):** `cloudflare-ai` no longer refuses image content parts for every Workers AI model ([#12002](https://github.com/diegosouzapw/OmniRoute/pull/12002)) — the plain-string `content` requirement behind #2539 is carried by the _model_ schema, not by the `/ai/v1/chat/completions` endpoint (measured: an all-text part array returns 200 on `@cf/mistralai/mistral-small-3.1-24b-instruct`, `@cf/meta/llama-4-scout-17b-16e-instruct` and `@cf/meta/llama-3.3-70b-instruct-fp8-fast`, and 400 on the text-only `@cf/qwen/qwen2.5-coder-32b-instruct`). `transformRequest()` flattened every array and threw on the first non-text part (#6390), so image input was refused for vision-capable Cloudflare models that accept it. All-text arrays are still flattened — the one shape every model accepts — while an array carrying a non-text part is passed through untouched, so the attachment is still never silently dropped. Regression guards: `tests/unit/cloudflare-ai-image-parts-6390.test.ts`. — thanks @davidlinfr +- **fix(resilience):** decouple the limiter-managed execution backstop from the queue-wait budget — new `requestQueue.executionMaxWaitMs` (env `RATE_LIMIT_EXECUTION_MAX_WAIT_MS`, default 600000 = 10 min) now feeds Bottleneck's post-dispatch `expiration`, while `requestQueue.maxWaitMs` keeps its documented queue-wait semantics. Previously the queue-wait budget doubled as the execution expiration, so legitimate long-running calls on non-incremental gateways (whole generation buffered before the first upstream byte, e.g. Console Go / Command Code tiers serving GLM models) were killed mid-flight at the queue budget with a false 504 `RATE_LIMIT_EXECUTION_TIMEOUT` — the local limiter undercut the provider-aware upstream fetch-start timeouts. The surfaced 504 message now names `requestQueue.executionMaxWaitMs`; the error keeps the #4165 guarantees (disclaims an upstream timeout, preserves the Bottleneck error as `cause`, branded code + trusted provenance, classified request-scoped so combo falls back). A real queue-wait bound (the `Promise.race` around `limiter.schedule()` sketched in #9533) remains future work. (#12025) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **Call logs:** keep the `error` field when an artifact exceeds the storage cap, instead of replacing it with the omission marker. The error is the only field that says *why* a request failed and is typically ~90 bytes next to the multi-hundred-KB bodies that trip the cap, so dropping it left a size-limited row undiagnosable — a provider outage, a local timeout and an upstream 400 all rendered identically. It is now preserved at every fallback stage, truncated to 4KB if it is itself large ([#12026](https://github.com/diegosouzapw/OmniRoute/issues/12026)). ([#12095](https://github.com/diegosouzapw/OmniRoute/pull/12095)) — thanks @ntdat812 +- **fix(diagnostics):** preserve the error field (truncated to 4KB with a `[truncated: …]` suffix) in every call-log artifact size-limit fallback stage. Previously the minimal fallback replaced the error with `[omitted: call log artifact size limit exceeded]`, so an oversized artifact row showed nothing about WHY the request failed — e.g. 91 of 847 opencode-go 504 rows on one production instance were undiagnosable from the dashboard. Oversized request/response bodies are still omitted exactly as before; the error cap is independent of the payload sizes that tripped the fallback. (#12026) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **fix(sse):** OpenAI Responses clients that declare the native `web_search` tool now receive a spec-shaped `web_search_call` output item with `action.sources` alongside the preserved function-call round-trip, so search results executed through OmniRoute's own search backend are consumable by standard Responses clients (Codex, pi-web-access, …). ([#12031](https://github.com/diegosouzapw/OmniRoute/pull/12031)) — thanks @watchingdogs +- **fix(cli):** use in-thread alias resolver hooks on modern runtimes to avoid deprecation noise and improve Node.js forward compatibility ([#12073](https://github.com/diegosouzapw/OmniRoute/issues/12073)). ([#12083](https://github.com/diegosouzapw/OmniRoute/pull/12083)) — thanks @xiaoyaner0201 +- **fix(combo):** an operator-set **Agent Features → Context length** on a combo is now honored at request time. The value was persisted and advertised through `/v1/models`, but `resolveComboContextLimit()` never consulted it — so a multi-target combo whose members carry no per-model window fell through to the provider's generic `defaultContextLength` (openrouter 128000, command-code 200000) and rejected large requests with `Input exceeds context window … limit 128000` despite the combo being explicitly sized much larger. An identical single-target combo worked, because it collapses to its concrete target before the guard runs. Invalid values (0/negative/NaN/Infinity) are ignored, so the existing target → combo-min → fallback order is unchanged. ([#12090](https://github.com/diegosouzapw/OmniRoute/pull/12090)) — thanks @adivekar-utexas +- **fix(sse):** passthrough streams now estimate usage on finish when upstream closes without usage even with `stream_options.include_usage` — avoids `0 tokens / 0%` for providers that stay silent (and correctly handles trailing empty-choices usage) ([#12151](https://github.com/diegosouzapw/OmniRoute/pull/12151)) — thanks @maxmad64bis +- **fix(combos):** clearing an agent feature in the combos editor now persists — unchecking context cache protection, or emptying the system message or tool filter, sends an explicit `null` instead of dropping the field from the `PUT` body, which the update merge read as "leave unchanged" ([#12177](https://github.com/diegosouzapw/OmniRoute/pull/12177)) — thanks @foreveryh / @ntdatt812 +- **fix(memory):** self-hosted embedding endpoints now vectorize — the vector width is measured from the first embedding that comes back instead of being read from a registry that cannot describe them, so `vec_memories` is created and memories stop piling up unvectorized behind a green health check ([#12180](https://github.com/diegosouzapw/OmniRoute/pull/12180)) — thanks @kanade-hoshino / @ntdatt812 +- **fix(translator):** the leading `system` message now reaches Responses-API upstreams when its `content` is a content-part array — it was read as `typeof content === "string" ? content : ""`, so a prompt-caching client (Anthropic `cache_control`, the shape LiteLLM and the Anthropic SDK emit) had its entire system prompt replaced by an empty `instructions`. The request was still accepted with a normal `prompt_tokens` count, so the model answered with no instructions and nothing in the response said they were missing. Mid-conversation system turns already handled the array shape ([#7056](https://github.com/diegosouzapw/OmniRoute/pull/7056)); only the first one did not ([#12206](https://github.com/diegosouzapw/OmniRoute/issues/12206)). Regression guard: `tests/unit/translator-openai-responses-system-content-parts.test.ts`. ([#12207](https://github.com/diegosouzapw/OmniRoute/pull/12207)) — thanks @Gorillaz322 +- **fix(free-tier):** `/api/free-tier/summary` no longer computes its totals from a Radar feed built before the catalog the running release ships. When the cached feed is older — or carries no build date at all — the route answers from the shipped catalog, resolved through the operator's local model state so disabled and tombstoned models stay out of the numbers ([#12215](https://github.com/diegosouzapw/OmniRoute/pull/12215)). — thanks @maxmad64bis +- **fix(oauth):** Keep a Claude personal workspace and a Team organization as separate connections — they share the same email and `accountUUID`, so the email-only OAuth dedup let the second login overwrite the first account's tokens; `organizationUUID` now disambiguates them, the way `workspaceId` does for Codex ([#12222](https://github.com/diegosouzapw/OmniRoute/pull/12222)) — thanks @davidebaraldo +- **fix(resilience):** a 402 on a single paid model of a passthrough/gateway provider (e.g. `kilo-gateway`, `ollama-cloud`) no longer terminalizes the whole connection with a never-auto-recovered `credits_exhausted` status — only the paid model is locked out, so free models on the same key keep serving. 402 variant of [#3027](https://github.com/diegosouzapw/OmniRoute/issues/3027). Single-credential providers are unaffected — a 402 there is still treated as the key being genuinely out of credit ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239) / [#10616](https://github.com/diegosouzapw/OmniRoute/issues/10616)) ([#12242](https://github.com/diegosouzapw/OmniRoute/issues/12242)) — thanks @brick30llc-ctrl ([#12266](https://github.com/diegosouzapw/OmniRoute/pull/12266)) — thanks @geekyNads +- **fix(sse):** trust `finish_reason: "length"`/`"max_tokens"` over the reasoning-consumed-token ratio in response quality validation, so a reasoning model truncated below the old 90% threshold correctly fails and retries instead of returning empty content as a silent "success" ([#12262](https://github.com/diegosouzapw/OmniRoute/pull/12262)) — thanks @brick30llc-ctrl +- **fix(settings):** `PUT /api/settings/cache-config` now persists `alwaysPreserveClientCache` to the flat general settings the runtime cache-control policy actually reads; previously the value landed in the databaseSettings "cache" section and was silently ignored, so the endpoint had no effect on `cache_control` passthrough ([#12304](https://github.com/diegosouzapw/OmniRoute/pull/12304)) — thanks @davidebaraldo +- **fix(grok-cli):** treat omitted SuperGrokPro `creditUsagePercent` as 0% used so Provider Limits still renders a weekly bar (proto3 zero-elision) ([#12312](https://github.com/diegosouzapw/OmniRoute/pull/12312)) — thanks @HouMinXi +- **fix(combo):** Expose the two Auto-Combo scoring factors nobody could set — the combo validation schema and the dashboard weight sliders both declared 13 of the scorer's 15 factors, so `connectionDensity` (spreads load across a provider's connections) and `quality` were dropped on save and offered nowhere. The sliders also shipped their own default table that differed from the engine's on every non-zero factor and summed to 1.05, so the percentages shown next to them added up to 105%. Both lists now match `DEFAULT_WEIGHTS`, and a test keeps them there. Note that a combo whose stored `weights` omitted the two keys was effectively running with them at zero and the other thirteen renormalized upward; it now runs with the engine's intended distribution, so its routing does shift ([#12314](https://github.com/diegosouzapw/OmniRoute/pull/12314)) — thanks @maxmad64bis +- **fix(docs):** The free-tier reference no longer says its numbers come "confidence tagged per row" — no catalog entry carries a confidence tag and the API serves none, so every figure on that page is an estimate of the same, unstated quality. The page now states what an entry does vouch for: an independently documented hard stop (set by hand with the source in a comment, never defaulted to `true`) and a prompt-training disclosure, both with live counts the `check:docs-counts` gate keeps honest ([#12318](https://github.com/diegosouzapw/OmniRoute/pull/12318)) — thanks @maxmad64bis +- **fix(usage):** `adobe-firefly` and `firefly` have had a working usage fetcher since Adobe Firefly landed, but neither was ever added to the registration list, so the provider-plugin manifest, `genericQuotaFetcher` and the free-access quota cache all reported them as having no usage support — while `USAGE_SUPPORTED_PROVIDERS` said the opposite. Both are now declared, which also means their credit balance is fetched like any other declared provider's: `registerGenericQuotaFetchers` now registers a generic quota fetcher for them, and `resolveFreeAccessState` no longer returns early. A test holds the registration list to the dispatcher's switch in both directions, which is what the module's own docstring already asked for in prose ([#12321](https://github.com/diegosouzapw/OmniRoute/pull/12321)) — thanks @maxmad64bis +- **fix(quota):** drop the generic quota cache (agy / Antigravity / Claude OAuth) on an upstream 429 so reset-aware scoring does not keep a 60s stale snapshot, and force-refresh the next usage fetch so inner provider caches cannot recache the same window ([#12325](https://github.com/diegosouzapw/OmniRoute/pull/12325)) — thanks @HouMinXi +- **fix(combos):** deleting a combo now clears its persisted LKGP pins instead of leaving unreachable `key_value` rows behind ([#12326](https://github.com/diegosouzapw/OmniRoute/issues/12326)) ([#12425](https://github.com/diegosouzapw/OmniRoute/pull/12425)) — thanks @KrzysiekSko +- **fix(sse):** Keep ZWNJ (U+200C) and ZWJ (U+200D) in assistant text, reasoning and tool-call arguments — Persian/Kurdish half-space (`ارائه‌دهنده`), Arabic/Indic shaping and emoji sequences no longer lose them; the response de-obfuscation now removes joiners only between ASCII word characters, where the request side inserts them ([#12186](https://github.com/diegosouzapw/OmniRoute/issues/12186)) — thanks @rezjalibd ([#12359](https://github.com/diegosouzapw/OmniRoute/pull/12359)) — thanks @pacocartones +- **fix(resilience):** count resolved upstream 5xx results against the provider circuit breaker on the chat path — `CircuitBreaker.execute()` no longer reads a resolved `{ success: false, status: 5xx }` as a success that cancels the call-site failure, so a provider answering 503s now trips its breaker instead of staying `CLOSED` at `failureCount: 1`; single-model and combo dispatches are each accounted exactly once ([#12254](https://github.com/diegosouzapw/OmniRoute/issues/12254)) ([#12360](https://github.com/diegosouzapw/OmniRoute/pull/12360)) — thanks @pacocartones +- **fix(providers):** resolve the Codex quota auto-ping model from the live provider catalog and lifecycle registry instead of the retired `gpt-5.1-codex-mini`, and pause the ping with one actionable warning when no selectable Codex model exists rather than retrying a shut-down id every cooldown window ([#11905](https://github.com/diegosouzapw/OmniRoute/issues/11905)) ([#12361](https://github.com/diegosouzapw/OmniRoute/pull/12361)) — thanks @pacocartones +- **fix(api):** keep the `{created, data}` wrapper on combo-routed `/v1/images/generations` responses and default Codex image results to `b64_json` on both `/v1/images/generations` and `/v1/images/edits` so Codex CLI's built-in `image_gen` can decode them ([#12268](https://github.com/diegosouzapw/OmniRoute/issues/12268)) ([#12362](https://github.com/diegosouzapw/OmniRoute/pull/12362)) — thanks @pacocartones +- **fix(sse):** Name the shadowed custom provider node when a built-in provider id/alias (e.g. `openference` → `of`) reserves the prefix of an existing OpenAI/Anthropic-compatible node, so the runtime `No active credentials for provider: ` error explains that the prefix routed to the built-in and never reached the node's healthy connections, instead of contradicting the dashboard ([#11943](https://github.com/diegosouzapw/OmniRoute/issues/11943)) — thanks @morpheus9393 ([#12365](https://github.com/diegosouzapw/OmniRoute/pull/12365)) — thanks @pacocartones +- **fix(i18n):** wrap `ccOnboardingKeyPlaceholder` in ICU single quotes across all 43 locales so angle brackets render literally instead of being parsed as rich-text tags, which crashed the Claude Code onboarding block with `INVALID_MESSAGE: INVALID_TAG` ([#12302](https://github.com/diegosouzapw/OmniRoute/issues/12302)) ([#12369](https://github.com/diegosouzapw/OmniRoute/pull/12369)) — thanks @gonisulaimann +- **fix(models):** publish `effort_tiers` on Kimi K3's synced base-model entries (`kmca/k3`, `kmca/k3-256k`) so catalog-only clients (OpenCode, plain SDK pickers) can see and select the reasoning tiers (`low`/`high`/`max`) the synced metadata already carried — the `isSkippedEffortProvider` gate no longer suppresses tier visibility on those base entries, while synthetic `-` variant generation stays prevented and Codex/GLM base models remain excluded unchanged ([#12299](https://github.com/diegosouzapw/OmniRoute/issues/12299)) ([#12371](https://github.com/diegosouzapw/OmniRoute/pull/12371)) — thanks @gonisulaimann +- **fix(guardrails):** keep `auto`/`auto/*` virtual combos exempt from the Vision Bridge `fixedModel` credential guard so a combo target is passed through instead of silently falling back to global auto-selection ([#12237](https://github.com/diegosouzapw/OmniRoute/issues/12237)) ([#12373](https://github.com/diegosouzapw/OmniRoute/pull/12373)) — thanks @pacocartones +- **fix(combo):** capability-filter exhaustion caused by `max_tokens` above every target's known output limit now reports that reason (requested `max_tokens` vs the pool's highest known ceiling) instead of the unrelated "supports structured output" message ([#12229](https://github.com/diegosouzapw/OmniRoute/issues/12229)) — thanks @DW-MediaLab ([#12374](https://github.com/diegosouzapw/OmniRoute/pull/12374)) — thanks @pacocartones +- **fix(auth):** the `least-used` account strategy now prefers accounts without backoff before falling back to oldest `lastUsedAt`, the same tie-break `round-robin` already applies, so a failover no longer lands on a just-rate-limited account for a single request ([#12279](https://github.com/diegosouzapw/OmniRoute/issues/12279)) — thanks @tenshiak ([#12375](https://github.com/diegosouzapw/OmniRoute/pull/12375)) — thanks @pacocartones +- **fix(docker):** the `chatgpt-web-codex-browser` image now finds the Chrome binary under `chrome-linux64/` (Chrome for Testing layout in `playwright:v1.62.0-noble`) as well as the legacy `chrome-linux/`, so the container no longer crash-loops with `exec: --headless=new: not found` ([#12024](https://github.com/diegosouzapw/OmniRoute/issues/12024)) ([#12376](https://github.com/diegosouzapw/OmniRoute/pull/12376)) — thanks @pacocartones +- **fix(providers):** declare `groq/compound` and `allam-2-7b` as non-reasoning models in the curated Groq registry so `reasoning_effort` / `output_config.effort` / `thinking` from Claude Code are stripped instead of forwarded, which Groq rejected with HTTP 400 ([#12134](https://github.com/diegosouzapw/OmniRoute/issues/12134)) ([#12379](https://github.com/diegosouzapw/OmniRoute/pull/12379)) — thanks @pacocartones +- **fix(executors):** `OpencodeExecutor` no longer forces a direct connection when the connection has a proxy assigned in Proxy Management but no per-account proxies: the single-account fast path used to wrap the upstream dispatch in the direct-egress sentinel, discarding the ambient proxy context the chat handler had pinned from `proxy_assignments`, so API-key `opencode`/`opencode-go` connections egressed from the host IP (and hit geoblocks) despite the assignment. The direct pin is now applied only when no ambient proxy context exists ([#11894](https://github.com/diegosouzapw/OmniRoute/issues/11894) — thanks @hizzt) ([#12380](https://github.com/diegosouzapw/OmniRoute/pull/12380)) — thanks @pacocartones +- **fix(api):** `GET /v1/models` with `MODELS_CATALOG_PREFIX_MODE=canonical` (or `?prefix=canonical`) now lists providers whose registry alias is undefined or equal to their own id (Antigravity, Antigravity CLI and other self-aliased built-ins) — their single `provider/model` id was dropped by the alias/canonical duplicate guard in the static, synced, custom and alias-backed catalog loops ([#12058](https://github.com/diegosouzapw/OmniRoute/issues/12058)) — thanks @cheynetom ([#12381](https://github.com/diegosouzapw/OmniRoute/pull/12381)) — thanks @pacocartones +- **fix(translator):** Drop replayed `thinking` blocks that carry no signature (the shape produced from cross-provider `reasoning_content`) instead of stamping the default Claude signature on them, which Anthropic rejected with `400 Invalid signature in thinking block` on the next turn served by an Anthropic rung ([#12105](https://github.com/diegosouzapw/OmniRoute/issues/12105)) — thanks @atescivitci-cmd ([#12386](https://github.com/diegosouzapw/OmniRoute/pull/12386)) — thanks @pacocartones +- **fix(cli):** Resolve Bun's `--preload` polyfill path against the package root instead of `dist/`, so `omniroute` installed with `bun install -g` no longer crashes at startup with `error: preload not found …/dist/open-sse/utils/setupPolyfill.ts` ([#11980](https://github.com/diegosouzapw/OmniRoute/issues/11980)) — thanks @joglomedia ([#12387](https://github.com/diegosouzapw/OmniRoute/pull/12387)) — thanks @pacocartones +- **fix(providers):** `gemini-business` now publishes its model catalog — `/v1/models` and `/v1/providers/gemini-business/models` list the 12 enterprise Gemini ids the executor understands instead of returning an empty list (#12107) ([#12389](https://github.com/diegosouzapw/OmniRoute/pull/12389)) — thanks @pacocartones +- **fix(db):** install `busy_timeout` before the SQLite connection's first statement so a process opening the database while another one closes its WAL connection waits out the transient EXCLUSIVE lock instead of dying with `database is locked`, and recognise the drivers' real BUSY/PROTOCOL/IOERR errors as transient in the corruption probe so the same lock no longer renames the database away as corrupt; deflakes `cross-process contenders never both acquire the same connection` (#12394 — thanks @pacocartones) +- **fix(chat-admission):** derive the `chat_admission_busy` 503 `Retry-After` from observed heavyweight-lease occupancy — the larger of the exhausted `OMNIROUTE_CHAT_ADMISSION_QUEUE_MS` window and the time since capacity last turned over, capped at 60 s — instead of a fixed 1 s (structural) / 2 s (byte-stage) hint that invited Codex/agent fan-out clients to re-send ~1 MiB `/v1/responses` bodies every second into a gate held for the whole SSE lifetime; an idle gate keeps the historical floors ([#12135](https://github.com/diegosouzapw/OmniRoute/issues/12135)) (#12395 — thanks @pacocartones) +- **fix(api-manager):** the API key permissions modal no longer silently drops `allowedCombos` entries its Combo picker cannot render — routing-rule names such as `rt-*`, which the backend already honours — when "All" is clicked and the key is switched back to "Restrict"; those entries now survive the toggle, are listed read-only under the combo list so the count and the list agree, and are saved back verbatim instead of persisting `[]` (deny-all) (#12397 — thanks @pacocartones) +- **fix(catalog):** write the NUL separator of the catalog connection memo key, the provider serviceKind memo key, the Video Bridge promotion group key and a JSON-exactness test fixture as the `\u0000` escape instead of a raw byte — same runtime value, but the raw byte made git, GitHub and ripgrep treat those files as binary (hidden PR diffs, silently skipped searches); a guard test now keeps raw NUL bytes out of `src/`, `open-sse/` and `tests/` (#12403 — thanks @pacocartones) +- **fix(providers):** add `CLAUDE_CODE_CLIENT_VERSION` and `GITHUB_COPILOT_CLI_VERSION` env overrides so Anthropic/Copilot client-version gates can be unblocked without a rebuild ([#12417](https://github.com/diegosouzapw/OmniRoute/issues/12417)) ([#12632](https://github.com/diegosouzapw/OmniRoute/pull/12632)) — thanks @HouMinXi +- **fix(db):** back-fill `last_ping_at` and `last_pinged_reset_key` on `provider_connections` during schema reconciliation so divergent lineages that skipped `123_quota_auto_ping` still accept quota auto-ping writes ([#12470](https://github.com/diegosouzapw/OmniRoute/pull/12470) — thanks @KooshaPari) +- **fix(ci):** document MIT exceptions for `@eloqnt/{config,format-json,format-po}` (next-intl transitive; locked tarballs omit `license`) and keep the A2A lifecycle vitest off the real SQLite persistence seam ([#12581](https://github.com/diegosouzapw/OmniRoute/issues/12581)) ([#12595](https://github.com/diegosouzapw/OmniRoute/pull/12595)) +- OpenCode plugin `/v1/models` catalog fetch now waits 30s by default and attaches HTTP `statusCode` on 401/5xx so host fallback plugins can hop instead of seeing an untyped AbortError/UnknownError. ([#12607](https://github.com/diegosouzapw/OmniRoute/pull/12607)) — thanks @RaviTharuma +- **CI:** the OpenAPI security-tier gate now mirrors `isAlwaysProtectedPath()` in full — it also reads `ALWAYS_PROTECTED_API_PATTERNS`, so the pattern-gated credential routes (`/api/providers/{id}/{claude,codex}-auth/{export,apply-local}`, GHSA-5926-2w35-7h4q) no longer report as unannotated. (#12605) +- **fix(api):** GET /v1/models no longer waits forever on a hung coalesced catalog rebuild; cold-path waits are bounded (`CATALOG_BUILD_TIMEOUT_MS`, default 8s) and a last-good 200 is served when the rebuild times out ([#12627](https://github.com/diegosouzapw/OmniRoute/issues/12627)). ([#12628](https://github.com/diegosouzapw/OmniRoute/pull/12628)) — thanks @RaviTharuma +- **fix(cursor):** a non-array `tool_calls` on an assistant message no longer crashes the cursor request translator with a `TypeError`; both loops now require an array ([#12689](https://github.com/diegosouzapw/OmniRoute/issues/12689)) ([#12691](https://github.com/diegosouzapw/OmniRoute/pull/12691)) — thanks @soroush5 +- **fix(chat):** Chat Completions no longer return empty `content` after a server-owned memory or skills tool; the first provider send and account/model recovery now share one pipeline so a follow-up round-trip can fill the reply ([#12696](https://github.com/diegosouzapw/OmniRoute/issues/12696)) — thanks @HouMinXi ([#12867](https://github.com/diegosouzapw/OmniRoute/pull/12867)) +- **fix(dashboard):** batch-deleting provider keys no longer toasts failure after a successful delete when the confirm button's click event is forwarded as `onAfter` ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) — thanks @HouMinXi +- **fix(glm):** drop the extra 16th argument to `createSSETransformStreamWithLogger` that TypeScript rejected (TS2554) and that never reached the TransformStream ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) ([#12770](https://github.com/diegosouzapw/OmniRoute/pull/12770)) — thanks @HouMinXi +- **fix(dashboard):** pass `nodeMap` into Runtime `QuotaGroup` so a quota monitor in error/exhausted/alerting no longer throws `ReferenceError: nodeMap is not defined`. ([#12868](https://github.com/diegosouzapw/OmniRoute/pull/12868)) — thanks @HouMinXi +- Restricted API keys whose `allowedModels` lists a combo name no longer skip every combo member at pre-dispatch (`ALL_TARGETS_SKIPPED`). Inner-target filtering still applies when the allow-list is a provider prefix or `disableNonPublicModels` is on ([#12899](https://github.com/diegosouzapw/OmniRoute/pull/12899)). — thanks @HouMinXi +- **fix(combos):** Effort-suffixed combo members inherit the base model's `model_context_overrides` row so priority order is not inverted on large requests ([#12926](https://github.com/diegosouzapw/OmniRoute/pull/12926)) — thanks @HouMinXi +- **fix(catalog):** Picker-added `customModels` enter the dispatch-time live catalog so combo members and bare inference no longer 400 ([#12934](https://github.com/diegosouzapw/OmniRoute/pull/12934)) — thanks @HouMinXi +- **fix(docker):** re-derive the Docker build's worker-pool memory budget from the MEASURED ~4.5 GB per-process RSS (the issue owner's own VPS dmesg OOM-killer reproduction) instead of the stale 2560 MB/worker inference #11419 shipped, and lower `OMNIROUTE_BUILD_WORKERS` 3 → 2 so 1 parent + 1 page-data worker (2 processes × 4.5 GB = 9 GB) fits the 12.288 GB (75%) budget on a 16 GB GitHub Actions runner — the previous default (1 parent + 2 workers = 13.5 GB) still overcommitted the runner and kept "Publish to Docker Hub" failing with `cannot allocate memory` after #11419 merged (#7518). ([#11663](https://github.com/diegosouzapw/OmniRoute/pull/11663)) +- **fix(electron):** the packaged Windows desktop build now passes the #7592 cold-restart smoke end to end. Five defects were found and fixed on the way: (1) optional-pack staging failed on any machine whose `tar` is GNU tar — it reads the drive letter in an absolute `-f C:\...` archive path as a remote rsh host (`Cannot connect to C:`), so staging now passes a bare filename with `cwd` at the tarball directory and surfaces tar stderr on failure; (2) the Electron `files` allowlist was missing `lib/loginHeaderCapture.js`, so the packaged main process crashed at startup with `Cannot find module './lib/loginHeaderCapture'`; (3) electron-builder ≥26 injects an `!**/node_modules/**` ignore into every extraResources pattern list that no positive filter can override, which silently dropped the staged runtime `node_modules` (including the better-sqlite3 N-API prebuild) from `resources/app` — a fresh v3.8.50 desktop build would have shipped with no native SQLite driver and reproduced the #7592 sql.js fallback on every machine; an `afterPack` hook now restores the staged `node_modules` after packing; (4) the packaged-app smoke harness redirected `USERPROFILE` into its temp DATA_DIR, but Electron resolves the Roaming profile from `%USERPROFILE%\AppData\Roaming\` (USERPROFILE takes precedence over the APPDATA env var) and the path service throws instead of creating it, so `requestSingleInstanceLock()` returned false and the app exited(0) silently before `app.whenReady()` — the harness now pre-creates the derived tree, and `ensureSmokeEnvDirs` is exported and unit-tested; (5) the #7592 driver assertion parsed a `[DB] Driver: ...` line that the server's primary DB path never printed (only the unused `openDatabaseAsync()` did) — `getDbInstance()` now emits the same line on open so the guard can actually assert the native driver. Also: the smoke env-allowlist unit test hardcoded POSIX paths and could only pass on Linux/macOS; expectations are now host-agnostic, with new regression tests for the USERPROFILE-derived tree and `tarPack` under absolute Windows-style paths ([#11443](https://github.com/diegosouzapw/OmniRoute/pull/11443)) — thanks @jonlwheat2-gif +- fix(sse): stop the auto-combo candidates inspector from silently dropping model-locked/cooled-down rows (#9133) ([#11994](https://github.com/diegosouzapw/OmniRoute/pull/11994)) — thanks @hartmark +- **fix(providers):** Perplexity Web no longer turns upstream stream failures into successful assistant text; pre-content failures remain eligible for fallback, partial output ends with a structured sanitized error, and failed sessions are not persisted ([#12465](https://github.com/diegosouzapw/OmniRoute/pull/12465)) +- **fix(video-bridge):** preserve chronological video and transcript fusion, source-frame timestamps, transcript provenance, full contact-sheet intervals, and pre-abort callback isolation ([#11681](https://github.com/diegosouzapw/OmniRoute/pull/11681)) +- **Z.ai Web:** HTTP 200 streams carrying an upstream error now terminate with a structured failure instead of assistant text plus a normal stop, preserving partial output while allowing pre-content combo fallback. ([#12454](https://github.com/diegosouzapw/OmniRoute/pull/12454)) +- Absorb `Error [AbortError]: request_signal_aborted` and DOMException AbortError shapes in the process-level client-abort crash guard so routine client disconnects no longer kill the server (exit code 7). ([#12165](https://github.com/diegosouzapw/OmniRoute/pull/12165)) — thanks @alvinveroy +- **fix(sse):** Treat Adapta Web `type:error` SSE events as sanitized non-stream failures instead of empty HTTP 200 completions. ([#12459](https://github.com/diegosouzapw/OmniRoute/pull/12459)) +- **fix(autoCombo):** layer 3 (`models_dev_tier`) no longer scores retired models as premium, merge cross-provider capability rows deterministically, and inherit tier scores through variant ids ([#11508](https://github.com/diegosouzapw/OmniRoute/issues/11508)). `getModelsDevTierFitness` now vetoes ids present in `config/quality/model-lifecycle.json` with status `retired` before any other signal; `loadModelCapabilities()` aggregates per `model_id` (any-non-null-true for booleans, max for `limit_context`) instead of last-write-wins over SQLite's undefined row order; a capabilities miss retries through `resolveScoresAs()`'s base id and reports `models_dev_tier:inherited`, matching layers 1–2 (#11492). Adds `getModelsDevTierFitnessWithSource()` and an `invalidateCapabilitiesCache()` test/ops hook. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) — thanks @Meet6338-X +- fix(sse): accept Claude `content:[]` + `stop_reason: max_tokens`/`tool_use` in `detectMalformedNonStream` (match `isEmptyContentResponse`) so Claude Code `/model` probes with `max_tokens: 1` no longer become a false 502 ([#11568](https://github.com/diegosouzapw/OmniRoute/pull/11568)) — thanks @Seramicx +- **fix(security):** sanitize `request.failed` diagnostics before publishing them to live dashboard listeners and replay history, while keeping status, model, provider, latency, and internal call-log diagnostics intact. ([#12469](https://github.com/diegosouzapw/OmniRoute/pull/12469)) +- **fix(pwa):** do not intercept dashboard navigations so Chrome can fall back from a stale HTTP/3 Alt-Svc advertisement (UDP :20128 is unpublished; F5 on a long-lived tab hung until a new tab opened a fresh TCP connection). ([#12767](https://github.com/diegosouzapw/OmniRoute/pull/12767)) — thanks @HouMinXi +- **fix(executors):** handle DuckDuckGo ERR_BN_LIMIT (418) without retrying — when the upstream returns `418 ERR_BN_LIMIT` (rate-limit/ban), the executor now returns the error immediately instead of burning another VQD acquisition that would only count against the IP limit. The retry logic for `418 ERR_CHALLENGE` (unsolved challenge) remains unchanged. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) ([#12110](https://github.com/diegosouzapw/OmniRoute/pull/12110)) — thanks @oyi77 / @Meet6338-X +- Fixed DuckDuckGo streaming responses losing JSON lines and UTF-8 characters split across network chunks. ([#11528](https://github.com/diegosouzapw/OmniRoute/pull/11528)) — thanks @pacocartones +- Harden generated Cloudflare, Vercel, and Deno relays by stripping hop-by-hop, framing, and proxy authentication headers before forwarding requests upstream. ([#11533](https://github.com/diegosouzapw/OmniRoute/pull/11533)) — thanks @pacocartones +- **feat(grok-cli):** Provider Limits shows grok-cli banked reset credits from `GetRemainingResets` (including a real zero; a failed RPC omits the row) and the existing View credits button now calls `ConsumerUiSvc/RedeemReset` for grok-cli. Live tokens use nested fields 10/20/30 (id + Timestamp), not compact 1/2/3. ([#12805](https://github.com/diegosouzapw/OmniRoute/pull/12805)) — thanks @HouMinXi +- **fix(memory):** Embedding Model Quick select, Embedding Source remote dropdown, and Rerank selector now list every configured provider with embedding/rerank support instead of only chat-catalog text matches plus OpenRouter live discovery; a generic OpenAI-compatible `/embeddings` + Cohere-compatible `/rerank` runtime fallback resolves any configured chat provider's embedding/rerank endpoint, so unlisted providers no longer fail with "Unknown embedding provider"; both memory selectors gained a free-text model override ([#11390](https://github.com/diegosouzapw/OmniRoute/pull/11390)) — thanks @rqzbeh +- Harden SQLite upgrades around the historical migration-074 version collision: missing discovery and inspector tables are replayed atomically, pre-existing databases (including setup-created skeletons) receive reusable content-addressed safety snapshots, and Node test/eval probes without `DATA_DIR` are isolated from the operator database. ([#12435](https://github.com/diegosouzapw/OmniRoute/pull/12435)) +- **fix(dashboard):** Moonshot/Kimi Open Platform voucher and cash leftover percentages follow the bucket balance, so an empty wallet no longer paints those rows as 100% while Available is 0% ([#12733](https://github.com/diegosouzapw/OmniRoute/pull/12733)) — thanks @HouMinXi +- **fix(combo):** return non-retryable HTTP 400 when all candidates for a pinned native Codex turn are unavailable due to model-scoped lockout, terminating the turn cleanly while preserving turn continuity and enabling standard Combo routing on subsequent turns ([#12240](https://github.com/diegosouzapw/OmniRoute/pull/12240)) — thanks @mdigitalbh81 +- Fixed auto-router strategies preserving the selected connection through ranking and dispatch when multiple connections share the same provider and model. ([#11530](https://github.com/diegosouzapw/OmniRoute/pull/11530)) — thanks @pacocartones +- Preserve Chat Completions JSON-object response formats when translating requests to the Responses API. ([#11529](https://github.com/diegosouzapw/OmniRoute/pull/11529)) — thanks @pacocartones +- Fix idempotency fingerprints so Responses API requests include `input` and other semantic generation fields using stable serialization, while excluding credentials and request metadata. ([#11532](https://github.com/diegosouzapw/OmniRoute/pull/11532)) — thanks @pacocartones +- **fix(api):** Generated API CLI commands now enforce required OpenAPI request bodies; Combo test commands forward the required `comboName` body, while API keys created by older writers after migration 149 preserve legacy allow-all Combo access without widening explicit empty allowlists — thanks @marcelokarval ([#12070](https://github.com/diegosouzapw/OmniRoute/pull/12070)) +- **fix(grok-web):** treat upstream streaming failures as failures instead of successful assistant text: error-only streams now fail readiness with HTTP 502, while failures after legitimate content preserve that partial output and terminate through the sanitized stream failure path without a normal `stop` completion. ([#12458](https://github.com/diegosouzapw/OmniRoute/pull/12458)) +- HuggingChat now turns HTTP 200 JSONL generation failures into a sanitized 502 before content, or a fixed public stream failure after partial output, so fallback and request persistence no longer record a false successful stop. ([#12456](https://github.com/diegosouzapw/OmniRoute/pull/12456)) +- **fix(providers):** keep 1min.ai HTTP 200 stream errors out of assistant content, preserve partial output, and expose sanitized terminal errors so pre-content failures can fall back. ([#12466](https://github.com/diegosouzapw/OmniRoute/pull/12466)) +- **fix(sessions):** preserve legacy Sessions data when the additive exclusive-session projection is unavailable, returning an empty projection and warning only once per contiguous outage instead of failing the endpoint; the Sessions badge intentionally reflects the merged legacy and exclusive row count introduced by [#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389) ([#11469](https://github.com/diegosouzapw/OmniRoute/pull/11469)) — thanks @KaspaPulse +- Render Nimble Search with the generic provider icon and serve Opper's proven logo locally. ([#12415](https://github.com/diegosouzapw/OmniRoute/pull/12415)) +- **fix(quality):** `config/quality/provider-assets-provenance.jsonl` was stale against the physical `public/providers/` tree (still listing 225 assets and 5 duplicate-content groups after several provider-retirement PRs removed 85 icon files), which would have made `check:provider-asset-provenance` fail on the next full run. Reconciled the manifest to the real 142-file snapshot (142 asset records, 1 duplicate-content group, 2 newly-registered files marked `unresolved` pending provenance research), repinned `auditedCommit`, and updated the regression test's hardcoded counts to match. ([#11876](https://github.com/diegosouzapw/OmniRoute/pull/11876)) +- fix(ui): wrap quota card footer action buttons to prevent clipping Refresh button on narrow cards — when extra action buttons such as `View credits` are present on Codex cards, allow the footer row to flex-wrap with `shrink-0` buttons so `Refresh now` remains visible and clickable across all responsive card widths ([#11464](https://github.com/diegosouzapw/OmniRoute/pull/11464)) — thanks @adevwithpurpose +- Keep Antigravity Gemini usable when the same connection's Claude weekly quota is empty; generic quota cache stays per-connection for every other provider. ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) — thanks @HouMinXi +- Fixed Tailscale tunnel setup to reject ports outside the valid 1-65535 range and safely install Windows MSI packages from paths containing apostrophes. ([#11534](https://github.com/diegosouzapw/OmniRoute/pull/11534)) — thanks @pacocartones +- Classify Cloudflared and Tailscale process-management routes as local-only and spawn-capable while preserving remote access to read-only tunnel status endpoints. ([#11531](https://github.com/diegosouzapw/OmniRoute/pull/11531)) — thanks @pacocartones +- Electron release workflow: the `publish-npm` job now grants `actions: read` to the reusable `npm-publish.yml` it calls (its `publish` job requests it), which is what made GitHub refuse the whole v3.8.50 run at startup and ship the release with zero desktop assets; a `workflow_dispatch` now builds the requested tag instead of the dispatching branch and can skip the npm leg (`publish_npm=false`) when only re-attaching assets ([#11974](https://github.com/diegosouzapw/OmniRoute/pull/11974)) +- **fix(dashboard):** The Combos page usage guide now reads its dismissal through `useSyncExternalStore` instead of correcting SSR state inside an effect, removing an extra commit of the page tree on every load (and the `react-hooks/set-state-in-effect` error it raised). ([#12671](https://github.com/diegosouzapw/OmniRoute/pull/12671)) +- Electron release: `electron/package-lock.json` regained the optional `electron-builder-squirrel-windows` subtree (13 entries) that `npm ci` had been refusing as out of sync, `electron-release.yml` gained a `build_ref` dispatch input and stops regenerating release notes on a re-attach dispatch, and the npm publish workflow attaches the SBOM to the GitHub Release on dispatch publishes too — so the v3.8.51 tag ships every desktop asset and the SBOM like v3.8.49 did ([#12022](https://github.com/diegosouzapw/OmniRoute/pull/12022)) +- Drained the reds every PR against `release/v3.8.51` was born with: documented the three service-worker build-id variables, registered the six retirement/tunnel tests with the mutation gate, approved `eslint-plugin-react-hooks` in the dependency allowlist, added the six `combo.sort.*` strings to `vi` and `pt-BR`, pointed the ChatGPT Web doc at the real migration-168 test, worded the g4f hint around the member key, and realigned four tests to the retired-provider catalog and the legacy-schema fixtures the retirement migrations touch. ([#11940](https://github.com/diegosouzapw/OmniRoute/pull/11940)) +- **fix(providers):** Claude, Grok, LMArena, Notion, and Perplexity web-cookie transports now use pooled `wreq-js` 3.2 instead of the native sidecar, with all nine supported bindings pinned and audited, and the applicable platform binding plus native-license evidence included in each release artifact ([#12429](https://github.com/diegosouzapw/OmniRoute/pull/12429), supersedes [#11753](https://github.com/diegosouzapw/OmniRoute/pull/11753)). +- **fix(providers):** Zed Hosted streaming failures now trigger fallback before content and end partial streams with a sanitized structured error instead of fake assistant text and a normal-success stop. ([#12455](https://github.com/diegosouzapw/OmniRoute/pull/12455)) +- **fix(test):** capture hook result after render (direct commit `b85d0abb0b`) +- **fix(sync):** repair the two regressions the v3.8.50 sync-back left on release/v3.8.51 (direct commit `5ade9e0851`) +- **revert(models):** drop the memo reset hook ported from #10055 (direct commit `282de841ae`) +- **fix(typecheck):** guard the optional connectionId before the reactive model sync (direct commit `f2825bad75`) +- **fix(quota-share):** release the winner's reserved in-flight slot (#11371) ([#11408](https://github.com/diegosouzapw/OmniRoute/pull/11408)) — thanks @oyi77 +- **fix(providers):** stop silently dropping reasoning effort on opencode families (#10788) ([#11409](https://github.com/diegosouzapw/OmniRoute/pull/11409)) — thanks @oyi77 +- **fix(combo):** enable genuine quota-aware routing for generic providers (antigravity, claude, etc.) ([#11411](https://github.com/diegosouzapw/OmniRoute/pull/11411)) — thanks @HouMinXi +- **fix(oauth):** allow expired token retry before deactivation and activate expired retry loop (#P0) ([#11414](https://github.com/diegosouzapw/OmniRoute/pull/11414)) — thanks @HouMinXi +- **perf(executors):** lazy-load the executor registry — defer class imports + construction to first use (#11220) ([#11421](https://github.com/diegosouzapw/OmniRoute/pull/11421)) — thanks @oyi77 +- **fix:** make release/v3.8.50 CI gates green (run 32786966560 root causes) ([#11450](https://github.com/diegosouzapw/OmniRoute/pull/11450)) — thanks @jonlwheat2-gif +- **fix(sse):** exact-domain cookie match and origin-equality URL assertions ([#11452](https://github.com/diegosouzapw/OmniRoute/pull/11452)) — thanks @hartmark +- **fix(providers):** send Responses API payload during OpenAI-compatible validation ([#11454](https://github.com/diegosouzapw/OmniRoute/pull/11454)) — thanks @tuandinh0801 +- **fix(command-code):** fallback to /alpha/generate for Go plan without Provider API access ([#11455](https://github.com/diegosouzapw/OmniRoute/pull/11455)) — thanks @Chewji9875 +- **fix(responses-continuation):** fail closed on a log-truncated stored input/output array ([#11473](https://github.com/diegosouzapw/OmniRoute/pull/11473)) — thanks @hartmark +- **fix(cli):** write jcode settings as [providers.omniroute] in config.toml ([#11484](https://github.com/diegosouzapw/OmniRoute/pull/11484)) — thanks @hongnoul +- **fix(logging):** raise chat-log array truncation cap to 1000 and unify duplicate implementations ([#11499](https://github.com/diegosouzapw/OmniRoute/pull/11499)) — thanks @hartmark +- **fix:** restore live websocket URL helpers ([#11509](https://github.com/diegosouzapw/OmniRoute/pull/11509)) — thanks @PauloFH +- **perf(core):** eliminate async waterfalls across home, cache, and db operations (#11396) ([#11512](https://github.com/diegosouzapw/OmniRoute/pull/11512)) — thanks @HouMinXi +- **fix(kiro):** add runtime.us-east-1.kiro.dev as first-attempt endpoint ([#11517](https://github.com/diegosouzapw/OmniRoute/pull/11517)) — thanks @rifqiawl +- **fix(providers):** remove duplicate 128-tool truncation in OpencodeExecutor (#11444) ([#11518](https://github.com/diegosouzapw/OmniRoute/pull/11518)) — thanks @HouMinXi +- **fix(providers):** poll onboardUser LRO before classifying BYOP ([#11519](https://github.com/diegosouzapw/OmniRoute/pull/11519)) — thanks @rifqiawl +- **fix(translator):** preserve exact tool name casing for Claude tool_use payloads (#11487) ([#11520](https://github.com/diegosouzapw/OmniRoute/pull/11520)) — thanks @HouMinXi +- **fix(kimi-web):** align health probe with executor domain ([#11521](https://github.com/diegosouzapw/OmniRoute/pull/11521)) — thanks @pacocartones +- **fix(cli):** preserve existing admin password during setup ([#11522](https://github.com/diegosouzapw/OmniRoute/pull/11522)) — thanks @pacocartones +- **fix(mitm):** keep Windows DNS elevation off the POSIX sudo path (#11430) ([#11542](https://github.com/diegosouzapw/OmniRoute/pull/11542)) — thanks @official-burak +- **fix(sse):** scale chat admission by ingest byte budget instead of a fixed request count ([#11548](https://github.com/diegosouzapw/OmniRoute/pull/11548)) — thanks @TheDemonTuan +- **fix:** accept Gemini Web cookie exports ([#11554](https://github.com/diegosouzapw/OmniRoute/pull/11554)) — thanks @rafacpti23 +- **fix(i18n):** complete Brazilian Portuguese translations ([#11558](https://github.com/diegosouzapw/OmniRoute/pull/11558)) — thanks @rafacpti23 +- **fix(sse):** explicit types for openai-responses pureHelpers — clears last failing core typecheck gate ([#11567](https://github.com/diegosouzapw/OmniRoute/pull/11567)) — thanks @jonlwheat2-gif +- **fix(opencode):** hydrate Proxy Pool references for no-auth connections ([#11584](https://github.com/diegosouzapw/OmniRoute/pull/11584)) — thanks @AStupidBear +- **fix(ci):** resolve inherited release/v3.8.51 base-red CI failures ([#11608](https://github.com/diegosouzapw/OmniRoute/pull/11608)) — thanks @jonlwheat2-gif +- **fix(db):** fallback load STORAGE_ENCRYPTION_KEY from env files ([#11614](https://github.com/diegosouzapw/OmniRoute/pull/11614)) — thanks @benzntech +- **fix(providers):** mark Brave Search as no longer having a free tier ([#11621](https://github.com/diegosouzapw/OmniRoute/pull/11621)) — thanks @turbolego +- **fix:** pass tool result content through as string in OpenAI/Claude -> Gemini translation ([#11624](https://github.com/diegosouzapw/OmniRoute/pull/11624)) — thanks @Hsia97 +- **fix(sse):** scope credential health to exact key ([#11628](https://github.com/diegosouzapw/OmniRoute/pull/11628)) — thanks @KaspaPulse +- **fix(install):** guard prepare script against missing husky ([#11634](https://github.com/diegosouzapw/OmniRoute/pull/11634)) — thanks @Prajeeth-12 +- **fix(security):** redact NVIDIA_API_KEY at the log sink in the ad-hoc diag script ([#11639](https://github.com/diegosouzapw/OmniRoute/pull/11639)) +- **fix(types):** clear the two type regressions red on release/v3.8.51 ([#11640](https://github.com/diegosouzapw/OmniRoute/pull/11640)) — thanks @jonlwheat2-gif +- **fix(build):** resolve workspace: protocol dependencies to concrete semver versions during prepublish (#11540) ([#11641](https://github.com/diegosouzapw/OmniRoute/pull/11641)) — thanks @HouMinXi +- **fix(search):** properly resolve configured search connection in /v1/responses pipeline (#11524) ([#11642](https://github.com/diegosouzapw/OmniRoute/pull/11642)) — thanks @HouMinXi +- **fix(authz):** trust cf-connecting-ip for via-proxy detection when peer is Cloudflare edge (#11514) ([#11643](https://github.com/diegosouzapw/OmniRoute/pull/11643)) — thanks @HouMinXi +- **fix(security):** compare the OIDC state cookie in constant time ([#11646](https://github.com/diegosouzapw/OmniRoute/pull/11646)) +- **fix(security):** namespace the dedup hash by the calling API key ([#11649](https://github.com/diegosouzapw/OmniRoute/pull/11649)) +- **fix(models):** gate the Codex-native catalog loop on prefix mode (#11632) ([#11662](https://github.com/diegosouzapw/OmniRoute/pull/11662)) — thanks @xiaoyaner0201 +- **fix(dashboard):** project Codex routing quota in account pools ([#11666](https://github.com/diegosouzapw/OmniRoute/pull/11666)) — thanks @KaspaPulse +- **fix(ddgw):** normaliza system no upstream da release v3.8.51 ([#11692](https://github.com/diegosouzapw/OmniRoute/pull/11692)) — thanks @RhianB14 +- **fix(memory):** honor category filter in GET /api/memory ([#11699](https://github.com/diegosouzapw/OmniRoute/pull/11699)) — thanks @Sabeekhann +- **fix(ci):** detect stale workspace lockfile entries ([#11714](https://github.com/diegosouzapw/OmniRoute/pull/11714)) — thanks @pacocartones +- **fix(ci):** stamp dist/BUILD_SHA before the npm publish provenance gate ([#11721](https://github.com/diegosouzapw/OmniRoute/pull/11721)) +- **fix(docker):** decouple the best-effort Bun image from the release manifest ([#11724](https://github.com/diegosouzapw/OmniRoute/pull/11724)) +- **perf(compression):** avoid cloning memo misses twice ([#11727](https://github.com/diegosouzapw/OmniRoute/pull/11727)) — thanks @RaviTharuma +- **fix(docker):** let the bun digest artifact be absent, not fatal ([#11740](https://github.com/diegosouzapw/OmniRoute/pull/11740)) +- **fix(shared):** block cloud-metadata hosts under default remote-image guard ([#11755](https://github.com/diegosouzapw/OmniRoute/pull/11755)) — thanks @santosraju99-hub +- **fix(api):** preserve caller-provided X-Correlation-Id on chat completions ([#11760](https://github.com/diegosouzapw/OmniRoute/pull/11760)) — thanks @aniruddhaadak80 +- **fix(resilience):** lock permanently retired models instead of short backoff (Gemini ban prevention) ([#11762](https://github.com/diegosouzapw/OmniRoute/pull/11762)) — thanks @turbolego +- **fix:** mark Vercel AI Gateway as passthroughModels ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **fix(resilience):** stop hammering permanently-moved endpoints and billing-suspended accounts ([#11774](https://github.com/diegosouzapw/OmniRoute/pull/11774)) — thanks @turbolego +- **fix(lease):** remove global static reservation and gate routing on live active lease occupancy ([#11775](https://github.com/diegosouzapw/OmniRoute/pull/11775)) — thanks @TheDemonTuan +- **fix(ci):** size the install-upgrade gate to a measured run, and log the pack cost ([#11776](https://github.com/diegosouzapw/OmniRoute/pull/11776)) +- **fix(pwa):** stop serving the stale shell after deploys ([#11779](https://github.com/diegosouzapw/OmniRoute/pull/11779)) — thanks @HouMinXi +- **fix(combo):** lock GitHub models rejected as "not supported" for future requests ([#11781](https://github.com/diegosouzapw/OmniRoute/pull/11781)) — thanks @turbolego +- **fix(cli):** probe both IPv4 and IPv6 loopback for server readiness (#11766) ([#11794](https://github.com/diegosouzapw/OmniRoute/pull/11794)) — thanks @vermasomesh835 +- **fix(sse):** preserve CLIProxyAPI wrappers per connection ([#11797](https://github.com/diegosouzapw/OmniRoute/pull/11797)) — thanks @RaviTharuma +- **fix(providers):** honor model auto-fetch opt-in on create ([#11798](https://github.com/diegosouzapw/OmniRoute/pull/11798)) — thanks @RaviTharuma +- **fix(dashboard):** make model auto-fetch order independent ([#11805](https://github.com/diegosouzapw/OmniRoute/pull/11805)) — thanks @RaviTharuma +- **fix(docker):** make GHA cache exports best effort ([#11806](https://github.com/diegosouzapw/OmniRoute/pull/11806)) — thanks @RaviTharuma +- **fix(kiro):** do not permanently ban on 'User is not authorized to make this call' ([#11809](https://github.com/diegosouzapw/OmniRoute/pull/11809)) — thanks @Deftera186 +- **fix(sse):** keep cache-write tokens in OpenAI-shaped usage ([#11814](https://github.com/diegosouzapw/OmniRoute/pull/11814)) — thanks @TheDemonTuan +- **fix(docs):** resolve relative markdown and wiki links across Fumadocs and GitHub wiki ([#11834](https://github.com/diegosouzapw/OmniRoute/pull/11834)) — thanks @AndrianBalanescu +- **fix(chat):** preserve unstripped model string for passthrough provider routing ([#11840](https://github.com/diegosouzapw/OmniRoute/pull/11840)) — thanks @raheemuddin786 +- **fix(mcp):** avoid async initialization inside sync esm wrappers (#11569) ([#11849](https://github.com/diegosouzapw/OmniRoute/pull/11849)) — thanks @HouMinXi +- **fix(sse):** preserve coherent markdown token boundaries across stream translation chunks (#11606) ([#11851](https://github.com/diegosouzapw/OmniRoute/pull/11851)) — thanks @HouMinXi +- **fix(ci):** run the install-upgrade gate on disk, not on the /tmp tmpfs ([#11855](https://github.com/diegosouzapw/OmniRoute/pull/11855)) +- **fix(skills):** expand shorthand property types in injected tool schemas ([#11857](https://github.com/diegosouzapw/OmniRoute/pull/11857)) — thanks @alvinveroy +- **fix(ci):** publish npm from a hosted runner so provenance is accepted ([#11877](https://github.com/diegosouzapw/OmniRoute/pull/11877)) +- **fix(lint):** pin react hooks plugin to 7.0.1 ([#11886](https://github.com/diegosouzapw/OmniRoute/pull/11886)) +- **fix(ci):** surface and clear release-green ESLint failure ([#11890](https://github.com/diegosouzapw/OmniRoute/pull/11890)) — thanks @MumuTW +- **fix(guardrails):** prevent duplicate prompt-injection-guard log output ([#11936](https://github.com/diegosouzapw/OmniRoute/pull/11936)) — thanks @patrykkopycinski +- **fix(auth):** downgrade expected transient states from warn to debug ([#11937](https://github.com/diegosouzapw/OmniRoute/pull/11937)) — thanks @patrykkopycinski +- **fix(codex):** fail over image generation for imported free plans ([#11948](https://github.com/diegosouzapw/OmniRoute/pull/11948)) — thanks @Bl0ck154 +- **fix(codex):** normalize non-stream responses ([#11951](https://github.com/diegosouzapw/OmniRoute/pull/11951)) — thanks @Bl0ck154 +- **fix(images):** forward Antigravity image size ([#11952](https://github.com/diegosouzapw/OmniRoute/pull/11952)) — thanks @Bl0ck154 +- **fix(db):** invalidate connection cache after upsert ([#11953](https://github.com/diegosouzapw/OmniRoute/pull/11953)) — thanks @Bl0ck154 +- **fix(codex):** restore imported account state ([#11954](https://github.com/diegosouzapw/OmniRoute/pull/11954)) — thanks @Bl0ck154 +- **fix(sse):** give extended-thinking targets the reasoning readiness budget ([#11959](https://github.com/diegosouzapw/OmniRoute/pull/11959)) — thanks @abhisheksharma2411 +- **fix(dashboard):** use opaque background and readable text color on cost chart tooltips ([#11960](https://github.com/diegosouzapw/OmniRoute/pull/11960)) — thanks @ZaimMarzuki +- **fix(antigravity):** send complete loadCodeAssist metadata (ideType/platform/pluginType as numeric enums) ([#11969](https://github.com/diegosouzapw/OmniRoute/pull/11969)) — thanks @rifqiawl +- **fix(core):** resolve DB init race condition and reasoning translation ([#12003](https://github.com/diegosouzapw/OmniRoute/pull/12003)) — thanks @raheemuddin786 +- **fix(admission):** exclude reclaimable page cache from the cgroup pressure ratio ([#12017](https://github.com/diegosouzapw/OmniRoute/pull/12017)) — thanks @HouMinXi +- **fix(build):** prune non-production trees in NFT trace excludes and tsconfig ([#12028](https://github.com/diegosouzapw/OmniRoute/pull/12028)) — thanks @Chewji9875 +- **fix(migrations):** add renamed migration compatibility for 056/073/077/101 ([#12036](https://github.com/diegosouzapw/OmniRoute/pull/12036)) — thanks @oyi77 +- **fix(sse):** default crash-guard logger to console.warn, not console ([#12042](https://github.com/diegosouzapw/OmniRoute/pull/12042)) — thanks @HouMinXi +- **fix(catalog):** derive vision/modalities for built-in auto combos from effective target pool ([#12046](https://github.com/diegosouzapw/OmniRoute/pull/12046)) — thanks @Prajeeth-12 +- **fix(release):** the packaged-app smoke verifies the database opened, not a driver line the primary path never prints (twin of #12032) ([#12047](https://github.com/diegosouzapw/OmniRoute/pull/12047)) +- **fix(barrel):** migrate src/app/ off the @/lib/localDb barrel import (#11795 Phase 2) ([#12051](https://github.com/diegosouzapw/OmniRoute/pull/12051)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate src/lib/ off the localDb barrel to direct db imports (#59) ([#12052](https://github.com/diegosouzapw/OmniRoute/pull/12052)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate open-sse, src/shared, src/sse, src/models, src/domain off the @/lib/localDb barrel import (#11795 Phase 4) ([#12053](https://github.com/diegosouzapw/OmniRoute/pull/12053)) — thanks @jonlwheat2-gif +- **fix(barrel):** delete the @/lib/localDb barrel — every consumer migrated (#11795 Phase 5) ([#12055](https://github.com/diegosouzapw/OmniRoute/pull/12055)) — thanks @jonlwheat2-gif +- **fix(radar):** restore D12 public boundary ([#12057](https://github.com/diegosouzapw/OmniRoute/pull/12057)) +- **fix(dev):** bound webpack and Tailwind scans ([#12075](https://github.com/diegosouzapw/OmniRoute/pull/12075)) — thanks @backryun +- **fix(dev):** isolate root layout settings reads ([#12076](https://github.com/diegosouzapw/OmniRoute/pull/12076)) — thanks @backryun +- **fix(dev):** reduce instrumentation executor fan-out (phase 3) ([#12078](https://github.com/diegosouzapw/OmniRoute/pull/12078)) — thanks @backryun +- **fix(dev):** make logging resources HMR-singleton ([#12079](https://github.com/diegosouzapw/OmniRoute/pull/12079)) — thanks @backryun +- **fix(sse):** keep unavailable forced connections scoped ([#12080](https://github.com/diegosouzapw/OmniRoute/pull/12080)) — thanks @keeltrace +- **fix(dev):** isolate batch dispatch from instrumentation ([#12081](https://github.com/diegosouzapw/OmniRoute/pull/12081)) — thanks @backryun +- **fix(ci):** reconcile release test contract drift ([#12082](https://github.com/diegosouzapw/OmniRoute/pull/12082)) — thanks @backryun +- **fix(ci):** accept CVE-2025-68121 in the prebuilt tls-client .so, auto-close base-red issues, guard Scorecard on the default branch ([#12085](https://github.com/diegosouzapw/OmniRoute/pull/12085)) +- **fix(db):** drop three consumer-less 1proxy exports — dead-code base-red on release/v3.8.51 after the barrel deletion (#12055) ([#12087](https://github.com/diegosouzapw/OmniRoute/pull/12087)) +- **fix(api):** clear the six API-route TypeScript regressions the new gate landed red on ([#12094](https://github.com/diegosouzapw/OmniRoute/pull/12094)) +- **fix(release):** never let the tag-push Create Release append auto notes to the curated body ([#12096](https://github.com/diegosouzapw/OmniRoute/pull/12096)) +- **fix(sse):** honor CLIProxyAPI environment API key ([#12099](https://github.com/diegosouzapw/OmniRoute/pull/12099)) — thanks @RaviTharuma +- **fix(oauth):** bind Google refresh to the client that issued the token ([#12106](https://github.com/diegosouzapw/OmniRoute/pull/12106)) — thanks @HouMinXi +- **fix(dashboard):** make RequestLoggerDetail loadable outside Next — CSS via globals.css + CJS/ESM interop (#11703 base-reds) ([#12114](https://github.com/diegosouzapw/OmniRoute/pull/12114)) +- **fix(leases):** project status lease row to lease columns so joined connection PII never escapes ([#12115](https://github.com/diegosouzapw/OmniRoute/pull/12115)) — thanks @geek007git +- **fix(plugins):** do not kill the plugin process when a fire-and-forget hook times out ([#12116](https://github.com/diegosouzapw/OmniRoute/pull/12116)) — thanks @geek007git +- **fix(guardrails):** restore injection-guard logging on middleware-only routes ([#12117](https://github.com/diegosouzapw/OmniRoute/pull/12117)) — thanks @geek007git +- **fix(plugins):** refresh stored manifest from disk on activate so new hook fields reach existing installs ([#12120](https://github.com/diegosouzapw/OmniRoute/pull/12120)) — thanks @geek007git +- **fix(codex):** preserve existing provider state when bulk-import upserts a matching connection ([#12122](https://github.com/diegosouzapw/OmniRoute/pull/12122)) — thanks @geek007git +- **fix(ci):** clear the two base-reds the 2026-08-30 merge batch left on release/v3.8.51 (round 3) ([#12123](https://github.com/diegosouzapw/OmniRoute/pull/12123)) +- **fix(sse):** strip type:'custom' from Claude tools on agentrouter dispatch ([#12126](https://github.com/diegosouzapw/OmniRoute/pull/12126)) — thanks @wahidsadik371-coder +- **fix(sse):** stop advertising video providers the dispatcher cannot run ([#12131](https://github.com/diegosouzapw/OmniRoute/pull/12131)) — thanks @quiterunner-commits +- **fix(search):** treat HTTP 432 and plan limit errors as transient cooldown ([#12139](https://github.com/diegosouzapw/OmniRoute/pull/12139)) — thanks @HouMinXi +- **fix(sse):** refuse an AI Horde queue that cannot fit the request budget ([#12143](https://github.com/diegosouzapw/OmniRoute/pull/12143)) — thanks @quiterunner-commits +- **fix(ci):** clear the base-reds the afternoon merge batch left on release/v3.8.51 (round 5: provider count 352, TS2554/TS2677) ([#12144](https://github.com/diegosouzapw/OmniRoute/pull/12144)) +- **fix(translator):** strip neutral tool_choice when tools absent in Responses-to-Chat (#12141) ([#12166](https://github.com/diegosouzapw/OmniRoute/pull/12166)) — thanks @HouMinXi +- **fix(guardrails):** pass providerId to getResolvedModelCapabilities in checkComboVision (#12112) ([#12169](https://github.com/diegosouzapw/OmniRoute/pull/12169)) — thanks @HouMinXi +- **fix(translator):** strip plaintext reasoning content for opaque responses backends (#12128) ([#12171](https://github.com/diegosouzapw/OmniRoute/pull/12171)) — thanks @HouMinXi +- **perf(stream):** compile hot-path regexes once, bound token caches, fix quadratic buffering ([#12179](https://github.com/diegosouzapw/OmniRoute/pull/12179)) — thanks @opensource-elearning +- **fix:** resolve compression worker file using runtime anchors instead of… ([#12183](https://github.com/diegosouzapw/OmniRoute/pull/12183)) — thanks @NightStalker-87 +- **fix:** prevent Claude Code session kills via liveness-aware readiness + auto model echo ([#12189](https://github.com/diegosouzapw/OmniRoute/pull/12189)) — thanks @opensource-elearning +- **fix(translator):** preserve falsy primitive values in Gemini and Antigravity function response results ([#12191](https://github.com/diegosouzapw/OmniRoute/pull/12191)) — thanks @Siva010 +- **fix(oauth):** mark empty Antigravity projectId as degraded and clear stale errors (#11284) ([#12205](https://github.com/diegosouzapw/OmniRoute/pull/12205)) — thanks @HouMinXi +- **fix(providers):** gate the Codex auto-ping usage read on the shared quota throttle ([#12209](https://github.com/diegosouzapw/OmniRoute/pull/12209)) — thanks @abhisheksharma2411 +- **fix(combos):** prioritize SQLite row id over inner JSON id and notify delete errors ([#12213](https://github.com/diegosouzapw/OmniRoute/pull/12213)) — thanks @HouMinXi +- **fix(providers):** mark featherless.ai as no longer having a free tier (`hasFree: false`) ([#12216](https://github.com/diegosouzapw/OmniRoute/pull/12216)) — thanks @turbolego +- **fix(deepseek-web):** stop Turbopack dev panic in the PoW worker path resolver ([#12221](https://github.com/diegosouzapw/OmniRoute/pull/12221)) — thanks @hartmark +- **fix(chat-admission):** clarify local 503 source ([#12223](https://github.com/diegosouzapw/OmniRoute/pull/12223)) — thanks @keeltrace +- **fix(dev):** silence webpack runtime module warnings ([#12228](https://github.com/diegosouzapw/OmniRoute/pull/12228)) — thanks @backryun +- **fix(db):** use module.require for CommonJS runtime driver loading ([#12230](https://github.com/diegosouzapw/OmniRoute/pull/12230)) — thanks @raheemuddin786 +- **fix(memory):** honest probe-driven FTS5 keyword status + memory_id rowid sync ([#12231](https://github.com/diegosouzapw/OmniRoute/pull/12231)) — thanks @raheemuddin786 +- **fix(system):** propagate abort signal to stream reader in HTTP version checks ([#12232](https://github.com/diegosouzapw/OmniRoute/pull/12232)) — thanks @raheemuddin786 +- **fix(resilience):** isolate local host execution errors from provider circuit breakers ([#12233](https://github.com/diegosouzapw/OmniRoute/pull/12233)) — thanks @raheemuddin786 +- **fix(sse):** sort injected tools deterministically for prompt caching ([#12234](https://github.com/diegosouzapw/OmniRoute/pull/12234)) — thanks @raheemuddin786 +- **fix(combo):** always clear the loop-safety timer, not just on the happy path (#11804) ([#12245](https://github.com/diegosouzapw/OmniRoute/pull/12245)) +- **fix(quality):** base-red pair — stryker tap registration + turn-pin suites aligned to the window gate ([#12255](https://github.com/diegosouzapw/OmniRoute/pull/12255)) — thanks @backryun +- **fix(dev):** qualify Turbopack runtime boundaries ([#12258](https://github.com/diegosouzapw/OmniRoute/pull/12258)) — thanks @backryun +- **fix(combo):** fall back to full pool when collapsed sole survivor is context-too-small ([#12278](https://github.com/diegosouzapw/OmniRoute/pull/12278)) — thanks @opensource-elearning +- **fix(combo):** bound the pre-dispatch unavailable skip so a stale label cannot dark a pool (#12168) ([#12285](https://github.com/diegosouzapw/OmniRoute/pull/12285)) +- **fix(security):** strip Qwen/Alibaba console-session cookies from provider API responses ([#12287](https://github.com/diegosouzapw/OmniRoute/pull/12287)) +- **fix(usage):** console-aware Token Plan guidance + subscription hint on bailian 401 ([#12288](https://github.com/diegosouzapw/OmniRoute/pull/12288)) +- **fix(resourcePressure):** log numeric detail on every rejection, recover faster ([#12293](https://github.com/diegosouzapw/OmniRoute/pull/12293)) — thanks @hartmark +- **fix(release):** drain the 2026-09-01 base-red window — passthrough usage regression + radarPage i18n keys ([#12327](https://github.com/diegosouzapw/OmniRoute/pull/12327)) +- **fix(ci):** openapi-security-tiers checker must honor routeGuard patterns + imported prefixes ([#12350](https://github.com/diegosouzapw/OmniRoute/pull/12350)) — thanks @ggiak +- **perf(ci):** cache node_modules in the npm-ci-retry composite (#8084 D3) ([#12408](https://github.com/diegosouzapw/OmniRoute/pull/12408)) +- **revert(ui):** point CTAs back at their real destinations ([#12410](https://github.com/diegosouzapw/OmniRoute/pull/12410)) +- **fix(release):** drain the 2026-09-02 base-red — rerank-providers import + api-typecheck baseline ratchet ([#12414](https://github.com/diegosouzapw/OmniRoute/pull/12414)) +- **fix(types):** make system prompt injection noImplicitAny-safe ([#12416](https://github.com/diegosouzapw/OmniRoute/pull/12416)) +- **fix(quality):** validate typecheck baseline schema ([#12419](https://github.com/diegosouzapw/OmniRoute/pull/12419)) +- **fix(memory):** resolve rerank provider node cache import ([#12421](https://github.com/diegosouzapw/OmniRoute/pull/12421)) +- **fix(ci):** repair release-root regressions — pack dedup, web-session syntax, uc-image ids ([#12423](https://github.com/diegosouzapw/OmniRoute/pull/12423)) — thanks @backryun +- **fix(providers):** separate MaxAI and UC credential contracts ([#12431](https://github.com/diegosouzapw/OmniRoute/pull/12431)) +- **fix(cli):** preserve Claude settings on config set ([#12432](https://github.com/diegosouzapw/OmniRoute/pull/12432)) — thanks @KrzysiekSko +- **fix(providers):** repair the maxai credential block truncated by merge auto-resolve ([#12433](https://github.com/diegosouzapw/OmniRoute/pull/12433)) +- **fix(quality):** record the 2026-09-02 merged growth in the file-size baseline ([#12434](https://github.com/diegosouzapw/OmniRoute/pull/12434)) +- **fix(uc):** route UC error strings through sanitizeErrorMessage; allowlist the retired codex id ([#12437](https://github.com/diegosouzapw/OmniRoute/pull/12437)) +- **fix(adapta):** redact streamed upstream errors ([#12438](https://github.com/diegosouzapw/OmniRoute/pull/12438)) +- **fix(sse):** remap non-contiguous upstream tool_calls index to a gap-free output_index ([#12445](https://github.com/diegosouzapw/OmniRoute/pull/12445)) — thanks @hartmark +- **fix(quality):** detect duplicate tool_calls entries in one response ([#12446](https://github.com/diegosouzapw/OmniRoute/pull/12446)) — thanks @hartmark +- **fix(conversations):** resolve turn content OmniRoute never sends back to the client ([#12447](https://github.com/diegosouzapw/OmniRoute/pull/12447)) — thanks @hartmark +- **fix(combo):** do not treat credits-exhausted 401 as auth skip ([#12449](https://github.com/diegosouzapw/OmniRoute/pull/12449)) — thanks @RaviTharuma +- **fix(auth):** do not park healthy quota accounts as expired ([#12452](https://github.com/diegosouzapw/OmniRoute/pull/12452)) — thanks @RaviTharuma +- **fix(responses-continuation):** fail closed on a collector-truncated, empty output array ([#12460](https://github.com/diegosouzapw/OmniRoute/pull/12460)) — thanks @hartmark +- **fix(logging):** raise the SSE payload collector's default cap ([#12461](https://github.com/diegosouzapw/OmniRoute/pull/12461)) — thanks @hartmark +- **fix(resilience):** surface Responses failed.error.message in 502s ([#12472](https://github.com/diegosouzapw/OmniRoute/pull/12472)) — thanks @RaviTharuma +- **fix(security):** unbiased maxai X-Random nonce + stricter URL/regex assertions ([#12502](https://github.com/diegosouzapw/OmniRoute/pull/12502)) +- **fix(video):** re-anchor transcript log-redaction so PII/credential maskers can't reopen the leak (#12150 P1 follow-up) ([#12503](https://github.com/diegosouzapw/OmniRoute/pull/12503)) +- **fix(monitoring):** serve cached credentialHealth off the request path ([#12533](https://github.com/diegosouzapw/OmniRoute/pull/12533)) — thanks @RaviTharuma +- **fix(quota):** keep Antigravity Gemini usable when Claude weekly is empty ([#12566](https://github.com/diegosouzapw/OmniRoute/pull/12566)) — thanks @HouMinXi +- **fix(authz):** hard-gate every credential export and CLI-config write (GHSA-5926-2w35-7h4q) ([#12600](https://github.com/diegosouzapw/OmniRoute/pull/12600)) +- **fix(docs):** restore the Next build — REMOVED_PROVIDERS.md had no frontmatter (base-red #12581) ([#12610](https://github.com/diegosouzapw/OmniRoute/pull/12610)) +- **fix(quality):** re-point the zcodeProtocol public-creds allowlist to line 313 ([#12615](https://github.com/diegosouzapw/OmniRoute/pull/12615)) +- **fix(security):** close 3 advisories — search baseUrl exfil, sk- in the error sanitizer, bifrost relay header leak ([#12620](https://github.com/diegosouzapw/OmniRoute/pull/12620)) +- **fix(providers):** sanitize boolean required and nested bare maps for Gemini (#12269) ([#12624](https://github.com/diegosouzapw/OmniRoute/pull/12624)) — thanks @HouMinXi +- **fix(resilience):** keep Overloaded STREAM_EARLY_EOF off the provider breaker ([#12626](https://github.com/diegosouzapw/OmniRoute/pull/12626)) — thanks @HouMinXi +- **fix(catalog):** re-audit free-tier quotas against official pages ([#12649](https://github.com/diegosouzapw/OmniRoute/pull/12649)) +- **fix(dashboard):** keep a request's pending-tracking id stable across combo target retries ([#12650](https://github.com/diegosouzapw/OmniRoute/pull/12650)) — thanks @hartmark +- **fix(tests):** retire dead model ids from the chat-pipeline integration suite (base-red #12581) ([#12670](https://github.com/diegosouzapw/OmniRoute/pull/12670)) +- **fix:** resolve SqliteError no such table compression_run_telemetry during cleanup ([#12682](https://github.com/diegosouzapw/OmniRoute/pull/12682)) — thanks @groovecityJO +- **fix(combo):** treat a pin-only step as implicit connection allowlist ([#12697](https://github.com/diegosouzapw/OmniRoute/pull/12697)) — thanks @HouMinXi +- **fix(ci):** ship .npmrc in published package so legacy-peer-deps applies to consumers (#11544) ([#12699](https://github.com/diegosouzapw/OmniRoute/pull/12699)) — thanks @KooshaPari +- **fix(docker):** pin 4 CLI tools to exact versions (#12576) ([#12703](https://github.com/diegosouzapw/OmniRoute/pull/12703)) — thanks @KooshaPari +- **fix(docker):** document and harden cli profile trust boundary (#12570) ([#12706](https://github.com/diegosouzapw/OmniRoute/pull/12706)) — thanks @KooshaPari +- **fix(sse):** redact video transcript in pre-guardrail rejected-request logs (#12150 P2 item 7) ([#12710](https://github.com/diegosouzapw/OmniRoute/pull/12710)) +- **fix(i18n):** quote placeholder in OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES description (#12505) ([#12769](https://github.com/diegosouzapw/OmniRoute/pull/12769)) — thanks @KooshaPari +- **fix(api/pricing):** surface validation error message as string, not raw object (#12494) ([#12771](https://github.com/diegosouzapw/OmniRoute/pull/12771)) — thanks @KooshaPari +- **fix(sse):** re-enable prompt compression for native Codex passthrough ([#12834](https://github.com/diegosouzapw/OmniRoute/pull/12834)) — thanks @thomasmaerz +- **fix(dashboard):** model health tests for a provider node set to the Responses API now call `/v1/responses` with a Responses-shaped body instead of `/v1/chat/completions` — those models were reported as `Provider returned HTTP 200 but no text content` even though the same model answered normally through `/v1/responses` ([#13070](https://github.com/diegosouzapw/OmniRoute/issues/13070)) ([#13087](https://github.com/diegosouzapw/OmniRoute/pull/13087)) — thanks @ntdatt812 +- fix(routing): stop a round-robin combo's "opencode" targets from collapsing onto the opencode-zen connection (#11912) ([#13283](https://github.com/diegosouzapw/OmniRoute/pull/13283)) +- fix(routing): stop the reactive-compaction debug log from lying when compression is globally disabled (#11977) ([#13276](https://github.com/diegosouzapw/OmniRoute/pull/13276)) +- fix(electron): relativize standalone-bundle symlink targets so Stage 8 manifest verification stops failing on Windows (#11979) ([#13251](https://github.com/diegosouzapw/OmniRoute/pull/13251)) +- fix(dashboard): surface a visible error when Compression Studio's combined preview run fails (#12061) ([#13288](https://github.com/diegosouzapw/OmniRoute/pull/13288)) +- fix(dashboard): make the compression "Effective pipeline" preview honor the active profile and warn when the master switch is off (#12063) ([#13287](https://github.com/diegosouzapw/OmniRoute/pull/13287)) +- fix(providers): scope TinyCMS Web signer's DOM shims to each call instead of leaking them for the process lifetime, and surface a clean HTTP status on a non-JSON interception-toggles error (#12072) ([#13254](https://github.com/diegosouzapw/OmniRoute/pull/13254)) +- fix(guardrails): stop Vision Bridge from re-selecting a model locked after a 404 (#12111) ([#13259](https://github.com/diegosouzapw/OmniRoute/pull/13259)) +- fix(sse): require Responses-shaped body before native OpenAI-compatible passthrough (#12129) ([#13278](https://github.com/diegosouzapw/OmniRoute/pull/13278)) +- fix(providers): minimax-m3 now collapses manual thinking.type:"enabled" to adaptive, preventing upstream 400 (2013) (#12132) ([#13249](https://github.com/diegosouzapw/OmniRoute/pull/13249)) +- fix(db): scope model visibility overrides by modality so hiding a Chat model no longer hides an identically-ID'd Image/Embeddings/etc. model (#12172) ([#13258](https://github.com/diegosouzapw/OmniRoute/pull/13258)) +- fix(db): scope local-provider apiKey dedup to matching base URL so LM Studio/Ollama-style connections support multiple accounts (#12173) ([#13250](https://github.com/diegosouzapw/OmniRoute/pull/13250)) +- fix(providers): refresh Trae's stale Referer/Origin and forward user timezone so imported connections stop failing with 401 (#12190) ([#13255](https://github.com/diegosouzapw/OmniRoute/pull/13255)) +- fix(providers): route opencode-go/gpt-5.6-luna to /responses instead of /chat/completions (#12196) ([#13275](https://github.com/diegosouzapw/OmniRoute/pull/13275)) +- fix(dashboard): allow deleting the last extra-upstream-header row even when invalid (#12251) ([#13252](https://github.com/diegosouzapw/OmniRoute/pull/13252)) +- **fix(i18n):** translate pre-existing `__MISSING__:` keys for `combo.sort`, `requestLogger.detail` expand/collapse, `common.profile`, and `settings.resilienceCredentialHealth*` across 39 locales ([#12272](https://github.com/diegosouzapw/OmniRoute/issues/12272)) ([#13034](https://github.com/diegosouzapw/OmniRoute/pull/13034)) — thanks @HouMinXi +- fix(cli): run the Node.js runtime compatibility guard before the heavy `tsx/esm` + Commander import chain so an unsupported runtime gets a clear message instead of a raw `Invalid regular expression flags` crash (#12296) ([#13253](https://github.com/diegosouzapw/OmniRoute/pull/13253)) +- fix(dashboard): refresh the providers list after deleting a compatible provider node (#12298) ([#13256](https://github.com/diegosouzapw/OmniRoute/pull/13256)) +- fix(usage): fail closed on API-key budget enforcement when a provider's `auto` routing alias has no pricing row, instead of silently counting it as $0 (#12341) ([#13257](https://github.com/diegosouzapw/OmniRoute/pull/13257)) +- **fix(providers):** include the submitted Agnes video model when polling by `video_id` ([#12356](https://github.com/diegosouzapw/OmniRoute/pull/12356)) — thanks @pacocartones +- **fix(routing):** custom OpenAI-compatible nodes now honor the saved Chat/Responses API type after edits instead of letting the node's original ID prefix override the live connection setting ([#11884](https://github.com/diegosouzapw/OmniRoute/issues/11884)). ([#12358](https://github.com/diegosouzapw/OmniRoute/pull/12358)) — thanks @pacocartones +- fix(sse): surface an error instead of a silent empty 200 when a Claude stream closes with zero bytes (#12398) ([#13285](https://github.com/diegosouzapw/OmniRoute/pull/13285)) +- fix(oauth): warn before the dead localhost:8080 redirect in antigravity/gemini `oauth start` (#12413) ([#13265](https://github.com/diegosouzapw/OmniRoute/pull/13265)) +- fix(providers): stop devin-cli spawn error from double-closing the SSE controller (#12517) ([#13261](https://github.com/diegosouzapw/OmniRoute/pull/13261)) +- **fix(rerank):** clamp Voyage `top_k` to the documents actually sent after empty-string filtering, and honor `return_documents: false` in the NVIDIA response adapter (#12523 — thanks @pacocartones) +- **fix(audio):** `/v1/audio/translations` now resolves combo names the way `/v1/audio/transcriptions` already does, so a combo that `GET /v1/models` advertises is fanned out to its targets instead of being rejected with `400 Invalid translation model: . Use format: provider/model`; literal `provider/model` ids and unknown bare names behave as before (#12536 — thanks @pacocartones) +- **fix(gemini):** strip the JSON-Schema-2020-12 `prefixItems` keyword from Gemini tool schemas at every nesting level, so Claude Code tool definitions no longer fail with `400 Unknown name "prefixItems"` on Gemini models (#12540 — thanks @pacocartones) +- **fix(api-manager):** Expose an accessible loading status while API keys are fetched instead of an empty accessibility tree (#12541 — thanks @pacocartones) +- **fix(video):** Clamp `estimateJpegFrameBytes` at zero for padding-only payloads and build the three encode-side frame data URIs from `JPEG_FRAME_DATA_URI_PREFIX` instead of a repeated literal (#12543 — thanks @pacocartones) +- **fix(devin):** accept Windows `DEVIN_AGENTIC_HOME` sandbox paths (`C:\...\.sandbox\...`) in the isolated-home check so the Devin Claude Bridge no longer fails closed on Windows ([#12405](https://github.com/diegosouzapw/OmniRoute/issues/12405)) (#12545 — thanks @pacocartones) +- **fix(db):** Add `conversation_turn_nodes` and orphaned `agentic_conversations` to the auto-cleanup cycle under the existing `retention.callLogs` window, so identity nodes whose call-log content has already been purged no longer accumulate without bound in `storage.sqlite` (#12548 — thanks @pacocartones) +- **fix(i18n):** Wrap the `~/.claude/profiles//settings.json` placeholder in ICU single quotes in the `OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES` feature-flag description across all 42 locales and the TypeScript default, so next-intl no longer fails with `INVALID_MESSAGE: UNCLOSED_TAG` and the Feature Flags card shows the description instead of the raw key (#12549 — thanks @pacocartones) +- **fix(orchestration):** `updateCloudAgentTask` now publishes the task's real `status` on `agent.task.updated` when an update only touches `result`, `activities` or `error`, instead of the fabricated `"updated"` state, and stays silent when no row matched the id (#12550 — thanks @pacocartones) +- **fix(i18n):** the home "Recent Requests" panel and the Provider Topology legend are now translated instead of rendering English copies on non-English dashboards; the legend reads its own `home.topologyLegend*` labels with consistent casing rather than borrowing the memory-settings "Recent" and analytics "Error" strings (#12551 — thanks @pacocartones). +- **docs(reference):** bring the `FEATURE_FLAGS.md` catalog back to 1:1 with `featureFlagDefinitions.ts` — 20 missing flags added, the two `*_BLOCK_THRESHOLD` env-only knobs moved out of the flag tables, category/total counts and the Live WS port corrected, guarded by a static test (#12552 — thanks @pacocartones) +- **fix(i18n):** backfill missing `usage.kiloPass*` strings in 39 locales and restore `featureFlagOmnirouteDisableThinkingLevelVariantsDescription` in `pt.json` ([#12561](https://github.com/diegosouzapw/OmniRoute/issues/12561)) ([#13027](https://github.com/diegosouzapw/OmniRoute/pull/13027)) — thanks @HouMinXi +- fix(docker): default docker-compose app ports (dashboard/API/live-WS) to loopback instead of `0.0.0.0`, closing the anonymous `/v1` LAN/WAN exposure gap left open by `REQUIRE_API_KEY=false` (#12568) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): close DNS-rebinding SSRF gap in webhook outbound-URL guard (#12569) ([#13243](https://github.com/diegosouzapw/OmniRoute/pull/13243)) +- fix(docker): require a per-session token on the VNC browser CDP bridge and isolate it on a dedicated Docker network (#12571) ([#13241](https://github.com/diegosouzapw/OmniRoute/pull/13241)) +- fix(open-sse): write Adobe Firefly session tokens and cookie jars with 0700/0600 permissions instead of the process umask (#12572) ([#13242](https://github.com/diegosouzapw/OmniRoute/pull/13242)) +- fix(api): remove hardcoded wildcard CORS in openai-to-gemini-sse.ts so the centralized fail-closed CORS gate is the sole source of `Access-Control-Allow-Origin` (#12573) ([#13239](https://github.com/diegosouzapw/OmniRoute/pull/13239)) +- fix(api): enforce API key policy (budget/rate-limit/schedule/endpoint scoping) on the ElevenLabs speech-to-text, text-to-speech and voices proxy routes (#12574) ([#13244](https://github.com/diegosouzapw/OmniRoute/pull/13244)) +- fix(sse): cap HuggingChat NDJSON body size and bound the read loop with the fetch timeout so a stalled or hostile upstream cannot buffer unbounded memory (#12577) ([#13282](https://github.com/diegosouzapw/OmniRoute/pull/13282)) +- fix(docker): scope the cliproxyapi/qdrant/bifrost sidecars to loopback by default and forward `CLIPROXYAPI_MANAGEMENT_KEY` into the cliproxyapi container so its management API is not left both unauthenticated and LAN-published (#12578) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): create DB export temp paths with `fs.mkdtempSync` instead of predictable timestamps (#12579) ([#13237](https://github.com/diegosouzapw/OmniRoute/pull/13237)) +- Cline 401 bodies that say "re-authenticate your Cline account" classify as a refreshable OAuth token, not a terminal expired key. The cooling panel no longer labels every cooldown as a 429; it shows the recorded last error instead. (#12594) ([#13060](https://github.com/diegosouzapw/OmniRoute/pull/13060)) — thanks @HouMinXi +- **fix(catalog):** degrade unknown combo targets instead of dropping LCD modalities, and persist OpenRouter `architecture.input_modalities` into the capability snapshot ([#12613](https://github.com/diegosouzapw/OmniRoute/issues/12613)) ([#13042](https://github.com/diegosouzapw/OmniRoute/pull/13042)) — thanks @HouMinXi +- fix(providers): send `x-api-key` instead of `Authorization: Bearer` for OpenCode Zen's `/v1/responses` endpoint (Muse Spark Contributor models), fixing a 401 on OmniRoute's auth header (#12633) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(sse): surface the actionable "Auggie CLI not found" message when the shell reports a missing `auggie` binary via exit code instead of a spawn error (#12645) ([#13263](https://github.com/diegosouzapw/OmniRoute/pull/13263)) +- **fix(config):** Persist deletions of built-in background-degradation entries — when a stored settings record exists its `degradationMap` is now authoritative instead of being merged under the defaults, so an entry the user removed in the dashboard no longer reappears on the next apply or restart ([#12424](https://github.com/diegosouzapw/OmniRoute/issues/12424)) ([#12647](https://github.com/diegosouzapw/OmniRoute/pull/12647)) — thanks @pacocartones +- **fix(gamification):** action-count badge milestones (First Token, Token Consumer, Token Machine, Token Whale, and the token-sharing tier) are now backed by a durable `xp_action_counts` counter incremented in `addXp()`, instead of a live `COUNT(*)` over `xp_audit_log`. The audit log is pruned by `retention.xpAuditLog` (default 30 days), so on a default install those "lifetime" milestones were really "actions in the last 30 days" and unlocked badges could stop unlocking once old rows aged out. `getActionCount()` and `checkActionCountBadges()` now read the same durable source, and a migration backfills existing totals from the surviving audit rows ([#12546](https://github.com/diegosouzapw/OmniRoute/issues/12546)) ([#12651](https://github.com/diegosouzapw/OmniRoute/pull/12651)) — thanks @pacocartones +- **fix(images):** `/v1/images/edits` now iterates a combo's targets the same way `/v1/images/generations` does (#9239) instead of flattening a bare combo to its first target. A combo whose first target is not edit-capable — or lacks credentials — now falls through to a later edit-capable target rather than hard-erroring, and missing credentials are skipped (not a hard `401`) to match the generations path. The per-target skip/terminal classification is extracted into a shared `runImageComboTargets` loop, so generations behavior is unchanged ([#12547](https://github.com/diegosouzapw/OmniRoute/issues/12547)). ([#12653](https://github.com/diegosouzapw/OmniRoute/pull/12653)) — thanks @pacocartones +- fix(sse): add first-byte watchdog to the TLS-fingerprint transport so a stalled wreq body falls back instead of hanging for minutes (#12656) ([#13272](https://github.com/diegosouzapw/OmniRoute/pull/13272)) +- fix(sse): exempt tiny-budget reasoning probes from combo quality failure and surface persisted-cooldown skips in ALL_TARGETS_SKIPPED diagnostics (#12659) ([#13269](https://github.com/diegosouzapw/OmniRoute/pull/13269)) +- fix(models): declare the real ~1M contextLength for OpenCode Zen's Muse Spark 1.2 models instead of falling back to the 200000 provider default (#12681) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(oauth): align codebuddy-cn OAuth User-Agent with the chat/usage CLI version to avoid WAF false positives (#12702) ([#13264](https://github.com/diegosouzapw/OmniRoute/pull/13264)) +- fix(dashboard): surface an authentication-required banner instead of silently blanking database settings for a guest session (#12709) ([#13270](https://github.com/diegosouzapw/OmniRoute/pull/13270)) +- **fix(resilience):** stop unbounded queue that hangs 6min until Aborted — gate, provider slot, and Bottleneck queue now share a per-connection `maxWaitMs` budget; `fail-closed` on exhaust (503); execution backstop `executionMaxWaitMs` overridable per connection with upstream clamp ([#12715](https://github.com/diegosouzapw/OmniRoute/pull/12715)) — thanks @maxmad64bis (with thanks to @Tushar49 for surfacing the slow-provider need in #12635) +- **fix(auto-combo):** every mode pack now carries `quality` and `reliability` weights (`quality` 0.02, 0.03 in `quality-first`; `reliability` 0.03, 0.04 in `reliability-first`), so selecting a pack no longer silences either signal; `DEFAULT_WEIGHTS` is unchanged ([#12731](https://github.com/diegosouzapw/OmniRoute/pull/12731)) — thanks @maxmad64bis +- **fix(sse):** the provider execution pipeline keeps the upstream error `code`/`type` again — both were lost when the non-streaming leg was extracted, so a config-class `422` (Antigravity missing project) degraded into a generic account cooldown instead of reaching the gate that recognises it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(db):** a legacy `call_logs` table no longer aborts startup — the provider-stats index is created after the column healing runs, not before it, so an install predating `request_type` opens instead of failing with `no such column` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(catalog):** a malformed row in the operator-writable custom-models blob no longer kills every `auto/*` pool ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(tests):** realign the guards that the same day's merges left asserting the old behavior — the provider-count total after GreenPT and EURouter, the GLM stream buffer slot that `#12925` turned into a declared parameter, and the injection scan bound that `#13104` reshaped from a head-only window into head-plus-tail ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- fix(cache): fold tool_choice/tools/response_format into the semantic cache signature so a cached tool_calls response can no longer be replayed for a request whose tool policy forbids it (#12734) ([#13267](https://github.com/diegosouzapw/OmniRoute/pull/13267)) +- **fix(models):** stop treating provider-supplied `isFree:true`/`:free`/`0/0` flags as trusted unless the provider has a documented free tier — only the shipped free-tier catalog decides otherwise; fetched `isFree:true` trusted only for a free-tier provider, custom `isFree:true` via a trusted path ([#12744](https://github.com/diegosouzapw/OmniRoute/pull/12744)) — thanks @maxmad64bis +- fix(memory): authenticate the internal /v1/rerank loopback call so memory reranking no longer silently degrades to unranked order when REQUIRE_API_KEY=true (#12745) ([#13268](https://github.com/diegosouzapw/OmniRoute/pull/13268)) +- fix(sse): parse Ollama Cloud's current usage markup (`$X of $Y used` aria-label, nested width style) (#12749) ([#13260](https://github.com/diegosouzapw/OmniRoute/pull/13260)) +- fix(cli): setup-opencode no longer sends an active context's management token to `/v1/models` when `--api-key`/`OMNIROUTE_API_KEY` is supplied — an explicit flag or the env var now always outranks the context's token, and the flag itself is no longer swallowed by the parent program's global `--api-key` option (#12783) ([#13246](https://github.com/diegosouzapw/OmniRoute/pull/13246)) +- fix(sse): register Arcee AI in the executor provider registry so requests reach api.arcee.ai instead of silently falling back to OpenAI (#12784) ([#13277](https://github.com/diegosouzapw/OmniRoute/pull/13277)) +- **fix(quota):** share one upstream quota read when identical saturation checks arrive at the same time, so traffic bursts don't multiply provider API calls ([#12787](https://github.com/diegosouzapw/OmniRoute/pull/12787)) — thanks @maxmad64bis +- **fix(db):** ignore expired or invalid rate-limit cooldown writes so a stale timestamp can't lock a connection that should be usable — clearing still works as before ([#12788](https://github.com/diegosouzapw/OmniRoute/pull/12788)) — thanks @maxmad64bis +- **fix(providers):** single source for provider order with `xao` ranking alongside `xai-oauth` ([#12790](https://github.com/diegosouzapw/OmniRoute/pull/12790)) — thanks @maxmad64bis +- **fix(routing):** off-table models check the free-model catalog before inheriting premium prices, latency bootstraps from the observed pool median, async tiers read live database pricing with a 90-day freshness gate, and the tier cache invalidates on every pricing write ([#12792](https://github.com/diegosouzapw/OmniRoute/pull/12792)) — thanks @maxmad64bis +- **fix(auto-combo):** snapshot scoring uses the observed per-connection account tier and per-model quality instead of neutral constants, and circuit-open providers rank lower at build time ([#12794](https://github.com/diegosouzapw/OmniRoute/pull/12794)) — thanks @maxmad64bis +- **fix(pool):** empty `auto/*` pools now say why they are empty, and the models listing uses the same paid check as routing ([#12795](https://github.com/diegosouzapw/OmniRoute/pull/12795)) — thanks @maxmad64bis +- fix(routing): recognize CLIProxyAPI's 'unknown provider for model' 400 as fallback-worthy (#12800) ([#13284](https://github.com/diegosouzapw/OmniRoute/pull/13284)) +- **fix(sse):** translate-mode streams now emit the estimated token counts as a trailing usage-only chunk before `[DONE]` when the upstream stays silent, so metered chat clients see totals instead of nothing ([#12828](https://github.com/diegosouzapw/OmniRoute/pull/12828)) — thanks @maxmad64bis +- **fix(db):** provider stats stay truthful on empty and legacy databases: fallback counts default to `0` instead of `null`, latency averages read `null` (not `0`) when no durations were recorded, failures logged before the error-type column existed group under `pre_migration` instead of `unclassified`, and per-provider queries use two new composite indexes ([#12832](https://github.com/diegosouzapw/OmniRoute/pull/12832)) — thanks @maxmad64bis +- fix(nvidia): fail open when a synced model catalog goes stale instead of gating forever (#12849) ([#13248](https://github.com/diegosouzapw/OmniRoute/pull/13248)) +- **fix(db):** WAL maintenance lives in its own module: a `TRUNCATE` checkpoint that hits a busy database now warns with its streak and retries once via `PASSIVE` instead of logging success, and closing-time checkpoints no longer report success on builds without a database file, with the busy totals visible in the authenticated monitoring health payload ([#12853](https://github.com/diegosouzapw/OmniRoute/pull/12853)) — thanks @maxmad64bis +- **fix(health):** quota with no snapshots reads empty instead of a contradicting 0% ([#12857](https://github.com/diegosouzapw/OmniRoute/pull/12857)) — thanks @maxmad64bis +- fix(a2a): accept the dashboard's own session cookie on /a2a so "Run message/send" no longer fails with "Unauthorized: missing or invalid API key" (#12888) ([#13271](https://github.com/diegosouzapw/OmniRoute/pull/13271)) +- **fix(logs):** the Logs grid's in-memory filter pass no longer discards rows the SQL query already matched — selecting an API key from the dropdown (which sends the key's id) returns its calls again, the Combo tab shows every combo instead of only those whose name contains a "1", and the model filter and search cover the same columns as the query ([#12896](https://github.com/diegosouzapw/OmniRoute/pull/12896)) — fixes [#12873](https://github.com/diegosouzapw/OmniRoute/issues/12873) — thanks @ntdatt812 +- **fix(a2a):** `/api/a2a/status` now builds the agent card from the request that asked for it, so a gateway reached at a non-localhost host no longer advertises `http://localhost:20128` as its A2A URL ([#12918](https://github.com/diegosouzapw/OmniRoute/pull/12918)). — thanks @ntdatt812 +- **fix(compression):** progressive aging now appends its `[COMPRESSED:aging:…]` annotation after a turn's `tool_result` blocks instead of in front of them, so Anthropic no longer rejects aged conversations with "`tool_use` ids were found without `tool_result` blocks immediately after" ([#12920](https://github.com/diegosouzapw/OmniRoute/pull/12920)). — thanks @ntdatt812 +- **fix(bedrock):** model import now resolves context limits for every vendor prefix instead of only `anthropic.*`, so `global.openai.gpt-5.6-*` no longer imports with a null `inputTokenLimit` and gets rejected pre-flight at the 200k default ([#12921](https://github.com/diegosouzapw/OmniRoute/pull/12921)). — thanks @ntdatt812 +- **fix(stream):** the 64 KB stream buffer GLM asks for is honoured instead of dropped, and the type error it caused no longer fails the API Route Typecheck gate on every open PR ([#12925](https://github.com/diegosouzapw/OmniRoute/pull/12925)) — thanks @ntdatt812 +- **fix(guardrails):** mask PII inside a `tool_result`'s nested content array, which the masker walked past while redacting its sibling block ([#12930](https://github.com/diegosouzapw/OmniRoute/pull/12930)) — thanks @ntdatt812 +- **fix(dashboard):** the Radar catalog table no longer leaves absent data unexplained — empty limits, unknown context windows, and unreported capabilities each explain themselves on hover, and a new check keeps it that way ([#12937](https://github.com/diegosouzapw/OmniRoute/pull/12937)) — thanks @maxmad64bis +- **fix(sse):** geo-blocked opencode requests rotate to the next account proxy instead of failing, so one refused egress no longer aborts the whole chain ([#12941](https://github.com/diegosouzapw/OmniRoute/pull/12941)) — thanks @maxmad64bis +- **fix(codex):** dashboard "clear cooldown" and the CAS recovery path now drop nested `codexScopeRateLimitedUntil` maps in the same write that nulls `rate_limited_until`, and fresh quota snapshots with headroom lift fallback-sourced scope cooldowns parked by quota preflight ([#12817](https://github.com/diegosouzapw/OmniRoute/issues/12817), [#12860](https://github.com/diegosouzapw/OmniRoute/issues/12860), [#12951](https://github.com/diegosouzapw/OmniRoute/pull/12951)) The dashboard reset-credit button (`consumeCodexResetCredit`) now exercises that same snapshot path after a successful redeem, so a filled quota bar is enough to unpark a leftover fallback Codex child without another manual clear. — thanks @HouMinXi +- **fix(db):** resolve `sql-wasm.wasm` across global npm install and hoisted layouts, ensuring OmniRoute can boot cleanly on Node 24 when native `better-sqlite3` is uncompiled. ([#13035](https://github.com/diegosouzapw/OmniRoute/pull/13035)) — thanks @HouMinXi +- **fix(api):** `DELETE /v1/batches/delete-completed` now sweeps only the calling API key's own completed batches (batches with no owner stay out of a key-scoped sweep on purpose), with an explicit instance-wide mode reserved for authenticated dashboard sessions, a 401 for a presented key that is unknown, revoked, deactivated, banned or expired (never falling through to the session branch), audit logging of every sweep, a sanitized 500 on failure, an owner-scoped file half (a key-scoped sweep never nulls a file another tenant owns) and an atomic sweep — chunked in 200-batch transactions in instance mode — so a mid-way error never leaves a batch pointing at a nulled file (GHSA-wvxc-jp3v-5mg5) ([#12969](https://github.com/diegosouzapw/OmniRoute/pull/12969)) +- **fix(combo):** quota-weighted routing stops drawing on an out-of-credit connection — a 402 now invalidates the stored quota snapshot instead of leaving its stale remaining percentage in place, and a snapshot older than 10 minutes no longer counts as confident headroom for the primary pool ([#12972](https://github.com/diegosouzapw/OmniRoute/pull/12972)) ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- **fix(vision):** Custom Models with "Vision capable" checked no longer have image requests swapped to `glm/glm-4.6v` when the client sends the advertised alias (`vllm/path/...`) or the bare path-shaped id — Vision Bridge now matches the stored override for all three id forms ([#12758](https://github.com/diegosouzapw/OmniRoute/issues/12758)) ([#12974](https://github.com/diegosouzapw/OmniRoute/pull/12974)) — thanks @HouMinXi +- **fix(sse):** transient opencode upstream failures rotate to the next account proxy instead of failing, so one flapping egress no longer aborts the whole chain ([#12975](https://github.com/diegosouzapw/OmniRoute/pull/12975)) — thanks @maxmad64bis +- **fix(azure):** Deployments from GPT-6 onward now send `max_completion_tokens` instead of `max_tokens`, which Azure rejects with HTTP 400. The rule matched a literal `gpt-5`, so each new generation arrived broken; it now matches the generation range, while `gpt-35-turbo` still keeps `max_tokens`. ([#13007](https://github.com/diegosouzapw/OmniRoute/pull/13007)) — thanks @ntdatt812 +- **fix(db):** add an opt-in self-restart circuit for sustained critical memory pressure, gate post-cleanup VACUUM behind a minimum freed-rows threshold, and checkpoint the SQLite WAL every 5 minutes with a size guard that escalates to TRUNCATE, so a growing WAL can no longer stall the event loop into a full outage. ([#13011](https://github.com/diegosouzapw/OmniRoute/pull/13011)) — thanks @HouMinXi +- **fix(auth):** an explicit connection pin may probe a quota-disabled row once and re-enable it on success ([#12874](https://github.com/diegosouzapw/OmniRoute/issues/12874)) ([#13017](https://github.com/diegosouzapw/OmniRoute/pull/13017)) — thanks @HouMinXi +- **fix(api):** thread `X-OmniRoute-Fallback-Attempts` through combo chat completions so streaming and non-streaming responses report how many prior legs were attempted ([#13038](https://github.com/diegosouzapw/OmniRoute/pull/13038)) — thanks @HouMinXi +- Restore provider failure classification and credential refresh on non-streaming requests: classify non-2xx failures to lock models on per-model quota exhaustion, update connection rate limits from headers and body, and pass credential refresh handlers to pipeline execution so 401 tokens can be refreshed and retried (#13043). ([#13069](https://github.com/diegosouzapw/OmniRoute/pull/13069)) — thanks @HouMinXi +- **fix(responses):** wrap forced-non-streaming web_search fallback JSON as Responses SSE so Codex still sees `response.completed` ([#13050](https://github.com/diegosouzapw/OmniRoute/pull/13050)) — thanks @HouMinXi +- **fix(antigravity):** Preserve upstream thought-token usage in normalized completion and reasoning token counts ([#13055](https://github.com/diegosouzapw/OmniRoute/pull/13055)) — thanks @pacocartones +- Fix a concurrency-slot leak in the DeepSeek PoW solver: a worker that failed to spawn (for example a missing worker script) never released its slot, so `MAX_CONCURRENT_WORKERS` failures disabled the solver until restart. ([#13097](https://github.com/diegosouzapw/OmniRoute/pull/13097)) — thanks @anhtahaylove +- **fix(acp):** bound the ACP session output buffers — `stdoutBuffer` and `stderrBuffer` now cap at 1 MiB keeping the most recent output behind a visible `[...output truncated...]` marker, and `stderrBuffer` is reset per prompt instead of accumulating for the lifetime of the session. ([#13100](https://github.com/diegosouzapw/OmniRoute/pull/13100)) — thanks @anhtahaylove +- **fix(acp):** release the `stdout`/`exit` listeners and the idle timer that a `sendPrompt` timeout used to leave attached to the `acpManager` singleton, and drop sessions that exited on their own from the session map instead of keeping them forever. ([#13096](https://github.com/diegosouzapw/OmniRoute/pull/13096)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection and PII scanners now read the text a `tool_result` block carries on `content` (string or nested block list), in messages and in system blocks, so tool output is judged by the same rules as user text ([#13101](https://github.com/diegosouzapw/OmniRoute/pull/13101)) — thanks @ntdatt812 +- **fix(gamification):** close the badge notification SSE stream when the request signal is already aborted before the stream starts — a client that disconnects while the route is still awaiting auth used to leave both the 2s unlock poll and the 15s heartbeat running for the lifetime of the process. ([#13106](https://github.com/diegosouzapw/OmniRoute/pull/13106)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection scan now spends its 16 KB budget on both ends of the request instead of the first 16 KB only, so `system`, `instructions`, `query`, `documents` and the newest turns are no longer hidden behind one long message ([#13104](https://github.com/diegosouzapw/OmniRoute/pull/13104)) — thanks @ntdatt812 +- **fix(dashboard):** expose the Volcano Ark console cookie on quota scraping and unwrap connect-error objects so the dashboard shows the upstream message ([#13107](https://github.com/diegosouzapw/OmniRoute/pull/13107)) — thanks @HouMinXi +- **fix(db):** release the `beforeExit`/`SIGINT`/`SIGTERM` handlers when a `node:sqlite` adapter closes, so a closed adapter and its database handle are no longer pinned to `process` for the lifetime of the run — the same treatment #7494 gave the sql.js adapter. ([#13109](https://github.com/diegosouzapw/OmniRoute/pull/13109)) — thanks @anhtahaylove +- **fix(translator):** `contentSchema` and `unevaluatedItems` are now treated as subschema positions by the tool-schema sanitizer, so a truncation placeholder in either is replaced with a permissive schema instead of being forwarded as a string ([#13110](https://github.com/diegosouzapw/OmniRoute/pull/13110)) — thanks @ntdatt812 +- **fix(cli-helper):** clear the `createLogStream` timeout on the abort path — `stop()` aborts the in-flight fetch and returned through the `signal.aborted` branch, which skipped `clearTimeout` and left an armed timer per stopped stream. The stream reader is now also cancelled when the read loop exits early. ([#13114](https://github.com/diegosouzapw/OmniRoute/pull/13114)) — thanks @anhtahaylove +- **fix(docs):** drop TLS-impersonation, MITM-decrypt, supply-chain attestation, and XOR-mask writeups from the public `/docs` catalog and Docker image. Files stay in git for engineers; operators who need them open the repo, not the website. ([#13136](https://github.com/diegosouzapw/OmniRoute/pull/13136)) — thanks @HouMinXi +- **fix(combo):** parse numeric-epoch `rate_limited_until` in the combo cooldown read path ([#13141](https://github.com/diegosouzapw/OmniRoute/pull/13141)) — thanks @maxmad64bis +- **fix(opencode):** both OpenCode plugins now reject a gateway address typed without `http://` at configuration time, instead of publishing every model with an api url no client can call, and the v2 plugin no longer publishes a model card whose api url is blank or relative ([#13142](https://github.com/diegosouzapw/OmniRoute/pull/13142)) — thanks @maxmad64bis +- **fix(providers):** lock opencode model on upstream 400 model-unavailable ([#13146](https://github.com/diegosouzapw/OmniRoute/pull/13146)) — thanks @maxmad64bis +- **fix(logging):** keep the provider exchange rather than the raw client bodies when a call log exceeds its size budget, and show that recovered payload in the request-detail panel instead of replacing it with the stored response body ([#13147](https://github.com/diegosouzapw/OmniRoute/pull/13147)) — thanks @maxmad64bis +- **fix(traffic-inspector):** the WebSocket route no longer leaks a traffic-buffer subscriber and a 30s ping timer when the client socket is already closed at handler time — listeners are attached before any resource is acquired, a destroyed socket bails out early, and the ping interval stops on a dead socket where `write()` never throws ([#13155](https://github.com/diegosouzapw/OmniRoute/pull/13155)) — thanks @anhtahaylove +- **fix(telegram):** bound the per-user API key cache in the Telegram chat proxy so a burst of distinct chat ids can no longer grow the process heap without limit ([#13165](https://github.com/diegosouzapw/OmniRoute/issues/13165)) ([#13166](https://github.com/diegosouzapw/OmniRoute/pull/13166)) — thanks @anhtahaylove +- **fix(stream):** cancel the upstream response body when the JSON-to-SSE sniff unwinds on a body timeout, so a stalled upstream no longer pins the connection ([#13169](https://github.com/diegosouzapw/OmniRoute/issues/13169)) ([#13171](https://github.com/diegosouzapw/OmniRoute/pull/13171)) — thanks @anhtahaylove +- **fix(telegram):** authenticate webhook deliveries with Telegram's `secret_token` so an unauthenticated caller can no longer mint API keys or spend upstream quota ([#13172](https://github.com/diegosouzapw/OmniRoute/issues/13172)) ([#13175](https://github.com/diegosouzapw/OmniRoute/pull/13175)) — thanks @anhtahaylove +- **fix(models):** give discoverable Gemini 3.8 Flash ids their own 65536 output spec so Antigravity no longer clamps them to 16384 ([#13195](https://github.com/diegosouzapw/OmniRoute/pull/13195)) — thanks @HouMinXi +- **fix(dashboard):** leftover catalog-removed provider rows (gemini-cli) can be listed and purged from the providers page ([#13067](https://github.com/diegosouzapw/OmniRoute/issues/13067)) ([#13197](https://github.com/diegosouzapw/OmniRoute/pull/13197)) — thanks @HouMinXi +- **fix(auth):** a dashboard session now requires the `authenticated: true` claim that login, OIDC and the session refresh already emit — a JWT merely signed with `JWT_SECRET` (for example the Cursor CLI passthrough token, which any API-key holder can obtain) no longer verifies as the `auth_token` cookie on any route, the WebSocket handshake or the live server; existing sessions keep working ([#13298](https://github.com/diegosouzapw/OmniRoute/issues/13298)) ([#13375](https://github.com/diegosouzapw/OmniRoute/pull/13375)) +- **fix(api):** `DELETE /v1/batches/delete-completed` now applies the caller's API-key policy (endpoint allowlist, schedule, usage cap, rate limit) like every other `/v1` route, commits the key-scoped sweep per 200-batch chunk instead of holding one write lock for the whole sweep, refuses to loop without progress, and rejects a scope that names both a key and `allTenants` ([#13684](https://github.com/diegosouzapw/OmniRoute/pull/13684)) +- Restore the API-route typecheck gate: the non-streaming leg result lost its discriminated-union narrowing after the server-owned tool loop reassignment, producing 13 new TS2339 diagnostics in `chatCore.ts`. ([#12963](https://github.com/diegosouzapw/OmniRoute/pull/12963)) +- Restore four non-streaming behaviours the server-owned tool loop refactor dropped: client aborts map to 499 with the fixed `Request aborted` message, an aborted request no longer logs a synthetic `clientResponse`, Claude prompt-cache telemetry is recorded again, and a body that cannot be canonicalized no longer throws when the tool loop is off. ([#12990](https://github.com/diegosouzapw/OmniRoute/pull/12990)) +- **fix(skills):** The CLI registry parser now reads positionals declared with `.addArgument()`, not only those written inline in `.command()`. `tunnel create [type]` was being published as `tunnel create`, so the agent-skills sync gate reported drift on every branch and regenerating would have deleted the argument. ([#13009](https://github.com/diegosouzapw/OmniRoute/pull/13009)) — thanks @ntdatt812 +- **fix(combos):** dashboard combo test uses a short prompt, serial probes, and a 60s timeout so reasoning models and rate-limited free pools do not fail the health check ([#13001](https://github.com/diegosouzapw/OmniRoute/pull/13001)) — thanks @HouMinXi +- fix(compression): terminate idle worker threads on eviction so long-running instances stop leaking OS threads and MessagePorts ([#13091](https://github.com/diegosouzapw/OmniRoute/pull/13091)) — thanks @anhtahaylove +- Redact Google API keys of any length in error bodies: the pattern required exactly 39 characters, so shorter or longer `AIza…` credentials passed through unredacted. ([#12964](https://github.com/diegosouzapw/OmniRoute/pull/12964)) +- **fix(grok-cli):** a 402 "Grok Build usage balance exhausted" parks that Grok login as out of credit (Grok Build CLI, grok.com cookie, and xAI OAuth share the weekly pool). Combo routing then tries the next login instead of locking the model for every account in the pool ([#13061](https://github.com/diegosouzapw/OmniRoute/pull/13061)) — thanks @HouMinXi +- fix(compression): spawn the LLMLingua worker with a file URL object so compression actually runs instead of silently failing open on Node ([#13093](https://github.com/diegosouzapw/OmniRoute/pull/13093)) — thanks @anhtahaylove +- **fix(test):** run the local `test` and `test:unit` scripts at concurrency 4 so a full-suite run no longer exhausts the machine's commit charge and kills unrelated processes ([#13187](https://github.com/diegosouzapw/OmniRoute/pull/13187)) — thanks @anhtahaylove +- **fix(monitoring):** `GET /api/monitoring/health` `credentialHealth` now includes a bounded `failedConnections` list (`connectionId`, `status`, sanitized `lastError`) when the probe-cache gauge `failed>0`, plus `source: probe-cache` and a cheap `staleDbNonOkCount` for sticky SQLite `test_status` on active rows. Documents that the live gauge is not `provider_connections.test_status`. ([#12876](https://github.com/diegosouzapw/OmniRoute/pull/12876)) — thanks @RaviTharuma +- **fix(resilience):** an openai-compatible multi-upstream gateway no longer marks the whole connection `credits_exhausted` when a single upstream (or the `/models` / representative-model health probe) returns 402. Quota failures stay model-scoped; true connection-wide auth failures are unchanged. Single-credential 402 key disable ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239)) is preserved. ([#12875](https://github.com/diegosouzapw/OmniRoute/pull/12875)) — thanks @RaviTharuma +- **fix(dashboard):** nine fixes on the Orchestration Canvas, all diagnosed in the Phase 2 reviews. A source that fails now keeps the timestamp of its FIRST failure instead of being re-stamped every poll — the stale line said "since the last poll" no matter how long the source had been down, and the churn also defeated the snapshot's stable identity (it serializes the source list), so the canvas re-rendered on every tick while anything was broken. A source that HAD data and only then started failing is flagged too: previously only a source with no node at all got the warning, so a source that went down mid-session kept a healthy-looking node forever. The rest are pointwise: clicking a filter chip cancels the pending search debounce (left armed, it fired ~300ms later and silently reverted the chip); the search input carries an accessible name; `?state=running, failed` parses like the unpadded form instead of dropping the padded value; the CSV toggle helper is defined once in `model/urlParams.ts` rather than twice; the agents tab tells "nothing running" apart from "the filter matched nothing", offering a clear-filters button instead of setup links that would be wrong advice there; edges stop emitting SMIL particles above 40 simultaneously active edges, keeping the colored stroke; and the drawer's error banner clears when a retried action succeeds. ([#12983](https://github.com/diegosouzapw/OmniRoute/pull/12983)) +- **fix(i18n):** the nine locales added with the EU-language batch (Greek, Estonian, Irish, Croatian, Lithuanian, Latvian, Maltese, Slovenian, Serbian) were missing the eleven Orchestration Canvas keys that Phase 3 introduced, so the compare-runs panel and the "no runs match these filters" empty state fell back to English in those languages (`deepMergeFallback` substitutes English for an absent key, so nothing rendered blank — it rendered untranslated). The coverage gate does not catch this: it enforces an 80% floor per locale, and eleven missing keys out of ~13,000 leaves coverage at 99.9%. Translated for real in each language, calibrated against the wording each file already uses for "run", "filter" and "skill". ([#13294](https://github.com/diegosouzapw/OmniRoute/pull/13294)) +- **fix(dashboard):** five follow-ups from the Phase 2 Orchestration Canvas review. The History drawer now shows the memory section for runs that already left the live TTL window: the persisted `memory_hits` event is parsed into `metadata.memoryHits` with the same defensive validation the drawer applies, and — because that event is observability rather than a state transition — it no longer leaks into the timeline, where it had been inheriting the task's state and rendering as a duplicate transition. The A2A memory recall runs against its own 1.5s deadline instead of inheriting the memory backend's 30s one; overshooting degrades exactly like any other recall failure (no hits, task proceeds), and the timer is cleared on both paths. Repeating a Conductor run carries its `requirements.cli`/`requirements.model` forward, so the new run is pinned to the same runner profile and model rather than drifting to whatever the fleet picks. A successful repeat from the Agents tab now focuses the run it created, instead of leaving the operator on the finished one. And the auth test for `POST /api/conductor/tasks` moved into the shared `ROUTES` array rather than restating the pattern. ([#12988](https://github.com/diegosouzapw/OmniRoute/pull/12988)) +- fix(plugins): stop leaking an exit listener per plugin hook timeout, which triggered MaxListenersExceededWarning on plugins that ignore SIGTERM ([#13092](https://github.com/diegosouzapw/OmniRoute/pull/13092)) — thanks @anhtahaylove +- **fix(validation):** Provider node edits no longer fail with a generic "Invalid request" when the optional daily-quota reset fields are left blank. The dashboard sends `dailyQuotaResetTimezone` and `dailyQuotaResetHour` as `null`, and only the hour accepted it. ([#13066](https://github.com/diegosouzapw/OmniRoute/issues/13066)) ([#13083](https://github.com/diegosouzapw/OmniRoute/pull/13083)) — thanks @ntdatt812 +- **fix(combo):** quota-aware expansion drops banned, inactive, missing, and wrong-provider connections before quota fetch or model dispatch; pins and allowlists stay selectors, not a bypass. Antigravity automatic exhaustion now requires a reported zero remaining, so a positive balance below 1% stays eligible. ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- Fix an intermittent failure in the quota-weighted routing test suite: scores are a function of `Date.now()`, so peers with identical quota scored microseconds apart never tied and swapped order. ([#12946](https://github.com/diegosouzapw/OmniRoute/pull/12946)) +- fix(volcengine): resolve quota window mapping gaps, safe multi-connection console binding, and response cookie sanitization ([#12950](https://github.com/diegosouzapw/OmniRoute/pull/12950)) — thanks @HouMinXi +- **fix(test):** resolve the WebDAV handler path with `fileURLToPath` so the suite's 37 WebDAV tests run on Windows instead of failing with a doubled `C:\C:\` drive prefix ([#13196](https://github.com/diegosouzapw/OmniRoute/pull/13196)) — thanks @anhtahaylove +- **fix(ui):** fall back from invalid AnySearch icon alias (direct commit `9587e07b69`) +- **fix(sse):** stop rebuilding a truncated summary from the collector's cap-dropped event array ([#12718](https://github.com/diegosouzapw/OmniRoute/pull/12718)) — thanks @hartmark +- **fix(conversations):** show a pending spinner for unresolved tool nodes instead of "(empty)" ([#12727](https://github.com/diegosouzapw/OmniRoute/pull/12727)) — thanks @hartmark +- **fix(streaming):** fail fast when an upstream stream produces only lifecycle/heartbeat events, never real content ([#12741](https://github.com/diegosouzapw/OmniRoute/pull/12741)) — thanks @hartmark +- **fix(responses-continuation):** bridge the write-in-flight window with an in-memory pending store ([#12854](https://github.com/diegosouzapw/OmniRoute/pull/12854)) — thanks @hartmark +- **fix(translator):** drop or map agent_message on Chat Completions fallback ([#12880](https://github.com/diegosouzapw/OmniRoute/pull/12880)) — thanks @RaviTharuma +- **fix(api):** page and stream-complete GET /v1/models for large catalogs ([#12882](https://github.com/diegosouzapw/OmniRoute/pull/12882)) — thanks @RaviTharuma +- **fix(quota):** align AUTH window labels with usage API and clarify 503 ([#12884](https://github.com/diegosouzapw/OmniRoute/pull/12884)) — thanks @RaviTharuma +- **fix(ci):** clear two base-reds on release/v3.8.51 — mutation-coverage gate + image-only-model guard ([#12945](https://github.com/diegosouzapw/OmniRoute/pull/12945)) +- **fix(api):** scope batch bulk-delete to the calling API key ([#13211](https://github.com/diegosouzapw/OmniRoute/pull/13211)) +- **fix(tests):** align model-sync log assertion to shipped path redaction (base-red) ([#13227](https://github.com/diegosouzapw/OmniRoute/pull/13227)) +- **fix(test):** make the ToS heading guard actually require the parentheses ([#13228](https://github.com/diegosouzapw/OmniRoute/pull/13228)) +- **fix(quality):** register 4 drifted covering tests in stryker tap.testFiles ([#13229](https://github.com/diegosouzapw/OmniRoute/pull/13229)) +- **fix(logging):** mask *-api-key headers in request-log pipeline (#13273) ([#13401](https://github.com/diegosouzapw/OmniRoute/pull/13401)) — thanks @KooshaPari +- **fix(tests):** correct stale Jina catalog prefix in custom-models test ([#13403](https://github.com/diegosouzapw/OmniRoute/pull/13403)) — thanks @KooshaPari +- **fix(backend):** prune old backups after health-check-repair snapshots (#13308) ([#13404](https://github.com/diegosouzapw/OmniRoute/pull/13404)) — thanks @KooshaPari +- **fix(backend):** guard memory_fts_au trigger with WHEN clause to stop FTS bloat (#13326) ([#13405](https://github.com/diegosouzapw/OmniRoute/pull/13405)) — thanks @KooshaPari +- **fix(free-tiers):** remove obsolete Together $25 signup credit (#12526) ([#13407](https://github.com/diegosouzapw/OmniRoute/pull/13407)) — thanks @KooshaPari +- **fix:** skip non-chat generation models in Test all models ([#13409](https://github.com/diegosouzapw/OmniRoute/pull/13409)) — thanks @KooshaPari +- **fix(providers):** de-duplicate agy and antigravity model catalogs ([#13410](https://github.com/diegosouzapw/OmniRoute/pull/13410)) — thanks @KooshaPari +- **fix(dashboard):** send provider in body for mini-playground web search ([#13411](https://github.com/diegosouzapw/OmniRoute/pull/13411)) — thanks @KooshaPari +- **fix(resilience):** add CJK quota-exhaustion patterns to 429 classifier ([#13412](https://github.com/diegosouzapw/OmniRoute/pull/13412)) — thanks @KooshaPari +- **fix(backend):** eval runner: failed calls no longer score as passed + dotAll regex ([#13413](https://github.com/diegosouzapw/OmniRoute/pull/13413)) — thanks @KooshaPari +- **fix(backend):** eval runner opts out of output-style and memory injection ([#13414](https://github.com/diegosouzapw/OmniRoute/pull/13414)) — thanks @KooshaPari +- **fix:** chain serial quarantine step in npm test script (fixes #13286) ([#13418](https://github.com/diegosouzapw/OmniRoute/pull/13418)) — thanks @KooshaPari +- **fix(antigravity):** derive stable session ID from credentials for prompt-cache reuse (#13305) ([#13424](https://github.com/diegosouzapw/OmniRoute/pull/13424)) — thanks @KooshaPari +- **fix(memory):** merge memory into top-level system field instead of unshifting at messages[0] (#13425) ([#13427](https://github.com/diegosouzapw/OmniRoute/pull/13427)) — thanks @KooshaPari +- **fix(compression):** preserve instruction blocks from lossy engines ([#13523](https://github.com/diegosouzapw/OmniRoute/pull/13523)) — thanks @KooshaPari +- **fix(compression):** preserve polarity words and newlines in ultra heuristic ([#13524](https://github.com/diegosouzapw/OmniRoute/pull/13524)) — thanks @KooshaPari +- **fix(compression):** run user preservePatterns before built-in patterns (#13457) ([#13525](https://github.com/diegosouzapw/OmniRoute/pull/13525)) — thanks @KooshaPari +- **fix(reasoning):** expand deepseek-v4 regex to match v4.1+ and add command-code provider (#13430) ([#13528](https://github.com/diegosouzapw/OmniRoute/pull/13528)) — thanks @KooshaPari +- **fix(providers):** add alwaysReasons metadata for reka-flash-3 ([#13531](https://github.com/diegosouzapw/OmniRoute/pull/13531)) — thanks @KooshaPari +- **fix(playground):** allow Compare columns to scroll independently ([#13532](https://github.com/diegosouzapw/OmniRoute/pull/13532)) — thanks @KooshaPari +- **fix(security):** empty whitelist allows through to prevent admin lockout ([#13534](https://github.com/diegosouzapw/OmniRoute/pull/13534)) — thanks @KooshaPari +- **fix(tests):** add DISABLE_IOREG_STRATEGY env var for macOS test compatibility (fixes #13467) ([#13539](https://github.com/diegosouzapw/OmniRoute/pull/13539)) — thanks @KooshaPari +- **fix(zed-hosted):** map developer role and Gemini enums for Zed proxy ([#13543](https://github.com/diegosouzapw/OmniRoute/pull/13543)) — thanks @KooshaPari +- **fix(analytics):** normalize provider aliases to canonical id in usage_history ([#13545](https://github.com/diegosouzapw/OmniRoute/pull/13545)) — thanks @KooshaPari +- **fix(combo):** use traceId for call log id to prevent UNIQUE constraint failure ([#13546](https://github.com/diegosouzapw/OmniRoute/pull/13546)) — thanks @KooshaPari +- **fix(auth):** check model lockout before returning synthetic noauth connection ([#13547](https://github.com/diegosouzapw/OmniRoute/pull/13547)) — thanks @KooshaPari +- **fix(guardrails):** skip credential redaction for base64 image data URLs ([#13550](https://github.com/diegosouzapw/OmniRoute/pull/13550)) — thanks @KooshaPari +- **fix(resilience):** increase requestQueue.maxWaitMs default from 15s to 30s ([#13553](https://github.com/diegosouzapw/OmniRoute/pull/13553)) — thanks @KooshaPari +- **fix(docs):** document OMNIROUTE_READY_TIMEOUT_MS and allowlist the test-only DISABLE_IOREG_STRATEGY ([#13692](https://github.com/diegosouzapw/OmniRoute/pull/13692)) + ### 📝 Maintenance ---- +- **docs(readme):** reconcile live v3.8.50 provider, free-tier, CLI, routing, test, community, sponsor, acknowledgment, and SVG metrics with their audited source denominators, including a deduplicated OmniRoute-in-Action snapshot and distinct contributor rankings for merged pull requests, GitHub-attributed commits, and Git history ([#11356](https://github.com/diegosouzapw/OmniRoute/pull/11356)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **docs:** sync the canonical provider count 354 → 356 across `README.md`, `AGENTS.md`, `llm.txt` (+ 42 i18n mirrors), the four README SVG diagrams, `docs/reference/PROVIDER_REFERENCE.md` (regenerated) and the `package.json` description after Opper (#11629) and 1min.ai (#11631) boarded the catalog — closes the `check:docs-counts-sync` strict drifts that kept `release/v3.8.51` red ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11673](https://github.com/diegosouzapw/OmniRoute/pull/11673)) — thanks @jonlwheat2-gif +- **test(providers):** update count-derived assertions after the v3.8.51 provider additions — `APIKEY_PROVIDERS` 233 → 235 (Opper #11629 + 1min.ai #11631), reserved-prefix REGISTRY walk 395 → 398, `WEB_FETCH_PROVIDERS` now includes `nimble-search` (#11620), and the provider translate-path golden snapshot regenerated ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11674](https://github.com/diegosouzapw/OmniRoute/pull/11674)) — thanks @jonlwheat2-gif +- **test(sse):** bump the hard-lease connection-query inventory for `src/lib/tokenHealthCheck.ts` to 2 — the verify-only web-cookie sweep added by #11495 split the single `getProviderConnections` call into oauth + cookie variants, which the frozen inventory had not tracked ([#11495](https://github.com/diegosouzapw/OmniRoute/pull/11495)). ([#11677](https://github.com/diegosouzapw/OmniRoute/pull/11677)) — thanks @jonlwheat2-gif / @oyi77 +- **chore(release):** clear the `release/v3.8.51` base-red gates — docs migration count (+38 i18n mirrors), stryker `tap.testFiles`, lockfile registry host, stale ESLint suppressions, the 7 lint regressions they were hiding, the stale `APIKEY_PROVIDERS` partition count (231 → 233), the `glm-5.3-max` tier missing from the GLM test table, a dead `modelType === "chat"` comparison in the models catalog, and the `resolveLiveWsUrl` / `sanitizeLiveWsPort` helpers from #11388 that a later merge dropped from `wsPath.ts` (dashboard live WebSocket URL resolution) ([#11502](https://github.com/diegosouzapw/OmniRoute/pull/11502)) — thanks @MumuTW +- **chore(free-models):** the free-tier regime table now declares which totals figure each regime feeds and whether it may take the no-auth shortcut, so the sets that used to repeat those answers by hand are derived from it — a new `freeType` no longer compiles until it has answered every question, instead of silently contributing to no total ([#11537](https://github.com/diegosouzapw/OmniRoute/pull/11537)) — thanks @maxmad64bis +- **docs(routing):** LKGP strategy copy now states provider-level stickiness and the explicit rules fallback instead of promising per-target pinning ([#11555](https://github.com/diegosouzapw/OmniRoute/pull/11555)) — thanks @maxmad64bis +- **test(chatcore):** pin the response tool-name alias resolution against the ordering hazard that broke every Gemini/Antigravity MCP tool call in v3.8.49. `extractRequestToolIdentityMap` deletes `translatedBody._toolNameMap`, so the later read is always undefined and the ledger survives only through the `requestToolIdentityMap` fallback — removing that fallback previously left the entire tool-name suite green. The resolution moves into `resolveResponseToolNameMap()` next to the map it depends on, with a regression guard that fails without the recovery. No behaviour change. ([#11575](https://github.com/diegosouzapw/OmniRoute/pull/11575)) — thanks @amartinawi +- **docs(env):** document `OMNIROUTE_CORPUS_CACHE_SIZE` in `.env.example` and `ENVIRONMENT.md`, so the env/docs contract check stops failing Docs Gates on every branch ([#11576](https://github.com/diegosouzapw/OmniRoute/pull/11576)) — thanks @ntdatt812 +- **chore(stryker):** register `route-guard-tunnel-processes-local-only` and `universal-quota-aware-routing` in `tap.testFiles`, so their mutant kills count for the three modules they cover ([#11577](https://github.com/diegosouzapw/OmniRoute/pull/11577)) — thanks @ntdatt812 +- **test(authz):** pin `LOCAL_ONLY_API_GET_EXEMPTIONS` by exact membership instead of by entry count, so the guard names the offending path and also catches a substitution ([#11580](https://github.com/diegosouzapw/OmniRoute/pull/11580)) — thanks @ntdatt812 +- **test(build):** pin `resolveNextBuildBundlerFlag` to the `OMNIROUTE_USE_TURBOPACK` contract it implements, across runtimes, instead of asserting an unimplemented Bun override ([#11583](https://github.com/diegosouzapw/OmniRoute/pull/11583)) — thanks @ntdatt812 +- **test(claude-web):** the Claude Web transport test's barrel anchors no longer pin the exact construction spelling. #11421 made the executor map lazy, so `/"claude-web": new ClaudeWebExecutor\(\)/` stopped matching and the test went red on `release/v3.8.51` itself — a base-red gate every open PR inherited. The anchors now match the alias key and the class name and tolerate either spelling in between, and were checked to still fail when an alias is genuinely repointed at another executor ([#11585](https://github.com/diegosouzapw/OmniRoute/pull/11585)) — thanks @ntdat812 +- **test(models):** pin `qwen3.8-max` resolution per provider — two of the four catalogs now serve the bare id and no longer carry `-preview` ([#11590](https://github.com/diegosouzapw/OmniRoute/pull/11590)) — thanks @ntdatt812 +- **test(executors):** drop the stale 128-tool truncation assertion that contradicted the #11444 pass-through contract ([#11591](https://github.com/diegosouzapw/OmniRoute/pull/11591)) — thanks @ntdatt812 +- **test(token-health):** pin the current terminal-skip boundary — expired with retry budget is probed, `account_deactivated` stays skipped ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)) — thanks @ntdatt812 +- **test(models):** follow #11503's retarget of the `gemini-3-pro-high` alias to the hyphenated catalog id ([#11593](https://github.com/diegosouzapw/OmniRoute/pull/11593)) — thanks @ntdatt812 +- **refactor(video bridge):** extract per-video acquisition, whole-result caching, description, and metrics/abort/cleanup out of `VideoBridgeGuardrail.preCall` into a `processVideoPart` seam in a new `videoBridgePipeline.ts`, behind explicit `VideoMediaBrokerPort`, `VideoAudioTranscriptionPort`, and `VideoDrilldownPort` boundaries; `preCall` now only handles request traversal, policy, and response aggregation. The Video tab's FFmpeg/ffprobe runtime status is now an explicit `unknown` / `restricted` / `unavailable` / `available` state instead of a nullable boolean pair, fixing a case where an in-flight or failed probe was mislabeled as "install FFmpeg" ([#11657](https://github.com/diegosouzapw/OmniRoute/issues/11657)). ([#12016](https://github.com/diegosouzapw/OmniRoute/pull/12016)) +- **test(opencode-plugin):** add unit test coverage for `formatFreeBudget()` naming helper ([#11660](https://github.com/diegosouzapw/OmniRoute/pull/11660)) — thanks @f9td56dbgh-hub +- **docs(video):** clarify that the Video Bridge transcript `source` field (`client`, `embedded`, `audio-bridge`) is presently caller-declared and not yet server-verified — OmniRoute enforces the enum shape but does not cryptographically confirm that an `embedded`/`audio-bridge` label came from a server-owned extraction ([#11661](https://github.com/diegosouzapw/OmniRoute/issues/11661)). ([#12001](https://github.com/diegosouzapw/OmniRoute/pull/12001)) +- **chore(providers):** retire the distributed Raycast Relay and Hailuo Web integrations after a provenance audit confirmed substantial derivation from GPL-3.0 sources; the official GitHub and MiniMax API providers remain available. Migration 163 disables legacy connections and invalidates their active leases while preserving connection IDs, audit/history rows, quota snapshots, usage records, and fail-closed API-key connection policies. ([#11691](https://github.com/diegosouzapw/OmniRoute/pull/11691)) +- **chore(providers):** retire the Felo Web provider while its GPL-derived provenance remains on licensing HOLD ([#11698](https://github.com/diegosouzapw/OmniRoute/pull/11698)) — stale runtime and database references now fail closed while existing connection, lease, and usage history remains preserved for auditability. +- **fix(sse):** retire the Gemini Web image-generation bridge while provenance and licensing remain on HOLD, preserving Gemini Web chat/auth/tools and legitimate Gemini/Nano Banana image providers ([#11708](https://github.com/diegosouzapw/OmniRoute/pull/11708)) +- **chore(audio):** retire the built-in EdgeTTS provider and its 11-voice catalog while provenance/licensing review remains on HOLD; the generic `/v1/audio/speech` endpoint and the other speech providers remain available ([#11711](https://github.com/diegosouzapw/OmniRoute/pull/11711)) +- **fix(providers):** retire the unofficial Qwen Web integration and its `qwen-web`/`qw` identities while third-party provenance remains on HOLD ([#11713](https://github.com/diegosouzapw/OmniRoute/pull/11713)) — direct requests now fail closed with a sanitized HTTP 410 and combos skip the retired target, while migration guards preserve connection IDs, allowlists, usage/audit history and invalidate active leases. Supported Qwen API, Qwen Code, cloud, token-plan, audio, local and model-name surfaces remain available; restoring Qwen Web requires a qualified provenance/license decision. +- **fix(providers):** retire `microsoft-designer-web` and its `msdesigner` alias from runtime and catalogs while preserving stored credentials, provider history and allowlists as inactive records — source dependency on gpt4free's [`MicrosoftDesigner.py`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/g4f/Provider/needs_auth/MicrosoftDesigner.py) is proven at snapshot `0943d16eef757a44deb60651c07bbe5fa958cafe`, whose [`LICENSE`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/LICENSE) is GPL-3.0; expressive adaptation is probable, which records provenance/licensing risk without asserting a legal conclusion ([#11720](https://github.com/diegosouzapw/OmniRoute/pull/11720)). +- **docs(legal):** centralize pinned MIT notices for the vendored GCF codec and local flag assets ([#11726](https://github.com/diegosouzapw/OmniRoute/pull/11726)) +- Split the 2,952-line `adobeFireflyClient.ts` into nine focused modules (catalog, credentials, payload, ARP session, upload, responses, IMS, account, poll) behind an unchanged barrel — no consumer edits and no public API change (#11728). — thanks @MumuTW +- **fix(dashboard):** replaced the Model Lockout card's unprovenanced notification MP3 with a short, locally synthesized Web Audio chime ([#11731](https://github.com/diegosouzapw/OmniRoute/pull/11731)) — both toggles retain optional audible feedback without shipping replacement media or third-party code, and unsupported, suspended, or rejected audio contexts fail open without blocking the setting change. +- **chore(deepseek-web):** remove the provenance-unresolved DeepSeek PoW WASM binary and its runtime loader/tracing while retaining the existing JavaScript solver (slower at high difficulty) ([#11732](https://github.com/diegosouzapw/OmniRoute/pull/11732)). +- **chore(assets):** remove four unused provider assets whose redistribution provenance could not be verified while retaining the active Freebuff SVG ([#11735](https://github.com/diegosouzapw/OmniRoute/pull/11735)) +- **docs(assets):** retired two raster images whose redistribution provenance could not be verified ([#11736](https://github.com/diegosouzapw/OmniRoute/pull/11736)) — PicoClaw documentation now reuses the existing SVG, while Zoo Code remains a text link without an image. +- **docs(legal):** add pinned notices for six LobeHub-derived provider SVGs and 65 byte-exact theSVG assets, retaining registry claims as unverified metadata with explicit trademark and HOLD boundaries ([#11737](https://github.com/diegosouzapw/OmniRoute/pull/11737)). +- **chore(quality):** added a versioned 225/225 provider-asset provenance manifest and blocking drift gate ([#11743](https://github.com/diegosouzapw/OmniRoute/pull/11743)) — the inventory pins magic MIME and SHA-256 for every physical file, records immutable source matches for 71 assets, keeps 69 probable and 85 unresolved entries explicit but non-blocking, and treats provenance as source evidence rather than copyright or trademark clearance. +- **refactor(mitm):** replace four previously derived Traffic Inspector utilities — conversation normalization, SSE merging, secret masking, and header sanitization — with independent clean-room implementations based on public provider schemas, WHATWG SSE, and RFC 6750, while keeping the two independently classified passthrough surfaces unchanged ([#11748](https://github.com/diegosouzapw/OmniRoute/pull/11748)). +- Added a unit test that fails seven days before any dated pack under `config/` (`validUntil` and sibling keys) lapses, naming the file and key. The Alibaba free-tier pack expired on 2026-08-27 and turned every PR red the next morning with no commit involved; renewal now happens on someone's terms, not the clock's. ([#11891](https://github.com/diegosouzapw/OmniRoute/pull/11891)) +- `check:workflows` now fails (under `--strict`/`--ratchet`) when any job routed to a self-hosted runner publishes with `--provenance` — npm rejects that with `422` at the registry, which in v3.8.50 only surfaced after the tag and Docker images were public. ([#11895](https://github.com/diegosouzapw/OmniRoute/pull/11895)) +- `scripts/ops/runner-janitor.sh` now proves a path is idle with one `lsof` snapshot and removes stale leftovers itself (tmpfs after 3 h — it is RAM — disk after 24 h), kills orphan `next-build` processes, prunes checkouts of stopped runners, and alerts on memory pressure; `--dry-run` shows exactly what it would do. `docs/ops/RUNNER_BOX.md` reconciled to the measured box (31 GB, 10 listeners). ([#11893](https://github.com/diegosouzapw/OmniRoute/pull/11893)) +- The `next-build` artefact (1.3 GB) is now written and read under `$RUNNER_TEMP` (per-runner, on disk) instead of `/tmp`, which on the self-hosted pool is a 12 GB tmpfs in RAM. Landing it there took 27–32 of the publish job's 76 minutes, and the fixed `/tmp/e2e-build.tar.gz` name let E2E jobs on different runners overwrite each other's download. ([#11896](https://github.com/diegosouzapw/OmniRoute/pull/11896)) +- The CI `build` job now runs in two concurrency lanes — `main` and pull requests — so a release build is never queued behind (or OOM-killed beside) PR builds on the self-hosted pool, which holds one `next-build` comfortably and two at the edge. ([#11901](https://github.com/diegosouzapw/OmniRoute/pull/11901)) +- Re-freeze the ESLint suppressions on `release/v3.8.51` from a clean-room measurement (2 stale file entries pruned, 55 pre-existing `no-explicit-any` in six new files frozen under #11924) and drop the dead `GPT_SIZE_MAP` constant orphaned by the Adobe Firefly client split, so `No new ESLint warnings` stops failing every PR with exit 2 (Refs #11924) ([#11955](https://github.com/diegosouzapw/OmniRoute/pull/11955)) +- Type the 55 `no-explicit-any` sites that had been frozen under #11924 — four redundant casts in `socksConnectorWithFamily.ts` (undici/socks types already accept them) and the mocks/fixtures of the socks-timeout and isFree suites — and drop their suppression entries; the ESLint ratchet shrinks from 5487 to 5432 (Closes #11924) ([#11975](https://github.com/diegosouzapw/OmniRoute/pull/11975)) +- Take the two hosted-runner builds off the PR rail: `Build App` (`build.yml`) is `workflow_dispatch`-only and `DAST smoke (PR)` runs only for PRs into `main` — the 7 GB hosted VM cannot build `release/v3.8.51` in any profile (19/30 red, VM shutdown ~8 min into `next build`) and both checks had turned into permanent noise on every release PR; the bundle stays validated by `ci.yml` on `main` and by `nightly-release-green` on `release/**` (Closes #11946) ([#11962](https://github.com/diegosouzapw/OmniRoute/pull/11962)) +- Move the four nightly jobs that build the backend (`nightly-schemathesis`, `nightly-llm-security` promptfoo + garak, `nightly-resilience` axe-a11y) off the hosted 7 GB runner — where they died on `release/v3.8.51` unseen — onto the box's new `omni-light` pool (two listeners), and document the reshaped fleet (4 active OmniRoute listeners: 2 `omni-build` + 2 `omni-light`, janitor ceiling 4) (Closes #11965) ([#11967](https://github.com/diegosouzapw/OmniRoute/pull/11967)) +- Stop painting every fork PR into `release/**` red: `quality.yml` `Build (advisory)` is skipped (GitHub still reports `continue-on-error` failures as check FAILURE). Hosted `ubuntu-latest` cannot finish `npm run build` on this tree — same class as #11962 taking `build.yml` off the PR rail. `docker-publish.yml` amd64 now runs on the `.113` `omni-build` pool (31 GB / 32 cores, two listeners) with Turbopack, shares the `heavy-build-main` lane with `ci.yml` `Build` so it queues instead of becoming a third heavy, and keeps per-ref concurrency (a merge storm was starting 8 concurrent OOM builds). arm64 stays on `ubuntu-24.04-arm` with webpack — there is no ARM box. Fallback when `USE_VPS_RUNNER` is off: hosted amd64 + webpack (#11976). ([#12021](https://github.com/diegosouzapw/OmniRoute/pull/12021)) +- `docker-publish.yml` builds with webpack (`OMNIROUTE_USE_TURBOPACK=0`) on **both** arches, including amd64 on the `.113` `omni-build` pool. Turbopack had the RAM (31 GB, `omniroute-113-6`) and still panicked (`TurbopackInternalError: there must be a path to a root`, run 33253576569, 26 min in). The same tree's arm64 webpack build on hosted `ubuntu-24.04-arm` succeeded (run 33264823398). Dockerfile already documents webpack as the Docker escape hatch (#11976). ([#12050](https://github.com/diegosouzapw/OmniRoute/pull/12050)) +- **docs(free-tier):** declare the counting vs deciding regimes for "is it free?" and guard the deciding path from DB-backed catalog resolution ([#12226](https://github.com/diegosouzapw/OmniRoute/pull/12226)) — thanks @maxmad64bis +- **docs(auto-combo):** The mode pack table in `docs/routing/AUTO-COMBO.md` now lists all six shipped packs with every weight each one sets, replacing a four-pack table whose numbers had also drifted from the source. It states plainly that no pack sets `quality`, so selecting any pack silences the observed-quality signal. Six more documents that quote the scoring factor count joined the `check:docs-counts` gate, which caught five stale claims — including one naming nine factors that do not exist — and two stale mode pack counts. The dashboard routing panel, which offered four of the six packs and labelled the default strategy "6-Factor Scoring", is now covered by a test; the two packs it was missing are `reliability-first` and `chaos-mode`, the latter labelled as the fault-injection profile it is rather than as one more routing preference ([#12316](https://github.com/diegosouzapw/OmniRoute/pull/12316)) — thanks @maxmad64bis +- **chore(quality):** rebaseline `src/lib/db/apiKeys.ts` for the ACL the key-creation path now preserves ([#12352](https://github.com/diegosouzapw/OmniRoute/pull/12352)) ([#12673](https://github.com/diegosouzapw/OmniRoute/pull/12673)) — thanks @KrzysiekSko +- **chore(providers):** bump the Claude Code wire identity and the Devin bridge image pin from `2.1.220` to `2.1.258` ([#12402](https://github.com/diegosouzapw/OmniRoute/pull/12402)) — thanks @ggiak ([#12604](https://github.com/diegosouzapw/OmniRoute/pull/12604)) +- **docs(env):** align `.env.example`, the README Bun section, and the troubleshooting guide with the code: `OMNIROUTE_USE_TURBOPACK` also governs `npm run build` (not dev-only), `bun run build` follows that flag instead of auto-selecting Webpack, `OMNIROUTE_CHAT_MAX_HEAVY_IN_FLIGHT` is unset by default (no request-count cap), and the structural `503 chat_admission_busy` message matches `chatAdmissionResponses.ts` (#12404 — thanks @pacocartones) +- **chore(electron):** upgrade the desktop app to Electron 44 (Chromium 152, Node 24.18.1) ([#12217](https://github.com/diegosouzapw/OmniRoute/pull/12217)). **Requires macOS 13 (Ventura) or later** — Chromium dropped macOS 12 (Monterey), so Monterey users must stay on an earlier OmniRoute desktop build. Windows and Linux are unaffected; the app already shipped only x64/arm64, so Electron 44 dropping 32-bit builds changes nothing. Removes the `openAsHidden`/`wasOpenedAsHidden` login-item fields deleted in Electron 44 — hidden autostart continues to work through the `--hidden` argument registered with the login item ([#12554](https://github.com/diegosouzapw/OmniRoute/pull/12554)) — thanks @app/dependabot +- **chore(quality):** rebaseline `src/sse/handlers/chat.ts` for the effective-input persistence the continuation fix needs ([#12641](https://github.com/diegosouzapw/OmniRoute/pull/12641)) ([#12680](https://github.com/diegosouzapw/OmniRoute/pull/12680)) — thanks @hartmark +- `Coverage` job on `ci.yml`: the informational Codecov upload gets its own 5-minute ceiling and `continue-on-error`, and the job budget grows from 20 to 30 minutes (the 8-shard c8 merge alone takes ~10) — a stalled upload no longer ends the job `cancelled` and drags a fully green `main` run's conclusion down with it ([#11972](https://github.com/diegosouzapw/OmniRoute/pull/11972)) +- CI hardening on the PR rail: the four `Unit Tests fast-path` shards get `timeout-minutes: 30` (a hung shard held a PR for 64 min instead of GitHub's 6 h default) and both ESLint file caches lose their `restore-keys` fallback, so a cache built under another suppressions file or lint config can no longer report stale verdicts (Refs #11600, #11924) ([#11963](https://github.com/diegosouzapw/OmniRoute/pull/11963)) +- Every CI job that runs a `next build` (`build`, the npm `publish`, both release-green validations) now targets the `omni-build` runner label, which only two of the eight self-hosted runners carry. The box holds one build comfortably and two at the edge; a third now queues on GitHub instead of being OOM-killed by the kernel. ([#11932](https://github.com/diegosouzapw/OmniRoute/pull/11932)) +- **refactor(combo):** move `handleRoundRobinCombo` (and `resolveTargetTokenLimit`) into `open-sse/services/combo/roundRobinCombo.ts`. `combo.ts` drops from 2164 to 1014 lines (`split("\n").length`); the leaf is 1199 (under the 1200 new-file cap). The round-robin call site uses a dynamic `import()` so `releaseStickyPinOnFailure` / `clearStaleLKGP` can stay exported from `combo.ts` without a static cycle. Skip / sticky / semaphore / safety-timer behavior is unchanged. ([#12811](https://github.com/diegosouzapw/OmniRoute/pull/12811)) — thanks @HouMinXi +- **chore(quality):** rebaseline the file-size caps the error-boundary campaign grew past (`open-sse/executors/codex.ts`, `open-sse/vendor/codex-chatgpt-web/bridge.ts`, both via [#12444](https://github.com/diegosouzapw/OmniRoute/pull/12444)) ([#12654](https://github.com/diegosouzapw/OmniRoute/pull/12654)) +- **chore(quality):** rebaseline the file-size caps the hartmark batch grew past (`combos/page.tsx` via [#12355](https://github.com/diegosouzapw/OmniRoute/pull/12355), `open-sse/services/combo.ts` via [#12338](https://github.com/diegosouzapw/OmniRoute/pull/12338)) ([#12623](https://github.com/diegosouzapw/OmniRoute/pull/12623)) — thanks @hartmark +- **chore(quality):** rebaseline the file-size caps the HouMinXi batch grew past when its PRs stacked (`providers/page.tsx`, `chatCore.ts`, `accountFallback.ts`) — each PR measured correctly in isolation, none saw the stacking ([#12619](https://github.com/diegosouzapw/OmniRoute/pull/12619)) +- **chore(quality):** rebaseline `open-sse/services/combo.ts` for the reset-aware scoring the HouMinXi batch stacked ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) ([#12678](https://github.com/diegosouzapw/OmniRoute/pull/12678)) — thanks @HouMinXi +- **docs:** sync provider catalog count (direct commit `4f11b2ae3d`) +- **docs(skills):** sync omni-inference search fallback (direct commit `b4e76a7ed9`) +- **chore(lint):** prune two stale base suppressions (direct commit `1325047d56`) +- **test(auto-combo):** tolerate full-suite rotation load (direct commit `ad3e293f9f`) +- **test:** drain inherited quality snapshot reds (direct commit `21ed68d8ac`) +- **chore(quality):** rebaseline measured CLI bundle growth (direct commit `d35b3f9779`) +- **test(ui):** reproduce invalid AnySearch icon alias (direct commit `12cc6ca834`) +- **test(translator):** fix the relative imports of the relocated deferred-finish test (direct commit `777d9d1629`) +- **docs(counts):** bring the documented provider and migration counts to the measured values (direct commit `e70bea30e9`) +- **test(security):** drop the guard-target set left unused by the sync (direct commit `a32ba0e362`) +- **chore(db):** renumber model_capabilities migration 163 → 169 (cross-branch collision with 163_radar_feed_cache_generated_at) (direct commit `70f5d4cbf9`) +- **docs(cli):** document CLI_PRIME_AGENT_BIN and correct the CLI Agents count (direct commit `9058e39b61`) +- **chore(deps):** refresh runtimes and adopt ESLint 10 ([#11259](https://github.com/diegosouzapw/OmniRoute/pull/11259)) — thanks @backryun +- **refactor(local-corpus):** implement dynamic root resolution and LRU cache ([#11491](https://github.com/diegosouzapw/OmniRoute/pull/11491)) — thanks @raheemuddin786 +- **chore(quality):** prune stale eslint-suppressions entries ([#11596](https://github.com/diegosouzapw/OmniRoute/pull/11596)) +- **chore(ci):** unblock quality gates - env-doc sync + stryker tap.testFiles registration (#9282) ([#11609](https://github.com/diegosouzapw/OmniRoute/pull/11609)) — thanks @Meet6338-X +- **chore(ci):** point the circular-deps gate at dpdm's real JS entrypoint ([#11615](https://github.com/diegosouzapw/OmniRoute/pull/11615)) — thanks @benzntech +- **chore(quality):** rebaseline file-size for #11448/#11495/#11561 growth ([#11630](https://github.com/diegosouzapw/OmniRoute/pull/11630)) +- **deps:** realign onnxruntime-node pin to @huggingface/transformers' 1.24.3 lockstep ([#11633](https://github.com/diegosouzapw/OmniRoute/pull/11633)) — thanks @jonlwheat2-gif +- **test(autoCombo):** port models_dev_tier #11508 guard from node:test to vitest ([#11635](https://github.com/diegosouzapw/OmniRoute/pull/11635)) — thanks @jonlwheat2-gif +- **chore(quality):** rebaseline file-size for #11083/#11631/#11628 growth ([#11636](https://github.com/diegosouzapw/OmniRoute/pull/11636)) +- **chore(lint):** prune stale ESLint suppressions (base-red #11449) ([#11644](https://github.com/diegosouzapw/OmniRoute/pull/11644)) — thanks @jonlwheat2-gif +- **docs(changelog):** aggregate the ten v3.8.50 fragments into CHANGELOG.md ([#11683](https://github.com/diegosouzapw/OmniRoute/pull/11683)) +- **chore(search):** drop unreachable credential sweep after #11642 ([#11687](https://github.com/diegosouzapw/OmniRoute/pull/11687)) — thanks @HouMinXi +- **ci:** add API route TypeScript regression gate ([#11705](https://github.com/diegosouzapw/OmniRoute/pull/11705)) — thanks @Sabeekhann +- **docs(dedup):** correct the CodeQL claim on the tenant-namespace comment ([#11712](https://github.com/diegosouzapw/OmniRoute/pull/11712)) +- **docs(changelog):** add consolidated v3.8.50 stats, top-25 ranking and a .mailmap ([#11715](https://github.com/diegosouzapw/OmniRoute/pull/11715)) +- **docs:** recommend gstack for AI-assisted workflows ([#11770](https://github.com/diegosouzapw/OmniRoute/pull/11770)) — thanks @santosraju99-hub +- **chore(quality):** tighten complexity/cognitive-complexity ratchets to the current tip; land the missed gateways.ts rebaseline ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **docs(i18n):** finish Freepik → Magnific rebrand in locale strings and README ([#11772](https://github.com/diegosouzapw/OmniRoute/pull/11772)) — thanks @turbolego +- **refactor(auto-combo):** fix divergent scoring in combo health reporting ([#11854](https://github.com/diegosouzapw/OmniRoute/pull/11854)) — thanks @jacobsparts +- **chore(changelog):** add missing fragments for #11919/#11918/#11916 ([#11938](https://github.com/diegosouzapw/OmniRoute/pull/11938)) +- **test(check):** escape the runs-on fixture with JSON.stringify, not a quote-only replace ([#11942](https://github.com/diegosouzapw/OmniRoute/pull/11942)) (direct commit `fb7445eaa3`) +- **test(release):** align five suites with the contracts #11933, #11919 and #11876 shipped on release/v3.8.51 ([#11944](https://github.com/diegosouzapw/OmniRoute/pull/11944)) +- **chore(quality):** remove unreachable code and restore test discovery ([#11950](https://github.com/diegosouzapw/OmniRoute/pull/11950)) — thanks @backryun +- **docs(agents):** sync-back landings are fast-forward, never squash ([#11964](https://github.com/diegosouzapw/OmniRoute/pull/11964)) +- **test(infra):** retry recursive temp-dir removal instead of failing a shard on ENOTEMPTY (#11966) ([#11968](https://github.com/diegosouzapw/OmniRoute/pull/11968)) +- **docs(ops):** the .113 heavy-build ceiling is one runner, not two ([#12048](https://github.com/diegosouzapw/OmniRoute/pull/12048)) +- **test(ui):** correct inactive auto-fetch expectation ([#12098](https://github.com/diegosouzapw/OmniRoute/pull/12098)) — thanks @RaviTharuma +- **docs:** document native dependency check escape hatch ([#12101](https://github.com/diegosouzapw/OmniRoute/pull/12101)) — thanks @RaviTharuma +- **test(providers):** regenerate the translate-path golden for OrcaRouter (#11923) ([#12118](https://github.com/diegosouzapw/OmniRoute/pull/12118)) +- **chore(quality):** velocity phase — loosen every numeric baseline by 20% until v4.0, monitor headroom nightly ([#12125](https://github.com/diegosouzapw/OmniRoute/pull/12125)) +- **chore(lint):** batch 0 of #12146 — type the call-log-cap sqlite rows instead of 45 as-any casts ([#12157](https://github.com/diegosouzapw/OmniRoute/pull/12157)) +- **chore(lint):** batch 4 of #12146 — shared/components react-hooks violations resolved ([#12159](https://github.com/diegosouzapw/OmniRoute/pull/12159)) +- **chore(lint):** batch 1 of #12146 — dashboard/cli-code react-hooks violations resolved ([#12160](https://github.com/diegosouzapw/OmniRoute/pull/12160)) +- **chore(lint):** batch 3 of #12146 — dashboard/settings react-hooks violations resolved ([#12162](https://github.com/diegosouzapw/OmniRoute/pull/12162)) +- **chore(lint):** batch 2 of #12146 — resolve the react-hooks compiler violations in dashboard/providers ([#12163](https://github.com/diegosouzapw/OmniRoute/pull/12163)) +- **chore(quality):** register search-432 cooldown test in stryker tap.testFiles ([#12170](https://github.com/diegosouzapw/OmniRoute/pull/12170)) +- **chore(lint):** batch 5 of #12146 — combos, endpoint, provider-stats, api-manager and costs react-hooks violations resolved ([#12174](https://github.com/diegosouzapw/OmniRoute/pull/12174)) +- **build:** add contributor fast profile ([#12192](https://github.com/diegosouzapw/OmniRoute/pull/12192)) — thanks @rafacpti23 +- **docs(readme):** README touch-ups ([#12193](https://github.com/diegosouzapw/OmniRoute/pull/12193)) ([#12194](https://github.com/diegosouzapw/OmniRoute/pull/12194)) ([#12202](https://github.com/diegosouzapw/OmniRoute/pull/12202)) — thanks @rafacpti23 +- **build:** force the webpack bundler in the contributor build profile ([#12197](https://github.com/diegosouzapw/OmniRoute/pull/12197)) — thanks @rafacpti23 +- **build:** skip standalone packaging in the contributor profile ([#12198](https://github.com/diegosouzapw/OmniRoute/pull/12198)) — thanks @rafacpti23 +- **docs(audit):** align every published number with the code and harden check:docs-counts ([#12200](https://github.com/diegosouzapw/OmniRoute/pull/12200)) +- **build:** stub the instrumentation entrypoints in the contributor profile ([#12203](https://github.com/diegosouzapw/OmniRoute/pull/12203)) — thanks @rafacpti23 +- **build:** omit the standalone output target for contributor builds ([#12204](https://github.com/diegosouzapw/OmniRoute/pull/12204)) — thanks @rafacpti23 +- **chore(lint):** batch 6 of #12146 — memory, radar, audit, analytics, cache, usage, activity, home and RequestLoggerV2 react-hooks violations resolved ([#12208](https://github.com/diegosouzapw/OmniRoute/pull/12208)) +- **docs(diagrams):** rename number-carrying diagram files to stable names ([#12210](https://github.com/diegosouzapw/OmniRoute/pull/12210)) +- **docs(dashboard):** redraw onboarding tier-flow SVGs for the real 4-tier model ([#12211](https://github.com/diegosouzapw/OmniRoute/pull/12211)) +- **docs(api):** document every implemented route in openapi.yaml (276 → 692 paths) ([#12212](https://github.com/diegosouzapw/OmniRoute/pull/12212)) +- **chore(lint):** batch 7 of #12146 — final src tail: 37 react-hooks violations across 33 files resolved ([#12244](https://github.com/diegosouzapw/OmniRoute/pull/12244)) +- Revert "docs: recommend gstack for AI-assisted workflows (#11770)" ([#12248](https://github.com/diegosouzapw/OmniRoute/pull/12248)) ([#12249](https://github.com/diegosouzapw/OmniRoute/pull/12249)) +- **docs:** Chaos Mode setup guide + weighted strategy semantics ([#12250](https://github.com/diegosouzapw/OmniRoute/pull/12250)) +- **docs(agents):** protected-surface merge rule — operator approval for agent-instruction files ([#12253](https://github.com/diegosouzapw/OmniRoute/pull/12253)) +- **chore(quality):** register native-codex turn-pin tests in stryker tap.testFiles ([#12263](https://github.com/diegosouzapw/OmniRoute/pull/12263)) +- **chore(quality):** dedupe tap.testFiles entries added by racing base-red fixes ([#12265](https://github.com/diegosouzapw/OmniRoute/pull/12265)) +- **docs(readme):** introduce OmniRouteTray — the macOS menu-bar companion ([#12276](https://github.com/diegosouzapw/OmniRoute/pull/12276)) — thanks @ggiak +- **chore(proxy):** purge the legacy 1proxy residue — sync/rotator modules, dead DB exports, dead settings tab and flag, docs (#12091) ([#12290](https://github.com/diegosouzapw/OmniRoute/pull/12290)) +- **chore(quality):** baseline-headroom skips generated and vendored files in the fileSize worst-file signal ([#12291](https://github.com/diegosouzapw/OmniRoute/pull/12291)) +- **refactor(video):** unify the JPEG frame data-URI contract ([#12322](https://github.com/diegosouzapw/OmniRoute/pull/12322)) +- **chore(deps):** freeze onnxruntime-node and eslint-plugin-react-hooks out of dependabot groups ([#12329](https://github.com/diegosouzapw/OmniRoute/pull/12329)) +- **test(executors):** restore the #10986 reasoning-only fallback guards ([#12364](https://github.com/diegosouzapw/OmniRoute/pull/12364)) — thanks @pacocartones +- **chore(quality):** re-tighten the file-size ratchet to the real LOC (plan 3.8.52 task 0) ([#12411](https://github.com/diegosouzapw/OmniRoute/pull/12411)) +- **test(grok):** format web executor suite ([#12412](https://github.com/diegosouzapw/OmniRoute/pull/12412)) +- **chore(quality):** ratchet open-sse typecheck baseline to zero ([#12418](https://github.com/diegosouzapw/OmniRoute/pull/12418)) +- **test(build):** guard the artifact path policy arrays against duplicates ([#12422](https://github.com/diegosouzapw/OmniRoute/pull/12422)) +- **chore(lint):** adopt eslint-plugin-react-hooks 7.1.1 ([#12428](https://github.com/diegosouzapw/OmniRoute/pull/12428)) +- **chore(providers):** remove a keyless provider integration at its operator's request ([#12440](https://github.com/diegosouzapw/OmniRoute/pull/12440)) +- **docs(providers):** register providers removed at their operator's request and guard against reintroduction ([#12478](https://github.com/diegosouzapw/OmniRoute/pull/12478)) +- **chore(quality):** base fixes — stryker tap.testFiles + node_modules cache key ([#12482](https://github.com/diegosouzapw/OmniRoute/pull/12482)) +- **docs(arch):** one-process recipe for tens of long /v1/responses ([#12493](https://github.com/diegosouzapw/OmniRoute/pull/12493)) — thanks @RaviTharuma +- **chore(quality):** tighten the CodeQL ratchet baseline from 11 to 6 ([#12530](https://github.com/diegosouzapw/OmniRoute/pull/12530)) +- **chore(deps):** pin browserslist override to ^4.28.8 ([#12592](https://github.com/diegosouzapw/OmniRoute/pull/12592)) — thanks @KooshaPari +- **chore(deps):** bump fast-uri to 3.1.7 in the electron lockfile ([#12601](https://github.com/diegosouzapw/OmniRoute/pull/12601)) +- **docs(quality):** document how the CodeQL ratchet refreshes and how to tighten it ([#12611](https://github.com/diegosouzapw/OmniRoute/pull/12611)) +- **test(ci):** pin the openapi-security-tiers two-arm contract with an executing gate test (#12581) ([#12652](https://github.com/diegosouzapw/OmniRoute/pull/12652)) +- **docs(gamification):** align XP Rewards table with code (#12501) ([#12667](https://github.com/diegosouzapw/OmniRoute/pull/12667)) — thanks @KooshaPari +- **refactor(combo):** split executeTarget into gates, attempt, and loop ([#12746](https://github.com/diegosouzapw/OmniRoute/pull/12746)) — thanks @HouMinXi +- **docs(security):** document socket.yml scanner config + CI workflow link (#12575) ([#12764](https://github.com/diegosouzapw/OmniRoute/pull/12764)) — thanks @KooshaPari +- **chore(ci):** guard commit identity in pre-commit to stop author misattribution ([#12772](https://github.com/diegosouzapw/OmniRoute/pull/12772)) +- **deps:** 17 Dependabot bumps — bump electron from 43.4.0 to 43.4.1 in /electron ([#11426](https://github.com/diegosouzapw/OmniRoute/pull/11426)); bump the development group across 1 directory with 8 updates ([#11428](https://github.com/diegosouzapw/OmniRoute/pull/11428)); bump the production group across 1 directory with 11 updates ([#11440](https://github.com/diegosouzapw/OmniRoute/pull/11440)); chore(deps): bump github/codeql-action/init from 4.37.7 to 4.37.8 ([#11925](https://github.com/diegosouzapw/OmniRoute/pull/11925)); chore(deps): bump github/codeql-action from 4.37.7 to 4.37.8 ([#11926](https://github.com/diegosouzapw/OmniRoute/pull/11926)); chore(deps): bump github/codeql-action/analyze from 4.37.7 to 4.37.8 ([#11927](https://github.com/diegosouzapw/OmniRoute/pull/11927)); build(deps): bump github/codeql-action/init from 4.37.8 to 4.37.9 ([#12345](https://github.com/diegosouzapw/OmniRoute/pull/12345)); build(deps): bump github/codeql-action/analyze from 4.37.8 to 4.37.9 ([#12346](https://github.com/diegosouzapw/OmniRoute/pull/12346)); bump the development group across 1 directory with 2 updates ([#12347](https://github.com/diegosouzapw/OmniRoute/pull/12347)); build(deps): bump github/codeql-action from 4.37.8 to 4.37.9 ([#12349](https://github.com/diegosouzapw/OmniRoute/pull/12349)); bump browserslist from 4.28.2 to 4.28.8 ([#12396](https://github.com/diegosouzapw/OmniRoute/pull/12396)); bump the production group across 1 directory with 4 updates ([#12399](https://github.com/diegosouzapw/OmniRoute/pull/12399)); bump @xmldom/xmldom ([#12500](https://github.com/diegosouzapw/OmniRoute/pull/12500)); bump qs from 6.15.2 to 6.16.0 ([#12512](https://github.com/diegosouzapw/OmniRoute/pull/12512)); bump @xmldom/xmldom from 0.9.10 to 0.9.12 ([#12513](https://github.com/diegosouzapw/OmniRoute/pull/12513)); bump fast-uri from 3.1.5 to 3.1.7 ([#12514](https://github.com/diegosouzapw/OmniRoute/pull/12514)); bump @humanfs/node from 0.16.7 to 0.16.8 ([#12515](https://github.com/diegosouzapw/OmniRoute/pull/12515)) +- Clear the `release/v3.8.51` `check:agent-skills-sync` base-red: regenerate `skills/cli-tunnel/SKILL.md` so the `tunnel create [type]` positional that #13009 taught the generator to read is reflected in the committed skill. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` typecheck base-red from #12731: the new `Mode pack ranking gates` candidates in `open-sse/services/autoCombo/__tests__/autoCombo.test.ts` omitted the required `provider`, `model` and `errorRate` fields of `ProviderCandidate`, failing `check:open-sse-typecheck` (and with it `Fast Quality Gates`) on every open PR. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` docs-sync base-red: EURouter (#13025) and GreenPT (#13024) took the live provider count to 358, leaving 7 STRICT drifts (`PROVIDER_REFERENCE.md`, 4 diagrams, 2 tier-flow images) plus the `AGENTS.md` / `llm.txt` / `package.json` count claims stale. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- **test(models):** Pin `CATALOG_BUILD_TIMEOUT_MS` in the #12058 canonical-catalog regression so a cold catalog build racing the 8s default bound no longer flakes the suite with a 500 ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Allow `@omniroute/opencode-plugin-v2/` in the npm pack policy — [#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870) shipped the v2 plugin beside its v1 sibling without widening the allowlist, so every packed file under it read as an unexpected artifact and failed `check:pack-policy` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Point the release-green validator's pack gate at the tree it is validating — the build-provenance guard defaulted to `origin/main`, which a release branch tip can never be an ancestor of mid-cycle, so `Package artifact` reported a false red on every run and the tarball boot-smoke cascaded off it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **docs(checks):** keep doc counts honest — headings, rankings, catalog, weights, quality gate and scoring diagram now covered ([#12507](https://github.com/diegosouzapw/OmniRoute/pull/12507)) — thanks @maxmad64bis +- **chore(lifecycle):** `check:model-lifecycle` now also diffs `DEFAULT_DEGRADATION_MAP` (the background-task redirect table) against the vendor lifecycle snapshot, refusing a retired id as source or target, with a table-driven unit test beside it. Three rows whose source the vendor had retired — `claude-sonnet-4-20250514`, `gemini-3-pro-preview` and `gpt-5.1-codex` (whose target `gpt-5.1-codex-mini` is retired too) — were dead code, since `checkLifecycle` answers 410 before the redirect runs; they are dropped (#12535 — thanks @pacocartones) +- **test(compression):** cover idle worker eviction at the resource level — the pool must call `terminate()` and must not retain the worker's `MessagePort`, complementing the `exit`-event assertion added with the fix ([#13371](https://github.com/diegosouzapw/OmniRoute/pull/13371)) +- **chore(quality):** raise the `imageGeneration.ts` file-size ceiling for the image-only-model guard that clears the #12945 base-red ([#13392](https://github.com/diegosouzapw/OmniRoute/pull/13392)) +- **chore(quality):** type the combo-test route's JSON response bodies instead of casting them to `any`, drop the now-empty suppression entry, and remove the `hasPerModelQuota` import `chatCore.ts` stopped using when the failure-classification helper was extracted ([#13379](https://github.com/diegosouzapw/OmniRoute/pull/13379)) +- **chore(electron):** the desktop bundle no longer ships the root-level files of every translated docs mirror (`README.md`, `llm.txt`, `CLAUDE.md`, `GEMINI.md`, `CONTRIBUTING.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`) — the packaged app only reads `docs/i18n//docs/**`, which stays. Saves ~11 MB on top of the translated CHANGELOGs already pruned. (#0000) ([#13235](https://github.com/diegosouzapw/OmniRoute/pull/13235)) +- **chore(quality):** retighten the `src/sse/handlers/chat.ts` unused-vars suppression count to the 8 violations that actually remain ([#13385](https://github.com/diegosouzapw/OmniRoute/pull/13385)) +- Bring the documented counts back in line with the code: 171 migrations (was 169) and 20 routing strategies (was 19), plus the regenerated `cli-tunnel` skill reference. ([#12970](https://github.com/diegosouzapw/OmniRoute/pull/12970)) +- Update the documented migration count to 172 after the call-logs provider-stats indexes landed. ([#13209](https://github.com/diegosouzapw/OmniRoute/pull/13209)) +- **chore(ci):** two gates that close the blind spots behind the exclusions above. `check:vitest-exclusions` requires every Vitest exclusion to name a tracking issue and to appear in `config/quality/vitest-exclusions.json` — the previous list grew to 62 files behind a comment pointing at an issue that had been closed for a month. `check-new-key-coverage` requires a key newly added to `en.json` to reach every locale; the existing coverage gate is a percentage floor per locale, so eleven absent keys out of ~13,000 left it at 99.9% while a whole feature shipped untranslated in nine languages. Both are diff-aware, so pre-existing debt stays frozen and neither needed a migration to turn on. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **build:** root layout no longer loads Inter from `next/font/google`, so a production image build does not need fonts.googleapis.com ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **docs(i18n):** bring the eight new `llm.txt` mirrors (km, kn, ml, my, ne, or, pa, si) up to the current provider and migration counts, so `check-docs-sync` stops rejecting every commit ([#13674](https://github.com/diegosouzapw/OmniRoute/pull/13674)) +- **refactor(ui):** flow surfaces (home topology, combo live studio, compression cockpit/waterfall/nodes, token-health badge) express state through the theme-aware `--orch-status-*` tokens instead of fixed dark-mode hex, so green/red/amber/grey stay legible in the light theme; dark mode is byte-identical. Categorical palettes (routing-strategy hues, compression-layer pills, provider brand colors) deliberately stay hex ([#12378](https://github.com/diegosouzapw/OmniRoute/issues/12378)) ([#13203](https://github.com/diegosouzapw/OmniRoute/pull/13203)) +- **chore(tests):** 51 test files that had been excluded from Vitest are running again, restoring roughly 350 assertions to the blocking suite. Each was measured individually first: of the 62 files parked behind the `// #8618 — pre-existing failure` comment, 51 pass against the current tree with no source change, so the exclusions had outlived the failures they were added for. The 11 that genuinely still fail stay excluded, but now point at a live tracker (#13204) rather than at #8618, which was closed in August while the list it tracked kept growing. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **chore(quality):** drop two ESLint suppression entries whose violations no longer exist, so `eslint --suppressions-location` stops rejecting every commit that touches the surrounding files ([#13384](https://github.com/diegosouzapw/OmniRoute/pull/13384)) +- **chore(release):** bring main's post-tag fixes into release/v3.8.51 — sync-back step 2/2 (direct commit `21c488f210`) +- **chore(deps):** patch toml and esbuild in the opencode plugin lockfiles ([#12965](https://github.com/diegosouzapw/OmniRoute/pull/12965)) +- **docs(changelog):** reconcile the v3.8.51 living section — fold 366 fragments, cover every cycle commit, credit every contributor ([#12971](https://github.com/diegosouzapw/OmniRoute/pull/12971)) +- **test(plugin):** wait for the v2 lazy-refresh tier instead of sleeping past it ([#12984](https://github.com/diegosouzapw/OmniRoute/pull/12984)) +- **chore(quality):** rebaseline chatCore.ts after the non-streaming regression fixes ([#13045](https://github.com/diegosouzapw/OmniRoute/pull/13045)) +- **docs(changelog):** add the missing bullet marker to the DeepSeek PoW fragment ([#13200](https://github.com/diegosouzapw/OmniRoute/pull/13200)) +- **chore(deps):** drain the Dependabot queue — 10 of 13 alerts ([#13213](https://github.com/diegosouzapw/OmniRoute/pull/13213)) +- **chore(ci):** make worktrees carry the hooks that enforce the identity gate ([#13236](https://github.com/diegosouzapw/OmniRoute/pull/13236)) +- **chore(deps):** pin csv-parse to ^7.0.2 via overrides (#13117) ([#13300](https://github.com/diegosouzapw/OmniRoute/pull/13300)) — thanks @KooshaPari +- **chore(deps):** bump hono from ^4.12.34 to ^4.13.7 (#13148) ([#13301](https://github.com/diegosouzapw/OmniRoute/pull/13301)) — thanks @KooshaPari +- **chore(deps):** pin joi to ^18.2.8 via overrides (#13085) ([#13302](https://github.com/diegosouzapw/OmniRoute/pull/13302)) — thanks @KooshaPari +- **docs(providers):** add Microsoft 365 Copilot (BizChat) provider guide (#12779) ([#13340](https://github.com/diegosouzapw/OmniRoute/pull/13340)) — thanks @KooshaPari +### 🙌 Contributors + +Thanks to everyone whose work landed in v3.8.51: + +| Contributor | PRs / Issues | +| --- | --- | +| [@5dive-bot](https://github.com/5dive-bot) | #11852 | +| [@abhisheksharma2411](https://github.com/abhisheksharma2411) | #11547, #11684, #11959, #12209 | +| [@adevwithpurpose](https://github.com/adevwithpurpose) | #11464 | +| [@adivekar-utexas](https://github.com/adivekar-utexas) | #12015, #12043, #12090, #12138 | +| [@AIB1TAL0S](https://github.com/AIB1TAL0S) | #12103 | +| [@alltomatos](https://github.com/alltomatos) | #11448 | +| [@alvinveroy](https://github.com/alvinveroy) | #11857, #12027, #12165 | +| [@amaleta](https://github.com/amaleta) | #11906, #11934 | +| [@amartinawi](https://github.com/amartinawi) | #11575 | +| [@AndrianBalanescu](https://github.com/AndrianBalanescu) | #11083, #11830, #11834, #11844 | +| [@anhtahaylove](https://github.com/anhtahaylove) | #13091, #13092, #13093, #13096, #13097, #13100, #13106, #13109, #13114, #13155, #13166, #13171, #13175, #13187, #13196 | +| [@aniruddhaadak80](https://github.com/aniruddhaadak80) | #11760 | +| [@app](https://github.com/app) | #12554 | +| [@arminanton](https://github.com/arminanton) | #11461, #11513 | +| [@AStupidBear](https://github.com/AStupidBear) | #11584 | +| [@atescivitci-cmd](https://github.com/atescivitci-cmd) | #12105 | +| [@b3nw](https://github.com/b3nw) | #11970, #11971, #12147 | +| [@backryun](https://github.com/backryun) | #11259, #11950, #12075, #12076, #12078, #12079, #12081, #12082, #12181, #12228, #12239, #12255, #12258, #12277, #12367, #12423, #12524, #12538 | +| [@benjaminkitt](https://github.com/benjaminkitt) | #11747 | +| [@benzntech](https://github.com/benzntech) | #11614, #11615 | +| [@Bl0ck154](https://github.com/Bl0ck154) | #11948, #11951, #11952, #11953, #11954 | +| [@brick30llc-ctrl](https://github.com/brick30llc-ctrl) | #11752, #12242, #12262 | +| [@Chewji9875](https://github.com/Chewji9875) | #11455, #11875, #12028 | +| [@cheynetom](https://github.com/cheynetom) | #12058 | +| [@davidebaraldo](https://github.com/davidebaraldo) | #12222, #12304 | +| [@davidlinfr](https://github.com/davidlinfr) | #6390 | +| [@ddarkr](https://github.com/ddarkr) | #12124 | +| [@Deftera186](https://github.com/Deftera186) | #11809 | +| [@dpozimski](https://github.com/dpozimski) | #11945 | +| [@DW-MediaLab](https://github.com/DW-MediaLab) | #12229 | +| [@echel0nn](https://github.com/echel0nn) | #11923 | +| [@f9td56dbgh-hub](https://github.com/f9td56dbgh-hub) | #11660 | +| [@fabioluissilva](https://github.com/fabioluissilva) | #11991 | +| [@foreveryh](https://github.com/foreveryh) | #12177 | +| [@geek007git](https://github.com/geek007git) | #12115, #12116, #12117, #12120, #12122 | +| [@geekyNads](https://github.com/geekyNads) | #11784, #12266 | +| [@ggiak](https://github.com/ggiak) | #11561, #12276, #12350, #12402 | +| [@gonisulaimann](https://github.com/gonisulaimann) | #12368, #12369, #12371 | +| [@Gorillaz322](https://github.com/Gorillaz322) | #12207 | +| [@groovecityJO](https://github.com/groovecityJO) | #12682 | +| [@hartmark](https://github.com/hartmark) | #11434, #11452, #11473, #11499, #11703, #11983, #11984, #11985, #11986, #11988, #11989, #11990, #11994, #12221, #12293, #12445, #12446, #12447, #12448, #12460, #12461, #12623, #12646, #12650, #12680, #12717, #12718, #12727, #12741, #12854 | +| [@hizzt](https://github.com/hizzt) | #11894 | +| [@hongnoul](https://github.com/hongnoul) | #11484 | +| [@HouMinXi](https://github.com/HouMinXi) | #11411, #11414, #11512, #11518, #11520, #11641, #11642, #11643, #11687, #11779, #11849, #11850, #11851, #11915, #11916, #11918, #11919, #11920, #12013, #12017, #12033, #12042, #12106, #12139, #12166, #12169, #12171, #12205, #12213, #12312, #12325, #12487, #12488, #12495, #12504, #12557, #12566, #12590, #12591, #12624, #12626, #12632, #12637, #12678, #12696, #12697, #12711, #12733, #12746, #12767, #12770, #12789, #12803, #12805, #12811, #12866, #12868, #12899, #12926, #12934, #12950, #12951, #12974, #13001, #13006, #13011, #13017, #13026, #13027, #13034, #13035, #13038, #13042, #13050, #13060, #13061, #13069, #13107, #13120, #13136, #13195, #13197 | +| [@Hsia97](https://github.com/Hsia97) | #11624 | +| [@jacobsparts](https://github.com/jacobsparts) | #11854, #12155, #12167 | +| [@joglomedia](https://github.com/joglomedia) | #11980 | +| [@jonlwheat2-gif](https://github.com/jonlwheat2-gif) | #11443, #11450, #11549, #11567, #11608, #11633, #11635, #11640, #11644, #11671, #11672, #11673, #11674, #11675, #11676, #11677, #11706, #11783, #12051, #12052, #12053, #12055 | +| [@kanade-hoshino](https://github.com/kanade-hoshino) | #12180 | +| [@Karan825](https://github.com/Karan825) | #11861 | +| [@KaspaPulse](https://github.com/KaspaPulse) | #11389, #11469, #11628, #11666, #11910 | +| [@keeltrace](https://github.com/keeltrace) | #12080, #12223 | +| [@killer30001000](https://github.com/killer30001000) | #12178 | +| [@KooshaPari](https://github.com/KooshaPari) | #12470, #12592, #12667, #12699, #12703, #12706, #12764, #12769, #12771, #13300, #13301, #13302, #13340, #13401, #13403, #13404, #13405, #13406, #13407, #13409, #13410, #13411, #13412, #13413, #13414, #13418, #13424, #13427, #13433, #13523, #13524, #13525, #13528, #13531, #13532, #13534, #13539, #13543, #13545, #13546, #13547, #13550, #13553 | +| [@kriptoburak](https://github.com/kriptoburak) | #11370 | +| [@KrzysiekSko](https://github.com/KrzysiekSko) | #12425, #12432, #12673 | +| [@marcelokarval](https://github.com/marcelokarval) | direct commit / report | +| [@maxmad64bis](https://github.com/maxmad64bis) | #1622, #11435, #11437, #11441, #11537, #11550, #11553, #11555, #11557, #11812, #11842, #11843, #11903, #12151, #12214, #12215, #12218, #12226, #12314, #12316, #12317, #12318, #12319, #12320, #12321, #12507, #12715, #12731, #12744, #12786, #12787, #12788, #12790, #12792, #12794, #12795, #12828, #12832, #12853, #12857, #12870, #12937, #12941, #12975, #13141, #13142, #13146, #13147 | +| [@mdigitalbh81](https://github.com/mdigitalbh81) | #12240 | +| [@Meet6338-X](https://github.com/Meet6338-X) | #11598, #11609, #12110 | +| [@morpheus9393](https://github.com/morpheus9393) | #11943 | +| [@MumuTW](https://github.com/MumuTW) | #11492, #11502, #11506, #11507, #11626, #11675, #11685, #11728, #11746, #11888, #11889, #11890, #11892 | +| [@Neuron-Mr-White](https://github.com/Neuron-Mr-White) | #11622, #11801, #12256 | +| [@NightStalker-87](https://github.com/NightStalker-87) | #12183 | +| [@NoxzRCW](https://github.com/NoxzRCW) | #11879, #11880, #11881, #11882, #11883, #11935 | +| [@ntdat812](https://github.com/ntdat812) | #11585, #12095 | +| [@ntdatt812](https://github.com/ntdatt812) | #11368, #11573, #11574, #11576, #11577, #11580, #11582, #11583, #11588, #11589, #11590, #11591, #11592, #11593, #11672, #12177, #12180, #12873, #12918, #12920, #12921, #12925, #12930, #13007, #13009, #13024, #13025, #13083, #13087, #13101, #13104, #13110 | +| [@official-burak](https://github.com/official-burak) | #11542 | +| [@opensource-elearning](https://github.com/opensource-elearning) | #12179, #12189, #12278, #12286 | +| [@oyi77](https://github.com/oyi77) | #11408, #11409, #11421, #11505, #11677, #12036, #12110 | +| [@pacocartones](https://github.com/pacocartones) | #11521, #11522, #11527, #11528, #11529, #11530, #11531, #11532, #11533, #11534, #11595, #11599, #11603, #11604, #11605, #11607, #11610, #11676, #11714, #11716, #11718, #11767, #11838, #11860, #11862, #11869, #11871, #11872, #11873, #11903, #11906, #11921, #11934, #12356, #12358, #12359, #12360, #12361, #12362, #12364, #12365, #12373, #12374, #12375, #12376, #12377, #12379, #12380, #12381, #12386, #12387, #12389, #12390, #12394, #12395, #12397, #12401, #12403, #12404, #12522, #12523, #12535, #12536, #12540, #12541, #12543, #12545, #12548, #12549, #12550, #12551, #12552, #12647, #12651, #12653, #13055 | +| [@patrykkopycinski](https://github.com/patrykkopycinski) | #11936, #11937, #12224 | +| [@PauloFH](https://github.com/PauloFH) | #11509 | +| [@PauloHSOliveira](https://github.com/PauloHSOliveira) | #12241 | +| [@ponkcore](https://github.com/ponkcore) | #12054 | +| [@Prajeeth-12](https://github.com/Prajeeth-12) | #11634, #12046 | +| [@quiterunner-commits](https://github.com/quiterunner-commits) | #12131, #12143 | +| [@rafacpti23](https://github.com/rafacpti23) | #11554, #11558, #12192, #12197, #12198, #12202, #12203, #12204 | +| [@ragnar-claude](https://github.com/ragnar-claude) | #11564 | +| [@raheemuddin786](https://github.com/raheemuddin786) | #11491, #11839, #11840, #11841, #12003, #12230, #12231, #12232, #12233, #12234 | +| [@rahilmavani](https://github.com/rahilmavani) | #11761 | +| [@Rahulsharma0810](https://github.com/Rahulsharma0810) | #11771 | +| [@RaviTharuma](https://github.com/RaviTharuma) | #11710, #11727, #11797, #11798, #11802, #11805, #11806, #11811, #12098, #12099, #12101, #12449, #12452, #12472, #12473, #12493, #12533, #12607, #12628, #12631, #12636, #12875, #12876, #12880, #12882, #12884 | +| [@rezjalibd](https://github.com/rezjalibd) | #12186 | +| [@RhianB14](https://github.com/RhianB14) | #11692 | +| [@rifqiawl](https://github.com/rifqiawl) | #11517, #11519, #11969 | +| [@rqzbeh](https://github.com/rqzbeh) | #11390 | +| [@Sabeekhann](https://github.com/Sabeekhann) | #11699, #11705 | +| [@santosraju99-hub](https://github.com/santosraju99-hub) | #11755, #11770 | +| [@Seramicx](https://github.com/Seramicx) | #11568 | +| [@Siva010](https://github.com/Siva010) | #12191 | +| [@solstxce](https://github.com/solstxce) | #11597 | +| [@soroush5](https://github.com/soroush5) | #12691 | +| [@tenshiak](https://github.com/tenshiak) | #12279 | +| [@TheDemonTuan](https://github.com/TheDemonTuan) | #11468, #11470, #11471, #11482, #11548, #11758, #11775, #11814 | +| [@thomasmaerz](https://github.com/thomasmaerz) | #12834 | +| [@tuandinh0801](https://github.com/tuandinh0801) | #11454 | +| [@turbolego](https://github.com/turbolego) | #11621, #11762, #11772, #11774, #11781, #12216 | +| [@Tushar49](https://github.com/Tushar49) | #11392 | +| [@ujjawalkaushik1110](https://github.com/ujjawalkaushik1110) | #11863 | +| [@vermasomesh835](https://github.com/vermasomesh835) | #11794 | +| [@vsd2807](https://github.com/vsd2807) | #11565, #11619 | +| [@wahidsadik371-coder](https://github.com/wahidsadik371-coder) | #12126 | +| [@watchingdogs](https://github.com/watchingdogs) | #12031 | +| [@wildcard](https://github.com/wildcard) | #11620 | +| [@xiaoyaner0201](https://github.com/xiaoyaner0201) | #11460, #11662, #12083 | +| [@Xxx91n](https://github.com/Xxx91n) | #11690 | +| [@yourspraveen](https://github.com/yourspraveen) | #11146 | +| [@yxyxy](https://github.com/yxyxy) | #11671 | +| [@ZaimMarzuki](https://github.com/ZaimMarzuki) | #11960 | +| [@Zartharas](https://github.com/Zartharas) | #11340 | +| [@zero-executioner](https://github.com/zero-executioner) | #11631 | +| [@diegosouzapw](https://github.com/diegosouzapw) | maintainer | + +--- ## [3.8.50] — 2026-08-25 _Living section — regenerated 2026-08-12 from all cycle commits (cycle open `ed2db6cb19` → tip). Bullets carry the merged PR and its author; direct pushes listed separately._ diff --git a/docs/i18n/zh-CN/CHANGELOG.md b/docs/i18n/zh-CN/CHANGELOG.md index 927df595d3..46be2739ce 100644 --- a/docs/i18n/zh-CN/CHANGELOG.md +++ b/docs/i18n/zh-CN/CHANGELOG.md @@ -8,16 +8,1119 @@ ## [3.8.51] — TBD -_Living section — cycle opened at the v3.8.50 freeze (parallel-cycle model). Bullets are aggregated from `changelog.d/` fragments at each `/generate-release` phase._ +_Living section — reconciled 2026-09-15 from all cycle commits (`091589089c` → `c0f92ec98a`, 916 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 | **123** | +| 📝 Commits in the cycle | **916** | +| 🔀 Pull requests referenced | **892** | +| 📋 Changelog entries | **919** | +| 🙌 Contributors credited in entries | **121** | +| 🤖 Automated dependency commits | 18 | + +**Entries by type** + +| Type | Count | +| --- | ---: | +| 🐛 Fixes | 592 | +| ✨ Features | 131 | +| 🧹 Chore | 93 | +| 📚 Docs | 42 | +| 🧪 Tests | 32 | +| ♻️ Refactor | 8 | +| 🏗️ Build | 7 | +| ⚡ Performance | 7 | +| ⚙️ CI | 2 | +| ⏪ Reverts | 2 | +| 📦 Dependencies | 2 | +| 🔒 Security | 1 | + +### 🏆 Top 25 contributors this cycle + +_By commits in `091589089c..c0f92ec98a`, author identities consolidated via `.mailmap` and the merged PR's GitHub login. Bots excluded._ + +| # | Contributor | Commits | +| ---: | --- | ---: | +| 🥇 | diegosouzapw | 323 | +| 🥈 | Bob.Hou (@HouMinXi) | 79 | +| 🥉 | Paco Cartones (@pacocartones) | 77 | +| 4 | Dizzle (@maxmad64bis) | 49 | +| 5 | Koosha Paridehpour (@KooshaPari) | 43 | +| 6 | Nguyen Thanh Dat (@ntdatt812) | 31 | +| 7 | Ravi Tharuma (@RaviTharuma) | 26 | +| 8 | Markus Hartung (@hartmark) | 22 | +| 9 | Webman (@jonlwheat2-gif) | 22 | +| 10 | backryun | 17 | +| 11 | anhtahaylove | 15 | +| 12 | Rafa Martins (@rafacpti23) | 10 | +| 13 | Syed Raheemuddin (@raheemuddin786) | 10 | +| 14 | Nguyễn Viết Tuấn (@TheDemonTuan) | 8 | +| 15 | MumuTW | 7 | +| 16 | Paijo (@oyi77) | 7 | +| 17 | Tobias Andersen (@turbolego) | 6 | +| 18 | Bl0ck (@Bl0ck154) | 5 | +| 19 | SHANMUGAPRIYAN (@geek007git) | 5 | +| 20 | KaspaPulse | 5 | +| 21 | Abhishek Sharma (@abhisheksharma2411) | 4 | +| 22 | Abhishek Divekar (@adivekar-utexas) | 4 | +| 23 | Andrew B. (@AndrianBalanescu) | 4 | +| 24 | NoxzRCW | 4 | +| 25 | opensource-elearning | 4 | ### ✨ New Features +- **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). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- Added Google AI Studio Gemini batch text-to-speech support through `POST /v1/audio/speech`. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- 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. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(routing):** subscription-first auto groupings — `auto/subscription` routes only through plan-included connections with a documented hard-stop overage and fails closed on exhaustion, while `auto/thrifty` orders the pool `subscription → keyless → free → cheap → premium` and steps up one rung at a time as each is exhausted. Billing class comes from a curated per-connection catalog (uncurated is treated as metered, never plan-included), both reuse STRICT_ZERO_COST's per-connection verification, and a quota reading whose `resetAt` has passed is now refreshed regardless of TTL so routing returns to plan capacity as soon as it resets ([#11146](https://github.com/diegosouzapw/OmniRoute/pull/11146)) — thanks @yourspraveen +- **feat(providers):** publish a management-authenticated versioned web-session credential contract from OmniRoute's canonical browser credential metadata ([#11340](https://github.com/diegosouzapw/OmniRoute/pull/11340)) — thanks @Zartharas +- **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)) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add Xquik X search with typed results, credential validation, REST routing, and MCP selection ([#11370](https://github.com/diegosouzapw/OmniRoute/pull/11370)) — thanks @kriptoburak +- **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)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(dashboard):** surface durable exclusive managed leases in the existing Sessions view, keeping leased clients visible across idle gaps while marking connections with in-flight work as active ([#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389)) — thanks @KaspaPulse +- **build(bun):** allow Turbopack bundler flag on Bun 1.4+ with configurable Webpack fallback ([#11471](https://github.com/diegosouzapw/OmniRoute/pull/11471)) — thanks @TheDemonTuan +- feat(api): add an opt-in `modelVisibilityAllowlist`/`modelVisibilityDenylist` settings pair to curate exactly which models `/v1/models` advertises, mirrored into every `auto/*` combo candidate pool so a denied model cannot be routed to via combo selection either (#11481) ([#11997](https://github.com/diegosouzapw/OmniRoute/pull/11997)) +- **feat(rankings):** the Free Provider Rankings page shows what each provider actually served over the last 24 h. It ranked by ELO alone, which left a provider that answers every call with an error in first place; the usage data was already served by the API but never requested. A provider with too small a sample shows a dash, not a number ([#11546](https://github.com/diegosouzapw/OmniRoute/pull/11546)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **feat(guardrails):** enforce a bounded, deterministic contract for Video Bridge transcripts — 256 cues, 4096 input code units and 4 KiB UTF-8 per cue, 64 KiB total text, malformed-Unicode rejection, focus-window scoping, cross-source reconciliation with contributing-source metadata, and a structural provenance trust boundary so caller JSON can never self-assert `embedded`/`audio-bridge` provenance ([#11652](https://github.com/diegosouzapw/OmniRoute/issues/11652)) ([#12009](https://github.com/diegosouzapw/OmniRoute/pull/12009)) +- **feat(video):** orchestrate optional Video Bridge audio extraction and Audio Bridge STT behind a dual opt-in (operator setting AND per-request signal) — a new loopback-only broker `mode=audio` operation shares the frame path's exact process queue, deadline, AbortSignal, and byte budgets to extract a bounded mono 16 kHz PCM WAV from the same already-downloaded video, then reuses the existing Audio Bridge transcription boundary; provider segment timing is preserved when available and marked coarse otherwise, and every failure degrades to a visual-only-safe partial instead of throwing (#11654). ([#12012](https://github.com/diegosouzapw/OmniRoute/pull/12012)) +- Add a tenant-bound Video Bridge drill-down lifecycle on top of the existing secure cache substrate: opaque hashed handles (never raw session/video identifiers), preview/standard/detail multiresolution variants resampled on read, response pagination capped at 8 frames and 32 MiB, and a new authenticated `/api/v1/video-bridge/drilldown` consumer route that stays disabled for remote access by default and denies cross-key access with the same response as a nonexistent handle (no existence oracle). ([#12006](https://github.com/diegosouzapw/OmniRoute/pull/12006)) +- **test(video):** Add the Video Bridge FU-07/FU-09 promotion-evidence harness (#11656) — a frozen Zod manifest schema covering the 8 required scenario kinds (static scenes, rapid cuts, late facts, fades, blur, small text, close events, visual prompt injection) with a minimum of 3 repetitions per case, deterministic declarative fixture recipes (`videoBridgePromotionFixtures.ts`), a pure medians/p95 metrics aggregator, a pure FU-07/FU-09 promotion-verdict evaluator applying the ticket's exact thresholds (missing token usage always holds), a digest-only persistence layer that never retains raw media or raw model responses, and a versioned per-model promotion allowlist shipped empty with every model defaulting to `hold`. The FU-07/FU-09 promotion verdicts themselves remain HOLD — they require a real evidence run against real models on VPS 192.168.0.15. ([#12008](https://github.com/diegosouzapw/OmniRoute/pull/12008)) +- **feat(video bridge):** "embedded" transcript provenance can now be legitimately earned instead of merely asserted — a bounded, allowlisted (`mov_text`/`subrip`/`webvtt`) subtitle probe runs through the loopback-only Video Bridge broker (at most 2 streams, 10s subdeadline bounded by the request deadline, 256 KiB output, 4096-code-unit lines), normalized through a bounded, ReDoS-safe WebVTT parser and Zod-validated end to end. The adapter always resolves to an explicit `success`/`absent`/`transient_failure` outcome — a subtitle failure never breaks the visual description path, and only a fingerprint-verified broker response (never a caller-declared label) can produce embedded cues (#11659). ([#12011](https://github.com/diegosouzapw/OmniRoute/pull/12011)) +- Default new Antigravity-family connections (agy CLI imports and Antigravity OAuth connects) to model auto-sync, so live model discovery lands in the synced catalog and `/v1/models` picks up freshly released upstream models (e.g. Gemini 3.7 Flash tiers) without code changes. Existing connections keep their current setting; the per-connection dashboard toggle remains the opt-out. (#11685 — thanks @MumuTW) ([#11887](https://github.com/diegosouzapw/OmniRoute/pull/11887)) +- **feat(ui):** enable React Compiler (`reactCompiler: true` + `babel-plugin-react-compiler`) for automatic memoization at build time ([#11783](https://github.com/diegosouzapw/OmniRoute/pull/11783)) — thanks @jonlwheat2-gif +- **feat(providers):** add SeekAi (`seekai.cc`) as an OpenAI-compatible New-API gateway — catalog id `seekai` (alias `ska`), `https://seekai.cc/v1`, live `/v1/models` via `passthroughModels`, aggregator-list membership so New-API balance detection can opt in. No referral/aff codes. ([#11786](https://github.com/diegosouzapw/OmniRoute/issues/11786)) ([#12557](https://github.com/diegosouzapw/OmniRoute/pull/12557)) — thanks @HouMinXi +- **feat(zai):** add GLM-5.3-Flash Coding Plan support (1M context, 128K output, vision, `low|high|max` reasoning) and route `zai` GLM-5.3-family API-key traffic through the OpenAI-compatible Coding Plan endpoint with native thinking defaults ([#11801](https://github.com/diegosouzapw/OmniRoute/pull/11801)) — thanks @Neuron-Mr-White +- **feat(combo):** choose how combo models are ordered — manual, provider, score, or name — via a sort control in the dashboard builder, persisted in `config.modelSort` and re-applied on load and after add ([#11812](https://github.com/diegosouzapw/OmniRoute/pull/11812)) — thanks @maxmad64bis +- **feat(free):** custom models can be marked free-tier via `customModels[].isFree`; `isFreeModel()` is the first door and `hidePaidModels` respects it even for providers outside the free budget ([#11843](https://github.com/diegosouzapw/OmniRoute/pull/11843)) — thanks @maxmad64bis +- **feat(nodejs):** add `5dive` as a `configure` target — `omniroute configure 5dive` / `omniroute setup-5dive` write a 5dive auth profile that points an agent fleet's `claude` seats at OmniRoute, with the root-only write, the loopback-vs-`https` endpoint rule and the per-seat model pin handled explicitly ([#11852](https://github.com/diegosouzapw/OmniRoute/pull/11852)) — thanks @5dive-bot +- **feat(sse):** treat `max` as a first-class reasoning-effort tier and clamp per model family (GLM 5.1+/DeepSeek V4+/Kimi K3+ keep native `max`; o1/MiniMax/Grok/Muse Spark clamp to their upstream ceiling) ([#11875](https://github.com/diegosouzapw/OmniRoute/pull/11875)) — thanks @Chewji9875 +- **feat(providers):** the provider plugin manifest now advertises a `usage-fetch` capability for the 40 providers that have a wired usage/quota fetcher, so external dashboards can read it from `GET /api/v1/provider-plugin-manifest` instead of parsing `open-sse/services/usage.ts` after every release. Discovery only — no new fetcher, no quota change, and the Dashboard quota widget stays gated by `USAGE_SUPPORTED_PROVIDERS`. `USAGE_FETCHER_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/fetcherProviders.ts`) and is re-exported from `services/usage.ts`, keeping the manifest module a light leaf instead of pulling the ~490-module usage dispatcher into the manifest route. ([#11903](https://github.com/diegosouzapw/OmniRoute/pull/11903)) — thanks @maxmad64bis / @pacocartones +- **feat(plugins):** `OMNIROUTE_PLUGINS_DIR` sets the directory the runtime plugin scanner reads — and the root the plugin manager installs into — overriding the `HOME`-derived default, so a Docker/K8s deployment can point straight at its bind-mounted plugin tree instead of moving `HOME` just to relocate the scan path. An image that exports no home no longer scans `/tmp/.omniroute/plugins` in silence: the resolved directory is logged once at startup as `scanner.dir_resolved`, naming the input that won. Unset, behaviour is unchanged. Distinct from the CLI-only `OMNIROUTE_PLUGIN_PATH`, which finds `omniroute-cmd-*` command packages and never reached this scanner ([#11906](https://github.com/diegosouzapw/OmniRoute/pull/11906)) — thanks @amaleta / @pacocartones +- **feat(leases):** add an explicit owner-authenticated status action that returns only the active lease's privacy-safe configured connection and provider labels, with generation fencing and no credential or internal-id disclosure ([#11910](https://github.com/diegosouzapw/OmniRoute/pull/11910)) — thanks @KaspaPulse +- **feat(providers):** import-from-file modal shows per-row API errors and ships a downloadable CSV template ([#12071](https://github.com/diegosouzapw/OmniRoute/issues/12071)) ([#12504](https://github.com/diegosouzapw/OmniRoute/pull/12504)) — thanks @HouMinXi +- **feat(providers):** dashboard search matches connection name and `baseUrl` so imported OpenAI-compat nodes surface on the provider card ([#12108](https://github.com/diegosouzapw/OmniRoute/issues/12108)) ([#12495](https://github.com/diegosouzapw/OmniRoute/pull/12495)) — thanks @HouMinXi +- **feat(routing):** add a `score` Auto router strategy that selects the highest configured weighted score and reuses `explorationRate`. ([#12155](https://github.com/diegosouzapw/OmniRoute/pull/12155)) — thanks @jacobsparts +- Add a runtime feature flag to disable universal context handoffs globally without changing the default behavior. ([#12167](https://github.com/diegosouzapw/OmniRoute/pull/12167)) — thanks @jacobsparts +- **feat(providers):** the provider plugin manifest now also advertises a `usage-supported` capability for the 46 providers whose usage API is accepted by the server and Dashboard routes, so integrators can distinguish "the server will serve quota for this provider" from "a fetcher is wired" without reading TypeScript. Discovery only — no fetcher or quota change. `usage-fetch` resolves on id or alias (the usage dispatcher accepts both); `usage-supported` resolves on id alone, matching the runtime guard `USAGE_SUPPORTED_PROVIDERS.includes(providerId)`. `USAGE_SUPPORTED_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/supportedProviders.ts`) and is re-exported from `providers.ts`, mirroring the `fetcherProviders` leaf from #11903 and keeping the manifest a light module. ([#12214](https://github.com/diegosouzapw/OmniRoute/pull/12214)) — thanks @maxmad64bis +- **feat(rankings):** order Free Provider Rankings by what each provider actually served — `GET /api/free-provider-rankings?sortBy=reliability` and a "Most reliable first" toggle on the page. Providers with too few calls to state a success rate keep their score order below the measured ones; the default order is unchanged ([#12218](https://github.com/diegosouzapw/OmniRoute/pull/12218)). — thanks @maxmad64bis +- **perf(sse):** defer `cloneLogPayload()` in the structured SSE collector until after the `maxEvents`/`maxBytes` cap check, eliminating ~9,800 wasted `structuredClone` calls per streaming response (65–71% faster `push()`). Reducer snapshot isolation restored for OpenAI and Responses summaries ([#12241](https://github.com/diegosouzapw/OmniRoute/pull/12241)) — thanks @PauloHSOliveira ([#12243](https://github.com/diegosouzapw/OmniRoute/pull/12243)) +- **feat(usage):** Devin CLI agentic quota (Codeium seat-management GetUserStatus) and OpenRouter key limits plus account credits now surface in Provider Limits ([#12256](https://github.com/diegosouzapw/OmniRoute/pull/12256) — thanks @Neuron-Mr-White) +- **feat(settings):** persist `headroomUrl` through Settings so status/start use the operator URL instead of only `HEADROOM_URL` ([#12306](https://github.com/diegosouzapw/OmniRoute/issues/12306)) ([#12487](https://github.com/diegosouzapw/OmniRoute/pull/12487)) — thanks @HouMinXi +- **feat(auto-combo):** Auto-Combo scoring can now weigh how often a provider/model has actually succeeded. The engine already carried that number on every candidate — 24 hours of usage history behind a ten-sample floor, real-time metrics otherwise — and the scoring function never read it, while the weight table described `stability` as if it did. `reliability` (`1 - failureRate`, with the same field precedence and the same rate-bounding the speed ranking already uses, so a corrupt reading means "nothing observed" rather than "fails every call") is now a declared factor shipping at weight `0`, so routing is unchanged until an operator gives it one, and the `stability` description now matches what that factor computes ([#12317](https://github.com/diegosouzapw/OmniRoute/pull/12317)) — thanks @maxmad64bis +- **feat(routing):** With `freeAccessPolicy: "strict"`, the read-only candidate listing (`GET /v1/auto-combo/{channel}/candidates`) no longer hides the candidates the zero-cost guard excludes — the same read-only transparency the resilience filter already honours (#9133). Each candidate now carries `freeAccessExclusion` saying why it would be kept out, and it tells an exhausted allowance apart from a quota reading that never arrived or went stale, which used to look identical from the outside. Routing is unchanged: the listing reports, it never enforces. The separate `excludeTosAvoid` guard still drops its candidates without a reason; that gap is now documented rather than closed ([#12319](https://github.com/diegosouzapw/OmniRoute/pull/12319)) — thanks @maxmad64bis +- **feat(radar):** The Radar catalog table now shows two facts it was already receiving from the feed and dropping on the floor: the per-model rate limits (requests and tokens, per minute and per day) in a new column, and a badge when a provider's terms state it may train on the prompts you send. A limit of zero renders as zero rather than "rate-only" — for a ceiling those are opposite facts — and a model with no training statement gets no badge, because an absent statement is not a guarantee ([#12320](https://github.com/diegosouzapw/OmniRoute/pull/12320)) — thanks @maxmad64bis +- **feat(radar):** explain Community, single-use, contributor, supporter, recovery, abuse, offers, and privacy rules before either Radar activation action, and remove the superseded fixed-PR grant promise from every UI locale ([#12342](https://github.com/diegosouzapw/OmniRoute/pull/12342)) +- **feat(gamification):** the dashboard Profile page now shows the real daily streak — `/api/gamification/level` returns `streak: { current, longest }` (per key with `apiKeyId`, operator-wide maximum otherwise) and the streak card reads it instead of a hard-coded 0 (#2403) ([#12377](https://github.com/diegosouzapw/OmniRoute/pull/12377)) — thanks @pacocartones +- **feat(gamification):** the dashboard leaderboard now shows each API key's display name under the Name column instead of a truncated key id; `GET /api/gamification/leaderboard` attaches `name` per entry (name only — no key material), while the shared ranking helper and the federation leaderboard stay id-only — thanks @pacocartones ([#12385](https://github.com/diegosouzapw/OmniRoute/pull/12385)) +- **feat(gamification):** enforce the documented 1000 XP/min per-API-key anti-cheat rate limit on the XP award path; over-limit awards are logged and skipped instead of persisted, and the sliding window now matches the timestamp format stored in `xp_audit_log` ([#2403](https://github.com/diegosouzapw/OmniRoute/issues/2403)) ([#12390](https://github.com/diegosouzapw/OmniRoute/pull/12390)) — thanks @pacocartones +- **feat(admin):** localize the gamification anomalies page — the loading state, the Status column and the Suspicious badge now come from the `common` catalog (new `common.suspicious` key propagated to every locale) — add it to the Gamification sidebar group as `gamification-admin` (`/dashboard/gamification/admin`), and expose the loading and empty states as polite `role="status"` live regions (#12401 — thanks @pacocartones) +- **feat(providers):** skip GitHub combo members missing from the live synced catalog, and drop Copilot models that are policy-disabled or hidden from the model picker ([#12473](https://github.com/diegosouzapw/OmniRoute/pull/12473)) — thanks @RaviTharuma +- **feat(i18n):** locale-expansion tooling — `npm run i18n:add-locale` adds a language to config, dashboard, docs mirrors, CLI, README/indexes and site in one command; browser, CLI and cookie detection resolve `uk`, `fil`/`tl`, `zh-Hant` (and the retired legacy `in` → `id`) via config aliases; `config/i18n.json` now ships in the npm package so the published CLI can read it; new real-translation ratio gate (`npm run i18n:check-ratio`, advisory) with a per-locale ratchet baseline; `run-translation --adopt` restores `.i18n-state.json`; `sync-language-bars` generates 🌐 bars from config; `validate_translation.py` loads its allowlist again. Retires the duplicate `in` locale (Indonesian mislabelled as Hindi) — 42 honest locales; saved `NEXT_LOCALE=in` / `OMNIROUTE_LANG=in` keep working. (#12496) +- **feat(api):** Emit gateway-measured `tokens_per_second` (TTFT excluded) on streaming usage and `X-OmniRoute-Tokens-Per-Second` when first-token latency is known ([#12616](https://github.com/diegosouzapw/OmniRoute/issues/12616)) ([#12631](https://github.com/diegosouzapw/OmniRoute/pull/12631)) — thanks @RaviTharuma +- **feat(opencode-plugin): map gateway cost/usage/tok/s onto OpenCode inference payloads** — the official plugin copies `X-OmniRoute-Response-Cost`, token counts, `X-OmniRoute-Tokens-Per-Second` / `usage.tokens_per_second`, TTFT, and the winning `X-OmniRoute-Model` onto the JSON/SSE body OpenCode already consumes. Missing tok/s is left unset (never `tokens / latency`). (#12636) — thanks @RaviTharuma +- **feat(combo):** add combo strategy `quota-weighted`: skip exhausted accounts, then weighted-draw by leftover / in-flight load; existing conversations stay pinned until the account is empty ([#12789](https://github.com/diegosouzapw/OmniRoute/pull/12789)) — thanks @HouMinXi +- **feat(models):** Account live listings become the chat catalog source for Claude, Codex, Copilot, and AGY; public metadata only fills prices on IDs those accounts already list. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **fix(models):** Union `agy` and `antigravity` live catalogs so `agy/gemini-3.8-flash-high` is not rejected after the prefix folds to `antigravity`. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **feat(opencode):** Add `@omniroute/opencode-plugin-v2`, an opencode v2 plugin that gives a v2 install a route to an OmniRoute gateway: it publishes models, combos and auto-combos into the host catalog, refreshes them lazily behind a 300s TTL, and keeps serving the last known catalog from an on-disk snapshot when the gateway is unreachable. The v1 plugin (`@omniroute/opencode-plugin`) is untouched and keeps working as before. The plugin adapts to the host instead of assuming one shape: it reads the shape the host seeds into the catalog draft and publishes the `api` block, the pre-`api` top-level fields, or both. The gateway key comes from the host credential store when one is connected, so no secret needs to sit in a config file; a source the gateway refuses is named in a warning instead of silently degrading the catalog to raw model ids; and tool schemas bound for a Gemini model are stripped of the JSON-Schema keywords Gemini rejects, so tool calling survives that route ([#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870)) — thanks @maxmad64bis +- feat(services): show sanitized CLIProxyAPI account health from its authenticated management API without exposing credentials, file paths, or raw account metadata (#6342) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add AnySearch free web search + URL extract (webFetch) with typed results, credential validation, REST routing, and MCP selection - fallback-only ([#11690](https://github.com/diegosouzapw/OmniRoute/pull/11690)) — thanks @Xxx91n +- **feat(dashboard):** display clamped `[0, 100]%` cached input token ratio in request logs table ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) — thanks @b3nw +- **feat(catalog):** add `OMNIROUTE_DISABLE_THINKING_LEVEL_VARIANTS` feature flag to optionally filter out thinking level variants from model catalog ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) — thanks @b3nw +- **feat(dashboard):** continuously export call logs to external analytics stores. A pluggable destination registry ships the full Logs-tab record set on an hourly `JobRegistry` cron, with a persisted per-destination cursor, batched inserts, a config UI rendered from each destination's own field descriptors, and a REST layer (`/api/log-export/*`) for CRUD, a connection test, and an on-demand run. A destination can opt into `includeBodies` to also ship the request and response payloads shown in the Logs detail pane, including the client and provider views of each call; this is off by default, and payloads inherit the dashboard's PII sanitisation, secret redaction and `noLog` handling. Google BigQuery is the first destination, using a service-account key stored encrypted at rest and streaming inserts keyed by call-log id, into a table that is day-partitioned on `timestamp` and clustered on `api_key_name`, `provider`, `model` and `status`. ([#11945](https://github.com/diegosouzapw/OmniRoute/pull/11945)) — thanks @dpozimski +- **feat(providers):** add MaxAI as a signed, OpenAI-compatible provider serving its 13 paid chat models (GPT-5.6 / Luna / Thinking, Claude 5 Sonnet, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite, Grok 4.1-fast / 4.5, DeepSeek V3.2 / R1, Llama 3.3 70B) through OmniRoute's `/v1` endpoint, with per-request HMAC-SHA1→SM3→AES request signing, live model + context-window discovery from `/models/get_config`, and prompted tool-calling translated to OpenAI `tool_calls` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI vision input — image_url content parts are forwarded inline in `message_content` to the 6 vision-capable models (GPT-5.6 / Luna / Thinking, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite) ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI image generation — 6 image models (gpt-image-1, dall-e-3, flux-1-schnell/dev/pro, sd3-medium) exposed through `POST /v1/images/generations` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI document RAG — inline base64 file/document attachments are uploaded to MaxAI (content-addressed `doc_id`) and attached to the chat via `doc_list` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** browserless MaxAI onboarding — email device-pair login (`/api/providers/[id]/login`) and signed access-token refresh, so a connection can be created and kept fresh without a real browser or Google OAuth ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** per-provider TLS impersonation profile (MaxAI presents a Windows Firefox-150 client fingerprint) so its bot-sensitive endpoints accept OmniRoute traffic ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** add **Nimble** as a web-search and web-fetch provider (`nimble-search`) — `/v1/search` routes to Nimble's search API at `lite` depth (locale, freshness and include/exclude domain filters mapped onto the shared request contract), and `/v1/web/fetch` routes to Nimble Extract, which covers all four fetch formats (`markdown`, `html`, `links`, `screenshot`) from a single call. One API key serves both surfaces. ([#11620](https://github.com/diegosouzapw/OmniRoute/pull/11620)) — thanks @wildcard +- The npm publish is automatic again, through npm Trusted Publishing (OIDC): the hosted `stage-npm` job publishes with a short-lived credential minted from GitHub's id-token — no `NPM_TOKEN`, no 2FA prompt, provenance attached. `publish_mode=staged` (owner approves with 2FA) and `direct` (token) remain available on `workflow_dispatch`. ([#11931](https://github.com/diegosouzapw/OmniRoute/pull/11931)) +- **feat(providers):** Add **Opper** as an API-key gateway provider — EU-hosted AI gateway with 700+ models from 30+ providers behind one OpenAI-compatible API and one key (`OPPER_API_KEY`); model ids use `provider/model` format (e.g. `anthropic/claude-sonnet-4-6`, `openai/gpt-5`); live model catalog at `https://api.opper.ai/v3/compat/models`; entry mirrors `requesty` (same shape, `passthroughModels: true`, no static seed) ([#11629](https://github.com/diegosouzapw/OmniRoute/pull/11629)) +- **feat(dashboard):** the `/dashboard/orchestration` snapshot hook now subscribes to the `agents` WebSocket channel (`agent.task.updated`) instead of `requests` as its refetch trigger, and relaxes its background poll from 5s to 30s while that WS connection is up — falling back to the tighter 5s cadence, reprogrammed live on any connect/disconnect transition, whenever the socket is down. ([#12409](https://github.com/diegosouzapw/OmniRoute/pull/12409)) +- **feat(dashboard):** Orchestration canvas quick wins — search box plus state/source/provider filter chips with a one-click clear, and per-source collapse/expand, all reflected in the URL so a filtered/collapsed view is shareable and survives a refresh; the detail drawer gained a "copy trace JSON" action and hardened error/empty-state and accessibility handling; the Agents-tab edges now animate traveling particles along active (running) connections; and the canvas node/edge status colors moved off fixed hex values onto theme-aware `--orch-status-*` CSS custom properties, so they adapt correctly to light/dark mode. ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- **chore(dashboard):** Orchestration UI hardening pass and the missing component/model test coverage it called for — `OrchestratorNode`/`ActivityNode`/`OverflowNode` rendering, the `?node=`/overflow-click page routing, the Agents-tab orchestrator-click no-op and `showCompleted` toggle, and the overview kanban's done-column sort order (#12270, #12271). ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- New `/dashboard/orchestration` page: live unified view of everything running — Cloud Agent, A2A and Conductor as a real-time graph (Agents tab), the combo cascade (Routing tab, reusing the Combo Live Studio) and a state kanban (Overview tab), with a detail drawer (trace, cost, approve/cancel). Read-only over existing APIs — no new backend. Canvas concept credit: PR #11815 design ([#12261](https://github.com/diegosouzapw/OmniRoute/pull/12261)) +- **feat(dashboard):** new "History" tab on `/dashboard/orchestration` — an Airflow-style grid of finished runs over a 24h/7d/30d preset window, one row per (source, identity), clicking a cell opens the existing detail drawer. It is backed by real persistence: A2A task lifecycle transitions are now written to the `a2a_tasks` table (purged after 30 days, configurable via `OMNIROUTE_A2A_HISTORY_RETENTION_DAYS`) and served by the new `GET /api/a2a/tasks/history` listing endpoint, with the task-detail route falling back to persisted history once a run leaves the in-memory snapshot. Conductor runs stay remote and are not persisted locally — the tab says so instead of silently omitting them. ([#12479](https://github.com/diegosouzapw/OmniRoute/pull/12479)) +- **feat(dashboard):** the orchestration detail drawer gained a "Repeat" action for Cloud Agent, A2A and Conductor tasks — a two-click confirm (click once to arm, click again within the confirm window to fire) re-submits the original prompt/input as a new run. The button is disabled with an explanatory tooltip whenever the original input can't be recovered from the loaded task detail (e.g. it never carried a prompt, or the detail failed to load). Two limitations of the A2A variant, by design: it targets the `/a2a` JSON-RPC endpoint, which authenticates with an API key only (`REQUIRE_API_KEY=true` or a configured `OMNIROUTE_API_KEY` makes a dashboard-session repeat answer `HTTP 400` — surfaced verbatim in the drawer's error line, never as a success), and the `message/send` call is SYNCHRONOUS: the POST blocks for the whole skill run, so the success confirmation only appears once the repeated task finishes. A dashboard-authenticated A2A creation path is deliberately left to a follow-up — widening the endpoint's auth posture is an operator decision, not a side effect of this feature. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(a2a):** A2A task execution now records which memories were consulted for the task's last user message as `metadata.memoryHits` (id/key/type/content-snippet) plus a `memory_hits` history event, purely for observability — the retrieved memory is never injected into a skill's prompt or behavior. Gated by the `OMNIROUTE_A2A_MEMORY_HITS` kill-switch (default enabled; set to `0` to skip the recall lookup entirely). The drawer's new "Memory used" section lists these hits for a2a tasks and is omitted whenever there are none. Known limitation: recall only resolves under the keyless posture — a keyed caller's task owner is a SHA-256 prefix of the API key, while memory rows are keyed by the database api-key id, and no hash→id lookup exists today, so the hit list stays empty for keyed callers. The recorded hits are also kept out of the task's own `input` (and therefore out of the persisted input and of the "Repeat" request body), so repeating a task never re-sends the previous run's memory snippets. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(providers):** add a Perplexity Agent API provider (`perplexity-agent` / `pplx-agent`) for Perplexity `/v1/responses`, including the documented Anthropic, OpenAI, Google, xAI, DeepSeek, Z.AI, Moonshot/Kimi, NVIDIA, and Perplexity model IDs plus Anthropic-model `max_output_tokens` compatibility. ([#12103](https://github.com/diegosouzapw/OmniRoute/pull/12103)) — thanks @AIB1TAL0S +- **feat(providers):** add RPD (Requests Per Day) limit to provider rate limit overrides across UI, schemas, DB, and i18n ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) — thanks @b3nw +- **feat(providers): add UC Direct (uncensored.com Developer API), the metered OpenAI-compatible surface.** A standard OpenAI-compatible passthrough (default executor) for uncensored.com's official REST API at `https://api.uncensored.com/api/v1`: `X-api-key` auth (never-expiring `uai_sk_live_` key), `POST /chat/completions` with streaming SSE and native tool-calling, and the full live metered catalog (82 models across 15 providers, discovered from the public `GET /v1/models`). Registered as provider `uc-direct` (alias `ucd`). Complements the un-metered `uc` persona provider — same models, metered credits and a plain API key instead of a subscription session. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(providers): add UC (uncensored.com), the un-metered subscription "persona" chat as an OpenAI-compatible provider.** A WebSocket web-app port: a durable Clerk credential mints a short-lived session token per connect (browserless — no API key), driving UC's persona socket. Ships the browserless email-code login (request → verify → harvest), the 19 verified persona models (Claude Opus, Gemini, Grok, GLM, Kimi, DeepSeek, MiniMax, incl. the uncensored variants), prompted `` tool-calling with a per-model code-style dialect + auto-cure retry for guardrailed models, live ``/reasoning split, streaming + non-streaming OpenAI responses, and full quota/auth error surfacing (paywall / message-limit / rate-limit → 429, invalid session → 401 re-login). Full multimodal parity via the persona blob-upload layer: **vision** (image input, 15 vision-capable models), **document RAG** (PDF/doc upload, server-side extraction), **image generation** (22 models), **video generation** (14 models, async signed-url → poll), and **TTS** (streaming MP3). Registered as provider `uc` (alias `ucn`). The metered OpenAI-compatible Developer API is a separate `uc-direct` provider. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(logs):** add copy-all button to request detail modal ([#11083](https://github.com/diegosouzapw/OmniRoute/pull/11083)) — thanks @AndrianBalanescu +- **feat(check):** make serviceKinds required and add the reverse-walk provider consistency gate ([#11392](https://github.com/diegosouzapw/OmniRoute/pull/11392)) — thanks @Tushar49 +- **feat(providers):** only advertise verified-working connections by default ([#11448](https://github.com/diegosouzapw/OmniRoute/pull/11448)) — thanks @alltomatos +- **feat(resilience):** add hierarchical concurrency admission ([#11493](https://github.com/diegosouzapw/OmniRoute/pull/11493)) +- **feat(providers):** derive + surface expiry for JWT-bearing web cookies (#11497) ([#11505](https://github.com/diegosouzapw/OmniRoute/pull/11505)) — thanks @oyi77 +- **feat(tunnels):** support named cloudflared tunnels via CLOUDFLARED_CONFIG ([#11561](https://github.com/diegosouzapw/OmniRoute/pull/11561)) — thanks @ggiak +- **feat(compression):** make proactive context-compression threshold a live setting ([#11564](https://github.com/diegosouzapw/OmniRoute/pull/11564)) — thanks @ragnar-claude +- **feat(resilience):** add provider peak-hour protection ([#11622](https://github.com/diegosouzapw/OmniRoute/pull/11622)) — thanks @Neuron-Mr-White +- **feat(providers):** add 1min.ai provider ([#11631](https://github.com/diegosouzapw/OmniRoute/pull/11631)) — thanks @zero-executioner +- **feat(dashboard):** collapsible JSON tree viewer for request/response payloads ([#11703](https://github.com/diegosouzapw/OmniRoute/pull/11703)) — thanks @hartmark +- **feat(routing):** add LiquidAI LFM2.5-2.6B free tier via OpenRouter ([#11752](https://github.com/diegosouzapw/OmniRoute/pull/11752)) — thanks @brick30llc-ctrl +- **feat(pricing):** add GLM-5.3-Flash pricing, model specs, and catalog registration ([#11830](https://github.com/diegosouzapw/OmniRoute/pull/11830)) — thanks @AndrianBalanescu +- **feat(routing):** add relayMode for schema-locked context handoffs ([#11839](https://github.com/diegosouzapw/OmniRoute/pull/11839)) — thanks @raheemuddin786 +- **feat(config):** add support for runtime system prompt configuration and hot-reloading ([#11841](https://github.com/diegosouzapw/OmniRoute/pull/11841)) — thanks @raheemuddin786 +- **feat(routing):** expand connection-aware quota prefilter across combo strategies (#11682) ([#11850](https://github.com/diegosouzapw/OmniRoute/pull/11850)) — thanks @HouMinXi +- **feat(models):** reactive discovery sync after Antigravity model-not-found 404 ([#11888](https://github.com/diegosouzapw/OmniRoute/pull/11888)) — thanks @MumuTW +- **feat(models):** auto-aliases and token limits from synced Antigravity discovery ([#11889](https://github.com/diegosouzapw/OmniRoute/pull/11889)) — thanks @MumuTW +- **feat(settings):** raise sticky round-robin limit caps to 1000 ([#12015](https://github.com/diegosouzapw/OmniRoute/pull/12015)) — thanks @adivekar-utexas +- **feat(resilience):** operator-configurable global credential health check interval ([#12043](https://github.com/diegosouzapw/OmniRoute/pull/12043)) — thanks @adivekar-utexas +- **feat(quota):** use official OpenCode Go usage API ([#12124](https://github.com/diegosouzapw/OmniRoute/pull/12124)) — thanks @ddarkr +- **feat(resilience):** default the credential health check sweep to 60 minutes ([#12138](https://github.com/diegosouzapw/OmniRoute/pull/12138)) — thanks @adivekar-utexas +- **feat(quality):** new-code mode for the complexity and dead-code ratchets (Clean as You Code) ([#12142](https://github.com/diegosouzapw/OmniRoute/pull/12142)) +- **feat(dashboard):** orchestration canvas — unified model + snapshot hook (part 1/2) ([#12156](https://github.com/diegosouzapw/OmniRoute/pull/12156)) +- **feat(usage):** add Kilo Code balance and Kilo Pass quotas ([#12178](https://github.com/diegosouzapw/OmniRoute/pull/12178)) — thanks @killer30001000 +- **feat(providers):** refresh vendored ChatGPT Web connector to v4.0.7 ([#12181](https://github.com/diegosouzapw/OmniRoute/pull/12181)) — thanks @backryun +- **feat(providers):** manual "Clear cooldown" action in the cooling panel ([#12224](https://github.com/diegosouzapw/OmniRoute/pull/12224)) — thanks @patrykkopycinski +- **feat(providers):** restore ChatGPT Web via clean-room browser transport ([#12239](https://github.com/diegosouzapw/OmniRoute/pull/12239)) — thanks @backryun +- **feat(sse):** wire the PROVIDER_PROFILES window gate into the global provider cooldown ([#12247](https://github.com/diegosouzapw/OmniRoute/pull/12247)) +- **feat(providers):** modernize CLOVA Studio chat and embeddings ([#12277](https://github.com/diegosouzapw/OmniRoute/pull/12277)) — thanks @backryun +- **feat(browser):** adopt Obscura as primary headless browser engine with Chromium fallback ([#12286](https://github.com/diegosouzapw/OmniRoute/pull/12286)) — thanks @opensource-elearning +- **feat(sse):** full language parity for output styles (es/de/fr/it/ru/zh + autoDetect) ([#12289](https://github.com/diegosouzapw/OmniRoute/pull/12289)) +- **feat(quality):** complete test:scoped — --full map rebuild, stdin selection, CI loader parity (#8084 D1) ([#12353](https://github.com/diegosouzapw/OmniRoute/pull/12353)) +- **feat(providers):** refresh Fable, Cursor, and Devin catalogs ([#12367](https://github.com/diegosouzapw/OmniRoute/pull/12367)) — thanks @backryun +- **feat(video):** redact transcript text from logs and durable memory (#12150 P1) ([#12427](https://github.com/diegosouzapw/OmniRoute/pull/12427)) +- **feat(dashboard):** parent-link, genuine-continuation badge, and modal perf fixes ([#12448](https://github.com/diegosouzapw/OmniRoute/pull/12448)) — thanks @hartmark +- **feat(dashboard):** adaptive context-budget dial on compression panel ([#12488](https://github.com/diegosouzapw/OmniRoute/pull/12488)) — thanks @HouMinXi +- **feat(providers):** refresh Z.ai Web models and browser transport ([#12524](https://github.com/diegosouzapw/OmniRoute/pull/12524)) — thanks @backryun +- **feat(video):** redact raw client-snapshot transcript fields in the detailed log (#12150 P2) ([#12528](https://github.com/diegosouzapw/OmniRoute/pull/12528)) +- **feat(providers):** refresh NVIDIA hosted models ([#12538](https://github.com/diegosouzapw/OmniRoute/pull/12538)) — thanks @backryun +- **feat(quota):** Moonshot Open Platform balance and TPD lock for custom nodes ([#12590](https://github.com/diegosouzapw/OmniRoute/pull/12590)) — thanks @HouMinXi +- **feat(video):** redact transcript in the in-memory pending-request snapshot (#12430 item 6) ([#12596](https://github.com/diegosouzapw/OmniRoute/pull/12596)) +- **feat(dashboard):** link a log entry's Conversation Context to its owning conversation ([#12646](https://github.com/diegosouzapw/OmniRoute/pull/12646)) — thanks @hartmark +- **feat(catalog):** eligibility-gated free-tier bucket ([#12669](https://github.com/diegosouzapw/OmniRoute/pull/12669)) +- **feat(db):** fail-closed previous_response_id continuation for redacted video turns (#12150 P2b) ([#12707](https://github.com/diegosouzapw/OmniRoute/pull/12707)) +- **feat(gamification): pay the documented `streak_bonus` and `badge_unlock` XP rewards.** `XP_REWARDS` listed both rewards but the award pipeline never paid them: the private reward table in `events.ts` omitted them, `updateStreak()` did not report when a streak extended, and badge unlocks carried no XP. Every request that extends a daily streak now pays `streak_bonus × streak length` once per UTC day (guarded by a same-day `xp_audit_log` check), and every badge unlocked through the pipeline pays `badge_unlock` once per badge (guarded by the `user_badges` primary key; `unlockBadge()` now reports whether it inserted). Bonus XP flows through the same `addXp` + level sync + global/weekly/monthly leaderboard path as action XP, so level-ups and rankings include it. The Radar supporter recognition unlock stays XP-free. (#12522 — thanks @pacocartones) +- **feat(providers):** advertise a `free-tier` capability in the provider plugin manifest for every provider with documented free models, so sidecars and dashboards can filter free-capable providers without reading the quota catalog ([#12786](https://github.com/diegosouzapw/OmniRoute/pull/12786)) — thanks @maxmad64bis +- **feat(providers):** Added EURouter as an OpenAI-compatible API-key gateway (`https://api.eurouter.ai/v1`), with live model discovery via `passthroughModels`. Its copy states that models are served by third-party upstreams listed per model, so an EU-based router is not read as EU data residency for inference. ([#13025](https://github.com/diegosouzapw/OmniRoute/pull/13025)) — thanks @ntdatt812 +- **feat(providers):** Added GreenPT as an OpenAI-compatible API-key provider (`https://api.greenpt.ai/v1`), with live model discovery via `passthroughModels`. No free-inference badge: the published docs describe a free API subscription billed per token, not a free tier. ([#13024](https://github.com/diegosouzapw/OmniRoute/pull/13024)) — thanks @ntdatt812 +- **feat(i18n):** 9 new locales — Greek (`el`), Croatian (`hr`), Serbian (`sr`), Lithuanian (`lt`), Estonian (`et`), Latvian (`lv`), Slovenian (`sl`), Maltese (`mt`), Irish (`ga`) — across the dashboard, docs mirrors, CLI, README and the site; OmniRoute now ships every official EU language (51 locales). (#13044) +- **feat(i18n):** 8 new locales — Kannada (`kn`), Malayalam (`ml`), Odia (`or`), Punjabi (`pa`), Nepali (`ne`), Sinhala (`si`), Burmese (`my`), Khmer (`km`) — across the dashboard, docs mirrors, CLI, README and the site (59 locales). The translator now restores the ICU literal escape around angle placeholders and splits oversized docs sections before translating. (#0000) ([#13660](https://github.com/diegosouzapw/OmniRoute/pull/13660)) +- feat(providers): **list Agnes 3.0 Flash as the current free chat model, drop retired 1.5 Flash, add Image 2.0/2.5 Flash plus Video 2.5/2.5 Flash, and discover the live `/v1/models` catalog (including the CN host `api.agnes-ai.cn`).** `agnes-1.5-flash` now forwards to `agnes-3.0-flash`. Video 2.5 polls `GET /v1/videos/{id}` (not the V2.0 `/agnesapi` contract). Live `/v1/models` (2026-09-09) no longer serves 1.5; the wiki marks it deprecated. 3.0 Flash is 512K context / 65,536 max output, same window as 2.5. CN-region keys use the existing per-connection base-URL field, default stays `apihub.agnes-ai.com`. ([#13120](https://github.com/diegosouzapw/OmniRoute/pull/13120)) — thanks @HouMinXi +- **feat(sse):** Codex and OpenAI catalogs list GPT-6 Astra with effort aliases (`-low` through `-ultra`); Codex CLI identity pins `@openai/codex@0.153.4` in lockstep with the image ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **feat(dashboard):** the orchestration History tab gained a "Compare runs" mode — toggling it turns each grid cell into a 2-item selection queue (a 3rd click drops the oldest pick), and picking two cells opens a side-by-side comparison panel instead of the usual detail drawer. The panel fetches both runs' detail the same way the drawer does (falling back to persisted history once a run leaves the live TTL window) and shows, per side: identity/source/state, start time, a signed `right - left` delta for duration/cost/event count, the event timeline aligned by index, and any memory hits. One side's fetch failing never blocks the other, and a delta is only ever computed when both sides have a finite value — otherwise it renders "—", never `NaN`. Comparing two runs from different sources or skills still works; a banner marks the deltas as informational rather than hiding them, since the two runs aren't a strict apples-to-apples pair. ([#12677](https://github.com/diegosouzapw/OmniRoute/pull/12677)) +- **feat(dashboard):** badge a conversation that never reached a clean stop ([#12717](https://github.com/diegosouzapw/OmniRoute/pull/12717)) — thanks @hartmark +- **feat(release):** reconcile-changelog tool + version-anchored fragment aggregation ([#12987](https://github.com/diegosouzapw/OmniRoute/pull/12987)) +- **feat(mcp):** carry context_length through MCP list_models_catalog projection (#12776) ([#13406](https://github.com/diegosouzapw/OmniRoute/pull/13406)) — thanks @KooshaPari +- **feat(cli):** make startup readiness budget configurable (#13369) ([#13433](https://github.com/diegosouzapw/OmniRoute/pull/13433)) — thanks @KooshaPari + ### 🐛 Bug Fixes +- **security(streaming):** sanitize generic mid-stream error messages before emitting OpenAI, + Responses, or Claude SSE failure frames and before diagnostic logging, while preserving raw + failures for internal classification and keeping client disconnects out of provider failure state. ([#12457](https://github.com/diegosouzapw/OmniRoute/pull/12457)) +- **fix(claude):** `blockExtraUsage: false` no longer lets 5h quota preflight skip the account; extra usage is billed after the session bar is gone, so the request must reach Anthropic ([#12803](https://github.com/diegosouzapw/OmniRoute/pull/12803)) — thanks @HouMinXi +- Sanitize HuggingChat conversation-creation and message-send transport failures before they reach client error bodies or provider logs. ([#12467](https://github.com/diegosouzapw/OmniRoute/pull/12467)) +- **fix(dashboard):** Keep local and theme-aware provider SVG icons at a definite layout size so Chromium does not collapse them to 0×0 after the v3.8.50 image-rendering change ([#12054](https://github.com/diegosouzapw/OmniRoute/pull/12054)) — thanks @ponkcore +- **fix(security):** Sanitize provider and runtime failures before public API, SSE and MCP responses and before persistent request, proxy and usage logs, preventing credentials, stack traces and host filesystem paths from crossing those boundaries while preserving stable error codes and useful diagnostics. ([#12506](https://github.com/diegosouzapw/OmniRoute/pull/12506)) +- **fix(cli):** `omniroute tunnel create` no longer crashes with `Cannot read properties of undefined (reading optsWithGlobals)` — removed the duplicate positional argument that caused Commander.js to misalign the action callback parameters ([#12295](https://github.com/diegosouzapw/OmniRoute/issues/12295)) ([#12368](https://github.com/diegosouzapw/OmniRoute/pull/12368)) — thanks @gonisulaimann +- Stop advertising Gemini Live-only models as supported audio endpoints until OmniRoute proxies the bidirectional Live protocol. ([#11710](https://github.com/diegosouzapw/OmniRoute/pull/11710)) — thanks @RaviTharuma +- **fix(kie):** reroute `flux/kontext` off the KIE Market `createTask` flow — it is catalogued with `isMarket: true` but has no Market catalog page, so KIE rejected it with "model name not supported"; it now hits the dedicated `POST /api/v1/flux/kontext/generate` / `GET /api/v1/flux/kontext/record-info` endpoints instead (#11296). ([#11985](https://github.com/diegosouzapw/OmniRoute/pull/11985)) — thanks @hartmark +- **fix(kie):** correct 12 more KIE Market catalog ids that were sent to `createTask` unchanged but diverge from KIE's documented upstream `model` values — GPT Image 2 T2I/I2I (drops the `gpt/` prefix), GPT Image 1.5 T2I/I2I (`gpt-image/` namespace), Seedream 5.0 Lite T2I/I2I (drops the `.0`), all 4 Flux 2 variants (`flux-2/` namespace, generic variant renamed `flex`), and Wan 2.7 Image / Image Pro (dash instead of dot) — each verified individually against the literal example request published on docs.kie.ai. `#11326`'s "everything else already matches" claim was wrong a second time (#11296); `z-image/4.0-*`/`z-image/4.5-*` and `flux/kontext` remain open, documented as unresolved in `KIE_MARKET_UPSTREAM_MODEL_IDS`'s comment pending further verification. ([#11667](https://github.com/diegosouzapw/OmniRoute/pull/11667)) +- **fix(dashboard):** `useApiKeySave.handleSaveApiKey` no longer forces a full upstream `/models` catalog sync on every non-curated provider connection save — callers can now pass `skipModelSync: true` to opt out, so a workflow that only wants to add one manual model no longer floods the provider's available-models list with hundreds/thousands of synced entries. The flag is a client-side intent signal only and is stripped before the connection payload is POSTed to `/api/providers`; default behavior (full sync on save) is unchanged when the flag is omitted (#11324) ([#11664](https://github.com/diegosouzapw/OmniRoute/pull/11664)) +- **Provider connections:** keep `tokenExpiresAt` when a connection is created. The create-path allowlist omitted it, so every insert stored NULL and the dashboard token badge could read a fresh connection as expired until its first background refresh ([#11368](https://github.com/diegosouzapw/OmniRoute/pull/11368)). — thanks @ntdatt812 +- **fix(combos):** the combo builder's precision-select, global-model-search, and manual-entry flows now serialize a model step's `model` string using the provider's already-computed routing-alias prefix (e.g. `oc/`) instead of rebuilding it from the raw canonical `providerId`, fixing the no-auth "OpenCode Free" provider (`opencode`) being routed to the unrelated paid "OpenCode Zen" provider (`opencode-zen`) because `opencode` doubles as a manual routing-prefix override ([#11433](https://github.com/diegosouzapw/OmniRoute/issues/11433)). ([#11670](https://github.com/diegosouzapw/OmniRoute/pull/11670)) +- **fix(radar):** the catalog feed cache now keeps `generatedAt`, the date the feed's data was built, next to `fetchedAt`, the date this install downloaded it (#11435). The feed schema requires that date and the sync path validates it, but the cache dropped it — so a feed fetched minutes ago and one carrying weeks-old figures looked identical to everything downstream, including the dashboard's "Last fetched" line. `getRadarCatalog().meta` and `GET /api/radar/status` now report both dates, the latter as its own field rather than folded into `version` — and omitted entirely for the offers and intel caches, which keep no build date, where a `null` would read as "unknown" rather than "never stored". The dashboard still shows only the fetch time; surfacing the build date there needs a new translated label and is left to a follow-up. Rows cached before migration 163 read back as `null`: unknown stays unknown instead of borrowing the fetch time. The referrals cache has persisted the same date since migration 142. — thanks @maxmad64bis +- **fix(cli):** postinstall no longer fills `JWT_SECRET` and `API_KEY_SECRET` in the installed package's `.env` (#11436). `.env.example` ships both blank on purpose: the server restores them from its durable store, or generates and persists them there, in `ensureSecrets()`. Pre-filling them defeated that — the file lives inside the package directory, so `npm i -g` replaced it and postinstall wrote _different_ values, while `ensureSecrets()` (which only acts on an empty variable) never got to restore the real ones. Both secrets rotated silently on every update, invalidating dashboard sessions and API-key CRCs. `STORAGE_ENCRYPTION_KEY` left the same list for the same reason in #1622; its comment pointed at a function that no longer exists and now names the real provisioning path. — thanks @maxmad64bis +- **fix(cli):** the CLI now says when a loaded `.env` lives inside the installed package directory (#11437). It already announces every env file it reads, without distinguishing the ones that survive an update from the one that does not: `npm i -g` replaces the package directory wholesale, so values set there are gone at the next update, silently. The warning names the durable path to move them to, and fires only when that file actually supplied a value — a file entirely shadowed by a durable one supplied nothing. A development checkout stays silent: there the same path is stable and documented in `SETUP_GUIDE.md`. — thanks @maxmad64bis +- **fix(free-models):** the shared `isFreeModel()` predicate no longer reports catalog entries marked `freeType: "discontinued"` as free, so `hidePaidModels` can't route to Pollinations' seven premium models that now require a paid key ([#11441](https://github.com/diegosouzapw/OmniRoute/pull/11441)) — thanks @maxmad64bis +- fix(ui): let AnySearch use the normal provider-icon fallback when LobeHub has no matching icon (#11449) +- Fixed the v3.8.50 Costs and Analytics dashboards so flat-rate Claude Code usage can be shown as an explicitly requested token-price estimate without changing default billed-cost semantics. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed archived usage retention so each request is priced individually instead of pricing a day's summed tokens once, which understated archived cost whenever a day mixed cache-heavy and ordinary requests. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed the Costs dashboard so it discloses when displayed figures include flat-rate token-price estimates instead of labelling them as billed spend, using the flag the analytics API already returns; the month-end projection and the CSV/JSON exports carry the same marker, and billed-cost mode is unchanged. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- **fix(combo):** attach the same combo-diagnostics trace (`poolSize`/`attemptOrder`/`excluded`/`terminalReason`, plus `x-omniroute-combo-*` headers) to the round-robin strategy's and the nested pipeline/fusion runtime-unit loop's "Maximum combo retry limit reached" 503 that the priority-strategy path already attaches for the identical terminal condition — previously those two paths returned a bare, contextless 503 ([#11462](https://github.com/diegosouzapw/OmniRoute/issues/11462)). ([#11665](https://github.com/diegosouzapw/OmniRoute/pull/11665)) +- **fix(bun):** use native `bun:sqlite` in `bootstrap-env` and `sync-env` to avoid loading `better-sqlite3` N-API addon during Bun startup ([#11468](https://github.com/diegosouzapw/OmniRoute/pull/11468)) — thanks @TheDemonTuan +- **fix(docker-bun):** make `Dockerfile.bun` install reliable with pre-install script helpers and native `bun:sqlite` smoke check ([#11470](https://github.com/diegosouzapw/OmniRoute/pull/11470)) — thanks @TheDemonTuan +- **fix(docker-bun):** remove every vendored `better-sqlite3` native addon from the Bun runtime image so startup cannot abort before the `bun:sqlite` fallback ([#11482](https://github.com/diegosouzapw/OmniRoute/pull/11482)) — thanks @TheDemonTuan +- **fix(autoCombo):** effort/alias model variants (`gpt-5.6-sol-xhigh`, `gpt-5.6`, cursor's `claude-4.6-opus-high`) inherit their base model's task fitness instead of falling to the wildcard 0.5 ([#11492](https://github.com/diegosouzapw/OmniRoute/pull/11492)) — thanks @MumuTW +- fix(db): rate-limit repeated Arena ELO leaderboard fetch-failure warnings instead of logging one per sync attempt (#11500) ([#11989](https://github.com/diegosouzapw/OmniRoute/pull/11989)) — thanks @hartmark +- **fix(db):** dedupe the raw `[Encryption] Decryption failed...` log line emitted by the lazy-decrypt views (`createLazyRowProxy`/`createLazyConnectionView`), which power `getProviderConnections()` and were re-triggering that line on every CredentialHealth/model-sync cycle for the same corrupt or stale-key credential — a fresh Proxy over a fresh row on every cycle meant the per-proxy memoization never suppressed it, unlike the dedup `decryptConnectionFields()` already had since [#9927](https://github.com/diegosouzapw/OmniRoute/issues/9927). Now shares that dedupe tracking so the line logs at most once per credential ([#11500](https://github.com/diegosouzapw/OmniRoute/issues/11500)). ([#11678](https://github.com/diegosouzapw/OmniRoute/pull/11678)) +- **fix(intelligence):** Arena ELO sync stores a synthesized base-model row for every effort/harness variant (`claude-opus-5-max` → `claude-opus-5`, `gpt-5.6-sol-xhigh (codex-harness)` → `gpt-5.6-sol`), so bare model requests reach the synced intelligence layer instead of the static fallback table; the hand-maintained `MODEL_ALIAS_MAP` — which collapsed generations (`gpt-5.5` → `gpt-5`) — is gone ([#11506](https://github.com/diegosouzapw/OmniRoute/pull/11506)) — thanks @MumuTW +- **fix(autoCombo,sse):** catalog hygiene — the static task-fitness table no longer ranks retired models above live ones (dead rows dropped, patterns matched on segment boundaries), the 7 deprecation aliases that forwarded to retired or non-existent models now point at the vendors' published replacements, an alias is no longer rewritten when the serving provider offers the id as-is, and a new `check:model-lifecycle` gate diffs both tables against a vendor lifecycle snapshot ([#11507](https://github.com/diegosouzapw/OmniRoute/pull/11507)) — thanks @MumuTW +- **fix(sse):** stop dropping resolved `thoughtSignature` values on parallel (multi tool-call) turns sent to Gemini 3.x — the claude→gemini and openai→gemini translators previously kept the signature only on the *first* function call of a message, causing Gemini to reject subsequent calls in the same turn with HTTP 400 "Function call is missing a thought_signature"; each function call now keeps its own resolved signature ([#11510](https://github.com/diegosouzapw/OmniRoute/issues/11510)). ([#11669](https://github.com/diegosouzapw/OmniRoute/pull/11669)) +- Treat Docker publishing from a non-default release branch as an expected skip instead of a failed workflow, while preserving the guard that prevents it from overwriting the `next` channel. ([#11527](https://github.com/diegosouzapw/OmniRoute/pull/11527)) — thanks @pacocartones +- fix(search): prefer credentialed providers over duckduckgo-free fallback (#11524) — the fallback-only loop ran before the credentialed-providers loop in `executeWebSearch`, making configured providers unreachable when `duckduckgo-free` was available. ([#11565](https://github.com/diegosouzapw/OmniRoute/pull/11565)) — thanks @vsd2807 +- **fix(sse):** cap the upstream headers-wait phase for STREAMING requests to a client-realistic ceiling (110s, under Codex's own ~120s hard client-abort window) instead of the flat 10-minute `FETCH_TIMEOUT_MS` default — that default was 5x longer than the body-phase readiness watchdog's own adaptive bound, so a request whose upstream never returned any response at all (not even headers, e.g. a stalled NVIDIA target behind a tool-heavy Responses→Chat translation) kept the client connection alive on keepalives only, guaranteeing the client's own patience ran out first with an opaque 499 instead of OmniRoute detecting and failing the stall fast. Non-streaming requests are unaffected — they keep the existing flat default (`open-sse/utils/fetchStartTimeoutPolicy.ts`) (#11526) ([#11668](https://github.com/diegosouzapw/OmniRoute/pull/11668)) +- **fix(ci):** protocol-clients E2E harness boots the peer-stamped custom server (`run-next.mjs dev`) instead of the bare `next` CLI, so `LOCAL_ONLY` locality resolves from the real TCP peer stamp and `/api/mcp/audit` answers 200 (open bootstrap preserved via an `OMNIROUTE_E2E_BOOTSTRAP_MODE=open` credential clear in `run-next.mjs`, pinned to loopback `HOST`) instead of a deterministic 403 (#11535) ([#11549](https://github.com/diegosouzapw/OmniRoute/pull/11549)) — thanks @jonlwheat2-gif +- **fix(docs):** the Auto-Combo engine is described with the number of factors it actually declares. Four different counts were in circulation — 6, 9, 13 and 14 across `AGENTS.md`, four guides and six code comments — while `DEFAULT_WEIGHTS` declares 15. `check:docs-counts` now reads that number from the source, so adding a factor without updating the prose fails CI ([#11545](https://github.com/diegosouzapw/OmniRoute/pull/11545)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **fix(quota-share):** in-flight leases are per request, so an aborted request's slot ages out instead of being kept alive by later traffic on the same connection ([#11547](https://github.com/diegosouzapw/OmniRoute/pull/11547)) — thanks @abhisheksharma2411 +- The free-tier summary route now serves the refreshed Radar catalog when the feed is active — the same numbers the dashboard shows — and states which catalog answered plus its real build date, instead of always reporting release-frozen figures with a stale curation date. The supporter-key live feed stays reserved to authenticated callers of the instance. ([#11550](https://github.com/diegosouzapw/OmniRoute/pull/11550)) — thanks @maxmad64bis +- **sse:** fix LiveWS/embed-WS servers crashing at startup under the Node/tsx runtime — `liveServer.ts`, `embedWsProxy.ts` and `apiBridgeServer.ts` imported `@/shared/utils/httpClientAbortGuard` without the `.mjs` extension, so the client-abort crash guard added by [#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556) was unreachable and every dependent test failed with `ERR_MODULE_NOT_FOUND` ([#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556)). ([#11671](https://github.com/diegosouzapw/OmniRoute/pull/11671)) — thanks @jonlwheat2-gif / @yxyxy +- fix(chat): shed any of the matched provider-node's routing identifiers (public prefix or internal id) before model lookup, so composites like `//` no longer reach upstream verbatim (#11557) — thanks @maxmad64bis +- **fix(cache):** a semantic-cache entry now expires on its TTL instead of surviving until the next UTC midnight, and `dbEntries` stops counting expired rows ([#11573](https://github.com/diegosouzapw/OmniRoute/pull/11573)) — thanks @ntdatt812 +- **fix(api):** `/v1/models` schedules its stale-while-revalidate rebuild through Next's `after()` again, so a stale catalog reaches the client before the rebuild blocks the event loop ([#11574](https://github.com/diegosouzapw/OmniRoute/pull/11574)) — thanks @ntdatt812 +- **fix(check):** the `known-symbols` executor conformance gate awaits the now-async `getExecutor()`, so it stops reporting all 142 registered aliases as dead symbols and can detect a lazy import that fails ([#11582](https://github.com/diegosouzapw/OmniRoute/pull/11582)) — thanks @ntdatt812 +- **fix(pack):** register `bin/cli/utils/volatileEnvPath.mjs` in `PACK_ARTIFACT_REQUIRED_PATHS`, so the prune can no longer silently drop a module the published CLI imports on every boot ([#11588](https://github.com/diegosouzapw/OmniRoute/pull/11588)) — thanks @ntdatt812 +- **i18n(vi):** translate the five reliability-column strings on the free-provider rankings page, which rendered as `__MISSING__:` markers in the Vietnamese UI ([#11589](https://github.com/diegosouzapw/OmniRoute/pull/11589)) — thanks @ntdatt812 +- **resilience:** restore the expired-connection retry-budget probe in the token-health sweep — the `!isGitHubAccessTokenOnlyConnection` carve-out reintroduced by #11608 contradicted the boundary pinned by #11592, so a GitHub connection parked at `expired` with retry budget remaining was never probed and could never self-heal ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)). ([#11672](https://github.com/diegosouzapw/OmniRoute/pull/11672)) — thanks @jonlwheat2-gif / @ntdatt812 +- **fix(dashboard):** Provider Stats now displays combo success rates on their native 0–100 percentage scale instead of multiplying them twice ([#11595](https://github.com/diegosouzapw/OmniRoute/pull/11595)) — thanks @pacocartones +- **fix(providers):** route DuckDuckGo Web Chat through the verified `duck.ai` endpoint, refresh its free model catalog, and avoid forwarding unsolved anti-abuse challenges ([#11597](https://github.com/diegosouzapw/OmniRoute/pull/11597)) — thanks @solstxce +- **fix(ui):** Console log Refresh and Copy controls now expose localized accessible names, keep copy actions visible on keyboard focus, and announce copy completion safely ([#11599](https://github.com/diegosouzapw/OmniRoute/pull/11599)) — thanks @pacocartones +- Dropped the stale-`.eslintcache` `restore-keys` fallback from both "Restore ESLint file cache" steps in `ci.yml`, so the blocking `Lint` job can no longer be served per-file verdicts computed under a different lint config, suppressions file or lockfile. `quality.yml` had already dropped it in #11963; `ci.yml` — the workflow that actually gates PRs — had not (#11600). ([#11996](https://github.com/diegosouzapw/OmniRoute/pull/11996)) +- fix(ci): pass `--pass-on-unpruned-suppressions` in `run-eslint-json.mjs` so the CI Lint job no longer fails when a suppression is merely orphaned by a genuine fix, mirroring the identical fix already in `validate-release-green.mjs` (#11600) ([#11983](https://github.com/diegosouzapw/OmniRoute/pull/11983)) — thanks @hartmark +- fix(handoff): enforce provider allowlist for universal handoff (#11602) — universal handoff now skips summarization when the selected summary model's provider is not included in the configured provider allowlist. ([#11619](https://github.com/diegosouzapw/OmniRoute/pull/11619)) — thanks @vsd2807 +- **fix(dashboard):** Show a stable error state when Search Analytics returns an HTTP error or malformed data ([#11603](https://github.com/diegosouzapw/OmniRoute/pull/11603)) — thanks @pacocartones +- **test(gamification):** pin the aggregate profile level to the XP-derived semantics of #11604 — `getAggregateXp()` now derives `currentLevel` from the summed XP (`calculateLevel(sum)`), not `MAX(stored current_level)`, and the #3484 fixture levels are aligned with the XP curve ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)). ([#11676](https://github.com/diegosouzapw/OmniRoute/pull/11676)) — thanks @jonlwheat2-gif / @pacocartones +- **fix(dashboard):** Keep the Profile level and progress aligned with aggregate XP, including bounded handling for invalid totals ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)) — thanks @pacocartones +- **fix(dashboard):** Prevent locked hidden badges from revealing their icon or opening private badge details before they are earned ([#11605](https://github.com/diegosouzapw/OmniRoute/pull/11605)) — thanks @pacocartones +- **fix(dashboard):** Restore keyboard focus after shared modals close and cancel delayed autofocus during cleanup ([#11607](https://github.com/diegosouzapw/OmniRoute/pull/11607)) — thanks @pacocartones +- **fix(dashboard):** Enable Enter and Space activation for clickable data-table rows without hijacking nested controls ([#11610](https://github.com/diegosouzapw/OmniRoute/pull/11610)) — thanks @pacocartones +- **fix(autoCombo,sse):** vendor-retired catalog ids are dropped from the auto-combo candidate pool and no longer win on leftover `arena_elo` / `user_override` rows; `getModelLifecycleDecision` consults `model-lifecycle.json` (prefix-stripped) so aggregator traffic is not `untracked` for ids the snapshot already knows ([#11625](https://github.com/diegosouzapw/OmniRoute/issues/11625)) ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)) — thanks @MumuTW +- **test(chatcore):** move Codex/Claude combo fixtures off models the lifecycle guard now rejects — `gpt-5.1-codex`/`gpt-5-codex` are vendor-retired (snapshot, #11626) and `claude-3-5-sonnet-20241022` is shut down, so native-passthrough and combo-fallback tests switched to `gpt-5.6-sol` and `claude-sonnet-4.6` ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)). ([#11675](https://github.com/diegosouzapw/OmniRoute/pull/11675)) — thanks @jonlwheat2-gif / @MumuTW +- fix(opencode-plugin): stop a free-tier budget that rounds up across a magnitude from rendering as `1000K`/`1000M` in the model picker — `fmtTokens` chose its unit from the raw token count and then rounded with `toFixed(1)`, so 999,950–999,999 printed as `1000K` rather than `1M` and just under a billion printed as `1000M` rather than `1B` ([#11684](https://github.com/diegosouzapw/OmniRoute/pull/11684)) — thanks @abhisheksharma2411 +- Cap prose-derived Antigravity quota resets at the configured model cooldown maximum while preserving authoritative Retry-After headers and Google RetryInfo hints. (#11695) Thanks @pacocartones. ([#11823](https://github.com/diegosouzapw/OmniRoute/pull/11823)) +- Compare `_rebaseline_*` notes structurally in the ratchet-bank verifier so an object-valued note no longer reports itself as rewritten on an unmodified tree — the nightly `bank-ratchet-shrinks` lane had been aborting on every run since 2026-08-11, leaving 41,672 lines of earned file-size shrinks unbanked (#11702). ([#11892](https://github.com/diegosouzapw/OmniRoute/pull/11892)) — thanks @MumuTW +- **fix(build):** `prepublish.ts` bundles the ChatGPT Web (Codex) MCP bridge through `runBuildTool()` instead of spawning `npx.cmd` raw, fixing the build crash on Node ≥ 20/Windows where `.cmd` shims cannot be spawned without a shell (EINVAL) ([#11704](https://github.com/diegosouzapw/OmniRoute/issues/11704)) ([#11706](https://github.com/diegosouzapw/OmniRoute/pull/11706)) — thanks @jonlwheat2-gif +- fix(codex): keep `parallel_tool_calls:false` on the translated Codex Responses Lite path (#11707) ([#11984](https://github.com/diegosouzapw/OmniRoute/pull/11984)) — thanks @hartmark +- **fix(tests):** the new Gemini Web Images retirement test (#11708) called the now-async `getExecutor()` synchronously, throwing `TypeError: getExecutor(...).getProvider is not a function` instead of asserting Gemini Web chat stays available. Awaited to match the R0.3 executor-registry refactor (#11220). ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(search):** Allow REST and MCP web-fetch requests to select the advertised `anysearch-search` provider explicitly ([#11716](https://github.com/diegosouzapw/OmniRoute/pull/11716)) — thanks @pacocartones +- **fix(providers):** Keep overnight peak-hour protection active after midnight according to the window's configured start day ([#11718](https://github.com/diegosouzapw/OmniRoute/pull/11718)) — thanks @pacocartones +- **fix(providers):** correct all five g4f.space gateway descriptions to document proof-of-work credits or member API keys, variable limits, and the remote prompt boundary instead of promising free keyless access at five requests per minute ([#11738](https://github.com/diegosouzapw/OmniRoute/pull/11738)). +- **fix(db):** `model_capabilities` is created by a migration instead of lazily on the first models.dev sync, so a clean install and an upgraded install converge on the same schema regardless of which features have run ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(ci):** `check:install-upgrade` now fails on an `npm` install truncated by ENOSPC (npm reports it as a warning and still exits 0), authenticates its health probe so the version assertion works against the hardened health payload, frees the clean-install tree before the upgrade phase, and no longer reports a schema divergence computed from a boot that never served ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(routing):** harden session-affinity key derivation against oversized explicit IDs and empty structural-payload collisions, while recognizing Gemini and common text request shapes ([#11746](https://github.com/diegosouzapw/OmniRoute/pull/11746)) — thanks @MumuTW +- **fix(packages/browser-pool):** regenerate `package-lock.json` so the `packages/browser-pool` workspace's locked `playwright`/`@types/node` (and transitives) match its `package.json` specs, fixing cache-only/offline installs (`npm ci --offline`, Nix `buildNpmPackage`) that previously failed with `ENOTCACHED` ([#11747](https://github.com/diegosouzapw/OmniRoute/issues/11747)) — thanks @benjaminkitt ([#11784](https://github.com/diegosouzapw/OmniRoute/pull/11784)) — thanks @geekyNads +- **fix(assets):** Stop distributing 79 provider and CLI logos without verified source/license provenance, keep every provider/tool available through the internal generic icon, and prevent those IDs from falling through to an external icon CDN. ([#11750](https://github.com/diegosouzapw/OmniRoute/pull/11750)) +- **fix(providers):** retire the common `chatgpt-web` / `cgpt-web` provider because its pre-key and proof-of-work provenance could not be cleared ([#11754](https://github.com/diegosouzapw/OmniRoute/pull/11754)) — explicit chat and image requests, slash-prefixed models, persisted aliases, proxy overrides, and combo targets now fail closed with a sanitized HTTP `410` / `PROVIDER_RETIRED` before upstream dispatch. Migration 163 tombstones matching connections and invalidates their active leases without deleting history or widening API-key allowlists. The independent `chatgpt-web-codex` / `cgpt-codex` provider and its MIT-noticed vendored implementation remain available. +- **fix(usage):** quota and usage refresh no longer 409 when an exclusive lease reserves the connection ([#11758](https://github.com/diegosouzapw/OmniRoute/pull/11758)) — thanks @TheDemonTuan +- Keep the embedding registry's vector width and `embedding` type on models when a synced model exists for the same id, so `/v1/models` no longer reports registry-described embedding models widthless or untyped (#11761) — thanks @rahilmavani +- Correct `google/gemini-embedding-001` on the OpenRouter route to 3072 dimensions, the width it returns when `dimensions` is not sent (#11761) — thanks @rahilmavani +- **fix(vision-bridge):** Restrict automatic vision routing and fallbacks to models available in authoritative live provider catalogs, including aliases and effort variants ([#11767](https://github.com/diegosouzapw/OmniRoute/issues/11767)) — thanks @pacocartones ([#11813](https://github.com/diegosouzapw/OmniRoute/pull/11813)) +- **fix(providers):** reclassify Cerebras as a one-time $5 signup credit (payment method required, 30-day validity), not a recurring no-card 1M tokens/day trial ([#11773](https://github.com/diegosouzapw/OmniRoute/issues/11773)) ([#12591](https://github.com/diegosouzapw/OmniRoute/pull/12591)) — thanks @HouMinXi +- fix(cli): stop prepublish from re-rebuilding the already-built ESM-only opencode-plugin dist (#11787) ([#11990](https://github.com/diegosouzapw/OmniRoute/pull/11990)) — thanks @hartmark +- **fix(db):** Fresh installs no longer log a non-fatal `no such table: compression_run_telemetry` warning when retention cleanup runs before the lazily-created telemetry table exists ([#11802](https://github.com/diegosouzapw/OmniRoute/pull/11802)) — thanks @RaviTharuma +- fix(sse): set X-OmniRoute-Selected-Connection-Id on successful combo dispatches so downstream consumers stop falling back to an empty connection id (#11810) ([#11986](https://github.com/diegosouzapw/OmniRoute/pull/11986)) — thanks @hartmark +- **fix(services):** embedded CLIProxyAPI lifecycle checks now use public `/healthz`, while model discovery uses the configured dedicated data-plane API key instead of the management password ([#11811](https://github.com/diegosouzapw/OmniRoute/pull/11811)) — thanks @RaviTharuma +- Fix persisted model steps returning an error in the playground route simulator, and warn when structural steps cannot be simulated. ([#11862](https://github.com/diegosouzapw/OmniRoute/pull/11862)) — thanks @pacocartones +- **fix(providers):** Antigravity's dynamic mitmAlias table no longer routes `gemini-3.7-flash-{high,medium,low}` to a literal tier-suffixed upstream id just because one connected account's own discovery listed it directly — those display ids always resolve through the safe `gemini-3.7-flash-tiered` static alias, so one account's Google-provisioned access no longer 404s every sibling account of the provider ([#11824](https://github.com/diegosouzapw/OmniRoute/issues/11824), [#11651](https://github.com/diegosouzapw/OmniRoute/issues/11651)) ([#11988](https://github.com/diegosouzapw/OmniRoute/pull/11988)) — thanks @hartmark +- **fix(dashboard):** Expose Profile loading, errors, page structure, and XP progress to assistive technologies ([#11838](https://github.com/diegosouzapw/OmniRoute/pull/11838)) — thanks @pacocartones +- **fix(socks):** `Agent.connectTimeout` now bounds both SOCKS handshake and TLS connect, and `family === null` no longer falls back to `fetch-socks` ([#11842](https://github.com/diegosouzapw/OmniRoute/pull/11842)) — thanks @maxmad64bis +- **perf(compression):** OOM mitigations for large payload hashing, memoization, and token estimation ([#11844](https://github.com/diegosouzapw/OmniRoute/pull/11844) — thanks @AndrianBalanescu) +- Excluded Next.js Node File Trace manifests (`*.nft.json`) from the published npm tarball. They are build-time metadata and are never read while serving, but had grown to 668.7 MB — 61% of the package — which pushed the upload past the registry limit and made `npm publish` fail with `413 Payload Too Large`. ([#11864](https://github.com/diegosouzapw/OmniRoute/pull/11864)) +- **fix(cli):** keep the global OmniRoute server `--base-url` separate from provider-node `--endpoint` values in add, update, and validate commands ([#11860](https://github.com/diegosouzapw/OmniRoute/pull/11860)) — thanks @pacocartones +- **fix(config):** Nous Research's `Hermes-4-405B` model now displays as "Hermes 4 405B (Nous Research)" in both the provider registry and the free-model catalog, instead of the mislabelled "Hermes 4 7B" ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#11993](https://github.com/diegosouzapw/OmniRoute/pull/11993)) +- **fix(provider/nous):** inject required user= tag into Nous Research inference requests to resolve upstream 400 "missing tags" error ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#12044](https://github.com/diegosouzapw/OmniRoute/pull/12044)) +- **fix(build):** `npm run build` now fails in one second with a named package and a copy-pasteable fix when npm silently drops an externalised optional native dependency, instead of dying four minutes in with `Module not found: Can't resolve 'better-sqlite3'` ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- **fix(install):** `postinstall` no longer throws `ReferenceError: isAndroid is not defined` — failing the whole `npm install` — when the `better-sqlite3` rebuild fallback times out; the manual-fix guidance is reachable again ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- Fixed the Alibaba free-tier allowlist test that went red on its own once the shipped catalog's `validUntil` (2026-08-27) passed, leaving every PR and `main` with a failing `Unit Tests (1/8)`. The test now builds its own packs with dates it controls, and covers the expired-pack fallback that production has actually been serving. ([#11867](https://github.com/diegosouzapw/OmniRoute/pull/11867)) +- Split the npm registry upload into its own GitHub-hosted job. npm refuses `--provenance` from a self-hosted runner (`422 ... Only "github-hosted" runners are supported`), which blocked the v3.8.50 publish; the heavy verification cannot move to a hosted runner, so it now hands the proven tarball over instead. ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(ui):** the shared `DataTable` loading state no longer reads its decorative ⏳ glyph out to assistive technology, and now carries the same `role="status"` / `aria-live="polite"` / `aria-busy="true"` semantics as `PageLoading` ([#11869](https://github.com/diegosouzapw/OmniRoute/pull/11869)) — thanks @pacocartones +- **fix(api):** Bound the `timeout` accepted by `POST /api/providers/[id]/login` to the same 15s–600s window the Adobe Firefly and Conol browser-login paths already enforce, so a request can no longer pin a headful browser and the single active-login slot with an arbitrarily large poll budget. ([#11871](https://github.com/diegosouzapw/OmniRoute/pull/11871)) — thanks @pacocartones +- **fix(api):** Stop the tunnel and MITM routes from returning raw child-process error text, which disclosed host paths, binary install locations and Tailscale `tskey-*` credentials in a body some of these routes serve to non-loopback callers, and return a real 400 instead of a framework 500 when a tunnel request body fails validation. ([#11872](https://github.com/diegosouzapw/OmniRoute/pull/11872)) — thanks @pacocartones +- **fix(open-sse):** stream TTFT/ITL are sampled from a monotonic clock (`performance.now`) instead of `Date.now`, so an NTP correction or wall-clock jump can no longer inflate a genuine TTFT or produce a negative one that the `ttft >= 0` guard silently drops — protecting the router's OTel/EWMA/`usage_history`/speed-ranking signals ([#11873](https://github.com/diegosouzapw/OmniRoute/pull/11873)) — thanks @pacocartones +- **fix(providers):** xAI `reasoning_effort: "xhigh"` now reaches grok-4.6+ instead of being silently clamped to `"high"` ([#11879](https://github.com/diegosouzapw/OmniRoute/pull/11879)) — thanks @NoxzRCW +- **fix(dashboard):** the providers page no longer crashes into the error boundary when a provider id collides with an `Object.prototype` member (`constructor`, `__proto__`); icon lookups are own-property guarded ([#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)) — thanks @NoxzRCW +- **fix(skills):** injected skill tools declared in shorthand (`{"content": "string"}`) now forward valid JSON Schema, unblocking providers that validate tool schemas strictly such as Zhipu GLM on the Console Go tier ([#11881](https://github.com/diegosouzapw/OmniRoute/pull/11881)) — thanks @NoxzRCW +- **fix(api):** `POST /api/playground/simulate-route` now surfaces `combo-ref` and `provider-wildcard` persisted combo steps with a specific warning (naming the referenced combo, or the unresolved `provider/model` wildcard) instead of folding them into a generic "unsupported step" count; a `provider-wildcard` step is also now included as an unresolved target so the operator can see it is in the route (ported from [#11882](https://github.com/diegosouzapw/OmniRoute/pull/11882) — thanks @NoxzRCW). ([#11930](https://github.com/diegosouzapw/OmniRoute/pull/11930)) +- **fix(translator):** the streaming OpenAI→Claude translator keeps upstream usage, including prompt-cache tokens, when it arrives on a trailing `choices: []` chunk (Fireworks and any upstream using `stream_options.include_usage`) ([#11883](https://github.com/diegosouzapw/OmniRoute/pull/11883)) — thanks @NoxzRCW +- **fix(cli):** `omniroute update --apply` now tells you explicitly whether a running server was detected and, if so, that you must run `omniroute restart` to apply the update — it never restarted anything and previously implied the update was already live once files were installed. The dashboard's npm-mode Update flow (`/api/system/version`) now tries OmniRoute's own PID-file-managed supervisor before falling back to pm2, and reports an honest "restart required" step instead of a silent pm2-only "skipped" that read like a completed update. The server-side latest-version lookup backing the dashboard's update banner also gained `--prefer-online`, closing the same stale-npm-cache class already fixed in the CLI's own copy for #4376 ([#11885](https://github.com/diegosouzapw/OmniRoute/issues/11885)). ([#12005](https://github.com/diegosouzapw/OmniRoute/pull/12005)) +- **fix(resilience):** clear persisted LKGP pins when a target suffers connection/provider exhaustion or is skipped before dispatch due to cooldown/exhaustion/unavailability, preventing subsequent requests from repeatedly prioritizing known-dead providers ([#11911](https://github.com/diegosouzapw/OmniRoute/issues/11911)). ([#12013](https://github.com/diegosouzapw/OmniRoute/pull/12013)) — thanks @HouMinXi +- **fix(sse):** the OpenAI→Claude stream translator now defers the terminal `message_delta`/`message_stop` emission until the real usage block has arrived (or a genuine end-of-stream flush forces it) instead of emitting it immediately on `finish_reason` — previously, when the trailing usage-only chunk (`{"choices":[],"usage":{...}}`) arrived *after* the `finish_reason` chunk (the normal order for Fireworks/vLLM/Together and other `stream_options.include_usage` upstreams), the client-visible `message_delta` still carried stale/zero usage even though `state.usage` was internally corrected too late to matter (ported from [#11915](https://github.com/diegosouzapw/OmniRoute/pull/11915) — thanks @HouMinXi). ([#11933](https://github.com/diegosouzapw/OmniRoute/pull/11933)) +- **fix(sse):** `stripStore()` now forces `store=false` for stateless OpenAI-compatible Responses-API targets (unless the connection has explicitly opted in via `providerSpecificData.openaiStoreEnabled`), instead of only handling the `openai`/`agentrouter` cases — a client-supplied `store` value previously passed through untouched to backends that don't actually persist responses server-side ([#11916](https://github.com/diegosouzapw/OmniRoute/pull/11916) — thanks @HouMinXi). +- **fix(models):** custom provider-node models (synced, custom, and alias-backed) now appear under their configured prefix in the unified catalog when the operator's model-id prefix mode is canonical, instead of being dropped whenever alias-inclusion was otherwise disabled ([#11918](https://github.com/diegosouzapw/OmniRoute/pull/11918) — thanks @HouMinXi). +- **fix(models):** the unified model catalog now suppresses stale static registry models (including effort-tier variants) for any provider whose active connection has an authoritative live synced catalog, not only providers already using exclusive-synced-listing — a connection with `providerUsesAuthoritativeLiveCatalog` previously kept serving both the live-synced models and the stale static rows side by side ([#11919](https://github.com/diegosouzapw/OmniRoute/pull/11919) — thanks @HouMinXi). +- **fix(ui):** `ProviderIcon`'s three remaining unguarded lookups (`PROVIDER_ICON_ALIASES`, `LOCAL_SVG_ALIASES`, `THEMED_SVGS`) now use `Object.hasOwn()` own-property checks — a provider id such as `constructor` or `__proto__` previously resolved through the prototype chain instead of falling through to the unknown-provider CDN fallback (`getLobeProviderIcon()` itself was already guarded by [#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)); `ProviderPageHeader`'s `color` field is also now optional, matching the rest of the component's defensive typing (ported from [#11920](https://github.com/diegosouzapw/OmniRoute/pull/11920) — thanks @HouMinXi). ([#11935](https://github.com/diegosouzapw/OmniRoute/pull/11935)) — thanks @NoxzRCW +- fix(ollama): preserve multi-byte UTF-8 content split across stream chunks in the Ollama NDJSON transform, which previously corrupted CJK/emoji into U+FFFD (#11921) — thanks @pacocartones +- **fix(providers):** OrcaRouter chat requests now target `/v1/chat/completions` instead of the bare `/v1` API root, fixing the upstream `404 Invalid URL (POST /v1)` ([#11923](https://github.com/diegosouzapw/OmniRoute/pull/11923)). — thanks @echel0nn +- **fix(plugins):** deliver the `onStreamComplete` event to disk-installed plugins. The event shipped in v3.8.50 (#9669) was emitted internally but had no plugin-facing wiring, so no plugin could ever subscribe: the manifest schema silently dropped `hooks.onStreamComplete`, and the loader/manager only knew the seven legacy hooks. `onStreamComplete` is now a declarable manifest hook, wired through the loader and registered by the manager like the other hooks, and its payload carries a `requestId` so consumers can correlate the stream-completion event with the originating request ([#11934](https://github.com/diegosouzapw/OmniRoute/pull/11934)) — thanks @amaleta / @pacocartones +- **fix(db):** the Qdrant embedding-model dropdown now lists local/self-hosted providers (Ollama, LM Studio, vLLM, etc.) — an active connection is treated as "configured" when the provider allows an optional API key, not only when it has a real key or OAuth, so a running local embedding provider is no longer hidden from the picker ([#11949](https://github.com/diegosouzapw/OmniRoute/issues/11949)) ([#11995](https://github.com/diegosouzapw/OmniRoute/pull/11995)) +- **fix(providers):** Vertex AI Anthropic partner-model discovery now calls the Model Garden `v1beta1` publisher list (`/v1beta1/publishers/anthropic/models`, global) and parses its `publisherModels` envelope, so Claude models auto-synced from Vertex populate the active live catalog and route at request time instead of returning `Model '' is not available in the active live catalog` ([#11991](https://github.com/diegosouzapw/OmniRoute/issues/11991)) — thanks @fabioluissilva ([#11998](https://github.com/diegosouzapw/OmniRoute/pull/11998)) +- **fix(cli):** support `--base-url` alongside `--endpoint` in `omniroute nodes add`, `update`, and `validate` subcommands to prevent global `--base-url` shadowing issues ([#11999](https://github.com/diegosouzapw/OmniRoute/issues/11999)). ([#12033](https://github.com/diegosouzapw/OmniRoute/pull/12033)) — thanks @HouMinXi +- **fix(providers):** `cloudflare-ai` no longer refuses image content parts for every Workers AI model ([#12002](https://github.com/diegosouzapw/OmniRoute/pull/12002)) — the plain-string `content` requirement behind #2539 is carried by the _model_ schema, not by the `/ai/v1/chat/completions` endpoint (measured: an all-text part array returns 200 on `@cf/mistralai/mistral-small-3.1-24b-instruct`, `@cf/meta/llama-4-scout-17b-16e-instruct` and `@cf/meta/llama-3.3-70b-instruct-fp8-fast`, and 400 on the text-only `@cf/qwen/qwen2.5-coder-32b-instruct`). `transformRequest()` flattened every array and threw on the first non-text part (#6390), so image input was refused for vision-capable Cloudflare models that accept it. All-text arrays are still flattened — the one shape every model accepts — while an array carrying a non-text part is passed through untouched, so the attachment is still never silently dropped. Regression guards: `tests/unit/cloudflare-ai-image-parts-6390.test.ts`. — thanks @davidlinfr +- **fix(resilience):** decouple the limiter-managed execution backstop from the queue-wait budget — new `requestQueue.executionMaxWaitMs` (env `RATE_LIMIT_EXECUTION_MAX_WAIT_MS`, default 600000 = 10 min) now feeds Bottleneck's post-dispatch `expiration`, while `requestQueue.maxWaitMs` keeps its documented queue-wait semantics. Previously the queue-wait budget doubled as the execution expiration, so legitimate long-running calls on non-incremental gateways (whole generation buffered before the first upstream byte, e.g. Console Go / Command Code tiers serving GLM models) were killed mid-flight at the queue budget with a false 504 `RATE_LIMIT_EXECUTION_TIMEOUT` — the local limiter undercut the provider-aware upstream fetch-start timeouts. The surfaced 504 message now names `requestQueue.executionMaxWaitMs`; the error keeps the #4165 guarantees (disclaims an upstream timeout, preserves the Bottleneck error as `cause`, branded code + trusted provenance, classified request-scoped so combo falls back). A real queue-wait bound (the `Promise.race` around `limiter.schedule()` sketched in #9533) remains future work. (#12025) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **Call logs:** keep the `error` field when an artifact exceeds the storage cap, instead of replacing it with the omission marker. The error is the only field that says *why* a request failed and is typically ~90 bytes next to the multi-hundred-KB bodies that trip the cap, so dropping it left a size-limited row undiagnosable — a provider outage, a local timeout and an upstream 400 all rendered identically. It is now preserved at every fallback stage, truncated to 4KB if it is itself large ([#12026](https://github.com/diegosouzapw/OmniRoute/issues/12026)). ([#12095](https://github.com/diegosouzapw/OmniRoute/pull/12095)) — thanks @ntdat812 +- **fix(diagnostics):** preserve the error field (truncated to 4KB with a `[truncated: …]` suffix) in every call-log artifact size-limit fallback stage. Previously the minimal fallback replaced the error with `[omitted: call log artifact size limit exceeded]`, so an oversized artifact row showed nothing about WHY the request failed — e.g. 91 of 847 opencode-go 504 rows on one production instance were undiagnosable from the dashboard. Oversized request/response bodies are still omitted exactly as before; the error cap is independent of the payload sizes that tripped the fallback. (#12026) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **fix(sse):** OpenAI Responses clients that declare the native `web_search` tool now receive a spec-shaped `web_search_call` output item with `action.sources` alongside the preserved function-call round-trip, so search results executed through OmniRoute's own search backend are consumable by standard Responses clients (Codex, pi-web-access, …). ([#12031](https://github.com/diegosouzapw/OmniRoute/pull/12031)) — thanks @watchingdogs +- **fix(cli):** use in-thread alias resolver hooks on modern runtimes to avoid deprecation noise and improve Node.js forward compatibility ([#12073](https://github.com/diegosouzapw/OmniRoute/issues/12073)). ([#12083](https://github.com/diegosouzapw/OmniRoute/pull/12083)) — thanks @xiaoyaner0201 +- **fix(combo):** an operator-set **Agent Features → Context length** on a combo is now honored at request time. The value was persisted and advertised through `/v1/models`, but `resolveComboContextLimit()` never consulted it — so a multi-target combo whose members carry no per-model window fell through to the provider's generic `defaultContextLength` (openrouter 128000, command-code 200000) and rejected large requests with `Input exceeds context window … limit 128000` despite the combo being explicitly sized much larger. An identical single-target combo worked, because it collapses to its concrete target before the guard runs. Invalid values (0/negative/NaN/Infinity) are ignored, so the existing target → combo-min → fallback order is unchanged. ([#12090](https://github.com/diegosouzapw/OmniRoute/pull/12090)) — thanks @adivekar-utexas +- **fix(sse):** passthrough streams now estimate usage on finish when upstream closes without usage even with `stream_options.include_usage` — avoids `0 tokens / 0%` for providers that stay silent (and correctly handles trailing empty-choices usage) ([#12151](https://github.com/diegosouzapw/OmniRoute/pull/12151)) — thanks @maxmad64bis +- **fix(combos):** clearing an agent feature in the combos editor now persists — unchecking context cache protection, or emptying the system message or tool filter, sends an explicit `null` instead of dropping the field from the `PUT` body, which the update merge read as "leave unchanged" ([#12177](https://github.com/diegosouzapw/OmniRoute/pull/12177)) — thanks @foreveryh / @ntdatt812 +- **fix(memory):** self-hosted embedding endpoints now vectorize — the vector width is measured from the first embedding that comes back instead of being read from a registry that cannot describe them, so `vec_memories` is created and memories stop piling up unvectorized behind a green health check ([#12180](https://github.com/diegosouzapw/OmniRoute/pull/12180)) — thanks @kanade-hoshino / @ntdatt812 +- **fix(translator):** the leading `system` message now reaches Responses-API upstreams when its `content` is a content-part array — it was read as `typeof content === "string" ? content : ""`, so a prompt-caching client (Anthropic `cache_control`, the shape LiteLLM and the Anthropic SDK emit) had its entire system prompt replaced by an empty `instructions`. The request was still accepted with a normal `prompt_tokens` count, so the model answered with no instructions and nothing in the response said they were missing. Mid-conversation system turns already handled the array shape ([#7056](https://github.com/diegosouzapw/OmniRoute/pull/7056)); only the first one did not ([#12206](https://github.com/diegosouzapw/OmniRoute/issues/12206)). Regression guard: `tests/unit/translator-openai-responses-system-content-parts.test.ts`. ([#12207](https://github.com/diegosouzapw/OmniRoute/pull/12207)) — thanks @Gorillaz322 +- **fix(free-tier):** `/api/free-tier/summary` no longer computes its totals from a Radar feed built before the catalog the running release ships. When the cached feed is older — or carries no build date at all — the route answers from the shipped catalog, resolved through the operator's local model state so disabled and tombstoned models stay out of the numbers ([#12215](https://github.com/diegosouzapw/OmniRoute/pull/12215)). — thanks @maxmad64bis +- **fix(oauth):** Keep a Claude personal workspace and a Team organization as separate connections — they share the same email and `accountUUID`, so the email-only OAuth dedup let the second login overwrite the first account's tokens; `organizationUUID` now disambiguates them, the way `workspaceId` does for Codex ([#12222](https://github.com/diegosouzapw/OmniRoute/pull/12222)) — thanks @davidebaraldo +- **fix(resilience):** a 402 on a single paid model of a passthrough/gateway provider (e.g. `kilo-gateway`, `ollama-cloud`) no longer terminalizes the whole connection with a never-auto-recovered `credits_exhausted` status — only the paid model is locked out, so free models on the same key keep serving. 402 variant of [#3027](https://github.com/diegosouzapw/OmniRoute/issues/3027). Single-credential providers are unaffected — a 402 there is still treated as the key being genuinely out of credit ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239) / [#10616](https://github.com/diegosouzapw/OmniRoute/issues/10616)) ([#12242](https://github.com/diegosouzapw/OmniRoute/issues/12242)) — thanks @brick30llc-ctrl ([#12266](https://github.com/diegosouzapw/OmniRoute/pull/12266)) — thanks @geekyNads +- **fix(sse):** trust `finish_reason: "length"`/`"max_tokens"` over the reasoning-consumed-token ratio in response quality validation, so a reasoning model truncated below the old 90% threshold correctly fails and retries instead of returning empty content as a silent "success" ([#12262](https://github.com/diegosouzapw/OmniRoute/pull/12262)) — thanks @brick30llc-ctrl +- **fix(settings):** `PUT /api/settings/cache-config` now persists `alwaysPreserveClientCache` to the flat general settings the runtime cache-control policy actually reads; previously the value landed in the databaseSettings "cache" section and was silently ignored, so the endpoint had no effect on `cache_control` passthrough ([#12304](https://github.com/diegosouzapw/OmniRoute/pull/12304)) — thanks @davidebaraldo +- **fix(grok-cli):** treat omitted SuperGrokPro `creditUsagePercent` as 0% used so Provider Limits still renders a weekly bar (proto3 zero-elision) ([#12312](https://github.com/diegosouzapw/OmniRoute/pull/12312)) — thanks @HouMinXi +- **fix(combo):** Expose the two Auto-Combo scoring factors nobody could set — the combo validation schema and the dashboard weight sliders both declared 13 of the scorer's 15 factors, so `connectionDensity` (spreads load across a provider's connections) and `quality` were dropped on save and offered nowhere. The sliders also shipped their own default table that differed from the engine's on every non-zero factor and summed to 1.05, so the percentages shown next to them added up to 105%. Both lists now match `DEFAULT_WEIGHTS`, and a test keeps them there. Note that a combo whose stored `weights` omitted the two keys was effectively running with them at zero and the other thirteen renormalized upward; it now runs with the engine's intended distribution, so its routing does shift ([#12314](https://github.com/diegosouzapw/OmniRoute/pull/12314)) — thanks @maxmad64bis +- **fix(docs):** The free-tier reference no longer says its numbers come "confidence tagged per row" — no catalog entry carries a confidence tag and the API serves none, so every figure on that page is an estimate of the same, unstated quality. The page now states what an entry does vouch for: an independently documented hard stop (set by hand with the source in a comment, never defaulted to `true`) and a prompt-training disclosure, both with live counts the `check:docs-counts` gate keeps honest ([#12318](https://github.com/diegosouzapw/OmniRoute/pull/12318)) — thanks @maxmad64bis +- **fix(usage):** `adobe-firefly` and `firefly` have had a working usage fetcher since Adobe Firefly landed, but neither was ever added to the registration list, so the provider-plugin manifest, `genericQuotaFetcher` and the free-access quota cache all reported them as having no usage support — while `USAGE_SUPPORTED_PROVIDERS` said the opposite. Both are now declared, which also means their credit balance is fetched like any other declared provider's: `registerGenericQuotaFetchers` now registers a generic quota fetcher for them, and `resolveFreeAccessState` no longer returns early. A test holds the registration list to the dispatcher's switch in both directions, which is what the module's own docstring already asked for in prose ([#12321](https://github.com/diegosouzapw/OmniRoute/pull/12321)) — thanks @maxmad64bis +- **fix(quota):** drop the generic quota cache (agy / Antigravity / Claude OAuth) on an upstream 429 so reset-aware scoring does not keep a 60s stale snapshot, and force-refresh the next usage fetch so inner provider caches cannot recache the same window ([#12325](https://github.com/diegosouzapw/OmniRoute/pull/12325)) — thanks @HouMinXi +- **fix(combos):** deleting a combo now clears its persisted LKGP pins instead of leaving unreachable `key_value` rows behind ([#12326](https://github.com/diegosouzapw/OmniRoute/issues/12326)) ([#12425](https://github.com/diegosouzapw/OmniRoute/pull/12425)) — thanks @KrzysiekSko +- **fix(sse):** Keep ZWNJ (U+200C) and ZWJ (U+200D) in assistant text, reasoning and tool-call arguments — Persian/Kurdish half-space (`ارائه‌دهنده`), Arabic/Indic shaping and emoji sequences no longer lose them; the response de-obfuscation now removes joiners only between ASCII word characters, where the request side inserts them ([#12186](https://github.com/diegosouzapw/OmniRoute/issues/12186)) — thanks @rezjalibd ([#12359](https://github.com/diegosouzapw/OmniRoute/pull/12359)) — thanks @pacocartones +- **fix(resilience):** count resolved upstream 5xx results against the provider circuit breaker on the chat path — `CircuitBreaker.execute()` no longer reads a resolved `{ success: false, status: 5xx }` as a success that cancels the call-site failure, so a provider answering 503s now trips its breaker instead of staying `CLOSED` at `failureCount: 1`; single-model and combo dispatches are each accounted exactly once ([#12254](https://github.com/diegosouzapw/OmniRoute/issues/12254)) ([#12360](https://github.com/diegosouzapw/OmniRoute/pull/12360)) — thanks @pacocartones +- **fix(providers):** resolve the Codex quota auto-ping model from the live provider catalog and lifecycle registry instead of the retired `gpt-5.1-codex-mini`, and pause the ping with one actionable warning when no selectable Codex model exists rather than retrying a shut-down id every cooldown window ([#11905](https://github.com/diegosouzapw/OmniRoute/issues/11905)) ([#12361](https://github.com/diegosouzapw/OmniRoute/pull/12361)) — thanks @pacocartones +- **fix(api):** keep the `{created, data}` wrapper on combo-routed `/v1/images/generations` responses and default Codex image results to `b64_json` on both `/v1/images/generations` and `/v1/images/edits` so Codex CLI's built-in `image_gen` can decode them ([#12268](https://github.com/diegosouzapw/OmniRoute/issues/12268)) ([#12362](https://github.com/diegosouzapw/OmniRoute/pull/12362)) — thanks @pacocartones +- **fix(sse):** Name the shadowed custom provider node when a built-in provider id/alias (e.g. `openference` → `of`) reserves the prefix of an existing OpenAI/Anthropic-compatible node, so the runtime `No active credentials for provider: ` error explains that the prefix routed to the built-in and never reached the node's healthy connections, instead of contradicting the dashboard ([#11943](https://github.com/diegosouzapw/OmniRoute/issues/11943)) — thanks @morpheus9393 ([#12365](https://github.com/diegosouzapw/OmniRoute/pull/12365)) — thanks @pacocartones +- **fix(i18n):** wrap `ccOnboardingKeyPlaceholder` in ICU single quotes across all 43 locales so angle brackets render literally instead of being parsed as rich-text tags, which crashed the Claude Code onboarding block with `INVALID_MESSAGE: INVALID_TAG` ([#12302](https://github.com/diegosouzapw/OmniRoute/issues/12302)) ([#12369](https://github.com/diegosouzapw/OmniRoute/pull/12369)) — thanks @gonisulaimann +- **fix(models):** publish `effort_tiers` on Kimi K3's synced base-model entries (`kmca/k3`, `kmca/k3-256k`) so catalog-only clients (OpenCode, plain SDK pickers) can see and select the reasoning tiers (`low`/`high`/`max`) the synced metadata already carried — the `isSkippedEffortProvider` gate no longer suppresses tier visibility on those base entries, while synthetic `-` variant generation stays prevented and Codex/GLM base models remain excluded unchanged ([#12299](https://github.com/diegosouzapw/OmniRoute/issues/12299)) ([#12371](https://github.com/diegosouzapw/OmniRoute/pull/12371)) — thanks @gonisulaimann +- **fix(guardrails):** keep `auto`/`auto/*` virtual combos exempt from the Vision Bridge `fixedModel` credential guard so a combo target is passed through instead of silently falling back to global auto-selection ([#12237](https://github.com/diegosouzapw/OmniRoute/issues/12237)) ([#12373](https://github.com/diegosouzapw/OmniRoute/pull/12373)) — thanks @pacocartones +- **fix(combo):** capability-filter exhaustion caused by `max_tokens` above every target's known output limit now reports that reason (requested `max_tokens` vs the pool's highest known ceiling) instead of the unrelated "supports structured output" message ([#12229](https://github.com/diegosouzapw/OmniRoute/issues/12229)) — thanks @DW-MediaLab ([#12374](https://github.com/diegosouzapw/OmniRoute/pull/12374)) — thanks @pacocartones +- **fix(auth):** the `least-used` account strategy now prefers accounts without backoff before falling back to oldest `lastUsedAt`, the same tie-break `round-robin` already applies, so a failover no longer lands on a just-rate-limited account for a single request ([#12279](https://github.com/diegosouzapw/OmniRoute/issues/12279)) — thanks @tenshiak ([#12375](https://github.com/diegosouzapw/OmniRoute/pull/12375)) — thanks @pacocartones +- **fix(docker):** the `chatgpt-web-codex-browser` image now finds the Chrome binary under `chrome-linux64/` (Chrome for Testing layout in `playwright:v1.62.0-noble`) as well as the legacy `chrome-linux/`, so the container no longer crash-loops with `exec: --headless=new: not found` ([#12024](https://github.com/diegosouzapw/OmniRoute/issues/12024)) ([#12376](https://github.com/diegosouzapw/OmniRoute/pull/12376)) — thanks @pacocartones +- **fix(providers):** declare `groq/compound` and `allam-2-7b` as non-reasoning models in the curated Groq registry so `reasoning_effort` / `output_config.effort` / `thinking` from Claude Code are stripped instead of forwarded, which Groq rejected with HTTP 400 ([#12134](https://github.com/diegosouzapw/OmniRoute/issues/12134)) ([#12379](https://github.com/diegosouzapw/OmniRoute/pull/12379)) — thanks @pacocartones +- **fix(executors):** `OpencodeExecutor` no longer forces a direct connection when the connection has a proxy assigned in Proxy Management but no per-account proxies: the single-account fast path used to wrap the upstream dispatch in the direct-egress sentinel, discarding the ambient proxy context the chat handler had pinned from `proxy_assignments`, so API-key `opencode`/`opencode-go` connections egressed from the host IP (and hit geoblocks) despite the assignment. The direct pin is now applied only when no ambient proxy context exists ([#11894](https://github.com/diegosouzapw/OmniRoute/issues/11894) — thanks @hizzt) ([#12380](https://github.com/diegosouzapw/OmniRoute/pull/12380)) — thanks @pacocartones +- **fix(api):** `GET /v1/models` with `MODELS_CATALOG_PREFIX_MODE=canonical` (or `?prefix=canonical`) now lists providers whose registry alias is undefined or equal to their own id (Antigravity, Antigravity CLI and other self-aliased built-ins) — their single `provider/model` id was dropped by the alias/canonical duplicate guard in the static, synced, custom and alias-backed catalog loops ([#12058](https://github.com/diegosouzapw/OmniRoute/issues/12058)) — thanks @cheynetom ([#12381](https://github.com/diegosouzapw/OmniRoute/pull/12381)) — thanks @pacocartones +- **fix(translator):** Drop replayed `thinking` blocks that carry no signature (the shape produced from cross-provider `reasoning_content`) instead of stamping the default Claude signature on them, which Anthropic rejected with `400 Invalid signature in thinking block` on the next turn served by an Anthropic rung ([#12105](https://github.com/diegosouzapw/OmniRoute/issues/12105)) — thanks @atescivitci-cmd ([#12386](https://github.com/diegosouzapw/OmniRoute/pull/12386)) — thanks @pacocartones +- **fix(cli):** Resolve Bun's `--preload` polyfill path against the package root instead of `dist/`, so `omniroute` installed with `bun install -g` no longer crashes at startup with `error: preload not found …/dist/open-sse/utils/setupPolyfill.ts` ([#11980](https://github.com/diegosouzapw/OmniRoute/issues/11980)) — thanks @joglomedia ([#12387](https://github.com/diegosouzapw/OmniRoute/pull/12387)) — thanks @pacocartones +- **fix(providers):** `gemini-business` now publishes its model catalog — `/v1/models` and `/v1/providers/gemini-business/models` list the 12 enterprise Gemini ids the executor understands instead of returning an empty list (#12107) ([#12389](https://github.com/diegosouzapw/OmniRoute/pull/12389)) — thanks @pacocartones +- **fix(db):** install `busy_timeout` before the SQLite connection's first statement so a process opening the database while another one closes its WAL connection waits out the transient EXCLUSIVE lock instead of dying with `database is locked`, and recognise the drivers' real BUSY/PROTOCOL/IOERR errors as transient in the corruption probe so the same lock no longer renames the database away as corrupt; deflakes `cross-process contenders never both acquire the same connection` (#12394 — thanks @pacocartones) +- **fix(chat-admission):** derive the `chat_admission_busy` 503 `Retry-After` from observed heavyweight-lease occupancy — the larger of the exhausted `OMNIROUTE_CHAT_ADMISSION_QUEUE_MS` window and the time since capacity last turned over, capped at 60 s — instead of a fixed 1 s (structural) / 2 s (byte-stage) hint that invited Codex/agent fan-out clients to re-send ~1 MiB `/v1/responses` bodies every second into a gate held for the whole SSE lifetime; an idle gate keeps the historical floors ([#12135](https://github.com/diegosouzapw/OmniRoute/issues/12135)) (#12395 — thanks @pacocartones) +- **fix(api-manager):** the API key permissions modal no longer silently drops `allowedCombos` entries its Combo picker cannot render — routing-rule names such as `rt-*`, which the backend already honours — when "All" is clicked and the key is switched back to "Restrict"; those entries now survive the toggle, are listed read-only under the combo list so the count and the list agree, and are saved back verbatim instead of persisting `[]` (deny-all) (#12397 — thanks @pacocartones) +- **fix(catalog):** write the NUL separator of the catalog connection memo key, the provider serviceKind memo key, the Video Bridge promotion group key and a JSON-exactness test fixture as the `\u0000` escape instead of a raw byte — same runtime value, but the raw byte made git, GitHub and ripgrep treat those files as binary (hidden PR diffs, silently skipped searches); a guard test now keeps raw NUL bytes out of `src/`, `open-sse/` and `tests/` (#12403 — thanks @pacocartones) +- **fix(providers):** add `CLAUDE_CODE_CLIENT_VERSION` and `GITHUB_COPILOT_CLI_VERSION` env overrides so Anthropic/Copilot client-version gates can be unblocked without a rebuild ([#12417](https://github.com/diegosouzapw/OmniRoute/issues/12417)) ([#12632](https://github.com/diegosouzapw/OmniRoute/pull/12632)) — thanks @HouMinXi +- **fix(db):** back-fill `last_ping_at` and `last_pinged_reset_key` on `provider_connections` during schema reconciliation so divergent lineages that skipped `123_quota_auto_ping` still accept quota auto-ping writes ([#12470](https://github.com/diegosouzapw/OmniRoute/pull/12470) — thanks @KooshaPari) +- **fix(ci):** document MIT exceptions for `@eloqnt/{config,format-json,format-po}` (next-intl transitive; locked tarballs omit `license`) and keep the A2A lifecycle vitest off the real SQLite persistence seam ([#12581](https://github.com/diegosouzapw/OmniRoute/issues/12581)) ([#12595](https://github.com/diegosouzapw/OmniRoute/pull/12595)) +- OpenCode plugin `/v1/models` catalog fetch now waits 30s by default and attaches HTTP `statusCode` on 401/5xx so host fallback plugins can hop instead of seeing an untyped AbortError/UnknownError. ([#12607](https://github.com/diegosouzapw/OmniRoute/pull/12607)) — thanks @RaviTharuma +- **CI:** the OpenAPI security-tier gate now mirrors `isAlwaysProtectedPath()` in full — it also reads `ALWAYS_PROTECTED_API_PATTERNS`, so the pattern-gated credential routes (`/api/providers/{id}/{claude,codex}-auth/{export,apply-local}`, GHSA-5926-2w35-7h4q) no longer report as unannotated. (#12605) +- **fix(api):** GET /v1/models no longer waits forever on a hung coalesced catalog rebuild; cold-path waits are bounded (`CATALOG_BUILD_TIMEOUT_MS`, default 8s) and a last-good 200 is served when the rebuild times out ([#12627](https://github.com/diegosouzapw/OmniRoute/issues/12627)). ([#12628](https://github.com/diegosouzapw/OmniRoute/pull/12628)) — thanks @RaviTharuma +- **fix(cursor):** a non-array `tool_calls` on an assistant message no longer crashes the cursor request translator with a `TypeError`; both loops now require an array ([#12689](https://github.com/diegosouzapw/OmniRoute/issues/12689)) ([#12691](https://github.com/diegosouzapw/OmniRoute/pull/12691)) — thanks @soroush5 +- **fix(chat):** Chat Completions no longer return empty `content` after a server-owned memory or skills tool; the first provider send and account/model recovery now share one pipeline so a follow-up round-trip can fill the reply ([#12696](https://github.com/diegosouzapw/OmniRoute/issues/12696)) — thanks @HouMinXi ([#12867](https://github.com/diegosouzapw/OmniRoute/pull/12867)) +- **fix(dashboard):** batch-deleting provider keys no longer toasts failure after a successful delete when the confirm button's click event is forwarded as `onAfter` ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) — thanks @HouMinXi +- **fix(glm):** drop the extra 16th argument to `createSSETransformStreamWithLogger` that TypeScript rejected (TS2554) and that never reached the TransformStream ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) ([#12770](https://github.com/diegosouzapw/OmniRoute/pull/12770)) — thanks @HouMinXi +- **fix(dashboard):** pass `nodeMap` into Runtime `QuotaGroup` so a quota monitor in error/exhausted/alerting no longer throws `ReferenceError: nodeMap is not defined`. ([#12868](https://github.com/diegosouzapw/OmniRoute/pull/12868)) — thanks @HouMinXi +- Restricted API keys whose `allowedModels` lists a combo name no longer skip every combo member at pre-dispatch (`ALL_TARGETS_SKIPPED`). Inner-target filtering still applies when the allow-list is a provider prefix or `disableNonPublicModels` is on ([#12899](https://github.com/diegosouzapw/OmniRoute/pull/12899)). — thanks @HouMinXi +- **fix(combos):** Effort-suffixed combo members inherit the base model's `model_context_overrides` row so priority order is not inverted on large requests ([#12926](https://github.com/diegosouzapw/OmniRoute/pull/12926)) — thanks @HouMinXi +- **fix(catalog):** Picker-added `customModels` enter the dispatch-time live catalog so combo members and bare inference no longer 400 ([#12934](https://github.com/diegosouzapw/OmniRoute/pull/12934)) — thanks @HouMinXi +- **fix(docker):** re-derive the Docker build's worker-pool memory budget from the MEASURED ~4.5 GB per-process RSS (the issue owner's own VPS dmesg OOM-killer reproduction) instead of the stale 2560 MB/worker inference #11419 shipped, and lower `OMNIROUTE_BUILD_WORKERS` 3 → 2 so 1 parent + 1 page-data worker (2 processes × 4.5 GB = 9 GB) fits the 12.288 GB (75%) budget on a 16 GB GitHub Actions runner — the previous default (1 parent + 2 workers = 13.5 GB) still overcommitted the runner and kept "Publish to Docker Hub" failing with `cannot allocate memory` after #11419 merged (#7518). ([#11663](https://github.com/diegosouzapw/OmniRoute/pull/11663)) +- **fix(electron):** the packaged Windows desktop build now passes the #7592 cold-restart smoke end to end. Five defects were found and fixed on the way: (1) optional-pack staging failed on any machine whose `tar` is GNU tar — it reads the drive letter in an absolute `-f C:\...` archive path as a remote rsh host (`Cannot connect to C:`), so staging now passes a bare filename with `cwd` at the tarball directory and surfaces tar stderr on failure; (2) the Electron `files` allowlist was missing `lib/loginHeaderCapture.js`, so the packaged main process crashed at startup with `Cannot find module './lib/loginHeaderCapture'`; (3) electron-builder ≥26 injects an `!**/node_modules/**` ignore into every extraResources pattern list that no positive filter can override, which silently dropped the staged runtime `node_modules` (including the better-sqlite3 N-API prebuild) from `resources/app` — a fresh v3.8.50 desktop build would have shipped with no native SQLite driver and reproduced the #7592 sql.js fallback on every machine; an `afterPack` hook now restores the staged `node_modules` after packing; (4) the packaged-app smoke harness redirected `USERPROFILE` into its temp DATA_DIR, but Electron resolves the Roaming profile from `%USERPROFILE%\AppData\Roaming\` (USERPROFILE takes precedence over the APPDATA env var) and the path service throws instead of creating it, so `requestSingleInstanceLock()` returned false and the app exited(0) silently before `app.whenReady()` — the harness now pre-creates the derived tree, and `ensureSmokeEnvDirs` is exported and unit-tested; (5) the #7592 driver assertion parsed a `[DB] Driver: ...` line that the server's primary DB path never printed (only the unused `openDatabaseAsync()` did) — `getDbInstance()` now emits the same line on open so the guard can actually assert the native driver. Also: the smoke env-allowlist unit test hardcoded POSIX paths and could only pass on Linux/macOS; expectations are now host-agnostic, with new regression tests for the USERPROFILE-derived tree and `tarPack` under absolute Windows-style paths ([#11443](https://github.com/diegosouzapw/OmniRoute/pull/11443)) — thanks @jonlwheat2-gif +- fix(sse): stop the auto-combo candidates inspector from silently dropping model-locked/cooled-down rows (#9133) ([#11994](https://github.com/diegosouzapw/OmniRoute/pull/11994)) — thanks @hartmark +- **fix(providers):** Perplexity Web no longer turns upstream stream failures into successful assistant text; pre-content failures remain eligible for fallback, partial output ends with a structured sanitized error, and failed sessions are not persisted ([#12465](https://github.com/diegosouzapw/OmniRoute/pull/12465)) +- **fix(video-bridge):** preserve chronological video and transcript fusion, source-frame timestamps, transcript provenance, full contact-sheet intervals, and pre-abort callback isolation ([#11681](https://github.com/diegosouzapw/OmniRoute/pull/11681)) +- **Z.ai Web:** HTTP 200 streams carrying an upstream error now terminate with a structured failure instead of assistant text plus a normal stop, preserving partial output while allowing pre-content combo fallback. ([#12454](https://github.com/diegosouzapw/OmniRoute/pull/12454)) +- Absorb `Error [AbortError]: request_signal_aborted` and DOMException AbortError shapes in the process-level client-abort crash guard so routine client disconnects no longer kill the server (exit code 7). ([#12165](https://github.com/diegosouzapw/OmniRoute/pull/12165)) — thanks @alvinveroy +- **fix(sse):** Treat Adapta Web `type:error` SSE events as sanitized non-stream failures instead of empty HTTP 200 completions. ([#12459](https://github.com/diegosouzapw/OmniRoute/pull/12459)) +- **fix(autoCombo):** layer 3 (`models_dev_tier`) no longer scores retired models as premium, merge cross-provider capability rows deterministically, and inherit tier scores through variant ids ([#11508](https://github.com/diegosouzapw/OmniRoute/issues/11508)). `getModelsDevTierFitness` now vetoes ids present in `config/quality/model-lifecycle.json` with status `retired` before any other signal; `loadModelCapabilities()` aggregates per `model_id` (any-non-null-true for booleans, max for `limit_context`) instead of last-write-wins over SQLite's undefined row order; a capabilities miss retries through `resolveScoresAs()`'s base id and reports `models_dev_tier:inherited`, matching layers 1–2 (#11492). Adds `getModelsDevTierFitnessWithSource()` and an `invalidateCapabilitiesCache()` test/ops hook. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) — thanks @Meet6338-X +- fix(sse): accept Claude `content:[]` + `stop_reason: max_tokens`/`tool_use` in `detectMalformedNonStream` (match `isEmptyContentResponse`) so Claude Code `/model` probes with `max_tokens: 1` no longer become a false 502 ([#11568](https://github.com/diegosouzapw/OmniRoute/pull/11568)) — thanks @Seramicx +- **fix(security):** sanitize `request.failed` diagnostics before publishing them to live dashboard listeners and replay history, while keeping status, model, provider, latency, and internal call-log diagnostics intact. ([#12469](https://github.com/diegosouzapw/OmniRoute/pull/12469)) +- **fix(pwa):** do not intercept dashboard navigations so Chrome can fall back from a stale HTTP/3 Alt-Svc advertisement (UDP :20128 is unpublished; F5 on a long-lived tab hung until a new tab opened a fresh TCP connection). ([#12767](https://github.com/diegosouzapw/OmniRoute/pull/12767)) — thanks @HouMinXi +- **fix(executors):** handle DuckDuckGo ERR_BN_LIMIT (418) without retrying — when the upstream returns `418 ERR_BN_LIMIT` (rate-limit/ban), the executor now returns the error immediately instead of burning another VQD acquisition that would only count against the IP limit. The retry logic for `418 ERR_CHALLENGE` (unsolved challenge) remains unchanged. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) ([#12110](https://github.com/diegosouzapw/OmniRoute/pull/12110)) — thanks @oyi77 / @Meet6338-X +- Fixed DuckDuckGo streaming responses losing JSON lines and UTF-8 characters split across network chunks. ([#11528](https://github.com/diegosouzapw/OmniRoute/pull/11528)) — thanks @pacocartones +- Harden generated Cloudflare, Vercel, and Deno relays by stripping hop-by-hop, framing, and proxy authentication headers before forwarding requests upstream. ([#11533](https://github.com/diegosouzapw/OmniRoute/pull/11533)) — thanks @pacocartones +- **feat(grok-cli):** Provider Limits shows grok-cli banked reset credits from `GetRemainingResets` (including a real zero; a failed RPC omits the row) and the existing View credits button now calls `ConsumerUiSvc/RedeemReset` for grok-cli. Live tokens use nested fields 10/20/30 (id + Timestamp), not compact 1/2/3. ([#12805](https://github.com/diegosouzapw/OmniRoute/pull/12805)) — thanks @HouMinXi +- **fix(memory):** Embedding Model Quick select, Embedding Source remote dropdown, and Rerank selector now list every configured provider with embedding/rerank support instead of only chat-catalog text matches plus OpenRouter live discovery; a generic OpenAI-compatible `/embeddings` + Cohere-compatible `/rerank` runtime fallback resolves any configured chat provider's embedding/rerank endpoint, so unlisted providers no longer fail with "Unknown embedding provider"; both memory selectors gained a free-text model override ([#11390](https://github.com/diegosouzapw/OmniRoute/pull/11390)) — thanks @rqzbeh +- Harden SQLite upgrades around the historical migration-074 version collision: missing discovery and inspector tables are replayed atomically, pre-existing databases (including setup-created skeletons) receive reusable content-addressed safety snapshots, and Node test/eval probes without `DATA_DIR` are isolated from the operator database. ([#12435](https://github.com/diegosouzapw/OmniRoute/pull/12435)) +- **fix(dashboard):** Moonshot/Kimi Open Platform voucher and cash leftover percentages follow the bucket balance, so an empty wallet no longer paints those rows as 100% while Available is 0% ([#12733](https://github.com/diegosouzapw/OmniRoute/pull/12733)) — thanks @HouMinXi +- **fix(combo):** return non-retryable HTTP 400 when all candidates for a pinned native Codex turn are unavailable due to model-scoped lockout, terminating the turn cleanly while preserving turn continuity and enabling standard Combo routing on subsequent turns ([#12240](https://github.com/diegosouzapw/OmniRoute/pull/12240)) — thanks @mdigitalbh81 +- Fixed auto-router strategies preserving the selected connection through ranking and dispatch when multiple connections share the same provider and model. ([#11530](https://github.com/diegosouzapw/OmniRoute/pull/11530)) — thanks @pacocartones +- Preserve Chat Completions JSON-object response formats when translating requests to the Responses API. ([#11529](https://github.com/diegosouzapw/OmniRoute/pull/11529)) — thanks @pacocartones +- Fix idempotency fingerprints so Responses API requests include `input` and other semantic generation fields using stable serialization, while excluding credentials and request metadata. ([#11532](https://github.com/diegosouzapw/OmniRoute/pull/11532)) — thanks @pacocartones +- **fix(api):** Generated API CLI commands now enforce required OpenAPI request bodies; Combo test commands forward the required `comboName` body, while API keys created by older writers after migration 149 preserve legacy allow-all Combo access without widening explicit empty allowlists — thanks @marcelokarval ([#12070](https://github.com/diegosouzapw/OmniRoute/pull/12070)) +- **fix(grok-web):** treat upstream streaming failures as failures instead of successful assistant text: error-only streams now fail readiness with HTTP 502, while failures after legitimate content preserve that partial output and terminate through the sanitized stream failure path without a normal `stop` completion. ([#12458](https://github.com/diegosouzapw/OmniRoute/pull/12458)) +- HuggingChat now turns HTTP 200 JSONL generation failures into a sanitized 502 before content, or a fixed public stream failure after partial output, so fallback and request persistence no longer record a false successful stop. ([#12456](https://github.com/diegosouzapw/OmniRoute/pull/12456)) +- **fix(providers):** keep 1min.ai HTTP 200 stream errors out of assistant content, preserve partial output, and expose sanitized terminal errors so pre-content failures can fall back. ([#12466](https://github.com/diegosouzapw/OmniRoute/pull/12466)) +- **fix(sessions):** preserve legacy Sessions data when the additive exclusive-session projection is unavailable, returning an empty projection and warning only once per contiguous outage instead of failing the endpoint; the Sessions badge intentionally reflects the merged legacy and exclusive row count introduced by [#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389) ([#11469](https://github.com/diegosouzapw/OmniRoute/pull/11469)) — thanks @KaspaPulse +- Render Nimble Search with the generic provider icon and serve Opper's proven logo locally. ([#12415](https://github.com/diegosouzapw/OmniRoute/pull/12415)) +- **fix(quality):** `config/quality/provider-assets-provenance.jsonl` was stale against the physical `public/providers/` tree (still listing 225 assets and 5 duplicate-content groups after several provider-retirement PRs removed 85 icon files), which would have made `check:provider-asset-provenance` fail on the next full run. Reconciled the manifest to the real 142-file snapshot (142 asset records, 1 duplicate-content group, 2 newly-registered files marked `unresolved` pending provenance research), repinned `auditedCommit`, and updated the regression test's hardcoded counts to match. ([#11876](https://github.com/diegosouzapw/OmniRoute/pull/11876)) +- fix(ui): wrap quota card footer action buttons to prevent clipping Refresh button on narrow cards — when extra action buttons such as `View credits` are present on Codex cards, allow the footer row to flex-wrap with `shrink-0` buttons so `Refresh now` remains visible and clickable across all responsive card widths ([#11464](https://github.com/diegosouzapw/OmniRoute/pull/11464)) — thanks @adevwithpurpose +- Keep Antigravity Gemini usable when the same connection's Claude weekly quota is empty; generic quota cache stays per-connection for every other provider. ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) — thanks @HouMinXi +- Fixed Tailscale tunnel setup to reject ports outside the valid 1-65535 range and safely install Windows MSI packages from paths containing apostrophes. ([#11534](https://github.com/diegosouzapw/OmniRoute/pull/11534)) — thanks @pacocartones +- Classify Cloudflared and Tailscale process-management routes as local-only and spawn-capable while preserving remote access to read-only tunnel status endpoints. ([#11531](https://github.com/diegosouzapw/OmniRoute/pull/11531)) — thanks @pacocartones +- Electron release workflow: the `publish-npm` job now grants `actions: read` to the reusable `npm-publish.yml` it calls (its `publish` job requests it), which is what made GitHub refuse the whole v3.8.50 run at startup and ship the release with zero desktop assets; a `workflow_dispatch` now builds the requested tag instead of the dispatching branch and can skip the npm leg (`publish_npm=false`) when only re-attaching assets ([#11974](https://github.com/diegosouzapw/OmniRoute/pull/11974)) +- **fix(dashboard):** The Combos page usage guide now reads its dismissal through `useSyncExternalStore` instead of correcting SSR state inside an effect, removing an extra commit of the page tree on every load (and the `react-hooks/set-state-in-effect` error it raised). ([#12671](https://github.com/diegosouzapw/OmniRoute/pull/12671)) +- Electron release: `electron/package-lock.json` regained the optional `electron-builder-squirrel-windows` subtree (13 entries) that `npm ci` had been refusing as out of sync, `electron-release.yml` gained a `build_ref` dispatch input and stops regenerating release notes on a re-attach dispatch, and the npm publish workflow attaches the SBOM to the GitHub Release on dispatch publishes too — so the v3.8.51 tag ships every desktop asset and the SBOM like v3.8.49 did ([#12022](https://github.com/diegosouzapw/OmniRoute/pull/12022)) +- Drained the reds every PR against `release/v3.8.51` was born with: documented the three service-worker build-id variables, registered the six retirement/tunnel tests with the mutation gate, approved `eslint-plugin-react-hooks` in the dependency allowlist, added the six `combo.sort.*` strings to `vi` and `pt-BR`, pointed the ChatGPT Web doc at the real migration-168 test, worded the g4f hint around the member key, and realigned four tests to the retired-provider catalog and the legacy-schema fixtures the retirement migrations touch. ([#11940](https://github.com/diegosouzapw/OmniRoute/pull/11940)) +- **fix(providers):** Claude, Grok, LMArena, Notion, and Perplexity web-cookie transports now use pooled `wreq-js` 3.2 instead of the native sidecar, with all nine supported bindings pinned and audited, and the applicable platform binding plus native-license evidence included in each release artifact ([#12429](https://github.com/diegosouzapw/OmniRoute/pull/12429), supersedes [#11753](https://github.com/diegosouzapw/OmniRoute/pull/11753)). +- **fix(providers):** Zed Hosted streaming failures now trigger fallback before content and end partial streams with a sanitized structured error instead of fake assistant text and a normal-success stop. ([#12455](https://github.com/diegosouzapw/OmniRoute/pull/12455)) +- **fix(test):** capture hook result after render (direct commit `b85d0abb0b`) +- **fix(sync):** repair the two regressions the v3.8.50 sync-back left on release/v3.8.51 (direct commit `5ade9e0851`) +- **revert(models):** drop the memo reset hook ported from #10055 (direct commit `282de841ae`) +- **fix(typecheck):** guard the optional connectionId before the reactive model sync (direct commit `f2825bad75`) +- **fix(quota-share):** release the winner's reserved in-flight slot (#11371) ([#11408](https://github.com/diegosouzapw/OmniRoute/pull/11408)) — thanks @oyi77 +- **fix(providers):** stop silently dropping reasoning effort on opencode families (#10788) ([#11409](https://github.com/diegosouzapw/OmniRoute/pull/11409)) — thanks @oyi77 +- **fix(combo):** enable genuine quota-aware routing for generic providers (antigravity, claude, etc.) ([#11411](https://github.com/diegosouzapw/OmniRoute/pull/11411)) — thanks @HouMinXi +- **fix(oauth):** allow expired token retry before deactivation and activate expired retry loop (#P0) ([#11414](https://github.com/diegosouzapw/OmniRoute/pull/11414)) — thanks @HouMinXi +- **perf(executors):** lazy-load the executor registry — defer class imports + construction to first use (#11220) ([#11421](https://github.com/diegosouzapw/OmniRoute/pull/11421)) — thanks @oyi77 +- **fix:** make release/v3.8.50 CI gates green (run 32786966560 root causes) ([#11450](https://github.com/diegosouzapw/OmniRoute/pull/11450)) — thanks @jonlwheat2-gif +- **fix(sse):** exact-domain cookie match and origin-equality URL assertions ([#11452](https://github.com/diegosouzapw/OmniRoute/pull/11452)) — thanks @hartmark +- **fix(providers):** send Responses API payload during OpenAI-compatible validation ([#11454](https://github.com/diegosouzapw/OmniRoute/pull/11454)) — thanks @tuandinh0801 +- **fix(command-code):** fallback to /alpha/generate for Go plan without Provider API access ([#11455](https://github.com/diegosouzapw/OmniRoute/pull/11455)) — thanks @Chewji9875 +- **fix(responses-continuation):** fail closed on a log-truncated stored input/output array ([#11473](https://github.com/diegosouzapw/OmniRoute/pull/11473)) — thanks @hartmark +- **fix(cli):** write jcode settings as [providers.omniroute] in config.toml ([#11484](https://github.com/diegosouzapw/OmniRoute/pull/11484)) — thanks @hongnoul +- **fix(logging):** raise chat-log array truncation cap to 1000 and unify duplicate implementations ([#11499](https://github.com/diegosouzapw/OmniRoute/pull/11499)) — thanks @hartmark +- **fix:** restore live websocket URL helpers ([#11509](https://github.com/diegosouzapw/OmniRoute/pull/11509)) — thanks @PauloFH +- **perf(core):** eliminate async waterfalls across home, cache, and db operations (#11396) ([#11512](https://github.com/diegosouzapw/OmniRoute/pull/11512)) — thanks @HouMinXi +- **fix(kiro):** add runtime.us-east-1.kiro.dev as first-attempt endpoint ([#11517](https://github.com/diegosouzapw/OmniRoute/pull/11517)) — thanks @rifqiawl +- **fix(providers):** remove duplicate 128-tool truncation in OpencodeExecutor (#11444) ([#11518](https://github.com/diegosouzapw/OmniRoute/pull/11518)) — thanks @HouMinXi +- **fix(providers):** poll onboardUser LRO before classifying BYOP ([#11519](https://github.com/diegosouzapw/OmniRoute/pull/11519)) — thanks @rifqiawl +- **fix(translator):** preserve exact tool name casing for Claude tool_use payloads (#11487) ([#11520](https://github.com/diegosouzapw/OmniRoute/pull/11520)) — thanks @HouMinXi +- **fix(kimi-web):** align health probe with executor domain ([#11521](https://github.com/diegosouzapw/OmniRoute/pull/11521)) — thanks @pacocartones +- **fix(cli):** preserve existing admin password during setup ([#11522](https://github.com/diegosouzapw/OmniRoute/pull/11522)) — thanks @pacocartones +- **fix(mitm):** keep Windows DNS elevation off the POSIX sudo path (#11430) ([#11542](https://github.com/diegosouzapw/OmniRoute/pull/11542)) — thanks @official-burak +- **fix(sse):** scale chat admission by ingest byte budget instead of a fixed request count ([#11548](https://github.com/diegosouzapw/OmniRoute/pull/11548)) — thanks @TheDemonTuan +- **fix:** accept Gemini Web cookie exports ([#11554](https://github.com/diegosouzapw/OmniRoute/pull/11554)) — thanks @rafacpti23 +- **fix(i18n):** complete Brazilian Portuguese translations ([#11558](https://github.com/diegosouzapw/OmniRoute/pull/11558)) — thanks @rafacpti23 +- **fix(sse):** explicit types for openai-responses pureHelpers — clears last failing core typecheck gate ([#11567](https://github.com/diegosouzapw/OmniRoute/pull/11567)) — thanks @jonlwheat2-gif +- **fix(opencode):** hydrate Proxy Pool references for no-auth connections ([#11584](https://github.com/diegosouzapw/OmniRoute/pull/11584)) — thanks @AStupidBear +- **fix(ci):** resolve inherited release/v3.8.51 base-red CI failures ([#11608](https://github.com/diegosouzapw/OmniRoute/pull/11608)) — thanks @jonlwheat2-gif +- **fix(db):** fallback load STORAGE_ENCRYPTION_KEY from env files ([#11614](https://github.com/diegosouzapw/OmniRoute/pull/11614)) — thanks @benzntech +- **fix(providers):** mark Brave Search as no longer having a free tier ([#11621](https://github.com/diegosouzapw/OmniRoute/pull/11621)) — thanks @turbolego +- **fix:** pass tool result content through as string in OpenAI/Claude -> Gemini translation ([#11624](https://github.com/diegosouzapw/OmniRoute/pull/11624)) — thanks @Hsia97 +- **fix(sse):** scope credential health to exact key ([#11628](https://github.com/diegosouzapw/OmniRoute/pull/11628)) — thanks @KaspaPulse +- **fix(install):** guard prepare script against missing husky ([#11634](https://github.com/diegosouzapw/OmniRoute/pull/11634)) — thanks @Prajeeth-12 +- **fix(security):** redact NVIDIA_API_KEY at the log sink in the ad-hoc diag script ([#11639](https://github.com/diegosouzapw/OmniRoute/pull/11639)) +- **fix(types):** clear the two type regressions red on release/v3.8.51 ([#11640](https://github.com/diegosouzapw/OmniRoute/pull/11640)) — thanks @jonlwheat2-gif +- **fix(build):** resolve workspace: protocol dependencies to concrete semver versions during prepublish (#11540) ([#11641](https://github.com/diegosouzapw/OmniRoute/pull/11641)) — thanks @HouMinXi +- **fix(search):** properly resolve configured search connection in /v1/responses pipeline (#11524) ([#11642](https://github.com/diegosouzapw/OmniRoute/pull/11642)) — thanks @HouMinXi +- **fix(authz):** trust cf-connecting-ip for via-proxy detection when peer is Cloudflare edge (#11514) ([#11643](https://github.com/diegosouzapw/OmniRoute/pull/11643)) — thanks @HouMinXi +- **fix(security):** compare the OIDC state cookie in constant time ([#11646](https://github.com/diegosouzapw/OmniRoute/pull/11646)) +- **fix(security):** namespace the dedup hash by the calling API key ([#11649](https://github.com/diegosouzapw/OmniRoute/pull/11649)) +- **fix(models):** gate the Codex-native catalog loop on prefix mode (#11632) ([#11662](https://github.com/diegosouzapw/OmniRoute/pull/11662)) — thanks @xiaoyaner0201 +- **fix(dashboard):** project Codex routing quota in account pools ([#11666](https://github.com/diegosouzapw/OmniRoute/pull/11666)) — thanks @KaspaPulse +- **fix(ddgw):** normaliza system no upstream da release v3.8.51 ([#11692](https://github.com/diegosouzapw/OmniRoute/pull/11692)) — thanks @RhianB14 +- **fix(memory):** honor category filter in GET /api/memory ([#11699](https://github.com/diegosouzapw/OmniRoute/pull/11699)) — thanks @Sabeekhann +- **fix(ci):** detect stale workspace lockfile entries ([#11714](https://github.com/diegosouzapw/OmniRoute/pull/11714)) — thanks @pacocartones +- **fix(ci):** stamp dist/BUILD_SHA before the npm publish provenance gate ([#11721](https://github.com/diegosouzapw/OmniRoute/pull/11721)) +- **fix(docker):** decouple the best-effort Bun image from the release manifest ([#11724](https://github.com/diegosouzapw/OmniRoute/pull/11724)) +- **perf(compression):** avoid cloning memo misses twice ([#11727](https://github.com/diegosouzapw/OmniRoute/pull/11727)) — thanks @RaviTharuma +- **fix(docker):** let the bun digest artifact be absent, not fatal ([#11740](https://github.com/diegosouzapw/OmniRoute/pull/11740)) +- **fix(shared):** block cloud-metadata hosts under default remote-image guard ([#11755](https://github.com/diegosouzapw/OmniRoute/pull/11755)) — thanks @santosraju99-hub +- **fix(api):** preserve caller-provided X-Correlation-Id on chat completions ([#11760](https://github.com/diegosouzapw/OmniRoute/pull/11760)) — thanks @aniruddhaadak80 +- **fix(resilience):** lock permanently retired models instead of short backoff (Gemini ban prevention) ([#11762](https://github.com/diegosouzapw/OmniRoute/pull/11762)) — thanks @turbolego +- **fix:** mark Vercel AI Gateway as passthroughModels ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **fix(resilience):** stop hammering permanently-moved endpoints and billing-suspended accounts ([#11774](https://github.com/diegosouzapw/OmniRoute/pull/11774)) — thanks @turbolego +- **fix(lease):** remove global static reservation and gate routing on live active lease occupancy ([#11775](https://github.com/diegosouzapw/OmniRoute/pull/11775)) — thanks @TheDemonTuan +- **fix(ci):** size the install-upgrade gate to a measured run, and log the pack cost ([#11776](https://github.com/diegosouzapw/OmniRoute/pull/11776)) +- **fix(pwa):** stop serving the stale shell after deploys ([#11779](https://github.com/diegosouzapw/OmniRoute/pull/11779)) — thanks @HouMinXi +- **fix(combo):** lock GitHub models rejected as "not supported" for future requests ([#11781](https://github.com/diegosouzapw/OmniRoute/pull/11781)) — thanks @turbolego +- **fix(cli):** probe both IPv4 and IPv6 loopback for server readiness (#11766) ([#11794](https://github.com/diegosouzapw/OmniRoute/pull/11794)) — thanks @vermasomesh835 +- **fix(sse):** preserve CLIProxyAPI wrappers per connection ([#11797](https://github.com/diegosouzapw/OmniRoute/pull/11797)) — thanks @RaviTharuma +- **fix(providers):** honor model auto-fetch opt-in on create ([#11798](https://github.com/diegosouzapw/OmniRoute/pull/11798)) — thanks @RaviTharuma +- **fix(dashboard):** make model auto-fetch order independent ([#11805](https://github.com/diegosouzapw/OmniRoute/pull/11805)) — thanks @RaviTharuma +- **fix(docker):** make GHA cache exports best effort ([#11806](https://github.com/diegosouzapw/OmniRoute/pull/11806)) — thanks @RaviTharuma +- **fix(kiro):** do not permanently ban on 'User is not authorized to make this call' ([#11809](https://github.com/diegosouzapw/OmniRoute/pull/11809)) — thanks @Deftera186 +- **fix(sse):** keep cache-write tokens in OpenAI-shaped usage ([#11814](https://github.com/diegosouzapw/OmniRoute/pull/11814)) — thanks @TheDemonTuan +- **fix(docs):** resolve relative markdown and wiki links across Fumadocs and GitHub wiki ([#11834](https://github.com/diegosouzapw/OmniRoute/pull/11834)) — thanks @AndrianBalanescu +- **fix(chat):** preserve unstripped model string for passthrough provider routing ([#11840](https://github.com/diegosouzapw/OmniRoute/pull/11840)) — thanks @raheemuddin786 +- **fix(mcp):** avoid async initialization inside sync esm wrappers (#11569) ([#11849](https://github.com/diegosouzapw/OmniRoute/pull/11849)) — thanks @HouMinXi +- **fix(sse):** preserve coherent markdown token boundaries across stream translation chunks (#11606) ([#11851](https://github.com/diegosouzapw/OmniRoute/pull/11851)) — thanks @HouMinXi +- **fix(ci):** run the install-upgrade gate on disk, not on the /tmp tmpfs ([#11855](https://github.com/diegosouzapw/OmniRoute/pull/11855)) +- **fix(skills):** expand shorthand property types in injected tool schemas ([#11857](https://github.com/diegosouzapw/OmniRoute/pull/11857)) — thanks @alvinveroy +- **fix(ci):** publish npm from a hosted runner so provenance is accepted ([#11877](https://github.com/diegosouzapw/OmniRoute/pull/11877)) +- **fix(lint):** pin react hooks plugin to 7.0.1 ([#11886](https://github.com/diegosouzapw/OmniRoute/pull/11886)) +- **fix(ci):** surface and clear release-green ESLint failure ([#11890](https://github.com/diegosouzapw/OmniRoute/pull/11890)) — thanks @MumuTW +- **fix(guardrails):** prevent duplicate prompt-injection-guard log output ([#11936](https://github.com/diegosouzapw/OmniRoute/pull/11936)) — thanks @patrykkopycinski +- **fix(auth):** downgrade expected transient states from warn to debug ([#11937](https://github.com/diegosouzapw/OmniRoute/pull/11937)) — thanks @patrykkopycinski +- **fix(codex):** fail over image generation for imported free plans ([#11948](https://github.com/diegosouzapw/OmniRoute/pull/11948)) — thanks @Bl0ck154 +- **fix(codex):** normalize non-stream responses ([#11951](https://github.com/diegosouzapw/OmniRoute/pull/11951)) — thanks @Bl0ck154 +- **fix(images):** forward Antigravity image size ([#11952](https://github.com/diegosouzapw/OmniRoute/pull/11952)) — thanks @Bl0ck154 +- **fix(db):** invalidate connection cache after upsert ([#11953](https://github.com/diegosouzapw/OmniRoute/pull/11953)) — thanks @Bl0ck154 +- **fix(codex):** restore imported account state ([#11954](https://github.com/diegosouzapw/OmniRoute/pull/11954)) — thanks @Bl0ck154 +- **fix(sse):** give extended-thinking targets the reasoning readiness budget ([#11959](https://github.com/diegosouzapw/OmniRoute/pull/11959)) — thanks @abhisheksharma2411 +- **fix(dashboard):** use opaque background and readable text color on cost chart tooltips ([#11960](https://github.com/diegosouzapw/OmniRoute/pull/11960)) — thanks @ZaimMarzuki +- **fix(antigravity):** send complete loadCodeAssist metadata (ideType/platform/pluginType as numeric enums) ([#11969](https://github.com/diegosouzapw/OmniRoute/pull/11969)) — thanks @rifqiawl +- **fix(core):** resolve DB init race condition and reasoning translation ([#12003](https://github.com/diegosouzapw/OmniRoute/pull/12003)) — thanks @raheemuddin786 +- **fix(admission):** exclude reclaimable page cache from the cgroup pressure ratio ([#12017](https://github.com/diegosouzapw/OmniRoute/pull/12017)) — thanks @HouMinXi +- **fix(build):** prune non-production trees in NFT trace excludes and tsconfig ([#12028](https://github.com/diegosouzapw/OmniRoute/pull/12028)) — thanks @Chewji9875 +- **fix(migrations):** add renamed migration compatibility for 056/073/077/101 ([#12036](https://github.com/diegosouzapw/OmniRoute/pull/12036)) — thanks @oyi77 +- **fix(sse):** default crash-guard logger to console.warn, not console ([#12042](https://github.com/diegosouzapw/OmniRoute/pull/12042)) — thanks @HouMinXi +- **fix(catalog):** derive vision/modalities for built-in auto combos from effective target pool ([#12046](https://github.com/diegosouzapw/OmniRoute/pull/12046)) — thanks @Prajeeth-12 +- **fix(release):** the packaged-app smoke verifies the database opened, not a driver line the primary path never prints (twin of #12032) ([#12047](https://github.com/diegosouzapw/OmniRoute/pull/12047)) +- **fix(barrel):** migrate src/app/ off the @/lib/localDb barrel import (#11795 Phase 2) ([#12051](https://github.com/diegosouzapw/OmniRoute/pull/12051)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate src/lib/ off the localDb barrel to direct db imports (#59) ([#12052](https://github.com/diegosouzapw/OmniRoute/pull/12052)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate open-sse, src/shared, src/sse, src/models, src/domain off the @/lib/localDb barrel import (#11795 Phase 4) ([#12053](https://github.com/diegosouzapw/OmniRoute/pull/12053)) — thanks @jonlwheat2-gif +- **fix(barrel):** delete the @/lib/localDb barrel — every consumer migrated (#11795 Phase 5) ([#12055](https://github.com/diegosouzapw/OmniRoute/pull/12055)) — thanks @jonlwheat2-gif +- **fix(radar):** restore D12 public boundary ([#12057](https://github.com/diegosouzapw/OmniRoute/pull/12057)) +- **fix(dev):** bound webpack and Tailwind scans ([#12075](https://github.com/diegosouzapw/OmniRoute/pull/12075)) — thanks @backryun +- **fix(dev):** isolate root layout settings reads ([#12076](https://github.com/diegosouzapw/OmniRoute/pull/12076)) — thanks @backryun +- **fix(dev):** reduce instrumentation executor fan-out (phase 3) ([#12078](https://github.com/diegosouzapw/OmniRoute/pull/12078)) — thanks @backryun +- **fix(dev):** make logging resources HMR-singleton ([#12079](https://github.com/diegosouzapw/OmniRoute/pull/12079)) — thanks @backryun +- **fix(sse):** keep unavailable forced connections scoped ([#12080](https://github.com/diegosouzapw/OmniRoute/pull/12080)) — thanks @keeltrace +- **fix(dev):** isolate batch dispatch from instrumentation ([#12081](https://github.com/diegosouzapw/OmniRoute/pull/12081)) — thanks @backryun +- **fix(ci):** reconcile release test contract drift ([#12082](https://github.com/diegosouzapw/OmniRoute/pull/12082)) — thanks @backryun +- **fix(ci):** accept CVE-2025-68121 in the prebuilt tls-client .so, auto-close base-red issues, guard Scorecard on the default branch ([#12085](https://github.com/diegosouzapw/OmniRoute/pull/12085)) +- **fix(db):** drop three consumer-less 1proxy exports — dead-code base-red on release/v3.8.51 after the barrel deletion (#12055) ([#12087](https://github.com/diegosouzapw/OmniRoute/pull/12087)) +- **fix(api):** clear the six API-route TypeScript regressions the new gate landed red on ([#12094](https://github.com/diegosouzapw/OmniRoute/pull/12094)) +- **fix(release):** never let the tag-push Create Release append auto notes to the curated body ([#12096](https://github.com/diegosouzapw/OmniRoute/pull/12096)) +- **fix(sse):** honor CLIProxyAPI environment API key ([#12099](https://github.com/diegosouzapw/OmniRoute/pull/12099)) — thanks @RaviTharuma +- **fix(oauth):** bind Google refresh to the client that issued the token ([#12106](https://github.com/diegosouzapw/OmniRoute/pull/12106)) — thanks @HouMinXi +- **fix(dashboard):** make RequestLoggerDetail loadable outside Next — CSS via globals.css + CJS/ESM interop (#11703 base-reds) ([#12114](https://github.com/diegosouzapw/OmniRoute/pull/12114)) +- **fix(leases):** project status lease row to lease columns so joined connection PII never escapes ([#12115](https://github.com/diegosouzapw/OmniRoute/pull/12115)) — thanks @geek007git +- **fix(plugins):** do not kill the plugin process when a fire-and-forget hook times out ([#12116](https://github.com/diegosouzapw/OmniRoute/pull/12116)) — thanks @geek007git +- **fix(guardrails):** restore injection-guard logging on middleware-only routes ([#12117](https://github.com/diegosouzapw/OmniRoute/pull/12117)) — thanks @geek007git +- **fix(plugins):** refresh stored manifest from disk on activate so new hook fields reach existing installs ([#12120](https://github.com/diegosouzapw/OmniRoute/pull/12120)) — thanks @geek007git +- **fix(codex):** preserve existing provider state when bulk-import upserts a matching connection ([#12122](https://github.com/diegosouzapw/OmniRoute/pull/12122)) — thanks @geek007git +- **fix(ci):** clear the two base-reds the 2026-08-30 merge batch left on release/v3.8.51 (round 3) ([#12123](https://github.com/diegosouzapw/OmniRoute/pull/12123)) +- **fix(sse):** strip type:'custom' from Claude tools on agentrouter dispatch ([#12126](https://github.com/diegosouzapw/OmniRoute/pull/12126)) — thanks @wahidsadik371-coder +- **fix(sse):** stop advertising video providers the dispatcher cannot run ([#12131](https://github.com/diegosouzapw/OmniRoute/pull/12131)) — thanks @quiterunner-commits +- **fix(search):** treat HTTP 432 and plan limit errors as transient cooldown ([#12139](https://github.com/diegosouzapw/OmniRoute/pull/12139)) — thanks @HouMinXi +- **fix(sse):** refuse an AI Horde queue that cannot fit the request budget ([#12143](https://github.com/diegosouzapw/OmniRoute/pull/12143)) — thanks @quiterunner-commits +- **fix(ci):** clear the base-reds the afternoon merge batch left on release/v3.8.51 (round 5: provider count 352, TS2554/TS2677) ([#12144](https://github.com/diegosouzapw/OmniRoute/pull/12144)) +- **fix(translator):** strip neutral tool_choice when tools absent in Responses-to-Chat (#12141) ([#12166](https://github.com/diegosouzapw/OmniRoute/pull/12166)) — thanks @HouMinXi +- **fix(guardrails):** pass providerId to getResolvedModelCapabilities in checkComboVision (#12112) ([#12169](https://github.com/diegosouzapw/OmniRoute/pull/12169)) — thanks @HouMinXi +- **fix(translator):** strip plaintext reasoning content for opaque responses backends (#12128) ([#12171](https://github.com/diegosouzapw/OmniRoute/pull/12171)) — thanks @HouMinXi +- **perf(stream):** compile hot-path regexes once, bound token caches, fix quadratic buffering ([#12179](https://github.com/diegosouzapw/OmniRoute/pull/12179)) — thanks @opensource-elearning +- **fix:** resolve compression worker file using runtime anchors instead of… ([#12183](https://github.com/diegosouzapw/OmniRoute/pull/12183)) — thanks @NightStalker-87 +- **fix:** prevent Claude Code session kills via liveness-aware readiness + auto model echo ([#12189](https://github.com/diegosouzapw/OmniRoute/pull/12189)) — thanks @opensource-elearning +- **fix(translator):** preserve falsy primitive values in Gemini and Antigravity function response results ([#12191](https://github.com/diegosouzapw/OmniRoute/pull/12191)) — thanks @Siva010 +- **fix(oauth):** mark empty Antigravity projectId as degraded and clear stale errors (#11284) ([#12205](https://github.com/diegosouzapw/OmniRoute/pull/12205)) — thanks @HouMinXi +- **fix(providers):** gate the Codex auto-ping usage read on the shared quota throttle ([#12209](https://github.com/diegosouzapw/OmniRoute/pull/12209)) — thanks @abhisheksharma2411 +- **fix(combos):** prioritize SQLite row id over inner JSON id and notify delete errors ([#12213](https://github.com/diegosouzapw/OmniRoute/pull/12213)) — thanks @HouMinXi +- **fix(providers):** mark featherless.ai as no longer having a free tier (`hasFree: false`) ([#12216](https://github.com/diegosouzapw/OmniRoute/pull/12216)) — thanks @turbolego +- **fix(deepseek-web):** stop Turbopack dev panic in the PoW worker path resolver ([#12221](https://github.com/diegosouzapw/OmniRoute/pull/12221)) — thanks @hartmark +- **fix(chat-admission):** clarify local 503 source ([#12223](https://github.com/diegosouzapw/OmniRoute/pull/12223)) — thanks @keeltrace +- **fix(dev):** silence webpack runtime module warnings ([#12228](https://github.com/diegosouzapw/OmniRoute/pull/12228)) — thanks @backryun +- **fix(db):** use module.require for CommonJS runtime driver loading ([#12230](https://github.com/diegosouzapw/OmniRoute/pull/12230)) — thanks @raheemuddin786 +- **fix(memory):** honest probe-driven FTS5 keyword status + memory_id rowid sync ([#12231](https://github.com/diegosouzapw/OmniRoute/pull/12231)) — thanks @raheemuddin786 +- **fix(system):** propagate abort signal to stream reader in HTTP version checks ([#12232](https://github.com/diegosouzapw/OmniRoute/pull/12232)) — thanks @raheemuddin786 +- **fix(resilience):** isolate local host execution errors from provider circuit breakers ([#12233](https://github.com/diegosouzapw/OmniRoute/pull/12233)) — thanks @raheemuddin786 +- **fix(sse):** sort injected tools deterministically for prompt caching ([#12234](https://github.com/diegosouzapw/OmniRoute/pull/12234)) — thanks @raheemuddin786 +- **fix(combo):** always clear the loop-safety timer, not just on the happy path (#11804) ([#12245](https://github.com/diegosouzapw/OmniRoute/pull/12245)) +- **fix(quality):** base-red pair — stryker tap registration + turn-pin suites aligned to the window gate ([#12255](https://github.com/diegosouzapw/OmniRoute/pull/12255)) — thanks @backryun +- **fix(dev):** qualify Turbopack runtime boundaries ([#12258](https://github.com/diegosouzapw/OmniRoute/pull/12258)) — thanks @backryun +- **fix(combo):** fall back to full pool when collapsed sole survivor is context-too-small ([#12278](https://github.com/diegosouzapw/OmniRoute/pull/12278)) — thanks @opensource-elearning +- **fix(combo):** bound the pre-dispatch unavailable skip so a stale label cannot dark a pool (#12168) ([#12285](https://github.com/diegosouzapw/OmniRoute/pull/12285)) +- **fix(security):** strip Qwen/Alibaba console-session cookies from provider API responses ([#12287](https://github.com/diegosouzapw/OmniRoute/pull/12287)) +- **fix(usage):** console-aware Token Plan guidance + subscription hint on bailian 401 ([#12288](https://github.com/diegosouzapw/OmniRoute/pull/12288)) +- **fix(resourcePressure):** log numeric detail on every rejection, recover faster ([#12293](https://github.com/diegosouzapw/OmniRoute/pull/12293)) — thanks @hartmark +- **fix(release):** drain the 2026-09-01 base-red window — passthrough usage regression + radarPage i18n keys ([#12327](https://github.com/diegosouzapw/OmniRoute/pull/12327)) +- **fix(ci):** openapi-security-tiers checker must honor routeGuard patterns + imported prefixes ([#12350](https://github.com/diegosouzapw/OmniRoute/pull/12350)) — thanks @ggiak +- **perf(ci):** cache node_modules in the npm-ci-retry composite (#8084 D3) ([#12408](https://github.com/diegosouzapw/OmniRoute/pull/12408)) +- **revert(ui):** point CTAs back at their real destinations ([#12410](https://github.com/diegosouzapw/OmniRoute/pull/12410)) +- **fix(release):** drain the 2026-09-02 base-red — rerank-providers import + api-typecheck baseline ratchet ([#12414](https://github.com/diegosouzapw/OmniRoute/pull/12414)) +- **fix(types):** make system prompt injection noImplicitAny-safe ([#12416](https://github.com/diegosouzapw/OmniRoute/pull/12416)) +- **fix(quality):** validate typecheck baseline schema ([#12419](https://github.com/diegosouzapw/OmniRoute/pull/12419)) +- **fix(memory):** resolve rerank provider node cache import ([#12421](https://github.com/diegosouzapw/OmniRoute/pull/12421)) +- **fix(ci):** repair release-root regressions — pack dedup, web-session syntax, uc-image ids ([#12423](https://github.com/diegosouzapw/OmniRoute/pull/12423)) — thanks @backryun +- **fix(providers):** separate MaxAI and UC credential contracts ([#12431](https://github.com/diegosouzapw/OmniRoute/pull/12431)) +- **fix(cli):** preserve Claude settings on config set ([#12432](https://github.com/diegosouzapw/OmniRoute/pull/12432)) — thanks @KrzysiekSko +- **fix(providers):** repair the maxai credential block truncated by merge auto-resolve ([#12433](https://github.com/diegosouzapw/OmniRoute/pull/12433)) +- **fix(quality):** record the 2026-09-02 merged growth in the file-size baseline ([#12434](https://github.com/diegosouzapw/OmniRoute/pull/12434)) +- **fix(uc):** route UC error strings through sanitizeErrorMessage; allowlist the retired codex id ([#12437](https://github.com/diegosouzapw/OmniRoute/pull/12437)) +- **fix(adapta):** redact streamed upstream errors ([#12438](https://github.com/diegosouzapw/OmniRoute/pull/12438)) +- **fix(sse):** remap non-contiguous upstream tool_calls index to a gap-free output_index ([#12445](https://github.com/diegosouzapw/OmniRoute/pull/12445)) — thanks @hartmark +- **fix(quality):** detect duplicate tool_calls entries in one response ([#12446](https://github.com/diegosouzapw/OmniRoute/pull/12446)) — thanks @hartmark +- **fix(conversations):** resolve turn content OmniRoute never sends back to the client ([#12447](https://github.com/diegosouzapw/OmniRoute/pull/12447)) — thanks @hartmark +- **fix(combo):** do not treat credits-exhausted 401 as auth skip ([#12449](https://github.com/diegosouzapw/OmniRoute/pull/12449)) — thanks @RaviTharuma +- **fix(auth):** do not park healthy quota accounts as expired ([#12452](https://github.com/diegosouzapw/OmniRoute/pull/12452)) — thanks @RaviTharuma +- **fix(responses-continuation):** fail closed on a collector-truncated, empty output array ([#12460](https://github.com/diegosouzapw/OmniRoute/pull/12460)) — thanks @hartmark +- **fix(logging):** raise the SSE payload collector's default cap ([#12461](https://github.com/diegosouzapw/OmniRoute/pull/12461)) — thanks @hartmark +- **fix(resilience):** surface Responses failed.error.message in 502s ([#12472](https://github.com/diegosouzapw/OmniRoute/pull/12472)) — thanks @RaviTharuma +- **fix(security):** unbiased maxai X-Random nonce + stricter URL/regex assertions ([#12502](https://github.com/diegosouzapw/OmniRoute/pull/12502)) +- **fix(video):** re-anchor transcript log-redaction so PII/credential maskers can't reopen the leak (#12150 P1 follow-up) ([#12503](https://github.com/diegosouzapw/OmniRoute/pull/12503)) +- **fix(monitoring):** serve cached credentialHealth off the request path ([#12533](https://github.com/diegosouzapw/OmniRoute/pull/12533)) — thanks @RaviTharuma +- **fix(quota):** keep Antigravity Gemini usable when Claude weekly is empty ([#12566](https://github.com/diegosouzapw/OmniRoute/pull/12566)) — thanks @HouMinXi +- **fix(authz):** hard-gate every credential export and CLI-config write (GHSA-5926-2w35-7h4q) ([#12600](https://github.com/diegosouzapw/OmniRoute/pull/12600)) +- **fix(docs):** restore the Next build — REMOVED_PROVIDERS.md had no frontmatter (base-red #12581) ([#12610](https://github.com/diegosouzapw/OmniRoute/pull/12610)) +- **fix(quality):** re-point the zcodeProtocol public-creds allowlist to line 313 ([#12615](https://github.com/diegosouzapw/OmniRoute/pull/12615)) +- **fix(security):** close 3 advisories — search baseUrl exfil, sk- in the error sanitizer, bifrost relay header leak ([#12620](https://github.com/diegosouzapw/OmniRoute/pull/12620)) +- **fix(providers):** sanitize boolean required and nested bare maps for Gemini (#12269) ([#12624](https://github.com/diegosouzapw/OmniRoute/pull/12624)) — thanks @HouMinXi +- **fix(resilience):** keep Overloaded STREAM_EARLY_EOF off the provider breaker ([#12626](https://github.com/diegosouzapw/OmniRoute/pull/12626)) — thanks @HouMinXi +- **fix(catalog):** re-audit free-tier quotas against official pages ([#12649](https://github.com/diegosouzapw/OmniRoute/pull/12649)) +- **fix(dashboard):** keep a request's pending-tracking id stable across combo target retries ([#12650](https://github.com/diegosouzapw/OmniRoute/pull/12650)) — thanks @hartmark +- **fix(tests):** retire dead model ids from the chat-pipeline integration suite (base-red #12581) ([#12670](https://github.com/diegosouzapw/OmniRoute/pull/12670)) +- **fix:** resolve SqliteError no such table compression_run_telemetry during cleanup ([#12682](https://github.com/diegosouzapw/OmniRoute/pull/12682)) — thanks @groovecityJO +- **fix(combo):** treat a pin-only step as implicit connection allowlist ([#12697](https://github.com/diegosouzapw/OmniRoute/pull/12697)) — thanks @HouMinXi +- **fix(ci):** ship .npmrc in published package so legacy-peer-deps applies to consumers (#11544) ([#12699](https://github.com/diegosouzapw/OmniRoute/pull/12699)) — thanks @KooshaPari +- **fix(docker):** pin 4 CLI tools to exact versions (#12576) ([#12703](https://github.com/diegosouzapw/OmniRoute/pull/12703)) — thanks @KooshaPari +- **fix(docker):** document and harden cli profile trust boundary (#12570) ([#12706](https://github.com/diegosouzapw/OmniRoute/pull/12706)) — thanks @KooshaPari +- **fix(sse):** redact video transcript in pre-guardrail rejected-request logs (#12150 P2 item 7) ([#12710](https://github.com/diegosouzapw/OmniRoute/pull/12710)) +- **fix(i18n):** quote placeholder in OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES description (#12505) ([#12769](https://github.com/diegosouzapw/OmniRoute/pull/12769)) — thanks @KooshaPari +- **fix(api/pricing):** surface validation error message as string, not raw object (#12494) ([#12771](https://github.com/diegosouzapw/OmniRoute/pull/12771)) — thanks @KooshaPari +- **fix(sse):** re-enable prompt compression for native Codex passthrough ([#12834](https://github.com/diegosouzapw/OmniRoute/pull/12834)) — thanks @thomasmaerz +- **fix(dashboard):** model health tests for a provider node set to the Responses API now call `/v1/responses` with a Responses-shaped body instead of `/v1/chat/completions` — those models were reported as `Provider returned HTTP 200 but no text content` even though the same model answered normally through `/v1/responses` ([#13070](https://github.com/diegosouzapw/OmniRoute/issues/13070)) ([#13087](https://github.com/diegosouzapw/OmniRoute/pull/13087)) — thanks @ntdatt812 +- fix(routing): stop a round-robin combo's "opencode" targets from collapsing onto the opencode-zen connection (#11912) ([#13283](https://github.com/diegosouzapw/OmniRoute/pull/13283)) +- fix(routing): stop the reactive-compaction debug log from lying when compression is globally disabled (#11977) ([#13276](https://github.com/diegosouzapw/OmniRoute/pull/13276)) +- fix(electron): relativize standalone-bundle symlink targets so Stage 8 manifest verification stops failing on Windows (#11979) ([#13251](https://github.com/diegosouzapw/OmniRoute/pull/13251)) +- fix(dashboard): surface a visible error when Compression Studio's combined preview run fails (#12061) ([#13288](https://github.com/diegosouzapw/OmniRoute/pull/13288)) +- fix(dashboard): make the compression "Effective pipeline" preview honor the active profile and warn when the master switch is off (#12063) ([#13287](https://github.com/diegosouzapw/OmniRoute/pull/13287)) +- fix(providers): scope TinyCMS Web signer's DOM shims to each call instead of leaking them for the process lifetime, and surface a clean HTTP status on a non-JSON interception-toggles error (#12072) ([#13254](https://github.com/diegosouzapw/OmniRoute/pull/13254)) +- fix(guardrails): stop Vision Bridge from re-selecting a model locked after a 404 (#12111) ([#13259](https://github.com/diegosouzapw/OmniRoute/pull/13259)) +- fix(sse): require Responses-shaped body before native OpenAI-compatible passthrough (#12129) ([#13278](https://github.com/diegosouzapw/OmniRoute/pull/13278)) +- fix(providers): minimax-m3 now collapses manual thinking.type:"enabled" to adaptive, preventing upstream 400 (2013) (#12132) ([#13249](https://github.com/diegosouzapw/OmniRoute/pull/13249)) +- fix(db): scope model visibility overrides by modality so hiding a Chat model no longer hides an identically-ID'd Image/Embeddings/etc. model (#12172) ([#13258](https://github.com/diegosouzapw/OmniRoute/pull/13258)) +- fix(db): scope local-provider apiKey dedup to matching base URL so LM Studio/Ollama-style connections support multiple accounts (#12173) ([#13250](https://github.com/diegosouzapw/OmniRoute/pull/13250)) +- fix(providers): refresh Trae's stale Referer/Origin and forward user timezone so imported connections stop failing with 401 (#12190) ([#13255](https://github.com/diegosouzapw/OmniRoute/pull/13255)) +- fix(providers): route opencode-go/gpt-5.6-luna to /responses instead of /chat/completions (#12196) ([#13275](https://github.com/diegosouzapw/OmniRoute/pull/13275)) +- fix(dashboard): allow deleting the last extra-upstream-header row even when invalid (#12251) ([#13252](https://github.com/diegosouzapw/OmniRoute/pull/13252)) +- **fix(i18n):** translate pre-existing `__MISSING__:` keys for `combo.sort`, `requestLogger.detail` expand/collapse, `common.profile`, and `settings.resilienceCredentialHealth*` across 39 locales ([#12272](https://github.com/diegosouzapw/OmniRoute/issues/12272)) ([#13034](https://github.com/diegosouzapw/OmniRoute/pull/13034)) — thanks @HouMinXi +- fix(cli): run the Node.js runtime compatibility guard before the heavy `tsx/esm` + Commander import chain so an unsupported runtime gets a clear message instead of a raw `Invalid regular expression flags` crash (#12296) ([#13253](https://github.com/diegosouzapw/OmniRoute/pull/13253)) +- fix(dashboard): refresh the providers list after deleting a compatible provider node (#12298) ([#13256](https://github.com/diegosouzapw/OmniRoute/pull/13256)) +- fix(usage): fail closed on API-key budget enforcement when a provider's `auto` routing alias has no pricing row, instead of silently counting it as $0 (#12341) ([#13257](https://github.com/diegosouzapw/OmniRoute/pull/13257)) +- **fix(providers):** include the submitted Agnes video model when polling by `video_id` ([#12356](https://github.com/diegosouzapw/OmniRoute/pull/12356)) — thanks @pacocartones +- **fix(routing):** custom OpenAI-compatible nodes now honor the saved Chat/Responses API type after edits instead of letting the node's original ID prefix override the live connection setting ([#11884](https://github.com/diegosouzapw/OmniRoute/issues/11884)). ([#12358](https://github.com/diegosouzapw/OmniRoute/pull/12358)) — thanks @pacocartones +- fix(sse): surface an error instead of a silent empty 200 when a Claude stream closes with zero bytes (#12398) ([#13285](https://github.com/diegosouzapw/OmniRoute/pull/13285)) +- fix(oauth): warn before the dead localhost:8080 redirect in antigravity/gemini `oauth start` (#12413) ([#13265](https://github.com/diegosouzapw/OmniRoute/pull/13265)) +- fix(providers): stop devin-cli spawn error from double-closing the SSE controller (#12517) ([#13261](https://github.com/diegosouzapw/OmniRoute/pull/13261)) +- **fix(rerank):** clamp Voyage `top_k` to the documents actually sent after empty-string filtering, and honor `return_documents: false` in the NVIDIA response adapter (#12523 — thanks @pacocartones) +- **fix(audio):** `/v1/audio/translations` now resolves combo names the way `/v1/audio/transcriptions` already does, so a combo that `GET /v1/models` advertises is fanned out to its targets instead of being rejected with `400 Invalid translation model: . Use format: provider/model`; literal `provider/model` ids and unknown bare names behave as before (#12536 — thanks @pacocartones) +- **fix(gemini):** strip the JSON-Schema-2020-12 `prefixItems` keyword from Gemini tool schemas at every nesting level, so Claude Code tool definitions no longer fail with `400 Unknown name "prefixItems"` on Gemini models (#12540 — thanks @pacocartones) +- **fix(api-manager):** Expose an accessible loading status while API keys are fetched instead of an empty accessibility tree (#12541 — thanks @pacocartones) +- **fix(video):** Clamp `estimateJpegFrameBytes` at zero for padding-only payloads and build the three encode-side frame data URIs from `JPEG_FRAME_DATA_URI_PREFIX` instead of a repeated literal (#12543 — thanks @pacocartones) +- **fix(devin):** accept Windows `DEVIN_AGENTIC_HOME` sandbox paths (`C:\...\.sandbox\...`) in the isolated-home check so the Devin Claude Bridge no longer fails closed on Windows ([#12405](https://github.com/diegosouzapw/OmniRoute/issues/12405)) (#12545 — thanks @pacocartones) +- **fix(db):** Add `conversation_turn_nodes` and orphaned `agentic_conversations` to the auto-cleanup cycle under the existing `retention.callLogs` window, so identity nodes whose call-log content has already been purged no longer accumulate without bound in `storage.sqlite` (#12548 — thanks @pacocartones) +- **fix(i18n):** Wrap the `~/.claude/profiles//settings.json` placeholder in ICU single quotes in the `OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES` feature-flag description across all 42 locales and the TypeScript default, so next-intl no longer fails with `INVALID_MESSAGE: UNCLOSED_TAG` and the Feature Flags card shows the description instead of the raw key (#12549 — thanks @pacocartones) +- **fix(orchestration):** `updateCloudAgentTask` now publishes the task's real `status` on `agent.task.updated` when an update only touches `result`, `activities` or `error`, instead of the fabricated `"updated"` state, and stays silent when no row matched the id (#12550 — thanks @pacocartones) +- **fix(i18n):** the home "Recent Requests" panel and the Provider Topology legend are now translated instead of rendering English copies on non-English dashboards; the legend reads its own `home.topologyLegend*` labels with consistent casing rather than borrowing the memory-settings "Recent" and analytics "Error" strings (#12551 — thanks @pacocartones). +- **docs(reference):** bring the `FEATURE_FLAGS.md` catalog back to 1:1 with `featureFlagDefinitions.ts` — 20 missing flags added, the two `*_BLOCK_THRESHOLD` env-only knobs moved out of the flag tables, category/total counts and the Live WS port corrected, guarded by a static test (#12552 — thanks @pacocartones) +- **fix(i18n):** backfill missing `usage.kiloPass*` strings in 39 locales and restore `featureFlagOmnirouteDisableThinkingLevelVariantsDescription` in `pt.json` ([#12561](https://github.com/diegosouzapw/OmniRoute/issues/12561)) ([#13027](https://github.com/diegosouzapw/OmniRoute/pull/13027)) — thanks @HouMinXi +- fix(docker): default docker-compose app ports (dashboard/API/live-WS) to loopback instead of `0.0.0.0`, closing the anonymous `/v1` LAN/WAN exposure gap left open by `REQUIRE_API_KEY=false` (#12568) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): close DNS-rebinding SSRF gap in webhook outbound-URL guard (#12569) ([#13243](https://github.com/diegosouzapw/OmniRoute/pull/13243)) +- fix(docker): require a per-session token on the VNC browser CDP bridge and isolate it on a dedicated Docker network (#12571) ([#13241](https://github.com/diegosouzapw/OmniRoute/pull/13241)) +- fix(open-sse): write Adobe Firefly session tokens and cookie jars with 0700/0600 permissions instead of the process umask (#12572) ([#13242](https://github.com/diegosouzapw/OmniRoute/pull/13242)) +- fix(api): remove hardcoded wildcard CORS in openai-to-gemini-sse.ts so the centralized fail-closed CORS gate is the sole source of `Access-Control-Allow-Origin` (#12573) ([#13239](https://github.com/diegosouzapw/OmniRoute/pull/13239)) +- fix(api): enforce API key policy (budget/rate-limit/schedule/endpoint scoping) on the ElevenLabs speech-to-text, text-to-speech and voices proxy routes (#12574) ([#13244](https://github.com/diegosouzapw/OmniRoute/pull/13244)) +- fix(sse): cap HuggingChat NDJSON body size and bound the read loop with the fetch timeout so a stalled or hostile upstream cannot buffer unbounded memory (#12577) ([#13282](https://github.com/diegosouzapw/OmniRoute/pull/13282)) +- fix(docker): scope the cliproxyapi/qdrant/bifrost sidecars to loopback by default and forward `CLIPROXYAPI_MANAGEMENT_KEY` into the cliproxyapi container so its management API is not left both unauthenticated and LAN-published (#12578) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): create DB export temp paths with `fs.mkdtempSync` instead of predictable timestamps (#12579) ([#13237](https://github.com/diegosouzapw/OmniRoute/pull/13237)) +- Cline 401 bodies that say "re-authenticate your Cline account" classify as a refreshable OAuth token, not a terminal expired key. The cooling panel no longer labels every cooldown as a 429; it shows the recorded last error instead. (#12594) ([#13060](https://github.com/diegosouzapw/OmniRoute/pull/13060)) — thanks @HouMinXi +- **fix(catalog):** degrade unknown combo targets instead of dropping LCD modalities, and persist OpenRouter `architecture.input_modalities` into the capability snapshot ([#12613](https://github.com/diegosouzapw/OmniRoute/issues/12613)) ([#13042](https://github.com/diegosouzapw/OmniRoute/pull/13042)) — thanks @HouMinXi +- fix(providers): send `x-api-key` instead of `Authorization: Bearer` for OpenCode Zen's `/v1/responses` endpoint (Muse Spark Contributor models), fixing a 401 on OmniRoute's auth header (#12633) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(sse): surface the actionable "Auggie CLI not found" message when the shell reports a missing `auggie` binary via exit code instead of a spawn error (#12645) ([#13263](https://github.com/diegosouzapw/OmniRoute/pull/13263)) +- **fix(config):** Persist deletions of built-in background-degradation entries — when a stored settings record exists its `degradationMap` is now authoritative instead of being merged under the defaults, so an entry the user removed in the dashboard no longer reappears on the next apply or restart ([#12424](https://github.com/diegosouzapw/OmniRoute/issues/12424)) ([#12647](https://github.com/diegosouzapw/OmniRoute/pull/12647)) — thanks @pacocartones +- **fix(gamification):** action-count badge milestones (First Token, Token Consumer, Token Machine, Token Whale, and the token-sharing tier) are now backed by a durable `xp_action_counts` counter incremented in `addXp()`, instead of a live `COUNT(*)` over `xp_audit_log`. The audit log is pruned by `retention.xpAuditLog` (default 30 days), so on a default install those "lifetime" milestones were really "actions in the last 30 days" and unlocked badges could stop unlocking once old rows aged out. `getActionCount()` and `checkActionCountBadges()` now read the same durable source, and a migration backfills existing totals from the surviving audit rows ([#12546](https://github.com/diegosouzapw/OmniRoute/issues/12546)) ([#12651](https://github.com/diegosouzapw/OmniRoute/pull/12651)) — thanks @pacocartones +- **fix(images):** `/v1/images/edits` now iterates a combo's targets the same way `/v1/images/generations` does (#9239) instead of flattening a bare combo to its first target. A combo whose first target is not edit-capable — or lacks credentials — now falls through to a later edit-capable target rather than hard-erroring, and missing credentials are skipped (not a hard `401`) to match the generations path. The per-target skip/terminal classification is extracted into a shared `runImageComboTargets` loop, so generations behavior is unchanged ([#12547](https://github.com/diegosouzapw/OmniRoute/issues/12547)). ([#12653](https://github.com/diegosouzapw/OmniRoute/pull/12653)) — thanks @pacocartones +- fix(sse): add first-byte watchdog to the TLS-fingerprint transport so a stalled wreq body falls back instead of hanging for minutes (#12656) ([#13272](https://github.com/diegosouzapw/OmniRoute/pull/13272)) +- fix(sse): exempt tiny-budget reasoning probes from combo quality failure and surface persisted-cooldown skips in ALL_TARGETS_SKIPPED diagnostics (#12659) ([#13269](https://github.com/diegosouzapw/OmniRoute/pull/13269)) +- fix(models): declare the real ~1M contextLength for OpenCode Zen's Muse Spark 1.2 models instead of falling back to the 200000 provider default (#12681) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(oauth): align codebuddy-cn OAuth User-Agent with the chat/usage CLI version to avoid WAF false positives (#12702) ([#13264](https://github.com/diegosouzapw/OmniRoute/pull/13264)) +- fix(dashboard): surface an authentication-required banner instead of silently blanking database settings for a guest session (#12709) ([#13270](https://github.com/diegosouzapw/OmniRoute/pull/13270)) +- **fix(resilience):** stop unbounded queue that hangs 6min until Aborted — gate, provider slot, and Bottleneck queue now share a per-connection `maxWaitMs` budget; `fail-closed` on exhaust (503); execution backstop `executionMaxWaitMs` overridable per connection with upstream clamp ([#12715](https://github.com/diegosouzapw/OmniRoute/pull/12715)) — thanks @maxmad64bis (with thanks to @Tushar49 for surfacing the slow-provider need in #12635) +- **fix(auto-combo):** every mode pack now carries `quality` and `reliability` weights (`quality` 0.02, 0.03 in `quality-first`; `reliability` 0.03, 0.04 in `reliability-first`), so selecting a pack no longer silences either signal; `DEFAULT_WEIGHTS` is unchanged ([#12731](https://github.com/diegosouzapw/OmniRoute/pull/12731)) — thanks @maxmad64bis +- **fix(sse):** the provider execution pipeline keeps the upstream error `code`/`type` again — both were lost when the non-streaming leg was extracted, so a config-class `422` (Antigravity missing project) degraded into a generic account cooldown instead of reaching the gate that recognises it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(db):** a legacy `call_logs` table no longer aborts startup — the provider-stats index is created after the column healing runs, not before it, so an install predating `request_type` opens instead of failing with `no such column` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(catalog):** a malformed row in the operator-writable custom-models blob no longer kills every `auto/*` pool ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(tests):** realign the guards that the same day's merges left asserting the old behavior — the provider-count total after GreenPT and EURouter, the GLM stream buffer slot that `#12925` turned into a declared parameter, and the injection scan bound that `#13104` reshaped from a head-only window into head-plus-tail ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- fix(cache): fold tool_choice/tools/response_format into the semantic cache signature so a cached tool_calls response can no longer be replayed for a request whose tool policy forbids it (#12734) ([#13267](https://github.com/diegosouzapw/OmniRoute/pull/13267)) +- **fix(models):** stop treating provider-supplied `isFree:true`/`:free`/`0/0` flags as trusted unless the provider has a documented free tier — only the shipped free-tier catalog decides otherwise; fetched `isFree:true` trusted only for a free-tier provider, custom `isFree:true` via a trusted path ([#12744](https://github.com/diegosouzapw/OmniRoute/pull/12744)) — thanks @maxmad64bis +- fix(memory): authenticate the internal /v1/rerank loopback call so memory reranking no longer silently degrades to unranked order when REQUIRE_API_KEY=true (#12745) ([#13268](https://github.com/diegosouzapw/OmniRoute/pull/13268)) +- fix(sse): parse Ollama Cloud's current usage markup (`$X of $Y used` aria-label, nested width style) (#12749) ([#13260](https://github.com/diegosouzapw/OmniRoute/pull/13260)) +- fix(cli): setup-opencode no longer sends an active context's management token to `/v1/models` when `--api-key`/`OMNIROUTE_API_KEY` is supplied — an explicit flag or the env var now always outranks the context's token, and the flag itself is no longer swallowed by the parent program's global `--api-key` option (#12783) ([#13246](https://github.com/diegosouzapw/OmniRoute/pull/13246)) +- fix(sse): register Arcee AI in the executor provider registry so requests reach api.arcee.ai instead of silently falling back to OpenAI (#12784) ([#13277](https://github.com/diegosouzapw/OmniRoute/pull/13277)) +- **fix(quota):** share one upstream quota read when identical saturation checks arrive at the same time, so traffic bursts don't multiply provider API calls ([#12787](https://github.com/diegosouzapw/OmniRoute/pull/12787)) — thanks @maxmad64bis +- **fix(db):** ignore expired or invalid rate-limit cooldown writes so a stale timestamp can't lock a connection that should be usable — clearing still works as before ([#12788](https://github.com/diegosouzapw/OmniRoute/pull/12788)) — thanks @maxmad64bis +- **fix(providers):** single source for provider order with `xao` ranking alongside `xai-oauth` ([#12790](https://github.com/diegosouzapw/OmniRoute/pull/12790)) — thanks @maxmad64bis +- **fix(routing):** off-table models check the free-model catalog before inheriting premium prices, latency bootstraps from the observed pool median, async tiers read live database pricing with a 90-day freshness gate, and the tier cache invalidates on every pricing write ([#12792](https://github.com/diegosouzapw/OmniRoute/pull/12792)) — thanks @maxmad64bis +- **fix(auto-combo):** snapshot scoring uses the observed per-connection account tier and per-model quality instead of neutral constants, and circuit-open providers rank lower at build time ([#12794](https://github.com/diegosouzapw/OmniRoute/pull/12794)) — thanks @maxmad64bis +- **fix(pool):** empty `auto/*` pools now say why they are empty, and the models listing uses the same paid check as routing ([#12795](https://github.com/diegosouzapw/OmniRoute/pull/12795)) — thanks @maxmad64bis +- fix(routing): recognize CLIProxyAPI's 'unknown provider for model' 400 as fallback-worthy (#12800) ([#13284](https://github.com/diegosouzapw/OmniRoute/pull/13284)) +- **fix(sse):** translate-mode streams now emit the estimated token counts as a trailing usage-only chunk before `[DONE]` when the upstream stays silent, so metered chat clients see totals instead of nothing ([#12828](https://github.com/diegosouzapw/OmniRoute/pull/12828)) — thanks @maxmad64bis +- **fix(db):** provider stats stay truthful on empty and legacy databases: fallback counts default to `0` instead of `null`, latency averages read `null` (not `0`) when no durations were recorded, failures logged before the error-type column existed group under `pre_migration` instead of `unclassified`, and per-provider queries use two new composite indexes ([#12832](https://github.com/diegosouzapw/OmniRoute/pull/12832)) — thanks @maxmad64bis +- fix(nvidia): fail open when a synced model catalog goes stale instead of gating forever (#12849) ([#13248](https://github.com/diegosouzapw/OmniRoute/pull/13248)) +- **fix(db):** WAL maintenance lives in its own module: a `TRUNCATE` checkpoint that hits a busy database now warns with its streak and retries once via `PASSIVE` instead of logging success, and closing-time checkpoints no longer report success on builds without a database file, with the busy totals visible in the authenticated monitoring health payload ([#12853](https://github.com/diegosouzapw/OmniRoute/pull/12853)) — thanks @maxmad64bis +- **fix(health):** quota with no snapshots reads empty instead of a contradicting 0% ([#12857](https://github.com/diegosouzapw/OmniRoute/pull/12857)) — thanks @maxmad64bis +- fix(a2a): accept the dashboard's own session cookie on /a2a so "Run message/send" no longer fails with "Unauthorized: missing or invalid API key" (#12888) ([#13271](https://github.com/diegosouzapw/OmniRoute/pull/13271)) +- **fix(logs):** the Logs grid's in-memory filter pass no longer discards rows the SQL query already matched — selecting an API key from the dropdown (which sends the key's id) returns its calls again, the Combo tab shows every combo instead of only those whose name contains a "1", and the model filter and search cover the same columns as the query ([#12896](https://github.com/diegosouzapw/OmniRoute/pull/12896)) — fixes [#12873](https://github.com/diegosouzapw/OmniRoute/issues/12873) — thanks @ntdatt812 +- **fix(a2a):** `/api/a2a/status` now builds the agent card from the request that asked for it, so a gateway reached at a non-localhost host no longer advertises `http://localhost:20128` as its A2A URL ([#12918](https://github.com/diegosouzapw/OmniRoute/pull/12918)). — thanks @ntdatt812 +- **fix(compression):** progressive aging now appends its `[COMPRESSED:aging:…]` annotation after a turn's `tool_result` blocks instead of in front of them, so Anthropic no longer rejects aged conversations with "`tool_use` ids were found without `tool_result` blocks immediately after" ([#12920](https://github.com/diegosouzapw/OmniRoute/pull/12920)). — thanks @ntdatt812 +- **fix(bedrock):** model import now resolves context limits for every vendor prefix instead of only `anthropic.*`, so `global.openai.gpt-5.6-*` no longer imports with a null `inputTokenLimit` and gets rejected pre-flight at the 200k default ([#12921](https://github.com/diegosouzapw/OmniRoute/pull/12921)). — thanks @ntdatt812 +- **fix(stream):** the 64 KB stream buffer GLM asks for is honoured instead of dropped, and the type error it caused no longer fails the API Route Typecheck gate on every open PR ([#12925](https://github.com/diegosouzapw/OmniRoute/pull/12925)) — thanks @ntdatt812 +- **fix(guardrails):** mask PII inside a `tool_result`'s nested content array, which the masker walked past while redacting its sibling block ([#12930](https://github.com/diegosouzapw/OmniRoute/pull/12930)) — thanks @ntdatt812 +- **fix(dashboard):** the Radar catalog table no longer leaves absent data unexplained — empty limits, unknown context windows, and unreported capabilities each explain themselves on hover, and a new check keeps it that way ([#12937](https://github.com/diegosouzapw/OmniRoute/pull/12937)) — thanks @maxmad64bis +- **fix(sse):** geo-blocked opencode requests rotate to the next account proxy instead of failing, so one refused egress no longer aborts the whole chain ([#12941](https://github.com/diegosouzapw/OmniRoute/pull/12941)) — thanks @maxmad64bis +- **fix(codex):** dashboard "clear cooldown" and the CAS recovery path now drop nested `codexScopeRateLimitedUntil` maps in the same write that nulls `rate_limited_until`, and fresh quota snapshots with headroom lift fallback-sourced scope cooldowns parked by quota preflight ([#12817](https://github.com/diegosouzapw/OmniRoute/issues/12817), [#12860](https://github.com/diegosouzapw/OmniRoute/issues/12860), [#12951](https://github.com/diegosouzapw/OmniRoute/pull/12951)) The dashboard reset-credit button (`consumeCodexResetCredit`) now exercises that same snapshot path after a successful redeem, so a filled quota bar is enough to unpark a leftover fallback Codex child without another manual clear. — thanks @HouMinXi +- **fix(db):** resolve `sql-wasm.wasm` across global npm install and hoisted layouts, ensuring OmniRoute can boot cleanly on Node 24 when native `better-sqlite3` is uncompiled. ([#13035](https://github.com/diegosouzapw/OmniRoute/pull/13035)) — thanks @HouMinXi +- **fix(api):** `DELETE /v1/batches/delete-completed` now sweeps only the calling API key's own completed batches (batches with no owner stay out of a key-scoped sweep on purpose), with an explicit instance-wide mode reserved for authenticated dashboard sessions, a 401 for a presented key that is unknown, revoked, deactivated, banned or expired (never falling through to the session branch), audit logging of every sweep, a sanitized 500 on failure, an owner-scoped file half (a key-scoped sweep never nulls a file another tenant owns) and an atomic sweep — chunked in 200-batch transactions in instance mode — so a mid-way error never leaves a batch pointing at a nulled file (GHSA-wvxc-jp3v-5mg5) ([#12969](https://github.com/diegosouzapw/OmniRoute/pull/12969)) +- **fix(combo):** quota-weighted routing stops drawing on an out-of-credit connection — a 402 now invalidates the stored quota snapshot instead of leaving its stale remaining percentage in place, and a snapshot older than 10 minutes no longer counts as confident headroom for the primary pool ([#12972](https://github.com/diegosouzapw/OmniRoute/pull/12972)) ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- **fix(vision):** Custom Models with "Vision capable" checked no longer have image requests swapped to `glm/glm-4.6v` when the client sends the advertised alias (`vllm/path/...`) or the bare path-shaped id — Vision Bridge now matches the stored override for all three id forms ([#12758](https://github.com/diegosouzapw/OmniRoute/issues/12758)) ([#12974](https://github.com/diegosouzapw/OmniRoute/pull/12974)) — thanks @HouMinXi +- **fix(sse):** transient opencode upstream failures rotate to the next account proxy instead of failing, so one flapping egress no longer aborts the whole chain ([#12975](https://github.com/diegosouzapw/OmniRoute/pull/12975)) — thanks @maxmad64bis +- **fix(azure):** Deployments from GPT-6 onward now send `max_completion_tokens` instead of `max_tokens`, which Azure rejects with HTTP 400. The rule matched a literal `gpt-5`, so each new generation arrived broken; it now matches the generation range, while `gpt-35-turbo` still keeps `max_tokens`. ([#13007](https://github.com/diegosouzapw/OmniRoute/pull/13007)) — thanks @ntdatt812 +- **fix(db):** add an opt-in self-restart circuit for sustained critical memory pressure, gate post-cleanup VACUUM behind a minimum freed-rows threshold, and checkpoint the SQLite WAL every 5 minutes with a size guard that escalates to TRUNCATE, so a growing WAL can no longer stall the event loop into a full outage. ([#13011](https://github.com/diegosouzapw/OmniRoute/pull/13011)) — thanks @HouMinXi +- **fix(auth):** an explicit connection pin may probe a quota-disabled row once and re-enable it on success ([#12874](https://github.com/diegosouzapw/OmniRoute/issues/12874)) ([#13017](https://github.com/diegosouzapw/OmniRoute/pull/13017)) — thanks @HouMinXi +- **fix(api):** thread `X-OmniRoute-Fallback-Attempts` through combo chat completions so streaming and non-streaming responses report how many prior legs were attempted ([#13038](https://github.com/diegosouzapw/OmniRoute/pull/13038)) — thanks @HouMinXi +- Restore provider failure classification and credential refresh on non-streaming requests: classify non-2xx failures to lock models on per-model quota exhaustion, update connection rate limits from headers and body, and pass credential refresh handlers to pipeline execution so 401 tokens can be refreshed and retried (#13043). ([#13069](https://github.com/diegosouzapw/OmniRoute/pull/13069)) — thanks @HouMinXi +- **fix(responses):** wrap forced-non-streaming web_search fallback JSON as Responses SSE so Codex still sees `response.completed` ([#13050](https://github.com/diegosouzapw/OmniRoute/pull/13050)) — thanks @HouMinXi +- **fix(antigravity):** Preserve upstream thought-token usage in normalized completion and reasoning token counts ([#13055](https://github.com/diegosouzapw/OmniRoute/pull/13055)) — thanks @pacocartones +- Fix a concurrency-slot leak in the DeepSeek PoW solver: a worker that failed to spawn (for example a missing worker script) never released its slot, so `MAX_CONCURRENT_WORKERS` failures disabled the solver until restart. ([#13097](https://github.com/diegosouzapw/OmniRoute/pull/13097)) — thanks @anhtahaylove +- **fix(acp):** bound the ACP session output buffers — `stdoutBuffer` and `stderrBuffer` now cap at 1 MiB keeping the most recent output behind a visible `[...output truncated...]` marker, and `stderrBuffer` is reset per prompt instead of accumulating for the lifetime of the session. ([#13100](https://github.com/diegosouzapw/OmniRoute/pull/13100)) — thanks @anhtahaylove +- **fix(acp):** release the `stdout`/`exit` listeners and the idle timer that a `sendPrompt` timeout used to leave attached to the `acpManager` singleton, and drop sessions that exited on their own from the session map instead of keeping them forever. ([#13096](https://github.com/diegosouzapw/OmniRoute/pull/13096)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection and PII scanners now read the text a `tool_result` block carries on `content` (string or nested block list), in messages and in system blocks, so tool output is judged by the same rules as user text ([#13101](https://github.com/diegosouzapw/OmniRoute/pull/13101)) — thanks @ntdatt812 +- **fix(gamification):** close the badge notification SSE stream when the request signal is already aborted before the stream starts — a client that disconnects while the route is still awaiting auth used to leave both the 2s unlock poll and the 15s heartbeat running for the lifetime of the process. ([#13106](https://github.com/diegosouzapw/OmniRoute/pull/13106)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection scan now spends its 16 KB budget on both ends of the request instead of the first 16 KB only, so `system`, `instructions`, `query`, `documents` and the newest turns are no longer hidden behind one long message ([#13104](https://github.com/diegosouzapw/OmniRoute/pull/13104)) — thanks @ntdatt812 +- **fix(dashboard):** expose the Volcano Ark console cookie on quota scraping and unwrap connect-error objects so the dashboard shows the upstream message ([#13107](https://github.com/diegosouzapw/OmniRoute/pull/13107)) — thanks @HouMinXi +- **fix(db):** release the `beforeExit`/`SIGINT`/`SIGTERM` handlers when a `node:sqlite` adapter closes, so a closed adapter and its database handle are no longer pinned to `process` for the lifetime of the run — the same treatment #7494 gave the sql.js adapter. ([#13109](https://github.com/diegosouzapw/OmniRoute/pull/13109)) — thanks @anhtahaylove +- **fix(translator):** `contentSchema` and `unevaluatedItems` are now treated as subschema positions by the tool-schema sanitizer, so a truncation placeholder in either is replaced with a permissive schema instead of being forwarded as a string ([#13110](https://github.com/diegosouzapw/OmniRoute/pull/13110)) — thanks @ntdatt812 +- **fix(cli-helper):** clear the `createLogStream` timeout on the abort path — `stop()` aborts the in-flight fetch and returned through the `signal.aborted` branch, which skipped `clearTimeout` and left an armed timer per stopped stream. The stream reader is now also cancelled when the read loop exits early. ([#13114](https://github.com/diegosouzapw/OmniRoute/pull/13114)) — thanks @anhtahaylove +- **fix(docs):** drop TLS-impersonation, MITM-decrypt, supply-chain attestation, and XOR-mask writeups from the public `/docs` catalog and Docker image. Files stay in git for engineers; operators who need them open the repo, not the website. ([#13136](https://github.com/diegosouzapw/OmniRoute/pull/13136)) — thanks @HouMinXi +- **fix(combo):** parse numeric-epoch `rate_limited_until` in the combo cooldown read path ([#13141](https://github.com/diegosouzapw/OmniRoute/pull/13141)) — thanks @maxmad64bis +- **fix(opencode):** both OpenCode plugins now reject a gateway address typed without `http://` at configuration time, instead of publishing every model with an api url no client can call, and the v2 plugin no longer publishes a model card whose api url is blank or relative ([#13142](https://github.com/diegosouzapw/OmniRoute/pull/13142)) — thanks @maxmad64bis +- **fix(providers):** lock opencode model on upstream 400 model-unavailable ([#13146](https://github.com/diegosouzapw/OmniRoute/pull/13146)) — thanks @maxmad64bis +- **fix(logging):** keep the provider exchange rather than the raw client bodies when a call log exceeds its size budget, and show that recovered payload in the request-detail panel instead of replacing it with the stored response body ([#13147](https://github.com/diegosouzapw/OmniRoute/pull/13147)) — thanks @maxmad64bis +- **fix(traffic-inspector):** the WebSocket route no longer leaks a traffic-buffer subscriber and a 30s ping timer when the client socket is already closed at handler time — listeners are attached before any resource is acquired, a destroyed socket bails out early, and the ping interval stops on a dead socket where `write()` never throws ([#13155](https://github.com/diegosouzapw/OmniRoute/pull/13155)) — thanks @anhtahaylove +- **fix(telegram):** bound the per-user API key cache in the Telegram chat proxy so a burst of distinct chat ids can no longer grow the process heap without limit ([#13165](https://github.com/diegosouzapw/OmniRoute/issues/13165)) ([#13166](https://github.com/diegosouzapw/OmniRoute/pull/13166)) — thanks @anhtahaylove +- **fix(stream):** cancel the upstream response body when the JSON-to-SSE sniff unwinds on a body timeout, so a stalled upstream no longer pins the connection ([#13169](https://github.com/diegosouzapw/OmniRoute/issues/13169)) ([#13171](https://github.com/diegosouzapw/OmniRoute/pull/13171)) — thanks @anhtahaylove +- **fix(telegram):** authenticate webhook deliveries with Telegram's `secret_token` so an unauthenticated caller can no longer mint API keys or spend upstream quota ([#13172](https://github.com/diegosouzapw/OmniRoute/issues/13172)) ([#13175](https://github.com/diegosouzapw/OmniRoute/pull/13175)) — thanks @anhtahaylove +- **fix(models):** give discoverable Gemini 3.8 Flash ids their own 65536 output spec so Antigravity no longer clamps them to 16384 ([#13195](https://github.com/diegosouzapw/OmniRoute/pull/13195)) — thanks @HouMinXi +- **fix(dashboard):** leftover catalog-removed provider rows (gemini-cli) can be listed and purged from the providers page ([#13067](https://github.com/diegosouzapw/OmniRoute/issues/13067)) ([#13197](https://github.com/diegosouzapw/OmniRoute/pull/13197)) — thanks @HouMinXi +- **fix(auth):** a dashboard session now requires the `authenticated: true` claim that login, OIDC and the session refresh already emit — a JWT merely signed with `JWT_SECRET` (for example the Cursor CLI passthrough token, which any API-key holder can obtain) no longer verifies as the `auth_token` cookie on any route, the WebSocket handshake or the live server; existing sessions keep working ([#13298](https://github.com/diegosouzapw/OmniRoute/issues/13298)) ([#13375](https://github.com/diegosouzapw/OmniRoute/pull/13375)) +- **fix(api):** `DELETE /v1/batches/delete-completed` now applies the caller's API-key policy (endpoint allowlist, schedule, usage cap, rate limit) like every other `/v1` route, commits the key-scoped sweep per 200-batch chunk instead of holding one write lock for the whole sweep, refuses to loop without progress, and rejects a scope that names both a key and `allTenants` ([#13684](https://github.com/diegosouzapw/OmniRoute/pull/13684)) +- Restore the API-route typecheck gate: the non-streaming leg result lost its discriminated-union narrowing after the server-owned tool loop reassignment, producing 13 new TS2339 diagnostics in `chatCore.ts`. ([#12963](https://github.com/diegosouzapw/OmniRoute/pull/12963)) +- Restore four non-streaming behaviours the server-owned tool loop refactor dropped: client aborts map to 499 with the fixed `Request aborted` message, an aborted request no longer logs a synthetic `clientResponse`, Claude prompt-cache telemetry is recorded again, and a body that cannot be canonicalized no longer throws when the tool loop is off. ([#12990](https://github.com/diegosouzapw/OmniRoute/pull/12990)) +- **fix(skills):** The CLI registry parser now reads positionals declared with `.addArgument()`, not only those written inline in `.command()`. `tunnel create [type]` was being published as `tunnel create`, so the agent-skills sync gate reported drift on every branch and regenerating would have deleted the argument. ([#13009](https://github.com/diegosouzapw/OmniRoute/pull/13009)) — thanks @ntdatt812 +- **fix(combos):** dashboard combo test uses a short prompt, serial probes, and a 60s timeout so reasoning models and rate-limited free pools do not fail the health check ([#13001](https://github.com/diegosouzapw/OmniRoute/pull/13001)) — thanks @HouMinXi +- fix(compression): terminate idle worker threads on eviction so long-running instances stop leaking OS threads and MessagePorts ([#13091](https://github.com/diegosouzapw/OmniRoute/pull/13091)) — thanks @anhtahaylove +- Redact Google API keys of any length in error bodies: the pattern required exactly 39 characters, so shorter or longer `AIza…` credentials passed through unredacted. ([#12964](https://github.com/diegosouzapw/OmniRoute/pull/12964)) +- **fix(grok-cli):** a 402 "Grok Build usage balance exhausted" parks that Grok login as out of credit (Grok Build CLI, grok.com cookie, and xAI OAuth share the weekly pool). Combo routing then tries the next login instead of locking the model for every account in the pool ([#13061](https://github.com/diegosouzapw/OmniRoute/pull/13061)) — thanks @HouMinXi +- fix(compression): spawn the LLMLingua worker with a file URL object so compression actually runs instead of silently failing open on Node ([#13093](https://github.com/diegosouzapw/OmniRoute/pull/13093)) — thanks @anhtahaylove +- **fix(test):** run the local `test` and `test:unit` scripts at concurrency 4 so a full-suite run no longer exhausts the machine's commit charge and kills unrelated processes ([#13187](https://github.com/diegosouzapw/OmniRoute/pull/13187)) — thanks @anhtahaylove +- **fix(monitoring):** `GET /api/monitoring/health` `credentialHealth` now includes a bounded `failedConnections` list (`connectionId`, `status`, sanitized `lastError`) when the probe-cache gauge `failed>0`, plus `source: probe-cache` and a cheap `staleDbNonOkCount` for sticky SQLite `test_status` on active rows. Documents that the live gauge is not `provider_connections.test_status`. ([#12876](https://github.com/diegosouzapw/OmniRoute/pull/12876)) — thanks @RaviTharuma +- **fix(resilience):** an openai-compatible multi-upstream gateway no longer marks the whole connection `credits_exhausted` when a single upstream (or the `/models` / representative-model health probe) returns 402. Quota failures stay model-scoped; true connection-wide auth failures are unchanged. Single-credential 402 key disable ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239)) is preserved. ([#12875](https://github.com/diegosouzapw/OmniRoute/pull/12875)) — thanks @RaviTharuma +- **fix(dashboard):** nine fixes on the Orchestration Canvas, all diagnosed in the Phase 2 reviews. A source that fails now keeps the timestamp of its FIRST failure instead of being re-stamped every poll — the stale line said "since the last poll" no matter how long the source had been down, and the churn also defeated the snapshot's stable identity (it serializes the source list), so the canvas re-rendered on every tick while anything was broken. A source that HAD data and only then started failing is flagged too: previously only a source with no node at all got the warning, so a source that went down mid-session kept a healthy-looking node forever. The rest are pointwise: clicking a filter chip cancels the pending search debounce (left armed, it fired ~300ms later and silently reverted the chip); the search input carries an accessible name; `?state=running, failed` parses like the unpadded form instead of dropping the padded value; the CSV toggle helper is defined once in `model/urlParams.ts` rather than twice; the agents tab tells "nothing running" apart from "the filter matched nothing", offering a clear-filters button instead of setup links that would be wrong advice there; edges stop emitting SMIL particles above 40 simultaneously active edges, keeping the colored stroke; and the drawer's error banner clears when a retried action succeeds. ([#12983](https://github.com/diegosouzapw/OmniRoute/pull/12983)) +- **fix(i18n):** the nine locales added with the EU-language batch (Greek, Estonian, Irish, Croatian, Lithuanian, Latvian, Maltese, Slovenian, Serbian) were missing the eleven Orchestration Canvas keys that Phase 3 introduced, so the compare-runs panel and the "no runs match these filters" empty state fell back to English in those languages (`deepMergeFallback` substitutes English for an absent key, so nothing rendered blank — it rendered untranslated). The coverage gate does not catch this: it enforces an 80% floor per locale, and eleven missing keys out of ~13,000 leaves coverage at 99.9%. Translated for real in each language, calibrated against the wording each file already uses for "run", "filter" and "skill". ([#13294](https://github.com/diegosouzapw/OmniRoute/pull/13294)) +- **fix(dashboard):** five follow-ups from the Phase 2 Orchestration Canvas review. The History drawer now shows the memory section for runs that already left the live TTL window: the persisted `memory_hits` event is parsed into `metadata.memoryHits` with the same defensive validation the drawer applies, and — because that event is observability rather than a state transition — it no longer leaks into the timeline, where it had been inheriting the task's state and rendering as a duplicate transition. The A2A memory recall runs against its own 1.5s deadline instead of inheriting the memory backend's 30s one; overshooting degrades exactly like any other recall failure (no hits, task proceeds), and the timer is cleared on both paths. Repeating a Conductor run carries its `requirements.cli`/`requirements.model` forward, so the new run is pinned to the same runner profile and model rather than drifting to whatever the fleet picks. A successful repeat from the Agents tab now focuses the run it created, instead of leaving the operator on the finished one. And the auth test for `POST /api/conductor/tasks` moved into the shared `ROUTES` array rather than restating the pattern. ([#12988](https://github.com/diegosouzapw/OmniRoute/pull/12988)) +- fix(plugins): stop leaking an exit listener per plugin hook timeout, which triggered MaxListenersExceededWarning on plugins that ignore SIGTERM ([#13092](https://github.com/diegosouzapw/OmniRoute/pull/13092)) — thanks @anhtahaylove +- **fix(validation):** Provider node edits no longer fail with a generic "Invalid request" when the optional daily-quota reset fields are left blank. The dashboard sends `dailyQuotaResetTimezone` and `dailyQuotaResetHour` as `null`, and only the hour accepted it. ([#13066](https://github.com/diegosouzapw/OmniRoute/issues/13066)) ([#13083](https://github.com/diegosouzapw/OmniRoute/pull/13083)) — thanks @ntdatt812 +- **fix(combo):** quota-aware expansion drops banned, inactive, missing, and wrong-provider connections before quota fetch or model dispatch; pins and allowlists stay selectors, not a bypass. Antigravity automatic exhaustion now requires a reported zero remaining, so a positive balance below 1% stays eligible. ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- Fix an intermittent failure in the quota-weighted routing test suite: scores are a function of `Date.now()`, so peers with identical quota scored microseconds apart never tied and swapped order. ([#12946](https://github.com/diegosouzapw/OmniRoute/pull/12946)) +- fix(volcengine): resolve quota window mapping gaps, safe multi-connection console binding, and response cookie sanitization ([#12950](https://github.com/diegosouzapw/OmniRoute/pull/12950)) — thanks @HouMinXi +- **fix(test):** resolve the WebDAV handler path with `fileURLToPath` so the suite's 37 WebDAV tests run on Windows instead of failing with a doubled `C:\C:\` drive prefix ([#13196](https://github.com/diegosouzapw/OmniRoute/pull/13196)) — thanks @anhtahaylove +- **fix(ui):** fall back from invalid AnySearch icon alias (direct commit `9587e07b69`) +- **fix(sse):** stop rebuilding a truncated summary from the collector's cap-dropped event array ([#12718](https://github.com/diegosouzapw/OmniRoute/pull/12718)) — thanks @hartmark +- **fix(conversations):** show a pending spinner for unresolved tool nodes instead of "(empty)" ([#12727](https://github.com/diegosouzapw/OmniRoute/pull/12727)) — thanks @hartmark +- **fix(streaming):** fail fast when an upstream stream produces only lifecycle/heartbeat events, never real content ([#12741](https://github.com/diegosouzapw/OmniRoute/pull/12741)) — thanks @hartmark +- **fix(responses-continuation):** bridge the write-in-flight window with an in-memory pending store ([#12854](https://github.com/diegosouzapw/OmniRoute/pull/12854)) — thanks @hartmark +- **fix(translator):** drop or map agent_message on Chat Completions fallback ([#12880](https://github.com/diegosouzapw/OmniRoute/pull/12880)) — thanks @RaviTharuma +- **fix(api):** page and stream-complete GET /v1/models for large catalogs ([#12882](https://github.com/diegosouzapw/OmniRoute/pull/12882)) — thanks @RaviTharuma +- **fix(quota):** align AUTH window labels with usage API and clarify 503 ([#12884](https://github.com/diegosouzapw/OmniRoute/pull/12884)) — thanks @RaviTharuma +- **fix(ci):** clear two base-reds on release/v3.8.51 — mutation-coverage gate + image-only-model guard ([#12945](https://github.com/diegosouzapw/OmniRoute/pull/12945)) +- **fix(api):** scope batch bulk-delete to the calling API key ([#13211](https://github.com/diegosouzapw/OmniRoute/pull/13211)) +- **fix(tests):** align model-sync log assertion to shipped path redaction (base-red) ([#13227](https://github.com/diegosouzapw/OmniRoute/pull/13227)) +- **fix(test):** make the ToS heading guard actually require the parentheses ([#13228](https://github.com/diegosouzapw/OmniRoute/pull/13228)) +- **fix(quality):** register 4 drifted covering tests in stryker tap.testFiles ([#13229](https://github.com/diegosouzapw/OmniRoute/pull/13229)) +- **fix(logging):** mask *-api-key headers in request-log pipeline (#13273) ([#13401](https://github.com/diegosouzapw/OmniRoute/pull/13401)) — thanks @KooshaPari +- **fix(tests):** correct stale Jina catalog prefix in custom-models test ([#13403](https://github.com/diegosouzapw/OmniRoute/pull/13403)) — thanks @KooshaPari +- **fix(backend):** prune old backups after health-check-repair snapshots (#13308) ([#13404](https://github.com/diegosouzapw/OmniRoute/pull/13404)) — thanks @KooshaPari +- **fix(backend):** guard memory_fts_au trigger with WHEN clause to stop FTS bloat (#13326) ([#13405](https://github.com/diegosouzapw/OmniRoute/pull/13405)) — thanks @KooshaPari +- **fix(free-tiers):** remove obsolete Together $25 signup credit (#12526) ([#13407](https://github.com/diegosouzapw/OmniRoute/pull/13407)) — thanks @KooshaPari +- **fix:** skip non-chat generation models in Test all models ([#13409](https://github.com/diegosouzapw/OmniRoute/pull/13409)) — thanks @KooshaPari +- **fix(providers):** de-duplicate agy and antigravity model catalogs ([#13410](https://github.com/diegosouzapw/OmniRoute/pull/13410)) — thanks @KooshaPari +- **fix(dashboard):** send provider in body for mini-playground web search ([#13411](https://github.com/diegosouzapw/OmniRoute/pull/13411)) — thanks @KooshaPari +- **fix(resilience):** add CJK quota-exhaustion patterns to 429 classifier ([#13412](https://github.com/diegosouzapw/OmniRoute/pull/13412)) — thanks @KooshaPari +- **fix(backend):** eval runner: failed calls no longer score as passed + dotAll regex ([#13413](https://github.com/diegosouzapw/OmniRoute/pull/13413)) — thanks @KooshaPari +- **fix(backend):** eval runner opts out of output-style and memory injection ([#13414](https://github.com/diegosouzapw/OmniRoute/pull/13414)) — thanks @KooshaPari +- **fix:** chain serial quarantine step in npm test script (fixes #13286) ([#13418](https://github.com/diegosouzapw/OmniRoute/pull/13418)) — thanks @KooshaPari +- **fix(antigravity):** derive stable session ID from credentials for prompt-cache reuse (#13305) ([#13424](https://github.com/diegosouzapw/OmniRoute/pull/13424)) — thanks @KooshaPari +- **fix(memory):** merge memory into top-level system field instead of unshifting at messages[0] (#13425) ([#13427](https://github.com/diegosouzapw/OmniRoute/pull/13427)) — thanks @KooshaPari +- **fix(compression):** preserve instruction blocks from lossy engines ([#13523](https://github.com/diegosouzapw/OmniRoute/pull/13523)) — thanks @KooshaPari +- **fix(compression):** preserve polarity words and newlines in ultra heuristic ([#13524](https://github.com/diegosouzapw/OmniRoute/pull/13524)) — thanks @KooshaPari +- **fix(compression):** run user preservePatterns before built-in patterns (#13457) ([#13525](https://github.com/diegosouzapw/OmniRoute/pull/13525)) — thanks @KooshaPari +- **fix(reasoning):** expand deepseek-v4 regex to match v4.1+ and add command-code provider (#13430) ([#13528](https://github.com/diegosouzapw/OmniRoute/pull/13528)) — thanks @KooshaPari +- **fix(providers):** add alwaysReasons metadata for reka-flash-3 ([#13531](https://github.com/diegosouzapw/OmniRoute/pull/13531)) — thanks @KooshaPari +- **fix(playground):** allow Compare columns to scroll independently ([#13532](https://github.com/diegosouzapw/OmniRoute/pull/13532)) — thanks @KooshaPari +- **fix(security):** empty whitelist allows through to prevent admin lockout ([#13534](https://github.com/diegosouzapw/OmniRoute/pull/13534)) — thanks @KooshaPari +- **fix(tests):** add DISABLE_IOREG_STRATEGY env var for macOS test compatibility (fixes #13467) ([#13539](https://github.com/diegosouzapw/OmniRoute/pull/13539)) — thanks @KooshaPari +- **fix(zed-hosted):** map developer role and Gemini enums for Zed proxy ([#13543](https://github.com/diegosouzapw/OmniRoute/pull/13543)) — thanks @KooshaPari +- **fix(analytics):** normalize provider aliases to canonical id in usage_history ([#13545](https://github.com/diegosouzapw/OmniRoute/pull/13545)) — thanks @KooshaPari +- **fix(combo):** use traceId for call log id to prevent UNIQUE constraint failure ([#13546](https://github.com/diegosouzapw/OmniRoute/pull/13546)) — thanks @KooshaPari +- **fix(auth):** check model lockout before returning synthetic noauth connection ([#13547](https://github.com/diegosouzapw/OmniRoute/pull/13547)) — thanks @KooshaPari +- **fix(guardrails):** skip credential redaction for base64 image data URLs ([#13550](https://github.com/diegosouzapw/OmniRoute/pull/13550)) — thanks @KooshaPari +- **fix(resilience):** increase requestQueue.maxWaitMs default from 15s to 30s ([#13553](https://github.com/diegosouzapw/OmniRoute/pull/13553)) — thanks @KooshaPari +- **fix(docs):** document OMNIROUTE_READY_TIMEOUT_MS and allowlist the test-only DISABLE_IOREG_STRATEGY ([#13692](https://github.com/diegosouzapw/OmniRoute/pull/13692)) + ### 📝 Maintenance ---- +- **docs(readme):** reconcile live v3.8.50 provider, free-tier, CLI, routing, test, community, sponsor, acknowledgment, and SVG metrics with their audited source denominators, including a deduplicated OmniRoute-in-Action snapshot and distinct contributor rankings for merged pull requests, GitHub-attributed commits, and Git history ([#11356](https://github.com/diegosouzapw/OmniRoute/pull/11356)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **docs:** sync the canonical provider count 354 → 356 across `README.md`, `AGENTS.md`, `llm.txt` (+ 42 i18n mirrors), the four README SVG diagrams, `docs/reference/PROVIDER_REFERENCE.md` (regenerated) and the `package.json` description after Opper (#11629) and 1min.ai (#11631) boarded the catalog — closes the `check:docs-counts-sync` strict drifts that kept `release/v3.8.51` red ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11673](https://github.com/diegosouzapw/OmniRoute/pull/11673)) — thanks @jonlwheat2-gif +- **test(providers):** update count-derived assertions after the v3.8.51 provider additions — `APIKEY_PROVIDERS` 233 → 235 (Opper #11629 + 1min.ai #11631), reserved-prefix REGISTRY walk 395 → 398, `WEB_FETCH_PROVIDERS` now includes `nimble-search` (#11620), and the provider translate-path golden snapshot regenerated ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11674](https://github.com/diegosouzapw/OmniRoute/pull/11674)) — thanks @jonlwheat2-gif +- **test(sse):** bump the hard-lease connection-query inventory for `src/lib/tokenHealthCheck.ts` to 2 — the verify-only web-cookie sweep added by #11495 split the single `getProviderConnections` call into oauth + cookie variants, which the frozen inventory had not tracked ([#11495](https://github.com/diegosouzapw/OmniRoute/pull/11495)). ([#11677](https://github.com/diegosouzapw/OmniRoute/pull/11677)) — thanks @jonlwheat2-gif / @oyi77 +- **chore(release):** clear the `release/v3.8.51` base-red gates — docs migration count (+38 i18n mirrors), stryker `tap.testFiles`, lockfile registry host, stale ESLint suppressions, the 7 lint regressions they were hiding, the stale `APIKEY_PROVIDERS` partition count (231 → 233), the `glm-5.3-max` tier missing from the GLM test table, a dead `modelType === "chat"` comparison in the models catalog, and the `resolveLiveWsUrl` / `sanitizeLiveWsPort` helpers from #11388 that a later merge dropped from `wsPath.ts` (dashboard live WebSocket URL resolution) ([#11502](https://github.com/diegosouzapw/OmniRoute/pull/11502)) — thanks @MumuTW +- **chore(free-models):** the free-tier regime table now declares which totals figure each regime feeds and whether it may take the no-auth shortcut, so the sets that used to repeat those answers by hand are derived from it — a new `freeType` no longer compiles until it has answered every question, instead of silently contributing to no total ([#11537](https://github.com/diegosouzapw/OmniRoute/pull/11537)) — thanks @maxmad64bis +- **docs(routing):** LKGP strategy copy now states provider-level stickiness and the explicit rules fallback instead of promising per-target pinning ([#11555](https://github.com/diegosouzapw/OmniRoute/pull/11555)) — thanks @maxmad64bis +- **test(chatcore):** pin the response tool-name alias resolution against the ordering hazard that broke every Gemini/Antigravity MCP tool call in v3.8.49. `extractRequestToolIdentityMap` deletes `translatedBody._toolNameMap`, so the later read is always undefined and the ledger survives only through the `requestToolIdentityMap` fallback — removing that fallback previously left the entire tool-name suite green. The resolution moves into `resolveResponseToolNameMap()` next to the map it depends on, with a regression guard that fails without the recovery. No behaviour change. ([#11575](https://github.com/diegosouzapw/OmniRoute/pull/11575)) — thanks @amartinawi +- **docs(env):** document `OMNIROUTE_CORPUS_CACHE_SIZE` in `.env.example` and `ENVIRONMENT.md`, so the env/docs contract check stops failing Docs Gates on every branch ([#11576](https://github.com/diegosouzapw/OmniRoute/pull/11576)) — thanks @ntdatt812 +- **chore(stryker):** register `route-guard-tunnel-processes-local-only` and `universal-quota-aware-routing` in `tap.testFiles`, so their mutant kills count for the three modules they cover ([#11577](https://github.com/diegosouzapw/OmniRoute/pull/11577)) — thanks @ntdatt812 +- **test(authz):** pin `LOCAL_ONLY_API_GET_EXEMPTIONS` by exact membership instead of by entry count, so the guard names the offending path and also catches a substitution ([#11580](https://github.com/diegosouzapw/OmniRoute/pull/11580)) — thanks @ntdatt812 +- **test(build):** pin `resolveNextBuildBundlerFlag` to the `OMNIROUTE_USE_TURBOPACK` contract it implements, across runtimes, instead of asserting an unimplemented Bun override ([#11583](https://github.com/diegosouzapw/OmniRoute/pull/11583)) — thanks @ntdatt812 +- **test(claude-web):** the Claude Web transport test's barrel anchors no longer pin the exact construction spelling. #11421 made the executor map lazy, so `/"claude-web": new ClaudeWebExecutor\(\)/` stopped matching and the test went red on `release/v3.8.51` itself — a base-red gate every open PR inherited. The anchors now match the alias key and the class name and tolerate either spelling in between, and were checked to still fail when an alias is genuinely repointed at another executor ([#11585](https://github.com/diegosouzapw/OmniRoute/pull/11585)) — thanks @ntdat812 +- **test(models):** pin `qwen3.8-max` resolution per provider — two of the four catalogs now serve the bare id and no longer carry `-preview` ([#11590](https://github.com/diegosouzapw/OmniRoute/pull/11590)) — thanks @ntdatt812 +- **test(executors):** drop the stale 128-tool truncation assertion that contradicted the #11444 pass-through contract ([#11591](https://github.com/diegosouzapw/OmniRoute/pull/11591)) — thanks @ntdatt812 +- **test(token-health):** pin the current terminal-skip boundary — expired with retry budget is probed, `account_deactivated` stays skipped ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)) — thanks @ntdatt812 +- **test(models):** follow #11503's retarget of the `gemini-3-pro-high` alias to the hyphenated catalog id ([#11593](https://github.com/diegosouzapw/OmniRoute/pull/11593)) — thanks @ntdatt812 +- **refactor(video bridge):** extract per-video acquisition, whole-result caching, description, and metrics/abort/cleanup out of `VideoBridgeGuardrail.preCall` into a `processVideoPart` seam in a new `videoBridgePipeline.ts`, behind explicit `VideoMediaBrokerPort`, `VideoAudioTranscriptionPort`, and `VideoDrilldownPort` boundaries; `preCall` now only handles request traversal, policy, and response aggregation. The Video tab's FFmpeg/ffprobe runtime status is now an explicit `unknown` / `restricted` / `unavailable` / `available` state instead of a nullable boolean pair, fixing a case where an in-flight or failed probe was mislabeled as "install FFmpeg" ([#11657](https://github.com/diegosouzapw/OmniRoute/issues/11657)). ([#12016](https://github.com/diegosouzapw/OmniRoute/pull/12016)) +- **test(opencode-plugin):** add unit test coverage for `formatFreeBudget()` naming helper ([#11660](https://github.com/diegosouzapw/OmniRoute/pull/11660)) — thanks @f9td56dbgh-hub +- **docs(video):** clarify that the Video Bridge transcript `source` field (`client`, `embedded`, `audio-bridge`) is presently caller-declared and not yet server-verified — OmniRoute enforces the enum shape but does not cryptographically confirm that an `embedded`/`audio-bridge` label came from a server-owned extraction ([#11661](https://github.com/diegosouzapw/OmniRoute/issues/11661)). ([#12001](https://github.com/diegosouzapw/OmniRoute/pull/12001)) +- **chore(providers):** retire the distributed Raycast Relay and Hailuo Web integrations after a provenance audit confirmed substantial derivation from GPL-3.0 sources; the official GitHub and MiniMax API providers remain available. Migration 163 disables legacy connections and invalidates their active leases while preserving connection IDs, audit/history rows, quota snapshots, usage records, and fail-closed API-key connection policies. ([#11691](https://github.com/diegosouzapw/OmniRoute/pull/11691)) +- **chore(providers):** retire the Felo Web provider while its GPL-derived provenance remains on licensing HOLD ([#11698](https://github.com/diegosouzapw/OmniRoute/pull/11698)) — stale runtime and database references now fail closed while existing connection, lease, and usage history remains preserved for auditability. +- **fix(sse):** retire the Gemini Web image-generation bridge while provenance and licensing remain on HOLD, preserving Gemini Web chat/auth/tools and legitimate Gemini/Nano Banana image providers ([#11708](https://github.com/diegosouzapw/OmniRoute/pull/11708)) +- **chore(audio):** retire the built-in EdgeTTS provider and its 11-voice catalog while provenance/licensing review remains on HOLD; the generic `/v1/audio/speech` endpoint and the other speech providers remain available ([#11711](https://github.com/diegosouzapw/OmniRoute/pull/11711)) +- **fix(providers):** retire the unofficial Qwen Web integration and its `qwen-web`/`qw` identities while third-party provenance remains on HOLD ([#11713](https://github.com/diegosouzapw/OmniRoute/pull/11713)) — direct requests now fail closed with a sanitized HTTP 410 and combos skip the retired target, while migration guards preserve connection IDs, allowlists, usage/audit history and invalidate active leases. Supported Qwen API, Qwen Code, cloud, token-plan, audio, local and model-name surfaces remain available; restoring Qwen Web requires a qualified provenance/license decision. +- **fix(providers):** retire `microsoft-designer-web` and its `msdesigner` alias from runtime and catalogs while preserving stored credentials, provider history and allowlists as inactive records — source dependency on gpt4free's [`MicrosoftDesigner.py`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/g4f/Provider/needs_auth/MicrosoftDesigner.py) is proven at snapshot `0943d16eef757a44deb60651c07bbe5fa958cafe`, whose [`LICENSE`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/LICENSE) is GPL-3.0; expressive adaptation is probable, which records provenance/licensing risk without asserting a legal conclusion ([#11720](https://github.com/diegosouzapw/OmniRoute/pull/11720)). +- **docs(legal):** centralize pinned MIT notices for the vendored GCF codec and local flag assets ([#11726](https://github.com/diegosouzapw/OmniRoute/pull/11726)) +- Split the 2,952-line `adobeFireflyClient.ts` into nine focused modules (catalog, credentials, payload, ARP session, upload, responses, IMS, account, poll) behind an unchanged barrel — no consumer edits and no public API change (#11728). — thanks @MumuTW +- **fix(dashboard):** replaced the Model Lockout card's unprovenanced notification MP3 with a short, locally synthesized Web Audio chime ([#11731](https://github.com/diegosouzapw/OmniRoute/pull/11731)) — both toggles retain optional audible feedback without shipping replacement media or third-party code, and unsupported, suspended, or rejected audio contexts fail open without blocking the setting change. +- **chore(deepseek-web):** remove the provenance-unresolved DeepSeek PoW WASM binary and its runtime loader/tracing while retaining the existing JavaScript solver (slower at high difficulty) ([#11732](https://github.com/diegosouzapw/OmniRoute/pull/11732)). +- **chore(assets):** remove four unused provider assets whose redistribution provenance could not be verified while retaining the active Freebuff SVG ([#11735](https://github.com/diegosouzapw/OmniRoute/pull/11735)) +- **docs(assets):** retired two raster images whose redistribution provenance could not be verified ([#11736](https://github.com/diegosouzapw/OmniRoute/pull/11736)) — PicoClaw documentation now reuses the existing SVG, while Zoo Code remains a text link without an image. +- **docs(legal):** add pinned notices for six LobeHub-derived provider SVGs and 65 byte-exact theSVG assets, retaining registry claims as unverified metadata with explicit trademark and HOLD boundaries ([#11737](https://github.com/diegosouzapw/OmniRoute/pull/11737)). +- **chore(quality):** added a versioned 225/225 provider-asset provenance manifest and blocking drift gate ([#11743](https://github.com/diegosouzapw/OmniRoute/pull/11743)) — the inventory pins magic MIME and SHA-256 for every physical file, records immutable source matches for 71 assets, keeps 69 probable and 85 unresolved entries explicit but non-blocking, and treats provenance as source evidence rather than copyright or trademark clearance. +- **refactor(mitm):** replace four previously derived Traffic Inspector utilities — conversation normalization, SSE merging, secret masking, and header sanitization — with independent clean-room implementations based on public provider schemas, WHATWG SSE, and RFC 6750, while keeping the two independently classified passthrough surfaces unchanged ([#11748](https://github.com/diegosouzapw/OmniRoute/pull/11748)). +- Added a unit test that fails seven days before any dated pack under `config/` (`validUntil` and sibling keys) lapses, naming the file and key. The Alibaba free-tier pack expired on 2026-08-27 and turned every PR red the next morning with no commit involved; renewal now happens on someone's terms, not the clock's. ([#11891](https://github.com/diegosouzapw/OmniRoute/pull/11891)) +- `check:workflows` now fails (under `--strict`/`--ratchet`) when any job routed to a self-hosted runner publishes with `--provenance` — npm rejects that with `422` at the registry, which in v3.8.50 only surfaced after the tag and Docker images were public. ([#11895](https://github.com/diegosouzapw/OmniRoute/pull/11895)) +- `scripts/ops/runner-janitor.sh` now proves a path is idle with one `lsof` snapshot and removes stale leftovers itself (tmpfs after 3 h — it is RAM — disk after 24 h), kills orphan `next-build` processes, prunes checkouts of stopped runners, and alerts on memory pressure; `--dry-run` shows exactly what it would do. `docs/ops/RUNNER_BOX.md` reconciled to the measured box (31 GB, 10 listeners). ([#11893](https://github.com/diegosouzapw/OmniRoute/pull/11893)) +- The `next-build` artefact (1.3 GB) is now written and read under `$RUNNER_TEMP` (per-runner, on disk) instead of `/tmp`, which on the self-hosted pool is a 12 GB tmpfs in RAM. Landing it there took 27–32 of the publish job's 76 minutes, and the fixed `/tmp/e2e-build.tar.gz` name let E2E jobs on different runners overwrite each other's download. ([#11896](https://github.com/diegosouzapw/OmniRoute/pull/11896)) +- The CI `build` job now runs in two concurrency lanes — `main` and pull requests — so a release build is never queued behind (or OOM-killed beside) PR builds on the self-hosted pool, which holds one `next-build` comfortably and two at the edge. ([#11901](https://github.com/diegosouzapw/OmniRoute/pull/11901)) +- Re-freeze the ESLint suppressions on `release/v3.8.51` from a clean-room measurement (2 stale file entries pruned, 55 pre-existing `no-explicit-any` in six new files frozen under #11924) and drop the dead `GPT_SIZE_MAP` constant orphaned by the Adobe Firefly client split, so `No new ESLint warnings` stops failing every PR with exit 2 (Refs #11924) ([#11955](https://github.com/diegosouzapw/OmniRoute/pull/11955)) +- Type the 55 `no-explicit-any` sites that had been frozen under #11924 — four redundant casts in `socksConnectorWithFamily.ts` (undici/socks types already accept them) and the mocks/fixtures of the socks-timeout and isFree suites — and drop their suppression entries; the ESLint ratchet shrinks from 5487 to 5432 (Closes #11924) ([#11975](https://github.com/diegosouzapw/OmniRoute/pull/11975)) +- Take the two hosted-runner builds off the PR rail: `Build App` (`build.yml`) is `workflow_dispatch`-only and `DAST smoke (PR)` runs only for PRs into `main` — the 7 GB hosted VM cannot build `release/v3.8.51` in any profile (19/30 red, VM shutdown ~8 min into `next build`) and both checks had turned into permanent noise on every release PR; the bundle stays validated by `ci.yml` on `main` and by `nightly-release-green` on `release/**` (Closes #11946) ([#11962](https://github.com/diegosouzapw/OmniRoute/pull/11962)) +- Move the four nightly jobs that build the backend (`nightly-schemathesis`, `nightly-llm-security` promptfoo + garak, `nightly-resilience` axe-a11y) off the hosted 7 GB runner — where they died on `release/v3.8.51` unseen — onto the box's new `omni-light` pool (two listeners), and document the reshaped fleet (4 active OmniRoute listeners: 2 `omni-build` + 2 `omni-light`, janitor ceiling 4) (Closes #11965) ([#11967](https://github.com/diegosouzapw/OmniRoute/pull/11967)) +- Stop painting every fork PR into `release/**` red: `quality.yml` `Build (advisory)` is skipped (GitHub still reports `continue-on-error` failures as check FAILURE). Hosted `ubuntu-latest` cannot finish `npm run build` on this tree — same class as #11962 taking `build.yml` off the PR rail. `docker-publish.yml` amd64 now runs on the `.113` `omni-build` pool (31 GB / 32 cores, two listeners) with Turbopack, shares the `heavy-build-main` lane with `ci.yml` `Build` so it queues instead of becoming a third heavy, and keeps per-ref concurrency (a merge storm was starting 8 concurrent OOM builds). arm64 stays on `ubuntu-24.04-arm` with webpack — there is no ARM box. Fallback when `USE_VPS_RUNNER` is off: hosted amd64 + webpack (#11976). ([#12021](https://github.com/diegosouzapw/OmniRoute/pull/12021)) +- `docker-publish.yml` builds with webpack (`OMNIROUTE_USE_TURBOPACK=0`) on **both** arches, including amd64 on the `.113` `omni-build` pool. Turbopack had the RAM (31 GB, `omniroute-113-6`) and still panicked (`TurbopackInternalError: there must be a path to a root`, run 33253576569, 26 min in). The same tree's arm64 webpack build on hosted `ubuntu-24.04-arm` succeeded (run 33264823398). Dockerfile already documents webpack as the Docker escape hatch (#11976). ([#12050](https://github.com/diegosouzapw/OmniRoute/pull/12050)) +- **docs(free-tier):** declare the counting vs deciding regimes for "is it free?" and guard the deciding path from DB-backed catalog resolution ([#12226](https://github.com/diegosouzapw/OmniRoute/pull/12226)) — thanks @maxmad64bis +- **docs(auto-combo):** The mode pack table in `docs/routing/AUTO-COMBO.md` now lists all six shipped packs with every weight each one sets, replacing a four-pack table whose numbers had also drifted from the source. It states plainly that no pack sets `quality`, so selecting any pack silences the observed-quality signal. Six more documents that quote the scoring factor count joined the `check:docs-counts` gate, which caught five stale claims — including one naming nine factors that do not exist — and two stale mode pack counts. The dashboard routing panel, which offered four of the six packs and labelled the default strategy "6-Factor Scoring", is now covered by a test; the two packs it was missing are `reliability-first` and `chaos-mode`, the latter labelled as the fault-injection profile it is rather than as one more routing preference ([#12316](https://github.com/diegosouzapw/OmniRoute/pull/12316)) — thanks @maxmad64bis +- **chore(quality):** rebaseline `src/lib/db/apiKeys.ts` for the ACL the key-creation path now preserves ([#12352](https://github.com/diegosouzapw/OmniRoute/pull/12352)) ([#12673](https://github.com/diegosouzapw/OmniRoute/pull/12673)) — thanks @KrzysiekSko +- **chore(providers):** bump the Claude Code wire identity and the Devin bridge image pin from `2.1.220` to `2.1.258` ([#12402](https://github.com/diegosouzapw/OmniRoute/pull/12402)) — thanks @ggiak ([#12604](https://github.com/diegosouzapw/OmniRoute/pull/12604)) +- **docs(env):** align `.env.example`, the README Bun section, and the troubleshooting guide with the code: `OMNIROUTE_USE_TURBOPACK` also governs `npm run build` (not dev-only), `bun run build` follows that flag instead of auto-selecting Webpack, `OMNIROUTE_CHAT_MAX_HEAVY_IN_FLIGHT` is unset by default (no request-count cap), and the structural `503 chat_admission_busy` message matches `chatAdmissionResponses.ts` (#12404 — thanks @pacocartones) +- **chore(electron):** upgrade the desktop app to Electron 44 (Chromium 152, Node 24.18.1) ([#12217](https://github.com/diegosouzapw/OmniRoute/pull/12217)). **Requires macOS 13 (Ventura) or later** — Chromium dropped macOS 12 (Monterey), so Monterey users must stay on an earlier OmniRoute desktop build. Windows and Linux are unaffected; the app already shipped only x64/arm64, so Electron 44 dropping 32-bit builds changes nothing. Removes the `openAsHidden`/`wasOpenedAsHidden` login-item fields deleted in Electron 44 — hidden autostart continues to work through the `--hidden` argument registered with the login item ([#12554](https://github.com/diegosouzapw/OmniRoute/pull/12554)) — thanks @app/dependabot +- **chore(quality):** rebaseline `src/sse/handlers/chat.ts` for the effective-input persistence the continuation fix needs ([#12641](https://github.com/diegosouzapw/OmniRoute/pull/12641)) ([#12680](https://github.com/diegosouzapw/OmniRoute/pull/12680)) — thanks @hartmark +- `Coverage` job on `ci.yml`: the informational Codecov upload gets its own 5-minute ceiling and `continue-on-error`, and the job budget grows from 20 to 30 minutes (the 8-shard c8 merge alone takes ~10) — a stalled upload no longer ends the job `cancelled` and drags a fully green `main` run's conclusion down with it ([#11972](https://github.com/diegosouzapw/OmniRoute/pull/11972)) +- CI hardening on the PR rail: the four `Unit Tests fast-path` shards get `timeout-minutes: 30` (a hung shard held a PR for 64 min instead of GitHub's 6 h default) and both ESLint file caches lose their `restore-keys` fallback, so a cache built under another suppressions file or lint config can no longer report stale verdicts (Refs #11600, #11924) ([#11963](https://github.com/diegosouzapw/OmniRoute/pull/11963)) +- Every CI job that runs a `next build` (`build`, the npm `publish`, both release-green validations) now targets the `omni-build` runner label, which only two of the eight self-hosted runners carry. The box holds one build comfortably and two at the edge; a third now queues on GitHub instead of being OOM-killed by the kernel. ([#11932](https://github.com/diegosouzapw/OmniRoute/pull/11932)) +- **refactor(combo):** move `handleRoundRobinCombo` (and `resolveTargetTokenLimit`) into `open-sse/services/combo/roundRobinCombo.ts`. `combo.ts` drops from 2164 to 1014 lines (`split("\n").length`); the leaf is 1199 (under the 1200 new-file cap). The round-robin call site uses a dynamic `import()` so `releaseStickyPinOnFailure` / `clearStaleLKGP` can stay exported from `combo.ts` without a static cycle. Skip / sticky / semaphore / safety-timer behavior is unchanged. ([#12811](https://github.com/diegosouzapw/OmniRoute/pull/12811)) — thanks @HouMinXi +- **chore(quality):** rebaseline the file-size caps the error-boundary campaign grew past (`open-sse/executors/codex.ts`, `open-sse/vendor/codex-chatgpt-web/bridge.ts`, both via [#12444](https://github.com/diegosouzapw/OmniRoute/pull/12444)) ([#12654](https://github.com/diegosouzapw/OmniRoute/pull/12654)) +- **chore(quality):** rebaseline the file-size caps the hartmark batch grew past (`combos/page.tsx` via [#12355](https://github.com/diegosouzapw/OmniRoute/pull/12355), `open-sse/services/combo.ts` via [#12338](https://github.com/diegosouzapw/OmniRoute/pull/12338)) ([#12623](https://github.com/diegosouzapw/OmniRoute/pull/12623)) — thanks @hartmark +- **chore(quality):** rebaseline the file-size caps the HouMinXi batch grew past when its PRs stacked (`providers/page.tsx`, `chatCore.ts`, `accountFallback.ts`) — each PR measured correctly in isolation, none saw the stacking ([#12619](https://github.com/diegosouzapw/OmniRoute/pull/12619)) +- **chore(quality):** rebaseline `open-sse/services/combo.ts` for the reset-aware scoring the HouMinXi batch stacked ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) ([#12678](https://github.com/diegosouzapw/OmniRoute/pull/12678)) — thanks @HouMinXi +- **docs:** sync provider catalog count (direct commit `4f11b2ae3d`) +- **docs(skills):** sync omni-inference search fallback (direct commit `b4e76a7ed9`) +- **chore(lint):** prune two stale base suppressions (direct commit `1325047d56`) +- **test(auto-combo):** tolerate full-suite rotation load (direct commit `ad3e293f9f`) +- **test:** drain inherited quality snapshot reds (direct commit `21ed68d8ac`) +- **chore(quality):** rebaseline measured CLI bundle growth (direct commit `d35b3f9779`) +- **test(ui):** reproduce invalid AnySearch icon alias (direct commit `12cc6ca834`) +- **test(translator):** fix the relative imports of the relocated deferred-finish test (direct commit `777d9d1629`) +- **docs(counts):** bring the documented provider and migration counts to the measured values (direct commit `e70bea30e9`) +- **test(security):** drop the guard-target set left unused by the sync (direct commit `a32ba0e362`) +- **chore(db):** renumber model_capabilities migration 163 → 169 (cross-branch collision with 163_radar_feed_cache_generated_at) (direct commit `70f5d4cbf9`) +- **docs(cli):** document CLI_PRIME_AGENT_BIN and correct the CLI Agents count (direct commit `9058e39b61`) +- **chore(deps):** refresh runtimes and adopt ESLint 10 ([#11259](https://github.com/diegosouzapw/OmniRoute/pull/11259)) — thanks @backryun +- **refactor(local-corpus):** implement dynamic root resolution and LRU cache ([#11491](https://github.com/diegosouzapw/OmniRoute/pull/11491)) — thanks @raheemuddin786 +- **chore(quality):** prune stale eslint-suppressions entries ([#11596](https://github.com/diegosouzapw/OmniRoute/pull/11596)) +- **chore(ci):** unblock quality gates - env-doc sync + stryker tap.testFiles registration (#9282) ([#11609](https://github.com/diegosouzapw/OmniRoute/pull/11609)) — thanks @Meet6338-X +- **chore(ci):** point the circular-deps gate at dpdm's real JS entrypoint ([#11615](https://github.com/diegosouzapw/OmniRoute/pull/11615)) — thanks @benzntech +- **chore(quality):** rebaseline file-size for #11448/#11495/#11561 growth ([#11630](https://github.com/diegosouzapw/OmniRoute/pull/11630)) +- **deps:** realign onnxruntime-node pin to @huggingface/transformers' 1.24.3 lockstep ([#11633](https://github.com/diegosouzapw/OmniRoute/pull/11633)) — thanks @jonlwheat2-gif +- **test(autoCombo):** port models_dev_tier #11508 guard from node:test to vitest ([#11635](https://github.com/diegosouzapw/OmniRoute/pull/11635)) — thanks @jonlwheat2-gif +- **chore(quality):** rebaseline file-size for #11083/#11631/#11628 growth ([#11636](https://github.com/diegosouzapw/OmniRoute/pull/11636)) +- **chore(lint):** prune stale ESLint suppressions (base-red #11449) ([#11644](https://github.com/diegosouzapw/OmniRoute/pull/11644)) — thanks @jonlwheat2-gif +- **docs(changelog):** aggregate the ten v3.8.50 fragments into CHANGELOG.md ([#11683](https://github.com/diegosouzapw/OmniRoute/pull/11683)) +- **chore(search):** drop unreachable credential sweep after #11642 ([#11687](https://github.com/diegosouzapw/OmniRoute/pull/11687)) — thanks @HouMinXi +- **ci:** add API route TypeScript regression gate ([#11705](https://github.com/diegosouzapw/OmniRoute/pull/11705)) — thanks @Sabeekhann +- **docs(dedup):** correct the CodeQL claim on the tenant-namespace comment ([#11712](https://github.com/diegosouzapw/OmniRoute/pull/11712)) +- **docs(changelog):** add consolidated v3.8.50 stats, top-25 ranking and a .mailmap ([#11715](https://github.com/diegosouzapw/OmniRoute/pull/11715)) +- **docs:** recommend gstack for AI-assisted workflows ([#11770](https://github.com/diegosouzapw/OmniRoute/pull/11770)) — thanks @santosraju99-hub +- **chore(quality):** tighten complexity/cognitive-complexity ratchets to the current tip; land the missed gateways.ts rebaseline ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **docs(i18n):** finish Freepik → Magnific rebrand in locale strings and README ([#11772](https://github.com/diegosouzapw/OmniRoute/pull/11772)) — thanks @turbolego +- **refactor(auto-combo):** fix divergent scoring in combo health reporting ([#11854](https://github.com/diegosouzapw/OmniRoute/pull/11854)) — thanks @jacobsparts +- **chore(changelog):** add missing fragments for #11919/#11918/#11916 ([#11938](https://github.com/diegosouzapw/OmniRoute/pull/11938)) +- **test(check):** escape the runs-on fixture with JSON.stringify, not a quote-only replace ([#11942](https://github.com/diegosouzapw/OmniRoute/pull/11942)) (direct commit `fb7445eaa3`) +- **test(release):** align five suites with the contracts #11933, #11919 and #11876 shipped on release/v3.8.51 ([#11944](https://github.com/diegosouzapw/OmniRoute/pull/11944)) +- **chore(quality):** remove unreachable code and restore test discovery ([#11950](https://github.com/diegosouzapw/OmniRoute/pull/11950)) — thanks @backryun +- **docs(agents):** sync-back landings are fast-forward, never squash ([#11964](https://github.com/diegosouzapw/OmniRoute/pull/11964)) +- **test(infra):** retry recursive temp-dir removal instead of failing a shard on ENOTEMPTY (#11966) ([#11968](https://github.com/diegosouzapw/OmniRoute/pull/11968)) +- **docs(ops):** the .113 heavy-build ceiling is one runner, not two ([#12048](https://github.com/diegosouzapw/OmniRoute/pull/12048)) +- **test(ui):** correct inactive auto-fetch expectation ([#12098](https://github.com/diegosouzapw/OmniRoute/pull/12098)) — thanks @RaviTharuma +- **docs:** document native dependency check escape hatch ([#12101](https://github.com/diegosouzapw/OmniRoute/pull/12101)) — thanks @RaviTharuma +- **test(providers):** regenerate the translate-path golden for OrcaRouter (#11923) ([#12118](https://github.com/diegosouzapw/OmniRoute/pull/12118)) +- **chore(quality):** velocity phase — loosen every numeric baseline by 20% until v4.0, monitor headroom nightly ([#12125](https://github.com/diegosouzapw/OmniRoute/pull/12125)) +- **chore(lint):** batch 0 of #12146 — type the call-log-cap sqlite rows instead of 45 as-any casts ([#12157](https://github.com/diegosouzapw/OmniRoute/pull/12157)) +- **chore(lint):** batch 4 of #12146 — shared/components react-hooks violations resolved ([#12159](https://github.com/diegosouzapw/OmniRoute/pull/12159)) +- **chore(lint):** batch 1 of #12146 — dashboard/cli-code react-hooks violations resolved ([#12160](https://github.com/diegosouzapw/OmniRoute/pull/12160)) +- **chore(lint):** batch 3 of #12146 — dashboard/settings react-hooks violations resolved ([#12162](https://github.com/diegosouzapw/OmniRoute/pull/12162)) +- **chore(lint):** batch 2 of #12146 — resolve the react-hooks compiler violations in dashboard/providers ([#12163](https://github.com/diegosouzapw/OmniRoute/pull/12163)) +- **chore(quality):** register search-432 cooldown test in stryker tap.testFiles ([#12170](https://github.com/diegosouzapw/OmniRoute/pull/12170)) +- **chore(lint):** batch 5 of #12146 — combos, endpoint, provider-stats, api-manager and costs react-hooks violations resolved ([#12174](https://github.com/diegosouzapw/OmniRoute/pull/12174)) +- **build:** add contributor fast profile ([#12192](https://github.com/diegosouzapw/OmniRoute/pull/12192)) — thanks @rafacpti23 +- **docs(readme):** README touch-ups ([#12193](https://github.com/diegosouzapw/OmniRoute/pull/12193)) ([#12194](https://github.com/diegosouzapw/OmniRoute/pull/12194)) ([#12202](https://github.com/diegosouzapw/OmniRoute/pull/12202)) — thanks @rafacpti23 +- **build:** force the webpack bundler in the contributor build profile ([#12197](https://github.com/diegosouzapw/OmniRoute/pull/12197)) — thanks @rafacpti23 +- **build:** skip standalone packaging in the contributor profile ([#12198](https://github.com/diegosouzapw/OmniRoute/pull/12198)) — thanks @rafacpti23 +- **docs(audit):** align every published number with the code and harden check:docs-counts ([#12200](https://github.com/diegosouzapw/OmniRoute/pull/12200)) +- **build:** stub the instrumentation entrypoints in the contributor profile ([#12203](https://github.com/diegosouzapw/OmniRoute/pull/12203)) — thanks @rafacpti23 +- **build:** omit the standalone output target for contributor builds ([#12204](https://github.com/diegosouzapw/OmniRoute/pull/12204)) — thanks @rafacpti23 +- **chore(lint):** batch 6 of #12146 — memory, radar, audit, analytics, cache, usage, activity, home and RequestLoggerV2 react-hooks violations resolved ([#12208](https://github.com/diegosouzapw/OmniRoute/pull/12208)) +- **docs(diagrams):** rename number-carrying diagram files to stable names ([#12210](https://github.com/diegosouzapw/OmniRoute/pull/12210)) +- **docs(dashboard):** redraw onboarding tier-flow SVGs for the real 4-tier model ([#12211](https://github.com/diegosouzapw/OmniRoute/pull/12211)) +- **docs(api):** document every implemented route in openapi.yaml (276 → 692 paths) ([#12212](https://github.com/diegosouzapw/OmniRoute/pull/12212)) +- **chore(lint):** batch 7 of #12146 — final src tail: 37 react-hooks violations across 33 files resolved ([#12244](https://github.com/diegosouzapw/OmniRoute/pull/12244)) +- Revert "docs: recommend gstack for AI-assisted workflows (#11770)" ([#12248](https://github.com/diegosouzapw/OmniRoute/pull/12248)) ([#12249](https://github.com/diegosouzapw/OmniRoute/pull/12249)) +- **docs:** Chaos Mode setup guide + weighted strategy semantics ([#12250](https://github.com/diegosouzapw/OmniRoute/pull/12250)) +- **docs(agents):** protected-surface merge rule — operator approval for agent-instruction files ([#12253](https://github.com/diegosouzapw/OmniRoute/pull/12253)) +- **chore(quality):** register native-codex turn-pin tests in stryker tap.testFiles ([#12263](https://github.com/diegosouzapw/OmniRoute/pull/12263)) +- **chore(quality):** dedupe tap.testFiles entries added by racing base-red fixes ([#12265](https://github.com/diegosouzapw/OmniRoute/pull/12265)) +- **docs(readme):** introduce OmniRouteTray — the macOS menu-bar companion ([#12276](https://github.com/diegosouzapw/OmniRoute/pull/12276)) — thanks @ggiak +- **chore(proxy):** purge the legacy 1proxy residue — sync/rotator modules, dead DB exports, dead settings tab and flag, docs (#12091) ([#12290](https://github.com/diegosouzapw/OmniRoute/pull/12290)) +- **chore(quality):** baseline-headroom skips generated and vendored files in the fileSize worst-file signal ([#12291](https://github.com/diegosouzapw/OmniRoute/pull/12291)) +- **refactor(video):** unify the JPEG frame data-URI contract ([#12322](https://github.com/diegosouzapw/OmniRoute/pull/12322)) +- **chore(deps):** freeze onnxruntime-node and eslint-plugin-react-hooks out of dependabot groups ([#12329](https://github.com/diegosouzapw/OmniRoute/pull/12329)) +- **test(executors):** restore the #10986 reasoning-only fallback guards ([#12364](https://github.com/diegosouzapw/OmniRoute/pull/12364)) — thanks @pacocartones +- **chore(quality):** re-tighten the file-size ratchet to the real LOC (plan 3.8.52 task 0) ([#12411](https://github.com/diegosouzapw/OmniRoute/pull/12411)) +- **test(grok):** format web executor suite ([#12412](https://github.com/diegosouzapw/OmniRoute/pull/12412)) +- **chore(quality):** ratchet open-sse typecheck baseline to zero ([#12418](https://github.com/diegosouzapw/OmniRoute/pull/12418)) +- **test(build):** guard the artifact path policy arrays against duplicates ([#12422](https://github.com/diegosouzapw/OmniRoute/pull/12422)) +- **chore(lint):** adopt eslint-plugin-react-hooks 7.1.1 ([#12428](https://github.com/diegosouzapw/OmniRoute/pull/12428)) +- **chore(providers):** remove a keyless provider integration at its operator's request ([#12440](https://github.com/diegosouzapw/OmniRoute/pull/12440)) +- **docs(providers):** register providers removed at their operator's request and guard against reintroduction ([#12478](https://github.com/diegosouzapw/OmniRoute/pull/12478)) +- **chore(quality):** base fixes — stryker tap.testFiles + node_modules cache key ([#12482](https://github.com/diegosouzapw/OmniRoute/pull/12482)) +- **docs(arch):** one-process recipe for tens of long /v1/responses ([#12493](https://github.com/diegosouzapw/OmniRoute/pull/12493)) — thanks @RaviTharuma +- **chore(quality):** tighten the CodeQL ratchet baseline from 11 to 6 ([#12530](https://github.com/diegosouzapw/OmniRoute/pull/12530)) +- **chore(deps):** pin browserslist override to ^4.28.8 ([#12592](https://github.com/diegosouzapw/OmniRoute/pull/12592)) — thanks @KooshaPari +- **chore(deps):** bump fast-uri to 3.1.7 in the electron lockfile ([#12601](https://github.com/diegosouzapw/OmniRoute/pull/12601)) +- **docs(quality):** document how the CodeQL ratchet refreshes and how to tighten it ([#12611](https://github.com/diegosouzapw/OmniRoute/pull/12611)) +- **test(ci):** pin the openapi-security-tiers two-arm contract with an executing gate test (#12581) ([#12652](https://github.com/diegosouzapw/OmniRoute/pull/12652)) +- **docs(gamification):** align XP Rewards table with code (#12501) ([#12667](https://github.com/diegosouzapw/OmniRoute/pull/12667)) — thanks @KooshaPari +- **refactor(combo):** split executeTarget into gates, attempt, and loop ([#12746](https://github.com/diegosouzapw/OmniRoute/pull/12746)) — thanks @HouMinXi +- **docs(security):** document socket.yml scanner config + CI workflow link (#12575) ([#12764](https://github.com/diegosouzapw/OmniRoute/pull/12764)) — thanks @KooshaPari +- **chore(ci):** guard commit identity in pre-commit to stop author misattribution ([#12772](https://github.com/diegosouzapw/OmniRoute/pull/12772)) +- **deps:** 17 Dependabot bumps — bump electron from 43.4.0 to 43.4.1 in /electron ([#11426](https://github.com/diegosouzapw/OmniRoute/pull/11426)); bump the development group across 1 directory with 8 updates ([#11428](https://github.com/diegosouzapw/OmniRoute/pull/11428)); bump the production group across 1 directory with 11 updates ([#11440](https://github.com/diegosouzapw/OmniRoute/pull/11440)); chore(deps): bump github/codeql-action/init from 4.37.7 to 4.37.8 ([#11925](https://github.com/diegosouzapw/OmniRoute/pull/11925)); chore(deps): bump github/codeql-action from 4.37.7 to 4.37.8 ([#11926](https://github.com/diegosouzapw/OmniRoute/pull/11926)); chore(deps): bump github/codeql-action/analyze from 4.37.7 to 4.37.8 ([#11927](https://github.com/diegosouzapw/OmniRoute/pull/11927)); build(deps): bump github/codeql-action/init from 4.37.8 to 4.37.9 ([#12345](https://github.com/diegosouzapw/OmniRoute/pull/12345)); build(deps): bump github/codeql-action/analyze from 4.37.8 to 4.37.9 ([#12346](https://github.com/diegosouzapw/OmniRoute/pull/12346)); bump the development group across 1 directory with 2 updates ([#12347](https://github.com/diegosouzapw/OmniRoute/pull/12347)); build(deps): bump github/codeql-action from 4.37.8 to 4.37.9 ([#12349](https://github.com/diegosouzapw/OmniRoute/pull/12349)); bump browserslist from 4.28.2 to 4.28.8 ([#12396](https://github.com/diegosouzapw/OmniRoute/pull/12396)); bump the production group across 1 directory with 4 updates ([#12399](https://github.com/diegosouzapw/OmniRoute/pull/12399)); bump @xmldom/xmldom ([#12500](https://github.com/diegosouzapw/OmniRoute/pull/12500)); bump qs from 6.15.2 to 6.16.0 ([#12512](https://github.com/diegosouzapw/OmniRoute/pull/12512)); bump @xmldom/xmldom from 0.9.10 to 0.9.12 ([#12513](https://github.com/diegosouzapw/OmniRoute/pull/12513)); bump fast-uri from 3.1.5 to 3.1.7 ([#12514](https://github.com/diegosouzapw/OmniRoute/pull/12514)); bump @humanfs/node from 0.16.7 to 0.16.8 ([#12515](https://github.com/diegosouzapw/OmniRoute/pull/12515)) +- Clear the `release/v3.8.51` `check:agent-skills-sync` base-red: regenerate `skills/cli-tunnel/SKILL.md` so the `tunnel create [type]` positional that #13009 taught the generator to read is reflected in the committed skill. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` typecheck base-red from #12731: the new `Mode pack ranking gates` candidates in `open-sse/services/autoCombo/__tests__/autoCombo.test.ts` omitted the required `provider`, `model` and `errorRate` fields of `ProviderCandidate`, failing `check:open-sse-typecheck` (and with it `Fast Quality Gates`) on every open PR. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` docs-sync base-red: EURouter (#13025) and GreenPT (#13024) took the live provider count to 358, leaving 7 STRICT drifts (`PROVIDER_REFERENCE.md`, 4 diagrams, 2 tier-flow images) plus the `AGENTS.md` / `llm.txt` / `package.json` count claims stale. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- **test(models):** Pin `CATALOG_BUILD_TIMEOUT_MS` in the #12058 canonical-catalog regression so a cold catalog build racing the 8s default bound no longer flakes the suite with a 500 ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Allow `@omniroute/opencode-plugin-v2/` in the npm pack policy — [#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870) shipped the v2 plugin beside its v1 sibling without widening the allowlist, so every packed file under it read as an unexpected artifact and failed `check:pack-policy` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Point the release-green validator's pack gate at the tree it is validating — the build-provenance guard defaulted to `origin/main`, which a release branch tip can never be an ancestor of mid-cycle, so `Package artifact` reported a false red on every run and the tarball boot-smoke cascaded off it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **docs(checks):** keep doc counts honest — headings, rankings, catalog, weights, quality gate and scoring diagram now covered ([#12507](https://github.com/diegosouzapw/OmniRoute/pull/12507)) — thanks @maxmad64bis +- **chore(lifecycle):** `check:model-lifecycle` now also diffs `DEFAULT_DEGRADATION_MAP` (the background-task redirect table) against the vendor lifecycle snapshot, refusing a retired id as source or target, with a table-driven unit test beside it. Three rows whose source the vendor had retired — `claude-sonnet-4-20250514`, `gemini-3-pro-preview` and `gpt-5.1-codex` (whose target `gpt-5.1-codex-mini` is retired too) — were dead code, since `checkLifecycle` answers 410 before the redirect runs; they are dropped (#12535 — thanks @pacocartones) +- **test(compression):** cover idle worker eviction at the resource level — the pool must call `terminate()` and must not retain the worker's `MessagePort`, complementing the `exit`-event assertion added with the fix ([#13371](https://github.com/diegosouzapw/OmniRoute/pull/13371)) +- **chore(quality):** raise the `imageGeneration.ts` file-size ceiling for the image-only-model guard that clears the #12945 base-red ([#13392](https://github.com/diegosouzapw/OmniRoute/pull/13392)) +- **chore(quality):** type the combo-test route's JSON response bodies instead of casting them to `any`, drop the now-empty suppression entry, and remove the `hasPerModelQuota` import `chatCore.ts` stopped using when the failure-classification helper was extracted ([#13379](https://github.com/diegosouzapw/OmniRoute/pull/13379)) +- **chore(electron):** the desktop bundle no longer ships the root-level files of every translated docs mirror (`README.md`, `llm.txt`, `CLAUDE.md`, `GEMINI.md`, `CONTRIBUTING.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`) — the packaged app only reads `docs/i18n//docs/**`, which stays. Saves ~11 MB on top of the translated CHANGELOGs already pruned. (#0000) ([#13235](https://github.com/diegosouzapw/OmniRoute/pull/13235)) +- **chore(quality):** retighten the `src/sse/handlers/chat.ts` unused-vars suppression count to the 8 violations that actually remain ([#13385](https://github.com/diegosouzapw/OmniRoute/pull/13385)) +- Bring the documented counts back in line with the code: 171 migrations (was 169) and 20 routing strategies (was 19), plus the regenerated `cli-tunnel` skill reference. ([#12970](https://github.com/diegosouzapw/OmniRoute/pull/12970)) +- Update the documented migration count to 172 after the call-logs provider-stats indexes landed. ([#13209](https://github.com/diegosouzapw/OmniRoute/pull/13209)) +- **chore(ci):** two gates that close the blind spots behind the exclusions above. `check:vitest-exclusions` requires every Vitest exclusion to name a tracking issue and to appear in `config/quality/vitest-exclusions.json` — the previous list grew to 62 files behind a comment pointing at an issue that had been closed for a month. `check-new-key-coverage` requires a key newly added to `en.json` to reach every locale; the existing coverage gate is a percentage floor per locale, so eleven absent keys out of ~13,000 left it at 99.9% while a whole feature shipped untranslated in nine languages. Both are diff-aware, so pre-existing debt stays frozen and neither needed a migration to turn on. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **build:** root layout no longer loads Inter from `next/font/google`, so a production image build does not need fonts.googleapis.com ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **docs(i18n):** bring the eight new `llm.txt` mirrors (km, kn, ml, my, ne, or, pa, si) up to the current provider and migration counts, so `check-docs-sync` stops rejecting every commit ([#13674](https://github.com/diegosouzapw/OmniRoute/pull/13674)) +- **refactor(ui):** flow surfaces (home topology, combo live studio, compression cockpit/waterfall/nodes, token-health badge) express state through the theme-aware `--orch-status-*` tokens instead of fixed dark-mode hex, so green/red/amber/grey stay legible in the light theme; dark mode is byte-identical. Categorical palettes (routing-strategy hues, compression-layer pills, provider brand colors) deliberately stay hex ([#12378](https://github.com/diegosouzapw/OmniRoute/issues/12378)) ([#13203](https://github.com/diegosouzapw/OmniRoute/pull/13203)) +- **chore(tests):** 51 test files that had been excluded from Vitest are running again, restoring roughly 350 assertions to the blocking suite. Each was measured individually first: of the 62 files parked behind the `// #8618 — pre-existing failure` comment, 51 pass against the current tree with no source change, so the exclusions had outlived the failures they were added for. The 11 that genuinely still fail stay excluded, but now point at a live tracker (#13204) rather than at #8618, which was closed in August while the list it tracked kept growing. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **chore(quality):** drop two ESLint suppression entries whose violations no longer exist, so `eslint --suppressions-location` stops rejecting every commit that touches the surrounding files ([#13384](https://github.com/diegosouzapw/OmniRoute/pull/13384)) +- **chore(release):** bring main's post-tag fixes into release/v3.8.51 — sync-back step 2/2 (direct commit `21c488f210`) +- **chore(deps):** patch toml and esbuild in the opencode plugin lockfiles ([#12965](https://github.com/diegosouzapw/OmniRoute/pull/12965)) +- **docs(changelog):** reconcile the v3.8.51 living section — fold 366 fragments, cover every cycle commit, credit every contributor ([#12971](https://github.com/diegosouzapw/OmniRoute/pull/12971)) +- **test(plugin):** wait for the v2 lazy-refresh tier instead of sleeping past it ([#12984](https://github.com/diegosouzapw/OmniRoute/pull/12984)) +- **chore(quality):** rebaseline chatCore.ts after the non-streaming regression fixes ([#13045](https://github.com/diegosouzapw/OmniRoute/pull/13045)) +- **docs(changelog):** add the missing bullet marker to the DeepSeek PoW fragment ([#13200](https://github.com/diegosouzapw/OmniRoute/pull/13200)) +- **chore(deps):** drain the Dependabot queue — 10 of 13 alerts ([#13213](https://github.com/diegosouzapw/OmniRoute/pull/13213)) +- **chore(ci):** make worktrees carry the hooks that enforce the identity gate ([#13236](https://github.com/diegosouzapw/OmniRoute/pull/13236)) +- **chore(deps):** pin csv-parse to ^7.0.2 via overrides (#13117) ([#13300](https://github.com/diegosouzapw/OmniRoute/pull/13300)) — thanks @KooshaPari +- **chore(deps):** bump hono from ^4.12.34 to ^4.13.7 (#13148) ([#13301](https://github.com/diegosouzapw/OmniRoute/pull/13301)) — thanks @KooshaPari +- **chore(deps):** pin joi to ^18.2.8 via overrides (#13085) ([#13302](https://github.com/diegosouzapw/OmniRoute/pull/13302)) — thanks @KooshaPari +- **docs(providers):** add Microsoft 365 Copilot (BizChat) provider guide (#12779) ([#13340](https://github.com/diegosouzapw/OmniRoute/pull/13340)) — thanks @KooshaPari +### 🙌 Contributors + +Thanks to everyone whose work landed in v3.8.51: + +| Contributor | PRs / Issues | +| --- | --- | +| [@5dive-bot](https://github.com/5dive-bot) | #11852 | +| [@abhisheksharma2411](https://github.com/abhisheksharma2411) | #11547, #11684, #11959, #12209 | +| [@adevwithpurpose](https://github.com/adevwithpurpose) | #11464 | +| [@adivekar-utexas](https://github.com/adivekar-utexas) | #12015, #12043, #12090, #12138 | +| [@AIB1TAL0S](https://github.com/AIB1TAL0S) | #12103 | +| [@alltomatos](https://github.com/alltomatos) | #11448 | +| [@alvinveroy](https://github.com/alvinveroy) | #11857, #12027, #12165 | +| [@amaleta](https://github.com/amaleta) | #11906, #11934 | +| [@amartinawi](https://github.com/amartinawi) | #11575 | +| [@AndrianBalanescu](https://github.com/AndrianBalanescu) | #11083, #11830, #11834, #11844 | +| [@anhtahaylove](https://github.com/anhtahaylove) | #13091, #13092, #13093, #13096, #13097, #13100, #13106, #13109, #13114, #13155, #13166, #13171, #13175, #13187, #13196 | +| [@aniruddhaadak80](https://github.com/aniruddhaadak80) | #11760 | +| [@app](https://github.com/app) | #12554 | +| [@arminanton](https://github.com/arminanton) | #11461, #11513 | +| [@AStupidBear](https://github.com/AStupidBear) | #11584 | +| [@atescivitci-cmd](https://github.com/atescivitci-cmd) | #12105 | +| [@b3nw](https://github.com/b3nw) | #11970, #11971, #12147 | +| [@backryun](https://github.com/backryun) | #11259, #11950, #12075, #12076, #12078, #12079, #12081, #12082, #12181, #12228, #12239, #12255, #12258, #12277, #12367, #12423, #12524, #12538 | +| [@benjaminkitt](https://github.com/benjaminkitt) | #11747 | +| [@benzntech](https://github.com/benzntech) | #11614, #11615 | +| [@Bl0ck154](https://github.com/Bl0ck154) | #11948, #11951, #11952, #11953, #11954 | +| [@brick30llc-ctrl](https://github.com/brick30llc-ctrl) | #11752, #12242, #12262 | +| [@Chewji9875](https://github.com/Chewji9875) | #11455, #11875, #12028 | +| [@cheynetom](https://github.com/cheynetom) | #12058 | +| [@davidebaraldo](https://github.com/davidebaraldo) | #12222, #12304 | +| [@davidlinfr](https://github.com/davidlinfr) | #6390 | +| [@ddarkr](https://github.com/ddarkr) | #12124 | +| [@Deftera186](https://github.com/Deftera186) | #11809 | +| [@dpozimski](https://github.com/dpozimski) | #11945 | +| [@DW-MediaLab](https://github.com/DW-MediaLab) | #12229 | +| [@echel0nn](https://github.com/echel0nn) | #11923 | +| [@f9td56dbgh-hub](https://github.com/f9td56dbgh-hub) | #11660 | +| [@fabioluissilva](https://github.com/fabioluissilva) | #11991 | +| [@foreveryh](https://github.com/foreveryh) | #12177 | +| [@geek007git](https://github.com/geek007git) | #12115, #12116, #12117, #12120, #12122 | +| [@geekyNads](https://github.com/geekyNads) | #11784, #12266 | +| [@ggiak](https://github.com/ggiak) | #11561, #12276, #12350, #12402 | +| [@gonisulaimann](https://github.com/gonisulaimann) | #12368, #12369, #12371 | +| [@Gorillaz322](https://github.com/Gorillaz322) | #12207 | +| [@groovecityJO](https://github.com/groovecityJO) | #12682 | +| [@hartmark](https://github.com/hartmark) | #11434, #11452, #11473, #11499, #11703, #11983, #11984, #11985, #11986, #11988, #11989, #11990, #11994, #12221, #12293, #12445, #12446, #12447, #12448, #12460, #12461, #12623, #12646, #12650, #12680, #12717, #12718, #12727, #12741, #12854 | +| [@hizzt](https://github.com/hizzt) | #11894 | +| [@hongnoul](https://github.com/hongnoul) | #11484 | +| [@HouMinXi](https://github.com/HouMinXi) | #11411, #11414, #11512, #11518, #11520, #11641, #11642, #11643, #11687, #11779, #11849, #11850, #11851, #11915, #11916, #11918, #11919, #11920, #12013, #12017, #12033, #12042, #12106, #12139, #12166, #12169, #12171, #12205, #12213, #12312, #12325, #12487, #12488, #12495, #12504, #12557, #12566, #12590, #12591, #12624, #12626, #12632, #12637, #12678, #12696, #12697, #12711, #12733, #12746, #12767, #12770, #12789, #12803, #12805, #12811, #12866, #12868, #12899, #12926, #12934, #12950, #12951, #12974, #13001, #13006, #13011, #13017, #13026, #13027, #13034, #13035, #13038, #13042, #13050, #13060, #13061, #13069, #13107, #13120, #13136, #13195, #13197 | +| [@Hsia97](https://github.com/Hsia97) | #11624 | +| [@jacobsparts](https://github.com/jacobsparts) | #11854, #12155, #12167 | +| [@joglomedia](https://github.com/joglomedia) | #11980 | +| [@jonlwheat2-gif](https://github.com/jonlwheat2-gif) | #11443, #11450, #11549, #11567, #11608, #11633, #11635, #11640, #11644, #11671, #11672, #11673, #11674, #11675, #11676, #11677, #11706, #11783, #12051, #12052, #12053, #12055 | +| [@kanade-hoshino](https://github.com/kanade-hoshino) | #12180 | +| [@Karan825](https://github.com/Karan825) | #11861 | +| [@KaspaPulse](https://github.com/KaspaPulse) | #11389, #11469, #11628, #11666, #11910 | +| [@keeltrace](https://github.com/keeltrace) | #12080, #12223 | +| [@killer30001000](https://github.com/killer30001000) | #12178 | +| [@KooshaPari](https://github.com/KooshaPari) | #12470, #12592, #12667, #12699, #12703, #12706, #12764, #12769, #12771, #13300, #13301, #13302, #13340, #13401, #13403, #13404, #13405, #13406, #13407, #13409, #13410, #13411, #13412, #13413, #13414, #13418, #13424, #13427, #13433, #13523, #13524, #13525, #13528, #13531, #13532, #13534, #13539, #13543, #13545, #13546, #13547, #13550, #13553 | +| [@kriptoburak](https://github.com/kriptoburak) | #11370 | +| [@KrzysiekSko](https://github.com/KrzysiekSko) | #12425, #12432, #12673 | +| [@marcelokarval](https://github.com/marcelokarval) | direct commit / report | +| [@maxmad64bis](https://github.com/maxmad64bis) | #1622, #11435, #11437, #11441, #11537, #11550, #11553, #11555, #11557, #11812, #11842, #11843, #11903, #12151, #12214, #12215, #12218, #12226, #12314, #12316, #12317, #12318, #12319, #12320, #12321, #12507, #12715, #12731, #12744, #12786, #12787, #12788, #12790, #12792, #12794, #12795, #12828, #12832, #12853, #12857, #12870, #12937, #12941, #12975, #13141, #13142, #13146, #13147 | +| [@mdigitalbh81](https://github.com/mdigitalbh81) | #12240 | +| [@Meet6338-X](https://github.com/Meet6338-X) | #11598, #11609, #12110 | +| [@morpheus9393](https://github.com/morpheus9393) | #11943 | +| [@MumuTW](https://github.com/MumuTW) | #11492, #11502, #11506, #11507, #11626, #11675, #11685, #11728, #11746, #11888, #11889, #11890, #11892 | +| [@Neuron-Mr-White](https://github.com/Neuron-Mr-White) | #11622, #11801, #12256 | +| [@NightStalker-87](https://github.com/NightStalker-87) | #12183 | +| [@NoxzRCW](https://github.com/NoxzRCW) | #11879, #11880, #11881, #11882, #11883, #11935 | +| [@ntdat812](https://github.com/ntdat812) | #11585, #12095 | +| [@ntdatt812](https://github.com/ntdatt812) | #11368, #11573, #11574, #11576, #11577, #11580, #11582, #11583, #11588, #11589, #11590, #11591, #11592, #11593, #11672, #12177, #12180, #12873, #12918, #12920, #12921, #12925, #12930, #13007, #13009, #13024, #13025, #13083, #13087, #13101, #13104, #13110 | +| [@official-burak](https://github.com/official-burak) | #11542 | +| [@opensource-elearning](https://github.com/opensource-elearning) | #12179, #12189, #12278, #12286 | +| [@oyi77](https://github.com/oyi77) | #11408, #11409, #11421, #11505, #11677, #12036, #12110 | +| [@pacocartones](https://github.com/pacocartones) | #11521, #11522, #11527, #11528, #11529, #11530, #11531, #11532, #11533, #11534, #11595, #11599, #11603, #11604, #11605, #11607, #11610, #11676, #11714, #11716, #11718, #11767, #11838, #11860, #11862, #11869, #11871, #11872, #11873, #11903, #11906, #11921, #11934, #12356, #12358, #12359, #12360, #12361, #12362, #12364, #12365, #12373, #12374, #12375, #12376, #12377, #12379, #12380, #12381, #12386, #12387, #12389, #12390, #12394, #12395, #12397, #12401, #12403, #12404, #12522, #12523, #12535, #12536, #12540, #12541, #12543, #12545, #12548, #12549, #12550, #12551, #12552, #12647, #12651, #12653, #13055 | +| [@patrykkopycinski](https://github.com/patrykkopycinski) | #11936, #11937, #12224 | +| [@PauloFH](https://github.com/PauloFH) | #11509 | +| [@PauloHSOliveira](https://github.com/PauloHSOliveira) | #12241 | +| [@ponkcore](https://github.com/ponkcore) | #12054 | +| [@Prajeeth-12](https://github.com/Prajeeth-12) | #11634, #12046 | +| [@quiterunner-commits](https://github.com/quiterunner-commits) | #12131, #12143 | +| [@rafacpti23](https://github.com/rafacpti23) | #11554, #11558, #12192, #12197, #12198, #12202, #12203, #12204 | +| [@ragnar-claude](https://github.com/ragnar-claude) | #11564 | +| [@raheemuddin786](https://github.com/raheemuddin786) | #11491, #11839, #11840, #11841, #12003, #12230, #12231, #12232, #12233, #12234 | +| [@rahilmavani](https://github.com/rahilmavani) | #11761 | +| [@Rahulsharma0810](https://github.com/Rahulsharma0810) | #11771 | +| [@RaviTharuma](https://github.com/RaviTharuma) | #11710, #11727, #11797, #11798, #11802, #11805, #11806, #11811, #12098, #12099, #12101, #12449, #12452, #12472, #12473, #12493, #12533, #12607, #12628, #12631, #12636, #12875, #12876, #12880, #12882, #12884 | +| [@rezjalibd](https://github.com/rezjalibd) | #12186 | +| [@RhianB14](https://github.com/RhianB14) | #11692 | +| [@rifqiawl](https://github.com/rifqiawl) | #11517, #11519, #11969 | +| [@rqzbeh](https://github.com/rqzbeh) | #11390 | +| [@Sabeekhann](https://github.com/Sabeekhann) | #11699, #11705 | +| [@santosraju99-hub](https://github.com/santosraju99-hub) | #11755, #11770 | +| [@Seramicx](https://github.com/Seramicx) | #11568 | +| [@Siva010](https://github.com/Siva010) | #12191 | +| [@solstxce](https://github.com/solstxce) | #11597 | +| [@soroush5](https://github.com/soroush5) | #12691 | +| [@tenshiak](https://github.com/tenshiak) | #12279 | +| [@TheDemonTuan](https://github.com/TheDemonTuan) | #11468, #11470, #11471, #11482, #11548, #11758, #11775, #11814 | +| [@thomasmaerz](https://github.com/thomasmaerz) | #12834 | +| [@tuandinh0801](https://github.com/tuandinh0801) | #11454 | +| [@turbolego](https://github.com/turbolego) | #11621, #11762, #11772, #11774, #11781, #12216 | +| [@Tushar49](https://github.com/Tushar49) | #11392 | +| [@ujjawalkaushik1110](https://github.com/ujjawalkaushik1110) | #11863 | +| [@vermasomesh835](https://github.com/vermasomesh835) | #11794 | +| [@vsd2807](https://github.com/vsd2807) | #11565, #11619 | +| [@wahidsadik371-coder](https://github.com/wahidsadik371-coder) | #12126 | +| [@watchingdogs](https://github.com/watchingdogs) | #12031 | +| [@wildcard](https://github.com/wildcard) | #11620 | +| [@xiaoyaner0201](https://github.com/xiaoyaner0201) | #11460, #11662, #12083 | +| [@Xxx91n](https://github.com/Xxx91n) | #11690 | +| [@yourspraveen](https://github.com/yourspraveen) | #11146 | +| [@yxyxy](https://github.com/yxyxy) | #11671 | +| [@ZaimMarzuki](https://github.com/ZaimMarzuki) | #11960 | +| [@Zartharas](https://github.com/Zartharas) | #11340 | +| [@zero-executioner](https://github.com/zero-executioner) | #11631 | +| [@diegosouzapw](https://github.com/diegosouzapw) | maintainer | + +--- ## [3.8.50] — 2026-08-25 _Living section — regenerated 2026-08-12 from all cycle commits (cycle open `ed2db6cb19` → tip). Bullets carry the merged PR and its author; direct pushes listed separately._ diff --git a/docs/i18n/zh-TW/CHANGELOG.md b/docs/i18n/zh-TW/CHANGELOG.md index e61403eadc..47421a2633 100644 --- a/docs/i18n/zh-TW/CHANGELOG.md +++ b/docs/i18n/zh-TW/CHANGELOG.md @@ -8,16 +8,1119 @@ ## [3.8.51] — TBD -_Living section — cycle opened at the v3.8.50 freeze (parallel-cycle model). Bullets are aggregated from `changelog.d/` fragments at each `/generate-release` phase._ +_Living section — reconciled 2026-09-15 from all cycle commits (`091589089c` → `c0f92ec98a`, 916 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 | **123** | +| 📝 Commits in the cycle | **916** | +| 🔀 Pull requests referenced | **892** | +| 📋 Changelog entries | **919** | +| 🙌 Contributors credited in entries | **121** | +| 🤖 Automated dependency commits | 18 | + +**Entries by type** + +| Type | Count | +| --- | ---: | +| 🐛 Fixes | 592 | +| ✨ Features | 131 | +| 🧹 Chore | 93 | +| 📚 Docs | 42 | +| 🧪 Tests | 32 | +| ♻️ Refactor | 8 | +| 🏗️ Build | 7 | +| ⚡ Performance | 7 | +| ⚙️ CI | 2 | +| ⏪ Reverts | 2 | +| 📦 Dependencies | 2 | +| 🔒 Security | 1 | + +### 🏆 Top 25 contributors this cycle + +_By commits in `091589089c..c0f92ec98a`, author identities consolidated via `.mailmap` and the merged PR's GitHub login. Bots excluded._ + +| # | Contributor | Commits | +| ---: | --- | ---: | +| 🥇 | diegosouzapw | 323 | +| 🥈 | Bob.Hou (@HouMinXi) | 79 | +| 🥉 | Paco Cartones (@pacocartones) | 77 | +| 4 | Dizzle (@maxmad64bis) | 49 | +| 5 | Koosha Paridehpour (@KooshaPari) | 43 | +| 6 | Nguyen Thanh Dat (@ntdatt812) | 31 | +| 7 | Ravi Tharuma (@RaviTharuma) | 26 | +| 8 | Markus Hartung (@hartmark) | 22 | +| 9 | Webman (@jonlwheat2-gif) | 22 | +| 10 | backryun | 17 | +| 11 | anhtahaylove | 15 | +| 12 | Rafa Martins (@rafacpti23) | 10 | +| 13 | Syed Raheemuddin (@raheemuddin786) | 10 | +| 14 | Nguyễn Viết Tuấn (@TheDemonTuan) | 8 | +| 15 | MumuTW | 7 | +| 16 | Paijo (@oyi77) | 7 | +| 17 | Tobias Andersen (@turbolego) | 6 | +| 18 | Bl0ck (@Bl0ck154) | 5 | +| 19 | SHANMUGAPRIYAN (@geek007git) | 5 | +| 20 | KaspaPulse | 5 | +| 21 | Abhishek Sharma (@abhisheksharma2411) | 4 | +| 22 | Abhishek Divekar (@adivekar-utexas) | 4 | +| 23 | Andrew B. (@AndrianBalanescu) | 4 | +| 24 | NoxzRCW | 4 | +| 25 | opensource-elearning | 4 | ### ✨ New Features +- **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). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- Added Google AI Studio Gemini batch text-to-speech support through `POST /v1/audio/speech`. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- 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. ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(routing):** subscription-first auto groupings — `auto/subscription` routes only through plan-included connections with a documented hard-stop overage and fails closed on exhaustion, while `auto/thrifty` orders the pool `subscription → keyless → free → cheap → premium` and steps up one rung at a time as each is exhausted. Billing class comes from a curated per-connection catalog (uncurated is treated as metered, never plan-included), both reuse STRICT_ZERO_COST's per-connection verification, and a quota reading whose `resetAt` has passed is now refreshed regardless of TTL so routing returns to plan capacity as soon as it resets ([#11146](https://github.com/diegosouzapw/OmniRoute/pull/11146)) — thanks @yourspraveen +- **feat(providers):** publish a management-authenticated versioned web-session credential contract from OmniRoute's canonical browser credential metadata ([#11340](https://github.com/diegosouzapw/OmniRoute/pull/11340)) — thanks @Zartharas +- **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)) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add Xquik X search with typed results, credential validation, REST routing, and MCP selection ([#11370](https://github.com/diegosouzapw/OmniRoute/pull/11370)) — thanks @kriptoburak +- **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)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(dashboard):** surface durable exclusive managed leases in the existing Sessions view, keeping leased clients visible across idle gaps while marking connections with in-flight work as active ([#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389)) — thanks @KaspaPulse +- **build(bun):** allow Turbopack bundler flag on Bun 1.4+ with configurable Webpack fallback ([#11471](https://github.com/diegosouzapw/OmniRoute/pull/11471)) — thanks @TheDemonTuan +- feat(api): add an opt-in `modelVisibilityAllowlist`/`modelVisibilityDenylist` settings pair to curate exactly which models `/v1/models` advertises, mirrored into every `auto/*` combo candidate pool so a denied model cannot be routed to via combo selection either (#11481) ([#11997](https://github.com/diegosouzapw/OmniRoute/pull/11997)) +- **feat(rankings):** the Free Provider Rankings page shows what each provider actually served over the last 24 h. It ranked by ELO alone, which left a provider that answers every call with an error in first place; the usage data was already served by the API but never requested. A provider with too small a sample shows a dash, not a number ([#11546](https://github.com/diegosouzapw/OmniRoute/pull/11546)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **feat(guardrails):** enforce a bounded, deterministic contract for Video Bridge transcripts — 256 cues, 4096 input code units and 4 KiB UTF-8 per cue, 64 KiB total text, malformed-Unicode rejection, focus-window scoping, cross-source reconciliation with contributing-source metadata, and a structural provenance trust boundary so caller JSON can never self-assert `embedded`/`audio-bridge` provenance ([#11652](https://github.com/diegosouzapw/OmniRoute/issues/11652)) ([#12009](https://github.com/diegosouzapw/OmniRoute/pull/12009)) +- **feat(video):** orchestrate optional Video Bridge audio extraction and Audio Bridge STT behind a dual opt-in (operator setting AND per-request signal) — a new loopback-only broker `mode=audio` operation shares the frame path's exact process queue, deadline, AbortSignal, and byte budgets to extract a bounded mono 16 kHz PCM WAV from the same already-downloaded video, then reuses the existing Audio Bridge transcription boundary; provider segment timing is preserved when available and marked coarse otherwise, and every failure degrades to a visual-only-safe partial instead of throwing (#11654). ([#12012](https://github.com/diegosouzapw/OmniRoute/pull/12012)) +- Add a tenant-bound Video Bridge drill-down lifecycle on top of the existing secure cache substrate: opaque hashed handles (never raw session/video identifiers), preview/standard/detail multiresolution variants resampled on read, response pagination capped at 8 frames and 32 MiB, and a new authenticated `/api/v1/video-bridge/drilldown` consumer route that stays disabled for remote access by default and denies cross-key access with the same response as a nonexistent handle (no existence oracle). ([#12006](https://github.com/diegosouzapw/OmniRoute/pull/12006)) +- **test(video):** Add the Video Bridge FU-07/FU-09 promotion-evidence harness (#11656) — a frozen Zod manifest schema covering the 8 required scenario kinds (static scenes, rapid cuts, late facts, fades, blur, small text, close events, visual prompt injection) with a minimum of 3 repetitions per case, deterministic declarative fixture recipes (`videoBridgePromotionFixtures.ts`), a pure medians/p95 metrics aggregator, a pure FU-07/FU-09 promotion-verdict evaluator applying the ticket's exact thresholds (missing token usage always holds), a digest-only persistence layer that never retains raw media or raw model responses, and a versioned per-model promotion allowlist shipped empty with every model defaulting to `hold`. The FU-07/FU-09 promotion verdicts themselves remain HOLD — they require a real evidence run against real models on VPS 192.168.0.15. ([#12008](https://github.com/diegosouzapw/OmniRoute/pull/12008)) +- **feat(video bridge):** "embedded" transcript provenance can now be legitimately earned instead of merely asserted — a bounded, allowlisted (`mov_text`/`subrip`/`webvtt`) subtitle probe runs through the loopback-only Video Bridge broker (at most 2 streams, 10s subdeadline bounded by the request deadline, 256 KiB output, 4096-code-unit lines), normalized through a bounded, ReDoS-safe WebVTT parser and Zod-validated end to end. The adapter always resolves to an explicit `success`/`absent`/`transient_failure` outcome — a subtitle failure never breaks the visual description path, and only a fingerprint-verified broker response (never a caller-declared label) can produce embedded cues (#11659). ([#12011](https://github.com/diegosouzapw/OmniRoute/pull/12011)) +- Default new Antigravity-family connections (agy CLI imports and Antigravity OAuth connects) to model auto-sync, so live model discovery lands in the synced catalog and `/v1/models` picks up freshly released upstream models (e.g. Gemini 3.7 Flash tiers) without code changes. Existing connections keep their current setting; the per-connection dashboard toggle remains the opt-out. (#11685 — thanks @MumuTW) ([#11887](https://github.com/diegosouzapw/OmniRoute/pull/11887)) +- **feat(ui):** enable React Compiler (`reactCompiler: true` + `babel-plugin-react-compiler`) for automatic memoization at build time ([#11783](https://github.com/diegosouzapw/OmniRoute/pull/11783)) — thanks @jonlwheat2-gif +- **feat(providers):** add SeekAi (`seekai.cc`) as an OpenAI-compatible New-API gateway — catalog id `seekai` (alias `ska`), `https://seekai.cc/v1`, live `/v1/models` via `passthroughModels`, aggregator-list membership so New-API balance detection can opt in. No referral/aff codes. ([#11786](https://github.com/diegosouzapw/OmniRoute/issues/11786)) ([#12557](https://github.com/diegosouzapw/OmniRoute/pull/12557)) — thanks @HouMinXi +- **feat(zai):** add GLM-5.3-Flash Coding Plan support (1M context, 128K output, vision, `low|high|max` reasoning) and route `zai` GLM-5.3-family API-key traffic through the OpenAI-compatible Coding Plan endpoint with native thinking defaults ([#11801](https://github.com/diegosouzapw/OmniRoute/pull/11801)) — thanks @Neuron-Mr-White +- **feat(combo):** choose how combo models are ordered — manual, provider, score, or name — via a sort control in the dashboard builder, persisted in `config.modelSort` and re-applied on load and after add ([#11812](https://github.com/diegosouzapw/OmniRoute/pull/11812)) — thanks @maxmad64bis +- **feat(free):** custom models can be marked free-tier via `customModels[].isFree`; `isFreeModel()` is the first door and `hidePaidModels` respects it even for providers outside the free budget ([#11843](https://github.com/diegosouzapw/OmniRoute/pull/11843)) — thanks @maxmad64bis +- **feat(nodejs):** add `5dive` as a `configure` target — `omniroute configure 5dive` / `omniroute setup-5dive` write a 5dive auth profile that points an agent fleet's `claude` seats at OmniRoute, with the root-only write, the loopback-vs-`https` endpoint rule and the per-seat model pin handled explicitly ([#11852](https://github.com/diegosouzapw/OmniRoute/pull/11852)) — thanks @5dive-bot +- **feat(sse):** treat `max` as a first-class reasoning-effort tier and clamp per model family (GLM 5.1+/DeepSeek V4+/Kimi K3+ keep native `max`; o1/MiniMax/Grok/Muse Spark clamp to their upstream ceiling) ([#11875](https://github.com/diegosouzapw/OmniRoute/pull/11875)) — thanks @Chewji9875 +- **feat(providers):** the provider plugin manifest now advertises a `usage-fetch` capability for the 40 providers that have a wired usage/quota fetcher, so external dashboards can read it from `GET /api/v1/provider-plugin-manifest` instead of parsing `open-sse/services/usage.ts` after every release. Discovery only — no new fetcher, no quota change, and the Dashboard quota widget stays gated by `USAGE_SUPPORTED_PROVIDERS`. `USAGE_FETCHER_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/fetcherProviders.ts`) and is re-exported from `services/usage.ts`, keeping the manifest module a light leaf instead of pulling the ~490-module usage dispatcher into the manifest route. ([#11903](https://github.com/diegosouzapw/OmniRoute/pull/11903)) — thanks @maxmad64bis / @pacocartones +- **feat(plugins):** `OMNIROUTE_PLUGINS_DIR` sets the directory the runtime plugin scanner reads — and the root the plugin manager installs into — overriding the `HOME`-derived default, so a Docker/K8s deployment can point straight at its bind-mounted plugin tree instead of moving `HOME` just to relocate the scan path. An image that exports no home no longer scans `/tmp/.omniroute/plugins` in silence: the resolved directory is logged once at startup as `scanner.dir_resolved`, naming the input that won. Unset, behaviour is unchanged. Distinct from the CLI-only `OMNIROUTE_PLUGIN_PATH`, which finds `omniroute-cmd-*` command packages and never reached this scanner ([#11906](https://github.com/diegosouzapw/OmniRoute/pull/11906)) — thanks @amaleta / @pacocartones +- **feat(leases):** add an explicit owner-authenticated status action that returns only the active lease's privacy-safe configured connection and provider labels, with generation fencing and no credential or internal-id disclosure ([#11910](https://github.com/diegosouzapw/OmniRoute/pull/11910)) — thanks @KaspaPulse +- **feat(providers):** import-from-file modal shows per-row API errors and ships a downloadable CSV template ([#12071](https://github.com/diegosouzapw/OmniRoute/issues/12071)) ([#12504](https://github.com/diegosouzapw/OmniRoute/pull/12504)) — thanks @HouMinXi +- **feat(providers):** dashboard search matches connection name and `baseUrl` so imported OpenAI-compat nodes surface on the provider card ([#12108](https://github.com/diegosouzapw/OmniRoute/issues/12108)) ([#12495](https://github.com/diegosouzapw/OmniRoute/pull/12495)) — thanks @HouMinXi +- **feat(routing):** add a `score` Auto router strategy that selects the highest configured weighted score and reuses `explorationRate`. ([#12155](https://github.com/diegosouzapw/OmniRoute/pull/12155)) — thanks @jacobsparts +- Add a runtime feature flag to disable universal context handoffs globally without changing the default behavior. ([#12167](https://github.com/diegosouzapw/OmniRoute/pull/12167)) — thanks @jacobsparts +- **feat(providers):** the provider plugin manifest now also advertises a `usage-supported` capability for the 46 providers whose usage API is accepted by the server and Dashboard routes, so integrators can distinguish "the server will serve quota for this provider" from "a fetcher is wired" without reading TypeScript. Discovery only — no fetcher or quota change. `usage-fetch` resolves on id or alias (the usage dispatcher accepts both); `usage-supported` resolves on id alone, matching the runtime guard `USAGE_SUPPORTED_PROVIDERS.includes(providerId)`. `USAGE_SUPPORTED_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/supportedProviders.ts`) and is re-exported from `providers.ts`, mirroring the `fetcherProviders` leaf from #11903 and keeping the manifest a light module. ([#12214](https://github.com/diegosouzapw/OmniRoute/pull/12214)) — thanks @maxmad64bis +- **feat(rankings):** order Free Provider Rankings by what each provider actually served — `GET /api/free-provider-rankings?sortBy=reliability` and a "Most reliable first" toggle on the page. Providers with too few calls to state a success rate keep their score order below the measured ones; the default order is unchanged ([#12218](https://github.com/diegosouzapw/OmniRoute/pull/12218)). — thanks @maxmad64bis +- **perf(sse):** defer `cloneLogPayload()` in the structured SSE collector until after the `maxEvents`/`maxBytes` cap check, eliminating ~9,800 wasted `structuredClone` calls per streaming response (65–71% faster `push()`). Reducer snapshot isolation restored for OpenAI and Responses summaries ([#12241](https://github.com/diegosouzapw/OmniRoute/pull/12241)) — thanks @PauloHSOliveira ([#12243](https://github.com/diegosouzapw/OmniRoute/pull/12243)) +- **feat(usage):** Devin CLI agentic quota (Codeium seat-management GetUserStatus) and OpenRouter key limits plus account credits now surface in Provider Limits ([#12256](https://github.com/diegosouzapw/OmniRoute/pull/12256) — thanks @Neuron-Mr-White) +- **feat(settings):** persist `headroomUrl` through Settings so status/start use the operator URL instead of only `HEADROOM_URL` ([#12306](https://github.com/diegosouzapw/OmniRoute/issues/12306)) ([#12487](https://github.com/diegosouzapw/OmniRoute/pull/12487)) — thanks @HouMinXi +- **feat(auto-combo):** Auto-Combo scoring can now weigh how often a provider/model has actually succeeded. The engine already carried that number on every candidate — 24 hours of usage history behind a ten-sample floor, real-time metrics otherwise — and the scoring function never read it, while the weight table described `stability` as if it did. `reliability` (`1 - failureRate`, with the same field precedence and the same rate-bounding the speed ranking already uses, so a corrupt reading means "nothing observed" rather than "fails every call") is now a declared factor shipping at weight `0`, so routing is unchanged until an operator gives it one, and the `stability` description now matches what that factor computes ([#12317](https://github.com/diegosouzapw/OmniRoute/pull/12317)) — thanks @maxmad64bis +- **feat(routing):** With `freeAccessPolicy: "strict"`, the read-only candidate listing (`GET /v1/auto-combo/{channel}/candidates`) no longer hides the candidates the zero-cost guard excludes — the same read-only transparency the resilience filter already honours (#9133). Each candidate now carries `freeAccessExclusion` saying why it would be kept out, and it tells an exhausted allowance apart from a quota reading that never arrived or went stale, which used to look identical from the outside. Routing is unchanged: the listing reports, it never enforces. The separate `excludeTosAvoid` guard still drops its candidates without a reason; that gap is now documented rather than closed ([#12319](https://github.com/diegosouzapw/OmniRoute/pull/12319)) — thanks @maxmad64bis +- **feat(radar):** The Radar catalog table now shows two facts it was already receiving from the feed and dropping on the floor: the per-model rate limits (requests and tokens, per minute and per day) in a new column, and a badge when a provider's terms state it may train on the prompts you send. A limit of zero renders as zero rather than "rate-only" — for a ceiling those are opposite facts — and a model with no training statement gets no badge, because an absent statement is not a guarantee ([#12320](https://github.com/diegosouzapw/OmniRoute/pull/12320)) — thanks @maxmad64bis +- **feat(radar):** explain Community, single-use, contributor, supporter, recovery, abuse, offers, and privacy rules before either Radar activation action, and remove the superseded fixed-PR grant promise from every UI locale ([#12342](https://github.com/diegosouzapw/OmniRoute/pull/12342)) +- **feat(gamification):** the dashboard Profile page now shows the real daily streak — `/api/gamification/level` returns `streak: { current, longest }` (per key with `apiKeyId`, operator-wide maximum otherwise) and the streak card reads it instead of a hard-coded 0 (#2403) ([#12377](https://github.com/diegosouzapw/OmniRoute/pull/12377)) — thanks @pacocartones +- **feat(gamification):** the dashboard leaderboard now shows each API key's display name under the Name column instead of a truncated key id; `GET /api/gamification/leaderboard` attaches `name` per entry (name only — no key material), while the shared ranking helper and the federation leaderboard stay id-only — thanks @pacocartones ([#12385](https://github.com/diegosouzapw/OmniRoute/pull/12385)) +- **feat(gamification):** enforce the documented 1000 XP/min per-API-key anti-cheat rate limit on the XP award path; over-limit awards are logged and skipped instead of persisted, and the sliding window now matches the timestamp format stored in `xp_audit_log` ([#2403](https://github.com/diegosouzapw/OmniRoute/issues/2403)) ([#12390](https://github.com/diegosouzapw/OmniRoute/pull/12390)) — thanks @pacocartones +- **feat(admin):** localize the gamification anomalies page — the loading state, the Status column and the Suspicious badge now come from the `common` catalog (new `common.suspicious` key propagated to every locale) — add it to the Gamification sidebar group as `gamification-admin` (`/dashboard/gamification/admin`), and expose the loading and empty states as polite `role="status"` live regions (#12401 — thanks @pacocartones) +- **feat(providers):** skip GitHub combo members missing from the live synced catalog, and drop Copilot models that are policy-disabled or hidden from the model picker ([#12473](https://github.com/diegosouzapw/OmniRoute/pull/12473)) — thanks @RaviTharuma +- **feat(i18n):** locale-expansion tooling — `npm run i18n:add-locale` adds a language to config, dashboard, docs mirrors, CLI, README/indexes and site in one command; browser, CLI and cookie detection resolve `uk`, `fil`/`tl`, `zh-Hant` (and the retired legacy `in` → `id`) via config aliases; `config/i18n.json` now ships in the npm package so the published CLI can read it; new real-translation ratio gate (`npm run i18n:check-ratio`, advisory) with a per-locale ratchet baseline; `run-translation --adopt` restores `.i18n-state.json`; `sync-language-bars` generates 🌐 bars from config; `validate_translation.py` loads its allowlist again. Retires the duplicate `in` locale (Indonesian mislabelled as Hindi) — 42 honest locales; saved `NEXT_LOCALE=in` / `OMNIROUTE_LANG=in` keep working. (#12496) +- **feat(api):** Emit gateway-measured `tokens_per_second` (TTFT excluded) on streaming usage and `X-OmniRoute-Tokens-Per-Second` when first-token latency is known ([#12616](https://github.com/diegosouzapw/OmniRoute/issues/12616)) ([#12631](https://github.com/diegosouzapw/OmniRoute/pull/12631)) — thanks @RaviTharuma +- **feat(opencode-plugin): map gateway cost/usage/tok/s onto OpenCode inference payloads** — the official plugin copies `X-OmniRoute-Response-Cost`, token counts, `X-OmniRoute-Tokens-Per-Second` / `usage.tokens_per_second`, TTFT, and the winning `X-OmniRoute-Model` onto the JSON/SSE body OpenCode already consumes. Missing tok/s is left unset (never `tokens / latency`). (#12636) — thanks @RaviTharuma +- **feat(combo):** add combo strategy `quota-weighted`: skip exhausted accounts, then weighted-draw by leftover / in-flight load; existing conversations stay pinned until the account is empty ([#12789](https://github.com/diegosouzapw/OmniRoute/pull/12789)) — thanks @HouMinXi +- **feat(models):** Account live listings become the chat catalog source for Claude, Codex, Copilot, and AGY; public metadata only fills prices on IDs those accounts already list. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **fix(models):** Union `agy` and `antigravity` live catalogs so `agy/gemini-3.8-flash-high` is not rejected after the prefix folds to `antigravity`. ([#12866](https://github.com/diegosouzapw/OmniRoute/pull/12866)) — thanks @HouMinXi +- **feat(opencode):** Add `@omniroute/opencode-plugin-v2`, an opencode v2 plugin that gives a v2 install a route to an OmniRoute gateway: it publishes models, combos and auto-combos into the host catalog, refreshes them lazily behind a 300s TTL, and keeps serving the last known catalog from an on-disk snapshot when the gateway is unreachable. The v1 plugin (`@omniroute/opencode-plugin`) is untouched and keeps working as before. The plugin adapts to the host instead of assuming one shape: it reads the shape the host seeds into the catalog draft and publishes the `api` block, the pre-`api` top-level fields, or both. The gateway key comes from the host credential store when one is connected, so no secret needs to sit in a config file; a source the gateway refuses is named in a warning instead of silently degrading the catalog to raw model ids; and tool schemas bound for a Gemini model are stripped of the JSON-Schema keywords Gemini rejects, so tool calling survives that route ([#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870)) — thanks @maxmad64bis +- feat(services): show sanitized CLIProxyAPI account health from its authenticated management API without exposing credentials, file paths, or raw account metadata (#6342) ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **feat(search):** Add AnySearch free web search + URL extract (webFetch) with typed results, credential validation, REST routing, and MCP selection - fallback-only ([#11690](https://github.com/diegosouzapw/OmniRoute/pull/11690)) — thanks @Xxx91n +- **feat(dashboard):** display clamped `[0, 100]%` cached input token ratio in request logs table ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) ([#11970](https://github.com/diegosouzapw/OmniRoute/pull/11970)) — thanks @b3nw +- **feat(catalog):** add `OMNIROUTE_DISABLE_THINKING_LEVEL_VARIANTS` feature flag to optionally filter out thinking level variants from model catalog ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) ([#11971](https://github.com/diegosouzapw/OmniRoute/pull/11971)) — thanks @b3nw +- **feat(dashboard):** continuously export call logs to external analytics stores. A pluggable destination registry ships the full Logs-tab record set on an hourly `JobRegistry` cron, with a persisted per-destination cursor, batched inserts, a config UI rendered from each destination's own field descriptors, and a REST layer (`/api/log-export/*`) for CRUD, a connection test, and an on-demand run. A destination can opt into `includeBodies` to also ship the request and response payloads shown in the Logs detail pane, including the client and provider views of each call; this is off by default, and payloads inherit the dashboard's PII sanitisation, secret redaction and `noLog` handling. Google BigQuery is the first destination, using a service-account key stored encrypted at rest and streaming inserts keyed by call-log id, into a table that is day-partitioned on `timestamp` and clustered on `api_key_name`, `provider`, `model` and `status`. ([#11945](https://github.com/diegosouzapw/OmniRoute/pull/11945)) — thanks @dpozimski +- **feat(providers):** add MaxAI as a signed, OpenAI-compatible provider serving its 13 paid chat models (GPT-5.6 / Luna / Thinking, Claude 5 Sonnet, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite, Grok 4.1-fast / 4.5, DeepSeek V3.2 / R1, Llama 3.3 70B) through OmniRoute's `/v1` endpoint, with per-request HMAC-SHA1→SM3→AES request signing, live model + context-window discovery from `/models/get_config`, and prompted tool-calling translated to OpenAI `tool_calls` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI vision input — image_url content parts are forwarded inline in `message_content` to the 6 vision-capable models (GPT-5.6 / Luna / Thinking, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite) ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI image generation — 6 image models (gpt-image-1, dall-e-3, flux-1-schnell/dev/pro, sd3-medium) exposed through `POST /v1/images/generations` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** MaxAI document RAG — inline base64 file/document attachments are uploaded to MaxAI (content-addressed `doc_id`) and attached to the chat via `doc_list` ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** browserless MaxAI onboarding — email device-pair login (`/api/providers/[id]/login`) and signed access-token refresh, so a connection can be created and kept fresh without a real browser or Google OAuth ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** per-provider TLS impersonation profile (MaxAI presents a Windows Firefox-150 client fingerprint) so its bot-sensitive endpoints accept OmniRoute traffic ([#11461](https://github.com/diegosouzapw/OmniRoute/pull/11461)) — thanks @arminanton +- **feat(providers):** add **Nimble** as a web-search and web-fetch provider (`nimble-search`) — `/v1/search` routes to Nimble's search API at `lite` depth (locale, freshness and include/exclude domain filters mapped onto the shared request contract), and `/v1/web/fetch` routes to Nimble Extract, which covers all four fetch formats (`markdown`, `html`, `links`, `screenshot`) from a single call. One API key serves both surfaces. ([#11620](https://github.com/diegosouzapw/OmniRoute/pull/11620)) — thanks @wildcard +- The npm publish is automatic again, through npm Trusted Publishing (OIDC): the hosted `stage-npm` job publishes with a short-lived credential minted from GitHub's id-token — no `NPM_TOKEN`, no 2FA prompt, provenance attached. `publish_mode=staged` (owner approves with 2FA) and `direct` (token) remain available on `workflow_dispatch`. ([#11931](https://github.com/diegosouzapw/OmniRoute/pull/11931)) +- **feat(providers):** Add **Opper** as an API-key gateway provider — EU-hosted AI gateway with 700+ models from 30+ providers behind one OpenAI-compatible API and one key (`OPPER_API_KEY`); model ids use `provider/model` format (e.g. `anthropic/claude-sonnet-4-6`, `openai/gpt-5`); live model catalog at `https://api.opper.ai/v3/compat/models`; entry mirrors `requesty` (same shape, `passthroughModels: true`, no static seed) ([#11629](https://github.com/diegosouzapw/OmniRoute/pull/11629)) +- **feat(dashboard):** the `/dashboard/orchestration` snapshot hook now subscribes to the `agents` WebSocket channel (`agent.task.updated`) instead of `requests` as its refetch trigger, and relaxes its background poll from 5s to 30s while that WS connection is up — falling back to the tighter 5s cadence, reprogrammed live on any connect/disconnect transition, whenever the socket is down. ([#12409](https://github.com/diegosouzapw/OmniRoute/pull/12409)) +- **feat(dashboard):** Orchestration canvas quick wins — search box plus state/source/provider filter chips with a one-click clear, and per-source collapse/expand, all reflected in the URL so a filtered/collapsed view is shareable and survives a refresh; the detail drawer gained a "copy trace JSON" action and hardened error/empty-state and accessibility handling; the Agents-tab edges now animate traveling particles along active (running) connections; and the canvas node/edge status colors moved off fixed hex values onto theme-aware `--orch-status-*` CSS custom properties, so they adapt correctly to light/dark mode. ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- **chore(dashboard):** Orchestration UI hardening pass and the missing component/model test coverage it called for — `OrchestratorNode`/`ActivityNode`/`OverflowNode` rendering, the `?node=`/overflow-click page routing, the Agents-tab orchestrator-click no-op and `showCompleted` toggle, and the overview kanban's done-column sort order (#12270, #12271). ([#12393](https://github.com/diegosouzapw/OmniRoute/pull/12393)) +- New `/dashboard/orchestration` page: live unified view of everything running — Cloud Agent, A2A and Conductor as a real-time graph (Agents tab), the combo cascade (Routing tab, reusing the Combo Live Studio) and a state kanban (Overview tab), with a detail drawer (trace, cost, approve/cancel). Read-only over existing APIs — no new backend. Canvas concept credit: PR #11815 design ([#12261](https://github.com/diegosouzapw/OmniRoute/pull/12261)) +- **feat(dashboard):** new "History" tab on `/dashboard/orchestration` — an Airflow-style grid of finished runs over a 24h/7d/30d preset window, one row per (source, identity), clicking a cell opens the existing detail drawer. It is backed by real persistence: A2A task lifecycle transitions are now written to the `a2a_tasks` table (purged after 30 days, configurable via `OMNIROUTE_A2A_HISTORY_RETENTION_DAYS`) and served by the new `GET /api/a2a/tasks/history` listing endpoint, with the task-detail route falling back to persisted history once a run leaves the in-memory snapshot. Conductor runs stay remote and are not persisted locally — the tab says so instead of silently omitting them. ([#12479](https://github.com/diegosouzapw/OmniRoute/pull/12479)) +- **feat(dashboard):** the orchestration detail drawer gained a "Repeat" action for Cloud Agent, A2A and Conductor tasks — a two-click confirm (click once to arm, click again within the confirm window to fire) re-submits the original prompt/input as a new run. The button is disabled with an explanatory tooltip whenever the original input can't be recovered from the loaded task detail (e.g. it never carried a prompt, or the detail failed to load). Two limitations of the A2A variant, by design: it targets the `/a2a` JSON-RPC endpoint, which authenticates with an API key only (`REQUIRE_API_KEY=true` or a configured `OMNIROUTE_API_KEY` makes a dashboard-session repeat answer `HTTP 400` — surfaced verbatim in the drawer's error line, never as a success), and the `message/send` call is SYNCHRONOUS: the POST blocks for the whole skill run, so the success confirmation only appears once the repeated task finishes. A dashboard-authenticated A2A creation path is deliberately left to a follow-up — widening the endpoint's auth posture is an operator decision, not a side effect of this feature. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(a2a):** A2A task execution now records which memories were consulted for the task's last user message as `metadata.memoryHits` (id/key/type/content-snippet) plus a `memory_hits` history event, purely for observability — the retrieved memory is never injected into a skill's prompt or behavior. Gated by the `OMNIROUTE_A2A_MEMORY_HITS` kill-switch (default enabled; set to `0` to skip the recall lookup entirely). The drawer's new "Memory used" section lists these hits for a2a tasks and is omitted whenever there are none. Known limitation: recall only resolves under the keyless posture — a keyed caller's task owner is a SHA-256 prefix of the API key, while memory rows are keyed by the database api-key id, and no hash→id lookup exists today, so the hit list stays empty for keyed callers. The recorded hits are also kept out of the task's own `input` (and therefore out of the persisted input and of the "Repeat" request body), so repeating a task never re-sends the previous run's memory snippets. ([#12508](https://github.com/diegosouzapw/OmniRoute/pull/12508)) +- **feat(providers):** add a Perplexity Agent API provider (`perplexity-agent` / `pplx-agent`) for Perplexity `/v1/responses`, including the documented Anthropic, OpenAI, Google, xAI, DeepSeek, Z.AI, Moonshot/Kimi, NVIDIA, and Perplexity model IDs plus Anthropic-model `max_output_tokens` compatibility. ([#12103](https://github.com/diegosouzapw/OmniRoute/pull/12103)) — thanks @AIB1TAL0S +- **feat(providers):** add RPD (Requests Per Day) limit to provider rate limit overrides across UI, schemas, DB, and i18n ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) ([#12147](https://github.com/diegosouzapw/OmniRoute/pull/12147)) — thanks @b3nw +- **feat(providers): add UC Direct (uncensored.com Developer API), the metered OpenAI-compatible surface.** A standard OpenAI-compatible passthrough (default executor) for uncensored.com's official REST API at `https://api.uncensored.com/api/v1`: `X-api-key` auth (never-expiring `uai_sk_live_` key), `POST /chat/completions` with streaming SSE and native tool-calling, and the full live metered catalog (82 models across 15 providers, discovered from the public `GET /v1/models`). Registered as provider `uc-direct` (alias `ucd`). Complements the un-metered `uc` persona provider — same models, metered credits and a plain API key instead of a subscription session. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(providers): add UC (uncensored.com), the un-metered subscription "persona" chat as an OpenAI-compatible provider.** A WebSocket web-app port: a durable Clerk credential mints a short-lived session token per connect (browserless — no API key), driving UC's persona socket. Ships the browserless email-code login (request → verify → harvest), the 19 verified persona models (Claude Opus, Gemini, Grok, GLM, Kimi, DeepSeek, MiniMax, incl. the uncensored variants), prompted `` tool-calling with a per-model code-style dialect + auto-cure retry for guardrailed models, live ``/reasoning split, streaming + non-streaming OpenAI responses, and full quota/auth error surfacing (paywall / message-limit / rate-limit → 429, invalid session → 401 re-login). Full multimodal parity via the persona blob-upload layer: **vision** (image input, 15 vision-capable models), **document RAG** (PDF/doc upload, server-side extraction), **image generation** (22 models), **video generation** (14 models, async signed-url → poll), and **TTS** (streaming MP3). Registered as provider `uc` (alias `ucn`). The metered OpenAI-compatible Developer API is a separate `uc-direct` provider. ([#11513](https://github.com/diegosouzapw/OmniRoute/pull/11513)) — thanks @arminanton +- **feat(logs):** add copy-all button to request detail modal ([#11083](https://github.com/diegosouzapw/OmniRoute/pull/11083)) — thanks @AndrianBalanescu +- **feat(check):** make serviceKinds required and add the reverse-walk provider consistency gate ([#11392](https://github.com/diegosouzapw/OmniRoute/pull/11392)) — thanks @Tushar49 +- **feat(providers):** only advertise verified-working connections by default ([#11448](https://github.com/diegosouzapw/OmniRoute/pull/11448)) — thanks @alltomatos +- **feat(resilience):** add hierarchical concurrency admission ([#11493](https://github.com/diegosouzapw/OmniRoute/pull/11493)) +- **feat(providers):** derive + surface expiry for JWT-bearing web cookies (#11497) ([#11505](https://github.com/diegosouzapw/OmniRoute/pull/11505)) — thanks @oyi77 +- **feat(tunnels):** support named cloudflared tunnels via CLOUDFLARED_CONFIG ([#11561](https://github.com/diegosouzapw/OmniRoute/pull/11561)) — thanks @ggiak +- **feat(compression):** make proactive context-compression threshold a live setting ([#11564](https://github.com/diegosouzapw/OmniRoute/pull/11564)) — thanks @ragnar-claude +- **feat(resilience):** add provider peak-hour protection ([#11622](https://github.com/diegosouzapw/OmniRoute/pull/11622)) — thanks @Neuron-Mr-White +- **feat(providers):** add 1min.ai provider ([#11631](https://github.com/diegosouzapw/OmniRoute/pull/11631)) — thanks @zero-executioner +- **feat(dashboard):** collapsible JSON tree viewer for request/response payloads ([#11703](https://github.com/diegosouzapw/OmniRoute/pull/11703)) — thanks @hartmark +- **feat(routing):** add LiquidAI LFM2.5-2.6B free tier via OpenRouter ([#11752](https://github.com/diegosouzapw/OmniRoute/pull/11752)) — thanks @brick30llc-ctrl +- **feat(pricing):** add GLM-5.3-Flash pricing, model specs, and catalog registration ([#11830](https://github.com/diegosouzapw/OmniRoute/pull/11830)) — thanks @AndrianBalanescu +- **feat(routing):** add relayMode for schema-locked context handoffs ([#11839](https://github.com/diegosouzapw/OmniRoute/pull/11839)) — thanks @raheemuddin786 +- **feat(config):** add support for runtime system prompt configuration and hot-reloading ([#11841](https://github.com/diegosouzapw/OmniRoute/pull/11841)) — thanks @raheemuddin786 +- **feat(routing):** expand connection-aware quota prefilter across combo strategies (#11682) ([#11850](https://github.com/diegosouzapw/OmniRoute/pull/11850)) — thanks @HouMinXi +- **feat(models):** reactive discovery sync after Antigravity model-not-found 404 ([#11888](https://github.com/diegosouzapw/OmniRoute/pull/11888)) — thanks @MumuTW +- **feat(models):** auto-aliases and token limits from synced Antigravity discovery ([#11889](https://github.com/diegosouzapw/OmniRoute/pull/11889)) — thanks @MumuTW +- **feat(settings):** raise sticky round-robin limit caps to 1000 ([#12015](https://github.com/diegosouzapw/OmniRoute/pull/12015)) — thanks @adivekar-utexas +- **feat(resilience):** operator-configurable global credential health check interval ([#12043](https://github.com/diegosouzapw/OmniRoute/pull/12043)) — thanks @adivekar-utexas +- **feat(quota):** use official OpenCode Go usage API ([#12124](https://github.com/diegosouzapw/OmniRoute/pull/12124)) — thanks @ddarkr +- **feat(resilience):** default the credential health check sweep to 60 minutes ([#12138](https://github.com/diegosouzapw/OmniRoute/pull/12138)) — thanks @adivekar-utexas +- **feat(quality):** new-code mode for the complexity and dead-code ratchets (Clean as You Code) ([#12142](https://github.com/diegosouzapw/OmniRoute/pull/12142)) +- **feat(dashboard):** orchestration canvas — unified model + snapshot hook (part 1/2) ([#12156](https://github.com/diegosouzapw/OmniRoute/pull/12156)) +- **feat(usage):** add Kilo Code balance and Kilo Pass quotas ([#12178](https://github.com/diegosouzapw/OmniRoute/pull/12178)) — thanks @killer30001000 +- **feat(providers):** refresh vendored ChatGPT Web connector to v4.0.7 ([#12181](https://github.com/diegosouzapw/OmniRoute/pull/12181)) — thanks @backryun +- **feat(providers):** manual "Clear cooldown" action in the cooling panel ([#12224](https://github.com/diegosouzapw/OmniRoute/pull/12224)) — thanks @patrykkopycinski +- **feat(providers):** restore ChatGPT Web via clean-room browser transport ([#12239](https://github.com/diegosouzapw/OmniRoute/pull/12239)) — thanks @backryun +- **feat(sse):** wire the PROVIDER_PROFILES window gate into the global provider cooldown ([#12247](https://github.com/diegosouzapw/OmniRoute/pull/12247)) +- **feat(providers):** modernize CLOVA Studio chat and embeddings ([#12277](https://github.com/diegosouzapw/OmniRoute/pull/12277)) — thanks @backryun +- **feat(browser):** adopt Obscura as primary headless browser engine with Chromium fallback ([#12286](https://github.com/diegosouzapw/OmniRoute/pull/12286)) — thanks @opensource-elearning +- **feat(sse):** full language parity for output styles (es/de/fr/it/ru/zh + autoDetect) ([#12289](https://github.com/diegosouzapw/OmniRoute/pull/12289)) +- **feat(quality):** complete test:scoped — --full map rebuild, stdin selection, CI loader parity (#8084 D1) ([#12353](https://github.com/diegosouzapw/OmniRoute/pull/12353)) +- **feat(providers):** refresh Fable, Cursor, and Devin catalogs ([#12367](https://github.com/diegosouzapw/OmniRoute/pull/12367)) — thanks @backryun +- **feat(video):** redact transcript text from logs and durable memory (#12150 P1) ([#12427](https://github.com/diegosouzapw/OmniRoute/pull/12427)) +- **feat(dashboard):** parent-link, genuine-continuation badge, and modal perf fixes ([#12448](https://github.com/diegosouzapw/OmniRoute/pull/12448)) — thanks @hartmark +- **feat(dashboard):** adaptive context-budget dial on compression panel ([#12488](https://github.com/diegosouzapw/OmniRoute/pull/12488)) — thanks @HouMinXi +- **feat(providers):** refresh Z.ai Web models and browser transport ([#12524](https://github.com/diegosouzapw/OmniRoute/pull/12524)) — thanks @backryun +- **feat(video):** redact raw client-snapshot transcript fields in the detailed log (#12150 P2) ([#12528](https://github.com/diegosouzapw/OmniRoute/pull/12528)) +- **feat(providers):** refresh NVIDIA hosted models ([#12538](https://github.com/diegosouzapw/OmniRoute/pull/12538)) — thanks @backryun +- **feat(quota):** Moonshot Open Platform balance and TPD lock for custom nodes ([#12590](https://github.com/diegosouzapw/OmniRoute/pull/12590)) — thanks @HouMinXi +- **feat(video):** redact transcript in the in-memory pending-request snapshot (#12430 item 6) ([#12596](https://github.com/diegosouzapw/OmniRoute/pull/12596)) +- **feat(dashboard):** link a log entry's Conversation Context to its owning conversation ([#12646](https://github.com/diegosouzapw/OmniRoute/pull/12646)) — thanks @hartmark +- **feat(catalog):** eligibility-gated free-tier bucket ([#12669](https://github.com/diegosouzapw/OmniRoute/pull/12669)) +- **feat(db):** fail-closed previous_response_id continuation for redacted video turns (#12150 P2b) ([#12707](https://github.com/diegosouzapw/OmniRoute/pull/12707)) +- **feat(gamification): pay the documented `streak_bonus` and `badge_unlock` XP rewards.** `XP_REWARDS` listed both rewards but the award pipeline never paid them: the private reward table in `events.ts` omitted them, `updateStreak()` did not report when a streak extended, and badge unlocks carried no XP. Every request that extends a daily streak now pays `streak_bonus × streak length` once per UTC day (guarded by a same-day `xp_audit_log` check), and every badge unlocked through the pipeline pays `badge_unlock` once per badge (guarded by the `user_badges` primary key; `unlockBadge()` now reports whether it inserted). Bonus XP flows through the same `addXp` + level sync + global/weekly/monthly leaderboard path as action XP, so level-ups and rankings include it. The Radar supporter recognition unlock stays XP-free. (#12522 — thanks @pacocartones) +- **feat(providers):** advertise a `free-tier` capability in the provider plugin manifest for every provider with documented free models, so sidecars and dashboards can filter free-capable providers without reading the quota catalog ([#12786](https://github.com/diegosouzapw/OmniRoute/pull/12786)) — thanks @maxmad64bis +- **feat(providers):** Added EURouter as an OpenAI-compatible API-key gateway (`https://api.eurouter.ai/v1`), with live model discovery via `passthroughModels`. Its copy states that models are served by third-party upstreams listed per model, so an EU-based router is not read as EU data residency for inference. ([#13025](https://github.com/diegosouzapw/OmniRoute/pull/13025)) — thanks @ntdatt812 +- **feat(providers):** Added GreenPT as an OpenAI-compatible API-key provider (`https://api.greenpt.ai/v1`), with live model discovery via `passthroughModels`. No free-inference badge: the published docs describe a free API subscription billed per token, not a free tier. ([#13024](https://github.com/diegosouzapw/OmniRoute/pull/13024)) — thanks @ntdatt812 +- **feat(i18n):** 9 new locales — Greek (`el`), Croatian (`hr`), Serbian (`sr`), Lithuanian (`lt`), Estonian (`et`), Latvian (`lv`), Slovenian (`sl`), Maltese (`mt`), Irish (`ga`) — across the dashboard, docs mirrors, CLI, README and the site; OmniRoute now ships every official EU language (51 locales). (#13044) +- **feat(i18n):** 8 new locales — Kannada (`kn`), Malayalam (`ml`), Odia (`or`), Punjabi (`pa`), Nepali (`ne`), Sinhala (`si`), Burmese (`my`), Khmer (`km`) — across the dashboard, docs mirrors, CLI, README and the site (59 locales). The translator now restores the ICU literal escape around angle placeholders and splits oversized docs sections before translating. (#0000) ([#13660](https://github.com/diegosouzapw/OmniRoute/pull/13660)) +- feat(providers): **list Agnes 3.0 Flash as the current free chat model, drop retired 1.5 Flash, add Image 2.0/2.5 Flash plus Video 2.5/2.5 Flash, and discover the live `/v1/models` catalog (including the CN host `api.agnes-ai.cn`).** `agnes-1.5-flash` now forwards to `agnes-3.0-flash`. Video 2.5 polls `GET /v1/videos/{id}` (not the V2.0 `/agnesapi` contract). Live `/v1/models` (2026-09-09) no longer serves 1.5; the wiki marks it deprecated. 3.0 Flash is 512K context / 65,536 max output, same window as 2.5. CN-region keys use the existing per-connection base-URL field, default stays `apihub.agnes-ai.com`. ([#13120](https://github.com/diegosouzapw/OmniRoute/pull/13120)) — thanks @HouMinXi +- **feat(sse):** Codex and OpenAI catalogs list GPT-6 Astra with effort aliases (`-low` through `-ultra`); Codex CLI identity pins `@openai/codex@0.153.4` in lockstep with the image ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **feat(dashboard):** the orchestration History tab gained a "Compare runs" mode — toggling it turns each grid cell into a 2-item selection queue (a 3rd click drops the oldest pick), and picking two cells opens a side-by-side comparison panel instead of the usual detail drawer. The panel fetches both runs' detail the same way the drawer does (falling back to persisted history once a run leaves the live TTL window) and shows, per side: identity/source/state, start time, a signed `right - left` delta for duration/cost/event count, the event timeline aligned by index, and any memory hits. One side's fetch failing never blocks the other, and a delta is only ever computed when both sides have a finite value — otherwise it renders "—", never `NaN`. Comparing two runs from different sources or skills still works; a banner marks the deltas as informational rather than hiding them, since the two runs aren't a strict apples-to-apples pair. ([#12677](https://github.com/diegosouzapw/OmniRoute/pull/12677)) +- **feat(dashboard):** badge a conversation that never reached a clean stop ([#12717](https://github.com/diegosouzapw/OmniRoute/pull/12717)) — thanks @hartmark +- **feat(release):** reconcile-changelog tool + version-anchored fragment aggregation ([#12987](https://github.com/diegosouzapw/OmniRoute/pull/12987)) +- **feat(mcp):** carry context_length through MCP list_models_catalog projection (#12776) ([#13406](https://github.com/diegosouzapw/OmniRoute/pull/13406)) — thanks @KooshaPari +- **feat(cli):** make startup readiness budget configurable (#13369) ([#13433](https://github.com/diegosouzapw/OmniRoute/pull/13433)) — thanks @KooshaPari + ### 🐛 Bug Fixes +- **security(streaming):** sanitize generic mid-stream error messages before emitting OpenAI, + Responses, or Claude SSE failure frames and before diagnostic logging, while preserving raw + failures for internal classification and keeping client disconnects out of provider failure state. ([#12457](https://github.com/diegosouzapw/OmniRoute/pull/12457)) +- **fix(claude):** `blockExtraUsage: false` no longer lets 5h quota preflight skip the account; extra usage is billed after the session bar is gone, so the request must reach Anthropic ([#12803](https://github.com/diegosouzapw/OmniRoute/pull/12803)) — thanks @HouMinXi +- Sanitize HuggingChat conversation-creation and message-send transport failures before they reach client error bodies or provider logs. ([#12467](https://github.com/diegosouzapw/OmniRoute/pull/12467)) +- **fix(dashboard):** Keep local and theme-aware provider SVG icons at a definite layout size so Chromium does not collapse them to 0×0 after the v3.8.50 image-rendering change ([#12054](https://github.com/diegosouzapw/OmniRoute/pull/12054)) — thanks @ponkcore +- **fix(security):** Sanitize provider and runtime failures before public API, SSE and MCP responses and before persistent request, proxy and usage logs, preventing credentials, stack traces and host filesystem paths from crossing those boundaries while preserving stable error codes and useful diagnostics. ([#12506](https://github.com/diegosouzapw/OmniRoute/pull/12506)) +- **fix(cli):** `omniroute tunnel create` no longer crashes with `Cannot read properties of undefined (reading optsWithGlobals)` — removed the duplicate positional argument that caused Commander.js to misalign the action callback parameters ([#12295](https://github.com/diegosouzapw/OmniRoute/issues/12295)) ([#12368](https://github.com/diegosouzapw/OmniRoute/pull/12368)) — thanks @gonisulaimann +- Stop advertising Gemini Live-only models as supported audio endpoints until OmniRoute proxies the bidirectional Live protocol. ([#11710](https://github.com/diegosouzapw/OmniRoute/pull/11710)) — thanks @RaviTharuma +- **fix(kie):** reroute `flux/kontext` off the KIE Market `createTask` flow — it is catalogued with `isMarket: true` but has no Market catalog page, so KIE rejected it with "model name not supported"; it now hits the dedicated `POST /api/v1/flux/kontext/generate` / `GET /api/v1/flux/kontext/record-info` endpoints instead (#11296). ([#11985](https://github.com/diegosouzapw/OmniRoute/pull/11985)) — thanks @hartmark +- **fix(kie):** correct 12 more KIE Market catalog ids that were sent to `createTask` unchanged but diverge from KIE's documented upstream `model` values — GPT Image 2 T2I/I2I (drops the `gpt/` prefix), GPT Image 1.5 T2I/I2I (`gpt-image/` namespace), Seedream 5.0 Lite T2I/I2I (drops the `.0`), all 4 Flux 2 variants (`flux-2/` namespace, generic variant renamed `flex`), and Wan 2.7 Image / Image Pro (dash instead of dot) — each verified individually against the literal example request published on docs.kie.ai. `#11326`'s "everything else already matches" claim was wrong a second time (#11296); `z-image/4.0-*`/`z-image/4.5-*` and `flux/kontext` remain open, documented as unresolved in `KIE_MARKET_UPSTREAM_MODEL_IDS`'s comment pending further verification. ([#11667](https://github.com/diegosouzapw/OmniRoute/pull/11667)) +- **fix(dashboard):** `useApiKeySave.handleSaveApiKey` no longer forces a full upstream `/models` catalog sync on every non-curated provider connection save — callers can now pass `skipModelSync: true` to opt out, so a workflow that only wants to add one manual model no longer floods the provider's available-models list with hundreds/thousands of synced entries. The flag is a client-side intent signal only and is stripped before the connection payload is POSTed to `/api/providers`; default behavior (full sync on save) is unchanged when the flag is omitted (#11324) ([#11664](https://github.com/diegosouzapw/OmniRoute/pull/11664)) +- **Provider connections:** keep `tokenExpiresAt` when a connection is created. The create-path allowlist omitted it, so every insert stored NULL and the dashboard token badge could read a fresh connection as expired until its first background refresh ([#11368](https://github.com/diegosouzapw/OmniRoute/pull/11368)). — thanks @ntdatt812 +- **fix(combos):** the combo builder's precision-select, global-model-search, and manual-entry flows now serialize a model step's `model` string using the provider's already-computed routing-alias prefix (e.g. `oc/`) instead of rebuilding it from the raw canonical `providerId`, fixing the no-auth "OpenCode Free" provider (`opencode`) being routed to the unrelated paid "OpenCode Zen" provider (`opencode-zen`) because `opencode` doubles as a manual routing-prefix override ([#11433](https://github.com/diegosouzapw/OmniRoute/issues/11433)). ([#11670](https://github.com/diegosouzapw/OmniRoute/pull/11670)) +- **fix(radar):** the catalog feed cache now keeps `generatedAt`, the date the feed's data was built, next to `fetchedAt`, the date this install downloaded it (#11435). The feed schema requires that date and the sync path validates it, but the cache dropped it — so a feed fetched minutes ago and one carrying weeks-old figures looked identical to everything downstream, including the dashboard's "Last fetched" line. `getRadarCatalog().meta` and `GET /api/radar/status` now report both dates, the latter as its own field rather than folded into `version` — and omitted entirely for the offers and intel caches, which keep no build date, where a `null` would read as "unknown" rather than "never stored". The dashboard still shows only the fetch time; surfacing the build date there needs a new translated label and is left to a follow-up. Rows cached before migration 163 read back as `null`: unknown stays unknown instead of borrowing the fetch time. The referrals cache has persisted the same date since migration 142. — thanks @maxmad64bis +- **fix(cli):** postinstall no longer fills `JWT_SECRET` and `API_KEY_SECRET` in the installed package's `.env` (#11436). `.env.example` ships both blank on purpose: the server restores them from its durable store, or generates and persists them there, in `ensureSecrets()`. Pre-filling them defeated that — the file lives inside the package directory, so `npm i -g` replaced it and postinstall wrote _different_ values, while `ensureSecrets()` (which only acts on an empty variable) never got to restore the real ones. Both secrets rotated silently on every update, invalidating dashboard sessions and API-key CRCs. `STORAGE_ENCRYPTION_KEY` left the same list for the same reason in #1622; its comment pointed at a function that no longer exists and now names the real provisioning path. — thanks @maxmad64bis +- **fix(cli):** the CLI now says when a loaded `.env` lives inside the installed package directory (#11437). It already announces every env file it reads, without distinguishing the ones that survive an update from the one that does not: `npm i -g` replaces the package directory wholesale, so values set there are gone at the next update, silently. The warning names the durable path to move them to, and fires only when that file actually supplied a value — a file entirely shadowed by a durable one supplied nothing. A development checkout stays silent: there the same path is stable and documented in `SETUP_GUIDE.md`. — thanks @maxmad64bis +- **fix(free-models):** the shared `isFreeModel()` predicate no longer reports catalog entries marked `freeType: "discontinued"` as free, so `hidePaidModels` can't route to Pollinations' seven premium models that now require a paid key ([#11441](https://github.com/diegosouzapw/OmniRoute/pull/11441)) — thanks @maxmad64bis +- fix(ui): let AnySearch use the normal provider-icon fallback when LobeHub has no matching icon (#11449) +- Fixed the v3.8.50 Costs and Analytics dashboards so flat-rate Claude Code usage can be shown as an explicitly requested token-price estimate without changing default billed-cost semantics. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed archived usage retention so each request is priced individually instead of pricing a day's summed tokens once, which understated archived cost whenever a day mixed cache-heavy and ordinary requests. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- Fixed the Costs dashboard so it discloses when displayed figures include flat-rate token-price estimates instead of labelling them as billed spend, using the flag the analytics API already returns; the month-end projection and the CSV/JSON exports carry the same marker, and billed-cost mode is unchanged. ([#11460](https://github.com/diegosouzapw/OmniRoute/pull/11460)) — thanks @xiaoyaner0201 +- **fix(combo):** attach the same combo-diagnostics trace (`poolSize`/`attemptOrder`/`excluded`/`terminalReason`, plus `x-omniroute-combo-*` headers) to the round-robin strategy's and the nested pipeline/fusion runtime-unit loop's "Maximum combo retry limit reached" 503 that the priority-strategy path already attaches for the identical terminal condition — previously those two paths returned a bare, contextless 503 ([#11462](https://github.com/diegosouzapw/OmniRoute/issues/11462)). ([#11665](https://github.com/diegosouzapw/OmniRoute/pull/11665)) +- **fix(bun):** use native `bun:sqlite` in `bootstrap-env` and `sync-env` to avoid loading `better-sqlite3` N-API addon during Bun startup ([#11468](https://github.com/diegosouzapw/OmniRoute/pull/11468)) — thanks @TheDemonTuan +- **fix(docker-bun):** make `Dockerfile.bun` install reliable with pre-install script helpers and native `bun:sqlite` smoke check ([#11470](https://github.com/diegosouzapw/OmniRoute/pull/11470)) — thanks @TheDemonTuan +- **fix(docker-bun):** remove every vendored `better-sqlite3` native addon from the Bun runtime image so startup cannot abort before the `bun:sqlite` fallback ([#11482](https://github.com/diegosouzapw/OmniRoute/pull/11482)) — thanks @TheDemonTuan +- **fix(autoCombo):** effort/alias model variants (`gpt-5.6-sol-xhigh`, `gpt-5.6`, cursor's `claude-4.6-opus-high`) inherit their base model's task fitness instead of falling to the wildcard 0.5 ([#11492](https://github.com/diegosouzapw/OmniRoute/pull/11492)) — thanks @MumuTW +- fix(db): rate-limit repeated Arena ELO leaderboard fetch-failure warnings instead of logging one per sync attempt (#11500) ([#11989](https://github.com/diegosouzapw/OmniRoute/pull/11989)) — thanks @hartmark +- **fix(db):** dedupe the raw `[Encryption] Decryption failed...` log line emitted by the lazy-decrypt views (`createLazyRowProxy`/`createLazyConnectionView`), which power `getProviderConnections()` and were re-triggering that line on every CredentialHealth/model-sync cycle for the same corrupt or stale-key credential — a fresh Proxy over a fresh row on every cycle meant the per-proxy memoization never suppressed it, unlike the dedup `decryptConnectionFields()` already had since [#9927](https://github.com/diegosouzapw/OmniRoute/issues/9927). Now shares that dedupe tracking so the line logs at most once per credential ([#11500](https://github.com/diegosouzapw/OmniRoute/issues/11500)). ([#11678](https://github.com/diegosouzapw/OmniRoute/pull/11678)) +- **fix(intelligence):** Arena ELO sync stores a synthesized base-model row for every effort/harness variant (`claude-opus-5-max` → `claude-opus-5`, `gpt-5.6-sol-xhigh (codex-harness)` → `gpt-5.6-sol`), so bare model requests reach the synced intelligence layer instead of the static fallback table; the hand-maintained `MODEL_ALIAS_MAP` — which collapsed generations (`gpt-5.5` → `gpt-5`) — is gone ([#11506](https://github.com/diegosouzapw/OmniRoute/pull/11506)) — thanks @MumuTW +- **fix(autoCombo,sse):** catalog hygiene — the static task-fitness table no longer ranks retired models above live ones (dead rows dropped, patterns matched on segment boundaries), the 7 deprecation aliases that forwarded to retired or non-existent models now point at the vendors' published replacements, an alias is no longer rewritten when the serving provider offers the id as-is, and a new `check:model-lifecycle` gate diffs both tables against a vendor lifecycle snapshot ([#11507](https://github.com/diegosouzapw/OmniRoute/pull/11507)) — thanks @MumuTW +- **fix(sse):** stop dropping resolved `thoughtSignature` values on parallel (multi tool-call) turns sent to Gemini 3.x — the claude→gemini and openai→gemini translators previously kept the signature only on the *first* function call of a message, causing Gemini to reject subsequent calls in the same turn with HTTP 400 "Function call is missing a thought_signature"; each function call now keeps its own resolved signature ([#11510](https://github.com/diegosouzapw/OmniRoute/issues/11510)). ([#11669](https://github.com/diegosouzapw/OmniRoute/pull/11669)) +- Treat Docker publishing from a non-default release branch as an expected skip instead of a failed workflow, while preserving the guard that prevents it from overwriting the `next` channel. ([#11527](https://github.com/diegosouzapw/OmniRoute/pull/11527)) — thanks @pacocartones +- fix(search): prefer credentialed providers over duckduckgo-free fallback (#11524) — the fallback-only loop ran before the credentialed-providers loop in `executeWebSearch`, making configured providers unreachable when `duckduckgo-free` was available. ([#11565](https://github.com/diegosouzapw/OmniRoute/pull/11565)) — thanks @vsd2807 +- **fix(sse):** cap the upstream headers-wait phase for STREAMING requests to a client-realistic ceiling (110s, under Codex's own ~120s hard client-abort window) instead of the flat 10-minute `FETCH_TIMEOUT_MS` default — that default was 5x longer than the body-phase readiness watchdog's own adaptive bound, so a request whose upstream never returned any response at all (not even headers, e.g. a stalled NVIDIA target behind a tool-heavy Responses→Chat translation) kept the client connection alive on keepalives only, guaranteeing the client's own patience ran out first with an opaque 499 instead of OmniRoute detecting and failing the stall fast. Non-streaming requests are unaffected — they keep the existing flat default (`open-sse/utils/fetchStartTimeoutPolicy.ts`) (#11526) ([#11668](https://github.com/diegosouzapw/OmniRoute/pull/11668)) +- **fix(ci):** protocol-clients E2E harness boots the peer-stamped custom server (`run-next.mjs dev`) instead of the bare `next` CLI, so `LOCAL_ONLY` locality resolves from the real TCP peer stamp and `/api/mcp/audit` answers 200 (open bootstrap preserved via an `OMNIROUTE_E2E_BOOTSTRAP_MODE=open` credential clear in `run-next.mjs`, pinned to loopback `HOST`) instead of a deterministic 403 (#11535) ([#11549](https://github.com/diegosouzapw/OmniRoute/pull/11549)) — thanks @jonlwheat2-gif +- **fix(docs):** the Auto-Combo engine is described with the number of factors it actually declares. Four different counts were in circulation — 6, 9, 13 and 14 across `AGENTS.md`, four guides and six code comments — while `DEFAULT_WEIGHTS` declares 15. `check:docs-counts` now reads that number from the source, so adding a factor without updating the prose fails CI ([#11545](https://github.com/diegosouzapw/OmniRoute/pull/11545)) ([#11553](https://github.com/diegosouzapw/OmniRoute/pull/11553)) — thanks @maxmad64bis +- **fix(quota-share):** in-flight leases are per request, so an aborted request's slot ages out instead of being kept alive by later traffic on the same connection ([#11547](https://github.com/diegosouzapw/OmniRoute/pull/11547)) — thanks @abhisheksharma2411 +- The free-tier summary route now serves the refreshed Radar catalog when the feed is active — the same numbers the dashboard shows — and states which catalog answered plus its real build date, instead of always reporting release-frozen figures with a stale curation date. The supporter-key live feed stays reserved to authenticated callers of the instance. ([#11550](https://github.com/diegosouzapw/OmniRoute/pull/11550)) — thanks @maxmad64bis +- **sse:** fix LiveWS/embed-WS servers crashing at startup under the Node/tsx runtime — `liveServer.ts`, `embedWsProxy.ts` and `apiBridgeServer.ts` imported `@/shared/utils/httpClientAbortGuard` without the `.mjs` extension, so the client-abort crash guard added by [#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556) was unreachable and every dependent test failed with `ERR_MODULE_NOT_FOUND` ([#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556)). ([#11671](https://github.com/diegosouzapw/OmniRoute/pull/11671)) — thanks @jonlwheat2-gif / @yxyxy +- fix(chat): shed any of the matched provider-node's routing identifiers (public prefix or internal id) before model lookup, so composites like `//` no longer reach upstream verbatim (#11557) — thanks @maxmad64bis +- **fix(cache):** a semantic-cache entry now expires on its TTL instead of surviving until the next UTC midnight, and `dbEntries` stops counting expired rows ([#11573](https://github.com/diegosouzapw/OmniRoute/pull/11573)) — thanks @ntdatt812 +- **fix(api):** `/v1/models` schedules its stale-while-revalidate rebuild through Next's `after()` again, so a stale catalog reaches the client before the rebuild blocks the event loop ([#11574](https://github.com/diegosouzapw/OmniRoute/pull/11574)) — thanks @ntdatt812 +- **fix(check):** the `known-symbols` executor conformance gate awaits the now-async `getExecutor()`, so it stops reporting all 142 registered aliases as dead symbols and can detect a lazy import that fails ([#11582](https://github.com/diegosouzapw/OmniRoute/pull/11582)) — thanks @ntdatt812 +- **fix(pack):** register `bin/cli/utils/volatileEnvPath.mjs` in `PACK_ARTIFACT_REQUIRED_PATHS`, so the prune can no longer silently drop a module the published CLI imports on every boot ([#11588](https://github.com/diegosouzapw/OmniRoute/pull/11588)) — thanks @ntdatt812 +- **i18n(vi):** translate the five reliability-column strings on the free-provider rankings page, which rendered as `__MISSING__:` markers in the Vietnamese UI ([#11589](https://github.com/diegosouzapw/OmniRoute/pull/11589)) — thanks @ntdatt812 +- **resilience:** restore the expired-connection retry-budget probe in the token-health sweep — the `!isGitHubAccessTokenOnlyConnection` carve-out reintroduced by #11608 contradicted the boundary pinned by #11592, so a GitHub connection parked at `expired` with retry budget remaining was never probed and could never self-heal ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)). ([#11672](https://github.com/diegosouzapw/OmniRoute/pull/11672)) — thanks @jonlwheat2-gif / @ntdatt812 +- **fix(dashboard):** Provider Stats now displays combo success rates on their native 0–100 percentage scale instead of multiplying them twice ([#11595](https://github.com/diegosouzapw/OmniRoute/pull/11595)) — thanks @pacocartones +- **fix(providers):** route DuckDuckGo Web Chat through the verified `duck.ai` endpoint, refresh its free model catalog, and avoid forwarding unsolved anti-abuse challenges ([#11597](https://github.com/diegosouzapw/OmniRoute/pull/11597)) — thanks @solstxce +- **fix(ui):** Console log Refresh and Copy controls now expose localized accessible names, keep copy actions visible on keyboard focus, and announce copy completion safely ([#11599](https://github.com/diegosouzapw/OmniRoute/pull/11599)) — thanks @pacocartones +- Dropped the stale-`.eslintcache` `restore-keys` fallback from both "Restore ESLint file cache" steps in `ci.yml`, so the blocking `Lint` job can no longer be served per-file verdicts computed under a different lint config, suppressions file or lockfile. `quality.yml` had already dropped it in #11963; `ci.yml` — the workflow that actually gates PRs — had not (#11600). ([#11996](https://github.com/diegosouzapw/OmniRoute/pull/11996)) +- fix(ci): pass `--pass-on-unpruned-suppressions` in `run-eslint-json.mjs` so the CI Lint job no longer fails when a suppression is merely orphaned by a genuine fix, mirroring the identical fix already in `validate-release-green.mjs` (#11600) ([#11983](https://github.com/diegosouzapw/OmniRoute/pull/11983)) — thanks @hartmark +- fix(handoff): enforce provider allowlist for universal handoff (#11602) — universal handoff now skips summarization when the selected summary model's provider is not included in the configured provider allowlist. ([#11619](https://github.com/diegosouzapw/OmniRoute/pull/11619)) — thanks @vsd2807 +- **fix(dashboard):** Show a stable error state when Search Analytics returns an HTTP error or malformed data ([#11603](https://github.com/diegosouzapw/OmniRoute/pull/11603)) — thanks @pacocartones +- **test(gamification):** pin the aggregate profile level to the XP-derived semantics of #11604 — `getAggregateXp()` now derives `currentLevel` from the summed XP (`calculateLevel(sum)`), not `MAX(stored current_level)`, and the #3484 fixture levels are aligned with the XP curve ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)). ([#11676](https://github.com/diegosouzapw/OmniRoute/pull/11676)) — thanks @jonlwheat2-gif / @pacocartones +- **fix(dashboard):** Keep the Profile level and progress aligned with aggregate XP, including bounded handling for invalid totals ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)) — thanks @pacocartones +- **fix(dashboard):** Prevent locked hidden badges from revealing their icon or opening private badge details before they are earned ([#11605](https://github.com/diegosouzapw/OmniRoute/pull/11605)) — thanks @pacocartones +- **fix(dashboard):** Restore keyboard focus after shared modals close and cancel delayed autofocus during cleanup ([#11607](https://github.com/diegosouzapw/OmniRoute/pull/11607)) — thanks @pacocartones +- **fix(dashboard):** Enable Enter and Space activation for clickable data-table rows without hijacking nested controls ([#11610](https://github.com/diegosouzapw/OmniRoute/pull/11610)) — thanks @pacocartones +- **fix(autoCombo,sse):** vendor-retired catalog ids are dropped from the auto-combo candidate pool and no longer win on leftover `arena_elo` / `user_override` rows; `getModelLifecycleDecision` consults `model-lifecycle.json` (prefix-stripped) so aggregator traffic is not `untracked` for ids the snapshot already knows ([#11625](https://github.com/diegosouzapw/OmniRoute/issues/11625)) ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)) — thanks @MumuTW +- **test(chatcore):** move Codex/Claude combo fixtures off models the lifecycle guard now rejects — `gpt-5.1-codex`/`gpt-5-codex` are vendor-retired (snapshot, #11626) and `claude-3-5-sonnet-20241022` is shut down, so native-passthrough and combo-fallback tests switched to `gpt-5.6-sol` and `claude-sonnet-4.6` ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)). ([#11675](https://github.com/diegosouzapw/OmniRoute/pull/11675)) — thanks @jonlwheat2-gif / @MumuTW +- fix(opencode-plugin): stop a free-tier budget that rounds up across a magnitude from rendering as `1000K`/`1000M` in the model picker — `fmtTokens` chose its unit from the raw token count and then rounded with `toFixed(1)`, so 999,950–999,999 printed as `1000K` rather than `1M` and just under a billion printed as `1000M` rather than `1B` ([#11684](https://github.com/diegosouzapw/OmniRoute/pull/11684)) — thanks @abhisheksharma2411 +- Cap prose-derived Antigravity quota resets at the configured model cooldown maximum while preserving authoritative Retry-After headers and Google RetryInfo hints. (#11695) Thanks @pacocartones. ([#11823](https://github.com/diegosouzapw/OmniRoute/pull/11823)) +- Compare `_rebaseline_*` notes structurally in the ratchet-bank verifier so an object-valued note no longer reports itself as rewritten on an unmodified tree — the nightly `bank-ratchet-shrinks` lane had been aborting on every run since 2026-08-11, leaving 41,672 lines of earned file-size shrinks unbanked (#11702). ([#11892](https://github.com/diegosouzapw/OmniRoute/pull/11892)) — thanks @MumuTW +- **fix(build):** `prepublish.ts` bundles the ChatGPT Web (Codex) MCP bridge through `runBuildTool()` instead of spawning `npx.cmd` raw, fixing the build crash on Node ≥ 20/Windows where `.cmd` shims cannot be spawned without a shell (EINVAL) ([#11704](https://github.com/diegosouzapw/OmniRoute/issues/11704)) ([#11706](https://github.com/diegosouzapw/OmniRoute/pull/11706)) — thanks @jonlwheat2-gif +- fix(codex): keep `parallel_tool_calls:false` on the translated Codex Responses Lite path (#11707) ([#11984](https://github.com/diegosouzapw/OmniRoute/pull/11984)) — thanks @hartmark +- **fix(tests):** the new Gemini Web Images retirement test (#11708) called the now-async `getExecutor()` synchronously, throwing `TypeError: getExecutor(...).getProvider is not a function` instead of asserting Gemini Web chat stays available. Awaited to match the R0.3 executor-registry refactor (#11220). ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(search):** Allow REST and MCP web-fetch requests to select the advertised `anysearch-search` provider explicitly ([#11716](https://github.com/diegosouzapw/OmniRoute/pull/11716)) — thanks @pacocartones +- **fix(providers):** Keep overnight peak-hour protection active after midnight according to the window's configured start day ([#11718](https://github.com/diegosouzapw/OmniRoute/pull/11718)) — thanks @pacocartones +- **fix(providers):** correct all five g4f.space gateway descriptions to document proof-of-work credits or member API keys, variable limits, and the remote prompt boundary instead of promising free keyless access at five requests per minute ([#11738](https://github.com/diegosouzapw/OmniRoute/pull/11738)). +- **fix(db):** `model_capabilities` is created by a migration instead of lazily on the first models.dev sync, so a clean install and an upgraded install converge on the same schema regardless of which features have run ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(ci):** `check:install-upgrade` now fails on an `npm` install truncated by ENOSPC (npm reports it as a warning and still exits 0), authenticates its health probe so the version assertion works against the hardened health payload, frees the clean-install tree before the upgrade phase, and no longer reports a schema divergence computed from a boot that never served ([#11845](https://github.com/diegosouzapw/OmniRoute/pull/11845)) +- **fix(routing):** harden session-affinity key derivation against oversized explicit IDs and empty structural-payload collisions, while recognizing Gemini and common text request shapes ([#11746](https://github.com/diegosouzapw/OmniRoute/pull/11746)) — thanks @MumuTW +- **fix(packages/browser-pool):** regenerate `package-lock.json` so the `packages/browser-pool` workspace's locked `playwright`/`@types/node` (and transitives) match its `package.json` specs, fixing cache-only/offline installs (`npm ci --offline`, Nix `buildNpmPackage`) that previously failed with `ENOTCACHED` ([#11747](https://github.com/diegosouzapw/OmniRoute/issues/11747)) — thanks @benjaminkitt ([#11784](https://github.com/diegosouzapw/OmniRoute/pull/11784)) — thanks @geekyNads +- **fix(assets):** Stop distributing 79 provider and CLI logos without verified source/license provenance, keep every provider/tool available through the internal generic icon, and prevent those IDs from falling through to an external icon CDN. ([#11750](https://github.com/diegosouzapw/OmniRoute/pull/11750)) +- **fix(providers):** retire the common `chatgpt-web` / `cgpt-web` provider because its pre-key and proof-of-work provenance could not be cleared ([#11754](https://github.com/diegosouzapw/OmniRoute/pull/11754)) — explicit chat and image requests, slash-prefixed models, persisted aliases, proxy overrides, and combo targets now fail closed with a sanitized HTTP `410` / `PROVIDER_RETIRED` before upstream dispatch. Migration 163 tombstones matching connections and invalidates their active leases without deleting history or widening API-key allowlists. The independent `chatgpt-web-codex` / `cgpt-codex` provider and its MIT-noticed vendored implementation remain available. +- **fix(usage):** quota and usage refresh no longer 409 when an exclusive lease reserves the connection ([#11758](https://github.com/diegosouzapw/OmniRoute/pull/11758)) — thanks @TheDemonTuan +- Keep the embedding registry's vector width and `embedding` type on models when a synced model exists for the same id, so `/v1/models` no longer reports registry-described embedding models widthless or untyped (#11761) — thanks @rahilmavani +- Correct `google/gemini-embedding-001` on the OpenRouter route to 3072 dimensions, the width it returns when `dimensions` is not sent (#11761) — thanks @rahilmavani +- **fix(vision-bridge):** Restrict automatic vision routing and fallbacks to models available in authoritative live provider catalogs, including aliases and effort variants ([#11767](https://github.com/diegosouzapw/OmniRoute/issues/11767)) — thanks @pacocartones ([#11813](https://github.com/diegosouzapw/OmniRoute/pull/11813)) +- **fix(providers):** reclassify Cerebras as a one-time $5 signup credit (payment method required, 30-day validity), not a recurring no-card 1M tokens/day trial ([#11773](https://github.com/diegosouzapw/OmniRoute/issues/11773)) ([#12591](https://github.com/diegosouzapw/OmniRoute/pull/12591)) — thanks @HouMinXi +- fix(cli): stop prepublish from re-rebuilding the already-built ESM-only opencode-plugin dist (#11787) ([#11990](https://github.com/diegosouzapw/OmniRoute/pull/11990)) — thanks @hartmark +- **fix(db):** Fresh installs no longer log a non-fatal `no such table: compression_run_telemetry` warning when retention cleanup runs before the lazily-created telemetry table exists ([#11802](https://github.com/diegosouzapw/OmniRoute/pull/11802)) — thanks @RaviTharuma +- fix(sse): set X-OmniRoute-Selected-Connection-Id on successful combo dispatches so downstream consumers stop falling back to an empty connection id (#11810) ([#11986](https://github.com/diegosouzapw/OmniRoute/pull/11986)) — thanks @hartmark +- **fix(services):** embedded CLIProxyAPI lifecycle checks now use public `/healthz`, while model discovery uses the configured dedicated data-plane API key instead of the management password ([#11811](https://github.com/diegosouzapw/OmniRoute/pull/11811)) — thanks @RaviTharuma +- Fix persisted model steps returning an error in the playground route simulator, and warn when structural steps cannot be simulated. ([#11862](https://github.com/diegosouzapw/OmniRoute/pull/11862)) — thanks @pacocartones +- **fix(providers):** Antigravity's dynamic mitmAlias table no longer routes `gemini-3.7-flash-{high,medium,low}` to a literal tier-suffixed upstream id just because one connected account's own discovery listed it directly — those display ids always resolve through the safe `gemini-3.7-flash-tiered` static alias, so one account's Google-provisioned access no longer 404s every sibling account of the provider ([#11824](https://github.com/diegosouzapw/OmniRoute/issues/11824), [#11651](https://github.com/diegosouzapw/OmniRoute/issues/11651)) ([#11988](https://github.com/diegosouzapw/OmniRoute/pull/11988)) — thanks @hartmark +- **fix(dashboard):** Expose Profile loading, errors, page structure, and XP progress to assistive technologies ([#11838](https://github.com/diegosouzapw/OmniRoute/pull/11838)) — thanks @pacocartones +- **fix(socks):** `Agent.connectTimeout` now bounds both SOCKS handshake and TLS connect, and `family === null` no longer falls back to `fetch-socks` ([#11842](https://github.com/diegosouzapw/OmniRoute/pull/11842)) — thanks @maxmad64bis +- **perf(compression):** OOM mitigations for large payload hashing, memoization, and token estimation ([#11844](https://github.com/diegosouzapw/OmniRoute/pull/11844) — thanks @AndrianBalanescu) +- Excluded Next.js Node File Trace manifests (`*.nft.json`) from the published npm tarball. They are build-time metadata and are never read while serving, but had grown to 668.7 MB — 61% of the package — which pushed the upload past the registry limit and made `npm publish` fail with `413 Payload Too Large`. ([#11864](https://github.com/diegosouzapw/OmniRoute/pull/11864)) +- **fix(cli):** keep the global OmniRoute server `--base-url` separate from provider-node `--endpoint` values in add, update, and validate commands ([#11860](https://github.com/diegosouzapw/OmniRoute/pull/11860)) — thanks @pacocartones +- **fix(config):** Nous Research's `Hermes-4-405B` model now displays as "Hermes 4 405B (Nous Research)" in both the provider registry and the free-model catalog, instead of the mislabelled "Hermes 4 7B" ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#11993](https://github.com/diegosouzapw/OmniRoute/pull/11993)) +- **fix(provider/nous):** inject required user= tag into Nous Research inference requests to resolve upstream 400 "missing tags" error ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825 ([#12044](https://github.com/diegosouzapw/OmniRoute/pull/12044)) +- **fix(build):** `npm run build` now fails in one second with a named package and a copy-pasteable fix when npm silently drops an externalised optional native dependency, instead of dying four minutes in with `Module not found: Can't resolve 'better-sqlite3'` ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- **fix(install):** `postinstall` no longer throws `ReferenceError: isAndroid is not defined` — failing the whole `npm install` — when the `better-sqlite3` rebuild fallback times out; the manual-fix guidance is reachable again ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks @ujjawalkaushik1110 +- Fixed the Alibaba free-tier allowlist test that went red on its own once the shipped catalog's `validUntil` (2026-08-27) passed, leaving every PR and `main` with a failing `Unit Tests (1/8)`. The test now builds its own packs with dates it controls, and covers the expired-pack fallback that production has actually been serving. ([#11867](https://github.com/diegosouzapw/OmniRoute/pull/11867)) +- Split the npm registry upload into its own GitHub-hosted job. npm refuses `--provenance` from a self-hosted runner (`422 ... Only "github-hosted" runners are supported`), which blocked the v3.8.50 publish; the heavy verification cannot move to a hosted runner, so it now hands the proven tarball over instead. ([#11868](https://github.com/diegosouzapw/OmniRoute/pull/11868)) +- **fix(ui):** the shared `DataTable` loading state no longer reads its decorative ⏳ glyph out to assistive technology, and now carries the same `role="status"` / `aria-live="polite"` / `aria-busy="true"` semantics as `PageLoading` ([#11869](https://github.com/diegosouzapw/OmniRoute/pull/11869)) — thanks @pacocartones +- **fix(api):** Bound the `timeout` accepted by `POST /api/providers/[id]/login` to the same 15s–600s window the Adobe Firefly and Conol browser-login paths already enforce, so a request can no longer pin a headful browser and the single active-login slot with an arbitrarily large poll budget. ([#11871](https://github.com/diegosouzapw/OmniRoute/pull/11871)) — thanks @pacocartones +- **fix(api):** Stop the tunnel and MITM routes from returning raw child-process error text, which disclosed host paths, binary install locations and Tailscale `tskey-*` credentials in a body some of these routes serve to non-loopback callers, and return a real 400 instead of a framework 500 when a tunnel request body fails validation. ([#11872](https://github.com/diegosouzapw/OmniRoute/pull/11872)) — thanks @pacocartones +- **fix(open-sse):** stream TTFT/ITL are sampled from a monotonic clock (`performance.now`) instead of `Date.now`, so an NTP correction or wall-clock jump can no longer inflate a genuine TTFT or produce a negative one that the `ttft >= 0` guard silently drops — protecting the router's OTel/EWMA/`usage_history`/speed-ranking signals ([#11873](https://github.com/diegosouzapw/OmniRoute/pull/11873)) — thanks @pacocartones +- **fix(providers):** xAI `reasoning_effort: "xhigh"` now reaches grok-4.6+ instead of being silently clamped to `"high"` ([#11879](https://github.com/diegosouzapw/OmniRoute/pull/11879)) — thanks @NoxzRCW +- **fix(dashboard):** the providers page no longer crashes into the error boundary when a provider id collides with an `Object.prototype` member (`constructor`, `__proto__`); icon lookups are own-property guarded ([#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)) — thanks @NoxzRCW +- **fix(skills):** injected skill tools declared in shorthand (`{"content": "string"}`) now forward valid JSON Schema, unblocking providers that validate tool schemas strictly such as Zhipu GLM on the Console Go tier ([#11881](https://github.com/diegosouzapw/OmniRoute/pull/11881)) — thanks @NoxzRCW +- **fix(api):** `POST /api/playground/simulate-route` now surfaces `combo-ref` and `provider-wildcard` persisted combo steps with a specific warning (naming the referenced combo, or the unresolved `provider/model` wildcard) instead of folding them into a generic "unsupported step" count; a `provider-wildcard` step is also now included as an unresolved target so the operator can see it is in the route (ported from [#11882](https://github.com/diegosouzapw/OmniRoute/pull/11882) — thanks @NoxzRCW). ([#11930](https://github.com/diegosouzapw/OmniRoute/pull/11930)) +- **fix(translator):** the streaming OpenAI→Claude translator keeps upstream usage, including prompt-cache tokens, when it arrives on a trailing `choices: []` chunk (Fireworks and any upstream using `stream_options.include_usage`) ([#11883](https://github.com/diegosouzapw/OmniRoute/pull/11883)) — thanks @NoxzRCW +- **fix(cli):** `omniroute update --apply` now tells you explicitly whether a running server was detected and, if so, that you must run `omniroute restart` to apply the update — it never restarted anything and previously implied the update was already live once files were installed. The dashboard's npm-mode Update flow (`/api/system/version`) now tries OmniRoute's own PID-file-managed supervisor before falling back to pm2, and reports an honest "restart required" step instead of a silent pm2-only "skipped" that read like a completed update. The server-side latest-version lookup backing the dashboard's update banner also gained `--prefer-online`, closing the same stale-npm-cache class already fixed in the CLI's own copy for #4376 ([#11885](https://github.com/diegosouzapw/OmniRoute/issues/11885)). ([#12005](https://github.com/diegosouzapw/OmniRoute/pull/12005)) +- **fix(resilience):** clear persisted LKGP pins when a target suffers connection/provider exhaustion or is skipped before dispatch due to cooldown/exhaustion/unavailability, preventing subsequent requests from repeatedly prioritizing known-dead providers ([#11911](https://github.com/diegosouzapw/OmniRoute/issues/11911)). ([#12013](https://github.com/diegosouzapw/OmniRoute/pull/12013)) — thanks @HouMinXi +- **fix(sse):** the OpenAI→Claude stream translator now defers the terminal `message_delta`/`message_stop` emission until the real usage block has arrived (or a genuine end-of-stream flush forces it) instead of emitting it immediately on `finish_reason` — previously, when the trailing usage-only chunk (`{"choices":[],"usage":{...}}`) arrived *after* the `finish_reason` chunk (the normal order for Fireworks/vLLM/Together and other `stream_options.include_usage` upstreams), the client-visible `message_delta` still carried stale/zero usage even though `state.usage` was internally corrected too late to matter (ported from [#11915](https://github.com/diegosouzapw/OmniRoute/pull/11915) — thanks @HouMinXi). ([#11933](https://github.com/diegosouzapw/OmniRoute/pull/11933)) +- **fix(sse):** `stripStore()` now forces `store=false` for stateless OpenAI-compatible Responses-API targets (unless the connection has explicitly opted in via `providerSpecificData.openaiStoreEnabled`), instead of only handling the `openai`/`agentrouter` cases — a client-supplied `store` value previously passed through untouched to backends that don't actually persist responses server-side ([#11916](https://github.com/diegosouzapw/OmniRoute/pull/11916) — thanks @HouMinXi). +- **fix(models):** custom provider-node models (synced, custom, and alias-backed) now appear under their configured prefix in the unified catalog when the operator's model-id prefix mode is canonical, instead of being dropped whenever alias-inclusion was otherwise disabled ([#11918](https://github.com/diegosouzapw/OmniRoute/pull/11918) — thanks @HouMinXi). +- **fix(models):** the unified model catalog now suppresses stale static registry models (including effort-tier variants) for any provider whose active connection has an authoritative live synced catalog, not only providers already using exclusive-synced-listing — a connection with `providerUsesAuthoritativeLiveCatalog` previously kept serving both the live-synced models and the stale static rows side by side ([#11919](https://github.com/diegosouzapw/OmniRoute/pull/11919) — thanks @HouMinXi). +- **fix(ui):** `ProviderIcon`'s three remaining unguarded lookups (`PROVIDER_ICON_ALIASES`, `LOCAL_SVG_ALIASES`, `THEMED_SVGS`) now use `Object.hasOwn()` own-property checks — a provider id such as `constructor` or `__proto__` previously resolved through the prototype chain instead of falling through to the unknown-provider CDN fallback (`getLobeProviderIcon()` itself was already guarded by [#11880](https://github.com/diegosouzapw/OmniRoute/pull/11880)); `ProviderPageHeader`'s `color` field is also now optional, matching the rest of the component's defensive typing (ported from [#11920](https://github.com/diegosouzapw/OmniRoute/pull/11920) — thanks @HouMinXi). ([#11935](https://github.com/diegosouzapw/OmniRoute/pull/11935)) — thanks @NoxzRCW +- fix(ollama): preserve multi-byte UTF-8 content split across stream chunks in the Ollama NDJSON transform, which previously corrupted CJK/emoji into U+FFFD (#11921) — thanks @pacocartones +- **fix(providers):** OrcaRouter chat requests now target `/v1/chat/completions` instead of the bare `/v1` API root, fixing the upstream `404 Invalid URL (POST /v1)` ([#11923](https://github.com/diegosouzapw/OmniRoute/pull/11923)). — thanks @echel0nn +- **fix(plugins):** deliver the `onStreamComplete` event to disk-installed plugins. The event shipped in v3.8.50 (#9669) was emitted internally but had no plugin-facing wiring, so no plugin could ever subscribe: the manifest schema silently dropped `hooks.onStreamComplete`, and the loader/manager only knew the seven legacy hooks. `onStreamComplete` is now a declarable manifest hook, wired through the loader and registered by the manager like the other hooks, and its payload carries a `requestId` so consumers can correlate the stream-completion event with the originating request ([#11934](https://github.com/diegosouzapw/OmniRoute/pull/11934)) — thanks @amaleta / @pacocartones +- **fix(db):** the Qdrant embedding-model dropdown now lists local/self-hosted providers (Ollama, LM Studio, vLLM, etc.) — an active connection is treated as "configured" when the provider allows an optional API key, not only when it has a real key or OAuth, so a running local embedding provider is no longer hidden from the picker ([#11949](https://github.com/diegosouzapw/OmniRoute/issues/11949)) ([#11995](https://github.com/diegosouzapw/OmniRoute/pull/11995)) +- **fix(providers):** Vertex AI Anthropic partner-model discovery now calls the Model Garden `v1beta1` publisher list (`/v1beta1/publishers/anthropic/models`, global) and parses its `publisherModels` envelope, so Claude models auto-synced from Vertex populate the active live catalog and route at request time instead of returning `Model '' is not available in the active live catalog` ([#11991](https://github.com/diegosouzapw/OmniRoute/issues/11991)) — thanks @fabioluissilva ([#11998](https://github.com/diegosouzapw/OmniRoute/pull/11998)) +- **fix(cli):** support `--base-url` alongside `--endpoint` in `omniroute nodes add`, `update`, and `validate` subcommands to prevent global `--base-url` shadowing issues ([#11999](https://github.com/diegosouzapw/OmniRoute/issues/11999)). ([#12033](https://github.com/diegosouzapw/OmniRoute/pull/12033)) — thanks @HouMinXi +- **fix(providers):** `cloudflare-ai` no longer refuses image content parts for every Workers AI model ([#12002](https://github.com/diegosouzapw/OmniRoute/pull/12002)) — the plain-string `content` requirement behind #2539 is carried by the _model_ schema, not by the `/ai/v1/chat/completions` endpoint (measured: an all-text part array returns 200 on `@cf/mistralai/mistral-small-3.1-24b-instruct`, `@cf/meta/llama-4-scout-17b-16e-instruct` and `@cf/meta/llama-3.3-70b-instruct-fp8-fast`, and 400 on the text-only `@cf/qwen/qwen2.5-coder-32b-instruct`). `transformRequest()` flattened every array and threw on the first non-text part (#6390), so image input was refused for vision-capable Cloudflare models that accept it. All-text arrays are still flattened — the one shape every model accepts — while an array carrying a non-text part is passed through untouched, so the attachment is still never silently dropped. Regression guards: `tests/unit/cloudflare-ai-image-parts-6390.test.ts`. — thanks @davidlinfr +- **fix(resilience):** decouple the limiter-managed execution backstop from the queue-wait budget — new `requestQueue.executionMaxWaitMs` (env `RATE_LIMIT_EXECUTION_MAX_WAIT_MS`, default 600000 = 10 min) now feeds Bottleneck's post-dispatch `expiration`, while `requestQueue.maxWaitMs` keeps its documented queue-wait semantics. Previously the queue-wait budget doubled as the execution expiration, so legitimate long-running calls on non-incremental gateways (whole generation buffered before the first upstream byte, e.g. Console Go / Command Code tiers serving GLM models) were killed mid-flight at the queue budget with a false 504 `RATE_LIMIT_EXECUTION_TIMEOUT` — the local limiter undercut the provider-aware upstream fetch-start timeouts. The surfaced 504 message now names `requestQueue.executionMaxWaitMs`; the error keeps the #4165 guarantees (disclaims an upstream timeout, preserves the Bottleneck error as `cause`, branded code + trusted provenance, classified request-scoped so combo falls back). A real queue-wait bound (the `Promise.race` around `limiter.schedule()` sketched in #9533) remains future work. (#12025) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **Call logs:** keep the `error` field when an artifact exceeds the storage cap, instead of replacing it with the omission marker. The error is the only field that says *why* a request failed and is typically ~90 bytes next to the multi-hundred-KB bodies that trip the cap, so dropping it left a size-limited row undiagnosable — a provider outage, a local timeout and an upstream 400 all rendered identically. It is now preserved at every fallback stage, truncated to 4KB if it is itself large ([#12026](https://github.com/diegosouzapw/OmniRoute/issues/12026)). ([#12095](https://github.com/diegosouzapw/OmniRoute/pull/12095)) — thanks @ntdat812 +- **fix(diagnostics):** preserve the error field (truncated to 4KB with a `[truncated: …]` suffix) in every call-log artifact size-limit fallback stage. Previously the minimal fallback replaced the error with `[omitted: call log artifact size limit exceeded]`, so an oversized artifact row showed nothing about WHY the request failed — e.g. 91 of 847 opencode-go 504 rows on one production instance were undiagnosable from the dashboard. Oversized request/response bodies are still omitted exactly as before; the error cap is independent of the payload sizes that tripped the fallback. (#12026) ([#12027](https://github.com/diegosouzapw/OmniRoute/pull/12027)) — thanks @alvinveroy +- **fix(sse):** OpenAI Responses clients that declare the native `web_search` tool now receive a spec-shaped `web_search_call` output item with `action.sources` alongside the preserved function-call round-trip, so search results executed through OmniRoute's own search backend are consumable by standard Responses clients (Codex, pi-web-access, …). ([#12031](https://github.com/diegosouzapw/OmniRoute/pull/12031)) — thanks @watchingdogs +- **fix(cli):** use in-thread alias resolver hooks on modern runtimes to avoid deprecation noise and improve Node.js forward compatibility ([#12073](https://github.com/diegosouzapw/OmniRoute/issues/12073)). ([#12083](https://github.com/diegosouzapw/OmniRoute/pull/12083)) — thanks @xiaoyaner0201 +- **fix(combo):** an operator-set **Agent Features → Context length** on a combo is now honored at request time. The value was persisted and advertised through `/v1/models`, but `resolveComboContextLimit()` never consulted it — so a multi-target combo whose members carry no per-model window fell through to the provider's generic `defaultContextLength` (openrouter 128000, command-code 200000) and rejected large requests with `Input exceeds context window … limit 128000` despite the combo being explicitly sized much larger. An identical single-target combo worked, because it collapses to its concrete target before the guard runs. Invalid values (0/negative/NaN/Infinity) are ignored, so the existing target → combo-min → fallback order is unchanged. ([#12090](https://github.com/diegosouzapw/OmniRoute/pull/12090)) — thanks @adivekar-utexas +- **fix(sse):** passthrough streams now estimate usage on finish when upstream closes without usage even with `stream_options.include_usage` — avoids `0 tokens / 0%` for providers that stay silent (and correctly handles trailing empty-choices usage) ([#12151](https://github.com/diegosouzapw/OmniRoute/pull/12151)) — thanks @maxmad64bis +- **fix(combos):** clearing an agent feature in the combos editor now persists — unchecking context cache protection, or emptying the system message or tool filter, sends an explicit `null` instead of dropping the field from the `PUT` body, which the update merge read as "leave unchanged" ([#12177](https://github.com/diegosouzapw/OmniRoute/pull/12177)) — thanks @foreveryh / @ntdatt812 +- **fix(memory):** self-hosted embedding endpoints now vectorize — the vector width is measured from the first embedding that comes back instead of being read from a registry that cannot describe them, so `vec_memories` is created and memories stop piling up unvectorized behind a green health check ([#12180](https://github.com/diegosouzapw/OmniRoute/pull/12180)) — thanks @kanade-hoshino / @ntdatt812 +- **fix(translator):** the leading `system` message now reaches Responses-API upstreams when its `content` is a content-part array — it was read as `typeof content === "string" ? content : ""`, so a prompt-caching client (Anthropic `cache_control`, the shape LiteLLM and the Anthropic SDK emit) had its entire system prompt replaced by an empty `instructions`. The request was still accepted with a normal `prompt_tokens` count, so the model answered with no instructions and nothing in the response said they were missing. Mid-conversation system turns already handled the array shape ([#7056](https://github.com/diegosouzapw/OmniRoute/pull/7056)); only the first one did not ([#12206](https://github.com/diegosouzapw/OmniRoute/issues/12206)). Regression guard: `tests/unit/translator-openai-responses-system-content-parts.test.ts`. ([#12207](https://github.com/diegosouzapw/OmniRoute/pull/12207)) — thanks @Gorillaz322 +- **fix(free-tier):** `/api/free-tier/summary` no longer computes its totals from a Radar feed built before the catalog the running release ships. When the cached feed is older — or carries no build date at all — the route answers from the shipped catalog, resolved through the operator's local model state so disabled and tombstoned models stay out of the numbers ([#12215](https://github.com/diegosouzapw/OmniRoute/pull/12215)). — thanks @maxmad64bis +- **fix(oauth):** Keep a Claude personal workspace and a Team organization as separate connections — they share the same email and `accountUUID`, so the email-only OAuth dedup let the second login overwrite the first account's tokens; `organizationUUID` now disambiguates them, the way `workspaceId` does for Codex ([#12222](https://github.com/diegosouzapw/OmniRoute/pull/12222)) — thanks @davidebaraldo +- **fix(resilience):** a 402 on a single paid model of a passthrough/gateway provider (e.g. `kilo-gateway`, `ollama-cloud`) no longer terminalizes the whole connection with a never-auto-recovered `credits_exhausted` status — only the paid model is locked out, so free models on the same key keep serving. 402 variant of [#3027](https://github.com/diegosouzapw/OmniRoute/issues/3027). Single-credential providers are unaffected — a 402 there is still treated as the key being genuinely out of credit ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239) / [#10616](https://github.com/diegosouzapw/OmniRoute/issues/10616)) ([#12242](https://github.com/diegosouzapw/OmniRoute/issues/12242)) — thanks @brick30llc-ctrl ([#12266](https://github.com/diegosouzapw/OmniRoute/pull/12266)) — thanks @geekyNads +- **fix(sse):** trust `finish_reason: "length"`/`"max_tokens"` over the reasoning-consumed-token ratio in response quality validation, so a reasoning model truncated below the old 90% threshold correctly fails and retries instead of returning empty content as a silent "success" ([#12262](https://github.com/diegosouzapw/OmniRoute/pull/12262)) — thanks @brick30llc-ctrl +- **fix(settings):** `PUT /api/settings/cache-config` now persists `alwaysPreserveClientCache` to the flat general settings the runtime cache-control policy actually reads; previously the value landed in the databaseSettings "cache" section and was silently ignored, so the endpoint had no effect on `cache_control` passthrough ([#12304](https://github.com/diegosouzapw/OmniRoute/pull/12304)) — thanks @davidebaraldo +- **fix(grok-cli):** treat omitted SuperGrokPro `creditUsagePercent` as 0% used so Provider Limits still renders a weekly bar (proto3 zero-elision) ([#12312](https://github.com/diegosouzapw/OmniRoute/pull/12312)) — thanks @HouMinXi +- **fix(combo):** Expose the two Auto-Combo scoring factors nobody could set — the combo validation schema and the dashboard weight sliders both declared 13 of the scorer's 15 factors, so `connectionDensity` (spreads load across a provider's connections) and `quality` were dropped on save and offered nowhere. The sliders also shipped their own default table that differed from the engine's on every non-zero factor and summed to 1.05, so the percentages shown next to them added up to 105%. Both lists now match `DEFAULT_WEIGHTS`, and a test keeps them there. Note that a combo whose stored `weights` omitted the two keys was effectively running with them at zero and the other thirteen renormalized upward; it now runs with the engine's intended distribution, so its routing does shift ([#12314](https://github.com/diegosouzapw/OmniRoute/pull/12314)) — thanks @maxmad64bis +- **fix(docs):** The free-tier reference no longer says its numbers come "confidence tagged per row" — no catalog entry carries a confidence tag and the API serves none, so every figure on that page is an estimate of the same, unstated quality. The page now states what an entry does vouch for: an independently documented hard stop (set by hand with the source in a comment, never defaulted to `true`) and a prompt-training disclosure, both with live counts the `check:docs-counts` gate keeps honest ([#12318](https://github.com/diegosouzapw/OmniRoute/pull/12318)) — thanks @maxmad64bis +- **fix(usage):** `adobe-firefly` and `firefly` have had a working usage fetcher since Adobe Firefly landed, but neither was ever added to the registration list, so the provider-plugin manifest, `genericQuotaFetcher` and the free-access quota cache all reported them as having no usage support — while `USAGE_SUPPORTED_PROVIDERS` said the opposite. Both are now declared, which also means their credit balance is fetched like any other declared provider's: `registerGenericQuotaFetchers` now registers a generic quota fetcher for them, and `resolveFreeAccessState` no longer returns early. A test holds the registration list to the dispatcher's switch in both directions, which is what the module's own docstring already asked for in prose ([#12321](https://github.com/diegosouzapw/OmniRoute/pull/12321)) — thanks @maxmad64bis +- **fix(quota):** drop the generic quota cache (agy / Antigravity / Claude OAuth) on an upstream 429 so reset-aware scoring does not keep a 60s stale snapshot, and force-refresh the next usage fetch so inner provider caches cannot recache the same window ([#12325](https://github.com/diegosouzapw/OmniRoute/pull/12325)) — thanks @HouMinXi +- **fix(combos):** deleting a combo now clears its persisted LKGP pins instead of leaving unreachable `key_value` rows behind ([#12326](https://github.com/diegosouzapw/OmniRoute/issues/12326)) ([#12425](https://github.com/diegosouzapw/OmniRoute/pull/12425)) — thanks @KrzysiekSko +- **fix(sse):** Keep ZWNJ (U+200C) and ZWJ (U+200D) in assistant text, reasoning and tool-call arguments — Persian/Kurdish half-space (`ارائه‌دهنده`), Arabic/Indic shaping and emoji sequences no longer lose them; the response de-obfuscation now removes joiners only between ASCII word characters, where the request side inserts them ([#12186](https://github.com/diegosouzapw/OmniRoute/issues/12186)) — thanks @rezjalibd ([#12359](https://github.com/diegosouzapw/OmniRoute/pull/12359)) — thanks @pacocartones +- **fix(resilience):** count resolved upstream 5xx results against the provider circuit breaker on the chat path — `CircuitBreaker.execute()` no longer reads a resolved `{ success: false, status: 5xx }` as a success that cancels the call-site failure, so a provider answering 503s now trips its breaker instead of staying `CLOSED` at `failureCount: 1`; single-model and combo dispatches are each accounted exactly once ([#12254](https://github.com/diegosouzapw/OmniRoute/issues/12254)) ([#12360](https://github.com/diegosouzapw/OmniRoute/pull/12360)) — thanks @pacocartones +- **fix(providers):** resolve the Codex quota auto-ping model from the live provider catalog and lifecycle registry instead of the retired `gpt-5.1-codex-mini`, and pause the ping with one actionable warning when no selectable Codex model exists rather than retrying a shut-down id every cooldown window ([#11905](https://github.com/diegosouzapw/OmniRoute/issues/11905)) ([#12361](https://github.com/diegosouzapw/OmniRoute/pull/12361)) — thanks @pacocartones +- **fix(api):** keep the `{created, data}` wrapper on combo-routed `/v1/images/generations` responses and default Codex image results to `b64_json` on both `/v1/images/generations` and `/v1/images/edits` so Codex CLI's built-in `image_gen` can decode them ([#12268](https://github.com/diegosouzapw/OmniRoute/issues/12268)) ([#12362](https://github.com/diegosouzapw/OmniRoute/pull/12362)) — thanks @pacocartones +- **fix(sse):** Name the shadowed custom provider node when a built-in provider id/alias (e.g. `openference` → `of`) reserves the prefix of an existing OpenAI/Anthropic-compatible node, so the runtime `No active credentials for provider: ` error explains that the prefix routed to the built-in and never reached the node's healthy connections, instead of contradicting the dashboard ([#11943](https://github.com/diegosouzapw/OmniRoute/issues/11943)) — thanks @morpheus9393 ([#12365](https://github.com/diegosouzapw/OmniRoute/pull/12365)) — thanks @pacocartones +- **fix(i18n):** wrap `ccOnboardingKeyPlaceholder` in ICU single quotes across all 43 locales so angle brackets render literally instead of being parsed as rich-text tags, which crashed the Claude Code onboarding block with `INVALID_MESSAGE: INVALID_TAG` ([#12302](https://github.com/diegosouzapw/OmniRoute/issues/12302)) ([#12369](https://github.com/diegosouzapw/OmniRoute/pull/12369)) — thanks @gonisulaimann +- **fix(models):** publish `effort_tiers` on Kimi K3's synced base-model entries (`kmca/k3`, `kmca/k3-256k`) so catalog-only clients (OpenCode, plain SDK pickers) can see and select the reasoning tiers (`low`/`high`/`max`) the synced metadata already carried — the `isSkippedEffortProvider` gate no longer suppresses tier visibility on those base entries, while synthetic `-` variant generation stays prevented and Codex/GLM base models remain excluded unchanged ([#12299](https://github.com/diegosouzapw/OmniRoute/issues/12299)) ([#12371](https://github.com/diegosouzapw/OmniRoute/pull/12371)) — thanks @gonisulaimann +- **fix(guardrails):** keep `auto`/`auto/*` virtual combos exempt from the Vision Bridge `fixedModel` credential guard so a combo target is passed through instead of silently falling back to global auto-selection ([#12237](https://github.com/diegosouzapw/OmniRoute/issues/12237)) ([#12373](https://github.com/diegosouzapw/OmniRoute/pull/12373)) — thanks @pacocartones +- **fix(combo):** capability-filter exhaustion caused by `max_tokens` above every target's known output limit now reports that reason (requested `max_tokens` vs the pool's highest known ceiling) instead of the unrelated "supports structured output" message ([#12229](https://github.com/diegosouzapw/OmniRoute/issues/12229)) — thanks @DW-MediaLab ([#12374](https://github.com/diegosouzapw/OmniRoute/pull/12374)) — thanks @pacocartones +- **fix(auth):** the `least-used` account strategy now prefers accounts without backoff before falling back to oldest `lastUsedAt`, the same tie-break `round-robin` already applies, so a failover no longer lands on a just-rate-limited account for a single request ([#12279](https://github.com/diegosouzapw/OmniRoute/issues/12279)) — thanks @tenshiak ([#12375](https://github.com/diegosouzapw/OmniRoute/pull/12375)) — thanks @pacocartones +- **fix(docker):** the `chatgpt-web-codex-browser` image now finds the Chrome binary under `chrome-linux64/` (Chrome for Testing layout in `playwright:v1.62.0-noble`) as well as the legacy `chrome-linux/`, so the container no longer crash-loops with `exec: --headless=new: not found` ([#12024](https://github.com/diegosouzapw/OmniRoute/issues/12024)) ([#12376](https://github.com/diegosouzapw/OmniRoute/pull/12376)) — thanks @pacocartones +- **fix(providers):** declare `groq/compound` and `allam-2-7b` as non-reasoning models in the curated Groq registry so `reasoning_effort` / `output_config.effort` / `thinking` from Claude Code are stripped instead of forwarded, which Groq rejected with HTTP 400 ([#12134](https://github.com/diegosouzapw/OmniRoute/issues/12134)) ([#12379](https://github.com/diegosouzapw/OmniRoute/pull/12379)) — thanks @pacocartones +- **fix(executors):** `OpencodeExecutor` no longer forces a direct connection when the connection has a proxy assigned in Proxy Management but no per-account proxies: the single-account fast path used to wrap the upstream dispatch in the direct-egress sentinel, discarding the ambient proxy context the chat handler had pinned from `proxy_assignments`, so API-key `opencode`/`opencode-go` connections egressed from the host IP (and hit geoblocks) despite the assignment. The direct pin is now applied only when no ambient proxy context exists ([#11894](https://github.com/diegosouzapw/OmniRoute/issues/11894) — thanks @hizzt) ([#12380](https://github.com/diegosouzapw/OmniRoute/pull/12380)) — thanks @pacocartones +- **fix(api):** `GET /v1/models` with `MODELS_CATALOG_PREFIX_MODE=canonical` (or `?prefix=canonical`) now lists providers whose registry alias is undefined or equal to their own id (Antigravity, Antigravity CLI and other self-aliased built-ins) — their single `provider/model` id was dropped by the alias/canonical duplicate guard in the static, synced, custom and alias-backed catalog loops ([#12058](https://github.com/diegosouzapw/OmniRoute/issues/12058)) — thanks @cheynetom ([#12381](https://github.com/diegosouzapw/OmniRoute/pull/12381)) — thanks @pacocartones +- **fix(translator):** Drop replayed `thinking` blocks that carry no signature (the shape produced from cross-provider `reasoning_content`) instead of stamping the default Claude signature on them, which Anthropic rejected with `400 Invalid signature in thinking block` on the next turn served by an Anthropic rung ([#12105](https://github.com/diegosouzapw/OmniRoute/issues/12105)) — thanks @atescivitci-cmd ([#12386](https://github.com/diegosouzapw/OmniRoute/pull/12386)) — thanks @pacocartones +- **fix(cli):** Resolve Bun's `--preload` polyfill path against the package root instead of `dist/`, so `omniroute` installed with `bun install -g` no longer crashes at startup with `error: preload not found …/dist/open-sse/utils/setupPolyfill.ts` ([#11980](https://github.com/diegosouzapw/OmniRoute/issues/11980)) — thanks @joglomedia ([#12387](https://github.com/diegosouzapw/OmniRoute/pull/12387)) — thanks @pacocartones +- **fix(providers):** `gemini-business` now publishes its model catalog — `/v1/models` and `/v1/providers/gemini-business/models` list the 12 enterprise Gemini ids the executor understands instead of returning an empty list (#12107) ([#12389](https://github.com/diegosouzapw/OmniRoute/pull/12389)) — thanks @pacocartones +- **fix(db):** install `busy_timeout` before the SQLite connection's first statement so a process opening the database while another one closes its WAL connection waits out the transient EXCLUSIVE lock instead of dying with `database is locked`, and recognise the drivers' real BUSY/PROTOCOL/IOERR errors as transient in the corruption probe so the same lock no longer renames the database away as corrupt; deflakes `cross-process contenders never both acquire the same connection` (#12394 — thanks @pacocartones) +- **fix(chat-admission):** derive the `chat_admission_busy` 503 `Retry-After` from observed heavyweight-lease occupancy — the larger of the exhausted `OMNIROUTE_CHAT_ADMISSION_QUEUE_MS` window and the time since capacity last turned over, capped at 60 s — instead of a fixed 1 s (structural) / 2 s (byte-stage) hint that invited Codex/agent fan-out clients to re-send ~1 MiB `/v1/responses` bodies every second into a gate held for the whole SSE lifetime; an idle gate keeps the historical floors ([#12135](https://github.com/diegosouzapw/OmniRoute/issues/12135)) (#12395 — thanks @pacocartones) +- **fix(api-manager):** the API key permissions modal no longer silently drops `allowedCombos` entries its Combo picker cannot render — routing-rule names such as `rt-*`, which the backend already honours — when "All" is clicked and the key is switched back to "Restrict"; those entries now survive the toggle, are listed read-only under the combo list so the count and the list agree, and are saved back verbatim instead of persisting `[]` (deny-all) (#12397 — thanks @pacocartones) +- **fix(catalog):** write the NUL separator of the catalog connection memo key, the provider serviceKind memo key, the Video Bridge promotion group key and a JSON-exactness test fixture as the `\u0000` escape instead of a raw byte — same runtime value, but the raw byte made git, GitHub and ripgrep treat those files as binary (hidden PR diffs, silently skipped searches); a guard test now keeps raw NUL bytes out of `src/`, `open-sse/` and `tests/` (#12403 — thanks @pacocartones) +- **fix(providers):** add `CLAUDE_CODE_CLIENT_VERSION` and `GITHUB_COPILOT_CLI_VERSION` env overrides so Anthropic/Copilot client-version gates can be unblocked without a rebuild ([#12417](https://github.com/diegosouzapw/OmniRoute/issues/12417)) ([#12632](https://github.com/diegosouzapw/OmniRoute/pull/12632)) — thanks @HouMinXi +- **fix(db):** back-fill `last_ping_at` and `last_pinged_reset_key` on `provider_connections` during schema reconciliation so divergent lineages that skipped `123_quota_auto_ping` still accept quota auto-ping writes ([#12470](https://github.com/diegosouzapw/OmniRoute/pull/12470) — thanks @KooshaPari) +- **fix(ci):** document MIT exceptions for `@eloqnt/{config,format-json,format-po}` (next-intl transitive; locked tarballs omit `license`) and keep the A2A lifecycle vitest off the real SQLite persistence seam ([#12581](https://github.com/diegosouzapw/OmniRoute/issues/12581)) ([#12595](https://github.com/diegosouzapw/OmniRoute/pull/12595)) +- OpenCode plugin `/v1/models` catalog fetch now waits 30s by default and attaches HTTP `statusCode` on 401/5xx so host fallback plugins can hop instead of seeing an untyped AbortError/UnknownError. ([#12607](https://github.com/diegosouzapw/OmniRoute/pull/12607)) — thanks @RaviTharuma +- **CI:** the OpenAPI security-tier gate now mirrors `isAlwaysProtectedPath()` in full — it also reads `ALWAYS_PROTECTED_API_PATTERNS`, so the pattern-gated credential routes (`/api/providers/{id}/{claude,codex}-auth/{export,apply-local}`, GHSA-5926-2w35-7h4q) no longer report as unannotated. (#12605) +- **fix(api):** GET /v1/models no longer waits forever on a hung coalesced catalog rebuild; cold-path waits are bounded (`CATALOG_BUILD_TIMEOUT_MS`, default 8s) and a last-good 200 is served when the rebuild times out ([#12627](https://github.com/diegosouzapw/OmniRoute/issues/12627)). ([#12628](https://github.com/diegosouzapw/OmniRoute/pull/12628)) — thanks @RaviTharuma +- **fix(cursor):** a non-array `tool_calls` on an assistant message no longer crashes the cursor request translator with a `TypeError`; both loops now require an array ([#12689](https://github.com/diegosouzapw/OmniRoute/issues/12689)) ([#12691](https://github.com/diegosouzapw/OmniRoute/pull/12691)) — thanks @soroush5 +- **fix(chat):** Chat Completions no longer return empty `content` after a server-owned memory or skills tool; the first provider send and account/model recovery now share one pipeline so a follow-up round-trip can fill the reply ([#12696](https://github.com/diegosouzapw/OmniRoute/issues/12696)) — thanks @HouMinXi ([#12867](https://github.com/diegosouzapw/OmniRoute/pull/12867)) +- **fix(dashboard):** batch-deleting provider keys no longer toasts failure after a successful delete when the confirm button's click event is forwarded as `onAfter` ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) — thanks @HouMinXi +- **fix(glm):** drop the extra 16th argument to `createSSETransformStreamWithLogger` that TypeScript rejected (TS2554) and that never reached the TransformStream ([#12711](https://github.com/diegosouzapw/OmniRoute/pull/12711)) ([#12770](https://github.com/diegosouzapw/OmniRoute/pull/12770)) — thanks @HouMinXi +- **fix(dashboard):** pass `nodeMap` into Runtime `QuotaGroup` so a quota monitor in error/exhausted/alerting no longer throws `ReferenceError: nodeMap is not defined`. ([#12868](https://github.com/diegosouzapw/OmniRoute/pull/12868)) — thanks @HouMinXi +- Restricted API keys whose `allowedModels` lists a combo name no longer skip every combo member at pre-dispatch (`ALL_TARGETS_SKIPPED`). Inner-target filtering still applies when the allow-list is a provider prefix or `disableNonPublicModels` is on ([#12899](https://github.com/diegosouzapw/OmniRoute/pull/12899)). — thanks @HouMinXi +- **fix(combos):** Effort-suffixed combo members inherit the base model's `model_context_overrides` row so priority order is not inverted on large requests ([#12926](https://github.com/diegosouzapw/OmniRoute/pull/12926)) — thanks @HouMinXi +- **fix(catalog):** Picker-added `customModels` enter the dispatch-time live catalog so combo members and bare inference no longer 400 ([#12934](https://github.com/diegosouzapw/OmniRoute/pull/12934)) — thanks @HouMinXi +- **fix(docker):** re-derive the Docker build's worker-pool memory budget from the MEASURED ~4.5 GB per-process RSS (the issue owner's own VPS dmesg OOM-killer reproduction) instead of the stale 2560 MB/worker inference #11419 shipped, and lower `OMNIROUTE_BUILD_WORKERS` 3 → 2 so 1 parent + 1 page-data worker (2 processes × 4.5 GB = 9 GB) fits the 12.288 GB (75%) budget on a 16 GB GitHub Actions runner — the previous default (1 parent + 2 workers = 13.5 GB) still overcommitted the runner and kept "Publish to Docker Hub" failing with `cannot allocate memory` after #11419 merged (#7518). ([#11663](https://github.com/diegosouzapw/OmniRoute/pull/11663)) +- **fix(electron):** the packaged Windows desktop build now passes the #7592 cold-restart smoke end to end. Five defects were found and fixed on the way: (1) optional-pack staging failed on any machine whose `tar` is GNU tar — it reads the drive letter in an absolute `-f C:\...` archive path as a remote rsh host (`Cannot connect to C:`), so staging now passes a bare filename with `cwd` at the tarball directory and surfaces tar stderr on failure; (2) the Electron `files` allowlist was missing `lib/loginHeaderCapture.js`, so the packaged main process crashed at startup with `Cannot find module './lib/loginHeaderCapture'`; (3) electron-builder ≥26 injects an `!**/node_modules/**` ignore into every extraResources pattern list that no positive filter can override, which silently dropped the staged runtime `node_modules` (including the better-sqlite3 N-API prebuild) from `resources/app` — a fresh v3.8.50 desktop build would have shipped with no native SQLite driver and reproduced the #7592 sql.js fallback on every machine; an `afterPack` hook now restores the staged `node_modules` after packing; (4) the packaged-app smoke harness redirected `USERPROFILE` into its temp DATA_DIR, but Electron resolves the Roaming profile from `%USERPROFILE%\AppData\Roaming\` (USERPROFILE takes precedence over the APPDATA env var) and the path service throws instead of creating it, so `requestSingleInstanceLock()` returned false and the app exited(0) silently before `app.whenReady()` — the harness now pre-creates the derived tree, and `ensureSmokeEnvDirs` is exported and unit-tested; (5) the #7592 driver assertion parsed a `[DB] Driver: ...` line that the server's primary DB path never printed (only the unused `openDatabaseAsync()` did) — `getDbInstance()` now emits the same line on open so the guard can actually assert the native driver. Also: the smoke env-allowlist unit test hardcoded POSIX paths and could only pass on Linux/macOS; expectations are now host-agnostic, with new regression tests for the USERPROFILE-derived tree and `tarPack` under absolute Windows-style paths ([#11443](https://github.com/diegosouzapw/OmniRoute/pull/11443)) — thanks @jonlwheat2-gif +- fix(sse): stop the auto-combo candidates inspector from silently dropping model-locked/cooled-down rows (#9133) ([#11994](https://github.com/diegosouzapw/OmniRoute/pull/11994)) — thanks @hartmark +- **fix(providers):** Perplexity Web no longer turns upstream stream failures into successful assistant text; pre-content failures remain eligible for fallback, partial output ends with a structured sanitized error, and failed sessions are not persisted ([#12465](https://github.com/diegosouzapw/OmniRoute/pull/12465)) +- **fix(video-bridge):** preserve chronological video and transcript fusion, source-frame timestamps, transcript provenance, full contact-sheet intervals, and pre-abort callback isolation ([#11681](https://github.com/diegosouzapw/OmniRoute/pull/11681)) +- **Z.ai Web:** HTTP 200 streams carrying an upstream error now terminate with a structured failure instead of assistant text plus a normal stop, preserving partial output while allowing pre-content combo fallback. ([#12454](https://github.com/diegosouzapw/OmniRoute/pull/12454)) +- Absorb `Error [AbortError]: request_signal_aborted` and DOMException AbortError shapes in the process-level client-abort crash guard so routine client disconnects no longer kill the server (exit code 7). ([#12165](https://github.com/diegosouzapw/OmniRoute/pull/12165)) — thanks @alvinveroy +- **fix(sse):** Treat Adapta Web `type:error` SSE events as sanitized non-stream failures instead of empty HTTP 200 completions. ([#12459](https://github.com/diegosouzapw/OmniRoute/pull/12459)) +- **fix(autoCombo):** layer 3 (`models_dev_tier`) no longer scores retired models as premium, merge cross-provider capability rows deterministically, and inherit tier scores through variant ids ([#11508](https://github.com/diegosouzapw/OmniRoute/issues/11508)). `getModelsDevTierFitness` now vetoes ids present in `config/quality/model-lifecycle.json` with status `retired` before any other signal; `loadModelCapabilities()` aggregates per `model_id` (any-non-null-true for booleans, max for `limit_context`) instead of last-write-wins over SQLite's undefined row order; a capabilities miss retries through `resolveScoresAs()`'s base id and reports `models_dev_tier:inherited`, matching layers 1–2 (#11492). Adds `getModelsDevTierFitnessWithSource()` and an `invalidateCapabilitiesCache()` test/ops hook. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) — thanks @Meet6338-X +- fix(sse): accept Claude `content:[]` + `stop_reason: max_tokens`/`tool_use` in `detectMalformedNonStream` (match `isEmptyContentResponse`) so Claude Code `/model` probes with `max_tokens: 1` no longer become a false 502 ([#11568](https://github.com/diegosouzapw/OmniRoute/pull/11568)) — thanks @Seramicx +- **fix(security):** sanitize `request.failed` diagnostics before publishing them to live dashboard listeners and replay history, while keeping status, model, provider, latency, and internal call-log diagnostics intact. ([#12469](https://github.com/diegosouzapw/OmniRoute/pull/12469)) +- **fix(pwa):** do not intercept dashboard navigations so Chrome can fall back from a stale HTTP/3 Alt-Svc advertisement (UDP :20128 is unpublished; F5 on a long-lived tab hung until a new tab opened a fresh TCP connection). ([#12767](https://github.com/diegosouzapw/OmniRoute/pull/12767)) — thanks @HouMinXi +- **fix(executors):** handle DuckDuckGo ERR_BN_LIMIT (418) without retrying — when the upstream returns `418 ERR_BN_LIMIT` (rate-limit/ban), the executor now returns the error immediately instead of burning another VQD acquisition that would only count against the IP limit. The retry logic for `418 ERR_CHALLENGE` (unsolved challenge) remains unchanged. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598)) ([#12110](https://github.com/diegosouzapw/OmniRoute/pull/12110)) — thanks @oyi77 / @Meet6338-X +- Fixed DuckDuckGo streaming responses losing JSON lines and UTF-8 characters split across network chunks. ([#11528](https://github.com/diegosouzapw/OmniRoute/pull/11528)) — thanks @pacocartones +- Harden generated Cloudflare, Vercel, and Deno relays by stripping hop-by-hop, framing, and proxy authentication headers before forwarding requests upstream. ([#11533](https://github.com/diegosouzapw/OmniRoute/pull/11533)) — thanks @pacocartones +- **feat(grok-cli):** Provider Limits shows grok-cli banked reset credits from `GetRemainingResets` (including a real zero; a failed RPC omits the row) and the existing View credits button now calls `ConsumerUiSvc/RedeemReset` for grok-cli. Live tokens use nested fields 10/20/30 (id + Timestamp), not compact 1/2/3. ([#12805](https://github.com/diegosouzapw/OmniRoute/pull/12805)) — thanks @HouMinXi +- **fix(memory):** Embedding Model Quick select, Embedding Source remote dropdown, and Rerank selector now list every configured provider with embedding/rerank support instead of only chat-catalog text matches plus OpenRouter live discovery; a generic OpenAI-compatible `/embeddings` + Cohere-compatible `/rerank` runtime fallback resolves any configured chat provider's embedding/rerank endpoint, so unlisted providers no longer fail with "Unknown embedding provider"; both memory selectors gained a free-text model override ([#11390](https://github.com/diegosouzapw/OmniRoute/pull/11390)) — thanks @rqzbeh +- Harden SQLite upgrades around the historical migration-074 version collision: missing discovery and inspector tables are replayed atomically, pre-existing databases (including setup-created skeletons) receive reusable content-addressed safety snapshots, and Node test/eval probes without `DATA_DIR` are isolated from the operator database. ([#12435](https://github.com/diegosouzapw/OmniRoute/pull/12435)) +- **fix(dashboard):** Moonshot/Kimi Open Platform voucher and cash leftover percentages follow the bucket balance, so an empty wallet no longer paints those rows as 100% while Available is 0% ([#12733](https://github.com/diegosouzapw/OmniRoute/pull/12733)) — thanks @HouMinXi +- **fix(combo):** return non-retryable HTTP 400 when all candidates for a pinned native Codex turn are unavailable due to model-scoped lockout, terminating the turn cleanly while preserving turn continuity and enabling standard Combo routing on subsequent turns ([#12240](https://github.com/diegosouzapw/OmniRoute/pull/12240)) — thanks @mdigitalbh81 +- Fixed auto-router strategies preserving the selected connection through ranking and dispatch when multiple connections share the same provider and model. ([#11530](https://github.com/diegosouzapw/OmniRoute/pull/11530)) — thanks @pacocartones +- Preserve Chat Completions JSON-object response formats when translating requests to the Responses API. ([#11529](https://github.com/diegosouzapw/OmniRoute/pull/11529)) — thanks @pacocartones +- Fix idempotency fingerprints so Responses API requests include `input` and other semantic generation fields using stable serialization, while excluding credentials and request metadata. ([#11532](https://github.com/diegosouzapw/OmniRoute/pull/11532)) — thanks @pacocartones +- **fix(api):** Generated API CLI commands now enforce required OpenAPI request bodies; Combo test commands forward the required `comboName` body, while API keys created by older writers after migration 149 preserve legacy allow-all Combo access without widening explicit empty allowlists — thanks @marcelokarval ([#12070](https://github.com/diegosouzapw/OmniRoute/pull/12070)) +- **fix(grok-web):** treat upstream streaming failures as failures instead of successful assistant text: error-only streams now fail readiness with HTTP 502, while failures after legitimate content preserve that partial output and terminate through the sanitized stream failure path without a normal `stop` completion. ([#12458](https://github.com/diegosouzapw/OmniRoute/pull/12458)) +- HuggingChat now turns HTTP 200 JSONL generation failures into a sanitized 502 before content, or a fixed public stream failure after partial output, so fallback and request persistence no longer record a false successful stop. ([#12456](https://github.com/diegosouzapw/OmniRoute/pull/12456)) +- **fix(providers):** keep 1min.ai HTTP 200 stream errors out of assistant content, preserve partial output, and expose sanitized terminal errors so pre-content failures can fall back. ([#12466](https://github.com/diegosouzapw/OmniRoute/pull/12466)) +- **fix(sessions):** preserve legacy Sessions data when the additive exclusive-session projection is unavailable, returning an empty projection and warning only once per contiguous outage instead of failing the endpoint; the Sessions badge intentionally reflects the merged legacy and exclusive row count introduced by [#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389) ([#11469](https://github.com/diegosouzapw/OmniRoute/pull/11469)) — thanks @KaspaPulse +- Render Nimble Search with the generic provider icon and serve Opper's proven logo locally. ([#12415](https://github.com/diegosouzapw/OmniRoute/pull/12415)) +- **fix(quality):** `config/quality/provider-assets-provenance.jsonl` was stale against the physical `public/providers/` tree (still listing 225 assets and 5 duplicate-content groups after several provider-retirement PRs removed 85 icon files), which would have made `check:provider-asset-provenance` fail on the next full run. Reconciled the manifest to the real 142-file snapshot (142 asset records, 1 duplicate-content group, 2 newly-registered files marked `unresolved` pending provenance research), repinned `auditedCommit`, and updated the regression test's hardcoded counts to match. ([#11876](https://github.com/diegosouzapw/OmniRoute/pull/11876)) +- fix(ui): wrap quota card footer action buttons to prevent clipping Refresh button on narrow cards — when extra action buttons such as `View credits` are present on Codex cards, allow the footer row to flex-wrap with `shrink-0` buttons so `Refresh now` remains visible and clickable across all responsive card widths ([#11464](https://github.com/diegosouzapw/OmniRoute/pull/11464)) — thanks @adevwithpurpose +- Keep Antigravity Gemini usable when the same connection's Claude weekly quota is empty; generic quota cache stays per-connection for every other provider. ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) — thanks @HouMinXi +- Fixed Tailscale tunnel setup to reject ports outside the valid 1-65535 range and safely install Windows MSI packages from paths containing apostrophes. ([#11534](https://github.com/diegosouzapw/OmniRoute/pull/11534)) — thanks @pacocartones +- Classify Cloudflared and Tailscale process-management routes as local-only and spawn-capable while preserving remote access to read-only tunnel status endpoints. ([#11531](https://github.com/diegosouzapw/OmniRoute/pull/11531)) — thanks @pacocartones +- Electron release workflow: the `publish-npm` job now grants `actions: read` to the reusable `npm-publish.yml` it calls (its `publish` job requests it), which is what made GitHub refuse the whole v3.8.50 run at startup and ship the release with zero desktop assets; a `workflow_dispatch` now builds the requested tag instead of the dispatching branch and can skip the npm leg (`publish_npm=false`) when only re-attaching assets ([#11974](https://github.com/diegosouzapw/OmniRoute/pull/11974)) +- **fix(dashboard):** The Combos page usage guide now reads its dismissal through `useSyncExternalStore` instead of correcting SSR state inside an effect, removing an extra commit of the page tree on every load (and the `react-hooks/set-state-in-effect` error it raised). ([#12671](https://github.com/diegosouzapw/OmniRoute/pull/12671)) +- Electron release: `electron/package-lock.json` regained the optional `electron-builder-squirrel-windows` subtree (13 entries) that `npm ci` had been refusing as out of sync, `electron-release.yml` gained a `build_ref` dispatch input and stops regenerating release notes on a re-attach dispatch, and the npm publish workflow attaches the SBOM to the GitHub Release on dispatch publishes too — so the v3.8.51 tag ships every desktop asset and the SBOM like v3.8.49 did ([#12022](https://github.com/diegosouzapw/OmniRoute/pull/12022)) +- Drained the reds every PR against `release/v3.8.51` was born with: documented the three service-worker build-id variables, registered the six retirement/tunnel tests with the mutation gate, approved `eslint-plugin-react-hooks` in the dependency allowlist, added the six `combo.sort.*` strings to `vi` and `pt-BR`, pointed the ChatGPT Web doc at the real migration-168 test, worded the g4f hint around the member key, and realigned four tests to the retired-provider catalog and the legacy-schema fixtures the retirement migrations touch. ([#11940](https://github.com/diegosouzapw/OmniRoute/pull/11940)) +- **fix(providers):** Claude, Grok, LMArena, Notion, and Perplexity web-cookie transports now use pooled `wreq-js` 3.2 instead of the native sidecar, with all nine supported bindings pinned and audited, and the applicable platform binding plus native-license evidence included in each release artifact ([#12429](https://github.com/diegosouzapw/OmniRoute/pull/12429), supersedes [#11753](https://github.com/diegosouzapw/OmniRoute/pull/11753)). +- **fix(providers):** Zed Hosted streaming failures now trigger fallback before content and end partial streams with a sanitized structured error instead of fake assistant text and a normal-success stop. ([#12455](https://github.com/diegosouzapw/OmniRoute/pull/12455)) +- **fix(test):** capture hook result after render (direct commit `b85d0abb0b`) +- **fix(sync):** repair the two regressions the v3.8.50 sync-back left on release/v3.8.51 (direct commit `5ade9e0851`) +- **revert(models):** drop the memo reset hook ported from #10055 (direct commit `282de841ae`) +- **fix(typecheck):** guard the optional connectionId before the reactive model sync (direct commit `f2825bad75`) +- **fix(quota-share):** release the winner's reserved in-flight slot (#11371) ([#11408](https://github.com/diegosouzapw/OmniRoute/pull/11408)) — thanks @oyi77 +- **fix(providers):** stop silently dropping reasoning effort on opencode families (#10788) ([#11409](https://github.com/diegosouzapw/OmniRoute/pull/11409)) — thanks @oyi77 +- **fix(combo):** enable genuine quota-aware routing for generic providers (antigravity, claude, etc.) ([#11411](https://github.com/diegosouzapw/OmniRoute/pull/11411)) — thanks @HouMinXi +- **fix(oauth):** allow expired token retry before deactivation and activate expired retry loop (#P0) ([#11414](https://github.com/diegosouzapw/OmniRoute/pull/11414)) — thanks @HouMinXi +- **perf(executors):** lazy-load the executor registry — defer class imports + construction to first use (#11220) ([#11421](https://github.com/diegosouzapw/OmniRoute/pull/11421)) — thanks @oyi77 +- **fix:** make release/v3.8.50 CI gates green (run 32786966560 root causes) ([#11450](https://github.com/diegosouzapw/OmniRoute/pull/11450)) — thanks @jonlwheat2-gif +- **fix(sse):** exact-domain cookie match and origin-equality URL assertions ([#11452](https://github.com/diegosouzapw/OmniRoute/pull/11452)) — thanks @hartmark +- **fix(providers):** send Responses API payload during OpenAI-compatible validation ([#11454](https://github.com/diegosouzapw/OmniRoute/pull/11454)) — thanks @tuandinh0801 +- **fix(command-code):** fallback to /alpha/generate for Go plan without Provider API access ([#11455](https://github.com/diegosouzapw/OmniRoute/pull/11455)) — thanks @Chewji9875 +- **fix(responses-continuation):** fail closed on a log-truncated stored input/output array ([#11473](https://github.com/diegosouzapw/OmniRoute/pull/11473)) — thanks @hartmark +- **fix(cli):** write jcode settings as [providers.omniroute] in config.toml ([#11484](https://github.com/diegosouzapw/OmniRoute/pull/11484)) — thanks @hongnoul +- **fix(logging):** raise chat-log array truncation cap to 1000 and unify duplicate implementations ([#11499](https://github.com/diegosouzapw/OmniRoute/pull/11499)) — thanks @hartmark +- **fix:** restore live websocket URL helpers ([#11509](https://github.com/diegosouzapw/OmniRoute/pull/11509)) — thanks @PauloFH +- **perf(core):** eliminate async waterfalls across home, cache, and db operations (#11396) ([#11512](https://github.com/diegosouzapw/OmniRoute/pull/11512)) — thanks @HouMinXi +- **fix(kiro):** add runtime.us-east-1.kiro.dev as first-attempt endpoint ([#11517](https://github.com/diegosouzapw/OmniRoute/pull/11517)) — thanks @rifqiawl +- **fix(providers):** remove duplicate 128-tool truncation in OpencodeExecutor (#11444) ([#11518](https://github.com/diegosouzapw/OmniRoute/pull/11518)) — thanks @HouMinXi +- **fix(providers):** poll onboardUser LRO before classifying BYOP ([#11519](https://github.com/diegosouzapw/OmniRoute/pull/11519)) — thanks @rifqiawl +- **fix(translator):** preserve exact tool name casing for Claude tool_use payloads (#11487) ([#11520](https://github.com/diegosouzapw/OmniRoute/pull/11520)) — thanks @HouMinXi +- **fix(kimi-web):** align health probe with executor domain ([#11521](https://github.com/diegosouzapw/OmniRoute/pull/11521)) — thanks @pacocartones +- **fix(cli):** preserve existing admin password during setup ([#11522](https://github.com/diegosouzapw/OmniRoute/pull/11522)) — thanks @pacocartones +- **fix(mitm):** keep Windows DNS elevation off the POSIX sudo path (#11430) ([#11542](https://github.com/diegosouzapw/OmniRoute/pull/11542)) — thanks @official-burak +- **fix(sse):** scale chat admission by ingest byte budget instead of a fixed request count ([#11548](https://github.com/diegosouzapw/OmniRoute/pull/11548)) — thanks @TheDemonTuan +- **fix:** accept Gemini Web cookie exports ([#11554](https://github.com/diegosouzapw/OmniRoute/pull/11554)) — thanks @rafacpti23 +- **fix(i18n):** complete Brazilian Portuguese translations ([#11558](https://github.com/diegosouzapw/OmniRoute/pull/11558)) — thanks @rafacpti23 +- **fix(sse):** explicit types for openai-responses pureHelpers — clears last failing core typecheck gate ([#11567](https://github.com/diegosouzapw/OmniRoute/pull/11567)) — thanks @jonlwheat2-gif +- **fix(opencode):** hydrate Proxy Pool references for no-auth connections ([#11584](https://github.com/diegosouzapw/OmniRoute/pull/11584)) — thanks @AStupidBear +- **fix(ci):** resolve inherited release/v3.8.51 base-red CI failures ([#11608](https://github.com/diegosouzapw/OmniRoute/pull/11608)) — thanks @jonlwheat2-gif +- **fix(db):** fallback load STORAGE_ENCRYPTION_KEY from env files ([#11614](https://github.com/diegosouzapw/OmniRoute/pull/11614)) — thanks @benzntech +- **fix(providers):** mark Brave Search as no longer having a free tier ([#11621](https://github.com/diegosouzapw/OmniRoute/pull/11621)) — thanks @turbolego +- **fix:** pass tool result content through as string in OpenAI/Claude -> Gemini translation ([#11624](https://github.com/diegosouzapw/OmniRoute/pull/11624)) — thanks @Hsia97 +- **fix(sse):** scope credential health to exact key ([#11628](https://github.com/diegosouzapw/OmniRoute/pull/11628)) — thanks @KaspaPulse +- **fix(install):** guard prepare script against missing husky ([#11634](https://github.com/diegosouzapw/OmniRoute/pull/11634)) — thanks @Prajeeth-12 +- **fix(security):** redact NVIDIA_API_KEY at the log sink in the ad-hoc diag script ([#11639](https://github.com/diegosouzapw/OmniRoute/pull/11639)) +- **fix(types):** clear the two type regressions red on release/v3.8.51 ([#11640](https://github.com/diegosouzapw/OmniRoute/pull/11640)) — thanks @jonlwheat2-gif +- **fix(build):** resolve workspace: protocol dependencies to concrete semver versions during prepublish (#11540) ([#11641](https://github.com/diegosouzapw/OmniRoute/pull/11641)) — thanks @HouMinXi +- **fix(search):** properly resolve configured search connection in /v1/responses pipeline (#11524) ([#11642](https://github.com/diegosouzapw/OmniRoute/pull/11642)) — thanks @HouMinXi +- **fix(authz):** trust cf-connecting-ip for via-proxy detection when peer is Cloudflare edge (#11514) ([#11643](https://github.com/diegosouzapw/OmniRoute/pull/11643)) — thanks @HouMinXi +- **fix(security):** compare the OIDC state cookie in constant time ([#11646](https://github.com/diegosouzapw/OmniRoute/pull/11646)) +- **fix(security):** namespace the dedup hash by the calling API key ([#11649](https://github.com/diegosouzapw/OmniRoute/pull/11649)) +- **fix(models):** gate the Codex-native catalog loop on prefix mode (#11632) ([#11662](https://github.com/diegosouzapw/OmniRoute/pull/11662)) — thanks @xiaoyaner0201 +- **fix(dashboard):** project Codex routing quota in account pools ([#11666](https://github.com/diegosouzapw/OmniRoute/pull/11666)) — thanks @KaspaPulse +- **fix(ddgw):** normaliza system no upstream da release v3.8.51 ([#11692](https://github.com/diegosouzapw/OmniRoute/pull/11692)) — thanks @RhianB14 +- **fix(memory):** honor category filter in GET /api/memory ([#11699](https://github.com/diegosouzapw/OmniRoute/pull/11699)) — thanks @Sabeekhann +- **fix(ci):** detect stale workspace lockfile entries ([#11714](https://github.com/diegosouzapw/OmniRoute/pull/11714)) — thanks @pacocartones +- **fix(ci):** stamp dist/BUILD_SHA before the npm publish provenance gate ([#11721](https://github.com/diegosouzapw/OmniRoute/pull/11721)) +- **fix(docker):** decouple the best-effort Bun image from the release manifest ([#11724](https://github.com/diegosouzapw/OmniRoute/pull/11724)) +- **perf(compression):** avoid cloning memo misses twice ([#11727](https://github.com/diegosouzapw/OmniRoute/pull/11727)) — thanks @RaviTharuma +- **fix(docker):** let the bun digest artifact be absent, not fatal ([#11740](https://github.com/diegosouzapw/OmniRoute/pull/11740)) +- **fix(shared):** block cloud-metadata hosts under default remote-image guard ([#11755](https://github.com/diegosouzapw/OmniRoute/pull/11755)) — thanks @santosraju99-hub +- **fix(api):** preserve caller-provided X-Correlation-Id on chat completions ([#11760](https://github.com/diegosouzapw/OmniRoute/pull/11760)) — thanks @aniruddhaadak80 +- **fix(resilience):** lock permanently retired models instead of short backoff (Gemini ban prevention) ([#11762](https://github.com/diegosouzapw/OmniRoute/pull/11762)) — thanks @turbolego +- **fix:** mark Vercel AI Gateway as passthroughModels ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **fix(resilience):** stop hammering permanently-moved endpoints and billing-suspended accounts ([#11774](https://github.com/diegosouzapw/OmniRoute/pull/11774)) — thanks @turbolego +- **fix(lease):** remove global static reservation and gate routing on live active lease occupancy ([#11775](https://github.com/diegosouzapw/OmniRoute/pull/11775)) — thanks @TheDemonTuan +- **fix(ci):** size the install-upgrade gate to a measured run, and log the pack cost ([#11776](https://github.com/diegosouzapw/OmniRoute/pull/11776)) +- **fix(pwa):** stop serving the stale shell after deploys ([#11779](https://github.com/diegosouzapw/OmniRoute/pull/11779)) — thanks @HouMinXi +- **fix(combo):** lock GitHub models rejected as "not supported" for future requests ([#11781](https://github.com/diegosouzapw/OmniRoute/pull/11781)) — thanks @turbolego +- **fix(cli):** probe both IPv4 and IPv6 loopback for server readiness (#11766) ([#11794](https://github.com/diegosouzapw/OmniRoute/pull/11794)) — thanks @vermasomesh835 +- **fix(sse):** preserve CLIProxyAPI wrappers per connection ([#11797](https://github.com/diegosouzapw/OmniRoute/pull/11797)) — thanks @RaviTharuma +- **fix(providers):** honor model auto-fetch opt-in on create ([#11798](https://github.com/diegosouzapw/OmniRoute/pull/11798)) — thanks @RaviTharuma +- **fix(dashboard):** make model auto-fetch order independent ([#11805](https://github.com/diegosouzapw/OmniRoute/pull/11805)) — thanks @RaviTharuma +- **fix(docker):** make GHA cache exports best effort ([#11806](https://github.com/diegosouzapw/OmniRoute/pull/11806)) — thanks @RaviTharuma +- **fix(kiro):** do not permanently ban on 'User is not authorized to make this call' ([#11809](https://github.com/diegosouzapw/OmniRoute/pull/11809)) — thanks @Deftera186 +- **fix(sse):** keep cache-write tokens in OpenAI-shaped usage ([#11814](https://github.com/diegosouzapw/OmniRoute/pull/11814)) — thanks @TheDemonTuan +- **fix(docs):** resolve relative markdown and wiki links across Fumadocs and GitHub wiki ([#11834](https://github.com/diegosouzapw/OmniRoute/pull/11834)) — thanks @AndrianBalanescu +- **fix(chat):** preserve unstripped model string for passthrough provider routing ([#11840](https://github.com/diegosouzapw/OmniRoute/pull/11840)) — thanks @raheemuddin786 +- **fix(mcp):** avoid async initialization inside sync esm wrappers (#11569) ([#11849](https://github.com/diegosouzapw/OmniRoute/pull/11849)) — thanks @HouMinXi +- **fix(sse):** preserve coherent markdown token boundaries across stream translation chunks (#11606) ([#11851](https://github.com/diegosouzapw/OmniRoute/pull/11851)) — thanks @HouMinXi +- **fix(ci):** run the install-upgrade gate on disk, not on the /tmp tmpfs ([#11855](https://github.com/diegosouzapw/OmniRoute/pull/11855)) +- **fix(skills):** expand shorthand property types in injected tool schemas ([#11857](https://github.com/diegosouzapw/OmniRoute/pull/11857)) — thanks @alvinveroy +- **fix(ci):** publish npm from a hosted runner so provenance is accepted ([#11877](https://github.com/diegosouzapw/OmniRoute/pull/11877)) +- **fix(lint):** pin react hooks plugin to 7.0.1 ([#11886](https://github.com/diegosouzapw/OmniRoute/pull/11886)) +- **fix(ci):** surface and clear release-green ESLint failure ([#11890](https://github.com/diegosouzapw/OmniRoute/pull/11890)) — thanks @MumuTW +- **fix(guardrails):** prevent duplicate prompt-injection-guard log output ([#11936](https://github.com/diegosouzapw/OmniRoute/pull/11936)) — thanks @patrykkopycinski +- **fix(auth):** downgrade expected transient states from warn to debug ([#11937](https://github.com/diegosouzapw/OmniRoute/pull/11937)) — thanks @patrykkopycinski +- **fix(codex):** fail over image generation for imported free plans ([#11948](https://github.com/diegosouzapw/OmniRoute/pull/11948)) — thanks @Bl0ck154 +- **fix(codex):** normalize non-stream responses ([#11951](https://github.com/diegosouzapw/OmniRoute/pull/11951)) — thanks @Bl0ck154 +- **fix(images):** forward Antigravity image size ([#11952](https://github.com/diegosouzapw/OmniRoute/pull/11952)) — thanks @Bl0ck154 +- **fix(db):** invalidate connection cache after upsert ([#11953](https://github.com/diegosouzapw/OmniRoute/pull/11953)) — thanks @Bl0ck154 +- **fix(codex):** restore imported account state ([#11954](https://github.com/diegosouzapw/OmniRoute/pull/11954)) — thanks @Bl0ck154 +- **fix(sse):** give extended-thinking targets the reasoning readiness budget ([#11959](https://github.com/diegosouzapw/OmniRoute/pull/11959)) — thanks @abhisheksharma2411 +- **fix(dashboard):** use opaque background and readable text color on cost chart tooltips ([#11960](https://github.com/diegosouzapw/OmniRoute/pull/11960)) — thanks @ZaimMarzuki +- **fix(antigravity):** send complete loadCodeAssist metadata (ideType/platform/pluginType as numeric enums) ([#11969](https://github.com/diegosouzapw/OmniRoute/pull/11969)) — thanks @rifqiawl +- **fix(core):** resolve DB init race condition and reasoning translation ([#12003](https://github.com/diegosouzapw/OmniRoute/pull/12003)) — thanks @raheemuddin786 +- **fix(admission):** exclude reclaimable page cache from the cgroup pressure ratio ([#12017](https://github.com/diegosouzapw/OmniRoute/pull/12017)) — thanks @HouMinXi +- **fix(build):** prune non-production trees in NFT trace excludes and tsconfig ([#12028](https://github.com/diegosouzapw/OmniRoute/pull/12028)) — thanks @Chewji9875 +- **fix(migrations):** add renamed migration compatibility for 056/073/077/101 ([#12036](https://github.com/diegosouzapw/OmniRoute/pull/12036)) — thanks @oyi77 +- **fix(sse):** default crash-guard logger to console.warn, not console ([#12042](https://github.com/diegosouzapw/OmniRoute/pull/12042)) — thanks @HouMinXi +- **fix(catalog):** derive vision/modalities for built-in auto combos from effective target pool ([#12046](https://github.com/diegosouzapw/OmniRoute/pull/12046)) — thanks @Prajeeth-12 +- **fix(release):** the packaged-app smoke verifies the database opened, not a driver line the primary path never prints (twin of #12032) ([#12047](https://github.com/diegosouzapw/OmniRoute/pull/12047)) +- **fix(barrel):** migrate src/app/ off the @/lib/localDb barrel import (#11795 Phase 2) ([#12051](https://github.com/diegosouzapw/OmniRoute/pull/12051)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate src/lib/ off the localDb barrel to direct db imports (#59) ([#12052](https://github.com/diegosouzapw/OmniRoute/pull/12052)) — thanks @jonlwheat2-gif +- **fix(barrel):** migrate open-sse, src/shared, src/sse, src/models, src/domain off the @/lib/localDb barrel import (#11795 Phase 4) ([#12053](https://github.com/diegosouzapw/OmniRoute/pull/12053)) — thanks @jonlwheat2-gif +- **fix(barrel):** delete the @/lib/localDb barrel — every consumer migrated (#11795 Phase 5) ([#12055](https://github.com/diegosouzapw/OmniRoute/pull/12055)) — thanks @jonlwheat2-gif +- **fix(radar):** restore D12 public boundary ([#12057](https://github.com/diegosouzapw/OmniRoute/pull/12057)) +- **fix(dev):** bound webpack and Tailwind scans ([#12075](https://github.com/diegosouzapw/OmniRoute/pull/12075)) — thanks @backryun +- **fix(dev):** isolate root layout settings reads ([#12076](https://github.com/diegosouzapw/OmniRoute/pull/12076)) — thanks @backryun +- **fix(dev):** reduce instrumentation executor fan-out (phase 3) ([#12078](https://github.com/diegosouzapw/OmniRoute/pull/12078)) — thanks @backryun +- **fix(dev):** make logging resources HMR-singleton ([#12079](https://github.com/diegosouzapw/OmniRoute/pull/12079)) — thanks @backryun +- **fix(sse):** keep unavailable forced connections scoped ([#12080](https://github.com/diegosouzapw/OmniRoute/pull/12080)) — thanks @keeltrace +- **fix(dev):** isolate batch dispatch from instrumentation ([#12081](https://github.com/diegosouzapw/OmniRoute/pull/12081)) — thanks @backryun +- **fix(ci):** reconcile release test contract drift ([#12082](https://github.com/diegosouzapw/OmniRoute/pull/12082)) — thanks @backryun +- **fix(ci):** accept CVE-2025-68121 in the prebuilt tls-client .so, auto-close base-red issues, guard Scorecard on the default branch ([#12085](https://github.com/diegosouzapw/OmniRoute/pull/12085)) +- **fix(db):** drop three consumer-less 1proxy exports — dead-code base-red on release/v3.8.51 after the barrel deletion (#12055) ([#12087](https://github.com/diegosouzapw/OmniRoute/pull/12087)) +- **fix(api):** clear the six API-route TypeScript regressions the new gate landed red on ([#12094](https://github.com/diegosouzapw/OmniRoute/pull/12094)) +- **fix(release):** never let the tag-push Create Release append auto notes to the curated body ([#12096](https://github.com/diegosouzapw/OmniRoute/pull/12096)) +- **fix(sse):** honor CLIProxyAPI environment API key ([#12099](https://github.com/diegosouzapw/OmniRoute/pull/12099)) — thanks @RaviTharuma +- **fix(oauth):** bind Google refresh to the client that issued the token ([#12106](https://github.com/diegosouzapw/OmniRoute/pull/12106)) — thanks @HouMinXi +- **fix(dashboard):** make RequestLoggerDetail loadable outside Next — CSS via globals.css + CJS/ESM interop (#11703 base-reds) ([#12114](https://github.com/diegosouzapw/OmniRoute/pull/12114)) +- **fix(leases):** project status lease row to lease columns so joined connection PII never escapes ([#12115](https://github.com/diegosouzapw/OmniRoute/pull/12115)) — thanks @geek007git +- **fix(plugins):** do not kill the plugin process when a fire-and-forget hook times out ([#12116](https://github.com/diegosouzapw/OmniRoute/pull/12116)) — thanks @geek007git +- **fix(guardrails):** restore injection-guard logging on middleware-only routes ([#12117](https://github.com/diegosouzapw/OmniRoute/pull/12117)) — thanks @geek007git +- **fix(plugins):** refresh stored manifest from disk on activate so new hook fields reach existing installs ([#12120](https://github.com/diegosouzapw/OmniRoute/pull/12120)) — thanks @geek007git +- **fix(codex):** preserve existing provider state when bulk-import upserts a matching connection ([#12122](https://github.com/diegosouzapw/OmniRoute/pull/12122)) — thanks @geek007git +- **fix(ci):** clear the two base-reds the 2026-08-30 merge batch left on release/v3.8.51 (round 3) ([#12123](https://github.com/diegosouzapw/OmniRoute/pull/12123)) +- **fix(sse):** strip type:'custom' from Claude tools on agentrouter dispatch ([#12126](https://github.com/diegosouzapw/OmniRoute/pull/12126)) — thanks @wahidsadik371-coder +- **fix(sse):** stop advertising video providers the dispatcher cannot run ([#12131](https://github.com/diegosouzapw/OmniRoute/pull/12131)) — thanks @quiterunner-commits +- **fix(search):** treat HTTP 432 and plan limit errors as transient cooldown ([#12139](https://github.com/diegosouzapw/OmniRoute/pull/12139)) — thanks @HouMinXi +- **fix(sse):** refuse an AI Horde queue that cannot fit the request budget ([#12143](https://github.com/diegosouzapw/OmniRoute/pull/12143)) — thanks @quiterunner-commits +- **fix(ci):** clear the base-reds the afternoon merge batch left on release/v3.8.51 (round 5: provider count 352, TS2554/TS2677) ([#12144](https://github.com/diegosouzapw/OmniRoute/pull/12144)) +- **fix(translator):** strip neutral tool_choice when tools absent in Responses-to-Chat (#12141) ([#12166](https://github.com/diegosouzapw/OmniRoute/pull/12166)) — thanks @HouMinXi +- **fix(guardrails):** pass providerId to getResolvedModelCapabilities in checkComboVision (#12112) ([#12169](https://github.com/diegosouzapw/OmniRoute/pull/12169)) — thanks @HouMinXi +- **fix(translator):** strip plaintext reasoning content for opaque responses backends (#12128) ([#12171](https://github.com/diegosouzapw/OmniRoute/pull/12171)) — thanks @HouMinXi +- **perf(stream):** compile hot-path regexes once, bound token caches, fix quadratic buffering ([#12179](https://github.com/diegosouzapw/OmniRoute/pull/12179)) — thanks @opensource-elearning +- **fix:** resolve compression worker file using runtime anchors instead of… ([#12183](https://github.com/diegosouzapw/OmniRoute/pull/12183)) — thanks @NightStalker-87 +- **fix:** prevent Claude Code session kills via liveness-aware readiness + auto model echo ([#12189](https://github.com/diegosouzapw/OmniRoute/pull/12189)) — thanks @opensource-elearning +- **fix(translator):** preserve falsy primitive values in Gemini and Antigravity function response results ([#12191](https://github.com/diegosouzapw/OmniRoute/pull/12191)) — thanks @Siva010 +- **fix(oauth):** mark empty Antigravity projectId as degraded and clear stale errors (#11284) ([#12205](https://github.com/diegosouzapw/OmniRoute/pull/12205)) — thanks @HouMinXi +- **fix(providers):** gate the Codex auto-ping usage read on the shared quota throttle ([#12209](https://github.com/diegosouzapw/OmniRoute/pull/12209)) — thanks @abhisheksharma2411 +- **fix(combos):** prioritize SQLite row id over inner JSON id and notify delete errors ([#12213](https://github.com/diegosouzapw/OmniRoute/pull/12213)) — thanks @HouMinXi +- **fix(providers):** mark featherless.ai as no longer having a free tier (`hasFree: false`) ([#12216](https://github.com/diegosouzapw/OmniRoute/pull/12216)) — thanks @turbolego +- **fix(deepseek-web):** stop Turbopack dev panic in the PoW worker path resolver ([#12221](https://github.com/diegosouzapw/OmniRoute/pull/12221)) — thanks @hartmark +- **fix(chat-admission):** clarify local 503 source ([#12223](https://github.com/diegosouzapw/OmniRoute/pull/12223)) — thanks @keeltrace +- **fix(dev):** silence webpack runtime module warnings ([#12228](https://github.com/diegosouzapw/OmniRoute/pull/12228)) — thanks @backryun +- **fix(db):** use module.require for CommonJS runtime driver loading ([#12230](https://github.com/diegosouzapw/OmniRoute/pull/12230)) — thanks @raheemuddin786 +- **fix(memory):** honest probe-driven FTS5 keyword status + memory_id rowid sync ([#12231](https://github.com/diegosouzapw/OmniRoute/pull/12231)) — thanks @raheemuddin786 +- **fix(system):** propagate abort signal to stream reader in HTTP version checks ([#12232](https://github.com/diegosouzapw/OmniRoute/pull/12232)) — thanks @raheemuddin786 +- **fix(resilience):** isolate local host execution errors from provider circuit breakers ([#12233](https://github.com/diegosouzapw/OmniRoute/pull/12233)) — thanks @raheemuddin786 +- **fix(sse):** sort injected tools deterministically for prompt caching ([#12234](https://github.com/diegosouzapw/OmniRoute/pull/12234)) — thanks @raheemuddin786 +- **fix(combo):** always clear the loop-safety timer, not just on the happy path (#11804) ([#12245](https://github.com/diegosouzapw/OmniRoute/pull/12245)) +- **fix(quality):** base-red pair — stryker tap registration + turn-pin suites aligned to the window gate ([#12255](https://github.com/diegosouzapw/OmniRoute/pull/12255)) — thanks @backryun +- **fix(dev):** qualify Turbopack runtime boundaries ([#12258](https://github.com/diegosouzapw/OmniRoute/pull/12258)) — thanks @backryun +- **fix(combo):** fall back to full pool when collapsed sole survivor is context-too-small ([#12278](https://github.com/diegosouzapw/OmniRoute/pull/12278)) — thanks @opensource-elearning +- **fix(combo):** bound the pre-dispatch unavailable skip so a stale label cannot dark a pool (#12168) ([#12285](https://github.com/diegosouzapw/OmniRoute/pull/12285)) +- **fix(security):** strip Qwen/Alibaba console-session cookies from provider API responses ([#12287](https://github.com/diegosouzapw/OmniRoute/pull/12287)) +- **fix(usage):** console-aware Token Plan guidance + subscription hint on bailian 401 ([#12288](https://github.com/diegosouzapw/OmniRoute/pull/12288)) +- **fix(resourcePressure):** log numeric detail on every rejection, recover faster ([#12293](https://github.com/diegosouzapw/OmniRoute/pull/12293)) — thanks @hartmark +- **fix(release):** drain the 2026-09-01 base-red window — passthrough usage regression + radarPage i18n keys ([#12327](https://github.com/diegosouzapw/OmniRoute/pull/12327)) +- **fix(ci):** openapi-security-tiers checker must honor routeGuard patterns + imported prefixes ([#12350](https://github.com/diegosouzapw/OmniRoute/pull/12350)) — thanks @ggiak +- **perf(ci):** cache node_modules in the npm-ci-retry composite (#8084 D3) ([#12408](https://github.com/diegosouzapw/OmniRoute/pull/12408)) +- **revert(ui):** point CTAs back at their real destinations ([#12410](https://github.com/diegosouzapw/OmniRoute/pull/12410)) +- **fix(release):** drain the 2026-09-02 base-red — rerank-providers import + api-typecheck baseline ratchet ([#12414](https://github.com/diegosouzapw/OmniRoute/pull/12414)) +- **fix(types):** make system prompt injection noImplicitAny-safe ([#12416](https://github.com/diegosouzapw/OmniRoute/pull/12416)) +- **fix(quality):** validate typecheck baseline schema ([#12419](https://github.com/diegosouzapw/OmniRoute/pull/12419)) +- **fix(memory):** resolve rerank provider node cache import ([#12421](https://github.com/diegosouzapw/OmniRoute/pull/12421)) +- **fix(ci):** repair release-root regressions — pack dedup, web-session syntax, uc-image ids ([#12423](https://github.com/diegosouzapw/OmniRoute/pull/12423)) — thanks @backryun +- **fix(providers):** separate MaxAI and UC credential contracts ([#12431](https://github.com/diegosouzapw/OmniRoute/pull/12431)) +- **fix(cli):** preserve Claude settings on config set ([#12432](https://github.com/diegosouzapw/OmniRoute/pull/12432)) — thanks @KrzysiekSko +- **fix(providers):** repair the maxai credential block truncated by merge auto-resolve ([#12433](https://github.com/diegosouzapw/OmniRoute/pull/12433)) +- **fix(quality):** record the 2026-09-02 merged growth in the file-size baseline ([#12434](https://github.com/diegosouzapw/OmniRoute/pull/12434)) +- **fix(uc):** route UC error strings through sanitizeErrorMessage; allowlist the retired codex id ([#12437](https://github.com/diegosouzapw/OmniRoute/pull/12437)) +- **fix(adapta):** redact streamed upstream errors ([#12438](https://github.com/diegosouzapw/OmniRoute/pull/12438)) +- **fix(sse):** remap non-contiguous upstream tool_calls index to a gap-free output_index ([#12445](https://github.com/diegosouzapw/OmniRoute/pull/12445)) — thanks @hartmark +- **fix(quality):** detect duplicate tool_calls entries in one response ([#12446](https://github.com/diegosouzapw/OmniRoute/pull/12446)) — thanks @hartmark +- **fix(conversations):** resolve turn content OmniRoute never sends back to the client ([#12447](https://github.com/diegosouzapw/OmniRoute/pull/12447)) — thanks @hartmark +- **fix(combo):** do not treat credits-exhausted 401 as auth skip ([#12449](https://github.com/diegosouzapw/OmniRoute/pull/12449)) — thanks @RaviTharuma +- **fix(auth):** do not park healthy quota accounts as expired ([#12452](https://github.com/diegosouzapw/OmniRoute/pull/12452)) — thanks @RaviTharuma +- **fix(responses-continuation):** fail closed on a collector-truncated, empty output array ([#12460](https://github.com/diegosouzapw/OmniRoute/pull/12460)) — thanks @hartmark +- **fix(logging):** raise the SSE payload collector's default cap ([#12461](https://github.com/diegosouzapw/OmniRoute/pull/12461)) — thanks @hartmark +- **fix(resilience):** surface Responses failed.error.message in 502s ([#12472](https://github.com/diegosouzapw/OmniRoute/pull/12472)) — thanks @RaviTharuma +- **fix(security):** unbiased maxai X-Random nonce + stricter URL/regex assertions ([#12502](https://github.com/diegosouzapw/OmniRoute/pull/12502)) +- **fix(video):** re-anchor transcript log-redaction so PII/credential maskers can't reopen the leak (#12150 P1 follow-up) ([#12503](https://github.com/diegosouzapw/OmniRoute/pull/12503)) +- **fix(monitoring):** serve cached credentialHealth off the request path ([#12533](https://github.com/diegosouzapw/OmniRoute/pull/12533)) — thanks @RaviTharuma +- **fix(quota):** keep Antigravity Gemini usable when Claude weekly is empty ([#12566](https://github.com/diegosouzapw/OmniRoute/pull/12566)) — thanks @HouMinXi +- **fix(authz):** hard-gate every credential export and CLI-config write (GHSA-5926-2w35-7h4q) ([#12600](https://github.com/diegosouzapw/OmniRoute/pull/12600)) +- **fix(docs):** restore the Next build — REMOVED_PROVIDERS.md had no frontmatter (base-red #12581) ([#12610](https://github.com/diegosouzapw/OmniRoute/pull/12610)) +- **fix(quality):** re-point the zcodeProtocol public-creds allowlist to line 313 ([#12615](https://github.com/diegosouzapw/OmniRoute/pull/12615)) +- **fix(security):** close 3 advisories — search baseUrl exfil, sk- in the error sanitizer, bifrost relay header leak ([#12620](https://github.com/diegosouzapw/OmniRoute/pull/12620)) +- **fix(providers):** sanitize boolean required and nested bare maps for Gemini (#12269) ([#12624](https://github.com/diegosouzapw/OmniRoute/pull/12624)) — thanks @HouMinXi +- **fix(resilience):** keep Overloaded STREAM_EARLY_EOF off the provider breaker ([#12626](https://github.com/diegosouzapw/OmniRoute/pull/12626)) — thanks @HouMinXi +- **fix(catalog):** re-audit free-tier quotas against official pages ([#12649](https://github.com/diegosouzapw/OmniRoute/pull/12649)) +- **fix(dashboard):** keep a request's pending-tracking id stable across combo target retries ([#12650](https://github.com/diegosouzapw/OmniRoute/pull/12650)) — thanks @hartmark +- **fix(tests):** retire dead model ids from the chat-pipeline integration suite (base-red #12581) ([#12670](https://github.com/diegosouzapw/OmniRoute/pull/12670)) +- **fix:** resolve SqliteError no such table compression_run_telemetry during cleanup ([#12682](https://github.com/diegosouzapw/OmniRoute/pull/12682)) — thanks @groovecityJO +- **fix(combo):** treat a pin-only step as implicit connection allowlist ([#12697](https://github.com/diegosouzapw/OmniRoute/pull/12697)) — thanks @HouMinXi +- **fix(ci):** ship .npmrc in published package so legacy-peer-deps applies to consumers (#11544) ([#12699](https://github.com/diegosouzapw/OmniRoute/pull/12699)) — thanks @KooshaPari +- **fix(docker):** pin 4 CLI tools to exact versions (#12576) ([#12703](https://github.com/diegosouzapw/OmniRoute/pull/12703)) — thanks @KooshaPari +- **fix(docker):** document and harden cli profile trust boundary (#12570) ([#12706](https://github.com/diegosouzapw/OmniRoute/pull/12706)) — thanks @KooshaPari +- **fix(sse):** redact video transcript in pre-guardrail rejected-request logs (#12150 P2 item 7) ([#12710](https://github.com/diegosouzapw/OmniRoute/pull/12710)) +- **fix(i18n):** quote placeholder in OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES description (#12505) ([#12769](https://github.com/diegosouzapw/OmniRoute/pull/12769)) — thanks @KooshaPari +- **fix(api/pricing):** surface validation error message as string, not raw object (#12494) ([#12771](https://github.com/diegosouzapw/OmniRoute/pull/12771)) — thanks @KooshaPari +- **fix(sse):** re-enable prompt compression for native Codex passthrough ([#12834](https://github.com/diegosouzapw/OmniRoute/pull/12834)) — thanks @thomasmaerz +- **fix(dashboard):** model health tests for a provider node set to the Responses API now call `/v1/responses` with a Responses-shaped body instead of `/v1/chat/completions` — those models were reported as `Provider returned HTTP 200 but no text content` even though the same model answered normally through `/v1/responses` ([#13070](https://github.com/diegosouzapw/OmniRoute/issues/13070)) ([#13087](https://github.com/diegosouzapw/OmniRoute/pull/13087)) — thanks @ntdatt812 +- fix(routing): stop a round-robin combo's "opencode" targets from collapsing onto the opencode-zen connection (#11912) ([#13283](https://github.com/diegosouzapw/OmniRoute/pull/13283)) +- fix(routing): stop the reactive-compaction debug log from lying when compression is globally disabled (#11977) ([#13276](https://github.com/diegosouzapw/OmniRoute/pull/13276)) +- fix(electron): relativize standalone-bundle symlink targets so Stage 8 manifest verification stops failing on Windows (#11979) ([#13251](https://github.com/diegosouzapw/OmniRoute/pull/13251)) +- fix(dashboard): surface a visible error when Compression Studio's combined preview run fails (#12061) ([#13288](https://github.com/diegosouzapw/OmniRoute/pull/13288)) +- fix(dashboard): make the compression "Effective pipeline" preview honor the active profile and warn when the master switch is off (#12063) ([#13287](https://github.com/diegosouzapw/OmniRoute/pull/13287)) +- fix(providers): scope TinyCMS Web signer's DOM shims to each call instead of leaking them for the process lifetime, and surface a clean HTTP status on a non-JSON interception-toggles error (#12072) ([#13254](https://github.com/diegosouzapw/OmniRoute/pull/13254)) +- fix(guardrails): stop Vision Bridge from re-selecting a model locked after a 404 (#12111) ([#13259](https://github.com/diegosouzapw/OmniRoute/pull/13259)) +- fix(sse): require Responses-shaped body before native OpenAI-compatible passthrough (#12129) ([#13278](https://github.com/diegosouzapw/OmniRoute/pull/13278)) +- fix(providers): minimax-m3 now collapses manual thinking.type:"enabled" to adaptive, preventing upstream 400 (2013) (#12132) ([#13249](https://github.com/diegosouzapw/OmniRoute/pull/13249)) +- fix(db): scope model visibility overrides by modality so hiding a Chat model no longer hides an identically-ID'd Image/Embeddings/etc. model (#12172) ([#13258](https://github.com/diegosouzapw/OmniRoute/pull/13258)) +- fix(db): scope local-provider apiKey dedup to matching base URL so LM Studio/Ollama-style connections support multiple accounts (#12173) ([#13250](https://github.com/diegosouzapw/OmniRoute/pull/13250)) +- fix(providers): refresh Trae's stale Referer/Origin and forward user timezone so imported connections stop failing with 401 (#12190) ([#13255](https://github.com/diegosouzapw/OmniRoute/pull/13255)) +- fix(providers): route opencode-go/gpt-5.6-luna to /responses instead of /chat/completions (#12196) ([#13275](https://github.com/diegosouzapw/OmniRoute/pull/13275)) +- fix(dashboard): allow deleting the last extra-upstream-header row even when invalid (#12251) ([#13252](https://github.com/diegosouzapw/OmniRoute/pull/13252)) +- **fix(i18n):** translate pre-existing `__MISSING__:` keys for `combo.sort`, `requestLogger.detail` expand/collapse, `common.profile`, and `settings.resilienceCredentialHealth*` across 39 locales ([#12272](https://github.com/diegosouzapw/OmniRoute/issues/12272)) ([#13034](https://github.com/diegosouzapw/OmniRoute/pull/13034)) — thanks @HouMinXi +- fix(cli): run the Node.js runtime compatibility guard before the heavy `tsx/esm` + Commander import chain so an unsupported runtime gets a clear message instead of a raw `Invalid regular expression flags` crash (#12296) ([#13253](https://github.com/diegosouzapw/OmniRoute/pull/13253)) +- fix(dashboard): refresh the providers list after deleting a compatible provider node (#12298) ([#13256](https://github.com/diegosouzapw/OmniRoute/pull/13256)) +- fix(usage): fail closed on API-key budget enforcement when a provider's `auto` routing alias has no pricing row, instead of silently counting it as $0 (#12341) ([#13257](https://github.com/diegosouzapw/OmniRoute/pull/13257)) +- **fix(providers):** include the submitted Agnes video model when polling by `video_id` ([#12356](https://github.com/diegosouzapw/OmniRoute/pull/12356)) — thanks @pacocartones +- **fix(routing):** custom OpenAI-compatible nodes now honor the saved Chat/Responses API type after edits instead of letting the node's original ID prefix override the live connection setting ([#11884](https://github.com/diegosouzapw/OmniRoute/issues/11884)). ([#12358](https://github.com/diegosouzapw/OmniRoute/pull/12358)) — thanks @pacocartones +- fix(sse): surface an error instead of a silent empty 200 when a Claude stream closes with zero bytes (#12398) ([#13285](https://github.com/diegosouzapw/OmniRoute/pull/13285)) +- fix(oauth): warn before the dead localhost:8080 redirect in antigravity/gemini `oauth start` (#12413) ([#13265](https://github.com/diegosouzapw/OmniRoute/pull/13265)) +- fix(providers): stop devin-cli spawn error from double-closing the SSE controller (#12517) ([#13261](https://github.com/diegosouzapw/OmniRoute/pull/13261)) +- **fix(rerank):** clamp Voyage `top_k` to the documents actually sent after empty-string filtering, and honor `return_documents: false` in the NVIDIA response adapter (#12523 — thanks @pacocartones) +- **fix(audio):** `/v1/audio/translations` now resolves combo names the way `/v1/audio/transcriptions` already does, so a combo that `GET /v1/models` advertises is fanned out to its targets instead of being rejected with `400 Invalid translation model: . Use format: provider/model`; literal `provider/model` ids and unknown bare names behave as before (#12536 — thanks @pacocartones) +- **fix(gemini):** strip the JSON-Schema-2020-12 `prefixItems` keyword from Gemini tool schemas at every nesting level, so Claude Code tool definitions no longer fail with `400 Unknown name "prefixItems"` on Gemini models (#12540 — thanks @pacocartones) +- **fix(api-manager):** Expose an accessible loading status while API keys are fetched instead of an empty accessibility tree (#12541 — thanks @pacocartones) +- **fix(video):** Clamp `estimateJpegFrameBytes` at zero for padding-only payloads and build the three encode-side frame data URIs from `JPEG_FRAME_DATA_URI_PREFIX` instead of a repeated literal (#12543 — thanks @pacocartones) +- **fix(devin):** accept Windows `DEVIN_AGENTIC_HOME` sandbox paths (`C:\...\.sandbox\...`) in the isolated-home check so the Devin Claude Bridge no longer fails closed on Windows ([#12405](https://github.com/diegosouzapw/OmniRoute/issues/12405)) (#12545 — thanks @pacocartones) +- **fix(db):** Add `conversation_turn_nodes` and orphaned `agentic_conversations` to the auto-cleanup cycle under the existing `retention.callLogs` window, so identity nodes whose call-log content has already been purged no longer accumulate without bound in `storage.sqlite` (#12548 — thanks @pacocartones) +- **fix(i18n):** Wrap the `~/.claude/profiles//settings.json` placeholder in ICU single quotes in the `OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES` feature-flag description across all 42 locales and the TypeScript default, so next-intl no longer fails with `INVALID_MESSAGE: UNCLOSED_TAG` and the Feature Flags card shows the description instead of the raw key (#12549 — thanks @pacocartones) +- **fix(orchestration):** `updateCloudAgentTask` now publishes the task's real `status` on `agent.task.updated` when an update only touches `result`, `activities` or `error`, instead of the fabricated `"updated"` state, and stays silent when no row matched the id (#12550 — thanks @pacocartones) +- **fix(i18n):** the home "Recent Requests" panel and the Provider Topology legend are now translated instead of rendering English copies on non-English dashboards; the legend reads its own `home.topologyLegend*` labels with consistent casing rather than borrowing the memory-settings "Recent" and analytics "Error" strings (#12551 — thanks @pacocartones). +- **docs(reference):** bring the `FEATURE_FLAGS.md` catalog back to 1:1 with `featureFlagDefinitions.ts` — 20 missing flags added, the two `*_BLOCK_THRESHOLD` env-only knobs moved out of the flag tables, category/total counts and the Live WS port corrected, guarded by a static test (#12552 — thanks @pacocartones) +- **fix(i18n):** backfill missing `usage.kiloPass*` strings in 39 locales and restore `featureFlagOmnirouteDisableThinkingLevelVariantsDescription` in `pt.json` ([#12561](https://github.com/diegosouzapw/OmniRoute/issues/12561)) ([#13027](https://github.com/diegosouzapw/OmniRoute/pull/13027)) — thanks @HouMinXi +- fix(docker): default docker-compose app ports (dashboard/API/live-WS) to loopback instead of `0.0.0.0`, closing the anonymous `/v1` LAN/WAN exposure gap left open by `REQUIRE_API_KEY=false` (#12568) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): close DNS-rebinding SSRF gap in webhook outbound-URL guard (#12569) ([#13243](https://github.com/diegosouzapw/OmniRoute/pull/13243)) +- fix(docker): require a per-session token on the VNC browser CDP bridge and isolate it on a dedicated Docker network (#12571) ([#13241](https://github.com/diegosouzapw/OmniRoute/pull/13241)) +- fix(open-sse): write Adobe Firefly session tokens and cookie jars with 0700/0600 permissions instead of the process umask (#12572) ([#13242](https://github.com/diegosouzapw/OmniRoute/pull/13242)) +- fix(api): remove hardcoded wildcard CORS in openai-to-gemini-sse.ts so the centralized fail-closed CORS gate is the sole source of `Access-Control-Allow-Origin` (#12573) ([#13239](https://github.com/diegosouzapw/OmniRoute/pull/13239)) +- fix(api): enforce API key policy (budget/rate-limit/schedule/endpoint scoping) on the ElevenLabs speech-to-text, text-to-speech and voices proxy routes (#12574) ([#13244](https://github.com/diegosouzapw/OmniRoute/pull/13244)) +- fix(sse): cap HuggingChat NDJSON body size and bound the read loop with the fetch timeout so a stalled or hostile upstream cannot buffer unbounded memory (#12577) ([#13282](https://github.com/diegosouzapw/OmniRoute/pull/13282)) +- fix(docker): scope the cliproxyapi/qdrant/bifrost sidecars to loopback by default and forward `CLIPROXYAPI_MANAGEMENT_KEY` into the cliproxyapi container so its management API is not left both unauthenticated and LAN-published (#12578) ([#13240](https://github.com/diegosouzapw/OmniRoute/pull/13240)) +- fix(api): create DB export temp paths with `fs.mkdtempSync` instead of predictable timestamps (#12579) ([#13237](https://github.com/diegosouzapw/OmniRoute/pull/13237)) +- Cline 401 bodies that say "re-authenticate your Cline account" classify as a refreshable OAuth token, not a terminal expired key. The cooling panel no longer labels every cooldown as a 429; it shows the recorded last error instead. (#12594) ([#13060](https://github.com/diegosouzapw/OmniRoute/pull/13060)) — thanks @HouMinXi +- **fix(catalog):** degrade unknown combo targets instead of dropping LCD modalities, and persist OpenRouter `architecture.input_modalities` into the capability snapshot ([#12613](https://github.com/diegosouzapw/OmniRoute/issues/12613)) ([#13042](https://github.com/diegosouzapw/OmniRoute/pull/13042)) — thanks @HouMinXi +- fix(providers): send `x-api-key` instead of `Authorization: Bearer` for OpenCode Zen's `/v1/responses` endpoint (Muse Spark Contributor models), fixing a 401 on OmniRoute's auth header (#12633) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(sse): surface the actionable "Auggie CLI not found" message when the shell reports a missing `auggie` binary via exit code instead of a spawn error (#12645) ([#13263](https://github.com/diegosouzapw/OmniRoute/pull/13263)) +- **fix(config):** Persist deletions of built-in background-degradation entries — when a stored settings record exists its `degradationMap` is now authoritative instead of being merged under the defaults, so an entry the user removed in the dashboard no longer reappears on the next apply or restart ([#12424](https://github.com/diegosouzapw/OmniRoute/issues/12424)) ([#12647](https://github.com/diegosouzapw/OmniRoute/pull/12647)) — thanks @pacocartones +- **fix(gamification):** action-count badge milestones (First Token, Token Consumer, Token Machine, Token Whale, and the token-sharing tier) are now backed by a durable `xp_action_counts` counter incremented in `addXp()`, instead of a live `COUNT(*)` over `xp_audit_log`. The audit log is pruned by `retention.xpAuditLog` (default 30 days), so on a default install those "lifetime" milestones were really "actions in the last 30 days" and unlocked badges could stop unlocking once old rows aged out. `getActionCount()` and `checkActionCountBadges()` now read the same durable source, and a migration backfills existing totals from the surviving audit rows ([#12546](https://github.com/diegosouzapw/OmniRoute/issues/12546)) ([#12651](https://github.com/diegosouzapw/OmniRoute/pull/12651)) — thanks @pacocartones +- **fix(images):** `/v1/images/edits` now iterates a combo's targets the same way `/v1/images/generations` does (#9239) instead of flattening a bare combo to its first target. A combo whose first target is not edit-capable — or lacks credentials — now falls through to a later edit-capable target rather than hard-erroring, and missing credentials are skipped (not a hard `401`) to match the generations path. The per-target skip/terminal classification is extracted into a shared `runImageComboTargets` loop, so generations behavior is unchanged ([#12547](https://github.com/diegosouzapw/OmniRoute/issues/12547)). ([#12653](https://github.com/diegosouzapw/OmniRoute/pull/12653)) — thanks @pacocartones +- fix(sse): add first-byte watchdog to the TLS-fingerprint transport so a stalled wreq body falls back instead of hanging for minutes (#12656) ([#13272](https://github.com/diegosouzapw/OmniRoute/pull/13272)) +- fix(sse): exempt tiny-budget reasoning probes from combo quality failure and surface persisted-cooldown skips in ALL_TARGETS_SKIPPED diagnostics (#12659) ([#13269](https://github.com/diegosouzapw/OmniRoute/pull/13269)) +- fix(models): declare the real ~1M contextLength for OpenCode Zen's Muse Spark 1.2 models instead of falling back to the 200000 provider default (#12681) ([#13247](https://github.com/diegosouzapw/OmniRoute/pull/13247)) +- fix(oauth): align codebuddy-cn OAuth User-Agent with the chat/usage CLI version to avoid WAF false positives (#12702) ([#13264](https://github.com/diegosouzapw/OmniRoute/pull/13264)) +- fix(dashboard): surface an authentication-required banner instead of silently blanking database settings for a guest session (#12709) ([#13270](https://github.com/diegosouzapw/OmniRoute/pull/13270)) +- **fix(resilience):** stop unbounded queue that hangs 6min until Aborted — gate, provider slot, and Bottleneck queue now share a per-connection `maxWaitMs` budget; `fail-closed` on exhaust (503); execution backstop `executionMaxWaitMs` overridable per connection with upstream clamp ([#12715](https://github.com/diegosouzapw/OmniRoute/pull/12715)) — thanks @maxmad64bis (with thanks to @Tushar49 for surfacing the slow-provider need in #12635) +- **fix(auto-combo):** every mode pack now carries `quality` and `reliability` weights (`quality` 0.02, 0.03 in `quality-first`; `reliability` 0.03, 0.04 in `reliability-first`), so selecting a pack no longer silences either signal; `DEFAULT_WEIGHTS` is unchanged ([#12731](https://github.com/diegosouzapw/OmniRoute/pull/12731)) — thanks @maxmad64bis +- **fix(sse):** the provider execution pipeline keeps the upstream error `code`/`type` again — both were lost when the non-streaming leg was extracted, so a config-class `422` (Antigravity missing project) degraded into a generic account cooldown instead of reaching the gate that recognises it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(db):** a legacy `call_logs` table no longer aborts startup — the provider-stats index is created after the column healing runs, not before it, so an install predating `request_type` opens instead of failing with `no such column` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(catalog):** a malformed row in the operator-writable custom-models blob no longer kills every `auto/*` pool ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- **fix(tests):** realign the guards that the same day's merges left asserting the old behavior — the provider-count total after GreenPT and EURouter, the GLM stream buffer slot that `#12925` turned into a declared parameter, and the injection scan bound that `#13104` reshaped from a head-only window into head-plus-tail ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#13349](https://github.com/diegosouzapw/OmniRoute/pull/13349)) +- fix(cache): fold tool_choice/tools/response_format into the semantic cache signature so a cached tool_calls response can no longer be replayed for a request whose tool policy forbids it (#12734) ([#13267](https://github.com/diegosouzapw/OmniRoute/pull/13267)) +- **fix(models):** stop treating provider-supplied `isFree:true`/`:free`/`0/0` flags as trusted unless the provider has a documented free tier — only the shipped free-tier catalog decides otherwise; fetched `isFree:true` trusted only for a free-tier provider, custom `isFree:true` via a trusted path ([#12744](https://github.com/diegosouzapw/OmniRoute/pull/12744)) — thanks @maxmad64bis +- fix(memory): authenticate the internal /v1/rerank loopback call so memory reranking no longer silently degrades to unranked order when REQUIRE_API_KEY=true (#12745) ([#13268](https://github.com/diegosouzapw/OmniRoute/pull/13268)) +- fix(sse): parse Ollama Cloud's current usage markup (`$X of $Y used` aria-label, nested width style) (#12749) ([#13260](https://github.com/diegosouzapw/OmniRoute/pull/13260)) +- fix(cli): setup-opencode no longer sends an active context's management token to `/v1/models` when `--api-key`/`OMNIROUTE_API_KEY` is supplied — an explicit flag or the env var now always outranks the context's token, and the flag itself is no longer swallowed by the parent program's global `--api-key` option (#12783) ([#13246](https://github.com/diegosouzapw/OmniRoute/pull/13246)) +- fix(sse): register Arcee AI in the executor provider registry so requests reach api.arcee.ai instead of silently falling back to OpenAI (#12784) ([#13277](https://github.com/diegosouzapw/OmniRoute/pull/13277)) +- **fix(quota):** share one upstream quota read when identical saturation checks arrive at the same time, so traffic bursts don't multiply provider API calls ([#12787](https://github.com/diegosouzapw/OmniRoute/pull/12787)) — thanks @maxmad64bis +- **fix(db):** ignore expired or invalid rate-limit cooldown writes so a stale timestamp can't lock a connection that should be usable — clearing still works as before ([#12788](https://github.com/diegosouzapw/OmniRoute/pull/12788)) — thanks @maxmad64bis +- **fix(providers):** single source for provider order with `xao` ranking alongside `xai-oauth` ([#12790](https://github.com/diegosouzapw/OmniRoute/pull/12790)) — thanks @maxmad64bis +- **fix(routing):** off-table models check the free-model catalog before inheriting premium prices, latency bootstraps from the observed pool median, async tiers read live database pricing with a 90-day freshness gate, and the tier cache invalidates on every pricing write ([#12792](https://github.com/diegosouzapw/OmniRoute/pull/12792)) — thanks @maxmad64bis +- **fix(auto-combo):** snapshot scoring uses the observed per-connection account tier and per-model quality instead of neutral constants, and circuit-open providers rank lower at build time ([#12794](https://github.com/diegosouzapw/OmniRoute/pull/12794)) — thanks @maxmad64bis +- **fix(pool):** empty `auto/*` pools now say why they are empty, and the models listing uses the same paid check as routing ([#12795](https://github.com/diegosouzapw/OmniRoute/pull/12795)) — thanks @maxmad64bis +- fix(routing): recognize CLIProxyAPI's 'unknown provider for model' 400 as fallback-worthy (#12800) ([#13284](https://github.com/diegosouzapw/OmniRoute/pull/13284)) +- **fix(sse):** translate-mode streams now emit the estimated token counts as a trailing usage-only chunk before `[DONE]` when the upstream stays silent, so metered chat clients see totals instead of nothing ([#12828](https://github.com/diegosouzapw/OmniRoute/pull/12828)) — thanks @maxmad64bis +- **fix(db):** provider stats stay truthful on empty and legacy databases: fallback counts default to `0` instead of `null`, latency averages read `null` (not `0`) when no durations were recorded, failures logged before the error-type column existed group under `pre_migration` instead of `unclassified`, and per-provider queries use two new composite indexes ([#12832](https://github.com/diegosouzapw/OmniRoute/pull/12832)) — thanks @maxmad64bis +- fix(nvidia): fail open when a synced model catalog goes stale instead of gating forever (#12849) ([#13248](https://github.com/diegosouzapw/OmniRoute/pull/13248)) +- **fix(db):** WAL maintenance lives in its own module: a `TRUNCATE` checkpoint that hits a busy database now warns with its streak and retries once via `PASSIVE` instead of logging success, and closing-time checkpoints no longer report success on builds without a database file, with the busy totals visible in the authenticated monitoring health payload ([#12853](https://github.com/diegosouzapw/OmniRoute/pull/12853)) — thanks @maxmad64bis +- **fix(health):** quota with no snapshots reads empty instead of a contradicting 0% ([#12857](https://github.com/diegosouzapw/OmniRoute/pull/12857)) — thanks @maxmad64bis +- fix(a2a): accept the dashboard's own session cookie on /a2a so "Run message/send" no longer fails with "Unauthorized: missing or invalid API key" (#12888) ([#13271](https://github.com/diegosouzapw/OmniRoute/pull/13271)) +- **fix(logs):** the Logs grid's in-memory filter pass no longer discards rows the SQL query already matched — selecting an API key from the dropdown (which sends the key's id) returns its calls again, the Combo tab shows every combo instead of only those whose name contains a "1", and the model filter and search cover the same columns as the query ([#12896](https://github.com/diegosouzapw/OmniRoute/pull/12896)) — fixes [#12873](https://github.com/diegosouzapw/OmniRoute/issues/12873) — thanks @ntdatt812 +- **fix(a2a):** `/api/a2a/status` now builds the agent card from the request that asked for it, so a gateway reached at a non-localhost host no longer advertises `http://localhost:20128` as its A2A URL ([#12918](https://github.com/diegosouzapw/OmniRoute/pull/12918)). — thanks @ntdatt812 +- **fix(compression):** progressive aging now appends its `[COMPRESSED:aging:…]` annotation after a turn's `tool_result` blocks instead of in front of them, so Anthropic no longer rejects aged conversations with "`tool_use` ids were found without `tool_result` blocks immediately after" ([#12920](https://github.com/diegosouzapw/OmniRoute/pull/12920)). — thanks @ntdatt812 +- **fix(bedrock):** model import now resolves context limits for every vendor prefix instead of only `anthropic.*`, so `global.openai.gpt-5.6-*` no longer imports with a null `inputTokenLimit` and gets rejected pre-flight at the 200k default ([#12921](https://github.com/diegosouzapw/OmniRoute/pull/12921)). — thanks @ntdatt812 +- **fix(stream):** the 64 KB stream buffer GLM asks for is honoured instead of dropped, and the type error it caused no longer fails the API Route Typecheck gate on every open PR ([#12925](https://github.com/diegosouzapw/OmniRoute/pull/12925)) — thanks @ntdatt812 +- **fix(guardrails):** mask PII inside a `tool_result`'s nested content array, which the masker walked past while redacting its sibling block ([#12930](https://github.com/diegosouzapw/OmniRoute/pull/12930)) — thanks @ntdatt812 +- **fix(dashboard):** the Radar catalog table no longer leaves absent data unexplained — empty limits, unknown context windows, and unreported capabilities each explain themselves on hover, and a new check keeps it that way ([#12937](https://github.com/diegosouzapw/OmniRoute/pull/12937)) — thanks @maxmad64bis +- **fix(sse):** geo-blocked opencode requests rotate to the next account proxy instead of failing, so one refused egress no longer aborts the whole chain ([#12941](https://github.com/diegosouzapw/OmniRoute/pull/12941)) — thanks @maxmad64bis +- **fix(codex):** dashboard "clear cooldown" and the CAS recovery path now drop nested `codexScopeRateLimitedUntil` maps in the same write that nulls `rate_limited_until`, and fresh quota snapshots with headroom lift fallback-sourced scope cooldowns parked by quota preflight ([#12817](https://github.com/diegosouzapw/OmniRoute/issues/12817), [#12860](https://github.com/diegosouzapw/OmniRoute/issues/12860), [#12951](https://github.com/diegosouzapw/OmniRoute/pull/12951)) The dashboard reset-credit button (`consumeCodexResetCredit`) now exercises that same snapshot path after a successful redeem, so a filled quota bar is enough to unpark a leftover fallback Codex child without another manual clear. — thanks @HouMinXi +- **fix(db):** resolve `sql-wasm.wasm` across global npm install and hoisted layouts, ensuring OmniRoute can boot cleanly on Node 24 when native `better-sqlite3` is uncompiled. ([#13035](https://github.com/diegosouzapw/OmniRoute/pull/13035)) — thanks @HouMinXi +- **fix(api):** `DELETE /v1/batches/delete-completed` now sweeps only the calling API key's own completed batches (batches with no owner stay out of a key-scoped sweep on purpose), with an explicit instance-wide mode reserved for authenticated dashboard sessions, a 401 for a presented key that is unknown, revoked, deactivated, banned or expired (never falling through to the session branch), audit logging of every sweep, a sanitized 500 on failure, an owner-scoped file half (a key-scoped sweep never nulls a file another tenant owns) and an atomic sweep — chunked in 200-batch transactions in instance mode — so a mid-way error never leaves a batch pointing at a nulled file (GHSA-wvxc-jp3v-5mg5) ([#12969](https://github.com/diegosouzapw/OmniRoute/pull/12969)) +- **fix(combo):** quota-weighted routing stops drawing on an out-of-credit connection — a 402 now invalidates the stored quota snapshot instead of leaving its stale remaining percentage in place, and a snapshot older than 10 minutes no longer counts as confident headroom for the primary pool ([#12972](https://github.com/diegosouzapw/OmniRoute/pull/12972)) ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- **fix(vision):** Custom Models with "Vision capable" checked no longer have image requests swapped to `glm/glm-4.6v` when the client sends the advertised alias (`vllm/path/...`) or the bare path-shaped id — Vision Bridge now matches the stored override for all three id forms ([#12758](https://github.com/diegosouzapw/OmniRoute/issues/12758)) ([#12974](https://github.com/diegosouzapw/OmniRoute/pull/12974)) — thanks @HouMinXi +- **fix(sse):** transient opencode upstream failures rotate to the next account proxy instead of failing, so one flapping egress no longer aborts the whole chain ([#12975](https://github.com/diegosouzapw/OmniRoute/pull/12975)) — thanks @maxmad64bis +- **fix(azure):** Deployments from GPT-6 onward now send `max_completion_tokens` instead of `max_tokens`, which Azure rejects with HTTP 400. The rule matched a literal `gpt-5`, so each new generation arrived broken; it now matches the generation range, while `gpt-35-turbo` still keeps `max_tokens`. ([#13007](https://github.com/diegosouzapw/OmniRoute/pull/13007)) — thanks @ntdatt812 +- **fix(db):** add an opt-in self-restart circuit for sustained critical memory pressure, gate post-cleanup VACUUM behind a minimum freed-rows threshold, and checkpoint the SQLite WAL every 5 minutes with a size guard that escalates to TRUNCATE, so a growing WAL can no longer stall the event loop into a full outage. ([#13011](https://github.com/diegosouzapw/OmniRoute/pull/13011)) — thanks @HouMinXi +- **fix(auth):** an explicit connection pin may probe a quota-disabled row once and re-enable it on success ([#12874](https://github.com/diegosouzapw/OmniRoute/issues/12874)) ([#13017](https://github.com/diegosouzapw/OmniRoute/pull/13017)) — thanks @HouMinXi +- **fix(api):** thread `X-OmniRoute-Fallback-Attempts` through combo chat completions so streaming and non-streaming responses report how many prior legs were attempted ([#13038](https://github.com/diegosouzapw/OmniRoute/pull/13038)) — thanks @HouMinXi +- Restore provider failure classification and credential refresh on non-streaming requests: classify non-2xx failures to lock models on per-model quota exhaustion, update connection rate limits from headers and body, and pass credential refresh handlers to pipeline execution so 401 tokens can be refreshed and retried (#13043). ([#13069](https://github.com/diegosouzapw/OmniRoute/pull/13069)) — thanks @HouMinXi +- **fix(responses):** wrap forced-non-streaming web_search fallback JSON as Responses SSE so Codex still sees `response.completed` ([#13050](https://github.com/diegosouzapw/OmniRoute/pull/13050)) — thanks @HouMinXi +- **fix(antigravity):** Preserve upstream thought-token usage in normalized completion and reasoning token counts ([#13055](https://github.com/diegosouzapw/OmniRoute/pull/13055)) — thanks @pacocartones +- Fix a concurrency-slot leak in the DeepSeek PoW solver: a worker that failed to spawn (for example a missing worker script) never released its slot, so `MAX_CONCURRENT_WORKERS` failures disabled the solver until restart. ([#13097](https://github.com/diegosouzapw/OmniRoute/pull/13097)) — thanks @anhtahaylove +- **fix(acp):** bound the ACP session output buffers — `stdoutBuffer` and `stderrBuffer` now cap at 1 MiB keeping the most recent output behind a visible `[...output truncated...]` marker, and `stderrBuffer` is reset per prompt instead of accumulating for the lifetime of the session. ([#13100](https://github.com/diegosouzapw/OmniRoute/pull/13100)) — thanks @anhtahaylove +- **fix(acp):** release the `stdout`/`exit` listeners and the idle timer that a `sendPrompt` timeout used to leave attached to the `acpManager` singleton, and drop sessions that exited on their own from the session map instead of keeping them forever. ([#13096](https://github.com/diegosouzapw/OmniRoute/pull/13096)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection and PII scanners now read the text a `tool_result` block carries on `content` (string or nested block list), in messages and in system blocks, so tool output is judged by the same rules as user text ([#13101](https://github.com/diegosouzapw/OmniRoute/pull/13101)) — thanks @ntdatt812 +- **fix(gamification):** close the badge notification SSE stream when the request signal is already aborted before the stream starts — a client that disconnects while the route is still awaiting auth used to leave both the 2s unlock poll and the 15s heartbeat running for the lifetime of the process. ([#13106](https://github.com/diegosouzapw/OmniRoute/pull/13106)) — thanks @anhtahaylove +- **fix(security):** the prompt-injection scan now spends its 16 KB budget on both ends of the request instead of the first 16 KB only, so `system`, `instructions`, `query`, `documents` and the newest turns are no longer hidden behind one long message ([#13104](https://github.com/diegosouzapw/OmniRoute/pull/13104)) — thanks @ntdatt812 +- **fix(dashboard):** expose the Volcano Ark console cookie on quota scraping and unwrap connect-error objects so the dashboard shows the upstream message ([#13107](https://github.com/diegosouzapw/OmniRoute/pull/13107)) — thanks @HouMinXi +- **fix(db):** release the `beforeExit`/`SIGINT`/`SIGTERM` handlers when a `node:sqlite` adapter closes, so a closed adapter and its database handle are no longer pinned to `process` for the lifetime of the run — the same treatment #7494 gave the sql.js adapter. ([#13109](https://github.com/diegosouzapw/OmniRoute/pull/13109)) — thanks @anhtahaylove +- **fix(translator):** `contentSchema` and `unevaluatedItems` are now treated as subschema positions by the tool-schema sanitizer, so a truncation placeholder in either is replaced with a permissive schema instead of being forwarded as a string ([#13110](https://github.com/diegosouzapw/OmniRoute/pull/13110)) — thanks @ntdatt812 +- **fix(cli-helper):** clear the `createLogStream` timeout on the abort path — `stop()` aborts the in-flight fetch and returned through the `signal.aborted` branch, which skipped `clearTimeout` and left an armed timer per stopped stream. The stream reader is now also cancelled when the read loop exits early. ([#13114](https://github.com/diegosouzapw/OmniRoute/pull/13114)) — thanks @anhtahaylove +- **fix(docs):** drop TLS-impersonation, MITM-decrypt, supply-chain attestation, and XOR-mask writeups from the public `/docs` catalog and Docker image. Files stay in git for engineers; operators who need them open the repo, not the website. ([#13136](https://github.com/diegosouzapw/OmniRoute/pull/13136)) — thanks @HouMinXi +- **fix(combo):** parse numeric-epoch `rate_limited_until` in the combo cooldown read path ([#13141](https://github.com/diegosouzapw/OmniRoute/pull/13141)) — thanks @maxmad64bis +- **fix(opencode):** both OpenCode plugins now reject a gateway address typed without `http://` at configuration time, instead of publishing every model with an api url no client can call, and the v2 plugin no longer publishes a model card whose api url is blank or relative ([#13142](https://github.com/diegosouzapw/OmniRoute/pull/13142)) — thanks @maxmad64bis +- **fix(providers):** lock opencode model on upstream 400 model-unavailable ([#13146](https://github.com/diegosouzapw/OmniRoute/pull/13146)) — thanks @maxmad64bis +- **fix(logging):** keep the provider exchange rather than the raw client bodies when a call log exceeds its size budget, and show that recovered payload in the request-detail panel instead of replacing it with the stored response body ([#13147](https://github.com/diegosouzapw/OmniRoute/pull/13147)) — thanks @maxmad64bis +- **fix(traffic-inspector):** the WebSocket route no longer leaks a traffic-buffer subscriber and a 30s ping timer when the client socket is already closed at handler time — listeners are attached before any resource is acquired, a destroyed socket bails out early, and the ping interval stops on a dead socket where `write()` never throws ([#13155](https://github.com/diegosouzapw/OmniRoute/pull/13155)) — thanks @anhtahaylove +- **fix(telegram):** bound the per-user API key cache in the Telegram chat proxy so a burst of distinct chat ids can no longer grow the process heap without limit ([#13165](https://github.com/diegosouzapw/OmniRoute/issues/13165)) ([#13166](https://github.com/diegosouzapw/OmniRoute/pull/13166)) — thanks @anhtahaylove +- **fix(stream):** cancel the upstream response body when the JSON-to-SSE sniff unwinds on a body timeout, so a stalled upstream no longer pins the connection ([#13169](https://github.com/diegosouzapw/OmniRoute/issues/13169)) ([#13171](https://github.com/diegosouzapw/OmniRoute/pull/13171)) — thanks @anhtahaylove +- **fix(telegram):** authenticate webhook deliveries with Telegram's `secret_token` so an unauthenticated caller can no longer mint API keys or spend upstream quota ([#13172](https://github.com/diegosouzapw/OmniRoute/issues/13172)) ([#13175](https://github.com/diegosouzapw/OmniRoute/pull/13175)) — thanks @anhtahaylove +- **fix(models):** give discoverable Gemini 3.8 Flash ids their own 65536 output spec so Antigravity no longer clamps them to 16384 ([#13195](https://github.com/diegosouzapw/OmniRoute/pull/13195)) — thanks @HouMinXi +- **fix(dashboard):** leftover catalog-removed provider rows (gemini-cli) can be listed and purged from the providers page ([#13067](https://github.com/diegosouzapw/OmniRoute/issues/13067)) ([#13197](https://github.com/diegosouzapw/OmniRoute/pull/13197)) — thanks @HouMinXi +- **fix(auth):** a dashboard session now requires the `authenticated: true` claim that login, OIDC and the session refresh already emit — a JWT merely signed with `JWT_SECRET` (for example the Cursor CLI passthrough token, which any API-key holder can obtain) no longer verifies as the `auth_token` cookie on any route, the WebSocket handshake or the live server; existing sessions keep working ([#13298](https://github.com/diegosouzapw/OmniRoute/issues/13298)) ([#13375](https://github.com/diegosouzapw/OmniRoute/pull/13375)) +- **fix(api):** `DELETE /v1/batches/delete-completed` now applies the caller's API-key policy (endpoint allowlist, schedule, usage cap, rate limit) like every other `/v1` route, commits the key-scoped sweep per 200-batch chunk instead of holding one write lock for the whole sweep, refuses to loop without progress, and rejects a scope that names both a key and `allTenants` ([#13684](https://github.com/diegosouzapw/OmniRoute/pull/13684)) +- Restore the API-route typecheck gate: the non-streaming leg result lost its discriminated-union narrowing after the server-owned tool loop reassignment, producing 13 new TS2339 diagnostics in `chatCore.ts`. ([#12963](https://github.com/diegosouzapw/OmniRoute/pull/12963)) +- Restore four non-streaming behaviours the server-owned tool loop refactor dropped: client aborts map to 499 with the fixed `Request aborted` message, an aborted request no longer logs a synthetic `clientResponse`, Claude prompt-cache telemetry is recorded again, and a body that cannot be canonicalized no longer throws when the tool loop is off. ([#12990](https://github.com/diegosouzapw/OmniRoute/pull/12990)) +- **fix(skills):** The CLI registry parser now reads positionals declared with `.addArgument()`, not only those written inline in `.command()`. `tunnel create [type]` was being published as `tunnel create`, so the agent-skills sync gate reported drift on every branch and regenerating would have deleted the argument. ([#13009](https://github.com/diegosouzapw/OmniRoute/pull/13009)) — thanks @ntdatt812 +- **fix(combos):** dashboard combo test uses a short prompt, serial probes, and a 60s timeout so reasoning models and rate-limited free pools do not fail the health check ([#13001](https://github.com/diegosouzapw/OmniRoute/pull/13001)) — thanks @HouMinXi +- fix(compression): terminate idle worker threads on eviction so long-running instances stop leaking OS threads and MessagePorts ([#13091](https://github.com/diegosouzapw/OmniRoute/pull/13091)) — thanks @anhtahaylove +- Redact Google API keys of any length in error bodies: the pattern required exactly 39 characters, so shorter or longer `AIza…` credentials passed through unredacted. ([#12964](https://github.com/diegosouzapw/OmniRoute/pull/12964)) +- **fix(grok-cli):** a 402 "Grok Build usage balance exhausted" parks that Grok login as out of credit (Grok Build CLI, grok.com cookie, and xAI OAuth share the weekly pool). Combo routing then tries the next login instead of locking the model for every account in the pool ([#13061](https://github.com/diegosouzapw/OmniRoute/pull/13061)) — thanks @HouMinXi +- fix(compression): spawn the LLMLingua worker with a file URL object so compression actually runs instead of silently failing open on Node ([#13093](https://github.com/diegosouzapw/OmniRoute/pull/13093)) — thanks @anhtahaylove +- **fix(test):** run the local `test` and `test:unit` scripts at concurrency 4 so a full-suite run no longer exhausts the machine's commit charge and kills unrelated processes ([#13187](https://github.com/diegosouzapw/OmniRoute/pull/13187)) — thanks @anhtahaylove +- **fix(monitoring):** `GET /api/monitoring/health` `credentialHealth` now includes a bounded `failedConnections` list (`connectionId`, `status`, sanitized `lastError`) when the probe-cache gauge `failed>0`, plus `source: probe-cache` and a cheap `staleDbNonOkCount` for sticky SQLite `test_status` on active rows. Documents that the live gauge is not `provider_connections.test_status`. ([#12876](https://github.com/diegosouzapw/OmniRoute/pull/12876)) — thanks @RaviTharuma +- **fix(resilience):** an openai-compatible multi-upstream gateway no longer marks the whole connection `credits_exhausted` when a single upstream (or the `/models` / representative-model health probe) returns 402. Quota failures stay model-scoped; true connection-wide auth failures are unchanged. Single-credential 402 key disable ([#5239](https://github.com/diegosouzapw/OmniRoute/issues/5239)) is preserved. ([#12875](https://github.com/diegosouzapw/OmniRoute/pull/12875)) — thanks @RaviTharuma +- **fix(dashboard):** nine fixes on the Orchestration Canvas, all diagnosed in the Phase 2 reviews. A source that fails now keeps the timestamp of its FIRST failure instead of being re-stamped every poll — the stale line said "since the last poll" no matter how long the source had been down, and the churn also defeated the snapshot's stable identity (it serializes the source list), so the canvas re-rendered on every tick while anything was broken. A source that HAD data and only then started failing is flagged too: previously only a source with no node at all got the warning, so a source that went down mid-session kept a healthy-looking node forever. The rest are pointwise: clicking a filter chip cancels the pending search debounce (left armed, it fired ~300ms later and silently reverted the chip); the search input carries an accessible name; `?state=running, failed` parses like the unpadded form instead of dropping the padded value; the CSV toggle helper is defined once in `model/urlParams.ts` rather than twice; the agents tab tells "nothing running" apart from "the filter matched nothing", offering a clear-filters button instead of setup links that would be wrong advice there; edges stop emitting SMIL particles above 40 simultaneously active edges, keeping the colored stroke; and the drawer's error banner clears when a retried action succeeds. ([#12983](https://github.com/diegosouzapw/OmniRoute/pull/12983)) +- **fix(i18n):** the nine locales added with the EU-language batch (Greek, Estonian, Irish, Croatian, Lithuanian, Latvian, Maltese, Slovenian, Serbian) were missing the eleven Orchestration Canvas keys that Phase 3 introduced, so the compare-runs panel and the "no runs match these filters" empty state fell back to English in those languages (`deepMergeFallback` substitutes English for an absent key, so nothing rendered blank — it rendered untranslated). The coverage gate does not catch this: it enforces an 80% floor per locale, and eleven missing keys out of ~13,000 leaves coverage at 99.9%. Translated for real in each language, calibrated against the wording each file already uses for "run", "filter" and "skill". ([#13294](https://github.com/diegosouzapw/OmniRoute/pull/13294)) +- **fix(dashboard):** five follow-ups from the Phase 2 Orchestration Canvas review. The History drawer now shows the memory section for runs that already left the live TTL window: the persisted `memory_hits` event is parsed into `metadata.memoryHits` with the same defensive validation the drawer applies, and — because that event is observability rather than a state transition — it no longer leaks into the timeline, where it had been inheriting the task's state and rendering as a duplicate transition. The A2A memory recall runs against its own 1.5s deadline instead of inheriting the memory backend's 30s one; overshooting degrades exactly like any other recall failure (no hits, task proceeds), and the timer is cleared on both paths. Repeating a Conductor run carries its `requirements.cli`/`requirements.model` forward, so the new run is pinned to the same runner profile and model rather than drifting to whatever the fleet picks. A successful repeat from the Agents tab now focuses the run it created, instead of leaving the operator on the finished one. And the auth test for `POST /api/conductor/tasks` moved into the shared `ROUTES` array rather than restating the pattern. ([#12988](https://github.com/diegosouzapw/OmniRoute/pull/12988)) +- fix(plugins): stop leaking an exit listener per plugin hook timeout, which triggered MaxListenersExceededWarning on plugins that ignore SIGTERM ([#13092](https://github.com/diegosouzapw/OmniRoute/pull/13092)) — thanks @anhtahaylove +- **fix(validation):** Provider node edits no longer fail with a generic "Invalid request" when the optional daily-quota reset fields are left blank. The dashboard sends `dailyQuotaResetTimezone` and `dailyQuotaResetHour` as `null`, and only the hour accepted it. ([#13066](https://github.com/diegosouzapw/OmniRoute/issues/13066)) ([#13083](https://github.com/diegosouzapw/OmniRoute/pull/13083)) — thanks @ntdatt812 +- **fix(combo):** quota-aware expansion drops banned, inactive, missing, and wrong-provider connections before quota fetch or model dispatch; pins and allowlists stay selectors, not a bypass. Antigravity automatic exhaustion now requires a reported zero remaining, so a positive balance below 1% stays eligible. ([#13006](https://github.com/diegosouzapw/OmniRoute/pull/13006)) — thanks @HouMinXi +- Fix an intermittent failure in the quota-weighted routing test suite: scores are a function of `Date.now()`, so peers with identical quota scored microseconds apart never tied and swapped order. ([#12946](https://github.com/diegosouzapw/OmniRoute/pull/12946)) +- fix(volcengine): resolve quota window mapping gaps, safe multi-connection console binding, and response cookie sanitization ([#12950](https://github.com/diegosouzapw/OmniRoute/pull/12950)) — thanks @HouMinXi +- **fix(test):** resolve the WebDAV handler path with `fileURLToPath` so the suite's 37 WebDAV tests run on Windows instead of failing with a doubled `C:\C:\` drive prefix ([#13196](https://github.com/diegosouzapw/OmniRoute/pull/13196)) — thanks @anhtahaylove +- **fix(ui):** fall back from invalid AnySearch icon alias (direct commit `9587e07b69`) +- **fix(sse):** stop rebuilding a truncated summary from the collector's cap-dropped event array ([#12718](https://github.com/diegosouzapw/OmniRoute/pull/12718)) — thanks @hartmark +- **fix(conversations):** show a pending spinner for unresolved tool nodes instead of "(empty)" ([#12727](https://github.com/diegosouzapw/OmniRoute/pull/12727)) — thanks @hartmark +- **fix(streaming):** fail fast when an upstream stream produces only lifecycle/heartbeat events, never real content ([#12741](https://github.com/diegosouzapw/OmniRoute/pull/12741)) — thanks @hartmark +- **fix(responses-continuation):** bridge the write-in-flight window with an in-memory pending store ([#12854](https://github.com/diegosouzapw/OmniRoute/pull/12854)) — thanks @hartmark +- **fix(translator):** drop or map agent_message on Chat Completions fallback ([#12880](https://github.com/diegosouzapw/OmniRoute/pull/12880)) — thanks @RaviTharuma +- **fix(api):** page and stream-complete GET /v1/models for large catalogs ([#12882](https://github.com/diegosouzapw/OmniRoute/pull/12882)) — thanks @RaviTharuma +- **fix(quota):** align AUTH window labels with usage API and clarify 503 ([#12884](https://github.com/diegosouzapw/OmniRoute/pull/12884)) — thanks @RaviTharuma +- **fix(ci):** clear two base-reds on release/v3.8.51 — mutation-coverage gate + image-only-model guard ([#12945](https://github.com/diegosouzapw/OmniRoute/pull/12945)) +- **fix(api):** scope batch bulk-delete to the calling API key ([#13211](https://github.com/diegosouzapw/OmniRoute/pull/13211)) +- **fix(tests):** align model-sync log assertion to shipped path redaction (base-red) ([#13227](https://github.com/diegosouzapw/OmniRoute/pull/13227)) +- **fix(test):** make the ToS heading guard actually require the parentheses ([#13228](https://github.com/diegosouzapw/OmniRoute/pull/13228)) +- **fix(quality):** register 4 drifted covering tests in stryker tap.testFiles ([#13229](https://github.com/diegosouzapw/OmniRoute/pull/13229)) +- **fix(logging):** mask *-api-key headers in request-log pipeline (#13273) ([#13401](https://github.com/diegosouzapw/OmniRoute/pull/13401)) — thanks @KooshaPari +- **fix(tests):** correct stale Jina catalog prefix in custom-models test ([#13403](https://github.com/diegosouzapw/OmniRoute/pull/13403)) — thanks @KooshaPari +- **fix(backend):** prune old backups after health-check-repair snapshots (#13308) ([#13404](https://github.com/diegosouzapw/OmniRoute/pull/13404)) — thanks @KooshaPari +- **fix(backend):** guard memory_fts_au trigger with WHEN clause to stop FTS bloat (#13326) ([#13405](https://github.com/diegosouzapw/OmniRoute/pull/13405)) — thanks @KooshaPari +- **fix(free-tiers):** remove obsolete Together $25 signup credit (#12526) ([#13407](https://github.com/diegosouzapw/OmniRoute/pull/13407)) — thanks @KooshaPari +- **fix:** skip non-chat generation models in Test all models ([#13409](https://github.com/diegosouzapw/OmniRoute/pull/13409)) — thanks @KooshaPari +- **fix(providers):** de-duplicate agy and antigravity model catalogs ([#13410](https://github.com/diegosouzapw/OmniRoute/pull/13410)) — thanks @KooshaPari +- **fix(dashboard):** send provider in body for mini-playground web search ([#13411](https://github.com/diegosouzapw/OmniRoute/pull/13411)) — thanks @KooshaPari +- **fix(resilience):** add CJK quota-exhaustion patterns to 429 classifier ([#13412](https://github.com/diegosouzapw/OmniRoute/pull/13412)) — thanks @KooshaPari +- **fix(backend):** eval runner: failed calls no longer score as passed + dotAll regex ([#13413](https://github.com/diegosouzapw/OmniRoute/pull/13413)) — thanks @KooshaPari +- **fix(backend):** eval runner opts out of output-style and memory injection ([#13414](https://github.com/diegosouzapw/OmniRoute/pull/13414)) — thanks @KooshaPari +- **fix:** chain serial quarantine step in npm test script (fixes #13286) ([#13418](https://github.com/diegosouzapw/OmniRoute/pull/13418)) — thanks @KooshaPari +- **fix(antigravity):** derive stable session ID from credentials for prompt-cache reuse (#13305) ([#13424](https://github.com/diegosouzapw/OmniRoute/pull/13424)) — thanks @KooshaPari +- **fix(memory):** merge memory into top-level system field instead of unshifting at messages[0] (#13425) ([#13427](https://github.com/diegosouzapw/OmniRoute/pull/13427)) — thanks @KooshaPari +- **fix(compression):** preserve instruction blocks from lossy engines ([#13523](https://github.com/diegosouzapw/OmniRoute/pull/13523)) — thanks @KooshaPari +- **fix(compression):** preserve polarity words and newlines in ultra heuristic ([#13524](https://github.com/diegosouzapw/OmniRoute/pull/13524)) — thanks @KooshaPari +- **fix(compression):** run user preservePatterns before built-in patterns (#13457) ([#13525](https://github.com/diegosouzapw/OmniRoute/pull/13525)) — thanks @KooshaPari +- **fix(reasoning):** expand deepseek-v4 regex to match v4.1+ and add command-code provider (#13430) ([#13528](https://github.com/diegosouzapw/OmniRoute/pull/13528)) — thanks @KooshaPari +- **fix(providers):** add alwaysReasons metadata for reka-flash-3 ([#13531](https://github.com/diegosouzapw/OmniRoute/pull/13531)) — thanks @KooshaPari +- **fix(playground):** allow Compare columns to scroll independently ([#13532](https://github.com/diegosouzapw/OmniRoute/pull/13532)) — thanks @KooshaPari +- **fix(security):** empty whitelist allows through to prevent admin lockout ([#13534](https://github.com/diegosouzapw/OmniRoute/pull/13534)) — thanks @KooshaPari +- **fix(tests):** add DISABLE_IOREG_STRATEGY env var for macOS test compatibility (fixes #13467) ([#13539](https://github.com/diegosouzapw/OmniRoute/pull/13539)) — thanks @KooshaPari +- **fix(zed-hosted):** map developer role and Gemini enums for Zed proxy ([#13543](https://github.com/diegosouzapw/OmniRoute/pull/13543)) — thanks @KooshaPari +- **fix(analytics):** normalize provider aliases to canonical id in usage_history ([#13545](https://github.com/diegosouzapw/OmniRoute/pull/13545)) — thanks @KooshaPari +- **fix(combo):** use traceId for call log id to prevent UNIQUE constraint failure ([#13546](https://github.com/diegosouzapw/OmniRoute/pull/13546)) — thanks @KooshaPari +- **fix(auth):** check model lockout before returning synthetic noauth connection ([#13547](https://github.com/diegosouzapw/OmniRoute/pull/13547)) — thanks @KooshaPari +- **fix(guardrails):** skip credential redaction for base64 image data URLs ([#13550](https://github.com/diegosouzapw/OmniRoute/pull/13550)) — thanks @KooshaPari +- **fix(resilience):** increase requestQueue.maxWaitMs default from 15s to 30s ([#13553](https://github.com/diegosouzapw/OmniRoute/pull/13553)) — thanks @KooshaPari +- **fix(docs):** document OMNIROUTE_READY_TIMEOUT_MS and allowlist the test-only DISABLE_IOREG_STRATEGY ([#13692](https://github.com/diegosouzapw/OmniRoute/pull/13692)) + ### 📝 Maintenance ---- +- **docs(readme):** reconcile live v3.8.50 provider, free-tier, CLI, routing, test, community, sponsor, acknowledgment, and SVG metrics with their audited source denominators, including a deduplicated OmniRoute-in-Action snapshot and distinct contributor rankings for merged pull requests, GitHub-attributed commits, and Git history ([#11356](https://github.com/diegosouzapw/OmniRoute/pull/11356)). ([#11434](https://github.com/diegosouzapw/OmniRoute/pull/11434)) — thanks @hartmark +- **docs:** sync the canonical provider count 354 → 356 across `README.md`, `AGENTS.md`, `llm.txt` (+ 42 i18n mirrors), the four README SVG diagrams, `docs/reference/PROVIDER_REFERENCE.md` (regenerated) and the `package.json` description after Opper (#11629) and 1min.ai (#11631) boarded the catalog — closes the `check:docs-counts-sync` strict drifts that kept `release/v3.8.51` red ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11673](https://github.com/diegosouzapw/OmniRoute/pull/11673)) — thanks @jonlwheat2-gif +- **test(providers):** update count-derived assertions after the v3.8.51 provider additions — `APIKEY_PROVIDERS` 233 → 235 (Opper #11629 + 1min.ai #11631), reserved-prefix REGISTRY walk 395 → 398, `WEB_FETCH_PROVIDERS` now includes `nimble-search` (#11620), and the provider translate-path golden snapshot regenerated ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)). ([#11674](https://github.com/diegosouzapw/OmniRoute/pull/11674)) — thanks @jonlwheat2-gif +- **test(sse):** bump the hard-lease connection-query inventory for `src/lib/tokenHealthCheck.ts` to 2 — the verify-only web-cookie sweep added by #11495 split the single `getProviderConnections` call into oauth + cookie variants, which the frozen inventory had not tracked ([#11495](https://github.com/diegosouzapw/OmniRoute/pull/11495)). ([#11677](https://github.com/diegosouzapw/OmniRoute/pull/11677)) — thanks @jonlwheat2-gif / @oyi77 +- **chore(release):** clear the `release/v3.8.51` base-red gates — docs migration count (+38 i18n mirrors), stryker `tap.testFiles`, lockfile registry host, stale ESLint suppressions, the 7 lint regressions they were hiding, the stale `APIKEY_PROVIDERS` partition count (231 → 233), the `glm-5.3-max` tier missing from the GLM test table, a dead `modelType === "chat"` comparison in the models catalog, and the `resolveLiveWsUrl` / `sanitizeLiveWsPort` helpers from #11388 that a later merge dropped from `wsPath.ts` (dashboard live WebSocket URL resolution) ([#11502](https://github.com/diegosouzapw/OmniRoute/pull/11502)) — thanks @MumuTW +- **chore(free-models):** the free-tier regime table now declares which totals figure each regime feeds and whether it may take the no-auth shortcut, so the sets that used to repeat those answers by hand are derived from it — a new `freeType` no longer compiles until it has answered every question, instead of silently contributing to no total ([#11537](https://github.com/diegosouzapw/OmniRoute/pull/11537)) — thanks @maxmad64bis +- **docs(routing):** LKGP strategy copy now states provider-level stickiness and the explicit rules fallback instead of promising per-target pinning ([#11555](https://github.com/diegosouzapw/OmniRoute/pull/11555)) — thanks @maxmad64bis +- **test(chatcore):** pin the response tool-name alias resolution against the ordering hazard that broke every Gemini/Antigravity MCP tool call in v3.8.49. `extractRequestToolIdentityMap` deletes `translatedBody._toolNameMap`, so the later read is always undefined and the ledger survives only through the `requestToolIdentityMap` fallback — removing that fallback previously left the entire tool-name suite green. The resolution moves into `resolveResponseToolNameMap()` next to the map it depends on, with a regression guard that fails without the recovery. No behaviour change. ([#11575](https://github.com/diegosouzapw/OmniRoute/pull/11575)) — thanks @amartinawi +- **docs(env):** document `OMNIROUTE_CORPUS_CACHE_SIZE` in `.env.example` and `ENVIRONMENT.md`, so the env/docs contract check stops failing Docs Gates on every branch ([#11576](https://github.com/diegosouzapw/OmniRoute/pull/11576)) — thanks @ntdatt812 +- **chore(stryker):** register `route-guard-tunnel-processes-local-only` and `universal-quota-aware-routing` in `tap.testFiles`, so their mutant kills count for the three modules they cover ([#11577](https://github.com/diegosouzapw/OmniRoute/pull/11577)) — thanks @ntdatt812 +- **test(authz):** pin `LOCAL_ONLY_API_GET_EXEMPTIONS` by exact membership instead of by entry count, so the guard names the offending path and also catches a substitution ([#11580](https://github.com/diegosouzapw/OmniRoute/pull/11580)) — thanks @ntdatt812 +- **test(build):** pin `resolveNextBuildBundlerFlag` to the `OMNIROUTE_USE_TURBOPACK` contract it implements, across runtimes, instead of asserting an unimplemented Bun override ([#11583](https://github.com/diegosouzapw/OmniRoute/pull/11583)) — thanks @ntdatt812 +- **test(claude-web):** the Claude Web transport test's barrel anchors no longer pin the exact construction spelling. #11421 made the executor map lazy, so `/"claude-web": new ClaudeWebExecutor\(\)/` stopped matching and the test went red on `release/v3.8.51` itself — a base-red gate every open PR inherited. The anchors now match the alias key and the class name and tolerate either spelling in between, and were checked to still fail when an alias is genuinely repointed at another executor ([#11585](https://github.com/diegosouzapw/OmniRoute/pull/11585)) — thanks @ntdat812 +- **test(models):** pin `qwen3.8-max` resolution per provider — two of the four catalogs now serve the bare id and no longer carry `-preview` ([#11590](https://github.com/diegosouzapw/OmniRoute/pull/11590)) — thanks @ntdatt812 +- **test(executors):** drop the stale 128-tool truncation assertion that contradicted the #11444 pass-through contract ([#11591](https://github.com/diegosouzapw/OmniRoute/pull/11591)) — thanks @ntdatt812 +- **test(token-health):** pin the current terminal-skip boundary — expired with retry budget is probed, `account_deactivated` stays skipped ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)) — thanks @ntdatt812 +- **test(models):** follow #11503's retarget of the `gemini-3-pro-high` alias to the hyphenated catalog id ([#11593](https://github.com/diegosouzapw/OmniRoute/pull/11593)) — thanks @ntdatt812 +- **refactor(video bridge):** extract per-video acquisition, whole-result caching, description, and metrics/abort/cleanup out of `VideoBridgeGuardrail.preCall` into a `processVideoPart` seam in a new `videoBridgePipeline.ts`, behind explicit `VideoMediaBrokerPort`, `VideoAudioTranscriptionPort`, and `VideoDrilldownPort` boundaries; `preCall` now only handles request traversal, policy, and response aggregation. The Video tab's FFmpeg/ffprobe runtime status is now an explicit `unknown` / `restricted` / `unavailable` / `available` state instead of a nullable boolean pair, fixing a case where an in-flight or failed probe was mislabeled as "install FFmpeg" ([#11657](https://github.com/diegosouzapw/OmniRoute/issues/11657)). ([#12016](https://github.com/diegosouzapw/OmniRoute/pull/12016)) +- **test(opencode-plugin):** add unit test coverage for `formatFreeBudget()` naming helper ([#11660](https://github.com/diegosouzapw/OmniRoute/pull/11660)) — thanks @f9td56dbgh-hub +- **docs(video):** clarify that the Video Bridge transcript `source` field (`client`, `embedded`, `audio-bridge`) is presently caller-declared and not yet server-verified — OmniRoute enforces the enum shape but does not cryptographically confirm that an `embedded`/`audio-bridge` label came from a server-owned extraction ([#11661](https://github.com/diegosouzapw/OmniRoute/issues/11661)). ([#12001](https://github.com/diegosouzapw/OmniRoute/pull/12001)) +- **chore(providers):** retire the distributed Raycast Relay and Hailuo Web integrations after a provenance audit confirmed substantial derivation from GPL-3.0 sources; the official GitHub and MiniMax API providers remain available. Migration 163 disables legacy connections and invalidates their active leases while preserving connection IDs, audit/history rows, quota snapshots, usage records, and fail-closed API-key connection policies. ([#11691](https://github.com/diegosouzapw/OmniRoute/pull/11691)) +- **chore(providers):** retire the Felo Web provider while its GPL-derived provenance remains on licensing HOLD ([#11698](https://github.com/diegosouzapw/OmniRoute/pull/11698)) — stale runtime and database references now fail closed while existing connection, lease, and usage history remains preserved for auditability. +- **fix(sse):** retire the Gemini Web image-generation bridge while provenance and licensing remain on HOLD, preserving Gemini Web chat/auth/tools and legitimate Gemini/Nano Banana image providers ([#11708](https://github.com/diegosouzapw/OmniRoute/pull/11708)) +- **chore(audio):** retire the built-in EdgeTTS provider and its 11-voice catalog while provenance/licensing review remains on HOLD; the generic `/v1/audio/speech` endpoint and the other speech providers remain available ([#11711](https://github.com/diegosouzapw/OmniRoute/pull/11711)) +- **fix(providers):** retire the unofficial Qwen Web integration and its `qwen-web`/`qw` identities while third-party provenance remains on HOLD ([#11713](https://github.com/diegosouzapw/OmniRoute/pull/11713)) — direct requests now fail closed with a sanitized HTTP 410 and combos skip the retired target, while migration guards preserve connection IDs, allowlists, usage/audit history and invalidate active leases. Supported Qwen API, Qwen Code, cloud, token-plan, audio, local and model-name surfaces remain available; restoring Qwen Web requires a qualified provenance/license decision. +- **fix(providers):** retire `microsoft-designer-web` and its `msdesigner` alias from runtime and catalogs while preserving stored credentials, provider history and allowlists as inactive records — source dependency on gpt4free's [`MicrosoftDesigner.py`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/g4f/Provider/needs_auth/MicrosoftDesigner.py) is proven at snapshot `0943d16eef757a44deb60651c07bbe5fa958cafe`, whose [`LICENSE`](https://github.com/xtekky/gpt4free/blob/0943d16eef757a44deb60651c07bbe5fa958cafe/LICENSE) is GPL-3.0; expressive adaptation is probable, which records provenance/licensing risk without asserting a legal conclusion ([#11720](https://github.com/diegosouzapw/OmniRoute/pull/11720)). +- **docs(legal):** centralize pinned MIT notices for the vendored GCF codec and local flag assets ([#11726](https://github.com/diegosouzapw/OmniRoute/pull/11726)) +- Split the 2,952-line `adobeFireflyClient.ts` into nine focused modules (catalog, credentials, payload, ARP session, upload, responses, IMS, account, poll) behind an unchanged barrel — no consumer edits and no public API change (#11728). — thanks @MumuTW +- **fix(dashboard):** replaced the Model Lockout card's unprovenanced notification MP3 with a short, locally synthesized Web Audio chime ([#11731](https://github.com/diegosouzapw/OmniRoute/pull/11731)) — both toggles retain optional audible feedback without shipping replacement media or third-party code, and unsupported, suspended, or rejected audio contexts fail open without blocking the setting change. +- **chore(deepseek-web):** remove the provenance-unresolved DeepSeek PoW WASM binary and its runtime loader/tracing while retaining the existing JavaScript solver (slower at high difficulty) ([#11732](https://github.com/diegosouzapw/OmniRoute/pull/11732)). +- **chore(assets):** remove four unused provider assets whose redistribution provenance could not be verified while retaining the active Freebuff SVG ([#11735](https://github.com/diegosouzapw/OmniRoute/pull/11735)) +- **docs(assets):** retired two raster images whose redistribution provenance could not be verified ([#11736](https://github.com/diegosouzapw/OmniRoute/pull/11736)) — PicoClaw documentation now reuses the existing SVG, while Zoo Code remains a text link without an image. +- **docs(legal):** add pinned notices for six LobeHub-derived provider SVGs and 65 byte-exact theSVG assets, retaining registry claims as unverified metadata with explicit trademark and HOLD boundaries ([#11737](https://github.com/diegosouzapw/OmniRoute/pull/11737)). +- **chore(quality):** added a versioned 225/225 provider-asset provenance manifest and blocking drift gate ([#11743](https://github.com/diegosouzapw/OmniRoute/pull/11743)) — the inventory pins magic MIME and SHA-256 for every physical file, records immutable source matches for 71 assets, keeps 69 probable and 85 unresolved entries explicit but non-blocking, and treats provenance as source evidence rather than copyright or trademark clearance. +- **refactor(mitm):** replace four previously derived Traffic Inspector utilities — conversation normalization, SSE merging, secret masking, and header sanitization — with independent clean-room implementations based on public provider schemas, WHATWG SSE, and RFC 6750, while keeping the two independently classified passthrough surfaces unchanged ([#11748](https://github.com/diegosouzapw/OmniRoute/pull/11748)). +- Added a unit test that fails seven days before any dated pack under `config/` (`validUntil` and sibling keys) lapses, naming the file and key. The Alibaba free-tier pack expired on 2026-08-27 and turned every PR red the next morning with no commit involved; renewal now happens on someone's terms, not the clock's. ([#11891](https://github.com/diegosouzapw/OmniRoute/pull/11891)) +- `check:workflows` now fails (under `--strict`/`--ratchet`) when any job routed to a self-hosted runner publishes with `--provenance` — npm rejects that with `422` at the registry, which in v3.8.50 only surfaced after the tag and Docker images were public. ([#11895](https://github.com/diegosouzapw/OmniRoute/pull/11895)) +- `scripts/ops/runner-janitor.sh` now proves a path is idle with one `lsof` snapshot and removes stale leftovers itself (tmpfs after 3 h — it is RAM — disk after 24 h), kills orphan `next-build` processes, prunes checkouts of stopped runners, and alerts on memory pressure; `--dry-run` shows exactly what it would do. `docs/ops/RUNNER_BOX.md` reconciled to the measured box (31 GB, 10 listeners). ([#11893](https://github.com/diegosouzapw/OmniRoute/pull/11893)) +- The `next-build` artefact (1.3 GB) is now written and read under `$RUNNER_TEMP` (per-runner, on disk) instead of `/tmp`, which on the self-hosted pool is a 12 GB tmpfs in RAM. Landing it there took 27–32 of the publish job's 76 minutes, and the fixed `/tmp/e2e-build.tar.gz` name let E2E jobs on different runners overwrite each other's download. ([#11896](https://github.com/diegosouzapw/OmniRoute/pull/11896)) +- The CI `build` job now runs in two concurrency lanes — `main` and pull requests — so a release build is never queued behind (or OOM-killed beside) PR builds on the self-hosted pool, which holds one `next-build` comfortably and two at the edge. ([#11901](https://github.com/diegosouzapw/OmniRoute/pull/11901)) +- Re-freeze the ESLint suppressions on `release/v3.8.51` from a clean-room measurement (2 stale file entries pruned, 55 pre-existing `no-explicit-any` in six new files frozen under #11924) and drop the dead `GPT_SIZE_MAP` constant orphaned by the Adobe Firefly client split, so `No new ESLint warnings` stops failing every PR with exit 2 (Refs #11924) ([#11955](https://github.com/diegosouzapw/OmniRoute/pull/11955)) +- Type the 55 `no-explicit-any` sites that had been frozen under #11924 — four redundant casts in `socksConnectorWithFamily.ts` (undici/socks types already accept them) and the mocks/fixtures of the socks-timeout and isFree suites — and drop their suppression entries; the ESLint ratchet shrinks from 5487 to 5432 (Closes #11924) ([#11975](https://github.com/diegosouzapw/OmniRoute/pull/11975)) +- Take the two hosted-runner builds off the PR rail: `Build App` (`build.yml`) is `workflow_dispatch`-only and `DAST smoke (PR)` runs only for PRs into `main` — the 7 GB hosted VM cannot build `release/v3.8.51` in any profile (19/30 red, VM shutdown ~8 min into `next build`) and both checks had turned into permanent noise on every release PR; the bundle stays validated by `ci.yml` on `main` and by `nightly-release-green` on `release/**` (Closes #11946) ([#11962](https://github.com/diegosouzapw/OmniRoute/pull/11962)) +- Move the four nightly jobs that build the backend (`nightly-schemathesis`, `nightly-llm-security` promptfoo + garak, `nightly-resilience` axe-a11y) off the hosted 7 GB runner — where they died on `release/v3.8.51` unseen — onto the box's new `omni-light` pool (two listeners), and document the reshaped fleet (4 active OmniRoute listeners: 2 `omni-build` + 2 `omni-light`, janitor ceiling 4) (Closes #11965) ([#11967](https://github.com/diegosouzapw/OmniRoute/pull/11967)) +- Stop painting every fork PR into `release/**` red: `quality.yml` `Build (advisory)` is skipped (GitHub still reports `continue-on-error` failures as check FAILURE). Hosted `ubuntu-latest` cannot finish `npm run build` on this tree — same class as #11962 taking `build.yml` off the PR rail. `docker-publish.yml` amd64 now runs on the `.113` `omni-build` pool (31 GB / 32 cores, two listeners) with Turbopack, shares the `heavy-build-main` lane with `ci.yml` `Build` so it queues instead of becoming a third heavy, and keeps per-ref concurrency (a merge storm was starting 8 concurrent OOM builds). arm64 stays on `ubuntu-24.04-arm` with webpack — there is no ARM box. Fallback when `USE_VPS_RUNNER` is off: hosted amd64 + webpack (#11976). ([#12021](https://github.com/diegosouzapw/OmniRoute/pull/12021)) +- `docker-publish.yml` builds with webpack (`OMNIROUTE_USE_TURBOPACK=0`) on **both** arches, including amd64 on the `.113` `omni-build` pool. Turbopack had the RAM (31 GB, `omniroute-113-6`) and still panicked (`TurbopackInternalError: there must be a path to a root`, run 33253576569, 26 min in). The same tree's arm64 webpack build on hosted `ubuntu-24.04-arm` succeeded (run 33264823398). Dockerfile already documents webpack as the Docker escape hatch (#11976). ([#12050](https://github.com/diegosouzapw/OmniRoute/pull/12050)) +- **docs(free-tier):** declare the counting vs deciding regimes for "is it free?" and guard the deciding path from DB-backed catalog resolution ([#12226](https://github.com/diegosouzapw/OmniRoute/pull/12226)) — thanks @maxmad64bis +- **docs(auto-combo):** The mode pack table in `docs/routing/AUTO-COMBO.md` now lists all six shipped packs with every weight each one sets, replacing a four-pack table whose numbers had also drifted from the source. It states plainly that no pack sets `quality`, so selecting any pack silences the observed-quality signal. Six more documents that quote the scoring factor count joined the `check:docs-counts` gate, which caught five stale claims — including one naming nine factors that do not exist — and two stale mode pack counts. The dashboard routing panel, which offered four of the six packs and labelled the default strategy "6-Factor Scoring", is now covered by a test; the two packs it was missing are `reliability-first` and `chaos-mode`, the latter labelled as the fault-injection profile it is rather than as one more routing preference ([#12316](https://github.com/diegosouzapw/OmniRoute/pull/12316)) — thanks @maxmad64bis +- **chore(quality):** rebaseline `src/lib/db/apiKeys.ts` for the ACL the key-creation path now preserves ([#12352](https://github.com/diegosouzapw/OmniRoute/pull/12352)) ([#12673](https://github.com/diegosouzapw/OmniRoute/pull/12673)) — thanks @KrzysiekSko +- **chore(providers):** bump the Claude Code wire identity and the Devin bridge image pin from `2.1.220` to `2.1.258` ([#12402](https://github.com/diegosouzapw/OmniRoute/pull/12402)) — thanks @ggiak ([#12604](https://github.com/diegosouzapw/OmniRoute/pull/12604)) +- **docs(env):** align `.env.example`, the README Bun section, and the troubleshooting guide with the code: `OMNIROUTE_USE_TURBOPACK` also governs `npm run build` (not dev-only), `bun run build` follows that flag instead of auto-selecting Webpack, `OMNIROUTE_CHAT_MAX_HEAVY_IN_FLIGHT` is unset by default (no request-count cap), and the structural `503 chat_admission_busy` message matches `chatAdmissionResponses.ts` (#12404 — thanks @pacocartones) +- **chore(electron):** upgrade the desktop app to Electron 44 (Chromium 152, Node 24.18.1) ([#12217](https://github.com/diegosouzapw/OmniRoute/pull/12217)). **Requires macOS 13 (Ventura) or later** — Chromium dropped macOS 12 (Monterey), so Monterey users must stay on an earlier OmniRoute desktop build. Windows and Linux are unaffected; the app already shipped only x64/arm64, so Electron 44 dropping 32-bit builds changes nothing. Removes the `openAsHidden`/`wasOpenedAsHidden` login-item fields deleted in Electron 44 — hidden autostart continues to work through the `--hidden` argument registered with the login item ([#12554](https://github.com/diegosouzapw/OmniRoute/pull/12554)) — thanks @app/dependabot +- **chore(quality):** rebaseline `src/sse/handlers/chat.ts` for the effective-input persistence the continuation fix needs ([#12641](https://github.com/diegosouzapw/OmniRoute/pull/12641)) ([#12680](https://github.com/diegosouzapw/OmniRoute/pull/12680)) — thanks @hartmark +- `Coverage` job on `ci.yml`: the informational Codecov upload gets its own 5-minute ceiling and `continue-on-error`, and the job budget grows from 20 to 30 minutes (the 8-shard c8 merge alone takes ~10) — a stalled upload no longer ends the job `cancelled` and drags a fully green `main` run's conclusion down with it ([#11972](https://github.com/diegosouzapw/OmniRoute/pull/11972)) +- CI hardening on the PR rail: the four `Unit Tests fast-path` shards get `timeout-minutes: 30` (a hung shard held a PR for 64 min instead of GitHub's 6 h default) and both ESLint file caches lose their `restore-keys` fallback, so a cache built under another suppressions file or lint config can no longer report stale verdicts (Refs #11600, #11924) ([#11963](https://github.com/diegosouzapw/OmniRoute/pull/11963)) +- Every CI job that runs a `next build` (`build`, the npm `publish`, both release-green validations) now targets the `omni-build` runner label, which only two of the eight self-hosted runners carry. The box holds one build comfortably and two at the edge; a third now queues on GitHub instead of being OOM-killed by the kernel. ([#11932](https://github.com/diegosouzapw/OmniRoute/pull/11932)) +- **refactor(combo):** move `handleRoundRobinCombo` (and `resolveTargetTokenLimit`) into `open-sse/services/combo/roundRobinCombo.ts`. `combo.ts` drops from 2164 to 1014 lines (`split("\n").length`); the leaf is 1199 (under the 1200 new-file cap). The round-robin call site uses a dynamic `import()` so `releaseStickyPinOnFailure` / `clearStaleLKGP` can stay exported from `combo.ts` without a static cycle. Skip / sticky / semaphore / safety-timer behavior is unchanged. ([#12811](https://github.com/diegosouzapw/OmniRoute/pull/12811)) — thanks @HouMinXi +- **chore(quality):** rebaseline the file-size caps the error-boundary campaign grew past (`open-sse/executors/codex.ts`, `open-sse/vendor/codex-chatgpt-web/bridge.ts`, both via [#12444](https://github.com/diegosouzapw/OmniRoute/pull/12444)) ([#12654](https://github.com/diegosouzapw/OmniRoute/pull/12654)) +- **chore(quality):** rebaseline the file-size caps the hartmark batch grew past (`combos/page.tsx` via [#12355](https://github.com/diegosouzapw/OmniRoute/pull/12355), `open-sse/services/combo.ts` via [#12338](https://github.com/diegosouzapw/OmniRoute/pull/12338)) ([#12623](https://github.com/diegosouzapw/OmniRoute/pull/12623)) — thanks @hartmark +- **chore(quality):** rebaseline the file-size caps the HouMinXi batch grew past when its PRs stacked (`providers/page.tsx`, `chatCore.ts`, `accountFallback.ts`) — each PR measured correctly in isolation, none saw the stacking ([#12619](https://github.com/diegosouzapw/OmniRoute/pull/12619)) +- **chore(quality):** rebaseline `open-sse/services/combo.ts` for the reset-aware scoring the HouMinXi batch stacked ([#12637](https://github.com/diegosouzapw/OmniRoute/pull/12637)) ([#12678](https://github.com/diegosouzapw/OmniRoute/pull/12678)) — thanks @HouMinXi +- **docs:** sync provider catalog count (direct commit `4f11b2ae3d`) +- **docs(skills):** sync omni-inference search fallback (direct commit `b4e76a7ed9`) +- **chore(lint):** prune two stale base suppressions (direct commit `1325047d56`) +- **test(auto-combo):** tolerate full-suite rotation load (direct commit `ad3e293f9f`) +- **test:** drain inherited quality snapshot reds (direct commit `21ed68d8ac`) +- **chore(quality):** rebaseline measured CLI bundle growth (direct commit `d35b3f9779`) +- **test(ui):** reproduce invalid AnySearch icon alias (direct commit `12cc6ca834`) +- **test(translator):** fix the relative imports of the relocated deferred-finish test (direct commit `777d9d1629`) +- **docs(counts):** bring the documented provider and migration counts to the measured values (direct commit `e70bea30e9`) +- **test(security):** drop the guard-target set left unused by the sync (direct commit `a32ba0e362`) +- **chore(db):** renumber model_capabilities migration 163 → 169 (cross-branch collision with 163_radar_feed_cache_generated_at) (direct commit `70f5d4cbf9`) +- **docs(cli):** document CLI_PRIME_AGENT_BIN and correct the CLI Agents count (direct commit `9058e39b61`) +- **chore(deps):** refresh runtimes and adopt ESLint 10 ([#11259](https://github.com/diegosouzapw/OmniRoute/pull/11259)) — thanks @backryun +- **refactor(local-corpus):** implement dynamic root resolution and LRU cache ([#11491](https://github.com/diegosouzapw/OmniRoute/pull/11491)) — thanks @raheemuddin786 +- **chore(quality):** prune stale eslint-suppressions entries ([#11596](https://github.com/diegosouzapw/OmniRoute/pull/11596)) +- **chore(ci):** unblock quality gates - env-doc sync + stryker tap.testFiles registration (#9282) ([#11609](https://github.com/diegosouzapw/OmniRoute/pull/11609)) — thanks @Meet6338-X +- **chore(ci):** point the circular-deps gate at dpdm's real JS entrypoint ([#11615](https://github.com/diegosouzapw/OmniRoute/pull/11615)) — thanks @benzntech +- **chore(quality):** rebaseline file-size for #11448/#11495/#11561 growth ([#11630](https://github.com/diegosouzapw/OmniRoute/pull/11630)) +- **deps:** realign onnxruntime-node pin to @huggingface/transformers' 1.24.3 lockstep ([#11633](https://github.com/diegosouzapw/OmniRoute/pull/11633)) — thanks @jonlwheat2-gif +- **test(autoCombo):** port models_dev_tier #11508 guard from node:test to vitest ([#11635](https://github.com/diegosouzapw/OmniRoute/pull/11635)) — thanks @jonlwheat2-gif +- **chore(quality):** rebaseline file-size for #11083/#11631/#11628 growth ([#11636](https://github.com/diegosouzapw/OmniRoute/pull/11636)) +- **chore(lint):** prune stale ESLint suppressions (base-red #11449) ([#11644](https://github.com/diegosouzapw/OmniRoute/pull/11644)) — thanks @jonlwheat2-gif +- **docs(changelog):** aggregate the ten v3.8.50 fragments into CHANGELOG.md ([#11683](https://github.com/diegosouzapw/OmniRoute/pull/11683)) +- **chore(search):** drop unreachable credential sweep after #11642 ([#11687](https://github.com/diegosouzapw/OmniRoute/pull/11687)) — thanks @HouMinXi +- **ci:** add API route TypeScript regression gate ([#11705](https://github.com/diegosouzapw/OmniRoute/pull/11705)) — thanks @Sabeekhann +- **docs(dedup):** correct the CodeQL claim on the tenant-namespace comment ([#11712](https://github.com/diegosouzapw/OmniRoute/pull/11712)) +- **docs(changelog):** add consolidated v3.8.50 stats, top-25 ranking and a .mailmap ([#11715](https://github.com/diegosouzapw/OmniRoute/pull/11715)) +- **docs:** recommend gstack for AI-assisted workflows ([#11770](https://github.com/diegosouzapw/OmniRoute/pull/11770)) — thanks @santosraju99-hub +- **chore(quality):** tighten complexity/cognitive-complexity ratchets to the current tip; land the missed gateways.ts rebaseline ([#11771](https://github.com/diegosouzapw/OmniRoute/pull/11771)) — thanks @Rahulsharma0810 / @Rahulsharma0810 +- **docs(i18n):** finish Freepik → Magnific rebrand in locale strings and README ([#11772](https://github.com/diegosouzapw/OmniRoute/pull/11772)) — thanks @turbolego +- **refactor(auto-combo):** fix divergent scoring in combo health reporting ([#11854](https://github.com/diegosouzapw/OmniRoute/pull/11854)) — thanks @jacobsparts +- **chore(changelog):** add missing fragments for #11919/#11918/#11916 ([#11938](https://github.com/diegosouzapw/OmniRoute/pull/11938)) +- **test(check):** escape the runs-on fixture with JSON.stringify, not a quote-only replace ([#11942](https://github.com/diegosouzapw/OmniRoute/pull/11942)) (direct commit `fb7445eaa3`) +- **test(release):** align five suites with the contracts #11933, #11919 and #11876 shipped on release/v3.8.51 ([#11944](https://github.com/diegosouzapw/OmniRoute/pull/11944)) +- **chore(quality):** remove unreachable code and restore test discovery ([#11950](https://github.com/diegosouzapw/OmniRoute/pull/11950)) — thanks @backryun +- **docs(agents):** sync-back landings are fast-forward, never squash ([#11964](https://github.com/diegosouzapw/OmniRoute/pull/11964)) +- **test(infra):** retry recursive temp-dir removal instead of failing a shard on ENOTEMPTY (#11966) ([#11968](https://github.com/diegosouzapw/OmniRoute/pull/11968)) +- **docs(ops):** the .113 heavy-build ceiling is one runner, not two ([#12048](https://github.com/diegosouzapw/OmniRoute/pull/12048)) +- **test(ui):** correct inactive auto-fetch expectation ([#12098](https://github.com/diegosouzapw/OmniRoute/pull/12098)) — thanks @RaviTharuma +- **docs:** document native dependency check escape hatch ([#12101](https://github.com/diegosouzapw/OmniRoute/pull/12101)) — thanks @RaviTharuma +- **test(providers):** regenerate the translate-path golden for OrcaRouter (#11923) ([#12118](https://github.com/diegosouzapw/OmniRoute/pull/12118)) +- **chore(quality):** velocity phase — loosen every numeric baseline by 20% until v4.0, monitor headroom nightly ([#12125](https://github.com/diegosouzapw/OmniRoute/pull/12125)) +- **chore(lint):** batch 0 of #12146 — type the call-log-cap sqlite rows instead of 45 as-any casts ([#12157](https://github.com/diegosouzapw/OmniRoute/pull/12157)) +- **chore(lint):** batch 4 of #12146 — shared/components react-hooks violations resolved ([#12159](https://github.com/diegosouzapw/OmniRoute/pull/12159)) +- **chore(lint):** batch 1 of #12146 — dashboard/cli-code react-hooks violations resolved ([#12160](https://github.com/diegosouzapw/OmniRoute/pull/12160)) +- **chore(lint):** batch 3 of #12146 — dashboard/settings react-hooks violations resolved ([#12162](https://github.com/diegosouzapw/OmniRoute/pull/12162)) +- **chore(lint):** batch 2 of #12146 — resolve the react-hooks compiler violations in dashboard/providers ([#12163](https://github.com/diegosouzapw/OmniRoute/pull/12163)) +- **chore(quality):** register search-432 cooldown test in stryker tap.testFiles ([#12170](https://github.com/diegosouzapw/OmniRoute/pull/12170)) +- **chore(lint):** batch 5 of #12146 — combos, endpoint, provider-stats, api-manager and costs react-hooks violations resolved ([#12174](https://github.com/diegosouzapw/OmniRoute/pull/12174)) +- **build:** add contributor fast profile ([#12192](https://github.com/diegosouzapw/OmniRoute/pull/12192)) — thanks @rafacpti23 +- **docs(readme):** README touch-ups ([#12193](https://github.com/diegosouzapw/OmniRoute/pull/12193)) ([#12194](https://github.com/diegosouzapw/OmniRoute/pull/12194)) ([#12202](https://github.com/diegosouzapw/OmniRoute/pull/12202)) — thanks @rafacpti23 +- **build:** force the webpack bundler in the contributor build profile ([#12197](https://github.com/diegosouzapw/OmniRoute/pull/12197)) — thanks @rafacpti23 +- **build:** skip standalone packaging in the contributor profile ([#12198](https://github.com/diegosouzapw/OmniRoute/pull/12198)) — thanks @rafacpti23 +- **docs(audit):** align every published number with the code and harden check:docs-counts ([#12200](https://github.com/diegosouzapw/OmniRoute/pull/12200)) +- **build:** stub the instrumentation entrypoints in the contributor profile ([#12203](https://github.com/diegosouzapw/OmniRoute/pull/12203)) — thanks @rafacpti23 +- **build:** omit the standalone output target for contributor builds ([#12204](https://github.com/diegosouzapw/OmniRoute/pull/12204)) — thanks @rafacpti23 +- **chore(lint):** batch 6 of #12146 — memory, radar, audit, analytics, cache, usage, activity, home and RequestLoggerV2 react-hooks violations resolved ([#12208](https://github.com/diegosouzapw/OmniRoute/pull/12208)) +- **docs(diagrams):** rename number-carrying diagram files to stable names ([#12210](https://github.com/diegosouzapw/OmniRoute/pull/12210)) +- **docs(dashboard):** redraw onboarding tier-flow SVGs for the real 4-tier model ([#12211](https://github.com/diegosouzapw/OmniRoute/pull/12211)) +- **docs(api):** document every implemented route in openapi.yaml (276 → 692 paths) ([#12212](https://github.com/diegosouzapw/OmniRoute/pull/12212)) +- **chore(lint):** batch 7 of #12146 — final src tail: 37 react-hooks violations across 33 files resolved ([#12244](https://github.com/diegosouzapw/OmniRoute/pull/12244)) +- Revert "docs: recommend gstack for AI-assisted workflows (#11770)" ([#12248](https://github.com/diegosouzapw/OmniRoute/pull/12248)) ([#12249](https://github.com/diegosouzapw/OmniRoute/pull/12249)) +- **docs:** Chaos Mode setup guide + weighted strategy semantics ([#12250](https://github.com/diegosouzapw/OmniRoute/pull/12250)) +- **docs(agents):** protected-surface merge rule — operator approval for agent-instruction files ([#12253](https://github.com/diegosouzapw/OmniRoute/pull/12253)) +- **chore(quality):** register native-codex turn-pin tests in stryker tap.testFiles ([#12263](https://github.com/diegosouzapw/OmniRoute/pull/12263)) +- **chore(quality):** dedupe tap.testFiles entries added by racing base-red fixes ([#12265](https://github.com/diegosouzapw/OmniRoute/pull/12265)) +- **docs(readme):** introduce OmniRouteTray — the macOS menu-bar companion ([#12276](https://github.com/diegosouzapw/OmniRoute/pull/12276)) — thanks @ggiak +- **chore(proxy):** purge the legacy 1proxy residue — sync/rotator modules, dead DB exports, dead settings tab and flag, docs (#12091) ([#12290](https://github.com/diegosouzapw/OmniRoute/pull/12290)) +- **chore(quality):** baseline-headroom skips generated and vendored files in the fileSize worst-file signal ([#12291](https://github.com/diegosouzapw/OmniRoute/pull/12291)) +- **refactor(video):** unify the JPEG frame data-URI contract ([#12322](https://github.com/diegosouzapw/OmniRoute/pull/12322)) +- **chore(deps):** freeze onnxruntime-node and eslint-plugin-react-hooks out of dependabot groups ([#12329](https://github.com/diegosouzapw/OmniRoute/pull/12329)) +- **test(executors):** restore the #10986 reasoning-only fallback guards ([#12364](https://github.com/diegosouzapw/OmniRoute/pull/12364)) — thanks @pacocartones +- **chore(quality):** re-tighten the file-size ratchet to the real LOC (plan 3.8.52 task 0) ([#12411](https://github.com/diegosouzapw/OmniRoute/pull/12411)) +- **test(grok):** format web executor suite ([#12412](https://github.com/diegosouzapw/OmniRoute/pull/12412)) +- **chore(quality):** ratchet open-sse typecheck baseline to zero ([#12418](https://github.com/diegosouzapw/OmniRoute/pull/12418)) +- **test(build):** guard the artifact path policy arrays against duplicates ([#12422](https://github.com/diegosouzapw/OmniRoute/pull/12422)) +- **chore(lint):** adopt eslint-plugin-react-hooks 7.1.1 ([#12428](https://github.com/diegosouzapw/OmniRoute/pull/12428)) +- **chore(providers):** remove a keyless provider integration at its operator's request ([#12440](https://github.com/diegosouzapw/OmniRoute/pull/12440)) +- **docs(providers):** register providers removed at their operator's request and guard against reintroduction ([#12478](https://github.com/diegosouzapw/OmniRoute/pull/12478)) +- **chore(quality):** base fixes — stryker tap.testFiles + node_modules cache key ([#12482](https://github.com/diegosouzapw/OmniRoute/pull/12482)) +- **docs(arch):** one-process recipe for tens of long /v1/responses ([#12493](https://github.com/diegosouzapw/OmniRoute/pull/12493)) — thanks @RaviTharuma +- **chore(quality):** tighten the CodeQL ratchet baseline from 11 to 6 ([#12530](https://github.com/diegosouzapw/OmniRoute/pull/12530)) +- **chore(deps):** pin browserslist override to ^4.28.8 ([#12592](https://github.com/diegosouzapw/OmniRoute/pull/12592)) — thanks @KooshaPari +- **chore(deps):** bump fast-uri to 3.1.7 in the electron lockfile ([#12601](https://github.com/diegosouzapw/OmniRoute/pull/12601)) +- **docs(quality):** document how the CodeQL ratchet refreshes and how to tighten it ([#12611](https://github.com/diegosouzapw/OmniRoute/pull/12611)) +- **test(ci):** pin the openapi-security-tiers two-arm contract with an executing gate test (#12581) ([#12652](https://github.com/diegosouzapw/OmniRoute/pull/12652)) +- **docs(gamification):** align XP Rewards table with code (#12501) ([#12667](https://github.com/diegosouzapw/OmniRoute/pull/12667)) — thanks @KooshaPari +- **refactor(combo):** split executeTarget into gates, attempt, and loop ([#12746](https://github.com/diegosouzapw/OmniRoute/pull/12746)) — thanks @HouMinXi +- **docs(security):** document socket.yml scanner config + CI workflow link (#12575) ([#12764](https://github.com/diegosouzapw/OmniRoute/pull/12764)) — thanks @KooshaPari +- **chore(ci):** guard commit identity in pre-commit to stop author misattribution ([#12772](https://github.com/diegosouzapw/OmniRoute/pull/12772)) +- **deps:** 17 Dependabot bumps — bump electron from 43.4.0 to 43.4.1 in /electron ([#11426](https://github.com/diegosouzapw/OmniRoute/pull/11426)); bump the development group across 1 directory with 8 updates ([#11428](https://github.com/diegosouzapw/OmniRoute/pull/11428)); bump the production group across 1 directory with 11 updates ([#11440](https://github.com/diegosouzapw/OmniRoute/pull/11440)); chore(deps): bump github/codeql-action/init from 4.37.7 to 4.37.8 ([#11925](https://github.com/diegosouzapw/OmniRoute/pull/11925)); chore(deps): bump github/codeql-action from 4.37.7 to 4.37.8 ([#11926](https://github.com/diegosouzapw/OmniRoute/pull/11926)); chore(deps): bump github/codeql-action/analyze from 4.37.7 to 4.37.8 ([#11927](https://github.com/diegosouzapw/OmniRoute/pull/11927)); build(deps): bump github/codeql-action/init from 4.37.8 to 4.37.9 ([#12345](https://github.com/diegosouzapw/OmniRoute/pull/12345)); build(deps): bump github/codeql-action/analyze from 4.37.8 to 4.37.9 ([#12346](https://github.com/diegosouzapw/OmniRoute/pull/12346)); bump the development group across 1 directory with 2 updates ([#12347](https://github.com/diegosouzapw/OmniRoute/pull/12347)); build(deps): bump github/codeql-action from 4.37.8 to 4.37.9 ([#12349](https://github.com/diegosouzapw/OmniRoute/pull/12349)); bump browserslist from 4.28.2 to 4.28.8 ([#12396](https://github.com/diegosouzapw/OmniRoute/pull/12396)); bump the production group across 1 directory with 4 updates ([#12399](https://github.com/diegosouzapw/OmniRoute/pull/12399)); bump @xmldom/xmldom ([#12500](https://github.com/diegosouzapw/OmniRoute/pull/12500)); bump qs from 6.15.2 to 6.16.0 ([#12512](https://github.com/diegosouzapw/OmniRoute/pull/12512)); bump @xmldom/xmldom from 0.9.10 to 0.9.12 ([#12513](https://github.com/diegosouzapw/OmniRoute/pull/12513)); bump fast-uri from 3.1.5 to 3.1.7 ([#12514](https://github.com/diegosouzapw/OmniRoute/pull/12514)); bump @humanfs/node from 0.16.7 to 0.16.8 ([#12515](https://github.com/diegosouzapw/OmniRoute/pull/12515)) +- Clear the `release/v3.8.51` `check:agent-skills-sync` base-red: regenerate `skills/cli-tunnel/SKILL.md` so the `tunnel create [type]` positional that #13009 taught the generator to read is reflected in the committed skill. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` typecheck base-red from #12731: the new `Mode pack ranking gates` candidates in `open-sse/services/autoCombo/__tests__/autoCombo.test.ts` omitted the required `provider`, `model` and `errorRate` fields of `ProviderCandidate`, failing `check:open-sse-typecheck` (and with it `Fast Quality Gates`) on every open PR. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- Clear the `release/v3.8.51` docs-sync base-red: EURouter (#13025) and GreenPT (#13024) took the live provider count to 358, leaving 7 STRICT drifts (`PROVIDER_REFERENCE.md`, 4 diagrams, 2 tier-flow images) plus the `AGENTS.md` / `llm.txt` / `package.json` count claims stale. ([#13216](https://github.com/diegosouzapw/OmniRoute/pull/13216)) +- **test(models):** Pin `CATALOG_BUILD_TIMEOUT_MS` in the #12058 canonical-catalog regression so a cold catalog build racing the 8s default bound no longer flakes the suite with a 500 ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Allow `@omniroute/opencode-plugin-v2/` in the npm pack policy — [#12870](https://github.com/diegosouzapw/OmniRoute/pull/12870) shipped the v2 plugin beside its v1 sibling without widening the allowlist, so every packed file under it read as an unexpected artifact and failed `check:pack-policy` ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **fix(ci):** Point the release-green validator's pack gate at the tree it is validating — the build-provenance guard defaulted to `origin/main`, which a release branch tip can never be an ancestor of mid-cycle, so `Package artifact` reported a false red on every run and the tarball boot-smoke cascaded off it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) ([#12959](https://github.com/diegosouzapw/OmniRoute/pull/12959)) +- **docs(checks):** keep doc counts honest — headings, rankings, catalog, weights, quality gate and scoring diagram now covered ([#12507](https://github.com/diegosouzapw/OmniRoute/pull/12507)) — thanks @maxmad64bis +- **chore(lifecycle):** `check:model-lifecycle` now also diffs `DEFAULT_DEGRADATION_MAP` (the background-task redirect table) against the vendor lifecycle snapshot, refusing a retired id as source or target, with a table-driven unit test beside it. Three rows whose source the vendor had retired — `claude-sonnet-4-20250514`, `gemini-3-pro-preview` and `gpt-5.1-codex` (whose target `gpt-5.1-codex-mini` is retired too) — were dead code, since `checkLifecycle` answers 410 before the redirect runs; they are dropped (#12535 — thanks @pacocartones) +- **test(compression):** cover idle worker eviction at the resource level — the pool must call `terminate()` and must not retain the worker's `MessagePort`, complementing the `exit`-event assertion added with the fix ([#13371](https://github.com/diegosouzapw/OmniRoute/pull/13371)) +- **chore(quality):** raise the `imageGeneration.ts` file-size ceiling for the image-only-model guard that clears the #12945 base-red ([#13392](https://github.com/diegosouzapw/OmniRoute/pull/13392)) +- **chore(quality):** type the combo-test route's JSON response bodies instead of casting them to `any`, drop the now-empty suppression entry, and remove the `hasPerModelQuota` import `chatCore.ts` stopped using when the failure-classification helper was extracted ([#13379](https://github.com/diegosouzapw/OmniRoute/pull/13379)) +- **chore(electron):** the desktop bundle no longer ships the root-level files of every translated docs mirror (`README.md`, `llm.txt`, `CLAUDE.md`, `GEMINI.md`, `CONTRIBUTING.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`) — the packaged app only reads `docs/i18n//docs/**`, which stays. Saves ~11 MB on top of the translated CHANGELOGs already pruned. (#0000) ([#13235](https://github.com/diegosouzapw/OmniRoute/pull/13235)) +- **chore(quality):** retighten the `src/sse/handlers/chat.ts` unused-vars suppression count to the 8 violations that actually remain ([#13385](https://github.com/diegosouzapw/OmniRoute/pull/13385)) +- Bring the documented counts back in line with the code: 171 migrations (was 169) and 20 routing strategies (was 19), plus the regenerated `cli-tunnel` skill reference. ([#12970](https://github.com/diegosouzapw/OmniRoute/pull/12970)) +- Update the documented migration count to 172 after the call-logs provider-stats indexes landed. ([#13209](https://github.com/diegosouzapw/OmniRoute/pull/13209)) +- **chore(ci):** two gates that close the blind spots behind the exclusions above. `check:vitest-exclusions` requires every Vitest exclusion to name a tracking issue and to appear in `config/quality/vitest-exclusions.json` — the previous list grew to 62 files behind a comment pointing at an issue that had been closed for a month. `check-new-key-coverage` requires a key newly added to `en.json` to reach every locale; the existing coverage gate is a percentage floor per locale, so eleven absent keys out of ~13,000 left it at 99.9% while a whole feature shipped untranslated in nine languages. Both are diff-aware, so pre-existing debt stays frozen and neither needed a migration to turn on. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **build:** root layout no longer loads Inter from `next/font/google`, so a production image build does not need fonts.googleapis.com ([#13026](https://github.com/diegosouzapw/OmniRoute/pull/13026)) — thanks @HouMinXi +- **docs(i18n):** bring the eight new `llm.txt` mirrors (km, kn, ml, my, ne, or, pa, si) up to the current provider and migration counts, so `check-docs-sync` stops rejecting every commit ([#13674](https://github.com/diegosouzapw/OmniRoute/pull/13674)) +- **refactor(ui):** flow surfaces (home topology, combo live studio, compression cockpit/waterfall/nodes, token-health badge) express state through the theme-aware `--orch-status-*` tokens instead of fixed dark-mode hex, so green/red/amber/grey stay legible in the light theme; dark mode is byte-identical. Categorical palettes (routing-strategy hues, compression-layer pills, provider brand colors) deliberately stay hex ([#12378](https://github.com/diegosouzapw/OmniRoute/issues/12378)) ([#13203](https://github.com/diegosouzapw/OmniRoute/pull/13203)) +- **chore(tests):** 51 test files that had been excluded from Vitest are running again, restoring roughly 350 assertions to the blocking suite. Each was measured individually first: of the 62 files parked behind the `// #8618 — pre-existing failure` comment, 51 pass against the current tree with no source change, so the exclusions had outlived the failures they were added for. The 11 that genuinely still fail stay excluded, but now point at a live tracker (#13204) rather than at #8618, which was closed in August while the list it tracked kept growing. ([#13233](https://github.com/diegosouzapw/OmniRoute/pull/13233)) +- **chore(quality):** drop two ESLint suppression entries whose violations no longer exist, so `eslint --suppressions-location` stops rejecting every commit that touches the surrounding files ([#13384](https://github.com/diegosouzapw/OmniRoute/pull/13384)) +- **chore(release):** bring main's post-tag fixes into release/v3.8.51 — sync-back step 2/2 (direct commit `21c488f210`) +- **chore(deps):** patch toml and esbuild in the opencode plugin lockfiles ([#12965](https://github.com/diegosouzapw/OmniRoute/pull/12965)) +- **docs(changelog):** reconcile the v3.8.51 living section — fold 366 fragments, cover every cycle commit, credit every contributor ([#12971](https://github.com/diegosouzapw/OmniRoute/pull/12971)) +- **test(plugin):** wait for the v2 lazy-refresh tier instead of sleeping past it ([#12984](https://github.com/diegosouzapw/OmniRoute/pull/12984)) +- **chore(quality):** rebaseline chatCore.ts after the non-streaming regression fixes ([#13045](https://github.com/diegosouzapw/OmniRoute/pull/13045)) +- **docs(changelog):** add the missing bullet marker to the DeepSeek PoW fragment ([#13200](https://github.com/diegosouzapw/OmniRoute/pull/13200)) +- **chore(deps):** drain the Dependabot queue — 10 of 13 alerts ([#13213](https://github.com/diegosouzapw/OmniRoute/pull/13213)) +- **chore(ci):** make worktrees carry the hooks that enforce the identity gate ([#13236](https://github.com/diegosouzapw/OmniRoute/pull/13236)) +- **chore(deps):** pin csv-parse to ^7.0.2 via overrides (#13117) ([#13300](https://github.com/diegosouzapw/OmniRoute/pull/13300)) — thanks @KooshaPari +- **chore(deps):** bump hono from ^4.12.34 to ^4.13.7 (#13148) ([#13301](https://github.com/diegosouzapw/OmniRoute/pull/13301)) — thanks @KooshaPari +- **chore(deps):** pin joi to ^18.2.8 via overrides (#13085) ([#13302](https://github.com/diegosouzapw/OmniRoute/pull/13302)) — thanks @KooshaPari +- **docs(providers):** add Microsoft 365 Copilot (BizChat) provider guide (#12779) ([#13340](https://github.com/diegosouzapw/OmniRoute/pull/13340)) — thanks @KooshaPari +### 🙌 Contributors + +Thanks to everyone whose work landed in v3.8.51: + +| Contributor | PRs / Issues | +| --- | --- | +| [@5dive-bot](https://github.com/5dive-bot) | #11852 | +| [@abhisheksharma2411](https://github.com/abhisheksharma2411) | #11547, #11684, #11959, #12209 | +| [@adevwithpurpose](https://github.com/adevwithpurpose) | #11464 | +| [@adivekar-utexas](https://github.com/adivekar-utexas) | #12015, #12043, #12090, #12138 | +| [@AIB1TAL0S](https://github.com/AIB1TAL0S) | #12103 | +| [@alltomatos](https://github.com/alltomatos) | #11448 | +| [@alvinveroy](https://github.com/alvinveroy) | #11857, #12027, #12165 | +| [@amaleta](https://github.com/amaleta) | #11906, #11934 | +| [@amartinawi](https://github.com/amartinawi) | #11575 | +| [@AndrianBalanescu](https://github.com/AndrianBalanescu) | #11083, #11830, #11834, #11844 | +| [@anhtahaylove](https://github.com/anhtahaylove) | #13091, #13092, #13093, #13096, #13097, #13100, #13106, #13109, #13114, #13155, #13166, #13171, #13175, #13187, #13196 | +| [@aniruddhaadak80](https://github.com/aniruddhaadak80) | #11760 | +| [@app](https://github.com/app) | #12554 | +| [@arminanton](https://github.com/arminanton) | #11461, #11513 | +| [@AStupidBear](https://github.com/AStupidBear) | #11584 | +| [@atescivitci-cmd](https://github.com/atescivitci-cmd) | #12105 | +| [@b3nw](https://github.com/b3nw) | #11970, #11971, #12147 | +| [@backryun](https://github.com/backryun) | #11259, #11950, #12075, #12076, #12078, #12079, #12081, #12082, #12181, #12228, #12239, #12255, #12258, #12277, #12367, #12423, #12524, #12538 | +| [@benjaminkitt](https://github.com/benjaminkitt) | #11747 | +| [@benzntech](https://github.com/benzntech) | #11614, #11615 | +| [@Bl0ck154](https://github.com/Bl0ck154) | #11948, #11951, #11952, #11953, #11954 | +| [@brick30llc-ctrl](https://github.com/brick30llc-ctrl) | #11752, #12242, #12262 | +| [@Chewji9875](https://github.com/Chewji9875) | #11455, #11875, #12028 | +| [@cheynetom](https://github.com/cheynetom) | #12058 | +| [@davidebaraldo](https://github.com/davidebaraldo) | #12222, #12304 | +| [@davidlinfr](https://github.com/davidlinfr) | #6390 | +| [@ddarkr](https://github.com/ddarkr) | #12124 | +| [@Deftera186](https://github.com/Deftera186) | #11809 | +| [@dpozimski](https://github.com/dpozimski) | #11945 | +| [@DW-MediaLab](https://github.com/DW-MediaLab) | #12229 | +| [@echel0nn](https://github.com/echel0nn) | #11923 | +| [@f9td56dbgh-hub](https://github.com/f9td56dbgh-hub) | #11660 | +| [@fabioluissilva](https://github.com/fabioluissilva) | #11991 | +| [@foreveryh](https://github.com/foreveryh) | #12177 | +| [@geek007git](https://github.com/geek007git) | #12115, #12116, #12117, #12120, #12122 | +| [@geekyNads](https://github.com/geekyNads) | #11784, #12266 | +| [@ggiak](https://github.com/ggiak) | #11561, #12276, #12350, #12402 | +| [@gonisulaimann](https://github.com/gonisulaimann) | #12368, #12369, #12371 | +| [@Gorillaz322](https://github.com/Gorillaz322) | #12207 | +| [@groovecityJO](https://github.com/groovecityJO) | #12682 | +| [@hartmark](https://github.com/hartmark) | #11434, #11452, #11473, #11499, #11703, #11983, #11984, #11985, #11986, #11988, #11989, #11990, #11994, #12221, #12293, #12445, #12446, #12447, #12448, #12460, #12461, #12623, #12646, #12650, #12680, #12717, #12718, #12727, #12741, #12854 | +| [@hizzt](https://github.com/hizzt) | #11894 | +| [@hongnoul](https://github.com/hongnoul) | #11484 | +| [@HouMinXi](https://github.com/HouMinXi) | #11411, #11414, #11512, #11518, #11520, #11641, #11642, #11643, #11687, #11779, #11849, #11850, #11851, #11915, #11916, #11918, #11919, #11920, #12013, #12017, #12033, #12042, #12106, #12139, #12166, #12169, #12171, #12205, #12213, #12312, #12325, #12487, #12488, #12495, #12504, #12557, #12566, #12590, #12591, #12624, #12626, #12632, #12637, #12678, #12696, #12697, #12711, #12733, #12746, #12767, #12770, #12789, #12803, #12805, #12811, #12866, #12868, #12899, #12926, #12934, #12950, #12951, #12974, #13001, #13006, #13011, #13017, #13026, #13027, #13034, #13035, #13038, #13042, #13050, #13060, #13061, #13069, #13107, #13120, #13136, #13195, #13197 | +| [@Hsia97](https://github.com/Hsia97) | #11624 | +| [@jacobsparts](https://github.com/jacobsparts) | #11854, #12155, #12167 | +| [@joglomedia](https://github.com/joglomedia) | #11980 | +| [@jonlwheat2-gif](https://github.com/jonlwheat2-gif) | #11443, #11450, #11549, #11567, #11608, #11633, #11635, #11640, #11644, #11671, #11672, #11673, #11674, #11675, #11676, #11677, #11706, #11783, #12051, #12052, #12053, #12055 | +| [@kanade-hoshino](https://github.com/kanade-hoshino) | #12180 | +| [@Karan825](https://github.com/Karan825) | #11861 | +| [@KaspaPulse](https://github.com/KaspaPulse) | #11389, #11469, #11628, #11666, #11910 | +| [@keeltrace](https://github.com/keeltrace) | #12080, #12223 | +| [@killer30001000](https://github.com/killer30001000) | #12178 | +| [@KooshaPari](https://github.com/KooshaPari) | #12470, #12592, #12667, #12699, #12703, #12706, #12764, #12769, #12771, #13300, #13301, #13302, #13340, #13401, #13403, #13404, #13405, #13406, #13407, #13409, #13410, #13411, #13412, #13413, #13414, #13418, #13424, #13427, #13433, #13523, #13524, #13525, #13528, #13531, #13532, #13534, #13539, #13543, #13545, #13546, #13547, #13550, #13553 | +| [@kriptoburak](https://github.com/kriptoburak) | #11370 | +| [@KrzysiekSko](https://github.com/KrzysiekSko) | #12425, #12432, #12673 | +| [@marcelokarval](https://github.com/marcelokarval) | direct commit / report | +| [@maxmad64bis](https://github.com/maxmad64bis) | #1622, #11435, #11437, #11441, #11537, #11550, #11553, #11555, #11557, #11812, #11842, #11843, #11903, #12151, #12214, #12215, #12218, #12226, #12314, #12316, #12317, #12318, #12319, #12320, #12321, #12507, #12715, #12731, #12744, #12786, #12787, #12788, #12790, #12792, #12794, #12795, #12828, #12832, #12853, #12857, #12870, #12937, #12941, #12975, #13141, #13142, #13146, #13147 | +| [@mdigitalbh81](https://github.com/mdigitalbh81) | #12240 | +| [@Meet6338-X](https://github.com/Meet6338-X) | #11598, #11609, #12110 | +| [@morpheus9393](https://github.com/morpheus9393) | #11943 | +| [@MumuTW](https://github.com/MumuTW) | #11492, #11502, #11506, #11507, #11626, #11675, #11685, #11728, #11746, #11888, #11889, #11890, #11892 | +| [@Neuron-Mr-White](https://github.com/Neuron-Mr-White) | #11622, #11801, #12256 | +| [@NightStalker-87](https://github.com/NightStalker-87) | #12183 | +| [@NoxzRCW](https://github.com/NoxzRCW) | #11879, #11880, #11881, #11882, #11883, #11935 | +| [@ntdat812](https://github.com/ntdat812) | #11585, #12095 | +| [@ntdatt812](https://github.com/ntdatt812) | #11368, #11573, #11574, #11576, #11577, #11580, #11582, #11583, #11588, #11589, #11590, #11591, #11592, #11593, #11672, #12177, #12180, #12873, #12918, #12920, #12921, #12925, #12930, #13007, #13009, #13024, #13025, #13083, #13087, #13101, #13104, #13110 | +| [@official-burak](https://github.com/official-burak) | #11542 | +| [@opensource-elearning](https://github.com/opensource-elearning) | #12179, #12189, #12278, #12286 | +| [@oyi77](https://github.com/oyi77) | #11408, #11409, #11421, #11505, #11677, #12036, #12110 | +| [@pacocartones](https://github.com/pacocartones) | #11521, #11522, #11527, #11528, #11529, #11530, #11531, #11532, #11533, #11534, #11595, #11599, #11603, #11604, #11605, #11607, #11610, #11676, #11714, #11716, #11718, #11767, #11838, #11860, #11862, #11869, #11871, #11872, #11873, #11903, #11906, #11921, #11934, #12356, #12358, #12359, #12360, #12361, #12362, #12364, #12365, #12373, #12374, #12375, #12376, #12377, #12379, #12380, #12381, #12386, #12387, #12389, #12390, #12394, #12395, #12397, #12401, #12403, #12404, #12522, #12523, #12535, #12536, #12540, #12541, #12543, #12545, #12548, #12549, #12550, #12551, #12552, #12647, #12651, #12653, #13055 | +| [@patrykkopycinski](https://github.com/patrykkopycinski) | #11936, #11937, #12224 | +| [@PauloFH](https://github.com/PauloFH) | #11509 | +| [@PauloHSOliveira](https://github.com/PauloHSOliveira) | #12241 | +| [@ponkcore](https://github.com/ponkcore) | #12054 | +| [@Prajeeth-12](https://github.com/Prajeeth-12) | #11634, #12046 | +| [@quiterunner-commits](https://github.com/quiterunner-commits) | #12131, #12143 | +| [@rafacpti23](https://github.com/rafacpti23) | #11554, #11558, #12192, #12197, #12198, #12202, #12203, #12204 | +| [@ragnar-claude](https://github.com/ragnar-claude) | #11564 | +| [@raheemuddin786](https://github.com/raheemuddin786) | #11491, #11839, #11840, #11841, #12003, #12230, #12231, #12232, #12233, #12234 | +| [@rahilmavani](https://github.com/rahilmavani) | #11761 | +| [@Rahulsharma0810](https://github.com/Rahulsharma0810) | #11771 | +| [@RaviTharuma](https://github.com/RaviTharuma) | #11710, #11727, #11797, #11798, #11802, #11805, #11806, #11811, #12098, #12099, #12101, #12449, #12452, #12472, #12473, #12493, #12533, #12607, #12628, #12631, #12636, #12875, #12876, #12880, #12882, #12884 | +| [@rezjalibd](https://github.com/rezjalibd) | #12186 | +| [@RhianB14](https://github.com/RhianB14) | #11692 | +| [@rifqiawl](https://github.com/rifqiawl) | #11517, #11519, #11969 | +| [@rqzbeh](https://github.com/rqzbeh) | #11390 | +| [@Sabeekhann](https://github.com/Sabeekhann) | #11699, #11705 | +| [@santosraju99-hub](https://github.com/santosraju99-hub) | #11755, #11770 | +| [@Seramicx](https://github.com/Seramicx) | #11568 | +| [@Siva010](https://github.com/Siva010) | #12191 | +| [@solstxce](https://github.com/solstxce) | #11597 | +| [@soroush5](https://github.com/soroush5) | #12691 | +| [@tenshiak](https://github.com/tenshiak) | #12279 | +| [@TheDemonTuan](https://github.com/TheDemonTuan) | #11468, #11470, #11471, #11482, #11548, #11758, #11775, #11814 | +| [@thomasmaerz](https://github.com/thomasmaerz) | #12834 | +| [@tuandinh0801](https://github.com/tuandinh0801) | #11454 | +| [@turbolego](https://github.com/turbolego) | #11621, #11762, #11772, #11774, #11781, #12216 | +| [@Tushar49](https://github.com/Tushar49) | #11392 | +| [@ujjawalkaushik1110](https://github.com/ujjawalkaushik1110) | #11863 | +| [@vermasomesh835](https://github.com/vermasomesh835) | #11794 | +| [@vsd2807](https://github.com/vsd2807) | #11565, #11619 | +| [@wahidsadik371-coder](https://github.com/wahidsadik371-coder) | #12126 | +| [@watchingdogs](https://github.com/watchingdogs) | #12031 | +| [@wildcard](https://github.com/wildcard) | #11620 | +| [@xiaoyaner0201](https://github.com/xiaoyaner0201) | #11460, #11662, #12083 | +| [@Xxx91n](https://github.com/Xxx91n) | #11690 | +| [@yourspraveen](https://github.com/yourspraveen) | #11146 | +| [@yxyxy](https://github.com/yxyxy) | #11671 | +| [@ZaimMarzuki](https://github.com/ZaimMarzuki) | #11960 | +| [@Zartharas](https://github.com/Zartharas) | #11340 | +| [@zero-executioner](https://github.com/zero-executioner) | #11631 | +| [@diegosouzapw](https://github.com/diegosouzapw) | maintainer | + +--- ## [3.8.50] — 2026-08-25 _Living section — regenerated 2026-08-12 from all cycle commits (cycle open `ed2db6cb19` → tip). Bullets carry the merged PR and its author; direct pushes listed separately._